diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 000000000000..ea217df9307d --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,106 @@ +version: 2.1 + +jobs: + + # The following job is to run any image comparison test, and runs on any branch + # or in any pull request. It will generate a summary page for each tox environment + # being run, and giles will report the URL of the summary page back to the pull + # request (alternatively you can find the summary page in the artifacts in the + # CircleCI UI). + figure: + parameters: + jobname: + type: string + docker: + - image: cimg/python:3.11 + environment: + TOXENV: << parameters.jobname >> + PY_COLORS: "1" + steps: + - checkout + - run: + name: Install dependencies + command: | + sudo apt update + sudo apt install texlive texlive-latex-extra texlive-fonts-recommended dvipng cm-super + pip install pip tox --upgrade + - run: + name: Run tests + command: tox -v + - run: + name: Upload coverage results to codecov + command: | + curl -Os https://uploader.codecov.io/latest/linux/codecov + chmod +x codecov + ./codecov -t ${CODECOV_TOKEN} -f coverage.xml + - store_artifacts: + path: results + - run: + name: "Image comparison page is available at: " + command: echo "${CIRCLE_BUILD_URL}/artifacts/${CIRCLE_NODE_INDEX}/results/fig_comparison.html" + + # The following job runs only on main - and its main purpose is to update the reference + # images in the astropy-figure-tests repository. This job needs a deploy key. To produce + # this, go to the astropy-figure-tests repository settings and go to SSH keys, then add + # your public SSH key. + deploy-reference-images: + parameters: + jobname: + type: string + docker: + - image: cimg/python:3.11 + environment: + TOXENV: << parameters.jobname >> + PY_COLORS: "1" + GIT_SSH_COMMAND: ssh -i ~/.ssh/id_rsa_bfaaefe38d95110b75c79252bafbe0fc + steps: + - checkout + - run: + name: Install dependencies + command: | + sudo apt update + sudo apt install texlive texlive-latex-extra texlive-fonts-recommended dvipng cm-super + pip install pip tox --upgrade + - run: ssh-add -D + - add_ssh_keys: + fingerprints: "bf:aa:ef:e3:8d:95:11:0b:75:c7:92:52:ba:fb:e0:fc" + - run: ssh-keyscan github.com >> ~/.ssh/known_hosts + - run: git config --global user.email "astropy@circleci" && git config --global user.name "Astropy Circle CI" + - run: git clone git@github.com:astropy/astropy-figure-tests.git --depth 1 -b astropy-${CIRCLE_BRANCH} ~/astropy-figure-tests/ + - run: + name: Generate reference images + command: tox -v -- --mpl-generate-path=/home/circleci/astropy-figure-tests/figures/$TOXENV + - run: | + cd ~/astropy-figure-tests/ + git pull + git status + git add . + git commit -m "Update reference figures from ${CIRCLE_BRANCH}" || echo "No changes to reference images to deploy" + git push + +workflows: + version: 2 + + figure-tests: + jobs: + - figure: + name: << matrix.jobname >> + matrix: + parameters: + jobname: + - "py311-test-image-mpl380-cov" + - "py311-test-image-mpldev-cov" + + - deploy-reference-images: + name: baseline-<< matrix.jobname >> + matrix: + parameters: + jobname: + - "py311-test-image-mpl380-cov" + - "py311-test-image-mpldev-cov" + requires: + - << matrix.jobname >> + filters: + branches: + only: + - main diff --git a/.clang-format b/.clang-format new file mode 100644 index 000000000000..0ae810b7e9dc --- /dev/null +++ b/.clang-format @@ -0,0 +1,32 @@ +# clang-format configuration for astropy C code + +BasedOnStyle: Google +IndentWidth: 4 +TabWidth: 4 +UseTab: Never +ContinuationIndentWidth: 4 +ColumnLimit: 100 +AlignAfterOpenBracket: BlockIndent +AlwaysBreakAfterReturnType: None +BreakBeforeBraces: Stroustrup +InsertBraces: true +BinPackArguments: false +BinPackParameters: false +PointerAlignment: Right +SpaceAfterCStyleCast: false +IncludeBlocks: Preserve +SortIncludes: false +ReflowComments: false +MaxEmptyLinesToKeep: 2 +KeepEmptyLinesAtTheStartOfBlocks: true +AllowShortEnumsOnASingleLine: false +AllowShortIfStatementsOnASingleLine: false +AllowShortFunctionsOnASingleLine: Empty +IndentPPDirectives: None +IndentGotoLabels: false +SpaceBeforeParens: ControlStatements +SpacesBeforeTrailingComments: 1 +BreakStringLiterals: false +DerivePointerAlignment: false +AlignEscapedNewlines: DontAlign +StatementMacros: [PyObject_HEAD, PyObject_VAR_HEAD, PyObject_HEAD_EXTRA] diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 000000000000..5830b977771d --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,17 @@ +{ + "name": "AstroPy", + "image": "mcr.microsoft.com/devcontainers/miniconda:0-3", + "onCreateCommand": "conda init bash && sudo cp .devcontainer/welcome-message.txt /usr/local/etc/vscode-dev-containers/first-run-notice.txt", + "postCreateCommand": "git fetch --tags && pip install tox", + "waitFor": "postCreateCommand", + "customizations": { + "vscode": { + "extensions": [ + "ms-python.python", + "ms-python.black-formatter", + "charliermarsh.ruff", + "stkb.rewrap" + ] + } + } +} diff --git a/.devcontainer/welcome-message.txt b/.devcontainer/welcome-message.txt new file mode 100644 index 000000000000..08e7cea12d05 --- /dev/null +++ b/.devcontainer/welcome-message.txt @@ -0,0 +1,12 @@ +👋 Welcome to "AstroPy" in GitHub Codespaces! + +🔍 To explore VS Code to its fullest, search using the Command Palette (Cmd/Ctrl + Shift + P or F1). + +â„šī¸ Look at https://docs.astropy.org/en/latest/development/quickstart.html + for more contribution details. + +⭐⭐ ================================= IMPORTANT!! ================================== ⭐⭐ + To complete setup of your development environment run the following in the terminal: + + pip install -e .[all,test_all,docs] +⭐⭐ ================================================================================== ⭐⭐ diff --git a/.flake8 b/.flake8 new file mode 100644 index 000000000000..6a3eb2a50ff5 --- /dev/null +++ b/.flake8 @@ -0,0 +1,31 @@ +[flake8] +max-line-length = 88 +select = E,F,W +exclude = extern,*parsetab.py,*lextab.py +extend-ignore = E203,E501,E711,E721,E731,E741,F403,F841,W5 +per-file-ignores = + __init__.py:F401,F403,E402 + test_*.py:E402 + astropy/io/fits/card.py:E131 + astropy/io/registry/compat.py:F822 + astropy/convolution/convolve.py:E241 + astropy/modeling/functional_models.py:E226,E241 + astropy/modeling/models.py:F401,F403,F405 + astropy/modeling/tests/test_constraints.py:E241 + astropy/stats/tests/test_histogram.py:E241 + astropy/stats/tests/test_sigma_clipping.py:E126,E131,E241 + astropy/units/__init__.py:F401,F821 + astropy/units/astrophys.py:F821 + astropy/units/cgs.py:F821 + astropy/units/imperial.py:F821 + astropy/units/misc.py:F821 + astropy/units/photometric.py:F821 + astropy/units/si.py:F821 + astropy/units/tests/test_quantity_decorator.py:F821 + astropy/units/tests/test_quantity_typing.py:F821 + astropy/wcs/wcs.py:F821 + astropy/wcs/wcsapi/fitswcs.py:F821 + astropy/wcs/wcsapi/utils.py:E127,E128 + docs/conf.py:F401 + examples/*.py:E1,E2,E402 + */examples/*.py:E1,E2,E402 diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 000000000000..c0f3b1a2fa57 --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,55 @@ +# Commits to ignore in blame: +# black for astropy.config +5a4c5fcb00bf917c4bbe3c342b66177cf6cb3c75 +# black for astropy.constants +e789729540438eb891ae83ef1a39171f7a4c007a +# black for astropy.convolution +9d4adb8a5ab74a8c85e0d4b41e65bb78ee19aedb +# black for astropy.coordinates +fbbc0cb370d033722fef7f501b6aed4b500812d0 +# black for astropy.cosmology +420686db541271e8a20ad15e3a2b68bf30655889 +# black for astropy.io.ascii +6e80e68e817fd629e04e80b45478052b6a4a3c4a +# black for astropy.io.fits +6750c7f86129858b92d83b0086c3b99b9470bf8b +# black for astropy.io.misc +128fe2873f25581feda0c2d726181b093bdc306a +# black for atropy.io.votable +7ca4c9e44e762e258b3c455bdfca793b83a93802 +# black for other astropy.io +2e948e310fa2882ef3fd483b130297e3c692481f +# black for astropy.modeling +d8f7f53c7342e4e40c1a76351a3befa1f9b2f2db +# black for astropy.nddata +a7cb990203a9d2093fa81bbbbd4ac0f763f6da6b +# black for astropy.samp +3c9c91ae593cad2f669d65bf3e67accc5c49333c +# black for astropy.stats +ecfab5a9f56d5f5724f30a6dabe580960136e4c6 +# black for astropy.table +1403108bcd07341657e41d47c99abad373f27644 +# black for astropy.tests +369402b47317f4752e3dadaa136a1f8ccce74a2e +# black for astropy.time +fa2df9f32e5c041d74f7d1ef3d5a188c521cbc79 +# black for astropy.timeseries +059495d362003d3f88c1e7456bfe56693c958e10 +# black for astropy.uncertainty +58470fa36befa7efd1e37bbc7374859d7c22223a +# black for astropy.units +53ba687f90131a7b5a578af32b376b8fac04863a +# black for astropy.utils +0630d6acf23aab2ef338b35f9a5130ee592e7aa5 +# black for astropy.visualization +1265c1a9970d467c9372f9143b8cb0a1d37facf1 +# black for astropy.wcs +5bf365bd9638f3c335963745b46246fabe428a0f +# black for other parts of astropy +d2165fa02c1a2d79f4a6cb69a5cebeee0b073033 +# DOC: Moved dev docs around +f5a1738f32d4920ac853c7c0ec6c941ccec47555 +# clang-format for most sub-packages except astropy.wcs +933e3e6eadff215165c838fd91c21e8615d0cf88 +# clang-format for astropy.wcs +616797e13c0b926ad48ea9e9aac22a0421b74d73 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000000..9461903e124f --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +*.fits -text +astropy/io/fits/tiled_compression/tests/data/*fits binary diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 000000000000..c94945f28da7 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,66 @@ +# Despite the name of this file, the people listed here DO NOT OWN the code in astropy subpackages, +# the copyright is held by the Astropy Project as a whole - see the license file for details. +# +# This file exists because as of 2022, GitHub does not offer a mechanism to subscribe to only +# a fraction of new PRs in astropy. Either one chooses to "watch" the entire repository and +# receives a large number of notifications or one needs to manually check for new PRs for +# sub-package(s) of interest. +# +# All names listed here by sub-packages will be requested to review any new PR +# and thus get notified. Only people with maintainer-level permission can be added as +# PR reviewers. +# +# Instructions for core maintainers -- Add your GitHub username to opt-in to automatic +# review requests for specific sub-packages below: + +# astropy/constants +astropy/convolution @larrybradley +# astropy/coordinates +astropy/cosmology @astropy/cosmology +astropy/io/ascii @taldcroft @dhomeier +astropy/io/fits @saimn +astropy/io/misc @WilliamJamieson @matteobachetti @neutrinoceros +astropy/io/registry @nstarman @neutrinoceros +# astropy/io/votable +astropy/modeling @astropy/modeling +# astropy/nddata +# astropy/samp +astropy/stats @larrybradley +astropy/table @taldcroft @neutrinoceros +astropy/time @taldcroft +# astropy/timeseries +# astropy/uncertainty +# astropy/units +# astropy/utils +astropy/visualization @astrofrog @larrybradley +astropy/wcs @mcara +astropy/wcs/wcsapi @astrofrog + +# docs/constants +docs/convolution @larrybradley +# docs/coordinates +docs/cosmology @astropy/cosmology +docs/io/ascii @taldcroft @dhomeier +docs/io/fits @saimn +# docs/io/votable +docs/modeling @astropy/modeling +# docs/nddata +# docs/samp +docs/stats @larrybradley +docs/table @taldcroft @neutrinoceros +docs/time @taldcroft +# docs/timeseries +# docs/uncertainty +# docs/units +# docs/utils +docs/visualization @astrofrog @larrybradley +docs/wcs @mcara + +scripts/* @neutrinoceros +pyproject.toml @neutrinoceros +setup.py @neutrinoceros +MANIFEST.in @neutrinoceros +tox.ini @neutrinoceros +*.git* @neutrinoceros # git files and .github dir +.pre-commit-config.yaml @WilliamJamieson @nstarman @neutrinoceros +.ruff.toml @WilliamJamieson @nstarman @neutrinoceros diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml new file mode 100644 index 000000000000..8e4612ef90a7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -0,0 +1,51 @@ +name: Bug report +description: Create a report describing unexpected or incorrect behavior in astropy. +labels: Bug +body: + - type: markdown + attributes: + value: >- + Thanks for taking the time to fill out this bug report! + Please have a search on our GitHub repository to see if a similar + issue has already been posted. If a similar issue is closed, have a + quick look to see if you are satisfied by the resolution. + If not please go ahead and open an issue! + Please check that the + [development version](https://docs.astropy.org/en/latest/development/quickstart.html#install-the-development-version-of-astropy) + still produces the same bug. + - type: textarea + attributes: + label: Description + description: >- + A clear and concise description of what the bug is. + - type: textarea + attributes: + label: Expected behavior + description: >- + A clear and concise description of what you expected to happen. + - type: textarea + attributes: + label: How to Reproduce + description: >- + A clear and concise description of what actually happened instead. + Was the output confusing or poorly described? Please provide steps to reproduce this bug. + value: | + 1. Get package from '...' + 2. Then run '...' + 3. An error occurs. + + ```python + # Put your Python code snippet here. + ``` + - type: textarea + attributes: + label: Versions + description: Please run the following script and paste the output + value: | + ```python + import astropy + astropy.system_info() + ``` + ``` + # Paste the result here + ``` diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 000000000000..5c70a42877ce --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,4 @@ +contact_links: + - name: Question/Help/Support + url: https://www.astropy.org/help + about: "If you have a question, please look at the listed resources available on the website." diff --git a/.github/ISSUE_TEMPLATE/feature_request.yaml b/.github/ISSUE_TEMPLATE/feature_request.yaml new file mode 100644 index 000000000000..546cc83995ed --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yaml @@ -0,0 +1,32 @@ +name: Feature request +description: Suggest an idea to improve astropy. +labels: "Feature Request" +body: + - type: markdown + attributes: + value: >- + Thanks for taking the time to fill out this feature request! + Please have a search on our GitHub repository to see if a similar + issue has already been posted. If a similar issue is closed, have a + quick look to see if you are satisfied by the resolution. + If not please go ahead and open an issue! + - type: textarea + attributes: + label: What is the problem this feature will solve? + description: >- + What are you trying to do, that you are unable to achieve with astropy + and its affiliated packages as it currently stands? + - type: textarea + attributes: + label: Describe the desired outcome + description: >- + Clear and concise description of what you want to happen. Please use examples + of real world use cases that this would help with, and how it solves the + problem described above. If you want to, you can suggest a draft design or API + so we can have a deeper discussion on the feature. + - type: textarea + attributes: + label: Additional context + description: >- + Add any other context, links, etc. relevant to the feature request. + You may also include screenshots if necessary. diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000000..3f2b6b3dd23a --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,47 @@ + + + + + + + + + + +### Description + + + + + + +This pull request is to address ... + + + +Fixes # + + + +- [ ] By checking this box, the PR author has requested that maintainers do **NOT** use the "Squash and Merge" button. Maintainers should respect this when possible; however, the final decision is at the discretion of the maintainer that merges the PR. + + diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000000..2266623596cc --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,22 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: + - package-ecosystem: "github-actions" # See documentation for possible values + directory: ".github/workflows" # Location of package manifests + labels: + - dependencies + - dev-automation + - github_actions + - no-changelog-entry-needed + schedule: + interval: "monthly" + groups: + actions: + patterns: + - "*" + cooldown: + default-days: 7 diff --git a/.github/labeler.yml b/.github/labeler.yml new file mode 100644 index 000000000000..be4183f0c795 --- /dev/null +++ b/.github/labeler.yml @@ -0,0 +1,247 @@ +dependencies: +- any: + - head-branch: + - '^update-astropy-iers-data-pin-\d+$' + +Docs: +- changed-files: + - any-glob-to-any-file: + - docs/* + - docs/_static/* + - docs/_templates/* + - docs/development/**/* + - docs/whatsnew/* + - examples/**/* + - licenses/* + - CITATION + - CITATION.cff + - .mailmap + - .readthedocs.yaml + - '*.md' + - all-globs-to-any-file: + - '**/*.rst' + - '!CHANGES.rst' + - '!docs/changes/**/*' + +testing: +- changed-files: + - any-glob-to-any-file: + - astropy/tests/**/* + - codecov.yml + - conftest.py + - '**/conftest.py' + - tox.ini + - .circleci/* + - .github/workflows/CFF-test.yml + - .github/workflows/check_changelog.yml + - .github/workflows/ci*.yml + - .github/workflows/codeql-analysis.yml + - .pyinstaller/**/* + - .flake8 + - .pycodestyle + - .pre-commit-config.yaml + - .ruff.toml + +dev-automation: +- changed-files: + - any-glob-to-any-file: + - .pre-commit-config.yaml + - .ruff.toml + - .devcontainer/* + - .github/ISSUE_TEMPLATE/* + - .github/* + - .github/workflows/open_actions.yml + - .github/workflows/stalebot.yml + - .github/workflows/update_astropy_iers_data_pin.* + +skip-changelog-checks: +- changed-files: + - any-glob-to-any-file: + - .pre-commit-config.yaml + +external: +- changed-files: + - any-glob-to-any-file: + - astropy/extern/**/* + - cextern/**/* + +installation: +- changed-files: + - any-glob-to-any-file: + - docs/install.rst + - MANIFEST.in + - setup.py + +Release: +- changed-files: + - any-glob-to-any-file: + - docs/development/maintainers/releasing.rst + - .github/workflows/publish.yml + +config: +- changed-files: + - any-glob-to-any-file: + - '**/config/**/*' + - astropy/extern/configobj/**/* + +constants: +- changed-files: + - any-glob-to-any-file: + - '**/constants/**/*' + +convolution: +- changed-files: + - any-glob-to-any-file: + - '**/convolution/**/*' + +coordinates: +- changed-files: + - any-glob-to-any-file: + - '**/coordinates/**/*' + +cosmology: +- changed-files: + - any-glob-to-any-file: + - '**/cosmology/**/*' + +io.ascii: +- changed-files: + - any-glob-to-any-file: + - '**/io/ascii/**/*' + +io.fits: +- changed-files: + - any-glob-to-any-file: + - '**/io/fits/**/*' + - cextern/cfitsio/**/* + +io.misc: +- changed-files: + - any-glob-to-any-file: + - astropy/io/misc/* + - astropy/io/misc/pandas/**/* + - astropy/io/misc/tests/**/* + - docs/io/misc*.rst + +io.registry: +- changed-files: + - any-glob-to-any-file: + - astropy/io/* + - astropy/io/registry/**/* + - astropy/io/tests/* + - docs/io/registry*.rst + +io.votable: +- changed-files: + - any-glob-to-any-file: + - '**/io/votable/**/*' + +logging: +- changed-files: + - any-glob-to-any-file: + - astropy/logger.py + - astropy/tests/test_logger.py + - docs/logging.rst + +modeling: +- changed-files: + - any-glob-to-any-file: + - '**/modeling/**/*' + +nddata: +- changed-files: + - any-glob-to-any-file: + - '**/nddata/**/*' + +samp: +- changed-files: + - any-glob-to-any-file: + - '**/samp/**/*' + +stats: +- changed-files: + - any-glob-to-any-file: + - '**/stats/**/*' + +table: +- changed-files: + - any-glob-to-any-file: + - '**/table/**/*' + +time: +- changed-files: + - any-glob-to-any-file: + - '**/time/**/*' + +timeseries: +- changed-files: + - any-glob-to-any-file: + - '**/timeseries/**/*' + +uncertainty: +- changed-files: + - any-glob-to-any-file: + - '**/uncertainty/**/*' + +unified-io: +- changed-files: + - any-glob-to-any-file: + - astropy/table/connect.py + - astropy/io/**/connect.py + - docs/io/unified.rst + +units: +- changed-files: + - any-glob-to-any-file: + - '**/units/**/*' + - astropy/extern/ply/**/* + +utils: +- changed-files: + - any-glob-to-any-file: + - cextern/expat/**/* + - all-globs-to-any-file: + - '**/utils/**/*' + - '!astropy/utils/iers/**/*' + - '!docs/utils/iers.rst' + - '!astropy/utils/masked/**/*' + - '!docs/utils/masked/**/*' + +utils.iers: +- changed-files: + - any-glob-to-any-file: + - astropy/utils/iers/**/* + - docs/utils/iers.rst + - .github/workflows/update_astropy_iers_data_pin.* + +utils.masked: +- changed-files: + - any-glob-to-any-file: + - astropy/utils/masked/**/* + - docs/utils/masked/**/* + +visualization: +- changed-files: + - all-globs-to-any-file: + - '**/visualization/**/*' + - '!**/visualization/wcsaxes/**/*' + +visualization.wcsaxes: +- changed-files: + - any-glob-to-any-file: + - '**/visualization/wcsaxes/**/*' + +wcs: +- changed-files: + - any-glob-to-any-file: + - cextern/wcslib/**/* + - all-globs-to-any-file: + - '**/wcs/**/*' + - '!astropy/wcs/wcsapi/**/*' + - '!docs/wcs/wcsapi.rst' + +wcs.wcsapi: +- changed-files: + - any-glob-to-any-file: + - astropy/wcs/wcsapi/**/* + - docs/wcs/wcsapi.rst diff --git a/.github/workflows/CFF-test.yml b/.github/workflows/CFF-test.yml new file mode 100644 index 000000000000..723a27e46cd6 --- /dev/null +++ b/.github/workflows/CFF-test.yml @@ -0,0 +1,27 @@ +name: Checking CITATION.cff + +on: + push: + paths: + - "CITATION.cff" + pull_request: + paths: + - "CITATION.cff" + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + cffconvert: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + - uses: citation-file-format/cffconvert-github-action@4cf11baa70a673bfdf9dad0acc7ee33b3f4b6084 # 2.0.0 + with: + args: --validate diff --git a/.github/workflows/check_changelog.yml b/.github/workflows/check_changelog.yml new file mode 100644 index 000000000000..4cf23b18fe2b --- /dev/null +++ b/.github/workflows/check_changelog.yml @@ -0,0 +1,26 @@ +name: Check PR change log + +on: + pull_request: + types: [opened, synchronize, labeled, unlabeled] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + pull-requests: read + +jobs: + changelog_checker: + name: Check if towncrier change log entry is correct + runs-on: ubuntu-latest + if: github.repository == 'astropy/astropy' + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + - uses: scientific-python/action-towncrier-changelog@f9c7df9a9f8b55cb0c12d94d14b281e9bcd101c0 # v2.0.0 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + BOT_USERNAME: gilesbot diff --git a/.github/workflows/check_milestone.yml b/.github/workflows/check_milestone.yml new file mode 100644 index 000000000000..9c852f41d393 --- /dev/null +++ b/.github/workflows/check_milestone.yml @@ -0,0 +1,33 @@ +name: Check PR milestone + +on: + pull_request: + types: [synchronize, milestoned, demilestoned] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + pull-requests: read + +jobs: + # https://stackoverflow.com/questions/69434370/how-can-i-get-the-latest-pr-data-specifically-milestones-when-running-yaml-jobs + milestone_checker: + runs-on: ubuntu-latest + steps: + - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + if: github.repository == 'astropy/astropy' + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const { data } = await github.request("GET /repos/{owner}/{repo}/pulls/{pr}", { + owner: context.repo.owner, + repo: context.repo.repo, + pr: context.payload.pull_request.number + }); + if (data.milestone) { + core.info(`This pull request has a milestone set: ${data.milestone.title}`); + } else { + core.setFailed(`A maintainer needs to set the milestone for this pull request.`); + } diff --git a/.github/workflows/ci_benchmark.yml b/.github/workflows/ci_benchmark.yml new file mode 100644 index 000000000000..11af0b70e681 --- /dev/null +++ b/.github/workflows/ci_benchmark.yml @@ -0,0 +1,101 @@ +### Inspired from https://github.com/scikit-image/scikit-image/blob/main/.github/workflows/benchmarks.yml + +name: Benchmark + +on: + pull_request: + types: [labeled, synchronize] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + benchmark: + if: (github.repository == 'astropy/astropy' && contains(github.event.pull_request.labels.*.name, 'benchmark')) + name: "Compare asv with astropy main" + runs-on: ubuntu-latest + env: + CCACHE_BASEDIR: "${{ github.workspace }}" + CCACHE_DIR: "${{ github.workspace }}/.ccache" + CCACHE_COMPRESS: true + CCACHE_COMPRESSLEVEL: 6 + CCACHE_MAXSIZE: 400M + + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + fetch-depth: 0 + + - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + name: Install Python + with: + python-version: "3.11" + + - name: Setup some dependencies + shell: bash -l {0} + run: | + sudo apt-get update -y && sudo apt-get install -y ccache + # Make gcc/gxx symlinks first in path + sudo /usr/sbin/update-ccache-symlinks + echo "/usr/lib/ccache" >> $GITHUB_PATH + + - name: "Prepare ccache" + id: prepare-ccache + shell: bash -l {0} + run: | + echo "key=benchmark-$RUNNER_OS" >> $GITHUB_OUTPUT + echo "timestamp=$(date +%Y%m%d-%H%M%S)" >> $GITHUB_OUTPUT + ccache -p + ccache -z + + - name: "Restore ccache" + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + with: + path: .ccache + key: ccache-${{ secrets.CACHE_VERSION }}-${{ steps.prepare-ccache.outputs.key }}-${{ steps.prepare-ccache.outputs.timestamp }} + restore-keys: | + ccache-${{ secrets.CACHE_VERSION }}-${{ steps.prepare-ccache.outputs.key }}- + + - name: Run benchmarks + shell: bash -l {0} + id: benchmark + env: + OPENBLAS_NUM_THREADS: 1 + MKL_NUM_THREADS: 1 + OMP_NUM_THREADS: 1 + ASV_FACTOR: 1.3 + ASV_SKIP_SLOW: 1 + BASE_SHA: ${{ github.event.pull_request.base.sha }} + BASE_LABEL: ${{ github.event.pull_request.base.label }} + run: | + set -x + python -m pip install asv virtualenv packaging + + git clone -b main https://github.com/astropy/astropy-benchmarks.git --single-branch + + # ID this runner + python -m asv machine --yes --conf asv.ci.conf.json + + echo "Baseline: ${BASE_SHA} (${BASE_LABEL})" + echo "Contender: ${GITHUB_SHA} (${BASE_LABEL})" + + # Run benchmarks for current commit against base + ASV_OPTIONS="--split --show-stderr --factor $ASV_FACTOR --conf asv.ci.conf.json" + python -m asv continuous $ASV_OPTIONS ${BASE_SHA} ${GITHUB_SHA} + + - name: "Check ccache performance" + shell: bash -l {0} + run: ccache -s + if: always() + + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + if: always() + with: + name: asv-benchmark-results + path: | + results/ diff --git a/.github/workflows/ci_cron_daily.yml b/.github/workflows/ci_cron_daily.yml new file mode 100644 index 000000000000..e00dffaa6406 --- /dev/null +++ b/.github/workflows/ci_cron_daily.yml @@ -0,0 +1,84 @@ +name: Daily cron + +on: + workflow_dispatch: + schedule: + # run every day at 3am UTC + - cron: '0 3 * * *' + pull_request: + # We also want this workflow triggered if the 'Extra CI' label is added + # or present when PR is updated + types: + - synchronize + - labeled + push: + # We want this workflow to always run on release branches as well as + # all tags since we want to be really sure we don't introduce + # regressions on the release branches, and it's also important to run + # this on pre-release and release tags. + branches: + - 'v*' + tags: + - '*' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + ARCH_ON_CI: "normal" + IS_CRON: "true" + PY_COLORS: "1" + +permissions: + contents: read + +jobs: + tests: + runs-on: ${{ matrix.os }} + if: | + github.repository == 'astropy/astropy' && ( + github.event_name == 'schedule' || + github.event_name == 'push' || + github.event_name == 'workflow_dispatch' || + contains(github.event.pull_request.labels.*.name, 'Extra CI') + ) + strategy: + fail-fast: false + matrix: + include: + - name: Bundling with pyinstaller + os: ubuntu-latest + python: '3.11' + toxenv: pyinstaller + + - name: Python 3.12 with all optional dependencies and pre-releases + os: ubuntu-latest + python: '3.12' + toxenv: py312-test-alldeps-predeps + toxargs: -v + toxposargs: --remote-data=any + + steps: + - name: Checkout code + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + fetch-depth: 0 + + - name: Set up Python + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + with: + python-version: ${{ matrix.python }} + + - name: Install language-pack-de and tzdata + if: ${{ matrix.os == 'ubuntu-latest' }} + run: | + sudo apt-get update + sudo apt-get install language-pack-de tzdata + + - name: Install Python dependencies + run: python -m pip install --upgrade tox + + - name: Run tests + run: tox ${{ matrix.toxargs}} -e ${{ matrix.toxenv}} -- ${{ matrix.toxposargs}} diff --git a/.github/workflows/ci_cron_monthly.yml b/.github/workflows/ci_cron_monthly.yml new file mode 100644 index 000000000000..1ee9939ae692 --- /dev/null +++ b/.github/workflows/ci_cron_monthly.yml @@ -0,0 +1,105 @@ +name: Monthly cron + +on: + workflow_dispatch: + schedule: + # run every first of month at 6am UTC + - cron: '0 6 1 * *' + pull_request: + # We also want this workflow triggered if the 'Extra CI' label is added + # or present when PR is updated + types: + - synchronize + - labeled + push: + # We want this workflow to always run on release branches as well as + # all tags since we want to be really sure we don't introduce + # regressions on the release branches, and it's also important to run + # this on pre-release and release tags. + branches: + - 'v*' + tags: + - '*' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + IS_CRON: 'true' + PY_COLORS: "1" + +permissions: + contents: read + +jobs: + + tests_more_arch_bundled_lib: + + # Testing exotic architectures using bundled external libs, i.e. without setting ASTROPY_USE_SYSTEM_ALL. + # This is a variant and subset of weekly cron, currently only including the big endian s390x arch. + + runs-on: ubuntu-24.04 + name: Emulated arch bundled lib + # keep condition in sync with test_arm64 + if: | + github.repository == 'astropy/astropy' && ( + github.event_name == 'schedule' || + github.event_name == 'push' || + github.event_name == 'workflow_dispatch' || + contains(github.event.pull_request.labels.*.name, 'Extra CI') + ) + env: + ARCH_ON_CI: ${{ matrix.arch }} + + strategy: + fail-fast: false + matrix: + include: + - arch: s390x + + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + fetch-depth: 0 + - uses: uraimo/run-on-arch-action@d94c13912ea685de38fccc1109385b83fd79427d # v3.0.1 + name: Run tests + id: build + with: + arch: ${{ matrix.arch }} + distro: ubuntu_rolling + + shell: /bin/bash + env: | + ARCH_ON_CI: ${{ env.ARCH_ON_CI }} + IS_CRON: ${{ env.IS_CRON }} + + install: | + apt-get update -q -y + apt-get install -q -y gnupg2 + apt-get update -q -y + apt-get install -q -y --no-install-recommends \ + git \ + g++ \ + pkg-config \ + cython3 \ + python3 \ + python3-erfa \ + python3-extension-helpers \ + python3-jinja2 \ + python3-numpy \ + python3-pytest-astropy \ + python3-setuptools-scm \ + python3-yaml \ + python3-venv \ + python3-wheel + + run: | + uname -a + echo "LONG_BIT="$(getconf LONG_BIT) + python3 -m venv --system-site-packages tests + source tests/bin/activate + pip install -v --no-build-isolation -e .[test] + pip list + python3 -m pytest --strict-markers --pyargs astropy -m "not hypothesis" diff --git a/.github/workflows/ci_cron_weekly.yml b/.github/workflows/ci_cron_weekly.yml new file mode 100644 index 000000000000..8e2bbea50bff --- /dev/null +++ b/.github/workflows/ci_cron_weekly.yml @@ -0,0 +1,198 @@ +name: Weekly cron + +on: + workflow_dispatch: + schedule: + # run every Monday at 6am UTC + - cron: '0 6 * * 1' + pull_request: + # We also want this workflow triggered if the 'Extra CI' label is added + # or present when PR is updated + types: + - synchronize + - labeled + push: + # We want this workflow to always run on release branches as well as + # all tags since we want to be really sure we don't introduce + # regressions on the release branches, and it's also important to run + # this on pre-release and release tags. + branches: + - 'v*' + tags: + - '*' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + IS_CRON: 'true' + PY_COLORS: "1" + +permissions: + contents: read + +jobs: + tests: + runs-on: ${{ matrix.os }} + if: | + github.repository == 'astropy/astropy' && ( + github.event_name == 'schedule' || + github.event_name == 'push' || + github.event_name == 'workflow_dispatch' || + contains(github.event.pull_request.labels.*.name, 'Extra CI') + ) + env: + ARCH_ON_CI: "normal" + strategy: + fail-fast: false + matrix: + include: + + # We check numpy-dev also in a job that only runs from cron, so that + # we can spot issues sooner. We do not use remote data here, since + # that gives too many false positives due to URL timeouts. We also + # install all dependencies via pip here so we pick up the latest + # releases. + - name: Python 3.11 with dev version of key dependencies + os: ubuntu-latest + python: '3.11' + toxenv: py311-test-devdeps + + # https://github.com/astropy/astropy/issues/15701 + - name: Python 3.11 with dev version of key dependencies without scipy + os: ubuntu-latest + python: '3.11' + toxenv: py311-test-devdeps-noscipy + + - name: Python 3.14 with dev version of infrastructure dependencies + os: ubuntu-latest + python: '3.14' + toxenv: py314-test-devpytest + + - name: Python 3.14t (free-threading) with recommended dependencies + os: ubuntu-latest + python: '3.14t' + toxenv: py314t-test-recdeps + PYTHON_GIL: 0 + # https://github.com/astropy/astropy/issues/19205 + PYTHON_CONTEXT_AWARE_WARNINGS: 0 + + - name: Documentation link check + os: ubuntu-latest + python: '3.x' + toxenv: linkcheck + toxposargs: --color + + steps: + - name: Checkout code + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + fetch-depth: 0 + - name: Set up Python + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + with: + python-version: ${{ matrix.python }} + - name: Install language-pack-de and tzdata + if: ${{ matrix.os == 'ubuntu-latest' }} + run: | + sudo apt-get update + sudo apt-get install language-pack-de tzdata + - name: Install graphviz + if: ${{ matrix.toxenv == 'linkcheck' }} + run: sudo apt-get install graphviz + - name: Install Python dependencies + run: python -m pip install --upgrade tox + - name: Run tests + run: tox ${{ matrix.toxargs}} -e ${{ matrix.toxenv}} -- ${{ matrix.toxposargs}} + env: + PYTHON_GIL: ${{ matrix.PYTHON_GIL }} + PYTHON_CONTEXT_AWARE_WARNINGS: ${{ matrix.PYTHON_CONTEXT_AWARE_WARNINGS }} + + + tests_more_architectures: + + # The following architectures are emulated and are therefore slow, so + # we include them just in the weekly cron. These also serve as a test + # of using system libraries and using pytest directly. + # No doctest run here due to architecture differences in some outputs + # (e.g., big-endian or 32-bit OS) with numpy 2. + + runs-on: ubuntu-24.04 + name: Emulated arch + # keep condition in sync with test_arm64 + if: | + github.repository == 'astropy/astropy' && ( + github.event_name == 'schedule' || + github.event_name == 'push' || + github.event_name == 'workflow_dispatch' || + contains(github.event.pull_request.labels.*.name, 'Extra CI') + ) + env: + ARCH_ON_CI: ${{ matrix.arch }} + + strategy: + fail-fast: false + matrix: + include: + - arch: s390x + - arch: ppc64le + - arch: armv7 + + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + fetch-depth: 0 + - uses: uraimo/run-on-arch-action@d94c13912ea685de38fccc1109385b83fd79427d # v3.0.1 + name: Run tests + id: build + with: + arch: ${{ matrix.arch }} + distro: ubuntu_rolling + + shell: /bin/bash + env: | + ARCH_ON_CI: ${{ env.ARCH_ON_CI }} + IS_CRON: ${{ env.IS_CRON }} + + install: | + apt-get update -q -y + apt-get install -q -y gnupg2 + # Add test-support repository for wcslib8 + echo "deb http://ppa.launchpadcontent.net/astropy/test-support/ubuntu lunar main" > /etc/apt/sources.list.d/test-support.list + gpg --keyserver hkp://keyserver.ubuntu.com:80 --recv CC75F07B3EF41EFC + gpg --export --armor CC75F07B3EF41EFC | tee /etc/apt/trusted.gpg.d/test-support.asc + apt-get update -q -y + apt-get install -q -y --no-install-recommends \ + git \ + g++ \ + pkg-config \ + python3 \ + python3-erfa \ + python3-extension-helpers \ + python3-jinja2 \ + python3-numpy \ + python3-pytest-astropy \ + python3-setuptools-scm \ + python3-yaml \ + python3-venv \ + python3-wheel \ + wcslib-dev + + run: | + uname -a + echo "LONG_BIT="$(getconf LONG_BIT) + python3 -m venv --system-site-packages tests + source tests/bin/activate + # cython and pyerfa versions in ubuntu repos are too old currently + pip install -U cython setuptools packaging + pip install -U --no-build-isolation pyerfa + ASTROPY_USE_SYSTEM_ALL=1 pip install -v --no-build-isolation -e .[test] + pip list + # A fresh CI runner has no ~/.astropy directory, but developer + # machines usually do. Create empty cache and config directories + # so we catch failures that only show up when they already exist. + python3 -c "from pathlib import Path; [(Path.home() / '.astropy' / d).mkdir(parents=True, exist_ok=True) for d in ('cache', 'config')]" + python3 -m pytest --strict-markers --pyargs astropy -m "not hypothesis" diff --git a/.github/workflows/ci_workflows.yml b/.github/workflows/ci_workflows.yml new file mode 100644 index 000000000000..5a615f98f421 --- /dev/null +++ b/.github/workflows/ci_workflows.yml @@ -0,0 +1,217 @@ +name: CI + +on: + push: + branches: + - main + - 'v*' + tags: + - '*' + pull_request: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + ARCH_ON_CI: "normal" + IS_CRON: "false" + +permissions: + contents: read + +jobs: + initial_checks: + name: Mandatory checks before CI + runs-on: ubuntu-latest + steps: + - name: Check base branch + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + if: github.event_name == 'pull_request' && github.repository == 'astropy/astropy' + with: + script: | + const skip_label = 'skip-basebranch-check'; + const { default_branch: allowed_basebranch } = context.payload.repository; + const pr = context.payload.pull_request; + if (pr.user.login === 'meeseeksmachine') { + core.info(`Base branch check is skipped since this is auto-backport by ${pr.user.login}`); + return; + } + if (pr.labels.find(lbl => lbl.name === skip_label)) { + core.info(`Base branch check is skipped due to the presence of ${skip_label} label`); + return; + } + if (pr.base.ref !== allowed_basebranch) { + core.setFailed(`PR opened against ${pr.base.ref}, not ${allowed_basebranch}`); + } else { + core.info(`PR opened correctly against ${allowed_basebranch}`); + } + + lowest-tree-check: + name: Check environment lowest resolution against reference + needs: [initial_checks] + permissions: + contents: none + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + fetch-depth: 0 + - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + name: Install Python + with: + python-version: "3.14" + - run: pipx run scripts/check-lowest-resolved-tree.py + + - if: failure() + run: pipx run scripts/check-lowest-resolved-tree.py --markdown --quiet >> "$GITHUB_STEP_SUMMARY" || true + + tests: + needs: [initial_checks] + uses: OpenAstronomy/github-actions-workflows/.github/workflows/tox.yml@2835f0cacddf3f8de198db9afdb5354a5cebe0ef # v2.6.3 + secrets: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + with: + setenv: | + ARCH_ON_CI: "normal" + IS_CRON: "false" + PY_COLORS: "1" + submodules: false + coverage: '' + libraries: | + apt: + - language-pack-fr + - tzdata + + envs: | + # NOTE: this coverage test is needed for tests and code that + # run only with minimal dependencies. + - name: Python 3.12 with minimal dependencies and full coverage + linux: py312-test-cov + coverage: codecov + cache-path: .tox + cache-key: mindeps-${{ github.ref_name }} + + - name: Python 3.13 in Parallel with all optional dependencies + linux: py313-test-alldeps-fitsio + libraries: + apt: + - language-pack-fr + - tzdata + - libbz2-dev + - libcfitsio-dev + toxargs: -v --develop + posargs: -n=4 --run-slow + cache-path: .tox + cache-key: alldeps-linux-${{ github.ref_name }} + + - name: Python 3.11 with oldest supported version of all dependencies + linux: py311-test-oldestdeps-alldeps-cov-clocale + posargs: --remote-data=astropy + coverage: codecov + + - name: Python 3.13 with all optional dependencies (Windows) + windows: py313-test-alldeps + posargs: --durations=50 + cache-path: .tox + cache-key: alldeps-windows-${{ github.ref_name }} + + - name: Python 3.13 with all optional dependencies (MacOS X) + macos: py313-test-alldeps + posargs: --durations=50 --run-slow + runs-on: macos-latest + cache-path: .tox + cache-key: alldeps-macos-${{ github.ref_name }} + + - name: Python 3.13 aarch64, Run tests twice + linux: py313-test-double + runs-on: ubuntu-24.04-arm + posargs: -n=4 + setenv: | + ARCH_ON_CI: "arm64" + IS_CRON: "false" + cache-path: .tox + cache-key: doubletest-${{ github.ref_name }} + + allowed_failures: + needs: [initial_checks] + uses: OpenAstronomy/github-actions-workflows/.github/workflows/tox.yml@2835f0cacddf3f8de198db9afdb5354a5cebe0ef # v2.6.3 + with: + setenv: | + ARCH_ON_CI: "normal" + IS_CRON: "false" + PY_COLORS: "1" + submodules: false + coverage: '' + libraries: | + apt: + - language-pack-de + - tzdata + envs: | + - name: (Allowed Failure) Python 3.14 with remote data and dev version of key dependencies + linux: py314-test-devdeps + posargs: --remote-data=any --verbose + + stub_tests: + needs: [initial_checks] + uses: OpenAstronomy/github-actions-workflows/.github/workflows/tox.yml@2835f0cacddf3f8de198db9afdb5354a5cebe0ef # v2.6.3 + with: + setenv: | + ARCH_ON_CI: "normal" + IS_CRON: "false" + submodules: false + envs: | + - name: Stubs for unit definition modules + linux: stubtest + cache-path: .tox + cache-key: stub_tests-${{ github.ref_name }} + + test_wheel_building: + needs: + - initial_checks + - lowest-tree-check + # This ensures that a couple of wheel targets work fine in pull requests and pushes + permissions: + contents: none + uses: OpenAstronomy/github-actions-workflows/.github/workflows/publish.yml@2835f0cacddf3f8de198db9afdb5354a5cebe0ef # v2.6.3 + with: + upload_to_pypi: false + upload_to_anaconda: false + test_extras: test + test_command: pytest -Wdefault --astropy-header -m "not hypothesis" -k "not test_data_out_of_range and not test_set_locale and not TestQuantityTyping" --strict-markers --pyargs astropy + targets: | + - cp311-manylinux_x86_64 + + + test_limited_api_build: + # Test to make sure that we can build astropy with the limited API + # This job is also important to check `build` as our build frontend + # see https://github.com/astropy/astropy/pull/18253 + strategy: + fail-fast: false + matrix: + os: + - ubuntu-latest + - macos-latest + - windows-latest + runs-on: ${{ matrix.os }} + name: Python 3.11 (build only, partial) with limited API (${{ matrix.os }}) + needs: + - initial_checks + - lowest-tree-check + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + fetch-depth: 0 + - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + name: Install Python + with: + python-version: "3.11" + - run: | + python -m pip install build + python -m build + name: Run build + env: + EXTENSION_HELPERS_PY_LIMITED_API: 'cp311' diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 000000000000..25b5122a9374 --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,83 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL" + +on: + schedule: + # run every Wednesday at 6am UTC + - cron: '0 6 * * 3' + +permissions: + contents: read + +jobs: + analyze: + permissions: + actions: read # for github/codeql-action/init to get workflow details + contents: read # for actions/checkout to fetch code + security-events: write # for github/codeql-action/autobuild to send a status report + name: Analyze + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + language: ['cpp', 'python'] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] + # Learn more: + # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed + + steps: + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + fetch-depth: 0 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@5618c9fc1e675841ca52c1c6b1304f5255a905a0 # codeql-bundle-v2.19.0 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + # queries: ./path/to/local/query, your-org/your-repo/queries@main + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + if: matrix.language != 'cpp' + uses: github/codeql-action/autobuild@5618c9fc1e675841ca52c1c6b1304f5255a905a0 # codeql-bundle-v2.19.0 + + # â„šī¸ Command-line programs to run using the OS shell. + # 📚 https://git.io/JvXDl + + # âœī¸ If the Autobuild fails above, remove it and uncomment the following three lines + # and modify them (or add more) to build your code if your project + # uses a compiled language + + - name: Set up Python + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + if: matrix.language == 'cpp' + with: + python-version: '3.11' + + - name: Manual build + if: matrix.language == 'cpp' + run: | + pip install -U pip setuptools_scm setuptools packaging wheel + pip install extension-helpers cython numpy pyerfa + python setup.py build_ext --inplace + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@5618c9fc1e675841ca52c1c6b1304f5255a905a0 # codeql-bundle-v2.19.0 diff --git a/.github/workflows/open_actions.yml b/.github/workflows/open_actions.yml new file mode 100644 index 000000000000..ac9c0cc2d12a --- /dev/null +++ b/.github/workflows/open_actions.yml @@ -0,0 +1,54 @@ +name: "When Opened" + +on: + issues: + types: + - opened + # pull_request_target is only dangerous if combined with an explicit checkout + # of the opener's code, which it isn't here. See + # https://securitylab.github.com/resources/github-actions-preventing-pwn-requests/ + pull_request_target: # zizmor: ignore[dangerous-triggers] + types: + - opened + +permissions: + pull-requests: write + +jobs: + triage: + runs-on: ubuntu-latest + steps: + - name: Label PR + uses: actions/labeler@634933edcd8ababfe52f92936142cc22ac488b1b # v6.0.1 + if: | + github.event_name == 'pull_request_target' && + github.event.pull_request.user.login != 'meeseeksmachine' + with: + repo-token: "${{ secrets.GITHUB_TOKEN }}" + - name: 'Reviewer Checklist' + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + if: github.event_name == 'pull_request_target' + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + await github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: `Thank you for your contribution to Astropy! 🌌 This checklist is meant to remind the package maintainers who will review this pull request of some common things to look for. + + - [ ] Do the proposed changes actually accomplish desired goals? + - [ ] Do the proposed changes follow the [Astropy coding guidelines](https://docs.astropy.org/en/latest/development/codeguide.html)? + - [ ] Are tests added/updated as required? If so, do they follow the [Astropy testing guidelines](https://docs.astropy.org/en/latest/development/testguide.html)? + - [ ] Are docs added/updated as required? If so, do they follow the [Astropy documentation guidelines](https://docs.astropy.org/en/latest/development/docguide.html)? + - [ ] Is rebase and/or squash necessary? If so, please provide the author with appropriate instructions. Also see instructions for [rebase](https://docs.astropy.org/en/latest/development/development_details.html#rebase-if-necessary) and [squash](https://docs.astropy.org/en/latest/development/development_details.html#squash-if-necessary). + - [ ] Did the CI pass? If no, are the failures related? If you need to run daily and weekly cron jobs as part of the PR, please apply the "Extra CI" label. Codestyle issues can be fixed by the [bot](https://docs.astropy.org/en/latest/development/development_details.html#pre-commit). + - [ ] Is a change log needed? If yes, did the change log check pass? If no, add the "no-changelog-entry-needed" label. If this is a manual backport, use the "skip-changelog-checks" label unless special changelog handling is necessary. + - [ ] Is this a big PR that makes a "What's new?" entry worthwhile and if so, is (1) a "what's new" entry included in this PR and (2) the "whatsnew-needed" label applied? + - [ ] At the time of adding the milestone, if the milestone set requires a backport to release branch(es), apply the appropriate "backport-X.Y.x" label(s) *before* merge.` + }) + # Special action for a special day. Until next year! + #- name: Special comment + # uses: pllim/action-special_pr_comment@5126c189c02418a55448480b28efd1a00af48d7b # 0.2 + # with: + # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 000000000000..5d16d356d333 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,94 @@ +name: Wheel building + +on: + schedule: + # run every day at 4am UTC + - cron: '0 4 * * *' + workflow_dispatch: + push: + pull_request: + # We also want this workflow triggered if the 'Build all wheels' label is added + # or present when PR is updated + types: + - synchronize + - labeled + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + build: + # This does the actual wheel building as part of the cron job + # or if triggered manually via the workflow dispatch, or for a tag. + permissions: + contents: none + uses: OpenAstronomy/github-actions-workflows/.github/workflows/publish.yml@2835f0cacddf3f8de198db9afdb5354a5cebe0ef # v2.6.3 + if: | + github.repository == 'astropy/astropy' && ( + startsWith(github.ref, 'refs/tags/v') || + github.event_name == 'schedule' || + github.event_name == 'workflow_dispatch' || + contains(github.event.pull_request.labels.*.name, 'Build all wheels') + ) + with: + + # We use trusted publishing so the upload is handled in a separate job below + upload_to_pypi: false + save_artifacts: true + upload_to_anaconda: ${{ (github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') }} + anaconda_user: astropy + anaconda_package: astropy + anaconda_keep_n_latest: 10 + env: | + EXTENSION_HELPERS_PY_LIMITED_API: 'cp311' + + test_extras: test + # FIXME: we exclude the test_data_out_of_range test since it + # currently fails, see https://github.com/astropy/astropy/issues/10409 + # We also exclude test_set_locale as it sometimes relies on the correct locale + # packages being installed, which it isn't always. + test_command: pytest -Wdefault --astropy-header -m "not hypothesis" -k "not test_data_out_of_range and not test_set_locale and not TestQuantityTyping" --strict-markers --pyargs astropy + targets: | + # Linux wheels + - cp3*-manylinux_x86_64 + - target: cp3*-manylinux_aarch64 + runs-on: ubuntu-24.04-arm + + # Note that following wheels are not currently tested: + - cp3*-musllinux_x86_64 + + # MacOS X wheels - as noted in https://github.com/astropy/astropy/pull/12379 we deliberately + # do not build universal2 wheels. + - cp3*-macosx_x86_64 + - cp3*-macosx_arm64 + + # Windows wheels + - cp3*-win32 + - cp3*-win_amd64 + - cp3*-win_arm64 + + secrets: + anaconda_token: ${{ secrets.anaconda_token }} + + upload: + # Upload to PyPI using trusted publishing for all tags starting with v but not ones ending in .dev + if: startsWith(github.ref, 'refs/tags/v') && !endsWith(github.ref, '.dev') && github.event_name == 'push' + name: Upload release to PyPI + runs-on: ubuntu-latest + needs: [build] + environment: pypi + permissions: + id-token: write + steps: + - name: Download artifacts + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + merge-multiple: true + pattern: dist-* + path: dist + - name: Upload to PyPI + uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0 diff --git a/.github/workflows/sphinx_rc_workflows.yml b/.github/workflows/sphinx_rc_workflows.yml new file mode 100644 index 000000000000..662170a467fe --- /dev/null +++ b/.github/workflows/sphinx_rc_workflows.yml @@ -0,0 +1,35 @@ +name: Sphinx RC testing + + # We do not run this from cron as the Sphinx RC period is very short, but do trigger it manually when necessary. +on: + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + tests: + uses: OpenAstronomy/github-actions-workflows/.github/workflows/tox.yml@2835f0cacddf3f8de198db9afdb5354a5cebe0ef # v2.6.3 + secrets: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + with: + setenv: | + ARCH_ON_CI: "normal" + IS_CRON: "false" + PY_COLORS: "1" + submodules: false + coverage: '' + libraries: | + apt: + - graphviz + envs: | + - name: CI with upstream RC including docs dependencies if available + linux: py313-test-docdeps-predeps + + - name: Build docs with Sphinx RC if available + linux: build_docs-predeps + python-version: '3.13' diff --git a/.github/workflows/stalebot.yml b/.github/workflows/stalebot.yml new file mode 100644 index 000000000000..87078a8ef621 --- /dev/null +++ b/.github/workflows/stalebot.yml @@ -0,0 +1,23 @@ +name: Astropy stalebot + +on: + schedule: + # * is a special character in YAML so you have to quote this string + # run every day at 5:30 am UTC + - cron: '30 5 * * *' + workflow_dispatch: + +permissions: + pull-requests: write + +jobs: + stalebot: + runs-on: ubuntu-latest + if: github.repository == 'astropy/astropy' + steps: + - uses: pllim/action-astropy-stalebot@8a20289632f0d5068e9c129360b27b0a35e4d256 # main + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + STALEBOT_MAX_ISSUES: -1 + STALEBOT_MAX_PRS: -1 + STALEBOT_SLEEP: 10 diff --git a/.github/workflows/update_astropy_iers_data_main.yml b/.github/workflows/update_astropy_iers_data_main.yml new file mode 100644 index 000000000000..950ef7e2b9e9 --- /dev/null +++ b/.github/workflows/update_astropy_iers_data_main.yml @@ -0,0 +1,36 @@ +# Due to a limitation of Github Actions where the `schedule` trigger +# only applies to the default branch, we need a way of triggering the +# jobs on the other branches. +# +# Therefore, this workflow triggers on main, and dispatches to other branches. +# +# This is adapted from +# https://github.com/sunpy/sunpy/blob/main/.github/workflows/scheduled_builds.yml + +name: Scheduled astropy-iers-data auto-update + +on: + workflow_dispatch: + schedule: + - cron: '0 0 2 * *' # Monthly + +permissions: {} + +jobs: + dispatch_release_branches: + if: github.repository == 'astropy/astropy' + permissions: + actions: write + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + - branch: "main" + - branch: "v7.2.x" + steps: + - name: Trigger workflow dispatch for auto-update + uses: benc-uk/workflow-dispatch@7a027648b88c2413826b6ddd6c76114894dc5ec4 # v1.3.1 + with: + workflow: "Auto-update astropy-iers-data minimum version" + ref: "${{ matrix.branch }}" diff --git a/.github/workflows/update_astropy_iers_data_pin.md b/.github/workflows/update_astropy_iers_data_pin.md new file mode 100644 index 000000000000..9d08b9ae0a17 --- /dev/null +++ b/.github/workflows/update_astropy_iers_data_pin.md @@ -0,0 +1,5 @@ +This is an automated update of the minimum version of astropy-iers-data package. + +One pull request per active branch would be opened separately. Please apply proper milestone to each of them. + +:warning: Please close and re-open this pull request to trigger the CI. :warning: \ No newline at end of file diff --git a/.github/workflows/update_astropy_iers_data_pin.yml b/.github/workflows/update_astropy_iers_data_pin.yml new file mode 100644 index 000000000000..98c9f818a42e --- /dev/null +++ b/.github/workflows/update_astropy_iers_data_pin.yml @@ -0,0 +1,61 @@ +# Regularly update the minimum version of astropy-iers-data in pyproject.toml, +# to ensure that if users update astropy, astropy-iers-data will also get +# updated to a recent version. + +name: Auto-update astropy-iers-data minimum version + +on: + workflow_dispatch: + +permissions: + contents: read + +jobs: + + update-astropy-iers-data-pin: + permissions: + contents: write # to create a branch + pull-requests: write # to create a PR + name: Auto-update astropy-iers-data minimum version + runs-on: ubuntu-latest + if: github.repository == 'astropy/astropy' + steps: + - name: Checkout code + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: true # needed for git push + fetch-depth: 0 + - name: Set up uv + uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 + + # we take advantage of uv add's ability to set a lower bound on a new dependency + # by first removing it (simulating astropy-iers-data being "new") and re-adding it. + # --bounds=lower is passed in order to make the default behavior extra explicit + - name: upgrade astropy-iers-data + run: | + uv remove astropy-iers-data --no-sync + uv add astropy-iers-data --no-sync --bounds=lower + uv run scripts/check-lowest-resolved-tree.py --overwrite + + - name: Commit changes + run: | + git config user.name github-actions + git config user.email github-actions@github.com + BRANCH_NAME=update-astropy-iers-data-pin-$(date +"%s") + git switch -c "$BRANCH_NAME" + git add --update + if ! git diff --cached --exit-code; then + git commit -m "Update minimum required version of astropy-iers-data" + fi + git push --set-upstream origin "$BRANCH_NAME" + + - name: Create Pull Request + run: | + gh pr create \ + --title "Update minimum required version of astropy-iers-data" \ + --label no-changelog-entry-needed --label utils.iers --label skip-basebranch-check \ + --body-file .github/workflows/update_astropy_iers_data_pin.md \ + --base "${PR_BASE_REF_NAME}" + env: + PR_BASE_REF_NAME: ${{ github.ref_name }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/update_credits.md b/.github/workflows/update_credits.md new file mode 100644 index 000000000000..fcb01c4ea112 --- /dev/null +++ b/.github/workflows/update_credits.md @@ -0,0 +1,13 @@ +This is an automated update of the credits for the core package. + +Checklist: + +* [ ] Apply backport labels to any active backport branches +* [ ] Check the resulting docs/credits.rst, and update the .mailmap file for any duplicates or missing names +* [ ] If you update .mailmap, re-run: + +``` +python scripts/update-credits.py +``` + +and commit and push the changes to this branch. diff --git a/.github/workflows/update_credits.yml b/.github/workflows/update_credits.yml new file mode 100644 index 000000000000..219e3ad41e5d --- /dev/null +++ b/.github/workflows/update_credits.yml @@ -0,0 +1,64 @@ +# Regularly update the credits.rst file, to reduce maintenance burden at the time +# of release. This will allow issues with the .mailmap issues to be spotted early. + +name: Auto-update credits + +on: + schedule: + - cron: '0 6 * * 1' # Weekly + pull_request: + paths: + - '.github/workflows/update_credits.yml' + - '.github/workflows/update_credits.md' + - 'scripts/update-credits.py' + - 'docs/credits.rst' + - '.mailmap' + workflow_dispatch: + +permissions: + contents: read + +jobs: + + update-credits: + permissions: + contents: write # to create a branch + pull-requests: write # to create a PR + name: Auto-update credits + runs-on: ubuntu-latest + if: github.repository == 'astropy/astropy' || github.event_name == 'pull_request' + steps: + - name: Checkout code + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: true # needed for git push + fetch-depth: 0 + - name: Set up Python + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + with: + python-version: 3.x + - name: Run update script + run: pipx run scripts/update-credits.py + - name: Show diff + run: git diff docs/credits.rst + - name: Commit changes + if: github.event_name != 'pull_request' + run: | + git config user.name github-actions + git config user.email github-actions@github.com + BRANCH_NAME=update-credits-$(date +"%s") + git switch -c "$BRANCH_NAME" + git add docs/credits.rst + if ! git diff --cached --exit-code; then + git commit -m "Update docs/credits.rst with new contributors" + fi + git push --set-upstream origin "$BRANCH_NAME" + - name: Create Pull Request + if: github.event_name != 'pull_request' + run: | + gh pr create \ + --title "Update credits to add new contributors" \ + --label no-changelog-entry-needed --label Docs \ + --body-file .github/workflows/update_credits.md + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index e3d6042c66ee..f6a2c965223f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,22 +1,37 @@ # Compiled files -*.py[co] +*.py[cod] *.a *.o *.so +*.pyd +*.dll __pycache__ +# Ignore .pyi files since those are all auto-generated, and there are no plans +# to use them otherwise (as of 2025-08-31). Can be adjusted as needed. +*.pyi + # Ignore .c files by default to avoid including generated code. If you want to -# add a non-generated .c extension, use `git add -f filename.c`. +# add a non-generated .c extension, put that into the src/ subdirectory of a +# package or else use `git add -f filename.c`. *.c +!astropy/*/src/*.c +!astropy/timeseries/periodograms/bls/bls.c +astropy/modeling/src/projections.c # Other generated files MANIFEST -astropy/version.py +astropy/cython_version.py astropy/wcs/include/wcsconfig.h +astropy/_version.py # Sphinx _build _generated +docs/api +docs/generated +docs/visualization/ngc6976*.jpeg +docs/sg_execution_times.rst # Packages/installer info *.egg @@ -24,6 +39,7 @@ _generated dist build eggs +.eggs parts bin var @@ -31,14 +47,50 @@ sdist develop-eggs .installed.cfg distribute-*.tar.gz +.venv +venv +# we are not currently using pipenv directly, but people who +# install astropy with pipenv will have these files generated +Pipfile +Pipfile.lock + +# pyinstaller files +.pyinstaller/astropy_tests/ +.pyinstaller/run_astropy_tests +.pyinstaller/run_astropy_tests.spec + # Other +.cache .tox .*.swp +.*.swo *~ +.project +.pydevproject +.settings .coverage cover htmlcov +.hypothesis +.github_cache # Mac OSX .DS_Store + +# PyCharm +.idea + +# Pytest +v +.pytest_cache + +# VSCode +.vscode +.history + +.tmp +pip-wheel-metadata + +# Files generated if figure tests are run +results diff --git a/.mailmap b/.mailmap new file mode 100644 index 000000000000..e707ced0f6f8 --- /dev/null +++ b/.mailmap @@ -0,0 +1,405 @@ +Aarya Patil +Aarya Patil +Adam Ginsburg +Adam Ginsburg +Adam Ginsburg +Adele Plunkett +Adrian Price-Whelan +Adrian Price-Whelan +Akshat Dixit +Albert Y. Shih +Aleh Khvalko +Aleksi Suutarinen +Alex Conley +Alex Conley +Alex Fox <156043000+AlexFoxOSU@users.noreply.github.com> +Alex Hagen +Alex Rudy +Alexander Bakanov +Alexandre Beelen +Alexandre Beelen +Amit Kumar +Ana Posses +Anany Shrey Jain <31594632+ananyashreyjain@users.noreply.github.com> +Andreas Faisst +Andreas Michael Hermansen <97125645+AMHermansen@users.noreply.github.com> +Andy Casey +Aniket Kulkarni +Aniket Sanghi +Anirudh Katipally +Anne Archibald +Anne Archibald +Anthony Horton +Arthur Xavier Joao Pedro Maia <90696992+arthurxvtv@users.noreply.github.com> +Aryan Shukla <88445101+Telomelonia@users.noreply.github.com> +Asish Panda +Asra Nizami +Asra Nizami +Austen Groener +Austen Groener +Axel Donath +Axel Donath +Benjamin Alan Weaver +Benjamin Alan Weaver +Benjamin Alan Weaver +Ben Green <120208759+ScatteredComet@users.noreply.github.com> +Benjamin Roulston +Benjamin Winkel +Bhavya Khandelwal +Bogdan Nicula +Brett Graham +Brett Morris +Brett Morris +Brett Morris +Brett Morris +Brian Soto +Brian Svoboda +Brigitta Sipőcz +Brigitta Sipőcz +Brigitta Sipőcz +Bruce Merry +Bruce Merry +Bryce Nordgren +Chiara Marmo +Chiara Marmo +Chiara Marmo +Chiara Marmo +Chris Osborne <2087801o@student.gla.ac.uk> +Chris Simpson +Christian Clauss +Christoph Gohlke +Christopher Bonnett +Clara Brasseur +Clara Brasseur +ClÊment Robert +ClÊment Robert +Craig Jones +Craig Jones +Curtis McCully +Dan Foreman-Mackey +Dan Foreman-Mackey +Dan P. Cunningham +Dan Taranu +Daniel Bell +Daniel Bell +Daniel D'Avella +Daniel D'Avella +Daniel Datsev +Daniel Datsev +Daniel Giles +Daniel Lenz +Daniel Ryan +Daria Cara +Daria Cara <36781821+daria-cara@users.noreply.github.com> +David Collom +David Collom +David Collom +David Collom +David Grant <33813984+DavoGrant@users.noreply.github.com> +David Kirkby +David PÊrez-SuÃĄrez +David Shupe +Deen-Dot +Deen-Dot <80238871+Deen-dot@users.noreply.github.com> +Demitri Muna +Demitri Muna +Demitri Muna +Dhruv Yadav +Derek Homeier +Derek Homeier +Derek Homeier <709020+dhomeier@users.noreply.github.com> +Diego Asterio de Zaballa +Douglas Burke +Dylan Gregersen +Edward Gomez +Edward Slavich +Eduardo Olinto <90293761+olintoeduardo@users.noreply.github.com> +Eero Vaher +Elijah Bernstein-Cooper +Emily Deibert +Emma Hogan +Eric Depagne +Eric Koch +E. Madison Bray +E. Madison Bray +E. Madison Bray +E. Rykoff +Emir Karamehmetoglu +Emir Karamehmetoglu +Evan Jones <60061381+E-W-Jones@users.noreply.github.com> +Everett Schlawin +Everett Schlawin +Esteban Pardo SÃĄnchez +Francesco Montesano +Gabriel Brammer +Gabriel Brammer +Gabriel Perren +Gabriel Perren +Geert Barentsen +George Galvin +Gerrit Schellenberger +Gilles Landais +Giorgio Calderone +Graham Kanarek +Guillaume Pernot +Guillaume Pernot +Gustavo Bragança +Henrike F. +Hannes Breytenbach +Hans Moritz GÃŧnther +Hans Moritz GÃŧnther +Harry Ferguson +Harshada Raut +Henrik Norman +Henrik Norman +Henry Schreiner +HÊlvio Peixoto +Himanshu Pathak +Humna Awan +Igor Lemos +Ivo Busko +Ivo Busko +J. Berg +Jackson Hayward +Jaime AndrÊs +Jake VanderPlas +Jake VanderPlas +Jake VanderPlas +James Davies +James McCormac +James Tocknell +James Tocknell +James Turner +James Turner +Jane Rigby +Jani Å umak +Jason Segnini <47617351+JasonS09@users.noreply.github.com> +Javier Blasco +Javier Duran +Javier Duran +Javier Duran +Javier Duran +Javier Duran +Javier Duran +Javier Duran +Javier Pascual Granado +Jeff Jennings +Jeff Taylor +Jennifer Karr +Jero Bado +Jero Bado <10357742+jerobado@users.noreply.github.com> +Jo Bovy +Joe Hunkeler +Johannes Zeman +John Parejko +John Parejko +Johnny Greco +Johnny Greco +Jon Carifio +Joren Hammudoglu +Jonathan Foster +Jonathan Foster +Jonathan Gagne +Jordan Mirocha +Joseph Long +Joseph Long +Joseph Schlitz +Juan Carlos Segovia +Juan Luis Cano Rodríguez +Juan Luis Cano Rodríguez +Juan Luis Cano Rodríguez +Juan Luis Cano Rodríguez +Julien Woillez +Julien Woillez +Jurien Huisman +Kacper Kowalik +Kacper Kowalik +Kacper Rutkowski +Kacper Rutkowski +Kang Wang +Karan Grover +Kartavay Verma +Karl Gordon +Karl Vyhmeister +Kelle Cruz +Kevin Gullikson +Kirill Tchernyshyov +Kris Stern +Kris Stern +Kunam Balaram Reddy +Kyle Barbary +Kyle Oman +Kyle Oman +Larry Bradley +Larry Bradley +Laura Watkins +Lauren Glattly <44421608+lglattly@users.noreply.github.com> +Laurent Michel +Lennard Kiehl +Leo Singer +Leo Singer +Leonardo Ferreira <[leonardo.ferreira.furg@gmail.com]> +Lia Corrales +Lingyi Hu +Lisa Martin <48742903+lisamartin72@users.noreply.github.com> +Lisa Walter +Loïc SÊguin-C +Luke G. Bouma +Luke Kelley +Luz Paz +Maximilian Linhoff +Maximilian Linhoff +Maximilian Linhoff +M. Atakan GÃŧrkan +M. S. R. Dinesh <39215691+msrdinesh@users.noreply.github.com> +Madhura Parikh +Magali Mebsout +Magnus Persson +Maneesh Yadav +Mangala Gowri Krishnamoorthy +Manon Marchand +Marcello Nascif <118627858+marcellonascif@users.noreply.github.com> +Marten van Kerkwijk +Marten van Kerkwijk +Marten van Kerkwijk +Marten van Kerkwijk +Marten van Kerkwijk +Matt Davis +Matteo Bachetti +Matthew Craig +Matthias Stein +Matthieu Baumann +Matthieu Bec +Matthieu Bec +Mavani Bhautik +Michael Belfrage <216956+mikez@users.noreply.github.com> +Michael Brewer +Michael Brewer +Michael Hirsch +Michael Lindner-D'Addario <38199062+MDAddario@users.noreply.github.com> +Michael Mommert +Michael Mommert +Michael Seifert +Michele Costa +Michele Costa +Miguel de Val-Borro +Mihai Cara +Mihai Cara +Mike Alexandersen +Mikhail Minin +Moataz Hisham +Mridul Seth +Mubin Manasia <48038715+Mubin17@users.noreply.github.com> +Nabil Freij +Nadia Dencheva +Nadia Dencheva +Nathaniel Starkman +Nathaniel Starkman +Nathaniel Starkman +Naveen Selvadurai <172697+naveensrinivasan@users.noreply.github.com> +Neil Crighton +Neal McBurnett +Nicholas Earl +Nicholas Earl +Nicholas Earl +Nick Lloyd +Nora Luetzgendorf +Ole Streicher +Ole Streicher +Parikshit Sakurikar +Patricio Rojo +Pauline Barmby +Perry Greenfield +P. L. Lim <2090236+pllim@users.noreply.github.com> +P. L. Lim <2090236+pllim@users.noreply.github.com> +Porter Averett <46609497+paverett@users.noreply.github.com> +Prajwel Joseph +Pratik Patel +Pritish Chakraborty +Rachel Guo +Ricardo Fonseca +Ricardo Fonseca +Richard R. +Richard R. <58728519+rrjbca@users.noreply.github.com> +R. Virinchi +Reem Hamraz +Ricky O'Steen <39831871+rosteen@users.noreply.github.com> +Ritiek Malhotra +Ritwick DSouza +Robel Geda +Robel Geda +Rohan Rajpal +Rohit Kapoor +Rohit Patil +Rohit Patil +Roman Tolesnikov +Ryan Cooke +Sam Bianco <70121323+snbianco@users.noreply.github.com> +Sam Lee +Sam Van Kooten +Sam Verstocken +Sanjeev Dubey +Sara Ogaz +Sarah Graves +Sashank Mishra +Sebastian Meßlinger <39328484+krachyon@users.noreply.github.com> +Sebastian Meßlinger +Sergio Pascual +Shane Maloney +Shane Maloney +Shantanu Srivastava +Sharath Ramkumar <29162020+tnfssc@users.noreply.github.com> +Shilpi Jain +Shivansh Mishra +Shivansh Mishra +Simon Alinder <92031780+AlinderS@users.noreply.github.com> +Simon Alinder +Simon Conseil +Simon Conseil +Simon Conseil +Simon Conseil +Simon Conseil +Simon Liedtke +Somia Floret +Somia Floret <57394764+somilia@users.noreply.github.com> +Sourabh Cheedella +Stelios Voutsinas +Steve Crawford +Steve Crawford +Steve Crawford +Stuart Littlefair +Stuart Mumford +Sudheesh Singanamalla +Sudheesh Singanamalla +Surya K. +Sushobhana Patra +Tanvi Pooranmal Meena <96572616+mtanvi19@users.noreply.github.com> <96572616+TanviPooranmal@users.noreply.github.com> +Thomas Erben +Thompson Le Blanc +Thompson Le Blanc +Tiago Gomes +Tim Jenness +Tim Jenness +Timothy P. Ellsworth Bowers +Tom Aldcroft +Tom Donaldson +Tom J Wilson +Tyler Finethy +VSN Reddy Janga +Vishnunarayan K. I. +Varun Kasyap Pentamaraju +Vishwas +Vital FernÃĄndez +William Jamieson +William Jamieson +Yannick Copin +Yaocheng Chen +Yaocheng Chen <51320658+yaochengchen@users.noreply.github.com> +Yash Kumar +Yash Nandwana +Yash Sharma +Yingqi Ying <33911276+dyq0811@users.noreply.github.com> +Zach Edwards +Zac Hatfield-Dodds +ZÊ Vinicius +Zhiyuan Ma diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 000000000000..ba4b50e1a340 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,113 @@ +ci: + autofix_commit_msg: "chore: pre-commit fixes" + autofix_prs: false + autoupdate_schedule: 'monthly' + +exclude: "^cextern" + +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v6.0.0 + hooks: + - id: check-added-large-files + args: ["--enforce-all", "--maxkb=300"] + exclude: "^(\ + cextern/expat/lib/xmlparse.c|\ + cextern/wcslib/C/flexed/.*|\ + CHANGES.rst|\ + )$" + # Prevent giant files from being committed. + - id: check-case-conflict + # Check for files with names that would conflict on a case-insensitive + # filesystem like MacOS HFS+ or Windows FAT. + - id: check-json + # Attempts to load all json files to verify syntax. + - id: check-merge-conflict + # Check for files that contain merge conflict strings. + - id: check-symlinks + # Checks for symlinks which do not point to anything. + - id: check-toml + # Attempts to load all TOML files to verify syntax. + - id: check-xml + # Attempts to load all xml files to verify syntax. + - id: check-yaml + # Attempts to load all yaml files to verify syntax. + exclude: ".*(.github.*)$" + - id: detect-private-key + # Checks for the existence of private keys. + - id: end-of-file-fixer + # Makes sure files end in a newline and only a newline. + exclude: ".*(data.*|extern.*|licenses.*|_static.*|_parsetab.py)$" + # - id: fix-encoding-pragma # covered by pyupgrade + - id: trailing-whitespace + # Trims trailing whitespace. + exclude_types: [python] # Covered by Ruff W291. + exclude: ".*(data.*|extern.*|licenses.*|_static.*)$" + + - repo: https://github.com/pre-commit/pygrep-hooks + rev: v1.10.0 + hooks: + - id: rst-directive-colons + # Detect mistake of rst directive not ending with double colon. + - id: rst-inline-touching-normal + # Detect mistake of inline code touching normal text in rst. + - id: text-unicode-replacement-char + # Forbid files which have a UTF-8 Unicode replacement character. + + - repo: https://github.com/zizmorcore/zizmor-pre-commit + rev: v1.24.1 + hooks: + - id: zizmor + + - repo: https://github.com/codespell-project/codespell + rev: v2.4.2 + hooks: + - id: codespell + args: ["--write-changes"] + additional_dependencies: + - tomli + + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.15.12 + hooks: + - id: ruff-check + args: ["--fix", "--show-fixes"] + - id: ruff-format + + - repo: https://github.com/scientific-python/cookie + rev: 2026.04.04 + hooks: + - id: sp-repo-review + + - repo: local + hooks: + - id: changelogs-rst + name: changelog filenames + language: fail + entry: >- + changelog files must be named /####.(bugfix|feature|api|perf).rst + or ####.other.rst (in the root directory only) + exclude: >- + ^docs/changes/[\w\.]+/(\d+\.(bugfix|feature|api|perf)(\.\d)?.rst|.gitkeep) + files: ^docs/changes/[\w\.]+/ + - id: changelogs-rst-other + name: changelog filenames for other category + language: fail + entry: >- + only "other" changelog files must be placed in the root directory + exclude: >- + ^docs/changes/(\d+\.other.rst|README.rst|template.rst) + files: ^docs/changes/\d+.\w+.rst + + # Linting hook for stylistic issues in rst doc files. + - repo: https://github.com/sphinx-contrib/sphinx-lint + rev: v1.0.2 + hooks: + - id: sphinx-lint + exclude: "(licenses/|docs/_build/|.*parsetab.py|astropy/extern/)" + + - repo: https://github.com/pre-commit/mirrors-clang-format + rev: 'v22.1.4' + hooks: + - id: clang-format + files: \.(c|h)$ diff --git a/.pycodestyle b/.pycodestyle new file mode 100644 index 000000000000..3d53d33c438a --- /dev/null +++ b/.pycodestyle @@ -0,0 +1,3 @@ +[pycodestyle] +max-line-length = 88 +exclude = extern,*parsetab.py,*lextab.py diff --git a/.pyinstaller/hooks/hook-astropy_iers_data.py b/.pyinstaller/hooks/hook-astropy_iers_data.py new file mode 100644 index 000000000000..af591ea6d83c --- /dev/null +++ b/.pyinstaller/hooks/hook-astropy_iers_data.py @@ -0,0 +1,3 @@ +from PyInstaller.utils.hooks import collect_data_files + +datas = collect_data_files("astropy_iers_data") diff --git a/.pyinstaller/hooks/hook-skyfield.py b/.pyinstaller/hooks/hook-skyfield.py new file mode 100644 index 000000000000..caefbfcb6fae --- /dev/null +++ b/.pyinstaller/hooks/hook-skyfield.py @@ -0,0 +1,6 @@ +# NOTE: this hook should be added to +# https://github.com/pyinstaller/pyinstaller-hooks-contrib +# once that repository is ready for pull requests +from PyInstaller.utils.hooks import collect_data_files + +datas = collect_data_files("skyfield") diff --git a/.pyinstaller/run_astropy_tests.py b/.pyinstaller/run_astropy_tests.py new file mode 100644 index 000000000000..74fbf1a32305 --- /dev/null +++ b/.pyinstaller/run_astropy_tests.py @@ -0,0 +1,128 @@ +import os +import shutil +import sys + +import erfa # noqa: F401 +import matplotlib as mpl +import pytest + +import astropy # noqa: F401 + +if len(sys.argv) == 3 and sys.argv[1] == "--astropy-root": + ROOT = sys.argv[2] +else: + # Make sure we don't allow any arguments to be passed - some tests call + # sys.executable which becomes this script when producing a pyinstaller + # bundle, but we should just error in this case since this is not the + # regular Python interpreter. + if len(sys.argv) > 1: + print("Extra arguments passed, exiting early") + sys.exit(1) + +for root, dirnames, files in os.walk(os.path.join(ROOT, "astropy")): + # NOTE: we can't simply use + # test_root = root.replace('astropy', 'astropy_tests') + # as we only want to change the one which is for the module, so instead + # we search for the last occurrence and replace that. + pos = root.rfind("astropy") + test_root = root[:pos] + "astropy_tests" + root[pos + 7 :] + + # Copy over the astropy 'tests' directories and their contents + for dirname in dirnames: + final_dir = os.path.relpath(os.path.join(test_root, dirname), ROOT) + # We only copy over 'tests' directories, but not astropy/tests (only + # astropy/tests/tests) since that is not just a directory with tests. + if dirname == "tests" and not root.endswith("astropy"): + shutil.copytree(os.path.join(root, dirname), final_dir, dirs_exist_ok=True) + else: + # Create empty __init__.py files so that 'astropy_tests' still + # behaves like a single package, otherwise pytest gets confused + # by the different conftest.py files. + init_filename = os.path.join(final_dir, "__init__.py") + if not os.path.exists(os.path.join(final_dir, "__init__.py")): + os.makedirs(final_dir, exist_ok=True) + with open(os.path.join(final_dir, "__init__.py"), "w") as f: + f.write("#") + # Copy over all conftest.py files + for file in files: + if file == "conftest.py": + final_file = os.path.relpath(os.path.join(test_root, file), ROOT) + shutil.copy2(os.path.join(root, file), final_file) + +# Add the top-level __init__.py file +with open(os.path.join("astropy_tests", "__init__.py"), "w") as f: + f.write("#") + +# Remove test file that tries to import all sub-packages at collection time +os.remove( + os.path.join("astropy_tests", "utils", "iers", "tests", "test_leap_second.py") +) + +# Remove convolution tests for now as there are issues with the loading of the C extension. +# FIXME: one way to fix this would be to migrate the convolution C extension away from using +# ctypes and using the regular extension mechanism instead. +shutil.rmtree(os.path.join("astropy_tests", "convolution")) +os.remove(os.path.join("astropy_tests", "modeling", "tests", "test_convolution.py")) +os.remove(os.path.join("astropy_tests", "modeling", "tests", "test_core.py")) +os.remove(os.path.join("astropy_tests", "visualization", "tests", "test_lupton_rgb.py")) + +# FIXME: PIL minversion check does not work +os.remove( + os.path.join("astropy_tests", "visualization", "wcsaxes", "tests", "test_misc.py") +) +os.remove( + os.path.join("astropy_tests", "visualization", "wcsaxes", "tests", "test_wcsapi.py") +) + +# FIXME: The following tests rely on the fully qualified name of classes which +# don't seem to be the same. +os.remove(os.path.join("astropy_tests", "table", "mixins", "tests", "test_registry.py")) + +# Copy the top-level conftest.py +shutil.copy2( + os.path.join(ROOT, "astropy", "conftest.py"), + os.path.join("astropy_tests", "conftest.py"), +) + +# matplotlib hook in pyinstaller 5.0 and later no longer collects every backend, see +# https://github.com/pyinstaller/pyinstaller/issues/6760 +mpl.use("svg") + +# We skip a few tests, which are generally ones that rely on explicitly +# checking the name of the current module (which ends up starting with +# astropy_tests rather than astropy). + +SKIP_TESTS = [ + "test_exception_logging_origin", + "test_log", + "test_configitem", + "test_config_noastropy_fallback", + "test_no_home", + "test_path", + "test_rename_path", + "test_data_name_third_party_package", + "test_pkg_finder", + "test_wcsapi_extension", + "test_find_current_module_bundle", + "test_minversion", + "test_imports", + "test_generate_config", + "test_generate_config2", + "test_create_config_file", + "test_download_parallel_fills_cache", + "test_defined_constants_do_not_change", + "test_physical_constants_versions", +] + +# Run the tests! +sys.exit( + pytest.main( + ["astropy_tests", "-k " + " and ".join("not " + test for test in SKIP_TESTS)], + plugins=[ + "pytest_astropy.plugin", + "pytest_doctestplus.plugin", + "pytest_remotedata.plugin", + "pytest_astropy_header.display", + ], + ) +) diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 000000000000..322fa423119f --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,32 @@ +version: 2 + +build: + os: "ubuntu-22.04" + tools: + python: "miniforge3-25.11" + jobs: + post_checkout: + - git fetch --unshallow || true + pre_install: + - git update-index --assume-unchanged docs/conf.py docs/rtd_environment.yaml + +conda: + environment: docs/rtd_environment.yaml + +sphinx: + builder: html + configuration: docs/conf.py + fail_on_warning: true + +# Install regular dependencies. +# Then, install special pinning for RTD. +python: + install: + - method: pip + path: . + extra_requirements: + - docs + - all + +# Don't build any extra formats +formats: [] diff --git a/.ruff.toml b/.ruff.toml new file mode 100644 index 000000000000..ec8b1b572e6b --- /dev/null +++ b/.ruff.toml @@ -0,0 +1,367 @@ +extend = "pyproject.toml" +lint.ignore = [ + # NOTE: to find a good code to fix, run: + # ruff check --select="ALL" --statistics astropy/ + + # flake8-annotations (ANN) : static typing + "ANN001", # Function argument without type annotation + "ANN003", # `**kwargs` without type annotation + "ANN202", # Private function without return type annotation + "ANN401", # Use of `Any` type + + # flake8-unused-arguments (ARG) + "ARG001", # unused-function-argument + "ARG002", # unused-method-argument + "ARG003", # unused-class-method-argument + "ARG004", # unused-static-method-argument + "ARG005", # unused-lambda-argument + + # flake8-bugbear (B) + "B006", # MutableArgumentDefault + "B023", # FunctionUsesLoopVariable + "B028", # No-explicit-stacklevel + "B904", # RaiseWithoutFromInsideExcept + "B905", # ZipWithoutExplicitStrict + + # flake8-blind-except (BLE) + "BLE001", # blind-except + + # mccabe (C90) : code complexity + # TODO: configure maximum allowed complexity. + "C901", # McCabeComplexity + + # pydocstyle (D) + # Missing Docstrings + "D100", # undocumented-public-module + "D101", # undocumented-public-class + "D103", # undocumented-public-function + "D104", # undocumented-public-package + "D205", # blank-line-after-summary + # Quotes Issues + "D301", # escape-sequence-in-docstring + # Docstring Content Issues + "D403", # first-line-capitalized + "D404", # docstring-starts-with-this + "D401", # non-imperative-mood. + "D414", # empty-docstring-section + "D419", # docstring is empty + + # flake8-datetimez (DTZ) + "DTZ001", # call-datetime-without-tzinfo + "DTZ005", # call-datetime-now-without-tzinfo + + # pycodestyle (E, W) + "E501", # line-too-long + "E721", # type-comparison + "E731", # lambda-assignment + + # flake8-errmsg (EM) : nicer error tracebacks + "EM101", # raw-string-in-exception + "EM102", # f-string-in-exception + "EM103", # dot-format-in-exception + + # eradicate (ERA) + # NOTE: be careful that developer notes are kept. + "ERA001", # commented-out-code + + # Pyflakes (F) + "F841", # unused-variable + + # flake8-boolean-trap (FBT) : boolean flags should be kwargs, not args + # NOTE: a good thing to fix, but changes API. + "FBT001", # boolean-positional-arg-in-function-definition + "FBT002", # boolean-default-value-in-function-definition + "FBT003", # boolean-positional-value-in-function-call + + # flake8-fixme (FIX) + "FIX001", # Line contains FIXME. this should be fixed or at least FIXME replaced with TODO + "FIX004", # Line contains HACK. replace HACK with NOTE. + + "FURB166", # int-on-sliced-str + + # pep8-naming (N) + # NOTE: some of these can/should be fixed, but this changes the API. + "N801", # invalid-class-name + "N802", # invalid-function-name + "N803", # invalid-argument-name + "N805", # invalid-first-argument-name-for-method + "N807", # dunder-function-name + "N813", # camelcase-imported-as-lowercase + "N815", # mixed-case-variable-in-class-scope + "N816", # mixed-case-variable-in-global-scope + "N818", # error-suffix-on-exception-name + + # NumPy-specific rules (NPY) + "NPY002", # Replace legacy `np.random.rand` call with `np.random.Generator` (2023-05-03) + + # Perflint (PERF) + "PERF203", # `try`-`except` within a loop incurs performance overhead + "PERF401", # Use a list comprehension to create a transformed list + + # Pylint (PLC, PLE, PLR, PLW) + "PLR0124", # Name compared with itself + "PLR0402", # ConsiderUsingFromImport + "PLR0912", # too-many-branches + "PLR0913", # too-many-args + "PLR0915", # too-many-statements + "PLR1714", # Consider merging multiple comparisons + "PLR2004", # MagicValueComparison + "PLR5501", # collapsible-else-if + "PLW0603", # global-statement + "PLW1641", # Object does not implement `__hash__` method + "PLW2901", # redefined-loop-name + + # flake8-pytest-style (PT) + "PT003", # pytest-extraneous-scope-function + "PT006", # pytest-parametrize-names-wrong-type + "PT007", # pytest-parametrize-values-wrong-type + "PT011", # pytest-raises-too-broad + "PT012", # pytest-raises-with-multiple-statements + "PT017", # pytest-assert-in-exceptinstead + "PT018", # pytest-composite-assertion + "PT028", # pytest-parameter-with-default-argument + "PT030", # pytest.warns({warning}) is too broad + + # flake8-return (RET) + "RET501", # unnecessary-return-none + "RET502", # implicit-return-value + "RET503", # implicit-return + "RET507", # superfluous-else-continue + + # flake8-raise (RSE) + "RSE102", # unnecessary-paren-on-raise-exception + + # Ruff-specific rules (RUF) + "RUF001", # ambiguous-unicode-character-string + "RUF002", # ambiguous-unicode-character-docstring + "RUF010", # use conversion in f-string + "RUF012", # Mutable class attributes should be annotated with `typing.ClassVar` + "RUF043", # pytest-raises-ambiguous-pattern + "RUF059", # unused-unpacked-variable + + # flake8-bandit (S) + "S101", # Use of `assert` detected + "S105", # hardcoded-password-string + "S110", # try-except-pass + "S112", # try-except-continue + "S301", # suspicious-pickle-usage + "S307", # Use of possibly insecure function; consider using `ast.literal_eval` + "S311", # suspicious-non-cryptographic-randomness + "S324", # hashlib-insecure-hash-function + "S506", # UnsafeYAMLLoad + "S310", # Suspicious-url-open-usage + "S603", # `subprocess` call: check for execution of untrusted input + "S607", # Starting a process with a partial executable path + + # flake8-simplify (SIM) + "SIM102", # NestedIfStatements + "SIM105", # UseContextlibSuppress + "SIM108", # UseTernaryOperator + "SIM114", # if-with-same-arms + "SIM115", # OpenFileWithContextHandler + "SIM117", # MultipleWithStatements + "SIM118", # KeyInDict + "SIM201", # NegateEqualOp + "SIM300", # yoda condition + + # flake8-print (T20) + "T201", # PrintUsed + + # flake8-todos (TD) + "TD001", # Invalid TODO tag + "TD003", # Missing issue link on the line following this TODO + "TD004", # Missing colon in TODO + "TD007", # Missing space after colon in TODO + + # tryceratops (TRY) + "TRY002", # raise-vanilla-class + "TRY003", # raise-vanilla-args + "TRY004", # prefer-type-error + "TRY201", # verbose-raise + "TRY301", # raise-within-try +] +lint.unfixable = [ + "E711" # NoneComparison. Hard to fix b/c numpy has it's own None. +] + +[lint.extend-per-file-ignores] +"__init__.py" = [ + "F401", + "PLC0415", # import-outside-top-level +] +"conftest.py" = [ + "PLC0415", # import-outside-top-level +] +"test_*.py" = [ + "PTH", # all flake8-use-pathlib + "RUF015", # Prefer next({iterable}) over single element slice +] +# TODO: fix these, on a per-subpackage basis. +# When a general exclusion is being fixed, but it affects many subpackages, it +# is better to fix for subpackages individually. The general exclusion should be +# copied to these subpackage sections and fixed there. +"astropy/**/setup_package.py" = [ + # all flake8-use-pathlib + # reason: subtle differences between pathlib.Path.relative_to and os.path.relpath + "PTH", +] +"astropy/config/*" = [ + "PLC0415", # import-outside-top-level + "PT031", # pytest-warns-with-multiple-statements +] +"astropy/constants/*" = [ + "PLC0415", # import-outside-top-level +] +"astropy/convolution/*" = [ + "PLC0415", # import-outside-top-level + "PT031", # pytest-warns-with-multiple-statements +] +"astropy/coordinates/*" = [ + "PLC0415", # import-outside-top-level + "PT031", # pytest-warns-with-multiple-statements + "PTH", # all flake8-use-pathlib + "DTZ007", # call-datetime-strptime-without-zone +] +"astropy/cosmology/*" = [ + "PLC0415", # import-outside-top-level + "PT019", # pytest-fixture-param-without-value + "PT031", # pytest-warns-with-multiple-statements +] +"astropy/io/*" = [ + "G001", # logging-string-format + "G004", # logging-f-string + "PLR0911", # too-many-return-statements + "PTH116", # os-stat + "PTH117", # os-path-isabs + "S314", # defusedxml + "SLOT000", # Subclasses of `str` should define `__slots__` + "TD005", # Missing issue description after `TODO` + "TRY400", # error-instead-of-exception + "TRY300", # Consider `else` block +] +"astropy/io/ascii/*" = [ + "B007", # UnusedLoopControlVariable + "PLC0415", # import-outside-top-level + "PT031", # pytest-warns-with-multiple-statements +] +"astropy/io/fits/*" = [ + "B007", # UnusedLoopControlVariable + "PLC0415", # import-outside-top-level + "PT031", # pytest-warns-with-multiple-statements + "PTH", +] +"astropy/io/misc/*" = [ + "B007", # UnusedLoopControlVariable + "PLC0415", # import-outside-top-level + "PT031", # pytest-warns-with-multiple-statements +] +"astropy/io/registry/*" = [ + "PLC0415", # import-outside-top-level + "PTH", +] +"astropy/io/votable/*" = [ + "B007", # UnusedLoopControlVariable + "PLC0415", # import-outside-top-level + "PT031", # pytest-warns-with-multiple-statements + "PTH", +] +"astropy/logger.py" = [ + "PLC0415", # import-outside-top-level +] +"astropy/modeling/*" = [ + "PLC0415", # import-outside-top-level + "PLR0911", # too-many-return-statements + "RET504", # unnecessary-assign + "SLOT001", # Subclasses of `tuple` should define `__slots__` + "TRY300", # Consider `else` block + "F811", # see https://github.com/astropy/astropy/pull/16633 +] +"astropy/nddata/*" = [ + "PLC0415", # import-outside-top-level +] +"astropy/samp/*" = [ + "PLC0415", # import-outside-top-level + "PTH", # all flake8-use-pathlib + "RET504", # unnecessary-assign +] +"astropy/stats/*" = [ + "B007", # UnusedLoopControlVariable + "PLC0415", # import-outside-top-level + "PLR0911", # too-many-return-statements + "PT031", # pytest-warns-with-multiple-statements + "RET504", # unnecessary-assign +] +"astropy/table/*" = [ + "PLC0415", # import-outside-top-level + "PT031", # pytest-warns-with-multiple-statements + "RET504", # unnecessary-assign + "S605", # Starting a process with a shell, possible injection detected + "TRY300", # Consider `else` block +] +"astropy/tests/*" = [ + "PLC0415", # import-outside-top-level + "PT031", # pytest-warns-with-multiple-statements + "PTH", # all flake8-use-pathlib +] +"astropy/time/*" = [ + "FIX003", # Line contains XXX. replace XXX with TODO + "PIE794", # duplicate-class-field-definition + "PLC0415", # import-outside-top-level + "PT031", # pytest-warns-with-multiple-statements + "RET504", # unnecessary-assign +] +"astropy/timeseries/*" = [ + "PLC0415", # import-outside-top-level + "PLR0911", # too-many-return-statements + "PT031", # pytest-warns-with-multiple-statements + "RET504", # unnecessary-assign +] +"astropy/units/*" = [ + "N812", # lowercase-imported-as-non-lowercase + "PLC0415", # import-outside-top-level + "PLR0911", # too-many-return-statements + "PT031", # pytest-warns-with-multiple-statements + "RET504", # unnecessary-assign + "TRY300", # Consider `else` block +] +"astropy/uncertainty/*" = [ + "PLC0415", # import-outside-top-level +] +"astropy/utils/*" = [ + "B007", # UnusedLoopControlVariable + "N811", # constant-imported-as-non-constant + "PLC0415", # import-outside-top-level + "PLR0911", # too-many-return-statements + "PT031", # pytest-warns-with-multiple-statements + "PTH", # all flake8-use-pathlib + "RET504", # unnecessary-assign + "S321", # Suspicious-ftp-lib-usage + "TRY300", # Consider `else` block +] +"astropy/visualization/*" = [ + "B015", + "PLC0415", # import-outside-top-level + "PLR0911", # too-many-return-statements +] +"astropy/wcs/*" = [ + "F821", # undefined-name + "PLC0415", # import-outside-top-level + "PT031", # pytest-warns-with-multiple-statements + "PTH", # all flake8-use-pathlib + "RET504", # unnecessary-assign + "S608", # Posslibe SQL injection + "SIM202", # NegateNotEqualOp + "TRY300", # Consider `else` block +] +"docs/*" = [] + +".pyinstaller/*.py" = ["PTH"] + + +[lint.flake8-import-conventions.aliases] +# xml is hardly ever used thus the alias should not be mandated +# There is no way to remove from the default list, only to override +# the default thus we list the things here that we actually should use. +"numpy" = "np" +"matplotlib" = "mpl" +"matplotlib.pyplot" = "plt" diff --git a/.stubtest.ini b/.stubtest.ini new file mode 100644 index 000000000000..3ce063d3c3d2 --- /dev/null +++ b/.stubtest.ini @@ -0,0 +1,2 @@ +[mypy] +follow_imports = silent diff --git a/CHANGES.rst b/CHANGES.rst index bdadf2a9bdef..f0fdc870ad8f 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,13 +1,19394 @@ -0.2 (unreleased) ----------------- +Version 7.2.0 (2025-11-25) +========================== -- Astropy doc themes moved into `astropy.sphinx` to allow affilated packages to access them -- Added option to disable building of "legacy" packages (pyfits, vo, etc.) -- `astropy.table` I/O infrastructure for custom readers/writers implemented -- Besancon Galaxy models (http://model.obs-besancon.fr) reader implemented in `astropy.ascii` +New Features +------------ -0.1 (2012-06-19) ----------------- +astropy.constants +^^^^^^^^^^^^^^^^^ -- Inital release. +- Added CODATA 2022 support in ``astropy.constants``. + + This update affects the following constants while the rest are unchanged from CODATA 2018: + + - ``m_p`` (Proton mass) + - ``m_n`` (Neutron mass) + - ``m_e`` (Electron mass) + - ``u`` (Atomic mass) + - ``eps0`` (Vacuum electric permittivity) + - ``Ryd`` (Rydberg constant) + - ``a0`` (Bohr radius) + - ``muB`` (Bohr magneton) + - ``alpha`` (Fine-structure constant) + - ``mu0`` (Vacuum magnetic permeability) + - ``sigma_T`` (Thomson scattering cross-section) [#18118] + +astropy.coordinates +^^^^^^^^^^^^^^^^^^^ + +- Allow ``np.concatenate``, ``np.stack`` and similar numpy functions to + be applied on representations and differentials. + + They can also be applied to coordinate frames and ``SkyCoord``, though + with the same limitation as for setting elements of frames and + coordinates: all frame attributes have to be scalars (or arrays with + only identical elements). [#18193] + +- The results of ``match_coordinates_3d()``, ``match_coordinates_sky()``, + ``search_around_3d()`` and ``search_around_sky()`` and the corresponding + ``SkyCoord`` methods now have named attributes. [#18459] + +astropy.cosmology +^^^^^^^^^^^^^^^^^ + +- The trait ``astropy.cosmology.traits.CurvatureComponent`` has been added to work with + objects that have attributes and methods related to the global curvature. [#18232] + +- The trait ``astropy.cosmology.traits.HubbleParameter`` has been added to work with objects that have attributes and methods related to the Hubble parameter. [#18271] + +- The trait ``astropy.cosmology.traits.DarkEnergyComponent`` has been added to work with objects that have attributes and methods related to the Dark Energy component. [#18447] + +- Cosmology methods now exclusively return arrays, not floats or other scalars. [#18632] + +- The trait ``astropy.cosmology.traits.DarkMatterComponent`` has been added to work with + objects that have attributes and methods related to dark matter. [#18760] + +- The trait ``astropy.cosmology.traits.MatterComponent`` has been added to work with + objects that have attributes and methods related to matter density. + The trait ``astropy.cosmology.traits.BaryonComponent`` has been added to work with + objects that have attributes and methods related to baryonic matter. + The trait ``astropy.cosmology.traits.CriticalDensity`` has been added to work with + objects that have attributes and methods related to the critical density. [#18769] + +- The trait ``astropy.cosmology.traits.PhotonComponent`` has been added to work with objects that have attributes and methods related to photons. [#18787] + +- The trait ``astropy.cosmology.traits.TotalComponent`` has been added to work with objects that have attributes and methods related to the total density component of the universe. [#18794] + +astropy.io.ascii +^^^^^^^^^^^^^^^^ + +- CDS table reader will find the metadata for gzipped tables in the accompanying ReadMe file. [#18506] + +astropy.io.fits +^^^^^^^^^^^^^^^ + +- Enable color and suggestion-on-typos in all ``argparse`` CLIs for Python 3.14 + (``fitscheck``, ``fitsdiff``, ``fitsheader`` and ``fitsinfo``). [#18151] + +- Allow reading a FITS file hosted on a cloud resource like Amazon S3 via + ``Table.read()``. This is done with a new ``fsspec_kwargs`` dict argument + that gets passed through to ``fsspec`` to access cloud resources. [#18379] + +- It is now possible to check the existence of ``Columns`` in ``ColDefs`` by using the membership operator. [#18717] + +astropy.io.misc +^^^^^^^^^^^^^^^ + +- Added a new ECSV table reading module that supports different backend engines for the + CSV data parsing. In addition to the default "io.ascii" engine, this includes engines + that use the PyArrow and Pandas CSV readers. These can be up to 16 times faster and are + more memory efficient than the native astropy ECSV reader. To get help with this + interface run ``Table.read.help(format="ecsv")``. [#18267] + +- Improve support for compressed file formats in the ECSV and the pyarrow CSV + Table readers. All formats supported by ``astropy.utils.data.get_readable_fileobj()`` + (currently gzip, bzip2, lzma (xz) or lzw (Z)) will now work with these readers. [#18712] + +astropy.io.registry +^^^^^^^^^^^^^^^^^^^ + +- Allow setting EXTNAME when writing a ``Table`` to a FITS file, e.g. + ``tbl.write("filename.fits", name="CAT", append=True)``. [#18470] + +astropy.io.votable +^^^^^^^^^^^^^^^^^^ + +- Enable color and suggestion-on-typos in ``volint`` CLI for Python 3.14 [#18151] + +- Modified the constructor for ``astropy.io.votable.tree.TableElement`` to use the version configuration information from the parent ``VOTableFile`` instance. This allows for better handling of version-specific features and ensures that the table element is created with the correct context regarding the VOTable version. [#18366] + +astropy.modeling +^^^^^^^^^^^^^^^^ + +- Add support for unit change propagation through the ``|`` (model composition) operator, + using either `~astropy.modeling.compose_models_with_units` or by setting the + ``unit_change_composition`` attribute on the model after composition. [#17304] + +astropy.nddata +^^^^^^^^^^^^^^ + +- The ``interpret_bit_flags`` function now strips whitespace from flag names. [#18205] + +astropy.samp +^^^^^^^^^^^^ + +- Enable color and suggestion-on-typos in ``samp_hub`` CLI for Python 3.14 [#18151] + +astropy.table +^^^^^^^^^^^^^ + +- Enable color and suggestion-on-typos in ``showtable`` CLI for Python 3.14 [#18151] + +- Added generic ``from_df`` and ``to_df`` methods to ``astropy.Table`` using + ``narwhals``. These methods provide a unified interface for converting between + Astropy Tables and various DataFrame formats (pandas, polars, pyarrow, etc.) + through the narwhals library. The ``to_df`` method converts an Astropy Table + to any supported DataFrame format, while ``from_df`` creates an Astropy Table + from any narwhals-compatible DataFrame. Narwhals is a lightweight compatibility + layer that provides a unified API across different DataFrame libraries, allowing + seamless interoperability without requiring all DataFrame libraries as dependencies. [#18435] + +- Setting the ``units`` or ``descriptions`` of ``QTable`` and ``Table`` + has been made more flexible for tables with optional columns that may + or may not appear in the data. This applies to directly creating a table + as well as reading formatted data with the ``read()`` method. + + In both cases you can supply ``units`` and ``description`` arguments as a + ``dict`` that specifies the units and descriptions for column names in + the table. Previously, if the input table did not contain a column that + was specified in the ``units`` or ``description`` dict, a ``ValueError`` + was raised. Now, such columns are simply ignored. [#18641] + +- A new method has been added for accessing a table index for tables with multiple + indices. You can now select the index with the ``with_index(index_id)`` method of the + ``.loc``, ``.iloc``, and ``.loc_indices`` properties. For example, for a table ``t`` + which has two indices on columns ``"a"`` and ``"b"`` respectively, + ``t.loc.with_index("b")[2]`` will use index ``"b"`` to find all the table rows where + ``t["b"] == 2``. Doing this query using the previous syntax ``t.loc["b", 2]`` is + deprecated and this functionality is planned for removal in astropy 9.0. + + In addition, support has been added for using ``.loc``, ``.iloc``, and ``.loc_indices`` + with an index based on two or more key columns. Previously this raised a ``ValueError``. [#18680] + +astropy.time +^^^^^^^^^^^^ + +- Allow ``np.concatenate``, ``np.stack`` and similar numpy functions to + be applied on ``Time`` and ``TimeDelta`` instances. [#18193] + +- Add a new time format ``galex`` for the GALEX satellite. + + In GALEX data, due to uncertainty in the spacecraft clock, the absolute time is only accurate to + about 1-10 seconds while the relative time within an observation is better than 0.005 s or so, + except on days with leap seconds, where relative times can be wrong by up to 1 s. + See question 101.2 in https://www.galex.caltech.edu/researcher/faq.html [#18330] + +astropy.units +^^^^^^^^^^^^^ + +- Some unit formats have deprecated units and converting such units to strings + emits a warning. + The new ``deprecations`` parameter of the unit ``to_string()`` methods allows + automatically converting deprecated units (if possible), silencing the warnings + or raising them as errors instead. [#18586] + +astropy.visualization +^^^^^^^^^^^^^^^^^^^^^ + +- Enable color and suggestion-on-typos in ``fits2bitmap`` CLI for Python 3.14 [#18151] + +- Added ``show_decimal_unit`` to ``set_major_formatter`` to control whether + or not units are shown in decimal mode. [#18312] + +- Added the methods ``set_visible()`` and ``set_position()`` to control the visibility and position of ticks, tick labels, and axis labels in a single call. + + Also added ``get_ticks_visible()``, ``get_ticklabel_visible()``, and ``get_axislabel_visible()`` methods to get the visibility state of each coordinate element. [#18443] + +- Added an image interval option (``SymmetricInterval``) for specifying a + symmetric extent about a midpoint, and the extent that contains both the image + minimum and maximum can be automatically determined. [#18602] + +astropy.wcs +^^^^^^^^^^^ + +- Enable color and suggestion-on-typos in all ``wcslint`` CLI for Python 3.14 [#18151] + +- Added a ``perserve_units`` keyword argument to ``WCS`` to optionally request + that units are not converted to SI (the default behavior is for celestial axes + to have units converted to degrees, and spectral axes to m or Hz). [#18338] + + +API Changes +----------- + +astropy.coordinates +^^^^^^^^^^^^^^^^^^^ + +- The functionality of ``astropy.coordinates.concatenate`` and + ``astropy.coordinates.concatenate_representations`` is now available using + ``np.concatenate``. Hence, these functions are being deprecated, emitting an + ``AstropyPendingDeprecationWarning`` starting with astropy 7.2. This will be + followed by a regular deprecation warning in astropy 8.0, and removal in 9.0. [#18193] + +- The ``matrix_utilities`` module was not included in the ``astropy`` API + documentation, but it was nonetheless explicitly referred to in some of the + other documentation. + This made it unclear if the functions in the module are public or private. + The public matrix utilities ``is_rotation_or_reflection()`` and + ``rotation_matrix()`` have been made available from the ``astropy.coordinates`` + namespace and should be imported from there. + Functions not available from the ``astropy.coordinate`` namespace are private + and may be changed or removed without warning. + However, three functions have been explicitly deprecated, despite being + private, as a courtesy to existing users. + ``matrix_utilites.angle_axis()`` and ``matrix_utilites.is_rotation()`` are + deprecated without replacement. + ``matrix_utilities.is_O3()`` is deprecated and the public + ``is_rotation_or_reflection()`` function can be used as a replacement. [#18418] + +- The undocumented ``earth_orientation`` module has been removed. [#18638] + +- ``astropy`` prefers reading data required for ``EarthLocation.of_site()`` from + a local cache and tries downloading (and caching) the data from the Internet if + the cache is empty. + As a last resort ``astropy`` has so far read a small bundled data file that + provided data for Greenwich as the single entry, but now ``astropy`` will raise + an error. [#18649] + +astropy.io.registry +^^^^^^^^^^^^^^^^^^^ + +- ``UnifiedInputRegistry`` and ``UnifiedOutputRegistry``'s ``delay_doc_updates`` + method's effect is disabled under Python's optimized mode (``-OO`` flag). [#17572] + +astropy.io.votable +^^^^^^^^^^^^^^^^^^ + +- Added a ``config`` property to ``astropy.io.votable.tree.VOTableFile``. + This property can be passed to the ``config`` parameter of constructors that need to know the associated VOTable version, such as ``TimeSys`` and ``CooSys``. [#18366] + +astropy.table +^^^^^^^^^^^^^ + +- Add additional detail to the text of the ``ValueError`` that is raised when + ``pprint`` cannot parse a column format string. [#17631] + +- Selecting a table index in the ``.loc``, ``.iloc``, or ``.loc_indices`` properties by + passing the index identifier as the first element of the item is deprecated and is + planned for removal in astropy 9.0. For example, if a table ``t`` has two indices on + columns ``"a"`` and ``"b"`` respectively, then ``t.loc["b", 2]`` (to find table rows + where ``t["b"] == 2``) is deprecated. This is replaced by ``t.loc.with_index("b")[2]``. [#18680] + +astropy.tests +^^^^^^^^^^^^^ + +- API changes towards a future deprecation of astropy test runner: + + * ``astropy.tests.runner.keyword`` is removed from public API. + It is used internally as a decorator within astropy test runner and + its exposure as public API was a mistake. In the future, it will be + removed without any deprecation. + * ``astropy.test``, ``astropy.tests.runner.TestRunnerBase``, and ``astropy.tests.runner.TestRunner`` + are now pending deprecation (``AstropyPendingDeprecationWarning``). + This will also affect downstream ``packagename.test`` generated using ``TestRunner``. + They may start to emit ``AstropyDeprecationWarning`` in v8.0 (but no earlier). [#17874] + +astropy.utils +^^^^^^^^^^^^^ + +- The ``isiterable()`` utility is deprecated. + ``numpy.iterable()`` can be used as a drop-in replacement. [#18053] + +- ``astropy.utils.metadata.MergeStrategy`` no longer modifies the ``merge()`` + methods of its subclasses at runtime to re-raise all exceptions as + ``MergeConflictError``. + This does not affect the functionality of ``MergeStrategy`` subclasses within + the ``astropy`` metadata merging machinery. [#18518] + +astropy.visualization +^^^^^^^^^^^^^^^^^^^^^ + +- A warning is now emitted for each axis name which is + invalid in ``set_ticklabel_position``, ``set_axislabel_position``, + and ``set_ticks_position``. This is a deprecation warning, + and in future invalid axis names will result in an error. [#18324] + +- A warning is now emitted if arguments are given to the getter method + ``get_axislabel_visibility_rule``. This is a deprecation warning, and in + future, giving arguments to this method will result in an error. [#18792] + + +Bug Fixes +--------- + +astropy.config +^^^^^^^^^^^^^^ + +- ``get_config_dir()`` and ``get_cache_dir()`` now emit warnings in all cases + where the ``XDG_CACHE_HOME`` (``XDG_CONFIG_HOME``, respectively) environment + variable doesn't meet internal assumptions and is ignored as a result. [#17934] + +astropy.coordinates +^^^^^^^^^^^^^^^^^^^ + +- The ``angle`` argument of the ``rotation_matrix()`` function can now be any + angle-like value, like its docstring states. + Previously some angle-like values (e.g. angle-like strings) were erroneously + rejected. [#18504] + +astropy.io.fits +^^^^^^^^^^^^^^^ + +- Fix bug with heap which was not updated after a VLA column is modified. [#18487] + +- Make ``fitscheck`` verify all HDUs before listing errors. [#18574] + +- Fix calculation of DATASUM/CHECKSUM for heap data in ``BinTableHDU``. [#18681] + +- Fixed a bug in ``fitsdiff`` script where failing to read a single file could + crash the entire program. A warning is now printed instead, and such files + are simply ignored. [#18882] + +astropy.io.misc +^^^^^^^^^^^^^^^ + +- Fixed a bug where writing a table to ECSV fails if meta + contains a value that is a numpy string. [#18677] + +astropy.io.votable +^^^^^^^^^^^^^^^^^^ + +- Updated IVOA UCD1+ controlled vocabulary from version 1.5 to 1.6. This adds + support for new atmospheric observation terms including ``obs.atmos.wind``, + ``obs.atmos.humidity``, ``obs.atmos.rain``, ``obs.atmos.turbulence``, + ``obs.atmos.turbulence.isoplanatic``, ``obs.atmos.water``, and + ``phys.temperature.dew`` which are now recognized when parsing UCDs with + ``check_controlled_vocabulary=True``. [#18483] + +- Fixed a bug in ``add_data_origin_info()`` where ``content`` is ignored for some INFO names. [#18771] + +astropy.modeling +^^^^^^^^^^^^^^^^ + +- Fixed a bug in ``modeling.tabular`` models when the ``lookup_table`` is a Quantity, where the result might lose its unit in some cases. [#18958] + +astropy.nddata +^^^^^^^^^^^^^^ + +- Fixed unexpected upcasting to 64 bits when doing arithmetic with Python scalars + on ``numpy`` 2. + + Don't upcast ``NDData`` unnecessarily when doing arithmetic involving a single + unit (consistent with the behaviour when there are no units). Upcasting still + occurs if an operand's unit gets converted to match the other, or where + required by the other operand's dtype. [#18392] + +astropy.samp +^^^^^^^^^^^^ + +- ``SAMPHubServer._call_and_wait`` raises a new ``SAMPProxyTimeoutError`` (derived from ``SAMPProxyError``) exception on timeout. + This allows client code to more easily distinguish timeouts from other kind of exceptions. [#18169] + +astropy.stats +^^^^^^^^^^^^^ + +- ``poisson_conf_interval`` ``kraft-burrows-nousek`` no longer fails for large N. [#18676] + +astropy.table +^^^^^^^^^^^^^ + +- Fix a bug when slicing a table that has a multi-column index. Previously, after slicing + the table then ``tbl.indices`` would show duplicates of the multi-column index, one for + each column in the index. The underlying indices on the index columns were incorrectly + distinct objects instead of the expected reference to a single index object. [#18694] + +- Fix bugs when indexing a ``QTable`` with a ``Quantity`` column. Previously, after adding + the index then indexed item access via with a ``Quantity`` or slicing was failing. [#18725] + +- Fix a bug where the ECSV writer was not correctly quoting column names if the first name + starts with the "#" character or any names contain leading/trailing whitespace. In this + situation, all column names are now surrounded by double quotes per the ECSV standard. + Likewise the ECSV reader was incorrectly stripping surrounding whitespace from column + names, leading to a consistency check failure when reading. [#18752] + +- Fixed a bug when writing ``Table`` to FITS files, if the table contained masked arrays of integers. [#18818] + +astropy.units +^^^^^^^^^^^^^ + +- The string representations of the liter with the different ``astropy`` unit + formatters are now more consistent with each other. + This change only affects converting units to strings, it has no effect on + parsing strings to units. [#18500] + +- So far only the ``"cds"`` unit format has been capable of parsing the string + ``"as"`` as the attosecond, but now the other unit formats recognize that + string too. [#18723] + +- The ``"ogip"`` unit formatter can now parse strings that include signed + fractions in the exponent, e.g. ``u.Unit("m**(-1/2)", format="ogip")``. [#18776] + +astropy.utils +^^^^^^^^^^^^^ + +- If ``numpy.msort()`` is called with a ``Masked`` array then ``astropy`` no + longer erroneously hides the deprecation warning (with ``numpy`` versions + 1.24-1.26). [#18173] + +- For ``numpy < 2.0``, applying ``np.atleast_*d`` to iterables of most astropy + classes will now return a list of instances instead of a tuple, to match the + behaviour for arrays. For numpy >= 2.0, tuples continue to be returned. [#18193] + +astropy.visualization +^^^^^^^^^^^^^^^^^^^^^ + +- Fixed an image-normalization bug where the interval on a ``ImageNormalize`` + instance could be ignored when plotting. [#18590] + +astropy.wcs +^^^^^^^^^^^ + +- Fixed a bug that caused world_to_array_index to return lists instead of Numpy arrays. [#18730] + +Other Changes and Additions +--------------------------- + +- The minimum required NumPy version is now 1.24. [#18160] + +- The minimum required Matplotlib version is now 3.8.0. [#18164] + +- Bundled ``expat`` is updated to version 2.7.3. [#18657] + +- Updated the bundled CFITSIO library to 4.6.3. [#18689] + +- Wheels are now provided for Windows arm64. [#18786] + +Version 7.1.1 (2025-10-10) +========================== + +Bug Fixes +--------- + +astropy.io.fits +^^^^^^^^^^^^^^^ + +- Writing zero-row BinTableHDU with string columns no longer raises a broadcast error in _ascii_encode() [#18230] + +- Compute maximum absolute and relative differences reported by ``ImageDataDiff`` + on the full arrays instead of only a few values. [#18451] + +- Fix slicing FITS compressed file with ``.section`` when data is scaled. [#18640] + +astropy.io.misc +^^^^^^^^^^^^^^^ + +- Fixed a bug where coordinate frame objects could not be serialized to YAML. This caused + an exception when saving a ``SkyCoord`` object in particular frames like + ``galactocentric`` in which frame attributes are themselves a frame. [#18526] + +astropy.io.votable +^^^^^^^^^^^^^^^^^^ + +- Fix handling of bounded variable-length char arrays in BINARY2 format which were previously treated as fixed length. [#18105] + +astropy.nddata +^^^^^^^^^^^^^^ + +- Fixed key error with numpy functions ``np.min``, ``np.max``, ``np.mean``, and ``np.sum``. [#18424] + +- Fix partial cutouts with FITS compressed file and scaled data. [#18640] + +astropy.table +^^^^^^^^^^^^^ + +- Fixed a bug in ``table.table_helpers.ArrayWrapper`` where byteorder of the + underlying data was not necessarily preserved through roundtrips. [#18139] + +- Fix bug #10732 where removing rows on an indexed table that was subsequently sliced + (e.g. ``t.add_index("a"); ts = t[1:5]; ts.remove_row(2)``) was giving incorrect results + or failing. [#18511] + +astropy.time +^^^^^^^^^^^^ + +- Ensure that the fast C parser for ``Time`` works also with numpy 2.3.0, fixing + a bug in our implementation which had no effect in previous numpy versions. [#18265] + +astropy.timeseries +^^^^^^^^^^^^^^^^^^ + +- Fixed the ``aggregate_downsample`` performance degradation when + non-default ``aggregate_func`` is used. [#18188] + +astropy.units +^^^^^^^^^^^^^ + +- Fixed the LaTeX representation of ``DexUnit`` in ``astropy.units``, + and thus also how it is represented in, e.g., jupyter notebooks. [#18627] + +astropy.visualization +^^^^^^^^^^^^^^^^^^^^^ + +- Fix a bug that caused ``WCSAxes.get_transform`` to not return the correct + transform when using WCS instances with celestial axes that were not in + degrees. [#18311] + +- Fixed a bug that under certain conditions could lead to ticks being incorrectly + labelled with a single "$" dollar sign in WCSAxes. [#18313] + +- Fixed WCSAxes.get_transform() in the case of 1D WCS [#18327] + +- Fixed a bug that caused the default format unit to be incorrect for RA/Dec WCSes with non-degree units [#18346] + +- Fix a bug where the units of the ``values=`` keyword argument to ``set_ticks`` was not respected. [#18577] + +astropy.wcs +^^^^^^^^^^^ + +- Fixed an issue which caused calls to WCS coordinate conversion routines to not be thread-safe due to calls to WCS.wcs.set() from multiple threads. [#16411] + +- Fix a bug that caused the output of ``WCS.wcs.print_contents()`` to be truncated + and to then cause the output of subsequent ``print_contents()`` calls (on + ``WCS.wcs`` or other wcs objects such as ``WCS.wcs.wtb``) to be corrupted. [#18350] + +- Fixed a bug in ``WCS.pixel_to_world`` for spectral WCS where ``restfrq`` was + defined but CTYPE was ``VOPT``, and likewise where ``restwav`` was defined but + CTYPE was ``VRAD``. [#18352] + +- Fixed a bug where world->pixel conversions did not work correctly on a 1D WCS + sliced via ``SlicedLowLevelWCS``. [#18394] + +- Fixed a bug that caused slicing of WCS objects with an ellipsis to not return a WCS + object but instead a SlicedLowLevelWCS object. [#18417] + +- Fixed a bug in ``wcs.py`` that caused the WCS object to not properly initialize + the `_naxis` attribute when the header was empty or did not contain any WCS + information. This could lead to crashes when attempting to take a slice of a 3D + WCS object or it could lead unexpected behavior when accessing pixel shape + or other properties that depend on the number of axes. [#18419] + +- Fixed a race condition when using the APE-14 API for the ``WCS`` class in a multi-threaded environment. [#18692] + + +Performance Improvements +------------------------ + +astropy.modeling +^^^^^^^^^^^^^^^^ + +- Improved performance of ``modeling.rotations.spherical2cartesian()`` by 11-18% depending on the size of the input data arrays. [#18238] + + +Other Changes and Additions +--------------------------- + +- Fixed errors with building the package from source on Windows via + ``python -m build`` and similar commands. [#18253] + +- Pre-built binaries (wheels) for Linux are now built using the ``manylinux_2_28`` + image (previously, ``manylinux2014`` was used). [#18374] + +Version 7.1.0 (2025-05-20) +========================== + + +New Features +------------ + +astropy.coordinates +^^^^^^^^^^^^^^^^^^^ + +- ``search_around_sky`` and ``search_around_3D`` now accept separations/distlimits + broadcastable to the same shape as ``coords1``. [#17824] + +astropy.io.ascii +^^^^^^^^^^^^^^^^ + +- Add functionality to read and write to a Table from the TDAT format as part of + the Unified File Read/Write Interface. [#16780] + +- ``io.ascii`` now supports on-the-fly decompression of LZW-compressed files + (typically ".Z" extension) via the optional package uncompresspy. [#17960] + +astropy.io.fits +^^^^^^^^^^^^^^^ + +- Astropy can now not only read but also write headers that have ``HIERARCH`` + keys with long values, by allowing the use of ``CONTINUE`` cards for those + (as was already the case for regular FITS keys). [#17748] + +- Add ``strip_spaces`` option to ``Table.read`` to strip trailing whitespaces in + string columns. This will be activated by default in the next major release. [#17777] + +- ``io.fits`` now supports on-the-fly decompression of LZW-compressed files + (typically ".Z" extension) via the optional package uncompresspy. [#17960] + +- ``io.fits`` now supports on-the-fly decompression of LZMA-compressed files + (typically ".xz" extension) if the lzma module is provided by the Python + installation. [#17968] + +astropy.io.misc +^^^^^^^^^^^^^^^ + +- Add a fast ``Table`` CSV reader that uses the PyArrow ``read_csv()`` function. This can + be significantly faster and more memory-efficient than the ``astropy.io.ascii`` fast + reader. This new reader can be used with ``Table.read()`` by setting + ``format="pyarrow.csv"``. [#17706] + +astropy.io.votable +^^^^^^^^^^^^^^^^^^ + +- New module ``astropy.io.votable.dataorigin`` to extract Data Origin information from INFO in VOTable. [#17839] + +- ``CooSys`` VOTable elements now have a method ``to_astropy_frame`` that returns the + corresponding astropy built-in frame, when possible. [#17999] + +astropy.modeling +^^^^^^^^^^^^^^^^ + +- Added a ``fit_info=`` keyword argument to ``parallel_fit_dask`` to allow users to preserve fit information from each individual fit. [#17538] + +astropy.nddata +^^^^^^^^^^^^^^ + +- Adds a utility class, ``astropy.nddata.Covariance``, used to construct, access, + and store covariance matrices. The class depends on use of the ``scipy.sparse`` + module. [#16690] + +- Add the ``limit_rounding_method`` parameter to `~astropy.nddata.Cutout2D`, + `~astropy.nddata.overlap_slices`, `~astropy.nddata.extract_array`, and + `~astropy.nddata.add_array` to allow users to specify the rounding method + used when calculating the pixel limits of the cutout. The default method + is to use `~numpy.ceil`. [#17876] + +astropy.table +^^^^^^^^^^^^^ + +- Document that ``Table.group_by``'s underlying sorting algorithm is guaranteed + to be stable. This reflects behavior that was already present but undocumented, + at least since astropy 6.0 . [#17676] + +astropy.timeseries +^^^^^^^^^^^^^^^^^^ + +- Downsampling now works correctly also on ``MaskedColumn`` and + ``MaskedQuantity`` with possibly masked elements. Furthermore, the type of + (Masked) column will now be properly preserved in downsampling. [#18023] + +astropy.units +^^^^^^^^^^^^^ + +- Units with the "micro" prefix can now be imported using ``"Îŧ"`` in the name. + For example, the microgram can now be imported with + ``from astropy.units import Îŧg``. [#17651] + +- It is now possible to import angstrÃļm, litre and ohm from ``astropy.units`` + using the ``Å``, ``ℓ`` and ``Ί`` symbols. [#17829] + +- Unit conversions between kelvins and degrees Rankine no longer require the + ``temperature`` equivalency. [#17985] + +astropy.utils +^^^^^^^^^^^^^ + +- Make commonly used Masked subclasses importable for ASDF support. + + Registered types associated with ASDF converters must be importable by + their fully qualified name. Masked classes are dynamically created and have + apparent names like ``astropy.utils.masked.core.MaskedQuantity`` although + they aren't actually attributes of this module. Customize module attribute + lookup so that certain commonly used Masked classes are importable. + + See: + + - https://asdf.readthedocs.io/en/latest/asdf/extending/converters.html#entry-point-performance-considerations + - https://github.com/astropy/asdf-astropy/pull/253 [#17685] + +- ``astropy.utils.data.download_file`` can now recover from a ``TimeoutError`` + when given a list of alternative source URLs. Previously, only ``URLError`` + exceptions were recoverable. An exception is still being raised after trying all + URLs provided if none of them could be reached. [#17691] + +- ``utils.data`` now supports on-the-fly decompression of LZW-compressed files + (typically ".Z" extension) via the optional package uncompresspy. [#17960] + + +API Changes +----------- + +astropy.coordinates +^^^^^^^^^^^^^^^^^^^ + +- On representations the method ``get_name`` has been deprecated in favor of the class-level + attribute ``name``. The method will be removed in a future release. [#17503] + +astropy.cosmology +^^^^^^^^^^^^^^^^^ + +- A new public module, ``astropy.cosmology.io``, has been added to provide support + for reading, writing, and converting cosmology instances. + + The private modules ``astropy.cosmology.funcs``, + ``astropy.cosmology.parameter``, ``astropy.cosmology.connect``, + ``astropy.cosmology.core``, and ``astropy.cosmology.flrw`` have been deprecated. + Their functionality remains accessible in the `astropy.cosmology` module or in + the new ``astropy.cosmology.io`` module. [#17543] + +- Comoving distances now accept an optional 2nd argument, where the two-argument form is + the comoving distance between two redshifts. The one-argument form is the comoving + distance from redshift 0 to the input redshift. [#17701] + +- A new public module, ``astropy.cosmology.traits``, has been added to provide building + blocks for custom cosmologies. The currently available traits are: + - ``astropy.cosmology.traits.ScaleFactor`` + - ``astropy.cosmology.traits.TemperatureCMB`` [#17702] + +astropy.extern +^^^^^^^^^^^^^^ + +- Astropy used to bundle the javascript libraries jQuery and DataTables for + interactive (e.g. sorting by column values) tables using the ``show_in_browser()`` + method. + This bundling requires relatively large files in astropy itself, for a relatively minor feature. + Furthermore, the astropy developers are not experts in javascript development, and + javascript libraries many need updates to improve on security vulnerabilities. + This change removes the bundled versions of jQuery and DataTables from astropy, + updates the default version of the remote URLs to version 2.1.8 of DataTables, and + sets the default for ``show_in_browser(use_local_files=False)`` to use the remote versions + in all cases. If the method is called with ``use_local_files=True``, a warning is + displayed and remote version are used anyway. + This may break the use of the method when working offline, unless the javascript + files are cached by the browser from a previous online session. [#17521] + +astropy.table +^^^^^^^^^^^^^ + +- ``showtable`` CLI is now deprecated to avoid a name clash on Debian; use ``showtable-astropy`` instead. [#18047] + +- Fix issues in the handling of a call like ``tbl.loc[item]`` or ``tbl.loc_indices[item]`` + and make the behavior consistent with pandas. Here ``tbl`` is a ``Table`` or ``QTable`` + with an index defined. + + If ``item`` is an empty list or zero-length ``np.ndarray`` or an empty slice, then + previously ``tbl.loc[item]`` would raise a ``KeyError`` exception. Now it returns the + zero-length table ``tbl[[]]``. + + If ``item`` is a one-element list like ``["foo"]``, then previously + ``tbl.loc[item]`` would return either a ``Row`` or a ``Table`` with multiple row, + depending on whether the index was unique. Now it always returns a ``Table``, consistent + with behavior for ``tbl.loc[[]]`` and ``tbl.loc[["foo", "bar"]]``. + + See https://github.com/astropy/astropy/pull/18051 for more details. [#18051] + +astropy.units +^^^^^^^^^^^^^ + +- Passing ``fraction='multiline'`` to ``unit.to_string()`` will no longer raise + an exception if the given format does not support multiline fractions, but + rather give a warning and use an inline fraction. [#17374] + +- Automatic conversion of a ``str`` or ``bytes`` instance to a unit when it is + multiplied or divided with an existing unit or quantity is deprecated. [#17586] + +- Accessing the contents of the ``units.deprecated`` module now emits deprecation + warnings. + The module may be removed in a future version. [#17929] + +astropy.visualization +^^^^^^^^^^^^^^^^^^^^^ + +- All arguments from ``simple_norm`` are marked as future keyword-only, with the + exception of the first two (``data`` and ``stretch``). + A warning is now displayed if any other arguments are passed positionally. [#17489] + + +Bug Fixes +--------- + +astropy.io.fits +^^^^^^^^^^^^^^^ + +- Fix possible int overflow in the tile compression C code. [#17995] + +astropy.io.votable +^^^^^^^^^^^^^^^^^^ + +- In ``CooSys`` elements, the system was not checked for votable version 1.5 [#17999] + +astropy.samp +^^^^^^^^^^^^ + +- Fix setting logging level from the ``samp_hub`` command line. + Previously, ``samp_hub --log-level OFF`` was documented as supported but actually caused an exception to be raised. + The patch infers valid choices from the standard library's ``logging`` module. + A ``CRITICAL`` level will closely emulate the intended ``OFF`` setting. [#17673] + +astropy.table +^^^^^^^^^^^^^ + +- Initializing a Table with ``rows`` or ``data`` set to ``[]`` or a numpy array with + zero size (e.g., ``np.array([[], []])``) is now equivalent to + ``Table(data=None, ...)`` and creates a table with no data values. This allows + defining the table names and/or dtype when creating the table, for instance: + ``Table(rows=[], names=["a", "b"], dtype=[int, float])``. Previously this + raised an exception. [#17717] + +- Fix issues in the handling of a call like ``tbl.loc[item]`` or ``tbl.loc_indices[item]`` + and make the behavior consistent with pandas. Here ``tbl`` is a ``Table`` or ``QTable`` + with an index defined. + + If ``item`` is an empty list or zero-length ``np.ndarray`` or an empty slice, then + previously ``tbl.loc[item]`` would raise a ``KeyError`` exception. Now it returns the + zero-length table ``tbl[[]]``. + + If ``item`` is a one-element list like ``["foo"]``, then previously + ``tbl.loc[item]`` would return either a ``Row`` or a ``Table`` with multiple row, + depending on whether the index was unique. Now it always returns a ``Table``, consistent + with behavior for ``tbl.loc[[]]`` and ``tbl.loc[["foo", "bar"]]``. + + See https://github.com/astropy/astropy/pull/18051 for more details. [#18051] + +astropy.timeseries +^^^^^^^^^^^^^^^^^^ + +- Made ``TimeSeries.from_pandas`` and ``BinnedTimeSeries.read`` more robust to + subclassing. [#17351] + +astropy.units +^^^^^^^^^^^^^ + +- For the Angstrom unit in the CDS module, ``u.cds.Angstrom``, the string + representation is now "Angstrom" (instead of "AA"), consistent with what was + always the case for ``u.Angstrom``, and conformant with the CDS standard. [#17536] + +- Previously the string representation of the ``solMass`` unit in the ``"cds"`` + format depended on whether the unit was imported directly from ``units`` or + from ``units.cds``. + Although both representations were valid according to the CDS standard, the + inconsistency was nonetheless needlessly surprising. + The representation of ``units.cds.solMass`` has been changed to match the + representation of ``units.solMass``. [#17560] + +- The degrees Rankine is now represented as "$\mathrm{{}^{\circ}R}$" in the + ``"latex"`` and ``"latex_inline"`` formats and as "°R" in the ``"unicode"`` + format. [#18049] + +astropy.utils +^^^^^^^^^^^^^ + +- Properly detect invalid LZMA files in ``utils.data``. [#17984] + +astropy.visualization +^^^^^^^^^^^^^^^^^^^^^ + +- Fixed an issue when using ``plot_coord`` after slicing the ``WCS`` object coordinates. [#18005] + + +Performance Improvements +------------------------ + +astropy.timeseries +^^^^^^^^^^^^^^^^^^ + +- Improved the ``aggregate_downsample`` performance using a new default ``aggregate_func``. [#17574] + +astropy.units +^^^^^^^^^^^^^ + +- Converting strings to units with ``Unit()`` is now up to 225% faster. [#17399] + +- ``UnitBase.compose()`` is now 20% faster. [#17425] + + +Other Changes and Additions +--------------------------- + +- After ``import astropy``, ``dir(astropy)`` will now list all subpackages, + including those that have not yet been loaded. This also means tab + completion will work as expected (e.g., ``from astropy.coo`` will + expand to ``from astropy.coordinates``). [#17598] + +- Updated bundled WCSLIB version to 8.4, fixing issues in ``wcs_chksum`` + and ``wcs_fletcher32``. For a full list of changes - see + ``astropy/cextern/wcslib/CHANGES``. [#17886] + +Version 7.0.2 (2025-05-12) +========================== + +Bug Fixes +--------- + +astropy.config +^^^^^^^^^^^^^^ + +- Fix a bug where config file generation did not parse nested subclasses of ``astropy.config.ConfigNamespace``. [#18107] + +astropy.io.fits +^^^^^^^^^^^^^^^ + +- Fix a bug in ``nddata.Cutout2D`` when creating partial cutouts of ``Section`` objects by adding a ``dtype`` property to the ``Section`` class. [#17611] + +- Fixed a bug so that now the scaling state from the source HDU to the new appended HDU is copied on the + destination file, when the HDU is read with ``do_not_scale_image_data=True``. [#17642] + +- Fix setting a slice on table rows (``FITS_record``). [#17737] + +- Fix checksum computation for tables with VLA columns, when table is loaded in + memory. [#17806] + +- Fix ``.fileinfo()`` for compressed HDUs. [#17815] + +- Fix FITS_rec repr when a column has scaling factors, leading to a crash with + numpy>=2.0. [#17933] + +- Fixed a bug that caused THEAP, ZBLANK, ZSCALE, and ZZERO to not be correctly + removed during decompression of tile-compressed FITS files. [#18072] + +astropy.io.votable +^^^^^^^^^^^^^^^^^^ + +- ``astropy`` v7.0.0 erroneously refused to write a VOTable if it contained units that + could not be represented in the CDS format. + Now ``astropy`` correctly chooses the unit format based on the VOTable version. + The bug in question did not cause any corruption in tables that were successfully + written because the newer VOUnit format is backwards compatible with the CDS format. + Furthermore, any unit that is in neither formats would still be written out + but would issue a warning. [#17570] + +- ``unicodeChar`` fields can now be of bounded variable size (``arraysize="10*``). [#18075] + +astropy.modeling +^^^^^^^^^^^^^^^^ + +- Fixed an issue where the ``filter_non_finite`` option was not working + for 2D models. An error is raised when the ``filter_non_finite`` option + is set to ``True`` and all values are non-finite. [#17869] + +astropy.stats +^^^^^^^^^^^^^ + +- Now ``bayesian_blocks(t, x, fitness="events")`` correctly handles the case + when the input data ``x`` contains zeros. [#17800] + +astropy.table +^^^^^^^^^^^^^ + +- Prevent corrupting a column by mutating its name to an invalid type. + A ``TypeError`` is now raised when a name is set to anything other than a + string. [#17450] + +- Fix a bug in creating a ``Table`` from a list of rows that dropped the units + of non-scalar Quantity, e.g., ``Table(rows=[([1] * u.m,), ([2] * u.m,)])``. [#17936] + +astropy.units +^^^^^^^^^^^^^ + +- Ensured that the units of ``yp``, ``refa`` and ``refb`` are properly + taken into account when calling ``erfa.apio`` (previously, the + conversion required for ``xp`` was applied to those inputs too). [#17742] + +- The machinery that injects units into a namespace (used e.g. by ``def_unit()``) + now applies NFKC normalization to unit names when checking for name collisions. + This prevents name collisions if the namespace belongs to a module and the unit + is accessed as an attribute of that module. [#17853] + +- The string representations of the prefixed versions of ``solLum``, ``solMass`` + and ``solRad`` units can now be parsed by default. + Previously they could only be parsed if the ``required_by_vounit`` module had + been imported, possibly indirectly by using the ``"vounit"`` format. [#17868] + +astropy.utils +^^^^^^^^^^^^^ + +- Prevent corrupting a mixin column's ``info`` attribute by mutating its name to + an invalid type. A ``TypeError`` is now raised when a name is set to anything + other than a string. [#17450] + +astropy.visualization +^^^^^^^^^^^^^^^^^^^^^ + +- Ensure that the ``astropy.visualization.wcsaxes.custom_ucd_coord_meta_mapping`` + context manager performs a (correct) cleanup. [#17749] + +- Fixed interval classes for masked input (``MaskedArray`` and ``MaskedNDArray``). [#17927] + +- Fixed the limits of ``a`` parameter in the ``PowerDistStretch`` + and ``InvertedPowerDistStretch`` classes so that a value of + 0 in no longer allowed. That value gives infinity values in + ``InvertedPowerDistStretch`` and it makes the ``PowerDistStretch`` + results independent of the input data. [#17941] + +- Fixed an issue where LinearStretch values were not being clipped to + [0:1] when ``clip=True``. [#17943] + +astropy.wcs +^^^^^^^^^^^ + +- Fix UCD for air wavelengths, following the IVOA recommendation that ``'em.wl'`` + be reserved for vacuum wavelengths. ``'em.wl;obs.atmos'`` is now used to + represent air wavelengths instead. [#17769] + + +Other Changes and Additions +--------------------------- + +- Updated the bundled CFITSIO library to 4.6.0. [#17904] + +Version 7.0.1 (2025-02-06) +========================== + +API Changes +----------- + +astropy.table +^^^^^^^^^^^^^ + +- The use of the keyword ``use_local_files`` for the js viewer in + ``astropy.table.Table.show_in_browser`` is now deprecated. Starting in Astropy + 7.1 this keyword will be ignored and use of it will issue a warning. The + default behavior will be to use the remote versions of jQuery and DataTables + from a CDN. [#17480] + +Bug Fixes +--------- + +astropy.config +^^^^^^^^^^^^^^ + +- With ``astropy`` v7.0.0 the cache directory cannot be customized with the + ``XDG_CACHE_HOME`` environment variable. + Instead, ``XDG_CONFIG_HOME`` erroneously controls both configuration and cache + directories. + The correct pre-v7.0.0 behaviour has been restored, but it is possible that + ``astropy`` v7.0.0 has written cache files to surprising locations. + Concerned users can use the ``get_cache_dir_path()`` function to check where + the cache files are written. + + The bug in question does not affect systems where the ``XDG_CACHE_HOME`` and + ``XDG_CONFIG_HOME`` environment variables are unset. [#17514] + +astropy.coordinates +^^^^^^^^^^^^^^^^^^^ + +- Fixed a numerical-precision bug with the calculation of the ``theta`` + component when converting from ``CylindricalRepresentation`` to + ``PhysicsSphericalRepresentation`` for vectors very close to the Z axis (within + milliarcseconds). [#17693] + +astropy.io.ascii +^^^^^^^^^^^^^^^^ + +- Fixed parsing ASCII table with data that starts with a tilda. [#17565] + +- Find and read ASCII tables even if there is white space before + ``\begin{tabular}``, ``\tablehead``, and similar markers. [#17624] + +astropy.io.fits +^^^^^^^^^^^^^^^ + +- Fix memory leak in ```BinTableHDU.copy()``` [#16143] + +- Fix overflow error with Numpy 2 and VLA columns using P format. [#17328] + +- Fix ``ImageHDU.scale`` with float. [#17458] + +- Fixed ``Table.write(..., format="fits", overwrite=True)`` when filename is + provided as ``pathlib.Path``. [#17552] + +astropy.io.votable +^^^^^^^^^^^^^^^^^^ + +- Updated XML writer for ``VOTableFile`` element to include or drop + ``coordinate_systems`` regardless of version. [#17356] + +astropy.modeling +^^^^^^^^^^^^^^^^ + +- Fix fitting of compound models when inputs has more than one dimension, and + specifically fix fitting of compound models with Polynomial2D components + [#17618] + +astropy.table +^^^^^^^^^^^^^ + +- Ensure that representations and differentials, like SkyCoord, can be used in + table join operations, by making use of the fact that they can now be masked. [#17381] + +- Fix a crash in ``Table.show_in_browser`` due to an internal type inconsistency. [#17513] + +- Fix incorrect description of the ``unique`` parameter in ``Table.add_index``'s + docstring. Add missing Raises section. [#17677] + +astropy.units +^^^^^^^^^^^^^ + +- Ensure that ``Unit.to`` allows as ``value`` argument all array types that + follow the array API standard and define ``__array_namespace__``. Furthermore, + for backwards compatibility, generally pass through arguments that define a + ``.dtype``, independent of whether that is a numpy data type. [#17469] + +- The zebi (Zi, 2^70) and yobi (Yi, 2^80) binary prefixes are now supported. [#17692] + +astropy.visualization +^^^^^^^^^^^^^^^^^^^^^ + +- Fix ``CoordinateHelper.ticklabels``. The getter was incorrectly returning + the helper's ticks rather than the labels. [#17444] + +- The following private classes from ``astropy.visualization.lupton_rgb``, that + were dropped without deprecation in astropy 7.0.0, were re-introduced following + a report that they were used downstream. The following classes are now + considered public: + + - ``Mapping`` + - ``AsinhMapping`` + - ``LinearMapping`` + - ``AsinhZScaleMapping`` [#17531] + +Other Changes and Additions +--------------------------- + +- Update bundled js library datatables to version 2.1.8, which is current at the time of this PR. [#17480] + +Version 7.0.0 (2024-11-21) +========================== + + +New Features +------------ + +astropy.config +^^^^^^^^^^^^^^ + +- Added ``get_config_dir_path`` (and ``get_cache_dir_path``) which is equivalent + to ``get_config_dir`` (respectively ``get_cache_dir``) except that it returns a + ``pathlib.Path`` object instead of ``str``. [#17118] + +astropy.coordinates +^^^^^^^^^^^^^^^^^^^ + +- ``BaseCoordinateFrame`` instances such as ``ICRS``, ``SkyOffsetFrame``, etc., + can now be stored directly in tables (previously, they were stored as + ``object`` type columns). Furthermore, storage in tables is now also possible + for frames that have no data (but which have attributes with the correct shape + to fit in the table). [#16831] + +- ``BaseCoordinateFrame`` now has a ``to_table()`` method, which converts the + frame to a ``QTable``, analogously to the ``SkyCoord.to_table()`` method. [#17009] + +- ``SkyCoord``, coordinate frames, and representations have all have gained the + ability to deal with ``Masked`` data. In general, the procedure is similar to + that of ``Time``, except that different representation components do not share + the mask, to enable holding, e.g., a catalogue of objects in which only some + have associated distances. [#17016] + +astropy.io.ascii +^^^^^^^^^^^^^^^^ + +- Add support for ``pathlib.Path`` objects in + ``astropy.io.ascii.core.BaseInputter.get_lines``. [#16930] + +astropy.io.fits +^^^^^^^^^^^^^^^ + +- Expanded ``FITSDiff`` output for ``PrimaryHDU`` and ``ImageHDU`` to include the + maximum relative and absolute differences in the data. [#17097] + +astropy.io.misc +^^^^^^^^^^^^^^^ + +- The HDF5 writer, ``write_table_hdf5()``, now accepts ``os.PathLike`` objects + as ``output``. [#16955] + +astropy.io.votable +^^^^^^^^^^^^^^^^^^ + +- Support reading and writing of VOTable version 1.5, including the new + ``refposition`` attribute of ``COOSYS``. [#16856] + +astropy.modeling +^^^^^^^^^^^^^^^^ + +- Added ``Model.has_tied``, ``Model.has_fixed``, and ``Model.has_bounds`` attributes to make + it easy to check whether models have various kinds of constraints set without having to + inspect ``Model.tied``, ``Model.fixed``, and ``Model.bounds`` in detail. [#16677] + +- Added a new ``parallel_fit_dask`` function that can be used to fit models to + many sections (e.g. spectra, image slices) on an N-dimensional array in + parallel. [#16696] + +- Added a ``Lorentz2D`` model. [#16800] + +- Added ``inplace=False/True`` keyword argument to the ``__call__`` method of most fitters, + to optionally allow the original model passed to the fitter to be modified with the fitted + values of the parameters, rather than return a copy. This can improve performance if users + don't need to keep hold of the initial parameter values. [#17033] + +astropy.stats +^^^^^^^^^^^^^ + +- Added a ``SigmaClippedStats`` convenience class for computing sigma-clipped + statistics. [#17221] + +astropy.table +^^^^^^^^^^^^^ + +- Changed a number of dict-like containers in ``io.ascii`` from ``OrderedDict`` to + ``dict``. The ``dict`` class maintains key order since Python 3.8 so ``OrderedDict`` is + no longer needed. The changes are largely internal and should not affect users in any + way. See also the API change log entry for this PR. [#16250] + +- Add a ``keep_order`` argument to the ``astropy.table.join`` function which specifies to + maintain the original order of the key table in the joined output. This applies for + inner, left, and right joins. The default is ``False`` in which case the output is + ordered by the join keys, consistent with prior behavior. [#16361] + +astropy.units +^^^^^^^^^^^^^ + +- Add a ``formatter`` argument to the ``to_string`` method of the ``Quantity`` + class. Enables custom number formatting with a callable formatter or + format_spec, especially useful for consistent notation. [#16087] + +- Add the unit foe (or Bethe, equivalent to 1e51 erg), which is often used to + express the energy emitted by a supernova explosion. [#16441] + +- Add ``magnetic_flux_field`` equivalency to convert magnetic field between + magnetic field strength (H) and magnetic flux density (B). [#16516] + +- Added SI-units ``sievert``, ``gray``, ``katal``, and ``hectare`` in ``astropy.units.si``. [#16729] + +- When parsing invalid unit strings with ``u.Unit(..., parse_strict="warn")`` or + ``u.Unit(..., parse_strict="silent")``, a normal unit may be returned if the + problem is not too serious. + If parsing the string fails completely then an ``UnrecognizedUnit`` instance is + returned, just as before. [#16892] + +- Added a ``np.arange`` dispatch for ``Quantity`` (requires one to use + ``like=``). [#17059] + +- Added support for calling numpy array constructors (``np.empty``, ``np.ones``, + ``np.zeros`` and ``np.full``) with ``like=Quantity(...)`` . [#17120] + +- Added support for calling numpy array constructors (``np.array``, + ``np.asarray``, ``np.asanyarray``, ``np.ascontiguousarray`` and + ``np.asfortranarray``) with ``like=Quantity(...)`` . [#17125] + +- Added support for calling numpy array constructors (``np.frombuffer``, + ``np.fromfile``, ``np.fromiter``, ``np.fromstring`` and ``np.fromfunction``) + with ``like=Quantity(...))`` . [#17128] + +- Added support for calling numpy array constructors (``np.require``, + ``np.identity``, ``np.eye``, ``np.tri``, ``np.genfromtxt`` and ``np.loadtxt``) + with ``like=Quantity(...))`` . [#17130] + +astropy.utils +^^^^^^^^^^^^^ + +- Added the ``astropy.system_info`` function to help document runtime systems in + bug reports. [#16335] + +- Add support for specifying files as ``pathlib.Path`` objects in ``IERS_A.read`` + and ``IERS_B.read``. [#16931] + +astropy.visualization +^^^^^^^^^^^^^^^^^^^^^ + +- Add ``make_rgb()``, a convenience + function for creating RGB images with independent scaling on each filter. + Refactors ``make_lupton_rgb()`` to work with instances of subclasses of + ``BaseStretch``, including the new Lupton-specific classes + ``LuptonAsinhStretch`` and ``LuptonAsinhZscaleStretch``. [#15081] + +- Add support for custom coordinate frames for ``WCSAxes`` through a context + manager ``astropy.visualization.wcsaxes.custom_ucd_coord_meta_mapping``. [#16347] + +- Added ``get_ticks_position``, ``get_ticklabel_position``, and + ``get_axislabel_position`` methods on ``CoordinateHelper`` in WCSAxes. [#16686] + +- Added the ability to disable the automatic simplification of WCSAxes tick labels + by specifying ``simplify=False`` to ``set_ticklabel()`` for a coordinate axis. [#16938] + +- Added the ability to specify that WCSAxes tick labels always include the sign + (namely for positive values) by starting the format string with a ``+`` + character. [#16985] + +- Allow ``astropy.visualization.units.quantity_support`` to be used as a + decorator in addition to the already supported use as a context manager. [#17006] + +- Added the ability to specify a callable function in ``CoordinateHelper.set_major_formatter`` [#17020] + +- Added a ``SimpleNorm`` class to create a matplotlib normalization object. [#17217] + +- WCSAxes will now select which axis to draw which tick labels and axis labels on based on the number of drawn tick labels, rather than picking them in the order they are listed in the WCS. This means that axes may be swapped in comparison with previous versions of Astropy by default. [#17243] + + +API Changes +----------- + +astropy.coordinates +^^^^^^^^^^^^^^^^^^^ + +- For non-scalar frames without data, ``len(frame)`` will now return the first + element of its ``shape``, just like for frames with data (or arrays more + generally). For scalar frames, a ``TypeError`` will be raised. Both these + instead of raising a ``ValueError`` stating the frame has no data. [#16833] + +- The deprecated ``coordinates.get_moon()`` function has been removed. Use + ``coordinates.get_body("moon")`` instead. [#17046] + +- The deprecated ``BaseCoordinateFrame.get_frame_attr_names()`` is removed. + Use ``get_frame_attr_defaults()`` instead. [#17252] + +astropy.cosmology +^^^^^^^^^^^^^^^^^ + +- Passing redshift arguments as keywords is deprecated in many methods. [#16597] + +- Deprecated ``cosmology.utils`` module has been removed. Any public API may + be imported directly from the ``cosmology`` module instead. [#16730] + +- Setting ``Ob0 = None`` in FLRW cosmologies has been deprecated in favor of ``Ob0 = + 0.0``. Conceptually this is a change in that baryons are now always a component of the + cosmology. Practically, the only change (besides that ``Ob0`` is never ``None``) is that + methods relying on ``Ob0`` always work, rather than sometimes raising an exception, + instead by default taking the contribution of the baryons to be negligible. [#16847] + +astropy.io.ascii +^^^^^^^^^^^^^^^^ + +- Remove all deprecated arguments from functions within ``astropy.io.ascii``. + + ``read()``: + - ``Reader`` is removed. Instead supply the equivalent ``format`` argument. + - Use ``inputter_cls`` instead of ``Inputter``. + - Use ``outputter_cls`` instead of ``Outputter``. + + ``get_reader()``: + - Use ``reader_cls`` instead of ``Reader``. + - Use ``inputter_cls`` instead of ``Inputter``. + - Use ``outputter_cls`` instead of ``Outputter``. + + ``write()``: + - ``Writer`` is removed. Instead supply the equivalent ``format`` argument. + + ``get_writer()``: + - Use ``writer_cls`` instead of ``Writer``. [#15758] + +astropy.io.fits +^^^^^^^^^^^^^^^ + +- The ``CompImageHDU`` class has been refactored to inherit from ``ImageHDU`` + instead of ``BinTableHDU``. This change should be for the most part preserve the + API, but any calls to ``isinstance(hdu, BinTableHDU)`` will now return ``False`` + if ``hdu`` is a ``CompImageHDU`` whereas before it would have returned ``True``. + In addition, the ``uint`` keyword argument to ``CompImageHDU`` now defaults to + ``True`` for consistency with ``ImageHDU``. [#15474] + +- Remove many unintended exports from ``astropy.io.fits.hdu.compressed``. + The low-level functions ``compress_image_data`` and ``decompress_image_data_section`` + are now only available at the qualified names + ``astropy.io.fits.hdu.compressed._tiled_compression.compress_image_data`` + and ``astropy.io.fits.hdu.compressed._tiled_compression.decompress_image_data_section``. + The rest of the removed exports are external modules or properly exported + elsewhere in astropy. May break imports in rare cases that relied + on these exports. [#15781] + +- The ``CompImageHeader`` class is now deprecated, and headers on ``CompImageHDU`` + instances are now plain ``Header`` instances. If a reserved keyword is set on + ``CompImageHDU.header``, a warning will now be emitted at the point where the + file is written rather than at the point where the keyword is set. [#17100] + +- - Remove code that was deprecated in previous versions: ``_ExtensionHDU`` and + ``_NonstandardExtHDU``, ``(Bin)Table.update``, ``tile_size`` argument for + ``CompImageHDU``. Also specifying an invalid ``tile_shape`` now raises an + error. [#17155] + +astropy.io.misc +^^^^^^^^^^^^^^^ + +- New format ``"parquet.votable"`` is added to read and write a parquet file + with a votable metadata included. [#16375] + +astropy.io.votable +^^^^^^^^^^^^^^^^^^ + +- ``Table.read(..., format='votable')``, ``votable.parse`` and + ``votable.parse_single_table`` now respect the ``columns`` argument and will only output + selected columns. Previously, unselected columns would just be masked (and unallocated). + ``astropy.io.votable.tree.TableElement.create_arrays`` also gained a ``colnumbers`` + keyword argument to allow column selection. [#15959] + +astropy.modeling +^^^^^^^^^^^^^^^^ + +- Subclasses of ``_NonLinearLSQFitter``, so any subclasses of the public ``LevMarLSQFitter``, ``TRFLSQFitter``, ``LMLSQFitter`` or ``DogBoxLSQFitter``, should now accept an additional ``fit_param_indices`` kwarg in the function signature of their ``objective_function`` methods. + Nothing is needed to be done with this kwarg, and it might not be set, but it can optionally be passed through to ``fitter_to_model_params_array`` for a performance improvement. + We also recommended accepting all kwargs (with ``**kwargs``) in this method so that future additional kwargs do not cause breakage. [#16673] + +- Exception message for when broadcast shapes mismatch has changed. + Previously, it used complicated regex to maintain backward compatibility. + To ease maintenance, this regex has been removed and now directly + passes exception from ``numpy.broadcast_shapes`` function. [#16770] + +- Using the ``LMLSQFitter`` fitter with models that have bounds is now deprecated, + as support for bounds was very basic. Instead, non-linear fitters with more + sophisticated support for bounds should be used instead. [#16994] + +- The optional ``use_min_max_bounds`` keyword argument in ``TRFLSQFitter`` and + ``DogBoxLSQFitter`` has now been deprecated and should not be used. These + fitters handle bounds correctly by default and this keyword argument was only + provided to opt-in to a more basic form of bounds handling. [#16995] + +- The deprecated ``comb()`` function has been removed. + Use ``math.comb()`` from the Python standard library instead. [#17248] + +astropy.stats +^^^^^^^^^^^^^ + +- Integer inputs to ``sigma_clip`` and ``SigmaClip`` are not converted to + ``np.float32`` instead of ``float`` if necessary. [#17116] + +astropy.table +^^^^^^^^^^^^^ + +- Change the default type for the ``meta`` attribute in ``Table`` and ``Column`` (and + subclasses) from ``OrderedDict`` to ``dict``. Since Python 3.8 the ``dict`` class is + ordered by default, so there is no need to use ``OrderedDict``. + + In addition the ECSV table writer in ``astropy.io.ascii`` was updated to consistently + write the ``meta`` attribute as an ordered map using the ``!!omap`` tag. This + convention conforms to the ECSV specification and is supported by existing ECSV readers. + Previously the ``meta`` attribute could be written as an ordinary YAML map, which is not + guaranteed to preserve the order of the keys. [#16250] + +- An exception is now raised when trying to add a multi-dimensional column as an + index via ``Table.add_index``. [#16360] + +- Aggregating table groups for ``MaskedColumn`` no longer converts + fully masked groups to ``NaN``, but instead returns a masked element. [#16498] + +- Always use ``MaskedQuantity`` in ``QTable`` to represent masked ``Quantity`` + data or when the ``QTable`` is created with ``masked=True``. Previously the + default was to use a normal ``Quantity`` with a ``mask`` attribute of type + ``FalseArray`` as a stub to allow a minimal level of compatibility for certain + operations. This update brings more consistent behavior and fixes functions + like reading of table data from a list of dict that includes quantities with + missing entries, and aggregation of ``MaskedQuantity`` in table groups. [#16500] + +- Setting an empty table to a scalar no longer raises an exception, but + creates an empty column. This is to support cases where the number of + elements in a table is not known in advance, and could be zero. [#17102] + +- ``show_in_notebook`` method for Astropy tables has been un-deprecated and the API has + been updated to accept a ``backend`` keyword and require only keyword arguments. The new + default ``backend="ipydatagrid"`` relies on an optional dependency, ``ipydatagrid``. The + previous default table viewer (prior to v7.0) is still available as + ``backend="classic"``, but it has been deprecated since v6.1 and will be removed in a future release. [#17165] + +- The default behavior of ``Table.pformat`` was changed to include all rows and columns + instead of truncating the outputs to fit the current terminal. The new default + keyword arguments ``max_width=-1`` and ``max_lines=-1`` now match those in + ``Table.pformat_all``. Since the ``Table.pformat_all`` method is now redundant, it is + pending deprecation. Similarly, the default behavior of ``Column.pformat`` was changed + to include all rows instead of truncating the outputs to fit the current terminal. [#17184] + +astropy.time +^^^^^^^^^^^^ + +- ``Time.ptp`` now properly emits a deprecation warning independently of NumPy's + version. This method was previously deprecated in astropy 6.1, but the warning + was not visible for users that had NumPy 1.x installed. Because of this, the + warning message was updated to state that ``Time.ptp`` is deprecated since + version 7.0 instead. [#17212] + +astropy.units +^^^^^^^^^^^^^ + +- The deprecated ``Quantity.nansum()`` method has been removed. Use + ``np.nansum`` instead. [#15642] + +- The ``factor`` parameter of the ``spectral_density`` equivalency, the use of + which has been discouraged in the documentation since version 0.3, is now + deprecated. + Use the ``wav`` parameter as a ``Quantity``, not as a bare unit. [#16343] + +- The ``format.Fits`` formatter class has been renamed to ``format.FITS`` and the + old name is deprecated. + Specifying the FITS format for converting ``Quantity`` and ``UnitBase`` + instances to and from strings is not affected by this change. [#16455] + +- Conversion from one unit to another using ``old_unit.to(new_unit, value)`` no longer + converts ``value`` automatically to a numpy array, but passes through array duck types + such as ``dask`` arrays, with equivalencies properly accounted for. [#16613] + +- The ``format_exponential_notation()`` method of the ``Base`` unit formatter has + changed. + Any unit formatters that inherit directly from ``Base`` but have not + implemented their own ``format_exponential_notation()`` and wish to retain + previous behavior should implement it as: + + .. code-block:: python + + def format_exponential_notation(cls, val, format_spec): + return format(val, format_spec) + + Any formatters that inherit directly from ``Base`` and call + ``super().format_exponential_notation(val, format_spec)`` should instead call + ``format(val, format_spec)`` + The specific unit formatters in ``astropy.units`` and custom formatters that + inherit from any of them are not affected. [#16676] + +- The deprecated ``units.format.Unscaled`` has been removed. Use ``units.format.Generic`` + instead. [#16707] + +- Added a __round__() dunder method to ``Quantity`` + in order to support the built-in round() function. [#16784] + +- For ``Masked`` initialization in which a mask is passed in, ensure that that + mask is combined with any mask present on the input. [#16875] + +- The ``get_format_name()`` method of ``NamedUnit`` and its subclasses is + deprecated. + The ``to_string()`` method can be used instead. [#16958] + +- The ``UnitBase.in_units()`` method is deprecated. + The ``to()`` method can be used as a drop-in replacement. [#17121] + +- Unit conversions to a given system with ``unit.to_system()``, + ``unit.si``, and ``unit.cgs``, will now prefer the simplest unit if it + is in the given system, rather than prioritizing more complicated + units if those had a base unit component. E.g., ``u.Pa.si`` will now + simply return ``Unit("Pa")`` rather than ``Unit("N / m2")``. However, + the case where a unit can be simply described in base units remains + unchanged: ``u.Gal.cgs`` will still give ``Unit("cm / s2")``. [#17122] + +- The ``CDS``, ``OGIP`` and ``VOUnit`` unit formatters are now subclasses of the + ``FITS`` unit formatter. [#17178] + +- The ``eV`` and ``rydberg`` units were moved to ``astropy.units.misc`` (from + ``astropy.units.si`` and ``astropy.units.astrophys``, respectively). + Practically, this means that ``Unit.to_system(u.si)`` no longer includes + ``eV`` as a SI-compatible unit. [#17246] + +astropy.utils +^^^^^^^^^^^^^ + +- ``IERS_Auto.open()`` now always returns a table of type ``IERS_Auto`` that + contains the combination of IERS-A and IERS-B data, even if automatic + updating of the IERS-A file is disabled or if downloading the new file fails. + Previously, under those conditions, it would return a table of a different type + (``IERS_B``) with only IERS-B data. [#16187] + +- ``astropy.utils.check_broadcast`` is now deprecated in favor of + ``numpy.broadcast_shapes`` [#16346] + +- Added a new keyword ``pending_warning_type`` to ``deprecated`` decorator so downstream developers could customize the type of warning for pending deprecation state. [#16463] + +- The ``introspection.resolve_name()`` function is deprecated. + It is better to use the standard library ``importlib`` instead. [#16479] + +- ``format_exception()`` is deprecated because it provides little benefit, if + any, over normal Python tracebacks. [#16807] + +- The ``utils.masked`` module has gained a mixin class, ``MaskableShapedLikeNDArray``, + as well as two utility functions, ``get_data_and_mask`` and ``combine_masks``, + that can help make a container classes carry masked data. Within astropy, these + are now used in the implementation of masks for ``Time``. [#16844] + +- The deprecated ``compat.override__dir__()`` utility has been removed. [#17190] + +astropy.visualization +^^^^^^^^^^^^^^^^^^^^^ + +- Removed deprecated ``exp`` attribute in the ``LogStretch``, + ``InvertedLogStretch``, ``PowerDistStretch``, and + ``InvertedPowerDistStretch`` stretch classes, and the ``power`` + attribute in the ``PowerStretch``. Instead, use the ``a`` attribute, + which matches the input keyword. [#15751] + +- Removes the unintended NumPy export previously at ``astropy.visualization.np``. [#15781] + +- Accessing or setting the following attributes on ``CoordinateHelper`` has been deprecated: + + * ``ticks`` + * ``ticklabels`` + * ``axislabels`` + + Setting the following attributes on ``CoordinateHelper`` directly has been deprecated: + + * ``parent_axes`` + * ``parent_map`` + * ``transform`` + * ``coord_index`` + * ``coord_unit`` + * ``coord_type`` (use ``set_coord_type`` instead) + * ``coord_wrap`` (use ``set_coord_type`` instead) + * ``frame`` + * ``default_label`` + + Accessing or setting the following attributes on ``CoordinateHelper`` has been + removed (without deprecation, as these were clearly internal variables): + + * ``grid_lines_kwargs`` + * ``grid_lines`` + * ``lblinfo`` + * ``lbl_world`` + * ``minor_frequency`` (there were already public methods to set/get this) [#16685] + +- The deprecated ``nsamples`` parameter of ``ZScaleInterval`` is removed. [#17186] + +astropy.wcs +^^^^^^^^^^^ + +- Errors may now occur if a ``BaseLowLevelWCS`` class defines + ``world_axis_object_components`` which returns values that are not scalars or + plain Numpy arrays as per APE 14. [#16287] + +- ``WCS.pixel_to_world_values``, ``WCS.world_to_pixel_values``, + ``WCS.pixel_to_world`` and ``WCS.world_to_pixel`` now correctly return NaN values for + pixel positions that are outside of ``pixel_bounds``. [#16328] + + +Bug Fixes +--------- + +astropy.io.ascii +^^^^^^^^^^^^^^^^ + +- Fix the broken behavior of reading an ASCII table and filling values using column names. + This PR addresses the issue and improves the functionality. [#15774] + +astropy.io.fits +^^^^^^^^^^^^^^^ + +- Fix a number of bugs in ``CompImageHDU``: + + * Fix the ability to pickle ``CompImageHDU`` objects + * Ensure that compression settings are not lost if initializing ``CompImageHDU`` + without data but with compression settings and setting the data later + * Make sure that keywords are properly updated when setting the header of a + ``CompImageHDU`` to an existing image header. + * Fix the ability to use ``CompImageHDU.section`` on instances that have not yet + been written to disk + * Fix the image checksum/datasum in ``CompImageHDU.header`` to be those for the + image HDU instead of for the underlying binary table. [#15474] + +- Fix a spurious exception when reading integer compressed images with blanks. [#17099] + +- Fix creating ``CompImageHDU`` from header with BSCALE/BZERO: keywords are now + ignored, as done in ``ImageHDU``. [#17237] + +astropy.io.votable +^^^^^^^^^^^^^^^^^^ + +- Making the "votable.parquet" format available as a reader format to ensure + consistency with the writer formats, even though the format it recognised + automatically by "votable". [#16488] + +- Explicitly set ``usedforsecurity=False`` when using ``hashlib.md5``. Without this, ``hashlib.md5`` will be blocked in FIPS mode. + FIPS (Federal Information Processing Standards) is a set of standards created by NIST (National Institute of Standards and Technology) for US government agencies regarding computer security and interoperability. + This affects validation results ingestion. [#17156] + +astropy.modeling +^^^^^^^^^^^^^^^^ + +- Fixed the output representation of models with parameters that have + units of ``dimensionless_unscaled``. [#16829] + +astropy.stats +^^^^^^^^^^^^^ + +- Fixed accuracy of sigma clipping for large ``float32`` arrays when + ``bottleneck`` is installed. Performance may be impacted for computations + involving arrays with dtype other than ``float64``. This change has no impact + for environments that do not have ``bottleneck`` installed. [#17204] + +- Fix an issue in sigma-clipping where the use of ``np.copy()`` was causing + the input data mask to be discarded in cases where ``grow`` was set. [#17402] + +astropy.table +^^^^^^^^^^^^^ + +- Fix a bug where column names would be lost when instantiating ``Table`` from a list of ``Row`` objects. [#15735] + +- Aggregating table groups for ``MaskedColumn`` now ensures that fully-masked + groups result in masked elements rather than ``NaN``. [#16498] + +- Ensure that tables holding coordinates or representations can also be stacked + if they have zero length. This fix also ensures that the ``insert`` method + works correctly with a zero-length table holding a coordinate object. [#17380] + +- Fixed table aggregate with empty columns when float is present. [#17385] + +astropy.units +^^^^^^^^^^^^^ + +- Allow SI-prefixes for radioactivity units ``becquerel`` and ``curie`` in ``astropy.units.si``, conforming to BIPM's guidelines for SI units. [#16529] + +- The OGIP unit parser no longer accepts strings where a component unit is + followed by a parenthesized unit without a separator in between, such as + ``'m(s)'`` or ``'m(s)**2'``. + Such strings are not allowed by the OGIP standard. [#16749] + +- A few edge cases that could result in a power of a unit to be a numerical value + from ``numpy``, instead of the intended Python ``int``, ``float`` or + ``fractions.Fraction`` instance, have been fixed. [#16779] + +- The OGIP unit parser now detects negative powers that are not enclosed in + parenthesis. + For example, ``u.Unit("s**-1", format="ogip")`` now raises an error because the + OGIP standard expects the string to be written as ``"s**(-1)"`` instead, but it + is still possible to parse the unit with + ``u.Unit("s**-1", format="ogip", parse_strict="warn")`` or + ``parse_strict="silent"``. [#16788] + +- ``UnitScaleError`` can now be imported from the ``astropy.units`` namespace. [#16861] + +- Parsing custom units with ``u.Unit()`` using the ``"vounit"`` format now obeys + the ``parse_strict`` parameter, unless the custom units are made explicit with + quotation marks. + For example, ``u.Unit("custom_unit", format="vounit")`` now raises an error, + but ``u.Unit("custom_unit", format="vounit", parse_strict="silent")`` or + ``u.Unit("'custom_unit'", format="vounit")`` do not. [#17232] + +- It is now possible to use ``Unit`` to create dimensionless units with a scale + factor that is a complex number or a ``fractions.Fraction`` instance. + It was already possible to create such units directly with ``CompositeUnit``. [#17355] + +astropy.utils +^^^^^^^^^^^^^ + +- Fixed the unintended behavior where the IERS-A file bundled in ``astropy-iers-data`` would be ignored if automatic updating of the IERS-A file were disabled or if downloading the new file failed. [#16187] + +- Ensure ``MaskedQuantity`` can be initialized with a list of masked + quantities (as long as their shapes match), just like regular + ``Quantity`` and ``ndarray``. [#16503] + +- For ``Masked`` instances, ``np.put``, ``np.putmask``, ``np.place`` and + ``np.copyto`` can now handle putting/copying not just ``np.ma.masked`` but + also ``np.ma.nomask``; for both cases, only the mask of the relevant entries + will be set. [#17014] + +- Explicitly set ``usedforsecurity=False`` when using ``hashlib.md5``. Without this, ``hashlib.md5`` will be blocked in FIPS mode. + FIPS (Federal Information Processing Standards) is a set of standards created by NIST (National Institute of Standards and Technology) for US government agencies regarding computer security and interoperability. + This affects download caching. [#17156] + +- Fixed a bug where an old IERS-A table with stale predictive values could trigger + the download of a new IERS-A table even if automatic downloading was disabled. [#17387] + +astropy.wcs +^^^^^^^^^^^ + +- Avoid a ``RuntimeWarning`` in ``WCS.world_to_array_index`` by converting + NaN inputs to int. [#17236] + + +Performance Improvements +------------------------ + +astropy.io.ascii +^^^^^^^^^^^^^^^^ + +- The performance of guessing the table format when reading large files with + ``astropy.io.ascii`` has been improved. Now the process uses at most + 10000 lines of the file to check if it matches the format. This behavior can + be configured using the ``astropy.io.ascii.conf.guess_limit_lines`` + configuration item, including disabling the limit entirely. [#16840] + +astropy.io.fits +^^^^^^^^^^^^^^^ + +- Optimize checksum computation. [#17209] + +astropy.modeling +^^^^^^^^^^^^^^^^ + +- Improved the performance of 1D models, models with scalar parameters, and models + without units, when evaluating them with scalar or small arrays of inputs. For + models that satisfy all of the conditions above, the improvement can be on the + order of 30-40% in execution time. [#16670] + +- Performance of most non-linear fitters has been significantly improved by reducing the overhead in evaluating models inside the objective function. [#16673] + +- Improved the performance of ``parallel_fit_dask`` by avoiding unnecessary copies of the + model inside the fitter. [#17033] + +- ``CompoundModel`` now implements numerical derivatives of parameters when using the +, -, * or / operators. This improves the speed of fitting these models because numerical derivatives of the parameters are not calculated. [#17034] + +astropy.stats +^^^^^^^^^^^^^ + +- The performance of biweight_location, biweight_scale, + biweight_midvariance, and median_absolute_deviation has been improved by + using the bottleneck nan* functions when available. This requires the + bottleneck optional dependency to be installed. [#16967] + +astropy.units +^^^^^^^^^^^^^ + +- The ``units.quantity_input`` decorator has been optimized, especially in the case that no equivalencies are provided to the decorator, and the speed-up is very noticeable when wrapping short functions. [#16742] + +- Parsing composite units with the OGIP formatter is now up to 25% faster. [#16761] + +- Parsing units with scale factors is now up to 50% faster. [#16813] + +- Parsing strings representing non-composite units with ``Unit`` is now up to 25% + faster. [#17004] + +- Converting composite units to strings with the ``"cds"``, ``"fits"``, + ``"ogip"`` and ``"vounit"`` formatters is now at least twice as fast. [#17043] + +astropy.visualization +^^^^^^^^^^^^^^^^^^^^^ + +- Removed redundant transformations when WCSAxes determines the coordinate ranges + for ticks/gridlines, which speeds up typical plot generation by ~10%, and by + much more if ``astropy.visualization.wcsaxes.conf.coordinate_range_samples`` is + set to a large value [#16366] + + +Other Changes and Additions +--------------------------- + +- Updated minimum supported Python version to 3.11. As a result, minimum + requirements were updated to compatible versions. + Astropy now requires + - ``numpy>=1.23.2`` + - ``PyYAML>=6.0.0`` + - ``packaging>=22.0.0`` [#16903] + +- The minimum supported version of Pandas is now v2.0. + This is in line with https://scientific-python.org/specs/spec-0000/. [#16308] + +- Update minimal recommendation for matplotlib from version 3.3.4 to 3.6.0 [#16557] + +- The Contributor documentation has been significantly improved. It now includes a + Quickstart Guide with concise instructions on setting up a development environment and + making a pull request. In addition, the developer documentation was reorganized and + simplified where possible to improve readability and accessibility. [#16561] + +Version 6.1.7 (2024-11-22) +========================== + +Bug Fixes +--------- + +astropy.stats +^^^^^^^^^^^^^ + +- Fix an issue in sigma-clipping where the use of ``np.copy()`` was causing + the input data mask to be discarded in cases where ``grow`` was set. [#17402] + +Version 6.1.6 (2024-11-11) +========================== + +Bug Fixes +--------- + +astropy.coordinates +^^^^^^^^^^^^^^^^^^^ + +- Fixed instantiating ``Angle`` from a ``pandas`` ``Series`` object. [#17358] + +astropy.units +^^^^^^^^^^^^^ + +- Fixed calling ``np.nanvar`` and ``np.nanstd`` with ``Quantity`` ``out`` argument. [#17354] + + +Version 6.1.5 (2024-11-07) +========================== + +Bug Fixes +--------- + +astropy.coordinates +^^^^^^^^^^^^^^^^^^^ + +- Ensure that coordinates can be transformed to other coordinate frames + also if they have size zero (i.e., hold empty data arrays). [#17013] + +- ``Longitude`` and ``Latitude`` can no longer be initialized with strings + ending in "N" or "S", and "E" or "W", respectively, since those suggest + the other type. [#17132] + +- ``np.nanvar(angle)`` now produces a ``Quantity`` with the correct + unit, rather than raising an exception. [#17239] + +- Fix a crash when instantiating ``Angle`` (or ``Latitude``, or ``Longitude``) + from a non-numpy array (for instance pyarrow arrays). [#17263] + +astropy.io.fits +^^^^^^^^^^^^^^^ + +- Fix access to VLA columns after slicing ``.data``. [#16996] + +astropy.io.votable +^^^^^^^^^^^^^^^^^^ + +- Updated xml writer for VOTable Resource elements to include groups. [#17344] + +astropy.nddata +^^^^^^^^^^^^^^ + +- Add support for positional only and keyword only arguments when using the ``support_nddata`` decorator. [#17281] + +astropy.stats +^^^^^^^^^^^^^ + +- Fixed a bug where float32 inputs to sigma_clip and SigmaClip were + changed to float. [#17086] + +astropy.table +^^^^^^^^^^^^^ + +- Fix a crash when calling ``Column.pprint`` on a scalar column. [#15749] + +- Ensure that setting an existing column to a scalar always properly fills it + (rather than breaking the table if there was only one column in it). [#17105] + +astropy.units +^^^^^^^^^^^^^ + +- The unit parsers are now better at recognizing unusual composite + units: + + - units involving special unicode symbols, like "L☉/pc²"; + - units that include CDS units ending in a 0, like "eps0/s"; + - units including the degree symbol, "°". For example, "°C/s" is no + longer incorrectly interpreted as "°C/s^2". [#17011] + +- Converting the ohm to a string with the OGIP unit formatter (e.g. + ``f"{u.ohm:ogip}"``) previously produced the string ``'V / A'``, but now + produces ``'ohm'`` as expected. [#17200] + +- The ``OGIP`` unit formatter now handles the unit ``day`` and the corresponding + string ``"d"`` in full compliance with the standard. [#17216] + +- The ``"ogip"`` unit format now represents the unit angstrom as ``"angstrom"`` + instead of ``"0.1 nm"``. [#17241] + +astropy.utils +^^^^^^^^^^^^^ + +- Ensure that queries of ``.ut1_utc()`` and ``.pm_xy()`` return the correct + results also when passing in an empty array of times. [#17013] + +- Fixed a bug where astropy's logger wouldn't perform lazy string interpolation. [#17196] + +astropy.visualization +^^^^^^^^^^^^^^^^^^^^^ + +- Fixed a bug that caused ``CoordinateHelper.get_axislabel()`` to return an + empty string instead of the default label if no label has been explicitly + provided. [#17175] + +astropy.wcs +^^^^^^^^^^^ + +- Fixed a bug that caused ``WCS.slice`` to ignore ``numpy_order`` and always + interpret the slices as if ``numpy_order`` was ``True``, in the specific case + where the slices were such that dimensions in the WCS would be dropped. [#17147] + +Version 6.1.4 (2024-09-26) +========================== + +Bug Fixes +--------- + +astropy.coordinates +^^^^^^^^^^^^^^^^^^^ + +- Keep ``Latitude`` from printing long input arrays in their entirety when failing + limits check in ``_validate_angles``, indicating their range instead. [#13997] + +- Avoid some components not being included in table output of coordinates if + the representation type was ``"unitspherical"``. + + In the process, also ensured that one can pass in the ``radial_velocity`` + keyword argument if one uses ``differential_type="radial"``. [#16999] + +astropy.io.votable +^^^^^^^^^^^^^^^^^^ + +- Ensure proper handling of null values during BINARY2 serialization. Previously, masks were handled in two different ways for BINARY2 serialization, resulting in incorrect handling of null values and errors. [#16091] + +astropy.stats +^^^^^^^^^^^^^ + +- Fixed a bug in biweight_location, biweight_scale, and + biweight_midvariance where the returned array shape would be wrong if + the input array had an axis length of 1 along any axis that was not + included in the axis keyword. Also fixed a bug in these same functions + where for constant data and axis set to a tuple containing all axes, the + returned value would be NaN instead of the constant value. [#16964] + +astropy.table +^^^^^^^^^^^^^ + +- Ensure that initializing a ``QTable`` with explicit units` also succeeds if + one of the units is ``u.one``. [#17048] + +astropy.units +^^^^^^^^^^^^^ + +- An exception is now raised if it is attempted to create a unit with a + scale of zero, avoiding bugs further downstream (including surprising + ones, such as a comparison of ``np.ma.masked == u.one`` leading to + a ``ZeroDivisionError``). [#17048] + +astropy.wcs +^^^^^^^^^^^ + +- Fix a bug that caused the results from local_partial_pixel_derivative to be incorrect when using normalize_by_world=True (the matrix was previously normalized along the wrong axis) [#17003] + + +Other Changes and Additions +--------------------------- + +- Minimal requirement for (optional dependency) matplotlib was bumped + to 3.5.0, which is the oldest version with support for Python 3.10 [#16993] + +Version 6.1.3 (2024-08-30) +========================== + +Bug Fixes +--------- + +astropy.io.fits +^^^^^^^^^^^^^^^ + +- Fix reading zero-width columns such as 0A fields. [#16894] + +- Ensure that ``QTable``, like ``Table``, can read zero-length string columns, + and not convert them to length 1 strings. In the process, avoid a needless + copy of all the data for ``QTable``. [#16898] + +astropy.io.votable +^^^^^^^^^^^^^^^^^^ + +- Fix KeyError when parsing certain VOTables. [#16830] + +astropy.modeling +^^^^^^^^^^^^^^^^ + +- Fixed the ``fit_deriv`` calculations in the ``Lorentz1D`` model. [#16794] + +astropy.table +^^^^^^^^^^^^^ + +- Pretty-printing of Tables now also works in the presence of zero-length string + columns (which sometimes are present in FITS tables). [#16898] + +astropy.utils +^^^^^^^^^^^^^ + +- Fix the return type for ``np.broadcast_arrays`` on a single ``Masked`` + instance: it now correctly returns a 1-element sequence instead of a single + array, just like would be the case with a regular array. [#16842] + +astropy.wcs +^^^^^^^^^^^ + +- Fix a bug where ``wcs_info_str``'s results would look different in numpy 2 VS + numpy 1. [#16586] + + +Other Changes and Additions +--------------------------- + +- The minimum required version of PyArrow is now v7.0.0. [#16785] + +Version 6.1.2 (2024-07-23) +========================== + +Bug Fixes +--------- + +astropy.io.ascii +^^^^^^^^^^^^^^^^ + +- When reading CDS and MRT files, only interpret a line as a section delimiter if + it contains exclusively dashes or equal signs. This enables rows starting with dashes. [#16735] + +astropy.io.fits +^^^^^^^^^^^^^^^ + +- Fix a spurious exception when reading integer compressed images with blanks. [#16550] + +- Fixed a crash that occurred for files opened via + ``fits.open(..., mode='update')``, on Windows, and with numpy 2.0 installed. + A warning is now emitted in cases most likely to escalate into + undefined behavior (e.g., segfaults), i.e., when a closed memory map object is + still referenced by external code. Please report any regression found. [#16581] + +astropy.modeling +^^^^^^^^^^^^^^^^ + +- Fixed a bug that caused models returned by non-linear fitters to have + ``sync_constraints`` set to `False`, which caused constraints accessed through, e.g., + ``Model.fixed`` to not be in sync with the ``fixed`` attribute of the parameters. [#16664] + +- Fixed a bug that caused ``CompoundModel.without_units_for_data`` to return an + incorrectly constructed model when the compound model contained a * or / + operation, and which also caused fitting to not work correctly with compound + models that contained * or / operations. [#16678] + +astropy.units +^^^^^^^^^^^^^ + +- The OGIP parser is now less restrictive with strings that represent a unit that + includes the ``sqrt`` function. + For example, ``u.Unit("sqrt(m)**3", format="ogip")`` no longer causes a + ``ValueError``. [#16743] + +astropy.utils +^^^^^^^^^^^^^ + +- Fixed an edge-case bug in ``overlap_slices`` where the function could + return an empty slice for non-overlapping slices. [#16544] + +astropy.visualization +^^^^^^^^^^^^^^^^^^^^^ + +- Fixed a WCSAxes bug when overlaying a frame with default units that are not degrees. [#16662] + + +Version 6.1.1 (2024-06-14) +========================== + + +Bug Fixes +--------- + +astropy.io.fits +^^^^^^^^^^^^^^^ + +- Let fitsdiff compare files with lower case HIERARCH keywords [#16357] + +- Fix writing a ``HDUList`` to file when numpy 2 is installed and at least some of + the data is represented as dask arrays. [#16384] + +- Fix display of diff reports with numpy 2. [#16426] + +- Ensure that also zero-length tables preserve whether integer data are + signed or unsigned. [#16505] + +astropy.io.misc +^^^^^^^^^^^^^^^ + +- Fix YAML table serialization compatibility with numpy 2. [#16416] + +astropy.io.votable +^^^^^^^^^^^^^^^^^^ + +- Fix bugs in io.votable related to numpy 2's representation of scalars. [#16442] + +astropy.stats +^^^^^^^^^^^^^ + +- Ensure that return types from ``sigma_clip`` ``cenfunc`` and ``stdfunc`` + are np.float64 for scalar values. [#16431] + +astropy.table +^^^^^^^^^^^^^ + +- Ensure structured ``MaskedColumn`` are serialized correctly, including + the mask. [#16380] + +- Fix problems converting Pandas Series to ``Table`` with numpy >=2.0. [#16439] + +astropy.time +^^^^^^^^^^^^ + +- Ensure Time in ymdhms format can also be serialized to files as part of a + table if it is masked. [#16380] + +astropy.utils +^^^^^^^^^^^^^ + +- Ensure Masked versions of ``np.recarray`` will show the correct class + name of ``MaskedRecarray`` in their ``repr``, and that they will be + serialized correctly if part of a table. [#16380] + +- Fix bugs with how masked structured arrays were represented with numpy 2. [#16443] + +- ``MaskedQuantity`` now works properly with ``np.block``. [#16499] + +astropy.visualization +^^^^^^^^^^^^^^^^^^^^^ + +- Fix a bug where ``WCSAxes`` could be missing negative signs on axis labels when using matplotlib's ``usetex`` mode. [#16406] + +astropy.wcs +^^^^^^^^^^^ + +- Fix compilation with gcc 14, avoid implicit pointer conversions. [#16450] + + +Other Changes and Additions +--------------------------- + +- Updated bundled WCSLIB version to 8.3. This update changes the behavior of + various ``*set`` functions in order to improve stability of WCSLIB in threaded + applications. For a full list of changes - see ``astropy/cextern/wcslib/CHANGES``. [#16451] + +Version 6.1.0 (2024-05-03) +========================== + +New Features +------------ + +astropy.coordinates +^^^^^^^^^^^^^^^^^^^ + +- ``BaseCoordinateFrame`` now has a ``position_angle()`` method, which is the + same as the ``position_angle`` method of ``SkyCoord`` instances. [#15737] + +- By default the ``SkyCoord`` and ``BaseCoordinateFrame`` ``separation()`` + methods now emit a warning if they have to perform a coordinate transformation + that is not a pure rotation to inform the user that the angular separation can + depend on the direction of the transformation. + It is possible to modify this behaviour with the new optional keyword-only + ``origin_mismatch`` argument. + Specifying ``origin_mismatch="ignore"`` allows any transformation to + succeed without warning, which has been the behaviour so far. + ``origin_mismatch="error"`` forbids all transformations that are not + pure rotations. [#16246] + +astropy.io.ascii +^^^^^^^^^^^^^^^^ + +- Clearer error message in reading ASCII tables when there is + a mismatch between converter type and column type. [#15991] + +astropy.io.registry +^^^^^^^^^^^^^^^^^^^ + +- The module ``astropy.io.typing`` has been added to provide type annotations for + I/O-related functionality. [#15916] + +astropy.samp +^^^^^^^^^^^^ + +- SAMP web profile CORS HTTP server implements `Private Network Access proposal `_. [#16193] + +astropy.table +^^^^^^^^^^^^^ + +- ``Table`` now has a ``setdefault()`` method, analogous to + ``dict.setdefault()``. [#16188] + +astropy.units +^^^^^^^^^^^^^ + +- Added a new module ``astropy.units.typing`` that provides support for type annotations related to + ``astropy.units``. [#15860] + +- Added a new CGS unit Oersted. [#15962] + +- Added "surface brightness", "surface brightness wav", "photon surface brightness", and "photon surface brightness wav" to recognized physical types. [#16032] + +- Added magnetic helicity as a physical type. [#16101] + +astropy.utils +^^^^^^^^^^^^^ + +- For gufuncs on ``Masked`` instances, add support for the ``axes`` argument. [#16121] + +- ``Masked`` instances now support the various numpy array set operations, such + as ``np.unique`` and ``np.isin``. [#16224] + +astropy.wcs +^^^^^^^^^^^ + +- Added support for slicing WCS objects containing ``cpdis`` or ``det2im`` distortions, which previously were ignored. [#16163] + + +API Changes +----------- + +astropy.coordinates +^^^^^^^^^^^^^^^^^^^ + +- The ``astropy.coordinates.transformations`` module has been refactored into a module. + There should be no user-visible changes, but if you notice any, please open an + Issue. [#15895] + +- Changed the default value of the ``copy`` argument in + ``astropy.coordinates.representation.CylindricalDifferential.__init__`` from + ``False`` to ``True``, which is the intended behaviour for all subclasses of + ``astropy.coordinates.representation.BaseDifferential``. [#16198] + +astropy.cosmology +^^^^^^^^^^^^^^^^^ + +- ``Cosmology`` and its subclasses are now frozen ``dataclass`` objects. [#15484] + +- The argument ``verbose`` in the function ``z_at_value`` is now keyword-only. [#15855] + +astropy.io.ascii +^^^^^^^^^^^^^^^^ + +- The ``io.ascii`` Python and C table readers were updated to use a 64-bit integer field by + default when reading a column of integer numeric data. This changes the default behavior + on Windows and potentially 32-bit architectures. Previously on those platforms, table + columns with any long integers which overflowed the 32-bit integer would be returned + as string columns. The new default behavior is consistent with ``numpy`` v2 and ``pandas``. [#16005] + +- The parallel fast-reader parser for reading ASCII files has been removed. + Since astropy v4.0.4 requesting this option has issued a warning that + this option is broken and that the serial parser will be used. + The ``parallel`` key in the ``fast_reader`` argument for reading + ASCII tables is no longer available. [#16103] + +astropy.table +^^^^^^^^^^^^^ + +- ``show_in_notebook`` is deprecated and it is recommended to use dedicated + tools in the Jupyter ecosystem to create interactive plots in notebooks. [#15905] + +- A warning is now emitted when ``Quantity`` values are inserted into empty ``Column`` objects + via ``Table.insert_row`` or ``Table.add_row``. [#16038] + +- ``show_in_browser`` is deprecated (pending feedback from the community). + Please use https://github.com/astropy/astropy/issues/16067 if you are + actively using the function. [#16068] + +- ``TableColumns.setdefault()`` and ``TableColumns.update()`` methods (which + would typically be called as ``Table.columns.setdefault()`` and + ``Table.columns.update()``) have been deprecated because they can easily + corrupt the ``Table`` instance the ``TableColumns`` instance is attached to. + The ``Table.setdefault()`` and ``Table.update()`` methods are safe. [#16154] + +astropy.time +^^^^^^^^^^^^ + +- ``TIME_FORMATS`` and ``TIME_DELTA_FORMATS`` in ``astropy.time.formats`` + are changed from ``OrderedDict`` to Python ``dict``. [#15491] + +- A ``FutureWarning`` is now emitted when mutating ``Time.location`` post-initialization. [#16063] + +- Following the removal of ``np.ndarray.ptp`` in Numpy v2, ``Time.ptp`` is now + deprecated in favor of ``np.ptp``. [#16212] + +astropy.units +^^^^^^^^^^^^^ + +- If any iterable such as a list of tuple was input to ``Quantity``, a check was + done to see if they contained only quantities, and, if so, the quantities were + concatenated. This makes sense for list and tuple, but is not necessarily + logical for all iterables and indeed was broken for those that do not have a + length (such as ``array_api`` array instances). Hence, the check will now be + done only for values where it makes sense, i.e., instances of list and tuple. [#15752] + +- Units now exposes ``get_converter`` which returns a function that + will convert a scalar or array from one unit to another. This can be + useful to speed up code that converts many quantities with the same + unit to another one, especially if the quantity has not many elements, + so that the overhead of creating a conversion function is relatively large. [#16139] + +astropy.utils +^^^^^^^^^^^^^ + +- Deprecate importing ``ErfaError`` and ``ErfaWarning`` from ``astropy.utils.exceptions``. + They should be imported directly from ``erfa`` instead. [#15777] + +- ``introspection.isinstancemethod()`` and ``introspection.find_mod_objs()`` are + deprecated. [#15934] + +- ``astropy.utils.console.terminal_size`` is now deprecated in favour of + ``shutil.get_terminal_size`` from the standard library. [#16045] + +- ``indent()`` is deprecated. + Use ``textwrap.indent()`` from Python standard library instead. [#16223] + +- Unmasked ``Masked`` scalar instances are now considered hashable, to match the + implicit behaviour of regular arrays, where if an operation leads to a scalar, + a hashable array scalar is returned. [#16224] + +astropy.visualization +^^^^^^^^^^^^^^^^^^^^^ + +- Renamed the ``min_cut`` and ``max_cut`` keywords in ``simple_norm`` and + ``fits2bitmap`` to ``vmin`` and ``vmax``. The old names are deprecated. [#15621] + +- If ``vmin == vmax``, the ``ImageNormalize`` class now maps the input + data to 0. If ``vmin > vmax``, the ``ImageNormalize`` class now raises a + ``ValueError``. [#15622] + + +Bug Fixes +--------- + +astropy.convolution +^^^^^^^^^^^^^^^^^^^ + +- Avoid a segfault when calling ``astropy.convolution.convolve`` on an empty array. + An exception is now raised instead. [#15840] + +astropy.coordinates +^^^^^^^^^^^^^^^^^^^ + +- Previously passing a ``SkyCoord`` instance to the ``BaseCoordinateFrame`` + ``separation()`` or ``separation_3d()`` methods could produce wrong results, + depending on what additional frame attributes were defined on the ``SkyCoord``, + but now ``SkyCoord`` input can be used safely. [#15659] + +- ``Distance`` now accepts as ``parallax`` any angle-like value. + This includes types like ``Column`` which have a unit but are not ``Quantity`` subclasses. [#15712] + +- The new default for the class method ``SkyCoord.from_name()`` + is to look for coordinates first in SIMBAD, then in NED, and then in VizieR, + instead of having no specific order. [#16046] + +- Fix ``Angle.to_string()`` for angles in degrees represented in 'hms' and angles in hours represented in 'dms'. [#16085] + +- Fix a bug where ``SkyCoord.spherical_offsets_by`` would crash when a wrap + was needed. [#16241] + +- ``search_around_3d()`` now always raises a ``UnitConversionError`` if the units + of the distances in ``coord1`` and ``coord2`` and the unit of ``distlimit`` do + not agree. + Previously the error was not raised if at least one of the coordinates was + empty. [#16280] + +astropy.cosmology +^^^^^^^^^^^^^^^^^ + +- Fixed a bug where the attribute ``ParametersAttribute.attr_name`` could be None + instead of a string. [#15882] + +astropy.io.ascii +^^^^^^^^^^^^^^^^ + +- Reading of CDS header files with multi-line descriptions where the continued line started with a number was broken. This is now fixed. [#15617] + +- Ensure that the names of mixin columns are properly propagated as + labels for the MRT format. [#15848] + +- Fixed reading IPAC tables for ``long`` column type on some platforms, e.g., Windows. [#16005] + +astropy.io.fits +^^^^^^^^^^^^^^^ + +- Avoid ``WinError 1455`` in opening some large files with memory + mapping on windows. [#15388] + +- Fix TDISP parsing for floating numbers. [#16007] + +- Fix a crash when calling FITS ``writeto`` methods with stdout as the output stream. [#16008] + +- Fix TDISP parsing for floating numbers in formats ES / EN. [#16015] + +- Fix conversion of ``Table`` to ``BinTableHDU`` with ``character_as_bytes=True``. [#16358] + +- Improved error message when instantiating a fits table with an ill-formed array. [#16363] + +astropy.io.misc +^^^^^^^^^^^^^^^ + +- Reading an empty table stored in parquet format now creates an empty + table instead of raising an unexpected error. [#16237] + +astropy.io.votable +^^^^^^^^^^^^^^^^^^ + +- When reading a VOTable, if some user-requested columns were not present then the + resulting error message previously listed all the requested column names. + Now only columns that are actually missing are shown. [#15956] + +astropy.stats +^^^^^^^^^^^^^ + +- Fix a spurious warning when calling ``sigma_clipped_stats`` on a ``MaskedColumn``. [#15844] + +astropy.table +^^^^^^^^^^^^^ + +- Fix a Table bug when setting items (via slice or index list) in a ``bytes`` type + ``MaskedColumn`` would cause the column mask to be set to all ``False``. A common way to + trigger this bug was reading a FITS file with masked string data and then sorting the + table. [#15669] + +- Fix slicing logic for Row. + Previously, slicing a ``astropy.table.row.Row`` object would incorrectly return a column, + now it correctly returns a list of values from that row. [#15733] + +- Fix a ``ValueError`` raised by ``table.join`` when fed with large tables. + This would typically happen in situations when the result joined table would be + too large to fit in memory. In those situations, the error message is now much more + clearly about the necessary memory size. [#15734] + +- Fix an unintended exception being raised when attempting to compare two unequal ``Table`` instances. [#15845] + +- Ensure that if a ``Column`` is initialized with a ``Quantity`` it will use by + default a possible name defined on the quantity's ``.info``. [#15848] + +- Fix a bug where columns with ``dtype=object`` wouldn't be properly deep-copied using ``copy.deepcopy``. [#15871] + +- Fix ``hasattr(Table, "iloc")`` raising an exception, preventing use of tables e.g. with scikit-learn. [#15913] + +- Calling ``Table.group_by`` on an empty table no longer raises an exception. [#16093] + +- The unit conversion ``convert_unit_to`` with MaskedColumn was + broken as it was storing the old unit in a dictionary attached + to underlying np.ma.MaskedArray. This fixes it by overwriting + the old unit after unit conversion. [#16118] + +- ``astropy.table.vstack`` will no longer modify the input list even when it + contains non-Table objects like ``astropy.table.Row``. [#16130] + +- Update old dataTables.js version. + This should not affect the end user. [#16315] + +astropy.time +^^^^^^^^^^^^ + +- Fix comparing NaN ``Quantity`` with ``TimeDelta`` object. [#15830] + +- Scalar ``Time`` instances are now hashable if they are not masked, also if one + uses ``Masked`` internally, matching the behaviour prior to astropy 6.0 (and + the current behaviour when masking using ``np.ma.MaskedArray``). [#16224] + +astropy.units +^^^^^^^^^^^^^ + +- Fix rare signature incompatibilities between helper and helped array functions. + Most involve cases where the corresponding numpy function has had its + arguments renamed between numpy versions. Since all those generally changed + the first arguments, which are typically passed as positional arguments, + this should not affect user code. + Affected functions: + - ``numpy.array_str`` + - ``numpy.choose`` + - ``numpy.convolve`` + - ``numpy.correlate`` + - ``numpy.histogram`` + - ``numpy.histogramdd`` + - ``numpy.histogram2d`` + - ``numpy.isin`` + - ``numpy.inner`` + - ``numpy.nanmedian`` + - ``numpy.unique`` + - ``numpy.matrix_rank`` + - ``numpy.unwrap`` + - ``numpy.vdot`` + - ``numpy.lib.recfunctions.unstructured_to_structured`` [#15710] + +- Fix an issue with unicode string representations of units shown as + superscripts (like degree) when raised to some power. Like for + LaTeX representations, now the superscript unicode character is + replaced by the literal short name before adding the power. [#15755] + +- Fix a missing ``Sun`` unit in the list of VOUnits simple_units. [#15832] + +- Fix an unhelpful ``TypeError`` when attempting truediv, ``lshift`` (``<<``) or ``mul`` (``*``) or ``truediv`` (``/``) with a ``Unit`` for right operand and a numpy array with non-numerical dtype for left operand. [#15883] + +- Fix write/read roundtrips with empty ``Table`` dumped to ECSV. [#15885] + +- Fix a bug where LaTeX formatter would return empty strings for unity (1) input. [#15923] + +- Fix extraneous space in LaTeX repr for ``Quantity`` objects with superscript + units (e.g. angles or temperatures in degree Celsius). [#16043] + +- Ensure powers of units are consistently as simple as possible. So, an + integer if possible, otherwise a float, or a fraction if the float is + really close to that. This also ensures the hash of a unit is unique + for any given unit (previously, the same power could be represented as + float, int or fraction, which made the hash different). [#16058] + +- Ensure that ``find_equivalent_units`` only returns actual units, not units + that raised to some power match the requested one. With this fix, + ``(u.m**-3).find_equivalent_units()`` properly finds nothing, rather than all + units of length. [#16127] + +- Using a dimensionless ``Quantity`` as an exponent works anew. + In astropy 6.0.1 an exception was erroneously raised. [#16261] + +astropy.utils +^^^^^^^^^^^^^ + +- Fix rare signature incompatibilities between helper and helped array functions. + These typically cover corner cases and should not affect user code. + Some arguments weren't being re-exposed correctly or at all, depending on + numpy's version. + Affected functions: + - ``numpy.broadcast_arrays`` + - ``numpy.median`` + - ``numpy.quantile`` + - ``numpy.empty_like`` + - ``numpy.ones_like`` + - ``numpy.zeros_like`` + - ``numpy.full_like`` [#16025] + +- Fix a bug where ``astropy.utils.console.Spinner`` would leak newlines for + messages longer than terminal width. [#16040] + +- Update ``report_diff_values`` so the diff no longer depends on the + console terminal size. [#16065] + +- Fix support in ``Masked`` for generalized ufuncs with more than a + single core dimension (such as ``erfa.rxp``). [#16120] + +- ``Masked`` array instances now deal more properly with structured dtypes, + combining field masks to get element masks for generalized ufuncs, and + allowing ``.view()`` any time the mask can be viewed as well. This allows a + larger number of ``erfa`` routines to work with masked data. [#16125] + +astropy.visualization +^^^^^^^^^^^^^^^^^^^^^ + +- ``WCSAxes`` will correctly set certain defaults when ``wcs.world_axis_physical_types`` contains ``custom:`` prefixes. [#15626] + +- Fix an edge case where ``quantity_support`` would produce duplicate tick labels for small data ranges. [#15841] + +- Fix a bug where ``AngleFormatterLocator`` and ``ScalarFormatterLocator`` wouldn't respect matplotlib.rc's ``axes.unicode_minus`` parameter. [#15902] + +- Fixed a bug in ``CoordinateHelper.grid`` method to properly handle ``draw_grid=False`` and ``draw_grid=None``, + ensuring grid lines are controlled correctly even when not explicitly called. [#15985] + +astropy.wcs +^^^^^^^^^^^ + +- Updated bundled WCSLIB version to 8.2.2. This update fixes character buffer + overflows in the comment string for the longitude and latitude axes triggered + by some projections in ``wcshdo()``, and also the formatting for generic + coordinate systems. For a full list of changes - see + http://www.atnf.csiro.au/people/mcalabre/WCS/CHANGES or + ``astropy/cextern/wcslib/CHANGES`` [#15795] + +- Fixed a bug in ``fit_wcs_from_points`` that does not set the default value of the ``cdelt`` of the returned WCS object. [#16027] + +- Fixed a bug in ``DistortionLookupTable`` (which implements ``cpdis`` and ``det2im`` projection corrections to a WCS) in which image pixels received an incorrect distortion value, from a location in the lookup table incorrectly offset by about 1 table pixel. [#16163] + + +Other Changes and Additions +--------------------------- + +- Update minimum supported Python version to 3.10 [#15603] + +- The minimum required NumPy version is now 1.23 and the minimum required SciPy version is 1.8. [#15706] + +- Fix loading parser tabs on pyc-only installations. + + Fix a bug in the wrappers for the lex and yacc wrappers that are + used for parsing Astropy units so that they work on pyc-only + installations. + + According to the Python module loading + `flow chart `_, when evaluating + ``import foo`` and ``foo.py`` is not found, Python then reads ``foo.pyc``. + + One can take advantage of this fact to strip source files and leave only Python + bytecode files for deployment inspace-constrained execution environments such + as AWS Lambda. Astropy is now compatible with pyc-only deployments. [#16159] + +- Change the default value of ``copy`` arguments in public APIs from ``False`` to + ``None`` if Numpy 2.0 or newer is installed. + For details, see the "Copy semantics" section on the What's New page for Astropy 6.1 . [#16181] + +- astropy is now compiled against NumPy 2.0, enabling runtime compatibility + with this new major release. Compatibility with NumPy 1.23 and newer + versions of NumPy 1.x is preserved through this change. [#16252] + +Version 6.0.1 (2024-03-25) +========================== + +Bug Fixes +--------- + +astropy.coordinates +^^^^^^^^^^^^^^^^^^^ + +- Previously passing a ``SkyCoord`` instance to the ``BaseCoordinateFrame`` + ``separation()`` or ``separation_3d()`` methods could produce wrong results, + depending on what additional frame attributes were defined on the ``SkyCoord``, + but now ``SkyCoord`` input can be used safely. [#15659] + +- ``Distance`` now accepts as ``parallax`` any angle-like value. + This includes types like ``Column`` which have a unit but are not ``Quantity`` subclasses. [#15712] + +- The new default for the class method ``SkyCoord.from_name()`` + is to look for coordinates first in SIMBAD, then in NED, and then in VizieR, + instead of having no specific order. [#16046] + +astropy.io.ascii +^^^^^^^^^^^^^^^^ + +- Reading of CDS header files with multi-line descriptions where the continued line started with a number was broken. This is now fixed. [#15617] + +- Ensure that the names of mixin columns are properly propagated as + labels for the MRT format. [#15848] + +- Fixed reading IPAC tables for ``long`` column type on some platforms, e.g., Windows. [#15992] + +astropy.io.fits +^^^^^^^^^^^^^^^ + +- Fix TDISP parsing for floating numbers. [#16007] + +- Fix a crash when calling FITS ``writeto`` methods with stdout as the output stream. [#16008] + +- Fix TDISP parsing for floating numbers in formats ES / EN. [#16015] + +astropy.stats +^^^^^^^^^^^^^ + +- Fix a spurious warning when calling ``sigma_clipped_stats`` on a ``MaskedColumn``. [#15844] + +astropy.table +^^^^^^^^^^^^^ + +- Fix a Table bug when setting items (via slice or index list) in a ``bytes`` type + ``MaskedColumn`` would cause the column mask to be set to all ``False``. A common way to + trigger this bug was reading a FITS file with masked string data and then sorting the + table. [#15669] + +- Fix slicing logic for Row. + Previously, slicing a ``astropy.table.row.Row`` object would incorrectly return a column, + now it correctly returns a list of values from that row. [#15733] + +- Fix a ``ValueError`` raised by ``table.join`` when fed with large tables. + This would typically happen in situations when the result joined table would be + too large to fit in memory. In those situations, the error message is now much more + clearly about the necessary memory size. [#15734] + +- Fix an unintended exception being raised when attempting to compare two unequal ``Table`` instances. [#15845] + +- Ensure that if a ``Column`` is initialized with a ``Quantity`` it will use by + default a possible name defined on the quantity's ``.info``. [#15848] + +- The unit conversion ``convert_unit_to`` with MaskedColumn was + broken as it was storing the old unit in a dictionary attached + to underlying np.ma.MaskedArray. This fixes it by overwriting + the old unit after unit conversion. [#16118] + +- ``astropy.table.vstack`` will no longer modify the input list even when it + contains non-Table objects like ``astropy.table.Row``. [#16130] + +astropy.units +^^^^^^^^^^^^^ + +- Fix an issue with unicode string representations of units shown as + superscripts (like degree) when raised to some power. Like for + LaTeX representations, now the superscript unicode character is + replaced by the literal short name before adding the power. [#15755] + +- Fix a missing ``Sun`` unit in the list of VOUnits simple_units. [#15832] + +- Fix write/read roundtrips with empty ``Table`` dumped to ECSV. [#15885] + +- Fix a bug where LaTeX formatter would return empty strings for unity (1) input. [#15923] + +- Ensure powers of units are consistently as simple as possible. So, an + integer if possible, otherwise a float, or a fraction if the float is + really close to that. This also ensures the hash of a unit is unique + for any given unit (previously, the same power could be represented as + float, int or fraction, which made the hash different). [#16058] + +- Ensure that ``find_equivalent_units`` only returns actual units, not units + that raised to some power match the requested one. With this fix, + ``(u.m**-3).find_equivalent_units()`` properly finds nothing, rather than all + units of length. [#16127] + +astropy.utils +^^^^^^^^^^^^^ + +- Fix a bug where ``astropy.utils.console.Spinner`` would leak newlines for + messages longer than terminal width. [#16040] + +- Update ``report_diff_values`` so the diff no longer depends on the + console terminal size. [#16065] + +- Fix support in ``Masked`` for generalized ufuncs with more than a + single core dimension (such as ``erfa.rxp``). [#16120] + +astropy.visualization +^^^^^^^^^^^^^^^^^^^^^ + +- Fix an edge case where ``quantity_support`` would produce duplicate tick labels for small data ranges. [#15841] + +astropy.wcs +^^^^^^^^^^^ + +- Updated bundled WCSLIB version to 8.2.2. This update fixes character buffer + overflows in the comment string for the longitude and latitude axes triggered + by some projections in ``wcshdo()``, and also the formatting for generic + coordinate systems. For a full list of changes - see + http://www.atnf.csiro.au/people/mcalabre/WCS/CHANGES or + ``astropy/cextern/wcslib/CHANGES`` [#15795] + +- Fixed a bug in ``fit_wcs_from_points`` that does not set the default value of the ``cdelt`` of the returned WCS object. [#16027] + +Other Changes and Additions +--------------------------- + +- Given the potential breaking changes with the upcoming Numpy 2.0 release, + this release pins Numpy<2.0 and support for Numpy 2.0 will be added in the + v6.1.0 release. + +Version 6.0.0 (2023-11-25) +========================== + +New Features +------------ + +astropy.config +^^^^^^^^^^^^^^ + +- The new ``ConfigNamespace.help()`` method provides a convenient way to get + information about configuration items. [#13499] + +astropy.coordinates +^^^^^^^^^^^^^^^^^^^ + +- Support has been added to create geodetic representations not just for existing ellipsoids + from ERFA, but also with explicitly provided values, by defining a subclass of + ``BaseGeodeticRepresentation`` with the equatorial radius and flattening assigned to + ``_equatorial_radius`` and ``_flattening`` attributes. [#14763] + +- Add ``BaseBodycentricRepresentation``, a new spheroidal representation for bodycentric + latitudes and longitudes. [#14851] + +- Support Numpy broadcasting over frame data and attributes. [#15121] + +astropy.cosmology +^^^^^^^^^^^^^^^^^ + +- Registered a ``latex`` writer for exporting a Cosmology object to a LaTex table. [#14701] + +- Added argument ``rename`` to Cosmology's I/O, allowing for input and output symbols to + be renamed. [#14780] + +- All non-abstract Cosmology subclasses are now automatically registered to work with + Astropy's YAML serialization. [#14979] + +- Cosmology I/O now auto-identifies the '.tex' suffix with the 'ascii.latex' format. [#15088] + +- The ``Cosmology`` class now has a new property to access the parameters of the + cosmology: ``.parameters``. This property return a read-only dictionary of all the + non-derived parameter values on the cosmology object. When accessed from the class (not + an instance) the dictionary contains ``Parameter`` instances, not the values. [#15168] + +- The field ``default`` has been added to ``Parameter``. This can be used to introspect + the default value of a parameter on a cosmology class e.g. ``LambdaCDM.H0.default``. [#15400] + +astropy.io.fits +^^^^^^^^^^^^^^^ + +- Add new option ``decompress_in_memory`` to ``fits.open``, to decompress the + whole file in memory at once, instead of decompressing the file progressively + as data is needed. Default behavior is better for memory usage but sometimes + slow, especially for files with many small HDUs. [#15501] + +astropy.io.votable +^^^^^^^^^^^^^^^^^^ + +- Add support for Parquet serialization of VOTables. Writing of this + serialization is available with using the new ``'votable.parquet'`` format. [#15281] + +- Added MIVOT feature through the ``MivotBlock`` class + that allows model annotations reading and writing in VOTable. [#15390] + +astropy.modeling +^^^^^^^^^^^^^^^^ + +- Added a ``GeneralSersic2D`` model that can have "boxy" or "disky" + isophotes. [#15545] + +astropy.nddata +^^^^^^^^^^^^^^ + +- A more flexible and/or compact string representation is now available for + ``NDData`` objects which visually indicates masked entries, and provides for + better for dask array support. [#14438] + +astropy.table +^^^^^^^^^^^^^ + +- The new ``Row.get()`` method, analogous to ``dict.get()``, returns the value of + the specified column from the row if the column present, otherwise it returns a + fallback value, which by default is ``None``. [#14878] + +astropy.time +^^^^^^^^^^^^ + +- Masked ``Time`` instances now use astropy's own ``Masked`` class internally. + This means that ``Masked`` input is now properly recognized, and that masks + get propagated also to ``Quantity`` output (such as from a ``TimeDelta`` + converted to a unit of time), creating ``MaskedQuantity`` instances. [#15231] + +- Added a ``TimeDelta`` format ``quantity_str`` that represents the time delta as a string + with one or more ``Quantity`` components. This format provides a human-readable + multi-scale string representation of a time delta. The default output sub-format is not + considered stable in this release, please see https://github.com/astropy/astropy/issues/15485 + for more information. [#15264] + +astropy.uncertainty +^^^^^^^^^^^^^^^^^^^ + +- Uncertainty ``Distribution`` now support structured data types, and as + a result it now works also with ``EarthLocation``. [#15304] + +- Uncertainty ``Distribution`` can now be used inside representations, which + also allows basic support in ``SkyCoord``. While most calculations work, there + are remaining issues. For instance, the ``repr`` does not show that the + coordinates are distributions. [#15395] + +astropy.units +^^^^^^^^^^^^^ + +- Add support for gc2gde and gd2gce erfa functions to allow geodetic representations + using equatorial radius and flattening. [#14729] + +astropy.utils +^^^^^^^^^^^^^ + +- The ``astropy.utils.metadata.MetaData`` default dictionary can now be + set with the ``default_factory`` keyword argument. [#15265] + +- ``astropy.utils.decorators.deprecated`` now adds the ``__deprecated__`` attribute to + the objects it wraps, following the practice in https://peps.python.org/pep-0702/. [#15310] + +astropy.visualization +^^^^^^^^^^^^^^^^^^^^^ + +- Add ``WCSAxes.text_coord`` method to print text using ``SkyCoord`` objects + parallel to plotting data points with ``WCSAxes.plot_coord``. [#14661] + +astropy.wcs +^^^^^^^^^^^ + +- Support WCS descriptions of basic planetary coordinate frames. [#14820] + +- Updated bundled WCSLIB version to 8.1. This update adds support planetary keywords ``A_RADIUS``, ``B_RADIUS``, ``C_RADIUS``, ``BLON_OBS``, ``BLAT_OBS``, and ``BDIS_OBS`` in ``auxprm`` and adds ``wcsprm::time`` to the ``wcsprm`` struct to record the ``TIME`` axis. This update also includes several bug fixes. For a full list of changes - see http://www.atnf.csiro.au/people/mcalabre/WCS/CHANGES [#15035] + + +API Changes +----------- + +astropy.config +^^^^^^^^^^^^^^ + +- Removed deprecated ``ConfigurationMissingWarning`` class and ``update_default_config`` function; + There are no replacements as they should no be used anymore. [#15466] + +astropy.convolution +^^^^^^^^^^^^^^^^^^^ + +- Invalid kernel arithmetic operations now raise a ``KernelArithmeticError`` instead of a + bare ``Exception``. [#14728] + +- Added base ``KernelError`` error class and removed ``DiscretizationError`` error class (a ``ValueError`` will be raised instead). [#14732] + +- ``discretize_model`` will now raise a ``ValueError`` if + ``mode='oversample'`` and ``factor`` does not have an integer value. [#14794] + +astropy.coordinates +^^^^^^^^^^^^^^^^^^^ + +- Removed deprecated angle parsing and formatting utilities from ``angle_utilities``. + Use the functions from ``angle_formats`` instead. [#14675] + +- The deprecated functionality of initializing ``Angle`` or ``Longitude`` from a + ``tuple`` is no longer supported. [#15205] + +- Angle-related classes and functions have been moved within ``astropy.coordinates``. + There is no change to public API as everything moved should still be imported from + ``astropy.coordinates``, not a sub-module. If you are using private API, try importing + from ``astropy.coordinates`` instead. If you need something that has been moved and is + not available in ``astropy.coordinates``, please open an issue on the Astropy issue + tracker. [#15220] + +- It is no longer possible to pass frame classes to the ``transform_to()`` method + of a low-level coordinate-frame class. It is still possible to pass frame + instances. The ``transform_to()`` method of the high-level ``SkyCoord`` class + is unaffected. [#15500] + +astropy.cosmology +^^^^^^^^^^^^^^^^^ + +- Removed support of importing private constants and functions from ``astropy.cosmology.flrw``. [#14672] + +- Removed deprecated Cosmology Parameter argument ``fmt``. [#14673] + +- Removed deprecated ``vectorize_if_needed`` and ``inf_like`` from ``cosmology.utils``. [#14677] + +- Removed deprecated import paths from ``astropy.cosmology.core``. [#14782] + +- Cosmology ``Parameter`` is now a ``dataclass``, and can work with all of Python's dataclasses + machinery, like field introspection and type conversion. [#14874] + +- A new property -- ``scale_factor0`` -- has been added to Cosmology objects. + This is the scale factor at redshift 0, and is defined to be 1.0. [#14931] + +- Added registration label ``ascii.latex`` to Cosmology IO. [#14938] + +- The private module ``astropy.cosmology.utils`` has been deprecated. [#14980] + +- Removed deprecated ``get_cosmology_from_string`` class method in ``default_cosmology``; use ``get`` instead. [#15467] + +astropy.io.ascii +^^^^^^^^^^^^^^^^ + +- Several arguments in functions within ``astropy.io.ascii`` have been deprecated and + are either renamed or scheduled to be removed. + + ``read()``: + - ``Reader`` will be removed. Instead supply the equivalent ``format`` argument. + - ``Inputter`` has been renamed to ``inputter_cls``. + - ``Outputter`` has been renamed to ``outputter_cls``. + + ``get_reader()``: + - ``Reader`` has been renamed to ``reader_cls``. + - ``Inputter`` has been renamed to ``inputter_cls``. + - ``Outputter`` has been renamed to ``outputter_cls``. + + ``write()``: + - ``Writer`` will be removed. Instead supply the equivalent ``format`` argument. + + ``get_writer()``: + - ``Writer`` has been renamed to ``writer_cls``. [#14914] + +- Removed deprecated ``astropy.io.ascii.tests.common.raises`` test helper; use ``pytest.raises`` instead. [#15470] + +astropy.io.fits +^^^^^^^^^^^^^^^ + +- Deprecate ``_ExtensionHDU`` and ``_NonstandardExtHDU`` (use ``ExtensionHDU`` or + ``NonstandardExtHDU`` instead). [#15396] + +- Remove special handling of TCTYP TCUNI TCRPX TCRVL TCDLT TRPOS (#7157). [#15396] + +- Rename and deprecate ``TableHDU.update`` to ``TableHDU.update_header``, for + consistency with ``ImageHDU``. [#15396] + +astropy.io.misc +^^^^^^^^^^^^^^^ + +- Removed deprecated ``astropy.io.misc.asdf`` subpackage. Use ``asdf-astropy`` package instead. [#14668] + +- ``fnunpickle`` and ``fnpickle`` are deprecated because they are not used anywhere within ``astropy``. + If you must, use the module from Python standard library but be advised that pickle is insecure + so you should only unpickle data that you trust. [#15418] + +astropy.io.votable +^^^^^^^^^^^^^^^^^^ + +- Removed deprecated ``pedantic`` option from the + ``astropy.io.votable.table.parse()`` function and the corresponding configuration + setting. Use the ``verify`` option instead. [#14669] + +- Class ``astropy.io.votable.tree.Table`` has been renamed to ``TableElement`` + to avoid sharing the name with ``astropy.table.Table``. [#15372] + +- Fully removed support for version = '1.0' on ``VOTableFile__init__()`` and changed its tests to check correctly. + It was raising a ``DeprecationWarning`` and now is raising a ``ValueError``. [#15490] + +astropy.modeling +^^^^^^^^^^^^^^^^ + +- Removed the ``AliasDict`` class from ``modeling.utils``. [#12943] + +- Creating a model instance with parameters that have incompatible shapes will + now raise a ``ValueError`` rather than an ``IncompatibleShapeError``. [#15209] + +- Removal of deprecated code ``_model_to_fit_params`` and ``_fitter_to_model_params`` from ``fitting.py``. [#15461] + +astropy.stats +^^^^^^^^^^^^^ + +- The ``BoxLeastSquares``, ``BoxLeastSquaresResults`` and ``LombScargle`` classes + are not available from ``astropy.stats`` anymore, they are now available only + from ``astropy.timeseries``. [#15530] + +astropy.tests +^^^^^^^^^^^^^ + +- Removed deprecated deprecation, warning, and exception handling functionality provided by ``astropy.tests.helper``. [#14670] + +- ``astropy.tests.command.FixRemoteDataOption`` and ``astropy.tests.command.AstropyTest`` are deprecated. + They are no longer necessary after sunsetting ``astropy-helpers``. [#15204] + +astropy.time +^^^^^^^^^^^^ + +- ``Time`` has switched to use ``Masked`` arrays internally, instead of + indicating masked values using NaN in the internal ``jd2`` attribute. As a + result, any output from instances, such as one gets with, say, the ``.isot`` + format, will also use ``Masked`` by default. + + For backwards compatibility, a new configuration item, + ``astropy.time.conf.masked_array_type`` is introduced which is set to + "astropy" by default (which indicates one wants to use ``Masked``), but can + also be set to "numpy", in which case ``numpy.ma.MaskedArray`` will be used + where possible (essentially, for all but ``Quantity``). [#15231] + +- Changed the ``TimeDelta`` init signature to be consistent with that of ``Time``. + Previously the argument order was ``val, val2, format, scale, copy``. Now the order is + ``val, val2, format, scale, *, precision, in_subfmt, out_subfmt, copy``, where the + arguments after the ``*`` must be specified by keyword. [#15264] + +astropy.timeseries +^^^^^^^^^^^^^^^^^^ + +- Removed deprecated ``midpoint_epoch`` in ``fold`` function; use ``epoch_time`` instead. [#15462] + +astropy.uncertainty +^^^^^^^^^^^^^^^^^^^ + +- The ``.dtype`` attribute exposed by ``Distribution`` is now that of + the samples, rather than one that has a "samples" entry. This makes + quantities with structured data types and units easier to support, and + generally makes the ``Distribution`` appear more similar to regular + arrays. It should have little effect on code. For instance, + ``distribution["samples"]`` still will return the actual distribution. + + As a consequence of this refactoring, most arrays that are not + C-contiguous can now be viewed and will thus not be copied on input + any more. The only exceptions are arrays for which the strides are + negative. + + Note that the true data type is considered an implementation detail. + But for reference, it now is a structured data type with a single + field, "samples", which itself is an array of "sample" fields, which + contain the actual data. [#15304] + +astropy.units +^^^^^^^^^^^^^ + +- Like ``np.ndarray``, under numpy 2.0 ``Quantity`` and all its subclasses + (``Angle``, ``Masked``, etc.) will no longer support the ``.ptp()`` method. + Use ``np.ptp(...)`` instead. + + Similarly, support for the much less frequently used ``.newbyteorder()`` and + ``.itemset()`` methods has been removed. [#15378] + +- The following deprecated functionality has been removed: + + * ``littleh`` unit and ``with_H0`` equivalency. They are still available from + ``cosmology.units``. + * ``brightness_temperature`` equivalency no longer automatically swaps the + order of its arguments if it does not match the expectation. + * ``PhysicalType`` no longer supports ``str`` methods and attributes. [#15514] + +astropy.utils +^^^^^^^^^^^^^ + +- Removed deprecated ``OrderedDescriptor``, ``OrderedDescriptorContainer``, and ``set_locale`` in ``astropy.utils.misc``. [#14679] + +- ``is_path_hidden()`` and ``walk_skip_hidden()`` are deprecated. [#14759] + +- The structure of ``utils.metadata`` has been refactored, but all the available + functions and classes are still present and should be imported as before. [#15166] + +- The ``astropy.utils.metadata.MetaData`` class, which is used throughout astropy + to carry metadata on tables, columns, etc., can now also be used on dataclasses. + + When accessing the meta attribute on a class ``astropy.utils.metadata.MetaData`` + now returns None instead of itself. [#15237] + +- The ``astropy.utils.metadata.MetaData`` class, which is used throughout astropy + to carry metadata on tables, columns, etc., can now also be used on frozen dataclasses. [#15404] + +- Removed deprecated ``version_path`` in ``minversion`` function; it is no longer used. [#15468] + +astropy.visualization +^^^^^^^^^^^^^^^^^^^^^ + +- The ``bboxes``, ``ticklabels_bbox``, and ``tick_out_size`` arguments to ``astropy.visualization.wcaxes.ticklabels.TickLabels.draw()`` now have no effect and are deprecated. + This is to allow rasterized ticks to be drawn correctly on WCSAxes. [#14760] + +- It is now not possible to pass any keyword arguments to ``astropy.visualization.wcsaxes.WCSAxes.draw()``. + Previously passing any keyword arguments would have errored anyway, as ``matplotlib.axes.Axes.draw()`` does not accept keyword arguments. [#14772] + +- Deprecated the ``exp`` attribute in the ``LogStretch``, + ``InvertedLogStretch``, ``PowerDistStretch``, and + ``InvertedPowerDistStretch`` stretch classes, and the ``power`` + attribute in the ``PowerStretch``. Instead, use the ``a`` attribute, + which matches the input keyword. [#15538] + +- Removed the maximum value of the ``a`` parameter in the ``AsinhStretch`` + and ``SinhStretch`` stretch classes. [#15539] + +astropy.wcs +^^^^^^^^^^^ + +- Removed deprecated ``accuracy`` from ``all_world2pix`` method in ``WCS``; use ``tolerance`` instead. [#15464] + +- ``NoConvergence`` no longer accepts arbitrary keyword arguments. [#15504] + + +Bug Fixes +--------- + +astropy.coordinates +^^^^^^^^^^^^^^^^^^^ + +- Fixed minor bug when getting solar system positions of objects from Type 3 SPICE kernel files. [#15612] + +astropy.cosmology +^^^^^^^^^^^^^^^^^ + +- The exponent in ``w0wzCDM.de_density_scale`` has been corrected to 3, from -3. + This correction has also been made to the scalar ``inv_efunc`` cpython functions. [#14991] + +- ``pandas.Series`` are now uniformly converted to their underlying data type when given + as an argument to a Cosmology method. [#15600] + +astropy.io.fits +^^^^^^^^^^^^^^^ + +- Reading a table from FITS now respects the TNULL property of a column, passing + it into the column's ``fill_value``. [#14723] + +- Fix crash when a PrimaryHDU has a GROUPS keyword with a non-boolean value (i.e. + not a random-groups HDU). [#14998] + +- Fixed a bug that caused ``Cutout2D`` to not work correctly with ``CompImageHDU.section`` [#14999] + +- Fixed a bug that caused compressed images with TFORM missing the optional '1' prefix to not be readable. [#15001] + +- Ensure that tables written to FITS with both masked and unmasked columns + roundtrip properly (previously, all integer columns would become masked + if any column was masked). [#15473] + +- Fix segfault with error report in tile decompression. [#15489] + +astropy.io.votable +^^^^^^^^^^^^^^^^^^ + +- Output of ``repr`` for VOTable instance now clearly shows it is a VOTable and not generic astropy Table. [#14702] + +astropy.modeling +^^^^^^^^^^^^^^^^ + +- All models can be pickled now. [#14902] + +astropy.nddata +^^^^^^^^^^^^^^ + +- Restore bitmask propagation behavior in ``NDData.mask``, plus a fix + for arithmetic between masked and unmasked ``NDData`` objects. [#14995] + +astropy.table +^^^^^^^^^^^^^ + +- ``Table.as_array`` now respects the ``fill_value`` property of masked columns. [#14723] + +- Fix a bug where table indexes were not using a stable sort order. This was causing the + order of rows within groups to not match the original table order when an indexed table + was grouped. [#14907] + +- Fixed issue #14964 that when grouping a Table on a mixin column such as ``Quantity`` or + ``Time``, the grouped table keys did not reflect the original column values. For + ``Quantity`` this meant that the key values were pure float values without the unit, + while for ``Time`` the key values were the pair of ``jd1`` and ``jd2`` float values. [#14966] + +astropy.time +^^^^^^^^^^^^ + +- Ensure that the ``Time`` caches of formats and scales do not get out + of sync with the actual data, even if another instance, holding a view + of the data is written to. E.g., if one does ``t01 = t[:2]``, and + sets ``t[0]`` after, it is now guaranteed that ``t01.value`` will + correctly reflect that change in value. [#15453] + +astropy.units +^^^^^^^^^^^^^ + +- In VOunits, "pix", "au", "a", and "ct" are removed from the list of deprecated units. [#14885] + +astropy.utils +^^^^^^^^^^^^^ + +- Ufuncs with more than 2 operands (such as ``erfa.dtf2d``) now work + also if all inputs are scalars and more than two inputs have masks. [#15450] + +- Ensured that ``str(masked_array)`` looks like ``str(unmasked_array)`` also for + array scalars. Thus, like regular array scalars, the precision is ignored for + float, and strings do not include extra quoting. [#15451] + +astropy.visualization +^^^^^^^^^^^^^^^^^^^^^ + +- The location of ticklabels on a WCSAxes is now correctly calculated when the figure is rasterized. [#14760] + +- Fixed a bug where a ``ValueError`` would be raised in the + ``AsinhStretch`` and ``SinhStretch`` classes for valid ``a`` parameter + values. [#15539] + +astropy.wcs +^^^^^^^^^^^ + +- ``wcs.validate(filename)`` now properly closes the file handler. [#15054] + +- Fix a regression in custom WCS mapping due to the recent introduction of + Solar System frames. [#15630] + + +Other Changes and Additions +--------------------------- + +- The minimum supported version of NumPy is now 1.22. [#15006] + +- Moved International Earth Rotation and Reference Systems (IERS) and Leap Second + files out into standalone astropy-iers-data package, maintaining full + backward-compatibility in the ``astropy.utils.iers`` API. Deprecation + warnings may be issued when certain files are accessed directly. [#14819] + +- Switch from using ``setup.cfg`` for project configuration to using ``pyproject.toml``. [#15247] + +- Update bundled expat to 2.5.0. [#15585] + +Version 5.3.4 (2023-10-03) +========================== + +Bug Fixes +--------- + +astropy.io.misc +^^^^^^^^^^^^^^^ + +- Updated ``astropy.io.misc.yaml`` so ``dump()`` with a numpy object array or + ``load()`` with YAML representing a Numpy object array both raise + ``TypeError``. This prevents problems like a segmentation fault. [#15373] + +astropy.io.votable +^^^^^^^^^^^^^^^^^^ + +- Fixed a bug in ``convert_to_writable_filelike`` where ``GzipFile`` was not + closed properly. [#15359] + +astropy.units +^^^^^^^^^^^^^ + +- In VOUnit, the spaces around the slash were removed in the formatting of + fractions, and fractional powers now also use the "**" operator. [#15282] + +- We now ensure that the unit ``u.cgs.cm`` is just an alias of ``u.si.cm``, + instead of a redefinition. This ensures that ``u.Unit("cm") / u.cm`` + will reliably cancel to dimensionless (instead of some "cm / cm"). [#15368] + +astropy.utils +^^^^^^^^^^^^^ + +- For ``Masked``, ``np.ptp`` and the ``.ptp()`` method now properly account for + the mask, ensuring the result is identical to subtracting the maximum and + minimum (with the same arguments). [#15380] + +Other Changes and Additions +--------------------------- + +- Compatibility with Python 3.12. [#14784] + +- Replaced the URL of ``IETF_LEAP_SECOND_URL`` because the original is now + defunct and IETF now defers to IANA for such look-up. [#15421] + + +Version v5.3.3 (2023-09-07) +=========================== + +Bug Fixes +--------- + +astropy.coordinates +^^^^^^^^^^^^^^^^^^^ + +- ``TransformGraph.to_dot_graph()`` now throws an exception for invalid ``savelayout``. + +astropy.cosmology +^^^^^^^^^^^^^^^^^ + +- The exponent of ``w0wzCDM`` functions in ``inv_efunc`` has been corrected to 3, from -3. [#15224] + +astropy.modeling +^^^^^^^^^^^^^^^^ + +- Astropy modeling can filter non-finite data values using the ``filter_non_finite`` + keyword argument in a fitter call. Now when ``filter_non_finite`` is True, + non-finite *weights* will also be filtered to prevent crashes in ``LevMarLSQFitter``. [#15215] + +astropy.units +^^^^^^^^^^^^^ + +- Fixed ``astropy.units.Quantity``'s implementation of ``numpy.nanmedian()``, + where for Numpy >= 1.25 an exception was raised for some array shapes and axis + combinations. [#15228] + + +Other Changes and Additions +--------------------------- + +- v5.3.x will not support NumPy 2.0 or later. [#15234] + + +Version 5.3.2 (2023-08-11) +========================== + +Bug Fixes +--------- + +astropy.coordinates +^^^^^^^^^^^^^^^^^^^ + +- Fixed import when called with Python ``-OO`` flag. [#15037] + +astropy.nddata +^^^^^^^^^^^^^^ + +- Fix for collapse operations on ``NDData`` without masks or units. [#15082] + +astropy.units +^^^^^^^^^^^^^ + +- Modified the implementation of ``np.power()`` for instances of ``Quantity`` to + allow any array as the second operand if all its elements have the same value. [#15101] + +Version 5.3.1 (2023-07-06) +========================== + +Bug Fixes +--------- + +astropy.cosmology +^^^^^^^^^^^^^^^^^ + +- The exponent in ``wowzCDM.de_density_scale`` has been corrected to 3, from -3. [#14991] + +astropy.io.fits +^^^^^^^^^^^^^^^ + +- Fix crash when a PrimaryHDU has a GROUPS keyword with a non-boolean value (i.e. + not a random-groups HDU). [#14998] + +- Fixed a bug that caused ``Cutout2D`` to not work correctly with ``CompImageHDU.section`` [#14999] + +- Fixed a bug that caused compressed images with TFORM missing the optional '1' prefix to not be readable. [#15001] + +astropy.modeling +^^^^^^^^^^^^^^^^ + +- All models can be pickled now. [#14902] + +astropy.nddata +^^^^^^^^^^^^^^ + +- Restore bitmask propagation behavior in ``NDData.mask``, plus a fix + for arithmetic between masked and unmasked ``NDData`` objects. [#14995] + +astropy.table +^^^^^^^^^^^^^ + +- Fix a bug where table indexes were not using a stable sort order. This was causing the + order of rows within groups to not match the original table order when an indexed table + was grouped. [#14907] + +astropy.units +^^^^^^^^^^^^^ + +- In VOunits, "pix", "au", "a", and "ct" are removed from the list of deprecated units. [#14885] + +Version 5.3 (2023-05-22) +======================== + +New Features +------------ + +astropy.coordinates +^^^^^^^^^^^^^^^^^^^ + +- Add optional parameter ``refresh_cache`` to ``EarthLocation.of_site()`` and + ``EarthLocation.get_site_names()`` to force the download of the latest site + registry. [#13993] + +- Added ``atol`` argument to function ``is_O3`` and ``is_rotation`` in matrix utilities. [#14371] + +- A new class ``astropy.coordinates.StokesCoord`` has been added to represent world coordinates describing polarization state. + This change introduces a breaking change to the return value of ``astropy.wcs.WCS.pixel_to_world`` where before a ``u.Quantity`` object would be returned containing numerical values representing a Stokes profile now a ``StokesCoord`` object is returned. The previous numerical values can be accessed with ``StokesCoord.value``. [#14482] + +- Add an optional parameter ``location`` to ``EarthLocation.get_itrs()`` + to allow the generation of topocentric ITRS coordinates with respect + to a specific location. [#14628] + +astropy.cosmology +^^^^^^^^^^^^^^^^^ + +- Two new cosmologies have been added, ``FlatwpwaCDM`` and ``Flatw0wzCDM``, which are the + flat variants of ``wpwaCDM`` and ``w0wzCDM``, respectively. [#12353] + +astropy.io.ascii +^^^^^^^^^^^^^^^^ + +- Add ability to read and write an RST (reStructuredText) ASCII table that + includes additional header rows specifying any or all of the column dtype, unit, + format, and description. This is available via the new ``header_rows`` keyword + argument. [#14182] + +astropy.io.fits +^^^^^^^^^^^^^^^ + +- Added support for >3D data in CompImageHDU [#14252] + +- Added a ``CompImageHDU.section`` property which can be used to + efficiently access subsets of the data, similarly to ``ImageHDU.section``. + When using this, only the tiles required to cover the section are + read from disk and decompressed. [#14353] + +- Added support for ``'NOCOMPRESS'`` for the ``compression_type`` option in ``CompImageHDU``. [#14408] + +- Added new properties ``compression_type`` and ``tile_shape`` on + ``CompImageHDU``, giving the name of the compression algorithm + and the shape of the tiles in the tiled compression respectively. [#14428] + +- Do not call ``gc.collect()`` when closing a ``CompImageHDU`` object as it has a + large performance penalty. [#14576] + +- VLA tables can now be written with the unified I/O interface. + When object types are present or the VLA contains different types a `TypeError` + is thrown. [#14578] + +astropy.io.misc +^^^^^^^^^^^^^^^ + +- Add support for writing/reading fixed-size and variable-length array columns to the parquet formatter. [#14237] + +astropy.io.votable +^^^^^^^^^^^^^^^^^^ + +- Added a method ``get_infos_by_name`` to make it easier to implement + DALI-compliant protocols [#14212] + +- Updating the built-in UCD list to upstream 1.5 (which requires a minor + update to the parser) [#14554] + +astropy.modeling +^^^^^^^^^^^^^^^^ + +- Enable check for poorly conditioned fits in ``LinearLSQFitter`` for polynomial + models with fixed inputs. [#14037] + +astropy.nddata +^^^^^^^^^^^^^^ + +- ``astropy.nddata.NDDataArray`` now has collapsing methods like ``sum``, + ``mean``, ``min``, and ``max`` which operate along any axes, and better + support for ``astropy.utils.Masked`` objects. [#14175] + +astropy.stats +^^^^^^^^^^^^^ + +- ``vonmisesmle`` has now functioning "weights" and "axis" parameters that work equivalently + to the rest of the functions in the ``circstats`` module (``circmean``, ``rayleightest``, etc.) [#14533] + +astropy.table +^^^^^^^^^^^^^ + +- ``Table`` and ``QTable`` can now use the ``|`` and ``|=`` operators for + dictionary-style merge and update. [#14187] + +astropy.time +^^^^^^^^^^^^ + +- Add a ``leap_second_strict`` argument to the ``Time.to_datetime()`` method. This + controls the behavior when converting a time within a leap second to the ``datetime`` + format and can take the values ``raise`` (the default), ``warn``, or ``silent``. [#14606] + +astropy.timeseries +^^^^^^^^^^^^^^^^^^ + +- Adds the ``astropy.timeseries.LombScargleMultiband`` class, which is an + extension of the ``astropy.timeseries.LombScargle`` class. It enables the + generation of periodograms for datasets with measurements taken in more than + one photometric band. [#14016] + +- Add ``unit_parse_strict`` parameter to the Kepler reader to control the warnings + emitted when reading files. [#14294] + +astropy.units +^^^^^^^^^^^^^ + +- Add support for degrees Celsius for FITS. Parsing "Celsius" and "deg C" is now + supported and astropy will output "Celsius" into FITS. + + Note that "deg C" is only provided for compatibility with existing FITS files, + as it does not conform to the normal unit standard, where this should be read + as "degree * Coulomb". Indeed, compound units like "deg C kg-1" will still be + parsed as "Coulomb degree per kilogram". [#14042] + +- Enabled the ``equal_nan`` keyword argument for ``np.array_equal()`` when the + arguments are ``astropy.units.Quantity`` instances. [#14135] + +- Allow "console" and "unicode" formats for conversion to string of + function units. [#14407] + +- Add a "fraction" options to all the unit ``format`` classes, which determine + whether, if a unit has bases raised to a negative power, a string + representation should just show the negative powers (``fraction=False``) or + use a fraction, and, in the latter case, whether to use a single-line + representation using a solidus (``fraction='inline'`` or ``fraction=True``) + or, if the format supports it, a multi-line presentation with the numerator + and denominator separated by a horizontal line (``fraction='multiline'``). [#14449] + +astropy.utils +^^^^^^^^^^^^^ + +- The ``mean`` method on ``NDDataArray`` now avoids a division by zero + warning when taking the mean of a fully-masked slice (and still + returns ``np.nan``). [#14341] + +- Ensure we can read the newer ``IERS_B`` files produced by the International + Earth Rotation and Reference Systems Service, and point + ``astropy.utils.iers.IERS_B_URL`` to the new location. [#14382] + + +API Changes +----------- + +astropy.coordinates +^^^^^^^^^^^^^^^^^^^ + +- ``get_moon()`` is deprecated and may be removed in a future version of + ``astropy``. Calling ``get_moon(...)`` should be replaced with + ``get_body("moon", ...)``. [#14354] + +astropy.io.fits +^^^^^^^^^^^^^^^ + +- Deprecate the auto-fixing of tile sizes for HCOMPRESS_1 tiled + image compression when the tile size could be changed by +1 + to make it acceptable. [#14410] + +- The ``tile_size=`` argument to ``CompImageHDU`` has been deprecated + as it was confusing that it was required to be in the opposite + order to the data shape (it was in header rather than Numpy order). + Instead, users should make use of the ``tile_shape=`` argument which + is in Numpy shape order. [#14428] + +astropy.modeling +^^^^^^^^^^^^^^^^ + +- Deprecate the ``humlicek2`` method for `~astropy.modeling.functional_models.Voigt1D` in favor + of using the ``wofz`` method using the `scipy.special.wofz` implementation of the + Fadeeva function whenever `scipy` is installed. [#14013] + +- Deprecated ``astropy.modeling.utils.comb()`` function in favor of ``comb()`` + from ``math`` standard library. [#14038] + +- Propagate measurement uncertainties via the ``weights`` keyword argument into the + parameter covariances. [#14519] + +astropy.units +^^^^^^^^^^^^^ + +- The conversion of ``astropy.units.Quantity`` to ``bool`` + that was deprecated since astropy 3.0 now raises a ``ValueError``. + This affects statements like ``if quantity``. + Use explicit comparisons like ``if quantity.value != 0`` + or ``if quantity is not None`` instead. [#14124] + +- Operations on ``Quantity`` in tables are sped up by only copying ``info`` when + it makes sense (i.e., when the object can still logically be thought of as the + same, such as in unit changes or slicing). ``info`` is no longer copied if a + ``Quantity`` is part of an operation. [#14253] + +- The ``Quantity.nansum`` method has been deprecated. It was always weird that it + was present, since ``ndarray`` does not have a similar method, and the other + ``nan*`` functions such as ``nanmean`` did not have a corresponding method. + Use ``np.nansum(quantity)`` instead. [#14267] + +- The unused ``units.format.Unscaled`` format class has been deprecated. [#14417] + +- The order in which unit bases are displayed has been changed to match the + order bases are stored in internally, which is by descending power to which + the base is raised, and alphabetical after. This helps avoid monstrosities + like ``beam^-1 Jy`` for ``format='fits'``. + + Note that this may affect doctests that use quantities with complicated units. [#14439] + +astropy.utils +^^^^^^^^^^^^^ + +- For ``Masked`` instances, the ``where`` argument for any ufunc can now + also be masked (with any masked elements masked in the output as well). + This is not very useful in itself, but avoids problems in conditional + functions (like ``np.add(ma, 1, where=ma>10)``). [#14590] + +astropy.visualization +^^^^^^^^^^^^^^^^^^^^^ + +- The pixel attribute of ``astropy.visualization.wcsaxes.frame.Spine`` is deprecated + and will be removed in a future astropy version. + Because it is (in general) not possible to correctly calculate pixel + coordinates before Matplotlib is drawing a figure, instead set the world or data + coordinates of the ``Spine`` using the appropriate setters. [#13989] + +- Passing a bare number as the ``coord_wrap`` argument to ``CoordinateHelper.set_coord_type`` is deprecated. + Pass a ``Quantity`` with units equivalent to angular degrees instead. + + The ``.coord_wrap`` attribute of ``CoordinateHelper`` is now a ``Quantity`` instead of a bare number. [#14050] + + +Bug Fixes +--------- + +astropy.coordinates +^^^^^^^^^^^^^^^^^^^ + +- ``Angle.to_string()`` was changed to ensure it matches the behaviour of + ``Quantity.to_string()`` in having a space between the value and the unit + for display with non-degree and hourangle units (i.e., the case in which + units are displayed by their name; the sexagesimal case for degrees or + hourangle that uses symbols is not changed). [#14379] + +astropy.io.ascii +^^^^^^^^^^^^^^^^ + +- Fix an issue in the ``io.ascii`` QDP format reader to allow lower-case commands in the + table data file. Previously it required all upper case in order to parse QDP files. [#14365] + +astropy.io.fits +^^^^^^^^^^^^^^^ + +- Compressing/decompressing a floating point dataset containing NaN values will + no longer read in the whole tile as NaNs. + + Fixed segmentation faults that occurred when compressing/decompressing data + with the PLIO_1 algorithm. [#14252] + +- ``Card`` now uses the default Python representation for floating point + values. [#14508] + +- ``ImageHDU`` now properly rejects Numpy scalars, avoiding data corruption. [#14528] + +- Fix issues with double quotes in CONTINUE cards. [#14598] + +- Fixes an issue where FITS_rec was incorrectly raising a ValueError exception when the heapsize was greater than 2**31 + when the Column type was 'Q' instead of 'P'. [#14810] + +astropy.io.misc +^^^^^^^^^^^^^^^ + +- Columns with big-endian byte ordering (such as those read in from a FITS table) can now be serialized with Parquet. [#14373] + +astropy.modeling +^^^^^^^^^^^^^^^^ + +- Bugfix for using ``getter/setter`` in properties to adjust the internal (computational) + value of a property vs its external proxy value when the values involve units. [#14512] + +- Fix issue with ``filter_non_finite`` option when fitting with ``weights`` via passing + the ``weights`` through the non-finite-filter alongside the input data. [#14695] + +- Fixed an issue with Parameter where a getter could be input without a + setter (or vice versa). [#14708] + +astropy.time +^^^^^^^^^^^^ + +- Using quantities with units of time for ``Time`` format 'decimalyear' will now + raise an error instead of converting the quantity to days and then + interpreting the value as years. An error is raised instead of attempting to + interpret the unit as years, since the interpretation is ambiguous: in + 'decimaltime' years are equal to 365 or 366 days, while for regular time units + the year is defined as 365.25 days. [#14566] + +astropy.uncertainty +^^^^^^^^^^^^^^^^^^^ + +- Ensure that ``Distribution`` can be compared with ``==`` and ``!=`` + with regular arrays or scalars, and that inplace operations like + ``dist[dist<0] *= -1`` work. [#14421] + +astropy.units +^^^^^^^^^^^^^ + +- Modified ``astropy.units.Quantity.__array_ufunc__()`` to return ``NotImplemented`` instead of raising a ``ValueError`` if the inputs are incompatible. [#13977] + +- Modified the behavior of ``numpy.array_equal()`` and ``numpy.array_equiv()`` to + return ``False`` instead of raising an error if their arguments are + ``astropy.units.Quantity`` instances with incompatible units. [#14163] + +- Spaces have been regularized for the ``unicode`` and ``console`` output + formats: no extraneous spaces in front of the unit, and always a space + between a possible scale factor and the unit. [#14413] + +- Prefixed degrees and arcmin are now typeset without using the symbol in + ``latex`` and ``unicode`` formats (i.e., ``mdeg`` instead of ``m°``), + as was already the case for arcsec. [#14419] + +- Ensure the unit is kept in ``np.median`` even if the result is a scalar ``nan`` + (the unit was lost for numpy < 1.22). [#14635] + +- Ensure that ``Quantity`` with structured dtype can be set using non-structured + ``Quantity`` (if units match), and that structured dtype names are inferred + correctly in the creation of ``StructuredUnit``, thus avoiding mismatches + when setting units. [#14680] + +astropy.utils +^^^^^^^^^^^^^ + +- When using astropy in environments with sparse file systems (e.g., where the temporary directory and astropy data directory resides in different volumes), ``os.rename`` may fail with ``OSError: [Errno 18] Invalid cross-device link``. + This may affect some clean-up operations executed by the ``data`` module, causing them to fail. + This patch is to catch ``OSError`` with ``errno == EXDEV`` (i.e., Errno 18) when performing these operations and try to use ``shutil.move`` instead to relocate the data. [#13730] + +- Ensure masks are propagated correctly for ``outer`` methods of ufuncs also if + one of the inputs is not actually masked. [#14624] + +astropy.visualization +^^^^^^^^^^^^^^^^^^^^^ + +- The location of a ``astropy.visualization.wcsaxes.frame.Spine`` in a plot is now + correctly calculated when the DPI of a figure changes between a WCSAxes being + created and the figure being drawn. [#13989] + +- ``CoordinateHelper.set_ticks()`` now accepts ``number=0``. Previously it errored. [#14160] + +- ``WCSAxes.plot_coord`` and ``plot_scatter`` now work correctly for APE 14 compliant WCSes where the units are not always converted to degrees. [#14251] + +- Fixed a bug where coordinate overlays did not automatically determine the + longitude wrap angle or the appropriate units. [#14326] + +astropy.wcs +^^^^^^^^^^^ + +- Fix bugs with high-level WCS API on ``wcs.WCS`` object when using ``-TAB`` + coordinates. [#13571] + +- Fixed a bug in how WCS handles ``PVi_ja`` header coefficients when ``CTYPE`` + has ``-SIP`` suffix and in how code detects TPV distortions. [#14295] + + +Other Changes and Additions +--------------------------- + +- The minimum supported version of Python is now 3.9, changing from 3.8. [#14286] + +- The minimum supported version of Numpy is now 1.21. [#14349] + +- The minimum supported version of matplotlib is now 3.3. [#14286, #14321] + +- ``astropy`` no longer publishes wheels for i686 architecture. [#14517] + +- Added a pre-commit configuration for codespell. [#13985] + +- Removed a large fraction of the bundled CFITSIO code and internally refactored + FITS compression-related code, which has resulted in a speedup when compiling + astropy from source (40% faster in some cases). [#14252] + +- The CFITSIO library is no longer bundled in full with astropy and + the option to build against an external installation of CFITSIO + has now been removed, so the ASTROPY_USE_SYSTEM_CFITSIO environment + variable will be ignored during building. [#14311] + +- Updated CDS URL for Sesame look-up as the old URL is deprecated. [#14681] + + +Version 5.2.2 (2023-03-28) +========================== + +Bug Fixes +--------- + +astropy.io.ascii +^^^^^^^^^^^^^^^^ + +- CDS and MRT tables with units that contain with multiple divisions, such as + ``km/s/Mpc`` now parse correctly as being equal to ``km/(s.Mpc)``. [#14369] + +astropy.io.fits +^^^^^^^^^^^^^^^ + +- Fix ``FITSDiff`` when table contains a VLA column with the Q type. [#14539] + +astropy.table +^^^^^^^^^^^^^ + +- Fix a bug when creating a ``QTable`` when a ``Quantity`` input column is present and the + ``units`` argument modifies the unit of that column. This now works as expected where + previously this caused an exception. [#14357] + +astropy.units +^^^^^^^^^^^^^ + +- CDS units with multiple divisions, such as ``km/s/Mpc`` now parse + correctly as being equal to ``km/(s.Mpc)``. [#14369] + +astropy.wcs +^^^^^^^^^^^ + +- Fixed a bug that caused subclasses of BaseHighLevelWCS and HighLevelWCSMixin to + not work correctly under certain conditions if they did not have ``world_n_dim`` + and ``pixel_n_dim`` defined on them. [#14495] + + +Version 5.2.1 (2023-01-06) +========================== + +Bug Fixes +--------- + +astropy.coordinates +^^^^^^^^^^^^^^^^^^^ + +- Fix to ITRS frame ``earth_location`` attribute to give the correct result for + a topocentric frame. [#14180] + +astropy.cosmology +^^^^^^^^^^^^^^^^^ + +- Bounds are no longer passed to the scipy minimizer for methods Brent and + Golden. The scipy minimizer never used the bounds but silently accepted them. + In scipy v1.11.0.dev0+ an error is raised, so we now pass None as the bounds + to the minimizer. Users should not be affected by this change. [#14232] + +astropy.io.fits +^^^^^^^^^^^^^^^ + +- Tables with multidimensional variable length array can now be properly read + and written. [#13417] + +astropy.units +^^^^^^^^^^^^^ + +- Modified the behavior of ``numpy.histogram()``, + ``numpy.histogram_bin_edges()``, ``numpy.histogram2d()``, and + ``numpy.histogramdd()`` so that the ``range`` argument must a compatible + instance of ``astropy.units.Quantity`` if the other arguments are instances of + ``astropy.units.Quantity``. [#14213] + +astropy.visualization +^^^^^^^^^^^^^^^^^^^^^ + +- Improved the performance of drawing WCSAxes grids by skipping some unnecessary + computations. [#14164] + +- Fixed WCSAxes sometimes triggering a NumPy RuntimeWarning when determining the + coordinate range of the axes. [#14211] + +Other Changes and Additions +--------------------------- + +- Fix compatibility with Numpy 1.24. [#14193] + +Version 5.2 (2022-12-12) +======================== + +New Features +------------ + +astropy.coordinates +^^^^^^^^^^^^^^^^^^^ + +- Adds new topocentric ITRS frame and direct transforms to and from the observed + frames ``AltAz`` and ``HADec`` with the ability to add or remove refraction + corrections as required. Since these frames are all within the ITRS, there are + no corrections applied other than refraction in the transforms. This makes the + topocentric ITRS frame and these transforms convenient for observers of near + Earth objects where stellar aberration should be omitted. [#13398] + +- Allow comparing ``SkyCoord`` to frames with data. [#13477] + +astropy.cosmology +^^^^^^^^^^^^^^^^^ + +- Cosmology instance can be parsed from or converted to a HTML table using + the new HTML methods in Cosmology's ``to/from_format`` I/O. [#13075] + +- A new comparison function has been added -- ``cosmology_equal()`` -- that + mirrors its ``numpy`` counterpart but allows for the arguments to be converted + to a ``Cosmology`` and to compare flat cosmologies with their non-flat + equivalents. [#13104] + +- Cosmology equivalence for flat FLRW cosmologies has been generalized to apply + to all cosmologies using the FlatCosmology mixin. [#13261] + +- The cosmological redshift unit now has a physical type of ``"redshift"``. [#13561] + +astropy.io.ascii +^^^^^^^^^^^^^^^^ + +- Add ability to read and write a fixed width ASCII table that includes additional + header rows specifying any or all of the column dtype, unit, format, and + description. This is available in the ``fixed_width`` and + ``fixed_width_two_line`` formats via the new ``header_rows`` keyword argument. [#13734] + +astropy.io.fits +^^^^^^^^^^^^^^^ + +- Added support to the ``io.fits`` API for reading and writing file paths of the + form ``~/file.fits`` or ``~/file.fits``, referring to the home + directory of the current user or the specified user, respectively. [#13131] + +- Added support for opening remote and cloud-hosted FITS files using the + ``fsspec`` package, which has been added as an optional dependency. [#13238] + +astropy.io.votable +^^^^^^^^^^^^^^^^^^ + +- Added support in ``io.votable`` for reading and writing file paths of the form + ``~/file.xml`` or ``~/file.xml``, referring to the home directory of + the current user or the specified user, respectively. [#13149] + +astropy.modeling +^^^^^^^^^^^^^^^^ + +- Add option to non-linear fitters which enables automatic + exclusion of non-finite values from the fit data. [#13259] + +astropy.nddata +^^^^^^^^^^^^^^ + +- Modified ``Cutout2D`` to allow objects of type ``astropy.io.fits.Section`` + to be passed to the ``data`` parameter. [#13238] + +- Add a PSF image representation to ``astropy.nddata.NDData`` and ``astropy.nddata.CCDData``. [#13743] + +astropy.table +^^^^^^^^^^^^^ + +- An Astropy table can now be converted to a scalar NumPy object array. For NumPy + >= 1.20, a list of Astropy tables can be converted to an NumPy object array of + tables. [#13469] + +astropy.time +^^^^^^^^^^^^ + +- Added the ``astropy.time.Time.mean()`` method which also enables the ``numpy.mean()`` function to be used on instances of ``astropy.time.Time``. [#13508] + +- Improve the performance of getting the string representation of a large ``Time`` + or ``TimeDelta`` object. This is done via a new ``to_string()`` method that does + the time string format conversion only for the outputted values. Previously the + entire array was formatted in advance. [#13555] + +astropy.units +^^^^^^^^^^^^^ + +- It is now possible to use unit format names as string format specifiers for a + ``Quantity``, e.g. ``f'{1e12*u.m/u.s:latex_inline}'`` now produces the string + ``'$1 \\times 10^{12} \\; \\mathrm{m\\,s^{-1}}$'``. [#13050] + +- Ensure that the ``argmin`` and ``argmax`` methods of ``Quantity`` support the + ``keepdims`` argument when numpy does (numpy version 1.22 and later). [#13329] + +- ``numpy.lib.recfunctions.merge_arrays()`` is registered with numpy overload for + ``Quantity``. [#13669] + +- Added SI prefixes for quecto ("q", :math:`10^{-30}`), ronto ("r", + :math:`10^{-27}`), ronna ("R", :math:`10^{27}`), and quetta ("Q", + :math:`10^{30}`). [#14046] + +astropy.utils +^^^^^^^^^^^^^ + +- Added the ``use_fsspec``, ``fsspec_kwargs``, and ``close_files`` arguments + to ``utils.data.get_readable_fileobj``. [#13238] + +- Ensure that the ``argmin`` and ``argmax`` methods of ``Masked`` instances + support the ``keepdims`` argument when numpy does (numpy version 1.22 and + later). [#13329] + +astropy.visualization +^^^^^^^^^^^^^^^^^^^^^ + +- Add helper functions for WCSAxes instances to draw the instrument beam and a physical scale. [#12102] + +- Add a ``scatter_coord`` method to the ``wcsaxes`` functionality based on the + existing ``plot_coord`` method but that calls ``matplotlib.pyplot.scatter``. [#13562] + +- Added a ``sinh`` stretch option to ``simple_norm``. [#13746] + +- It is now possible to define "tickable" gridlines for the purpose of placing ticks or tick labels in the interior of WCSAxes plots. [#13829] + + +API Changes +----------- + +astropy.convolution +^^^^^^^^^^^^^^^^^^^ + +- Removed deprecated ``MexicanHat1DKernel`` and ``MexicanHat2DKernel`` + classes. Please use ``RickerWavelet1DKernel`` and + ``RickerWavelet2DKernel`` instead. [#13300] + +astropy.units +^^^^^^^^^^^^^ + +- Multiplying a ``LogQuantity`` like ``Magnitude`` with dimensionless physical + units by an array will no longer downcast to ``Quantity``. [#12579] + +- Quantity normally upcasts integer dtypes to floats, unless the dtype is + specifically provided. + Before this happened when ``dtype=None``; now the default has been changed to + ``dtype=numpy.inexact`` and ``dtype=None`` has the same meaning as in `numpy`. [#12941] + +- In "in-place unit changes" of the form ``quantity <<= new_unit``, the result + will now share memory with the original only if the conversion could be done + through a simple multiplication with a scale factor. Hence, memory will not be + shared if the quantity has integer ```dtype``` or is structured, or when the + conversion is through an equivalency. [#13638] + +- When ``Quantity`` is constructed from a structured array and ``unit`` is + ``None``, the default unit is now structured like the input data. [#13676] + +astropy.utils +^^^^^^^^^^^^^ + +- ``astropy.utils.misc.suppress`` has been removed, use ``contextlib.suppress`` + instead. ``astropy.utils.namedtuple_asdict`` has been removed, instead use + method ``._asdict`` on a ``namedtuple``. ``override__dir__`` has been deprecated + and will be removed in a future version, see the docstring for the better + alternative. [#13636] + +- ``astropy.utils.misc.possible_filename`` has been removed. [#13661] + +astropy.visualization +^^^^^^^^^^^^^^^^^^^^^ + +- Rename number-of-samples keyword ``nsamples`` in ``ZScaleInterval`` to align + with the ``n_samples`` keyword used in all other ``Interval`` classes in + this module. [#13810] + + +Bug Fixes +--------- + +astropy.convolution +^^^^^^^^^^^^^^^^^^^ + +- Fixed convolution Kernels to ensure the that returned kernels + are normalized to sum to one (e.g., ``Gaussian1DKernel``, + ``Gaussian2DKernel``). Also fixed the Kernel ``truncation`` calculation. [#13299] + +- Fix import error with setuptools v65.6.0 by replacing + ``numpy.ctypeslib.load_library`` with Cython to load the C convolution + extension. [#14035] + +astropy.coordinates +^^^^^^^^^^^^^^^^^^^ + +- ``BaseCoordinateFrame.get_frame_attr_names()`` had a misleading name, + because it actually provided a ``dict`` of attribute names and + their default values. It is now deprecated and replaced by ``BaseCoordinateFrame.get_frame_attr_defaults()``. + The fastest way to obtain the attribute names is ``BaseFrame.frame_attributes.keys()``. [#13484] + +- Fixed bug that caused ``earth_orientation.nutation_matrix()`` to error instead of returning output. [#13572] + +- Ensure that ``angle.to_string()`` continues to work after pickling, + and that units passed on to ``to_string()`` or the ``Angle`` + initializer can be composite units (like ``u.hour**1``), which might + result from preceding calculations. [#13933] + +astropy.io.fits +^^^^^^^^^^^^^^^ + +- ``report_diff_values()`` have now two new parameters ``rtol`` and ``atol`` to make the + report consistent with ``numpy.allclose`` results. + This fixes ``FITSDiff`` with multi-dimensional columns. [#13465] + +astropy.io.votable +^^^^^^^^^^^^^^^^^^ + +- Fixed two bugs in validator.validator.make_validation_report: + - ProgressBar iterator was not called correctly. + - make_validation_report now handles input string urls correctly. [#14102] + +astropy.timeseries +^^^^^^^^^^^^^^^^^^ + +- Fixed a performance regression in ``timeseries.aggregate_downsample`` + introduced in Astropy 5.0 / #11266. [#13069] + +astropy.units +^^^^^^^^^^^^^ + +- Unit changes of the form ``quantity <<= new_unit`` will now work also if the + quantity is integer. The result will always be float. This means that the result + will not share memory with the original. [#13638] + +- Ensure dimensionless quantities can be added inplace to regular ndarray. [#13913] + +astropy.utils +^^^^^^^^^^^^^ + +- Fixed an incompatibility with latest Python 3.1x versions that kept + ``astropy.utils.data.download_file`` from switching to TLS+FTP mode. [#14092] + +- ``np.quantile`` and ``np.percentile`` can now be used on ``Masked`` + arrays and quantities also with ``keepdims=True``. [#14113] + +astropy.visualization +^^^^^^^^^^^^^^^^^^^^^ + +- Significantly improve performance of ``ManualInterval`` when both limits + are specified manually. [#13898] + + +Other Changes and Additions +--------------------------- + +- The deprecated private ``astropy._erfa`` module has been removed. Use + ``pyerfa``, which is a dependency of ``astropy`` and can be imported directly + using ``import erfa``. [#13317] + +- The minimum version required for numpy is now 1.20 and that for scipy 1.5. [#13885] + +- Updated the bundled CFITSIO library to 4.2.0. [#14020] + + +Version 5.1.1 (2022-10-23) +========================== + +API Changes +----------- + +astropy.wcs +^^^^^^^^^^^ + +- The ``pixel`` argument to ``astropy.visualization.wcsaxes.ticklabels.TickLabels.add`` + no longer does anything, is deprecated, and will be removed in a future + astropy version. It has been replaced by a new required ``data`` argument, which + should be used to specify the data coordinates of the tick label being added. + + This changes has been made because it is (in general) not possible to correctly + calculate pixel coordinates before Matplotlib is drawing a figure. [#12630] + + +Bug Fixes +--------- + +astropy.coordinates +^^^^^^^^^^^^^^^^^^^ + +- Fixed a bug that prevented ``SkyOffsetFrame`` instances to be pickled by adding + a custom ``__reduce__`` method to the class (see issue #9249). [#13305] + +- Fixed the check for invalid ``Latitude`` values for float32 values. + ``Latitude`` now accepts the float32 value of pi/2, which was rejected + before because a comparison was made using the slightly smaller float64 representation. + See issue #13708. [#13745] + +astropy.io.ascii +^^^^^^^^^^^^^^^^ + +- Fixed confusing chained exception messages of ``read()`` function when it fails. [#13170] + +- When writing out a :class:`~astropy.table.Table` to HTML format, the + ``formats`` keyword argument to the :meth:`~astropy.table.Table.write` method + will now be applied. [#13453] + +astropy.io.fits +^^^^^^^^^^^^^^^ + +- ``heapsize`` is now checked for VLA tables. An error is thrown whether P format is used + but the heap size is bigger than what can be indexed with a 32 bit signed int. [#13429] + +- Fix parsing of ascii TFORM when precision is missing. [#13520] + +- A compressed image HDU created from the header of a PRIMARY HDU, now correctly updates + 'XTENSION' and 'SIMPLE' keywords. [#13557] + +- Empty variable-length arrays are now properly handled when pathological combinations of + heapoffset and heapsize are encountered. [#13621] + +- ``PCOUNT`` and ``GCOUNT`` keywords are now removed from an uncompressed Primary header, + for compliance with ``fitsverify`` behavior. [#13753] + +astropy.modeling +^^^^^^^^^^^^^^^^ + +- Bugfix for using ``MagUnit`` units on model parameters. [#13158] + +- Fix bug in using non-linear fitters to fit 0-degree polynomials using weights. [#13628] + +astropy.table +^^^^^^^^^^^^^ + +- Fix a problem where accessing one field of a structured column returned a Column + with the same info as the original column. This resulted in unintuitive behavior + in general and an exception if the format for the column was set. [#13269] + +- Tables with columns with structured data can now be properly stacked and joined. [#13306] + +- Update jQuery to 3.6.0, to pick up security fixes. [#13438] + +- Fix a Python 3.11 compatibility issue. Ensure that when removing a table column + that the ``pprint_include_names`` or ``pprint_exclude_names`` attributes get + updated correctly. [#13639] + +- When using ``add_columns`` with same indexes in ``indexes`` option or without + specifying the option, the order of the new columns will now be kept. [#13783] + +- Fix a bug when printing or getting the representation of a multidimensional + table column that has a zero dimension. [#13838] + +- Ensure that mixin columns and their ``info`` are not shared between tables + even when their underlying data is shared with ``copy=False``. [#13842] + +astropy.time +^^^^^^^^^^^^ + +- Fix ``Time.insert()`` on times which have their ``out_subfmt`` set. [#12732] + +- Prevent ``Time()`` from being initialized with an invalid precision + leading to incorrect results when representing the time as a string. [#13068] + +- Fix a bug in Time where a date string like "2022-08-01.123" was being parsed + as an ISO-format time "2022-08-01 00:00:00.123". The fractional part at the + end of the string was being taken as seconds. Now this raises an exception + because the string is not in ISO format. [#13731] + +astropy.units +^^^^^^^^^^^^^ + +- Significantly improved the performance of parsing composite units with the FITS + format, by ensuring the ``detailed_exception`` argument is properly passed on + and thus used. [#12699] + +- Ensure that ``np.concatenate`` on quantities can take a ``dtype`` argument (added in numpy 1.20). [#13323] + +- Ensure that the units of any ``initial`` argument to reductions such as + ``np.add.reduce`` (which underlies ``np.sum``) are properly taken into account. [#13340] + +astropy.utils +^^^^^^^^^^^^^ + +- Ensure that ``np.concatenate`` on masked data can take a ``dtype`` argument (added in numpy 1.20). [#13323] + +- Fix error when suppressing download progress bar while using non-default + ``sys.stdout`` stream. [#13352] + +- Ensure ``str`` and ``repr`` work properly for ``Masked`` versions of + structured subarrays. [#13404] + +- If an attribute is created using ``deprecated_attribute()`` with the + ``alternative`` argument then getting or setting the value of the deprecated + attribute now accesses its replacement. [#13824] + +astropy.visualization +^^^^^^^^^^^^^^^^^^^^^ + +- Fixed calling ``.tight_layout()`` on a WCSAxes. [#12418] + +astropy.wcs +^^^^^^^^^^^ + +- ``WCS.pixel_to_world`` now creates an ``EarthLocation`` object using ``MJD-AVG`` + if present before falling back to the old behaviour of using ``MJD-OBS``. [#12598] + +- The locations of ``WCSAxes`` ticks and tick-labels are now correctly calculated + when the DPI of a figure changes between a WCSAxes being created and the figure + being drawn, or when a rasterized artist is added to the WCSAxes. [#12630] + +- Fix a bug where ``SlicedLowLevelWCS.world_to_pixel_values`` would break when + the result of the transform is dependent on the coordinate of a sliced out + pixel. [#13579] + +- Updated bundled WCSLIB version to 7.12. This update includes bug fixes to + ``wcssub()`` in how it handles temporal axes with -TAB and fixes handling + of status returns from ``linp2x()`` and ``linx2p()`` relating to distortion + functions, in particular affecting TPV distortions - see #13509. For a full + list of changes - see http://www.atnf.csiro.au/people/mcalabre/WCS/CHANGES or + `astropy/cextern/wcslib/CHANGES `_. [#13635] + +- Fixed WCS validation not working properly if HDUList is needed + for multi-extension FITS file. [#13668] + + +Other Changes and Additions +--------------------------- + +- Development wheels of astropy should now be installed from + https://pypi.anaconda.org/astropy/simple instead of from + https://pkgs.dev.azure.com/astropy-project/astropy/_packaging/nightly/pypi/simple. [#13431] + +- Compatibility with Python 3.11, 3.10.7, 3.9.14, 3.8.14 [#13614] + + +Version 5.1 (2022-05-24) +======================== + +New Features +------------ + +astropy.coordinates +^^^^^^^^^^^^^^^^^^^ + +- The ephemeris used in ``astropy.coordinates`` can now be set to any version of + the JPL ephemeris available from https://naif.jpl.nasa.gov/pub/naif/generic_kernels/spk/planets/. [#12541] + +- ``Angle.to_string()`` now accepts the ``'latex_inline'`` unit format. [#13056] + +astropy.cosmology +^^^^^^^^^^^^^^^^^ + +- Cosmology instance can be parsed from or converted to a YAML string using + the new "yaml" format in Cosmology's ``to/from_format`` I/O. [#12279] + +- Register "astropy.row" into Cosmology's to/from format I/O, allowing a + Cosmology instance to be parse from or converted to an Astropy Table Row. [#12313] + +- A method ``clone`` has been added to ``Parameter`` to quickly deep copy the + object and change any constructor argument. + A supporting equality method is added, and ``repr`` is enhanced to be able to + roundtrip -- ``eval(repr(Parameter()))`` -- if the Parameter's arguments can + similarly roundtrip. + Parameter's arguments are made keyword-only. [#12479] + +- Add methods ``Otot`` and ``Otot0`` to FLRW cosmologies to calculate the total + energy density of the Universe. [#12590] + +- Add property ``is_flat`` to cosmologies to calculate the curvature of the Universe. + ``Cosmology`` is now an abstract class and subclasses must override the + abstract property ``is_flat``. [#12606] + +- For converting a cosmology to a mapping, two new boolean keyword arguments are + added: ``cosmology_as_str`` for turning the class reference to a string, + instead of the class object itself, and ``move_from_meta`` to merge the + metadata with the rest of the returned mapping instead of adding it as a + nested dictionary. [#12710] + +- Register format "astropy.cosmology" with Cosmology I/O. [#12736] + +- Cosmological equivalency (``Cosmology.is_equivalent``) can now be extended + to any Python object that can be converted to a Cosmology, using the new + keyword argument ``format``. + This allows e.g. a properly formatted Table to be equivalent to a Cosmology. [#12740] + +- The new module ``cosmology/tests/helper.py`` has been added to provide tools + for testing the cosmology module and related extensions. [#12966] + +- A new property ``nonflat`` has been added to flat cosmologies + (``FlatCosmologyMixin`` subclasses) to get an equivalent cosmology, but of the + corresponding non-flat class. [#13076] + +- ``clone`` has been enhanced to allow for flat cosmologies to clone on the + equivalent non-flat cosmology. [#13099] + +- ``cosmology`` file I/O uses the Unified Table I/O interface, which has added + support for reading and writing file paths of the form ``~/file.ecsv`` or + ``~/file.ecsv``, referring to the home directory of the current user + or the specified user, respectively. [#13129] + +astropy.io.ascii +^^^^^^^^^^^^^^^^ + +- Simplify the way that the ``converters`` argument of ``io.ascii.read`` is + provided. Previously this required wrapping each data type as the tuple returned + by the ``io.ascii.convert_numpy()`` function and ensuring that the value is a + ``list``. With this update you can write ``converters={'col1': bool}`` to force + conversion as a ``bool`` instead of the previous syntax ``converters={'col1': + [io.ascii.convert_numpy(bool)]}``. Note that this update is back-compatible with + the old behavior. [#13073] + +- Added support in ``io.ascii`` for reading and writing file paths of the form + ``~/file.csv`` or ``~/file.csv``, referring to the home directory of + the current user or the specified user, respectively. [#13130] + +astropy.io.fits +^^^^^^^^^^^^^^^ + +- Add option ``unit_parse_strict`` to ``astropy.io.fits.connect.read_table_fits`` + to enable warnings or errors about invalid FITS units when using ``astropy.table.Table.read``. + The default for this new option is ``"warn"``, which means warnings are now raised for + columns with invalid units. [#11843] + +- Changes default FITS behavior to use buffered I/O + rather than unbuffered I/O for performance reasons. [#12081] + +- ``astropy.io.fits.Header`` now has a method to calculate the size + (in bytes) of the data portion (with or without padding) following + that header. [#12110] + +astropy.io.misc +^^^^^^^^^^^^^^^ + +- Allow serialization of model unit equivalencies. [#10198] + +- Built-in Cosmology subclasses can now be converted to/from YAML with the + functions ``dump`` and ``load`` in ``astropy.io.misc.yaml``. [#12279] + +- Add asdf support for ``Cosine1D``, ``Tangent1D``, ``ArcSine1D``, + ``ArcCosine1D``, and ``ArcTangent1D`` models. [#12895] + +- Add asdf support for ``Spline1D`` models. [#12897] + +astropy.io.registry +^^^^^^^^^^^^^^^^^^^ + +- Added support to the Unified Table I/O interface for reading and writing file + paths of the form ``~/file.csv`` or ``~/file.csv``, referring to the + home directory of the current user or the specified user, respectively. [#13129] + +astropy.modeling +^^^^^^^^^^^^^^^^ + +- Add new fitters based on ``scipy.optimize.least_squares`` method of non-linear + least-squares optimization: [#12051] + + - ``TRFLSQFitter`` using the Trust Region Reflective algorithm. + - ``LMLSQFitter`` using the Levenberg-Marquardt algorithm (implemented by ``scipy.optimize.least_squares``). + - ``DogBoxLSQFitter`` using the dogleg algorithm. + +- Enable direct use of the ``ignored`` feature of ``ModelBoundingBox`` by users in + addition to its use as part of enabling ``CompoundBoundingBox``. [#12384] + +- Switch ``modeling.projections`` to use ``astropy.wcs.Prjprm`` wrapper internally and provide access to the ``astropy.wcs.Prjprm`` structure. [#12558] + +- Add error to non-finite inputs to the ``LevMarLSQFitter``, to protect against soft scipy failure. [#12811] + +- Allow the ``Ellipse2D`` and ``Sersic2D`` theta parameter to be input as + an angular quantity. [#13030] + +- Added ``Schechter1D`` model. [#13116] + +astropy.nddata +^^^^^^^^^^^^^^ + +- Add support for converting between uncertainty types. This uncertainty + conversion system uses a similar flow to the coordinate subsystem, where + Cartesian is used as the common system. In this case, variance is used as the + common system. [#12057] + +- The ``as_image_hdu`` option is now available for ``CCDData.to_hdu`` and + ``CCDData.write``. This option allows the user to get an ``ImageHDU`` as the + first item of the returned ``HDUList``, instead of the default ``PrimaryHDU``. [#12962] + +- File I/O through ``nddata.CCDData`` uses the Unified I/O interface, which has + added support for reading and writing file paths of the form ``~/file.csv`` or + ``~/file.csv``, referring to the home directory of the current user + or the specified user, respectively. [#13129] + +astropy.table +^^^^^^^^^^^^^ + +- A new keyword-only argument ``kind`` was added to the ``Table.sort`` method to + specify the sort algorithm. [#12637] + +- Columns which are ``numpy`` structured arrays are now fully supported, + effectively allowing tables within tables. This applies to ``Column``, + ``MaskedColumn``, and ``Quantity`` columns. These structured data columns + can be stored in ECSV, FITS, and HDF5 formats. [#12644] + +- Improve the performance of ``np.searchsorted`` by a factor of 1000 for a + bytes-type ``Column`` when the search value is ``str`` or an array of ``str``. + This happens commonly for string data stored in FITS or HDF5 format files. [#12680] + +- Add support for using mixin columns in group aggregation operations when the + mixin supports the specified operation (e.g. ``np.sum`` works for ``Quantity`` + but not ``Time``). In cases where the operation is not supported the code now + issues a warning and drops the column instead of raising an exception. [#12825] + +- Added support to the Unified Table I/O interface for reading and writing file + paths of the form ``~/file.csv`` or ``~/file.csv``, referring to the + home directory of the current user or the specified user, respectively. [#13129] + +astropy.time +^^^^^^^^^^^^ + +- Add support for calling ``numpy.linspace()`` with two ``Time`` instances to + generate a or multiple linearly spaced set(s) of times. [#13132] + +astropy.units +^^^^^^^^^^^^^ + +- ``structured_to_unstructured`` and ``unstructured_to_structured`` in + ``numpy.lib.recfunctions`` now work with Quantity. [#12486] + +- Implement multiplication and division of LogQuantities and numbers [#12566] + +- New ``doppler_redshift`` equivalency to convert between + Doppler redshift and radial velocity. [#12709] + +- Added the ``where`` keyword argument to the ``mean()``,``var()``, ``std()`` and ``nansum()`` methods of + ``astropy.units.Quantity``. Also added the ``initial`` keyword argument to ``astropy.units.Quantity.nansum()``. [#12891] + +- Added "Maxwell" as a unit for magnetic flux to the CGS module. [#12975] + +- ``Quantity.to_string()`` and ``FunctionUnitBase.to_string()`` now accept the + ``'latex_inline'`` unit format. The output of ``StructuredUnit.to_string()`` + when called with ``format='latex_inline'`` is now more consistent with the + output when called with ``format='latex'``. [#13056] + +astropy.utils +^^^^^^^^^^^^^ + +- Added the ``where`` keyword argument to the ``mean()``, ``var()``, ``std()``, ``any()``, and ``all()`` methods of + ``astropy.utils.masked.MaskedNDArray``. [#12891] + +- Improve handling of unavailable IERS-A (predictive future Earth rotation) data + in two ways. First, allow conversions with degraded accuracy if the IERS-A data + are missing or do not cover the required time span. This is done with a new + config item ``conf.iers_degraded_accuracy`` which specifies the behavior when + times are outside the range of IERS table. The options are 'error' (raise an + ``IERSRangeError``, default), 'warn' (issue a ``IERSDegradedAccuracyWarning``) + or 'ignore' (ignore the problem). Second, the logic for auto-downloads was + changed to guarantee that no matter what happens with the IERS download + operations, only warnings will be issued. [#13052] + +astropy.wcs +^^^^^^^^^^^ + +- ``astropy.wcs.Celprm`` and ``astropy.wcs.Prjprm`` have been added + to allow access to lower level WCSLIB functionality and to allow direct + access to the ``cel`` and ``prj`` members of ``Wcsprm``. [#12514] + +- Add ``temporal`` properties for convenient access of/selection of/testing for + the ``TIME`` axis introduced in WCSLIB version 7.8. [#13094] + +API Changes +----------- + +astropy.coordinates +^^^^^^^^^^^^^^^^^^^ + +- The ``dms_to_degrees`` and ``hms_to_hours`` functions (and implicitly + tuple-based initialization of ``Angle``) is now deprecated, as it was + difficult to be sure about the intent of the user for signed values of + the degrees/hours, minutes, and seconds. [#13162] + +astropy.cosmology +^^^^^^^^^^^^^^^^^ + +- The already deprecated ``Planck18_arXiv_v2`` has been removed. + Use ``Planck18`` instead [#12354] + +- ``default_cosmology.get_cosmology_from_string`` is deprecated and will be + removed in two minor versions. + Use ``getattr(astropy.cosmology, )`` instead. [#12375] + +- In I/O, conversions of Parameters move more relevant information from the + Parameter to the Column. + The default Parameter ``format_spec`` is changed from ``".3g"`` to ``""``. [#12612] + +- Units of redshift are added to ``z_reion`` in built-in realizations' metadata. [#12624] + +- Cosmology realizations (e.g. ``Planck18``) and parameter dictionaries are now + lazily loaded from source files. [#12746] + +- The Cosmology Parameter argument "fmt" for specifying a format spec + has been deprecated in favor of using the built-in string representation from + the Parameter's value's dtype. [#13072] + +astropy.io.ascii +^^^^^^^^^^^^^^^^ + +- When reading an ECSV file, changed the type checking + to issue an ``InvalidEcsvDatatypeWarning`` instead of raising a ``ValueError`` + exception if the ``datatype`` of a column is not recognized in the ECSV standard. + This also applies to older versions of ECSV files which used to silently + proceed but now warn first. [#12841] + +astropy.io.fits +^^^^^^^^^^^^^^^ + +- Removed deprecated ``clobber`` argument from functions in ``astropy.io.fits``. [#12258] + +- Add ``-s/--sort`` argument to ``fitsheader`` to sort the fitsort-mode output. [#13106] + +astropy.io.misc +^^^^^^^^^^^^^^^ + +- Deprecate asdf in astropy core in favor of the asdf-astropy package. [#12903, #12930] + +astropy.modeling +^^^^^^^^^^^^^^^^ + +- Made ``astropy.modeling.fitting._fitter_to_model_params`` and ``astropy.modeling.fitting._model_to_fit_params`` + public methods. [#12585] + +astropy.table +^^^^^^^^^^^^^ + +- Change the repr of the Table object to replace embedded newlines and tabs with + ``r'\n'`` and ``r'\t'`` respectively. This improves the display of such tables. [#12631] + +- A new keyword-only argument ``kind`` was added to the ``Table.sort`` method to + specify the sort algorithm. The signature of ``Table.sort`` was modified so that + the ``reverse`` argument is now keyword-only. Previously ``reverse`` could be + specified as the second positional argument. [#12637] + +- Changed behavior when a structured ``numpy.ndarray`` is added as a column to a + ``Table``. Previously this was converted to a ``NdarrayMixin`` subclass of + ``ndarray`` and added as a mixin column. This was because saving as a file (e.g. + HDF5, FITS, ECSV) was not supported for structured array columns. Now a + structured ``numpy.ndarray`` is added to the table as a native ``Column`` and + saving to file is supported. [#13236] + +astropy.tests +^^^^^^^^^^^^^ + +- Backward-compatible import of ``astropy.tests.disable_internet`` + has been removed; use ``pytest_remotedata.disable_internet`` + from ``pytest-remotedata`` instead. [#12633] + +- Backward-compatible import of ``astropy.tests.helper.remote_data`` + has been removed; use ``pytest.mark.remote_data`` from ``pytest-remotedata`` + instead. [#12633] + +- The following are deprecated and will be removed in a future release. + Use ``pytest`` warning and exception handling instead: [#12633] + + * ``astropy.io.ascii.tests.common.raises`` + * ``astropy.tests.helper.catch_warnings`` + * ``astropy.tests.helper.ignore_warnings`` + * ``astropy.tests.helper.raises`` + * ``astropy.tests.helper.enable_deprecations_as_exceptions`` + * ``astropy.tests.helper.treat_deprecations_as_exceptions`` + +- Backward-compatible plugin ``astropy.tests.plugins.display`` + has been removed; use ``pytest-astropy-header`` instead. [#12633] + +astropy.time +^^^^^^^^^^^^ + +- Creating an `~astropy.time.TimeDelta` object with numerical inputs + that do not have a unit and without specifying an explicit format, + for example ``TimeDelta(5)``, + now results in a `~astropy.time.TimeDeltaMissingUnitWarning`. + This also affects statements like ``Time("2020-01-01") + 5`` or + ``Time("2020-01-05") - Time("2020-01-03") < 5``, which implicitly + transform the right-hand side into an `~astropy.time.TimeDelta` instance. [#12888] + +Bug Fixes +--------- + +astropy.coordinates +^^^^^^^^^^^^^^^^^^^ + +- The machinery that makes observatory locations available as ``EarthLocation`` + objects is now smarter about processing observatory names from its data files. + More names are available for use and the empty string is no longer considered + to be a valid name. [#12721] + +astropy.io.ascii +^^^^^^^^^^^^^^^^ + +- Fixed ``io.ascii`` read and write functions for most formats to correctly handle + data fields with embedded newlines for both the fast and pure-Python readers and + writers. [#12631] + +- Fix an issue when writing ``Time`` table columns to a file when the time + ``format`` is one of ``datetime``, ``datetime64``, or ``ymdhms``. Previously, + writing a ``Time`` column with one of these formats could result in an exception + or else an incorrect output file that cannot be read back in. [#12842] + +astropy.io.fits +^^^^^^^^^^^^^^^ + +- Add a ``mask_invalid`` option to ``Table.read`` to allow deactivating the + masking of NaNs in float columns and empty strings in string columns. This + option is necessary to allow effective use of memory-mapped reading with + ``memmap=True``. [#12544] + +- Fix ``CompImageHeader.clear()``. [#13102] + +astropy.modeling +^^^^^^^^^^^^^^^^ + +- Bugfix for ``ignore`` functionality failing in ``ModelBoundingBox`` when using + ``ignore`` option alongside passing bounding box data as tuples. [#13032] + +astropy.table +^^^^^^^^^^^^^ + +- Fixed a bug in ``Table.show_in_browser`` using the ``jsviewer=True`` option + to display the table with sortable columns. Previously the sort direction arrows + were not being shown due to missing image files for the arrows. [#12716] + +- Fix an issue when writing ``Time`` table columns to a file when the time + ``format`` is one of ``datetime``, ``datetime64``, or ``ymdhms``. Previously, + writing a ``Time`` column with one of these formats could result in an exception + or else an incorrect output file that cannot be read back in. [#12842] + +- Fixed a bug where it is not possible to set the ``.info.format`` property of a + table structured column and get formatted output. [#13233] + +- Fixed a bug when adding a masked structured array to a table. Previously this + was auto-converted to a ``NdarrayMixin`` which loses the mask. With this fix + the data are added to the table as a ``MaskedColumn`` and the mask is preserved. [#13236] + +astropy.time +^^^^^^^^^^^^ + +- Fix an issue when writing ``Time`` table columns to a file when the time + ``format`` is one of ``datetime``, ``datetime64``, or ``ymdhms``. Previously, + writing a ``Time`` column with one of these formats could result in an exception + or else an incorrect output file that cannot be read back in. [#12842] + +astropy.utils +^^^^^^^^^^^^^ + +- Fixed a bug which caused ``numpy.interp`` to produce incorrect + results when ``Masked`` arrays were passed. [#12978] + +- Fixed HAS_YAML not working as intended. [#13066] + +astropy.wcs +^^^^^^^^^^^ + +- Convert ``NoConvergence`` errors to warnings in ``world_to_pixel_values`` so that callers can work at least with the non converged solution. [#11693] + +- Expose the ability to select TIME axis introduced in WCSLIB version 7.8. [#13062] + +- Do not call ``wcstab`` on ``wcscopy`` and copy ``wtb`` members from the original WCS. [#13063] + +- Updated bundled WCSLIB version to 7.11. This update together with 7.10 + includes bug fixes to ``tabini()`` and ``tabcpy()`` as well as several + print formatting enhancements. For a full list of + changes - see http://www.atnf.csiro.au/people/mcalabre/WCS/CHANGES [#13171] + +- Fixed error that occurred in ``WCS.world_to_pixel`` for ``WCS`` objects with a + spectral axis and observer location information when passing a ``SpectralCoord`` + that had missing observer or target information. [#13228] + +Version 5.0.4 (2022-03-31) +========================== + +Bug Fixes +--------- + +astropy.modeling +^^^^^^^^^^^^^^^^ + +- Fixed the ``Gaussian2D`` ``bounding_box`` when ``theta`` is an angular + ``Quantity``. [#13021] + +astropy.utils +^^^^^^^^^^^^^ + +- Reverted ``astropy.utils.iers.iers.IERS_A_URL`` to ``maia.usno.navy.mil`` domain instead + of NASA FTP to work around server issues. [#13004] + +Other Changes and Additions +--------------------------- + +- Updated bundled WCSLIB to version 7.9 with several bugfixes and added + support for time coordinate axes in ``wcsset()`` and ``wcssub()``. The + four-digit type code for the time axis will have the first digit set to 4, + i.e., four digit code will be 4xxx where x is a digit 0-9. For a full list of + bug fixes see https://www.atnf.csiro.au/people/mcalabre/WCS/CHANGES [#12994] + + +Version 5.0.3 (2022-03-25) +========================== + +Bug Fixes +--------- + +astropy.convolution +^^^^^^^^^^^^^^^^^^^ + +- Bugfix in ``astropy.convolution.utils.discretize_model`` which allows the function to handle a ``CompoundModel``. + Before this fix, ``discretize_model`` was confusing ``CompoundModel`` with a callable function. [#12959] + +astropy.io.fits +^^^^^^^^^^^^^^^ + +- Fix write and read FITS tables with multidimensional items, using ``from_columns`` + without previously defined ``ColDefs`` structure. [#12863] + +astropy.io.votable +^^^^^^^^^^^^^^^^^^ + +- Fix VOTable linting to avoid use of shell option. [#12985] + +astropy.utils +^^^^^^^^^^^^^ + +- Fix XML linting to avoid use of shell option. [#12985] + +Other Changes and Additions +--------------------------- + +- Updated the bundled CFITSIO library to 4.1.0. [#12967] + +Version 5.0.2 (2022-03-10) +========================== + +Bug Fixes +--------- + +astropy.io.ascii +^^^^^^^^^^^^^^^^ + +- Bugfix to add backwards compatibility for reading ECSV version 0.9 files with + non-standard column datatypes (such as ``object``, ``str``, ``datetime64``, + etc.), which would raise a ValueError in ECSV version 1.0. [#12880] + +astropy.io.misc +^^^^^^^^^^^^^^^ + +- Bugfix for ``units_mapping`` schema's property name conflicts. Changes: + * ``inputs`` to ``unit_inputs`` + * ``outputs`` to ``unit_outputs`` [#12800] + +astropy.io.votable +^^^^^^^^^^^^^^^^^^ + +- Fixed a bug where ``astropy.io.votable.validate`` was printing output to + ``sys.stdout`` when the ``output`` parameter was set to ``None``. ``validate`` + now returns a string when ``output`` is set to ``None``, as documented. + [#12604] + +astropy.modeling +^^^^^^^^^^^^^^^^ + +- Fix handling of units on ``scale`` parameter in BlackBody model. [#12318] + +- Indexing on models can now be used with all types of integers + (like ``numpy.int64``) instead of just ``int``. [#12561] + +- Fix computation of the separability of a ``CompoundModel`` where another + ``CompoundModel`` is on the right hand side of the ``&`` operator. [#12907] + +- Provide a hook (``Model._calculate_separability_matrix``) to allow subclasses + of ``Model`` to define how to compute their separability matrix. [#12900] + +astropy.stats +^^^^^^^^^^^^^ + +- Fixed a bug in which running ``kuiper_false_positive_probability(D,N)`` on + distributions with many data points could produce NaN values for the false + positive probability of the Kuiper statistic. [#12896] + +astropy.wcs +^^^^^^^^^^^ + +- Fixed a bug due to which ``naxis``, ``pixel_shape``, and + ``pixel_bounds`` attributes of ``astropy.wcs.WCS`` were not restored when + an ``astropy.wcs.WCS`` object was unpickled. This fix also eliminates + ``FITSFixedWarning`` warning issued during unpiclikng of the WCS objects + related to the number of axes. This fix also eliminates errors when + unpickling WCS objects originally created using non-default values for + ``key``, ``colsel``, and ``keysel`` parameters. [#12844] + +Version 5.0.1 (2022-01-26) +========================== + +Bug Fixes +--------- + +astropy.coordinates +^^^^^^^^^^^^^^^^^^^ + +- Trying to create an instance of ``astropy.coordinates.Distance`` by providing + both ``z`` and ``parallax`` now raises the expected ``ValueError``. [#12531] + +- Fixed a bug where changing the wrap angle of the longitude component of a + representation could raise a warning or error in certain situations. [#12556] + +- ``astropy.coordinates.Distance`` constructor no longer ignores the ``unit`` + keyword when ``parallax`` is provided. [#12569] + +astropy.cosmology +^^^^^^^^^^^^^^^^^ + +- ``astropy.cosmology.utils.aszarr`` can now convert ``Column`` objects. [#12525] + +- Reading a cosmology from an ECSV will load redshift and Hubble parameter units + from the cosmology units module. [#12636] + +astropy.io.fits +^^^^^^^^^^^^^^^ + +- Fix formatting issue in ``_dump_coldefs`` and add tests for ``tabledump`` and + ``tableload`` convenience functions. [#12526] + +astropy.io.misc +^^^^^^^^^^^^^^^ + +- YAML can now also represent quantities and arrays with structured dtype, + as well as structured scalars based on ``np.void``. [#12509] + +astropy.modeling +^^^^^^^^^^^^^^^^ + +- Fixes error when fitting multiplication or division based compound models + where the sub-models have different output units. [#12475] + +- Bugfix for incorrectly initialized and filled ``parameters`` data for ``Spline1D`` model. [#12523] + +- Bugfix for ``keyerror`` thrown by ``Model.input_units_equivalencies`` when + used on ``fix_inputs`` models which have no set unit equivalencies. [#12597] + +astropy.table +^^^^^^^^^^^^^ + +- ``astropy.table.Table.keep_columns()`` and + ``astropy.table.Table.remove_columns()`` now work with generators of column + names. [#12529] + +- Avoid duplicate storage of info in serialized columns if the column + used to serialize already can hold that information. [#12607] + +astropy.timeseries +^^^^^^^^^^^^^^^^^^ + +- Fixed edge case bugs which emerged when using ``aggregate_downsample`` with custom bins. [#12527] + +astropy.units +^^^^^^^^^^^^^ + +- Structured units can be serialized to/from yaml. [#12492] + +- Fix bad typing problems by removing interaction with ``NDArray.__class_getitem__``. [#12511] + +- Ensure that ``Quantity.to_string(format='latex')`` properly typesets exponents + also when ``u.quantity.conf.latex_array_threshold = -1`` (i.e., when the threshold + is taken from numpy). [#12573] + +- Structured units can now be copied with ``copy.copy`` and ``copy.deepcopy`` + and also pickled and unpicked also for ``protocol`` >= 2. + This does not work for big-endian architecture with older ``numpy<1.21.1``. [#12583] + +astropy.utils +^^^^^^^^^^^^^ + +- Ensure that a ``Masked`` instance can be used to initialize (or viewed + as) a ``numpy.ma.Maskedarray``. [#12482] + +- Ensure ``Masked`` also works with numpy >=1.22, which has a keyword argument + name change for ``np.quantile``. [#12511] + +- ``astropy.utils.iers.LeapSeconds.auto_open()`` no longer emits unnecessary + warnings when ``astropy.utils.iers.conf.auto_max_age`` is set to ``None``. [#12713] + + +Version 5.0 (2021-11-15) +======================== + + +New Features +------------ + +astropy.convolution +^^^^^^^^^^^^^^^^^^^ + +- Added dealiasing support to ``convolve_fft``. [#11495] + +astropy.coordinates +^^^^^^^^^^^^^^^^^^^ + +- Added missing coordinate transformations where the starting and ending frames + are the same (i.e., loopback transformations). [#10909] + +- Allow negation, multiplication and division also of representations that + include a differential (e.g., ``SphericalRepresentation`` with a + ``SphericalCosLatDifferential``). For all operations, the outcome is + equivalent to transforming the representation and differential to cartesian, + then operating on those, and transforming back to the original representation + (except for ``UnitSphericalRepresentation``, which will return a + ``SphericalRepresentation`` if there is a scale change). [#11470] + +- ``RadialRepresentation.transform`` can work with a multiplication matrix only. + All other matrices still raise an exception. [#11576] + +- ``transform`` methods are added to ``BaseDifferential`` and ``CartesianDifferential``. + All transform methods on Representations now delegate transforming differentials + to the differential objects. [#11654] + +- Adds new ``HADec`` built-in frame with transformations to/from ``ICRS`` and ``CIRS``. + This frame complements ``AltAz`` to give observed coordinates (hour angle and declination) + in the ``ITRS`` for an equatorially mounted telescope. [#11676] + +- ``SkyCoord`` objects now have a ``to_table()`` method, which allows them to be + converted to a ``QTable``. [#11743] + +astropy.cosmology +^^^^^^^^^^^^^^^^^ + +- Cosmologies now store metadata in a mutable parameter ``meta``. + The initialization arguments ``name`` and ``meta`` are keyword-only. [#11542] + +- A new unit, ``redshift``, is defined. It is a dimensionless unit to distinguish + redshift quantities from other non-redshift values. For compatibility with + dimensionless quantities the equivalency ``dimensionless_redshift`` is added. + This equivalency is enabled by default. [#11786] + +- Add equality operator for comparing Cosmology instances. Comparison is done on + all immutable fields (this excludes 'meta'). + + Now the following will work: + + .. code-block:: python + + >>> from astropy.cosmology import Planck13, Planck18 + >>> Planck13 == Planck18 + False + + >>> Planck18 == Planck18 + True [#11813] + +- Added ``read/write`` methods to Cosmology using the Unified I/O registry. + Now custom file format readers, writers, and format-identifier functions + can be registered to read, write, and identify, respectively, Cosmology + objects. Details are discussed in an addition to the docs. [#11948] + +- Added ``to_format/from_format`` methods to Cosmology using the Unified I/O + registry. Now custom format converters and format-identifier functions + can be registered to transform Cosmology objects. + The transformation between Cosmology and dictionaries is pre-registered. + Details are discussed in an addition to the docs. [#11998] + +- Added units module for defining and collecting cosmological units and + equivalencies. [#12092] + +- Flat cosmologies are now set by a mixin class, ``FlatCosmologyMixin`` and its + FLRW-specific subclass ``FlatFLRWMixin``. All ``FlatCosmologyMixin`` are flat, + but not all flat cosmologies are instances of ``FlatCosmologyMixin``. As + example, ``LambdaCDM`` **may** be flat (for the a specific set of parameter + values), but ``FlatLambdaCDM`` **will** be flat. + + Cosmology parameters are now descriptors. When accessed from a class they + transparently stores information, like the units and accepted equivalencies. + On a cosmology instance, the descriptor will return the parameter value. + Parameters can have custom ``getter`` methods. + + Cosmological equality is refactored to check Parameters (and the name) + A new method, ``is_equivalent``, is added to check Cosmology equivalence, so + a ``FlatLambdaCDM`` and flat ``LambdaCDM`` are equivalent. [#12136] + +- Replaced ``z = np.asarray(z)`` with ``z = u.Quantity(z, u.dimensionless_unscaled).value`` + in Cosmology methods. Input of values with incorrect units raises a UnitConversionError + or TypeError. [#12145] + +- Cosmology Parameters allow for custom value setters. + Values can be set once, but will error if set a second time. + If not specified, the default setter is used, which will assign units + using the Parameters ``units`` and ``equivalencies`` (if present). + Alternate setters may be registered with Parameter to be specified by a str, + not a decorator on the Cosmology. [#12190] + +- Cosmology instance conversion to dict now accepts keyword argument ``cls`` to + determine dict type, e.g. ``OrderedDict``. [#12209] + +- A new equivalency is added between redshift and the Hubble parameter and values + with units of little-h. + This equivalency is also available in the catch-all equivalency ``with_redshift``. [#12211] + +- A new equivalency is added between redshift and distance -- comoving, lookback, + and luminosity. This equivalency is also available in the catch-all equivalency + ``with_redshift``. [#12212] + +- Register Astropy Table into Cosmology's ``to/from_format`` I/O, allowing + a Cosmology instance to be parsed from or converted to a Table instance. + Also adds the ``__astropy_table__`` method allowing ``Table(cosmology)``. [#12213] + +- The WMAP1 and WMAP3 are accessible as builtin cosmologies. [#12248] + +- Register Astropy Model into Cosmology's ``to/from_format`` I/O, allowing + a Cosmology instance to be parsed from or converted to a Model instance. [#12269] + +- Register an ECSV reader and writer into Cosmology's I/O, allowing a Cosmology + instance to be read from from or written to an ECSV file. [#12321] + +astropy.io.ascii +^^^^^^^^^^^^^^^^ + +- Added new way to specify the dtype for tables that are read: ``converters`` + can specify column names with wildcards. [#11892] + +- Added a new ``astropy.io.ascii.Mrt`` class to write tables in the American + Astronomical Society Machine-Readable Table format, + including documentation and tests for the same. [#11897, #12301, #12302] + +- When writing, the input data are no longer copied, improving performance. + Metadata that might be changed, such as format and serialization + information, is copied, hence users can continue to count on no + changes being made to the input data. [#11919] + +astropy.io.misc +^^^^^^^^^^^^^^^ + +- Add Parquet serialization of Tables with pyarrow, including metadata support and + columnar access. [#12215] + +astropy.modeling +^^^^^^^^^^^^^^^^ + +- Added fittable spline models to ``modeling``. [#11634] + +- Extensive refactor of ``BoundingBox`` for better usability and maintainability. [#11930] + +- Added ``CompoundBoundingBox`` feature to ``~astropy.modeling``, which allows more flexibility in + defining bounding boxes for models that are applied to images with many slices. [#11942] + +- Improved parameter support for ``astropy.modeling.core.custom_model`` created models. [#11984] + +- Added the following trigonometric models and linked them to their appropriate inverse models: + * ``Cosine1D`` [#12158] + * ``Tangent1D`` + * ``ArcSine1D`` + * ``ArcCosine1D`` + * ``ArcTangent1D`` [#12185] + +astropy.table +^^^^^^^^^^^^^ + +- Added a new method ``Table.update()`` which does a dictionary-style update of a + ``Table`` by adding or replacing columns. [#11904] + +- Masked quantities are now fully supported in tables. This includes ``QTable`` + automatically converting ``MaskedColumn`` instances to ``MaskedQuantity``, + and ``Table`` doing the reverse. [#11914] + +- Added new keyword arguments ``keys_left`` and ``keys_right`` to the table ``join`` + function to support joining tables on key columns with different names. In + addition the new keywords can accept a list of column-like objects which are + used as the match keys. This allows joining on arbitrary data which are not part + of the tables being joined. [#11954] + +- Formatting of any numerical values in the output of ``Table.info()`` and + ``Column.info()`` has been improved. [#12022] + +- It is now possible to add dask arrays as columns in tables + and have them remain as dask arrays rather than be converted + to Numpy arrays. [#12219] + +- Added a new registry for mixin handlers, which can be used + to automatically convert array-like Python objects into + mixin columns when assigned to a table column. [#12219] + +astropy.time +^^^^^^^^^^^^ + +- Adds a new method ``earth_rotation_angle`` to calculate the Local Earth Rotation Angle. + Also adjusts Local Sidereal Time for the Terrestrial Intermediate Origin (``TIO``) + and adds a rigorous correction for polar motion. The ``TIO`` adjustment is approximately + 3 microseconds per century from ``J2000`` and the polar motion correction is at most + about +/-50 nanoseconds. For models ``IAU1982`` and ``IAU1994``, no such adjustments are + made as they pre-date the TIO concept. [#11680] + +astropy.timeseries +^^^^^^^^^^^^^^^^^^ + +- A custom binning scheme is now available in ``aggregate_downsample``. + It allows ``time_bin_start`` and ``time_bin_size`` to be arrays, and adds + an optional ``time_bin_end``. + This scheme mirrors the API for ``BinnedTimeSeries``. [#11266] + +astropy.units +^^^^^^^^^^^^^ + +- ``Quantity`` gains a ``__class_getitem__`` to create unit-aware annotations + with the syntax ``Quantity[unit or physical_type, shape, numpy.dtype]``. + If the python version is 3.9+ or ``typing_extensions`` is installed, + these are valid static type annotations. [#10662] + +- Each physical type is added to ``astropy.units.physical`` + (e.g., ``physical.length`` or ``physical.electrical_charge_ESU``). + The attribute-accessible names (underscored, without parenthesis) also + work with ``astropy.units.physical.get_physical_type``. [#11691] + +- It is now possible to have quantities based on structured arrays in + which the unit has matching structure, giving each field its own unit, + using units constructed like ``Unit('AU,AU/day')``. [#11775] + +- The milli- prefix has been added to ``astropy.units.Angstrom``. [#11788] + +- Added attributes ``base``, ``coords``, and ``index`` and method ``copy()`` to + ``QuantityIterator`` to match ``numpy.ndarray.flatiter``. [#11796] + +- Added "angular frequency" and "angular velocity" as aliases for the "angular + speed" physical type. [#11865] + +- Add light-second to units of length [#12128] + +astropy.utils +^^^^^^^^^^^^^ + +- The ``astropy.utils.deprecated_renamed_argument()`` decorator now supports + custom warning messages. [#12305] + +- The NaN-aware numpy functions such as ``np.nansum`` now work on Masked + arrays, with masked values being treated as NaN, but without raising + warnings or exceptions. [#12454] + +astropy.visualization +^^^^^^^^^^^^^^^^^^^^^ + +- Added a feature so that SphericalCircle will accept center parameter as a SkyCoord object. [#11790] + +astropy.wcs +^^^^^^^^^^^ + +- ``astropy.wcs.utils.obsgeo_to_frame`` has been added to convert the obsgeo coordinate + array on ``astropy.wcs.WCS`` objects to an ``ITRS`` coordinate frame instance. [#11716] + +- Updated bundled ``WCSLIB`` to version 7.7 with several bugfixes. [#12034] + + +API Changes +----------- + +astropy.config +^^^^^^^^^^^^^^ + +- ``update_default_config`` and ``ConfigurationMissingWarning`` are deprecated. [#11502] + +astropy.constants +^^^^^^^^^^^^^^^^^ + +- Removed deprecated ``astropy.constants.set_enabled_constants`` context manager. [#12105] + +astropy.coordinates +^^^^^^^^^^^^^^^^^^^ + +- Positions for the Moon using the 'builtin' ephemeris now use the new + ``erfa.moon98`` function instead of our own implementation of the Meeus + algorithm. As this also corrects a misunderstanding of the frame returned by + the Meeus, this improves the agreement with the JPL ephemeris from about 30 to + about 6 km rms. [#11753] + +- Removed deprecated ``representation`` attribute from + ``astropy.coordinates.BaseCoordinateFrame`` class. [#12257] + +- ``SpectralQuantity`` and ``SpectralCoord`` ``.to_value`` method can now be called without + ``unit`` argument in order to maintain a consistent interface with ``Quantity.to_value`` [#12440] + +astropy.cosmology +^^^^^^^^^^^^^^^^^ + +- ``z_at_value`` now works with arrays for all arguments (except ``func``, + ``verbose``, and ``method``). Consequently, ``coordinates.Distance.z`` can + be used when Distance is an array. [#11778] + +- Remove deprecation warning and error remapping in ``Cosmology.clone``. + Now unknown arguments will raise a ``TypeError``, not an ``AttributeError``. [#11785] + +- The ``read/write`` and ``to/from_format`` Unified I/O registries are separated + and apply only to ``Cosmology``. [#12015] + +- Cosmology parameters in ``cosmology.parameters.py`` now have units, + where applicable. [#12116] + +- The function ``astropy.cosmology.utils.inf_like()`` is deprecated. [#12175] + +- The function ``astropy.cosmology.utils.vectorize_if_needed()`` is deprecated. + A new function ``astropy.cosmology.utils.vectorize_redshift_method()`` is added + as replacement. [#12176] + +- Cosmology base class constructor now only accepts arguments ``name`` and ``meta``. + Subclasses should add relevant arguments and not pass them to the base class. [#12191] + +astropy.io +^^^^^^^^^^ + +- When ``astropy`` raises an ``OSError`` because a file it was told to write + already exists, the error message now always suggests the use of the + ``overwrite=True`` argument. The wording is now consistent for all I/O formats. [#12179] + +astropy.io.ascii +^^^^^^^^^^^^^^^^ + +- Removed deprecated ``overwrite=None`` option for + ``astropy.io.ascii.ui.write()``. Overwriting existing files now only happens if + ``overwrite=True``. [#12171] + +astropy.io.fits +^^^^^^^^^^^^^^^ + +- The internal class _CardAccessor is no longer registered as a subclass of + the Sequence or Mapping ABCs. [#11923] + +- The deprecated ``clobber`` argument will be removed from the + ``astropy.io.fits`` functions in version 5.1, and the deprecation warnings now + announce that too. [#12311] + +astropy.io.registry +^^^^^^^^^^^^^^^^^^^ + +- The ``write`` function now is allowed to return possible content results, which + means that custom writers could, for example, create and return an instance of + some container class rather than a file on disk. [#11916] + +- The registry functions are refactored into a class-based system. + New Read-only, write-only, and read/write registries can be created. + All functions accept a new argument ``registry``, which if not specified, + defaults to the global default registry. [#12015] + +astropy.io.votable +^^^^^^^^^^^^^^^^^^ + +- Deprecated the ``pedantic`` keyword argument in the + ``astropy.io.votable.table.parse`` function and the corresponding configuration + setting. It has been replaced by the ``verify`` option. [#12129] + +astropy.modeling +^^^^^^^^^^^^^^^^ + +- Refactored how ``astropy.modeling.Model`` handles model evaluation in order to better + organize the code. [#11931] + +- Removed the following deprecated modeling features: + ``astropy.modeling.utils.ExpressionTree`` class, + ``astropy.modeling.functional_models.MexicanHat1D`` model, + ``astropy.modeling.functional_models.MexicanHat2D`` model, + ``astropy.modeling.core.Model.inputs`` setting in model initialize, + ``astropy.modeling.core.CompoundModel.inverse`` setting in model initialize, and + ``astropy.modeling.core.CompoundModel.both_inverses_exist()`` method. [#11978] + +- Deprecated the ``AliasDict`` class in ``modeling.utils``. [#12411] + +astropy.nddata +^^^^^^^^^^^^^^ + +- Removed ``block_reduce`` and ``block_replicate`` functions from + ``nddata.utils``. These deprecated functions in ``nddata.utils`` were + moved to ``nddata.blocks``. [#12288] + +astropy.stats +^^^^^^^^^^^^^ + +- Removed the following deprecated features from ``astropy.stats``: + + * ``conf`` argument for ``funcs.binom_conf_interval()`` and + ``funcs.binned_binom_proportion()``, + * ``conflevel`` argument for ``funcs.poisson_conf_interval()``, and + * ``conf_lvl`` argument for ``jackknife.jackknife_stats()``. [#12200] + +astropy.table +^^^^^^^^^^^^^ + +- Printing a ``Table`` now shows the qualified class name of mixin columns in the + dtype header row instead of "object". This applies to all the ``Table`` formatted output + methods whenever ``show_dtype=True`` is selected. [#11660] + +- The 'overwrite' argument has been added to the jsviewer table writer. + Overwriting an existing file requires 'overwrite' to be True. [#11853] + +- The 'overwrite' argument has been added to the pandas table writers. + Overwriting an existing file requires 'overwrite' to be True. [#11854] + +- The table ``join`` function now accepts only the first four arguments ``left``, + ``right``, ``keys``, and ``join_type`` as positional arguments. All other + arguments must be supplied as keyword arguments. [#11954] + +- Adding a dask array to a Table will no longer convert + that dask to a Numpy array, so accessing t['dask_column'] + will now return a dask array instead of a Numpy array. [#12219] + +astropy.time +^^^^^^^^^^^^ + +- Along with the new method ``earth_rotation_angle``, ``sidereal_time`` now accepts + an ``EarthLocation`` as the ``longitude`` argument. [#11680] + +astropy.units +^^^^^^^^^^^^^ + +- Unit ``littleh`` and equivalency ``with_H0`` have been moved to the + ``cosmology`` module and are deprecated from ``astropy.units``. [#12092] + +astropy.utils +^^^^^^^^^^^^^ + +- ``astropy.utils.introspection.minversion()`` now uses + ``importlib.metadata.version()``. Therefore, its ``version_path`` keyword is no + longer used and deprecated. This keyword will be removed in a future release. [#11714] + +- Updated ``utils.console.Spinner`` to better resemble the API of + ``utils.console.ProgressBar``, including an ``update()`` method and + iterator support. [#11772] + +- Removed deprecated ``check_hashes`` in ``check_download_cache()``. The function also + no longer returns anything. [#12293] + +- Removed unused ``download_cache_lock_attempts`` configuration item in + ``astropy.utils.data``. Deprecation was not possible. [#12293] + +- Removed deprecated ``hexdigest`` keyword from ``import_file_to_cache()``. [#12293] + +- Setting ``remote_timeout`` configuration item in ``astropy.utils.data`` to 0 will + no longer disable download from the Internet; Set ``allow_internet`` configuration + item to ``False`` instead. [#12293] + +astropy.visualization +^^^^^^^^^^^^^^^^^^^^^ + +- Removed deprecated ``imshow_only_kwargs`` keyword from ``imshow_norm``. [#12290] + +astropy.wcs +^^^^^^^^^^^ + +- Move complex logic from ``HighLevelWCSMixin.pixel_to_world`` and + ``HighLevelWCSMixin.world_to_pixel`` into the helper functions + ``astropy.wcs.wcsapi.high_level_api.high_level_objects_to_values`` and + ``astropy.wcs.wcsapi.high_level_api.values_to_high_level_objects`` to allow + reuse in other places. [#11950] + + +Bug Fixes +--------- + +astropy.config +^^^^^^^^^^^^^^ + +- ``generate_config`` no longer outputs wrong syntax for list type. [#12037] + +astropy.constants +^^^^^^^^^^^^^^^^^ + +- Fixed a bug where an older constants version cannot be set directly after + astropy import. [#12084] + +astropy.convolution +^^^^^^^^^^^^^^^^^^^ + +- Passing an ``array`` argument for any Kernel1D or Kernel2D subclasses (with the + exception of CustomKernel) will now raise a ``TypeError``. [#11969] + +astropy.coordinates +^^^^^^^^^^^^^^^^^^^ + +- If a ``Table`` containing a ``SkyCoord`` object as a column is written to a + FITS, ECSV or HDF5 file then any velocity information that might be present + will be retained. [#11750] + +- The output of ``SkyCoord.apply_space_motion()`` now always has the same + differential type as the ``SkyCoord`` itself. [#11932] + +- Fixed bug where Angle, Latitude and Longitude with NaN values could not be printed. [#11943] + +- Fixed a bug with the transformation from ``PrecessedGeocentric`` to ``GCRS`` + where changes in ``obstime``, ``obsgeoloc``, or ``obsgeovel`` were ignored. + This bug would also affect loopback transformations from one ``PrecessedGeocentric`` + frame to another ``PrecessedGeocentric`` frame. [#12152] + +- Fixed a bug with the transformations between ``TEME`` and ``ITRS`` or between ``TEME`` + and itself where a change in ``obstime`` was ignored. [#12152] + +- Avoid unnecessary transforms through CIRS for AltAz and HADec and + use ICRS as intermediate frame for these transformations instead. [#12203] + +- Fixed a bug where instantiating a representation with a longitude component + could mutate input provided for that component even when copying is specified. [#12307] + +- Wrapping an ``Angle`` array will now ignore NaN values instead of attempting to wrap + them, which would produce unexpected warnings/errors when working with coordinates + and representations due to internal broadcasting. [#12317] + +astropy.cosmology +^^^^^^^^^^^^^^^^^ + +- Dictionaries for in-built cosmology realizations are not altered by creating + the realization and are also made immutable. [#12278] + +astropy.io.fits +^^^^^^^^^^^^^^^ + +- Prevent zero-byte writes for FITS binary tables to + speed up writes on the Lustre filesystem. [#11955] + +- Enable ``json.dump`` for FITS_rec with variable length (VLF) arrays. [#11957] + +- Add support for reading and writing int8 images [#11996] + +- Ensure header passed to ``astropy.io.fits.CompImageHDU`` does not need to contain + standard cards that can be automatically generated, such as ``BITPIX`` and ``NAXIS``. [#12061] + +- Fixed a bug where ``astropy.io.fits.HDUDiff`` would ignore the ``ignore_blank_cards`` + keyword argument. [#12122] + +- Open uncompressed file even if extension says it's compressed [#12135] + +- Fix the computation of the DATASUM in a ``CompImageHDU`` when the data is >1D. [#12138] + +- Reading files where the SIMPLE card is present but with an invalid format now + issues a warning instead of raising an exception [#12234] + +- Convert UNDEFINED to None when iterating over card values. [#12310] + +astropy.io.misc +^^^^^^^^^^^^^^^ + +- Update ASDF tag versions in ExtensionType subclasses to match ASDF Standard 1.5.0. [#11986] + +- Fix ASDF serialization of model inputs and outputs and add relevant assertion to + test helper. [#12381] + +- Fix bug preventing ASDF serialization of bounding box for models with only one input. [#12385] + +astropy.io.votable +^^^^^^^^^^^^^^^^^^ + +- Now accepting UCDs containing phot.color. [#11982] + +astropy.modeling +^^^^^^^^^^^^^^^^ + +- Added ``Parameter`` descriptions to the implemented models which were + missing. [#11232] + +- The ``separable`` property is now correctly set on models constructed with + ``astropy.modeling.custom_model``. [#11744] + +- Minor bugfixes and improvements to modeling including the following: + * Fixed typos and clarified several errors and their messages throughout + modeling. + * Removed incorrect try/except blocks around scipy code in + ``convolution.py`` and ``functional_models.py``. + * Fixed ``Ring2D`` model's init to properly accept all combinations + of ``r_in``, ``r_out``, and ``width``. + * Fixed bug in ``tau`` validator for the ``Logarithmic1D`` and + ``Exponential1D`` models when using them as model sets. + * Fixed ``copy`` method for ``Parameter`` in order to prevent an + automatic ``KeyError``, and fixed ``bool`` for ``Parameter`` so + that it functions with vector values. + * Removed unreachable code from ``Parameter``, the ``_Tabular`` model, + and the ``Drude1D`` model. + * Fixed validators in ``Drude1D`` model so that it functions in a + model set. + * Removed duplicated code from ``polynomial.py`` for handing of + ``domain`` and ``window``. + * Fixed the ``Pix2Sky_HEALPixPolar`` and ``Sky2Pix_HEALPixPolar`` modes + so that their ``evaluate`` and ``inverse`` methods actually work + without raising an error. [#12232] + +astropy.nddata +^^^^^^^^^^^^^^ + +- Ensure that the ``wcs=`` argument to ``NDData`` is always parsed into a high + level WCS object. [#11985] + +astropy.stats +^^^^^^^^^^^^^ + +- Fixed a bug in sigma clipping where the bounds would not be returned for + completely empty or masked data. [#11994] + +- Fixed a bug in ``biweight_midvariance`` and ``biweight_scale`` where + output data units would be dropped for constant data and where the + result was a scalar NaN. [#12146] + +astropy.table +^^^^^^^^^^^^^ + +- Ensured that ``MaskedColumn.info`` is propagated in all cases, so that when + tables are sliced, writing will still be as requested on + ``info.serialize_method``. [#11917] + +- ``table.conf.replace_warnings`` and ``table.jsviewer.conf.css_urls`` configuration + items now have correct ``'string_list'`` type. [#12037] + +- Fixed an issue where initializing from a list of dict-like rows (Mappings) did + not work unless the row values were instances of ``dict``. Now any object that + is an instance of the more general ``collections.abc.Mapping`` will work. [#12417] + +astropy.uncertainty +^^^^^^^^^^^^^^^^^^^ + +- Ensure that scalar ``QuantityDistribution`` unit conversion in ufuncs + works properly again. [#12471] + +astropy.units +^^^^^^^^^^^^^ + +- Add quantity support for ``scipy.special`` dimensionless functions + erfinv, erfcinv, gammaln and loggamma. [#10934] + +- ``VOUnit.to_string`` output is now compliant with IVOA VOUnits 1.0 standards. [#11565] + +- Units initialization with unicode has been expanded to include strings such as + 'M☉' and 'eâģ'. [#11827] + +- Give a more informative ``NotImplementedError`` when trying to parse a unit + using an output-only format such as 'unicode' or 'latex'. [#11829] + +astropy.utils +^^^^^^^^^^^^^ + +- Fixed a bug in ``get_readable_fileobj`` that prevented the unified file read + interface from closing ASCII files. [#11809] + +- The function ``astropy.utils.decorators.deprecated_attribute()`` no longer + ignores its ``message``, ``alternative``, and ``pending`` arguments. [#12184] + +- Ensure that when taking the minimum or maximum of a ``Masked`` array, + any masked NaN values are ignored. [#12454] + +astropy.visualization +^^^^^^^^^^^^^^^^^^^^^ + +- The tick labelling for radians has been fixed to remove a redundant ``.0`` in + the label for integer multiples of pi at 2pi and above. [#12221] + +- Fix a bug where non-``astropy.wcs.WCS`` WCS instances were not accepted in + ``WCSAxes.get_transform``. [#12286] + +- Fix compatibility with Matplotlib 3.5 when using the ``grid_type='contours'`` + mode for drawing grid lines. [#12447] + +astropy.wcs +^^^^^^^^^^^ + +- Enabled ``SlicedLowLevelWCS.pixel_to_world_values`` to handle slices including + non-``int`` integers, e.g. ``numpy.int64``. [#11980] + + +Other Changes and Additions +--------------------------- + +- In docstrings, Sphinx cross-reference targets now use intersphinx, even if the + target is an internal link (``link`` is now ``'astropy:link``). + When built in Astropy these links are interpreted as internal links. When built + in affiliate packages, the link target is set by the key 'astropy' in the + intersphinx mapping. [#11690] + +- Made PyYaml >= 3.13 a strict runtime dependency. [#11903] + +- Minimum version of required Python is now 3.8. [#11934] + +- Minimum version of required Scipy is now 1.3. [#11934] + +- Minimum version of required Matplotlib is now 3.1. [#11934] + +- Minimum version of required Numpy is now 1.18. [#11935] + +- Fix deprecation warnings with Python 3.10 [#11962] + +- Speed up ``minversion()`` in cases where a module with a ``__version__`` + attribute is passed. [#12174] + +- ``astropy`` now requires ``packaging``. [#12199] + +- Updated the bundled CFITSIO library to 4.0.0. When compiling with an external + library, version 3.35 or later is required. [#12272] + + +Version 4.3.1 (2021-08-11) +========================== + +Bug Fixes +--------- + +astropy.io.fits +^^^^^^^^^^^^^^^ + +- In ``fits.io.getdata`` do not fall back to first non-primary extension when + user explicitly specifies an extension. [#11860] + +- Ensure multidimensional masked columns round-trip properly to FITS. [#11911] + +- Ensure masked times round-trip to FITS, even if multi-dimensional. [#11913] + +- Raise ``ValueError`` if an ``np.float32`` NaN/Inf value is assigned to a + header keyword. [#11922] + +astropy.modeling +^^^^^^^^^^^^^^^^ + +- Fixed bug in ``fix_inputs`` handling of bounding boxes. [#11908] + +astropy.table +^^^^^^^^^^^^^ + +- Fix an error when converting to pandas any ``Table`` subclass that + automatically adds a table index when the table is created. An example is a + binned ``TimeSeries`` table. [#12018] + +astropy.units +^^^^^^^^^^^^^ + +- Ensure that unpickling quantities and units in new sessions does not change + hashes and thus cause problems with (de)composition such as getting different + answers from the ``.si`` attribute. [#11879] + +- Fixed cannot import name imperial from astropy.units namespace. [#11977] + +astropy.utils +^^^^^^^^^^^^^ + +- Ensure any ``.info`` on ``Masked`` instances is propagated correctly when + viewing or slicing. As a consequence, ``MaskedQuantity`` can now be correctly + written to, e.g., ECSV format with ``serialize_method='data_mask'``. [#11910] + + +Version 4.3 (2021-07-26) +======================== + +New Features +------------ + +astropy.convolution +^^^^^^^^^^^^^^^^^^^ + +- Change padding sizes for ``fft_pad`` in ``convolve_fft`` from powers of + 2 only to scipy-optimized numbers, applied separately to each dimension; + yielding some performance gains and avoiding potential large memory + impact for certain multi-dimensional inputs. [#11533] + +astropy.coordinates +^^^^^^^^^^^^^^^^^^^ + +- Adds the ability to create topocentric ``CIRS`` frames. Using these, + ``AltAz`` calculations are now accurate down to the milli-arcsecond + level. [#10994] + +- Adds a direct transformation from ``ICRS`` to ``AltAz`` frames. This + provides a modest speedup of approximately 10 percent. [#11079] + +- Adds new ``WGS84GeodeticRepresentation``, ``WGS72GeodeticRepresentation``, + and ``GRS80GeodeticRepresentation``. These are mostly for use inside + ``EarthLocation`` but can also be used to convert between geocentric + (cartesian) and different geodetic representations directly. [#11086] + +- ``SkyCoord.guess_from_table`` now also searches for differentials in the table. + In addition, multiple regex matches can be resolved when they are exact + component names, e.g. having both columns “dec” and “pm_dec” no longer errors + and will be included in the SkyCoord. [#11417] + +- All representations now have a ``transform`` method, which allows them to be + transformed by a 3x3 matrix in a Cartesian basis. By default, transformations + are routed through ``CartesianRepresentation``. ``SphericalRepresentation`` and + ``PhysicssphericalRepresentation`` override this for speed and to prevent NaN + leakage from the distance to the angular components. + Also, the functions ``is_O3`` and ``is_rotation`` have been added to + ``matrix_utities`` for checking whether a matrix is in the O(3) group or is a + rotation (proper or improper), respectively. [#11444] + +- Moved angle formatting and parsing utilities to + ``astropy.coordinates.angle_formats``. + Added new functionality to ``astropy.coordinates.angle_utilities`` for + generating points on or in spherical surfaces, either randomly or on a grid. [#11628] + +- Added a new method to ``SkyCoord``, ``spherical_offsets_by()``, which is the + conceptual inverse of ``spherical_offsets_to()``: Given angular offsets in + longitude and latitude, this method returns a new coordinate with the offsets + applied. [#11635] + +- Refactor conversions between ``GCRS`` and ``CIRS,TETE`` for better accuracy + and substantially improved speed. [#11069] + +- Also refactor ``EarthLocation.get_gcrs`` for an increase in performance of + an order of magnitude, which enters as well in getting observed positions of + planets using ``get_body``. [#11073] + +- Refactored the usage of metaclasses in ``astropy.coordinates`` to instead use + ``__init_subclass__`` where possible. [#11090] + +- Removed duplicate calls to ```transform_to``` from ```match_to_catalog_sky``` + and ```match_to_catalog_3d```, improving their performance. [#11449] + +- The new DE440 and DE440s ephemerides are now available via shortcuts 'de440' + and 'de440s'. The DE 440s ephemeris will probably become the default + ephemeris when choosing 'jpl' in 5.0. [#11601] + +astropy.cosmology +^^^^^^^^^^^^^^^^^ + +- Cosmology parameter dictionaries now also specify the Cosmology class to which + the parameters correspond. For example, the dictionary for + ``astropy.cosmology.parameters.Planck18`` has the added key-value pair + ("cosmology", "FlatLambdaCDM"). [#11530] + +astropy.io.ascii +^^^^^^^^^^^^^^^^ + +- Added support for reading and writing ASCII tables in QDP (Quick and Dandy + Plotter) format. [#11256] + +- Added support for reading and writing multidimensional column data (masked and + unmasked) to ECSV. Also added formal support for reading and writing object-type + column data which can contain items consisting of lists, dicts, and basic scalar + types. This can be used to store columns of variable-length arrays. Both of + these features use JSON to convert the object to a string that is stored in the + ECSV output. [#11569, #11662, #11720] + +astropy.io.fits +^^^^^^^^^^^^^^^ + +- Added ``append`` keyword to append table objects to an existing FITS file [#2632, #11149] + +- Check that the SIMPLE card is present when opening a file, to ensure that the + file is a valid FITS file and raise a better error when opening a non FITS + one. ``ignore_missing_simple`` can be used to skip this verification. [#10895] + +- Expose ``Header.strip`` as a public method, to remove the most common + structural keywords. [#11174] + +- Enable the use of ``os.PathLike`` objects when dealing with (mainly FITS) files. [#11580] + +astropy.io.registry +^^^^^^^^^^^^^^^^^^^ + +- Readers and writers can now set a priority, to assist with resolving which + format to use. [#11214] + +astropy.io.votable +^^^^^^^^^^^^^^^^^^ + +- Version 1.4 VOTables now use the VOUnit format specification. [#11032] + +- When reading VOTables using the Unified File Read/Write Interface (i.e. using + the ``Table.read()`` or ``QTable.read()`` functions) it is now possible to + specify all keyword arguments that are valid for + ``astropy.io.votable.table.parse()``. [#11643] + +astropy.modeling +^^^^^^^^^^^^^^^^ + +- Added a state attribute to models to allow preventing the syncing of + constraint values from the constituent models. This syncing can + greatly slow down fitting if there are large numbers of fit parameters. + model.sync_constraints = True means check constituent model constraints + for compound models every time the constraint is accessed, False, do not. + Fitters that support constraints will set this to False on the model copy + and then set back to True when the fit is complete before returning. [#11365] + +- The ``convolve_models_fft`` function implements model convolution so that one + insures that the convolution remains consistent across multiple different + inputs. [#11456] + +astropy.nddata +^^^^^^^^^^^^^^ + +- Prevent unnecessary copies of the data during ``NDData`` arithmetic when units + need to be added. [#11107] + +- NDData str representations now show units, if present. [#11553] + +astropy.stats +^^^^^^^^^^^^^ + +- Added the ability to specify stdfunc='mad_std' when doing sigma clipping, + which will use a built-in function and lead to significant performance + improvements if cenfunc is 'mean' or 'median'. [#11664] + + +- Significantly improved the performance of sigma clipping when cenfunc and + stdfunc are passed as strings and the ``grow`` option is not used. [#11219] + +- Improved performance of ``bayesian_blocks()`` by removing one ``np.log()`` + call [#11356] + +astropy.table +^^^^^^^^^^^^^ + +- Add table attributes to include or exclude columns from the output when + printing a table. This functionality includes a context manager to + include/exclude columns temporarily. [#11190] + +- Improved the string representation of objects related to ``Table.indices`` so + they now indicate the object type and relevant attributes. [#11333] + +astropy.timeseries +^^^^^^^^^^^^^^^^^^ + +- An exception is raised when ``n_bins`` is passed as an argument while + any of the parameters ``time_bin_start`` or ``time_bin_size`` is not + scalar. [#11463] + +astropy.units +^^^^^^^^^^^^^ + +- The ``physical_type`` attributes of each unit are now objects of the (new) + ``astropy.units.physical.PhysicalType`` class instead of strings and the + function ``astropy.units.physical.get_physical_type`` can now translate + strings to these objects. [#11204] + +- The function ``astropy.units.physical.def_physical_type`` was created to + either define entirely new physical types, or to add more physical type + names to an existing physical types. [#11204] + +- ``PhysicalType``'s can be operated on using operations multiplication, + division, and exponentiation are to facilitate dimensional analysis. [#11204] + +- It is now possible to define aliases for units using + ``astropy.units.set_enabled_aliases``. This can be used when reading files + that have misspelled units. [#11258] + +- Add a new "DN" unit, ``units.dn`` or ``units.DN``, representing data number + for a detector. [#11591] + +astropy.utils +^^^^^^^^^^^^^ + +- Added ``ssl_context`` and ``allow_insecure`` options to ``download_file``, + as well as the ability to optionally use the ``certifi`` package to provide + root CA certificates when downloading from sites secured with + TLS/SSL. [#10434] + +- ``astropy.utils.data.get_pkg_data_path`` is publicly scoped (previously the + private function ``_find_pkg_data_path``) for obtaining file paths without + checking if the file/directory exists, as long as the package and module + do. [#11006] + +- Deprecated ``astropy.utils.OrderedDescriptor`` and + ``astropy.utils.OrderedDescriptorContainer``, as new features in Python 3 + make their use less compelling. [#11094, #11099] + +- ``astropy.utils.masked`` provides a new ``Masked`` class/factory that can be + used to represent masked ``ndarray`` and all its subclasses, including + ``Quantity`` and its subclasses. These classes can be used inside + coordinates, but the mask is not yet exposed. Generally, the interface should + be considered experimental. [#11127, #11792] + +- Add new ``utils.parsing`` module to with helper wrappers around + ``ply``. [#11227] + +- Change the Time and IERS leap second handling so that the leap second table is + updated only when a Time transform involving UTC is performed. Previously this + update check was done the first time a ``Time`` object was created, which in + practice occurred when importing common astropy subpackages like + ``astropy.coordinates``. Now you can prevent querying internet resources (for + instance on a cluster) by setting ``iers.conf.auto_download = False``. This + can be done after importing astropy but prior to performing any ``Time`` + scale transformations related to UTC. [#11638] + + +- Added a new module at ``astropy.utils.compat.optional_deps`` to consolidate + the definition of ``HAS_x`` optional dependency flag variables, + like ``HAS_SCIPY``. [#11490] + +astropy.wcs +^^^^^^^^^^^ + +- Add IVOA UCD mappings for some FITS WCS keywords commonly used in solar + physics. [#10965] + +- Add ``STOKES`` FITS WCS keyword to the IVOA UCD mapping. [#11236] + +- Updated bundled version of WCSLIB to version 7.6. See + https://www.atnf.csiro.au/people/mcalabre/WCS/CHANGES for a list of + included changes. [#11549] + + +API Changes +----------- + +astropy.coordinates +^^^^^^^^^^^^^^^^^^^ + +- For input to representations, subclasses of the class required for a + given attribute will now be allowed in. [#11113] + +- Except for ``UnitSphericalRepresentation``, shortcuts in representations now + allow for attached differentials. [#11467] + +- Allow coordinate name strings as input to + ``SkyCoord.is_transformable_to``. [#11552] + +astropy.cosmology +^^^^^^^^^^^^^^^^^ + +- Change ``z_at_value`` to use ``scipy.optimize.minimize_scalar`` with default + method ``Brent`` (other options ``Bounded`` and ``Golden``) and accept + ``bracket`` option to set initial search region. [#11080] + +- Clarified definition of inputs to ``angular_diameter_distance_z1z2``. + The function now emits ``AstropyUserWarning`` when ``z2`` is less than + ``z1``. [#11197] + +- Split cosmology realizations from core classes, moving the former to new file + ``realizations``. [#11345] + +- Since cosmologies are immutable, the initialization signature and values can + be stored, greatly simplifying cloning logic and extending it to user-defined + cosmology classes that do not have attributes with the same name as each + initialization argument. [#11515] + +- Cloning a cosmology with changed parameter(s) now appends "(modified)" to the + new instance's name, unless a name is explicitly passed to ``clone``. [#11536] + +- Allow ``m_nu`` to be input as any quantity-like or array-like -- Quantity, + array, float, str, etc. Input is passed to the Quantity constructor and + converted to eV, still with the prior mass-energy equivalence + enabled. [#11640] + +astropy.io.fits +^^^^^^^^^^^^^^^ + +- For conversion between FITS tables and astropy ``Table``, the standard mask + values of ``NaN`` for float and null string for string are now properly + recognized, leading to a ``MaskedColumn`` with appropriately set mask + instead of a ``Column`` with those values exposed. Conversely, when writing + an astropy ``Table`` to a FITS tables, masked values are now consistently + converted to the standard FITS mask values of ``NaN`` for float and null + string for string (i.e., not just for tables with ``masked=True``, which no + longer is guaranteed to signal the presence of ``MaskedColumn``). [#11222] + +astropy.io.votable +^^^^^^^^^^^^^^^^^^ + +- The use of ``version='1.0'`` is now fully deprecated in constructing + a ``astropy.io.votable.tree.VOTableFile``. [#11659] + +astropy.modeling +^^^^^^^^^^^^^^^^ + +- Removed deprecated ``astropy.modeling.blackbody`` module. [#10972] + +astropy.table +^^^^^^^^^^^^^ + +- Added ``Column.value`` as an alias for the existing ``Column.data`` attribute. + This makes accessing a column's underlying data array consistent with the + ``.value`` attribute available for ``Time`` and ``Quantity`` objects. [#10962] + +- In reading from a FITS tables, the standard mask values of ``NaN`` for float + and null string for string are properly recognized, leading to a + ``MaskedColumn`` with appropriately set mask. [#11222] + +- Changed the implementation of the ``table.index.Index`` class so instantiating + from this class now returns an ``Index`` object as expected instead of a + ``SlicedIndex`` object. [#11333] + +astropy.units +^^^^^^^^^^^^^ + +- The ``physical_type`` attribute of units now returns an instance of + ``astropy.units.physical.PhysicalType`` instead of a string. Because + ``PhysicalType`` instances can be compared to strings, no code changes + should be necessary when making comparisons. The string representations + of different physical types will differ from previous releases. [#11204] + +- Calling ``Unit()`` with no argument now returns a dimensionless unit, + as was documented but not implemented. [#11295] + +astropy.utils +^^^^^^^^^^^^^ + +- Removed deprecated ``utils.misc.InheritDocstrings`` and ``utils.timer``. [#10281] + +- Removed usage of deprecated ``ipython`` stream in ``utils.console``. [#10942] + +astropy.wcs +^^^^^^^^^^^ + +- Deprecate ``accuracy`` argument in ``all_world2pix`` which was mistakenly + *documented*, in the case ``accuracy`` was ever used. [#11055] + + +Bug Fixes +--------- + +astropy.convolution +^^^^^^^^^^^^^^^^^^^ + +- Fixes for ``convolve_fft`` documentation examples. [#11510] + +astropy.coordinates +^^^^^^^^^^^^^^^^^^^ + +- Allow ``Distance`` instances with negative distance values as input for + ``SphericalRepresentation``. This was always noted as allowed in an + exception message when a negative ``Quantity`` with length units was + passed in, but was not actually possible to do. [#11113] + +- Makes the ``Angle.to_string`` method to follow the format described in the + docstring with up to 8 significant decimals instead of 4. [#11153] + +- Ensure that proper motions can be calculated when converting a ``SkyCoord`` + with cartesian representation to unit-spherical, by fixing the conversion of + ``CartesianDifferential`` to ``UnitSphericalDifferential``. [#11469] + +- When re-representing coordinates from spherical to unit-spherical and vice + versa, the type of differential will now be preserved. For instance, if only a + radial velocity was present, that will remain the case (previously, a zero + proper motion component was added). [#11482] + +- Ensure that wrapping of ``Angle`` does not raise a warning even if ``nan`` are + present. Also try to make sure that the result is within the wrapped range + even in the presence of rounding errors. [#11568] + +- Comparing a non-SkyCoord object to a ``SkyCoord`` using ``==`` no longer + raises an error. [#11666] + +- Different ``SkyOffsetFrame`` classes no longer interfere with each other, + causing difficult to debug problems with the ``origin`` attribute. The + ``origin`` attribute now no longer is propagated, so while it remains + available on a ``SkyCoord`` that is an offset, it no longer is available once + that coordinate is transformed to another frame. [#11730] [#11730] + +astropy.cosmology +^^^^^^^^^^^^^^^^^ + +- Cosmology instance names are now immutable. [#11535] + +astropy.io.ascii +^^^^^^^^^^^^^^^^ + +- Fixed bug where writing a table that has comments defined (via + ``tbl.meta['comments']``) with the 'csv' format was failing. Since the + formally defined CSV format does not support comments, the comments are now + just ignored unless ``comment=`` is supplied to the + ``write()`` call. [#11475] + +- Fixed the issue where the CDS reader failed to treat columns + as nullable if the ReadMe file contains a limits specifier. [#11531] + +- Made sure that the CDS reader does not ignore an order specifier that + may be present after the null specifier '?'. Also made sure that it + checks null values only when an '=' symbol is present and reads + description text even if there is no whitespace after '?'. [#11593] + +astropy.io.fits +^^^^^^^^^^^^^^^ + +- Fix ``ColDefs.add_col/del_col`` to allow in-place addition or removal of + a column. [#11338] + +- Fix indexing of ``fits.Header`` with Numpy integers. [#11387] + +- Do not delete ``EXTNAME`` for compressed image header if a default and + non-default ``EXTNAME`` are present. [#11396] + +- Prevent warnings about ``HIERARCH`` with ``CompImageHeader`` class. [#11404] + +- Fixed regression introduced in Astropy 4.0.5 and 4.2.1 with verification of + FITS headers with HISTORY or COMMENT cards with long (> 72 characters) + values. [#11487] + +- Fix reading variable-length arrays when there is a gap between the data and the + heap. [#11688] + +astropy.io.votable +^^^^^^^^^^^^^^^^^^ + +- ``NumericArray`` converter now properly broadcasts scalar mask to array. [#11157] + +- VOTables are now written with the correct namespace and schema location + attributes. [#11659] + +astropy.modeling +^^^^^^^^^^^^^^^^ + +- Fixes the improper propagation of ``bounding_box`` from + ``astropy.modeling.models`` to their inverses. For cases in which the inverses + ``bounding_box`` can be determined, the proper calculation has been + implemented. [#11414] + +- Bugfix to allow rotation models to accept arbitrarily-shaped inputs. [#11435] + +- Bugfixes for ``astropy.modeling`` to allow ``fix_inputs`` to accept empty + dictionaries and dictionaries with ``numpy`` integer keys. [#11443] + +- Bugfix for how ``SPECIAL_OPERATORS`` are handled. [#11512] + +- Fixes ``Model`` crashes when some inputs are scalars and during some types of + output reshaping. [#11548] + +- Fixed bug in ``LevMarLSQFitter`` when using weights and vector inputs. [#11603] + +astropy.stats +^^^^^^^^^^^^^ + +- Fixed a bug with the ``copy=False`` option when carrying out sigma + clipping - previously if ``masked=False`` this still copied the data, + but this will now change the array in-place. [#11219] + +astropy.table +^^^^^^^^^^^^^ + +- Ensure that adding a ``Quantity`` or other mixin column to a ``Table`` + does not have side effects, such as creating an associated ``info`` + instance (which would lead to slow-down of, e.g., slicing afterwards). [#11077] + +- When writing to a FITS tables, masked values are again always converted to + the standard FITS mask values of ``NaN`` for float and null string + for string, not just for table with ``masked=True``. [#11222] + +- Using ``Table.to_pandas()`` on an indexed ``Table`` with masked integer values + now correctly construct the ``pandas.DataFrame``. [#11432] + +- Fixed ``Table`` HTML representation in Jupyter notebooks so that it is + horizontally scrollable within Visual Studio Code. This was done by wrapping + the ```` in a ``
`` element. [#11476] + +- Fix a bug where a string-valued ``Column`` that happened to have a ``unit`` + attribute could not be added to a ``QTable``. Such columns are now simply + kept as ``Column`` instances (with a warning). [#11585] + +- Fix an issue in ``Table.to_pandas(index=)`` where the index column name + was not being set properly for the ``DataFrame`` index. This was introduced by + an API change in pandas version 1.3.0. Previously when creating a ``DataFrame`` + with the index set to an astropy ``Column``, the ``DataFrame`` index name was + automatically set to the column name. [#11921] + +astropy.time +^^^^^^^^^^^^ + +- Fix a thread-safety issue with initialization of the leap-second table + (which is only an issue when ERFA's built-in table is out of date). [#11234] + +- Fixed converting a zero-length time object from UTC to + UT1 when an empty array is passed. [#11516] + +astropy.uncertainty +^^^^^^^^^^^^^^^^^^^ + +- ``Distribution`` instances can now be used as input to ``Quantity`` to + initialize ``QuantityDistribution``. Hence, ``distribution * unit`` + and ``distribution << unit`` will work too. [#11210] + +astropy.units +^^^^^^^^^^^^^ + +- Move non-astronomy units from astrophys.py to a new misc.py file. [#11142] + +- The physical type of ``astropy.units.mol / astropy.units.m ** 3`` is now + defined as molar concentration. It was previously incorrectly defined + as molar volume. [#11204] + +- Make ufunc helper lookup thread-safe. [#11226] + +- Make ``Unit`` string parsing (as well as ``Angle`` parsing) thread-safe. [#11227] + +- Decorator ``astropy.units.decorators.quantity_input`` now only evaluates + return type annotations based on ``UnitBase`` or ``FunctionUnitBase`` types. + Other annotations are skipped over and are not attempted to convert to the + correct type. [#11506] + +astropy.utils +^^^^^^^^^^^^^ + +- Make ``lazyproperty`` and ``classdecorator`` thread-safe. This should fix a + number of thread safety issues. [#11224] + +astropy.visualization +^^^^^^^^^^^^^^^^^^^^^ + +- Fixed a bug that resulted in some parts of grid lines being visible when they + should have been hidden. [#11380] + +- Fixed a bug that resulted in ``time_support()`` failing for intervals of + a few months if one of the ticks was the month December. [#11615] + +astropy.wcs +^^^^^^^^^^^ + +- ``fit_wcs_from_points`` now produces a WCS with integer ``NAXIXn`` + values. [#10865] + +- Updated bundled version of ``WCSLIB`` to v7.4, fixing a bug that caused + the coefficients of the TPD distortion function to not be written to the + header. [#11260] + +- Fixed a bug in assigning type when converting ``colsel`` to + ``numpy.ndarray``. [#11431] + +- Added ``WCSCOMPARE_*`` constants to the list of WCSLIB constants + available/exposed through the ``astropy.wcs`` module. [#11647] + +- Fix a bug that caused APE 14 WCS transformations for FITS WCS with ZOPT, BETA, + VELO, VOPT, or VRAD CTYPE to not work correctly. [#11781] + + +Other Changes and Additions +--------------------------- + +- The configuration file is no longer created by default when importing astropy + and its existence is no longer required. Affiliated packages should update their + ``__init__.py`` module to remove the block using ``update_default_config`` and + ``ConfigurationDefaultMissingWarning``. [#10877] + +- Replace ``pkg_resources`` (from setuptools) with ``importlib.metadata`` which + comes from the stdlib, except for Python 3.7 where the backport package is added + as a new dependency. [#11091] + +- Turn on numpydoc's ``numpydoc_xref_param_type`` to create cross-references + for the parameter types in the Parameters, Other Parameters, Returns and Yields + sections of the docstrings. [#11118] + +- Docstrings across the package are standardized to enable references. + Also added is an Astropy glossary-of-terms to define standard inputs, + e.g. ``quantity-like`` indicates an input that can be interpreted by + ``astropy.units.Quantity``. [#11118] + +- Binary wheels are now built to the manylinux2010 specification. These wheels + should be supported on all versions of pip shipped with Python 3.7+. [#11377] + +- The name of the default branch for the astropy git repository has been renamed + to ``main``, and the documentation and tooling has been updated accordingly. + If you have made a local clone you may wish to update it following the + instructions in the repository's README. [#11379] + +- Sphinx cross-reference link targets are added for every ``PhysicalType``. + Now in the parameter types in the Parameters, Other Parameters, Returns and + Yields sections of the docstring, the physical type of a quantity can be + annotated in square brackets. + E.g. ``distance : `~astropy.units.Quantity` ['length']`` [#11595] + +- The minimum supported version of ``ipython`` is now 4.2. [#10942] + +- The minimum supported version of ``pyerfa`` is now 1.7.3. [#11637] + + +Version 4.2.1 (2021-04-01) +========================== + +Bug Fixes +--------- + +astropy.cosmology +^^^^^^^^^^^^^^^^^ + +- Fixed an issue where specializations of the comoving distance calculation + for certain cosmologies could not handle redshift arrays. [#10980] + +astropy.io.fits +^^^^^^^^^^^^^^^ + +- Fix bug where manual fixes to invalid header cards were not preserved when + saving a FITS file. [#11108] + +astropy.io.votable +^^^^^^^^^^^^^^^^^^ + +- ``NumericArray`` converter now properly broadcasts scalar mask to array. + [#11157] + +astropy.table +^^^^^^^^^^^^^ + +- Fix bug when initializing a ``Table`` subclass that uses ``TableAttribute``'s. + If the data were an instance of the table then attributes provided in the + table initialization call could be ignored. [#11217] + +astropy.time +^^^^^^^^^^^^ + +- Change epoch of ``TimeUnixTAI`` (``"unix_tai"``) from ``1970-01-01T00:00:00 UTC`` + to ``1970-01-01T00:00:00 TAI`` to match the intended and documented behaviour. + This essentially changes the resulting times by 8.000082 seconds, the initial + offset between TAI and UTC. [#11249] + +astropy.units +^^^^^^^^^^^^^ + +- Fixed a bug with the ``quantity_input`` decorator where allowing + dimensionless inputs for an argument inadvertently disabled any checking of + compatible units for that argument. [#11283] + +astropy.utils +^^^^^^^^^^^^^ + +- Fix a bug so that ``np.shape``, ``np.ndim`` and ``np.size`` again work on + classes that use ``ShapedLikeNDArray``, like representations, frames, + sky coordinates, and times. [#11133] + +astropy.wcs +^^^^^^^^^^^ + +- Fix error when a user defined ``proj_point`` parameter is passed to ``fit_wcs_from_points``. [#11139] + + +Other Changes and Additions +--------------------------- + + +- Change epoch of ``TimeUnixTAI`` (``"unix_tai"``) from ``1970-01-01T00:00:00 UTC`` + to ``1970-01-01T00:00:00 TAI`` to match the intended and documented behaviour. + This essentially changes the resulting times by 8.000082 seconds, the initial + offset between TAI and UTC. [#11249] + + +Version 4.2 (2020-11-24) +======================== + +New Features +------------ + +astropy.convolution +^^^^^^^^^^^^^^^^^^^ + +- Methods ``convolve`` and ``convolve_fft`` both now return Quantity arrays + if user input is given in one. [#10822] + +astropy.coordinates +^^^^^^^^^^^^^^^^^^^ + +- Numpy functions that broadcast, change shape, or index (like + ``np.broadcast_to``, ``np.rot90``, or ``np.roll``) now work on + coordinates, frames, and representations. [#10337] + +- Add a new science state ``astropy.coordinates.erfa_astrom.erfa_astrom`` and + two classes ``ErfaAstrom``, ``ErfaAstromInterpolator`` as wrappers to + the ``pyerfa`` astrometric functions used in the coordinate transforms. + Using ``ErfaAstromInterpolator``, which interpolates astrometric properties for + ``SkyCoord`` instances with arrays of obstime, can dramatically speed up + coordinate transformations while keeping microarcsecond resolution. + Depending on needed precision and the obstime array in question, speed ups + reach factors of 10x to >100x. [#10647] + +- ``galactocentric_frame_defaults`` can now also be used as a registry, with + user-defined parameter values and metadata. [#10624] + +- Method ``.realize_frame`` from coordinate frames now accepts ``**kwargs``, + including ``representation_type``. [#10727] + +- Avoid an unnecessary call to ``erfa.epv00`` in transformations between + ``CIRS`` and ``ICRS``, improving performance by 50 %. [#10814] + +- A new equatorial coordinate frame, with RA and Dec measured w.r.t to the True + Equator and Equinox (TETE). This frame is commonly known as "apparent place" + and is the correct frame for coordinates returned from JPL Horizons. [#10867] + +- Added a context manager ``impose_finite_difference_dt`` to the + ``TransformGraph`` class to override the finite-difference time step + attribute (``finite_difference_dt``) for all transformations in the graph + with that attribute. [#10341] + +- Improve performance of ``SpectralCoord`` by refactoring internal + implementation. [#10398] + +astropy.cosmology +^^^^^^^^^^^^^^^^^ + +- The final version of the Planck 2018 cosmological parameters are included + as the ``Planck18`` object, which is now the default cosmology. The + parameters are identical to those of the ``Planck18_arXiv_v2`` object, + which is now deprecated and will be removed in a future release. [#10915] + +astropy.modeling +^^^^^^^^^^^^^^^^ + +- Added NFW profile and tests to modeling package [#10505] + +- Added missing logic for evaluate to compound models [#10002] + +- Stop iteration in ``FittingWithOutlierRemoval`` before reaching ``niter`` if + the masked points are no longer changing. [#10642] + +- Keep a (shallow) copy of ``fit_info`` from the last iteration of the wrapped + fitter in ``FittingWithOutlierRemoval`` and also record the actual number of + iterations performed in it. [#10642] + +- Added attributes for fitting uncertainties (covariance matrix, standard + deviations) to models. Parameter covariance matrix can be accessed via + ``model.cov_matrix``, standard deviations by ``model.stds`` or individually + for each parameter by ``parameter.std``. Currently implemented for + ``LinearLSQFitter`` and ``LevMarLSQFitter``. [#10552] + +- N-dimensional least-squares statistic and specific 1,2,3-D methods [#10670] + +astropy.stats +^^^^^^^^^^^^^ + +- Added ``circstd`` function to obtain a circular standard deviation. [#10690] + +astropy.table +^^^^^^^^^^^^^ + +- Allow initializing a ``Table`` using a list of ``names`` in conjunction with + a ``dtype`` from a numpy structured array. The list of ``names`` overrides the + names specified in the ``dtype``. [#10419] + +astropy.time +^^^^^^^^^^^^ + +- Add new ``isclose()`` method to ``Time`` and ``TimeDelta`` classes to allow + comparison of time objects to within a specified tolerance. [#10646] + +- Improve initialization time by a factor of four when creating a scalar ``Time`` + object in a format like ``unix`` or ``cxcsec`` (time delta from a reference + epoch time). [#10406] + +- Improve initialization time by a factor of ~25 or more for large arrays of + string times in ISO, ISOT or year day-of-year formats. This is done with a new + C-based time parser that can be adapted for other fixed-format custom time + formats. [#10360] + +- Numpy functions that broadcast, change shape, or index (like + ``np.broadcast_to``, ``np.rot90``, or ``np.roll``) now work on times. + [#10337, #10502] + +astropy.timeseries +^^^^^^^^^^^^^^^^^^ + +- Improve memory and speed performance when iterating over the entire time + column of a ``TimeSeries`` object. Previously this involved O(N^2) operations + and memory. [#10889] + +astropy.units +^^^^^^^^^^^^^ + +- ``Quantity.to`` has gained a ``copy`` option to allow copies to be avoided + when the units do not change. [#10517] + +- Added the ``spat`` unit of solid angle that represents the full sphere. + [#10726] + +astropy.utils +^^^^^^^^^^^^^ + +- ``ShapedLikeNDArray`` has gained the capability to use numpy functions + that broadcast, change shape, or index. [#10337] + +- ``get_free_space_in_dir`` now takes a new ``unit`` keyword and + ``check_free_space_in_dir`` takes ``size`` defined as ``Quantity``. [#10627] + +- New ``astropy.utils.data.conf.allow_internet`` configuration item to + control downloading data from the Internet. Setting ``allow_internet=False`` + is the same as ``remote_timeout=0``. Using ``remote_timeout=0`` to control + internet access will stop working in a future release. [#10632] + +- New ``is_url`` function so downstream packages do not have to secretly use + the hidden ``_is_url`` anymore. [#10684] + +astropy.visualization +^^^^^^^^^^^^^^^^^^^^^ + +- Added the ``Quadrangle`` patch for ``WCSAxes`` for a latitude-longitude + quadrangle. Unlike ``matplotlib.patches.Rectangle``, the edges of this + patch will be rendered as curved lines if appropriate for the WCS + transformation. [#10862] + +- The position of tick labels are now only calculated when needed. If any text + parameters are changed (color, font weight, size etc.) that don't effect the + tick label position, the positions are not recomputed, improving performance. + [#10806] + +astropy.wcs +^^^^^^^^^^^ + +- ``WCS.to_header()`` now appends comments to SIP coefficients. [#10480] + +- A new property ``dropped_world_dimensions`` has been added to + ``SlicedLowLevelWCS`` to record information about any world axes removed by + slicing a WCS. [#10195] + +- New ``WCS.proj_plane_pixel_scales()`` and ``WCS.proj_plane_pixel_area()`` + methods to return pixel scales and area, respectively, as Quantity. [#10872] + + +API Changes +----------- + +astropy.config +^^^^^^^^^^^^^^ + +- ``set_temp_config`` now preserves the existing cache rather than deleting + it and relying on reloading it from the previous config file. This ensures + that any programmatically made changes are preserved as well. [#10474] + +- Configuration path detection logic has changed: Now, it looks for ``~`` first + before falling back to older logic. In addition, ``HOMESHARE`` is no longer + used in Windows. [#10705] + +astropy.coordinates +^^^^^^^^^^^^^^^^^^^ + +- The passing of frame classes (as opposed to frame instances) to the + ``transform_to()`` methods of low-level coordinate-frame classes has been + deprecated. Frame classes can still be passed to the ``transform_to()`` + method of the high-level ``SkyCoord`` class, and using ``SkyCoord`` is + recommended for all typical use cases of transforming coordinates. [#10475] + +astropy.stats +^^^^^^^^^^^^^ + +- Added a ``grow`` parameter to ``SigmaClip``, ``sigma_clip`` and + ``sigma_clipped_stats``, to allow expanding the masking of each deviant + value to its neighbours within a specified radius. [#10613] + +- Passing float ``n`` to ``poisson_conf_interval`` when using + ``interval='kraft-burrows-nousek'`` now raises ``TypeError`` as its value + must be an integer. [#10838] + +astropy.table +^^^^^^^^^^^^^ + +- Change ``Table.columns.keys()`` and ``Table.columns.values()`` to both return + generators instead of a list. This matches the behavior for Python ``dict`` + objects. [#10543] + +- Removed the ``FastBST`` and ``FastRBT`` indexing engines because they depend + on the ``bintrees`` package, which is no longer maintained and is deprecated. + Instead, use the ``SCEngine`` indexing engine, which is similar in + performance and relies on the ``sortedcontainers`` package. [#10622] + +- When slicing a mixin column in a table that had indices, the indices are no + longer copied since they generally are not useful, having the wrong shape. + With this, the behaviour becomes the same as that for a regular ``Column``. + (Note that this does not affect slicing of a table; sliced columns in those + will continue to carry a sliced version of any indices). [#10890] + +- Change behavior so that when getting a single item out of a mixin column such + as ``Time``, ``TimeDelta``, ``SkyCoord`` or ``Quantity``, the ``info`` + attribute is no longer copied. This improves performance, especially when the + object is an indexed column in a ``Table``. [#10889] + +- Raise a TypeError when a scalar column is added to an unsized table. [#10476] + +- The order of columns when creating a table from a ``list`` of ``dict`` may be + changed. Previously, the order was alphabetical because the ``dict`` keys + were assumed to be in random order. Since Python 3.7, the keys are always in + order of insertion, so ``Table`` now uses the order of keys in the first row + to set the column order. To alphabetize the columns to match the previous + behavior, use ``t = t[sorted(t.colnames)]``. [#10900] + +astropy.time +^^^^^^^^^^^^ + +- Refactor ``Time`` and ``TimeDelta`` classes to inherit from a common + ``TimeBase`` class. The ``TimeDelta`` class no longer inherits from ``Time``. + A number of methods that only apply to ``Time`` (e.g. ``light_travel_time``) + are no longer available in the ``TimeDelta`` class. [#10656] + +astropy.units +^^^^^^^^^^^^^ + +- The ``bar`` unit is no longer wrongly considered an SI unit, meaning that + SI decompositions like ``(u.kg*u.s**-2* u.sr**-1 * u.nm**-1).si`` will + no longer include it. [#10586] + +astropy.utils +^^^^^^^^^^^^^ + +- Shape-related items from ``astropy.utils.misc`` -- ``ShapedLikeNDArray``, + ``check_broadcast``, ``unbroadcast``, and ``IncompatibleShapeError`` -- + have been moved to their own module, ``astropy.utils.shapes``. They remain + importable from ``astropy.utils``. [#10337] + +- ``check_hashes`` keyword in ``check_download_cache`` is deprecated and will + be removed in a future release. [#10628] + +- ``hexdigest`` keyword in ``import_file_to_cache`` is deprecated and will + be removed in a future release. [#10628] + + +Bug Fixes +--------- + +astropy.config +^^^^^^^^^^^^^^ + +- Fix a few issues with ``generate_config`` when used with other packages. + [#10893] + +astropy.coordinates +^^^^^^^^^^^^^^^^^^^ + +- Fixed a bug in the coordinate-frame attribute ``CoordinateAttribute`` where + the internal transformation could behave differently depending on whether + the input was a low-level coordinate frame or a high-level ``SkyCoord``. + ``CoordinateAttribute`` now always performs a ``SkyCoord``-style internal + transformation, including the by-default merging of frame attributes. [#10475] + +astropy.modeling +^^^^^^^^^^^^^^^^ + +- Fixed an issue of ``Model.render`` when the input ``out`` datatype is not + float64. [#10542] + +astropy.visualization +^^^^^^^^^^^^^^^^^^^^^ + +- Fix support for referencing WCSAxes coordinates by their world axes names. + [#10484] + +astropy.wcs +^^^^^^^^^^^ + +- Objective functions called by ``astropy.wcs.fit_wcs_from_points`` were + treating longitude and latitude distances equally. Now longitude scaled + properly. [#10759] + + +Other Changes and Additions +--------------------------- + +- Minimum version of required Python is now 3.7. [#10900] + +- Minimum version of required Numpy is now 1.17. [#10664] + +- Minimum version of required Scipy is now 1.1. [#10900] + +- Minimum version of required PyYAML is now 3.13. [#10900] + +- Minimum version of required Matplotlib is now 3.0. [#10900] + +- The private ``_erfa`` module has been converted to its own package, + ``pyerfa``, which is a required dependency for astropy, and can be imported + with ``import erfa``. Importing ``_erfa`` from ``astropy`` will give a + deprecation warning. [#10329] + +- Added ``optimize=True`` flag to calls of ``yacc.yacc`` (as already done for + ``lex.lex``) to allow running in ``python -OO`` session without raising an + exception in ``astropy.units.format``. [#10379] + +- Shortened FITS comment strings for some D2IM and CPDIS FITS keywords to + reduce the number of FITS ``VerifyWarning`` warnings when working with WCSes + containing lookup table distortions. [#10513] + +- When importing astropy without first building the extension modules first, + raise an error directly instead of trying to auto-build. [#10883] + + + +Version 4.1 (2020-10-21) +======================== + +New Features +------------ + +astropy.config +^^^^^^^^^^^^^^ + +- Add new function ``generate_config`` to generate the configuration file and + include it in the documentation. [#10148] + +- ``ConfigNamespace.__iter__`` and ``ConfigNamespace.keys`` now yield ``ConfigItem`` + names defined within it. Similarly, ``items`` and ``values`` would yield like a + Python dictionary would. [#10139] + +astropy.coordinates +^^^^^^^^^^^^^^^^^^^ + +- Added a new ``SpectralCoord`` class that can be used to define spectral + coordinates and transform them between different velocity frames. [#10185] + +- Angle parsing now supports ``cardinal direction`` in the cases + where angles are initialized as ``string`` instances. eg ``"17°53'27"W"``.[#9859] + +- Allow in-place modification of array-valued ``Frame`` and ``SkyCoord`` objects. + This provides limited support for updating coordinate data values from another + coordinate object of the same class and equivalent frame attributes. [#9857] + +- Added a robust equality operator for comparing ``SkyCoord``, frame, and + representation objects. A comparison like ``sc1 == sc2`` will now return a + boolean or boolean array where the objects are strictly equal in all relevant + frame attributes and coordinate representation values. [#10154] + +- Added the True Equator Mean Equinox (TEME) frame. [#10149] + +- The ``Galactocentric`` frame will now use the "latest" parameter definitions + by default. This currently corresponds to the values defined in v4.0, but will + change with future releases. [#10238] + +- The ``SkyCoord.from_name()`` and Sesame name resolving functionality now is + able to cache results locally and will do so by default. [#9162] + +- Allow in-place modification of array-valued ``Representation`` and ``Differential`` + objects, including of representations with attached differentials. [#10210] + +astropy.io.ascii +^^^^^^^^^^^^^^^^ + +- Functional Units can now be processed in CDS-tables. [#9971] + +- Allow reading in ASCII tables which have duplicate column names. [#9939] + +- Fixed failure of ASCII ``fast_reader`` to handle ``names``, ``include_names``, + ``exclude_names`` arguments for ``RDB`` formatted tables. Homogenised checks + and exceptions for invalid ``names`` arguments. Improved performance when + parsing "wide" tables with many columns. [#10306] + +- Added type validation of key arguments in calls to ``io.ascii.read()`` and + ``io.ascii.write()`` functions. [#10005] + +astropy.io.misc +^^^^^^^^^^^^^^^ +- Added serialization of parameter constraints fixed and bounds. [#10082] + +- Added 'functional_models.py' and 'physical_models.py' to asdf/tags/transform, + with to allow serialization of all functional and physical models. [#10028, #10293] + +- Fix ASDF serialization of circular model inverses, and remove explicit calls + to ``asdf.yamlutil`` functions that became unnecessary in asdf 2.6.0. [#10189, #10384] + +astropy.io.fits +^^^^^^^^^^^^^^^ + +- Added support for writing Dask arrays to disk efficiently for ``ImageHDU`` and + ``PrimaryHDU``. [#9742] + +- Add HDU name and ver to FITSDiff report where appropriate [#10197] + +astropy.io.votable +^^^^^^^^^^^^^^^^^^ + +- New ``exceptions.conf.max_warnings`` configuration item to control the number of times a + type of warning appears before being suppressed. [#10152] + +- No longer ignore attributes whose values were specified as empty + strings. [#10583] + +astropy.modeling +^^^^^^^^^^^^^^^^ +- Added Plummer1D model to ``functional_models``. [#9896] + +- Added ``UnitsMapping`` model and ``Model.coerce_units`` to support units on otherwise + unitless models. [#9936] + +- Added ``domain`` and ``window`` attributes to ``repr`` and ``str``. Fixed bug with + ``_format_repr`` in core.py. [#9941] + +- Polynomial attributes ``domain`` and ``window`` are now tuples of size 2 and are + validated. `repr` and `print` show only their non-default values. [#10145] + +- Added ``replace_submodel()`` method to ``CompoundModel`` to modify an + existing instance. [#10176] + +- Delay construction of ``CompoundModel`` inverse until property is accessed, + to support ASDF deserialization of circular inverses in component models. [#10384] + +astropy.nddata +^^^^^^^^^^^^^^ + +- Added support in the ``bitmask`` module for using mnemonic bit flag names + when specifying the bit flags to be used or ignored when converting a bit + field to a boolean. [#10095, #10208] + +- Added ``reshape_as_blocks`` function to reshape a data array into + blocks, which is useful to efficiently apply functions on block + subsets of the data instead of using loops. The reshaped array is a + view of the input data array. [#10214] + +- Added a ``cache`` keyword option to allow caching for ``CCDData.read`` if + filename is a URL. [#10265] + +astropy.table +^^^^^^^^^^^^^ + +- Added ability to specify a custom matching function for table joins. In + particular this makes it possible to do cross-match table joins on ``SkyCoord``, + ``Quantity``, or standard columns, where column entries within a specified + distance are considered to be matched. [#10169] + +- Added ``units`` and ``descriptions`` keyword arguments to the Table object + initialization and ``Table.read()`` methods. This allows directly setting + the ``unit`` and ``description`` for the table columns at the time of + creating or reading the table. [#9671] + +- Make table ``Row`` work as mappings, by adding ``.keys()`` and ``.values()`` + methods. With this ``**row`` becomes possible, as does, more simply, turning + a ``Row`` into a dictionary with ``dict(row)``. [#9712] + +- Added two new ``Table`` methods ``.items()`` and ``.values()``, which return + respectively ``tbl.columns.items()`` (iterator over name, column tuples) and + ``tbl.columns.values()`` (list of columns) for a ``Table`` object ``tbl``. [#9780] + +- Added new ``Table`` method ``.round()``, which rounds numeric columns to the + specified number of decimals. [#9862] + +- Updated ``to_pandas()`` and ``from_pandas()`` to use and support Pandas + nullable integer data type for masked integer data. [#9541] + +- The HDF5 writer, ``write_table_hdf5()``, now allows passing through + additional keyword arguments to the ``h5py.Group.create_dataset()``. [#9602] + +- Added capability to add custom table attributes to a ``Table`` subclass. + These attributes are persistent and can be set during table creation. [#10097] + +- Added support for ``SkyCoord`` mixin columns in ``dstack``, ``vstack`` and + ``insert_row`` functions. [#9857] + +- Added support for coordinate ``Representation`` and ``Differential`` mixin + columns. [#10210] + +astropy.time +^^^^^^^^^^^^ + +- Added a new time format ``unix_tai`` which is essentially Unix time but with + leap seconds included. More precisely, this is the number of seconds since + ``1970-01-01 00:00:08 TAI`` and corresponds to the ``CLOCK_TAI`` clock + available on some linux platforms. [#10081] + +astropy.units +^^^^^^^^^^^^^ + +- Added ``torr`` pressure unit. [#9787] + +- Added the ``equal_nan`` keyword argument to ``isclose`` and ``allclose``, and + updated the docstrings. [#9849] + +- Added ``Rankine`` temperature unit. [#9916] + +- Added integrated flux unit conversion to ``spectral_density`` equivalency. + [#10015] + +- Changed ``pixel_scale`` equivalency to allow scales defined in any unit. + [#10123] + +- The ``quantity_input`` decorator now optionally allows passing through + numeric values or numpy arrays with numeric dtypes to arguments where + ``dimensionless_unscaled`` is an allowed unit. [#10232] + +astropy.utils +^^^^^^^^^^^^^ + +- Added a new ``MetaAttribute`` class to support easily adding custom attributes + to a subclass of classes like ``Table`` or ``NDData`` that have a ``meta`` + attribute. [#10097] + +astropy.visualization +^^^^^^^^^^^^^^^^^^^^^ + +- Added ``invalid`` keyword to ``SqrtStretch``, ``LogStretch``, + ``PowerStretch``, and ``ImageNormalize`` classes and the + ``simple_norm`` function. This keyword is used to replace generated + NaN values. [#10182] + +- Fixed an issue where ticks were sometimes not drawn at the edges of a spherical + projection on a WCSAxes. [#10442] + +astropy.wcs +^^^^^^^^^^^ + +- WCS objects with a spectral axis will now return ``SpectralCoord`` + objects when calling ``pixel_to_world`` instead of ``Quantity``, + and can now take either ``Quantity`` or ``SpectralCoord`` as input + to ``pixel_to_world``. [#10185] + +- Implemented support for the ``-TAB`` algorithm (WCS Paper III). [#9641] + +- Added an ``_as_mpl_axes`` method to the ``HightLevelWCSWrapper`` class. [#10138] + +- Add .upper() to ctype or ctype names to wcsapi/fitwcs.py to mitigate bugs from + unintended lower/upper case issues [#10557] + +API Changes +----------- + +astropy.coordinates +^^^^^^^^^^^^^^^^^^^ + +- The equality operator for comparing ``SkyCoord``, frame, and representation + objects was changed. A comparison like ``sc1 == sc2`` was previously + equivalent to ``sc1 is sc2``. It will now return a boolean or boolean array + where the objects are strictly equal in all relevant frame attributes and + coordinate representation values. If the objects have different frame + attributes or representation types then an exception will be raised. [#10154] + +- ```SkyCoord.radial_velocity_correction``` now allows you to pass an ```obstime``` directly + when the ```SkyCoord``` also has an ```obstime``` set. In this situation, the position of the + ```SkyCoord``` has space motion applied to correct to the passed ```obstime```. This allows + mm/s radial velocity precision for objects with large space motion. [#10094] + +- For consistency with other astropy classes, coordinate ``Representations`` + and ``Differentials`` can now be initialized with an instance of their own class + if that instance is passed in as the first argument. [#10210] + +astropy.io.ascii +^^^^^^^^^^^^^^^^ + +- Changed the behavior when reading a table where both the ``names`` argument + is provided (to specify the output column names) and the ``converters`` + argument is provided (to specify column conversion functions). Previously the + ``converters`` dict names referred to the *input* table column names, but now + they refer to the *output* table column names. [#9739] + +astropy.io.votable +^^^^^^^^^^^^^^^^^^ + +- For FIELDs with datatype="char", store the values as strings instead + of bytes. [#9505] + +astropy.table +^^^^^^^^^^^^^ + +- ``Table.from_pandas`` now supports a ``units`` dictionary as argument to pass units + for columns in the ``DataFrame``. [#9472] + +astropy.time +^^^^^^^^^^^^ + +- Require that ``in_subfmt`` and ``out_subfmt`` properties of a ``Time`` object + have allowed values at the time of being set, either when creating the object + or when setting those properties on an existing ``Time`` instance. Previously + the validation of those properties was not strictly enforced. [#9868] + +astropy.utils +^^^^^^^^^^^^^ + +- Changed the exception raised by ``get_readable_fileobj`` on missing + compression modules (for ``bz2`` or ``lzma``/``xz`` support) to + ``ModuleNotFoundError``, consistent with ``io.fits`` file handlers. [#9761] + +astropy.visualization +^^^^^^^^^^^^^^^^^^^^^ + +- Deprecated the ``imshow_only_kwargs`` keyword in ``imshow_norm``. + [#9915] + +- Non-finite input values are now automatically excluded in + ``HistEqStretch`` and ``InvertedHistEqStretch``. [#10177] + +- The ``PowerDistStretch`` and ``InvertedPowerDistStretch`` ``a`` + value is restricted to be ``a >= 0`` in addition to ``a != 1``. + [#10177] + +- The ``PowerStretch``, ``LogStretch``, and ``InvertedLogStretch`` + ``a`` value is restricted to be ``a > 0``. [#10177] + +- The ``AsinhStretch`` and ``SinhStretch`` ``a`` value is restricted + to be ``0 < a <= 1``. [#10177] + +Bug Fixes +--------- + +astropy.coordinates +^^^^^^^^^^^^^^^^^^^ + +- Fix a bug where for light deflection by the Sun it was always assumed that the + source was at infinite distance, which in the (rare and) absolute worst-case + scenario could lead to errors up to 3 arcsec. [#10666] + +astropy.io.votable +^^^^^^^^^^^^^^^^^^ + +- For FIELDs with datatype="char", store the values as strings instead + of bytes. [#9505] + +astropy.table +^^^^^^^^^^^^^ + +- Fix a bug that prevented ``Time`` columns from being used to sort a table. + [#10824] + +astropy.wcs +^^^^^^^^^^^ + +- WCS objects with a spectral axis will now return ``SpectralCoord`` + objects when calling ``pixel_to_world`` instead of ``Quantity`` + (note that ``SpectralCoord`` is a sub-class of ``Quantity``). [#10185] + +- Add .upper() to ctype or ctype names to wcsapi/fitwcs.py to mitigate bugs from + unintended lower/upper case issues [#10557] + +- Added bounds to ``fit_wcs_from_points`` to ensure CRPIX is on + input image. [#10346] + + +Other Changes and Additions +--------------------------- + +- The way in which users can specify whether to build astropy against + existing installations of C libraries rather than the bundled one + has changed, and should now be done via environment variables rather + than setup.py flags (e.g. --use-system-erfa). The available variables + are ``ASTROPY_USE_SYSTEM_CFITSIO``, ``ASTROPY_USE_SYSTEM_ERFA``, + ``ASTROPY_USE_SYSTEM_EXPAT``, ``ASTROPY_USE_SYSTEM_WCSLIB``, and + ``ASTROPY_USE_SYSTEM_ALL``. These should be set to ``1`` to build + against the system libraries. [#9730] + +- The infrastructure of the package has been updated in line with the + APE 17 roadmap (https://github.com/astropy/astropy-APEs/blob/main/APE17.rst). + The main changes are that the ``python setup.py test`` and + ``python setup.py build_docs`` commands will no longer work. The easiest + way to replicate these commands is to install the tox + (https://tox.readthedocs.io) package and run ``tox -e test`` and + ``tox -e build_docs``. It is also possible to run pytest and sphinx + directly. Other significant changes include switching to setuptools_scm to + manage the version number, and adding a ``pyproject.toml`` to opt in to + isolated builds as described in PEP 517/518. [#9726] + +- Bundled ``expat`` is updated to version 2.2.9. [#10038] + +- Increase minimum asdf version to 2.6.0. [#10189] + +- The bundled version of PLY was updated to 3.11. [#10258] + +- Removed dependency on scikit-image. [#10214] + +Version 4.0.5 (2021-03-26) +========================== + +Bug Fixes +--------- + +astropy.io.fits +^^^^^^^^^^^^^^^ + +- Fix bug where manual fixes to invalid header cards were not preserved when + saving a FITS file. [#11108] + +- Fix parsing of RVKC header card patterns that were not recognised + where multiple spaces were separating field-specifier and value like + "DP1.AXIS.1: 1". [#11301] + +- Fix misleading missing END card error when extra data are found at the end + of the file. [#11285] + +- Fix incorrect wrapping of long card values as CONTINUE cards when some + words in the value are longer than a single card. [#11304] + +astropy.io.misc +^^^^^^^^^^^^^^^ + +- Fixed problem when writing serialized metadata to HDF5 using h5py >= 3.0. + With the newer h5py this was writing the metadata table as a variable-length + string array instead of the previous fixed-length bytes array. Fixed astropy + to force using a fixed-length bytes array. [#11359] + +astropy.modeling +^^^^^^^^^^^^^^^^ + +- Change ``Voigt1D`` function to use Humlicek's approximation to avoid serious + inaccuracies + option to use (compiled) ``scipy.special.wofz`` error function + for yet more accurate results. [#11177] + +astropy.table +^^^^^^^^^^^^^ + +- Fixed bug when initializing a ``Table`` with a column as list of ``Quantity``, + for example ``Table({'x': [1*u.m, 2*u.m]})``. Previously this resulted in an + ``object`` dtype with no column ``unit`` set, but now gives a float array with + the correct unit. [#11329] + +- Fixed byteorder conversion in ``to_pandas()``, which had incorrectly + triggered swapping when native endianness was stored with explicit + ``dtype`` code ``'<'`` (or ``'>'``) instead of ``'='``. [#11288, #11294] + +- Fixed a compatibility issue with numpy 1.21. Initializing a Table with a + column like ``['str', np.ma.masked]`` was failing in tests due to a change in + numpy. [#11364] + +- Fixed bug when validating the inputs to ``table.hstack``, ``table.vstack``, + and ``table.dstack``. Previously, mistakenly calling ``table.hstack(t1, t2)`` + (instead of ``table.hstack([t1, t2]))`` would return ``t1`` instead of raising + an exception. [#11336] + +- Fixed byteorder conversion in ``to_pandas()``, which had incorrectly + triggered swapping when native endianness was stored with explicit + ``dtype`` code ``'<'`` (or ``'>'``) instead of ``'='``. [#11288] + +astropy.time +^^^^^^^^^^^^ + +- Fix leap second update when using a non english locale. [#11062] + +- Fix default assumed location to be the geocenter when transforming times + to and from solar-system barycenter scales. [#11134] + +- Fix inability to write masked times with ``formatted_value``. [#11195] + +astropy.units +^^^^^^^^^^^^^ + +- Ensure ``keepdims`` works for taking ``mean``, ``std``, and ``var`` of + ``Quantity``. [#11198] + +- For ``Quantity.to_string()``, ensure that the precision argument is also + used when the format is not latex. [#11145] + +astropy.wcs +^^^^^^^^^^^ + +- Allow "un-setting" of auxiliary WCS parameters in the ``aux`` attribute of + ``Wcsprm``. [#11166] + + + + + +Version 4.0.4 (2020-11-24) +========================== + +Bug Fixes +--------- + +astropy.coordinates +^^^^^^^^^^^^^^^^^^^ + +- The ``norm()`` method for ``RadialDifferential`` no longer requires ``base`` + to be specified. The ``norm()`` method for other non-Cartesian differential + classes now gives a clearer error message if ``base`` is not specified. [#10969] + +- The transformations between ``ICRS`` and any of the heliocentric ecliptic + frames (``HeliocentricMeanEcliptic``, ``HeliocentricTrueEcliptic``, and + ``HeliocentricEclipticIAU76``) now correctly account for the small motion of + the Sun when transforming a coordinate with velocity information. [#10970] + +astropy.io.ascii +^^^^^^^^^^^^^^^^ + +- Partially fixed a performance issue when reading in parallel mode. Parallel + reading currently has substantially worse performance than the default serial + reading, so we now ignore the parallel option and fall back to serial reading. + [#10880] + +- Fixed a bug where "" (blank string) as input data for a boolean type column + was causing an exception instead of indicating a masked value. As a + consequence of the fix, the values "0" and "1" are now also allowed as valid + inputs for boolean type columns. These new allowed values apply for both ECSV + and for basic character-delimited data files ('basic' format with appropriate + ``converters`` specified). [#10995] + +astropy.modeling +^^^^^^^^^^^^^^^^ + +- Fixed use of weights with ``LinearLSQFitter``. [#10687] + +astropy.stats +^^^^^^^^^^^^^ + +- Fixed an issue in biweight stats when MAD=0 to give the same output + with and without an input ``axis``. [#10912] + +astropy.time +^^^^^^^^^^^^ + +- Fix a problem with the ``plot_date`` format for matplotlib >= 3.3 caused by + a change in the matplotlib plot date default reference epoch in that release. + [#10876] + +- Improve initialization time by a factor of four when creating a scalar ``Time`` + object in a format like ``unix`` or ``cxcsec`` (time delta from a reference + epoch time). [#10406] + +astropy.visualization +^^^^^^^^^^^^^^^^^^^^^ + +- Fixed the calculation of the tight bounding box of a ``WCSAxes``. This should + also significantly improve the application of ``tight_layout()`` to figures + containing ``WCSAxes``. [#10797] + + +Version 4.0.3 (2020-10-14) +========================== + +Bug Fixes +--------- + +astropy.table +^^^^^^^^^^^^^ + +- Fixed a small bug where initializing an empty ``Column`` with a structured dtype + with a length and a shape failed to give the requested dtype. [#10819] + +Other Changes and Additions +--------------------------- + +- Fixed installation of the source distribution with pip<19. [#10837, #10852] + + +Version 4.0.2 (2020-10-10) +========================== + +New Features +------------ + +astropy.utils +^^^^^^^^^^^^^ + +- ``astropy.utils.data.download_file`` now supports FTPS/FTP over TLS. [#9964] + +- ``astropy.utils.data`` now uses a lock-free mechanism for caching. This new + mechanism uses a new cache layout and so ignores caches created using earlier + mechanisms (which were causing lockups on clusters). The two cache formats can + coexist but do not share any files. [#10437, #10683] + +- ``astropy.utils.data`` now ignores the config item + ``astropy.utils.data.conf.download_cache_lock_attempts`` since no locking is + done. [#10437, #10683] + +- ``astropy.utils.data.download_file`` and related functions now interpret the + parameter or config file setting ``timeout=0`` to mean they should make no + attempt to download files. [#10437, #10683] + +- ``astropy.utils.import_file_to_cache`` now accepts a keyword-only argument + ``replace``, defaulting to True, to determine whether it should replace existing + files in the cache, in a way as close to atomic as possible. [#10437, #10683] + +- ``astropy.utils.data.download_file`` and related functions now treat + ``http://example.com`` and ``http://example.com/`` as equivalent. [#10631] + +astropy.wcs +^^^^^^^^^^^ + +- The new auxiliary WCS parameters added in WCSLIB 7.1 are now exposed as + the ``aux`` attribute of ``Wcsprm``. [#10333] + +- Updated bundled version of ``WCSLIB`` to v7.3. [#10433] + + +Bug fixes +--------- + +astropy.config +^^^^^^^^^^^^^^ + +- Added an extra fallback to ``os.expanduser('~')`` when trying to find the + user home directory. [#10570] + +astropy.constants +^^^^^^^^^^^^^^^^^ + +- Corrected definition of parsec to 648 000 / pi AU following IAU 2015 B2 [#10569] + +astropy.convolution +^^^^^^^^^^^^^^^^^^^ + +- Fixed a bug where a float-typed integers in the argument ``x_range`` of + ``astropy.convolution.utils.discretize_oversample_1D`` (and the 2D version as + well) fails because it uses ``numpy.linspace``, which requires an ``int``. + [#10696] + +astropy.coordinates +^^^^^^^^^^^^^^^^^^^ + +- Ensure that for size-1 array ``SkyCoord`` and coordinate frames + the attributes also properly become scalars when indexed with 0. + [#10113] + +- Fixed a bug where ``SkyCoord.separation()`` and ``SkyCoord.separation_3d`` + were not accepting a frame object. [#10332] + +- Ensure that the ``lon`` values in ``SkyOffsetFrame`` are wrapped correctly at + 180 degree regardless of how the underlying data is represented. [#10163] + +- Fixed an error in the obliquity of the ecliptic when transforming to/from the + ``*TrueEcliptic`` coordinate frames. The error would primarily result in an + inaccuracy in the ecliptic latitude on the order of arcseconds. [#10129] + +- Fixed an error in the computation of the location of solar system bodies where the + Earth location of the observer was ignored during the correction for light travel + time. [#10292] + +- Ensure that coordinates with proper motion that are transformed to other + coordinate frames still can be represented properly. [#10276] + +- Improve the error message given when trying to get a cartesian representation + for coordinates that have both proper motion and radial velocity, but no + distance. [#10276] + +- Fixed an error where ``SkyCoord.apply_space_motion`` would return incorrect + results when no distance is set and proper motion is high. [#10296] + +- Make the parsing of angles thread-safe so that ``Angle`` can be used in + Python multithreading. [#10556] + +- Fixed reporting of ``EarthLocation.info`` which previously raised an exception. + [#10592] + +astropy.io.ascii +^^^^^^^^^^^^^^^^ + +- Fixed a bug with the C ``fast_reader`` not correctly parsing newlines when + ``delimiter`` was also set to ``\n`` or ``\r``; ensured consistent handling + of input strings without newline characters. [#9929] + +astropy.io.fits +^^^^^^^^^^^^^^^ + +- Fix integer formats of ``TFORMn=Iw`` columns in ASCII tables to correctly read + values exceeding int32 - setting int16, int32 or int64 according to ``w``. [#9901] + +- Fix unclosed memory-mapped FITS files in ``FITSDiff`` when difference found. + [#10159] + +- Fix crash when reading an invalid table file. [#10171] + +- Fix duplication issue when setting a keyword ending with space. [#10482] + +- Fix ResourceWarning with ``fits.writeto`` and ``pathlib.Path`` object. + [#10599] + +- Fix repr for commentary cards and strip spaces for commentary keywords. + [#10640] + +- Fix compilation of cfitsio with Xcode 12. [#10772] + +- Fix handling of 1-dimensional arrays with a single element in ``BinTableHDU`` [#10768] + +astropy.io.misc +^^^^^^^^^^^^^^^ + +- Fix id URL in ``baseframe-1.0.0`` ASDF schema. [#10223] + +- Write keys to ASDF only if the value is present, to account + for a change in behavior in asdf 2.8. [#10674] + +astropy.io.registry +^^^^^^^^^^^^^^^^^^^ + +- Fix ``Table.(read|write).help`` when reader or writer has no docstring. [#10460] + +astropy.io.votable +^^^^^^^^^^^^^^^^^^ + +- Fixed parsing failure of VOTable with no fields. When detecting a non-empty + table with no fields, the following warning/exception is issued: + E25 "No FIELDs are defined; DATA section will be ignored." [#10192] + +astropy.modeling +^^^^^^^^^^^^^^^^ + +- Fixed a problem with mapping ``input_units`` and ``return_units`` + of a ``CompoundModel`` to the units of the constituent models. [#10158] + +- Removed hard-coded names of inputs and outputs. [#10174] + +- Fixed a problem where slicing a ``CompoundModel`` by name will crash if + there ``fix_inputs`` operators are present. [#10224] + +- Removed a limitation of fitting of data with units with compound models + without units when the expression involves operators other than addition + and subtraction. [#10415] + +- Fixed a problem with fitting ``Linear1D`` and ``Planar2D`` in model sets. [#10623] + +- Fixed reported module name of ``math_functions`` model classes. [#10694] + +- Fixed reported module name of ``tabular`` model classes. [#10709] + +- Do not create new ``math_functions`` models for ufuncs that are + only aliases (divide and mod). [#10697] + +- Fix calculation of the ``Moffat2D`` derivative with respect to gamma. [#10784] + +astropy.stats +^^^^^^^^^^^^^ + +- Fixed an API regression where ``SigmaClip.__call__`` would convert masked + elements to ``nan`` and upcast the dtype to ``float64`` in its output + ``MaskedArray`` when using the ``axis`` parameter along with the defaults + ``masked=True`` and ``copy=True``. [#10610] + +- Fixed an issue where fully masked ``MaskedArray`` input to + ``sigma_clipped_stats`` gave incorrect results. [#10099] + +- Fixed an issue where ``sigma_clip`` and ``SigmaClip.__call__`` + would return a masked array instead of a ``ndarray`` when + ``masked=False`` and the input was a full-masked ``MaskedArray``. + [#10099] + +- Fixed bug with ``funcs.poisson_conf_interval`` where an integer for N + with ``interval='kraft-burrows-nousek'`` would throw an error with + mpmath backend. [#10427] + +- Fixed bug in ``funcs.poisson_conf_interval`` with + ``interval='kraft-burrows-nousek'`` where certain combinations of source + and background count numbers led to ``ValueError`` due to the choice of + starting value for numerical optimization. [#10618] + +astropy.table +^^^^^^^^^^^^^ + +- Fixed a bug when writing a table with mixin columns to FITS, ECSV or HDF5. + If one of the data attributes of the mixin (e.g. ``skycoord.ra``) had the + same name as one of the table column names (``ra``), the column (``ra``) + would be dropped when reading the table back. [#10222] + +- Fixed a bug when sorting an indexed table on the indexed column after first + sorting on another column. [#10103] + +- Fixed a bug in table argsort when called with ``reverse=True`` for an + indexed table. [#10103] + +- Fixed a performance regression introduced in #9048 when initializing a table + from Python lists. Also fixed incorrect behavior (for data types other than + float) when those lists contain ``np.ma.masked`` elements to indicate masked + data. [#10636] + +- Avoid modifying ``.meta`` when serializing columns to FITS. [#10485] + +- Avoid crash when reading a FITS table that contains mixin info and PyYAML + is missing. [#10485] + +astropy.time +^^^^^^^^^^^^ + +- Ensure that for size-1 array ``Time``, the location also properly becomes + a scalar when indexed with 0. [#10113] + +astropy.units +^^^^^^^^^^^^^ + +- Refined test_parallax to resolve difference between 2012 and 2015 definitions. [#10569] + +astropy.utils +^^^^^^^^^^^^^ + +- The default IERS server has been updated to use the FTPS server hosted by + CDDIS. [#9964] + +- Fixed memory allocation on 64-bit systems within ``xml.iterparse`` [#10076] + +- Fix case where ``None`` could be used in a numerical computation. [#10126] + +astropy.visualization +^^^^^^^^^^^^^^^^^^^^^ + +- Fixed a bug where the ``ImageNormalize`` ``clip`` keyword was + ignored when used with calling the object on data. [#10098] + +- Fixed a bug where ``axes.xlabel``/``axes.ylabel`` where not correctly set + nor returned on an ``EllipticalFrame`` class ``WCSAxes`` plot. [#10446] + +astropy.wcs +^^^^^^^^^^^ + +- Handled WCS 360 -> 0 deg crossover in ``fit_wcs_from_points`` [#10155] + +- Do not issue ``DATREF`` warning when ``MJDREF`` has default value. [#10440] + +- Fixed a bug due to which ``naxis`` argument was ignored if ``header`` + was supplied during the initialization of a WCS object. [#10532] + +Other Changes and Additions +--------------------------- + +- Improved the speed of sorting a large ``Table`` on a single column by a factor + of around 5. [#10103] + +- Ensure that astropy can be used inside Application bundles built with + pyinstaller. [#8795] + +- Updated the bundled CFITSIO library to 3.49. See + ``cextern/cfitsio/docs/changes.txt`` for additional information. + [#10256, #10665] + +- ``extract_array`` raises a ``ValueError`` if the data type of the + input array is inconsistent with the ``fill_value``. [#10602] + + +Version 4.0.1 (2020-03-27) +========================== + +Bug fixes +--------- + +astropy.config +^^^^^^^^^^^^^^ + +- Fixed a bug where importing a development version of a package that uses + ``astropy`` configuration system can result in a + ``~/.astropy/config/package..cfg`` file. [#9975] + +astropy.coordinates +^^^^^^^^^^^^^^^^^^^ + +- Fixed a bug where a vestigal trace of a frame class could persist in the + transformation graph even after the removal of all transformations involving + that frame class. [#9815] + +- Fixed a bug with ``TransformGraph.remove_transform()`` when the "from" and + "to" frame classes are not explicitly specified. [#9815] + +- Read-only longitudes can now be passed in to ``EarthLocation`` even if + they include angles outside of the range of -180 to 180 degrees. [#9900] + +- ```SkyCoord.radial_velocity_correction``` no longer raises an Exception + when space motion information is present on the SkyCoord. [#9980] + +astropy.io +^^^^^^^^^^ + +- Fixed a bug that prevented the unified I/O infrastructure from working with + datasets that are represented by directories rather than files. [#9866] + +astropy.io.ascii +^^^^^^^^^^^^^^^^ + +- Fixed a bug in the ``fast_reader`` C parsers incorrectly returning entries + of isolated positive/negative signs as ``float`` instead of ``str``. [#9918] + +- Fixed a segmentation fault in the ``fast_reader`` C parsers when parsing an + invalid file with ``guess=True`` and the file contains inconsistent column + numbers in combination with a quoted field; e.g., ``"1 2\n 3 4 '5'"``. + [#9923] + +- Magnitude, decibel, and dex can now be stored in ``ecsv`` files. [#9933] + +astropy.io.misc +^^^^^^^^^^^^^^^ + +- Magnitude, decibel, and dex can now be stored in ``hdf5`` files. [#9933] + +- Fixed serialization of polynomial models to include non default values of + domain and window values. [#9956, #9961] + +- Fixed a bug which affected overwriting tables within ``hdf5`` files. + Overwriting an existing path with associated column meta data now also + overwrites the meta data associated with the table. [#9950] + +- Fixed serialization of Time objects with location under time-1.0.0 + ASDF schema. [#9983] + +astropy.io.fits +^^^^^^^^^^^^^^^ + +- Fix regression with ``GroupsHDU`` which needs to modify the header to handle + invalid headers, and fix accessing ``.data`` for empty HDU. [#9711, #9934] + +- Fix ``fitsdiff`` when its arguments are directories that contain other + directories. [#9711] + +- Fix writing noncontiguous data to a compressed HDU. [#9958] + +- Added verification of ``disp`` (``TDISP``) keyword to ``fits.Column`` and + extended tests for ``TFORM`` and ``TDISP`` validation. [#9978] + +- Fix checksum verification to process all HDUs instead of only the first one + because of the lazy loading feature. [#10012] + +- Allow passing ``output_verify`` to ``.close`` when using the context manager. + [#10030] + +- Prevent instantiation of ``PrimaryHDU`` and ``ImageHDU`` with a scalar. + [#10041] + +- Fix column access by attribute with FITS_rec: columns with scaling or columns + from ASCII tables where not properly converted when accessed by attribute + name. [#10069] + +astropy.io.misc +^^^^^^^^^^^^^^^ + +- Magnitude, decibel, and dex can now be stored in ``hdf5`` files. [#9933] + +- Fixed serialization of polynomial models to include non default values of + domain and window values. [#9956, #9961] + +- Fixed a bug which affected overwriting tables within ``hdf5`` files. + Overwriting an existing path with associated column meta data now also + overwrites the meta data associated with the table. [#9950] + +- Fixed serialization of Time objects with location under time-1.0.0 + ASDF schema. [#9983] + +astropy.modeling +^^^^^^^^^^^^^^^^ + +- Fixed a bug in setting default values of parameters of orthonormal + polynomials when constructing a model set. [#9987] + +astropy.table +^^^^^^^^^^^^^ + +- Fixed bug in ``Table.reverse`` for tables that contain non-mutable mixin columns + (like ``SkyCoord``) for which in-place item update is not allowed. [#9839] + +- Tables containing Magnitude, decibel, and dex columns can now be saved to + ``ecsv`` files. [#9933] + +- Fixed bug where adding or inserting a row fails on a table with an index + defined on a column that is not the first one. [#10027] + +- Ensured that ``table.show_in_browser`` also worked for mixin columns like + ``Time`` and ``SkyCoord``. [#10068] + +astropy.time +^^^^^^^^^^^^ + +- Fix inaccuracy when converting between TimeDelta and datetime.timedelta. [#9679] + +- Fixed exception when changing ``format`` in the case when ``out_subfmt`` is + defined and is incompatible with the new format. [#9812] + +- Fixed exceptions in ``Time.to_value()``: when supplying any ``subfmt`` argument + for string-based formats like 'iso', and for ``subfmt='long'`` for the formats + 'byear', 'jyear', and 'decimalyear'. [#9812] + +- Fixed bug where the location attribute was lost when creating a new ``Time`` + object from an existing ``Time`` or list of ``Time`` objects. [#9969] + +- Fixed a bug where an exception occurred when creating a ``Time`` object + if the ``val1`` argument was a regular double and the ``val2`` argument + was a ``longdouble``. [#10034] + +astropy.timeseries +^^^^^^^^^^^^^^^^^^ + +- Fixed issue with reference time for the ``transit_time`` parameter returned by + the ``BoxLeastSquares`` periodogram. Now, the ``transit_time`` will be within + the range of the input data and arbitrary time offsets/zero points no longer + affect results. [#10013] + +astropy.units +^^^^^^^^^^^^^ + +- Fix for ``quantity_input`` annotation raising an exception on iterable + types that don't define a general ``__contains__`` for checking if ``None`` + is contained (e.g. Enum as of python3.8), by instead checking for instance of + Sequence. [#9948] + +- Fix for ``u.Quantity`` not taking into account ``ndmin`` if constructed from + another ``u.Quantity`` instance with different but convertible unit [#10066] + +astropy.utils +^^^^^^^^^^^^^ + +- Fixed ``deprecated_renamed_argument`` not passing in user value to + deprecated keyword when the keyword has no new name. [#9981] + +- Fixed ``deprecated_renamed_argument`` not issuing a deprecation warning when + deprecated keyword without new name is passed in as positional argument. + [#9985] + +- Fixed detection of read-only filesystems in the caching code. [#10007] + +astropy.visualization +^^^^^^^^^^^^^^^^^^^^^ + +- Fixed bug from matplotlib >=3.1 where an empty Quantity array is + sent for unit conversion as an empty list. [#9848] + +- Fix bug in ``ZScaleInterval`` to return the array minimum and + maximum when there are less then ``min_npixels`` in the input array. [#9913] + +- Fix a bug in simplifying axis labels that affected non-rectangular frames. + [#8004, #9991] + + +Other Changes and Additions +--------------------------- + +- Increase minimum asdf version to 2.5.2. [#9996, #9819] + +- Updated bundled version of ``WCSLIB`` to v7.2. [#10021] + + + +Version 4.0 (2019-12-16) +======================== + +New Features +------------ + +astropy.config +^^^^^^^^^^^^^^ + +- The config and cache directories and the name of the config file are now + customizable. This allows affiliated packages to put their configuration + files in locations other than ``CONFIG_DIR/.astropy/``. [#8237] + +astropy.constants +^^^^^^^^^^^^^^^^^ + +- The version of constants can be specified via ScienceState in a way + that ``constants`` and ``units`` will be consistent. [#8517] + +- Default constants now use CODATA 2018 and IAU 2015 definitions. [#8761] + +- Constants can be pickled and unpickled. [#9377] + +astropy.convolution +^^^^^^^^^^^^^^^^^^^ + +- Fixed a bug [#9168] where having a kernel defined using unitless astropy + quantity objects would result in a crash [#9300] + +astropy.coordinates +^^^^^^^^^^^^^^^^^^^ + +- Changed ``coordinates.solar_system_ephemeris`` to also accept local files + as input. The ephemeris can now be selected by either keyword (e.g. 'jpl', + 'de430'), URL or file path. [#8767] + +- Added a ``cylindrical`` property to ``SkyCoord`` for shorthand access to a + ``CylindricalRepresentation`` of the coordinate, as is already available + for other common representations. [#8857] + +- The default parameters for the ``Galactocentric`` frame are now controlled by + a ``ScienceState`` subclass, ``galactocentric_frame_defaults``. New + parameter sets will be added to this object periodically to keep up with + ever-improved measurements of the solar position and motion. [#9346] + +- Coordinate frame classes can now have multiple aliases by assigning a list + of aliases to the class variable ``name``. Any of the aliases can be used + for attribute-style access or as the target of ``tranform_to()`` calls. + [#8834] + +- Passing a NaN to ``Distance`` no longer raises a warning. [#9598] + +astropy.cosmology +^^^^^^^^^^^^^^^^^ + +- The pre-publication Planck 2018 cosmological parameters are included as the + ``Planck2018_arXiv_v2`` object. Please note that the values are preliminary, + and when the paper is accepted a final version will be included as + ``Planck18``. [#8111] + +astropy.io.ascii +^^^^^^^^^^^^^^^^ + +- Removed incorrect warnings on ``Overflow`` when reading in + ``FloatType`` 0.0 with ``use_fast_converter``; synchronised + ``IntType`` ``Overflow`` warning messages. [#9082] + +astropy.io.misc +^^^^^^^^^^^^^^^ + +- Eliminate deprecated compatibility mode when writing ``Table`` metadata to + HDF5 format. [#8899] + +- Add support for orthogonal polynomial models to ASDF. [#9107] + +astropy.io.fits +^^^^^^^^^^^^^^^ + +- Changed the ``fitscheck`` and ``fitsdiff`` script to use the ``argparse`` + module instead of ``optparse``. [#9148] + +- Allow writing of ``Table`` objects with ``Time`` columns that are also table + indices to FITS files. [#8077] + +astropy.io.votable +^^^^^^^^^^^^^^^^^^ + +- Support VOTable version 1.4. The main addition is the new element, TIMESYS, + which allows defining of metadata for temporal coordinates much like COOSYS + defines metadata for celestial coordinates. [#9475] + +astropy.logger +^^^^^^^^^^^^^^ + +- Added a configuration option to specify the text encoding of the log file, + with the default behavior being the platform-preferred encoding. [#9203] + +astropy.modeling +^^^^^^^^^^^^^^^^ + +- Major rework of modeling internals. `See modeling documentation for details. + `_ . [#8769] + +- Add ``Tabular1D.inverse``. [#9083] + +- ``Model.rename`` was changed to add the ability to rename ``Model.inputs`` + and ``Model.outputs``. [#9220] + +- New function ``fix_inputs`` to generate new models from others by fixing + specific inputs variable values to constants. [#9135] + +- ``inputs`` and ``outputs`` are now model instance attributes, and ``n_inputs`` + and ``n_outputs`` are class attributes. Backwards compatible default + values of ``inputs`` and ``outputs`` are generated. ``Model.inputs`` and + ``Model.outputs`` are now settable which allows renaming them on per user + case. [#9298] + +- Add a new model representing a sequence of rotations in 3D around an + arbitrary number of axes. [#9369] + +- Add many of the numpy ufunc functions as models. [#9401] + +- Add ``BlackBody`` model. [#9282] + +- Add ``Drude1D`` model. [#9452] + +- Added analytical King model (KingProjectedAnalytic1D). [#9084] + +- Added Exponential1D and Logarithmic1D models. [#9351] + +astropy.nddata +^^^^^^^^^^^^^^ + +- Add a way for technically invalid but unambiguous units in a fits header + to be parsed by ``CCDData``. [#9397] + +- ``NDData`` now only accepts WCS objects which implement either the high, or + low level APE 14 WCS API. All WCS objects are converted to a high level WCS + object, so ``NDData.wcs`` now always returns a high level APE 14 object. Not + all array slices are valid for wcs objects, so some slicing operations which + used to work may now fail. [#9067] + +astropy.stats +^^^^^^^^^^^^^ + +- The ``biweight_location``, ``biweight_scale``, and + ``biweight_midvariance`` functions now allow for the ``axis`` + keyword to be a tuple of integers. [#9309] + +- Added an ``ignore_nan`` option to the ``biweight_location``, + ``biweight_scale``, and ``biweight_midvariance`` functions. [#9457] + +- A numpy ``MaskedArray`` can now be input to the ``biweight_location``, + ``biweight_scale``, and ``biweight_midvariance`` functions. [#9466] + +- Removed the warning related to p0 in the Bayesian blocks algorithm. The + caveat related to p0 is described in the docstring for ``Events``. [#9567] + +astropy.table +^^^^^^^^^^^^^ + +- Improved the implementation of ``Table.replace_column()`` to provide + a speed-up of 5 to 10 times for wide tables. The method can now accept + any input which convertible to a column of the correct length, not just + ``Column`` subclasses. [#8902] + +- Improved the implementation of ``Table.add_column()`` to provide a speed-up + of 2 to 10 (or more) when adding a column to tables, with increasing benefit + as the number of columns increases. The method can now accept any input + which is convertible to a column of the correct length, not just ``Column`` + subclasses. [#8933] + +- Changed the implementation of ``Table.add_columns()`` to use the new + ``Table.add_column()`` method. In most cases the performance is similar + or slightly faster to the previous implementation. [#8933] + +- ``MaskedColumn.data`` will now return a plain ``MaskedArray`` rather than + the previous (unintended) ``masked_BaseColumn``. [#8855] + +- Added depth-wise stacking ``dstack()`` in higher level table operation. + It help will in stacking table column depth-wise. [#8939] + +- Added a new table equality method ``values_equal()`` which allows comparison + table values to another table, list, or value, and returns an + element-by-element equality table. [#9068] + +- Added new ``join_type='cartesian'`` option to the ``join`` operation. [#9288] + +- Allow adding a table column as a list of mixin-type objects, for instance + ``t['q'] = [1 * u.m, 2 * u.m]``. [#9165] + +- Allow table ``join()`` using any sortable key column (e.g. Time), not + just ndarray subclasses. A column is considered sortable if there is a + ``.info.get_sortable_arrays()`` method that is implemented. [#9340] + +- Added ``Table.iterrows()`` for making row-wise iteration faster. [#8969] + +- Allow table to be initialized with a list of dict where the dict keys + are not the same in every row. The table column names are the set of all keys + found in the input data, and any missing key/value pairs are turned into + missing data in the table. [#9425] + +- Prevent unnecessary ERFA warnings when indexing by ``Time`` columns. [#9545] + +- Added support for sorting tables which contain non-mutable mixin columns + (like ``SkyCoord``) for which in-place item update is not allowed. [#9549] + +- Ensured that inserting ``np.ma.masked`` (or any other value with a mask) into + a ``MaskedColumn`` causes a masked entry to be inserted. [#9623] + +- Fixed a bug that caused an exception when initializing a ``MaskedColumn`` from + another ``MaskedColumn`` that has a structured dtype. [#9651] + +astropy.tests +^^^^^^^^^^^^^ + +- The plugin that handles the custom header in the test output has been + moved to the ``pytest-astropy-header plugin`` package. `See the README at + `__ for information about + using this new plugin. [#9214] + +astropy.time +^^^^^^^^^^^^ + +- Added a new time format ``ymdhms`` for representing times via year, month, + day, hour, minute, and second attributes. [#7644] + +- ``TimeDelta`` gained a ``to_value`` method, so that it becomes easier to + use it wherever a ``Quantity`` with units of time could be used. [#8762] + +- Made scalar ``Time`` and ``TimeDelta`` objects hashable based on JD, time + scale, and location attributes. [#8912] + +- Improved error message when bad input is used to initialize a ``Time`` or + ``TimeDelta`` object and the format is specified. [#9296] + +- Allow numeric time formats to be initialized with numpy ``longdouble``, + ``Decimal`` instances, and strings. One can select just one of these + using ``in_subfmt``. The output can be similarly set using ``out_subfmt``. + [#9361] + +- Introduce a new ``.to_value()`` method for ``Time`` (and adjusted the + existing method for ``TimeDelta``) so that one can get values in a given + ``format`` and possible ``subfmt`` (e.g., ``to_value('mjd', 'str')``. [#9361] + +- Prevent unnecessary ERFA warnings when sorting ``Time`` objects. [#9545] + +astropy.timeseries +^^^^^^^^^^^^^^^^^^ + +- Adding ``epoch_phase``, ``wrap_phase`` and ``normalize_phase`` keywords to + ``TimeSeries.fold()`` to control the phase of the epoch and to return + normalized phase rather than time for the folded TimeSeries. [#9455] + +astropy.uncertainty +^^^^^^^^^^^^^^^^^^^ + +- ``Distribution`` was rewritten such that it deals better with subclasses. + As a result, Quantity distributions now behave correctly with ``to`` methods + yielding new distributions of the kind expected for the starting + distribution, and ``to_value`` yielding ``NdarrayDistribution`` instances. + [#9429, #9442] + +- The ``pdf_*`` properties that were used to calculate statistical properties + of ``Distribution`` instances were changed into methods. This allows one + to pass parameters such as ``ddof`` to ``pdf_std`` and ``pdf_var`` (which + generally should equal 1 instead of the default 0), and reflects that these + are fairly involved calculations, not just "properties". [#9613] + +astropy.units +^^^^^^^^^^^^^ + +- Support for unicode parsing. Currently supported are superscripts, Ohm, + ÅngstrÃļm, and the micro-sign. [#9348] + +- Accept non-unit type annotations in @quantity_input. [#8984] + +- For numpy 1.17 and later, the new ``__array_function__`` protocol is used to + ensure that all top-level numpy functions interact properly with + ``Quantity``, preserving units also in operations like ``np.concatenate``. + [#8808] + +- Add equivalencies for surface brightness units to spectral_density. [#9282] + +astropy.utils +^^^^^^^^^^^^^ + +- ``astropy.utils.data.download_file`` and + ``astropy.utils.data.get_readable_fileobj`` now provides an ``http_headers`` + keyword to pass in specific request headers for the download. It also now + defaults to providing ``User-Agent: Astropy`` and ``Accept: */*`` + headers. The default ``User-Agent`` value can be set with a new + ``astropy.data.conf.default_http_user_agent`` configuration item. + [#9508, #9564] + +- Added a new ``astropy.utils.misc.unbroadcast`` function which can be used + to return the smallest array that can be broadcasted back to the initial + array. [#9209] + +- The specific IERS Earth rotation parameter table used for time and + coordinate transformations can now be set, either in a context or per + session, using ``astropy.utils.iers.earth_rotation_table``. [#9244] + +- Added ``export_cache`` and ``import_cache`` to permit transporting + downloaded data to machines with no Internet connection. Several new + functions are available to investigate the cache contents; e.g., + ``check_download_cache`` can be used to confirm that the persistent + cache has not become damaged. [#9182] + +- A new ``astropy.utils.iers.LeapSeconds`` class has been added to track + leap seconds. [#9365] + +astropy.visualization +^^^^^^^^^^^^^^^^^^^^^ + +- Added a new ``time_support`` context manager/function for making it easy to + plot and format ``Time`` objects in Matplotlib. [#8782] + +- Added support for plotting any WCS compliant with the generalized (APE 14) + WCS API with WCSAxes. [#8885, #9098] + +- Improved display of information when inspecting ``WCSAxes.coords``. [#9098] + +- Improved error checking for the ``slices=`` argument to ``WCSAxes``. [#9098] + +- Added support for more solar frames in WCSAxes. [#9275] + +- Add support for one dimensional plots to ``WCSAxes``. [#9266] + +- Add a ``get_format_unit`` to ``wcsaxes.CoordinateHelper``. [#9392] + +- ``WCSAxes`` now, by default, sets a default label for plot axes which is the + WCS physical type (and unit) for that axis. This can be disabled using the + ``coords[i].set_auto_axislabel(False)`` or by explicitly setting an axis + label. [#9392] + +- Fixed the display of tick labels when plotting all sky images that have a + coord_wrap less than 360. [#9542] + +astropy.wcs +^^^^^^^^^^^ + +- Added a ``astropy.wcs.wcsapi.pixel_to_pixel`` function that can be used to + transform pixel coordinates in one dataset with a WCS to pixel coordinates + in another dataset with a different WCS. This function is designed to be + efficient when the input arrays are broadcasted views of smaller + arrays. [#9209] + +- Added a ``local_partial_pixel_derivatives`` function that can be used to + determine a matrix of partial derivatives of each world coordinate with + respect to each pixel coordinate. [#9392] + +- Updated wcslib to v6.4. [#9125] + +- Improved the ``SlicedLowLevelWCS`` class in ``astropy.wcs.wcsapi`` to avoid + storing chains of nested ``SlicedLowLevelWCS`` objects when applying multiple + slicing operations in turn. [#9210] + +- Added a ``wcs_info_str`` function to ``astropy.wcs.wcsapi`` to show a summary + of an APE-14-compliant WCS as a string. [#8546, #9207] + +- Added two new optional attributes to the APE 14 low-level WCS: + ``pixel_axis_names`` and ``world_axis_names``. [#9156] + +- Updated the WCS class to now correctly take and return ``Time`` objects in + the high-level APE 14 API (e.g. ``pixel_to_world``. [#9376] + +- ``SlicedLowLevelWCS`` now raises ``IndexError`` rather than ``ValueError`` on + an invalid slice. [#9067] + +- Added ``fit_wcs_from_points`` function to ``astropy.wcs.utils``. Fits a WCS + object to set of matched detector/sky coordinates. [#9469] + +- Fix various bugs in ``SlicedLowLevelWCS`` when the WCS being sliced was one + dimensional. [#9693] + + +API Changes +----------- + +astropy.constants +^^^^^^^^^^^^^^^^^ + +- Deprecated ``set_enabled_constants`` context manager. Use + ``astropy.physical_constants`` and ``astropy.astronomical_constants``. + [#9025] + +astropy.convolution +^^^^^^^^^^^^^^^^^^^ + +- Removed the deprecated keyword argument ``interpolate_nan`` from + ``convolve_fft``. [#9356] + +- Removed the deprecated keyword argument ``stddev`` from + ``Gaussian2DKernel``. [#9356] + +- Deprecated and renamed ``MexicanHat1DKernel`` and ``MexicanHat2DKernel`` + to ``RickerWavelet1DKernel`` and ``RickerWavelet2DKernel``. [#9445] + +astropy.coordinates +^^^^^^^^^^^^^^^^^^^ + +- Removed the ``recommended_units`` attribute from Representations; it was + deprecated since 3.0. [#8892] + +- Removed the deprecated frame attribute classes, ``FrameAttribute``, + ``TimeFrameAttribute``, ``QuantityFrameAttribute``, + ``CartesianRepresentationFrameAttribute``; deprecated since 3.0. [#9326] + +- Removed ``longitude`` and ``latitude`` attributes from ``EarthLocation``; + deprecated since 2.0. [#9326] + +- The ``DifferentialAttribute`` for frame classes now passes through any input + to the ``allowed_classes`` if only one allowed class is specified, i.e. this + now allows passing a quantity in for frame attributes that use + ``DifferentialAttribute``. [#9325] + +- Removed the deprecated ``galcen_ra`` and ``galcen_dec`` attributes from the + ``Galactocentric`` frame. [#9346] + +astropy.extern +^^^^^^^^^^^^^^ + +- Remove the bundled ``six`` module. [#8315] + +astropy.io.ascii +^^^^^^^^^^^^^^^^ + +- Masked column handling has changed, see ``astropy.table`` entry below. + [#8789] + +astropy.io.misc +^^^^^^^^^^^^^^^ + +- Masked column handling has changed, see ``astropy.table`` entry below. + [#8789] + +- Removed deprecated ``usecPickle`` kwarg from ``fnunpickle`` and + ``fnpickle``. [#8890] + +astropy.io.fits +^^^^^^^^^^^^^^^ + +- Masked column handling has changed, see ``astropy.table`` entry below. + [#8789] + +- ``io.fits.Header`` has been made safe for subclasses for copying and slicing. + As a result of this change, the private subclass ``CompImageHeader`` + now always should be passed an explicit ``image_header``. [#9229] + +- Removed the deprecated ``tolerance`` option in ``fitsdiff`` and + ``io.fits.diff`` classes. [#9520] + +- Removed deprecated keyword arguments for ``CompImageHDU``: + ``compressionType``, ``tileSize``, ``hcompScale``, ``hcompSmooth``, + ``quantizeLevel``. [#9520] + +astropy.io.votable +^^^^^^^^^^^^^^^^^^ + +- Changed ``pedantic`` argument to ``verify`` and change it to have three + string-based options (``ignore``, ``warn``, and ``exception``) instead of + just being a boolean. In addition, changed default to ``ignore``, which means + that warnings will not be shown by default when loading VO tables. [#8715] + +astropy.modeling +^^^^^^^^^^^^^^^^ + +- Eliminates support for compound classes (but not compound instances!) [#8769] + +- Slicing compound models more restrictive. [#8769] + +- Shape of parameters now includes n_models as dimension. [#8769] + +- Parameter instances now hold values instead of models. [#8769] + +- Compound model parameters now share instance and value with + constituent models. [#8769] + +- No longer possible to assign slices of parameter values to model parameters + attribute (it is possible to replace it with a complete array). [#8769] + +- Many private attributes and methods have changed (see documentation). [#8769] + +- Deprecated ``BlackBody1D`` model and ``blackbody_nu`` and + ``blackbody_lambda`` functions. [#9282] + +- The deprecated ``rotations.rotation_matrix_from_angle`` was removed. [#9363] + +- Deprecated and renamed ``MexicanHat1D`` and ``MexicanHat2D`` + to ``RickerWavelet1D`` and ``RickerWavelet2D``. [#9445] + +- Deprecated ``modeling.utils.ExpressionTree``. [#9576] + +astropy.stats +^^^^^^^^^^^^^ + +- Removed the ``iters`` keyword from sigma clipping stats functions. [#8948] + +- Renamed the ``a`` parameter to ``data`` in biweight stat functions. [#8948] + +- Renamed the ``a`` parameter to ``data`` in ``median_absolute_deviation``. + [#9011] + +- Renamed the ``conflevel`` keyword to ``confidence_level`` in + ``poisson_conf_interval``. Usage of ``conflevel`` now issues + ``AstropyDeprecationWarning``. [#9408] + +- Renamed the ``conf`` keyword to ``confidence_level`` in + ``binom_conf_interval`` and ``binned_binom_proportion``. Usage of ``conf`` + now issues ``AstropyDeprecationWarning``. [#9408] + +- Renamed the ``conf_lvl`` keyword to ``confidence_level`` in + ``jackknife_stats``. Usage of ``conf_lvl`` now issues + ``AstropyDeprecationWarning``. [#9408] + +astropy.table +^^^^^^^^^^^^^ + +- The handling of masked columns in the ``Table`` class has changed in a way + that may impact program behavior. Now a ``Table`` with ``masked=False`` + may contain both ``Column`` and ``MaskedColumn`` objects, and adding a + masked column or row to a table no longer "upgrades" the table and columns + to masked. This means that tables with masked data which are read via + ``Table.read()`` will now always have ``masked=False``, though specific + columns will be masked as needed. Two new table properties + ``has_masked_columns`` and ``has_masked_values`` were added. See the + `Masking change in astropy 4.0 section within + `_ for + details. [#8789] + +- Table operation functions such as ``join``, ``vstack``, ``hstack``, etc now + always return a table with ``masked=False``, though the individual columns + may be masked as necessary. [#8957] + +- Changed implementation of ``Table.add_column()`` and ``Table.add_columns()`` + methods. Now it is possible add any object(s) which can be converted or + broadcasted to a valid column for the table. ``Table.__setitem__`` now + just calls ``add_column``. [#8933] + +- Changed default table configuration setting ``replace_warnings`` from + ``['slice']`` to ``[]``. This removes the default warning when replacing + a table column that is a slice of another column. [#9144] + +- Removed the non-public method + ``astropy.table.np_utils.recarray_fromrecords``. [#9165] + +astropy.tests +^^^^^^^^^^^^^ + +- In addition to ``DeprecationWarning``, now ``FutureWarning`` and + ``ImportWarning`` would also be turned into exceptions. [#8506] + +- ``warnings_to_ignore_by_pyver`` option in + ``enable_deprecations_as_exceptions()`` has changed. Please refer to API + documentation. [#8506] + +- Default settings for ``warnings_to_ignore_by_pyver`` are updated to remove + very old warnings that are no longer relevant and to add a new warning + caused by ``pytest-doctestplus``. [#8506] + +astropy.time +^^^^^^^^^^^^ + +- ``Time.get_ut1_utc`` now uses the auto-updated ``IERS_Auto`` by default, + instead of the bundled ``IERS_B`` file. [#9226] + +- Time formats that do not use ``val2`` now raise ValueError instead of + silently ignoring a provided value. [#9373] + +- Custom time formats can now accept floating-point types with extended + precision. Existing time formats raise exceptions rather than discarding + extended precision through conversion to ordinary floating-point. [#9368] + +- Time formats (implemented in subclasses of ``TimeFormat``) now have + their input and output routines more thoroughly validated, making it more + difficult to create damaged ``Time`` objects. [#9375] + +- The ``TimeDelta.to_value()`` method now can also take the ``format`` name + as its argument, in which case the value will be calculated using the + ``TimeFormat`` machinery. For this case, one can also pass a ``subfmt`` + argument to retrieve the value in another form than ``float``. [#9361] + +astropy.timeseries +^^^^^^^^^^^^^^^^^^ + +- Keyword ``midpoint_epoch`` is renamed to ``epoch_time``. [#9455] + +astropy.uncertainty +^^^^^^^^^^^^^^^^^^^ + +- ``Distribution`` was rewritten such that it deals better with subclasses. + As a result, Quantity distributions now behave correctly with ``to`` methods + yielding new distributions of the kind expected for the starting distribution, + and ``to_value`` yielding ``NdarrayDistribution`` instances. [#9442] + +astropy.units +^^^^^^^^^^^^^ + +- For consistency with ``ndarray``, scalar ``Quantity.value`` will now return + a numpy scalar rather than a python one. This should help keep track of + precision better, but may lead to unexpected results for the rare cases + where numpy scalars behave differently than python ones (e.g., taking the + square root of a negative number). [#8876] + +- Removed the ``magnitude_zero_points`` module, which was deprecated in + favour of ``astropy.units.photometric`` since 3.1. [#9353] + +- ``EquivalentUnitsList`` now has a ``_repr_html_`` method to output a HTML + table on a call to ``find_equivalent_units`` in Jupyter notebooks. [#9495] + +astropy.utils +^^^^^^^^^^^^^ + +- ``download_file`` and related functions now accept a list of fallback + sources, and they are able to update the cache at the user's request. [#9182] + +- Allow ``astropy.utils.console.ProgressBarOrSpinner.map`` and + ``.map_unordered`` to take an argument ``multiprocessing_start_method`` to + control how subprocesses are started; the different methods (``fork``, + ``spawn``, and ``forkserver``) have different implications in terms of + security, efficiency, and behavioural anomalies. The option is useful in + particular for cross-platform testing because Windows supports only ``spawn`` + while Linux defaults to ``fork``. [#9182] + +- All operations that act on the astropy download cache now take an argument + ``pkgname`` that allows one to specify which package's cache to use. + [#8237, #9182] + +- Removed deprecated ``funcsigs`` and ``futures`` from + ``astropy.utils.compat``. [#8909] + +- Removed the deprecated ``astropy.utils.compat.numpy`` module. [#8910] + +- Deprecated ``InheritDocstrings`` as it is natively supported by + Sphinx 1.7 or higher. [#8881] + +- Deprecated ``astropy.utils.timer`` module, which has been moved to + ``astroquery.utils.timer`` and will be part of ``astroquery`` 0.4.0. [#9038] + +- Deprecated ``astropy.utils.misc.set_locale`` function, as it is meant for + internal use only. [#9471] + +- The implementation of ``data_info.DataInfo`` has changed (for a considerable + performance boost). Generally, this should not affect simple subclasses, but + because the class now uses ``__slots__`` any attributes on the class have to + be explicitly given a slot. [#8998] + +- ``IERS`` tables now use ``nan`` to mark missing values + (rather than ``1e20``). [#9226] + +astropy.visualization +^^^^^^^^^^^^^^^^^^^^^ + +- The default ``clip`` value is now ``False`` in ``ImageNormalize``. [#9478] + +- The default ``clip`` value is now ``False`` in ``simple_norm``. + [#9698] + +- Infinite values are now excluded when calculating limits in + ``ManualInterval`` and ``MinMaxInterval``. They were already excluded in + all other interval classes. [#9480] + + +Bug Fixes +--------- + +astropy.convolution +^^^^^^^^^^^^^^^^^^^ + +- Fixed ``nan_treatment='interpolate'`` option to ``convolve_fft`` to properly + take into account ``fill_value``. [#8122] + +astropy.coordinates +^^^^^^^^^^^^^^^^^^^ + +- The ``QuantityAttribute`` class now supports a None default value if a unit + is specified. [#9345] + +- When ``Representation`` classes with the same name are defined, this no + longer leads to a ``ValueError``, but instead to a warning and the removal + of both from the name registry (i.e., one either has to use the class itself + to set, e.g., ``representation_type``, or refer to the class by its fully + qualified name). [#8561] + +astropy.io.fits +^^^^^^^^^^^^^^^ + +- Implemented skip (after warning) of header cards with reserved + keywords in ``table_to_hdu``. [#9390] + +- Add ``AstropyDeprecationWarning`` to ``read_table_fits`` when ``hdu=`` is + selected, but does not match single present table HDU. [#9512] + +astropy.io.votable +^^^^^^^^^^^^^^^^^^ + +- Address issue #8995 by ignoring BINARY2 null mask bits for string values + on parsing a VOTable. In this way, the reader should never create masked + values for string types. [#9057] + +- Corrected a spurious warning issued for the ``value`` attribute of the + ``
+ + + + + + + + + + + +
cosmology name H0 Om0 Tcmb0Neff m_nu Ob0
FlatLambdaCDM Planck18 67.66 0.309662.7255 3.046 0.0 0.0 0.060.04897
+ + + + + +The cosmology's metadata is not included in the file. + +To save the cosmology in an existing file, use ``overwrite=True``; otherwise, an +error will be raised. + + >>> Planck18.write(file, overwrite=True) + +To use a different table class as the underlying writer, use the ``cls`` kwarg. For +more information on the available table classes, see the documentation on Astropy's +table classes and on ``Cosmology.to_format("astropy.table")``. + +By default the parameter names are not converted to LaTeX / MathJax format. To +enable this, set ``latex_names=True``. + + >>> file = Path(temp_dir.name) / "file2.html" + >>> Planck18.write(file, latex_names=True) + >>> with open(file) as f: print(f.read()) + + ... + + + cosmology + name + $$H_{0}$$ + $$\Omega_{m,0}$$ + $$T_{0}$$ + $$N_{eff}$$ + $$m_{nu}$$ + $$\Omega_{b,0}$$ + + ... + +.. note:: + + A HTML file containing a Cosmology HTML table should have scripts enabling MathJax. + + .. code-block:: html + + + +.. testcleanup:: + + >>> temp_dir.cleanup() +""" + +from typing import Any, TypeVar + +import astropy.units as u +from astropy.table import QTable, Table + +# isort: split +import astropy.cosmology.units as cu +from astropy.cosmology._src.core import Cosmology +from astropy.cosmology._src.io.connect import readwrite_registry +from astropy.cosmology._src.parameter import Parameter +from astropy.cosmology._src.typing import _CosmoT +from astropy.io.typing import PathLike, ReadableFileLike, WriteableFileLike + +from .table import from_table, to_table + +_TableT = TypeVar("_TableT", bound=Table) + +# Format look-up for conversion, {original_name: new_name} +# TODO! move this information into the Parameters themselves +_FORMAT_TABLE = { + "H0": "$$H_{0}$$", + "Om0": "$$\\Omega_{m,0}$$", + "Ode0": "$$\\Omega_{\\Lambda,0}$$", + "Tcmb0": "$$T_{0}$$", + "Neff": "$$N_{eff}$$", + "m_nu": "$$m_{nu}$$", + "Ob0": "$$\\Omega_{b,0}$$", + "w0": "$$w_{0}$$", + "wa": "$$w_{a}$$", + "wz": "$$w_{z}$$", + "wp": "$$w_{p}$$", + "zp": "$$z_{p}$$", +} + + +def read_html_table( + filename: PathLike | ReadableFileLike[Table], + index: int | str | None = None, + *, + move_to_meta: bool = False, + cosmology: str | type[_CosmoT] | None = None, + latex_names: bool = True, + **kwargs: Any, +) -> _CosmoT: + r"""Read a |Cosmology| from an HTML file. + + Parameters + ---------- + filename : path-like or file-like + From where to read the Cosmology. + index : int or str or None, optional + Needed to select the row in tables with multiple rows. ``index`` can be an + integer for the row number or, if the table is indexed by a column, the value of + that column. If the table is not indexed and ``index`` is a string, the "name" + column is used as the indexing column. + + move_to_meta : bool, optional keyword-only + Whether to move keyword arguments that are not in the Cosmology class' signature + to the Cosmology's metadata. This will only be applied if the Cosmology does NOT + have a keyword-only argument (e.g. ``**kwargs``). Arguments moved to the + metadata will be merged with existing metadata, preferring specified metadata in + the case of a merge conflict (e.g. for ``Cosmology(meta={'key':10}, key=42)``, + the ``Cosmology.meta`` will be ``{'key': 10}``). + cosmology : str or |Cosmology| class or None, optional keyword-only + The cosmology class (or string name thereof) to use when constructing the + cosmology instance. The class also provides default parameter values, filling in + any non-mandatory arguments missing in 'table'. + latex_names : bool, optional keyword-only + Whether the |Table| (might) have latex column names for the parameters that need + to be mapped to the correct parameter name -- e.g. $$H_{0}$$ to 'H0'. This is + `True` by default, but can be turned off (set to `False`) if there is a known + name conflict (e.g. both an 'H0' and '$$H_{0}$$' column) as this will raise an + error. In this case, the correct name ('H0') is preferred. + **kwargs : Any + Passed to ``QTable.read``. ``format`` is set to 'ascii.html', regardless of + input. + + Returns + ------- + |Cosmology| subclass instance + + Raises + ------ + ValueError + If the keyword argument 'format' is given and is not "ascii.html". + """ + # Check that the format is 'ascii.html' (or not specified) + format = kwargs.pop("format", "ascii.html") + if format != "ascii.html": + raise ValueError(f"format must be 'ascii.html', not {format}") + + # Reading is handled by `QTable`. + with u.add_enabled_units(cu): # (cosmology units not turned on by default) + table = QTable.read(filename, format="ascii.html", **kwargs) + + # Need to map the table's column names to Cosmology inputs (parameter + # names). + # TODO! move the `latex_names` into `from_table` + if latex_names: + table_columns = set(table.colnames) + for name, latex in _FORMAT_TABLE.items(): + if latex in table_columns: + table.rename_column(latex, name) + + # Build the cosmology from table, using the private backend. + return from_table( + table, index=index, move_to_meta=move_to_meta, cosmology=cosmology, rename=None + ) + + +def write_html_table( + cosmology: Cosmology, + file: PathLike | WriteableFileLike[_TableT], + *, + overwrite: bool = False, + cls: type[_TableT] = QTable, + latex_names: bool = False, + **kwargs: Any, +) -> None: + r"""Serialize the |Cosmology| into a HTML table. + + Parameters + ---------- + cosmology : |Cosmology| subclass instance + The cosmology to serialize. + file : path-like or file-like + Where to write the html table. + + overwrite : bool, optional keyword-only + Whether to overwrite the file, if it exists. + cls : |Table| class, optional keyword-only + Astropy |Table| (sub)class to use when writing. Default is |QTable| class. + latex_names : bool, optional keyword-only + Whether to format the parameters (column) names to latex -- e.g. 'H0' to + $$H_{0}$$. + **kwargs : Any + Passed to ``cls.write``. + + Raises + ------ + TypeError + If the optional keyword-argument 'cls' is not a subclass of |Table|. + ValueError + If the keyword argument 'format' is given and is not "ascii.html". + + Examples + -------- + We assume the following setup: + + >>> from pathlib import Path + >>> from tempfile import TemporaryDirectory + >>> temp_dir = TemporaryDirectory() + + Writing a cosmology to a html file will produce a table with the cosmology's type, + name, and parameters as columns. + + >>> from astropy.cosmology import Planck18 + >>> file = Path(temp_dir.name) / "file.html" + >>> Planck18.write(file, overwrite=True) + >>> with open(file) as f: print(f.read()) + + + + + + + + + + + + + + + + + + +
cosmology name H0 Om0 Tcmb0Neff m_nu Ob0
FlatLambdaCDM Planck18 67.66 0.309662.7255 3.046 0.0 0.0 0.060.04897
+ + + + + + The cosmology's metadata is not included in the file. + + To save the cosmology in an existing file, use ``overwrite=True``; otherwise, an + error will be raised. + + >>> Planck18.write(file, overwrite=True) + + To use a different table class as the underlying writer, use the ``cls`` kwarg. For + more information on the available table classes, see the documentation on Astropy's + table classes and on ``Cosmology.to_format("astropy.table")``. + + By default the parameter names are not converted to LaTeX / MathJax format. To + enable this, set ``latex_names=True``. + + >>> file = Path(temp_dir.name) / "file2.html" + >>> Planck18.write(file, latex_names=True) + >>> with open(file) as f: print(f.read()) + + ... + + + cosmology + name + $$H_{0}$$ + $$\Omega_{m,0}$$ + $$T_{0}$$ + $$N_{eff}$$ + $$m_{nu}$$ + $$\Omega_{b,0}$$ + + ... + + .. testcleanup:: + + >>> temp_dir.cleanup() + + Notes + ----- + A HTML file containing a Cosmology HTML table should have scripts enabling MathJax. + + .. code-block:: html + + + """ + # Check that the format is 'ascii.html' (or not specified) + format = kwargs.pop("format", "ascii.html") + if format != "ascii.html": + raise ValueError(f"format must be 'ascii.html', not {format}") + + # Set cosmology_in_meta as false for now since there is no metadata being kept + table = to_table(cosmology, cls=cls, cosmology_in_meta=False) + + cosmo_cls = type(cosmology) + for name, col in table.columns.items(): + param = cosmo_cls.parameters.get(name) + if not isinstance(param, Parameter) or param.unit in (None, u.one): + continue + # Replace column with unitless version + table.replace_column(name, (col << param.unit).value, copy=False) + + if latex_names: + new_names = [_FORMAT_TABLE.get(k, k) for k in cosmology.parameters] + table.rename_columns(tuple(cosmology.parameters), new_names) + + # Write HTML, using table I/O + table.write(file, overwrite=overwrite, format="ascii.html", **kwargs) + + +def html_identify( + origin: object, filepath: object, *args: object, **kwargs: object +) -> bool: + """Identify if an object uses the HTML Table format. + + Parameters + ---------- + origin : object + Not used. + filepath : object + From where to read the Cosmology. + *args : object + Not used. + **kwargs : object + Not used. + + Returns + ------- + bool + If the filepath is a string ending with '.html'. + """ + return isinstance(filepath, str) and filepath.endswith(".html") + + +# =================================================================== +# Register + +readwrite_registry.register_reader("ascii.html", Cosmology, read_html_table) +readwrite_registry.register_writer("ascii.html", Cosmology, write_html_table) +readwrite_registry.register_identifier("ascii.html", Cosmology, html_identify) diff --git a/astropy/cosmology/_src/io/builtin/latex.py b/astropy/cosmology/_src/io/builtin/latex.py new file mode 100644 index 000000000000..af71a42e31de --- /dev/null +++ b/astropy/cosmology/_src/io/builtin/latex.py @@ -0,0 +1,214 @@ +r"""|Cosmology| <-> LaTeX I/O, using |Cosmology.read| and |Cosmology.write|. + +We assume the following setup: + + >>> from pathlib import Path + >>> from tempfile import TemporaryDirectory + >>> temp_dir = TemporaryDirectory() + +Writing a cosmology to a LaTeX file will produce a table with the cosmology's type, +name, and parameters as columns. + + >>> from astropy.cosmology import Cosmology, Planck18 + >>> file = Path(temp_dir.name) / "file.tex" + + >>> Planck18.write(file, format="ascii.latex") + >>> with open(file) as f: print(f.read()) + \begin{table} + \begin{tabular}{cccccccc} + cosmology & name & $H_0$ & $\Omega_{m,0}$ & $T_{0}$ & $N_{eff}$ & $m_{nu}$ & $\Omega_{b,0}$ \\ + & & $\mathrm{km\,Mpc^{-1}\,s^{-1}}$ & & $\mathrm{K}$ & & $\mathrm{eV}$ & \\ + FlatLambdaCDM & Planck18 & 67.66 & 0.30966 & 2.7255 & 3.046 & 0.0 .. 0.06 & 0.04897 \\ + \end{tabular} + \end{table} + + +The cosmology's metadata is not included in the table. + +To save the cosmology in an existing file, use ``overwrite=True``; otherwise, an +error will be raised. + + >>> Planck18.write(file, format="ascii.latex", overwrite=True) + +To use a different table class as the underlying writer, use the ``cls`` kwarg. For +more information on the available table classes, see the documentation on Astropy's +table classes and on ``Cosmology.to_format("astropy.table")``. + +By default the parameter names are converted to LaTeX format. To disable this, set +``latex_names=False``. + + >>> file = Path(temp_dir.name) / "file2.tex" + >>> Planck18.write(file, format="ascii.latex", latex_names=False) + >>> with open(file) as f: print(f.read()) + \begin{table} + \begin{tabular}{cccccccc} + cosmology & name & H0 & Om0 & Tcmb0 & Neff & m_nu & Ob0 \\ + & & $\mathrm{km\,Mpc^{-1}\,s^{-1}}$ & & $\mathrm{K}$ & & $\mathrm{eV}$ & \\ + FlatLambdaCDM & Planck18 & 67.66 & 0.30966 & 2.7255 & 3.046 & 0.0 .. 0.06 & 0.04897 \\ + \end{tabular} + \end{table} + + +.. testcleanup:: + + >>> temp_dir.cleanup() +""" + +from typing import Any, TypeVar + +import astropy.units as u +from astropy.cosmology._src.core import Cosmology +from astropy.cosmology._src.io.connect import readwrite_registry +from astropy.cosmology._src.parameter import Parameter +from astropy.io.typing import PathLike, WriteableFileLike +from astropy.table import QTable, Table + +from .table import to_table + +_TableT = TypeVar("_TableT", bound=Table) + +_FORMAT_TABLE = { + "H0": "$H_0$", + "Om0": r"$\Omega_{m,0}$", + "Ode0": r"$\Omega_{\Lambda,0}$", + "Tcmb0": "$T_{0}$", + "Neff": "$N_{eff}$", + "m_nu": "$m_{nu}$", + "Ob0": r"$\Omega_{b,0}$", + "w0": "$w_{0}$", + "wa": "$w_{a}$", + "wz": "$w_{z}$", + "wp": "$w_{p}$", + "zp": "$z_{p}$", +} + + +def write_latex( + cosmology: Cosmology, + file: PathLike | WriteableFileLike[_TableT], + *, + overwrite: bool = False, + cls: type[_TableT] = QTable, + latex_names: bool = True, + **kwargs: Any, +) -> None: + r"""Serialize the |Cosmology| into a LaTeX. + + Parameters + ---------- + cosmology : `~astropy.cosmology.Cosmology` subclass instance + The cosmology to serialize. + file : path-like or file-like + Location to save the serialized cosmology. + + overwrite : bool + Whether to overwrite the file, if it exists. + cls : type, optional keyword-only + Astropy :class:`~astropy.table.Table` (sub)class to use when writing. Default is + :class:`~astropy.table.QTable`. + latex_names : bool, optional keyword-only + Whether to use LaTeX names for the parameters. Default is `True`. + **kwargs + Passed to ``cls.write`` + + Raises + ------ + TypeError + If kwarg (optional) 'cls' is not a subclass of `astropy.table.Table` + + Examples + -------- + We assume the following setup: + + >>> from pathlib import Path + >>> from tempfile import TemporaryDirectory + >>> temp_dir = TemporaryDirectory() + + Writing a cosmology to a LaTeX file will produce a table with the cosmology's type, + name, and parameters as columns. + + >>> from astropy.cosmology import Planck18 + >>> file = Path(temp_dir.name) / "file.tex" + + >>> Planck18.write(file, format="ascii.latex") + >>> with open(file) as f: print(f.read()) + \begin{table} + \begin{tabular}{cccccccc} + cosmology & name & $H_0$ & $\Omega_{m,0}$ & $T_{0}$ & $N_{eff}$ & $m_{nu}$ & $\Omega_{b,0}$ \\ + & & $\mathrm{km\,Mpc^{-1}\,s^{-1}}$ & & $\mathrm{K}$ & & $\mathrm{eV}$ & \\ + FlatLambdaCDM & Planck18 & 67.66 & 0.30966 & 2.7255 & 3.046 & 0.0 .. 0.06 & 0.04897 \\ + \end{tabular} + \end{table} + + + The cosmology's metadata is not included in the table. + + To save the cosmology in an existing file, use ``overwrite=True``; otherwise, an + error will be raised. + + >>> Planck18.write(file, format="ascii.latex", overwrite=True) + + To use a different table class as the underlying writer, use the ``cls`` kwarg. For + more information on the available table classes, see the documentation on Astropy's + table classes and on ``Cosmology.to_format("astropy.table")``. + + By default the parameter names are converted to LaTeX format. To disable this, set + ``latex_names=False``. + + >>> file = Path(temp_dir.name) / "file2.tex" + >>> Planck18.write(file, format="ascii.latex", latex_names=False) + >>> with open(file) as f: print(f.read()) + \begin{table} + \begin{tabular}{cccccccc} + cosmology & name & H0 & Om0 & Tcmb0 & Neff & m_nu & Ob0 \\ + & & $\mathrm{km\,Mpc^{-1}\,s^{-1}}$ & & $\mathrm{K}$ & & $\mathrm{eV}$ & \\ + FlatLambdaCDM & Planck18 & 67.66 & 0.30966 & 2.7255 & 3.046 & 0.0 .. 0.06 & 0.04897 \\ + \end{tabular} + \end{table} + + + .. testcleanup:: + + >>> temp_dir.cleanup() + """ + # Check that the format is 'latex', 'ascii.latex' (or not specified) + fmt = kwargs.pop("format", "ascii.latex") + if fmt != "ascii.latex": + raise ValueError(f"format must be 'ascii.latex', not {fmt}") + + # Set cosmology_in_meta as false for now since there is no metadata being kept + table = to_table(cosmology, cls=cls, cosmology_in_meta=False) + + cosmo_cls = type(cosmology) + for name in table.columns.keys(): + param = cosmo_cls.parameters.get(name) + if not isinstance(param, Parameter) or param.unit in (None, u.one): + continue + # Get column to correct unit + table[name] <<= param.unit + + # Convert parameter names to LaTeX format + if latex_names: + new_names = [_FORMAT_TABLE.get(k, k) for k in cosmology.parameters] + table.rename_columns(tuple(cosmology.parameters), new_names) + + table.write(file, overwrite=overwrite, format="ascii.latex", **kwargs) + + +def latex_identify( + origin: object, filepath: str | None, *args: object, **kwargs: object +) -> bool: + """Identify if object uses the Table format. + + Returns + ------- + bool + """ + return filepath is not None and filepath.endswith(".tex") + + +# =================================================================== +# Register + +readwrite_registry.register_writer("ascii.latex", Cosmology, write_latex) +readwrite_registry.register_identifier("ascii.latex", Cosmology, latex_identify) diff --git a/astropy/cosmology/_src/io/builtin/mapping.py b/astropy/cosmology/_src/io/builtin/mapping.py new file mode 100644 index 000000000000..8e46ee9bf82a --- /dev/null +++ b/astropy/cosmology/_src/io/builtin/mapping.py @@ -0,0 +1,432 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +"""|Cosmology| <-> Mapping I/O, using |Cosmology.to_format| and |Cosmology.from_format|. + +This module provides functions to transform a |Cosmology| instance to a mapping +(`dict`-like) object and vice versa, from a mapping object back to a |Cosmology| +instance. The functions are registered with ``convert_registry`` under the format name +"mapping". The mapping object is a `dict`-like object, with the cosmology's parameters +and metadata as items. `dict` is a fundamental data structure in Python, and this +representation of a |Cosmology| is useful for translating between many serialization and +storage formats, or even passing arguments to functions. + +We start with the simple case of outputting a |Cosmology| as a mapping. + + >>> from astropy.cosmology import Cosmology, Planck18 + >>> cm = Planck18.to_format('mapping') + >>> cm + {'cosmology': , + 'name': 'Planck18', 'H0': , 'Om0': 0.30966, + 'Tcmb0': , 'Neff': 3.046, + 'm_nu': , 'Ob0': 0.04897, + 'meta': ... + +``cm`` is a `dict`, with the cosmology's parameters and metadata as items. + +How might we use this `dict`? One use is to unpack the `dict` into a function: + + >>> def function(H0, Tcmb0, **kwargs): ... + >>> function(**cm) + +Another use is to merge the `dict` with another `dict`: + + >>> cm2 = {'H0': 70, 'Tcmb0': 2.7} + >>> cm | cm2 + {'cosmology': , ..., 'H0': 70, ...} + +Most saliently, the `dict` can also be used to construct a new cosmological instance +identical to the |Planck18| cosmology from which it was generated. + + >>> cosmo = Cosmology.from_format(cm, format="mapping") + >>> cosmo + FlatLambdaCDM(name='Planck18', H0=, Om0=0.30966, Tcmb0=, Neff=3.046, m_nu=, Ob0=0.04897) + +How did |Cosmology.from_format| know to return an instance of the |FlatLambdaCDM| class? +The mapping object has a field ``cosmology`` which can be either the string name of the +cosmology class (e.g. "FlatLambdaCDM") or the class itself. + +This field can be omitted under two conditions. + +1. If the cosmology class is passed as the ``cosmology`` keyword argument to + |Cosmology.from_format|, +2. If a specific cosmology class, e.g. |FlatLambdaCDM|, is used to parse the data. + +To the first point, we can pass the cosmology class as the ``cosmology`` keyword +argument to |Cosmology.from_format|. + + >>> del cm["cosmology"] # remove cosmology class + + >>> Cosmology.from_format(cm, cosmology="FlatLambdaCDM") + FlatLambdaCDM(name='Planck18', H0=, Om0=0.30966, Tcmb0=, Neff=3.046, m_nu=, Ob0=0.04897) + +To the second point, we can use specific cosmology class to parse the data. + + >>> from astropy.cosmology import FlatLambdaCDM + >>> FlatLambdaCDM.from_format(cm) + FlatLambdaCDM(name='Planck18', H0=, Om0=0.30966, Tcmb0=, Neff=3.046, m_nu=, Ob0=0.04897) + +Also, the class' default parameter values are used to fill in any information missing in +the data. For example, if ``Tcmb0`` is missing, the default value of 0.0 K is used. + + >>> del cm["Tcmb0"] # show FlatLambdaCDM provides default + >>> FlatLambdaCDM.from_format(cm) + FlatLambdaCDM(name='Planck18', H0=..., Tcmb0=, ...) + +If instead of *missing* information, there is *extra* information, there are a few +options. The first is to use the ``move_to_meta`` keyword argument to move fields that +are not in the Cosmology constructor to the Cosmology's metadata. + + >>> cm2 = cm | {"extra": 42, "cosmology": "FlatLambdaCDM"} + >>> cosmo = Cosmology.from_format(cm2, move_to_meta=True) + >>> cosmo.meta + {'extra': 42, ...} + +Alternatively, the ``rename`` keyword argument can be used to rename keys in the mapping +to fields of the |Cosmology|. This is crucial when the mapping has keys that are not +valid arguments to the |Cosmology| constructor. + + >>> cm3 = dict(cm) # copy + >>> cm3["cosmo_cls"] = "FlatLambdaCDM" + >>> cm3["cosmo_name"] = cm3.pop("name") + + >>> rename = {'cosmo_cls': 'cosmology', 'cosmo_name': 'name'} + >>> Cosmology.from_format(cm3, rename=rename) + FlatLambdaCDM(name='Planck18', H0=, Om0=0.30966, Tcmb0=, Neff=3.046, m_nu=None, Ob0=0.04897) + +Let's take a closer look at |Cosmology.to_format|, because there a lot of options, to +tailor the output to specific needs. + +The dictionary type may be changed with the ``cls`` keyword argument: + + >>> from collections import OrderedDict + >>> Planck18.to_format('mapping', cls=OrderedDict) + OrderedDict({'cosmology': , 'name': 'Planck18', 'H0': , 'Om0': 0.30966, 'Tcmb0': , 'Neff': 3.046, 'm_nu': , 'Ob0': 0.04897, 'meta': {...}}) + +Sometimes it is more useful to have the name of the cosmology class, not the type +itself. The keyword argument ``cosmology_as_str`` may be used: + + >>> Planck18.to_format('mapping', cosmology_as_str=True) + {'cosmology': 'FlatLambdaCDM', ... + +The metadata is normally included as a nested mapping. To move the metadata into the +main mapping, use the keyword argument ``move_from_meta``. This kwarg inverts +``move_to_meta`` in ``Cosmology.to_format("mapping", move_to_meta=...)`` where extra +items are moved to the metadata (if the cosmology constructor does not have a variable +keyword-only argument -- ``**kwargs``). + + >>> from astropy.cosmology import Planck18 + >>> Planck18.to_format('mapping', move_from_meta=True) + {'cosmology': , + 'name': 'Planck18', 'Oc0': 0.2607, 'n': 0.9665, 'sigma8': 0.8102, ... + +Lastly, the keys in the mapping may be renamed with the ``rename`` keyword. + + >>> rename = {'cosmology': 'cosmo_cls', 'name': 'cosmo_name'} + >>> Planck18.to_format('mapping', rename=rename) + {'cosmo_cls': , + 'cosmo_name': 'Planck18', ... +""" + +__all__: list[str] = [] # nothing is publicly scoped + +import copy +import inspect +from collections.abc import Mapping, MutableMapping +from typing import Any, TypeVar + +from astropy.cosmology._src.core import _COSMOLOGY_CLASSES, Cosmology +from astropy.cosmology._src.io.connect import convert_registry +from astropy.cosmology._src.typing import _CosmoT + +_MapT = TypeVar("_MapT", bound=MutableMapping[str, Any]) + + +def _rename_map( + map: Mapping[str, Any], /, renames: Mapping[str, str] +) -> dict[str, Any]: + """Apply rename to map.""" + if common_names := set(renames.values()).intersection(map): + raise ValueError( + "'renames' values must be disjoint from 'map' keys, " + f"the common keys are: {common_names}" + ) + return {renames.get(k, k): v for k, v in map.items()} # dict separate from input + + +def _get_cosmology_class( + cosmology: type[_CosmoT] | str | None, params: dict[str, Any], / +) -> type[_CosmoT]: + # get cosmology + # 1st from argument. Allows for override of the cosmology, if on file. + # 2nd from params. This MUST have the cosmology if 'kwargs' did not. + if cosmology is None: + cosmology = params.pop("cosmology") + else: + params.pop("cosmology", None) # pop, but don't use + # if string, parse to class + return _COSMOLOGY_CLASSES[cosmology] if isinstance(cosmology, str) else cosmology + + +def from_mapping( + mapping: Mapping[str, Any], + /, + *, + move_to_meta: bool = False, + cosmology: str | type[_CosmoT] | None = None, + rename: Mapping[str, str] | None = None, +) -> _CosmoT: + """Load `~astropy.cosmology.Cosmology` from mapping object. + + Parameters + ---------- + mapping : Mapping + Arguments into the class -- like "name" or "meta". If 'cosmology' is None, must + have field "cosmology" which can be either the string name of the cosmology + class (e.g. "FlatLambdaCDM") or the class itself. + + move_to_meta : bool (optional, keyword-only) + Whether to move keyword arguments that are not in the Cosmology class' signature + to the Cosmology's metadata. This will only be applied if the Cosmology does NOT + have a keyword-only argument (e.g. ``**kwargs``). Arguments moved to the + metadata will be merged with existing metadata, preferring specified metadata in + the case of a merge conflict (e.g. for ``Cosmology(meta={'key':10}, key=42)``, + the ``Cosmology.meta`` will be ``{'key': 10}``). + + cosmology : str, |Cosmology| class, or None (optional, keyword-only) + The cosmology class (or string name thereof) to use when constructing the + cosmology instance. The class also provides default parameter values, filling in + any non-mandatory arguments missing in 'map'. + + rename : Mapping[str, str] or None (optional, keyword-only) + A mapping of keys in ``map`` to fields of the `~astropy.cosmology.Cosmology`. + + Returns + ------- + `~astropy.cosmology.Cosmology` subclass instance + + Examples + -------- + To see loading a `~astropy.cosmology.Cosmology` from a dictionary with + ``from_mapping``, we will first make a mapping using + :meth:`~astropy.cosmology.Cosmology.to_format`. + + >>> from astropy.cosmology import Cosmology, Planck18 + >>> cm = Planck18.to_format('mapping') + >>> cm + {'cosmology': , + 'name': 'Planck18', 'H0': , 'Om0': 0.30966, + 'Tcmb0': , 'Neff': 3.046, + 'm_nu': , 'Ob0': 0.04897, + 'meta': ... + + Now this dict can be used to load a new cosmological instance identical to the + |Planck18| cosmology from which it was generated. + + >>> cosmo = Cosmology.from_format(cm, format="mapping") + >>> cosmo + FlatLambdaCDM(name='Planck18', H0=, Om0=0.30966, Tcmb0=, Neff=3.046, m_nu=, Ob0=0.04897) + + The ``cosmology`` field can be omitted if the cosmology class (or its string name) + is passed as the ``cosmology`` keyword argument to |Cosmology.from_format|. + + >>> del cm["cosmology"] # remove cosmology class + >>> Cosmology.from_format(cm, cosmology="FlatLambdaCDM") + FlatLambdaCDM(name='Planck18', H0=, Om0=0.30966, Tcmb0=, Neff=3.046, m_nu=, Ob0=0.04897) + + Alternatively, specific cosmology classes can be used to parse the data. + + >>> from astropy.cosmology import FlatLambdaCDM + >>> FlatLambdaCDM.from_format(cm) + FlatLambdaCDM(name='Planck18', H0=, Om0=0.30966, Tcmb0=, Neff=3.046, m_nu=, Ob0=0.04897) + + When using a specific cosmology class, the class' default parameter values are used + to fill in any missing information. + + >>> del cm["Tcmb0"] # show FlatLambdaCDM provides default + >>> FlatLambdaCDM.from_format(cm) + FlatLambdaCDM(name='Planck18', H0=, Om0=0.30966, Tcmb0=, Neff=3.046, m_nu=None, Ob0=0.04897) + + The ``move_to_meta`` keyword argument can be used to move fields that are not in the + Cosmology constructor to the Cosmology's metadata. This is useful when the + dictionary contains extra information that is not part of the Cosmology. + + >>> cm2 = cm | {"extra": 42, "cosmology": "FlatLambdaCDM"} + >>> cosmo = Cosmology.from_format(cm2, move_to_meta=True) + >>> cosmo.meta + {'extra': 42, ...} + + The ``rename`` keyword argument can be used to rename keys in the mapping to fields + of the |Cosmology|. This is crucial when the mapping has keys that are not valid + arguments to the |Cosmology| constructor. + + >>> cm3 = dict(cm) # copy + >>> cm3["cosmo_cls"] = "FlatLambdaCDM" + >>> cm3["cosmo_name"] = cm3.pop("name") + + >>> rename = {'cosmo_cls': 'cosmology', 'cosmo_name': 'name'} + >>> Cosmology.from_format(cm3, rename=rename) + FlatLambdaCDM(name='Planck18', H0=, Om0=0.30966, Tcmb0=, Neff=3.046, m_nu=None, Ob0=0.04897) + """ + # Rename keys, if given a ``renames`` dict. + # Also, make a copy of the mapping, so we can pop from it. + params = _rename_map(dict(mapping), renames=rename or {}) + + # Get cosmology class + cosmology = _get_cosmology_class(cosmology, params) + + # select arguments from mapping that are in the cosmo's signature. + sig = inspect.signature(cosmology) + ba = sig.bind_partial() # blank set of args + ba.apply_defaults() # fill in the defaults + for k in sig.parameters.keys(): + if k in params: # transfer argument, if in params + ba.arguments[k] = params.pop(k) + + # deal with remaining params. If there is a **kwargs use that, else + # allow to transfer to metadata. Raise TypeError if can't. + lastp = next(reversed(sig.parameters.values())) + if lastp.kind == 4: # variable keyword-only + ba.arguments[lastp.name] = params + elif move_to_meta: # prefers current meta, which was explicitly set + meta = ba.arguments["meta"] or {} # (None -> dict) + ba.arguments["meta"] = {**params, **meta} + elif params: + raise TypeError(f"there are unused parameters {params}.") + # else: pass # no kwargs, no move-to-meta, and all the params are used + + return cosmology(*ba.args, **ba.kwargs) + + +def to_mapping( + cosmology: Cosmology, + *args: object, + cls: type[_MapT] = dict, + cosmology_as_str: bool = False, + move_from_meta: bool = False, + rename: Mapping[str, str] | None = None, +) -> _MapT: + """Return the cosmology class, parameters, and metadata as a `dict`. + + Parameters + ---------- + cosmology : :class:`~astropy.cosmology.Cosmology` + The cosmology instance to convert to a mapping. + *args : object + Not used. Needed for compatibility with + `~astropy.io.registry.UnifiedReadWriteMethod` + cls : type (optional, keyword-only) + `dict` or `collections.Mapping` subclass. + The mapping type to return. Default is `dict`. + cosmology_as_str : bool (optional, keyword-only) + Whether the cosmology value is the class (if `False`, default) or + the semi-qualified name (if `True`). + move_from_meta : bool (optional, keyword-only) + Whether to add the Cosmology's metadata as an item to the mapping (if + `False`, default) or to merge with the rest of the mapping, preferring + the original values (if `True`) + rename : Mapping[str, str] or None (optional, keyword-only) + A mapping of field names of the :class:`~astropy.cosmology.Cosmology` to keys in + the map. + + Returns + ------- + MutableMapping[str, Any] + A mapping of type ``cls``, by default a `dict`. + Has key-values for the cosmology parameters and also: + - 'cosmology' : the class + - 'meta' : the contents of the cosmology's metadata attribute. + If ``move_from_meta`` is `True`, this key is missing and the + contained metadata are added to the main `dict`. + + Examples + -------- + A Cosmology as a mapping will have the cosmology's name and + parameters as items, and the metadata as a nested dictionary. + + >>> from astropy.cosmology import Planck18 + >>> Planck18.to_format('mapping') + {'cosmology': , + 'name': 'Planck18', 'H0': , 'Om0': 0.30966, + 'Tcmb0': , 'Neff': 3.046, + 'm_nu': , 'Ob0': 0.04897, + 'meta': ... + + The dictionary type may be changed with the ``cls`` keyword argument: + + >>> from collections import OrderedDict + >>> Planck18.to_format('mapping', cls=OrderedDict) + OrderedDict({'cosmology': , 'name': 'Planck18', 'H0': , 'Om0': 0.30966, 'Tcmb0': , 'Neff': 3.046, 'm_nu': , 'Ob0': 0.04897, 'meta': {...}}) + + Sometimes it is more useful to have the name of the cosmology class, not + the type itself. The keyword argument ``cosmology_as_str`` may be used: + + >>> Planck18.to_format('mapping', cosmology_as_str=True) + {'cosmology': 'FlatLambdaCDM', ... + + The metadata is normally included as a nested mapping. To move the metadata + into the main mapping, use the keyword argument ``move_from_meta``. This + kwarg inverts ``move_to_meta`` in + ``Cosmology.to_format("mapping", move_to_meta=...)`` where extra items + are moved to the metadata (if the cosmology constructor does not have a + variable keyword-only argument -- ``**kwargs``). + + >>> from astropy.cosmology import Planck18 + >>> Planck18.to_format('mapping', move_from_meta=True) + {'cosmology': , + 'name': 'Planck18', 'Oc0': 0.2607, 'n': 0.9665, 'sigma8': 0.8102, ... + + Lastly, the keys in the mapping may be renamed with the ``rename`` keyword. + + >>> rename = {'cosmology': 'cosmo_cls', 'name': 'cosmo_name'} + >>> Planck18.to_format('mapping', rename=rename) + {'cosmo_cls': , + 'cosmo_name': 'Planck18', ... + """ + if not issubclass(cls, (dict, Mapping)): + raise TypeError(f"'cls' must be a (sub)class of dict or Mapping, not {cls}") + + m = cls() + # start with the cosmology class & name + m["cosmology"] = ( + cosmology.__class__.__qualname__ if cosmology_as_str else cosmology.__class__ + ) + m["name"] = cosmology.name # here only for dict ordering + + meta = copy.deepcopy(cosmology.meta) # metadata (mutable) + if move_from_meta: + # Merge the mutable metadata. Since params are added later they will + # be preferred in cases of overlapping keys. Likewise, need to pop + # cosmology and name from meta. + meta.pop("cosmology", None) + meta.pop("name", None) + m.update(meta) + + # Add all the immutable inputs + m.update(cosmology.parameters) + # Lastly, add the metadata, if haven't already (above) + if not move_from_meta: + m["meta"] = meta # TODO? should meta be type(cls) + # Rename keys + return m if rename is None else _rename_map(m, rename) + + +def mapping_identify( + origin: str, format: str | None, *args: object, **kwargs: object +) -> bool: + """Identify if object uses the mapping format. + + Returns + ------- + bool + """ + itis = False + if origin == "read": + itis = isinstance(args[1], Mapping) and (format in (None, "mapping")) + return itis + + +# =================================================================== +# Register + +convert_registry.register_reader("mapping", Cosmology, from_mapping) +convert_registry.register_writer("mapping", Cosmology, to_mapping) +convert_registry.register_identifier("mapping", Cosmology, mapping_identify) diff --git a/astropy/cosmology/_src/io/builtin/model.py b/astropy/cosmology/_src/io/builtin/model.py new file mode 100644 index 000000000000..6bbfeb9124bd --- /dev/null +++ b/astropy/cosmology/_src/io/builtin/model.py @@ -0,0 +1,299 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +"""|Cosmology| <-> Model I/O, using |Cosmology.to_format| and |Cosmology.from_format|. + +This module provides functions to transform a |Cosmology| object to and from a +:class:`~astropy.modeling.Model`. The functions are registered with ``convert_registry`` +under the format name "astropy.model". + +Using ``format="astropy.model"`` any redshift(s) method of a cosmology may be turned +into a :class:`astropy.modeling.Model`. Each |Cosmology| +:class:`~astropy.cosmology.Parameter` is converted to a :class:`astropy.modeling.Model` +:class:`~astropy.modeling.Parameter` and the redshift-method to the model's ``__call__ / +evaluate``. Now you can fit cosmologies with data! + +.. code-block:: + + >>> from astropy.cosmology import Cosmology, Planck18 + >>> model = Planck18.to_format("astropy.model", method="lookback_time") + >>> model + + +The |Planck18| cosmology can be recovered with |Cosmology.from_format|. + + >>> print(Cosmology.from_format(model)) + FlatLambdaCDM(name="Planck18", H0=67.66 km / (Mpc s), Om0=0.30966, + Tcmb0=2.7255 K, Neff=3.046, m_nu=[0. 0. 0.06] eV, Ob0=0.04897) +""" + +import abc +import copy +import inspect +from dataclasses import replace +from typing import Generic + +import numpy as np + +from astropy.modeling import FittableModel, Model +from astropy.utils.decorators import classproperty + +# isort: split +from astropy.cosmology._src.core import Cosmology +from astropy.cosmology._src.io.connect import convert_registry +from astropy.cosmology._src.typing import _CosmoT + +from .utils import convert_parameter_to_model_parameter + +__all__: list[str] = [] # nothing is publicly scoped + + +class _CosmologyModel(FittableModel, Generic[_CosmoT]): + """Base class for Cosmology redshift-method Models. + + .. note:: + + This class is not publicly scoped so should not be used directly. + Instead, from a Cosmology instance use ``.to_format("astropy.model")`` + to create an instance of a subclass of this class. + + `_CosmologyModel` (subclasses) wrap a redshift-method of a + :class:`~astropy.cosmology.Cosmology` class, converting each non-`None` + |Cosmology| :class:`~astropy.cosmology.Parameter` to a + :class:`astropy.modeling.Model` :class:`~astropy.modeling.Parameter` + and the redshift-method to the model's ``__call__ / evaluate``. + + See Also + -------- + astropy.cosmology.Cosmology.to_format + """ + + @abc.abstractmethod + def _cosmology_class(self) -> type[_CosmoT]: + """Cosmology class as a private attribute. + + Set in subclasses. + """ + + @abc.abstractmethod + def _method_name(self) -> str: + """Cosmology method name as a private attribute. + + Set in subclasses. + """ + + @classproperty + def cosmology_class(cls) -> type[_CosmoT]: + """|Cosmology| class.""" + return cls._cosmology_class + + @classproperty(lazy=True) + def _cosmology_class_sig(cls): + """Signature of |Cosmology| class.""" + return inspect.signature(cls._cosmology_class) + + @property + def cosmology(self) -> _CosmoT: + """Return |Cosmology| using `~astropy.modeling.Parameter` values.""" + return self._cosmology_class( + name=self.name, + **{ + k: (v.value if not (v := getattr(self, k)).unit else v.quantity) + for k in self.param_names + }, + ) + + @classproperty + def method_name(self) -> str: + """Redshift-method name on |Cosmology| instance.""" + return self._method_name + + # --------------------------------------------------------------- + + # NOTE: cannot add type annotations b/c of how Model introspects + def evaluate(self, *args, **kwargs): + """Evaluate method {method!r} of {cosmo_cls!r} Cosmology. + + The Model wraps the :class:`~astropy.cosmology.Cosmology` method, + converting each |Cosmology| :class:`~astropy.cosmology.Parameter` to a + :class:`astropy.modeling.Model` :class:`~astropy.modeling.Parameter` + (unless the Parameter is None, in which case it is skipped). + Here an instance of the cosmology is created using the current + Parameter values and the method is evaluated given the input. + + Parameters + ---------- + *args, **kwargs + The first ``n_inputs`` of ``*args`` are for evaluating the method + of the cosmology. The remaining args and kwargs are passed to the + cosmology class constructor. + Any unspecified Cosmology Parameter use the current value of the + corresponding Model Parameter. + + Returns + ------- + Any + Results of evaluating the Cosmology method. + """ + # TODO: speed up using ``replace`` + + # create BoundArgument with all available inputs beyond the Parameters, + # which will be filled in next + ba = self._cosmology_class_sig.bind_partial(*args[self.n_inputs :], **kwargs) + + # fill in missing Parameters + for k in self.param_names: + if k not in ba.arguments: + v = getattr(self, k) + ba.arguments[k] = v.value if not v.unit else v.quantity + + # unvectorize, since Cosmology is not vectorized + # TODO! remove when vectorized + if np.shape(ba.arguments[k]): # only in __call__ + # m_nu is a special case # TODO! fix by making it 'structured' + if k == "m_nu" and len(ba.arguments[k].shape) == 1: + continue + ba.arguments[k] = ba.arguments[k][0] + + # make instance of cosmology + cosmo = self._cosmology_class(**ba.arguments) + # evaluate method + return getattr(cosmo, self._method_name)(*args[: self.n_inputs]) + + +############################################################################## + + +def from_model(model: _CosmologyModel[_CosmoT]) -> _CosmoT: + """Load |Cosmology| from `~astropy.modeling.Model` object. + + Parameters + ---------- + model : `_CosmologyModel` subclass instance + See ``Cosmology.to_format.help("astropy.model") for details. + + Returns + ------- + `~astropy.cosmology.Cosmology` subclass instance + + Examples + -------- + >>> from astropy.cosmology import Cosmology, Planck18 + >>> model = Planck18.to_format("astropy.model", method="lookback_time") + >>> print(Cosmology.from_format(model)) + FlatLambdaCDM(name="Planck18", H0=67.66 km / (Mpc s), Om0=0.30966, + Tcmb0=2.7255 K, Neff=3.046, m_nu=[0. 0. 0.06] eV, Ob0=0.04897) + """ + cosmo = model.cosmology + + # assemble the metadata + meta = copy.deepcopy(model.meta) + for n in model.param_names: + p = getattr(model, n) + meta[p.name] = { + n: getattr(p, n) + for n in dir(p) + if not (n.startswith("_") or callable(getattr(p, n))) + } + return replace(cosmo, meta=meta) + + +def to_model(cosmology: _CosmoT, *_: object, method: str) -> _CosmologyModel[_CosmoT]: + """Convert a `~astropy.cosmology.Cosmology` to a `~astropy.modeling.Model`. + + Parameters + ---------- + cosmology : `~astropy.cosmology.Cosmology` subclass instance + method : str, keyword-only + The name of the method on the ``cosmology``. + + Returns + ------- + `_CosmologyModel` subclass instance + The Model wraps the |Cosmology| method, converting each non-`None` + :class:`~astropy.cosmology.Parameter` to a + :class:`astropy.modeling.Model` :class:`~astropy.modeling.Parameter` + and the method to the model's ``__call__ / evaluate``. + + Examples + -------- + >>> from astropy.cosmology import Planck18 + >>> model = Planck18.to_format("astropy.model", method="lookback_time") + >>> model + + """ + cosmo_cls = cosmology.__class__ + + # get bound method & sig from cosmology (unbound if class). + if not hasattr(cosmology, method): + raise AttributeError(f"{method} is not a method on {cosmology.__class__}.") + func = getattr(cosmology, method) + if not callable(func): + raise ValueError(f"{cosmology.__class__}.{method} is not callable.") + msig = inspect.signature(func) + + # introspect for number of positional inputs, ignoring "self" + n_inputs = len([p for p in tuple(msig.parameters.values()) if (p.kind in (0, 1))]) + + attrs = {} # class attributes + attrs["_cosmology_class"] = cosmo_cls + attrs["_method_name"] = method + attrs["n_inputs"] = n_inputs + attrs["n_outputs"] = 1 + + params = { + k: convert_parameter_to_model_parameter( + cosmo_cls.parameters[k], v, meta=cosmology.meta.get(k) + ) + for k, v in cosmology.parameters.items() + if v is not None + } + + # class name is cosmology name + Cosmology + method name + Model + clsname = ( + cosmo_cls.__qualname__.replace(".", "_") + + "Cosmology" + + method.replace("_", " ").title().replace(" ", "") + + "Model" + ) + + # make Model class + CosmoModel = type(clsname, (_CosmologyModel,), {**attrs, **params}) + # override __signature__ and format the doc. + CosmoModel.evaluate.__signature__ = msig + if CosmoModel.evaluate.__doc__ is not None: + # guard against PYTHONOPTIMIZE mode + CosmoModel.evaluate.__doc__ = CosmoModel.evaluate.__doc__.format( + cosmo_cls=cosmo_cls.__qualname__, method=method + ) + + # instantiate class using default values + return CosmoModel( + **cosmology.parameters, name=cosmology.name, meta=copy.deepcopy(cosmology.meta) + ) + + +def model_identify( + origin: str, format: str | None, *args: object, **kwargs: object +) -> bool: + """Identify if object uses the :class:`~astropy.modeling.Model` format. + + Returns + ------- + bool + """ + itis = False + if origin == "read": + itis = isinstance(args[1], Model) and (format in (None, "astropy.model")) + + return itis + + +# =================================================================== +# Register + +convert_registry.register_reader("astropy.model", Cosmology, from_model) +convert_registry.register_writer("astropy.model", Cosmology, to_model) +convert_registry.register_identifier("astropy.model", Cosmology, model_identify) diff --git a/astropy/cosmology/_src/io/builtin/mrt.py b/astropy/cosmology/_src/io/builtin/mrt.py new file mode 100644 index 000000000000..1217fcd11748 --- /dev/null +++ b/astropy/cosmology/_src/io/builtin/mrt.py @@ -0,0 +1,324 @@ +r"""|Cosmology| <-> MRT I/O, using |Cosmology.read| and |Cosmology.write|. + +We assume the following setup: + + >>> from pathlib import Path + >>> from tempfile import TemporaryDirectory + >>> temp_dir = TemporaryDirectory() + +Writing a cosmology to a mrt file will produce a table with the cosmology's type, +name, and parameters as columns. Note that the cosmology class is also included as +a column since MRT format does not preserve table metadata. + + >>> from astropy.cosmology import Planck18 + >>> file = Path(temp_dir.name) / "file.mrt" + >>> Planck18.write(file) + >>> with open(file) as f: print(f.read()) + Title: + Authors: + Table: + ================================================================================ + Byte-by-byte Description of file: table.dat + -------------------------------------------------------------------------------- + Bytes Format Units Label Explanations + -------------------------------------------------------------------------------- + 1-13 A13 --- cosmology Description of cosmology + 15-22 A8 --- name Description of name + 24-28 F5.2 km.Mpc-1.s-1 H0 [67.66/67.66] Hubble constant at z=0. + 30-36 F7.5 --- Om0 [0.3/0.31] Omega matter; matter + density/critical density at z=0. + 38-43 F6.4 K Tcmb0 [2.72/2.73] Temperature of the CMB at z=0. + 45-49 F5.3 --- Neff [3.04/3.05] Number of effective neutrino + species. + 51-64 A14 --- m_nu [[0. 0. 0.06]] Mass of neutrino + species. + 66-72 F7.5 --- Ob0 [0.04/0.05] Omega baryon; baryonic matter + density/critical density at z=0. + -------------------------------------------------------------------------------- + ... + + +.. testcleanup:: + + >>> temp_dir.cleanup() +""" + +__all__ = ("mrt_identify", "read_mrt", "write_mrt") + +import contextlib +import json +from typing import Any, TypeVar + +import astropy.cosmology.units as cu +import astropy.units as u +from astropy.cosmology._src.core import Cosmology +from astropy.cosmology._src.io.connect import readwrite_registry +from astropy.cosmology._src.typing import _CosmoT +from astropy.io.typing import PathLike, ReadableFileLike, WriteableFileLike +from astropy.table import Column, QTable, Table + +from .table import from_table, to_table + +_TableT = TypeVar("_TableT", bound=Table) + + +def read_mrt( + filename: PathLike | ReadableFileLike[Table], + /, + index: int | str | None = None, + *, + move_to_meta: bool = False, + cosmology: str | type[_CosmoT] | None = None, + **kwargs: Any, +) -> _CosmoT: + r"""Read a `~astropy.cosmology.Cosmology` from an MRT file. + + Parameters + ---------- + filename : path-like or file-like + From where to read the Cosmology. + + index : int, str, or None, optional + Needed to select the row in tables with multiple rows. ``index`` can be an + integer for the row number or, if the table is indexed by a column, the value of + that column. If the table is not indexed and ``index`` is a string, the "name" + column is used as the indexing column. + + move_to_meta : bool (optional, keyword-only) + Whether to move keyword arguments that are not in the Cosmology class' signature + to the Cosmology's metadata. This will only be applied if the Cosmology does NOT + have a keyword-only argument (e.g. ``**kwargs``). Arguments moved to the + metadata will be merged with existing metadata, preferring specified metadata in + the case of a merge conflict (e.g. for ``Cosmology(meta={'key':10}, key=42)``, + the ``Cosmology.meta`` will be ``{'key': 10}``). + + cosmology : str or type or None (optional, keyword-only) + The cosmology class (or string name thereof) to use when constructing the + cosmology instance. The class also provides default parameter values, filling in + any non-mandatory arguments missing in 'table'. + + **kwargs + Passed to ``QTable.read`` + + Returns + ------- + `~astropy.cosmology.Cosmology` subclass instance + + + Examples + -------- + We assume the following setup: + + >>> from pathlib import Path + >>> from tempfile import TemporaryDirectory + >>> temp_dir = TemporaryDirectory() + + Writing a cosmology to a Mrt file will produce a table with the cosmology's type, + name, and parameters as columns. + + >>> from astropy.cosmology import Planck18 + >>> file = Path(temp_dir.name) / "file.mrt" + + >>> Planck18.write(file, format="ascii.mrt") + >>> with open(file) as f: print(f.read()) + Title: + Authors: + Table: + ================================================================================ + Byte-by-byte Description of file: table.dat + -------------------------------------------------------------------------------- + Bytes Format Units Label Explanations + -------------------------------------------------------------------------------- + 1-13 A13 --- cosmology Description of cosmology + 15-22 A8 --- name Description of name + 24-28 F5.2 km.Mpc-1.s-1 H0 [67.66/67.66] Hubble constant at z=0. + 30-36 F7.5 --- Om0 [0.3/0.31] Omega matter; matter + density/critical density at z=0. + 38-43 F6.4 K Tcmb0 [2.72/2.73] Temperature of the CMB at z=0. + 45-49 F5.3 --- Neff [3.04/3.05] Number of effective neutrino + species. + 51-64 A14 --- m_nu [[0. 0. 0.06]] Mass of neutrino + species. + 66-72 F7.5 --- Ob0 [0.04/0.05] Omega baryon; baryonic matter + density/critical density at z=0. + -------------------------------------------------------------------------------- + ... + + .. testcleanup:: + + >>> temp_dir.cleanup() + """ + if (fmt := kwargs.pop("format", "ascii.mrt")) != "ascii.mrt": + raise ValueError(f"format must be 'ascii.mrt',not {fmt}") + + with u.add_enabled_units(cu): + table = QTable.read(filename, format="ascii.mrt", **kwargs) + + # Decode JSON-encoded columns (for arrays) + for col in table.itercols(): + # Check if this might be a JSON-encoded column (string type with array-like content) + if col.dtype.kind in ("U", "S", "O"): # Unicode, byte string, or object + with contextlib.suppress(json.JSONDecodeError, ValueError, TypeError): + # Try to decode the first value to see if it's JSON + first_val = col[0] + if isinstance(first_val, str | bytes): + decoded = json.loads(first_val) + # If successful and it's a list, decode all values + if isinstance(decoded, list): + decoded_data = [json.loads(val) for val in col] + # Replace the column with decoded values + table[col.name] = decoded_data + + # Build the cosmology from table, using the private backend. + return from_table( + table, index=index, move_to_meta=move_to_meta, cosmology=cosmology + ) + + +def write_mrt( + cosmo: Cosmology, + /, + file: PathLike | WriteableFileLike[_TableT], + *, + overwrite: bool = False, + cls: type[_TableT] = QTable, + **kwargs: Any, +): + r"""Serialize the |Cosmology| into a MRT table. + + Parameters + ---------- + cosmology : |Cosmology| subclass instance + The cosmology to serialize. + file : path-like or file-like + Where to write the MRT table. + overwrite : bool, optional keyword-only + Whether to overwrite the file, if it exists. + cls : |Table| class, optional keyword-only + Astropy |Table| (sub)class to use when writing. Default is |QTable| class. + **kwargs : Any + Passed to ``cls.write``. + + Raises + ------ + TypeError + If the optional keyword-argument 'cls' is not a subclass of |Table|. + ValueError + If the keyword argument 'format' is given and is not "ascii.mrt". + + Examples + -------- + We assume the following setup: + + >>> from pathlib import Path + >>> from tempfile import TemporaryDirectory + >>> temp_dir = TemporaryDirectory() + + Writing a cosmology to a MRT file will produce a table with the cosmology's type, + name, and parameters as columns. The cosmology class is included as a column + since MRT format does not preserve table metadata. + + >>> from astropy.cosmology import Planck18 + >>> file = Path(temp_dir.name) / "file.mrt" + >>> Planck18.write(file, overwrite=True) + >>> with open(file) as f: print(f.read()) + Title: + Authors: + Table: + ================================================================================ + Byte-by-byte Description of file: table.dat + -------------------------------------------------------------------------------- + Bytes Format Units Label Explanations + -------------------------------------------------------------------------------- + 1-13 A13 --- cosmology Description of cosmology + 15-22 A8 --- name Description of name + 24-28 F5.2 km.Mpc-1.s-1 H0 [67.66/67.66] Hubble constant at z=0. + 30-36 F7.5 --- Om0 [0.3/0.31] Omega matter; matter + density/critical density at z=0. + 38-43 F6.4 K Tcmb0 [2.72/2.73] Temperature of the CMB at z=0. + 45-49 F5.3 --- Neff [3.04/3.05] Number of effective neutrino + species. + 51-64 A14 --- m_nu [[0. 0. 0.06]] Mass of neutrino + species. + 66-72 F7.5 --- Ob0 [0.04/0.05] Omega baryon; baryonic matter + density/critical density at z=0. + -------------------------------------------------------------------------------- + ... + + + .. testcleanup:: + + >>> temp_dir.cleanup() + + Notes + ----- + + """ + if (fmt := kwargs.pop("format", "ascii.mrt")) != "ascii.mrt": + raise ValueError(f"format must be 'ascii.mrt', not {fmt}") + + table = to_table(cosmo, cls=cls, cosmology_in_meta=False) + + Parameters = cosmo.__class__.parameters # dict of Parameter objects + for name, col in table.columns.items(): + # MRT can't serialize redshift units, so remove them + if col.unit is cu.redshift: + table[name] <<= u.dimensionless_unscaled + + # check if col is multi dimensional + if len(col.shape) > 1 or col.info.dtype.kind == "0": + + def format_col_item(idx): + obj = col[idx] + # Get the value in the default units + if hasattr(obj, "to_value"): + obj = obj.to_value(Parameters[name].unit) + with contextlib.suppress(AttributeError): + obj = obj.tolist() + + return json.dumps(obj, separators=(",", ":")) + + try: + table[name] = Column( + data=[format_col_item(idx) for idx in range(len(col))], + name=name, + description=str(col.value) + " " + col.info.description, + ) + except TypeError as exc: + msg = f"could not convert column {col.info.name!r} to string: {exc}" + raise TypeError(msg) from exc + + # Write MRT + table.write(file, overwrite=overwrite, format="ascii.mrt", **kwargs) + + +def mrt_identify( + _: object, filepath: str | None, /, *args: object, **kwargs: object +) -> bool: + """Identify if an object uses the HTML Table format. + + Parameters + ---------- + origin : object + Not used. + filepath : object + From where to read the Cosmology. + *args : object + Not used. + **kwargs : object + Not used. + + Returns + ------- + bool + If the filepath is a string ending with '.mrt'. + """ + return filepath is not None and filepath.endswith(".mrt") + + +# =================================================================== +# Register + +readwrite_registry.register_reader("ascii.mrt", Cosmology, read_mrt) +readwrite_registry.register_writer("ascii.mrt", Cosmology, write_mrt) +readwrite_registry.register_identifier("ascii.mrt", Cosmology, mrt_identify) diff --git a/astropy/cosmology/_src/io/builtin/row.py b/astropy/cosmology/_src/io/builtin/row.py new file mode 100644 index 000000000000..eacc89827137 --- /dev/null +++ b/astropy/cosmology/_src/io/builtin/row.py @@ -0,0 +1,283 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +"""|Cosmology| <-> |Row| I/O, using |Cosmology.to_format| and |Cosmology.from_format|. + +A `~astropy.cosmology.Cosmology` as a `~astropy.table.Row` will have +the cosmology's name and parameters as columns. + + >>> from astropy.cosmology import Planck18 + >>> cr = Planck18.to_format("astropy.row") + >>> cr + + cosmology name H0 Om0 Tcmb0 Neff m_nu Ob0 + km / (Mpc s) K eV + str13 str8 float64 float64 float64 float64 float64[3] float64 + ------------- -------- ------------ ------- ------- ------- ----------- ------- + FlatLambdaCDM Planck18 67.66 0.30966 2.7255 3.046 0.0 .. 0.06 0.04897 + +The cosmological class and other metadata, e.g. a paper reference, are in +the Table's metadata. + + >>> cr.meta + {'Oc0': 0.2607, 'n': 0.9665, ...} + +Now this row can be used to load a new cosmological instance identical +to the ``Planck18`` cosmology from which it was generated. + + >>> cosmo = Cosmology.from_format(cr, format="astropy.row") + >>> cosmo + FlatLambdaCDM(name='Planck18', H0=, Om0=0.30966, Tcmb0=, Neff=3.046, m_nu=, Ob0=0.04897) + +For more information on the argument options, see :ref:`cosmology_io_builtin-table`. +""" + +import copy +from collections import defaultdict +from collections.abc import Mapping + +from astropy.table import QTable, Row, Table + +# isort: split +from astropy.cosmology._src.core import Cosmology +from astropy.cosmology._src.io.connect import convert_registry +from astropy.cosmology._src.typing import _CosmoT + +from .mapping import from_mapping + + +def from_row( + row: Row, + *, + move_to_meta: bool = False, + cosmology: str | type[_CosmoT] | None = None, + rename: Mapping[str, str] | None = None, +) -> _CosmoT: + """Instantiate a `~astropy.cosmology.Cosmology` from a `~astropy.table.Row`. + + Parameters + ---------- + row : `~astropy.table.Row` + The object containing the Cosmology information. + move_to_meta : bool (optional, keyword-only) + Whether to move keyword arguments that are not in the Cosmology class' + signature to the Cosmology's metadata. This will only be applied if the + Cosmology does NOT have a keyword-only argument (e.g. ``**kwargs``). + Arguments moved to the metadata will be merged with existing metadata, + preferring specified metadata in the case of a merge conflict + (e.g. for ``Cosmology(meta={'key':10}, key=42)``, the ``Cosmology.meta`` + will be ``{'key': 10}``). + + cosmology : str, type, or None (optional, keyword-only) + The cosmology class (or string name thereof) to use when constructing + the cosmology instance. The class also provides default parameter values, + filling in any non-mandatory arguments missing in 'table'. + + rename : Mapping[str, str] or None (optional, keyword-only) + A mapping of column names in the row to field names of the |Cosmology|. + + Returns + ------- + `~astropy.cosmology.Cosmology` + + Examples + -------- + To see loading a `~astropy.cosmology.Cosmology` from a Row with + ``from_row``, we will first make a `~astropy.table.Row` using + :func:`~astropy.cosmology.Cosmology.to_format`. + + >>> from astropy.cosmology import Cosmology, Planck18 + >>> cr = Planck18.to_format("astropy.row") + >>> cr + + cosmology name H0 Om0 Tcmb0 Neff m_nu Ob0 + km / (Mpc s) K eV + str13 str8 float64 float64 float64 float64 float64[3] float64 + ------------- -------- ------------ ------- ------- ------- ----------- ------- + FlatLambdaCDM Planck18 67.66 0.30966 2.7255 3.046 0.0 .. 0.06 0.04897 + + Now this row can be used to load a new cosmological instance identical + to the ``Planck18`` cosmology from which it was generated. + + >>> cosmo = Cosmology.from_format(cr, format="astropy.row") + >>> cosmo + FlatLambdaCDM(name='Planck18', H0=, Om0=0.30966, Tcmb0=, Neff=3.046, m_nu=, Ob0=0.04897) + + The ``cosmology`` information (column or metadata) may be omitted if the cosmology + class (or its string name) is passed as the ``cosmology`` keyword argument to + |Cosmology.from_format|. + + >>> del cr.columns["cosmology"] # remove cosmology from metadata + >>> Cosmology.from_format(cr, cosmology="FlatLambdaCDM") + FlatLambdaCDM(name='Planck18', H0=, Om0=0.30966, Tcmb0=, Neff=3.046, m_nu=, Ob0=0.04897) + + Alternatively, specific cosmology classes can be used to parse the data. + + >>> from astropy.cosmology import FlatLambdaCDM + >>> FlatLambdaCDM.from_format(cr) + FlatLambdaCDM(name='Planck18', H0=, Om0=0.30966, Tcmb0=, Neff=3.046, m_nu=, Ob0=0.04897) + + When using a specific cosmology class, the class' default parameter values are used + to fill in any missing information. + + >>> del cr.columns["Tcmb0"] # show FlatLambdaCDM provides default + >>> FlatLambdaCDM.from_format(cr) + FlatLambdaCDM(name='Planck18', H0=, Om0=0.30966, Tcmb0=, Neff=3.046, m_nu=None, Ob0=0.04897) + + If a `~astropy.table.Row` object has columns that do not match the fields of the + `~astropy.cosmology.Cosmology` class, they can be mapped using the ``rename`` + keyword argument. + + >>> renamed = Planck18.to_format("astropy.row", rename={"H0": "Hubble"}) + >>> renamed + + cosmology name Hubble Om0 Tcmb0 Neff m_nu Ob0 + km / (Mpc s) K eV + str13 str8 float64 float64 float64 float64 float64[3] float64 + ------------- -------- ------------ ------- ------- ------- ----------- ------- + FlatLambdaCDM Planck18 67.66 0.30966 2.7255 3.046 0.0 .. 0.06 0.04897 + + >>> cosmo = Cosmology.from_format(renamed, format="astropy.row", + ... rename={"Hubble": "H0"}) + >>> cosmo == Planck18 + True + """ + inv_rename = {v: k for k, v in rename.items()} if rename is not None else {} + kname = inv_rename.get("name", "name") + kmeta = inv_rename.get("meta", "meta") + kcosmo = inv_rename.get("cosmology", "cosmology") + + # special values + name = row.get(kname) + + meta = defaultdict(dict, copy.deepcopy(row.meta)) + # Now need to add the Columnar metadata. This is only available on the + # parent table. If Row is ever separated from Table, this should be moved + # to ``to_table``. + for col in row._table.itercols(): + if col.info.meta: # Only add metadata if not empty + meta[col.name].update(col.info.meta) + + # turn row into mapping, filling cosmo if not in a column + mapping = dict(row) + mapping[kname] = name + mapping.setdefault(kcosmo, meta.pop(kcosmo, None)) + mapping[kmeta] = dict(meta) + + # build cosmology from map + return from_mapping( + mapping, move_to_meta=move_to_meta, cosmology=cosmology, rename=rename + ) + + +def to_row( + cosmology: Cosmology, + *args: object, + cosmology_in_meta: bool = False, + table_cls: type[Table] = QTable, + rename: Mapping[str, str] | None = None, +) -> Row: + """Serialize the cosmology into a `~astropy.table.Row`. + + Parameters + ---------- + cosmology : `~astropy.cosmology.Cosmology` + The cosmology instance to convert to a mapping. + *args + Not used. Needed for compatibility with + `~astropy.io.registry.UnifiedReadWriteMethod` + table_cls : type (optional, keyword-only) + Astropy :class:`~astropy.table.Table` class or subclass type to use. Default is + :class:`~astropy.table.QTable`. + cosmology_in_meta : bool + Whether to put the cosmology class in the Table metadata (if `True`) or as the + first column (if `False`, default). + rename : Mapping[str, str] or None (optional, keyword-only) + A mapping of field names of the |Cosmology| to column names in the row. + + Returns + ------- + `~astropy.table.Row` + With columns for the cosmology parameters, and metadata in the Table's ``meta`` + attribute. The cosmology class name will either be a column or in ``meta``, + depending on 'cosmology_in_meta'. + + Examples + -------- + A `~astropy.cosmology.Cosmology` as a `~astropy.table.Row` will have the cosmology's + name and parameters as columns. + + >>> from astropy.cosmology import Planck18 + >>> cr = Planck18.to_format("astropy.row") + >>> cr + + cosmology name H0 Om0 Tcmb0 Neff m_nu Ob0 + km / (Mpc s) K eV + str13 str8 float64 float64 float64 float64 float64[3] float64 + ------------- -------- ------------ ------- ------- ------- ----------- ------- + FlatLambdaCDM Planck18 67.66 0.30966 2.7255 3.046 0.0 .. 0.06 0.04897 + + The cosmological class and other metadata, e.g. a paper reference, are in the + Table's metadata. + + >>> cr.meta + {'Oc0': 0.2607, 'n': 0.9665, ...} + + To move the cosmology class from a column to the Table's metadata, set the + ``cosmology_in_meta`` argument to `True`: + + >>> Planck18.to_format("astropy.table", cosmology_in_meta=True) + + name H0 Om0 Tcmb0 Neff m_nu Ob0 + km / (Mpc s) K eV + str8 float64 float64 float64 float64 float64[3] float64 + -------- ------------ ------- ------- ------- ----------- ------- + Planck18 67.66 0.30966 2.7255 3.046 0.0 .. 0.06 0.04897 + + In Astropy, Row objects are always part of a Table. :class:`~astropy.table.QTable` + is recommended for tables with |Quantity| columns. However the returned type may be + overridden using the ``cls`` argument: + + >>> from astropy.table import Table + >>> Planck18.to_format("astropy.table", cls=Table) + + ... + + The columns can be renamed using the ``rename`` keyword argument. + + >>> renamed = Planck18.to_format("astropy.row", rename={"H0": "Hubble"}) + >>> renamed + + cosmology name Hubble Om0 Tcmb0 Neff m_nu Ob0 + km / (Mpc s) K eV + str13 str8 float64 float64 float64 float64 float64[3] float64 + ------------- -------- ------------ ------- ------- ------- ----------- ------- + FlatLambdaCDM Planck18 67.66 0.30966 2.7255 3.046 0.0 .. 0.06 0.04897 + """ + from .table import to_table + + table = to_table( + cosmology, cls=table_cls, cosmology_in_meta=cosmology_in_meta, rename=rename + ) + return table[0] # extract row from table + + +def row_identify( + origin: str, format: str | None, *args: object, **kwargs: object +) -> bool: + """Identify if object uses the `~astropy.table.Row` format. + + Returns + ------- + bool + """ + itis = False + if origin == "read": + itis = isinstance(args[1], Row) and (format in (None, "astropy.row")) + return itis + + +# =================================================================== +# Register + +convert_registry.register_reader("astropy.row", Cosmology, from_row) +convert_registry.register_writer("astropy.row", Cosmology, to_row) +convert_registry.register_identifier("astropy.row", Cosmology, row_identify) diff --git a/astropy/cosmology/_src/io/builtin/table.py b/astropy/cosmology/_src/io/builtin/table.py new file mode 100644 index 000000000000..76c5bc061de0 --- /dev/null +++ b/astropy/cosmology/_src/io/builtin/table.py @@ -0,0 +1,484 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +"""|Cosmology| <-> |Table| I/O, using |Cosmology.to_format| and |Cosmology.from_format|. + +This module provides functions to transform a |Cosmology| object to and from a |Table| +object. The functions are registered with ``convert_registry`` under the format name +"astropy.table". |Table| itself has an abundance of I/O methods, making this conversion +useful for further interoperability with other formats. + +A Cosmology as a `~astropy.table.QTable` will have the cosmology's name and parameters +as columns. + + >>> from astropy.cosmology import Planck18 + >>> ct = Planck18.to_format("astropy.table") + >>> ct + + name H0 Om0 Tcmb0 Neff m_nu Ob0 + km / (Mpc s) K eV + str8 float64 float64 float64 float64 float64[3] float64 + -------- ------------ ------- ------- ------- ----------- ------- + Planck18 67.66 0.30966 2.7255 3.046 0.0 .. 0.06 0.04897 + +The cosmological class and other metadata, e.g. a paper reference, are in the Table's +metadata. + + >>> ct.meta + {..., 'cosmology': 'FlatLambdaCDM'} + + +Cosmology supports the astropy Table-like protocol (see :ref:`Table-like Objects`) to +the same effect: + +.. code-block:: + + >>> from astropy.table import QTable + >>> QTable(Planck18) + + name H0 Om0 Tcmb0 Neff m_nu Ob0 + km / (Mpc s) K eV + str8 float64 float64 float64 float64 float64[3] float64 + -------- ------------ ------- ------- ------- ----------- ------- + Planck18 67.66 0.30966 2.7255 3.046 0.0 .. 0.06 0.04897 + + +To move the cosmology class from the metadata to a Table row, set the +``cosmology_in_meta`` argument to `False`: + + >>> Planck18.to_format("astropy.table", cosmology_in_meta=False) + + cosmology name H0 Om0 Tcmb0 Neff m_nu Ob0 + km / (Mpc s) K eV + str13 str8 float64 float64 float64 float64 float64[3] float64 + ------------- -------- ------------ ------- ------- ------- ----------- ------- + FlatLambdaCDM Planck18 67.66 0.30966 2.7255 3.046 0.0 .. 0.06 0.04897 + +Astropy recommends `~astropy.table.QTable` for tables with |Quantity| columns. However +the returned type may be overridden using the ``cls`` argument: + + >>> from astropy.table import Table + >>> Planck18.to_format("astropy.table", cls=Table) +
+ ... + +Fields of the cosmology may be renamed using the ``rename`` argument. + + >>> Planck18.to_format("astropy.table", rename={"H0": "Hubble"}) + + name Hubble Om0 Tcmb0 Neff m_nu Ob0 + km / (Mpc s) K eV + str8 float64 float64 float64 float64 float64[3] float64 + -------- ------------ ------- ------- ------- ----------- ------- + Planck18 67.66 0.30966 2.7255 3.046 0.0 .. 0.06 0.04897 + +Appropriately formatted tables can be converted to |Cosmology| instances. Since the +|Table| can hold arbitrary metadata, we can faithfully round-trip a |Cosmology| through +|Table|, e.g. to construct a ``Planck18`` cosmology identical to the instance from which +it was generated. + + >>> ct = Planck18.to_format("astropy.table") + + >>> cosmo = Cosmology.from_format(ct, format="astropy.table") + >>> cosmo + FlatLambdaCDM(name='Planck18', H0=, Om0=0.30966, Tcmb0=, Neff=3.046, m_nu=, Ob0=0.04897) + >>> cosmo == Planck18 + True + +The ``cosmology`` information (row or metadata) may be omitted if the cosmology class +(or its string name) is passed as the ``cosmology`` keyword argument to +|Cosmology.from_format|. + + >>> del ct.meta["cosmology"] # remove cosmology from metadata + >>> Cosmology.from_format(ct, cosmology="FlatLambdaCDM") + FlatLambdaCDM(name='Planck18', H0=, Om0=0.30966, Tcmb0=, Neff=3.046, m_nu=, Ob0=0.04897) + +Alternatively, specific cosmology classes can be used to parse the data. + + >>> from astropy.cosmology import FlatLambdaCDM + >>> FlatLambdaCDM.from_format(ct) + FlatLambdaCDM(name='Planck18', H0=, Om0=0.30966, Tcmb0=, Neff=3.046, m_nu=, Ob0=0.04897) + +When using a specific cosmology class, the class' default parameter values are used to +fill in any missing information. + + >>> del ct["Tcmb0"] # show FlatLambdaCDM provides default + >>> FlatLambdaCDM.from_format(ct) + FlatLambdaCDM(name='Planck18', H0=, Om0=0.30966, Tcmb0=, Neff=3.046, m_nu=None, Ob0=0.04897) + +For tables with multiple rows of cosmological parameters, the ``index`` argument is +needed to select the correct row. The index can be an integer for the row number or, if +the table is indexed by a column, the value of that column. If the table is not indexed +and ``index`` is a string, the "name" column is used as the indexing column. + +Here is an example where ``index`` is needed and can be either an integer (for the row +number) or the name of one of the cosmologies, e.g. 'Planck15'. + + >>> from astropy.cosmology import Planck13, Planck15, Planck18 + >>> from astropy.table import vstack + >>> cts = vstack([c.to_format("astropy.table") + ... for c in (Planck13, Planck15, Planck18)], + ... metadata_conflicts='silent') + >>> cts + + name H0 Om0 Tcmb0 Neff m_nu Ob0 + km / (Mpc s) K eV + str8 float64 float64 float64 float64 float64[3] float64 + -------- ------------ ------- ------- ------- ----------- -------- + Planck13 67.77 0.30712 2.7255 3.046 0.0 .. 0.06 0.048252 + Planck15 67.74 0.3075 2.7255 3.046 0.0 .. 0.06 0.0486 + Planck18 67.66 0.30966 2.7255 3.046 0.0 .. 0.06 0.04897 + + >>> cosmo = Cosmology.from_format(cts, index=1, format="astropy.table") + >>> cosmo == Planck15 + True + +Fields in the table can be renamed to match the `~astropy.cosmology.Cosmology` class' +signature using the ``rename`` argument. This is useful when the table's column names do +not match the class' parameter names. + + >>> renamed_table = Planck18.to_format("astropy.table", rename={"H0": "Hubble"}) + >>> renamed_table + + name Hubble Om0 Tcmb0 Neff m_nu Ob0 + km / (Mpc s) K eV + str8 float64 float64 float64 float64 float64[3] float64 + -------- ------------ ------- ------- ------- ----------- ------- + Planck18 67.66 0.30966 2.7255 3.046 0.0 .. 0.06 0.04897 + + >>> cosmo = Cosmology.from_format(renamed_table, format="astropy.table", + ... rename={"Hubble": "H0"}) + >>> cosmo == Planck18 + True +""" + +from collections.abc import Mapping +from typing import TypeVar + +import numpy as np + +from astropy.cosmology._src.core import Cosmology +from astropy.cosmology._src.io.connect import convert_registry +from astropy.cosmology._src.typing import _CosmoT +from astropy.table import Column, QTable, Table + +from .mapping import to_mapping +from .row import from_row +from .utils import convert_parameter_to_column + +_TableT = TypeVar("_TableT", bound=Table) + + +def from_table( + table: Table, + index: int | str | None = None, + *, + move_to_meta: bool = False, + cosmology: str | type[_CosmoT] | None = None, + rename: Mapping[str, str] | None = None, +) -> _CosmoT: + """Instantiate a `~astropy.cosmology.Cosmology` from a |QTable|. + + Parameters + ---------- + table : `~astropy.table.Table` + The object to parse into a |Cosmology|. + index : int, str, or None, optional + Needed to select the row in tables with multiple rows. ``index`` can be an + integer for the row number or, if the table is indexed by a column, the value of + that column. If the table is not indexed and ``index`` is a string, the "name" + column is used as the indexing column. + + move_to_meta : bool (optional, keyword-only) + Whether to move keyword arguments that are not in the Cosmology class' signature + to the Cosmology's metadata. This will only be applied if the Cosmology does NOT + have a keyword-only argument (e.g. ``**kwargs``). Arguments moved to the + metadata will be merged with existing metadata, preferring specified metadata in + the case of a merge conflict (e.g. for ``Cosmology(meta={'key':10}, key=42)``, + the ``Cosmology.meta`` will be ``{'key': 10}``). + + cosmology : str or type or None (optional, keyword-only) + The cosmology class (or string name thereof) to use when constructing the + cosmology instance. The class also provides default parameter values, filling in + any non-mandatory arguments missing in 'table'. + + rename : Mapping[str, str] or None (optional, keyword-only) + A mapping of column names in 'table' to field names of the |Cosmology| class. + + Returns + ------- + `~astropy.cosmology.Cosmology` + + Examples + -------- + To see loading a `~astropy.cosmology.Cosmology` from a Table with ``from_table``, we + will first make a |QTable| using :func:`~astropy.cosmology.Cosmology.to_format`. + + >>> from astropy.cosmology import Cosmology, Planck18 + >>> ct = Planck18.to_format("astropy.table") + >>> ct + + name H0 Om0 Tcmb0 Neff m_nu Ob0 + km / (Mpc s) K eV + str8 float64 float64 float64 float64 float64[3] float64 + -------- ------------ ------- ------- ------- ----------- ------- + Planck18 67.66 0.30966 2.7255 3.046 0.0 .. 0.06 0.04897 + + Now this table can be used to load a new cosmological instance identical to the + ``Planck18`` cosmology from which it was generated. + + >>> cosmo = Cosmology.from_format(ct, format="astropy.table") + >>> cosmo + FlatLambdaCDM(name='Planck18', H0=, Om0=0.30966, Tcmb0=, Neff=3.046, m_nu=, Ob0=0.04897) + + The ``cosmology`` information (column or metadata) may be omitted if the cosmology + class (or its string name) is passed as the ``cosmology`` keyword argument to + |Cosmology.from_format|. + + >>> del ct.meta["cosmology"] # remove cosmology from metadata + >>> Cosmology.from_format(ct, cosmology="FlatLambdaCDM") + FlatLambdaCDM(name='Planck18', H0=, Om0=0.30966, Tcmb0=, Neff=3.046, m_nu=, Ob0=0.04897) + + Alternatively, specific cosmology classes can be used to parse the data. + + >>> from astropy.cosmology import FlatLambdaCDM + >>> FlatLambdaCDM.from_format(ct) + FlatLambdaCDM(name='Planck18', H0=, Om0=0.30966, Tcmb0=, Neff=3.046, m_nu=, Ob0=0.04897) + + When using a specific cosmology class, the class' default parameter values are used + to fill in any missing information. + + >>> del ct["Tcmb0"] # show FlatLambdaCDM provides default + >>> FlatLambdaCDM.from_format(ct) + FlatLambdaCDM(name='Planck18', H0=, Om0=0.30966, Tcmb0=, Neff=3.046, m_nu=None, Ob0=0.04897) + + For tables with multiple rows of cosmological parameters, the ``index`` argument is + needed to select the correct row. The index can be an integer for the row number or, + if the table is indexed by a column, the value of that column. If the table is not + indexed and ``index`` is a string, the "name" column is used as the indexing column. + + Here is an example where ``index`` is needed and can be either an integer (for the + row number) or the name of one of the cosmologies, e.g. 'Planck15'. + + >>> from astropy.cosmology import Planck13, Planck15, Planck18 + >>> from astropy.table import vstack + >>> cts = vstack([c.to_format("astropy.table") + ... for c in (Planck13, Planck15, Planck18)], + ... metadata_conflicts='silent') + >>> cts + + name H0 Om0 Tcmb0 Neff m_nu Ob0 + km / (Mpc s) K eV + str8 float64 float64 float64 float64 float64[3] float64 + -------- ------------ ------- ------- ------- ----------- -------- + Planck13 67.77 0.30712 2.7255 3.046 0.0 .. 0.06 0.048252 + Planck15 67.74 0.3075 2.7255 3.046 0.0 .. 0.06 0.0486 + Planck18 67.66 0.30966 2.7255 3.046 0.0 .. 0.06 0.04897 + + >>> cosmo = Cosmology.from_format(cts, index="Planck15", format="astropy.table") + >>> cosmo == Planck15 + True + + Fields in the table can be renamed to match the `~astropy.cosmology.Cosmology` + class' signature using the ``rename`` argument. This is useful when the table's + column names do not match the class' parameter names. + + >>> renamed_table = Planck18.to_format("astropy.table", rename={"H0": "Hubble"}) + >>> renamed_table + + name Hubble Om0 Tcmb0 Neff m_nu Ob0 + km / (Mpc s) K eV + str8 float64 float64 float64 float64 float64[3] float64 + -------- ------------ ------- ------- ------- ----------- ------- + Planck18 67.66 0.30966 2.7255 3.046 0.0 .. 0.06 0.04897 + + >>> cosmo = Cosmology.from_format(renamed_table, format="astropy.table", + ... rename={"Hubble": "H0"}) + >>> cosmo == Planck18 + True + + For further examples, see :doc:`astropy:cosmology/io`. + """ + # Get row from table + # string index uses the indexed column on the table to find the row index. + if isinstance(index, str): + if not table.indices: # no indexing column, find by string match + nc = "name" # default name column + if rename is not None: # from inverted `rename` + for key, value in rename.items(): + if value == "name": + nc = key + break + + indices = np.where(table[nc] == index)[0] + else: # has indexing column + indices = table.loc_indices[index] # need to convert to row index (int) + + if isinstance(indices, (int, np.integer)): # loc_indices + index = indices + elif len(indices) == 1: # only happens w/ np.where + index = indices[0] + elif len(indices) == 0: # matches from loc_indices + raise KeyError(f"No matches found for key {indices}") + else: # like the Highlander, there can be only 1 Cosmology + raise ValueError(f"more than one cosmology found for key {indices}") + + # no index is needed for a 1-row table. For a multi-row table... + if index is None: + if len(table) != 1: # multi-row table and no index + raise ValueError( + "need to select a specific row (e.g. index=1) when " + "constructing a Cosmology from a multi-row table." + ) + else: # single-row table + index = 0 + row = table[index] # index is now the row index (int) + + # parse row to cosmo + return from_row(row, move_to_meta=move_to_meta, cosmology=cosmology, rename=rename) + + +def to_table( + cosmology: Cosmology, + *args: object, + cls: type[_TableT] = QTable, + cosmology_in_meta: bool = True, + rename: Mapping[str, str] | None = None, +) -> _TableT: + """Serialize the cosmology into a `~astropy.table.QTable`. + + Parameters + ---------- + cosmology : `~astropy.cosmology.Cosmology` + The cosmology instance to convert to a table. + *args : object + Not used. Needed for compatibility with + `~astropy.io.registry.UnifiedReadWriteMethod` + cls : type (optional, keyword-only) + Astropy :class:`~astropy.table.Table` class or subclass type to return. + Default is :class:`~astropy.table.QTable`. + cosmology_in_meta : bool (optional, keyword-only) + Whether to put the cosmology class in the Table metadata (if `True`, + default) or as the first column (if `False`). + rename : Mapping[str, str] or None (optional, keyword-only) + A mapping of field names of the |Cosmology| class to column names in the + |Table|. + + Returns + ------- + `~astropy.table.QTable` + With columns for the cosmology parameters, and metadata and + cosmology class name in the Table's ``meta`` attribute + + Raises + ------ + TypeError + If kwarg (optional) 'cls' is not a subclass of `astropy.table.Table` + + Examples + -------- + A Cosmology as a `~astropy.table.QTable` will have the cosmology's name and + parameters as columns. + + >>> from astropy.cosmology import Planck18 + >>> ct = Planck18.to_format("astropy.table") + >>> ct + + name H0 Om0 Tcmb0 Neff m_nu Ob0 + km / (Mpc s) K eV + str8 float64 float64 float64 float64 float64[3] float64 + -------- ------------ ------- ------- ------- ----------- ------- + Planck18 67.66 0.30966 2.7255 3.046 0.0 .. 0.06 0.04897 + + The cosmological class and other metadata, e.g. a paper reference, are in + the Table's metadata. + + >>> ct.meta + {..., 'cosmology': 'FlatLambdaCDM'} + + To move the cosmology class from the metadata to a Table column, set the + ``cosmology_in_meta`` argument to `False`: + + >>> Planck18.to_format("astropy.table", cosmology_in_meta=False) + + cosmology name H0 Om0 Tcmb0 Neff m_nu Ob0 + km / (Mpc s) K eV + str13 str8 float64 float64 float64 float64 float64[3] float64 + ------------- -------- ------------ ------- ------- ------- ----------- ------- + FlatLambdaCDM Planck18 67.66 0.30966 2.7255 3.046 0.0 .. 0.06 0.04897 + + Astropy recommends `~astropy.table.QTable` for tables with |Quantity| columns. + However the returned type may be overridden using the ``cls`` argument: + + >>> from astropy.table import Table + >>> Planck18.to_format("astropy.table", cls=Table) +
+ ... + + Fields of the cosmology may be renamed using the ``rename`` argument. + + >>> Planck18.to_format("astropy.table", rename={"H0": "Hubble"}) + + name Hubble Om0 Tcmb0 Neff m_nu Ob0 + km / (Mpc s) K eV + str8 float64 float64 float64 float64 float64[3] float64 + -------- ------------ ------- ------- ------- ----------- ------- + Planck18 67.66 0.30966 2.7255 3.046 0.0 .. 0.06 0.04897 + """ + if not issubclass(cls, Table): + raise TypeError(f"'cls' must be a (sub)class of Table, not {type(cls)}") + + # Start by getting a map representation. + data = to_mapping(cosmology) + data["cosmology"] = data["cosmology"].__qualname__ # change to str + + # Metadata + meta = data.pop("meta") # remove the meta + if cosmology_in_meta: + meta["cosmology"] = data.pop("cosmology") + + # Need to turn everything into something Table can process: + # - Column for Parameter + # - list for anything else + cosmo_cls = cosmology.__class__ + for k, v in data.items(): + if k in cosmology.parameters: + col = convert_parameter_to_column( + cosmo_cls.parameters[k], v, cosmology.meta.get(k) + ) + else: + col = Column([v]) + data[k] = col + + tbl = cls(data, meta=meta) + + # Renames + renames = rename or {} + for name in tbl.colnames: + tbl.rename_column(name, renames.get(name, name)) + + # Add index + tbl.add_index(renames.get("name", "name"), unique=True) + + return tbl + + +def table_identify( + origin: str, format: str | None, *args: object, **kwargs: object +) -> bool: + """Identify if object uses the Table format. + + Returns + ------- + bool + """ + itis = False + if origin == "read": + itis = isinstance(args[1], Table) and (format in (None, "astropy.table")) + return itis + + +# =================================================================== +# Register + +convert_registry.register_reader("astropy.table", Cosmology, from_table) +convert_registry.register_writer("astropy.table", Cosmology, to_table) +convert_registry.register_identifier("astropy.table", Cosmology, table_identify) diff --git a/astropy/cosmology/_src/io/builtin/utils.py b/astropy/cosmology/_src/io/builtin/utils.py new file mode 100644 index 000000000000..58e8d2e49da6 --- /dev/null +++ b/astropy/cosmology/_src/io/builtin/utils.py @@ -0,0 +1,70 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +import numpy as np + +from astropy.modeling import Parameter as ModelParameter +from astropy.table import Column + + +def convert_parameter_to_column(parameter, value, meta=None): + """Convert a |Cosmology| Parameter to a Table |Column|. + + Parameters + ---------- + parameter : `astropy.cosmology._src.parameter.Parameter` + value : Any + meta : dict or None, optional + Information from the Cosmology's metadata. + + Returns + ------- + `astropy.table.Column` + """ + shape = (1,) + np.shape(value) # minimum of 1d + + return Column( + data=np.reshape(value, shape), + name=parameter.name, + dtype=None, # inferred from the data + description=parameter.__doc__, + format=None, + meta=meta, + ) + + +def convert_parameter_to_model_parameter(parameter, value, meta=None): + """Convert a Cosmology Parameter to a Model Parameter. + + Parameters + ---------- + parameter : `astropy.cosmology._src.parameter.Parameter` + value : Any + meta : dict or None, optional + Information from the Cosmology's metadata. + This function will use any of: 'getter', 'setter', 'fixed', 'tied', + 'min', 'max', 'bounds', 'prior', 'posterior'. + + Returns + ------- + `astropy.modeling.Parameter` + """ + # Get from meta information relevant to Model + attrs = ( + "getter", + "setter", + "fixed", + "tied", + "min", + "max", + "bounds", + "prior", + "posterior", + ) + extra = {k: v for k, v in (meta or {}).items() if k in attrs} + + return ModelParameter( + description=parameter.__doc__, + default=value, + unit=getattr(value, "unit", None), + **extra, + ) diff --git a/astropy/cosmology/_src/io/builtin/yaml.py b/astropy/cosmology/_src/io/builtin/yaml.py new file mode 100644 index 000000000000..8a0f0ac77c73 --- /dev/null +++ b/astropy/cosmology/_src/io/builtin/yaml.py @@ -0,0 +1,265 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +r"""|Cosmology| <-> YAML I/O, using |Cosmology.to_format| and |Cosmology.from_format|. + +This module provides functions to transform a |Cosmology| object to and from a `yaml +`_ representation. The functions are registered with +``convert_registry`` under the format name "yaml". This format is primarily intended for +use by other I/O functions, e.g. |Table|'s metadata serialization, which themselves +require YAML serialization. + + >>> from astropy.cosmology import Planck18 + >>> yml = Planck18.to_format("yaml") + >>> yml # doctest: +NORMALIZE_WHITESPACE + "!astropy.cosmology...FlatLambdaCDM\nH0: !astropy.units.Quantity... + + >>> print(Cosmology.from_format(yml, format="yaml")) + FlatLambdaCDM(name="Planck18", H0=67.66 km / (Mpc s), Om0=0.30966, + Tcmb0=2.7255 K, Neff=3.046, m_nu=[0. 0. 0.06] eV, Ob0=0.04897) +""" # this is shown in the docs. + +__all__: list[str] = [] # nothing is publicly scoped + +from collections.abc import Callable + +from yaml import MappingNode + +import astropy.units as u +from astropy.io.misc.yaml import AstropyDumper, AstropyLoader, dump, load + +# isort: split +import astropy.cosmology.units as cu +from astropy.cosmology._src.core import _COSMOLOGY_CLASSES, Cosmology +from astropy.cosmology._src.io.connect import convert_registry +from astropy.cosmology._src.typing import _CosmoT + +from .mapping import from_mapping + +FULLQUALNAME_SUBSTITUTIONS = { + "astropy.cosmology._src.flrw.base.FLRW": "astropy.cosmology.FLRW", + "astropy.cosmology._src.flrw.lambdacdm.LambdaCDM": "astropy.cosmology.LambdaCDM", + "astropy.cosmology._src.flrw.lambdacdm.FlatLambdaCDM": ( + "astropy.cosmology.FlatLambdaCDM" + ), + "astropy.cosmology._src.flrw.w0wacdm.w0waCDM": "astropy.cosmology.w0waCDM", + "astropy.cosmology._src.flrw.w0wacdm.Flatw0waCDM": "astropy.cosmology.Flatw0waCDM", + "astropy.cosmology._src.flrw.w0wzcdm.w0wzCDM": "astropy.cosmology.w0wzCDM", + "astropy.cosmology._src.flrw.w0cdm.wCDM": "astropy.cosmology.wCDM", + "astropy.cosmology._src.flrw.w0cdm.FlatwCDM": "astropy.cosmology.FlatwCDM", + "astropy.cosmology._src.flrw.wpwazpcdm.wpwaCDM": "astropy.cosmology.wpwaCDM", + # ==== Paths removed in v8.0 ==== + "astropy.cosmology.flrw.base.FLRW": "astropy.cosmology.FLRW", + "astropy.cosmology.flrw.lambdacdm.LambdaCDM": "astropy.cosmology.LambdaCDM", + "astropy.cosmology.flrw.lambdacdm.FlatLambdaCDM": "astropy.cosmology.FlatLambdaCDM", + "astropy.cosmology.flrw.w0wacdm.w0waCDM": "astropy.cosmology.w0waCDM", + "astropy.cosmology.flrw.w0wacdm.Flatw0waCDM": "astropy.cosmology.Flatw0waCDM", + "astropy.cosmology.flrw.w0wzcdm.w0wzCDM": "astropy.cosmology.w0wzCDM", + "astropy.cosmology.flrw.w0cdm.wCDM": "astropy.cosmology.wCDM", + "astropy.cosmology.flrw.w0cdm.FlatwCDM": "astropy.cosmology.FlatwCDM", + "astropy.cosmology.flrw.wpwazpcdm.wpwaCDM": "astropy.cosmology.wpwaCDM", +} +"""Substitutions mapping the actual qualified name to its preferred value.""" + + +############################################################################## +# Serializer Functions +# these do Cosmology <-> YAML through a modified dictionary representation of +# the Cosmology object. The Unified-I/O functions are just wrappers to the YAML +# that calls these functions. + + +_representer_doc = """Cosmology yaml representer function for {}. + +Parameters +---------- +dumper : :class:`~astropy.io.misc.yaml.AstropyDumper` + The dumper object with which to serialize the |Cosmology| object. +obj : :class:`~astropy.cosmology.Cosmology` + The |Cosmology| object to serialize. + +Returns +------- +str + :mod:`yaml` representation of |Cosmology| object. +""" + + +def yaml_representer(tag: str) -> Callable[[AstropyDumper, Cosmology], str]: + """`yaml `_ representation of |Cosmology| object. + + Parameters + ---------- + tag : str + The class tag, e.g. '!astropy.cosmology.LambdaCDM' + + Returns + ------- + representer : callable[[`~astropy.io.misc.yaml.AstropyDumper`, |Cosmology|], str] + Function to construct :mod:`yaml` representation of |Cosmology| object. + """ + + def representer(dumper: AstropyDumper, obj: Cosmology) -> str: + # convert to mapping + map = obj.to_format("mapping") + # remove the cosmology class info. It's already recorded in `tag` + map.pop("cosmology") + # make the metadata serializable in an order-preserving way. + map["meta"] = tuple(map["meta"].items()) + + return dumper.represent_mapping(tag, map) + + representer.__doc__ = _representer_doc.format(tag) + + return representer + + +def yaml_constructor( + cls: type[_CosmoT], +) -> Callable[[AstropyLoader, MappingNode], _CosmoT]: + """Cosmology| object from :mod:`yaml` representation. + + Parameters + ---------- + cls : type + The class type, e.g. `~astropy.cosmology.LambdaCDM`. + + Returns + ------- + constructor : callable + Function to construct |Cosmology| object from :mod:`yaml` representation. + """ + + def constructor(loader: AstropyLoader, node: MappingNode) -> _CosmoT: + """Cosmology yaml constructor function. + + Parameters + ---------- + loader : `~astropy.io.misc.yaml.AstropyLoader` + node : `yaml.nodes.MappingNode` + yaml representation of |Cosmology| object. + + Returns + ------- + `~astropy.cosmology.Cosmology` subclass instance + """ + # create mapping from YAML node + map = loader.construct_mapping(node) + # restore metadata to dict + map["meta"] = dict(map["meta"]) + # get cosmology class qualified name from node + cosmology = str(node.tag).split(".")[-1] + # create Cosmology from mapping + return from_mapping(map, move_to_meta=False, cosmology=cosmology) + + return constructor + + +def register_cosmology_yaml(cosmo_cls: type[Cosmology]) -> None: + """Register :mod:`yaml` for Cosmology class. + + Parameters + ---------- + cosmo_cls : `~astropy.cosmology.Cosmology` class + """ + fqn = f"{cosmo_cls.__module__}.{cosmo_cls.__qualname__}" + fqn = FULLQUALNAME_SUBSTITUTIONS.get(fqn, fqn) # Possibly sub for a preferred path + tag = "!" + fqn + + AstropyDumper.add_representer(cosmo_cls, yaml_representer(tag)) + AstropyLoader.add_constructor(tag, yaml_constructor(cosmo_cls)) + + +############################################################################## +# Unified-I/O Functions + + +def from_yaml(yml: str, *, cosmology: type[_CosmoT] | None = None) -> _CosmoT: + """Load `~astropy.cosmology.Cosmology` from :mod:`yaml` object. + + Parameters + ---------- + yml : str + :mod:`yaml` representation of |Cosmology| object + cosmology : str, |Cosmology| class, or None (optional, keyword-only) + The expected cosmology class (or string name thereof). This argument is + is only checked for correctness if not `None`. + + Returns + ------- + `~astropy.cosmology.Cosmology` subclass instance + + Raises + ------ + TypeError + If the |Cosmology| object loaded from ``yml`` is not an instance of + the ``cosmology`` (and ``cosmology`` is not `None`). + + Examples + -------- + >>> from astropy.cosmology import Cosmology, Planck18 + >>> yml = Planck18.to_format("yaml") + >>> print(Cosmology.from_format(yml, format="yaml")) + FlatLambdaCDM(name="Planck18", H0=67.66 km / (Mpc s), Om0=0.30966, + Tcmb0=2.7255 K, Neff=3.046, m_nu=[0. 0. 0.06] eV, Ob0=0.04897) + """ + with u.add_enabled_units(cu): + cosmo = load(yml) + + # Check argument `cosmology`, if not None + # This kwarg is required for compatibility with |Cosmology.from_format| + if isinstance(cosmology, str): + cosmology = _COSMOLOGY_CLASSES[cosmology] + if cosmology is not None and not isinstance(cosmo, cosmology): + raise TypeError(f"cosmology {cosmo} is not an {cosmology} instance.") + + return cosmo + + +def to_yaml(cosmology: Cosmology, *args: object) -> str: + r"""Return the cosmology class, parameters, and metadata as a :mod:`yaml` object. + + Parameters + ---------- + cosmology : `~astropy.cosmology.Cosmology` subclass instance + The cosmology to serialize. + *args : Any + Not used. Needed for compatibility with + `~astropy.io.registry.UnifiedReadWriteMethod` + + Returns + ------- + str + :mod:`yaml` representation of |Cosmology| object + + Examples + -------- + >>> from astropy.cosmology import Planck18 + >>> Planck18.to_format("yaml") + "!astropy.cosmology...FlatLambdaCDM\nH0: !astropy.units.Quantity... + """ + return dump(cosmology) + + +# ``read`` cannot handle non-path strings. +# TODO! this says there should be different types of I/O registries. +# not just hacking object conversion on top of file I/O. +# def yaml_identify(origin, format, *args, **kwargs): +# """Identify if object uses the yaml format. +# +# Returns +# ------- +# bool +# """ +# itis = False +# if origin == "read": +# itis = isinstance(args[1], str) and args[1][0].startswith("!") +# itis &= format in (None, "yaml") +# +# return itis + + +# =================================================================== +# Register + +convert_registry.register_reader("yaml", Cosmology, from_yaml) +convert_registry.register_writer("yaml", Cosmology, to_yaml) +# convert_registry.register_identifier("yaml", Cosmology, yaml_identify) diff --git a/astropy/cosmology/_src/io/connect.py b/astropy/cosmology/_src/io/connect.py new file mode 100644 index 000000000000..650988912e2a --- /dev/null +++ b/astropy/cosmology/_src/io/connect.py @@ -0,0 +1,369 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +__all__ = ( + # classes + "CosmologyFromFormat", + "CosmologyRead", + "CosmologyToFormat", + "CosmologyWrite", +) + +from collections.abc import Mapping +from typing import TYPE_CHECKING, Any, Literal, TypeVar, overload + +from astropy.cosmology._src.typing import _CosmoT +from astropy.io import registry as io_registry +from astropy.table import Row, Table +from astropy.units import add_enabled_units + +# isort: split +import astropy.cosmology._src.units as cu + +if TYPE_CHECKING: + import astropy.cosmology + + +__doctest_skip__ = __all__ + + +# NOTE: private b/c RTD error +_MT = TypeVar("_MT", bound=Mapping) # type: ignore[type-arg] + + +# ============================================================================== +# Read / Write + +readwrite_registry = io_registry.UnifiedIORegistry() + + +class CosmologyRead(io_registry.UnifiedReadWrite): + """Read and parse data to a `~astropy.cosmology.Cosmology`. + + This function provides the Cosmology interface to the Astropy unified I/O + layer. This allows easily reading a file in supported data formats using + syntax such as:: + + >>> from astropy.cosmology import Cosmology + >>> cosmo1 = Cosmology.read('') + + When the ``read`` method is called from a subclass the subclass will + provide a keyword argument ``cosmology=`` to the registered read + method. The method uses this cosmology class, regardless of the class + indicated in the file, and sets parameters' default values from the class' + signature. + + Get help on the available readers using the ``help()`` method:: + + >>> Cosmology.read.help() # Get help reading and list supported formats + >>> Cosmology.read.help(format='') # Get detailed help on a format + >>> Cosmology.read.list_formats() # Print list of available formats + + See also: https://docs.astropy.org/en/stable/io/unified.html + + Parameters + ---------- + *args + Positional arguments passed through to data reader. If supplied the + first argument is typically the input filename. + format : str (optional, keyword-only) + File format specifier. + **kwargs + Keyword arguments passed through to data reader. + + Returns + ------- + out : `~astropy.cosmology.Cosmology` subclass instance + `~astropy.cosmology.Cosmology` corresponding to file contents. + + Notes + ----- + """ + + def __init__( + self, + instance: "astropy.cosmology.Cosmology", + cosmo_cls: type["astropy.cosmology.Cosmology"], + ) -> None: + super().__init__(instance, cosmo_cls, "read", registry=readwrite_registry) + + def __call__(self, *args: Any, **kwargs: Any) -> "astropy.cosmology.Cosmology": + from astropy.cosmology._src.core import Cosmology + + # so subclasses can override, also pass the class as a kwarg. + # allows for `FlatLambdaCDM.read` and + # `Cosmology.read(..., cosmology=FlatLambdaCDM)` + if self._cls is not Cosmology: + kwargs.setdefault("cosmology", self._cls) # set, if not present + # check that it is the correct cosmology, can be wrong if user + # passes in e.g. `w0wzCDM.read(..., cosmology=FlatLambdaCDM)` + valid = (self._cls, self._cls.__qualname__) + if kwargs["cosmology"] not in valid: + raise ValueError( + "keyword argument `cosmology` must be either the class " + f"{valid[0]} or its qualified name '{valid[1]}'" + ) + + with add_enabled_units(cu): + return self.registry.read(self._cls, *args, **kwargs) + + +class CosmologyWrite(io_registry.UnifiedReadWrite): + """Write this Cosmology object out in the specified format. + + This function provides the Cosmology interface to the astropy unified I/O + layer. This allows easily writing a file in supported data formats + using syntax such as:: + + >>> from astropy.cosmology import Planck18 + >>> Planck18.write('') + + Get help on the available writers for ``Cosmology`` using the ``help()`` + method:: + + >>> Cosmology.write.help() # Get help writing and list supported formats + >>> Cosmology.write.help(format='') # Get detailed help on format + >>> Cosmology.write.list_formats() # Print list of available formats + + Parameters + ---------- + *args + Positional arguments passed through to data writer. If supplied the + first argument is the output filename. + format : str (optional, keyword-only) + File format specifier. + **kwargs + Keyword arguments passed through to data writer. + + Notes + ----- + """ + + def __init__( + self, + instance: "astropy.cosmology.Cosmology", + cls: type["astropy.cosmology.Cosmology"], + ) -> None: + super().__init__(instance, cls, "write", registry=readwrite_registry) + + def __call__(self, *args: Any, **kwargs: Any) -> None: + self.registry.write(self._instance, *args, **kwargs) + + +# ============================================================================== +# Format Interchange +# for transforming instances, e.g. Cosmology <-> dict + +convert_registry = io_registry.UnifiedIORegistry() + + +class CosmologyFromFormat(io_registry.UnifiedReadWrite): + """Transform object to a `~astropy.cosmology.Cosmology`. + + This function provides the Cosmology interface to the Astropy unified I/O + layer. This allows easily parsing supported data formats using + syntax such as:: + + >>> from astropy.cosmology import Cosmology + >>> cosmo1 = Cosmology.from_format(cosmo_mapping, format='mapping') + + When the ``from_format`` method is called from a subclass the subclass will + provide a keyword argument ``cosmology=`` to the registered parser. + The method uses this cosmology class, regardless of the class indicated in + the data, and sets parameters' default values from the class' signature. + + Get help on the available readers using the ``help()`` method:: + + >>> Cosmology.from_format.help() # Get help and list supported formats + >>> Cosmology.from_format.help('') # Get detailed help on a format + >>> Cosmology.from_format.list_formats() # Print list of available formats + + See also: https://docs.astropy.org/en/stable/io/unified.html + + Parameters + ---------- + obj : object + The object to parse according to 'format' + *args + Positional arguments passed through to data parser. + format : str or None, optional keyword-only + Object format specifier. For `None` (default) CosmologyFromFormat tries + to identify the correct format. + **kwargs + Keyword arguments passed through to data parser. + Parsers should accept the following keyword arguments: + + - cosmology : the class (or string name thereof) to use / check when + constructing the cosmology instance. + + Returns + ------- + out : `~astropy.cosmology.Cosmology` subclass instance + `~astropy.cosmology.Cosmology` corresponding to ``obj`` contents. + """ + + def __init__( + self, + instance: "astropy.cosmology.Cosmology", + cosmo_cls: type["astropy.cosmology.Cosmology"], + ) -> None: + super().__init__(instance, cosmo_cls, "read", registry=convert_registry) + + # =============================================================== + # __call__ overloads + # note: format: ... | None means the format can be auto-detected from the input. + + @overload + def __call__( + self, + obj: _CosmoT, + *args: Any, + format: Literal["astropy.cosmology"] | None, + **kwargs: Any, + ) -> _CosmoT: ... + + @overload + def __call__( + self, + obj: "astropy.cosmology._src.io.builtin.model._CosmologyModel", + *args: Any, + format: Literal["astropy.model"] | None, + **kwargs: Any, + ) -> "astropy.cosmology.Cosmology": ... + + @overload + def __call__( + self, obj: Row, *args: Any, format: Literal["astropy.row"] | None, **kwargs: Any + ) -> "astropy.cosmology.Cosmology": ... + + @overload + def __call__( + self, + obj: Table, + *args: Any, + format: Literal["astropy.table"] | None, + **kwargs: Any, + ) -> "astropy.cosmology.Cosmology": ... + + @overload + def __call__( + self, + obj: Mapping[str, Any], + *args: Any, + format: Literal["mapping"] | None, + **kwargs: Any, + ) -> "astropy.cosmology.Cosmology": ... + + @overload + def __call__( + self, obj: str, *args: Any, format: Literal["yaml"], **kwargs: Any + ) -> "astropy.cosmology.Cosmology": ... + + @overload + def __call__( + self, obj: Any, *args: Any, format: str | None = None, **kwargs: Any + ) -> "astropy.cosmology.Cosmology": ... + + def __call__( + self, obj: Any, *args: Any, format: str | None = None, **kwargs: Any + ) -> "astropy.cosmology.Cosmology": + from astropy.cosmology._src.core import Cosmology + + # so subclasses can override, also pass the class as a kwarg. + # allows for `FlatLambdaCDM.read` and + # `Cosmology.read(..., cosmology=FlatLambdaCDM)` + if self._cls is not Cosmology: + kwargs.setdefault("cosmology", self._cls) # set, if not present + # check that it is the correct cosmology, can be wrong if user + # passes in e.g. `w0wzCDM.read(..., cosmology=FlatLambdaCDM)` + valid = (self._cls, self._cls.__qualname__) + if kwargs["cosmology"] not in valid: + raise ValueError( + "keyword argument `cosmology` must be either the class " + f"{valid[0]} or its qualified name '{valid[1]}'" + ) + + with add_enabled_units(cu): + return self.registry.read(self._cls, obj, *args, format=format, **kwargs) + + +class CosmologyToFormat(io_registry.UnifiedReadWrite): + """Transform this Cosmology to another format. + + This function provides the Cosmology interface to the astropy unified I/O + layer. This allows easily transforming to supported data formats + using syntax such as:: + + >>> from astropy.cosmology import Planck18 + >>> Planck18.to_format("mapping") + {'cosmology': astropy.cosmology.core.FlatLambdaCDM, + 'name': 'Planck18', + 'H0': , + 'Om0': 0.30966, + ... + + Get help on the available representations for ``Cosmology`` using the + ``help()`` method:: + + >>> Cosmology.to_format.help() # Get help and list supported formats + >>> Cosmology.to_format.help('') # Get detailed help on format + >>> Cosmology.to_format.list_formats() # Print list of available formats + + Parameters + ---------- + format : str + Format specifier. + *args + Positional arguments passed through to data writer. If supplied the + first argument is the output filename. + **kwargs + Keyword arguments passed through to data writer. + """ + + def __init__( + self, + instance: "astropy.cosmology.Cosmology", + cls: type["astropy.cosmology.Cosmology"], + ) -> None: + super().__init__(instance, cls, "write", registry=convert_registry) + + # =============================================================== + # __call__ overloads + + @overload + def __call__( + self, format: Literal["astropy.cosmology"], *args: Any, **kwargs: Any + ) -> "astropy.cosmology.Cosmology": ... + + @overload + def __call__( + self, format: Literal["astropy.model"], *args: Any, **kwargs: Any + ) -> "astropy.cosmology._src.io.builtin.model._CosmologyModel": ... + + @overload + def __call__( + self, format: Literal["astropy.row"], *args: Any, **kwargs: Any + ) -> Row: ... + + @overload + def __call__( + self, format: Literal["astropy.table"], *args: Any, **kwargs: Any + ) -> Table: ... + + @overload # specific mapping option, where the mapping class is specified. + def __call__( + self, format: Literal["mapping"], *args: Any, cls: _MT, **kwargs: Any + ) -> _MT: ... + + @overload + def __call__( + self, format: Literal["mapping"], *args: Any, **kwargs: Any + ) -> dict[str, Any]: ... + + @overload + def __call__(self, format: Literal["yaml"], *args: Any, **kwargs: Any) -> str: ... + + @overload + def __call__(self, format: str, *args: Any, **kwargs: Any) -> Any: ... + + def __call__(self, format: str, *args: Any, **kwargs: Any) -> Any: + return self.registry.write(self._instance, None, *args, format=format, **kwargs) diff --git a/astropy/cosmology/_src/parameter/__init__.py b/astropy/cosmology/_src/parameter/__init__.py new file mode 100644 index 000000000000..571c1893f997 --- /dev/null +++ b/astropy/cosmology/_src/parameter/__init__.py @@ -0,0 +1,24 @@ +"""Cosmological Parameters. Private API.""" +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +__all__ = ( # noqa: RUF100, RUF022 + "Parameter", + "ParametersAttribute", + "MISSING", + "all_parameters", + # converters + "validate_with_unit", + "validate_to_float", + "validate_to_scalar", + "validate_non_negative", +) + +from .converter import ( + validate_non_negative, + validate_to_float, + validate_to_scalar, + validate_with_unit, +) +from .core import MISSING, Parameter +from .descriptors import ParametersAttribute +from .utils import all_parameters diff --git a/astropy/cosmology/_src/parameter/converter.py b/astropy/cosmology/_src/parameter/converter.py new file mode 100644 index 000000000000..d2a35c8c03aa --- /dev/null +++ b/astropy/cosmology/_src/parameter/converter.py @@ -0,0 +1,122 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +__all__ = ( + "validate_non_negative", + "validate_to_float", + "validate_to_scalar", + "validate_with_unit", +) + +from collections.abc import Callable +from typing import TYPE_CHECKING, Any + +from numpy.typing import NDArray + +import astropy.units as u + +if TYPE_CHECKING: + import astropy.cosmology + +FValidateCallable = Callable[[object, object, Any], Any] +_REGISTRY_FVALIDATORS: dict[str, FValidateCallable] = {} + + +def _register_validator( + key: str, fvalidate: FValidateCallable | None = None +) -> FValidateCallable | Callable[[FValidateCallable], FValidateCallable]: + """Decorator to register a new kind of validator function. + + Parameters + ---------- + key : str + fvalidate : callable[[object, object, Any], Any] or None, optional + Value validation function. + + Returns + ------- + ``validator`` or callable[``validator``] + if validator is None returns a function that takes and registers a + validator. This allows ``register_validator`` to be used as a + decorator. + """ + if key in _REGISTRY_FVALIDATORS: + raise KeyError(f"validator {key!r} already registered with Parameter.") + + # fvalidate directly passed + if fvalidate is not None: + _REGISTRY_FVALIDATORS[key] = fvalidate + return fvalidate + + # for use as a decorator + def register(fvalidate): + """Register validator function. + + Parameters + ---------- + fvalidate : callable[[object, object, Any], Any] + Validation function. + + Returns + ------- + ``validator`` + """ + _REGISTRY_FVALIDATORS[key] = fvalidate + return fvalidate + + return register + + +# ====================================================================== + + +@_register_validator("default") +def validate_with_unit( + cosmology: "astropy.cosmology.Cosmology", + param: "astropy.cosmology.Parameter", + value: Any, +) -> Any: + """Default Parameter value validator. + + Adds/converts units if Parameter has a unit. + """ + if param.unit is not None: + with u.add_enabled_equivalencies(param.equivalencies): + value = u.Quantity(value, param.unit) + return value + + +@_register_validator("float") +def validate_to_float( + cosmology: "astropy.cosmology.Cosmology", + param: "astropy.cosmology.Parameter", + value: Any, +) -> float: + """Parameter value validator with units, and converted to float.""" + value = validate_with_unit(cosmology, param, value) + return float(value) + + +@_register_validator("scalar") +def validate_to_scalar( + cosmology: "astropy.cosmology.Cosmology", + param: "astropy.cosmology.Parameter", + value: Any, +) -> NDArray: + """Parameter value validator where value is a scalar.""" + value = validate_with_unit(cosmology, param, value) + if not value.isscalar: + raise ValueError(f"{param.name} is a non-scalar quantity") + return value + + +@_register_validator("non-negative") +def validate_non_negative( + cosmology: "astropy.cosmology.Cosmology", + param: "astropy.cosmology.Parameter", + value: Any, +) -> float: + """Parameter value validator where value is a positive float.""" + value = validate_to_float(cosmology, param, value) + if value < 0.0: + raise ValueError(f"{param.name} cannot be negative.") + return value diff --git a/astropy/cosmology/_src/parameter/core.py b/astropy/cosmology/_src/parameter/core.py new file mode 100644 index 000000000000..7c0d3fa72741 --- /dev/null +++ b/astropy/cosmology/_src/parameter/core.py @@ -0,0 +1,304 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +__all__ = ("MISSING", "Parameter") + +import copy +from collections.abc import Sequence +from dataclasses import KW_ONLY, dataclass, field, fields, is_dataclass, replace +from enum import Enum, auto +from typing import TYPE_CHECKING, Any, Union + +import astropy.units as u + +from .converter import _REGISTRY_FVALIDATORS, FValidateCallable, _register_validator + +if TYPE_CHECKING: + import astropy.cosmology + + +class Sentinel(Enum): + """Sentinel values for Parameter fields.""" + + MISSING = auto() + """A sentinel value signifying a missing default.""" + + def __repr__(self) -> str: + return f"<{self.name}>" + + +MISSING = Sentinel.MISSING + + +@dataclass(frozen=True) +class _UnitField: + # TODO: rm this class when py3.13+ allows for `field(converter=...)` + + def __get__( + self, obj: Union["Parameter", None], objcls: type["Parameter"] | None + ) -> u.Unit | None: + if obj is None: # calling `Parameter.unit` from the class + return None + return getattr(obj, "_unit", None) + + def __set__(self, obj: "Parameter", value: Any) -> None: + object.__setattr__(obj, "_unit", u.Unit(value) if value is not None else None) + + +@dataclass(frozen=True) +class _FValidateField: + default: FValidateCallable | str = "default" + + def __get__( + self, obj: Union["Parameter", None], objcls: type["Parameter"] | None + ) -> FValidateCallable | str: + if obj is None: # calling `Parameter.fvalidate` from the class + return self.default + return obj._fvalidate # calling `Parameter.fvalidate` from an instance + + def __set__(self, obj: "Parameter", value: Any) -> None: + # Always store input fvalidate. + object.__setattr__(obj, "_fvalidate_in", value) + + # Process to the callable. + if value in _REGISTRY_FVALIDATORS: + value = _REGISTRY_FVALIDATORS[value] + elif isinstance(value, str): + msg = f"`fvalidate`, if str, must be in {_REGISTRY_FVALIDATORS.keys()}" + raise ValueError(msg) + elif not callable(value): + msg = f"`fvalidate` must be a function or {_REGISTRY_FVALIDATORS.keys()}" + raise TypeError(msg) + object.__setattr__(obj, "_fvalidate", value) + + +@dataclass(frozen=True) +class Parameter: + r"""Cosmological parameter (descriptor). + + Should only be used with a :class:`~astropy.cosmology.Cosmology` subclass. + + Parameters + ---------- + default : Any (optional, keyword-only) + Default value of the Parameter. If not given the + Parameter must be set when initializing the cosmology. + derived : bool (optional, keyword-only) + Whether the Parameter is 'derived', default `False`. + Derived parameters behave similarly to normal parameters, but are not + sorted by the |Cosmology| signature (probably not there) and are not + included in all methods. For reference, see ``Ode0`` in + ``FlatFLRWMixin``, which removes :math:`\Omega_{de,0}`` as an + independent parameter (:math:`\Omega_{de,0} \equiv 1 - \Omega_{tot}`). + unit : unit-like or None (optional, keyword-only) + The `~astropy.units.Unit` for the Parameter. If None (default) no + unit as assumed. + equivalencies : `~astropy.units.Equivalency` or sequence thereof + Unit equivalencies for this Parameter. + fvalidate : callable[[object, object, Any], Any] or str (optional, keyword-only) + Function to validate the Parameter value from instances of the + cosmology class. If "default", uses default validator to assign units + (with equivalencies), if Parameter has units. + For other valid string options, see ``Parameter._registry_validators``. + 'fvalidate' can also be set through a decorator with + :meth:`~astropy.cosmology.Parameter.validator`. + doc : str or None (optional, keyword-only) + Parameter description. + + Examples + -------- + For worked examples see :class:`~astropy.cosmology.FLRW`. + """ + + _: KW_ONLY + + default: Any = MISSING + """Default value of the Parameter. + + By default set to ``MISSING``, which indicates the parameter must be set + when initializing the cosmology. + """ + + derived: bool = False + """Whether the Parameter can be set, or is derived, on the cosmology.""" + + # Units + unit: _UnitField = _UnitField() + """The unit of the Parameter (can be `None` for unitless).""" + + equivalencies: u.Equivalency | Sequence[u.Equivalency] = field(default_factory=list) + """Unit equivalencies available when setting the parameter.""" + + # Setting + fvalidate: _FValidateField = _FValidateField(default="default") + """Function to validate/convert values when setting the Parameter.""" + + # Info + doc: str | None = None + """Parameter description.""" + + name: str = field(init=False, compare=True, default=None, repr=False) + """The name of the Parameter on the Cosmology. + + Cannot be set directly. + """ + + def __post_init__(self) -> None: + self._fvalidate_in: FValidateCallable | str + self._fvalidate: FValidateCallable + object.__setattr__(self, "__doc__", self.doc) + # Now setting a dummy attribute name. The cosmology class will call + # `__set_name__`, passing the real attribute name. However, if Parameter is not + # init'ed as a descriptor then this ensures that all declared fields exist. + self.__set_name__(None, "name not initialized") + + def __set_name__(self, cosmo_cls: type, name: str) -> None: + # attribute name on container cosmology class + object.__setattr__(self, "name", name) + + # ------------------------------------------- + # descriptor and property-like methods + + def __get__( + self, + cosmology: Union["astropy.cosmology.Cosmology", None], + cosmo_cls: Union["type[astropy.cosmology.Cosmology]", None] = None, + ) -> Any: + # Get from class + if cosmology is None: + # If the Parameter is being set as part of a dataclass constructor, then we + # raise an AttributeError if the default is MISSING. This is to prevent the + # Parameter from being set as the default value of the dataclass field and + # erroneously included in the class' __init__ signature. + if self.default is MISSING and ( + not is_dataclass(cosmo_cls) + or self.name not in cosmo_cls.__dataclass_fields__ + ): + raise AttributeError + return self + # Get from instance + return cosmology.__dict__[self.name] + + def __set__(self, cosmology: "astropy.cosmology.Cosmology", value: Any) -> None: + """Allows attribute setting once. + + Raises AttributeError subsequently. + """ + # Raise error if setting 2nd time. The built-in Cosmology objects are frozen + # dataclasses and this is redundant, however user defined cosmology classes do + # not have to be frozen. + if self.name in cosmology.__dict__: + raise AttributeError(f"cannot assign to field {self.name!r}") + + # Change `self` to the default value if default is MISSING. + # This is done for backwards compatibility only - so that Parameter can be used + # in a dataclass and still return `self` when accessed from a class. + # Accessing the Parameter object via `cosmo_cls.param_name` will be removed + # in favor of `cosmo_cls.parameters["param_name"]`. + if value is self: + value = self.default + + # Validate value, generally setting units if present + value = self.validate(cosmology, copy.deepcopy(value)) + + # Make the value read-only, if ndarray-like + if hasattr(value, "setflags"): + value.setflags(write=False) + + # Set the value on the cosmology + cosmology.__dict__[self.name] = value + + # ------------------------------------------- + # validate value + + def validator(self, fvalidate: FValidateCallable) -> "Parameter": + """Make new Parameter with custom ``fvalidate``. + + Note: ``Parameter.fvalidator`` must be the top-most descriptor decorator. + + Parameters + ---------- + fvalidate : callable[[type, type, Any], Any] + + Returns + ------- + `~astropy.cosmology.Parameter` + Copy of this Parameter but with custom ``fvalidate``. + """ + return self.clone(fvalidate=fvalidate) + + def validate(self, cosmology: "astropy.cosmology.Cosmology", value: Any) -> Any: + """Run the validator on this Parameter. + + Parameters + ---------- + cosmology : `~astropy.cosmology.Cosmology` instance + value : Any + The object to validate. + + Returns + ------- + Any + The output of calling ``fvalidate(cosmology, self, value)`` + (yes, that parameter order). + """ + return self._fvalidate(cosmology, self, value) + + @staticmethod + def register_validator(key, fvalidate: FValidateCallable | None = None) -> Any: + """Decorator to register a new kind of validator function. + + Parameters + ---------- + key : str + fvalidate : callable[[object, object, Any], Any] or None, optional + Value validation function. + + Returns + ------- + ``validator`` or callable[``validator``] + if validator is None returns a function that takes and registers a + validator. This allows ``register_validator`` to be used as a + decorator. + """ + return _register_validator(key, fvalidate=fvalidate) + + # ------------------------------------------- + + def clone(self, **kw: Any) -> "Parameter": + """Clone this `Parameter`, changing any constructor argument. + + Parameters + ---------- + **kw + Passed to constructor. The current values, eg. ``fvalidate`` are + used as the default values, so an empty ``**kw`` is an exact copy. + + Examples + -------- + >>> p = Parameter() + >>> p + Parameter(derived=False, unit=None, equivalencies=[], + fvalidate='default', doc=None) + + >>> p.clone(unit="km") + Parameter(derived=False, unit=Unit("km"), equivalencies=[], + fvalidate='default', doc=None) + """ + kw.setdefault("fvalidate", self._fvalidate_in) # prefer the input fvalidate + cloned = replace(self, **kw) + # Transfer over the __set_name__ stuff. If `clone` is used to make a + # new descriptor, __set_name__ will be called again, overwriting this. + cloned.__set_name__(None, self.name) + + return cloned + + def __repr__(self) -> str: + """Return repr(self).""" + fields_repr = ( + # Get the repr, using the input fvalidate over the processed value + f"{f.name}={(getattr(self, f.name if f.name != 'fvalidate' else '_fvalidate_in'))!r}" + for f in fields(self) + # Only show fields that should be displayed and are not sentinel values + if f.repr and (f.name != "default" or self.default is not MISSING) + ) + return f"{self.__class__.__name__}({', '.join(fields_repr)})" diff --git a/astropy/cosmology/_src/parameter/descriptors.py b/astropy/cosmology/_src/parameter/descriptors.py new file mode 100644 index 000000000000..b7d570424475 --- /dev/null +++ b/astropy/cosmology/_src/parameter/descriptors.py @@ -0,0 +1,69 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +__all__: list[str] = ["ParametersAttribute"] + +from dataclasses import dataclass, field +from types import MappingProxyType +from typing import TYPE_CHECKING, Any, NoReturn, Union + +if TYPE_CHECKING: + import astropy.cosmology + + +@dataclass(frozen=True, slots=True) +class ParametersAttribute: + """Immutable mapping of the :class:`~astropy.cosmology.Parameter` objects or values. + + If accessed from the :class:`~astropy.cosmology.Cosmology` class, this returns a + mapping of the :class:`~astropy.cosmology.Parameter` objects themselves. If + accessed from an instance, this returns a mapping of the values of the Parameters. + + This class is used to implement :obj:`astropy.cosmology.Cosmology.parameters`. + + Parameters + ---------- + attr_name : str + The name of the class attribute that is a `~types.MappingProxyType[str, + astropy.cosmology.Parameter]` of all the cosmology's parameters. When accessed + from the class, this attribute is returned. When accessed from an instance, a + mapping of the cosmology instance's values for each key is returned. + + Examples + -------- + The normal usage of this class is the ``parameters`` attribute of + :class:`~astropy.cosmology.Cosmology`. + + >>> from astropy.cosmology import FlatLambdaCDM, Planck18 + + >>> FlatLambdaCDM.parameters + mappingproxy({'H0': Parameter(...), ...}) + + >>> Planck18.parameters + mappingproxy({'H0': , ...}) + """ + + attr_name: str + """Class attribute name on Cosmology for the mapping of Parameter objects.""" + + _name: str = field(init=False) + """The name of the descriptor on the containing class.""" + + def __set_name__(self, owner: Any, name: str) -> None: + object.__setattr__(self, "_name", name) + + def __get__( + self, + instance: Union["astropy.cosmology.Cosmology", None], + owner: type["astropy.cosmology.Cosmology"] | None, + ) -> MappingProxyType[str, Any]: + # Called from the class + if instance is None: + return getattr(owner, self.attr_name) + # Called from the instance + return MappingProxyType( + {n: getattr(instance, n) for n in getattr(instance, self.attr_name)} + ) + + def __set__(self, instance: Any, value: Any) -> NoReturn: + msg = f"cannot set {self._name!r} of {instance!r}." + raise AttributeError(msg) diff --git a/astropy/cosmology/_src/parameter/utils.py b/astropy/cosmology/_src/parameter/utils.py new file mode 100644 index 000000000000..5187bbf6e34f --- /dev/null +++ b/astropy/cosmology/_src/parameter/utils.py @@ -0,0 +1,33 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +__all__: tuple[str, ...] = () # nothing is publicly scoped + +import functools +import operator +from dataclasses import Field +from typing import TypeGuard + +from .core import Parameter + + +def is_parameter_or_field(obj: object, /) -> TypeGuard[Parameter | Field[Parameter]]: + """Return if object is a Parameter or dataclass field thereof.""" + return isinstance(obj, Parameter) or ( + isinstance(obj, Field) and isinstance(obj.default, Parameter) + ) + + +def all_parameters(obj: object, /) -> dict[str, Parameter]: + """Get all `Parameter` fields of an object. + + Returns all fields of the object, including those not yet finalized in the class, if + it's still under construction, e.g. in ``__init_subclass__``. + """ + cls = obj if isinstance(obj, type) else obj.__class__ + all_cls_vars = functools.reduce(operator.__or__, map(vars, cls.mro()[::-1])) + + return { + k: (v if isinstance(v, Parameter) else v.default) + for k, v in all_cls_vars.items() + if is_parameter_or_field(v) + } diff --git a/astropy/cosmology/_src/scipy_compat.py b/astropy/cosmology/_src/scipy_compat.py new file mode 100644 index 000000000000..414c2d819574 --- /dev/null +++ b/astropy/cosmology/_src/scipy_compat.py @@ -0,0 +1,22 @@ +"""Scipy compatibility.""" + +__all__ = ("ellipkinc", "hyp2f1", "quad") + +from typing import Any, Never + +from astropy.utils.compat.optional_deps import HAS_SCIPY + +if HAS_SCIPY: + from scipy.integrate import quad + from scipy.special import ellipkinc, hyp2f1 + +else: + + def quad(*args: Any, **kwargs: Any) -> Never: + raise ModuleNotFoundError("No module named 'scipy.integrate'") + + def ellipkinc(*args: Any, **kwargs: Any) -> Never: + raise ModuleNotFoundError("No module named 'scipy.special'") + + def hyp2f1(*args: Any, **kwargs: Any) -> Never: + raise ModuleNotFoundError("No module named 'scipy.special'") diff --git a/astropy/cosmology/_src/setup_package.py b/astropy/cosmology/_src/setup_package.py new file mode 100644 index 000000000000..7d3fbce6feb5 --- /dev/null +++ b/astropy/cosmology/_src/setup_package.py @@ -0,0 +1,29 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +import sys +from os.path import relpath +from pathlib import Path + +from setuptools import Extension + +ASTROPY_COSMOLOGY_SRC_ROOT = Path(__file__).parent + + +if sys.platform.startswith("win"): + # on windows, -Werror (and possibly -Wall too) isn't recognized + extra_compile_args = [] +else: + # be extra careful with this extension as it calls PyErr_WarnEx + # with a formatted message whose size cannot be determined at compile time, + # which is never done within the standard library + extra_compile_args = ["-Werror", "-Wall"] + + +def get_extensions(): + return [ + Extension( + "astropy.cosmology._src.signature_deprecations", + [relpath(Path(ASTROPY_COSMOLOGY_SRC_ROOT, "signature_deprecations.c"))], + extra_compile_args=extra_compile_args, + ), + ] diff --git a/astropy/cosmology/_src/signature_deprecations.c b/astropy/cosmology/_src/signature_deprecations.c new file mode 100644 index 000000000000..7668a69fc886 --- /dev/null +++ b/astropy/cosmology/_src/signature_deprecations.c @@ -0,0 +1,292 @@ +// This extension is adapted from the positional_defaults PyPI package +// https://pypi.org/project/positional-defaults/ version 2023.4.19 +// MIT. see licenses/POSITIONAL_DEFAULTS.rst + + +#include +#include // snprintf +#include + +#define SINCE_CHAR_SIZE 32 +#define NAMES_CHAR_SIZE 128 +#define MSG_SIZE 512 + +typedef struct { + PyObject_HEAD + PyObject *dict; + PyObject *wrapped; + PyObject *names; + PyObject *since; +} DeprKwsObject; + + +static void depr_kws_wrap_dealloc(DeprKwsObject *self) +{ + Py_XDECREF(self->wrapped); + Py_XDECREF(self->names); + Py_XDECREF(self->since); + PyTypeObject *type = Py_TYPE((PyObject *)self); + freefunc free_func = PyType_GetSlot(type, Py_tp_free); + free_func((PyObject *)self); + Py_DECREF((PyObject *)type); +} + + +static PyObject *depr_kws_wrap_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + allocfunc alloc_func = PyType_GetSlot(type, Py_tp_alloc); + DeprKwsObject *self = (DeprKwsObject *)alloc_func(type, 0); + + if (self != NULL) { + self->names = PyTuple_New(0); + if (self->names == NULL) { + Py_DECREF(self); + return NULL; + } + + Py_INCREF(Py_None); + self->wrapped = Py_None; + + Py_INCREF(Py_None); + self->since = Py_None; + } + + return (PyObject *)self; +} + + +static int depr_kws_wrap_init(DeprKwsObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"wrapped", "names", "since", NULL}; + Py_ssize_t i, n_names; + PyObject *wrapped, *names, *since, *tmp; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "OOO:wrap", kwlist, &wrapped, &names, &since)) { + return -1; + } + + if (!PyTuple_Check(names)) { + PyErr_SetString(PyExc_TypeError, "names must be a tuple"); + return -1; + } + + n_names = PyTuple_Size(names); + + for (i = 0; i < n_names; ++i) { + PyObject *name = PyTuple_GetItem(names, i); + if (!PyUnicode_Check(name)) { + PyErr_Format(PyExc_TypeError, "names[%zd] must be a string", i); + return -1; + } + } + + if (!PyUnicode_Check(since)) { + PyErr_Format(PyExc_TypeError, "since must be a string", i); + return -1; + } + + tmp = self->wrapped; + Py_INCREF(wrapped); + self->wrapped = wrapped; + Py_XDECREF(tmp); + + tmp = self->names; + Py_INCREF(names); + self->names = names; + Py_XDECREF(tmp); + + tmp = self->since; + Py_INCREF(since); + self->since = since; + Py_XDECREF(tmp); + return 0; +} + + +static PyMemberDef depr_kws_wrap_members[] = { + {"__dict__", T_OBJECT, offsetof(DeprKwsObject, dict), READONLY}, + {"__dictoffset__", T_PYSSIZET, offsetof(DeprKwsObject, dict), READONLY}, + {"wrapped", T_OBJECT, offsetof(DeprKwsObject, wrapped), READONLY}, + {"names", T_OBJECT, offsetof(DeprKwsObject, names), READONLY}, + {"since", T_OBJECT, offsetof(DeprKwsObject, since), READONLY}, + {NULL}, +}; + + +static PyObject *depr_kws_wrap_call(DeprKwsObject *self, PyObject *args, PyObject *kwds) +{ + // step 0: return early whenever possible + if (self->wrapped == NULL) { + Py_RETURN_NONE; + } + + if (kwds == NULL) { + return PyObject_Call(self->wrapped, args, kwds); + } + + // step 1: detect any deprecated keyword arguments, return if none. + Py_ssize_t n_names = PyTuple_Size(self->names); + PyObject *deprecated_kwargs = PyList_New(n_names); + Py_INCREF(deprecated_kwargs); + PyObject *name = NULL; + Py_ssize_t i = 0; + int has_kw = -2; + + Py_ssize_t n_depr = 0; + for (i = 0; i < n_names; ++i) { + name = PyTuple_GetItem(self->names, i); + has_kw = PyDict_Contains(kwds, name); + if (has_kw) { + PyList_SetItem(deprecated_kwargs, n_depr, name); + ++n_depr; + } + } + + if (n_depr == 0) { + return PyObject_Call(self->wrapped, args, kwds); + } + + // step 2: create and emit warning message + char names_char[NAMES_CHAR_SIZE]; + char *s, *arguments, *respectively, *pronoun; + + PyObject *names_unicode; + if (n_depr > 1) { + names_unicode = PyObject_Str(PyList_GetSlice(deprecated_kwargs, 0, n_depr)); + s = "s"; + arguments = " arguments"; + respectively = ", respectively"; + pronoun = "them"; + } + else { + names_unicode = PyObject_Repr(PyList_GetItem(deprecated_kwargs, 0)); + s = arguments = respectively = ""; + pronoun = "it"; + } + const char *names_utf8 = PyUnicode_AsUTF8AndSize(names_unicode, NULL); + snprintf(names_char, NAMES_CHAR_SIZE, "%s", names_utf8); + + PyObject *since_unicode = PyObject_Str(self->since); + const char *since_utf8 = PyUnicode_AsUTF8AndSize(since_unicode, NULL); + char since_char[SINCE_CHAR_SIZE]; + snprintf(since_char, SINCE_CHAR_SIZE, "%s", since_utf8); + + char msg[MSG_SIZE]; + snprintf( + msg, + MSG_SIZE, + "Passing %s%s as keyword%s " + "is deprecated since version %s " + "and will stop working in a future release. " + "Pass %s positionally to suppress this warning.", + names_char, + arguments, + s, + since_char, + pronoun + ); + const char *msg_ptr = msg; + + int status = PyErr_WarnEx(PyExc_FutureWarning, msg_ptr, 2); + if (status == -1) { + // avoid leaking memory if Warning is promoted to Exception + Py_DECREF(deprecated_kwargs); + } + + return PyObject_Call(self->wrapped, args, kwds); +} + +// replace PyMethod_New (not part of the limited API) +// this is adapted from Cython's ObjectHandling.c +// https://github.com/cython/cython/blob/dba606bc50e90dbaf37850779d1f84f1c0a22c7a/Cython/Utility/ObjectHandling.c#L2977 +#ifdef Py_LIMITED_API +static PyObject *CachedMethodType = NULL; + + +static PyObject *PyMethod_New(PyObject *func, PyObject *self) +{ + PyObject *result; +#if Py_LIMITED_API >= 0x030C0000 + { + PyObject *args[] = {func, self}; + result = PyObject_Vectorcall(CachedMethodType, args, 2, NULL); + } +#else + result = PyObject_CallFunctionObjArgs(CachedMethodType, func, self, NULL); +#endif + return result; +} +#endif + +static PyObject *depr_kws_wrap_get(PyObject *self, PyObject *obj, PyObject *type) +{ + if (obj == Py_None || obj == NULL) { + Py_INCREF(self); + return self; + } + return PyMethod_New(self, obj); +} + + +static PyType_Spec DeprKwsWrapType_spec = { + .name = "signature_deprecations.wrap", + .basicsize = sizeof(DeprKwsObject), + .itemsize = 0, + .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_IMMUTABLETYPE | + Py_TPFLAGS_METHOD_DESCRIPTOR, + .slots = (PyType_Slot[]){ + {Py_tp_doc, PyDoc_STR("wrap a function with deprecated keyword arguments")}, + {Py_tp_new, depr_kws_wrap_new}, + {Py_tp_init, (initproc)depr_kws_wrap_init}, + {Py_tp_dealloc, (destructor)depr_kws_wrap_dealloc}, + {Py_tp_members, depr_kws_wrap_members}, + {Py_tp_call, (ternaryfunc)depr_kws_wrap_call}, + {Py_tp_descr_get, depr_kws_wrap_get}, + {0, NULL}, + }, +}; + +static PyObject *DeprKwsWrapType = NULL; + +static struct PyModuleDef moduledef = { + PyModuleDef_HEAD_INIT, + .m_name = "signature_deprecations", + .m_doc = PyDoc_STR("fast decorators to mark signature details as deprecated"), + .m_size = -1, +}; + + +PyMODINIT_FUNC PyInit_signature_deprecations(void) +{ + PyObject *m; + + m = PyModule_Create(&moduledef); + if (m == NULL) { + return NULL; + } +#if Py_LIMITED_API + { + PyObject *typesModule = PyImport_ImportModule("types"); + if (!typesModule) { + return NULL; + } + CachedMethodType = PyObject_GetAttrString(typesModule, "MethodType"); + Py_DECREF(typesModule); + if (!CachedMethodType) { + return NULL; + } + } +#endif + DeprKwsWrapType = PyType_FromModuleAndSpec(m, &DeprKwsWrapType_spec, NULL); + if (DeprKwsWrapType == NULL) { + return NULL; + } + + if (PyModule_AddObject(m, "_depr_kws_wrap", DeprKwsWrapType) < 0) { + Py_DECREF(DeprKwsWrapType); + Py_DECREF(m); + return NULL; + } + + return m; +} diff --git a/astropy/sphinx/ext/tests/__init__.py b/astropy/cosmology/_src/tests/__init__.py similarity index 100% rename from astropy/sphinx/ext/tests/__init__.py rename to astropy/cosmology/_src/tests/__init__.py diff --git a/astropy/cosmology/_src/tests/conftest.py b/astropy/cosmology/_src/tests/conftest.py new file mode 100644 index 000000000000..cd1f3649bd63 --- /dev/null +++ b/astropy/cosmology/_src/tests/conftest.py @@ -0,0 +1,6 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +"""Configure the tests for :mod:`astropy.cosmology`.""" + +from astropy.cosmology._src.tests.helper import clean_registry # noqa: F401 +from astropy.tests.helper import pickle_protocol # noqa: F401 diff --git a/astropy/tools/tests/__init__.py b/astropy/cosmology/_src/tests/flrw/__init__.py similarity index 100% rename from astropy/tools/tests/__init__.py rename to astropy/cosmology/_src/tests/flrw/__init__.py diff --git a/astropy/cosmology/_src/tests/flrw/conftest.py b/astropy/cosmology/_src/tests/flrw/conftest.py new file mode 100644 index 000000000000..20adfe0015aa --- /dev/null +++ b/astropy/cosmology/_src/tests/flrw/conftest.py @@ -0,0 +1,32 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +"""Configure the tests for :mod:`astropy.cosmology`.""" + +from collections.abc import Iterable, Mapping, Sequence +from typing import TypeVar + +from astropy.cosmology._src.tests.helper import clean_registry # noqa: F401 +from astropy.tests.helper import pickle_protocol # noqa: F401 + +K = TypeVar("K") +V = TypeVar("V") + + +def filter_keys_from_items( + m: Mapping[K, V], /, filter_out: Sequence[K] +) -> Iterable[K, V]: + """Filter ``m``, returning key-value pairs not including keys in ``filter``. + + Parameters + ---------- + m : mapping[K, V] + A mapping from which to remove keys in ``filter_out``. + filter_out : sequence[K] + Sequence of keys to filter out from ``m``. + + Returns + ------- + iterable[K, V] + Iterable of ``(key, value)`` pairs with the ``filter_out`` keys removed. + """ + return ((k, v) for k, v in m.items() if k not in filter_out) diff --git a/astropy/cosmology/_src/tests/flrw/data/cosmo_closed.ecsv b/astropy/cosmology/_src/tests/flrw/data/cosmo_closed.ecsv new file mode 100644 index 000000000000..adaec308ff07 --- /dev/null +++ b/astropy/cosmology/_src/tests/flrw/data/cosmo_closed.ecsv @@ -0,0 +1,61 @@ +# %ECSV 1.0 +# --- +# datatype: +# - {name: redshift, unit: redshift, datatype: float64} +# - {name: dm, unit: Mpc, datatype: float64} +# - {name: da, unit: Mpc, datatype: float64} +# - {name: dl, unit: Mpc, datatype: float64} +# meta: !!omap +# - {source: icosmo (icosmo.org)} +# - {Om: 2} +# - {w: -1} +# - {h: 0.7} +# - {Ol: 0.1} +# - __serialized_columns__: +# da: +# __class__: astropy.units.quantity.Quantity +# unit: &id001 !astropy.units.Unit {unit: Mpc} +# value: !astropy.table.SerializedColumn {name: da} +# dl: +# __class__: astropy.units.quantity.Quantity +# unit: *id001 +# value: !astropy.table.SerializedColumn {name: dl} +# dm: +# __class__: astropy.units.quantity.Quantity +# unit: *id001 +# value: !astropy.table.SerializedColumn {name: dm} +# redshift: +# __class__: astropy.units.quantity.Quantity +# unit: !astropy.units.Unit {unit: redshift} +# value: !astropy.table.SerializedColumn {name: redshift} +# schema: astropy-2.0 +redshift dm da dl +0.0 0.0 0.0 0.0 +0.1625 601.8016 517.67879 699.59436 +0.325 1057.9502 798.45297 1401.784 +0.5 1438.2161 958.81076 2157.3242 +0.6625 1718.6778 1033.7912 2857.3019 +0.825 1948.24 1067.5288 3555.5381 +1.0 2152.7954 1076.3977 4305.5908 +1.1625 2312.3427 1069.2914 5000.441 +1.325 2448.9755 1053.3228 5693.8681 +1.5 2575.6795 1030.2718 6439.1988 +1.6625 2677.9671 1005.8092 7130.0873 +1.825 2768.1157 979.86398 7819.927 +2.0 2853.9222 951.30739 8561.7665 +2.1625 2924.8116 924.84161 9249.7167 +2.325 2988.5333 898.80701 9936.8732 +2.5 3050.3065 871.51614 10676.073 +2.6625 3102.1909 847.01459 11361.774 +2.825 3149.5043 823.39982 12046.854 +3.0 3195.9966 798.99915 12783.986 +3.1625 3235.5334 777.30533 13467.908 +3.325 3271.9832 756.5279 14151.327 +3.5 3308.1758 735.15017 14886.791 +3.6625 3339.2521 716.19347 15569.263 +3.825 3368.1489 698.06195 16251.319 +4.0 3397.0803 679.41605 16985.401 +4.1625 3422.1142 662.87926 17666.664 +4.325 3445.5542 647.05243 18347.576 +4.5 3469.1805 630.76008 19080.493 +4.6625 3489.7534 616.29199 19760.729 diff --git a/astropy/cosmology/_src/tests/flrw/data/cosmo_flat.ecsv b/astropy/cosmology/_src/tests/flrw/data/cosmo_flat.ecsv new file mode 100644 index 000000000000..ba6dd55e911c --- /dev/null +++ b/astropy/cosmology/_src/tests/flrw/data/cosmo_flat.ecsv @@ -0,0 +1,61 @@ +# %ECSV 1.0 +# --- +# datatype: +# - {name: redshift, unit: redshift, datatype: float64} +# - {name: dm, unit: Mpc, datatype: float64} +# - {name: da, unit: Mpc, datatype: float64} +# - {name: dl, unit: Mpc, datatype: float64} +# meta: !!omap +# - {source: icosmo (icosmo.org)} +# - {Om: 0.3} +# - {w: -1} +# - {h: 0.7} +# - {Ol: 0.7} +# - __serialized_columns__: +# da: +# __class__: astropy.units.quantity.Quantity +# unit: &id001 !astropy.units.Unit {unit: Mpc} +# value: !astropy.table.SerializedColumn {name: da} +# dl: +# __class__: astropy.units.quantity.Quantity +# unit: *id001 +# value: !astropy.table.SerializedColumn {name: dl} +# dm: +# __class__: astropy.units.quantity.Quantity +# unit: *id001 +# value: !astropy.table.SerializedColumn {name: dm} +# redshift: +# __class__: astropy.units.quantity.Quantity +# unit: !astropy.units.Unit {unit: redshift} +# value: !astropy.table.SerializedColumn {name: redshift} +# schema: astropy-2.0 +redshift dm da dl +0.0 0.0 0.0 0.0 +0.1625 669.77536 576.15085 778.61386 +0.325 1285.5964 970.26143 1703.4152 +0.5 1888.6254 1259.0836 2832.9381 +0.6625 2395.5489 1440.9317 3982.6 +0.825 2855.5732 1564.6976 5211.421 +1.0 3303.8288 1651.9144 6607.6577 +1.1625 3681.1867 1702.2829 7960.5663 +1.325 4025.5229 1731.4077 9359.3408 +1.5 4363.8558 1745.5423 10909.64 +1.6625 4651.483 1747.0359 12384.573 +1.825 4916.597 1740.3883 13889.387 +2.0 5179.8621 1726.6207 15539.586 +2.1625 5406.0204 1709.4136 17096.54 +2.325 5616.5075 1689.1752 18674.888 +2.5 5827.5418 1665.012 20396.396 +2.6625 6010.4886 1641.089 22013.414 +2.825 6182.1688 1616.2533 23646.796 +3.0 6355.6855 1588.9214 25422.742 +3.1625 6507.2491 1563.3031 27086.425 +3.325 6650.452 1537.6768 28763.205 +3.5 6796.1499 1510.2555 30582.674 +3.6625 6924.2096 1485.0852 32284.127 +3.825 7045.8876 1460.2876 33996.408 +4.0 7170.3664 1434.0733 35851.832 +4.1625 7280.3423 1410.2358 37584.767 +4.325 7385.3277 1386.916 39326.87 +4.5 7493.2222 1362.404 41212.722 +4.6625 7588.9589 1340.2135 42972.48 diff --git a/astropy/cosmology/_src/tests/flrw/data/cosmo_open.ecsv b/astropy/cosmology/_src/tests/flrw/data/cosmo_open.ecsv new file mode 100644 index 000000000000..d39dd7a8d587 --- /dev/null +++ b/astropy/cosmology/_src/tests/flrw/data/cosmo_open.ecsv @@ -0,0 +1,61 @@ +# %ECSV 1.0 +# --- +# datatype: +# - {name: redshift, unit: redshift, datatype: float64} +# - {name: dm, unit: Mpc, datatype: float64} +# - {name: da, unit: Mpc, datatype: float64} +# - {name: dl, unit: Mpc, datatype: float64} +# meta: !!omap +# - {source: icosmo (icosmo.org)} +# - {Om: 0.3} +# - {w: -1} +# - {h: 0.7} +# - {Ol: 0.1} +# - __serialized_columns__: +# da: +# __class__: astropy.units.quantity.Quantity +# unit: &id001 !astropy.units.Unit {unit: Mpc} +# value: !astropy.table.SerializedColumn {name: da} +# dl: +# __class__: astropy.units.quantity.Quantity +# unit: *id001 +# value: !astropy.table.SerializedColumn {name: dl} +# dm: +# __class__: astropy.units.quantity.Quantity +# unit: *id001 +# value: !astropy.table.SerializedColumn {name: dm} +# redshift: +# __class__: astropy.units.quantity.Quantity +# unit: !astropy.units.Unit {unit: redshift} +# value: !astropy.table.SerializedColumn {name: redshift} +# schema: astropy-2.0 +redshift dm da dl +0.0 0.0 0.0 0.0 +0.1625 643.08185 553.18868 747.58265 +0.325 1200.9858 906.40441 1591.3062 +0.5 1731.6262 1154.4175 2597.4393 +0.6625 2174.3252 1307.8648 3614.8157 +0.825 2578.7616 1413.0201 4706.2399 +1.0 2979.346 1489.673 5958.692 +1.1625 3324.2002 1537.2024 7188.5829 +1.325 3646.8432 1568.5347 8478.9104 +1.5 3972.8407 1589.1363 9932.1017 +1.6625 4258.1131 1599.2913 11337.226 +1.825 4528.5346 1603.0211 12793.11 +2.0 4804.9314 1601.6438 14414.794 +2.1625 5049.2007 1596.5852 15968.097 +2.325 5282.6693 1588.7727 17564.875 +2.5 5523.0914 1578.0261 19330.82 +2.6625 5736.9813 1566.4113 21011.694 +2.825 5942.5803 1553.6158 22730.37 +3.0 6155.4289 1538.8572 24621.716 +3.1625 6345.6997 1524.4924 26413.975 +3.325 6529.3655 1509.6799 28239.506 +3.5 6720.2676 1493.3928 30241.204 +3.6625 6891.5474 1478.0799 32131.84 +3.825 7057.4213 1462.678 34052.058 +4.0 7230.3723 1446.0745 36151.862 +4.1625 7385.9998 1430.7021 38130.224 +4.325 7537.1112 1415.4199 40135.117 +4.5 7695.0718 1399.104 42322.895 +4.6625 7837.551 1384.115 44380.133 diff --git a/astropy/cosmology/_src/tests/flrw/test_base.py b/astropy/cosmology/_src/tests/flrw/test_base.py new file mode 100644 index 000000000000..48cc07ebbe4c --- /dev/null +++ b/astropy/cosmology/_src/tests/flrw/test_base.py @@ -0,0 +1,568 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +"""Testing :mod:`astropy.cosmology.flrw.base`. + +This module sets up the tests for subclasses of :class:`astropy.cosmology.FLRW`. The +tests for the specific abstract class :class:`astropy.cosmology.FLRW` are in +``test_flrw``. + +""" + +import abc +from functools import cached_property + +import numpy as np +import pytest + +import astropy.constants as const +import astropy.units as u +from astropy.cosmology import FLRW, FlatLambdaCDM, LambdaCDM, Planck18 +from astropy.cosmology._src.core import _COSMOLOGY_CLASSES, dataclass_decorator +from astropy.cosmology._src.flrw.base import a_B_c2 +from astropy.cosmology._src.tests.helper import get_redshift_methods +from astropy.cosmology._src.tests.test_core import ( + CosmologyTest, + FlatCosmologyMixinTest, + invalid_zs, + valid_zs, +) +from astropy.tests.helper import assert_quantity_allclose +from astropy.utils.compat.optional_deps import HAS_PANDAS, HAS_SCIPY + +from .conftest import filter_keys_from_items +from .test_parameters import ( + ParameterFlatOde0TestMixin, + ParameterH0TestMixin, + Parameterm_nuTestMixin, + ParameterNeffTestMixin, + ParameterOb0TestMixin, + ParameterOde0TestMixin, + ParameterOm0TestMixin, + ParameterTcmb0TestMixin, +) + +############################################################################## +# TESTS +############################################################################## + + +class FLRWTest( + CosmologyTest, + ParameterH0TestMixin, + ParameterOm0TestMixin, + ParameterOde0TestMixin, + ParameterTcmb0TestMixin, + ParameterNeffTestMixin, + Parameterm_nuTestMixin, + ParameterOb0TestMixin, +): + abstract_w = False + + @abc.abstractmethod + def setup_class(self): + """Setup for testing.""" + super().setup_class(self) + + # Default cosmology args and kwargs + self._cls_args = dict( + H0=70 * u.km / u.s / u.Mpc, Om0=0.27 * u.one, Ode0=0.73 * u.one + ) + self.cls_kwargs = dict( + Tcmb0=3.0 * u.K, + Ob0=0.03 * u.one, + name=self.__class__.__name__, + meta={"a": "b"}, + ) + + @pytest.fixture(scope="class") + @classmethod + def nonflatcosmo(cls): + """A non-flat cosmology used in equivalence tests.""" + return LambdaCDM(70, 0.4, 0.8) + + # =============================================================== + # Method & Attribute Tests + + def test_init(self, cosmo_cls): + """Test initialization.""" + super().test_init(cosmo_cls) + + # TODO! tests for initializing calculated values, e.g. `h` + # TODO! transfer tests for initializing neutrinos + + def test_init_Tcmb0_zeroing(self, cosmo_cls, ba): + """Test if setting Tcmb0 parameter to 0 influences other parameters. + + TODO: consider moving this test to ``FLRWTest`` + """ + ba.arguments["Tcmb0"] = 0.0 + cosmo = cosmo_cls(*ba.args, **ba.kwargs) + + assert cosmo.Ogamma0 == 0.0 + assert cosmo.Onu0 == 0.0 + + if not self.abstract_w: + assert u.allclose(cosmo.Ogamma(1.5), [0, 0, 0, 0]) + assert u.allclose(cosmo.Ogamma([0, 1, 2, 3]), [0, 0, 0, 0]) + assert u.allclose(cosmo.Onu(1.5), [0, 0, 0, 0]) + assert u.allclose(cosmo.Onu([0, 1, 2, 3]), [0, 0, 0, 0]) + + # --------------------------------------------------------------- + # Properties + + def test_Odm0(self, cosmo_cls, cosmo): + """Test ``cached_property`` ``Odm0``.""" + # on the class + assert isinstance(cosmo_cls.Odm0, cached_property) + + # on the instance + assert np.allclose(cosmo.Odm0, cosmo.Om0 - cosmo.Ob0) + + def test_Ok0(self, cosmo_cls, cosmo): + """Test ``cached_property`` ``Ok0``.""" + # on the class + assert isinstance(cosmo_cls.Ok0, cached_property) + + # on the instance + assert np.allclose( + cosmo.Ok0, 1.0 - (cosmo.Om0 + cosmo.Ode0 + cosmo.Ogamma0 + cosmo.Onu0) + ) + + def test_is_flat(self, cosmo_cls, cosmo): + """Test property ``is_flat``.""" + # on the class + assert isinstance(cosmo_cls.is_flat, property) + assert cosmo_cls.is_flat.fset is None # immutable + + # on the instance + assert isinstance(cosmo.is_flat, bool) + assert cosmo.is_flat is bool((cosmo.Ok0 == 0.0) and (cosmo.Otot0 == 1.0)) + + def test_Tnu0(self, cosmo_cls, cosmo): + """Test ``cached_property`` ``Tnu0``.""" + # on the class + assert isinstance(cosmo_cls.Tnu0, cached_property) + + # on the instance + assert cosmo.Tnu0.unit == u.K + assert u.allclose(cosmo.Tnu0, 0.7137658555036082 * cosmo.Tcmb0, rtol=1e-5) + + def test_has_massive_nu(self, cosmo_cls, cosmo): + """Test property ``has_massive_nu``.""" + # on the class + assert isinstance(cosmo_cls.has_massive_nu, property) + assert cosmo_cls.has_massive_nu.fset is None # immutable + + # on the instance + if cosmo.Tnu0 == 0: + assert cosmo.has_massive_nu is False + else: + assert cosmo.has_massive_nu is cosmo._nu_info.has_massive_nu + + def test_h(self, cosmo_cls, cosmo): + """Test ``cached_property`` ``h``.""" + # on the class + assert isinstance(cosmo_cls.h, cached_property) + + # on the instance + assert np.allclose(cosmo.h, cosmo.H0.value / 100.0) + + def test_hubble_time(self, cosmo_cls, cosmo): + """Test ``cached_property`` ``hubble_time``.""" + # on the class + assert isinstance(cosmo_cls.hubble_time, cached_property) + + # on the instance + assert u.allclose(cosmo.hubble_time, (1 / cosmo.H0) << u.Gyr) + + def test_hubble_distance(self, cosmo_cls, cosmo): + """Test ``cached_property`` ``hubble_distance``.""" + # on the class + assert isinstance(cosmo_cls.hubble_distance, cached_property) + + # on the instance + assert cosmo.hubble_distance == (const.c / cosmo.H0).to(u.Mpc) + + def test_critical_density0(self, cosmo_cls, cosmo): + """Test ``cached_property`` ``critical_density0``.""" + # on the class + assert isinstance(cosmo_cls.critical_density0, cached_property) + + # on the instance + assert cosmo.critical_density0.unit == u.g / u.cm**3 + assert u.allclose( # sanity check + cosmo.critical_density0, 3 * cosmo.H0**2 / (8 * np.pi * const.G) + ) + + def test_Ogamma0(self, cosmo_cls, cosmo): + """Test ``cached_property`` ``Ogamma0``.""" + # on the class + assert isinstance(cosmo_cls.Ogamma0, cached_property) + + # on the instance + # Ogamma cor \propto T^4/rhocrit + expect = a_B_c2 * cosmo.Tcmb0.value**4 / cosmo.critical_density0.value + assert np.allclose(cosmo.Ogamma0, expect) + # check absolute equality to 0 if Tcmb0 is 0 + if cosmo.Tcmb0 == 0: + assert cosmo.Ogamma0 == 0 + + def test_Onu0(self, cosmo_cls, cosmo): + """Test ``cached_property`` ``Onu0``.""" + # on the class + assert isinstance(cosmo_cls.Onu0, cached_property) + + # on the instance + # neutrino temperature <= photon temperature since the neutrinos + # decouple first. + if cosmo.has_massive_nu: # Tcmb0 > 0 & has massive + # check the expected formula + assert cosmo.Onu0 == cosmo.Ogamma0 * cosmo.nu_relative_density(0) + # a sanity check on on the ratio of neutrinos to photons + # technically it could be 1, but not for any of the tested cases. + assert cosmo.nu_relative_density(0) <= 1 + elif cosmo.Tcmb0 == 0: + assert cosmo.Onu0 == 0 + else: + # check the expected formula + assert cosmo.Onu0 == 0.22710731766 * cosmo.__dict__["Neff"] * cosmo.Ogamma0 + # and check compatibility with nu_relative_density + assert np.allclose( + cosmo.nu_relative_density(0), 0.22710731766 * cosmo.__dict__["Neff"] + ) + + def test_Otot0(self, cosmo): + """Test :attr:`astropy.cosmology.FLRW.Otot0`.""" + assert ( + cosmo.Otot0 + == cosmo.Om0 + cosmo.Ogamma0 + cosmo.Onu0 + cosmo.Ode0 + cosmo.Ok0 + ) + + # --------------------------------------------------------------- + # Methods + + _FLRW_redshift_methods = get_redshift_methods( + FLRW, include_private=True, include_z2=False + ) + + @pytest.mark.skipif(not HAS_SCIPY, reason="scipy is not installed") + @pytest.mark.parametrize("z, exc", invalid_zs) + @pytest.mark.parametrize("method", sorted(_FLRW_redshift_methods)) + def test_redshift_method_bad_input(self, cosmo, method, z, exc): + """Test all the redshift methods for bad input.""" + with pytest.raises(exc): + getattr(cosmo, method)(z) + + @pytest.mark.parametrize("z", valid_zs) + @abc.abstractmethod + def test_w(self, cosmo, z): + """Test :meth:`astropy.cosmology.FLRW.w`. + + Since ``w`` is abstract, each test class needs to define further tests. + """ + # super().test_w(cosmo, z) # NOT b/c abstract `w(z)` + w = cosmo.w(z) + assert np.shape(w) == np.shape(z) # test same shape + assert u.Quantity(w).unit == u.one # test no units or dimensionless + + # ------------------------------------------- + + @pytest.mark.parametrize("z", valid_zs) + def test_Otot(self, cosmo, z): + """Test :meth:`astropy.cosmology.FLRW.Otot`.""" + # super().test_Otot(cosmo) # NOT b/c abstract `w(z)` + assert np.allclose( + cosmo.Otot(z), + cosmo.Om(z) + cosmo.Ogamma(z) + cosmo.Onu(z) + cosmo.Ode(z) + cosmo.Ok(z), + ) + + def test_scale_factor0(self, cosmo): + """Test :meth:`astropy.cosmology.FLRW.scale_factor`.""" + assert isinstance(cosmo.scale_factor0, u.Quantity) + assert cosmo.scale_factor0.unit == u.one + assert cosmo.scale_factor0 == 1 + assert np.allclose(cosmo.scale_factor0, cosmo.scale_factor(0)) + + @pytest.mark.parametrize("z", valid_zs) + def test_scale_factor(self, cosmo, z): + """Test :meth:`astropy.cosmology.FLRW.scale_factor`.""" + assert np.allclose(cosmo.scale_factor(z), 1 / (1 + np.array(z))) + + # ------------------------------------------- + + @pytest.mark.skipif(not HAS_SCIPY, reason="scipy required for this test.") + def test_comoving_distance_1arg_equal_to_2arg(self, cosmo): + """Test :meth:`astropy.cosmology.FLRW.comoving_distance`.""" + # Special case of z1 = 0 + z = np.linspace(0, 1, 10) + assert u.allclose(cosmo.comoving_distance(z), cosmo.comoving_distance(0, z)) + + # General case of z1, z2 + z1 = z + z2 = z + 1 + assert u.allclose( + cosmo.comoving_distance(z2) - cosmo.comoving_distance(z1), + cosmo.comoving_distance(z1, z2), + ) + + @pytest.mark.skipif( + not (HAS_PANDAS and HAS_SCIPY), reason="requires pandas and scipy" + ) + def test_luminosity_distance_pandas(self, cosmo): + """Test :meth:`astropy.cosmology.FLRW.luminosity_distance`. + + Regression test for https://github.com/astropy/astropy/issues/15576. + """ + import pandas as pd + + z = pd.Series([0.1, 0.2, 0.3]) + d = cosmo.luminosity_distance(z) + + assert isinstance(d, u.Quantity) + assert d.unit == u.Mpc + np.testing.assert_array_equal(d, cosmo.luminosity_distance(np.array(z))) + + # --------------------------------------------------------------- + + def test_efunc_vs_invefunc(self, cosmo): + """Test that ``efunc`` and ``inv_efunc`` give inverse values. + + Note that the test doesn't need scipy because it doesn't need to call + ``de_density_scale``. + """ + # super().test_efunc_vs_invefunc(cosmo) # NOT b/c abstract `w(z)` + z0 = 0.5 + z = np.array([0.5, 1.0, 2.0, 5.0]) + + assert np.allclose(cosmo.efunc(z0), 1.0 / cosmo.inv_efunc(z0)) + assert np.allclose(cosmo.efunc(z), 1.0 / cosmo.inv_efunc(z)) + + # --------------------------------------------------------------- + # from Cosmology + + def test_clone_change_param(self, cosmo): + """Test method ``.clone()`` changing a(many) Parameter(s).""" + super().test_clone_change_param(cosmo) + + # don't change any values + kwargs = dict(cosmo.parameters) + c = cosmo.clone(**kwargs) + assert c.__class__ == cosmo.__class__ + assert c == cosmo + + # change ``H0`` + # Note that H0 affects Ode0 because it changes Ogamma0 + c = cosmo.clone(H0=100) + assert c.__class__ == cosmo.__class__ + assert c.name == cosmo.name + " (modified)" + assert c.H0.value == 100 + for n, v in filter_keys_from_items(c.parameters, ("H0",)): + v_expect = getattr(cosmo, n) + assert_quantity_allclose(v, v_expect, atol=1e-4 * getattr(v, "unit", 1)) + assert not u.allclose(c.Ogamma0, cosmo.Ogamma0) + assert not u.allclose(c.Onu0, cosmo.Onu0) + + # change multiple things + c = cosmo.clone(name="new name", H0=100, Tcmb0=2.8, meta=dict(zz="tops")) + assert c.__class__ == cosmo.__class__ + assert c.name == "new name" + assert c.H0.value == 100 + assert c.Tcmb0.value == 2.8 + assert c.meta == {**cosmo.meta, **dict(zz="tops")} + for n, v in filter_keys_from_items(c.parameters, ("H0", "Tcmb0")): + v_expect = getattr(cosmo, n) + assert_quantity_allclose(v, v_expect, atol=1e-4 * getattr(v, "unit", 1)) + assert not u.allclose(c.Ogamma0, cosmo.Ogamma0) + assert not u.allclose(c.Onu0, cosmo.Onu0) + assert not u.allclose(c.Tcmb0.value, cosmo.Tcmb0.value) + + def test_is_equivalent(self, cosmo): + """Test :meth:`astropy.cosmology.FLRW.is_equivalent`.""" + super().test_is_equivalent(cosmo) # pass to CosmologyTest + + # test against a FlatFLRWMixin + # case (3) in FLRW.is_equivalent + if isinstance(cosmo, FlatLambdaCDM): + assert cosmo.is_equivalent(Planck18) + assert Planck18.is_equivalent(cosmo) + else: + assert not cosmo.is_equivalent(Planck18) + assert not Planck18.is_equivalent(cosmo) + + # =============================================================== + # Usage Tests + + # TODO: this test should be subsumed by other tests + @pytest.mark.parametrize("method", ("Om", "Ode", "w", "de_density_scale")) + def test_distance_broadcast(self, cosmo, method): + """Test distance methods broadcast z correctly.""" + g = getattr(cosmo, method) + z = np.linspace(0.1, 1, 6) + z2d = z.reshape(2, 3) + z3d = z.reshape(3, 2, 1) + + value_flat = g(z) + assert value_flat.shape == z.shape + + value_2d = g(z2d) + assert value_2d.shape == z2d.shape + + value_3d = g(z3d) + assert value_3d.shape == z3d.shape + assert u.allclose(value_flat, value_2d.flatten()) + assert u.allclose(value_flat, value_3d.flatten()) + + @pytest.mark.skipif(not HAS_SCIPY, reason="scipy required for this test.") + def test_comoving_distance_example(self, cosmo_cls, args, kwargs, expected): + """Test :meth:`astropy.cosmology.LambdaCDM.comoving_distance`. + + These do not come from external codes -- they are just internal checks to make + sure nothing changes if we muck with the distance calculators. + """ + z = np.array([1.0, 2.0, 3.0, 4.0]) + + cosmo = cosmo_cls(*args, **kwargs) + assert u.allclose(cosmo.comoving_distance(z), expected, rtol=1e-4) + + +# ============================================================================== + + +class FlatFLRWMixinTest(FlatCosmologyMixinTest, ParameterFlatOde0TestMixin): + """Tests for :class:`astropy.cosmology.FlatFLRWMixin` subclasses. + + E.g to use this class:: + + class TestFlatSomeFLRW(FlatFLRWMixinTest, TestSomeFLRW): + ... + """ + + def setup_class(self): + """Setup for testing. + + Set up as for regular FLRW test class, but remove dark energy component + since flat cosmologies are forbidden Ode0 as an argument, + see ``test_init_subclass``. + """ + super().setup_class(self) + self._cls_args.pop("Ode0") + + # =============================================================== + # Method & Attribute Tests + + # --------------------------------------------------------------- + # class-level + + def test_init_subclass(self, cosmo_cls): + """Test initializing subclass, mostly that can't have Ode0 in init.""" + super().test_init_subclass(cosmo_cls) + + with pytest.raises(TypeError, match="subclasses of"): + + @dataclass_decorator + class HASOde0SubClass(cosmo_cls): + def __init__(self, Ode0): + pass + + _COSMOLOGY_CLASSES.pop(HASOde0SubClass.__qualname__, None) + + # --------------------------------------------------------------- + # instance-level + + def test_init(self, cosmo_cls): + super().test_init(cosmo_cls) + + cosmo = cosmo_cls(*self.cls_args, **self.cls_kwargs) + assert cosmo.Ok0 == 0.0 + assert cosmo.Ode0 == 1.0 - (cosmo.Om0 + cosmo.Ogamma0 + cosmo.Onu0 + cosmo.Ok0) + + def test_Ok0(self, cosmo_cls, cosmo): + """Test property ``Ok0``.""" + super().test_Ok0(cosmo_cls, cosmo) + + # for flat cosmologies, Ok0 is not *close* to 0, it *is* 0 + assert cosmo.Ok0 == 0.0 + + def test_Otot0(self, cosmo): + """Test :attr:`astropy.cosmology.FLRW.Otot0`. Should always be 1.""" + super().test_Otot0(cosmo) + + # for flat cosmologies, Otot0 is not *close* to 1, it *is* 1 + assert cosmo.Otot0 == 1.0 + + @pytest.mark.parametrize("z", valid_zs) + def test_Otot(self, cosmo, z): + """Test :meth:`astropy.cosmology.FLRW.Otot`. Should always be 1.""" + super().test_Otot(cosmo, z) + + # for flat cosmologies, Otot is 1, within precision. + assert u.allclose(cosmo.Otot(z), 1.0) + + @pytest.mark.skipif(not HAS_SCIPY, reason="scipy is not installed") + @pytest.mark.parametrize("z, exc", invalid_zs) + @pytest.mark.parametrize( + "method", sorted(FLRWTest._FLRW_redshift_methods - {"Otot"}) + ) + def test_redshift_method_bad_input(self, cosmo, method, z, exc): + """Test all the redshift methods for bad input.""" + super().test_redshift_method_bad_input(cosmo, method, z, exc) + + # --------------------------------------------------------------- + + def test_clone_to_nonflat_change_param(self, cosmo): + """Test method ``.clone()`` changing a(many) Parameter(s).""" + super().test_clone_to_nonflat_change_param(cosmo) + + # change Ode0, without non-flat + msg = "Cannot set 'Ode0' in clone unless 'to_nonflat=True'. " + with pytest.raises(ValueError, match=msg): + cosmo.clone(Ode0=1) + + # change to non-flat + nc = cosmo.clone(to_nonflat=True, Ode0=cosmo.Ode0) + assert isinstance(nc, cosmo.__nonflatclass__) + assert nc == cosmo.nonflat + + nc = cosmo.clone(to_nonflat=True, Ode0=1) + assert nc.Ode0 == 1.0 + assert nc.name == cosmo.name + " (modified)" + + # --------------------------------------------------------------- + + def test_is_equivalent(self, cosmo, nonflatcosmo): + """Test :meth:`astropy.cosmology.FLRW.is_equivalent`.""" + super().test_is_equivalent(cosmo) # pass to TestFLRW + + # against non-flat Cosmology + assert not cosmo.is_equivalent(nonflatcosmo) + assert not nonflatcosmo.is_equivalent(cosmo) + + # non-flat version of class + nonflat_cosmo_cls = cosmo.__nonflatclass__ + # keys check in `test_is_equivalent_nonflat_class_different_params` + + # non-flat + nonflat = nonflat_cosmo_cls(*self.cls_args, Ode0=0.9, **self.cls_kwargs) + assert not nonflat.is_equivalent(cosmo) + assert not cosmo.is_equivalent(nonflat) + + # Flat, but not FlatFLRWMixin + # This will require forcing flatness by overriding attribute values. + # Since Cosmology is frozen, the easiest way is via __dict__. + flat = nonflat_cosmo_cls( + *self.cls_args, + Ode0=1.0 - cosmo.Om0 - cosmo.Ogamma0 - cosmo.Onu0, + **self.cls_kwargs, + ) + flat.__dict__["Ok0"] = 0.0 # manually forcing flatness by setting `Ok0`. + assert flat.is_equivalent(cosmo) + assert cosmo.is_equivalent(flat) + + def test_repr(self, cosmo_cls, cosmo): + """ + Test method ``.__repr__()``. Skip non-flat superclass test. + e.g. `TestFlatLambdaCDDM` -> `FlatFLRWMixinTest` + vs `TestFlatLambdaCDDM` -> `TestLambdaCDDM` -> `FlatFLRWMixinTest` + """ + # test eliminated Ode0 from parameters + assert "Ode0" not in repr(cosmo) diff --git a/astropy/cosmology/_src/tests/flrw/test_flrw.py b/astropy/cosmology/_src/tests/flrw/test_flrw.py new file mode 100644 index 000000000000..2a9afcc86b7e --- /dev/null +++ b/astropy/cosmology/_src/tests/flrw/test_flrw.py @@ -0,0 +1,112 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +"""Testing :mod:`astropy.cosmology.FLRW`.""" + +from typing import final + +import pytest + +from astropy.cosmology import FLRW +from astropy.cosmology._src.core import _COSMOLOGY_CLASSES, dataclass_decorator +from astropy.cosmology._src.tests.helper import get_redshift_methods +from astropy.cosmology._src.tests.test_core import invalid_zs +from astropy.utils.compat.optional_deps import HAS_SCIPY + +from .test_base import FLRWTest + + +@dataclass_decorator +class SubFLRW(FLRW): + def w(self, z): + return super().w(z) + + +@final +class TestFLRW(FLRWTest): + """Test :class:`astropy.cosmology.FLRW`.""" + + abstract_w = True + + def setup_class(self): + """ + Setup for testing. + FLRW is abstract, so tests are done on a subclass. + """ + super().setup_class(self) + + # make sure SubCosmology is known + _COSMOLOGY_CLASSES["SubFLRW"] = SubFLRW + + self.cls = SubFLRW + + def teardown_class(self): + super().teardown_class(self) + _COSMOLOGY_CLASSES.pop("SubFLRW", None) + + # =============================================================== + # Method & Attribute Tests + + # --------------------------------------------------------------- + # Methods + + def test_w(self, cosmo): + """Test abstract :meth:`astropy.cosmology.FLRW.w`.""" + with pytest.raises(NotImplementedError, match="not implemented"): + cosmo.w(1) + + def test_Otot(self, cosmo): + """Test :meth:`astropy.cosmology.FLRW.Otot`.""" + exception = NotImplementedError if HAS_SCIPY else ModuleNotFoundError + with pytest.raises(exception): + assert cosmo.Otot(1) + + def test_efunc_vs_invefunc(self, cosmo): + """ + Test that efunc and inv_efunc give inverse values. + Here they just fail b/c no ``w(z)`` or no scipy. + """ + exception = NotImplementedError if HAS_SCIPY else ModuleNotFoundError + + with pytest.raises(exception): + cosmo.efunc(0.5) + + with pytest.raises(exception): + cosmo.inv_efunc(0.5) + + @pytest.mark.skip(reason="w(z) is abstract") + def test_luminosity_distance_pandas(self, cosmo): + """Test :meth:`astropy.cosmology.FLRW.luminosity_distance`.""" + + _FLRW_redshift_methods = get_redshift_methods( + FLRW, include_private=True, include_z2=False + ) - {"w"} + + @pytest.mark.skipif(not HAS_SCIPY, reason="scipy is not installed") + @pytest.mark.parametrize("z, exc", invalid_zs) + @pytest.mark.parametrize("method", sorted(_FLRW_redshift_methods)) + def test_redshift_method_bad_input(self, cosmo, method, z, exc): + """Test all the redshift methods for bad input.""" + with pytest.raises(exc): + getattr(cosmo, method)(z) + + # =============================================================== + # Usage Tests + + @pytest.mark.skipif(not HAS_SCIPY, reason="scipy required for this test.") + @pytest.mark.parametrize("method", ("Om", "Ode", "w", "de_density_scale")) + def test_distance_broadcast(self, cosmo, method): + with pytest.raises(NotImplementedError): + super().test_distance_broadcast(cosmo, method) + + @pytest.mark.skip(reason="w(z) is abstract") + def test_comoving_distance_1arg_equal_to_2arg(self, cosmo): + """Test :meth:`astropy.cosmology.FLRW.luminosity_distance`.""" + + @pytest.mark.skipif(not HAS_SCIPY, reason="scipy required for this test.") + @pytest.mark.parametrize( + ("args", "kwargs", "expected"), + [((70, 0.27, 0.73), {"Tcmb0": 3.0, "Ob0": 0.03}, None)], + ) + def test_comoving_distance_example(self, cosmo_cls, args, kwargs, expected): + with pytest.raises(NotImplementedError): + super().test_comoving_distance_example(cosmo_cls, args, kwargs, expected) diff --git a/astropy/cosmology/_src/tests/flrw/test_lambdacdm.py b/astropy/cosmology/_src/tests/flrw/test_lambdacdm.py new file mode 100644 index 000000000000..b25954339331 --- /dev/null +++ b/astropy/cosmology/_src/tests/flrw/test_lambdacdm.py @@ -0,0 +1,1059 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +"""Testing :mod:`astropy.cosmology.flrw.lambdacdm`.""" + +import pathlib +import re + +import numpy as np +import pytest + +import astropy.constants as const +import astropy.cosmology.units as cu +import astropy.units as u +from astropy.cosmology import FlatLambdaCDM, LambdaCDM +from astropy.cosmology._src.flrw.lambdacdm import ellipkinc, hyp2f1 +from astropy.cosmology._src.tests.helper import get_redshift_methods +from astropy.cosmology._src.tests.test_core import invalid_zs, valid_zs +from astropy.table import QTable +from astropy.utils.compat.optional_deps import HAS_SCIPY +from astropy.utils.exceptions import AstropyDeprecationWarning, AstropyUserWarning + +from .test_base import FlatFLRWMixinTest, FLRWTest + +############################################################################## +# TESTS +############################################################################## + + +@pytest.mark.skipif(HAS_SCIPY, reason="scipy is installed") +def test_optional_deps_functions(): + """Test stand-in functions when optional dependencies not installed.""" + with pytest.raises(ModuleNotFoundError, match="No module named 'scipy.special'"): + ellipkinc() + + with pytest.raises(ModuleNotFoundError, match="No module named 'scipy.special'"): + hyp2f1() + + +############################################################################## + + +class TestLambdaCDM(FLRWTest): + """Test :class:`astropy.cosmology.LambdaCDM`.""" + + def setup_class(self): + """Setup for testing.""" + super().setup_class(self) + self.cls = LambdaCDM + + # =============================================================== + # Method & Attribute Tests + + _FLRW_redshift_methods = get_redshift_methods( + LambdaCDM, include_private=True, include_z2=False + ) - {"_dS_age"} + # `_dS_age` is removed because it doesn't strictly rely on the value of `z`, + # so any input that doesn't trip up ``np.shape`` is "valid" + + @pytest.mark.skipif(not HAS_SCIPY, reason="scipy is not installed") + @pytest.mark.parametrize("z, exc", invalid_zs) + @pytest.mark.parametrize("method", sorted(_FLRW_redshift_methods)) + def test_redshift_method_bad_input(self, cosmo, method, z, exc): + """Test all the redshift methods for bad input.""" + super().test_redshift_method_bad_input(cosmo, method, z, exc) + + @pytest.mark.parametrize("z", valid_zs) + def test_w(self, cosmo, z): + """Test :meth:`astropy.cosmology.LambdaCDM.w`.""" + super().test_w(cosmo, z) + + w = cosmo.w(z) + assert u.allclose(w, -1.0) + + def test_repr(self, cosmo): + """Test method ``.__repr__()``.""" + assert repr(cosmo) == ( + "LambdaCDM(name='ABCMeta', H0=, Om0=0.27," + " Ode0=0.73, Tcmb0=, Neff=3.04," + " m_nu=, Ob0=0.03)" + ) + + def test_str(self, cosmo): + """Test method ``.__str__()``.""" + assert str(cosmo) == ( + 'LambdaCDM(name="ABCMeta", H0=70.0 km / (Mpc s), Om0=0.27, Ode0=0.73,' + " Tcmb0=3.0 K, Neff=3.04, m_nu=[0. 0. 0.] eV, Ob0=0.03)" + ) + + @pytest.mark.skipif(not HAS_SCIPY, reason="scipy is not installed") + @pytest.mark.parametrize( + ("args", "kwargs", "expected"), + [ + ( # no relativistic species + (75.0, 0.25, 0.5), + {"Tcmb0": 0.0}, + [2953.93001902, 4616.7134253, 5685.07765971, 6440.80611897] * u.Mpc, + ), + ( # massless neutrinos + (75.0, 0.25, 0.6), + {"Tcmb0": 3.0, "Neff": 3, "m_nu": u.Quantity(0.0, u.eV)}, + [3037.12620424, 4776.86236327, 5889.55164479, 6671.85418235] * u.Mpc, + ), + ( # massive neutrinos + (75.0, 0.3, 0.4), + {"Tcmb0": 3.0, "Neff": 3, "m_nu": u.Quantity(10.0, u.eV)}, + [2471.80626824, 3567.1902565, 4207.15995626, 4638.20476018] * u.Mpc, + ), + ], + ) + def test_comoving_distance_example(self, cosmo_cls, args, kwargs, expected): + """Test :meth:`astropy.cosmology.LambdaCDM.comoving_distance`. + + These do not come from external codes -- they are just internal checks to make + sure nothing changes if we muck with the distance calculators. + """ + super().test_comoving_distance_example(cosmo_cls, args, kwargs, expected) + + +# ----------------------------------------------------------------------------- + + +class TestFlatLambdaCDM(FlatFLRWMixinTest, TestLambdaCDM): + """Test :class:`astropy.cosmology.FlatLambdaCDM`.""" + + def setup_class(self): + """Setup for testing.""" + super().setup_class(self) + self.cls = FlatLambdaCDM + + @pytest.mark.skipif(not HAS_SCIPY, reason="scipy is not installed") + @pytest.mark.parametrize("z, exc", invalid_zs) + @pytest.mark.parametrize( + "method", sorted(TestLambdaCDM._FLRW_redshift_methods - {"Otot"}) + ) + def test_redshift_method_bad_input(self, cosmo, method, z, exc): + """Test all the redshift methods for bad input.""" + super().test_redshift_method_bad_input(cosmo, method, z, exc) + + # =============================================================== + # Method & Attribute Tests + + def test_repr(self, cosmo): + """Test method ``.__repr__()``.""" + assert repr(cosmo) == ( + "FlatLambdaCDM(name='ABCMeta', H0=, Om0=0.27," + " Tcmb0=, Neff=3.04, m_nu=," + " Ob0=0.03)" + ) + + def test_str(self, cosmo): + """Test method ``.__str__()``.""" + assert str(cosmo) == ( + 'FlatLambdaCDM(name="ABCMeta", H0=70.0 km / (Mpc s), Om0=0.27, ' + "Tcmb0=3.0 K, Neff=3.04, m_nu=[0. 0. 0.] eV, Ob0=0.03)" + ) + + # =============================================================== + # Usage Tests + + @pytest.mark.skipif(not HAS_SCIPY, reason="scipy is not installed") + @pytest.mark.parametrize( + ("args", "kwargs", "expected"), + [ + ( # no relativistic species + (75.0, 0.25), + {"Tcmb0": 0.0}, + [3180.83488552, 5060.82054204, 6253.6721173, 7083.5374303] * u.Mpc, + ), + ( # massless neutrinos + (75.0, 0.25), + {"Tcmb0": 3.0, "Neff": 3, "m_nu": u.Quantity(0.0, u.eV)}, + [3180.42662867, 5059.60529655, 6251.62766102, 7080.71698117] * u.Mpc, + ), + ( # massive neutrinos + (75.0, 0.25), + {"Tcmb0": 3.0, "Neff": 3, "m_nu": u.Quantity(10.0, u.eV)}, + [2337.54183142, 3371.91131264, 3988.40711188, 4409.09346922] * u.Mpc, + ), + ( # work the scalar nu density functions + (75.0, 0.25), + {"Tcmb0": 3.0, "m_nu": u.Quantity([10.0, 0, 0], u.eV)}, + [2777.71589173, 4186.91111666, 5046.0300719, 5636.10397302] * u.Mpc, + ), + ( # work the scalar nu density functions + (75.0, 0.25), + {"Tcmb0": 3.0, "m_nu": u.Quantity([10.0, 5, 0], u.eV)}, + [2636.48149391, 3913.14102091, 4684.59108974, 5213.07557084] * u.Mpc, + ), + ( # work the scalar nu density functions + (75.0, 0.25), + {"Tcmb0": 3.0, "m_nu": u.Quantity([4.0, 5, 9], u.eV)}, + [2563.5093049, 3776.63362071, 4506.83448243, 5006.50158829] * u.Mpc, + ), + ( # work the scalar nu density functions + (75.0, 0.25), + {"Tcmb0": 3.0, "Neff": 4.2, "m_nu": u.Quantity([1.0, 4.0, 5, 9], u.eV)}, + [2525.58017482, 3706.87633298, 4416.58398847, 4901.96669755] * u.Mpc, + ), + ], + ) + def test_comoving_distance_example(self, cosmo_cls, args, kwargs, expected): + """Test :meth:`astropy.cosmology.LambdaCDM.comoving_distance`. + + These do not come from external codes -- they are just internal checks to make + sure nothing changes if we muck with the distance calculators. + """ + super().test_comoving_distance_example(cosmo_cls, args, kwargs, expected) + + +############################################################################## +# Comparison to Other Codes + + +@pytest.mark.skipif(not HAS_SCIPY, reason="requires scipy.") +def test_flat_z1(): + """Test a flat cosmology at z=1 against several other on-line calculators. + + Test values were taken from the following web cosmology calculators on + 2012-02-11: + + Wright: http://www.astro.ucla.edu/~wright/CosmoCalc.html + (https://ui.adsabs.harvard.edu/abs/2006PASP..118.1711W) + Kempner: http://www.kempner.net/cosmic.php + iCosmos: http://www.icosmos.co.uk/index.html + """ + cosmo = FlatLambdaCDM(H0=70, Om0=0.27, Tcmb0=0.0) + + # The order of values below is Wright, Kempner, iCosmos' + assert u.allclose( + cosmo.comoving_distance(1), [3364.5, 3364.8, 3364.7988] * u.Mpc, rtol=1e-4 + ) + assert u.allclose( + cosmo.angular_diameter_distance(1), + [1682.3, 1682.4, 1682.3994] * u.Mpc, + rtol=1e-4, + ) + assert u.allclose( + cosmo.luminosity_distance(1), [6729.2, 6729.6, 6729.5976] * u.Mpc, rtol=1e-4 + ) + assert u.allclose( + cosmo.lookback_time(1), [7.841, 7.84178, 7.843] * u.Gyr, rtol=1e-3 + ) + assert u.allclose( + cosmo.lookback_distance(1), [2404.0, 2404.24, 2404.4] * u.Mpc, rtol=1e-3 + ) + + +############################################################################## +# Regression Tests + + +SPECIALIZED_COMOVING_DISTANCE_COSMOLOGIES = [ + FlatLambdaCDM(H0=70, Om0=0.0, Tcmb0=0.0), # de Sitter + FlatLambdaCDM(H0=70, Om0=1.0, Tcmb0=0.0), # Einstein - de Sitter + FlatLambdaCDM(H0=70, Om0=0.3, Tcmb0=0.0), # Hypergeometric + LambdaCDM(H0=70, Om0=0.3, Ode0=0.6, Tcmb0=0.0), # Elliptic +] + +ITERABLE_REDSHIFTS = [ + (0, 1, 2, 3, 4), # tuple + [0, 1, 2, 3, 4], # list + np.array([0, 1, 2, 3, 4]), # array +] + + +@pytest.mark.skipif(not HAS_SCIPY, reason="test requires scipy") +@pytest.mark.parametrize("cosmo", SPECIALIZED_COMOVING_DISTANCE_COSMOLOGIES) +@pytest.mark.parametrize("z", ITERABLE_REDSHIFTS) +def test_comoving_distance_iterable_argument(cosmo, z): + """ + Regression test for #10980 + Test that specialized comoving distance methods handle iterable arguments. + """ + + assert u.allclose( + cosmo.comoving_distance(z), cosmo._integral_comoving_distance_z1z2(0.0, z) + ) + + +@pytest.mark.skipif(not HAS_SCIPY, reason="test requires scipy") +@pytest.mark.parametrize("cosmo", SPECIALIZED_COMOVING_DISTANCE_COSMOLOGIES) +def test_comoving_distance_broadcast(cosmo): + """ + Regression test for #10980 + Test that specialized comoving distance methods broadcast array arguments. + """ + + z1 = np.zeros((2, 5)) + z2 = np.ones((3, 1, 5)) + z3 = np.ones((7, 5)) + output_shape = np.broadcast(z1, z2).shape + + # Check compatible array arguments return an array with the correct shape + assert cosmo.comoving_distance(z1, z2).shape == output_shape + + # Check incompatible array arguments raise an error + with pytest.raises(ValueError, match="z1 and z2 have different shapes"): + cosmo.comoving_distance(z1, z3) + + +@pytest.mark.skipif(not HAS_SCIPY, reason="test requires scipy") +def test_elliptic_comoving_distance_z1z2(): + """Regression test for #8388.""" + cosmo = LambdaCDM(70.0, 2.3, 0.05, Tcmb0=0) + z = 0.2 + assert u.allclose( + cosmo.comoving_distance(z), cosmo._integral_comoving_distance_z1z2(0.0, z) + ) + assert u.allclose( + cosmo._elliptic_comoving_distance_z1z2(0.0, z), + cosmo._integral_comoving_distance_z1z2(0.0, z), + ) + + +@pytest.mark.skipif(not HAS_SCIPY, reason="test requires scipy") +def test_ogamma(): + """Tests the effects of changing the temperature of the CMB""" + + # Tested against Ned Wright's advanced cosmology calculator, + # Sep 7 2012. The accuracy of our comparison is limited by + # how many digits it outputs, which limits our test to about + # 0.2% accuracy. The NWACC does not allow one + # to change the number of nuetrino species, fixing that at 3. + # Also, inspection of the NWACC code shows it uses inaccurate + # constants at the 0.2% level (specifically, a_B), + # so we shouldn't expect to match it that well. The integral is + # also done rather crudely. Therefore, we should not expect + # the NWACC to be accurate to better than about 0.5%, which is + # unfortunate, but reflects a problem with it rather than this code. + # More accurate tests below using Mathematica + z = np.array([1.0, 10.0, 500.0, 1000.0]) + + cosmo = FlatLambdaCDM(H0=70, Om0=0.3, Tcmb0=0, Neff=3) + assert u.allclose( + cosmo.angular_diameter_distance(z), + [1651.9, 858.2, 26.855, 13.642] * u.Mpc, + rtol=5e-4, + ) + + cosmo = FlatLambdaCDM(H0=70, Om0=0.3, Tcmb0=2.725, Neff=3) + assert u.allclose( + cosmo.angular_diameter_distance(z), + [1651.8, 857.9, 26.767, 13.582] * u.Mpc, + rtol=5e-4, + ) + + cosmo = FlatLambdaCDM(H0=70, Om0=0.3, Tcmb0=4.0, Neff=3) + assert u.allclose( + cosmo.angular_diameter_distance(z), + [1651.4, 856.6, 26.489, 13.405] * u.Mpc, + rtol=5e-4, + ) + + # Next compare with doing the integral numerically in Mathematica, + # which allows more precision in the test. It is at least as + # good as 0.01%, possibly better + cosmo = FlatLambdaCDM(H0=70, Om0=0.3, Tcmb0=0, Neff=3.04) + assert u.allclose( + cosmo.angular_diameter_distance(z), + [1651.91, 858.205, 26.8586, 13.6469] * u.Mpc, + rtol=1e-5, + ) + + cosmo = FlatLambdaCDM(H0=70, Om0=0.3, Tcmb0=2.725, Neff=3.04) + assert u.allclose( + cosmo.angular_diameter_distance(z), + [1651.76, 857.817, 26.7688, 13.5841] * u.Mpc, + rtol=1e-5, + ) + + cosmo = FlatLambdaCDM(H0=70, Om0=0.3, Tcmb0=4.0, Neff=3.04) + assert u.allclose( + cosmo.angular_diameter_distance(z), + [1651.21, 856.411, 26.4845, 13.4028] * u.Mpc, + rtol=1e-5, + ) + + # Just to be really sure, we also do a version where the integral + # is analytic, which is a Ode = 0 flat universe. In this case + # Integrate(1/E(x),{x,0,z}) = 2 ( sqrt((1+Or z)/(1+z)) - 1 )/(Or - 1) + # Recall that c/H0 * Integrate(1/E) is FLRW.comoving_distance. + Ogamma0h2 = 4 * 5.670373e-8 / 299792458.0**3 * 2.725**4 / 1.87837e-26 + Onu0h2 = Ogamma0h2 * 7.0 / 8.0 * (4.0 / 11.0) ** (4.0 / 3.0) * 3.04 + Or0 = (Ogamma0h2 + Onu0h2) / 0.7**2 + Om0 = 1.0 - Or0 + hubdis = (299792.458 / 70.0) * u.Mpc + cosmo = FlatLambdaCDM(H0=70, Om0=Om0, Tcmb0=2.725, Neff=3.04) + targvals = 2.0 * hubdis * (np.sqrt((1.0 + Or0 * z) / (1.0 + z)) - 1.0) / (Or0 - 1.0) + assert u.allclose(cosmo.comoving_distance(z), targvals, rtol=1e-5) + + # And integers for z + assert u.allclose(cosmo.comoving_distance(z.astype(int)), targvals, rtol=1e-5) + + # Try Tcmb0 = 4 + Or0 *= (4.0 / 2.725) ** 4 + Om0 = 1.0 - Or0 + cosmo = FlatLambdaCDM(H0=70, Om0=Om0, Tcmb0=4.0, Neff=3.04) + targvals = 2.0 * hubdis * (np.sqrt((1.0 + Or0 * z) / (1.0 + z)) - 1.0) / (Or0 - 1.0) + assert u.allclose(cosmo.comoving_distance(z), targvals, rtol=1e-5) + + +@pytest.mark.skipif(not HAS_SCIPY, reason="test requires scipy") +@pytest.mark.parametrize( + "file_name", ["cosmo_flat.ecsv", "cosmo_open.ecsv", "cosmo_closed.ecsv"] +) +def test_flat_open_closed_icosmo(file_name): + """Test against the tabulated values generated from icosmo.org + with three example cosmologies (flat, open and closed). + """ + with u.add_enabled_units(cu): + tbl = QTable.read(pathlib.Path(__file__).parent / "data" / file_name) + cosmo = LambdaCDM( + H0=100 * tbl.meta["h"], Om0=tbl.meta["Om"], Ode0=tbl.meta["Ol"], Tcmb0=0.0 + ) + assert u.allclose(cosmo.comoving_transverse_distance(tbl["redshift"]), tbl["dm"]) + assert u.allclose(cosmo.angular_diameter_distance(tbl["redshift"]), tbl["da"]) + assert u.allclose(cosmo.luminosity_distance(tbl["redshift"]), tbl["dl"]) + + +@pytest.mark.skipif(not HAS_SCIPY, reason="test requires scipy") +def test_comoving_transverse_distance_z1z2(): + tcos = FlatLambdaCDM(100, 0.3, Tcmb0=0.0) + + with pytest.raises(ValueError): # test diff size z1, z2 fail + tcos._comoving_transverse_distance_z1z2((1, 2), (3, 4, 5)) + + # Tests that should actually work, target values computed with + # http://www.astro.multivax.de:8000/phillip/angsiz_prog/README.HTML + # Kayser, Helbig, and Schramm (Astron.Astrophys. 318 (1997) 680-686) + assert u.allclose( + tcos._comoving_transverse_distance_z1z2(1, 2), 1313.2232194828466 * u.Mpc + ) + + # In a flat universe comoving distance and comoving transverse + # distance are identical + z1 = 0, 0, 2, 0.5, 1 + z2 = 2, 1, 1, 2.5, 1.1 + + assert u.allclose( + tcos.comoving_distance(z1, z2), + tcos._comoving_transverse_distance_z1z2(z1, z2), + ) + + # Test Flat Universe with Omega_M > 1. Rarely used, but perfectly valid. + tcos = FlatLambdaCDM(100, 1.5, Tcmb0=0.0) + results = ( + 2202.72682564, + 1559.51679971, + -643.21002593, + 1408.36365679, + 85.09286258, + ) * u.Mpc + + assert u.allclose(tcos._comoving_transverse_distance_z1z2(z1, z2), results) + + # In a flat universe comoving distance and comoving transverse + # distance are identical + z1 = 0, 0, 2, 0.5, 1 + z2 = 2, 1, 1, 2.5, 1.1 + + assert u.allclose( + tcos.comoving_distance(z1, z2), + tcos._comoving_transverse_distance_z1z2(z1, z2), + ) + # Test non-flat cases to avoid simply testing + # comoving_distance. Test array, array case. + tcos = LambdaCDM(100, 0.3, 0.5, Tcmb0=0.0) + results = ( + 3535.931375645655, + 2226.430046551708, + -1208.6817970036532, + 2595.567367601969, + 151.36592003406884, + ) * u.Mpc + + assert u.allclose(tcos._comoving_transverse_distance_z1z2(z1, z2), results) + + # Test positive curvature with scalar, array combination. + tcos = LambdaCDM(100, 1.0, 0.2, Tcmb0=0.0) + z1 = 0.1 + z2 = 0, 0.1, 0.2, 0.5, 1.1, 2 + results = ( + -281.31602666724865, + 0.0, + 248.58093707820436, + 843.9331377460543, + 1618.6104987686672, + 2287.5626543279927, + ) * u.Mpc + + assert u.allclose(tcos._comoving_transverse_distance_z1z2(z1, z2), results) + + +@pytest.mark.skipif(not HAS_SCIPY, reason="test requires scipy") +def test_angular_diameter_distance_z1z2(): + tcos = FlatLambdaCDM(70.4, 0.272, Tcmb0=0.0) + + with pytest.raises(ValueError): # test diff size z1, z2 fail + tcos.angular_diameter_distance([1, 2], [3, 4, 5]) + + # Tests that should actually work, target values computed with + # http://www.astro.multivax.de:8000/phillip/angsiz_prog/README.HTML + # Kayser, Helbig, and Schramm (Astron.Astrophys. 318 (1997) 680-686) + assert u.allclose(tcos.angular_diameter_distance(1, 2), 646.22968662822018 * u.Mpc) + + z1 = 2 # Separate test for z2 Om0 errors + tba.arguments["Ob0"] = tba.arguments["Om0"] + 0.1 + with pytest.raises(ValueError, match="baryonic density can not be larger"): + cosmo_cls(*tba.args, **tba.kwargs) + + # In FLRW `Ob(z)` requires `w(z)`. + if not self.abstract_w: + assert cosmo.Ob(1) == 0 + + # The default value is None + assert cosmo_cls.parameters["Ob0"].default == 0.0 + + def test_Ob0_cannot_be_None(self, cosmo_cls: type[Cosmology], ba: BoundArguments): + """Test that Ob0 cannot be None.""" + ba.arguments["Ob0"] = None + with pytest.raises(TypeError): + cosmo_cls(*ba.args, **ba.kwargs) + + +# ============================================================================= + + +class ParameterFlatOde0TestMixin(ParameterOde0TestMixin): + """Tests for `astropy.cosmology.Parameter` Ode0 on a flat Cosmology. + + This will augment or override some tests in ``ParameterOde0TestMixin``. + + Ode0 is a descriptor, which are tested by mixin, here with ``TestFLRW``. + These tests expect dicts ``_cls_args`` and ``cls_kwargs`` which give the + args and kwargs for the cosmology class, respectively. See ``TestFLRW``. + """ + + def test_Parameter_Ode0(self, cosmo_cls: type[Cosmology]): + """Test Parameter ``Ode0`` on the class.""" + super().test_Parameter_Ode0(cosmo_cls) + Ode0 = cosmo_cls.parameters.get("Ode0", cosmo_cls._derived_parameters["Ode0"]) + assert Ode0.derived in (True, np.True_) + + def test_Ode0(self, cosmo: Cosmology): + """Test no-longer-Parameter ``Ode0``.""" + assert cosmo.Ode0 is cosmo.__dict__["Ode0"] + assert cosmo.Ode0 == 1.0 - (cosmo.Om0 + cosmo.Ogamma0 + cosmo.Onu0) + + def test_init_Ode0(self, cosmo_cls: type[Cosmology], ba: BoundArguments): + """Test initialization for values of ``Ode0``.""" + cosmo = cosmo_cls(*ba.args, **ba.kwargs) + assert cosmo.Ode0 == 1.0 - (cosmo.Om0 + cosmo.Ogamma0 + cosmo.Onu0 + cosmo.Ok0) + + # Ode0 is not in the signature + with pytest.raises(TypeError, match="Ode0"): + cosmo_cls(*ba.args, **ba.kwargs, Ode0=1) diff --git a/astropy/cosmology/_src/tests/flrw/test_w.py b/astropy/cosmology/_src/tests/flrw/test_w.py new file mode 100644 index 000000000000..a19aa6aa6dba --- /dev/null +++ b/astropy/cosmology/_src/tests/flrw/test_w.py @@ -0,0 +1,89 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +"""Testing :mod:`astropy.cosmology.FLRW` neutrinos.""" + +import numpy as np +import pytest + +import astropy.units as u +from astropy.cosmology import FLRW, wCDM +from astropy.utils.compat.optional_deps import HAS_SCIPY + +############################################################################## +# TYPES + + +class W1(FLRW): + """ + This class is to test whether the routines work correctly if one only overloads w(z). + """ + + def __init__(self): + super().__init__(70.0, 0.27, 0.73, Tcmb0=0.0, name="test_cos") + self.__dict__["w0"] = -0.9 + + def w(self, z): + return self.w0 * np.ones_like(z) + + +class W1nu(FLRW): + """Similar, but with neutrinos.""" + + def __init__(self): + super().__init__( + 70.0, 0.27, 0.73, Tcmb0=3.0, m_nu=0.1 * u.eV, name="test_cos_nu" + ) + self.__dict__["w0"] = -0.8 + + def w(self, z): + return self.w0 * np.ones_like(z) + + +############################################################################## +# TESTS +############################################################################## + + +@pytest.mark.skipif(not HAS_SCIPY, reason="test requires scipy") +def test_de_subclass(): + z = [0.2, 0.4, 0.6, 0.9] + + # This is the comparison object + cosmo = wCDM(H0=70, Om0=0.27, Ode0=0.73, w0=-0.9, Tcmb0=0.0) + # Values taken from Ned Wrights advanced cosmo calculator, Aug 17 2012 + assert u.allclose( + cosmo.luminosity_distance(z), [975.5, 2158.2, 3507.3, 5773.1] * u.Mpc, rtol=1e-3 + ) + + # Now try the subclass that only gives w(z) + cosmo = W1() + assert u.allclose( + cosmo.luminosity_distance(z), [975.5, 2158.2, 3507.3, 5773.1] * u.Mpc, rtol=1e-3 + ) + # Test efunc + assert u.allclose(cosmo.efunc(1.0), 1.7489240754, rtol=1e-5) + assert u.allclose(cosmo.efunc([0.5, 1.0]), [1.31744953, 1.7489240754], rtol=1e-5) + assert u.allclose(cosmo.inv_efunc([0.5, 1.0]), [0.75904236, 0.57178011], rtol=1e-5) + # Test de_density_scale + assert u.allclose(cosmo.de_density_scale(1.0), 1.23114444, rtol=1e-4) + assert u.allclose( + cosmo.de_density_scale([0.5, 1.0]), [1.12934694, 1.23114444], rtol=1e-4 + ) + + +@pytest.mark.skipif(not HAS_SCIPY, reason="test requires scipy") +def test_efunc_vs_invefunc_flrw(): + """Test that efunc and inv_efunc give inverse values""" + z0 = 0.5 + z = np.array([0.5, 1.0, 2.0, 5.0]) + + # FLRW is abstract, so requires W1 defined earlier + # This requires scipy, unlike the built-ins, because it + # calls de_density_scale, which has an integral in it + cosmo = W1() + assert u.allclose(cosmo.efunc(z0), 1.0 / cosmo.inv_efunc(z0)) + assert u.allclose(cosmo.efunc(z), 1.0 / cosmo.inv_efunc(z)) + # Add neutrinos + cosmo = W1nu() + assert u.allclose(cosmo.efunc(z0), 1.0 / cosmo.inv_efunc(z0)) + assert u.allclose(cosmo.efunc(z), 1.0 / cosmo.inv_efunc(z)) diff --git a/astropy/cosmology/_src/tests/flrw/test_w0cdm.py b/astropy/cosmology/_src/tests/flrw/test_w0cdm.py new file mode 100644 index 000000000000..2cdf0e2e42b6 --- /dev/null +++ b/astropy/cosmology/_src/tests/flrw/test_w0cdm.py @@ -0,0 +1,211 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +"""Testing :mod:`astropy.cosmology.w0cdm`.""" + +import numpy as np +import pytest + +import astropy.units as u +from astropy.cosmology import FlatwCDM, wCDM +from astropy.cosmology._src.parameter import Parameter +from astropy.cosmology._src.tests.test_core import ParameterTestMixin, valid_zs +from astropy.tests.helper import assert_quantity_allclose +from astropy.utils.compat.optional_deps import HAS_SCIPY + +from .conftest import filter_keys_from_items +from .test_base import FlatFLRWMixinTest, FLRWTest + +############################################################################## +# TESTS +############################################################################## + + +class Parameterw0TestMixin(ParameterTestMixin): + """Tests for `astropy.cosmology.Parameter` w0 on a Cosmology. + + w0 is a descriptor, which are tested by mixin, here with ``TestFLRW``. + These tests expect dicts ``_cls_args`` and ``cls_kwargs`` which give the + args and kwargs for the cosmology class, respectively. See ``TestFLRW``. + """ + + def test_w0(self, cosmo_cls, cosmo): + """Test Parameter ``w0``.""" + # on the class + w0 = cosmo_cls.parameters["w0"] + assert isinstance(w0, Parameter) + assert "Dark energy equation of state" in w0.__doc__ + assert w0.unit is None + assert w0.default == -1.0 + + # on the instance + assert cosmo.w0 is cosmo.__dict__["w0"] + assert cosmo.w0 == self.cls_kwargs["w0"] + + def test_init_w0(self, cosmo_cls, ba): + """Test initialization for values of ``w0``.""" + # test that it works with units + ba.arguments["w0"] = ba.arguments["w0"] << u.one # ensure units + cosmo = cosmo_cls(*ba.args, **ba.kwargs) + assert cosmo.w0 == ba.arguments["w0"] + + # also without units + ba.arguments["w0"] = ba.arguments["w0"].value # strip units + cosmo = cosmo_cls(*ba.args, **ba.kwargs) + assert cosmo.w0 == ba.arguments["w0"] + + # must be dimensionless + ba.arguments["w0"] = 10 * u.km + with pytest.raises(TypeError): + cosmo_cls(*ba.args, **ba.kwargs) + + +class TestwCDM(FLRWTest, Parameterw0TestMixin): + """Test :class:`astropy.cosmology.wCDM`.""" + + def setup_class(self): + """Setup for testing.""" + super().setup_class(self) + + self.cls = wCDM + self.cls_kwargs.update(w0=-0.5) + + # =============================================================== + # Method & Attribute Tests + + def test_clone_change_param(self, cosmo): + """Test method ``.clone()`` changing a(many) Parameter(s).""" + super().test_clone_change_param(cosmo) + + # `w` params + c = cosmo.clone(w0=0.1) + assert c.w0 == 0.1 + for n, v in filter_keys_from_items(c.parameters, ("w0",)): + v_expect = getattr(cosmo, n) + assert_quantity_allclose(v, v_expect, atol=1e-4 * getattr(v, "unit", 1)) + + @pytest.mark.parametrize("z", valid_zs) + def test_w(self, cosmo, z): + """Test :meth:`astropy.cosmology.wCDM.w`.""" + super().test_w(cosmo, z) + + w = cosmo.w(z) + assert u.allclose(w, self.cls_kwargs["w0"]) + + def test_repr(self, cosmo_cls, cosmo): + """Test method ``.__repr__()``.""" + assert repr(cosmo) == ( + "wCDM(name='ABCMeta', H0=, Om0=0.27, " + "Ode0=0.73, Tcmb0=, Neff=3.04, " + "m_nu=, Ob0=0.03, w0=-0.5)" + ) + + # =============================================================== + # Usage Tests + + @pytest.mark.skipif(not HAS_SCIPY, reason="scipy is not installed") + @pytest.mark.parametrize( + ("args", "kwargs", "expected"), + [ + ( # no relativistic species + (75.0, 0.25, 0.4), + {"w0": -0.9, "Tcmb0": 0.0}, + [2849.6163356, 4428.71661565, 5450.97862778, 6179.37072324] * u.Mpc, + ), + ( # massless neutrinos + (75.0, 0.25, 0.4), + {"w0": -1.1, "Tcmb0": 3.0, "Neff": 3, "m_nu": u.Quantity(0.0, u.eV)}, + [2904.35580229, 4511.11471267, 5543.43643353, 6275.9206788] * u.Mpc, + ), + ( # massive neutrinos + (75.0, 0.25, 0.4), + {"w0": -0.9, "Tcmb0": 3.0, "Neff": 3, "m_nu": u.Quantity(10.0, u.eV)}, + [2473.32522734, 3581.54519631, 4232.41674426, 4671.83818117] * u.Mpc, + ), + ], + ) + def test_comoving_distance_example(self, cosmo_cls, args, kwargs, expected): + """Test :meth:`astropy.cosmology.LambdaCDM.comoving_distance`. + + These do not come from external codes -- they are just internal checks to make + sure nothing changes if we muck with the distance calculators. + """ + super().test_comoving_distance_example(cosmo_cls, args, kwargs, expected) + + +# ----------------------------------------------------------------------------- + + +class TestFlatwCDM(FlatFLRWMixinTest, TestwCDM): + """Test :class:`astropy.cosmology.FlatwCDM`.""" + + def setup_class(self): + """Setup for testing.""" + super().setup_class(self) + self.cls = FlatwCDM + self.cls_kwargs.update(w0=-0.5) + + def test_repr(self, cosmo_cls, cosmo): + """Test method ``.__repr__()``.""" + super().test_repr(cosmo_cls, cosmo) + + assert repr(cosmo) == ( + "FlatwCDM(name='ABCMeta', H0=, Om0=0.27, " + "Tcmb0=, Neff=3.04, m_nu=, " + "Ob0=0.03, w0=-0.5)" + ) + + # =============================================================== + # Usage Tests + + @pytest.mark.skipif(not HAS_SCIPY, reason="scipy is not installed") + @pytest.mark.parametrize( + ("args", "kwargs", "expected"), + [ + ( # no relativistic species + (75.0, 0.25), + {"w0": -1.05, "Tcmb0": 0.0}, + [3216.8296894, 5117.2097601, 6317.05995437, 7149.68648536] * u.Mpc, + ), + ( # massless neutrinos + (75.0, 0.25), + {"w0": -0.95, "Tcmb0": 3.0, "Neff": 3, "m_nu": u.Quantity(0.0, u.eV)}, + [3143.56537758, 5000.32196494, 6184.11444601, 7009.80166062] * u.Mpc, + ), + ( # massive neutrinos + (75.0, 0.25), + {"w0": -0.9, "Tcmb0": 3.0, "Neff": 3, "m_nu": u.Quantity(10.0, u.eV)}, + [2337.76035371, 3372.1971387, 3988.71362289, 4409.40817174] * u.Mpc, + ), + ], + ) + def test_comoving_distance_example(self, cosmo_cls, args, kwargs, expected): + """Test :meth:`astropy.cosmology.LambdaCDM.comoving_distance`. + + These do not come from external codes -- they are just internal checks to make + sure nothing changes if we muck with the distance calculators. + """ + super().test_comoving_distance_example(cosmo_cls, args, kwargs, expected) + + +############################################################################## +# Miscellaneous +# TODO: these should be better integrated into the new test framework + + +@pytest.mark.skipif(not HAS_SCIPY, reason="test requires scipy") +def test_de_densityscale(): + cosmo = wCDM(H0=70, Om0=0.3, Ode0=0.60, w0=-0.5) + + z = np.array([0.1, 0.2, 0.5, 1.5, 2.5]) + assert u.allclose( + cosmo.de_density_scale(z), + [1.15369, 1.31453, 1.83712, 3.95285, 6.5479], + rtol=1e-4, + ) + + assert u.allclose(cosmo.de_density_scale(3), cosmo.de_density_scale(3.0), rtol=1e-7) + assert u.allclose( + cosmo.de_density_scale([1, 2, 3]), + cosmo.de_density_scale([1.0, 2.0, 3.0]), + rtol=1e-7, + ) diff --git a/astropy/cosmology/_src/tests/flrw/test_w0wacdm.py b/astropy/cosmology/_src/tests/flrw/test_w0wacdm.py new file mode 100644 index 000000000000..beb596ee87b5 --- /dev/null +++ b/astropy/cosmology/_src/tests/flrw/test_w0wacdm.py @@ -0,0 +1,284 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +"""Testing :mod:`astropy.cosmology.w0wacdm`.""" + +import numpy as np +import pytest + +import astropy.units as u +from astropy.cosmology import Flatw0waCDM, Planck18, w0waCDM +from astropy.cosmology._src.parameter import Parameter +from astropy.cosmology._src.tests.test_core import ParameterTestMixin +from astropy.tests.helper import assert_quantity_allclose +from astropy.utils.compat.optional_deps import HAS_SCIPY + +from .conftest import filter_keys_from_items +from .test_base import FlatFLRWMixinTest, FLRWTest +from .test_w0cdm import Parameterw0TestMixin + +############################################################################## +# TESTS +############################################################################## + + +class ParameterwaTestMixin(ParameterTestMixin): + """Tests for `astropy.cosmology.Parameter` wa on a Cosmology. + + wa is a descriptor, which are tested by mixin, here with ``TestFLRW``. + These tests expect dicts ``_cls_args`` and ``cls_kwargs`` which give the + args and kwargs for the cosmology class, respectively. See ``TestFLRW``. + """ + + def test_wa(self, cosmo_cls, cosmo): + """Test Parameter ``wa``.""" + # on the class + wa = cosmo_cls.parameters["wa"] + assert isinstance(wa, Parameter) + assert "Negative derivative" in wa.__doc__ + assert wa.unit is None + assert wa.default == 0.0 + + # on the instance + assert cosmo.wa is cosmo.__dict__["wa"] + assert cosmo.wa == self.cls_kwargs["wa"] + + def test_init_wa(self, cosmo_cls, ba): + """Test initialization for values of ``wa``.""" + # test that it works with units + ba.arguments["wa"] = ba.arguments["wa"] << u.one # ensure units + cosmo = cosmo_cls(*ba.args, **ba.kwargs) + assert cosmo.wa == ba.arguments["wa"] + + # also without units + ba.arguments["wa"] = ba.arguments["wa"].value # strip units + cosmo = cosmo_cls(*ba.args, **ba.kwargs) + assert cosmo.wa == ba.arguments["wa"] + + # must be dimensionless + ba.arguments["wa"] = 10 * u.km + with pytest.raises(TypeError): + cosmo_cls(*ba.args, **ba.kwargs) + + +class Testw0waCDM(FLRWTest, Parameterw0TestMixin, ParameterwaTestMixin): + """Test :class:`astropy.cosmology.w0waCDM`.""" + + def setup_class(self): + """Setup for testing.""" + super().setup_class(self) + self.cls = w0waCDM + self.cls_kwargs.update(w0=-1, wa=-0.5) + + # =============================================================== + # Method & Attribute Tests + + def test_clone_change_param(self, cosmo): + """Test method ``.clone()`` changing a(many) Parameter(s).""" + super().test_clone_change_param(cosmo) + + # `w` params + c = cosmo.clone(w0=0.1, wa=0.2) + assert c.w0 == 0.1 + assert c.wa == 0.2 + for n, v in filter_keys_from_items(c.parameters, ("w0", "wa")): + v_expect = getattr(cosmo, n) + assert_quantity_allclose(v, v_expect, atol=1e-4 * getattr(v, "unit", 1)) + + # @pytest.mark.parametrize("z", valid_zs) # TODO! recompute comparisons below + def test_w(self, cosmo): + """Test :meth:`astropy.cosmology.w0waCDM.w`.""" + # super().test_w(cosmo, z) + + assert u.allclose(cosmo.w(1.0), -1.25) + assert u.allclose( + cosmo.w([0.0, 0.5, 1.0, 1.5, 2.3]), + [-1, -1.16666667, -1.25, -1.3, -1.34848485], + ) + + def test_repr(self, cosmo_cls, cosmo): + """Test method ``.__repr__()``.""" + assert repr(cosmo) == ( + "w0waCDM(name='ABCMeta', H0=, Om0=0.27, " + "Ode0=0.73, Tcmb0=, Neff=3.04, " + "m_nu=, Ob0=0.03, w0=-1.0, wa=-0.5)" + ) + + # =============================================================== + # Usage Tests + + @pytest.mark.skipif(not HAS_SCIPY, reason="scipy is not installed") + @pytest.mark.parametrize( + ("args", "kwargs", "expected"), + [ + ( # no relativistic species + (75.0, 0.3, 0.6), + {"w0": -0.9, "wa": 0.1, "Tcmb0": 0.0}, + [2937.7807638, 4572.59950903, 5611.52821924, 6339.8549956] * u.Mpc, + ), + ( # massless neutrinos + (75.0, 0.25, 0.5), + { + "w0": -0.9, + "wa": 0.1, + "Tcmb0": 3.0, + "Neff": 3, + "m_nu": u.Quantity(0.0, u.eV), + }, + [2907.34722624, 4539.01723198, 5593.51611281, 6342.3228444] * u.Mpc, + ), + ( # massive neutrinos + (75.0, 0.25, 0.5), + { + "w0": -0.9, + "wa": 0.1, + "Tcmb0": 3.0, + "Neff": 3, + "m_nu": u.Quantity(10.0, u.eV), + }, + [2507.18336722, 3633.33231695, 4292.44746919, 4736.35404638] * u.Mpc, + ), + ], + ) + def test_comoving_distance_example(self, cosmo_cls, args, kwargs, expected): + """Test :meth:`astropy.cosmology.LambdaCDM.comoving_distance`. + + These do not come from external codes -- they are just internal checks to make + sure nothing changes if we muck with the distance calculators. + """ + super().test_comoving_distance_example(cosmo_cls, args, kwargs, expected) + + +# ----------------------------------------------------------------------------- + + +class TestFlatw0waCDM(FlatFLRWMixinTest, Testw0waCDM): + """Test :class:`astropy.cosmology.Flatw0waCDM`.""" + + def setup_class(self): + """Setup for testing.""" + super().setup_class(self) + self.cls = Flatw0waCDM + self.cls_kwargs.update(w0=-1, wa=-0.5) + + def test_repr(self, cosmo_cls, cosmo): + """Test method ``.__repr__()``.""" + super().test_repr(cosmo_cls, cosmo) + + assert repr(cosmo) == ( + "Flatw0waCDM(name='ABCMeta', H0=, Om0=0.27, " + "Tcmb0=, Neff=3.04, m_nu=, " + "Ob0=0.03, w0=-1.0, wa=-0.5)" + ) + + # =============================================================== + # Usage Tests + + @pytest.mark.skipif(not HAS_SCIPY, reason="scipy is not installed") + @pytest.mark.parametrize( + ("args", "kwargs", "expected"), + [ + ( # no relativistic species + (75.0, 0.25), + {"w0": -0.95, "wa": 0.15, "Tcmb0": 0.0}, + [3123.29892781, 4956.15204302, 6128.15563818, 6948.26480378] * u.Mpc, + ), + ( # massless neutrinos + (75.0, 0.25), + { + "w0": -0.95, + "wa": 0.15, + "Tcmb0": 3.0, + "Neff": 3, + "m_nu": u.Quantity(0.0, u.eV), + }, + [3122.92671907, 4955.03768936, 6126.25719576, 6945.61856513] * u.Mpc, + ), + ( # massive neutrinos + (75.0, 0.25), + { + "w0": -0.95, + "wa": 0.15, + "Tcmb0": 3.0, + "Neff": 3, + "m_nu": u.Quantity(10.0, u.eV), + }, + [2337.70072701, 3372.13719963, 3988.6571093, 4409.35399673] * u.Mpc, + ), + ], + ) + def test_comoving_distance_example(self, cosmo_cls, args, kwargs, expected): + """Test :meth:`astropy.cosmology.LambdaCDM.comoving_distance`. + + These do not come from external codes -- they are just internal checks to make + sure nothing changes if we muck with the distance calculators. + """ + super().test_comoving_distance_example(cosmo_cls, args, kwargs, expected) + + +############################################################################## +# Comparison to Other Codes + + +@pytest.mark.skipif(not HAS_SCIPY, reason="requires scipy.") +def test_varyde_lumdist_mathematica(): + """Tests a few varying dark energy EOS models against a Mathematica computation.""" + z = np.array([0.2, 0.4, 0.9, 1.2]) + + # w0wa models + cosmo = w0waCDM(H0=70, Om0=0.2, Ode0=0.8, w0=-1.1, wa=0.2, Tcmb0=0.0) + assert u.allclose( + cosmo.luminosity_distance(z), + [1004.0, 2268.62, 6265.76, 9061.84] * u.Mpc, + rtol=1e-4, + ) + assert u.allclose(cosmo.de_density_scale(0.0), 1.0, rtol=1e-5) + assert u.allclose( + cosmo.de_density_scale([0.0, 0.5, 1.5]), + [1.0, 0.9246310669529021, 0.9184087000251957], + ) + + cosmo = w0waCDM(H0=70, Om0=0.3, Ode0=0.7, w0=-0.9, wa=0.0, Tcmb0=0.0) + assert u.allclose( + cosmo.luminosity_distance(z), + [971.667, 2141.67, 5685.96, 8107.41] * u.Mpc, + rtol=1e-4, + ) + + cosmo = w0waCDM(H0=70, Om0=0.3, Ode0=0.7, w0=-0.9, wa=-0.5, Tcmb0=0.0) + assert u.allclose( + cosmo.luminosity_distance(z), + [974.087, 2157.08, 5783.92, 8274.08] * u.Mpc, + rtol=1e-4, + ) + + +############################################################################## +# Miscellaneous +# TODO: these should be better integrated into the new test framework + + +def test_equality(): + """Test equality and equivalence.""" + # mismatched signatures, both directions. + newcosmo = w0waCDM(**Planck18.parameters, Ode0=0.6) + assert newcosmo != Planck18 + assert Planck18 != newcosmo + + +@pytest.mark.skipif(not HAS_SCIPY, reason="test requires scipy") +def test_de_densityscale(): + cosmo = w0waCDM(H0=70, Om0=0.3, Ode0=0.70, w0=-1, wa=-0.5) + + z = np.array([0.1, 0.2, 0.5, 1.5, 2.5]) + assert u.allclose( + cosmo.de_density_scale(z), + [0.9934201, 0.9767912, 0.897450, 0.622236, 0.4458753], + rtol=1e-4, + ) + + assert u.allclose(cosmo.de_density_scale(3), cosmo.de_density_scale(3.0), rtol=1e-7) + assert u.allclose( + cosmo.de_density_scale([1, 2, 3]), + cosmo.de_density_scale([1.0, 2.0, 3.0]), + rtol=1e-7, + ) diff --git a/astropy/cosmology/_src/tests/flrw/test_w0wzcdm.py b/astropy/cosmology/_src/tests/flrw/test_w0wzcdm.py new file mode 100644 index 000000000000..ba6267c2e9d4 --- /dev/null +++ b/astropy/cosmology/_src/tests/flrw/test_w0wzcdm.py @@ -0,0 +1,303 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +"""Testing :mod:`astropy.cosmology.w0wzcdm`.""" + +import numpy as np +import pytest + +import astropy.units as u +from astropy.cosmology import Flatw0wzCDM, w0wzCDM +from astropy.cosmology._src.parameter import Parameter +from astropy.cosmology._src.tests.test_core import ParameterTestMixin, make_valid_zs +from astropy.utils.compat.optional_deps import HAS_SCIPY + +from .conftest import filter_keys_from_items +from .test_base import FlatFLRWMixinTest, FLRWTest +from .test_w0cdm import Parameterw0TestMixin + +############################################################################## +# PARAMETERS + +COMOVING_DISTANCE_EXAMPLE_KWARGS = {"w0": -0.9, "wz": 0.1, "Tcmb0": 0.0} + +valid_zs = make_valid_zs(max_z=400)[-1] + + +############################################################################## +# TESTS +############################################################################## + + +class ParameterwzTestMixin(ParameterTestMixin): + """Tests for `astropy.cosmology.Parameter` wz on a Cosmology. + + wz is a descriptor, which are tested by mixin, here with ``TestFLRW``. + These tests expect dicts ``_cls_args`` and ``cls_kwargs`` which give the + args and kwargs for the cosmology class, respectively. See ``TestFLRW``. + """ + + def test_wz(self, cosmo_cls, cosmo): + """Test Parameter ``wz``.""" + # on the class + wz = cosmo_cls.parameters["wz"] + assert isinstance(wz, Parameter) + assert "Derivative of the dark energy" in wz.__doc__ + assert wz.unit is None + assert wz.default == 0.0 + + # on the instance + assert cosmo.wz is cosmo.__dict__["wz"] + assert cosmo.wz == self.cls_kwargs["wz"] + + def test_init_wz(self, cosmo_cls, ba): + """Test initialization for values of ``wz``.""" + # test that it works with units + ba.arguments["wz"] = ba.arguments["wz"] << u.one # ensure units + cosmo = cosmo_cls(*ba.args, **ba.kwargs) + assert cosmo.wz == ba.arguments["wz"] + + # also without units + ba.arguments["wz"] = ba.arguments["wz"].value # strip units + cosmo = cosmo_cls(*ba.args, **ba.kwargs) + assert cosmo.wz == ba.arguments["wz"] + + # must be dimensionless + ba.arguments["wz"] = 10 * u.km + with pytest.raises(TypeError): + cosmo_cls(*ba.args, **ba.kwargs) + + +class Testw0wzCDM(FLRWTest, Parameterw0TestMixin, ParameterwzTestMixin): + """Test :class:`astropy.cosmology.w0wzCDM`.""" + + def setup_class(self): + """Setup for testing.""" + super().setup_class(self) + self.cls = w0wzCDM + self.cls_kwargs.update(w0=-1, wz=0.5) + + # =============================================================== + # Method & Attribute Tests + + def test_clone_change_param(self, cosmo): + """Test method ``.clone()`` changing a(many) Parameter(s).""" + super().test_clone_change_param(cosmo) + + # `w` params + c = cosmo.clone(w0=0.1, wz=0.2) + assert c.w0 == 0.1 + assert c.wz == 0.2 + for n, v in filter_keys_from_items(c.parameters, ("w0", "wz")): + assert u.allclose(v, getattr(cosmo, n), atol=1e-4 * getattr(v, "unit", 1)) + + # @pytest.mark.parametrize("z", valid_zs) # TODO! recompute comparisons below + def test_w(self, cosmo): + """Test :meth:`astropy.cosmology.w0wzCDM.w`.""" + # super().test_w(cosmo, z) + + assert u.allclose(cosmo.w(1.0), -0.5) + assert u.allclose( + cosmo.w([0.0, 0.5, 1.0, 1.5, 2.3]), [-1.0, -0.75, -0.5, -0.25, 0.15] + ) + + def test_repr(self, cosmo_cls, cosmo): + """Test method ``.__repr__()``.""" + assert repr(cosmo) == ( + "w0wzCDM(name='ABCMeta', H0=, Om0=0.27, " + "Ode0=0.73, Tcmb0=, Neff=3.04, " + "m_nu=, Ob0=0.03, w0=-1.0, wz=0.5)" + ) + + # --------------------------------------------------------------- + + @pytest.mark.parametrize("z", valid_zs) + def test_Otot(self, cosmo, z): + """Test :meth:`astropy.cosmology.w0wzCDM.Otot`. + + This is tested in the base class, but we need to override it here because + this class is quite unstable. + """ + super().test_Otot(cosmo, z) + + def test_Otot_overflow(self, cosmo): + """Test :meth:`astropy.cosmology.w0wzCDM.Otot` for overflow.""" + with ( + np.errstate(invalid="ignore", over="warn"), + pytest.warns(RuntimeWarning, match="overflow encountered in exp"), + ): + cosmo.Otot(1e3) + + # =============================================================== + # I/O Tests + + @pytest.mark.filterwarnings("ignore:overflow encountered") + def test_toformat_model(self, cosmo, to_format, method_name): + """Test cosmology -> astropy.model.""" + super().test_toformat_model(cosmo, to_format, method_name) + + # =============================================================== + # Usage Tests + + @pytest.mark.skipif(not HAS_SCIPY, reason="scipy is not installed") + @pytest.mark.parametrize( + ("args", "kwargs", "expected"), + [ + ( # no relativistic species + (75.0, 0.3, 0.6), + {}, + [2934.20187523, 4559.94636182, 5590.71080419, 6312.66783729] * u.Mpc, + ), + ( # massless neutrinos + (75.0, 0.25, 0.5), + {"Tcmb0": 3.0, "Neff": 3, "m_nu": 0 * u.eV}, + [2904.47062713, 4528.59073707, 5575.95892989, 6318.98689566] * u.Mpc, + ), + ( # massive neutrinos + (75.0, 0.25, 0.5), + {"Tcmb0": 3.0, "Neff": 4, "m_nu": 5 * u.eV}, + [2613.84726408, 3849.66574595, 4585.51172509, 5085.16795412] * u.Mpc, + ), + ], + ) + def test_comoving_distance_example(self, cosmo_cls, args, kwargs, expected): + """Test :meth:`astropy.cosmology.LambdaCDM.comoving_distance`. + + These do not come from external codes -- they are just internal checks to make + sure nothing changes if we muck with the distance calculators. + """ + super().test_comoving_distance_example( + cosmo_cls, args, {**COMOVING_DISTANCE_EXAMPLE_KWARGS, **kwargs}, expected + ) + + @pytest.mark.skipif(not HAS_SCIPY, reason="scipy is not installed") + def test_comoving_distance_mathematica(self, cosmo_cls): + """Test with Mathematica example. + + This test should be updated as the code changes. + + :: + In[1]:= {Om0, w0, wz, H0, c}={0.3,-0.9, 0.2, 70, 299792.458}; + c/H0 NIntegrate[1/Sqrt[Om0*(1+z)^3+(1-Om0)(1+z)^(3(1+w0-wz)) Exp[3 *wz*z]],{z, 0, 0.5}] + Out[1]= 1849.75 + """ + assert u.allclose( + cosmo_cls(H0=70, Om0=0.3, w0=-0.9, wz=0.2, Ode0=0.7).comoving_distance(0.5), + 1849.75 * u.Mpc, + rtol=1e-4, + ) + + +class TestFlatw0wzCDM(FlatFLRWMixinTest, Testw0wzCDM): + """Test :class:`astropy.cosmology.Flatw0wzCDM`.""" + + def setup_class(self): + """Setup for testing.""" + super().setup_class(self) + self.cls = Flatw0wzCDM + + def test_repr(self, cosmo_cls, cosmo): + """Test method ``.__repr__()``.""" + super().test_repr(cosmo_cls, cosmo) + + assert repr(cosmo) == ( + "Flatw0wzCDM(name='ABCMeta', H0=, Om0=0.27, " + "Tcmb0=, Neff=3.04, m_nu=, " + "Ob0=0.03, w0=-1.0, wz=0.5)" + ) + + # --------------------------------------------------------------- + + @pytest.mark.parametrize("z", valid_zs) + def test_Otot(self, cosmo, z): + """Test :meth:`astropy.cosmology.Flatw0wzCDM.Otot`. + + This is tested in the base class, but we need to override it here because + this class is quite unstable. + """ + super().test_Otot(cosmo, z) + + def test_Otot_overflow(self, cosmo): + """Test :meth:`astropy.cosmology.Flatw0wzCDM.Otot` for NOT overflowing.""" + cosmo.Otot(1e5) + + # --------------------------------------------------------------- + + @pytest.mark.skipif(not HAS_SCIPY, reason="scipy is not installed") + @pytest.mark.parametrize( + ("args", "kwargs", "expected"), + [ + ( # no relativistic species + (75.0, 0.3), + {}, + [3004.55645039, 4694.15295565, 5760.90038238, 6504.07869144] * u.Mpc, + ), + ( # massless neutrinos + (75.0, 0.25), + {"Tcmb0": 3.0, "Neff": 3, "m_nu": 0 * u.eV}, + [3086.14574034, 4885.09170925, 6035.4563298, 6840.89215656] * u.Mpc, + ), + ( # massive neutrinos + (75.0, 0.25), + {"Tcmb0": 3.0, "Neff": 4, "m_nu": 5 * u.eV}, + [2510.44035219, 3683.87910326, 4389.97760294, 4873.33577288] * u.Mpc, + ), + ], + ) + def test_comoving_distance_example(self, cosmo_cls, args, kwargs, expected): + """Test :meth:`astropy.cosmology.LambdaCDM.comoving_distance`. + + These do not come from external codes -- they are just internal checks to make + sure nothing changes if we muck with the distance calculators. + """ + super().test_comoving_distance_example( + cosmo_cls, args, {**COMOVING_DISTANCE_EXAMPLE_KWARGS, **kwargs}, expected + ) + + @pytest.mark.skipif(not HAS_SCIPY, reason="scipy is not installed") + def test_comoving_distance_mathematica(self, cosmo_cls): + """Test with Mathematica example. + + This test should be updated as the code changes. + + :: + In[1]:= {Om0, w0, wz, H0, c}={0.3,-0.9, 0.2, 70, 299792.458}; + c/H0 NIntegrate[1/Sqrt[Om0*(1+z)^3+(1-Om0)(1+z)^(3(1+w0-wz)) Exp[3 *wz*z]],{z, 0, 0.5}] + Out[1]= 1849.75 + """ + assert u.allclose( + cosmo_cls(H0=70, Om0=0.3, w0=-0.9, wz=0.2).comoving_distance(0.5), + 1849.75 * u.Mpc, + rtol=1e-4, + ) + + +############################################################################## +# Miscellaneous +# TODO: these should be better integrated into the new test framework + + +@pytest.mark.skipif(not HAS_SCIPY, reason="test requires scipy") +def test_de_densityscale(): + cosmo = w0wzCDM(H0=70, Om0=0.3, Ode0=0.50, w0=-1, wz=0.5) + + z = np.array([0.1, 0.2, 0.5, 1.5, 2.5]) + assert u.allclose( + cosmo.de_density_scale(z), + [1.00705953, 1.02687239, 1.15234885, 2.40022841, 6.49384982], + rtol=1e-4, + ) + + assert u.allclose(cosmo.de_density_scale(3), cosmo.de_density_scale(3.0), rtol=1e-7) + assert u.allclose( + cosmo.de_density_scale([1, 2, 3]), + cosmo.de_density_scale([1.0, 2.0, 3.0]), + rtol=1e-7, + ) + + # Flat tests + cosmo = w0wzCDM(H0=70, Om0=0.3, Ode0=0.7, w0=-1, wz=0.5) + flatcosmo = Flatw0wzCDM(H0=70, Om0=0.3, w0=-1, wz=0.5) + + assert u.allclose( + cosmo.de_density_scale(z), flatcosmo.de_density_scale(z), rtol=1e-4 + ) diff --git a/astropy/cosmology/_src/tests/flrw/test_wpwazpcdm.py b/astropy/cosmology/_src/tests/flrw/test_wpwazpcdm.py new file mode 100644 index 000000000000..125e4ccaf183 --- /dev/null +++ b/astropy/cosmology/_src/tests/flrw/test_wpwazpcdm.py @@ -0,0 +1,310 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +"""Testing :mod:`astropy.cosmology.wpwazpcdm`.""" + +import numpy as np +import pytest + +import astropy.cosmology.units as cu +import astropy.units as u +from astropy.cosmology import FlatwpwaCDM, wpwaCDM +from astropy.cosmology._src.parameter import Parameter +from astropy.cosmology._src.tests.test_core import ParameterTestMixin +from astropy.tests.helper import assert_quantity_allclose +from astropy.utils.compat.optional_deps import HAS_SCIPY + +from .conftest import filter_keys_from_items +from .test_base import FlatFLRWMixinTest, FLRWTest +from .test_w0wacdm import ParameterwaTestMixin + +############################################################################## +# PARAMETERS + +COMOVING_DISTANCE_EXAMPLE_KWARGS = {"wp": -0.9, "zp": 0.5, "wa": 0.1, "Tcmb0": 0.0} + + +############################################################################## +# TESTS +############################################################################## + + +class ParameterwpTestMixin(ParameterTestMixin): + """Tests for `astropy.cosmology.Parameter` wp on a Cosmology. + + wp is a descriptor, which are tested by mixin, here with ``TestFLRW``. + These tests expect dicts ``_cls_args`` and ``cls_kwargs`` which give the + args and kwargs for the cosmology class, respectively. See ``TestFLRW``. + """ + + def test_wp(self, cosmo_cls, cosmo): + """Test Parameter ``wp``.""" + # on the class + wp = cosmo_cls.parameters["wp"] + assert isinstance(wp, Parameter) + assert "at the pivot" in wp.__doc__ + assert wp.unit is None + assert wp.default == -1.0 + + # on the instance + assert cosmo.wp is cosmo.__dict__["wp"] + assert cosmo.wp == self.cls_kwargs["wp"] + + def test_init_wp(self, cosmo_cls, ba): + """Test initialization for values of ``wp``.""" + # test that it works with units + ba.arguments["wp"] = ba.arguments["wp"] << u.one # ensure units + cosmo = cosmo_cls(*ba.args, **ba.kwargs) + assert cosmo.wp == ba.arguments["wp"] + + # also without units + ba.arguments["wp"] = ba.arguments["wp"].value # strip units + cosmo = cosmo_cls(*ba.args, **ba.kwargs) + assert cosmo.wp == ba.arguments["wp"] + + # must be dimensionless + ba.arguments["wp"] = 10 * u.km + with pytest.raises(TypeError): + cosmo_cls(*ba.args, **ba.kwargs) + + +class ParameterzpTestMixin(ParameterTestMixin): + """Tests for `astropy.cosmology.Parameter` zp on a Cosmology. + + zp is a descriptor, which are tested by mixin, here with ``TestFLRW``. + These tests expect dicts ``_cls_args`` and ``cls_kwargs`` which give the + args and kwargs for the cosmology class, respectively. See ``TestFLRW``. + """ + + def test_zp(self, cosmo_cls, cosmo): + """Test Parameter ``zp``.""" + # on the class + zp = cosmo_cls.parameters["zp"] + assert isinstance(zp, Parameter) + assert "pivot redshift" in zp.__doc__ + assert zp.unit == cu.redshift + assert zp.default == 0.0 + + # on the instance + assert cosmo.zp is cosmo.__dict__["zp"] + assert cosmo.zp == self.cls_kwargs["zp"] << cu.redshift + + def test_init_zp(self, cosmo_cls, ba): + """Test initialization for values of ``zp``.""" + # test that it works with units + ba.arguments["zp"] = ba.arguments["zp"] << u.one # ensure units + cosmo = cosmo_cls(*ba.args, **ba.kwargs) + assert cosmo.zp == ba.arguments["zp"] + + # also without units + ba.arguments["zp"] = ba.arguments["zp"].value # strip units + cosmo = cosmo_cls(*ba.args, **ba.kwargs) + assert cosmo.zp.value == ba.arguments["zp"] + + # must be dimensionless + ba.arguments["zp"] = 10 * u.km + with pytest.raises(u.UnitConversionError): + cosmo_cls(*ba.args, **ba.kwargs) + + +class TestwpwaCDM( + FLRWTest, ParameterwpTestMixin, ParameterwaTestMixin, ParameterzpTestMixin +): + """Test :class:`astropy.cosmology.wpwaCDM`.""" + + def setup_class(self): + """Setup for testing.""" + super().setup_class(self) + self.cls = wpwaCDM + self.cls_kwargs.update(wp=-0.9, wa=0.2, zp=0.5) + + # =============================================================== + # Method & Attribute Tests + + def test_clone_change_param(self, cosmo): + """Test method ``.clone()`` changing a(many) Parameter(s).""" + super().test_clone_change_param(cosmo) + + # `w` params + c = cosmo.clone(wp=0.1, wa=0.2, zp=14) + assert c.wp == 0.1 + assert c.wa == 0.2 + assert c.zp == 14 + for n, v in filter_keys_from_items(c.parameters, ("wp", "wa", "zp")): + v_expect = getattr(cosmo, n) + assert_quantity_allclose(v, v_expect, atol=1e-4 * getattr(v, "unit", 1)) + + # @pytest.mark.parametrize("z", valid_zs) # TODO! recompute comparisons below + def test_w(self, cosmo): + """Test :meth:`astropy.cosmology.wpwaCDM.w`.""" + # super().test_w(cosmo, z) + + assert u.allclose(cosmo.w(0.5), -0.9) + assert u.allclose( + cosmo.w([0.1, 0.2, 0.5, 1.5, 2.5, 11.5]), + [-0.94848485, -0.93333333, -0.9, -0.84666667, -0.82380952, -0.78266667], + ) + + def test_repr(self, cosmo_cls, cosmo): + """Test method ``.__repr__()``.""" + assert repr(cosmo) == ( + "wpwaCDM(name='ABCMeta', H0=, Om0=0.27," + " Ode0=0.73, Tcmb0=, Neff=3.04," + " m_nu=, Ob0=0.03, wp=-0.9, wa=0.2," + " zp=)" + ) + + # =============================================================== + # Usage Tests + + @pytest.mark.skipif(not HAS_SCIPY, reason="scipy required for this test.") + @pytest.mark.parametrize( + ("args", "kwargs", "expected"), + [ + ( # no relativistic species + (75.0, 0.3, 0.6), + {}, + [2954.68975298, 4599.83254834, 5643.04013201, 6373.36147627] * u.Mpc, + ), + ( # massless neutrinos + (75.0, 0.25, 0.5), + {"zp": 0.4, "Tcmb0": 3.0, "Neff": 3, "m_nu": 0 * u.eV}, + [2919.00656215, 4558.0218123, 5615.73412391, 6366.10224229] * u.Mpc, + ), + ( # massive neutrinos + (75.0, 0.25, 0.5), + {"zp": 1.0, "Tcmb0": 3.0, "Neff": 4, "m_nu": 5 * u.eV}, + [2629.48489827, 3874.13392319, 4614.31562397, 5116.51184842] * u.Mpc, + ), + # FLAT: these match the tests in TestFlatwpwaCDM, except Ode0 is set manually. + ( # no relativistic species + (75.0, 0.3, 0.7), + {}, + [3030.70481348, 4745.82435272, 5828.73710847, 6582.60454542] * u.Mpc, + ), + ( # massless neutrinos + (75.0, 0.25, 0.75), + {"zp": 0.4, "Tcmb0": 3.0, "Neff": 3, "m_nu": 0 * u.eV}, + [3113.62199365, 4943.28425668, 6114.45491003, 6934.07461377] * u.Mpc, + ), + ( # massive neutrinos + (75.0, 0.25, 0.2458794183661), # to make Ok0 = 0, Otot0 = 1 + {"zp": 1.0, "Tcmb0": 3.0, "Neff": 4, "m_nu": 5 * u.eV}, + [2517.08634022, 3694.21111754, 4402.17802962, 4886.65787948] * u.Mpc, + ), + ], + ) + def test_comoving_distance_example(self, cosmo_cls, args, kwargs, expected): + """Test :meth:`astropy.cosmology.LambdaCDM.comoving_distance`. + + These do not come from external codes -- they are just internal checks to make + sure nothing changes if we muck with the distance calculators. + """ + super().test_comoving_distance_example( + cosmo_cls, args, {**COMOVING_DISTANCE_EXAMPLE_KWARGS, **kwargs}, expected + ) + + +class TestFlatwpwaCDM(FlatFLRWMixinTest, TestwpwaCDM): + """Test :class:`astropy.cosmology.FlatwpwaCDM`.""" + + def setup_class(self): + """Setup for testing.""" + super().setup_class(self) + self.cls = FlatwpwaCDM + + def test_repr(self, cosmo_cls, cosmo): + """Test method ``.__repr__()``.""" + super().test_repr(cosmo_cls, cosmo) + + assert repr(cosmo) == ( + "FlatwpwaCDM(name='ABCMeta', H0=, Om0=0.27," + " Tcmb0=, Neff=3.04, m_nu=," + " Ob0=0.03, wp=-0.9, wa=0.2, zp=)" + ) + + @pytest.mark.skipif(not HAS_SCIPY, reason="scipy required for this test.") + @pytest.mark.parametrize( + ("args", "kwargs", "expected"), + [ + ( # no relativistic species + (75.0, 0.3), + {}, + [3030.70481348, 4745.82435272, 5828.73710847, 6582.60454542] * u.Mpc, + ), + ( # massless neutrinos + (75.0, 0.25), + {"zp": 0.4, "wa": 0.1, "Tcmb0": 3.0, "Neff": 3, "m_nu": 0.0 * u.eV}, + [3113.62199365, 4943.28425668, 6114.45491003, 6934.07461377] * u.Mpc, + ), + ( # massive neutrinos + (75.0, 0.25), + {"zp": 1.0, "Tcmb0": 3.0, "Neff": 4, "m_nu": 5 * u.eV}, + [2517.08634022, 3694.21111754, 4402.17802962, 4886.65787948] * u.Mpc, + ), + ], + ) + def test_comoving_distance_example(self, cosmo_cls, args, kwargs, expected): + """Test :meth:`astropy.cosmology.LambdaCDM.comoving_distance`. + + These do not come from external codes -- they are just internal checks to make + sure nothing changes if we muck with the distance calculators. + """ + super().test_comoving_distance_example( + cosmo_cls, args, {**COMOVING_DISTANCE_EXAMPLE_KWARGS, **kwargs}, expected + ) + + +############################################################################### +# Comparison to Other Codes + + +@pytest.mark.skipif(not HAS_SCIPY, reason="requires scipy.") +def test_varyde_lumdist_mathematica(): + """Tests a few varying dark energy EOS models against a Mathematica computation.""" + z = np.array([0.2, 0.4, 0.9, 1.2]) + + # wpwa models + cosmo = wpwaCDM(H0=70, Om0=0.2, Ode0=0.8, wp=-1.1, wa=0.2, zp=0.5, Tcmb0=0.0) + assert u.allclose( + cosmo.luminosity_distance(z), + [1010.81, 2294.45, 6369.45, 9218.95] * u.Mpc, + rtol=1e-4, + ) + + cosmo = wpwaCDM(H0=70, Om0=0.2, Ode0=0.8, wp=-1.1, wa=0.2, zp=0.9, Tcmb0=0.0) + assert u.allclose( + cosmo.luminosity_distance(z), + [1013.68, 2305.3, 6412.37, 9283.33] * u.Mpc, + rtol=1e-4, + ) + + +############################################################################## +# Miscellaneous +# TODO: these should be better integrated into the new test framework + + +@pytest.mark.skipif(not HAS_SCIPY, reason="test requires scipy") +def test_de_densityscale(): + cosmo = wpwaCDM(H0=70, Om0=0.3, Ode0=0.70, wp=-0.9, wa=0.2, zp=0.5) + + z = np.array([0.1, 0.2, 0.5, 1.5, 2.5]) + assert u.allclose( + cosmo.de_density_scale(z), + [1.012246048, 1.0280102, 1.087439, 1.324988, 1.565746], + rtol=1e-4, + ) + + assert u.allclose(cosmo.de_density_scale(3), cosmo.de_density_scale(3.0), rtol=1e-7) + assert u.allclose( + cosmo.de_density_scale([1, 2, 3]), + cosmo.de_density_scale([1.0, 2.0, 3.0]), + rtol=1e-7, + ) + + # Flat tests + cosmo = wpwaCDM(H0=70, Om0=0.3, Ode0=0.70, wp=-0.9, wa=0.2, zp=0.5) + flatcosmo = FlatwpwaCDM(H0=70, Om0=0.3, wp=-0.9, wa=0.2, zp=0.5) + assert u.allclose( + cosmo.de_density_scale(z), flatcosmo.de_density_scale(z), rtol=1e-7 + ) diff --git a/astropy/cosmology/_src/tests/funcs/__init__.py b/astropy/cosmology/_src/tests/funcs/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/astropy/cosmology/_src/tests/funcs/test_comparison.py b/astropy/cosmology/_src/tests/funcs/test_comparison.py new file mode 100644 index 000000000000..8ecb0cd55f2a --- /dev/null +++ b/astropy/cosmology/_src/tests/funcs/test_comparison.py @@ -0,0 +1,346 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +"""Tests for :mod:`astropy.cosmology.comparison`""" + +import re + +import numpy as np +import pytest + +from astropy.cosmology import Cosmology, FlatCosmologyMixin, Planck18, cosmology_equal +from astropy.cosmology._src.funcs.comparison import ( + _CANT_BROADCAST, + _cosmology_not_equal, + _CosmologyWrapper, + _parse_format, + _parse_formats, +) +from astropy.cosmology._src.tests.io.base import ToFromTestMixinBase +from astropy.cosmology.io import convert_registry + + +class ComparisonFunctionTestBase(ToFromTestMixinBase): + """Tests for cosmology comparison functions. + + This class inherits from + `astropy.cosmology._src.tests.io.base.ToFromTestMixinBase` because the cosmology + comparison functions all have a kwarg ``format`` that allow the arguments to + be converted to a |Cosmology| using the ``to_format`` architecture. + + This class will not be directly called by :mod:`pytest` since its name does + not begin with ``Test``. To activate the contained tests this class must be + inherited in a subclass. + """ + + @pytest.fixture(scope="class") + @classmethod + def cosmo(cls): + return Planck18 + + @pytest.fixture(scope="class") + @classmethod + def cosmo_eqvxflat(cls, cosmo): + if isinstance(cosmo, FlatCosmologyMixin): + return cosmo.nonflat + + pytest.skip( + "cosmology is not flat, so does not have an equivalent non-flat cosmology." + ) + + @pytest.fixture( + scope="class", + params=sorted( + {k for k, _ in convert_registry._readers.keys()} - {"astropy.cosmology"} + ), + ) + @classmethod + def format(cls, request): + return request.param + + @pytest.fixture(scope="class") + @classmethod + def xfail_cant_autoidentify(cls, format): + """`pytest.fixture` form of method ``can_autoidentify`.""" + if not cls.can_autodentify(format): + pytest.xfail("cannot autoidentify") + + @pytest.fixture(scope="class") + @classmethod + def converted(cls, to_format, format): + if format == "astropy.model": # special case Model + return to_format(format, method="comoving_distance") + return to_format(format) + + @pytest.fixture(scope="class") + @classmethod + def pert_cosmo(cls, cosmo): + # change one parameter + p, v = next(iter(cosmo.parameters.items())) + return cosmo.clone( + **{p: v * 1.0001 if v != 0 else 0.001 * getattr(v, "unit", 1)} + ) + + @pytest.fixture(scope="class") + @classmethod + def pert_cosmo_eqvxflat(cls, pert_cosmo): + if isinstance(pert_cosmo, FlatCosmologyMixin): + return pert_cosmo.nonflat + + pytest.skip( + "cosmology is not flat, so does not have an equivalent non-flat cosmology." + ) + + @pytest.fixture(scope="class") + @classmethod + def pert_converted(cls, pert_cosmo, format): + if format == "astropy.model": # special case Model + return pert_cosmo.to_format(format, method="comoving_distance") + return pert_cosmo.to_format(format) + + +class Test_parse_format(ComparisonFunctionTestBase): + """Test functions ``_parse_format``.""" + + @pytest.fixture(scope="class") + @classmethod + def converted(cls, to_format, format): + if format == "astropy.model": # special case Model + return to_format(format, method="comoving_distance") + + converted = to_format(format) + + # Some raise a segfault! TODO: figure out why + if isinstance(converted, _CANT_BROADCAST): + converted = _CosmologyWrapper(converted) + + return converted + + # ======================================================================== + + def test_shortcut(self, cosmo): + """Test the already-a-cosmology shortcut.""" + # A Cosmology + for fmt in (None, True, False, "astropy.cosmology"): + assert _parse_format(cosmo, fmt) is cosmo, f"{fmt} failed" + + # A Cosmology, but improperly formatted + # see ``test_parse_format_error_wrong_format``. + + def test_convert(self, converted, format, cosmo): + """Test converting a cosmology-like object""" + out = _parse_format(converted, format) + + assert isinstance(out, Cosmology) + assert out == cosmo + + def test_parse_format_error_wrong_format(self, cosmo): + """ + Test ``_parse_format`` errors when given a Cosmology object and format + is not compatible. + """ + with pytest.raises( + ValueError, match=re.escape("for parsing a Cosmology, 'format'") + ): + _parse_format(cosmo, "mapping") + + def test_parse_format_error_noncosmology_cant_convert(self): + """ + Test ``_parse_format`` errors when given a non-Cosmology object + and format is `False`. + """ + notacosmo = object() + + with pytest.raises(TypeError, match=re.escape("if 'format' is False")): + _parse_format(notacosmo, False) + + def test_parse_format_vectorized(self, cosmo, format, converted): + # vectorized on cosmos + out = _parse_format([cosmo, cosmo], None) + assert len(out) == 2 + assert np.all(out == cosmo) + + # vectorized on formats + out = _parse_format(cosmo, [None, None]) + assert len(out) == 2 + assert np.all(out == cosmo) + + # more complex broadcast + out = _parse_format( + [[cosmo, converted], [converted, cosmo]], [[None, format], [format, None]] + ) + assert out.shape == (2, 2) + assert np.all(out == cosmo) + + def test_parse_formats_vectorized(self, cosmo): + # vectorized on cosmos + out = _parse_formats(cosmo, cosmo, format=None) + assert len(out) == 2 + assert np.all(out == cosmo) + + # does NOT vectorize on formats + with pytest.raises(ValueError, match="operands could not be broadcast"): + _parse_formats(cosmo, format=[None, None]) + + +class Test_cosmology_equal(ComparisonFunctionTestBase): + """Test :func:`astropy.cosmology.comparison.cosmology_equal`""" + + def test_cosmology_equal_simple(self, cosmo, pert_cosmo): + # equality + assert cosmology_equal(cosmo, cosmo) is True + + # not equal to perturbed cosmology + assert cosmology_equal(cosmo, pert_cosmo) is False + + def test_cosmology_equal_equivalent( + self, cosmo, cosmo_eqvxflat, pert_cosmo, pert_cosmo_eqvxflat + ): + # now need to check equivalent, but not equal, cosmologies. + assert cosmology_equal(cosmo, cosmo_eqvxflat, allow_equivalent=True) is True + assert cosmology_equal(cosmo, cosmo_eqvxflat, allow_equivalent=False) is False + + assert ( + cosmology_equal(pert_cosmo, pert_cosmo_eqvxflat, allow_equivalent=True) + is True + ) + assert ( + cosmology_equal(pert_cosmo, pert_cosmo_eqvxflat, allow_equivalent=False) + is False + ) + + def test_cosmology_equal_too_many_cosmo(self, cosmo): + with pytest.raises( + TypeError, match="cosmology_equal takes 2 positional arguments" + ): + cosmology_equal(cosmo, cosmo, cosmo) + + def test_cosmology_equal_format_error(self, cosmo, converted): + # Not converting `converted` + with pytest.raises(TypeError): + cosmology_equal(cosmo, converted) + + with pytest.raises(TypeError): + cosmology_equal(cosmo, converted, format=False) + + def test_cosmology_equal_format_auto( + self, cosmo, converted, xfail_cant_autoidentify + ): + # These tests only run if the format can autoidentify. + assert cosmology_equal(cosmo, converted, format=None) is True + assert cosmology_equal(cosmo, converted, format=True) is True + + def test_cosmology_equal_format_specify( + self, cosmo, format, converted, pert_converted + ): + # equality + assert cosmology_equal(cosmo, converted, format=[None, format]) is True + assert cosmology_equal(converted, cosmo, format=[format, None]) is True + + # non-equality + assert cosmology_equal(cosmo, pert_converted, format=[None, format]) is False + + def test_cosmology_equal_equivalent_format_specify( + self, cosmo, format, converted, cosmo_eqvxflat + ): + # specifying the format + assert ( + cosmology_equal( + cosmo_eqvxflat, converted, format=[None, format], allow_equivalent=True + ) + is True + ) + assert ( + cosmology_equal( + converted, cosmo_eqvxflat, format=[format, None], allow_equivalent=True + ) + is True + ) + + +class Test_cosmology_not_equal(ComparisonFunctionTestBase): + """Test :func:`astropy.cosmology.comparison._cosmology_not_equal`""" + + def test_cosmology_not_equal_simple(self, cosmo, pert_cosmo): + # equality + assert _cosmology_not_equal(cosmo, cosmo) is False + + # not equal to perturbed cosmology + assert _cosmology_not_equal(cosmo, pert_cosmo) is True + + def test_cosmology_not_equal_too_many_cosmo(self, cosmo): + with pytest.raises(TypeError, match="_cosmology_not_equal takes 2 positional"): + _cosmology_not_equal(cosmo, cosmo, cosmo) + + def test_cosmology_not_equal_equivalent( + self, cosmo, cosmo_eqvxflat, pert_cosmo, pert_cosmo_eqvxflat + ): + # now need to check equivalent, but not equal, cosmologies. + assert ( + _cosmology_not_equal(cosmo, cosmo_eqvxflat, allow_equivalent=False) is True + ) + assert ( + _cosmology_not_equal(cosmo, cosmo_eqvxflat, allow_equivalent=True) is False + ) + + assert ( + _cosmology_not_equal( + pert_cosmo, pert_cosmo_eqvxflat, allow_equivalent=False + ) + is True + ) + assert ( + _cosmology_not_equal(pert_cosmo, pert_cosmo_eqvxflat, allow_equivalent=True) + is False + ) + + def test_cosmology_not_equal_format_error(self, cosmo, converted): + # Not converting `converted` + with pytest.raises(TypeError): + _cosmology_not_equal(cosmo, converted) + + with pytest.raises(TypeError): + _cosmology_not_equal(cosmo, converted, format=False) + + def test_cosmology_not_equal_format_auto( + self, cosmo, pert_converted, xfail_cant_autoidentify + ): + assert _cosmology_not_equal(cosmo, pert_converted, format=None) is True + assert _cosmology_not_equal(cosmo, pert_converted, format=True) is True + + def test_cosmology_not_equal_format_specify( + self, cosmo, format, converted, pert_converted + ): + # specifying the format + assert ( + _cosmology_not_equal(cosmo, pert_converted, format=[None, format]) is True + ) + assert ( + _cosmology_not_equal(pert_converted, cosmo, format=[format, None]) is True + ) + + # equality + assert _cosmology_not_equal(cosmo, converted, format=[None, format]) is False + + def test_cosmology_not_equal_equivalent_format_specify( + self, cosmo, format, converted, cosmo_eqvxflat + ): + # specifying the format + assert ( + _cosmology_not_equal( + cosmo_eqvxflat, converted, format=[None, format], allow_equivalent=False + ) + is True + ) + assert ( + _cosmology_not_equal( + cosmo_eqvxflat, converted, format=[None, format], allow_equivalent=True + ) + is False + ) + + assert ( + _cosmology_not_equal( + converted, cosmo_eqvxflat, format=[format, None], allow_equivalent=True + ) + is False + ) diff --git a/astropy/cosmology/_src/tests/funcs/test_funcs.py b/astropy/cosmology/_src/tests/funcs/test_funcs.py new file mode 100644 index 000000000000..98578b1d84b8 --- /dev/null +++ b/astropy/cosmology/_src/tests/funcs/test_funcs.py @@ -0,0 +1,426 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +import inspect +import sys +from contextlib import nullcontext +from io import StringIO + +import numpy as np +import pytest + +from astropy import units as u +from astropy.cosmology import ( + WMAP1, + WMAP3, + WMAP5, + WMAP7, + WMAP9, + CosmologyError, + FlatLambdaCDM, + Flatw0waCDM, + FlatwCDM, + LambdaCDM, + Planck13, + Planck15, + Planck18, + w0waCDM, + w0wzCDM, + wCDM, + wpwaCDM, + z_at_value, +) +from astropy.units import allclose +from astropy.utils.compat.optional_deps import HAS_SCIPY +from astropy.utils.exceptions import AstropyUserWarning + + +@pytest.mark.skipif(not HAS_SCIPY, reason="test requires scipy") +def test_z_at_value_scalar(): + # These are tests of expected values, and hence have less precision + # than the roundtrip tests below (test_z_at_value_roundtrip); + # here we have to worry about the cosmological calculations + # giving slightly different values on different architectures, + # there we are checking internal consistency on the same architecture + # and so can be more demanding + cosmo = Planck13 + assert allclose(z_at_value(cosmo.age, 2 * u.Gyr), 3.19812268, rtol=1e-6) + assert allclose(z_at_value(cosmo.lookback_time, 7 * u.Gyr), 0.795198375, rtol=1e-6) + assert allclose(z_at_value(cosmo.distmod, 46 * u.mag), 1.991389168, rtol=1e-6) + assert allclose( + z_at_value(cosmo.luminosity_distance, 1e4 * u.Mpc), 1.36857907, rtol=1e-6 + ) + assert allclose( + z_at_value(cosmo.luminosity_distance, 26.037193804 * u.Gpc, ztol=1e-10), + 3, + rtol=1e-9, + ) + assert allclose( + z_at_value(cosmo.angular_diameter_distance, 1500 * u.Mpc, zmax=2), + 0.681277696, + rtol=1e-6, + ) + assert allclose( + z_at_value(cosmo.angular_diameter_distance, 1500 * u.Mpc, zmin=2.5), + 3.7914908, + rtol=1e-6, + ) + + # test behavior when the solution is outside z limits (should + # raise a CosmologyError) + with ( + pytest.raises(CosmologyError), + pytest.warns(AstropyUserWarning, match="fval is not bracketed"), + ): + z_at_value(cosmo.angular_diameter_distance, 1500 * u.Mpc, zmax=0.5) + + with ( + pytest.raises(CosmologyError), + pytest.warns(AstropyUserWarning, match="fval is not bracketed"), + np.errstate(over="ignore"), + ): + z_at_value(cosmo.angular_diameter_distance, 1500 * u.Mpc, zmin=4.0) + + +@pytest.mark.skipif(not HAS_SCIPY, reason="test requires scipy") +class Test_ZatValue: + def setup_class(self): + self.cosmo = Planck13 + + def test_broadcast_arguments(self): + """Test broadcast of arguments.""" + # broadcasting main argument + assert allclose( + z_at_value(self.cosmo.age, [2, 7] * u.Gyr), + [3.1981206134773115, 0.7562044333305182], + rtol=1e-6, + ) + + # basic broadcast of secondary arguments + assert allclose( + z_at_value( + self.cosmo.angular_diameter_distance, + 1500 * u.Mpc, + zmin=[0, 2.5], + zmax=[2, 4], + ), + [0.681277696, 3.7914908], + rtol=1e-6, + ) + + # more interesting broadcast + assert allclose( + z_at_value( + self.cosmo.angular_diameter_distance, + 1500 * u.Mpc, + zmin=[[0, 2.5]], + zmax=[2, 4], + ), + [[0.681277696, 3.7914908]], + rtol=1e-6, + ) + + def test_broadcast_bracket(self): + """`bracket` has special requirements.""" + # start with an easy one + assert allclose( + z_at_value(self.cosmo.age, 2 * u.Gyr, bracket=None), + 3.1981206134773115, + rtol=1e-6, + ) + + # now actually have a bracket + assert allclose( + z_at_value(self.cosmo.age, 2 * u.Gyr, bracket=[0, 4]), + 3.1981206134773115, + rtol=1e-6, + ) + + # now a bad length + with pytest.raises(ValueError, match="sequence"): + z_at_value(self.cosmo.age, 2 * u.Gyr, bracket=[0, 4, 4, 5]) + + # now the wrong dtype : an ndarray, but not an object array + with pytest.raises(TypeError, match="dtype"): + z_at_value(self.cosmo.age, 2 * u.Gyr, bracket=np.array([0, 4])) + + # now an object array of brackets + bracket = np.array([[0, 4], [0, 3, 4]], dtype=object) + assert allclose( + z_at_value(self.cosmo.age, 2 * u.Gyr, bracket=bracket), + [3.1981206134773115, 3.1981206134773115], + rtol=1e-6, + ) + + def test_bad_broadcast(self): + """Shapes mismatch as expected""" + with pytest.raises(ValueError, match="broadcast"): + z_at_value( + self.cosmo.angular_diameter_distance, + 1500 * u.Mpc, + zmin=[0, 2.5, 0.1], + zmax=[2, 4], + ) + + def test_scalar_input_to_output(self): + """Test scalar input returns a scalar.""" + z = z_at_value( + self.cosmo.angular_diameter_distance, 1500 * u.Mpc, zmin=0, zmax=2 + ) + assert isinstance(z, u.Quantity) + assert z.dtype == np.float64 + assert z.shape == () + + +@pytest.mark.skipif(not HAS_SCIPY, reason="test requires scipy") +def test_z_at_value_verbose(monkeypatch): + cosmo = Planck13 + + # Test the "verbose" flag. Since this uses "print", need to mod stdout + mock_stdout = StringIO() + monkeypatch.setattr(sys, "stdout", mock_stdout) + + resx = z_at_value(cosmo.age, 2 * u.Gyr, verbose=True) + assert str(resx.value) in mock_stdout.getvalue() # test "verbose" prints res + + +@pytest.mark.skipif(not HAS_SCIPY, reason="test requires scipy") +@pytest.mark.parametrize("method", ["Brent", "Golden", "Bounded"]) +def test_z_at_value_bracketed(method): + """ + Test 2 solutions for angular diameter distance by not constraining zmin, zmax, + but setting `bracket` on the appropriate side of the turning point z. + Setting zmin / zmax should override `bracket`. + """ + cosmo = Planck13 + + if method == "Bounded": + with pytest.warns(AstropyUserWarning, match="fval is not bracketed"): + z = z_at_value(cosmo.angular_diameter_distance, 1500 * u.Mpc, method=method) + if z > 1.6: + z = 3.7914908 + bracket = (0.9, 1.5) + else: + z = 0.6812777 + bracket = (1.6, 2.0) + with ( + pytest.warns(UserWarning, match="Option 'bracket' is ignored"), + pytest.warns(AstropyUserWarning, match="fval is not bracketed"), + ): + assert allclose( + z_at_value( + cosmo.angular_diameter_distance, + 1500 * u.Mpc, + method=method, + bracket=bracket, + ), + z, + rtol=1e-6, + ) + else: + assert allclose( + z_at_value( + cosmo.angular_diameter_distance, + 1500 * u.Mpc, + method=method, + bracket=(0.3, 1.0), + ), + 0.6812777, + rtol=1e-6, + ) + assert allclose( + z_at_value( + cosmo.angular_diameter_distance, + 1500 * u.Mpc, + method=method, + bracket=(2.0, 4.0), + ), + 3.7914908, + rtol=1e-6, + ) + assert allclose( + z_at_value( + cosmo.angular_diameter_distance, + 1500 * u.Mpc, + method=method, + bracket=(0.1, 1.5), + ), + 0.6812777, + rtol=1e-6, + ) + assert allclose( + z_at_value( + cosmo.angular_diameter_distance, + 1500 * u.Mpc, + method=method, + bracket=(0.1, 1.0, 2.0), + ), + 0.6812777, + rtol=1e-6, + ) + with pytest.warns(AstropyUserWarning, match=r"fval is not bracketed"): + assert allclose( + z_at_value( + cosmo.angular_diameter_distance, + 1500 * u.Mpc, + method=method, + bracket=(0.9, 1.5), + ), + 0.6812777, + rtol=1e-6, + ) + assert allclose( + z_at_value( + cosmo.angular_diameter_distance, + 1500 * u.Mpc, + method=method, + bracket=(1.6, 2.0), + ), + 3.7914908, + rtol=1e-6, + ) + assert allclose( + z_at_value( + cosmo.angular_diameter_distance, + 1500 * u.Mpc, + method=method, + bracket=(1.6, 2.0), + zmax=1.6, + ), + 0.6812777, + rtol=1e-6, + ) + assert allclose( + z_at_value( + cosmo.angular_diameter_distance, + 1500 * u.Mpc, + method=method, + bracket=(0.9, 1.5), + zmin=1.5, + ), + 3.7914908, + rtol=1e-6, + ) + + if method == "Bounded": + ctx_bracket = pytest.warns( + UserWarning, match="Option 'bracket' is ignored by method Bounded" + ) + else: + ctx_bracket = nullcontext() + + with ( + pytest.raises(CosmologyError), + pytest.warns(AstropyUserWarning, match="fval is not bracketed"), + ctx_bracket, + ): + z_at_value( + cosmo.angular_diameter_distance, + 1500 * u.Mpc, + method=method, + bracket=(3.9, 5.0), + zmin=4.0, + ) + + +@pytest.mark.skipif(not HAS_SCIPY, reason="test requires scipy") +@pytest.mark.parametrize("method", ["Brent", "Golden", "Bounded"]) +def test_z_at_value_unconverged(method): + """ + Test warnings on non-converged solution when setting `maxfun` to too small iteration number - + only 'Bounded' returns status value and specific message. + """ + cosmo = Planck18 + ztol = {"Brent": [1e-4, 1e-4], "Golden": [1e-3, 1e-2], "Bounded": [1e-3, 1e-1]} + + if method == "Bounded": + ctx = pytest.warns( + AstropyUserWarning, + match="Solver returned 1: Maximum number of function calls reached", + ) + else: + ctx = pytest.warns(AstropyUserWarning, match="Solver returned None") + + with ctx: + z0 = z_at_value( + cosmo.angular_diameter_distance, 1 * u.Gpc, zmax=2, maxfun=13, method=method + ) + with ctx: + z1 = z_at_value( + cosmo.angular_diameter_distance, 1 * u.Gpc, zmin=2, maxfun=13, method=method + ) + + assert allclose(z0, 0.32442, rtol=ztol[method][0]) + assert allclose(z1, 8.18551, rtol=ztol[method][1]) + + +@pytest.mark.skipif(not HAS_SCIPY, reason="test requires scipy") +@pytest.mark.parametrize( + "cosmo", + [ + Planck13, + Planck15, + Planck18, + WMAP1, + WMAP3, + WMAP5, + WMAP7, + WMAP9, + LambdaCDM, + FlatLambdaCDM, + wpwaCDM, + w0wzCDM, + wCDM, + FlatwCDM, + w0waCDM, + Flatw0waCDM, + ], +) +def test_z_at_value_roundtrip(cosmo): + """ + Calculate values from a known redshift, and then check that + z_at_value returns the right answer. + """ + z = 0.5 + + # Skip Ok, w, de_density_scale because in the Planck cosmologies + # they are redshift independent and hence uninvertable, + # *_distance_z1z2 methods take multiple arguments, so require + # special handling + # clone is not a redshift-dependent method + # nu_relative_density is not redshift-dependent in the WMAP cosmologies + skip = ( + "Ok", + "Otot", + "angular_diameter_distance_z1z2", + "clone", + "is_equivalent", + "de_density_scale", + "w", + ) + if str(cosmo.name).startswith("WMAP"): + skip += ("nu_relative_density",) + + methods = inspect.getmembers(cosmo, predicate=inspect.ismethod) + + for name, func in methods: + if name.startswith("_") or name in skip: + continue + fval = func(z) + # we need zmax here to pick the right solution for + # angular_diameter_distance and related methods. + # Be slightly more generous with rtol than the default 1e-8 + # used in z_at_value + got = z_at_value(func, fval, bracket=[0.3, 1.0], ztol=1e-12) + assert allclose(got, z, rtol=2e-11), f"Round-trip testing {name} failed" + + # Test distance functions between two redshifts; only for realizations + if isinstance(getattr(cosmo, "name", None), str): + z2 = 2.0 + func_z1z2 = [ + lambda z1: cosmo.comoving_distance(z1, z2), + lambda z1: cosmo._comoving_transverse_distance_z1z2(z1, z2), + lambda z1: cosmo.angular_diameter_distance(z1, z2), + ] + for func in func_z1z2: + fval = func(z) + assert allclose(z, z_at_value(func, fval, zmax=1.5, ztol=1e-12), rtol=2e-11) diff --git a/astropy/cosmology/_src/tests/helper.py b/astropy/cosmology/_src/tests/helper.py new file mode 100644 index 000000000000..7519304ed788 --- /dev/null +++ b/astropy/cosmology/_src/tests/helper.py @@ -0,0 +1,107 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +""" +This module provides the tools used to internally run the cosmology test suite +from the installed astropy. It makes use of the |pytest| testing framework. +""" + +__all__ = ("clean_registry", "get_redshift_methods") + +import inspect + +import pytest + +from astropy.cosmology._src import core + +############################################################################### +# FUNCTIONS + + +def get_redshift_methods( + cosmology, + include_deprecated: bool = False, + include_private: bool = True, + include_z2: bool = True, +) -> set[str]: + """Get redshift methods from a cosmology. + + Parameters + ---------- + cosmology : |Cosmology| class or instance + include_deprecated : bool, optional + Whether to include deprecated methods, i.e. methods with a + ``__deprecated__`` attribute. Default is False. + include_private : bool, optional + Whether to include private methods, i.e. starts with an underscore. + Default is True. + include_z2 : bool, optional + Whether to include methods that are functions of 2 (or more) redshifts, + not the more common 1 redshift argument. Default is True. + + Returns + ------- + set[str] + The names of the redshift methods on `cosmology`, satisfying + `include_deprecated`, `include_private` and `include_z2`. + """ + # Get all the method names, optionally sieving out private methods + methods = set() + for n in dir(cosmology): + try: # get method, some will error on ABCs + m = getattr(cosmology, n) + except NotImplementedError: + continue + + # Add anything callable, optionally excluding private and deprecated methods. + if ( + callable(m) + and (not n.startswith("_") or include_private) + and (not hasattr(m, "__deprecated__") or include_deprecated) + ): + methods.add(n) + + # Sieve out incompatible methods. + # The index to check for redshift depends on whether cosmology is a class + # or instance and does/doesn't include 'self'. + iz1 = int(isinstance(cosmology, type)) + for n in tuple(methods): + try: + sig = inspect.signature(getattr(cosmology, n)) + except ValueError: # Remove non-introspectable methods. + methods.discard(n) + continue + else: + params = list(sig.parameters.keys()) + + # Remove non redshift methods: + if len(params) <= iz1: # Check there are enough arguments. + methods.discard(n) + elif len(params) >= iz1 + 1 and not params[iz1].startswith( + "z" + ): # First non-self arg is z. + methods.discard(n) + # If methods with 2 z args are not allowed, the following arg is checked. + elif ( + not include_z2 + and (len(params) >= iz1 + 2) + and params[iz1 + 1].startswith("z") + ): + methods.discard(n) + + return methods + + +############################################################################### +# FIXTURES + + +@pytest.fixture +def clean_registry(): + """`pytest.fixture` for clearing and restoring ``_COSMOLOGY_CLASSES``.""" + # TODO! with monkeypatch instead for thread safety. + ORIGINAL_COSMOLOGY_CLASSES = core._COSMOLOGY_CLASSES + core._COSMOLOGY_CLASSES = {} # set as empty dict + + yield core._COSMOLOGY_CLASSES + + core._COSMOLOGY_CLASSES = ORIGINAL_COSMOLOGY_CLASSES diff --git a/astropy/cosmology/_src/tests/io/__init__.py b/astropy/cosmology/_src/tests/io/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/astropy/cosmology/_src/tests/io/base.py b/astropy/cosmology/_src/tests/io/base.py new file mode 100644 index 000000000000..26c3385ba6eb --- /dev/null +++ b/astropy/cosmology/_src/tests/io/base.py @@ -0,0 +1,215 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +import pytest + +import astropy.units as u +from astropy.cosmology import Cosmology, Parameter, realizations +from astropy.cosmology import units as cu +from astropy.cosmology._src.core import _COSMOLOGY_CLASSES, dataclass_decorator +from astropy.cosmology.realizations import available + +cosmo_instances = [getattr(realizations, name) for name in available] + + +############################################################################## + + +class IOTestBase: + """Base class for Cosmology I/O tests. + + This class will not be directly called by :mod:`pytest` since its name does + not begin with ``Test``. To activate the contained tests this class must + be inherited in a subclass. Subclasses must define a :func:`pytest.fixture` + ``cosmo`` that returns/yields an instance of a |Cosmology|. + See ``TestCosmology`` for an example. + """ + + +class ToFromTestMixinBase(IOTestBase): + """Tests for a Cosmology[To/From]Format with some ``format``. + + This class will not be directly called by :mod:`pytest` since its name does + not begin with ``Test``. To activate the contained tests this class must + be inherited in a subclass. Subclasses must define a :func:`pytest.fixture` + ``cosmo`` that returns/yields an instance of a |Cosmology|. + See ``TestCosmology`` for an example. + """ + + @pytest.fixture(scope="class") + @classmethod + def from_format(cls): + """Convert to Cosmology using ``Cosmology.from_format()``.""" + return Cosmology.from_format + + @pytest.fixture(scope="class") + @classmethod + def to_format(cls, cosmo): + """Convert Cosmology instance using ``.to_format()``.""" + return cosmo.to_format + + @staticmethod + def can_autodentify(format): + """Check whether a format can auto-identify.""" + return format in Cosmology.from_format.registry._identifiers + + +class ReadWriteTestMixinBase(IOTestBase): + """Tests for a Cosmology[Read/Write]. + + This class will not be directly called by :mod:`pytest` since its name does + not begin with ``Test``. To activate the contained tests this class must + be inherited in a subclass. Subclasses must define a :func:`pytest.fixture` + ``cosmo`` that returns/yields an instance of a |Cosmology|. + See ``TestCosmology`` for an example. + """ + + @pytest.fixture(scope="class") + @classmethod + def read(cls): + """Read Cosmology instance using ``Cosmology.read()``.""" + return Cosmology.read + + @pytest.fixture(scope="class") + @classmethod + def write(cls, cosmo): + """Write Cosmology using ``.write()``.""" + return cosmo.write + + @pytest.fixture + def add_cu(self): + """Add :mod:`astropy.cosmology.units` to the enabled units.""" + # TODO! autoenable 'cu' if cosmology is imported? + with u.add_enabled_units(cu): + yield + + +############################################################################## + + +class IODirectTestBase(IOTestBase): + """Directly test Cosmology I/O functions. + + These functions are not public API and are discouraged from public use, in + favor of the I/O methods on |Cosmology|. They are tested b/c they are used + internally and because some tests for the methods on |Cosmology| don't need + to be run in the |Cosmology| class's large test matrix. + + This class will not be directly called by :mod:`pytest` since its name does + not begin with ``Test``. To activate the contained tests this class must + be inherited in a subclass. + """ + + @pytest.fixture(scope="class", autouse=True) + @classmethod + def setup(cls): + """Setup and teardown for tests.""" + + @dataclass_decorator + class CosmologyWithKwargs(Cosmology): + Tcmb0: Parameter = Parameter(default=0, unit=u.K) + + def __init__( + self, Tcmb0=0, name="cosmology with kwargs", meta=None, **kwargs + ): + super().__init__(name=name, meta=meta) + self.__dict__["Tcmb0"] = Tcmb0 << u.K + + yield # run tests + + # pop CosmologyWithKwargs from registered classes + # but don't error b/c it can fail in parallel + _COSMOLOGY_CLASSES.pop(CosmologyWithKwargs.__qualname__, None) + + @pytest.fixture(scope="class", params=cosmo_instances) + @classmethod + def cosmo(cls, request): + """Cosmology instance.""" + if isinstance(request.param, str): # CosmologyWithKwargs + return _COSMOLOGY_CLASSES[request.param](Tcmb0=3) + return request.param + + @pytest.fixture(scope="class") + @classmethod + def cosmo_cls(cls, cosmo): + """Cosmology classes.""" + return cosmo.__class__ + + +class ToFromDirectTestBase(IODirectTestBase, ToFromTestMixinBase): + """Directly test ``to/from_``. + + These functions are not public API and are discouraged from public use, in + favor of ``Cosmology.to/from_format(..., format="")``. They are + tested because they are used internally and because some tests for the + methods on |Cosmology| don't need to be run in the |Cosmology| class's + large test matrix. + + This class will not be directly called by :mod:`pytest` since its name does + not begin with ``Test``. To activate the contained tests this class must + be inherited in a subclass. + + Subclasses should have an attribute ``functions`` which is a dictionary + containing two items: ``"to"=`` and + ``"from"=``. + """ + + @pytest.fixture(scope="class") + @classmethod + def from_format(cls): + """Convert to Cosmology using function ``from``.""" + + def use_from_format(*args, **kwargs): + kwargs.pop("format", None) # specific to Cosmology.from_format + return cls.functions["from"](*args, **kwargs) + + return use_from_format + + @pytest.fixture(scope="class") + @classmethod + def to_format(cls, cosmo): + """Convert Cosmology to format using function ``to``.""" + + def use_to_format(*args, **kwargs): + return cls.functions["to"](cosmo, *args, **kwargs) + + return use_to_format + + +class ReadWriteDirectTestBase(IODirectTestBase, ToFromTestMixinBase): + """Directly test ``read/write_``. + + These functions are not public API and are discouraged from public use, in + favor of ``Cosmology.read/write(..., format="")``. They are tested + because they are used internally and because some tests for the + methods on |Cosmology| don't need to be run in the |Cosmology| class's + large test matrix. + + This class will not be directly called by :mod:`pytest` since its name does + not begin with ``Test``. To activate the contained tests this class must + be inherited in a subclass. + + Subclasses should have an attribute ``functions`` which is a dictionary + containing two items: ``"read"=`` and + ``"write"=``. + """ + + @pytest.fixture(scope="class") + @classmethod + def read(cls): + """Read Cosmology from file using function ``read``.""" + + def use_read(*args, **kwargs): + kwargs.pop("format", None) # specific to Cosmology.from_format + return cls.functions["read"](*args, **kwargs) + + return use_read + + @pytest.fixture(scope="class") + @classmethod + def write(cls, cosmo): + """Write Cosmology to file using function ``write``.""" + + def use_write(*args, **kwargs): + return cls.functions["write"](cosmo, *args, **kwargs) + + return use_write diff --git a/astropy/cosmology/_src/tests/io/test_.py b/astropy/cosmology/_src/tests/io/test_.py new file mode 100644 index 000000000000..695e11fa0840 --- /dev/null +++ b/astropy/cosmology/_src/tests/io/test_.py @@ -0,0 +1,33 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +"""Test that all expected methods are present, before I/O tests import. + +This file is weirdly named so that it's the first test of I/O. +""" + +from astropy.cosmology.io import convert_registry, readwrite_registry + + +def test_expected_readwrite_io(): + """Test that ONLY the expected I/O is registered.""" + + got = {k for k, _ in readwrite_registry._readers.keys()} + expected = {"ascii.ecsv", "ascii.html", "ascii.mrt"} + + assert got == expected + + +def test_expected_convert_io(): + """Test that ONLY the expected I/O is registered.""" + + got = {k for k, _ in convert_registry._readers.keys()} + expected = { + "astropy.cosmology", + "mapping", + "astropy.model", + "astropy.row", + "astropy.table", + "yaml", + } + + assert got == expected diff --git a/astropy/cosmology/_src/tests/io/test_connect.py b/astropy/cosmology/_src/tests/io/test_connect.py new file mode 100644 index 000000000000..72e042b0b42b --- /dev/null +++ b/astropy/cosmology/_src/tests/io/test_connect.py @@ -0,0 +1,303 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +import inspect +import sys + +import pytest + +from astropy import cosmology +from astropy.cosmology import Cosmology, w0wzCDM +from astropy.cosmology._src.tests.io import ( + test_cosmology, + test_ecsv, + test_html, + test_json, + test_latex, + test_mapping, + test_model, + test_mrt, + test_row, + test_table, + test_yaml, +) +from astropy.cosmology.io import readwrite_registry +from astropy.table import QTable, Row +from astropy.utils.compat.optional_deps import HAS_BS4 + +############################################################################### +# SETUP + +cosmo_instances = cosmology.realizations.available + +# Collect the registered read/write formats. +# (format, supports_metadata, has_all_required_dependencies) +readwrite_formats = [ + ("ascii.ecsv", True, True), + ("ascii.html", False, HAS_BS4), + ("ascii.latex", False, True), + ("ascii.mrt", False, True), + ("json", True, True), + ("latex", False, True), +] + + +# Collect all the registered to/from formats. Unfortunately this is NOT +# automatic since the output format class is not stored on the registry. +# (format, data type) +tofrom_formats = [ + ("mapping", dict), + ("yaml", str), + ("astropy.cosmology", Cosmology), + ("astropy.row", Row), + ("astropy.table", QTable), +] + + +############################################################################### + + +class ReadWriteTestMixin( + test_ecsv.ReadWriteECSVTestMixin, + test_html.ReadWriteHTMLTestMixin, + test_json.ReadWriteJSONTestMixin, + test_latex.WriteLATEXTestMixin, + test_mrt.ReadWriteMRTTestMixin, +): + """ + Tests for a CosmologyRead/Write on a |Cosmology|. + This class will not be directly called by :mod:`pytest` since its name does + not begin with ``Test``. To activate the contained tests this class must + be inherited in a subclass. Subclasses must define a :func:`pytest.fixture` + ``cosmo`` that returns/yields an instance of a |Cosmology|. + See ``TestReadWriteCosmology`` or ``TestCosmology`` for examples. + """ + + @pytest.mark.parametrize("format, metaio, has_deps", readwrite_formats) + def test_readwrite_complete_info(self, cosmo, tmp_path, format, metaio, has_deps): + """ + Test writing from an instance and reading from the base class. + This requires full information. + The round-tripped metadata can be in a different order, so the + OrderedDict must be converted to a dict before testing equality. + """ + if not has_deps: + pytest.skip("missing a dependency") + if (format, Cosmology) not in readwrite_registry._readers: + pytest.xfail(f"no read method is registered for format {format!r}") + + fname = tmp_path / f"{cosmo.name}.{format}" + cosmo.write(fname, format=format) + + # Also test kwarg "overwrite" + assert fname.is_file() + with pytest.raises(IOError): + cosmo.write(fname, format=format, overwrite=False) + + assert fname.exists() # overwrite file existing file + cosmo.write(fname, format=format, overwrite=True) + + # Read back + got = Cosmology.read(fname, format=format) + + assert got == cosmo + assert (not metaio) ^ (dict(got.meta) == dict(cosmo.meta)) + + @pytest.mark.parametrize("format, metaio, has_deps", readwrite_formats) + def test_readwrite_from_subclass_complete_info( + self, cosmo_cls, cosmo, tmp_path, format, metaio, has_deps + ): + """ + Test writing from an instance and reading from that class, when there's + full information saved. + """ + if not has_deps: + pytest.skip("missing a dependency") + if (format, Cosmology) not in readwrite_registry._readers: + pytest.xfail(f"no read method is registered for format {format!r}") + + fname = str(tmp_path / f"{cosmo.name}.{format}") + cosmo.write(fname, format=format) + + # read with the same class that wrote. + got = cosmo_cls.read(fname, format=format) + assert got == cosmo + assert (not metaio) ^ (dict(got.meta) == dict(cosmo.meta)) + + # this should be equivalent to + got = Cosmology.read(fname, format=format, cosmology=cosmo_cls) + assert got == cosmo + assert (not metaio) ^ (dict(got.meta) == dict(cosmo.meta)) + + # and also + got = Cosmology.read(fname, format=format, cosmology=cosmo_cls.__qualname__) + assert got == cosmo + assert (not metaio) ^ (dict(got.meta) == dict(cosmo.meta)) + + +class TestCosmologyReadWrite(ReadWriteTestMixin): + """Test the classes CosmologyRead/Write.""" + + @pytest.fixture(scope="class", params=cosmo_instances) + @classmethod + def cosmo(cls, request): + return getattr(cosmology.realizations, request.param) + + @pytest.fixture(scope="class") + @classmethod + def cosmo_cls(cls, cosmo): + return cosmo.__class__ + + # ============================================================== + + @pytest.mark.parametrize("format, _, has_deps", readwrite_formats) + def test_write_methods_have_explicit_kwarg_overwrite(self, format, _, has_deps): + if not has_deps: + pytest.skip("missing a dependency") + if (format, Cosmology) not in readwrite_registry._readers: + pytest.xfail(f"no read method is registered for format {format!r}") + + writer = readwrite_registry.get_writer(format, Cosmology) + # test in signature + sig = inspect.signature(writer) + assert "overwrite" in sig.parameters + + # also in docstring + if not sys.flags.optimize: + assert "overwrite : bool" in writer.__doc__ + + @pytest.mark.parametrize("format, _, has_deps", readwrite_formats) + def test_readwrite_reader_class_mismatch( + self, cosmo, tmp_path, format, _, has_deps + ): + """Test when the reader class doesn't match the file.""" + if not has_deps: + pytest.skip("missing a dependency") + if (format, Cosmology) not in readwrite_registry._readers: + pytest.xfail(f"no read method is registered for format {format!r}") + + fname = tmp_path / f"{cosmo.name}.{format}" + cosmo.write(fname, format=format) + + # class mismatch + # when reading directly + with pytest.raises(TypeError, match="missing 1 required"): + w0wzCDM.read(fname, format=format) + + with pytest.raises(TypeError, match="missing 1 required"): + Cosmology.read(fname, format=format, cosmology=w0wzCDM) + + # when specifying the class + with pytest.raises(ValueError, match="`cosmology` must be either"): + w0wzCDM.read(fname, format=format, cosmology="FlatLambdaCDM") + + +############################################################################### +# To/From_Format Tests + + +class ToFromFormatTestMixin( + test_cosmology.ToFromCosmologyTestMixin, + test_mapping.ToFromMappingTestMixin, + test_model.ToFromModelTestMixin, + test_row.ToFromRowTestMixin, + test_table.ToFromTableTestMixin, + test_yaml.ToFromYAMLTestMixin, +): + """ + Tests for a Cosmology[To/From]Format on a |Cosmology|. + This class will not be directly called by :mod:`pytest` since its name does + not begin with ``Test``. To activate the contained tests this class must + be inherited in a subclass. Subclasses must define a :func:`pytest.fixture` + ``cosmo`` that returns/yields an instance of a |Cosmology|. + See ``TestCosmology`` for an example. + """ + + @pytest.mark.parametrize("format, totype", tofrom_formats) + def test_tofromformat_complete_info( + self, cosmo, format, totype, xfail_if_not_registered_with_yaml + ): + """Read tests happen later.""" + # test to_format + obj = cosmo.to_format(format) + assert isinstance(obj, totype) + + # test from_format + got = Cosmology.from_format(obj, format=format) + + # Test autodetect, if enabled + if self.can_autodentify(format): + got2 = Cosmology.from_format(obj) + assert got2 == got # internal consistency + + assert got == cosmo # external consistency + assert got.meta == cosmo.meta + + @pytest.mark.parametrize("format, totype", tofrom_formats) + def test_fromformat_subclass_complete_info( + self, cosmo_cls, cosmo, format, totype, xfail_if_not_registered_with_yaml + ): + """ + Test transforming an instance and parsing from that class, when there's + full information available. + Partial information tests are handled in the Mixin super classes. + """ + # test to_format + obj = cosmo.to_format(format) + assert isinstance(obj, totype) + + # read with the same class that wrote. + got = cosmo_cls.from_format(obj, format=format) + + if self.can_autodentify(format): + got2 = Cosmology.from_format(obj) # and autodetect + assert got2 == got # internal consistency + + assert got == cosmo # external consistency + assert got.meta == cosmo.meta + + # this should be equivalent to + got = Cosmology.from_format(obj, format=format, cosmology=cosmo_cls) + assert got == cosmo + assert got.meta == cosmo.meta + + # and also + got = Cosmology.from_format( + obj, format=format, cosmology=cosmo_cls.__qualname__ + ) + assert got == cosmo + assert got.meta == cosmo.meta + + +class TestCosmologyToFromFormat(ToFromFormatTestMixin): + """Test Cosmology[To/From]Format classes.""" + + @pytest.fixture(scope="class", params=cosmo_instances) + @classmethod + def cosmo(cls, request): + return getattr(cosmology.realizations, request.param) + + @pytest.fixture(scope="class") + @classmethod + def cosmo_cls(cls, cosmo): + return cosmo.__class__ + + # ============================================================== + + @pytest.mark.parametrize("format_type", tofrom_formats) + def test_fromformat_class_mismatch(self, cosmo, format_type): + format, totype = format_type + + # test to_format + obj = cosmo.to_format(format) + assert isinstance(obj, totype) + + # class mismatch + with pytest.raises(TypeError): + w0wzCDM.from_format(obj, format=format) + + with pytest.raises(TypeError): + Cosmology.from_format(obj, format=format, cosmology=w0wzCDM) + + # when specifying the class + with pytest.raises(ValueError, match="`cosmology` must be either"): + w0wzCDM.from_format(obj, format=format, cosmology="FlatLambdaCDM") diff --git a/astropy/cosmology/_src/tests/io/test_cosmology.py b/astropy/cosmology/_src/tests/io/test_cosmology.py new file mode 100644 index 000000000000..6d1bbfade320 --- /dev/null +++ b/astropy/cosmology/_src/tests/io/test_cosmology.py @@ -0,0 +1,57 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +import pytest + +from astropy.cosmology._src.io.builtin.cosmology import from_cosmology, to_cosmology + +from .base import IODirectTestBase, ToFromTestMixinBase + +############################################################################### + + +class ToFromCosmologyTestMixin(ToFromTestMixinBase): + """ + Tests for a Cosmology[To/From]Format with ``format="astropy.cosmology"``. + This class will not be directly called by :mod:`pytest` since its name does + not begin with ``Test``. To activate the contained tests this class must + be inherited in a subclass. Subclasses must define a :func:`pytest.fixture` + ``cosmo`` that returns/yields an instance of a |Cosmology|. + See ``TestCosmology`` for an example. + """ + + def test_to_cosmology_default(self, cosmo, to_format): + """Test cosmology -> cosmology.""" + newcosmo = to_format("astropy.cosmology") + assert newcosmo is cosmo + + def test_from_not_cosmology(self, cosmo, from_format): + """Test incorrect type in ``Cosmology``.""" + with pytest.raises(TypeError): + from_format("NOT A COSMOLOGY", format="astropy.cosmology") + + def test_from_cosmology_default(self, cosmo, from_format): + """Test cosmology -> cosmology.""" + newcosmo = from_format(cosmo) + assert newcosmo is cosmo + + @pytest.mark.parametrize("format", [True, False, None, "astropy.cosmology"]) + def test_is_equivalent_to_cosmology(self, cosmo, to_format, format): + """Test :meth:`astropy.cosmology.Cosmology.is_equivalent`. + + This test checks that Cosmology equivalency can be extended to any + Python object that can be converted to a Cosmology -- in this case + a Cosmology! Since it's the identity conversion, the cosmology is + always equivalent to itself, regardless of ``format``. + """ + obj = to_format("astropy.cosmology") + assert obj is cosmo + + is_equiv = cosmo.is_equivalent(obj, format=format) + assert is_equiv is True # equivalent to self + + +class TestToFromCosmology(IODirectTestBase, ToFromCosmologyTestMixin): + """Directly test ``to/from_cosmology``.""" + + def setup_class(self): + self.functions = {"to": to_cosmology, "from": from_cosmology} diff --git a/astropy/cosmology/_src/tests/io/test_ecsv.py b/astropy/cosmology/_src/tests/io/test_ecsv.py new file mode 100644 index 000000000000..1de3dafec8c3 --- /dev/null +++ b/astropy/cosmology/_src/tests/io/test_ecsv.py @@ -0,0 +1,230 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +import pytest + +from astropy.cosmology._src.core import _COSMOLOGY_CLASSES +from astropy.cosmology._src.io.builtin.ecsv import read_ecsv, write_ecsv +from astropy.table import QTable, Table, vstack + +from .base import ReadWriteDirectTestBase, ReadWriteTestMixinBase + +############################################################################### + + +class ReadWriteECSVTestMixin(ReadWriteTestMixinBase): + """ + Tests for a Cosmology[Read/Write] with ``format="ascii.ecsv"``. + This class will not be directly called by :mod:`pytest` since its name does + not begin with ``Test``. To activate the contained tests this class must + be inherited in a subclass. Subclasses must define a :func:`pytest.fixture` + ``cosmo`` that returns/yields an instance of a |Cosmology|. + See ``TestCosmology`` for an example. + """ + + def test_to_ecsv_bad_index(self, read, write, tmp_path): + """Test if argument ``index`` is incorrect""" + fp = tmp_path / "test_to_ecsv_bad_index.ecsv" + + write(fp, format="ascii.ecsv") + + # single-row table and has a non-0/None index + with pytest.raises(IndexError, match="index 2 out of range"): + read(fp, index=2, format="ascii.ecsv") + + # string index where doesn't match + with pytest.raises(KeyError, match="No matches found for key"): + read(fp, index="row 0", format="ascii.ecsv") + + # ----------------------- + + def test_to_ecsv_failed_cls(self, write, tmp_path): + """Test failed table type.""" + fp = tmp_path / "test_to_ecsv_failed_cls.ecsv" + + with pytest.raises(TypeError, match="'cls' must be"): + write(fp, format="ascii.ecsv", cls=list) + + @pytest.mark.parametrize("tbl_cls", [QTable, Table]) + def test_to_ecsv_cls(self, write, tbl_cls, tmp_path): + fp = tmp_path / "test_to_ecsv_cls.ecsv" + write(fp, format="ascii.ecsv", cls=tbl_cls) + + # ----------------------- + + @pytest.mark.parametrize("in_meta", [True, False]) + def test_to_ecsv_in_meta(self, cosmo_cls, write, in_meta, tmp_path, add_cu): + """Test where the cosmology class is placed.""" + fp = tmp_path / "test_to_ecsv_in_meta.ecsv" + write(fp, format="ascii.ecsv", cosmology_in_meta=in_meta) + + # if it's in metadata, it's not a column. And vice versa. + tbl = QTable.read(fp) + if in_meta: + assert tbl.meta["cosmology"] == cosmo_cls.__qualname__ + assert "cosmology" not in tbl.colnames # not also a column + else: + assert tbl["cosmology"][0] == cosmo_cls.__qualname__ + assert "cosmology" not in tbl.meta + + # ----------------------- + + def test_readwrite_ecsv_instance( + self, cosmo_cls, cosmo, read, write, tmp_path, add_cu + ): + """Test cosmology -> ascii.ecsv -> cosmology.""" + fp = tmp_path / "test_readwrite_ecsv_instance.ecsv" + + # ------------ + # To Table + + write(fp, format="ascii.ecsv") + + # some checks on the saved file + tbl = QTable.read(fp) + assert tbl.meta["cosmology"] == cosmo_cls.__qualname__ + assert tbl["name"] == cosmo.name + + # ------------ + # From Table + + tbl["mismatching"] = "will error" + tbl.write(fp, format="ascii.ecsv", overwrite=True) + + # tests are different if the last argument is a **kwarg + if cosmo._init_has_kwargs: + got = read(fp, format="ascii.ecsv") + + assert got.__class__ is cosmo_cls + assert got.name == cosmo.name + assert "mismatching" not in got.meta + + return # don't continue testing + + # read with mismatching parameters errors + with pytest.raises(TypeError, match="there are unused parameters"): + read(fp, format="ascii.ecsv") + + # unless mismatched are moved to meta + got = read(fp, format="ascii.ecsv", move_to_meta=True) + assert got == cosmo + assert got.meta["mismatching"] == "will error" + + # it won't error if everything matches up + tbl.remove_column("mismatching") + tbl.write(fp, format="ascii.ecsv", overwrite=True) + got = read(fp, format="ascii.ecsv") + assert got == cosmo + + # and it will also work if the cosmology is a class + # Note this is not the default output of ``write``. + tbl.meta["cosmology"] = _COSMOLOGY_CLASSES[tbl.meta["cosmology"]] + got = read(fp, format="ascii.ecsv") + assert got == cosmo + + # also it auto-identifies 'format' + got = read(fp) + assert got == cosmo + + def test_readwrite_ecsv_renamed_columns( + self, cosmo_cls, cosmo, read, write, tmp_path, add_cu + ): + """Test rename argument to read/write.""" + fp = tmp_path / "test_readwrite_ecsv_rename.ecsv" + rename = {"name": "cosmo_name"} + + write(fp, format="ascii.ecsv", rename=rename) + + tbl = QTable.read(fp, format="ascii.ecsv") + + assert "name" not in tbl.colnames + assert "cosmo_name" in tbl.colnames + + # Errors if reading + with pytest.raises( + TypeError, match="there are unused parameters {'cosmo_name':" + ): + read(fp) + + # Roundtrips + inv_rename = {v: k for k, v in rename.items()} + got = read(fp, rename=inv_rename) + assert got == cosmo + + def test_readwrite_ecsv_subclass_partial_info( + self, cosmo_cls, cosmo, read, write, tmp_path, add_cu + ): + """ + Test writing from an instance and reading from that class. + This works with missing information. + """ + fp = tmp_path / "test_read_ecsv_subclass_partial_info.ecsv" + + # test write + write(fp, format="ascii.ecsv") + + # partial information + tbl = QTable.read(fp) + tbl.meta.pop("cosmology", None) + del tbl["Tcmb0"] + tbl.write(fp, overwrite=True) + + # read with the same class that wrote fills in the missing info with + # the default value + got = cosmo_cls.read(fp, format="ascii.ecsv") + got2 = read(fp, format="ascii.ecsv", cosmology=cosmo_cls) + got3 = read(fp, format="ascii.ecsv", cosmology=cosmo_cls.__qualname__) + + assert (got == got2) and (got2 == got3) # internal consistency + + # not equal, because Tcmb0 is changed, which also changes m_nu + assert got != cosmo + assert got.Tcmb0 == cosmo_cls.parameters["Tcmb0"].default + assert got.clone(name=cosmo.name, Tcmb0=cosmo.Tcmb0, m_nu=cosmo.m_nu) == cosmo + # but the metadata is the same + assert got.meta == cosmo.meta + + def test_readwrite_ecsv_mutlirow(self, cosmo, read, write, tmp_path, add_cu): + """Test if table has multiple rows.""" + fp = tmp_path / "test_readwrite_ecsv_mutlirow.ecsv" + + # Make + cosmo1 = cosmo.clone(name="row 0") + cosmo2 = cosmo.clone(name="row 2") + tbl = vstack( + [c.to_format("astropy.table") for c in (cosmo1, cosmo, cosmo2)], + metadata_conflicts="silent", + ) + tbl.write(fp, format="ascii.ecsv") + + # ------------ + # From Table + + # it will error on a multi-row table + with pytest.raises(ValueError, match="need to select a specific row"): + read(fp, format="ascii.ecsv") + + # unless the index argument is provided + got = read(fp, index=1, format="ascii.ecsv") + assert got == cosmo + + # the index can be a string + got = read(fp, index=cosmo.name, format="ascii.ecsv") + assert got == cosmo + + # it's better if the table already has an index + # this will be identical to the previous ``got`` + tbl.add_index("name") + got2 = read(fp, index=cosmo.name, format="ascii.ecsv") + assert got2 == cosmo + + +class TestReadWriteECSV(ReadWriteDirectTestBase, ReadWriteECSVTestMixin): + """ + Directly test ``read/write_ecsv``. + These are not public API and are discouraged from use, in favor of + ``Cosmology.read/write(..., format="ascii.ecsv")``, but should be + tested regardless b/c they are used internally. + """ + + def setup_class(self): + self.functions = {"read": read_ecsv, "write": write_ecsv} diff --git a/astropy/cosmology/_src/tests/io/test_html.py b/astropy/cosmology/_src/tests/io/test_html.py new file mode 100644 index 000000000000..30478bcfb1be --- /dev/null +++ b/astropy/cosmology/_src/tests/io/test_html.py @@ -0,0 +1,261 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +import pytest + +import astropy.units as u +from astropy.cosmology._src.io.builtin.html import ( + _FORMAT_TABLE, + read_html_table, + write_html_table, +) +from astropy.table import QTable, Table, vstack +from astropy.utils.compat.optional_deps import HAS_BS4 + +from .base import ReadWriteDirectTestBase, ReadWriteTestMixinBase + +############################################################################### + + +class ReadWriteHTMLTestMixin(ReadWriteTestMixinBase): + """ + Tests for a Cosmology[Read/Write] with ``format="ascii.html"``. + This class will not be directly called by :mod:`pytest` since its name does + not begin with ``Test``. To activate the contained tests this class must + be inherited in a subclass. Subclasses must define a :func:`pytest.fixture` + ``cosmo`` that returns/yields an instance of a |Cosmology|. + See ``TestCosmology`` for an example. + """ + + @pytest.mark.skipif(not HAS_BS4, reason="requires beautifulsoup4") + def test_to_html_table_bad_index(self, read, write, tmp_path): + """Test if argument ``index`` is incorrect""" + fp = tmp_path / "test_to_html_table_bad_index.html" + + write(fp, format="ascii.html") + + # single-row table and has a non-0/None index + with pytest.raises(IndexError, match="index 2 out of range"): + read(fp, index=2, format="ascii.html") + + # string index where doesn't match + with pytest.raises(KeyError, match="No matches found for key"): + read(fp, index="row 0", format="ascii.html") + + # ----------------------- + + @pytest.mark.skipif(not HAS_BS4, reason="requires beautifulsoup4") + def test_to_html_table_failed_cls(self, write, tmp_path): + """Test failed table type.""" + fp = tmp_path / "test_to_html_table_failed_cls.html" + + with pytest.raises(TypeError, match="'cls' must be"): + write(fp, format="ascii.html", cls=list) + + @pytest.mark.parametrize("tbl_cls", [QTable, Table]) + @pytest.mark.skipif(not HAS_BS4, reason="requires beautifulsoup4") + def test_to_html_table_cls(self, write, tbl_cls, tmp_path): + fp = tmp_path / "test_to_html_table_cls.html" + write(fp, format="ascii.html", cls=tbl_cls) + + # ----------------------- + + @pytest.mark.skipif(not HAS_BS4, reason="requires beautifulsoup4") + def test_readwrite_html_table_instance( + self, cosmo_cls, cosmo, read, write, tmp_path, add_cu + ): + """Test cosmology -> ascii.html -> cosmology.""" + fp = tmp_path / "test_readwrite_html_table_instance.html" + + # ------------ + # To Table + + write(fp, format="ascii.html") + + # some checks on the saved file + tbl = QTable.read(fp) + # assert tbl.meta["cosmology"] == cosmo_cls.__qualname__ # metadata read not implemented + assert tbl["name"] == cosmo.name + + # ------------ + # From Table + + tbl["mismatching"] = "will error" + tbl.write(fp, format="ascii.html", overwrite=True) + + # tests are different if the last argument is a **kwarg + if cosmo._init_has_kwargs: + got = read(fp, format="ascii.html") + + assert got.__class__ is cosmo_cls + assert got.name == cosmo.name + # assert "mismatching" not in got.meta # metadata read not implemented + + return # don't continue testing + + # read with mismatching parameters errors + with pytest.raises(TypeError, match="there are unused parameters"): + read(fp, format="ascii.html") + + # unless mismatched are moved to meta + got = read(fp, format="ascii.html", move_to_meta=True) + assert got == cosmo + # assert got.meta["mismatching"] == "will error" # metadata read not implemented + + # it won't error if everything matches up + tbl.remove_column("mismatching") + tbl.write(fp, format="ascii.html", overwrite=True) + got = read(fp, format="ascii.html") + assert got == cosmo + + # and it will also work if the cosmology is a class + # Note this is not the default output of ``write``. + # tbl.meta["cosmology"] = _COSMOLOGY_CLASSES[tbl.meta["cosmology"]] # + # metadata read not implemented + got = read(fp, format="ascii.html") + assert got == cosmo + + got = read(fp) + assert got == cosmo + + @pytest.mark.skipif(not HAS_BS4, reason="requires beautifulsoup4") + def test_rename_html_table_columns(self, read, write, tmp_path): + """Tests renaming columns""" + fp = tmp_path / "test_rename_html_table_columns.html" + + write(fp, format="ascii.html", latex_names=True) + + tbl = QTable.read(fp) + + # asserts each column name has not been reverted yet + # For now, Cosmology class and name are stored in first 2 slots + for column_name in tbl.colnames[2:]: + assert column_name in _FORMAT_TABLE.values() + + cosmo = read(fp, format="ascii.html") + converted_tbl = cosmo.to_format("astropy.table") + + # asserts each column name has been reverted + # cosmology name is still stored in first slot + for column_name in converted_tbl.colnames[1:]: + assert column_name in _FORMAT_TABLE.keys() + + @pytest.mark.skipif(not HAS_BS4, reason="requires beautifulsoup4") + @pytest.mark.parametrize("latex_names", [True, False]) + def test_readwrite_html_subclass_partial_info( + self, cosmo_cls, cosmo, read, write, latex_names, tmp_path, add_cu + ): + """ + Test writing from an instance and reading from that class. + This works with missing information. + """ + fp = tmp_path / "test_read_html_subclass_partial_info.html" + + # test write + write(fp, format="ascii.html", latex_names=latex_names) + + # partial information + tbl = QTable.read(fp) + + # tbl.meta.pop("cosmology", None) # metadata not implemented + cname = "$$T_{0}$$" if latex_names else "Tcmb0" + del tbl[cname] # format is not converted to original units + tbl.write(fp, overwrite=True) + + # read with the same class that wrote fills in the missing info with + # the default value + got = cosmo_cls.read(fp, format="ascii.html") + got2 = read(fp, format="ascii.html", cosmology=cosmo_cls) + got3 = read(fp, format="ascii.html", cosmology=cosmo_cls.__qualname__) + + assert (got == got2) and (got2 == got3) # internal consistency + + # not equal, because Tcmb0 is changed, which also changes m_nu + assert got != cosmo + assert got.Tcmb0 == cosmo_cls.parameters["Tcmb0"].default + assert got.clone(name=cosmo.name, Tcmb0=cosmo.Tcmb0, m_nu=cosmo.m_nu) == cosmo + # but the metadata is the same + # assert got.meta == cosmo.meta # metadata read not implemented + + @pytest.mark.skipif(not HAS_BS4, reason="requires beautifulsoup4") + def test_readwrite_html_mutlirow(self, cosmo, read, write, tmp_path, add_cu): + """Test if table has multiple rows.""" + fp = tmp_path / "test_readwrite_html_mutlirow.html" + + # Make + cosmo1 = cosmo.clone(name="row 0") + cosmo2 = cosmo.clone(name="row 2") + table = vstack( + [c.to_format("astropy.table") for c in (cosmo1, cosmo, cosmo2)], + metadata_conflicts="silent", + ) + + cosmo_cls = type(cosmo) + assert cosmo is not None + + for n, col in zip(table.colnames, table.itercols()): + if n not in cosmo_cls.parameters: + continue + param = cosmo_cls.parameters[n] + if param.unit in (None, u.one): + continue + # Replace column with unitless version + table.replace_column(n, (col << param.unit).value, copy=False) + + table.write(fp, format="ascii.html") + + # ------------ + # From Table + + # it will error on a multi-row table + with pytest.raises(ValueError, match="need to select a specific row"): + read(fp, format="ascii.html") + + # unless the index argument is provided + got = cosmo_cls.read(fp, index=1, format="ascii.html") + # got = read(fp, index=1, format="ascii.html") + assert got == cosmo + + # the index can be a string + got = cosmo_cls.read(fp, index=cosmo.name, format="ascii.html") + assert got == cosmo + + # it's better if the table already has an index + # this will be identical to the previous ``got`` + table.add_index("name") + got2 = cosmo_cls.read(fp, index=cosmo.name, format="ascii.html") + assert got2 == cosmo + + +class TestReadWriteHTML(ReadWriteDirectTestBase, ReadWriteHTMLTestMixin): + """ + Directly test ``read/write_html``. + These are not public API and are discouraged from use, in favor of + ``Cosmology.read/write(..., format="ascii.html")``, but should be + tested regardless b/c they are used internally. + """ + + def setup_class(self): + self.functions = {"read": read_html_table, "write": write_html_table} + + @pytest.mark.skipif(not HAS_BS4, reason="requires beautifulsoup4") + def test_rename_direct_html_table_columns(self, read, write, tmp_path): + """Tests renaming columns""" + + fp = tmp_path / "test_rename_html_table_columns.html" + + write(fp, format="ascii.html", latex_names=True) + + tbl = QTable.read(fp) + + # asserts each column name has not been reverted yet + for column_name in tbl.colnames[2:]: + # for now, Cosmology as metadata and name is stored in first 2 slots + assert column_name in _FORMAT_TABLE.values() + + cosmo = read(fp, format="ascii.html") + converted_tbl = cosmo.to_format("astropy.table") + + # asserts each column name has been reverted + for column_name in converted_tbl.colnames[1:]: + # for now now, metadata is still stored in first slot + assert column_name in _FORMAT_TABLE.keys() diff --git a/astropy/cosmology/_src/tests/io/test_json.py b/astropy/cosmology/_src/tests/io/test_json.py new file mode 100644 index 000000000000..4f04f6329a82 --- /dev/null +++ b/astropy/cosmology/_src/tests/io/test_json.py @@ -0,0 +1,167 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +import json +import os +from pathlib import Path + +import pytest + +import astropy.units as u +from astropy.cosmology import Cosmology +from astropy.cosmology import units as cu +from astropy.cosmology.io import readwrite_registry + +from .base import ReadWriteDirectTestBase, ReadWriteTestMixinBase + +############################################################################### + + +def read_json(filename, **kwargs): + """Read JSON. + + Parameters + ---------- + filename : str | bytes | os.PathLike + **kwargs + Keyword arguments into :meth:`~astropy.cosmology.Cosmology.from_format` + + Returns + ------- + `~astropy.cosmology.Cosmology` instance + """ + # read + if isinstance(filename, (str, bytes, os.PathLike)): + data = Path(filename).read_text() + else: # file-like : this also handles errors in dumping + data = filename.read() + + mapping = json.loads(data) # parse json mappable to dict + + # deserialize Quantity + with u.add_enabled_units(cu.redshift): + for k, v in mapping.items(): + if isinstance(v, dict) and "value" in v and "unit" in v: + mapping[k] = u.Quantity(v["value"], v["unit"]) + for k, v in mapping.get("meta", {}).items(): # also the metadata + if isinstance(v, dict) and "value" in v and "unit" in v: + mapping["meta"][k] = u.Quantity(v["value"], v["unit"]) + + return Cosmology.from_format(mapping, format="mapping", **kwargs) + + +def write_json(cosmology, file, *, overwrite=False): + """Write Cosmology to JSON. + + Parameters + ---------- + cosmology : `astropy.cosmology.Cosmology` subclass instance + file : path-like or file-like + overwrite : bool (optional, keyword-only) + """ + data = cosmology.to_format("mapping") # start by turning into dict + data["cosmology"] = data["cosmology"].__qualname__ + + # serialize Quantity + for k, v in data.items(): + if isinstance(v, u.Quantity): + data[k] = {"value": v.value.tolist(), "unit": str(v.unit)} + for k, v in data.get("meta", {}).items(): # also serialize the metadata + if isinstance(v, u.Quantity): + data["meta"][k] = {"value": v.value.tolist(), "unit": str(v.unit)} + + # check that file exists and whether to overwrite. + file = Path(file) + if file.exists() and not overwrite: + raise OSError(f"{file} exists. Set 'overwrite' to write over.") + with file.open("w") as write_file: + json.dump(data, write_file) + + +def json_identify(origin, filepath, fileobj, *args, **kwargs): + return filepath is not None and filepath.endswith(".json") + + +############################################################################### + + +class ReadWriteJSONTestMixin(ReadWriteTestMixinBase): + """ + Tests for a Cosmology[Read/Write] with ``format="json"``. + This class will not be directly called by :mod:`pytest` since its name does + not begin with ``Test``. To activate the contained tests this class must + be inherited in a subclass. Subclasses must define a :func:`pytest.fixture` + ``cosmo`` that returns/yields an instance of a |Cosmology|. + See ``TestCosmology`` for an example. + """ + + @pytest.fixture(scope="class", autouse=True) + @classmethod + def register_and_unregister_json(cls): + """Setup & teardown for JSON read/write tests.""" + # Register + readwrite_registry.register_reader("json", Cosmology, read_json, force=True) + readwrite_registry.register_writer("json", Cosmology, write_json, force=True) + readwrite_registry.register_identifier( + "json", Cosmology, json_identify, force=True + ) + + yield # Run all tests in class + + # Unregister + readwrite_registry.unregister_reader("json", Cosmology) + readwrite_registry.unregister_writer("json", Cosmology) + readwrite_registry.unregister_identifier("json", Cosmology) + + # ======================================================================== + + def test_readwrite_json_subclass_partial_info( + self, cosmo_cls, cosmo, read, write, tmp_path, add_cu + ): + """ + Test writing from an instance and reading from that class. + This works with missing information. + """ + fp = tmp_path / "test_readwrite_json_subclass_partial_info.json" + + # test write + cosmo.write(fp, format="json") + + # partial information + with fp.open() as file: + L = file.readline() + L = ( + L[: L.index('"cosmology":')] + L[L.index(", ") + 2 :] + ) # remove cosmology : #203 + i = L.index('"Tcmb0":') # delete Tcmb0 + L = ( + L[:i] + L[L.index(", ", L.index(", ", i) + 1) + 2 :] + ) # second occurrence : #203 + + tempfname = tmp_path / f"{cosmo.name}_temp.json" + tempfname.write_text("".join(L)) + # read with the same class that wrote fills in the missing info with + # the default value + got = cosmo_cls.read(tempfname, format="json") + got2 = read(tempfname, format="json", cosmology=cosmo_cls) + got3 = read(tempfname, format="json", cosmology=cosmo_cls.__qualname__) + + assert (got == got2) and (got2 == got3) # internal consistency + + # not equal, because Tcmb0 is changed, which also changes m_nu + assert got != cosmo + assert got.Tcmb0 == cosmo_cls.parameters["Tcmb0"].default + assert got.clone(name=cosmo.name, Tcmb0=cosmo.Tcmb0, m_nu=cosmo.m_nu) == cosmo + # but the metadata is the same + assert got.meta == cosmo.meta + + +class TestReadWriteJSON(ReadWriteDirectTestBase, ReadWriteJSONTestMixin): + """ + Directly test ``read/write_json``. + These are not public API and are discouraged from use, in favor of + ``Cosmology.read/write(..., format="json")``, but should be + tested regardless b/c they are used internally. + """ + + def setup_class(self): + self.functions = {"read": read_json, "write": write_json} diff --git a/astropy/cosmology/_src/tests/io/test_latex.py b/astropy/cosmology/_src/tests/io/test_latex.py new file mode 100644 index 000000000000..2f73cc2080be --- /dev/null +++ b/astropy/cosmology/_src/tests/io/test_latex.py @@ -0,0 +1,86 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +import pytest + +from astropy.cosmology._src.io.builtin.latex import _FORMAT_TABLE, write_latex +from astropy.io.registry.base import IORegistryError +from astropy.table import QTable, Table + +from .base import ReadWriteDirectTestBase, ReadWriteTestMixinBase + + +class WriteLATEXTestMixin(ReadWriteTestMixinBase): + """ + Tests for a Cosmology[Write] with ``format="latex"``. + This class will not be directly called by :mod:`pytest` since its name does + not begin with ``Test``. To activate the contained tests this class must + be inherited in a subclass. Subclasses must define a :func:`pytest.fixture` + ``cosmo`` that returns/yields an instance of a |Cosmology|. + See ``TestCosmology`` for an example. + """ + + def test_to_latex_failed_cls(self, write, tmp_path): + """Test failed table type.""" + fp = tmp_path / "test_to_latex_failed_cls.tex" + + with pytest.raises(TypeError, match="'cls' must be"): + write(fp, cls=list) + + @pytest.mark.parametrize("tbl_cls", [QTable, Table]) + def test_to_latex_cls(self, write, tbl_cls, tmp_path): + fp = tmp_path / "test_to_latex_cls.tex" + write(fp, cls=tbl_cls) + + def test_latex_columns(self, write, tmp_path): + fp = tmp_path / "test_rename_latex_columns.tex" + write(fp, latex_names=True) + tbl = QTable.read(fp) + # asserts each column name has not been reverted yet + # For now, Cosmology class and name are stored in first 2 slots + for column_name in tbl.colnames[2:]: + assert column_name in _FORMAT_TABLE.values() + + def test_write_latex_invalid_path(self, write): + """Test passing an invalid path""" + invalid_fp = "" + with pytest.raises(FileNotFoundError, match="No such file or directory"): + write(invalid_fp, format="ascii.latex") + + def test_write_latex_false_overwrite(self, write, tmp_path): + """Test to write a LaTeX file without overwriting an existing file""" + # Test that passing an invalid path to write_latex() raises a IOError + fp = tmp_path / "test_write_latex_false_overwrite.tex" + write(fp) + with pytest.raises(OSError, match="overwrite=True"): + write(fp, overwrite=False) + + def test_write_latex_unsupported_format(self, write, tmp_path): + """Test for unsupported format""" + fp = tmp_path / "test_write_latex_unsupported_format.tex" + invalid_format = "unsupported" + with pytest.raises((ValueError, IORegistryError)) as exc_info: + pytest.raises(ValueError, match="format must be 'ascii.latex'") + pytest.raises(IORegistryError, match="No writer defined for format") + write(fp, format=invalid_format) + + +class TestReadWriteLaTex(ReadWriteDirectTestBase, WriteLATEXTestMixin): + """ + Directly test ``write_latex``. + These are not public API and are discouraged from use, in favor of + ``Cosmology.write(..., format="latex")``, but should be + tested regardless b/c they are used internally. + """ + + def setup_class(self): + self.functions = {"write": write_latex} + + def test_rename_direct_latex_columns(self, write, tmp_path): + """Tests renaming columns""" + fp = tmp_path / "test_rename_latex_columns.tex" + write(fp, latex_names=True) + tbl = QTable.read(fp) + # asserts each column name has not been reverted yet + for column_name in tbl.colnames[2:]: + # for now, Cosmology as metadata and name is stored in first 2 slots + assert column_name in _FORMAT_TABLE.values() diff --git a/astropy/cosmology/_src/tests/io/test_mapping.py b/astropy/cosmology/_src/tests/io/test_mapping.py new file mode 100644 index 000000000000..10067efceb51 --- /dev/null +++ b/astropy/cosmology/_src/tests/io/test_mapping.py @@ -0,0 +1,240 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +from collections import OrderedDict + +import numpy as np +import pytest + +from astropy.cosmology import Cosmology +from astropy.cosmology._src.io.builtin.mapping import from_mapping, to_mapping + +from .base import ToFromDirectTestBase, ToFromTestMixinBase + +############################################################################### + + +class ToFromMappingTestMixin(ToFromTestMixinBase): + """Tests for a Cosmology[To/From]Format with ``format="mapping"``. + + This class will not be directly called by :mod:`pytest` since its name does + not begin with ``Test``. To activate the contained tests this class must + be inherited in a subclass. Subclasses must define a :func:`pytest.fixture` + ``cosmo`` that returns/yields an instance of a |Cosmology|. + See ``TestCosmology`` for an example. + """ + + def test_to_mapping_default(self, cosmo, to_format): + """Test default usage of Cosmology -> mapping.""" + m = to_format("mapping") + keys = tuple(m.keys()) + + assert isinstance(m, dict) + # Check equality of all expected items + assert keys[0] == "cosmology" + assert m.pop("cosmology") is cosmo.__class__ + assert keys[1] == "name" + assert m.pop("name") == cosmo.name + for i, (k, v) in enumerate(cosmo.parameters.items(), start=2): + assert keys[i] == k + np.testing.assert_array_equal(m.pop(k), v) + assert keys[-1] == "meta" + assert m.pop("meta") == cosmo.meta + + # No unexpected items + assert not m + + def test_to_mapping_wrong_cls(self, to_format): + """Test incorrect argument ``cls`` in ``to_mapping()``.""" + with pytest.raises(TypeError, match="'cls' must be"): + to_format("mapping", cls=list) + + @pytest.mark.parametrize("map_cls", [dict, OrderedDict]) + def test_to_mapping_cls(self, to_format, map_cls): + """Test argument ``cls`` in ``to_mapping()``.""" + m = to_format("mapping", cls=map_cls) + assert isinstance(m, map_cls) # test type + + def test_to_mapping_cosmology_as_str(self, cosmo_cls, to_format): + """Test argument ``cosmology_as_str`` in ``to_mapping()``.""" + default = to_format("mapping") + + # Cosmology is the class + m = to_format("mapping", cosmology_as_str=False) + assert isinstance(m["cosmology"], type) + assert cosmo_cls is m["cosmology"] + + assert m == default # False is the default option + + # Cosmology is a string + m = to_format("mapping", cosmology_as_str=True) + assert isinstance(m["cosmology"], str) + assert m["cosmology"] == cosmo_cls.__qualname__ # Correct class + assert tuple(m.keys())[0] == "cosmology" # Stayed at same index + + def test_tofrom_mapping_cosmology_as_str(self, cosmo, to_format, from_format): + """Test roundtrip with ``cosmology_as_str=True``. + + The test for the default option (`False`) is in ``test_tofrom_mapping_instance``. + """ + m = to_format("mapping", cosmology_as_str=True) + + got = from_format(m, format="mapping") + assert got == cosmo + assert got.meta == cosmo.meta + + def test_to_mapping_move_from_meta(self, to_format): + """Test argument ``move_from_meta`` in ``to_mapping()``.""" + default = to_format("mapping") + + # Metadata is 'separate' from main mapping + m = to_format("mapping", move_from_meta=False) + assert "meta" in m.keys() + assert not any(k in m for k in m["meta"]) # Not added to main + + assert m == default # False is the default option + + # Metadata is mixed into main mapping. + m = to_format("mapping", move_from_meta=True) + assert "meta" not in m.keys() + assert all(k in m for k in default["meta"]) # All added to main + # The parameters take precedence over the metadata + assert all(np.array_equal(v, m[k]) for k, v in default.items() if k != "meta") + + def test_tofrom_mapping_move_tofrom_meta(self, cosmo, to_format, from_format): + """Test roundtrip of ``move_from/to_meta`` in ``to/from_mapping()``.""" + # Metadata is mixed into main mapping. + m = to_format("mapping", move_from_meta=True) + # (Just adding something to ensure there's 'metadata') + m["mismatching"] = "will error" + + # (Tests are different if the last argument is a **kwarg) + if cosmo._init_has_kwargs: + got = from_format(m, format="mapping") + + assert got.name == cosmo.name + assert "mismatching" not in got.meta + + return # don't continue testing + + # Reading with mismatching parameters errors... + with pytest.raises(TypeError, match="there are unused parameters"): + from_format(m, format="mapping") + + # unless mismatched are moved to meta. + got = from_format(m, format="mapping", move_to_meta=True) + assert got == cosmo # (Doesn't check metadata) + assert got.meta["mismatching"] == "will error" + + def test_to_mapping_rename_conflict(self, cosmo, to_format): + """Test ``rename`` in ``to_mapping()``.""" + to_rename = {"name": "name", "H0": "H_0"} + match = ( + "'renames' values must be disjoint from 'map' keys, " + "the common keys are: {'name'}" + ) + with pytest.raises(ValueError, match=match): + to_format("mapping", rename=to_rename) + + def test_from_mapping_rename_conflict(self, cosmo, to_format, from_format): + """Test ``rename`` in `from_mapping()``.""" + m = to_format("mapping") + + match = ( + "'renames' values must be disjoint from 'map' keys, " + "the common keys are: {'name'}" + ) + with pytest.raises(ValueError, match=match): + from_format(m, format="mapping", rename={"name": "name", "H0": "H_0"}) + + def test_tofrom_mapping_rename_roundtrip(self, cosmo, to_format, from_format): + """Test roundtrip in ``to/from_mapping()`` with ``rename``.""" + to_rename = {"name": "cosmo_name"} + m = to_format("mapping", rename=to_rename) + + assert "name" not in m + assert "cosmo_name" in m + + # Wrong names = error + with pytest.raises( + TypeError, match="there are unused parameters {'cosmo_name':" + ): + from_format(m, format="mapping") + + # Roundtrip. correct names = success + from_rename = {v: k for k, v in to_rename.items()} + got = from_format(m, format="mapping", rename=from_rename) + assert got == cosmo + + # ----------------------------------------------------- + + def test_from_not_mapping(self, cosmo, from_format): + """Test incorrect map type in ``from_mapping()``.""" + with pytest.raises((TypeError, ValueError)): + from_format("NOT A MAP", format="mapping") + + def test_from_mapping_default(self, cosmo, to_format, from_format): + """Test (cosmology -> Mapping) -> cosmology.""" + m = to_format("mapping") + + # Read from exactly as given. + got = from_format(m, format="mapping") + assert got == cosmo + assert got.meta == cosmo.meta + + # Reading auto-identifies 'format' + got = from_format(m) + assert got == cosmo + assert got.meta == cosmo.meta + + def test_fromformat_subclass_partial_info_mapping(self, cosmo): + """ + Test writing from an instance and reading from that class. + This works with missing information. + """ + m = cosmo.to_format("mapping") + + # partial information + m.pop("cosmology", None) + m.pop("Tcmb0", None) + + # read with the same class that wrote fills in the missing info with + # the default value + got = cosmo.__class__.from_format(m, format="mapping") + got2 = Cosmology.from_format(m, format="mapping", cosmology=cosmo.__class__) + got3 = Cosmology.from_format( + m, format="mapping", cosmology=cosmo.__class__.__qualname__ + ) + + assert (got == got2) and (got2 == got3) # internal consistency + + # not equal, because Tcmb0 is changed, which also changes m_nu + assert got != cosmo + assert got.Tcmb0 == cosmo.__class__.parameters["Tcmb0"].default + assert got.clone(name=cosmo.name, Tcmb0=cosmo.Tcmb0, m_nu=cosmo.m_nu) == cosmo + # but the metadata is the same + assert got.meta == cosmo.meta + + @pytest.mark.parametrize("format", [True, False, None, "mapping"]) + def test_is_equivalent_to_mapping(self, cosmo, to_format, format): + """Test :meth:`astropy.cosmology.Cosmology.is_equivalent`. + + This test checks that Cosmology equivalency can be extended to any + Python object that can be converted to a Cosmology -- in this case + a mapping. + """ + obj = to_format("mapping") + assert not isinstance(obj, Cosmology) + + is_equiv = cosmo.is_equivalent(obj, format=format) + assert is_equiv is (format is not False) + + +class TestToFromMapping(ToFromDirectTestBase, ToFromMappingTestMixin): + """Directly test ``to/from_mapping``.""" + + def setup_class(self): + self.functions = {"to": to_mapping, "from": from_mapping} + + @pytest.mark.skip("N/A") + def test_fromformat_subclass_partial_info_mapping(self): + """This test does not apply to the direct functions.""" diff --git a/astropy/cosmology/_src/tests/io/test_model.py b/astropy/cosmology/_src/tests/io/test_model.py new file mode 100644 index 000000000000..4fb0a64a93ef --- /dev/null +++ b/astropy/cosmology/_src/tests/io/test_model.py @@ -0,0 +1,182 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +import inspect +import random + +import numpy as np +import pytest + +from astropy.cosmology import Cosmology, w0wzCDM +from astropy.cosmology._src.io.builtin.model import ( + _CosmologyModel, + from_model, + to_model, +) +from astropy.cosmology._src.tests.helper import get_redshift_methods +from astropy.modeling.models import Gaussian1D +from astropy.utils.compat.optional_deps import HAS_SCIPY + +from .base import ToFromDirectTestBase, ToFromTestMixinBase + +############################################################################### + + +class ToFromModelTestMixin(ToFromTestMixinBase): + """Tests for a Cosmology[To/From]Format with ``format="astropy.model"``. + + This class will not be directly called by :mod:`pytest` since its name does + not begin with ``Test``. To activate the contained tests this class must + be inherited in a subclass. Subclasses must define a :func:`pytest.fixture` + ``cosmo`` that returns/yields an instance of a |Cosmology|. + See ``TestCosmologyToFromFormat`` or ``TestCosmology`` for examples. + """ + + @pytest.fixture(scope="class") + @classmethod + def method_name(cls, cosmo): + # get methods, ignoring private and dunder + methods = get_redshift_methods(cosmo, include_private=False, include_z2=True) + + # dynamically detect ABC and optional dependencies + for n in tuple(methods): + params = inspect.signature(getattr(cosmo, n)).parameters.keys() + + ERROR_SEIVE = (NotImplementedError, ValueError) + # # ABC can't introspect for good input + if not HAS_SCIPY: + ERROR_SEIVE = ERROR_SEIVE + (ModuleNotFoundError,) + + args = np.arange(len(params)) + 1 + try: + getattr(cosmo, n)(*args) + except ERROR_SEIVE: + methods.discard(n) + except TypeError: + # w0wzCDM has numerical instabilities when evaluating at z->inf + # TODO: a more robust fix in w0wzCDM itself would be better + if isinstance(cosmo, w0wzCDM): + methods.discard(n) + + # TODO! pytest doesn't currently allow multiple yields (`cosmo`) so + # testing with 1 random method + # yield from methods + return random.choice(tuple(methods)) if methods else None + + # =============================================================== + + def test_fromformat_model_wrong_cls(self, from_format): + """Test when Model is not the correct class.""" + model = Gaussian1D(amplitude=10, mean=14) + + with pytest.raises(AttributeError): + from_format(model) + + def test_toformat_model_not_method(self, to_format): + """Test when method is not a method.""" + with pytest.raises(AttributeError): + to_format("astropy.model", method="this is definitely not a method.") + + def test_toformat_model_not_callable(self, to_format): + """Test when method is actually an attribute.""" + with pytest.raises(ValueError): + to_format("astropy.model", method="name") + + def test_toformat_model(self, cosmo, to_format, method_name): + """Test cosmology -> astropy.model.""" + if method_name is None: # no test if no method + return + + model = to_format("astropy.model", method=method_name) + assert isinstance(model, _CosmologyModel) + + # Parameters + expect = tuple(k for k, v in cosmo.parameters.items() if v is not None) + assert model.param_names == expect + + # scalar result + args = np.arange(model.n_inputs) + 1 + + got = model.evaluate(*args) + expected = getattr(cosmo, method_name)(*args) + assert np.all(got == expected) + + got = model(*args) + expected = getattr(cosmo, method_name)(*args) + np.testing.assert_allclose(got, expected) + + # vector result + if "scalar" not in method_name: + args = (np.ones((model.n_inputs, 3)).T + np.arange(model.n_inputs)).T + + got = model.evaluate(*args) + expected = getattr(cosmo, method_name)(*args) + assert np.all(got == expected) + + got = model(*args) + expected = getattr(cosmo, method_name)(*args) + np.testing.assert_allclose(got, expected) + + def test_tofromformat_model_instance( + self, cosmo_cls, cosmo, method_name, to_format, from_format + ): + """Test cosmology -> astropy.model -> cosmology.""" + if method_name is None: # no test if no method + return + + # ------------ + # To Model + # this also serves as a test of all added methods / attributes + # in _CosmologyModel. + + model = to_format("astropy.model", method=method_name) + + assert isinstance(model, _CosmologyModel) + assert model.cosmology_class is cosmo_cls + assert model.cosmology == cosmo + assert model.method_name == method_name + + # ------------ + # From Model + + # it won't error if everything matches up + got = from_format(model, format="astropy.model") + assert got == cosmo + assert set(cosmo.meta.keys()).issubset(got.meta.keys()) + # Note: model adds parameter attributes to the metadata + + # also it auto-identifies 'format' + got = from_format(model) + assert got == cosmo + assert set(cosmo.meta.keys()).issubset(got.meta.keys()) + + def test_fromformat_model_subclass_partial_info(self) -> None: + """ + Test writing from an instance and reading from that class. + This works with missing information. + + There's no partial information with a Model + """ + + @pytest.mark.parametrize("format", [True, False, None, "astropy.model"]) + def test_is_equivalent_to_model(self, cosmo, method_name, to_format, format): + """Test :meth:`astropy.cosmology.Cosmology.is_equivalent`. + + This test checks that Cosmology equivalency can be extended to any + Python object that can be converted to a Cosmology -- in this case + a model. + """ + if method_name is None: # no test if no method + return + + obj = to_format("astropy.model", method=method_name) + assert not isinstance(obj, Cosmology) + + is_equiv = cosmo.is_equivalent(obj, format=format) + assert is_equiv is (format is not False) + + +class TestToFromModel(ToFromDirectTestBase, ToFromModelTestMixin): + """Directly test ``to/from_model``.""" + + def setup_class(self): + self.functions = {"to": to_model, "from": from_model} diff --git a/astropy/cosmology/_src/tests/io/test_mrt.py b/astropy/cosmology/_src/tests/io/test_mrt.py new file mode 100644 index 000000000000..d8224ec8abe0 --- /dev/null +++ b/astropy/cosmology/_src/tests/io/test_mrt.py @@ -0,0 +1,201 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +# THIRD PARTY + +import pytest + +from astropy.cosmology import Planck18 +from astropy.cosmology._src.io.builtin.mrt import ( + read_mrt, + write_mrt, +) +from astropy.table import QTable, Table + +from .base import ReadWriteDirectTestBase, ReadWriteTestMixinBase + + +class ReadWriteMRTTestMixin(ReadWriteTestMixinBase): + """ + Tests for a Cosmology[Read/Write] with ``format="mrt"``. + This class will not be directly called by :mod:`pytest` since its name does + not begin with ``Test``. To activate the contained tests this class must + be inherited in a subclass. Subclasses must define a :func:`pytest.fixture` + ``cosmo`` that returns/yields an instance of a |Cosmology|. + See ``TestCosmology`` for an example. + """ + + def test_to_mrt_bad_index(self, read, write, tmp_path): + """Test if argument ``index`` is incorrect""" + fp = tmp_path / "test_to_mrt_bad_index.mrt" + + write(fp, format="ascii.mrt") + + # single-row table and has a non-0/None index + with pytest.raises(IndexError, match="index 2 out of range"): + read(fp, index=2, format="ascii.mrt") + + # string index where doesn't match + with pytest.raises(KeyError, match="No matches found for key"): + read(fp, index="row 0", format="ascii.mrt") + + # ----------------------- + + def test_to_mrt_failed_cls(self, write, tmp_path): + """Test failed table type.""" + fp = tmp_path / "test_to_mrt_failed_cls.mrt" + + with pytest.raises(TypeError, match="'cls' must be"): + write(fp, format="ascii.mrt", cls=list) + + # ----------------------- + + @pytest.mark.parametrize("tbl_cls", [QTable, Table]) + def test_to_mrt_cls(self, write, tbl_cls, tmp_path): + fp = tmp_path / "test_to_mrt_cls.mrt" + write(fp, format="ascii.mrt", cls=tbl_cls) + + # ----------------------- + + def test_readwrite_mrt_instance(self, cosmo_cls, cosmo, read, write, tmp_path): + """Test cosmology -> ascii.mrt -> cosmology.""" + fp = tmp_path / "test_readwrite_mrt_instance.mrt" + + # ------------ + # To Table + + write(fp, format="ascii.mrt") + + # some checks on the saved file + tbl = Table.read(fp, format="ascii.mrt") + assert tbl["name"] == cosmo.name + + # ------------ + # From Table + + tbl["mismatching"] = "will error" + tbl.write(fp, format="ascii.mrt", overwrite=True) + + # tests are different if the last argument is a **kwarg + if cosmo._init_has_kwargs: + got = read(fp, format="ascii.mrt") + + assert got.__class__ is cosmo_cls + assert got.name == cosmo.name + assert "mismatching" not in got.meta + + return # don't continue testing + + # read with mismatching parameters errors + with pytest.raises(TypeError, match="there are unused parameters"): + read(fp, format="ascii.mrt") + + # unless mismatched are moved to meta + got = read(fp, format="ascii.mrt", move_to_meta=True) + assert got == cosmo + assert got.meta["mismatching"] == "will error" + + # it won't error if everything matches up + tbl.remove_column("mismatching") + tbl.write(fp, format="ascii.mrt", overwrite=True) + got = read(fp, format="ascii.mrt") + assert got == cosmo + + # ----------------------- + + def test_readwrite_mrt_subclass_partial_info( + self, cosmo_cls, cosmo, read, write, tmp_path + ): + """ + Test writing from an instance and reading from that class. + This works with missing information. + """ + fp = tmp_path / "test_read_mrt_subclass_partial_info.mrt" + + # test write + write(fp, format="ascii.mrt") + + # partial information + tbl = Table.read(fp, format="ascii.mrt") + del tbl["Tcmb0"] + tbl.write(fp, format="ascii.mrt", overwrite=True) + + # read with the same class that wrote fills in the missing info with + # the default value + got = cosmo_cls.read(fp, format="ascii.mrt") + got2 = read(fp, format="ascii.mrt", cosmology=cosmo_cls) + + assert got == got2 # internal consistency + + # not equal, because Tcmb0 is changed, which also changes m_nu + assert got != cosmo + assert got.Tcmb0 == cosmo_cls.parameters["Tcmb0"].default + assert got.clone(name=cosmo.name, Tcmb0=cosmo.Tcmb0, m_nu=cosmo.m_nu) == cosmo + + +class WriteMRTTestMixin(ReadWriteTestMixinBase): + def test_to_mrt_failed_cls(self, write, tmp_path): + """Test failed table type.""" + fp = tmp_path / "test_to_mrt_failed_cls.mrt" + + with pytest.raises(TypeError, match="'cls' must be"): + write(fp, format="ascii.mrt", cls=list) + + @pytest.mark.parametrize("tbl_cls", [QTable, Table]) + def test_to_mrt_cls(self, write, tbl_cls, tmp_path): + fp = tmp_path / "test_to_mrt_cls.mrt" + write(fp, format="ascii.mrt", cls=tbl_cls) + + def test_to_mrt_bad_index(self, read, write, tmp_path): + """Test if argument ``index`` is incorrect""" + fp = tmp_path / "test_to_mrt_bad_index.mrt" + + write(fp, format="ascii.mrt") + + # single-row table and has a non-0/None index + with pytest.raises(IndexError, match="index 2 out of range"): + read(fp, index=2, format="ascii.mrt") + + # string index where doesn't match + with pytest.raises(KeyError, match="No matches found for key"): + read(fp, index="row 0", format="ascii.mrt") + + def test_write_mrt_invalid_path(self, write): + """Test passing an invalid path""" + invalid_fp = "" + with pytest.raises(FileNotFoundError, match="No such file or directory"): + write(invalid_fp, format="ascii.mrt") + + def test_readwrite_mrt_instance(self, cosmo_cls, cosmo, read, write, tmp_path): + fp = tmp_path / "test_readwrite_mrt_instance.mrt" + write(fp, format="ascii.mrt") + tb1 = Table.read(fp, format="ascii.mrt") + assert tb1["name"] == cosmo.name + + +class TestReadWriteMRT(ReadWriteDirectTestBase, WriteMRTTestMixin): + """ + Directly test ``read/write_mrt``. + These are not public API and are discouraged from use, in favor of + ``Cosmology.read/write(..., format="mrt")``, but should be + tested regardless b/c they are used internally. + """ + + def setup_class(self): + self.functions = {"read": read_mrt, "write": write_mrt} + + +def test_write_mrt_invalid_format(tmp_path): + """Test passing an invalid format""" + fp = tmp_path / "test_write_mrt_invalid_format.mrt" + with pytest.raises(ValueError, match="format must be 'ascii.mrt'"): + write_mrt(Planck18, fp, format="ascii.ecsv") + + +def test_read_mrt_invalid_format(tmp_path): + """Test read_mrt with invalid format parameter.""" + fp = tmp_path / "test_read_mrt_invalid_format.mrt" + Planck18.write(fp, format="ascii.mrt") + + # Test that passing a different format raises ValueError + with pytest.raises(ValueError, match="format must be 'ascii.mrt'"): + read_mrt(fp, format="ascii.ecsv") diff --git a/astropy/cosmology/_src/tests/io/test_row.py b/astropy/cosmology/_src/tests/io/test_row.py new file mode 100644 index 000000000000..9bdcaf062cf6 --- /dev/null +++ b/astropy/cosmology/_src/tests/io/test_row.py @@ -0,0 +1,145 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +import pytest + +from astropy.cosmology._src.core import _COSMOLOGY_CLASSES, Cosmology +from astropy.cosmology._src.io.builtin.row import from_row, to_row +from astropy.table import Row + +from .base import ToFromDirectTestBase, ToFromTestMixinBase + +############################################################################### + + +class ToFromRowTestMixin(ToFromTestMixinBase): + """ + Tests for a Cosmology[To/From]Format with ``format="astropy.row"``. + This class will not be directly called by :mod:`pytest` since its name does + not begin with ``Test``. To activate the contained tests this class must + be inherited in a subclass. Subclasses must define a :func:`pytest.fixture` + ``cosmo`` that returns/yields an instance of a |Cosmology|. + See ``TestCosmologyToFromFormat`` or ``TestCosmology`` for examples. + """ + + @pytest.mark.parametrize("in_meta", [True, False]) + def test_to_row_in_meta(self, cosmo_cls, cosmo, in_meta): + """Test where the cosmology class is placed.""" + row = cosmo.to_format("astropy.row", cosmology_in_meta=in_meta) + + # if it's in metadata, it's not a column. And vice versa. + if in_meta: + assert row.meta["cosmology"] == cosmo_cls.__qualname__ + assert "cosmology" not in row.colnames # not also a column + else: + assert row["cosmology"] == cosmo_cls.__qualname__ + assert "cosmology" not in row.meta + + # ----------------------- + + def test_from_not_row(self, cosmo, from_format): + """Test not passing a Row to the Row parser.""" + with pytest.raises(AttributeError): + from_format("NOT A ROW", format="astropy.row") + + def test_tofrom_row_instance(self, cosmo, to_format, from_format): + """Test cosmology -> astropy.row -> cosmology.""" + # ------------ + # To Row + + row = to_format("astropy.row") + assert isinstance(row, Row) + assert row["cosmology"] == cosmo.__class__.__qualname__ + assert row["name"] == cosmo.name + + # ------------ + # From Row + + row.table["mismatching"] = "will error" + + # tests are different if the last argument is a **kwarg + if cosmo._init_has_kwargs: + got = from_format(row, format="astropy.row") + + assert got.__class__ is cosmo.__class__ + assert got.name == cosmo.name + assert "mismatching" not in got.meta + + return # don't continue testing + + # read with mismatching parameters errors + with pytest.raises(TypeError, match="there are unused parameters"): + from_format(row, format="astropy.row") + + # unless mismatched are moved to meta + got = from_format(row, format="astropy.row", move_to_meta=True) + assert got == cosmo + assert got.meta["mismatching"] == "will error" + + # it won't error if everything matches up + row.table.remove_column("mismatching") + got = from_format(row, format="astropy.row") + assert got == cosmo + + # and it will also work if the cosmology is a class + # Note this is not the default output of ``to_format``. + cosmology = _COSMOLOGY_CLASSES[row["cosmology"]] + row.table.remove_column("cosmology") + row.table["cosmology"] = cosmology + got = from_format(row, format="astropy.row") + assert got == cosmo + + # also it auto-identifies 'format' + got = from_format(row) + assert got == cosmo + + def test_tofrom_row_rename(self, cosmo, to_format, from_format): + """Test renaming columns in row.""" + rename = {"name": "cosmo_name"} + row = to_format("astropy.row", rename=rename) + + assert "name" not in row.colnames + assert "cosmo_name" in row.colnames + + # Error if just reading + with pytest.raises(TypeError, match="there are unused parameters"): + from_format(row) + + # Roundtrip + inv_rename = {v: k for k, v in rename.items()} + got = from_format(row, rename=inv_rename) + assert got == cosmo + + def test_fromformat_row_subclass_partial_info(self, cosmo: Cosmology) -> None: + """ + Test writing from an instance and reading from that class. + This works with missing information. + + There are no partial info options + """ + + @pytest.mark.parametrize("format", [True, False, None, "astropy.row"]) + def test_is_equivalent_to_row(self, cosmo, to_format, format): + """Test :meth:`astropy.cosmology.Cosmology.is_equivalent`. + + This test checks that Cosmology equivalency can be extended to any + Python object that can be converted to a Cosmology -- in this case + a Row. + """ + obj = to_format("astropy.row") + assert not isinstance(obj, Cosmology) + + is_equiv = cosmo.is_equivalent(obj, format=format) + assert is_equiv is (format is not False) + + +class TestToFromRow(ToFromDirectTestBase, ToFromRowTestMixin): + """ + Directly test ``to/from_row``. + These are not public API and are discouraged from use, in favor of + ``Cosmology.to/from_format(..., format="astropy.row")``, but should be + tested regardless b/c 3rd party packages might use these in their Cosmology + I/O. Also, it's cheap to test. + """ + + def setup_class(self): + self.functions = {"to": to_row, "from": from_row} diff --git a/astropy/cosmology/_src/tests/io/test_table.py b/astropy/cosmology/_src/tests/io/test_table.py new file mode 100644 index 000000000000..1380839c6c78 --- /dev/null +++ b/astropy/cosmology/_src/tests/io/test_table.py @@ -0,0 +1,258 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +import pytest + +from astropy.cosmology import Cosmology +from astropy.cosmology._src.core import _COSMOLOGY_CLASSES +from astropy.cosmology._src.io.builtin.table import from_table, to_table +from astropy.table import QTable, Table, vstack + +from .base import ToFromDirectTestBase, ToFromTestMixinBase + +############################################################################### + + +class ToFromTableTestMixin(ToFromTestMixinBase): + """ + Tests for a Cosmology[To/From]Format with ``format="astropy.table"``. + This class will not be directly called by :mod:`pytest` since its name does + not begin with ``Test``. To activate the contained tests this class must + be inherited in a subclass. Subclasses must define a :func:`pytest.fixture` + ``cosmo`` that returns/yields an instance of a |Cosmology|. + See ``TestCosmology`` for an example. + """ + + def test_to_table_bad_index(self, from_format, to_format): + """Test if argument ``index`` is incorrect""" + tbl = to_format("astropy.table") + + # single-row table and has a non-0/None index + with pytest.raises(IndexError, match="index 2 out of range"): + from_format(tbl, index=2, format="astropy.table") + + # string index where doesn't match + with pytest.raises(KeyError, match="No matches found for key"): + from_format(tbl, index="row 0", format="astropy.table") + + # ----------------------- + + def test_to_table_failed_cls(self, to_format): + """Test failed table type.""" + with pytest.raises(TypeError, match="'cls' must be"): + to_format("astropy.table", cls=list) + + @pytest.mark.parametrize("tbl_cls", [QTable, Table]) + def test_to_table_cls(self, to_format, tbl_cls): + tbl = to_format("astropy.table", cls=tbl_cls) + assert isinstance(tbl, tbl_cls) # test type + + # ----------------------- + + @pytest.mark.parametrize("in_meta", [True, False]) + def test_to_table_in_meta(self, cosmo_cls, to_format, in_meta): + """Test where the cosmology class is placed.""" + tbl = to_format("astropy.table", cosmology_in_meta=in_meta) + + # if it's in metadata, it's not a column. And vice versa. + if in_meta: + assert tbl.meta["cosmology"] == cosmo_cls.__qualname__ + assert "cosmology" not in tbl.colnames # not also a column + else: + assert tbl["cosmology"][0] == cosmo_cls.__qualname__ + assert "cosmology" not in tbl.meta + + # ----------------------- + + def test_to_table(self, cosmo_cls, cosmo, to_format): + """Test cosmology -> astropy.table.""" + tbl = to_format("astropy.table") + + # Test properties of Table. + assert isinstance(tbl, QTable) + assert tbl.meta["cosmology"] == cosmo_cls.__qualname__ + assert tbl["name"] == cosmo.name + assert tbl.indices # indexed + + # Test each Parameter column has expected information. + for n, P in cosmo_cls.parameters.items(): + col = tbl[n] # Column + + # Compare the two + assert col.info.name == P.name + assert col.info.description == P.__doc__ + assert col.info.meta == (cosmo.meta.get(n) or {}) + + # ----------------------- + + def test_from_not_table(self, cosmo, from_format): + """Test not passing a Table to the Table parser.""" + with pytest.raises((TypeError, ValueError)): + from_format("NOT A TABLE", format="astropy.table") + + def test_tofrom_table_instance(self, cosmo_cls, cosmo, from_format, to_format): + """Test cosmology -> astropy.table -> cosmology.""" + tbl = to_format("astropy.table") + + # add information + tbl["mismatching"] = "will error" + + # tests are different if the last argument is a **kwarg + if cosmo._init_has_kwargs: + got = from_format(tbl, format="astropy.table") + + assert got.__class__ is cosmo_cls + assert got.name == cosmo.name + assert "mismatching" not in got.meta + + return # don't continue testing + + # read with mismatching parameters errors + with pytest.raises(TypeError, match="there are unused parameters"): + from_format(tbl, format="astropy.table") + + # unless mismatched are moved to meta + got = from_format(tbl, format="astropy.table", move_to_meta=True) + assert got == cosmo + assert got.meta["mismatching"] == "will error" + + # it won't error if everything matches up + tbl.remove_column("mismatching") + got = from_format(tbl, format="astropy.table") + assert got == cosmo + + # and it will also work if the cosmology is a class + # Note this is not the default output of ``to_format``. + tbl.meta["cosmology"] = _COSMOLOGY_CLASSES[tbl.meta["cosmology"]] + got = from_format(tbl, format="astropy.table") + assert got == cosmo + + # also it auto-identifies 'format' + got = from_format(tbl) + assert got == cosmo + + def test_fromformat_table_subclass_partial_info( + self, cosmo_cls, cosmo, from_format, to_format + ): + """ + Test writing from an instance and reading from that class. + This works with missing information. + """ + # test to_format + tbl = to_format("astropy.table") + assert isinstance(tbl, QTable) + + # partial information + tbl.meta.pop("cosmology", None) + del tbl["Tcmb0"] + + # read with the same class that wrote fills in the missing info with + # the default value + got = cosmo_cls.from_format(tbl, format="astropy.table") + got2 = from_format(tbl, format="astropy.table", cosmology=cosmo_cls) + got3 = from_format( + tbl, format="astropy.table", cosmology=cosmo_cls.__qualname__ + ) + + assert (got == got2) and (got2 == got3) # internal consistency + + # not equal, because Tcmb0 is changed, which also changes m_nu + assert got != cosmo + assert got.Tcmb0 == cosmo_cls.parameters["Tcmb0"].default + assert got.clone(name=cosmo.name, Tcmb0=cosmo.Tcmb0, m_nu=cosmo.m_nu) == cosmo + # but the metadata is the same + assert got.meta == cosmo.meta + + @pytest.mark.parametrize("add_index", [True, False]) + def test_tofrom_table_mutlirow(self, cosmo_cls, cosmo, from_format, add_index): + """Test if table has multiple rows.""" + # ------------ + # To Table + + cosmo1 = cosmo.clone(name="row 0") + cosmo2 = cosmo.clone(name="row 2") + tbl = vstack( + [c.to_format("astropy.table") for c in (cosmo1, cosmo, cosmo2)], + metadata_conflicts="silent", + ) + + assert isinstance(tbl, QTable) + assert tbl.meta["cosmology"] == cosmo_cls.__qualname__ + assert tbl[1]["name"] == cosmo.name + + # whether to add an index. `from_format` can work with or without. + if add_index: + tbl.add_index("name", unique=True) + + # ------------ + # From Table + + # it will error on a multi-row table + with pytest.raises(ValueError, match="need to select a specific row"): + from_format(tbl, format="astropy.table") + + # unless the index argument is provided + got = from_format(tbl, index=1, format="astropy.table") + assert got == cosmo + + # the index can be a string + got = from_format(tbl, index=cosmo.name, format="astropy.table") + assert got == cosmo + + # when there's more than one cosmology found + tbls = vstack([tbl, tbl], metadata_conflicts="silent") + with pytest.raises(ValueError, match="more than one"): + from_format(tbls, index=cosmo.name, format="astropy.table") + + def test_tofrom_table_rename(self, cosmo, to_format, from_format): + """Test renaming columns in row.""" + rename = {"name": "cosmo_name"} + table = to_format("astropy.table", rename=rename) + + assert "name" not in table.colnames + assert "cosmo_name" in table.colnames + + # Error if just reading + with pytest.raises(TypeError, match="there are unused parameters"): + from_format(table) + + # Roundtrip + inv_rename = {v: k for k, v in rename.items()} + got = from_format(table, rename=inv_rename) + assert got == cosmo + + def test_from_table_renamed_index_column(self, cosmo, to_format, from_format): + """Test reading from a table with a renamed index column.""" + cosmo1 = cosmo.clone(name="row 0") + cosmo2 = cosmo.clone(name="row 2") + tbl = vstack( + [c.to_format("astropy.table") for c in (cosmo1, cosmo, cosmo2)], + metadata_conflicts="silent", + ) + tbl.rename_column("name", "cosmo_name") + + inv_rename = {"cosmo_name": "name"} + newcosmo = from_format( + tbl, index="row 0", rename=inv_rename, format="astropy.table" + ) + assert newcosmo == cosmo1 + + @pytest.mark.parametrize("format", [True, False, None, "astropy.table"]) + def test_is_equivalent_to_table(self, cosmo, to_format, format): + """Test :meth:`astropy.cosmology.Cosmology.is_equivalent`. + + This test checks that Cosmology equivalency can be extended to any + Python object that can be converted to a Cosmology -- in this case + a |Table|. + """ + obj = to_format("astropy.table") + assert not isinstance(obj, Cosmology) + + is_equiv = cosmo.is_equivalent(obj, format=format) + assert is_equiv is (format is not False) + + +class TestToFromTable(ToFromDirectTestBase, ToFromTableTestMixin): + """Directly test ``to/from_table``.""" + + def setup_class(self): + self.functions = {"to": to_table, "from": from_table} diff --git a/astropy/cosmology/_src/tests/io/test_yaml.py b/astropy/cosmology/_src/tests/io/test_yaml.py new file mode 100644 index 000000000000..4ddfc8f6f6be --- /dev/null +++ b/astropy/cosmology/_src/tests/io/test_yaml.py @@ -0,0 +1,184 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +import pytest + +import astropy.units as u +from astropy.cosmology import Cosmology, FlatLambdaCDM, Planck18 +from astropy.cosmology import units as cu +from astropy.cosmology._src.io.builtin.yaml import ( + from_yaml, + to_yaml, + yaml_constructor, + yaml_representer, +) +from astropy.io.misc.yaml import AstropyDumper, dump, load + +from .base import ToFromDirectTestBase, ToFromTestMixinBase + +############################################################################## +# Test Serializer + + +def test_yaml_representer(): + """Test :func:`~astropy.cosmology._src.io.builtin.yaml.yaml_representer`.""" + # test function `representer` + representer = yaml_representer("!astropy.cosmology.LambdaCDM") + assert callable(representer) + + # test the normal method of dumping to YAML + yml = dump(Planck18) + assert isinstance(yml, str) + assert yml.startswith("!astropy.cosmology.FlatLambdaCDM") + + +def test_yaml_constructor(): + """Test :func:`~astropy.cosmology._src.io.builtin.yaml.yaml_constructor`.""" + # test function `constructor` + constructor = yaml_constructor(FlatLambdaCDM) + assert callable(constructor) + + # it's too hard to manually construct a node, so we only test dump/load + # this is also a good round-trip test + yml = dump(Planck18) + with u.add_enabled_units(cu): # needed for redshift units + cosmo = load(yml) + assert isinstance(cosmo, FlatLambdaCDM) + assert cosmo == Planck18 + assert cosmo.meta == Planck18.meta + + +############################################################################## +# Test Unified I/O + + +class ToFromYAMLTestMixin(ToFromTestMixinBase): + """ + Tests for a Cosmology[To/From]Format with ``format="yaml"``. + This class will not be directly called by :mod:`pytest` since its name does + not begin with ``Test``. To activate the contained tests this class must + be inherited in a subclass. Subclasses must define a :func:`pytest.fixture` + ``cosmo`` that returns/yields an instance of a |Cosmology|. + See ``TestCosmologyToFromFormat`` or ``TestCosmology`` for examples. + """ + + @pytest.fixture + def xfail_if_not_registered_with_yaml(self, cosmo_cls): + """ + YAML I/O only works on registered classes. So the thing to check is + if this class is registered. If not, :func:`pytest.xfail` this test. + Some of the tests define custom cosmologies. They are not registered. + """ + if cosmo_cls not in AstropyDumper.yaml_representers: + pytest.xfail( + f"Cosmologies of type {cosmo_cls} are not registered with YAML." + ) + + # =============================================================== + + def test_to_yaml(self, cosmo_cls, to_format, xfail_if_not_registered_with_yaml): + """Test cosmology -> YAML.""" + yml = to_format("yaml") + + assert isinstance(yml, str) # test type + assert yml.startswith("!" + ".".join(cosmo_cls.__module__.split(".")[:2])) + # e.g. "astropy.cosmology" for built-in cosmologies, or "__main__" for the test + # SubCosmology class defined in ``astropy.cosmology._src.tests.test_core``. + + def test_from_yaml_default( + self, cosmo, to_format, from_format, xfail_if_not_registered_with_yaml + ): + """Test cosmology -> YAML -> cosmology.""" + yml = to_format("yaml") + + got = from_format(yml, format="yaml") # (cannot autoidentify) + + assert got.name == cosmo.name + assert got.meta == cosmo.meta + + # it won't error if everything matches up + got = from_format(yml, format="yaml") + assert got == cosmo + assert got.meta == cosmo.meta + + # auto-identify test moved because it doesn't work. + # see test_from_yaml_autoidentify + + def test_from_yaml_autoidentify( + self, cosmo, to_format, from_format, xfail_if_not_registered_with_yaml + ): + """As a non-path string, it does NOT auto-identifies 'format'. + + TODO! this says there should be different types of I/O registries. + not just hacking object conversion on top of file I/O. + """ + assert self.can_autodentify("yaml") is False + + # Showing the specific error. The str is interpreted as a file location + # but is too long a file name. + yml = to_format("yaml") + with pytest.raises((FileNotFoundError, OSError)): # OSError in Windows + from_format(yml) + + # # TODO! this is a challenging test to write. It's also unlikely to happen. + # def test_fromformat_subclass_partial_info_yaml(self, cosmo): + # """ + # Test writing from an instance and reading from that class. + # This works with missing information. + # """ + + # ----------------------------------------------------- + + @pytest.mark.parametrize("format", [True, False, None]) + def test_is_equivalent_to_yaml( + self, cosmo, to_format, format, xfail_if_not_registered_with_yaml + ): + """Test :meth:`astropy.cosmology.Cosmology.is_equivalent`. + + This test checks that Cosmology equivalency can be extended to any + Python object that can be converted to a Cosmology -- in this case + a YAML string. YAML can't be identified without "format" specified. + """ + obj = to_format("yaml") + assert not isinstance(obj, Cosmology) + + is_equiv = cosmo.is_equivalent(obj, format=format) + assert is_equiv is False + + def test_is_equivalent_to_yaml_specify_format( + self, cosmo, to_format, xfail_if_not_registered_with_yaml + ): + """Test :meth:`astropy.cosmology.Cosmology.is_equivalent`. + + Same as ``test_is_equivalent_to_yaml`` but with ``format="yaml"``. + """ + assert cosmo.is_equivalent(to_format("yaml"), format="yaml") is True + + +class TestToFromYAML(ToFromDirectTestBase, ToFromYAMLTestMixin): + """ + Directly test ``to/from_yaml``. + These are not public API and are discouraged from use, in favor of + ``Cosmology.to/from_format(..., format="yaml")``, but should be tested + regardless b/c 3rd party packages might use these in their Cosmology I/O. + Also, it's cheap to test. + """ + + def setup_class(self): + """Set up fixtures to use ``to/from_yaml``, not the I/O abstractions.""" + self.functions = {"to": to_yaml, "from": from_yaml} + + @pytest.fixture(scope="class", autouse=True) + @classmethod + def setup(cls): + """ + Setup and teardown for tests. + This overrides from super because `ToFromDirectTestBase` adds a custom + Cosmology ``CosmologyWithKwargs`` that is not registered with YAML. + """ + return # run tests + + def test_from_yaml_autoidentify(self, cosmo, to_format, from_format): + """ + If directly calling the function there's no auto-identification. + So this overrides the test from `ToFromYAMLTestMixin` + """ diff --git a/astropy/cosmology/_src/tests/parameter/__init__.py b/astropy/cosmology/_src/tests/parameter/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/astropy/cosmology/_src/tests/parameter/conftest.py b/astropy/cosmology/_src/tests/parameter/conftest.py new file mode 100644 index 000000000000..4fd6dcdefd16 --- /dev/null +++ b/astropy/cosmology/_src/tests/parameter/conftest.py @@ -0,0 +1,6 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +"""Configure the tests for :mod:`astropy._src.cosmology.parameter`.""" + +from astropy.cosmology._src.tests.helper import clean_registry # noqa: F401 +from astropy.tests.helper import pickle_protocol # noqa: F401 diff --git a/astropy/cosmology/_src/tests/parameter/test_descriptors.py b/astropy/cosmology/_src/tests/parameter/test_descriptors.py new file mode 100644 index 000000000000..610396d36ce6 --- /dev/null +++ b/astropy/cosmology/_src/tests/parameter/test_descriptors.py @@ -0,0 +1,129 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +"""Testing :mod:`astropy.cosmology._src.parameter.descriptor`.""" + +from types import MappingProxyType +from typing import ClassVar + +import pytest + +from astropy.cosmology import Cosmology, Parameter +from astropy.cosmology._src.parameter import ParametersAttribute, all_parameters + + +class Obj: + """Example class with a ParametersAttribute.""" + + # Attributes that will be accessed by ParametersAttribute when called an instance + # of this class. On a Cosmology these would be the Parameter objects. + a: ClassVar[int] = 1 + b: ClassVar[int] = 2 + c: ClassVar[int] = 3 + # The class attribute that is accessed by ParametersAttribute when called on the + # class. On a Cosmology this would be the mapping of Parameter objects. + # Here it is just the names of the attributes that will be accessed by the + # ParametersAttribute to better distinguish between the class and instance + # attributes. + _attr_map: ClassVar[tuple[str, ...]] = ("a", "b", "c") + + # The ParametersAttribute descriptor. This will return a mapping of the values of + # the attributes listed in ``_attr_map`` when called on an instance of this class. + # When called on the class, it will return ``_attr_map`` itself. + attr = ParametersAttribute(attr_name="_attr_map") + + +class TestParametersAttribute: + """Test the descriptor ``ParametersAttribute``.""" + + def test_init(self) -> None: + """Test constructing a ParametersAttribute.""" + # Proper construction + attr = ParametersAttribute("attr_name") + assert attr.attr_name == "attr_name" + + # Improper construction + # There isn't type checking on the attr_name, so this is allowed, but will fail + # later when the descriptor is used. + attr = ParametersAttribute(1) # type: ignore[arg-type] + assert attr.attr_name == 1 + + def test_get_from_class(self) -> None: + """Test the descriptor ``__get__`` from the class.""" + assert Obj.attr == ("a", "b", "c") + + def test_get_from_instance(self) -> None: + """Test the descriptor ``__get__``.""" + obj = Obj() # Construct an instance for the attribute `attr`. + assert isinstance(obj.attr, MappingProxyType) + assert tuple(obj.attr.keys()) == obj._attr_map + + def test_set_from_instance(self) -> None: + """Test the descriptor ``__set__``.""" + obj = Obj() # Construct an instance for the attribute `attr`. + with pytest.raises(AttributeError, match="cannot set 'attr' of"): + obj.attr = {} + + def test_descriptor_attr_name_not_str(self) -> None: + """Test when ``attr_name`` is not a string and used as a descriptor. + + This is a regression test for #15882. + """ + + class Obj2(Obj): + attr = ParametersAttribute(attr_name=None) # type: ignore[arg-type] + + obj = Obj2() + with pytest.raises( + TypeError, match=r"attribute name must be string, not 'NoneType'" + ): + _ = obj.attr + + +############################################################################## + + +class ParametersAttributeTestMixin: + """Test the descriptor for ``parameters`` on Cosmology classes. + + This is a mixin class and is mixed into + :class:`~astropy.cosmology._src.tests.test_core.CosmologyTest`. + """ + + @pytest.mark.parametrize("name", ["parameters", "_derived_parameters"]) + def test_parameters_from_class(self, cosmo_cls: type[Cosmology], name: str) -> None: + """Test descriptor ``parameters`` accessed from the class.""" + # test presence + assert hasattr(cosmo_cls, name) + # test Parameter is a MappingProxyType + parameters = getattr(cosmo_cls, name) + assert isinstance(parameters, MappingProxyType) + # Test items + assert all(isinstance(p, Parameter) for p in parameters.values()) + assert set(parameters) == { + k + for k, v in all_parameters(cosmo_cls).items() + if v.derived == ("derived" in name) + } + + @pytest.mark.parametrize("name", ["parameters", "_derived_parameters"]) + def test_parameters_from_instance(self, cosmo: Cosmology, name: str) -> None: + """Test descriptor ``parameters`` accessed from the instance.""" + # test presence + assert hasattr(cosmo, name) + # test Parameter is a MappingProxyType + parameters = getattr(cosmo, name) + assert isinstance(parameters, MappingProxyType) + # Test keys + assert set(parameters) == { + k + for k, v in all_parameters(cosmo).items() + if (v.derived == ("derived" in name)) + } + + @pytest.mark.parametrize("name", ["parameters", "_derived_parameters"]) + def test_parameters_cannot_set_on_instance( + self, cosmo: Cosmology, name: str + ) -> None: + """Test descriptor ``parameters`` cannot be set on the instance.""" + with pytest.raises(AttributeError, match=f"cannot assign to field {name!r}"): + setattr(cosmo, name, {}) diff --git a/astropy/cosmology/_src/tests/parameter/test_parameter.py b/astropy/cosmology/_src/tests/parameter/test_parameter.py new file mode 100644 index 000000000000..a1646a694578 --- /dev/null +++ b/astropy/cosmology/_src/tests/parameter/test_parameter.py @@ -0,0 +1,447 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +"""Testing :mod:`astropy.cosmology._src.parameter`.""" + +from collections.abc import Callable + +import numpy as np +import pytest + +import astropy.units as u +from astropy.cosmology import Cosmology, Parameter +from astropy.cosmology._src.core import _COSMOLOGY_CLASSES, dataclass_decorator +from astropy.cosmology._src.parameter.converter import ( + _REGISTRY_FVALIDATORS, + validate_with_unit, +) +from astropy.cosmology._src.parameter.core import MISSING + +############################################################################## + + +def test_registry_validators(): + """Test :class:`astropy.cosmology.Parameter` attributes on class.""" + # _registry_validators + assert isinstance(_REGISTRY_FVALIDATORS, dict) + assert all(isinstance(k, str) for k in _REGISTRY_FVALIDATORS.keys()) + assert all(callable(v) for v in _REGISTRY_FVALIDATORS.values()) + + +class Test_Parameter: + """Test :class:`astropy.cosmology.Parameter` not on a cosmology.""" + + @pytest.mark.parametrize( + "kwargs", + [ + {}, + dict( + default=1.0, + fvalidate="float", + doc="DOCSTRING", + unit="km", + equivalencies=[u.mass_energy()], + derived=True, + ), + ], + ) + def test_Parameter_init(self, kwargs): + """Test :class:`astropy.cosmology.Parameter` instantiation.""" + unit = kwargs.get("unit") + + param = Parameter(**kwargs) + assert param.default == kwargs.get("default", MISSING) + assert param.fvalidate is _REGISTRY_FVALIDATORS.get( + kwargs.get("fvalidate"), validate_with_unit + ) + assert param.doc == kwargs.get("doc") + assert param.unit is (u.Unit(unit) if unit is not None else None) + assert param.equivalencies == kwargs.get("equivalencies", []) + assert param.derived is kwargs.get("derived", False) + assert param.name == "name not initialized" + + def test_Parameter_default(self): + """Test :attr:`astropy.cosmology.Parameter.default`.""" + parameter = Parameter() + assert parameter.default is MISSING + assert repr(parameter.default) == "" + + +class ParameterTestMixin: + """Tests for a :class:`astropy.cosmology.Parameter` on a Cosmology. + + :class:`astropy.cosmology.Parameter` is a descriptor and this test suite + tests descriptors by class inheritance, so ``ParameterTestMixin`` is mixed + into ``TestCosmology`` (tests :class:`astropy.cosmology.Cosmology`). + """ + + @pytest.fixture + def parameter(self, cosmo_cls): + """Cosmological Parameters""" + yield from cosmo_cls.parameters.values() + + @pytest.fixture + def all_parameter(self, cosmo_cls): + """Cosmological All Parameter instances""" + # just return one parameter at random + n = set(cosmo_cls._parameters_all).pop() + try: + yield cosmo_cls.parameters[n] + except KeyError: + yield cosmo_cls._derived_parameters[n] + + # =============================================================== + # Method Tests + + def test_Parameter_instance_attributes(self, all_parameter): + """Test :class:`astropy.cosmology.Parameter` attributes from init.""" + assert hasattr(all_parameter, "fvalidate") + assert callable(all_parameter.fvalidate) + + assert hasattr(all_parameter, "__doc__") + + # Parameter + assert hasattr(all_parameter, "_unit") + assert hasattr(all_parameter, "equivalencies") + assert hasattr(all_parameter, "derived") + + # __set_name__ + assert hasattr(all_parameter, "name") + + def test_Parameter_fvalidate(self, all_parameter): + """Test :attr:`astropy.cosmology.Parameter.fvalidate`.""" + assert hasattr(all_parameter, "fvalidate") + assert callable(all_parameter.fvalidate) + assert hasattr(all_parameter, "_fvalidate_in") + assert isinstance(all_parameter._fvalidate_in, (str, Callable)) + + def test_Parameter_name(self, all_parameter): + """Test :attr:`astropy.cosmology.Parameter.name`.""" + assert hasattr(all_parameter, "name") + assert isinstance(all_parameter.name, str) + + def test_Parameter_unit(self, all_parameter): + """Test :attr:`astropy.cosmology.Parameter.unit`.""" + assert hasattr(all_parameter, "unit") + assert isinstance(all_parameter.unit, (u.UnitBase, type(None))) + assert all_parameter.unit is all_parameter._unit + + def test_Parameter_equivalencies(self, all_parameter): + """Test :attr:`astropy.cosmology.Parameter.equivalencies`.""" + assert hasattr(all_parameter, "equivalencies") + assert isinstance(all_parameter.equivalencies, (list, u.Equivalency)) + + def test_Parameter_derived(self, cosmo_cls, all_parameter): + """Test :attr:`astropy.cosmology.Parameter.derived`.""" + assert hasattr(all_parameter, "derived") + assert isinstance(all_parameter.derived, bool) + + # test value + assert all_parameter.derived is (all_parameter.name not in cosmo_cls.parameters) + + def test_Parameter_default(self, cosmo_cls, all_parameter): + """Test :attr:`astropy.cosmology.Parameter.default`.""" + assert hasattr(all_parameter, "default") + assert all_parameter.default is MISSING or isinstance( + all_parameter.default, (type(None), int, float, u.Quantity) + ) + + # ------------------------------------------- + # descriptor methods + + def test_Parameter_descriptor_get(self, cosmo_cls, cosmo, all_parameter): + """Test :attr:`astropy.cosmology.Parameter.__get__`.""" + # from class + np.testing.assert_array_equal( + getattr(cosmo_cls, all_parameter.name).default, all_parameter.default + ) + + # from instance + parameter = getattr(cosmo, all_parameter.name) + assert np.all(parameter == cosmo.__dict__[all_parameter.name]) + + def test_Parameter_descriptor_set(self, cosmo, all_parameter): + """Test :attr:`astropy.cosmology.Parameter.__set__`.""" + # test it's already set + assert all_parameter.name in cosmo.__dict__ + + # ------------------------------------------- + # validate value + # tested later. + + # =============================================================== + # Usage Tests + + def test_Parameter_listed(self, cosmo_cls, all_parameter): + """Test each `astropy.cosmology.Parameter` attached to Cosmology.""" + # just double check that each entry is a Parameter + assert isinstance(all_parameter, Parameter) + + # the reverse: check that if it is a Parameter, it's listed. + if all_parameter.derived: + assert all_parameter.name in cosmo_cls._derived_parameters + else: + assert all_parameter.name in cosmo_cls.parameters + + +# ======================================================================== + + +class TestParameter(ParameterTestMixin): + """ + Test `astropy.cosmology.Parameter` directly. Adds a lot of specific tests + that wouldn't be covered by the per-cosmology tests. + """ + + def setup_class(self): + theparam = Parameter( + default=15, + doc="Description of example parameter.", + unit=u.m, + equivalencies=u.mass_energy(), + ) + + @dataclass_decorator + class Example1(Cosmology): + param: Parameter = theparam.clone() + + @property + def is_flat(self) -> bool: + return super().is_flat() + + # with validator + @dataclass_decorator + class Example2(Example1): + param: Parameter = theparam.clone(default=15 * u.m) + + @param.validator + def param(self, param, value): + return value.to(u.km) + + # attributes + self.classes = {"Example1": Example1, "Example2": Example2} + + def teardown_class(self): + for cls in self.classes.values(): + _COSMOLOGY_CLASSES.pop(cls.__qualname__, None) + + @pytest.fixture(scope="class", params=["Example1", "Example2"]) + @classmethod + def cosmo_cls(cls, request): + """Cosmology class.""" + return cls.classes[request.param] + + @pytest.fixture(scope="class") + @classmethod + def cosmo(cls, cosmo_cls): + """Cosmology instance""" + return cosmo_cls() + + @pytest.fixture(scope="class") + @classmethod + def param(cls, cosmo_cls): + """Get Parameter 'param' from cosmology class.""" + return cosmo_cls.parameters["param"] + + @pytest.fixture(scope="class") + @classmethod + def param_cls(cls, param): + """Get Parameter class from cosmology class.""" + return type(param) + + # ============================================================== + + def test_Parameter_instance_attributes(self, param): + """Test :class:`astropy.cosmology.Parameter` attributes from init.""" + super().test_Parameter_instance_attributes(param) + + # property + assert param.__doc__ == "Description of example parameter." + + # custom from init + assert param.unit == u.m + assert param.equivalencies == u.mass_energy() + assert param.derived == np.False_ + + # custom from set_name + assert param.name == "param" + + def test_Parameter_fvalidate(self, cosmo, param): + """Test :attr:`astropy.cosmology.Parameter.fvalidate`.""" + super().test_Parameter_fvalidate(param) + + value = param.fvalidate(cosmo, param, 1000 * u.m) + assert value == 1 * u.km + + def test_Parameter_name(self, param): + """Test :attr:`astropy.cosmology.Parameter.name`.""" + super().test_Parameter_name(param) + + assert param.name == "param" + + def test_Parameter_unit(self, param): + """Test :attr:`astropy.cosmology.Parameter.unit`.""" + super().test_Parameter_unit(param) + + assert param.unit == u.m + + def test_Parameter_equivalencies(self, param): + """Test :attr:`astropy.cosmology.Parameter.equivalencies`.""" + super().test_Parameter_equivalencies(param) + + assert param.equivalencies == u.mass_energy() + + def test_Parameter_derived(self, cosmo_cls, param): + """Test :attr:`astropy.cosmology.Parameter.derived`.""" + super().test_Parameter_derived(cosmo_cls, param) + + assert param.derived is False + + # ------------------------------------------- + # descriptor methods + + def test_Parameter_descriptor_get(self, cosmo_cls, cosmo, param): + """Test :meth:`astropy.cosmology.Parameter.__get__`.""" + super().test_Parameter_descriptor_get(cosmo_cls, cosmo, param) + + # from instance + value = getattr(cosmo, param.name) + assert value == 15 * u.m + + # ------------------------------------------- + # validation + + def test_Parameter_validator(self, param): + """Test :meth:`astropy.cosmology.Parameter.validator`.""" + for k in _REGISTRY_FVALIDATORS: + newparam = param.validator(k) + assert newparam.fvalidate == _REGISTRY_FVALIDATORS[k] + + # error for non-registered str + with pytest.raises(ValueError, match="`fvalidate`, if str"): + Parameter(fvalidate="NOT REGISTERED") + + # error if wrong type + with pytest.raises(TypeError, match="`fvalidate` must be a function or"): + Parameter(fvalidate=object()) + + def test_Parameter_validate(self, cosmo, param): + """Test :meth:`astropy.cosmology.Parameter.validate`.""" + value = param.validate(cosmo, 1000 * u.m) + + # whether has custom validator + if param.fvalidate is _REGISTRY_FVALIDATORS["default"]: + assert value.unit == u.m + assert value.value == 1000 + else: + assert value.unit == u.km + assert value.value == 1 + + def test_Parameter_register_validator(self, param_cls): + """Test :meth:`astropy.cosmology.Parameter.register_validator`.""" + # already registered + with pytest.raises(KeyError, match="validator 'default' already"): + param_cls.register_validator("default", None) + + # validator not None + def notnonefunc(x): + return x + + try: + validator = param_cls.register_validator("newvalidator", notnonefunc) + assert validator is notnonefunc + finally: + _REGISTRY_FVALIDATORS.pop("newvalidator", None) + + # used as decorator + try: + + @param_cls.register_validator("newvalidator") + def func(cosmology, param, value): + return value + + assert _REGISTRY_FVALIDATORS["newvalidator"] is func + finally: + _REGISTRY_FVALIDATORS.pop("newvalidator", None) + + # ------------------------------------------- + + def test_Parameter_clone(self, param): + """Test :meth:`astropy.cosmology.Parameter.clone`.""" + # this implicitly relies on `__eq__` testing properly. Which is tested. + + # basic test that nothing changes + assert param.clone() == param + assert param.clone() is not param # but it's not a 'singleton' + + # passing kwargs will change stuff + newparam = param.clone(unit="km/(yr sr)") + assert newparam.unit == u.km / u.yr / u.sr + assert param.unit != u.km / u.yr / u.sr # original is unchanged + + # expected failure for not-an-argument + with pytest.raises(TypeError): + param.clone(not_a_valid_parameter=True) + + # ------------------------------------------- + + def test_Parameter_equality(self): + """Test Parameter equality. + + Determined from the processed initialization args (including defaults). + """ + p1 = Parameter(unit="km / (s Mpc)") + p2 = Parameter(unit="km / (s Mpc)") + assert p1 == p2 + + # not equal parameters + p3 = Parameter(unit="km / s") + assert p3 != p1 + + # misc + assert p1 != 2 # show doesn't error + + # ------------------------------------------- + + def test_Parameter_repr(self, cosmo_cls, param): + """Test Parameter repr.""" + r = repr(param) + + assert "Parameter(" in r + for subs in ( + "derived=False", + 'unit=Unit("m")', + 'equivalencies=[(Unit("kg"), Unit("J")', + "doc='Description of example parameter.'", + ): + assert subs in r, subs + + # `fvalidate` is a little tricker b/c one of them is custom! + if param.fvalidate in _REGISTRY_FVALIDATORS.values(): # not custom + assert "fvalidate='default'" in r + else: + assert "fvalidate=<" in r # Some function, don't care about details. + + def test_Parameter_repr_roundtrip(self, param): + """Test ``eval(repr(Parameter))`` can round trip to ``Parameter``.""" + P = Parameter(doc="A description of this parameter.", derived=True) + NP = eval(repr(P)) # Evaluate string representation back into a param. + + assert P == NP + + # ======================================================================== + + def test_make_from_Parameter(self, cosmo_cls, clean_registry): + """Test the parameter creation process. Uses ``__set__``.""" + + @dataclass_decorator + class Example(cosmo_cls): + param: Parameter = Parameter(unit=u.eV, equivalencies=u.mass_energy()) + + @property + def is_flat(self) -> bool: + return super().is_flat() + + assert Example(1).param == 1 * u.eV + assert Example(1 * u.eV).param == 1 * u.eV + assert Example(1 * u.J).param == (1 * u.J).to(u.eV) + assert Example(1 * u.kg).param == (1 * u.kg).to(u.eV, u.mass_energy()) diff --git a/astropy/cosmology/_src/tests/parameter/test_utils.py b/astropy/cosmology/_src/tests/parameter/test_utils.py new file mode 100644 index 000000000000..18e2004d8cdc --- /dev/null +++ b/astropy/cosmology/_src/tests/parameter/test_utils.py @@ -0,0 +1,26 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + + +from astropy.cosmology._src.parameter import Parameter, all_parameters + + +def test_all_parameters(): + """Test :func:`astropy.cosmology._src.utils.all_parameters`.""" + + class ClassA: + a = 1 + b = 2 + + got = all_parameters(ClassA) + assert got == {} + + class ClassB(ClassA): + H0: Parameter = Parameter( + doc="Hubble constant at z=0.", + unit="km/(s Mpc)", + fvalidate="scalar", + ) + + got = all_parameters(ClassB) + assert got.keys() == {"H0"} + assert all(isinstance(p, Parameter) for p in got.values()) diff --git a/astropy/cosmology/_src/tests/test_core.py b/astropy/cosmology/_src/tests/test_core.py new file mode 100644 index 000000000000..6f3dcc262150 --- /dev/null +++ b/astropy/cosmology/_src/tests/test_core.py @@ -0,0 +1,507 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +"""Testing :mod:`astropy.cosmology.core`.""" + +import abc +import inspect +import pickle + +import numpy as np +import pytest +from numpy.typing import NDArray + +import astropy.cosmology.units as cu +import astropy.units as u +from astropy.cosmology import Cosmology, FlatCosmologyMixin, Parameter +from astropy.cosmology._src.core import _COSMOLOGY_CLASSES, dataclass_decorator +from astropy.cosmology._src.tests.io.test_connect import ( + ReadWriteTestMixin, + ToFromFormatTestMixin, +) +from astropy.table import Column, QTable, Table + +from .parameter.test_descriptors import ( + ParametersAttributeTestMixin, +) +from .parameter.test_parameter import ParameterTestMixin + +############################################################################## +# SETUP / TEARDOWN + + +def make_valid_zs(max_z: float = 1e5) -> tuple[list, NDArray[float], list, list]: + """Make a list of valid redshifts for testing.""" + # scalar + scalar_zs = [ + 0, + 1, + min(1100, max_z), # interesting times + # FIXME! np.inf breaks some funcs. 0 * inf is an error + np.float64(min(3300, max_z)), # different type + 2 * cu.redshift, + 3 * u.one, # compatible units + ] + # array + _zarr = np.linspace(0, min(1e5, max_z), num=20) + array_zs = [ + _zarr, # numpy + _zarr.tolist(), # pure python + Column(_zarr), # table-like + _zarr * cu.redshift, # Quantity + ] + return scalar_zs, _zarr, array_zs, scalar_zs + array_zs + + +scalar_zs, z_arr, array_zs, valid_zs = make_valid_zs() + +invalid_zs = [ + (None, TypeError), # wrong type + # Wrong units (the TypeError is for the cython, which can differ) + (4 * u.MeV, (u.UnitConversionError, TypeError)), # scalar + ([0, 1] * u.m, (u.UnitConversionError, TypeError)), # array +] + + +@dataclass_decorator +class SubCosmology(Cosmology): + """Defined here to be serializable.""" + + H0: Parameter = Parameter(unit="km/(s Mpc)") + Tcmb0: Parameter = Parameter(default=0 * u.K, unit=u.K) + m_nu: Parameter = Parameter(default=0 * u.eV, unit=u.eV) + + @property + def is_flat(self) -> bool: + return super().is_flat() + + +############################################################################## +# TESTS +############################################################################## + + +class MetaTestMixin: + """Tests for a :class:`astropy.utils.metadata.MetaData` on a Cosmology.""" + + def test_meta_on_class(self, cosmo_cls): + assert cosmo_cls.meta is None + + def test_meta_on_instance(self, cosmo): + assert isinstance(cosmo.meta, dict) # test type + # value set at initialization + assert cosmo.meta == self.cls_kwargs.get("meta", {}) + + def test_meta_mutable(self, cosmo): + """The metadata is NOT immutable on a cosmology""" + key = next(iter(cosmo.meta.keys())) # select some key + cosmo.meta[key] = cosmo.meta.pop(key) # will error if immutable + + +class CosmologyTest( + ParameterTestMixin, + ParametersAttributeTestMixin, + MetaTestMixin, + ReadWriteTestMixin, + ToFromFormatTestMixin, + metaclass=abc.ABCMeta, +): + """Test subclasses of :class:`astropy.cosmology.Cosmology`.""" + + @abc.abstractmethod + def setup_class(self): + """Setup for testing.""" + + def teardown_class(self): + pass + + @property + def cls_args(self): + return tuple(self._cls_args.values()) + + @pytest.fixture(scope="class") + @classmethod + def cosmo_cls(cls): + """The Cosmology class as a :func:`pytest.fixture`.""" + return cls.cls + + @pytest.fixture(scope="function") # ensure not cached. + def ba(self): + """Return filled `inspect.BoundArguments` for cosmology.""" + ba = inspect.signature(self.cls).bind(*self.cls_args, **self.cls_kwargs) + ba.apply_defaults() + return ba + + @pytest.fixture(scope="class") + @classmethod + def cosmo(cls, cosmo_cls): + """The cosmology instance with which to test.""" + # `cls_args` is a @property on the test class, which doesn't fire + # when accessed via cls (only via self), so dereference _cls_args + # directly here. + cls_args = tuple(cls._cls_args.values()) + ba = inspect.signature(cls.cls).bind(*cls_args, **cls.cls_kwargs) + ba.apply_defaults() + return cosmo_cls(*ba.args, **ba.kwargs) + + # =============================================================== + # Method & Attribute Tests + + # --------------------------------------------------------------- + # class-level + + def test_init_subclass(self, cosmo_cls): + """Test creating subclasses registers classes and manages Parameters.""" + + # ----------------------------------------------------------- + # Normal subclass creation + + class InitSubclassTest(cosmo_cls): + pass + + # test parameters + assert InitSubclassTest.parameters == cosmo_cls.parameters + + # test and cleanup registry + registrant = _COSMOLOGY_CLASSES.pop(InitSubclassTest.__qualname__) + assert registrant is InitSubclassTest + + # ----------------------------------------------------------- + # Skip + + class UnRegisteredSubclassTest(cosmo_cls): + @classmethod + def _register_cls(cls): + """Override to not register.""" + + assert UnRegisteredSubclassTest.parameters == cosmo_cls.parameters + assert UnRegisteredSubclassTest.__qualname__ not in _COSMOLOGY_CLASSES + + # --------------------------------------------------------------- + # instance-level + + def test_init(self, cosmo_cls): + """Test initialization.""" + # Cosmology only does name and meta, but this subclass adds H0 & Tcmb0. + cosmo = cosmo_cls(*self.cls_args, name="test_init", meta={"m": 1}) + assert cosmo.name == "test_init" + assert cosmo.meta["m"] == 1 + + # if meta is None, it is changed to a dict + cosmo = cosmo_cls(*self.cls_args, name="test_init", meta=None) + assert cosmo.meta == {} + + def test_name(self, cosmo): + """Test property ``name``.""" + assert cosmo.name is None or isinstance(cosmo.name, str) # type + assert cosmo.name == self.cls_kwargs["name"] # test has expected value + + def test_name_immutable(self, cosmo): + """The name field should be immutable.""" + match = "cannot assign to field 'name'" + with pytest.raises(AttributeError, match=match): + cosmo.name = None + + def test_name_on_cls(self, cosmo_cls): + """Test accessing :attr:`~astropy.cosmology.Cosmology.name` from the class.""" + assert cosmo_cls.name is None + + @abc.abstractmethod + def test_is_flat(self, cosmo_cls, cosmo): + """Test property ``is_flat``.""" + + # ------------------------------------------------ + # clone + + def test_clone_identical(self, cosmo): + """Test method ``.clone()`` if no (kw)args.""" + assert cosmo.clone() is cosmo + + def test_clone_name(self, cosmo): + """Test method ``.clone()`` name argument.""" + # test changing name. clone treats 'name' differently (see next test) + c = cosmo.clone(name="cloned cosmo") + assert c.name == "cloned cosmo" # changed + # show name is the only thing changed + object.__setattr__(c, "name", cosmo.name) # first change name back + assert c == cosmo + assert c.meta == cosmo.meta + + # now change a different parameter and see how 'name' changes + c = cosmo.clone(meta={"test_clone_name": True}) + assert c.name == cosmo.name + " (modified)" + + def test_clone_meta(self, cosmo): + """Test method ``.clone()`` meta argument: updates meta, doesn't clear.""" + # start with no change + c = cosmo.clone(meta=None) + assert c.meta == cosmo.meta + + # add something + c = cosmo.clone(meta=dict(test_clone_meta=True)) + assert c.meta["test_clone_meta"] is True + c.meta.pop("test_clone_meta") # remove from meta + assert c.meta == cosmo.meta # now they match + + def test_clone_change_param(self, cosmo): + """ + Test method ``.clone()`` changing a(many) Parameter(s). + Nothing here b/c no Parameters. + """ + + def test_clone_fail_unexpected_arg(self, cosmo): + """Test when ``.clone()`` gets an unexpected argument.""" + with pytest.raises(TypeError, match="unexpected keyword argument"): + cosmo.clone(not_an_arg=4) + + def test_clone_fail_positional_arg(self, cosmo): + with pytest.raises(TypeError, match="1 positional argument"): + cosmo.clone(None) + + # --------------------------------------------------------------- + # comparison methods + + def test_is_equivalent(self, cosmo): + """Test :meth:`astropy.cosmology.Cosmology.is_equivalent`.""" + # to self + assert cosmo.is_equivalent(cosmo) + + # same class, different instance + newclone = cosmo.clone(name="test_is_equivalent") + assert cosmo.is_equivalent(newclone) + assert newclone.is_equivalent(cosmo) + + # different class and not convertible to Cosmology. + assert not cosmo.is_equivalent(2) + + def test_equality(self, cosmo): + """Test method ``.__eq__().""" + # wrong class + assert (cosmo != 2) and (2 != cosmo) + # correct + assert cosmo == cosmo + # different name <= not equal, but equivalent + newcosmo = cosmo.clone(name="test_equality") + assert (cosmo != newcosmo) and (newcosmo != cosmo) + assert cosmo.__equiv__(newcosmo) and newcosmo.__equiv__(cosmo) + + # ------------------------------------------------ + + @pytest.mark.parametrize("in_meta", [True, False]) + @pytest.mark.parametrize("table_cls", [Table, QTable]) + def test_astropy_table(self, cosmo, table_cls, in_meta): + """Test ``astropy.table.Table(cosmology)``.""" + tbl = table_cls(cosmo, cosmology_in_meta=in_meta) + + assert isinstance(tbl, table_cls) + # the name & all parameters are columns + for n in ("name", *cosmo.parameters): + assert n in tbl.colnames + assert np.all(tbl[n] == getattr(cosmo, n)) + # check if Cosmology is in metadata or a column + if in_meta: + assert tbl.meta["cosmology"] == cosmo.__class__.__qualname__ + assert "cosmology" not in tbl.colnames + else: + assert "cosmology" not in tbl.meta + assert tbl["cosmology"][0] == cosmo.__class__.__qualname__ + # the metadata is transferred + for k, v in cosmo.meta.items(): + assert np.all(tbl.meta[k] == v) + + # =============================================================== + # Usage Tests + + def test_immutability(self, cosmo): + """ + Test immutability of cosmologies. + The metadata is mutable: see ``test_meta_mutable``. + """ + for n in (*cosmo.parameters, *cosmo._derived_parameters): + with pytest.raises(AttributeError): + setattr(cosmo, n, getattr(cosmo, n)) + + def test_pickle_class(self, cosmo_cls, pickle_protocol): + """Test classes can pickle and unpickle.""" + # pickle and unpickle + f = pickle.dumps(cosmo_cls, protocol=pickle_protocol) + unpickled = pickle.loads(f) + + # test equality + assert unpickled == cosmo_cls + + def test_pickle_instance(self, cosmo, pickle_protocol): + """Test instances can pickle and unpickle.""" + # pickle and unpickle + f = pickle.dumps(cosmo, protocol=pickle_protocol) + with u.add_enabled_units(cu): + unpickled = pickle.loads(f) + + assert unpickled == cosmo + assert unpickled.meta == cosmo.meta + + +class TestCosmology(CosmologyTest): + """Test :class:`astropy.cosmology.Cosmology`. + + Subclasses should define tests for: + + - ``test_clone_change_param()`` + - ``test_repr()`` + """ + + def setup_class(self): + """ + Setup for testing. + Cosmology should not be instantiated, so tests are done on a subclass. + """ + # make sure SubCosmology is known + _COSMOLOGY_CLASSES["SubCosmology"] = SubCosmology + + self.cls = SubCosmology + self._cls_args = dict( + H0=70 * (u.km / u.s / u.Mpc), Tcmb0=2.7 * u.K, m_nu=0.6 * u.eV + ) + self.cls_kwargs = dict(name=self.__class__.__name__, meta={"a": "b"}) + + def teardown_class(self): + """Teardown for testing.""" + super().teardown_class(self) + _COSMOLOGY_CLASSES.pop("SubCosmology", None) + + # =============================================================== + # Method & Attribute Tests + + def test_is_flat(self, cosmo_cls, cosmo): + """Test property ``is_flat``. It's an ABC.""" + with pytest.raises(NotImplementedError, match="is_flat is not implemented"): + cosmo.is_flat + + +# ----------------------------------------------------------------------------- + + +class FlatCosmologyMixinTest: + """Tests for :class:`astropy.cosmology.core.FlatCosmologyMixin` subclasses. + + The test suite structure mirrors the implementation of the tested code. + Just like :class:`astropy.cosmology.FlatCosmologyMixin` is an abstract + base class (ABC) that cannot be used by itself, so too is this corresponding + test class an ABC mixin. + + E.g to use this class:: + + class TestFlatSomeCosmology(FlatCosmologyMixinTest, TestSomeCosmology): + ... + """ + + def test_nonflat_class_(self, cosmo_cls, cosmo): + """Test :attr:`astropy.cosmology.core.FlatCosmologyMixin.nonflat_cls`.""" + # Test it's a method on the class + assert issubclass(cosmo_cls, cosmo_cls.__nonflatclass__) + + # It also works from the instance. # TODO! as a "metaclassmethod" + assert issubclass(cosmo_cls, cosmo.__nonflatclass__) + + # Maybe not the most robust test, but so far all Flat classes have the + # name of their parent class. + assert cosmo.__nonflatclass__.__name__ in cosmo_cls.__name__ + + def test_is_flat(self, cosmo_cls, cosmo): + """Test property ``is_flat``.""" + super().test_is_flat(cosmo_cls, cosmo) + + # it's always True + assert cosmo.is_flat is True + + def test_nonflat(self, cosmo): + """Test :attr:`astropy.cosmology.core.FlatCosmologyMixin.nonflat`.""" + assert cosmo.nonflat.is_equivalent(cosmo) + assert cosmo.is_equivalent(cosmo.nonflat) + + # ------------------------------------------------ + # clone + + def test_clone_to_nonflat_equivalent(self, cosmo): + """Test method ``.clone()``to_nonflat argument.""" + # just converting the class + nc = cosmo.clone(to_nonflat=True) + assert isinstance(nc, cosmo.__nonflatclass__) + assert nc == cosmo.nonflat + + @abc.abstractmethod + def test_clone_to_nonflat_change_param(self, cosmo): + """ + Test method ``.clone()`` changing a(many) Parameter(s). No parameters + are changed here because FlatCosmologyMixin has no Parameters. + See class docstring for why this test method exists. + """ + # send to non-flat + nc = cosmo.clone(to_nonflat=True) + assert isinstance(nc, cosmo.__nonflatclass__) + assert nc == cosmo.nonflat + + # ------------------------------------------------ + + def test_is_equivalent(self, cosmo): + """Test :meth:`astropy.cosmology.core.FlatCosmologyMixin.is_equivalent`. + + Normally this would pass up via super(), but ``__equiv__`` is meant + to be overridden, so we skip super(). + e.g. FlatFLRWMixinTest -> FlatCosmologyMixinTest -> TestCosmology + vs FlatFLRWMixinTest -> FlatCosmologyMixinTest -> TestFLRW -> TestCosmology + """ + CosmologyTest.test_is_equivalent(self, cosmo) + + # See FlatFLRWMixinTest for tests. It's a bit hard here since this class + # is for an ABC. + + # =============================================================== + # Usage Tests + + def test_subclassing(self, cosmo_cls): + """Test when subclassing a flat cosmology.""" + + class SubClass1(cosmo_cls): + pass + + # The classes have the same non-flat parent class + assert SubClass1.__nonflatclass__ is cosmo_cls.__nonflatclass__ + + # A more complex example is when Mixin classes are used. + class Mixin: + pass + + class SubClass2(Mixin, cosmo_cls): + pass + + # The classes have the same non-flat parent class + assert SubClass2.__nonflatclass__ is cosmo_cls.__nonflatclass__ + + # The order of the Mixin should not matter + class SubClass3(cosmo_cls, Mixin): + pass + + # The classes have the same non-flat parent class + assert SubClass3.__nonflatclass__ is cosmo_cls.__nonflatclass__ + + +def test__nonflatclass__multiple_nonflat_inheritance(): + """ + Test :meth:`astropy.cosmology.core.FlatCosmologyMixin.__nonflatclass__` + when there's more than one non-flat class in the inheritance. + """ + + # Define a non-operable minimal subclass of Cosmology. + @dataclass_decorator + class SubCosmology2(Cosmology): + @property + def is_flat(self) -> bool: + return False + + # Now make an ambiguous flat cosmology from the two SubCosmologies + with pytest.raises(TypeError, match="cannot create a consistent non-flat class"): + + class FlatSubCosmology(FlatCosmologyMixin, SubCosmology, SubCosmology2): + @property + def nonflat(self): + pass diff --git a/astropy/cosmology/_src/tests/test_parameters.py b/astropy/cosmology/_src/tests/test_parameters.py new file mode 100644 index 000000000000..21902f82aae1 --- /dev/null +++ b/astropy/cosmology/_src/tests/test_parameters.py @@ -0,0 +1,44 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +from types import MappingProxyType + +import numpy as np +import pytest + +from astropy.cosmology import parameters, realizations + + +def test_realizations_in_dir(): + """Test the realizations are in ``dir`` of :mod:`astropy.cosmology.parameters`.""" + d = dir(parameters) + + assert set(d) == set(parameters.__all__) + for n in parameters.available: + assert n in d + + +@pytest.mark.parametrize("name", parameters.available) +def test_getting_parameters(name): + """ + Test getting 'parameters' and that it is derived from the corresponding + realization. + """ + params = getattr(parameters, name) + + assert isinstance(params, MappingProxyType) + assert params["name"] == name + + # Check parameters have the right keys and values + cosmo = getattr(realizations, name) + assert params["name"] == cosmo.name + assert params["cosmology"] == cosmo.__class__.__qualname__ + # All the cosmology parameters are equal + for k, v in cosmo.parameters.items(): + np.testing.assert_array_equal(params[k], v) + # All the metadata is included. Parameter values take precedence, so only + # checking the keys. + assert set(cosmo.meta.keys()).issubset(params.keys()) + + # Lastly, check the generation process. + m = cosmo.to_format("mapping", cosmology_as_str=True, move_from_meta=True) + assert params == m diff --git a/astropy/cosmology/_src/tests/test_realizations.py b/astropy/cosmology/_src/tests/test_realizations.py new file mode 100644 index 000000000000..bfe7a1352e52 --- /dev/null +++ b/astropy/cosmology/_src/tests/test_realizations.py @@ -0,0 +1,105 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +import pickle + +import pytest + +import astropy.cosmology.units as cu +import astropy.units as u +from astropy import cosmology +from astropy.cosmology import parameters, realizations +from astropy.cosmology.realizations import default_cosmology + + +def test_realizations_in_toplevel_dir(): + """Test the realizations are in ``dir`` of :mod:`astropy.cosmology`.""" + d = dir(cosmology) + + assert set(d) == set(cosmology.__all__) + for n in parameters.available: + assert n in d + + +def test_realizations_in_realizations_dir(): + """Test the realizations are in ``dir`` of :mod:`astropy.cosmology.realizations`.""" + d = dir(realizations) + + assert set(d) == set(realizations.__all__) + for n in parameters.available: + assert n in d + + +class Test_default_cosmology: + """Tests for :class:`~astropy.cosmology.realizations.default_cosmology`.""" + + # ----------------------------------------------------- + # Get + + def test_get_current(self): + """Test :meth:`astropy.cosmology.default_cosmology.get` current value.""" + cosmo = default_cosmology.get() + assert cosmo is default_cosmology.validate(default_cosmology._value) + + # ----------------------------------------------------- + # Validate + + def test_validate_fail(self): + """Test :meth:`astropy.cosmology.default_cosmology.validate`.""" + # bad input type + with pytest.raises(TypeError, match="must be a string or Cosmology"): + default_cosmology.validate(TypeError) + + # a not-valid option, but still a str + with pytest.raises(ValueError, match="Unknown cosmology"): + default_cosmology.validate("fail!") + + # a not-valid type + with pytest.raises(TypeError, match="cannot find a Cosmology"): + default_cosmology.validate("available") + + def test_validate_default(self): + """Test method ``validate`` for specific values.""" + value = default_cosmology.validate(None) + assert value is realizations.Planck18 + + @pytest.mark.parametrize("name", parameters.available) + def test_validate_str(self, name): + """Test method ``validate`` for string input.""" + value = default_cosmology.validate(name) + assert value is getattr(realizations, name) + + @pytest.mark.parametrize("name", parameters.available) + def test_validate_cosmo(self, name): + """Test method ``validate`` for cosmology instance input.""" + cosmo = getattr(realizations, name) + value = default_cosmology.validate(cosmo) + assert value is cosmo + + def test_validate_no_default(self): + """Test :meth:`astropy.cosmology.default_cosmology.get` to `None`.""" + cosmo = default_cosmology.validate("no_default") + assert cosmo is None + + +@pytest.mark.parametrize("name", parameters.available) +def test_pickle_builtin_realizations(name, pickle_protocol): + """ + Test in-built realizations can pickle and unpickle. + Also a regression test for #12008. + """ + # get class instance + original = getattr(cosmology, name) + + # pickle and unpickle + f = pickle.dumps(original, protocol=pickle_protocol) + with u.add_enabled_units(cu): + unpickled = pickle.loads(f) + + assert unpickled == original + assert unpickled.meta == original.meta + + # if the units are not enabled, it isn't equal because redshift units + # are not equal. This is a weird, known issue. + unpickled = pickle.loads(f) + assert unpickled == original + assert unpickled.meta != original.meta diff --git a/astropy/cosmology/_src/tests/test_scipy_compat.py b/astropy/cosmology/_src/tests/test_scipy_compat.py new file mode 100644 index 000000000000..5457472250e5 --- /dev/null +++ b/astropy/cosmology/_src/tests/test_scipy_compat.py @@ -0,0 +1,13 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +import pytest + +from astropy.cosmology._src.flrw.base import quad +from astropy.utils.compat.optional_deps import HAS_SCIPY + + +@pytest.mark.skipif(HAS_SCIPY, reason="scipy is installed") +def test_optional_deps_functions(): + """Test stand-in functions when optional dependencies not installed.""" + with pytest.raises(ModuleNotFoundError, match="No module named 'scipy.integrate'"): + quad() diff --git a/astropy/cosmology/_src/tests/test_units.py b/astropy/cosmology/_src/tests/test_units.py new file mode 100644 index 000000000000..2c67498f4a9f --- /dev/null +++ b/astropy/cosmology/_src/tests/test_units.py @@ -0,0 +1,372 @@ +"""Testing :mod:`astropy.cosmology.units`.""" + +import pytest + +import astropy.cosmology.units as cu +import astropy.units as u +from astropy.cosmology import Planck13, default_cosmology +from astropy.tests.helper import assert_quantity_allclose +from astropy.utils.compat.optional_deps import HAS_SCIPY + +############################################################################## +# TESTS +############################################################################## + + +def test_littleh(): + """Test :func:`astropy.cosmology.units.with_H0`.""" + H0_70 = 70 * u.km / u.s / u.Mpc + h70dist = 70 * u.Mpc / cu.littleh + + assert_quantity_allclose(h70dist.to(u.Mpc, cu.with_H0(H0_70)), 100 * u.Mpc) + + # make sure using the default cosmology works + cosmodist = default_cosmology.get().H0.value * u.Mpc / cu.littleh + assert_quantity_allclose(cosmodist.to(u.Mpc, cu.with_H0()), 100 * u.Mpc) + + # Now try a luminosity scaling + h1lum = 0.49 * u.Lsun * cu.littleh**-2 + assert_quantity_allclose(h1lum.to(u.Lsun, cu.with_H0(H0_70)), 1 * u.Lsun) + + # And the trickiest one: magnitudes. Using H0=10 here for the round numbers + H0_10 = 10 * u.km / u.s / u.Mpc + # assume the "true" magnitude M = 12. + # Then M - 5*log_10(h) = M + 5 = 17 + withlittlehmag = 17 * (u.mag - u.MagUnit(cu.littleh**2)) + assert_quantity_allclose(withlittlehmag.to(u.mag, cu.with_H0(H0_10)), 12 * u.mag) + + +@pytest.mark.skipif(not HAS_SCIPY, reason="Cosmology needs scipy") +def test_dimensionless_redshift(): + """Test :func:`astropy.cosmology.units.dimensionless_redshift`.""" + z = 3 * cu.redshift + val = 3 * u.one + + # show units not equal + assert z.unit == cu.redshift + assert z.unit != u.one + assert u.get_physical_type(z) == "redshift" + + # test equivalency enabled by default + assert z == val + + # also test that it works for powers + assert (3 * cu.redshift**3) == val + + # and in composite units + assert (3 * u.km / cu.redshift**3) == 3 * u.km + + # test it also works as an equivalency + with u.set_enabled_equivalencies([]): # turn off default equivalencies + assert z.to(u.one, equivalencies=cu.dimensionless_redshift()) == val + + with pytest.raises(ValueError): + z.to(u.one) + + # if this fails, something is really wrong + with u.add_enabled_equivalencies(cu.dimensionless_redshift()): + assert z == val + + +@pytest.mark.skipif(not HAS_SCIPY, reason="Cosmology needs scipy") +def test_redshift_temperature(): + """Test :func:`astropy.cosmology.units.redshift_temperature`.""" + cosmo = Planck13.clone(Tcmb0=3 * u.K) + default_cosmo = default_cosmology.get() + z = 15 * cu.redshift + Tcmb = cosmo.Tcmb(z) + + # 1) Default (without specifying the cosmology) + with default_cosmology.set(cosmo): + equivalency = cu.redshift_temperature() + assert_quantity_allclose(z.to(u.K, equivalency), Tcmb) + assert_quantity_allclose(Tcmb.to(cu.redshift, equivalency), z) + + # showing the answer changes if the cosmology changes + # this test uses the default cosmology + equivalency = cu.redshift_temperature() + assert_quantity_allclose(z.to(u.K, equivalency), default_cosmo.Tcmb(z)) + assert default_cosmo.Tcmb(z) != Tcmb + + # 2) Specifying the cosmology + equivalency = cu.redshift_temperature(cosmo) + assert_quantity_allclose(z.to(u.K, equivalency), Tcmb) + assert_quantity_allclose(Tcmb.to(cu.redshift, equivalency), z) + + # Test `atzkw` + equivalency = cu.redshift_temperature(cosmo, ztol=1e-10) + assert_quantity_allclose(Tcmb.to(cu.redshift, equivalency), z) + + +@pytest.mark.skipif(not HAS_SCIPY, reason="Cosmology needs scipy") +def test_redshift_hubble(): + """Test :func:`astropy.cosmology.units.redshift_hubble`.""" + unit = u.km / u.s / u.Mpc + cosmo = Planck13.clone(H0=100 * unit) + default_cosmo = default_cosmology.get() + z = 15 * cu.redshift + H = cosmo.H(z) + h = H.to_value(u.km / u.s / u.Mpc) / 100 * cu.littleh + + # 1) Default (without specifying the cosmology) + with default_cosmology.set(cosmo): + equivalency = cu.redshift_hubble() + # H + assert_quantity_allclose(z.to(unit, equivalency), H) + assert_quantity_allclose(H.to(cu.redshift, equivalency), z) + # little-h + assert_quantity_allclose(z.to(cu.littleh, equivalency), h) + assert_quantity_allclose(h.to(cu.redshift, equivalency), z) + + # showing the answer changes if the cosmology changes + # this test uses the default cosmology + equivalency = cu.redshift_hubble() + assert_quantity_allclose(z.to(unit, equivalency), default_cosmo.H(z)) + assert default_cosmo.H(z) != H + + # 2) Specifying the cosmology + equivalency = cu.redshift_hubble(cosmo) + # H + assert_quantity_allclose(z.to(unit, equivalency), H) + assert_quantity_allclose(H.to(cu.redshift, equivalency), z) + # little-h + assert_quantity_allclose(z.to(cu.littleh, equivalency), h) + assert_quantity_allclose(h.to(cu.redshift, equivalency), z) + + # Test `atzkw` + equivalency = cu.redshift_hubble(cosmo, ztol=1e-10) + assert_quantity_allclose(H.to(cu.redshift, equivalency), z) # H + assert_quantity_allclose(h.to(cu.redshift, equivalency), z) # little-h + + +@pytest.mark.skipif(not HAS_SCIPY, reason="Cosmology needs scipy") +@pytest.mark.parametrize( + "kind", + [cu.redshift_distance.__defaults__[-1], "comoving", "lookback", "luminosity"], +) +def test_redshift_distance(kind): + """Test :func:`astropy.cosmology.units.redshift_distance`.""" + z = 15 * cu.redshift + d = getattr(Planck13, kind + "_distance")(z) + + equivalency = cu.redshift_distance(cosmology=Planck13, kind=kind) + + # properties of Equivalency + assert equivalency.name[0] == "redshift_distance" + assert equivalency.kwargs[0]["cosmology"] == Planck13 + assert equivalency.kwargs[0]["distance"] == kind + + # roundtrip + assert_quantity_allclose(z.to(u.Mpc, equivalency), d) + assert_quantity_allclose(d.to(cu.redshift, equivalency), z) + + +def test_redshift_distance_wrong_kind(): + """Test :func:`astropy.cosmology.units.redshift_distance` wrong kind.""" + with pytest.raises(ValueError, match="`kind`"): + cu.redshift_distance(kind=None) + + +@pytest.mark.skipif(not HAS_SCIPY, reason="Cosmology needs scipy") +class Test_with_redshift: + """Test `astropy.cosmology.units.with_redshift`.""" + + @pytest.fixture(scope="class") + @classmethod + def cosmo(cls): + """Test cosmology.""" + return Planck13.clone(Tcmb0=3 * u.K) + + # =========================================== + + def test_cosmo_different(self, cosmo): + """The default is different than the test cosmology.""" + default_cosmo = default_cosmology.get() + assert default_cosmo != cosmo # shows changing default + + def test_no_equivalency(self, cosmo): + """Test the equivalency ``with_redshift`` without any enabled.""" + equivalency = cu.with_redshift(distance=None, hubble=False, Tcmb=False) + assert len(equivalency) == 0 + + # ------------------------------------------- + + def test_temperature_off(self, cosmo): + """Test ``with_redshift`` with the temperature off.""" + z = 15 * cu.redshift + err_msg = ( + r"^'redshift' \(redshift\) and 'K' \(temperature\) are not convertible$" + ) + + # 1) Default (without specifying the cosmology) + with default_cosmology.set(cosmo): + equivalency = cu.with_redshift(Tcmb=False) + with pytest.raises(u.UnitConversionError, match=err_msg): + z.to(u.K, equivalency) + + # 2) Specifying the cosmology + equivalency = cu.with_redshift(cosmo, Tcmb=False) + with pytest.raises(u.UnitConversionError, match=err_msg): + z.to(u.K, equivalency) + + def test_temperature(self, cosmo): + """Test temperature equivalency component.""" + default_cosmo = default_cosmology.get() + z = 15 * cu.redshift + Tcmb = cosmo.Tcmb(z) + + # 1) Default (without specifying the cosmology) + with default_cosmology.set(cosmo): + equivalency = cu.with_redshift(Tcmb=True) + assert_quantity_allclose(z.to(u.K, equivalency), Tcmb) + assert_quantity_allclose(Tcmb.to(cu.redshift, equivalency), z) + + # showing the answer changes if the cosmology changes + # this test uses the default cosmology + equivalency = cu.with_redshift(Tcmb=True) + assert_quantity_allclose(z.to(u.K, equivalency), default_cosmo.Tcmb(z)) + assert default_cosmo.Tcmb(z) != Tcmb + + # 2) Specifying the cosmology + equivalency = cu.with_redshift(cosmo, Tcmb=True) + assert_quantity_allclose(z.to(u.K, equivalency), Tcmb) + assert_quantity_allclose(Tcmb.to(cu.redshift, equivalency), z) + + # Test `atzkw` + # this is really just a test that 'atzkw' doesn't fail + equivalency = cu.with_redshift(cosmo, Tcmb=True, atzkw={"ztol": 1e-10}) + assert_quantity_allclose(Tcmb.to(cu.redshift, equivalency), z) + + # ------------------------------------------- + + def test_hubble_off(self, cosmo): + """Test ``with_redshift`` with Hubble off.""" + unit = u.km / u.s / u.Mpc + z = 15 * cu.redshift + err_msg = ( + r"^'redshift' \(redshift\) and 'km / \(Mpc s\)' \(frequency\) are not " + "convertible$" + ) + + # 1) Default (without specifying the cosmology) + with default_cosmology.set(cosmo): + equivalency = cu.with_redshift(hubble=False) + with pytest.raises(u.UnitConversionError, match=err_msg): + z.to(unit, equivalency) + + # 2) Specifying the cosmology + equivalency = cu.with_redshift(cosmo, hubble=False) + with pytest.raises(u.UnitConversionError, match=err_msg): + z.to(unit, equivalency) + + def test_hubble(self, cosmo): + """Test Hubble equivalency component.""" + unit = u.km / u.s / u.Mpc + default_cosmo = default_cosmology.get() + z = 15 * cu.redshift + H = cosmo.H(z) + h = H.to_value(u.km / u.s / u.Mpc) / 100 * cu.littleh + + # 1) Default (without specifying the cosmology) + with default_cosmology.set(cosmo): + equivalency = cu.with_redshift(hubble=True) + # H + assert_quantity_allclose(z.to(unit, equivalency), H) + assert_quantity_allclose(H.to(cu.redshift, equivalency), z) + # little-h + assert_quantity_allclose(z.to(cu.littleh, equivalency), h) + assert_quantity_allclose(h.to(cu.redshift, equivalency), z) + + # showing the answer changes if the cosmology changes + # this test uses the default cosmology + equivalency = cu.with_redshift(hubble=True) + assert_quantity_allclose(z.to(unit, equivalency), default_cosmo.H(z)) + assert default_cosmo.H(z) != H + + # 2) Specifying the cosmology + equivalency = cu.with_redshift(cosmo, hubble=True) + # H + assert_quantity_allclose(z.to(unit, equivalency), H) + assert_quantity_allclose(H.to(cu.redshift, equivalency), z) + # little-h + assert_quantity_allclose(z.to(cu.littleh, equivalency), h) + assert_quantity_allclose(h.to(cu.redshift, equivalency), z) + + # Test `atzkw` + # this is really just a test that 'atzkw' doesn't fail + equivalency = cu.with_redshift(cosmo, hubble=True, atzkw={"ztol": 1e-10}) + assert_quantity_allclose(H.to(cu.redshift, equivalency), z) # H + assert_quantity_allclose(h.to(cu.redshift, equivalency), z) # h + + # ------------------------------------------- + + def test_distance_off(self, cosmo): + """Test ``with_redshift`` with the distance off.""" + z = 15 * cu.redshift + err_msg = r"^'redshift' \(redshift\) and 'Mpc' \(length\) are not convertible$" + + # 1) Default (without specifying the cosmology) + with default_cosmology.set(cosmo): + equivalency = cu.with_redshift(distance=None) + with pytest.raises(u.UnitConversionError, match=err_msg): + z.to(u.Mpc, equivalency) + + # 2) Specifying the cosmology + equivalency = cu.with_redshift(cosmo, distance=None) + with pytest.raises(u.UnitConversionError, match=err_msg): + z.to(u.Mpc, equivalency) + + def test_distance_default(self): + """Test distance equivalency default.""" + z = 15 * cu.redshift + d = default_cosmology.get().comoving_distance(z) + + equivalency = cu.with_redshift() + assert_quantity_allclose(z.to(u.Mpc, equivalency), d) + assert_quantity_allclose(d.to(cu.redshift, equivalency), z) + + def test_distance_wrong_kind(self): + """Test distance equivalency, but the wrong kind.""" + with pytest.raises(ValueError, match="`kind`"): + cu.with_redshift(distance=ValueError) + + @pytest.mark.parametrize("kind", ["comoving", "lookback", "luminosity"]) + def test_distance(self, kind): + """Test distance equivalency.""" + cosmo = Planck13 + z = 15 * cu.redshift + dist = getattr(cosmo, kind + "_distance")(z) + + default_cosmo = default_cosmology.get() + assert default_cosmo != cosmo # shows changing default + + # 1) without specifying the cosmology + with default_cosmology.set(cosmo): + equivalency = cu.with_redshift(distance=kind) + assert_quantity_allclose(z.to(u.Mpc, equivalency), dist) + + # showing the answer changes if the cosmology changes + # this test uses the default cosmology + equivalency = cu.with_redshift(distance=kind) + assert_quantity_allclose( + z.to(u.Mpc, equivalency), getattr(default_cosmo, kind + "_distance")(z) + ) + assert not u.allclose(getattr(default_cosmo, kind + "_distance")(z), dist) + + # 2) Specifying the cosmology + equivalency = cu.with_redshift(cosmo, distance=kind) + assert_quantity_allclose(z.to(u.Mpc, equivalency), dist) + assert_quantity_allclose(dist.to(cu.redshift, equivalency), z) + + # Test atzkw + # this is really just a test that 'atzkw' doesn't fail + equivalency = cu.with_redshift(cosmo, distance=kind, atzkw={"ztol": 1e-10}) + assert_quantity_allclose(dist.to(cu.redshift, equivalency), z) + + +def test_equivalency_context_manager(): + base_registry = u.get_current_unit_registry() + + # check starting with only the dimensionless_redshift equivalency. + assert len(base_registry.equivalencies) == 1 + assert str(base_registry.equivalencies[0][0]) == "redshift" diff --git a/astropy/cosmology/_src/tests/test_utils.py b/astropy/cosmology/_src/tests/test_utils.py new file mode 100644 index 000000000000..beac4b9e92fc --- /dev/null +++ b/astropy/cosmology/_src/tests/test_utils.py @@ -0,0 +1,177 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +import numpy as np +import pytest + +import astropy.units as u +from astropy.cosmology._src.utils import ( + aszarr, + deprecated_keywords, + vectorize_redshift_method, +) +from astropy.utils.compat.optional_deps import HAS_PANDAS + +from .test_core import invalid_zs, valid_zs, z_arr + + +def test_vectorize_redshift_method(): + """Test :func:`astropy.cosmology._src.utils.vectorize_redshift_method`.""" + + class Class: + @vectorize_redshift_method + def method(self, z): + return z + + c = Class() + + assert hasattr(c.method, "__vectorized__") + assert isinstance(c.method.__vectorized__, np.vectorize) + + # calling with Number + assert c.method(1) == 1 + assert isinstance(c.method(1), int) + + # calling with a numpy scalar + assert c.method(np.float64(1)) == np.float64(1) + assert isinstance(c.method(np.float64(1)), np.float64) + + # numpy array + assert all(c.method(np.array([1, 2])) == np.array([1, 2])) + assert isinstance(c.method(np.array([1, 2])), np.ndarray) + + # non-scalar + assert all(c.method([1, 2]) == np.array([1, 2])) + assert isinstance(c.method([1, 2]), np.ndarray) + + +# ------------------------------------------------------------------- + + +class Test_aszarr: + @pytest.mark.parametrize( + "z, expect", + list( + zip( + valid_zs, + [0, 1, 1100, np.float64(3300), 2.0, 3.0, z_arr, z_arr, z_arr, z_arr], + ) + ), + ) + def test_valid(self, z, expect): + """Test :func:`astropy.cosmology._src.utils.aszarr`.""" + got = aszarr(z) + assert np.array_equal(got, expect) + + @pytest.mark.parametrize("z, exc", invalid_zs) + def test_invalid(self, z, exc): + """Test :func:`astropy.cosmology._src.utils.aszarr`.""" + with pytest.raises(exc): + aszarr(z) + + @pytest.mark.skipif(not HAS_PANDAS, reason="requires pandas") + def test_pandas(self): + import pandas as pd + + x = pd.Series([1, 2, 3, 4, 5]) + + # Demonstrate Pandas doesn't work with units + assert not isinstance(x * u.km, u.Quantity) + + # Test aszarr works with Pandas + assert isinstance(aszarr(x), np.ndarray) + np.testing.assert_array_equal(aszarr(x), x.values) + + +# ------------------------------------------------------------------- + + +class TestDeprecatedKeywords: + @classmethod + def setup_class(cls): + def noop(a, b, c, d): + # a minimal function that does nothing, + # with multiple positional-or-keywords arguments + return + + cls.base_func = noop + cls.depr_funcs = { + 1: deprecated_keywords("a", since="999.999.999")(noop), + 2: deprecated_keywords("a", "b", since="999.999.999")(noop), + 4: deprecated_keywords("a", "b", "c", "d", since="999.999.999")(noop), + } + + def test_type_safety(self): + dec = deprecated_keywords(b"a", since="999.999.999") + with pytest.raises(TypeError, match=r"names\[0\] must be a string"): + dec(self.base_func) + + dec = deprecated_keywords("a", since=b"999.999.999") + with pytest.raises(TypeError, match=r"since must be a string"): + dec(self.base_func) + + @pytest.mark.parametrize("n_deprecated_keywords", [1, 2, 4]) + def test_no_warn(self, n_deprecated_keywords): + func = self.depr_funcs[n_deprecated_keywords] + func(1, 2, 3, 4) + + @pytest.mark.parametrize( + "n_deprecated_keywords, args, kwargs, match", + [ + pytest.param( + 1, + (), + {"a": 1, "b": 2, "c": 3, "d": 4}, + r"Passing 'a' as keyword is deprecated since", + id="1 deprecation, 1 warn", + ), + pytest.param( + 2, + (1,), + {"b": 2, "c": 3, "d": 4}, + r"Passing 'b' as keyword is deprecated since", + id="2 deprecation, 1 warn", + ), + pytest.param( + 2, + (), + {"a": 1, "b": 2, "c": 3, "d": 4}, + r"Passing \['a', 'b'\] arguments as keywords is deprecated since", + id="2 deprecations, 2 warns", + ), + pytest.param( + 4, + (), + {"a": 1, "b": 2, "c": 3, "d": 4}, + ( + r"Passing \['a', 'b', 'c', 'd'\] arguments as keywords " + "is deprecated since" + ), + id="4 deprecations, 4 warns", + ), + pytest.param( + 4, + (1,), + {"b": 2, "c": 3, "d": 4}, + r"Passing \['b', 'c', 'd'\] arguments as keywords is deprecated since", + id="4 deprecations, 3 warns", + ), + pytest.param( + 4, + (1, 2), + {"c": 3, "d": 4}, + r"Passing \['c', 'd'\] arguments as keywords is deprecated since", + id="4 deprecations, 2 warns", + ), + pytest.param( + 4, + (1, 2, 3), + {"d": 4}, + r"Passing 'd' as keyword is deprecated since", + id="4 deprecations, 1 warn", + ), + ], + ) + def test_warn(self, n_deprecated_keywords, args, kwargs, match): + func = self.depr_funcs[n_deprecated_keywords] + with pytest.warns(FutureWarning, match=match): + func(*args, **kwargs) diff --git a/astropy/cosmology/_src/tests/traits/__init__.py b/astropy/cosmology/_src/tests/traits/__init__.py new file mode 100644 index 000000000000..46dc7ab34bba --- /dev/null +++ b/astropy/cosmology/_src/tests/traits/__init__.py @@ -0,0 +1,3 @@ +"""Test package for cosmology trait tests.""" + +__all__ = [] diff --git a/astropy/cosmology/_src/tests/traits/helper.py b/astropy/cosmology/_src/tests/traits/helper.py new file mode 100644 index 000000000000..58a952463977 --- /dev/null +++ b/astropy/cosmology/_src/tests/traits/helper.py @@ -0,0 +1,17 @@ +import inspect +from collections.abc import Callable + + +def is_positional_only(func: Callable, /, param: str) -> bool: + """Return True if ``param:str`` is a positional-only parameter. + + Parameters + ---------- + func: Callable + Function to check whether parameter `param` is positional-only. + param : str + The name of the parameter in `func` to check. + """ + sig = inspect.signature(func) + p = sig.parameters.get(param) + return p is not None and p.kind == inspect.Parameter.POSITIONAL_ONLY diff --git a/astropy/cosmology/_src/tests/traits/test_trait_baryons.py b/astropy/cosmology/_src/tests/traits/test_trait_baryons.py new file mode 100644 index 000000000000..1a7a349d8e03 --- /dev/null +++ b/astropy/cosmology/_src/tests/traits/test_trait_baryons.py @@ -0,0 +1,25 @@ +import numpy as np +import pytest +from numpy.testing import assert_allclose + +from astropy.cosmology._src.traits.baryons import BaryonComponent + +from .helper import is_positional_only + + +class DummyBaryon(BaryonComponent): + Ob0 = 0.05 + + def inv_efunc(self, z): + return np.ones_like(np.asarray(z)) + + +@pytest.fixture +def dummy_baryon(): + return DummyBaryon() + + +def test_baryon_signature_and_behavior(dummy_baryon): + assert hasattr(BaryonComponent, "Ob") + assert is_positional_only(BaryonComponent.Ob, "z") + assert_allclose(dummy_baryon.Ob(1), 0.05 * (1 + 1) ** 3) diff --git a/astropy/cosmology/_src/tests/traits/test_trait_curvature.py b/astropy/cosmology/_src/tests/traits/test_trait_curvature.py new file mode 100644 index 000000000000..fe50939cb0ac --- /dev/null +++ b/astropy/cosmology/_src/tests/traits/test_trait_curvature.py @@ -0,0 +1,42 @@ +import numpy as np +import pytest +from numpy.testing import assert_allclose + +from astropy.cosmology._src.traits.curvature import CurvatureComponent + +from .helper import is_positional_only + + +class DummyCurvature(CurvatureComponent): + def __init__(self, ok0): + self._ok0 = ok0 + + @property + def Ok0(self): + return self._ok0 + + @property + def is_flat(self): + return self._ok0 == 0 + + def inv_efunc(self, z): + return np.ones_like(np.asarray(z)) + + +@pytest.fixture +def dummy_curvature_zero(): + return DummyCurvature(0.0) + + +@pytest.fixture +def dummy_curvature_nonzero(): + return DummyCurvature(-0.02) + + +def test_curvature_signature_and_behavior( + dummy_curvature_zero, dummy_curvature_nonzero +): + assert hasattr(CurvatureComponent, "Ok") + assert is_positional_only(CurvatureComponent.Ok, "z") + assert_allclose(dummy_curvature_zero.Ok(1), 0.0) + assert_allclose(dummy_curvature_nonzero.Ok(1), -0.02 * (1 + 1) ** 2) diff --git a/astropy/cosmology/_src/tests/traits/test_trait_darkenergy.py b/astropy/cosmology/_src/tests/traits/test_trait_darkenergy.py new file mode 100644 index 000000000000..112a508e42a4 --- /dev/null +++ b/astropy/cosmology/_src/tests/traits/test_trait_darkenergy.py @@ -0,0 +1,48 @@ +import numpy as np +import pytest + +from astropy.cosmology._src.traits.darkenergy import DarkEnergyComponent + +from .helper import is_positional_only + + +class MinimalDarkEnergy(DarkEnergyComponent): + Ode0 = 0.7 + + def w(self, z, /): + return -1.0 + + +class ConcreteDarkEnergy(DarkEnergyComponent): + Ode0 = 0.7 + + def w(self, z, /): + return -1.0 + + def inv_efunc(self, z): + return np.ones_like(np.asarray(z)) + + +@pytest.fixture +def minimal_de(): + return MinimalDarkEnergy() + + +@pytest.fixture +def concrete_de(): + return ConcreteDarkEnergy() + + +def test_darkenergy_signature_and_missing_inv_efunc_raises(minimal_de): + assert hasattr(DarkEnergyComponent, "w") + assert is_positional_only(DarkEnergyComponent.w, "z") + # Ode requires inv_efunc; calling should raise NotImplementedError + with pytest.raises(NotImplementedError): + minimal_de.Ode(1) + + +def test_darkenergy_with_inv_efunc(concrete_de): + pytest.importorskip("scipy") + # For w = -1 (cosmological constant) the density scale is 1, so Ode==Ode0 + val = concrete_de.Ode(1) + assert pytest.approx(val) == 0.7 diff --git a/astropy/cosmology/_src/tests/traits/test_trait_darkmatter.py b/astropy/cosmology/_src/tests/traits/test_trait_darkmatter.py new file mode 100644 index 000000000000..2973e9f246e6 --- /dev/null +++ b/astropy/cosmology/_src/tests/traits/test_trait_darkmatter.py @@ -0,0 +1,25 @@ +import numpy as np +import pytest +from numpy.testing import assert_allclose + +from astropy.cosmology._src.traits.darkmatter import DarkMatterComponent + +from .helper import is_positional_only + + +class DummyDarkMatter(DarkMatterComponent): + Odm0 = 0.25 + + def inv_efunc(self, z): + return np.ones_like(np.asarray(z)) + + +@pytest.fixture +def dummy_darkmatter(): + return DummyDarkMatter() + + +def test_darkmatter_signature_and_behavior(dummy_darkmatter): + assert hasattr(DarkMatterComponent, "Odm") + assert is_positional_only(DarkMatterComponent.Odm, "z") + assert_allclose(dummy_darkmatter.Odm(1), 0.25 * (1 + 1) ** 3) diff --git a/astropy/cosmology/_src/tests/traits/test_trait_hubble.py b/astropy/cosmology/_src/tests/traits/test_trait_hubble.py new file mode 100644 index 000000000000..6be7fae7ea2c --- /dev/null +++ b/astropy/cosmology/_src/tests/traits/test_trait_hubble.py @@ -0,0 +1,31 @@ +import numpy as np +import pytest + +import astropy.units as u +from astropy.cosmology._src.traits.hubble import HubbleParameter +from astropy.tests.helper import assert_quantity_allclose + + +class DummyHubble(HubbleParameter): + H0 = 70 * u.km / (u.s * u.Mpc) + + def efunc(self, z): + return np.ones_like(np.asarray(z)) + + def inv_efunc(self, z): + return np.ones_like(np.asarray(z)) + + +@pytest.fixture +def dummy_hubble(): + return DummyHubble() + + +def test_hubble_H_and_properties(dummy_hubble): + h = dummy_hubble + H1 = h.H(1) + assert isinstance(H1, u.Quantity) + assert_quantity_allclose(H1, 70 * u.km / (u.s * u.Mpc)) + # h property + assert hasattr(h, "h") + assert isinstance(h.h, (float, np.floating)) diff --git a/astropy/cosmology/_src/tests/traits/test_trait_matter.py b/astropy/cosmology/_src/tests/traits/test_trait_matter.py new file mode 100644 index 000000000000..4fb30cbf891f --- /dev/null +++ b/astropy/cosmology/_src/tests/traits/test_trait_matter.py @@ -0,0 +1,56 @@ +import numpy as np +import pytest +from numpy.testing import assert_allclose + +from astropy.cosmology._src.traits.matter import MatterComponent + +from .helper import is_positional_only + + +class DummyMatter(MatterComponent): + Om0 = 0.3 + + def inv_efunc(self, z): + return np.ones_like(np.asarray(z)) + + +class ZeroMatter(MatterComponent): + Om0 = 0.0 + + def inv_efunc(self, z): + return np.ones_like(np.asarray(z)) + + +@pytest.fixture +def dummy_matter(): + return DummyMatter() + + +@pytest.fixture +def zero_matter(): + return ZeroMatter() + + +def test_matter_exists_and_signature(): + assert hasattr(MatterComponent, "Om") + assert is_positional_only(MatterComponent.Om, "z") + + +def test_matter_scalar_array_quantity_behavior(dummy_matter): + d = dummy_matter + # keyword should raise TypeError because z is positional-only + with pytest.raises(TypeError): + d.Om(z=1) + + # scalar + assert_allclose(d.Om(1), 0.3 * (1 + 1) ** 3) + + # array + zin = np.array([0.0, 1.0]) + out = d.Om(zin) + assert out.shape == zin.shape + assert_allclose(out, 0.3 * (1 + zin) ** 3) + + +def test_matter_zero_case(zero_matter): + assert_allclose(zero_matter.Om(1), 0.0) diff --git a/astropy/cosmology/_src/tests/traits/test_trait_neutrino.py b/astropy/cosmology/_src/tests/traits/test_trait_neutrino.py new file mode 100644 index 000000000000..946532aa0a88 --- /dev/null +++ b/astropy/cosmology/_src/tests/traits/test_trait_neutrino.py @@ -0,0 +1,39 @@ +import numpy as np +import pytest + +import astropy.units as u +from astropy.cosmology._src.traits.neutrino import NeutrinoComponent +from astropy.tests.helper import assert_quantity_allclose + + +class DummyNeutrino(NeutrinoComponent): + Tcmb0 = 2.7255 * u.K + Ogamma0 = 5e-5 + + @property + def has_massive_nu(self): + return False + + @property + def Onu0(self): + return 0.22710731766 * 3.046 * self.Ogamma0 + + def nu_relative_density(self, z): + return 0.22710731766 * 3.046 * np.ones_like(np.asarray(z)) + + def Ogamma(self, z): + return self.Ogamma0 * (np.asarray(z) + 1.0) ** 4 + + +@pytest.fixture +def dummy_neutrino(): + return DummyNeutrino() + + +def test_neutrino_onu_and_tnu_basic(dummy_neutrino): + d = dummy_neutrino + out = d.Onu(1) + # scalar-like + assert np.asarray(out).shape == () + # Tnu scales as (1+z) + assert_quantity_allclose(d.Tnu(1), d.Tnu0 * (1 + 1)) diff --git a/astropy/cosmology/_src/tests/traits/test_trait_photoncomponent.py b/astropy/cosmology/_src/tests/traits/test_trait_photoncomponent.py new file mode 100644 index 000000000000..008dacbbb627 --- /dev/null +++ b/astropy/cosmology/_src/tests/traits/test_trait_photoncomponent.py @@ -0,0 +1,25 @@ +import numpy as np +import pytest + +from astropy.cosmology._src.traits.photoncomponent import PhotonComponent +from astropy.tests.helper import assert_quantity_allclose + +from .helper import is_positional_only + + +class DummyPhoton(PhotonComponent): + Ogamma0 = 1e-4 + + def inv_efunc(self, z): + return np.ones_like(np.asarray(z)) + + +@pytest.fixture +def dummy_photon(): + return DummyPhoton() + + +def test_photon_signature_and_behavior(dummy_photon): + assert hasattr(PhotonComponent, "Ogamma") + assert is_positional_only(PhotonComponent.Ogamma, "z") + assert_quantity_allclose(dummy_photon.Ogamma(1), 1e-4 * (1 + 1) ** 4) diff --git a/astropy/cosmology/_src/tests/traits/test_trait_rhocrit.py b/astropy/cosmology/_src/tests/traits/test_trait_rhocrit.py new file mode 100644 index 000000000000..8f77eaeaa8f6 --- /dev/null +++ b/astropy/cosmology/_src/tests/traits/test_trait_rhocrit.py @@ -0,0 +1,24 @@ +import numpy as np +import pytest + +import astropy.units as u +from astropy.cosmology._src.traits.rhocrit import CriticalDensity +from astropy.tests.helper import assert_quantity_allclose + + +class DummyRho(CriticalDensity): + critical_density0 = 1.0 * u.kg / (u.m**3) + + def efunc(self, z): + return np.ones_like(np.asarray(z)) + + +@pytest.fixture +def dummy_rho(): + return DummyRho() + + +def test_critical_density_returns_quantity(dummy_rho): + rho = dummy_rho.critical_density(1) + assert isinstance(rho, u.Quantity) + assert_quantity_allclose(rho, 1.0 * u.kg / (u.m**3)) diff --git a/astropy/cosmology/_src/tests/traits/test_trait_scale_factor.py b/astropy/cosmology/_src/tests/traits/test_trait_scale_factor.py new file mode 100644 index 000000000000..c4d3d8b9f6e9 --- /dev/null +++ b/astropy/cosmology/_src/tests/traits/test_trait_scale_factor.py @@ -0,0 +1,39 @@ +import pytest +from numpy.testing import assert_allclose + +from astropy.cosmology._src.traits.scale_factor import ScaleFactor + +from .helper import is_positional_only + + +class DummyScale(ScaleFactor): + pass + + +@pytest.fixture +def dummy_scale(): + return DummyScale() + + +def test_scale_factor_behavior_and_signature(dummy_scale): + s = dummy_scale + assert hasattr(ScaleFactor, "scale_factor") + # basic value + assert_allclose(s.scale_factor(1), 1.0 / (1 + 1)) + + # scale_factor0 default + assert s.scale_factor0 == 1 << s.scale_factor0.unit + + # positional-only API + assert is_positional_only(ScaleFactor.scale_factor, "z") + + # passing as keyword should raise TypeError (positional-only) + with pytest.raises(TypeError): + s.scale_factor(z=1) + + # array input returns same-shaped array + import numpy as np + + arr = np.array([0.0, 1.0, 2.0]) + out = s.scale_factor(arr) + assert out.shape == arr.shape diff --git a/astropy/cosmology/_src/tests/traits/test_trait_tcmb.py b/astropy/cosmology/_src/tests/traits/test_trait_tcmb.py new file mode 100644 index 000000000000..c2ae729b9246 --- /dev/null +++ b/astropy/cosmology/_src/tests/traits/test_trait_tcmb.py @@ -0,0 +1,22 @@ +import pytest + +import astropy.units as u +from astropy.cosmology._src.traits.tcmb import TemperatureCMB +from astropy.tests.helper import assert_quantity_allclose + + +class DummyTcmb(TemperatureCMB): + Tcmb0 = 2.7 * u.K + + +@pytest.fixture +def dummy_tcmb(): + return DummyTcmb() + + +def test_tcmb_behavior_and_signature(dummy_tcmb): + t = dummy_tcmb + assert hasattr(TemperatureCMB, "Tcmb") + tout = t.Tcmb(1) + assert tout.unit == u.K + assert_quantity_allclose(tout, 2.7 * u.K * (1 + 1)) diff --git a/astropy/cosmology/_src/tests/traits/test_trait_totalcomponent.py b/astropy/cosmology/_src/tests/traits/test_trait_totalcomponent.py new file mode 100644 index 000000000000..cc7bbaf11aca --- /dev/null +++ b/astropy/cosmology/_src/tests/traits/test_trait_totalcomponent.py @@ -0,0 +1,24 @@ +import numpy as np +import pytest +from numpy.testing import assert_allclose + +from astropy.cosmology._src.traits.totalcomponent import TotalComponent + + +class DummyTotal(TotalComponent): + @property + def Otot0(self): + return 1.0 + + def Otot(self, z, /): + z = np.asarray(z) + return np.ones_like(z) + + +@pytest.fixture +def dummy_total(): + return DummyTotal() + + +def test_totalcomponent_minimal_impl(dummy_total): + assert_allclose(dummy_total.Otot(1), 1.0) diff --git a/astropy/cosmology/_src/traits/__init__.py b/astropy/cosmology/_src/traits/__init__.py new file mode 100644 index 000000000000..f0f80abf9b12 --- /dev/null +++ b/astropy/cosmology/_src/traits/__init__.py @@ -0,0 +1,33 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +"""Astropy Cosmology. **NOT public API**. + +The public API is provided by `astropy.cosmology.traits`. +""" + +__all__ = ( + "BaryonComponent", + "CriticalDensity", + "CurvatureComponent", + "DarkEnergyComponent", + "DarkMatterComponent", + "HubbleParameter", + "MatterComponent", + "NeutrinoComponent", + "PhotonComponent", + "ScaleFactor", + "TemperatureCMB", + "TotalComponent", +) + +from .baryons import BaryonComponent +from .curvature import CurvatureComponent +from .darkenergy import DarkEnergyComponent +from .darkmatter import DarkMatterComponent +from .hubble import HubbleParameter +from .matter import MatterComponent +from .neutrino import NeutrinoComponent +from .photoncomponent import PhotonComponent +from .rhocrit import CriticalDensity +from .scale_factor import ScaleFactor +from .tcmb import TemperatureCMB +from .totalcomponent import TotalComponent diff --git a/astropy/cosmology/_src/traits/baryons.py b/astropy/cosmology/_src/traits/baryons.py new file mode 100644 index 000000000000..91c0215ee018 --- /dev/null +++ b/astropy/cosmology/_src/traits/baryons.py @@ -0,0 +1,47 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +"""Baryon component.""" + +__all__ = ("BaryonComponent",) + +from collections.abc import Callable +from typing import Any + +import numpy as np +from numpy.typing import ArrayLike, NDArray + +from astropy.cosmology._src.typing import FArray +from astropy.cosmology._src.utils import aszarr +from astropy.units import Quantity + + +class BaryonComponent: + """The cosmology has attributes and methods for the baryon density.""" + + Ob0: float | np.floating + """Omega baryons: density of baryonic matter in units of the critical density at z=0.""" + + inv_efunc: Callable[[NDArray[Any]], NDArray[Any]] + + def Ob(self, z: Quantity | ArrayLike, /) -> FArray: + """Return the density parameter for baryonic matter at redshift ``z``. + + Parameters + ---------- + z : Quantity-like ['redshift'], array-like + Input redshift. + + .. versionchanged:: 7.0 + Passing z as a keyword argument is deprecated. + + .. versionchanged:: 8.0 + z must be a positional argument. + + Returns + ------- + Ob : ndarray + The density of baryonic matter relative to the critical density at + each redshift. + + """ + z = aszarr(z) + return self.Ob0 * (z + 1.0) ** 3 * self.inv_efunc(z) ** 2 diff --git a/astropy/cosmology/_src/traits/curvature.py b/astropy/cosmology/_src/traits/curvature.py new file mode 100644 index 000000000000..bce1e84d3ba4 --- /dev/null +++ b/astropy/cosmology/_src/traits/curvature.py @@ -0,0 +1,90 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +"""Global Curvature. + +This is private API. See `~astropy.cosmology.traits` for public API. + +""" + +__all__ = ("CurvatureComponent",) + +import abc + +import numpy as np +from numpy.typing import ArrayLike, NDArray + +from astropy.cosmology._src.utils import aszarr +from astropy.units import Quantity + + +class CurvatureComponent: + """The object has attributes and methods related to the global curvature. + + This is a trait class; it is not meant to be instantiated directly, but + instead to be used as a mixin to other classes. + + """ + + @property + @abc.abstractmethod + def Ok0(self) -> float | np.floating: + """Omega curvature; the effective curvature density/critical density at z=0.""" + raise NotImplementedError + + @property + @abc.abstractmethod + def is_flat(self) -> bool: + """Return `bool`; `True` if the cosmology is globally flat.""" + raise NotImplementedError + + def Ok(self, z: Quantity | ArrayLike, /) -> NDArray[np.floating]: + """Return the equivalent density parameter for curvature at redshift ``z``. + + Parameters + ---------- + z : Quantity-like ['redshift'], array-like + Input redshift. + + .. versionchanged:: 7.0 + Passing z as a keyword argument is deprecated. + + .. versionchanged:: 8.0 + z must be a positional argument. + + Returns + ------- + Ok : ndarray + The equivalent density parameter for curvature at each redshift. + + .. versionchanged:: 7.2 + Always returns a numpy object, never a `float`. + + Examples + -------- + >>> import numpy as np + >>> from astropy.cosmology import Planck18, units as cu + + >>> Planck18.Ok(2) + array(0.) + + >>> Planck18.Ok([1, 2]) + array([0., 0.]) + + >>> Planck18.Ok(np.array([2])) + array([0.]) + + >>> Planck18.Ok(2 * cu.redshift) + array(0.) + + >>> cosmo = Planck18.clone(Ode0=0.71, to_nonflat=True) + + >>> cosmo.Ok0 + np.float64(-0.021153694455455927) + + >>> cosmo.Ok(100) + np.float64(-0.0006557825253017665) + + """ + z = aszarr(z) + if self.Ok0 == 0: # Common enough to be worth checking explicitly + return np.zeros(getattr(z, "shape", ())) + return self.Ok0 * (z + 1.0) ** 2 * self.inv_efunc(z) ** 2 diff --git a/astropy/cosmology/_src/traits/darkenergy.py b/astropy/cosmology/_src/traits/darkenergy.py new file mode 100644 index 000000000000..50f7f7090b1a --- /dev/null +++ b/astropy/cosmology/_src/traits/darkenergy.py @@ -0,0 +1,151 @@ +from abc import abstractmethod +from math import exp, log + +import numpy as np +from numpy.typing import ArrayLike + +from astropy.cosmology._src.scipy_compat import quad +from astropy.cosmology._src.typing import FArray +from astropy.cosmology._src.utils import aszarr +from astropy.units import Quantity + + +class DarkEnergyComponent: + # Subclasses should use `Parameter` to make this a parameter of the cosmology. + Ode0: float | np.floating + """Omega dark energy; dark energy density/critical density at z=0.""" + + @abstractmethod + def w(self, z: Quantity | ArrayLike, /) -> FArray: + r"""The dark energy equation of state. + + Parameters + ---------- + z : Quantity-like ['redshift'], array-like + Input redshift. + + .. versionchanged:: 7.0 + Passing z as a keyword argument is deprecated. + + .. versionchanged:: 8.0 + z must be a positional argument. + + Returns + ------- + w : ndarray or float + The dark energy equation of state. + `float` if scalar input. + + Notes + ----- + The dark energy equation of state is defined as + :math:`w(z) = P(z)/\rho(z)`, where :math:`P(z)` is the pressure at + redshift z and :math:`\rho(z)` is the density at redshift z, both in + units where c=1. + + This must be overridden by subclasses. + """ + raise NotImplementedError("w(z) is not implemented") + + def _w_integrand(self, ln1pz: float | FArray, /) -> FArray: + """Internal convenience function for w(z) integral (eq. 5 of [1]_). + + Parameters + ---------- + ln1pz : `~numbers.Number` or scalar ndarray, positional-only + Assumes scalar input, since this should only be called inside an + integral. + + .. versionchanged:: 7.0 + The argument is positional-only. + + References + ---------- + .. [1] Linder, E. (2003). Exploring the Expansion History of the + Universe. Phys. Rev. Lett., 90, 091301. + """ + return 1.0 + self.w(exp(ln1pz) - 1.0) + + def de_density_scale(self, z: Quantity | ArrayLike, /) -> FArray: + r"""Evaluates the redshift dependence of the dark energy density. + + Parameters + ---------- + z : Quantity-like ['redshift'], array-like + Input redshift. + + .. versionchanged:: 7.0 + Passing z as a keyword argument is deprecated. + + .. versionchanged:: 8.0 + z must be a positional argument. + + Returns + ------- + I : ndarray or float + The scaling of the energy density of dark energy with redshift. + Returns `float` if the input is scalar. + + Notes + ----- + The scaling factor, I, is defined by :math:`\rho(z) = \rho_0 I`, + and is given by + + .. math:: + + I = \exp \left( 3 \int_{a}^1 \frac{ da^{\prime} }{ a^{\prime} } + \left[ 1 + w\left( a^{\prime} \right) \right] \right) + + The actual integral used is rewritten from [1]_ to be in terms of z. + + It will generally helpful for subclasses to overload this method if + the integral can be done analytically for the particular dark + energy equation of state that they implement. + + References + ---------- + .. [1] Linder, E. (2003). Exploring the Expansion History of the + Universe. Phys. Rev. Lett., 90, 091301. + """ + # This allows for an arbitrary w(z) following eq (5) of + # Linder 2003, PRL 90, 91301. The code here evaluates + # the integral numerically. However, most popular + # forms of w(z) are designed to make this integral analytic, + # so it is probably a good idea for subclasses to overload this + # method if an analytic form is available. + z = aszarr(z) + ival = ( + quad(self._w_integrand, 0, log(z + 1.0))[0] # scalar + if z.ndim == 0 + else np.asarray([quad(self._w_integrand, 0, log(1 + _z))[0] for _z in z]) + ) + return np.exp(3 * ival) + + def Ode(self, z: Quantity | ArrayLike, /) -> FArray: + """Return the density parameter for dark energy at redshift ``z``. + + Parameters + ---------- + z : Quantity-like ['redshift'], array-like + Input redshift. + + .. versionchanged:: 7.0 + Passing z as a keyword argument is deprecated. + + .. versionchanged:: 8.0 + z must be a positional argument. + + Returns + ------- + Ode : ndarray + The density of dark energy relative to the critical density at each + redshift. + """ + z = aszarr(z) + if self.Ode0 == 0: # Common enough to be worth checking explicitly + return np.zeros_like(z) + # Ensure self.inv_efunc is implemented by the main class + if not hasattr(self, "inv_efunc") or not callable(self.inv_efunc): + msg = "The main class must implement an 'inv_efunc(z)' method." + raise NotImplementedError(msg) + return self.Ode0 * self.de_density_scale(z) * self.inv_efunc(z) ** 2 diff --git a/astropy/cosmology/_src/traits/darkmatter.py b/astropy/cosmology/_src/traits/darkmatter.py new file mode 100644 index 000000000000..2e5f47b5ca0f --- /dev/null +++ b/astropy/cosmology/_src/traits/darkmatter.py @@ -0,0 +1,50 @@ +"""Trait for dark matter component of cosmology.""" + +__all__ = ("DarkMatterComponent",) + +from collections.abc import Callable +from typing import Any + +import numpy as np +from numpy.typing import ArrayLike, NDArray + +from astropy.cosmology._src.typing import FArray +from astropy.cosmology._src.utils import aszarr +from astropy.units import Quantity + + +class DarkMatterComponent: + """The cosmology has attributes and methods for the dark matter density. + + This trait provides an ``Odm`` method that returns the dark matter + density parameter (i.e., total matter minus baryons) at redshift ``z``. + """ + + Odm0: float | np.floating + """Omega dark matter: dark matter density/critical density at z=0.""" + + inv_efunc: Callable[[NDArray[Any]], NDArray[Any]] + + def Odm(self, z: Quantity | ArrayLike, /) -> FArray: + """Return the density parameter for dark matter at redshift ``z``. + + Parameters + ---------- + z : Quantity-like ['redshift'], array-like + Input redshift. + + .. versionchanged:: 7.0 + Passing z as a keyword argument is deprecated. + + .. versionchanged:: 8.0 + z must be a positional argument. + + Returns + ------- + Odm : ndarray + The density of dark matter relative to the critical density at + each redshift. + + """ + z = aszarr(z) + return self.Odm0 * (z + 1.0) ** 3 * self.inv_efunc(z) ** 2 diff --git a/astropy/cosmology/_src/traits/hubble.py b/astropy/cosmology/_src/traits/hubble.py new file mode 100644 index 000000000000..94b18e2b2c9f --- /dev/null +++ b/astropy/cosmology/_src/traits/hubble.py @@ -0,0 +1,60 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +"""Hubble parameter trait. + +This is private API. See `~astropy.cosmology.traits` for public API. +""" + +__all__ = ("HubbleParameter",) + +from collections.abc import Callable +from functools import cached_property +from typing import Any + +import numpy as np +from numpy.typing import ArrayLike, NDArray + +import astropy.units as u +from astropy import constants as const +from astropy.cosmology._src.typing import FArray +from astropy.units import Quantity + + +class HubbleParameter: + """The object has attributes and methods for the Hubble parameter.""" + + H0: Quantity + """Hubble Parameter at redshift 0.""" + + efunc: Callable[[Any], NDArray[Any]] + + inv_efunc: Callable[[Any], FArray | float] + + def H(self, z: Quantity | ArrayLike, /) -> Quantity: + """Hubble parameter at redshift ``z``. + + Parameters + ---------- + z : Quantity-like ['redshift'], array-like + Input redshift. + + Returns + ------- + H : Quantity ['frequency'] + Hubble parameter at each input redshift. + """ + return self.H0 * self.efunc(z) + + @cached_property + def h(self) -> np.floating: + """Dimensionless Hubble constant: h = H_0 / 100 [km/sec/Mpc].""" + return self.H0.to_value("km/(s Mpc)") / 100.0 + + @cached_property + def hubble_time(self) -> u.Quantity: + """Hubble time.""" + return (1 / self.H0).to(u.Gyr) + + @cached_property + def hubble_distance(self) -> u.Quantity: + """Hubble distance.""" + return (const.c / self.H0).to(u.Mpc) diff --git a/astropy/cosmology/_src/traits/matter.py b/astropy/cosmology/_src/traits/matter.py new file mode 100644 index 000000000000..0f6c19a0f240 --- /dev/null +++ b/astropy/cosmology/_src/traits/matter.py @@ -0,0 +1,45 @@ +"""Matter component.""" + +import numpy as np +from numpy.typing import ArrayLike + +from astropy.cosmology._src.typing import FArray +from astropy.cosmology._src.utils import aszarr +from astropy.units import Quantity + +__all__ = ("MatterComponent",) + + +class MatterComponent: + """The cosmology has attributes and methods for the matter density.""" + + Om0: float | np.floating + """Omega matter; matter density/critical density at z=0.""" + + def Om(self, z: Quantity | ArrayLike, /) -> FArray: + """Return the density parameter for non-relativistic matter at redshift ``z``. + + Parameters + ---------- + z : Quantity-like ['redshift'], array-like + Input redshift. + + .. versionchanged:: 7.0 + Passing z as a keyword argument is deprecated. + + .. versionchanged:: 8.0 + z must be a positional argument. + + Returns + ------- + Om : ndarray + The density of non-relativistic matter relative to the critical + density at each redshift. + + Notes + ----- + This does not include neutrinos, even if non-relativistic at the + redshift of interest. + """ + z = aszarr(z) + return self.Om0 * (z + 1.0) ** 3 * self.inv_efunc(z) ** 2 diff --git a/astropy/cosmology/_src/traits/neutrino.py b/astropy/cosmology/_src/traits/neutrino.py new file mode 100644 index 000000000000..390501fb2687 --- /dev/null +++ b/astropy/cosmology/_src/traits/neutrino.py @@ -0,0 +1,258 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +r"""Neutrino component trait. + +This is private API. See `~astropy.cosmology.traits` for public API. +""" + +__all__ = ["NeutrinoComponent"] + +from abc import abstractmethod +from collections.abc import Callable +from functools import cached_property +from typing import Final + +import numpy as np +from numpy.typing import ArrayLike + +from astropy.cosmology._src.typing import FArray +from astropy.cosmology._src.utils import aszarr, deprecated_keywords +from astropy.units import Quantity + +# Physics constants for neutrino calculations +TEMP_NEUTRINO: Final = 0.7137658555036082 # (4/11)^1/3 +NEUTRINO_FERMI_DIRAC_CORRECTION: Final = 0.22710731766 # 7/8 (4/11)^4/3 + + +############################################################################## + + +class NeutrinoComponent: + r"""The cosmology has attributes and methods for the neutrino density. + + This trait handles both massless neutrinos (relativistic, radiation-like) + and massive neutrinos (with complex evolution). + + This is an abstract trait. Subclasses must implement: + + - `has_massive_nu` (property): Whether there are massive neutrinos + - `Onu0` (property): Neutrino density parameter at z=0 + - `nu_relative_density` (method): Neutrino-to-photon density ratio at redshift z + + The parent class must provide ``Tcmb0`` (CMB temperature) and ``Ogamma`` + (method to compute photon density at redshift z). + + Notes + ----- + The density in neutrinos is given by: + + .. math:: + + \rho_{\nu} \left(a\right) = 0.2271 \, N_{eff} \, + f\left(m_{\nu} a / T_{\nu 0} \right) \, + \rho_{\\gamma} \left( a \right) + + where + + .. math:: + + f \left(y\right) = \frac{120}{7 \pi^4} + \int_0^{\\infty} \, dx \frac{x^2 \\sqrt{x^2 + y^2}} + {e^x + 1} + + assuming that all neutrino species have the same mass. + + If they have different masses, a similar term is calculated for each + one. + + Note that ``f`` has the asymptotic behavior :math:`f(0) = 1`. This + method returns :math:`0.2271 f` using an analytical fitting formula + (Komatsu et al., 2011), ApJS, 192, 18. + + The neutrino density evolution depends on whether neutrinos are massive or massless: + + - **Massless neutrinos**: Behave like radiation with density scaling as (1+z)^4. + The density is simply proportional to the photon density with a constant ratio + determined by Neff and Fermi-Dirac statistics. + + - **Massive neutrinos**: Have complex evolution that transitions from relativistic + (radiation-like) at early times to non-relativistic (matter-like) at late times. + The implementation typically uses the Komatsu fitting formula (Komatsu + et al., 2011) for computational efficiency. + + References + ---------- + Komatsu et al. (2011), "Seven-Year Wilkinson Microwave Anisotropy Probe + (WMAP) Observations: Cosmological Interpretation", ApJS, 192, 18. + + Examples + -------- + >>> import numpy as np + >>> import astropy.units as u + >>> from astropy.cosmology.traits import NeutrinoComponent + >>> NEUTRINO_FERMI_DIRAC_CORRECTION = 0.22710731766 # 7/8 (4/11)^4/3 + >>> + >>> class ExampleNeutrinoCosmology(NeutrinoComponent): + ... def __init__(self): + ... self.Tcmb0 = 2.7255 * u.K + ... self.Neff = 3.046 + ... self.Ogamma0 = 5e-5 + ... @property + ... def has_massive_nu(self): + ... return False + ... @property + ... def Onu0(self): + ... return NEUTRINO_FERMI_DIRAC_CORRECTION * self.Neff * self.Ogamma0 + ... def nu_relative_density(self, z): + ... return NEUTRINO_FERMI_DIRAC_CORRECTION * self.Neff * np.ones_like(np.asarray(z)) + ... def Ogamma(self, z): + ... return self.Ogamma0 * (np.asarray(z) + 1.0) ** 4 + """ + + # Type annotations for dependencies (provided by parent class) + Tcmb0: Quantity + Ogamma: Callable[[ArrayLike], FArray] + + @property + @abstractmethod + def has_massive_nu(self) -> bool: + """Does this cosmology have at least one massive neutrino species? + + Returns + ------- + has_massive_nu : bool + True if at least one neutrino species has non-zero mass. + + Notes + ----- + Subclasses must implement this property. + """ + + @property + @abstractmethod + def Onu0(self) -> float: + """Omega nu; the density/critical density of neutrinos at z=0. + + Returns + ------- + Onu0 : float + The density parameter for neutrinos at z=0. + + Notes + ----- + Subclasses must implement this property. + """ + + @abstractmethod + def nu_relative_density(self, z: Quantity | ArrayLike) -> FArray: + r"""Neutrino density function relative to the energy density in photons. + + Parameters + ---------- + z : Quantity-like ['redshift'], array-like + Input redshift. + + Returns + ------- + f : array + The neutrino density scaling factor relative to the density in + photons at each redshift. + + Notes + ----- + Subclasses must implement this method. For massless neutrinos, this + should return a constant. For massive neutrinos, this should use an + appropriate fitting formula (e.g., Komatsu et al. 2011). + """ + + @cached_property + def Tnu0(self) -> Quantity: + """Temperature of the neutrino background as |Quantity| at z=0. + + Returns + ------- + Tnu0 : Quantity ['temperature'] + The neutrino temperature at z=0 in Kelvin. + + Notes + ----- + The neutrino temperature is related to the CMB temperature by: + + .. math:: + + T_{\\nu 0} = \\left(\\frac{4}{11}\\right)^{1/3} T_{CMB} + + This comes from the decoupling of neutrinos before electron-positron + annihilation. See Weinberg 'Cosmology' p 154 eq (3.1.21). + """ + # The constant in front is (4/11)^1/3 -- see any cosmology book for an + # explanation -- for example, Weinberg 'Cosmology' p 154 eq (3.1.21). + return TEMP_NEUTRINO * self.Tcmb0 + + @deprecated_keywords("z", since="7.0") + def Onu(self, z: Quantity | ArrayLike) -> FArray: + r"""Return the density parameter for neutrinos at redshift ``z``. + + Parameters + ---------- + z : Quantity-like ['redshift'], array-like + Input redshift. + + .. versionchanged:: 7.0 + Passing z as a keyword argument is deprecated. + + Returns + ------- + Onu : ndarray + The energy density of neutrinos relative to the critical density at + each redshift. Note that this includes their kinetic energy (if + they have mass), so it is not equal to the commonly used + :math:`\sum \frac{m_{\nu}}{94 eV}`, which does not include + kinetic energy. + + Notes + ----- + The neutrino density parameter evolves with redshift according to: + + .. math:: + + \\Omega_{\\nu}(z) = \\Omega_{\\gamma}(z) \\times f(z) + + where f(z) is the neutrino-to-photon density ratio computed by + nu_relative_density(z). For massless neutrinos, f(z) is constant. + For massive neutrinos, f(z) evolves as neutrinos transition from + relativistic to non-relativistic. + """ + z = aszarr(z) + if self.Onu0 == 0: # Common enough to be worth checking explicitly + return np.zeros_like(z) + return self.Ogamma(z) * self.nu_relative_density(z) + + @deprecated_keywords("z", since="7.0") + def Tnu(self, z: Quantity | ArrayLike) -> Quantity: + """Return the neutrino temperature at redshift ``z``. + + Parameters + ---------- + z : Quantity-like ['redshift'], array-like + Input redshift. + + .. versionchanged:: 7.0 + Passing z as a keyword argument is deprecated. + + Returns + ------- + Tnu : Quantity ['temperature'] + The temperature of the cosmic neutrino background in K. + + Notes + ----- + The neutrino temperature scales with redshift as: + + .. math:: + + T_{\\nu}(z) = T_{\\nu 0} (1 + z) + + This simple scaling applies to both massless and massive neutrinos, + as the temperature depends only on the expansion of the universe. + """ + return self.Tnu0 * (aszarr(z) + 1.0) diff --git a/astropy/cosmology/_src/traits/photoncomponent.py b/astropy/cosmology/_src/traits/photoncomponent.py new file mode 100644 index 000000000000..d06490a2353b --- /dev/null +++ b/astropy/cosmology/_src/traits/photoncomponent.py @@ -0,0 +1,46 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +"""Photon component.""" + +__all__ = ("PhotonComponent",) + +from collections.abc import Callable +from typing import Any + +import numpy as np +from numpy.typing import ArrayLike, NDArray + +from astropy.cosmology._src.typing import FArray +from astropy.cosmology._src.utils import aszarr +from astropy.units import Quantity + + +class PhotonComponent: + """The cosmology has attributes and methods for the photon density.""" + + Ogamma0: float | np.floating + """Omega gamma; the density/critical density of photons at z=0.""" + + inv_efunc: Callable[[NDArray[Any]], NDArray[Any]] + + def Ogamma(self, z: Quantity | ArrayLike, /) -> FArray: + """Return the density parameter for photons at redshift ``z``. + + Parameters + ---------- + z : Quantity-like ['redshift'], array-like + Input redshift. + + .. versionchanged:: 7.0 + Passing z as a keyword argument is deprecated. + + .. versionchanged:: 8.0 + z must be a positional argument. + + Returns + ------- + Ogamma : array + The energy density of photons relative to the critical density at + each redshift. + """ + z = aszarr(z) + return self.Ogamma0 * (z + 1.0) ** 4 * self.inv_efunc(z) ** 2 diff --git a/astropy/cosmology/_src/traits/rhocrit.py b/astropy/cosmology/_src/traits/rhocrit.py new file mode 100644 index 000000000000..feb555017733 --- /dev/null +++ b/astropy/cosmology/_src/traits/rhocrit.py @@ -0,0 +1,41 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +"""Critical density component.""" + +__all__ = ("CriticalDensity",) + +from collections.abc import Callable +from typing import Any + +from numpy.typing import ArrayLike, NDArray + +from astropy.units import Quantity + + +class CriticalDensity: + """The object has attributes and methods for the critical density.""" + + critical_density0: Quantity + """Critical density at redshift 0.""" + + efunc: Callable[[Any], NDArray[Any]] + + def critical_density(self, z: Quantity | ArrayLike, /) -> Quantity: + """Critical density in grams per cubic cm at redshift ``z``. + + Parameters + ---------- + z : Quantity-like ['redshift'], array-like + Input redshift. + + .. versionchanged:: 7.0 + Passing z as a keyword argument is deprecated. + + .. versionchanged:: 8.0 + z must be a positional argument. + + Returns + ------- + rho : Quantity ['mass density'] + Critical density at each input redshift. + """ + return self.critical_density0 * self.efunc(z) ** 2 diff --git a/astropy/cosmology/_src/traits/scale_factor.py b/astropy/cosmology/_src/traits/scale_factor.py new file mode 100644 index 000000000000..737e2c0a4474 --- /dev/null +++ b/astropy/cosmology/_src/traits/scale_factor.py @@ -0,0 +1,57 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +"""Scale factor. + +This is private API. See `~astropy.cosmology.traits` for public API. + +""" + +__all__ = ("ScaleFactor",) + + +from numpy.typing import ArrayLike + +import astropy.units as u +from astropy.cosmology._src.utils import aszarr + + +class ScaleFactor: + """The trait for computing the cosmological scale factor. + + The scale factor is defined as :math:`a = a_0 / (1 + z)`. + + """ + + @property + def scale_factor0(self) -> u.Quantity: + r"""Scale factor at redshift 0. + + The scale factor is defined as :math:`a = a_0 / (1 + z)`. The common convention + is to set :math:`a_0 = 1`. However, in some cases, like in some old CMB papers, + :math:`a_0` is used to normalize `a` to be a convenient number at the redshift + of interest for that paper. Explicitly using :math:`a_0` in both calculation and + code avoids ambiguity. + """ + return 1 << u.one + + def scale_factor(self, z: u.Quantity | ArrayLike, /) -> u.Quantity: + """Compute the scale factor at redshift ``z``. + + The scale factor is defined as :math:`a = a_0 / (1 + z)`. + + Parameters + ---------- + z : Quantity-like ['redshift'] | array-like + Input redshift. + + .. versionchanged:: 7.0 + Passing z as a keyword argument is deprecated. + + .. versionchanged:: 8.0 + z must be a positional argument. + + Returns + ------- + |Quantity| + Scale factor at each input redshift. + """ + return self.scale_factor0 / (aszarr(z) + 1) diff --git a/astropy/cosmology/_src/traits/tcmb.py b/astropy/cosmology/_src/traits/tcmb.py new file mode 100644 index 000000000000..97830c92d869 --- /dev/null +++ b/astropy/cosmology/_src/traits/tcmb.py @@ -0,0 +1,59 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +"""CMB Temperature. + +This is private API. See `~astropy.cosmology.traits` for public API. + +""" + +__all__ = ("TemperatureCMB",) + + +from numpy.typing import ArrayLike + +from astropy.cosmology._src.utils import aszarr +from astropy.units import Quantity + + +class TemperatureCMB: + """The trait for computing the cosmological background temperature.""" + + Tcmb0: Quantity + """Temperature of the CMB at z=0.""" + + def Tcmb(self, z: Quantity | ArrayLike, /) -> Quantity: + """Compute the CMB temperature at redshift ``z``. + + Parameters + ---------- + z : Quantity-like ['redshift'], array-like + Input redshift. + + .. versionchanged:: 7.0 + Passing z as a keyword argument is deprecated. + + .. versionchanged:: 8.0 + z must be a positional argument. + + Returns + ------- + Tcmb : Quantity ['temperature'] + The temperature of the CMB. + + Examples + -------- + >>> import astropy.units as u + >>> from astropy.cosmology import Planck18, units as cu + + >>> Planck18.Tcmb(u.Quantity([0.5, 1.0], cu.redshift)) + + + >>> Planck18.Tcmb(u.Quantity(0.5, '')) + + + >>> Planck18.Tcmb(0.5) + + + >>> Planck18.Tcmb([0.5, 1.0]) + + """ + return self.Tcmb0 * (aszarr(z) + 1.0) diff --git a/astropy/cosmology/_src/traits/totalcomponent.py b/astropy/cosmology/_src/traits/totalcomponent.py new file mode 100644 index 000000000000..d72f387b9329 --- /dev/null +++ b/astropy/cosmology/_src/traits/totalcomponent.py @@ -0,0 +1,42 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +__all__ = ("TotalComponent",) + +from abc import abstractmethod + +import numpy as np +from numpy.typing import ArrayLike + +from astropy.cosmology._src.typing import FArray +from astropy.units import Quantity + + +class TotalComponent: + """The cosmology has attributes and methods for the total density. + + This trait has the abstract ``Otot`` method that returns the total density + parameter at redshift ``z``. It should be the sum of all other components. + """ + + @property + @abstractmethod + def Otot0(self) -> float | np.floating: + """Omega total; the total density/critical density at z=0.""" + raise NotImplementedError # pragma: no cover + + @abstractmethod + def Otot(self, z: Quantity | ArrayLike, /) -> FArray: + """The total density parameter at redshift ``z``. + + Parameters + ---------- + z : Quantity-like ['redshift'], array-like + Input redshifts. + + Returns + ------- + Otot : array + The total density relative to the critical density at each + redshift. + """ + raise NotImplementedError # pragma: no cover diff --git a/astropy/cosmology/_src/typing.py b/astropy/cosmology/_src/typing.py new file mode 100644 index 000000000000..9003f3bdf067 --- /dev/null +++ b/astropy/cosmology/_src/typing.py @@ -0,0 +1,22 @@ +"""Static typing for :mod:`astropy.cosmology`. PRIVATE API.""" +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +__all__ = ("CosmoMeta", "FArray", "_CosmoT") + +from collections.abc import Mapping +from typing import TYPE_CHECKING, Any, TypeAlias, TypeVar + +import numpy as np +from numpy.typing import NDArray + +if TYPE_CHECKING: + import astropy.cosmology + +_CosmoT = TypeVar("_CosmoT", bound="astropy.cosmology.Cosmology") +"""Type variable for :class:`~astropy.cosmology.Cosmology` and subclasses.""" + +CosmoMeta: TypeAlias = Mapping[Any, Any] +"""Type alias for cosmology metadata.""" + +FArray: TypeAlias = NDArray[np.floating] +"""Type alias for numpy array of floating dtype.""" diff --git a/astropy/cosmology/_src/units.py b/astropy/cosmology/_src/units.py new file mode 100644 index 000000000000..997c6f9bf0c3 --- /dev/null +++ b/astropy/cosmology/_src/units.py @@ -0,0 +1,35 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +"""Cosmological units.""" + +__all__ = ("littleh", "redshift") + +from typing import Final + +import astropy.units as u + +_ns = globals() + + +# This is not formally a unit, but is used in that way in many contexts, and +# an appropriate equivalency is only possible if it's treated as a unit. +redshift: Final = u.def_unit( + ["redshift"], + prefixes=False, + namespace=_ns, + doc="Cosmological redshift.", + format={"latex": r""}, +) +u.def_physical_type(redshift, "redshift") + +# This is not formally a unit, but is used in that way in many contexts, and +# an appropriate equivalency is only possible if it's treated as a unit (see +# https://arxiv.org/pdf/1308.4150.pdf for more) +# Also note that h or h100 or h_100 would be a better name, but they either +# conflict or have numbers in them, which is disallowed +littleh: Final = u.def_unit( + ["littleh"], + namespace=_ns, + prefixes=False, + doc='Reduced/"dimensionless" Hubble constant', + format={"latex": r"h_{100}"}, +) diff --git a/astropy/cosmology/_src/units_equivalencies.py b/astropy/cosmology/_src/units_equivalencies.py new file mode 100644 index 000000000000..f4374c33ff4a --- /dev/null +++ b/astropy/cosmology/_src/units_equivalencies.py @@ -0,0 +1,369 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +"""Cosmological unit equivalencies.""" + +__all__ = ( + # redshift equivalencies + "dimensionless_redshift", + "redshift_distance", + "redshift_hubble", + "redshift_temperature", + # other equivalencies + "with_H0", + "with_redshift", +) + + +import sys +from typing import TYPE_CHECKING, Any, Literal, TypeAlias, Union + +import astropy.units as u +from astropy.cosmology._src.funcs.optimize import _ZAtValueKWArgs + +from .default import default_cosmology +from .funcs.optimize import z_at_value +from .units import littleh, redshift + +if TYPE_CHECKING: + import astropy.cosmology + +if sys.version_info < (3, 12): + _UnpackZAtValueKWArgs = Any +else: + from typing import Unpack + + _UnpackZAtValueKWArgs: TypeAlias = Unpack[_ZAtValueKWArgs] + + +__doctest_requires__ = {("with_redshift", "redshift_distance"): ["scipy"]} + + +def dimensionless_redshift() -> u.Equivalency: + """Allow redshift to be 1-to-1 equivalent to dimensionless. + + It is special compared to other equivalency pairs in that it allows + this independent of the power to which the redshift is raised, and + independent of whether it is part of a more complicated unit. It is + similar to u.dimensionless_angles() in this respect. + """ + return u.Equivalency([(redshift, None)], "dimensionless_redshift") + + +def redshift_distance( + cosmology: Union["astropy.cosmology.Cosmology", str, None] = None, + kind: Literal["comoving", "lookback", "luminosity"] = "comoving", + **atzkw: _UnpackZAtValueKWArgs, +) -> u.Equivalency: + """Convert quantities between redshift and distance. + + Care should be taken to not misinterpret a relativistic, gravitational, etc + redshift as a cosmological one. + + Parameters + ---------- + cosmology : `~astropy.cosmology.Cosmology`, str, or None, optional + A cosmology realization or built-in cosmology's name (e.g. 'Planck18'). + If None, will use the default cosmology + (controlled by |default_cosmology|). + kind : {'comoving', 'lookback', 'luminosity'}, optional + The distance type for the Equivalency. + Note this does NOT include the angular diameter distance as this + distance measure is not monotonic. + **atzkw + keyword arguments for :func:`~astropy.cosmology.z_at_value`, which is used to + convert distance to redshift. + + Returns + ------- + `~astropy.units.equivalencies.Equivalency` + Equivalency between redshift and temperature. + + Raises + ------ + `~astropy.cosmology.CosmologyError` + If the distance corresponds to a redshift that is larger than ``zmax``. + Exception + See :func:`~astropy.cosmology.z_at_value` for possible exceptions, e.g. if the + distance maps to a redshift that is larger than ``zmax``, the maximum redshift. + + Examples + -------- + >>> import astropy.units as u + >>> import astropy.cosmology.units as cu + >>> from astropy.cosmology import WMAP9 + + >>> z = 1100 * cu.redshift + >>> d = z.to(u.Mpc, cu.redshift_distance(WMAP9, kind="comoving")) + >>> d # doctest: +FLOAT_CMP + + + The reverse operation is also possible, though not always as simple. To convert a + very large distance to a redshift it might be necessary to specify a large enough + ``zmax`` value. See :func:`~astropy.cosmology.z_at_value` for details. + + >>> d.to(cu.redshift, cu.redshift_distance(WMAP9, kind="comoving", zmax=1200)) # doctest: +FLOAT_CMP + + """ + # get cosmology: None -> default and process str / class + cosmology = cosmology if cosmology is not None else default_cosmology.get() + with default_cosmology.set(cosmology): # if already cosmo, passes through + cosmology = default_cosmology.get() + + allowed_kinds = ("comoving", "lookback", "luminosity") + if kind not in allowed_kinds: + raise ValueError(f"`kind` is not one of {allowed_kinds}") + + method = getattr(cosmology, kind + "_distance") + + def z_to_distance(z): + """Redshift to distance.""" + return method(z) + + def distance_to_z(d): + """Distance to redshift.""" + return z_at_value(method, d << u.Mpc, **atzkw) + + return u.Equivalency( + [(redshift, u.Mpc, z_to_distance, distance_to_z)], + "redshift_distance", + {"cosmology": cosmology, "distance": kind}, + ) + + +def redshift_hubble( + cosmology: Union["astropy.cosmology.Cosmology", str, None] = None, + **atzkw: _UnpackZAtValueKWArgs, +) -> u.Equivalency: + """Convert quantities between redshift and Hubble parameter and little-h. + + Care should be taken to not misinterpret a relativistic, gravitational, etc + redshift as a cosmological one. + + Parameters + ---------- + cosmology : `~astropy.cosmology.Cosmology`, str, or None, optional + A cosmology realization or built-in cosmology's name (e.g. 'Planck18'). + If None, will use the default cosmology + (controlled by |default_cosmology|). + **atzkw + keyword arguments for :func:`~astropy.cosmology.z_at_value` + + Returns + ------- + `~astropy.units.equivalencies.Equivalency` + Equivalency between redshift and Hubble parameter and little-h unit. + + Examples + -------- + >>> import astropy.units as u + >>> import astropy.cosmology.units as cu + >>> from astropy.cosmology import WMAP9 + + >>> z = 1100 * cu.redshift + >>> equivalency = cu.redshift_hubble(WMAP9) # construct equivalency + + >>> z.to(u.km / u.s / u.Mpc, equivalency) # doctest: +FLOAT_CMP + + + >>> z.to(cu.littleh, equivalency) # doctest: +FLOAT_CMP + + """ + # get cosmology: None -> default and process str / class + cosmology = cosmology if cosmology is not None else default_cosmology.get() + with default_cosmology.set(cosmology): # if already cosmo, passes through + cosmology = default_cosmology.get() + + def z_to_hubble(z): + """Redshift to Hubble parameter.""" + return cosmology.H(z) + + def hubble_to_z(H): + """Hubble parameter to redshift.""" + return z_at_value(cosmology.H, H << (u.km / u.s / u.Mpc), **atzkw) + + def z_to_littleh(z): + """Redshift to :math:`h`-unit Quantity.""" + return z_to_hubble(z).to_value(u.km / u.s / u.Mpc) / 100 * littleh + + def littleh_to_z(h): + """:math:`h`-unit Quantity to redshift.""" + return hubble_to_z(h * 100) + + return u.Equivalency( + [ + (redshift, u.km / u.s / u.Mpc, z_to_hubble, hubble_to_z), + (redshift, littleh, z_to_littleh, littleh_to_z), + ], + "redshift_hubble", + {"cosmology": cosmology}, + ) + + +def redshift_temperature( + cosmology: Union["astropy.cosmology.Cosmology", str, None] = None, + **atzkw: _UnpackZAtValueKWArgs, +) -> u.Equivalency: + """Convert quantities between redshift and CMB temperature. + + Care should be taken to not misinterpret a relativistic, gravitational, etc + redshift as a cosmological one. + + Parameters + ---------- + cosmology : `~astropy.cosmology.Cosmology`, str, or None, optional + A cosmology realization or built-in cosmology's name (e.g. 'Planck18'). + If None, will use the default cosmology + (controlled by |default_cosmology|). + **atzkw + keyword arguments for :func:`~astropy.cosmology.z_at_value` + + Returns + ------- + `~astropy.units.equivalencies.Equivalency` + Equivalency between redshift and temperature. + + Examples + -------- + >>> import astropy.units as u + >>> import astropy.cosmology.units as cu + >>> from astropy.cosmology import WMAP9 + + >>> z = 1100 * cu.redshift + >>> z.to(u.K, cu.redshift_temperature(WMAP9)) + + """ + # get cosmology: None -> default and process str / class + cosmology = cosmology if cosmology is not None else default_cosmology.get() + with default_cosmology.set(cosmology): # if already cosmo, passes through + cosmology = default_cosmology.get() + + def z_to_Tcmb(z): + return cosmology.Tcmb(z) + + def Tcmb_to_z(T): + return z_at_value(cosmology.Tcmb, T << u.K, **atzkw) + + return u.Equivalency( + [(redshift, u.K, z_to_Tcmb, Tcmb_to_z)], + "redshift_temperature", + {"cosmology": cosmology}, + ) + + +def with_redshift( + cosmology: Union["astropy.cosmology.Cosmology", str, None] = None, + *, + distance: Literal["comoving", "lookback", "luminosity"] = "comoving", + hubble: bool = True, + Tcmb: bool = True, + atzkw: _ZAtValueKWArgs | None = None, +) -> u.Equivalency: + """Convert quantities between measures of cosmological distance. + + Note: by default all equivalencies are on and must be explicitly turned off. + Care should be taken to not misinterpret a relativistic, gravitational, etc + redshift as a cosmological one. + + Parameters + ---------- + cosmology : `~astropy.cosmology.Cosmology`, str, or None, optional + A cosmology realization or built-in cosmology's name (e.g. 'Planck18'). + If `None`, will use the default cosmology + (controlled by |default_cosmology|). + + distance : {'comoving', 'lookback', 'luminosity'} or None (optional, keyword-only) + The type of distance equivalency to create or `None`. + Default is 'comoving'. + hubble : bool (optional, keyword-only) + Whether to create a Hubble parameter <-> redshift equivalency, using + ``Cosmology.H``. Default is `True`. + Tcmb : bool (optional, keyword-only) + Whether to create a CMB temperature <-> redshift equivalency, using + ``Cosmology.Tcmb``. Default is `True`. + + atzkw : dict or None (optional, keyword-only) + keyword arguments for :func:`~astropy.cosmology.z_at_value` + + Returns + ------- + `~astropy.units.equivalencies.Equivalency` + With equivalencies between redshift and distance / Hubble / temperature. + + Examples + -------- + >>> import astropy.units as u + >>> import astropy.cosmology.units as cu + >>> from astropy.cosmology import WMAP9 + + >>> equivalency = cu.with_redshift(WMAP9) + >>> z = 1100 * cu.redshift + + Redshift to (comoving) distance: + + >>> z.to(u.Mpc, equivalency) # doctest: +FLOAT_CMP + + + Redshift to the Hubble parameter: + + >>> z.to(u.km / u.s / u.Mpc, equivalency) # doctest: +FLOAT_CMP + + + >>> z.to(cu.littleh, equivalency) # doctest: +FLOAT_CMP + + + Redshift to CMB temperature: + + >>> z.to(u.K, equivalency) + + """ + # get cosmology: None -> default and process str / class + cosmology = cosmology if cosmology is not None else default_cosmology.get() + with default_cosmology.set(cosmology): # if already cosmo, passes through + cosmology = default_cosmology.get() + + atzkw = atzkw if atzkw is not None else {} + equivs: list[u.Equivalency] = [] # will append as built + + # Hubble <-> Redshift + if hubble: + equivs.extend(redshift_hubble(cosmology, **atzkw)) + + # CMB Temperature <-> Redshift + if Tcmb: + equivs.extend(redshift_temperature(cosmology, **atzkw)) + + # Distance <-> Redshift, but need to choose which distance + if distance is not None: + equivs.extend(redshift_distance(cosmology, kind=distance, **atzkw)) + + # ----------- + return u.Equivalency( + equivs, + "with_redshift", + {"cosmology": cosmology, "distance": distance, "hubble": hubble, "Tcmb": Tcmb}, + ) + + +# =================================================================== + + +def with_H0(H0: u.Quantity | None = None) -> u.Equivalency: + """Convert between quantities with little-h and the equivalent physical units. + + Parameters + ---------- + H0 : None or Quantity ['frequency'] + The value of the Hubble constant to assume. If a |Quantity|, will assume the + quantity *is* ``H0``. If `None` (default), use the ``H0`` attribute from + |default_cosmology|. + + References + ---------- + For an illuminating discussion on why you may or may not want to use + little-h at all, see https://arxiv.org/pdf/1308.4150.pdf + """ + if H0 is None: + H0 = default_cosmology.get().H0 + + h100_val_unit = u.Unit(100 / (H0.to_value((u.km / u.s) / u.Mpc)) * littleh) + + return u.Equivalency([(h100_val_unit, None)], "with_H0", kwargs={"H0": H0}) diff --git a/astropy/cosmology/_src/utils.py b/astropy/cosmology/_src/utils.py new file mode 100644 index 000000000000..b40042bf08b4 --- /dev/null +++ b/astropy/cosmology/_src/utils.py @@ -0,0 +1,128 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +__all__: list[str] = [] # nothing is publicly scoped + +import functools +from collections.abc import Callable +from numbers import Number +from typing import Any, Final, ParamSpec, Protocol, TypeAlias, TypeVar + +import numpy as np +from numpy.typing import ArrayLike, NDArray + +from astropy.units import Quantity + +# isort: split +import astropy.cosmology._src.units as cu + +from .signature_deprecations import _depr_kws_wrap + +P = ParamSpec("P") +R = TypeVar("R") + + +def vectorize_redshift_method(func=None, nin=1): + """Vectorize a method of redshift(s). + + Parameters + ---------- + func : callable or None + method to wrap. If `None` returns a :func:`functools.partial` + with ``nin`` loaded. + nin : int + Number of positional redshift arguments. + + Returns + ------- + wrapper : callable + :func:`functools.wraps` of ``func`` where the first ``nin`` + arguments are converted from |Quantity| to :class:`numpy.ndarray`. + """ + # allow for pie-syntax & setting nin + if func is None: + return functools.partial(vectorize_redshift_method, nin=nin) + + @functools.wraps(func) + def wrapper(self, *args, **kwargs): + """Wrapper converting arguments to numpy-compatible inputs. + + :func:`functools.wraps` of ``func`` where the first ``nin`` arguments are + converted from |Quantity| to `numpy.ndarray` or scalar. + """ + # process inputs + # TODO! quantity-aware vectorization can simplify this. + zs = [ + z if not isinstance(z, Quantity) else z.to_value(cu.redshift) + for z in args[:nin] + ] + # scalar inputs + if all(isinstance(z, (Number, np.generic)) for z in zs): + return func(self, *zs, *args[nin:], **kwargs) + # non-scalar. use vectorized func + return wrapper.__vectorized__(self, *zs, *args[nin:], **kwargs) + + wrapper.__vectorized__ = np.vectorize(func) # attach vectorized function + # TODO! use frompyfunc when can solve return type errors + + return wrapper + + +# =================================================================== + +ScalarTypes: TypeAlias = Number | np.generic +SCALAR_TYPES: Final = (float, int, np.generic, Number) # arranged for speed + + +class HasShape(Protocol): + shape: tuple[int, ...] + + +def aszarr( + z: Quantity | NDArray[Any] | ArrayLike | ScalarTypes | HasShape, / +) -> NDArray[Any]: + """Redshift as an Array duck type. + + Allows for any ndarray ducktype by checking for attribute "shape". + """ + # Scalars + if isinstance(z, SCALAR_TYPES): + return np.asarray(z) + + # Quantities. We do this before checking for normal ndarray because Quantity is a + # subclass of ndarray. + elif isinstance(z, Quantity): + return z.to_value(cu.redshift)[...] + + # Arrays + elif isinstance(z, np.ndarray): + return z + + return Quantity(z, cu.redshift, copy=None, subok=True).view(np.ndarray) + + +# =================================================================== + + +def deprecated_keywords( + *kws: str, since: str | tuple[str, ...] +) -> Callable[[Callable[P, R]], Callable[P, R]]: + """Deprecate calling one or more arguments as keywords. + + Parameters + ---------- + *kws: str + Names of the arguments that will become positional-only. + + since : str, float, or tuple of str or float + The release at which the old argument became deprecated. Can be a single + version (e.g., "7.0" or 7.0) or a tuple of versions for multiple arguments. + """ + return functools.partial(_depr_kws, kws=kws, since=since) + + +def _depr_kws( + func: Callable[P, R], /, kws: tuple[str, ...], since: str | tuple[str, ...] +) -> Callable[P, R]: + wrapper = _depr_kws_wrap(func, kws, since) + functools.update_wrapper(wrapper, func) + return wrapper diff --git a/astropy/cosmology/core.py b/astropy/cosmology/core.py deleted file mode 100644 index 8a42faae56c0..000000000000 --- a/astropy/cosmology/core.py +++ /dev/null @@ -1,587 +0,0 @@ -# Licensed under a 3-clause BSD style license - see LICENSE.rst -import sys -import warnings -from math import sqrt, pi - -import numpy as np - -from ..constants.cgs import pc, G, c -from ..config import ConfigurationItem -from ..utils.misc import isiterable - -import parameters - -# Originally authored by Andrew Becker (becker@astro.washington.edu), -# and modified by Neil Crighton (neilcrighton@gmail.com) and Roban -# Kramer (robanhk@gmail.com). - -# Many of these adapted from astro-ph/9905116 - -__all__ = ("FLRWCosmology get_current set_current WMAP5 WMAP7").split() - -# Constants - -# speed of light in km/s -c_kms = c * 1e-5 - -# Mpc in cm -Mpc = 1e6 * pc - -# Mpc in km -Mpc_km = 1e-5 * Mpc - -# Gyr in seconds -Gyr = 1e9 * 365.25 * 24 * 60 * 60 - - -DEFAULT_COSMOLOGY = ConfigurationItem( - 'default_cosmology', 'no_default', - 'The default cosmology to use. Note this is only read on import, ' - 'changing this value at runtime has no effect.') - - -class CosmologyError(Exception): - pass - -class Cosmology(object): - """ Placeholder for when a more general Cosmology class is - implemented. """ - pass - -class FLRWCosmology(Cosmology): - """ A class describing an isotropic and homogeneous - (Friedmann-Lemaitre-Robertson-Walker) cosmology. - - Attributes - ---------- - H0 : float - Hubble parameter at z=0 in km/s/Mpc - Om : float - Omega matter; matter density / critical density at z=0 - Ol : float - Omega lambda; dark energy density / critical density at z=0 - Ok : float - Omega_k, the curvature density at z=0. Defined as 1 - Om - Ol - h : float - Dimensionless Hubble parameter (H0 = 100*h km/s/Mpc). - Often used to quote cosmological values independently of H0. - hubble_time : float - Hubble time in Gyr. - hubble_distance : float - Hubble distance in Mpc. - critical_density0 : float - Critical density in g cm^-3 at z=0. - - Notes - ----- - Note the energy density from radiation, omega_r, is ignored (valid - for redshifts < ~10). - - Examples - -------- - >>> from astro.cosmology import FLRWCosmology - >>> cosmo = FLRWCosmology(H0=70, Om=0.3, Ol=0.7) - - The comoving distance in Mpc at redshift z: - - >>> dc = cosmo.comoving_distance(z) - """ - def __init__(self, H0, Om, Ol, name='FLRWCosmology'): - - # all densities are in units of the critical density - self.Om = float(Om) - self.Ol = float(Ol) - Ok = 1 - self.Om - self.Ol - if abs(Ok) < 1e-5: - Ok = 0 - self.Ok = Ok - self.name = name - - # Hubble parameter at z=0, km/s/Mpc - self.H0 = float(H0) - # H0 in s^-1 - H0_s = self.H0 / Mpc_km - # 100 km/s/Mpc * h = H0 (so h is dimensionless) - self.h = self.H0 / 100. - # Hubble time in Gyr - self.hubble_time = 1. / H0_s / Gyr - # Hubble distance in Mpc - self.hubble_distance = c_kms / self.H0 - - # critical density at z=0 (grams per cubic cm) - self.critical_density0 = 3. * H0_s**2 / (8. * pi * G) - - def __repr__(self): - s = "%s(H0=%.3g, Om=%.3g, Ol=%.3g, Ok=%.3g)" % ( - self.name, self.H0, self.Om, self.Ol, self.Ok) - return s - - def _efunc(self, z): - """ Function used to calculate the hubble parameter as a - function of redshift. Eqn 14 from Hogg.""" - if isiterable(z): - z = np.asarray(z) - zp1 = 1. + z - return np.sqrt(self.Om*zp1**3 + self.Ok*zp1**2 + self.Ol) - - def _inv_efunc(self, z): - """ Integrand of the comoving distance. - """ - zp1 = 1. + z - return 1. / np.sqrt(self.Om*zp1**3 + self.Ok*zp1**2 + self.Ol) - - def _tfunc(self, z): - """ Integrand of the lookback time. - - Eqn 30 from Hogg.""" - zp1 = 1. + z - return 1. / (zp1*np.sqrt(self.Om*zp1**3 + self.Ok*zp1**2 + self.Ol)) - - def _xfunc(self, z): - """ Integrand of the absorption distance. - - See Hogg 1999 section 11. - """ - zp1 = 1. + z - return zp1**2 / np.sqrt(self.Om*zp1**3 + self.Ok*zp1**2 + self.Ol) - - def H(self, z): - """ Hubble parameter (km/s/Mpc) at redshift `z`. - - Parameters - ---------- - z : array_like - Input redshifts. - - Returns - ------- - H : ndarray, or float if input scalar - Hubble parameter in km/s/Mpc at each input redshift. - """ - return self.H0 * self._efunc(z) - - def scale_factor(self, z): - """ Scale factor at redshift `z`. - - The scale factor is defined as `a = 1 / (1 + z)`. - - Parameters - ---------- - z : array_like - Input redshifts. - - Returns - ------- - a : ndarray, or float if input scalar - Scale factor at each input redshift. - """ - - if isiterable(z): - z = np.asarray(z) - - return 1. / (1. + z) - - def lookback_time(self, z): - """ Lookback time in Gyr to redshift `z`. - - The lookback time is the difference between the age of the - Universe now and the age at redshift `z`. - - Parameters - ---------- - z : array_like - Input redshifts. - - Returns - ------- - t : ndarray, or float if input scalar - Lookback time in Gyr to each input redshift. - """ - from scipy.integrate import quad - if not isiterable(z): - return self.hubble_time * quad(self._tfunc, 0, z)[0] - - out = np.array([quad(self._tfunc, 0, redshift)[0] for redshift in z]) - return self.hubble_time * np.array(out) - - def age(self, z): - """ Age of the universe in Gyr at redshift `z`. - - Parameters - ---------- - z : array_like - Input redshifts. - - Returns - ------- - t : ndarray, or float if input scalar - The age of the universe in Gyr at each input redshift. - """ - from scipy.integrate import quad - if not isiterable(z): - return self.hubble_time * quad(self._tfunc, z, np.inf)[0] - - out = [quad(self._tfunc, redshift, np.inf)[0] for redshift in z] - return self.hubble_time * np.array(out) - - def critical_density(self, z): - """ Critical density in grams per cubic cm at redshift `z`. - - Parameters - ---------- - z : array_like - Input redshifts. - - Returns - ------- - rho : ndarray, or float if input scalar - Critical density in g/cm^3 at each input redshift. - """ - return self.critical_density0 * (self._efunc(z))**2 - - def comoving_distance(self, z): - """ Comoving line-of-sight distance in Mpc at a given - redshift. - - The comoving distance along the line-of-sight between two - objects remains constant with time for objects in the Hubble - flow. - - Parameters - ---------- - z : array_like - Input redshifts. - - Returns - ------- - d : ndarray, or float if input scalar - Comoving distance in Mpc to each input redshift. - """ - from scipy.integrate import quad - if not isiterable(z): - return self.hubble_distance * quad(self._inv_efunc, 0, z)[0] - - out = [quad(self._inv_efunc, 0, redshift)[0] for redshift in z] - return self.hubble_distance * np.array(out) - - def comoving_transverse_distance(self, z): - """ Comoving transverse distance in Mpc at a given redshift. - - This value is the transverse comoving distance at redshift `z` - corresponding to an angular separation of 1 radian. This is - the same as the comoving distance if omega_k is zero (as in - the current concordance lambda CDM model). - - Parameters - ---------- - z : array_like - Input redshifts. - - Returns - ------- - d : ndarray, or float if input scalar - Comoving transverse distance in Mpc at each input redshift. - - Notes - ----- - This quantity also called the 'proper motion distance' in some - texts. - """ - Ok = self.Ok - dc = self.comoving_distance(z) - if Ok == 0: - return dc - sqrtOk = sqrt(abs(Ok)) - dh = self.hubble_distance - if Ok > 0: - return dh / sqrtOk * np.sinh(sqrtOk * dc / dh) - else: - return dh / sqrtOk * np.sin(sqrtOk * dc / dh) - - def angular_diameter_distance(self, z): - """ Angular diameter distance in Mpc at a given redshift. - - This gives the proper (sometimes called 'physical') transverse - distance corresponding to an angle of 1 radian for an object - at redshift `z`. - - Weinberg, 1972, pp 421-424; Weedman, 1986, pp 65-67; Peebles, - 1993, pp 325-327. - - Parameters - ---------- - z : array_like - Input redshifts. - - Returns - ------- - d : ndarray, or float if input scalar - Angular diameter distance in Mpc at each input redshift. - """ - if isiterable(z): - z = np.asarray(z) - - return self.comoving_transverse_distance(z) / (1. + z) - - def luminosity_distance(self, z): - """ Luminosity distance in Mpc at redshift `z`. - - This is the distance to use when converting between the - bolometric flux from an object at redshift `z` and its - bolometric luminosity. - - Parameters - ---------- - z : array_like - Input redshifts. - - Returns - ------- - d : ndarray, or float if input scalar - Luminosity distance in Mpc at each input redshift. - - References - ---------- - Weinberg, 1972, pp 420-424; Weedman, 1986, pp 60-62. - """ - if isiterable(z): - z = np.asarray(z) - - return (1. + z) * self.comoving_transverse_distance(z) - - def angular_diameter_distance_z1z2(self, z1, z2): - """ Angular diameter distance between objects at 2 redshifts. - Useful for gravitational lensing. - - Parameters - ---------- - z1, z2 : array_like, shape (N,) - Input redshifts. z2 must be large than z1. - - Returns - ------- - d : ndarray, shape (N,) or float if input scalar - The angular diameter distance between each input redshift - pair. - - Raises - ------ - CosmologyError - If omega_k is < 0. - - Notes - ----- - This method only works for flat or open curvature - (omega_k >= 0). - """ - # does not work for negative curvature - Ok = self.Ok - if Ok < 0: - raise CosmologyError('Ok must be > 0 to use this method.') - - outscalar = False - if not isiterable(z1) and not isiterable(z2): - outscalar = True - - z1 = np.atleast_1d(z1) - z2 = np.atleast_1d(z2) - - if z1.size != z2.size: - raise ValueError('z1 and z2 must be the same size.') - - if (z1 > z2).any(): - raise ValueError('z2 must greater than z1') - - # z1 < z2 - if (z2 < z1).any(): - z1, z2 = z2, z1 - - dm1 = self.comoving_transverse_distance(z1) - dm2 = self.comoving_transverse_distance(z2) - dh_2 = self.hubble_distance**2 - - out = 1. / (1. + z2) * (dm2*np.sqrt(1. + Ok*dm1**2 / dh_2) - - dm1*np.sqrt(1. + Ok*dm2**2 / dh_2)) - - if outscalar: - return out[0] - - return out - - def absorption_distance(self, z): - """ Absorption distance at redshift `z`. - - This is used to calculate the number of objects with some - cross section of absorption and number density intersecting a - sightline per unit redshift path. - - Parameters - ---------- - z : array_like - Input redshifts. - - Returns - ------- - d : ndarray, or float if input scalar - Absorption distance (dimensionless) at each input redshift. - - References - ---------- - Hogg 1999 Section 11. (astro-ph/9905116) - Bahcall, John N. and Peebles, P.J.E. 1969, ApJ, 156L, 7B - """ - from scipy.integrate import quad - if not isiterable(z): - return quad(self._xfunc, 0, z)[0] - - out = [quad(self._xfunc, 0, redshift)[0] for redshift in z] - return np.array(out) - - def distmod(self, z): - """ Distance modulus at redshift `z`. - - The distance modulus is defined as the (apparent magnitude - - absolute magnitude) for an object at redshift `z`. - - Parameters - ---------- - z : array_like - Input redshifts. - - Returns - ------- - distmod : ndarray, or float if input scalar - Distance modulus at each input redshift. - """ - # Remember that the luminosity distance is in Mpc - return 5. * np.log10(self.luminosity_distance(z) * 1.e5) - - def comoving_volume(self, z): - """ Comoving volume in cubic Mpc at redshift `z`. - - This is the volume of the universe encompassed by redshifts - less than `z`. For the case of omega_k = 0 it is a sphere of - radius `comoving_distance(z)` but it is less intuitive if - omega_k is not 0. - - Parameters - ---------- - z : array_like - Input redshifts. - - Returns - ------- - V : ndarray, or float if input scalar - Comoving volume in Mpc^3 at each input redshift. - """ - Ok = self.Ok - if Ok == 0: - return 4. / 3. * pi * self.comoving_distance(z)**3 - - dh = self.hubble_distance - dm = self.comoving_transverse_distance(z) - term1 = 4. * pi * dh**3 / (2. * Ok) - term2 = dm / dh * sqrt(1 + Ok * (dm / dh)**2) - term3 = sqrt(abs(Ok)) * dm / dh - - if Ok > 0: - return term1 * (term2 - 1. / sqrt(abs(Ok)) * np.arcsinh(term3)) - else: - return term1 * (term2 - 1. / sqrt(abs(Ok)) * np.arcsin(term3)) - - -# Pre-defined cosmologies. This loops over the parameter sets in the -# parameters module and creates a FLRWCosmology instance with the same -# name as the parameter set in the current module's namespace. - -for key in parameters.available: - par = getattr(parameters, key) - cosmo = FLRWCosmology(par['H0'], par['Om'], par['Ol'], name=key) - cosmo.__doc__ = "%s cosmology\n\n(from %s)" % (key, par['reference']) - setattr(sys.modules[__name__], key, cosmo) - -# don't leave these variables floating around in the namespace -del key, par, cosmo - -######################################################################### -# The variable below contains the current cosmology used by the -# convenience functions below and by other astropy functions if no -# cosmology is explicitly given. It can be set with set_current() and -# should be accessed using get_current(). -######################################################################### - - -def get_cosmology_from_string(arg): - """ Return a cosmology instance from a string. - """ - if arg == 'no_default': - cosmo = None - else: - try: - cosmo = getattr(sys.modules[__name__], arg) - except AttributeError: - s = "Unknown cosmology '%s'. Valid cosmologies:\n%s" % ( - arg, parameters.available) - raise ValueError(s) - return cosmo - -_current = get_cosmology_from_string(DEFAULT_COSMOLOGY()) - - -def get_current(): - """ Get the current cosmology. - - If no current has been set, the WMAP7 comology is returned and a - warning is given. - - Returns - ------- - cosmo : `Cosmology` instance - - See Also - -------- - set_current : sets the current cosmology - """ - if _current is None: - warnings.warn('No default cosmology has been specified, ' - 'using 7-year WMAP.') - return WMAP7 - - return _current - - -def set_current(cosmo): - """ Set the current cosmology. - - Call this with an empty string ('') to get a list of the strings - that map to available pre-defined cosmologies. - - .. warning:: - `set_current` is the only way to change the current cosmology at - runtime! The current cosmology can also be read from an option - in the astropy configuration file when astropy.cosmology is first - imported. However, any subsequent changes to the cosmology - configuration option using `ConfigurationItem.set - ` at run-time - will not update the current cosmology. - - Parameters - ---------- - cosmo : str or `Cosmology` instance - The cosmology to use. - - - - See Also - -------- - get_current : returns the currently-set cosmology - """ - global _current - if isinstance(cosmo, basestring): - _current = get_cosmology_from_string(cosmo) - elif isinstance(cosmo, Cosmology): - _current = cosmo - else: - raise ValueError( - "Argument must be a string or cosmology instance. Valid strings:" - "\n%s" % parameters.available) diff --git a/astropy/cosmology/data/Planck13.ecsv b/astropy/cosmology/data/Planck13.ecsv new file mode 100644 index 000000000000..8ed0494f631a --- /dev/null +++ b/astropy/cosmology/data/Planck13.ecsv @@ -0,0 +1,39 @@ +# %ECSV 1.0 +# --- +# datatype: +# - {name: name, datatype: string} +# - {name: H0, unit: km / (Mpc s), datatype: float64, format: '', description: Hubble constant as an `~astropy.units.Quantity` at z=0.} +# - {name: Om0, datatype: float64, format: '', description: Omega matter; matter density/critical density at z=0.} +# - {name: Tcmb0, unit: K, datatype: float64, format: '', description: Temperature of the CMB as `~astropy.units.Quantity` at z=0.} +# - {name: Neff, datatype: float64, format: '', description: Number of effective neutrino species.} +# - {name: m_nu, unit: eV, datatype: string, format: '', description: Mass of neutrino species., subtype: 'float64[3]'} +# - {name: Ob0, datatype: float64, format: '', description: Omega baryon; baryonic matter density/critical density at z=0.} +# meta: !!omap +# - {Oc0: 0.25886} +# - {n: 0.9611} +# - {sigma8: 0.8288} +# - {tau: 0.0952} +# - z_reion: !astropy.units.Quantity +# unit: !astropy.units.Unit {unit: redshift} +# value: 11.52 +# - t0: !astropy.units.Quantity +# unit: !astropy.units.Unit {unit: Gyr} +# value: 13.7965 +# - {reference: 'Planck Collaboration 2014, A&A, 571, A16 (Paper XVI), Table 5 (Planck + WP + highL + BAO)'} +# - {cosmology: FlatLambdaCDM} +# - __serialized_columns__: +# H0: +# __class__: astropy.units.quantity.Quantity +# unit: !astropy.units.Unit {unit: km / (Mpc s)} +# value: !astropy.table.SerializedColumn {name: H0} +# Tcmb0: +# __class__: astropy.units.quantity.Quantity +# unit: !astropy.units.Unit {unit: K} +# value: !astropy.table.SerializedColumn {name: Tcmb0} +# m_nu: +# __class__: astropy.units.quantity.Quantity +# unit: !astropy.units.Unit {unit: eV} +# value: !astropy.table.SerializedColumn {name: m_nu} +# schema: astropy-2.0 +name H0 Om0 Tcmb0 Neff m_nu Ob0 +Planck13 67.77 0.30712 2.7255 3.046 [0.0,0.0,0.06] 0.048252 diff --git a/astropy/cosmology/data/Planck15.ecsv b/astropy/cosmology/data/Planck15.ecsv new file mode 100644 index 000000000000..cdb20da91423 --- /dev/null +++ b/astropy/cosmology/data/Planck15.ecsv @@ -0,0 +1,39 @@ +# %ECSV 1.0 +# --- +# datatype: +# - {name: name, datatype: string} +# - {name: H0, unit: km / (Mpc s), datatype: float64, format: '', description: Hubble constant as an `~astropy.units.Quantity` at z=0.} +# - {name: Om0, datatype: float64, format: '', description: Omega matter; matter density/critical density at z=0.} +# - {name: Tcmb0, unit: K, datatype: float64, format: '', description: Temperature of the CMB as `~astropy.units.Quantity` at z=0.} +# - {name: Neff, datatype: float64, format: '', description: Number of effective neutrino species.} +# - {name: m_nu, unit: eV, datatype: string, format: '', description: Mass of neutrino species., subtype: 'float64[3]'} +# - {name: Ob0, datatype: float64, format: '', description: Omega baryon; baryonic matter density/critical density at z=0.} +# meta: !!omap +# - {Oc0: 0.2589} +# - {n: 0.9667} +# - {sigma8: 0.8159} +# - {tau: 0.066} +# - z_reion: !astropy.units.Quantity +# unit: !astropy.units.Unit {unit: redshift} +# value: 8.8 +# - t0: !astropy.units.Quantity +# unit: !astropy.units.Unit {unit: Gyr} +# value: 13.799 +# - {reference: 'Planck Collaboration 2016, A&A, 594, A13 (Paper XIII), Table 4 (TT, TE, EE + lowP + lensing + ext)'} +# - {cosmology: FlatLambdaCDM} +# - __serialized_columns__: +# H0: +# __class__: astropy.units.quantity.Quantity +# unit: !astropy.units.Unit {unit: km / (Mpc s)} +# value: !astropy.table.SerializedColumn {name: H0} +# Tcmb0: +# __class__: astropy.units.quantity.Quantity +# unit: !astropy.units.Unit {unit: K} +# value: !astropy.table.SerializedColumn {name: Tcmb0} +# m_nu: +# __class__: astropy.units.quantity.Quantity +# unit: !astropy.units.Unit {unit: eV} +# value: !astropy.table.SerializedColumn {name: m_nu} +# schema: astropy-2.0 +name H0 Om0 Tcmb0 Neff m_nu Ob0 +Planck15 67.74 0.3075 2.7255 3.046 [0.0,0.0,0.06] 0.0486 diff --git a/astropy/cosmology/data/Planck18.ecsv b/astropy/cosmology/data/Planck18.ecsv new file mode 100644 index 000000000000..c278799578b4 --- /dev/null +++ b/astropy/cosmology/data/Planck18.ecsv @@ -0,0 +1,39 @@ +# %ECSV 1.0 +# --- +# datatype: +# - {name: name, datatype: string} +# - {name: H0, unit: km / (Mpc s), datatype: float64, format: '', description: Hubble constant as an `~astropy.units.Quantity` at z=0.} +# - {name: Om0, datatype: float64, format: '', description: Omega matter; matter density/critical density at z=0.} +# - {name: Tcmb0, unit: K, datatype: float64, format: '', description: Temperature of the CMB as `~astropy.units.Quantity` at z=0.} +# - {name: Neff, datatype: float64, format: '', description: Number of effective neutrino species.} +# - {name: m_nu, unit: eV, datatype: string, format: '', description: Mass of neutrino species., subtype: 'float64[3]'} +# - {name: Ob0, datatype: float64, format: '', description: Omega baryon; baryonic matter density/critical density at z=0.} +# meta: !!omap +# - {Oc0: 0.2607} +# - {n: 0.9665} +# - {sigma8: 0.8102} +# - {tau: 0.0561} +# - z_reion: !astropy.units.Quantity +# unit: !astropy.units.Unit {unit: redshift} +# value: 7.82 +# - t0: !astropy.units.Quantity +# unit: !astropy.units.Unit {unit: Gyr} +# value: 13.787 +# - {reference: 'Planck Collaboration 2018, 2020, A&A, 641, A6 (Paper VI), Table 2 (TT, TE, EE + lowE + lensing + BAO)'} +# - {cosmology: FlatLambdaCDM} +# - __serialized_columns__: +# H0: +# __class__: astropy.units.quantity.Quantity +# unit: !astropy.units.Unit {unit: km / (Mpc s)} +# value: !astropy.table.SerializedColumn {name: H0} +# Tcmb0: +# __class__: astropy.units.quantity.Quantity +# unit: !astropy.units.Unit {unit: K} +# value: !astropy.table.SerializedColumn {name: Tcmb0} +# m_nu: +# __class__: astropy.units.quantity.Quantity +# unit: !astropy.units.Unit {unit: eV} +# value: !astropy.table.SerializedColumn {name: m_nu} +# schema: astropy-2.0 +name H0 Om0 Tcmb0 Neff m_nu Ob0 +Planck18 67.66 0.30966 2.7255 3.046 [0.0,0.0,0.06] 0.04897 diff --git a/astropy/cosmology/data/WMAP1.ecsv b/astropy/cosmology/data/WMAP1.ecsv new file mode 100644 index 000000000000..9e5fbb770519 --- /dev/null +++ b/astropy/cosmology/data/WMAP1.ecsv @@ -0,0 +1,40 @@ +# %ECSV 1.0 +# --- +# datatype: +# - {name: name, datatype: string} +# - {name: H0, unit: km / (Mpc s), datatype: float64, format: '', description: Hubble constant as an `~astropy.units.Quantity` at z=0.} +# - {name: Om0, datatype: float64, format: '', description: Omega matter; matter density/critical density at z=0.} +# - {name: Tcmb0, unit: K, datatype: float64, format: '', description: Temperature of the CMB as `~astropy.units.Quantity` at z=0.} +# - {name: Neff, datatype: float64, format: '', description: Number of effective neutrino species.} +# - {name: m_nu, unit: eV, datatype: string, format: '', description: Mass of neutrino species., subtype: 'float64[3]'} +# - {name: Ob0, datatype: float64, format: '', description: Omega baryon; baryonic matter density/critical density at z=0.} +# meta: !!omap +# - {Oc0: 0.213} +# - {n: 0.96} +# - {sigma8: 0.75} +# - {tau: 0.117} +# - z_reion: !astropy.units.Quantity +# unit: !astropy.units.Unit {unit: redshift} +# value: 17.0 +# - t0: !astropy.units.Quantity +# unit: !astropy.units.Unit {unit: Gyr} +# value: 13.4 +# - {reference: 'Spergel et al. 2003, ApJS, 148, 175, doi: 10.1086/377226. Table 7 (WMAP + CBI + ACBAR + 2dFGRS + Lya).\nPending WMAP +# team approval and subject to change.'} +# - {cosmology: FlatLambdaCDM} +# - __serialized_columns__: +# H0: +# __class__: astropy.units.quantity.Quantity +# unit: !astropy.units.Unit {unit: km / (Mpc s)} +# value: !astropy.table.SerializedColumn {name: H0} +# Tcmb0: +# __class__: astropy.units.quantity.Quantity +# unit: !astropy.units.Unit {unit: K} +# value: !astropy.table.SerializedColumn {name: Tcmb0} +# m_nu: +# __class__: astropy.units.quantity.Quantity +# unit: !astropy.units.Unit {unit: eV} +# value: !astropy.table.SerializedColumn {name: m_nu} +# schema: astropy-2.0 +name H0 Om0 Tcmb0 Neff m_nu Ob0 +WMAP1 72.0 0.257 2.725 3.04 [0.0,0.0,0.0] 0.0436 diff --git a/astropy/cosmology/data/WMAP3.ecsv b/astropy/cosmology/data/WMAP3.ecsv new file mode 100644 index 000000000000..0bed115874ce --- /dev/null +++ b/astropy/cosmology/data/WMAP3.ecsv @@ -0,0 +1,40 @@ +# %ECSV 1.0 +# --- +# datatype: +# - {name: name, datatype: string} +# - {name: H0, unit: km / (Mpc s), datatype: float64, format: '', description: Hubble constant as an `~astropy.units.Quantity` at z=0.} +# - {name: Om0, datatype: float64, format: '', description: Omega matter; matter density/critical density at z=0.} +# - {name: Tcmb0, unit: K, datatype: float64, format: '', description: Temperature of the CMB as `~astropy.units.Quantity` at z=0.} +# - {name: Neff, datatype: float64, format: '', description: Number of effective neutrino species.} +# - {name: m_nu, unit: eV, datatype: string, format: '', description: Mass of neutrino species., subtype: 'float64[3]'} +# - {name: Ob0, datatype: float64, format: '', description: Omega baryon; baryonic matter density/critical density at z=0.} +# meta: !!omap +# - {Oc0: 0.23} +# - {n: 0.946} +# - {sigma8: 0.784} +# - {tau: 0.079} +# - z_reion: !astropy.units.Quantity +# unit: !astropy.units.Unit {unit: redshift} +# value: 10.3 +# - t0: !astropy.units.Quantity +# unit: !astropy.units.Unit {unit: Gyr} +# value: 13.78 +# - {reference: 'Spergel et al. 2007, ApJS, 170, 377, doi: 10.1086/513700. Table 6 (WMAP + SNGold) obtained from: https://lambda.gsfc.nasa.gov/product/map/dr2/params/lcdm_wmap_sngold.cfm \nPending +# WMAP team approval and subject to change.'} +# - {cosmology: FlatLambdaCDM} +# - __serialized_columns__: +# H0: +# __class__: astropy.units.quantity.Quantity +# unit: !astropy.units.Unit {unit: km / (Mpc s)} +# value: !astropy.table.SerializedColumn {name: H0} +# Tcmb0: +# __class__: astropy.units.quantity.Quantity +# unit: !astropy.units.Unit {unit: K} +# value: !astropy.table.SerializedColumn {name: Tcmb0} +# m_nu: +# __class__: astropy.units.quantity.Quantity +# unit: !astropy.units.Unit {unit: eV} +# value: !astropy.table.SerializedColumn {name: m_nu} +# schema: astropy-2.0 +name H0 Om0 Tcmb0 Neff m_nu Ob0 +WMAP3 70.1 0.276 2.725 3.04 [0.0,0.0,0.0] 0.0454 diff --git a/astropy/cosmology/data/WMAP5.ecsv b/astropy/cosmology/data/WMAP5.ecsv new file mode 100644 index 000000000000..1a6b7f1e707d --- /dev/null +++ b/astropy/cosmology/data/WMAP5.ecsv @@ -0,0 +1,39 @@ +# %ECSV 1.0 +# --- +# datatype: +# - {name: name, datatype: string} +# - {name: H0, unit: km / (Mpc s), datatype: float64, format: '', description: Hubble constant as an `~astropy.units.Quantity` at z=0.} +# - {name: Om0, datatype: float64, format: '', description: Omega matter; matter density/critical density at z=0.} +# - {name: Tcmb0, unit: K, datatype: float64, format: '', description: Temperature of the CMB as `~astropy.units.Quantity` at z=0.} +# - {name: Neff, datatype: float64, format: '', description: Number of effective neutrino species.} +# - {name: m_nu, unit: eV, datatype: string, format: '', description: Mass of neutrino species., subtype: 'float64[3]'} +# - {name: Ob0, datatype: float64, format: '', description: Omega baryon; baryonic matter density/critical density at z=0.} +# meta: !!omap +# - {Oc0: 0.231} +# - {n: 0.962} +# - {sigma8: 0.817} +# - {tau: 0.088} +# - z_reion: !astropy.units.Quantity +# unit: !astropy.units.Unit {unit: redshift} +# value: 11.3 +# - t0: !astropy.units.Quantity +# unit: !astropy.units.Unit {unit: Gyr} +# value: 13.72 +# - {reference: 'Komatsu et al. 2009, ApJS, 180, 330, doi: 10.1088/0067-0049/180/2/330. Table 1 (WMAP + BAO + SN ML).'} +# - {cosmology: FlatLambdaCDM} +# - __serialized_columns__: +# H0: +# __class__: astropy.units.quantity.Quantity +# unit: !astropy.units.Unit {unit: km / (Mpc s)} +# value: !astropy.table.SerializedColumn {name: H0} +# Tcmb0: +# __class__: astropy.units.quantity.Quantity +# unit: !astropy.units.Unit {unit: K} +# value: !astropy.table.SerializedColumn {name: Tcmb0} +# m_nu: +# __class__: astropy.units.quantity.Quantity +# unit: !astropy.units.Unit {unit: eV} +# value: !astropy.table.SerializedColumn {name: m_nu} +# schema: astropy-2.0 +name H0 Om0 Tcmb0 Neff m_nu Ob0 +WMAP5 70.2 0.277 2.725 3.04 [0.0,0.0,0.0] 0.0459 diff --git a/astropy/cosmology/data/WMAP7.ecsv b/astropy/cosmology/data/WMAP7.ecsv new file mode 100644 index 000000000000..e5a20f8f0e5e --- /dev/null +++ b/astropy/cosmology/data/WMAP7.ecsv @@ -0,0 +1,39 @@ +# %ECSV 1.0 +# --- +# datatype: +# - {name: name, datatype: string} +# - {name: H0, unit: km / (Mpc s), datatype: float64, format: '', description: Hubble constant as an `~astropy.units.Quantity` at z=0.} +# - {name: Om0, datatype: float64, format: '', description: Omega matter; matter density/critical density at z=0.} +# - {name: Tcmb0, unit: K, datatype: float64, format: '', description: Temperature of the CMB as `~astropy.units.Quantity` at z=0.} +# - {name: Neff, datatype: float64, format: '', description: Number of effective neutrino species.} +# - {name: m_nu, unit: eV, datatype: string, format: '', description: Mass of neutrino species., subtype: 'float64[3]'} +# - {name: Ob0, datatype: float64, format: '', description: Omega baryon; baryonic matter density/critical density at z=0.} +# meta: !!omap +# - {Oc0: 0.226} +# - {n: 0.967} +# - {sigma8: 0.81} +# - {tau: 0.085} +# - z_reion: !astropy.units.Quantity +# unit: !astropy.units.Unit {unit: redshift} +# value: 10.3 +# - t0: !astropy.units.Quantity +# unit: !astropy.units.Unit {unit: Gyr} +# value: 13.76 +# - {reference: 'Komatsu et al. 2011, ApJS, 192, 18, doi: 10.1088/0067-0049/192/2/18. Table 1 (WMAP + BAO + H0 ML).'} +# - {cosmology: FlatLambdaCDM} +# - __serialized_columns__: +# H0: +# __class__: astropy.units.quantity.Quantity +# unit: !astropy.units.Unit {unit: km / (Mpc s)} +# value: !astropy.table.SerializedColumn {name: H0} +# Tcmb0: +# __class__: astropy.units.quantity.Quantity +# unit: !astropy.units.Unit {unit: K} +# value: !astropy.table.SerializedColumn {name: Tcmb0} +# m_nu: +# __class__: astropy.units.quantity.Quantity +# unit: !astropy.units.Unit {unit: eV} +# value: !astropy.table.SerializedColumn {name: m_nu} +# schema: astropy-2.0 +name H0 Om0 Tcmb0 Neff m_nu Ob0 +WMAP7 70.4 0.272 2.725 3.04 [0.0,0.0,0.0] 0.0455 diff --git a/astropy/cosmology/data/WMAP9.ecsv b/astropy/cosmology/data/WMAP9.ecsv new file mode 100644 index 000000000000..1a13d6a67423 --- /dev/null +++ b/astropy/cosmology/data/WMAP9.ecsv @@ -0,0 +1,39 @@ +# %ECSV 1.0 +# --- +# datatype: +# - {name: name, datatype: string} +# - {name: H0, unit: km / (Mpc s), datatype: float64, format: '', description: Hubble constant as an `~astropy.units.Quantity` at z=0.} +# - {name: Om0, datatype: float64, format: '', description: Omega matter; matter density/critical density at z=0.} +# - {name: Tcmb0, unit: K, datatype: float64, format: '', description: Temperature of the CMB as `~astropy.units.Quantity` at z=0.} +# - {name: Neff, datatype: float64, format: '', description: Number of effective neutrino species.} +# - {name: m_nu, unit: eV, datatype: string, format: '', description: Mass of neutrino species., subtype: 'float64[3]'} +# - {name: Ob0, datatype: float64, format: '', description: Omega baryon; baryonic matter density/critical density at z=0.} +# meta: !!omap +# - {Oc0: 0.2402} +# - {n: 0.9608} +# - {sigma8: 0.82} +# - {tau: 0.081} +# - z_reion: !astropy.units.Quantity +# unit: !astropy.units.Unit {unit: redshift} +# value: 10.1 +# - t0: !astropy.units.Quantity +# unit: !astropy.units.Unit {unit: Gyr} +# value: 13.772 +# - {reference: 'Hinshaw et al. 2013, ApJS, 208, 19, doi: 10.1088/0067-0049/208/2/19. Table 4 (WMAP9 + eCMB + BAO + H0, last column)'} +# - {cosmology: FlatLambdaCDM} +# - __serialized_columns__: +# H0: +# __class__: astropy.units.quantity.Quantity +# unit: !astropy.units.Unit {unit: km / (Mpc s)} +# value: !astropy.table.SerializedColumn {name: H0} +# Tcmb0: +# __class__: astropy.units.quantity.Quantity +# unit: !astropy.units.Unit {unit: K} +# value: !astropy.table.SerializedColumn {name: Tcmb0} +# m_nu: +# __class__: astropy.units.quantity.Quantity +# unit: !astropy.units.Unit {unit: eV} +# value: !astropy.table.SerializedColumn {name: m_nu} +# schema: astropy-2.0 +name H0 Om0 Tcmb0 Neff m_nu Ob0 +WMAP9 69.32 0.2865 2.725 3.04 [0.0,0.0,0.0] 0.04628 diff --git a/astropy/cosmology/funcs.py b/astropy/cosmology/funcs.py deleted file mode 100644 index 4292b81e0625..000000000000 --- a/astropy/cosmology/funcs.py +++ /dev/null @@ -1,254 +0,0 @@ -# Licensed under a 3-clause BSD style license - see LICENSE.rst -""" -Convenience functions for `astropy.cosmology`. -""" -from .core import get_current as _get_current -from math import pi as _pi - -_arcsec_in_radians = 1 / 3600. * _pi / 180 -_arcmin_in_radians = 1 / 60. * _pi / 180 - - -def kpc_comoving_per_arcmin(z, cosmo=None): - """ Separation in transverse comoving kpc corresponding to an - arcminute at redshift `z`. - - Parameters - ---------- - z : array_like - Input redshifts. - - Returns - ------- - d : ndarray, or float if input scalar - The distance in comoving kpc corresponding to an arcmin at each - input redshift. - """ - - - if cosmo is None: - cosmo = _get_current() - return cosmo.comoving_transverse_distance(z) * 1.e3 * _arcmin_in_radians - - -def kpc_proper_per_arcmin(z, cosmo=None): - """ Separation in transverse proper kpc corresponding to an - arcminute at redshift `z`. - - Parameters - ---------- - z : array_like - Input redshifts. - - Returns - ------- - d : ndarray, or float if input scalar - The distance in proper kpc corresponding to an arcmin at each - input redshift. - """ - if cosmo is None: - cosmo = _get_current() - return cosmo.angular_diameter_distance(z) * 1.e3 * _arcmin_in_radians - - -def arcsec_per_kpc_comoving(z, cosmo=None): - """ Angular separation in arcsec corresponding to a comoving kpc - at redshift `z`. - - Parameters - ---------- - z : array_like - Input redshifts. - - Returns - ------- - theta : ndarray, or float if input scalar - The angular separation in arcsec corresponding to a comoving kpc - at each input redshift. - """ - if cosmo is None: - cosmo = _get_current() - return 1 / (cosmo.comoving_transverse_distance(z) * - 1.e3 * _arcsec_in_radians) - - -def arcsec_per_kpc_proper(z, cosmo=None): - """ Angular separation in arcsec corresponding to a proper kpc at - redshift `z`. - - Parameters - ---------- - z : array_like - Input redshifts. - - Returns - ------- - theta : ndarray, or float if input scalar - The angular separation in arcsec corresponding to a proper kpc - at each input redshift. - """ - if cosmo is None: - cosmo = _get_current() - return 1 / (cosmo.angular_diameter_distance(z) * 1.e3 * _arcsec_in_radians) - - -def distmod(z, cosmo=None): - """ Distance modulus at redshift `z`. - - The distance modulus is defined as the (apparent magnitude - - absolute magnitude) for an object at redshift `z`. - - Parameters - ---------- - z : array_like - Input redshifts. - - Returns - ------- - distmod : ndarray, or float if input scalar - Distance modulus at each input redshift. - """ - if cosmo is None: - cosmo = _get_current() - return cosmo.distmod(z) - - -def H(z, cosmo=None): - """ Hubble parameter (km/s/Mpc) at redshift `z`. - - Parameters - ---------- - z : array_like - Input redshifts. - - Returns - ------- - H : ndarray, or float if input scalar - Hubble parameter at each input redshift. - """ - if cosmo is None: - cosmo = _get_current() - return cosmo.H(z) - - -def scale_factor(z, cosmo=None): - """ Scale factor at redshift `z`. - - The scale factor is defined as `a = 1 / (1 + z)`. - - Parameters - ---------- - z : array_like - Input redshifts. - - Returns - ------- - distmod : ndarray, or float if input scalar - Scale factor at each input redshift. - """ - if cosmo is None: - cosmo = _get_current() - return cosmo.scale_factor(z) - - -def critical_density(z, cosmo=None): - """ Critical density in grams per cubic cm at redshift `z`. - - Parameters - ---------- - z : array_like - Input redshifts. - - Returns - ------- - critdens : ndarray, or float if input scalar - Critical density at each input redshift. - """ - if cosmo is None: - cosmo = _get_current() - return cosmo.critical_density(z) - - -def lookback_time(z, cosmo=None): - """ Lookback time in Gyr to redshift `z`. - - The lookback time is the difference between the age of the - Universe now and the age at redshift `z`. - - Parameters - ---------- - z : array_like - Input redshifts. - - Returns - ------- - t : ndarray, or float if input scalar - Lookback time at each input redshift. - """ - if cosmo is None: - cosmo = _get_current() - return cosmo.lookback_time(z) - - -def comoving_distance(z, cosmo=None): - """ Comoving distance in Mpc at redshift `z`. - - The comoving distance along the line-of-sight between two objects - remains constant with time for objects in the Hubble flow. - - Parameters - ---------- - z : array_like - Input redshifts. - - Returns - ------- - codist : ndarray, or float if input scalar - Comoving distance at each input redshift. - """ - if cosmo is None: - cosmo = _get_current() - return cosmo.comoving_distance(z) - - -def angular_diameter_distance(z, cosmo=None): - """ Angular diameter distance in Mpc at a given redshift. - - This gives the proper (sometimes called 'physical') transverse - distance corresponding to an angle of 1 radian for an object at - redshift `z`. - - Parameters - ---------- - z : array_like - Input redshifts. - - Returns - ------- - angdist : ndarray, or float if input scalar - Angular diameter distance at each input redshift. - """ - if cosmo is None: - cosmo = _get_current() - return cosmo.angular_diameter_distance(z) - - -def luminosity_distance(z, cosmo=None): - """ Luminosity distance in Mpc at redshift `z`. - - This is the distance to use when converting between the bolometric - flux from an object at redshift `z` and its bolometric luminosity. - - Parameters - ---------- - z : array_like - Input redshifts. - - Returns - ------- - lumdist : ndarray, or float if input scalar - Angular diameter distance at each input redshift. - """ - if cosmo is None: - cosmo = _get_current() - return cosmo.luminosity_distance(z) diff --git a/astropy/cosmology/io.py b/astropy/cosmology/io.py new file mode 100644 index 000000000000..1a1153f26161 --- /dev/null +++ b/astropy/cosmology/io.py @@ -0,0 +1,33 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +"""I/O subpackage for cosmology. + +This subpackage contains classes and functions for reading, writing +and converting cosmology objects to and from various formats. + +User access to the I/O functionality is provided through the methods +on the `~astropy.cosmology.Cosmology` class (and its subclasses) and its instances: + +- |Cosmology.read| for reading from a file, +- |Cosmology.write| for writing to a file, +- |Cosmology.from_format| to construct a Cosmology from an object +- |Cosmology.to_format| to convert a Cosmology to an object +""" + +__all__ = ( + "CosmologyFromFormat", + "CosmologyRead", + "CosmologyToFormat", + "CosmologyWrite", + "convert_registry", + "readwrite_registry", +) + +# Importing the I/O subpackage registers the I/O methods. +from ._src.io.connect import ( + CosmologyFromFormat, + CosmologyRead, + CosmologyToFormat, + CosmologyWrite, + convert_registry, + readwrite_registry, +) diff --git a/astropy/cosmology/parameters.py b/astropy/cosmology/parameters.py index b7199e3c84be..9170a5f2e3d3 100644 --- a/astropy/cosmology/parameters.py +++ b/astropy/cosmology/parameters.py @@ -1,67 +1,42 @@ # Licensed under a 3-clause BSD style license - see LICENSE.rst -""" This module contains dictionaries with sets of parameters for a -given cosmology. +"""This module contains dictionaries with sets of parameters for a given cosmology. -Each cosmology has the following parameters defined: +The list of cosmologies available are given by the tuple `available`. +""" - ========== ===================================== - Oc Omega cold dark matter - Ob Omega baryon - Om Omega matter - Ol Omega lambda - H0 Hubble parameter at z=0 in km/s/Mpc - n Density perturbation spectral index - sigma8 Density perturbation amplitude - tau Ionisation optical depth - z_reion Redshift of hydrogen reionisation - t0 Age of the universe in Gyr - reference Reference for the parameters - ========== ===================================== +import sys +from types import MappingProxyType -The list of cosmologies available are given by the tuple -`available`. Current cosmologies available: +from .realizations import available -WMAP 7 year parameters from Komatsu et al. 2011, ApJS, 192, 18. Table -1 (WMAP + BAO + H0 ML). +__all__ = ( # noqa: F822, RUF022 + "available", + # ---- + "WMAP1", + "WMAP3", + "WMAP5", + "WMAP7", + "WMAP9", + "Planck13", + "Planck15", + "Planck18", +) -WMAP 5 year parameters from Komatsu et al. 2009, ApJS, 180, 330. Table -1 (WMAP + BAO + SN ML). -Both these cosmologies are flat (omega matter + omega lambda = 1). -""" +def __getattr__(name): + """Get parameters of cosmology representations with lazy import from ``PEP 562``.""" + from astropy.cosmology import realizations -# Komatsu et al. 2011, WMAP + BAO + H0 ML (table 1). + cosmo = getattr(realizations, name) + m = cosmo.to_format("mapping", cosmology_as_str=True, move_from_meta=True) + proxy = MappingProxyType(m) -WMAP7 = dict( - Oc = 0.226, - Ob = 0.0455, - Om = 0.272, - Ol = 0.728, - H0 = 70.4, - n = 0.967, - sigma8 = 0.810, - tau = 0.085, - z_reion = 10.3, - t0 = 13.76, - reference = ("Komatsu et al. 2011, ApJS, 192, 18. " - "Table 1 (WMAP + BAO + H0 ML)") - ) + # Cache in this module so `__getattr__` is only called once per `name`. + setattr(sys.modules[__name__], name, proxy) -# Komatsu et al. 2009 WMAP + BAO + SN ML (table 1). + return proxy -WMAP5 = dict( - Oc = 0.231, - Ob = 0.0459, - Om = 0.277, - Ol = 0.723, - H0 = 70.2, - n = 0.962, - sigma8 = 0.817, - tau = 0.088, - z_reion = 11.3, - t0 = 13.72, - reference = ("Komatsu et al. 2009, ApJS, 180, 330. " - "Table 1 (WMAP + BAO + SN ML)") - ) -available = tuple(k for k in locals() if not k.startswith('_')) +def __dir__(): + """Directory, including lazily-imported objects.""" + return __all__ diff --git a/astropy/cosmology/realizations.py b/astropy/cosmology/realizations.py new file mode 100644 index 000000000000..df71e7e30625 --- /dev/null +++ b/astropy/cosmology/realizations.py @@ -0,0 +1,75 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +"""Built-in cosmologies. + +See :attr:`~astropy.cosmology.realizations.available` for a full list. +""" + +__all__ = [ # noqa: F822, RUF022 + "available", + "default_cosmology", + # ---- + "WMAP1", + "WMAP3", + "WMAP5", + "WMAP7", + "WMAP9", + "Planck13", + "Planck15", + "Planck18", +] + +import pathlib +import sys + +from astropy.utils.data import get_pkg_data_path + +from ._src.core import Cosmology +from ._src.default import default_cosmology + +__doctest_requires__ = {"*": ["scipy"]} + +_COSMOLOGY_DATA_DIR = pathlib.Path( + get_pkg_data_path("cosmology", "data", package="astropy") +) +available = ( + "WMAP1", + "WMAP3", + "WMAP5", + "WMAP7", + "WMAP9", + "Planck13", + "Planck15", + "Planck18", +) + + +def __getattr__(name: str) -> Cosmology: + """Make specific realizations from data files with lazy import from ``PEP 562``. + + Raises + ------ + AttributeError + If "name" is not in :mod:`astropy.cosmology.realizations` + """ + if name not in available: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}.") + + cosmo = Cosmology.read( + str(_COSMOLOGY_DATA_DIR / name) + ".ecsv", format="ascii.ecsv" + ) + object.__setattr__( + cosmo, + "__doc__", + f"{name} instance of {cosmo.__class__.__qualname__} " + f"cosmology\n(from {cosmo.meta['reference']})", + ) + + # Cache in this module so `__getattr__` is only called once per `name`. + setattr(sys.modules[__name__], name, cosmo) + + return cosmo + + +def __dir__() -> list[str]: + """Directory, including lazily-imported objects.""" + return __all__ diff --git a/astropy/cosmology/tests/test_cosmology.py b/astropy/cosmology/tests/test_cosmology.py deleted file mode 100644 index 69dd62ac527c..000000000000 --- a/astropy/cosmology/tests/test_cosmology.py +++ /dev/null @@ -1,266 +0,0 @@ -# Licensed under a 3-clause BSD style license - see LICENSE.rst -from StringIO import StringIO -from .. import core, funcs -import numpy as np -import pytest - -try: - import scipy -except ImportError: - HAS_SCIPY = False -else: - HAS_SCIPY = True - - -@pytest.mark.skipif('not HAS_SCIPY') -def test_flat_z1(): - """ Test a flat cosmology at z=1 against several other on-line - calculators. - """ - cosmo = core.FLRWCosmology(H0=70, Om=0.27, Ol=0.73) - z = 1 - - # Test values were taken from the following web cosmology - # calculators on 27th Feb 2012: - - # Wright: http://www.astro.ucla.edu/~wright/CosmoCalc.html - # (http://adsabs.harvard.edu/abs/2006PASP..118.1711W) - # Kempner: http://www.kempner.net/cosmic.php - # iCosmos: http://www.icosmos.co.uk/index.html - - # The order of values below is Wright, Kempner, iCosmos' - assert np.allclose(cosmo.comoving_distance(z), - [3364.5, 3364.8, 3364.7988], rtol=1e-4) - assert np.allclose(cosmo.angular_diameter_distance(z), - [1682.3, 1682.4, 1682.3994], rtol=1e-4) - assert np.allclose(cosmo.luminosity_distance(z), - [6729.2, 6729.6, 6729.5976], rtol=1e-4) - assert np.allclose(cosmo.lookback_time(z), - [7.841, 7.84178, 7.843], rtol=1e-3) - -@pytest.mark.skipif('not HAS_SCIPY') -def test_convenience(): - #these are all for WMAP7 - core.set_current(core.WMAP7) - - # scalars - assert np.allclose(funcs.arcsec_per_kpc_comoving(3), 0.0317179) - assert np.allclose(funcs.arcsec_per_kpc_proper(3), 0.1268716668) - assert np.allclose(funcs.kpc_comoving_per_arcmin(3), 1891.6753126) - assert np.allclose(funcs.kpc_proper_per_arcmin(3), 472.918828) - assert np.allclose(funcs.distmod(3), 47.075902) - assert np.allclose(funcs.H(3), 299.80813491298068) - assert np.allclose(funcs.scale_factor(3), 0.25) - assert np.allclose(funcs.critical_density(3), 1.6884621680232328e-28) - assert np.allclose(funcs.lookback_time(3), 11.555469926558361) - assert np.allclose(funcs.comoving_distance(3), 6503.100697385924) - assert np.allclose(funcs.angular_diameter_distance(3), 1625.775174346481) - assert np.allclose(funcs.luminosity_distance(3), 26012.402789543696) - - # arrays - assert np.allclose(funcs.arcsec_per_kpc_comoving([0.1,0.5]), - [ 0.4946986 , 0.10876163]) - assert np.allclose(funcs.arcsec_per_kpc_proper([0.1,0.5]), - [0.54416846354697479, 0.16314245192751084]) - assert np.allclose(funcs.kpc_comoving_per_arcmin([0.1,0.5]), - [ 121.2859701 , 551.66511804]) - assert np.allclose(funcs.kpc_proper_per_arcmin([0.1,0.5]), - [ 110.25997282, 367.77674536]) - assert np.allclose(funcs.distmod([0.1,0.5]), - [ 38.30738567, 42.27020333]) - -@pytest.mark.skipif('not HAS_SCIPY') -def test_comoving_volume(): - - c_flat = core.FLRWCosmology(H0=70, Om=0.27, Ol=0.73) - c_open = core.FLRWCosmology(H0=70, Om=0.27, Ol=0.0) - c_closed = core.FLRWCosmology(H0=70, Om=2, Ol=0.0) - - redshifts = 0.5, 1, 2, 3, 5, 9 - - # test against ned wright's calculator (cubic Gpc) - wright_flat = 29.123, 159.529, 630.427, 1178.531, 2181.485, 3654.802 - wright_open = 20.501, 99.019, 380.278, 747.049, 1558.363, 3123.814 - wright_closed = 12.619, 44.708, 114.904, 173.709, 258.82, 358.992 - for i,z in enumerate(redshifts): - assert np.allclose(c_flat.comoving_volume(z), wright_flat[i] * 1e9, - rtol=1e-2) - assert np.allclose(c_open.comoving_volume(z), wright_open[i] * 1e9, - rtol=1e-2) - assert np.allclose(c_closed.comoving_volume(z), wright_closed[i] * 1e9, - rtol=1e-3) - -@pytest.mark.skipif('not HAS_SCIPY') -def test_flat_open_closed_icosmo(): - """ Test against the tabulated values generated from icosmo.org - with three example cosmologies (flat, open and closed). - """ - - cosmo_flat = """\ -# from icosmo (icosmo.org) -# Om 0.3 w -1 h 0.7 Ol 0.7 -# z comoving_transvers_dist angular_diameter_dist luminosity_dist - 0.0000000 0.0000000 0.0000000 0.0000000 - 0.16250000 669.77536 576.15085 778.61386 - 0.32500000 1285.5964 970.26143 1703.4152 - 0.50000000 1888.6254 1259.0836 2832.9381 - 0.66250000 2395.5489 1440.9317 3982.6000 - 0.82500000 2855.5732 1564.6976 5211.4210 - 1.0000000 3303.8288 1651.9144 6607.6577 - 1.1625000 3681.1867 1702.2829 7960.5663 - 1.3250000 4025.5229 1731.4077 9359.3408 - 1.5000000 4363.8558 1745.5423 10909.640 - 1.6625000 4651.4830 1747.0359 12384.573 - 1.8250000 4916.5970 1740.3883 13889.387 - 2.0000000 5179.8621 1726.6207 15539.586 - 2.1625000 5406.0204 1709.4136 17096.540 - 2.3250000 5616.5075 1689.1752 18674.888 - 2.5000000 5827.5418 1665.0120 20396.396 - 2.6625000 6010.4886 1641.0890 22013.414 - 2.8250000 6182.1688 1616.2533 23646.796 - 3.0000000 6355.6855 1588.9214 25422.742 - 3.1625000 6507.2491 1563.3031 27086.425 - 3.3250000 6650.4520 1537.6768 28763.205 - 3.5000000 6796.1499 1510.2555 30582.674 - 3.6625000 6924.2096 1485.0852 32284.127 - 3.8250000 7045.8876 1460.2876 33996.408 - 4.0000000 7170.3664 1434.0733 35851.832 - 4.1625000 7280.3423 1410.2358 37584.767 - 4.3250000 7385.3277 1386.9160 39326.870 - 4.5000000 7493.2222 1362.4040 41212.722 - 4.6625000 7588.9589 1340.2135 42972.480 -""" - - cosmo_open = """\ -# from icosmo (icosmo.org) -# Om 0.3 w -1 h 0.7 Ol 0.1 -# z comoving_transvers_dist angular_diameter_dist luminosity_dist - 0.0000000 0.0000000 0.0000000 0.0000000 - 0.16250000 643.08185 553.18868 747.58265 - 0.32500000 1200.9858 906.40441 1591.3062 - 0.50000000 1731.6262 1154.4175 2597.4393 - 0.66250000 2174.3252 1307.8648 3614.8157 - 0.82500000 2578.7616 1413.0201 4706.2399 - 1.0000000 2979.3460 1489.6730 5958.6920 - 1.1625000 3324.2002 1537.2024 7188.5829 - 1.3250000 3646.8432 1568.5347 8478.9104 - 1.5000000 3972.8407 1589.1363 9932.1017 - 1.6625000 4258.1131 1599.2913 11337.226 - 1.8250000 4528.5346 1603.0211 12793.110 - 2.0000000 4804.9314 1601.6438 14414.794 - 2.1625000 5049.2007 1596.5852 15968.097 - 2.3250000 5282.6693 1588.7727 17564.875 - 2.5000000 5523.0914 1578.0261 19330.820 - 2.6625000 5736.9813 1566.4113 21011.694 - 2.8250000 5942.5803 1553.6158 22730.370 - 3.0000000 6155.4289 1538.8572 24621.716 - 3.1625000 6345.6997 1524.4924 26413.975 - 3.3250000 6529.3655 1509.6799 28239.506 - 3.5000000 6720.2676 1493.3928 30241.204 - 3.6625000 6891.5474 1478.0799 32131.840 - 3.8250000 7057.4213 1462.6780 34052.058 - 4.0000000 7230.3723 1446.0745 36151.862 - 4.1625000 7385.9998 1430.7021 38130.224 - 4.3250000 7537.1112 1415.4199 40135.117 - 4.5000000 7695.0718 1399.1040 42322.895 - 4.6625000 7837.5510 1384.1150 44380.133 -""" - - cosmo_closed = """\ -# from icosmo (icosmo.org) -# Om 2 w -1 h 0.7 Ol 0.1 -# z comoving_transvers_dist angular_diameter_dist luminosity_dist - 0.0000000 0.0000000 0.0000000 0.0000000 - 0.16250000 601.80160 517.67879 699.59436 - 0.32500000 1057.9502 798.45297 1401.7840 - 0.50000000 1438.2161 958.81076 2157.3242 - 0.66250000 1718.6778 1033.7912 2857.3019 - 0.82500000 1948.2400 1067.5288 3555.5381 - 1.0000000 2152.7954 1076.3977 4305.5908 - 1.1625000 2312.3427 1069.2914 5000.4410 - 1.3250000 2448.9755 1053.3228 5693.8681 - 1.5000000 2575.6795 1030.2718 6439.1988 - 1.6625000 2677.9671 1005.8092 7130.0873 - 1.8250000 2768.1157 979.86398 7819.9270 - 2.0000000 2853.9222 951.30739 8561.7665 - 2.1625000 2924.8116 924.84161 9249.7167 - 2.3250000 2988.5333 898.80701 9936.8732 - 2.5000000 3050.3065 871.51614 10676.073 - 2.6625000 3102.1909 847.01459 11361.774 - 2.8250000 3149.5043 823.39982 12046.854 - 3.0000000 3195.9966 798.99915 12783.986 - 3.1625000 3235.5334 777.30533 13467.908 - 3.3250000 3271.9832 756.52790 14151.327 - 3.5000000 3308.1758 735.15017 14886.791 - 3.6625000 3339.2521 716.19347 15569.263 - 3.8250000 3368.1489 698.06195 16251.319 - 4.0000000 3397.0803 679.41605 16985.401 - 4.1625000 3422.1142 662.87926 17666.664 - 4.3250000 3445.5542 647.05243 18347.576 - 4.5000000 3469.1805 630.76008 19080.493 - 4.6625000 3489.7534 616.29199 19760.729 -""" - - redshifts, dm, da, dl = np.loadtxt(StringIO(cosmo_flat), unpack=1) - cosmo = core.FLRWCosmology(H0=70, Om=0.3, Ol=0.70) - assert np.allclose(cosmo.comoving_transverse_distance(redshifts), dm) - assert np.allclose(cosmo.angular_diameter_distance(redshifts), da) - assert np.allclose(cosmo.luminosity_distance(redshifts), dl) - - redshifts, dm, da, dl = np.loadtxt(StringIO(cosmo_open), unpack=1) - cosmo = core.FLRWCosmology(H0=70, Om=0.3, Ol=0.1) - assert np.allclose(cosmo.comoving_transverse_distance(redshifts), dm) - assert np.allclose(cosmo.angular_diameter_distance(redshifts), da) - assert np.allclose(cosmo.luminosity_distance(redshifts), dl) - - redshifts, dm, da, dl = np.loadtxt(StringIO(cosmo_closed), unpack=1) - cosmo = core.FLRWCosmology(H0=70, Om=2, Ol=0.1) - assert np.allclose(cosmo.comoving_transverse_distance(redshifts), dm) - assert np.allclose(cosmo.angular_diameter_distance(redshifts), da) - assert np.allclose(cosmo.luminosity_distance(redshifts), dl) - - -def test_current(): - cosmo = core.get_current() - assert cosmo == core.WMAP7 - core.set_current('WMAP5') - assert core.get_current() == core.WMAP5 - core.set_current(cosmo) - assert core.get_current() == cosmo - -@pytest.mark.skipif('not HAS_SCIPY') -def test_age(): - assert np.allclose(core.WMAP7.age([1,5]), [ 5.97113193, 1.20553129]) - -@pytest.mark.skipif('not HAS_SCIPY') -def test_distmod(): - assert np.allclose(core.WMAP7.distmod([1,5]), [ 44.124857, 48.40167258]) - assert np.allclose(funcs.distmod([1,5], cosmo=core.WMAP7), - [ 44.124857, 48.40167258]) - -@pytest.mark.skipif('not HAS_SCIPY') -def test_critical_density(): - assert np.allclose(core.WMAP7.critical_density([1,5]), - [ 2.70362491e-29, 5.53758986e-28]) - -@pytest.mark.skipif('not HAS_SCIPY') -def test_angular_diameter_distance_z1z2(): - assert (core.WMAP7.angular_diameter_distance_z1z2(1, 2) == - 646.22968662822018) - z1 = 0, 0, 1, 0.5, 1 - z2 = 2, 1, 2, 2.5, 1.1 - results = (1760.0628637762106, - 1670.7497657219858, - 646.22968662822018, - 1159.0970895962193, - 115.72768186186921) - - assert np.allclose(core.WMAP7.angular_diameter_distance_z1z2(z1, z2), - results) - -@pytest.mark.skipif('not HAS_SCIPY') -def test_absorption_distance(): - assert np.allclose(core.WMAP7.absorption_distance([1,3]), - [ 1.72576635, 7.98685853]) - assert np.allclose(core.WMAP7.absorption_distance(3), 7.98685853) - diff --git a/astropy/cosmology/tests/test_scalar_inv_efuncs.py b/astropy/cosmology/tests/test_scalar_inv_efuncs.py new file mode 100644 index 000000000000..56c35907c92d --- /dev/null +++ b/astropy/cosmology/tests/test_scalar_inv_efuncs.py @@ -0,0 +1,128 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +"""Tests for the scalar_inv_efuncs Cython extension in astropy.cosmology.""" + +from numpy.testing import assert_allclose + +from astropy.cosmology._src.flrw.scalar_inv_efuncs import ( + flcdm_inv_efunc_nomnu, + flcdm_inv_efunc_norel, + fwcdm_inv_efunc_norel, + lcdm_inv_efunc_nomnu, + lcdm_inv_efunc_norel, + wcdm_inv_efunc_norel, +) + +# --------------------------------------------------------------------------- +# LambdaCDM (no relativistic species) +# At z=0: E(0)=1 => inv_efunc=1 for flat universe (Om0+Ode0=1, Ok0=0) +# --------------------------------------------------------------------------- + + +def test_lcdm_inv_efunc_norel_z0(): + # Flat universe at z=0: Om0+Ode0=1, Ok0=0 => E(0)=1 + assert_allclose(lcdm_inv_efunc_norel(0.0, 0.3, 0.7, 0.0), 1.0) + + +def test_lcdm_inv_efunc_norel_matter_dominated(): + # Matter-only universe (Ode0=0, Ok0=0, Om0=1): E(z)=(1+z)^1.5 + # => inv_efunc(z) = (1+z)^-1.5 + z = 1.0 + expected = (1.0 + z) ** -1.5 + assert_allclose(lcdm_inv_efunc_norel(z, 1.0, 0.0, 0.0), expected) + + +def test_lcdm_inv_efunc_norel_positive(): + """inv_efunc must always be positive for physical parameters.""" + assert lcdm_inv_efunc_norel(0.5, 0.3, 0.7, 0.0) > 0.0 + + +# --------------------------------------------------------------------------- +# LambdaCDM (massless neutrinos) +# --------------------------------------------------------------------------- + + +def test_lcdm_inv_efunc_nomnu_z0(): + # Flat universe at z=0: Om0+Ode0+Or0=1, Ok0=0 => E(0)=1 + assert_allclose(lcdm_inv_efunc_nomnu(0.0, 0.3, 0.6999, 0.0, 0.0001), 1.0) + + +def test_lcdm_inv_efunc_nomnu_positive(): + """inv_efunc must always be positive for physical parameters.""" + assert lcdm_inv_efunc_nomnu(1.0, 0.3, 0.7, 0.0, 0.0) > 0.0 + + +# --------------------------------------------------------------------------- +# FlatLambdaCDM (no relativistic species) +# --------------------------------------------------------------------------- + + +def test_flcdm_inv_efunc_norel_z0(): + # Flat: Om0+Ode0=1 => E(0)=1 + assert_allclose(flcdm_inv_efunc_norel(0.0, 0.3, 0.7), 1.0) + + +def test_flcdm_inv_efunc_norel_matter_dominated(): + # Matter-only flat: Ode0=0, Om0=1 => inv_efunc(z)=(1+z)^-1.5 + z = 2.0 + expected = (1.0 + z) ** -1.5 + assert_allclose(flcdm_inv_efunc_norel(z, 1.0, 0.0), expected) + + +def test_flcdm_inv_efunc_norel_positive(): + """inv_efunc must always be positive for physical parameters.""" + assert flcdm_inv_efunc_norel(0.5, 0.3, 0.7) > 0.0 + + +# --------------------------------------------------------------------------- +# FlatLambdaCDM (massless neutrinos) +# --------------------------------------------------------------------------- + + +def test_flcdm_inv_efunc_nomnu_z0(): + # Flat: Om0+Ode0+Or0=1 => E(0)=1 + assert_allclose(flcdm_inv_efunc_nomnu(0.0, 0.3, 0.6999, 0.0001), 1.0) + + +# --------------------------------------------------------------------------- +# wCDM (no relativistic species) +# --------------------------------------------------------------------------- + + +def test_wcdm_inv_efunc_norel_z0(): + # Flat wCDM at z=0 with Om0+Ode0=1, Ok0=0 => E(0)=1 + assert_allclose(wcdm_inv_efunc_norel(0.0, 0.3, 0.7, 0.0, -1.0), 1.0) + + +def test_wcdm_inv_efunc_norel_lcdm_limit(): + # w0=-1 => wCDM reduces to LambdaCDM + z = 0.5 + Om0, Ode0, Ok0, w0 = 0.3, 0.7, 0.0, -1.0 + assert_allclose( + wcdm_inv_efunc_norel(z, Om0, Ode0, Ok0, w0), + lcdm_inv_efunc_norel(z, Om0, Ode0, Ok0), + ) + + +def test_wcdm_inv_efunc_norel_positive(): + """inv_efunc must always be positive for physical parameters.""" + assert wcdm_inv_efunc_norel(1.0, 0.3, 0.7, 0.0, -0.8) > 0.0 + + +# --------------------------------------------------------------------------- +# FlatwCDM (no relativistic species) +# --------------------------------------------------------------------------- + + +def test_fwcdm_inv_efunc_norel_z0(): + # Flat wCDM at z=0: Om0+Ode0=1 => E(0)=1 + assert_allclose(fwcdm_inv_efunc_norel(0.0, 0.3, 0.7, -1.0), 1.0) + + +def test_fwcdm_inv_efunc_norel_lcdm_limit(): + # w0=-1 => flat wCDM reduces to flat LambdaCDM + z = 1.0 + Om0, Ode0, w0 = 0.3, 0.7, -1.0 + assert_allclose( + fwcdm_inv_efunc_norel(z, Om0, Ode0, w0), + flcdm_inv_efunc_norel(z, Om0, Ode0), + ) diff --git a/astropy/cosmology/traits.py b/astropy/cosmology/traits.py new file mode 100644 index 000000000000..dc4ad40ea2cb --- /dev/null +++ b/astropy/cosmology/traits.py @@ -0,0 +1,32 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +"""Traits for building ``astropy`` :class:`~astropy.cosmology.Cosmology` classes.""" + +__all__ = ( + "BaryonComponent", + "CriticalDensity", + "CurvatureComponent", + "DarkEnergyComponent", + "DarkMatterComponent", + "HubbleParameter", + "MatterComponent", + "NeutrinoComponent", + "PhotonComponent", + "ScaleFactor", + "TemperatureCMB", + "TotalComponent", +) + +from ._src.traits import ( + BaryonComponent, + CriticalDensity, + CurvatureComponent, + DarkEnergyComponent, + DarkMatterComponent, + HubbleParameter, + MatterComponent, + NeutrinoComponent, + PhotonComponent, + ScaleFactor, + TemperatureCMB, + TotalComponent, +) diff --git a/astropy/cosmology/units.py b/astropy/cosmology/units.py new file mode 100644 index 000000000000..4d2d66cce254 --- /dev/null +++ b/astropy/cosmology/units.py @@ -0,0 +1,44 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +"""Cosmological units and equivalencies.""" + +__all__ = ( + # redshift equivalencies + "dimensionless_redshift", + "littleh", + "redshift", + "redshift_distance", + "redshift_hubble", + "redshift_temperature", + # other equivalencies + "with_H0", + "with_redshift", +) + +from astropy.units import add_enabled_equivalencies as _add_enabled_equivalencies +from astropy.units.docgen import generate_unit_summary as _generate_unit_summary + +from ._src.units import littleh, redshift +from ._src.units_equivalencies import ( + dimensionless_redshift, + redshift_distance, + redshift_hubble, + redshift_temperature, + with_H0, + with_redshift, +) + +# =================================================================== +# Enable the set of default equivalencies. +# If the cosmology package is imported, this is added to the list astropy-wide. +_add_enabled_equivalencies(dimensionless_redshift()) + + +# ============================================================================= +# DOCSTRING + +# This generates a docstring for this module that describes all of the +# standard units defined here. +if __doc__ is not None: + from ._src.units import _ns + + __doc__ += "\n" + _generate_unit_summary(_ns) diff --git a/astropy/data/README.rst b/astropy/data/README.rst deleted file mode 100644 index feb98cc1dc22..000000000000 --- a/astropy/data/README.rst +++ /dev/null @@ -1,7 +0,0 @@ -Data directory -============== - -This directory contains data files included with the astropy source code -distribution. Note that this is intended only for relatively small files - -large files should be externally hosted and downloaded as needed. - diff --git a/astropy/extern/README.rst b/astropy/extern/README.rst new file mode 100644 index 000000000000..47880ccd9851 --- /dev/null +++ b/astropy/extern/README.rst @@ -0,0 +1,58 @@ +astropy.extern +============== + +This sub-package contains third-party Python packages/modules that are +required for some of Astropy's core functionality. It also contains third- +party JavaScript libraries used for browser-based features. + +In particular, this currently includes for Python: + +- ConfigObj_: This provides the core config file handling for Astropy's + configuration system. + +- PLY_: This is a parser generator providing lex/yacc-like tools in Python. + It is used for Astropy's unit parsing and angle/coordinate string parsing. + +Notes for third-party packagers +------------------------------- + +Packagers preparing Astropy for inclusion in packaging frameworks have +different options for how to handle these third-party extern packages, if they +would prefer to use their system packages rather than the bundled versions. + +jQuery/DataTables +^^^^^^^^^^^^^^^^^ + +It is possible to change the default urls for the remote versions of these +files by using the Astropy +`Configuration system `_. The default +configuration file (``$XDG_CONFIG_HOME/astropy/astropy.cfg``) contains a commented section +``[table.jsviewer]`` with two items for jQuery_ and DataTables_. It is also +possible to display the default value and modify it by importing the +configuration module:: + + In [1]: from astropy.table.jsviewer import conf + + In [2]: conf.jquery_url + Out[2]: u'https://code.jquery.com/jquery-1.11.3.min.js' + + In [3]: conf.jquery_url = '...' + +Third-party packagers can override the defaults for these configuration items +by modifying the configuration objects in ``astropy/table/jsviewer.py``, or +provide astropy config files that include the overrides appropriate for the +packaged version. + + +Other +^^^^^ + +To replace any of the other Python modules included in this package, simply +remove them and update any imports in Astropy to import the system versions +rather than the bundled copies. + + +.. _ConfigObj: https://github.com/DiffSK/configobj +.. _PLY: http://www.dabeaz.com/ply/ +.. _jQuery: http://jquery.com/ +.. _DataTables: http://www.datatables.net/ diff --git a/astropy/extern/__init__.py b/astropy/extern/__init__.py index 100633fe0fa2..4c54f845d191 100644 --- a/astropy/extern/__init__.py +++ b/astropy/extern/__init__.py @@ -1,8 +1,10 @@ # Licensed under a 3-clause BSD style license - see LICENSE.rst """ -This packages contains python packages that are bundled with Astropy -but are external to Astropy, and hence are developed in a separate -source tree. Note that this package is distinct from the /cextern -directory of the source code distribution, as that directory only -contains C extension code. +This packages contains python packages that are bundled with Astropy but are +external to Astropy, and hence are developed in a separate source tree. Note +that this package is distinct from the /cextern directory of the source code +distribution, as that directory only contains C extension code. + +See the README.rst in this directory of the Astropy source repository for more +details. """ diff --git a/astropy/extern/_strptime.py b/astropy/extern/_strptime.py new file mode 100644 index 000000000000..cbb891454900 --- /dev/null +++ b/astropy/extern/_strptime.py @@ -0,0 +1,529 @@ +"""Strptime-related classes and functions. + +CLASSES: + LocaleTime -- Discovers and stores locale-specific time information + TimeRE -- Creates regexes for pattern matching a string of text containing + time information + +FUNCTIONS: + _getlang -- Figure out what language is being used for the locale + strptime -- Calculates the time struct represented by the passed-in string + +""" +# ----------------------------------------------------------------------------- +# _strptime.py +# +# Licensed under PYTHON SOFTWARE FOUNDATION LICENSE +# See licenses/PYTHON.rst +# +# Copied from https://github.com/python/cpython/blob/3.5/Lib/_strptime.py +# ----------------------------------------------------------------------------- +import time +import locale +import calendar +from re import compile as re_compile +from re import IGNORECASE +from re import escape as re_escape +from datetime import (date as datetime_date, + timedelta as datetime_timedelta, + timezone as datetime_timezone) +try: + from _thread import allocate_lock as _thread_allocate_lock +except ImportError: + from _dummy_thread import allocate_lock as _thread_allocate_lock + +__all__ = [] + +def _getlang(): + # Figure out what the current language is set to. + return locale.getlocale(locale.LC_TIME) + +class LocaleTime(object): + """Stores and handles locale-specific information related to time. + + ATTRIBUTES: + f_weekday -- full weekday names (7-item list) + a_weekday -- abbreviated weekday names (7-item list) + f_month -- full month names (13-item list; dummy value in [0], which + is added by code) + a_month -- abbreviated month names (13-item list, dummy value in + [0], which is added by code) + am_pm -- AM/PM representation (2-item list) + LC_date_time -- format string for date/time representation (string) + LC_date -- format string for date representation (string) + LC_time -- format string for time representation (string) + timezone -- daylight- and non-daylight-savings timezone representation + (2-item list of sets) + lang -- Language used by instance (2-item tuple) + """ + + def __init__(self): + """Set all attributes. + + Order of methods called matters for dependency reasons. + + The locale language is set at the offset and then checked again before + exiting. This is to make sure that the attributes were not set with a + mix of information from more than one locale. This would most likely + happen when using threads where one thread calls a locale-dependent + function while another thread changes the locale while the function in + the other thread is still running. Proper coding would call for + locks to prevent changing the locale while locale-dependent code is + running. The check here is done in case someone does not think about + doing this. + + Only other possible issue is if someone changed the timezone and did + not call tz.tzset . That is an issue for the programmer, though, + since changing the timezone is worthless without that call. + + """ + self.lang = _getlang() + self.__calc_weekday() + self.__calc_month() + self.__calc_am_pm() + self.__calc_timezone() + self.__calc_date_time() + if _getlang() != self.lang: + raise ValueError("locale changed during initialization") + if time.tzname != self.tzname or time.daylight != self.daylight: + raise ValueError("timezone changed during initialization") + + def __pad(self, seq, front): + # Add '' to seq to either the front (is True), else the back. + seq = list(seq) + if front: + seq.insert(0, '') + else: + seq.append('') + return seq + + def __calc_weekday(self): + # Set self.a_weekday and self.f_weekday using the calendar + # module. + a_weekday = [calendar.day_abbr[i].lower() for i in range(7)] + f_weekday = [calendar.day_name[i].lower() for i in range(7)] + self.a_weekday = a_weekday + self.f_weekday = f_weekday + + def __calc_month(self): + # Set self.f_month and self.a_month using the calendar module. + a_month = [calendar.month_abbr[i].lower() for i in range(13)] + f_month = [calendar.month_name[i].lower() for i in range(13)] + self.a_month = a_month + self.f_month = f_month + + def __calc_am_pm(self): + # Set self.am_pm by using time.strftime(). + + # The magic date (1999,3,17,hour,44,55,2,76,0) is not really that + # magical; just happened to have used it everywhere else where a + # static date was needed. + am_pm = [] + for hour in (1, 22): + time_tuple = time.struct_time((1999,3,17,hour,44,55,2,76,0)) + am_pm.append(time.strftime("%p", time_tuple).lower()) + self.am_pm = am_pm + + def __calc_date_time(self): + # Set self.date_time, self.date, & self.time by using + # time.strftime(). + + # Use (1999,3,17,22,44,55,2,76,0) for magic date because the amount of + # overloaded numbers is minimized. The order in which searches for + # values within the format string is very important; it eliminates + # possible ambiguity for what something represents. + time_tuple = time.struct_time((1999,3,17,22,44,55,2,76,0)) + date_time = [None, None, None] + date_time[0] = time.strftime("%c", time_tuple).lower() + date_time[1] = time.strftime("%x", time_tuple).lower() + date_time[2] = time.strftime("%X", time_tuple).lower() + replacement_pairs = [('%', '%%'), (self.f_weekday[2], '%A'), + (self.f_month[3], '%B'), (self.a_weekday[2], '%a'), + (self.a_month[3], '%b'), (self.am_pm[1], '%p'), + ('1999', '%Y'), ('99', '%y'), ('22', '%H'), + ('44', '%M'), ('55', '%S'), ('76', '%j'), + ('17', '%d'), ('03', '%m'), ('3', '%m'), + # '3' needed for when no leading zero. + ('2', '%w'), ('10', '%I')] + replacement_pairs.extend([(tz, "%Z") for tz_values in self.timezone + for tz in tz_values]) + for offset,directive in ((0,'%c'), (1,'%x'), (2,'%X')): + current_format = date_time[offset] + for old, new in replacement_pairs: + # Must deal with possible lack of locale info + # manifesting itself as the empty string (e.g., Swedish's + # lack of AM/PM info) or a platform returning a tuple of empty + # strings (e.g., MacOS 9 having timezone as ('','')). + if old: + current_format = current_format.replace(old, new) + # If %W is used, then Sunday, 2005-01-03 will fall on week 0 since + # 2005-01-03 occurs before the first Monday of the year. Otherwise + # %U is used. + time_tuple = time.struct_time((1999,1,3,1,1,1,6,3,0)) + if '00' in time.strftime(directive, time_tuple): + U_W = '%W' + else: + U_W = '%U' + date_time[offset] = current_format.replace('11', U_W) + self.LC_date_time = date_time[0] + self.LC_date = date_time[1] + self.LC_time = date_time[2] + + def __calc_timezone(self): + # Set self.timezone by using time.tzname. + # Do not worry about possibility of time.tzname[0] == time.tzname[1] + # and time.daylight; handle that in strptime. + try: + time.tzset() + except AttributeError: + pass + self.tzname = time.tzname + self.daylight = time.daylight + no_saving = frozenset({"utc", "gmt", self.tzname[0].lower()}) + if self.daylight: + has_saving = frozenset({self.tzname[1].lower()}) + else: + has_saving = frozenset() + self.timezone = (no_saving, has_saving) + + +class TimeRE(dict): + """Handle conversion from format directives to regexes.""" + + def __init__(self, locale_time=None): + """Create keys/values. + + Order of execution is important for dependency reasons. + + """ + if locale_time: + self.locale_time = locale_time + else: + self.locale_time = LocaleTime() + base = super() + base.__init__({ + # The " \d" part of the regex is to make %c from ANSI C work + 'd': r"(?P3[0-1]|[1-2]\d|0[1-9]|[1-9]| [1-9])", + 'f': r"(?P[0-9]{1,6})", + 'H': r"(?P2[0-3]|[0-1]\d|\d)", + 'I': r"(?P1[0-2]|0[1-9]|[1-9])", + 'j': r"(?P36[0-6]|3[0-5]\d|[1-2]\d\d|0[1-9]\d|00[1-9]|[1-9]\d|0[1-9]|[1-9])", + 'm': r"(?P1[0-2]|0[1-9]|[1-9])", + 'M': r"(?P[0-5]\d|\d)", + 'S': r"(?P6[0-1]|[0-5]\d|\d)", + 'U': r"(?P5[0-3]|[0-4]\d|\d)", + 'w': r"(?P[0-6])", + # W is set below by using 'U' + 'y': r"(?P\d\d)", + #XXX: Does 'Y' need to worry about having less or more than + # 4 digits? + 'Y': r"(?P\d\d\d\d)", + 'z': r"(?P[+-]\d\d[0-5]\d)", + 'A': self.__seqToRE(self.locale_time.f_weekday, 'A'), + 'a': self.__seqToRE(self.locale_time.a_weekday, 'a'), + 'B': self.__seqToRE(self.locale_time.f_month[1:], 'B'), + 'b': self.__seqToRE(self.locale_time.a_month[1:], 'b'), + 'p': self.__seqToRE(self.locale_time.am_pm, 'p'), + 'Z': self.__seqToRE((tz for tz_names in self.locale_time.timezone + for tz in tz_names), + 'Z'), + '%': '%'}) + base.__setitem__('W', base.__getitem__('U').replace('U', 'W')) + base.__setitem__('c', self.pattern(self.locale_time.LC_date_time)) + base.__setitem__('x', self.pattern(self.locale_time.LC_date)) + base.__setitem__('X', self.pattern(self.locale_time.LC_time)) + + def __seqToRE(self, to_convert, directive): + """Convert a list to a regex string for matching a directive. + + Want possible matching values to be from longest to shortest. This + prevents the possibility of a match occurring for a value that also + a substring of a larger value that should have matched (e.g., 'abc' + matching when 'abcdef' should have been the match). + + """ + to_convert = sorted(to_convert, key=len, reverse=True) + for value in to_convert: + if value != '': + break + else: + return '' + regex = '|'.join(re_escape(stuff) for stuff in to_convert) + regex = '(?P<%s>%s' % (directive, regex) + return '%s)' % regex + + def pattern(self, format): + """Return regex pattern for the format string. + + Need to make sure that any characters that might be interpreted as + regex syntax are escaped. + + """ + processed_format = '' + # The sub() call escapes all characters that might be misconstrued + # as regex syntax. Cannot use re.escape since we have to deal with + # format directives (%m, etc.). + regex_chars = re_compile(r"([\\.^$*+?\(\){}\[\]|])") + format = regex_chars.sub(r"\\\1", format) + whitespace_replacement = re_compile(r'\s+') + format = whitespace_replacement.sub(r'\\s+', format) + while '%' in format: + directive_index = format.index('%')+1 + processed_format = "%s%s%s" % (processed_format, + format[:directive_index-1], + self[format[directive_index]]) + format = format[directive_index+1:] + return "%s%s" % (processed_format, format) + + def compile(self, format): + """Return a compiled re object for the format string.""" + return re_compile(self.pattern(format), IGNORECASE) + +_cache_lock = _thread_allocate_lock() +# DO NOT modify _TimeRE_cache or _regex_cache without acquiring the cache lock +# first! +_TimeRE_cache = TimeRE() +_CACHE_MAX_SIZE = 5 # Max number of regexes stored in _regex_cache +_regex_cache = {} + +def _calc_julian_from_U_or_W(year, week_of_year, day_of_week, week_starts_Mon): + """Calculate the Julian day based on the year, week of the year, and day of + the week, with week_start_day representing whether the week of the year + assumes the week starts on Sunday or Monday (6 or 0).""" + first_weekday = datetime_date(year, 1, 1).weekday() + # If we are dealing with the %U directive (week starts on Sunday), it's + # easier to just shift the view to Sunday being the first day of the + # week. + if not week_starts_Mon: + first_weekday = (first_weekday + 1) % 7 + day_of_week = (day_of_week + 1) % 7 + # Need to watch out for a week 0 (when the first day of the year is not + # the same as that specified by %U or %W). + week_0_length = (7 - first_weekday) % 7 + if week_of_year == 0: + return 1 + day_of_week - first_weekday + else: + days_to_week = week_0_length + (7 * (week_of_year - 1)) + return 1 + days_to_week + day_of_week + + +def _strptime(data_string, format="%a %b %d %H:%M:%S %Y"): + """Return a 2-tuple consisting of a time struct and an int containing + the number of microseconds based on the input string and the + format string.""" + + for index, arg in enumerate([data_string, format]): + if not isinstance(arg, str): + msg = "strptime() argument {} must be str, not {}" + raise TypeError(msg.format(index, type(arg))) + + global _TimeRE_cache, _regex_cache + with _cache_lock: + locale_time = _TimeRE_cache.locale_time + if (_getlang() != locale_time.lang or + time.tzname != locale_time.tzname or + time.daylight != locale_time.daylight): + _TimeRE_cache = TimeRE() + _regex_cache.clear() + locale_time = _TimeRE_cache.locale_time + if len(_regex_cache) > _CACHE_MAX_SIZE: + _regex_cache.clear() + format_regex = _regex_cache.get(format) + if not format_regex: + try: + format_regex = _TimeRE_cache.compile(format) + # KeyError raised when a bad format is found; can be specified as + # \\, in which case it was a stray % but with a space after it + except KeyError as err: + bad_directive = err.args[0] + if bad_directive == "\\": + bad_directive = "%" + del err + raise ValueError("'%s' is a bad directive in format '%s'" % + (bad_directive, format)) from None + # IndexError only occurs when the format string is "%" + except IndexError: + raise ValueError("stray %% in format '%s'" % format) from None + _regex_cache[format] = format_regex + found = format_regex.match(data_string) + if not found: + raise ValueError("time data %r does not match format %r" % + (data_string, format)) + if len(data_string) != found.end(): + raise ValueError("unconverted data remains: %s" % + data_string[found.end():]) + + year = None + month = day = 1 + hour = minute = second = fraction = 0 + tz = -1 + tzoffset = None + # Default to -1 to signify that values not known; not critical to have, + # though + week_of_year = -1 + week_of_year_start = -1 + # weekday and julian defaulted to None so as to signal need to calculate + # values + weekday = julian = None + found_dict = found.groupdict() + for group_key in found_dict.keys(): + # Directives not explicitly handled below: + # c, x, X + # handled by making out of other directives + # U, W + # worthless without day of the week + if group_key == 'y': + year = int(found_dict['y']) + # Open Group specification for strptime() states that a %y + #value in the range of [00, 68] is in the century 2000, while + #[69,99] is in the century 1900 + if year <= 68: + year += 2000 + else: + year += 1900 + elif group_key == 'Y': + year = int(found_dict['Y']) + elif group_key == 'm': + month = int(found_dict['m']) + elif group_key == 'B': + month = locale_time.f_month.index(found_dict['B'].lower()) + elif group_key == 'b': + month = locale_time.a_month.index(found_dict['b'].lower()) + elif group_key == 'd': + day = int(found_dict['d']) + elif group_key == 'H': + hour = int(found_dict['H']) + elif group_key == 'I': + hour = int(found_dict['I']) + ampm = found_dict.get('p', '').lower() + # If there was no AM/PM indicator, we'll treat this like AM + if ampm in ('', locale_time.am_pm[0]): + # We're in AM so the hour is correct unless we're + # looking at 12 midnight. + # 12 midnight == 12 AM == hour 0 + if hour == 12: + hour = 0 + elif ampm == locale_time.am_pm[1]: + # We're in PM so we need to add 12 to the hour unless + # we're looking at 12 noon. + # 12 noon == 12 PM == hour 12 + if hour != 12: + hour += 12 + elif group_key == 'M': + minute = int(found_dict['M']) + elif group_key == 'S': + second = int(found_dict['S']) + elif group_key == 'f': + s = found_dict['f'] + # Pad to always return microseconds. + s += "0" * (6 - len(s)) + fraction = int(s) + elif group_key == 'A': + weekday = locale_time.f_weekday.index(found_dict['A'].lower()) + elif group_key == 'a': + weekday = locale_time.a_weekday.index(found_dict['a'].lower()) + elif group_key == 'w': + weekday = int(found_dict['w']) + if weekday == 0: + weekday = 6 + else: + weekday -= 1 + elif group_key == 'j': + julian = int(found_dict['j']) + elif group_key in ('U', 'W'): + week_of_year = int(found_dict[group_key]) + if group_key == 'U': + # U starts week on Sunday. + week_of_year_start = 6 + else: + # W starts week on Monday. + week_of_year_start = 0 + elif group_key == 'z': + z = found_dict['z'] + tzoffset = int(z[1:3]) * 60 + int(z[3:5]) + if z.startswith("-"): + tzoffset = -tzoffset + elif group_key == 'Z': + # Since -1 is default value only need to worry about setting tz if + # it can be something other than -1. + found_zone = found_dict['Z'].lower() + for value, tz_values in enumerate(locale_time.timezone): + if found_zone in tz_values: + # Deal with bad locale setup where timezone names are the + # same and yet time.daylight is true; too ambiguous to + # be able to tell what timezone has daylight savings + if (time.tzname[0] == time.tzname[1] and + time.daylight and found_zone not in ("utc", "gmt")): + break + else: + tz = value + break + leap_year_fix = False + if year is None and month == 2 and day == 29: + year = 1904 # 1904 is first leap year of 20th century + leap_year_fix = True + elif year is None: + year = 1900 + # If we know the week of the year and what day of that week, we can figure + # out the Julian day of the year. + if julian is None and week_of_year != -1 and weekday is not None: + week_starts_Mon = True if week_of_year_start == 0 else False + julian = _calc_julian_from_U_or_W(year, week_of_year, weekday, + week_starts_Mon) + if julian <= 0: + year -= 1 + yday = 366 if calendar.isleap(year) else 365 + julian += yday + # Cannot pre-calculate datetime_date() since can change in Julian + # calculation and thus could have different value for the day of the week + # calculation. + if julian is None: + # Need to add 1 to result since first day of the year is 1, not 0. + julian = datetime_date(year, month, day).toordinal() - \ + datetime_date(year, 1, 1).toordinal() + 1 + else: # Assume that if they bothered to include Julian day it will + # be accurate. + datetime_result = datetime_date.fromordinal((julian - 1) + datetime_date(year, 1, 1).toordinal()) + year = datetime_result.year + month = datetime_result.month + day = datetime_result.day + if weekday is None: + weekday = datetime_date(year, month, day).weekday() + # Add timezone info + tzname = found_dict.get("Z") + if tzoffset is not None: + gmtoff = tzoffset * 60 + else: + gmtoff = None + + if leap_year_fix: + # the caller didn't supply a year but asked for Feb 29th. We couldn't + # use the default of 1900 for computations. We set it back to ensure + # that February 29th is smaller than March 1st. + year = 1900 + + return (year, month, day, + hour, minute, second, + weekday, julian, tz, tzname, gmtoff), fraction + +def _strptime_time(data_string, format="%a %b %d %H:%M:%S %Y"): + """Return a time struct based on the input string and the + format string.""" + tt = _strptime(data_string, format)[0] + return time.struct_time(tt[:time._STRUCT_TM_ITEMS]) + +def _strptime_datetime(cls, data_string, format="%a %b %d %H:%M:%S %Y"): + """Return a class cls instance based on the input string and the + format string.""" + tt, fraction = _strptime(data_string, format) + tzname, gmtoff = tt[-2:] + args = tt[:6] + (fraction,) + if gmtoff is not None: + tzdelta = datetime_timedelta(seconds=gmtoff) + if tzname: + tz = datetime_timezone(tzdelta, tzname) + else: + tz = datetime_timezone(tzdelta) + args += (tz,) + + return cls(*args) diff --git a/astropy/extern/configobj.py b/astropy/extern/configobj.py deleted file mode 100644 index b457b22bfd2e..000000000000 --- a/astropy/extern/configobj.py +++ /dev/null @@ -1,17 +0,0 @@ -# Licensed under a 3-clause BSD style license - see LICENSE.rst - -""" -This module just pulls in the appropriate `configobj` package, depending on the -currently installed version of python. - -Also, this should actually never actually show up as a docstring, because -it should get overwritten by the appropriate configobj docstring. -""" -from sys import version_info - -if version_info[0]>2: - from .configobj_py3 import configobj,validate,__doc__ -else: - from .configobj_py2 import configobj,validate,__doc__ - -del version_info #cleans up the namespace diff --git a/astropy/extern/configobj/__init__.py b/astropy/extern/configobj/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/astropy/extern/configobj/configobj.py b/astropy/extern/configobj/configobj.py new file mode 100755 index 000000000000..167608ab37c0 --- /dev/null +++ b/astropy/extern/configobj/configobj.py @@ -0,0 +1,2472 @@ +# configobj.py +# A config file reader/writer that supports nested sections in config files. +# Copyright (C) 2005-2014: +# (name) : (email) +# Michael Foord: fuzzyman AT voidspace DOT org DOT uk +# Nicola Larosa: nico AT tekNico DOT net +# Rob Dennis: rdennis AT gmail DOT com +# Eli Courtwright: eli AT courtwright DOT org + +# This software is licensed under the terms of the BSD license. +# http://opensource.org/licenses/BSD-3-Clause + +# ConfigObj 5 - main repository for documentation and issue tracking: +# https://github.com/DiffSK/configobj + +import os +import re +import sys +from collections.abc import Mapping + +from codecs import BOM_UTF8, BOM_UTF16, BOM_UTF16_BE, BOM_UTF16_LE + +# imported lazily to avoid startup performance hit if it isn't used +compiler = None + +# A dictionary mapping BOM to +# the encoding to decode with, and what to set the +# encoding attribute to. +BOMS = { + BOM_UTF8: ('utf_8', None), + BOM_UTF16_BE: ('utf16_be', 'utf_16'), + BOM_UTF16_LE: ('utf16_le', 'utf_16'), + BOM_UTF16: ('utf_16', 'utf_16'), + } +# All legal variants of the BOM codecs. +# TODO: the list of aliases is not meant to be exhaustive, is there a +# better way ? +BOM_LIST = { + 'utf_16': 'utf_16', + 'u16': 'utf_16', + 'utf16': 'utf_16', + 'utf-16': 'utf_16', + 'utf16_be': 'utf16_be', + 'utf_16_be': 'utf16_be', + 'utf-16be': 'utf16_be', + 'utf16_le': 'utf16_le', + 'utf_16_le': 'utf16_le', + 'utf-16le': 'utf16_le', + 'utf_8': 'utf_8', + 'u8': 'utf_8', + 'utf': 'utf_8', + 'utf8': 'utf_8', + 'utf-8': 'utf_8', + } + +# Map of encodings to the BOM to write. +BOM_SET = { + 'utf_8': BOM_UTF8, + 'utf_16': BOM_UTF16, + 'utf16_be': BOM_UTF16_BE, + 'utf16_le': BOM_UTF16_LE, + None: BOM_UTF8 + } + + +def match_utf8(encoding): + return BOM_LIST.get(encoding.lower()) == 'utf_8' + + +# Quote strings used for writing values +squot = "'%s'" +dquot = '"%s"' +noquot = "%s" +wspace_plus = ' \r\n\v\t\'"' +tsquot = '"""%s"""' +tdquot = "'''%s'''" + +# Sentinel for use in getattr calls to replace hasattr +MISSING = object() + +__all__ = ( + 'DEFAULT_INDENT_TYPE', + 'DEFAULT_INTERPOLATION', + 'ConfigObjError', + 'NestingError', + 'ParseError', + 'DuplicateError', + 'ConfigspecError', + 'ConfigObj', + 'SimpleVal', + 'InterpolationError', + 'InterpolationLoopError', + 'MissingInterpolationOption', + 'RepeatSectionError', + 'ReloadError', + 'UnreprError', + 'UnknownType', + 'flatten_errors', + 'get_extra_values' +) + +DEFAULT_INTERPOLATION = 'configparser' +DEFAULT_INDENT_TYPE = ' ' +MAX_INTERPOL_DEPTH = 10 + +OPTION_DEFAULTS = { + 'interpolation': True, + 'raise_errors': False, + 'list_values': True, + 'create_empty': False, + 'file_error': False, + 'configspec': None, + 'stringify': True, + # option may be set to one of ('', ' ', '\t') + 'indent_type': None, + 'encoding': None, + 'default_encoding': None, + 'unrepr': False, + 'write_empty_values': False, +} + +# this could be replaced if six is used for compatibility, or there are no +# more assertions about items being a string + + +def getObj(s): + global compiler + if compiler is None: + import compiler + s = "a=" + s + p = compiler.parse(s) + return p.getChildren()[1].getChildren()[0].getChildren()[1] + + +class UnknownType(Exception): + pass + + +class Builder(object): + + def build(self, o): + if m is None: + raise UnknownType(o.__class__.__name__) + return m(o) + + def build_List(self, o): + return list(map(self.build, o.getChildren())) + + def build_Const(self, o): + return o.value + + def build_Dict(self, o): + d = {} + i = iter(map(self.build, o.getChildren())) + for el in i: + d[el] = next(i) + return d + + def build_Tuple(self, o): + return tuple(self.build_List(o)) + + def build_Name(self, o): + if o.name == 'None': + return None + if o.name == 'True': + return True + if o.name == 'False': + return False + + # An undefined Name + raise UnknownType('Undefined Name') + + def build_Add(self, o): + real, imag = list(map(self.build_Const, o.getChildren())) + try: + real = float(real) + except TypeError: + raise UnknownType('Add') + if not isinstance(imag, complex) or imag.real != 0.0: + raise UnknownType('Add') + return real+imag + + def build_Getattr(self, o): + parent = self.build(o.expr) + return getattr(parent, o.attrname) + + def build_UnarySub(self, o): + return -self.build_Const(o.getChildren()[0]) + + def build_UnaryAdd(self, o): + return self.build_Const(o.getChildren()[0]) + + +_builder = Builder() + + +def unrepr(s): + if not s: + return s + + # this is supposed to be safe + import ast + return ast.literal_eval(s) + + +class ConfigObjError(SyntaxError): + """ + This is the base class for all errors that ConfigObj raises. + It is a subclass of SyntaxError. + """ + def __init__(self, message='', line_number=None, line=''): + self.line = line + self.line_number = line_number + SyntaxError.__init__(self, message) + + +class NestingError(ConfigObjError): + """ + This error indicates a level of nesting that doesn't match. + """ + + +class ParseError(ConfigObjError): + """ + This error indicates that a line is badly written. + It is neither a valid ``key = value`` line, + nor a valid section marker line. + """ + + +class ReloadError(IOError): + """ + A 'reload' operation failed. + This exception is a subclass of ``IOError``. + """ + def __init__(self): + IOError.__init__(self, 'reload failed, filename is not set.') + + +class DuplicateError(ConfigObjError): + """ + The keyword or section specified already exists. + """ + + +class ConfigspecError(ConfigObjError): + """ + An error occured whilst parsing a configspec. + """ + + +class InterpolationError(ConfigObjError): + """Base class for the two interpolation errors.""" + + +class InterpolationLoopError(InterpolationError): + """Maximum interpolation depth exceeded in string interpolation.""" + + def __init__(self, option): + InterpolationError.__init__( + self, + 'interpolation loop detected in value "%s".' % option) + + +class RepeatSectionError(ConfigObjError): + """ + This error indicates additional sections in a section with a + ``__many__`` (repeated) section. + """ + + +class MissingInterpolationOption(InterpolationError): + """A value specified for interpolation was missing.""" + def __init__(self, option): + msg = 'missing option "%s" in interpolation.' % option + InterpolationError.__init__(self, msg) + + +class UnreprError(ConfigObjError): + """An error parsing in unrepr mode.""" + + + +class InterpolationEngine(object): + """ + A helper class to help perform string interpolation. + + This class is an abstract base class; its descendants perform + the actual work. + """ + + # compiled regexp to use in self.interpolate() + _KEYCRE = re.compile(r"%\(([^)]*)\)s") + _cookie = '%' + + def __init__(self, section): + # the Section instance that "owns" this engine + self.section = section + + + def interpolate(self, key, value): + # short-cut + if not self._cookie in value: + return value + + def recursive_interpolate(key, value, section, backtrail): + """The function that does the actual work. + + ``value``: the string we're trying to interpolate. + ``section``: the section in which that string was found + ``backtrail``: a dict to keep track of where we've been, + to detect and prevent infinite recursion loops + + This is similar to a depth-first-search algorithm. + """ + # Have we been here already? + if (key, section.name) in backtrail: + # Yes - infinite loop detected + raise InterpolationLoopError(key) + # Place a marker on our backtrail so we won't come back here again + backtrail[(key, section.name)] = 1 + + # Now start the actual work + match = self._KEYCRE.search(value) + while match: + # The actual parsing of the match is implementation-dependent, + # so delegate to our helper function + k, v, s = self._parse_match(match) + if k is None: + # That's the signal that no further interpolation is needed + replacement = v + else: + # Further interpolation may be needed to obtain final value + replacement = recursive_interpolate(k, v, s, backtrail) + # Replace the matched string with its final value + start, end = match.span() + value = ''.join((value[:start], replacement, value[end:])) + new_search_start = start + len(replacement) + # Pick up the next interpolation key, if any, for next time + # through the while loop + match = self._KEYCRE.search(value, new_search_start) + + # Now safe to come back here again; remove marker from backtrail + del backtrail[(key, section.name)] + + return value + + # Back in interpolate(), all we have to do is kick off the recursive + # function with appropriate starting values + value = recursive_interpolate(key, value, self.section, {}) + return value + + + def _fetch(self, key): + """Helper function to fetch values from owning section. + + Returns a 2-tuple: the value, and the section where it was found. + """ + # switch off interpolation before we try and fetch anything ! + save_interp = self.section.main.interpolation + self.section.main.interpolation = False + + # Start at section that "owns" this InterpolationEngine + current_section = self.section + while True: + # try the current section first + val = current_section.get(key) + if val is not None and not isinstance(val, Section): + break + # try "DEFAULT" next + val = current_section.get('DEFAULT', {}).get(key) + if val is not None and not isinstance(val, Section): + break + # move up to parent and try again + # top-level's parent is itself + if current_section.parent is current_section: + # reached top level, time to give up + break + current_section = current_section.parent + + # restore interpolation to previous value before returning + self.section.main.interpolation = save_interp + if val is None: + raise MissingInterpolationOption(key) + return val, current_section + + + def _parse_match(self, match): + """Implementation-dependent helper function. + + Will be passed a match object corresponding to the interpolation + key we just found (e.g., "%(foo)s" or "$foo"). Should look up that + key in the appropriate config file section (using the ``_fetch()`` + helper function) and return a 3-tuple: (key, value, section) + + ``key`` is the name of the key we're looking for + ``value`` is the value found for that key + ``section`` is a reference to the section where it was found + + ``key`` and ``section`` should be None if no further + interpolation should be performed on the resulting value + (e.g., if we interpolated "$$" and returned "$"). + """ + raise NotImplementedError() + + + +class ConfigParserInterpolation(InterpolationEngine): + """Behaves like ConfigParser.""" + _cookie = '%' + _KEYCRE = re.compile(r"%\(([^)]*)\)s") + + def _parse_match(self, match): + key = match.group(1) + value, section = self._fetch(key) + return key, value, section + + + +class TemplateInterpolation(InterpolationEngine): + """Behaves like string.Template.""" + _cookie = '$' + _delimiter = '$' + _KEYCRE = re.compile(r""" + \$(?: + (?P\$) | # Two $ signs + (?P[_a-z][_a-z0-9]*) | # $name format + {(?P[^}]*)} # ${name} format + ) + """, re.IGNORECASE | re.VERBOSE) + + def _parse_match(self, match): + # Valid name (in or out of braces): fetch value from section + key = match.group('named') or match.group('braced') + if key is not None: + value, section = self._fetch(key) + return key, value, section + # Escaped delimiter (e.g., $$): return single delimiter + if match.group('escaped') is not None: + # Return None for key and section to indicate it's time to stop + return None, self._delimiter, None + # Anything else: ignore completely, just return it unchanged + return None, match.group(), None + + +interpolation_engines = { + 'configparser': ConfigParserInterpolation, + 'template': TemplateInterpolation, +} + + +def __newobj__(cls, *args): + # Hack for pickle + return cls.__new__(cls, *args) + +class Section(dict): + """ + A dictionary-like object that represents a section in a config file. + + It does string interpolation if the 'interpolation' attribute + of the 'main' object is set to True. + + Interpolation is tried first from this object, then from the 'DEFAULT' + section of this object, next from the parent and its 'DEFAULT' section, + and so on until the main object is reached. + + A Section will behave like an ordered dictionary - following the + order of the ``scalars`` and ``sections`` attributes. + You can use this to change the order of members. + + Iteration follows the order: scalars, then sections. + """ + + + def __setstate__(self, state): + dict.update(self, state[0]) + self.__dict__.update(state[1]) + + def __reduce__(self): + state = (dict(self), self.__dict__) + return (__newobj__, (self.__class__,), state) + + + def __init__(self, parent, depth, main, indict=None, name=None): + """ + * parent is the section above + * depth is the depth level of this section + * main is the main ConfigObj + * indict is a dictionary to initialise the section with + """ + if indict is None: + indict = {} + dict.__init__(self) + # used for nesting level *and* interpolation + self.parent = parent + # used for the interpolation attribute + self.main = main + # level of nesting depth of this Section + self.depth = depth + # purely for information + self.name = name + # + self._initialise() + # we do this explicitly so that __setitem__ is used properly + # (rather than just passing to ``dict.__init__``) + for entry, value in indict.items(): + self[entry] = value + + + def _initialise(self): + # the sequence of scalar values in this Section + self.scalars = [] + # the sequence of sections in this Section + self.sections = [] + # for comments :-) + self.comments = {} + self.inline_comments = {} + # the configspec + self.configspec = None + # for defaults + self.defaults = [] + self.default_values = {} + self.extra_values = [] + self._created = False + + + def _interpolate(self, key, value): + try: + # do we already have an interpolation engine? + engine = self._interpolation_engine + except AttributeError: + # not yet: first time running _interpolate(), so pick the engine + name = self.main.interpolation + if name == True: # note that "if name:" would be incorrect here + # backwards-compatibility: interpolation=True means use default + name = DEFAULT_INTERPOLATION + name = name.lower() # so that "Template", "template", etc. all work + class_ = interpolation_engines.get(name, None) + if class_ is None: + # invalid value for self.main.interpolation + self.main.interpolation = False + return value + else: + # save reference to engine so we don't have to do this again + engine = self._interpolation_engine = class_(self) + # let the engine do the actual work + return engine.interpolate(key, value) + + + def __getitem__(self, key): + """Fetch the item and do string interpolation.""" + val = dict.__getitem__(self, key) + if self.main.interpolation: + if isinstance(val, str): + return self._interpolate(key, val) + if isinstance(val, list): + def _check(entry): + if isinstance(entry, str): + return self._interpolate(key, entry) + return entry + new = [_check(entry) for entry in val] + if new != val: + return new + return val + + + def __setitem__(self, key, value, unrepr=False): + """ + Correctly set a value. + + Making dictionary values Section instances. + (We have to special case 'Section' instances - which are also dicts) + + Keys must be strings. + Values need only be strings (or lists of strings) if + ``main.stringify`` is set. + + ``unrepr`` must be set when setting a value to a dictionary, without + creating a new sub-section. + """ + if not isinstance(key, str): + raise ValueError('The key "%s" is not a string.' % key) + + # add the comment + if key not in self.comments: + self.comments[key] = [] + self.inline_comments[key] = '' + # remove the entry from defaults + if key in self.defaults: + self.defaults.remove(key) + # + if isinstance(value, Section): + if key not in self: + self.sections.append(key) + dict.__setitem__(self, key, value) + elif isinstance(value, Mapping) and not unrepr: + # First create the new depth level, + # then create the section + if key not in self: + self.sections.append(key) + new_depth = self.depth + 1 + dict.__setitem__( + self, + key, + Section( + self, + new_depth, + self.main, + indict=value, + name=key)) + else: + if key not in self: + self.scalars.append(key) + if not self.main.stringify: + if isinstance(value, str): + pass + elif isinstance(value, (list, tuple)): + for entry in value: + if not isinstance(entry, str): + raise TypeError('Value is not a string "%s".' % entry) + else: + raise TypeError('Value is not a string "%s".' % value) + dict.__setitem__(self, key, value) + + + def __delitem__(self, key): + """Remove items from the sequence when deleting.""" + dict. __delitem__(self, key) + if key in self.scalars: + self.scalars.remove(key) + else: + self.sections.remove(key) + del self.comments[key] + del self.inline_comments[key] + + + def get(self, key, default=None): + """A version of ``get`` that doesn't bypass string interpolation.""" + try: + return self[key] + except KeyError: + return default + + + def update(self, indict): + """ + A version of update that uses our ``__setitem__``. + """ + for entry in indict: + self[entry] = indict[entry] + + + def pop(self, key, default=MISSING): + """ + 'D.pop(k[,d]) -> v, remove specified key and return the corresponding value. + If key is not found, d is returned if given, otherwise KeyError is raised' + """ + try: + val = self[key] + except KeyError: + if default is MISSING: + raise + val = default + else: + del self[key] + return val + + + def popitem(self): + """Pops the first (key,val)""" + sequence = (self.scalars + self.sections) + if not sequence: + raise KeyError(": 'popitem(): dictionary is empty'") + key = sequence[0] + val = self[key] + del self[key] + return key, val + + + def clear(self): + """ + A version of clear that also affects scalars/sections + Also clears comments and configspec. + + Leaves other attributes alone : + depth/main/parent are not affected + """ + dict.clear(self) + self.scalars = [] + self.sections = [] + self.comments = {} + self.inline_comments = {} + self.configspec = None + self.defaults = [] + self.extra_values = [] + + + def setdefault(self, key, default=None): + """A version of setdefault that sets sequence if appropriate.""" + try: + return self[key] + except KeyError: + self[key] = default + return self[key] + + + def items(self): + """D.items() -> list of D's (key, value) pairs, as 2-tuples""" + return list(zip((self.scalars + self.sections), list(self.values()))) + + + def keys(self): + """D.keys() -> list of D's keys""" + return (self.scalars + self.sections) + + + def values(self): + """D.values() -> list of D's values""" + return [self[key] for key in (self.scalars + self.sections)] + + + def iteritems(self): + """D.iteritems() -> an iterator over the (key, value) items of D""" + return iter(list(self.items())) + + + def iterkeys(self): + """D.iterkeys() -> an iterator over the keys of D""" + return iter((self.scalars + self.sections)) + + __iter__ = iterkeys + + + def itervalues(self): + """D.itervalues() -> an iterator over the values of D""" + return iter(list(self.values())) + + + def __repr__(self): + """x.__repr__() <==> repr(x)""" + def _getval(key): + try: + return self[key] + except MissingInterpolationOption: + return dict.__getitem__(self, key) + return '{%s}' % ', '.join([('%s: %s' % (repr(key), repr(_getval(key)))) + for key in (self.scalars + self.sections)]) + + __str__ = __repr__ + __str__.__doc__ = "x.__str__() <==> str(x)" + + + # Extra methods - not in a normal dictionary + + def dict(self): + """ + Return a deepcopy of self as a dictionary. + + All members that are ``Section`` instances are recursively turned to + ordinary dictionaries - by calling their ``dict`` method. + + >>> n = a.dict() + >>> n == a + 1 + >>> n is a + 0 + """ + newdict = {} + for entry in self: + this_entry = self[entry] + if isinstance(this_entry, Section): + this_entry = this_entry.dict() + elif isinstance(this_entry, list): + # create a copy rather than a reference + this_entry = list(this_entry) + elif isinstance(this_entry, tuple): + # create a copy rather than a reference + this_entry = tuple(this_entry) + newdict[entry] = this_entry + return newdict + + + def merge(self, indict): + """ + A recursive update - useful for merging config files. + + >>> a = '''[section1] + ... option1 = True + ... [[subsection]] + ... more_options = False + ... # end of file'''.splitlines() + >>> b = '''# File is user.ini + ... [section1] + ... option1 = False + ... # end of file'''.splitlines() + >>> c1 = ConfigObj(b) + >>> c2 = ConfigObj(a) + >>> c2.merge(c1) + >>> c2 + ConfigObj({'section1': {'option1': 'False', 'subsection': {'more_options': 'False'}}}) + """ + for key, val in list(indict.items()): + if (key in self and isinstance(self[key], Mapping) and + isinstance(val, Mapping)): + self[key].merge(val) + else: + self[key] = val + + + def rename(self, oldkey, newkey): + """ + Change a keyname to another, without changing position in sequence. + + Implemented so that transformations can be made on keys, + as well as on values. (used by encode and decode) + + Also renames comments. + """ + if oldkey in self.scalars: + the_list = self.scalars + elif oldkey in self.sections: + the_list = self.sections + else: + raise KeyError('Key "%s" not found.' % oldkey) + pos = the_list.index(oldkey) + # + val = self[oldkey] + dict.__delitem__(self, oldkey) + dict.__setitem__(self, newkey, val) + the_list.remove(oldkey) + the_list.insert(pos, newkey) + comm = self.comments[oldkey] + inline_comment = self.inline_comments[oldkey] + del self.comments[oldkey] + del self.inline_comments[oldkey] + self.comments[newkey] = comm + self.inline_comments[newkey] = inline_comment + + + def walk(self, function, raise_errors=True, + call_on_sections=False, **keywargs): + """ + Walk every member and call a function on the keyword and value. + + Return a dictionary of the return values + + If the function raises an exception, raise the errror + unless ``raise_errors=False``, in which case set the return value to + ``False``. + + Any unrecognized keyword arguments you pass to walk, will be pased on + to the function you pass in. + + Note: if ``call_on_sections`` is ``True`` then - on encountering a + subsection, *first* the function is called for the *whole* subsection, + and then recurses into it's members. This means your function must be + able to handle strings, dictionaries and lists. This allows you + to change the key of subsections as well as for ordinary members. The + return value when called on the whole subsection has to be discarded. + + See the encode and decode methods for examples, including functions. + + .. admonition:: caution + + You can use ``walk`` to transform the names of members of a section + but you mustn't add or delete members. + + >>> config = '''[XXXXsection] + ... XXXXkey = XXXXvalue'''.splitlines() + >>> cfg = ConfigObj(config) + >>> cfg + ConfigObj({'XXXXsection': {'XXXXkey': 'XXXXvalue'}}) + >>> def transform(section, key): + ... val = section[key] + ... newkey = key.replace('XXXX', 'CLIENT1') + ... section.rename(key, newkey) + ... if isinstance(val, (tuple, list, dict)): + ... pass + ... else: + ... val = val.replace('XXXX', 'CLIENT1') + ... section[newkey] = val + >>> cfg.walk(transform, call_on_sections=True) + {'CLIENT1section': {'CLIENT1key': None}} + >>> cfg + ConfigObj({'CLIENT1section': {'CLIENT1key': 'CLIENT1value'}}) + """ + out = {} + # scalars first + for i in range(len(self.scalars)): + entry = self.scalars[i] + try: + val = function(self, entry, **keywargs) + # bound again in case name has changed + entry = self.scalars[i] + out[entry] = val + except Exception: + if raise_errors: + raise + else: + entry = self.scalars[i] + out[entry] = False + # then sections + for i in range(len(self.sections)): + entry = self.sections[i] + if call_on_sections: + try: + function(self, entry, **keywargs) + except Exception: + if raise_errors: + raise + else: + entry = self.sections[i] + out[entry] = False + # bound again in case name has changed + entry = self.sections[i] + # previous result is discarded + out[entry] = self[entry].walk( + function, + raise_errors=raise_errors, + call_on_sections=call_on_sections, + **keywargs) + return out + + + def as_bool(self, key): + """ + Accepts a key as input. The corresponding value must be a string or + the objects (``True`` or 1) or (``False`` or 0). We allow 0 and 1 to + retain compatibility with Python 2.2. + + If the string is one of ``True``, ``On``, ``Yes``, or ``1`` it returns + ``True``. + + If the string is one of ``False``, ``Off``, ``No``, or ``0`` it returns + ``False``. + + ``as_bool`` is not case sensitive. + + Any other input will raise a ``ValueError``. + + >>> a = ConfigObj() + >>> a['a'] = 'fish' + >>> a.as_bool('a') + Traceback (most recent call last): + ValueError: Value "fish" is neither True nor False + >>> a['b'] = 'True' + >>> a.as_bool('b') + 1 + >>> a['b'] = 'off' + >>> a.as_bool('b') + 0 + """ + val = self[key] + if val == True: + return True + elif val == False: + return False + else: + try: + if not isinstance(val, str): + # TODO: Why do we raise a KeyError here? + raise KeyError() + else: + return self.main._bools[val.lower()] + except KeyError: + raise ValueError('Value "%s" is neither True nor False' % val) + + + def as_int(self, key): + """ + A convenience method which coerces the specified value to an integer. + + If the value is an invalid literal for ``int``, a ``ValueError`` will + be raised. + + >>> a = ConfigObj() + >>> a['a'] = 'fish' + >>> a.as_int('a') + Traceback (most recent call last): + ValueError: invalid literal for int() with base 10: 'fish' + >>> a['b'] = '1' + >>> a.as_int('b') + 1 + >>> a['b'] = '3.2' + >>> a.as_int('b') + Traceback (most recent call last): + ValueError: invalid literal for int() with base 10: '3.2' + """ + return int(self[key]) + + + def as_float(self, key): + """ + A convenience method which coerces the specified value to a float. + + If the value is an invalid literal for ``float``, a ``ValueError`` will + be raised. + + >>> a = ConfigObj() + >>> a['a'] = 'fish' + >>> a.as_float('a') #doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ValueError: invalid literal for float(): fish + >>> a['b'] = '1' + >>> a.as_float('b') + 1.0 + >>> a['b'] = '3.2' + >>> a.as_float('b') #doctest: +ELLIPSIS + 3.2... + """ + return float(self[key]) + + + def as_list(self, key): + """ + A convenience method which fetches the specified value, guaranteeing + that it is a list. + + >>> a = ConfigObj() + >>> a['a'] = 1 + >>> a.as_list('a') + [1] + >>> a['a'] = (1,) + >>> a.as_list('a') + [1] + >>> a['a'] = [1] + >>> a.as_list('a') + [1] + """ + result = self[key] + if isinstance(result, (tuple, list)): + return list(result) + return [result] + + + def restore_default(self, key): + """ + Restore (and return) default value for the specified key. + + This method will only work for a ConfigObj that was created + with a configspec and has been validated. + + If there is no default value for this key, ``KeyError`` is raised. + """ + default = self.default_values[key] + dict.__setitem__(self, key, default) + if key not in self.defaults: + self.defaults.append(key) + return default + + + def restore_defaults(self): + """ + Recursively restore default values to all members + that have them. + + This method will only work for a ConfigObj that was created + with a configspec and has been validated. + + It doesn't delete or modify entries without default values. + """ + for key in self.default_values: + self.restore_default(key) + + for section in self.sections: + self[section].restore_defaults() + + +class ConfigObj(Section): + """An object to read, create, and write config files.""" + + _keyword = re.compile(r'''^ # line start + (\s*) # indentation + ( # keyword + (?:".*?")| # double quotes + (?:'.*?')| # single quotes + (?:[^'"=].*?) # no quotes + ) + \s*=\s* # divider + (.*) # value (including list values and comments) + $ # line end + ''', + re.VERBOSE) + + _sectionmarker = re.compile(r'''^ + (\s*) # 1: indentation + ((?:\[\s*)+) # 2: section marker open + ( # 3: section name open + (?:"\s*\S.*?\s*")| # at least one non-space with double quotes + (?:'\s*\S.*?\s*')| # at least one non-space with single quotes + (?:[^'"\s].*?) # at least one non-space unquoted + ) # section name close + ((?:\s*\])+) # 4: section marker close + \s*(\#.*)? # 5: optional comment + $''', + re.VERBOSE) + + # this regexp pulls list values out as a single string + # or single values and comments + # FIXME: this regex adds a '' to the end of comma terminated lists + # workaround in ``_handle_value`` + _valueexp = re.compile(r'''^ + (?: + (?: + ( + (?: + (?: + (?:".*?")| # double quotes + (?:'.*?')| # single quotes + (?:[^'",\#][^,\#]*?) # unquoted + ) + \s*,\s* # comma + )* # match all list items ending in a comma (if any) + ) + ( + (?:".*?")| # double quotes + (?:'.*?')| # single quotes + (?:[^'",\#\s][^,]*?)| # unquoted + (?:(? 1: + msg = "Parsing failed with several errors.\nFirst error %s" % info + error = ConfigObjError(msg) + else: + error = self._errors[0] + # set the errors attribute; it's a list of tuples: + # (error_type, message, line_number) + error.errors = self._errors + # set the config attribute + error.config = self + raise error + # delete private attributes + del self._errors + + if configspec is None: + self.configspec = None + else: + self._handle_configspec(configspec) + + + def _initialise(self, options=None): + if options is None: + options = OPTION_DEFAULTS + + # initialise a few variables + self.filename = None + self._errors = [] + self.raise_errors = options['raise_errors'] + self.interpolation = options['interpolation'] + self.list_values = options['list_values'] + self.create_empty = options['create_empty'] + self.file_error = options['file_error'] + self.stringify = options['stringify'] + self.indent_type = options['indent_type'] + self.encoding = options['encoding'] + self.default_encoding = options['default_encoding'] + self.BOM = False + self.newlines = None + self.write_empty_values = options['write_empty_values'] + self.unrepr = options['unrepr'] + + self.initial_comment = [] + self.final_comment = [] + self.configspec = None + + if self._inspec: + self.list_values = False + + # Clear section attributes as well + Section._initialise(self) + + + def __repr__(self): + def _getval(key): + try: + return self[key] + except MissingInterpolationOption: + return dict.__getitem__(self, key) + return ('%s({%s})' % (self.__class__.__name__, + ', '.join([('%s: %s' % (repr(key), repr(_getval(key)))) + for key in (self.scalars + self.sections)]))) + + + def _handle_bom(self, infile): + """ + Handle any BOM, and decode if necessary. + + If an encoding is specified, that *must* be used - but the BOM should + still be removed (and the BOM attribute set). + + (If the encoding is wrongly specified, then a BOM for an alternative + encoding won't be discovered or removed.) + + If an encoding is not specified, UTF8 or UTF16 BOM will be detected and + removed. The BOM attribute will be set. UTF16 will be decoded to + unicode. + + NOTE: This method must not be called with an empty ``infile``. + + Specifying the *wrong* encoding is likely to cause a + ``UnicodeDecodeError``. + + ``infile`` must always be returned as a list of lines, but may be + passed in as a single string. + """ + + if ((self.encoding is not None) and + (self.encoding.lower() not in BOM_LIST)): + # No need to check for a BOM + # the encoding specified doesn't have one + # just decode + return self._decode(infile, self.encoding) + + if isinstance(infile, (list, tuple)): + line = infile[0] + else: + line = infile + + if isinstance(line, str): + # it's already decoded and there's no need to do anything + # else, just use the _decode utility method to handle + # listifying appropriately + return self._decode(infile, self.encoding) + + if self.encoding is not None: + # encoding explicitly supplied + # And it could have an associated BOM + # TODO: if encoding is just UTF16 - we ought to check for both + # TODO: big endian and little endian versions. + enc = BOM_LIST[self.encoding.lower()] + if enc == 'utf_16': + # For UTF16 we try big endian and little endian + for BOM, (encoding, final_encoding) in list(BOMS.items()): + if not final_encoding: + # skip UTF8 + continue + if infile.startswith(BOM): + ### BOM discovered + ##self.BOM = True + # Don't need to remove BOM + return self._decode(infile, encoding) + + # If we get this far, will *probably* raise a DecodeError + # As it doesn't appear to start with a BOM + return self._decode(infile, self.encoding) + + # Must be UTF8 + BOM = BOM_SET[enc] + if not line.startswith(BOM): + return self._decode(infile, self.encoding) + + newline = line[len(BOM):] + + # BOM removed + if isinstance(infile, (list, tuple)): + infile[0] = newline + else: + infile = newline + self.BOM = True + return self._decode(infile, self.encoding) + + # No encoding specified - so we need to check for UTF8/UTF16 + for BOM, (encoding, final_encoding) in list(BOMS.items()): + if not isinstance(line, bytes) or not line.startswith(BOM): + # didn't specify a BOM, or it's not a bytestring + continue + else: + # BOM discovered + self.encoding = final_encoding + if not final_encoding: + self.BOM = True + # UTF8 + # remove BOM + newline = line[len(BOM):] + if isinstance(infile, (list, tuple)): + infile[0] = newline + else: + infile = newline + # UTF-8 + if isinstance(infile, str): + return infile.splitlines(True) + elif isinstance(infile, bytes): + return infile.decode('utf-8').splitlines(True) + else: + return self._decode(infile, 'utf-8') + # UTF16 - have to decode + return self._decode(infile, encoding) + + # No BOM discovered and no encoding specified, default to UTF-8 + if isinstance(infile, bytes): + return infile.decode('utf-8').splitlines(True) + else: + return self._decode(infile, 'utf-8') + + + def _a_to_u(self, aString): + """Decode ASCII strings to unicode if a self.encoding is specified.""" + if isinstance(aString, bytes) and self.encoding: + return aString.decode(self.encoding) + else: + return aString + + + def _decode(self, infile, encoding): + """ + Decode infile to unicode. Using the specified encoding. + + if is a string, it also needs converting to a list. + """ + if isinstance(infile, str): + return infile.splitlines(True) + if isinstance(infile, bytes): + # NOTE: Could raise a ``UnicodeDecodeError`` + if encoding: + return infile.decode(encoding).splitlines(True) + else: + return infile.splitlines(True) + + if encoding: + for i, line in enumerate(infile): + if isinstance(line, bytes): + # NOTE: The isinstance test here handles mixed lists of unicode/string + # NOTE: But the decode will break on any non-string values + # NOTE: Or could raise a ``UnicodeDecodeError`` + infile[i] = line.decode(encoding) + return infile + + + def _decode_element(self, line): + """Decode element to unicode if necessary.""" + if isinstance(line, bytes) and self.default_encoding: + return line.decode(self.default_encoding) + else: + return line + + + # TODO: this may need to be modified + def _str(self, value): + """ + Used by ``stringify`` within validate, to turn non-string values + into strings. + """ + if not isinstance(value, str): + return str(value) + else: + return value + + + def _parse(self, infile): + """Actually parse the config file.""" + temp_list_values = self.list_values + if self.unrepr: + self.list_values = False + + comment_list = [] + done_start = False + this_section = self + maxline = len(infile) - 1 + cur_index = -1 + reset_comment = False + + while cur_index < maxline: + if reset_comment: + comment_list = [] + cur_index += 1 + line = infile[cur_index] + sline = line.strip() + # do we have anything on the line ? + if not sline or sline.startswith('#'): + reset_comment = False + comment_list.append(line) + continue + + if not done_start: + # preserve initial comment + self.initial_comment = comment_list + comment_list = [] + done_start = True + + reset_comment = True + # first we check if it's a section marker + mat = self._sectionmarker.match(line) + if mat is not None: + # is a section line + (indent, sect_open, sect_name, sect_close, comment) = mat.groups() + if indent and (self.indent_type is None): + self.indent_type = indent + cur_depth = sect_open.count('[') + if cur_depth != sect_close.count(']'): + self._handle_error("Cannot compute the section depth", + NestingError, infile, cur_index) + continue + + if cur_depth < this_section.depth: + # the new section is dropping back to a previous level + try: + parent = self._match_depth(this_section, + cur_depth).parent + except SyntaxError: + self._handle_error("Cannot compute nesting level", + NestingError, infile, cur_index) + continue + elif cur_depth == this_section.depth: + # the new section is a sibling of the current section + parent = this_section.parent + elif cur_depth == this_section.depth + 1: + # the new section is a child the current section + parent = this_section + else: + self._handle_error("Section too nested", + NestingError, infile, cur_index) + continue + + sect_name = self._unquote(sect_name) + if sect_name in parent: + self._handle_error('Duplicate section name', + DuplicateError, infile, cur_index) + continue + + # create the new section + this_section = Section( + parent, + cur_depth, + self, + name=sect_name) + parent[sect_name] = this_section + parent.inline_comments[sect_name] = comment + parent.comments[sect_name] = comment_list + continue + # + # it's not a section marker, + # so it should be a valid ``key = value`` line + mat = self._keyword.match(line) + if mat is None: + self._handle_error( + 'Invalid line ({0!r}) (matched as neither section nor keyword)'.format(line), + ParseError, infile, cur_index) + else: + # is a keyword value + # value will include any inline comment + (indent, key, value) = mat.groups() + if indent and (self.indent_type is None): + self.indent_type = indent + # check for a multiline value + if value[:3] in ['"""', "'''"]: + try: + value, comment, cur_index = self._multiline( + value, infile, cur_index, maxline) + except SyntaxError: + self._handle_error( + 'Parse error in multiline value', + ParseError, infile, cur_index) + continue + else: + if self.unrepr: + comment = '' + try: + value = unrepr(value) + except Exception as e: + if type(e) == UnknownType: + msg = 'Unknown name or type in value' + else: + msg = 'Parse error from unrepr-ing multiline value' + self._handle_error(msg, UnreprError, infile, + cur_index) + continue + else: + if self.unrepr: + comment = '' + try: + value = unrepr(value) + except Exception as e: + if isinstance(e, UnknownType): + msg = 'Unknown name or type in value' + else: + msg = 'Parse error from unrepr-ing value' + self._handle_error(msg, UnreprError, infile, + cur_index) + continue + else: + # extract comment and lists + try: + (value, comment) = self._handle_value(value) + except SyntaxError: + self._handle_error( + 'Parse error in value', + ParseError, infile, cur_index) + continue + # + key = self._unquote(key) + if key in this_section: + self._handle_error( + 'Duplicate keyword name', + DuplicateError, infile, cur_index) + continue + # add the key. + # we set unrepr because if we have got this far we will never + # be creating a new section + this_section.__setitem__(key, value, unrepr=True) + this_section.inline_comments[key] = comment + this_section.comments[key] = comment_list + continue + # + if self.indent_type is None: + # no indentation used, set the type accordingly + self.indent_type = '' + + # preserve the final comment + if not self and not self.initial_comment: + self.initial_comment = comment_list + elif not reset_comment: + self.final_comment = comment_list + self.list_values = temp_list_values + + + def _match_depth(self, sect, depth): + """ + Given a section and a depth level, walk back through the sections + parents to see if the depth level matches a previous section. + + Return a reference to the right section, + or raise a SyntaxError. + """ + while depth < sect.depth: + if sect is sect.parent: + # we've reached the top level already + raise SyntaxError() + sect = sect.parent + if sect.depth == depth: + return sect + # shouldn't get here + raise SyntaxError() + + + def _handle_error(self, text, ErrorClass, infile, cur_index): + """ + Handle an error according to the error settings. + + Either raise the error or store it. + The error will have occured at ``cur_index`` + """ + line = infile[cur_index] + cur_index += 1 + message = '{0} at line {1}.'.format(text, cur_index) + error = ErrorClass(message, cur_index, line) + if self.raise_errors: + # raise the error - parsing stops here + raise error + # store the error + # reraise when parsing has finished + self._errors.append(error) + + + def _unquote(self, value): + """Return an unquoted version of a value""" + if not value: + # should only happen during parsing of lists + raise SyntaxError + if (value[0] == value[-1]) and (value[0] in ('"', "'")): + value = value[1:-1] + return value + + + def _quote(self, value, multiline=True): + """ + Return a safely quoted version of a value. + + Raise a ConfigObjError if the value cannot be safely quoted. + If multiline is ``True`` (default) then use triple quotes + if necessary. + + * Don't quote values that don't need it. + * Recursively quote members of a list and return a comma joined list. + * Multiline is ``False`` for lists. + * Obey list syntax for empty and single member lists. + + If ``list_values=False`` then the value is only quoted if it contains + a ``\\n`` (is multiline) or '#'. + + If ``write_empty_values`` is set, and the value is an empty string, it + won't be quoted. + """ + if multiline and self.write_empty_values and value == '': + # Only if multiline is set, so that it is used for values not + # keys, and not values that are part of a list + return '' + + if multiline and isinstance(value, (list, tuple)): + if not value: + return ',' + elif len(value) == 1: + return self._quote(value[0], multiline=False) + ',' + return ', '.join([self._quote(val, multiline=False) + for val in value]) + if not isinstance(value, str): + if self.stringify: + value = str(value) + else: + raise TypeError('Value "%s" is not a string.' % value) + + if not value: + return '""' + + no_lists_no_quotes = not self.list_values and '\n' not in value and '#' not in value + need_triple = multiline and ((("'" in value) and ('"' in value)) or ('\n' in value )) + hash_triple_quote = multiline and not need_triple and ("'" in value) and ('"' in value) and ('#' in value) + check_for_single = (no_lists_no_quotes or not need_triple) and not hash_triple_quote + + if check_for_single: + if not self.list_values: + # we don't quote if ``list_values=False`` + quot = noquot + # for normal values either single or double quotes will do + elif '\n' in value: + # will only happen if multiline is off - e.g. '\n' in key + raise ConfigObjError('Value "%s" cannot be safely quoted.' % value) + elif ((value[0] not in wspace_plus) and + (value[-1] not in wspace_plus) and + (',' not in value)): + quot = noquot + else: + quot = self._get_single_quote(value) + else: + # if value has '\n' or "'" *and* '"', it will need triple quotes + quot = self._get_triple_quote(value) + + if quot == noquot and '#' in value and self.list_values: + quot = self._get_single_quote(value) + + return quot % value + + + def _get_single_quote(self, value): + if ("'" in value) and ('"' in value): + raise ConfigObjError('Value "%s" cannot be safely quoted.' % value) + elif '"' in value: + quot = squot + else: + quot = dquot + return quot + + + def _get_triple_quote(self, value): + if (value.find('"""') != -1) and (value.find("'''") != -1): + raise ConfigObjError('Value "%s" cannot be safely quoted.' % value) + if value.find('"""') == -1: + quot = tdquot + else: + quot = tsquot + return quot + + + def _handle_value(self, value): + """ + Given a value string, unquote, remove comment, + handle lists. (including empty and single member lists) + """ + if self._inspec: + # Parsing a configspec so don't handle comments + return (value, '') + # do we look for lists in values ? + if not self.list_values: + mat = self._nolistvalue.match(value) + if mat is None: + raise SyntaxError() + # NOTE: we don't unquote here + return mat.groups() + # + mat = self._valueexp.match(value) + if mat is None: + # the value is badly constructed, probably badly quoted, + # or an invalid list + raise SyntaxError() + (list_values, single, empty_list, comment) = mat.groups() + if (list_values == '') and (single is None): + # change this if you want to accept empty values + raise SyntaxError() + # NOTE: note there is no error handling from here if the regex + # is wrong: then incorrect values will slip through + if empty_list is not None: + # the single comma - meaning an empty list + return ([], comment) + if single is not None: + # handle empty values + if list_values and not single: + # FIXME: the '' is a workaround because our regex now matches + # '' at the end of a list if it has a trailing comma + single = None + else: + single = single or '""' + single = self._unquote(single) + if list_values == '': + # not a list value + return (single, comment) + the_list = self._listvalueexp.findall(list_values) + the_list = [self._unquote(val) for val in the_list] + if single is not None: + the_list += [single] + return (the_list, comment) + + + def _multiline(self, value, infile, cur_index, maxline): + """Extract the value, where we are in a multiline situation.""" + quot = value[:3] + newvalue = value[3:] + single_line = self._triple_quote[quot][0] + multi_line = self._triple_quote[quot][1] + mat = single_line.match(value) + if mat is not None: + retval = list(mat.groups()) + retval.append(cur_index) + return retval + elif newvalue.find(quot) != -1: + # somehow the triple quote is missing + raise SyntaxError() + # + while cur_index < maxline: + cur_index += 1 + newvalue += '\n' + line = infile[cur_index] + if line.find(quot) == -1: + newvalue += line + else: + # end of multiline, process it + break + else: + # we've got to the end of the config, oops... + raise SyntaxError() + mat = multi_line.match(line) + if mat is None: + # a badly formed line + raise SyntaxError() + (value, comment) = mat.groups() + return (newvalue + value, comment, cur_index) + + + def _handle_configspec(self, configspec): + """Parse the configspec.""" + # FIXME: Should we check that the configspec was created with the + # correct settings ? (i.e. ``list_values=False``) + if not isinstance(configspec, ConfigObj): + try: + configspec = ConfigObj(configspec, + raise_errors=True, + file_error=True, + _inspec=True) + except ConfigObjError as e: + # FIXME: Should these errors have a reference + # to the already parsed ConfigObj ? + raise ConfigspecError('Parsing configspec failed: %s' % e) + except IOError as e: + raise IOError('Reading configspec failed: %s' % e) + + self.configspec = configspec + + + + def _set_configspec(self, section, copy): + """ + Called by validate. Handles setting the configspec on subsections + including sections to be validated by __many__ + """ + configspec = section.configspec + many = configspec.get('__many__') + if isinstance(many, dict): + for entry in section.sections: + if entry not in configspec: + section[entry].configspec = many + + for entry in configspec.sections: + if entry == '__many__': + continue + if entry not in section: + section[entry] = {} + section[entry]._created = True + if copy: + # copy comments + section.comments[entry] = configspec.comments.get(entry, []) + section.inline_comments[entry] = configspec.inline_comments.get(entry, '') + + # Could be a scalar when we expect a section + if isinstance(section[entry], Section): + section[entry].configspec = configspec[entry] + + + def _write_line(self, indent_string, entry, this_entry, comment): + """Write an individual line, for the write method""" + # NOTE: the calls to self._quote here handles non-StringType values. + if not self.unrepr: + val = self._decode_element(self._quote(this_entry)) + else: + val = repr(this_entry) + return '%s%s%s%s%s' % (indent_string, + self._decode_element(self._quote(entry, multiline=False)), + self._a_to_u(' = '), + val, + self._decode_element(comment)) + + + def _write_marker(self, indent_string, depth, entry, comment): + """Write a section marker line""" + return '%s%s%s%s%s' % (indent_string, + self._a_to_u('[' * depth), + self._quote(self._decode_element(entry), multiline=False), + self._a_to_u(']' * depth), + self._decode_element(comment)) + + + def _handle_comment(self, comment): + """Deal with a comment.""" + if not comment: + return '' + start = self.indent_type + if not comment.startswith('#'): + start += self._a_to_u(' # ') + return (start + comment) + + + # Public methods + + def write(self, outfile=None, section=None): + """ + Write the current ConfigObj as a file + + tekNico: FIXME: use StringIO instead of real files + + >>> filename = a.filename + >>> a.filename = 'test.ini' + >>> a.write() + >>> a.filename = filename + >>> a == ConfigObj('test.ini', raise_errors=True) + 1 + >>> import os + >>> os.remove('test.ini') + """ + if self.indent_type is None: + # this can be true if initialised from a dictionary + self.indent_type = DEFAULT_INDENT_TYPE + + out = [] + cs = self._a_to_u('#') + csp = self._a_to_u('# ') + if section is None: + int_val = self.interpolation + self.interpolation = False + section = self + for line in self.initial_comment: + line = self._decode_element(line) + stripped_line = line.strip() + if stripped_line and not stripped_line.startswith(cs): + line = csp + line + out.append(line) + + indent_string = self.indent_type * section.depth + for entry in (section.scalars + section.sections): + if entry in section.defaults: + # don't write out default values + continue + for comment_line in section.comments[entry]: + comment_line = self._decode_element(comment_line.lstrip()) + if comment_line and not comment_line.startswith(cs): + comment_line = csp + comment_line + out.append(indent_string + comment_line) + this_entry = section[entry] + comment = self._handle_comment(section.inline_comments[entry]) + + if isinstance(this_entry, Section): + # a section + out.append(self._write_marker( + indent_string, + this_entry.depth, + entry, + comment)) + out.extend(self.write(section=this_entry)) + else: + out.append(self._write_line( + indent_string, + entry, + this_entry, + comment)) + + if section is self: + for line in self.final_comment: + line = self._decode_element(line) + stripped_line = line.strip() + if stripped_line and not stripped_line.startswith(cs): + line = csp + line + out.append(line) + self.interpolation = int_val + + if section is not self: + return out + + if (self.filename is None) and (outfile is None): + # output a list of lines + # might need to encode + # NOTE: This will *screw* UTF16, each line will start with the BOM + if self.encoding: + out = [l.encode(self.encoding) for l in out] + if (self.BOM and ((self.encoding is None) or + (BOM_LIST.get(self.encoding.lower()) == 'utf_8'))): + # Add the UTF8 BOM + if not out: + out.append('') + out[0] = BOM_UTF8 + out[0] + return out + + # Turn the list to a string, joined with correct newlines + newline = self.newlines or os.linesep + if (getattr(outfile, 'mode', None) is not None and outfile.mode == 'w' + and sys.platform == 'win32' and newline == '\r\n'): + # Windows specific hack to avoid writing '\r\r\n' + newline = '\n' + output = self._a_to_u(newline).join(out) + if not output.endswith(newline): + output += newline + + if isinstance(output, bytes): + output_bytes = output + else: + output_bytes = output.encode(self.encoding or + self.default_encoding or + 'ascii') + + if self.BOM and ((self.encoding is None) or match_utf8(self.encoding)): + # Add the UTF8 BOM + output_bytes = BOM_UTF8 + output_bytes + + if outfile is not None: + outfile.write(output_bytes) + else: + with open(self.filename, 'wb') as h: + h.write(output_bytes) + + def validate(self, validator, preserve_errors=False, copy=False, + section=None): + """ + Test the ConfigObj against a configspec. + + It uses the ``validator`` object from *validate.py*. + + To run ``validate`` on the current ConfigObj, call: :: + + test = config.validate(validator) + + (Normally having previously passed in the configspec when the ConfigObj + was created - you can dynamically assign a dictionary of checks to the + ``configspec`` attribute of a section though). + + It returns ``True`` if everything passes, or a dictionary of + pass/fails (True/False). If every member of a subsection passes, it + will just have the value ``True``. (It also returns ``False`` if all + members fail). + + In addition, it converts the values from strings to their native + types if their checks pass (and ``stringify`` is set). + + If ``preserve_errors`` is ``True`` (``False`` is default) then instead + of a marking a fail with a ``False``, it will preserve the actual + exception object. This can contain info about the reason for failure. + For example the ``VdtValueTooSmallError`` indicates that the value + supplied was too small. If a value (or section) is missing it will + still be marked as ``False``. + + You must have the validate module to use ``preserve_errors=True``. + + You can then use the ``flatten_errors`` function to turn your nested + results dictionary into a flattened list of failures - useful for + displaying meaningful error messages. + """ + if section is None: + if self.configspec is None: + raise ValueError('No configspec supplied.') + if preserve_errors: + # We do this once to remove a top level dependency on the validate module + # Which makes importing configobj faster + from .validate import VdtMissingValue + self._vdtMissingValue = VdtMissingValue + + section = self + + if copy: + section.initial_comment = section.configspec.initial_comment + section.final_comment = section.configspec.final_comment + section.encoding = section.configspec.encoding + section.BOM = section.configspec.BOM + section.newlines = section.configspec.newlines + section.indent_type = section.configspec.indent_type + + # + # section.default_values.clear() #?? + configspec = section.configspec + self._set_configspec(section, copy) + + + def validate_entry(entry, spec, val, missing, ret_true, ret_false): + section.default_values.pop(entry, None) + + try: + section.default_values[entry] = validator.get_default_value(configspec[entry]) + except (KeyError, AttributeError, validator.baseErrorClass): + # No default, bad default or validator has no 'get_default_value' + # (e.g. SimpleVal) + pass + + try: + check = validator.check(spec, + val, + missing=missing + ) + except validator.baseErrorClass as e: + if not preserve_errors or isinstance(e, self._vdtMissingValue): + out[entry] = False + else: + # preserve the error + out[entry] = e + ret_false = False + ret_true = False + else: + ret_false = False + out[entry] = True + if self.stringify or missing: + # if we are doing type conversion + # or the value is a supplied default + if not self.stringify: + if isinstance(check, (list, tuple)): + # preserve lists + check = [self._str(item) for item in check] + elif missing and check is None: + # convert the None from a default to a '' + check = '' + else: + check = self._str(check) + if (check != val) or missing: + section[entry] = check + if not copy and missing and entry not in section.defaults: + section.defaults.append(entry) + return ret_true, ret_false + + # + out = {} + ret_true = True + ret_false = True + + unvalidated = [k for k in section.scalars if k not in configspec] + incorrect_sections = [k for k in configspec.sections if k in section.scalars] + incorrect_scalars = [k for k in configspec.scalars if k in section.sections] + + for entry in configspec.scalars: + if entry in ('__many__', '___many___'): + # reserved names + continue + if (not entry in section.scalars) or (entry in section.defaults): + # missing entries + # or entries from defaults + missing = True + val = None + if copy and entry not in section.scalars: + # copy comments + section.comments[entry] = ( + configspec.comments.get(entry, [])) + section.inline_comments[entry] = ( + configspec.inline_comments.get(entry, '')) + # + else: + missing = False + val = section[entry] + + ret_true, ret_false = validate_entry(entry, configspec[entry], val, + missing, ret_true, ret_false) + + many = None + if '__many__' in configspec.scalars: + many = configspec['__many__'] + elif '___many___' in configspec.scalars: + many = configspec['___many___'] + + if many is not None: + for entry in unvalidated: + val = section[entry] + ret_true, ret_false = validate_entry(entry, many, val, False, + ret_true, ret_false) + unvalidated = [] + + for entry in incorrect_scalars: + ret_true = False + if not preserve_errors: + out[entry] = False + else: + ret_false = False + msg = 'Value %r was provided as a section' % entry + out[entry] = validator.baseErrorClass(msg) + for entry in incorrect_sections: + ret_true = False + if not preserve_errors: + out[entry] = False + else: + ret_false = False + msg = 'Section %r was provided as a single value' % entry + out[entry] = validator.baseErrorClass(msg) + + # Missing sections will have been created as empty ones when the + # configspec was read. + for entry in section.sections: + # FIXME: this means DEFAULT is not copied in copy mode + if section is self and entry == 'DEFAULT': + continue + if section[entry].configspec is None: + unvalidated.append(entry) + continue + if copy: + section.comments[entry] = configspec.comments.get(entry, []) + section.inline_comments[entry] = configspec.inline_comments.get(entry, '') + check = self.validate(validator, preserve_errors=preserve_errors, copy=copy, section=section[entry]) + out[entry] = check + if check == False: + ret_true = False + elif check == True: + ret_false = False + else: + ret_true = False + + section.extra_values = unvalidated + if preserve_errors and not section._created: + # If the section wasn't created (i.e. it wasn't missing) + # then we can't return False, we need to preserve errors + ret_false = False + # + if ret_false and preserve_errors and out: + # If we are preserving errors, but all + # the failures are from missing sections / values + # then we can return False. Otherwise there is a + # real failure that we need to preserve. + ret_false = not any(out.values()) + if ret_true: + return True + elif ret_false: + return False + return out + + + def reset(self): + """Clear ConfigObj instance and restore to 'freshly created' state.""" + self.clear() + self._initialise() + # FIXME: Should be done by '_initialise', but ConfigObj constructor (and reload) + # requires an empty dictionary + self.configspec = None + # Just to be sure ;-) + self._original_configspec = None + + + def reload(self): + """ + Reload a ConfigObj from file. + + This method raises a ``ReloadError`` if the ConfigObj doesn't have + a filename attribute pointing to a file. + """ + if not isinstance(self.filename, str): + raise ReloadError() + + filename = self.filename + current_options = {} + for entry in OPTION_DEFAULTS: + if entry == 'configspec': + continue + current_options[entry] = getattr(self, entry) + + configspec = self._original_configspec + current_options['configspec'] = configspec + + self.clear() + self._initialise(current_options) + self._load(filename, configspec) + + + +class SimpleVal(object): + """ + A simple validator. + Can be used to check that all members expected are present. + + To use it, provide a configspec with all your members in (the value given + will be ignored). Pass an instance of ``SimpleVal`` to the ``validate`` + method of your ``ConfigObj``. ``validate`` will return ``True`` if all + members are present, or a dictionary with True/False meaning + present/missing. (Whole missing sections will be replaced with ``False``) + """ + + def __init__(self): + self.baseErrorClass = ConfigObjError + + def check(self, check, member, missing=False): + """A dummy check method, always returns the value unchanged.""" + if missing: + raise self.baseErrorClass() + return member + + +def flatten_errors(cfg, res, levels=None, results=None): + """ + An example function that will turn a nested dictionary of results + (as returned by ``ConfigObj.validate``) into a flat list. + + ``cfg`` is the ConfigObj instance being checked, ``res`` is the results + dictionary returned by ``validate``. + + (This is a recursive function, so you shouldn't use the ``levels`` or + ``results`` arguments - they are used by the function.) + + Returns a list of keys that failed. Each member of the list is a tuple:: + + ([list of sections...], key, result) + + If ``validate`` was called with ``preserve_errors=False`` (the default) + then ``result`` will always be ``False``. + + *list of sections* is a flattened list of sections that the key was found + in. + + If the section was missing (or a section was expected and a scalar provided + - or vice-versa) then key will be ``None``. + + If the value (or section) was missing then ``result`` will be ``False``. + + If ``validate`` was called with ``preserve_errors=True`` and a value + was present, but failed the check, then ``result`` will be the exception + object returned. You can use this as a string that describes the failure. + + For example *The value "3" is of the wrong type*. + """ + if levels is None: + # first time called + levels = [] + results = [] + if res == True: + return sorted(results) + if res == False or isinstance(res, Exception): + results.append((levels[:], None, res)) + if levels: + levels.pop() + return sorted(results) + for (key, val) in list(res.items()): + if val == True: + continue + if isinstance(cfg.get(key), Mapping): + # Go down one level + levels.append(key) + flatten_errors(cfg[key], val, levels, results) + continue + results.append((levels[:], key, val)) + # + # Go up one level + if levels: + levels.pop() + # + return sorted(results) + + +def get_extra_values(conf, _prepend=()): + """ + Find all the values and sections not in the configspec from a validated + ConfigObj. + + ``get_extra_values`` returns a list of tuples where each tuple represents + either an extra section, or an extra value. + + The tuples contain two values, a tuple representing the section the value + is in and the name of the extra values. For extra values in the top level + section the first member will be an empty tuple. For values in the 'foo' + section the first member will be ``('foo',)``. For members in the 'bar' + subsection of the 'foo' section the first member will be ``('foo', 'bar')``. + + NOTE: If you call ``get_extra_values`` on a ConfigObj instance that hasn't + been validated it will return an empty list. + """ + out = [] + + out.extend([(_prepend, name) for name in conf.extra_values]) + for name in conf.sections: + if name not in conf.extra_values: + out.extend(get_extra_values(conf[name], _prepend + (name,))) + return out + + +"""*A programming language is a medium of expression.* - Paul Graham""" diff --git a/astropy/extern/configobj/validate.py b/astropy/extern/configobj/validate.py new file mode 100755 index 000000000000..a4cd7ed473d0 --- /dev/null +++ b/astropy/extern/configobj/validate.py @@ -0,0 +1,1472 @@ +# validate.py +# A Validator object +# Copyright (C) 2005-2014: +# (name) : (email) +# Michael Foord: fuzzyman AT voidspace DOT org DOT uk +# Mark Andrews: mark AT la-la DOT com +# Nicola Larosa: nico AT tekNico DOT net +# Rob Dennis: rdennis AT gmail DOT com +# Eli Courtwright: eli AT courtwright DOT org + +# This software is licensed under the terms of the BSD license. +# http://opensource.org/licenses/BSD-3-Clause + +# ConfigObj 5 - main repository for documentation and issue tracking: +# https://github.com/DiffSK/configobj + +""" + The Validator object is used to check that supplied values + conform to a specification. + + The value can be supplied as a string - e.g. from a config file. + In this case the check will also *convert* the value to + the required type. This allows you to add validation + as a transparent layer to access data stored as strings. + The validation checks that the data is correct *and* + converts it to the expected type. + + Some standard checks are provided for basic data types. + Additional checks are easy to write. They can be + provided when the ``Validator`` is instantiated or + added afterwards. + + The standard functions work with the following basic data types : + + * integers + * floats + * booleans + * strings + * ip_addr + + plus lists of these datatypes + + Adding additional checks is done through coding simple functions. + + The full set of standard checks are : + + * 'integer': matches integer values (including negative) + Takes optional 'min' and 'max' arguments : :: + + integer() + integer(3, 9) # any value from 3 to 9 + integer(min=0) # any positive value + integer(max=9) + + * 'float': matches float values + Has the same parameters as the integer check. + + * 'boolean': matches boolean values - ``True`` or ``False`` + Acceptable string values for True are : + true, on, yes, 1 + Acceptable string values for False are : + false, off, no, 0 + + Any other value raises an error. + + * 'ip_addr': matches an Internet Protocol address, v.4, represented + by a dotted-quad string, i.e. '1.2.3.4'. + + * 'string': matches any string. + Takes optional keyword args 'min' and 'max' + to specify min and max lengths of the string. + + * 'list': matches any list. + Takes optional keyword args 'min', and 'max' to specify min and + max sizes of the list. (Always returns a list.) + + * 'tuple': matches any tuple. + Takes optional keyword args 'min', and 'max' to specify min and + max sizes of the tuple. (Always returns a tuple.) + + * 'int_list': Matches a list of integers. + Takes the same arguments as list. + + * 'float_list': Matches a list of floats. + Takes the same arguments as list. + + * 'bool_list': Matches a list of boolean values. + Takes the same arguments as list. + + * 'ip_addr_list': Matches a list of IP addresses. + Takes the same arguments as list. + + * 'string_list': Matches a list of strings. + Takes the same arguments as list. + + * 'mixed_list': Matches a list with different types in + specific positions. List size must match + the number of arguments. + + Each position can be one of : + 'integer', 'float', 'ip_addr', 'string', 'boolean' + + So to specify a list with two strings followed + by two integers, you write the check as : :: + + mixed_list('string', 'string', 'integer', 'integer') + + * 'pass': This check matches everything ! It never fails + and the value is unchanged. + + It is also the default if no check is specified. + + * 'option': This check matches any from a list of options. + You specify this check with : :: + + option('option 1', 'option 2', 'option 3') + + You can supply a default value (returned if no value is supplied) + using the default keyword argument. + + You specify a list argument for default using a list constructor syntax in + the check : :: + + checkname(arg1, arg2, default=list('val 1', 'val 2', 'val 3')) + + A badly formatted set of arguments will raise a ``VdtParamError``. +""" + +__version__ = '1.0.1' + + +__all__ = ( + '__version__', + 'dottedQuadToNum', + 'numToDottedQuad', + 'ValidateError', + 'VdtUnknownCheckError', + 'VdtParamError', + 'VdtTypeError', + 'VdtValueError', + 'VdtValueTooSmallError', + 'VdtValueTooBigError', + 'VdtValueTooShortError', + 'VdtValueTooLongError', + 'VdtMissingValue', + 'Validator', + 'is_integer', + 'is_float', + 'is_boolean', + 'is_list', + 'is_tuple', + 'is_ip_addr', + 'is_string', + 'is_int_list', + 'is_bool_list', + 'is_float_list', + 'is_string_list', + 'is_ip_addr_list', + 'is_mixed_list', + 'is_option', + '__docformat__', +) + + +import re +import sys +from pprint import pprint + +#TODO - #21 - six is part of the repo now, but we didn't switch over to it here +# this could be replaced if six is used for compatibility, or there are no +# more assertions about items being a string +if sys.version_info < (3,): + string_type = basestring +else: + string_type = str + # so tests that care about unicode on 2.x can specify unicode, and the same + # tests when run on 3.x won't complain about a undefined name "unicode" + # since all strings are unicode on 3.x we just want to pass it through + # unchanged + unicode = lambda x: x + # in python 3, all ints are equivalent to python 2 longs, and they'll + # never show "L" in the repr + long = int + +_list_arg = re.compile(r''' + (?: + ([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*list\( + ( + (?: + \s* + (?: + (?:".*?")| # double quotes + (?:'.*?')| # single quotes + (?:[^'",\s\)][^,\)]*?) # unquoted + ) + \s*,\s* + )* + (?: + (?:".*?")| # double quotes + (?:'.*?')| # single quotes + (?:[^'",\s\)][^,\)]*?) # unquoted + )? # last one + ) + \) + ) +''', re.VERBOSE | re.DOTALL) # two groups + +_list_members = re.compile(r''' + ( + (?:".*?")| # double quotes + (?:'.*?')| # single quotes + (?:[^'",\s=][^,=]*?) # unquoted + ) + (?: + (?:\s*,\s*)|(?:\s*$) # comma + ) +''', re.VERBOSE | re.DOTALL) # one group + +_paramstring = r''' + (?: + ( + (?: + [a-zA-Z_][a-zA-Z0-9_]*\s*=\s*list\( + (?: + \s* + (?: + (?:".*?")| # double quotes + (?:'.*?')| # single quotes + (?:[^'",\s\)][^,\)]*?) # unquoted + ) + \s*,\s* + )* + (?: + (?:".*?")| # double quotes + (?:'.*?')| # single quotes + (?:[^'",\s\)][^,\)]*?) # unquoted + )? # last one + \) + )| + (?: + (?:".*?")| # double quotes + (?:'.*?')| # single quotes + (?:[^'",\s=][^,=]*?)| # unquoted + (?: # keyword argument + [a-zA-Z_][a-zA-Z0-9_]*\s*=\s* + (?: + (?:".*?")| # double quotes + (?:'.*?')| # single quotes + (?:[^'",\s=][^,=]*?) # unquoted + ) + ) + ) + ) + (?: + (?:\s*,\s*)|(?:\s*$) # comma + ) + ) + ''' + +_matchstring = '^%s*' % _paramstring + +# Python pre 2.2.1 doesn't have bool +try: + bool +except NameError: + def bool(val): + """Simple boolean equivalent function. """ + if val: + return 1 + else: + return 0 + + +def dottedQuadToNum(ip): + """ + Convert decimal dotted quad string to long integer + + >>> int(dottedQuadToNum('1 ')) + 1 + >>> int(dottedQuadToNum(' 1.2')) + 16777218 + >>> int(dottedQuadToNum(' 1.2.3 ')) + 16908291 + >>> int(dottedQuadToNum('1.2.3.4')) + 16909060 + >>> dottedQuadToNum('255.255.255.255') + 4294967295 + >>> dottedQuadToNum('255.255.255.256') + Traceback (most recent call last): + ValueError: Not a good dotted-quad IP: 255.255.255.256 + """ + + # import here to avoid it when ip_addr values are not used + import socket, struct + + try: + return struct.unpack('!L', + socket.inet_aton(ip.strip()))[0] + except socket.error: + raise ValueError('Not a good dotted-quad IP: %s' % ip) + return + + +def numToDottedQuad(num): + """ + Convert int or long int to dotted quad string + + >>> numToDottedQuad(long(-1)) + Traceback (most recent call last): + ValueError: Not a good numeric IP: -1 + >>> numToDottedQuad(long(1)) + '0.0.0.1' + >>> numToDottedQuad(long(16777218)) + '1.0.0.2' + >>> numToDottedQuad(long(16908291)) + '1.2.0.3' + >>> numToDottedQuad(long(16909060)) + '1.2.3.4' + >>> numToDottedQuad(long(4294967295)) + '255.255.255.255' + >>> numToDottedQuad(long(4294967296)) + Traceback (most recent call last): + ValueError: Not a good numeric IP: 4294967296 + >>> numToDottedQuad(-1) + Traceback (most recent call last): + ValueError: Not a good numeric IP: -1 + >>> numToDottedQuad(1) + '0.0.0.1' + >>> numToDottedQuad(16777218) + '1.0.0.2' + >>> numToDottedQuad(16908291) + '1.2.0.3' + >>> numToDottedQuad(16909060) + '1.2.3.4' + >>> numToDottedQuad(4294967295) + '255.255.255.255' + >>> numToDottedQuad(4294967296) + Traceback (most recent call last): + ValueError: Not a good numeric IP: 4294967296 + + """ + + # import here to avoid it when ip_addr values are not used + import socket, struct + + # no need to intercept here, 4294967295L is fine + if num > long(4294967295) or num < 0: + raise ValueError('Not a good numeric IP: %s' % num) + try: + return socket.inet_ntoa( + struct.pack('!L', long(num))) + except (socket.error, struct.error, OverflowError): + raise ValueError('Not a good numeric IP: %s' % num) + + +class ValidateError(Exception): + """ + This error indicates that the check failed. + It can be the base class for more specific errors. + + Any check function that fails ought to raise this error. + (or a subclass) + + >>> raise ValidateError + Traceback (most recent call last): + ValidateError + """ + + +class VdtMissingValue(ValidateError): + """No value was supplied to a check that needed one.""" + + +class VdtUnknownCheckError(ValidateError): + """An unknown check function was requested""" + + def __init__(self, value): + """ + >>> raise VdtUnknownCheckError('yoda') + Traceback (most recent call last): + VdtUnknownCheckError: the check "yoda" is unknown. + """ + ValidateError.__init__(self, 'the check "%s" is unknown.' % (value,)) + + +class VdtParamError(SyntaxError): + """An incorrect parameter was passed""" + + def __init__(self, name, value): + """ + >>> raise VdtParamError('yoda', 'jedi') + Traceback (most recent call last): + VdtParamError: passed an incorrect value "jedi" for parameter "yoda". + """ + SyntaxError.__init__(self, 'passed an incorrect value "%s" for parameter "%s".' % (value, name)) + + +class VdtTypeError(ValidateError): + """The value supplied was of the wrong type""" + + def __init__(self, value): + """ + >>> raise VdtTypeError('jedi') + Traceback (most recent call last): + VdtTypeError: the value "jedi" is of the wrong type. + """ + ValidateError.__init__(self, 'the value "%s" is of the wrong type.' % (value,)) + + +class VdtValueError(ValidateError): + """The value supplied was of the correct type, but was not an allowed value.""" + + def __init__(self, value): + """ + >>> raise VdtValueError('jedi') + Traceback (most recent call last): + VdtValueError: the value "jedi" is unacceptable. + """ + ValidateError.__init__(self, 'the value "%s" is unacceptable.' % (value,)) + + +class VdtValueTooSmallError(VdtValueError): + """The value supplied was of the correct type, but was too small.""" + + def __init__(self, value): + """ + >>> raise VdtValueTooSmallError('0') + Traceback (most recent call last): + VdtValueTooSmallError: the value "0" is too small. + """ + ValidateError.__init__(self, 'the value "%s" is too small.' % (value,)) + + +class VdtValueTooBigError(VdtValueError): + """The value supplied was of the correct type, but was too big.""" + + def __init__(self, value): + """ + >>> raise VdtValueTooBigError('1') + Traceback (most recent call last): + VdtValueTooBigError: the value "1" is too big. + """ + ValidateError.__init__(self, 'the value "%s" is too big.' % (value,)) + + +class VdtValueTooShortError(VdtValueError): + """The value supplied was of the correct type, but was too short.""" + + def __init__(self, value): + """ + >>> raise VdtValueTooShortError('jed') + Traceback (most recent call last): + VdtValueTooShortError: the value "jed" is too short. + """ + ValidateError.__init__( + self, + 'the value "%s" is too short.' % (value,)) + + +class VdtValueTooLongError(VdtValueError): + """The value supplied was of the correct type, but was too long.""" + + def __init__(self, value): + """ + >>> raise VdtValueTooLongError('jedie') + Traceback (most recent call last): + VdtValueTooLongError: the value "jedie" is too long. + """ + ValidateError.__init__(self, 'the value "%s" is too long.' % (value,)) + + +class Validator(object): + """ + Validator is an object that allows you to register a set of 'checks'. + These checks take input and test that it conforms to the check. + + This can also involve converting the value from a string into + the correct datatype. + + The ``check`` method takes an input string which configures which + check is to be used and applies that check to a supplied value. + + An example input string would be: + 'int_range(param1, param2)' + + You would then provide something like: + + >>> def int_range_check(value, min, max): + ... # turn min and max from strings to integers + ... min = int(min) + ... max = int(max) + ... # check that value is of the correct type. + ... # possible valid inputs are integers or strings + ... # that represent integers + ... if not isinstance(value, (int, long, string_type)): + ... raise VdtTypeError(value) + ... elif isinstance(value, string_type): + ... # if we are given a string + ... # attempt to convert to an integer + ... try: + ... value = int(value) + ... except ValueError: + ... raise VdtValueError(value) + ... # check the value is between our constraints + ... if not min <= value: + ... raise VdtValueTooSmallError(value) + ... if not value <= max: + ... raise VdtValueTooBigError(value) + ... return value + + >>> fdict = {'int_range': int_range_check} + >>> vtr1 = Validator(fdict) + >>> vtr1.check('int_range(20, 40)', '30') + 30 + >>> vtr1.check('int_range(20, 40)', '60') + Traceback (most recent call last): + VdtValueTooBigError: the value "60" is too big. + + New functions can be added with : :: + + >>> vtr2 = Validator() + >>> vtr2.functions['int_range'] = int_range_check + + Or by passing in a dictionary of functions when Validator + is instantiated. + + Your functions *can* use keyword arguments, + but the first argument should always be 'value'. + + If the function doesn't take additional arguments, + the parentheses are optional in the check. + It can be written with either of : :: + + keyword = function_name + keyword = function_name() + + The first program to utilise Validator() was Michael Foord's + ConfigObj, an alternative to ConfigParser which supports lists and + can validate a config file using a config schema. + For more details on using Validator with ConfigObj see: + https://configobj.readthedocs.org/en/latest/configobj.html + """ + + # this regex does the initial parsing of the checks + _func_re = re.compile(r'([^\(\)]+?)\((.*)\)', re.DOTALL) + + # this regex takes apart keyword arguments + _key_arg = re.compile(r'^([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*(.*)$', re.DOTALL) + + + # this regex finds keyword=list(....) type values + _list_arg = _list_arg + + # this regex takes individual values out of lists - in one pass + _list_members = _list_members + + # These regexes check a set of arguments for validity + # and then pull the members out + _paramfinder = re.compile(_paramstring, re.VERBOSE | re.DOTALL) + _matchfinder = re.compile(_matchstring, re.VERBOSE | re.DOTALL) + + + def __init__(self, functions=None): + """ + >>> vtri = Validator() + """ + self.functions = { + '': self._pass, + 'integer': is_integer, + 'float': is_float, + 'boolean': is_boolean, + 'ip_addr': is_ip_addr, + 'string': is_string, + 'list': is_list, + 'tuple': is_tuple, + 'int_list': is_int_list, + 'float_list': is_float_list, + 'bool_list': is_bool_list, + 'ip_addr_list': is_ip_addr_list, + 'string_list': is_string_list, + 'mixed_list': is_mixed_list, + 'pass': self._pass, + 'option': is_option, + 'force_list': force_list, + } + if functions is not None: + self.functions.update(functions) + # tekNico: for use by ConfigObj + self.baseErrorClass = ValidateError + self._cache = {} + + + def check(self, check, value, missing=False): + """ + Usage: check(check, value) + + Arguments: + check: string representing check to apply (including arguments) + value: object to be checked + Returns value, converted to correct type if necessary + + If the check fails, raises a ``ValidateError`` subclass. + + >>> vtor.check('yoda', '') + Traceback (most recent call last): + VdtUnknownCheckError: the check "yoda" is unknown. + >>> vtor.check('yoda()', '') + Traceback (most recent call last): + VdtUnknownCheckError: the check "yoda" is unknown. + + >>> vtor.check('string(default="")', '', missing=True) + '' + """ + fun_name, fun_args, fun_kwargs, default = self._parse_with_caching(check) + + if missing: + if default is None: + # no information needed here - to be handled by caller + raise VdtMissingValue() + value = self._handle_none(default) + + if value is None: + return None + + return self._check_value(value, fun_name, fun_args, fun_kwargs) + + + def _handle_none(self, value): + if value == 'None': + return None + elif value in ("'None'", '"None"'): + # Special case a quoted None + value = self._unquote(value) + return value + + + def _parse_with_caching(self, check): + if check in self._cache: + fun_name, fun_args, fun_kwargs, default = self._cache[check] + # We call list and dict below to work with *copies* of the data + # rather than the original (which are mutable of course) + fun_args = list(fun_args) + fun_kwargs = dict(fun_kwargs) + else: + fun_name, fun_args, fun_kwargs, default = self._parse_check(check) + fun_kwargs = dict([(str(key), value) for (key, value) in list(fun_kwargs.items())]) + self._cache[check] = fun_name, list(fun_args), dict(fun_kwargs), default + return fun_name, fun_args, fun_kwargs, default + + + def _check_value(self, value, fun_name, fun_args, fun_kwargs): + try: + fun = self.functions[fun_name] + except KeyError: + raise VdtUnknownCheckError(fun_name) + else: + return fun(value, *fun_args, **fun_kwargs) + + + def _parse_check(self, check): + fun_match = self._func_re.match(check) + if fun_match: + fun_name = fun_match.group(1) + arg_string = fun_match.group(2) + arg_match = self._matchfinder.match(arg_string) + if arg_match is None: + # Bad syntax + raise VdtParamError('Bad syntax in check "%s".' % check) + fun_args = [] + fun_kwargs = {} + # pull out args of group 2 + for arg in self._paramfinder.findall(arg_string): + # args may need whitespace removing (before removing quotes) + arg = arg.strip() + listmatch = self._list_arg.match(arg) + if listmatch: + key, val = self._list_handle(listmatch) + fun_kwargs[key] = val + continue + keymatch = self._key_arg.match(arg) + if keymatch: + val = keymatch.group(2) + if not val in ("'None'", '"None"'): + # Special case a quoted None + val = self._unquote(val) + fun_kwargs[keymatch.group(1)] = val + continue + + fun_args.append(self._unquote(arg)) + else: + # allows for function names without (args) + return check, (), {}, None + + # Default must be deleted if the value is specified too, + # otherwise the check function will get a spurious "default" keyword arg + default = fun_kwargs.pop('default', None) + return fun_name, fun_args, fun_kwargs, default + + + def _unquote(self, val): + """Unquote a value if necessary.""" + if (len(val) >= 2) and (val[0] in ("'", '"')) and (val[0] == val[-1]): + val = val[1:-1] + return val + + + def _list_handle(self, listmatch): + """Take apart a ``keyword=list('val, 'val')`` type string.""" + out = [] + name = listmatch.group(1) + args = listmatch.group(2) + for arg in self._list_members.findall(args): + out.append(self._unquote(arg)) + return name, out + + + def _pass(self, value): + """ + Dummy check that always passes + + >>> vtor.check('', 0) + 0 + >>> vtor.check('', '0') + '0' + """ + return value + + + def get_default_value(self, check): + """ + Given a check, return the default value for the check + (converted to the right type). + + If the check doesn't specify a default value then a + ``KeyError`` will be raised. + """ + fun_name, fun_args, fun_kwargs, default = self._parse_with_caching(check) + if default is None: + raise KeyError('Check "%s" has no default value.' % check) + value = self._handle_none(default) + if value is None: + return value + return self._check_value(value, fun_name, fun_args, fun_kwargs) + + +def _is_num_param(names, values, to_float=False): + """ + Return numbers from inputs or raise VdtParamError. + + Lets ``None`` pass through. + Pass in keyword argument ``to_float=True`` to + use float for the conversion rather than int. + + >>> _is_num_param(('', ''), (0, 1.0)) + [0, 1] + >>> _is_num_param(('', ''), (0, 1.0), to_float=True) + [0.0, 1.0] + >>> _is_num_param(('a'), ('a')) + Traceback (most recent call last): + VdtParamError: passed an incorrect value "a" for parameter "a". + """ + fun = to_float and float or int + out_params = [] + for (name, val) in zip(names, values): + if val is None: + out_params.append(val) + elif isinstance(val, (int, long, float, string_type)): + try: + out_params.append(fun(val)) + except ValueError as e: + raise VdtParamError(name, val) + else: + raise VdtParamError(name, val) + return out_params + + +# built in checks +# you can override these by setting the appropriate name +# in Validator.functions +# note: if the params are specified wrongly in your input string, +# you will also raise errors. + +def is_integer(value, min=None, max=None): + """ + A check that tests that a given value is an integer (int, or long) + and optionally, between bounds. A negative value is accepted, while + a float will fail. + + If the value is a string, then the conversion is done - if possible. + Otherwise a VdtError is raised. + + >>> vtor.check('integer', '-1') + -1 + >>> vtor.check('integer', '0') + 0 + >>> vtor.check('integer', 9) + 9 + >>> vtor.check('integer', 'a') + Traceback (most recent call last): + VdtTypeError: the value "a" is of the wrong type. + >>> vtor.check('integer', '2.2') + Traceback (most recent call last): + VdtTypeError: the value "2.2" is of the wrong type. + >>> vtor.check('integer(10)', '20') + 20 + >>> vtor.check('integer(max=20)', '15') + 15 + >>> vtor.check('integer(10)', '9') + Traceback (most recent call last): + VdtValueTooSmallError: the value "9" is too small. + >>> vtor.check('integer(10)', 9) + Traceback (most recent call last): + VdtValueTooSmallError: the value "9" is too small. + >>> vtor.check('integer(max=20)', '35') + Traceback (most recent call last): + VdtValueTooBigError: the value "35" is too big. + >>> vtor.check('integer(max=20)', 35) + Traceback (most recent call last): + VdtValueTooBigError: the value "35" is too big. + >>> vtor.check('integer(0, 9)', False) + 0 + """ + (min_val, max_val) = _is_num_param(('min', 'max'), (min, max)) + if not isinstance(value, (int, long, string_type)): + raise VdtTypeError(value) + if isinstance(value, string_type): + # if it's a string - does it represent an integer ? + try: + value = int(value) + except ValueError: + raise VdtTypeError(value) + if (min_val is not None) and (value < min_val): + raise VdtValueTooSmallError(value) + if (max_val is not None) and (value > max_val): + raise VdtValueTooBigError(value) + return value + + +def is_float(value, min=None, max=None): + """ + A check that tests that a given value is a float + (an integer will be accepted), and optionally - that it is between bounds. + + If the value is a string, then the conversion is done - if possible. + Otherwise a VdtError is raised. + + This can accept negative values. + + >>> vtor.check('float', '2') + 2.0 + + From now on we multiply the value to avoid comparing decimals + + >>> vtor.check('float', '-6.8') * 10 + -68.0 + >>> vtor.check('float', '12.2') * 10 + 122.0 + >>> vtor.check('float', 8.4) * 10 + 84.0 + >>> vtor.check('float', 'a') + Traceback (most recent call last): + VdtTypeError: the value "a" is of the wrong type. + >>> vtor.check('float(10.1)', '10.2') * 10 + 102.0 + >>> vtor.check('float(max=20.2)', '15.1') * 10 + 151.0 + >>> vtor.check('float(10.0)', '9.0') + Traceback (most recent call last): + VdtValueTooSmallError: the value "9.0" is too small. + >>> vtor.check('float(max=20.0)', '35.0') + Traceback (most recent call last): + VdtValueTooBigError: the value "35.0" is too big. + """ + (min_val, max_val) = _is_num_param( + ('min', 'max'), (min, max), to_float=True) + if not isinstance(value, (int, long, float, string_type)): + raise VdtTypeError(value) + if not isinstance(value, float): + # if it's a string - does it represent a float ? + try: + value = float(value) + except ValueError: + raise VdtTypeError(value) + if (min_val is not None) and (value < min_val): + raise VdtValueTooSmallError(value) + if (max_val is not None) and (value > max_val): + raise VdtValueTooBigError(value) + return value + + +bool_dict = { + True: True, 'on': True, '1': True, 'true': True, 'yes': True, + False: False, 'off': False, '0': False, 'false': False, 'no': False, +} + + +def is_boolean(value): + """ + Check if the value represents a boolean. + + >>> vtor.check('boolean', 0) + 0 + >>> vtor.check('boolean', False) + 0 + >>> vtor.check('boolean', '0') + 0 + >>> vtor.check('boolean', 'off') + 0 + >>> vtor.check('boolean', 'false') + 0 + >>> vtor.check('boolean', 'no') + 0 + >>> vtor.check('boolean', 'nO') + 0 + >>> vtor.check('boolean', 'NO') + 0 + >>> vtor.check('boolean', 1) + 1 + >>> vtor.check('boolean', True) + 1 + >>> vtor.check('boolean', '1') + 1 + >>> vtor.check('boolean', 'on') + 1 + >>> vtor.check('boolean', 'true') + 1 + >>> vtor.check('boolean', 'yes') + 1 + >>> vtor.check('boolean', 'Yes') + 1 + >>> vtor.check('boolean', 'YES') + 1 + >>> vtor.check('boolean', '') + Traceback (most recent call last): + VdtTypeError: the value "" is of the wrong type. + >>> vtor.check('boolean', 'up') + Traceback (most recent call last): + VdtTypeError: the value "up" is of the wrong type. + + """ + if isinstance(value, string_type): + try: + return bool_dict[value.lower()] + except KeyError: + raise VdtTypeError(value) + # we do an equality test rather than an identity test + # this ensures Python 2.2 compatibilty + # and allows 0 and 1 to represent True and False + if value == False: + return False + elif value == True: + return True + else: + raise VdtTypeError(value) + + +def is_ip_addr(value): + """ + Check that the supplied value is an Internet Protocol address, v.4, + represented by a dotted-quad string, i.e. '1.2.3.4'. + + >>> vtor.check('ip_addr', '1 ') + '1' + >>> vtor.check('ip_addr', ' 1.2') + '1.2' + >>> vtor.check('ip_addr', ' 1.2.3 ') + '1.2.3' + >>> vtor.check('ip_addr', '1.2.3.4') + '1.2.3.4' + >>> vtor.check('ip_addr', '0.0.0.0') + '0.0.0.0' + >>> vtor.check('ip_addr', '255.255.255.255') + '255.255.255.255' + >>> vtor.check('ip_addr', '255.255.255.256') + Traceback (most recent call last): + VdtValueError: the value "255.255.255.256" is unacceptable. + >>> vtor.check('ip_addr', '1.2.3.4.5') + Traceback (most recent call last): + VdtValueError: the value "1.2.3.4.5" is unacceptable. + >>> vtor.check('ip_addr', 0) + Traceback (most recent call last): + VdtTypeError: the value "0" is of the wrong type. + """ + if not isinstance(value, string_type): + raise VdtTypeError(value) + value = value.strip() + try: + dottedQuadToNum(value) + except ValueError: + raise VdtValueError(value) + return value + + +def is_list(value, min=None, max=None): + """ + Check that the value is a list of values. + + You can optionally specify the minimum and maximum number of members. + + It does no check on list members. + + >>> vtor.check('list', ()) + [] + >>> vtor.check('list', []) + [] + >>> vtor.check('list', (1, 2)) + [1, 2] + >>> vtor.check('list', [1, 2]) + [1, 2] + >>> vtor.check('list(3)', (1, 2)) + Traceback (most recent call last): + VdtValueTooShortError: the value "(1, 2)" is too short. + >>> vtor.check('list(max=5)', (1, 2, 3, 4, 5, 6)) + Traceback (most recent call last): + VdtValueTooLongError: the value "(1, 2, 3, 4, 5, 6)" is too long. + >>> vtor.check('list(min=3, max=5)', (1, 2, 3, 4)) + [1, 2, 3, 4] + >>> vtor.check('list', 0) + Traceback (most recent call last): + VdtTypeError: the value "0" is of the wrong type. + >>> vtor.check('list', '12') + Traceback (most recent call last): + VdtTypeError: the value "12" is of the wrong type. + """ + (min_len, max_len) = _is_num_param(('min', 'max'), (min, max)) + if isinstance(value, string_type): + raise VdtTypeError(value) + try: + num_members = len(value) + except TypeError: + raise VdtTypeError(value) + if min_len is not None and num_members < min_len: + raise VdtValueTooShortError(value) + if max_len is not None and num_members > max_len: + raise VdtValueTooLongError(value) + return list(value) + + +def is_tuple(value, min=None, max=None): + """ + Check that the value is a tuple of values. + + You can optionally specify the minimum and maximum number of members. + + It does no check on members. + + >>> vtor.check('tuple', ()) + () + >>> vtor.check('tuple', []) + () + >>> vtor.check('tuple', (1, 2)) + (1, 2) + >>> vtor.check('tuple', [1, 2]) + (1, 2) + >>> vtor.check('tuple(3)', (1, 2)) + Traceback (most recent call last): + VdtValueTooShortError: the value "(1, 2)" is too short. + >>> vtor.check('tuple(max=5)', (1, 2, 3, 4, 5, 6)) + Traceback (most recent call last): + VdtValueTooLongError: the value "(1, 2, 3, 4, 5, 6)" is too long. + >>> vtor.check('tuple(min=3, max=5)', (1, 2, 3, 4)) + (1, 2, 3, 4) + >>> vtor.check('tuple', 0) + Traceback (most recent call last): + VdtTypeError: the value "0" is of the wrong type. + >>> vtor.check('tuple', '12') + Traceback (most recent call last): + VdtTypeError: the value "12" is of the wrong type. + """ + return tuple(is_list(value, min, max)) + + +def is_string(value, min=None, max=None): + """ + Check that the supplied value is a string. + + You can optionally specify the minimum and maximum number of members. + + >>> vtor.check('string', '0') + '0' + >>> vtor.check('string', 0) + Traceback (most recent call last): + VdtTypeError: the value "0" is of the wrong type. + >>> vtor.check('string(2)', '12') + '12' + >>> vtor.check('string(2)', '1') + Traceback (most recent call last): + VdtValueTooShortError: the value "1" is too short. + >>> vtor.check('string(min=2, max=3)', '123') + '123' + >>> vtor.check('string(min=2, max=3)', '1234') + Traceback (most recent call last): + VdtValueTooLongError: the value "1234" is too long. + """ + if not isinstance(value, string_type): + raise VdtTypeError(value) + (min_len, max_len) = _is_num_param(('min', 'max'), (min, max)) + try: + num_members = len(value) + except TypeError: + raise VdtTypeError(value) + if min_len is not None and num_members < min_len: + raise VdtValueTooShortError(value) + if max_len is not None and num_members > max_len: + raise VdtValueTooLongError(value) + return value + + +def is_int_list(value, min=None, max=None): + """ + Check that the value is a list of integers. + + You can optionally specify the minimum and maximum number of members. + + Each list member is checked that it is an integer. + + >>> vtor.check('int_list', ()) + [] + >>> vtor.check('int_list', []) + [] + >>> vtor.check('int_list', (1, 2)) + [1, 2] + >>> vtor.check('int_list', [1, 2]) + [1, 2] + >>> vtor.check('int_list', [1, 'a']) + Traceback (most recent call last): + VdtTypeError: the value "a" is of the wrong type. + """ + return [is_integer(mem) for mem in is_list(value, min, max)] + + +def is_bool_list(value, min=None, max=None): + """ + Check that the value is a list of booleans. + + You can optionally specify the minimum and maximum number of members. + + Each list member is checked that it is a boolean. + + >>> vtor.check('bool_list', ()) + [] + >>> vtor.check('bool_list', []) + [] + >>> check_res = vtor.check('bool_list', (True, False)) + >>> check_res == [True, False] + 1 + >>> check_res = vtor.check('bool_list', [True, False]) + >>> check_res == [True, False] + 1 + >>> vtor.check('bool_list', [True, 'a']) + Traceback (most recent call last): + VdtTypeError: the value "a" is of the wrong type. + """ + return [is_boolean(mem) for mem in is_list(value, min, max)] + + +def is_float_list(value, min=None, max=None): + """ + Check that the value is a list of floats. + + You can optionally specify the minimum and maximum number of members. + + Each list member is checked that it is a float. + + >>> vtor.check('float_list', ()) + [] + >>> vtor.check('float_list', []) + [] + >>> vtor.check('float_list', (1, 2.0)) + [1.0, 2.0] + >>> vtor.check('float_list', [1, 2.0]) + [1.0, 2.0] + >>> vtor.check('float_list', [1, 'a']) + Traceback (most recent call last): + VdtTypeError: the value "a" is of the wrong type. + """ + return [is_float(mem) for mem in is_list(value, min, max)] + + +def is_string_list(value, min=None, max=None): + """ + Check that the value is a list of strings. + + You can optionally specify the minimum and maximum number of members. + + Each list member is checked that it is a string. + + >>> vtor.check('string_list', ()) + [] + >>> vtor.check('string_list', []) + [] + >>> vtor.check('string_list', ('a', 'b')) + ['a', 'b'] + >>> vtor.check('string_list', ['a', 1]) + Traceback (most recent call last): + VdtTypeError: the value "1" is of the wrong type. + >>> vtor.check('string_list', 'hello') + Traceback (most recent call last): + VdtTypeError: the value "hello" is of the wrong type. + """ + if isinstance(value, string_type): + raise VdtTypeError(value) + return [is_string(mem) for mem in is_list(value, min, max)] + + +def is_ip_addr_list(value, min=None, max=None): + """ + Check that the value is a list of IP addresses. + + You can optionally specify the minimum and maximum number of members. + + Each list member is checked that it is an IP address. + + >>> vtor.check('ip_addr_list', ()) + [] + >>> vtor.check('ip_addr_list', []) + [] + >>> vtor.check('ip_addr_list', ('1.2.3.4', '5.6.7.8')) + ['1.2.3.4', '5.6.7.8'] + >>> vtor.check('ip_addr_list', ['a']) + Traceback (most recent call last): + VdtValueError: the value "a" is unacceptable. + """ + return [is_ip_addr(mem) for mem in is_list(value, min, max)] + + +def force_list(value, min=None, max=None): + """ + Check that a value is a list, coercing strings into + a list with one member. Useful where users forget the + trailing comma that turns a single value into a list. + + You can optionally specify the minimum and maximum number of members. + A minumum of greater than one will fail if the user only supplies a + string. + + >>> vtor.check('force_list', ()) + [] + >>> vtor.check('force_list', []) + [] + >>> vtor.check('force_list', 'hello') + ['hello'] + """ + if not isinstance(value, (list, tuple)): + value = [value] + return is_list(value, min, max) + + + +fun_dict = { + 'integer': is_integer, + 'float': is_float, + 'ip_addr': is_ip_addr, + 'string': is_string, + 'boolean': is_boolean, +} + + +def is_mixed_list(value, *args): + """ + Check that the value is a list. + Allow specifying the type of each member. + Work on lists of specific lengths. + + You specify each member as a positional argument specifying type + + Each type should be one of the following strings : + 'integer', 'float', 'ip_addr', 'string', 'boolean' + + So you can specify a list of two strings, followed by + two integers as : + + mixed_list('string', 'string', 'integer', 'integer') + + The length of the list must match the number of positional + arguments you supply. + + >>> mix_str = "mixed_list('integer', 'float', 'ip_addr', 'string', 'boolean')" + >>> check_res = vtor.check(mix_str, (1, 2.0, '1.2.3.4', 'a', True)) + >>> check_res == [1, 2.0, '1.2.3.4', 'a', True] + 1 + >>> check_res = vtor.check(mix_str, ('1', '2.0', '1.2.3.4', 'a', 'True')) + >>> check_res == [1, 2.0, '1.2.3.4', 'a', True] + 1 + >>> vtor.check(mix_str, ('b', 2.0, '1.2.3.4', 'a', True)) + Traceback (most recent call last): + VdtTypeError: the value "b" is of the wrong type. + >>> vtor.check(mix_str, (1, 2.0, '1.2.3.4', 'a')) + Traceback (most recent call last): + VdtValueTooShortError: the value "(1, 2.0, '1.2.3.4', 'a')" is too short. + >>> vtor.check(mix_str, (1, 2.0, '1.2.3.4', 'a', 1, 'b')) + Traceback (most recent call last): + VdtValueTooLongError: the value "(1, 2.0, '1.2.3.4', 'a', 1, 'b')" is too long. + >>> vtor.check(mix_str, 0) + Traceback (most recent call last): + VdtTypeError: the value "0" is of the wrong type. + + >>> vtor.check('mixed_list("yoda")', ('a')) + Traceback (most recent call last): + VdtParamError: passed an incorrect value "KeyError('yoda',)" for parameter "'mixed_list'" + """ + try: + length = len(value) + except TypeError: + raise VdtTypeError(value) + if length < len(args): + raise VdtValueTooShortError(value) + elif length > len(args): + raise VdtValueTooLongError(value) + try: + return [fun_dict[arg](val) for arg, val in zip(args, value)] + except KeyError as e: + raise VdtParamError('mixed_list', e) + + +def is_option(value, *options): + """ + This check matches the value to any of a set of options. + + >>> vtor.check('option("yoda", "jedi")', 'yoda') + 'yoda' + >>> vtor.check('option("yoda", "jedi")', 'jed') + Traceback (most recent call last): + VdtValueError: the value "jed" is unacceptable. + >>> vtor.check('option("yoda", "jedi")', 0) + Traceback (most recent call last): + VdtTypeError: the value "0" is of the wrong type. + """ + if not isinstance(value, string_type): + raise VdtTypeError(value) + if not value in options: + raise VdtValueError(value) + return value + + +def _test(value, *args, **keywargs): + """ + A function that exists for test purposes. + + >>> checks = [ + ... '3, 6, min=1, max=3, test=list(a, b, c)', + ... '3', + ... '3, 6', + ... '3,', + ... 'min=1, test="a b c"', + ... 'min=5, test="a, b, c"', + ... 'min=1, max=3, test="a, b, c"', + ... 'min=-100, test=-99', + ... 'min=1, max=3', + ... '3, 6, test="36"', + ... '3, 6, test="a, b, c"', + ... '3, max=3, test=list("a", "b", "c")', + ... '''3, max=3, test=list("'a'", 'b', "x=(c)")''', + ... "test='x=fish(3)'", + ... ] + >>> v = Validator({'test': _test}) + >>> for entry in checks: + ... pprint(v.check(('test(%s)' % entry), 3)) + (3, ('3', '6'), {'max': '3', 'min': '1', 'test': ['a', 'b', 'c']}) + (3, ('3',), {}) + (3, ('3', '6'), {}) + (3, ('3',), {}) + (3, (), {'min': '1', 'test': 'a b c'}) + (3, (), {'min': '5', 'test': 'a, b, c'}) + (3, (), {'max': '3', 'min': '1', 'test': 'a, b, c'}) + (3, (), {'min': '-100', 'test': '-99'}) + (3, (), {'max': '3', 'min': '1'}) + (3, ('3', '6'), {'test': '36'}) + (3, ('3', '6'), {'test': 'a, b, c'}) + (3, ('3',), {'max': '3', 'test': ['a', 'b', 'c']}) + (3, ('3',), {'max': '3', 'test': ["'a'", 'b', 'x=(c)']}) + (3, (), {'test': 'x=fish(3)'}) + + >>> v = Validator() + >>> v.check('integer(default=6)', '3') + 3 + >>> v.check('integer(default=6)', None, True) + 6 + >>> v.get_default_value('integer(default=6)') + 6 + >>> v.get_default_value('float(default=6)') + 6.0 + >>> v.get_default_value('pass(default=None)') + >>> v.get_default_value("string(default='None')") + 'None' + >>> v.get_default_value('pass') + Traceback (most recent call last): + KeyError: 'Check "pass" has no default value.' + >>> v.get_default_value('pass(default=list(1, 2, 3, 4))') + ['1', '2', '3', '4'] + + >>> v = Validator() + >>> v.check("pass(default=None)", None, True) + >>> v.check("pass(default='None')", None, True) + 'None' + >>> v.check('pass(default="None")', None, True) + 'None' + >>> v.check('pass(default=list(1, 2, 3, 4))', None, True) + ['1', '2', '3', '4'] + + Bug test for unicode arguments + >>> v = Validator() + >>> v.check(unicode('string(min=4)'), unicode('test')) == unicode('test') + True + + >>> v = Validator() + >>> v.get_default_value(unicode('string(min=4, default="1234")')) == unicode('1234') + True + >>> v.check(unicode('string(min=4, default="1234")'), unicode('test')) == unicode('test') + True + + >>> v = Validator() + >>> default = v.get_default_value('string(default=None)') + >>> default == None + 1 + """ + return (value, args, keywargs) + + +def _test2(): + """ + >>> + >>> v = Validator() + >>> v.get_default_value('string(default="#ff00dd")') + '#ff00dd' + >>> v.get_default_value('integer(default=3) # comment') + 3 + """ + +def _test3(): + r""" + >>> vtor.check('string(default="")', '', missing=True) + '' + >>> vtor.check('string(default="\n")', '', missing=True) + '\n' + >>> print(vtor.check('string(default="\n")', '', missing=True)) + + + >>> vtor.check('string()', '\n') + '\n' + >>> vtor.check('string(default="\n\n\n")', '', missing=True) + '\n\n\n' + >>> vtor.check('string()', 'random \n text goes here\n\n') + 'random \n text goes here\n\n' + >>> vtor.check('string(default=" \nrandom text\ngoes \n here\n\n ")', + ... '', missing=True) + ' \nrandom text\ngoes \n here\n\n ' + >>> vtor.check("string(default='\n\n\n')", '', missing=True) + '\n\n\n' + >>> vtor.check("option('\n','a','b',default='\n')", '', missing=True) + '\n' + >>> vtor.check("string_list()", ['foo', '\n', 'bar']) + ['foo', '\n', 'bar'] + >>> vtor.check("string_list(default=list('\n'))", '', missing=True) + ['\n'] + """ + + +if __name__ == '__main__': + # run the code tests in doctest format + import sys + import doctest + m = sys.modules.get('__main__') + globs = m.__dict__.copy() + globs.update({ + 'vtor': Validator(), + }) + + failures, tests = doctest.testmod( + m, globs=globs, + optionflags=doctest.IGNORE_EXCEPTION_DETAIL | doctest.ELLIPSIS) + assert not failures, '{} failures out of {} tests'.format(failures, tests) diff --git a/astropy/extern/configobj_py2/__init__.py b/astropy/extern/configobj_py2/__init__.py deleted file mode 100644 index f5a38a00b26d..000000000000 --- a/astropy/extern/configobj_py2/__init__.py +++ /dev/null @@ -1,100 +0,0 @@ -# Licensed under a 3-clause BSD style license - see LICENSE.rst - -""" -This is a copy of the main portions of the `configobj -`_ package. This is used -internally in the Astropy configuration system. The license for configobj is -available in the ``licenses/CONFIGOBJ_LICENSE.rst`` file in the Astropy -source distribution. - -This is the original 4.7.2 version of configobj, which is only compatible with -python 2.x - the 3.x version is in ``astropy/extern/configobj-py3``. -""" - -#this holds the contents of the setup.py file used by configobj -_configobj_setup_dot_py=""" -# setup.py -# Install script for ConfigObj -# Copyright (C) 2005-2010 Michael Foord, Mark Andrews, Nicola Larosa -# E-mail: fuzzyman AT voidspace DOT org DOT uk -# mark AT la-la DOT com -# nico AT tekNico DOT net - -# This software is licensed under the terms of the BSD license. -# http://www.voidspace.org.uk/python/license.shtml - -import sys -from distutils.core import setup -from configobj import __version__ as VERSION - -NAME = 'configobj' - -MODULES = 'configobj', 'validate' - -DESCRIPTION = 'Config file reading, writing and validation.' - -URL = 'http://www.voidspace.org.uk/python/configobj.html' - -DOWNLOAD_URL = "http://www.voidspace.org.uk/downloads/configobj-%s.zip" % VERSION - -LONG_DESCRIPTION = ""#"**ConfigObj** is a simple but powerful config file reader and writer: an *ini -file round tripper*. Its main feature is that it is very easy to use, with a -straightforward programmer's interface and a simple syntax for config files. -It has lots of other features though : - -* Nested sections (subsections), to any level -* List values -* Multiple line values -* Full Unicode support -* String interpolation (substitution) -* Integrated with a powerful validation system - - - including automatic type checking/conversion - - and allowing default values - - repeated sections - -* All comments in the file are preserved -* The order of keys/sections is preserved -* Powerful ``unrepr`` mode for storing/retrieving Python data-types - -| Release 4.7.2 fixes several bugs in 4.7.1 -| Release 4.7.1 fixes a bug with the deprecated options keyword in -| 4.7.0. -| Release 4.7.0 improves performance adds features for validation and -| fixes some bugs.""#" - -CLASSIFIERS = [ - 'Development Status :: 6 - Mature', - 'Intended Audience :: Developers', - 'License :: OSI Approved :: BSD License', - 'Programming Language :: Python', - 'Programming Language :: Python :: 2.3', - 'Programming Language :: Python :: 2.4', - 'Programming Language :: Python :: 2.5', - 'Programming Language :: Python :: 2.6', - 'Operating System :: OS Independent', - 'Topic :: Software Development :: Libraries', - 'Topic :: Software Development :: Libraries :: Python Modules', -] - -AUTHOR = 'Michael Foord & Nicola Larosa' - -AUTHOR_EMAIL = 'fuzzyman@voidspace.org.uk' - -KEYWORDS = "config, ini, dictionary, application, admin, sysadmin, configuration, validation".split(', ') - - -setup(name=NAME, - version=VERSION, - description=DESCRIPTION, - long_description=LONG_DESCRIPTION, - download_url=DOWNLOAD_URL, - author=AUTHOR, - author_email=AUTHOR_EMAIL, - url=URL, - py_modules=MODULES, - classifiers=CLASSIFIERS, - keywords=KEYWORDS - ) -""".replace('""#"','"""') -#the replacement is necessary because """ would otherwise terminate the string diff --git a/astropy/extern/configobj_py2/configobj.py b/astropy/extern/configobj_py2/configobj.py deleted file mode 100644 index c1f6e6df86e5..000000000000 --- a/astropy/extern/configobj_py2/configobj.py +++ /dev/null @@ -1,2468 +0,0 @@ -# configobj.py -# A config file reader/writer that supports nested sections in config files. -# Copyright (C) 2005-2010 Michael Foord, Nicola Larosa -# E-mail: fuzzyman AT voidspace DOT org DOT uk -# nico AT tekNico DOT net - -# ConfigObj 4 -# http://www.voidspace.org.uk/python/configobj.html - -# Released subject to the BSD License -# Please see http://www.voidspace.org.uk/python/license.shtml - -# Scripts maintained at http://www.voidspace.org.uk/python/index.shtml -# For information about bugfixes, updates and support, please join the -# ConfigObj mailing list: -# http://lists.sourceforge.net/lists/listinfo/configobj-develop -# Comments, suggestions and bug reports welcome. - -from __future__ import generators - -import os -import re -import sys - -from codecs import BOM_UTF8, BOM_UTF16, BOM_UTF16_BE, BOM_UTF16_LE - - -# imported lazily to avoid startup performance hit if it isn't used -compiler = None - -# A dictionary mapping BOM to -# the encoding to decode with, and what to set the -# encoding attribute to. -BOMS = { - BOM_UTF8: ('utf_8', None), - BOM_UTF16_BE: ('utf16_be', 'utf_16'), - BOM_UTF16_LE: ('utf16_le', 'utf_16'), - BOM_UTF16: ('utf_16', 'utf_16'), - } -# All legal variants of the BOM codecs. -# TODO: the list of aliases is not meant to be exhaustive, is there a -# better way ? -BOM_LIST = { - 'utf_16': 'utf_16', - 'u16': 'utf_16', - 'utf16': 'utf_16', - 'utf-16': 'utf_16', - 'utf16_be': 'utf16_be', - 'utf_16_be': 'utf16_be', - 'utf-16be': 'utf16_be', - 'utf16_le': 'utf16_le', - 'utf_16_le': 'utf16_le', - 'utf-16le': 'utf16_le', - 'utf_8': 'utf_8', - 'u8': 'utf_8', - 'utf': 'utf_8', - 'utf8': 'utf_8', - 'utf-8': 'utf_8', - } - -# Map of encodings to the BOM to write. -BOM_SET = { - 'utf_8': BOM_UTF8, - 'utf_16': BOM_UTF16, - 'utf16_be': BOM_UTF16_BE, - 'utf16_le': BOM_UTF16_LE, - None: BOM_UTF8 - } - - -def match_utf8(encoding): - return BOM_LIST.get(encoding.lower()) == 'utf_8' - - -# Quote strings used for writing values -squot = "'%s'" -dquot = '"%s"' -noquot = "%s" -wspace_plus = ' \r\n\v\t\'"' -tsquot = '"""%s"""' -tdquot = "'''%s'''" - -# Sentinel for use in getattr calls to replace hasattr -MISSING = object() - -__version__ = '4.7.2' - -try: - any -except NameError: - def any(iterable): - for entry in iterable: - if entry: - return True - return False - - -__all__ = ( - '__version__', - 'DEFAULT_INDENT_TYPE', - 'DEFAULT_INTERPOLATION', - 'ConfigObjError', - 'NestingError', - 'ParseError', - 'DuplicateError', - 'ConfigspecError', - 'ConfigObj', - 'SimpleVal', - 'InterpolationError', - 'InterpolationLoopError', - 'MissingInterpolationOption', - 'RepeatSectionError', - 'ReloadError', - 'UnreprError', - 'UnknownType', - 'flatten_errors', - 'get_extra_values' -) - -DEFAULT_INTERPOLATION = 'configparser' -DEFAULT_INDENT_TYPE = ' ' -MAX_INTERPOL_DEPTH = 10 - -OPTION_DEFAULTS = { - 'interpolation': True, - 'raise_errors': False, - 'list_values': True, - 'create_empty': False, - 'file_error': False, - 'configspec': None, - 'stringify': True, - # option may be set to one of ('', ' ', '\t') - 'indent_type': None, - 'encoding': None, - 'default_encoding': None, - 'unrepr': False, - 'write_empty_values': False, -} - - - -def getObj(s): - global compiler - if compiler is None: - import compiler - s = "a=" + s - p = compiler.parse(s) - return p.getChildren()[1].getChildren()[0].getChildren()[1] - - -class UnknownType(Exception): - pass - - -class Builder(object): - - def build(self, o): - m = getattr(self, 'build_' + o.__class__.__name__, None) - if m is None: - raise UnknownType(o.__class__.__name__) - return m(o) - - def build_List(self, o): - return map(self.build, o.getChildren()) - - def build_Const(self, o): - return o.value - - def build_Dict(self, o): - d = {} - i = iter(map(self.build, o.getChildren())) - for el in i: - d[el] = i.next() - return d - - def build_Tuple(self, o): - return tuple(self.build_List(o)) - - def build_Name(self, o): - if o.name == 'None': - return None - if o.name == 'True': - return True - if o.name == 'False': - return False - - # An undefined Name - raise UnknownType('Undefined Name') - - def build_Add(self, o): - real, imag = map(self.build_Const, o.getChildren()) - try: - real = float(real) - except TypeError: - raise UnknownType('Add') - if not isinstance(imag, complex) or imag.real != 0.0: - raise UnknownType('Add') - return real+imag - - def build_Getattr(self, o): - parent = self.build(o.expr) - return getattr(parent, o.attrname) - - def build_UnarySub(self, o): - return -self.build_Const(o.getChildren()[0]) - - def build_UnaryAdd(self, o): - return self.build_Const(o.getChildren()[0]) - - -_builder = Builder() - - -def unrepr(s): - if not s: - return s - return _builder.build(getObj(s)) - - - -class ConfigObjError(SyntaxError): - """ - This is the base class for all errors that ConfigObj raises. - It is a subclass of SyntaxError. - """ - def __init__(self, message='', line_number=None, line=''): - self.line = line - self.line_number = line_number - SyntaxError.__init__(self, message) - - -class NestingError(ConfigObjError): - """ - This error indicates a level of nesting that doesn't match. - """ - - -class ParseError(ConfigObjError): - """ - This error indicates that a line is badly written. - It is neither a valid ``key = value`` line, - nor a valid section marker line. - """ - - -class ReloadError(IOError): - """ - A 'reload' operation failed. - This exception is a subclass of ``IOError``. - """ - def __init__(self): - IOError.__init__(self, 'reload failed, filename is not set.') - - -class DuplicateError(ConfigObjError): - """ - The keyword or section specified already exists. - """ - - -class ConfigspecError(ConfigObjError): - """ - An error occured whilst parsing a configspec. - """ - - -class InterpolationError(ConfigObjError): - """Base class for the two interpolation errors.""" - - -class InterpolationLoopError(InterpolationError): - """Maximum interpolation depth exceeded in string interpolation.""" - - def __init__(self, option): - InterpolationError.__init__( - self, - 'interpolation loop detected in value "%s".' % option) - - -class RepeatSectionError(ConfigObjError): - """ - This error indicates additional sections in a section with a - ``__many__`` (repeated) section. - """ - - -class MissingInterpolationOption(InterpolationError): - """A value specified for interpolation was missing.""" - def __init__(self, option): - msg = 'missing option "%s" in interpolation.' % option - InterpolationError.__init__(self, msg) - - -class UnreprError(ConfigObjError): - """An error parsing in unrepr mode.""" - - - -class InterpolationEngine(object): - """ - A helper class to help perform string interpolation. - - This class is an abstract base class; its descendants perform - the actual work. - """ - - # compiled regexp to use in self.interpolate() - _KEYCRE = re.compile(r"%\(([^)]*)\)s") - _cookie = '%' - - def __init__(self, section): - # the Section instance that "owns" this engine - self.section = section - - - def interpolate(self, key, value): - # short-cut - if not self._cookie in value: - return value - - def recursive_interpolate(key, value, section, backtrail): - """The function that does the actual work. - - ``value``: the string we're trying to interpolate. - ``section``: the section in which that string was found - ``backtrail``: a dict to keep track of where we've been, - to detect and prevent infinite recursion loops - - This is similar to a depth-first-search algorithm. - """ - # Have we been here already? - if (key, section.name) in backtrail: - # Yes - infinite loop detected - raise InterpolationLoopError(key) - # Place a marker on our backtrail so we won't come back here again - backtrail[(key, section.name)] = 1 - - # Now start the actual work - match = self._KEYCRE.search(value) - while match: - # The actual parsing of the match is implementation-dependent, - # so delegate to our helper function - k, v, s = self._parse_match(match) - if k is None: - # That's the signal that no further interpolation is needed - replacement = v - else: - # Further interpolation may be needed to obtain final value - replacement = recursive_interpolate(k, v, s, backtrail) - # Replace the matched string with its final value - start, end = match.span() - value = ''.join((value[:start], replacement, value[end:])) - new_search_start = start + len(replacement) - # Pick up the next interpolation key, if any, for next time - # through the while loop - match = self._KEYCRE.search(value, new_search_start) - - # Now safe to come back here again; remove marker from backtrail - del backtrail[(key, section.name)] - - return value - - # Back in interpolate(), all we have to do is kick off the recursive - # function with appropriate starting values - value = recursive_interpolate(key, value, self.section, {}) - return value - - - def _fetch(self, key): - """Helper function to fetch values from owning section. - - Returns a 2-tuple: the value, and the section where it was found. - """ - # switch off interpolation before we try and fetch anything ! - save_interp = self.section.main.interpolation - self.section.main.interpolation = False - - # Start at section that "owns" this InterpolationEngine - current_section = self.section - while True: - # try the current section first - val = current_section.get(key) - if val is not None and not isinstance(val, Section): - break - # try "DEFAULT" next - val = current_section.get('DEFAULT', {}).get(key) - if val is not None and not isinstance(val, Section): - break - # move up to parent and try again - # top-level's parent is itself - if current_section.parent is current_section: - # reached top level, time to give up - break - current_section = current_section.parent - - # restore interpolation to previous value before returning - self.section.main.interpolation = save_interp - if val is None: - raise MissingInterpolationOption(key) - return val, current_section - - - def _parse_match(self, match): - """Implementation-dependent helper function. - - Will be passed a match object corresponding to the interpolation - key we just found (e.g., "%(foo)s" or "$foo"). Should look up that - key in the appropriate config file section (using the ``_fetch()`` - helper function) and return a 3-tuple: (key, value, section) - - ``key`` is the name of the key we're looking for - ``value`` is the value found for that key - ``section`` is a reference to the section where it was found - - ``key`` and ``section`` should be None if no further - interpolation should be performed on the resulting value - (e.g., if we interpolated "$$" and returned "$"). - """ - raise NotImplementedError() - - - -class ConfigParserInterpolation(InterpolationEngine): - """Behaves like ConfigParser.""" - _cookie = '%' - _KEYCRE = re.compile(r"%\(([^)]*)\)s") - - def _parse_match(self, match): - key = match.group(1) - value, section = self._fetch(key) - return key, value, section - - - -class TemplateInterpolation(InterpolationEngine): - """Behaves like string.Template.""" - _cookie = '$' - _delimiter = '$' - _KEYCRE = re.compile(r""" - \$(?: - (?P\$) | # Two $ signs - (?P[_a-z][_a-z0-9]*) | # $name format - {(?P[^}]*)} # ${name} format - ) - """, re.IGNORECASE | re.VERBOSE) - - def _parse_match(self, match): - # Valid name (in or out of braces): fetch value from section - key = match.group('named') or match.group('braced') - if key is not None: - value, section = self._fetch(key) - return key, value, section - # Escaped delimiter (e.g., $$): return single delimiter - if match.group('escaped') is not None: - # Return None for key and section to indicate it's time to stop - return None, self._delimiter, None - # Anything else: ignore completely, just return it unchanged - return None, match.group(), None - - -interpolation_engines = { - 'configparser': ConfigParserInterpolation, - 'template': TemplateInterpolation, -} - - -def __newobj__(cls, *args): - # Hack for pickle - return cls.__new__(cls, *args) - -class Section(dict): - """ - A dictionary-like object that represents a section in a config file. - - It does string interpolation if the 'interpolation' attribute - of the 'main' object is set to True. - - Interpolation is tried first from this object, then from the 'DEFAULT' - section of this object, next from the parent and its 'DEFAULT' section, - and so on until the main object is reached. - - A Section will behave like an ordered dictionary - following the - order of the ``scalars`` and ``sections`` attributes. - You can use this to change the order of members. - - Iteration follows the order: scalars, then sections. - """ - - - def __setstate__(self, state): - dict.update(self, state[0]) - self.__dict__.update(state[1]) - - def __reduce__(self): - state = (dict(self), self.__dict__) - return (__newobj__, (self.__class__,), state) - - - def __init__(self, parent, depth, main, indict=None, name=None): - """ - * parent is the section above - * depth is the depth level of this section - * main is the main ConfigObj - * indict is a dictionary to initialise the section with - """ - if indict is None: - indict = {} - dict.__init__(self) - # used for nesting level *and* interpolation - self.parent = parent - # used for the interpolation attribute - self.main = main - # level of nesting depth of this Section - self.depth = depth - # purely for information - self.name = name - # - self._initialise() - # we do this explicitly so that __setitem__ is used properly - # (rather than just passing to ``dict.__init__``) - for entry, value in indict.iteritems(): - self[entry] = value - - - def _initialise(self): - # the sequence of scalar values in this Section - self.scalars = [] - # the sequence of sections in this Section - self.sections = [] - # for comments :-) - self.comments = {} - self.inline_comments = {} - # the configspec - self.configspec = None - # for defaults - self.defaults = [] - self.default_values = {} - self.extra_values = [] - self._created = False - - - def _interpolate(self, key, value): - try: - # do we already have an interpolation engine? - engine = self._interpolation_engine - except AttributeError: - # not yet: first time running _interpolate(), so pick the engine - name = self.main.interpolation - if name == True: # note that "if name:" would be incorrect here - # backwards-compatibility: interpolation=True means use default - name = DEFAULT_INTERPOLATION - name = name.lower() # so that "Template", "template", etc. all work - class_ = interpolation_engines.get(name, None) - if class_ is None: - # invalid value for self.main.interpolation - self.main.interpolation = False - return value - else: - # save reference to engine so we don't have to do this again - engine = self._interpolation_engine = class_(self) - # let the engine do the actual work - return engine.interpolate(key, value) - - - def __getitem__(self, key): - """Fetch the item and do string interpolation.""" - val = dict.__getitem__(self, key) - if self.main.interpolation: - if isinstance(val, basestring): - return self._interpolate(key, val) - if isinstance(val, list): - def _check(entry): - if isinstance(entry, basestring): - return self._interpolate(key, entry) - return entry - new = [_check(entry) for entry in val] - if new != val: - return new - return val - - - def __setitem__(self, key, value, unrepr=False): - """ - Correctly set a value. - - Making dictionary values Section instances. - (We have to special case 'Section' instances - which are also dicts) - - Keys must be strings. - Values need only be strings (or lists of strings) if - ``main.stringify`` is set. - - ``unrepr`` must be set when setting a value to a dictionary, without - creating a new sub-section. - """ - if not isinstance(key, basestring): - raise ValueError('The key "%s" is not a string.' % key) - - # add the comment - if key not in self.comments: - self.comments[key] = [] - self.inline_comments[key] = '' - # remove the entry from defaults - if key in self.defaults: - self.defaults.remove(key) - # - if isinstance(value, Section): - if key not in self: - self.sections.append(key) - dict.__setitem__(self, key, value) - elif isinstance(value, dict) and not unrepr: - # First create the new depth level, - # then create the section - if key not in self: - self.sections.append(key) - new_depth = self.depth + 1 - dict.__setitem__( - self, - key, - Section( - self, - new_depth, - self.main, - indict=value, - name=key)) - else: - if key not in self: - self.scalars.append(key) - if not self.main.stringify: - if isinstance(value, basestring): - pass - elif isinstance(value, (list, tuple)): - for entry in value: - if not isinstance(entry, basestring): - raise TypeError('Value is not a string "%s".' % entry) - else: - raise TypeError('Value is not a string "%s".' % value) - dict.__setitem__(self, key, value) - - - def __delitem__(self, key): - """Remove items from the sequence when deleting.""" - dict. __delitem__(self, key) - if key in self.scalars: - self.scalars.remove(key) - else: - self.sections.remove(key) - del self.comments[key] - del self.inline_comments[key] - - - def get(self, key, default=None): - """A version of ``get`` that doesn't bypass string interpolation.""" - try: - return self[key] - except KeyError: - return default - - - def update(self, indict): - """ - A version of update that uses our ``__setitem__``. - """ - for entry in indict: - self[entry] = indict[entry] - - - def pop(self, key, default=MISSING): - """ - 'D.pop(k[,d]) -> v, remove specified key and return the corresponding value. - If key is not found, d is returned if given, otherwise KeyError is raised' - """ - try: - val = self[key] - except KeyError: - if default is MISSING: - raise - val = default - else: - del self[key] - return val - - - def popitem(self): - """Pops the first (key,val)""" - sequence = (self.scalars + self.sections) - if not sequence: - raise KeyError(": 'popitem(): dictionary is empty'") - key = sequence[0] - val = self[key] - del self[key] - return key, val - - - def clear(self): - """ - A version of clear that also affects scalars/sections - Also clears comments and configspec. - - Leaves other attributes alone : - depth/main/parent are not affected - """ - dict.clear(self) - self.scalars = [] - self.sections = [] - self.comments = {} - self.inline_comments = {} - self.configspec = None - self.defaults = [] - self.extra_values = [] - - - def setdefault(self, key, default=None): - """A version of setdefault that sets sequence if appropriate.""" - try: - return self[key] - except KeyError: - self[key] = default - return self[key] - - - def items(self): - """D.items() -> list of D's (key, value) pairs, as 2-tuples""" - return zip((self.scalars + self.sections), self.values()) - - - def keys(self): - """D.keys() -> list of D's keys""" - return (self.scalars + self.sections) - - - def values(self): - """D.values() -> list of D's values""" - return [self[key] for key in (self.scalars + self.sections)] - - - def iteritems(self): - """D.iteritems() -> an iterator over the (key, value) items of D""" - return iter(self.items()) - - - def iterkeys(self): - """D.iterkeys() -> an iterator over the keys of D""" - return iter((self.scalars + self.sections)) - - __iter__ = iterkeys - - - def itervalues(self): - """D.itervalues() -> an iterator over the values of D""" - return iter(self.values()) - - - def __repr__(self): - """x.__repr__() <==> repr(x)""" - def _getval(key): - try: - return self[key] - except MissingInterpolationOption: - return dict.__getitem__(self, key) - return '{%s}' % ', '.join([('%s: %s' % (repr(key), repr(_getval(key)))) - for key in (self.scalars + self.sections)]) - - __str__ = __repr__ - __str__.__doc__ = "x.__str__() <==> str(x)" - - - # Extra methods - not in a normal dictionary - - def dict(self): - """ - Return a deepcopy of self as a dictionary. - - All members that are ``Section`` instances are recursively turned to - ordinary dictionaries - by calling their ``dict`` method. - - >>> n = a.dict() - >>> n == a - 1 - >>> n is a - 0 - """ - newdict = {} - for entry in self: - this_entry = self[entry] - if isinstance(this_entry, Section): - this_entry = this_entry.dict() - elif isinstance(this_entry, list): - # create a copy rather than a reference - this_entry = list(this_entry) - elif isinstance(this_entry, tuple): - # create a copy rather than a reference - this_entry = tuple(this_entry) - newdict[entry] = this_entry - return newdict - - - def merge(self, indict): - """ - A recursive update - useful for merging config files. - - >>> a = '''[section1] - ... option1 = True - ... [[subsection]] - ... more_options = False - ... # end of file'''.splitlines() - >>> b = '''# File is user.ini - ... [section1] - ... option1 = False - ... # end of file'''.splitlines() - >>> c1 = ConfigObj(b) - >>> c2 = ConfigObj(a) - >>> c2.merge(c1) - >>> c2 - ConfigObj({'section1': {'option1': 'False', 'subsection': {'more_options': 'False'}}}) - """ - for key, val in indict.items(): - if (key in self and isinstance(self[key], dict) and - isinstance(val, dict)): - self[key].merge(val) - else: - self[key] = val - - - def rename(self, oldkey, newkey): - """ - Change a keyname to another, without changing position in sequence. - - Implemented so that transformations can be made on keys, - as well as on values. (used by encode and decode) - - Also renames comments. - """ - if oldkey in self.scalars: - the_list = self.scalars - elif oldkey in self.sections: - the_list = self.sections - else: - raise KeyError('Key "%s" not found.' % oldkey) - pos = the_list.index(oldkey) - # - val = self[oldkey] - dict.__delitem__(self, oldkey) - dict.__setitem__(self, newkey, val) - the_list.remove(oldkey) - the_list.insert(pos, newkey) - comm = self.comments[oldkey] - inline_comment = self.inline_comments[oldkey] - del self.comments[oldkey] - del self.inline_comments[oldkey] - self.comments[newkey] = comm - self.inline_comments[newkey] = inline_comment - - - def walk(self, function, raise_errors=True, - call_on_sections=False, **keywargs): - """ - Walk every member and call a function on the keyword and value. - - Return a dictionary of the return values - - If the function raises an exception, raise the errror - unless ``raise_errors=False``, in which case set the return value to - ``False``. - - Any unrecognised keyword arguments you pass to walk, will be pased on - to the function you pass in. - - Note: if ``call_on_sections`` is ``True`` then - on encountering a - subsection, *first* the function is called for the *whole* subsection, - and then recurses into it's members. This means your function must be - able to handle strings, dictionaries and lists. This allows you - to change the key of subsections as well as for ordinary members. The - return value when called on the whole subsection has to be discarded. - - See the encode and decode methods for examples, including functions. - - .. admonition:: caution - - You can use ``walk`` to transform the names of members of a section - but you mustn't add or delete members. - - >>> config = '''[XXXXsection] - ... XXXXkey = XXXXvalue'''.splitlines() - >>> cfg = ConfigObj(config) - >>> cfg - ConfigObj({'XXXXsection': {'XXXXkey': 'XXXXvalue'}}) - >>> def transform(section, key): - ... val = section[key] - ... newkey = key.replace('XXXX', 'CLIENT1') - ... section.rename(key, newkey) - ... if isinstance(val, (tuple, list, dict)): - ... pass - ... else: - ... val = val.replace('XXXX', 'CLIENT1') - ... section[newkey] = val - >>> cfg.walk(transform, call_on_sections=True) - {'CLIENT1section': {'CLIENT1key': None}} - >>> cfg - ConfigObj({'CLIENT1section': {'CLIENT1key': 'CLIENT1value'}}) - """ - out = {} - # scalars first - for i in range(len(self.scalars)): - entry = self.scalars[i] - try: - val = function(self, entry, **keywargs) - # bound again in case name has changed - entry = self.scalars[i] - out[entry] = val - except Exception: - if raise_errors: - raise - else: - entry = self.scalars[i] - out[entry] = False - # then sections - for i in range(len(self.sections)): - entry = self.sections[i] - if call_on_sections: - try: - function(self, entry, **keywargs) - except Exception: - if raise_errors: - raise - else: - entry = self.sections[i] - out[entry] = False - # bound again in case name has changed - entry = self.sections[i] - # previous result is discarded - out[entry] = self[entry].walk( - function, - raise_errors=raise_errors, - call_on_sections=call_on_sections, - **keywargs) - return out - - - def as_bool(self, key): - """ - Accepts a key as input. The corresponding value must be a string or - the objects (``True`` or 1) or (``False`` or 0). We allow 0 and 1 to - retain compatibility with Python 2.2. - - If the string is one of ``True``, ``On``, ``Yes``, or ``1`` it returns - ``True``. - - If the string is one of ``False``, ``Off``, ``No``, or ``0`` it returns - ``False``. - - ``as_bool`` is not case sensitive. - - Any other input will raise a ``ValueError``. - - >>> a = ConfigObj() - >>> a['a'] = 'fish' - >>> a.as_bool('a') - Traceback (most recent call last): - ValueError: Value "fish" is neither True nor False - >>> a['b'] = 'True' - >>> a.as_bool('b') - 1 - >>> a['b'] = 'off' - >>> a.as_bool('b') - 0 - """ - val = self[key] - if val == True: - return True - elif val == False: - return False - else: - try: - if not isinstance(val, basestring): - # TODO: Why do we raise a KeyError here? - raise KeyError() - else: - return self.main._bools[val.lower()] - except KeyError: - raise ValueError('Value "%s" is neither True nor False' % val) - - - def as_int(self, key): - """ - A convenience method which coerces the specified value to an integer. - - If the value is an invalid literal for ``int``, a ``ValueError`` will - be raised. - - >>> a = ConfigObj() - >>> a['a'] = 'fish' - >>> a.as_int('a') - Traceback (most recent call last): - ValueError: invalid literal for int() with base 10: 'fish' - >>> a['b'] = '1' - >>> a.as_int('b') - 1 - >>> a['b'] = '3.2' - >>> a.as_int('b') - Traceback (most recent call last): - ValueError: invalid literal for int() with base 10: '3.2' - """ - return int(self[key]) - - - def as_float(self, key): - """ - A convenience method which coerces the specified value to a float. - - If the value is an invalid literal for ``float``, a ``ValueError`` will - be raised. - - >>> a = ConfigObj() - >>> a['a'] = 'fish' - >>> a.as_float('a') - Traceback (most recent call last): - ValueError: invalid literal for float(): fish - >>> a['b'] = '1' - >>> a.as_float('b') - 1.0 - >>> a['b'] = '3.2' - >>> a.as_float('b') - 3.2000000000000002 - """ - return float(self[key]) - - - def as_list(self, key): - """ - A convenience method which fetches the specified value, guaranteeing - that it is a list. - - >>> a = ConfigObj() - >>> a['a'] = 1 - >>> a.as_list('a') - [1] - >>> a['a'] = (1,) - >>> a.as_list('a') - [1] - >>> a['a'] = [1] - >>> a.as_list('a') - [1] - """ - result = self[key] - if isinstance(result, (tuple, list)): - return list(result) - return [result] - - - def restore_default(self, key): - """ - Restore (and return) default value for the specified key. - - This method will only work for a ConfigObj that was created - with a configspec and has been validated. - - If there is no default value for this key, ``KeyError`` is raised. - """ - default = self.default_values[key] - dict.__setitem__(self, key, default) - if key not in self.defaults: - self.defaults.append(key) - return default - - - def restore_defaults(self): - """ - Recursively restore default values to all members - that have them. - - This method will only work for a ConfigObj that was created - with a configspec and has been validated. - - It doesn't delete or modify entries without default values. - """ - for key in self.default_values: - self.restore_default(key) - - for section in self.sections: - self[section].restore_defaults() - - -class ConfigObj(Section): - """An object to read, create, and write config files.""" - - _keyword = re.compile(r'''^ # line start - (\s*) # indentation - ( # keyword - (?:".*?")| # double quotes - (?:'.*?')| # single quotes - (?:[^'"=].*?) # no quotes - ) - \s*=\s* # divider - (.*) # value (including list values and comments) - $ # line end - ''', - re.VERBOSE) - - _sectionmarker = re.compile(r'''^ - (\s*) # 1: indentation - ((?:\[\s*)+) # 2: section marker open - ( # 3: section name open - (?:"\s*\S.*?\s*")| # at least one non-space with double quotes - (?:'\s*\S.*?\s*')| # at least one non-space with single quotes - (?:[^'"\s].*?) # at least one non-space unquoted - ) # section name close - ((?:\s*\])+) # 4: section marker close - \s*(\#.*)? # 5: optional comment - $''', - re.VERBOSE) - - # this regexp pulls list values out as a single string - # or single values and comments - # FIXME: this regex adds a '' to the end of comma terminated lists - # workaround in ``_handle_value`` - _valueexp = re.compile(r'''^ - (?: - (?: - ( - (?: - (?: - (?:".*?")| # double quotes - (?:'.*?')| # single quotes - (?:[^'",\#][^,\#]*?) # unquoted - ) - \s*,\s* # comma - )* # match all list items ending in a comma (if any) - ) - ( - (?:".*?")| # double quotes - (?:'.*?')| # single quotes - (?:[^'",\#\s][^,]*?)| # unquoted - (?:(? 1: - msg = "Parsing failed with several errors.\nFirst error %s" % info - error = ConfigObjError(msg) - else: - error = self._errors[0] - # set the errors attribute; it's a list of tuples: - # (error_type, message, line_number) - error.errors = self._errors - # set the config attribute - error.config = self - raise error - # delete private attributes - del self._errors - - if configspec is None: - self.configspec = None - else: - self._handle_configspec(configspec) - - - def _initialise(self, options=None): - if options is None: - options = OPTION_DEFAULTS - - # initialise a few variables - self.filename = None - self._errors = [] - self.raise_errors = options['raise_errors'] - self.interpolation = options['interpolation'] - self.list_values = options['list_values'] - self.create_empty = options['create_empty'] - self.file_error = options['file_error'] - self.stringify = options['stringify'] - self.indent_type = options['indent_type'] - self.encoding = options['encoding'] - self.default_encoding = options['default_encoding'] - self.BOM = False - self.newlines = None - self.write_empty_values = options['write_empty_values'] - self.unrepr = options['unrepr'] - - self.initial_comment = [] - self.final_comment = [] - self.configspec = None - - if self._inspec: - self.list_values = False - - # Clear section attributes as well - Section._initialise(self) - - - def __repr__(self): - def _getval(key): - try: - return self[key] - except MissingInterpolationOption: - return dict.__getitem__(self, key) - return ('ConfigObj({%s})' % - ', '.join([('%s: %s' % (repr(key), repr(_getval(key)))) - for key in (self.scalars + self.sections)])) - - - def _handle_bom(self, infile): - """ - Handle any BOM, and decode if necessary. - - If an encoding is specified, that *must* be used - but the BOM should - still be removed (and the BOM attribute set). - - (If the encoding is wrongly specified, then a BOM for an alternative - encoding won't be discovered or removed.) - - If an encoding is not specified, UTF8 or UTF16 BOM will be detected and - removed. The BOM attribute will be set. UTF16 will be decoded to - unicode. - - NOTE: This method must not be called with an empty ``infile``. - - Specifying the *wrong* encoding is likely to cause a - ``UnicodeDecodeError``. - - ``infile`` must always be returned as a list of lines, but may be - passed in as a single string. - """ - if ((self.encoding is not None) and - (self.encoding.lower() not in BOM_LIST)): - # No need to check for a BOM - # the encoding specified doesn't have one - # just decode - return self._decode(infile, self.encoding) - - if isinstance(infile, (list, tuple)): - line = infile[0] - else: - line = infile - if self.encoding is not None: - # encoding explicitly supplied - # And it could have an associated BOM - # TODO: if encoding is just UTF16 - we ought to check for both - # TODO: big endian and little endian versions. - enc = BOM_LIST[self.encoding.lower()] - if enc == 'utf_16': - # For UTF16 we try big endian and little endian - for BOM, (encoding, final_encoding) in BOMS.items(): - if not final_encoding: - # skip UTF8 - continue - if infile.startswith(BOM): - ### BOM discovered - ##self.BOM = True - # Don't need to remove BOM - return self._decode(infile, encoding) - - # If we get this far, will *probably* raise a DecodeError - # As it doesn't appear to start with a BOM - return self._decode(infile, self.encoding) - - # Must be UTF8 - BOM = BOM_SET[enc] - if not line.startswith(BOM): - return self._decode(infile, self.encoding) - - newline = line[len(BOM):] - - # BOM removed - if isinstance(infile, (list, tuple)): - infile[0] = newline - else: - infile = newline - self.BOM = True - return self._decode(infile, self.encoding) - - # No encoding specified - so we need to check for UTF8/UTF16 - for BOM, (encoding, final_encoding) in BOMS.items(): - if not line.startswith(BOM): - continue - else: - # BOM discovered - self.encoding = final_encoding - if not final_encoding: - self.BOM = True - # UTF8 - # remove BOM - newline = line[len(BOM):] - if isinstance(infile, (list, tuple)): - infile[0] = newline - else: - infile = newline - # UTF8 - don't decode - if isinstance(infile, basestring): - return infile.splitlines(True) - else: - return infile - # UTF16 - have to decode - return self._decode(infile, encoding) - - # No BOM discovered and no encoding specified, just return - if isinstance(infile, basestring): - # infile read from a file will be a single string - return infile.splitlines(True) - return infile - - - def _a_to_u(self, aString): - """Decode ASCII strings to unicode if a self.encoding is specified.""" - if self.encoding: - return aString.decode('ascii') - else: - return aString - - - def _decode(self, infile, encoding): - """ - Decode infile to unicode. Using the specified encoding. - - if is a string, it also needs converting to a list. - """ - if isinstance(infile, basestring): - # can't be unicode - # NOTE: Could raise a ``UnicodeDecodeError`` - return infile.decode(encoding).splitlines(True) - for i, line in enumerate(infile): - if not isinstance(line, unicode): - # NOTE: The isinstance test here handles mixed lists of unicode/string - # NOTE: But the decode will break on any non-string values - # NOTE: Or could raise a ``UnicodeDecodeError`` - infile[i] = line.decode(encoding) - return infile - - - def _decode_element(self, line): - """Decode element to unicode if necessary.""" - if not self.encoding: - return line - if isinstance(line, str) and self.default_encoding: - return line.decode(self.default_encoding) - return line - - - def _str(self, value): - """ - Used by ``stringify`` within validate, to turn non-string values - into strings. - """ - if not isinstance(value, basestring): - return str(value) - else: - return value - - - def _parse(self, infile): - """Actually parse the config file.""" - temp_list_values = self.list_values - if self.unrepr: - self.list_values = False - - comment_list = [] - done_start = False - this_section = self - maxline = len(infile) - 1 - cur_index = -1 - reset_comment = False - - while cur_index < maxline: - if reset_comment: - comment_list = [] - cur_index += 1 - line = infile[cur_index] - sline = line.strip() - # do we have anything on the line ? - if not sline or sline.startswith('#'): - reset_comment = False - comment_list.append(line) - continue - - if not done_start: - # preserve initial comment - self.initial_comment = comment_list - comment_list = [] - done_start = True - - reset_comment = True - # first we check if it's a section marker - mat = self._sectionmarker.match(line) - if mat is not None: - # is a section line - (indent, sect_open, sect_name, sect_close, comment) = mat.groups() - if indent and (self.indent_type is None): - self.indent_type = indent - cur_depth = sect_open.count('[') - if cur_depth != sect_close.count(']'): - self._handle_error("Cannot compute the section depth at line %s.", - NestingError, infile, cur_index) - continue - - if cur_depth < this_section.depth: - # the new section is dropping back to a previous level - try: - parent = self._match_depth(this_section, - cur_depth).parent - except SyntaxError: - self._handle_error("Cannot compute nesting level at line %s.", - NestingError, infile, cur_index) - continue - elif cur_depth == this_section.depth: - # the new section is a sibling of the current section - parent = this_section.parent - elif cur_depth == this_section.depth + 1: - # the new section is a child the current section - parent = this_section - else: - self._handle_error("Section too nested at line %s.", - NestingError, infile, cur_index) - - sect_name = self._unquote(sect_name) - if sect_name in parent: - self._handle_error('Duplicate section name at line %s.', - DuplicateError, infile, cur_index) - continue - - # create the new section - this_section = Section( - parent, - cur_depth, - self, - name=sect_name) - parent[sect_name] = this_section - parent.inline_comments[sect_name] = comment - parent.comments[sect_name] = comment_list - continue - # - # it's not a section marker, - # so it should be a valid ``key = value`` line - mat = self._keyword.match(line) - if mat is None: - # it neither matched as a keyword - # or a section marker - self._handle_error( - 'Invalid line at line "%s".', - ParseError, infile, cur_index) - else: - # is a keyword value - # value will include any inline comment - (indent, key, value) = mat.groups() - if indent and (self.indent_type is None): - self.indent_type = indent - # check for a multiline value - if value[:3] in ['"""', "'''"]: - try: - value, comment, cur_index = self._multiline( - value, infile, cur_index, maxline) - except SyntaxError: - self._handle_error( - 'Parse error in value at line %s.', - ParseError, infile, cur_index) - continue - else: - if self.unrepr: - comment = '' - try: - value = unrepr(value) - except Exception, e: - if type(e) == UnknownType: - msg = 'Unknown name or type in value at line %s.' - else: - msg = 'Parse error in value at line %s.' - self._handle_error(msg, UnreprError, infile, - cur_index) - continue - else: - if self.unrepr: - comment = '' - try: - value = unrepr(value) - except Exception, e: - if isinstance(e, UnknownType): - msg = 'Unknown name or type in value at line %s.' - else: - msg = 'Parse error in value at line %s.' - self._handle_error(msg, UnreprError, infile, - cur_index) - continue - else: - # extract comment and lists - try: - (value, comment) = self._handle_value(value) - except SyntaxError: - self._handle_error( - 'Parse error in value at line %s.', - ParseError, infile, cur_index) - continue - # - key = self._unquote(key) - if key in this_section: - self._handle_error( - 'Duplicate keyword name at line %s.', - DuplicateError, infile, cur_index) - continue - # add the key. - # we set unrepr because if we have got this far we will never - # be creating a new section - this_section.__setitem__(key, value, unrepr=True) - this_section.inline_comments[key] = comment - this_section.comments[key] = comment_list - continue - # - if self.indent_type is None: - # no indentation used, set the type accordingly - self.indent_type = '' - - # preserve the final comment - if not self and not self.initial_comment: - self.initial_comment = comment_list - elif not reset_comment: - self.final_comment = comment_list - self.list_values = temp_list_values - - - def _match_depth(self, sect, depth): - """ - Given a section and a depth level, walk back through the sections - parents to see if the depth level matches a previous section. - - Return a reference to the right section, - or raise a SyntaxError. - """ - while depth < sect.depth: - if sect is sect.parent: - # we've reached the top level already - raise SyntaxError() - sect = sect.parent - if sect.depth == depth: - return sect - # shouldn't get here - raise SyntaxError() - - - def _handle_error(self, text, ErrorClass, infile, cur_index): - """ - Handle an error according to the error settings. - - Either raise the error or store it. - The error will have occured at ``cur_index`` - """ - line = infile[cur_index] - cur_index += 1 - message = text % cur_index - error = ErrorClass(message, cur_index, line) - if self.raise_errors: - # raise the error - parsing stops here - raise error - # store the error - # reraise when parsing has finished - self._errors.append(error) - - - def _unquote(self, value): - """Return an unquoted version of a value""" - if not value: - # should only happen during parsing of lists - raise SyntaxError - if (value[0] == value[-1]) and (value[0] in ('"', "'")): - value = value[1:-1] - return value - - - def _quote(self, value, multiline=True): - """ - Return a safely quoted version of a value. - - Raise a ConfigObjError if the value cannot be safely quoted. - If multiline is ``True`` (default) then use triple quotes - if necessary. - - * Don't quote values that don't need it. - * Recursively quote members of a list and return a comma joined list. - * Multiline is ``False`` for lists. - * Obey list syntax for empty and single member lists. - - If ``list_values=False`` then the value is only quoted if it contains - a ``\\n`` (is multiline) or '#'. - - If ``write_empty_values`` is set, and the value is an empty string, it - won't be quoted. - """ - if multiline and self.write_empty_values and value == '': - # Only if multiline is set, so that it is used for values not - # keys, and not values that are part of a list - return '' - - if multiline and isinstance(value, (list, tuple)): - if not value: - return ',' - elif len(value) == 1: - return self._quote(value[0], multiline=False) + ',' - return ', '.join([self._quote(val, multiline=False) - for val in value]) - if not isinstance(value, basestring): - if self.stringify: - value = str(value) - else: - raise TypeError('Value "%s" is not a string.' % value) - - if not value: - return '""' - - no_lists_no_quotes = not self.list_values and '\n' not in value and '#' not in value - need_triple = multiline and ((("'" in value) and ('"' in value)) or ('\n' in value )) - hash_triple_quote = multiline and not need_triple and ("'" in value) and ('"' in value) and ('#' in value) - check_for_single = (no_lists_no_quotes or not need_triple) and not hash_triple_quote - - if check_for_single: - if not self.list_values: - # we don't quote if ``list_values=False`` - quot = noquot - # for normal values either single or double quotes will do - elif '\n' in value: - # will only happen if multiline is off - e.g. '\n' in key - raise ConfigObjError('Value "%s" cannot be safely quoted.' % value) - elif ((value[0] not in wspace_plus) and - (value[-1] not in wspace_plus) and - (',' not in value)): - quot = noquot - else: - quot = self._get_single_quote(value) - else: - # if value has '\n' or "'" *and* '"', it will need triple quotes - quot = self._get_triple_quote(value) - - if quot == noquot and '#' in value and self.list_values: - quot = self._get_single_quote(value) - - return quot % value - - - def _get_single_quote(self, value): - if ("'" in value) and ('"' in value): - raise ConfigObjError('Value "%s" cannot be safely quoted.' % value) - elif '"' in value: - quot = squot - else: - quot = dquot - return quot - - - def _get_triple_quote(self, value): - if (value.find('"""') != -1) and (value.find("'''") != -1): - raise ConfigObjError('Value "%s" cannot be safely quoted.' % value) - if value.find('"""') == -1: - quot = tdquot - else: - quot = tsquot - return quot - - - def _handle_value(self, value): - """ - Given a value string, unquote, remove comment, - handle lists. (including empty and single member lists) - """ - if self._inspec: - # Parsing a configspec so don't handle comments - return (value, '') - # do we look for lists in values ? - if not self.list_values: - mat = self._nolistvalue.match(value) - if mat is None: - raise SyntaxError() - # NOTE: we don't unquote here - return mat.groups() - # - mat = self._valueexp.match(value) - if mat is None: - # the value is badly constructed, probably badly quoted, - # or an invalid list - raise SyntaxError() - (list_values, single, empty_list, comment) = mat.groups() - if (list_values == '') and (single is None): - # change this if you want to accept empty values - raise SyntaxError() - # NOTE: note there is no error handling from here if the regex - # is wrong: then incorrect values will slip through - if empty_list is not None: - # the single comma - meaning an empty list - return ([], comment) - if single is not None: - # handle empty values - if list_values and not single: - # FIXME: the '' is a workaround because our regex now matches - # '' at the end of a list if it has a trailing comma - single = None - else: - single = single or '""' - single = self._unquote(single) - if list_values == '': - # not a list value - return (single, comment) - the_list = self._listvalueexp.findall(list_values) - the_list = [self._unquote(val) for val in the_list] - if single is not None: - the_list += [single] - return (the_list, comment) - - - def _multiline(self, value, infile, cur_index, maxline): - """Extract the value, where we are in a multiline situation.""" - quot = value[:3] - newvalue = value[3:] - single_line = self._triple_quote[quot][0] - multi_line = self._triple_quote[quot][1] - mat = single_line.match(value) - if mat is not None: - retval = list(mat.groups()) - retval.append(cur_index) - return retval - elif newvalue.find(quot) != -1: - # somehow the triple quote is missing - raise SyntaxError() - # - while cur_index < maxline: - cur_index += 1 - newvalue += '\n' - line = infile[cur_index] - if line.find(quot) == -1: - newvalue += line - else: - # end of multiline, process it - break - else: - # we've got to the end of the config, oops... - raise SyntaxError() - mat = multi_line.match(line) - if mat is None: - # a badly formed line - raise SyntaxError() - (value, comment) = mat.groups() - return (newvalue + value, comment, cur_index) - - - def _handle_configspec(self, configspec): - """Parse the configspec.""" - # FIXME: Should we check that the configspec was created with the - # correct settings ? (i.e. ``list_values=False``) - if not isinstance(configspec, ConfigObj): - try: - configspec = ConfigObj(configspec, - raise_errors=True, - file_error=True, - _inspec=True) - except ConfigObjError, e: - # FIXME: Should these errors have a reference - # to the already parsed ConfigObj ? - raise ConfigspecError('Parsing configspec failed: %s' % e) - except IOError, e: - raise IOError('Reading configspec failed: %s' % e) - - self.configspec = configspec - - - - def _set_configspec(self, section, copy): - """ - Called by validate. Handles setting the configspec on subsections - including sections to be validated by __many__ - """ - configspec = section.configspec - many = configspec.get('__many__') - if isinstance(many, dict): - for entry in section.sections: - if entry not in configspec: - section[entry].configspec = many - - for entry in configspec.sections: - if entry == '__many__': - continue - if entry not in section: - section[entry] = {} - section[entry]._created = True - if copy: - # copy comments - section.comments[entry] = configspec.comments.get(entry, []) - section.inline_comments[entry] = configspec.inline_comments.get(entry, '') - - # Could be a scalar when we expect a section - if isinstance(section[entry], Section): - section[entry].configspec = configspec[entry] - - - def _write_line(self, indent_string, entry, this_entry, comment): - """Write an individual line, for the write method""" - # NOTE: the calls to self._quote here handles non-StringType values. - if not self.unrepr: - val = self._decode_element(self._quote(this_entry)) - else: - val = repr(this_entry) - return '%s%s%s%s%s' % (indent_string, - self._decode_element(self._quote(entry, multiline=False)), - self._a_to_u(' = '), - val, - self._decode_element(comment)) - - - def _write_marker(self, indent_string, depth, entry, comment): - """Write a section marker line""" - return '%s%s%s%s%s' % (indent_string, - self._a_to_u('[' * depth), - self._quote(self._decode_element(entry), multiline=False), - self._a_to_u(']' * depth), - self._decode_element(comment)) - - - def _handle_comment(self, comment): - """Deal with a comment.""" - if not comment: - return '' - start = self.indent_type - if not comment.startswith('#'): - start += self._a_to_u(' # ') - return (start + comment) - - - # Public methods - - def write(self, outfile=None, section=None): - """ - Write the current ConfigObj as a file - - tekNico: FIXME: use StringIO instead of real files - - >>> filename = a.filename - >>> a.filename = 'test.ini' - >>> a.write() - >>> a.filename = filename - >>> a == ConfigObj('test.ini', raise_errors=True) - 1 - >>> import os - >>> os.remove('test.ini') - """ - if self.indent_type is None: - # this can be true if initialised from a dictionary - self.indent_type = DEFAULT_INDENT_TYPE - - out = [] - cs = self._a_to_u('#') - csp = self._a_to_u('# ') - if section is None: - int_val = self.interpolation - self.interpolation = False - section = self - for line in self.initial_comment: - line = self._decode_element(line) - stripped_line = line.strip() - if stripped_line and not stripped_line.startswith(cs): - line = csp + line - out.append(line) - - indent_string = self.indent_type * section.depth - for entry in (section.scalars + section.sections): - if entry in section.defaults: - # don't write out default values - continue - for comment_line in section.comments[entry]: - comment_line = self._decode_element(comment_line.lstrip()) - if comment_line and not comment_line.startswith(cs): - comment_line = csp + comment_line - out.append(indent_string + comment_line) - this_entry = section[entry] - comment = self._handle_comment(section.inline_comments[entry]) - - if isinstance(this_entry, dict): - # a section - out.append(self._write_marker( - indent_string, - this_entry.depth, - entry, - comment)) - out.extend(self.write(section=this_entry)) - else: - out.append(self._write_line( - indent_string, - entry, - this_entry, - comment)) - - if section is self: - for line in self.final_comment: - line = self._decode_element(line) - stripped_line = line.strip() - if stripped_line and not stripped_line.startswith(cs): - line = csp + line - out.append(line) - self.interpolation = int_val - - if section is not self: - return out - - if (self.filename is None) and (outfile is None): - # output a list of lines - # might need to encode - # NOTE: This will *screw* UTF16, each line will start with the BOM - if self.encoding: - out = [l.encode(self.encoding) for l in out] - if (self.BOM and ((self.encoding is None) or - (BOM_LIST.get(self.encoding.lower()) == 'utf_8'))): - # Add the UTF8 BOM - if not out: - out.append('') - out[0] = BOM_UTF8 + out[0] - return out - - # Turn the list to a string, joined with correct newlines - newline = self.newlines or os.linesep - if (getattr(outfile, 'mode', None) is not None and outfile.mode == 'w' - and sys.platform == 'win32' and newline == '\r\n'): - # Windows specific hack to avoid writing '\r\r\n' - newline = '\n' - output = self._a_to_u(newline).join(out) - if self.encoding: - output = output.encode(self.encoding) - if self.BOM and ((self.encoding is None) or match_utf8(self.encoding)): - # Add the UTF8 BOM - output = BOM_UTF8 + output - - if not output.endswith(newline): - output += newline - if outfile is not None: - outfile.write(output) - else: - h = open(self.filename, 'wb') - h.write(output) - h.close() - - - def validate(self, validator, preserve_errors=False, copy=False, - section=None): - """ - Test the ConfigObj against a configspec. - - It uses the ``validator`` object from *validate.py*. - - To run ``validate`` on the current ConfigObj, call: :: - - test = config.validate(validator) - - (Normally having previously passed in the configspec when the ConfigObj - was created - you can dynamically assign a dictionary of checks to the - ``configspec`` attribute of a section though). - - It returns ``True`` if everything passes, or a dictionary of - pass/fails (True/False). If every member of a subsection passes, it - will just have the value ``True``. (It also returns ``False`` if all - members fail). - - In addition, it converts the values from strings to their native - types if their checks pass (and ``stringify`` is set). - - If ``preserve_errors`` is ``True`` (``False`` is default) then instead - of a marking a fail with a ``False``, it will preserve the actual - exception object. This can contain info about the reason for failure. - For example the ``VdtValueTooSmallError`` indicates that the value - supplied was too small. If a value (or section) is missing it will - still be marked as ``False``. - - You must have the validate module to use ``preserve_errors=True``. - - You can then use the ``flatten_errors`` function to turn your nested - results dictionary into a flattened list of failures - useful for - displaying meaningful error messages. - """ - if section is None: - if self.configspec is None: - raise ValueError('No configspec supplied.') - if preserve_errors: - # We do this once to remove a top level dependency on the validate module - # Which makes importing configobj faster - from validate import VdtMissingValue - self._vdtMissingValue = VdtMissingValue - - section = self - - if copy: - section.initial_comment = section.configspec.initial_comment - section.final_comment = section.configspec.final_comment - section.encoding = section.configspec.encoding - section.BOM = section.configspec.BOM - section.newlines = section.configspec.newlines - section.indent_type = section.configspec.indent_type - - # - # section.default_values.clear() #?? - configspec = section.configspec - self._set_configspec(section, copy) - - - def validate_entry(entry, spec, val, missing, ret_true, ret_false): - section.default_values.pop(entry, None) - - try: - section.default_values[entry] = validator.get_default_value(configspec[entry]) - except (KeyError, AttributeError, validator.baseErrorClass): - # No default, bad default or validator has no 'get_default_value' - # (e.g. SimpleVal) - pass - - try: - check = validator.check(spec, - val, - missing=missing - ) - except validator.baseErrorClass, e: - if not preserve_errors or isinstance(e, self._vdtMissingValue): - out[entry] = False - else: - # preserve the error - out[entry] = e - ret_false = False - ret_true = False - else: - ret_false = False - out[entry] = True - if self.stringify or missing: - # if we are doing type conversion - # or the value is a supplied default - if not self.stringify: - if isinstance(check, (list, tuple)): - # preserve lists - check = [self._str(item) for item in check] - elif missing and check is None: - # convert the None from a default to a '' - check = '' - else: - check = self._str(check) - if (check != val) or missing: - section[entry] = check - if not copy and missing and entry not in section.defaults: - section.defaults.append(entry) - return ret_true, ret_false - - # - out = {} - ret_true = True - ret_false = True - - unvalidated = [k for k in section.scalars if k not in configspec] - incorrect_sections = [k for k in configspec.sections if k in section.scalars] - incorrect_scalars = [k for k in configspec.scalars if k in section.sections] - - for entry in configspec.scalars: - if entry in ('__many__', '___many___'): - # reserved names - continue - if (not entry in section.scalars) or (entry in section.defaults): - # missing entries - # or entries from defaults - missing = True - val = None - if copy and entry not in section.scalars: - # copy comments - section.comments[entry] = ( - configspec.comments.get(entry, [])) - section.inline_comments[entry] = ( - configspec.inline_comments.get(entry, '')) - # - else: - missing = False - val = section[entry] - - ret_true, ret_false = validate_entry(entry, configspec[entry], val, - missing, ret_true, ret_false) - - many = None - if '__many__' in configspec.scalars: - many = configspec['__many__'] - elif '___many___' in configspec.scalars: - many = configspec['___many___'] - - if many is not None: - for entry in unvalidated: - val = section[entry] - ret_true, ret_false = validate_entry(entry, many, val, False, - ret_true, ret_false) - unvalidated = [] - - for entry in incorrect_scalars: - ret_true = False - if not preserve_errors: - out[entry] = False - else: - ret_false = False - msg = 'Value %r was provided as a section' % entry - out[entry] = validator.baseErrorClass(msg) - for entry in incorrect_sections: - ret_true = False - if not preserve_errors: - out[entry] = False - else: - ret_false = False - msg = 'Section %r was provided as a single value' % entry - out[entry] = validator.baseErrorClass(msg) - - # Missing sections will have been created as empty ones when the - # configspec was read. - for entry in section.sections: - # FIXME: this means DEFAULT is not copied in copy mode - if section is self and entry == 'DEFAULT': - continue - if section[entry].configspec is None: - unvalidated.append(entry) - continue - if copy: - section.comments[entry] = configspec.comments.get(entry, []) - section.inline_comments[entry] = configspec.inline_comments.get(entry, '') - check = self.validate(validator, preserve_errors=preserve_errors, copy=copy, section=section[entry]) - out[entry] = check - if check == False: - ret_true = False - elif check == True: - ret_false = False - else: - ret_true = False - - section.extra_values = unvalidated - if preserve_errors and not section._created: - # If the section wasn't created (i.e. it wasn't missing) - # then we can't return False, we need to preserve errors - ret_false = False - # - if ret_false and preserve_errors and out: - # If we are preserving errors, but all - # the failures are from missing sections / values - # then we can return False. Otherwise there is a - # real failure that we need to preserve. - ret_false = not any(out.values()) - if ret_true: - return True - elif ret_false: - return False - return out - - - def reset(self): - """Clear ConfigObj instance and restore to 'freshly created' state.""" - self.clear() - self._initialise() - # FIXME: Should be done by '_initialise', but ConfigObj constructor (and reload) - # requires an empty dictionary - self.configspec = None - # Just to be sure ;-) - self._original_configspec = None - - - def reload(self): - """ - Reload a ConfigObj from file. - - This method raises a ``ReloadError`` if the ConfigObj doesn't have - a filename attribute pointing to a file. - """ - if not isinstance(self.filename, basestring): - raise ReloadError() - - filename = self.filename - current_options = {} - for entry in OPTION_DEFAULTS: - if entry == 'configspec': - continue - current_options[entry] = getattr(self, entry) - - configspec = self._original_configspec - current_options['configspec'] = configspec - - self.clear() - self._initialise(current_options) - self._load(filename, configspec) - - - -class SimpleVal(object): - """ - A simple validator. - Can be used to check that all members expected are present. - - To use it, provide a configspec with all your members in (the value given - will be ignored). Pass an instance of ``SimpleVal`` to the ``validate`` - method of your ``ConfigObj``. ``validate`` will return ``True`` if all - members are present, or a dictionary with True/False meaning - present/missing. (Whole missing sections will be replaced with ``False``) - """ - - def __init__(self): - self.baseErrorClass = ConfigObjError - - def check(self, check, member, missing=False): - """A dummy check method, always returns the value unchanged.""" - if missing: - raise self.baseErrorClass() - return member - - -def flatten_errors(cfg, res, levels=None, results=None): - """ - An example function that will turn a nested dictionary of results - (as returned by ``ConfigObj.validate``) into a flat list. - - ``cfg`` is the ConfigObj instance being checked, ``res`` is the results - dictionary returned by ``validate``. - - (This is a recursive function, so you shouldn't use the ``levels`` or - ``results`` arguments - they are used by the function.) - - Returns a list of keys that failed. Each member of the list is a tuple:: - - ([list of sections...], key, result) - - If ``validate`` was called with ``preserve_errors=False`` (the default) - then ``result`` will always be ``False``. - - *list of sections* is a flattened list of sections that the key was found - in. - - If the section was missing (or a section was expected and a scalar provided - - or vice-versa) then key will be ``None``. - - If the value (or section) was missing then ``result`` will be ``False``. - - If ``validate`` was called with ``preserve_errors=True`` and a value - was present, but failed the check, then ``result`` will be the exception - object returned. You can use this as a string that describes the failure. - - For example *The value "3" is of the wrong type*. - """ - if levels is None: - # first time called - levels = [] - results = [] - if res == True: - return results - if res == False or isinstance(res, Exception): - results.append((levels[:], None, res)) - if levels: - levels.pop() - return results - for (key, val) in res.items(): - if val == True: - continue - if isinstance(cfg.get(key), dict): - # Go down one level - levels.append(key) - flatten_errors(cfg[key], val, levels, results) - continue - results.append((levels[:], key, val)) - # - # Go up one level - if levels: - levels.pop() - # - return results - - -def get_extra_values(conf, _prepend=()): - """ - Find all the values and sections not in the configspec from a validated - ConfigObj. - - ``get_extra_values`` returns a list of tuples where each tuple represents - either an extra section, or an extra value. - - The tuples contain two values, a tuple representing the section the value - is in and the name of the extra values. For extra values in the top level - section the first member will be an empty tuple. For values in the 'foo' - section the first member will be ``('foo',)``. For members in the 'bar' - subsection of the 'foo' section the first member will be ``('foo', 'bar')``. - - NOTE: If you call ``get_extra_values`` on a ConfigObj instance that hasn't - been validated it will return an empty list. - """ - out = [] - - out.extend([(_prepend, name) for name in conf.extra_values]) - for name in conf.sections: - if name not in conf.extra_values: - out.extend(get_extra_values(conf[name], _prepend + (name,))) - return out - - -"""*A programming language is a medium of expression.* - Paul Graham""" diff --git a/astropy/extern/configobj_py2/validate.py b/astropy/extern/configobj_py2/validate.py deleted file mode 100644 index 73dbdb891b94..000000000000 --- a/astropy/extern/configobj_py2/validate.py +++ /dev/null @@ -1,1450 +0,0 @@ -# validate.py -# A Validator object -# Copyright (C) 2005-2010 Michael Foord, Mark Andrews, Nicola Larosa -# E-mail: fuzzyman AT voidspace DOT org DOT uk -# mark AT la-la DOT com -# nico AT tekNico DOT net - -# This software is licensed under the terms of the BSD license. -# http://www.voidspace.org.uk/python/license.shtml -# Basically you're free to copy, modify, distribute and relicense it, -# So long as you keep a copy of the license with it. - -# Scripts maintained at http://www.voidspace.org.uk/python/index.shtml -# For information about bugfixes, updates and support, please join the -# ConfigObj mailing list: -# http://lists.sourceforge.net/lists/listinfo/configobj-develop -# Comments, suggestions and bug reports welcome. - -""" - The Validator object is used to check that supplied values - conform to a specification. - - The value can be supplied as a string - e.g. from a config file. - In this case the check will also *convert* the value to - the required type. This allows you to add validation - as a transparent layer to access data stored as strings. - The validation checks that the data is correct *and* - converts it to the expected type. - - Some standard checks are provided for basic data types. - Additional checks are easy to write. They can be - provided when the ``Validator`` is instantiated or - added afterwards. - - The standard functions work with the following basic data types : - - * integers - * floats - * booleans - * strings - * ip_addr - - plus lists of these datatypes - - Adding additional checks is done through coding simple functions. - - The full set of standard checks are : - - * 'integer': matches integer values (including negative) - Takes optional 'min' and 'max' arguments : :: - - integer() - integer(3, 9) # any value from 3 to 9 - integer(min=0) # any positive value - integer(max=9) - - * 'float': matches float values - Has the same parameters as the integer check. - - * 'boolean': matches boolean values - ``True`` or ``False`` - Acceptable string values for True are : - true, on, yes, 1 - Acceptable string values for False are : - false, off, no, 0 - - Any other value raises an error. - - * 'ip_addr': matches an Internet Protocol address, v.4, represented - by a dotted-quad string, i.e. '1.2.3.4'. - - * 'string': matches any string. - Takes optional keyword args 'min' and 'max' - to specify min and max lengths of the string. - - * 'list': matches any list. - Takes optional keyword args 'min', and 'max' to specify min and - max sizes of the list. (Always returns a list.) - - * 'tuple': matches any tuple. - Takes optional keyword args 'min', and 'max' to specify min and - max sizes of the tuple. (Always returns a tuple.) - - * 'int_list': Matches a list of integers. - Takes the same arguments as list. - - * 'float_list': Matches a list of floats. - Takes the same arguments as list. - - * 'bool_list': Matches a list of boolean values. - Takes the same arguments as list. - - * 'ip_addr_list': Matches a list of IP addresses. - Takes the same arguments as list. - - * 'string_list': Matches a list of strings. - Takes the same arguments as list. - - * 'mixed_list': Matches a list with different types in - specific positions. List size must match - the number of arguments. - - Each position can be one of : - 'integer', 'float', 'ip_addr', 'string', 'boolean' - - So to specify a list with two strings followed - by two integers, you write the check as : :: - - mixed_list('string', 'string', 'integer', 'integer') - - * 'pass': This check matches everything ! It never fails - and the value is unchanged. - - It is also the default if no check is specified. - - * 'option': This check matches any from a list of options. - You specify this check with : :: - - option('option 1', 'option 2', 'option 3') - - You can supply a default value (returned if no value is supplied) - using the default keyword argument. - - You specify a list argument for default using a list constructor syntax in - the check : :: - - checkname(arg1, arg2, default=list('val 1', 'val 2', 'val 3')) - - A badly formatted set of arguments will raise a ``VdtParamError``. -""" - -__version__ = '1.0.1' - - -__all__ = ( - '__version__', - 'dottedQuadToNum', - 'numToDottedQuad', - 'ValidateError', - 'VdtUnknownCheckError', - 'VdtParamError', - 'VdtTypeError', - 'VdtValueError', - 'VdtValueTooSmallError', - 'VdtValueTooBigError', - 'VdtValueTooShortError', - 'VdtValueTooLongError', - 'VdtMissingValue', - 'Validator', - 'is_integer', - 'is_float', - 'is_boolean', - 'is_list', - 'is_tuple', - 'is_ip_addr', - 'is_string', - 'is_int_list', - 'is_bool_list', - 'is_float_list', - 'is_string_list', - 'is_ip_addr_list', - 'is_mixed_list', - 'is_option', - '__docformat__', -) - - -import re - - -_list_arg = re.compile(r''' - (?: - ([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*list\( - ( - (?: - \s* - (?: - (?:".*?")| # double quotes - (?:'.*?')| # single quotes - (?:[^'",\s\)][^,\)]*?) # unquoted - ) - \s*,\s* - )* - (?: - (?:".*?")| # double quotes - (?:'.*?')| # single quotes - (?:[^'",\s\)][^,\)]*?) # unquoted - )? # last one - ) - \) - ) -''', re.VERBOSE | re.DOTALL) # two groups - -_list_members = re.compile(r''' - ( - (?:".*?")| # double quotes - (?:'.*?')| # single quotes - (?:[^'",\s=][^,=]*?) # unquoted - ) - (?: - (?:\s*,\s*)|(?:\s*$) # comma - ) -''', re.VERBOSE | re.DOTALL) # one group - -_paramstring = r''' - (?: - ( - (?: - [a-zA-Z_][a-zA-Z0-9_]*\s*=\s*list\( - (?: - \s* - (?: - (?:".*?")| # double quotes - (?:'.*?')| # single quotes - (?:[^'",\s\)][^,\)]*?) # unquoted - ) - \s*,\s* - )* - (?: - (?:".*?")| # double quotes - (?:'.*?')| # single quotes - (?:[^'",\s\)][^,\)]*?) # unquoted - )? # last one - \) - )| - (?: - (?:".*?")| # double quotes - (?:'.*?')| # single quotes - (?:[^'",\s=][^,=]*?)| # unquoted - (?: # keyword argument - [a-zA-Z_][a-zA-Z0-9_]*\s*=\s* - (?: - (?:".*?")| # double quotes - (?:'.*?')| # single quotes - (?:[^'",\s=][^,=]*?) # unquoted - ) - ) - ) - ) - (?: - (?:\s*,\s*)|(?:\s*$) # comma - ) - ) - ''' - -_matchstring = '^%s*' % _paramstring - -# Python pre 2.2.1 doesn't have bool -try: - bool -except NameError: - def bool(val): - """Simple boolean equivalent function. """ - if val: - return 1 - else: - return 0 - - -def dottedQuadToNum(ip): - """ - Convert decimal dotted quad string to long integer - - >>> int(dottedQuadToNum('1 ')) - 1 - >>> int(dottedQuadToNum(' 1.2')) - 16777218 - >>> int(dottedQuadToNum(' 1.2.3 ')) - 16908291 - >>> int(dottedQuadToNum('1.2.3.4')) - 16909060 - >>> dottedQuadToNum('255.255.255.255') - 4294967295L - >>> dottedQuadToNum('255.255.255.256') - Traceback (most recent call last): - ValueError: Not a good dotted-quad IP: 255.255.255.256 - """ - - # import here to avoid it when ip_addr values are not used - import socket, struct - - try: - return struct.unpack('!L', - socket.inet_aton(ip.strip()))[0] - except socket.error: - # bug in inet_aton, corrected in Python 2.4 - if ip.strip() == '255.255.255.255': - return 0xFFFFFFFFL - else: - raise ValueError('Not a good dotted-quad IP: %s' % ip) - return - - -def numToDottedQuad(num): - """ - Convert long int to dotted quad string - - >>> numToDottedQuad(-1L) - Traceback (most recent call last): - ValueError: Not a good numeric IP: -1 - >>> numToDottedQuad(1L) - '0.0.0.1' - >>> numToDottedQuad(16777218L) - '1.0.0.2' - >>> numToDottedQuad(16908291L) - '1.2.0.3' - >>> numToDottedQuad(16909060L) - '1.2.3.4' - >>> numToDottedQuad(4294967295L) - '255.255.255.255' - >>> numToDottedQuad(4294967296L) - Traceback (most recent call last): - ValueError: Not a good numeric IP: 4294967296 - """ - - # import here to avoid it when ip_addr values are not used - import socket, struct - - # no need to intercept here, 4294967295L is fine - if num > 4294967295L or num < 0: - raise ValueError('Not a good numeric IP: %s' % num) - try: - return socket.inet_ntoa( - struct.pack('!L', long(num))) - except (socket.error, struct.error, OverflowError): - raise ValueError('Not a good numeric IP: %s' % num) - - -class ValidateError(Exception): - """ - This error indicates that the check failed. - It can be the base class for more specific errors. - - Any check function that fails ought to raise this error. - (or a subclass) - - >>> raise ValidateError - Traceback (most recent call last): - ValidateError - """ - - -class VdtMissingValue(ValidateError): - """No value was supplied to a check that needed one.""" - - -class VdtUnknownCheckError(ValidateError): - """An unknown check function was requested""" - - def __init__(self, value): - """ - >>> raise VdtUnknownCheckError('yoda') - Traceback (most recent call last): - VdtUnknownCheckError: the check "yoda" is unknown. - """ - ValidateError.__init__(self, 'the check "%s" is unknown.' % (value,)) - - -class VdtParamError(SyntaxError): - """An incorrect parameter was passed""" - - def __init__(self, name, value): - """ - >>> raise VdtParamError('yoda', 'jedi') - Traceback (most recent call last): - VdtParamError: passed an incorrect value "jedi" for parameter "yoda". - """ - SyntaxError.__init__(self, 'passed an incorrect value "%s" for parameter "%s".' % (value, name)) - - -class VdtTypeError(ValidateError): - """The value supplied was of the wrong type""" - - def __init__(self, value): - """ - >>> raise VdtTypeError('jedi') - Traceback (most recent call last): - VdtTypeError: the value "jedi" is of the wrong type. - """ - ValidateError.__init__(self, 'the value "%s" is of the wrong type.' % (value,)) - - -class VdtValueError(ValidateError): - """The value supplied was of the correct type, but was not an allowed value.""" - - def __init__(self, value): - """ - >>> raise VdtValueError('jedi') - Traceback (most recent call last): - VdtValueError: the value "jedi" is unacceptable. - """ - ValidateError.__init__(self, 'the value "%s" is unacceptable.' % (value,)) - - -class VdtValueTooSmallError(VdtValueError): - """The value supplied was of the correct type, but was too small.""" - - def __init__(self, value): - """ - >>> raise VdtValueTooSmallError('0') - Traceback (most recent call last): - VdtValueTooSmallError: the value "0" is too small. - """ - ValidateError.__init__(self, 'the value "%s" is too small.' % (value,)) - - -class VdtValueTooBigError(VdtValueError): - """The value supplied was of the correct type, but was too big.""" - - def __init__(self, value): - """ - >>> raise VdtValueTooBigError('1') - Traceback (most recent call last): - VdtValueTooBigError: the value "1" is too big. - """ - ValidateError.__init__(self, 'the value "%s" is too big.' % (value,)) - - -class VdtValueTooShortError(VdtValueError): - """The value supplied was of the correct type, but was too short.""" - - def __init__(self, value): - """ - >>> raise VdtValueTooShortError('jed') - Traceback (most recent call last): - VdtValueTooShortError: the value "jed" is too short. - """ - ValidateError.__init__( - self, - 'the value "%s" is too short.' % (value,)) - - -class VdtValueTooLongError(VdtValueError): - """The value supplied was of the correct type, but was too long.""" - - def __init__(self, value): - """ - >>> raise VdtValueTooLongError('jedie') - Traceback (most recent call last): - VdtValueTooLongError: the value "jedie" is too long. - """ - ValidateError.__init__(self, 'the value "%s" is too long.' % (value,)) - - -class Validator(object): - """ - Validator is an object that allows you to register a set of 'checks'. - These checks take input and test that it conforms to the check. - - This can also involve converting the value from a string into - the correct datatype. - - The ``check`` method takes an input string which configures which - check is to be used and applies that check to a supplied value. - - An example input string would be: - 'int_range(param1, param2)' - - You would then provide something like: - - >>> def int_range_check(value, min, max): - ... # turn min and max from strings to integers - ... min = int(min) - ... max = int(max) - ... # check that value is of the correct type. - ... # possible valid inputs are integers or strings - ... # that represent integers - ... if not isinstance(value, (int, long, basestring)): - ... raise VdtTypeError(value) - ... elif isinstance(value, basestring): - ... # if we are given a string - ... # attempt to convert to an integer - ... try: - ... value = int(value) - ... except ValueError: - ... raise VdtValueError(value) - ... # check the value is between our constraints - ... if not min <= value: - ... raise VdtValueTooSmallError(value) - ... if not value <= max: - ... raise VdtValueTooBigError(value) - ... return value - - >>> fdict = {'int_range': int_range_check} - >>> vtr1 = Validator(fdict) - >>> vtr1.check('int_range(20, 40)', '30') - 30 - >>> vtr1.check('int_range(20, 40)', '60') - Traceback (most recent call last): - VdtValueTooBigError: the value "60" is too big. - - New functions can be added with : :: - - >>> vtr2 = Validator() - >>> vtr2.functions['int_range'] = int_range_check - - Or by passing in a dictionary of functions when Validator - is instantiated. - - Your functions *can* use keyword arguments, - but the first argument should always be 'value'. - - If the function doesn't take additional arguments, - the parentheses are optional in the check. - It can be written with either of : :: - - keyword = function_name - keyword = function_name() - - The first program to utilise Validator() was Michael Foord's - ConfigObj, an alternative to ConfigParser which supports lists and - can validate a config file using a config schema. - For more details on using Validator with ConfigObj see: - http://www.voidspace.org.uk/python/configobj.html - """ - - # this regex does the initial parsing of the checks - _func_re = re.compile(r'(.+?)\((.*)\)', re.DOTALL) - - # this regex takes apart keyword arguments - _key_arg = re.compile(r'^([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*(.*)$', re.DOTALL) - - - # this regex finds keyword=list(....) type values - _list_arg = _list_arg - - # this regex takes individual values out of lists - in one pass - _list_members = _list_members - - # These regexes check a set of arguments for validity - # and then pull the members out - _paramfinder = re.compile(_paramstring, re.VERBOSE | re.DOTALL) - _matchfinder = re.compile(_matchstring, re.VERBOSE | re.DOTALL) - - - def __init__(self, functions=None): - """ - >>> vtri = Validator() - """ - self.functions = { - '': self._pass, - 'integer': is_integer, - 'float': is_float, - 'boolean': is_boolean, - 'ip_addr': is_ip_addr, - 'string': is_string, - 'list': is_list, - 'tuple': is_tuple, - 'int_list': is_int_list, - 'float_list': is_float_list, - 'bool_list': is_bool_list, - 'ip_addr_list': is_ip_addr_list, - 'string_list': is_string_list, - 'mixed_list': is_mixed_list, - 'pass': self._pass, - 'option': is_option, - 'force_list': force_list, - } - if functions is not None: - self.functions.update(functions) - # tekNico: for use by ConfigObj - self.baseErrorClass = ValidateError - self._cache = {} - - - def check(self, check, value, missing=False): - """ - Usage: check(check, value) - - Arguments: - check: string representing check to apply (including arguments) - value: object to be checked - Returns value, converted to correct type if necessary - - If the check fails, raises a ``ValidateError`` subclass. - - >>> vtor.check('yoda', '') - Traceback (most recent call last): - VdtUnknownCheckError: the check "yoda" is unknown. - >>> vtor.check('yoda()', '') - Traceback (most recent call last): - VdtUnknownCheckError: the check "yoda" is unknown. - - >>> vtor.check('string(default="")', '', missing=True) - '' - """ - fun_name, fun_args, fun_kwargs, default = self._parse_with_caching(check) - - if missing: - if default is None: - # no information needed here - to be handled by caller - raise VdtMissingValue() - value = self._handle_none(default) - - if value is None: - return None - - return self._check_value(value, fun_name, fun_args, fun_kwargs) - - - def _handle_none(self, value): - if value == 'None': - return None - elif value in ("'None'", '"None"'): - # Special case a quoted None - value = self._unquote(value) - return value - - - def _parse_with_caching(self, check): - if check in self._cache: - fun_name, fun_args, fun_kwargs, default = self._cache[check] - # We call list and dict below to work with *copies* of the data - # rather than the original (which are mutable of course) - fun_args = list(fun_args) - fun_kwargs = dict(fun_kwargs) - else: - fun_name, fun_args, fun_kwargs, default = self._parse_check(check) - fun_kwargs = dict([(str(key), value) for (key, value) in fun_kwargs.items()]) - self._cache[check] = fun_name, list(fun_args), dict(fun_kwargs), default - return fun_name, fun_args, fun_kwargs, default - - - def _check_value(self, value, fun_name, fun_args, fun_kwargs): - try: - fun = self.functions[fun_name] - except KeyError: - raise VdtUnknownCheckError(fun_name) - else: - return fun(value, *fun_args, **fun_kwargs) - - - def _parse_check(self, check): - fun_match = self._func_re.match(check) - if fun_match: - fun_name = fun_match.group(1) - arg_string = fun_match.group(2) - arg_match = self._matchfinder.match(arg_string) - if arg_match is None: - # Bad syntax - raise VdtParamError('Bad syntax in check "%s".' % check) - fun_args = [] - fun_kwargs = {} - # pull out args of group 2 - for arg in self._paramfinder.findall(arg_string): - # args may need whitespace removing (before removing quotes) - arg = arg.strip() - listmatch = self._list_arg.match(arg) - if listmatch: - key, val = self._list_handle(listmatch) - fun_kwargs[key] = val - continue - keymatch = self._key_arg.match(arg) - if keymatch: - val = keymatch.group(2) - if not val in ("'None'", '"None"'): - # Special case a quoted None - val = self._unquote(val) - fun_kwargs[keymatch.group(1)] = val - continue - - fun_args.append(self._unquote(arg)) - else: - # allows for function names without (args) - return check, (), {}, None - - # Default must be deleted if the value is specified too, - # otherwise the check function will get a spurious "default" keyword arg - default = fun_kwargs.pop('default', None) - return fun_name, fun_args, fun_kwargs, default - - - def _unquote(self, val): - """Unquote a value if necessary.""" - if (len(val) >= 2) and (val[0] in ("'", '"')) and (val[0] == val[-1]): - val = val[1:-1] - return val - - - def _list_handle(self, listmatch): - """Take apart a ``keyword=list('val, 'val')`` type string.""" - out = [] - name = listmatch.group(1) - args = listmatch.group(2) - for arg in self._list_members.findall(args): - out.append(self._unquote(arg)) - return name, out - - - def _pass(self, value): - """ - Dummy check that always passes - - >>> vtor.check('', 0) - 0 - >>> vtor.check('', '0') - '0' - """ - return value - - - def get_default_value(self, check): - """ - Given a check, return the default value for the check - (converted to the right type). - - If the check doesn't specify a default value then a - ``KeyError`` will be raised. - """ - fun_name, fun_args, fun_kwargs, default = self._parse_with_caching(check) - if default is None: - raise KeyError('Check "%s" has no default value.' % check) - value = self._handle_none(default) - if value is None: - return value - return self._check_value(value, fun_name, fun_args, fun_kwargs) - - -def _is_num_param(names, values, to_float=False): - """ - Return numbers from inputs or raise VdtParamError. - - Lets ``None`` pass through. - Pass in keyword argument ``to_float=True`` to - use float for the conversion rather than int. - - >>> _is_num_param(('', ''), (0, 1.0)) - [0, 1] - >>> _is_num_param(('', ''), (0, 1.0), to_float=True) - [0.0, 1.0] - >>> _is_num_param(('a'), ('a')) - Traceback (most recent call last): - VdtParamError: passed an incorrect value "a" for parameter "a". - """ - fun = to_float and float or int - out_params = [] - for (name, val) in zip(names, values): - if val is None: - out_params.append(val) - elif isinstance(val, (int, long, float, basestring)): - try: - out_params.append(fun(val)) - except ValueError, e: - raise VdtParamError(name, val) - else: - raise VdtParamError(name, val) - return out_params - - -# built in checks -# you can override these by setting the appropriate name -# in Validator.functions -# note: if the params are specified wrongly in your input string, -# you will also raise errors. - -def is_integer(value, min=None, max=None): - """ - A check that tests that a given value is an integer (int, or long) - and optionally, between bounds. A negative value is accepted, while - a float will fail. - - If the value is a string, then the conversion is done - if possible. - Otherwise a VdtError is raised. - - >>> vtor.check('integer', '-1') - -1 - >>> vtor.check('integer', '0') - 0 - >>> vtor.check('integer', 9) - 9 - >>> vtor.check('integer', 'a') - Traceback (most recent call last): - VdtTypeError: the value "a" is of the wrong type. - >>> vtor.check('integer', '2.2') - Traceback (most recent call last): - VdtTypeError: the value "2.2" is of the wrong type. - >>> vtor.check('integer(10)', '20') - 20 - >>> vtor.check('integer(max=20)', '15') - 15 - >>> vtor.check('integer(10)', '9') - Traceback (most recent call last): - VdtValueTooSmallError: the value "9" is too small. - >>> vtor.check('integer(10)', 9) - Traceback (most recent call last): - VdtValueTooSmallError: the value "9" is too small. - >>> vtor.check('integer(max=20)', '35') - Traceback (most recent call last): - VdtValueTooBigError: the value "35" is too big. - >>> vtor.check('integer(max=20)', 35) - Traceback (most recent call last): - VdtValueTooBigError: the value "35" is too big. - >>> vtor.check('integer(0, 9)', False) - 0 - """ - (min_val, max_val) = _is_num_param(('min', 'max'), (min, max)) - if not isinstance(value, (int, long, basestring)): - raise VdtTypeError(value) - if isinstance(value, basestring): - # if it's a string - does it represent an integer ? - try: - value = int(value) - except ValueError: - raise VdtTypeError(value) - if (min_val is not None) and (value < min_val): - raise VdtValueTooSmallError(value) - if (max_val is not None) and (value > max_val): - raise VdtValueTooBigError(value) - return value - - -def is_float(value, min=None, max=None): - """ - A check that tests that a given value is a float - (an integer will be accepted), and optionally - that it is between bounds. - - If the value is a string, then the conversion is done - if possible. - Otherwise a VdtError is raised. - - This can accept negative values. - - >>> vtor.check('float', '2') - 2.0 - - From now on we multiply the value to avoid comparing decimals - - >>> vtor.check('float', '-6.8') * 10 - -68.0 - >>> vtor.check('float', '12.2') * 10 - 122.0 - >>> vtor.check('float', 8.4) * 10 - 84.0 - >>> vtor.check('float', 'a') - Traceback (most recent call last): - VdtTypeError: the value "a" is of the wrong type. - >>> vtor.check('float(10.1)', '10.2') * 10 - 102.0 - >>> vtor.check('float(max=20.2)', '15.1') * 10 - 151.0 - >>> vtor.check('float(10.0)', '9.0') - Traceback (most recent call last): - VdtValueTooSmallError: the value "9.0" is too small. - >>> vtor.check('float(max=20.0)', '35.0') - Traceback (most recent call last): - VdtValueTooBigError: the value "35.0" is too big. - """ - (min_val, max_val) = _is_num_param( - ('min', 'max'), (min, max), to_float=True) - if not isinstance(value, (int, long, float, basestring)): - raise VdtTypeError(value) - if not isinstance(value, float): - # if it's a string - does it represent a float ? - try: - value = float(value) - except ValueError: - raise VdtTypeError(value) - if (min_val is not None) and (value < min_val): - raise VdtValueTooSmallError(value) - if (max_val is not None) and (value > max_val): - raise VdtValueTooBigError(value) - return value - - -bool_dict = { - True: True, 'on': True, '1': True, 'true': True, 'yes': True, - False: False, 'off': False, '0': False, 'false': False, 'no': False, -} - - -def is_boolean(value): - """ - Check if the value represents a boolean. - - >>> vtor.check('boolean', 0) - 0 - >>> vtor.check('boolean', False) - 0 - >>> vtor.check('boolean', '0') - 0 - >>> vtor.check('boolean', 'off') - 0 - >>> vtor.check('boolean', 'false') - 0 - >>> vtor.check('boolean', 'no') - 0 - >>> vtor.check('boolean', 'nO') - 0 - >>> vtor.check('boolean', 'NO') - 0 - >>> vtor.check('boolean', 1) - 1 - >>> vtor.check('boolean', True) - 1 - >>> vtor.check('boolean', '1') - 1 - >>> vtor.check('boolean', 'on') - 1 - >>> vtor.check('boolean', 'true') - 1 - >>> vtor.check('boolean', 'yes') - 1 - >>> vtor.check('boolean', 'Yes') - 1 - >>> vtor.check('boolean', 'YES') - 1 - >>> vtor.check('boolean', '') - Traceback (most recent call last): - VdtTypeError: the value "" is of the wrong type. - >>> vtor.check('boolean', 'up') - Traceback (most recent call last): - VdtTypeError: the value "up" is of the wrong type. - - """ - if isinstance(value, basestring): - try: - return bool_dict[value.lower()] - except KeyError: - raise VdtTypeError(value) - # we do an equality test rather than an identity test - # this ensures Python 2.2 compatibilty - # and allows 0 and 1 to represent True and False - if value == False: - return False - elif value == True: - return True - else: - raise VdtTypeError(value) - - -def is_ip_addr(value): - """ - Check that the supplied value is an Internet Protocol address, v.4, - represented by a dotted-quad string, i.e. '1.2.3.4'. - - >>> vtor.check('ip_addr', '1 ') - '1' - >>> vtor.check('ip_addr', ' 1.2') - '1.2' - >>> vtor.check('ip_addr', ' 1.2.3 ') - '1.2.3' - >>> vtor.check('ip_addr', '1.2.3.4') - '1.2.3.4' - >>> vtor.check('ip_addr', '0.0.0.0') - '0.0.0.0' - >>> vtor.check('ip_addr', '255.255.255.255') - '255.255.255.255' - >>> vtor.check('ip_addr', '255.255.255.256') - Traceback (most recent call last): - VdtValueError: the value "255.255.255.256" is unacceptable. - >>> vtor.check('ip_addr', '1.2.3.4.5') - Traceback (most recent call last): - VdtValueError: the value "1.2.3.4.5" is unacceptable. - >>> vtor.check('ip_addr', 0) - Traceback (most recent call last): - VdtTypeError: the value "0" is of the wrong type. - """ - if not isinstance(value, basestring): - raise VdtTypeError(value) - value = value.strip() - try: - dottedQuadToNum(value) - except ValueError: - raise VdtValueError(value) - return value - - -def is_list(value, min=None, max=None): - """ - Check that the value is a list of values. - - You can optionally specify the minimum and maximum number of members. - - It does no check on list members. - - >>> vtor.check('list', ()) - [] - >>> vtor.check('list', []) - [] - >>> vtor.check('list', (1, 2)) - [1, 2] - >>> vtor.check('list', [1, 2]) - [1, 2] - >>> vtor.check('list(3)', (1, 2)) - Traceback (most recent call last): - VdtValueTooShortError: the value "(1, 2)" is too short. - >>> vtor.check('list(max=5)', (1, 2, 3, 4, 5, 6)) - Traceback (most recent call last): - VdtValueTooLongError: the value "(1, 2, 3, 4, 5, 6)" is too long. - >>> vtor.check('list(min=3, max=5)', (1, 2, 3, 4)) - [1, 2, 3, 4] - >>> vtor.check('list', 0) - Traceback (most recent call last): - VdtTypeError: the value "0" is of the wrong type. - >>> vtor.check('list', '12') - Traceback (most recent call last): - VdtTypeError: the value "12" is of the wrong type. - """ - (min_len, max_len) = _is_num_param(('min', 'max'), (min, max)) - if isinstance(value, basestring): - raise VdtTypeError(value) - try: - num_members = len(value) - except TypeError: - raise VdtTypeError(value) - if min_len is not None and num_members < min_len: - raise VdtValueTooShortError(value) - if max_len is not None and num_members > max_len: - raise VdtValueTooLongError(value) - return list(value) - - -def is_tuple(value, min=None, max=None): - """ - Check that the value is a tuple of values. - - You can optionally specify the minimum and maximum number of members. - - It does no check on members. - - >>> vtor.check('tuple', ()) - () - >>> vtor.check('tuple', []) - () - >>> vtor.check('tuple', (1, 2)) - (1, 2) - >>> vtor.check('tuple', [1, 2]) - (1, 2) - >>> vtor.check('tuple(3)', (1, 2)) - Traceback (most recent call last): - VdtValueTooShortError: the value "(1, 2)" is too short. - >>> vtor.check('tuple(max=5)', (1, 2, 3, 4, 5, 6)) - Traceback (most recent call last): - VdtValueTooLongError: the value "(1, 2, 3, 4, 5, 6)" is too long. - >>> vtor.check('tuple(min=3, max=5)', (1, 2, 3, 4)) - (1, 2, 3, 4) - >>> vtor.check('tuple', 0) - Traceback (most recent call last): - VdtTypeError: the value "0" is of the wrong type. - >>> vtor.check('tuple', '12') - Traceback (most recent call last): - VdtTypeError: the value "12" is of the wrong type. - """ - return tuple(is_list(value, min, max)) - - -def is_string(value, min=None, max=None): - """ - Check that the supplied value is a string. - - You can optionally specify the minimum and maximum number of members. - - >>> vtor.check('string', '0') - '0' - >>> vtor.check('string', 0) - Traceback (most recent call last): - VdtTypeError: the value "0" is of the wrong type. - >>> vtor.check('string(2)', '12') - '12' - >>> vtor.check('string(2)', '1') - Traceback (most recent call last): - VdtValueTooShortError: the value "1" is too short. - >>> vtor.check('string(min=2, max=3)', '123') - '123' - >>> vtor.check('string(min=2, max=3)', '1234') - Traceback (most recent call last): - VdtValueTooLongError: the value "1234" is too long. - """ - if not isinstance(value, basestring): - raise VdtTypeError(value) - (min_len, max_len) = _is_num_param(('min', 'max'), (min, max)) - try: - num_members = len(value) - except TypeError: - raise VdtTypeError(value) - if min_len is not None and num_members < min_len: - raise VdtValueTooShortError(value) - if max_len is not None and num_members > max_len: - raise VdtValueTooLongError(value) - return value - - -def is_int_list(value, min=None, max=None): - """ - Check that the value is a list of integers. - - You can optionally specify the minimum and maximum number of members. - - Each list member is checked that it is an integer. - - >>> vtor.check('int_list', ()) - [] - >>> vtor.check('int_list', []) - [] - >>> vtor.check('int_list', (1, 2)) - [1, 2] - >>> vtor.check('int_list', [1, 2]) - [1, 2] - >>> vtor.check('int_list', [1, 'a']) - Traceback (most recent call last): - VdtTypeError: the value "a" is of the wrong type. - """ - return [is_integer(mem) for mem in is_list(value, min, max)] - - -def is_bool_list(value, min=None, max=None): - """ - Check that the value is a list of booleans. - - You can optionally specify the minimum and maximum number of members. - - Each list member is checked that it is a boolean. - - >>> vtor.check('bool_list', ()) - [] - >>> vtor.check('bool_list', []) - [] - >>> check_res = vtor.check('bool_list', (True, False)) - >>> check_res == [True, False] - 1 - >>> check_res = vtor.check('bool_list', [True, False]) - >>> check_res == [True, False] - 1 - >>> vtor.check('bool_list', [True, 'a']) - Traceback (most recent call last): - VdtTypeError: the value "a" is of the wrong type. - """ - return [is_boolean(mem) for mem in is_list(value, min, max)] - - -def is_float_list(value, min=None, max=None): - """ - Check that the value is a list of floats. - - You can optionally specify the minimum and maximum number of members. - - Each list member is checked that it is a float. - - >>> vtor.check('float_list', ()) - [] - >>> vtor.check('float_list', []) - [] - >>> vtor.check('float_list', (1, 2.0)) - [1.0, 2.0] - >>> vtor.check('float_list', [1, 2.0]) - [1.0, 2.0] - >>> vtor.check('float_list', [1, 'a']) - Traceback (most recent call last): - VdtTypeError: the value "a" is of the wrong type. - """ - return [is_float(mem) for mem in is_list(value, min, max)] - - -def is_string_list(value, min=None, max=None): - """ - Check that the value is a list of strings. - - You can optionally specify the minimum and maximum number of members. - - Each list member is checked that it is a string. - - >>> vtor.check('string_list', ()) - [] - >>> vtor.check('string_list', []) - [] - >>> vtor.check('string_list', ('a', 'b')) - ['a', 'b'] - >>> vtor.check('string_list', ['a', 1]) - Traceback (most recent call last): - VdtTypeError: the value "1" is of the wrong type. - >>> vtor.check('string_list', 'hello') - Traceback (most recent call last): - VdtTypeError: the value "hello" is of the wrong type. - """ - if isinstance(value, basestring): - raise VdtTypeError(value) - return [is_string(mem) for mem in is_list(value, min, max)] - - -def is_ip_addr_list(value, min=None, max=None): - """ - Check that the value is a list of IP addresses. - - You can optionally specify the minimum and maximum number of members. - - Each list member is checked that it is an IP address. - - >>> vtor.check('ip_addr_list', ()) - [] - >>> vtor.check('ip_addr_list', []) - [] - >>> vtor.check('ip_addr_list', ('1.2.3.4', '5.6.7.8')) - ['1.2.3.4', '5.6.7.8'] - >>> vtor.check('ip_addr_list', ['a']) - Traceback (most recent call last): - VdtValueError: the value "a" is unacceptable. - """ - return [is_ip_addr(mem) for mem in is_list(value, min, max)] - - -def force_list(value, min=None, max=None): - """ - Check that a value is a list, coercing strings into - a list with one member. Useful where users forget the - trailing comma that turns a single value into a list. - - You can optionally specify the minimum and maximum number of members. - A minumum of greater than one will fail if the user only supplies a - string. - - >>> vtor.check('force_list', ()) - [] - >>> vtor.check('force_list', []) - [] - >>> vtor.check('force_list', 'hello') - ['hello'] - """ - if not isinstance(value, (list, tuple)): - value = [value] - return is_list(value, min, max) - - - -fun_dict = { - 'integer': is_integer, - 'float': is_float, - 'ip_addr': is_ip_addr, - 'string': is_string, - 'boolean': is_boolean, -} - - -def is_mixed_list(value, *args): - """ - Check that the value is a list. - Allow specifying the type of each member. - Work on lists of specific lengths. - - You specify each member as a positional argument specifying type - - Each type should be one of the following strings : - 'integer', 'float', 'ip_addr', 'string', 'boolean' - - So you can specify a list of two strings, followed by - two integers as : - - mixed_list('string', 'string', 'integer', 'integer') - - The length of the list must match the number of positional - arguments you supply. - - >>> mix_str = "mixed_list('integer', 'float', 'ip_addr', 'string', 'boolean')" - >>> check_res = vtor.check(mix_str, (1, 2.0, '1.2.3.4', 'a', True)) - >>> check_res == [1, 2.0, '1.2.3.4', 'a', True] - 1 - >>> check_res = vtor.check(mix_str, ('1', '2.0', '1.2.3.4', 'a', 'True')) - >>> check_res == [1, 2.0, '1.2.3.4', 'a', True] - 1 - >>> vtor.check(mix_str, ('b', 2.0, '1.2.3.4', 'a', True)) - Traceback (most recent call last): - VdtTypeError: the value "b" is of the wrong type. - >>> vtor.check(mix_str, (1, 2.0, '1.2.3.4', 'a')) - Traceback (most recent call last): - VdtValueTooShortError: the value "(1, 2.0, '1.2.3.4', 'a')" is too short. - >>> vtor.check(mix_str, (1, 2.0, '1.2.3.4', 'a', 1, 'b')) - Traceback (most recent call last): - VdtValueTooLongError: the value "(1, 2.0, '1.2.3.4', 'a', 1, 'b')" is too long. - >>> vtor.check(mix_str, 0) - Traceback (most recent call last): - VdtTypeError: the value "0" is of the wrong type. - - This test requires an elaborate setup, because of a change in error string - output from the interpreter between Python 2.2 and 2.3 . - - >>> res_seq = ( - ... 'passed an incorrect value "', - ... 'yoda', - ... '" for parameter "mixed_list".', - ... ) - >>> res_str = "'".join(res_seq) - >>> try: - ... vtor.check('mixed_list("yoda")', ('a')) - ... except VdtParamError, err: - ... str(err) == res_str - 1 - """ - try: - length = len(value) - except TypeError: - raise VdtTypeError(value) - if length < len(args): - raise VdtValueTooShortError(value) - elif length > len(args): - raise VdtValueTooLongError(value) - try: - return [fun_dict[arg](val) for arg, val in zip(args, value)] - except KeyError, e: - raise VdtParamError('mixed_list', e) - - -def is_option(value, *options): - """ - This check matches the value to any of a set of options. - - >>> vtor.check('option("yoda", "jedi")', 'yoda') - 'yoda' - >>> vtor.check('option("yoda", "jedi")', 'jed') - Traceback (most recent call last): - VdtValueError: the value "jed" is unacceptable. - >>> vtor.check('option("yoda", "jedi")', 0) - Traceback (most recent call last): - VdtTypeError: the value "0" is of the wrong type. - """ - if not isinstance(value, basestring): - raise VdtTypeError(value) - if not value in options: - raise VdtValueError(value) - return value - - -def _test(value, *args, **keywargs): - """ - A function that exists for test purposes. - - >>> checks = [ - ... '3, 6, min=1, max=3, test=list(a, b, c)', - ... '3', - ... '3, 6', - ... '3,', - ... 'min=1, test="a b c"', - ... 'min=5, test="a, b, c"', - ... 'min=1, max=3, test="a, b, c"', - ... 'min=-100, test=-99', - ... 'min=1, max=3', - ... '3, 6, test="36"', - ... '3, 6, test="a, b, c"', - ... '3, max=3, test=list("a", "b", "c")', - ... '''3, max=3, test=list("'a'", 'b', "x=(c)")''', - ... "test='x=fish(3)'", - ... ] - >>> v = Validator({'test': _test}) - >>> for entry in checks: - ... print v.check(('test(%s)' % entry), 3) - (3, ('3', '6'), {'test': ['a', 'b', 'c'], 'max': '3', 'min': '1'}) - (3, ('3',), {}) - (3, ('3', '6'), {}) - (3, ('3',), {}) - (3, (), {'test': 'a b c', 'min': '1'}) - (3, (), {'test': 'a, b, c', 'min': '5'}) - (3, (), {'test': 'a, b, c', 'max': '3', 'min': '1'}) - (3, (), {'test': '-99', 'min': '-100'}) - (3, (), {'max': '3', 'min': '1'}) - (3, ('3', '6'), {'test': '36'}) - (3, ('3', '6'), {'test': 'a, b, c'}) - (3, ('3',), {'test': ['a', 'b', 'c'], 'max': '3'}) - (3, ('3',), {'test': ["'a'", 'b', 'x=(c)'], 'max': '3'}) - (3, (), {'test': 'x=fish(3)'}) - - >>> v = Validator() - >>> v.check('integer(default=6)', '3') - 3 - >>> v.check('integer(default=6)', None, True) - 6 - >>> v.get_default_value('integer(default=6)') - 6 - >>> v.get_default_value('float(default=6)') - 6.0 - >>> v.get_default_value('pass(default=None)') - >>> v.get_default_value("string(default='None')") - 'None' - >>> v.get_default_value('pass') - Traceback (most recent call last): - KeyError: 'Check "pass" has no default value.' - >>> v.get_default_value('pass(default=list(1, 2, 3, 4))') - ['1', '2', '3', '4'] - - >>> v = Validator() - >>> v.check("pass(default=None)", None, True) - >>> v.check("pass(default='None')", None, True) - 'None' - >>> v.check('pass(default="None")', None, True) - 'None' - >>> v.check('pass(default=list(1, 2, 3, 4))', None, True) - ['1', '2', '3', '4'] - - Bug test for unicode arguments - >>> v = Validator() - >>> v.check(u'string(min=4)', u'test') - u'test' - - >>> v = Validator() - >>> v.get_default_value(u'string(min=4, default="1234")') - u'1234' - >>> v.check(u'string(min=4, default="1234")', u'test') - u'test' - - >>> v = Validator() - >>> default = v.get_default_value('string(default=None)') - >>> default == None - 1 - """ - return (value, args, keywargs) - - -def _test2(): - """ - >>> - >>> v = Validator() - >>> v.get_default_value('string(default="#ff00dd")') - '#ff00dd' - >>> v.get_default_value('integer(default=3) # comment') - 3 - """ - -def _test3(): - r""" - >>> vtor.check('string(default="")', '', missing=True) - '' - >>> vtor.check('string(default="\n")', '', missing=True) - '\n' - >>> print vtor.check('string(default="\n")', '', missing=True), - - >>> vtor.check('string()', '\n') - '\n' - >>> vtor.check('string(default="\n\n\n")', '', missing=True) - '\n\n\n' - >>> vtor.check('string()', 'random \n text goes here\n\n') - 'random \n text goes here\n\n' - >>> vtor.check('string(default=" \nrandom text\ngoes \n here\n\n ")', - ... '', missing=True) - ' \nrandom text\ngoes \n here\n\n ' - >>> vtor.check("string(default='\n\n\n')", '', missing=True) - '\n\n\n' - >>> vtor.check("option('\n','a','b',default='\n')", '', missing=True) - '\n' - >>> vtor.check("string_list()", ['foo', '\n', 'bar']) - ['foo', '\n', 'bar'] - >>> vtor.check("string_list(default=list('\n'))", '', missing=True) - ['\n'] - """ - - -if __name__ == '__main__': - # run the code tests in doctest format - import sys - import doctest - m = sys.modules.get('__main__') - globs = m.__dict__.copy() - globs.update({ - 'vtor': Validator(), - }) - doctest.testmod(m, globs=globs) diff --git a/astropy/extern/configobj_py3/__init__.py b/astropy/extern/configobj_py3/__init__.py deleted file mode 100644 index 54e9e57d060a..000000000000 --- a/astropy/extern/configobj_py3/__init__.py +++ /dev/null @@ -1,104 +0,0 @@ -# Licensed under a 3-clause BSD style license - see LICENSE.rst - -""" -This is a copy of the main portions of the `configobj -`_ package. This is used -internally in the Astropy configuration system. The license for configobj is -available in the ``licenses/CONFIGOBJ_LICENSE.rst`` file in the Astropy -source distribution. - -This is a version of configobj that has been modified by Zubin Mithra to be -compatible with python 3.x. This version is not official, but has been -"blessed" by configobj's original author. This version of the code was -obtained from https://bitbucket.org/zubin71/configobj-py3 - -For a python 2.x version, see the ``astropy/extern/configobj`` directory. -""" - -#this holds the contents of the setup.py file used by configobj -_configobj_setup_dot_py=""" -# setup.py -# Install script for ConfigObj -# Copyright (C) 2005-2010 Michael Foord, Mark Andrews, Nicola Larosa -# E-mail: fuzzyman AT voidspace DOT org DOT uk -# mark AT la-la DOT com -# nico AT tekNico DOT net - -# This software is licensed under the terms of the BSD license. -# http://www.voidspace.org.uk/python/license.shtml - -import sys -from distutils.core import setup -from configobj import __version__ as VERSION - -NAME = 'configobj' - -MODULES = 'configobj', 'validate' - -DESCRIPTION = 'Config file reading, writing and validation.' - -URL = 'http://www.voidspace.org.uk/python/configobj.html' - -DOWNLOAD_URL = "http://www.voidspace.org.uk/downloads/configobj-%s.zip" % VERSION - -LONG_DESCRIPTION = ""#"**ConfigObj** is a simple but powerful config file reader and writer: an *ini -file round tripper*. Its main feature is that it is very easy to use, with a -straightforward programmer's interface and a simple syntax for config files. -It has lots of other features though : - -* Nested sections (subsections), to any level -* List values -* Multiple line values -* Full Unicode support -* String interpolation (substitution) -* Integrated with a powerful validation system - - - including automatic type checking/conversion - - and allowing default values - - repeated sections - -* All comments in the file are preserved -* The order of keys/sections is preserved -* Powerful ``unrepr`` mode for storing/retrieving Python data-types - -| Release 4.7.2 fixes several bugs in 4.7.1 -| Release 4.7.1 fixes a bug with the deprecated options keyword in -| 4.7.0. -| Release 4.7.0 improves performance adds features for validation and -| fixes some bugs.""#" - -CLASSIFIERS = [ - 'Development Status :: 6 - Mature', - 'Intended Audience :: Developers', - 'License :: OSI Approved :: BSD License', - 'Programming Language :: Python', - 'Programming Language :: Python :: 2.3', - 'Programming Language :: Python :: 2.4', - 'Programming Language :: Python :: 2.5', - 'Programming Language :: Python :: 2.6', - 'Operating System :: OS Independent', - 'Topic :: Software Development :: Libraries', - 'Topic :: Software Development :: Libraries :: Python Modules', -] - -AUTHOR = 'Michael Foord & Nicola Larosa' - -AUTHOR_EMAIL = 'fuzzyman@voidspace.org.uk' - -KEYWORDS = "config, ini, dictionary, application, admin, sysadmin, configuration, validation".split(', ') - - -setup(name=NAME, - version=VERSION, - description=DESCRIPTION, - long_description=LONG_DESCRIPTION, - download_url=DOWNLOAD_URL, - author=AUTHOR, - author_email=AUTHOR_EMAIL, - url=URL, - py_modules=MODULES, - classifiers=CLASSIFIERS, - keywords=KEYWORDS - ) -""".replace('""#"','"""') -#the replacement is necessary because """ would otherwise terminate the string diff --git a/astropy/extern/configobj_py3/configobj.py b/astropy/extern/configobj_py3/configobj.py deleted file mode 100644 index 70c89f2d87b4..000000000000 --- a/astropy/extern/configobj_py3/configobj.py +++ /dev/null @@ -1,2405 +0,0 @@ -# configobj.py -# A config file reader/writer that supports nested sections in config files. -# Copyright (C) 2005-2010 Michael Foord, Nicola Larosa -# E-mail: fuzzyman AT voidspace DOT org DOT uk -# nico AT tekNico DOT net - -# ConfigObj 4 -# http://www.voidspace.org.uk/python/configobj.html - -# Released subject to the BSD License -# Please see http://www.voidspace.org.uk/python/license.shtml - -# Scripts maintained at http://www.voidspace.org.uk/python/index.shtml -# For information about bugfixes, updates and support, please join the -# ConfigObj mailing list: -# http://lists.sourceforge.net/lists/listinfo/configobj-develop -# Comments, suggestions and bug reports welcome. -import pdb - -import os -import re -import sys - -from codecs import BOM_UTF8, BOM_UTF16, BOM_UTF16_BE, BOM_UTF16_LE - -from ast import parse - -# A dictionary mapping BOM to -# the encoding to decode with, and what to set the -# encoding attribute to. -BOMS = { - BOM_UTF8: ('utf_8', None), - BOM_UTF16_BE: ('utf16_be', 'utf_16'), - BOM_UTF16_LE: ('utf16_le', 'utf_16'), - BOM_UTF16: ('utf_16', 'utf_16'), - } - -# All legal variants of the BOM codecs. -# TODO: the list of aliases is not meant to be exhaustive, is there a -# better way ? -BOM_LIST = { - 'utf_16': 'utf_16', - 'u16': 'utf_16', - 'utf16': 'utf_16', - 'utf-16': 'utf_16', - 'utf16_be': 'utf16_be', - 'utf_16_be': 'utf16_be', - 'utf-16be': 'utf16_be', - 'utf16_le': 'utf16_le', - 'utf_16_le': 'utf16_le', - 'utf-16le': 'utf16_le', - 'utf_8': 'utf_8', - 'u8': 'utf_8', - 'utf': 'utf_8', - 'utf8': 'utf_8', - 'utf-8': 'utf_8', - } - -# Map of encodings to the BOM to write. -BOM_SET = { - 'utf_8': BOM_UTF8, - 'utf_16': BOM_UTF16, - 'utf16_be': BOM_UTF16_BE, - 'utf16_le': BOM_UTF16_LE, - None: BOM_UTF8 - } - - -def match_utf8(encoding): - return BOM_LIST.get(encoding.lower()) == 'utf_8' - -# Quote strings used for writing values -squot = "'%s'" -dquot = '"%s"' -noquot = "%s" -wspace_plus = ' \r\n\v\t\'"' -tsquot = '"""%s"""' -tdquot = "'''%s'''" - -# Sentinel for use in getattr calls to replace hasattr -MISSING = object() - -__version__ = '4.7.2' - -try: - any -except NameError: - def any(iterable): - for entry in iterable: - if entry: - return True - return False - -__all__ = ( - '__version__', - 'DEFAULT_INDENT_TYPE', - 'DEFAULT_INTERPOLATION', - 'ConfigObjError', - 'NestingError', - 'ParseError', - 'DuplicateError', - 'ConfigspecError', - 'ConfigObj', - 'SimpleVal', - 'InterpolationError', - 'InterpolationLoopError', - 'MissingInterpolationOption', - 'RepeatSectionError', - 'ReloadError', - 'UnreprError', - 'UnknownType', - 'flatten_errors', - 'get_extra_values' -) - -DEFAULT_INTERPOLATION = 'configparser' -DEFAULT_INDENT_TYPE = ' ' -MAX_INTERPOL_DEPTH = 10 - -OPTION_DEFAULTS = { - 'interpolation': True, - 'raise_errors': False, - 'list_values': True, - 'create_empty': False, - 'file_error': False, - 'configspec': None, - 'stringify': True, - # option may be set to one of ('', ' ', '\t') - 'indent_type': None, - 'encoding': None, - 'default_encoding': None, - 'unrepr': False, - 'write_empty_values': False, -} - -def getObj(s): - p = parse("a=" + s) - obj = p.body[0].value - return obj - -class UnknownType(Exception): - pass - -class Builder(object): - - def build(self, o): - - m = getattr(self, 'build_' + o.__class__.__name__, None) - if m is None: - raise UnknownType(o.__class__.__name__) - return m(o) - - def build_List(self, o): - return map(self.build, o.elts) - - def build_Num(self, o): - return o.n - - def build_Str(str, o): - return o.s - - def build_Dict(self, o): - d = {} - items = zip(o.keys, o.values) - for key, value in items: - key = self.build(key) - value = self.build(value) - d[key] = value - return d - - def build_Tuple(self, o): - return tuple(self.build_List(o)) - - def build_Name(self, o): - value = o.id - if value == 'None': - return None - if value == 'True': - return True - if value == 'False': - return False - - # An undefined Name - raise UnknownType('Undefined Name') - -_builder = Builder() - -def unrepr(s): - if not s: - return s - return _builder.build(getObj(s)) - - -class ConfigObjError(SyntaxError): - """ - This is the base class for all errors that ConfigObj raises. - It is a subclass of SyntaxError. - """ - def __init__(self, message='', line_number=None, line=''): - self.line = line - self.line_number = line_number - SyntaxError.__init__(self, message) - - -class NestingError(ConfigObjError): - """ - This error indicates a level of nesting that doesn't match. - """ - - -class ParseError(ConfigObjError): - """ - This error indicates that a line is badly written. - It is neither a valid ``key = value`` line, - nor a valid section marker line. - """ - - -class ReloadError(IOError): - """ - A 'reload' operation failed. - This exception is a subclass of ``IOError``. - """ - def __init__(self): - IOError.__init__(self, 'reload failed, filename is not set.') - - -class DuplicateError(ConfigObjError): - """ - The keyword or section specified already exists. - """ - - -class ConfigspecError(ConfigObjError): - """ - An error occured whilst parsing a configspec. - """ - - -class InterpolationError(ConfigObjError): - """Base class for the two interpolation errors.""" - - -class InterpolationLoopError(InterpolationError): - """Maximum interpolation depth exceeded in string interpolation.""" - - def __init__(self, option): - InterpolationError.__init__( - self, - 'interpolation loop detected in value "%s".' % option) - - -class RepeatSectionError(ConfigObjError): - """ - This error indicates additional sections in a section with a - ``__many__`` (repeated) section. - """ - - -class MissingInterpolationOption(InterpolationError): - """A value specified for interpolation was missing.""" - def __init__(self, option): - msg = 'missing option "%s" in interpolation.' % option - InterpolationError.__init__(self, msg) - - -class UnreprError(ConfigObjError): - """An error parsing in unrepr mode.""" - - -class InterpolationEngine(object): - """ - A helper class to help perform string interpolation. - - This class is an abstract base class; its descendants perform - the actual work. - """ - - # compiled regexp to use in self.interpolate() - _KEYCRE = re.compile(r"%\(([^)]*)\)s") - _cookie = '%' - - def __init__(self, section): - # the Section instance that "owns" this engine - self.section = section - - - def interpolate(self, key, value): - # short-cut - if not self._cookie in value: - return value - - def recursive_interpolate(key, value, section, backtrail): - """The function that does the actual work. - - ``value``: the string we're trying to interpolate. - ``section``: the section in which that string was found - ``backtrail``: a dict to keep track of where we've been, - to detect and prevent infinite recursion loops - - This is similar to a depth-first-search algorithm. - """ - # Have we been here already? - if (key, section.name) in backtrail: - # Yes - infinite loop detected - raise InterpolationLoopError(key) - # Place a marker on our backtrail so we won't come back here again - backtrail[(key, section.name)] = 1 - - # Now start the actual work - match = self._KEYCRE.search(value) - while match: - # The actual parsing of the match is implementation-dependent, - # so delegate to our helper function - k, v, s = self._parse_match(match) - if k is None: - # That's the signal that no further interpolation is needed - replacement = v - else: - # Further interpolation may be needed to obtain final value - replacement = recursive_interpolate(k, v, s, backtrail) - # Replace the matched string with its final value - start, end = match.span() - value = ''.join((value[:start], replacement, value[end:])) - new_search_start = start + len(replacement) - # Pick up the next interpolation key, if any, for next time - # through the while loop - match = self._KEYCRE.search(value, new_search_start) - - # Now safe to come back here again; remove marker from backtrail - del backtrail[(key, section.name)] - - return value - - # Back in interpolate(), all we have to do is kick off the recursive - # function with appropriate starting values - value = recursive_interpolate(key, value, self.section, {}) - return value - - - def _fetch(self, key): - """Helper function to fetch values from owning section. - - Returns a 2-tuple: the value, and the section where it was found. - """ - # switch off interpolation before we try and fetch anything ! - save_interp = self.section.main.interpolation - self.section.main.interpolation = False - - # Start at section that "owns" this InterpolationEngine - current_section = self.section - while True: - # try the current section first - val = current_section.get(key) - if val is not None and not isinstance(val, Section): - break - # try "DEFAULT" next - val = current_section.get('DEFAULT', {}).get(key) - if val is not None and not isinstance(val, Section): - break - # move up to parent and try again - # top-level's parent is itself - if current_section.parent is current_section: - # reached top level, time to give up - break - current_section = current_section.parent - - # restore interpolation to previous value before returning - self.section.main.interpolation = save_interp - if val is None: - raise MissingInterpolationOption(key) - return val, current_section - - - def _parse_match(self, match): - """Implementation-dependent helper function. - - Will be passed a match object corresponding to the interpolation - key we just found (e.g., "%(foo)s" or "$foo"). Should look up that - key in the appropriate config file section (using the ``_fetch()`` - helper function) and return a 3-tuple: (key, value, section) - - ``key`` is the name of the key we're looking for - ``value`` is the value found for that key - ``section`` is a reference to the section where it was found - - ``key`` and ``section`` should be None if no further - interpolation should be performed on the resulting value - (e.g., if we interpolated "$$" and returned "$"). - """ - raise NotImplementedError() - - -class ConfigParserInterpolation(InterpolationEngine): - """Behaves like ConfigParser.""" - _cookie = '%' - _KEYCRE = re.compile(r"%\(([^)]*)\)s") - - def _parse_match(self, match): - key = match.group(1) - value, section = self._fetch(key) - return key, value, section - - -class TemplateInterpolation(InterpolationEngine): - """Behaves like string.Template.""" - _cookie = '$' - _delimiter = '$' - _KEYCRE = re.compile(r""" - \$(?: - (?P\$) | # Two $ signs - (?P[_a-z][_a-z0-9]*) | # $name format - {(?P[^}]*)} # ${name} format - ) - """, re.IGNORECASE | re.VERBOSE) - - def _parse_match(self, match): - # Valid name (in or out of braces): fetch value from section - key = match.group('named') or match.group('braced') - if key is not None: - value, section = self._fetch(key) - return key, value, section - # Escaped delimiter (e.g., $$): return single delimiter - if match.group('escaped') is not None: - # Return None for key and section to indicate it's time to stop - return None, self._delimiter, None - # Anything else: ignore completely, just return it unchanged - return None, match.group(), None - - -interpolation_engines = { - 'configparser': ConfigParserInterpolation, - 'template': TemplateInterpolation, -} - -def __newobj__(cls, *args): - # Hack for pickle - return cls.__new__(cls, *args) - -class Section(dict): - """ - A dictionary-like object that represents a section in a config file. - - It does string interpolation if the 'interpolation' attribute - of the 'main' object is set to True. - - Interpolation is tried first from this object, then from the 'DEFAULT' - section of this object, next from the parent and its 'DEFAULT' section, - and so on until the main object is reached. - - A Section will behave like an ordered dictionary - following the - order of the ``scalars`` and ``sections`` attributes. - You can use this to change the order of members. - - Iteration follows the order: scalars, then sections. - """ - - def __setstate__(self, state): - dict.update(self, state[0]) - self.__dict__.update(state[1]) - - def __reduce__(self): - state = (dict(self), self.__dict__) - return (__newobj__, (self.__class__,), state) - - def __init__(self, parent, depth, main, indict=None, name=None): - """ - * parent is the section above - * depth is the depth level of this section - * main is the main ConfigObj - * indict is a dictionary to initialise the section with - """ - if indict is None: - indict = {} - dict.__init__(self) - # used for nesting level *and* interpolation - self.parent = parent - # used for the interpolation attribute - self.main = main - # level of nesting depth of this Section - self.depth = depth - # purely for information - self.name = name - # - self._initialise() - # we do this explicitly so that __setitem__ is used properly - # (rather than just passing to ``dict.__init__``) - for entry, value in list(indict.items()): - self[entry] = value - - def _initialise(self): - # the sequence of scalar values in this Section - self.scalars = [] - # the sequence of sections in this Section - self.sections = [] - # for comments :-) - self.comments = {} - self.inline_comments = {} - # the configspec - self.configspec = None - # for defaults - self.defaults = [] - self.default_values = {} - self.extra_values = [] - self._created = False - - def _interpolate(self, key, value): - try: - # do we already have an interpolation engine? - engine = self._interpolation_engine - except AttributeError: - # not yet: first time running _interpolate(), so pick the engine - name = self.main.interpolation - if name == True: # note that "if name:" would be incorrect here - # backwards-compatibility: interpolation=True means use default - name = DEFAULT_INTERPOLATION - name = name.lower() # so that "Template", "template", etc. all work - class_ = interpolation_engines.get(name, None) - if class_ is None: - # invalid value for self.main.interpolation - self.main.interpolation = False - return value - else: - # save reference to engine so we don't have to do this again - engine = self._interpolation_engine = class_(self) - # let the engine do the actual work - return engine.interpolate(key, value) - - def __getitem__(self, key): - """Fetch the item and do string interpolation.""" - val = dict.__getitem__(self, key) - if self.main.interpolation: - if isinstance(val, str): - return self._interpolate(key, val) - if isinstance(val, list): - def _check(entry): - if isinstance(entry, str): - return self._interpolate(key, entry) - return entry - new = [_check(entry) for entry in val] - if new != val: - return new - return val - - def __setitem__(self, key, value, unrepr=False): - """ - Correctly set a value. - - Making dictionary values Section instances. - (We have to special case 'Section' instances - which are also dicts) - - Keys must be strings. - Values need only be strings (or lists of strings) if - ``main.stringify`` is set. - - ``unrepr`` must be set when setting a value to a dictionary, without - creating a new sub-section. - """ - if not isinstance(key, str): - raise ValueError('The key "%s" is not a string.' % key) - - # add the comment - if key not in self.comments: - self.comments[key] = [] - self.inline_comments[key] = '' - # remove the entry from defaults - if key in self.defaults: - self.defaults.remove(key) - # - if isinstance(value, Section): - if key not in self: - self.sections.append(key) - dict.__setitem__(self, key, value) - elif isinstance(value, dict) and not unrepr: - # First create the new depth level, - # then create the section - if key not in self: - self.sections.append(key) - new_depth = self.depth + 1 - dict.__setitem__( - self, - key, - Section( - self, - new_depth, - self.main, - indict=value, - name=key)) - else: - if key not in self: - self.scalars.append(key) - if not self.main.stringify: - if isinstance(value, str): - pass - elif isinstance(value, (list, tuple)): - for entry in value: - if not isinstance(entry, str): - raise TypeError('Value is not a string "%s".' % entry) - else: - raise TypeError('Value is not a string "%s".' % value) - dict.__setitem__(self, key, value) - - - def __delitem__(self, key): - """Remove items from the sequence when deleting.""" - dict. __delitem__(self, key) - if key in self.scalars: - self.scalars.remove(key) - else: - self.sections.remove(key) - del self.comments[key] - del self.inline_comments[key] - - def get(self, key, default=None): - """A version of ``get`` that doesn't bypass string interpolation.""" - try: - return self[key] - except KeyError: - return default - - - def update(self, indict): - """ - A version of update that uses our ``__setitem__``. - """ - for entry in indict: - self[entry] = indict[entry] - - - def pop(self, key, default=MISSING): - """ - 'D.pop(k[,d]) -> v, remove specified key and return the corresponding value. - If key is not found, d is returned if given, otherwise KeyError is raised' - """ - try: - val = self[key] - except KeyError: - if default is MISSING: - raise - val = default - else: - del self[key] - return val - - def popitem(self): - """Pops the first (key,val)""" - sequence = (self.scalars + self.sections) - if not sequence: - raise KeyError(": 'popitem(): dictionary is empty'") - key = sequence[0] - val = self[key] - del self[key] - return key, val - - def clear(self): - """ - A version of clear that also affects scalars/sections - Also clears comments and configspec. - - Leaves other attributes alone : - depth/main/parent are not affected - """ - dict.clear(self) - self.scalars = [] - self.sections = [] - self.comments = {} - self.inline_comments = {} - self.configspec = None - self.defaults = [] - self.extra_values = [] - - - def setdefault(self, key, default=None): - """A version of setdefault that sets sequence if appropriate.""" - try: - return self[key] - except KeyError: - self[key] = default - return self[key] - - - def items(self): - """D.items() -> list of D's (key, value) pairs, as 2-tuples""" - return list(zip((self.scalars + self.sections), list(self.values()))) - - - def keys(self): - """D.keys() -> list of D's keys""" - return (self.scalars + self.sections) - - - def values(self): - """D.values() -> list of D's values""" - return [self[key] for key in (self.scalars + self.sections)] - - - def iteritems(self): - """D.iteritems() -> an iterator over the (key, value) items of D""" - return iter(list(self.items())) - - - def iterkeys(self): - """D.iterkeys() -> an iterator over the keys of D""" - return iter((self.scalars + self.sections)) - - __iter__ = iterkeys - - - def itervalues(self): - """D.itervalues() -> an iterator over the values of D""" - return iter(list(self.values())) - - - def __repr__(self): - """x.__repr__() <==> repr(x)""" - def _getval(key): - try: - return self[key] - except MissingInterpolationOption: - return dict.__getitem__(self, key) - return '{%s}' % ', '.join([('%s: %s' % (repr(key), repr(_getval(key)))) - for key in (self.scalars + self.sections)]) - - __str__ = __repr__ - __str__.__doc__ = "x.__str__() <==> str(x)" - - - # Extra methods - not in a normal dictionary - - def dict(self): - """ - Return a deepcopy of self as a dictionary. - - All members that are ``Section`` instances are recursively turned to - ordinary dictionaries - by calling their ``dict`` method. - - >>> n = a.dict() - >>> n == a - 1 - >>> n is a - 0 - """ - newdict = {} - for entry in self: - this_entry = self[entry] - if isinstance(this_entry, Section): - this_entry = this_entry.dict() - elif isinstance(this_entry, list): - # create a copy rather than a reference - this_entry = list(this_entry) - elif isinstance(this_entry, tuple): - # create a copy rather than a reference - this_entry = tuple(this_entry) - newdict[entry] = this_entry - return newdict - - - def merge(self, indict): - """ - A recursive update - useful for merging config files. - - >>> a = '''[section1] - ... option1 = True - ... [[subsection]] - ... more_options = False - ... # end of file'''.splitlines() - >>> b = '''# File is user.ini - ... [section1] - ... option1 = False - ... # end of file'''.splitlines() - >>> c1 = ConfigObj(b) - >>> c2 = ConfigObj(a) - >>> c2.merge(c1) - >>> c2 - ConfigObj({'section1': {'option1': 'False', 'subsection': {'more_options': 'False'}}}) - """ - for key, val in list(indict.items()): - if (key in self and isinstance(self[key], dict) and - isinstance(val, dict)): - self[key].merge(val) - else: - self[key] = val - - - def rename(self, oldkey, newkey): - """ - Change a keyname to another, without changing position in sequence. - - Implemented so that transformations can be made on keys, - as well as on values. (used by encode and decode) - - Also renames comments. - """ - if oldkey in self.scalars: - the_list = self.scalars - elif oldkey in self.sections: - the_list = self.sections - else: - raise KeyError('Key "%s" not found.' % oldkey) - pos = the_list.index(oldkey) - # - val = self[oldkey] - dict.__delitem__(self, oldkey) - dict.__setitem__(self, newkey, val) - the_list.remove(oldkey) - the_list.insert(pos, newkey) - comm = self.comments[oldkey] - inline_comment = self.inline_comments[oldkey] - del self.comments[oldkey] - del self.inline_comments[oldkey] - self.comments[newkey] = comm - self.inline_comments[newkey] = inline_comment - - def walk(self, function, raise_errors=True, - call_on_sections=False, **keywargs): - """ - Walk every member and call a function on the keyword and value. - - Return a dictionary of the return values - - If the function raises an exception, raise the errror - unless ``raise_errors=False``, in which case set the return value to - ``False``. - - Any unrecognised keyword arguments you pass to walk, will be pased on - to the function you pass in. - - Note: if ``call_on_sections`` is ``True`` then - on encountering a - subsection, *first* the function is called for the *whole* subsection, - and then recurses into it's members. This means your function must be - able to handle strings, dictionaries and lists. This allows you - to change the key of subsections as well as for ordinary members. The - return value when called on the whole subsection has to be discarded. - - See the encode and decode methods for examples, including functions. - - .. admonition:: caution - - You can use ``walk`` to transform the names of members of a section - but you mustn't add or delete members. - - >>> config = '''[XXXXsection] - ... XXXXkey = XXXXvalue'''.splitlines() - >>> cfg = ConfigObj(config) - >>> cfg - ConfigObj({'XXXXsection': {'XXXXkey': 'XXXXvalue'}}) - >>> def transform(section, key): - ... val = section[key] - ... newkey = key.replace('XXXX', 'CLIENT1') - ... section.rename(key, newkey) - ... if isinstance(val, (tuple, list, dict)): - ... pass - ... else: - ... val = val.replace('XXXX', 'CLIENT1') - ... section[newkey] = val - >>> cfg.walk(transform, call_on_sections=True) - {'CLIENT1section': {'CLIENT1key': None}} - >>> cfg - ConfigObj({'CLIENT1section': {'CLIENT1key': 'CLIENT1value'}}) - """ - out = {} - # scalars first - for i in range(len(self.scalars)): - entry = self.scalars[i] - try: - val = function(self, entry, **keywargs) - # bound again in case name has changed - entry = self.scalars[i] - out[entry] = val - except Exception: - if raise_errors: - raise - else: - entry = self.scalars[i] - out[entry] = False - # then sections - for i in range(len(self.sections)): - entry = self.sections[i] - if call_on_sections: - try: - function(self, entry, **keywargs) - except Exception: - if raise_errors: - raise - else: - entry = self.sections[i] - out[entry] = False - # bound again in case name has changed - entry = self.sections[i] - # previous result is discarded - out[entry] = self[entry].walk( - function, - raise_errors=raise_errors, - call_on_sections=call_on_sections, - **keywargs) - return out - - def as_bool(self, key): - """ - Accepts a key as input. The corresponding value must be a string or - the objects (``True`` or 1) or (``False`` or 0). We allow 0 and 1 to - retain compatibility with Python 2.2. - - If the string is one of ``True``, ``On``, ``Yes``, or ``1`` it returns - ``True``. - - If the string is one of ``False``, ``Off``, ``No``, or ``0`` it returns - ``False``. - - ``as_bool`` is not case sensitive. - - Any other input will raise a ``ValueError``. - - >>> a = ConfigObj() - >>> a['a'] = 'fish' - >>> a.as_bool('a') - Traceback (most recent call last): - ValueError: Value "fish" is neither True nor False - >>> a['b'] = 'True' - >>> a.as_bool('b') - 1 - >>> a['b'] = 'off' - >>> a.as_bool('b') - 0 - """ - val = self[key] - if val == True: - return True - elif val == False: - return False - else: - try: - if not isinstance(val, str): - # TODO: Why do we raise a KeyError here? - raise KeyError() - else: - return self.main._bools[val.lower()] - except KeyError: - raise ValueError('Value "%s" is neither True nor False' % val) - - def as_int(self, key): - """ - A convenience method which coerces the specified value to an integer. - - If the value is an invalid literal for ``int``, a ``ValueError`` will - be raised. - - >>> a = ConfigObj() - >>> a['a'] = 'fish' - >>> a.as_int('a') - Traceback (most recent call last): - ValueError: invalid literal for int() with base 10: 'fish' - >>> a['b'] = '1' - >>> a.as_int('b') - 1 - >>> a['b'] = '3.2' - >>> a.as_int('b') - Traceback (most recent call last): - ValueError: invalid literal for int() with base 10: '3.2' - """ - return int(self[key]) - - def as_float(self, key): - """ - A convenience method which coerces the specified value to a float. - - If the value is an invalid literal for ``float``, a ``ValueError`` will - be raised. - - >>> a = ConfigObj() - >>> a['a'] = 'fish' - >>> a.as_float('a') - Traceback (most recent call last): - ValueError: invalid literal for float(): fish - >>> a['b'] = '1' - >>> a.as_float('b') - 1.0 - >>> a['b'] = '3.2' - >>> a.as_float('b') - 3.2000000000000002 - """ - return float(self[key]) - - def as_list(self, key): - """ - A convenience method which fetches the specified value, guaranteeing - that it is a list. - - >>> a = ConfigObj() - >>> a['a'] = 1 - >>> a.as_list('a') - [1] - >>> a['a'] = (1,) - >>> a.as_list('a') - [1] - >>> a['a'] = [1] - >>> a.as_list('a') - [1] - """ - result = self[key] - if isinstance(result, (tuple, list)): - return list(result) - return [result] - - def restore_default(self, key): - """ - Restore (and return) default value for the specified key. - - This method will only work for a ConfigObj that was created - with a configspec and has been validated. - - If there is no default value for this key, ``KeyError`` is raised. - """ - default = self.default_values[key] - dict.__setitem__(self, key, default) - if key not in self.defaults: - self.defaults.append(key) - return default - - def restore_defaults(self): - """ - Recursively restore default values to all members - that have them. - - This method will only work for a ConfigObj that was created - with a configspec and has been validated. - - It doesn't delete or modify entries without default values. - """ - for key in self.default_values: - self.restore_default(key) - - for section in self.sections: - self[section].restore_defaults() - - -class ConfigObj(Section): - """An object to read, create, and write config files.""" - - _keyword = re.compile(r'''^ # line start - (\s*) # indentation - ( # keyword - (?:".*?")| # double quotes - (?:'.*?')| # single quotes - (?:[^'"=].*?) # no quotes - ) - \s*=\s* # divider - (.*) # value (including list values and comments) - $ # line end - ''', - re.VERBOSE) - - _sectionmarker = re.compile(r'''^ - (\s*) # 1: indentation - ((?:\[\s*)+) # 2: section marker open - ( # 3: section name open - (?:"\s*\S.*?\s*")| # at least one non-space with double quotes - (?:'\s*\S.*?\s*')| # at least one non-space with single quotes - (?:[^'"\s].*?) # at least one non-space unquoted - ) # section name close - ((?:\s*\])+) # 4: section marker close - \s*(\#.*)? # 5: optional comment - $''', - re.VERBOSE) - - # this regexp pulls list values out as a single string - # or single values and comments - # FIXME: this regex adds a '' to the end of comma terminated lists - # workaround in ``_handle_value`` - _valueexp = re.compile(r'''^ - (?: - (?: - ( - (?: - (?: - (?:".*?")| # double quotes - (?:'.*?')| # single quotes - (?:[^'",\#][^,\#]*?) # unquoted - ) - \s*,\s* # comma - )* # match all list items ending in a comma (if any) - ) - ( - (?:".*?")| # double quotes - (?:'.*?')| # single quotes - (?:[^'",\#\s][^,]*?)| # unquoted - (?:(? 1: - msg = "Parsing failed with several errors.\nFirst error %s" % info - error = ConfigObjError(msg) - else: - error = self._errors[0] - # set the errors attribute; it's a list of tuples: - # (error_type, message, line_number) - error.errors = self._errors - # set the config attribute - error.config = self - raise error - # delete private attributes - del self._errors - - if configspec is None: - self.configspec = None - else: - self._handle_configspec(configspec) - - def _initialise(self, options=None): - if options is None: - options = OPTION_DEFAULTS - - # initialise a few variables - self.filename = None - self._errors = [] - self.raise_errors = options['raise_errors'] - self.interpolation = options['interpolation'] - self.list_values = options['list_values'] - self.create_empty = options['create_empty'] - self.file_error = options['file_error'] - self.stringify = options['stringify'] - self.indent_type = options['indent_type'] - self.encoding = options['encoding'] - self.default_encoding = options['default_encoding'] - self.BOM = False - self.newlines = None - self.write_empty_values = options['write_empty_values'] - self.unrepr = options['unrepr'] - - self.initial_comment = [] - self.final_comment = [] - self.configspec = None - - if self._inspec: - self.list_values = False - - # Clear section attributes as well - Section._initialise(self) - - def __repr__(self): - def _getval(key): - try: - return self[key] - except MissingInterpolationOption: - return dict.__getitem__(self, key) - return ('ConfigObj({%s})' % - ', '.join([('%s: %s' % (repr(key), repr(_getval(key)))) - for key in (self.scalars + self.sections)])) - - def _handle_bom(self, infile): - """ - Handle any BOM, and decode if necessary. - - If an encoding is specified, that *must* be used - but the BOM should - still be removed (and the BOM attribute set). - - (If the encoding is wrongly specified, then a BOM for an alternative - encoding won't be discovered or removed.) - - If an encoding is not specified, UTF8 or UTF16 BOM will be detected and - removed. The BOM attribute will be set. UTF16 will be decoded to - unicode. - - NOTE: This method must not be called with an empty ``infile``. - - Specifying the *wrong* encoding is likely to cause a - ``UnicodeDecodeError``. - - ``infile`` must always be returned as a list of lines, but may be - passed in as a single string. - """ - if ((self.encoding is not None) and - (self.encoding.lower() not in BOM_LIST)): - # No need to check for a BOM - # the encoding specified doesn't have one - # just decode - return self._decode(infile, self.encoding) - - if isinstance(infile, (list, tuple)): - line = infile[0] - else: - line = infile - if self.encoding is not None: - # encoding explicitly supplied - # And it could have an associated BOM - # TODO: if encoding is just UTF16 - we ought to check for both - # TODO: big endian and little endian versions. - enc = BOM_LIST[self.encoding.lower()] - if enc == 'utf_16': - # For UTF16 we try big endian and little endian - for BOM, (encoding, final_encoding) in list(BOMS.items()): - if not final_encoding: - # skip UTF8 - continue - if isinstance(infile, bytes) and infile.startswith(BOM): - ### BOM discovered - ##self.BOM = True - # Don't need to remove BOM - return self._decode(infile, encoding) - - # If we get this far, will *probably* raise a DecodeError - # As it doesn't appear to start with a BOM - return self._decode(infile, self.encoding) - - # Must be UTF8 - BOM = BOM_SET[enc] - if isinstance(line, bytes) and not line.startswith(BOM): - return self._decode(infile, self.encoding) - - newline = line[len(BOM):] - - # BOM removed - if isinstance(infile, (list, tuple)): - infile[0] = newline - else: - infile = newline - self.BOM = True - return self._decode(infile, self.encoding) - - # No encoding specified - so we need to check for UTF8/UTF16 - for BOM, (encoding, final_encoding) in list(BOMS.items()): - if isinstance(line, bytes) and not line.startswith(BOM): - continue - else: - # BOM discovered - # self.encoding = final_encoding - if not final_encoding: - self.BOM = True - # UTF8 - # remove BOM - newline = line[len(BOM):] - if isinstance(infile, (list, tuple)): - infile[0] = newline - else: - infile = newline - # UTF8 - don't decode - if isinstance(infile, str): - return infile.splitlines(True) - else: - return infile - - infile = self._decode(infile, encoding) - if isinstance(infile, str): - # infile read from a file will be a single string - return infile.splitlines(True) - return self._decode(infile, encoding) - # UTF16 - have to decode - - - # No BOM discovered and no encoding specified, just return - if isinstance(infile, str): - # infile read from a file will be a single string - return infile.splitlines(True) - - if isinstance(infile, bytes): - return self._decode(infile, self.encoding) - return infile - - def _decode(self, infile, encoding): - """ - Decode infile to unicode. Using the specified encoding. - - if is a string, it also needs converting to a list. - """ - - encoding = encoding or 'utf-8' - - # If `infile` is a Unicode string, return as such - if isinstance(infile, str): - return infile - - # If `infile` is bytes type; decode and split - if isinstance(infile, bytes): - return infile.decode(encoding).splitlines(True) - - # If `infile` is a mix of bytes and unicode strings - for i, line in enumerate(infile): - if isinstance(line, bytes): - infile[i] = line.decode(encoding) - return infile - - def _decode_element(self, line): - """Decode element to unicode if necessary.""" - if not self.encoding: - return line - if isinstance(line, bytes) and self.default_encoding: - return line.decode(self.default_encoding) - return line - - def _str(self, value): - """ - Used by ``stringify`` within validate, to turn non-string values - into strings. - """ - - # Bytes type string should NOT be stringified at any cost. - if isinstance(value, bytes): - return value - if not isinstance(value, str): - return str(value) - else: - return value - - def _parse(self, infile): - """Actually parse the config file.""" - - temp_list_values = self.list_values - if self.unrepr: - self.list_values = False - - comment_list = [] - done_start = False - this_section = self - maxline = len(infile) - 1 - cur_index = -1 - reset_comment = False - - while cur_index < maxline: - if reset_comment: - comment_list = [] - cur_index += 1 - line = infile[cur_index] - sline = line.strip() - # do we have anything on the line ? - if not sline or sline.startswith('#'): - reset_comment = False - comment_list.append(line) - continue - - if not done_start: - # preserve initial comment - self.initial_comment = comment_list - comment_list = [] - done_start = True - - reset_comment = True - # first we check if it's a section marker - mat = self._sectionmarker.match(line) - if mat is not None: - # is a section line - (indent, sect_open, sect_name, sect_close, comment) = mat.groups() - if indent and (self.indent_type is None): - self.indent_type = indent - cur_depth = sect_open.count('[') - if cur_depth != sect_close.count(']'): - self._handle_error("Cannot compute the section depth at line %s.", - NestingError, infile, cur_index) - continue - - if cur_depth < this_section.depth: - # the new section is dropping back to a previous level - try: - parent = self._match_depth(this_section, - cur_depth).parent - except SyntaxError: - self._handle_error("Cannot compute nesting level at line %s.", - NestingError, infile, cur_index) - continue - elif cur_depth == this_section.depth: - # the new section is a sibling of the current section - parent = this_section.parent - elif cur_depth == this_section.depth + 1: - # the new section is a child the current section - parent = this_section - else: - self._handle_error("Section too nested at line %s.", - NestingError, infile, cur_index) - - sect_name = self._unquote(sect_name) - if sect_name in parent: - self._handle_error('Duplicate section name at line %s.', - DuplicateError, infile, cur_index) - continue - - # create the new section - this_section = Section( - parent, - cur_depth, - self, - name=sect_name) - parent[sect_name] = this_section - parent.inline_comments[sect_name] = comment - parent.comments[sect_name] = comment_list - continue - # - # it's not a section marker, - # so it should be a valid ``key = value`` line - mat = self._keyword.match(line) - if mat is None: - # it neither matched as a keyword - # or a section marker - self._handle_error( - 'Invalid line at line "%s".', - ParseError, infile, cur_index) - else: - # is a keyword value - # value will include any inline comment - (indent, key, value) = mat.groups() - if indent and (self.indent_type is None): - self.indent_type = indent - # check for a multiline value - if value[:3] in ['"""', "'''"]: - try: - value, comment, cur_index = self._multiline( - value, infile, cur_index, maxline) - except SyntaxError: - self._handle_error( - 'Parse error in value at line %s.', - ParseError, infile, cur_index) - continue - else: - if self.unrepr: - comment = '' - try: - value = unrepr(value) - except Exception as e: - if type(e) == UnknownType: - msg = 'Unknown name or type in value at line %s.' - else: - msg = 'Parse error in value at line %s.' - self._handle_error(msg, UnreprError, infile, - cur_index) - continue - else: - if self.unrepr: - comment = '' - try: - value = unrepr(value) - except Exception as e: - if isinstance(e, UnknownType): - msg = 'Unknown name or type in value at line %s.' - else: - msg = 'Parse error in value at line %s.' - self._handle_error(msg, UnreprError, infile, - cur_index) - continue - else: - # extract comment and lists - try: - (value, comment) = self._handle_value(value) - except SyntaxError: - self._handle_error( - 'Parse error in value at line %s.', - ParseError, infile, cur_index) - continue - # - key = self._unquote(key) - if key in this_section: - self._handle_error( - 'Duplicate keyword name at line %s.', - DuplicateError, infile, cur_index) - continue - # add the key. - # we set unrepr because if we have got this far we will never - # be creating a new section - this_section.__setitem__(key, value, unrepr=True) - this_section.inline_comments[key] = comment - this_section.comments[key] = comment_list - continue - # - if self.indent_type is None: - # no indentation used, set the type accordingly - self.indent_type = '' - - # preserve the final comment - if not self and not self.initial_comment: - self.initial_comment = comment_list - elif not reset_comment: - self.final_comment = comment_list - self.list_values = temp_list_values - - def _match_depth(self, sect, depth): - """ - Given a section and a depth level, walk back through the sections - parents to see if the depth level matches a previous section. - - Return a reference to the right section, - or raise a SyntaxError. - """ - while depth < sect.depth: - if sect is sect.parent: - # we've reached the top level already - raise SyntaxError() - sect = sect.parent - if sect.depth == depth: - return sect - # shouldn't get here - raise SyntaxError() - - def _handle_error(self, text, ErrorClass, infile, cur_index): - """ - Handle an error according to the error settings. - - Either raise the error or store it. - The error will have occured at ``cur_index`` - """ - line = infile[cur_index] - cur_index += 1 - message = text % cur_index - error = ErrorClass(message, cur_index, line) - if self.raise_errors: - # raise the error - parsing stops here - raise error - # store the error - # reraise when parsing has finished - self._errors.append(error) - - def _unquote(self, value): - """Return an unquoted version of a value""" - if not value: - # should only happen during parsing of lists - raise SyntaxError - if (value[0] == value[-1]) and (value[0] in ('"', "'")): - value = value[1:-1] - return value - - def _quote(self, value, multiline=True): - """ - Return a safely quoted version of a value. - - Raise a ConfigObjError if the value cannot be safely quoted. - If multiline is ``True`` (default) then use triple quotes - if necessary. - - * Don't quote values that don't need it. - * Recursively quote members of a list and return a comma joined list. - * Multiline is ``False`` for lists. - * Obey list syntax for empty and single member lists. - - If ``list_values=False`` then the value is only quoted if it contains - a ``\\n`` (is multiline) or '#'. - - If ``write_empty_values`` is set, and the value is an empty string, it - won't be quoted. - """ - if multiline and self.write_empty_values and value == '': - # Only if multiline is set, so that it is used for values not - # keys, and not values that are part of a list - return '' - - if multiline and isinstance(value, (list, tuple)): - if not value: - return ',' - elif len(value) == 1: - return self._quote(value[0], multiline=False) + ',' - return ', '.join([self._quote(val, multiline=False) - for val in value]) - if not isinstance(value, str): - if self.stringify and not isinstance(value, bytes): - value = str(value) - else: - raise TypeError('Value "%s" is not a string.' % value) - - if not value: - return '""' - - no_lists_no_quotes = not self.list_values and '\n' not in value and '#' not in value - need_triple = multiline and ((("'" in value) and ('"' in value)) or ('\n' in value )) - hash_triple_quote = multiline and not need_triple and ("'" in value) and ('"' in value) and ('#' in value) - check_for_single = (no_lists_no_quotes or not need_triple) and not hash_triple_quote - - if check_for_single: - if not self.list_values: - # we don't quote if ``list_values=False`` - quot = noquot - # for normal values either single or double quotes will do - elif '\n' in value: - # will only happen if multiline is off - e.g. '\n' in key - raise ConfigObjError('Value "%s" cannot be safely quoted.' % value) - elif ((value[0] not in wspace_plus) and - (value[-1] not in wspace_plus) and - (',' not in value)): - quot = noquot - else: - quot = self._get_single_quote(value) - else: - # if value has '\n' or "'" *and* '"', it will need triple quotes - quot = self._get_triple_quote(value) - - if quot == noquot and '#' in value and self.list_values: - quot = self._get_single_quote(value) - - return quot % value - - def _get_single_quote(self, value): - if ("'" in value) and ('"' in value): - raise ConfigObjError('Value "%s" cannot be safely quoted.' % value) - elif '"' in value: - quot = squot - else: - quot = dquot - return quot - - def _get_triple_quote(self, value): - if (value.find('"""') != -1) and (value.find("'''") != -1): - raise ConfigObjError('Value "%s" cannot be safely quoted.' % value) - if value.find('"""') == -1: - quot = tdquot - else: - quot = tsquot - return quot - - def _handle_value(self, value): - """ - Given a value string, unquote, remove comment, - handle lists. (including empty and single member lists) - """ - if self._inspec: - # Parsing a configspec so don't handle comments - return (value, '') - # do we look for lists in values ? - if not self.list_values: - mat = self._nolistvalue.match(value) - if mat is None: - raise SyntaxError() - # NOTE: we don't unquote here - return mat.groups() - # - mat = self._valueexp.match(value) - if mat is None: - # the value is badly constructed, probably badly quoted, - # or an invalid list - raise SyntaxError() - (list_values, single, empty_list, comment) = mat.groups() - if (list_values == '') and (single is None): - # change this if you want to accept empty values - raise SyntaxError() - # NOTE: note there is no error handling from here if the regex - # is wrong: then incorrect values will slip through - if empty_list is not None: - # the single comma - meaning an empty list - return ([], comment) - if single is not None: - # handle empty values - if list_values and not single: - # FIXME: the '' is a workaround because our regex now matches - # '' at the end of a list if it has a trailing comma - single = None - else: - single = single or '""' - single = self._unquote(single) - if list_values == '': - # not a list value - return (single, comment) - the_list = self._listvalueexp.findall(list_values) - the_list = [self._unquote(val) for val in the_list] - if single is not None: - the_list += [single] - return (the_list, comment) - - def _multiline(self, value, infile, cur_index, maxline): - """Extract the value, where we are in a multiline situation.""" - quot = value[:3] - newvalue = value[3:] - single_line = self._triple_quote[quot][0] - multi_line = self._triple_quote[quot][1] - mat = single_line.match(value) - if mat is not None: - retval = list(mat.groups()) - retval.append(cur_index) - return retval - elif newvalue.find(quot) != -1: - # somehow the triple quote is missing - raise SyntaxError() - # - while cur_index < maxline: - cur_index += 1 - newvalue += '\n' - line = infile[cur_index] - if line.find(quot) == -1: - newvalue += line - else: - # end of multiline, process it - break - else: - # we've got to the end of the config, oops... - raise SyntaxError() - mat = multi_line.match(line) - if mat is None: - # a badly formed line - raise SyntaxError() - (value, comment) = mat.groups() - return (newvalue + value, comment, cur_index) - - def _handle_configspec(self, configspec): - """Parse the configspec.""" - # FIXME: Should we check that the configspec was created with the - # correct settings ? (i.e. ``list_values=False``) - if not isinstance(configspec, ConfigObj): - try: - configspec = ConfigObj(configspec, - raise_errors=True, - file_error=True, - _inspec=True) - except ConfigObjError as e: - # FIXME: Should these errors have a reference - # to the already parsed ConfigObj ? - raise ConfigspecError('Parsing configspec failed: %s' % e) - except IOError as e: - raise IOError('Reading configspec failed: %s' % e) - - self.configspec = configspec - - def _set_configspec(self, section, copy): - """ - Called by validate. Handles setting the configspec on subsections - including sections to be validated by __many__ - """ - configspec = section.configspec - many = configspec.get('__many__') - if isinstance(many, dict): - for entry in section.sections: - if entry not in configspec: - section[entry].configspec = many - - for entry in configspec.sections: - if entry == '__many__': - continue - if entry not in section: - section[entry] = {} - section[entry]._created = True - if copy: - # copy comments - section.comments[entry] = configspec.comments.get(entry, []) - section.inline_comments[entry] = configspec.inline_comments.get(entry, '') - - # Could be a scalar when we expect a section - if isinstance(section[entry], Section): - section[entry].configspec = configspec[entry] - - def _write_line(self, indent_string, entry, this_entry, comment): - """Write an individual line, for the write method""" - # NOTE: the calls to self._quote here handles non-StringType values. - if not self.unrepr: - val = self._decode_element(self._quote(this_entry)) - else: - val = repr(this_entry) - return '%s%s%s%s%s' % (indent_string, - self._decode_element(self._quote(entry, multiline=False)), - ' = ', - val, - self._decode_element(comment)) - - def _write_marker(self, indent_string, depth, entry, comment): - """Write a section marker line""" - return '%s%s%s%s%s' % (indent_string, - '[' * depth, - self._quote(self._decode_element(entry), multiline=False), - ']' * depth, - self._decode_element(comment)) - - def _handle_comment(self, comment): - """Deal with a comment.""" - if not comment: - return '' - start = self.indent_type - if not comment.startswith('#'): - start += ' # ' - return (start + comment) - - # Public methods - - def write(self, outfile=None, section=None): - """ - Write the current ConfigObj as a file - - tekNico: FIXME: use StringIO instead of real files - - >>> filename = a.filename - >>> a.filename = 'test.ini' - >>> a.write() - >>> a.filename = filename - >>> a == ConfigObj('test.ini', raise_errors=True) - 1 - >>> import os - >>> os.remove('test.ini') - """ - if self.indent_type is None: - # this can be true if initialised from a dictionary - self.indent_type = DEFAULT_INDENT_TYPE - - out = [] - cs = '#' - csp = '# ' - if section is None: - int_val = self.interpolation - self.interpolation = False - section = self - for line in self.initial_comment: - line = self._decode_element(line) - stripped_line = line.strip() - if stripped_line and not stripped_line.startswith(cs): - line = csp + line - out.append(line) - - indent_string = self.indent_type * section.depth - for entry in (section.scalars + section.sections): - if entry in section.defaults: - # don't write out default values - continue - for comment_line in section.comments[entry]: - comment_line = self._decode_element(comment_line.lstrip()) - if comment_line and not comment_line.startswith(cs): - comment_line = csp + comment_line - out.append(indent_string + comment_line) - this_entry = section[entry] - comment = self._handle_comment(section.inline_comments[entry]) - - if isinstance(this_entry, dict): - # a section - out.append(self._write_marker( - indent_string, - this_entry.depth, - entry, - comment)) - out.extend(self.write(section=this_entry)) - else: - out.append(self._write_line( - indent_string, - entry, - this_entry, - comment)) - - if section is self: - for line in self.final_comment: - line = self._decode_element(line) - stripped_line = line.strip() - if stripped_line and not stripped_line.startswith(cs): - line = csp + line - out.append(line) - self.interpolation = int_val - - if section is not self: - return out - - if (self.filename is None) and (outfile is None): - - # output a list of lines - # might need to encode - # NOTE: This will *screw* UTF16, each line will start with the BOM - if self.encoding: - out = [l.encode(self.encoding) for l in out] - if (self.BOM and ((self.encoding is None) or - (BOM_LIST.get(self.encoding.lower()) == 'utf_8'))): - # Add the UTF8 BOM - if not out: - out.append('') - out[0] = BOM_UTF8 + out[0] - return out - - - # Turn the list to a string, joined with correct newlines - newline = self.newlines or os.linesep - if (getattr(outfile, 'mode', None) is not None and outfile.mode == 'w' - and sys.platform == 'win32' and newline == '\r\n'): - # Windows specific hack to avoid writing '\r\r\n' - newline = '\n' - output = newline.join(out) - if self.encoding: - output = output.encode(self.encoding) - - if self.BOM and ((self.encoding is None) or match_utf8(self.encoding)): - # Add the UTF8 BOM - output = BOM_UTF8 + output - - if not output.endswith(newline): - output += newline - if outfile is not None: - outfile.write(output) - else: - h = open(self.filename, 'wb') - output = output.encode() # encoding the data to bytes - h.write(output) - h.close() - - def validate(self, validator, preserve_errors=False, copy=False, - section=None): - """ - Test the ConfigObj against a configspec. - - It uses the ``validator`` object from *validate.py*. - - To run ``validate`` on the current ConfigObj, call: :: - - test = config.validate(validator) - - (Normally having previously passed in the configspec when the ConfigObj - was created - you can dynamically assign a dictionary of checks to the - ``configspec`` attribute of a section though). - - It returns ``True`` if everything passes, or a dictionary of - pass/fails (True/False). If every member of a subsection passes, it - will just have the value ``True``. (It also returns ``False`` if all - members fail). - - In addition, it converts the values from strings to their native - types if their checks pass (and ``stringify`` is set). - - If ``preserve_errors`` is ``True`` (``False`` is default) then instead - of a marking a fail with a ``False``, it will preserve the actual - exception object. This can contain info about the reason for failure. - For example the ``VdtValueTooSmallError`` indicates that the value - supplied was too small. If a value (or section) is missing it will - still be marked as ``False``. - - You must have the validate module to use ``preserve_errors=True``. - - You can then use the ``flatten_errors`` function to turn your nested - results dictionary into a flattened list of failures - useful for - displaying meaningful error messages. - """ - if section is None: - if self.configspec is None: - raise ValueError('No configspec supplied.') - if preserve_errors: - # We do this once to remove a top level dependency on the validate module - # Which makes importing configobj faster - from validate import VdtMissingValue - self._vdtMissingValue = VdtMissingValue - - section = self - - if copy: - section.initial_comment = section.configspec.initial_comment - section.final_comment = section.configspec.final_comment - section.encoding = section.configspec.encoding - section.BOM = section.configspec.BOM - section.newlines = section.configspec.newlines - section.indent_type = section.configspec.indent_type - - # - # section.default_values.clear() #?? - configspec = section.configspec - self._set_configspec(section, copy) - - def validate_entry(entry, spec, val, missing, ret_true, ret_false): - section.default_values.pop(entry, None) - - try: - section.default_values[entry] = validator.get_default_value(configspec[entry]) - except (KeyError, AttributeError, validator.baseErrorClass): - # No default, bad default or validator has no 'get_default_value' - # (e.g. SimpleVal) - pass - - try: - check = validator.check(spec, - val, - missing=missing - ) - except validator.baseErrorClass as e: - if not preserve_errors or isinstance(e, self._vdtMissingValue): - out[entry] = False - else: - # preserve the error - out[entry] = e - ret_false = False - ret_true = False - else: - ret_false = False - out[entry] = True - if self.stringify or missing: - # if we are doing type conversion - # or the value is a supplied default - if not self.stringify: - if isinstance(check, (list, tuple)): - # preserve lists - check = [self._str(item) for item in check] - elif missing and check is None: - # convert the None from a default to a '' - check = '' - else: - check = self._str(check) - if (check != val) or missing: - section[entry] = check - if not copy and missing and entry not in section.defaults: - section.defaults.append(entry) - return ret_true, ret_false - - # - out = {} - ret_true = True - ret_false = True - - unvalidated = [k for k in section.scalars if k not in configspec] - incorrect_sections = [k for k in configspec.sections if k in section.scalars] - incorrect_scalars = [k for k in configspec.scalars if k in section.sections] - - for entry in configspec.scalars: - if entry in ('__many__', '___many___'): - # reserved names - continue - if (not entry in section.scalars) or (entry in section.defaults): - # missing entries - # or entries from defaults - missing = True - val = None - if copy and entry not in section.scalars: - # copy comments - section.comments[entry] = ( - configspec.comments.get(entry, [])) - section.inline_comments[entry] = ( - configspec.inline_comments.get(entry, '')) - # - else: - missing = False - val = section[entry] - - ret_true, ret_false = validate_entry(entry, configspec[entry], val, - missing, ret_true, ret_false) - - many = None - if '__many__' in configspec.scalars: - many = configspec['__many__'] - elif '___many___' in configspec.scalars: - many = configspec['___many___'] - - if many is not None: - for entry in unvalidated: - val = section[entry] - ret_true, ret_false = validate_entry(entry, many, val, False, - ret_true, ret_false) - unvalidated = [] - - for entry in incorrect_scalars: - ret_true = False - if not preserve_errors: - out[entry] = False - else: - ret_false = False - msg = 'Value %r was provided as a section' % entry - out[entry] = validator.baseErrorClass(msg) - for entry in incorrect_sections: - ret_true = False - if not preserve_errors: - out[entry] = False - else: - ret_false = False - msg = 'Section %r was provided as a single value' % entry - out[entry] = validator.baseErrorClass(msg) - - # Missing sections will have been created as empty ones when the - # configspec was read. - for entry in section.sections: - # FIXME: this means DEFAULT is not copied in copy mode - if section is self and entry == 'DEFAULT': - continue - if section[entry].configspec is None: - unvalidated.append(entry) - continue - if copy: - section.comments[entry] = configspec.comments.get(entry, []) - section.inline_comments[entry] = configspec.inline_comments.get(entry, '') - check = self.validate(validator, preserve_errors=preserve_errors, copy=copy, section=section[entry]) - out[entry] = check - if check == False: - ret_true = False - elif check == True: - ret_false = False - else: - ret_true = False - - section.extra_values = unvalidated - if preserve_errors and not section._created: - # If the section wasn't created (i.e. it wasn't missing) - # then we can't return False, we need to preserve errors - ret_false = False - # - if ret_false and preserve_errors and out: - # If we are preserving errors, but all - # the failures are from missing sections / values - # then we can return False. Otherwise there is a - # real failure that we need to preserve. - ret_false = not any(out.values()) - if ret_true: - return True - elif ret_false: - return False - return out - - - def reset(self): - """Clear ConfigObj instance and restore to 'freshly created' state.""" - self.clear() - self._initialise() - # FIXME: Should be done by '_initialise', but ConfigObj constructor (and reload) - # requires an empty dictionary - self.configspec = None - # Just to be sure ;-) - self._original_configspec = None - - def reload(self): - """ - Reload a ConfigObj from file. - - This method raises a ``ReloadError`` if the ConfigObj doesn't have - a filename attribute pointing to a file. - """ - if not isinstance(self.filename, str): - raise ReloadError() - - filename = self.filename - current_options = {} - for entry in OPTION_DEFAULTS: - if entry == 'configspec': - continue - current_options[entry] = getattr(self, entry) - - configspec = self._original_configspec - current_options['configspec'] = configspec - - self.clear() - self._initialise(current_options) - self._load(filename, configspec) - -class SimpleVal(object): - """ - A simple validator. - Can be used to check that all members expected are present. - - To use it, provide a configspec with all your members in (the value given - will be ignored). Pass an instance of ``SimpleVal`` to the ``validate`` - method of your ``ConfigObj``. ``validate`` will return ``True`` if all - members are present, or a dictionary with True/False meaning - present/missing. (Whole missing sections will be replaced with ``False``) - """ - - def __init__(self): - self.baseErrorClass = ConfigObjError - - def check(self, check, member, missing=False): - """A dummy check method, always returns the value unchanged.""" - if missing: - raise self.baseErrorClass() - return member - -def flatten_errors(cfg, res, levels=None, results=None): - """ - An example function that will turn a nested dictionary of results - (as returned by ``ConfigObj.validate``) into a flat list. - - ``cfg`` is the ConfigObj instance being checked, ``res`` is the results - dictionary returned by ``validate``. - - (This is a recursive function, so you shouldn't use the ``levels`` or - ``results`` arguments - they are used by the function.) - - Returns a list of keys that failed. Each member of the list is a tuple:: - - ([list of sections...], key, result) - - If ``validate`` was called with ``preserve_errors=False`` (the default) - then ``result`` will always be ``False``. - - *list of sections* is a flattened list of sections that the key was found - in. - - If the section was missing (or a section was expected and a scalar provided - - or vice-versa) then key will be ``None``. - - If the value (or section) was missing then ``result`` will be ``False``. - - If ``validate`` was called with ``preserve_errors=True`` and a value - was present, but failed the check, then ``result`` will be the exception - object returned. You can use this as a string that describes the failure. - - For example *The value "3" is of the wrong type*. - """ - if levels is None: - # first time called - levels = [] - results = [] - if res == True: - return results - if res == False or isinstance(res, Exception): - results.append((levels[:], None, res)) - if levels: - levels.pop() - return results - for (key, val) in list(res.items()): - if val == True: - continue - if isinstance(cfg.get(key), dict): - # Go down one level - levels.append(key) - flatten_errors(cfg[key], val, levels, results) - continue - results.append((levels[:], key, val)) - # - # Go up one level - if levels: - levels.pop() - # - return results - -def get_extra_values(conf, _prepend=()): - """ - Find all the values and sections not in the configspec from a validated - ConfigObj. - - ``get_extra_values`` returns a list of tuples where each tuple represents - either an extra section, or an extra value. - - The tuples contain two values, a tuple representing the section the value - is in and the name of the extra values. For extra values in the top level - section the first member will be an empty tuple. For values in the 'foo' - section the first member will be ``('foo',)``. For members in the 'bar' - subsection of the 'foo' section the first member will be ``('foo', 'bar')``. - - NOTE: If you call ``get_extra_values`` on a ConfigObj instance that hasn't - been validated it will return an empty list. - """ - out = [] - - out.extend([(_prepend, name) for name in conf.extra_values]) - for name in conf.sections: - if name not in conf.extra_values: - out.extend(get_extra_values(conf[name], _prepend + (name,))) - return out - - -"""*A programming language is a medium of expression.* - Paul Graham""" \ No newline at end of file diff --git a/astropy/extern/configobj_py3/validate.py b/astropy/extern/configobj_py3/validate.py deleted file mode 100644 index af12814bfc60..000000000000 --- a/astropy/extern/configobj_py3/validate.py +++ /dev/null @@ -1,1419 +0,0 @@ -# validate.py -# A Validator object -# Copyright (C) 2005-2010 Michael Foord, Mark Andrews, Nicola Larosa -# E-mail: fuzzyman AT voidspace DOT org DOT uk -# mark AT la-la DOT com -# nico AT tekNico DOT net - -# This software is licensed under the terms of the BSD license. -# http://www.voidspace.org.uk/python/license.shtml -# Basically you're free to copy, modify, distribute and relicense it, -# So long as you keep a copy of the license with it. - -# Scripts maintained at http://www.voidspace.org.uk/python/index.shtml -# For information about bugfixes, updates and support, please join the -# ConfigObj mailing list: -# http://lists.sourceforge.net/lists/listinfo/configobj-develop -# Comments, suggestions and bug reports welcome. - -""" - The Validator object is used to check that supplied values - conform to a specification. - - The value can be supplied as a string - e.g. from a config file. - In this case the check will also *convert* the value to - the required type. This allows you to add validation - as a transparent layer to access data stored as strings. - The validation checks that the data is correct *and* - converts it to the expected type. - - Some standard checks are provided for basic data types. - Additional checks are easy to write. They can be - provided when the ``Validator`` is instantiated or - added afterwards. - - The standard functions work with the following basic data types : - - * integers - * floats - * booleans - * strings - * ip_addr - - plus lists of these datatypes - - Adding additional checks is done through coding simple functions. - - The full set of standard checks are : - - * 'integer': matches integer values (including negative) - Takes optional 'min' and 'max' arguments : :: - - integer() - integer(3, 9) # any value from 3 to 9 - integer(min=0) # any positive value - integer(max=9) - - * 'float': matches float values - Has the same parameters as the integer check. - - * 'boolean': matches boolean values - ``True`` or ``False`` - Acceptable string values for True are : - true, on, yes, 1 - Acceptable string values for False are : - false, off, no, 0 - - Any other value raises an error. - - * 'ip_addr': matches an Internet Protocol address, v.4, represented - by a dotted-quad string, i.e. '1.2.3.4'. - - * 'string': matches any string. - Takes optional keyword args 'min' and 'max' - to specify min and max lengths of the string. - - * 'list': matches any list. - Takes optional keyword args 'min', and 'max' to specify min and - max sizes of the list. (Always returns a list.) - - * 'tuple': matches any tuple. - Takes optional keyword args 'min', and 'max' to specify min and - max sizes of the tuple. (Always returns a tuple.) - - * 'int_list': Matches a list of integers. - Takes the same arguments as list. - - * 'float_list': Matches a list of floats. - Takes the same arguments as list. - - * 'bool_list': Matches a list of boolean values. - Takes the same arguments as list. - - * 'ip_addr_list': Matches a list of IP addresses. - Takes the same arguments as list. - - * 'string_list': Matches a list of strings. - Takes the same arguments as list. - - * 'mixed_list': Matches a list with different types in - specific positions. List size must match - the number of arguments. - - Each position can be one of : - 'integer', 'float', 'ip_addr', 'string', 'boolean' - - So to specify a list with two strings followed - by two integers, you write the check as : :: - - mixed_list('string', 'string', 'integer', 'integer') - - * 'pass': This check matches everything ! It never fails - and the value is unchanged. - - It is also the default if no check is specified. - - * 'option': This check matches any from a list of options. - You specify this check with : :: - - option('option 1', 'option 2', 'option 3') - - You can supply a default value (returned if no value is supplied) - using the default keyword argument. - - You specify a list argument for default using a list constructor syntax in - the check : :: - - checkname(arg1, arg2, default=list('val 1', 'val 2', 'val 3')) - - A badly formatted set of arguments will raise a ``VdtParamError``. -""" - -__version__ = '1.0.1' - - -__all__ = ( - '__version__', - 'dottedQuadToNum', - 'numToDottedQuad', - 'ValidateError', - 'VdtUnknownCheckError', - 'VdtParamError', - 'VdtTypeError', - 'VdtValueError', - 'VdtValueTooSmallError', - 'VdtValueTooBigError', - 'VdtValueTooShortError', - 'VdtValueTooLongError', - 'VdtMissingValue', - 'Validator', - 'is_integer', - 'is_float', - 'is_boolean', - 'is_list', - 'is_tuple', - 'is_ip_addr', - 'is_string', - 'is_int_list', - 'is_bool_list', - 'is_float_list', - 'is_string_list', - 'is_ip_addr_list', - 'is_mixed_list', - 'is_option', - '__docformat__', -) - - - -import re - - -_list_arg = re.compile(r''' - (?: - ([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*list\( - ( - (?: - \s* - (?: - (?:".*?")| # double quotes - (?:'.*?')| # single quotes - (?:[^'",\s\)][^,\)]*?) # unquoted - ) - \s*,\s* - )* - (?: - (?:".*?")| # double quotes - (?:'.*?')| # single quotes - (?:[^'",\s\)][^,\)]*?) # unquoted - )? # last one - ) - \) - ) -''', re.VERBOSE | re.DOTALL) # two groups - -_list_members = re.compile(r''' - ( - (?:".*?")| # double quotes - (?:'.*?')| # single quotes - (?:[^'",\s=][^,=]*?) # unquoted - ) - (?: - (?:\s*,\s*)|(?:\s*$) # comma - ) -''', re.VERBOSE | re.DOTALL) # one group - -_paramstring = r''' - (?: - ( - (?: - [a-zA-Z_][a-zA-Z0-9_]*\s*=\s*list\( - (?: - \s* - (?: - (?:".*?")| # double quotes - (?:'.*?')| # single quotes - (?:[^'",\s\)][^,\)]*?) # unquoted - ) - \s*,\s* - )* - (?: - (?:".*?")| # double quotes - (?:'.*?')| # single quotes - (?:[^'",\s\)][^,\)]*?) # unquoted - )? # last one - \) - )| - (?: - (?:".*?")| # double quotes - (?:'.*?')| # single quotes - (?:[^'",\s=][^,=]*?)| # unquoted - (?: # keyword argument - [a-zA-Z_][a-zA-Z0-9_]*\s*=\s* - (?: - (?:".*?")| # double quotes - (?:'.*?')| # single quotes - (?:[^'",\s=][^,=]*?) # unquoted - ) - ) - ) - ) - (?: - (?:\s*,\s*)|(?:\s*$) # comma - ) - ) - ''' - -_matchstring = '^%s*' % _paramstring - -def dottedQuadToNum(ip): - """ - Convert decimal dotted quad string to long integer - - >>> int(dottedQuadToNum('1 ')) - 1 - >>> int(dottedQuadToNum(' 1.2')) - 16777218 - >>> int(dottedQuadToNum(' 1.2.3 ')) - 16908291 - >>> int(dottedQuadToNum('1.2.3.4')) - 16909060 - >>> dottedQuadToNum('255.255.255.255') - 4294967295L - >>> dottedQuadToNum('255.255.255.256') - Traceback (most recent call last): - ValueError: Not a good dotted-quad IP: 255.255.255.256 - """ - - # import here to avoid it when ip_addr values are not used - import socket, struct - - try: - return struct.unpack('!L', - socket.inet_aton(ip.strip()))[0] - except socket.error: - # bug in inet_aton, corrected in Python 2.4 - if ip.strip() == '255.255.255.255': - return 0xFFFFFFFF - else: - raise ValueError('Not a good dotted-quad IP: %s' % ip) - return - -def numToDottedQuad(num): - """ - Convert long int to dotted quad string - - >>> numToDottedQuad(-1L) - Traceback (most recent call last): - ValueError: Not a good numeric IP: -1 - >>> numToDottedQuad(1L) - '0.0.0.1' - >>> numToDottedQuad(16777218L) - '1.0.0.2' - >>> numToDottedQuad(16908291L) - '1.2.0.3' - >>> numToDottedQuad(16909060L) - '1.2.3.4' - >>> numToDottedQuad(4294967295L) - '255.255.255.255' - >>> numToDottedQuad(4294967296L) - Traceback (most recent call last): - ValueError: Not a good numeric IP: 4294967296 - """ - - # import here to avoid it when ip_addr values are not used - import socket, struct - - # no need to intercept here, 4294967295L is fine - if num > 4294967295 or num < 0: - raise ValueError('Not a good numeric IP: %s' % num) - try: - return socket.inet_ntoa( - struct.pack('!L', int(num))) - except (socket.error, struct.error, OverflowError): - raise ValueError('Not a good numeric IP: %s' % num) - -class ValidateError(Exception): - """ - This error indicates that the check failed. - It can be the base class for more specific errors. - - Any check function that fails ought to raise this error. - (or a subclass) - - >>> raise ValidateError - Traceback (most recent call last): - ValidateError - """ - -class VdtMissingValue(ValidateError): - """No value was supplied to a check that needed one.""" - - -class VdtUnknownCheckError(ValidateError): - """An unknown check function was requested""" - - def __init__(self, value): - """ - >>> raise VdtUnknownCheckError('yoda') - Traceback (most recent call last): - VdtUnknownCheckError: the check "yoda" is unknown. - """ - ValidateError.__init__(self, 'the check "%s" is unknown.' % (value,)) - - -class VdtParamError(SyntaxError): - """An incorrect parameter was passed""" - - def __init__(self, name, value): - """ - >>> raise VdtParamError('yoda', 'jedi') - Traceback (most recent call last): - VdtParamError: passed an incorrect value "jedi" for parameter "yoda". - """ - SyntaxError.__init__(self, 'passed an incorrect value "%s" for parameter "%s".' % (value, name)) - - -class VdtTypeError(ValidateError): - """The value supplied was of the wrong type""" - - def __init__(self, value): - """ - >>> raise VdtTypeError('jedi') - Traceback (most recent call last): - VdtTypeError: the value "jedi" is of the wrong type. - """ - ValidateError.__init__(self, 'the value "%s" is of the wrong type.' % (value,)) - - -class VdtValueError(ValidateError): - """The value supplied was of the correct type, but was not an allowed value.""" - - def __init__(self, value): - """ - >>> raise VdtValueError('jedi') - Traceback (most recent call last): - VdtValueError: the value "jedi" is unacceptable. - """ - ValidateError.__init__(self, 'the value "%s" is unacceptable.' % (value,)) - - -class VdtValueTooSmallError(VdtValueError): - """The value supplied was of the correct type, but was too small.""" - - def __init__(self, value): - """ - >>> raise VdtValueTooSmallError('0') - Traceback (most recent call last): - VdtValueTooSmallError: the value "0" is too small. - """ - ValidateError.__init__(self, 'the value "%s" is too small.' % (value,)) - - -class VdtValueTooBigError(VdtValueError): - """The value supplied was of the correct type, but was too big.""" - - def __init__(self, value): - """ - >>> raise VdtValueTooBigError('1') - Traceback (most recent call last): - VdtValueTooBigError: the value "1" is too big. - """ - ValidateError.__init__(self, 'the value "%s" is too big.' % (value,)) - - -class VdtValueTooShortError(VdtValueError): - """The value supplied was of the correct type, but was too short.""" - - def __init__(self, value): - """ - >>> raise VdtValueTooShortError('jed') - Traceback (most recent call last): - VdtValueTooShortError: the value "jed" is too short. - """ - ValidateError.__init__( - self, - 'the value "%s" is too short.' % (value,)) - - -class VdtValueTooLongError(VdtValueError): - """The value supplied was of the correct type, but was too long.""" - - def __init__(self, value): - """ - >>> raise VdtValueTooLongError('jedie') - Traceback (most recent call last): - VdtValueTooLongError: the value "jedie" is too long. - """ - ValidateError.__init__(self, 'the value "%s" is too long.' % (value,)) - -class Validator(object): - """ - Validator is an object that allows you to register a set of 'checks'. - These checks take input and test that it conforms to the check. - - This can also involve converting the value from a string into - the correct datatype. - - The ``check`` method takes an input string which configures which - check is to be used and applies that check to a supplied value. - - An example input string would be: - 'int_range(param1, param2)' - - You would then provide something like: - - >>> def int_range_check(value, min, max): - ... # turn min and max from strings to integers - ... min = int(min) - ... max = int(max) - ... # check that value is of the correct type. - ... # possible valid inputs are integers or strings - ... # that represent integers - ... if not isinstance(value, (int, long, str)): - ... raise VdtTypeError(value) - ... elif isinstance(value, str): - ... # if we are given a string - ... # attempt to convert to an integer - ... try: - ... value = int(value) - ... except ValueError: - ... raise VdtValueError(value) - ... # check the value is between our constraints - ... if not min <= value: - ... raise VdtValueTooSmallError(value) - ... if not value <= max: - ... raise VdtValueTooBigError(value) - ... return value - - >>> fdict = {'int_range': int_range_check} - >>> vtr1 = Validator(fdict) - >>> vtr1.check('int_range(20, 40)', '30') - 30 - >>> vtr1.check('int_range(20, 40)', '60') - Traceback (most recent call last): - VdtValueTooBigError: the value "60" is too big. - - New functions can be added with : :: - - >>> vtr2 = Validator() - >>> vtr2.functions['int_range'] = int_range_check - - Or by passing in a dictionary of functions when Validator - is instantiated. - - Your functions *can* use keyword arguments, - but the first argument should always be 'value'. - - If the function doesn't take additional arguments, - the parentheses are optional in the check. - It can be written with either of : :: - - keyword = function_name - keyword = function_name() - - The first program to utilise Validator() was Michael Foord's - ConfigObj, an alternative to ConfigParser which supports lists and - can validate a config file using a config schema. - For more details on using Validator with ConfigObj see: - http://www.voidspace.org.uk/python/configobj.html - """ - - # this regex does the initial parsing of the checks - _func_re = re.compile(r'(.+?)\((.*)\)', re.DOTALL) - - # this regex takes apart keyword arguments - _key_arg = re.compile(r'^([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*(.*)$', re.DOTALL) - - - # this regex finds keyword=list(....) type values - _list_arg = _list_arg - - # this regex takes individual values out of lists - in one pass - _list_members = _list_members - - # These regexes check a set of arguments for validity - # and then pull the members out - _paramfinder = re.compile(_paramstring, re.VERBOSE | re.DOTALL) - _matchfinder = re.compile(_matchstring, re.VERBOSE | re.DOTALL) - - - def __init__(self, functions=None): - """ - >>> vtri = Validator() - """ - self.functions = { - '': self._pass, - 'integer': is_integer, - 'float': is_float, - 'boolean': is_boolean, - 'ip_addr': is_ip_addr, - 'string': is_string, - 'list': is_list, - 'tuple': is_tuple, - 'int_list': is_int_list, - 'float_list': is_float_list, - 'bool_list': is_bool_list, - 'ip_addr_list': is_ip_addr_list, - 'string_list': is_string_list, - 'mixed_list': is_mixed_list, - 'pass': self._pass, - 'option': is_option, - 'force_list': force_list, - } - if functions is not None: - self.functions.update(functions) - # tekNico: for use by ConfigObj - self.baseErrorClass = ValidateError - self._cache = {} - - - def check(self, check, value, missing=False): - """ - Usage: check(check, value) - - Arguments: - check: string representing check to apply (including arguments) - value: object to be checked - Returns value, converted to correct type if necessary - - If the check fails, raises a ``ValidateError`` subclass. - - >>> vtor.check('yoda', '') - Traceback (most recent call last): - VdtUnknownCheckError: the check "yoda" is unknown. - >>> vtor.check('yoda()', '') - Traceback (most recent call last): - VdtUnknownCheckError: the check "yoda" is unknown. - - >>> vtor.check('string(default="")', '', missing=True) - '' - """ - fun_name, fun_args, fun_kwargs, default = self._parse_with_caching(check) - - if missing: - if default is None: - # no information needed here - to be handled by caller - raise VdtMissingValue() - value = self._handle_none(default) - - if value is None: - return None - - return self._check_value(value, fun_name, fun_args, fun_kwargs) - - - def _handle_none(self, value): - if value == 'None': - return None - elif value in ("'None'", '"None"'): - # Special case a quoted None - value = self._unquote(value) - return value - - - def _parse_with_caching(self, check): - if check in self._cache: - fun_name, fun_args, fun_kwargs, default = self._cache[check] - # We call list and dict below to work with *copies* of the data - # rather than the original (which are mutable of course) - fun_args = list(fun_args) - fun_kwargs = dict(fun_kwargs) - else: - fun_name, fun_args, fun_kwargs, default = self._parse_check(check) - fun_kwargs = dict([(str(key), value) for (key, value) in list(fun_kwargs.items())]) - self._cache[check] = fun_name, list(fun_args), dict(fun_kwargs), default - return fun_name, fun_args, fun_kwargs, default - - - def _check_value(self, value, fun_name, fun_args, fun_kwargs): - try: - fun = self.functions[fun_name] - except KeyError: - raise VdtUnknownCheckError(fun_name) - else: - return fun(value, *fun_args, **fun_kwargs) - - - def _parse_check(self, check): - fun_match = self._func_re.match(check) - if fun_match: - fun_name = fun_match.group(1) - arg_string = fun_match.group(2) - arg_match = self._matchfinder.match(arg_string) - if arg_match is None: - # Bad syntax - raise VdtParamError('Bad syntax in check "%s".' % check) - fun_args = [] - fun_kwargs = {} - # pull out args of group 2 - for arg in self._paramfinder.findall(arg_string): - # args may need whitespace removing (before removing quotes) - arg = arg.strip() - listmatch = self._list_arg.match(arg) - if listmatch: - key, val = self._list_handle(listmatch) - fun_kwargs[key] = val - continue - keymatch = self._key_arg.match(arg) - if keymatch: - val = keymatch.group(2) - if not val in ("'None'", '"None"'): - # Special case a quoted None - val = self._unquote(val) - fun_kwargs[keymatch.group(1)] = val - continue - - fun_args.append(self._unquote(arg)) - else: - # allows for function names without (args) - return check, (), {}, None - - # Default must be deleted if the value is specified too, - # otherwise the check function will get a spurious "default" keyword arg - default = fun_kwargs.pop('default', None) - return fun_name, fun_args, fun_kwargs, default - - - def _unquote(self, val): - """Unquote a value if necessary.""" - if (len(val) >= 2) and (val[0] in ("'", '"')) and (val[0] == val[-1]): - val = val[1:-1] - return val - - - def _list_handle(self, listmatch): - """Take apart a ``keyword=list('val, 'val')`` type string.""" - out = [] - name = listmatch.group(1) - args = listmatch.group(2) - for arg in self._list_members.findall(args): - out.append(self._unquote(arg)) - return name, out - - - def _pass(self, value): - """ - Dummy check that always passes - - >>> vtor.check('', 0) - 0 - >>> vtor.check('', '0') - '0' - """ - return value - - - def get_default_value(self, check): - """ - Given a check, return the default value for the check - (converted to the right type). - - If the check doesn't specify a default value then a - ``KeyError`` will be raised. - """ - fun_name, fun_args, fun_kwargs, default = self._parse_with_caching(check) - if default is None: - raise KeyError('Check "%s" has no default value.' % check) - value = self._handle_none(default) - if value is None: - return value - return self._check_value(value, fun_name, fun_args, fun_kwargs) - -def _is_num_param(names, values, to_float=False): - """ - Return numbers from inputs or raise VdtParamError. - - Lets ``None`` pass through. - Pass in keyword argument ``to_float=True`` to - use float for the conversion rather than int. - - >>> _is_num_param(('', ''), (0, 1.0)) - [0, 1] - >>> _is_num_param(('', ''), (0, 1.0), to_float=True) - [0.0, 1.0] - >>> _is_num_param(('a'), ('a')) - Traceback (most recent call last): - VdtParamError: passed an incorrect value "a" for parameter "a". - """ - fun = to_float and float or int - out_params = [] - for (name, val) in zip(names, values): - if val is None: - out_params.append(val) - elif isinstance(val, (int, int, float, str)): - try: - out_params.append(fun(val)) - except ValueError as e: - raise VdtParamError(name, val) - else: - raise VdtParamError(name, val) - return out_params - -# built in checks -# you can override these by setting the appropriate name -# in Validator.functions -# note: if the params are specified wrongly in your input string, -# you will also raise errors. - -def is_integer(value, min=None, max=None): - """ - A check that tests that a given value is an integer (int, or long) - and optionally, between bounds. A negative value is accepted, while - a float will fail. - - If the value is a string, then the conversion is done - if possible. - Otherwise a VdtError is raised. - - >>> vtor.check('integer', '-1') - -1 - >>> vtor.check('integer', '0') - 0 - >>> vtor.check('integer', 9) - 9 - >>> vtor.check('integer', 'a') - Traceback (most recent call last): - VdtTypeError: the value "a" is of the wrong type. - >>> vtor.check('integer', '2.2') - Traceback (most recent call last): - VdtTypeError: the value "2.2" is of the wrong type. - >>> vtor.check('integer(10)', '20') - 20 - >>> vtor.check('integer(max=20)', '15') - 15 - >>> vtor.check('integer(10)', '9') - Traceback (most recent call last): - VdtValueTooSmallError: the value "9" is too small. - >>> vtor.check('integer(10)', 9) - Traceback (most recent call last): - VdtValueTooSmallError: the value "9" is too small. - >>> vtor.check('integer(max=20)', '35') - Traceback (most recent call last): - VdtValueTooBigError: the value "35" is too big. - >>> vtor.check('integer(max=20)', 35) - Traceback (most recent call last): - VdtValueTooBigError: the value "35" is too big. - >>> vtor.check('integer(0, 9)', False) - 0 - """ - (min_val, max_val) = _is_num_param(('min', 'max'), (min, max)) - if not isinstance(value, (int, int, str)): - raise VdtTypeError(value) - if isinstance(value, str): - # if it's a string - does it represent an integer ? - try: - value = int(value) - except ValueError: - raise VdtTypeError(value) - if (min_val is not None) and (value < min_val): - raise VdtValueTooSmallError(value) - if (max_val is not None) and (value > max_val): - raise VdtValueTooBigError(value) - return value - -def is_float(value, min=None, max=None): - """ - A check that tests that a given value is a float - (an integer will be accepted), and optionally - that it is between bounds. - - If the value is a string, then the conversion is done - if possible. - Otherwise a VdtError is raised. - - This can accept negative values. - - >>> vtor.check('float', '2') - 2.0 - - From now on we multiply the value to avoid comparing decimals - - >>> vtor.check('float', '-6.8') * 10 - -68.0 - >>> vtor.check('float', '12.2') * 10 - 122.0 - >>> vtor.check('float', 8.4) * 10 - 84.0 - >>> vtor.check('float', 'a') - Traceback (most recent call last): - VdtTypeError: the value "a" is of the wrong type. - >>> vtor.check('float(10.1)', '10.2') * 10 - 102.0 - >>> vtor.check('float(max=20.2)', '15.1') * 10 - 151.0 - >>> vtor.check('float(10.0)', '9.0') - Traceback (most recent call last): - VdtValueTooSmallError: the value "9.0" is too small. - >>> vtor.check('float(max=20.0)', '35.0') - Traceback (most recent call last): - VdtValueTooBigError: the value "35.0" is too big. - """ - (min_val, max_val) = _is_num_param( - ('min', 'max'), (min, max), to_float=True) - if not isinstance(value, (int, int, float, str)): - raise VdtTypeError(value) - if not isinstance(value, float): - # if it's a string - does it represent a float ? - try: - value = float(value) - except ValueError: - raise VdtTypeError(value) - if (min_val is not None) and (value < min_val): - raise VdtValueTooSmallError(value) - if (max_val is not None) and (value > max_val): - raise VdtValueTooBigError(value) - return value - -bool_dict = { - True: True, 'on': True, '1': True, 'true': True, 'yes': True, - False: False, 'off': False, '0': False, 'false': False, 'no': False, -} - -def is_boolean(value): - """ - Check if the value represents a boolean. - - >>> vtor.check('boolean', 0) - 0 - >>> vtor.check('boolean', False) - 0 - >>> vtor.check('boolean', '0') - 0 - >>> vtor.check('boolean', 'off') - 0 - >>> vtor.check('boolean', 'false') - 0 - >>> vtor.check('boolean', 'no') - 0 - >>> vtor.check('boolean', 'nO') - 0 - >>> vtor.check('boolean', 'NO') - 0 - >>> vtor.check('boolean', 1) - 1 - >>> vtor.check('boolean', True) - 1 - >>> vtor.check('boolean', '1') - 1 - >>> vtor.check('boolean', 'on') - 1 - >>> vtor.check('boolean', 'true') - 1 - >>> vtor.check('boolean', 'yes') - 1 - >>> vtor.check('boolean', 'Yes') - 1 - >>> vtor.check('boolean', 'YES') - 1 - >>> vtor.check('boolean', '') - Traceback (most recent call last): - VdtTypeError: the value "" is of the wrong type. - >>> vtor.check('boolean', 'up') - Traceback (most recent call last): - VdtTypeError: the value "up" is of the wrong type. - - """ - if isinstance(value, str): - try: - return bool_dict[value.lower()] - except KeyError: - raise VdtTypeError(value) - # we do an equality test rather than an identity test - # this ensures Python 2.2 compatibilty - # and allows 0 and 1 to represent True and False - if value == False: - return False - elif value == True: - return True - else: - raise VdtTypeError(value) - -def is_ip_addr(value): - """ - Check that the supplied value is an Internet Protocol address, v.4, - represented by a dotted-quad string, i.e. '1.2.3.4'. - - >>> vtor.check('ip_addr', '1 ') - '1' - >>> vtor.check('ip_addr', ' 1.2') - '1.2' - >>> vtor.check('ip_addr', ' 1.2.3 ') - '1.2.3' - >>> vtor.check('ip_addr', '1.2.3.4') - '1.2.3.4' - >>> vtor.check('ip_addr', '0.0.0.0') - '0.0.0.0' - >>> vtor.check('ip_addr', '255.255.255.255') - '255.255.255.255' - >>> vtor.check('ip_addr', '255.255.255.256') - Traceback (most recent call last): - VdtValueError: the value "255.255.255.256" is unacceptable. - >>> vtor.check('ip_addr', '1.2.3.4.5') - Traceback (most recent call last): - VdtValueError: the value "1.2.3.4.5" is unacceptable. - >>> vtor.check('ip_addr', 0) - Traceback (most recent call last): - VdtTypeError: the value "0" is of the wrong type. - """ - if not isinstance(value, str): - raise VdtTypeError(value) - value = value.strip() - try: - dottedQuadToNum(value) - except ValueError: - raise VdtValueError(value) - return value - -def is_list(value, min=None, max=None): - """ - Check that the value is a list of values. - - You can optionally specify the minimum and maximum number of members. - - It does no check on list members. - - >>> vtor.check('list', ()) - [] - >>> vtor.check('list', []) - [] - >>> vtor.check('list', (1, 2)) - [1, 2] - >>> vtor.check('list', [1, 2]) - [1, 2] - >>> vtor.check('list(3)', (1, 2)) - Traceback (most recent call last): - VdtValueTooShortError: the value "(1, 2)" is too short. - >>> vtor.check('list(max=5)', (1, 2, 3, 4, 5, 6)) - Traceback (most recent call last): - VdtValueTooLongError: the value "(1, 2, 3, 4, 5, 6)" is too long. - >>> vtor.check('list(min=3, max=5)', (1, 2, 3, 4)) - [1, 2, 3, 4] - >>> vtor.check('list', 0) - Traceback (most recent call last): - VdtTypeError: the value "0" is of the wrong type. - >>> vtor.check('list', '12') - Traceback (most recent call last): - VdtTypeError: the value "12" is of the wrong type. - """ - (min_len, max_len) = _is_num_param(('min', 'max'), (min, max)) - if isinstance(value, str): - raise VdtTypeError(value) - try: - num_members = len(value) - except TypeError: - raise VdtTypeError(value) - if min_len is not None and num_members < min_len: - raise VdtValueTooShortError(value) - if max_len is not None and num_members > max_len: - raise VdtValueTooLongError(value) - return list(value) - -def is_tuple(value, min=None, max=None): - """ - Check that the value is a tuple of values. - - You can optionally specify the minimum and maximum number of members. - - It does no check on members. - - >>> vtor.check('tuple', ()) - () - >>> vtor.check('tuple', []) - () - >>> vtor.check('tuple', (1, 2)) - (1, 2) - >>> vtor.check('tuple', [1, 2]) - (1, 2) - >>> vtor.check('tuple(3)', (1, 2)) - Traceback (most recent call last): - VdtValueTooShortError: the value "(1, 2)" is too short. - >>> vtor.check('tuple(max=5)', (1, 2, 3, 4, 5, 6)) - Traceback (most recent call last): - VdtValueTooLongError: the value "(1, 2, 3, 4, 5, 6)" is too long. - >>> vtor.check('tuple(min=3, max=5)', (1, 2, 3, 4)) - (1, 2, 3, 4) - >>> vtor.check('tuple', 0) - Traceback (most recent call last): - VdtTypeError: the value "0" is of the wrong type. - >>> vtor.check('tuple', '12') - Traceback (most recent call last): - VdtTypeError: the value "12" is of the wrong type. - """ - return tuple(is_list(value, min, max)) - -def is_string(value, min=None, max=None): - """ - Check that the supplied value is a string. - - You can optionally specify the minimum and maximum number of members. - - >>> vtor.check('string', '0') - '0' - >>> vtor.check('string', 0) - Traceback (most recent call last): - VdtTypeError: the value "0" is of the wrong type. - >>> vtor.check('string(2)', '12') - '12' - >>> vtor.check('string(2)', '1') - Traceback (most recent call last): - VdtValueTooShortError: the value "1" is too short. - >>> vtor.check('string(min=2, max=3)', '123') - '123' - >>> vtor.check('string(min=2, max=3)', '1234') - Traceback (most recent call last): - VdtValueTooLongError: the value "1234" is too long. - """ - if not isinstance(value, str): - raise VdtTypeError(value) - (min_len, max_len) = _is_num_param(('min', 'max'), (min, max)) - try: - num_members = len(value) - except TypeError: - raise VdtTypeError(value) - if min_len is not None and num_members < min_len: - raise VdtValueTooShortError(value) - if max_len is not None and num_members > max_len: - raise VdtValueTooLongError(value) - return value - - -def is_int_list(value, min=None, max=None): - """ - Check that the value is a list of integers. - - You can optionally specify the minimum and maximum number of members. - - Each list member is checked that it is an integer. - - >>> vtor.check('int_list', ()) - [] - >>> vtor.check('int_list', []) - [] - >>> vtor.check('int_list', (1, 2)) - [1, 2] - >>> vtor.check('int_list', [1, 2]) - [1, 2] - >>> vtor.check('int_list', [1, 'a']) - Traceback (most recent call last): - VdtTypeError: the value "a" is of the wrong type. - """ - return [is_integer(mem) for mem in is_list(value, min, max)] - - -def is_bool_list(value, min=None, max=None): - """ - Check that the value is a list of booleans. - - You can optionally specify the minimum and maximum number of members. - - Each list member is checked that it is a boolean. - - >>> vtor.check('bool_list', ()) - [] - >>> vtor.check('bool_list', []) - [] - >>> check_res = vtor.check('bool_list', (True, False)) - >>> check_res == [True, False] - 1 - >>> check_res = vtor.check('bool_list', [True, False]) - >>> check_res == [True, False] - 1 - >>> vtor.check('bool_list', [True, 'a']) - Traceback (most recent call last): - VdtTypeError: the value "a" is of the wrong type. - """ - return [is_boolean(mem) for mem in is_list(value, min, max)] - - -def is_float_list(value, min=None, max=None): - """ - Check that the value is a list of floats. - - You can optionally specify the minimum and maximum number of members. - - Each list member is checked that it is a float. - - >>> vtor.check('float_list', ()) - [] - >>> vtor.check('float_list', []) - [] - >>> vtor.check('float_list', (1, 2.0)) - [1.0, 2.0] - >>> vtor.check('float_list', [1, 2.0]) - [1.0, 2.0] - >>> vtor.check('float_list', [1, 'a']) - Traceback (most recent call last): - VdtTypeError: the value "a" is of the wrong type. - """ - return [is_float(mem) for mem in is_list(value, min, max)] - -def is_string_list(value, min=None, max=None): - """ - Check that the value is a list of strings. - - You can optionally specify the minimum and maximum number of members. - - Each list member is checked that it is a string. - - >>> vtor.check('string_list', ()) - [] - >>> vtor.check('string_list', []) - [] - >>> vtor.check('string_list', ('a', 'b')) - ['a', 'b'] - >>> vtor.check('string_list', ['a', 1]) - Traceback (most recent call last): - VdtTypeError: the value "1" is of the wrong type. - >>> vtor.check('string_list', 'hello') - Traceback (most recent call last): - VdtTypeError: the value "hello" is of the wrong type. - """ - if isinstance(value, str): - raise VdtTypeError(value) - return [is_string(mem) for mem in is_list(value, min, max)] - -def is_ip_addr_list(value, min=None, max=None): - """ - Check that the value is a list of IP addresses. - - You can optionally specify the minimum and maximum number of members. - - Each list member is checked that it is an IP address. - - >>> vtor.check('ip_addr_list', ()) - [] - >>> vtor.check('ip_addr_list', []) - [] - >>> vtor.check('ip_addr_list', ('1.2.3.4', '5.6.7.8')) - ['1.2.3.4', '5.6.7.8'] - >>> vtor.check('ip_addr_list', ['a']) - Traceback (most recent call last): - VdtValueError: the value "a" is unacceptable. - """ - return [is_ip_addr(mem) for mem in is_list(value, min, max)] - -def force_list(value, min=None, max=None): - """ - Check that a value is a list, coercing strings into - a list with one member. Useful where users forget the - trailing comma that turns a single value into a list. - - You can optionally specify the minimum and maximum number of members. - A minumum of greater than one will fail if the user only supplies a - string. - - >>> vtor.check('force_list', ()) - [] - >>> vtor.check('force_list', []) - [] - >>> vtor.check('force_list', 'hello') - ['hello'] - """ - if not isinstance(value, (list, tuple)): - value = [value] - return is_list(value, min, max) - -fun_dict = { - 'integer': is_integer, - 'float': is_float, - 'ip_addr': is_ip_addr, - 'string': is_string, - 'boolean': is_boolean, -} - - -def is_mixed_list(value, *args): - """ - Check that the value is a list. - Allow specifying the type of each member. - Work on lists of specific lengths. - - You specify each member as a positional argument specifying type - - Each type should be one of the following strings : - 'integer', 'float', 'ip_addr', 'string', 'boolean' - - So you can specify a list of two strings, followed by - two integers as : - - mixed_list('string', 'string', 'integer', 'integer') - - The length of the list must match the number of positional - arguments you supply. - - >>> mix_str = "mixed_list('integer', 'float', 'ip_addr', 'string', 'boolean')" - >>> check_res = vtor.check(mix_str, (1, 2.0, '1.2.3.4', 'a', True)) - >>> check_res == [1, 2.0, '1.2.3.4', 'a', True] - 1 - >>> check_res = vtor.check(mix_str, ('1', '2.0', '1.2.3.4', 'a', 'True')) - >>> check_res == [1, 2.0, '1.2.3.4', 'a', True] - 1 - >>> vtor.check(mix_str, ('b', 2.0, '1.2.3.4', 'a', True)) - Traceback (most recent call last): - VdtTypeError: the value "b" is of the wrong type. - >>> vtor.check(mix_str, (1, 2.0, '1.2.3.4', 'a')) - Traceback (most recent call last): - VdtValueTooShortError: the value "(1, 2.0, '1.2.3.4', 'a')" is too short. - >>> vtor.check(mix_str, (1, 2.0, '1.2.3.4', 'a', 1, 'b')) - Traceback (most recent call last): - VdtValueTooLongError: the value "(1, 2.0, '1.2.3.4', 'a', 1, 'b')" is too long. - >>> vtor.check(mix_str, 0) - Traceback (most recent call last): - VdtTypeError: the value "0" is of the wrong type. - - This test requires an elaborate setup, because of a change in error string - output from the interpreter between Python 2.2 and 2.3 . - - >>> res_seq = ( - ... 'passed an incorrect value "', - ... 'yoda', - ... '" for parameter "mixed_list".', - ... ) - >>> res_str = "'".join(res_seq) - >>> try: - ... vtor.check('mixed_list("yoda")', ('a')) - ... except VdtParamError, err: - ... str(err) == res_str - 1 - """ - try: - length = len(value) - except TypeError: - raise VdtTypeError(value) - if length < len(args): - raise VdtValueTooShortError(value) - elif length > len(args): - raise VdtValueTooLongError(value) - try: - return [fun_dict[arg](val) for arg, val in zip(args, value)] - except KeyError as e: - raise VdtParamError('mixed_list', e) - - -def is_option(value, *options): - """ - This check matches the value to any of a set of options. - - >>> vtor.check('option("yoda", "jedi")', 'yoda') - 'yoda' - >>> vtor.check('option("yoda", "jedi")', 'jed') - Traceback (most recent call last): - VdtValueError: the value "jed" is unacceptable. - >>> vtor.check('option("yoda", "jedi")', 0) - Traceback (most recent call last): - VdtTypeError: the value "0" is of the wrong type. - """ - if not isinstance(value, str): - raise VdtTypeError(value) - if not value in options: - raise VdtValueError(value) - return value - -def _test(value, *args, **keywargs): - """ - A function that exists for test purposes. - - >>> checks = [ - ... '3, 6, min=1, max=3, test=list(a, b, c)', - ... '3', - ... '3, 6', - ... '3,', - ... 'min=1, test="a b c"', - ... 'min=5, test="a, b, c"', - ... 'min=1, max=3, test="a, b, c"', - ... 'min=-100, test=-99', - ... 'min=1, max=3', - ... '3, 6, test="36"', - ... '3, 6, test="a, b, c"', - ... '3, max=3, test=list("a", "b", "c")', - ... '''3, max=3, test=list("'a'", 'b', "x=(c)")''', - ... "test='x=fish(3)'", - ... ] - >>> v = Validator({'test': _test}) - >>> for entry in checks: - ... print v.check(('test(%s)' % entry), 3) - (3, ('3', '6'), {'test': ['a', 'b', 'c'], 'max': '3', 'min': '1'}) - (3, ('3',), {}) - (3, ('3', '6'), {}) - (3, ('3',), {}) - (3, (), {'test': 'a b c', 'min': '1'}) - (3, (), {'test': 'a, b, c', 'min': '5'}) - (3, (), {'test': 'a, b, c', 'max': '3', 'min': '1'}) - (3, (), {'test': '-99', 'min': '-100'}) - (3, (), {'max': '3', 'min': '1'}) - (3, ('3', '6'), {'test': '36'}) - (3, ('3', '6'), {'test': 'a, b, c'}) - (3, ('3',), {'test': ['a', 'b', 'c'], 'max': '3'}) - (3, ('3',), {'test': ["'a'", 'b', 'x=(c)'], 'max': '3'}) - (3, (), {'test': 'x=fish(3)'}) - - >>> v = Validator() - >>> v.check('integer(default=6)', '3') - 3 - >>> v.check('integer(default=6)', None, True) - 6 - >>> v.get_default_value('integer(default=6)') - 6 - >>> v.get_default_value('float(default=6)') - 6.0 - >>> v.get_default_value('pass(default=None)') - >>> v.get_default_value("string(default='None')") - 'None' - >>> v.get_default_value('pass') - Traceback (most recent call last): - KeyError: 'Check "pass" has no default value.' - >>> v.get_default_value('pass(default=list(1, 2, 3, 4))') - ['1', '2', '3', '4'] - - >>> v = Validator() - >>> v.check("pass(default=None)", None, True) - >>> v.check("pass(default='None')", None, True) - 'None' - >>> v.check('pass(default="None")', None, True) - 'None' - >>> v.check('pass(default=list(1, 2, 3, 4))', None, True) - ['1', '2', '3', '4'] - - Bug test for unicode arguments - >>> v = Validator() - >>> v.check(u'string(min=4)', u'test') - u'test' - - >>> v = Validator() - >>> v.get_default_value(u'string(min=4, default="1234")') - u'1234' - >>> v.check(u'string(min=4, default="1234")', u'test') - u'test' - - >>> v = Validator() - >>> default = v.get_default_value('string(default=None)') - >>> default == None - 1 - """ - return (value, args, keywargs) - - -def _test2(): - """ - >>> - >>> v = Validator() - >>> v.get_default_value('string(default="#ff00dd")') - '#ff00dd' - >>> v.get_default_value('integer(default=3) # comment') - 3 - """ - -def _test3(): - r""" - >>> vtor.check('string(default="")', '', missing=True) - '' - >>> vtor.check('string(default="\n")', '', missing=True) - '\n' - >>> print vtor.check('string(default="\n")', '', missing=True), - - >>> vtor.check('string()', '\n') - '\n' - >>> vtor.check('string(default="\n\n\n")', '', missing=True) - '\n\n\n' - >>> vtor.check('string()', 'random \n text goes here\n\n') - 'random \n text goes here\n\n' - >>> vtor.check('string(default=" \nrandom text\ngoes \n here\n\n ")', - ... '', missing=True) - ' \nrandom text\ngoes \n here\n\n ' - >>> vtor.check("string(default='\n\n\n')", '', missing=True) - '\n\n\n' - >>> vtor.check("option('\n','a','b',default='\n')", '', missing=True) - '\n' - >>> vtor.check("string_list()", ['foo', '\n', 'bar']) - ['foo', '\n', 'bar'] - >>> vtor.check("string_list(default=list('\n'))", '', missing=True) - ['\n'] - """ - -if __name__ == '__main__': - # run the code tests in doctest format - import sys - import doctest - m = sys.modules.get('__main__') - globs = m.__dict__.copy() - globs.update({ - 'vtor': Validator(), - }) - doctest.testmod(m, globs=globs) \ No newline at end of file diff --git a/astropy/extern/ply/__init__.py b/astropy/extern/ply/__init__.py new file mode 100644 index 000000000000..23707c63541e --- /dev/null +++ b/astropy/extern/ply/__init__.py @@ -0,0 +1,5 @@ +# PLY package +# Author: David Beazley (dave@dabeaz.com) + +__version__ = '3.11' +__all__ = ['lex','yacc'] diff --git a/astropy/extern/ply/cpp.py b/astropy/extern/ply/cpp.py new file mode 100644 index 000000000000..2422916c9f54 --- /dev/null +++ b/astropy/extern/ply/cpp.py @@ -0,0 +1,914 @@ +# ----------------------------------------------------------------------------- +# cpp.py +# +# Author: David Beazley (http://www.dabeaz.com) +# Copyright (C) 2007 +# All rights reserved +# +# This module implements an ANSI-C style lexical preprocessor for PLY. +# ----------------------------------------------------------------------------- +from __future__ import generators + +import sys + +# Some Python 3 compatibility shims +if sys.version_info.major < 3: + STRING_TYPES = (str, unicode) +else: + STRING_TYPES = str + xrange = range + +# ----------------------------------------------------------------------------- +# Default preprocessor lexer definitions. These tokens are enough to get +# a basic preprocessor working. Other modules may import these if they want +# ----------------------------------------------------------------------------- + +tokens = ( + 'CPP_ID','CPP_INTEGER', 'CPP_FLOAT', 'CPP_STRING', 'CPP_CHAR', 'CPP_WS', 'CPP_COMMENT1', 'CPP_COMMENT2', 'CPP_POUND','CPP_DPOUND' +) + +literals = "+-*/%|&~^<>=!?()[]{}.,;:\\\'\"" + +# Whitespace +def t_CPP_WS(t): + r'\s+' + t.lexer.lineno += t.value.count("\n") + return t + +t_CPP_POUND = r'\#' +t_CPP_DPOUND = r'\#\#' + +# Identifier +t_CPP_ID = r'[A-Za-z_][\w_]*' + +# Integer literal +def CPP_INTEGER(t): + r'(((((0x)|(0X))[0-9a-fA-F]+)|(\d+))([uU][lL]|[lL][uU]|[uU]|[lL])?)' + return t + +t_CPP_INTEGER = CPP_INTEGER + +# Floating literal +t_CPP_FLOAT = r'((\d+)(\.\d+)(e(\+|-)?(\d+))? | (\d+)e(\+|-)?(\d+))([lL]|[fF])?' + +# String literal +def t_CPP_STRING(t): + r'\"([^\\\n]|(\\(.|\n)))*?\"' + t.lexer.lineno += t.value.count("\n") + return t + +# Character constant 'c' or L'c' +def t_CPP_CHAR(t): + r'(L)?\'([^\\\n]|(\\(.|\n)))*?\'' + t.lexer.lineno += t.value.count("\n") + return t + +# Comment +def t_CPP_COMMENT1(t): + r'(/\*(.|\n)*?\*/)' + ncr = t.value.count("\n") + t.lexer.lineno += ncr + # replace with one space or a number of '\n' + t.type = 'CPP_WS'; t.value = '\n' * ncr if ncr else ' ' + return t + +# Line comment +def t_CPP_COMMENT2(t): + r'(//.*?(\n|$))' + # replace with '/n' + t.type = 'CPP_WS'; t.value = '\n' + return t + +def t_error(t): + t.type = t.value[0] + t.value = t.value[0] + t.lexer.skip(1) + return t + +import re +import copy +import time +import os.path + +# ----------------------------------------------------------------------------- +# trigraph() +# +# Given an input string, this function replaces all trigraph sequences. +# The following mapping is used: +# +# ??= # +# ??/ \ +# ??' ^ +# ??( [ +# ??) ] +# ??! | +# ??< { +# ??> } +# ??- ~ +# ----------------------------------------------------------------------------- + +_trigraph_pat = re.compile(r'''\?\?[=/\'\(\)\!<>\-]''') +_trigraph_rep = { + '=':'#', + '/':'\\', + "'":'^', + '(':'[', + ')':']', + '!':'|', + '<':'{', + '>':'}', + '-':'~' +} + +def trigraph(input): + return _trigraph_pat.sub(lambda g: _trigraph_rep[g.group()[-1]],input) + +# ------------------------------------------------------------------ +# Macro object +# +# This object holds information about preprocessor macros +# +# .name - Macro name (string) +# .value - Macro value (a list of tokens) +# .arglist - List of argument names +# .variadic - Boolean indicating whether or not variadic macro +# .vararg - Name of the variadic parameter +# +# When a macro is created, the macro replacement token sequence is +# pre-scanned and used to create patch lists that are later used +# during macro expansion +# ------------------------------------------------------------------ + +class Macro(object): + def __init__(self,name,value,arglist=None,variadic=False): + self.name = name + self.value = value + self.arglist = arglist + self.variadic = variadic + if variadic: + self.vararg = arglist[-1] + self.source = None + +# ------------------------------------------------------------------ +# Preprocessor object +# +# Object representing a preprocessor. Contains macro definitions, +# include directories, and other information +# ------------------------------------------------------------------ + +class Preprocessor(object): + def __init__(self,lexer=None): + if lexer is None: + lexer = lex.lexer + self.lexer = lexer + self.macros = { } + self.path = [] + self.temp_path = [] + + # Probe the lexer for selected tokens + self.lexprobe() + + tm = time.localtime() + self.define("__DATE__ \"%s\"" % time.strftime("%b %d %Y",tm)) + self.define("__TIME__ \"%s\"" % time.strftime("%H:%M:%S",tm)) + self.parser = None + + # ----------------------------------------------------------------------------- + # tokenize() + # + # Utility function. Given a string of text, tokenize into a list of tokens + # ----------------------------------------------------------------------------- + + def tokenize(self,text): + tokens = [] + self.lexer.input(text) + while True: + tok = self.lexer.token() + if not tok: break + tokens.append(tok) + return tokens + + # --------------------------------------------------------------------- + # error() + # + # Report a preprocessor error/warning of some kind + # ---------------------------------------------------------------------- + + def error(self,file,line,msg): + print("%s:%d %s" % (file,line,msg)) + + # ---------------------------------------------------------------------- + # lexprobe() + # + # This method probes the preprocessor lexer object to discover + # the token types of symbols that are important to the preprocessor. + # If this works right, the preprocessor will simply "work" + # with any suitable lexer regardless of how tokens have been named. + # ---------------------------------------------------------------------- + + def lexprobe(self): + + # Determine the token type for identifiers + self.lexer.input("identifier") + tok = self.lexer.token() + if not tok or tok.value != "identifier": + print("Couldn't determine identifier type") + else: + self.t_ID = tok.type + + # Determine the token type for integers + self.lexer.input("12345") + tok = self.lexer.token() + if not tok or int(tok.value) != 12345: + print("Couldn't determine integer type") + else: + self.t_INTEGER = tok.type + self.t_INTEGER_TYPE = type(tok.value) + + # Determine the token type for strings enclosed in double quotes + self.lexer.input("\"filename\"") + tok = self.lexer.token() + if not tok or tok.value != "\"filename\"": + print("Couldn't determine string type") + else: + self.t_STRING = tok.type + + # Determine the token type for whitespace--if any + self.lexer.input(" ") + tok = self.lexer.token() + if not tok or tok.value != " ": + self.t_SPACE = None + else: + self.t_SPACE = tok.type + + # Determine the token type for newlines + self.lexer.input("\n") + tok = self.lexer.token() + if not tok or tok.value != "\n": + self.t_NEWLINE = None + print("Couldn't determine token for newlines") + else: + self.t_NEWLINE = tok.type + + self.t_WS = (self.t_SPACE, self.t_NEWLINE) + + # Check for other characters used by the preprocessor + chars = [ '<','>','#','##','\\','(',')',',','.'] + for c in chars: + self.lexer.input(c) + tok = self.lexer.token() + if not tok or tok.value != c: + print("Unable to lex '%s' required for preprocessor" % c) + + # ---------------------------------------------------------------------- + # add_path() + # + # Adds a search path to the preprocessor. + # ---------------------------------------------------------------------- + + def add_path(self,path): + self.path.append(path) + + # ---------------------------------------------------------------------- + # group_lines() + # + # Given an input string, this function splits it into lines. Trailing whitespace + # is removed. Any line ending with \ is grouped with the next line. This + # function forms the lowest level of the preprocessor---grouping into text into + # a line-by-line format. + # ---------------------------------------------------------------------- + + def group_lines(self,input): + lex = self.lexer.clone() + lines = [x.rstrip() for x in input.splitlines()] + for i in xrange(len(lines)): + j = i+1 + while lines[i].endswith('\\') and (j < len(lines)): + lines[i] = lines[i][:-1]+lines[j] + lines[j] = "" + j += 1 + + input = "\n".join(lines) + lex.input(input) + lex.lineno = 1 + + current_line = [] + while True: + tok = lex.token() + if not tok: + break + current_line.append(tok) + if tok.type in self.t_WS and '\n' in tok.value: + yield current_line + current_line = [] + + if current_line: + yield current_line + + # ---------------------------------------------------------------------- + # tokenstrip() + # + # Remove leading/trailing whitespace tokens from a token list + # ---------------------------------------------------------------------- + + def tokenstrip(self,tokens): + i = 0 + while i < len(tokens) and tokens[i].type in self.t_WS: + i += 1 + del tokens[:i] + i = len(tokens)-1 + while i >= 0 and tokens[i].type in self.t_WS: + i -= 1 + del tokens[i+1:] + return tokens + + + # ---------------------------------------------------------------------- + # collect_args() + # + # Collects comma separated arguments from a list of tokens. The arguments + # must be enclosed in parenthesis. Returns a tuple (tokencount,args,positions) + # where tokencount is the number of tokens consumed, args is a list of arguments, + # and positions is a list of integers containing the starting index of each + # argument. Each argument is represented by a list of tokens. + # + # When collecting arguments, leading and trailing whitespace is removed + # from each argument. + # + # This function properly handles nested parenthesis and commas---these do not + # define new arguments. + # ---------------------------------------------------------------------- + + def collect_args(self,tokenlist): + args = [] + positions = [] + current_arg = [] + nesting = 1 + tokenlen = len(tokenlist) + + # Search for the opening '('. + i = 0 + while (i < tokenlen) and (tokenlist[i].type in self.t_WS): + i += 1 + + if (i < tokenlen) and (tokenlist[i].value == '('): + positions.append(i+1) + else: + self.error(self.source,tokenlist[0].lineno,"Missing '(' in macro arguments") + return 0, [], [] + + i += 1 + + while i < tokenlen: + t = tokenlist[i] + if t.value == '(': + current_arg.append(t) + nesting += 1 + elif t.value == ')': + nesting -= 1 + if nesting == 0: + if current_arg: + args.append(self.tokenstrip(current_arg)) + positions.append(i) + return i+1,args,positions + current_arg.append(t) + elif t.value == ',' and nesting == 1: + args.append(self.tokenstrip(current_arg)) + positions.append(i+1) + current_arg = [] + else: + current_arg.append(t) + i += 1 + + # Missing end argument + self.error(self.source,tokenlist[-1].lineno,"Missing ')' in macro arguments") + return 0, [],[] + + # ---------------------------------------------------------------------- + # macro_prescan() + # + # Examine the macro value (token sequence) and identify patch points + # This is used to speed up macro expansion later on---we'll know + # right away where to apply patches to the value to form the expansion + # ---------------------------------------------------------------------- + + def macro_prescan(self,macro): + macro.patch = [] # Standard macro arguments + macro.str_patch = [] # String conversion expansion + macro.var_comma_patch = [] # Variadic macro comma patch + i = 0 + while i < len(macro.value): + if macro.value[i].type == self.t_ID and macro.value[i].value in macro.arglist: + argnum = macro.arglist.index(macro.value[i].value) + # Conversion of argument to a string + if i > 0 and macro.value[i-1].value == '#': + macro.value[i] = copy.copy(macro.value[i]) + macro.value[i].type = self.t_STRING + del macro.value[i-1] + macro.str_patch.append((argnum,i-1)) + continue + # Concatenation + elif (i > 0 and macro.value[i-1].value == '##'): + macro.patch.append(('c',argnum,i-1)) + del macro.value[i-1] + i -= 1 + continue + elif ((i+1) < len(macro.value) and macro.value[i+1].value == '##'): + macro.patch.append(('c',argnum,i)) + del macro.value[i + 1] + continue + # Standard expansion + else: + macro.patch.append(('e',argnum,i)) + elif macro.value[i].value == '##': + if macro.variadic and (i > 0) and (macro.value[i-1].value == ',') and \ + ((i+1) < len(macro.value)) and (macro.value[i+1].type == self.t_ID) and \ + (macro.value[i+1].value == macro.vararg): + macro.var_comma_patch.append(i-1) + i += 1 + macro.patch.sort(key=lambda x: x[2],reverse=True) + + # ---------------------------------------------------------------------- + # macro_expand_args() + # + # Given a Macro and list of arguments (each a token list), this method + # returns an expanded version of a macro. The return value is a token sequence + # representing the replacement macro tokens + # ---------------------------------------------------------------------- + + def macro_expand_args(self,macro,args): + # Make a copy of the macro token sequence + rep = [copy.copy(_x) for _x in macro.value] + + # Make string expansion patches. These do not alter the length of the replacement sequence + + str_expansion = {} + for argnum, i in macro.str_patch: + if argnum not in str_expansion: + str_expansion[argnum] = ('"%s"' % "".join([x.value for x in args[argnum]])).replace("\\","\\\\") + rep[i] = copy.copy(rep[i]) + rep[i].value = str_expansion[argnum] + + # Make the variadic macro comma patch. If the variadic macro argument is empty, we get rid + comma_patch = False + if macro.variadic and not args[-1]: + for i in macro.var_comma_patch: + rep[i] = None + comma_patch = True + + # Make all other patches. The order of these matters. It is assumed that the patch list + # has been sorted in reverse order of patch location since replacements will cause the + # size of the replacement sequence to expand from the patch point. + + expanded = { } + for ptype, argnum, i in macro.patch: + # Concatenation. Argument is left unexpanded + if ptype == 'c': + rep[i:i+1] = args[argnum] + # Normal expansion. Argument is macro expanded first + elif ptype == 'e': + if argnum not in expanded: + expanded[argnum] = self.expand_macros(args[argnum]) + rep[i:i+1] = expanded[argnum] + + # Get rid of removed comma if necessary + if comma_patch: + rep = [_i for _i in rep if _i] + + return rep + + + # ---------------------------------------------------------------------- + # expand_macros() + # + # Given a list of tokens, this function performs macro expansion. + # The expanded argument is a dictionary that contains macros already + # expanded. This is used to prevent infinite recursion. + # ---------------------------------------------------------------------- + + def expand_macros(self,tokens,expanded=None): + if expanded is None: + expanded = {} + i = 0 + while i < len(tokens): + t = tokens[i] + if t.type == self.t_ID: + if t.value in self.macros and t.value not in expanded: + # Yes, we found a macro match + expanded[t.value] = True + + m = self.macros[t.value] + if not m.arglist: + # A simple macro + ex = self.expand_macros([copy.copy(_x) for _x in m.value],expanded) + for e in ex: + e.lineno = t.lineno + tokens[i:i+1] = ex + i += len(ex) + else: + # A macro with arguments + j = i + 1 + while j < len(tokens) and tokens[j].type in self.t_WS: + j += 1 + if j < len(tokens) and tokens[j].value == '(': + tokcount,args,positions = self.collect_args(tokens[j:]) + if not m.variadic and len(args) != len(m.arglist): + self.error(self.source,t.lineno,"Macro %s requires %d arguments" % (t.value,len(m.arglist))) + i = j + tokcount + elif m.variadic and len(args) < len(m.arglist)-1: + if len(m.arglist) > 2: + self.error(self.source,t.lineno,"Macro %s must have at least %d arguments" % (t.value, len(m.arglist)-1)) + else: + self.error(self.source,t.lineno,"Macro %s must have at least %d argument" % (t.value, len(m.arglist)-1)) + i = j + tokcount + else: + if m.variadic: + if len(args) == len(m.arglist)-1: + args.append([]) + else: + args[len(m.arglist)-1] = tokens[j+positions[len(m.arglist)-1]:j+tokcount-1] + del args[len(m.arglist):] + + # Get macro replacement text + rep = self.macro_expand_args(m,args) + rep = self.expand_macros(rep,expanded) + for r in rep: + r.lineno = t.lineno + tokens[i:j+tokcount] = rep + i += len(rep) + else: + # This is not a macro. It is just a word which + # equals to name of the macro. Hence, go to the + # next token. + i += 1 + + del expanded[t.value] + continue + elif t.value == '__LINE__': + t.type = self.t_INTEGER + t.value = self.t_INTEGER_TYPE(t.lineno) + + i += 1 + return tokens + + # ---------------------------------------------------------------------- + # evalexpr() + # + # Evaluate an expression token sequence for the purposes of evaluating + # integral expressions. + # ---------------------------------------------------------------------- + + def evalexpr(self,tokens): + # tokens = tokenize(line) + # Search for defined macros + i = 0 + while i < len(tokens): + if tokens[i].type == self.t_ID and tokens[i].value == 'defined': + j = i + 1 + needparen = False + result = "0L" + while j < len(tokens): + if tokens[j].type in self.t_WS: + j += 1 + continue + elif tokens[j].type == self.t_ID: + if tokens[j].value in self.macros: + result = "1L" + else: + result = "0L" + if not needparen: break + elif tokens[j].value == '(': + needparen = True + elif tokens[j].value == ')': + break + else: + self.error(self.source,tokens[i].lineno,"Malformed defined()") + j += 1 + tokens[i].type = self.t_INTEGER + tokens[i].value = self.t_INTEGER_TYPE(result) + del tokens[i+1:j+1] + i += 1 + tokens = self.expand_macros(tokens) + for i,t in enumerate(tokens): + if t.type == self.t_ID: + tokens[i] = copy.copy(t) + tokens[i].type = self.t_INTEGER + tokens[i].value = self.t_INTEGER_TYPE("0L") + elif t.type == self.t_INTEGER: + tokens[i] = copy.copy(t) + # Strip off any trailing suffixes + tokens[i].value = str(tokens[i].value) + while tokens[i].value[-1] not in "0123456789abcdefABCDEF": + tokens[i].value = tokens[i].value[:-1] + + expr = "".join([str(x.value) for x in tokens]) + expr = expr.replace("&&"," and ") + expr = expr.replace("||"," or ") + expr = expr.replace("!"," not ") + try: + result = eval(expr) + except Exception: + self.error(self.source,tokens[0].lineno,"Couldn't evaluate expression") + result = 0 + return result + + # ---------------------------------------------------------------------- + # parsegen() + # + # Parse an input string/ + # ---------------------------------------------------------------------- + def parsegen(self,input,source=None): + + # Replace trigraph sequences + t = trigraph(input) + lines = self.group_lines(t) + + if not source: + source = "" + + self.define("__FILE__ \"%s\"" % source) + + self.source = source + chunk = [] + enable = True + iftrigger = False + ifstack = [] + + for x in lines: + for i,tok in enumerate(x): + if tok.type not in self.t_WS: break + if tok.value == '#': + # Preprocessor directive + + # insert necessary whitespace instead of eaten tokens + for tok in x: + if tok.type in self.t_WS and '\n' in tok.value: + chunk.append(tok) + + dirtokens = self.tokenstrip(x[i+1:]) + if dirtokens: + name = dirtokens[0].value + args = self.tokenstrip(dirtokens[1:]) + else: + name = "" + args = [] + + if name == 'define': + if enable: + for tok in self.expand_macros(chunk): + yield tok + chunk = [] + self.define(args) + elif name == 'include': + if enable: + for tok in self.expand_macros(chunk): + yield tok + chunk = [] + oldfile = self.macros['__FILE__'] + for tok in self.include(args): + yield tok + self.macros['__FILE__'] = oldfile + self.source = source + elif name == 'undef': + if enable: + for tok in self.expand_macros(chunk): + yield tok + chunk = [] + self.undef(args) + elif name == 'ifdef': + ifstack.append((enable,iftrigger)) + if enable: + if not args[0].value in self.macros: + enable = False + iftrigger = False + else: + iftrigger = True + elif name == 'ifndef': + ifstack.append((enable,iftrigger)) + if enable: + if args[0].value in self.macros: + enable = False + iftrigger = False + else: + iftrigger = True + elif name == 'if': + ifstack.append((enable,iftrigger)) + if enable: + result = self.evalexpr(args) + if not result: + enable = False + iftrigger = False + else: + iftrigger = True + elif name == 'elif': + if ifstack: + if ifstack[-1][0]: # We only pay attention if outer "if" allows this + if enable: # If already true, we flip enable False + enable = False + elif not iftrigger: # If False, but not triggered yet, we'll check expression + result = self.evalexpr(args) + if result: + enable = True + iftrigger = True + else: + self.error(self.source,dirtokens[0].lineno,"Misplaced #elif") + + elif name == 'else': + if ifstack: + if ifstack[-1][0]: + if enable: + enable = False + elif not iftrigger: + enable = True + iftrigger = True + else: + self.error(self.source,dirtokens[0].lineno,"Misplaced #else") + + elif name == 'endif': + if ifstack: + enable,iftrigger = ifstack.pop() + else: + self.error(self.source,dirtokens[0].lineno,"Misplaced #endif") + else: + # Unknown preprocessor directive + pass + + else: + # Normal text + if enable: + chunk.extend(x) + + for tok in self.expand_macros(chunk): + yield tok + chunk = [] + + # ---------------------------------------------------------------------- + # include() + # + # Implementation of file-inclusion + # ---------------------------------------------------------------------- + + def include(self,tokens): + # Try to extract the filename and then process an include file + if not tokens: + return + if tokens: + if tokens[0].value != '<' and tokens[0].type != self.t_STRING: + tokens = self.expand_macros(tokens) + + if tokens[0].value == '<': + # Include <...> + i = 1 + while i < len(tokens): + if tokens[i].value == '>': + break + i += 1 + else: + print("Malformed #include <...>") + return + filename = "".join([x.value for x in tokens[1:i]]) + path = self.path + [""] + self.temp_path + elif tokens[0].type == self.t_STRING: + filename = tokens[0].value[1:-1] + path = self.temp_path + [""] + self.path + else: + print("Malformed #include statement") + return + for p in path: + iname = os.path.join(p,filename) + try: + data = open(iname,"r").read() + dname = os.path.dirname(iname) + if dname: + self.temp_path.insert(0,dname) + for tok in self.parsegen(data,filename): + yield tok + if dname: + del self.temp_path[0] + break + except IOError: + pass + else: + print("Couldn't find '%s'" % filename) + + # ---------------------------------------------------------------------- + # define() + # + # Define a new macro + # ---------------------------------------------------------------------- + + def define(self,tokens): + if isinstance(tokens,STRING_TYPES): + tokens = self.tokenize(tokens) + + linetok = tokens + try: + name = linetok[0] + if len(linetok) > 1: + mtype = linetok[1] + else: + mtype = None + if not mtype: + m = Macro(name.value,[]) + self.macros[name.value] = m + elif mtype.type in self.t_WS: + # A normal macro + m = Macro(name.value,self.tokenstrip(linetok[2:])) + self.macros[name.value] = m + elif mtype.value == '(': + # A macro with arguments + tokcount, args, positions = self.collect_args(linetok[1:]) + variadic = False + for a in args: + if variadic: + print("No more arguments may follow a variadic argument") + break + astr = "".join([str(_i.value) for _i in a]) + if astr == "...": + variadic = True + a[0].type = self.t_ID + a[0].value = '__VA_ARGS__' + variadic = True + del a[1:] + continue + elif astr[-3:] == "..." and a[0].type == self.t_ID: + variadic = True + del a[1:] + # If, for some reason, "." is part of the identifier, strip off the name for the purposes + # of macro expansion + if a[0].value[-3:] == '...': + a[0].value = a[0].value[:-3] + continue + if len(a) > 1 or a[0].type != self.t_ID: + print("Invalid macro argument") + break + else: + mvalue = self.tokenstrip(linetok[1+tokcount:]) + i = 0 + while i < len(mvalue): + if i+1 < len(mvalue): + if mvalue[i].type in self.t_WS and mvalue[i+1].value == '##': + del mvalue[i] + continue + elif mvalue[i].value == '##' and mvalue[i+1].type in self.t_WS: + del mvalue[i+1] + i += 1 + m = Macro(name.value,mvalue,[x[0].value for x in args],variadic) + self.macro_prescan(m) + self.macros[name.value] = m + else: + print("Bad macro definition") + except LookupError: + print("Bad macro definition") + + # ---------------------------------------------------------------------- + # undef() + # + # Undefine a macro + # ---------------------------------------------------------------------- + + def undef(self,tokens): + id = tokens[0].value + try: + del self.macros[id] + except LookupError: + pass + + # ---------------------------------------------------------------------- + # parse() + # + # Parse input text. + # ---------------------------------------------------------------------- + def parse(self,input,source=None,ignore={}): + self.ignore = ignore + self.parser = self.parsegen(input,source) + + # ---------------------------------------------------------------------- + # token() + # + # Method to return individual tokens + # ---------------------------------------------------------------------- + def token(self): + try: + while True: + tok = next(self.parser) + if tok.type not in self.ignore: return tok + except StopIteration: + self.parser = None + return None + +if __name__ == '__main__': + import ply.lex as lex + lexer = lex.lex() + + # Run a preprocessor + import sys + f = open(sys.argv[1]) + input = f.read() + + p = Preprocessor(lexer) + p.parse(input,sys.argv[1]) + while True: + tok = p.token() + if not tok: break + print(p.source, tok) diff --git a/astropy/extern/ply/ctokens.py b/astropy/extern/ply/ctokens.py new file mode 100644 index 000000000000..b265e59ff835 --- /dev/null +++ b/astropy/extern/ply/ctokens.py @@ -0,0 +1,127 @@ +# ---------------------------------------------------------------------- +# ctokens.py +# +# Token specifications for symbols in ANSI C and C++. This file is +# meant to be used as a library in other tokenizers. +# ---------------------------------------------------------------------- + +# Reserved words + +tokens = [ + # Literals (identifier, integer constant, float constant, string constant, char const) + 'ID', 'TYPEID', 'INTEGER', 'FLOAT', 'STRING', 'CHARACTER', + + # Operators (+,-,*,/,%,|,&,~,^,<<,>>, ||, &&, !, <, <=, >, >=, ==, !=) + 'PLUS', 'MINUS', 'TIMES', 'DIVIDE', 'MODULO', + 'OR', 'AND', 'NOT', 'XOR', 'LSHIFT', 'RSHIFT', + 'LOR', 'LAND', 'LNOT', + 'LT', 'LE', 'GT', 'GE', 'EQ', 'NE', + + # Assignment (=, *=, /=, %=, +=, -=, <<=, >>=, &=, ^=, |=) + 'EQUALS', 'TIMESEQUAL', 'DIVEQUAL', 'MODEQUAL', 'PLUSEQUAL', 'MINUSEQUAL', + 'LSHIFTEQUAL','RSHIFTEQUAL', 'ANDEQUAL', 'XOREQUAL', 'OREQUAL', + + # Increment/decrement (++,--) + 'INCREMENT', 'DECREMENT', + + # Structure dereference (->) + 'ARROW', + + # Ternary operator (?) + 'TERNARY', + + # Delimeters ( ) [ ] { } , . ; : + 'LPAREN', 'RPAREN', + 'LBRACKET', 'RBRACKET', + 'LBRACE', 'RBRACE', + 'COMMA', 'PERIOD', 'SEMI', 'COLON', + + # Ellipsis (...) + 'ELLIPSIS', +] + +# Operators +t_PLUS = r'\+' +t_MINUS = r'-' +t_TIMES = r'\*' +t_DIVIDE = r'/' +t_MODULO = r'%' +t_OR = r'\|' +t_AND = r'&' +t_NOT = r'~' +t_XOR = r'\^' +t_LSHIFT = r'<<' +t_RSHIFT = r'>>' +t_LOR = r'\|\|' +t_LAND = r'&&' +t_LNOT = r'!' +t_LT = r'<' +t_GT = r'>' +t_LE = r'<=' +t_GE = r'>=' +t_EQ = r'==' +t_NE = r'!=' + +# Assignment operators + +t_EQUALS = r'=' +t_TIMESEQUAL = r'\*=' +t_DIVEQUAL = r'/=' +t_MODEQUAL = r'%=' +t_PLUSEQUAL = r'\+=' +t_MINUSEQUAL = r'-=' +t_LSHIFTEQUAL = r'<<=' +t_RSHIFTEQUAL = r'>>=' +t_ANDEQUAL = r'&=' +t_OREQUAL = r'\|=' +t_XOREQUAL = r'\^=' + +# Increment/decrement +t_INCREMENT = r'\+\+' +t_DECREMENT = r'--' + +# -> +t_ARROW = r'->' + +# ? +t_TERNARY = r'\?' + +# Delimeters +t_LPAREN = r'\(' +t_RPAREN = r'\)' +t_LBRACKET = r'\[' +t_RBRACKET = r'\]' +t_LBRACE = r'\{' +t_RBRACE = r'\}' +t_COMMA = r',' +t_PERIOD = r'\.' +t_SEMI = r';' +t_COLON = r':' +t_ELLIPSIS = r'\.\.\.' + +# Identifiers +t_ID = r'[A-Za-z_][A-Za-z0-9_]*' + +# Integer literal +t_INTEGER = r'\d+([uU]|[lL]|[uU][lL]|[lL][uU])?' + +# Floating literal +t_FLOAT = r'((\d+)(\.\d+)(e(\+|-)?(\d+))? | (\d+)e(\+|-)?(\d+))([lL]|[fF])?' + +# String literal +t_STRING = r'\"([^\\\n]|(\\.))*?\"' + +# Character constant 'c' or L'c' +t_CHARACTER = r'(L)?\'([^\\\n]|(\\.))*?\'' + +# Comment (C-Style) +def t_COMMENT(t): + r'/\*(.|\n)*?\*/' + t.lexer.lineno += t.value.count('\n') + return t + +# Comment (C++-Style) +def t_CPPCOMMENT(t): + r'//.*\n' + t.lexer.lineno += 1 + return t diff --git a/astropy/extern/ply/lex.py b/astropy/extern/ply/lex.py new file mode 100644 index 000000000000..f95bcdbf1bb5 --- /dev/null +++ b/astropy/extern/ply/lex.py @@ -0,0 +1,1098 @@ +# ----------------------------------------------------------------------------- +# ply: lex.py +# +# Copyright (C) 2001-2018 +# David M. Beazley (Dabeaz LLC) +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of the David Beazley or Dabeaz LLC may be used to +# endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# ----------------------------------------------------------------------------- + +__version__ = '3.11' +__tabversion__ = '3.10' + +import re +import sys +import types +import copy +import os +import inspect + +# This tuple contains known string types +try: + # Python 2.6 + StringTypes = (types.StringType, types.UnicodeType) +except AttributeError: + # Python 3.0 + StringTypes = (str, bytes) + +# This regular expression is used to match valid token names +_is_identifier = re.compile(r'^[a-zA-Z0-9_]+$') + +# Exception thrown when invalid token encountered and no default error +# handler is defined. +class LexError(Exception): + def __init__(self, message, s): + self.args = (message,) + self.text = s + + +# Token class. This class is used to represent the tokens produced. +class LexToken(object): + def __str__(self): + return 'LexToken(%s,%r,%d,%d)' % (self.type, self.value, self.lineno, self.lexpos) + + def __repr__(self): + return str(self) + + +# This object is a stand-in for a logging object created by the +# logging module. + +class PlyLogger(object): + def __init__(self, f): + self.f = f + + def critical(self, msg, *args, **kwargs): + self.f.write((msg % args) + '\n') + + def warning(self, msg, *args, **kwargs): + self.f.write('WARNING: ' + (msg % args) + '\n') + + def error(self, msg, *args, **kwargs): + self.f.write('ERROR: ' + (msg % args) + '\n') + + info = critical + debug = critical + + +# Null logger is used when no output is generated. Does nothing. +class NullLogger(object): + def __getattribute__(self, name): + return self + + def __call__(self, *args, **kwargs): + return self + + +# ----------------------------------------------------------------------------- +# === Lexing Engine === +# +# The following Lexer class implements the lexer runtime. There are only +# a few public methods and attributes: +# +# input() - Store a new string in the lexer +# token() - Get the next token +# clone() - Clone the lexer +# +# lineno - Current line number +# lexpos - Current position in the input string +# ----------------------------------------------------------------------------- + +class Lexer: + def __init__(self): + self.lexre = None # Master regular expression. This is a list of + # tuples (re, findex) where re is a compiled + # regular expression and findex is a list + # mapping regex group numbers to rules + self.lexretext = None # Current regular expression strings + self.lexstatere = {} # Dictionary mapping lexer states to master regexs + self.lexstateretext = {} # Dictionary mapping lexer states to regex strings + self.lexstaterenames = {} # Dictionary mapping lexer states to symbol names + self.lexstate = 'INITIAL' # Current lexer state + self.lexstatestack = [] # Stack of lexer states + self.lexstateinfo = None # State information + self.lexstateignore = {} # Dictionary of ignored characters for each state + self.lexstateerrorf = {} # Dictionary of error functions for each state + self.lexstateeoff = {} # Dictionary of eof functions for each state + self.lexreflags = 0 # Optional re compile flags + self.lexdata = None # Actual input data (as a string) + self.lexpos = 0 # Current position in input text + self.lexlen = 0 # Length of the input text + self.lexerrorf = None # Error rule (if any) + self.lexeoff = None # EOF rule (if any) + self.lextokens = None # List of valid tokens + self.lexignore = '' # Ignored characters + self.lexliterals = '' # Literal characters that can be passed through + self.lexmodule = None # Module + self.lineno = 1 # Current line number + self.lexoptimize = False # Optimized mode + + def clone(self, object=None): + c = copy.copy(self) + + # If the object parameter has been supplied, it means we are attaching the + # lexer to a new object. In this case, we have to rebind all methods in + # the lexstatere and lexstateerrorf tables. + + if object: + newtab = {} + for key, ritem in self.lexstatere.items(): + newre = [] + for cre, findex in ritem: + newfindex = [] + for f in findex: + if not f or not f[0]: + newfindex.append(f) + continue + newfindex.append((getattr(object, f[0].__name__), f[1])) + newre.append((cre, newfindex)) + newtab[key] = newre + c.lexstatere = newtab + c.lexstateerrorf = {} + for key, ef in self.lexstateerrorf.items(): + c.lexstateerrorf[key] = getattr(object, ef.__name__) + c.lexmodule = object + return c + + # ------------------------------------------------------------ + # writetab() - Write lexer information to a table file + # ------------------------------------------------------------ + def writetab(self, lextab, outputdir=''): + if isinstance(lextab, types.ModuleType): + raise IOError("Won't overwrite existing lextab module") + basetabmodule = lextab.split('.')[-1] + filename = os.path.join(outputdir, basetabmodule) + '.py' + with open(filename, 'w') as tf: + tf.write('# %s.py. This file automatically created by PLY (version %s). Don\'t edit!\n' % (basetabmodule, __version__)) + tf.write('_tabversion = %s\n' % repr(__tabversion__)) + tf.write('_lextokens = set(%s)\n' % repr(tuple(sorted(self.lextokens)))) + tf.write('_lexreflags = %s\n' % repr(int(self.lexreflags))) + tf.write('_lexliterals = %s\n' % repr(self.lexliterals)) + tf.write('_lexstateinfo = %s\n' % repr(self.lexstateinfo)) + + # Rewrite the lexstatere table, replacing function objects with function names + tabre = {} + for statename, lre in self.lexstatere.items(): + titem = [] + for (pat, func), retext, renames in zip(lre, self.lexstateretext[statename], self.lexstaterenames[statename]): + titem.append((retext, _funcs_to_names(func, renames))) + tabre[statename] = titem + + tf.write('_lexstatere = %s\n' % repr(tabre)) + tf.write('_lexstateignore = %s\n' % repr(self.lexstateignore)) + + taberr = {} + for statename, ef in self.lexstateerrorf.items(): + taberr[statename] = ef.__name__ if ef else None + tf.write('_lexstateerrorf = %s\n' % repr(taberr)) + + tabeof = {} + for statename, ef in self.lexstateeoff.items(): + tabeof[statename] = ef.__name__ if ef else None + tf.write('_lexstateeoff = %s\n' % repr(tabeof)) + + # ------------------------------------------------------------ + # readtab() - Read lexer information from a tab file + # ------------------------------------------------------------ + def readtab(self, tabfile, fdict): + if isinstance(tabfile, types.ModuleType): + lextab = tabfile + else: + exec('import %s' % tabfile) + lextab = sys.modules[tabfile] + + if getattr(lextab, '_tabversion', '0.0') != __tabversion__: + raise ImportError('Inconsistent PLY version') + + self.lextokens = lextab._lextokens + self.lexreflags = lextab._lexreflags + self.lexliterals = lextab._lexliterals + self.lextokens_all = self.lextokens | set(self.lexliterals) + self.lexstateinfo = lextab._lexstateinfo + self.lexstateignore = lextab._lexstateignore + self.lexstatere = {} + self.lexstateretext = {} + for statename, lre in lextab._lexstatere.items(): + titem = [] + txtitem = [] + for pat, func_name in lre: + titem.append((re.compile(pat, lextab._lexreflags), _names_to_funcs(func_name, fdict))) + + self.lexstatere[statename] = titem + self.lexstateretext[statename] = txtitem + + self.lexstateerrorf = {} + for statename, ef in lextab._lexstateerrorf.items(): + self.lexstateerrorf[statename] = fdict[ef] + + self.lexstateeoff = {} + for statename, ef in lextab._lexstateeoff.items(): + self.lexstateeoff[statename] = fdict[ef] + + self.begin('INITIAL') + + # ------------------------------------------------------------ + # input() - Push a new string into the lexer + # ------------------------------------------------------------ + def input(self, s): + # Pull off the first character to see if s looks like a string + c = s[:1] + if not isinstance(c, StringTypes): + raise ValueError('Expected a string') + self.lexdata = s + self.lexpos = 0 + self.lexlen = len(s) + + # ------------------------------------------------------------ + # begin() - Changes the lexing state + # ------------------------------------------------------------ + def begin(self, state): + if state not in self.lexstatere: + raise ValueError('Undefined state') + self.lexre = self.lexstatere[state] + self.lexretext = self.lexstateretext[state] + self.lexignore = self.lexstateignore.get(state, '') + self.lexerrorf = self.lexstateerrorf.get(state, None) + self.lexeoff = self.lexstateeoff.get(state, None) + self.lexstate = state + + # ------------------------------------------------------------ + # push_state() - Changes the lexing state and saves old on stack + # ------------------------------------------------------------ + def push_state(self, state): + self.lexstatestack.append(self.lexstate) + self.begin(state) + + # ------------------------------------------------------------ + # pop_state() - Restores the previous state + # ------------------------------------------------------------ + def pop_state(self): + self.begin(self.lexstatestack.pop()) + + # ------------------------------------------------------------ + # current_state() - Returns the current lexing state + # ------------------------------------------------------------ + def current_state(self): + return self.lexstate + + # ------------------------------------------------------------ + # skip() - Skip ahead n characters + # ------------------------------------------------------------ + def skip(self, n): + self.lexpos += n + + # ------------------------------------------------------------ + # opttoken() - Return the next token from the Lexer + # + # Note: This function has been carefully implemented to be as fast + # as possible. Don't make changes unless you really know what + # you are doing + # ------------------------------------------------------------ + def token(self): + # Make local copies of frequently referenced attributes + lexpos = self.lexpos + lexlen = self.lexlen + lexignore = self.lexignore + lexdata = self.lexdata + + while lexpos < lexlen: + # This code provides some short-circuit code for whitespace, tabs, and other ignored characters + if lexdata[lexpos] in lexignore: + lexpos += 1 + continue + + # Look for a regular expression match + for lexre, lexindexfunc in self.lexre: + m = lexre.match(lexdata, lexpos) + if not m: + continue + + # Create a token for return + tok = LexToken() + tok.value = m.group() + tok.lineno = self.lineno + tok.lexpos = lexpos + + i = m.lastindex + func, tok.type = lexindexfunc[i] + + if not func: + # If no token type was set, it's an ignored token + if tok.type: + self.lexpos = m.end() + return tok + else: + lexpos = m.end() + break + + lexpos = m.end() + + # If token is processed by a function, call it + + tok.lexer = self # Set additional attributes useful in token rules + self.lexmatch = m + self.lexpos = lexpos + + newtok = func(tok) + + # Every function must return a token, if nothing, we just move to next token + if not newtok: + lexpos = self.lexpos # This is here in case user has updated lexpos. + lexignore = self.lexignore # This is here in case there was a state change + break + + # Verify type of the token. If not in the token map, raise an error + if not self.lexoptimize: + if newtok.type not in self.lextokens_all: + raise LexError("%s:%d: Rule '%s' returned an unknown token type '%s'" % ( + func.__code__.co_filename, func.__code__.co_firstlineno, + func.__name__, newtok.type), lexdata[lexpos:]) + + return newtok + else: + # No match, see if in literals + if lexdata[lexpos] in self.lexliterals: + tok = LexToken() + tok.value = lexdata[lexpos] + tok.lineno = self.lineno + tok.type = tok.value + tok.lexpos = lexpos + self.lexpos = lexpos + 1 + return tok + + # No match. Call t_error() if defined. + if self.lexerrorf: + tok = LexToken() + tok.value = self.lexdata[lexpos:] + tok.lineno = self.lineno + tok.type = 'error' + tok.lexer = self + tok.lexpos = lexpos + self.lexpos = lexpos + newtok = self.lexerrorf(tok) + if lexpos == self.lexpos: + # Error method didn't change text position at all. This is an error. + raise LexError("Scanning error. Illegal character '%s'" % (lexdata[lexpos]), lexdata[lexpos:]) + lexpos = self.lexpos + if not newtok: + continue + return newtok + + self.lexpos = lexpos + raise LexError("Illegal character '%s' at index %d" % (lexdata[lexpos], lexpos), lexdata[lexpos:]) + + if self.lexeoff: + tok = LexToken() + tok.type = 'eof' + tok.value = '' + tok.lineno = self.lineno + tok.lexpos = lexpos + tok.lexer = self + self.lexpos = lexpos + newtok = self.lexeoff(tok) + return newtok + + self.lexpos = lexpos + 1 + if self.lexdata is None: + raise RuntimeError('No input string given with input()') + return None + + # Iterator interface + def __iter__(self): + return self + + def next(self): + t = self.token() + if t is None: + raise StopIteration + return t + + __next__ = next + +# ----------------------------------------------------------------------------- +# ==== Lex Builder === +# +# The functions and classes below are used to collect lexing information +# and build a Lexer object from it. +# ----------------------------------------------------------------------------- + +# ----------------------------------------------------------------------------- +# _get_regex(func) +# +# Returns the regular expression assigned to a function either as a doc string +# or as a .regex attribute attached by the @TOKEN decorator. +# ----------------------------------------------------------------------------- +def _get_regex(func): + return getattr(func, 'regex', func.__doc__) + +# ----------------------------------------------------------------------------- +# get_caller_module_dict() +# +# This function returns a dictionary containing all of the symbols defined within +# a caller further down the call stack. This is used to get the environment +# associated with the yacc() call if none was provided. +# ----------------------------------------------------------------------------- +def get_caller_module_dict(levels): + f = sys._getframe(levels) + ldict = f.f_globals.copy() + if f.f_globals != f.f_locals: + ldict.update(f.f_locals) + return ldict + +# ----------------------------------------------------------------------------- +# _funcs_to_names() +# +# Given a list of regular expression functions, this converts it to a list +# suitable for output to a table file +# ----------------------------------------------------------------------------- +def _funcs_to_names(funclist, namelist): + result = [] + for f, name in zip(funclist, namelist): + if f and f[0]: + result.append((name, f[1])) + else: + result.append(f) + return result + +# ----------------------------------------------------------------------------- +# _names_to_funcs() +# +# Given a list of regular expression function names, this converts it back to +# functions. +# ----------------------------------------------------------------------------- +def _names_to_funcs(namelist, fdict): + result = [] + for n in namelist: + if n and n[0]: + result.append((fdict[n[0]], n[1])) + else: + result.append(n) + return result + +# ----------------------------------------------------------------------------- +# _form_master_re() +# +# This function takes a list of all of the regex components and attempts to +# form the master regular expression. Given limitations in the Python re +# module, it may be necessary to break the master regex into separate expressions. +# ----------------------------------------------------------------------------- +def _form_master_re(relist, reflags, ldict, toknames): + if not relist: + return [] + regex = '|'.join(relist) + try: + lexre = re.compile(regex, reflags) + + # Build the index to function map for the matching engine + lexindexfunc = [None] * (max(lexre.groupindex.values()) + 1) + lexindexnames = lexindexfunc[:] + + for f, i in lexre.groupindex.items(): + handle = ldict.get(f, None) + if type(handle) in (types.FunctionType, types.MethodType): + lexindexfunc[i] = (handle, toknames[f]) + lexindexnames[i] = f + elif handle is not None: + lexindexnames[i] = f + if f.find('ignore_') > 0: + lexindexfunc[i] = (None, None) + else: + lexindexfunc[i] = (None, toknames[f]) + + return [(lexre, lexindexfunc)], [regex], [lexindexnames] + except Exception: + m = int(len(relist)/2) + if m == 0: + m = 1 + llist, lre, lnames = _form_master_re(relist[:m], reflags, ldict, toknames) + rlist, rre, rnames = _form_master_re(relist[m:], reflags, ldict, toknames) + return (llist+rlist), (lre+rre), (lnames+rnames) + +# ----------------------------------------------------------------------------- +# def _statetoken(s,names) +# +# Given a declaration name s of the form "t_" and a dictionary whose keys are +# state names, this function returns a tuple (states,tokenname) where states +# is a tuple of state names and tokenname is the name of the token. For example, +# calling this with s = "t_foo_bar_SPAM" might return (('foo','bar'),'SPAM') +# ----------------------------------------------------------------------------- +def _statetoken(s, names): + parts = s.split('_') + for i, part in enumerate(parts[1:], 1): + if part not in names and part != 'ANY': + break + + if i > 1: + states = tuple(parts[1:i]) + else: + states = ('INITIAL',) + + if 'ANY' in states: + states = tuple(names) + + tokenname = '_'.join(parts[i:]) + return (states, tokenname) + + +# ----------------------------------------------------------------------------- +# LexerReflect() +# +# This class represents information needed to build a lexer as extracted from a +# user's input file. +# ----------------------------------------------------------------------------- +class LexerReflect(object): + def __init__(self, ldict, log=None, reflags=0): + self.ldict = ldict + self.error_func = None + self.tokens = [] + self.reflags = reflags + self.stateinfo = {'INITIAL': 'inclusive'} + self.modules = set() + self.error = False + self.log = PlyLogger(sys.stderr) if log is None else log + + # Get all of the basic information + def get_all(self): + self.get_tokens() + self.get_literals() + self.get_states() + self.get_rules() + + # Validate all of the information + def validate_all(self): + self.validate_tokens() + self.validate_literals() + self.validate_rules() + return self.error + + # Get the tokens map + def get_tokens(self): + tokens = self.ldict.get('tokens', None) + if not tokens: + self.log.error('No token list is defined') + self.error = True + return + + if not isinstance(tokens, (list, tuple)): + self.log.error('tokens must be a list or tuple') + self.error = True + return + + if not tokens: + self.log.error('tokens is empty') + self.error = True + return + + self.tokens = tokens + + # Validate the tokens + def validate_tokens(self): + terminals = {} + for n in self.tokens: + if not _is_identifier.match(n): + self.log.error("Bad token name '%s'", n) + self.error = True + if n in terminals: + self.log.warning("Token '%s' multiply defined", n) + terminals[n] = 1 + + # Get the literals specifier + def get_literals(self): + self.literals = self.ldict.get('literals', '') + if not self.literals: + self.literals = '' + + # Validate literals + def validate_literals(self): + try: + for c in self.literals: + if not isinstance(c, StringTypes) or len(c) > 1: + self.log.error('Invalid literal %s. Must be a single character', repr(c)) + self.error = True + + except TypeError: + self.log.error('Invalid literals specification. literals must be a sequence of characters') + self.error = True + + def get_states(self): + self.states = self.ldict.get('states', None) + # Build statemap + if self.states: + if not isinstance(self.states, (tuple, list)): + self.log.error('states must be defined as a tuple or list') + self.error = True + else: + for s in self.states: + if not isinstance(s, tuple) or len(s) != 2: + self.log.error("Invalid state specifier %s. Must be a tuple (statename,'exclusive|inclusive')", repr(s)) + self.error = True + continue + name, statetype = s + if not isinstance(name, StringTypes): + self.log.error('State name %s must be a string', repr(name)) + self.error = True + continue + if not (statetype == 'inclusive' or statetype == 'exclusive'): + self.log.error("State type for state %s must be 'inclusive' or 'exclusive'", name) + self.error = True + continue + if name in self.stateinfo: + self.log.error("State '%s' already defined", name) + self.error = True + continue + self.stateinfo[name] = statetype + + # Get all of the symbols with a t_ prefix and sort them into various + # categories (functions, strings, error functions, and ignore characters) + + def get_rules(self): + tsymbols = [f for f in self.ldict if f[:2] == 't_'] + + # Now build up a list of functions and a list of strings + self.toknames = {} # Mapping of symbols to token names + self.funcsym = {} # Symbols defined as functions + self.strsym = {} # Symbols defined as strings + self.ignore = {} # Ignore strings by state + self.errorf = {} # Error functions by state + self.eoff = {} # EOF functions by state + + for s in self.stateinfo: + self.funcsym[s] = [] + self.strsym[s] = [] + + if len(tsymbols) == 0: + self.log.error('No rules of the form t_rulename are defined') + self.error = True + return + + for f in tsymbols: + t = self.ldict[f] + states, tokname = _statetoken(f, self.stateinfo) + self.toknames[f] = tokname + + if hasattr(t, '__call__'): + if tokname == 'error': + for s in states: + self.errorf[s] = t + elif tokname == 'eof': + for s in states: + self.eoff[s] = t + elif tokname == 'ignore': + line = t.__code__.co_firstlineno + file = t.__code__.co_filename + self.log.error("%s:%d: Rule '%s' must be defined as a string", file, line, t.__name__) + self.error = True + else: + for s in states: + self.funcsym[s].append((f, t)) + elif isinstance(t, StringTypes): + if tokname == 'ignore': + for s in states: + self.ignore[s] = t + if '\\' in t: + self.log.warning("%s contains a literal backslash '\\'", f) + + elif tokname == 'error': + self.log.error("Rule '%s' must be defined as a function", f) + self.error = True + else: + for s in states: + self.strsym[s].append((f, t)) + else: + self.log.error('%s not defined as a function or string', f) + self.error = True + + # Sort the functions by line number + for f in self.funcsym.values(): + f.sort(key=lambda x: x[1].__code__.co_firstlineno) + + # Sort the strings by regular expression length + for s in self.strsym.values(): + s.sort(key=lambda x: len(x[1]), reverse=True) + + # Validate all of the t_rules collected + def validate_rules(self): + for state in self.stateinfo: + # Validate all rules defined by functions + + for fname, f in self.funcsym[state]: + line = f.__code__.co_firstlineno + file = f.__code__.co_filename + module = inspect.getmodule(f) + self.modules.add(module) + + tokname = self.toknames[fname] + if isinstance(f, types.MethodType): + reqargs = 2 + else: + reqargs = 1 + nargs = f.__code__.co_argcount + if nargs > reqargs: + self.log.error("%s:%d: Rule '%s' has too many arguments", file, line, f.__name__) + self.error = True + continue + + if nargs < reqargs: + self.log.error("%s:%d: Rule '%s' requires an argument", file, line, f.__name__) + self.error = True + continue + + if not _get_regex(f): + self.log.error("%s:%d: No regular expression defined for rule '%s'", file, line, f.__name__) + self.error = True + continue + + try: + c = re.compile('(?P<%s>%s)' % (fname, _get_regex(f)), self.reflags) + if c.match(''): + self.log.error("%s:%d: Regular expression for rule '%s' matches empty string", file, line, f.__name__) + self.error = True + except re.error as e: + self.log.error("%s:%d: Invalid regular expression for rule '%s'. %s", file, line, f.__name__, e) + if '#' in _get_regex(f): + self.log.error("%s:%d. Make sure '#' in rule '%s' is escaped with '\\#'", file, line, f.__name__) + self.error = True + + # Validate all rules defined by strings + for name, r in self.strsym[state]: + tokname = self.toknames[name] + if tokname == 'error': + self.log.error("Rule '%s' must be defined as a function", name) + self.error = True + continue + + if tokname not in self.tokens and tokname.find('ignore_') < 0: + self.log.error("Rule '%s' defined for an unspecified token %s", name, tokname) + self.error = True + continue + + try: + c = re.compile('(?P<%s>%s)' % (name, r), self.reflags) + if (c.match('')): + self.log.error("Regular expression for rule '%s' matches empty string", name) + self.error = True + except re.error as e: + self.log.error("Invalid regular expression for rule '%s'. %s", name, e) + if '#' in r: + self.log.error("Make sure '#' in rule '%s' is escaped with '\\#'", name) + self.error = True + + if not self.funcsym[state] and not self.strsym[state]: + self.log.error("No rules defined for state '%s'", state) + self.error = True + + # Validate the error function + efunc = self.errorf.get(state, None) + if efunc: + f = efunc + line = f.__code__.co_firstlineno + file = f.__code__.co_filename + module = inspect.getmodule(f) + self.modules.add(module) + + if isinstance(f, types.MethodType): + reqargs = 2 + else: + reqargs = 1 + nargs = f.__code__.co_argcount + if nargs > reqargs: + self.log.error("%s:%d: Rule '%s' has too many arguments", file, line, f.__name__) + self.error = True + + if nargs < reqargs: + self.log.error("%s:%d: Rule '%s' requires an argument", file, line, f.__name__) + self.error = True + + for module in self.modules: + self.validate_module(module) + + # ----------------------------------------------------------------------------- + # validate_module() + # + # This checks to see if there are duplicated t_rulename() functions or strings + # in the parser input file. This is done using a simple regular expression + # match on each line in the source code of the given module. + # ----------------------------------------------------------------------------- + + def validate_module(self, module): + try: + lines, linen = inspect.getsourcelines(module) + except IOError: + return + + fre = re.compile(r'\s*def\s+(t_[a-zA-Z_0-9]*)\(') + sre = re.compile(r'\s*(t_[a-zA-Z_0-9]*)\s*=') + + counthash = {} + linen += 1 + for line in lines: + m = fre.match(line) + if not m: + m = sre.match(line) + if m: + name = m.group(1) + prev = counthash.get(name) + if not prev: + counthash[name] = linen + else: + filename = inspect.getsourcefile(module) + self.log.error('%s:%d: Rule %s redefined. Previously defined on line %d', filename, linen, name, prev) + self.error = True + linen += 1 + +# ----------------------------------------------------------------------------- +# lex(module) +# +# Build all of the regular expression rules from definitions in the supplied module +# ----------------------------------------------------------------------------- +def lex(module=None, object=None, debug=False, optimize=False, lextab='lextab', + reflags=int(re.VERBOSE), nowarn=False, outputdir=None, debuglog=None, errorlog=None): + + if lextab is None: + lextab = 'lextab' + + global lexer + + ldict = None + stateinfo = {'INITIAL': 'inclusive'} + lexobj = Lexer() + lexobj.lexoptimize = optimize + global token, input + + if errorlog is None: + errorlog = PlyLogger(sys.stderr) + + if debug: + if debuglog is None: + debuglog = PlyLogger(sys.stderr) + + # Get the module dictionary used for the lexer + if object: + module = object + + # Get the module dictionary used for the parser + if module: + _items = [(k, getattr(module, k)) for k in dir(module)] + ldict = dict(_items) + # If no __file__ attribute is available, try to obtain it from the __module__ instead + if '__file__' not in ldict: + ldict['__file__'] = sys.modules[ldict['__module__']].__file__ + else: + ldict = get_caller_module_dict(2) + + # Determine if the module is package of a package or not. + # If so, fix the tabmodule setting so that tables load correctly + pkg = ldict.get('__package__') + if pkg and isinstance(lextab, str): + if '.' not in lextab: + lextab = pkg + '.' + lextab + + # Collect parser information from the dictionary + linfo = LexerReflect(ldict, log=errorlog, reflags=reflags) + linfo.get_all() + if not optimize: + if linfo.validate_all(): + raise SyntaxError("Can't build lexer") + + if optimize and lextab: + try: + lexobj.readtab(lextab, ldict) + token = lexobj.token + input = lexobj.input + lexer = lexobj + return lexobj + + except ImportError: + pass + + # Dump some basic debugging information + if debug: + debuglog.info('lex: tokens = %r', linfo.tokens) + debuglog.info('lex: literals = %r', linfo.literals) + debuglog.info('lex: states = %r', linfo.stateinfo) + + # Build a dictionary of valid token names + lexobj.lextokens = set() + for n in linfo.tokens: + lexobj.lextokens.add(n) + + # Get literals specification + if isinstance(linfo.literals, (list, tuple)): + lexobj.lexliterals = type(linfo.literals[0])().join(linfo.literals) + else: + lexobj.lexliterals = linfo.literals + + lexobj.lextokens_all = lexobj.lextokens | set(lexobj.lexliterals) + + # Get the stateinfo dictionary + stateinfo = linfo.stateinfo + + regexs = {} + # Build the master regular expressions + for state in stateinfo: + regex_list = [] + + # Add rules defined by functions first + for fname, f in linfo.funcsym[state]: + regex_list.append('(?P<%s>%s)' % (fname, _get_regex(f))) + if debug: + debuglog.info("lex: Adding rule %s -> '%s' (state '%s')", fname, _get_regex(f), state) + + # Now add all of the simple rules + for name, r in linfo.strsym[state]: + regex_list.append('(?P<%s>%s)' % (name, r)) + if debug: + debuglog.info("lex: Adding rule %s -> '%s' (state '%s')", name, r, state) + + regexs[state] = regex_list + + # Build the master regular expressions + + if debug: + debuglog.info('lex: ==== MASTER REGEXS FOLLOW ====') + + for state in regexs: + lexre, re_text, re_names = _form_master_re(regexs[state], reflags, ldict, linfo.toknames) + lexobj.lexstatere[state] = lexre + lexobj.lexstateretext[state] = re_text + lexobj.lexstaterenames[state] = re_names + if debug: + for i, text in enumerate(re_text): + debuglog.info("lex: state '%s' : regex[%d] = '%s'", state, i, text) + + # For inclusive states, we need to add the regular expressions from the INITIAL state + for state, stype in stateinfo.items(): + if state != 'INITIAL' and stype == 'inclusive': + lexobj.lexstatere[state].extend(lexobj.lexstatere['INITIAL']) + lexobj.lexstateretext[state].extend(lexobj.lexstateretext['INITIAL']) + lexobj.lexstaterenames[state].extend(lexobj.lexstaterenames['INITIAL']) + + lexobj.lexstateinfo = stateinfo + lexobj.lexre = lexobj.lexstatere['INITIAL'] + lexobj.lexretext = lexobj.lexstateretext['INITIAL'] + lexobj.lexreflags = reflags + + # Set up ignore variables + lexobj.lexstateignore = linfo.ignore + lexobj.lexignore = lexobj.lexstateignore.get('INITIAL', '') + + # Set up error functions + lexobj.lexstateerrorf = linfo.errorf + lexobj.lexerrorf = linfo.errorf.get('INITIAL', None) + if not lexobj.lexerrorf: + errorlog.warning('No t_error rule is defined') + + # Set up eof functions + lexobj.lexstateeoff = linfo.eoff + lexobj.lexeoff = linfo.eoff.get('INITIAL', None) + + # Check state information for ignore and error rules + for s, stype in stateinfo.items(): + if stype == 'exclusive': + if s not in linfo.errorf: + errorlog.warning("No error rule is defined for exclusive state '%s'", s) + if s not in linfo.ignore and lexobj.lexignore: + errorlog.warning("No ignore rule is defined for exclusive state '%s'", s) + elif stype == 'inclusive': + if s not in linfo.errorf: + linfo.errorf[s] = linfo.errorf.get('INITIAL', None) + if s not in linfo.ignore: + linfo.ignore[s] = linfo.ignore.get('INITIAL', '') + + # Create global versions of the token() and input() functions + token = lexobj.token + input = lexobj.input + lexer = lexobj + + # If in optimize mode, we write the lextab + if lextab and optimize: + if outputdir is None: + # If no output directory is set, the location of the output files + # is determined according to the following rules: + # - If lextab specifies a package, files go into that package directory + # - Otherwise, files go in the same directory as the specifying module + if isinstance(lextab, types.ModuleType): + srcfile = lextab.__file__ + else: + if '.' not in lextab: + srcfile = ldict['__file__'] + else: + parts = lextab.split('.') + pkgname = '.'.join(parts[:-1]) + exec('import %s' % pkgname) + srcfile = getattr(sys.modules[pkgname], '__file__', '') + outputdir = os.path.dirname(srcfile) + try: + lexobj.writetab(lextab, outputdir) + if lextab in sys.modules: + del sys.modules[lextab] + except IOError as e: + errorlog.warning("Couldn't write lextab module %r. %s" % (lextab, e)) + + return lexobj + +# ----------------------------------------------------------------------------- +# runmain() +# +# This runs the lexer as a main program +# ----------------------------------------------------------------------------- + +def runmain(lexer=None, data=None): + if not data: + try: + filename = sys.argv[1] + f = open(filename) + data = f.read() + f.close() + except IndexError: + sys.stdout.write('Reading from standard input (type EOF to end):\n') + data = sys.stdin.read() + + if lexer: + _input = lexer.input + else: + _input = input + _input(data) + if lexer: + _token = lexer.token + else: + _token = token + + while True: + tok = _token() + if not tok: + break + sys.stdout.write('(%s,%r,%d,%d)\n' % (tok.type, tok.value, tok.lineno, tok.lexpos)) + +# ----------------------------------------------------------------------------- +# @TOKEN(regex) +# +# This decorator function can be used to set the regex expression on a function +# when its docstring might need to be set in an alternative way +# ----------------------------------------------------------------------------- + +def TOKEN(r): + def set_regex(f): + if hasattr(r, '__call__'): + f.regex = _get_regex(r) + else: + f.regex = r + return f + return set_regex + +# Alternative spelling of the TOKEN decorator +Token = TOKEN diff --git a/astropy/extern/ply/yacc.py b/astropy/extern/ply/yacc.py new file mode 100644 index 000000000000..d9079cb38806 --- /dev/null +++ b/astropy/extern/ply/yacc.py @@ -0,0 +1,3433 @@ +# ----------------------------------------------------------------------------- +# ply: yacc.py +# +# Copyright (C) 2001-2018 +# David M. Beazley (Dabeaz LLC) +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of the David Beazley or Dabeaz LLC may be used to +# endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# ----------------------------------------------------------------------------- +# +# This implements an LR parser that is constructed from grammar rules defined +# as Python functions. The grammar is specified by supplying the BNF inside +# Python documentation strings. The inspiration for this technique was borrowed +# from John Aycock's Spark parsing system. PLY might be viewed as cross between +# Spark and the GNU bison utility. +# +# The current implementation is only somewhat object-oriented. The +# LR parser itself is defined in terms of an object (which allows multiple +# parsers to co-exist). However, most of the variables used during table +# construction are defined in terms of global variables. Users shouldn't +# notice unless they are trying to define multiple parsers at the same +# time using threads (in which case they should have their head examined). +# +# This implementation supports both SLR and LALR(1) parsing. LALR(1) +# support was originally implemented by Elias Ioup (ezioup@alumni.uchicago.edu), +# using the algorithm found in Aho, Sethi, and Ullman "Compilers: Principles, +# Techniques, and Tools" (The Dragon Book). LALR(1) has since been replaced +# by the more efficient DeRemer and Pennello algorithm. +# +# :::::::: WARNING ::::::: +# +# Construction of LR parsing tables is fairly complicated and expensive. +# To make this module run fast, a *LOT* of work has been put into +# optimization---often at the expensive of readability and what might +# consider to be good Python "coding style." Modify the code at your +# own risk! +# ---------------------------------------------------------------------------- + +import re +import types +import sys +import os.path +import inspect +import warnings + +__version__ = '3.11' +__tabversion__ = '3.10' + +#----------------------------------------------------------------------------- +# === User configurable parameters === +# +# Change these to modify the default behavior of yacc (if you wish) +#----------------------------------------------------------------------------- + +yaccdebug = True # Debugging mode. If set, yacc generates a + # a 'parser.out' file in the current directory + +debug_file = 'parser.out' # Default name of the debugging file +tab_module = 'parsetab' # Default name of the table module +default_lr = 'LALR' # Default LR table generation method + +error_count = 3 # Number of symbols that must be shifted to leave recovery mode + +yaccdevel = False # Set to True if developing yacc. This turns off optimized + # implementations of certain functions. + +resultlimit = 40 # Size limit of results when running in debug mode. + +# String type-checking compatibility +if sys.version_info[0] < 3: + string_types = basestring +else: + string_types = str + +MAXINT = sys.maxsize + +# This object is a stand-in for a logging object created by the +# logging module. PLY will use this by default to create things +# such as the parser.out file. If a user wants more detailed +# information, they can create their own logging object and pass +# it into PLY. + +class PlyLogger(object): + def __init__(self, f): + self.f = f + + def debug(self, msg, *args, **kwargs): + self.f.write((msg % args) + '\n') + + info = debug + + def warning(self, msg, *args, **kwargs): + self.f.write('WARNING: ' + (msg % args) + '\n') + + def error(self, msg, *args, **kwargs): + self.f.write('ERROR: ' + (msg % args) + '\n') + + critical = debug + +# Null logger is used when no output is generated. Does nothing. +class NullLogger(object): + def __getattribute__(self, name): + return self + + def __call__(self, *args, **kwargs): + return self + +# Exception raised for yacc-related errors +class YaccError(Exception): + pass + +# Format the result message that the parser produces when running in debug mode. +def format_result(r): + repr_str = repr(r) + if '\n' in repr_str: + repr_str = repr(repr_str) + if len(repr_str) > resultlimit: + repr_str = repr_str[:resultlimit] + ' ...' + result = '<%s @ 0x%x> (%s)' % (type(r).__name__, id(r), repr_str) + return result + +# Format stack entries when the parser is running in debug mode +def format_stack_entry(r): + repr_str = repr(r) + if '\n' in repr_str: + repr_str = repr(repr_str) + if len(repr_str) < 16: + return repr_str + else: + return '<%s @ 0x%x>' % (type(r).__name__, id(r)) + +# Panic mode error recovery support. This feature is being reworked--much of the +# code here is to offer a deprecation/backwards compatible transition + +_errok = None +_token = None +_restart = None +_warnmsg = '''PLY: Don't use global functions errok(), token(), and restart() in p_error(). +Instead, invoke the methods on the associated parser instance: + + def p_error(p): + ... + # Use parser.errok(), parser.token(), parser.restart() + ... + + parser = yacc.yacc() +''' + +def errok(): + warnings.warn(_warnmsg) + return _errok() + +def restart(): + warnings.warn(_warnmsg) + return _restart() + +def token(): + warnings.warn(_warnmsg) + return _token() + +# Utility function to call the p_error() function with some deprecation hacks +def call_errorfunc(errorfunc, token, parser): + global _errok, _token, _restart + _errok = parser.errok + _token = parser.token + _restart = parser.restart + r = errorfunc(token) + try: + del _errok, _token, _restart + except NameError: + pass + return r + +#----------------------------------------------------------------------------- +# === LR Parsing Engine === +# +# The following classes are used for the LR parser itself. These are not +# used during table construction and are independent of the actual LR +# table generation algorithm +#----------------------------------------------------------------------------- + +# This class is used to hold non-terminal grammar symbols during parsing. +# It normally has the following attributes set: +# .type = Grammar symbol type +# .value = Symbol value +# .lineno = Starting line number +# .endlineno = Ending line number (optional, set automatically) +# .lexpos = Starting lex position +# .endlexpos = Ending lex position (optional, set automatically) + +class YaccSymbol: + def __str__(self): + return self.type + + def __repr__(self): + return str(self) + +# This class is a wrapper around the objects actually passed to each +# grammar rule. Index lookup and assignment actually assign the +# .value attribute of the underlying YaccSymbol object. +# The lineno() method returns the line number of a given +# item (or 0 if not defined). The linespan() method returns +# a tuple of (startline,endline) representing the range of lines +# for a symbol. The lexspan() method returns a tuple (lexpos,endlexpos) +# representing the range of positional information for a symbol. + +class YaccProduction: + def __init__(self, s, stack=None): + self.slice = s + self.stack = stack + self.lexer = None + self.parser = None + + def __getitem__(self, n): + if isinstance(n, slice): + return [s.value for s in self.slice[n]] + elif n >= 0: + return self.slice[n].value + else: + return self.stack[n].value + + def __setitem__(self, n, v): + self.slice[n].value = v + + def __getslice__(self, i, j): + return [s.value for s in self.slice[i:j]] + + def __len__(self): + return len(self.slice) + + def lineno(self, n): + return getattr(self.slice[n], 'lineno', 0) + + def set_lineno(self, n, lineno): + self.slice[n].lineno = lineno + + def linespan(self, n): + startline = getattr(self.slice[n], 'lineno', 0) + endline = getattr(self.slice[n], 'endlineno', startline) + return startline, endline + + def lexpos(self, n): + return getattr(self.slice[n], 'lexpos', 0) + + def set_lexpos(self, n, lexpos): + self.slice[n].lexpos = lexpos + + def lexspan(self, n): + startpos = getattr(self.slice[n], 'lexpos', 0) + endpos = getattr(self.slice[n], 'endlexpos', startpos) + return startpos, endpos + + def error(self): + raise SyntaxError + +# ----------------------------------------------------------------------------- +# == LRParser == +# +# The LR Parsing engine. +# ----------------------------------------------------------------------------- + +class LRParser: + def __init__(self, lrtab, errorf): + self.productions = lrtab.lr_productions + self.action = lrtab.lr_action + self.goto = lrtab.lr_goto + self.errorfunc = errorf + self.set_defaulted_states() + self.errorok = True + + def errok(self): + self.errorok = True + + def restart(self): + del self.statestack[:] + del self.symstack[:] + sym = YaccSymbol() + sym.type = '$end' + self.symstack.append(sym) + self.statestack.append(0) + + # Defaulted state support. + # This method identifies parser states where there is only one possible reduction action. + # For such states, the parser can make a choose to make a rule reduction without consuming + # the next look-ahead token. This delayed invocation of the tokenizer can be useful in + # certain kinds of advanced parsing situations where the lexer and parser interact with + # each other or change states (i.e., manipulation of scope, lexer states, etc.). + # + # See: http://www.gnu.org/software/bison/manual/html_node/Default-Reductions.html#Default-Reductions + def set_defaulted_states(self): + self.defaulted_states = {} + for state, actions in self.action.items(): + rules = list(actions.values()) + if len(rules) == 1 and rules[0] < 0: + self.defaulted_states[state] = rules[0] + + def disable_defaulted_states(self): + self.defaulted_states = {} + + def parse(self, input=None, lexer=None, debug=False, tracking=False, tokenfunc=None): + if debug or yaccdevel: + if isinstance(debug, int): + debug = PlyLogger(sys.stderr) + return self.parsedebug(input, lexer, debug, tracking, tokenfunc) + elif tracking: + return self.parseopt(input, lexer, debug, tracking, tokenfunc) + else: + return self.parseopt_notrack(input, lexer, debug, tracking, tokenfunc) + + + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + # parsedebug(). + # + # This is the debugging enabled version of parse(). All changes made to the + # parsing engine should be made here. Optimized versions of this function + # are automatically created by the ply/ygen.py script. This script cuts out + # sections enclosed in markers such as this: + # + # #--! DEBUG + # statements + # #--! DEBUG + # + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + def parsedebug(self, input=None, lexer=None, debug=False, tracking=False, tokenfunc=None): + #--! parsedebug-start + lookahead = None # Current lookahead symbol + lookaheadstack = [] # Stack of lookahead symbols + actions = self.action # Local reference to action table (to avoid lookup on self.) + goto = self.goto # Local reference to goto table (to avoid lookup on self.) + prod = self.productions # Local reference to production list (to avoid lookup on self.) + defaulted_states = self.defaulted_states # Local reference to defaulted states + pslice = YaccProduction(None) # Production object passed to grammar rules + errorcount = 0 # Used during error recovery + + #--! DEBUG + debug.info('PLY: PARSE DEBUG START') + #--! DEBUG + + # If no lexer was given, we will try to use the lex module + if not lexer: + from . import lex + lexer = lex.lexer + + # Set up the lexer and parser objects on pslice + pslice.lexer = lexer + pslice.parser = self + + # If input was supplied, pass to lexer + if input is not None: + lexer.input(input) + + if tokenfunc is None: + # Tokenize function + get_token = lexer.token + else: + get_token = tokenfunc + + # Set the parser() token method (sometimes used in error recovery) + self.token = get_token + + # Set up the state and symbol stacks + + statestack = [] # Stack of parsing states + self.statestack = statestack + symstack = [] # Stack of grammar symbols + self.symstack = symstack + + pslice.stack = symstack # Put in the production + errtoken = None # Err token + + # The start state is assumed to be (0,$end) + + statestack.append(0) + sym = YaccSymbol() + sym.type = '$end' + symstack.append(sym) + state = 0 + while True: + # Get the next symbol on the input. If a lookahead symbol + # is already set, we just use that. Otherwise, we'll pull + # the next token off of the lookaheadstack or from the lexer + + #--! DEBUG + debug.debug('') + debug.debug('State : %s', state) + #--! DEBUG + + if state not in defaulted_states: + if not lookahead: + if not lookaheadstack: + lookahead = get_token() # Get the next token + else: + lookahead = lookaheadstack.pop() + if not lookahead: + lookahead = YaccSymbol() + lookahead.type = '$end' + + # Check the action table + ltype = lookahead.type + t = actions[state].get(ltype) + else: + t = defaulted_states[state] + #--! DEBUG + debug.debug('Defaulted state %s: Reduce using %d', state, -t) + #--! DEBUG + + #--! DEBUG + debug.debug('Stack : %s', + ('%s . %s' % (' '.join([xx.type for xx in symstack][1:]), str(lookahead))).lstrip()) + #--! DEBUG + + if t is not None: + if t > 0: + # shift a symbol on the stack + statestack.append(t) + state = t + + #--! DEBUG + debug.debug('Action : Shift and goto state %s', t) + #--! DEBUG + + symstack.append(lookahead) + lookahead = None + + # Decrease error count on successful shift + if errorcount: + errorcount -= 1 + continue + + if t < 0: + # reduce a symbol on the stack, emit a production + p = prod[-t] + pname = p.name + plen = p.len + + # Get production function + sym = YaccSymbol() + sym.type = pname # Production name + sym.value = None + + #--! DEBUG + if plen: + debug.info('Action : Reduce rule [%s] with %s and goto state %d', p.str, + '['+','.join([format_stack_entry(_v.value) for _v in symstack[-plen:]])+']', + goto[statestack[-1-plen]][pname]) + else: + debug.info('Action : Reduce rule [%s] with %s and goto state %d', p.str, [], + goto[statestack[-1]][pname]) + + #--! DEBUG + + if plen: + targ = symstack[-plen-1:] + targ[0] = sym + + #--! TRACKING + if tracking: + t1 = targ[1] + sym.lineno = t1.lineno + sym.lexpos = t1.lexpos + t1 = targ[-1] + sym.endlineno = getattr(t1, 'endlineno', t1.lineno) + sym.endlexpos = getattr(t1, 'endlexpos', t1.lexpos) + #--! TRACKING + + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + # The code enclosed in this section is duplicated + # below as a performance optimization. Make sure + # changes get made in both locations. + + pslice.slice = targ + + try: + # Call the grammar rule with our special slice object + del symstack[-plen:] + self.state = state + p.callable(pslice) + del statestack[-plen:] + #--! DEBUG + debug.info('Result : %s', format_result(pslice[0])) + #--! DEBUG + symstack.append(sym) + state = goto[statestack[-1]][pname] + statestack.append(state) + except SyntaxError: + # If an error was set. Enter error recovery state + lookaheadstack.append(lookahead) # Save the current lookahead token + symstack.extend(targ[1:-1]) # Put the production slice back on the stack + statestack.pop() # Pop back one state (before the reduce) + state = statestack[-1] + sym.type = 'error' + sym.value = 'error' + lookahead = sym + errorcount = error_count + self.errorok = False + + continue + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + else: + + #--! TRACKING + if tracking: + sym.lineno = lexer.lineno + sym.lexpos = lexer.lexpos + #--! TRACKING + + targ = [sym] + + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + # The code enclosed in this section is duplicated + # above as a performance optimization. Make sure + # changes get made in both locations. + + pslice.slice = targ + + try: + # Call the grammar rule with our special slice object + self.state = state + p.callable(pslice) + #--! DEBUG + debug.info('Result : %s', format_result(pslice[0])) + #--! DEBUG + symstack.append(sym) + state = goto[statestack[-1]][pname] + statestack.append(state) + except SyntaxError: + # If an error was set. Enter error recovery state + lookaheadstack.append(lookahead) # Save the current lookahead token + statestack.pop() # Pop back one state (before the reduce) + state = statestack[-1] + sym.type = 'error' + sym.value = 'error' + lookahead = sym + errorcount = error_count + self.errorok = False + + continue + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + if t == 0: + n = symstack[-1] + result = getattr(n, 'value', None) + #--! DEBUG + debug.info('Done : Returning %s', format_result(result)) + debug.info('PLY: PARSE DEBUG END') + #--! DEBUG + return result + + if t is None: + + #--! DEBUG + debug.error('Error : %s', + ('%s . %s' % (' '.join([xx.type for xx in symstack][1:]), str(lookahead))).lstrip()) + #--! DEBUG + + # We have some kind of parsing error here. To handle + # this, we are going to push the current token onto + # the tokenstack and replace it with an 'error' token. + # If there are any synchronization rules, they may + # catch it. + # + # In addition to pushing the error token, we call call + # the user defined p_error() function if this is the + # first syntax error. This function is only called if + # errorcount == 0. + if errorcount == 0 or self.errorok: + errorcount = error_count + self.errorok = False + errtoken = lookahead + if errtoken.type == '$end': + errtoken = None # End of file! + if self.errorfunc: + if errtoken and not hasattr(errtoken, 'lexer'): + errtoken.lexer = lexer + self.state = state + tok = call_errorfunc(self.errorfunc, errtoken, self) + if self.errorok: + # User must have done some kind of panic + # mode recovery on their own. The + # returned token is the next lookahead + lookahead = tok + errtoken = None + continue + else: + if errtoken: + if hasattr(errtoken, 'lineno'): + lineno = lookahead.lineno + else: + lineno = 0 + if lineno: + sys.stderr.write('yacc: Syntax error at line %d, token=%s\n' % (lineno, errtoken.type)) + else: + sys.stderr.write('yacc: Syntax error, token=%s' % errtoken.type) + else: + sys.stderr.write('yacc: Parse error in input. EOF\n') + return + + else: + errorcount = error_count + + # case 1: the statestack only has 1 entry on it. If we're in this state, the + # entire parse has been rolled back and we're completely hosed. The token is + # discarded and we just keep going. + + if len(statestack) <= 1 and lookahead.type != '$end': + lookahead = None + errtoken = None + state = 0 + # Nuke the pushback stack + del lookaheadstack[:] + continue + + # case 2: the statestack has a couple of entries on it, but we're + # at the end of the file. nuke the top entry and generate an error token + + # Start nuking entries on the stack + if lookahead.type == '$end': + # Whoa. We're really hosed here. Bail out + return + + if lookahead.type != 'error': + sym = symstack[-1] + if sym.type == 'error': + # Hmmm. Error is on top of stack, we'll just nuke input + # symbol and continue + #--! TRACKING + if tracking: + sym.endlineno = getattr(lookahead, 'lineno', sym.lineno) + sym.endlexpos = getattr(lookahead, 'lexpos', sym.lexpos) + #--! TRACKING + lookahead = None + continue + + # Create the error symbol for the first time and make it the new lookahead symbol + t = YaccSymbol() + t.type = 'error' + + if hasattr(lookahead, 'lineno'): + t.lineno = t.endlineno = lookahead.lineno + if hasattr(lookahead, 'lexpos'): + t.lexpos = t.endlexpos = lookahead.lexpos + t.value = lookahead + lookaheadstack.append(lookahead) + lookahead = t + else: + sym = symstack.pop() + #--! TRACKING + if tracking: + lookahead.lineno = sym.lineno + lookahead.lexpos = sym.lexpos + #--! TRACKING + statestack.pop() + state = statestack[-1] + + continue + + # Call an error function here + raise RuntimeError('yacc: internal parser error!!!\n') + + #--! parsedebug-end + + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + # parseopt(). + # + # Optimized version of parse() method. DO NOT EDIT THIS CODE DIRECTLY! + # This code is automatically generated by the ply/ygen.py script. Make + # changes to the parsedebug() method instead. + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + def parseopt(self, input=None, lexer=None, debug=False, tracking=False, tokenfunc=None): + #--! parseopt-start + lookahead = None # Current lookahead symbol + lookaheadstack = [] # Stack of lookahead symbols + actions = self.action # Local reference to action table (to avoid lookup on self.) + goto = self.goto # Local reference to goto table (to avoid lookup on self.) + prod = self.productions # Local reference to production list (to avoid lookup on self.) + defaulted_states = self.defaulted_states # Local reference to defaulted states + pslice = YaccProduction(None) # Production object passed to grammar rules + errorcount = 0 # Used during error recovery + + + # If no lexer was given, we will try to use the lex module + if not lexer: + from . import lex + lexer = lex.lexer + + # Set up the lexer and parser objects on pslice + pslice.lexer = lexer + pslice.parser = self + + # If input was supplied, pass to lexer + if input is not None: + lexer.input(input) + + if tokenfunc is None: + # Tokenize function + get_token = lexer.token + else: + get_token = tokenfunc + + # Set the parser() token method (sometimes used in error recovery) + self.token = get_token + + # Set up the state and symbol stacks + + statestack = [] # Stack of parsing states + self.statestack = statestack + symstack = [] # Stack of grammar symbols + self.symstack = symstack + + pslice.stack = symstack # Put in the production + errtoken = None # Err token + + # The start state is assumed to be (0,$end) + + statestack.append(0) + sym = YaccSymbol() + sym.type = '$end' + symstack.append(sym) + state = 0 + while True: + # Get the next symbol on the input. If a lookahead symbol + # is already set, we just use that. Otherwise, we'll pull + # the next token off of the lookaheadstack or from the lexer + + + if state not in defaulted_states: + if not lookahead: + if not lookaheadstack: + lookahead = get_token() # Get the next token + else: + lookahead = lookaheadstack.pop() + if not lookahead: + lookahead = YaccSymbol() + lookahead.type = '$end' + + # Check the action table + ltype = lookahead.type + t = actions[state].get(ltype) + else: + t = defaulted_states[state] + + + if t is not None: + if t > 0: + # shift a symbol on the stack + statestack.append(t) + state = t + + + symstack.append(lookahead) + lookahead = None + + # Decrease error count on successful shift + if errorcount: + errorcount -= 1 + continue + + if t < 0: + # reduce a symbol on the stack, emit a production + p = prod[-t] + pname = p.name + plen = p.len + + # Get production function + sym = YaccSymbol() + sym.type = pname # Production name + sym.value = None + + + if plen: + targ = symstack[-plen-1:] + targ[0] = sym + + #--! TRACKING + if tracking: + t1 = targ[1] + sym.lineno = t1.lineno + sym.lexpos = t1.lexpos + t1 = targ[-1] + sym.endlineno = getattr(t1, 'endlineno', t1.lineno) + sym.endlexpos = getattr(t1, 'endlexpos', t1.lexpos) + #--! TRACKING + + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + # The code enclosed in this section is duplicated + # below as a performance optimization. Make sure + # changes get made in both locations. + + pslice.slice = targ + + try: + # Call the grammar rule with our special slice object + del symstack[-plen:] + self.state = state + p.callable(pslice) + del statestack[-plen:] + symstack.append(sym) + state = goto[statestack[-1]][pname] + statestack.append(state) + except SyntaxError: + # If an error was set. Enter error recovery state + lookaheadstack.append(lookahead) # Save the current lookahead token + symstack.extend(targ[1:-1]) # Put the production slice back on the stack + statestack.pop() # Pop back one state (before the reduce) + state = statestack[-1] + sym.type = 'error' + sym.value = 'error' + lookahead = sym + errorcount = error_count + self.errorok = False + + continue + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + else: + + #--! TRACKING + if tracking: + sym.lineno = lexer.lineno + sym.lexpos = lexer.lexpos + #--! TRACKING + + targ = [sym] + + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + # The code enclosed in this section is duplicated + # above as a performance optimization. Make sure + # changes get made in both locations. + + pslice.slice = targ + + try: + # Call the grammar rule with our special slice object + self.state = state + p.callable(pslice) + symstack.append(sym) + state = goto[statestack[-1]][pname] + statestack.append(state) + except SyntaxError: + # If an error was set. Enter error recovery state + lookaheadstack.append(lookahead) # Save the current lookahead token + statestack.pop() # Pop back one state (before the reduce) + state = statestack[-1] + sym.type = 'error' + sym.value = 'error' + lookahead = sym + errorcount = error_count + self.errorok = False + + continue + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + if t == 0: + n = symstack[-1] + result = getattr(n, 'value', None) + return result + + if t is None: + + + # We have some kind of parsing error here. To handle + # this, we are going to push the current token onto + # the tokenstack and replace it with an 'error' token. + # If there are any synchronization rules, they may + # catch it. + # + # In addition to pushing the error token, we call call + # the user defined p_error() function if this is the + # first syntax error. This function is only called if + # errorcount == 0. + if errorcount == 0 or self.errorok: + errorcount = error_count + self.errorok = False + errtoken = lookahead + if errtoken.type == '$end': + errtoken = None # End of file! + if self.errorfunc: + if errtoken and not hasattr(errtoken, 'lexer'): + errtoken.lexer = lexer + self.state = state + tok = call_errorfunc(self.errorfunc, errtoken, self) + if self.errorok: + # User must have done some kind of panic + # mode recovery on their own. The + # returned token is the next lookahead + lookahead = tok + errtoken = None + continue + else: + if errtoken: + if hasattr(errtoken, 'lineno'): + lineno = lookahead.lineno + else: + lineno = 0 + if lineno: + sys.stderr.write('yacc: Syntax error at line %d, token=%s\n' % (lineno, errtoken.type)) + else: + sys.stderr.write('yacc: Syntax error, token=%s' % errtoken.type) + else: + sys.stderr.write('yacc: Parse error in input. EOF\n') + return + + else: + errorcount = error_count + + # case 1: the statestack only has 1 entry on it. If we're in this state, the + # entire parse has been rolled back and we're completely hosed. The token is + # discarded and we just keep going. + + if len(statestack) <= 1 and lookahead.type != '$end': + lookahead = None + errtoken = None + state = 0 + # Nuke the pushback stack + del lookaheadstack[:] + continue + + # case 2: the statestack has a couple of entries on it, but we're + # at the end of the file. nuke the top entry and generate an error token + + # Start nuking entries on the stack + if lookahead.type == '$end': + # Whoa. We're really hosed here. Bail out + return + + if lookahead.type != 'error': + sym = symstack[-1] + if sym.type == 'error': + # Hmmm. Error is on top of stack, we'll just nuke input + # symbol and continue + #--! TRACKING + if tracking: + sym.endlineno = getattr(lookahead, 'lineno', sym.lineno) + sym.endlexpos = getattr(lookahead, 'lexpos', sym.lexpos) + #--! TRACKING + lookahead = None + continue + + # Create the error symbol for the first time and make it the new lookahead symbol + t = YaccSymbol() + t.type = 'error' + + if hasattr(lookahead, 'lineno'): + t.lineno = t.endlineno = lookahead.lineno + if hasattr(lookahead, 'lexpos'): + t.lexpos = t.endlexpos = lookahead.lexpos + t.value = lookahead + lookaheadstack.append(lookahead) + lookahead = t + else: + sym = symstack.pop() + #--! TRACKING + if tracking: + lookahead.lineno = sym.lineno + lookahead.lexpos = sym.lexpos + #--! TRACKING + statestack.pop() + state = statestack[-1] + + continue + + # Call an error function here + raise RuntimeError('yacc: internal parser error!!!\n') + + #--! parseopt-end + + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + # parseopt_notrack(). + # + # Optimized version of parseopt() with line number tracking removed. + # DO NOT EDIT THIS CODE DIRECTLY. This code is automatically generated + # by the ply/ygen.py script. Make changes to the parsedebug() method instead. + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + def parseopt_notrack(self, input=None, lexer=None, debug=False, tracking=False, tokenfunc=None): + #--! parseopt-notrack-start + lookahead = None # Current lookahead symbol + lookaheadstack = [] # Stack of lookahead symbols + actions = self.action # Local reference to action table (to avoid lookup on self.) + goto = self.goto # Local reference to goto table (to avoid lookup on self.) + prod = self.productions # Local reference to production list (to avoid lookup on self.) + defaulted_states = self.defaulted_states # Local reference to defaulted states + pslice = YaccProduction(None) # Production object passed to grammar rules + errorcount = 0 # Used during error recovery + + + # If no lexer was given, we will try to use the lex module + if not lexer: + from . import lex + lexer = lex.lexer + + # Set up the lexer and parser objects on pslice + pslice.lexer = lexer + pslice.parser = self + + # If input was supplied, pass to lexer + if input is not None: + lexer.input(input) + + if tokenfunc is None: + # Tokenize function + get_token = lexer.token + else: + get_token = tokenfunc + + # Set the parser() token method (sometimes used in error recovery) + self.token = get_token + + # Set up the state and symbol stacks + + statestack = [] # Stack of parsing states + self.statestack = statestack + symstack = [] # Stack of grammar symbols + self.symstack = symstack + + pslice.stack = symstack # Put in the production + errtoken = None # Err token + + # The start state is assumed to be (0,$end) + + statestack.append(0) + sym = YaccSymbol() + sym.type = '$end' + symstack.append(sym) + state = 0 + while True: + # Get the next symbol on the input. If a lookahead symbol + # is already set, we just use that. Otherwise, we'll pull + # the next token off of the lookaheadstack or from the lexer + + + if state not in defaulted_states: + if not lookahead: + if not lookaheadstack: + lookahead = get_token() # Get the next token + else: + lookahead = lookaheadstack.pop() + if not lookahead: + lookahead = YaccSymbol() + lookahead.type = '$end' + + # Check the action table + ltype = lookahead.type + t = actions[state].get(ltype) + else: + t = defaulted_states[state] + + + if t is not None: + if t > 0: + # shift a symbol on the stack + statestack.append(t) + state = t + + + symstack.append(lookahead) + lookahead = None + + # Decrease error count on successful shift + if errorcount: + errorcount -= 1 + continue + + if t < 0: + # reduce a symbol on the stack, emit a production + p = prod[-t] + pname = p.name + plen = p.len + + # Get production function + sym = YaccSymbol() + sym.type = pname # Production name + sym.value = None + + + if plen: + targ = symstack[-plen-1:] + targ[0] = sym + + + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + # The code enclosed in this section is duplicated + # below as a performance optimization. Make sure + # changes get made in both locations. + + pslice.slice = targ + + try: + # Call the grammar rule with our special slice object + del symstack[-plen:] + self.state = state + p.callable(pslice) + del statestack[-plen:] + symstack.append(sym) + state = goto[statestack[-1]][pname] + statestack.append(state) + except SyntaxError: + # If an error was set. Enter error recovery state + lookaheadstack.append(lookahead) # Save the current lookahead token + symstack.extend(targ[1:-1]) # Put the production slice back on the stack + statestack.pop() # Pop back one state (before the reduce) + state = statestack[-1] + sym.type = 'error' + sym.value = 'error' + lookahead = sym + errorcount = error_count + self.errorok = False + + continue + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + else: + + + targ = [sym] + + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + # The code enclosed in this section is duplicated + # above as a performance optimization. Make sure + # changes get made in both locations. + + pslice.slice = targ + + try: + # Call the grammar rule with our special slice object + self.state = state + p.callable(pslice) + symstack.append(sym) + state = goto[statestack[-1]][pname] + statestack.append(state) + except SyntaxError: + # If an error was set. Enter error recovery state + lookaheadstack.append(lookahead) # Save the current lookahead token + statestack.pop() # Pop back one state (before the reduce) + state = statestack[-1] + sym.type = 'error' + sym.value = 'error' + lookahead = sym + errorcount = error_count + self.errorok = False + + continue + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + if t == 0: + n = symstack[-1] + result = getattr(n, 'value', None) + return result + + if t is None: + + + # We have some kind of parsing error here. To handle + # this, we are going to push the current token onto + # the tokenstack and replace it with an 'error' token. + # If there are any synchronization rules, they may + # catch it. + # + # In addition to pushing the error token, we call call + # the user defined p_error() function if this is the + # first syntax error. This function is only called if + # errorcount == 0. + if errorcount == 0 or self.errorok: + errorcount = error_count + self.errorok = False + errtoken = lookahead + if errtoken.type == '$end': + errtoken = None # End of file! + if self.errorfunc: + if errtoken and not hasattr(errtoken, 'lexer'): + errtoken.lexer = lexer + self.state = state + tok = call_errorfunc(self.errorfunc, errtoken, self) + if self.errorok: + # User must have done some kind of panic + # mode recovery on their own. The + # returned token is the next lookahead + lookahead = tok + errtoken = None + continue + else: + if errtoken: + if hasattr(errtoken, 'lineno'): + lineno = lookahead.lineno + else: + lineno = 0 + if lineno: + sys.stderr.write('yacc: Syntax error at line %d, token=%s\n' % (lineno, errtoken.type)) + else: + sys.stderr.write('yacc: Syntax error, token=%s' % errtoken.type) + else: + sys.stderr.write('yacc: Parse error in input. EOF\n') + return + + else: + errorcount = error_count + + # case 1: the statestack only has 1 entry on it. If we're in this state, the + # entire parse has been rolled back and we're completely hosed. The token is + # discarded and we just keep going. + + if len(statestack) <= 1 and lookahead.type != '$end': + lookahead = None + errtoken = None + state = 0 + # Nuke the pushback stack + del lookaheadstack[:] + continue + + # case 2: the statestack has a couple of entries on it, but we're + # at the end of the file. nuke the top entry and generate an error token + + # Start nuking entries on the stack + if lookahead.type == '$end': + # Whoa. We're really hosed here. Bail out + return + + if lookahead.type != 'error': + sym = symstack[-1] + if sym.type == 'error': + # Hmmm. Error is on top of stack, we'll just nuke input + # symbol and continue + lookahead = None + continue + + # Create the error symbol for the first time and make it the new lookahead symbol + t = YaccSymbol() + t.type = 'error' + + if hasattr(lookahead, 'lineno'): + t.lineno = t.endlineno = lookahead.lineno + if hasattr(lookahead, 'lexpos'): + t.lexpos = t.endlexpos = lookahead.lexpos + t.value = lookahead + lookaheadstack.append(lookahead) + lookahead = t + else: + sym = symstack.pop() + statestack.pop() + state = statestack[-1] + + continue + + # Call an error function here + raise RuntimeError('yacc: internal parser error!!!\n') + + #--! parseopt-notrack-end + +# ----------------------------------------------------------------------------- +# === Grammar Representation === +# +# The following functions, classes, and variables are used to represent and +# manipulate the rules that make up a grammar. +# ----------------------------------------------------------------------------- + +# regex matching identifiers +_is_identifier = re.compile(r'^[a-zA-Z0-9_-]+$') + +# ----------------------------------------------------------------------------- +# class Production: +# +# This class stores the raw information about a single production or grammar rule. +# A grammar rule refers to a specification such as this: +# +# expr : expr PLUS term +# +# Here are the basic attributes defined on all productions +# +# name - Name of the production. For example 'expr' +# prod - A list of symbols on the right side ['expr','PLUS','term'] +# prec - Production precedence level +# number - Production number. +# func - Function that executes on reduce +# file - File where production function is defined +# lineno - Line number where production function is defined +# +# The following attributes are defined or optional. +# +# len - Length of the production (number of symbols on right hand side) +# usyms - Set of unique symbols found in the production +# ----------------------------------------------------------------------------- + +class Production(object): + reduced = 0 + def __init__(self, number, name, prod, precedence=('right', 0), func=None, file='', line=0): + self.name = name + self.prod = tuple(prod) + self.number = number + self.func = func + self.callable = None + self.file = file + self.line = line + self.prec = precedence + + # Internal settings used during table construction + + self.len = len(self.prod) # Length of the production + + # Create a list of unique production symbols used in the production + self.usyms = [] + for s in self.prod: + if s not in self.usyms: + self.usyms.append(s) + + # List of all LR items for the production + self.lr_items = [] + self.lr_next = None + + # Create a string representation + if self.prod: + self.str = '%s -> %s' % (self.name, ' '.join(self.prod)) + else: + self.str = '%s -> ' % self.name + + def __str__(self): + return self.str + + def __repr__(self): + return 'Production(' + str(self) + ')' + + def __len__(self): + return len(self.prod) + + def __nonzero__(self): + return 1 + + def __getitem__(self, index): + return self.prod[index] + + # Return the nth lr_item from the production (or None if at the end) + def lr_item(self, n): + if n > len(self.prod): + return None + p = LRItem(self, n) + # Precompute the list of productions immediately following. + try: + p.lr_after = self.Prodnames[p.prod[n+1]] + except (IndexError, KeyError): + p.lr_after = [] + try: + p.lr_before = p.prod[n-1] + except IndexError: + p.lr_before = None + return p + + # Bind the production function name to a callable + def bind(self, pdict): + if self.func: + self.callable = pdict[self.func] + +# This class serves as a minimal standin for Production objects when +# reading table data from files. It only contains information +# actually used by the LR parsing engine, plus some additional +# debugging information. +class MiniProduction(object): + def __init__(self, str, name, len, func, file, line): + self.name = name + self.len = len + self.func = func + self.callable = None + self.file = file + self.line = line + self.str = str + + def __str__(self): + return self.str + + def __repr__(self): + return 'MiniProduction(%s)' % self.str + + # Bind the production function name to a callable + def bind(self, pdict): + if self.func: + self.callable = pdict[self.func] + + +# ----------------------------------------------------------------------------- +# class LRItem +# +# This class represents a specific stage of parsing a production rule. For +# example: +# +# expr : expr . PLUS term +# +# In the above, the "." represents the current location of the parse. Here +# basic attributes: +# +# name - Name of the production. For example 'expr' +# prod - A list of symbols on the right side ['expr','.', 'PLUS','term'] +# number - Production number. +# +# lr_next Next LR item. Example, if we are ' expr -> expr . PLUS term' +# then lr_next refers to 'expr -> expr PLUS . term' +# lr_index - LR item index (location of the ".") in the prod list. +# lookaheads - LALR lookahead symbols for this item +# len - Length of the production (number of symbols on right hand side) +# lr_after - List of all productions that immediately follow +# lr_before - Grammar symbol immediately before +# ----------------------------------------------------------------------------- + +class LRItem(object): + def __init__(self, p, n): + self.name = p.name + self.prod = list(p.prod) + self.number = p.number + self.lr_index = n + self.lookaheads = {} + self.prod.insert(n, '.') + self.prod = tuple(self.prod) + self.len = len(self.prod) + self.usyms = p.usyms + + def __str__(self): + if self.prod: + s = '%s -> %s' % (self.name, ' '.join(self.prod)) + else: + s = '%s -> ' % self.name + return s + + def __repr__(self): + return 'LRItem(' + str(self) + ')' + +# ----------------------------------------------------------------------------- +# rightmost_terminal() +# +# Return the rightmost terminal from a list of symbols. Used in add_production() +# ----------------------------------------------------------------------------- +def rightmost_terminal(symbols, terminals): + i = len(symbols) - 1 + while i >= 0: + if symbols[i] in terminals: + return symbols[i] + i -= 1 + return None + +# ----------------------------------------------------------------------------- +# === GRAMMAR CLASS === +# +# The following class represents the contents of the specified grammar along +# with various computed properties such as first sets, follow sets, LR items, etc. +# This data is used for critical parts of the table generation process later. +# ----------------------------------------------------------------------------- + +class GrammarError(YaccError): + pass + +class Grammar(object): + def __init__(self, terminals): + self.Productions = [None] # A list of all of the productions. The first + # entry is always reserved for the purpose of + # building an augmented grammar + + self.Prodnames = {} # A dictionary mapping the names of nonterminals to a list of all + # productions of that nonterminal. + + self.Prodmap = {} # A dictionary that is only used to detect duplicate + # productions. + + self.Terminals = {} # A dictionary mapping the names of terminal symbols to a + # list of the rules where they are used. + + for term in terminals: + self.Terminals[term] = [] + + self.Terminals['error'] = [] + + self.Nonterminals = {} # A dictionary mapping names of nonterminals to a list + # of rule numbers where they are used. + + self.First = {} # A dictionary of precomputed FIRST(x) symbols + + self.Follow = {} # A dictionary of precomputed FOLLOW(x) symbols + + self.Precedence = {} # Precedence rules for each terminal. Contains tuples of the + # form ('right',level) or ('nonassoc', level) or ('left',level) + + self.UsedPrecedence = set() # Precedence rules that were actually used by the grammer. + # This is only used to provide error checking and to generate + # a warning about unused precedence rules. + + self.Start = None # Starting symbol for the grammar + + + def __len__(self): + return len(self.Productions) + + def __getitem__(self, index): + return self.Productions[index] + + # ----------------------------------------------------------------------------- + # set_precedence() + # + # Sets the precedence for a given terminal. assoc is the associativity such as + # 'left','right', or 'nonassoc'. level is a numeric level. + # + # ----------------------------------------------------------------------------- + + def set_precedence(self, term, assoc, level): + assert self.Productions == [None], 'Must call set_precedence() before add_production()' + if term in self.Precedence: + raise GrammarError('Precedence already specified for terminal %r' % term) + if assoc not in ['left', 'right', 'nonassoc']: + raise GrammarError("Associativity must be one of 'left','right', or 'nonassoc'") + self.Precedence[term] = (assoc, level) + + # ----------------------------------------------------------------------------- + # add_production() + # + # Given an action function, this function assembles a production rule and + # computes its precedence level. + # + # The production rule is supplied as a list of symbols. For example, + # a rule such as 'expr : expr PLUS term' has a production name of 'expr' and + # symbols ['expr','PLUS','term']. + # + # Precedence is determined by the precedence of the right-most non-terminal + # or the precedence of a terminal specified by %prec. + # + # A variety of error checks are performed to make sure production symbols + # are valid and that %prec is used correctly. + # ----------------------------------------------------------------------------- + + def add_production(self, prodname, syms, func=None, file='', line=0): + + if prodname in self.Terminals: + raise GrammarError('%s:%d: Illegal rule name %r. Already defined as a token' % (file, line, prodname)) + if prodname == 'error': + raise GrammarError('%s:%d: Illegal rule name %r. error is a reserved word' % (file, line, prodname)) + if not _is_identifier.match(prodname): + raise GrammarError('%s:%d: Illegal rule name %r' % (file, line, prodname)) + + # Look for literal tokens + for n, s in enumerate(syms): + if s[0] in "'\"": + try: + c = eval(s) + if (len(c) > 1): + raise GrammarError('%s:%d: Literal token %s in rule %r may only be a single character' % + (file, line, s, prodname)) + if c not in self.Terminals: + self.Terminals[c] = [] + syms[n] = c + continue + except SyntaxError: + pass + if not _is_identifier.match(s) and s != '%prec': + raise GrammarError('%s:%d: Illegal name %r in rule %r' % (file, line, s, prodname)) + + # Determine the precedence level + if '%prec' in syms: + if syms[-1] == '%prec': + raise GrammarError('%s:%d: Syntax error. Nothing follows %%prec' % (file, line)) + if syms[-2] != '%prec': + raise GrammarError('%s:%d: Syntax error. %%prec can only appear at the end of a grammar rule' % + (file, line)) + precname = syms[-1] + prodprec = self.Precedence.get(precname) + if not prodprec: + raise GrammarError('%s:%d: Nothing known about the precedence of %r' % (file, line, precname)) + else: + self.UsedPrecedence.add(precname) + del syms[-2:] # Drop %prec from the rule + else: + # If no %prec, precedence is determined by the rightmost terminal symbol + precname = rightmost_terminal(syms, self.Terminals) + prodprec = self.Precedence.get(precname, ('right', 0)) + + # See if the rule is already in the rulemap + map = '%s -> %s' % (prodname, syms) + if map in self.Prodmap: + m = self.Prodmap[map] + raise GrammarError('%s:%d: Duplicate rule %s. ' % (file, line, m) + + 'Previous definition at %s:%d' % (m.file, m.line)) + + # From this point on, everything is valid. Create a new Production instance + pnumber = len(self.Productions) + if prodname not in self.Nonterminals: + self.Nonterminals[prodname] = [] + + # Add the production number to Terminals and Nonterminals + for t in syms: + if t in self.Terminals: + self.Terminals[t].append(pnumber) + else: + if t not in self.Nonterminals: + self.Nonterminals[t] = [] + self.Nonterminals[t].append(pnumber) + + # Create a production and add it to the list of productions + p = Production(pnumber, prodname, syms, prodprec, func, file, line) + self.Productions.append(p) + self.Prodmap[map] = p + + # Add to the global productions list + try: + self.Prodnames[prodname].append(p) + except KeyError: + self.Prodnames[prodname] = [p] + + # ----------------------------------------------------------------------------- + # set_start() + # + # Sets the starting symbol and creates the augmented grammar. Production + # rule 0 is S' -> start where start is the start symbol. + # ----------------------------------------------------------------------------- + + def set_start(self, start=None): + if not start: + start = self.Productions[1].name + if start not in self.Nonterminals: + raise GrammarError('start symbol %s undefined' % start) + self.Productions[0] = Production(0, "S'", [start]) + self.Nonterminals[start].append(0) + self.Start = start + + # ----------------------------------------------------------------------------- + # find_unreachable() + # + # Find all of the nonterminal symbols that can't be reached from the starting + # symbol. Returns a list of nonterminals that can't be reached. + # ----------------------------------------------------------------------------- + + def find_unreachable(self): + + # Mark all symbols that are reachable from a symbol s + def mark_reachable_from(s): + if s in reachable: + return + reachable.add(s) + for p in self.Prodnames.get(s, []): + for r in p.prod: + mark_reachable_from(r) + + reachable = set() + mark_reachable_from(self.Productions[0].prod[0]) + return [s for s in self.Nonterminals if s not in reachable] + + # ----------------------------------------------------------------------------- + # infinite_cycles() + # + # This function looks at the various parsing rules and tries to detect + # infinite recursion cycles (grammar rules where there is no possible way + # to derive a string of only terminals). + # ----------------------------------------------------------------------------- + + def infinite_cycles(self): + terminates = {} + + # Terminals: + for t in self.Terminals: + terminates[t] = True + + terminates['$end'] = True + + # Nonterminals: + + # Initialize to false: + for n in self.Nonterminals: + terminates[n] = False + + # Then propagate termination until no change: + while True: + some_change = False + for (n, pl) in self.Prodnames.items(): + # Nonterminal n terminates iff any of its productions terminates. + for p in pl: + # Production p terminates iff all of its rhs symbols terminate. + for s in p.prod: + if not terminates[s]: + # The symbol s does not terminate, + # so production p does not terminate. + p_terminates = False + break + else: + # didn't break from the loop, + # so every symbol s terminates + # so production p terminates. + p_terminates = True + + if p_terminates: + # symbol n terminates! + if not terminates[n]: + terminates[n] = True + some_change = True + # Don't need to consider any more productions for this n. + break + + if not some_change: + break + + infinite = [] + for (s, term) in terminates.items(): + if not term: + if s not in self.Prodnames and s not in self.Terminals and s != 'error': + # s is used-but-not-defined, and we've already warned of that, + # so it would be overkill to say that it's also non-terminating. + pass + else: + infinite.append(s) + + return infinite + + # ----------------------------------------------------------------------------- + # undefined_symbols() + # + # Find all symbols that were used the grammar, but not defined as tokens or + # grammar rules. Returns a list of tuples (sym, prod) where sym in the symbol + # and prod is the production where the symbol was used. + # ----------------------------------------------------------------------------- + def undefined_symbols(self): + result = [] + for p in self.Productions: + if not p: + continue + + for s in p.prod: + if s not in self.Prodnames and s not in self.Terminals and s != 'error': + result.append((s, p)) + return result + + # ----------------------------------------------------------------------------- + # unused_terminals() + # + # Find all terminals that were defined, but not used by the grammar. Returns + # a list of all symbols. + # ----------------------------------------------------------------------------- + def unused_terminals(self): + unused_tok = [] + for s, v in self.Terminals.items(): + if s != 'error' and not v: + unused_tok.append(s) + + return unused_tok + + # ------------------------------------------------------------------------------ + # unused_rules() + # + # Find all grammar rules that were defined, but not used (maybe not reachable) + # Returns a list of productions. + # ------------------------------------------------------------------------------ + + def unused_rules(self): + unused_prod = [] + for s, v in self.Nonterminals.items(): + if not v: + p = self.Prodnames[s][0] + unused_prod.append(p) + return unused_prod + + # ----------------------------------------------------------------------------- + # unused_precedence() + # + # Returns a list of tuples (term,precedence) corresponding to precedence + # rules that were never used by the grammar. term is the name of the terminal + # on which precedence was applied and precedence is a string such as 'left' or + # 'right' corresponding to the type of precedence. + # ----------------------------------------------------------------------------- + + def unused_precedence(self): + unused = [] + for termname in self.Precedence: + if not (termname in self.Terminals or termname in self.UsedPrecedence): + unused.append((termname, self.Precedence[termname][0])) + + return unused + + # ------------------------------------------------------------------------- + # _first() + # + # Compute the value of FIRST1(beta) where beta is a tuple of symbols. + # + # During execution of compute_first1, the result may be incomplete. + # Afterward (e.g., when called from compute_follow()), it will be complete. + # ------------------------------------------------------------------------- + def _first(self, beta): + + # We are computing First(x1,x2,x3,...,xn) + result = [] + for x in beta: + x_produces_empty = False + + # Add all the non- symbols of First[x] to the result. + for f in self.First[x]: + if f == '': + x_produces_empty = True + else: + if f not in result: + result.append(f) + + if x_produces_empty: + # We have to consider the next x in beta, + # i.e. stay in the loop. + pass + else: + # We don't have to consider any further symbols in beta. + break + else: + # There was no 'break' from the loop, + # so x_produces_empty was true for all x in beta, + # so beta produces empty as well. + result.append('') + + return result + + # ------------------------------------------------------------------------- + # compute_first() + # + # Compute the value of FIRST1(X) for all symbols + # ------------------------------------------------------------------------- + def compute_first(self): + if self.First: + return self.First + + # Terminals: + for t in self.Terminals: + self.First[t] = [t] + + self.First['$end'] = ['$end'] + + # Nonterminals: + + # Initialize to the empty set: + for n in self.Nonterminals: + self.First[n] = [] + + # Then propagate symbols until no change: + while True: + some_change = False + for n in self.Nonterminals: + for p in self.Prodnames[n]: + for f in self._first(p.prod): + if f not in self.First[n]: + self.First[n].append(f) + some_change = True + if not some_change: + break + + return self.First + + # --------------------------------------------------------------------- + # compute_follow() + # + # Computes all of the follow sets for every non-terminal symbol. The + # follow set is the set of all symbols that might follow a given + # non-terminal. See the Dragon book, 2nd Ed. p. 189. + # --------------------------------------------------------------------- + def compute_follow(self, start=None): + # If already computed, return the result + if self.Follow: + return self.Follow + + # If first sets not computed yet, do that first. + if not self.First: + self.compute_first() + + # Add '$end' to the follow list of the start symbol + for k in self.Nonterminals: + self.Follow[k] = [] + + if not start: + start = self.Productions[1].name + + self.Follow[start] = ['$end'] + + while True: + didadd = False + for p in self.Productions[1:]: + # Here is the production set + for i, B in enumerate(p.prod): + if B in self.Nonterminals: + # Okay. We got a non-terminal in a production + fst = self._first(p.prod[i+1:]) + hasempty = False + for f in fst: + if f != '' and f not in self.Follow[B]: + self.Follow[B].append(f) + didadd = True + if f == '': + hasempty = True + if hasempty or i == (len(p.prod)-1): + # Add elements of follow(a) to follow(b) + for f in self.Follow[p.name]: + if f not in self.Follow[B]: + self.Follow[B].append(f) + didadd = True + if not didadd: + break + return self.Follow + + + # ----------------------------------------------------------------------------- + # build_lritems() + # + # This function walks the list of productions and builds a complete set of the + # LR items. The LR items are stored in two ways: First, they are uniquely + # numbered and placed in the list _lritems. Second, a linked list of LR items + # is built for each production. For example: + # + # E -> E PLUS E + # + # Creates the list + # + # [E -> . E PLUS E, E -> E . PLUS E, E -> E PLUS . E, E -> E PLUS E . ] + # ----------------------------------------------------------------------------- + + def build_lritems(self): + for p in self.Productions: + lastlri = p + i = 0 + lr_items = [] + while True: + if i > len(p): + lri = None + else: + lri = LRItem(p, i) + # Precompute the list of productions immediately following + try: + lri.lr_after = self.Prodnames[lri.prod[i+1]] + except (IndexError, KeyError): + lri.lr_after = [] + try: + lri.lr_before = lri.prod[i-1] + except IndexError: + lri.lr_before = None + + lastlri.lr_next = lri + if not lri: + break + lr_items.append(lri) + lastlri = lri + i += 1 + p.lr_items = lr_items + +# ----------------------------------------------------------------------------- +# == Class LRTable == +# +# This basic class represents a basic table of LR parsing information. +# Methods for generating the tables are not defined here. They are defined +# in the derived class LRGeneratedTable. +# ----------------------------------------------------------------------------- + +class VersionError(YaccError): + pass + +class LRTable(object): + def __init__(self): + self.lr_action = None + self.lr_goto = None + self.lr_productions = None + self.lr_method = None + + def read_table(self, module): + if isinstance(module, types.ModuleType): + parsetab = module + else: + exec('import %s' % module) + parsetab = sys.modules[module] + + if parsetab._tabversion != __tabversion__: + raise VersionError('yacc table file version is out of date') + + self.lr_action = parsetab._lr_action + self.lr_goto = parsetab._lr_goto + + self.lr_productions = [] + for p in parsetab._lr_productions: + self.lr_productions.append(MiniProduction(*p)) + + self.lr_method = parsetab._lr_method + return parsetab._lr_signature + + # Bind all production function names to callable objects in pdict + def bind_callables(self, pdict): + for p in self.lr_productions: + p.bind(pdict) + + +# ----------------------------------------------------------------------------- +# === LR Generator === +# +# The following classes and functions are used to generate LR parsing tables on +# a grammar. +# ----------------------------------------------------------------------------- + +# ----------------------------------------------------------------------------- +# digraph() +# traverse() +# +# The following two functions are used to compute set valued functions +# of the form: +# +# F(x) = F'(x) U U{F(y) | x R y} +# +# This is used to compute the values of Read() sets as well as FOLLOW sets +# in LALR(1) generation. +# +# Inputs: X - An input set +# R - A relation +# FP - Set-valued function +# ------------------------------------------------------------------------------ + +def digraph(X, R, FP): + N = {} + for x in X: + N[x] = 0 + stack = [] + F = {} + for x in X: + if N[x] == 0: + traverse(x, N, stack, F, X, R, FP) + return F + +def traverse(x, N, stack, F, X, R, FP): + stack.append(x) + d = len(stack) + N[x] = d + F[x] = FP(x) # F(X) <- F'(x) + + rel = R(x) # Get y's related to x + for y in rel: + if N[y] == 0: + traverse(y, N, stack, F, X, R, FP) + N[x] = min(N[x], N[y]) + for a in F.get(y, []): + if a not in F[x]: + F[x].append(a) + if N[x] == d: + N[stack[-1]] = MAXINT + F[stack[-1]] = F[x] + element = stack.pop() + while element != x: + N[stack[-1]] = MAXINT + F[stack[-1]] = F[x] + element = stack.pop() + +class LALRError(YaccError): + pass + +# ----------------------------------------------------------------------------- +# == LRGeneratedTable == +# +# This class implements the LR table generation algorithm. There are no +# public methods except for write() +# ----------------------------------------------------------------------------- + +class LRGeneratedTable(LRTable): + def __init__(self, grammar, method='LALR', log=None): + if method not in ['SLR', 'LALR']: + raise LALRError('Unsupported method %s' % method) + + self.grammar = grammar + self.lr_method = method + + # Set up the logger + if not log: + log = NullLogger() + self.log = log + + # Internal attributes + self.lr_action = {} # Action table + self.lr_goto = {} # Goto table + self.lr_productions = grammar.Productions # Copy of grammar Production array + self.lr_goto_cache = {} # Cache of computed gotos + self.lr0_cidhash = {} # Cache of closures + + self._add_count = 0 # Internal counter used to detect cycles + + # Diagonistic information filled in by the table generator + self.sr_conflict = 0 + self.rr_conflict = 0 + self.conflicts = [] # List of conflicts + + self.sr_conflicts = [] + self.rr_conflicts = [] + + # Build the tables + self.grammar.build_lritems() + self.grammar.compute_first() + self.grammar.compute_follow() + self.lr_parse_table() + + # Compute the LR(0) closure operation on I, where I is a set of LR(0) items. + + def lr0_closure(self, I): + self._add_count += 1 + + # Add everything in I to J + J = I[:] + didadd = True + while didadd: + didadd = False + for j in J: + for x in j.lr_after: + if getattr(x, 'lr0_added', 0) == self._add_count: + continue + # Add B --> .G to J + J.append(x.lr_next) + x.lr0_added = self._add_count + didadd = True + + return J + + # Compute the LR(0) goto function goto(I,X) where I is a set + # of LR(0) items and X is a grammar symbol. This function is written + # in a way that guarantees uniqueness of the generated goto sets + # (i.e. the same goto set will never be returned as two different Python + # objects). With uniqueness, we can later do fast set comparisons using + # id(obj) instead of element-wise comparison. + + def lr0_goto(self, I, x): + # First we look for a previously cached entry + g = self.lr_goto_cache.get((id(I), x)) + if g: + return g + + # Now we generate the goto set in a way that guarantees uniqueness + # of the result + + s = self.lr_goto_cache.get(x) + if not s: + s = {} + self.lr_goto_cache[x] = s + + gs = [] + for p in I: + n = p.lr_next + if n and n.lr_before == x: + s1 = s.get(id(n)) + if not s1: + s1 = {} + s[id(n)] = s1 + gs.append(n) + s = s1 + g = s.get('$end') + if not g: + if gs: + g = self.lr0_closure(gs) + s['$end'] = g + else: + s['$end'] = gs + self.lr_goto_cache[(id(I), x)] = g + return g + + # Compute the LR(0) sets of item function + def lr0_items(self): + C = [self.lr0_closure([self.grammar.Productions[0].lr_next])] + i = 0 + for I in C: + self.lr0_cidhash[id(I)] = i + i += 1 + + # Loop over the items in C and each grammar symbols + i = 0 + while i < len(C): + I = C[i] + i += 1 + + # Collect all of the symbols that could possibly be in the goto(I,X) sets + asyms = {} + for ii in I: + for s in ii.usyms: + asyms[s] = None + + for x in asyms: + g = self.lr0_goto(I, x) + if not g or id(g) in self.lr0_cidhash: + continue + self.lr0_cidhash[id(g)] = len(C) + C.append(g) + + return C + + # ----------------------------------------------------------------------------- + # ==== LALR(1) Parsing ==== + # + # LALR(1) parsing is almost exactly the same as SLR except that instead of + # relying upon Follow() sets when performing reductions, a more selective + # lookahead set that incorporates the state of the LR(0) machine is utilized. + # Thus, we mainly just have to focus on calculating the lookahead sets. + # + # The method used here is due to DeRemer and Pennelo (1982). + # + # DeRemer, F. L., and T. J. Pennelo: "Efficient Computation of LALR(1) + # Lookahead Sets", ACM Transactions on Programming Languages and Systems, + # Vol. 4, No. 4, Oct. 1982, pp. 615-649 + # + # Further details can also be found in: + # + # J. Tremblay and P. Sorenson, "The Theory and Practice of Compiler Writing", + # McGraw-Hill Book Company, (1985). + # + # ----------------------------------------------------------------------------- + + # ----------------------------------------------------------------------------- + # compute_nullable_nonterminals() + # + # Creates a dictionary containing all of the non-terminals that might produce + # an empty production. + # ----------------------------------------------------------------------------- + + def compute_nullable_nonterminals(self): + nullable = set() + num_nullable = 0 + while True: + for p in self.grammar.Productions[1:]: + if p.len == 0: + nullable.add(p.name) + continue + for t in p.prod: + if t not in nullable: + break + else: + nullable.add(p.name) + if len(nullable) == num_nullable: + break + num_nullable = len(nullable) + return nullable + + # ----------------------------------------------------------------------------- + # find_nonterminal_trans(C) + # + # Given a set of LR(0) items, this functions finds all of the non-terminal + # transitions. These are transitions in which a dot appears immediately before + # a non-terminal. Returns a list of tuples of the form (state,N) where state + # is the state number and N is the nonterminal symbol. + # + # The input C is the set of LR(0) items. + # ----------------------------------------------------------------------------- + + def find_nonterminal_transitions(self, C): + trans = [] + for stateno, state in enumerate(C): + for p in state: + if p.lr_index < p.len - 1: + t = (stateno, p.prod[p.lr_index+1]) + if t[1] in self.grammar.Nonterminals: + if t not in trans: + trans.append(t) + return trans + + # ----------------------------------------------------------------------------- + # dr_relation() + # + # Computes the DR(p,A) relationships for non-terminal transitions. The input + # is a tuple (state,N) where state is a number and N is a nonterminal symbol. + # + # Returns a list of terminals. + # ----------------------------------------------------------------------------- + + def dr_relation(self, C, trans, nullable): + state, N = trans + terms = [] + + g = self.lr0_goto(C[state], N) + for p in g: + if p.lr_index < p.len - 1: + a = p.prod[p.lr_index+1] + if a in self.grammar.Terminals: + if a not in terms: + terms.append(a) + + # This extra bit is to handle the start state + if state == 0 and N == self.grammar.Productions[0].prod[0]: + terms.append('$end') + + return terms + + # ----------------------------------------------------------------------------- + # reads_relation() + # + # Computes the READS() relation (p,A) READS (t,C). + # ----------------------------------------------------------------------------- + + def reads_relation(self, C, trans, empty): + # Look for empty transitions + rel = [] + state, N = trans + + g = self.lr0_goto(C[state], N) + j = self.lr0_cidhash.get(id(g), -1) + for p in g: + if p.lr_index < p.len - 1: + a = p.prod[p.lr_index + 1] + if a in empty: + rel.append((j, a)) + + return rel + + # ----------------------------------------------------------------------------- + # compute_lookback_includes() + # + # Determines the lookback and includes relations + # + # LOOKBACK: + # + # This relation is determined by running the LR(0) state machine forward. + # For example, starting with a production "N : . A B C", we run it forward + # to obtain "N : A B C ." We then build a relationship between this final + # state and the starting state. These relationships are stored in a dictionary + # lookdict. + # + # INCLUDES: + # + # Computes the INCLUDE() relation (p,A) INCLUDES (p',B). + # + # This relation is used to determine non-terminal transitions that occur + # inside of other non-terminal transition states. (p,A) INCLUDES (p', B) + # if the following holds: + # + # B -> LAT, where T -> epsilon and p' -L-> p + # + # L is essentially a prefix (which may be empty), T is a suffix that must be + # able to derive an empty string. State p' must lead to state p with the string L. + # + # ----------------------------------------------------------------------------- + + def compute_lookback_includes(self, C, trans, nullable): + lookdict = {} # Dictionary of lookback relations + includedict = {} # Dictionary of include relations + + # Make a dictionary of non-terminal transitions + dtrans = {} + for t in trans: + dtrans[t] = 1 + + # Loop over all transitions and compute lookbacks and includes + for state, N in trans: + lookb = [] + includes = [] + for p in C[state]: + if p.name != N: + continue + + # Okay, we have a name match. We now follow the production all the way + # through the state machine until we get the . on the right hand side + + lr_index = p.lr_index + j = state + while lr_index < p.len - 1: + lr_index = lr_index + 1 + t = p.prod[lr_index] + + # Check to see if this symbol and state are a non-terminal transition + if (j, t) in dtrans: + # Yes. Okay, there is some chance that this is an includes relation + # the only way to know for certain is whether the rest of the + # production derives empty + + li = lr_index + 1 + while li < p.len: + if p.prod[li] in self.grammar.Terminals: + break # No forget it + if p.prod[li] not in nullable: + break + li = li + 1 + else: + # Appears to be a relation between (j,t) and (state,N) + includes.append((j, t)) + + g = self.lr0_goto(C[j], t) # Go to next set + j = self.lr0_cidhash.get(id(g), -1) # Go to next state + + # When we get here, j is the final state, now we have to locate the production + for r in C[j]: + if r.name != p.name: + continue + if r.len != p.len: + continue + i = 0 + # This look is comparing a production ". A B C" with "A B C ." + while i < r.lr_index: + if r.prod[i] != p.prod[i+1]: + break + i = i + 1 + else: + lookb.append((j, r)) + for i in includes: + if i not in includedict: + includedict[i] = [] + includedict[i].append((state, N)) + lookdict[(state, N)] = lookb + + return lookdict, includedict + + # ----------------------------------------------------------------------------- + # compute_read_sets() + # + # Given a set of LR(0) items, this function computes the read sets. + # + # Inputs: C = Set of LR(0) items + # ntrans = Set of nonterminal transitions + # nullable = Set of empty transitions + # + # Returns a set containing the read sets + # ----------------------------------------------------------------------------- + + def compute_read_sets(self, C, ntrans, nullable): + FP = lambda x: self.dr_relation(C, x, nullable) + R = lambda x: self.reads_relation(C, x, nullable) + F = digraph(ntrans, R, FP) + return F + + # ----------------------------------------------------------------------------- + # compute_follow_sets() + # + # Given a set of LR(0) items, a set of non-terminal transitions, a readset, + # and an include set, this function computes the follow sets + # + # Follow(p,A) = Read(p,A) U U {Follow(p',B) | (p,A) INCLUDES (p',B)} + # + # Inputs: + # ntrans = Set of nonterminal transitions + # readsets = Readset (previously computed) + # inclsets = Include sets (previously computed) + # + # Returns a set containing the follow sets + # ----------------------------------------------------------------------------- + + def compute_follow_sets(self, ntrans, readsets, inclsets): + FP = lambda x: readsets[x] + R = lambda x: inclsets.get(x, []) + F = digraph(ntrans, R, FP) + return F + + # ----------------------------------------------------------------------------- + # add_lookaheads() + # + # Attaches the lookahead symbols to grammar rules. + # + # Inputs: lookbacks - Set of lookback relations + # followset - Computed follow set + # + # This function directly attaches the lookaheads to productions contained + # in the lookbacks set + # ----------------------------------------------------------------------------- + + def add_lookaheads(self, lookbacks, followset): + for trans, lb in lookbacks.items(): + # Loop over productions in lookback + for state, p in lb: + if state not in p.lookaheads: + p.lookaheads[state] = [] + f = followset.get(trans, []) + for a in f: + if a not in p.lookaheads[state]: + p.lookaheads[state].append(a) + + # ----------------------------------------------------------------------------- + # add_lalr_lookaheads() + # + # This function does all of the work of adding lookahead information for use + # with LALR parsing + # ----------------------------------------------------------------------------- + + def add_lalr_lookaheads(self, C): + # Determine all of the nullable nonterminals + nullable = self.compute_nullable_nonterminals() + + # Find all non-terminal transitions + trans = self.find_nonterminal_transitions(C) + + # Compute read sets + readsets = self.compute_read_sets(C, trans, nullable) + + # Compute lookback/includes relations + lookd, included = self.compute_lookback_includes(C, trans, nullable) + + # Compute LALR FOLLOW sets + followsets = self.compute_follow_sets(trans, readsets, included) + + # Add all of the lookaheads + self.add_lookaheads(lookd, followsets) + + # ----------------------------------------------------------------------------- + # lr_parse_table() + # + # This function constructs the parse tables for SLR or LALR + # ----------------------------------------------------------------------------- + def lr_parse_table(self): + Productions = self.grammar.Productions + Precedence = self.grammar.Precedence + goto = self.lr_goto # Goto array + action = self.lr_action # Action array + log = self.log # Logger for output + + actionp = {} # Action production array (temporary) + + log.info('Parsing method: %s', self.lr_method) + + # Step 1: Construct C = { I0, I1, ... IN}, collection of LR(0) items + # This determines the number of states + + C = self.lr0_items() + + if self.lr_method == 'LALR': + self.add_lalr_lookaheads(C) + + # Build the parser table, state by state + st = 0 + for I in C: + # Loop over each production in I + actlist = [] # List of actions + st_action = {} + st_actionp = {} + st_goto = {} + log.info('') + log.info('state %d', st) + log.info('') + for p in I: + log.info(' (%d) %s', p.number, p) + log.info('') + + for p in I: + if p.len == p.lr_index + 1: + if p.name == "S'": + # Start symbol. Accept! + st_action['$end'] = 0 + st_actionp['$end'] = p + else: + # We are at the end of a production. Reduce! + if self.lr_method == 'LALR': + laheads = p.lookaheads[st] + else: + laheads = self.grammar.Follow[p.name] + for a in laheads: + actlist.append((a, p, 'reduce using rule %d (%s)' % (p.number, p))) + r = st_action.get(a) + if r is not None: + # Whoa. Have a shift/reduce or reduce/reduce conflict + if r > 0: + # Need to decide on shift or reduce here + # By default we favor shifting. Need to add + # some precedence rules here. + + # Shift precedence comes from the token + sprec, slevel = Precedence.get(a, ('right', 0)) + + # Reduce precedence comes from rule being reduced (p) + rprec, rlevel = Productions[p.number].prec + + if (slevel < rlevel) or ((slevel == rlevel) and (rprec == 'left')): + # We really need to reduce here. + st_action[a] = -p.number + st_actionp[a] = p + if not slevel and not rlevel: + log.info(' ! shift/reduce conflict for %s resolved as reduce', a) + self.sr_conflicts.append((st, a, 'reduce')) + Productions[p.number].reduced += 1 + elif (slevel == rlevel) and (rprec == 'nonassoc'): + st_action[a] = None + else: + # Hmmm. Guess we'll keep the shift + if not rlevel: + log.info(' ! shift/reduce conflict for %s resolved as shift', a) + self.sr_conflicts.append((st, a, 'shift')) + elif r < 0: + # Reduce/reduce conflict. In this case, we favor the rule + # that was defined first in the grammar file + oldp = Productions[-r] + pp = Productions[p.number] + if oldp.line > pp.line: + st_action[a] = -p.number + st_actionp[a] = p + chosenp, rejectp = pp, oldp + Productions[p.number].reduced += 1 + Productions[oldp.number].reduced -= 1 + else: + chosenp, rejectp = oldp, pp + self.rr_conflicts.append((st, chosenp, rejectp)) + log.info(' ! reduce/reduce conflict for %s resolved using rule %d (%s)', + a, st_actionp[a].number, st_actionp[a]) + else: + raise LALRError('Unknown conflict in state %d' % st) + else: + st_action[a] = -p.number + st_actionp[a] = p + Productions[p.number].reduced += 1 + else: + i = p.lr_index + a = p.prod[i+1] # Get symbol right after the "." + if a in self.grammar.Terminals: + g = self.lr0_goto(I, a) + j = self.lr0_cidhash.get(id(g), -1) + if j >= 0: + # We are in a shift state + actlist.append((a, p, 'shift and go to state %d' % j)) + r = st_action.get(a) + if r is not None: + # Whoa have a shift/reduce or shift/shift conflict + if r > 0: + if r != j: + raise LALRError('Shift/shift conflict in state %d' % st) + elif r < 0: + # Do a precedence check. + # - if precedence of reduce rule is higher, we reduce. + # - if precedence of reduce is same and left assoc, we reduce. + # - otherwise we shift + + # Shift precedence comes from the token + sprec, slevel = Precedence.get(a, ('right', 0)) + + # Reduce precedence comes from the rule that could have been reduced + rprec, rlevel = Productions[st_actionp[a].number].prec + + if (slevel > rlevel) or ((slevel == rlevel) and (rprec == 'right')): + # We decide to shift here... highest precedence to shift + Productions[st_actionp[a].number].reduced -= 1 + st_action[a] = j + st_actionp[a] = p + if not rlevel: + log.info(' ! shift/reduce conflict for %s resolved as shift', a) + self.sr_conflicts.append((st, a, 'shift')) + elif (slevel == rlevel) and (rprec == 'nonassoc'): + st_action[a] = None + else: + # Hmmm. Guess we'll keep the reduce + if not slevel and not rlevel: + log.info(' ! shift/reduce conflict for %s resolved as reduce', a) + self.sr_conflicts.append((st, a, 'reduce')) + + else: + raise LALRError('Unknown conflict in state %d' % st) + else: + st_action[a] = j + st_actionp[a] = p + + # Print the actions associated with each terminal + _actprint = {} + for a, p, m in actlist: + if a in st_action: + if p is st_actionp[a]: + log.info(' %-15s %s', a, m) + _actprint[(a, m)] = 1 + log.info('') + # Print the actions that were not used. (debugging) + not_used = 0 + for a, p, m in actlist: + if a in st_action: + if p is not st_actionp[a]: + if not (a, m) in _actprint: + log.debug(' ! %-15s [ %s ]', a, m) + not_used = 1 + _actprint[(a, m)] = 1 + if not_used: + log.debug('') + + # Construct the goto table for this state + + nkeys = {} + for ii in I: + for s in ii.usyms: + if s in self.grammar.Nonterminals: + nkeys[s] = None + for n in nkeys: + g = self.lr0_goto(I, n) + j = self.lr0_cidhash.get(id(g), -1) + if j >= 0: + st_goto[n] = j + log.info(' %-30s shift and go to state %d', n, j) + + action[st] = st_action + actionp[st] = st_actionp + goto[st] = st_goto + st += 1 + + # ----------------------------------------------------------------------------- + # write() + # + # This function writes the LR parsing tables to a file + # ----------------------------------------------------------------------------- + + def write_table(self, tabmodule, outputdir='', signature=''): + if isinstance(tabmodule, types.ModuleType): + raise IOError("Won't overwrite existing tabmodule") + + basemodulename = tabmodule.split('.')[-1] + filename = os.path.join(outputdir, basemodulename) + '.py' + try: + f = open(filename, 'w') + + f.write(''' +# %s +# This file is automatically generated. Do not edit. +# pylint: disable=W,C,R +_tabversion = %r + +_lr_method = %r + +_lr_signature = %r + ''' % (os.path.basename(filename), __tabversion__, self.lr_method, signature)) + + # Change smaller to 0 to go back to original tables + smaller = 1 + + # Factor out names to try and make smaller + if smaller: + items = {} + + for s, nd in self.lr_action.items(): + for name, v in nd.items(): + i = items.get(name) + if not i: + i = ([], []) + items[name] = i + i[0].append(s) + i[1].append(v) + + f.write('\n_lr_action_items = {') + for k, v in items.items(): + f.write('%r:([' % k) + for i in v[0]: + f.write('%r,' % i) + f.write('],[') + for i in v[1]: + f.write('%r,' % i) + + f.write(']),') + f.write('}\n') + + f.write(''' +_lr_action = {} +for _k, _v in _lr_action_items.items(): + for _x,_y in zip(_v[0],_v[1]): + if not _x in _lr_action: _lr_action[_x] = {} + _lr_action[_x][_k] = _y +del _lr_action_items +''') + + else: + f.write('\n_lr_action = { ') + for k, v in self.lr_action.items(): + f.write('(%r,%r):%r,' % (k[0], k[1], v)) + f.write('}\n') + + if smaller: + # Factor out names to try and make smaller + items = {} + + for s, nd in self.lr_goto.items(): + for name, v in nd.items(): + i = items.get(name) + if not i: + i = ([], []) + items[name] = i + i[0].append(s) + i[1].append(v) + + f.write('\n_lr_goto_items = {') + for k, v in items.items(): + f.write('%r:([' % k) + for i in v[0]: + f.write('%r,' % i) + f.write('],[') + for i in v[1]: + f.write('%r,' % i) + + f.write(']),') + f.write('}\n') + + f.write(''' +_lr_goto = {} +for _k, _v in _lr_goto_items.items(): + for _x, _y in zip(_v[0], _v[1]): + if not _x in _lr_goto: _lr_goto[_x] = {} + _lr_goto[_x][_k] = _y +del _lr_goto_items +''') + else: + f.write('\n_lr_goto = { ') + for k, v in self.lr_goto.items(): + f.write('(%r,%r):%r,' % (k[0], k[1], v)) + f.write('}\n') + + # Write production table + f.write('_lr_productions = [\n') + for p in self.lr_productions: + if p.func: + f.write(' (%r,%r,%d,%r,%r,%d),\n' % (p.str, p.name, p.len, + p.func, os.path.basename(p.file), p.line)) + else: + f.write(' (%r,%r,%d,None,None,None),\n' % (str(p), p.name, p.len)) + f.write(']\n') + f.close() + + except IOError as e: + raise + + +# ----------------------------------------------------------------------------- +# === INTROSPECTION === +# +# The following functions and classes are used to implement the PLY +# introspection features followed by the yacc() function itself. +# ----------------------------------------------------------------------------- + +# ----------------------------------------------------------------------------- +# get_caller_module_dict() +# +# This function returns a dictionary containing all of the symbols defined within +# a caller further down the call stack. This is used to get the environment +# associated with the yacc() call if none was provided. +# ----------------------------------------------------------------------------- + +def get_caller_module_dict(levels): + f = sys._getframe(levels) + ldict = f.f_globals.copy() + if f.f_globals != f.f_locals: + ldict.update(f.f_locals) + return ldict + +# ----------------------------------------------------------------------------- +# parse_grammar() +# +# This takes a raw grammar rule string and parses it into production data +# ----------------------------------------------------------------------------- +def parse_grammar(doc, file, line): + grammar = [] + # Split the doc string into lines + pstrings = doc.splitlines() + lastp = None + dline = line + for ps in pstrings: + dline += 1 + p = ps.split() + if not p: + continue + try: + if p[0] == '|': + # This is a continuation of a previous rule + if not lastp: + raise SyntaxError("%s:%d: Misplaced '|'" % (file, dline)) + prodname = lastp + syms = p[1:] + else: + prodname = p[0] + lastp = prodname + syms = p[2:] + assign = p[1] + if assign != ':' and assign != '::=': + raise SyntaxError("%s:%d: Syntax error. Expected ':'" % (file, dline)) + + grammar.append((file, dline, prodname, syms)) + except SyntaxError: + raise + except Exception: + raise SyntaxError('%s:%d: Syntax error in rule %r' % (file, dline, ps.strip())) + + return grammar + +# ----------------------------------------------------------------------------- +# ParserReflect() +# +# This class represents information extracted for building a parser including +# start symbol, error function, tokens, precedence list, action functions, +# etc. +# ----------------------------------------------------------------------------- +class ParserReflect(object): + def __init__(self, pdict, log=None): + self.pdict = pdict + self.start = None + self.error_func = None + self.tokens = None + self.modules = set() + self.grammar = [] + self.error = False + + if log is None: + self.log = PlyLogger(sys.stderr) + else: + self.log = log + + # Get all of the basic information + def get_all(self): + self.get_start() + self.get_error_func() + self.get_tokens() + self.get_precedence() + self.get_pfunctions() + + # Validate all of the information + def validate_all(self): + self.validate_start() + self.validate_error_func() + self.validate_tokens() + self.validate_precedence() + self.validate_pfunctions() + self.validate_modules() + return self.error + + # Compute a signature over the grammar + def signature(self): + parts = [] + try: + if self.start: + parts.append(self.start) + if self.prec: + parts.append(''.join([''.join(p) for p in self.prec])) + if self.tokens: + parts.append(' '.join(self.tokens)) + for f in self.pfuncs: + if f[3]: + parts.append(f[3]) + except (TypeError, ValueError): + pass + return ''.join(parts) + + # ----------------------------------------------------------------------------- + # validate_modules() + # + # This method checks to see if there are duplicated p_rulename() functions + # in the parser module file. Without this function, it is really easy for + # users to make mistakes by cutting and pasting code fragments (and it's a real + # bugger to try and figure out why the resulting parser doesn't work). Therefore, + # we just do a little regular expression pattern matching of def statements + # to try and detect duplicates. + # ----------------------------------------------------------------------------- + + def validate_modules(self): + # Match def p_funcname( + fre = re.compile(r'\s*def\s+(p_[a-zA-Z_0-9]*)\(') + + for module in self.modules: + try: + lines, linen = inspect.getsourcelines(module) + except IOError: + continue + + counthash = {} + for linen, line in enumerate(lines): + linen += 1 + m = fre.match(line) + if m: + name = m.group(1) + prev = counthash.get(name) + if not prev: + counthash[name] = linen + else: + filename = inspect.getsourcefile(module) + self.log.warning('%s:%d: Function %s redefined. Previously defined on line %d', + filename, linen, name, prev) + + # Get the start symbol + def get_start(self): + self.start = self.pdict.get('start') + + # Validate the start symbol + def validate_start(self): + if self.start is not None: + if not isinstance(self.start, string_types): + self.log.error("'start' must be a string") + + # Look for error handler + def get_error_func(self): + self.error_func = self.pdict.get('p_error') + + # Validate the error function + def validate_error_func(self): + if self.error_func: + if isinstance(self.error_func, types.FunctionType): + ismethod = 0 + elif isinstance(self.error_func, types.MethodType): + ismethod = 1 + else: + self.log.error("'p_error' defined, but is not a function or method") + self.error = True + return + + eline = self.error_func.__code__.co_firstlineno + efile = self.error_func.__code__.co_filename + module = inspect.getmodule(self.error_func) + self.modules.add(module) + + argcount = self.error_func.__code__.co_argcount - ismethod + if argcount != 1: + self.log.error('%s:%d: p_error() requires 1 argument', efile, eline) + self.error = True + + # Get the tokens map + def get_tokens(self): + tokens = self.pdict.get('tokens') + if not tokens: + self.log.error('No token list is defined') + self.error = True + return + + if not isinstance(tokens, (list, tuple)): + self.log.error('tokens must be a list or tuple') + self.error = True + return + + if not tokens: + self.log.error('tokens is empty') + self.error = True + return + + self.tokens = sorted(tokens) + + # Validate the tokens + def validate_tokens(self): + # Validate the tokens. + if 'error' in self.tokens: + self.log.error("Illegal token name 'error'. Is a reserved word") + self.error = True + return + + terminals = set() + for n in self.tokens: + if n in terminals: + self.log.warning('Token %r multiply defined', n) + terminals.add(n) + + # Get the precedence map (if any) + def get_precedence(self): + self.prec = self.pdict.get('precedence') + + # Validate and parse the precedence map + def validate_precedence(self): + preclist = [] + if self.prec: + if not isinstance(self.prec, (list, tuple)): + self.log.error('precedence must be a list or tuple') + self.error = True + return + for level, p in enumerate(self.prec): + if not isinstance(p, (list, tuple)): + self.log.error('Bad precedence table') + self.error = True + return + + if len(p) < 2: + self.log.error('Malformed precedence entry %s. Must be (assoc, term, ..., term)', p) + self.error = True + return + assoc = p[0] + if not isinstance(assoc, string_types): + self.log.error('precedence associativity must be a string') + self.error = True + return + for term in p[1:]: + if not isinstance(term, string_types): + self.log.error('precedence items must be strings') + self.error = True + return + preclist.append((term, assoc, level+1)) + self.preclist = preclist + + # Get all p_functions from the grammar + def get_pfunctions(self): + p_functions = [] + for name, item in self.pdict.items(): + if not name.startswith('p_') or name == 'p_error': + continue + if isinstance(item, (types.FunctionType, types.MethodType)): + line = getattr(item, 'co_firstlineno', item.__code__.co_firstlineno) + module = inspect.getmodule(item) + p_functions.append((line, module, name, item.__doc__)) + + # Sort all of the actions by line number; make sure to stringify + # modules to make them sortable, since `line` may not uniquely sort all + # p functions + p_functions.sort(key=lambda p_function: ( + p_function[0], + str(p_function[1]), + p_function[2], + p_function[3])) + self.pfuncs = p_functions + + # Validate all of the p_functions + def validate_pfunctions(self): + grammar = [] + # Check for non-empty symbols + if len(self.pfuncs) == 0: + self.log.error('no rules of the form p_rulename are defined') + self.error = True + return + + for line, module, name, doc in self.pfuncs: + file = inspect.getsourcefile(module) + func = self.pdict[name] + if isinstance(func, types.MethodType): + reqargs = 2 + else: + reqargs = 1 + if func.__code__.co_argcount > reqargs: + self.log.error('%s:%d: Rule %r has too many arguments', file, line, func.__name__) + self.error = True + elif func.__code__.co_argcount < reqargs: + self.log.error('%s:%d: Rule %r requires an argument', file, line, func.__name__) + self.error = True + elif not func.__doc__: + self.log.warning('%s:%d: No documentation string specified in function %r (ignored)', + file, line, func.__name__) + else: + try: + parsed_g = parse_grammar(doc, file, line) + for g in parsed_g: + grammar.append((name, g)) + except SyntaxError as e: + self.log.error(str(e)) + self.error = True + + # Looks like a valid grammar rule + # Mark the file in which defined. + self.modules.add(module) + + # Secondary validation step that looks for p_ definitions that are not functions + # or functions that look like they might be grammar rules. + + for n, v in self.pdict.items(): + if n.startswith('p_') and isinstance(v, (types.FunctionType, types.MethodType)): + continue + if n.startswith('t_'): + continue + if n.startswith('p_') and n != 'p_error': + self.log.warning('%r not defined as a function', n) + if ((isinstance(v, types.FunctionType) and v.__code__.co_argcount == 1) or + (isinstance(v, types.MethodType) and v.__func__.__code__.co_argcount == 2)): + if v.__doc__: + try: + doc = v.__doc__.split(' ') + if doc[1] == ':': + self.log.warning('%s:%d: Possible grammar rule %r defined without p_ prefix', + v.__code__.co_filename, v.__code__.co_firstlineno, n) + except IndexError: + pass + + self.grammar = grammar + +# ----------------------------------------------------------------------------- +# yacc(module) +# +# Build a parser +# ----------------------------------------------------------------------------- + +def yacc(method='LALR', debug=yaccdebug, module=None, tabmodule=tab_module, start=None, + check_recursion=True, optimize=False, write_tables=True, debugfile=debug_file, + outputdir=None, debuglog=None, errorlog=None): + + if tabmodule is None: + tabmodule = tab_module + + # Reference to the parsing method of the last built parser + global parse + + if errorlog is None: + errorlog = PlyLogger(sys.stderr) + + # Get the module dictionary used for the parser + if module: + _items = [(k, getattr(module, k)) for k in dir(module)] + pdict = dict(_items) + # If no __file__ or __package__ attributes are available, try to obtain them + # from the __module__ instead + if '__file__' not in pdict: + pdict['__file__'] = sys.modules[pdict['__module__']].__file__ + if '__package__' not in pdict and '__module__' in pdict: + if hasattr(sys.modules[pdict['__module__']], '__package__'): + pdict['__package__'] = sys.modules[pdict['__module__']].__package__ + else: + pdict = get_caller_module_dict(2) + + if outputdir is None: + # If no output directory is set, the location of the output files + # is determined according to the following rules: + # - If tabmodule specifies a package, files go into that package directory + # - Otherwise, files go in the same directory as the specifying module + if isinstance(tabmodule, types.ModuleType): + srcfile = tabmodule.__file__ + else: + if '.' not in tabmodule: + srcfile = pdict['__file__'] + else: + parts = tabmodule.split('.') + pkgname = '.'.join(parts[:-1]) + exec('import %s' % pkgname) + srcfile = getattr(sys.modules[pkgname], '__file__', '') + outputdir = os.path.dirname(srcfile) + + # Determine if the module is package of a package or not. + # If so, fix the tabmodule setting so that tables load correctly + pkg = pdict.get('__package__') + if pkg and isinstance(tabmodule, str): + if '.' not in tabmodule: + tabmodule = pkg + '.' + tabmodule + + + + # Set start symbol if it's specified directly using an argument + if start is not None: + pdict['start'] = start + + # Collect parser information from the dictionary + pinfo = ParserReflect(pdict, log=errorlog) + pinfo.get_all() + + if pinfo.error: + raise YaccError('Unable to build parser') + + # Check signature against table files (if any) + signature = pinfo.signature() + + # Read the tables + try: + lr = LRTable() + read_signature = lr.read_table(tabmodule) + if optimize or (read_signature == signature): + try: + lr.bind_callables(pinfo.pdict) + parser = LRParser(lr, pinfo.error_func) + parse = parser.parse + return parser + except Exception as e: + errorlog.warning('There was a problem loading the table file: %r', e) + except VersionError as e: + errorlog.warning(str(e)) + except ImportError: + pass + + if debuglog is None: + if debug: + try: + debuglog = PlyLogger(open(os.path.join(outputdir, debugfile), 'w')) + except IOError as e: + errorlog.warning("Couldn't open %r. %s" % (debugfile, e)) + debuglog = NullLogger() + else: + debuglog = NullLogger() + + debuglog.info('Created by PLY version %s (http://www.dabeaz.com/ply)', __version__) + + errors = False + + # Validate the parser information + if pinfo.validate_all(): + raise YaccError('Unable to build parser') + + if not pinfo.error_func: + errorlog.warning('no p_error() function is defined') + + # Create a grammar object + grammar = Grammar(pinfo.tokens) + + # Set precedence level for terminals + for term, assoc, level in pinfo.preclist: + try: + grammar.set_precedence(term, assoc, level) + except GrammarError as e: + errorlog.warning('%s', e) + + # Add productions to the grammar + for funcname, gram in pinfo.grammar: + file, line, prodname, syms = gram + try: + grammar.add_production(prodname, syms, funcname, file, line) + except GrammarError as e: + errorlog.error('%s', e) + errors = True + + # Set the grammar start symbols + try: + if start is None: + grammar.set_start(pinfo.start) + else: + grammar.set_start(start) + except GrammarError as e: + errorlog.error(str(e)) + errors = True + + if errors: + raise YaccError('Unable to build parser') + + # Verify the grammar structure + undefined_symbols = grammar.undefined_symbols() + for sym, prod in undefined_symbols: + errorlog.error('%s:%d: Symbol %r used, but not defined as a token or a rule', prod.file, prod.line, sym) + errors = True + + unused_terminals = grammar.unused_terminals() + if unused_terminals: + debuglog.info('') + debuglog.info('Unused terminals:') + debuglog.info('') + for term in unused_terminals: + errorlog.warning('Token %r defined, but not used', term) + debuglog.info(' %s', term) + + # Print out all productions to the debug log + if debug: + debuglog.info('') + debuglog.info('Grammar') + debuglog.info('') + for n, p in enumerate(grammar.Productions): + debuglog.info('Rule %-5d %s', n, p) + + # Find unused non-terminals + unused_rules = grammar.unused_rules() + for prod in unused_rules: + errorlog.warning('%s:%d: Rule %r defined, but not used', prod.file, prod.line, prod.name) + + if len(unused_terminals) == 1: + errorlog.warning('There is 1 unused token') + if len(unused_terminals) > 1: + errorlog.warning('There are %d unused tokens', len(unused_terminals)) + + if len(unused_rules) == 1: + errorlog.warning('There is 1 unused rule') + if len(unused_rules) > 1: + errorlog.warning('There are %d unused rules', len(unused_rules)) + + if debug: + debuglog.info('') + debuglog.info('Terminals, with rules where they appear') + debuglog.info('') + terms = list(grammar.Terminals) + terms.sort() + for term in terms: + debuglog.info('%-20s : %s', term, ' '.join([str(s) for s in grammar.Terminals[term]])) + + debuglog.info('') + debuglog.info('Nonterminals, with rules where they appear') + debuglog.info('') + nonterms = list(grammar.Nonterminals) + nonterms.sort() + for nonterm in nonterms: + debuglog.info('%-20s : %s', nonterm, ' '.join([str(s) for s in grammar.Nonterminals[nonterm]])) + debuglog.info('') + + if check_recursion: + unreachable = grammar.find_unreachable() + for u in unreachable: + errorlog.warning('Symbol %r is unreachable', u) + + infinite = grammar.infinite_cycles() + for inf in infinite: + errorlog.error('Infinite recursion detected for symbol %r', inf) + errors = True + + unused_prec = grammar.unused_precedence() + for term, assoc in unused_prec: + errorlog.error('Precedence rule %r defined for unknown symbol %r', assoc, term) + errors = True + + if errors: + raise YaccError('Unable to build parser') + + # Run the LRGeneratedTable on the grammar + if debug: + errorlog.debug('Generating %s tables', method) + + lr = LRGeneratedTable(grammar, method, debuglog) + + if debug: + num_sr = len(lr.sr_conflicts) + + # Report shift/reduce and reduce/reduce conflicts + if num_sr == 1: + errorlog.warning('1 shift/reduce conflict') + elif num_sr > 1: + errorlog.warning('%d shift/reduce conflicts', num_sr) + + num_rr = len(lr.rr_conflicts) + if num_rr == 1: + errorlog.warning('1 reduce/reduce conflict') + elif num_rr > 1: + errorlog.warning('%d reduce/reduce conflicts', num_rr) + + # Write out conflicts to the output file + if debug and (lr.sr_conflicts or lr.rr_conflicts): + debuglog.warning('') + debuglog.warning('Conflicts:') + debuglog.warning('') + + for state, tok, resolution in lr.sr_conflicts: + debuglog.warning('shift/reduce conflict for %s in state %d resolved as %s', tok, state, resolution) + + already_reported = set() + for state, rule, rejected in lr.rr_conflicts: + if (state, id(rule), id(rejected)) in already_reported: + continue + debuglog.warning('reduce/reduce conflict in state %d resolved using rule (%s)', state, rule) + debuglog.warning('rejected rule (%s) in state %d', rejected, state) + errorlog.warning('reduce/reduce conflict in state %d resolved using rule (%s)', state, rule) + errorlog.warning('rejected rule (%s) in state %d', rejected, state) + already_reported.add((state, id(rule), id(rejected))) + + warned_never = [] + for state, rule, rejected in lr.rr_conflicts: + if not rejected.reduced and (rejected not in warned_never): + debuglog.warning('Rule (%s) is never reduced', rejected) + errorlog.warning('Rule (%s) is never reduced', rejected) + warned_never.append(rejected) + + # Write the table file if requested + if write_tables: + try: + lr.write_table(tabmodule, outputdir, signature) + if tabmodule in sys.modules: + del sys.modules[tabmodule] + except IOError as e: + errorlog.warning("Couldn't create %r. %s" % (tabmodule, e)) + + # Build the parser + lr.bind_callables(pinfo.pdict) + parser = LRParser(lr, pinfo.error_func) + + parse = parser.parse + return parser diff --git a/astropy/extern/ply/ygen.py b/astropy/extern/ply/ygen.py new file mode 100644 index 000000000000..03b93180a7de --- /dev/null +++ b/astropy/extern/ply/ygen.py @@ -0,0 +1,69 @@ +# ply: ygen.py +# +# This is a support program that auto-generates different versions of the YACC parsing +# function with different features removed for the purposes of performance. +# +# Users should edit the method LRParser.parsedebug() in yacc.py. The source code +# for that method is then used to create the other methods. See the comments in +# yacc.py for further details. + +import os.path +import shutil + +def get_source_range(lines, tag): + srclines = enumerate(lines) + start_tag = '#--! %s-start' % tag + end_tag = '#--! %s-end' % tag + + for start_index, line in srclines: + if line.strip().startswith(start_tag): + break + + for end_index, line in srclines: + if line.strip().endswith(end_tag): + break + + return (start_index + 1, end_index) + +def filter_section(lines, tag): + filtered_lines = [] + include = True + tag_text = '#--! %s' % tag + for line in lines: + if line.strip().startswith(tag_text): + include = not include + elif include: + filtered_lines.append(line) + return filtered_lines + +def main(): + dirname = os.path.dirname(__file__) + shutil.copy2(os.path.join(dirname, 'yacc.py'), os.path.join(dirname, 'yacc.py.bak')) + with open(os.path.join(dirname, 'yacc.py'), 'r') as f: + lines = f.readlines() + + parse_start, parse_end = get_source_range(lines, 'parsedebug') + parseopt_start, parseopt_end = get_source_range(lines, 'parseopt') + parseopt_notrack_start, parseopt_notrack_end = get_source_range(lines, 'parseopt-notrack') + + # Get the original source + orig_lines = lines[parse_start:parse_end] + + # Filter the DEBUG sections out + parseopt_lines = filter_section(orig_lines, 'DEBUG') + + # Filter the TRACKING sections out + parseopt_notrack_lines = filter_section(parseopt_lines, 'TRACKING') + + # Replace the parser source sections with updated versions + lines[parseopt_notrack_start:parseopt_notrack_end] = parseopt_notrack_lines + lines[parseopt_start:parseopt_end] = parseopt_lines + + lines = [line.rstrip()+'\n' for line in lines] + with open(os.path.join(dirname, 'yacc.py'), 'w') as f: + f.writelines(lines) + + print('Updated yacc.py') + +if __name__ == '__main__': + main() diff --git a/astropy/extern/pytest.py b/astropy/extern/pytest.py deleted file mode 100644 index cda3fd2485b3..000000000000 --- a/astropy/extern/pytest.py +++ /dev/null @@ -1,2471 +0,0 @@ -#! /usr/bin/env python - -sources = """ -eNrsvWuXI1dyIDa7a6+8sLWS7LXs9UrrJMq9mclGoauanIewBMc9zW6pJQ7Zh90tcU+xDGYBWaic -AjLRmYmuKo2o4+/+Ef7gH+NP/uR/4F/ieN1n3gRQTXJGPsdFdhWQeR9x48aNGxE3bsT/+s+/e/uT -5M0fb+7Gs1W1HM9mRVm0s9nbf/bmr4fDYQTPlkW5jJ68fBEl8aauFtt5XjdxlJWLKJ5XZbNd03f4 -WObzNl9E74osus7vbqp60aQRNDIYvP3nb/4Ae2jaxdt/8fp//2c/+Umx3lR1GzV3zWAwX2VNE71q -F0l18RtoI50MIvjB7tfZdd5EbbU5XuXv8lW0uWuvqjJaAxgreJG9y4pVdrHKowy+lFHWtnVxsW3z -EbWAP9wRDqG9ytcRVL4s6qaNsvk8b5qx6mlAHxb5ZaQwkDT56lJAwR/8CuhZFHN4GU0R9LHAYVde -5i1CIfVHUZmtc6uVtr4zX/BnDU1BlwQlVKLiukB+O883bfSC3j6r66p2K9dZ0eTREzVqKpEMAdOA -6AlMyXa1iMqqFSRED5ph9CByu6jzdlsDRgcDqAOw4DSkg7f/2Zs/xAmbV4t8jL/e/uev/92lnrbN -3cBM4CiqmvEma68Gg4ttsQJcz+p8U0Nb+GcwwN+r4gK+Q4tSYjwDRHATSYwF4lEUS8E4VSTxFDru -0sRNnW02eR1ldbUFInzJJIFARly2oQkNzucIUHaDRa0pkScMHw0Y5lAeJqq4SwbwFIfH7/rnlspe -FqscUW4qQCcz9TRUHshzVZR5WflVzIvj6LRbs9uL04MQk0stIXp6fbdRpITEk9m4nUQPaiAihZdR -mtrEn7/VeK5gudU2lpnODPqmXAS/2E2U+b4mECYsoJsw1ZEK/XWLJCM1MyogI4k2VVEyY6iiptrW -85wGqmgHfzZMFFhrvKrm2SpR8NtzaIijuCToNuP5VT6/TlIXu0fR119/DSzt7iJHWomusnoBdLwq -rnNkTtFNXtQL5LjF3KtXlFSgaYHrZlgGltMZksI8g57G280ia/nzebSo8uaXTn0cRQhuH7EbRiTh -CMZdV7DK2rsEv4+iL6oyV7+HjMZLAKpobOoYWtRwuV2tGK27Z0TW3CueAZmby6qmEWMjanIQbO6U -J8puT3++rKt1pDiX4nvcgCkDjY4i4uH0ApZcubBARTx1GCRWGqjaApGFJPPUQZUzD+7P0B4b7J5t -BnyKtql+nH5vfFrtxo3qvCpXd0FkHulVawpKUxlQeQaohflw5kKRkgOFhVU9FNwn62WzcyztdgNT -flMAsSHwUB6EjbKlHawJjalnFFewbBjbV0AO8y3jAyCg5Y9g2JuBtVr8YckCepfVBMLZRB5AE7DZ -lu252rme1/C+s3X9nbtzZWrvusTS0VW1WiA8lzNiNQ2JWJez5aq6gG/UBvCBm6tifgW8fFOD+FKA -/BXNQfABjpK/y1ZbYAOLcb84M+KufKnG2/5o26WC48tZYPfTm5MqE9iUeCtTwFtl7eFYBWXIVpv0 -ILTtUglrTYC8lSNZ+KQEzFiPbmytWlheuDa9nS9It8NhGtzAvCZRYHDBEBxxbf3KZhj64YH8Ymha -IQ7BNAMfModDIBXIVNP+HH34IVBr460wRSsowS/yWO0vFmbVT4y1QdqvYYltWqC3bBVli0UhH2mW -IihToBTeDAI4bahpoNbtqlXsW/qHNsJM3JCDQx6A981dknbKyQaY0Ej9CSOMMC5cohzp+jb+bvP5 -7AAEQrH3Qd5/3IW8Hw8VluDNA9yNj8hCCArlSh6z2VmHWcdNdgnYSMqqPK7z+bZuinfQB1D1MS6G -FJZBjeyNdAPkv7HsQ8Fxm/VYVGNsmeAQCAx0RQOKxDbvA1Bakc3hXnvPqmiYXHEPaiJSzEYR0i0M -BcHPYD9RO1LTNwYoDzN3dm7mCWvWSyQaw0kUPJ682NEnTKNj3EjKRZJAvZFLGWfw6DxNnYoi+v9N -fhcQ+lnmg02LN0uWP2BPqeYwibDf5GW0bXDmXjZ386qzJRI8at97XWfz/CKbXz8rAfqu7pZF2BJs -6jm+RyzALq/qGD0ct7eivMQ9BtniYIcyRw111HT1gtUh+ujtODUrS4rh84atyo7bixnvlJYU8WZ5 -NY4ejz+mffnx+KfRorgEumwiUEFyxlNekhiQI6FbNdfA+gpaBWYvaMYwNBBNt1A3u6i2LUv41WqL -3GEUgYZmtQCCC+rysM2jQIvsomdLtkfQty3X+YpgmTp1jy3EyAZnFE57BnAlds0jQg7DT1wSiB40 -kweLT1Fl9JtnvcIC4eFpesC2fh9pd1vXuGGardNenlqI74xbb+ydzf/3sd0rPcusE55he9sPKb62 -wOKh/V56HqjHvipGerIrzDPz2Q+DI9npvU0DoVvyIJGSgAuQnPN6ld2RqIxNDp3dqsDlB8JxiG6+ -Mm95SFmxwmYMrnFpK7ElgxZBKVvliwh5Ub12BRb44XV7g8oQgk/vG9ru4RvWEGHcl0g1ewuKooYu -W955xxq+dIyb6CZxufutxcdmDgZEITXoHwknmeHQp69h63SbEgNGAVwajQ0gAt+OEI40sBH5tiKD -W8ZgjrbH8li2fTIbRdCctzW5CJlGt0HaUQUcktP8KawWH6Gx969Zw2KR2TKgidJ0fBrBTpohl7B0 -YGUTzW6THTxxFJ24S2Cvdq7pzayi8XuzpEKZA3YqNLR96uZbVNml9QACLcNzuXC3h34j52ykSneZ -Y52VyzyB1x2TdvIClvEtUc4o+luUquizR2cdMKKH1mYGin2+rUFOK+Zkw2urDezJzUaYgzZaRADU -2KoluFvnQBXlcoKPQHYBQU7JPtsGzxaiLwD9BJVTl/7MsOQsItwR00B2YqParRABra2qm+gmjxZV -GQPPyIDZkK2KebmhntQRFQsUkRiLFhYI4x6qNDmcFefjWtjEGMo1aD9J4kkcWMOM38LBqoa4zrPr -8AZ4RoQ0gdos2HbNYXo8jqx+VSwWebljgSBDwpHYm7cYR/CAB3UlEED0Rgjt5bOZR/YgQr0Tqy42 -52pk6woInm1YxK5QDYPZDorvHUrvbmakWw47EA075G6ffDR3TX4bsLt7dYJ9P4dN0BGHmzYgi3Ug -vyzt7QR39RCEOdL6OKAhUPX4l7/8pdHV5KzB51OOWbkDhpI6A/vayt/YjMpyUWX14gXOVr3toGUv -4qTPIUDf0S6HUfQcjcoPapBRcaU9aL4pI/qNAutl6YmnfJo3ojYtwsaHBwpfYi/UaBI06nXD7TuS -jxT3RR8lgnl6V4J6q6Vw6Rf6bAx0lGzTbFdo/kHOVaESE10Vyys8i8BTVj0IPiPllWTLnkVuHZzi -32eia7myf5/W1l54qx/fFtmq+PuceeKyeJeX6qy39Ubg7pjALIA14Ilr0l6MohjUnjK/bX1mRwcS -CbCUABO8uUIaQF0XmD9KP93NVP3cFTloYTSprOBii8GS2NwUf48FIo8om3bsW2ZhAJZslMNCn+yt -BFUMHc63rTzGFT5l+mHalS+W9CJPYMmstotcV+hTtAwBKMmQz2GRFNVBAO2euqDLeC/ukMjfkYE7 -K++AfNcXRUkSM1Zl3UP2eLJ723IWbAcuP6ITfN4YaONHAHDPaKvji/xYi5+GdAAwEObzeg0tLlzI -COpsBdtzgxhUrgLSiRpbEAOOXKG2bNYUWjZ1ZQ1qBEmdr6t3LJMAyNuS9p68oUIXRdvwgcoiz1ZO -c3TygYcXJDUq2ykxOcDbIz28NGw6BGBulaHJJSUx+N9arKnzXtRDV2e0ll1CCqMSPiPozNSa0oSm -OJOd1ZFYJGfXxpWnlrJqiZwCVm0Vp5ElhekfrKKKjqmg3Xja079QmdX1rbafTIUGe6raGoRTP6wh -YHvW1zQNmvNY8FH8+9YYr4JnD56vSQEbqOYG17ltCWPbY7OFnSXR7fOOlo7tyljNZqiW9gevYPmB -jpc0qwK+n6T+IKQXdo6hzQhahIcd4MlKqHkxyEK5sitfltNVtr5YZNHthOb0dqxlxfQ+DAmXyxz2 -0QyIHsfWRLTw/BUP4gwu+ehyW86JAdHqQ5HV2CeVZXdkd/UC2nSXgXQ9Ip4lRjpbliWrIK5aBMey -4mUwOJe+xMpiTRTJetwCIKXDTgGNGR6pEP/icRIfc5t5QWjgQ0M0TDBanbZgFnIjnKdo1XiXp7us -8oZaZR6VpJS6CvG8zporIuUdMj+QTEuGAgbAN3Px5KzyzKBLUKUZIfNnXW8cluYvtLjKMHe8NtoL -z/yuavjDby/Ojk/PbaMTnXZUwNZBp90xVCIELKN4ObGNR85k4YTXuWnTAamqiyXumjDTADRsvyA3 -1gV8Z2mRR2Lqsgm/tijNxgj5i8AQf/udq3KOjHE+L9G7D8+TvEGJW8rCOYSn81mUofJ8gbtvFd1U -9bWcdntV2Z2FNNsIFPEMRrIEZKxxo5MDtUU+r6DvqiZ3F3Hs2BReQ0zay7wkOBvXD4xo5yp7R/rj -1SM6vYnyt1uQNds7tyF0zUHAkQdAO668BmwQEMWqeseCXSySzhv0dRA8+gYU6g2tWS3I9ugmRIZM -GKWZtqwdEjN3xT/cjMndp8lbWfzMn8/OO0a8VXenuXRH0Hm/quZ4uN49ereJg/ytsCTM0SosI0Pv -l2N1Lnc5luPXGWG93+CNxwoyfBqkADE7ncKH+1d7PFWQhjZdbz27JKXP1uxJNSfFIfvaQI2P5KX1 -BvSJJO4dEUoFvXDHwbHGv0T/SESlcY58ptjfi/KyCntJNuSkCnxyhrZTYO1qXWi9z8zyVb7a0BSX -2btimWkx2GOrioHMSF9vQbFB00Dcq+ptN1rRYAuur2UcRV/8asyHqcoxUUzodfEOVvQHUfRqe0FD -hheKBD2LnIOLY3KoUzXW2R1zAD6tIyO/7mhs7wEAa9iUii+mHir9Beeb9AnFliAFbZydno+iJwBU -jZCGzJ3e0ae4Euu68bpZxszpu0pnAIYw4VsdNLrx3e3hWNSXMWlbYlTMaDRR3LPGWCp0KMUd/ySK -vYNbwLAAB4C571AMFYMMMfKRtaeret0atqEKv4OUio8ssyG/0lKHOUsxYo6jZu89AXWoMXoA29DF -Ki+nfAwaJQ5ooPSKRdSAkDr+KHP0ulPLqb4jF7YpyYb9QoYRjchsykcwrgRJcmWsGozFcJo3ym46 -0mZt0xhKza5nqTp1cqd1xJ6C5LM3b5EvmjZEofZUcLsXxG7ewELNVKdaoU+aKkBCUMSzlZLQzyID -tn2Rg9SEzpVhqZat+DTvTbsw8zBmVX6mQZvpqegQo2lZ88Txb6qiJG2z6bzFP9pK7zAhmZCOcZ9q -WGvPW1uBFWh1daaJzK5x3hFs0U5lSK+uLQbGBAio2G+5dwmmNApaRfQc0ii8jtQCx+6shYd++UAc -pFb4q08Wilrqat3oFeOedSrnEHfhjV2dxfKfss1TSozih0EZT0MJhb+Cz2hl/hx2eERKYjeGNmUB -Ne161kgjjk5l+WPBwG5YbmB+gCe1d6t8OlxV5XLoChLZRUM2PinYXrAWNOWljpoweiDt4iu4WaR0 -18ZboMryZ0077hPuKa8GdUKf1ck/esN6Cp9bDwc0iXBA/0Dz+A9l9Q9oNnxnSSZcytPdeHwTVHhz -ZbSOElauOgc2EblXbJVR36j3IHE3RLeM2Sl3bfCn9H4ZHLZTLMsKVK6wFlpISygDxtxYHDzoQapx -JTx8onekL6hq0vXj2sfCkv6Nmlha/2tekjuqqz7PTs6NDatbIU2Fb7rLObVwf7lGxvWcDan54hnv -7YlF7+ajInr6HaZ5+WtRvfpgUb760HX8XwOnxg0/V2Ag49l7BhfmMGQ4cDWuDn/o4y54SjtyuVva -NRHCKkWtx4ZvWxa4S/+TgVHgETiVx7w/2x2NRpgOWwi0P4444SjXiudi02Md5y/ZLFDVjTk1OmKb -hX+gxXc7VtXNbJ3V1zme3gw/5RrYtvX0Wb93/R6OrCmSue6hTJjPSg2TmVr9eIWYt3gMkadXcayp -7tfziJTu0QggH90CAjy8l0/uzoyGrVIdQMnhv63mXRZL9HXEyeKifAmDjvo815buRTp1ahzwvyPR -hrs7Pk1/+APkoBeuCxCumD6H212ddwHoA8JaPSf+cvoYFh1jIY2OWZ3Qx+mpJz7RWrX8lGT12sSm -fDn6HVMcz6PwgalxZOIZW+RCHmnYo8SCUjt7a5/uro7VdQv3PNZn2r+7O0Llhaeo3IVcloDlsm25 -bbPGBh/YkkgWUmMk6nHjVq3aPtvckEjkahiq3dRzkbNEAmRvT7AP3qpswXDmTKty/UO2PCOb8/SY -ZU1thBlFe3XJS8WUiZsSC1xE242aXtJtxkFdysLfHkc14zzk3b1Bj42048vBg4HiJ3YH1ptPopNJ -X62H08hiF32eVlaRrpNVoQ4JucmAK32dXxa3+rDB2lEeosNJNHQXe+eYX6GsqwWa01ncBbd5f9fD -qNOR+MFIkYfGWaxTShMqu7k4BqG5si+Fl70w9qkR8NSu4GHc9AUqJ/alXBJnSmyQvkbS5pT/jIgI -s5X4rna4CbXpLgvXguI3+7Fp0V8HIVJW4xvCfx/KV2uPW4pjcZ07Ng9bXKksv/78VvUhYPWoreNm -syraJP6mjC2iJYnHxrctpzwU4M5OJ+7lE6IDZFzc96R/+q0OHkYuLVinZoK9wHmDDZ+HqfBsEeO2 -/MhHoiUGeLelT3YH0GXf1/kdPUVplpAgJxiitF3iJ1Bbog9gZv/nYbfuuMFABWlnSyDjIjSEZboY -4E1iKt2cYeHz0FJnEyUohLOZOAk2s1kcXvvODA3tCtDRJ+rbp8OuQTfMaZhuX5N7tHFc4cgRaJS/ -yNkBBfj+xV3HEce0QDbRJNVn6iM5mIN2ySwjsR3GuIkBxnpaWRTNcluQ3Exc5l1eo6tQSbIj2h7G -Yf0TFDAJOeFtqZ5tzukNpx05vVROYef4+YnyRLEMUjsU36Noxw+54o34AtkowrghfWdR7qQ+OD49 -QWqlaB3iEaiB7BnLrsnVtnXsRjf/zTdkjKbm+1rVQQv6X4vBYUMHmvJHMIZA59l6qrRBZHA3dQGS -cK948zkvfrGZuoxBa2wz41Egwp0r1zhaR2DLYjNlQcYaZNbkgNBeeNYn1xXfl509/5wfXcTpzu+R -EdA7bvzmdsPI8jk5QVf435BTZX+XjjJ/TFcg+vsxNyB2iH5GLwVGmQxpWxvSjR5Qib0FQS8VzrWh -TOp4RLOpRVT/wve7l52pR4KyasptOKUueJOq9TwlmigBuyNaW2IF79r0e5e009lKe4Bd502TLcmB -mdyTkSPwfLiBR/oZvGlBLQU+zWN5Q5+QAdsbuhgWXZ/JH+Mw0R0zY/jxdsNilcM+J1y4x+ZtExda -vgU2D1HEA6She86tU9eaXhEqpH/vJN+3uhJ3oekaaWIZWU2P7MHKjIfE5Mn3kHY/7oq2HdhsL3H+ -zRKspUvraTNuwa4wpWLpKOPOAaaPshsUJx1f5MjsV9RXlzjEGvLlq57bx0qrD3iZ4g5dbnBvlr2a -m09DV5iJXsvNINRsz17iagXOFQpzHqwXi3Zt9vmbPm7w0GfasCzyvvuZ6z7mE72hp7F7IPfVFrTA -dfDaVKdF07vnq2agoF3QKulYgdX5icPcgZbrTNRKB2S+XcN8setJZo65vbtecu6fMCSyFaU2u0Iu -1eEIdBplG7KD0oKSE2iavTtmfKFBxC/dXIdFCzKnHnpDLhwGMcMPPvgAlq7y7EIndAqSlzTIdUUB -+Q/RpmooKkQ6POwelnMYlMgQRqZnfSqiN1JfnLJPM0ILAAvZBBxAreJaafDcU1Gtd8wz2HvQ5fQ8 -Mm2ayy7kx5+tsOpk3zlMY7yTzTmIK7mBfgeKBs4POfegIvhJ9FHAAj3G+/2LPIm37eXxL+KuLfOg -UxcvIkR7w/y0qMZqYH9HUnKiPNG88Dtt1Uq5pLXiVGGz62yTqH4r1hhg5+rAORyKb4K5T3y4d8uD -xhzoZ2304OTW3O7XHuDkFak8grFZD6rEmHWdTcbYd02gwArdEtjGe5hp3ZjUpaymr8BwSGM5ztmV -5EFNLjpB8zLTXpdcbWpMJ72RKvqIeuLHZnCXnfO9W1QtNuMr4J4H5XIYRhSnYcsWC3ljBakckV2R -bGxNvpkOj4edIyhpTRuyu9Xs8waLTsV76WbnaPsIWxla3J50BBUFlbf33sCLDXS8GUVdURnekjIs -DTqza9hqYGZZB8xRBBrOov6DQLWLBLlyCAtmh7W+DbxL8mZb0Z93nf8dPBObCkmN75baWNe7tw9m -9+qxq7CRbhc2a2NnYUuFnjIcq+C5a5hQUzcc+nEEeBR059ff3+/6aMtEZBB8ToJ04pYJk4u4ZDjP -+vlBe8FGjkl4AofsGzLcRShnWiOQrlWb5/10I+7KhpVZ1Q9jZD2AK+VfXh9Ke4BgMgklaksyjaVp -B9AQiCxCHbAkLY1OnfzxN+VHEtDqegbJQ+xMjXWCbn/tFtTn+OZLoDUGB4/jDWAdnwA2l+DfQ1Fu -mdB9Fdrvu2/V+GcJBjkh3W4hGLO9F4fP8ICqf3Uzt7yoVgtxjoBmpvDPrXHUxwx4d+8M2Z6VvpHL -612b0b5hHz7kw4drDyF08HFkc0+9JkbRkE2qPf12majTxT6uaZHK5H7t9xDYPmFeSW14Q5//kRF7 -+E3ZZR+7Yq+GBnt4eQHe4U+OteswTmr7gRpbmMdylBVOGT56wn55/qf00lj+5NOhHOIo0jGrUfmW -Tqptu5HIqXmGIUhd974jCRKXlVbJddbyHSyMuxHliwJdsSKKkEaxjHXtdbNUepoCVhMbDqBZUpBe -mmn3riqe2R2fejHVqTX4fTYpbEVLiBJDkjUTsaZqLDuxHkZY292B5GzisLnduUfea4c8hOHYbMSl -SuUuchjU5FTiAS0bGm1k99xhumyWDw8uM/KjGwaPUyXGoh5utxEyQiJMMJHeMVnPcZ+yIzYpnmTn -4pSPcOCDx9GniEEMsXRTLHwbqOdkQrX6b5TZM8Ed9B/4CR5gLPc4qz0MDNP+Q0AT7AIwzEA3u7vy -AfUa2A3JHkTYGwT8wManjq75QupAgtrNr/QJd5Kpex+yR8pNO3aRsNxEt616RezLvS4S6X7aSsIv -x41+aHyZ8HqtqjjZHV1cl3NMMdaQ7Aub3u2V2L29qe8eWgYQ3Yp3dSwU3LWnLK5f9cB7pQYrGJsc -NAYpfB/gpcpuqNW0mYNSeaJsSO9HEXwf6CCioAmWqNyHEsVB+LdQeebTwPl4U6lrQKGp2I0qp2U1 -M6rJgbo1Iuksqovf0C2yuXahstFEwpV1gdry0FV+HQYZIbsdVEMbYKXDcu3JOQHlrXuOBFxcrGfY -WcyunzuLYjnq7aDCB5dUI/DL8vWsOd1VLRdOwBKu6CbpiLvOg25x6Afagn5S3Z64U8BcbjDUUdHQ -Xp64TrDq55Yht+Z2rJo0kyxngoF9RAFzuxdyb5ZvA9M+GLz9l2/+eLa5w7vz499sQay4Xa/e/sHr -f/vnP/kJUxcxS3wtwcDRjhz99Rsoefz1rz8XcXFENLdtJJrHX20XDfr4A3qQyBcUtW3JkTZBPqjR -bj8eDH6VNaBzkacdRZJiIqbF/FUFstDn2c0qvxsPKMBwJ4dN1ahPdW7ltVEf8ZQN9qgjxRcej78m -gD6Cv7jeAJiLggIK6CWB1u6rOvnZT9OBrAAdS9AugNfFr2q3Gp0vPIl3VeTgPaASmJpocEtO+yrh -W3LCa/V1i7/GGUJODbM0xuLNJtMO+xgNmkb8dznFRMC9Urk2NtsL2ApUAIuiBJGrWGiwyIG2wcBh -Vb3gKHzQDE7v6fjECkPCtQoJzLkxDHcxjqK/yimaC3DsbDWnQGUDCUu9uAM5r0C6vqNDiDzDe+yU -SAW6p5shLTTwGuGEBcTgYAnqD1qZQ1F0/JlET+FTNJlMo6Pbv4j+AX4/od+fwe+zo9vHJ8fw+efP -n5/z92cnJ/jk+fPnn50Pgl5fVOz0hMudnkDJ5+eD2SpfZqsZ9zqNkpPbk78YRfD7Cf0G/V5KCN6g -CE0AFHx8gkUQBNFk4RlCgU8RDPOUOsXH3Cu80M3C5M5qJJYzRVogMx+DxJyi2ixUuqowVoV8wThw -QXcwXJZYdESh4lKcOwf2QVhWrW6iTzhVVnYrMJyHoYPOb1MTtMpG3TmIr06dQbHymqi1rJDoVXT2 -vzxozoG5Ptip2evicco2BKcnwMUiXznQ2A9k7NYTAZA23ouipO95M882OfrsW7oXMMRVskaBxuXu -qO/C4tGvxsu62m7sa1Sk9n4yJUIIXh7UQzq6fXDy+GtEgRXkoivxh6p9bFdTt3QdrI+BFeBh7WoU -CR+xximiB+8Hs2yx4AQIyQYQqJM60chQ2qOH6BDDYx0q7VJ2jQI9xUyNsWkuPj5Wew3s1hmJKNNh -01Z1DmrPAvqeDuEd6vfuhViMOfMuq6dDfqWiKU07YbkxLsZ0OK9zjJdIfR1Dg3KLUzY1ykyEIaQ4 -biE6xuyBl93we0G2yvSADdx/P9TQAgrkyukfuDgxf06fA2vMDIdNNkN31pTjLZ2TwSeZNcEnxTrB -x2Me21ieKzFWvhpKk9JIPVD382oJ20IipUZeWxYCUr+BzWq7LMp1VmZLTB+WL4sGT8ut5t1hgJDY -OxBL9uNXKJYxMZkwGzwYMxBc7VZvu+HblhpChoxAg7fLVT5D+Gg2yH6hbC88P8AWb9HcuMrQU3S8 -uUM9fmhxSJlGAA5tYHGSxuemOjoxTPVH084jaCUei+uCSqiGpZRgIPPi3L4MGbiqJVL9SGjLdm/g -N8jFOC0gBu8GeQlkOhB8nUeYpCSR8qlv2uw0U4KASA5mqoZnKVWXX/iD5zMBiPbcl8Tw1zS5jjHd -XBegwS4cz1t9s8IUI2eBhkoZ9FSw0rAXvDBuDoHdQyQ1sZ3Z55IwwkVeLOS2x3AyGVpIsVaumteJ -7aKljG48ei/co66LmgWonsnJyC6dBpCllHkSFMd6ZOF2p8OxGOBNV54Bnoox3AC2+w4l7Klah4wL -oNLFluX8mNyMzfVxy4+BQQxdFDQDwe7UYNyLgvxwhjTQN2M2kTycRqedykgZyFh66h+JYVuPa4yy -OB4ieL4P1uGyNaPDW2x/SFfTuLpKrBo4vpIxdoQdnkGh7UTM61Nu+lju9JFatuGISdsSVid5qa3u -hqFo/WqZEEL6pYomx8NLvPegxq5cNbyTRIADCjKYCp8aTIJMng67FdWoAxgOgW5dbeq/zZPEsB1S -wK26DqnC4jVC7GCOUksyfJptgJUCTkB7D2VZteZUavf4QGRLa0silAAcID+3+fo4fhhu1B8dNJJ4 -oqdydAnhRJhbkLznFYh88/bHJHMbcpcGQnO695TOuLyD1MbQo61AU5DXsfDynYNXS+d3Mnjd2Q84 -eGmzM3hnIfujJ8zs5IsO3FL8vaB2F3uTY8C195mwPRP1ngy1d15U1bOYWo3PD5ogZ8CK00Yum9sV -BlydzZqjYwSNQ02zS4mN/Y6jli7r+Ca84tFNomGA3zntmy9nfzE5f99dyDmD9WHeh0W8tALKDZsa -scXhvhoK3wb4QFiaQ9aGqBT1lmQ9zJjOsO8gORkcCxEdi7C8pWhs6DmHtq3hBGOrUhghXgyP2jyr -F9VNF2r2unVkT4FhsGOHYJHHL5ivDDy8JwTWxP6+vEF9oAe1EyJmH6H2wgeUdl21X+wakEzne43I -7ksRsCrnU4Zw3b1Ugcb+HZRxb1yHJsyH3d/M3w/b/q7YhwmVX8XeR/Jb8mLzNxKbp2slO2szPgi0 -LEuq/g59ZdBlOmHlxS3DUGKnI8OddYoYejvsYe+WFsRD72FHXES1OUy7KGskpS8yZhXHgx91/ES3 -RZvPqOAMtSf0vIQ/Y/yV9DZ8CSp8c+W2jJNSUDidbTMCLZ9yFfIhkkuxcjR10G0Jo7xLLbQrzpsx -EnRiq/cg3N7EI8qbiOb7aedmRSANhjEMHNyaQZ7grdqE0OaWwoezRb4iOvQrHofnwaj627WyOTg6 -pC1yD3ybifjCxp/8Ek1TguXp8HR8MjSDGtKghr/81MKSW99QPYGXdDkLvQvo50OPdHlhTq1FOupo -YMBJpASPyi2BXEJeC8PwVH+EZKqwFTALDB+MP7pEIcGfFFM2HSvLtdyePEm7qJmvqia0NJSpedZs -16AM60ir8pi5W26zLP8VY32G3pfDYzTSqWDkC7IwYu9K0HGoFYB8+1+8+SN0J7Fum7z9V68f/8FP -ftI5pIR1N1KmsMFATvxV1AOS9AYSY4FXnbk/U99xE0mMBWLKqUEFTSzpV9A5uoYldpgGyzWgkQjM -eJRGig2FceDI/MWaE5ioHM+U5gRT+UhZa11RTOSSEnQ0blCU0kRLafj+ziJabGsVhBr5kRt/2ktN -fNuTeomOw9EYnrtjG4uXt1T27sZ5QU9vR5wX6YA+7HlxG7ZLSXppeECNf+j53XWu3R5Fr2sOp/su -K4vVKuMkyhyT6TrnJLI0G1aq7cKK/OvfZMOuE93zD56sbI7u9Do+c3/cbHws8Zm1FR6rxipGMxLr -trwuQfSN0/1Xk1U3oo3nwVvIgRHdLw6b1YsGLnScFn9y9qBBTWeYKpJmtROWxDmGhRYnG5i2k9sH -t5/GyCCCvbG6p/oFmjEX7XQ4a7pxd7tTc1QBT9LoU3G1zW5xpQYu6pCR+hbNxIld8vij9NGjx13d -4jemvFv8uEjDsfPQMxd2w3g8Hse4K3KE4vT4N55OaW5WUlJg60oeBm6iXqaPPz7pujFlxISOiT2h -XQ5qEu5VPlKVuILP7CS1GDXyXEJJqpuQ2veJ0882ea0vQDbRTcFBcnRMXEm2Qd4rmQS0cCNB0bqN -1VBiyV2BLg0tLGY+SJ+jk0RbRSXwnVpSrVocEjguojmSwD0qjw0teUrLJcEdOfUGjRO53iO1Q6ga -lAdD+XKQRzbomdHj8c8w5YbLcI9giO+K/MYajMpMxTkgZSvSW0lqHhM9MNqnata8t7hh9LyjgP/w -8vRnJ/apVaMtbGzWfzt486fKNXM803561Wrx9r98/X8+69tSxdVqQM5C4kJQK9dOOpgeRRnG8dnc -YcswWQPHC3Sse1KVfsW80w+JPtOheDerjDOfDQYoFrZXMEvLq/zW27iJUenwqCxp9Z7H4dFRR626 -nbvx8iVWQT9TpgIl+xfhH9X738LEdwKy4sPoAuMjU6Ex73UvLqOnsvdYQgOWpTxuZfQU3Tz4PB1L -berq9k6zQpMqhhKq8tNbcemRWPK6USzC1SW6z1NksLKcWDy82EKj0YcKlA+x2lMrFZwEtYrq7Qqg -oSSt2Bms0XdVsaAT3a3OwsMeSSCY48AZChJOuvAk7uifUjwrQQNjmzJw8PACLd0KMp9YKTFws8/b -q2rBY72klS2pcrhX5BnZtq1QqGIfqZocpEpsD5v7klaSlaqaMmU0OQf98vNvqBTWUWV1gotBaNDG -IYqABi0yX2r6iBzeAQFT9HAdlZ/aY2QQRwOShhrAbzUgggVsy8I5EGmu+V13KmczLAvNUOAtnZ2X -WmLvAqAhLnSd30E5xirA/CudVGvEDJE6gpatzjlmFDVGd1qQeRSXxdyd7+jmCrQNAwoGqiWE+7Ms -K6asoP78yjRC4dhgghUgWQ1vAWFzylBEmwuhkDJXD8SRVRMTBuXEgMrAq7M1RR97mpCzL29XnNlw -VVXXHNdUd8sNEfzYgwZ/GiWwT+MFtQokVvjIDqsU2A2dkdpoUeUN5jXGSzqY31HcDqUHDLoVbrHA -29zYoE43UUb0goczgs8KR7gT3lHGZlI7bVw+lQ0R0QZLtikWec1eixe5pJKiaVWrakUmGLz0urpj -DAfJS6JoLWoSEYC8spL3ooy3DMGWzalGtluiO9kjbKF6h0FcFixeaBLkMb7KJRaZzFok8nxeLnSM -/XW12KooeOTMih+oIS+bl41p515diU+TuqpaAo0wLYoA/Pnw+mbhXwFCSwuLR53a3s6hFjBV8F/p -SlRAfxtYcRZNqA6NGmtz6kaFdkJO6MoaG2fQwnkgHnQgClFPU0wLanm4iRL7czIi/zJ3quCLc7fO -YrMdPo4uLsCdrgrg0LDi7whNzIFx67BbqXNaXxyPV6rzNMUNxx910SvgKj3Lni8B0h6Fwf+OvMZS -3eDNboHcF00TlM9spDlN7gX19B3e3UyWVghPVRDGsa4rd0a6Go+K++kQAnIkJLZg3E5RnqHOUypI -IoWBGmGVuk/Hao2dd23y2EhvnCt+7aPUDtDhE14ovacF1JRyAycYY8k8TbrrLnXvMXtjm4QCrVkB -p8yAJahl4KjQlEHGY9WwHJmuqmKeW2F5LErxacQ/MZG6/WnZneG6B0GoYEr9FI/aToOtSImzk/Od -sRc5fTFvLmQxR34tdUPNUgpVyuJGmNOAjIBfHx58J37QJA/qNNaWRGe4linAXp7K5dCjjrmOq4uk -QC/IJxT+QE1TEFmwexsVbwBoOaX02+WWPNKQvNmm4sA8hdL6VgIF8wFFukVJMSFpWasbT1gbg20g -z0k5UTr2JSmlbZ0p+XisdFZL1dKxLxSqi0bdI1NZaDGGexfnHBXFJEV6p++8+hWOQC5ZrY4pcA9d -YmHbEGVO47MWArvp39CwJN3Va8fPdKXEnUy//Fjf1Z5G8ScI3qdxaGtjVr2v8LwiJVQ0XQuKp/BE -Z9NIsOMUeTA+TtIwB+Wg+JL/sWtktDXf+xoY2SDy3HJKSv0AGgKEx2tFL7bJovMIc3gT6Nq+LM3Z -ZuJteU8q0Akr7kEEvyZpL+HgjvjgVbtukzN7Rs/TfSQBoO6eZO7l8AmWeb3N57PfycRqpJfAMm3z -SQ+XDBhaEn+STd4ZvPuUOHxHWrQ2MkQ9lnO4B4e+DBOBDvgbP6hNoPEkVYaK5aq6oAfIytnoYqdk -7dCESsdAmNe5Vc36UtkKfti5CDJA6J2hP3Do/98e6u4twhkrSUvWVKM+ax47aAicyFoJQVwnz3/K -CNqxGXLa+S/0qFIGjosFI9DqJeU3tYtvEy3JSn6KdzHrgxazFD1oJMKPXdFEwVOnTrngIMkComxu -nAj3sQ5vUW2a3lvCDE/Mm0/IEfmIzxfm21ayYuH9RrKFGZmjCWYcQMXTISB7mwu7JrtxSNWIH3dQ -Q0+7m9LjIG565hYje9D/w54zMUS1qTHq4Nfl9U5vas0OZzOV03a2yi9b7NB6VBfLqxa7100fkBXK -ET06a/JQ71CiVwe2KY2YW36/Vmg4U8aNkmYCh6M7mMT9Dkl3SWjWqiKA1AJ+Ui4OWbxQ7NCFq0jA -u/pDMYxuKRGFOfJogmJYl7YD8lYfZdsQ6KziHu0GA2QJBVmzPti7gq3CgRXsrt7AkosTPIeN+XCS -7/LY4GOgnDiN1VR9WR8yU1/W//9E/SiTBGjZNUeDIzRwvCmz+s4+7ZlOB9d5vslWmD2V8Ezm/0ZZ -guHTBrN94U3fMvqtHM2A6Au0Bj+TKEaqs5gKfpCE9VjuRfkOs4FDueQfvVKpFPtuXIAWRCnSBtp3 -kiF9UqMneoiqupTFNgTrQmaXvuzhTM3H9ADiCWzueykoMFmmU339Mg4j734/uwnzfhuTgfH9thX8 -YG9Ov/tNZcB2XiFrtXgNRaWyGn5VBJbDYfT/ZLEQ+k98meFhZ49NrQXxanvRV/F4Z8Vfb1d9FT/c -WfGz4l1fxUe7e6x6x/hgZ8WX1U1e94DaD2uYD/Ac/V4YAQEcZAT4Ju2U7WUENMxwS4yBbun7MBVr -xe5dsEG2g8DHIxlwPxs5uD0aATQoI7Ha+33yJRKaaZ6+v9DMI/unxd+slWJMWU+z1QojPh6kAUtZ -19pRVfvNOtaJkIUq8TDCFtL4+xov7rcr+lBMbV3292wGEV+qADMghy2nXJAN9MvG7zim0G/txXhZ -xhNui4f/XWD+nOJJ7Mja2Y6wm3hKa6Y+Y4P03/AVyIAsK5cjkdxci592BqdXCMsBF5BMa538L1kH -v5m7SrNe/iqJeD2sPFigjQ6PCxHFbg18cibVzmkAYalfwRtMB2fNx8OpBgJk91EcMnV0NJOsn233 -XN/SncUPmumDZkRGSIFxpCBID+qcW/Aa6OH7JjFAxinWPKOeehxeIfp1Gq51z2nFevHOyTQtBybV -wuGHqIT1T1sQa1THAj00gQpdix58LfYgbNGDscX7ogydgXajbHEwzt4LaVRpsQdtYfth8qBJu9ZD -5rO25RCvGARU6UBuxzHAxNelAfh0Z75zNzSLhYZde+M+6yHI0y5D+rFPUsXMRDizzkKYfFSiVGW7 -J9khZLqv9WC656l7hN0YY7z/VqI8f1N+R1ynBllzFAUO9FgI+ktxcDpABpKiv5tTgOAGTKWZm/Ku -C+DsPh7bSyQHKee/kzP4zlzKSJOu+d4ZfGqdpRfsRal95tDxJm+MD7GSR0bsgcyRwxuKEgpjbu27 -T3oCklgdsHi4GuGZAMarmc2GfH4XBwRROdf0Z1HV7MzljqM8HIa+bKanU4nF3Yza95vtH3a6fVid -PDtk57Te/544ABl6vsqPC71K2aEDoNuuVsYFg2w/6tCBrmkcdO5AJQ/xAaFgHkFmgW9Sp1yQWRxF -TbHerIrLuyjm+yWsc0Q3V0DX8nmKntKxPQcJN2hwYsf4iKkWYFPV5rDDndu4Tn2z5SHy/bSbfnG8 -Tu09Ojv96eT48bk1Mkq4Zl9UzJpIj/ITq6rlteJyPepjv2OPahNlCB+swc6jFKuD9ICMqMwJg9d+ -fvfmAk3VxbI8kKqh5CFU/f23wL1nJqFZBCLHPzCJ/r4R8rk6BvgVcekoxRmNca2uSrDpsnEQYJRy -NgSTv0/XXm8x1XW12O2mBV2cu+V3OWYd4JQFLYR8sgLbiu2g9XsWCYQgPyuaeVYfdL4rRf/pkmSH -DtUtepz2AwaI5Q4ZHTnbQtldp5/0voMBeJh2io2xJxk/uwRzBAMdD0b17Y2Wuh13nO90bFTrYfBM -N6IUJEU5oTQk/vp1LRZeNXbjxcuKTbuotq1KFseho1AiIBUPiX1uuT/n6sKja5agMc+vckz7LNim -yLkybHVY2zXa8GO+NYkXPOg7hlv3C6i0Fc+pgEVrQqjkwYydx0iMcqVdtinLuMbCqOsGzGWD9kSi -R2NPdPhB0LC4e6130qc6d0/peecG6Zml7naoKjDPwW3XMlz66jVTQF7XigLMrVuJfBQpauihVqT5 -o/f/AdnyycsX0aOIEhFGmwqEmAYevn+DRI1aUtUSvZxZNVfVdsWhrSSFw0QuHeK+0CEBISxpI0be -H6cWTRyx1DVcVq00AUuSPwy6xl2BQWL34N2Fhkn6NXxMJ4eTvUOKcnfN4kLfh8bUbSafzO5F2hZB -Sq6HTqpdE76RUIh5sw3rs2ap44acDBOfSEd0H5nCohXwN8OLWiSwIKfvZv4eUo/s2Ye3XPNFgUHI -ibfhzfU2WhScNoQioUbRq+1yiVovZsMNtYfX21GJFo5jXUy4yC+rOlfCEr6ULBzHx2W1zpbFPB2G -1rGMla9WSK6ZdbNM3HziLnXJu+4lIpXl3RCUm2nZSTd+JEQqiU2hX049mrQXdoFd1HmkwtrrRch5 -GNUeSdN/YaUy17Rwpsx7xu7X1tDzWKuYJrM2Frvl7BveUofygdVOF3UH4StYt5hB2L5OyBIXLktS -N5Kh7kWmJkcCKVXQiwc17Ze3o9QJAH6r5+57igIUdYU3XzsiuOS9cxRTinENWqUdxQrLYTwxTFoW -RZ98ohxA1X6e9sgJ2IxkNKe8ezxz+W3LpuCJaceTE3xzMpqboJqjNrsK3UStj9jR929ZL71tz05/ -JhFM1M0veCjSFgp6v2O5Y/d2EdopfkSW7YsFg0FBN5JpNtB0E+NlwKKczeKJxByRq9Am7MVl0r3w -8VP9dhl4+5F+e5UEwkTFFGKF9TCWDYfQR/QhtoUw/VT4nrwjbpuk3YfJpfj8Yz1gnidemUtubqnr -Yoydj+0SBb7vtI3nkPCQKp+4ryzG8PjhRw8/BtpaVVmLDTAFwrQNifW49W7VuEwpIWoZHdBFVW2a -WKpxCdi8RhFG9D4dRY/Dbxh4uysMCnSGLcK4z2kMH7uwxFf5alXFZ/ieSODK6TVebq/5PPaKsADv -3v5Xb/41Bl+hnAB0Y+DtH77+P9aUZWpA3ykDB+7oK2LFzHAo95MbS21EFkGMz4k3oDEMopUbSgd5 -UY3hc9hA16jvFcAbyo8eC70DE2nxzhEFcKUXQ6SARGcVo5BWSObxiG13hIm4bK0IbFA0lHTBv05e -Bi8pure6qwbHJY4v0bBpZ5T/0Wxqvclo7Zo9mWh1ig8rTKlVi/I1mK+mv+qmlMhn3s1vRmXIkPBF -1b5Qk5gvZIP7+uuvI8Zx6gtvmxtjzeTU9cQZKXDhmOgtX1AYtgRKoiFlc7MtFmJJhk+du/3UiLoK -TAOg9BLoQ9jeJfSA84So30MuVFHuyquhhW5ObOMN3wpXxalwyDaE5mqMWKhDL/3wmFrWmwMxBSU5 -L89SY2q5D1M6rY/CFD3QpP6yaorbl5jIg5fTGD9jBjeL8OdXgEkhTYy4NeJGca3OpyceCudXnOYL -cd9cFRsM/GFCe1HALmLMlIvHwZ77LlpndxjwQ2K6cEiZDEN5XuCkusZcfoXqq1z5n+P+jcJbBx7P -hWi+rRsKPTK2R6E/AyGyPIvAFYsE/xh8L9VbghheC26d6ANeNm6dMYZjQcG2gRkZ5tNVtr5YZNHt -JLrlSUfx6RqjZ05Cl4a8QuFbQmEqqpoxzSiJr7ALjyJaeA4lHVKT1o9d2YrbmGEssusdS4xPB2F1 -ZcC11xfVqpijyHntLjQp3AuN6mikXFtqzsutIVlf4+u2UmlJVguPZ1OSWlwDkkQKs9cRIEi3KsQM -U5YNWC9EAg1uNNKXhSwHMBi2A5tYybKLplqBXjg99RcWZ73y8GXCD/ECY7wmZBLhlH/OKNIOI1P9 -uRTUOz6BmgfFuoWP/H73J4pPJpcFmd0kgSTRRzwmnhUjQzRR/Cim6FWrm+wOo5VxE9Sqt6pXRhV0 -YslId0A3K8A7VvSvy68WfFwotuhwMQ7tT0VhHNuyFR+pfOO52WY1ZmpUCY3yDetFSTweg/CRfliC -JJBoaEeR56W7fxK4gw71i/5o8yzRbty9uavTMnsH2rGIj/jyNNLbNNARt3n22FGu8Jnu2+WITu9q -v+v2LtuK273awPTmh/1zsx4A9HAweP7qV0xn3DoLh7iv6L0OBUJvuzMZtwDJtB9yM1YsSwkrVdUF -bexsQ7jM5hQbUoIBcyguWm5MuSg8cMYYi6mtMq6vY+660R0l/AZh69dFQyFZWIzgZ5xy2mOrHNO0 -QmYpEYZTcdiocw4BuJamkOvOZggY6HaNDlwF5Mn5veCDBcZTor26UfKB+p5OHEd/9OhrfX7ff0hr -R4nBmhQiKnQ+25eseGfzRhrWbZvQ36CoEKwhD0sVcZeX3bPPv/zy5f1bX/U03zNoB42Log4hUVWF -SuNXsxevPnvxVWLaSdLxmkwGTlM4wYe09dWzv9zbFqhjTdvsas1qwK0akgP4EGwv0nxIP//ibxLg -uBZ8dhQ1ClaMbdnmHLU8XnAqYgxsiitUh6XlRUo8QXGhsYGfnkvgOVq1eFdP5FkQHDHcH84YcIWq -tgTIFxSFTwxIHEbPrnFT1de4NeuaFMcwu85L0wToEBJE2oFNb37zrK4xBJ7evjnRpVsflNuKG0EL -vFxKz6wtVbE0CkRITi53ghpjTcOYd7cZFGpQHbJqjaM3DWYkvgFGIymJM/wKnHXTI027pmeVajKk -dlg1aGBWNLgpBinqZSmORkybqNGU1Q5pu7QZuuAQjp4+3Q23h/GU/JMzN7Wm5g/SHXNVwM/8ZpF4 -CVE66LBjzqLgwbbidF/rNDygBUrJ2MnRiLIaiQY7I1Kz9vq3KCGJ0oqkT3ElNxSchbRqjx6Hu2+H -DMkxtTzO15v2TgKgN0guep+19OHu2as9ULa274v4d5U1V70hu/Bl0qMpzGb5W22AoR3ctrmcyqmC -51n5WB5z+b32AW7mdLzCa3oes+O2HnfeqXGeIuU3j12+1wuwSiaKtlCm46mUsRtYtfsa0GPGxNFm -oLoNZAp6o+m2Yil8pIwDYmIqE8NjDLlNNERRVZG7cOpeEPSxtbiXh3hSIzVIK8dYIb0Fo6JW+yvF -DKnj+K8xFtzvfIfLsDnIuqAUHZEoxjGHNNoOU3Q7S0wNQzXj6OP2mCyNfF29MxkD59PTkSS1mkla -Eftc20wd1sKobjgxsJTNlpXQBSXznQLBsb0DNNexgxu7H2WXGUk6E0xDT7mboSd7UywknLtuCLZg -bmjRSxesV5JZBJrBIQatIyGrjDiuVag0yqgrtmMQ/yMpHoVrmOVFdRMM4xIkAYdjz69AdEk+/vgX -MgUpdFnNW9yNT35+cjI4zHyjUv9cbdtiNa7XiHlXAwzf2HOn2/l2yLWlfhvMGpB9uA1gF6ZcLO1C -zw6LEE5erz1IDt31RjCiPYFz2K0XPx2CPHK1La8pk8FPH3/8+Be/CHOzq/x2USzFqxGbYLsL5zHA -eNEdm3FHUQlqLqIiY4uYGiCjIMMhvcjSBIP2QTUsOgZprrLTYZgwTTkq1t3O2bNyNmPIYL9QNVyi -wadW5hCoZVCbJh3PxsTV60ZRWLMNiyWfVRjdGiP5RVfwDzOpiy/Fg5o6HUYPNJgjO86qUjkoWVVc -X8Q7HDeZMZ0GAoNssZ1LMj4mmlzSvtBOUDyMeksoGWt6CuiNVGC7WWRtnkBj1nAw8dHK9zftJldC -GZ2JHUNK95kVYcrYzi6prnSYa1ezUErBZbUCUQVZtrpemdXLLTv0U1N3eD+tqLbcAHrbtc1kMvCG -l00eNdU6f4RlHrXVo+wRLR08p3cL3t7uEDMpKningvfjVCjq4D1Y/8eqi9L4wXUUm9rW+cH1VGVa -JW1wkwNdpKuRuCZOQx6ElJEa6kgPYOSANWJ3CW4Elu/FHZ64eDLHkNtSTem6fkNDR4yK1auYwjvf -dDaC2K4vhSicDjQVrBPmB0UJ3LdY6PD/7InC7jXXN7s2ow06FF3fjJu8laj0iQuTi6uDsh611OQZ -jeE8xLrDQc9ZV22aA7ZicvNtyZCBDMb3kRnHPcdBDFs8Zvd9d4EhxHrUeJkUUMOlnDstsZCAmiwF -A1ZXr879YOH2O7ym6izWQJxwdz5UVU3JZha8kk2+iUdR1xQP6yWgLWvt2Ol8+CBR3TQPEmwG/uj5 -b3ySEh6OEfaNXmVWkSR3hI8e121yMp2QwHCDFpoYC8VibPIPwbxgA8RK9akCD0x8uMzgB9ZtYiwM -bLXFIJCkxGNnyutrFKeRpYPTbV+O1StXfx0XeJWWPRiSgG9TAInRFMddKpS7xDSAs5NzX+50mpBp -723EPVPhJvGmb3qINKtmlN3AoaZ9RTg0Ir02wktLYAozin4wlOuRqjeuMWcHLeFw2cuIHJOOTye9 -24nDk2XNm+9xHGYNveDtaxJzh5m95ayYnPdBrnHpcNj+XhWx9DLgMOEg393bKBRK3wMR/XsPUlFw -A3LBNkkHDL8g+hX5zGT+8NIQOsnUkHMQN0O/BxoQpQxaraIYq8WoNzimTJSPYeGDbDZWh77TU1TF -t3hohLezEHzyssIY9lRE8sYQA5WbW8hJ8Cip1+wcMNR0GYVtuTOnAYo920zNMsVurFNUu5ZwtzN2 -ZTDeFfj83IaFMYpm2CQG6GGj8O0CWLegrIDoMZJQDjzuID0+HUX8L+yNofaUooGmVa2z4rxnI7ZG -q4v2FZQB6oIPT3tWmBuvkALI1GTxl6oTP8ZEROFOxDW6c3a9S08/QloDzWtbar8JPqHPKX2fNpOM -u7zb6zZ+FOB0ppCOcfWINvYuiFBo0qd4qZ1RX5vFBnZPyEMkss1gx2vo0JPG+TIYHoMcKnB0rCQh -KULnU6arIfk0ruMedlBSWVjHZKXz/EGw7n18aqAlz5gUeedtmN1EHVOOsM+pyBSgv4eO3rC8ZSic -V2WLjGiEDh5NccHWNRBNVMowhhueUWJEl2hBMtG1sL8dxkD7Bk2x8l31WX1eYfZVPn96/sWv8Wwc -JCR4nPbLXrYMtMMJiFDkWaG8tKxXxWqxrVeKgmgX6O5xBR3k6sNETIdQrBJVOQ2dg+mQLbqU5yKK -mKMcNTXeXGmctPOhDQrNGztcuVR+YZy8bbnI69Ud5QUjmzEfMAboT0UVwhNnSjNpXKMoAXh/f5SW -i60VstNSEnCV8w+X2r4OqQvbKrjRqabFowYzPgEhBs3iWJz3XzwLpgpju0PLf1uaGUXkACrfjnFF -MYvabiht9aLPjo1InJ52DdjSq2XsDpCCdj+S0lrQGIflKYEbS3ww5TpeqorNHdm78oWDrV0aNh8+ -zJNN2n9k67oyHuQtWTsaleVZIzz3Vny3urGuyX8rv7n10CJVw2XHgGHi3ClsJpjfWVC+1zHztGe/ -oUZtF8Bb7cqYpCHlwWo4SBKhKbodUT97musjHg2mO2LrRAkJyKUDf7kSo9Q2RGu13NPJU/IxuGdc -0qcF0mK73qiDSMyjd1GUHS/KTTG/NnypKAEmgg1PDZGX2IB55uKbnebinYc13OsYARTYLgm8+9ty -19dm7/0woCOIOfc/6BtPmHBRb75I6iAocK4mFIddp1ZnL/LSkfdOD0HEU7JJOxvJxoDOl4UZ9EXW -ZkqwufEFGypIRcwEdfxW4wuyQWETwVxptiNF0VzctXmTYJPpIRZF4/iAWT6bJqL6wz3nWt1u8b5Z -X6/3h5MQiVhhpwMqdQ9Dg1S1O2wrDeGIbs8stTktL+cVqpVJ7/nJ2gma2Q18IbfDXTAPpfMZsx0g -rY7bGedZVaBo1jxwfIa4CJfZr37qKhZT9NVDKWADlu44fT45xA+S87PTkg4Hs9WuiM++fvHqdUgT -w1uwKFQvCor5QGLXI2hQOMHCTpXaXuEu80iIehxoDQ0FqwzWVaF82EiKQRYSIuA9Yw4HKwl67sgu -c4gZhIuyY1vGmv6xMDZi5eysGCUXOoOwSpOsBR3CUjrGIdxVW7Hb4B0S/yyL7JB0Czj2T8BKLW5y -guB6rrPc2o4R/QaSnezWs1hAewGLhRagwkQZOO+wJJlwHZsz7RM4NmpjjFO9gEPgWZpEwEkYp/Qr -o8o2IqPzhfSgpPBKtTLa4SJPlwkd/cvWYQ8CZPWDQLLaDUqTt0bhGbHyE1KjoVxA6WGHA6U080ZJ -uyO1Eyu9UbeUWAmYU0PFOMVxw51jHewMj9XFYxUfW26xxjlVYjuhKZACK+DZnelZX8Vc5KiPIvfs -09Z1z90AvfvEwy1W7dgs8OH+vF33azRBEyC33A3VpDn1iy/+9snnP0RvErgQaSM1/Vq38gLO4tYN -PMs3q2KPNsvzucJcn74Hs3vUvFp0r88cdEFshzuQ6d25LLZis1ivxm9ZvG+uivkVGZFgl2JfOvs6 -VDPu0/zNIJQ9zuk4tDSzQwwR2ZwExHubIDLXBLEv2SxnY5N8s46N3GqiaQMt2JYaDjyBPeWNuoyt -oH65B2qvt83d5nqp0Qf875qiCYdYl6WKvLxrr/COSja/zpa5PsTA5NlkM0KrtbtjcbNj8qqXL9oa -pnPaY9M4GwPPx0PoeruBrXTRCPE0Lbq7axLKSn39ery569gbb65gYzPnhcjWaAzH7BCFERxcQeor -E4WFLNSR4InOdskhCDkwhpBBkaVHNJAqXoR1HKqItsokQqeXSY1hE5qcw5V0PehdtAVVEUf83WNi -wJuF27xPoSkag5lEmtX2pkOz2blQkYAUW7OEAgdB2nfxVeOPW7jfQBS75kYOc8jwBoqqh6B9p/OX -B57Xs1nasGBxCwUVzV5yrlVZjokSl+WilB59ILER4M3ZybmXP1uiUUkXcmlUFR8CK+9E/+KzrpJi -tZ5AAWdXQu9Nc26x85L4BhcBJ0Z0r4nzqQWseLKU0pGjS9Rlmy/Rm1rtXqCqk8Iq3uRVc6zOYqkJ -T9fxrogDGCSm7L0N3vU95yG6FxaDoYBoIA/qaL1tiANkpRoEBcOhdtL3uzketoXu2KDJOVPd/Xa1 -+ENqiQ3OOxbS1w713OvtYCQqm1Cvz6WsLYKvUBJP5tZQp+MtQ6Lf+PMo/SjeNnL3BGHySNOy5XT4 -OxSBQW3nrEvO56D9YXGJ1UN7j1PnNSaZ55d0bQrmcrNtH2G3AOx2QxMEa4TLNDsJyVKfg/RjhMov -vnz2xWvPmq0WLgi3smgZZ8ORa9zo2Uy6yJsMwjyUNxq936eB/UWpwTiq3j3GoYJd9yo9biftG921 -a/WCIq6Xsqpj3Fbs0CBn5wFDf7bOG/s8N79tp3GsLosfAIRqgf52HLJ6PGIa9qHDT+iERP7aaqfr -8dnm9jfVJuAsrKYUWhkPzaHiQS7+RyhOK9GsmYNyPxJhaZ1d58hs5fA1/yFn17Xi7RoR02GfS23H -UV0qejMPYsSims9m8XmHjp2jMWcigPqHvedX2PFRdANSHfml03pG95n2irxyGny1xrRVfW5FVJmu -ZtJUbdCip6YBAy63FSzt+TUpctS6P246ap1SUGN1sTvAJ/HF2fHHk3PsK4lhTHPKt7G5q0Ieo067 -VHfiO8nRQZK8taKT/08YAQ2VrUOb/YtzTEKCcl0P2KZxffUp30AdZ4LCNnZ3Uj/YO6m9Q398Pjjg -/kbTWLSqb8dJM3tsvMTkd7TJma6CIncTFDR5FyFQQtEE9AIR8CQ5+CBM4zvMijvv+Ju98Ew6vKdX -NC2Pq4wuos1BfKrWkYZ8UaGC1eTbRSUKWc8FFid8KsfzRulM4SDIdvRiCl7+tAVif4BMOYcHK7DO -ZDAGIN+odEQtndKiL2xBz5lPvto/A2EzfYACjNHyrkE4LXP9OzTXb9AePKs2bdNnf8C40RyhkZwN -sZEtxanBsDYYJYP9YtS528i7kZNLWFS+ICoXSyj6L/JGbq2rkaPIpg7yQEbRGkNRviOhTTmxS4xa -A0uD8fDCEhyB32wvdLt8ceslO1C9fPHyme2A/Q4JItvgnFKGr3eWxK1xdhYzftj93n0MHIIeOw1j -3/iMrPBnml6QmZJ7D3XkzYvlO4l94QaPjXOAsDlF5dmWaGLO3TvIqsBNVrTeWVvgAJMb70R8oNkP -nkBqaPaeQZJm1CIjP+kaOsKgwPgCe4MZeBAceHcIOI6wrg7W5usF0uKY2WydU1jD1tae7pNT0Z2p -UfdqOL13Fid5kVNADGaTrM3VO4xzmRMQ4rLalgvbMCe2fl4XrkHAcvl6+eT1X7lO0KTEkybGENg6 -gjt7rVan1LKEFS2egbRnsRLXXjVsC8zQfyKrXYPdPCvFwkYjGIkVrtHxd53SeI4xAe5QEFuAybvI -0LEQW1AxOtBWSeehHEUoNH6MIo2x6rAKlp/fLaFwr97nugoHjGIbJklzlz3sFbj3dE6fvr3fXVx2 -6NUbJ4Cdl++KuirPYrQUx+fq/sh/jIO6TRyzaFJKS5zK3n3YsxnSJKu0c6GrEP1Bf2h+yIWd/G41 -yK/+06vXz3791Zdfvo7P++L+9EsgwbtZu50tFPrO6nwM20QSP3hFsH0FsD2IRxakYsfbzRHY8kuR -N7jp8xBSsgWKOmdxjFuAM2OwMJ99/VpPmhBhRy/tufq9gxSgYUMKk/BcMVhJHI/SQdii1UMNdByw -WKC4AIW4pR50dxbNbWq0X4qqx2YnzHtILQab6SWsQxedV1643mQvuxdBXsonm3Ry0A7Ra8LeyQh2 -KiFPnj599mrHVSB7GdgB52X54c6DXtWo/q3z9gqNwfw0dW/IXVUYQr+meDqT/oPaW28h/9WXv35m -LeGdS9erO8S6n3314m+fDf31gW+YlP2xAXiJuFBaUHsjtN7IKI8U1z+i7TBbSTQ1bW3E0BdI3n6U -OF4KbhsSUm8GrzfIMTyceUds3E4MEnGOMilskeYkzEFPIvI0FOP4Fhl82zZb9M/S3kO2j2LYadNa -c0rJkhZRdMLP5N7EzdjD8JBov3IdHvEpSzP4Dh1ydskxLy05xgmDCTJ73lzRUfIBqMGTQtA7FR6q -bU2SW8++LgkrZOCOsMog9/g2yEuKaTV25jgNUWLYPK/xLbiim7JTFDalfRX3Sd57iJenNs6z63zG -kXahD1mlsE/V+WVxOwXli85sjmN3QkaUGX360S7hFujkeoYn36wJnP788S9OTtIJafntTRUtsrsm -NK2gk7zd2j4N7MKqwgEvaZbQ5p85sdZcQ1l2W6y3a5DR8DgZlUKpjedLTbNds8zJd+G0kphdYsM8 -9HE3s3S+oehttQUch66xwVuRhwDClgAQ8PAYK7p7pZJ+OYBJ/72W96cnJywfJR3AOU4Cd1PIURoL -EC5VyOVty/F6SQ5J2EGQJHM5dGMcpQ7AJCCXKv5a0G4LDV04uRGlnXvEejSpQ5KL8gwv7qk2znuj -PBp33x3y3sD1jNy2jBFFSYIZITayKFCeFnUHy0m0iACZ7BpZ00Ir/pkMxxxBQWXiky70FeHF30Gf -WChzry4fhY7GuQ1v6oMHE1Bw52mOAxTmFeAvI6zpCXlHfHpAlASsN8xwg1O7ZVJWA2M/WUYk5e+u -pdeHp+kP5T0b9pTVu4B40wcaKpjvrPOsJG85YDB02W3L+0+2BCUyhGlNCFPB5+QepjlDRVx3cJB8 -yN4WNm1u0cUX9/BrvrUlg3Gmix3+PC4VZa1D44wtSt5gj9Fi+u7w8I1Y2nGyxdGDIPFUifXdhoKt -cyRFjCzc0YhVImHV6CiKrSs2obMIVdK5ikN0hb0dFEhANyG3G3oqI8sFGp8x3maqVmiRHvFRECzt -6ySNmqLdSl5mupCgPJ00sjlbUYi0OYwZViCESnS+m0LYOhO6NAMsXHJ1hloqmmtk/U2ekyx1lcO6 -dEQo+Negwp7VQPjPKebjTdiO70OlaE3cmmiMSTGGBXSTy64caEg7n5K1uKb7omUBwp2VOotbTMc9 -97UVFaElkWZs19Zy+Fak6UEi7h0QZfgeNgdOjAJtL/FuSZ0ESCp1VjZMak6unJZwMvAEmMnvY0th -ozs8+2SqhKLomMDpUX0pFitJEf1M4iA1vsUgnKtL5d0td0F7S2NYTj2ph9URZd6RddGScdEk7ePj -9hSDafbzxD4eThPaXBcbR9Dk83lsLV8cpuD3k5siOb7hT2uPVxoKerCxNXrdcUjxsori1WV8/ykQ -91taIBwJcRfooNxfVFm9oKxZ9XbT7onPsaOtCfEOAJ3O48l3x8HLKPo7jlxA3/DwfbchZOAJOZTB -wqrUwQLGyBdvBtv+8ObVs6/ic5vFQUvb21GEQZVXh1k7wiJUf39fPEFLCvYVCmq51yZqtRyLABwb -fDT1PJJT0y1ZRcxGyIG/6/nZBH6pgDfHMR1bwV/4rZruRyP6zJd0W3bh5HAXqL98FQDaYaahFkUE -SACsURRsN5GGR5EfvjCQ8ycNdO/r9FslTHY0bl9H99+rBLr2lWwN90SSekloQmiLY1kCmV5leBEA -lvMSd3Q6IqPClzhjNC9+cEQHVZcyf5T64pBrsfuiKBINfM9AimH3YQJVxLIDwiZyMg/rFlOnGMGq -LypSwljb1dk6yhHXrk6GXSpzdnKOhz+rzVXGyenkIefci9P+GMID2wGM3cx0hJThbIjBndJQBGhO -ZCVpbLBr3KjTwdt//ebfUE41ObVUfjFv/+hNghaAK+CRx6v8HboQbC+OlcB5BTv3CuVA1PPf/vGb -P8I2ispU/5M3f47VixL9ImF7Q/XiKl9tdJ3/+s0fzzZIee34qqqu0UL69r95/WdDyhoX4SP3EJEt -pVwj2qy2y6LEjKtyTEgn8ZipcLy5I6lCDnNVyTGbUAZH0fEP9QNt6YQCnDz7h2x8wO60ONpZtlgQ -ihIezDors6WOsA3DQvsfqWEyWpAEsgVfRkGTJ0XpgTYQ9ajNUVvRuyJDVxqMF9VW1JLTuhYvuWd2 -HUnpYo0Dm3ZWSww8QnXoqoL8BYscfyrGWL4buc4WebRcVRdkZs7eZcUKl08k6jFJ7ndj7OCRzLju -h6x2sHkThRRNJIMXmR9dFzKJVkd5aKsNEw+atUlAXZg0K/Y45muk53zGeTgdVJCzRtMZnskaQP7D -l8VSDM4j6kiUJCt1mblrH+pzfFnUjUnZR7HFgwDCYicYuU8fOIm6KnhgHJiYrIIlRooCc9xBBhAL -F+G0pBap4SkevKLHxxz0SWO4xIOCQp4KRjgzMhEbThRvM0AD337LLY91V99+Sy3YL6C1BASp9Ntv -d88ZrntBh5WpRwiDuAb79qspwfIehubsp/DaXIxUyYqsjJVs+GCWozzLoU+8h8xriu7WbUsCDjtZ -VdUmOOPEqfZNuGq8d2hslXYGoiaDPOUu8rx0iF65u+OiF8aopq2l4ynoiFYbHwVRfeQlfYtmW+4H -UinjuWWuKNg8BM122xT8IeoSOWM4fFKlMnqs8ElnonAE1eZ8PZVsBPkidSbG6jU4Lz/svmGBQ4Tz -o+0cpqMALtWNF6ZkDRFMUlvBd+9asdR3yVnX2k/MquSMOVSBLjQKqFGkmBk97qd0g7ZMyFMGgZnc -0f9GfIUA8jo/rupFXusNhZoGUj8mWWncYSYGQiaRfuo7ABpaViqYdpfBSuB9aUInwfFWkJ00AyOg -4AW/sqXj4wJGxpSv4upcVjZIvF7djRKqof8arx5kAXLgWzqGIeimwImvaLTQh2StrvVWNmeiNSwh -OKgDKWKmbcsq9w1b7Hr5SJ1RaHLyFjV2aXf0PLIQqZruDoWPLLQ9oKlDWdPxF3jDC2Ah61boXv6T -8o6EMEzvPeDwSvmCHFeJZbsSA22G0CXujQ0dutH9QpFkV9VyiXjgvcfFQGAkdLiWyJeqtjGsnvGx -Z6PbCckGuIzkPaib+M1q6SaPfoN323QBAJoYMpbrtiXFOA95wn+CcCmmvRMyUN5yC6wmvG1ocJrI -VEAvR4n60W2X1G5FDgJrCI2Kn377rX47Vis8FfFGOyg85RdfUXMOpQa6+x1sSXInm3d/2NeJmlWS -wh93k9rcqdHi0FnT2LPisoi99y0S2bHoPF6ojq8sosiz+ZVxMCUkyPU+u4E8xBu4Tb2KL3IOpytB -ZW4yjJzB+pVE5bJa51XLbJisqouKdHXO0EhL3irdYbghxO1ja24dXAeGlpWjMKgxASbHly6wxiNr -YVIGILmdqeOpcSKJLZ6+ybSMdwBOvGEv2EiUKpwafvY4DymylheyQ8hjt3Pd1L5el3mZ1zBnM/wG -SnDeZljX6laViJI1tFGAtpAi0QIa25o1Q9R62PPIBelHMEcQMLA9Kyn4x1u+3n7AWwt+GtHGZrtm -IZaSBQabwPsEC0oe7sn6M9jJqIl03KcOzJRUqnrJb1uPArSuxo4oqmID5Lt5hNPwqM2zelHduCKu -lg+ZYejtAW1L89WWrCbzbANrAD+xDVjru7aIxKxab8gSKmdC8ZehvYlpWgL+o3OZBamBCkU6JUja -zZSkNGFLKCXMYT+EJbY4bqvji/wYMWJ1kSh+WHACl4DNtGBEYaT/NUhQmNEWtSLmlDr2FcmXjaSV -FU7m/kw2dxMk68m33pwpfH+rBiGs5KKqVnlWTnQ6uLKCdVHTUTRLq47arY63rWsBHVbok8m+le1T -XoJkiznMVQhIi7AaEGxBOSakk1sYJgXblqXcWII/q3yXlONQYhLgW0a6/dZHITE8qvLtt/0tm1Kd -hvWNKJYtCcxvv8WyuxpUM9e/2hxVKAj2t9++N+0qwjV0ESA7Ux5jVakWuwQsqVtpVw7Sr5LbOMOp -Gjoal9EDQZY4y7V5SXfD6FQA2HsdWlYq3apCGrszaAGBNioihWO1FzRh64qxJF3nInbydGATIZkI -FyKdF02+VaZ1pNO8Hr+Gzyxqfiu79EAd8xvWZ1WX2i+QTCJlT+1v/ikA9KK8rL7tXZdmDPdYmX16 -gTIjyZYa4vBcR/L1kuuv5vPQ9QZVCNC2NleYI7y6dK4g0sB+jNMCAZaMCr8j0Vp6YxbXtWcI11Gm -HbKjpsao3uUPUtI1kIzImIdOEVtb57q5qhRfRK8iUeFEK/+hcWvpwCSnFtr+LvGs+Qj1x8M0d0Ph -u9BJNKuNgb7awLaSX+L9P3Q46Bwk5LebVVZmOnge1y8a3PlAlr7MihXHEKCBQOlaZlX4qx1riiLT -V0pYt1q2Hb9M4utComWhXxen5RWnaJWkV+lKElrzgowXaGlu6MpbVpoH1NCHRfkhboocY0zVzhuQ -oMjaa6IGIhvEJjjyYI1GWzRg63SftNkv6CQ9albFsr1a3Y3YkEe5AxBbHHLTb0KF32y263WmUnL/ -COvZ0FxRXq62OegkHFdNxMDEOZwUljnjmF/ZKv3RSJEhmF3lGWhDSZ9VUU8So2tRNEArd7ilg5pM -VXFYldhMGGYzuI75VDolRYk4QZdtm6wCwPqPURtYVjXMKUh3dbvCWyI1CdXv8voCw61RDNZLMuXa -vfZ1uG9jUYOYCV0k6gG3ZJ9y4WkpnZgilWW4XbM9BN0UFSqkFQu4H4PGFtWcOOiPu01IL3K2iNYn -WmKJ/O3OoXIYXahMHmIIYEFCmrMmq6eD34Fdi/y6jLsCn3a16Fq0ihb5xdYyof54Bi46apupE/V8 -IefLeGThHeWrKJd3EWZ+leP8ZYWSjKrc3ZWl/W3Z6cFpuNuoXSNg6RU8EQoT0HxxdwsbVTVKOZVw -t61rcd/jRtF/DxtEBhNuUJWPdHlu8+2/efMn5K6CZjntbPLfvqH625LN5mSWk5t02abgin/65o+U -1CrU+Pa/e/1//yG7mwD3m1fvhP+gtCJFGokuv1UHJeZ0lDmg3PnihjH+74CYPYKIgejGcwpdx4Ve -C+sAQbweRfj7OTT0uSichxzAL+tqu1GetjV6L9OTZCjmNIlNTw/NyXoyPD6W8RzLWIbmehYffU+H -IDDAwsQ7TMOROgPnxCemLDrwTIc+fnAnR7ebbtvoTjUdSln1ei+M6BfSB6AF2xALfzhubzHWGZrm -3mX1dAiEMfQB1sASYdhxBJBqdYtsoZEWw2Mg0NJ7nROJI8RUB9ek7wMdMRJzU91SlPqhG4sIbw5T -2TGjaOziMegh9hkX+XXAhj7QsZoS3SmHgILxUggoYMVxyumVGFSlF6AwXbQchY2i46JV95uB48gr -V6JLwu5UAAcCJQeQxMVfN7KtwP0agOricTDga7O0YkAYlsKJvZ6kSY6Ay1xB57nf1MqoQ7ngc/va -LIVFsotQ0kvz1S3Igu80MvIuXemolFCgEqHY6ZXpfi+KpSpmI1X2vHZv6Cmmi8nTftjGVk/Qh8KL -4AOV9cRS3C2UkFqCeoSJru9yXyooooa+4i8PbIKUWugtbcW/TKTkGCBBS8Nz7umQ+Caq5psSdBU6 -inumLKyp5wQpRWUYAKYChkjM81vO0IKILqpOpbG8GHTsSJ2SzsjZE3SlkoUSxOq7e5UJJhDULyki -3x4qeMwD9wLfGtYZhnkzQ8IIVZjaGdr3orh5tOrvIokCaySwjFTrqZd4jCOx+JM9/pJcNZ9KCAK3 -0lfPXn751evZm89ePH/erWm/7WBOrRz3qrgCNlWJj/Immdd++qdCbhdaOB3RgxM1yOg4Oj0BtnQU -ff3117/szIiXvc1flRq+s2LC7Z0HrnhhARWPZPjg5KNF9IASASfFw1OGIxC2sMBsgaf+HbXwKgov -orQHFGxYJnHM7rWzRXF5CZIctiUE178CPWpfYgQSewJTieYx/KYcHnIDrkA74EwGxvNM8o9ezWge -TJwVC4txPiMe1D/Cs+GbL559/fLZ09fPPoueff302cvXL778YsKo3xPvYlMnDlTca3re35uKXID3 -QS+y+fUYzcJZO9OHPsmHh4xA9jR/uwptRDvC8qi4TtsNrEOLxUt8vrHD0xU3twPqs1p6WfUEk6fd -5bLhnZZv7g/PhCzOh97eojdmBxDZbJD/2JsNG3D9bvduLzgWPB9sK4sdEx+mnr0oYhxDSwbA0fFA -5JmxwVel7HPpn8Woy1W2bKaq+Weff/7i5asXr0bdWF6zqpyROkMxY0bKLIHcyUWOSFs/ImpUBEse -rRudVPlsdgNZigeDhKEUkWymy7NWor466NxBlm6jApAO+5weMJsYGjzQ5GgP1g+fy3Tw9r9/8yf6 -iIKsC6tq+fbfvn4u6l6CN1UvVmzQ1IlQxD9DzBG4dHD1i90nkxBgHLawUKlgjAL4vfS3roENdJqh -Bl3p7OLd26NCxcfHugYI9F29CRUZXcJScWzl6crSsIgreLqU9jVcZ6hF5ce4c5PvvuANmh53VKSg -k7AZ3tTTdPSbgY6bQJ6PeNeIbayqJkxOs0LHuZLCvie3MLOi61CMQFVORXdU18OVbT6m2nRLxb50 -A1XkRjpdb9LtQIUbQO0pihokPlxsac9dWEm2eZnZg/uKPn9eLXW30n7qVwtfwUg6jaaHuWHbQACx -uSOf2cRCDhsdtNmbhjwaC+idXE8YZLQD5+7xGfNT4o9MXFnIzmOfbM+vcBFO+aQdjy/ogYChQpWf -0UO8YmXCnDKnQkpT7/iJflm3TnBdPkhZkIDIVU4nllhY5je6RSxmt6buepkiU+m+E3iPO+6KUxxl -XEW/C0VUC4C8W0Jzm7QD6nXLu2X97Ny6a7LX2tfRFDIcvUjuweHlNsBlnJzFoc7cQOYOAG7kQ4N2 -hd+BJdXEMd8MX/I+pi0HagHy4Xe/tcBfoJ6lQBtzLCuOsQ5opqE+ccSXvKaIki6/cBNi4mn3DDhc -rTMQw7pm0YwPRuaUYmJVlUvLCOsFDaZMADNQThqlmth1VYsppevOpzbIadBaoXpjVYC1s7ST79Tv -XfrG4sGeTPo1GDJoLvNKpz3jzW/vkNVILKamasbsxuMwNKFBXSsYM8lqU7kpuAuaBuHP1Z5ZsvNi -9Pg22ANPJ258WwLjBrOzYcByNI4PxYeY3rBXVkhp8BKX2+IfHnSM9xzZTQUci3kvckJMc2bFfif7 -5IKE0Pg29kO6MArk9rdAbBDjxI7XrXzd30ocu3V24MCqZcWgsmuxbPo9IJa3GIdhc7+Gzh6fpx2u -oZeBIuN9dOT6zffTUEkHR72Y2o0Ra+aHz4eBmDe7xknq6bzOmqtDrAfi2uCitReYVzuBAfY3ebAQ -E0HkgTXoJKJ4H/S7B2PanuqxKY0Bi09JKWBU+m2HV3VZGxcEhUDk8w5v6+dr0thwfrOYMEZcC1y6 -m8Exc4s/iDlstRokaFj/w5t/hYdchIK3/+71/zYgzWogOhQ8LiuTfYl2YfF2f/Elnh5U7GOELixY -TbSo5q4ZwasR11e7NwfQfsZhK9Clz46scEDaPKCIMe6JNRJFBDhI3FyA8EmsB7O9JutOTTbR3r8e -pR/ZW20YSfoWHTee80Hild/98cOPyAhmauy1x6d78wjifjI8e9CcywJL7j+w9x5TN+b5YDC7ATkD -iQUaA9B+S0UeT5iAJJkGt/lR6OHpz/VTig7HTx/bDbz+7MVX8vhnPw8+/6l+SmFzRxQ4R2V+uSDB -4JeD7xxq/jW6Q7rCKNolVtnfF5xv/F2B6qy2njoriC6rqCQ80csvX734WhaMjlya4eWmS86GnLOD -VExF4kjd5I+iJ+iIup1jkCc2QRtv1mZ7IdB6q844U+FfDpAz5b8cHpd6ecyVYT6+sylKWJoWtr24 -55aiQGl3hqFsWG7MFU834LMXzWwRFOnFyDIrLRIhPFSGoE2gss0NuQ0D6AirhuKxuhGNrQZlYyir -XaGNneykFvLO8BjisJg/AIMoX0yGxJPJ+o5Zo+m0ZPimvC6rm/IZFniwwIWLz/3gdlSREIQHUAnz -2UTaH0X8YNSzSH8bGz4KKquKqRT3LGmdgmnC6Z5rcTMBdH2XdvfpDm54uARvCKH6nZWU0IqWy3OD -Dtj7Eo2Tv3Zm7sSRBE6kmJWyz+nzAUr1uQFtaU75R5wgqLOZPle4gsVN+WQcBbqPNLDnxIfRpwzC -UYA23FhYUthf1B1yGnHcBlAaLyJOGqlOOsJJwpVVTWoJq0l7chE5T0NoCZg0sEF4I8cuDrPrDXvV -TdQo8NnhvXYlq4++ALI3vM3Don7Zk6wII45g/0AW0iHdhoM36HqVIZdH3izRpLrSbj+jwo+HiNW9 -4df627b30zP60B88d3eqBMZg92yen2Mca5DLHnDiSyRxI0txRJN0L5mwu2Bu70G8r4J4+mdv/lQd -ALBygWblbVus3v756//nz0hWfQPfiraQTVWXMl6Bvn3/V2zpeKJKqt3PMoLI32bsFkKnRvSqJjlV -3MOjTO1kdLEK3iJ46nwFhHm6sHJxp4OwaggHR+wVt6lziUtCmhEF9tCjqHOS5ekiPyYaxpstFCuA -TxJusoZukGNsD3GKjHhDo4BAVyqiiZIiKNpfhlfB+P4Txgg9ij5DVL1QsOQgHTgjlLjGZNDVR6na -Iz2xPhsZiO7ucGHKAGoVYlb+Bb6jqEEAaL6+yBc4BO2Vjv2Ku/kIxnCDOZElWCG8kksgdZ5bN/km -0Tflb0fw6zvCxDflP4oTPPuUY0RybJXczheil7K3IHSI7ukWjA1Ks7m7W5iJtgtqK7Oc1TdRwulc -Z3ibBpO98zct8CRpKnBR6FfOaaNbITsRPrNvE+hLR80GUKluqPLZM0gm4+WY6vEWyQ7gKLOiI0Hj -SnpHEUY5W2FooVi0deLSko6LP08xViIbavjxNDoZ9EW05qt0U13SgntM4QCH1ChMjWocNF8q7Ki+ -Uh2DYk8G/fHgOIKZgkchrQChDik/L7drusZr0+QZQTg576bSnpN8+ttA3kDupuPzwWYuqvVdb61j -v5YdRRRLHBL3LuBHwGKzw4+S4ba8yFa4JwKLQcYKi0B4sX1nJLVF6wVZenDSHkaFFaxcpq9cuI5i -Bo9Y9TjirKbxN6Vn5LPpdep0fzah7s7JgcqbloenP51Au5gr9eEutdaD4+GpHwye4Ufc/5zPEbKb -mfIZsoHBrRvzwEqSGxhGKguDuY3hQDhflxUGXWP+DayFGcs/DhyHJN0TKDzneh2vKOSufuUcOmEU -bTtGfvzbmFwx3YffhR7+oy+NOf5Mq12HQQwIYRrnD0aOqeUG1gGj8og8m4h5GHqeX+MYT6zv87K1 -HjkHDeGhwlN/tJ2lqFpG+ALZ3dByjKgHdeMAeYmKM0HEnrsJegMpZOkDYU9GsWHp8gD1VjVz4jsN -YYv6zC16iFiOAe4PqUOqnR6fYuqihlL7lfmZQ8zEYzo4+87HmTDvUMHAkLvphfUwuq94CGdUAnFA -SFCA7iCwPpD+Md6NIsCNQU0HJ6pVU2KqpkSdFQIpsyVKppNuJb5k40uETjZyQwrDSFKEi48GWqS2 -Xk+tL4OwcuCUhg8SQzFwaXDfZUHJJFQ0ntxBC6qpMMbnBreyqm4e0ady0ahN/KZYkDX4FyfIj38K -vxA71SaFT4+Bk8Gj+VVWN3LxTjzVTiNORpzVlA9QAqshjDMxuYMAXFTjJsOTzU2dMPjr7BYDuU4x -Hwd1/OixrBcaWE9demcqU8VjhFL3J42o61ewYum8E/6P+chTyvG9S9OX2DYLQPlbvKtH57TZ+mKR -RbcTW0m8HUErBd7maLcYz0PcIhry/OmvY6ZYVaD4l/0V8HWqYdpVEt4K9O5mqbOGOIoeRrbf0F47 -ncaBVH+U2xLRxL7+8sCmtv7tmXxMZ7oFRaRdYceg2elKPzywO1kVs/ztzGlvd8et12f7Xt3tHyLO -oNMVPbg3JsXpb0Mn6OMNKz/c7j1ShIZbYWC8XULIg5Mw/uAkAs0WZQ+NmDj0OtkqZRG2zmy0hcjP -aLzPudi2idkAncWJc0ebYhySFjyhM7q80fE9QbF1N/4YVOesWDXizjjG3YHzg2Y63NEVxWkij7k7 -fRQ1Tj27pxyfdb2FzweDgSXpW7BPgin2ROr8jNLMc4gajFK1zFGZb3AIi6zNRlbMFOUchjZJ1RWy -VOUrY+vhafQpbA8f/mLSi8zPCCOUFM44SGJyevHUHGEerJJi/cTnA3u/PROe7Qn0shf2sBa9/31l -ktv5F+Y5Ic/lJR623OQ5O2kOTNQvuoZOSRNWebZQ10DbWi7W456XzdscNj4OyqMMBQXuggWm5gMV -mrJ86Z7WRVmss5WrJHuoYpkHby+cwIRR/BB0JsOQEzhCClJJGQiQgv4+ryuclaVySUP1lHWBcpkn -0F+itsF0RNMnS9s9QMH3Z8U5eozQe/jcpxbjSofZ/thK+1qQInTSPUjUUhFJGHR710qe6ZPIKzzA -R8zCtmxwGPfxsljNijURRUl4xi29OLe0+EvaJ2mQlkzJaNRDljdC5szHplMLae7tLAvNunxX06BO -j23UHhcBlcNVxwM49vG8B7c+fh++B4IZyyF6D6PZR/Xk2Hvp4lu/9uEUib4uNpbC3HFktBRB5f4O -IK2Ki3GJH2g+HBe0e+XN9oB2GpK7IN0wHIol7RM/Jv3L/sdavx1aeNKSuH6LtID4QkMOVn9Qx3uu -yCTFSPU30r1592McdmFW06fBxdSB7XMkIqToDCNNUwxZChc38rdZtrQC3deZBEtjcT6gWZyZns9H -Ai3rvhq6Tw6D7iui4h8QPMahhkODF4jzcgR74PcUAY/uIf6F6Lk9lJQJTIsfgLZIdcxLhx+QWnnZ -KqLRtYMzobX5Zxq5jcoGRn0BnLb7My0rivlVhlruad2bJ/InTx0ACfJ7Q8jj3Qei1/a9YeznT5ao -jTdIRiT4qPQjxBSm9IgN6liEW8T4LvLqbEIFeaZRslPP6fFDJGaqJ3vqvKoxfvJMlGFq6CHV09Tg -Kjd2BYFPwh7fSOEzVOA5PjUuRDlsmgTWF49R2QY+fpx2TYnEAXdaEdXe6RvGOlkSQ5WPo/eq9rBT -TYZvG7OUBeuxa9XzrWVeTXNlXOV64feDt//+zZ93j0DrXB8dvv0fX/9f//InPzGud9ZZpx2xyDtA -VdEsgqehysnJO24Ilu13wv/Qc8MIVh8HK9nTgA8OyI5Fx+DrRixzCdY6OznvyyXap7f2ezj4emyg -3+EnZxd1dQ2ai9Ifz3E7z9rowcnt4lN01wnuAgKr8b8bgTioR7CLii4tNfo5hqvFBO3kDHBJ3/xk -qmHMVdua7GiX3NDldrXiZ6FzLCm9N6PszqRxukv+gB5O6LtOwUKTS307XJMs3SLcmVjuBfK5Pfnw -dK9Ont79BwpdqNGljCFf5GxlxWNdlpH3pK8lt8UdkxCETTLdYQzMDOYmqubzbR0tOJitxQzE1kun -yOH8n/NqpkMgYMDNnHL5DD8BZf/TYScnNkO1k+it3gUno+hS2S3oZnNg8noObuylNCe7B9IY+j3g -vcrctlV8Ogx5MGkX1J7G8RWeCKqeRiBT4TBh0VA6gqok9ygQjJOPRtGJMK8Ox1L+cQio8hUZCvfW -2Jhhqk4ogH/8KSJgTb4kTon10XBg8NLDuqEx3ZJi4eZB1vgAhEcHGHg8in5GB4zEMUAkaRG19nY3 -/A0AqKO89MAD29TB8Fijs566JEQwD95Gb/797r7efvD6X5ywr/rzQgUDW+eLgtzW0V1JEM1nQhzg -iI+SNKPBuMk1BYJVWcaevHo9HpCtScxukpEtstGOMZLoHusW/VfGA9u/yNp+s6Yd7N2KFcpwR+7b -rW1c7d601WTvmE0ymXUI4hOih5+Ooscy20fRqzyPrtp2M3n06GK7bMa/oZOzcVUvHxVNs81PP/6L -n4ur8O2mpkU1/FVVrb7c4JXnXxUlf3hTZvUdf/ycjkXw04vLZ7f06LNibsdoEm/Nz4umfQoqDpb4 -Sw7MXtVS4z8V+WqBH56yDkQf8UpVpxWM24Bvv9iu8c+rlr5pNxx6tr1o5sCyWyoHHDEMC759jWdI -cmF71rTrlkf8XByEPssvCRLcvuUzGzpplPkq5w5hwopl2e3lyXapXkXDl6j64YfnFYH8d+h1w2ij -rwVdHhl+hRJKt6nX9R3b0wnq+u45Zz6U3oFcqCUiI/PpOVBet6lnt/mc5oASvOEnmAQC6SUMk6YZ -zQo8Gyw+KwwhTcz4Ljc6KLaJcubO8ECOXLn5ai5tQ6UQkYXee1Wm+UiNGFo0MyhKbVJA8O5dErre -qsUtDQH32mkI2z+8IQO+xfAOhMs6MCzp9hKUH2P59D5ABVvB8mhBYMlewtIkJkyTttCzP+QlnRKz -x5fmPyhtII8kltcj9Vv67XQ47Ny8zbbkBNbjjExlXOtFV18OyBs1ObbbMgd5m0nvsF1DQ4gETrnH -1bhX8s4j/0/fCzKhVgN5U6UKu/lhSkXnUEwwO3FCaHCkq/+XvXfbciNHEgRrX+ac5c7ZnXnYeZhz -dtbL1VF0Sh7UJVU3tqgppVKZpVOZko4unVkbGc1kkB4R3iLpFJ1URFRW9o/s037A/to+zQes3QAY -4HCSISmres5une4UAw4YAIPBYDDYxZ+0dsKUhUO5XRZG/rXqDTW3xnpnaeZOFBOxA4PykhxYrtFM -FBNpUfxmmGvT8S4lfRMbSaiDlNP+YTovuJGzMIbg+0nyanN2BscU211G4KGKCk0lG9GOdVZ3+Qgj -QqI4POS/hxSho2eCT8Cgs+r0FC7JMLyR+CPjyujsFOiDvxKR1r+ScPF+tySbEdLQV7RfG9rArlUR -Vbvhmd6P2ctqAhciRDwjrfP+MJRBhlFijq+jWojpmdY+hfZszuLrDtnvpEny4IHxJ2f7+baHVmdP -o4IbiaIIvTINFDNYurgam+/G1Mhl+3Lt6VF8uWVg6KDrOYdTf6TCuvubgaeA5WdBYx8z0i5juMc/ -L9fPVwlQ5V/lSJPC7yoq/We/9BFwOSj9lSr9+tV5ebrG0gcPVPFLW/zwoSp+NCUAt1QRyBVYdKiK -vkH7Oii7qcq+KN9j0W1V9OWsqlamXH/4pqJeDlTRk3dYMhyqomfVmkt/qUu/5rl4JU+oSNf6iqfm -lVCth7rWi+qCpqHn8bTGorL2imAoXIpcQ39ZUPHCHzWXsiVDik52G5QbG0srQLHegdfd4j0SEHz6 -V6/8jVkJv9QsGZRiXyYMVMj/ucdp8U/M790JaSvhYSgha+ASfTYrxnNkZacbHZRYXVol2FDLycnc -JTgwDf+if11TE1iFzyBxb/OFgRtozj1DVws+By6KZErWBpShbmxM0IIsLX2tetsmsfgH6xPL4Lle -oFy1bip4LmAYMHtqblUXWbtUh4o+Xu6ySRXVslhppkWhZ8We3hbbZdUXPweJVWHMO00H5ud6vonO -NmHNR6BEHzvCSsf7oA+kbkx3kfb2VI0K9qDJ6BOjT5m15L76al8z9smY6XIxBWmTHZJJcO1pT2Yz -dyZ2I/rNMU9VikSRNgVh20Qqpw/UzdtTyDxMGZTqkP02kLCdZLtqCcDH2xArSHjCEIwsLwNC4vdt -F5A+JEZ1/6SaBmuouhAp3gf+bGxjrwS+uBECNbTkcxDfu5av/5RyqkiMqpAiDTj2YBWW6cGKw8zA -55rzjiCD5rTgUEDCAqern+Y6GkmDqrVcHiVm6mMHO9hOyzckP2eFQscGJ4aehUB90X48YsYXISoX -GTOh0FJrPRjY7Rh5O6N6vagbqEFEyC62cJRgtUXzYSMdzZdaNuT3XSzt2+dba+jrQRcTW00TTAv0 -Xu2RJ5rgYh6kUbXEpf5Lucyoh2pZ8wj6/CRN9sqNJ7LLoGMqiXUsXfSCEIGj+mp+Us04ipuV+Y6q -pbt4H2/h56kxLE6tYbGfUMV0sL9BSjinwLbe7owRnZwoAPDDOwxDFdGDMg3KDWEn5w/3yIecnXkS -DGyoaOEaZjnhXIZqZT/ugGnF7TYP4chGlLHs43Lle8RS1oKnJmnBx7wPNnvZtRW37piO5kd0xfR8 -NVuf5TiGVKOBIj09Cq/LXjPaUL3LfHilolDsz9pYgSycDdMLVpq3waKSokapt7hOH/kTClHPV9FL -bRA4mjka+VQgNxMYrOtote8Ij0/mWAKmVZy0rlmtBG3jNKJpI81wFwlJkAjGBs4hxZNI/kN/t1zs -0yxNbvEBFBoM1+h4k/bSD1gz0fDLotG9Tce14+QMSHrmRndEv/px9i0IDTk0F8ZWwAAL1sGfuRkF -sdpmB1uFEtVW8/OCVOG93ra4D/sxYPwx9Ke4rzSzB9O8xubDNxuz98pFFYoVe0oP1LTvyxDsvuOP -QTx62gAE7hpxEYCrxikppH0jBfRaxIDryQCNGfU6H378N87+D5GNf+bzvnHW6wX8u9DrYxcARmXS -JAaPiaE9CFjiUxnnjcYs0B1lhjNqPzXo7x/1mmLrNBkQ8J80lA2/LjcOHcohzLEZIkYKQfcUPyO6 -QaC812gpp4Ien5V4Fxkw+EC0XNRHphnG4HF9hZB5MuYcM21624bu1Y6usuSGotRwBiPy94diRZqb -+/ffBj/S6UhdhuvhgQA2I4qtl4/VAAy0NX2HsZ3aMB2H0BIEAKMYIMZRJ/8RdOjB2AvjWDn9FGSY -3hQcXxdPXsMd6OEYTR+DnDDK0xbUvL2Y1p8INR+Omz2QgxPib2TjS+E8JBBgCLdNGoM9kplzuMmq -/Q78jsPueOY7jl4KvGX6o/hbP+NBe/Pmov6Ep6ETnwFp3y9+PEAU4K+ftKi+3ENr3SoQQ22KOht5 -u9v3PEYtt5hQEcHFdZF6Lqw5w/SFRktINxdXbrSJaXNt1ditMk1+fOzCqowqH6JcFAAc9yyQVlz8 -H0YOGrQ0o/yqqy1W6E/Wl3yz/boaT3vtw/WVuQQ7QFwg7HJZVLrAfoNDtLF/+8IoYrAJQGwIwb4k -bblhOKbN30gIpjuXh4RPuGkb2HIbt897l1AX37DX9Yr7VGD2W8gbCaUUM08D+JGyN6Pl4nhhI5C2 -vxIYU65gLXKKI1lO1qNRN09+/Cm275Vo84moBYc9MmP+OYkm7Ch8V/C/q/eFiErTq9tgCcs9HzD3 -OVh+7mNDGCO9yBmuWNde8HPy9/eASMj7CN/CppSZqHUnip2J8JwG7F5nt0JZdd/bwymFuUXcXPeT -68djOFfjjSC+PFs4xMMfakp0Avio56IW3EPrHadGv98nGnMmSC3YF+mYzC9Qmmiq3eyRuM2LmJ1I -hnpsxrGktc2kmo2q09O6WPvtXHlP544ZcSUZrCBUGsJVB71aTNRhfzS7xtE+nthIIlYEdmzHW1lk -1I6gQckR+4EmY9TU8TPrg3RXnXfpm//cNJq3OaVvvP6P/5H9BOrNkoznydmCw4VQlMRYIIyS7Sgp -xoSBWfd1jPvlVSfmckf53jxD/nm1gMv5EvPpGvN9VbSPX8DHZCQjMySOHNKeboy7bc01xp/ROiKN -U+zkvConRT3MUglXylnP2MICf9NlLm2LOG0zQbvWNpXZN8+/eNLSKycySzFz3npVzWKhXzGgCSYn -6NIAuhhEHdcY38hi1aGiGXVXO6XUEdeR8emaI5JeicUt2hRIxHcFWxm+ajsw7Ijm2oVzCIORmOR1 -UhzrkbLW1Wubx9xSSafRc1u3aH7WSgWLqoUQdmQTZyJZVGPjXsAL88WTFy+fPH6EGT+Ld5sS9iym -foOh+na+28YzH5+Vkw8cDbX9gMGEnq+vcAGs5R/9ZYPVhIGIttrFm5RUc98KjD3ROCUK/uN/oGjJ -Lq8e/dlfVdWaYsKndgD7ZeqTbhpZvdUGt0a7jUp2gclrLPKZMa6mJt3J7jeA55ImyO72LVH1lStV -6O5JXyL+nqZTy362GKNtcZfqortULPR5HL6Z1y/tdF3LEUZ0EpfBOpt71pFzWnV7GmSNTIKjyawY -L3BrsI5u3t8spkohAYOWG1UkfDXcqnyZMxrEXzG6IN61RBmoqrf6zrJ9DaW2fHAAX1IBMNk/QgXt -8QELgNyeUgT2YdB4GbiTExyudTFeLUbjk2qzHs1L4GmLMxcBTKHTIIy/Ed+Ecfj7OPM2YVuzvswB -/2mtQ1sxS+1OmyZIN4wZcaqVSLxzY8m4T6pH6bptVGYBqFrU1sPH56qYV++LjJHZaaaeQhTWfFBY -zz/yNDYdVIvZFWnDcYLkVayPILQ4EUgSUPsGXNN1OGyjz0N2OR9jjkg8qkBYoBBnPJhDyvYpzTOO -3TWtJASvgPf69ehSht//cJRRMjVYs5GAshjxMGaSvkFFEKxULktKSoGW1uWCrtvx0J+mI3d/otyX -pc7kFokjGgapgCkOqaGh5Go5NB0OVa/D4P2c/OXgUoByNK6HGkqDJ5p6UZsydhD5V/EQMVV7MZss -Nc6Ww6aFh/gGmxgqXZBGEeBO4ADGkMxo2D6WiK6wz1jKQQ7TAoctB8Q/HmmMoKHwJuosji3P8YEW -RbWpWwB1D7o2dAyHCu0nTzh6NBTNKe5JP55MgfAH/+2LY3GGHh1JenCQ9iLIppuPsaRq2noNNdlF -aXVdjFfT6mKhyTUGx8X3FwCyA05BgqnPAw7xqfcdi6l6EJET00pgX3NuJSsZuEQJM4xVSDkU+iaS -H78umMMo95zR253aDfCw9vaDbxcsqs/z232iDZpehzpcv/Y3DJyp1HNLXXeacs/2SdAJIuneKDsD -Ojm7G7ALrGj5uj4O0q36jNTs3vJsAXL8NN0W2CQYRGpu45TlEk6SK0kJyMksCIdpxzsE6/W0WK04 -FV6Wfvvo5bOnz74aJGgC5wFvjzufwmDZXzVy/RMUoD5kg3TXPvdUkn+A4FasZpSZ4QUzrdI5G21r -n2FnV9Um2eA0Dcc7fP5fe9+T4N96ehiDz+aXzrt/ePPvrYZivHr77uD1//2/cVZ1eUuCO/jkfAz7 -fy55wldvTchNuJqg7ODGYvJk1Ozg5GlD8jCpOurDKBR1ZtNbE6P7sYt9dAfJN/CP9fvPej99lAKE -ZoPOIeqaOdL3zLdKKI7rP8S2Qt05u12lovjTkz9/+/zlF0++e/EyDdOsk/iEbrdMt7x95qQLOivf -gzhljGDchb2fhLSQPvL0CMBw6xKhgQBFeDysC7K9Rx1EsZrjIgQAnmCqOfyWzDF8KQ0Ag+AWkwJ1 -BvyJ5NfuYRdF10Vxhny8Aeg1J7jnHChmQNxwQA3n47fs28ux9niq/N8Q1pjy1p1wRMa1YCjb1BRW -g1IOHwJmYIVhGOLy0li+9FDHLYgvH1KVqI2tpslTMT16+ad9Fo9mgUTPK4dgty+biMCD5HCeEG3f -tfnr8a973X7QwpulnuQhzWGFMReiuhAar0t3Vp9XF4k0STK5DeYmZxCOAUShQ5DDKfBxhU6+fYNi -2UDQO5z9Weo6Nj9Z+4Fs3+56+NpFjTaGW+8G94z5FL+M5pje3bvpOB0DT7Mv8Afbk81Pq6Z2wykQ -LmwoeqHU2beUPimLp8Z2wqk32dCDhRT4K5zyUILCkbd1OoCJ3w38Ey7MifMHxV37B/UA74IC6qSa -TSNBkaAtQsf3unX8S28nbiIXy1CVfqcTWRu4z17xljUJuaKXRRTKytMrip6YSZBNb1WFn+G+cNdY -WWD5xqoo3EzRWma76mjaGqrbQgIh9MjriPYMRgxiMEVCdEITAFTAjg7vDjhL5SD1VHKqrZfcz5+e -hjQ4vGvDZCNCOUeECb1ZMEwymFKRZQGxJsQkIdMT08JJY+TrkyspzaRprqsFdOt6NdojadTboRNz -82/eyXwMNIKVoXjZXJjgXZVq4Dq7WdhGLb46e8xlazhHb2EaIGwMUdtLgwvpe7qrxntg6ArUGzJ+ -OULych0b7TL6uWDso9bQifMrSisRqIlHXIw6Q/rh52TFDttysirnQyQ2DY13+h5LYl7i0DLCfkNj -jdzOxzTuVyf/0jcGHb29dNON4wBv1hOlnNDsVIBY3sEd7L1BUMqV2eDuluv/VatIBrLCeIEu/m45 -MAo9h+Rj3litbtPEvUDyTe7V4FUdQyrG+pkT35q/fCoXq2l6nYQzHY419rlWHcjRpMNRF5XwKszr -RBkjDrtejq9QhY5dcAOd8EcmkyG4f2bigM4NtuHf3JtHL26t5s4WPUvlGa/w8ONPKuscVomrEUyL -/mY5RR0vhWSyw3DP9ay4p694BVqsNVWb+h21HbZOb+AS/82Yg8MnIxj008C/++2F4fFxK3dcBgPm -YpcDWMSrXhseNrDNPMe7Vbl98OUYifeKRjmgmoMfsOoXxaSiqj9Igow6OcQtUWHGojFrFsbJDz+A -tGX35Q8/JHhDnRXrStmCJckTEYQH7oaeuGd0W/QHDatfz6oLek3X78Rsz2IEzyygMVJ5SIokvFxg -BMKkawB1vflhrhEzNWoi2Tt/+MHr4gdTpxG642fPgo26M0nIyz10he2Fz1Ji0oGGeQEIIQNvPaWK -iqtADaOTaD7InYZnSCi0R2QxrazyJhxZveC8kysHiN0SCi4uxxujln2k+ZMCJGYRz3YI8xh321bH -OEdSNaOqJgWetTfCS1N22QtJwKjKtiAsSg3GSnoMuDzD3D34yMTNbUgFeldy+9sustvfj2CRpTBy -c+MMKlgkmelht36LzzyU/lredEpRvE+AC8HNv20XOfXGFWCNWg5WxengByCFVVm85zSKnI92XHNs -dcvtHwCBsB0HHq0Pf+CD1JuTy29P2rfqdA3j5DFNYSVJ71DWmsnwnZtuhI6vPHv0zZNQo09JUQu/ -Ow/KvRgUWoG7bPjUAyiohJnjXP8CA/JhMbsEXGME3zUi+KQwOEZk2DWiQEL+GukZ/YEG07keR9Qi -SUTW5DspOkYMRZy4sH80DBWgezHPw386seC69A8Glg3C+jFYlCwuTBU44dWoOGQ2jUr1O7VGayJP -9ifV8iprmAtOKZVh14yw2+CC6QOfnA7w/ygoN2NgqpkipZf3g5K3ppaHfY54xpWU848eZiiyr3FS -GZglLng74VMxKpDQOsd/K6rwJeqCEtNPp9zzbdMxVD/kgLSkapP2XnCprYHTxQ1SIos3U+OQU03N -KRaJXM0BhA3JRpvRwqlJvw+ZfqM2JdPD6tGr4HWqx5vwnYDUxr32sN14dlBqcle9tW7TDwMDIwD3 -3QJfEKvA4y1/lhPZHrc22x5rnA8UvqfSjrh+vPLIoOIjaodyXs2mxYrvJArxHi/gmE+91iVjEI3A -jK1dmXMlC3qxPCbX/KTXHq1925C5tw9AqrSks37fEQkDwoG4W8uF4WvcKuRqby/MfSaEKlzUsdxb -VBSNS2X94kNkEofnoQu7f3vhiRK4Ak6K+EZefyRXnjl14TxvuTPYs7q/39FjhqHj1w2YtVenLvW6 -f6LETiJoBXeUEhnieGbZb8KZQIv5cn3FexwER3kGLKatx5iGatQCFiSeRtjL6soDvKY3ghbYwRkY -iLtQhOPEjZpppAht9I53n5beWYdriMccre/Byqw0n3pJFo8Tt32z2QEY+m9dPiQbPMHGlpz42MNL -FitaCBV4ysgsvfPLw4hhhB5Wer3Iut0KFk7vsGA/aaIE4bqJS5zBFcYbdzMwkm6BD2pkcsBWxjBL -eSA9JMdfbyri+m8H7i4DMj2f2fhdZmpRvNl3Ou9+9eY/mDfcJdpWnZSLd93X/3uH33Hrzcm8XNvw -w0b2r5t2wTwBAwJqrN6XeHH33nJzMnQXDrFZzVxGZHyfN6HiCUh/Uax5+pfz2Wo5kdTKGGLmNpfc -5s/0UKU+4t/w6WPee9fy7iP2AWgX0Pr0e2imrJ9zAzt3eW/r0ntb17wpmobqTZFkZ3dzvloWw5QN -46GSsZA/6rKxOADqAp10j8M3xxrIXAzK/4oPpBSeH5bHIba5Tq3Gv6PRHEZWsswWPNTIusIlbIlP -rRx5Xdfvi2lD1ovrZ+0wUL2BEfgbKvORqYIdUDBf7qv/usDOxxicflZk3YtbXe184l6E/NctWGMu -yLpmkXmNi5VqD6czPcBhf6v+yLzHBVemYsSvdHGhXsPxqkSs8fxZyuMf5YfxYgO5kRAipP/9bEKV -3Gus/rper1r4jY6qLoq3OlO98IBZdaZsTb0WcMBPI9bIXp3JrKr1m+S0mEUrOhX+qrq8KqcsTtIf -WQ+zerzAylnKxiRproanPeEZIHAedloURzL4u49sIzfQe1usgNTWoR4OoYsBuT0iLNXFp6NHRIos -v7nAHnXZ9ePYKsIFF3a5Ywk6VD4aa27GzHRWnpjt/ApYQrF6geAitl+qDSxeiUJMSzsRI1QxIZrb -9njv1x71GhSMJDtwFuLEzS780m9hLL8Uu69imkbfeGlpQmCmE8NoyUhthab961r7lQZDoBUZ1QWc -IENKDcJx38NTkiiV0IJ83VP1rcN5kFtR4CMa75be9lNGLp6sEgLXYTz3ZB1cE6IYbyN54gY6ApN1 -XtAfoYF8Rwx+9vFiNYmelv1ZtThDwZMci8kEHs1w6S+gplVZ1EeHd4/pb9z5s2oSc3PdpgV2/eGu -cUH2MUWhWEcsQ+OINqMPNs4tq4itBU5mXRn8ZOuLIIQn9n/RNwCc+XIQSYXtAlFxEhwOjtWp9Yry -utAv+9psTg4YTUoHdXJ4+FDICNNcebyt13mXvflfAWMjmJlBATVfveu9/sMtdpfs/BEEEriQO10x -EpY4cbCWkluyDI/LXfc7nA1JuUbmSaUzEnYuysVn90YY6m6CMlJtrTMCZxwSK6hyGjGGFYAMwpZG -YFvrjW1+Q/xuRFcBWOhpCdc7ZL11FspKMOGqzk8ni/UsB9rYyNMRCkyoY8JyIMPJepbdzaV2//XT -54+/+vbps1f/R55+f+fOnfTm70z2TjTRzy/KKdyKKKccwOtvFkvYURlI1PC/lEObJb3kaHDPy4Eg -jRNq3bEHiWPD9CGLGRJ7TTHLZzBnja4tmRJdBNQw8umN5MtHX3/9+aPHf1Irw32Vi3UGOCkW78sV -sEhiRI+ff/3mm2evQCT+3R3hc/6xdSP57rvv6N4ICzytLurEG7E8OCQn1dmmRk+IdbdO6vGiPL2C -W81JudZcmgfyILl/x2c7ZoC/u6OxLNj1kcpct4HpTofHuaGO6ao7KsgrYMp61xNghxe0UGMY+Igy -rmScTxXq5bSDRAMPfAI3MvEt+DDb1OdeQhsMGUGJeRrZTljSdpKPTt5BeThWOkkhPjvXE2tSpbSe -NKI1Jr3SlgQ1XuXoW6O9t3d/afeu5Q39EiXYq6yZ4yPrijPJUff7y7snRwf1HJPGTqqp2EhRiDjo -57iXRKy1CUqzmGHdmXd7QkOPnr16itYaDLJA6/faXFPZ6QVRHozuFqdD6YSzbXCaLdOEZndlBsFL -JCcI9AzMHJoJ+dnRJSHhUgAgsEtE792tyWUFsrMHELBrepMYJj9miJVB8uXzl0++evn8zbMvRt/+ -8enrJ3nE22WB8s8sqg7NPrub9zwoL598kUd9ZlZKE+aDuBeA+OrlkyfPYgMBwaNYtAD5LAbkr42B -3UiuihnuwjiU+wGUz79+E0EJQDmZbYoWGL+OwGgOBF8LN6vlrA3Kb3ZAESTdSCZX4zac/DaA0brC -F+f6guwD+f2+QGg3RYGo0JhrEtkNIRL7J0YTduAJ1kjMQRA+hPPXoW729NnrJ7DBX//ZVnz1+ovR -8zevX7x5Pfrjo2dffP0Eej68e9f7/uTly+cv9ed7XlQeYbGOm/rDkHwVw+SrYv1qPf0j/ZmFcLft -03YI3sg97QaxsJrbPIbjr5oVpCdkWL3+hZWu606IsMy1/1Vy5/LOqdIJvLLgXgPnc+HKGK4ELHMH -OhllcVrxy3UveZh8du+3v/ld8NzoNCKco5zqBNlNdKYnhuHZnGH5Vqj7z8BOPiZkNKDagxZP36Ae -lWWidpPMbNOKbDs2ywyr+DaT9tmyxHF0+Yzo9pqnhv+mip9Daen1k5ffQEs4ArrTzfyk22xBVno7 -/NoNaIlECcAWZLnP2t3gGsVKb5nRSB0jqM7ITmYgrw4/u4MW8tMhnAjMqIfA2IXbDoE9x5/TkI8O -ge0KMxwC9ySONgQGyGxpCGws3vZz6vc+9PsS+r0P/X5F/d6Hfv/M/d7/rLUt9Hsf+n3B/d6Hfh9j -v/eh32+p3/tt/ZLx/l18FEY3W+jsBOSGt0OAUFIWquFvhXZGIlKImbT4VsOmgasYWy6RCAgHY8dJ -uVO8SU/QTMDcPe2DU2vaKCc32gsvB8VIigWIOlAS2nK0yo9G/yRwWlIJ2aENjcBprt+X66fPs31S -Pbt2wFn7wmGrTZA+Kdg3zvygyREAGJErqZS5vp29+RHmmZRiPJLMT5C17FM3d2o+wf0k3axPD3+X -BmBG0r2n6uSQSZvZbOuVwasNk2UGggJ8lKeoJyu5WQgJ2FuEv8ZWNvfAD2Ls95MI4U7mDt8jOWWf -Gb3MSY8dVeyNm0YQPttYHoYWslE7O8Om2iKp/ROqc8TQbrN4u6guFjKuAQduyGKhc9Et4+KIQvJG -MmDUE22x4cbADXrxF3qzkowHlvpxJdVqo1pSsl4Vy8n5eAX1yrXd9ZbM5O8Al7jZLSFGd7ymU59w -1R2c3m0rTn+0riSNErIz5GRozYYZoiheFj7A1+WJ2g032PmV6k+qxbRkfer5mHVnCKKXPBgmzX5R -AsCptic2uYGJ7S7Gi3VyL7mV3LuJAGG3zDCjBckn2LwFOrcv+2iknKj2gubezWd7AzH/awDQbZJD -De0wudcChFpl7c16ye3bSeZ35RPqs+QjASAKiRzoY3IzedYJg+s1cithGyFNOpZm22Rft25bENay -UgFWYS4aRmygbh5Zazsd5hT5SAU8pS7XG8kGwBEigA5XVcU+0eMFB4uw0IGekcQp9kbuQ1uCCFZO -NjOohWqogt14eVOMxSLQASJz1nSUpAhPb8KV+MGT46/ZFKyfRFTKAPARuTq127MTGPnxfrulJ2/1 -Qj6RD5q4vGWR6XRJ/kFGOmhOmEusyDEyeR1lVhZjVE23iEAv5U7c3J33Eds8ayGF2s0TeqJCmSWr -o5FKgvOXbVLiJmAWsDnEzCSbFvn6Qbj1u71K2KM9GDAjqxeqQ1fF+7K4YB8kMuJG9eh5hZey6qyc -dMLZWQnHKOv0+yJ6KTxIPqNvGtmYz6ecNNNWmgOMocqqNKUskuHCBSQpda/YqdIJvaXHnpHe8Nie -0BhanpKsPRTJ2ocF5TbvHtRdMeUhSyhU4UEfNo1x46B2q0O0LWtCYQAiNEyLa4V/UcNFXDbkUfr7 -RShReoYH0W9+ouegR3H7MUNeFWrQZltWy3XdDD++TVBsik2T8QIbYYAMiSRySANAXgaivH0gSuOB -dUcNlWsDM6s4ZrxJqH1Rc9JKYK6LggMy0ikGO5p5OfLMWvFI1fTy8tLkxZ1XFARoPFnTVY0c2zF9 -bsUmkPSI7gbzX1WOpIXg3/LYLZksEXJh082OVE8f7DMTdeY1o3pgumy50wnK4cSBI9KM7tA2v5Xc -De3t9Jh50t5ZExKlRMUXFcO3qEAXbY2oGXytgwqudd1TQyvJYg7Ke3L7ayvnPlJJF4RDHZ3gxTRQ -2N358k6jvmhVXbPYCff2gt0jUHsBXEs/JkXhtWlXt8FeFR8A+uWTLyL26HrEwHCuDxYV5tvhkpbq -+oBJ874dMmu9PhD0X3fips043UAMSebOb5urdj1l799EFPsAmSkYn5344NrzVarh+FGPEWMCxhM5 -qvFSA8dEDfdhDJQvpjh1QWHp+PGx7rTKAIY1WjWWp3iNaP6o8byA03barutrU3UFjFrBgkrqr7j0 -Ph2vxxGxxwAPAlFAZeRQ8M8OibExlow6cmPgpY8avaPZyWnjZZZr+WcL6gob508nbldCx7088spX -6IK/i97WPAFPKKwlWsaAePGtmCpMuI/OriepxnOUeo/y+Nqjx3+iSQ95c9+h50iMhUg6tkb1N+Ls -KNXv4iUSlXXmJXyxHsOOJk18P2xNDEm3vtfSmnhpozmwr8Tr/H5Lczg4Go3pHVA3/m1Ywx5Kpsbv -fPBlTQHW4JaNfhjcARqlbMUkIBINxdFTk4AQaptNfazejTaN4FbBCHF7bzsMhWEFJMTw/e1AVhE0 -hHj+7Z2wRojn30U7CbHNRP3H5y9fo/0s7ZD+ZFSfY7xyspkiXvf4+fOXX2Ty+RVZQG1WmnsBwy9m -03pEPjHd7+BQJZgtkeSz7p9tjWPVzatvHn39NWDr8ev9+/q6OF3v7O51tdxZ5yVqg3bW+rxar6t5 -dPSPnz979fzrJ6NXj5FmRp+/+fLLJy9hWb58vv9sphevyr+gHEUYbx3F9OLxZlVXqxfiQrWzgRJk -u7nljP1vt7WpV8wccbJ2YbYM6ZvxZTnfzLmRNw3lEOUJ6XBRmzadogRZaERG6SZXcHX57F6/2VKI -Ny4zAD7dk2sLyJam3mgj1wJ1nqLR93Dbyvd2jcF18WqCzOPzzelpsaLuvo8YIeQGzsnVqjjNcAC9 -xosFlvqaqqax5AddnWS+cbSoa/qrzUmNZscUtY7PZrYLnK7Pc+U6TQ8NRMkgtYyXypHPKsvEwPt7 -zD66qcmES9z46MIKx/i0rJez8VU/hoU+b6j+n3Pvz+/gyny303l3883/gva1s+qsj8FeoaN3t17/ -P//jL34Rd6f6Ql59AY/fcvWsWdQuB4qFL6KAE8s03PbJoBr+6xdjE87qdN7U4C4qCZCxqPb2Ojyo -BwdTY75uu8g10Ft3czumngJcr9vhmvpioztelojUjMwaRI0pSICiydtZ8b6Y4cu8MV3WIvENtsPE -82pe1evZFWyyF0/hHCViYrPse/37t2XZ6v7yqlsnJmqvbLsbSKl0rqK4oXwEjNGIvibZIYWaT8qo -I9aaZOmOf2cqBgm1ggp31TMonrhDbto/HVG040lF1yLKgXha4uuj7ZIMRQ7vBmY71HoQODMFUMPL -1aTa3s8Q+4m6+dOYW5TpFgApkODfaDU//NEWowLBTsOikru4pTGpF8DM+8TIei0xifVoGRJvLHwF -y2DFJ0Ui5Ch5xjNDlJZOXUqDLCRY9xP3iKVd86PnYkc3mt6N0zq9/HJx/P3ULKezxACyXWK0IyRH -sqSwvetHIOIN0ILID6quxhd0q7O1mVZWrB8UBuIjtKERpSQDq8Z2UDQWKkadLtqHJDlFDSjxumq8 -ZNEk7m5ZbweHxwYUIkVxWLaWmrTxRjJPC12V9LSRuGRDZ6Z8PXJtjrcM0jZLH7Be5WEaW14ByjZk -I9rHaEejrckXHlM4XczEpoX2O7BML4oqtlnM4AY/ZbuyFPjkhFPDNMqrNGKvI+MyPzl+pqdO8+H8 -A5IbHpZ7ALPR9bu2FTrcIivvtYZEJyxShlIM1jgKA3ZFn6hU30hq49XZ+zCuyp4uTzdA9jgpplPK -Q2ajgsPBAzL4ml8mTA85vXOcbM6SG7/77Pd3f31327C6Zjrd8A2mueRBU8aJBAVmQeGKzvM+JefN -TFXH0ljBFBFmmgIKg2WeY49aiouP6STKSbnOpBhdYNbFWbW6Ggq4vEHgQ3wklPo0RHWbkMR75muY -eY9jawHwcDA23FANUi7r1v2ICUApprIBAlRD2UupHnro5286KAdezmdnxeLd4ev3/4Wdq4TeTsnc -jGLyudjqq3I8K/+Cf0MzjsKzhh+1uePVnZMrCQUvcaskCrsEJ4C7dzbpYZySM+Cnb1fFWxQ95M/x -OpkXK0DC5jIpNv3k3p07v29Na4cmnZ1OzB/14RAdUu8okXST1RHBzX3mt9rs0lcaGgO/S7Luk0pN -Az8Bd9lXdbKmPRUC6nXc5m4dlxmNqJjlTwyXz7/sM9UzE6v+mwLOISzK8JbkieJ7BgI0wTdH3Q+J -BIigsbkd0K7HWVtRlAhoJiB3J53qcXyG570LsSUF6rhECyyppWQItjRWVndS59pzo9HJE9aPPzXf -0eFiUE7eXvFJGEgMpulRF3YKBf06DsPBTOj8pgd+jqGRuYlKaJy8lztQWhW9VrEXuTFAa1AdlFm7 -6fFZpmJFSSY5BOJp8ltucS2BCPzYZ1timHiwWgOW1ZslvrWOz/ga1rOZMjM/wgGHVOF1od80DxcJ -QfXstuSW65vZcnBvKBbroTgeyL0PvRAdmE6Da/CkpOk91cPMtxJ9RfxQ7EL+CVPTAr2ZMFoGQE+S -1vpRtcwVNmUz2Flvd6wdc/J7gZZajUjSBwcr3CUmhtLB9KEL45iUEuULunU7dxjjP11bBjKNEFae -AA3/SD121R7uDhIXlaSraR6+IAmYD3qPwSdJqfiTfZL6IxxASNzw/5rlX3N1EErbouQJqYZJASOu -jtuWaZOlac+t1A2KTLvCkFSUfY9PTJtoRGaBhZnFnvFnUFiB4cpMLXE6vOh7pcKxcYM4gjPsbs85 -6zlZoTvOxycnq3w8WVWLq3k+nk4xrnSO4R+LdT6Gy21+kp9Mq/ykPMvJmyB30lr3BKStt+821brI -T6rpVQ6QgJeuq0U+GVOggXxSoMSYTzDFES4I/GemIcCfFPwGyufoSJBPp/kUZILp6SKfliv4//f5 -FP5c58U8JzFUt2Y1Mgz0tFrgf1bznG5mWHR+Nz+/l59/lp/fz89/nZ//Jkc3/RwRrUGUeUlN8nJ+ -lpeL5WadY3bCtyfTfDY+gZHMijOkhVmZ0+yRh6Kcp0DMx8t8Pl692xRFDnPY5BivJ+dwNTDbRQVo -WVQ8+EXFA9TtF1U9WZXLdS4bBtpUS44ZlHO8hnyZg9yav8vrXKqq5hzePa/ncMXLgXwW6P5dvi3w -nwpGWq+vZvDH5gT+f5mT0bduvqaVW09z1BfRgq9Pq2qdg0C8Joyxzex6la/X+SbfzPLL+dIjgjFs -SPwPLwIh83yVo5ppWlzmFHk0r8fQ6P14xe16Eq22m3d75DZ6LCxNnkRwxHufS+GdC6k8T67YGr/P -6SAiT/8YQPfS3cZGeAs77PY6baH6uEOE3LMi2Gp84Q8TpNV/wSw24+SkumTrWYyqKq9cUGzEOb4P -G/tayj3F993FZLbBmxYQP4CdXZlkbtVmDbS5JV4dQIahhNpVLmXpEX6YgUcPo3AmwNBQT13C/e49 -V0HNMwcKknlsjaAnic3MqZqj1tv9oXgqhcCIGS+Y137/02Q8OS98kczle6c0Fj/+BJdWJIQp3FRZ -x1SdmulUC78ZD4kCAEyNK5PrywwZdSjmd6ipXlHaLe88YV8kO0V2mzF/oDkg/iU6feTXqJwAadUd -7O6AyZOFb+WKS4P6f8pzsMLg4FihW/PuuU2RSumA8UO9sbhJMeatRNCu8qL+rc2Fw/sRgDkOtV1/ -Kq4iugNcAGA7IuOTNAo9z1dVKCw3+zvzNp0BYuWXtrCe5akHp9Uv47rK2wgyVHT2JnESkqCmateJ -wOLcAm7NGaZM2t/MN5RFDn6JWeqwt05mRB+spj11kFnFQPuQhWN4tkZ1fEwSYQbEQyz3XJxkK0b3 -55E0OA5eD1y2AvgauWfwjsA6anAo+YmvlpX//LFh+daxNUgfWsimNostW/zI0wNK0BY85CLE7wHx -LD1DXOAAfVxAiVGNyj5Y453I3iRpshGxvnErV+zLeN/yjQAPZ7Ihk656W03Ak1sg1HYTOKtvBmB7 -wVU8AsYN4dZQM9y2DqGnBxha6CF0B1cQGWDuLn38ok9o60XM8GStjLcbVmvKB6IVaLE3Zkq73AIc -bSl722Zw20zAIHgbYg7jiGnyIoQk916BeyuGlqhR8YizeVICjJZlj67EbVkI03fodhjnmA0wDx1K -HCiLHR281c0ltrFvWC1koap60QQkTi1IUDjPvq86MRX6NRzAag1n7T6dpkmo97PGo3QFt4OxnYq6 -LVwNbLj1ZLKxtVeFUnrM+iCfW/9NYAwk1/M22HEFdd4W3jglRC78XbdrBu8dkz//KFQNUsgxxaE0 -mJgHrHsu6UaDqKt4yf57i5pWNzmoh+lBnXadciI49DjhgaYni6kYNalsSxYvdOCBzFFyKAGSYgAA -pVLW8lTj3KBuJAkW0gD93Xzj2+PVRYZ0dLz1xRegm5DMl7e6A0DPreRK7j90b7ADMregKFaPkLdT -VcYtblEo+kfg90xCticvyLNmJxa1ARUBuvD6k3FYIvR7PYGbzPtitSqnwOpojCLbFbXGrdbOOcHZ -610OsJ+ra3EJcTomc0uKXZ16kgZDMiwotUui9C6dpgHZyYr0DnTt5osyagzg9kwqBFI40PW6GxVf -u6yvoCt3V9+pxZ+7NE4/+w5nnKA2KBFtUHKSmGt9cjKtkpPyDCTmBHU5HKZqeopmSQlViIywWyYw -uYQGmbw9mSakUEneJRgCbb6UXH4JKS7QAYpeSdC5NAaLlRm4ZqgmToyyIlmvk02CigUzfSDb3vFH -MT16CmHZ6iOYHtdtTS0Q5CAyBE8acEVuRhMezMLr+Hp70kQ9EBnTSMXc8Do7rAUQt7IqEokh0GIf -1vAF4FT1Q1EYm/8BO0K+NMAfv0J94z92ezn+8cCWzmzZQ1t2RmUhpF/Z70CE0ijtprZwWdWNZoGm -AT2Mi9PRqrik2KN9zFyNFikA6K/m4FXzwYxvwH31qTQSxZO54lJSt5bnCQZyxJmROZT4HT/evpcQ -ZsO6peCUg7vDoWDXRbn3tVH+8SZg3MNjZgBHB+lQ0oc9Ki9ZZpJuUJ2OXWKhDJDB3vXf/M8mWPxq -s1gUq3e3X3/zWw4VDyyonJhUhnSNgSoULX65qtYVfEiII6P2WLzhKc6nb82Irv/zoiOGdH1jnIZ5 -+7ie8cJ4CRttv5TgSg09LmddSysDipGeKwXt23KpP+Pf6jMPoFpxtUGi/1bVistyraHg3/z5p07n -RueGjNdkUqZcVp86Wn2OkQndH+NTqDOMpjH3QtlPNyteGZXOvJEQm6PSl4t1GLfehb9/pmLfm5j0 -GKX/WYJ59DiBwHqzvE14sJ0m2bPhHY6JAGJAP4Wt/SHBoR284a4o0baqzd1q2zbOg71iRk+NnOfS -8+LWwkIVQrpPR46n7FbxlqVBwwnXmDnA9xzjdvFIYyL6VKe7sKGOxWRn6gNX05q66xbwweFsPD+Z -jpPLQXJpEdVTFVcFWnOo0P4E3SBQG+k1AmMbKvDXP+1FTfxaWx/UIQAQiO0fKnq6uWfiv0cDW0M4 -s0J9gBvU+lH4ZQz0zH/Yd4h0MID1w3x/8CvtddqCKN+51793WicHh7+TCCfeauHqWOTm1M/FebHI -pesg660EnCdT0Ez+kOWXv/oj2lhIZci5X+Efr/APWKUmoFM44MkRbgek/roYr6bVxWIEGzOzD7rP -YIwuw0/kXQFNvNYOsrMMl3K015Wf3jzl3BiZcyPjHLwLECUpj+3AJrztlzqlsmk3q84YR8FKDqkJ -/3aj41LzVy5mV7Q3GF7LMIZ2PM1sqSTPRlvD2CQir4WjjG2RDtkGlLwPmbdIy5QWJCUQpkv8TA8a -8PO4Y/UWyz7nktPbnKrapNXxHrBYOvB62NHMkAc3day/iSkPVQI8uvQ01yxc7RhtLgEMSDSZJOGO -wMJRN0DJxyzexMxoC+Vt3ScU+SFs7PdEkx/hb2y7qTMucVbX/DfxA7IFsOvv8O1fiaQBx+QfxKJC -GSNIedSbkYNxjvbmJ1VdHGJqqpjiIaWIJ9jzE/oPepimvp2t9I2i0DLs3ECRj8TCKT3Cn56+ePHk -i3R3TmJ0nOb/77AI9dQTJBmxceJkdup2HT5E28zLEnOdWpqlR4bitZUWHG4dPuIDpGU9apdAeYwZ -zcdvCzWiIYPGLof4H3t6YhYR5/sU52oCh/8ZCsE0t5TCx65ZDey0RK+XBr3imwK24GHK/M212uEh -tzC88TyGMZAXFgsTA4agESHj4GEN8T/2fDGN3Tv3y6IGafP2E1LMmfxTyVg5KCzeCx93d6QbbMCE -D7e2ocrD1Tfh58V9zD4BxaJkUhZAGqSXYs448VSnagQDci+DIrdxmb/qlpp3pvNiXsn9KchhRkxg -6BZCRdEcE+XhpamP/8m2xIuJmp+LYhwRKykus2igpD8VVycVjPUp2gmtNst1S1DESNuWTh3OjReI -tz46GwkK2rOryKMVXEqWwfR3mtzZOAPcf8M/B5gxboTCDEZ80TiIlGrIYnxvu7OPAGMES1RIsR/l -sjySY9ZQPm8KzDFbP3TteAMz7J7N2lDPxu8LHBYJnsoSwVt16YKNC2wTegvv7KHznhrDBNvUPZeT -3016UPfp/0gWP+pqg+/u8dHgs2NPJgvHgA81COXooD5OKAFJ8oLt0l2AQD8qxFG3nHaPc/xRX9Um -WjGWvMejDYo5BRk+EXQj4TMNs/l8XBcvmb1a85rOfsZMLca1ihJVkhh5n99oeymTEMc8U5m/t2Rx -J0OSMNvYZs1h8xrUwOn/kCSimeFNfwDXjTSEbrP2qMngNLaQvx1QrToxyjXJdjxE7dCyWK2vMn3h -hKaTijW8KdeUOxZLN/s0k6xP3Exkj33aGTFFxvkH08CFD0GLnvNtdspyVzRJ4eF+iB49MUEzkA5Y -MBCowvQpF6Tl/OayaD4gAzyUX2MJi2AToreYldKxbSod94IDkIDJn/pqr8vdtC3m7DrFyNoeqj6R -bOk0Hi5IauTxE6O5H8zQhBaiBI3hbIKOSaQ1XB5fotwITAJ3pBLXe3vPhpp8sdb1SV6KOIzJatyI -R6SGSHtoJX5Jycq4IMGvOVxExmfFjo24DRPlqSMy2gITPxVjZDh8h8LjVVJrmaWJ2BygX5qS2Fj3 -x6lEWfbZ2dFI9zRaXvmdmZs3LMtLt5XsPT9yvW/+z+yG3OAqTxxXpAO3paHZj0OnIrMB8O143OGi -jDY/J305abDkAiFG/dl4VlcYEZHDZjO6KOKA4ItWq4Zl4/P9vLgittjrG9itqZID1Yc/qd04gLFw -Gq9hBkRop34Hj0QKYhtkXqasL+Vfiql5FijFBjIpA5Mzq2rjHx0NZkxBgQs43vF1WTkAwhAqdEwk -UuCo12gbWk4ko+65l9EZbuSb8czOnVJCM/qRQpJD+CeZY5gVfICelhjoAm1+THwIX96n2cC8UMQv -+md93EPjxJkzlovzYkVGrtR+rACy215/D5WYhwO6nx0+lOdKlzfa2CyT7x9cZy3zH+vYlAOKS47O -oECa1aSkzNsSL5hx4G5OQd5jd5iYn97IqLUlmPHsYnxV25uPHAm55Tu544hBP45HyS+vF3ODG9tU -xJQusDYqgAY6LQOxwpQGJ+Pr0t5Cg3LcUfiv2WJdtKAWWircQxZITOHK6auZ7oKUztAHbL0JW8BQ -FDIJ9qxTQl6cl5NzijaKgb+CNavPYWTjkzArgNmIFF6DzD4novX21qbE9941QIVrO0wI5pGQ2Tu+ -LOKMfKBKtjA/O9ulXN7018go7pgi5hQ31xtZ8XhSccUf8kRfgBTVKKbL60f3ljbu23Icykq6K3m7 -XttwxSbbayW/tuULbdHbUKyERxQaR8KDjLZL/qxWvmrLKmDs9/5IqRkCpQOfpzG57UNkwIaDvG79 -c4lVap6h0KC7h4njGZKywQbJXn8PyYsUq0YEtiP35Co96mg0TXOLs7BQQ1y5uMmRHBOq18fcq9kv -CkyrKGpreJo+hvMyoEa7cZsihQVvtIk4U2DBrBbpiuuA0wZ6HTT29BZpJyLNiOrFl2daNnMomuw4 -ta7HCKziTX7AEddwTmll9Xuz5uYd1ggZLYyaNQZ80eWFEH7LRdfh+N66IdOfFQue7PCg3s75G9yf -cw1YtPVaDoEGTWubka0RsLYwchP4ag99jqf38OD0OMEVpxI2Ll7ukbbh2FWjsc404fdcuQyQbEvG -E2Oy+6EbgZVjyUHJbr96i99XOFcKkhM7kEj7ClI8xaL+8SdlAT6d2m82j5T8neMg1JuZmRHs9PEE -pU5b0wQ7ZUsnadX3zg0uMzYZOScsIG88GIIP63ysUrXWHE5nGqa08J6z+3p8oV+KSx5l56UySDlZ -S6rbkS4UTlvRqaOCSMsc0N8zz622pt5vy2pJr1z2iTJYSzOEoRpB4AbB47BYwGuAGRUzBbN0qluc -shu5rHZziT1aacwXh2En6nuGSVAxWzeMKLOg0DwepDDXzMJT/m+ZXtvgebzBTC0umgQZRj9jyrUL -42dTD0lj1DbXkJRqe/mJU5fjSpqsA6pg9LpWsfAQDcq6BsX4iylTcG5Mbq6R4fJjuQQAiL23sy1B -MS2mI8fZ8ECWauwzJX/08ZCcnI+RHGLHqZvFurqAX3XWAB0lI1NbZItGm31xbV6fhN0Om6COBvac -o0q9SJqwpkNk6xrauRhLiTbypwOHdXMcBAhu8xXrUdyJkhBqDc/GY+cw8JskAtDaqiWmmak29ezK -B9/X/Da2vmZHqRX90LWUINvnBeC8PHUpFEO9FPB7fsacUihPlCDCXexvv1YzPfQPgLNCkA5EPomZ -6unc6KuCes5uQtO+btiLDaFJNgHVDI6j76V4HBhznmrW2/1AjKNhc5zou7C9rMVb6nlIhDP4Re+B -Wa/lBblzA/bFR/wP2qOeIXnOcqEdISc5QntUPJ8Il7CNjK4QZA4jkoUNs/BGilslrMSJj2DXKFdq -vt3WTpOklXIm2AAbMZxwtpuaTacVXYp0W+/U7oKEKoa4yyuM0VRE/fXbwrhyE3qMp1/7v6MDrHhm -JyP92iitfiQbh6CHNtRr01c2bwb54eFY+ZlVAVm4HjZWK2a6OgeyL+tz1Pwln73F7D6n47cUoHeG -YRMkdoNsxVoaoofzasoSKkXPk67UcwZlhKIoauT8tLq9KCdiCzwasaaXBt01oLtm2F+SOqBt1OTv -xnyIFciYJY5j2rFqB4ZkdCaoKYCzzlDGjn6fXJbrrGHMEekW47HP58UUdcX4anq2Gs/JmL1OskWV -EJFgsNP6Nhtkl0Xd20GdNkkmbLu68uzXWmizMdB+5NJmYvfgsHl3iyZebAtx0Jm73eEksYh2G64d -TI6yLozxTaXZZXIBH9ar8uyswOD3CtEWB+flNIjuw1HLnpieOx3s0Wm2oCp+k+TPQFYZ4SfVdz8o -5XVH+wViUsgdSGnv7kuig+onICWuiwEwoG6dbOoN2ssQKMkisqYXJWpo6AZfBEjhVp7K6wAFrC6A -TlaFeReAEol4Yp7QOQsnUDkxL5uJs+Z8g/OyJv8XwqtYY+CdVMgCz51iMQFa6WNwdDMgtvnEDrgd -eUZIXBCg7oD37YF4wxQQq4R/X7GIttFcg1eAthCvQJR7Msnw/itm/NyGazPZrPBNZ3Z1uH2VvpFV -MrrUPxh2K/7F2BVVxx1gXzYoXSTlUbSvHurd4No4EY4jKHHzlH97HezEwxG36DCOtLtLBiyGHzXm -lLC6DoIGK3ddbgZ0I+EqMbA13auBws7LMwyMMhoZM6ERXuAX9rmN3TSQlBMMlEVZIZKu67KbHKp4 -7cQXtSJZBAx55pKdIiQlh76l12tg0ziRWRxgCr9u8R5tZjB7SIV2tpNzkHXgfL8kGbJuWmIBHyPM -ACcTQD1tgfWUPgbmV97k0gmmd1a5OyXlsBmW2PrMORQKyF28APWRVLD24g6fbV4vCKQjRl4S1sAo -beeYNqirVrCrb/Z+FHPXURjFnL8g77dVjPVMP+oWYhuQStS1st2akarrsxQpwMkDAdSGYqHZgxUR -rKbTA9Iiv9uUqJ8ra5PvOW4lYClFhqC3jW8Tj4h+d+fNfzBedoYnvrv7+vIf2M+u3ixptUkdCJ9v -XxI3MjawLOTKa2+/4WLHcFV81o9yPdvpUbbaLGh4Xc/UwHMpg622KciXrF4PU9NAOZdJJEGdbYJc -yvA1k+znkwJ5LF/rrhI8vGj604Qh+Y9n7AFGLyv0y7yXsWMYDB7EC3pjFw8ewaQy2035zMR7mjn7 -euwsqDg+nQLOLhmEEATKz6vu1puiEQJwLXWKMmekA4GGAefkE9f0nK5uNfE0EVoVNCFXjtNtrJL7 -IGmM0V6exwgDR0vJvjmcUQXxfUoZr75H393Un2dt7/mSFRPRWy0kYwY0sqd8X5r2ro1PWiaNzlwE -RLnOwELLYUwLyyMyo7I4VgMf1yIws5GGHJ795Hl4ngC1SFfGYZWpTQODaV9VGwmiTZRGCaeBzfOZ -HxlLP3m0uMIM5iX3p8G51OXsaVubsHKGQmDAGYlSdG7xkvX4cnlKhgvA0hQ8Hi8dZSzDETg8FTCf -+hJ4HcisZmX28Zclk0FakiH91z5UfCcChLAmX1r4gHvLpbq40MC4QJbek4UvjcAViFnWPMpxv0Dy -Ymj9a4hJZprcste5bIhF3xm5iPHyDeBdtqg5rCP3H9Fs+q757IjP6nr8J3h5dKFW2t7xzqvZVB5f -WmxRPWNPCt1vYeuTmkd8UlWz1rc8/MiNuVdzMV9Ui78UGCyOLucMQiX8G9eAGxOaNmKJvg5tm0lV -5gBgUPRNETZvqKr0nEfSpte5nhOD78AQc14w9v/bVVlB8hZpc3T3OE9ekVBIcl1EHcgX4CPJoKsa -9qvTU6D05FZyH5Oup/+c5sex1ka1l6p+BmjchUsgEmm6T1woHonEs7d7ps+3j5FVgIyqxewqu2lH -Orh37ENXzCJLaTj2JIRNDIIVsIcVu9J9v0hbjCyBCeB/DuqtVQ7q9o8HmupltGgIgFHTOAwE3hJ7 -eSsAc1nisMJ+/irJixGSKAW47FZ1d2DwWMHJhoevK6GIB10+LKHU7VgucuEskMGZJzWqgEFCWzfD -1FgBYLP+yKQc0ZHn9ohu5EPB/xhADXXe1AtIEtuwNmcy8Y6gI53d1WAzHh2Nm2MOi3qXLxNHBmrm -92Gas2r8rRDVnhdLcWXB0b7lmbiaOeqDuGQ8TvJ8mY5wW2QhAeQJg5r2onDaE/k29p7bZ0bpMBYJ -JO21zUi8llo78PHdyJh0bSReJ9Tnlr6FJv3YpM6uKNzC5kzkaEN+UIuWQ1VohuPY06HqNfWBowA0 -XkTNbfCToX3KqsNCRzd8HRdTL6w+2GYDZk/Q5Sr22mTdaffAr6lr5dVBgr6gygluuWqwARxgZ0+H -7oZzB0tIQrRfmixQ0VAVuFfowjX0pS+B0WWp2aUmMvX7RjBoufDbesGyySWdnRGwEgukLb3zjVfu -QfjkOKISEJQQHRFP9eUVMVdyVOffClERCKqO+Pq29OKj2rEVEwDF3LYVRarJBdPV1GgLIxjVBGur -MXVvkLSDV6lwIfgWkB49e/765Ztnx0R1HpxgYXa4VY1GHOCdTHaMGK68rD6KDm9gzKX1mu4idKPE -azfGgaP0SsZYCy75o83CXEfrDeV47gTvxUI7zYpdr+slCdlqSn25hfp5tUwkkTZ3nht4lccnZHrJ -X40X9QyfecoFPnVVF2YxONF3USeXaNgbyNtLe604EpI/ZldXZGHMLVCVzd44zXn1GuC2GMs6/3a9 -aJ5xrP+2utvUOLjX9XrxTaB93XF97LdWJm0qxO4nnYgpND/4Xn/Ag4ajnNrgMGzSumROo9bIh7QP -Le291N4EWHOlH/Iiaxy34fYXevcgdzGs6zA/j7HZiysrX/bgeHHr+OvMfhu223lgcJrv6863Y8t9 -ovE0d67AkFhXFrbOHzGL9xqEusB3XzHwPLlqBOTiN2t817Wh0K4XgMUfJ4Y3aQ51z9Anl8796ZL+ -QxsyGkIlFr7FgnH+VN/Rf148evUqDdBAysgAFYaX3Oan1Q8Jq9Ye9Ex22JocF6AYIwwqFN3QIZoN -JuxrsvxpZqbjR3ugOXAaShF8h7/TC6pGgm/98emz1wN69u4errrymogHHh7O7IeVNoEY5TejI7TO -qdNes4l/ZxFOz740MJDaDwiH6JFocFF84fbFKrhzL4Nti1H0RoLCxkKxW2Td88nKwvouCovxfh1Y -uIqnX8aAcSbBNlg5x18spnBwpl8+evo1BnRo66B+Fe1AXvWvOfMnHzRYerjtmnhHbrAYqwereDHq -RuuLvopQR0lDWCpUZJT6hnwU8xcmG0BTEImM8T8i5e41cNwpecKKOrN3L2KxEKEHP6RCo4bbdPjL -JXoNWJSKoBfjXsuq9mLoNRLf2vhiPOjkIFuipqynZ72L6LnLy33mYrmxndDllhldXntK8qA03HKG -RWeffmd2BXrCIAp6kZj0JHE1PeY9QIm9qssjhofJHVteMGnjc2zFpBebAzEZBp7TmAy//YyYxIPR -xoxhcvJRoZVuoarNuxg6e16uxDcAat713C5ItHQfXd6jWACeRvUj7Py4szVXzw31am/LyAIxefgQ -nyPq9RS4Vp5kKcE8nJe1sUxIfH0N/iXpSk0oDWPWwliYQ9EwxeEprtUyakzQJQJhZgB7KkuTelVU -Q6eoPZsSN68z4enGLcuhjRS179FjHjW1oXQF1EBdwvdmvBzxP0CDaKhHeeU/A6xQ7RybquCg2gkH -vngOONRAnGL9Q5xq0mfKmDRtpi+zySEyHAZX7eX4eGP7F7TMNHPfccLtksVcnBtPZupKeZemZ8i7 -gdQbTt4Csgd5yTUMxS1f1GqKROmBk/FIbsrtk/uhGF/PKzFo01FRLQCyZhfaCESuICT7KdspDON0 -5WUbp0+BJsSPMDtSIQq8EzxtZqdZbOZ5YpwkrVsw8yw8CyO9eWycQ/ZIbnKxR4TLdMubgWKG+OPo -94PjHccABlhMjg6mGEprcDAdRALQ2kC0W+YC6H93781/MSZJvLnwAg3otnnG3332+r998YtfxDNA -w/UDVT8c1Vv4zMpYrZFRUQ57do1KN+QdZ8WCqza73KzLmWlo3yLt1TNPPmdz50emAXHQTgdPnvX5 -qtqcnVMoeO12ASMsLrVV9mZVbPUpbsTCv5zImyKpPeT3+qT9jdg6tYpXsen9n8qimacQCym/A6eN -6/PF4ulp8pjyMTt3g+qUAKB7JFz8H2eXPfZvKLAWXIYvr0yEmTFgQpRllLKQSy/7SfIa/pRAKRYo -mehSc3k4eozULaZf/GJ6ssHnpJtmKDex2WNK6oc8yl1MV2iAmJwUs+oCO4OL1fuqnNK5srEJ/y7E -lPM9TpxHQS5IzfFk/uwfA+orgwbGNmpuZHoRSJeCTPsIKsZzbDzOGe4RU8Y/gHsl6+jNukIz3AkZ -EwGWMRQMwkNwz9eU7m9ZmGDiZM5jtMFj1RlAglpIyBRLxnWC+0FoUOMQje8dWmS9zPIRObwHAuZ0 -OBK3huExMmoCMBpBi9HIDUSwgLAUzjnrh7wXNpdyNMK6aLGLcXcYcaHdrlSCAw/qMVZhzJ9fmXcy -IlXpCCCrzsvaAptXRrl+Wk789U4uzqtaDQUjwhLCw1WWHbOAq/8GLXOth07NC2wGMl7BV3IHwGAs -1pSIs4nx1BQxkRH7l/SeTAZ2OWw6etxgG6zTcgUjn2HsFQpqb7tlQDR+7MEOf5hk/X4/J0ObPIGf -rHlEEwsxm55WRY1maKflAh3UriReh/SAxmFxiCXKPQgwN+u0SOiDZLKF3wZHGNX9ak32+yhma1w+ -RvLBHPLoWA1oLqfoEMEuAlRNltXsqhnQDDLm98XsijEcJS9M/4Mhl1ZksAXkNV6QUTfQ69KkbpRt -b0idjpQ17brTYLFzhCD5g3ASigR5juh1QL5/smqhczj6lusYTWSeRpZOCCiQ7TWmvagICyzNVlW1 -pqERpr2U6tM6OEcwhgRHpG20boR64A1MDcJPthFVsH8FgrNUtqjZFjbWd8yyjS02jgDCnplGW0Ax -LZjtkXkCunp21/iVZxl7MFPyOC2jKjbb4OMcmCw5L4FDw46/IjQxB8ajQ0MBDo37C/1eljbFLy1T -FxXL5pzcZntg1ksGqWfh8L/Fgk+aq2SwCgI5LzgQOd9MXMYs/5VIBLF+WTtMD8IgcDWl2OWKMI/5 -qvJXpGkvII18QpBrEqV3hu+NpHxsuHH0mCo+9vN84Vil7eO+2WPHnT3NU+wLAX7uNHO1OfQFhGcx -qLemG5R4GxQgK7vSrLnver6eL5hb9KFcPfe5Ced8tkbuA64OMh7VQt3Vz6ty4lIfepQS0kj4kCht -txhY6un6Wle8t0l7unjfjUKRGhgWtZ2sbuBd4QS1pGTejE+AyK+lbQws5qbJuv+1K5izA8mBX+8f -tKZ7UGcHq17XRlz2pquSG+vt2RPlRkAdk5nZgjbLssqx7CoiC/a1ZvBNJ2UOqY4gBaRxVRazqW7Y -caVQ2/o3PzW3NpQUM5KW7XXjEV/I4BgoCvaJF/+pUzi6+dFE5GNrVK1uYCZmmLLeHclNy0SIWUWz -k0kaP9PMGsM1G9wAuWQ2O0RzVMKT8SlVBm447Lr9QBMzNJAB+k9so8xfzLB+H0NQikF29wEO72E3 -drQxq95VecJRz+Wyq0bxGEq+opsV2hORzg55MBY33lVNyPYVZe1kRWbDvtO7+V7X2tn5DW4kbkRv -a/iq8F6syaJRBEvHQycjER3WSeUE3CyuSQUUKgnvAdcggm9I2svY2QQLXq3n6+xIr+hxbxdJwFC3 -LzL3sv8Cy7peFpPR32RhLdLRhX20xWhRdmxT/ZKFa9yzHOcZzCXz2I4AVOcYYv6Z9XUQ5oGByto4 -QV1tVhSyoHtAr4ZUt856Rk9hLYmRk1v7750uBIx46JvMPNT24v56n3gpovwPeufR7zn1/76nuv2E -8OYqCeLtUuN11hV7aIiFEXX2/34u0H/LCNpyFnIa0md2Vj0eHFcLpTq2LTdbKgS1jW2zB5IJrjen -KEJ7bGaputdMhB37kknE0JgYb2ySpAAxKjd+y7tnbfyrZd0aqMc3D48I2zc4uMdkw/o7FN4wGBHF -ATHndh3T8tO90yMgfcrFtfwR83eaSYgaKm2eSfeiuGlZ25QeSKN5NRyqtXY9xK/P673ezJ5NR3hC -ULjQ0aw4pax9qmiFAbexewv62hlrwj3Z7sPT+F8wtiHNWGKDfhAUms6QcWOEmUjMpC1M4np5dLYJ -aGpX0YDMBn60mO6zeaHavhvXkEAQzrjhaUMSWVQKa9J2RNxqo2w9AvtyG9BuZC9YClKr3tm5g1Xl -3q6wbJEt180w+XuXjq0uO57p4ePzcLdnY/U8X+2zUs9X//9C/SyLBGjZtkYUpix5Q0H41WPPcNh5 -WxTLMQWWIjyT9r82imD4tRxjTB56JP5RXmZA9AVaSzBhcBepTjEV8ubJbb2nGDlijfWyfw1q9aTa -T84cgV1IiJpopI9W+Noco6omZbEKQZnlNulLT2fofvb2IJ7I4b6TgiKL5Tq1uVm7ceRd73/bCfN6 -B5Mb44cdK/hDH05/+0NF8goJWZvN6yiqJ7vh8zKyHfaj/0fTqdB/FsoMtxpnbE9tiFebk7aGh1sb -frOZtTW8ubXhF+X7toa3t/dYtc7xYGvDF9VFsWoZavtY43yA1+jvwghowFFGgF96jbqtjICmGYfE -GGjWvg5TUTt254aNsh0cfDeXCbezkb3h0QwAoMxEwft78iUSmmmdPl5o5pn92+Jvaqc4VRam1ECv -wL1uwFLX13aYOBrbVB3qQUihSgyMEEKv+7HKi+udiuEohvou+3dWg4gpVYQZkL2Wn3IyxgbaZeP3 -Yw57rzfj6aI7YFg8/Z8i6+dVz7qerD22gnYz6IAfR2DM+ug/selzRJYVo2gkN1/jZ01h6VPD4z7u -8+2g2cQvGo6P37G/S8et/BUmabTkCisHU9TR4WshothvgSVH0uyYJhCX+s14G2HrgvW4NbSDANk9 -78ZUHY2bybidbbdEJLCddQ/q4UGdkxJSxpibEfT26pwhBABa+L6KKL4aNSnKFsd3iP3ci7e65rJi -u+7WxXSQI4uqcHgTL2HtyxbFGrVRQ48toEHXtAVf0x0Im7ZgbPqhKENboO0om+6Nsw9CGjWa7kBb -XH+YHdS9pvaQ+azWHGKEzMhV2l8VmkcfxsQm0TD4UD9t2Cv/OBoc3j3uRNCw7WzcpT0EedpnSD/3 -Q6qomQhn6i2EyQd1EFp3T7JDTHW/spNpPqfuEHa7GFfpxwMkd/z1E3EdDG+ZJ5EHPRaCvhL7pj1k -IKn6t3kFiB7AVJu5KZ+6MJztz2M7iWSvy/nf5Am+sZYy06ypvvcmr30qJCOCNZlLKOC8MyE28kjO -BsiUgZKSXMIycQzt5gJkXfPAEuAqxzcBzlWV8vtdNyKIyrtmuIqmZWMttzzl4TRGZgp2OY1Y3AwQ -db3V/rTLHY7Vi2tEek71/e/EAUjR87I4tL4fNp8qhaq2Fhik+zGPDuSQsde7A9XcxwSEvHOizAK/ -9Lx6UWZxg8P3s2cCb/ZxDaQn7mMP1LSVaYfPGwj4busXAxNPWh6P+9zZ+uCgOugNdt/1mV/E3WD+ -5pdqu/bl2WLPtYea+6z9xx8UO18WYqvY7/fxH4xIFHDXmGHSIUUoZuJaG0v1Mc1xbvwJxF3RQ4C7 -urK6lKximlptxXo4yPcWWybo4tivv816aQ/LJYAQM1yKMF9txfR3PjiFIL8o68l4tdcrqFT9t0uS -DTo0WVFw2feYINbbZ3ZkkQp1t70R0vcGBqCw16iGwXfM/NluVuKx2HTy0ncwW+q237BQc4HeXGH0 -5RNvLeg8wjGLwv3r3+uDZmzrKm7WmOKRvEYzuAShYyXFJiWxCx3+lI1wYbwC/cu7C9uW1YJtHXDQ -PGk2VRtczK6F6AVBf2d3e40Kxq/7S6qgaE0Ilcx8s9qmLugoz+lGKEvfVpbrRrVuRI9O6+bxg6j6 -bfted/tc6nkOmlTecLM8UpfCBlXFMxM3j12l3gsvodbR3lCAdU814S8SQw0t1Io0f+PD/wcS2KMX -T5PbyZMF4DdZwrV6XUPhhwPkBB9mIa3cKy879TlmmiAkSvzbgUktgeHmQxIQwhIYXeT93Z6iCQlZ -kJ4B0hlEmsuPSNIIGYOJRHy1LGom6dfwszfYn+w9UhQHL8WFPobGjMtPSGbXIm1FkOzfbKMuq9PV -xGEmFGIYZsf61Co18/mmWUikOTntUoywklMcrQoSWJDTN6OApTbTFZGJyQtFvA09vNfJtORkJBQS -LElebc7O8G5YLYA/RuChGzheNYXjKOv9k+IUIwOIsIQf0b4bDvPDQ/57CFupXPSiiUFkwuyEIAFY -5/VZZjJoDyIkZnOCh+42JkCfoyobfY+SqntpuW8IpVJYH4obuD6h4AjrE11hG4neMDlz7E5EAOYw -5mOa/MpNnD1LEEdGE+ZUZHDzXp/07W2s18dovyYnziX5eIX7HepHtjxnK447K12GUY5Z7MK9SXeO -LLW9yNIUSCWLQw49LLFJAIyfBuXSrt1HygOIBTmBdWwOicqlmkhWmO73C5XWl+od3TlGpWKaJA8e -GFtJc6j3WoQFBMPqThWYCpNxsNZ04OAEwkKoeUXNDObwUJEiuv6tbmA2Sde7Gl/y5fRyfXT3NwMv -cB4WisiF0t7fWPjYfmbEjoufkW+HskGnU5LvLq0Gajm66DZXYlYjk0xQnIZdgIjTrOka8WsX/jny -9TOXvSG7jHifLdAFu+ti/2Yp9JHcRFg4pl+nPf2NWG7WaxZmp2Ief8aJcO8EdU4Z3JltWwK27usa -JX5vwMYnOyikxnf8T4ox3Lv12a37QFuzarxGAEyBsGwpsR6/3aWZl6ulUghDZ0AXVbWsu9KMa8AJ -licYFP9untyLf+HB667m48vsCCHCvI9pDvf9sXTPi9ms6h7hdyKBc6/X7tnmLT9dnhMW4Nu7+2/+ -HQcqeffr1//6P1CWpg7GBSbsm8wk45nNN0gOdS+u1ueYxA0rj0YU2RRfnrtIb93jjh8AZUKhclgs -wAp58gZzmREDgDs1bCPA85SDO9ZeU9OKIMAhP5lPkSVF66j8Vtt2AiuMUVSgVEX1BI4VOqNPrpLu -kqaVHM4lvmRXJ+W7quEs4pyIlM2313GHdzCJTFKpLavlhkITCyZuJjZvDQb7wExQkpCml1xUq7d1 -591v3vx7jbd3v339fz3kRZHpvqAuvgFGe4aRlU7GdTlJMMxLaRNac5gEOF1hvfqdbNJL/ljNoHby -p1Xxtpgl9+7cuX94787dO7R+OtZNVZs/TdQbFxMnhvTzqnqL9XC65xSWsliQGERiPbIJGhm6GHRk -UwBzVUul8plh+o3kIVLRXXxwu989xlhgtCdns7EkLK2A386NSIYuABhro6qSajbFiD/z6j1lS9ss -z1ZjuODBinY5IJ3Xq1xhMdzDSJYMlf4dChh/Wp5xpqY55YqWQFVIMguMy8okspyecPRsRMZkvFxj -fkObAxKGl67ny2mJcaIWb4urJeXUWxWTizEwSZCp18UJAEcCkB4XGAvEiblngEcmTYT1L9jV5Xwm -it5ZdZZMqwn2nfYEg1Y/9Hp89hoFq7ZkPo2U8evx2T2MGuLiQthvdEVchRYV/JyHqTDxmL4Ty0Ww -iLrJ2qG92pxIxcxk+1OJvimfqSQ8lGowxpqMEOpIUg4ZpfjNNW0poF48P4adA8UcvKknFkkHDUe+ -VDZJV8bLrMbECTSuXtxyjQeHihYg1qOD+pju1hn3khuweZIOBCpOtdfbGdHCrNoR1j/OFIL2C2ax -tEaRFL6tWMtAGd/8R0gqlhz4h9dclq1aeStmS4NIDu6o5XrNZCNYjkIgecLjH4ZTDNLetmgOTR0Q -98C3o842JKLkZ8bb2E1Asq1xtdC/nmccYgy/SCAT/4OZHvyjoHKMcIF6MyB3C7Af2SL9kABkWeZX -4cLElsSBbqxkP1zIvXe7Hy4ls33kCgO3FANghHvHmx9OLIJ5PHM9rY5bW4R7T5LxNjnbCINh4PNr -EBLHVXDngjKo40/nJb0PNj6YJOSWaLIeRxplWHOaVNqLdjSalihf0X1dwcWseXAFWLwvVyBvUTTE -F39+/eTV69EXTz5/81VoY1KsVnJrYCWf/xHTIeDx5PICUMDNrvmAx+5mffq77h5mudwTHKtl1Z9u -lqcUfBOhGWBD8yNqD2tVEZFUGmGyBodbS6PCrKwaM8ApiiUwuj/CPy+L2fgqOzKCCsgUy/kwGgzg -DBbAZKPT+EcaGzTCJ5LUg8EzfTGixX5ZsuVypQzb6jgBuutcEkjzrpIckiBdorVTg9LVPUNsbAws -zHRIcHoGoFNku8R8KCoZUjDddo3g3CW2nJUWkm/wZIKiN3bbFnNY+8zoljRLzZAxmRPP2gy5176l -j4x1WTA5qkhsVHBtppW6JLFEC9DVj129L1eSweynCC31Taob3qoKydJ2qJduGI5eNE+yirHTXKjH -WLWFAJrHnN8M/h8NFO40EWckL7TEsAS3WURJTohtEShUZAbCSqPWqpbNmlxQgnwCtWi+E9qBs8Se -RZc7gng3cEvfDWSbgP25MfVZcOCjBgkZ95JmKgxuODTE1dzTmOOhhSS1sbwabMvWjkizPvZ67WKt -Xlo9eRh9dF+y1q+OpP4wyzfEtq0RqXwaMvtIZkYsbRBZwJGrCFWIlZ2Wl0OzHVM/25i5P8eDdqh1 -lIqqNWfNwqDRwEjigokOJXA+rjWaA3r28mgLeg7oiiEd0PuY02uTPsxC3CYVuTykwULHENE+jaiC -M3xC2OVi0UbCe10g2mCwxGN0GPhQrbO1iiISXwCRVgEbazhkxnRt5m9O1AOpx6447uZVgAck9KHL -TulJSVI7kuWItseOPQV1jEo97zaYqY6VaCJJ4mBDqtViAqPJzCgVGe7F12++evrsVRqLZLFVZLDd -Uma6NdBUDcBhnfk5dWe+WdLlLN+ewVHGzye10eigXMWgRgwrR4sTNq0Ece1Ztf7Sht9VNPKUWreT -yY3ku+++A7zXwFjHCWpBtN0l+XBThMJG91mXKenu3VDWFTkGg++HviOyQ7wQ1ZbhNBmckoiOfjto -ROqTHqJMNVHpYXZIQkaLUC6CTJNR+doeqdA7SqBZVIyOLc2evUYvHzbsO/SKZVagiHq2iwzhnWoB -U7SEisIPP5NwxEv/bsvxT9Z3c/gPBT75CzBeDjeK/z26OzhuHlvYgFKkHC7TFrnbdU9jBFgZ9hAd -oK1hR+jzXCjwCGpRDdI4SULNo8+adHS9493OQUlrSkBbxD1BozLy4d0tkmRzYDjzLV5JTeaEDSIY -Rc0m7jlBqPmTX8Eiko+dpl9TJuwX9kcjvHKORjHWaUfAdQN4saFKRR4oWgeqO9aaEhaYaxJ8dJcJ -kULgLpH5lyOsuitUJiddzWa0zVg9FeFN0j3X7rVeQ5sd7neQBB/N/IOTtqlUkzqsudsiwxpYu47c -9qPKuyvycPmmaEB7tdmWlKsFQ/gE6dTbjzmYufSz76Hjpbn3VkEA4VHUu6bdaOEEovanY0WOxstB -BF5JPhvNvts0pp41aZptnazsHM332A5RaddsGgiTzcIK4WJ+kDkSpFyNvd7O67LlL7SHo+QTZR/I -D9T7BC8pP9sY69xcsss0c+rA9adclHT1ksRu+LZUrIBpeD2nQPgUc33Aj09jeltLKF0WWsDwA5h5 -j+VI8Otzif7tWyiZC8t8PAFcFqsrjuiO1nbrirMTlGsO8H57XCfFeDW7wtfQZQVXmxPgrc2kOdeZ -BYYn/1tNAvvCOdAjazCFcMWayht9Ke81LNi4mYsbUdoU9tv0S/aBN6WwvSgfKtUy7BiAE1PlgCAP -914GkmMlj6sG4d6E+rrRw2+78ouzG6RN9/kfuySfGTVYf2TyJXHxT73rdWX3R7QrGb/flRfkmPHB -86Xo84iSXWsU0f1As2Z6o+YzlJg5o39WhFXxqWJEABpO7HbpHwbtIpSAFGlRMNZnq6qoH7wbg2dJ -yq4NaK6WRbmZNvplXlYfSUUUC+FnW099ZdRxHIfZRAbxybAefDfADE+PD1RvA2LQbPe2j+t6cyiR -ttcaDOaSgsrJL4dCYrHAiqQooqXbc4KiaJc97o00ZFaNxKbyqhpPb0qC09Y0idYcL/juJxUmKYuL -mi8eeC67ay6d0k2smLyPKX43loL4W01xWjlGZHgw8Rg1m7m/ORTnclUk0Q3Gb0bLDmHoffOyl81F -r2YafIMx2DGoSSZNc830DJtTGZr1iHee9+otpo1bB0pwsfwYCqzwzNX6b9c7/xpqCP5A4XDdTzSx -wfWD7p1ye/e4dGcyMjOmoJX31NIPXiB0/OZqXZ5ejQojOMocrJUzU0FEeS4JcinnDFMzOnQSRYeO -Pldkw5li5r10m9BoKnIamFRL3uilqkyZ4Wggv1UUbVBhQWbHeSNvKwf+HdqDZCnxBdy3bs5PZb28 -OZQh/dd90IpJmypBrw5ZL8Nup2SwmYx6KP82n6dcWrHx4ipbFWGAfp32lUyTBZCxzPh+EbvnND1O -nj57/eTls0dfU2rah8bdhDPFbml9OtvU53pX2o3ukqjo18O69RGr3vaK5ZJOGItEFWDG3MTZaAHd -Yck+xb5G7NK4qxcMzwCBko9cx3LHGnn7JzSlBmqExzRPd4sk+kod1T8ii1QyjxHvmh7rESkV2wJV -mxtNtyVaMA7WHJnYJBJcZhaFig1bgarH0DagddHWtnU0siI211pkXaKa1lmfbCKnGY65t80GhUjA -ZGmZNR4dZkprpcRu/10Rx81b4O1FoGEVMI0zcBicmRaC2URH/OO415TjuZMh/5PzjZItFNntQh+l -HadtkqHzH+rhUI58Vx61oKewzMHrkor8J6qUUeZJo0bZ4gvCXsbaqGrnxl4KlRvGV6PXx9xmotkd -ouI1SDzq6zxwJ8uDlxq4wkunZeyuyrGxm7ILa3OhJIJ9uIXzdXkBF2ebh0bsWM1dvL5t8qIkJjFK -xNzKyk2xJY+aYTkhjeha/gw2AkODOvwj5lMcM7dSnaO1vPtrd64cpPZNTf4sUzGqhdkccPZE9m6h -FK5qAL08sUVmGg0CSx/YdYBzzeBpeLB6iCor7jXXk1bHmdkswVAvzstZ4SHTZztcaA4ts4rLahm8 -GFkkGw0tF8iSNNKz4P2QvmSY760ZMIozWO3xfCHoM6x1VdTRsyNc0bY0siY5VvRRv7W9VrbKgLzn -dsGFpnK1BBZ1SpNDofAkVFW5MK/TFp17nLAM1Qsu5ujCFsdOobhc4J6pmJFS+Hra72xWmsZRGh9G -SNiyM4mL27ki87BJnZtpwvFzf2Rqd/Y4Rq1oYzTLNuOb4VPcJ0n5fgUhVjUkWiX4U0kx+CdZuDFG -QNim+txlbXdSHKr4glwEmcdR9OdCVc1DB/oMSRVoYhFyZMAP1EfA8qTaLNbH+6DrkhM3isWKzQmM -rm0wEtRl8ZCOurbTLuU7D7wV+QSx1pKt1rbGiBLtgecRU56GpY1SsKm2eBIEG8R+RTZv/2jY3epa -4cflHBdlHrfIXc61ASlZqCCw1Dcbs94s6Jph+oopa61Nk6llkKE1mIGFlEYe1x20Tc/wSlvghglA -KUG6b0/p9J5Cw8yRagWhVQkavlk1Bmc78Y5b+x5KHcKmUp/tJbYB43wiZrl4QlrzQx66liXU73ZF -mm58PmnWM7jyLOSsOMZ+/FlqMhsvigtaI7FBbVyPCVosSss/oQZP/IWtGzmmuEWIB0JVCb1doRDQ -tPQghGsC6bl4vQ5XW3flCvdtBJExkzyqC2ixv4PM3s4YZl9xK7rl/P64eO8khukDN/HESE82B5KX -DNH3mbjZuPg4CTQYEXCMvqdCCLIqeQZuU9TOKMVhQ3pb0nd9Has/xXDcPayhY7je+EyNqCAfkgm/ -tqtBhBJgaJIvnlS3hokzNZnjXo/oXGN3h5DAtmpSSDadqMtlUy5tkUXFigCdEuvzNNdptdLDw4cp -uhCqWZ6iqnwWc79qTv1QT12JqR20fML9ymcWCVANb1HnRIo1Dfv3XWHI1YWu1T0HBTNUsYKVrKWM -u0BT/yU+cE3Fl7kUwO0Sfr5HS6uOVb1oaxVaMxAzluP1eZ+Ulb0GlCO8BROlKCCh2QsBykhxJrLA -VsGB6zcdxJrMl7h0IrEZWDzfUDJF7GRgojUQPHVzk6GbS/75rLgUhaYj+fLULo4dxMjTaAMEu3x4 -97qjwntgDq7NSV282+BgcBeizyq7l9Kj9mRV0Pt1cgoUc24DVjqtRthVG2V0jCxFw/E07lja8bWg -g+soCQNwgfGfp6nwHi7YY5sfkbOIutU4UPjw2exvKDwMqZ18rrfTOOo8bJiO4rJEv/FpQZFCGOQp -ei+OcY6H4s9mvdpXGzZBIu92tEY4o3zuk2o+x7sGqb0NPbEEh1jHlgCP9fjjGRGayfiOd5li6vAp -ibeVgsU+w0R2sp2giRFRrq3OIvY+JGgmLEUeYmRlHBgjXzhP/Cy01SEVEn6hObBVT7LB+gmVvQce -QC9ymGL+3e/e/Cf0saZ7zMj6MYMI9O73r3+4wx7sX5aUxVxF4MF4WhsxCzGKHrRm47c0FYxTQhAl -cslJHr163e+8xmz3HOwlkbwTieu7mk0BPdAFANggH+dwBcrf3fwcw6qFju52Ms4n2wQaaOYOzVti -VlIgAmStUHONzTzjsH8Zvx9LEFmsY7zTyQnwQZLdy5Nf58m9ngne8aookvP1ejm4fftkc1b3/4Vj -MFSrs9tkV333/u9/y+cIhiMih/b086qaPV/CuZZ+Xi74B6W94Z9fj+cn0zH+enr65JKKvoBLY8Ni -I/0a6BrzIWING0VRWvwZEzTjD0mYSD8B3U0oL0Hiw6/PNnP859Wa/rKXWyoDPklu71QPDub4WPDr -azxA5AI3wqh8POMvRVHwRXFKI0Eql98vaRPQLItZwR1yzMpmL482Z+ZTkr7AqwL++LKiIX+LKjlG -G/0Jq0nw8URqgnq9uuKtRaNeXX3JUoX0DuRCkIi23K8vgQaboJ6AyENrQJlC8ReGtaMhwjRpmTE9 -GK8Gv0QYDCFNjCjsIYme68xc4MbGFqFnr5EUu5iISKH3Wo1pPZyr8KisYV/SlllRcLqmxE+hkKy3 -sB3ByCVC1oAQ/v6A3PBVyI49x6UkEazAwQRdTMw9BxWFwsElDSc2wT4jbJiYCSB4A5yC1cE2LhWe -QMgLibO1XBAVoxqmaSjrT8YYnSwMNbQjaqcL2PnBwfckhKeOt8e9vgdkAXuB718UwOhsqCUQNtoC -4kmTPv3rXn7aIitJ0KRrxsWTf/8/FbJuUUnUOopEZMJuVaencD+BsY1UhLfrxeDyQ2yFEbk8ocUR -V7TfXiPamlmoWMy1yOFt6jczR4gfqKEX3iqGSOTNb+/obPQha6iVqDjIM4ZF1gA6bYvo5gK6bQ/n -hnYZ+4Rz82d55/iakd3Slshu6bUiu3U4+V61AlF2iS8uNonc5+X6+SoB0v5rmuvC7yoq/We/9BGw -Sij9lSr9+tV5eYqpJ9MHD1TxS1v88KEqxoR+UHYr9VP1QdFh6iXho6Y3Uz+/HhTdVkVfzqpqZcr1 -B0ypB2UHqujJOywZDlXRs2rNpb/UpV/zXLySJ1Ska33FU/NKqNZDXetFdUHT0PN4WmNRWXtFmOeT -SpF49ZcFFS/8UXMpP9SknZ86nQ0Kn42lFaBY78DrziQLTf/VK39jVsIvNUsGpdiXiZcdHiLc47T4 -Jz403DFrK+GJmrC4A/f0s1kxniM/PN3M4HgFaGfMlpmV4AZPth2/jTRppOsSPkj/6qdIEK7LyYgP -MlFw+xLFDdStzzBsJh8mF0UyrRZdtDp8jzoEVCGX6OPqGd33Nd/ZJvb4p7OLmpz5ychsoHVjKThf -lsZVYneYf6NEdqjgyMqT6rrBQvxA6a0Ze/2+yFQv9rpgr8nqZhfouEmltU3ii0a0P8JKx/ugD0R3 -VGym+2ZG9EPUf0r0KU/y3Pcnb1pQMRz/BMjSyZjpcjEFkZW9dkj61b7udu4SR0DkR8BEMUyRKNKm -NG2bSOX0gbqke3k9HrKfmDawpq01QsJ24vGqJXoRb0OKCh7aaTOYb9r9BnXMebSoP6mmMU2y7HS+ -CvjAKXFCNAJAhECdYYfmIGGuHNQhSL4cE0KAVLn9ZgqclHPYsFlqZh34zujqCQUkM9DjYTnNe50t -VK2F+ygxUx872MF2Wr7B3A+4NVDCBieGOmSgvmg//uOpEcTwY9RqxEwyZAVbuEWwkqIasfbY86WW -GDmtLJZSIlnvg69lohJ/vXmd/QS07E0NVAMi1ahaGn9q6qFa1jyC/oQGhbJWaOhL7byOqSTWsXTh -c45qOaqv5icV4lrLc0fV0t3Mj7fwagzsSP8nllghHmwH+2cBDefUi+dmSxuZjetGtlcalBvCTq4e -0v+HnIt5NHPt6OMz16qV/bjD43rZxmXTbbP18jPd7++42+Kt2pL6p9nLrm23dXe4PNMrXH5106UH -b95zHu+hstgTJ31QdKaH4fW53wOov8O0Bd3+fIzVycLGMJJNpRkZCEWktlHKLq7TR2aE0tDz1T5Z -a5l9Adug1wLpR6IXDdq4RngOMnsSML2deSRbqZfRSYCHPMNdNGTMmAkbOIcU9SnyH/q77aKeGXvm -SLJJ+JL20g9YM9H3y6LRBaynLe44mf0wsVezI/rVj/NqQWjIjrkwtgIGWLAO/szNKIivNjvYKl2o -tmmQnynNe9sNCfbitpTpyZ/ivmLJHhzyGpsPX3DM3isXVShD7CkqUNMgYz0dBX57LmoHECSqj5/3 -XDVOSSHtmyO/13LmX+/Ab8wokmVz77O+cdB/iJD7Mx/u8ZT0f096fezMjdDmIbDK9RdX7HQVlWGT -vjOoFUuNUfupQX//qNcUW6fJgID/pKFsJMFjeOjAJzxwqOtmxOigezJRiG4QCocTtjT+rDqJuHM0 -AAYfyJELMske2STitq8QMk/G5v2WNr1tQ/dqR1cZsWHzkwtG5O8PxYo070dcs38+/EinI3WrrYcH -AtiMKLZePlYDMGxFNDKRi/fBdByCd8h5TwOEcdSufwQdejD2wjhWTj8FGaY3BcfXxZPXcAd62JDw -Y5ATc2xpQQ3mOf9EqPlw3OyBHJwQfysXZP2MxgAsT4ZwWx+MVGb0Jqv2O2jY0Xnd8cx3HL3Yhe2P -rfN+voP25s1F/QlPQyc+p0EWYi2qL/dQP7cKxFAbT9hl5BVu3/N45DKzE8HFlYp6LivxrnHqPrq5 -uHKjFkyba6vGbjVn8uNjF9ZpZj9IS+jFog6kFWuGpLzJt9ulQoX+ZH3JN9uvq3Ho9KWH62tlCXaA -uEDY5bKodIH9BodoY/9KMJksBpsAxIYQ7EtJPM/b0rT5GwnBdOfykPAJN20DW27j9nnvEuriG/Ya -urbwkeajwOy3kDeSx5gIyuj4KXhgWXO4VzJ8ZeJtV/cbw65G2vuuTV5Pca0i+16JNp+IWsJ88j8b -0XxM4vq9uPu+Z8XPfRIIr+N89sLo6nq1DhLT+3RGJVFWhE2DfPVkGeJD8KxEiPnC7OnfZCj2r983 -AwmFQEACEhDNOCNhZUltr4uO7v56cHivVf0gxirC7ho4aJjtKJx8+rz2H6dzj9GBGm6EGEzW9lzy -u/sxtjcB2+GiFnqA1jsOJ85Cry2XWjBtgumaFPJN7Z49ebMt/BSNnhbVUI+tz2XtbSbVbFSdntbF -2m/nytUwi4sRV5LBCkKlIZA+8NTa+Ob5o9k1jvbxxEYSsTqwYzveyomjdgfxxFO+vUGT/2rq+JnV -TrqrzrvBm/9swoIgn56OZ9WiWBdzNL0v3v3j6//2737xixu/TG5v6tXtk3Jxu1i8lyAYnY6JgT4k -Q54/vHr+5uXjJ6/+0OIucDKui9/cN3/9ZVae2KR586U1O4LDkk2590hjJP2HRkJuWPLL7V10MPWj -FmO0K/EYHK/PI7GPTAUbw02Atkqt0ca3km7fjL57fVBehmMbyAm2cHwynt2TZN5WtQfAA2zdjj7N -E4qQaFw2XKDELdRtdoXM5cjADQLn1qPl27OGEcHWyAytoH1stnTk8m7Y3WslKL3sknw2yDYrRs4S -OQZzJ0nGosy1VHEkLeJDODbgNssvt4WBm1K1ZM2GuGLFipqGdMWzbFhPcTskZI5B6dbChalDhlNM -iAnZFiyibo37E4AS22FRzLWl7ghXkEIINNzEJSBKLMZ1KwCfBHpRF9Igm2lqspmm1tq64T/0cJhk -n+XJHc9oCLCVSpRAizq4yPcGifcnyleNQKvl5O2sCIR+xZn6lO2rwNS3k7JMMf9psajRyP4EeXKk -IUOk3AZ1hky0Py2QrtHeMGMWSyXTgiBkhkf2YonTTZ7YFwSUIgH7A941edxXrNbwZv8hw2UHVD3e -jhsiuW96Z4Op17GRWuEKRqRv9H6mpQCi7Bi4C//w5Nnrl3/+g0RrkmnR19zqZ3qddw/e/E+U/ZRJ -7N3w9f/5n0xCWfbpQwF8eQVTGsBJvCyn1ikSP0yL98WsWpIf7WZdzoAc1+h2J9xlUwN2oBFsYnS2 -oxCB479cHRrvyHpzIlXrDoKjwxHIJUlenxeUthZaHuK2RQ9KmFdyAt1dsBXraQmtkO4PH7Ip/pwT -xdRo+boSZ4hkMkYXBhhsBVfelcuw21FQK/b7FFpG3HYieXEpdiQCAb7q58hVuWMxQfrd/v3+b7sd -4yloPQMZEZ3ODczuS4GtAcfkQ9m/aS/fgC8YO9ETZg/XAobmU13TlEK2COj+o1k5rkW8S02NlMIu -90fmjy63A3oxzQTRmTPpx+v58MeuVOgOTA8/0e0SBgR7pR7+KL6H4wl7y1YJRrZi+WqFbrFudbFi -t15PARQwNPgxwD/yKIBlVZeX6Fy6qLo1JXZmsmAgPHoCQz8HXJAz+XdBypuWq25CFUa4W5DjDbiU -++vKaiGQ5dVArV43h8Gg8fQFjp/McdblSTkr11cmNxELkYf3+ncw9CB6ZYME6cgqR4tr9I4Z41J4 -06ecz2+LYonpiIF1nwKR0oKbfoQhdSm3dEKDo5+5K0bJfgbCYutn9vT1P0vvHGwdw7yh8mddLQ9n -uHu99VrBdmBw4gKNkH60DA/VPNUERTv5H2FZqg7MR3dj6cJ4SO6I1e/Lx4GppNq9LWezrjoivXb4 -EX8PqJZq9WW1eltM0cez22x1Sh/xOWGg6nHrnwz1CL37k5bt0TUQudLAFKsBPFqWvPu6Xk1XHHQH -ICT6r9/j00X5WMrdRGzlgfus+n6BrnIkXnZjbdTnYBDI8PZaZqgXW+P6/eJi0g3XCrkofRm8er/4 -9vFjdpV/gX35bTcrtdJe2/+Xu3frciNJ0sT66GV1IO1qL7odPUWD4kREEQkyWb2j2ZxGVbN56aam -iiyRyemeycoFkUAgMzaRAIgAmJnd23P0qEf9Cb3r3+hNv0VuN3fzSwTAmt6RjvrMFJHhd3dzc3Nz -s89MChRuKYpnWLJZstX9Dv4bFjLVPdvBcNv7iun+FD0QPESG/jK84jGc5UeX5O0MuotnP7ym6YSE -PdMpDUPW5K4hAT6Zn0XEE87jlXuPSbpYXI7zqFLPscNZuhR2EXPofQa3/LyrBOVQRezd/rXhxnmq -iJ9DFcVgucCU87bWXA5VDFz+GlKh5G2TofP4RRl0LW9pUeVQ5TCOu7ylNXmiXJBDld0to9JB2SiH -Kj321Ya517J1fzgJcukKAJ2EfSPG5nzK0xWEuVpqyMNJS9YQlLb2rx2lvWy6eOy4macrSGQMNztQ -EiKoosgHwU3IoGg1F4AJFkX4r0OYJ2dN7fZqubsBLuKNW/K7RFVCEO3zVAs2UbNZuCHMgv0jBThR -ZZ8s72MmItkhUef1D+ogr38+N5YyUt3wCcIIz38wd0SfliSvS1Qlfm2uWZaJ5EEJP1GVUoAV9TYs -5SdqejOyeHXXMqGcqBkD6KbGLdk50d8MqPRMrq9N1AXMrY4uCnmigEvUVAc62rxlLShRNwB3WLq6 -5nEDKtHr1Aq8dlv2ASfq/HWDd/n0qCXRL9DRACfq/IZr1zegZ0nNkksMighaZp4qYhODQvrwiAqF -54Z3YoQFUuwe1meuJIRo8TBRSxRmJ8J1MlnAJoaMsF6ud9uj1W5r/smuqoUNw5rXq/0ijoigqxTT -46DwyfzD6WS9BUgGyaSFAbOmr9+mxBVVjjNp3gC0E5YLi0kmLem8eE6JeUc5l0nLYttZXDQsqTIl -i756ke8vajJ5E0QxXH4H0RQ2uV9YArxgqIXNSZDXOwGaeoyMKdH7oBaV15ehXJia23qGQndLDYm8 -+tSYwE15vclTayeJJzZXSMTNDegU4CS/qSbL7O5m8fhqe7PInOxOJG0SDqBpbNdkNaVTZA01B8Tp -FcF0vVqTyzC7lx/S9cE/ue3MDukq+xvRSuTp7C5d85bGkFhqY3IhTg8ukYtVcId9kJlP6AoP0ZwL -0CfMdlMjmeS4FhDjC1wx4e8pIIFNjQySfa4nYpSr7PDaF8I0kVoFuHdDEOE8kX/I0YVPbCZ9h+ZO -JgtCYzaDL9XIYPJkIZ3BkyEqhFuLVkeVo/TgbOksdJkohLfgFNnYYYXX5NMXbz+c5u0FOINf5OW7 -d91FIIMuct8g2bQXoQyO1P5U9j598+Ffjm1gLdZaf/vhv38QamGfDp8Of5H3Pv3qw79y0HNS4NkH -wERqudZn/rUeX30//frDfw3VhDqoT89P/7f/7Gc/c/Bw/GsFqJT3TQTxhi80t/Xy66fo8mVj/TRg -Q7XES3OW5xgeIjcMNQGfLo8Z+IS5N0QFvWlAb4t1PYsD2Qe6uKK/nTTXkD17/Cp7/MPrF9nDGXjm -r8GNO/W20tnAD+/ePn/5/v349OW771+/eXb6MtM4qAhHSe7/Ix7P0EzNDL0JNstq8fXT4dt1tfyB -+li0GkJEzTDC9CBb14F3X0szfBJuK2mL+jXIjo4PKv98sWqq32IZLloGYGLJOVoRIeHsZsf/lsGK -goxAqLgigkp5cZ/VMxWJw9Xc+/Tiw7+R3XGzWhpeijqDTy9P5/8an3Uy9VUecW5W02v4LSj6E9B3 -D7NMGzhYelZR6Dic1Xis6iw2AGRqEdxNFfCSY3E3P35UeT9+zLgKGNrnGhFVrip+Nwd5s9pYRGSA -RF3N6vm9IGVSOEZ8walNQfAKhXfGz/XGXPNPeuqd2TY4bIsFOEAjEzMHCig1LDurFmHZ/YVMg4Cs -XvDTVEsAwqAZLPGlzZiRF96IADW0Ws5GAdZ60FZU7IDG7ht8eucGwMXyKp1zejWrN5yOGZ4ZKsZV -rKfspIUItxdVtlvOVgCiiiCsWyQZpCO5ceBTpA0NCy8vBNNcmY0AFPbxI3f840eKeDABDSRUNqtI -tITX93k2sUYYQDF+6ASGeKUO4R17JqayZoIfw2xh6+TEKP1YrjKy4hoKxROGHU4BxtF0m0OQy3Bo -EDYXMaTrP0DUYZoymAcP34y+93rL1dZ0AlwokfwhaBKZEqn6Hb+gTPjahLs8A/jYzQxUWLwHHgOV -gZnTY17PbGoY12XVFeIniknAdaUCLjDlp5Kmt2CJ5hv5+Kj9e/an6waMFaZlIgtpCAHKGdZilufj -R1OR+Wl4x8ePWNHHjwNgn2zk0gsAahw1AIStM5KmmgVRr7qrzeLZWUI+vph9RuyQRBBYWjjfEoQG -Qy/KVJTwU0zGJHKOR6YFGGMz5bneUfgA225ZppdKxT/TnaReeIXaoqa6E40ZYrRkHWuFe6hKLRc+ -lvOC6TUKwBr8qQAgl/QiGeb0uV7tmsV9ar2C+OZu1qL4FzyWNlPbYGWCSPLpQON7lqKdhAJHsOhA -Kr3thKcIrY05KP1FjDeQsoYgCxK7MGbzkHmrN4PeJtcjUW1BcGUxZEoNwqSfiX8cNuHRVrr/+2nL -pygY14AXy3J+sCwzQ15VgAHE9BGSBxhfsTdTFJN7P11IU4dTxD9iHpkY3Hx6dAAnvI6FEogHgSWo -0AMLUmjY89lIWKAf9GjCMtQhzsfHj1whn79kowjB5a7MSWwu9xtwN5jMcLtOd5sNVJxshCjB+g2C -9RHVjEVts9lkBn6E5oi2oOyqD65Zb2EFstNBc3rhP7kdDB/FprBKpvTmW2qifx/Zso9UiTPfPpQM -/Hhjulwd7DVcui8lfjW/8R7YusOsGUZb24qhUT+lE/yvZjqBYCjBm43453dWJsssmEkUesJrMv0N -SPkgq/DlY8FY8k0nF5d4ys3kc8VdCcOA8m5zGdj+FX6enail4m9qQwLJ4FDUgEm+bR0mCVQexd+u -NnjVMuWMfLYyfJbNccSibYYVaa/LK2vCpoNX+LE2WN7TretZcmJX0v5US2UrtD01v30sWpljaHKQ -9XHgoZsNdoempIvjmRaiewFMJki+oZQJswjf7XHuhNQM4f0xdCUr1/DyaItisWY7mV6bvAA2gaic -GAIEIikYblKbHc5iVDWfg7i8Wy4qFT33frWD27WRsDdVdG+2FxVsCP3F9awjSlEgO7nw6CwBRKcK -ZyMsvZQ82C2aec5CyTi2CZkhKZuYzRADYDgZoX1YwDQ6h5UcUtItSh1r3OxZbHC/17reUSdEU0Vk -bQhwP5ndM8+cgYMFWO0RRqBZ0VnVmO05owATh0xq0EErziQP+Whq1fY6hIU5dmUddVT2SCiIc7Ry -hnSoTrtfbd6ylXvgne7Tqw//SnRQl9WSIjZ8+s3ps/+cNFCsXUWGZrbSojqaSwzTI4iQTNAErMvF -WClsiQz7yylae9bRZbtaoyVjoUR4xNdkHk8BSiRIL5qx2RHA0zXBsDluWnBBDa19YTJB3seeL0kN -YcYvzPxU02sjsW1Gx2VLYO+LwA8L6iIgBPDHXt+Du0TkMQDAoFQ3zFBb5SZXz0me361W17u1Fj5J -ZXeNATwLmStzjq9WW2Ln6uhagysm+ZVeDfGPojwDiBXJLR9LH547H+Y0mjNp4NyIQmd3w/VuU8FY -UZ6CRbnD5YBKzl3XzBKOWUWmF1HqwoBy0ULbqNBPm83UhV+FezvnCybPbH+yx/WcCKG07bXXX9hg -NgFYRuGHbVqbw2Vy6cdZXN/PKQKDK0nej/lXZpXDzUwuPLI2xFPdClFdAbCN9NgUOkfChTy6dwqW -FDLSHIsvgp1l/leczibbiYtyxc4Ms93NunGay6dlIiu6OlhHB0gbZP8ulZF9IMgPhH0gIIeXF/5h -N4kiR1eR3BsTpPeCAfEiNDh74iPHnXZEocOPYEYVco/yDndrU31VpKjR60TrVEqkAeJuY+J74nEh -3fQnPB6FzUcb4Ga9iLiTOFWVQ8Nx4HtR0t7LY89GpDl+JAI+BxV6pIxfzX+HHCGpyK1/Yz7I3Aql -MpKDicmGYwxCSVU9761gMptRHKwCQ3oIeMblZrVDGEz8CKInfgFfnIvdJZkus8MNJgxdPf2jI3u6 -gLPvlCKYNEaqhug7rD6ieGDqtarZjvq6HHgImLvnqA8zqWLqwPPDqM+x19zE2sOIKsgmW44FRjpg -YpwSDaM9/JYMXzri4naZSlB4KFQvbVQF+0kpYqV8SHge28jtufnXfE68v28MkYBdW8FDEueBIfaz -LHPf8xhcTuBxcix+BdbD4NxlLJ10ocfmE7BNKePMQzQJKcJ0JqsnvU+//fDP4R3WGrZ/en367b8h -weLCjGx5NAMtQ4P49kxWuPtNgaNme28+wu5phr3ieZm9Wy2X99kP88nSNH91U8/MPdl3+jk6yr5/ -fWoO8CnYG84S7j79J8Onhml9ftrvmRQMNwrynbLhH3jG+eeiuHffoqg+CSdgOqrZJTy7aS7VUWKL -D/cW8mVSvgJ7giMmUBGTRD/8RFMPvGs0lzqogeHp7fFaHzYnNpRIYVse6LYeHQ9s7fZt4z29yv9u -A75u+x2liSIiVS4FMpJwevSjPaqt8zs2/VrN23w+HXoPb1yVnYOGBjpZUOFRZeYiGTAo6BVEsxj5 -kSwTzUgtEnoVqpLC/K+rm//1IuJe0jXETpqpobvFhhahObPNnp+ZQudeSItttUk9Ek11eIWgvnAo -ZwqFQM2/37+DJx+KRRGw8aqK5s4F9wIzjqh40Mw9hK4LaILCZAfD9Go3SW3VEIGLipZJ3LKJvdRN -WwaO5FBpq/ey1VMZARXpYVlXOlqoOUVQ+EhqguYE0bp0e/QAsO7t6rpaWg9mjuVZY0CuQEmdjJiL -CcMpGFB06o2SzaDEqAIoSQTc4OZt/XT/+KdQHTsVsFCT5K2ocE1LKIHKg/oT6TqE5pPz67qEh3Ah -beSG10rBqwrc4YEA4B6cpy7bAhTgde085tb6LaO1P7rPS39W0pqU5ABmO7MIU5SVuLKHm/yh22Vl -mZ4IxVzwx7m/SO1KFzuuZUtFP6nzWCX2nFb74G63PWRxU7SD06c3yWNKGlAHpHd0u0o5LJ6tFPmv -D0lA8Dve27swYy9aiyZ1jGNrJtQ6nRSpyuUaOpBbjNqQkL+g8GtUaRkE4oCo975yLCBSUUKRezlz -qrRmyo1UfOGLlk2L95hUVzhfQG/7utO6kVBta0p+Ae2Zi9r23i4OWgCmaE7WzlM/HToJLkhPNAlT -CgQ66Z6FL1iQA0I3JOdht6zu1mwFTI77qmeJKTGH55YiFcDQIah2Os+YkZXgJ9Mr/D47enpynuq8 -LdO+0D95DK3tQcdaFODU5RyQ4B42OSIeSwl9P28nBxztydExKORIQ1OmcER8SFC3mR2TsVtao+5A -CGxAcfWiEPJJxIHS8wd5CQEDN6D20cuUyPrXLVkfZBeLyfKaYhymgiSeHBic6kG05WuSNCFwIhjd -npF9On6CSYNv53mLVtfkOYapjRrxWZwNdM8NDesGze6L8gB0OC12+JM1MjfL4zJFVP2TrC+qrjRd -0aHzt1ArHTrJ8C4u/QvebFo7DL1K9viA1g7Zc0gHcHqbXQK/y9ZwTUxe3MthTG1JtpgIbKbpjEbK -VaVvkd6xFErx9s7tCXl4Syq8gj7YNRdrZcqKWE22R9lx6kYanpcH3E3bAHg5d9ElJ5WHIWOJ2pet -9TouscH1PDDg6RBqO811uP1AC6Fa3H8FTt48VW/o/mn1IC2X0MN6wI9njZ0VgK6PrvYcxsDrRe/T -//zhv7JeEkR5n/7m9H948rOfoSHJeDzfgasZRMwmReKleMY2CQg89qcY0B2p/kOlnu064dem63tB -HXQ4Zr2eJTWLpNOQrQN1DiQBTvnh/vmr8ds33/3dGIJ0TpoM/h2/+u7Zb3ptXhI2h2nxCX0h0YIM -VEUTRQBg3h0ddI5GGrq52W3RcomNbq9WixkZ3TLmLfqUzDeTSzTEcbYdq6apLxbwDl0vZxWHcw7M -iGU6pqvdkuChnrTpB77CVzUAMWfg+pNYp9cwRw7CW8xguqkU8ptcumROGN8QnE7mIDd9jPLie9gE -6YFe/KLLMlpqmpSY38PXREctLpwF2oaMA16esrse+D30JQh7Okc1FltzKUSu3mzLPRWf3YnEguiz -/jvn+QGNma3HzrhD5Ux9yHBYFPgxJSKDmSJ2K32cUqx4W+NJq3sNmMZJLpCJ5JA76UQmjUOnJQeS -EN3TF349dHByow1J8yfkOppVZWKFQee1p0apobAfy0TU7epuSx4PNo/ajNUnuxURJuyAs1LvyxGV -CrrPXMu3Mz7p2A9YCZqrlanbMjXLpialbfUAIPx9mmO/G6iXrpfbcs+4SZPcvvKmVpPDUFy1ltO8 -IKnkuGwzvn1tVvLOBZmFYg084BiOTHyCTEbBnq5a98vWDuKIsaQZMvUCqY1+QlhLNTmLatn6/rEQ -jWpMNKoFlsSoDUNmqp5ldWuB1PkkKuNEy+HV9GJ1J6a287BTtpgy4MR9nbDBc0XkTOOzjmxvzbQv -xFFHG+4uKoL5U3c3U9XN6nM1GyZtFd3g4aAb2IlzgyWmhfmyX2bWWheWOBh0C5uioo+0HyLVCXV9 -w8mJOk1yK+uDoke6xvbF0iulTLj2LVa4UmsjcmyMSCBGtheVOXOqUW7OYLRMxF/ME/Msz77KfpFe -0omRTtb3YGWFVqjR4vqvyNQMe3BjQ3l2i0KpWQfsjxVgwqWlom5K6G8VIGZO4g0n45/lYRvAHsNZ -QUM2txy8/1nlphV4Met5x9ahbvGfjzL5l7oX8Ob2jcRnCa3Nn30ZwOaVOkZVm8m/uMeCZIpAX49I -iEivxhcylDOe2EeHTWv7xIh3+o2bHrnJW4wm9lhMzhPzb1sH7N3pVSY3H88CGO0KcEKwid3NhSGw -ggTpGV0dnpQH8CEO0eI6vgET5CLqd5l6RNV7OjkLVNlPmIpCnRQZCqs8Gc16QjOR3Zj7wc1kEfI/ -nrpNdQk6ZW8GgVs7QqL+DBMX5slSnbIkyWfNDhr3xiWm63NgDempfpD9/ve/hzbBxnpnLl5o6X5R -bcm+2pRHl+DsqtptjCheT7PbyX2TDYfDdBWT+6yCvptquBJbsslOjqJQDcWT7JfycGZOFHvilCk9 -gZYsuMhqh54KONy+UvE+yI6HaLVIOxqPFdneMkc+JjlcEyEcN/v3M77flNAPe/qsSTzhUANmP/rE -eXSMLuyRTOUA0WKlOWllo+M0yvgABzNfLRarW6AbYhaAXgvzCkDa9+i2CxbW7AqB4sJUhzhWfcqb -3RoOlJoIjw4ZQmvyPnn48rHut2vFrivTpwnADuOtvp9WntLk9G0wK2m5j1V639NNs/awCpW7rfKZ -mMCcB68zn3b19Dq7mpj/bFfM1/UJAb0CqzPwa2icgwj4LZk+X66SHVBOLbkZ7t3d3UkePfPb08Fc -LTO27ZUKwjck73//PkPVdga7cb9yO6DywracVEcX7+8Nl6LlHGRvP1ebuaE9/tNpj8tUO7wiQdef -qj0KO6t1h8o+qyjqtN5lYLlk2caj1LVEz2d4KCQI0GYf1g0+xIB2qe26ba90chhoBoe9TULOR5tj -iW6e9UxIi/g3C3W4BR5CrGp4fyLeEr8iqT7oo+5isZpeO3+wxCOSYd+xMHcH2Pc/LnOnTVHbhio5 -OXfzh61IYVZADqX1IqDbBxQswpXptWnLYWFdtjI70vpzqzTgWz/FYEl4VFopL75BSR0kxFEV/on7 -es6f7UusIdFldrkDLJCJiHsESMAZ4TzxNx9w6nrTgFfR8sjdx4ZZ9n530QAgwXLLc08CAXrDTHwd -yOq22qSaE8wEI8PW5LpxYdJvDE8KO3GP54NhsLsbk9HwtAvSQMHGu9kttjVxqA5JwRzwF4SwTSjW -pGDeQF3rDSAzGA7PoDK2wp7Pq4hLNlm1nQ7X629/klBMdzePAChByKA85Jqgtjd7OosiK+HiyfVA -ChrtUFcNTdhKBk7WATurwBdIK5mVg316siNubdHxwaI2VE8loYfIWwkY9hj9MPB6t5jcXMwm2d2J -jVJyZ46hSTPL4xAlCW/psDq2G2929bbyHJtkuEF5y4VZAza0K1h2thuW64iD9MAPYPcIeNgg69/1 -4xhK8ZB0odQj2cVqspm9hpeAzW6dwor4KbEu975vuhgzB9j6/rjkCK9JjZedHArkY37ap1AM1FKN -8gAL3p6+i8nlyD07DbmmzRgS4uwzc+IbQdGIIfV2ZESdsSmK0ak6+DNXOaMXG2LQQ4xvxP0UDuzv -DDCyBJ2R4W1bgLapJwtXgrjpzGz1hbm3REyZ1vsxHrAQ94DZKjkNm00M1usYU6vNtxdlbWkMHZC0 -qb2klNoV6kl8H7Cz02714rKQzzSin8LfxXHJdwHqsC8zLQf2XdvWMJwLrPVYOjjwUhN2jOyo1//l -w9kRFDa5URzxQpXxK1nCZHKMAv94HGb1NYGJOU0Yq8pskxcfiClg3z77Bq1zzICTpm/pR41UXZtM -V2fnJ1Wxi8+Z2nUsQLWzKooW5d5cmfu4fUlbcoB7L2JHShgP1KF3TCImI8YbKsqz6GqzqY4ArtNc -7ZdbZoIZRvLgeKTA2eGsYog+PrP9SFDNZeJGdVLdsUtB9GxoUuhwjhdC6rLRtLP+VzY7TOS/D+5I -UYECmQb4F1s2YgSSiwm88OKI4EYEPhAYytpuTN+ssrrFyVNTi297tLTSZJkoJD0duUEmMtmtaH8n -MgFsMmWBX4lrA2brfrMBgsn+wr2ttz5FTVfxMxsqHEH8D15TU6pGsWAOIlyBNdc3o+zrk4TWAqTI -9f3XeaOvCUT5sCpFmSGvbLDhSegy6upBcPhsXa2/fvI0U2HMIITlbQUHUL5lMbqjki0dNkwzR4hK -QP7oy4pE5PnkugKRLbpRIxmayVIB2vrj9T3UJ/fqdVPtZiuOh5dQd7B3pkwEh6u5QKfSM6HR81Dh -FJTWcYkgH+S/iXvqh3o77iVqgsWdTsz0D/G/Xg+K44EytGoGLVvI0VXvQe9Btt5dLOopwng2V0ZG -ne4cjFxjcvSUUDKO+F9CLkHSbka+lrlNKgmkEGU3IlYuTtnKvBxiIJl9dqtlkIEHhwPueaCrpbsf -hP818gbOFwsbE9DINRn4f260eLapIS5y8MhAxikrwrJQbXpNTpb3gCC3MxvsM6jQyC3Sk5Ma324F -ZAq0ywlMqNUTsUz2GMOivj8NVQKwYDswfmKd/26NNxCzoxBV+P3pt6HseehR1vgLQwT0ZfJN4+6I -WmFF0VOFCoqWxp0san8FLsY9cSa28UnA9EjRzzv9LmgBcrJCuw2Udl2Jwlig7bFSQRMBvZJfVA4t -cWbfGfp9B4Mqy+uJEkgF6OSJUh1Ei8HeanBa4E6BqBAH9AYD9yJgST5vhmqzICxykpPBzEmBSHJJ -dEbfYHC8yhlIkILmSw6G50nYS+iQzx/tMXt07EVJbXzsiVat6HjgqgD1ZGONz5Jq0ddvW4xmAfMk -AdlrB4TL5Tl/2mYxCTVGSk7gMNhqD8s7A1h6aAqmJmQYxIoZ0DXivsEIE5DHlMps33YxWPHURPEE -afMqjM0RON+ovCe9pG0vL2RaPRQ+lqJiTUzY/cdSNxAPPkOYlUyXPBLa4URWfeab2nIuHJEbuz+D -2w2BZETbKpqs106/F5CVraT/Y//Xu8vLexHOBasFgJhqcF7brS83aPoxENYCrxTU4I/MQmJiovrJ -dknPjvBZTrYzQfNkNTie+k3f8utQgXoSeT/Je05gBac9FKq7tdn928lFE5iriU3lcBHakYtwGu9M -K62Dfhkf1Y/wCSGW0HwzugScltT0JBjryHyKLZHqZquV4UaUD6wsYUoBIAI9v6xgw2UOnjgyW8YM -wSWUjG1Ao5QqkefEdzfo2tPSi5i8JSuUGi7l1rLHds4rNB5DsfG4F1cOQzUc2fxf0ZAfzLg0vyv3 -e2z9Qes/wGZkIAbyEC2knVjrgtVl31giaBJvPLj+Rv54Sa9TGJIFum7EvVlXfbKyZesLLz9A4l9H -2fF5O4UrTwNL5M5L5oQIrtWrJdHsGV/Dz83AXvD+jR1feATWkQldOqIXudq9x/HyVInZJqnyBd7J -hGPIq6B7vF7NE4XsM4K4IBf1sBqqz6ybKA8aQXNWn3v8tggZrjObH57Cj/BN057qD7JnM5LN+eEG -4W9hhE1lOvhyeInKy8mSm0Jrj0nD0NZDjwGIaSs/r3kEdO4DC/H33qfvPkCQYA5y+en70//jv6HI -wRz1Eqd3s1rQ4ypGja1mLlIqUPNEThCGvRn2eqbr2dV2uz55/Hh9v66HlGG42lzi348liC7E570i -qI5rhOrAoLz/LvPwOnoKMmzleSp0uiHEAX0R4eMvczpxJFwu4zbpkLgcORcQ4YpSietQpEakb5bF -ecAcjd5OELl91wHY5WoxI9QwHasdLO+5BwyipOCONBQ1FgbgFsYNytkVVM4LhVcwJwRICi99gTJc -wW8dszNXAVzH570Au0614+LpKrLFClwK1EFFhupzR6WiO4jqtAlelfK1o0YKIB/Vx58RpS+cjDXJ -c2s0sZCmqMC5tOTC1bChiCMPMdgIe8IRbLLgaQK7Q2ne2PATrYrAVkGNZdtY+6aIocjxWEN2utow -SSpidD+iNxu4Nk3rALKz3hiKvRvZdKJ/Fa+cFUOc4TxDSPwZ7yTcekYCLcwnujFAeeXNxYHER+Ds -gpnHY5fX+XcOMjVVdh5g/qW+SBRilZywNsJvV3o6ZFTmk1QgToxDVqNZtWSAJ2brknWmD4PszmOj -9FXci9xEOzbkIYGYoYm33qF+BVjoCx0JHO1aUE/AORuP230JJNN1haO1NZR+7xvraxhCkLvOesCZ -XA/ccvBdf3tfyDQMbJW+B572hiJqJJqBK49HrUQySKopKBWJgx7623OixXi6c0TgGoJpvMt+PoK5 -BGCXDYDQQtwWIM2wKpzbBC4JUjv3FjO4PzP2KNSahRhMVvwOcbqhg4Q4j/A1KdGIDGL6ZkrBQqA/ -8CdCgZ+0wtEGGOMx+M7AmyQ9Zel++Q4leiWBYyUG0ewu+C0tf9gMHzY5Y035o4jFNCNShOyOq2oj -oARCg2J1XBhYNlV90JxR1kM9oRqiCZlAdgM7SXiBMbdkhzHyt3qSmARmc5KPVF3wC95SzBKqZ2cf -ohTRaNl7fpinZX/XCUXIj+Rzrw1FImbef4719td8UU+acNW5X+miX7bUwliHVhxoH9CXkEY7eYTs -xULTJA5ZzT7hOTY261jsBStOi3tY1j6OcpZR3yw6tCMLZYuWZWcTLHi21Z+jGJ0HlTNMpu8tnzwv -81/arZ/h+zs9vgfU1M/4jX0R2z76NaSKe3Ntj8uk+7qR+xeTP9Tw0LK6WUNYE0JTsM7k5t9U/BEY -4G55vVzdLj0wcuHu0mqavTupgqAsg0c/kg7CE61bTrA16SzIgRJVlQl7MxANv6JayqLD6Cqi7KDN -ADm8E3UALHCp3+HjFVacPtlT2gZ4crsfZF7W7Ka+vOJnYQlc1LTJVpcRLyj3RwfaF3HEYkN1Cd+9 -fSyJajkAHc5hkCdXYu9qiMYDwxFTICt6hjyS23y9JZwOkzhb7S4W1RG0CZ4nV8geUhteA96gEyYM -EAUttzf1fqU7UsgbH8COBDP8xYKfT5db0CvxCyvfXcjKFe+js+wWTHelPiAw0A7qiDjmatVMN96r -vr2j4R0HfylJEj6AKaoUJbdV6axnEwk5W3FCAp7brCIkqhDnUEThL8CGCW8L6QP6gLtKpAzTeCE1 -P1/befbvEPit5CdWff6bBfPOf7ch9DWB5IYz35/LfCwCABIzy+ar3+279L5z+phAqA9hyOJJuFM3 -zbu2HQwXfT4q78rERdiIbzQavo2qOfHuox4+Z5vEIL0S4a5lWKmhPBqZIxZkBMnUcli7/sFxDXQp -Z65dwrsy7C3PElJUG3BMgvnK4nqAK8oWVnhHHGQpCj6TqtAP/KM4DkXq6OqoBPNI9pBHkSDvsvfp -zYd/xqDPn95++L81gAs0LZdfPOjA2sSebxbGBYpaFU9FINIUdJSwo1XwU1f7ICwoUnTOnckJ0R7z -9JC5UuwVsN6hqGRrI36ZuwkoiYHD5pC1yZXWtIfHhOgChuv73qcfPvy3OnKvqfW6msEr9Kf/5fRX -//xnP+uJqPQKU16ZFBePdALuhsDCNzs4YGwESjyLqCqJy9oTa5lLPNezetvIKnDMrWY7A3dByGN+ -VpsNeA/Ce84ElPeLBb+LmiPtcjlZ0BUMJhfckXbNrhnS0qJtTIVOAIDzPqs35PGApv2m9GRJiu9e -IipxAq3nZrJprpwqys2Cj3bz8vevT9+fPjv98H788vfPX/5w+vrtG7NcX7cpYMxUIQJRw9oWerTl -P5YAeYBhDjxrZGAPJlPaCBlTwssI1ZouwGmxdsV0DTTYu6X/mXPDP36CrYh++ImyBKPM/fJMRIY3 -1zNIKgLw7XcvT//22Xeu3LBaNrtNVeSkGsyD7BRyO5GdqCqR/eW7d+nshvI0GuG65shPQM9+3CeT -dEKS13qCAaw0rXvtUSXmv77oSYWnV/Vi1l52jOmFIwp9V6I0ZoIuh5bCNihTm2uPNpvE0FBuxw6y -CzD9Qx8ol0tVYqRTFORevwW3460V1D0RErJdgy2a4UrAwXhHM1sCY0143UY5rF5vIQaUukGpAqPM -/XCLO0Ts5fxWr6UtM5wvds2VWp75LKhsiOZmK38FbS5zbzmOIvvMduunhWTxsPm4YWBSo8z9cKSV -6CxWpEu09whymR497eiRyWKjjjjF+lx6QduHetG/vVDGrY5TWbOItEvTXBFUwkajGUKyR5Z7hVyr -/RdGU3xleQuYrih+UrYUnnMMBObLHIeF0sqUAdhJSoAGK2hlIWT9gF6jPX6skaT1opa5ONjvr5aL -+yKBguzNMQ4qdT44ykghfzPVhtDfqjupJJmkMA3iVOGn48S3p963MUosrsOK3dxO6i1FlmaWAx+q -zciUgl9++HYM295gIA06os1cUP5CeOIge+IRv84dUaNp5HevX71//Zs3z757+aLQecvUIotkQNz7 -d6cv331vCvvlAPHx6V8doFWOqnPz49foO3KQDuLlnbmWA3m9moDnQ0FZ/ckZZNOb2aDLd9zizLDD -g6EOI9TKX4YgOp30vAG4bU/CFD2eu4X6i+zJ3f80D+9rqgoLAYTFT3rtm1wzonxzkR/MJQSPmP7S -YXk6t42roGN/wP2L+QdoHoR/jH3E4KTHH5X2dH/Jwyrob/KMCPMQgitCQAUiEUvK71AxU7iFGPAK -DDJ5R5XDihrUooKqOLiTSqg7EYbIGy4VHVRyUF2Fp7U1F662SOjemFiS5rHsi+vwBWMNWvUo3v0R -hDiw9I8//ES73Pwc7SVq2SJoWJ3xvU/vPvwLDMmzGk4na4AJ/fT+9H8d/OxnXZcOdyeBOZv7MJ+o -za9XcnV8j/ZVr9+2QnhifskVleol/IOyX2bF1wO5etBynVZ329dvCymn7TDgYKC4ROSlDZsoCWap -HiwJoXsHcYpmKWs03vycgbP7Gt98jPHSTHfAO/rD6aujv8qDs1h6O4z6p67gNDIwW7Zz0jbXv4bb -d8dU01Rxrp8+V18wT3TAWJN9QKqAKK4X9/IsApDPEFcoxDbfMz09jOVqCIMVqH98cpLB5agGd+1j -+g33KiOD0h9wa/qTvSW/eE607my8+ANeIFaPcV4n4nnRHFEgQyB0dzsgC682vkCPEHMjQmxph8jd -eXWL8AAYEQZHkISJajCcKdehriQDPNrgtCI8Cv+0qmDBIQr8nL2TNoAR8RoBnrgbZvF8tmlDF0/s -Zh6e8o+itOgQ0GRos7rM0NsQvGNakLiQJ8soRnZAmrO7frmnGlvk554ZtBi5ef3EAb+Czua3F4/C -85srH2XmZoIjApbN23LUx00ZONbFsimNwtbEv8IoNWbB5nwTN20V3tD9WzmveyqM82oxMylKKUyK -N0XrZ1LneQCGdZuoDzFd9ClIH/bZIYEyAQ6jQg8t8sN5+z7lhRMi0/eh9CzYOshtEbBmkMWGCf1Z -PcPwzagE4DFk21tzk/u2X8ZSgaUvQyw++CMvVUBBvEoo8s2qz8vdYkGKfPPx7fjdC0CgLltutt6N -wLukzGfRG0D4EE6rm5ftUaHj5faGeD4AI+jtOyOXvTIc6jWYk3djX0jP9XTYe/0ga6HS/4T91x1R -hDlbcbSKKH441obLOl1Uk2W2Ww9Y3CSYPG93os8eu8qVSZbkTQiTdus82MVNb4SIAmN25c17U1XX -xZNOW4n0FH/h9HItSUhD6Yy6NMPhutpcpo98WATM4QDKOOj9alNfwg0n1JqlEWmAZa/beXfZyY0S -K0YVWlIuo4PQJJLY4F+akpcyzi+Mn12T3ImBLsR0fl/s5vMKpgFgSYDbwU88wdXBot4bIwApPLsV -maK+Moe3ChfIajXP5j0dcxqPR5s+E4EV5RNDNrZTuZjtu0Oe0sz94Q9mezBqEK+ikQpsSXIJyXlE -uZYIGVeqAnnC90eoxR2BXsbRpRmMXibb+kKdkHq0iHjUQBEAgwaQc2oREngaCe5KlZeiWyABK5HQ -rFazDt9WZPVzX4+553na9agLn0ccJcULQ5/8c2eEXdFDKgk1+MOcfXN4Oat6LdgHAHygLjcWCo4o -q/01WbVmg/z2LwC2KJBv7Iqb3AgNFIwJDv8Zno04LnF+tsUcmQ8y3DDz2chFDgh8Vflloqu+8svG -yb18iaGoZ68Cka6MV4hFfl1g350eIJKqyY2qNlQYUAYwOKFfwQVbRjGyNQTsVjDnrMdsfLNCv9H0 -xYrcR81/OSJ34bVa+pGLwipjbHnPCiMhQmBrgJ3luTDriWBOi+n+QCmwo8MOxIAM6r7JAbJYSWhz -+K1oPt6LTALaHtkTZlhDu7L81k60wXe/mC5AAC2miwbfQ8F5VR4CohgdHpsvNujKCqof0PtYfN8Q -AkSek0Eh4+B08GzFl+XHCKnDf/jGAzvcwfZBuxINrg8+OBXbJrrI2uyEoG9G8Dh4E5VHcFYF+f3g -RuFlPNGed+9DJ/RF03Wsb9A/A7pUhNPafVjLvAJRroYwadsiFRPNrUDPzsWIVCA35ixbzQr4pCiK -qkoC6IOxn3vMe8zaUpgpAX2gCZspmwB2LByGmF+hyIe15+W+exT4/k0WoJK9pw71Y80s+RJ7XN30 -B44/nAe+wBJ0HgrcRphi9UMvMb19PD5037k2e8viv0lymkXrBFyKcyT0415h/5VROsu7A3//fBQ1 -z0nJ5mkIkiPRvFc4piBLPO7yvGvWgoTaAkG32+A7e7OcrJsr00EmiwYi1d8YgdnIYCIAB4RhmlMK -eOgufSnKw5Yy1X/svYRA2s6Yzb16UfAvJaKeXpkTl3IyAA0+whMXQP6LX169OMbJf/XiaYBqA9bO -yyWIaZPszYfvvmMVFBR5khXojrABaO+t58HNcGG8teplSeoq8IplM8wng+PB0/CKoYIIAg4UQd/W -DGXKWCiyI31km8RpbyaKNXJmvvjXTX1XzVisd20tx6Hqjv4UnV4kJqzWEp8YbUma6DEFNmBxqCIH -DiN9AGLh8KlkObbG09z6WW4+KpNT9RbksoCuVFlYO0p0WUCD6rLgDMWZ8LPKJjMV55SU3LMIMl09 -AGIFq6KhWlUuIGLHqtZgCX2xhxdRfiThWZJarkhqItZ40qJ7jEEnlZcnsJkcd1hKseIqWQUiQPvE -0AK7iTl2E8P/DrpiP7lp2zdFEaEzHclig31AqC39KVNrKC9+bWfZCQmua966e7JQS4EmK4cshYgU -3U/yHevfvna089zaPf0nXDvc4DJjYMDxk9fO1zcD34pfdAMxCHhU6kXXfLdq7PbSaEKXKA3rvr80 -WtQlSsMcRCp0cD64qVrERJPCBw9IyihmWzUZHPxZ4uRvZfBWJzrA56AOVOkiFA1KNlNdrV13InnD -UXOKTlsm2Vewq33VJgOG2zCQVlpWw29GEWObrKebgex7m0kQXMA0gPTiamApUk83wVHupDBvYXoe -TA+LeO0CpRUkyYVsNc/8G4hd3ICmAr8/7ftR5PQumqJ6upMFvqLR1CWM2UPYCpUxyUXooS3UhwdX -w5TQjqu9nKqmkGTcQ3vsjOFUS20xGBoytbMoY9sVvGvSLd4UKxNd327MxRUQF56UBw7Mujtuqlgz -v4jF9J8upLuL6n9kMunXyyO8gNz3PbEd5WW6ajJ672qZAdIk1gPTH8jnw+y1ufQtFTr9jZn4ORSI -Wv2PWCKzXhtklTVbVajac4qBFUQe2i1nAP4PXBNUdNkLJ+tnBcQL0PcXRkacbMufJNw7OV6L+R1S -/ELsp+wQ4xwsMlu72ziHmQuXo172EsIMMDxfMISdE+5S6gwbvJQpqx/dwxb5qUVuomGEsqYNgX6g -qES1pHto7+W9hDRv/vvTLkMdgobMSxiMQJtzc5aoWDRzvmE1Z4mKRbcYu+qZOlzgd+I59/9DAoAl -qejQT53C8ZTy1tkjEiSVQom3Uks+kWzQ0RvvKruYRaJ7JDkkdURtvelY6JHe+V8iFvxTiZf/SQUQ -IgJRI+7fjv4+BHgsbDA0LLcyZ8vx64T+dvJJqQktJXU1jFJoV8N4XwgajrSJdH5H+96e8vZd3Mz0 -7oKOe9CKzSpQhdNDK1GYEQbILR1itEnsREceDMB8UWX0TgrQkgMiGhu8HHTi+AYj2jKGRM3mk01G -rxH02G9O46oyxz/JIK7qKfjHX+42FMBma75fXtEN9aKaTuDgBhFgt13d4Ls1ePyBM14D2jpTkUQK -NGLMZtJcYcjAHvkzYyQ0dg2sFvfxSY/SIQdPD15jOEDWW1adbziSLckyOHcEZMnKepCqePx9gXly -6JLi4i3fGv3RiBtbxkTHb7Z3/OYchlmJ1PqmzXpTTWFy3suqEu47MQhz4sI7vTxh90sVf8gcxfct -gVxcbBh8x7JGLCESoTMlVZZQ/JZNH3odD+bk9UCIDaMsX25zbUqq68vffPguT7wQB7kem78fw4e8 -9+n0w78Gw2R0gLERDz99OP2//ktrnOybJPd+TQL8M8lMsBqebM//NkM/U6+H7tBou7CRkxC834Bt -bAcuRhZgiGOsLwbA3VQI4r5GkZSK1w0EgVk3ZEUyNgmG9sfV3XoxWSLUQaF+O+keNxZlbsjKwmYi -qnoDaQvetNXNRTUDSAKLAQv9NkLzZA3c4Gp1W33GEJwQVlNCNm6vzCZ25hvNSfbj8o8D858/4ZH6 -4/IfcINLsLHt7QprhRGanThjjHSod4mRIXUfwfzdVG6fGBF4XkjGy2jx16q7yc3anFdZMfxcN0Zo -f44n1CCjvyzBFWXJ/VoAKD2sTO1qQYYI31Qb6CBId6G1mUoE+4QnWAYZrgBbE8oR4VN0H2AQ5ioz -D6DwN5Pbsex6vXBgyZHnpWA32YhPD3gR3MJgPBqMs0mUY2ac5vsfegKsip6xtqWzJxwZD2F6CSBV -ko5Pzj3hdkFHewNsvcj/mCOcuf/xT6mP/xCh8XhQp10mhdSRo+NzcPjPf4Qgf4/gAuvBk3A0Fw4c -A/gp1zDGJ+rv6XKrPkWQxPFQAcA2GG0Myss1Q/8SKlbgMTD1WZYfoMHF7PhsH2THVuxkVcuCAVXK -OBf3JY5UJKlSzZMyAfRiIZKyRzDLuen3V9ggli6PjsFDTEJdw4wFxh/RnP0pnDPmY6mMiSED+E96 -kIkkGsIZ5oA5wEmQjnYQWFuX/iHvniIzN25qojmRWl2OkSyJQFPYWD2Cj2MBHr3DokieM+3mRKF0 -kiw+TBYKfesPfRu7aS7ZZgdKmT2WfNvqQNZPm711ea2qdvu/PLvYAPixRRk5zx42gKrw8Mnd7Btw -90jDulFfXbyvQVbP7Ai6eNJcOcu+wrAfYRCQA/z6bHyAOVUEQZA56GNCn+giOLbDu3Si6XhNchQC -L8L6fBiHFHeWdi2wOy4q7AGtJuMCdWPT+b3eSsxDHYpxmELSdySHmsbhyzdvX7457ViEZN8gkgtK -60bAMGuTrabTnTVMEllsUxEe94BkgzCqPNWjotch7EaFV53+L83l5Jv+sJdc7E6iV6278DUDviqN -55N6kVi8lnNHb6UpXrWAxmx0lwwMRUFQNIP8pp/yZLNe6y2VMyJGIS0Bvl3SGBVsUTMxRo04FkHA -4O2nL5I1i01uNkADAhngn3CJCCfys5kcuEASOvP9133l/Ubh5OQeMLY7AWpj0d9WCVZXQcO9NhPb -p4PsL1EuQk5hxLotTKk+b/r/wXSsLzjgbf0Ak+49/VCjUV99ksG+9j797Yd/MWYsHoJO//S709f/ -OwKzZz8Q2DpeoY3MioL4PUjd292aTNF2FHwSMtg4LaSWifw2OZJI0rcTEXz88WKQIM5wikQ4Wbwz -nJ2ySZdvVsvr6p4cNgSexn2yiDacvTfdbROAK4XUZiEghyYX4ndzzBBKH09mM3q6LiiOLC/U5Wa1 -W7vgsoaj4peiT5EdFnyDxo9DV0d+dARzBhsjDl86wbkc9ZvtalONt2Yb9yHab7M1n8xVSwriRzSw -b7E7gSA6VEQTvb0ybS53wPjhKkVw++a2sF7sLusld5rHZHptZIWiTySC0wRtAyD+qE8d0fb72KEC -0ZbGXxm6gse/r8ZMZHnp8lL3Lheri6Nme78gK3wwJIAwZRRRTxMhI+RZWuzsJJ7oUTdd9/qnpsr+ -IOoNoc1WceskmR3WuN0N7c1vv7B576JL9qReZzSlTm8oFArCa5HGzAW3ob+HRIZDTU7qHUp9lQpC -jc+Tntemi2MCIFuF6eEEakgFaDJzhrbskmcoYxtiCoSe+0N1WEwW6Im9zq0ZeWhy47LYNlTVxVdr -i7CyHnrhgWQCWctY+bPHU0eLjWpbvCUX/ZvJ5towfLUN+ro90wDCviPCFZYz4rlADvmra1UH7pjt -Q5RH8mJBc2iVCfQHFVqAyl5mvblh0oYTvySFx4mu7Fe6Y7kpdmy259nx4Ol5md3i0b+AZ1hQw94S -LFIjppEQKHKDalXdOd39ASpGxNr7eHTM8QBX6Mrjvj8dUg1w4/mVxYLbXA8NnYDORa+FDQEiAAx4 -RWgcRo1FgRuC0OO+66vWH22HcyqeZydckVuzfLpaLMwhlZ/o/ASHZ77RD8PLngMnMB/wX/P3a/Z3 -MJ/kp6r0Fc+NSX1lpyn/jUQpMp/tb1UKmOzCHhAnmf6T8Mn+5FEsp435MCNKNffKT4bWtupJH9aR -sjA1i7MTLQ6hzoJ6fHVzIzF7MsMrGgsRbA3eseoh1eL1ZX0PvRmjM4OR2YBYpwj9CZB+kAQA7o4h -gUjmZRuStb8XGggqtptkpOoZmv573s4uZVw3GCeK8Pvw1A0NcFSthiuoov5tOHEB5LXw+yJfOxr5 -SvKEHAdJD89WDstIGGrcY4qyi1KLBKxaX8iXteFUEBDaYmBi9GIM6tU3R24/QhqlqoeG+kFIHYLL -Tr2lkCUAZB4HB1hPtg7WjdccRB049HItFqRe58kH/Wo4X96AXAZttAR98sOXtV5YFN9XFMmdq69W -q+uhJUWZWEBiIwGiCJyRtlcjPeEjnveApBP1JJaJu/J9IkNHfUA74U7hdEBgIOBP57tFBkQtW0bW -HzIldQXOYaVnbaApyp+RJoCjFb6X2ANlj2U7NUTXrnxnyAahOD2APdfIAwDPM1XCO3JNTqc39R1A -/KEwRSGTQRB7DuHMZ9WmBt/9wCjANYqlYDYMqQFsVsIwzNmNAEkHQ5H/IePGsJVSM2hupjsjbt/Q -FPQxS79sw9rG5IIWhsnGViaOiV7XYdeHPcfD0Zrf4ISOx7Seeeimp8LaxWPiTtlzpLNjrUhOFEtW -zcjSirGFI0EOxaM71Cbnyb6Mw05iiSGoRCDEbfYX2ddPDa3YGq080SEEmvysAXB3TwJKBy9js8um -4vRKRMlUaM6/G3JFRqcNF4s5/aDJ+tgf7s3QvwfKjX32AJo5QLSZJ4LvdKF7ucg4+jA7QAAObL7E -TdPagph+YPfaGtXNYcfb4+6EzbjQO3qpBTd7jnF35viWSc4RfWVxx2HcaCp5k9nOey4nPID0m7Pn -acncn1xO4UMQsYJgj/GMYwun7cr8RazcGupNF7sZs/ckTI4laY5DZ2SmDeAq158rsVwAh3hz74Jx -UkW+ocr0auKMdcD3FD+oJcK/h+C7tPHcxCSOS2hmi5qSJRXrQk5aohu0CKgJrsjxDX3lJUdHgMLD -CGI71QAdfYnq+T0Eg2NVyxnrvEA8SYRC5FbNP2cnR1+fJ/Xhav1O2gKpeCvarmCmWDf8sNMeBiYW -TzoKUlK8jmjxPOTYIJgpxs1wHvXDM/CJPOsHm8LG+f6iEF9S6kvCfHm+MYq3aNbxILu7uzP3xq1c -EcGK1hyoDPZ0D1AeE7hHbGbogp+ykqfzD0c9nkRipPNAlxy9wOBVxubHOfbiofcCUtTBhL06zo7P -KUj1wOOQ6blU9kGgbQSFbxz5AUC4p5PplRn8t/tmM5oUQrgY4wuCDbEdztADs0ebKhM0jHpRb+99 -MV7ieGssdZhRp1Y/tzrQcJtTYX/fTvspo3wb/hx/mN177O/eBzz5T/yvGM29M4p7lgrWHhkKIAHA -qJKTlooCw/ndLHTd+rxA5fqYtYRWtrSBOdUh1EWNUZx0DTDhdWHggmKJpPJcRDiSV5zoIoD6Q5tD -YjTg0e8LqEnPC7wJUjQuCerhXwVj/WdCPMfoiO7Zg2OktQpG6F2uLMv8K8A/rp+iJP6z95JmOOYD -b349xCvR51U9gzDEs9WNiDEcQ62qrhHozkgSLvCLIQwd+OVBVgBDEVPcxT1QysaQ77RiB4qJIYQf -7tEsCSQdsCaDV4RvSy8QDIoTnhQltxCOGzPI/vin0pc3QM8wXTQYks9tyJvNylbgntPDCBTQpByW -XM/QRVhx/Lxa+jD8Ce8iUwi6gFW2xTIE/kp52kMZ8lITnVTLtIiQlIykqzZgmgeDENQPEe9+bs79 -cb/LJ4heR0UhoO5cLdIN3/HbbQE6O58GIkUnpAA1Jd3bM/PPeTp2t8SH9lyPFkN4ZSquq/vRYnJz -MZtkMMwT/O9QHZ7l2cnT85THkuwuO0M6hop/K9ZHdYdCCGvxEUyEN45c5SPbwshHnPHvyLo38NXv -kQ1NKwcBXVg4Yk7pSQBmX5h+xHlR61DqbAR7ApkJsA5+QlVwXnoGDebqvWzm1WbMTxUF9xAwlZoB -966Mnk0gshT/VCWQl44UX/UfHk2NI1XrKKzczBqsBSyLjJHZs14p/0FJiaxmKEB9VKvnzI98qc0+ -BgsK90GsGCWr46Nno2ecvhRg14UlU70frlHHzXkHdtZG9vXLlnrldNdWZvAUTpKhX0ZhoeQJC3VD -TVK+l9K+5seP6ZXgolghB1jtbMYFz+w/bM4eNudgU0VtSR3DOgB0VOp6r3sjGzhTdTP2Mpd6R/KD -moJdaCnR8JJbc6loRn9UvTgBRvynFmdI6VSKv8CW3r9PeE+TTL9V3gmGWOGVC50U6E6OYd1MPUxf -GXoR0ieugxO4hptqc8nPZ/xOg1oGzDO0z5xX6FaErrVBn5I3QNpO2I0Rlx26b19yFYxOEv/+7ypN -HiAYnwl6US9Vn+JDBr7KrHdpLF0dLjcLwaJ+J3H3Ffo1hIKxNp/s1DLRdgVv1voP1Qw3fA46sFzA -L8nKBDvDjM7VHKbFEqFTkN+i4Tx6u4rd/8rMqxEJNzrSeHKRKcocRRGhq9r6ntouKJKPufThS4dv -EsaL/v5+uZ2kjPi+LDIIOZtgJ3iuyXxVAoSAAhgDroKtB5qlbLb9MuqNHgUhin9fN/hylOohX2hN -2TFLD8GFs61XMQwwmwuhEcoNN3ny47LflrOayan+cIN+M8gN5ArtvBHSdWTZwyaZcHtVgz0TnWPo -CwWcd0Los/TcDj005HI7IYcpFlq+sJ3fvn5zepJRKIIMHo5BQQEdf5zB7Z58+0CueGx2LnhWTVLY -ybtl/WlXZfL8iNv83lzeVU9Z2RAXzh5m1TB6Ly2jSLR2upUZUB8Zn68EYimCGObNZDm5NLzOfGzq -GXB0G9cuFdRQufaCgdseABG+KGFWrjhSyJjz4UQrZUwF02voNemKMYW6GihygK2adTXyKTyYrVcN -xqqbLMB2YoBweOvN6mKC/nTymE/2U6EJ6wNwrbutKCQqKqcZMIzpljTwkU7X3etgccxvd7fTIy7N -raYNEijIais42Bw1qkGxVXMZ3sxWt8vDFkly/7+9TsN/0oUKRr1nrYLcP225wkoc3iWKuOEJ3K2P -alGh8IY9SwvR8rrRL0nS7Bdl35eGz/fscyK5KT8QJ3QjKkOE/lHPvfKtV5G2RuxXgIu5QS1aPtC5 -yy+uZoy1oCrnkHrUwu/dbXa5O2bLz5OasKCW1jlrbc1P8GbOTyp/Yn1qCg+vUE8ktSC0edg++ALZ -1L18ah65rG69140QZaTzmVhXD4oJCWPCl6dQs6zsy8i8Y843HbwIgSkZWLGu3CUYlBXWWiH2tE7u -TKjdiMJgNc826NSKoRjPBnEv/ini3m4+VzOauPRTE01MkLUV6FitQ+sTq6qXV0wvUQpWoGuFWmGT -fYroJU6LWuBoLYmepJ58RbjBvHlHw35+WYY8YKm4h1Ybrk/tN2QUpA9o4aZB0T1s1c/9JQxNU5jk -OZTIvnRy3Vl58PzaIvEUd6+9V1Cm5sAVSJfdz6V19tQamK21W4JB0LS6MOKWDW6GN8VO4D68ewd4 -bb5NO/jRYc2h+gIR6+Um+9wCmUdyDr2wqXdDtBUzZSiBnPVaXhXtkDDIGl187Td/XXVW+3s43W0L -ZZSo2hmp31HsFF2bqi4W17xmly3tJhHNDm5iXzNmXsAqA1sjp5wypXYYtlTBT39+LL/1Bn3edpvK -3GR9chpkrHFYrJaX/SDQgQQv3WwUP39FNrrv2Mb5u9XqerfWXrfpB2FzaSA/WIJUdi8asT/7IBun -HrwdfH1MlcCJ6oF1Wq+WuxtUkONzcZN+22KfZnSP9HybzazlZdtblFUpmzlQYwcHrCIYccJT/6R+ -BPYT4Ksps0vGZOX+iU8a8qdNPL26QaMZ73gtykS9iYSdneH9hSfikIavHEbkJYQVeKYDkeF/PdQj -VzQkS0JD0tFblD0AA3Tz+94g6+OTL33cNZNLCtECKP4YoqWf1lt+Sc9jTrq9kOE4ATBBE9pDr90t -XdxfPZ6GLlECk4GQgIhiH0qrkgkCq/HPCOVNyhIOlPwVxHIXF9yR7U6YQTN9zeSd+LDa8ngl9N6t -vhXdohe1jrytkCa8wQRHJxfsk9bukbd3O2pTY2VwkP6Py9Ciwas81QDEuJoF2uF4KLaah83Jw1k/ -e8gevW5pw0l8BJ7wQj7OQDnYZl90JWo1rkAJzVn0ojYdjQkmjXvXbTCyu7mc3KNHtypeLz+vriuS -Ih9b+Q8eYFbr3WKyEZcXbYZRL8no4uKe7zt41emT62MfhCpCxppAqAZ82Fkiqg72tQmQCsUdY4xd -QL/8odneINEV/lPeA8S+8SZx6PxysXVXx7hu3EVmfPz1LwLLueCS0yFIBi+IsaEGH1F3/vkkx1lR -JoRhetVDzzj9/gof0fvwrkwxf4UkgTkvFhVF1zggIujDTcZuQaaqZSbliXwoOKjTYGDnyrKXtBlp -M72Qp9KzhzN4KM3qA7Rotkz+sMmxVMrctttWJUb7MmOVd3Sii0aocHnEmnus0g2bjanC+2iXnYt9 -XMVpC56iqU5YyhH5XMojLi5Xl5mHpoKW8EWB7QquWIHRb/gJMnkHx3x6MwHMEoR1pe3aHh/p7Mn5 -wAMxgVeP6MBSq0ll0pase0BpuAbPiMPbKdKfsCqeyHBvGSb8oPcgQ2+b7DWYQ5m/x7Pdzc09VQYs -tQwUPSGfZq782vnvwZ154hQ7kIJR2qpmDe8uArJguBEGgdutPTxgDutjEibi7OxvR+qXryECy5B6 -pucmIWx4BgYkQCEN0k+2YME/YhnYWh5wZiZYmixldEDJzLVDKc4XJ60kKb3U/RuEr+zQNzassdXz -v2FADyYG7xFNsCf3+z+KTT5GjhEri8TLqPBL52lTOMmzBHgUcrr6XFnHyH6XrqqeK9OTLkAbkkkl -68iWSmdUTpnWHsR+M1SoTip/FoWilBVJ6jZl3eDsXJGbd7/lJiUiOIoN4vVu28APh76vqKGpUfA8 -oqEjTSMRabvbDmfvRZWzpU5Ch69fIsJ7KX6RDTHcrWdw3msOCabRpjDYabprLa2Fdx2UOhI9D6uX -v/f7CTEPte5ArkF2LjGSqjm2wL0CbHGsY/zE+p7JMQDGm0fHSQ5NTir1+cHeSYFjUmKXdhmiIBGo -PaNumjs0TEtpNdk7FGVU5YfVodJMWU46r27n8sw2Znse1ZK8kLJ2hUyw/u+h1kU5wxdBB8bj6pND -YweLrAPcYwpLIqDZwlL0lzmkOnY2bUgpQH92lhAJW4rw351lcN9KAfijLbcfCyEnpiZPbqaKzkJY -fVSqxS7jS92GfJdKt1LLqnWlRIphDa+dAV3cUMsVVxCXhMTCf5hxboHsxRp7CcOfoWY8l0PbmxXU -+ddb300XSv9cpCl72CdJjhwGeizoMjUj1kXh0DRQYwc4Z3davCDTQ2UsCDgvAK3TZJ8nG6yjx/B2 -+AcpvZvtbBgYDBQtrrmodCtFcq3nqh/xpUelRe3Y1xBbK4p9x3AAPOkpuCD9AOuGjwxgLFny1khE -iZJjW8ytG8Fv0fKZy6lqeoGR2elPu7wqS0RdMrVnbvQnR6rAeW9P5nNaeo+T2UlyojWkgy1aI9E3 -IaPj1CxasIWt01hrBu1bvApsh4cvAqr08RKCkNTNttHXARuwdLF4b1b06d5QtgHuj0OXcwbl8rN3 -gIDDcuwMLnOh8oF29uQmWQRApTBK3yhzI2vJM7a6h20UMI/yiOymqlLqKPvekBj8FCp20+fNhbLk -b+z4RcrxJsXLSYP28tEnL5ebNPin0BPp51Mz5U9LMtdY62n8r4n8Mm/RXIb7aaomdDy9qqbXsGlW -W3amrgR43Xz1NQDmg9WEMjUAqIT6GqNbpVUlLho6KUPAiKv0HcixgbTLWafjLXWMFCjhIfo31X3i -+LRXOD2PYFxqCfAA7U/syOxJkno5MNevxH3fQb7PWk7X/hE7MN9MjLS33aB2Hx7l5E6sqK30xUNE -LXEgAwCPNsgcQtZnEBK2AMQaOPTBephEWNc/1OsiLJGEKkhSEtBQr11w4j6VZybfOQEdJNmRKL3A -7UGPT4LCDCy/hsEM6HoRjAkVpWkC7RjAXZlmncISYq4Bsok1nGijobbBeTA+TIdd9bBSYQ/9Jpr2 -eQYTppj0M89sf12yZ79W7qAvEv1iZ6RQTeMevfCRH8Gs/JNLHLcCG/zw5uxhloRrI3JYq6jXFWQx -vhc4sbBebovA7akMyMO6iX3BuMgHKHHiGvoIopfRqWmXRcPLWZ4dot+BazOFoZBgWDXr04KL67MZ -RhzAt5kpwfkLzkbbPTbbobAEWYCSnY3M3LVvDdQoTrn0cJhlP/hKAIyOQSZBlTsQGf+XXnzwXQmy -rs11oqJ4wverHfreYkQAfJep7sx+agAmZFPRU79bDsgNAY3R+NdODFwQUFquAcMPsf0mwiuy+QTe -siA6C/gKTCeO6qwB01WlGqVO/IddA6g3Jr0S4EGM6bLdoRPxZrccOliFE2hHZuYEIkRYjEG8GiN2 -E4oYNLMuqYkroWk/USWCheCG/FpRkw/wg97uCEqmuxAORKb1BNgYTm01mV7Zrpl13JDOGgO3mGXT -44VoHF4XLshS2ywwIu+SSE2MyxQFy2+oDOjCaUTtmjWBwdAVYKttEWNaFu8GVihYvUaTHa0nUlw4 -Pbyw7I0GAW4ACF3RKcBJRtMzM4sjM8nP6LDTcZKiueEF44VIjYfeVzcVoqAIfiQR3GwIO2S5wvox -y2b1uQbtMhZKzbV7wuLIPoh6dk+u7l5nhpp7dLwUOZ7U9VykGLcrUYZZeBoMryzMz0GJlMmSik0+ -jzojBBk5I4g3IPiDAeY4ygCW3FdrdDSA0DBN9W1kIMSSr923KTU25KEuxGdUi0lQ6mGRIwXZXaKe -Tt1hDMKWmzGWrdDyiYaPHI606E0fdn/fPiBEMxZqh+sZ+HADoPPrF9/Dr8DQio4qvP6AmMrZNQir -y28OmcSRF/rI6rPRJJ35F7vy/CRlsEXyqf8krnBg24CSQCfBoi1ov+BPWaQEYBJ3338lGdq7aWeJ -oZXKO+Xx5qw+b6/HgiC5essWmUKyOKFhMiPHSl9uZsGtno2c2MhytPoSSAzqYWqAXmtKIClRnpig -RIHou5Eo4eGNOpGi48DHo8fGGRtC2CRUtE+26s5EB4SMsqQAYOjhqWpmUNag+vWmXm08nS8IfjDV -Zs8ZHghHFR7gVJ5fVEGdn/2dkSuYl6BTKfrwSR/07vLEFYZsshjDFiVcjdS+GQTHiL3KOK7ArzWI -1AHeURvsx4xCJaDJjZ52OkCq7lbq2QnVARtMzCwvSVaCWannNcmK2Xq3MdwSBTuSynw8ktUy38Lj -xBo8tJbZx3r2Ec99OWQyNpGoZzYglz2Kwk4R7jQ4ehG9Ad4xCmdS0hznEDQSFyxbTCBDIBn4zMtK -vBBGC8Lc4cR8/OiJGx8/OjfT9NnH3MSq11h/i4BK7jx0V1VYJ484dMn0Q62O9py+zGrrlS87eZRB -aCFHA7jbQi1IA+hmD5HMhFjEdOZA9F7/NZVuyfEQ4wPQcCWcRwwoQcG2+okLd9slGfVtGBvIsUZV -fMbBToL7dz1zp1B0HHaptExBPJ4zXaGtBziT02O0qC09rSHrOVIajhTTt7AbjXOHoiO75T4f39Dh -cROO+ieqAGGlOr2NrwZK4F2wVEZZk1bFak+AMAcWflu0ZS4TJzVUh2B+jxp56OWOJtx5ZAhRYC5n -PmUqMVXGRlFW27YILXFZ3+8eDJ7ZN4G5wlFVTJzhodoYrD29OJRfjbEByRzYjPbjx4jtIPjkRAVt -ooK3Eyyp2djKYYzIMUSH+XA4LB33ottBs8OLreGdUNNsRRavE+hBCh/m40cXSGG1uh7QtcD2EZkw -Y76iveMyNCoyM8SQYWZFQ1B1l0ebZVFMAFwOZRFdpL0F8Bke40Ou7D1PPepYIQF71KbkiuDTlRWn -TfIgyVOv6y4VHsq0eUqe0qJahZyCR9eFYmgeeKUG8mMArkLpmwYSx5h95DCk58V/CLVMDpBHtdoJ -y8OojMLYnQtfjM5OYnHIqGBln8odPfGixB7ZsFZ6/WOleZsZB77nyYZk1H3vZmy3im+K4SntA+D7 -li6IiUyqC5LmtW37hTU7djAcZhzJaALRP09OsqfDJ0k5o62X0lxbV9O4KOhKSgpKiocY9/TWWXUb -aeygvjgkLRYrNPhWS/8AKSrROdr2BShtDEshE4uf0FMf6auzrwH4F8FdmcJpHG+LBNb2stPiGwxj -k7RgeKslS7Rfug6BPVh9A236ioKydfopkEWil62RLMw0raY1qosYSqlufPa6j04kpkXL/m4sinSi -S4h40tw3wIgR+lNvM0vSqLWVSXP+pYd1jzqgDonYHkp5E7U/PgQr4GuIEjK5shb1jNvG/oFCIa2I -aA60Jt1jQMqKLbljpHvnuu1dMVIHQBuAeiLrGdd5rnwTOI3itnM6OLlYbQZcKAk/TAwD8I/wbYPu -nZxI90J4ulhEPNkdDLYCjN7sHKLNVWi+W8hdni62gg6EszoRvv+YmnPPIkuM75xoLNKhU0m4456s -70+QA558HOvwPd+b/7wQCOiPKdSS6aaacKSxidXBgEw3dLW8efb9ywJkwo8fD9XnUtcGmVfP7//u -71uxSJMaTb7hTvxqvvKB6pPkIqfcGXUECwrZ8IQ7lgYwS7Oxs5RkaA4HLyGW5UZOqUZ9i3eEAg/A -ZyYeyt6xqRGSDgWn5MiWhIOEM/7xIzZkpPe/sIU/fpRWzWe6rMFHbBvE/OXM/CUtmw8uKGQU7NKr -yp0PfijzZkXXi2Z30QBPXrLpuaEDVZH0kyJSbSoiG2SkMrCQOqXtE9e0KVfVn8m9wAj3n+vVrjFb -jh5PXD1eNZgINL5cHVkllq0R+0EVtpWHiYPy/IpCz0urHcp6Hz9KTR8/DsxfSJv0k1ZZXWNwc25w -LtDy38wGKM2n1D6qIglQPFvU82p6P11Ucta0dE1W8SRDYQ6fquBKChc7qcskQy2FOiiYEMvOvejh -LonvQk5EjgSfvFqwt6nKhpI3WzjeVpNrcyX7Vvm/mBzQQ4kkqoTygUPKwO4SQCx23O2c0q8qQB9V -3Wi366E7P2Y6k/4caNRDRUOzZ9sbV11gdRJAMXR7TJBVoVnS+g/wIJLWqs2qResQUnAOqJZoOTQN -Lbn27C/hXrQCoUgDo9OGVd6J6lkNxGxuU1d4nkW6DeBwFoLOilS9CI5U37PoHEX9XYOx6+JaQ+Xs -dsWR+ziqXWsJUjqTNgTeObe3q16ggOSstXrORrC/IOKjVEzy5UYPiu+rYD4A6v2PH4PZNFxDOkzv -xobWMcKS96QwWxm+DNIEsrbZTIDMLBRpzFE0D9gTdSIUpQ7bLZ4FeZtwpy/tKWsmnR6Jckm4QHgu -RE6WVkeT/mHkaSnSxjtQCcHdiq8A65ceSZz02D5POSWi0lowQfnsNOfZbgMWAYv7kP92DDc1Lyj+ -LFBRBap1sorz+8PL5jQiHU0M1yvN0laLGalJQibt8KXXaHhkY/Wqwjdr1C5rTwZXQe4YQwfirWd1 -qu918uBpbTmjypgkC9+3YaAkxbKDTOOXhng0pGhjbfkABVGz6zjaUPugWvYSRnVu2PzMLZdEfgwg -oOcULiDq4m45W8XwX5uq8V2s1MFKYisyd88eFz9h4EFhX/0O1pDQUyK0lquFhZ90JZ5zbFAQRaug -2N1PUbTwwO66j13u0Z2/ZtSbTET49ERx4kHT1KrC6rVfbHbL6yUiWdHZTMp4ap5NNuC3Z+vsnemC -3sF/+yeyyemS3JFlzhwn8k/m8O7hnWmaIuvGXIf0w3Xkh7ZHxnCkpv3AusYQk+50tWDdeasY2X77 -SwE3eM0H3mv8eaT6w+2P+F/PoQ0xXNr8nPq/9J+CUJB4uPnG2uKojnqISdEJ0CZ3ucjh0Xsaw5c7 -XQsdiO3Pw58nm4bx8tuDcWigouDwbJFntSume1RVpc5bo18okcIOtD3yhc2SjvnVAQel6SUJDUV5 -++r1CFBprYQ3XxkmnX7U46ddOcwGfp2PTKU/LoP472Sk8bABKoEAY/S8aPOU6TpAwswFyNbFr8/O -tvgWtL06zwmhvlqsMzRcMIPt9xJI3Pq9zDTA/nipsOO0Fvhq6VRNQHME1n27mazHvP+0z5X+Xsh9 -dKwbwOjo3HCcUAT+99wP2en8gCdhe13YVRtSFxAWTpIBJlSGsyfn3qtWLxYi4pJRASMezeoNWWUj -avrC3G/ExGd7Swn1aigwTb/b1FtrLPe52lwAwrEYv0P1dA/rcxLrvfz9Hu30Di7BoE9MyAEXSIfm -SXCCAJOtkyeE/KCj7HnvH8cVIo5w5g333Nerp+uiqeBOc7iHMUraQSS25Gx2dNAQAun9F3zjLbjc -gIkmifPH654er+wSsjTsP2wMI2BGQj0zTQ3iatPQBnGFEeiNhp1SWQfZ5aaqlgFilbvPQl1yDx+P -zd/jMVqX9lMDNsktfVNgWyZTO77WTwDy2j8zXkUPGzwOTC9YsWiXHaYeJ33QEbBJsL3E4dgRhPOb -IYpgR2hir+zISwxgGbGYMCSfcx7uCR6gRGTUL4C+o/EwEaMP7M4gCOpiuyq8ftmOhMma+Vt4smK+ -lMP40bEZvIsZzEcJHkkNHligokcYBfxUvLxb4+OcRSEcZF+RqdNXX13fonuVQsChlzFUbE8IaPTC -zND1Y6c4Bg0Mt/arqO6eAOLQETnJGLGPr6NoMACKpFuTynrv13N2dGHcnKfDf4vOExerz2YTiksJ -nNieeRw4LJDRBPiTQZB4VnycnDh9+jfffEOqH56Lv682qxf15xqOn/AOPBwO4Z/jx0+o/FsM/4De -DC76pwX1uSAvF9Kgb1dHF9URXxIoJFjYi7YODCQ/NOz21C+98CTQt2+ovlWiVyBAXdTbDZik2g7i -sK21VNgdVK4W5tQQSjt+fKdn4sC+GzF7T6cPrududMjwnwERbGYQo6Dh2KBwnTW3NIrrwSyFbm0M -FjI7fC368+JJ2d8/ImtsNbZgrlf1DEKOjFQARdZo+Q+1PN/vsA/PiXTjPapCr/vOHg1CZYEZn77y -QbziADspDsZJ2aCoY0AbBpa5bwjhBv4ujlWoLTp/NggxM0ZeGVkdma8W0ebWd9eUuCfML6g5flgi -Wd+vul1rFGAev0clLg6pxAip9SKEHlesGghhzOOXBslHs8FjvgxCWMCbzayeLfPT7KYy28vmBjWH -9FUpmnEnThbfBtWQDWHdEL6pmfjVveEPqEbXW4w1dRENJPUoXeGL/AOYQQqYZtpnFkPi4YkA6Gvq -VPjzdxD+BxDI7qJ2Zk64EVHCnXMkpg6IQO1LngFKM1SXD7LcSCc6BNgabhH5w6Z4uHnYlLk5QDW0 -BsJqkFSak5dGSahqg+yaGvOstV+8fpG9eXuavXv2+v3LvrVc9bfwPjyJeIsHho9RBjOC9PnqsGJt -VCsfQGk8htB+mxYTZFfKwrssq1uTObl06VgKXIfX5J0a7VfbtWq2lU3qR8E1hBZN2qx3LkZqaA6d -znQj7H/dNLsLFdrCYnSjv1B6Kcyaf/r9h//CTJDZ/JdGZrz89Hen/+y/+9nP4CC4mDT11LCRy0sx -g6VHJzibAFdxhqYp4B0IEKubxxDHCdynDCO8qtCDEfiNc45FlvHsh9cnWWEkH7Dz3aFfpeGkq6yB -djDw3bfKyt18NFP6HSal33tgiCPTf+j++9MXbz+cDloeVC92l4dkBIRYI+iNfHglKIXqoL6RRBcr -2Oi3q81CexRAFi6cyJUeEvaef1u2i9bWLVeFg4cRjAPWk28Ma3O3NOei89tvAKt67z4XAxpiKOHe -szarI5vRz6BhELXt4BY05S2K0yyEsLA1aVU46Qna6oBQmBkq3/onXJ3X4dJT5JpWWvW4ZGpNjYF1 -s7xQ4ggchPIPvB38CQ0c30ogALtzYFOwyWEDAdzN4Yxr0rC8h1tjpt+3JxBznnZbztxuILafYPSD -4gnGqUJ/52Y7W+0UiqX5u9oQJAjCem+nw+xDgzr+LXk0L+7hJgAhqo+Oh8fOEh9/MMlg6Fn6BdjO -K3qlZ88pivqkkfpkxp9aPoE23PvpjX/dgPpmw5ZXcJing1C4cjkqA0KzFkWp6FhsC7DuIARnGHeR -tsNFpN6l+byfZSSQWmPve7pVW8bPe/jbA7MKocrsYQMPD5m/GcYpxMYxSxYeYqs/5/k4B7EGElLG -e/4TbaCJt8Qv6nexAwy6ZHYa6a28hdEPtozUyvVF56Ik9Nr8kr4KGBrs1VvQwILpJxO4+NSvwdh4 -A7bLmZBx0ZTegxjLp4llRNhsWyqY+MChr8OZD+RaLC2s26+KGbQV5/6Gvn+PXTjQm2vfXoXHt62A -W7WwyqgOfb9iKB5XCULpbau9HRlOF9VkU5T7sgn4FlbqQ1brFXB8JiAAQYazy5xhtFGx7CNYEss6 -ekGEmZp4N4CXUkDhFQQW3NVbVGHYKg2LvZ0swL/WIgle4ktK5G57gREPNZqGW25rdJTo6yTFiAj/ -GcZnWH4xN3sBsmIYF9Np5hjMpMqkPRE6zsPW30yWhgBBg2/ncZA9GWRHx2F8g/B6tpdczqzJ7kl9 -fh6Gn2k3SxLVmRcIuZswDVVgwBUYcD6w/FnSffioFvoZ2NkPKAmgBRQZoak8flSL6D+pP+AYBPUf -iPEgyEoL6Llr3tedJI86DwIsOvdK31IjcaBaHtNxqNpqx9FpidUGduGKjKGLSSNwFRoAbEu1HTjr -xcA0D4qjoGer9OPZ2BVQnNUGqUqGK4jXM2FPK7nM9OChkR8Q4SApsYN9O4MBsEfPwGk94VXZCGtH -i/qaBmmbDR4sFMOGsNpuEHsYpt1t5/TQiB9J3x5uB3wPdmItsB/OkrnJcBKoPkNJEM0KUoznmCl3 -xyeo60g2HeJcosMttPYIX3SwO7HkZDrsnXGFPFarvdq6S5M1DvcUtfXz+aVOrtb6dNaed4ZySWZR -6QpcZooS8Jy7AjECxOEYlju6dMB109IEvLmvSbWPr0FITqW4ZekAjyF2Wxz9SOh/7gjfMbBYScvh -hRDhVW8fKL5aV8s8FLvRTWuUzVvFNkeFe4Q2mILYYgnqD6jMiJpEZy2A11QIUG8Xu+YqaS9P1WK6 -A2f9AbzRulcGH6ZgHhCfmkbS0E0PSneujQ3tQ+/bDNjWS9mUT+6r2ZhcNQTX7WI3n1cbZ+AYui1T -pXB1xx+JWRQQd1hN/jNcTNuIyWd/hxayfv+SCPmmB/PaDzXtPgay6A1p1r1RwNzmkxyIML910Rnn -oDNcW7MkN6NQxz8dYSY8NQZZHxvsl3vm4ycRt7NLtouyn6ZhxKR5Cg4Cn5gdCTNXB8YfMHrzqZ3R -QyMv3737skYg9trBpwntzvf3oHvc3wJa52PebDapMIpUFxAAoAKBppR02P692Sam1cKuKJlivf3N -+PWbV28DZGWXS37++SmS0cJp2PxP4TVPYQShATOhINuOIQWuBv2X379895vs2Xcv351mz9+9Ps3M -ama/e/buzes3vwFV9+vnLzMYV/bi5a8//KYvcih1lKoBczszerDRwA+96CohqgBaxQFlG1j3V28A -nMoS0x6MfDQb//T3H/6lGLXRP9Xm09np/3mOGvKsmNUNiGfouycXJsQrAkUrOoyws5/Y5AEDkt9s -ocWAEko7Sy3JF9P3QbZq5M9N1QtMP/hPQA3soRnefIn3PzER4T97rSZ678lubZC9/P3r0/Hbv6Gc -TiSWfCiujf16pgCEyOm/Xa2u3wETp/3LGB2T2YzQSQBLpLGC1+VmtVsTimxDAg5+Kfrr+0V9wXwK -Pw1dDfnR0XJ1tF2tFs3RankEhi4+6PwE3yhG/WZr+jXebnbglglvlKO+YXZQbrWEUn17y0scl2Bs -Mir6ZnGnq8/o+LsCOLll9sOz09+i8301mcH17XJF+gBCf4JnppsZ2hJx90tvIsh8D6JMepaTD9hV -uL682iqbbAvvBetqiOQtAIRtJ9eVu++TWpaecGVBxhBbF73PlZEQE1S0eZJleF+8MQdgsCXC7HBq -NmRtNLkgn3umijGeGePxcENWVf3VtK8uKomKwk9ggruYGMGx/z/CvKKvALyuru/7/qxaCBiuQRwr -fKn6B0x7trm0yVY4k5TWlw+/QsuAHbS/hB3UWibAuNmAo/XMagrgk6pE5zCV0ObhAhjWZbxWWE06 -NxldjulPQ4GcHf4LHCGMWqjQ7ZzBu1cf2fi7CiMdqs7tpFqzcWcAQXXSHeOLzAriG0SOT9W5mHEG -lhKsFAa0M0NHrM7jqloygY9TDp/yFHSM2C7u1Z07oH2pODAMAefM2Rl3/zzWubuZyR5uiq++ergp -lbU/TcrMEqBe+NaZ9FzgYmghnWotVPnvAKerDTZalrcJVL0hrTlaJlrrCvdncw2yAvSWouQJhFmb -Dfpl//BNlOUzUIrNkkS/sDk9P0Q7PF9bQgYPmAbrlQCeGKs9ClW1mPsK0gPbTksnWqynlRUzWAf8 -3Mg5435biDKSZFQnZWPRyAAzFVaVDtKCOlKGYQXsGFQ9RZlyPJSJsitBLmy8+ROYHJ535qa6rEFK -KqREsBWhUsvtjKigaWR9M0pUOQiEY3iAHQkAmHYoCrlYuK8JjJInIiYMijodLVVqkLvlvmGqauXB -wm3tYMVsaBJYtyg2Gn2Prc3JrMcQm/1xfWuFmtaAQua3UEi4byUGHxtWIVFC1/oR5HIDtZqtVjyB -R1af387ZTDxoHnwxJrYHnX3XcT9vK7R/BDs3IxpNJ/DHbeUDp5jZBcnofl3pgKEYcxkRkTYrgMpe -grGJNRsdkGmuKoCPgI/FRox0EQ2ZrIDMDu89DdeqmzGiI4JKKRMt01VrcSQWfP3ArB3IgMzxy+Zh -gXNWNu0G6z5JDT3Exs64BO4kKri5jZzHnsIfLjMPUwkS9JVTOEJKGAG2Ued9cCKo04DxiuN3CoE/ -Ilx4vgb2Ut5pCVQhRICG+ArRhm5HOBUuPV00LW4CF5tqcn1A6MfYmRPmgyP24rYitzDTIrlyJj1h -BhHTuK7u/YVYpHGv7bCn8enFsSdZ+knPngdviQF/O+L9coATWW2zkeCh80vs4bovlrbjFkUUJ56a -EaBwadWmQ2tsuTpSCgvP7jc2xL29AjUeV5YMeo2g7iPJgsLlkzKGD1/OBnYlgojauCZn9cl5+vRX -izMaJYwolD7IzE7RB9Sl75+dPv9tf6CCB5dtjpOVOc8KHMVAzdGAm2XRtsNLRpp9/tuXz//m5Ttp -GWRmqrY097Gjb/pd3Wh3nfEG9ra7jc4mks+93lSAcyQ8xD8KYF3b97rfuXje++CEkOxVesCCVkWW -nlN840PeAN0yfIJojZ1EFfl5Zm6rtUJgD2xx9m88QrrvItSyi3u0EijchTSxn7c98kO642b1kgIy -hJMxpUsThJ85cbNRnvslh2inNivO+hl5tqES8q50ltauS8rK2l+GH5eC2QtVBoHE2qd6oXDm6Ozz -HzxUcIAFxgU4HsiaJpgrWLCn1RnQKxAf0vqM70zqc0htUYZA+veg9UMRtaUCzpCuAf6a1WHp7c2a -E4DCbtanQS7dhMvb622qu/FqtzUDqvA24SSj4sfZozIrfrx9BA4qfCHbLd9VjZEFO3Qy5jJpKsS1 -G4Bgxr9mu80kZYVuCmDDW/+zVAGqLP4ZWF5zzXAS8E8/A79sjPSMFlJXGeWFBwo/r1Qb5JWBgL0g -//RiODUVz2eTuueIT+SmAnRYxg6wo423ek64Qw3a80GeeKdLcziNdjUxQDNsFiiUdFaVci1AVz6S -b3S53t0A18KLfHdFWNmZyXqOwUu2hSmaPjHkoV0UMI6M//EqQC/5uUQtEw2cwJiJqWMEWLH1aoug -LPucpa/vSGBkv6luVuYqJHDyhOoHyFA3a9iqdHtVEhPEE1knOkb5r8yNZ2HuGdWy2ZnJqgyb6fNO -Vi3z/VSqsFDl0U01MBY7fvLkENsw7vpIeju8uQZGg60SQFMdKDlYcW24PN7Thi9f/v71+9MDjMNi -IQBvnhJwg6ENBU6s2V0AS3NZ5WL6AuPlgD+zBR3IGBjJXFAXq8lMXx1hyvldicTipr5AO7wZV1OH -nMbOCP1QExKwDodIEGn5jGAM2nWmlzjD9ArqhGDrk8Ut3IDxwwH6ZIKq5T+/BBzF7T4GRWHkQBrk -wNfvYO0pfrd29z9viKHDjb0d8BsD5CnWXSHOx6vFzHQkbe1BaTJranWNWGQ2DsMJN/SUjNqKi6pa -Ii0AvC5Gx8JxJuAYbkidSMFhTY+5LlEoxjzcZIiJHV2x5TXU5EAXMXqcScYQt77dqPaYLz2oF7GD -oT6XHdiFqsMJnQHBqwTPIy7Gd7AQFOObWFS4DBh7Hf47DCJDRuVBz9heGlLbLJw4xrhVVOYldlaR -Mrh1oGoVhmUfcWBAQaZh8Oij+f4wfDVqK49MN6rNH4IFCIN86gaPhBrsH0PE0hXmLCE9Jw1TDtkZ -MLuLWaD6tMY6CPtpHRRvAzN8pHO59fuekyEUdhgnlXB1fVSD7QrdGdH0g+T+iHjBzyguIvb05tRp -q88/Scx5JSZRml22H5B2tEqvWkg1Ax6Or5QT18iYbRCcbU2BAoKJWQfrjNOAB8hwWd0WZjwj8//l -wZMZeC1j02U5XDCUht+2GOEQTGW1RIyJ/m47P/qrPlzzby/6EaYbjDRpI+OmYR3T/FZBfacILg0T -EYG62aIxlfr1G1FRfBhoqdIV2vry6RxwEhGLbMRF/BpFODi02vX9nI19sVi60i/upiHH2vRzu7qL -K4QwAcvaDKStSktrtmmuIvarhopIAH29rEloNmfyWU58MT/3h8FjVb47X7CUuRG38s7F3N5t/1H1 -m/LtDbBswtucn3bNh1HCRgzsItL2YWQxoTZyr03AcfwEzm74FLm3eeKSDnTEud3kXMvZEelE9Fwk -hFNVxfq+tRJHMAm5ds2XEXMF4psZWYKExERtsUGTqfC9Ah5jsl0C36EeCISYH7S+CUuLvU5kwHBy -Yq0XGDG29IbjY/F4yXcKL4xA6YYXAKVQLciORgFh09ZAGw9W0nBBFM0K/mMkOGhqTjQoWQiIdnZ3 -DmZySzwm2PZX4ym0N0vvrGG7wFtrNFLfNSM2JOsGLEWkzO2VWg8kuZOgC0IWqPNwNlSY9wvWbN+E -e9X9/3jOqXG7ARDMsklSvyQCBp4G8ONx0jNz+CbFKJ0QcoyLh2gaUFAUuA4xj7sl6JqpUZhybiCb -3VLj3IanzwOOPwk3LJN1qaBPORBn4wmaSqlL1QaH1QMVABSVRhRKyEd/xtbgbRrthTlqMqFzYxe8 -2KVYjQ3kM+qKwRNKkGpyaGijuEYYCaUmYIPpewA4Wi9BhTc2iTR4b2rNEci2jcEx6B3zcjQHk7cQ -KV5XYcTps/V58ghxPSm+WnT08LjFgZexBrBJH/HFCrN4m/Axsb9oJOkh8D0wNQiVydwRG3eHs9fh -phCjlg3EITT/LlaXlNRPPi9wMXxk+Hpga35AguVjIMrHEh8gcTGEzGfH59H0Biziqw65p14aIp+a -PYzD9CcmPzpitgehbfPz5Dq2tIHtw4jUbOqW/EW1Q5rqJnR+C5zbDCxAfCBxUVuKm8n1U32inf+c -RnUSGXREZr53ImGkXALN0ouwxYwn0Ipw0bKtHeigjVDFQwwCD/qXS26Otn1wtU/iAif1iGK1zrU9 -F1VneD2lMxu7inCtjjrcGpzZ0udlvI3Mf/XhyPslfsjxtsSU3twicByhKF8fomSKbl5yFj4wwnf/ -6LOfI8UcldWaNLM5UK1d3axT/qK+Mjp+U0buxQvQd1WN+C1UieHm/1G8yV1zau3J+kKb8seyl04d -js30Abo5TxntKK1zDk/MennEG1FCwal4o6BgJye3z6t6ZhZwAq7w2asXWk/erOCUFXu7bJL1ITqo -2WaT9RZDc5sxA6u7ITjjzH7PQvvXB6jSBO6ewVUCoIfArZJP71AIG4Pd3hK8DxJ2VsnAFBb2F4yA -oBMVp/TLYdjnjkXw1JjtCnc/Qi1jLfq1iAxpxIbQ9SDinjqiXrAzoFh6c7QK6IS7FsnT/nzNVrFL -xAED57HurfunjNqKfzhcK/uA4IWRQvr2jbwfPECI1KulyKaI5FKxigOQcTGskNpb8RG88AvM7cyV -HgOiQbOemRlp3U9+XBLkr1RuZeRosI0/Wt+DUsW+NNmpbvOtTZnDGVmAOKMKzv02VSX+LNNiAOGM -CnNIAKOEq37kl3oNgNl/7NatnoSIitxhQfn9Uy+lTVGSHwIM6VNSupNQdqtiop0Az7UHIYqb3S57 -L7YgbtvZUE+uyqVVNBmiw/AvsLwekEPb7+FpO764x1kSw2NcpEgdE9zmKNdwfFPdrOzzZmwxRAWG -3TZDdvNhZs/EqRJhzVwV+HQh7CqLWRUgP1XLz+SGZH7UG0MBga+G+XyW//B3p799+wa8t/Jz57TU -VGvSgHuwGGf++sL7T4O2PrczI+SA1eNnwgtRlQ6yPDcijAKDvD3LTUZszfwb4Yaab/2Bl+K0oajA -213wsTn8ASclmI6RPysjb3LUhDrxa586UzwhzDH1XE4pf+V+peNIbjf3psC2TTbVvFer2hKyjjkT -DdN26PYHn6ktNUVHrb7iWYGW35NMTrY6N5cvSjTrcqalUXrY09MSVygywmaaUG9bd8sinH1PmyEr -xCvdpmnG+6Tk0dFCumrgLyk5lpOUfH+ceiTqE8GpWV8/bctnKFHnYztKUDqYyeurrd0nZPBRfxAG -RlDW39Cd9fEQd4H/RjSHHqyfppKWKzgjwHNyCP/RKwa5LedN7q35sd1X86eDwF9o1VTj+cycUKhj -X0y2oOBDX4nbevn10350D8I2hrcT36b9eIhV6U9Po09kV2YGb64DRtDf6BFS2iXQLxxzMzA5K8l2 -Hk28NAtEkzMzUYlqKE1VYz60VINQMLub9ZgS2N5uvj7EmEebpGHBFktnpwNjsoHMBHExmq+T5j4f -4PFoVr3E98QWOCisjA1vIfAucWFz8XjYWOud1TzDN0mgUTBCma81tI4aOPFdC2CQzIPM2OEPRFzB -GjgWYsiINowDTbJHhoh9FrG+v7CcvJkaOWZLkl0oloMPLe93GysJy1L4aVsS9DQetrbmNMhopKbS -DzOmKgs6E7sDenJaFMHEd8oOSEmCjJp+Le4zjIkF7Zg/GUyEUQvM1RRMpjIWAcDDJ6jntpII1rDU -fQULMCQwdfDiH1D1ZT8ojBRSoS9RvZxBHEqEgqC+1E1WDS+HKiSxqTCFH93SZuT4PCg7Lv7SRnMN -TtU8JLNSYESOBn+xfzxqBNz6BAQFkQm8RQTkigphZDAcRQBbgSmBBOxF5LpvOFMRveWngnpjk2IY -wAglYN1H35FflWUreYZTyesXhMRzXQrsXjAY0E8mThh330E0/HX85vpwU/612KYDwjK+fVqNTNm1 -zlh5HGc+sXjjqRWwEO/Xu53jl7b1MfPMOVonuIVk+0fTvmsw7JQzW/gq8VbgHfR4cxovdzcX1aaa -jeHZV/w7bV1H/UEANVetGQR2s1qB3eBI26SFijutahs9bHKzHutBFnG+By0avAdiHw3a7OXcTAFY -PMmD710Z5ExYkvqL+8BCi3hfVGddM6NhPog6qnSyd4G7gyT5Xnd35HF37m3klLbX9WGdWxXt2ZPz -facEnUp9Zk19WXZl9LCe3C7HHmUQbB88X4J36BgOPJC8jp8Mn/zZNqjPK2PGiAHHwRk0Y9E0W1N3 -jrDD9EjYL32r6eomtKEia4U+l9X4SsvPq2sMLpnGsfYPUjt/2unPSKY2hJMiYvZ5KaiFge2ZzGua -Y+KoQNiNpt3/M1w5dyHft148B7TLce6JP642tAYyR4aDPB3+ogVnDdBu8vX9+n4Mngu14XAAkp6X -hN31l7/AzceMSeRvw0cA7rQqyiTAPrdu6jz6y19kF/WWBBOC6almfkc8uR6if5rLgTn3+8ma78iV -SAY+W1WELXm72lyD1FIbhomSC1Xy7c99vHiGEkvcomDih/6Fy4wQDTW5Mb2eXNWI/5Ubkb6MYOkh -LxxoJbyVjPSikNuGotJXjJO9MaUB1ItKWOu+Xlpmf1G1yOxiHP76zenLd2+efQdzemTq+qsjqpiO -IXjfRjR1JnvReWZF2+WiXg2biZEdwb0QBjNwMS8i091OMPwAykUjG3hmuIovO3QOAXfpyOVgFCg8 -8GGuaXq3e6bBUiqQADtdClsrk1JBZXtcwRWmsiAYK5gUOa2aFMoycmkMNuhhk+0bLTnmyUH0IAhg -IeBc8hKesE/3hjJqfZx3776oFZUX+2gmz+44rGhwbAfd9awdzVxQGXnfhTzryWY76iux6Cd1EOIF -VMvwGdyDvt6ifRRF8byCEJSz1Ra2m3htwyGKfTTrplcm4atuqtEDlgmmnoe00qpG4CsldJyCsVXr -If4B+heYxL79DDJWNUurGcRCQowjbMQzMaE4yhbVNjeS1OUS4M22MPjewY7He4DlPIC5YHR2ZOKx -YXoE+Krms3hrwFjh7wP9mgSURwgnQ591+QM9BNdDeCaoZxI88eQkFTzRogSYApH5wCIF2+yhIvhu -vmrnuR3/kOLp6l2ZQbi+xeLnhqtHHSrsMAKgZ3a9/SY73tcrejS7oTWemKNrWWmi93pGz2mdaBtu -YsHXt8XZV7EZjiHo85n8i7ZxnuAzsNf27bmSrOvXQwotHfermnE7YPPYBfOvx5Gnu+gqB9bf5rZK -OzbwSTOS2zr8SN07mMd0WFWdxP4G7ZyDk3GLjoTftOgxsYrkhrE46FAXDy+hGKGE/TXQXCQUrvg9 -WV4eJrCPA2lqwEX0a6GZsLbFElqDvRb53XurrA80ervmJGvzC/0YPbE9gZ/Ul9ETr8XJQvoMv22/ -4Q9LEon2w5dzobMRWwxJtVFGS3z/D3tvuuRGkq2J6Z/MoDGNzGQmk/5FIS8HAIkMMsnq230xharL -y2J1pzWLLHHp6p5kNhoJRGaiE0CACCCXXu4D6FX0Ry+iV9CzyM/mfnyJAJJVd8ZmpLJuZiDC9+X4 -8bN8x6XkV1FSqd+lpDdq6RMMVCrYhse8EIzC4JSWt82tEKdSRdTCUxH7WF9dhJllOWJBGNgzYgix -4LNSxbK+3Ry/0bhXNFYjQZFhUTmtAnx+GjAiOmgrJSBrWyoAYleQNen8rmcVC0d56iYxtkynadV6 -TB79SP3zZHyLnXga12NrFyE9d3KJXnpocrmwaBwZP0VVFFf6K3XJ1GQK1rGNw32o8QQoTy9n2Fet -rnnqzS5nqL3kZCFWAG+uJYPxLLVhufiGJlx8LdCGyx/IaBtXgi/Oos8pFCleIVEsTel5c3l+prqi -6UGMSHIfDsJebyCNclvW8zBaG+6uXNT11VZkJUT+7NXq057GoFiaHic1bsAqQRmGat1igAlqKEgM -EcyhGY2IGP/pIBO8HszT+5nRtLCBIZKWoe2r7QYRyxB0Ij3UdWOMq/WyvAHrxSO0Cn4aSw4DNaRL -yspIJRkPVXz7zqHd226OTlSw+6WhVDQ9vqenImAeB7QXBFDTmlmWOHjYYzxL0e7KS0KgWdTwePZU -i5kUJzCzgoUneWqwL2Ga2sXteMLM+OCzFpvlmGSFS62NC50q5yz7VEwZYJhmG5dB6rpna+tws/jC -5U9WA3IgtolT79OJ5Erw1lGyy3Bp3afLbvUKb2oT3xtHC2yBQN+xXVpa9ACF6wWbb6AsEOfZtOTT -xw//ToOffzp9///87wTjLhjmFKeUbJMNq2Cx28kSa5DB6djPrO8VXyiyeVmuQhD3loe8bbW88rqs -+oTrbn637L4ltU/kNzgKQLV7TofYah2gCxgcXRXJtFAfToo/6HqL3cIQUg+f37989/7dd8+PX738 -NgNLeXyJkta3H354jy+fqpevAcD/7Zu35vUzev3h3fNfv5R3X7ZaaHG5Lhz6noZ3+uPJ+PAvzw// -0+j0483Dfwiwu2ug4RkS3nwFv+E2yGAmW5AVztYIAi4IKRCqfmO43KUyImeQ1y2qDpSMDPBBh21Q -tSjo924nfwjGey9+9w7+jKbj9aSCp7+ah8u/i638QdAgGn6smGtAJV7btoYj2OPiwYDuQDXZ6r2S -pUXRN3UB1EaYNeBWVTtP2g8fPsYRewj+xTqP5V5ditUdDJL5/XDEADNtOiYOes3I+xfFsjC7E3KL -SgyWkTmfSA6Q0WRVHjj/SKPz38LgHR7CkkSEevCETiPy12Hv0wkNeP22kHaUAAH6MQFLjjekZaEo -eIj+gwwN3cpwENocUTfR6MPFGPU4HQThHV+P18P2cruIq/W6YnpB82WIpI0xwCWpHj5pavz4HFwa -qNWmxkwEKdB4wprNBeA41W5gQyd1Y5yuFmxFrCNGRgVglJt+djNew5SjRRTAtKUakFo1LOyBVeNE -RvXxG7RTmGtkQ9wGlUH1iroDL8WsmRS2JlcJJl5kr4Del4tg8r3mrO6AJuwaQ6ptYyjOBmKbmFlb -gXUfqC4sprC5rmZkk2HGaHIF8XvzmmFoHx6SaLnt6qXjsK3WIMWpCNrAImmUvU/Jz8WNetbFqBGH -GIi3mPbypmmw1gxuoOWVWr9oaeE8AqRtmCg1GQBtpLGtOhBqcm4o3zUFgEPKODO7EpSdO9cWhrsm -tb+qDr0wrKMyprkQYiV2x+PmVWjdoaTz8iLsel2XvZ5D5gxyl+uxWSPufKITeVaxUHu7xC7rIxCl -sSsIdMGHn6zmYQYIrd1jU+wQ/umL42G5HtqnPkZ9G36HYcnY8334TjulC9gdlMVFD/nvXtFJhOUK -fRcAwGxhVuHsDC5Kdy3rAIAmGjTQuSXhkbcOJ2B6iXwINgYi1InPu3VtmJYzFTjt3RVga5VLj80B -Ju1iPV7Y2E+NeADidu68501CCX5Dgnz6UFgkYk8fFSmn7u/fFNZylCrv/vgDYbFPfQt9M5B2VKO8 -Fj8PJ/wDxIVKqK4WFTquoEnYBG0zur2To9OcYeN9M7YwAlcb2UZQpoCh3wOIGxUGsKyfG8d3hk3+ -bXF3Vo7X02OgzOvtKnDSMamgnUo09BLzmWqOsf27xv6Kyx/NpIIulznkv/v2QfHZQSd+hib7y89c -Ew0POioknzS57+/Be7VcLgOh3oSLBuBFJVt7d1cZwvXydrZJXLHjtQG3MrhLDbLJeAvBkN6tzAFX -bjFEGxf0hZV+okPg/M6XE9qV/7VZ+oM9t1QNtkbi7mqxNhLDZCi9hbNAvrti9hNI1FEvEXcvKoKY -7biIwZ5TpG52taNytB/hSjhPtjzNX9gG/ywhnIcRer14xwnnT5J5TM6Hks7qSJUL0ucCzvpnwAac -0xiYfbaZgcsocgLu2t4S3Aw0MoFBFpuBEuNvOyOTxIpxrFaaBCeysJQAFnacxz+Aw7L9MasDlfFL -SVTHxaA1JRXhH8KKwY6E91bywwDi4g6oAcSpzBBHJjojQdBjoVZUnpPZo6PTkKIfL6fFbeL0UYV4 -UlH0Oqwf/ZEsDUQ/GM6Qn5KyhvLg2wFyGykks7kVrFLWBJLKnjyFA7VxOb29g0Oqp4xYejuf5P/p -M2HIG28uleAHW0kZ0ZfROdCNKMb9OfXbfEPkBblGjezFA3HHVsmSvJ9m4lnMbOZmvp0WZtm42pwx -Lhfca7kzgVMrGq0KtiDuvrRLa5NdEafeEBI22tIrTofT+mFd3t7V6rUI/SnhkkgYthV7BNNDowev -qoENdrzQZ0GwNw4R5eBYVZG4aDmXp6nAOJ5iiEIYpN2E+6ZvGq4LhpZJfk7RVR3tpdSlrrU52fpx -Ru27mID3D9pJi5yuCWb/Gbq1ueuqcUGNDbjSpvWGLoQniVDd2MgqkDKhkD5HTPfvq+YCCYAfaCQ6 -FnsslBKDHxBzumw0ir5UWInVkr4GI9YocDPe9igBrFQQArxG+euMESjcfXyzLgrSMNsrG+BHY2aT -ARGB0dZ2XZgj6hghZ0DFbYjJAvgbdOCu8qZAs+QCBqAcS7my0szzDzlzAkO/g0E2zrbL2adtQUje -hBeOjk1mWFFIeVlwuf4miILpcXkuvQwBxvqeFnlgrEBphlJ4WAahbNFOo8FvdqMH+GcsKddwDqpA -PSFwGUc3evOX7OHOA128vTnKkytfUNB0BSjFRf6UyBIBg7l6bsZVZmF6ECKsht7Y9Y51QZTxiuKb -BijNhIfE1sa6yQIgvALql97j+FIC2lsUBfTSx3OJ4/59jxANOMLe5m3TB6bxL3ALxInwPac5dgBn -YTL5xCm/Y/PcREr5JClnybZ9N7MtOyYOIarRvG4HpgeTrTmgF7gh05R7gnGFNMkOyTSIPUyiL4Zp -khXpliGU8mi8moHAtdt+mj8B0Sm7oOJ2eVDB6nSky3yOLiNtR8BGqzs53BFrFQBM8jzvKex66mWq -GLU3kNYAKfS7JwR+Xt0D1R2cVNYq6KVEPLAAHf1gSDsYTJPXurXZpkOI6CwaLAHTju1sESQBz61t -GFmyhs2qc1XggJyYp3UP812dLxsytKTgwM5C30eu7DaAwY6bqVvFFIfreJSBVS54Rsp+1XNRfLKn -AYYoaIzJiSn6eGL1koPiq/29kZJZBM09lqNewSXqY7i+1HkhOfAXq5XcWSBf9ZFAnTPXyLrOWZAT -8jK2pejsl+Pqsnadwsdu1zayr9ukoW/RXDxlr+qSiC15cyqET5n9pZgqvwvYBFS7xXdU8Ca38hkc -wUbFLcZCl3eeSewWVbD+rnLZw1MErCE404x8lhJAwwqScl3ghafLmU6enPalgJMj9fz0tCGYgjQm -vfD8ptu0TbsYSLOMWjchyXTtN0d0cZu4wTVK4dywep6wIvYMaELdwHMpvYbKq2TXwZyiasZzhbvd -5FIAXpr9bYBRDfmzKtuuUC8DFQfIN2OUhzCGalliGSFv61lNYkNiFMUwcC5ZEZEkoW7pYVFpxEJV -LN76AxaVcnLcoxj7FD/744cqmDoD51siWLGBM416z7ec1xesGsK+77XMh0mGLoojpheqjQPMpMdY -PgK5DY4B/hQW5xfJT+EQy1DSV0XdVuvtsrDmLHYrkIS8liYiK8GbxPAyfjaw9LybFwmAdD2aLMkC -DAWsPjiusYT2vFxetJuc7ikYh9+HSL5PoBQk27Fyb3I3kPDCuNOm480YBx5iJdBtzosQI7CIs803 -Xq+grWnc93SfN2eUBVwSTKvWm4RPAqeQBHtYWtkc/qDxzAfd7nKQqgoxI/rNPjJkNEbBKIeJ/riv -uwvCecV/eb/opQTYlcHSknu9vYx3FRsEFNPd0p1HorDQfE03V7t1ub24zJz81RoxwGxfbmFOQTY6 -uwbYEjiBpuBMZ0UBVr6s20L2rlbJE1rCL9mqJVL5921UVlMHs/mGnIHyjm3SAvS3+oOiUmbytrdd -wsEl1bocGnEYVLzvBneJtGk9yU1fl5tjMbgrpmzsOzZL2Wy8jb6j6RmsJSjUhxUgfwG10w3hrP5B -Vaux4rBtbkZ6kVoOfacpLwo+k/cKc5SbRKIT7dVAeQV0L6B4vFl7AffYMI2onQSpEVglFGuJSQ/X -wPEEUMgRcggN0ID/VAup26v3rfV51s7ICjPMbU1wRylIFW1mwQPu3f9giKNvsQykVx8BIxDTEqPI -Fam5su98dYJOap/zyZbE8MNaQSncq1RelTlhF60rWfq1sIQbKwvsQHsphXBeUxZDGFrJ5XfvHI2z -T72dIvF7ixGt9MoX4vOQmWPu9vY2Q8Mgcbge0SoEIcjq7pswmqBEZuDgR35AFORKalnGdTF3BZgf -m5JFaXUTaNIMkuG4EV963kq8Ri90s2/nYLBTAn7gqp+1H2ugz63ZeV01/HSp7OV2xLXA1oUgiawx -ksqIXRIEZhFgQWrJYBpdIdf7fa73VEOcjOSOgCifOBjoiP24HU0MF85PzSNorzlz6jIvaLAmVsNa -L48X4m/Vu3iBGaOglo9ESgziwJABGENps0kWQlXjTQNOrYLc0VDwn4HTuGGT5AZKhkIo/13MwIDh -zDyAQZ456JIlVj5DECo39SGIymlz6Wy4Wcgmpg0rR38sIxNL5PtJySTXfeRkkseFQrP96EUga3aN -wRYfZ5+2Ziiyal7e+OPr0D7hSAt0XRzV3MP1HKmiMVO7n/317709QAtl82GuE+mMOdBPU/7+vy3u -ajz9bUG7NpeqIgVvGrYiVlIGg96VJWFzgagGuBP74ulpZH+lZxt8ecKJ175u8s0qzNCpMOJjkUED -xm+KgSrFr54B7LCUwsy5/YBXcm+zihlfTAJ4MysFfGQOFvBJ1ezCcEnA1GYzl83ZaAauoARYjQ5V -HZYnVR0AFAHF0N2zWs1crF/Gs+Gd+I8E50IAgeofxgmjnWFwdAx9X19xYlbbowFihnQVhuekstoB -DGAQM0ybN4mBpFOgWbOHyIeMgI6AaXEGBPgzB/kSwirHNuRa4M6OINbRQpshJHxFIihkYV7FijKJ -B7jL4sMk8c09vDxxnRHwgA0qDT/8BtC7XJmOdcg3QeKMKWCQEnAmOqLN6yBucUqioWfrUWhp6ixw -9UpxVh3i0RBxT5LRivK9er4eSoKEbCFaJWB0v1qhCTd6RDyYWleIejynqNJeyyFz+RAUyJ+m50KF -jKlgI1omJ4pGFtieUPVsZcYWKEpk5ytihcOOi7PmKpZWq3nQaymw+nIhbZhBt2HFAtxOrShWtib1 -p74EvmFxWFAvISVLZf6qSFvKAf0EZygONeBoVT+FYAOETbWVLM6qYVBbZPzZUH1k3qmppA4Po++r -taOt+j4Io9CmxXYaiZcnwINTdEQRgOS8etu0GjgCbSIH0c3Zcgo3I29vC8XckNd0IhS4XrrYvk0I -XOCSUMSqsAhZKYlQScgfry/SIXGkPF5h4PRhEvnxEdONEI0A/mhMDAZm4ymn9PgpZz+Rpw4FK+WK -VopFfvQyI4Sp6N+ZvteVUVs156M/Qw+upWayD/Vky8ll5ztGAzCDixIXR7rSacUzHahyd1nSxRcC -jQAu1R2Ijh6se0iUpcRYyuUfmJE/AeJ2kjc/eQIQtBKWF7gNs/patlzyegIwOBwdsF6mvQPb55wt -lQQQi8ob1J87dHuzUQx11BMop5e0AAioTI0k75zsjTZV4pQxbyPiAjM1GIQQo7RDEltEKA2pH/DM -ZQ+2NgVCrclST2oaUd+d0kwfITUu93ezYj7NblO3K75ZxPkOJAij4coh3OIYIzzxTqTTryrTufj6 -Qe5a8zvUS1nxPhiOWd+JNA9jt5CQJVrDkW9Mwg9+n11dDxHQlNsdYtHhpUYdL1g7SDBfWukuFkA5 -cIDi3FxJJ1ddACs+CuaU7x8IVgAl9LP2bGmYytkUF+2Dtez5WJkgLBbWcG34sg2EaBmSsDm7HWS3 -XC9CcRz1+qnoekMRsuN1oJ+dnZN2yqwGQ2IDJql5xWJkn4B9a1q2iYBtAj1lh4yb3oDagpdhtHXq -1rQmRtkMWxQtBhXaOuiQZ1HoMbCRrM/C+iuWOTE84Hqiz7qEnftQW2Jrvi/uk8Dox7xb4jwd2UEa -eoLtAE1VVsfnjEgKyP3n6zH1LTgTNvrwDq7C0dDrtUYAGVB1rzYak39T33s1pAbf+r/WzkDKy8TN -ijlMzJhcmx2D3tk8O7d+ZLLQA4Sp1QkHd8rHZxVFPsw7vVOwnGN8fm9Mkbsxg3rL4FqQOIxBwR7X -hvvZMDDtGLBBYROM2QadygHBb5AXKIEhOKCIg6zz2VWRic95djObz1FXCzY4puYgr9lbE4QlAZC2 -KUuhKNN4stnC/YeKngEpR8RpCkQXSla3Zt1AixHwGkRwmxIk02aI4cBcg5whtGIOiuC+kld7pAcj -+I+coXUwrkjyIuTZQu5mHqYYmoxQFkD2FoyE1aOEmh3xIEKklRqJbLIZaUsIMJSb1mudXHNt+J5w -gLADJ09PQTkCnfjht78efXv89uWL92/e/iGFC+svZLObMFqb6XbvdB+QHM5v0kfhlc07fcuVYz/F -ISgDMiuaYOjQzRYUGiSqQQojeM2IZ17czqpN5QtRiU0FwbPHYaQtWmh7pPhcFvkE5CFgWvhuyXVp -UNuEukuuh1Yl1X5s2GDSS/Va9fpm4ra5HDN0Z1UgKJW4uo4K9/ax5kn13Xldt3GA8SzArZipi9Q+ -Vj26lNqsdbc2yPwoiwfbjB67p8WiMg9YO2Bo+lbcH/Mx+qbisrXjLPeSiWAhylbbNqe2VABlIUxP -TNjb0cCMZE2Goh5+Dbh4Jrthe6nt977RY93D7ElKCC16lqDhJ4Oj02QMSM8Eef9p4EVs1TLl2vHz -ybu4pGwl9OYeOoDcDrSOH9Sfci+hDEcDTcHgSi5TGIia5O4u9ceMqLPxWSLaDa/v45qA2ed1HU00 -xyKZwcV/P1Ru7n5dq5yKK0DomxZ7Sa7ilpD0Ki6gWXoVl7NDjnI5rkYMxpYEEPRuObsELaau2x2R -RFOzoYUyaq2f3J723RJrQL/z+5CEviMb0PGEGI1vK7IJIOAh9Olrd3ttMkZA5aQ1JqxbaLpOdGdH -FGEZHgppjh/cgEAV6QGxnXShqAKXnp9z6GoXZYNM01myS9Vad7LUUYmXvp+KpruSss2pWp+33+MO -QKnWyo08xuOdQFdsDLC7897/39w+Pyc/1n1FpmHAC5lhKqFXv69TAsH7LLhPf/zwP2PoNQ6mO1lM -ARjs0+h9/t8j9mOrpXEbGZZRfroovArYEa2J3BcJhoFhevvZD8c/vGS/Z6oKAvRoIwha+VsKgMNI -toRLDXhlcMh2TA4McjTO4HoPIAtiLJ6zlTPyAFxB/pJylsvvSPUKL0lqiZcPhQfJoPBipuTEm3Sr -NJVcowR1mXWK9bqjPKbZxlhOdswMTOEhdwAHRVWVCwgAOhS7weJLrI2TZKuUi7SE4HwsI0TQ2EQ4 -4Qp/YS17JGm3R803V9otosNjtWMVjQlvJP2s8+H9d4e/6vg2V9KyYZaKuWyWw2Uxnyfs2k37wMNk -PB8tixsENE0kkmDNqmSzPlzUZv897UCJBQo8taxaM6gwHgC3YeEVYDB0hCxgw7/Kng3M4QQIdHfP -QMJtRisLRrJPn5/CyNQrgXmMRzLIDPmUHP2D7M8Ee7cY3/Fpdl1oOcQelmI19dEA5vatoSM8i0Eg -XO4kBWeVcUvkDusJI+FKORiMVZUDMEW7y7FwPJJzZSiTmjL8PAhM4YP926VU/Uz+4jKUVeEBIEDg -LjaPCgsxRA83aK6t2BM2SrYylP2kq3QNtmZduR8LIESz51FgZCD/o6qIJtn+DLz8Mfad+dd/TdNk -/vVf0yrAAXE9rTYNLsrBiA3A6ERpI1VP+rZBfdsGRt5F0u+TUnOjLgiGFzm1Dph1+acPzUmn5f0E -DCe/RS27KYMPuW+SFlfQqclFnteQh1vAG7Pr78x+9v5uVeiFgw5bn/704X+As5RN4D6N34//1//u -v3MRUFuuwex9N0WTX3qUul6bNqj9T9atlMSM+aet2VBBPKr4M9yu+S272oB7uPmdodFSJeAVksFJ -5N5KOIgwe9JVxXNHoKIAJXgkLRqNOr0ai1lKneu03Ug4lCwcLNnMlkiUjdTCTk23LXpbkOfaQepl -i22Flsdj24yUcTW3SvrvBreljCrjZBYMxQOpCYlK8SlliGZeo/DlU/yJoLyJ7sPdB0qIqjBtiLdz -YKfsZ4I7SyrDzBkkLxRbo+8P2deh5IUyJjW0yssVenkyO00rfHU3Z3VCaSgsMfHvNuXqmPzKNAIJ -jY0Zs4vN5egSghXsGiLVabdjwbZjCP827FPztYsL4WxeBPYKyNzLt0j4clsrgPdu154iSjVtDtaJ -5t+mps3nn9c02Ia3e+rEPGWVbV6FMURh9vGhppFMNEcTjB8L/x6Y1Um6mC0y8yvAD2ZjTpCyKvQB -LNj2D45niYBs0kssZNqqXsgkUzBX6GOlnUO+epUG4JmYXLU6HIxHREVDrJC7Xp2EDIdL9bx7i7AB -d5GxUFpafb96msrHgIRd0+luQS6HPcPZ8BMuEv6hl8ppszURj1C9UsgNvvk3ag06zNkVq6dG8tUW -Ps9hQXQ5XVMUcU7Z8w2McZ2ECWN/+V2rRBzkZehgGLsjNa4QZiRSQM31zgHULnNz/EuxNI9kgqhe -1Owkun8XG3vzDnIZvkzb8bQsUiGwjfKsWvEv46qwfK1J4/2uaUOYx6V35f4awfTNmQmwpSaN97um -XDp1vZS1LsHvwePWsZ0wTzCH7GYBdrPwjryRDJEBARC8s/EINs4oCvFXM7wkCqjznAHBXvxAV7un -+S8wDivOanlj6uEvLiIW3UUJrMzz9I9GI2BgbSeqTqtFQB3guxJ5cfQVDmw/+75YlOs7Zli94g3n -kLgfg0l691k/EwIJjHIGdnBwZuKVzTxk/xHfj4bwr0AZ0jITzw+BJgLVewa+lzemjQoMG4LLkv8t -9XwE7ml09ya1pLJzKDfAoBieqm8FHwmgAw3eA0nPQOoVEEPzAe4/Z3+W0MuJC+48AQYE5cWhtKg0 -UKGap4h5NO/ULWtWYXu6t/Edy49CT83WGbH3O/NB83zMrtkC+JhuAi4nQJKRFMhYQx7DVQsQjlci -JESI9PuU2RZ3s3ayTJyGe5YHeVLlwXJEk6Zzk+5iXp6J1f+8nFSJJYNJ0ic4+w7D9sCer8G2IDBE -o+zDjFyLR/DTcEKhl5lKQYANgURnnnn+1rj+MFuyXVwgVu0YODg/URgHHW+v2w1oQBVGbgOfOBBG -jzUKV9JdwCQTiw2nq2PUOLSSw/LM0QI1Iz3A2BIbQ00MUZ1tAoU/0o3upOT5oanR8R9NC+Cwxy2V -QqLC1YAbEhYCwGmOwIHJHetMiEZyAwfQrcrRJSQ2RFKcICtBgtwPSwc5Dr080lnE7UX/R3r8jE2P -m/feW54b/zNs+9kC9/1/+V1f51zbRAZ695Gg1pcHj1hk3GIOb/eQPFsSuLLAaSzMZXExpt1MahJw -bXt2xUcmQosDy+eb/FSoiepkHU2XOuYt6jaoolBIADnoC9obY2o1COjH0vm49Is0r2uLpCy6SEit -CQMu8A78VYWgqlXnwu89EPw6qbTHKd8kTZaAoAG4I1kLncCBquGNKddpr1nMI1bT6m5IAWc4JnXg -HDTejFBNmAr/d5v2x2G+QHIm1H3gOk8o/VUYilh/w/75n1Vz/Nu0ymfmJDjrRkQI+eBxZ5382HeF -IpNXs0B3BkzkcxQ8hZMnFh6d+xylY4QQxc81pymdjLU1iVu3V05w5srxWluILSBsAAzSU2/AZawb -OeknWqsg8IATyGzWaz/bnKlJ2jnWrCiXYxBL6KhcIA4N9CYmjTocmZ0387urPY1tYdWqytWSNVkz -RHsUim0Dnhr8d/3soH6WkAfmiKAjvvtQG2IptTHZWEK0sGo7AYk/AJ7dMWNQTFm96pa5srKqWqHN -c2DcVHdAjUZUvEUuUTjbMEXTcuJNzi7zWxfZJAGmaOftocVd+3T24d9LBMR1MQHQ3E+T9//Hv6PQ -jTZwtQtfxhBZFGJPQIULUYbUBWrEaIylH8aCYc1AiI/1dtcgJK8UFrjVJ/zItb/lwNrW5IdU0azn -xvu4WV8Ma8tS9IfZn/4Epwzody/MnZYo3J/+NLBCk7HpCvfPGeOhup2z5LYgCjOOufHRInrJ+EBK -TP2uKLLLzWY1ePzYzGGVU+yyvFxfPJ7PziCQ1WPJk19uFhJVA/xTHR4E2A5ww7gtsyJANamjIE/7 -v9SXFpoG3Uq6is6nhG0ElMu2h1/peLn2W4WRPBkQqcOK2o6Pqg9IYegkAqWElrmJWuAWYhuieERc -DuBnijeM2V8KcMxXRVOtN2b9mBLCNdIVZW6iFMiSy8+eHzqlmDRHEOOEf+04BGm8T3QGWfDm71RQ -8BZ5R8MNJpnCMC67AIn96U+QK+Ik//Qnjp41u7iAORxn33JlZi3wgPjLhUd/IRDkjLzqLYy5swA1 -0wKfRsXtaj6boKRNWGCvJEOrvHQddy3x3jfwwnMx9PQZrrAFUQm6nTuaF7TqsxsTtyEe2zwcN+93 -TXpOFodEW6MEN7kG0uGa7tuW3e1hNlYhThzQDcXLtj9PQrzAc1zsZrUyO/5gnU1nU7Femm4NiY+X -NFgQ4C7qeXt3DfJptthgKjDlHLUWGgzm2Le0niKQ01kMhkfLMgpjTQaclNH0ip8C+wouDu/39Bhg -j3EtMK386Cegyjna97KMP/In6XFI/2q63KtZIzW0QNVYbXzLcIn0zum7ew+lp0XzKpBdF8xejFCy -/7TtgURFJi7z6ai5N2nby3QPEyG3iXGzN8+Uq/VXHOH0af6Pn9vIqEGB9VBQgjv1ederbzV0wc+t -0zu5R7lyaMpDnsTgUmnSKIZ7zRMudfWZPCgNzLkzNvS1IBw76yYMnMVrKuE2W0mQmO5N7kYuhmoO -Vfu4RKFzs969715P+hkQN+sXBK0lL228DrnmsgbE8lEEJLW+07v3oI6nO/A4rNJwyJI9Z9Y1kWw0 -4kdJOxrZ1E7Aii8CCmJbTUzciYLrFu4qQuz3iUy4nBJrtPVp+uF/kjvK1lAyePhUvP+//xe6pExn -1QTMk+4o0hzHnivBIHB6yLx21paMbQZfRmQjuq4EweSDW8tqegZaTwTYXI+X1TmFyViM11fAraLC -VFrHAYo5uyQfSVrNVyaiikwcqCXtYCfLltbzvY62IhqMdjvyrWMtIW3qSFLBqxl8Ts/K6R1gNJA9 -KQT/uDb7T7Lm780/L8YsUfLI5qyCRGBj4/YR3t2jvL2WD+BXF0XUofVzWquVHcS6Tl8Rj9sZKk7e -rj+YD9IYDxl0OHG+AXx+emnFgwC+KEFMDaYIRGUu1u4E9QbiFX5UahB73or3N+PPc50UBKiXgx7Q -ak7m1rFOv9byBmoDFCedeG2B93NfKQL/3YaB0nAGYz8WYLNIXympb0nuDx864DYeYgN7S77L+fsk -9KGuR74QIpeyiTubclp2EtSYxwhKzxH/zFwIxxXgTrnsOWTu9eRnyq9EhkhCH/lrI4AxSIYnARlD -cgg7JvmHFS6cTiIqCOartfqAr90Q7VYvTIE1pDb1dgdIqW8n5PjW5Pi3bqptmd1p0ejzrH7n63VG -Tpbl47UmJ8Th1U0AsHboxfkxPXZxaEIYLn95++X0s0S2AEdbp+5UV7NVhKdN3YNPQQZ811Awr6i9 -yoSVv6M43U4YwxFJyjq1uIbURpXU1bXH2muqX/Ls14QgdbIVqAeBpcWsp2Suj4wxnk55iQlk5Pgm -Rk0/MOcaBNQ1HOvGrKXZeK65UlifJrfhJ25A/ToFWYxJgWAQYNZSzMsbLVq/cYvaYk3alyDxdb86 -XoPqJcm7glmnCmm+jSRvScmXIi7ig08hpqMcU4XHfphqhm8l5/wp26/fvH85yI6XymDKWYS9lVgE -HHu4Xevg1ja84Wo+viPQWwoZMfi4/Lhsp9vAuwoPljbrFec9dCiEng1Rx9ZLhK4mQ3+V3c1BP2sC -zI10kzWFD3a3l4O/f1heLc1WqRu8hsFae+NqxonuJ2p9Ng/FfuHi0z21MaUSIzhIjcme8ds5sloM -XN0ZuW12ctoTyYNdos74ecoBNHyyUkMtqD5FWcJlz0V+58Xc+CmF6lLfyXHgFYkcUpOxhD5MOHUr -PTGQpG5iolb6GqhgSG9X6P+7exykA8N2e48+EFNYIYQvdqSuJ7dNa+xeXfmwLLgz70htWDMDfgeo -iq3NyypH9OmB1P68JguuPdkA7/c+JyHD9jazVF1yQY5AvvaNSuXzeHlzPjXPDcFGJC6Hr4gXHDt0 -PsgDuwC6Mo9G7tLs8Us1cUZS4UFcyJPWP+triVmZKNNKhI3Hqz77UVOQdNDvuADyykyLPoeccmC5 -ClEThIwFQcZMubmjkl7KEGxQzAkxD0N9tg7SDE2qU17w9z4gukNQBjKx++y+dbj6HKu3N2q0IlIS -EL/X20A+cmJLYe0rw153fAcipkEiJgfzPrNQObKRvM35zhvE0SsmV3aHjGYSc6kaYbMZCsC7/Ts7 -nInZpjrq0gjd69gaB34DspX+eZYAq6fJXm1MU383XkfmO2pEbQ31RjBu03uMakNYxnr3ElUr9KO5 -Ut5jlHKIkF5d297evu3sShFqOGXoevvzs8FyCCfIzY2UXR9JTc+Meq7lIBu0A5/Vsl6wPaJVTVQV -F+LuG4A1WVEbPCfTj6Ibmq0lTILr2xB0jCEe9t1cU7Ouhhad3yy6aULm6bxb/lKuihwjU5yPCQIQ -bmoomrBB0yqX3CdEMyKHnOn4LbfB+Ze4Irohheu79MwLmnYK43nUa306//DvEU4DUc3KxaJcfrp4 -v8kJSkNJqgMgDRV5D4asWDNZHE0xxsSoBF0vdIIihXdIh2L43s58tryCv9PZGv6gHWZtwI0Av5Pl -K4zzpqMnmdIit/C6sHRaYTAvPiPbtNwkc3rBnlXMqxzlBBW43yAepbaMTIP5iv3m+iLMmwLRM+8J -x62+KWhCPvS9EGlOPqP/0q9k02tHIaqfzNahd/crSA8Jgtcpv3OIEbZXYZTUz86oqvsVIIn9Isza -p4Y1lSHCKVzduc3ilzQqgMCKWrGfXd0EbrAkP+bjFUyPCcI8QiVEXMUkdhcLaj1X0lpKnJDqpoT3 -e9i1K5TRk8GzU1gXHbPaOzUHurQ/idvVeKo2Nvvk2eA0fczv2YVIS5SQY9dApgYG4cmi28sSUPon -RF6z8bU5r9B7A+YepSRZ1wtxbtbSHoYHgCPM8guI9Tq+QfcGaG/PvB2ZNTgpDZOdfZ0d1TJYXYQQ -BGE8MUu97I88TU14bgn/673YubPS8PlUkakHf5Ei4CdV6+RnAqvy+s3L1+9B2ORevP/2+G0Nprjo -wIgnTJx6tf2RnFc39c1GBwS8NyaCq99raBVkI24yc1B4dj+f1zhYAD+xgZ7vPccaA3fXt+jtGjnJ -kvSA0v1g1jo47DrsCrHwqC7HgH5s+TaKeAbzFcAhZ5TVWXUIG2PGSR41VzKdXVumpAQX3JqjAcW3 -6GaJiXrWlHxbYBEQwpgKg4DRd8+uak7VwHLFnqEAwIq+EHAE1Qd4NRNzdletikm3I1k7PUHrdAdy -JpEEu/LONwa3Jy4bhfc8hquusfzpM9rKOV1TpSjVUn7lN5RfJtq5Mjx+08jCd4o3iRUxzgw2eZ8W -6+Jds/Vb3Xb93u+A/pLoRQL1BBqP6I5gg63bnXVnSwhBKxblwDXu1RdTmusC+RzalpuffoPNi/Sq -UOG2mp3QlAeEhZfHrS2dkaj1+A42VjH1rbrBnR1gTi5m4MeOfbeuVDWhqlXXl8WNXfbDjhkj3Ltp -M1tiLsdTseA0Z+aws+5EPRoT2OdanAjQp5LcNPFiBylkXuJtkbKr/wrM6p/1BumwEp0t3KE+dJLn -PKaApqapM3wBU1JgBQQrGwwttDOdtQABh92FhylZ511yvp+rro+tLuOLsHtix+dHIEkPsPhCYEYH -WkhjC6Aea2DD8OTpw5cWY/tZgMF1sSjBCMlmBQdDQG8cTy6x1GiK4OybBPwgoCnCUrUgRGYEOusP -nWTQIE4sAQo+LjtNkB/+HMSFNolNqCU0qHsKSxITA2Y+KerTdV4GPYUkYHZytl2uZpOruYyrG5Se -N5ph3846u9eX5c+IM2YHB1YGU605tLifnd9/DcJaEA2G2f5FGGcUvptVYpYUUptNycmiRcKv3YU0 -Cf3leM3j1797/qpLuWLZXpvjGmH1HEvH1D0GhEtHO0sQz0NdjT71NBV4HHIXY22VtOr337783QAj -ZrDH6mRdVtXhtLieTQrYHYkgRpNydReVrPGyYIj1PReEagkYMHVEjBnpMvO1xXRO8FwkGQxI35Xr -tlahEX4/n1IPvas11IufMT6GGWpEE7KyLKA+fCqiX5TXxR/NiQS4hfYU6ms2V9BTsVQSvjAzeQOL -AYlUUCCPuojYBq2kI+/wiIxGMMqydfePUj2xqSj6B7pKdwXiE7FgsFXxrRijQWWqEvMiFWPuymtJ -tD5ojUG/Id0BP/t9+kO5RQtRYElm53cu2rZchM30YfC0cmlGj+ZmDOz+IKZlyUAaXrSmPre6B82h -FmcrwwZQrPEyHM7ESRBelK5uDE37q8g7s0F29Pck9yGXjC7HDLbSHrMY6yRSHBkoBFMkNkotMOFq -HoMzKfKghESOzoror3rY4bI6asHRYuMPZrzH6/Fkk1h2D4WB4ELh1kaRdLxk3wTJgFPj8Oq2bC/D -SVV8Og0y2JR01/aBBk++oBxhBomsC+lthmO7+ah7hH9c8S4/ZGDmEoE+2S93CxDKOgqGWdYASCzB -m7Ag670JlGGcdR52IBnHWiY4OOA71BKEbGrYZ37DAOMYUOe5gcn2SWLdsHI5v8ss4vwF9G3jrYZG -jvi71xh+olh3ZZF1A6W7lq1ylJQd1Prmcja5ZMgqyILGUPZWKJTQHVUlLU9k5jtcRSdv2nxKwWsD -t1CwRBER9HZBbj5YD5gccqslWh1xMmiMxEUr8ZpJ6yLOkOXHPAqqphJhSGX78+TwCEMHMbpkEJRa -ZXvk0jjbQAkD7hc33DPpU69m3SF4x73xIYD5WmJuCBswscNQBTez5bOnbRgskayCgqiDrqti2EuC -3VCua0qTOEhLUxqaeXDtPS3ZD4l3KpOMeoMPDZV8ghCkLsPg1EdZ4GS6fp269VklCy5xW8l44DZI -KXkrTYtqs+8+GutdBJyJ8NZe+7pm1g33IUHBxu523YNXUCGADVyit2/MoeEtWLUTpxEzwSKWWIEm -fys8WfHcPgO/DkNAFnBdl65A9vRGjmNVUxAlqbR2Ys1ymGzXIR9ypuywSZvZxQFOgaNAWmRuudnk -fW/yI3r+clreVA2rKlEu1PpUt4BI5tm4ikI6zKfkWDMlDIR0Mh4KLDIehyXXgp9zFNp3ZVv3skcB -pm1awg5lPIlxNXGrwdiemrN+GUeRmKdnxjowc4rgnopXAumzaaSYuqY2FyX+DLAkNzNKBKhUjAKq -SqakNfsOYjgdkpOgyBcAshbjvcAJLBtqFoR/Wc23lePw6S6WXvYi2hr62xdHH94oEIRL4NIDXQzC -RwylFF8SYUvmJxfVMsKnwVJswsEeYWq1wTRn+zwsUgce6vyccLumReyBvJDSEivBIn8UBsI3zBqH -PDTTBocSc1vrgq5BuqT0TPFQezpSFL/1szvgMf8izhC0ynrcdvkZe4LewvlbL7KB+lJzfdtKJVS3 -6PF0WquhUMO3LG40f0Pj1sEMHTD7tByr5Rv3kp/iG/n1SCtAVBMni9U+TQSIW9Zrdw/NFc2stUdH -vbz53FCYwXySr4kfpOngn5+Dv4ZFClPUz1TfADNUdK9u3qEW1en5prbPdZ3QHci+8nvwmTQRG2/K -cq13baR4zNREiMos8NMTfqJwqUOlncPYyyxUxeDLKXKKbm8VUk5oeEWOKZ5k37pYKTmFlWLApa8L -l1V7NYLoWwxc2OtHoez0LcuyJCjmxdO+oAZB49ken+/4SBa67hJrGpAYxWIahAEFBJj9msqwKuY+ -ty7nlaHoBUyAb8fD8e4qOB0neHFMNTMIREphbKERS+VtQTpSrNN8okTTG7jUCrKLOREQrdo/EUxW -uECrq1mVdc/upBl9nEmH7IsR2tkeLhybs3OYHZR54QRMxoCIPsYDZbq5ZMCrYrwGvtbc4ECsT/Wm -QsgAmg1nyrNv6d1AQOV9vFCIzKUrxjdmpcFRjaQO/PVBsu+kpnMz8/M07bdqlt/BDjG3RTPVuDFk -T8A+oB3QgxhYKUFvHLcbkuPwC0dScRGe+Re8GdQ5rcJHhOoig8hO8v5TEVY4Fr0HL2gzaP82Q8xV -fPEGmi3yTTp918U5qNL5IIFSELJPzpxxRSSuVjUntG849AgWrWyejFp4lYZJqgNhxiwxaDJQo6ES -jZjfdSHpsL64BL4GAFaUiPK8QiyCs8wslqMmlrVdk9pi2fUAhmvgm0jF06yyAeRUiA1DsDD+a6Yx -Qx5L/6O3nYdmmAPcj9UG9x8FUiAZCKHaAbC89ZkAf24dKzBt+hm7IPKuJktQRIQAI9DohFfdSJ2O -LU3P7b3NDY4p0/nBU4e6JyuSABL6xya6B2hbKxY3owSdZrPLJweeOyVslkl31eudRsFkojGOlcCk -/4F2QDvTlkorHaavSzlqDHaIWK1a6ezSfe5yTfRjPsKld/LOdDGuM6xPyvgv0mkmL3a7DxqMkgMx -vHCBePQPPeksc764ndMr2+XTxegxEZkxxErv4mrHO76hjofBxVAMTrS5bANB4ORAY0nUc0BZhf5S -VNTqevlNyFlxi0Ha/SiTJtGj13qntAX1LNNAaztrA77jeLZany4//G+CazIWlC6zHRFU9tPs/f/1 -I9qlv6UXmU2SPX/3Hk4bge9agnoSdZiC2lRpjEi4OfGjSbQs5QdE79iUhkezLxYreVyM1+amOXfm -8BawZbPeTjYKvkUewSmiaoVxJqPuiV3/dmNoLzhB/UiyJxSEVxmZJcJqRguNKnu4nN26b2ChmLcC -Ca0WY4KkVnwAf3j+/jejF2++/+HNa1PmyOSGqPUUfG1ZsgWkQmDdnR6qx/gcdzmCv0zGqIihGduA -Uc4doMODA4d5go+jETZXzjvT+X7Wvig2o834wrbzD+9fvns/ev/813DwLFY5f++CXKt9SJ/bGkdd -sUaAn9Ne3a3uRtqOpu0DU8JZh4naLScDDmXcfx5fj9txNgqN2E4Bw3CKyUoluUaUltCuJ+5n+0F1 -+KAy/3D3wL4XCuxDCRgFB/5KNHBwnjO/+1hnq/XDH16MXv7+PRSTm07BME3aMCyj0bQ4216MRtjU -rF22Me3758evMDEkVc2AH1hSq/X25Y9vj9+/HL1++eOr49cv3yU6cTIgbUL3aT/7JR1uKfulZ/3s -qeXeLPgebWJzHflNWV5FlpwENptBTFiWfzMVqHjXk+XmTrw5lohWFYV18cQ4hKlHbnugtP27B/cx -4lw2Ahr+Cos+XxrqRQwCfs/NNe98dgEr1jSo26aVMELr0zBgvGsWP3lIVlOOqKJM1unoSASH8IpL -mp0zHQ76X1Uuxhm1wOmDKL5ORdFKsU8jHnn45CeU+M2q2XjZJFWpoJr7zgI6Lny+Jlsosx77mYLb -BhmXF2/+8MhJRlex8wKdlR6CjA2+bXUZwZG5st5EOpo9lFRnwp+0uDqfUlwJuL8z1dKTKD3h0z9l -il6Lr1w3f9zO82lz2KrzaRQVA3uxIln85OTpaVgkfKM+/PAHPACOX738Nmlj6NNxCrAxgoNuhNS+ -XcNznS95jKIc3fPlfdw9saDz5clArwxL0k0/vrD9ePfmw9sXL1MYiN9imHeAsDC0Zrwho6KZMiFt -moWExR60SdSVqORYgXyQFjrIhdF6BRZ7z4w90Gqg2cpYbWkOS/Fyu6NiEFvcG5uD7Liilo4ZGdvQ -mG/iO4TZv3BPnW1QCXC+DGbE8BsFwbGRuhBB7SZjc4PfzjMUlJ8VJL4hPgVPc6yWokuNl0FxDAxs -WjW5m8yLPBU1MkmO67cWqeQtR080t9a/wg6fZTPNjwYPBY+CiQGFHVFxtTF7u9erLSPWlDQv27rt -XGuWmTgwmi76zV3q3oAEBg2sZnAbBxn3lABn0909yN6jCQfCWltU+mxuTucqm8+uCr0GQdYip7Vh -qXOKa6aEqgcoFVqUFXDVF+DU6xuFUOjbAdqdkgCJ4/ZIoVpOeCD8QJ8oveRxLCjlzmGdj+emaUhm -/DSqNDm9UByLSngYJ2ji3QTLsMFYb0AAKm3GGGwUKqw8V8WZo1OE3cIjc/8smyy2Q9B0eAaW2Qxa -mY2vy9m05e2sydVdBrMK5U7FCO4G7LtmZC2EdkvlfF7eoCR7eT1ez8bLzQAmUDdrjEvCVIXi4/nN -+A7oCKD3zIsNuRXOptTnNysOUQo2RhBwjkdAT8GmXMxM0h/evDv+fafi3xmZmUKpBZKNS9PNuzwI -UjckOmX4RgzFhC9HYDdvgx2RVzvcHeDaH5BWt9utY0hbXTbanlQFC9/jMDc1LK5AmmSrTZ7Yb97V -nNZFhGBgGPec7popwAI4bflO9fL3x+/ep4nGQfZyhuJVmGTVRyXMHs9BcnLHlpVZN5So64WJik/E -aAE5zWxj5u3MHDNXZl2c3aFGYnkIAw6aiTw7Xmb1hc3xBp8R8sxN0ZnPrXICyTbPKiyn1l4ulPYE -x6FJ30PrBunN0nPjwUVt6OzSjAfQPTL2skPWR/o1v6spTA4/06k1E4S/zFYU6iaZRRZ1naNjMN3P -X7x4+a4GFUMTcfR8QBM/23JFq8MtUHNS7Wpb7WnlQWfRqhMccVu3iJsMK/bslC+ScM1UaDyTcPPa -7dVXpepj53W5mUnEDQpWea6nAkbl0B+VfnbcWWQXJWvNUPqqCgReYqzIH59OYgRt6Hq52kDkoTzP -/SByI6gMlrAbb3AWnXgExqRMXhmCuRSyX8tbSI20bwgoCYrouwGPzDnq6qaO/7Auz8Znc9jY7+7M -WXFLMer5yNh4Blp7XDoSlBQ9HQFtaGTPVRyusOE4ambCy96+HAyBV/OkebIl7Qztj6K+3J/AsjrF -QIBYeUrn5Lu7xHdvxUByKXJ1FmBkgDgK1+9xdjOrLs2fSbmdT7M/byuKr4F3EqyIYxNOkdfuZ2db -CAcI5MXQdXO50kFdgRqBZIL9D+Z3SJU5nOuz/OmjPt8HTPk3WN9ZgWcuFD8p17BDFKE7ADLvtSHX -eMEBrJEMITR8WdzIAPkdjg5Tkyq33YHxBx/zKPaA7PX1Aphf6YUYNsCqht7liZJpTWDZel6J3OQc -SDF3gRwpFyHzhZqiQYROFfW/VU8VZSlFWSgSlD0AsRl863U0RCDW3xeTyyXC3NwhXzfFm6lcz+iv -aFR5+Rv2HhSq3Rc9Wgl9MLKfFC3LaYPYD3hK8I7GVUFRhXEtmV1kFgGPE9/w8uw35U2BwkW0lOoI -zLvhbjfzgrHiMnACwo0NvOtxdmmWJEbQNdRT7pJjikoMNvNcRLzszWfh2Rd51oUQRmLhxeG71X40 -JZbXRU4zt8CahuAK1VUDmuP7LpMCb0W62KNIgdo3Z20PLvz4TcDPmUN6D04OjnRIuB+jckAnziXa -SqI1yBJmGmwjxxmxf9BrT5RmhnyTMnE5qGMDzZToL6Y+M80Y39jj6lq7vfTtIg9GkgP9iXQeL3Jd -RX9tCtKPmEN/ctVtfzVv92niVFJWreTT7WKF2+N8VRNhJgjw6kEIvH0NAu2P64/Ldo6hks3Bsd2c -H/7KzDF9SnyQ0HKpc9YJo9+bId2U2vuUOKmH58uH2h2VFq65OJI02zpde0PngtouySWyvQ6W4cvl -9WxdLmH9B+sxPJCB2ZUDQKJ1s7gcLqfA0TzNf8kiUDIKQtMkc5E3N1rTpWf5Ud+uKlD+rc1dOiN8 -SFY1scMsS8PMWg8VA4mu8ZVbvIrfvu5nr0F38zoejs26gBxjiGQAtfNW9gZEMSwaI/lVwaI6iOux -XZHTi3A0ZmgFMiuvEVaPhQBhxTWij3DIZa2w1KEL7U+QGi/uMKSBFeWMEtsqGnlzJ4/hXuZCvFvx -JGk3Z+zzTKHLtxfc+0g6AlIjy3YSl3HMhwuKPcHCOTunoHS41jfFfL5z3Lh/e48cez/zrmtiFR2P -GG5CIBKKB8TtZl4/NOkf5kGAul36UJZ/ir51PYY7opmO+bYCH4sxoaOa0uHMIAQR3EviooXnMck6 -VHlrhneoSuL3WBC2ZGsoJGrBBTJgD7j/vYSK0TQXDgzY2c5NoZ+Ry4cIXGaVlch8C8MV0CVVGLsR -o1SQBYITMttCHzYUVJFjG5YH1DjjXQ2iPXe3M0lHxBPDv49Ys4jGW4h/uppNuz7saarPXEoYu74S -h2VJwMMj1JvvZSImdCWY9fADbZ07pt5OCmeayS525FmsVxR7570VL2JvtcHisrE6tV25mRQ/8F0j -87HexXyE+ycgmyGnHXFDPiNk+drxZqwiqv8qspeqP33qroQHhJjD0SRJcmgewRHa0JUpKBKRV/WW -PMBIgVoO2tMDLc2vTJ6opSeDL09FhaPYjDAp8xjbpeIyMPuXg1NANIEiiOvY3Ruk3MKSIPTB+arX -4HgIixdNPfIXZnWAZ2EvZvjGQMUR/9GsPQiQqMULdS1xBHMXM0Q7ITyaDPeo45pa2Xlm3QI5inLF -QCGWfgZKegCTzddbRACBykbV+BwwhTn8zKzM5UWNjUsOxi1i6GJh+g0ZWZIjv7m/jOLXvL/5AwB/ -mRu2+m7o3qwalWs/SGa7C5Sni1+QdKGXIv+DvxnpRZeEhiW9NteI1lrQHUSzXxfdcoWe/ABTXAH0 -zQoCy4Ad8chFueGQYGjOCynEOcScKhcFLnVTSq8XltNk4ghYnSwfXxdVtFHTOOKcKYx7PY3kQk5n -BeGbzATlus9pnfLEHGhmgodxeujdyezUDUvwAzzSfBNHLqtWdy3LH5PpGYZxM6twuxyv7wxFALr6 -Vw6OvMlfm7IGAJ63AauDvn1/TDCC5tO/eu8/vNuewdtD/+3z6RTePjJvW39vtc5my3IV1fUvs82b -NST7m8pqXv6+xLd/9N8+X2KR/0G9ffXucnaOjfrqK/X6rX399dfqtbRJvZLGq1ffg8W8efdQvft2 -dg2vHqtX383Lci3v9YfvS6zlwQNDQw+yopqMV+zxLkBGuB834u4PeV5+gizDoSrGzAO9/UK/fUWd -9d68xFc61a+p794bTPW1TvWD4Vuhn7qjxxW8mnkTX/GSoIXmLQl8vfRbTW8JQQDnviUGQaCDp8hN -htC74IuTcj4qz8+rQpkvvTNXE/TokDw6zDJY/G7XFUYGsSSXSN3sdlfhvHnalKCNYb+BYxlZeVmg -GcevdYE9oSRXxb6luRx4yZEfntnw5HI2x4CDMKxwhIzwzQgKqLCTwTGJncc0yd63bJraAdI0AhK1 -amzOzHFGcz0t2JegN/ACEziUL9/Z4bsZcMLgCZo6QB+a9A89+QDKtBKwBXDUQii8QSQBJQckuOaU -eZOxurkXLqkJcPETFbVYaIwJ+GFjrsWsCGNBEtz7xuegqhsvtSAYooXj5q5YdHS+3SA2thTpGmO4 -O4DtBUdVGET82VWyVv4LER6BZR+NDKv7z6u7kbxv95LQ5a6sdq0BcJuKwgAA43W7p3z4UWAxst0I -zz9zf/Y8ne12eOJH9AR0SGaF4vkBjjWqCH3iwzADSBJvV2s0iYwVvn7ynHHUkZxv1iksA1OdBFTA -xHmV0iK32Y7z2zev349YSIM72mSv05W+d2sDlA3TWQUy22lKapA3GLKksIBggB8N0fTONKCXHQYe -6TXzlkBqnQvkbHKgyWzuO8N3sh7bjBL6dGVfZ0/CiwIPFqThbpubQdut95S9ml0sVLRPQOsNgGDN -mf4faetg3J28c6jd3RNc9adC0IYxXRs+SWpzUZMJeZHI0q481QocXMInphkD839W3UAD9L2NYkta -k9qWJvPYUlNO6IuO3+JTARnVKevBepHTBslEztHXQp8K+CZ5IKTctAo6Hao6ey7wbvYCRofNMBy6 -PZlc4F4suNeIeKtvfHRIIUuGA7cD/tZsMwxRCXblNNJ5Y3rTiRyRPTnoHPnnYq0NJmj16ntdLiM0 -UGGNqev7/O79jg7bFbRHdRXDx9CpTqvENLSXNp3oxqtBmpSktb6FJ/FdLIsztGdN9nRgUg6nJIam -dchLtYVFuyXs+SZLNxOq7NWtXD1etB6VhzZYcZ3NUwC2v0a9Eax7SeRxHAfZB/S81ThaJKEBjxIO -HTyFGIOmTnLZRLMz9CK+9Mg+W3zgaU5DICK+pcDT5tKGEeKRWABkq5CX77anvv5YmDdQDrtYVGaS -LpY28Mgq8ED9NYhqH8L7hzAQYGSrB0BcnHTtYaw2x8dJs3hvQ71IrCGuLXsr0TkNUXR6vVNuUNSD -RB6IyeuF8eI4gbUde4Ge8554BWS+iFFq124tsO9lMYfoqG3J2uYqXP2cQrsXPIxherEVY05sbX85 -Lruu3KwKHmvbec2t+YNg2T6z9TmHBTLocklgyNdmuPJ0bjXY0MwulEbHQxc7AlH2NGqx6jyzo2kD -DxSXWY2eaMfZoeEhJH7odZ1TJPvvGN+dfXAjoAvsZw1rSMmwRoAPt0ivJrH1XZPb0/33ra7HFlad -2MdTDOqzWkfYVw9QGufqBzFb1dbw3NWlSPgQF/c2HRIu3QDy2gn38uRKtnFDVg0RXq5STSAB3SgY -Sh+5h6vzWR7rkLOJgro198Xl8hxdroo7yzWaG0LX/O4hM2MeYEcK0A+k66pbEfcJrF14VX5rHiF7 -xbtEcuKtAtBqel5mzvUvs+UbErziYPRFTARgN6qOXvKkoAT3X3HgNMVBm+5Nk6HOz6PJF8WyWM8m -Iw2JErCmZuP/Bm2qPA7Ct9Vh0Saqi0xjPGLBsgPFIRDrYxkE1W67KPgMXHoI3BbkturHC8tRg1wO -GpOwF8K+jIiDlXA1+GPki5r4Zb6oLmLFBtoHWbPjsbjb5sxxwU+CGJptkgqPEy79tO5E9tl5BnZb -TkeGoZmBJ0S3jsNIZAzXHN3pyOWXEdR6CWqSKMmtzsTHFE3TRoG++7LoYM+2ywlC3ytuRGFjrkbW -5L+vyb7lbXDFylxtPPg1c9EDCyKzFrnNMkvuBDNXRYtyYPvmNnRxIZWhdBwk74YksMQU6IDXwL3Y -q+PzrhTbx/oxJq5WR+o+tnmwgJBoFZEdoGIB2n45foFMqmTK1qi6sI72MdmXUhQZXFhSLxxVrKgC -m6bqoqfNxmJewAohUataww0UtxPOhAyNFGSGxjTkNOZnAohOzzn3yWn29TB7lsA/HXEdCOYEEQzD -4uK7ZFO+MDdMpoUCxHzeUpwX4zXOV7k2yyhzGxZM1IqNGLotBPQojw5Wm2UQgGWrrd9wQsTxe2xO -X3AwwaYOM3Xy2JR9NbXQSn9CE8jn8S7A4vXQfDe7RTFQttwuzsxU5p50stosnA+cKy1kMJSeAnJY -op6LZEl+h5LzGIRHqgjPjNcu9lvAOYspAGMaizUKei+CdM6aSKBgrZxUerUT9Acz5m3MA+JiXPR1 -Cx9AtxoK4Qi8u0opV04stwRyZhVHmp/aEGYnVsVKT6E1eO2cTQH9D1TFJ9C1PjXu1CNpspyOz1/e -rrpQIp/dckjjgFppsC04eUeuPfaDmx7N2r+YY+6NhOombboXxKgaXeNqr7sqIwOIeJg+U4LLHrhJ -GN3kHjDrPbrk2dLMNY/2Qw1JJEU+GbYopok6kJcryvVGM5BymgErNowWs91T5pAb6TSJgw8FybA2 -QIzNVRKz7Au09zr3f2B7KgQgAwt+w0b2wSh7vTmczNaTLZI+0BEVxVRb47Pg8toXWvrNiXQVs4Qj -LfR4tlwi55MQkjYw3ua0huO9r8oIzut6/shliali85jxwmSDCo/TufZT6TMneVHg9R3dFYgpDpQS -c25SA6uguYzQeQdEJookmWaZsVLFNlCiHdMAWqUuBvCO3H9m2Ve8XOOJh6lD0K8qLWEf1cU35Iw1 -TB98jRfArgVWyw7SAtu5try7AW91f3XFyWCbt3asUC8NzpZiKlMcIG3Adj9TtAxHZ7sg86OAm21Y -TV519TdXbxEzoWw6BObRISCzSKcAmtAkoaiscc0JPhk6C942oE0fqbsaE7ER7lH5Ee9VKQK/N91v -9TqTTH1dSy99+5V2P8i6uhX9+AxEUQYfgWDTo+VMd4uzElpujX1O8Kmm7/PifMPyEHkMuk254aNq -9eziUrLZ52Q+/Fp7Heo+qDL8Xw8tr20L+twNXfquEadBUf2RbqtC1jUj75nr+WNt2bE+UkQ10uA/ -BnHtzWli/k2MAKTP4ZviIdYXmDC410NRIOuN31L8tvg92IKDrlKSpMCd4TVLfHMoZbDPiYThewN9 -G7bNwaIHUdhtnySFP1ss4rsp11PbGv69X4s4cZ4Iae9GSBNhzmAzmu+pEzJqt0oPEt6h3NJ9ez9o -P4x+PKIyL6lOeNl2tKP9sL5m6u0gPQq1FUsUwB3VJurlAtsPqq7sUrva+xmE7SMzVFuaGmRoV8hF -uE0j66qvZrGvxtC+bRQlchVeGm8r7iVNtCfBFpT5rs8fl399AFXC09+x81J8P3NPIXVydMWVFwks -7RWCZZYbD3U1NtCGBPlkc+tOzV496rYvAMaydXykrUTcThAtrCfYammC67ogtj+QlbRXyQvUT5qa -FfKx3pzkNC3hWoWUNTPlOq1aW38kRGcB35f5ODC/IvTMxosAHk5Yes1ZC0WGRy0xS04kFFxs6VoF -lNWZaGMxaKetDnrwPxW0MnehuPbmCgvDC5pXrZIMw5WjvhhPNtVUzgwm07crd42moc1RBoFPZJwN -YaMqnzKF4vG7RfCGrdThpQz+aXghBQ4BNDojZrqg2bNNSGJveebwKZ45XUB084MmWFMBLim4LN0t -PJYtzaxJH/Vh9w7RIheJ2x9tF+KvEuyV6kqcN6oicUHkbbUOhElukZtewB1Reny6lzhRX3vVajuZ -nZ7anbwOWpLeV4k5CwxKJIS87zFwjv6JoA+SYPW5f6HiM827TIXOFu3+Ttsi6On77QrsYcyc+reh -e2R2G/uzi2BXkM/MbT1BkkQfIrTo3Z19HUIX0umCLDzJ9nAJLFEx6zrXKNbHElxaDYG4r3qx7jrL -YopPf/7wP5rlQr751fXyZvLp6v03/ycC+bbM70Oz/BdAPABAZgooiDp0OzqmvtuesWYj+7FcX82W -Fy/KFcWKRk+9d9fLH19wMRgsWoLOASwWxUMw6TQCMAQrQ6RYcAqD3YF3FLOLxmsF2CuYvtszdoUk -9ybpjTg0EZRSq3Vw+Pn/tQ6yF2OKYwEigGqDoRXQOB1cF4FUYnyGKb4/xOgMJk/3QitMTK8rCLFh -Y+GIscLMG1SzIg5aBO0MPrjZYnMIVkI/rf1sao+IA7TGAKWExcVspIGAsPaXA3DnF5CBcVipsLem -qy+X1i0qAcW9XSOXck0TaWhizFiYJCDPWAfA9iYTSsSu/de2GPPRPmuY2WqTiFUr1i4wN4B2+h/N -0UFnR1fa0LfV9oOaerq7L9z4QXQFsM+G14RZZ9Y3QFmD5dikNDuimOpVQmsCKcwS8TRma7tyqhwD -YBqeeDZFsD5TOfhnUsyzCXuMY8gMhFlSgAZ28bErAQTHCtxcubBh9vQJhsQBQV7FjgaERnCDdhAX -6LBvmEzzE93OoTyMbySV7Anoi70mfsXFjwJt2t6pV9tN/RJKIOzCcmkA1vWCedollEaPDVZYTl66 -LY+5gr1xZ5V92IFIwI8Le0hJYYUlYOjwU3pJp9PGu8Ke+6v1zFCSNjj9QHtA7AlZdljCx0cOdW3o -Nnc3tYlTcSvMIFhBCWT0E0kDKQVvDdtKZeGjJl7LpMqbYEI+Yz400ICdl7RZO9T3VTRDjxxtMDuq -xmuDO0orD1VGi0UxBTCe7OVmvbyrnRrtQymt67uZb+1Oe3gU0jx53bKUiGi6JWbonnwAQQTMQbri -AxOcs56/evXmx5ffjl785vlbQBdvj7LDxx8/Dv8h/9dHD9rZwXg6VUGIwUZ7WcAhDEYDiJqzQTDS -Vn14Uho/v55H5uug7Vc++s2bd4CdHqTMOv886LBLsFk2pgfMhXTN3+HJKU+s57bLo0KQ7Z57v1mT -IbrHNQP8EnORTxZTgAXptmGsDj9lh4dcnwKxvAYMkZm2NoRCOjkLk8xnBNI2L3qAzq6SFWvZPNfR -vf2ae0neniNmzYGVkz6aRw4UgW/FbZjjOnjj/4VpD46/Cvwa5rfQMJ1/AEnYx4//0PHc9yCROGOD -QQEwmKOzMZp9rSssA+NRlTcFvxt6k6d8sie4bTded0QumM+q8Xy5XXSDPQp87GzpO1BPyM9GVbkj -j0YnivCVWjYKvekVdcqRCsPXXa6ueijI+rQFU7EK9FTjMzmyi1uzKcwZtjZbzrCuF9vZtMxu8m+E -jdqUQN5mxPfwkmgPwM1X0NVh7iAd+j8BMoMKdHBZgibL5GcMavMky+pxx8NnPyBIyomFfzCdwT1b -CQwPwE1hRZOSUH4rDwEiObtUf7xDe9nHMKRjMj+3PCgiHQn6dyDX4lDQ0nhoOyPaIHXrIQUb/bT/ -hMkzdxWJTd2li0NuY1U7vg9+Blw7ji/nDhl6F0kLorCY/fe4ozipC1h48xqeVcfbwm/ELxPE4Oau -y7m1trqftSER8oNw5TB3OLzctXs7GeVUeGO4+xeV7WZ3tpzMt1P6cn1IplC9XaHCdM2X4+qylkeH -j16ESNVoiGhJvMHDh1c3QbMnZEI5Bt8Hws2WO6kMBA5C9hxil193QEuwXYTRWWfL6WwyxigW6LMj -fK9vG+uDOIsKSAqsqAVkOVduqV7aW4NBKzjELzebldn4sKVAGPgYTunHkOExwrEAmfUz/K3mgvc3 -5Y5KKLM7BR1BkX/LoohEe+Skeg2pK+6d25age6nXUXn2Z8DkIbC20QhUIrRsnAixpxMze3x1A1g0 -XZxmd63zU463SDolKfyUtPDcawWD2beD0/c6C+J0a4YCe/HsDlwGgujobSnFZvPKMEX4ONgd+dQB -Wnd1E/GyHZ2fEwG97ZiiknnSFFV8zQRainY40darmybB1OqMRs9cNzgKZNdvkz9MoRx2E+eGluNo -RpYzkByuzHDcmmfNxAMflbhXYfnmGyr9/B10dXPiRhfcYkxPKJXz5/AbxnNnGsdPvbqUhrjLIoIA -YHpG/WoD/CyzHm1ERXDJkXoqEE+s4I8dx4qmpmFm6ksLskqg9rM/+2cRr2AOq2MeA0ILUgIXnADp -msbzzrOsY0hhh4Kv0hniNRClX3C0juE0BO0HFiM0MkehChVLIUwpvGtAp4nHMswNxpReT4u1NMJb -zLlPcO/KLWJJU5q7gJITjfZz3JNA/+zk+XOJ808lzY2EeR26b9B0Db0jn3nSeDt45t1mIoR57Yd7 -WYUWiq5Vck3HJGazyy5NB07V/mY2tD02GiPThDqmuS7ZbtvGoqmww6PTfWK5SpGo0+V8sRAC9Gac -MF9jbEEgd0kLwVSsQTs3iePK/e507gE031TUyQAApeyv2eA0KVaRUfUOi7ogLG50a8+SeL7gINlZ -YHTONHecjk5QCPPJqUISYWwUOjOT3tJg1am43+LTPjHWNxyvF+kiqRWuHeQ22yhj5JgU5+3iofsh -elM++F0nbh9yKHP4IaFBww9+xPhlUdsZC/RDgVJtIS47bkLm52PHZncPAUYcb1VdqzhBgG1hznss -xMaYRwu4cvudlHMBWoHlEMWnOBpwVlWdMBJFOt40266kLM4CnHo2QwKSuEFV4on5xTTMEkRtygaJ -T3sRPfVuQuama1gVSKkNkfig99niFKXrRSoOn9OVCaMilSjeXDWdVDZ2xfbRlZcbDBxPQw5hMJf2 -rurHk0aLFdtwqSS5gzCp3yAboDtoCyq00DGfK6XAdH7V0kpdeRBw3WI7YkoVeHD2lxTKQ6b80SGJ -DAACtwaDgmrR2vsygZjnUIirlZBCm6tFPQ3dfZmPR8RR1ZCdlWI1LRZxVZfjNYY9mK+yRbG5LKea -jJEkUuyFFtN44wfCSkjT4rJZbjibTxfjW7Mcdc8OglVlUswW24VTc5HAAfqFJVRZV5Mq3KL8xQkl -Dqhd1y6A9LV779BvUP8pSoQRR6xGxTsNkGqhK9A0sItbCtLkJIUAW2i+cB54J8G1HgGz3kmXLH1r -GgeldT60YwFNV6INr7skJ4rERwdscpagGVaMxC136Q9AIwheL4Yaz0vDKd+M51fg/wdcidU6HkLj -5LCa2XCVBw6a6CgYQSuaP/CpqTeu2DdvufR6caYV1AnSFooVmUgwtoEkIQ4PRtsLymFVQJehfvtZ -IPnP8XUv0WSnVDuoU0ge+LyEraNNoUfgYJkWm2K9QAfD4kZmO/Nmmwye8KCpXV0kyERHv2JdiRRT -fqudSignkVI2iXUakgwcR6YbV7AKmAnvpAJxre5yxEPPm6NnevvuplxfVVrvinvG+7hvu12D4yj0 -yVZioOXaZqYg0FKMoxf+QPcCiPHPN+hQWuen98drIcUn+6lt/CmNSq8FPP4hGsBovFqP8FQk5azs -yplVoq/DO1PqnhSLxNxuxHp4w0klLWchsHY1wdWnxWybYT4Z6Rt11UB6dTPNfn/wh8MHi8MH0/cP -fjN48P3gwbu2r1qDbIsrzOTKs0YoPxheBbw5ESoEYT2cVmKcwVtDKkgFCzzxebHBiN/khWhIm5mY -d9dLsekSI2xzVs7Hf5nN7zy4U9+Wh1jQq+KOrNYUGZmheNZLfNK95bMEydYtyiQ562mAP6Aos75b -QIjmTbGwRQKsyiBiH7nyVGLNt1PypMGHx4ji6hVm1CtE9bSKK2ML7DrWlXb9LfpMF8JIhIrxlGKW -i+m8ejF6/urV8EXW0WvFXN5BdY9xYwz7B5q+7fIKeSOOu1CV8+vC3SKBKTDsqGhG4NWnbUl+rVVl -Vkjr+NWrl79+/spq/TsPs79lH7PH2SD7Kvs6+yb7uMk+LrOPt0/O4J9J9nHdEQFOZnaa6VRZwc0D -ZtwrjDrlvTKM2KK8LrqUo9c6fvfj8etv3/wowcy1zQAPTcuwVhcj1POOprPqCs1hconkse780Vy1 -Dv9y+nHw8WPvm5M/Dk4fgQbbJDnuaX01Hv+oXuK5mM+LizFwTF4DT1iKUa2EddC8lOmrbbFSXFNR -0rfOoBPB0wd9yCkMbrXapQLt4ERKMDzGwcO4oHPQzA16XBUB/JKmtFr5OnV4TejJLJzN0V4FY0Gp -bNyLXQ2ScXP4dQ8wOzS0Axpa+ABRR5h0by5Hm3J0Xtnx72fj6XS8GcIpyd2Ppqh5CjA/LmX8CozX -Fz6Vx6ydB9U/P6iwTdWqb9NKEA8pKJHrNy+ffyv5PFJdrahbZldRhOxwVVE/ud1Rx/HcpQJhExZk -bQL2GqbA+ewsx7cNK43kP8Oa5UR1KbGrNIYenInHx49g4/HYX6ZYRn6xLrer7lGwLm1JnccPKh5T -P32i8N2G19hdbvYJmFb7ZfYGHvxMHNZbWqXLScVoaUjIErfUInKddguJ3sWLKVmbt5Q4p7eckJMb -PH7sF95TlgnPt2bxkD5UHftMB8zeQ4ESaDY1mjnaJTgL7YYTflsVrOyEgK+g1Ja4n1AobtE+Rckw -W312XehN6+x5uRAwTOHH8LinsnFb0GOAH2WrBPhy+8NPpJpB4BPyS0lNxleFubmVGIQhYma3GnlS -WurWbZvsntodLZQzrZ06ToHa3phljRDsSlECdohQUSQ/FMF05/BQGjNsG+YTl8I2jBqL6xRa01SO -tNCVQ3mCgkRCq8a9qdRleQhJDjF1J12Smo7mopaHKmkn4p464oS5BhCcfa28v+KdYtff8EGV5Xn+ -tbP3loXeA7vI29HZnNaCx0l8rB52P04f9fDvu0e9rJs/hAPWbUfPqaHBWmgVmwQZHu28IIRyDFX0 -2JfclWiPeUPOFGaDr2aFkkkfbxANnKVyWTVbzOaA2cQBrLbLCYUDN+cwh/PF49FPp6Sh2AerxIWa -J/MZ4A56ZuRkukSsmq8DALOMCfjZ3EygsiEZISHNCCy1SRMQmnSYvN46Us6hVKIhQ/MEhAp9dBIs -YBMnKXwNXhmU3ldGT5g6c1nIoXupZjU2jqBVoUx7HG3uv5QZ5H4mb9xUdnmTgQlxGW0H2HbVdkoa -6ydnKuvR10ib4cwJrNmZ2TBn03F2O0Dt0q2rthdYorERGXyy99zrdEm3JDYwmxNpy/BJj3QVXnki -DfON2RqUalqeoAZnCHYMiGyNGQzlth87ERptjYne7NwWQnV3RiaVZE/fNtnHRondY1P6EcluUaAM -jwdsVtiVNyxs9qyaYtxKW1ZfB9sdgR6VZiWNKwY5Wp9navjjCwTh9+bmMzUFVj4JQUd5Zm0sJ0tY -Z5OruYTNI6BGnF1BWKvTd9ipppimULBS8VwvE3OtRPHitSXGnlYK/+OLQ4xB4CsN6yecy5NtyhXL -JHsLHIww6088dNvrPlgTjIFnPZkdZH03r3sfoqJAVhtbZQXOCkhGDWslOIDEitZ75WjjAz09kC8P -uDddPXw3XXZrqo9a0fCwYQUs/sEQqxZ4Gi66iinTMBs0VGEHezVbBVqB9ZMCW7UxaF+o4IaNddKB -o5a4fZPoNGw4OcTLBoKV1fPCoKDO2JUoMQl8QIy55beEC/RK9daKNhwHf5rz2S37LhJCYJGdGdJs -6DOogwiLH4nnDRxbKKtU7vQcL0RJvQB6JGsTQzdvCEqeFDEDlvIQ5WDfv3z37vmvX76LDVcuy/mU -WJSCgiTmSSke2gTYNCfmO9gBdl7EBZLXXMIFJKSfeNELAwCq7QAtS1uWxA2BtPcwTYFA6EEhrVjm -ntRkBY5fqVDaYRhHirhdgAw/BxuKdWySRalyEr7DleC83C6nnV54ofa5nkAvQCQltsrShbdfPn1i -/vunQfsnlw2+Dl67UXFPWhBpeSpWTZxHoJbvmffm6BemK08H+2ZocygMMnq3kcVlJHq7hwIjmSeG -wkUU10aiW20DcTNDcWXCSw9OSfoMtwwy//jw9pV/IhIBEiLeofSGazoxZZ0qGopcd6kdQflmYfie -kNajHITTU8hl4EsweD1AmzJ37zUjhp9ip83kiaXU6dpX2df0wrnCBlPhaoQV1uyNVRsgJXBuQ5uu -zlH+LGX6DM0EFzqUNLUbpGWz8/pyG4p9MAUWI3RGTFMnfax2DtcdxpgnI7JUImRQalbJpOzQKa8W -yHYFwWt5ecCi6KB8rVFGBQuF8unr9R0dqnbNWr+PrIvzevh1BkX3/BVkuANcQdA5bMBpBHNUJwiB -rBaeKpKEtGuHYQsW5pBZDQOilVvoHrQ1wuB4xbBz0wm6TsjmYpGEHDPtEIyefVfBKNxMEvvVscxU -nVeRFzWFTCt22vUBV2HpFtnlsQBC+OtmQ6nA1k2x9NyChxxjBOUQyt5NXU2W4Ldn0kccLACdOAMx -31zEyt8wNAMSHbCxN9fVwEWCEuha4hgU9dnx8+LKt1aIruaorMcK9IiTg1Ri7CkpOfmPiYM8BH5M -LPDp1Mq6Z3cZezWYe6UProYrxWwE0wOwphdL+nHkB4XmIDS9uAk7oQ/V0voMAP001HBimgF78Azc -uNzawFlNGmSuZIaQp3zoc74wRzy+sehIvjDxK6ZD0sbEVGyVj6fTCPOWLm6+hweSMnQnAjuYfvYk -jWO2qlkSds2tEguucTGteE+22/qd32zbZCcevxLbn/Q+Zd7iP6Q3LK8atMjFjoeWso1WsnrCokUe -nybustXBxJ1es/kthqWaTlN3ePBaLyHK2PxcxKwxb4I1mZQdRS1YnSuHzWR41Kc1OzyKCByk5J0C -LIFezIaHK3JwQZx0KMasWfv+4rxYlmB4CTdWQ2QBEAR/zm/GdxXZhXflGlae+zzK0qSd38GZhu78 -xWK83MwmNdbMLDAyLemjBAFudBTKGpsPR5IKhNtOqwyCTRQctnSVRJvpKUA+8IB3x8u7henkN4Y6 -/3lbSZU+9fRklziRolHvNQF8nM/HCbYOJypQF0JCpYzAJJ1eaiVQvWZHP8RMmkU1rAMvic0Y8FPC -PQSsBVI4ELpjiiAoVxpdAPMJvpmz5u+TozzV1PMW59Jh/idbQimctahqULZPi3BN8Cjdp2Vm/q5S -+7DCuHjwFaK2TuZbWGY9CaC2LiqzSU1NHru1dSbbliGCEjq9yD2IF2kE0nGQGTKNoByAGkIWmHQz -KiRc+w6R/HYJXh9LyoxWFGZ0sB+sSNHSz+2yrv/bJY2A2K3OMbgGFbSz01Rsutsmg+chaTIMOr2f -YxRsQPuuqePky4F3V5sX4+V2lZaaEjlc3mHvKrqe1c4yAV9Vl2iHdwaW67fAnaAQen73xRdf1AuO -6HZGQ94LhCAhb1Ypa3bA6ttWcs3Ey0E1fEJU/gn6OYG6cF55PJpiZQ0zjHFacQW/w8JEJm1lw7EB -/oH4FpoZOivLK0PepodnZhjRzxDfXG4W8wPw359cHj47rEyBh1/mz/IjVYb+7+nTJ0f0cPRPT+Xl -n7eLjGJj+EPc8j1sqYe79FEwNXxMmOnACywPXi9rN2vB2uXS1oO3rSq7K7Tfc3zsHxzlTwWUphq4 -VoK07vCQDspD+za0gVWJO/59fRLyJRMvTQqGb0J1eodipxUs2mlZYAx7vFkCKQNHlMqZXvBfFeGX -yVRi/A+iTqR67IkuaOEGYgt6ifm3TV1UCVWx0RYzZwIkoUnPDq/NkXC7mGdoFkDNywSZEy0OkmuC -6+oT72G745/rKcKHqqHPEm4m2v2fv8W4lMpyw60YZj++eOdITy8HwkiSZaCwpLZpRIfUZf3++1f3 -Kk68BmwZ+g5/fq6kKglRm/XNg6ThvZ0MDi7GoIh03gsgF+vypTJ0CWfLBXRhgspqGNaUwI6lb7CL -YqGdFi61D9eZlV71ms9X6JUVNzWJQtFwpBFEBR3SYIDADZBtLMwVwMxmKjxUV4YL8YEXEP2OIo/3 -0gAZqvHYcGyPZ0cDCKb+oGEaKBMGE7kFsN7FF9590RAvThQoKEUICgz5eBpmZOzhrq2GO+AHx4l2 -GUAmu5b1Xf0hLtAC7XTYZodNHW3G2D/crIvFPlAYZiBWZjQxvNcDdC8Dvo4MgyxdZ4fb3eYe7eJ2 -ZU5rw7hIfE1AKcbBiLCGryWCLIRvW5C5Y9VNQS3LUu5CrCVYxZITu55yPDaZaLGOYMhoSYV2TvnD -F/h+UzjHrYwsn3K2nf72zfvnr1711LUHMjCJWFQXw06H78TR/QdrRCmBoMuhv50+RzlVlWADZ9nF -FkMygbYS77WWL5yCXPasgMAiGYS5/OaLb1oBtefaDxcAF92W28vhvLwgk9XqImW8149uERHHAOU/ -MhVkh687rb3Jf3SYguoOTV3QMADVvZHu7rfFXeI4Q/7VZ/rjXUJNcRPPm8WkTYpPYFEtnLWt729b -eQ7AfcHaT8mN4Brjed8CeScni3KZclfEekJzJZAKQcCdbsP4gfhvSqKkDlYQyIVELpaIr6FoJnev -I10DWQC4VNGVdtWzyv1GRe/USSf295y+iIaqfoRUqALX7gvb7tTuR2wLcOWFW6IZvvFsDntoWdwA -wfDbadZifTvNx2JT/LSmmjJ+pqZa32++odUdvQtDLvGOex54g8OCtG/IMypvHePNAHgJsnNGAbXi -c6xjlRRrGHvCeUax1hbxGPCLKSyQhSauHIkRQlbh8G1auAkWKSNLMtFlHdpkx2EPGPFEdUFdcuAD -UAb79Hxc1qQ5uRXpg/Pwwm8nR4PT01QXPNc1ajed8FqOde2iHacnFxI4kxSwjlxeCGNlV+MsmMwp -eEK1lDgzElenpohYJ08QiLV7c5Qc7ZqcncYz+r8NiLv/H+HuHkBKqDfyV4824A00m7A4cFk0LL76 -7Du0qDZdg8Y0RGyp1TP+V4rdssccOMWV6vzPMLRocWXdcMgA+qhmVJcZuPHCWbmdbECfS/z1NUK5 -Xs9A06IcgJLmqFIHqZksD5oLu9KLLRnOyz3M9Pga5dE+yNqpcwbfQ3azn3GaiDJzZ1T1A6uWceJ9 -cw+rkKu3S2u2H+tTTTVgVKoVbW4AKucswQbziXU5b//ctfvWW+4i9e53r7Oj/Bn6jfAclWDlOwWD -PhDUmJs8Bbefwj2mS3gd5vIEd9+gPF6GT74ArU9pRvbMpEP/4352tsXoAWbdb8EpuZTKZlJtUBaw -TtiIPM8jeynKYdkMME/qpAzj3MITm0RlfTjOrHrSKhw6+5vJ6TGnOnope372q7ceQV1JG/T3BZrt -rc0iGZ8BMjOH5oHIKabF5U2FexmmgPyCYIDQPMxcfyMbhj3BvbVnDexxJGxfhJTt/kuwXTe8g3b2 -qPGMbIO89Yuh+LLYVvWDNkWBe/27MvtItKKLrHnnFJAM+UFtNjwHGxFW5XrTKNqsik/bYjlBCCWg -JJXCkuRCKSKHwPDPwBYagneAqI/0/iL9c7E/qFkgxsGryTL0C5tclrNJUX+IKf8O0xe8o4beuTOw -VGRvtO9efw+XfrMnzOteIF3ZLtFyR+x1DGsDbcLD5BVMwQ8KMsWDBzETD5RdeTqHZiaQ06IcwqIE -4aFSLNDFyZNLwi3CHbyzyFVDJpIq78WigP1P3VhIiN1BfLysizbLZhBNe8wf8zplFYQLQtAJI6sB -s7bwlorJaK3Fpji4Juw6NddR4a3Idm/dqT3+IS3IFoFGW76KLBchb9Jozpbv4OQC071d4K9pDxqd -C9InMYh80U4NFFECbyRlE4fOLhozJAn1I2ABMfuLUQ+FZu2PIWTBtKT6BpCbbkAw+6Hdde8+4EL/ -WXilJJvU6X0xrGVO6trrFX6/8/gzaooZnj3BmMg45cLpuTA668aZmI/M1gYjL9PcM3MviQwEk2qe -V+XFS45Fw8g6AUhby9YkQdDwB8Pps/DdqcmsU+9sbXVj3DbJ7wVoCvOS1TKgsnA3AgEXFJBos+lq -SeimDCME1sEsa5n64i02I1PmYD08YDB0mWEMKzzg1jwMhpeYrbR7gQzGMPMGBq2ugRq30ZCdDevp -u5cbBmKYqSGpyVko43pSG0rFQ0nJyHVU5DBLuZOYr+UKw7W2GwVANhkoHasB8zm2Uru+vPgvMD2c -TyYL+3F4jb1QVZJHlSng2vewUuc7ggKdGbYQeBLL6rtZM+cg11Xj8ebKgmN32eerQV8uDeRotYJj -5VlXNehRjXFK+r8OWU9dsB2BaOIzC1lwr9Jk5Pt6OPu2s/37FVbjG0fKJd3je/Rij0ZmiegXyHLA -UMN82XCH+Q94qIODYZJw0owNdYbjH17WpjWzumfay2I+JzgQ+12xQP46GVLDQfa3MAwniB67YWJS -/lgP5U2JUSptQXdoVs2EzbDkJfDO2ifT8K2zabnov7w1Y4anIlwNMPqjmY9uo69hAcclF5CjE+M7 -spmg6iN7E1fHLmykJasFhDbv0MsT/3thQ5kZmrsxZyedR2g2jIfACwDCzBEO87Xh3xKwCFJIvjTf -39+tEBbbvnz56uX3hiUZvX7z7cskorlSNMvJ0JXcvZ0C7P+vAOTuG8omYLn9O4rGYQZ0XOKaxYyH -CgR4AQlL3e2I5L/T76BJNWitzfCdz2cT0AR2tks+pOGH2Cl14m3cIZUeJgNl0MgVDIWgiSs+ouHT -yAYMThU1W4IYA4qDHIBLuZhVqGuG32zP3iGEhSt6YrX7NHa57bXq0IkE8UJMkvD+4n7g4bVOAY7k -AcjHXoFGqWwgDfgQo2YgjaGHVjq0AaaU2QsvMho2gvTOJ9qedjyfKzcqlFUQ1xaohaYuPOt96he8 -fAaCo+AxVzcn8PI0pgpQrNzKL6Km92ock08gCwhpjjy392l+VdyFvlCmg4EeI4d3sQPLXPCpQYBB -osdqAmpZw+yy1BFYngKiXJPLxFNzjx0DU3tWbG4Kc4RahCpxuDxgbMtLc1m5hpiocKVGKRoFlENt -L5Uxo+yiR4aaUES67GwEN7sgR8IzUtSZ71UJMXYMSV2XgNo/6DqLHGu9FyAPPQL7m78d9vDp3SP8 -mz/6xvz969P+3wWISBaLMvQzu3XcR6O+z9ouke5GaJG1ZwbbbajE8DyddGyQpIFj0CJpjLTDTTMT -HNp70Dr/fATzLDMH0AKtoR6k7L4gsQiP4yUaxQPE6ePYncwDwcRTZAe0QojcR1DxDsc4fD4Z/OqU -NNonvwqCXxzw/W1SzrcL37R+8qQ/OepPnvYnz/qTL/uTX/Rv/7E/+SXw9VCDXwxEfnrYEU17aNMP -PCI1H7O2+xi6rUs+KwidU23kJTwHwmkAh3wCZXe++f1xQnx8vuSO8sDTOjqqEy6YskBg/01NLA5L -k93KIN3aublqjM+q4VEvLQywyyvnY0qYlRDfyFPIcGt+f4/WOElirSxbpQ40hK4X9eBQKJVURcSy -yUSn5Uy/T6+P/+3mgE/3sDX1u81fs9JKWHX/+kUHAUi/xDa/6ySWN4dhKTc2Cn0xZfvNdTEpZtcg -FDXLnTft5EnQkoUiSbkiwGwZR5tiPwtSaPcvsaUPa0YX9wsUmYxd9HPug4BH27U0aqmf3B9gj/uC -u9Cae9AkGfRIOJmqVhuh1ianIRgyJD9L4/i0Uf4lnV72da04kVgHdGFE3Tn4QpvzelqiGWme5+Da -cjleVaDIvBkv4WtNQdWGzvcFSvE2hdakomMj98ScI30IkLyeXVxuasoCYdtsg2IzkuttytXh3PAj -c+c2A/aC7El5M5sUNSV1S9BameokXz+TN+ZOul6Y8cnsPQFdcXo1JTk/U2yRYadQkczxQKvAn+d+ -c3mQXRUFmPrdhd4AaQPtEJidLbXlcO7tJQOOGI8+bdMas+v7bs4DFoZyUhaHttIn4/cJupHKDzdT -OEcgiuQUtMdkW+55FVNMPZ5RuU7Dco5t1RXhkDtfE8HQ58hzItDP8MejTjZoKhzX6b4lf9tpLIsv -q/uW9qK5NLkv71vcvzYXpy+8+xb5RXOR7ka9b4FvmwuU+/bO4hBX/Ek91+yxX6IPaCw0uRF/4jkO -/T6q3USqjZ5oo6md4sCHyGYlXALBd49gUK3fHvkZRC15ii15RZvjF/jjt83NIkFIU3ua2Yt7HP5p -zFQo2dG0HUsnlI+kKUlSWpKiC4HsJHHGOwZisCfvQ5W7H7tve7H7G/Js9iYNgnYwyZgQym7XfbEI -wNrjTh54y/z8t3I69DpQUifrmqoFYs8adG3IKBKRHzY9wvYZZ5W9vidv67Tu4WptnRr7HixLdQnO -7shuDJCNUFnx1HE8gDM57KPiCpkOTHO+ndN3aO3sXMMMXhYEvXQzRoNkZE/QPchedAxDpr0LgQkp -dRHTYjy3diuoaMVQFtB4Mxx4QcH4FpvskD6jOxfwWaoQ52kL+2e81uwTeyuPgSE0/VBslFYoOY6q -XJKgiJW7SnpSldLA7NzUgcKUGbT/3156IiqS7P46kmk5qVGRwGrcW0Gy2ywhYvrAAUc7tm3BjB69 -oU2bQCf0kr0o/+Xu/fgCwnPaq4qPTM4Z69xnAzJCiSEoK9TxXKJuohV/qMnBrQOqkWKOgqnadmGi -ToQRhewlFxDUhljEgdsSNreY+3kStd1MDimtuW498UcZV7MsMSkwqBrSdOLDQ2cdgp7AmnIkDqla -5pltstJ32/vLd5I3DGRmguba2+F+bd0h/6mX/ej+paU/+0l+PkPqs/dYiFbm32DaakRCn99Up176 -t2jtXqx2vQiLo0qmt1GCXqR3EunyMgb3VR/MSZgOPA21dp7E+jPLiXW+iT9a3iv1ETERh2FA6MSM -tOU20U7ZRVbVHhcAlrMLDduU/hHkhhx94HeTO0oY0zphpbmcNDtph0yS7TeL8aB2Gr6P6NzSVaQO -L0xbUxGAqtmyBrViZQz+hPjQNrEZFmUxMGi0zebuPDJtRQMCPI7TCr7kgomasaPLUApO3MfGZu3V -fmxR0PLW/clFKyGSUVsAzJmtuhz2bFpc000o3WfTj/cU4ESnsGmHKHVB9xtXwrzpwDMGiJN5h7fS -+ccpRV5CCZ21QapqoFZCzvwEf0ffIjeMfTWk4ZBvMLqiaJodte573e9FmRovv+jnvYvxwUQxudZ5 -WWfepd727YD3Pl8S8V/dJV3L5ogKwJ/Q2cUsvum8wCi2FbOjgnsCxpCLEiXm52Xg8CxTU+0k+7rk -eNJcQYmxU5y0S5c6PtYev7xuZpgTJFvnx2Vj7XYsDekNfqqKJ3QBvdpn9AhDLrIeK5ZdLqH3GUKs -n1PAEjpVDepMg9jZSqNngRHZIIQ9+/D21UAckiFCZmWu+lf5stgABttjcKZCx+TN2lDDx9NZtVHv -/JLewsqbIen+8OH420F2Pn0y/eXZ+dPD6fnZPx4+eXb05PBX02dHh2e/LCbnxT/943g8HXv5WZGW -PT36hcZzgxMu++3MdNadDurzO3PITLfzYsCiEvXpFdi3veAj5DnuW9PZ1VVdEtMEqP3Jk7oE35ol -Z1I8efLs0PTm6S/N4+DLZ4OjL7NHT0y2rPs9SHrM+zfmMINk2v74B8JXmBUVFfoBV/BUyjsyQ5Qd -fTn48peDL3/llWfevy6vubwmOyexBREvwZ/fGsTFdfUtHzqDDhg+hGlNIvOvVU5aaJkMNnuw0aRU -/JtUEE8lHsS1x4DVgB4iOv30pAPxh/bEkCFpi6dje13jn9EOhOWhoKaf1WZlEX5sd0fxq6HNwKvB -r86pBBFn11yUIiKYMnBZXsod4+F0zyaX5d9Pe/uNjCoCZWjpcMUeQK2pBsU1YWxjtHXVsYXRPtaT -TXXAMJUZNYRtADFSokGE/jAdeX0L8p7Wlsw3i7rCIeXInvp+wZz1tK5o5ODrCl5wNGyK2n0zgfMe -jXX9OrCM0wRGD2dXZT3Mjp7gf58RAGw0AtAUihSH6ewbHVtctdKPLu4siitTnqEZGH0PxNzmOJiY -C8SH9y+cETFIlccgW/gMIkooZ2KX0gFzwEP+f2b+P+D/97LuyaPDU3zKHxo64wUqj61XYrU6ZyBL -twDprC7yOVXzF3C0iVTnB6BEgxKY+bMpESgecJP6XmxshehlBu/+UdSzdBR1cM5YTsdrXD8XCz+S -ugQHTeHp3EyAY2mO6EcnTnOadXHrm3W21YlYLrMOGnEO2r1oafloQ+w8fPi1Rs9xSEN2sTlYHgfH -E5+MsCRuOVY9VOJOVXL4x1JUlKrZUo4+dPXpPlE+F4TDx0vUN8FIm03ttulgSKtDtG5MGncw8XWL -TrPsys4bJrFBEAgViVG9csFQg8ARwwDP6MmpB6hs7rmhFJ9LC4Yqeazbmq3zML+IIvnZlGa1L8CY -6HJ8XVAwJUGvMmvpCwXdDTN6QoMAjIOHtyTqI1uqt10wa4t2htMJEQrJyamLV49vItKKby17n5ms -+RQ0W1iQKI787zjfaxBsm2ZJSqc5ajl3f45qdpJQYJ0GWx5awVcH8VypvTJYj5ZBq4ZzsB4zddJA -Xwk0X6HrYuC3YwtpdNiBrL63Dr5pdtXxMr7G5QeSZDqs0zdK33mBciunonpxH0qBE7Utqouaqmx6 -V3693I5O9+rifo2qFy8nyk1IKes6hbxIje0gHuRPfnn49J/em4P8yS8GR0f5L/7pV//47Jf/KZmB -D6z7d4wCz5BshbiS8Wo98niSvTuESANNS4LdkwJqGHmApFc41le7vENBWrTUV3ss9doGCxGF2z55 -qmFxvd6+oTM7X70SlzuwwjD8BJtgPKhQpGX+fh17cP6/zX1blxtHkt6++PgY3ovPPtp+KBVNo0oE -imxqNKOBBc5SFDVLe0aSNeSOfVptsBqo7q4hgAJRQLMxF9s/xP/HT/43/gGOW96zCiA1Wq9mpEZV -5T0jIyMjI75QnGJkr6iRmTP05Xq7fPW3s80B9QYFRjZFvWl9/Xb18tN//Rd/gbu9ggJCWXOUYJIE -5rUtr5Hj77blnL3wMdd+K0hOtN0Lt9wczC/STshTg2rSNYpb7Ds5IKarmjJHi1VJeVuyb5DIvpRg -Vi4k5ibLTEr0pf1W0eIWeSIjhQ4X1eX+mpspZ1z6UJhyhuOx9BUxlUmymaZkOjvDcCapK0jhQEzT -RQ1SS3mQRsG2eqnHCzdm6YCNcpXalVu9SMc3Keyg4zEWnMYbAJTS7qYpp4i0Bo1gnBlS0VnM3FBb -utowHG+srjPN6lo3y/01zBc9c8glXIWBdLmqdiVM2DTFKUuDz9zQqtwuD+NlUy4EDoQLT7IVQgKM -S8ZOy93BcmYKCa+S6Qwq6Rk7K5/pCWMmdLSVcugQNyWHLG+uaFiJVDcHxh2A1o66mkvU914NpRwn -NpHtuykwG9oBqUVJRdjER/4LTKr0rUCIYRxkZ2XNVwSWPaMllM1mNCdwUFnOZrLIePxg/p2PBYJ9 -7LXbdH0l6QoehoKqnPgiskR+L0DyJU/C1GufpdOhkOzoSc4iIkaNs6LDU1UzykfeutPEWF9dqchT -2kSSaxnfbxHzRP4Ad1zDz+/X83eLKf6laL344/s1RpHxAv/Q5M9mUiR6yW4O7nNaSBhZOAdldB5E -wyKVgK4UPMuLhlzIof4sH+k+Ndv6mvD7gu4SbRZ0iGirHfVxm0lnLQ0PVCroZTIM+IfCnZuxduhk -1yTYbaURc+VXqnow+DsZgVW5fQMNOaCWxCaj/VoxHorCB7+MfH5TtnRlxu8xILmeN/vQEkxqMV82 -reOXH+kaXtcc79jAU6t6FblHnb7hlhjssRW0KmHu/b67i0KIYeLEinN1dprSsGGwBns6/1Ks9qAA -2o/UXoT0zXsRWizjPhsbhSxK0xbVMZcWKDmgU36WxGQrJ511gtvpTB7+l6Ub70oT7SVlepCk0IHA -7ZYvCdji3BlnZJQBYXG1q3INW+UWzoKzgGLNWMPWimUEH7qKipB/2FgiGr9obubuHRNC3RQvq+0K -sbx/ywQnOq53JsIl0a5IPNBf+cVUT+HqlQcTZMLRy2JP9+CxrTZZkk5REBEeT6wUiB3ZRZu6+dJz -JpgLmMJ6LIgsyqJ0DQdiLwSBsPYCUv9x19zRXyga9sr5Fdc0Sf2WDXx3ca+7eFr2HMexwyCZgtxu -iyqRfKSHIFdym14xYzy2Ln2ZJinrIa1gfBuKu5OCLJ7dbzm4PfvKY47cV1OlSXJ//PgnOjbZBmO1 -YKMt21bpP/vrwhNaJr+rF7sb5XyvRyj5Dx3zCNNo8203W2pLLzJz4eAr+saEbFw8JRf4auFT3Uwl -wzEtKK2iOTV5cnaQKSQ+qUjmgVvLJDJiMGRnn7aJjJkp78Qx6yd3vMn12wTCLaXLRGCaifxLABEK -LpGlVg3WjgLtEHb0C17YfRxy4mpDRddp89coY5FWzPDSV2NKdjBZ1vGpsDK00HZNs2yBIK4hO4Vr -lE5NUldJhMWPVPd6+POSTK6VAQynwt1ctqqheHHzBx8lU6yHaOGA2JWUOwW0idWjJQoao894rumV -jCOqq+YRtHfdV0TufKAqcIQXSufs1WibsUUuWcIW0zs7cYGWvGmc19YRo2sy9uiyPzFCKPyCo+OY -un90S3alzNy6fYaX77k5S45JZDBFoSkppJZgDE7oLMeKjpGavPKCJQmjidM/UsNjfqfYTIAqYlOu -x1A8+TOk2JgtCW7EXKNKdorvGN07GOLvJVfaER61E4e9bfMY6Q7uJdMP+Qfy3ZbLmlR1MjztYb0r -70hHcNM0b9oPLtpeT8KgDI/JZO7UrMgIszkNTzRFJbQmhPtNbTI3Pzi79Ap32xojTTBJ4Ltipr7Y -cgC+EpOkzK5EpVVLh/fzurEErrvdi28yo537Fs2AMz+KTDR6sCpMieSJfwTkk5wNLeZmILGWe1wS -RK4BlOR7Hxk5Uy2TC7/GSyf+jrc7SP02RJ7gbpAFKIowDIzj4vZeV2tY8nMcpCwCmxNYoHgYPTS4 -UcsWtU1w+2DRMQspl5gFtoquyNY8+CnfKKmwpphnkvqBWjQn4JEjFM3uZRoZDm7bwFNowauZRHUj -uFylkJSu5IFx2tBRjJDRgVVM2EXro4rP4xXhVoG9h8bQWHsSrPos7dVNxXd5cACDVEcbx2Ruz288 -nTVXGqv5/pbhWpVBIMq+GXwN4ov5Bcx3+3KJaw9vw8gAjVkjH2rgvR77/nJUxTxkOjCIFNeP/j1S -a1f1uaOuGLXZ/1xuq/JNgJBD8M2Qsxseh66sUU2jiD3ovBM7OihL7noj4hl+IfEstjnx/fe39OUf -eMuACZCr8Pvt5Pu1iGnMdzT/gnrIVCKjwMp8vxkvRUfotYwKZE/CXmDAGJ2DTQf4xreLN8kOuZaL -pNSV2AmV1LHax4T2XbnKcAWHdpjn2SwV6wt712gufxdsXdbWhHHf5IiK6N8mtVpC/D1WbeqHDuTy -zyXLhTWHUOxIFeVIB5KHm23oBHvkDhLMoEyfkZsgUaGIYmSF4KDLGdEjIP0jePbH6jU2Sd5RLdwc -GLi361d/gzKqFYvxbfPy9z+n66TBqkG7UFnZ6BdsR54lnbA4oSIs+0IF2laOZQMJXk1QpsMiIYUX -FwkHiHrFgQgwrK2KNULhJEjEqa9vqu0AIz+sEN+EwYNJaQ7chn1m2VO43C5rEwNDwmTZ11btoWXb -IjzfWPdcpFmDA7V6oywZEKNLfeRxUUkY/j7+jRC80Y0XfsTSFWRJsN8B85EcX+zr5WLetLunFJTh -GX4fJU9hEVw/Y6uDL59/8eqXfJegluhvbtdit/wtIfmpygr4gG++KPWuzA7e3EI7GMAOsWmaqysY -MCseRLZp2rbG8BFsQ59bMy0UKcbJdSUWQow4t2y31S2HfZlG+wTC1B1ehkK+6dnjz3KVDR36dEbT -bSf5o0ePYJsv78QubvrTR8UjB8xxXb2bzbI5Opj7PvDkDxlBbsR7CSLawsqed4RD4ELngdk6W3TQ -xPpeD1iv+oa/Y765vNr4vtk0Tk8myTiX5WJ+UyLOveM5aJewZWOg4cOhbyHKRftg9n3YlNJsp8V2 -GKOjl9+IuKjLD4HX1d24xHu9v83xCtxubS/4tZd5lEgBMShUisVr2otLs8YAubdipoPYxtEok3In -htEQkBkJ+xolfPZLmv02QXEWDaMF15vx0b1wGnoYoopJx3QU28aKYdOmnnFQ0YqH4+3QdPYiecCi -rpv9tMrccaLPf75RsgdmSQ3HHBwUG8q/CMIwDxW6NbeYQrRW5ODFEQEJ1gHdECG9yb1U+g93ALRn -s18qoj1XC6Sk3HYvuKrvCOFexQGsNFYVArhQkB2y731H4WtQl+qdC0n7KWLo0mEpxMp9Az8SenE0 -yVCW9nnlrGJZhdpxryRIAy7SzE8aCyUd5FEze9KUJkNE1oDJsqeRkcoNr7KQ4lGNt1oEK4E4S2c4 -bR32dgjZdfoCTWnhU4gc71C09I1D4ppeq9ex7ga2/QIV7obB+AFR4Y9DUmAYKjtMxleoKHh+Byu4 -VUEruiJS6Ur87OUS42YfoKl2MSfEoqLQIV54Np3uKHERDPsHkNbIIMJi7AZa29gBQsXF0AD/dGlu -o/DxbYrbWJF23DFQIydhDfyMFi+KG5M5jDCC4k+lkCw1Q0QzsleLxrqRDd4MD67w7s7jOcwBe+GG -2HJboSskUBWqj+UuO9LzjxaDCOYaX2rf2XSLSoDtq3SUH7MRp3yi7pAQ4tWCzln4ZZTnPZFIxBcb -kRqRnsudtB+tu5NqtdmxhZHZKj5cNDCra6jqikd/OGLZGCUfR6vTKXL1tYXikHxYi4ydY7RtNv3A -Ad1CLT8pRChSjXGD53PqlQP4RO9wn/ZCgxLRleuDsnTDZFoPFY+fttRBnLjx4hrBr6qNIwaQH2ye -fJ78JEahhim/+Pofnv5KRcTDs7XiZaRqSe15M6WC0P2T7jnsjzHaP/8YI1YFXpsOh3lHYeKrw2F8 -kZMr3Qsb/SfkcHCJWhd1ubEwWvvVGxNQr3+SJWLxv4/PNk+qTCSqaAc2jAjsN2RfHkQfpqCRFQeB -ZnAJZZYZn3VOM2M7cG4mYRFQ6ehcgd1BrLDNgQxNoc8NR4iwZo97YseENQMbS2WJ8EOqAO+3x6vh -yGqOHzbO2xqswtTCCuNvmERmGWLoLpkeVCVVO2Dn7fV0CO9rBokLeurzeQqSSr3FAFRUiMKoIJ2+ -NoSFgguP0yudmqqbglGPiIfm3Rt9fF2pMlJ1gAOyIAPm1X5Heu8g9J4rRTMDxM6MV8T+zH+AEWbQ -+n43rpDx5R4z5BY6F0+xyeRk9kRaQc9xsUYmLOUviw7a9PkpqUl1pLFTJi7pmDluyg+ZN+GHP2TW -8LZGz9p4DGLnvHJnr3/mcG7/bNPHpv3hKuxM7MwvXTyZoPZnsh75w2kLktMCT2SBZWux0YxCjZpn -imnEIe1R+MHZH5ywbG155x95qrcrZ3mqlRmZvfedJz0J1R1KjmqRNZ7Azacf0vUSaiYdWRLDVfWJ -lDIqn1UcJTrEoYIAw9s16He6ZMVE3Y6MtOlZOFtWqDroXnTrkvp8y1Vpf7dKJovQfQSLriO9FD/K -L96PPjyVlzAS0vGh3gs/n0AMMg+seUL0BN5paaMdhRsgN9aa6DXCup8knXBS1rKXrKgai0xCw84H -9CSD9ansHUsX3YyXY14kL66SQ7NnJ2eMqRsKLYhjQDF8XEc0HYVRAa4Sn1voCDKubNwZFvj/A+eW -zaVHGkKxypW4WA565JoWka+y7I/ovdVioMRq21Y0XKG75J0CVg81Ju6NcERSjoaz1kIGRkena3U4 -VZDcS5dlH6XOweAOpXfOEjXHUM3D+YZ/O/U6ODp4rrdb8/LLF99ld3Sgt+blN/w2JvPfWaxChG3V -OFh3y11j59MBmac6tXcCongulkf1Dg7JPItuBF+QaPFTb2Q9zlrIelQVosUhj0ug5plyHEW5c4vJ -ax0H11QWsuhlXVbG1XYAcbpppcVEymqs8o6cd16yLlV1J69ztJad+5k1e1doqr7sGGWRMULuSKSg -jnuk878KT32Wcow98OPRvbZVa2u41U3NUDINfashEyLwfDI+u6CAGdsaYz6UNe2ScFilwEpu/XQ/ -EmjYuqvG9EPXGpxDarU29EHw/fxOOegbLsQO/2eTi4tAt6c1mo5bvTiTYzZL2CBdciQaodGeIbQV -jsZ+vqMgJHKpPob+3NYY4MN2bXSYPXBItu51RR3HaA0zz9rqLftrQvJiJtHTZ+qzleNyrUpT1Byx -/CT7V7HJVsXHY8oBd7xcdwZg1sbV3exYxGdDFEHbu4cWWrWvEJCl4daMqd1mE5XravxMF8z2yGJd -GIMU7ZVPCQneRZNLsmUf3w6jscF/zCDejJuO9qHrHfuLlDsr/GhvkO3OqRhxFR3u5lb16fPHCMvz -80n6Y9fE9yhk6IXGiz96z4YCUULOCaWxr+gPkP5nqPfVmmzsQBhEle6PV5sbUEksXkkSYo/h96n5 -6bNnz3/TX7OfhTT9kbTHWHmE33n4BeQM1yp3OA/extb2tmFEPWOvRab/gkYoNj0Z5+hE6sVMbFVP -eLHFcJLINcRZ8SkygcUe40nCB+RObbcWyu6fuiTPTOnMmfPuMfHjaHp2fSrZn+3u6TRNgn1jYcyS -8JDQbIkHZx3WInJpNTrxBsOcDJixn3yN4lyedTfrwxpjNUfvcrLJycKBY6XYRlHs10hAEPKVMhub -OOWgcZWyDmOYDtQumas5eEdmie4pFk8hKhf7oXUeNC2DKyiLTvneXnlFRrsiLH319a/R2Ba9p+tl -p5AiQ94tosjNiZ2NLlHOQsPckVqvduIgBoKRVRzBh8ElcHExPCGsXPKdjiCYy2nPum3Eg80p1/hy -zHPv8TX+ijlNkzBNHCyzVrovhDk6mivfVFkXa8GC8TtODQeMC18nAwRAIjilYwIMZHr6Zsloy+Za -KzhvZ2SLK/SLz8CzUOkp8e6nvkOCE3VW+QAqBCetICPViNFvFwNdE/IZCbayJfd7be2Vid6GVHKN -BPrGSKy4nrgEPBNJfooQ3pX3758//RKysG8XdgNzcUxmrcOJtJmMYdG1Ap1M5jeMaKGQvtkCtvv+ -Ok/uIb/F2DUtmYZuZQwqMsWwJkWNxDRxRgWngGg6xeaTwbb+7uSuCIzTGo+OnPB1YBOcVfNUJRV7 -Oy4zWKP4BXYPtL9Ke5mxTkZ31xMxrtYVasrKbTc9nBjJp6aJOjG+pS5YVd6tlmTOMk06L86BqJPx -GBLi3bm5Pj+R22fShZHdrlHiXp6bk9YR5DQ+hVSVH4knk37YK7RVIDIuQJkCOvIRnLBYG5iqF6pM -4Zk5aGXqZT+KEzdMyTDmQK1WjUZcy2MqBMg6GNx7dPb4k598+tOfffbzE3799GcDdPt4/PjTn4r/ -zuaNKvjsp58yzvBPkrOfTT79VOPDFZvDgKNitZtGBdX65R5GfETBL8+KT4pH6OgImy9aZuNRq1zW -12sK8kkKyFauphfVRx99RE04++TscfK75ma9PlgDcvbTxz9Lfl0ekkefIhbyJ48Jr3q2qObNtoRd -vaW2uGDYDhQ2x88aPvrFMFHYXPhiVS8QerMmMxfYx2q+UkKu+u6mQlsXSqYheutWSmNIb6TRinVx -5Fm+lEjfSwSSQXcBF9jSzNXwvyUfZ7/49nMg/CcESfoAnxgd6wmGL4cXj37BaRAHlxLlv0hcjfiQ -vqPJwZPv3z1IHny/+MPjPyUPzr9fTC5UmchFnxQf5/9umHcC/NWOyHRPR94qET0A3b/YC54WHi/3 -VmGDF0Vh2nRvRnN1BnNF//xuv1KfHiX/cb+EyU3OPp08/gwmH3j+zUMDkYmijxJv9OhFETMppPqU -czC2JaGRBrddrL3F1OcsmYRXNJQIg1eR+PJwOIlpGUuDqsvpUUEXJhSQZtLb9zBrJy01y4wefYPl -Qqo98vQTeExq3rfDwTGQY+qrB24cwyymdAFWsY3CzEnwYXghkp4qn1/SaebRoAfAGB9mqOmZrWqK -Dz87VOVWCvFBjP+RAYwH92Yf8A8wmHu0zlG4gJMBnes+sCgDodwxTiGccrkul4ffVxzxGEeHGBkt -yhKxla8FkBWZVyqrFDbzgdyd0YmacW4JvBgNvwmtDb9hlVZA5aHUziRXri7r62YvRkdKDlPuQwIf -zNXMUHwTKOJrmkMFoLKj+yr5BkWLlkIwnOFIdhNCGFMWWQOjZHj/cqhVe4vycDz9AtI/5vQksE4T -JwlwOuo3eXXvtxOQFvY7FhUdLSawi3SSklYESjlihWlomsr224glYNP+qxuBCetneWVP91ICIsR6 -UFP6xMkwMunjlSDeM9TzyeTTi6BVOFPYAiMyzbQ4lGGiEc/KCId65NQ3Sh6N6H/OqVPnf8KFu+NE -1Y5BxB38oLo6MKtVecbpU6FcdqLaMvrlpNPDqBODtODbq2z46uVX4898HyWG09MFuIC4/HGYdxah -Db2lFIokHQO4bzYHXPgzp7VuZSrNmIMjdtZp1+uUG8HBctKYjae3etyP0Lzk7ebVXxtoTUL4fPvy -f33BCJ/KN4P5F2vMRoTpREY8CkNSwQXJzQDwPHHaC4A+yT+y6UH0ZLfgXzOCgwcP2g1F6ECBCJCC -B0j4jH64KfOBjdNFJZn7fw/U7hT8OkZhOOvx/7iq1xbi1HxZlev9hvhe4ADyYr2o7iJ3K66FABSY -WcsLIZfM1sTPHMjMnkljZGx8KyNrcY+GVqIEkakFGoi5ORazct2sD6uGQgx9Q4TyS0JXTef7dgdz -LcSTjgR/deoqj7gQhkR3D37iB6GrN+4z8OClogZj/FP8630TVC83Cov+FIFSZ7EP1pG816NkqkcI -T5F4bKd1R5Y1qYOjqAYL8nevWL8zqcK0o1ws1o32oMOfwazgS40mQylMdg2Aa27KCWF0vq03jD2K -WLOwtqPRnNFwBs32xDgBIx+ToSe3MqHJL8wuNsGvk2S/rt/uK9ZcipG8ZGBkVJPeasgETnWEWarf -EEUzHq4KEWByUpMnug6SMbl4pGh0+GhQtlhU5ETXUYqt1cL0DB5crx1CDSaUO6H0stTlrltkSqo/ -Kmxie+UEE9KxdGotl6jG1iMokFpbrWGdb0shEtXuPNJw02wevyOGSXZxRb1GtV9WPzgbcU8C7Qf3 -1qZcZ/V8DE9k5IYWYL6NGyRmbx1GLxy3u8NSkY1rWOBxIgtn16vAWsvE9LkVnn0dlaYxEwlg1vz+ -9YGnijmspwnXbMzhag9EM24aeBEnsV7iUoh8YfBTIBSEg6BU9lpRRReOXYR2SNzsNDD2AdE0dZcL -mxh135ki885ycNAVUl7mNDmaR0ACTSYerUwVF3eLMxkFWB5mLjtHc807yxSG3Hb9uZ611c6hPbav -DPg3h1tpZOGJXagEQXLlBNfKhEPC0B2FKUAfcRUwmS87CoSH1GaVFPSfTFDthQSbl8PBDcal7NwK -JjrCxGVpaXlOeLHnDKevlSyqchee6P0ZH3OdiFY2RWkVL0PQUw7bjX9R/qB3eXx/Ple4JlkErDPv -2Lj1DkfGW0oasui387gR3/WEz8YEHdE7OWuJ7ZisJTe1SwwYigKRvPCjcglv4R/93FLCqhzjmAJs -TIpDXozOvDWqufGln0Vq8pmtattMX6oqKm5vQHTfw8Rs5brMkjPfq0v/WK0kW91YI00uW9iL9HDi -xw8yKUKEY5RlazVLxYySIhOMm3fAh/NHrBAdc1g5fHN2UdStmL702bbY7qiUfI5uflSlIkg4qVfb -22qRhsfejXWsiJBs4QrESk6Nkb1aqFqWlaXqbafxif7WjuYQUx3Y3y0R2ltXlhNDZyVF9CSkEG3p -yTWZwA0MGZbsYoriVUsFcLna1Oaamd/tnBar44bdcj6D2JNCL7zpwGvG79cCB3GeYpIJYZ1a1+aU -7yIXhOxofnRRj31Xu6+/HeHpkafwmegBMsa7sRSkpORkPSjFk2RQrLloCnifa5U2myCX4LVlFf6u -3C5arWfQmE4K8YhecqWeKjQWZ3XNKgXZG7HQ+R5tNuKnWtzBHmOilYPqZRKo8lA8lJ9eCtVuKmrV -LGKlmGaIcoAfDC8CCYHQ7splXFalYUbHqQYVAgvy6kAjAbzNQUt8VEfxDWqDcIYJyHvXy+ayXMro -u8wJB1Y1uxUkLaVYx/unfbtHO25yFYLz1OyyusIgFb4nDXuNIBGTQsjMIFsQrMoD7VQxHVNg3PMO -9ev7liOIrRvMtFlWuwrNfcqrKnlXUqC5BbxCRHSbgEQxM79JWNUt506tAyH3EEYVJECqJV7fsMFi -R1uoMMzFEOiHFnFiOJpPkvy2Sn6Hrsc4HRj+HEtCA3bnLEUhhbfIDBdJUXQ4/Yr3uO86ldsbI15X -jceGZIbuQRBX/RYNNTI0PmKgQg+QZLM7o0DGW/p8Xl/kETXFmQ1OB8951x51Rtc6m113SC3VjuRJ -UnfHScLziAyAcZ46hxPmBUfC4706ag8aaS5ytGnasUMGVWEB59hO7OiDs8lFEHyvZ/VujhycLWhN -OlTAqVAqv+jEIiZUzKHp0jAnOoTXDmbgeJyeGhdY+EI4xtGuIkYhZdBuS8k9XJGMu9o2K1kJzIS7 -NFk2Kz1HbnuhNjqMayK8QZh7xvWFI34PCGg3FKMkFXmHjA4+JivUKESp3fYuEzfHlUtyKIPGlMpP -++OC3Z1QgTMikV7f5e/tDyZHr0dwtAK2iAFIkMERhyW+9IvU0fj5NUbBW6LWbDpISLgRi0GXQbEo -PB9QXws+p0KnUcqg2I+nRTmur7TPYWig24HFwne52hsSq8UrCC8+9GKjUO9C9xS7A+eBUSbk/Iiz -RpaiYhI+34hRrSQmy1bs5EdT+xWb8xMgILrI8ZcuBgc0EmGVXWEHbYFFjQKxBzsyRNppNm5nd0Fs -utcEDac2q8LBYWpTZWV2oRGoXcp/PnnUx1EWm5izWoT2kCtieZbhzSWhklZrDLQ7Scrbpl6IAaSI -oSQJbRPkRrDfWznLqytELAXJ5RKknSWIKa0sLUY4JWAHJZEwsrFTt7qE4g5alxBb18eN17AvxK4Q -TFsFamf7c8jGVg3S6czkDnRNHLfdqdLNe0L9xCG6Z8WtV94W4j3rAfij8FyvVaKT3JsYABdHQUOM -40MIDS58Rl+Udvg0BZspcxjFnpQCSnvRuVQsiF02LU98HVpk+eRunm626qLVeeeOc/vFiQx28+Za -uIDTrM1BPmSBnKBydLLkGXu6zgQ5Yga7FE9o5lYBabRn6iAuc3X3Dk026KTlNZvnw2u1cHg/dZzx -1xoiylwGOTwk7PGVHMAIkaztyBZXDPsbndoMhOtLS9jZmrowVa3r5rVCZytxj+IF1XYmp8YJZ141 -UYffTdPuZGSDFGYJOvh1OoOGaotdnaoTdcz+T31zq7SrE8S7DmqDP5ZvsLOUFtWSHBeF0ZxL0otB -73qhXVbpQlaLb+QKJNCG3DRL1Gaw+YI6YyKWc6l4T1v0KDAiQBCOWZ2KamFjbp0QRTb93LQ5ub99 -QuGn7HJHua3oqa/Djhl4ZzesKGsYFKC8WF4wKTOwO0fj6FXbOHn9XebeJKZD0PUqbUW5QK/k27pM -zKWTqCzhCJ7bxdF6cqPEYhIvB+36bUzPieYmhgTyLoWkaDsd6mZNY0p/Jsn9zba5Ts6FSi6Scwoy -02xhUrbBE7Tpwgurallt+PYEJqHT9dKbpwDchRW/TpqplweGzzHjyTD6lXdkZ8tSCqA6jZRpB1RE -SUeCzgW3RvowMrUUkErH53IKldQrQgJmRJqAX8ILJwUiHirx2JjHMhb5O1owVuAQUgWDvIhLiu4s -CUtcLhnwZVSxh8dc8rOHcd23FfnT4qbS7i9lOhVIui3Ck9ETlG8HeWGngCVi3KP6brtfsOZsh47x -pF5EM+N5uQGeYKvQRMep7bUogSEJ730RNdGKBW2S7gZRPTfAq/Qt60guwFpbqW6l9q9YhQFaoxoB -a3HzqgBUscifLk0olYNEkID22Yd2z2RMMzOfBD1JkDcan6PTCsDrmnJhnbolSML9LXJo9+XIh5bq -aHpwqpL6XRALZXakr7zcHVrppsgjaCjHaVFMwbuIUZNciamMFrsc6Rx5zA5q151e/ZLbYdMB4Bkq -RopMrrp1aX25e7nUum7naM7R1XxBs+BtJYuGKrPuWiIToCOvtVneq0lSgpndtIugvgdBYyPnK+lu -4PzIZdjwyvqGwSNSjyHdE/ZQaZt1YhVGK4SlJNm9um331c8/8ZlPvd6x4zhFRDTsm7gwApfR2Xpd -VQtxWkTHQ/s8fYkI2ppDaRGistgRfIUhj/N0PQewo3A/5IsNlEn5CzRdXlWSqhcnKHb29KbGusAJ -cfOjuDiknN8DhVdbUtRyo9p9i6K4bhW5PF1fExIOhS6hC5JrpJ+dd8bzpGiOnQs1SHQx+JXHUpjo -ugG4AaN2GxqiHmpzlJiUivvn1TVHrYEfwt/PrfCvaCMiEWDxpw4Cm17ExahCTGCGskkgcEl1B7wz -eq+EX7HO4ZHCVjo8IubAC6WV3A693dcUHfNgR1Eeuryz8q3KzBbm4YFpqYLGzaWMWll+tCFG3fnE -VnVh3lTSpnkcp45UCdLc7LR9wtmFT8tiQonOyA9y0+CV84n1VevbQFaOsKWjrVk0nn2Jmt7jY6sl -wkCGUWPBe/vUzO3Uj2zhjLR32mK6UlPHq0GQfG2K48DhVmP5q9tWLoi/iNdO6uuxVwdOFcYK7cqB -lVGmz5PbmBmoggKg8l7hKYU1X3FYM/R6XkzUsmlNWNOR0nbqN8O0Cw/VHittq68RNeQDzlJz5Yxj -B6oFpxhFxiQ/zTD0Hn+U+w51kjf3yUDyYnIlcrhEALIK+Boj0RO2EadDa6018JfLSl2qNwgBtZG7 -LdrpBHXJVxj6MpXmcKMI1lyqa+IuWI2nKLurhpAFpOpndvODo1ezra/FLjLCb/qYh2PYoqKpRBby -jBIEIKFOGb5RJ7NbS1jMfZusMEQhoTuKjktZBRHc4/zdIoDKtbvs2WGSaDijSNK2Ztzz49UWerwz -odATmGCi0YVWZBCSOe3rl1W1ttRUcLzEvcjELcPuHSqCPUd4cRCM2FaBrL0IHwJrvKxga2SPwoEF -4ikmIwppqAZKpg65R8s7b99xFbRCkxbEyt2ILjRMkjs10jQsCsG9Fg9HOl0v+NKxXrvQelJjFNjP -urKMaJ/EG5OGme5GCe7U7j/DniK2OBl4woJCP0wcbzsm9u5m2+yvb1BJQnC5r1+z6RhP/+vXlgi5 -XCaZ0jyJL6hEVAR+Yl1JoqaceOqi6L8zdZT7Sg/hRfjsVebHclIY0KWxVI/NqXsh5J6xOucjaL7j -x+AY9for2rUEPq1v4UWvigYboYaJnJ5pdeY9gr1zc8b7zPs0Cu8KpIudMFLW4Epavwg2qm778w6H -sek6d1zrd4J1YcyyJ5E7b8fUqGPnzTsuR5Zx9LJttVQXJsJd25tldScSCJu5hz3T8bQWG75+lmIs -+58ojS7dcBW612SAHl1SnY3qKEmbsEdLO9+xQQQHot5ky3J1uSiTu0mi0TrlTlaJYBjWOacZujhu -6GGTQ8cSte9sZ5ZOYKYm/ui9bbAS6NYUJgD1sREtyWm3yv0rmJvvdAsKioKHoxpGxVuP0eIyhI6R -tpM9nHQjqp2ydi5JN3Irj6FjSdlT9asA2WdJyruHqZfdDdISliBd1hTPLYhbvOkVIukCLc8yTgfx -6R9xfIWYJTt9iG0m9EEXG4XOjVCKQxz27s4xgo8Rp7XVv36NyV6/Fk6N1q3VgkQfsWENNQCWT2K2 -qEBKnVNkGOymjrtHJrSWbyIZm2HhlsceflZRfctEx2N2bFzjdp1dO/t1TNHpjukJVgrOsNuTbgbU -nelwBppt+6benDQPzhCafvBEMvob+oiCFGSb3bNGDOrgVSezx1j1rhQ0m5EW/LKcv7mpFxWihrjB -tGN7tiWhmob4zEhIG5MfMR/zjDs6xKLT7Cmsvmdor3d/qwRUNKW0hRK+v3bUZLARK5MIdRxV8Qru -XK837X5CHnGukWgqcHnR05Aqz7M4vhhETFedXAGPhnfutQsHlqKsxzHyyXjZdJfOI1bvw6lC2QU/ -M+O00uYx27pNN/4+w6XW+gYNeqUfixfrWsEIdNq/DVmvMJQ2S862mnf4bXq0pLOcq4IubDSCP/xJ -4ngDi9pvyDMiExvekUbHV9AHHQbk8j6PQxYE5tDhSHaPoJKooqWFGzUWhCKVpIiVFTOHHwzebl/9 -pbp1XDdt9bZ9+W//OUNWbPdr0pkk7b6GvwnqrncVu4xjUh+ZYkfXSAahQoKYW4AVDvYDFE9/EZQH -UZS3u2w2WyHIInK52WxEUWNHxPNkcH4Dq/0l31FrHm/MS1j9hy1Tir9RMlRZHF0gjJZ6727PUBWi -bBNqKEFyWC8QstMSalQB3rwps+p2f9nu6t1+V0lsdS6U45u5rNuXP7HSx3RRAZXPZnT1PZuJ+Dtx -WR/yJaeRLG/nPGzFu5tqnQeF6y5yHY/VM7DKvxNSWJXbNwVsCQi0E5s0Uk9zgN/JIISD5ZnLpLCv -xOMld4UhL33B8PRanfjLal0R9J03wtcV3khZOfruRiExEMEMiQJ+UquHMdN+pDiWFGBZQ8qiufwd -egpwhhjns1qP6b3Gv5BvHXwxrE+w+furxYROX/xNXHrvFk9D5RTskywpWeQ+fAl7yTLh0mtS7Wwl -jKGoLyVFjGq9OqN9ilHTrgL227xbHyMon54mJ3dc1eD3/VirTb6BjfaryMuh224yu0f/idTl5Oio -ThnS2Y1z8jlDitwUDgnLJczUTNiqPOqV5A6t/tq78tzG6zyxyXWTUgJrLxVAecPBdQKPQ3NCexBf -CDmi18qaMJIul9WqZWB6xWYr7avNQid0hEEuy7VVVFtjUJXlIamv181WwocbdFWuOwyhQovt7e7V -v1JbJjvJlcu3+5f/5xlvm+pVwqOPZYtZ+tUelllwCzF4qcEor5sGvYAIiNN2Miw5wOVtua0RF8gU -zAZ/hBzcuQ3jvis/G3cHNnd7jsOvRrHwDPfSsGcUudd6YJCb9JoIaCkXHewub9WG8TAwKsZYwHCH -dIdFkAFzkN53aXDdhBYq01SSpwaJ4RGjNEzTeo3wOTDdnKbeHYpUbq0i1b/l6t/ua4pUc1LllDhW -9aKSYEsnVb0dWuWriltYSdwtrIkHdH4Dw5+6oBMjJMwSyADayp9NWdwYwjvkW3sR3eDgTji/KNG0 -lnb+8pBQGUl2lZewGhYwlV7P0+x5TjjlsJW3OcobG0wGB6EryZH9lxytdatF0T3XSx5sbBlJzW3n -CMARdW+GwWSwRoFO2vFec1rCvVUnXejcom7RzI/6q6ybeho75sE/dZK654csCIKGWqd8glBKxtu8 -pzG7y6FVJMH1hOQZb9/uUpLrBg4R+2kYZCd8EyCnpp5jav7RTs8lOfD2GxqQBCRr/C+qgOip3NW3 -1fAiKJB7qucAL3jWOw75nWGZD6nAh1jOQy7k4brpnRPkm1TesL/zLv3oXD3kY7d40aDGar7nkG8O -DZm7B7TJ3jMFORh6cRQ71zZSgZCPp94HYi0Cu4zu2ejvDHIAVkaQ7zSCjGUP+1ZNKNPXJW7gCqu5 -xOAOyo8Kdr+vvmQpjfVmbJ1kYD+MxdLAt0Fs0QQRt3I6/ujbcEo8YjxfIIM+Bfe6eneF23vTFguE -RGPLKM7oB0O7x7SRopUV5YOpor++EigGEBo45EWwiFXHoTFXC4poT8WP5EtBYeMTz0nXxxRUt6xy -/pKs82WDflWi9WN2QBYiL2WX/E5eCUmoOl2kROeWXt2PZqo0kgi5NPVqqOUjl4YW1eX+mlSEzmsi -Y341cYJbrQ5kioaRmcmqyjXMIBmIYmSniSBh4A0MnDYlqa+948axeRvf26fnVt0XBKDhhLmWdlrW -6SBEKmvZbSaGbBOxVh/pBudan8e1omWts+j069bEFYAndQXpjg8nHhi3e77XtHWsGJ4FVl9BtDrL -0i+ff/vd82dPXz7/ciIsnEz7q5Li6akdRJkfePyaHDjdZegYA0YbofR4MEDEBhjrxrn5GqVxg19K -P1W/1LVZ7DyrE8OgyVafdgDhmAF+gInTOHqAXeAdCwynFXiXWvPIMkp82uibmjvr1cRRl+Irvq2K -JND3MuVW2Qqb1sQuqOyWYq6BE5lAfXVPufR6RsbHcOTZt7K+zWmMnwsWpUyty2rHLCUteEhoXCWx -zFEsdRum5vGPJf4qdWN4UHLUG6GfdIpnOW/WTNarNOx9ASwOjWJGkm7kvS8Iw8kArfrcshOTSHFR -WkExiBnjqsFOA85HLZurK43o1uxhJYNUeQPL2pgZmlKeTH1seEzNNuKnpkZhiE+DpIrz8/hZZuv9 -Ss7d1YKQKn1o53IXQ9aZ77eRsK2u7pgMpqK3N/SlS3Iwbdu9Ew1/U6gZ/S3vLxQUOWgRajH0aDlX -1RLcy177MV7vuR6VLWos6diqGmRemos5eGU8MbB4W8NxQ7jhfxgKsxpOkuEdCcC80vC5Hf6JtMyY -dkT5A48V4TZ+N0wbeI/kzguqt+NRgSumdVVbMlIfTSPjFwPP9saXf7iSl0uqTCPFJeLWs3FI4O9g -zzXZiHrbyD1lnPKBRbJlvDTrAcod+aAj0dYJsMrjKe6pcLq6qu+UryM9jPg8THCEEZ9PjQzuDBoi -T1DuyckDEB16LqSvx5zCWY3U3klfJkphded4Q8aP/bDoM4m461tWRwfkBw4DrXB3xgxb35H29OOP -ebEGNv2602FSnwgsg1H8GS2UTBgYpskNu0K1eUMTIUF675VuITtUduc+sBGmy+n3WxScO6vjbreV -sjGgsN27erdUkKVdo3q8n1yoKi5au8g3hDUHDJ9URSrY590cvaRzVxRTFrHYb5XCtt2KUJl9onjx -9cvn33399FfPv/vum++eJGpkAh58FjSRz1kzY4DqeEPHiP/4Kco6I337q1e/fPG1Zd864fjjgpE/ -8jgl+W+RuTqc0VE3UuKBH0+ZygeM9OkP1W0ABb/wijBYbHuMDr3br0vCcBN/pjbBeBviWOb4dPnZ -r8vt5dJNzxY8eLbxpqB3euh05w/8ooJkJLHIiBM6QxiVAkQXPP+JpiUbmnywA59f5Mq2nbMH1aiL -pGVzTSYaGsl9UdVw2keJx7swuidcUPkOVBoAic55qDAkLD7HtvlecoZ4dFUrwX/lyoytQvC+3IHL -09yPm6EIfTJJncgoiuR8kXAShL810TGlPzTuSmr4WPeya5rcPZKZSpp6xpmeMNu5JF0BRjUCi+uZ -HLmQUtEhrVOQnK7I9M/BfrQia8uitL2Kus5W061tHQjjYs4j75otxzdsjxAh5ULa08aBm8AFQg5D -CkgKy/YvV/GaqrwkRxE83nlru89QQ82GORR8DmJ/ry/wFjn2EAlOVHi909k7pVBUoaaVu+kB/URx -r7wNzM8YZnIvI3EAYcvZb5axq3P+qiV9fDyhSXysDU7XDh9XRf5hCDRarYcTvOn7U1yxQQPjnac7 -CtviseFIUcFBvqOsQ4XXnLHiunlDyyQh85gDk8CnOKPooaReqolxFZ4oIzIE4IMemfSl76GzTikm -4l7f35+Y7HV+v0W95X2aJcxYXMMe+66EU+4iP0b4x0bArywizZwsz+tLCFIORJwl7fjE+lTcxdkR -IYQLAkGkKIoEzk2XzXLho3q7tfZz9m5FVDf7TUmeTH0W7EQg7lWI9RStFJy9hZOs4dmESl0S3Mvl -XXfG+gfNWC761DcPpoTfKuKMz/A75uheH6+mwRV2PXw+jAgBsldKIzLn1OJ8UWHHQaYPjNhpUih0 -Y7XOrNEl2zv6yAKbdaLgse7IopQrXibSSUH9USEoNePYFzbYS43knLonsAd8BAvnh5C9mQAc9Sj3 -PlIN6q2Th8n9hSRBtsG/nMmNkaiXX5EmFCA/TyWP+Ji5VQRo5UwYslkT77bWes/Nmif8M+fq5kgz -8k08MDabx51ixBnnNS0CnSOGjxHx5VWgNbCTqqidrMOkSIISuDHGII3at9eK1hoBPCqnU8JbIJsL -rlqOBWl0RG8xZhKpfdPCv2BDJavypMZE55NP7KUhZ87Nstwh8D0Gox6Pk28PuxuoUyJjYxEqwUhV -lsdQZyiM3HBz2Bxmdp3+HnmsvUEBXqNVw5EIzzHx+H4L/7+g1krhXSV9cuF2nggZuqwd3qkQZRZr -e6H3SNJPkkcJXakFh319k/r9wDG9tHxKnAw4fCUc+y9rDTgwifYb20xhAcRsubqr5vsdWqfkg95D -tb2MTzgMMfHa+AqFrzSnkjrAQZWS5mqJMRXWtLTb/Acuf2BMdXvTuWbjahdlZbj2EV2UYzwc1DXL -VqyFii68jdU5Z5vNRxT9ec8xhtb2R7i2TW8SzIdKlgh2L7vImQBnJ9Umh99i16jr/kw1IO9yVD2L -uQs8+iEc7Z5EPMO5E+8UL+qZTE+A4hBT1hErJGH1vlaHUVwzKT53kV5iUxnTF91Dqxg2G0HTDpbR -SvYyqwlsEDVq2j61tTKiaQ2a08xLjiWwrBAJAEtR1tpQ0psycXDeLqt5iZowNrER1sKRDq4bQVbC -CH7beiFmnugef4BTwhHiVtZAn0dO9L3Jx2cxNN392r/8czyAMK4CQRHAcMXpT8IxkZWxo7AaTiZD -tJGxdVZh3doLnh8L4534CIWps2izOAHloAVDsOiZlCA4rscOa0xt91ugMRKaMqvQ/JST5KnjQ2Pi -jlB/QhlF7cI6mWT5ZIKsBFWAJ/SKC8h7pZAdmtg5Trr1esHePV7gxZ4+IkpYtZiZVaO6iT7O85sS -I6Oen00u0HAaLUgYHR+b58cGYW2o7+OqyJobOw3rO5/Q2QC/5xfx8Xfh+53+e1FfteFHs8T+hpWZ -uiaRyrhIORNCroj2gqyvljrOY5rlEcuWe2LlFMaMcGYpM60Zn+XJxyAiJOngKLkLQ6VSkNqXeae4 -7Oy91V0tmtJR4rhbWevMeS9iSvddWJp6l5eqAgpg9whYxyh5HJMfxDZ5xuek2DWiSqH22+hVYygH -qU10Jvkz34jOj/vptBom9HFM3BDp6k11uGzK7YJuvbb7TQCCTd4zmCFIOVtVq2YQ7aF1T5LHU5Ag -kYWTrGqZmQbpKziS+v2TUbxZQInKhYzNLbYZBioiXK74YcyO3RzqmzwgpY5ah/l7jrQRGToTeW2R -e0atHz0yK3y6kkzIu7fzbdneFCtYS0EoZOv4hyKiI6zDGKT/Sep6oepKCcuivT5lx9f2yyFrUc07 -Ki/Gd7ywd9GCzFi7OmW5t2cXI6Vy0jcFkGQNR7lFsypr98LNzZIwdBLBC9CVlwoHdYMI3kn6MB2v -Ma4bAukyVnvEIgYFP/5pttnvv8ct9mGaU6APp07PtEnpUoGdJp+P1dk53rM8rD1+QQc0H7NzwYBN -NDT9CDaSBo6LZ91anIk6RXLqEFefxr5HDTTR51CZphO0aOeKCC7SAB2DykWTHRcmQ9g2Hj0sysFr -tCirYA29qsZnDXEC4/tCfaMROxNxwp4eKuB2W3mT8i39LzzcD1wsnV3pBJqSSxi83HXX3XvhUfQF -5vCryM8nn3ri+olxOaxohLRqxcOAd6FaYhQ5goWVzjHMbuN4VhFImbvIaZml+POLvPe68w63ls3i -Ek+462Ef9tFdH6ZLIGd0mUS5ViHsYkN2susmwDCiMbAQNNSoaD1ArGOSqGteupQwRgP51dMXv3r1 -3fPfpPmgQy3RWUV/LxVeU4cNt3MH6SyVbbXpOewESqTwnrQrvBJv1bpGn9ucUi+N28zfukOJpNnj -9Gl5kooOqEfk2B+VdviG5cchHTKr6iEcs0LPpR2R09OHTEr05hlN0Lt0ZVqW6KEL7k5iX/6wC0r3 -TTxZvZP/A5popJPjpZc78ZRvrt6jeGX7cWIN2gysp5KTafoEeva/xva6o9pK3ibmShlDlpvCfTog -XFyV6zgdqfwBtUtx52NUSUw53G48whQfsVX6yfjsoseOVZJFVjafvYJrK9oEZwsFiRi7YUrGHfdR -xngfDiTkqcTbgrYSkltTczBMRR/njjHk9nZPLC8Lt01MKB4umCIcL3yrdkv4bcHyl8yMvF1bag4L -48owsqVnxqj4GiGwluukWm12B0w7Yjf8ynXCjwBRGfmAcrnozi4YVVcEP2iA6mV6fyGKadTBQJ58 -hK3Jc99OF7kjX3pRfh+KH3hM40T5kBV8n1Qg94vHuAUAdS24Lrpl9amn964qsPqK8W+o9AMvb/2c -Af17Vpzu7mbbarqkODmGafkm7o0DcxCYdiFR9Yh36fgNH9/eeHeO8Rrw+ry622wDwO7eKlYMd5qs -8uObKZAWB8Iwo4Mu6IyXGlbiWEac20N6AUSpnTCXiOxjz5RYb2wRj+KmUYDd2a3tIYXrIXTuucWR -8W6awwhe6jhwvy3o/3xti4Nwawfxyl4eNnyiGFm+umFwLDyn3Eqz9f2iJNMngWUXShBI+xlqpJWB -YATq7GDfXN5FxIdDXS0XyaFndXCKu8Hg7e2rv/EC8rx99/L//huG+dhU2zGfGMkf6iF7cloBNVYV -OmTX7aodJa9fw3sY7devSWVBj1cLeNLBwDXouYTv6sbz+IEgHkexOcbSVwsYw0ALWFgHNlgKAxQw -ako66sYUuFqQQ9WhFTwBCz2AnfD1qNrjSDgwCDhEAXkX2iE//yOU9Md104Md0Pq9mFEMqHTEsaCm -KHwrwADpd+q3ifAKEBcACUwPz5TrDRCzCPvbwQZwgjfFojZNjO//uxIkFrzhrLZk3GGGAbXX5eLA -QRCpkIcaIhMjwKj9BzkydFsDGELy1G50GqIOakgeHA3jOtoBCmAoZIoTqUPsdBR5tVBF2gvNfIcy -OIEONvOMS1chzzgpz3CHvzxnxTt6PxLNQDDlYYXC6uSjBf+2uE5MTv19vcnOU0iKzBySpxdeRlcW -DQ5wWshV20eWSr8WyDGYh/oV59ot9utGUht/WBIdy+XS34I1BgLvBRhq51iSatebQkLjdAU4xBFJ -dQxDZ7I6vXdlxfJcRt13Z/ztsQqKFIkL5xQCCZxn6/yCoBk7EC7R9dTvxJUlpkuS4mWFnBUEna8w -g9ncEStCu7XCAqDy8GZlPW9wvU3TVy+/Gn9mnZivFCyEP2hYlNdCvEldX9dNxzArZ9q73YtvMi8g -lwpaJDE/eYE4ZKnGiBdgF1g3VPCb3UKm8Ksvs3XzrgMiBWlfogA6o5tTVKXoF6cIz2TXah6u/5Pa -d3rr9MgGrTNfTmxdRDcj6NrPgnBWEc1yN5h+sL2JaCnMzo2/xmhknh+8H34tgASZ2xwkYLzRtD19 -iaqf/dI8eOpUQgfqnZXhj2P3I71ov/EdJUrq2hUn3L3wRFoV10XyO5KV48Ni9iNrtn0OQyx0pucw -UA1Yti2lUZ15HC4eqBg+FsyhHftsK4DaDPNZ1lF5ZFfV+jchHTLmUJRDD5FbLXvUGG9xyDuer4Zj -XEX6hAYMQxTmhjkLMOV8vl/t0VpKtkuEI0L3OswUhTR3o8MFC8D9bHM8fxuJ3DrrKfK7EK7Meblm -LSpWN9LillmoBN1KqzSgTdF/6bQj15JdpiWKr+BNmbulWUShU3hEhMvMlpDMbmlaPk288gjqdhNv -UKyWc3684Oh5jprfmzaflLXQ0scmheb3ZJLtSyHuzCMVRcWHRYVi/m2JrhFit5DlH0Qa3oyEMVw/ -YEbcQe++/tWLisZOxLAu+5J4y/DIDIPUU3Zmrd/zRxfJg0T/HnWCajur/vzMynV2EYVy56+xGejl -LQq821TWJX8qOgnnXXy7D/jC45AemzNpENxM8sebdEK0MDMjM1NW16rUSXi2w5eeulXvJ5KEg8Co -5hcqY3xXwW4L+ns2ZFUEMm3SQsTxkP0GaYM43YA8lku+FTP20bCWc2yBettmdBhsw/uOMY6E+YsU -1bnw2GwxzDHp6WB00iJ2iP6wkINO/fvq+KLWpQaGVzGIXx4dDz/dx/3tFREMpK+SE8wbX1gIZMGe -jdwT8LqQ/GRx34Mf8/22rW8rVf+IdOxb2Bgpbnec69nW4QwHjGtmhXcKFKT7XUVh3azCg2Ii5hvI -448ZQ0ZDzmpeKyZ8bqhZw0qiigmf4cE3iYIeVTV1eNszQntUTAxni2VKSvYBVc2VVuI9a3JShdzh -Q5uj0cQ/tPMfZl35PjJGnCzev6tW7IQoBwgiKJwoL51G+r1U7k6xsVGI+uEfWQhB7oStEo5eqQeH -lEH8DBOXNGjhWaMuAzWb8Q6aYTBWEwEiTdNqXRKWihG9G8HoIeRYg5720GA/0oGVdBMDswdiDxmL -pbwt6yWWmtzWpb5HKPBwIiOWv36tuDrOVDsw2C7SD8yXqQjYmJyuTwrVahvET+mCuKeZr4PJO8fj -avHew0HQciqaITodnNFYPP4nMiKeqGjpEaIRmCgM+tUiUQICuh20goIrGsLTxvirL3NPuSopu6ER -ORnlCVARtTrVTmV0abaqyfjVdpRiH+dMNi3THLUCVzdLMW6oqlDKj6h4pNRUlnZAz3pcjeqVbWjE -lMEq21i3bXHt7d2rv4KpmuFxr0TW//bw6mf6km6wOcRAFilDGF7v7e9f/Ut1s7hZXL79w8v//Zd8 -q0jbDHFmXBqXew5IT6qHb7/8YkSaFHH1/ZI+V9tjYXj8uDt//gtD6MJ7IXPv2wqynAjLTSTHGiRr -bGQMFjIGqGRi8zsfmZuiXG3QMFzDA3P4J4xZwTHRhhNJDCOc5YV+/6dTIL6N1lUHSVP9s082vXDP -3y4uX6xvmzekTh9C1pqehpoP6OZp1pp821b7RYNEwQhW0NJqS2wVhwoIfZlAQSYGHHl/WVCbWrgP -0Pl0//0lwfSJLaNqdTpDqDCj22azoevK9SF58Y1h/W40uqstexsi4yYzQXyGsb+aIeS6s0nrqLH4 -23hsS+sHA0/v0X3M0/eLljLUmxFoCb/IwptET6ygnYO5KqTpFvHfR/cR0a3GVDWjUBXjSEt0mOnE -Y/XTxTAclRHeE1S1uHOdvfhmbPZzXEq4M19d5dYQyf0a0h+Qtr2kMppotUKB0LXkzSOhCb2QqSet -cexkQ90LTiHhW218NlX1nXBlHzthHzlbO6dH0w17mRmi9faoPjk7OF7Gy+Yl3H9C6BpjzROE22h2 -NPkzHEjMCaTb3/C004YV4kkHMPNRGvoCtkk3UFYrnqs4Qfl7FkGEfQmE/cXi8j/v611ce4kHB9sP -jBCFU2X+KuZt7dGsfKHS4lHVwBSWl80tBjqS4VFq8moRUWmweeI73A/Gewlc7wNuD1sdsYgB9iPO -74RV2Vboao/6ldumxjBF0Cp1E45xdzGoCSyDZo8yv1UIhwtEn/wVyNgMUrmEVYmbVEtxZB2mdRJ3 -9h060xzNeAfdXM3haDq2R5p3GTTbpsxO3gqFEOwxLKDUNgR9miyaOVHYq3V1tyHjPU1maleCwbza -L9mGp8Hlgq1fFVYxr2Sa9jCl2+UBazJBrSTOQJd9XBgIcKR4cU/bPBLeXarQg05J+MRwLI/7YmuH -uWfyw85n9T3bXbqToN15/Nh2waHcLkTxtsWlse+ABxZ1FFfLZEfK3VAYGHKbHLAV8BWI2f4JBT8D -M+NdBMsoorkCPKbukLhY1qq8Qwdp4wGejP1gJEZBXTOeI6Y7ry9AGgBpiWMe0ZVTGkT4BXL10buc -BoynZ3F3Ne4rDyi0kvZwfnJOZ5tCS+RwDmC8YRiCt3989dc6PNpqA6eet396uf8XfL5p9xs6lNAK -2Da3NXGPnTLASSA1bWk1qwg4ALTYSCrTyPCkQ06kqs5VswYOu8E40okktV6ZuAPUtL+HMV0ejTrw -XqEGWE6augFOaIZ4NGyEVAYwxO4raBwyUBnhOEzPTggRHY7cgUFX8MjorkxgKtdwdFpLHRgNDN9B -++olBaSDcxaM8AuKc7Rv9yh2OCVcMtYpTIvaS4bco2FoxsrqFjdWD003Yqwoy//9un67r8bK5nOM -hwv24zS9cdWEa7pFv96X2xJIr6IQSZcVFxePEX4PmMGyuYYT66Z+V25h73hyVpwhJ6dOUPvD5qfx -6OswixjDl+YrF/TJzJ4y+Nea3dUba2ZV8N9Rst6vLhEk2g/XrIq2/L5Mbb4YpArx4iFJ8GbMU6ze -QHMiQYdDrr3xFRcFScCqjhmWw/ieU92NiP9ItcGoeIgNgmOh2kHwy29m6PSCZlruVZ1ZMVnKw4VG -QsHob5zY6npUIgdUyUAxsiOLozgpgDwrf1Q17+Ok68+ha2UVlCgTqt6HjNoqz9UsqQ9R9xdNAr3B -uXUqOOaCTBm5ufRI6RRsga4Gd1OUROcexyArZ1ZxO4u2w5RCQ+vqnU6UBjuyYpqGmixsFJ/JS4mc -4rR4b9ROa7OREdtRVDB7v3GimPjxxgTq/BzGHaTABnpRcCPE0GKFxsV8Ka6uaocz5mQ3XPwQ8ozI -xgmtVEXP62VV2+fQ7EGYrTCPQQlR3T9XHCr/taa9c4vicMKWch73HdoSkK1XJbxx9v/E7A/MfTju -5iIhwIh2f2lVIBFXXTbAs25YQfKSVGVKicHRjSkQ62uHdl/PWN4xjXZvCgQ6S4YAJFhjloGf7DQi -mqIqfX+Zpeff//YC9yKU1QyTvrMKU8ThzG8hO0tsP7GVnOjE8t9f/S1qoak3cxjTar+rl2//x8t/ -9lckkXH0WQGyRzN0jCuY0El8d4P68nFbXuF1+Rw1sHiCw/2xZHlsMHi6XCbP8Bt79fHCAhbdbDFs -2oLd9OinQgFfVBh5d3tgH8MB39MwhBtf3Ku1xYIA3fDxCWdXlzuJ1CvtIVGOw98yjggCfYrMR7+B -Mq4r8bdkue+Lsq3n1OKMpzLvlP1AOseWgig6PXv8mc8ezFcW5OXBTbTZ7teIoYfn2vUus/KMrTwP -P/OthBb1fMcW8NYtBZyZ47cUmLrg757lMI80dwfHO349QgWcw/cLG5Jv7+dGw3d4EVRPnYSZA3Jl -M4ss1h0qH6F1sAzbLGgZVOOynR6ADWM2Y3pwkkExemRzJRF3eTQNdeQNRcxWExVJ9zSOumUb2lhk -f1Ij3QK61pM3F3tTjZovf87pLZ/qHQhGfxYj0hUlIshERcd0NcXZEs7nilhrkqdlmWg3R5ovV6Y1 -6Z5M/RXmh1SobnF9lPN5s10IBiN1athKGwJzZTKoy7jnnIQXRKe9o3g4yxgaG25aa2JjdxFWU7Sk -aR34UG/I0q0ujl32EBhwUo4nvtevapfVAQ3odz6hTBddgAmQ7tcvk7be7Zl3c7h05ubJioyjLhHS -cl11w2DYrDsUDoS/Im3Om3b3dI6RPJnTGqZr5IPkKad9Ccz5ISceE2gjTmhsu7H0hPBNDLooDAuH -9oRtDFWMe5EIKKTLFva1OaYauHiH+9W4pCqrdtxcjcsxF/Ex7RrjXTOmJTaGMsbWOsF/XlIcdngl -pI/VAOelUNsgsHCzGB+UqNSF7zZbl7UV4D7b3jRLOtu2ezgOzxGx0PT3K0RvdMYiuVpWd/UlHNzh -GL1iz3k4VpOLpJGqSOJTUyutQUCJkrrpyjD3eMPUspac8smKDrrH1z0yKpDQskqcncAc+RJ3qjZj -a4Gwc7/ksK3E14toeuFhv6VxrhbPhGKeE11CYbhgQWbB+oAmhSjjqY/t/6JBhUHlWQ02PhVSlknE -dQdn8lTMW5eBtkbqtxlCvjeO7e2S60FQUHwjNy1RbYMlAAS1o7HZm2X6FK9rexYnSYVMqcSkWmL6 -/MLQLdISYek2CZCvS1BHBSp6FlyE6dmj4pHdd1wFmWnkiPqXF7pAU1QeiGVSJotl8nCqWOTuuaFU -gVCRtImAjHC3qTGSWMyYxRVyQ8cTteG73+39+f3WV/cq4u7QSFprRK8rhIFzh62rORbdvNfyoWEi -poU1+vR9yhJySzADY6bAWzm7Ps6x0/KFLKS3/3Nf/D8FV4fQ -""" - -import sys -import base64 -import zlib -import imp - -class DictImporter(object): - def __init__(self, sources): - self.sources = sources - - def find_module(self, fullname, path=None): - if fullname in self.sources: - return self - if fullname + '.__init__' in self.sources: - return self - return None - - def load_module(self, fullname): - # print "load_module:", fullname - from types import ModuleType - try: - s = self.sources[fullname] - is_pkg = False - except KeyError: - s = self.sources[fullname + '.__init__'] - is_pkg = True - - co = compile(s, fullname, 'exec') - module = sys.modules.setdefault(fullname, ModuleType(fullname)) - module.__file__ = "%s/%s" % (__file__, fullname) - module.__loader__ = self - if is_pkg: - module.__path__ = [fullname] - - do_exec(co, module.__dict__) - return sys.modules[fullname] - - def get_source(self, name): - res = self.sources.get(name) - if res is None: - res = self.sources.get(name + '.__init__') - return res - -if __name__ == "__main__": - if sys.version_info >= (3, 0): - exec("def do_exec(co, loc): exec(co, loc)\n") - import pickle - sources = sources.encode("ascii") # ensure bytes - sources = pickle.loads(zlib.decompress(base64.decodebytes(sources))) - else: - import cPickle as pickle - exec("def do_exec(co, loc): exec co in loc\n") - sources = pickle.loads(zlib.decompress(base64.decodestring(sources))) - - importer = DictImporter(sources) - sys.meta_path.append(importer) - - entry = "import py; raise SystemExit(py.test.cmdline.main())" - do_exec(entry, locals()) diff --git a/astropy/io/__init__.py b/astropy/io/__init__.py index b062db1285e0..29db62b9ced0 100644 --- a/astropy/io/__init__.py +++ b/astropy/io/__init__.py @@ -1,5 +1,5 @@ # Licensed under a 3-clause BSD style license - see LICENSE.rst """ This subpackage contains modules and packages for interpreting data storage -formats used in astropy. +formats used by and in astropy. """ diff --git a/astropy/io/ascii/__init__.py b/astropy/io/ascii/__init__.py index f6fcc2bd3a20..6c3b31147d36 100644 --- a/astropy/io/ascii/__init__.py +++ b/astropy/io/ascii/__init__.py @@ -1,58 +1,86 @@ -""" An extensible ASCII table reader and writer. +# Licensed under a 3-clause BSD style license - see LICENSE.rst +"""An extensible ASCII table reader and writer.""" -""" -## -## Redistribution and use in source and binary forms, with or without -## modification, are permitted provided that the following conditions are met: -## * Redistributions of source code must retain the above copyright -## notice, this list of conditions and the following disclaimer. -## * Redistributions in binary form must reproduce the above copyright -## notice, this list of conditions and the following disclaimer in the -## documentation and/or other materials provided with the distribution. -## * Neither the name of the Smithsonian Astrophysical Observatory nor the -## names of its contributors may be used to endorse or promote products -## derived from this software without specific prior written permission. -## -## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -## ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -## WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -## DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY -## DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -## (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -## LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -## ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -## SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -## -## Copyright: Smithsonian Astrophysical Observatory (2010) -## Author: Tom Aldcroft (aldcroft@head.cfa.harvard.edu) +from astropy import config as _config -from __future__ import absolute_import - -from .core import (InconsistentTableError, - NoType, StrType, NumType, FloatType, IntType, AllType, - Column, Keyword, - BaseInputter, ContinuationLinesInputter, - BaseHeader, - BaseData, - BaseOutputter, NumpyOutputter, TableOutputter, - BaseReader, - BaseSplitter, DefaultSplitter, WhitespaceSplitter, - convert_numpy, - ) -from .basic import (Basic, - Rdb, - Tab, - NoHeader, - CommentedHeader) +from . import connect +from .basic import ( + Basic, + BasicData, + BasicHeader, + CommentedHeader, + Csv, + NoHeader, + Rdb, + Tab, +) from .cds import Cds -from .latex import Latex, AASTex, latexdicts -from .ipac import Ipac +from .core import ( + AllType, + BaseData, + BaseHeader, + BaseInputter, + BaseOutputter, + BaseReader, + BaseSplitter, + Column, + ContinuationLinesInputter, + DefaultSplitter, + FloatType, + InconsistentTableError, + IntType, + NoType, + NumType, + ParameterError, + StrType, + TableOutputter, + WhitespaceSplitter, + convert_numpy, + masked, +) from .daophot import Daophot -from .memory import Memory -from .fixedwidth import (FixedWidth, FixedWidthNoHeader, - FixedWidthTwoLine, FixedWidthSplitter, - FixedWidthHeader, FixedWidthData) -from .ui import (set_guess, get_reader, read, get_writer, write) +from .ecsv import Ecsv +from .fastbasic import ( + FastBasic, + FastCommentedHeader, + FastCsv, + FastNoHeader, + FastRdb, + FastTab, +) +from .fixedwidth import ( + FixedWidth, + FixedWidthData, + FixedWidthHeader, + FixedWidthNoHeader, + FixedWidthSplitter, + FixedWidthTwoLine, +) +from .html import HTML +from .ipac import Ipac +from .latex import AASTex, Latex, latexdicts +from .mesa import Mesa +from .mrt import Mrt +from .qdp import QDP +from .rst import RST +from .sextractor import SExtractor +from .tdat import Tdat +from .ui import get_read_trace, get_reader, get_writer, read, set_guess, write + + +class Conf(_config.ConfigNamespace): + """ + Configuration parameters for `astropy.io.ascii`. + """ + + guess_limit_lines = _config.ConfigItem( + 10000, + "When guessing the format of a table, this is the number of lines that " + "will be used for initial guessing. If the reading succeeds based on this " + "number of lines, then reading the full table will be attempted. If the reading " + "based on the subset of lines fails, the format will no longer be considered. " + "This can be set to `None` to disable the limit", + ) + -from .version import version as __version__ +conf = Conf() diff --git a/astropy/io/ascii/basic.py b/astropy/io/ascii/basic.py index 6ba1f91da19c..5123a0870b42 100644 --- a/astropy/io/ascii/basic.py +++ b/astropy/io/ascii/basic.py @@ -1,4 +1,5 @@ -"""Asciitable: an extensible ASCII table reader and writer. +# Licensed under a 3-clause BSD style license - see LICENSE.rst +"""An extensible ASCII table reader and writer. basic.py: Basic table read / write functionality for simple character @@ -8,51 +9,45 @@ :Author: Tom Aldcroft (aldcroft@head.cfa.harvard.edu) """ -## -## Redistribution and use in source and binary forms, with or without -## modification, are permitted provided that the following conditions are met: -## * Redistributions of source code must retain the above copyright -## notice, this list of conditions and the following disclaimer. -## * Redistributions in binary form must reproduce the above copyright -## notice, this list of conditions and the following disclaimer in the -## documentation and/or other materials provided with the distribution. -## * Neither the name of the Smithsonian Astrophysical Observatory nor the -## names of its contributors may be used to endorse or promote products -## derived from this software without specific prior written permission. -## -## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -## ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -## WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -## DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY -## DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -## (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -## LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -## ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -## SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - import re + from . import core -from .core import io, next, izip, any + + +class BasicHeader(core.BaseHeader): + """ + Basic table Header Reader. + + Set a few defaults for common ascii table formats + (start at line 0, comments begin with ``#`` and possibly white space) + """ + + start_line = 0 + comment = r"\s*#" + write_comment = "# " + + +class BasicData(core.BaseData): + """ + Basic table Data Reader. + + Set a few defaults for common ascii table formats + (start at line 1, comments begin with ``#`` and possibly white space) + """ + + start_line = 1 + comment = r"\s*#" + write_comment = "# " + class Basic(core.BaseReader): - """Read a character-delimited table with a single header line at the top - followed by data lines to the end of the table. Lines beginning with # as - the first non-whitespace character are comments. This reader is highly - configurable. - :: - - rdr = asciitable.get_reader(Reader=asciitable.Basic) - rdr.header.splitter.delimiter = ' ' - rdr.data.splitter.delimiter = ' ' - rdr.header.start_line = 0 - rdr.data.start_line = 1 - rdr.data.end_line = None - rdr.header.comment = r'\s*#' - rdr.data.comment = r'\s*#' + r"""Character-delimited table with a single header line at the top. + + Lines beginning with a comment character (default='#') as the first + non-whitespace character are comments. Example table:: - + # Column definition is the first uncommented line # Default delimiter is the space character. apples oranges pears @@ -61,165 +56,386 @@ class Basic(core.BaseReader): 1 2 3 4 5 6 """ - def __init__(self): - core.BaseReader.__init__(self) - self.header.splitter.delimiter = ' ' - self.data.splitter.delimiter = ' ' - self.header.start_line = 0 - self.data.start_line = 1 - self.header.comment = r'\s*#' - self.header.write_comment = '# ' - self.data.comment = r'\s*#' - self.data.write_comment = '# ' + + _format_name = "basic" + _description = "Basic table with custom delimiters" + _io_registry_format_aliases = ["ascii"] + + header_class = BasicHeader + data_class = BasicData + + +class NoHeaderHeader(BasicHeader): + """ + Reader for table header without a header. + + Set the start of header line number to `None`, which tells the basic + reader there is no header line. + """ + + start_line = None + + +class NoHeaderData(BasicData): + """ + Reader for table data without a header. + + Data starts at first uncommented line since there is no header line. + """ + + start_line = 0 class NoHeader(Basic): - """Read a table with no header line. Columns are autonamed using - header.auto_format which defaults to "col%d". Otherwise this reader - the same as the :class:`Basic` class from which it is derived. Example:: + """Character-delimited table with no header line. + + When reading, columns are autonamed using header.auto_format which defaults + to "col%d". Otherwise this reader the same as the :class:`Basic` class + from which it is derived. Example:: # Table data 1 2 "hello there" 3 4 world + """ - def __init__(self): - Basic.__init__(self) - self.header.start_line = None - self.data.start_line = 0 + _format_name = "no_header" + _description = "Basic table with no headers" + header_class = NoHeaderHeader + data_class = NoHeaderData -class CommentedHeaderHeader(core.BaseHeader): - """Header class for which the column definition line starts with the + +class CommentedHeaderHeader(BasicHeader): + """ + Header class for which the column definition line starts with the comment character. See the :class:`CommentedHeader` class for an example. """ + def process_lines(self, lines): - """Return only lines that start with the comment regexp. For these - lines strip out the matching characters.""" + """ + Return only lines that start with the comment regexp. For these + lines strip out the matching characters. + """ re_comment = re.compile(self.comment) for line in lines: match = re_comment.match(line) if match: - yield line[match.end():] + yield line[match.end() :] def write(self, lines): - lines.append(self.write_comment + self.splitter.join([x.name for x in self.cols])) + lines.append(self.write_comment + self.splitter.join(self.colnames)) + +class CommentedHeader(Basic): + """Character-delimited table with column names in a comment line. -class CommentedHeader(core.BaseReader): - """Read a file where the column names are given in a line that begins with - the header comment character. `header_start` can be used to specify the + When reading, ``header_start`` can be used to specify the line index of column names, and it can be a negative index (for example -1 for the last commented line). The default delimiter is the - character.:: + character. + + This matches the format produced by ``np.savetxt()``, with ``delimiter=','``, + and ``header=''``. + + Example:: # col1 col2 col3 # Comment line 1 2 3 4 5 6 + + """ + + _format_name = "commented_header" + _description = "Column names in a commented line" + + header_class = CommentedHeaderHeader + data_class = NoHeaderData + + def read(self, table): + """ + Read input data (file-like object, filename, list of strings, or + single string) into a Table and return the result. + """ + out = super().read(table) + + # Strip off the comment line set as the header line for + # commented_header format (first by default). + if "comments" in out.meta: + idx = self.header.start_line + if idx < 0: + idx = len(out.meta["comments"]) + idx + out.meta["comments"] = ( + out.meta["comments"][:idx] + out.meta["comments"][idx + 1 :] + ) + if not out.meta["comments"]: + del out.meta["comments"] + + return out + + def write_header(self, lines, meta): + """ + Write comment lines after, rather than before, the header. + """ + self.header.write(lines) + self.header.write_comments(lines, meta) + + +class TabHeaderSplitter(core.DefaultSplitter): + """Split lines on tab and do not remove whitespace.""" + + delimiter = "\t" + + def process_line(self, line): + return line + "\n" + + +class TabDataSplitter(TabHeaderSplitter): + """ + Don't strip data value whitespace since that is significant in TSV tables. + """ + + process_val = None + skipinitialspace = False + + +class TabHeader(BasicHeader): + """ + Reader for header of tables with tab separated header. """ - def __init__(self): - core.BaseReader.__init__(self) - self.header = CommentedHeaderHeader() - self.header.data = self.data - self.data.header = self.header - self.header.splitter.delimiter = ' ' - self.data.splitter.delimiter = ' ' - self.header.start_line = 0 - self.data.start_line = 0 - self.header.comment = r'\s*#' - self.header.write_comment = '# ' - self.data.comment = r'\s*#' - self.data.write_comment = '# ' + + splitter_class = TabHeaderSplitter + + +class TabData(BasicData): + """ + Reader for data of tables with tab separated data. + """ + + splitter_class = TabDataSplitter class Tab(Basic): - """Read a tab-separated file. Unlike the :class:`Basic` reader, whitespace is - not stripped from the beginning and end of lines. By default whitespace is - still stripped from the beginning and end of individual column values. + """Tab-separated table. + + Unlike the :class:`Basic` reader, whitespace is not stripped from the + beginning and end of either lines or individual column values. Example:: col1 col2 col3 # Comment line 1 2 5 + """ - def __init__(self): - Basic.__init__(self) - self.header.splitter.delimiter = '\t' - self.data.splitter.delimiter = '\t' - # Don't strip line whitespace since that includes tabs - self.header.splitter.process_line = None - self.data.splitter.process_line = None - # Don't strip data value whitespace since that is significant in TSV tables - self.data.splitter.process_val = None - self.data.splitter.skipinitialspace = False + _format_name = "tab" + _description = "Basic table with tab-separated values" + header_class = TabHeader + data_class = TabData -class Rdb(Tab): - """Read a tab-separated file with an extra line after the column definition - line. The RDB format meets this definition. Example:: - col1 col2 col3 - N S N - 1 2 5 +class CsvSplitter(core.DefaultSplitter): + """ + Split on comma for CSV (comma-separated-value) tables. + """ + + delimiter = "," + + +class CsvHeader(BasicHeader): + """ + Header that uses the :class:`astropy.io.ascii.basic.CsvSplitter`. + """ + + splitter_class = CsvSplitter + comment = None + write_comment = None + + +class CsvData(BasicData): + """ + Data that uses the :class:`astropy.io.ascii.basic.CsvSplitter`. + """ + + splitter_class = CsvSplitter + fill_values = [(core.masked, "")] + comment = None + write_comment = None + + +class Csv(Basic): + """CSV (comma-separated-values) table. + + This file format may contain rows with fewer entries than the number of + columns, a situation that occurs in output from some spreadsheet editors. + The missing entries are marked as masked in the output table. + + Masked values (indicated by an empty '' field value when reading) are + written out in the same way with an empty ('') field. This is different + from the typical default for `astropy.io.ascii` in which missing values are + indicated by ``--``. + + By default leading or trailing whitespace in column names is stripped. If + you pass ``strip_column_names=False`` then this is disabled. + + Since the `CSV format `_ does not + formally support comments, any comments defined for the table via + ``tbl.meta['comments']`` are ignored by default. If you would still like to + write those comments then include a keyword ``comment='#'`` to the + ``write()`` call. + + Example:: + + num,ra,dec,radius,mag + 1,32.23222,10.1211 + 2,38.12321,-88.1321,2.2,17.0 - In this reader the second line is just ignored. """ - def __init__(self): - Tab.__init__(self) - self.header = RdbHeader() - self.header.start_line = 0 - self.header.comment = r'\s*#' - self.header.write_comment = '# ' - self.header.splitter.delimiter = '\t' - self.header.splitter.process_line = None - self.header.data = self.data - self.data.header = self.header - self.data.start_line = 2 + _format_name = "csv" + _io_registry_format_aliases = ["csv"] + _io_registry_can_write = True + _io_registry_suffix = ".csv" + _description = "Comma-separated-values" + + header_class = CsvHeader + data_class = CsvData -class RdbHeader(core.BaseHeader): - col_type_map = {'n': core.NumType, - 's': core.StrType} + def __init__(self, *, strip_column_names=True): + super().__init__() + if not strip_column_names: + self.header.splitter.process_val = None + + def inconsistent_handler(self, str_vals, ncols): + """ + Adjust row if it is too short. + + If a data row is shorter than the header, add empty values to make it the + right length. + Note that this will *not* be called if the row already matches the header. + + Parameters + ---------- + str_vals : list + A list of value strings from the current row of the table. + ncols : int + The expected number of entries from the table header. + + Returns + ------- + str_vals : list + List of strings to be parsed into data entries in the output table. + """ + if len(str_vals) < ncols: + str_vals.extend((ncols - len(str_vals)) * [""]) + + return str_vals + + +class RdbHeader(TabHeader): + """ + Header for RDB tables. + """ + + col_type_map = {"n": core.NumType, "s": core.StrType} def get_type_map_key(self, col): return col.raw_type[-1] def get_cols(self, lines): - """Initialize the header Column objects from the table ``lines``. - + """ + Initialize the header Column objects from the table ``lines``. + This is a specialized get_cols for the RDB type: Line 0: RDB col names Line 1: RDB col definitions Line 2+: RDB data rows - :param lines: list of table lines - :returns: None + Parameters + ---------- + lines : list + List of table lines + + Returns + ------- + None + """ - header_lines = self.process_lines(lines) # this is a generator + header_lines = self.process_lines(lines) # this is a generator header_vals_list = [hl for _, hl in zip(range(2), self.splitter(header_lines))] if len(header_vals_list) != 2: - raise ValueError('RDB header requires 2 lines') + raise ValueError("RDB header requires 2 lines") self.names, raw_types = header_vals_list if len(self.names) != len(raw_types): - raise ValueError('RDB header mismatch between number of column names and column types') - - if any(not re.match(r'\d*(N|S)$', x, re.IGNORECASE) for x in raw_types): - raise ValueError('RDB types definitions do not all match [num](N|S): %s' % raw_types) - + raise core.InconsistentTableError( + "RDB header mismatch between number of column names and column types." + ) + + if any(not re.match(r"\d*(N|S)$", x, re.IGNORECASE) for x in raw_types): + raise core.InconsistentTableError( + f"RDB types definitions do not all match [num](N|S): {raw_types}" + ) + self._set_cols_from_names() for col, raw_type in zip(self.cols, raw_types): col.raw_type = raw_type col.type = self.get_col_type(col) def write(self, lines): - lines.append(self.splitter.join([x.name for x in self.cols])) + lines.append(self.splitter.join(self.colnames)) rdb_types = [] for col in self.cols: - if issubclass(col.type, core.NumType): - rdb_types.append('N') - else: - rdb_types.append('S') + # Check if dtype.kind is string or unicode. See help(np.core.numerictypes) + rdb_type = "S" if col.info.dtype.kind in ("S", "U") else "N" + rdb_types.append(rdb_type) + lines.append(self.splitter.join(rdb_types)) + +class RdbData(TabData): + """ + Data reader for RDB data. Starts reading at line 2. + """ + + start_line = 2 + + +class Rdb(Tab): + """Tab-delimited table with a column name row and a type definition row. + + The ``rdb`` format is a legacy format that was originally created in 1991 as the + basis for a suite of Unix command-line relational database utilities. + + The ``rdb`` format is defined as follows: + + - The table text starts with zero or more comment lines that begin with ``#``. + - Comments are allowed only at the beginning of the table. + - First row after the (optional) comments specifies the column names. + - Second row after the comments specifies the data types: + + - Data type can be either ``S`` for string or ``N`` for numeric (case-insensitive). + - Data type specifier can optionally be preceded with an integer to indicate the + width when printing the table, but the ``astropy`` reader ignores it. + - Subsequent rows contain the data values. + - All row entries in the header and data are separated by a tab character. + + Example (where the added spaces are for visual clarity):: + + # Comment line + # ----------------- + name age eye-color + 6S 5N S + Bob 45 blue + Mary 32 brown + Jill 80 hazel + """ + + _format_name = "rdb" + _io_registry_format_aliases = ["rdb"] + _io_registry_suffix = ".rdb" + _description = "Tab-separated with a type definition header line" + + header_class = RdbHeader + data_class = RdbData diff --git a/astropy/io/ascii/cds.py b/astropy/io/ascii/cds.py index 7eda27211e68..694fc02094af 100644 --- a/astropy/io/ascii/cds.py +++ b/astropy/io/ascii/cds.py @@ -1,4 +1,5 @@ -"""Asciitable: an extensible ASCII table reader and writer. +# Licensed under a 3-clause BSD style license - see LICENSE.rst +"""An extensible ASCII table reader and writer. cds.py: Classes to read CDS / Vizier table format @@ -7,76 +8,84 @@ :Author: Tom Aldcroft (aldcroft@head.cfa.harvard.edu) """ -## -## Redistribution and use in source and binary forms, with or without -## modification, are permitted provided that the following conditions are met: -## * Redistributions of source code must retain the above copyright -## notice, this list of conditions and the following disclaimer. -## * Redistributions in binary form must reproduce the above copyright -## notice, this list of conditions and the following disclaimer in the -## documentation and/or other materials provided with the distribution. -## * Neither the name of the Smithsonian Astrophysical Observatory nor the -## names of its contributors may be used to endorse or promote products -## derived from this software without specific prior written permission. -## -## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -## ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -## WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -## DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY -## DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -## (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -## LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -## ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -## SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - import fnmatch import itertools +import os import re +from contextlib import suppress +from pathlib import Path + +from astropy.table.operations import vstack +from astropy.units import Unit, UnitsWarning, UnrecognizedUnit + +from . import core, fixedwidth + +__doctest_skip__ = ["*"] + + +def _is_section_delimiter(line): + """Check if line is a section delimiter. + + CDS/MRT tables use dashes or equal signs ("------" or "======") to + separate sections. This function checks if a line contains only either + of these characters. + + Parameters + ---------- + line : str + String containing an entire line from the table text file. + + Returns + ------- + status : bool + True if the line is a section delimiter, False otherwise. + + """ + # Check that line starts with either 6 "-" or "=" + # and that it contains only a single repeated character. + # Latter condition fixes cases where a regular row starts with 6 "-". + return line.startswith(("------", "=======")) and len(set(line.strip())) == 1 -from . import core -from . import fixedwidth class CdsHeader(core.BaseHeader): - col_type_map = {'e': core.FloatType, - 'f': core.FloatType, - 'i': core.IntType, - 'a': core.StrType} + _subfmt = "CDS" + + col_type_map = { + "e": core.FloatType, + "f": core.FloatType, + "i": core.IntType, + "a": core.StrType, + } + + "The ReadMe file to construct header from." + readme = None def get_type_map_key(self, col): - match = re.match(r'\d*(\S)', col.raw_type.lower()) + match = re.match(r"\d*(\S)", col.raw_type.lower()) if not match: - raise ValueError('Unrecognized CDS format "%s" for column "%s"' % ( - col.raw_type, col.name)) + raise ValueError( + f'Unrecognized {self._subfmt} format "{col.raw_type}" for column' + f'"{col.name}"' + ) return match.group(1) - def __init__(self, readme=None): - """Initialize ReadMe filename. - - :param readme: The ReadMe file to construct header from. - :type readme: String - - CDS tables have their header information in a separate file - named "ReadMe". The ``get_cols`` method will read the contents - of the ReadMe file given by ``self.readme`` and set the various - properties needed to read the data file. The data file name - will be the ``table`` passed to the ``read`` method. - """ - core.BaseHeader.__init__(self) - self.readme = readme - def get_cols(self, lines): - """Initialize the header Column objects from the table ``lines`` for a CDS + """ + Initialize the header Column objects from the table ``lines`` for a CDS/MRT header. - :param lines: list of table lines - :returns: list of table Columns + Parameters + ---------- + lines : list + List of table lines + """ # Read header block for the table ``self.data.table_name`` from the read # me file ``self.readme``. if self.readme and self.data.table_name: in_header = False - f = open(self.readme,"r") + readme_inputter = core.BaseInputter() + f = readme_inputter.get_lines(self.readme) # Header info is not in data lines but in a separate file. lines = [] comment_lines = 0 @@ -84,120 +93,182 @@ def get_cols(self, lines): line = line.strip() if in_header: lines.append(line) - if line.startswith('------') or line.startswith('======='): - comment_lines += 1 - if comment_lines == 3: - break + if _is_section_delimiter(line): + comment_lines += 1 + if comment_lines == 3: + break else: - match = re.match(r'Byte-by-byte Description of file: (?P.+)$', - line, re.IGNORECASE) + match = re.match( + r"Byte-by-byte Description of file: (?P.+)$", + line, + re.IGNORECASE, + ) if match: # Split 'name' in case in contains multiple files - names = [s for s in re.split('[, ]+', match.group('name')) - if s] + names = [s for s in re.split("[, ]+", match.group("name")) if s] # Iterate on names to find if one matches the tablename # including wildcards. for pattern in names: - if fnmatch.fnmatch(self.data.table_name, pattern): + if fnmatch.fnmatch( + self.data.table_name.removesuffix(".gz"), pattern + ): in_header = True lines.append(line) break else: - raise core.InconsistentTableError("Cant' find table {0} in {1}".format( - self.data.table_name, self.readme)) - f.close() + raise core.InconsistentTableError( + f"Can't find table {self.data.table_name} in {self.readme}" + ) + + found_line = False for i_col_def, line in enumerate(lines): - if re.match(r'Byte-by-byte Description', line, re.IGNORECASE): + if re.match(r"Byte-by-byte Description", line, re.IGNORECASE): + found_line = True + elif found_line: # First line after list of file descriptions + i_col_def -= 1 # Set i_col_def to last description line break - - re_col_def = re.compile(r"""\s* - (?P \d+ \s* -)? \s* - (?P \d+) \s+ - (?P [\w.]+) \s+ - (?P \S+) \s+ - (?P \S+) \s+ - (?P \S.+)""", - re.VERBOSE) + else: + raise ValueError('no line with "Byte-by-byte Description" found') + + re_col_def = re.compile( + r"""\s* + (?P \d+ \s* -)? \s* + (?P \d+) \s+ + (?P [\w.]+) \s+ + (?P \S+) \s+ + (?P \S+) + (\s+ (?P \S.*))?""", + re.VERBOSE, + ) cols = [] - for i, line in enumerate(itertools.islice(lines, i_col_def+4, None)): - if line.startswith('------') or line.startswith('======='): + for line in itertools.islice(lines, i_col_def + 4, None): + if _is_section_delimiter(line): break match = re_col_def.match(line) if match: - col = core.Column(name=match.group('name'), index=i) - col.start = int(re.sub(r'[-\s]', '', match.group('start') or match.group('end'))) - 1 - col.end = int(match.group('end')) - col.units = match.group('units') - col.descr = match.group('descr') - col.raw_type = match.group('format') - col.type = self.get_col_type(col) - - match = re.match(r'\? (?P =)? (?P \S*)', col.descr, re.VERBOSE) + col = core.Column(name=match.group("name")) + col.start = int( + re.sub(r'[-\s]', '', match.group('start') or match.group('end'))) - 1 # fmt: skip + col.end = int(match.group("end")) + unit = match.group("units") + if unit == "---": + col.unit = None # "---" is the marker for no unit in CDS/MRT table + else: + try: + col.unit = Unit(unit, format="cds", parse_strict="warn") + except UnitsWarning: + # catch when warnings are turned into errors so we can check + # whether this line is likely a multi-line description (see below) + col.unit = UnrecognizedUnit(unit) + col.description = (match.group("descr") or "").strip() + col.raw_type = match.group("format") + try: + col.type = self.get_col_type(col) + except ValueError: + # If parsing the format fails and the unit is unrecognized, + # then this line is likely a continuation of the previous col's + # description that happens to start with a number + if isinstance(col.unit, UnrecognizedUnit): + if len(cols[-1].description) > 0: + cols[-1].description += " " + cols[-1].description += line.strip() + continue + else: + if col.unit is not None: + # Because we may have ignored a UnitsWarning turned into an error + # we do this again so it can be raised again if it is a real error + col.unit = Unit(unit, format="cds", parse_strict="warn") + match = re.match( + # Matches limits specifier (eg []) that may or may not be + # present + r"(?P[\[\]] \S* [\[\]])?" + # Matches '?' directly + r"\?" + # Matches to nullval if and only if '=' is present + r"((?P=)(?P \S*))?" + # Matches to order specifier: ('+', '-', '+=', '-=') + r"(?P[-+]?[=]?)" + # Matches description text even even if no whitespace is + # present after '?' + r"(\s* (?P \S.*))?", + col.description, + re.VERBOSE, + ) if match: + col.description = (match.group("descriptiontext") or "").strip() if issubclass(col.type, core.FloatType): - fillval = 'nan' + fillval = "nan" else: - fillval = '-999' - if match.group('nullval') == '': - col.null = '' - elif match.group('nullval') == '-': - col.null = '---' + fillval = "0" + + if match.group("nullval") == "-": + col.null = "---" + # CDS/MRT tables can use -, --, ---, or ---- to mark missing values + # see https://github.com/astropy/astropy/issues/1335 + for i in [1, 2, 3, 4]: + self.data.fill_values.append(("-" * i, fillval, col.name)) else: - col.null = match.group('nullval') - self.data.fill_values.append((col.null, fillval, col.name)) + col.null = match.group("nullval") + if col.null is None: + col.null = "" + self.data.fill_values.append((col.null, fillval, col.name)) cols.append(col) else: # could be a continuation of the previous col's description if cols: - cols[-1].descr += line.strip() + if len(cols[-1].description) > 0: + cols[-1].description += " " + cols[-1].description += line.strip() else: - raise ValueError('Line "%s" not parsable as CDS header' % line) + raise ValueError(f'Line "{line}" not parsable as CDS header') self.names = [x.name for x in cols] - names = set(self.names) - if self.include_names is not None: - names.intersection_update(self.include_names) - if self.exclude_names is not None: - names.difference_update(self.exclude_names) - self.cols = [x for x in cols if x.name in names] - self.n_data_cols = len(self.cols) - - # Re-index the cols because the FixedWidthSplitter does NOT return the ignored - # cols (as is the case for typical delimiter-based splitters) - for i, col in enumerate(self.cols): - col.index = i + self.cols = cols class CdsData(core.BaseData): - """CDS table data reader - """ + """CDS table data reader.""" + + _subfmt = "CDS" splitter_class = fixedwidth.FixedWidthSplitter def process_lines(self, lines): - """Skip over CDS header by finding the last section delimiter""" + """Skip over CDS/MRT header by finding the last section delimiter.""" # If the header has a ReadMe and data has a filename # then no need to skip, as the data lines do not have header # info. The ``read`` method adds the table_name to the ``data`` # attribute. if self.header.readme and self.table_name: return lines - i_sections = [i for (i, x) in enumerate(lines) - if x.startswith('------') or x.startswith('=======')] + i_sections = [i for i, x in enumerate(lines) if _is_section_delimiter(x)] if not i_sections: - raise core.InconsistentTableError('No CDS section delimiter found') - return lines[i_sections[-1]+1 : ] + raise core.InconsistentTableError( + f"No {self._subfmt} section delimiter found" + ) + return lines[i_sections[-1] + 1 :] class Cds(core.BaseReader): - """Read a CDS format table. See http://vizier.u-strasbg.fr/doc/catstd.htx. + """CDS format table. + + See: https://vizier.unistra.fr/doc/catstd.htx + Example:: - Table: Spitzer-identified YSOs: Addendum + Table: Table name here = ============================================================================== + Catalog reference paper + Bibliography info here + ================================================================================ + ADC_Keywords: Keyword ; Another keyword ; etc + + Description: + Catalog description here. + ================================================================================ Byte-by-byte Description of file: datafile3.txt -------------------------------------------------------------------------------- Bytes Format Units Label Explanations @@ -206,20 +277,52 @@ class Cds(core.BaseReader): 5- 6 I2 h RAh Hour of Right Ascension (J2000) 8- 9 I2 min RAm Minute of Right Ascension (J2000) 11- 15 F5.2 s RAs Second of Right Ascension (J2000) + -------------------------------------------------------------------------------- + Note (1): A CDS file can contain sections with various metadata. + Notes can be multiple lines. + Note (2): Another note. -------------------------------------------------------------------------------- 1 03 28 39.09 + 2 04 18 24.11 + + **About parsing the CDS format** + + The CDS format consists of a table description and the table data. These + can be in separate files as a ``ReadMe`` file plus data file(s), or + combined in a single file. Different subsections within the description + are separated by lines of dashes or equal signs ("------" or "======"). + The table which specifies the column information must be preceded by a line + starting with "Byte-by-byte Description of file:". + + In the case where the table description is combined with the data values, + the data must be in the last section and must be preceded by a section + delimiter line (dashes or equal signs only). **Basic usage** - Use the ``asciitable.read()`` function as normal, with an optional ``readme`` + Use the ``ascii.read()`` function as normal, with an optional ``readme`` parameter indicating the CDS ReadMe file. If not supplied it is assumed that the header information is at the top of the given table. Examples:: - >>> import asciitable - >>> table = asciitable.read("t/cds.dat") - >>> table = asciitable.read("t/vizier/table1.dat", readme="t/vizier/ReadMe") - >>> table = asciitable.read("t/cds/multi/lhs2065.dat", readme="t/cds/multi/ReadMe") - >>> table = asciitable.read("t/cds/glob/lmxbrefs.dat", readme="t/cds/glob/ReadMe") + >>> from astropy.io import ascii + >>> table = ascii.read("data/cds.dat") + >>> table = ascii.read("data/vizier/table1.dat", readme="data/vizier/ReadMe") + >>> table = ascii.read("data/cds/multi/lhs2065.dat", readme="data/cds/multi/ReadMe") + >>> table = ascii.read("data/cds/glob/lmxbrefs.dat", readme="data/cds/glob/ReadMe") + + The table name and the CDS ReadMe file can be entered as URLs. This can be used + to directly load tables from the Internet. For example, Vizier tables from the + CDS:: + + >>> table = ascii.read("ftp://cdsarc.unistra.fr/pub/cats/VII/253/snrs.dat", + ... readme="ftp://cdsarc.unistra.fr/pub/cats/VII/253/ReadMe") + + If the header (ReadMe) and data are stored in a single file and there + is content between the header and the data (for instance Notes), then the + parsing process may fail. In this case you can instruct the reader to + guess the actual start of the data by supplying ``data_start='guess'`` in the + call to the ``ascii.read()`` function. You should verify that the output + data table matches expectation based on the input CDS file. **Using a reader object** @@ -230,38 +333,95 @@ class Cds(core.BaseReader): ``InconsistentTableError`` is raised if the ``readme`` file does not have header information for the given table. - >>> readme = "t/vizier/ReadMe" - >>> r = asciitable.get_reader(asciitable.Cds, readme=readme) - >>> table = r.read("t/vizier/table1.dat") + >>> readme = "data/vizier/ReadMe" + >>> r = ascii.get_reader(ascii.Cds, readme=readme) + >>> table = r.read("data/vizier/table1.dat") >>> # table5.dat has the same ReadMe file - >>> table = r.read("t/vizier/table5.dat") + >>> table = r.read("data/vizier/table5.dat") If no ``readme`` parameter is specified, then the header information is assumed to be at the top of the given table. - >>> r = asciitable.get_reader(asciitable.Cds) - >>> table = r.read("t/cds.dat") + >>> r = ascii.get_reader(ascii.Cds) + >>> table = r.read("data/cds.dat") >>> #The following gives InconsistentTableError, since no >>> #readme file was given and table1.dat does not have a header. - >>> table = r.read("t/vizier/table1.dat") + >>> table = r.read("data/vizier/table1.dat") Traceback (most recent call last): ... InconsistentTableError: No CDS section delimiter found Caveats: - * Format, Units, and Explanations are available in the ``Reader.cols`` attribute. - * All of the other metadata defined by this format is ignored. - - Code contribution to enhance the parsing to include metadata in a Reader.meta - attribute would be welcome. + * The Units and Explanations are available in the column ``unit`` and + ``description`` attributes, respectively. + * The other metadata defined by this format is not available in the output table. + In rare cases, the data at CDS is split into multiple files to keep the file size + below 10 MB. In that case, the datafile name is given as e.g. "tyc2.dat", which will + read and merge all files named "tyc2.dat.00", "tyc2.dat.01", etc. """ + + _format_name = "cds" + _io_registry_format_aliases = ["cds"] + _io_registry_can_write = False + _description = "CDS format table" + + data_class = CdsData + header_class = CdsHeader + def __init__(self, readme=None): - core.BaseReader.__init__(self) - self.header = CdsHeader(readme) - self.data = CdsData() + super().__init__() + self.header.readme = readme def write(self, table=None): - """Not available for the Cds class (raises NotImplementedError)""" + """Not available for the CDS class (raises NotImplementedError).""" raise NotImplementedError + + def read_table(self, table): + # If the read kwarg `data_start` is 'guess' then the table may have extraneous + # lines between the end of the header and the beginning of data. + if self.data.start_line == "guess": + # Replicate the first part of BaseReader.read up to the point where + # the table lines are initially read in. + with suppress(TypeError): + # For strings only + if os.linesep not in table + "": + self.data.table_name = Path(table).name + + self.data.header = self.header + self.header.data = self.data + + # Get a list of the lines (rows) in the table + lines = self.inputter.get_lines(table) + + # Now try increasing data.start_line by one until the table reads successfully. + # For efficiency use the in-memory list of lines instead of `table`, which + # could be a file. + for data_start in range(len(lines)): + self.data.start_line = data_start + with suppress(Exception): + return super().read(lines) + else: + return super().read(table) + + def read(self, table): + try: + return self.read_table(table) + except FileNotFoundError as e: + # deal with table where the ReadMe is present, but the data is split over several data files + if self.header.readme is not None: + path = Path(table) + if f_list := sorted(path.parent.glob(path.name + "*")): + pattern = re.compile(r"\.(\d{2,3})(\.gz)?$") + numbers = [int(pattern.search(f.name).group(1)) for f in f_list] + if numbers != list(range(len(numbers))): + raise core.InconsistentTableError( + f"Files for {table} appear to be split into multiple parts, " + "but the numbering is not consecutive for filenames: " + f"{[f.name for f in f_list]}" + ) from e + all_data = [self.read_table(f) for f in f_list] + return vstack(all_data, join_type="exact") + + raise e diff --git a/astropy/io/ascii/connect.py b/astropy/io/ascii/connect.py new file mode 100644 index 000000000000..1501e450352e --- /dev/null +++ b/astropy/io/ascii/connect.py @@ -0,0 +1,57 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +# This file connects the readers/writers to the astropy.table.Table class + + +import re + +from astropy.io import registry as io_registry # noqa: F401 +from astropy.table import Table + +__all__ = [] + + +def io_read(format, filename, **kwargs): + from .ui import read + + if format != "ascii": + format = re.sub(r"^ascii\.", "", format) + kwargs["format"] = format + return read(filename, **kwargs) + + +def io_write(format, table, filename, **kwargs): + from .ui import write + + if format != "ascii": + format = re.sub(r"^ascii\.", "", format) + kwargs["format"] = format + return write(table, filename, **kwargs) + + +def io_identify(suffix, origin, filepath, fileobj, *args, **kwargs): + return filepath is not None and filepath.endswith(suffix) + + +def _get_connectors_table(): + from .core import FORMAT_CLASSES + + rows = [] + rows.append( + ("ascii", "", "Yes", "ASCII table in any supported format (uses guessing)") + ) + for format in sorted(FORMAT_CLASSES): + cls = FORMAT_CLASSES[format] + + io_format = "ascii." + cls._format_name + description = getattr(cls, "_description", "") + class_link = f":class:`~{cls.__module__}.{cls.__name__}`" + suffix = getattr(cls, "_io_registry_suffix", "") + can_write = "Yes" if getattr(cls, "_io_registry_can_write", True) else "" + + rows.append((io_format, suffix, can_write, f"{class_link}: {description}")) + out = Table(list(zip(*rows)), names=("Format", "Suffix", "Write", "Description")) + for colname in ("Format", "Description"): + width = max(len(x) for x in out[colname]) + out[colname].format = f"%-{width}s" + + return out diff --git a/astropy/io/ascii/core.py b/astropy/io/ascii/core.py index 761866814f38..de6d3ec92e4c 100644 --- a/astropy/io/ascii/core.py +++ b/astropy/io/ascii/core.py @@ -1,4 +1,5 @@ -""" Asciitable: an extensible ASCII table reader and writer. +# Licensed under a 3-clause BSD style license - see LICENSE.rst +"""An extensible ASCII table reader and writer. core.py: Core base classes and functions for reading and writing tables. @@ -6,167 +7,364 @@ :Copyright: Smithsonian Astrophysical Observatory (2010) :Author: Tom Aldcroft (aldcroft@head.cfa.harvard.edu) """ -## -## Redistribution and use in source and binary forms, with or without -## modification, are permitted provided that the following conditions are met: -## * Redistributions of source code must retain the above copyright -## notice, this list of conditions and the following disclaimer. -## * Redistributions in binary form must reproduce the above copyright -## notice, this list of conditions and the following disclaimer in the -## documentation and/or other materials provided with the distribution. -## * Neither the name of the Smithsonian Astrophysical Observatory nor the -## names of its contributors may be used to endorse or promote products -## derived from this software without specific prior written permission. -## -## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -## ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -## WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -## DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY -## DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -## (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -## LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -## ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -## SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import os -import sys -import re +from __future__ import annotations + +import copy import csv +import fnmatch +import functools +import inspect import itertools -import numpy +import operator +import os +import re +import warnings +from contextlib import suppress +from io import StringIO +from pathlib import Path +from typing import ClassVar, Final, Self, SupportsFloat, TypeGuard + +import numpy as np + +from astropy.table import Table +from astropy.utils.data import get_readable_fileobj +from astropy.utils.exceptions import AstropyWarning + +from . import connect +from .docs import READ_DOCSTRING, WRITE_DOCSTRING + +# Global dictionary mapping format arg to the corresponding Reader class +FORMAT_CLASSES: dict[str, MetaBaseReader] = {} + +# Similar dictionary for fast readers +FAST_CLASSES: dict[str, MetaBaseReader] = {} + + +def _check_multidim_table(table: Table, max_ndim: int | None) -> None: + """Check that ``table`` has only columns with ndim <= ``max_ndim``. + + Currently ECSV is the only built-in format that supports output of arbitrary + N-d columns, but HTML supports 2-d. + """ + # No limit? + if max_ndim is None: + return + + # Check for N-d columns + nd_names = [col.info.name for col in table.itercols() if len(col.shape) > max_ndim] + if nd_names: + raise ValueError( + f"column(s) with dimension > {max_ndim} " + "cannot be be written with this format, try using 'ecsv' " + "(Enhanced CSV) format" + ) + + +class CsvWriter: + """ + Internal class to replace the csv writer ``writerow`` and ``writerows`` + functions so that in the case of ``delimiter=' '`` and + ``quoting=csv.QUOTE_MINIMAL``, the output field value is quoted for empty + fields (when value == ''). + + This changes the API slightly in that the writerow() and writerows() + methods return the output written string instead of the length of + that string. + + Examples + -------- + >>> from astropy.io.ascii.core import CsvWriter + >>> writer = CsvWriter(delimiter=' ') + >>> print(writer.writerow(['hello', '', 'world'])) + hello "" world + """ + + # Random 16-character string that gets injected instead of any + # empty fields and is then replaced post-write with doubled-quotechar. + # Created with: + # ''.join(random.choice(string.printable[:90]) for _ in range(16)) + replace_sentinel: Final[str] = "2b=48Av%0-V3p>bX" + + def __init__(self, csvfile=None, **kwargs): + self.csvfile = csvfile + + # Temporary StringIO for catching the real csv.writer() object output + self.temp_out = StringIO() + self.writer = csv.writer(self.temp_out, **kwargs) + + dialect = self.writer.dialect + self.quotechar2 = dialect.quotechar * 2 + self.quote_empty = (dialect.quoting == csv.QUOTE_MINIMAL) and ( + dialect.delimiter == " " + ) + + def writerow(self, values): + """ + Similar to csv.writer.writerow but with the custom quoting behavior. + Returns the written string instead of the length of that string. + """ + has_empty = False + + # If QUOTE_MINIMAL and space-delimited then replace empty fields with + # the sentinel value. + if self.quote_empty: + for i, value in enumerate(values): + if value == "": + has_empty = True + values[i] = self.replace_sentinel + + return self._writerow(self.writer.writerow, values, has_empty) + + def writerows(self, values_list): + """ + Similar to csv.writer.writerows but with the custom quoting behavior. + Returns the written string instead of the length of that string. + """ + has_empty = False + + # If QUOTE_MINIMAL and space-delimited then replace empty fields with + # the sentinel value. + if self.quote_empty: + for values in values_list: + for i, value in enumerate(values): + if value == "": + has_empty = True + values[i] = self.replace_sentinel + + return self._writerow(self.writer.writerows, values_list, has_empty) + + def _writerow(self, writerow_func, values, has_empty): + """ + Call ``writerow_func`` (either writerow or writerows) with ``values``. + If it has empty fields that have been replaced then change those + sentinel strings back to quoted empty strings, e.g. ``""``. + """ + # Clear the temporary StringIO buffer that self.writer writes into and + # then call the real csv.writer().writerow or writerows with values. + self.temp_out.seek(0) + self.temp_out.truncate() + writerow_func(values) + + row_string = self.temp_out.getvalue() + + if self.quote_empty and has_empty: + row_string = re.sub(self.replace_sentinel, self.quotechar2, row_string) + + # self.csvfile is defined then write the output. In practice the pure + # Python writer calls with csvfile=None, while the fast writer calls with + # a file-like object. + if self.csvfile: + self.csvfile.write(row_string) + + return row_string + -from ...table import Table +class MaskedConstant(np.ma.core.MaskedConstant): + """A trivial extension of numpy.ma.masked. + + We want to be able to put the generic term ``masked`` into a dictionary. + The constant ``numpy.ma.masked`` is not hashable (see + https://github.com/numpy/numpy/issues/4660), so we need to extend it + here with a hash value. + + See https://github.com/numpy/numpy/issues/11021 for rationale for + __copy__ and __deepcopy__ methods. + """ + + def __hash__(self): + """All instances of this class shall have the same hash.""" + # Any large number will do. + return 1234567890 + + def __copy__(self) -> Self: + """This is a singleton so just return self.""" + return self + + def __deepcopy__(self, memo): + return self + + +masked: Final[MaskedConstant] = MaskedConstant() class InconsistentTableError(ValueError): - pass - -# Python 3 compatibility tweaks. Should work back through 2.4. -try: - import cStringIO as io -except ImportError: - import io - -try: - next = next -except NameError: - next = lambda x: x.next() - -try: - izip = itertools.izip -except AttributeError: - izip = zip - -try: - long = long -except NameError: - long = int - -try: - unicode = unicode -except NameError: - unicode = str - -# Python 2.4 comptability: any() function is built-in only for 2.5 onward -try: - any = any -except NameError: - def any(vals): - for val in vals: - if val: - return True - return False - -class Keyword(object): - """Table keyword""" - def __init__(self, name, value, units=None, comment=None, format=None): - self.name = name - self.value = value - self.units = units - self.comment = comment - self.format = format + """ + Indicates that an input table is inconsistent in some way. + + The default behavior of ``BaseReader`` is to throw an instance of + this class if a data row doesn't match the header. + """ + + +class OptionalTableImportError(ImportError): + """ + Indicates that a dependency for table reading is not present. + + An instance of this class is raised whenever an optional reader + with certain required dependencies cannot operate because of + an ImportError. + """ + + +class ParameterError(NotImplementedError): + """ + Indicates that a reader cannot handle a passed parameter. + + The C-based fast readers in ``io.ascii`` raise an instance of + this error class upon encountering a parameter that the + C engine cannot handle. + """ + + +class FastOptionsError(NotImplementedError): + """ + Indicates that one of the specified options for fast + reading is invalid. + """ + + +class NoType: + """ + Superclass for ``StrType`` and ``NumType`` classes. + + This class is the default type of ``Column`` and provides a base + class for other data types. + """ -class NoType(object): - pass class StrType(NoType): - pass + """ + Indicates that a column consists of text data. + """ + class NumType(NoType): - pass + """ + Indicates that a column consists of numerical data. + """ + class FloatType(NumType): - pass + """ + Describes floating-point data. + """ + + +class BoolType(NoType): + """ + Describes boolean data. + """ + class IntType(NumType): - pass + """ + Describes integer data. + """ + class AllType(StrType, FloatType, IntType): - pass + """ + Subclass of all other data types. -class Column(object): + This type is returned by ``convert_numpy`` if the given numpy + type does not match ``StrType``, ``FloatType``, or ``IntType``. + """ + + +class Column: """Table column. The key attributes of a Column object are: * **name** : column name - * **index** : column index (first column has index=0, second has index=1, etc) * **type** : column type (NoType, StrType, NumType, FloatType, IntType) + * **dtype** : numpy dtype (optional, overrides **type** if set) * **str_vals** : list of column values as strings + * **fill_values** : dict of fill values + * **shape** : list of element shape (default [] => scalar) * **data** : list of converted column values + * **subtype** : actual datatype for columns serialized with JSON """ - def __init__(self, name, index): + + def __init__(self, name): self.name = name - self.index = index - self.type = NoType + self.type = NoType # Generic type (Int, Float, Str etc) + self.dtype = None # Numpy dtype if available self.str_vals = [] self.fill_values = {} - self.formatter = None - - def __iter__(self): - '''iterate over formated column values - - Each value is paased through self.formatter. - If str(self.formatter(value)) is found in the fill_values specification, - the corresponding fill_value is returned, otherwise the formated value. - ''' - for val in self.data: - yield self.fill_values.get(str(self.formatter(val)).strip(), self.formatter(val)) - -class BaseInputter(object): - """Get the lines from the table input and return a list of lines. The input table can be one of: - - * File name - * String (newline separated) with all header and data lines (must have at least 2 lines) - * File-like object with read() method - * List of strings + self.shape = [] + self.subtype = None + + +class BaseInputter: """ - def get_lines(self, table): + Get the lines from the table input and return a list of lines. + + """ + + encoding = None + """Encoding used to read the file""" + + def get_lines(self, table, newline=None): """Get the lines from the ``table`` input. - - :param table: table input - :returns: list of lines + + The input table can be one of: + + * File name (str or pathlike) + * String (newline separated) with all header and data lines (must have at least 2 lines) + * File-like object with read() method + * List of strings + + Parameters + ---------- + table : str, file-like, list + Can be either a file name, string (newline separated) with all header and data + lines (must have at least 2 lines), a file-like object with a + ``read()`` method, or a list of strings. + newline : + Line separator. If `None` use OS default from ``splitlines()``. + + Returns + ------- + lines : list + List of lines """ try: - if hasattr(table, 'read'): - table = table.read() - elif '\n' not in table and '\r' not in table + '': - table = open(table, 'r').read() - lines = table.splitlines() + if ( + hasattr(table, "read") + or isinstance(table, os.PathLike) + or ("\n" not in table + "" and "\r" not in table + "") + ): + with get_readable_fileobj(table, encoding=self.encoding) as fileobj: + table = fileobj.read() + if newline is None: + lines = table.splitlines() + else: + lines = table.split(newline) except TypeError: try: # See if table supports indexing, slicing, and iteration table[0] table[0:1] iter(table) - lines = table + if len(table) > 1: + lines = table + else: + # treat single entry as if string had been passed directly + if newline is None: + lines = table[0].splitlines() + else: + lines = table[0].split(newline) + except TypeError: - raise TypeError('Input "table" must be a string (filename or data) or an iterable') + raise TypeError( + 'Input "table" must be a string (filename or data) or an iterable' + ) return self.process_lines(lines) - def process_lines(self, lines): + def process_lines(self, lines: list[str]) -> list[str]: """Process lines for subsequent use. In the default case do nothing. This routine is not generally intended for removing comment lines or stripping whitespace. These are done (if needed) in the header and @@ -175,11 +373,14 @@ def process_lines(self, lines): Override this method if something more has to be done to convert raw input lines to the table rows. For example the ContinuationLinesInputter derived class accounts for continuation - characters if a row is split into lines.""" + characters if a row is split into lines. + """ return lines -class BaseSplitter(object): - """Base splitter that uses python's split method to do the work. + +class BaseSplitter: + """ + Base splitter that uses python's split method to do the work. This does not handle quoted values. A key feature is the formulation of __call__ as a generator that returns a list of the split line values at @@ -194,17 +395,19 @@ class BaseSplitter(object): reader.header.splitter.process_val = lambda x: x.lstrip() reader.data.splitter.process_val = None - - :param delimiter: one-character string used to separate fields + """ - delimiter = None - - def process_line(self, line): + + delimiter: str | None = None + """ one-character string used to separate fields """ + + def process_line(self, line: str) -> str: """Remove whitespace at the beginning or end of line. This is especially useful for - whitespace-delimited files to prevent spurious columns at the beginning or end.""" + whitespace-delimited files to prevent spurious columns at the beginning or end. + """ return line.strip() - def process_val(self, val): + def process_val(self, val: str) -> str: """Remove whitespace at the beginning or end of value.""" return val.strip() @@ -218,13 +421,13 @@ def __call__(self, lines): else: yield vals - def join(self, vals): + def join(self, vals: list[str]) -> str: if self.delimiter is None: - delimiter = ' ' + delimiter = " " else: delimiter = self.delimiter return delimiter.join(str(x) for x in vals) - + class DefaultSplitter(BaseSplitter): """Default class to split strings into columns using python csv. The class @@ -233,107 +436,127 @@ class DefaultSplitter(BaseSplitter): Typical usage:: # lines = .. - splitter = asciitable.DefaultSplitter() + splitter = ascii.DefaultSplitter() for col_vals in splitter(lines): for col_val in col_vals: ... - :param delimiter: one-character string used to separate fields. - :param doublequote: control how instances of *quotechar* in a field are quoted - :param escapechar: character to remove special meaning from following character - :param quotechar: one-character stringto quote fields containing special characters - :param quoting: control when quotes are recognised by the reader - :param skipinitialspace: ignore whitespace immediately following the delimiter """ - delimiter = ' ' + + delimiter = " " + """ one-character string used to separate fields. """ quotechar = '"' + """ control how instances of *quotechar* in a field are quoted """ doublequote = True + """ character to remove special meaning from following character """ escapechar = None + """ one-character stringto quote fields containing special characters """ quoting = csv.QUOTE_MINIMAL + """ control when quotes are recognized by the reader """ skipinitialspace = True - + """ ignore whitespace immediately following the delimiter """ + csv_writer = None + csv_writer_out = StringIO() + def process_line(self, line): """Remove whitespace at the beginning or end of line. This is especially useful for - whitespace-delimited files to prevent spurious columns at the beginning or end. - If splitting on whitespace then replace unquoted tabs with space first""" - if self.delimiter == '\s': + whitespace-delimited files to prevent spurious columns at the beginning or end. + If splitting on whitespace then replace unquoted tabs with space first. + """ + if self.delimiter == r"\s": line = _replace_tab_with_space(line, self.escapechar, self.quotechar) - return line.strip() + return line.strip() + "\n" - def __init__(self): - self.csv_writer = None - self.csv_writer_out = io.StringIO() + def process_val(self, val: str) -> str: + """Remove whitespace at the beginning or end of value.""" + return val.strip(" \t") def __call__(self, lines): """Return an iterator over the table ``lines``, where each iterator output is a list of the split line values. - :param lines: list of table lines - :returns: iterator + Parameters + ---------- + lines : list + List of table lines + + Yields + ------ + line : list of str + Each line's split values. + """ if self.process_line: lines = [self.process_line(x) for x in lines] - if self.delimiter == '\s': - delimiter = ' ' - else: - delimiter = self.delimiter - - csv_reader = csv.reader(lines, - delimiter = delimiter, - doublequote = self.doublequote, - escapechar =self.escapechar, - quotechar = self.quotechar, - quoting = self.quoting, - skipinitialspace = self.skipinitialspace - ) + delimiter = " " if self.delimiter == r"\s" else self.delimiter + + csv_reader = csv.reader( + lines, + delimiter=delimiter, + doublequote=self.doublequote, + escapechar=self.escapechar, + quotechar=self.quotechar, + quoting=self.quoting, + skipinitialspace=self.skipinitialspace, + ) for vals in csv_reader: if self.process_val: yield [self.process_val(x) for x in vals] else: yield vals - + def join(self, vals): - if self.delimiter is None: - delimiter = ' ' - else: - delimiter = self.delimiter + delimiter = " " if self.delimiter is None else str(self.delimiter) if self.csv_writer is None: - self.csv_writer = csv.writer(self.csv_writer_out, - delimiter = self.delimiter, - doublequote = self.doublequote, - escapechar = self.escapechar, - quotechar = self.quotechar, - quoting = self.quoting, - lineterminator = '', - ) - self.csv_writer_out.seek(0) - self.csv_writer_out.truncate() - self.csv_writer.writerow(vals) - - return self.csv_writer_out.getvalue() - -def _replace_tab_with_space(line, escapechar, quotechar): - """Replace tab with space within ``line`` while respecting quoted substrings""" + self.csv_writer = CsvWriter( + delimiter=delimiter, + doublequote=self.doublequote, + escapechar=self.escapechar, + quotechar=self.quotechar, + quoting=self.quoting, + ) + if self.process_val: + vals = [self.process_val(x) for x in vals] + return self.csv_writer.writerow(vals).rstrip("\r\n") + + +def _replace_tab_with_space(line: str, escapechar: str, quotechar: str) -> str: + """Replace tabs with spaces in given string, preserving quoted substrings. + + Parameters + ---------- + line : str + String containing tabs to be replaced with spaces. + escapechar : str + Character in ``line`` used to escape special characters. + quotechar : str + Character in ``line`` indicating the start/end of a substring. + + Returns + ------- + line : str + A copy of ``line`` with tabs replaced by spaces, preserving quoted substrings. + """ newline = [] in_quote = False - lastchar = 'NONE' + lastchar = "NONE" for char in line: if char == quotechar and lastchar != escapechar: in_quote = not in_quote - if char == '\t' and not in_quote: - char = ' ' + if char == "\t" and not in_quote: + char = " " lastchar = char newline.append(char) - return ''.join(newline) + return "".join(newline) + def _get_line_index(line_or_func, lines): """Return the appropriate line index, depending on ``line_or_func`` which can be either a function, a positive or negative int, or None. """ - - if hasattr(line_or_func, '__call__'): + if callable(line_or_func): return line_or_func(lines) elif line_or_func: if line_or_func >= 0: @@ -345,111 +568,151 @@ def _get_line_index(line_or_func, lines): return line_or_func -class BaseHeader(object): - """Base table header reader - - :param auto_format: format string for auto-generating column names - :param start_line: None, int, or a function of ``lines`` that returns None or int - :param comment: regular expression for comment lines - :param splitter_class: Splitter class for splitting data lines into columns - :param names: list of names corresponding to each data column - :param include_names: list of names to include in output (default=None selects all names) - :param exclude_names: list of names to exlude from output (applied after ``include_names``) +class BaseHeader: + """ + Base table header reader. """ - auto_format = 'col%d' + + auto_format = "col{}" + """ format string for auto-generating column names """ start_line = None + """ None, int, or a function of ``lines`` that returns None or int """ comment = None - splitter_class = DefaultSplitter + """ regular expression for comment lines """ + splitter_class: ClassVar[type[BaseSplitter]] = DefaultSplitter + """ Splitter class for splitting data lines into columns """ names = None - include_names = None - exclude_names = None - write_spacer_lines = ['ASCIITABLE_WRITE_SPACER_LINE'] + """ list of names corresponding to each data column """ + write_comment = False + write_spacer_lines = ["ASCII_TABLE_WRITE_SPACER_LINE"] def __init__(self): - self.splitter = self.__class__.splitter_class() - + self.splitter = self.splitter_class() + def _set_cols_from_names(self): - # Filter full list of non-null column names with the include/exclude lists - names = set(self.names) - if self.include_names is not None: - names.intersection_update(self.include_names) - if self.exclude_names is not None: - names.difference_update(self.exclude_names) - - self.cols = [Column(name=x, index=i) for i, x in enumerate(self.names) if x in names] + self.cols = [Column(name=x) for x in self.names] + + def update_meta(self, lines, meta): + """ + Extract any table-level metadata, e.g. keywords, comments, column metadata, from + the table ``lines`` and update the OrderedDict ``meta`` in place. This base + method extracts comment lines and stores them in ``meta`` for output. + """ + if self.comment: + re_comment = re.compile(self.comment) + comment_lines = [x for x in lines if re_comment.match(x)] + else: + comment_lines = [] + comment_lines = [ + re.sub("^" + self.comment, "", x).strip() for x in comment_lines + ] + if comment_lines: + meta.setdefault("table", {})["comments"] = comment_lines def get_cols(self, lines): """Initialize the header Column objects from the table ``lines``. Based on the previously set Header attributes find or create the column names. - Sets ``self.cols`` with the list of Columns. This list only includes the actual - requested columns after filtering by the include_names and exclude_names - attributes. See ``self.names`` for the full list. + Sets ``self.cols`` with the list of Columns. - :param lines: list of table lines - :returns: None - """ + Parameters + ---------- + lines : list + List of table lines + """ start_line = _get_line_index(self.start_line, self.process_lines(lines)) if start_line is None: # No header line so auto-generate names from n_data_cols - if self.names is None: - # Get the data values from the first line of table data to determine n_data_cols - try: - first_data_vals = next(self.data.get_str_vals()) - except StopIteration: - raise InconsistentTableError('No data lines found so cannot autogenerate column names') - n_data_cols = len(first_data_vals) - self.names = [self.auto_format % i for i in range(1, n_data_cols+1)] - - elif self.names is None: - # No column names supplied so read them from header line in table. + # Get the data values from the first line of table data to determine n_data_cols + try: + first_data_vals = next(self.data.get_str_vals()) + except StopIteration: + raise InconsistentTableError( + "No data lines found so cannot autogenerate column names" + ) + n_data_cols = len(first_data_vals) + self.names = [self.auto_format.format(i) for i in range(1, n_data_cols + 1)] + + else: for i, line in enumerate(self.process_lines(lines)): if i == start_line: break - else: # No header line matching - raise ValueError('No header line found in table') + else: # No header line matching + raise ValueError("No header line found in table") self.names = next(self.splitter([line])) - + self._set_cols_from_names() def process_lines(self, lines): - """Generator to yield non-comment lines""" - if self.comment: - re_comment = re.compile(self.comment) + """Generator to yield non-blank and non-comment lines.""" + re_comment = re.compile(self.comment) if self.comment else None # Yield non-comment lines for line in lines: - if line and (not self.comment or not re_comment.match(line)): + if line.strip() and (not self.comment or not re_comment.match(line)): yield line - def write(self, lines): + def write_comments(self, lines, meta): + if self.write_comment not in (False, None): + for comment in meta.get("comments", []): + lines.append(self.write_comment + comment) + + def write(self, lines: list[str]) -> None: if self.start_line is not None: - for i, spacer_line in izip(range(self.start_line), - itertools.cycle(self.write_spacer_lines)): + for i, spacer_line in zip( + range(self.start_line), itertools.cycle(self.write_spacer_lines) + ): lines.append(spacer_line) - lines.append(self.splitter.join([x.name for x in self.cols])) + lines.append(self.splitter.join([x.info.name for x in self.cols])) @property - def colnames(self): - """Return the column names of the table""" - return tuple(col.name for col in self.cols) - - def _get_n_data_cols(self): - """Return the number of expected data columns from data splitting. - This is either explicitly set (typically for fixedwidth splitters) - or set to self.names otherwise. + def colnames(self) -> tuple[str, ...]: + """Return the column names of the table.""" + return tuple( + col.name if isinstance(col, Column) else col.info.name for col in self.cols + ) + + def remove_columns(self, names: list[str]) -> None: + """ + Remove several columns from the table. + + Parameters + ---------- + names : list + A list containing the names of the columns to remove """ - if not hasattr(self, '_n_data_cols'): - self._n_data_cols = len(self.names) - return self._n_data_cols + colnames = self.colnames + for name in names: + if name not in colnames: + raise KeyError(f"Column {name} does not exist") - def _set_n_data_cols(self, val): - """Return the number of expected data columns from data splitting. + self.cols = [col for col in self.cols if col.name not in names] + + def rename_column(self, name: str, new_name: str) -> None: + """ + Rename a column. + + Parameters + ---------- + name : str + The current name of the column. + new_name : str + The new name for the column """ - self._n_data_cols = val + try: + idx = self.colnames.index(name) + except ValueError: + raise KeyError(f"Column {name} does not exist") - n_data_cols = property(_get_n_data_cols, _set_n_data_cols) + col = self.cols[idx] + + # For writing self.cols can contain cols that are not Column. Raise + # exception in that case. + if isinstance(col, Column): + col.name = new_name + else: + raise TypeError(f"got column type {type(col)} instead of required {Column}") def get_type_map_key(self, col): return col.raw_type @@ -459,48 +722,116 @@ def get_col_type(self, col): type_map_key = self.get_type_map_key(col) return self.col_type_map[type_map_key.lower()] except KeyError: - raise ValueError('Unknown data type ""%s"" for column "%s"' % ( - col.raw_type, col.name)) - + raise ValueError( + f'Unknown data type ""{col.raw_type}"" for column "{col.name}"' + ) -class BaseData(object): - """Base table data reader. - - :param start_line: None, int, or a function of ``lines`` that returns None or int - :param end_line: None, int, or a function of ``lines`` that returns None or int - :param comment: Regular expression for comment lines - :param splitter_class: Splitter class for splitting data lines into columns + def check_column_names( + self, names: list[str], strict_names: bool, guessing: bool + ) -> None: + """ + Check column names. + + This must be done before applying the names transformation + so that guessing will fail appropriately if ``names`` is supplied. + For instance if the basic reader is given a table with no column header + row. + + Parameters + ---------- + names : list + User-supplied list of column names + strict_names : bool + Whether to impose extra requirements on names + guessing : bool + True if this method is being called while guessing the table format + """ + if strict_names: + # Impose strict requirements on column names (normally used in guessing) + bads = [" ", ",", "|", "\t", "'", '"'] + for name in self.colnames: + if ( + _is_number(name) + or len(name) == 0 + or name[0] in bads + or name[-1] in bads + ): + raise InconsistentTableError( + f"Column name {name!r} does not meet strict name requirements" + ) + # When guessing require at least two columns, except for ECSV which can + # reliably be guessed from the header requirements. + if ( + guessing + and len(self.colnames) <= 1 + and self.__class__.__name__ != "EcsvHeader" + ): + raise ValueError( + "Table format guessing requires at least two columns, " + f"got {list(self.colnames)}" + ) + + if names is not None and len(names) != len(self.colnames): + raise InconsistentTableError( + f"Length of names argument ({len(names)}) does not match number " + f"of table columns ({len(self.colnames)})" + ) + + +class BaseData: + """ + Base table data reader. """ + start_line = None + """ None, int, or a function of ``lines`` that returns None or int """ end_line = None + """ None, int, or a function of ``lines`` that returns None or int """ comment = None - splitter_class = DefaultSplitter - write_spacer_lines = ['ASCIITABLE_WRITE_SPACER_LINE'] - formats = {} - default_formatter = str - fill_values = [] + """ Regular expression for comment lines """ + splitter_class: ClassVar[type[BaseSplitter]] = DefaultSplitter + """ Splitter class for splitting data lines into columns """ + write_spacer_lines = ["ASCII_TABLE_WRITE_SPACER_LINE"] fill_include_names = None fill_exclude_names = None - + fill_values = [(masked, "")] + formats = {} + def __init__(self): - self.splitter = self.__class__.splitter_class() + # Need to make sure fill_values list is instance attribute, not class attribute. + # On read, this will be overwritten by the default in the ui.read (thus, in + # the current implementation there can be no different default for different + # Readers). On write, ui.py does not specify a default, so this line here matters. + self.fill_values = copy.copy(self.fill_values) + self.formats = copy.copy(self.formats) + self.splitter = self.splitter_class() + + def process_lines(self, lines: list[str]) -> list[str]: + """ + READ: Strip out comment lines and blank lines from list of ``lines``. - def process_lines(self, lines): - """Strip out comment lines and blank lines from list of ``lines`` + Parameters + ---------- + lines : list + All lines in table + + Returns + ------- + lines : list + List of lines - :param lines: all lines in table - :returns: list of lines """ nonblank_lines = (x for x in lines if x.strip()) if self.comment: re_comment = re.compile(self.comment) return [x for x in nonblank_lines if not re_comment.match(x)] else: - return [x for x in nonblank_lines] + return list(nonblank_lines) - def get_data_lines(self, lines): - """Set the ``data_lines`` attribute to the lines slice comprising the - table data values.""" + def get_data_lines(self, lines: list[str]) -> None: + """ + READ: Set ``data_lines`` attribute to lines slice comprising table data values. + """ data_lines = self.process_lines(lines) start_line = _get_line_index(self.start_line, data_lines) end_line = _get_line_index(self.end_line, data_lines) @@ -512,12 +843,13 @@ def get_data_lines(self, lines): def get_str_vals(self): """Return a generator that returns a list of column values (as strings) - for each data line.""" + for each data line. + """ return self.splitter(self.data_lines) def masks(self, cols): - """Set fill value for each column and then apply that fill value - + """READ: Set fill value for each column and then apply that fill value. + In the first step it is evaluated with value from ``fill_values`` applies to which column using ``fill_include_names`` and ``fill_exclude_names``. In the second step all replacements are done for the appropriate columns. @@ -525,9 +857,9 @@ def masks(self, cols): if self.fill_values: self._set_fill_values(cols) self._set_masks(cols) - + def _set_fill_values(self, cols): - """Set the fill values of the individual cols based on fill_values of BaseData + """READ, WRITE: Set fill values of individual cols based on fill_values of BaseData. fill values has the following form: = (, , ...) @@ -535,259 +867,457 @@ def _set_fill_values(self, cols): """ if self.fill_values: - #if input is only one , then make it a list - try: - self.fill_values[0] + '' - self.fill_values = [ self.fill_values ] - except TypeError: - pass - # Step 1: Set the default list of columns which are affected by fill_values + # when we write tables the columns may be astropy.table.Columns + # which don't carry a fill_values by default + for col in cols: + if not hasattr(col, "fill_values"): + col.fill_values = {} + + # if input is only one , then make it a list + with suppress(TypeError): + self.fill_values[0] + "" + self.fill_values = [self.fill_values] + + # Step 1: Set the default list of columns which are affected by + # fill_values colnames = set(self.header.colnames) if self.fill_include_names is not None: colnames.intersection_update(self.fill_include_names) if self.fill_exclude_names is not None: colnames.difference_update(self.fill_exclude_names) - + # Step 2a: Find out which columns are affected by this tuple - # iterate over reversed order, so last condition is set first and overwritten by earlier conditions + # iterate over reversed order, so last condition is set first and + # overwritten by earlier conditions for replacement in reversed(self.fill_values): if len(replacement) < 2: - raise ValueError("Format of fill_values must be (, , , ...)") + raise ValueError( + "Format of fill_values must be " + "(, , , ...)" + ) elif len(replacement) == 2: affect_cols = colnames else: affect_cols = replacement[2:] - - for i, key in ((i, x) for i, x in enumerate(self.header.colnames) if x in affect_cols): + + for i, key in ( + (i, x) + for i, x in enumerate(self.header.colnames) + if x in affect_cols + ): cols[i].fill_values[replacement[0]] = str(replacement[1]) def _set_masks(self, cols): - """Replace string values in col.str_vales and set masks""" + """READ: Replace string values in col.str_vals and set masks.""" if self.fill_values: for col in (col for col in cols if col.fill_values): - col.mask = [False] * len(col.str_vals) - for i, str_val in ((i, x) for i, x in enumerate(col.str_vals) if x in col.fill_values): + col.mask = np.zeros(len(col.str_vals), dtype=bool) + for i, str_val in ( + (i, x) for i, x in enumerate(col.str_vals) if x in col.fill_values + ): col.str_vals[i] = col.fill_values[str_val] col.mask[i] = True + def _replace_vals(self, cols): + """WRITE: replace string values in col.str_vals.""" + if self.fill_values: + for col in (col for col in cols if col.fill_values): + for i, str_val in ( + (i, x) for i, x in enumerate(col.str_vals) if x in col.fill_values + ): + col.str_vals[i] = col.fill_values[str_val] + if masked in col.fill_values and hasattr(col, "mask"): + mask_val = col.fill_values[masked] + for i in col.mask.nonzero()[0]: + col.str_vals[i] = mask_val + + def str_vals(self): + """WRITE: convert all values in table to a list of lists of strings. + + This sets the fill values and possibly column formats from the input + formats={} keyword, then ends up calling table.pprint._pformat_col_iter() + by a circuitous path. That function does the real work of formatting. + Finally replace anything matching the fill_values. + + Returns + ------- + values : list of list of str + """ + self._set_fill_values(self.cols) + self._set_col_formats() + for col in self.cols: + col.str_vals = list(col.info.iter_str_vals()) + self._replace_vals(self.cols) + return [col.str_vals for col in self.cols] + def write(self, lines): - if hasattr(self.start_line, '__call__'): - raise TypeError('Start_line attribute cannot be callable for write()') + """Write ``self.cols`` in place to ``lines``. + + Parameters + ---------- + lines : list + List for collecting output of writing self.cols. + """ + if callable(self.start_line): + raise TypeError("Start_line attribute cannot be callable for write()") else: data_start_line = self.start_line or 0 while len(lines) < data_start_line: lines.append(itertools.cycle(self.write_spacer_lines)) - formatters = [] - for col in self.cols: - formatter = self.formats.get(col.name, self.default_formatter) - if not hasattr(formatter, '__call__'): - formatter = _format_func(formatter) - col.formatter = formatter - - for vals in izip(*self.cols): + col_str_iters = self.str_vals() + for vals in zip(*col_str_iters): lines.append(self.splitter.join(vals)) + def _set_col_formats(self): + """WRITE: set column formats.""" + for col in self.cols: + if col.info.name in self.formats: + col.info.format = self.formats[col.info.name] -def _format_func(format_str): - def func(val): - return format_str % val - return func - - -class DictLikeNumpy(dict): - """Provide minimal compatibility with numpy rec array API for BaseOutputter - object:: - - table = asciitable.read('mytable.dat', numpy=False) - table.field('x') # List of elements in column 'x' - table.dtype.names # get column names in order - table[1] # returns row 1 as a list - table[1][2] # 3nd column in row 1 - table['col1'][1] # Row 1 in column col1 - for row_vals in table: # iterate over table rows - print row_vals # print list of vals in each row +def convert_numpy(numpy_type): + """Return a tuple containing a function which converts a list into a numpy + array and the type produced by the converter function. + + Parameters + ---------- + numpy_type : numpy data-type + The numpy type required of an array returned by ``converter``. Must be a + valid `numpy type `_ + (e.g., numpy.uint, numpy.int8, numpy.int64, numpy.float64) or a python + type covered by a numpy type (e.g., int, float, str, bool). + + Returns + ------- + converter : callable + ``converter`` is a function which accepts a list and converts it to a + numpy array of type ``numpy_type``. + converter_type : type + ``converter_type`` tracks the generic data type produced by the + converter function. + + Raises + ------ + ValueError + Raised by ``converter`` if the list elements could not be converted to + the required type. """ - # To do: - add colnames property to set colnames and dtype.names as well. - # - ordered dict? - - class Dtype(object): - pass - - def __init__(self, *args, **kwargs): - self.dtype = DictLikeNumpy.Dtype() - dict.__init__(self, *args, **kwargs) - - def __getitem__(self, item): - try: - return dict.__getitem__(self, item + '') - except TypeError: - return [dict.__getitem__(self, x)[item] for x in self.dtype.names] - - def field(self, colname): - return self[colname] + # Infer converter type from an instance of numpy_type. + type_name = np.array([], dtype=numpy_type).dtype.name + if "int" in type_name: + converter_type = IntType + elif "float" in type_name: + converter_type = FloatType + elif "bool" in type_name: + converter_type = BoolType + elif "str" in type_name: + converter_type = StrType + else: + converter_type = AllType - def __len__(self): - return len(list(self.values())[0]) + def bool_converter(vals): + """ + Convert values "False" and "True" to bools. Raise an exception + for any other string values. + """ + if len(vals) == 0: + return np.array([], dtype=bool) - def __iter__(self): - self.__index = 0 - return self + # Try a smaller subset first for a long array + if len(vals) > 10000: + svals = np.asarray(vals[:1000]) + if not np.all( + (svals == "False") | (svals == "True") | (svals == "0") | (svals == "1") + ): + raise ValueError('bool input strings must be False, True, 0, 1, or ""') + vals = np.asarray(vals) - def __next__(self): - try: - vals = self[self.__index] - except IndexError: - raise StopIteration - else: - self.__index += 1 - return vals + trues = (vals == "True") | (vals == "1") + falses = (vals == "False") | (vals == "0") + if not np.all(trues | falses): + raise ValueError('bool input strings must be only False, True, 0, 1, or ""') - if sys.version_info[0] < 3: # pragma: py2 - next = __next__ + return trues + def generic_converter(vals): + return np.array(vals, numpy_type) -def convert_numpy(numpy_type): - """Return a tuple ``(converter_func, converter_type)``. The converter - function converts a list into a numpy array of the given ``numpy_type``. - This type must be a valid `numpy type - `_, e.g. - numpy.int, numpy.uint, numpy.int8, numpy.int64, numpy.float, numpy.float64, - numpy.str. The converter type is used to track the generic data type (int, - float, str) that is produced by the converter function. - """ + converter = bool_converter if converter_type is BoolType else generic_converter - # Infer converter type from an instance of numpy_type. - type_name = numpy.array([], dtype=numpy_type).dtype.name - if 'int' in type_name: - converter_type = IntType - elif 'float' in type_name: - converter_type = FloatType - elif 'str' in type_name: - converter_type = StrType - else: - converter_type = AllType - - def converter(vals): - return numpy.array(vals, numpy_type) return converter, converter_type -class BaseOutputter(object): + +class BaseOutputter: """Output table as a dict of column objects keyed on column name. The table data are stored as plain python lists within the column objects. """ + + # User-defined converters which gets set in ascii.ui if a `converter` kwarg + # is supplied. converters = {} + # Derived classes must define default_converters and __call__ @staticmethod def _validate_and_copy(col, converters): """Validate the format for the type converters and then copy those which are valid converters for this column (i.e. converter type is - a subclass of col.type)""" + a subclass of col.type). + """ + # Allow specifying a single converter instead of a list of converters. + # The input `converters` must be a ``type`` value that can init np.dtype. + if type(converters) is type: + try: + # Don't allow list-like things that dtype accepts + converters = [np.dtype(converters)] + except TypeError: + pass + converters_out = [] try: for converter in converters: - converter_func, converter_type = converter + try: + converter_func, converter_type = converter + except TypeError as err: + if str(err).startswith("cannot unpack"): + converter_func, converter_type = convert_numpy(converter) + else: + raise if not issubclass(converter_type, NoType): - raise ValueError() + raise ValueError("converter_type must be a subclass of NoType") if issubclass(converter_type, col.type): converters_out.append((converter_func, converter_type)) - - except (ValueError, TypeError): - raise ValueError('Error: invalid format for converters, see documentation\n%s' % - converters) + + except (ValueError, TypeError) as err: + raise ValueError( + "Error: invalid format for converters, see " + f"documentation\n{converters}: {err}" + ) return converters_out def _convert_vals(self, cols): for col in cols: - converters = self.converters.get(col.name, - self.default_converters) + for key, converters in self.converters.items(): + if fnmatch.fnmatch(col.name, key): + break + else: + if col.dtype is not None: + converters = [convert_numpy(col.dtype)] + else: + converters = self.default_converters + col.converters = self._validate_and_copy(col, converters) - while not hasattr(col, 'data'): + # Catch the last error in order to provide additional information + # in case all attempts at column conversion fail. The initial + # value of of last_error will apply if no converters are defined + # and the first col.converters[0] access raises IndexError. + last_err = "no converters defined" + + while not hasattr(col, "data"): + # Try converters, popping the unsuccessful ones from the list. + # If there are no converters left here then fail. + if not col.converters: + raise ValueError(f"Column {col.name} failed to convert: {last_err}") + + converter_func, converter_type = col.converters[0] + if not issubclass(converter_type, col.type): + raise TypeError( + f"converter type {converter_type.__name__} does not match" + f" column type {col.type.__name__} for column {col.name}" + ) + try: - converter_func, converter_type = col.converters[0] - if not issubclass(converter_type, col.type): - raise TypeError() col.data = converter_func(col.str_vals) col.type = converter_type - except (TypeError, ValueError): - col.converters.pop(0) - except IndexError: - raise ValueError('Column %s failed to convert' % col.name) - -class NumpyOutputter(BaseOutputter): - """Output the table as a numpy.rec.recarray - - Missing or bad data values are handled at two levels. The first is in - the data reading step where if ``data.fill_values`` is set then any - occurences of a bad value are replaced by the correspond fill value. - At the same time a boolean list ``mask`` is created in the column object. - - The second stage is when converting to numpy arrays which by default generates - masked arrays, if ``data.fill_values`` is set and plain arrays if it is not. - In the rare case that plain arrays are needed set ``auto_masked`` (default = True) and - ``default_masked`` (default = False) to control this behavior as follows: - - =========== ============== =========== ============ - auto_masked default_masked fill_values output - =========== ============== =========== ============ - -- True -- masked_array - -- False None array - True -- dict(..) masked_array - False -- dict(..) array - =========== ============== =========== ============ - - To set these values use:: - - Outputter = asciitable.NumpyOutputter() - Outputter.default_masked = True - + except (OverflowError, TypeError, ValueError) as err: + # Overflow during conversion (most likely an int that + # doesn't fit in native C long). Put string at the top of + # the converters list for the next while iteration. + # With python/cpython#95778 this has been supplemented with a + # "ValueError: Exceeds the limit (4300) for integer string conversion" + # so need to catch that as well. + if isinstance(err, OverflowError) or ( + isinstance(err, ValueError) + and str(err).startswith("Exceeds the limit") + ): + warnings.warn( + f"OverflowError converting to {converter_type.__name__} in" + f" column {col.name}, reverting to String.", + AstropyWarning, + ) + col.converters.insert(0, convert_numpy(str)) + else: + col.converters.pop(0) + last_err = err + + +def _deduplicate_names(names: list[str]) -> list[str]: + """Ensure there are no duplicates in ``names``. + + This is done by iteratively adding ``_`` to the name for increasing N + until the name is unique. """ + new_names = [] + existing_names = set() + + for name in names: + base_name = name + "_" + i = 1 + while name in existing_names: + # Iterate until a unique name is found + name = base_name + str(i) + i += 1 + new_names.append(name) + existing_names.add(name) - auto_masked_array = True - default_masked_array = False + return new_names - default_converters = [convert_numpy(numpy.int), - convert_numpy(numpy.float), - convert_numpy(numpy.str)] - def __call__(self, cols): +class TableOutputter(BaseOutputter): + """ + Output the table as an astropy.table.Table object. + """ + + default_converters = [ + # Use `np.int64` to ensure large integers can be read as ints + # on platforms such as Windows + # https://github.com/astropy/astropy/issues/5744 + convert_numpy(np.int64), + convert_numpy(float), + convert_numpy(str), + ] + + def __call__(self, cols, meta): + # Sets col.data to numpy array and col.type to io.ascii Type class (e.g. + # FloatType) for each col. self._convert_vals(cols) - recarr = numpy.rec.fromarrays([x.data for x in cols], names=[x.name for x in cols]) - if self.default_masked_array or (self.auto_masked_array and - any(col.fill_values for col in cols)): - maarr = recarr.view(numpy.ma.MaskedArray) - for col in cols: - if col.fill_values: - maarr[col.name] = numpy.ma.masked_where(col.mask, maarr[col.name]) - return maarr - else: - return recarr + t_cols = [ + np.ma.MaskedArray(x.data, mask=x.mask) + if hasattr(x, "mask") and np.any(x.mask) + else x.data + for x in cols + ] + out = Table(t_cols, names=[x.name for x in cols], meta=meta["table"]) + + for col, out_col in zip(cols, out.columns.values()): + for attr in ("format", "unit", "description"): + if hasattr(col, attr): + setattr(out_col, attr, getattr(col, attr)) + if hasattr(col, "meta"): + out_col.meta.update(col.meta) -class TableOutputter(BaseOutputter): - """Output the table as an astropy.table.Table object. + return out + + +class MetaBaseReader(type): + def __init__(cls, name, bases, dct): + super().__init__(name, bases, dct) + + format = dct.get("_format_name") + if format is None: + return + + fast = dct.get("_fast") + if fast is not None: + FAST_CLASSES[format] = cls + + FORMAT_CLASSES[format] = cls + + io_formats = ["ascii." + format] + dct.get("_io_registry_format_aliases", []) + + if dct.get("_io_registry_suffix"): + func = functools.partial(connect.io_identify, dct["_io_registry_suffix"]) + connect.io_registry.register_identifier(io_formats[0], Table, func) + + for io_format in io_formats: + func = functools.partial(connect.io_read, io_format) + header = f"ASCII reader '{io_format}' details\n" + func.__doc__ = ( + inspect.cleandoc(READ_DOCSTRING).strip() + + "\n\n" + + header + + re.sub(".", "=", header) + + "\n" + ) + # NOTE: cls.__doc__ is None for -OO flag + func.__doc__ += inspect.cleandoc(cls.__doc__ or "").strip() + connect.io_registry.register_reader(io_format, Table, func) + + if dct.get("_io_registry_can_write", True): + func = functools.partial(connect.io_write, io_format) + header = f"ASCII writer '{io_format}' details\n" + func.__doc__ = ( + inspect.cleandoc(WRITE_DOCSTRING).strip() + + "\n\n" + + header + + re.sub(".", "=", header) + + "\n" + ) + func.__doc__ += inspect.cleandoc(cls.__doc__ or "").strip() + connect.io_registry.register_writer(io_format, Table, func) + + +def _is_number(x) -> TypeGuard[SupportsFloat]: + with suppress(ValueError): + x = float(x) + return True + return False + + +def _apply_include_exclude_names(table, names, include_names, exclude_names): + """ + Apply names, include_names and exclude_names to a table or BaseHeader. + + For the latter this relies on BaseHeader implementing ``colnames``, + ``rename_column``, and ``remove_columns``. + + Parameters + ---------- + table : `~astropy.table.Table`, `~astropy.io.ascii.BaseHeader` + Input table or BaseHeader subclass instance + names : list + List of names to override those in table (set to None to use existing names) + include_names : list + List of names to include in output + exclude_names : list + List of names to exclude from output (applied after ``include_names``) - Missing or bad data values are not presently handled and raise an - exception. This will be changed, but in the meantime use the - NumpyOutputter. """ - default_converters = [convert_numpy(numpy.int), - convert_numpy(numpy.float), - convert_numpy(numpy.str)] + def rename_columns(table, names): + # Rename table column names to those passed by user + # Temporarily rename with names that are not in `names` or `table.colnames`. + # This ensures that rename succeeds regardless of existing names. + xxxs = "x" * max(len(name) for name in list(names) + list(table.colnames)) + for ii, colname in enumerate(table.colnames): + table.rename_column(colname, xxxs + str(ii)) - def __call__(self, cols): - self._convert_vals(cols) - out = Table([x.data for x in cols], names=[x.name for x in cols]) - # To Do: add support for column and table metadata - return out + for ii, name in enumerate(names): + table.rename_column(xxxs + str(ii), name) + + if names is not None: + rename_columns(table, names) + else: + colnames_uniq = _deduplicate_names(table.colnames) + if colnames_uniq != list(table.colnames): + rename_columns(table, colnames_uniq) + names_set = set(table.colnames) -class BaseReader(object): + if include_names is not None: + names_set.intersection_update(include_names) + if exclude_names is not None: + names_set.difference_update(exclude_names) + if names_set != set(table.colnames): + remove_names = set(table.colnames) - names_set + table.remove_columns(remove_names) + + +class BaseReader(metaclass=MetaBaseReader): """Class providing methods to read and write an ASCII table using the specified header, data, inputter, and outputter instances. @@ -800,19 +1330,58 @@ class BaseReader(object): The default behavior is to raise an InconsistentTableError. """ + + names = None + include_names = None + exclude_names = None + strict_names = False + guessing = False + encoding = None + + header_class = BaseHeader + data_class = BaseData + inputter_class = BaseInputter + outputter_class = TableOutputter + + # Max column dimension that writer supports for this format. Exceptions + # include ECSV (no limit) and HTML (max_ndim=2). + max_ndim: ClassVar[int | None] = 1 + def __init__(self): - self.header = BaseHeader() - self.data = BaseData() - self.inputter = BaseInputter() - self.outputter = TableOutputter() - self.meta = {} # Placeholder for storing table metadata - self.keywords = [] # Placeholder for storing table Keywords + self.header = self.header_class() + self.data = self.data_class() + self.inputter = self.inputter_class() + self.outputter = self.outputter_class() # Data and Header instances benefit from a little cross-coupling. Header may need to # know about number of data columns for auto-column name generation and Data may # need to know about header (e.g. for fixed-width tables where widths are spec'd in header. self.data.header = self.header self.header.data = self.data + # Metadata, consisting of table-level meta and column-level meta. The latter + # could include information about column type, description, formatting, etc, + # depending on the table meta format. + self.meta = {"table": {}, "cols": {}} + + def _check_multidim_table(self, table: Table) -> None: + """Check that the dimensions of columns in ``table`` are acceptable. + + The reader class attribute ``max_ndim`` defines the maximum dimension of + columns that can be written using this format. The base value is ``1``, + corresponding to normal scalar columns with just a length. + + Parameters + ---------- + table : `~astropy.table.Table` + Input table. + + Raises + ------ + ValueError + If any column exceeds the number of allowed dimensions + """ + _check_multidim_table(table, self.max_ndim) + def read(self, table): """Read the ``table`` and return the results in a format determined by the ``outputter`` attribute. @@ -822,84 +1391,127 @@ def read(self, table): one of: * File name + * File-like object * String (newline separated) with all header and data lines (must have at least 2 lines) * List of strings - :param table: table input - :returns: output table + Parameters + ---------- + table : str, file-like, list + Input table. + + Returns + ------- + table : `~astropy.table.Table` + Output table + """ # If ``table`` is a file then store the name in the ``data`` # attribute. The ``table`` is a "file" if it is a string # without the new line specific to the OS. - try: - if os.linesep not in table + '': - self.data.table_name = os.path.basename(table) - except TypeError: - # Not a string. - pass - - # Same from __init__. ??? Do these need to be here? - self.data.header = self.header - self.header.data = self.data + with suppress(TypeError): + # Strings only + if os.linesep not in table + "": + self.data.table_name = Path(table).name + + # If one of the newline chars is set as field delimiter, only + # accept the other one as line splitter + if self.header.splitter.delimiter == "\n": + newline = "\r" + elif self.header.splitter.delimiter == "\r": + newline = "\n" + else: + newline = None + + # Get a list of the lines (rows) in the table + self.lines = self.inputter.get_lines(table, newline=newline) - self.lines = self.inputter.get_lines(table) + # Set self.data.data_lines to a slice of lines contain the data rows self.data.get_data_lines(self.lines) + + # Extract table meta values (e.g. keywords, comments, etc). Updates self.meta. + self.header.update_meta(self.lines, self.meta) + + # Get the table column definitions self.header.get_cols(self.lines) - cols = self.header.cols # header.cols corresponds to *output* columns requested - n_data_cols = self.header.n_data_cols # number of data cols expected from splitter + + # Make sure columns are valid + self.header.check_column_names(self.names, self.strict_names, self.guessing) + + self.cols = cols = self.header.cols self.data.splitter.cols = cols + n_cols = len(cols) for i, str_vals in enumerate(self.data.get_str_vals()): - if len(str_vals) != n_data_cols: - str_vals = self.inconsistent_handler(str_vals, n_data_cols) - - #if str_vals is None, we skip this row + if len(str_vals) != n_cols: + str_vals = self.inconsistent_handler(str_vals, n_cols) + + # if str_vals is None, we skip this row if str_vals is None: continue - - #otherwise, we raise an error only if it is still inconsistent - if len(str_vals) != n_data_cols: - errmsg = ('Number of header columns (%d) inconsistent with ' - 'data columns (%d) at data line %d\n' - 'Header values: %s\n' - 'Data values: %s' % (len(cols), len(str_vals), i, - [x.name for x in cols], str_vals)) + + # otherwise, we raise an error only if it is still inconsistent + if len(str_vals) != n_cols: + errmsg = ( + f"Number of header columns ({n_cols}) inconsistent with " + f"data columns ({len(str_vals)}) at data line {i}\n" + f"Header values: {[x.name for x in cols]}\n" + f"Data values: {str_vals}" + ) + raise InconsistentTableError(errmsg) - for col in cols: - col.str_vals.append(str_vals[col.index]) + for j, col in enumerate(cols): + col.str_vals.append(str_vals[j]) + + if hasattr(self.header, "table_meta"): + self.meta["table"].update(self.header.table_meta) + _apply_include_exclude_names( + self.header, self.names, self.include_names, self.exclude_names + ) self.data.masks(cols) - self.table = self.outputter(cols) + + table = self.outputter(self.header.cols, self.meta) self.cols = self.header.cols - return self.table - - def inconsistent_handler(self, str_vals, ncols): - """Adjust or skip data entries if a row is inconsistent with the header. - + return table + + def inconsistent_handler(self, str_vals: list[str], ncols: int) -> list[str]: + """ + Adjust or skip data entries if a row is inconsistent with the header. + The default implementation does no adjustment, and hence will always trigger - an exception in read() any time the number of data entries does not match + an exception in read() any time the number of data entries does not match the header. - + Note that this will *not* be called if the row already matches the header. - :param str_vals: A list of value strings from the current row of the table. - :param ncols: The expected number of entries from the table header. - :returns: - list of strings to be parsed into data entries in the output table. If + Parameters + ---------- + str_vals : list + A list of value strings from the current row of the table. + ncols : int + The expected number of entries from the table header. + + Returns + ------- + str_vals : list + List of strings to be parsed into data entries in the output table. If the length of this list does not match ``ncols``, an exception will be raised in read(). Can also be None, in which case the row will be skipped. """ - #an empty list will always trigger an InconsistentTableError in read() + # an empty list will always trigger an InconsistentTableError in read() return str_vals @property - def comment_lines(self): - """Return lines in the table that match header.comment regexp""" - if not hasattr(self, 'lines'): - raise ValueError('Table must be read prior to accessing the header_comment_lines') + def comment_lines(self) -> list[str]: + """Return lines in the table that match header.comment regexp.""" + if not hasattr(self, "lines"): + raise ValueError( + "Table must be read prior to accessing the header comment lines" + ) if self.header.comment: re_comment = re.compile(self.header.comment) comment_lines = [x for x in self.lines if re_comment.match(x)] @@ -907,30 +1519,84 @@ def comment_lines(self): comment_lines = [] return comment_lines - def write(self, table=None): - """Write ``table`` as list of strings. + def update_table_data(self, table): + """ + Update table columns in place if needed. + + This is a hook to allow updating the table columns after name + filtering but before setting up to write the data. This is currently + only used by ECSV and is otherwise just a pass-through. + + Parameters + ---------- + table : `astropy.table.Table` + Input table for writing - :param table: asciitable Reader object - :returns: list of strings corresponding to ASCII table + Returns + ------- + table : `astropy.table.Table` + Output table for writing """ - if table is None: - table = self + return table + def write_header(self, lines, meta): + self.header.write_comments(lines, meta) + self.header.write(lines) + + def write(self, table: Table) -> list[str]: + """ + Write ``table`` as list of strings. + + Parameters + ---------- + table : `~astropy.table.Table` + Input table data. + + Returns + ------- + lines : list + List of strings corresponding to ASCII table + + """ + # Check column names before altering + self.header.cols = list(table.columns.values()) + self.header.check_column_names(self.names, self.strict_names, False) + + # In-place update of columns in input ``table`` to reflect column + # filtering. Note that ``table`` is guaranteed to be a copy of the + # original user-supplied table. + _apply_include_exclude_names( + table, self.names, self.include_names, self.exclude_names + ) + + # This is a hook to allow updating the table columns after name + # filtering but before setting up to write the data. This is currently + # only used by ECSV and is otherwise just a pass-through. + table = self.update_table_data(table) + + # Check that table column dimensions are supported by this format class. + # Most formats support only 1-d columns, but some like ECSV support N-d. + self._check_multidim_table(table) + + # Now use altered columns + new_cols = list(table.columns.values()) # link information about the columns to the writer object (i.e. self) - self.header.cols = table.cols - self.data.cols = self.header.cols - self.data.masks(self.data.cols) + self.header.cols = new_cols + self.data.cols = new_cols + self.header.table_meta = table.meta - # Write header and data to lines list - lines = [] - self.header.write(lines) + # Write header and data to lines list + lines: list[str] = [] + self.write_header(lines, table.meta) self.data.write(lines) return lines + class ContinuationLinesInputter(BaseInputter): - """Inputter where lines ending in ``continuation_char`` are joined - with the subsequent line. Example:: + """Inputter where lines ending in ``continuation_char`` are joined with the subsequent line. + + Example:: col1 col2 col3 1 \ @@ -939,137 +1605,257 @@ class ContinuationLinesInputter(BaseInputter): 6 """ - continuation_char = '\\' + continuation_char = "\\" + replace_char = " " + # If no_continue is not None then lines matching this regex are not subject + # to line continuation. The initial use case here is Daophot. In this + # case the continuation character is just replaced with replace_char. + no_continue = None def process_lines(self, lines): - striplines = (x.strip() for x in lines) - lines = [x for x in striplines if len(x) > 0] + re_no_continue = re.compile(self.no_continue) if self.no_continue else None parts = [] outlines = [] for line in lines: + if re_no_continue and re_no_continue.match(line): + line = line.replace(self.continuation_char, self.replace_char) if line.endswith(self.continuation_char): - parts.append(line.rstrip(self.continuation_char)) + parts.append(line.replace(self.continuation_char, self.replace_char)) else: parts.append(line) - outlines.append(''.join(parts)) + outlines.append("".join(parts)) parts = [] return outlines class WhitespaceSplitter(DefaultSplitter): - def process_line(self, line): - """Replace tab with space within ``line`` while respecting quoted substrings""" + def process_line(self, line: str) -> str: + """Replace tab with space within ``line`` while respecting quoted substrings.""" newline = [] in_quote = False lastchar = None for char in line: - if char == self.quotechar and (self.escapechar is None or - lastchar != self.escapechar): + if char == self.quotechar and ( + self.escapechar is None or lastchar != self.escapechar + ): in_quote = not in_quote - if char == '\t' and not in_quote: - char = ' ' + if char == "\t" and not in_quote: + char = " " lastchar = char newline.append(char) - return ''.join(newline) - -extra_reader_pars = ('Reader', 'Inputter', 'Outputter', - 'delimiter', 'comment', 'quotechar', 'header_start', - 'data_start', 'data_end', 'converters', - 'data_Splitter', 'header_Splitter', - 'names', 'include_names', 'exclude_names', - 'fill_values', 'fill_include_names', 'fill_exclude_names') - -def _get_reader(Reader, Inputter=None, Outputter=None, **kwargs): + return "".join(newline) + + +extra_reader_pars = ( + "delimiter", + "comment", + "quotechar", + "header_start", + "data_start", + "data_end", + "converters", + "encoding", + "data_splitter_cls", + "header_splitter_cls", + "names", + "include_names", + "exclude_names", + "strict_names", + "fill_values", + "fill_include_names", + "fill_exclude_names", +) + + +def _get_reader(reader_cls, inputter_cls=None, outputter_cls=None, **kwargs): """Initialize a table reader allowing for common customizations. See ui.get_reader() for param docs. This routine is for internal (package) use only and is useful because it depends only on the "core" module. """ - - reader_kwargs = dict([k, v] for k, v in kwargs.items() if k not in extra_reader_pars) - reader = Reader(**reader_kwargs) - - if Inputter is not None: - reader.inputter = Inputter() - reader.outputter = TableOutputter() - - if Outputter is not None: - reader.outputter = Outputter() - - if 'delimiter' in kwargs: - reader.header.splitter.delimiter = kwargs['delimiter'] - reader.data.splitter.delimiter = kwargs['delimiter'] - if 'comment' in kwargs: - reader.header.comment = kwargs['comment'] - reader.data.comment = kwargs['comment'] - if 'quotechar' in kwargs: - reader.header.splitter.quotechar = kwargs['quotechar'] - reader.data.splitter.quotechar = kwargs['quotechar'] - if 'data_start' in kwargs: - reader.data.start_line = kwargs['data_start'] - if 'data_end' in kwargs: - reader.data.end_line = kwargs['data_end'] - if 'header_start' in kwargs: - reader.header.start_line = kwargs['header_start'] - if 'converters' in kwargs: - reader.outputter.converters = kwargs['converters'] - if 'data_Splitter' in kwargs: - reader.data.splitter = kwargs['data_Splitter']() - if 'header_Splitter' in kwargs: - reader.header.splitter = kwargs['header_Splitter']() - if 'names' in kwargs: - reader.header.names = kwargs['names'] - if 'include_names' in kwargs: - reader.header.include_names = kwargs['include_names'] - if 'exclude_names' in kwargs: - reader.header.exclude_names = kwargs['exclude_names'] - if 'fill_values' in kwargs: - reader.data.fill_values = kwargs['fill_values'] - if 'fill_include_names' in kwargs: - reader.data.fill_include_names = kwargs['fill_include_names'] - if 'fill_exclude_names' in kwargs: - reader.data.fill_exclude_names = kwargs['fill_exclude_names'] + from .fastbasic import FastBasic + + if issubclass(reader_cls, FastBasic): # Fast readers handle args separately + if inputter_cls is not None: + kwargs["inputter_cls"] = inputter_cls + return reader_cls(**kwargs) + + # If user explicitly passed a fast reader with enable='force' + # (e.g. by passing non-default options), raise an error for slow readers + if "fast_reader" in kwargs: + if kwargs["fast_reader"]["enable"] == "force": + raise ParameterError( + "fast_reader required with " + "{}, but this is not a fast C reader: {}".format( + kwargs["fast_reader"], reader_cls + ) + ) + else: + del kwargs["fast_reader"] # Otherwise ignore fast_reader parameter + + reader_kwargs = {k: v for k, v in kwargs.items() if k not in extra_reader_pars} + reader = reader_cls(**reader_kwargs) + + if inputter_cls is not None: + reader.inputter = inputter_cls() + + if outputter_cls is not None: + reader.outputter = outputter_cls() + + # Issue #855 suggested to set data_start to header_start + default_header_length + # Thus, we need to retrieve this from the class definition before resetting these numbers. + try: + default_header_length = reader.data.start_line - reader.header.start_line + except TypeError: # Start line could be None or an instancemethod + default_header_length = None + + # csv.reader is hard-coded to recognise either '\r' or '\n' as end-of-line, + # therefore DefaultSplitter cannot handle these as delimiters. + if "delimiter" in kwargs: + if kwargs["delimiter"] in ("\n", "\r", "\r\n"): + reader.header.splitter = BaseSplitter() + reader.data.splitter = BaseSplitter() + reader.header.splitter.delimiter = kwargs["delimiter"] + reader.data.splitter.delimiter = kwargs["delimiter"] + if "comment" in kwargs: + reader.header.comment = kwargs["comment"] + reader.data.comment = kwargs["comment"] + if "quotechar" in kwargs: + reader.header.splitter.quotechar = kwargs["quotechar"] + reader.data.splitter.quotechar = kwargs["quotechar"] + if "data_start" in kwargs: + reader.data.start_line = kwargs["data_start"] + if "data_end" in kwargs: + reader.data.end_line = kwargs["data_end"] + if "header_start" in kwargs: + if reader.header.start_line is not None: + reader.header.start_line = kwargs["header_start"] + # For FixedWidthTwoLine the data_start is calculated relative to the position line. + # However, position_line is given as absolute number and not relative to header_start. + # So, ignore this Reader here. + if ( + ("data_start" not in kwargs) + and (default_header_length is not None) + and reader._format_name + not in ["fixed_width_two_line", "commented_header"] + ): + reader.data.start_line = ( + reader.header.start_line + default_header_length + ) + elif kwargs["header_start"] is not None: + # User trying to set a None header start to some value other than None + raise ValueError("header_start cannot be modified for this Reader") + if "converters" in kwargs: + reader.outputter.converters = kwargs["converters"] + if "data_splitter_cls" in kwargs: + reader.data.splitter = kwargs["data_splitter_cls"]() + if "header_splitter_cls" in kwargs: + reader.header.splitter = kwargs["header_splitter_cls"]() + if "names" in kwargs: + reader.names = kwargs["names"] + if None in reader.names: + raise TypeError("Cannot have None for column name") + if len(set(reader.names)) != len(reader.names): + raise ValueError("Duplicate column names") + if "include_names" in kwargs: + reader.include_names = kwargs["include_names"] + if "exclude_names" in kwargs: + reader.exclude_names = kwargs["exclude_names"] + # Strict names is normally set only within the guessing process to + # indicate that column names cannot be numeric or have certain + # characters at the beginning or end. It gets used in + # BaseHeader.check_column_names(). + if "strict_names" in kwargs: + reader.strict_names = kwargs["strict_names"] + if "fill_values" in kwargs: + reader.data.fill_values = kwargs["fill_values"] + if "fill_include_names" in kwargs: + reader.data.fill_include_names = kwargs["fill_include_names"] + if "fill_exclude_names" in kwargs: + reader.data.fill_exclude_names = kwargs["fill_exclude_names"] + if "encoding" in kwargs: + reader.encoding = kwargs["encoding"] + reader.inputter.encoding = kwargs["encoding"] return reader -extra_writer_pars = ('delimiter', 'comment', 'quotechar', 'formats', - 'names', 'include_names', 'exclude_names', - 'fill_values', 'fill_include_names', - 'fill_exclude_names') -def _get_writer(Writer, **kwargs): +extra_writer_pars = ( + "delimiter", + "comment", + "quotechar", + "formats", + "strip_whitespace", + "names", + "include_names", + "exclude_names", + "fill_values", + "fill_include_names", + "fill_exclude_names", +) + + +def _get_writer(writer_cls, fast_writer, **kwargs): """Initialize a table writer allowing for common customizations. This routine is for internal (package) use only and is useful because it depends - only on the "core" module. """ - - writer_kwargs = dict([k, v] for k, v in kwargs.items() if k not in extra_writer_pars) - writer = Writer(**writer_kwargs) - - if 'delimiter' in kwargs: - writer.header.splitter.delimiter = kwargs['delimiter'] - writer.data.splitter.delimiter = kwargs['delimiter'] - if 'write_comment' in kwargs: - writer.header.write_comment = kwargs['write_comment'] - writer.data.write_comment = kwargs['write_comment'] - if 'quotechar' in kwargs: - writer.header.splitter.quotechar = kwargs['quotechar'] - writer.data.splitter.quotechar = kwargs['quotechar'] - if 'formats' in kwargs: - writer.data.formats = kwargs['formats'] - if 'names' in kwargs: - writer.header.names = kwargs['names'] - if 'include_names' in kwargs: - writer.header.include_names = kwargs['include_names'] - if 'exclude_names' in kwargs: - writer.header.exclude_names = kwargs['exclude_names'] - if 'fill_values' in kwargs: - writer.data.fill_values = kwargs['fill_values'] - if 'fill_include_names' in kwargs: - writer.data.fill_include_names = kwargs['fill_include_names'] - if 'fill_exclude_names' in kwargs: - writer.data.fill_exclude_names = kwargs['fill_exclude_names'] + only on the "core" module. + """ + from .fastbasic import FastBasic + + # A value of None for fill_values imply getting the default string + # representation of masked values (depending on the writer class), but the + # machinery expects a list. The easiest here is to just pop the value off, + # i.e. fill_values=None is the same as not providing it at all. + if "fill_values" in kwargs and kwargs["fill_values"] is None: + del kwargs["fill_values"] + + if issubclass(writer_cls, FastBasic): # Fast writers handle args separately + return writer_cls(**kwargs) + elif fast_writer and f"fast_{writer_cls._format_name}" in FAST_CLASSES: + # Switch to fast writer + kwargs["fast_writer"] = fast_writer + return FAST_CLASSES[f"fast_{writer_cls._format_name}"](**kwargs) + + writer_kwargs = {k: v for k, v in kwargs.items() if k not in extra_writer_pars} + writer = writer_cls(**writer_kwargs) + + if "delimiter" in kwargs: + writer.header.splitter.delimiter = kwargs["delimiter"] + writer.data.splitter.delimiter = kwargs["delimiter"] + if "comment" in kwargs: + writer.header.write_comment = kwargs["comment"] + writer.data.write_comment = kwargs["comment"] + if "quotechar" in kwargs: + writer.header.splitter.quotechar = kwargs["quotechar"] + writer.data.splitter.quotechar = kwargs["quotechar"] + if "formats" in kwargs: + writer.data.formats = kwargs["formats"] + if "strip_whitespace" in kwargs: + if kwargs["strip_whitespace"]: + # Restore the default SplitterClass process_val method which strips + # whitespace. This may have been changed in the Writer + # initialization (e.g. Rdb and Tab) + writer.data.splitter.process_val = operator.methodcaller("strip", " \t") + else: + writer.data.splitter.process_val = None + if "names" in kwargs: + writer.header.names = kwargs["names"] + if "include_names" in kwargs: + writer.include_names = kwargs["include_names"] + if "exclude_names" in kwargs: + writer.exclude_names = kwargs["exclude_names"] + if "fill_values" in kwargs: + # Prepend user-specified values to the class default. + with suppress(TypeError, IndexError): + # Test if it looks like (match, replace_string, optional_colname), + # in which case make it a list + kwargs["fill_values"][1] + "" + kwargs["fill_values"] = [kwargs["fill_values"]] + writer.data.fill_values = kwargs["fill_values"] + writer.data.fill_values + if "fill_include_names" in kwargs: + writer.data.fill_include_names = kwargs["fill_include_names"] + if "fill_exclude_names" in kwargs: + writer.data.fill_exclude_names = kwargs["fill_exclude_names"] return writer - - diff --git a/astropy/io/ascii/cparser.pyx b/astropy/io/ascii/cparser.pyx new file mode 100644 index 000000000000..192c003d9719 --- /dev/null +++ b/astropy/io/ascii/cparser.pyx @@ -0,0 +1,985 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +#cython: language_level=3 + +import csv +import math +import mmap +import multiprocessing +import os +import queue as Queue +import warnings + +import numpy as np + +cimport numpy as np + +from numpy import ma + +from cpython.buffer cimport ( + Py_buffer, + PyBUF_SIMPLE, + PyBuffer_Release, + PyObject_GetBuffer, +) +from libc cimport stdio +from libc.stdint cimport int64_t + +from astropy.utils.data import get_readable_fileobj +from astropy.utils.exceptions import AstropyWarning + +from . import core + +np.import_array() + + +cdef extern from "src/tokenizer.h": + ctypedef enum tokenizer_state: + START_LINE + START_FIELD + START_QUOTED_FIELD + FIELD + QUOTED_FIELD + COMMENT + CARRIAGE_RETURN + + ctypedef enum err_code: + NO_ERROR + INVALID_LINE + TOO_MANY_COLS + NOT_ENOUGH_COLS + CONVERSION_ERROR + OVERFLOW_ERROR + + ctypedef struct tokenizer_t: + char *source # single string containing all of the input + size_t source_len # length of the input + size_t source_pos # current index in source for tokenization + char delimiter # delimiter character + char comment # comment character + char quotechar # quote character + char expchar # exponential character in scientific notation + char newline # EOL character + char **output_cols # array of output strings for each column + char **col_ptrs # array of pointers to current output position for each col + int *output_len # length of each output column string + int num_cols # number of table columns + int num_rows # number of table rows + int fill_extra_cols # represents whether or not to fill rows with too few values + tokenizer_state state # current state of the tokenizer + err_code code # represents the latest error that has occurred + int iter_col # index of the column being iterated over + char *curr_pos # current iteration position + char *buf # buffer for empty data + int strip_whitespace_lines # whether to strip whitespace at the beginning and end of lines + int strip_whitespace_fields # whether to strip whitespace at the beginning and end of fields + int use_fast_converter # whether to use the fast converter for floats + char *comment_lines # single null-delimited string containing comment lines + int comment_lines_len # length of comment_lines in memory + int comment_pos # current index in comment_lines + # Example input/output + # -------------------- + # source: "A,B,C\n10,5.,6\n1,2,3" + # output_cols: ["A\x0010\x001", "B\x005.\x002", "C\x006\x003"] + + ctypedef struct memory_map: + char *ptr + int len + void *file_ptr + void *handle + + tokenizer_t *create_tokenizer(char delimiter, char comment, char quotechar, char expchar, + int fill_extra_cols, int strip_whitespace_lines, + int strip_whitespace_fields, int use_fast_converter) + void delete_tokenizer(tokenizer_t *tokenizer) + int skip_lines(tokenizer_t *self, int offset, int header) + int tokenize(tokenizer_t *self, int end, int header, int num_cols) + int64_t str_to_int64_t(tokenizer_t *self, char *str) + double fast_str_to_double(tokenizer_t *self, char *str) + double str_to_double(tokenizer_t *self, char *str) + void start_iteration(tokenizer_t *self, int col) + char *next_field(tokenizer_t *self, int *size) + char *get_line(char *ptr, size_t *len, size_t map_len) + void reset_comments(tokenizer_t *self) + +cdef extern from "Python.h": + int PyObject_AsReadBuffer(object obj, const void **buffer, Py_ssize_t *buffer_len) + +class CParserError(Exception): + """ + An instance of this class is thrown when an error occurs + during C parsing. + """ + +ERR_CODES = dict(enumerate([ + "no error", + "invalid line supplied", + lambda line: "too many columns found in line {0} of data".format(line), + lambda line: "not enough columns found in line {0} of data".format(line), + "type conversion error", + "overflow error" + ])) + +cdef class FileString: + """ + A wrapper class for a memory-mapped file pointer. + """ + cdef: + object fhandle + object mmap + const void *mmap_ptr + Py_buffer buf + + def __cinit__(self, fname): + self.fhandle = open(fname, 'r') + if not self.fhandle: + raise OSError('File "{0}" could not be opened'.format(fname)) + self.mmap = mmap.mmap(self.fhandle.fileno(), 0, access=mmap.ACCESS_READ) + cdef Py_ssize_t buf_len = len(self.mmap) + PyObject_GetBuffer(self.mmap, &self.buf, PyBUF_SIMPLE) + self.mmap_ptr = self.buf.buf + + def __dealloc__(self): + if self.mmap: + PyBuffer_Release(&self.buf) + self.mmap.close() + self.fhandle.close() + + def __len__(self): + return len(self.mmap) + + def __getitem__(self, i): + return self.mmap[i] + + def splitlines(self): + """ + Return a generator yielding lines from the memory map. + """ + cdef char *ptr = self.mmap_ptr + cdef char *tmp + cdef size_t line_len + cdef size_t map_len = len(self.mmap) + + while ptr: + tmp = get_line(ptr, &line_len, map_len) + yield ptr[:line_len].decode('ascii') + ptr = tmp + +cdef class CParser: + """ + A fast Cython parser class which uses underlying C code + for tokenization. + """ + + cdef: + tokenizer_t *tokenizer + object names + object header_names + int data_start + object data_end + object include_names + object exclude_names + object fill_values + object fill_empty + object fill_include_names + object fill_exclude_names + object fill_names + int fill_extra_cols + bytes source_bytes + char *source_ptr + set use_cols + + cdef public: + int width + object source + object header_start + object header_chars + + def __cinit__(self, source, strip_line_whitespace, strip_line_fields, + delimiter=',', + comment=None, + quotechar='"', + header_start=0, + data_start=1, + data_end=None, + names=None, + include_names=None, + exclude_names=None, + fill_values=('', '0'), + fill_include_names=None, + fill_exclude_names=None, + fill_extra_cols=0, + fast_reader=None): + + if fast_reader is None: + fast_reader = {} + + # Handle fast_reader parameter + expchar = fast_reader.pop('exponent_style', 'E').upper() + # use_fast_reader are False by default, and it + # supports Fortran double precision notation + if expchar == 'E': + use_fast_converter = fast_reader.pop('use_fast_converter', False) + else: + use_fast_converter = fast_reader.pop('use_fast_converter', True) + if not use_fast_converter: + raise core.FastOptionsError("fast_reader: exponent_style requires use_fast_converter") + if expchar.startswith('FORT'): + expchar = 'A' + + if fast_reader: + raise core.FastOptionsError("Invalid parameter in fast_reader dict") + + if comment is None: + comment = '\x00' # tokenizer ignores all comments if comment='\x00' + self.tokenizer = create_tokenizer(ord(delimiter), ord(comment), + ord(quotechar), ord(expchar), + fill_extra_cols, + strip_line_whitespace, + strip_line_fields, + use_fast_converter) + self.source = None + if source is not None: + self.setup_tokenizer(source) + self.header_start = header_start + self.data_start = data_start + self.data_end = data_end + self.names = names + self.include_names = include_names + self.exclude_names = exclude_names + self.fill_values = fill_values + self.fill_include_names = fill_include_names + self.fill_exclude_names = fill_exclude_names + self.fill_names = None + self.fill_extra_cols = fill_extra_cols + + if self.names is not None: + if None in self.names: + raise TypeError('Cannot have None for column name') + if len(set(self.names)) != len(self.names): + raise ValueError('Duplicate column names') + + def __dealloc__(self): + if self.tokenizer: + delete_tokenizer(self.tokenizer) # perform C memory cleanup + + cdef get_error(self, code, num_rows, msg): + err_msg = ERR_CODES.get(code, "unknown error") + + # error code is lambda function taking current line as input + if callable(err_msg): + err_msg = err_msg(num_rows + 1) + + return CParserError("{0}: {1}".format(msg, err_msg)) + + cdef raise_error(self, msg): + raise self.get_error(self.tokenizer.code, self.tokenizer.num_rows, msg) + + cpdef setup_tokenizer(self, source): + cdef FileString fstring + + if isinstance(source, str): # filename or data + if '\n' not in source and '\r' not in source: # filename + fstring = FileString(source) + self.tokenizer.source = fstring.mmap_ptr + self.source_ptr = fstring.mmap_ptr + self.source = fstring + self.tokenizer.source_len = len(fstring) + return + # Otherwise, source is the actual data so we leave it be + elif hasattr(source, 'read'): # file-like object + with get_readable_fileobj(source) as file_obj: + source = file_obj.read() + elif isinstance(source, FileString): + self.tokenizer.source = ((source).mmap_ptr) + self.source = source + self.tokenizer.source_len = len(source) + return + else: + # Iterable sequence of lines, merge with newline character + try: + if self.tokenizer.delimiter == ord('\n'): + newline = '\r' + else: + newline = '\n' + source = newline.join(source) + except TypeError: + raise TypeError('Input "table" must be a file-like object, a ' + 'string (filename or data), or an iterable') + # Create a reference to the Python object so its char * pointer remains valid + self.source = source + + # encode in ASCII for char * handling + self.source_bytes = self.source.encode('ascii') + self.tokenizer.source = self.source_bytes + self.tokenizer.source_len = len(self.source_bytes) + + def read_header(self, deduplicate=True, filter_names=True): + self.tokenizer.source_pos = 0 + + # header_start is a valid line number + if self.header_start is not None and self.header_start >= 0: + if skip_lines(self.tokenizer, self.header_start, 1) != 0: + self.raise_error("an error occurred while advancing to the " + "first header line") + if tokenize(self.tokenizer, -1, 1, 0) != 0: + self.raise_error("an error occurred while tokenizing the header line") + self.header_names = [] + name = '' + + for i in range(self.tokenizer.output_len[0]): # header is in first col string + c = self.tokenizer.output_cols[0][i] # next char in header string + if not c: # zero byte -- field terminator + if name: + # replace empty placeholder with '' + self.header_names.append(name.replace('\x01', '')) + name = '' + else: + break # end of string + else: + name += chr(c) + self.width = len(self.header_names) + if deduplicate and not self.names: # skip if custom names were provided + self._deduplicate_names() + + else: + # Get number of columns from first data row + if tokenize(self.tokenizer, -1, 1, 0) != 0: + self.raise_error("an error occurred while tokenizing the first line of data") + self.width = 0 + for i in range(self.tokenizer.output_len[0]): # header is in first col string + # zero byte -- field terminator + if not self.tokenizer.output_cols[0][i]: + # ends valid field + if i > 0 and self.tokenizer.output_cols[0][i - 1]: + self.width += 1 + else: # end of line + break + if self.width == 0: # no data + raise core.InconsistentTableError('No data lines found, C reader ' + 'cannot autogenerate column names') + # auto-generate names + self.header_names = ['col{0}'.format(i + 1) for i in range(self.width)] + + if self.names: + self.width = len(self.names) + else: + self.names = self.header_names + + # self.use_cols should only contain columns included in output + self.use_cols = set(self.names) + if filter_names and self.include_names is not None: + self.use_cols.intersection_update(self.include_names) + if filter_names and self.exclude_names is not None: + self.use_cols.difference_update(self.exclude_names) + + self.width = len(self.names) + + def read(self, try_int, try_float, try_string): + # Read in a single process + self.tokenizer.source_pos = 0 + if skip_lines(self.tokenizer, self.data_start, 0) != 0: + self.raise_error("an error occurred while advancing to the first " + "line of data") + + self.header_chars = self.source[:self.tokenizer.source_pos] + + cdef int data_end = -1 # keep reading data until the end + if self.data_end is not None and self.data_end >= 0: + data_end = max(self.data_end - self.data_start, 0) # read nothing if data_end < 0 + + if tokenize(self.tokenizer, data_end, 0, len(self.names)) != 0: + if self.tokenizer.code in (NOT_ENOUGH_COLS, TOO_MANY_COLS): + raise core.InconsistentTableError("Number of header columns " + + "({0}) inconsistent with data columns in data line {1}" + .format(self.tokenizer.num_cols, self.tokenizer.num_rows)) + else: + self.raise_error("an error occurred while parsing table data") + elif self.tokenizer.num_rows == 0: # no data + return ([np.array([], dtype=np.int_)] * self.width, + self._get_comments(self.tokenizer)) + self._set_fill_values() + cdef int num_rows = self.tokenizer.num_rows + if self.data_end is not None and self.data_end < 0: # negative indexing + num_rows += self.data_end + return self._convert_data(self.tokenizer, try_int, try_float, + try_string, num_rows) + + cdef _set_fill_values(self): + if self.fill_names is None: + self.fill_names = set(self.names) + if self.fill_include_names is not None: + self.fill_names.intersection_update(self.fill_include_names) + if self.fill_exclude_names is not None: + self.fill_names.difference_update(self.fill_exclude_names) + self.fill_values, self.fill_empty = get_fill_values(self.fill_values) + + cdef _get_comments(self, tokenizer_t *t): + line_comments = [] + comment = '' + for i in range(t.comment_pos): + c = t.comment_lines[i] # next char in comment string + if not c: # zero byte -- line terminator + # replace empty placeholder with '' + line_comments.append(comment.replace('\x01', '').strip()) + comment = '' + else: + comment += chr(c) + return line_comments + + cdef _convert_data(self, tokenizer_t *t, try_int, try_float, try_string, num_rows): + cols = {} + + for i, name in enumerate(self.names): + if name not in self.use_cols: + continue + # Try int first, then float, then string + try: + if try_int and not try_int[name]: + raise ValueError() + cols[name] = self._convert_int(t, i, num_rows) + except ValueError: + try: + if t.code == OVERFLOW_ERROR: + # Overflow during int conversion (extending range) + warnings.warn("OverflowError converting to {0} in column {1}, reverting to String." + .format('IntType', name), AstropyWarning) + if try_string and not try_string[name]: + raise ValueError('Column {0} failed to convert'.format(name)) + t.code = NO_ERROR + cols[name] = self._convert_str(t, i, num_rows) + else: + if try_float and not try_float[name]: + raise ValueError() + t.code = NO_ERROR + cols[name] = self._convert_float(t, i, num_rows) + if t.code == OVERFLOW_ERROR: + # Overflow during float conversion (extending range) + warnings.warn("OverflowError converting to {0} in column {1}, possibly resulting in degraded precision." + .format('FloatType', name), AstropyWarning) + t.code = NO_ERROR + except ValueError: + if try_string and not try_string[name]: + raise ValueError('Column {0} failed to convert'.format(name)) + cols[name] = self._convert_str(t, i, num_rows) + + return cols, self._get_comments(t) + + cdef np.ndarray _convert_int(self, tokenizer_t *t, int i, int nrows): + cdef int num_rows = t.num_rows + if nrows != -1: + num_rows = nrows + # initialize ndarray + # use `int64_t` for integers to ensure large integers are converted correctly + # on some platforms, e.g. Windows (where `long` is 32 bits) + # https://github.com/astropy/astropy/issues/5744 + cdef np.ndarray col = np.empty(num_rows, dtype=np.int64) + cdef int64_t converted + cdef int row = 0 + cdef int64_t *data = col.data # pointer to raw data + cdef char *field + cdef char *empty_field = t.buf # memory address of designated empty buffer + cdef bytes new_value + mask = set() # set of indices for masked values + start_iteration(t, i) # begin the iteration process in C + + for row in range(num_rows): + # retrieve the next field as a C pointer + field = next_field(t, 0) + replace_info = None + + if field == empty_field and self.fill_empty: + replace_info = self.fill_empty + # hopefully this implicit char * -> byte conversion for fill values + # checking can be avoided in most cases, since self.fill_values will + # be empty in the default case (self.fill_empty will do the work + # instead) + elif field != empty_field and self.fill_values and field in self.fill_values: + replace_info = self.fill_values[field] + + if replace_info is not None: + # Either this column applies to the field as specified in the + # fill_values parameter, or no specific columns are specified + # and this column should apply fill_values. + if (len(replace_info) > 1 and self.names[i] in replace_info[1:]) \ + or (len(replace_info) == 1 and self.names[i] in self.fill_names): + mask.add(row) + new_value = str(replace_info[0]).encode('ascii') + # try converting the new value + converted = str_to_int64_t(t, new_value) + else: + converted = str_to_int64_t(t, field) + else: + # convert the field to long (widest integer type) + converted = str_to_int64_t(t, field) + + if t.code in (CONVERSION_ERROR, OVERFLOW_ERROR): + # no dice + if t.code == CONVERSION_ERROR: + t.code = NO_ERROR + raise ValueError() + + data[row] = converted + row += 1 + + if mask: + # convert to masked_array + return ma.masked_array(col, mask=[1 if i in mask else 0 for i in + range(row)]) + else: + return col + + cdef np.ndarray _convert_float(self, tokenizer_t *t, int i, int nrows): + # very similar to _convert_int() + cdef int num_rows = t.num_rows + if nrows != -1: + num_rows = nrows + + cdef np.ndarray col = np.empty(num_rows, dtype=np.float64) + cdef double converted + cdef int row = 0 + cdef double *data = col.data + cdef char *field + cdef char *empty_field = t.buf + cdef bytes new_value + cdef int replacing + cdef err_code overflown = NO_ERROR # store any OVERFLOW to raise warning + mask = set() + + start_iteration(t, i) + for row in range(num_rows): + field = next_field(t, 0) + replace_info = None + replacing = False + + if field == empty_field and self.fill_empty: + replace_info = self.fill_empty + + elif field != empty_field and self.fill_values and field in self.fill_values: + replace_info = self.fill_values[field] + + if replace_info is not None: + if (len(replace_info) > 1 and self.names[i] in replace_info[1:]) \ + or (len(replace_info) == 1 and self.names[i] in self.fill_names): + mask.add(row) + new_value = str(replace_info[0]).encode('ascii') + replacing = True + converted = str_to_double(t, new_value) + else: + converted = str_to_double(t, field) + else: + converted = str_to_double(t, field) + + if t.code == CONVERSION_ERROR: + t.code = NO_ERROR + raise ValueError() + else: + data[row] = converted + if t.code == OVERFLOW_ERROR: + t.code = NO_ERROR + overflown = OVERFLOW_ERROR + row += 1 + t.code = overflown + + if mask: + return ma.masked_array(col, mask=[1 if i in mask else 0 for i in + range(row)]) + else: + return col + + cdef _convert_str(self, tokenizer_t *t, int i, int nrows): + # similar to _convert_int, but no actual conversion + cdef int num_rows = t.num_rows + if nrows != -1: + num_rows = nrows + + cdef int row = 0 + cdef bytes field + cdef int field_len + cdef int max_len = 0 + cdef list fields_list = [] + mask = set() + + start_iteration(t, i) + for row in range(num_rows): + field = next_field(t, &field_len) + replace_info = None + + if field_len == 0 and self.fill_empty: + replace_info = self.fill_empty + + elif field_len > 0 and self.fill_values and field in self.fill_values: + replace_info = self.fill_values[field] + + if replace_info is not None: + el = replace_info[0].encode('ascii') + if (len(replace_info) > 1 and self.names[i] in replace_info[1:]) \ + or (len(replace_info) == 1 and self.names[i] in self.fill_names): + mask.add(row) + field = el + + fields_list.append(field) + if field_len > max_len: + max_len = field_len + row += 1 + + cdef np.ndarray col = np.array(fields_list, dtype=(str, max_len)) + + if mask: + return ma.masked_array(col, mask=[1 if i in mask else 0 for i in + range(row)]) + else: + return col + + def get_names(self): + # ignore excluded columns + return [name for name in self.names if name in self.use_cols] + + def set_names(self, names): + self.names = names + + def get_header_names(self): + return self.header_names + + def _deduplicate_names(self): + """Ensure there are no duplicates in ``self.header_names`` + Cythonic version of core._deduplicate_names. + """ + cdef int i + new_names = [] + existing_names = set() + + for name in self.header_names: + base_name = name + '_' + i = 1 + while name in existing_names: + # Iterate until a unique name is found + name = base_name + str(i) + i += 1 + new_names.append(name) + existing_names.add(name) + + self.header_names = new_names + + def __reduce__(self): + cdef bytes source = self.source_ptr if self.source_ptr else self.source_bytes + fast_reader = dict(exponent_style=chr(self.tokenizer.expchar), + use_fast_converter=self.tokenizer.use_fast_converter) + return (_copy_cparser, (source, self.use_cols, self.fill_names, + self.fill_values, self.fill_empty, self.tokenizer.strip_whitespace_lines, + self.tokenizer.strip_whitespace_fields, + dict(delimiter=chr(self.tokenizer.delimiter), + comment=chr(self.tokenizer.comment), + quotechar=chr(self.tokenizer.quotechar), + header_start=self.header_start, + data_start=self.data_start, + data_end=self.data_end, + names=self.names, + include_names=self.include_names, + exclude_names=self.exclude_names, + fill_values=None, + fill_include_names=self.fill_include_names, + fill_exclude_names=self.fill_exclude_names, + fill_extra_cols=self.tokenizer.fill_extra_cols, + fast_reader=fast_reader))) + +def _copy_cparser(bytes source, use_cols, fill_names, fill_values, + fill_empty, strip_whitespace_lines, strip_whitespace_fields, kwargs): + + parser = CParser(None, strip_whitespace_lines, strip_whitespace_fields, **kwargs) + + parser.use_cols = use_cols + parser.fill_names = fill_names + parser.fill_values = fill_values + parser.fill_empty = fill_empty + + parser.tokenizer.source = source + parser.tokenizer.source_len = len(source) + parser.source_bytes = source + + return parser + + +def _read_chunk(CParser self, start, end, try_int, + try_float, try_string, queue, reconvert_queue, i): + cdef tokenizer_t *chunk_tokenizer = self.tokenizer + chunk_tokenizer.source_len = end + chunk_tokenizer.source_pos = start + reset_comments(chunk_tokenizer) + + data = None + err = None + + if tokenize(chunk_tokenizer, -1, 0, len(self.names)) != 0: + err = (chunk_tokenizer.code, chunk_tokenizer.num_rows) + if chunk_tokenizer.num_rows == 0: # no data + data = dict((name, np.array([], np.int_)) for name in self.get_names()) + line_comments = self._get_comments(chunk_tokenizer) + else: + try: + data, line_comments = self._convert_data(chunk_tokenizer, + try_int, try_float, try_string, -1) + except Exception as e: + delete_tokenizer(chunk_tokenizer) + self.tokenizer = NULL # prevent another de-allocation in __dalloc__ + queue.put((None, e, i)) + return + + try: + queue.put(((line_comments, data), err, i)) + except Queue.Full as e: + # hopefully this shouldn't happen + delete_tokenizer(chunk_tokenizer) + self.tokenizer = NULL # prevent another de-allocation in __dalloc__ + queue.pop() + queue.put((None, e, i)) + return + + reconvert_cols = reconvert_queue.get() + for col in reconvert_cols: + queue.put((self._convert_str(chunk_tokenizer, col, -1), i, col)) + delete_tokenizer(chunk_tokenizer) + self.tokenizer = NULL # prevent another de-allocation in __dalloc__ + reconvert_queue.put(reconvert_cols) # return to the queue for other processes + + +cdef class FastWriter: + """ + A fast Cython writing class for writing tables + as ASCII data. + """ + + cdef: + object table + list use_names + dict fill_values + set fill_cols + list col_iters + list formats + list format_funcs + list types + list line_comments + str quotechar + str expchar + str delimiter + int strip_whitespace + object comment + + def __cinit__(self, table, + delimiter=',', + comment='# ', + quotechar='"', + expchar='e', + formats=None, + strip_whitespace=True, + names=None, # ignore, already used in _get_writer + include_names=None, + exclude_names=None, + fill_values=[], + fill_include_names=None, + fill_exclude_names=None, + fast_writer=True): + + from astropy.table import pprint # Here to avoid circular import + + if fast_writer is True: + fast_writer = {} + # fast_writer might contain custom writing options + + self.table = table + self.comment = comment + self.strip_whitespace = strip_whitespace + use_names = set(table.colnames) + + # Apply include_names before exclude_names + if include_names is not None: + use_names.intersection_update(include_names) + if exclude_names is not None: + use_names.difference_update(exclude_names) + # preserve column ordering via list + self.use_names = [x for x in table.colnames if x in use_names] + + fill_values = get_fill_values(fill_values, False) + self.fill_values = fill_values.copy() + + # Add int/float versions of each fill value (if applicable) + # to the fill_values dict. This prevents the writer from having + # to call unicode() on every value, which is a major + # performance hit. + for key, val in fill_values.items(): + try: + self.fill_values[int(key)] = val + self.fill_values[float(key)] = val + except (ValueError, np.ma.MaskError): + pass + + fill_names = set(self.use_names) + # Apply fill_include_names before fill_exclude_names + if fill_include_names is not None: + fill_names.intersection_update(fill_include_names) + if fill_exclude_names is not None: + fill_names.difference_update(fill_exclude_names) + # Preserve column ordering + self.fill_cols = set([i for i, name in enumerate(self.use_names) if + name in fill_names]) + + # formats in user-specified dict should override + # existing column formats + if formats is not None: + for name in self.use_names: + if name in formats: + self.table[name].format = formats[name] + + self.col_iters = [] + self.formats = [] + self.format_funcs = [] + self.line_comments = table.meta.get('comments', []) + + for col in table.columns.values(): + if col.name in self.use_names: # iterate over included columns + # If col.format is None then don't use any formatter to improve + # speed. However, if the column is a byte string and this + # is Py3, then use the default formatter (which in this case + # does val.decode('utf-8')) in order to avoid a leading 'b'. + if col.format is None and not col.dtype.kind == 'S': + self.format_funcs.append(None) + else: + self.format_funcs.append(col.info._format_funcs.get( + col.format, pprint.get_auto_format_func(col))) + # col is a numpy.ndarray, so we convert it to + # an ordinary list because csv.writer will call + # np.array_str() on each numpy value, which is + # very inefficient + self.col_iters.append(iter(col.tolist())) + self.formats.append(col.format) + + self.quotechar = None if quotechar is None else str(quotechar) + self.delimiter = ' ' if delimiter is None else str(delimiter) + # 'S' for string types, 'N' for numeric types + self.types = ['S' if self.table[name].dtype.kind in ('S', 'U') else 'N' + for name in self.use_names] + + cdef _write_comments(self, output): + if self.comment not in (False, None): + for comment_line in self.line_comments: + output.write(self.comment + comment_line + '\n') + + def _write_header(self, output, writer, header_output, output_types): + if header_output is not None and header_output == 'comment': + output.write(self.comment) + writer.writerow([x.strip() for x in self.use_names] if + self.strip_whitespace else self.use_names) + self._write_comments(output) + else: + self._write_comments(output) + if header_output is not None: + writer.writerow([x.strip() for x in self.use_names] if + self.strip_whitespace else self.use_names) + if output_types: + writer.writerow(self.types) + + def write(self, output, header_output, output_types): + opened_file = False + + if not hasattr(output, 'write'): # output is a filename + # NOTE: we need to specify newline='', otherwise the default + # behavior is for Python to translate \r\n (which we write because + # of os.linesep) into \r\r\n. Specifying newline='' disables any + output = open(output, 'w', newline='') + opened_file = True # remember to close file afterwards + writer = core.CsvWriter(output, + delimiter=self.delimiter, + doublequote=True, + escapechar=None, + quotechar=self.quotechar, + quoting=csv.QUOTE_MINIMAL, + lineterminator=os.linesep) + self._write_header(output, writer, header_output, output_types) + + # Split rows into N-sized chunks, since we don't want to + # store all the rows in memory at one time (inefficient) + # or fail to take advantage of the speed boost of writerows() + # over writerow(). + cdef int i = -1 + cdef int N = 100 + cdef int num_cols = len(self.use_names) + cdef int num_rows = len(self.table) + # cache string columns beforehand + cdef set string_rows = set([i for i, type in enumerate(self.types) if + type == 'S']) + cdef list rows = [[None] * num_cols for i in range(N)] + + for i in range(num_rows): + for j in range(num_cols): + orig_field = next(self.col_iters[j]) # get field + # str_val monitors whether we should check if the field + # should be stripped + str_val = True + + if orig_field is None: # tolist() converts ma.masked to None + field = core.masked + rows[i % N][j] = '' + + elif self.format_funcs[j] is not None: + field = self.format_funcs[j](self.formats[j], orig_field) + rows[i % N][j] = field + + else: + field = orig_field + rows[i % N][j] = field + str_val = j in string_rows + + if field in self.fill_values: + new_val = self.fill_values[field][0] + # Either this column applies to the field as specified in + # the fill_values parameter, or no specific columns are + # specified and this column should apply fill_values. + if (len(self.fill_values[field]) > 1 and self.use_names[j] in self.fill_values[field][1:]) \ + or (len(self.fill_values[field]) == 1 and j in self.fill_cols): + str_val = True + rows[i % N][j] = new_val + if self.strip_whitespace: # new_val should be a string + rows[i % N][j] = rows[i % N][j].strip() + + if str_val and self.strip_whitespace: + rows[i % N][j] = rows[i % N][j].strip(' \t') + + if i >= N - 1 and i % N == N - 1: # rows is now full + writer.writerows(rows) + + # Write leftover rows not included in previous chunks + if i >= 0 and i % N != N - 1: + writer.writerows(rows[:i % N + 1]) + + if opened_file: + output.close() + +def get_fill_values(fill_values, read=True): + if len(fill_values) > 0 and isinstance(fill_values[0], str): + # e.g. fill_values=('999', '0') + fill_values = [fill_values] + else: + fill_values = fill_values + + # look for an empty replacement to cache for speedy conversion + fill_empty = None + for el in fill_values: + if el[0] == '': + fill_empty = el[1:] + break + + try: + # Create a dict with the values to be replaced as keys + if read: + fill_values = dict([(l[0].encode('ascii'), l[1:]) for + l in fill_values if l[0] != '']) + else: + # don't worry about encoding for writing + fill_values = dict([(l[0], l[1:]) for l in fill_values]) + + except IndexError: + raise ValueError("Format of fill_values must be " + "(, , , ...)") + if read: + return (fill_values, fill_empty) + else: + return fill_values # cache for empty values doesn't matter for writing diff --git a/astropy/io/ascii/daophot.py b/astropy/io/ascii/daophot.py index 7e23013949bf..c922e92a8f48 100644 --- a/astropy/io/ascii/daophot.py +++ b/astropy/io/ascii/daophot.py @@ -1,121 +1,394 @@ -"""Asciitable: an extensible ASCII table reader and writer. +# Licensed under a 3-clause BSD style license - see LICENSE.rst +""" +An extensible ASCII table reader and writer. -daophot.py: - Classes to read DAOphot table format +Classes to read DAOphot table format :Copyright: Smithsonian Astrophysical Observatory (2011) :Author: Tom Aldcroft (aldcroft@head.cfa.harvard.edu) """ -## -## Redistribution and use in source and binary forms, with or without -## modification, are permitted provided that the following conditions are met: -## * Redistributions of source code must retain the above copyright -## notice, this list of conditions and the following disclaimer. -## * Redistributions in binary form must reproduce the above copyright -## notice, this list of conditions and the following disclaimer in the -## documentation and/or other materials provided with the distribution. -## * Neither the name of the Smithsonian Astrophysical Observatory nor the -## names of its contributors may be used to endorse or promote products -## derived from this software without specific prior written permission. -## -## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -## ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -## WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -## DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY -## DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -## (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -## LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -## ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -## SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - +import itertools as itt import re -from . import core -from . import basic +from collections import defaultdict + +import numpy as np + +from . import core, fixedwidth +from .misc import first_false_index, first_true_index, groupmore + + +class DaophotHeader(core.BaseHeader): + """ + Read the header from a file produced by the IRAF DAOphot routine. + """ + + comment = r"\s*#K" + + # Regex for extracting the format strings + re_format = re.compile(r"%-?(\d+)\.?\d?[sdfg]") + re_header_keyword = re.compile( + r"[#]K\s+ (?P \w+)\s* = (?P .+) $", re.VERBOSE + ) + aperture_values = () + + def __init__(self): + core.BaseHeader.__init__(self) + + def parse_col_defs(self, grouped_lines_dict): + """Parse a series of column definition lines. + + Examples + -------- + When parsing, there may be several such blocks in a single file + (where continuation characters have already been stripped). + #N ID XCENTER YCENTER MAG MERR MSKY NITER + #U ## pixels pixels magnitudes magnitudes counts ## + #F %-9d %-10.3f %-10.3f %-12.3f %-14.3f %-15.7g %-6d + """ + line_ids = ("#N", "#U", "#F") + coldef_dict = defaultdict(list) + + # Function to strip identifier lines + stripper = lambda s: s[2:].strip(" \\") + for defblock in zip(*map(grouped_lines_dict.get, line_ids)): + for key, line in zip(line_ids, map(stripper, defblock)): + coldef_dict[key].append(line.split()) + + # Save the original columns so we can use it later to reconstruct the + # original header for writing + if self.data.is_multiline: + # Database contains multi-aperture data. + # Autogen column names, units, formats from last row of column headers + last_names, last_units, last_formats = list( + zip(*map(coldef_dict.get, line_ids)) + )[-1] + N_multiline = len(self.data.first_block) + for i in np.arange(1, N_multiline + 1).astype("U2"): + # extra column names eg. RAPERT2, SUM2 etc... + extended_names = list(map("".join, zip(last_names, itt.repeat(i)))) + if i == "1": # Enumerate the names starting at 1 + coldef_dict["#N"][-1] = extended_names + else: + coldef_dict["#N"].append(extended_names) + coldef_dict["#U"].append(last_units) + coldef_dict["#F"].append(last_formats) + + # Get column widths from column format specifiers + get_col_width = lambda s: int(self.re_format.search(s).groups()[0]) + col_widths = [ + [get_col_width(f) for f in formats] for formats in coldef_dict["#F"] + ] + # original data format might be shorter than 80 characters and filled with spaces + row_widths = np.fromiter(map(sum, col_widths), int) + row_short = Daophot.table_width - row_widths + # fix last column widths + for w, r in zip(col_widths, row_short): + w[-1] += r + + self.col_widths = col_widths + + # merge the multi-line header data into single line data + return {k: list(itt.chain(*v)) for (k, v) in coldef_dict.items()} + + def update_meta(self, lines, meta): + """ + Extract table-level keywords for DAOphot table. These are indicated by + a leading '#K ' prefix. + """ + table_meta = meta["table"] + + # self.lines = self.get_header_lines(lines) + Nlines = len(self.lines) + if Nlines > 0: + # Group the header lines according to their line identifiers (#K, + # #N, #U, #F or just # (spacer line)) function that grabs the line + # identifier + get_line_id = lambda s: s.split(None, 1)[0] + + # Group lines by the line identifier ('#N', '#U', '#F', '#K') and + # capture line index + gid, groups = zip(*groupmore(get_line_id, self.lines, range(Nlines))) + + # Groups of lines and their indices + grouped_lines, gix = zip(*groups) + + # Dict of line groups keyed by line identifiers + grouped_lines_dict = dict(zip(gid, grouped_lines)) + + # Update the table_meta keywords if necessary + if "#K" in grouped_lines_dict: + keywords = dict( + map(self.extract_keyword_line, grouped_lines_dict["#K"]) + ) + table_meta["keywords"] = keywords + + coldef_dict = self.parse_col_defs(grouped_lines_dict) + + line_ids = ("#N", "#U", "#F") + for name, unit, fmt in zip(*map(coldef_dict.get, line_ids)): + meta["cols"][name] = {"unit": unit, "format": fmt} + + self.meta = meta + self.names = coldef_dict["#N"] + + def extract_keyword_line(self, line): + """ + Extract info from a header keyword line (#K). + """ + m = self.re_header_keyword.match(line) + if m: + vals = m.group("stuff").strip().rsplit(None, 2) + keyword_dict = { + "units": vals[-2], + "format": vals[-1], + "value": (vals[0] if len(vals) > 2 else ""), + } + return m.group("name"), keyword_dict + + def get_cols(self, lines): + """ + Initialize the header Column objects from the table ``lines`` for a DAOphot + header. The DAOphot header is specialized so that we just copy the entire BaseHeader + get_cols routine and modify as needed. + + Parameters + ---------- + lines : list + List of table lines + + Returns + ------- + col : list + List of table Columns + """ + if not self.names: + raise core.InconsistentTableError("No column names found in DAOphot header") + + # Create the list of io.ascii column objects + self._set_cols_from_names() + + # Set unit and format as needed. + coldefs = self.meta["cols"] + for col in self.cols: + unit, fmt = map(coldefs[col.name].get, ("unit", "format")) + if unit != "##": + col.unit = unit + if fmt != "##": + col.format = fmt + + # Set column start and end positions. + col_width = list(itt.chain.from_iterable(self.col_widths)) + ends = np.cumsum(col_width) + starts = ends - col_width + for i, col in enumerate(self.cols): + col.start, col.end = starts[i], ends[i] + col.span = col.end - col.start + if hasattr(col, "format"): + if any(x in col.format for x in "fg"): + col.type = core.FloatType + elif "d" in col.format: + col.type = core.IntType + elif "s" in col.format: + col.type = core.StrType + + # INDEF is the missing value marker + self.data.fill_values.append(("INDEF", "0")) + + +class DaophotData(core.BaseData): + splitter_class = fixedwidth.FixedWidthSplitter + start_line = 0 + comment = r"\s*#" + + def __init__(self): + core.BaseData.__init__(self) + self.is_multiline = False + + def get_data_lines(self, lines): + # Special case for multiline daophot databases. Extract the aperture + # values from the first multiline data block + if self.is_multiline: + # Grab the first column of the special block (aperture values) and + # recreate the aperture description string + aplist = next(zip(*map(str.split, self.first_block))) + self.header.aperture_values = tuple(map(float, aplist)) + + # Set self.data.data_lines to a slice of lines contain the data rows + core.BaseData.get_data_lines(self, lines) + + +class DaophotInputter(core.ContinuationLinesInputter): + continuation_char = "\\" + multiline_char = "*" + replace_char = " " + re_multiline = re.compile(r"(#?)[^\\*#]*(\*?)(\\*) ?$") + + def search_multiline(self, lines, depth=150): + """ + Search lines for special continuation character to determine number of + continued rows in a datablock. For efficiency, depth gives the upper + limit of lines to search. + """ + # The list of apertures given in the #K APERTURES keyword may not be + # complete!! This happens if the string description of the aperture + # list is longer than the field width of the #K APERTURES field. In + # this case we have to figure out how many apertures there are based on + # the file structure. + + comment, special, cont = zip( + *(self.re_multiline.search(line).groups() for line in lines[:depth]) + ) + + # Find first non-comment line + data_start = first_false_index(comment) + + # No data in lines[:depth]. This may be because there is no data in + # the file, or because the header is really huge. If the latter, + # increasing the search depth should help + if data_start is None: + return None, None, lines[:depth] + + header_lines = lines[:data_start] + + # Find first line ending on special row continuation character '*' + # indexed relative to data_start + first_special = first_true_index(special[data_start:depth]) + if first_special is None: # no special lines + return None, None, header_lines + + # last line ending on special '*', but not on line continue '/' + last_special = first_false_index(special[data_start + first_special : depth]) + # index relative to first_special + + # if first_special is None: #no end of special lines within search + # depth! increase search depth return self.search_multiline( lines, + # depth=2*depth ) + + # indexing now relative to line[0] + markers = np.cumsum([data_start, first_special, last_special]) + # multiline portion of first data block + multiline_block = lines[markers[1] : markers[-1]] + + return markers, multiline_block, header_lines + + def process_lines(self, lines): + markers, block, header = self.search_multiline(lines) + self.data.is_multiline = markers is not None + self.data.markers = markers + self.data.first_block = block + # set the header lines returned by the search as a attribute of the header + self.data.header.lines = header + + if markers is not None: + lines = lines[markers[0] :] + + continuation_char = self.continuation_char + multiline_char = self.multiline_char + replace_char = self.replace_char + + parts = [] + outlines = [] + for i, line in enumerate(lines): + mo = self.re_multiline.search(line) + if mo: + comment, special, cont = mo.groups() + if comment or cont: + line = line.replace(continuation_char, replace_char) + if special: + line = line.replace(multiline_char, replace_char) + if cont and not comment: + parts.append(line) + if not cont: + parts.append(line) + outlines.append("".join(parts)) + parts = [] + else: + raise core.InconsistentTableError( + f"multiline re could not match line {i}: {line}" + ) + + return outlines + class Daophot(core.BaseReader): - """Read a DAOphot file. + """ + DAOphot format table. + Example:: - #K MERGERAD = INDEF scaleunit %-23.7g + #K MERGERAD = INDEF scaleunit %-23.7g #K IRAF = NOAO/IRAFV2.10EXPORT version %-23s #K USER = davis name %-23s #K HOST = tucana computer %-23s # #N ID XCENTER YCENTER MAG MERR MSKY NITER \\ #U ## pixels pixels magnitudes magnitudes counts ## \\ - #F %-9d %-10.3f %-10.3f %-12.3f %-14.3f %-15.7g %-6d + #F %-9d %-10.3f %-10.3f %-12.3f %-14.3f %-15.7g %-6d # #N SHARPNESS CHI PIER PERROR \\ #U ## ## ## perrors \\ #F %-23.3f %-12.3f %-6d %-13s # - 14 138.538 256.405 15.461 0.003 34.85955 4 \\ - -0.032 0.802 0 No_error + 14 138.538 INDEF 15.461 0.003 34.85955 4 \\ + -0.032 0.802 0 No_error - The keywords defined in the #K records are available via the Daophot reader object:: + The keywords defined in the #K records are available via the output table + ``meta`` attribute:: - reader = asciitable.get_reader(Reader=asciitable.Daophot) - data = reader.read('t/daophot.dat') - for keyword in reader.keywords: - print keyword.name, keyword.value, keyword.units, keyword.format - - """ - - def __init__(self): - core.BaseReader.__init__(self) - self.header = DaophotHeader() - self.inputter = core.ContinuationLinesInputter() - self.data.splitter.delimiter = ' ' - self.data.start_line = 0 - self.data.comment = r'\s*#' - - def read(self, table): - output = core.BaseReader.read(self, table) - reader = core._get_reader(Reader=basic.NoHeader, comment=r'(?!#K)', - names = ['temp1','keyword','temp2','value','unit','format']) - headerkeywords = reader.read(self.comment_lines) - - for line in headerkeywords: - self.keywords.append(core.Keyword(line['keyword'], line['value'], - units=line['unit'], format=line['format'])) - self.table = output - self.cols = self.header.cols - - return self.table + >>> import os + >>> from astropy.io import ascii + >>> filename = os.path.join(ascii.__path__[0], 'tests/data/daophot.dat') + >>> data = ascii.read(filename) + >>> for name, keyword in data.meta['keywords'].items(): + ... print(name, keyword['value'], keyword['units'], keyword['format']) + ... + MERGERAD INDEF scaleunit %-23.7g + IRAF NOAO/IRAFV2.10EXPORT version %-23s + USER name %-23s + ... - def write(self, table=None): - raise NotImplementedError + The unit and formats are available in the output table columns:: + >>> for colname in data.colnames: + ... col = data[colname] + ... print(colname, col.unit, col.format) + ... + ID None %-9d + XCENTER pixels %-10.3f + YCENTER pixels %-10.3f + ... -class DaophotHeader(core.BaseHeader): - """Read the header from a file produced by the IRAF DAOphot routine.""" - def __init__(self): - core.BaseHeader.__init__(self) - self.comment = r'\s*#K' + Any column values of INDEF are interpreted as a missing value and will be + masked out in the resultant table. - def get_cols(self, lines): - """Initialize the header Column objects from the table ``lines`` for a DAOphot - header. The DAOphot header is specialized so that we just copy the entire BaseHeader - get_cols routine and modify as needed. + In case of multi-aperture daophot files containing repeated entries for the last + row of fields, extra unique column names will be created by suffixing + corresponding field names with numbers starting from 2 to N (where N is the + total number of apertures). + For example, + first aperture radius will be RAPERT and corresponding magnitude will be MAG, + second aperture radius will be RAPERT2 and corresponding magnitude will be MAG2, + third aperture radius will be RAPERT3 and corresponding magnitude will be MAG3, + and so on. - :param lines: list of table lines - :returns: list of table Columns - """ + """ - self.names = [] - re_name_def = re.compile(r'#N([^#]+)#') - for line in lines: - if not line.startswith('#'): - break # End of header lines - else: - match = re_name_def.search(line) - if match: - self.names.extend(match.group(1).split()) - - if not self.names: - raise core.InconsistentTableError('No column names found in DAOphot header') - - self._set_cols_from_names() + _format_name = "daophot" + _io_registry_format_aliases = ["daophot"] + _io_registry_can_write = False + _description = "IRAF DAOphot format table" + + header_class = DaophotHeader + data_class = DaophotData + inputter_class = DaophotInputter + table_width = 80 + + def __init__(self): + core.BaseReader.__init__(self) + # The inputter needs to know about the data (see DaophotInputter.process_lines) + self.inputter.data = self.data + + def write(self, table=None): + raise NotImplementedError diff --git a/astropy/io/ascii/docs.py b/astropy/io/ascii/docs.py new file mode 100644 index 000000000000..5ef32ad4f612 --- /dev/null +++ b/astropy/io/ascii/docs.py @@ -0,0 +1,201 @@ +READ_DOCSTRING = """ + Read the input ``table`` and return the table. Most of the default behavior for + various parameters is determined by the ``format`` argument. + + Help on the ``read()`` function arguments is available as shown in this example:: + + from astropy.io import ascii + ascii.read.help() # Common help for all formats + ascii.read.help("html") # Common help plus "html" format-specific args + + See also: + + - https://docs.astropy.org/en/stable/io/ascii/ + - https://docs.astropy.org/en/stable/io/ascii/read.html + + Parameters + ---------- + table : str, file-like, list, `pathlib.Path` object + Input table as a file name, file-like object, list of string[s], + single newline-separated string or `pathlib.Path` object. + guess : bool + Try to guess the table format. Defaults to None. + format : str, `~astropy.io.ascii.BaseReader` + Input table format + delimiter : str + Column delimiter string + comment : str + Regular expression defining a comment line in table + quotechar : str + One-character string to quote fields containing special characters + header_start : int + Line index for the header line not counting comment or blank lines. + A line with only whitespace is considered blank. + data_start : int + Line index for the start of data not counting comment or blank lines. + A line with only whitespace is considered blank. + data_end : int + Line index for the end of data not counting comment or blank lines. + This value can be negative to count from the end. + converters : dict + Dictionary of converters to specify output column dtypes. Each key in + the dictionary is a column name or else a name matching pattern + including wildcards. The value is either a data type such as ``int`` or + ``np.float32``; a list of such types which is tried in order until a + successful conversion is achieved; or a list of converter tuples (see + the `~astropy.io.ascii.convert_numpy` function for details). + names : list + List of names corresponding to each data column + include_names : list + List of names to include in output. + exclude_names : list + List of names to exclude from output (applied after ``include_names``) + fill_values : tuple, list of tuple + specification of fill values for bad or missing table values + fill_include_names : list + List of names to include in fill_values. + fill_exclude_names : list + List of names to exclude from fill_values (applied after ``fill_include_names``) + fast_reader : bool, str or dict + Whether to use the C engine, can also be a dict with options which + defaults to `False`; parameters for options dict: + + use_fast_converter: bool + enable faster but slightly imprecise floating point conversion method + exponent_style: str + One-character string defining the exponent or ``'Fortran'`` to auto-detect + Fortran-style scientific notation like ``'3.14159D+00'`` (``'E'``, ``'D'``, ``'Q'``), + all case-insensitive; default ``'E'``, all other imply ``use_fast_converter`` + chunk_size : int + If supplied with a value > 0 then read the table in chunks of + approximately ``chunk_size`` bytes. Default is reading table in one pass. + chunk_generator : bool + If True and ``chunk_size > 0`` then return an iterator that returns a + table for each chunk. The default is to return a single stacked table + for all the chunks. + + encoding : str + Allow to specify encoding to read the file (default= ``None``). + + Other Parameters + ---------------- + inputter_cls : `~astropy.io.ascii.BaseInputter` + Inputter class + outputter_cls : `~astropy.io.ascii.BaseOutputter` + Outputter class + data_splitter_cls : `~astropy.io.ascii.BaseSplitter` + Splitter class to split data columns + header_splitter_cls : `~astropy.io.ascii.BaseSplitter` + Splitter class to split header columns + + Returns + ------- + dat : `~astropy.table.Table` or + Output table + + """ + +# Specify allowed types for core read() keyword arguments. Each entry +# corresponds to the name of an argument and either a type (e.g. int) or a +# list of types. These get used in io.ascii.ui._validate_read_write_kwargs(). +# - The commented-out kwargs are too flexible for a useful check +# - 'list-list' is a special case for an iterable that is not a string. +READ_KWARG_TYPES = { + # 'table' + "guess": bool, + # 'format' + # 'reader_cls' + # 'inputter_cls' + # 'outputter_cls' + "delimiter": str, + "comment": str, + "quotechar": str, + "header_start": int, + "data_start": (int, str), # CDS allows 'guess' + "data_end": int, + "converters": dict, + # 'data_splitter_cls' + # 'header_splitter_cls' + "names": "list-like", + "include_names": "list-like", + "exclude_names": "list-like", + "fill_values": "list-like", + "fill_include_names": "list-like", + "fill_exclude_names": "list-like", + "fast_reader": (bool, str, dict), + "encoding": str, +} + + +WRITE_DOCSTRING = """ + Write the input ``table`` to ``filename``. Most of the default behavior + for various parameters is determined by the Writer class. + + Help on the ``write()`` function arguments is available as shown in this example:: + + from astropy.io import ascii + ascii.write.help() # Common help for all formats + ascii.write.help("html") # Common help plus "html" format-specific args + + See also: + + - https://docs.astropy.org/en/stable/io/ascii/ + - https://docs.astropy.org/en/stable/io/ascii/write.html + + Parameters + ---------- + table : `~astropy.io.ascii.BaseReader`, array-like, str, file-like, list + Input table as a Reader object, Numpy struct array, file name, + file-like object, list of strings, or single newline-separated string. + output : str, file-like + Output [filename, file-like object]. Defaults to``sys.stdout``. + format : str + Output table format. Defaults to 'basic'. + delimiter : str + Column delimiter string + comment : str, bool + String defining a comment line in table. If `False` then comments + are not written out. + quotechar : str + One-character string to quote fields containing special characters + formats : dict + Dictionary of format specifiers or formatting functions + strip_whitespace : bool + Strip surrounding whitespace from column values. + names : list + List of names corresponding to each data column + include_names : list + List of names to include in output. + exclude_names : list + List of names to exclude from output (applied after ``include_names``) + fast_writer : bool, str + Whether to use the fast Cython writer. Can be `True` (use fast writer + if available), `False` (do not use fast writer), or ``'force'`` (use + fast writer and fail if not available, mostly for testing). + overwrite : bool + If ``overwrite=False`` (default) and the file exists, then an OSError + is raised. This parameter is ignored when the ``output`` arg is not a + string (e.g., a file object). + + """ +# Specify allowed types for core write() keyword arguments. Each entry +# corresponds to the name of an argument and either a type (e.g. int) or a +# list of types. These get used in io.ascii.ui._validate_read_write_kwargs(). +# - The commented-out kwargs are too flexible for a useful check +# - 'list-list' is a special case for an iterable that is not a string. +WRITE_KWARG_TYPES = { + # 'table' + # 'output' + "format": str, + "delimiter": str, + "comment": (str, bool), + "quotechar": str, + "header_start": int, + "formats": dict, + "strip_whitespace": (bool), + "names": "list-like", + "include_names": "list-like", + "exclude_names": "list-like", + "fast_writer": (bool, str), + "overwrite": (bool), +} diff --git a/astropy/io/ascii/ecsv.py b/astropy/io/ascii/ecsv.py new file mode 100644 index 000000000000..0d088e289c18 --- /dev/null +++ b/astropy/io/ascii/ecsv.py @@ -0,0 +1,549 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +""" +Define the Enhanced Character-Separated-Values (ECSV) which allows for reading and +writing all the meta data associated with an astropy Table object. +""" + +import csv +import json +import re +import warnings +from collections import OrderedDict + +import numpy as np + +from astropy.io.ascii.core import convert_numpy +from astropy.io.misc.ecsv import table_meta_as_dict +from astropy.table import meta, serialize +from astropy.utils.data_info import serialize_context_as +from astropy.utils.exceptions import AstropyUserWarning + +from . import basic, core + +ECSV_VERSION = "1.0" +DELIMITERS = (" ", ",") +ECSV_DATATYPES = ( + "bool", + "int8", + "int16", + "int32", + "int64", + "uint8", + "uint16", + "uint32", + "uint64", + "float16", + "float32", + "float64", + "float128", + "string", +) # Raise warning if not one of these standard dtypes + + +class InvalidEcsvDatatypeWarning(AstropyUserWarning): + """ + ECSV specific Astropy warning class. + """ + + +class ECSVHeaderSplitter(core.DefaultSplitter): + """Splitter for reading header that does not strip column names. + + For instance column names like " # weird "" name ", the ECSV header preserves + leading and trailing whitespace. + """ + + process_val = None + + +class ECSVHeaderSplitterQuoteAll(ECSVHeaderSplitter): + """Special case splitter used for writing header line to quote all the column names. + + This is used if the first column name starts with the ECSV comment regex or if any + column names have leading or trailing whitespace. See issue #18710. + """ + + quoting = csv.QUOTE_ALL + + +class EcsvHeader(basic.BasicHeader): + """Header class for which the column definition line starts with the + comment character. See the :class:`CommentedHeader` class for an example. + """ + + splitter_class = ECSVHeaderSplitter + + def process_lines(self, lines): + """Return only non-blank lines that start with the comment regexp. For these + lines strip out the matching characters and leading/trailing whitespace. + """ + re_comment = re.compile(self.comment) + for line in lines: + line = line.strip() + if not line: + continue + match = re_comment.match(line) + if match: + out = line[match.end() :] + if out: + yield out + else: + # Stop iterating on first failed match for a non-blank line + return + + def write(self, lines): + """ + Write header information in the ECSV ASCII format. + + This function is called at the point when preprocessing has been done to + convert the input table columns to `self.cols` which is a list of + `astropy.io.ascii.core.Column` objects. In particular `col.str_vals` + is available for each column with the string representation of each + column item for output. + + This format starts with a delimiter separated list of the column names + in order to make this format readable by humans and simple csv-type + readers. It then encodes the full table meta and column attributes and + meta as YAML and pretty-prints this in the header. Finally the + delimited column names are repeated again, for humans and readers that + look for the *last* comment line as defining the column names. + """ + if self.splitter.delimiter not in DELIMITERS: + raise ValueError( + "only space and comma are allowed for delimiter in ECSV format" + ) + + # Now assemble the header dict that will be serialized by the YAML dumper + header = {"cols": self.cols, "schema": "astropy-2.0"} + + if self.table_meta: + header["meta"] = OrderedDict(self.table_meta) + + # Set the delimiter only for the non-default option(s) + if self.splitter.delimiter != " ": + header["delimiter"] = self.splitter.delimiter + + header_yaml_lines = [ + f"%ECSV {ECSV_VERSION}", + "---", + ] + meta.get_yaml_from_header(header) + + lines.extend([self.write_comment + line for line in header_yaml_lines]) + + names = [col.info.name for col in self.cols] + # If first col name looks like ECSV header start (r"\s*#") or any have leading + # or trailing whitespace then quote all fields in the header line. + if (names and re.match(self.comment, names[0])) or any( + name.strip() != name for name in names + ): + splitter = ECSVHeaderSplitterQuoteAll() + splitter.delimiter = self.splitter.delimiter + else: + splitter = self.splitter # use default splitter + lines.append(splitter.join(names)) + + def write_comments(self, lines, meta): + """ + WRITE: Override the default write_comments to do nothing since this is handled + in the custom write method. + """ + + def update_meta(self, lines, meta): + """ + READ: Override the default update_meta to do nothing. This process is done + in get_cols() for this reader. + """ + + def get_cols(self, lines): + """ + READ: Initialize the header Column objects from the table ``lines``. + + Parameters + ---------- + lines : list + List of table lines + + """ + # Cache a copy of the original input lines before processing below + raw_lines = lines + + # Extract non-blank comment (header) lines with comment character stripped + lines = list(self.process_lines(lines)) + + # Validate that this is a ECSV file + ecsv_header_re = r"""%ECSV [ ] + (?P \d+) + \. (?P \d+) + \.? (?P \d+)? $""" + + no_header_msg = ( + 'ECSV header line like "# %ECSV " not found as first line.' + " This is required for a ECSV file." + ) + + if not lines: + raise core.InconsistentTableError(no_header_msg) + + match = re.match(ecsv_header_re, lines[0].strip(), re.VERBOSE) + if not match: + raise core.InconsistentTableError(no_header_msg) + + try: + header = meta.get_header_from_yaml(lines) + except meta.YamlParseError as e: + raise core.InconsistentTableError( + "unable to parse yaml in meta header" + ) from e + + self.table_meta = table_meta_as_dict(header) + + if "delimiter" in header: + delimiter = header["delimiter"] + if delimiter not in DELIMITERS: + raise ValueError( + "only space and comma are allowed for delimiter in ECSV format" + ) + self.splitter.delimiter = delimiter + self.data.splitter.delimiter = delimiter + + # Create the list of io.ascii column objects from `header` + header_cols = {x["name"]: x for x in header["datatype"]} + + self.names = [x["name"] for x in header["datatype"]] + + # Read the first non-commented line of table and split to get the CSV + # header column names. This is essentially what the Basic reader does. + try: + header_line = next(super().process_lines(raw_lines)) + header_names = next(self.splitter([header_line])) + except StopIteration: + # there are no non-commented lines + header_line = "" + header_names = [] + + # Check for consistency of the ECSV vs. CSV header column names + if header_names != self.names: + raise core.InconsistentTableError( + f"column names from ECSV header {self.names} do not " + f"match names from header line of CSV data {header_names}" + ) + + # BaseHeader method to create self.cols, which is a list of + # io.ascii.core.Column objects (*not* Table Column objects). + self._set_cols_from_names() + + # Transfer attributes from the column descriptor stored in the input + # header YAML metadata to the new columns to create this table. + for col in self.cols: + for attr in ("description", "format", "unit", "meta", "subtype"): + if attr in header_cols[col.name]: + setattr(col, attr, header_cols[col.name][attr]) + + col.dtype = header_cols[col.name]["datatype"] + # Warn if col dtype is not a valid ECSV datatype, but allow reading for + # back-compatibility with existing older files that have numpy datatypes + # like datetime64 or object or python str, which are not in the ECSV standard. + if col.dtype not in ECSV_DATATYPES: + msg = ( + f"unexpected datatype {col.dtype!r} of column {col.name!r} " + f"is not in allowed ECSV datatypes {ECSV_DATATYPES}. " + "Using anyway as a numpy dtype but beware since unexpected " + "results are possible." + ) + warnings.warn(msg, category=InvalidEcsvDatatypeWarning) + + # Subtype is written like "int64[2,null]" and we want to split this + # out to "int64" and [2, None]. + subtype = col.subtype + if subtype and "[" in subtype: + idx = subtype.index("[") + col.subtype = subtype[:idx] + col.shape = json.loads(subtype[idx:]) + + # Convert ECSV "string" to numpy "str" + for attr in ("dtype", "subtype"): + if getattr(col, attr) == "string": + setattr(col, attr, "str") + + # ECSV subtype of 'json' maps to numpy 'object' dtype + if col.subtype == "json": + col.subtype = "object" + + +def _check_dtype_is_str(col): + if col.dtype != "str": + raise ValueError(f'datatype of column {col.name!r} must be "string"') + + +class EcsvOutputter(core.TableOutputter): + """ + After reading the input lines and processing, convert the Reader columns + and metadata to an astropy.table.Table object. This overrides the default + converters to be an empty list because there is no "guessing" of the + conversion function. + """ + + default_converters = [] + + def __call__(self, cols, meta): + # Convert to a Table with all plain Column subclass columns + out = super().__call__(cols, meta) + + # If mixin columns exist (based on the special '__mixin_columns__' + # key in the table ``meta``), then use that information to construct + # appropriate mixin columns and remove the original data columns. + # If no __mixin_columns__ exists then this function just passes back + # the input table. + return serialize._construct_mixins_from_columns(out) + + def _convert_vals(self, cols): + """READ: Convert str_vals in `cols` to final arrays with correct dtypes. + + This is adapted from ``BaseOutputter._convert_vals``. In the case of ECSV + there is no guessing and all types are known in advance. A big change + is handling the possibility of JSON-encoded values, both unstructured + object data and structured values that may contain masked data. + """ + for col in cols: + try: + # 1-d or N-d object columns are serialized as JSON. + if col.subtype == "object": + _check_dtype_is_str(col) + col_vals = [json.loads(val) for val in col.str_vals] + col.data = np.empty([len(col_vals)] + col.shape, dtype=object) + col.data[...] = col_vals + + # Variable length arrays with shape (n, m, ..., *) for fixed + # n, m, .. and variable in last axis. Masked values here are + # not currently supported. + elif col.shape and col.shape[-1] is None: + _check_dtype_is_str(col) + + # Empty (blank) values in original ECSV are changed to "0" + # in str_vals with corresponding col.mask being created and + # set accordingly. Instead use an empty list here. + if hasattr(col, "mask"): + for idx in np.nonzero(col.mask)[0]: + col.str_vals[idx] = "[]" + + # Remake as a 1-d object column of numpy ndarrays or + # MaskedArray using the datatype specified in the ECSV file. + col_vals = [] + for str_val in col.str_vals: + obj_val = json.loads(str_val) # list or nested lists + try: + arr_val = np.array(obj_val, dtype=col.subtype) + except TypeError: + # obj_val has entries that are inconsistent with + # dtype. For a valid ECSV file the only possibility + # is None values (indicating missing values). + data = np.array(obj_val, dtype=object) + # Replace all the None with an appropriate fill value + mask = data == None + kind = np.dtype(col.subtype).kind + data[mask] = {"U": "", "S": b""}.get(kind, 0) + arr_val = np.ma.array(data.astype(col.subtype), mask=mask) + + col_vals.append(arr_val) + + col.shape = () + col.dtype = np.dtype(object) + # np.array(col_vals_arr, dtype=object) fails ?? so this workaround: + col.data = np.empty(len(col_vals), dtype=object) + col.data[:] = col_vals + + # Multidim columns with consistent shape (n, m, ...). These + # might be masked. + elif col.shape: + _check_dtype_is_str(col) + + # Change empty (blank) values in original ECSV to something + # like "[[null, null],[null,null]]" so subsequent JSON + # decoding works. Delete `col.mask` so that later code in + # core TableOutputter.__call__() that deals with col.mask + # does not run (since handling is done here already). + if hasattr(col, "mask"): + all_none_arr = np.full( + shape=col.shape, fill_value=None, dtype=object + ) + all_none_json = json.dumps(all_none_arr.tolist()) + for idx in np.nonzero(col.mask)[0]: + col.str_vals[idx] = all_none_json + del col.mask + + col_vals = [json.loads(val) for val in col.str_vals] + # Make a numpy object array of col_vals to look for None + # (masked values) + data = np.array(col_vals, dtype=object) + mask = data == None + if not np.any(mask): + # No None's, just convert to required dtype + col.data = data.astype(col.subtype) + else: + # Replace all the None with an appropriate fill value + kind = np.dtype(col.subtype).kind + data[mask] = {"U": "", "S": b""}.get(kind, 0) + # Finally make a MaskedArray with the filled data + mask + col.data = np.ma.array(data.astype(col.subtype), mask=mask) + + # Regular scalar value column + else: + if col.subtype: + warnings.warn( + f"unexpected subtype {col.subtype!r} set for column " + f"{col.name!r}, using dtype={col.dtype!r} instead.", + category=InvalidEcsvDatatypeWarning, + ) + converter_func, _ = convert_numpy(col.dtype) + col.data = converter_func(col.str_vals) + + if col.data.shape[1:] != tuple(col.shape): + raise ValueError( + "shape mismatch between value and column specifier" + ) + + except json.JSONDecodeError: + raise ValueError( + f"column {col.name!r} failed to convert: " + "column value is not valid JSON" + ) + except Exception as exc: + raise ValueError(f"column {col.name!r} failed to convert: {exc}") + + +class EcsvData(basic.BasicData): + def _set_fill_values(self, cols): + """READ: Set the fill values of the individual cols based on fill_values of BaseData. + + For ECSV handle the corner case of data that has been serialized using + the serialize_method='data_mask' option, which writes the full data and + mask directly, AND where that table includes a string column with zero-length + string entries ("") which are valid data. + + Normally the super() method will set col.fill_value=('', '0') to replace + blanks with a '0'. But for that corner case subset, instead do not do + any filling. + """ + super()._set_fill_values(cols) + + # Get the serialized columns spec. It might not exist and there might + # not even be any table meta, so punt in those cases. + try: + scs = self.header.table_meta["__serialized_columns__"] + except (AttributeError, KeyError): + return + + # Got some serialized columns, so check for string type and serialized + # as a MaskedColumn. Without 'data_mask', MaskedColumn objects are + # stored to ECSV as normal columns. + for col in cols: + if ( + col.dtype == "str" + and col.name in scs + and scs[col.name]["__class__"] == "astropy.table.column.MaskedColumn" + ): + col.fill_values = {} # No data value replacement + + def str_vals(self): + """WRITE: convert all values in table to a list of lists of strings. + + This version considerably simplifies the base method: + - No need to set fill values and column formats + - No per-item formatting, just use repr() + - Use JSON for object-type or multidim values + - Only Column or MaskedColumn can end up as cols here. + - Only replace masked values with "", not the generalized filling + """ + for col in self.cols: + if len(col.shape) > 1 or col.info.dtype.kind == "O": + + def format_col_item(idx): + obj = col[idx] + try: + obj = obj.tolist() + except AttributeError: + pass + return json.dumps(obj, separators=(",", ":")) + + else: + + def format_col_item(idx): + return str(col[idx]) + + try: + col.str_vals = [format_col_item(idx) for idx in range(len(col))] + except TypeError as exc: + raise TypeError( + f"could not convert column {col.info.name!r} to string: {exc}" + ) from exc + + # Replace every masked value in a 1-d column with an empty string. + # For multi-dim columns this gets done by JSON via "null". + if hasattr(col, "mask") and col.ndim == 1: + for idx in col.mask.nonzero()[0]: + col.str_vals[idx] = "" + + return [col.str_vals for col in self.cols] + + +class Ecsv(basic.Basic): + """ECSV (Enhanced Character Separated Values) format table. + + Th ECSV format allows for specification of key table and column meta-data, in + particular the data type and unit. + + See: https://github.com/astropy/astropy-APEs/blob/main/APE6.rst + + Examples + -------- + >>> from astropy.table import Table + >>> ecsv_content = '''# %ECSV 0.9 + ... # --- + ... # datatype: + ... # - {name: a, unit: m / s, datatype: int64, format: '%03d'} + ... # - {name: b, unit: km, datatype: int64, description: This is column b} + ... a b + ... 001 2 + ... 004 3 + ... ''' + + >>> Table.read(ecsv_content, format='ascii.ecsv') +
+ a b + m / s km + int64 int64 + ----- ----- + 001 2 + 004 3 + """ + + _format_name = "ecsv" + _description = "Enhanced CSV" + _io_registry_suffix = ".ecsv" + + header_class = EcsvHeader + data_class = EcsvData + outputter_class = EcsvOutputter + + max_ndim = None # No limit on column dimensionality + + def update_table_data(self, table): + """ + Update table columns in place if mixin columns are present. + + This is a hook to allow updating the table columns after name + filtering but before setting up to write the data. This is currently + only used by ECSV and is otherwise just a pass-through. + + Parameters + ---------- + table : `astropy.table.Table` + Input table for writing + + Returns + ------- + table : `astropy.table.Table` + Output table for writing + """ + with serialize_context_as("ecsv"): + return serialize.represent_mixins_as_columns(table) diff --git a/astropy/io/ascii/fastbasic.py b/astropy/io/ascii/fastbasic.py new file mode 100644 index 000000000000..ca7565799818 --- /dev/null +++ b/astropy/io/ascii/fastbasic.py @@ -0,0 +1,426 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +import copy +import re + +from astropy.table import Table +from astropy.utils.misc import _set_locale + +from . import core, cparser + + +class FastBasic(metaclass=core.MetaBaseReader): + """ + This class is intended to handle the same format addressed by the + ordinary :class:`Basic` writer, but it acts as a wrapper for underlying C + code and is therefore much faster. Unlike the other ASCII readers and + writers, this class is not very extensible and is restricted + by optimization requirements. + """ + + _format_name = "fast_basic" + _description = "Basic table with custom delimiter using the fast C engine" + _fast = True + fill_extra_cols = False + guessing = False + strict_names = False + + def __init__(self, default_kwargs={}, **user_kwargs): + # Make sure user does not set header_start to None for a reader + # that expects a non-None value (i.e. a number >= 0). This mimics + # what happens in the Basic reader. + if ( + default_kwargs.get("header_start", 0) is not None + and user_kwargs.get("header_start", 0) is None + ): + raise ValueError("header_start cannot be set to None for this Reader") + + # Set up kwargs and copy any user kwargs. Use deepcopy user kwargs + # since they may contain a dict item which would end up as a ref to the + # original and get munged later (e.g. in cparser.pyx validation of + # fast_reader dict). + kwargs = copy.deepcopy(default_kwargs) + kwargs.update(copy.deepcopy(user_kwargs)) + + delimiter = kwargs.pop("delimiter", " ") + self.delimiter = str(delimiter) if delimiter is not None else None + self.write_comment = kwargs.get("comment", "# ") + self.comment = kwargs.pop("comment", "#") + if self.comment is not None: + self.comment = str(self.comment) + self.quotechar = str(kwargs.pop("quotechar", '"')) + self.header_start = kwargs.pop("header_start", 0) + # If data_start is not specified, start reading + # data right after the header line + data_start_default = user_kwargs.get( + "data_start", self.header_start + 1 if self.header_start is not None else 1 + ) + self.data_start = kwargs.pop("data_start", data_start_default) + self.kwargs = kwargs + self.strip_whitespace_lines = True + self.strip_whitespace_fields = True + + def _read_header(self): + # Use the tokenizer by default -- this method + # can be overridden for specialized headers + self.engine.read_header() + + def read(self, table): + """ + Read input data (file-like object, filename, list of strings, or + single string) into a Table and return the result. + """ + if self.comment is not None and len(self.comment) != 1: + raise core.ParameterError("The C reader does not support a comment regex") + elif self.data_start is None: + raise core.ParameterError( + "The C reader does not allow data_start to be None" + ) + elif ( + self.header_start is not None + and self.header_start < 0 + and not isinstance(self, FastCommentedHeader) + ): + raise core.ParameterError( + "The C reader does not allow header_start to be " + "negative except for commented-header files" + ) + elif self.data_start < 0: + raise core.ParameterError( + "The C reader does not allow data_start to be negative" + ) + elif len(self.delimiter) != 1: + raise core.ParameterError("The C reader only supports 1-char delimiters") + elif len(self.quotechar) != 1: + raise core.ParameterError( + "The C reader only supports a length-1 quote character" + ) + elif "converters" in self.kwargs: + raise core.ParameterError( + "The C reader does not support passing specialized converters" + ) + elif "encoding" in self.kwargs: + raise core.ParameterError( + "The C reader does not use the encoding parameter" + ) + elif "outputter_cls" in self.kwargs: + raise core.ParameterError( + "The C reader does not use the outputter_cls parameter" + ) + elif "inputter_cls" in self.kwargs: + raise core.ParameterError( + "The C reader does not use the inputter_cls parameter" + ) + elif "data_splitter_cls" in self.kwargs or "header_splitter_cls" in self.kwargs: + raise core.ParameterError("The C reader does not use a Splitter class") + + self.strict_names = self.kwargs.pop("strict_names", False) + + # Process fast_reader kwarg, which may or may not exist (though ui.py will always + # pass this as a dict with at least 'enable' set). + fast_reader = self.kwargs.get("fast_reader", True) + if not isinstance(fast_reader, dict): + fast_reader = {} + + fast_reader.pop("enable", None) + self.return_header_chars = fast_reader.pop("return_header_chars", False) + # Put fast_reader dict back into kwargs. + self.kwargs["fast_reader"] = fast_reader + + self.engine = cparser.CParser( + table, + self.strip_whitespace_lines, + self.strip_whitespace_fields, + delimiter=self.delimiter, + header_start=self.header_start, + comment=self.comment, + quotechar=self.quotechar, + data_start=self.data_start, + fill_extra_cols=self.fill_extra_cols, + **self.kwargs, + ) + conversion_info = self._read_header() + self.check_header() + if conversion_info is not None: + try_int, try_float, try_string = conversion_info + else: + try_int = {} + try_float = {} + try_string = {} + + with _set_locale("C"): + data, comments = self.engine.read(try_int, try_float, try_string) + out = self.make_table(data, comments) + + if self.return_header_chars: + out.meta["__ascii_fast_reader_header_chars__"] = self.engine.header_chars + + return out + + def make_table(self, data, comments): + """Actually make the output table give the data and comments.""" + meta = {} + if comments: + meta["comments"] = comments + + names = core._deduplicate_names(self.engine.get_names()) + return Table(data, names=names, meta=meta) + + def check_header(self): + names = self.engine.get_header_names() or self.engine.get_names() + if self.strict_names: + # Impose strict requirements on column names (normally used in guessing) + bads = [" ", ",", "|", "\t", "'", '"'] + for name in names: + if ( + core._is_number(name) + or len(name) == 0 + or name[0] in bads + or name[-1] in bads + ): + raise ValueError( + f"Column name {name!r} does not meet strict name requirements" + ) + # When guessing require at least two columns + if self.guessing and len(names) <= 1: + raise ValueError( + f"Table format guessing requires at least two columns, got {names}" + ) + + def write(self, table, output): + """ + Use a fast Cython method to write table data to output, + where output is a filename or file-like object. + """ + self._write(table, output, {}) + + def _write( + self, table, output, default_kwargs, header_output=True, output_types=False + ): + # Fast writer supports only 1-d columns + core._check_multidim_table(table, max_ndim=1) + + write_kwargs = { + "delimiter": self.delimiter, + "quotechar": self.quotechar, + "strip_whitespace": self.strip_whitespace_fields, + "comment": self.write_comment, + } + write_kwargs.update(default_kwargs) + # user kwargs take precedence over default kwargs + write_kwargs.update(self.kwargs) + writer = cparser.FastWriter(table, **write_kwargs) + writer.write(output, header_output, output_types) + + +class FastCsv(FastBasic): + """ + A faster version of the ordinary :class:`Csv` writer that uses the + optimized C parsing engine. Note that this reader will append empty + field values to the end of any row with not enough columns, while + :class:`FastBasic` simply raises an error. + """ + + _format_name = "fast_csv" + _description = "Comma-separated values table using the fast C engine" + _fast = True + fill_extra_cols = True + + def __init__(self, **kwargs): + super().__init__({"delimiter": ",", "comment": None}, **kwargs) + + def write(self, table, output): + """ + Override the default write method of `FastBasic` to + output masked values as empty fields. + """ + self._write(table, output, {"fill_values": [(core.masked, "")]}) + + +class FastTab(FastBasic): + """ + A faster version of the ordinary :class:`Tab` reader that uses + the optimized C parsing engine. + """ + + _format_name = "fast_tab" + _description = "Tab-separated values table using the fast C engine" + _fast = True + + def __init__(self, **kwargs): + super().__init__({"delimiter": "\t"}, **kwargs) + self.strip_whitespace_lines = False + self.strip_whitespace_fields = False + + +class FastNoHeader(FastBasic): + """ + This class uses the fast C engine to read tables with no header line. If + the names parameter is unspecified, the columns will be autonamed with + "col{}". + """ + + _format_name = "fast_no_header" + _description = "Basic table with no headers using the fast C engine" + _fast = True + + def __init__(self, **kwargs): + super().__init__({"header_start": None, "data_start": 0}, **kwargs) + + def write(self, table, output): + """ + Override the default writing behavior in `FastBasic` so + that columns names are not included in output. + """ + self._write(table, output, {}, header_output=None) + + +class FastCommentedHeader(FastBasic): + """ + A faster version of the :class:`CommentedHeader` reader, which looks for + column names in a commented line. ``header_start`` denotes the index of + the header line among all commented lines and is 0 by default. + """ + + _format_name = "fast_commented_header" + _description = "Columns name in a commented line using the fast C engine" + _fast = True + + def __init__(self, **kwargs): + super().__init__({}, **kwargs) + # Mimic CommentedHeader's behavior in which data_start + # is relative to header_start if unspecified; see #2692 + if "data_start" not in kwargs: + self.data_start = 0 + + def make_table(self, data, comments): + """ + Actually make the output table give the data and comments. This is + slightly different from the base FastBasic method in the way comments + are handled. + """ + meta = {} + if comments: + idx = self.header_start + if idx < 0: + idx = len(comments) + idx + meta["comments"] = comments[:idx] + comments[idx + 1 :] + if not meta["comments"]: + del meta["comments"] + + names = core._deduplicate_names(self.engine.get_names()) + return Table(data, names=names, meta=meta) + + def _read_header(self): + tmp = self.engine.source + commented_lines = [] + + for line in tmp.splitlines(): + line = line.lstrip() + if line and line[0] == self.comment: # line begins with a comment + commented_lines.append(line[1:]) + if len(commented_lines) == self.header_start + 1: + break + + if len(commented_lines) <= self.header_start: + raise cparser.CParserError("not enough commented lines") + + self.engine.setup_tokenizer([commented_lines[self.header_start]]) + self.engine.header_start = 0 + self.engine.read_header() + self.engine.setup_tokenizer(tmp) + + def write(self, table, output): + """ + Override the default writing behavior in `FastBasic` so + that column names are commented. + """ + self._write(table, output, {}, header_output="comment") + + +class FastRdb(FastBasic): + """ + A faster version of the :class:`Rdb` reader. This format is similar to + tab-delimited, but it also contains a header line after the column + name line denoting the type of each column (N for numeric, S for string). + """ + + _format_name = "fast_rdb" + _description = "Tab-separated with a type definition header line" + _fast = True + + def __init__(self, **kwargs): + super().__init__({"delimiter": "\t", "data_start": 2}, **kwargs) + self.strip_whitespace_lines = False + self.strip_whitespace_fields = False + + def _read_header(self): + tmp = self.engine.source + line1 = "" + line2 = "" + for line in tmp.splitlines(): + # valid non-comment line + if not line1 and line.strip() and line.lstrip()[0] != self.comment: + line1 = line + elif not line2 and line.strip() and line.lstrip()[0] != self.comment: + line2 = line + break + else: # less than 2 lines in table + raise ValueError("RDB header requires 2 lines") + + # Tokenize the two header lines separately. + # Each call to self.engine.read_header by default + # - calls _deduplicate_names to ensure unique header_names + # - sets self.names from self.header_names if not provided as kwarg + # - applies self.include_names/exclude_names to self.names. + # For parsing the types disable 1+3, but self.names needs to be set. + self.engine.setup_tokenizer([line2]) + self.engine.header_start = 0 + self.engine.read_header(deduplicate=False, filter_names=False) + types = self.engine.get_header_names() + + # If no kwarg names have been passed, reset to have column names read from header line 1. + if types == self.engine.get_names(): + self.engine.set_names([]) + self.engine.setup_tokenizer([line1]) + # Get full list of column names prior to applying include/exclude_names, + # which have to be applied to the unique name set after deduplicate. + self.engine.read_header(deduplicate=True, filter_names=False) + col_names = self.engine.get_names() + self.engine.read_header(deduplicate=False) + if len(col_names) != len(types): + raise core.InconsistentTableError( + "RDB header mismatch between number of column names and column types" + ) + # If columns have been removed via include/exclude_names, extract matching types. + if len(self.engine.get_names()) != len(types): + types = [types[col_names.index(n)] for n in self.engine.get_names()] + + if any(not re.match(r"\d*(N|S)$", x, re.IGNORECASE) for x in types): + raise core.InconsistentTableError( + f"RDB type definitions do not all match [num](N|S): {types}" + ) + + try_int = {} + try_float = {} + try_string = {} + + for name, col_type in zip(self.engine.get_names(), types): + if col_type[-1].lower() == "s": + try_int[name] = 0 + try_float[name] = 0 + try_string[name] = 1 + else: + try_int[name] = 1 + try_float[name] = 1 + try_string[name] = 0 + + self.engine.setup_tokenizer(tmp) + return (try_int, try_float, try_string) + + def write(self, table, output): + """ + Override the default writing behavior in `FastBasic` to + output a line with column types after the column name line. + """ + self._write(table, output, {}, output_types=True) diff --git a/astropy/io/ascii/fixedwidth.py b/astropy/io/ascii/fixedwidth.py index 51e93ff6b9b2..a00cc6fc1d9d 100644 --- a/astropy/io/ascii/fixedwidth.py +++ b/astropy/io/ascii/fixedwidth.py @@ -1,4 +1,5 @@ -"""Asciitable: an extensible ASCII table reader and writer. +# Licensed under a 3-clause BSD style license - see LICENSE.rst +"""An extensible ASCII table reader and writer. fixedwidth.py: Read or write a table with fixed width columns. @@ -7,132 +8,119 @@ :Author: Tom Aldcroft (aldcroft@head.cfa.harvard.edu) """ -## -## Redistribution and use in source and binary forms, with or without -## modification, are permitted provided that the following conditions are met: -## * Redistributions of source code must retain the above copyright -## notice, this list of conditions and the following disclaimer. -## * Redistributions in binary form must reproduce the above copyright -## notice, this list of conditions and the following disclaimer in the -## documentation and/or other materials provided with the distribution. -## * Neither the name of the Smithsonian Astrophysical Observatory nor the -## names of its contributors may be used to endorse or promote products -## derived from this software without specific prior written permission. -## -## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -## ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -## WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -## DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY -## DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -## (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -## LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -## ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -## SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import re -import itertools -from . import core -from .core import io, next, izip, any +from . import basic, core +from .core import DefaultSplitter, InconsistentTableError + class FixedWidthSplitter(core.BaseSplitter): - """Split line based on fixed start and end positions for each ``col`` in + """ + Split line based on fixed start and end positions for each ``col`` in ``self.cols``. This class requires that the Header class will have defined ``col.start`` and ``col.end`` for each column. The reference to the ``header.cols`` gets put in the splitter object by the base Reader.read() function just in time - for splitting data lines by a ``data`` object. + for splitting data lines by a ``data`` object. Note that the ``start`` and ``end`` positions are defined in the pythonic style so line[start:end] is the desired substring for a column. This splitter class does not have a hook for ``process_lines`` since that is generally not useful for fixed-width input. + """ - delimiter_pad = '' + + delimiter_pad = "" bookend = False + delimiter = "|" def __call__(self, lines): for line in lines: - vals = [line[x.start:x.end] for x in self.cols] + vals = [line[x.start : x.end] for x in self.cols] if self.process_val: yield [self.process_val(x) for x in vals] else: yield vals def join(self, vals, widths): - pad = self.delimiter_pad or '' - delimiter = self.delimiter or '' + pad = self.delimiter_pad or "" + delimiter = self.delimiter or "" padded_delim = pad + delimiter + pad if self.bookend: bookend_left = delimiter + pad bookend_right = pad + delimiter else: - bookend_left = '' - bookend_right = '' - vals = [' ' * (width - len(val)) + val for val, width in zip(vals, widths)] + bookend_left = "" + bookend_right = "" + vals = [" " * (width - len(val)) + val for val, width in zip(vals, widths)] return bookend_left + padded_delim.join(vals) + bookend_right -class FixedWidthHeader(core.BaseHeader): - """Fixed width table header reader. - - The key settable class attributes are: - - :param auto_format: format string for auto-generating column names - :param start_line: None, int, or a function of ``lines`` that returns None or int - :param comment: regular expression for comment lines - :param splitter_class: Splitter class for splitting data lines into columns - :param names: list of names corresponding to each data column - :param include_names: list of names to include in output (default=None selects all names) - :param exclude_names: list of names to exlude from output (applied after ``include_names``) - :param position_line: row index of line that specifies position (default = 1) - :param position_char: character used to write the position line (default = "-") - :param col_starts: list of start positions for each column (0-based counting) - :param col_ends: list of end positions (inclusive) for each column - :param delimiter_pad: padding around delimiter when writing (default = None) - :param bookend: put the delimiter at start and end of line when writing (default = False) +class FixedWidthHeaderSplitter(DefaultSplitter): + """Splitter class that splits on ``|``.""" + + delimiter = "|" + + +class FixedWidthHeader(basic.BasicHeader): + """ + Fixed width table header reader. """ - position_line = None # secondary header line position + splitter_class = FixedWidthHeaderSplitter + """ Splitter class for splitting data lines into columns """ + position_line = None # secondary header line position + """ row index of line that specifies position (default = 1) """ + set_of_position_line_characters = set(r"""`~!#$%^&*-_+=\|":'""") def get_line(self, lines, index): for i, line in enumerate(self.process_lines(lines)): if i == index: break - else: # No header line matching - raise InconsistentTableError('No header line found in table') + else: # No header line matching + raise InconsistentTableError("No header line found in table") return line def get_cols(self, lines): - """Initialize the header Column objects from the table ``lines``. + """ + Initialize the header Column objects from the table ``lines``. Based on the previously set Header attributes find or create the column names. - Sets ``self.cols`` with the list of Columns. This list only includes the actual - requested columns after filtering by the include_names and exclude_names - attributes. See ``self.names`` for the full list. + Sets ``self.cols`` with the list of Columns. + + Parameters + ---------- + lines : list + List of table lines - :param lines: list of table lines - :returns: None """ + header_rows = getattr(self, "header_rows", ["name"]) # See "else" clause below for explanation of start_line and position_line start_line = core._get_line_index(self.start_line, self.process_lines(lines)) - position_line = core._get_line_index(self.position_line, self.process_lines(lines)) + position_line = core._get_line_index( + self.position_line, self.process_lines(lines) + ) # If start_line is none then there is no header line. Column positions are # determined from first data line and column names are either supplied by user # or auto-generated. if start_line is None: if position_line is not None: - raise ValueError("Cannot set position_line without also setting header_start") - data_lines = self.data.process_lines(lines) + raise ValueError( + "Cannot set position_line without also setting header_start" + ) + + # data.data_lines attribute already set via self.data.get_data_lines(lines) + # in BaseReader.read(). This includes slicing for data_start / data_end. + data_lines = self.data.data_lines + if not data_lines: - raise InconsistentTableError('No data lines found so cannot autogenerate column names') + raise InconsistentTableError( + "No data lines found so cannot autogenerate column names" + ) vals, starts, ends = self.get_fixedwidth_params(data_lines[0]) - if self.names is None: - self.names = [self.auto_format % i for i in range(1, len(vals) + 1)] + self.names = [self.auto_format.format(i) for i in range(1, len(vals) + 1)] else: # This bit of code handles two cases: @@ -150,51 +138,83 @@ def get_cols(self, lines): # slice col_ends but expects inclusive col_ends on input (for # more intuitive user interface). line = self.get_line(lines, position_line) + if len(set(line) - {self.splitter.delimiter, " "}) != 1: + raise InconsistentTableError( + "Position line should only contain delimiters and " + 'one other character, e.g. "--- ------- ---".' + ) + # The line above lies. It accepts white space as well. + # We don't want to encourage using three different + # characters, because that can cause ambiguities, but white + # spaces are so common everywhere that practicality beats + # purity here. + charset = self.set_of_position_line_characters.union( + {self.splitter.delimiter, " "} + ) + if not set(line).issubset(charset): + raise InconsistentTableError( + f"Characters in position line must be part of {charset}" + ) vals, self.col_starts, col_ends = self.get_fixedwidth_params(line) - self.col_ends = [x - 1 for x in col_ends] - - # Get the header column names and column positions - line = self.get_line(lines, start_line) - vals, starts, ends = self.get_fixedwidth_params(line) - - # Possibly override the column names with user-supplied values - if self.names is None: - self.names = vals - - # Filter self.names using include_names and exclude_names, then create - # the actual Column objects. + self.col_ends = [x - 1 if x is not None else None for x in col_ends] + + # Get the column names from the header line + line = self.get_line(lines, start_line + header_rows.index("name")) + self.names, starts, ends = self.get_fixedwidth_params(line) + self._set_cols_from_names() - self.n_data_cols = len(self.cols) - - # Set column start and end positions. Also re-index the cols because - # the FixedWidthSplitter does NOT return the ignored cols (as is the - # case for typical delimiter-based splitters) + + for ii, attr in enumerate(header_rows): + if attr != "name": + line = self.get_line(lines, start_line + ii) + vals = self.get_fixedwidth_params(line)[0] + for col, val in zip(self.cols, vals): + if val: + setattr(col, attr, val) + + # Set column start and end positions. for i, col in enumerate(self.cols): - col.start = starts[col.index] - col.end = ends[col.index] - col.index = i + col.start = starts[i] + col.end = ends[i] def get_fixedwidth_params(self, line): - """Split ``line`` on the delimiter and determine column values and + """ + Split ``line`` on the delimiter and determine column values and column start and end positions. This might include null columns with zero length (e.g. for ``header row = "| col1 || col2 | col3 |"`` or ``header2_row = "----- ------- -----"``). The null columns are stripped out. Returns the values between delimiters and the corresponding start and end positions. - :param line: input line - :returns: (vals, starts, ends) - """ + Parameters + ---------- + line : str + Input line + + Returns + ------- + vals : list + List of values. + starts : list + List of starting indices. + ends : list + List of ending indices. - # If column positions are already specified then just use those, otherwise - # figure out positions between delimiters. + """ + # If column positions are already specified then just use those. + # If neither column starts or ends are given, figure out positions + # between delimiters. Otherwise, either the starts or the ends have + # been given, so figure out whichever wasn't given. if self.col_starts is not None and self.col_ends is not None: starts = list(self.col_starts) # could be any iterable, e.g. np.array - ends = [x + 1 for x in self.col_ends] # user supplies inclusive endpoint + # user supplies inclusive endpoint + ends = [x + 1 if x is not None else None for x in self.col_ends] if len(starts) != len(ends): - raise ValueError('Fixed width col_starts and col_ends must have the same length') + raise ValueError( + "Fixed width col_starts and col_ends must have the same length" + ) vals = [line[start:end].strip() for start, end in zip(starts, ends)] - else: + elif self.col_starts is None and self.col_ends is None: # There might be a cleaner way to do this but it works... vals = line.split(self.splitter.delimiter) starts = [0] @@ -208,7 +228,16 @@ def get_fixedwidth_params(self, line): starts = starts[:-1] vals = [x.strip() for x in vals if x] if len(vals) != len(starts) or len(vals) != len(ends): - raise InconsistentTableError('Error parsing fixed width header') + raise InconsistentTableError("Error parsing fixed width header") + else: + # exactly one of col_starts or col_ends is given... + if self.col_starts is not None: + starts = list(self.col_starts) + ends = starts[1:] + [None] # Assume each col ends where the next starts + else: # self.col_ends is not None + ends = [x + 1 for x in self.col_ends] + starts = [0] + ends[:-1] # Assume each col starts where the last ended + vals = [line[start:end].strip() for start, end in zip(starts, ends)] return vals, starts, ends @@ -218,46 +247,49 @@ def write(self, lines): pass -class FixedWidthData(core.BaseData): - """Base table data reader. - - :param start_line: None, int, or a function of ``lines`` that returns None or int - :param end_line: None, int, or a function of ``lines`` that returns None or int - :param comment: Regular expression for comment lines - :param splitter_class: Splitter class for splitting data lines into columns +class FixedWidthData(basic.BasicData): + """ + Base table data reader. """ splitter_class = FixedWidthSplitter + """ Splitter class for splitting data lines into columns """ + start_line = None def write(self, lines): - formatters = [] - for col in self.cols: - formatter = self.formats.get(col.name, self.default_formatter) - if not hasattr(formatter, '__call__'): - formatter = core._format_func(formatter) - col.formatter = formatter - - vals_list = [] - # Col iterator does the formatting defined above so each val is a string - # and vals is a tuple of strings for all columns of each row - for vals in izip(*self.cols): - vals_list.append(vals) - - for i, col in enumerate(self.cols): - col.width = max([len(vals[i]) for vals in vals_list]) - if self.header.start_line is not None: - col.width = max(col.width, len(col.name)) - - widths = [col.width for col in self.cols] - - if self.header.start_line is not None: - lines.append(self.splitter.join([col.name for col in self.cols], widths)) + default_header_rows = [] if self.header.start_line is None else ["name"] + header_rows = getattr(self, "header_rows", default_header_rows) + # First part is getting the widths of each column. + # List (rows) of list (column values) for data lines + vals_list = list(zip(*self.str_vals())) + + # List (rows) of list (columns values) for header lines. + hdrs_list = [] + for col_attr in header_rows: + vals = [ + "" if (val := getattr(col.info, col_attr)) is None else str(val) + for col in self.cols + ] + hdrs_list.append(vals) + + # Widths for data columns + widths = [ + max(len(vals[i_col]) for vals in vals_list) + for i_col in range(len(self.cols)) + ] + # Incorporate widths for header columns (if there are any) + if hdrs_list: + for i_col in range(len(self.cols)): + widths[i_col] = max( + widths[i_col], *(len(vals[i_col]) for vals in hdrs_list) + ) + + # Now collect formatted header and data lines into the output lines + for vals in hdrs_list: + lines.append(self.splitter.join(vals, widths)) if self.header.position_line is not None: - char = self.header.position_char - if len(char) != 1: - raise ValueError('Position_char="%s" must be a single character' % char) - vals = [char * col.width for col in self.cols] + vals = [self.header.position_char * width for width in widths] lines.append(self.splitter.join(vals, widths)) for vals in vals_list: @@ -266,97 +298,145 @@ def write(self, lines): return lines -class FixedWidth(core.BaseReader): - """Read or write a fixed width table with a single header line that defines column - names and positions. Examples:: +class FixedWidth(basic.Basic): + """Fixed width table with single header line defining column names and positions. + + Examples:: # Bar delimiter in header and data - + | Col1 | Col2 | Col3 | | 1.2 | hello there | 3 | | 2.4 | many words | 7 | - + # Bar delimiter in header only - - Col1 | Col2 | Col3 - 1.2 hello there 3 - 2.4 many words 7 - + + Col1 | Col2 | Col3 + 1.2 hello there 3 + 2.4 many words 7 + # No delimiter with column positions specified as input - - Col1 Col2Col3 - 1.2hello there 3 - 2.4many words 7 - See the :ref:`fixed_width_gallery` for specific usage examples. + Col1 Col2Col3 + 1.2hello there 3 + 2.4many words 7 - :param col_starts: list of start positions for each column (0-based counting) - :param col_ends: list of end positions (inclusive) for each column - :param delimiter_pad: padding around delimiter when writing (default = None) - :param bookend: put the delimiter at start and end of line when writing (default = False) - """ - def __init__(self, col_starts=None, col_ends=None, delimiter_pad=' ', bookend=True): - core.BaseReader.__init__(self) + See the :ref:`astropy:fixed_width_gallery` for specific usage examples. - self.header = FixedWidthHeader() - self.data = FixedWidthData() - self.data.header = self.header - self.header.data = self.data + """ - self.header.splitter.delimiter = '|' - self.data.splitter.delimiter = '|' + _format_name = "fixed_width" + _description = "Fixed width" + + header_class = FixedWidthHeader + data_class = FixedWidthData + + def __init__( + self, + col_starts=None, + col_ends=None, + delimiter_pad=" ", + bookend=True, + header_rows=None, + ): + if header_rows is None: + header_rows = ["name"] + super().__init__() self.data.splitter.delimiter_pad = delimiter_pad self.data.splitter.bookend = bookend - self.header.start_line = 0 - self.data.start_line = 1 - self.header.comment = r'\s*#' - self.header.write_comment = '# ' - self.data.comment = r'\s*#' - self.data.write_comment = '# ' self.header.col_starts = col_starts self.header.col_ends = col_ends + self.header.header_rows = header_rows + self.data.header_rows = header_rows + if self.data.start_line is None: + self.data.start_line = len(header_rows) + + +class FixedWidthNoHeaderHeader(FixedWidthHeader): + """Header reader for fixed with tables with no header line.""" + + start_line = None + + +class FixedWidthNoHeaderData(FixedWidthData): + """Data reader for fixed width tables with no header line.""" + + start_line = 0 class FixedWidthNoHeader(FixedWidth): - """Read or write a fixed width table which has no header line. Column - names are either input (``names`` keyword) or auto-generated. Column - positions are determined either by input (``col_starts`` and ``col_stops`` - keywords) or by splitting the first data line. In the latter case a - ``delimiter`` is required to split the data line. + """Fixed width table which has no header line. + + When reading, column names are either input (``names`` keyword) or + auto-generated. Column positions are determined either by input + (``col_starts`` and ``col_stops`` keywords) or by splitting the first data + line. In the latter case a ``delimiter`` is required to split the data + line. Examples:: # Bar delimiter in header and data - + | 1.2 | hello there | 3 | | 2.4 | many words | 7 | - + # Compact table having no delimiter and column positions specified as input - 1.2hello there3 - 2.4many words 7 + 1.2hello there3 + 2.4many words 7 + + This class is just a convenience wrapper around the ``FixedWidth`` reader + but with ``header_start=None`` and ``data_start=0``. + + See the :ref:`astropy:fixed_width_gallery` for specific usage examples. + + """ + + _format_name = "fixed_width_no_header" + _description = "Fixed width with no header" + header_class = FixedWidthNoHeaderHeader + data_class = FixedWidthNoHeaderData - This class is just a convenience wrapper around :class:`~asciitable.FixedWidth` - but with ``header.start_line = None`` and ``data.start_line = 0``. + def __init__(self, col_starts=None, col_ends=None, delimiter_pad=" ", bookend=True): + super().__init__( + col_starts, + col_ends, + delimiter_pad=delimiter_pad, + bookend=bookend, + header_rows=[], + ) - See the :ref:`fixed_width_gallery` for specific usage examples. - :param col_starts: list of start positions for each column (0-based counting) - :param col_ends: list of end positions (inclusive) for each column - :param delimiter_pad: padding around delimiter when writing (default = None) - :param bookend: put the delimiter at start and end of line when writing (default = False) +class FixedWidthTwoLineHeader(FixedWidthHeader): + """Header reader for fixed width tables splitting on whitespace. + + For fixed width tables with several header lines, there is typically + a white-space delimited format line, so splitting on white space is + needed. """ - def __init__(self, col_starts=None, col_ends=None, delimiter_pad=' ', bookend=True): - FixedWidth.__init__(self, col_starts, col_ends, - delimiter_pad=delimiter_pad, bookend=bookend) - self.header.start_line = None - self.data.start_line = 0 - + splitter_class = DefaultSplitter + + +class FixedWidthTwoLineDataSplitter(FixedWidthSplitter): + """Splitter for fixed width tables splitting on ``' '``.""" + + delimiter = " " + + +class FixedWidthTwoLineData(FixedWidthData): + """Data reader for fixed with tables with two header lines.""" + + splitter_class = FixedWidthTwoLineDataSplitter + + class FixedWidthTwoLine(FixedWidth): - """Read or write a fixed width table which has two header lines. The first - header line defines the column names and the second implicitly defines the - column positions. Examples:: + """Fixed width table which has two header lines. + + The first header line defines the column names and the second implicitly + defines the column positions. + + Examples:: # Typical case with column extent defined by ---- under column names. @@ -365,7 +445,7 @@ class FixedWidthTwoLine(FixedWidth): 1 bee flies <== data_start = 2 2 fish swims - # Pretty-printed table + # Pretty-printed table +------+------------+ | Col1 | Col2 | @@ -374,19 +454,32 @@ class FixedWidthTwoLine(FixedWidth): | 2.4 | there world| +------+------------+ - See the :ref:`fixed_width_gallery` for specific usage examples. + See the :ref:`astropy:fixed_width_gallery` for specific usage examples. - :param position_line: row index of line that specifies position (default = 1) - :param position_char: character used to write the position line (default = "-") - :param delimiter_pad: padding around delimiter when writing (default = None) - :param bookend: put the delimiter at start and end of line when writing (default = False) """ - def __init__(self, position_line=1, position_char='-', delimiter_pad=None, bookend=False): - FixedWidth.__init__(self, delimiter_pad=delimiter_pad, bookend=bookend) + + _format_name = "fixed_width_two_line" + _description = "Fixed width with second header line" + data_class = FixedWidthTwoLineData + header_class = FixedWidthTwoLineHeader + + def __init__( + self, + position_line=None, + position_char="-", + delimiter_pad=None, + bookend=False, + header_rows=None, + ): + if len(position_char) != 1: + raise ValueError( + f'Position_char="{position_char}" must be a single character' + ) + super().__init__( + delimiter_pad=delimiter_pad, bookend=bookend, header_rows=header_rows + ) + if position_line is None: + position_line = len(self.header.header_rows) self.header.position_line = position_line self.header.position_char = position_char self.data.start_line = position_line + 1 - self.header.splitter.delimiter = ' ' - self.data.splitter.delimiter = ' ' - - diff --git a/astropy/io/ascii/html.py b/astropy/io/ascii/html.py new file mode 100644 index 000000000000..e9e695062d88 --- /dev/null +++ b/astropy/io/ascii/html.py @@ -0,0 +1,503 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +"""An extensible HTML table reader and writer. + +html.py: + Classes to read and write HTML tables + +`BeautifulSoup `_ +must be installed to read HTML tables. +""" + +import warnings +from copy import deepcopy + +from astropy.table import Column +from astropy.utils.compat.optional_deps import HAS_BS4 +from astropy.utils.xml import writer + +from . import core + + +class SoupString(str): + """ + Allows for strings to hold BeautifulSoup data. + """ + + def __new__(cls, *args, **kwargs): + return str.__new__(cls, *args, **kwargs) + + def __init__(self, val): + self.soup = val + + +class ListWriter: + """ + Allows for XMLWriter to write to a list instead of a file. + """ + + def __init__(self, out): + self.out = out + + def write(self, data): + self.out.append(data) + + +def identify_table(soup, htmldict, numtable): + """ + Checks whether the given BeautifulSoup tag is the table + the user intends to process. + """ + if soup is None or soup.name != "table": + return False # Tag is not a
+ + elif "table_id" not in htmldict: + return numtable == 1 + table_id = htmldict["table_id"] + + if isinstance(table_id, str): + return "id" in soup.attrs and soup["id"] == table_id + elif isinstance(table_id, int): + return table_id == numtable + + # Return False if an invalid parameter is given + return False + + +class HTMLInputter(core.BaseInputter): + """ + Input lines of HTML in a valid form. + + This requires `BeautifulSoup + `_ to be installed. + """ + + def process_lines(self, lines): + """ + Convert the given input into a list of SoupString rows + for further processing. + """ + if not HAS_BS4: + raise core.OptionalTableImportError( + "BeautifulSoup must be installed to read HTML tables" + ) + from bs4 import BeautifulSoup + + if "parser" not in self.html: + with warnings.catch_warnings(): + # Ignore bs4 parser warning #4550. + warnings.filterwarnings( + "ignore", ".*no parser was explicitly specified.*" + ) + soup = BeautifulSoup("\n".join(lines)) + else: # use a custom backend parser + soup = BeautifulSoup("\n".join(lines), self.html["parser"]) + tables = soup.find_all("table") + for i, possible_table in enumerate(tables): + if identify_table(possible_table, self.html, i + 1): + table = possible_table # Find the correct table + break + else: + if isinstance(self.html["table_id"], int): + err_descr = f"number {self.html['table_id']}" + else: + err_descr = f"id '{self.html['table_id']}'" + raise core.InconsistentTableError( + f"ERROR: HTML table {err_descr} not found" + ) + + # Get all table rows + return [SoupString(x) for x in table.find_all("tr")] + + +class HTMLSplitter(core.BaseSplitter): + """ + Split HTML table data. + """ + + def __call__(self, lines): + """ + Return HTML data from lines as a generator. + """ + for line in lines: + if not isinstance(line, SoupString): + raise TypeError("HTML lines should be of type SoupString") + soup = line.soup + header_elements = soup.find_all("th") + if header_elements: + # Return multicolumns as tuples for HTMLHeader handling + yield [ + (el.text.strip(), el["colspan"]) + if el.has_attr("colspan") + else el.text.strip() + for el in header_elements + ] + data_elements = soup.find_all("td") + if data_elements: + yield [el.text.strip() for el in data_elements] + if len(lines) == 0: + raise core.InconsistentTableError( + "HTML tables must contain data in a
tag" + ) + + +class HTMLOutputter(core.TableOutputter): + """ + Output the HTML data as an ``astropy.table.Table`` object. + + This subclass allows for the final table to contain + multidimensional columns (defined using the colspan attribute + of ", + "", + "", + "", + ] + assert [str(x) for x in inputter.get_lines(table)] == expected + + # Should raise an InconsistentTableError if the table is not found + inputter.html = {"table_id": 4} + with pytest.raises(core.InconsistentTableError): + inputter.get_lines(table) + + # Identification by string ID + inputter.html["table_id"] = "second" + expected = [ + "", + "", + "", + "", + ] + assert [str(x) for x in inputter.get_lines(table)] == expected + + # Identification by integer index + inputter.html["table_id"] = 3 + expected = [ + "", + "", + "", + "", + ] + assert [str(x) for x in inputter.get_lines(table)] == expected + + +@pytest.mark.skipif(not HAS_BS4, reason="requires BeautifulSoup4") +def test_htmlsplitter(): + """ + Test to make sure that HTMLSplitter correctly inputs lines + of type SoupString to return a generator that gives all + header and data elements. + """ + + splitter = html.HTMLSplitter() + + lines = [ + html.SoupString( + BeautifulSoup( + "
). + """ + + default_converters = [ + core.convert_numpy(int), + core.convert_numpy(float), + core.convert_numpy(str), + ] + + def __call__(self, cols, meta): + """ + Process the data in multidimensional columns. + """ + new_cols = [] + col_num = 0 + + while col_num < len(cols): + col = cols[col_num] + if hasattr(col, "colspan"): + # Join elements of spanned columns together into list of tuples + span_cols = cols[col_num : col_num + col.colspan] + new_col = core.Column(col.name) + new_col.str_vals = list(zip(*[x.str_vals for x in span_cols])) + new_cols.append(new_col) + col_num += col.colspan + else: + new_cols.append(col) + col_num += 1 + + return super().__call__(new_cols, meta) + + +class HTMLHeader(core.BaseHeader): + splitter_class = HTMLSplitter + + def start_line(self, lines): + """ + Return the line number at which header data begins. + """ + for i, line in enumerate(lines): + if not isinstance(line, SoupString): + raise TypeError("HTML lines should be of type SoupString") + soup = line.soup + if soup.th is not None: + return i + + return None + + def _set_cols_from_names(self): + """ + Set columns from header names, handling multicolumns appropriately. + """ + self.cols = [] + new_names = [] + + for name in self.names: + if isinstance(name, tuple): + col = core.Column(name=name[0]) + col.colspan = int(name[1]) + self.cols.append(col) + new_names.append(name[0]) + for i in range(1, int(name[1])): + # Add dummy columns + self.cols.append(core.Column("")) + new_names.append("") + else: + self.cols.append(core.Column(name=name)) + new_names.append(name) + + self.names = new_names + + +class HTMLData(core.BaseData): + splitter_class = HTMLSplitter + + def start_line(self, lines): + """ + Return the line number at which table data begins. + """ + for i, line in enumerate(lines): + if not isinstance(line, SoupString): + raise TypeError("HTML lines should be of type SoupString") + soup = line.soup + + if soup.td is not None: + if soup.th is not None: + raise core.InconsistentTableError( + "HTML tables cannot have headings and data in the same row" + ) + return i + + raise core.InconsistentTableError("No start line found for HTML data") + + def end_line(self, lines): + """ + Return the line number at which table data ends. + """ + last_index = -1 + + for i, line in enumerate(lines): + if not isinstance(line, SoupString): + raise TypeError("HTML lines should be of type SoupString") + soup = line.soup + if soup.td is not None: + last_index = i + + if last_index == -1: + return None + return last_index + 1 + + +class HTML(core.BaseReader): + """HTML format table. + + In order to customize input and output, a dict of parameters may + be passed to this class holding specific customizations. + + .. note:: + Be aware that in many cases reading tables from published HTML journal articles will not work + for a variety of reasons, including inconsistent mark-ups, CAPTCHAs, changing formats, + embedded javascript, or the table actually being an image. If possible you should consider + retrieving the table in a standard data format such as CSV or FITS, perhaps from an archive + such as CDS/Vizier. + + **htmldict** : Dictionary of parameters for HTML input/output. + + * css : Customized styling + If present, this parameter will be included in a + + + + + + + +
Column 1Column 2Column 3
1a1.05
2b2.75
3c-1.25
+ + + + + +
Column AColumn BColumn C
4d10.5
5e27.5
6f-12.5
+ + + + + +
C1C2C3
7g105.0
8h275.0
9i-125.0
+ + \ No newline at end of file diff --git a/astropy/io/ascii/tests/data/html2.html b/astropy/io/ascii/tests/data/html2.html new file mode 100644 index 000000000000..50b1fee0632c --- /dev/null +++ b/astropy/io/ascii/tests/data/html2.html @@ -0,0 +1,28 @@ + + + + + + + + +Row with no data elements + + + + + + + + + + + + + + + Some junk + +
AB
12.50000000000000000013
1a13.5
+ + diff --git a/astropy/io/ascii/tests/data/ipac.dat b/astropy/io/ascii/tests/data/ipac.dat new file mode 100644 index 000000000000..f7d51cd66ba3 --- /dev/null +++ b/astropy/io/ascii/tests/data/ipac.dat @@ -0,0 +1,12 @@ +\intval = 1 +\floatval=2.3e3 +\date = "Wed Sp 20 09:48:36 1995" +\key_continue = 'IPAC keywords ' +\key_continue = 'can continue across lines' +\ This is an example of a valid comment +| ra | dec | sai |-----v2---| sptype | +| real | real | int | real | char | +| unit | unit | unit | unit | ergs | +| null | null | -999 | null | -999 | + null 29.09056 -999 2.06000 -999 +12345678901234567890123456789012345678901234567890123456789012345 diff --git a/astropy/io/ascii/tests/data/ipac.dat.Z b/astropy/io/ascii/tests/data/ipac.dat.Z new file mode 100644 index 000000000000..46820a90d62d Binary files /dev/null and b/astropy/io/ascii/tests/data/ipac.dat.Z differ diff --git a/astropy/io/ascii/tests/data/ipac.dat.bz2 b/astropy/io/ascii/tests/data/ipac.dat.bz2 new file mode 100644 index 000000000000..a1dd3412aa5b Binary files /dev/null and b/astropy/io/ascii/tests/data/ipac.dat.bz2 differ diff --git a/astropy/io/ascii/tests/data/ipac.dat.xz b/astropy/io/ascii/tests/data/ipac.dat.xz new file mode 100644 index 000000000000..cf06f78fef00 Binary files /dev/null and b/astropy/io/ascii/tests/data/ipac.dat.xz differ diff --git a/astropy/io/ascii/tests/data/latex1.tex b/astropy/io/ascii/tests/data/latex1.tex new file mode 100644 index 000000000000..c45115196a51 --- /dev/null +++ b/astropy/io/ascii/tests/data/latex1.tex @@ -0,0 +1,10 @@ +\begin{table} +\caption{\ion{Ne}{ix} Ly series and \ion{Mg}{xi} triplet fluxes (errors are 5$1\sigma$ confidence intervals) \label{tab:nely}} + \begin{tabular}{lrr}\hline + cola & colb & colc\\ + \hline + a & 1 & 2\\ + b & 3 & 4\\ + \hline + \end{tabular} +\end{table} diff --git a/astropy/io/ascii/tests/data/latex1.tex.gz b/astropy/io/ascii/tests/data/latex1.tex.gz new file mode 100644 index 000000000000..da586f2bce20 Binary files /dev/null and b/astropy/io/ascii/tests/data/latex1.tex.gz differ diff --git a/astropy/io/ascii/tests/t/latex2.tex b/astropy/io/ascii/tests/data/latex2.tex similarity index 91% rename from astropy/io/ascii/tests/t/latex2.tex rename to astropy/io/ascii/tests/data/latex2.tex index f0d85ee16269..7f61c0af1354 100644 --- a/astropy/io/ascii/tests/t/latex2.tex +++ b/astropy/io/ascii/tests/data/latex2.tex @@ -6,9 +6,13 @@ \tablehead{\colhead{Facility} & \colhead{Id} & \colhead{exposure} & \colhead{date}} \startdata +\toprule Chandra & \dataset[ADS/Sa.CXO#obs/06438]{ObsId 6438} & 23 ks & 2006-12-10\\ +\midrule Spitzer & AOR 3656448 & 41.6 s & 2004-06-09\\ +\midrule FLWO & filter: $B$ & 600 s & 2009-11-18\\ +\bottomrule \enddata \end{deluxetable} diff --git a/astropy/io/ascii/tests/data/latex3.tex b/astropy/io/ascii/tests/data/latex3.tex new file mode 100644 index 000000000000..c52ff1d69dae --- /dev/null +++ b/astropy/io/ascii/tests/data/latex3.tex @@ -0,0 +1,8 @@ + \begin{tabular}{lrr}\hline +cola & colb & colc\\ +\hline +a & 1 & 2\\ +\midrule +b & 3 & 4\\ +\hline + \end{tabular} diff --git a/astropy/io/ascii/tests/t/nls1_stackinfo.dbout b/astropy/io/ascii/tests/data/nls1_stackinfo.dbout similarity index 100% rename from astropy/io/ascii/tests/t/nls1_stackinfo.dbout rename to astropy/io/ascii/tests/data/nls1_stackinfo.dbout diff --git a/astropy/io/ascii/tests/t/no_data_cds.dat b/astropy/io/ascii/tests/data/no_data_cds.dat similarity index 100% rename from astropy/io/ascii/tests/t/no_data_cds.dat rename to astropy/io/ascii/tests/data/no_data_cds.dat diff --git a/astropy/io/ascii/tests/t/no_data_daophot.dat b/astropy/io/ascii/tests/data/no_data_daophot.dat similarity index 93% rename from astropy/io/ascii/tests/t/no_data_daophot.dat rename to astropy/io/ascii/tests/data/no_data_daophot.dat index bea716b0bd5d..3fb49eff720f 100644 --- a/astropy/io/ascii/tests/t/no_data_daophot.dat +++ b/astropy/io/ascii/tests/data/no_data_daophot.dat @@ -1,7 +1,7 @@ #K MERGERAD = INDEF scaleunit %-23.7g #N ID XCENTER YCENTER MAG MERR MSKY NITER \ #U ## pixels pixels magnitudes magnitudes counts ## \ -#F %-9d %-10.3f %-10.3f %-12.3f %-14.3f %-15.7g %-6d # +#F %-9d %-10.3f %-10.3f %-12.3f %-14.3f %-15.7g %-6d #N SHARPNESS CHI PIER PERROR \ #U ## ## ## perrors \ -#F %-23.3f %-12.3f %-6d %-13s # +#F %-23.3f %-12.3f %-6d %-13s diff --git a/astropy/io/ascii/tests/t/no_data_ipac.dat b/astropy/io/ascii/tests/data/no_data_ipac.dat similarity index 100% rename from astropy/io/ascii/tests/t/no_data_ipac.dat rename to astropy/io/ascii/tests/data/no_data_ipac.dat diff --git a/astropy/io/ascii/tests/data/no_data_sextractor.dat b/astropy/io/ascii/tests/data/no_data_sextractor.dat new file mode 100644 index 000000000000..a9da39b1d773 --- /dev/null +++ b/astropy/io/ascii/tests/data/no_data_sextractor.dat @@ -0,0 +1,5 @@ +# 1 NUMBER Galaxy ID number +# 2 FLUX_ISO +# 3 FLUXERR_ISO +# 4 VALUES Note column 5 is missing +# 6 FLAG diff --git a/astropy/io/ascii/tests/t/no_data_with_header.dat b/astropy/io/ascii/tests/data/no_data_with_header.dat similarity index 100% rename from astropy/io/ascii/tests/t/no_data_with_header.dat rename to astropy/io/ascii/tests/data/no_data_with_header.dat diff --git a/astropy/io/ascii/tests/t/no_data_without_header.dat b/astropy/io/ascii/tests/data/no_data_without_header.dat similarity index 100% rename from astropy/io/ascii/tests/t/no_data_without_header.dat rename to astropy/io/ascii/tests/data/no_data_without_header.dat diff --git a/astropy/io/ascii/tests/data/profile_mesa.data b/astropy/io/ascii/tests/data/profile_mesa.data new file mode 100644 index 000000000000..d1bba704a749 --- /dev/null +++ b/astropy/io/ascii/tests/data/profile_mesa.data @@ -0,0 +1,11 @@ + 1 2 3 4 5 + model_number num_zones initial_mass initial_z star_age + 97859 3914 1.1154501210237844E+000 2.0000000000000000E-002 3.7224226375473790E+007 + + 1 2 3 + zone logT logRho + 1 6.0008301916978137E+000 -3.8469080693877298E+000 + 2 6.0235141275720263E+000 -3.8148777853833469E+000 + 3 6.0414750939774153E+000 -3.7837445910251999E+000 + 4 6.0564552892575501E+000 -3.7543335223653611E+000 + 5 6.0693692456762856E+000 -3.7268012634063457E+000 diff --git a/astropy/io/ascii/tests/data/sextractor.dat b/astropy/io/ascii/tests/data/sextractor.dat new file mode 100644 index 000000000000..59062194d6ef --- /dev/null +++ b/astropy/io/ascii/tests/data/sextractor.dat @@ -0,0 +1,8 @@ +# 1 NUMBER Galaxy ID number +# 2 FLUX_ISO +# 3 FLUXERR_ISO +# 4 VALU-ES Note column 5 is missing +# 6 FLAG +1 0.02580616000000000 0.03974229000000000 1.6770000000000000 0.2710000000000000 0 +2 5.72769100000000009 0.20643300000000001 2.6250000000000000 2.5219999999999998 0 +3 88.31933999999999685 0.59369850000000002 5.9249999999999998 4.7140000000000004 0 diff --git a/astropy/io/ascii/tests/data/sextractor2.dat b/astropy/io/ascii/tests/data/sextractor2.dat new file mode 100644 index 000000000000..679ea39c2ef6 --- /dev/null +++ b/astropy/io/ascii/tests/data/sextractor2.dat @@ -0,0 +1,14 @@ +# 1 NUMBER Running object number +# 2 XWIN_IMAGE Windowed position estimate along x [pixel] +# 3 YWIN_IMAGE Windowed position estimate along y [pixel] +# 4 MAG_AUTO Kron-like elliptical aperture magnitude [mag] +# 5 MAGERR_AUTO RMS error for AUTO magnitude [mag] +# 6 FLAGS Extraction flags +# 7 X2_IMAGE [pixel**2] +# 8 X_MAMA Barycenter position along MAMA x axis [m**(-6)] +# 9 MU_MAX Peak surface brightness above background [mag * arcsec**(-2)] +1 100.523 11.911 -5.3246 0.0416 19 1000.0 0.00304 -3.498 +2 100.660 4.872 -6.4538 0.0214 27 1500.0 0.00908 1.401 +3 131.046 10.382 -4.6836 0.0524 17 500.0 0.01004 2.512 +4 338.959 4.966 -7.1747 0.0173 25 1200.0 0.00792 2.901 +5 166.280 3.956 -4.0865 0.0621 25 800.0 0.00699 -6.489 diff --git a/astropy/io/ascii/tests/data/sextractor3.dat b/astropy/io/ascii/tests/data/sextractor3.dat new file mode 100644 index 000000000000..51adb21c3c3d --- /dev/null +++ b/astropy/io/ascii/tests/data/sextractor3.dat @@ -0,0 +1,10 @@ +# 1 X_IMAGE Object position along x [pixel] +# 2 Y_IMAGE [pixel] +# 3 ALPHA_J2000 Right ascension of barycenter (J2000) [deg] +# 4 DELTA_J2000 Declination of barycenter (J2000) [deg] +# 5 MAG_AUTO Kron-like elliptical aperture magnitude [mag] +# 6 MAGERR_AUTO RMS error for AUTO magnitude [mag] +# 7 MAG_APER Fixed aperture magnitude vector [mag] +# 14 MAGERR_APER RMS error vector for fixed aperture mag. [mag] + 1367.000 184.404 265.1445228 +68.7507679 22.9929 0.2218 24.1804 23.4541 22.9567 22.5162 22.1912 21.5363 21.0361 0.3262 0.2675 0.2203 0.1856 0.1683 0.1621 0.1673 + 1380.235 189.444 265.1384412 +68.7516124 20.9258 0.0569 22.2374 21.5987 21.2943 21.1244 20.9838 20.6672 20.0695 0.0645 0.0497 0.0495 0.0520 0.0533 0.0602 0.0515 diff --git a/astropy/io/ascii/tests/t/short.rdb b/astropy/io/ascii/tests/data/short.rdb similarity index 100% rename from astropy/io/ascii/tests/t/short.rdb rename to astropy/io/ascii/tests/data/short.rdb diff --git a/astropy/io/ascii/tests/data/short.rdb.Z b/astropy/io/ascii/tests/data/short.rdb.Z new file mode 100644 index 000000000000..f417807b3b99 Binary files /dev/null and b/astropy/io/ascii/tests/data/short.rdb.Z differ diff --git a/astropy/io/ascii/tests/data/short.rdb.bz2 b/astropy/io/ascii/tests/data/short.rdb.bz2 new file mode 100644 index 000000000000..9e6c49586d7f Binary files /dev/null and b/astropy/io/ascii/tests/data/short.rdb.bz2 differ diff --git a/astropy/io/ascii/tests/data/short.rdb.gz b/astropy/io/ascii/tests/data/short.rdb.gz new file mode 100644 index 000000000000..92172ff323db Binary files /dev/null and b/astropy/io/ascii/tests/data/short.rdb.gz differ diff --git a/astropy/io/ascii/tests/data/short.rdb.xz b/astropy/io/ascii/tests/data/short.rdb.xz new file mode 100644 index 000000000000..93faba985344 Binary files /dev/null and b/astropy/io/ascii/tests/data/short.rdb.xz differ diff --git a/astropy/io/ascii/tests/t/short.tab b/astropy/io/ascii/tests/data/short.tab similarity index 100% rename from astropy/io/ascii/tests/t/short.tab rename to astropy/io/ascii/tests/data/short.tab diff --git a/astropy/io/ascii/tests/t/simple.txt b/astropy/io/ascii/tests/data/simple.txt similarity index 100% rename from astropy/io/ascii/tests/t/simple.txt rename to astropy/io/ascii/tests/data/simple.txt diff --git a/astropy/io/ascii/tests/t/simple2.txt b/astropy/io/ascii/tests/data/simple2.txt similarity index 100% rename from astropy/io/ascii/tests/t/simple2.txt rename to astropy/io/ascii/tests/data/simple2.txt diff --git a/astropy/io/ascii/tests/t/simple3.txt b/astropy/io/ascii/tests/data/simple3.txt similarity index 100% rename from astropy/io/ascii/tests/t/simple3.txt rename to astropy/io/ascii/tests/data/simple3.txt diff --git a/astropy/io/ascii/tests/t/simple4.txt b/astropy/io/ascii/tests/data/simple4.txt similarity index 100% rename from astropy/io/ascii/tests/t/simple4.txt rename to astropy/io/ascii/tests/data/simple4.txt diff --git a/astropy/io/ascii/tests/t/simple5.txt b/astropy/io/ascii/tests/data/simple5.txt similarity index 100% rename from astropy/io/ascii/tests/t/simple5.txt rename to astropy/io/ascii/tests/data/simple5.txt diff --git a/astropy/io/ascii/tests/data/simple_csv.csv b/astropy/io/ascii/tests/data/simple_csv.csv new file mode 100644 index 000000000000..efb98239f9ac --- /dev/null +++ b/astropy/io/ascii/tests/data/simple_csv.csv @@ -0,0 +1,3 @@ +a,b,c +1,2,3 +4,5,6 \ No newline at end of file diff --git a/astropy/io/ascii/tests/data/simple_csv_missing.csv b/astropy/io/ascii/tests/data/simple_csv_missing.csv new file mode 100644 index 000000000000..9c87d2d85b63 --- /dev/null +++ b/astropy/io/ascii/tests/data/simple_csv_missing.csv @@ -0,0 +1,3 @@ +a,b,c +1 +4,5,6 diff --git a/astropy/io/ascii/tests/t/space_delim_blank_lines.txt b/astropy/io/ascii/tests/data/space_delim_blank_lines.txt similarity index 100% rename from astropy/io/ascii/tests/t/space_delim_blank_lines.txt rename to astropy/io/ascii/tests/data/space_delim_blank_lines.txt diff --git a/astropy/io/ascii/tests/t/space_delim_no_header.dat b/astropy/io/ascii/tests/data/space_delim_no_header.dat similarity index 100% rename from astropy/io/ascii/tests/t/space_delim_no_header.dat rename to astropy/io/ascii/tests/data/space_delim_no_header.dat diff --git a/astropy/io/ascii/tests/t/space_delim_no_names.dat b/astropy/io/ascii/tests/data/space_delim_no_names.dat similarity index 100% rename from astropy/io/ascii/tests/t/space_delim_no_names.dat rename to astropy/io/ascii/tests/data/space_delim_no_names.dat diff --git a/astropy/io/ascii/tests/data/subtypes.ecsv b/astropy/io/ascii/tests/data/subtypes.ecsv new file mode 100644 index 000000000000..089ca6985c2c --- /dev/null +++ b/astropy/io/ascii/tests/data/subtypes.ecsv @@ -0,0 +1,121 @@ +# %ECSV 1.0 +# --- +# delimiter: ',' +# datatype: +# - +# name: i_index +# datatype: int32 +# description: Row index +# - +# name: s_byte +# datatype: int8 +# - +# name: s_short +# datatype: int16 +# - +# name: s_int +# datatype: int32 +# - +# name: s_long +# datatype: int64 +# - +# name: s_float +# datatype: float32 +# - +# name: s_double +# datatype: float64 +# - +# name: s_string +# datatype: string +# - +# name: s_boolean +# datatype: bool +# - +# name: f_byte +# datatype: string +# subtype: 'int8[3]' +# - +# name: f_short +# datatype: string +# subtype: 'int16[3]' +# - +# name: f_int +# datatype: string +# subtype: 'int32[3]' +# - +# name: f_long +# datatype: string +# subtype: 'int64[3]' +# - +# name: f_float +# datatype: string +# subtype: 'float32[3]' +# - +# name: f_double +# datatype: string +# subtype: 'float64[3]' +# - +# name: f_string +# datatype: string +# subtype: 'string[3]' +# - +# name: f_boolean +# datatype: string +# subtype: 'bool[3]' +# - +# name: v_byte +# datatype: string +# subtype: 'int8[null]' +# - +# name: v_short +# datatype: string +# subtype: 'int16[null]' +# - +# name: v_int +# datatype: string +# subtype: 'int32[null]' +# - +# name: v_long +# datatype: string +# subtype: 'int64[null]' +# - +# name: v_float +# datatype: string +# subtype: 'float32[null]' +# - +# name: v_double +# datatype: string +# subtype: 'float64[null]' +# - +# name: v_string +# datatype: string +# subtype: 'string[null]' +# - +# name: v_boolean +# datatype: string +# subtype: 'bool[null]' +# - +# name: m_int +# datatype: string +# subtype: 'int32[4,2]' +# - +# name: m_double +# datatype: string +# subtype: 'float64[2,3]' +i_index,s_byte,s_short,s_int,s_long,s_float,s_double,s_string,s_boolean,f_byte,f_short,f_int,f_long,f_float,f_double,f_string,f_boolean,v_byte,v_short,v_int,v_long,v_float,v_double,v_string,v_boolean,m_int,m_double +0,0,0,0,0,0.0,0.0,zero,False,"[0,1,2]","[0,1,2]","[0,1,2]","[0,1,2]","[0.0,null,2.5]","[0.0,null,2.5]","[""foo"",null,""zero""]","[false,false,false]","[0,1,2]","[0,1,2]","[0,1,2]","[0,1,2]","[0.0,null,2.5]","[0.0,null,2.5]","[""foo"",null,""zero""]","[false,false,false]","[[1000,1001],[2000,2001],[3000,3001],[4000,4001]]","[[0.25,0.5,0.75],[-0.25,-0.5,-0.75]]" +1,,1,1,1,1.0,,one,True,,"[1,2,3]","[1,2,3]","[1,2,3]","[1.0,null,3.5]","[1.0,null,3.5]","[""foo"",null,""one""]","[true,false,false]",,"[1,2]","[1,2]","[1,2]","[1.0,null]","[1.0,null]","[""foo"",null]","[true,false]",,"[[1.25,1.5,1.75],[-1.25,-1.5,-1.75]]" +2,2,,2,2,,2.0,two,False,"[2,3,4]",,"[2,3,4]","[2,3,4]","[2.0,null,4.5]","[2.0,null,4.5]","[""foo"",null,""two""]","[false,true,false]",[2],,[2],[2],[2.0],[2.0],"[""foo""]",[false],"[[1002,1003],[2002,2003],[3002,3003],[4002,4003]]", +3,3,3,,3,3.0,3.0,three,True,"[3,4,5]","[3,4,5]",,"[3,4,5]","[3.0,null,5.5]","[3.0,null,5.5]","[""foo"",null,""three""]","[true,true,false]",[],[],,[],[],[],[],[],"[[1003,1004],[2003,2004],[3003,3004],[4003,4004]]","[[3.25,3.5,3.75],[-3.25,-3.5,-3.75]]" +4,4,4,4,,4.0,4.0,four,False,"[4,5,6]","[4,5,6]","[4,5,6]",,"[4.0,null,6.5]","[4.0,null,6.5]","[""foo"",null,""four""]","[false,false,true]","[4,5,6]","[4,5,6]","[4,5,6]",,"[4.0,null,6.5]","[4.0,null,6.5]","[""foo"",null,""four""]","[false,false,true]","[[1004,1005],[2004,2005],[3004,3005],[4004,4005]]","[[4.25,4.5,4.75],[-4.25,-4.5,-4.75]]" +5,5,5,5,5,nan,5.0,five,True,"[5,6,7]","[5,6,7]","[5,6,7]","[5,6,7]",,"[5.0,null,7.5]","[""foo"",null,""five""]","[true,false,true]","[5,6]","[5,6]","[5,6]","[5,6]",,"[5.0,null]","[""foo"",null]","[true,false]","[[1005,1006],[2005,2006],[3005,3006],[4005,4006]]","[[5.25,5.5,5.75],[-5.25,-5.5,-5.75]]" +6,6,6,6,6,6.0,nan,six,False,"[6,7,8]","[6,7,8]","[6,7,8]","[6,7,8]","[6.0,null,8.5]",,"[""foo"",null,""six""]","[false,true,true]",[6],[6],[6],[6],[6.0],,"[""foo""]",[false],"[[1006,1007],[2006,2007],[3006,3007],[4006,4007]]","[[6.25,6.5,6.75],[-6.25,-6.5,-6.75]]" +7,7,7,7,7,7.0,7.0,,True,"[7,8,9]","[7,8,9]","[7,8,9]","[7,8,9]","[7.0,null,9.5]","[7.0,null,9.5]",,"[true,true,true]",[],[],[],[],[],[],,[],"[[1007,1008],[2007,2008],[3007,3008],[4007,4008]]","[[7.25,7.5,7.75],[-7.25,-7.5,-7.75]]" +8,8,8,8,8,8.0,8.0,"' ""\""""' ; '&<>",,"[8,9,10]","[8,9,10]","[8,9,10]","[8,9,10]","[8.0,null,10.5]","[8.0,null,10.5]","[""foo"",null,""' \""\\\""\""' ; '&<>""]",,"[8,9,10]","[8,9,10]","[8,9,10]","[8,9,10]","[8.0,null,10.5]","[8.0,null,10.5]","[""foo"",null,""' \""\\\""\""' ; '&<>""]",,"[[1008,1009],[2008,2009],[3008,3009],[4008,4009]]","[[8.25,8.5,8.75],[-8.25,-8.5,-8.75]]" +9,9,9,9,9,nan,nan,,True,"[9,10,11]","[9,10,11]","[9,10,11]","[9,10,11]","[9.0,null,11.5]","[9.0,null,11.5]","[""foo"",null,""""]","[true,false,false]","[9,10]","[9,10]","[9,10]","[9,10]","[9.0,null]","[9.0,null]","[""foo"",null]","[true,false]","[[1009,1010],[2009,2010],[3009,3010],[4009,4010]]","[[9.25,9.5,9.75],[-9.25,-9.5,-9.75]]" +10,-10,-10,-10,-10,-10.0,-10.0,10,False,"[10,11,12]","[10,11,12]","[10,11,12]","[10,11,12]","[10.0,null,12.5]","[10.0,null,12.5]","[""foo"",null,""10""]","[false,true,false]",[10],[10],[10],[10],[10.0],[10.0],"[""foo""]",[false],"[[1010,1011],[2010,2011],[3010,3011],[4010,4011]]","[[10.25,10.5,10.75],[-10.25,-10.5,-10.75]]" +11,,-11,-11,-11,-11.0,-11.0,10 + one,True,,"[11,12,13]","[11,12,13]","[11,12,13]","[11.0,null,13.5]","[11.0,null,13.5]","[""foo"",null,""10 + one""]","[true,true,false]",,[],[],[],[],[],[],[],,"[[11.25,11.5,11.75],[-11.25,-11.5,-11.75]]" +12,-12,,-12,-12,-12.0,-12.0,10 + two,False,"[12,13,14]",,"[12,13,14]","[12,13,14]","[12.0,null,14.5]","[12.0,null,14.5]","[""foo"",null,""10 + two""]","[false,false,true]","[12,13,14]",,"[12,13,14]","[12,13,14]","[12.0,null,14.5]","[12.0,null,14.5]","[""foo"",null,""10 + two""]","[false,false,true]","[[1012,1013],[2012,2013],[3012,3013],[4012,4013]]", +13,-13,-13,,-13,-13.0,-13.0,10 + three,True,"[13,14,15]","[13,14,15]",,"[13,14,15]","[13.0,null,15.5]","[13.0,null,15.5]","[""foo"",null,""10 + three""]","[true,false,true]","[13,14]","[13,14]",,"[13,14]","[13.0,null]","[13.0,null]","[""foo"",null]","[true,false]","[[1013,1014],[2013,2014],[3013,3014],[4013,4014]]","[[13.25,13.5,13.75],[-13.25,-13.5,-13.75]]" +14,-14,-14,-14,,-14.0,-14.0,10 + four,False,"[14,15,16]","[14,15,16]","[14,15,16]",,"[14.0,null,16.5]","[14.0,null,16.5]","[""foo"",null,""10 + four""]","[false,true,true]",[14],[14],[14],,[14.0],[14.0],"[""foo""]",[false],"[[1014,1015],[2014,2015],[3014,3015],[4014,4015]]","[[14.25,14.5,14.75],[-14.25,-14.5,-14.75]]" +15,-15,-15,-15,-15,nan,-15.0,10 + five,True,"[15,16,17]","[15,16,17]","[15,16,17]","[15,16,17]",,"[15.0,null,17.5]","[""foo"",null,""10 + five""]","[true,true,true]",[],[],[],[],,[],[],[],"[[1015,1016],[2015,2016],[3015,3016],[4015,4016]]","[[15.25,15.5,15.75],[-15.25,-15.5,-15.75]]" diff --git a/astropy/io/ascii/tests/t/test4.dat b/astropy/io/ascii/tests/data/test4.dat similarity index 91% rename from astropy/io/ascii/tests/t/test4.dat rename to astropy/io/ascii/tests/data/test4.dat index 2bded52af8d1..329d5f4c121e 100644 --- a/astropy/io/ascii/tests/t/test4.dat +++ b/astropy/io/ascii/tests/data/test4.dat @@ -1,6 +1,6 @@ # whitespace separated zabs1.nh p1.gamma p1.ampl statname statval - 0.0872113431031 1.26764544642 0.000699751823872 input 0.0 + 0.0872113431031 1.26764500000 0.000699751823872 input 0.0 0.0863775314648 1.26769713012 0.000698799851356 chi2constvar 494.396534577 0.0839710433091 1.25997502704 0.000696444029148 chi2modvar 497.56468441 0.0867933991271 1.27045571779 0.000699526507899 cash -579508.340504 diff --git a/astropy/io/ascii/tests/t/test5.dat b/astropy/io/ascii/tests/data/test5.dat similarity index 100% rename from astropy/io/ascii/tests/t/test5.dat rename to astropy/io/ascii/tests/data/test5.dat diff --git a/astropy/io/ascii/tests/t/vizier/ReadMe b/astropy/io/ascii/tests/data/vizier/ReadMe similarity index 100% rename from astropy/io/ascii/tests/t/vizier/ReadMe rename to astropy/io/ascii/tests/data/vizier/ReadMe diff --git a/astropy/io/ascii/tests/data/vizier/table1.dat b/astropy/io/ascii/tests/data/vizier/table1.dat new file mode 100644 index 000000000000..42d97d02ae1d --- /dev/null +++ b/astropy/io/ascii/tests/data/vizier/table1.dat @@ -0,0 +1,15 @@ +Cr110 2108 06 38 52.5 +02 01 58.4 14.79 13.35 -- --- 9.76 6 16200 70 Cl* Collinder 110 DI 2108 +Cr110 2129 06 38 41.1 +02 01 05.5 15.00 13.66 12.17 12.94 10.29 7 18900 70 Cl* Collinder 110 DI 2129 +Cr110 3144 06 38 30.3 +02 03 03.0 14.80 13.49 12.04 12.72 10.19 6 16195 65 Cl* Collinder 110 DI 3144 +NGC2099 67 05 52 16.6 +32 34 45.6 12.38 11.12 9.87 --- 8.17 3 3600 95 NGC 2099 67 +NGC2099 148 05 52 08.1 +32 30 33.1 12.36 11.09 - --- 8.05 3 3600 105 NGC 2099 148 +NGC2099 508 05 52 33.2 +32 27 43.5 12.24 10.98 -- --- 7.92 3 3900 85 NGC 2099 508 +NGC2420 41 07 38 06.2 +21 36 54.7 13.75 12.67 11.61 12.13 10.13 5 9000 70 NGC 2420 41 +NGC2420 76 07 38 15.5 +21 38 01.8 13.65 12.66 11.65 12.14 10.31 5 9000 75 NGC 2420 76 +NGC2420 174 07 38 26.9 +21 38 24.8 13.41 12.40 ---- --- 9.98 5 9000 60 NGC 2420 174 +NGC2682 141 08 51 22.8 +11 48 01.7 11.59 10.48 9.40 9.92 7.92 3 2700 85 Cl* NGC 2682 MMU 141 +NGC2682 223 08 51 43.9 +11 56 42.3 11.68 10.58 9.50 10.02 8.00 3 2700 85 Cl* NGC 2682 MMU 223 +NGC2682 286 08 52 18.6 +11 44 26.3 11.53 10.47 9.43 9.93 7.92 3 2700 105 Cl* NGC 2682 MMU 286 +NGC7789 5237 23 56 50.6 +56 49 20.9 13.92 12.81 11.52 --- 9.89 5 9000 70 Cl* NGC 7789 G 5237 +NGC7789 7840 23 57 19.3 +56 40 51.5 14.03 12.82 11.49 --- 9.83 6 9000 75 Cl* NGC 7789 G 7840 +NGC7789 8556 23 57 27.6 +56 45 39.2 14.18 12.97 11.65 --- 10.03 3 5400 45 Cl* NGC 7789 G 8556 diff --git a/astropy/io/ascii/tests/data/vizier/table5.dat.gz b/astropy/io/ascii/tests/data/vizier/table5.dat.gz new file mode 100644 index 000000000000..253c7e8125bc Binary files /dev/null and b/astropy/io/ascii/tests/data/vizier/table5.dat.gz differ diff --git a/astropy/io/ascii/tests/t/vots_spec.dat b/astropy/io/ascii/tests/data/vots_spec.dat similarity index 98% rename from astropy/io/ascii/tests/t/vots_spec.dat rename to astropy/io/ascii/tests/data/vots_spec.dat index bc90130f057c..c2153636e309 100644 --- a/astropy/io/ascii/tests/t/vots_spec.dat +++ b/astropy/io/ascii/tests/data/vots_spec.dat @@ -4,7 +4,7 @@ ## ## This is the specification of the VOTable-Simple (VOTS) format, given as an ## example data table with comments and references. This data table format is -## intented to provide a way of specifying metadata and data for simple tabular +## intended to provide a way of specifying metadata and data for simple tabular ## data sets. This specification is intended as a subset of the VOTable data ## model and allow easy generation of a VOTable-compliant data structure. This ## provides a uniform starting point for generating table documentation and @@ -34,7 +34,7 @@ ## VOTable elements and attributes. ## ## The actual table data must follow the header and consist of space or tab delimited -## data fields. The chosen delimiter must be used consistently througout the table. +## data fields. The chosen delimiter must be used consistently throughout the table. ## ##---------------------------------------------------------------------------------- ## Table description, corresponding to the VOTable TABLE::DESCRIPTION element. @@ -66,7 +66,7 @@ ## 'datatype', 'unit', and 'description' are required. Optional attributes are: ## 'width', 'precision', 'ucd', 'utype', 'ref', and 'type'. ## See http://www.ivoa.net/Documents/REC/VOTable/VOTable-20040811.html#ToC25 for -## the VOTable defintions. +## the VOTable definitions. ## Allowed values of datatype are: ## boolean, unsignedByte, short, int, long, string, float, double ## Units: (from http://www.ivoa.net/Documents/REC/VOTable/VOTable-20040811.html#sec:unit) diff --git a/astropy/io/ascii/tests/t/whitespace.dat b/astropy/io/ascii/tests/data/whitespace.dat similarity index 100% rename from astropy/io/ascii/tests/t/whitespace.dat rename to astropy/io/ascii/tests/data/whitespace.dat diff --git a/astropy/io/ascii/tests/t/cds/glob/ReadMe b/astropy/io/ascii/tests/t/cds/glob/ReadMe deleted file mode 100644 index cc680cbb2e27..000000000000 --- a/astropy/io/ascii/tests/t/cds/glob/ReadMe +++ /dev/null @@ -1,572 +0,0 @@ -B/cb Cataclysmic Binaries, LMXBs, and related objects (Ritter+, 2011) -================================================================================ -Catalogue of cataclysmic binaries, low-mass X-ray binaries -and related objects (7th Edition, rev. 7.14, September 2010) - Ritter H., Kolb U. - - =2003A&A...404..301R -================================================================================ -ADC_Keywords: Binaries, cataclysmic ; Binaries, X-ray ; Novae -Keywords: catalogues - stars: novae, cataclysmic variables - - stars: binaries: close - -Description (Release 7.15): - Cataclysmic Binaries are semi-detached binaries consisting of a white - dwarf or a white dwarf precursor primary and a low-mass secondary - which is filling its critical Roche lobe. The secondary is not - necessarily unevolved, it may even be a highly evolved star as for - example in the case of the AM CVn-type stars. - - Low-Mass X-Ray Binaries are semi-detached binaries consisting of - either a neutron star or a black hole primary, and a low-mass - secondary which is filling its critical Roche lobe. - - Related Objects are detached binaries consisting of either a white - dwarf or a white dwarf precursor primary and of a low-mass secondary. - The secondary may also be a highly evolved star. - - The catalogue lists coordinates, apparent magnitudes, orbital - parameters, and stellar parameters of the components and other - characteristic properties of 880 cataclysmic binaries, 98 low-mass - X-ray binaries and 319 related objects with known or suspected orbital - periods together with a comprehensive selection of the relevant recent - literature. In addition the catalogue contains a list of references to - published finding charts for 1259 of the 1297 objects, and a cross- - reference list of alias object designations. Literature published - before 1 July 2010 has, as far as possible, been taken into account. - Updated information will be provided regularly, currently every six - months. - - Old editions include catalogue (5th edition), - (6th edition) and (7th edition); - the successive versions of the 7th edition are available - in dedicated subdirectories (v7.00 tp v7.13) - -File Summary: --------------------------------------------------------------------------------- - FileName Lrecl Records Explanations --------------------------------------------------------------------------------- -ReadMe 80 . This file -cbdata.dat 226 880 Catalogue of Cataclysmic Binaries -lmxbdata.dat 228 98 Catalogue of Low-Mass X-Ray Binaries -pcbdata.dat 216 319 Catalogue of Related Objects -findrefs.dat 274 3230 References for finding charts -cbrefs.dat 257 1937 References for cbdata.dat -lmxbrefs.dat 236 291 References for lmxbdata.dat -pcbrefs.dat 302 655 References for pcbdata.dat -whoswho.txt 72 8927 *Names of objects, and references of designations -whoswho1.dat 199 5052 *Alternative names in lexigraphical order -whoswho2.dat 100 3595 *Provisional and Common designations -whoswho5.dat 73 1453 *References to the catalogue acronyms --------------------------------------------------------------------------------- -Note on whoswho.txt: - contains the 3 parts whoswho1.dat to whoswho5.dat (without the bibcodes) -Note on whoswho1.dat, whoswho2.dat, whoswho5.dat: - formatted files corresponding to whoswho.txt --------------------------------------------------------------------------------- - -See also: - http://www.MPA-Garching.MPG.DE/RKcat/ : Catalog Home page or - http://physics.open.ac.uk/RKcat/ : Catalog Home page - -Byte-by-byte Description of file: cbdata.dat --------------------------------------------------------------------------------- - Bytes Format Units Label Explanations --------------------------------------------------------------------------------- - 1- 12 A12 --- Name Object name (G1) - 14 A1 --- whoswho [*] * indicating that further alternative - designations are in the whoswho1.dat file - 16- 27 A12 --- AltName A frequently used alternative name (G2) - 30- 31 I2 h RAh Right Ascension J2000 (hours) - 33- 34 I2 min RAm Right Ascension J2000 (minutes) - 36- 39 F4.1 s RAs [0,60]? Right Ascension J2000 (seconds) - 41 A1 --- DE- Declination J2000 (sign) - 42- 43 I2 deg DEd Declination J2000 (degrees) - 45- 46 I2 arcmin DEm Declination J2000 (minutes of arc) - 48- 49 I2 arcsec DEs [0,60]? Declination J2000 (seconds of arc) - 51 A1 arcsec epos [0-9] Position accuracy in (G3) - 53- 54 A2 --- Type1 Object type (3) - 55 A1 --- u_Type1 [?:] Uncertainty flag for object type - 57- 58 A2 --- Type2 Object type (3) - 59 A1 --- u_Type2 [?:] Uncertainty flag for object type - 61- 62 A2 --- Type3 Object type (3) - 63 A1 --- u_Type3 [?:] Uncertainty flag for Object type - 65- 66 A2 --- Type4 Object type (3) - 67 A1 --- u_Type4 [?] Uncertainty flag for Object type - 69 A1 --- l_mag1 [><] Limit flag for magnitude mag1 - 70- 73 F4.1 mag mag1 ? Apparent V (or B, b, g, R, I) magnitude - at maximum brightness (4) - 74 A1 --- f_mag1 [:BbgRiIJKprw] uncertainty flag/band for mag1 - (w="white light") - 76 A1 --- l_mag2 [><] Limit flag for magnitude mag2 - 77- 80 F4.1 mag mag2 ? Apparent V (or B, g, R) magnitude - at mideclipse (5) - 81 A1 --- f_mag2 [:?BbgRiKpw] uncertainty flag/band for mag2 - 83 A1 --- l_mag3 [><] Limit flag for magnitude mag3 - 84- 87 F4.1 mag mag3 ? Apparent V (or B, g, R) magnitude - of outbursts (6) - 88 A1 --- f_mag3 [:?BbgpRrw] uncertainty flag/band for mag3 - 90 A1 --- l_mag4 [><] Limit flag for magnitude mag4 - 91- 94 F4.1 mag mag4 ? Apparent V (or B, R) magnitude - in superoutburst (7) - 95 A1 --- f_mag4 [:?BgRUIpw] uncertainty flag/band for mag4 - 97-101 A5 d T1 Time interval between two subsequent - outbursts (8) - 103-107 A5 d T2 Time interval between two subsequent - superoutbursts (8) - 109-116 F8.6 d Orb.Per ? Orbital period, in case of object - type DQ: spectroscopic period, if it is - different from the photometric one - 117 A1 --- u_Orb.Per [:*] Uncertainty flag for Orb.Per (9) - 119-126 F8.6 d 2.__Per ? Second period (10) - 127 A1 --- u_2.__Per Uncertainty flag for 2.__Per - 128-137 F10.3 s 3.__Per ? Additional period in the system (11) - 138 A1 --- f_3.__Per [:TQ] Flag for 3.__Per (12) - 139-148 F10.3 s 4.__Per ? Additional period in the system (13) - 149 A1 --- f_4.__Per [:T] ":" uncertainty flag for 4.__Per - "T" flag indicating transient pulsations - 151 A1 --- EB [D21 ] Flag indicating the - occurrence of eclipses (G4) - 152 A1 --- u_EB [?:] Uncertainty flag for EB - 154 I1 --- SB [1,2]? Flag specifying the type of - spectroscopic binary (G5) - 155 A1 --- u_SB [:] Uncertainty flag for SB - 157-163 A7 --- SpType2 Spectral type of the secondary (G6) - 165-171 A7 --- SpType1 Spectral type of the primary (G6) - 174 A1 --- l_M1/M2 Limit flag for M1/M2 - 175-179 F5.2 --- M1/M2 ? Mass ratio M1/M2 - 180 A1 --- u_M1/M2 Uncertainty flag for M1/M2 - 183-186 F4.2 --- e_M1/M2 ? Error of M1/M2 - 188 A1 --- l_Incl Limit flag for the orbital inclination - 189-192 F4.1 deg Incl ? Orbital inclination - 193 A1 --- u_Incl Uncertainty flag for the inclination - 195-198 F4.1 deg e_Incl ? Error of orbital inclination - 200 A1 --- l_M1 Limit flag for primary mass M1 - 201-205 F5.3 solMass M1 ? Primary mass M1 - 206 A1 --- u_M1 Uncertainty flag for primary mass M1 - 208-212 F5.3 solMass e_M1 ? Error of primary mass M1 - 214 A1 --- l_M2 Limit flag for secondary mass M2 - 215-219 F5.3 solMass M2 ? Secondary mass M2 - 220 A1 --- u_M2 Uncertainty flag for secondary mass M2 - 222-226 F5.3 solMass e_M2 ? Error of secondary mass M2 --------------------------------------------------------------------------------- -Note (3): Object type coarsely characterised using the following abbreviations: - AC = AM CVn star, spectrum devoid of hydrogen lines, subtype of NL - AM = polar = AM Her system, subtype of NL, contains a synchronously - or nearly synchronously rotating, magnetized white dwarf - AS = subtype of AM, with a slowly asynchronously rotating, magnetized - white dwarf - BD = secondary star is a brown dwarf - CP = coherent pulsator, contains a coherently pulsating white dwarf - CV = cataclysmic variable of unspecified subtype - DA = non-magnetic direct accretor - DN = dwarf nova - DQ = DQ Her star, contains a non-synchronously rotating, magnetized - white dwarf; usually not seen in X-rays - EG = extragalactic source - ER = ER UMa star = SU UMa star with an extremely short supercycle - GC = source in a globular cluster - GW = contains a pulsating white dwarf of the GW Vir = PG 1159-035 type - IP = intermediate polar, shows coherent X-ray period from a - non-synchronously spinning, magnetized white dwarf; usually a - strong X-ray source - LA = low accretion rate polar (LARP), i.e. a somewhat detached magnetic - CV/pre-CV - N = classical nova - Na = fast nova (decline from max. by 3mag in less than about 100days) - Nb = slow nova (decline from max. by 3mag in more than about 100days) - Nc = extremely slow nova (typical time scale of the decline from - maximum: decades) - NL = nova-like variable - Nr = recurrent nova - NS = system showing negative (nodal) superhumps - PW = precessing white dwarf - SH = non-SU UMa star showing either permanent or transient positive - (apsidal) superhumps - SS = supersoft X-ray source; CV with stationary hydrogen burning on - the white dwarf - SU = SU UMa star, subtype of DN - SW = SW Sex star, subtype of NL - UG = dwarf nova of either U Gem or SS Cyg subtype - UL = ultra-luminous X-ray source - UX = UX UMa star, subtype of NL - VY = VY Scl star (anti dwarf nova), subtype of NL - WZ = WZ Sge star = SU UMa star with an extremely long supercycle - ZC = Z Cam star, subtype of DN - ZZ = white dwarf shows ZZ Ceti-type pulsations - -Note (4): Apparent V magnitude at maximum brightness of: - novae (N,Na,Nb,Nc,Nr) in minimum - DN (UG,ZC,SU) in minimum - NL (UX,AC) in normal state - NL (DQ,IP,AM,VY) in high state. - SS in high state. - -Note (5): In case of eclipses magnitude at mideclipse, of: - novae (N,Na,Nb,Nc,Nr) in minimum - DN (UG,ZC,SU) in minimum - NL (UX,AC) in normal state - NL (DQ,IP,AM,VY) in high state. - SS in high state. - -Note (6): Apparent magnitude at maximum brightness of: - novae (N,Na,Nb,Nc,Nr) in outburst - DN (UG,ZC) in outburst - DN (SU) in normal outburst - DN (WZ) in echo outburst - NL (AM,VY) in low state - NL (DQ,IP) in low state - SS in low state. - -Note (7): Apparent magnitude at maximum brightness of: - DN (ZC) in standstill - DN (SU) in superoutburst - WZ in superoutburst - NL (DQ,IP) in flaring state or outburst - iNL (AM, VY) in low state - SS in low state - -Note (8): Time interval between outbursts is defined: - - for dwarf novae of subtype UG or ZC: the typical time interval - between two subsequent outbursts; - - for dwarf novae of subtype SU: - T1 is the typical time interval between two subsequent normal - outburst, and - T2 is the typical time interval between subsequent superoutbursts. - -Note (9): the * indicates, in case of object type SU, that the orbital - period has been estimated from the known superhump period using the - empirical relation given by B. Stolz and R. Schoembs (1984A&A...132..187S). - -Note (10): The second period is, in case of object type: - DQ or IP: photometric period if it is different from the - spectroscopic one - AM: polarization period = spin period of the white dwarf, if it is - different from the presumed orbital period (subtype AS) - SU: superhump period, wherever possible, at the beginning of a - superoutburst - SH: photometric period, presumably superhump period of either - permanent or transient superhumps - NS: photometric period, period of either permanent or transient - negative superhumps if 2.__Per. < Orb.Per. - -Note (11): This additional period is, in case of object type: - CP: period of coherent pulsation, (transient if f_3.__Per=T) - DQ: spin period of the white dwarf - IP: spin period of the white dwarf, usually detected in X-Rays - SW: probably the spin period of the white dwarf - -Note (12): the flag takes the values: - ':' uncertainty flag - 'T' indicating transient pulsations - 'Q' indicating the occurrence of quasi- periodic oscillations (QPO) - in objects of type N, DN, NL. - -Note (13): This additional period is, in case of object type: - CP: second period of coherent pulsation, (transient if f_4.__Per=T) - DQ: additional period, presumably due to reprocessed X-Rays - IP: additional period, usually seen in the optical and presumably - due to reprocessed X-Rays --------------------------------------------------------------------------------- - -Byte-by-byte Description of file: lmxbdata.dat --------------------------------------------------------------------------------- - Bytes Format Units Label Explanations --------------------------------------------------------------------------------- - 1- 12 A12 --- Name Object name (G1) - 14 A1 --- whoswho [*] * indicating that further alternative - designations are in the whoswho1.dat file - 16- 27 A12 --- AltName A frequently used alternative name (G2) - 30- 31 I2 h RAh Right Ascension J2000 (hours) - 33- 34 I2 min RAm Right Ascension J2000 (minutes) - 36- 39 F4.1 s RAs Right Ascension J2000 (seconds) - 41 A1 --- DE- Declination J2000 (sign) - 42- 43 I2 deg DEd Declination J2000 (degrees) - 45- 46 I2 arcmin DEm Declination J2000 (minutes of arc) - 48- 49 I2 arcsec DEs Declination J2000 (seconds of arc) - 51 A1 arcsec epos [0-9] Position accuracy in (G3) - 53- 54 A2 --- Type1 Object type (3) - 55 A1 --- u_Type1 [?] Uncertainty flag for object type - 57- 58 A2 --- Type2 Object type (3) - 59 A1 --- u_Type2 [?] Uncertainty flag for object type - 61- 62 A2 --- Type3 Object type (3) - 63 A1 --- u_Type3 [?] Uncertainty flag for Object type - 65- 66 A2 --- Type4 Object type (3) - 67 A1 --- u_Type4 [?] Uncertainty flag for Object type - 69 A1 --- l_mag1 [><] Limit flag for magnitude mag1 - 70- 73 F4.1 mag mag1 ? Apparent V (or B, g, R, I, K) magnitude - at maximum brightness, - in case of XT in quiescence - 74 A1 --- f_mag1 [:UBgRIJK] uncertainty flag/band for mag1 - 76 A1 --- l_mag2 [><] Limit flag for magnitude mag2 - 77- 80 F4.1 mag mag2 ? Apparent V (or B, R, I) magnitude - at mid-eclipse (4) - 81 A1 --- f_mag2 [:BRIJK] Uncertainty flag/band for mag2 - 84- 87 F4.1 mag mag3 ? Apparent V (or other) magnitude - at outburst (5) - 88 A1 --- f_mag3 [:BRI] Uncertainty flag/band for mag3 - 90 A1 --- l_mag4 Limit flag of magnitude mag4 - 91- 94 F4.1 mag mag4 ? Apparent V (or other) magnitude at - superoutburst (5) - 96 A1 --- l_LX/Lopt Limit flag on LX/Lopt - 97-103 F7.1 --- LX/Lopt ? The ratio of X-ray to optical luminosity - 106-108 I3 d T1 ? Typical time interval between two subsequent - X-ray active states in case of subtype XT - 110-118 F9.6 d Orb.Per ? Orbital period - 119 A1 --- u_Orb.Per [:*] Uncertainty flag for Orb.Per (6) - 120-128 F9.6 d 2.__Per ? Second period, in case of object type SH: - photometric period, presumably superhump - period of either permanent or transient - superhumps - 129 A1 --- u_2.__Per Uncertainty flag for 2.__Per - 130-140 F11.7 s 3.__Per ? Additional period in the system, in case of - object type - BO: period of burst oscillations = rotation - period of the neutron star; - XP: pulse period of the pulsar - 141 A1 --- u_3.__Per Uncertainty flag for 3.__Per - 142-146 F5.1 s 4.__Per ? Aditional period in the system, in case of - object type XP: optical period, presumably - due to e_processed X-Rays - 152 A1 --- EB [D1 ] Occurrence of eclipses (G4) - 153 A1 --- u_EB [?] Uncertainty flag on EB - 155 I1 --- SB [1,2]? Flag specifying the type of - spectroscopic binary (G5) - 158-164 A7 --- SpType2 Spectral type of the secondary (G6) - 167-173 A7 --- SpType1 Spectral type of the primary (G6) - 175 A1 --- l_M1/M2 Limit flag for M1/M2 - 176-180 F5.2 --- M1/M2 ? Mass ratio M1/M2 - 181 A1 --- u_M1/M2 Uncertainty flag for M1/M2 - 182-186 F5.2 --- e_M1/M2 ? Error of M1/M2 - 189 A1 --- l_Incl Limit flag for the orbital inclination - 190-193 F4.1 deg Incl ? Orbital inclination - 194 A1 --- u_Incl Uncertainty flag (:) on Incl - 196-199 F4.1 deg e_Incl ? Error of orbital inclination - 201 A1 --- l_M1 Limit flag on M1 - 202-206 F5.2 solMass M1 ? Primary mass M1 - 207 A1 --- u_M1 Uncertainty flag (:) on M1 - 209-213 F5.2 solMass e_M1 ? Error of primary mass M1 - 215 A1 --- l_M2 Limit flag for secondary mass M2 - 216-221 F6.3 solMass M2 ? Secondary mass M2 - 222 A1 --- u_M2 Uncertainty flag (:) on M2 - 223-228 F6.3 solMass e_M2 ? Error of secondary mass M2 --------------------------------------------------------------------------------- -Note (3): the object type is coarsely characterised using - the following abbreviations: - AS = atoll source, subtype of the LMXBs - BH = black hole candidate, subtype of the LMXBs - BO = X-ray burster with coherent burst oscillations at the neutron - star spin period - DC = source with an accretion disc corona, subtype of the LMXBs - GC = source in a globular cluster - MQ = microquasar, source of relativistic jets - NS = system showing negative (nodal) superhumps - QN = quiescent neutron star LMXB - RP = primary is also seen as a radio pulsar - SH = system showing either permanent or transient superhumps - SS = supersoft X-ray source - UL = ultra-luminous X-ray source - XB = X-ray burst source - XP = X-ray pulsar - XT = transient X-ray source - ZS = Z-source, subtype of the LMXBs - -Note (4): in case of eclipses magnitude at mideclipse, - in case of XT in quiescence - -Note (5): in case of XL (XB, XT) in outburst - -Note (6): the * indicates, in case of object type SU, that the orbital - period has been estimated from the known superhump period using the - empirical relation given by B. Stolz and R. Schoembs (1984A&A...132..187S). --------------------------------------------------------------------------------- - -Byte-by-byte Description of file: pcbdata.dat --------------------------------------------------------------------------------- - Bytes Format Units Label Explanations --------------------------------------------------------------------------------- - 1- 12 A12 --- Name Object name (G1) - 14 A1 --- whoswho [*] * indicating that further alternative - designations are in the whoswho1.dat file - 16- 27 A12 --- AltName A frequently used alternative name (G2) - 30- 31 I2 h RAh ? Right Ascension J2000 (hours) - 33- 34 I2 min RAm ? Right Ascension J2000 (minutes) - 36- 39 F4.1 s RAs ? Right Ascension J2000 (seconds) - 41 A1 --- DE- ? Declination J2000 (sign) - 42- 43 I2 deg DEd ? Declination J2000 (degrees) - 45- 46 I2 arcmin DEm ? Declination J2000 (minutes of arc) - 48- 49 I2 arcsec DEs ? Declination J2000 (seconds of arc) - 51 A1 arcsec epos [0-9P] Position accuracy (G3) - 53- 54 A2 --- Type1 Object type (3) - 55 A1 --- u_Type1 [?] Uncertainty flag for object type - 57- 58 A2 --- Type2 Object type (3) - 59 A1 --- u_Type2 [?] Uncertainty flag for object type - 61- 62 A2 --- Type3 Object type (3) - 63 A1 --- u_Type3 [?] Uncertainty flag for object type - 65- 66 A2 --- Type4 Object type (3) - 70- 73 F4.1 mag mag1 ? Apparent V (or other) magnitude at maximum - brightness outside eclipse - 74 A1 --- f_mag1 [:BbpgRiIK] uncertainty flag/band for mag1 - 76 A1 --- l_mag2 [><] Limit flag for magnitude mag2 - 77- 80 F4.1 mag mag2 ? Apparent V (or other) magnitude at minimum - brightness, in case of eclipses magnitude - at mideclipse. - 81 A1 --- f_mag2 [:BgRiI] uncertainty flag/band for mag2 - 82- 90 F9.6 d Orb.Per Orbital period - 91 A1 --- u_Orb.Per Uncertainty flag for Orb.Per - 92-101 F10.4 s 2.__Per ? Spin period of the accretor (white dwarf - or neutron star). - 103 I1 --- EB [1,2]? Flag indicating the occurrence of - eclipses (G4) - 104 A1 --- u_EB [?] Uncertainty flag for EB - 106 I1 --- SB [1,2]? Flag specifying the type of - spectroscopic binary (G5) - 109-115 A7 --- SpType2 Spectral type of the secondary (G6) - 117-123 A7 --- SpType1 Spectral type of the primary (G6) - 125 A1 --- l_E [><] Limit flag for the orbital eccentricity - 126-129 F4.2 --- E ? Orbital eccentricity - 130 A1 --- u_E Uncertainty flag on orbital eccentricity - 132-135 F4.2 --- e_E ? Error of orbital eccentricity - 137 A1 --- l_M1/M2 Limit flag for mass ratio M1/M2 - 138-141 F4.2 --- M1/M2 ? Mass ratio M1/M2 - 142 A1 --- u_M1/M2 Uncertainty flag on mass ratio M1/M2 - 144-147 F4.2 --- e_M1/M2 ? Error of M1/M2 - 149 A1 --- l_Incl Limit flag for the orbital inclination - 150-153 F4.1 deg Incl ? Orbital inclination - 154 A1 --- u_Incl Uncertainty flag for the inclination - 156-159 F4.1 deg e_Incl ? Error of orbital inclination - 161 A1 --- l_M1 Limit flag for primary mass M1 - 162-166 F5.3 solMass M1 ? Primary mass M1 - 167 A1 --- u_M1 Uncertainty flag for primary mass M1 - 169-173 F5.3 solMass e_M1 ? Error of primary mass M1 - 175 A1 --- l_R1 Limit flag for primary radius R1 - 176-180 F5.3 solRad R1 ? Primary radius - 181 A1 --- u_R1 Uncertainty flag [:] for primary radius R1 - 183-187 F5.3 solRad e_R1 ? Error of primary radius R1 - 189 A1 --- l_M2 Limit flag for secondary mass M2 - 190-194 F5.3 solMass M2 ? Secondary mass M2 - 195 A1 --- u_M2 Uncertainty flag for secondary mass M2 - 197-201 F5.3 solMass e_M2 ? Error of secondary mass M2 - 203 A1 --- l_R2 Limit flag on secondary radius R2 - 204-209 F6.4 solRad R2 ? Secondary radius R2 - 210 A1 --- u_R2 Uncertainty flag [:] for secondary radius R2 - 212-217 F6.4 solRad e_R2 ? Error of secondary radius --------------------------------------------------------------------------------- -Note (3): Object type coarsely characterised using the following abbreviations: - CP = coherent pulsator, contains a coherently pulsating white dwarf or - subdwarf - DD = system consists of two degenerate components - DS = detached system - EC = contains a pulsating sdB star of the EC 14026-2647 type - GC = source in a globular cluster - GP = sdB-star with g-mode pulsations - GW = contains a pulsating white dwarf of the GW Vir = PG 1159-035 type - PN = central star of a planetary nebula - RS = system shows RS CVn-like chromospheric activity - SC = sub-stellar companion --------------------------------------------------------------------------------- - -Byte-by-byte Description of file: *refs.dat --------------------------------------------------------------------------------- - Bytes Format Units Label Explanations --------------------------------------------------------------------------------- - 1- 12 A12 --- Name Object name - 14- 32 A19 --- BibCode BibCode - 34-302 A269 --- Text Text of reference --------------------------------------------------------------------------------- - -Byte-by-byte Description of file: whoswho1.dat --------------------------------------------------------------------------------- - Bytes Format Units Label Explanations --------------------------------------------------------------------------------- - 1 A1 --- B [B] when the name is based on B1950 position - 2- 25 A24 --- Name Object name - 27 A1 --- --- [=] - 29-212 A184 --- AltName Other name, or comment (1) --------------------------------------------------------------------------------- -Note (1): Catalogue designations involving the equatorial coordinates - are given in the following format: - HHMM+DDMM (catalogue acronyms) if the position is given in B1950 - coordinates -- a 'B' is then present in byte 1. - JHHMM+DDMM (catalogue acronyms) if the position is given in J2000 - coordinates. - Here HHMM is the truncated right ascension in hours (HH) and - minutes (MM), DDMM the truncated declination in degrees (DD) - and arcminutes (MM), and + the sign of the declination. --------------------------------------------------------------------------------- - -Byte-by-byte Description of file: whoswho2.dat --------------------------------------------------------------------------------- - Bytes Format Units Label Explanations --------------------------------------------------------------------------------- - 1 A1 --- B [B] when the name is based on B1950 position - 2- 53 A52 --- cName Common or Provisional designation (G2) - 55- 56 A2 --- --- [->] - 58-101 A44 --- Name Usual name --------------------------------------------------------------------------------- - -Byte-by-byte Description of file: whoswho5.dat --------------------------------------------------------------------------------- - Bytes Format Units Label Explanations --------------------------------------------------------------------------------- - 1- 10 A10 --- Abbr Catalogue abbreviation - 14- 73 A60 --- Text Text of References --------------------------------------------------------------------------------- - -Global Notes: - -Note (G1): Wherever possible, the designation of the object given in the - General Catalogue of Variable Stars (Cat. ) is used here. - -Note (G2): The acronyms used in lists are detailed in the last part of - the file "whoswho.txt" - -Note (G3): The number indicates the accuracy of position in seconds of arc. - If the positional error is larger than 9arcsec, or unknown, this field - is left blank. The letter [P] indicates an object with a large proper - motion. - -Note (G4): The EB flag means: - EB= : (blank) no eclipses observed. - EB=1: 1 eclipse per orbital revolution observed. - EB=2: 2 eclipses per orbital revolution observed. - EB=D: periodic eclipse-like dips observed. - -Note (G5): The SB flag means: - SB=1: single-line spectroscopic binary - SB=2: double-line spectroscopic binary - -Note (G6): Spectral types are given in the following format: - [Spectral class/Luminosity class], where the usual roman numerals for - the latter are replaced by the corresponding arabic numerals, i.e. - I = 1, II = 2, III = 3, IV = 4, V = 5, VI = 6. --------------------------------------------------------------------------------- - -History: - * 16-Apr-2003: 7th Edition - * 28-Aug-2003: 7.1 Edition - * 12-Mar-2004: 7.2 Edition - * 01-Sep-2004: 7.3 Edition - * 24-Mar-2005: 7.4 Edition - * 25-Jul-2005: 7.5 Edition - * 01-Feb-2006: 7.6 Edition - * 29-May-2006: 7.6rev1 Edition (no new object) - * 07-Dec-2006: 7.7 Edition - * 17-Aug-2007: 7.8 Edition - * 18-Mar-2008: 7.9 Edition - * 26-Jul-2008: 7.10 Edition - * 06-Apr-2009: 7.11 Edition - * 18-Sep-2009: 7.12 Edition - * 20-Mar-2010: 7.13 Edition - * 05-Nov-2010: 7.14 Edition - * 23-Mar-2011: 7.15 Edition - -References: - Ritter H., 1984A&AS...57..385R (3rd edition) - Ritter H., 1987A&AS...70..335R (4th edition) - Ritter H., 1990A&AS...85.1179R (5th edition) (Catalogue: V/59) - Ritter H., Kolb U., 1995, in "X-ray Binaries", Lewin W.H.G, - van Paradijs J., van den Heuvel E.P. (eds), - Cambridge Univ. Press, p. 578 (Cat. ) -================================================================================ -(End) H. Ritter, U. Kolb [MPA Garching], Francois Ochsenbein [CDS] 05-Nov-2010 diff --git a/astropy/io/ascii/tests/t/cds/multi/ReadMe b/astropy/io/ascii/tests/t/cds/multi/ReadMe deleted file mode 100644 index 16114b349696..000000000000 --- a/astropy/io/ascii/tests/t/cds/multi/ReadMe +++ /dev/null @@ -1,63 +0,0 @@ -J/MNRAS/301/1031 High resolution spectra of VLM stars (Tinney+ 1998) -================================================================================ -High resolution spectra of Very Low-Mass Stars - Tinney C.G., Reid I.N. - - =1998MNRAS.301.1031T -================================================================================ -ADC_Keywords: Stars, dwarfs ; Stars, late-type ; Spectroscopy - -Description: - A high resolution optical spectral atlas for three very low-mass - stars are provided, along with a high resolution observation of - an atmospheric absorption calibrator. This is the data used to - produce Figures 4-9 in the paper. - - These data were acquired with CASPEC on the ESO3.6m telescope. - The FWHM resolution is 16km/s (eg. 0.043nm at 800nm), at a dispersion - of 9km/s. Incomplete wavelength coverage produces inter-order gaps - at wavelengths longer than 804.5nm. - -Objects: - --------------------------------------------------------------------- - RA (2000) DE Designation(s) (File) - --------------------------------------------------------------------- - 16 55 35.7 -08 23 36 VB 8 = LHS 429 = Gl 644 C (vb8.dat) - 08 53 36 -03 29 30 LHS 2065 = LP 666-9 (lhs2065.dat) - 03 39 34.6 -35 25 51 LP 944-20 (lp944-20.dat) - 05 45 59.9 -32 18 23 {mu} Col = HR 1996 = HD 38666 (mucol.dat) - --------------------------------------------------------------------- - -File Summary: ---------------------------------------------------------------------- - FileName Lrecl Records Explanations ---------------------------------------------------------------------- -ReadMe 80 . This file -vb8.dat 26 14390 Spectrum for VB8 -lhs2065.dat 26 14390 Spectrum for LHS2065 -lp944-20.dat 26 14390 Spectrum for LP944-20 -mucol.dat 23 14390 Atmospheric Spectrum for Mu Columbae ---------------------------------------------------------------------- - -Byte-by-byte Description of file: vb8.dat, lhs2065.dat, lp944-20.dat -------------------------------------------------------------------------- - Bytes Format Units Label Explanations -------------------------------------------------------------------------- - 1- 12 F12.2 0.1nm Lambda Central wavelength of the flux bin - 13- 26 A14.9 mJy Fnu Data in interorder gaps has value 0.0 -------------------------------------------------------------------------- - -Byte-by-byte Description of file: mucol.dat -------------------------------------------------------------------------- - Bytes Format Units Label Explanations -------------------------------------------------------------------------- - 1- 12 F12.2 0.1nm Lambda Central wavelength of the flux bin - 13- 23 F11.6 --- Fnu *Data in interorder gaps has value 0.0 -------------------------------------------------------------------------- -Note on Fnu: - mJy which have been normalised to value 1.0 - in the continuum of the atmospheric standard star -------------------------------------------------------------------------- - -================================================================================ -(End) C.G. Tinney [AAO] 04-Feb-1999 diff --git a/astropy/io/ascii/tests/t/ipac.dat b/astropy/io/ascii/tests/t/ipac.dat deleted file mode 100644 index fff863ca03be..000000000000 --- a/astropy/io/ascii/tests/t/ipac.dat +++ /dev/null @@ -1,12 +0,0 @@ -\catalog = sao -\date = "Wed Sp 20 09:48:36 1995" -\mykeyword = 'Another way for defining keyvalue string' -\ This is an example of a valid comment. -\ The 2nd data line is used to verify the exact column parsing -\ (unclear if this is a valid for the IPAC format) -| ra | dec | sai |-----v2---| sptype | -| real | real | int | real | char | -| unit | unit | unit | unit | ergs | -| null | null | null | null | -999 | - 2.09708 29.09056 73765 2.06000 B8IVpMnHg -12345678901234567890123456789012345678901234567890123456789012345 diff --git a/astropy/io/ascii/tests/t/latex1.tex b/astropy/io/ascii/tests/t/latex1.tex deleted file mode 100644 index cf65b3a61eb8..000000000000 --- a/astropy/io/ascii/tests/t/latex1.tex +++ /dev/null @@ -1,10 +0,0 @@ -\begin{table} -\caption{\ion{Ne}{ix} Ly series and \ion{Mg}{xi} triplet fluxes (errors are 5$1\sigma$ confidence intervals) \label{tab:nely}} -\begin{tabular}{lrr}\hline -cola & colb & colc\\ -\hline -a & 1 & 2\\ -b & 3 & 4\\ -\hline -\end{tabular} -\end{table} \ No newline at end of file diff --git a/astropy/io/ascii/tests/t/vizier/table1.dat b/astropy/io/ascii/tests/t/vizier/table1.dat deleted file mode 100644 index 2d92a640a729..000000000000 --- a/astropy/io/ascii/tests/t/vizier/table1.dat +++ /dev/null @@ -1,15 +0,0 @@ -Cr110 2108 06 38 52.5 +02 01 58.4 14.79 13.35 --- --- 9.76 6 16200 70 Cl* Collinder 110 DI 2108 -Cr110 2129 06 38 41.1 +02 01 05.5 15.00 13.66 12.17 12.94 10.29 7 18900 70 Cl* Collinder 110 DI 2129 -Cr110 3144 06 38 30.3 +02 03 03.0 14.80 13.49 12.04 12.72 10.19 6 16195 65 Cl* Collinder 110 DI 3144 -NGC2099 67 05 52 16.6 +32 34 45.6 12.38 11.12 9.87 --- 8.17 3 3600 95 NGC 2099 67 -NGC2099 148 05 52 08.1 +32 30 33.1 12.36 11.09 --- --- 8.05 3 3600 105 NGC 2099 148 -NGC2099 508 05 52 33.2 +32 27 43.5 12.24 10.98 --- --- 7.92 3 3900 85 NGC 2099 508 -NGC2420 41 07 38 06.2 +21 36 54.7 13.75 12.67 11.61 12.13 10.13 5 9000 70 NGC 2420 41 -NGC2420 76 07 38 15.5 +21 38 01.8 13.65 12.66 11.65 12.14 10.31 5 9000 75 NGC 2420 76 -NGC2420 174 07 38 26.9 +21 38 24.8 13.41 12.40 --- --- 9.98 5 9000 60 NGC 2420 174 -NGC2682 141 08 51 22.8 +11 48 01.7 11.59 10.48 9.40 9.92 7.92 3 2700 85 Cl* NGC 2682 MMU 141 -NGC2682 223 08 51 43.9 +11 56 42.3 11.68 10.58 9.50 10.02 8.00 3 2700 85 Cl* NGC 2682 MMU 223 -NGC2682 286 08 52 18.6 +11 44 26.3 11.53 10.47 9.43 9.93 7.92 3 2700 105 Cl* NGC 2682 MMU 286 -NGC7789 5237 23 56 50.6 +56 49 20.9 13.92 12.81 11.52 --- 9.89 5 9000 70 Cl* NGC 7789 G 5237 -NGC7789 7840 23 57 19.3 +56 40 51.5 14.03 12.82 11.49 --- 9.83 6 9000 75 Cl* NGC 7789 G 7840 -NGC7789 8556 23 57 27.6 +56 45 39.2 14.18 12.97 11.65 --- 10.03 3 5400 45 Cl* NGC 7789 G 8556 diff --git a/astropy/io/ascii/tests/t/vizier/table5.dat b/astropy/io/ascii/tests/t/vizier/table5.dat deleted file mode 100644 index 61bc51a8af0d..000000000000 --- a/astropy/io/ascii/tests/t/vizier/table5.dat +++ /dev/null @@ -1,49 +0,0 @@ -Cr110 2108 6696.79 Al1 4.02 -1.42 29.5 2.2 0.289 -Cr110 2108 6698.67 Al1 3.14 -1.65 58.0 2.0 0.325 -Cr110 2108 7361.57 Al1 4.02 -0.90 44.1 4.0 0.510 -Cr110 2108 7362.30 Al1 4.02 -0.75 62.7 3.9 0.577 -Cr110 2108 7835.31 Al1 4.02 -0.65 73.7 6.6 0.539 -Cr110 2108 7836.13 Al1 4.02 -0.49 87.6 4.1 0.390 -Cr110 2108 8772.86 Al1 4.02 -0.32 87.6 5.1 0.957 -Cr110 2108 8773.90 Al1 4.02 -0.16 118.6 14.6 0.736 -Cr110 2108 5853.67 Ba2 0.60 -1.00 121.9 5.5 1.435 -Cr110 2108 6141.71 Ba2 0.70 -0.08 191.0 8.7 1.117 -Cr110 2108 6496.90 Ba2 0.60 -0.38 175.8 6.8 1.473 -Cr110 2108 5261.70 Ca1 2.52 -0.59 149.1 5.3 0.808 -Cr110 2108 5512.98 Ca1 2.93 -0.71 106.7 6.2 1.416 -Cr110 2108 5857.45 Ca1 2.93 0.26 163.8 19.8 2.209 -Cr110 2108 6156.02 Ca1 2.52 -2.50 42.0 4.0 0.617 -Cr110 2108 6166.44 Ca1 2.52 -1.16 110.7 3.3 1.046 -Cr110 2108 6169.04 Ca1 2.52 -0.80 127.3 5.5 1.604 -Cr110 2108 6169.56 Ca1 2.53 -0.53 148.2 6.0 1.419 -Cr110 2108 6471.66 Ca1 2.53 -0.65 130.4 5.0 1.431 -Cr110 2108 6499.65 Ca1 2.52 -0.72 129.0 5.4 1.183 -Cr110 2108 5230.20 Co1 1.74 -1.84 60.4 6.7 1.210 -Cr110 2108 5530.77 Co1 1.71 -2.06 73.2 4.3 1.005 -Cr110 2108 5590.72 Co1 2.04 -1.87 69.9 3.2 0.706 -Cr110 2108 5935.38 Co1 1.88 -2.68 33.0 4.4 0.665 -Cr110 2108 6429.91 Co1 2.14 -2.41 28.2 1.3 0.340 -Cr110 2108 6490.34 Co1 2.04 -2.52 33.6 3.5 0.323 -Cr110 2108 6632.43 Co1 2.28 -2.00 50.9 2.1 0.391 -Cr110 2108 7154.67 Co1 2.04 -2.42 45.9 1.9 0.280 -Cr110 2108 7388.69 Co1 2.72 -1.65 36.6 1.8 0.343 -Cr110 2108 7417.37 Co1 2.04 -2.07 71.4 1.9 0.369 -Cr110 2108 7838.13 Co1 3.97 -0.30 32.7 2.7 0.495 -Cr110 2108 5243.36 Cr1 3.40 -0.57 47.9 4.0 0.828 -Cr110 2108 5329.14 Cr1 2.91 -0.06 110.4 4.9 1.113 -Cr110 2108 5442.37 Cr1 3.42 -1.06 33.3 2.5 0.499 -Cr110 2108 5712.75 Cr1 3.01 -1.30 49.4 5.3 1.038 -Cr110 2108 5788.39 Cr1 3.01 -1.83 26.1 1.3 0.260 -Cr110 2108 5844.59 Cr1 3.01 -1.76 26.2 3.9 0.863 -Cr110 2108 6330.09 Cr1 0.94 -2.92 94.4 6.6 1.638 -Cr110 2108 6537.93 Cr1 1.00 -4.07 33.0 2.4 0.479 -Cr110 2108 6630.01 Cr1 1.03 -3.56 60.7 1.5 0.232 -Cr110 2108 6661.08 Cr1 4.19 -0.19 33.5 6.4 0.627 -Cr110 2108 7355.94 Cr1 2.89 -0.28 126.7 4.1 0.671 -Cr110 2108 5055.99 Fe1 4.31 -2.01 41.2 3.3 0.371 -Cr110 2108 5178.80 Fe1 4.39 -1.84 45.4 7.1 0.851 -Cr110 2108 5285.13 Fe1 4.43 -1.64 50.1 5.2 0.607 -Cr110 2108 5294.55 Fe1 3.64 -2.86 -9.9 -9.9 -9.999 -Cr110 2108 5295.31 Fe1 4.42 -1.69 38.3 9.5 1.958 -Cr110 2108 5373.71 Fe1 4.47 -0.86 91.5 5.3 1.416 -Cr110 2108 5386.33 Fe1 4.15 -1.77 55.9 6.6 0.949 diff --git a/astropy/io/ascii/tests/test_c_reader.py b/astropy/io/ascii/tests/test_c_reader.py new file mode 100644 index 000000000000..b997da433ae8 --- /dev/null +++ b/astropy/io/ascii/tests/test_c_reader.py @@ -0,0 +1,1748 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +import functools +import io +import os +import re +import sys +from contextlib import nullcontext +from io import BytesIO +from textwrap import dedent + +import numpy as np +import pytest +from numpy import ma +from numpy.testing import assert_allclose + +from astropy.io import ascii +from astropy.io.ascii.core import ( + FastOptionsError, + InconsistentTableError, + ParameterError, +) +from astropy.io.ascii.fastbasic import ( + FastBasic, + FastCommentedHeader, + FastCsv, + FastNoHeader, + FastRdb, + FastTab, +) +from astropy.table import MaskedColumn, Table +from astropy.utils.data import get_pkg_data_filename +from astropy.utils.exceptions import AstropyWarning + +StringIO = lambda x: BytesIO(x.encode("ascii")) + + +def assert_table_equal(t1, t2, check_meta=False, rtol=1.0e-15, atol=1.0e-300): + """ + Test equality of all columns in a table, with stricter tolerances for + float columns than the np.allclose default. + """ + __tracebackhide__ = True + + assert len(t1) == len(t2) + assert t1.colnames == t2.colnames + if check_meta: + assert t1.meta == t2.meta + for name in t1.colnames: + if len(t1) != 0: + assert t1[name].dtype.kind == t2[name].dtype.kind + if not isinstance(t1[name], MaskedColumn): + for i, el in enumerate(t1[name]): + try: + if not isinstance(el, str) and np.isnan(el): + assert not isinstance(t2[name][i], str) and np.isnan( + t2[name][i] + ) + elif isinstance(el, str): + assert el == t2[name][i] + else: + assert_allclose(el, t2[name][i], rtol=rtol, atol=atol) + except (TypeError, NotImplementedError): + pass # ignore for now + + +# Use this counter to create a unique filename for each file created in a test +# if this function is called more than once in a single test +_filename_counter = 0 + + +def _read( + tmp_path, + table, + reader_cls=None, + format=None, + check_meta=False, + **kwargs, +): + # make sure we have a newline so table can't be misinterpreted as a filename + global _filename_counter + + table += "\n" + reader = reader_cls(**kwargs) + t1 = reader.read(table) + t2 = reader.read(StringIO(table)) + t3 = reader.read(table.splitlines()) + t4 = ascii.read(table, format=format, guess=False, **kwargs) + t5 = ascii.read(table, format=format, guess=False, fast_reader=False, **kwargs) + assert_table_equal(t1, t2, check_meta=check_meta) + assert_table_equal(t2, t3, check_meta=check_meta) + assert_table_equal(t3, t4, check_meta=check_meta) + assert_table_equal(t4, t5, check_meta=check_meta) + + filename = tmp_path / f"table{_filename_counter}.txt" + _filename_counter += 1 + + with open(filename, "wb") as f: + f.write(table.encode("ascii")) + f.flush() + + t7 = ascii.read(filename, format=format, guess=False, **kwargs) + + assert_table_equal(t1, t7, check_meta=check_meta) + return t1 + + +@pytest.fixture(scope="function") +def read_basic(tmp_path, request): + return functools.partial(_read, tmp_path, reader_cls=FastBasic, format="basic") + + +@pytest.fixture(scope="function") +def read_csv(tmp_path, request): + return functools.partial(_read, tmp_path, reader_cls=FastCsv, format="csv") + + +@pytest.fixture(scope="function") +def read_tab(tmp_path, request): + return functools.partial(_read, tmp_path, reader_cls=FastTab, format="tab") + + +@pytest.fixture(scope="function") +def read_commented_header(tmp_path, request): + return functools.partial( + _read, tmp_path, reader_cls=FastCommentedHeader, format="commented_header" + ) + + +@pytest.fixture(scope="function") +def read_rdb(tmp_path, request): + return functools.partial(_read, tmp_path, reader_cls=FastRdb, format="rdb") + + +@pytest.fixture(scope="function") +def read_no_header(tmp_path, request): + return functools.partial( + _read, tmp_path, reader_cls=FastNoHeader, format="no_header" + ) + + +@pytest.mark.parametrize("delimiter", [",", "\t", " ", "csv"]) +@pytest.mark.parametrize("quotechar", ['"', "'"]) +@pytest.mark.parametrize("fast", [False, True]) +def test_embedded_newlines(delimiter, quotechar, fast): + """Test that embedded newlines are supported for io.ascii readers + and writers, both fast and Python readers.""" + # Start with an assortment of values with different embedded newlines and whitespace + dat = [ + ["\t a ", " b \n cd ", "\n"], + [" 1\n ", '2 \n" \t 3\n4\n5', "1\n '2\n"], + [" x,y \nz\t", "\t 12\n\t34\t ", "56\t\n"], + ] + dat = Table(dat, names=("a", "b", "c")) + + # Construct a table which is our expected result of writing the table and + # reading it back. Certain stripping of whitespace is expected. + exp = {} # expected output from reading + for col in dat.itercols(): + vals = [] + for val in col: + # Readers and writers both strip whitespace from ends of values + val = val.strip(" \t") + if not fast: + # Pure Python reader has a "feature" where it strips trailing + # whitespace from each input line. This means a value like + # " x \ny \t\n" gets read as "x\ny". + bits = val.splitlines(keepends=True) + bits_out = [] + for bit in bits: + bit = re.sub(r"[ \t]+(\n?)$", r"\1", bit.strip(" \t")) + bits_out.append(bit) + val = "".join(bits_out) + vals.append(val) + exp[col.info.name] = vals + exp = Table(exp) + + if delimiter == "csv": + format = "csv" + delimiter = "," + else: + format = "basic" + + # Write the table to `text` + fh = io.StringIO() + ascii.write( + dat, + fh, + format=format, + delimiter=delimiter, + quotechar=quotechar, + fast_writer=fast, + ) + text = fh.getvalue() + + # Read it back and compare to the expected + dat_out = ascii.read( + text, + format=format, + guess=False, + delimiter=delimiter, + quotechar=quotechar, + fast_reader=fast, + ) + + eq = dat_out.values_equal(exp) + assert all(np.all(col) for col in eq.itercols()) + + +def test_simple_data(read_basic): + """ + Make sure the fast reader works with basic input data. + """ + table = read_basic("A B C\n1 2 3\n4 5 6") + expected = Table([[1, 4], [2, 5], [3, 6]], names=("A", "B", "C")) + assert_table_equal(table, expected) + + +def test_read_types(): + """ + Make sure that the read() function takes filenames, + strings, and lists of strings in addition to file-like objects. + """ + t1 = ascii.read("a b c\n1 2 3\n4 5 6", format="fast_basic", guess=False) + # TODO: also read from file + t2 = ascii.read(StringIO("a b c\n1 2 3\n4 5 6"), format="fast_basic", guess=False) + t3 = ascii.read(["a b c", "1 2 3", "4 5 6"], format="fast_basic", guess=False) + assert_table_equal(t1, t2) + assert_table_equal(t2, t3) + + +def test_supplied_names(read_basic): + """ + If passed as a parameter, names should replace any + column names found in the header. + """ + table = read_basic("A B C\n1 2 3\n4 5 6", names=("X", "Y", "Z")) + expected = Table([[1, 4], [2, 5], [3, 6]], names=("X", "Y", "Z")) + assert_table_equal(table, expected) + + +def test_no_header(read_basic, read_no_header): + """ + The header should not be read when header_start=None. Unless names is + passed, the column names should be auto-generated. + """ + # Cannot set header_start=None for basic format + with pytest.raises(ValueError): + read_basic("A B C\n1 2 3\n4 5 6", header_start=None, data_start=0) + + t2 = read_no_header("A B C\n1 2 3\n4 5 6") + expected = Table( + [["A", "1", "4"], ["B", "2", "5"], ["C", "3", "6"]], + names=("col1", "col2", "col3"), + ) + assert_table_equal(t2, expected) + + +def test_no_header_supplied_names(read_basic, read_no_header): + """ + If header_start=None and names is passed as a parameter, header + data should not be read and names should be used instead. + """ + table = read_no_header("A B C\n1 2 3\n4 5 6", names=("X", "Y", "Z")) + expected = Table( + [["A", "1", "4"], ["B", "2", "5"], ["C", "3", "6"]], names=("X", "Y", "Z") + ) + assert_table_equal(table, expected) + + +def test_comment(read_basic): + """ + Make sure that line comments are ignored by the C reader. + """ + table = read_basic("# comment\nA B C\n # another comment\n1 2 3\n4 5 6") + expected = Table([[1, 4], [2, 5], [3, 6]], names=("A", "B", "C")) + assert_table_equal(table, expected) + + +def test_empty_lines(read_basic): + """ + Make sure that empty lines are ignored by the C reader. + """ + table = read_basic("\n\nA B C\n1 2 3\n\n\n4 5 6\n\n\n\n") + expected = Table([[1, 4], [2, 5], [3, 6]], names=("A", "B", "C")) + assert_table_equal(table, expected) + + +def test_lstrip_whitespace(read_basic): + """ + Test to make sure the reader ignores whitespace at the beginning of fields. + """ + text = """ + 1, 2, \t3 + A,\t\t B, C + a, b, c + \n""" + + table = read_basic(text, delimiter=",") + expected = Table([["A", "a"], ["B", "b"], ["C", "c"]], names=("1", "2", "3")) + assert_table_equal(table, expected) + + +def test_rstrip_whitespace(read_basic): + """ + Test to make sure the reader ignores whitespace at the end of fields. + """ + text = " 1 ,2 \t,3 \nA\t,B ,C\t \t \n \ta ,b , c \n" + table = read_basic(text, delimiter=",") + expected = Table([["A", "a"], ["B", "b"], ["C", "c"]], names=("1", "2", "3")) + assert_table_equal(table, expected) + + +def test_conversion(read_basic): + """ + The reader should try to convert each column to ints. If this fails, the + reader should try to convert to floats. Failing this, i.e. on parsing + non-numeric input including isolated positive/negative signs, it should + fall back to strings. + """ + text = """ +A B C D E F G H +1 a 3 4 5 6 7 8 +2. 1 9 -.1e1 10.0 8.7 6 -5.3e4 +4 2 -12 .4 +.e1 - + six +""" + table = read_basic(text) + assert table["A"].dtype.kind == "f" + assert table["B"].dtype.kind in ("S", "U") + assert table["C"].dtype.kind == "i" + assert table["D"].dtype.kind == "f" + assert table["E"].dtype.kind in ("S", "U") + assert table["F"].dtype.kind in ("S", "U") + assert table["G"].dtype.kind in ("S", "U") + assert table["H"].dtype.kind in ("S", "U") + + +def test_delimiter(read_basic): + """ + Make sure that different delimiters work as expected. + """ + text = dedent( + """ + COL1 COL2 COL3 + 1 A -1 + 2 B -2 + """ + ) + expected = Table([[1, 2], ["A", "B"], [-1, -2]], names=("COL1", "COL2", "COL3")) + + for sep in " ,\t#;": + table = read_basic( + text.replace(" ", sep), + delimiter=sep, + ) + assert_table_equal(table, expected) + + +def test_include_names(read_basic): + """ + If include_names is not None, the parser should read only those columns in include_names. + """ + table = read_basic("A B C D\n1 2 3 4\n5 6 7 8", include_names=["A", "D"]) + expected = Table([[1, 5], [4, 8]], names=("A", "D")) + assert_table_equal(table, expected) + + +def test_exclude_names(read_basic): + """ + If exclude_names is not None, the parser should exclude the columns in exclude_names. + """ + table = read_basic("A B C D\n1 2 3 4\n5 6 7 8", exclude_names=["A", "D"]) + expected = Table([[2, 6], [3, 7]], names=("B", "C")) + assert_table_equal(table, expected) + + +def test_include_exclude_names(read_basic): + """ + Make sure that include_names is applied before exclude_names if both are specified. + """ + text = dedent( + """ + A B C D E F G H + 1 2 3 4 5 6 7 8 + 9 10 11 12 13 14 15 16 + """ + ) + table = read_basic( + text, include_names=["A", "B", "D", "F", "H"], exclude_names=["B", "F"] + ) + expected = Table([[1, 9], [4, 12], [8, 16]], names=("A", "D", "H")) + assert_table_equal(table, expected) + + +def test_doubled_quotes(read_csv): + """ + Test #8283 (fix for #8281), parsing doubled-quotes "ab""cd" in a quoted + field was incorrect. + + """ + tbl = "\n".join( # noqa: FLY002 + [ + "a,b", + '"d""","d""q"', + '"""q",""""', + ] + ) + # fmt: off + expected = Table([['d"', '"q'], + ['d"q', '"']], + names=('a', 'b')) + # fmt: on + dat = read_csv(tbl) + assert_table_equal(dat, expected) + + # In addition to the local read_csv wrapper, check that default + # parsing with guessing gives the right answer. + for fast_reader in True, False: + dat = ascii.read(tbl, fast_reader=fast_reader) + assert_table_equal(dat, expected) + + +@pytest.mark.filterwarnings( + "ignore:OverflowError converting to IntType in column TIMESTAMP" +) +def test_doubled_quotes_segv(): + """ + Test the exact example from #8281 which resulted in SEGV prior to #8283 + (in contrast to the tests above that just gave the wrong answer). + Attempts to produce a more minimal example were unsuccessful, so the whole + thing is included. + """ + tbl = dedent( + """ + "ID","TIMESTAMP","addendum_id","bib_reference","bib_reference_url","client_application","client_category","client_sort_key","color","coordsys","creator","creator_did","data_pixel_bitpix","dataproduct_subtype","dataproduct_type","em_max","em_min","format","hips_builder","hips_copyright","hips_creation_date","hips_creation_date_1","hips_creator","hips_data_range","hips_estsize","hips_frame","hips_glu_tag","hips_hierarchy","hips_initial_dec","hips_initial_fov","hips_initial_ra","hips_lon_asc","hips_master_url","hips_order","hips_order_1","hips_order_4","hips_order_min","hips_overlay","hips_pixel_bitpix","hips_pixel_cut","hips_pixel_scale","hips_progenitor_url","hips_publisher","hips_release_date","hips_release_date_1","hips_rgb_blue","hips_rgb_green","hips_rgb_red","hips_sampling","hips_service_url","hips_service_url_1","hips_service_url_2","hips_service_url_3","hips_service_url_4","hips_service_url_5","hips_service_url_6","hips_service_url_7","hips_service_url_8","hips_skyval","hips_skyval_method","hips_skyval_value","hips_status","hips_status_1","hips_status_2","hips_status_3","hips_status_4","hips_status_5","hips_status_6","hips_status_7","hips_status_8","hips_tile_format","hips_tile_format_1","hips_tile_format_4","hips_tile_width","hips_version","hipsgen_date","hipsgen_date_1","hipsgen_date_10","hipsgen_date_11","hipsgen_date_12","hipsgen_date_2","hipsgen_date_3","hipsgen_date_4","hipsgen_date_5","hipsgen_date_6","hipsgen_date_7","hipsgen_date_8","hipsgen_date_9","hipsgen_params","hipsgen_params_1","hipsgen_params_10","hipsgen_params_11","hipsgen_params_12","hipsgen_params_2","hipsgen_params_3","hipsgen_params_4","hipsgen_params_5","hipsgen_params_6","hipsgen_params_7","hipsgen_params_8","hipsgen_params_9","label","maxOrder","moc_access_url","moc_order","moc_release_date","moc_sky_fraction","obs_ack","obs_collection","obs_copyrigh_url","obs_copyright","obs_copyright_1","obs_copyright_url","obs_copyright_url_1","obs_description","obs_description_url","obs_descrition_url","obs_id","obs_initial_dec","obs_initial_fov","obs_initial_ra","obs_provenance","obs_regime","obs_title","ohips_frame","pixelCut","pixelRange","prov_did","prov_progenitor","prov_progenitor_url","publisher_did","publisher_id","s_pixel_scale","t_max","t_min" + "CDS/P/2MASS/H","1524123841000","","2006AJ....131.1163S","http://cdsbib.unistra.fr/cgi-bin/cdsbib?2006AJ....131.1163S","AladinDesktop","Image/Infrared/2MASS","04-001-03","","","","ivo://CDS/P/2MASS/H","","","image","1.798E-6","1.525E-6","","Aladin/HipsGen v9.017","CNRS/Unistra","2013-05-06T20:36Z","","CDS (A.Oberto)","","","equatorial","","mean","","","","","","9","","","","","","0 60","2.236E-4","","","2016-04-22T13:48Z","","","","","","http://alasky.unistra.fr/2MASS/H","https://irsa.ipac.caltech.edu/data/hips/CDS/2MASS/H","http://alaskybis.unistra.fr/2MASS/H","https://alaskybis.unistra.fr/2MASS/H","","","","","","","","","public master clonableOnce","public mirror unclonable","public mirror clonableOnce","public mirror clonableOnce","","","","","","jpeg fits","","","512","1.31","","","","","","","","","","","","","","","","","","","","","","","","","","","","","http://alasky.unistra.fr/2MASS/H/Moc.fits","9","","1","University of Massachusetts & IPAC/Caltech","The Two Micron All Sky Survey - H band (2MASS H)","","University of Massachusetts & IPAC/Caltech","","http://www.ipac.caltech.edu/2mass/","","2MASS has uniformly scanned the entire sky in three near-infrared bands to detect and characterize point sources brighter than about 1 mJy in each band, with signal-to-noise ratio (SNR) greater than 10, using a pixel size of 2.0"". This has achieved an 80,000-fold improvement in sensitivity relative to earlier surveys. 2MASS used two highly-automated 1.3-m telescopes, one at Mt. Hopkins, AZ, and one at CTIO, Chile. Each telescope was equipped with a three-channel camera, each channel consisting of a 256x256 array of HgCdTe detectors, capable of observing the sky simultaneously at J (1.25 microns), H (1.65 microns), and Ks (2.17 microns). The University of Massachusetts (UMass) was responsible for the overall management of the project, and for developing the infrared cameras and on-site computing systems at both facilities. The Infrared Processing and Analysis Center (IPAC) is responsible for all data processing through the Production Pipeline, and construction and distribution of the data products. Funding is provided primarily by NASA and the NSF","","","","+0","0.11451621372724685","0","","Infrared","2MASS H (1.66um)","","","","","IPAC/NASA","","","","","51941","50600" + """ + ) + ascii.read(tbl, format="csv", fast_reader=True, guess=False) + + +def test_quoted_fields(read_basic): + """ + The character quotechar (default '"') should denote the start of a field which can + contain the field delimiter and newlines. + """ + text = dedent( + """ + "A B" C D + 1.5 2.1 -37.1 + a b " c + d" + """ + ) + table = read_basic(text) + expected = Table( + [["1.5", "a"], ["2.1", "b"], ["-37.1", "c\nd"]], names=("A B", "C", "D") + ) + assert_table_equal(table, expected) + table = read_basic(text.replace('"', "'"), quotechar="'") + assert_table_equal(table, expected) + + +@pytest.mark.parametrize( + "key,val", + [ + ("delimiter", ",,"), # multi-char delimiter + ("comment", "##"), # multi-char comment + ("data_start", None), # data_start=None + ("data_start", -1), # data_start negative + ("quotechar", "##"), # multi-char quote signifier + ("header_start", -1), # negative header_start + ( + "converters", + {i + 1: ascii.convert_numpy(np.uint) for i in range(3)}, + ), # passing converters + ("inputter_cls", ascii.ContinuationLinesInputter), # passing inputter_cls + ("header_splitter_cls", ascii.DefaultSplitter), # passing Splitter + ("data_splitter_cls", ascii.DefaultSplitter), + ], +) +def test_invalid_parameters(key, val): + """ + Make sure the C reader raises an error if passed parameters it can't handle. + """ + with pytest.raises(ParameterError): + FastBasic(**{key: val}).read("1 2 3\n4 5 6") + with pytest.raises(ParameterError): + ascii.read("1 2 3\n4 5 6", format="fast_basic", guess=False, **{key: val}) + + +def test_invalid_parameters_other(): + with pytest.raises(TypeError): + FastBasic(foo=7).read("1 2 3\n4 5 6") # unexpected argument + with pytest.raises(FastOptionsError): # don't fall back on the slow reader + ascii.read("1 2 3\n4 5 6", format="basic", fast_reader={"foo": 7}) + with pytest.raises(ParameterError): + # outputter_cls cannot be specified in constructor + FastBasic(outputter_cls=ascii.TableOutputter).read("1 2 3\n4 5 6") + + +def test_too_many_cols1(): + """ + If a row contains too many columns, the C reader should raise an error. + """ + text = dedent( + """ + A B C + 1 2 3 + 4 5 6 + 7 8 9 10 + 11 12 13 + """ + ) + with pytest.raises(InconsistentTableError) as e: + FastBasic().read(text) + assert ( + "Number of header columns (3) inconsistent with data columns in data line 2" + in str(e.value) + ) + + +def test_too_many_cols2(): + text = """\ +aaa,bbb +1,2, +3,4, +""" + with pytest.raises(InconsistentTableError) as e: + FastCsv().read(text) + assert ( + "Number of header columns (2) inconsistent with data columns in data line 0" + in str(e.value) + ) + + +def test_too_many_cols3(): + text = """\ +aaa,bbb +1,2,, +3,4, +""" + with pytest.raises(InconsistentTableError) as e: + FastCsv().read(text) + assert ( + "Number of header columns (2) inconsistent with data columns in data line 0" + in str(e.value) + ) + + +def test_too_many_cols4(): + # https://github.com/astropy/astropy/issues/9922 + with pytest.raises(InconsistentTableError) as e: + ascii.read( + get_pkg_data_filename("data/conf_py.txt"), fast_reader=True, guess=True + ) + assert "Unable to guess table format with the guesses listed below" in str(e.value) + + +def test_not_enough_cols(read_csv): + """ + If a row does not have enough columns, the FastCsv reader should add empty + fields while the FastBasic reader should raise an error. + """ + text = """ +A,B,C +1,2,3 +4,5 +6,7,8 +""" + table = read_csv(text) + assert table["B"][1] is not ma.masked + assert table["C"][1] is ma.masked + + with pytest.raises(InconsistentTableError): + table = FastBasic(delimiter=",").read(text) + + +def test_data_end(read_basic, read_rdb): + """ + The parameter data_end should specify where data reading ends. + """ + text = """ +A B C +1 2 3 +4 5 6 +7 8 9 +10 11 12 +""" + table = read_basic(text, data_end=3) + expected = Table([[1, 4], [2, 5], [3, 6]], names=("A", "B", "C")) + assert_table_equal(table, expected) + + # data_end supports negative indexing + table = read_basic(text, data_end=-2) + assert_table_equal(table, expected) + + text = """ +A\tB\tC +N\tN\tS +1\t2\ta +3\t4\tb +5\t6\tc +""" + # make sure data_end works with RDB + table = read_rdb(text, data_end=-1) + expected = Table([[1, 3], [2, 4], ["a", "b"]], names=("A", "B", "C")) + assert_table_equal(table, expected) + + # positive index + table = read_rdb(text, data_end=3) + expected = Table([[1], [2], ["a"]], names=("A", "B", "C")) + assert_table_equal(table, expected) + + # empty table if data_end is too small + table = read_rdb(text, data_end=1) + expected = Table([[], [], []], names=("A", "B", "C")) + assert_table_equal(table, expected) + + +def test_inf_nan(read_basic): + """ + Test that inf and nan-like values are correctly parsed on all platforms. + + Regression test for https://github.com/astropy/astropy/pull/3525 + """ + + text = dedent( + """\ + A + nan + +nan + -nan + inf + infinity + +inf + +infinity + -inf + -infinity + """ + ) + + expected = Table( + { + "A": [ + np.nan, + np.nan, + np.nan, + np.inf, + np.inf, + np.inf, + np.inf, + -np.inf, + -np.inf, + ] + } + ) + + table = read_basic(text) + assert table["A"].dtype.kind == "f" + assert_table_equal(table, expected) + + +def test_fill_values(read_basic): + """ + Make sure that the parameter fill_values works as intended. If fill_values + is not specified, the default behavior should be to convert '' to 0. + """ + text = """ +A, B, C +, 2, nan +a, -999, -3.4 +nan, 5, -9999 +8, nan, 7.6e12 +""" + table = read_basic(text, delimiter=",") + # The empty value in row A should become a masked '0' + assert isinstance(table["A"], MaskedColumn) + assert table["A"][0] is ma.masked + # '0' rather than 0 because there is a string in the column + assert table["A"].data.data[0] == "0" + assert table["A"][1] is not ma.masked + + table = read_basic(text, delimiter=",", fill_values=("-999", "0")) + assert isinstance(table["B"], MaskedColumn) + assert table["A"][0] is not ma.masked # empty value unaffected + assert table["C"][2] is not ma.masked # -9999 is not an exact match + assert table["B"][1] is ma.masked + # Numeric because the rest of the column contains numeric data + assert table["B"].data.data[1] == 0.0 + assert table["B"][0] is not ma.masked + + table = read_basic(text, delimiter=",", fill_values=[]) + # None of the columns should be masked + for name in "ABC": + assert not isinstance(table[name], MaskedColumn) + + table = read_basic( + text, delimiter=",", fill_values=[("", "0", "A"), ("nan", "999", "A", "C")] + ) + assert np.isnan(table["B"][3]) # nan filling skips column B + # should skip masking as well as replacing nan + assert table["B"][3] is not ma.masked + assert table["A"][0] is ma.masked + assert table["A"][2] is ma.masked + assert table["A"].data.data[0] == "0" + assert table["A"].data.data[2] == "999" + assert table["C"][0] is ma.masked + assert_allclose(table["C"].data.data[0], 999.0) + assert_allclose(table["C"][1], -3.4) # column is still of type float + + +def test_fill_include_exclude_names(read_csv): + """ + fill_include_names and fill_exclude_names should filter missing/empty value handling + in the same way that include_names and exclude_names filter output columns. + """ + text = """ +A, B, C +, 1, 2 +3, , 4 +5, 5, +""" + table = read_csv(text, fill_include_names=["A", "B"]) + assert table["A"][0] is ma.masked + assert table["B"][1] is ma.masked + assert table["C"][2] is not ma.masked # C not in fill_include_names + + table = read_csv(text, fill_exclude_names=["A", "B"]) + assert table["C"][2] is ma.masked + assert table["A"][0] is not ma.masked + assert table["B"][1] is not ma.masked # A and B excluded from fill handling + + table = read_csv(text, fill_include_names=["A", "B"], fill_exclude_names=["B"]) + assert table["A"][0] is ma.masked + # fill_exclude_names applies after fill_include_names + assert table["B"][1] is not ma.masked + assert table["C"][2] is not ma.masked + + +def test_many_rows(read_basic): + """ + Make sure memory reallocation works okay when the number of rows + is large (so that each column string is longer than INITIAL_COL_SIZE). + """ + text = "A B C\n" + for i in range(500): # create 500 rows + text += " ".join([str(i) for i in range(3)]) + text += "\n" + + table = read_basic(text) + expected = Table([[0] * 500, [1] * 500, [2] * 500], names=("A", "B", "C")) + assert_table_equal(table, expected) + + +def test_many_columns(read_basic): + """ + Make sure memory reallocation works okay when the number of columns + is large (so that each header string is longer than INITIAL_HEADER_SIZE). + """ + # create a string with 500 columns and two data rows + text = " ".join([str(i) for i in range(500)]) + text += "\n" + text + "\n" + text + table = read_basic(text) + expected = Table([[i, i] for i in range(500)], names=[str(i) for i in range(500)]) + assert_table_equal(table, expected) + + +def test_fast_reader(): + """ + Make sure that ascii.read() works as expected by default and with + fast_reader specified. + """ + text = "a b c\n1 2 3\n4 5 6" + with pytest.raises(ParameterError): # C reader can't handle regex comment + ascii.read(text, format="fast_basic", guess=False, comment="##") + + # Enable the fast converter + ascii.read( + text, format="basic", guess=False, fast_reader={"use_fast_converter": True} + ) + + # Should raise an error if fast_reader has an invalid key + with pytest.raises(FastOptionsError): + ascii.read(text, format="fast_basic", guess=False, fast_reader={"foo": True}) + + # Use the slow reader instead + ascii.read(text, format="basic", guess=False, comment="##", fast_reader=False) + # Will try the slow reader afterwards by default + ascii.read(text, format="basic", guess=False, comment="##") + + +def test_read_tab(read_tab): + """ + The fast reader for tab-separated values should not strip whitespace, unlike + the basic reader. + """ + text = '1\t2\t3\n a\t b \t\n c\t" d\n e"\t ' + table = read_tab(text) + assert table["1"][0] == " a" # preserve line whitespace + assert table["2"][0] == " b " # preserve field whitespace + assert table["3"][0] is ma.masked # empty value should be masked + assert table["2"][1] == " d\n e" # preserve whitespace in quoted fields + assert table["3"][1] == " " # preserve end-of-line whitespace + + +def test_default_data_start(read_basic): + """ + If data_start is not explicitly passed to read(), data processing should + beginning right after the header. + """ + text = "ignore this line\na b c\n1 2 3\n4 5 6" + table = read_basic(text, header_start=1) + expected = Table([[1, 4], [2, 5], [3, 6]], names=("a", "b", "c")) + assert_table_equal(table, expected) + + +def test_commented_header(read_commented_header): + """ + The FastCommentedHeader reader should mimic the behavior of the + CommentedHeader by overriding the default header behavior of FastBasic. + """ + text = """ + # A B C + 1 2 3 + 4 5 6 +""" + t1 = read_commented_header(text) + expected = Table([[1, 4], [2, 5], [3, 6]], names=("A", "B", "C")) + assert_table_equal(t1, expected) + + text = "# first commented line\n # second commented line\n\n" + text + t2 = read_commented_header(text, header_start=2, data_start=0) + assert_table_equal(t2, expected) + # negative indexing allowed + t3 = read_commented_header(text, header_start=-1, data_start=0) + assert_table_equal(t3, expected) + + text += "7 8 9" + t4 = read_commented_header(text, header_start=2, data_start=2) + expected = Table([[7], [8], [9]], names=("A", "B", "C")) + assert_table_equal(t4, expected) + + with pytest.raises(ParameterError): + # data_start cannot be negative + read_commented_header( + text, + header_start=-1, + data_start=-1, + ) + + +def test_rdb(read_rdb): + """ + Make sure the FastRdb reader works as expected. + """ + text = """ + +A\tB\tC +1n\tS\t4N +1\t 9\t4.3 +""" + table = read_rdb(text) + expected = Table([[1], [" 9"], [4.3]], names=("A", "B", "C")) + assert_table_equal(table, expected) + assert table["A"].dtype.kind == "i" + assert table["B"].dtype.kind in ("S", "U") + assert table["C"].dtype.kind == "f" + + with pytest.raises(ValueError) as e: + text = "A\tB\tC\nN\tS\tN\n4\tb\ta" # C column contains non-numeric data + read_rdb( + text, + ) + assert "Column C failed to convert" in str(e.value) + + with pytest.raises(ValueError) as e: + text = "A\tB\tC\nN\tN\n1\t2\t3" # not enough types specified + read_rdb( + text, + ) + assert "mismatch between number of column names and column types" in str(e.value) + + with pytest.raises(ValueError) as e: + text = "A\tB\tC\nN\tN\t5\n1\t2\t3" # invalid type for column C + read_rdb( + text, + ) + assert "type definitions do not all match [num](N|S)" in str(e.value) + + +def test_data_start(read_basic): + """ + Make sure that data parsing begins at data_start (ignoring empty and + commented lines but not taking quoted values into account). + """ + text = """ +A B C +1 2 3 +4 5 6 + +7 8 "9 +1" +# comment +10 11 12 +""" + table = read_basic(text, data_start=2) + expected = Table( + [[4, 7, 10], [5, 8, 11], ["6", "9\n1", "12"]], names=("A", "B", "C") + ) + assert_table_equal(table, expected) + + table = read_basic(text, data_start=3) + # ignore empty line + expected = Table([[7, 10], [8, 11], ["9\n1", "12"]], names=("A", "B", "C")) + assert_table_equal(table, expected) + + with pytest.raises(InconsistentTableError) as e: + # tries to begin in the middle of quoted field + read_basic( + text, + data_start=4, + ) + assert "header columns (3) inconsistent with data columns in data line 0" in str( + e.value + ) + + table = read_basic(text, data_start=5) + # ignore commented line + expected = Table([[10], [11], [12]], names=("A", "B", "C")) + assert_table_equal(table, expected) + + text = """ +A B C +1 2 3 +4 5 6 + +7 8 9 +# comment +10 11 12 +""" + + +def test_quoted_empty_values(read_basic): + """ + Quoted empty values spanning multiple lines should be treated correctly. + """ + text = 'a b c\n1 2 " \n "' + table = read_basic(text) + assert table["c"][0] == "\n" # empty value masked by default + + +def test_csv_comment_default(read_csv): + """ + Unless the comment parameter is specified, the CSV reader should + not treat any lines as comments. + """ + text = "a,b,c\n#1,2,3\n4,5,6" + table = read_csv(text) + expected = Table([["#1", "4"], [2, 5], [3, 6]], names=("a", "b", "c")) + assert_table_equal(table, expected) + + +def test_whitespace_before_comment(read_tab): + """ + Readers that don't strip whitespace from data (Tab, RDB) + should still treat lines with leading whitespace and then + the comment char as comment lines. + """ + text = "a\tb\tc\n # comment line\n1\t2\t3" + table = read_tab(text) + expected = Table([[1], [2], [3]], names=("a", "b", "c")) + assert_table_equal(table, expected) + + +def test_strip_line_trailing_whitespace(read_basic): + """ + Readers that strip whitespace from lines should ignore + trailing whitespace after the last data value of each + row. + """ + text = "a b c\n1 2 \n3 4 5" + with pytest.raises(InconsistentTableError) as e: + ascii.read(StringIO(text), format="fast_basic", guess=False) + assert "header columns (3) inconsistent with data columns in data line 0" in str( + e.value + ) + + text = "a b c\n 1 2 3 \t \n 4 5 6 " + table = read_basic(text) + expected = Table([[1, 4], [2, 5], [3, 6]], names=("a", "b", "c")) + assert_table_equal(table, expected) + + +def test_no_data(read_basic): + """ + As long as column names are supplied, the C reader + should return an empty table in the absence of data. + """ + table = read_basic("a b c") + expected = Table([[], [], []], names=("a", "b", "c")) + assert_table_equal(table, expected) + + table = read_basic("a b c\n1 2 3", data_start=2) + assert_table_equal(table, expected) + + +def test_line_endings(read_basic, read_commented_header, read_rdb): + """ + Make sure the fast reader accepts CR and CR+LF + as newlines. + """ + text = "a b c\n1 2 3\n4 5 6\n7 8 9\n" + expected = Table([[1, 4, 7], [2, 5, 8], [3, 6, 9]], names=("a", "b", "c")) + + for newline in ("\r\n", "\r"): + table = read_basic( + text.replace("\n", newline), + ) + assert_table_equal(table, expected) + + # Make sure the splitlines() method of FileString + # works with CR/CR+LF line endings + text = "#" + text + for newline in ("\r\n", "\r"): + table = read_commented_header( + text.replace("\n", newline), + ) + assert_table_equal(table, expected) + + expected = Table( + [MaskedColumn([1, 4, 7]), [2, 5, 8], MaskedColumn([3, 6, 9])], + names=("a", "b", "c"), + ) + expected["a"][0] = np.ma.masked + expected["c"][0] = np.ma.masked + text = "a\tb\tc\nN\tN\tN\n\t2\t\n4\t5\t6\n7\t8\t9\n" + for newline in ("\r\n", "\r"): + table = read_rdb( + text.replace("\n", newline), + ) + assert_table_equal(table, expected) + assert np.all(table == expected) + + +def test_store_comments(read_basic): + """ + Make sure that the output Table produced by the fast + reader stores any comment lines in its meta attribute. + """ + text = """ +# header comment +a b c +# comment 2 +# comment 3 +1 2 3 +4 5 6 +""" + table = read_basic(text, check_meta=True) + assert table.meta["comments"] == ["header comment", "comment 2", "comment 3"] + + +def test_empty_quotes(read_basic): + """ + Make sure the C reader doesn't segfault when the + input data contains empty quotes. [#3407] + """ + table = read_basic('a b\n1 ""\n2 ""') + expected = Table([[1, 2], [0, 0]], names=("a", "b")) + assert_table_equal(table, expected) + + +def test_fast_tab_with_names(read_tab): + """ + Make sure the C reader doesn't segfault when the header for the + first column is missing [#3545] + """ + content = """# +\tdecDeg\tRate_pn_offAxis\tRate_mos2_offAxis\tObsID\tSourceID\tRADeg\tversion\tCounts_pn\tRate_pn\trun\tRate_mos1\tRate_mos2\tInserted_pn\tInserted_mos2\tbeta\tRate_mos1_offAxis\trcArcsec\tname\tInserted\tCounts_mos1\tInserted_mos1\tCounts_mos2\ty\tx\tCounts\toffAxis\tRot +-3.007559\t0.0000\t0.0010\t0013140201\t0\t213.462574\t0\t2\t0.0002\t0\t0.0001\t0.0001\t0\t1\t0.66\t0.0217\t3.0\tfakeXMMXCS J1413.8-0300\t3\t1\t2\t1\t398.000\t127.000\t5\t13.9\t72.3\t""" + head = [f"A{i}" for i in range(28)] + read_tab(content, data_start=1, names=head) + + +@pytest.mark.hugemem +def test_read_big_table(tmp_path): + """Test reading of a huge file. + + This test generates a huge CSV file (~2.3Gb) before reading it (see + https://github.com/astropy/astropy/pull/5319). The test is run only if the + ``--run-hugemem`` cli option is given. Note that running the test requires + quite a lot of memory (~18Gb when reading the file) !! + + """ + NB_ROWS = 250000 + NB_COLS = 500 + filename = tmp_path / "big_table.csv" + + print(f"Creating a {NB_ROWS} rows table ({NB_COLS} columns).") + data = np.random.random(NB_ROWS) + t = Table(data=[data] * NB_COLS, names=[str(i) for i in range(NB_COLS)]) + data = None + + print(f"Saving the table to {filename}") + t.write(filename, format="ascii.csv", overwrite=True) + t = None + + print( + "Counting the number of lines in the csv, it should be {NB_ROWS} + 1 (header)." + ) + with open(filename) as f: + assert sum(1 for line in f) == NB_ROWS + 1 + + print("Reading the file with astropy.") + t = Table.read(filename, format="ascii.csv", fast_reader=True) + assert len(t) == NB_ROWS + + +@pytest.mark.hugemem +def test_read_big_table2(tmp_path): + """Test reading of a file with a huge column.""" + # (2**32 // 2) : max value for int + # // 10 : we use a value for rows that have 10 chars (1e9) + # + 5 : add a few lines so the length cannot be stored by an int + NB_ROWS = 2**32 // 2 // 10 + 5 + filename = tmp_path / "big_table.csv" + + print(f"Creating a {NB_ROWS} rows table.") + data = np.full(NB_ROWS, int(1e9), dtype=np.int32) + t = Table(data=[data], names=["a"], copy=False) + + print(f"Saving the table to {filename}") + t.write(filename, format="ascii.csv", overwrite=True) + t = None + + print( + "Counting the number of lines in the csv, it should be {NB_ROWS} + 1 (header)." + ) + with open(filename) as f: + assert sum(1 for line in f) == NB_ROWS + 1 + + print("Reading the file with astropy.") + t = Table.read(filename, format="ascii.csv", fast_reader=True) + assert len(t) == NB_ROWS + + +# Test these both with guessing turned on and off +@pytest.mark.parametrize("guess", [True, False]) +# fast_reader configurations: False| 'use_fast_converter'=False|True +@pytest.mark.parametrize( + "fast_reader", + [False, {"use_fast_converter": False}, {"use_fast_converter": True}], +) +def test_data_out_of_range(fast_reader, guess): + """ + Numbers with exponents beyond float64 range (|~4.94e-324 to 1.7977e+308|) + shall be returned as 0 and +-inf respectively by the C parser, just like + the Python parser. + Test fast converter only to nominal accuracy. + """ + # Python reader and strtod() are expected to return precise results + rtol = 1.0e-30 + + # Update fast_reader dict; adapt relative precision for fast_converter + if fast_reader: + if fast_reader.get("use_fast_converter"): + rtol = 1.0e-15 + elif sys.maxsize < 2**32: + # On 32bit the standard C parser (strtod) returns strings for these + pytest.xfail("C parser cannot handle float64 on 32bit systems") + + if not fast_reader: + ctx = nullcontext() + else: + ctx = pytest.warns() + fields = ["10.1E+199", "3.14e+313", "2048e+306", "0.6E-325", "-2.e345"] + values = np.array([1.01e200, np.inf, np.inf, 0.0, -np.inf]) + # NOTE: Warning behavior varies for the parameters being passed in. + with ctx as w: + t = ascii.read( + StringIO(" ".join(fields)), + format="no_header", + guess=guess, + fast_reader=fast_reader, + ) + if fast_reader: # Assert precision warnings for cols 2-5 + assert len(w) == 4 + for i in range(len(w)): + assert f"OverflowError converting to FloatType in column col{i + 2}" in str( + w[i].message + ) + read_values = np.array([col[0] for col in t.itercols()]) + assert_allclose(read_values, values, rtol=rtol, atol=1.0e-324) + + # Test some additional corner cases + fields = [ + ".0101E202", + "0.000000314E+314", + "1777E+305", + "-1799E+305", + "0.2e-323", + "5200e-327", + " 0.0000000000000000000001024E+330", + ] + values = np.array( + [1.01e200, 3.14e307, 1.777e308, -np.inf, 0.0, 4.94e-324, 1.024e308] + ) + with ctx as w: + t = ascii.read( + StringIO(" ".join(fields)), + format="no_header", + guess=guess, + fast_reader=fast_reader, + ) + if fast_reader: # Assert precision warnings for cols 4-6 + if sys.platform == "win32" and not fast_reader.get("use_fast_converter"): + assert len(w) == 2 + else: + assert len(w) == 3 + for i in range(len(w)): + assert f"OverflowError converting to FloatType in column col{i + 4}" in str( + w[i].message + ) + read_values = np.array([col[0] for col in t.itercols()]) + assert_allclose(read_values, values, rtol=rtol, atol=1.0e-324) + + # Test corner cases again with non-standard exponent_style (auto-detection) + if fast_reader and fast_reader.get("use_fast_converter"): + fast_reader.update({"exponent_style": "A"}) + else: + pytest.skip("Fortran exponent style only available in fast converter") + + fields = [ + ".0101D202", + "0.000000314d+314", + "1777+305", + "-1799E+305", + "0.2e-323", + "2500-327", + " 0.0000000000000000000001024Q+330", + ] + with ctx as w: + t = ascii.read( + StringIO(" ".join(fields)), + format="no_header", + guess=guess, + fast_reader=fast_reader, + ) + if fast_reader: + # CI Windows identifies as "win32" but has 64 bit compiler; + # its `strtod` not emitting certain warnings. + if sys.platform == "win32" and not fast_reader.get("use_fast_converter"): + assert len(w) == 2 + else: + assert len(w) == 3 + read_values = np.array([col[0] for col in t.itercols()]) + assert_allclose(read_values, values, rtol=rtol, atol=1.0e-324) + + +@pytest.mark.parametrize("guess", [True, False]) +# fast_reader configurations: False| 'use_fast_converter'=False|True +@pytest.mark.parametrize( + "fast_reader", + [False, {"use_fast_converter": False}, {"use_fast_converter": True}], +) +def test_data_at_range_limit(fast_reader, guess): + """ + Test parsing of fixed-format float64 numbers near range limits + (|~4.94e-324 to 1.7977e+308|) - within limit for full precision + (|~2.5e-307| for strtod C parser, factor 10 better for fast_converter) + exact numbers shall be returned, beyond that an Overflow warning raised. + Input of exactly 0.0 must not raise an OverflowError. + """ + # Python reader and strtod() are expected to return precise results + rtol = 1.0e-30 + + # CI "win32" 64 bit compiler with `strtod` not emitting certain warnings. + if sys.platform == "win32": + ctx = nullcontext() + else: + ctx = pytest.warns() + + # Update fast_reader dict; adapt relative precision for fast_converter + if fast_reader: + # `xstrtod` behaves the same on win32 + if fast_reader.get("use_fast_converter"): + rtol = 1.0e-15 + ctx = pytest.warns() + elif sys.maxsize < 2**32: + # On 32bit the standard C parser (strtod) returns strings for these + pytest.xfail("C parser cannot handle float64 on 32bit systems") + + # Test very long fixed-format strings (to strtod range limit w/o Overflow) + for D in 99, 202, 305: + t = ascii.read( + StringIO(99 * "0" + "." + D * "0" + "1"), + format="no_header", + guess=guess, + fast_reader=fast_reader, + ) + assert_allclose(t["col1"][0], 10.0 ** -(D + 1), rtol=rtol, atol=1.0e-324) + for D in 99, 202, 308: + t = ascii.read( + StringIO("1" + D * "0" + ".0"), + format="no_header", + guess=guess, + fast_reader=fast_reader, + ) + assert_allclose(t["col1"][0], 10.0**D, rtol=rtol, atol=1.0e-324) + + # 0.0 is always exact (no Overflow warning)! + for s in "0.0", "0.0e+0", 399 * "0" + "." + 365 * "0": + t = ascii.read( + StringIO(s), format="no_header", guess=guess, fast_reader=fast_reader + ) + assert t["col1"][0] == 0.0 + + # Test OverflowError at precision limit with laxer rtol + if not fast_reader: + pytest.skip("Python/numpy reader does not raise on Overflow") + with ctx as w: + t = ascii.read( + StringIO("0." + 314 * "0" + "1"), + format="no_header", + guess=guess, + fast_reader=fast_reader, + ) + + if not isinstance(ctx, nullcontext): + assert len(w) == 1, f"Expected 1 warning, found {len(w)}" + assert ( + "OverflowError converting to FloatType in column col1, possibly " + "resulting in degraded precision" in str(w[0].message) + ) + + assert_allclose(t["col1"][0], 1.0e-315, rtol=1.0e-10, atol=1.0e-324) + + +@pytest.mark.parametrize("guess", [True, False]) +def test_int_out_of_range(guess): + """ + Integer numbers outside int range shall be returned as string columns + consistent with the standard (Python) parser (no 'upcasting' to float). + """ + imin = np.iinfo(np.int64).min + 1 + imax = np.iinfo(np.int64).max - 1 + huge = f"{imax + 2:d}" + + text = f"P M S\n {imax:d} {imin:d} {huge:s}" + expected = Table([[imax], [imin], [huge]], names=("P", "M", "S")) + # NOTE: Warning behavior varies for the parameters being passed in. + with pytest.warns() as w: + table = ascii.read( + text, + format="basic", + guess=guess, + ) + assert_table_equal(table, expected) + + # Check with leading zeroes to make sure strtol does not read them as octal + text = f"P M S\n000{imax:d} -0{-imin:d} 00{huge:s}" + expected = Table([[imax], [imin], ["00" + huge]], names=("P", "M", "S")) + with pytest.warns() as w: + table = ascii.read(text, format="basic", guess=guess) + assert_table_equal(table, expected) + + +@pytest.mark.parametrize("guess", [True, False]) +def test_int_out_of_order(guess): + """ + Mixed columns should be returned as float, but if the out-of-range integer + shows up first, it will produce a string column - with both readers. + """ + imax = np.iinfo(np.int64).max - 1 + text = f"A B\n 12.3 {imax:d}0\n {imax:d}0 45.6e7" + expected = Table([[12.3, 10.0 * imax], [f"{imax:d}0", "45.6e7"]], names=("A", "B")) + + with pytest.warns( + AstropyWarning, + match=r"OverflowError converting to " + r"IntType in column B, reverting to String", + ): + table = ascii.read(text, format="basic", guess=guess, fast_reader=True) + assert_table_equal(table, expected) + with pytest.warns( + AstropyWarning, + match=r"OverflowError converting to " + r"IntType in column B, reverting to String", + ): + table = ascii.read(text, format="basic", guess=guess, fast_reader=False) + assert_table_equal(table, expected) + + +@pytest.mark.parametrize("guess", [True, False]) +def test_fortran_reader(guess): + """ + Make sure that ascii.read() can read Fortran-style exponential notation + using the fast_reader. + """ + + # Check for nominal np.float64 precision + rtol = 1.0e-15 + atol = 0.0 + text = ( + "A B C D\n100.01{:s}99 2.0 2.0{:s}-103 3\n" + " 4.2{:s}-1 5.0{:s}-1 0.6{:s}4 .017{:s}+309" + ) + expc = Table( + [[1.0001e101, 0.42], [2, 0.5], [2.0e-103, 6.0e3], [3, 1.7e307]], + names=("A", "B", "C", "D"), + ) + + expstyles = { + "e": 6 * "E", + "D": ("D", "d", "d", "D", "d", "D"), + "Q": 3 * ("q", "Q"), + "Fortran": ("E", "0", "D", "Q", "d", "0"), + } + + # C strtod (not-fast converter) can't handle Fortran exp + with pytest.raises(FastOptionsError) as e: + ascii.read( + text.format(*(6 * "D")), + format="basic", + guess=guess, + fast_reader={ + "use_fast_converter": False, + "exponent_style": "D", + }, + ) + assert "fast_reader: exponent_style requires use_fast_converter" in str(e.value) + + # Enable multiprocessing and the fast converter iterate over + # all style-exponent combinations, with auto-detection + for s, c in expstyles.items(): + table = ascii.read( + text.format(*c), + guess=guess, + fast_reader={"exponent_style": s}, + ) + assert_table_equal(table, expc, rtol=rtol, atol=atol) + + # Additional corner-case checks including triple-exponents without + # any character and mixed whitespace separators + text = ( + "A B\t\t C D\n1.0001+101 2.0+000\t 0.0002-099 3\n " + "0.42-000 \t 0.5 6.+003 0.000000000000000000000017+330" + ) + table = ascii.read(text, guess=guess, fast_reader={"exponent_style": "A"}) + assert_table_equal(table, expc, rtol=rtol, atol=atol) + + +@pytest.mark.parametrize("guess", [True, False]) +def test_fortran_invalid_exp(guess): + """ + Test Fortran-style exponential notation in the fast_reader with invalid + exponent-like patterns (no triple-digits) to make sure they are returned + as strings instead, as with the standard C parser. + """ + + formats = {"basic": " ", "tab": "\t", "csv": ","} + header = ["S1", "F2", "S2", "F3", "S3", "F4", "F5", "S4", "I1", "F6", "F7"] + # Tested entries and expected returns, first for auto-detect, + # then for different specified exponents + # fmt: off + fields = ['1.0001+1', '.42d1', '2.3+10', '0.5', '3+1001', '3000.', + '2', '4.56e-2.3', '8000', '4.2-022', '.00000145e314'] + vals_e = ['1.0001+1', '.42d1', '2.3+10', 0.5, '3+1001', 3.e3, + 2, '4.56e-2.3', 8000, '4.2-022', 1.45e308] + vals_d = ['1.0001+1', 4.2, '2.3+10', 0.5, '3+1001', 3.e3, + 2, '4.56e-2.3', 8000, '4.2-022', '.00000145e314'] + vals_a = ['1.0001+1', 4.2, '2.3+10', 0.5, '3+1001', 3.e3, + 2, '4.56e-2.3', 8000, 4.2e-22, 1.45e308] + vals_v = ['1.0001+1', 4.2, '2.3+10', 0.5, '3+1001', 3.e3, + 2, '4.56e-2.3', 8000, '4.2-022', 1.45e308] + # fmt: on + + # Iterate over supported format types and separators + for f, s in formats.items(): + t1 = ascii.read( + StringIO(s.join(header) + "\n" + s.join(fields)), + format=f, + guess=guess, + fast_reader={"exponent_style": "A"}, + ) + assert_table_equal(t1, Table([[col] for col in vals_a], names=header)) + + # Non-basic separators require guessing enabled to be detected + if guess: + formats["bar"] = "|" + else: + formats = {"basic": " "} + + for s in formats.values(): + t2 = ascii.read( + StringIO(s.join(header) + "\n" + s.join(fields)), + guess=guess, + fast_reader={"exponent_style": "a"}, + ) + + assert_table_equal(t2, Table([[col] for col in vals_a], names=header)) + + # Iterate for (default) expchar 'E' + for s in formats.values(): + t3 = ascii.read( + StringIO(s.join(header) + "\n" + s.join(fields)), + guess=guess, + fast_reader={"use_fast_converter": True}, + ) + + assert_table_equal(t3, Table([[col] for col in vals_e], names=header)) + + # Iterate for expchar 'D' + for s in formats.values(): + t4 = ascii.read( + StringIO(s.join(header) + "\n" + s.join(fields)), + guess=guess, + fast_reader={"exponent_style": "D"}, + ) + + assert_table_equal(t4, Table([[col] for col in vals_d], names=header)) + + # Iterate for regular converter (strtod) + for s in formats.values(): + t5 = ascii.read( + StringIO(s.join(header) + "\n" + s.join(fields)), + guess=guess, + fast_reader={"use_fast_converter": False}, + ) + + read_values = [col[0] for col in t5.itercols()] + if os.name == "nt": + # Apparently C strtod() on (some?) MSVC recognizes 'd' exponents! + assert read_values in (vals_v, vals_e) + else: + assert read_values == vals_e + + +def test_fortran_reader_notbasic(): + """ + Check if readers without a fast option raise a value error when a + fast_reader is asked for (implies the default 'guess=True'). + """ + + tabstr = dedent( + """ + a b + 1 1.23D4 + 2 5.67D-8 + """ + )[1:-1] + + t1 = ascii.read(tabstr.split("\n"), fast_reader={"exponent_style": "D"}) + + assert t1["b"].dtype.kind == "f" + + tabrdb = dedent( + """ + a\tb + # A simple RDB table + N\tN + 1\t 1.23D4 + 2\t 5.67-008 + """ + )[1:-1] + + t2 = ascii.read( + tabrdb.split("\n"), format="rdb", fast_reader={"exponent_style": "fortran"} + ) + + assert t2["b"].dtype.kind == "f" + + tabrst = dedent( + """ + = ======= + a b + = ======= + 1 1.23E4 + 2 5.67E-8 + = ======= + """ + )[1:-1] + + t3 = ascii.read(tabrst.split("\n"), format="rst") + + assert t3["b"].dtype.kind == "f" + + t4 = ascii.read(tabrst.split("\n"), guess=True) + + assert t4["b"].dtype.kind == "f" + + # In the special case of fast_converter=True (the default), + # incompatibility is ignored + t5 = ascii.read(tabrst.split("\n"), format="rst", fast_reader=True) + + assert t5["b"].dtype.kind == "f" + + with pytest.raises(ParameterError): + ascii.read(tabrst.split("\n"), format="rst", guess=False, fast_reader="force") + + with pytest.raises(ParameterError): + ascii.read( + tabrst.split("\n"), + format="rst", + guess=False, + fast_reader={"use_fast_converter": False}, + ) + + tabrst = tabrst.replace("E", "D") + + with pytest.raises(ParameterError): + ascii.read( + tabrst.split("\n"), + format="rst", + guess=False, + fast_reader={"exponent_style": "D"}, + ) + + +@pytest.mark.parametrize("guess", [True, False]) +@pytest.mark.parametrize( + "fast_reader", [{"exponent_style": "D"}, {"exponent_style": "A"}] +) +def test_dict_kwarg_integrity(fast_reader, guess): + """ + Check if dictionaries passed as kwargs (fast_reader in this test) are + left intact by ascii.read() + """ + expstyle = fast_reader.get("exponent_style", "E") + fields = ["10.1D+199", "3.14d+313", "2048d+306", "0.6D-325", "-2.d345"] + + ascii.read(StringIO(" ".join(fields)), guess=guess, fast_reader=fast_reader) + assert fast_reader.get("exponent_style", None) == expstyle + + +@pytest.mark.parametrize("fast_reader", [False, {}]) +def test_read_empty_basic_table_with_comments(fast_reader): + """ + Test for reading a "basic" format table that has no data but has comments. + Tests the fix for #8267. + """ + dat = """ + # comment 1 + # comment 2 + col1 col2 + """ + t = ascii.read(dat, fast_reader=fast_reader) + assert t.meta["comments"] == ["comment 1", "comment 2"] + assert len(t) == 0 + assert t.colnames == ["col1", "col2"] + + +@pytest.mark.parametrize( + "fast_reader", [{"use_fast_converter": True}, {"exponent_style": "A"}] +) +def test_conversion_fast(fast_reader): + """ + The reader should try to convert each column to ints. If this fails, the + reader should try to convert to floats. Failing this, i.e. on parsing + non-numeric input including isolated positive/negative signs, it should + fall back to strings. + """ + text = """ + A B C D E F G H + 1 a 3 4 5 6 7 8 + 2. 1 9 -.1e1 10.0 8.7 6 -5.3e4 + 4 2 -12 .4 +.e1 - + six + """ + table = ascii.read(text, fast_reader=fast_reader) + assert table["A"].dtype.kind == "f" + assert table["B"].dtype.kind in ("S", "U") + assert table["C"].dtype.kind == "i" + assert table["D"].dtype.kind == "f" + assert table["E"].dtype.kind in ("S", "U") + assert table["F"].dtype.kind in ("S", "U") + assert table["G"].dtype.kind in ("S", "U") + assert table["H"].dtype.kind in ("S", "U") + + +@pytest.mark.parametrize("delimiter", ["\n", "\r"]) +@pytest.mark.parametrize("fast_reader", [False, True, "force"]) +def test_newline_as_delimiter(delimiter, fast_reader): + """ + Check that newline characters are correctly handled as delimiters. + Tests the fix for #9928. + """ + if delimiter == "\r": + eol = "\n" + else: + eol = "\r" + + inp0 = ["a | b | c ", " 1 | '2' | 3.00000 "] + inp1 = ( + f"a {delimiter:s} b {delimiter:s}c{eol:s} 1 {delimiter:s}'2'{delimiter:s} 3.0" + ) + inp2 = [f"a {delimiter} b{delimiter} c", f"1{delimiter} '2' {delimiter} 3.0"] + + t0 = ascii.read(inp0, delimiter="|", fast_reader=fast_reader) + t1 = ascii.read(inp1, delimiter=delimiter, fast_reader=fast_reader) + t2 = ascii.read(inp2, delimiter=delimiter, fast_reader=fast_reader) + + assert t1.colnames == t2.colnames == ["a", "b", "c"] + assert len(t1) == len(t2) == 1 + assert t1["b"].dtype.kind in ("S", "U") + assert t2["b"].dtype.kind in ("S", "U") + assert_table_equal(t1, t0) + assert_table_equal(t2, t0) + + inp0 = 'a {0:s} b {0:s} c{1:s} 1 {0:s}"2"{0:s} 3.0'.format("|", eol) + inp1 = ( + f'a {delimiter:s} b {delimiter:s} c{eol:s} 1 {delimiter:s}"2"{delimiter:s} 3.0' + ) + + t0 = ascii.read(inp0, delimiter="|", fast_reader=fast_reader) + t1 = ascii.read(inp1, delimiter=delimiter, fast_reader=fast_reader) + + if not fast_reader: + pytest.xfail("Quoted fields are not parsed correctly by BaseSplitter") + assert t1["b"].dtype.kind == "i" + + +@pytest.mark.parametrize("delimiter", [" ", "|", "\n", "\r"]) +@pytest.mark.parametrize("fast_reader", [False, True, "force"]) +def test_single_line_string(delimiter, fast_reader): + """ + String input without a newline character is interpreted as filename, + unless element of an iterable. Maybe not logical, but test that it is + at least treated consistently. + """ + expected = Table([[1], [2], [3.00]], names=("col1", "col2", "col3")) + text = f"1{delimiter:s}2{delimiter:s}3.0" + + if delimiter in ("\r", "\n"): + t1 = ascii.read( + text, format="no_header", delimiter=delimiter, fast_reader=fast_reader + ) + assert_table_equal(t1, expected) + else: + # Windows raises OSError, but not the other OSes. + with pytest.raises((FileNotFoundError, OSError)): + t1 = ascii.read( + text, format="no_header", delimiter=delimiter, fast_reader=fast_reader + ) + + t2 = ascii.read( + [text], format="no_header", delimiter=delimiter, fast_reader=fast_reader + ) + assert_table_equal(t2, expected) diff --git a/astropy/io/ascii/tests/test_cds.py b/astropy/io/ascii/tests/test_cds.py new file mode 100644 index 000000000000..d2b4746d6abd --- /dev/null +++ b/astropy/io/ascii/tests/test_cds.py @@ -0,0 +1,558 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +""" +This module tests some methods related to ``CDS`` format +reader/writer. +Requires `pyyaml `_ to be installed. +""" + +from io import StringIO + +import numpy as np +import pytest +from numpy.testing import assert_allclose + +from astropy import units as u +from astropy.coordinates import SkyCoord +from astropy.io import ascii +from astropy.table import Column, MaskedColumn, QTable, Table +from astropy.time import Time +from astropy.utils.data import get_pkg_data_filename +from astropy.utils.exceptions import AstropyWarning + +test_dat = [ + "names e d s i", + "HD81809 1E-7 22.25608 +2 67", + "HD103095 -31.6e5 +27.2500 -9E34 -30", +] + + +def test_roundtrip_mrt_table(): + """ + Tests whether or not the CDS writer can roundtrip a table, + i.e. read a table to ``Table`` object and write it exactly + as it is back to a file. Since, presently CDS uses a + MRT format template while writing, only the Byte-By-Byte + and the data section of the table can be compared between + original and the newly written table. + + Further, the CDS Reader does not have capability to recognize + column format from the header of a CDS/MRT table, so this test + can work for a limited set of simple tables, which don't have + whitespaces in the column values or mix-in columns. Because of + this the written table output cannot be directly matched with + the original file and have to be checked against a list of lines. + Masked columns are read properly though, and thus are being tested + during round-tripping. + + The difference between ``cdsFunctional2.dat`` file and ``exp_output`` + is the following: + * Metadata is different because MRT template is used for writing. + * Spacing between ``Label`` and ``Explanations`` column in the + Byte-By-Byte. + * Units are written as ``[cm.s-2]`` and not ``[cm/s2]``, since both + are valid according to CDS/MRT standard. + """ + exp_output = [ + "================================================================================", + "Byte-by-byte Description of file: table.dat", + "--------------------------------------------------------------------------------", + " Bytes Format Units Label Explanations", + "--------------------------------------------------------------------------------", + " 1- 7 A7 --- ID Star ID ", + " 9-12 I4 K Teff [4337/4654] Effective temperature ", + "14-17 F4.2 [cm.s-2] logg [0.77/1.28] Surface gravity ", + "19-22 F4.2 km.s-1 vturb [1.23/1.82] Micro-turbulence velocity", + "24-28 F5.2 [-] [Fe/H] [-2.11/-1.5] Metallicity ", + "30-33 F4.2 [-] e_[Fe/H] ? rms uncertainty on [Fe/H] ", + "--------------------------------------------------------------------------------", + "Notes:", + "--------------------------------------------------------------------------------", + "S05-5 4337 0.77 1.80 -2.07 ", + "S08-229 4625 1.23 1.23 -1.50 ", + "S05-10 4342 0.91 1.82 -2.11 0.14", + "S05-47 4654 1.28 1.74 -1.64 0.16", + ] + dat = get_pkg_data_filename( + "data/cdsFunctional2.dat", package="astropy.io.ascii.tests" + ) + t = Table.read(dat, format="ascii.mrt") + out = StringIO() + t.write(out, format="ascii.mrt") + lines = out.getvalue().splitlines() + i_bbb = lines.index("=" * 80) + lines = lines[i_bbb:] # Select Byte-By-Byte section and later lines. + assert lines == exp_output + + +def test_write_byte_by_byte_units(): + t = ascii.read(test_dat) + col_units = [None, u.C, u.kg, u.m / u.s, u.year] + t._set_column_attribute("unit", col_units) + # Add a column with magnitude units. + # Note that magnitude has to be assigned for each value explicitly. + t["magnitude"] = [u.Magnitude(25), u.Magnitude(-9)] + col_units.append(u.mag) + out = StringIO() + t.write(out, format="ascii.mrt") + # Read written table. + columns = ascii.read(out.getvalue(), format="cds").itercols() + assert [col.unit for col in columns] == col_units + + +def test_write_readme_with_default_options(): + exp_output = [ + "Title:", + "Authors:", + "Table:", + "================================================================================", + "Byte-by-byte Description of file: table.dat", + "--------------------------------------------------------------------------------", + " Bytes Format Units Label Explanations", + "--------------------------------------------------------------------------------", + " 1- 8 A8 --- names Description of names ", + "10-14 E5.1 --- e [-3160000.0/0.01] Description of e", + "16-23 F8.5 --- d [22.25/27.25] Description of d ", + "25-31 E7.1 --- s [-9e+34/2.0] Description of s ", + "33-35 I3 --- i [-30/67] Description of i ", + "--------------------------------------------------------------------------------", + "Notes:", + "--------------------------------------------------------------------------------", + "HD81809 1e-07 22.25608 2e+00 67", + "HD103095 -3e+06 27.25000 -9e+34 -30", + ] + t = ascii.read(test_dat) + out = StringIO() + t.write(out, format="ascii.mrt") + assert out.getvalue().splitlines() == exp_output + + +def test_write_empty_table(): + out = StringIO() + with pytest.raises(NotImplementedError): + Table().write(out, format="ascii.mrt") + + +def test_write_null_data_values(): + exp_output = [ + "HD81809 1e-07 22.25608 2.0e+00 67", + "HD103095 -3e+06 27.25000 -9.0e+34 -30", + "Sun 5.3e+27 ", + ] + t = ascii.read(test_dat) + t.add_row( + ["Sun", "3.25", "0", "5.3e27", "2"], mask=[False, True, True, False, True] + ) + out = StringIO() + t.write(out, format="ascii.mrt") + lines = out.getvalue().splitlines() + i_secs = [i for i, s in enumerate(lines) if s.startswith(("------", "======="))] + lines = lines[i_secs[-1] + 1 :] # Last section is the data. + assert lines == exp_output + + +def test_write_byte_by_byte_for_masked_column(): + """ + This test differs from the ``test_write_null_data_values`` + above in that it tests the column value limits in the Byte-By-Byte + description section for columns whose values are masked. + It also checks the description for columns with same values. + """ + exp_output = [ + "================================================================================", + "Byte-by-byte Description of file: table.dat", + "--------------------------------------------------------------------------------", + " Bytes Format Units Label Explanations", + "--------------------------------------------------------------------------------", + " 1- 8 A8 --- names Description of names ", + "10-14 E5.1 --- e [0.0/0.01]? Description of e ", + "16-17 F2.0 --- d ? Description of d ", + "19-25 E7.1 --- s [-9e+34/2.0] Description of s ", + "27-29 I3 --- i [-30/67] Description of i ", + "31-33 F3.1 --- sameF [5.0/5.0] Description of sameF", + "35-36 I2 --- sameI [20] Description of sameI ", + "--------------------------------------------------------------------------------", + "Notes:", + "--------------------------------------------------------------------------------", + "HD81809 1e-07 2e+00 67 5.0 20", + "HD103095 -9e+34 -30 5.0 20", + ] + t = ascii.read(test_dat) + t.add_column([5.0, 5.0], name="sameF") + t.add_column([20, 20], name="sameI") + t["e"] = MaskedColumn(t["e"], mask=[False, True]) + t["d"] = MaskedColumn(t["d"], mask=[True, True]) + out = StringIO() + t.write(out, format="ascii.mrt") + lines = out.getvalue().splitlines() + i_bbb = lines.index("=" * 80) + lines = lines[i_bbb:] # Select Byte-By-Byte section and later lines. + assert lines == exp_output + + +exp_coord_cols_output = { + "generic": [ + "================================================================================", + "Byte-by-byte Description of file: table.dat", + "--------------------------------------------------------------------------------", + " Bytes Format Units Label Explanations", + "--------------------------------------------------------------------------------", + " 1- 8 A8 --- names Description of names ", + "10-14 E5.1 --- e [-3160000.0/0.01] Description of e", + "16-23 F8.5 --- d [22.25/27.25] Description of d ", + "25-31 E7.1 --- s [-9e+34/2.0] Description of s ", + "33-35 I3 --- i [-30/67] Description of i ", + "37-39 F3.1 --- sameF [5.0/5.0] Description of sameF ", + "41-42 I2 --- sameI [20] Description of sameI ", + "44-45 I2 h RAh Right Ascension (hour) ", + "47-48 I2 min RAm Right Ascension (minute) ", + "50-62 F13.10 s RAs Right Ascension (second) ", + " 64 A1 --- DE- Sign of Declination ", + "65-66 I2 deg DEd Declination (degree) ", + "68-69 I2 arcmin DEm Declination (arcmin) ", + "71-82 F12.9 arcsec DEs Declination (arcsec) ", + "--------------------------------------------------------------------------------", + "Notes:", + "--------------------------------------------------------------------------------", + "HD81809 1e-07 22.25608 2e+00 67 5.0 20 22 02 15.4500000000 -61 39 34.599996000", + "HD103095 -3e+06 27.25000 -9e+34 -30 5.0 20 12 48 15.2244072000 +17 46 26.496624000", + ], + "positive_de": [ + "================================================================================", + "Byte-by-byte Description of file: table.dat", + "--------------------------------------------------------------------------------", + " Bytes Format Units Label Explanations", + "--------------------------------------------------------------------------------", + " 1- 8 A8 --- names Description of names ", + "10-14 E5.1 --- e [-3160000.0/0.01] Description of e", + "16-23 F8.5 --- d [22.25/27.25] Description of d ", + "25-31 E7.1 --- s [-9e+34/2.0] Description of s ", + "33-35 I3 --- i [-30/67] Description of i ", + "37-39 F3.1 --- sameF [5.0/5.0] Description of sameF ", + "41-42 I2 --- sameI [20] Description of sameI ", + "44-45 I2 h RAh Right Ascension (hour) ", + "47-48 I2 min RAm Right Ascension (minute) ", + "50-62 F13.10 s RAs Right Ascension (second) ", + " 64 A1 --- DE- Sign of Declination ", + "65-66 I2 deg DEd Declination (degree) ", + "68-69 I2 arcmin DEm Declination (arcmin) ", + "71-82 F12.9 arcsec DEs Declination (arcsec) ", + "--------------------------------------------------------------------------------", + "Notes:", + "--------------------------------------------------------------------------------", + "HD81809 1e-07 22.25608 2e+00 67 5.0 20 12 48 15.2244072000 +17 46 26.496624000", + "HD103095 -3e+06 27.25000 -9e+34 -30 5.0 20 12 48 15.2244072000 +17 46 26.496624000", + ], + "galactic": [ + "================================================================================", + "Byte-by-byte Description of file: table.dat", + "--------------------------------------------------------------------------------", + " Bytes Format Units Label Explanations", + "--------------------------------------------------------------------------------", + " 1- 8 A8 --- names Description of names ", + "10-14 E5.1 --- e [-3160000.0/0.01] Description of e", + "16-23 F8.5 --- d [22.25/27.25] Description of d ", + "25-31 E7.1 --- s [-9e+34/2.0] Description of s ", + "33-35 I3 --- i [-30/67] Description of i ", + "37-39 F3.1 --- sameF [5.0/5.0] Description of sameF ", + "41-42 I2 --- sameI [20] Description of sameI ", + "44-59 F16.12 deg GLON Galactic Longitude ", + "61-76 F16.12 deg GLAT Galactic Latitude ", + "--------------------------------------------------------------------------------", + "Notes:", + "--------------------------------------------------------------------------------", + "HD81809 1e-07 22.25608 2e+00 67 5.0 20 330.071639591690 -45.548080484609", + "HD103095 -3e+06 27.25000 -9e+34 -30 5.0 20 330.071639591690 -45.548080484609", + ], + "ecliptic": [ + "================================================================================", + "Byte-by-byte Description of file: table.dat", + "--------------------------------------------------------------------------------", + " Bytes Format Units Label Explanations", + "--------------------------------------------------------------------------------", + " 1- 8 A8 --- names Description of names ", + "10-14 E5.1 --- e [-3160000.0/0.01] Description of e ", + "16-23 F8.5 --- d [22.25/27.25] Description of d ", + "25-31 E7.1 --- s [-9e+34/2.0] Description of s ", + "33-35 I3 --- i [-30/67] Description of i ", + "37-39 F3.1 --- sameF [5.0/5.0] Description of sameF ", + "41-42 I2 --- sameI [20] Description of sameI ", + "44-59 F16.12 deg ELON Ecliptic Longitude (geocentrictrueecliptic)", + "61-76 F16.12 deg ELAT Ecliptic Latitude (geocentrictrueecliptic) ", + "--------------------------------------------------------------------------------", + "Notes:", + "--------------------------------------------------------------------------------", + "HD81809 1e-07 22.25608 2e+00 67 5.0 20 306.224208650096 -45.621789850825", + "HD103095 -3e+06 27.25000 -9e+34 -30 5.0 20 306.224208650096 -45.621789850825", + ], +} + + +def test_write_coord_cols(): + """ + There can only be one such coordinate column in a single table, + because division of columns into individual component columns requires + iterating over the table columns, which will have to be done again + if additional such coordinate columns are present. + """ + t = ascii.read(test_dat) + t.add_column([5.0, 5.0], name="sameF") + t.add_column([20, 20], name="sameI") + + # Coordinates of ASASSN-15lh + coord = SkyCoord(330.564375, -61.65961111, unit=u.deg) + # Coordinates of ASASSN-14li + coordp = SkyCoord(192.06343503, 17.77402684, unit=u.deg) + cols = [ + Column([coord, coordp]), # Generic coordinate column + coordp, # Coordinate column with positive DEC + coord.galactic, # Galactic coordinates + coord.geocentrictrueecliptic, # Ecliptic coordinates + ] + + # Loop through different types of coordinate columns. + for col, coord_type in zip(cols, exp_coord_cols_output): + exp_output = exp_coord_cols_output[coord_type] + t["coord"] = col + out = StringIO() + t.write(out, format="ascii.mrt") + lines = out.getvalue().splitlines() + i_bbb = lines.index("=" * 80) + lines = lines[i_bbb:] # Select Byte-By-Byte section and later lines. + # Check the written table. + assert lines == exp_output + + # Check if the original table columns remains unmodified. + assert t.colnames == ["names", "e", "d", "s", "i", "sameF", "sameI", "coord"] + + +def test_write_byte_by_byte_bytes_col_format(): + """ + Tests the alignment of Byte counts with respect to hyphen + in the Bytes column of Byte-By-Byte. The whitespace around the + hyphen is govered by the number of digits in the total Byte + count. Single Byte columns should have a single Byte count + without the hyphen. + """ + exp_output = [ + "================================================================================", + "Byte-by-byte Description of file: table.dat", + "--------------------------------------------------------------------------------", + " Bytes Format Units Label Explanations", + "--------------------------------------------------------------------------------", + " 1- 8 A8 --- names Description of names ", + "10-21 E12.6 --- e [-3160000.0/0.01] Description of e", + "23-30 F8.5 --- d [22.25/27.25] Description of d ", + "32-38 E7.1 --- s [-9e+34/2.0] Description of s ", + "40-42 I3 --- i [-30/67] Description of i ", + "44-46 F3.1 --- sameF [5.0/5.0] Description of sameF ", + "48-49 I2 --- sameI [20] Description of sameI ", + " 51 I1 --- singleByteCol [2] Description of singleByteCol ", + "53-54 I2 h RAh Right Ascension (hour) ", + "56-57 I2 min RAm Right Ascension (minute) ", + "59-71 F13.10 s RAs Right Ascension (second) ", + " 73 A1 --- DE- Sign of Declination ", + "74-75 I2 deg DEd Declination (degree) ", + "77-78 I2 arcmin DEm Declination (arcmin) ", + "80-91 F12.9 arcsec DEs Declination (arcsec) ", + "--------------------------------------------------------------------------------", + ] + t = ascii.read(test_dat) + t.add_column([5.0, 5.0], name="sameF") + t.add_column([20, 20], name="sameI") + t["coord"] = SkyCoord(330.564375, -61.65961111, unit=u.deg) + t["singleByteCol"] = [2, 2] + t["e"].format = ".5E" + out = StringIO() + t.write(out, format="ascii.mrt") + lines = out.getvalue().splitlines() + i_secs = [i for i, s in enumerate(lines) if s.startswith(("------", "======="))] + # Select only the Byte-By-Byte section. + lines = lines[i_secs[0] : i_secs[-2]] + lines.append("-" * 80) # Append a separator line. + assert lines == exp_output + + +def test_write_byte_by_byte_wrapping(): + """ + Test line wrapping in the description column of the + Byte-By-Byte section of the ReadMe. + """ + exp_output = """\ +================================================================================ +Byte-by-byte Description of file: table.dat +-------------------------------------------------------------------------------- + Bytes Format Units Label Explanations +-------------------------------------------------------------------------------- + 1- 8 A8 --- thisIsALongColumnLabel This is a tediously long + description. But they do sometimes + have them. Better to put extra + details in the notes. This is a + tediously long description. But they + do sometimes have them. Better to put + extra details in the notes. +10-14 E5.1 --- e [-3160000.0/0.01] Description of e +16-23 F8.5 --- d [22.25/27.25] Description of d +-------------------------------------------------------------------------------- +""" + t = ascii.read(test_dat) + t.remove_columns(["s", "i"]) + description = ( + "This is a tediously long description." + " But they do sometimes have them." + " Better to put extra details in the notes. " + ) + t["names"].description = description * 2 + t["names"].name = "thisIsALongColumnLabel" + out = StringIO() + t.write(out, format="ascii.mrt") + lines = out.getvalue().splitlines() + i_secs = [i for i, s in enumerate(lines) if s.startswith(("------", "======="))] + # Select only the Byte-By-Byte section. + lines = lines[i_secs[0] : i_secs[-2]] + lines.append("-" * 80) # Append a separator line. + assert lines == exp_output.splitlines() + + +def test_write_mixin_and_broken_cols(): + """ + Tests conversion to string values for ``mix-in`` columns other than + ``SkyCoord`` and for columns with only partial ``SkyCoord`` values. + """ + exp_output = [ + "================================================================================", + "Byte-by-byte Description of file: table.dat", + "--------------------------------------------------------------------------------", + " Bytes Format Units Label Explanations", + "--------------------------------------------------------------------------------", + " 1- 7 A7 --- name Description of name ", + " 9- 74 A66 --- Unknown Description of Unknown ", + " 76-114 A39 --- cart Description of cart ", + "116-138 A23 --- time Description of time ", + "140-142 F3.1 m q [1.0/1.0] Description of q", + "--------------------------------------------------------------------------------", + "Notes:", + "--------------------------------------------------------------------------------", + "HD81809 (0.41342785, -0.23329341, -0.88014294) 2019-01-01 00:00:00.000 1.0", + "random 12 (0.41342785, -0.23329341, -0.88014294) 2019-01-01 00:00:00.000 1.0", + ] + t = Table() + t["name"] = ["HD81809"] + coord = SkyCoord(330.564375, -61.65961111, unit=u.deg) + t["coord"] = Column(coord) + t.add_row(["random", 12]) + t["cart"] = coord.cartesian + t["time"] = Time("2019-1-1") + t["q"] = u.Quantity(1.0, u.m) + out = StringIO() + t.write(out, format="ascii.mrt") + lines = out.getvalue().splitlines() + i_bbb = lines.index("=" * 80) + lines = lines[i_bbb:] # Select Byte-By-Byte section and later lines. + # Check the written table. + assert lines == exp_output + + +def test_write_extra_skycoord_cols(): + """ + Tests output for cases when table contains multiple ``SkyCoord`` columns. + """ + exp_output = [ + "================================================================================", + "Byte-by-byte Description of file: table.dat", + "--------------------------------------------------------------------------------", + " Bytes Format Units Label Explanations", + "--------------------------------------------------------------------------------", + " 1- 7 A7 --- name Description of name ", + " 9-10 I2 h RAh Right Ascension (hour) ", + "12-13 I2 min RAm Right Ascension (minute)", + "15-27 F13.10 s RAs Right Ascension (second)", + " 29 A1 --- DE- Sign of Declination ", + "30-31 I2 deg DEd Declination (degree) ", + "33-34 I2 arcmin DEm Declination (arcmin) ", + "36-47 F12.9 arcsec DEs Declination (arcsec) ", + "49-62 A14 --- coord2 Description of coord2 ", + "--------------------------------------------------------------------------------", + "Notes:", + "--------------------------------------------------------------------------------", + "HD4760 0 49 39.9000000000 +06 24 07.999200000 12.4163 6.407 ", + "HD81809 22 02 15.4500000000 -61 39 34.599996000 330.564 -61.66", + ] + t = Table() + t["name"] = ["HD4760", "HD81809"] + t["coord1"] = SkyCoord([12.41625, 330.564375], [6.402222, -61.65961111], unit=u.deg) + t["coord2"] = SkyCoord([12.41630, 330.564400], [6.407, -61.66], unit=u.deg) + out = StringIO() + with pytest.warns( + UserWarning, + match=r"column 2 is being skipped with designation of a " + r"string valued column `coord2`", + ): + t.write(out, format="ascii.mrt") + + lines = out.getvalue().splitlines() + i_bbb = lines.index("=" * 80) + lines = lines[i_bbb:] # Select Byte-By-Byte section and following lines. + # Check the written table. + assert lines[:-2] == exp_output[:-2] + + for a, b in zip(lines[-2:], exp_output[-2:]): + assert a[:18] == b[:18] + assert a[30:42] == b[30:42] + assert_allclose(np.fromstring(a[2:], sep=" "), np.fromstring(b[2:], sep=" ")) + + +def test_write_skycoord_with_format(): + """ + Tests output with custom setting for ``SkyCoord`` (second) columns. + """ + exp_output = [ + "================================================================================", + "Byte-by-byte Description of file: table.dat", + "--------------------------------------------------------------------------------", + " Bytes Format Units Label Explanations", + "--------------------------------------------------------------------------------", + " 1- 7 A7 --- name Description of name ", + " 9-10 I2 h RAh Right Ascension (hour) ", + "12-13 I2 min RAm Right Ascension (minute)", + "15-19 F5.2 s RAs Right Ascension (second)", + " 21 A1 --- DE- Sign of Declination ", + "22-23 I2 deg DEd Declination (degree) ", + "25-26 I2 arcmin DEm Declination (arcmin) ", + "28-31 F4.1 arcsec DEs Declination (arcsec) ", + "--------------------------------------------------------------------------------", + "Notes:", + "--------------------------------------------------------------------------------", + "HD4760 0 49 39.90 +06 24 08.0", + "HD81809 22 02 15.45 -61 39 34.6", + ] + t = Table() + t["name"] = ["HD4760", "HD81809"] + t["coord"] = SkyCoord([12.41625, 330.564375], [6.402222, -61.65961111], unit=u.deg) + + out = StringIO() + # This will raise a warning because `formats` is checked before the writer creating the + # final list of columns is called. + with pytest.warns( + AstropyWarning, + match=r"The key.s. {'[RD][AE]s', '[RD][AE]s'} specified in " + r"the formats argument do not match a column name.", + ): + t.write(out, format="ascii.mrt", formats={"RAs": "05.2f", "DEs": "04.1f"}) + + lines = out.getvalue().splitlines() + i_bbb = lines.index("=" * 80) + lines = lines[i_bbb:] # Select Byte-By-Byte section and following lines. + # Check the written table. + assert lines == exp_output + + +def test_write_qtable(): + # Regression test for gh-12804 + qt = QTable([np.arange(4) * u.m, ["a", "b", "c", "ddd"]], names=["a", "b"]) + out = StringIO() + qt.write(out, format="mrt") + result = out.getvalue() + assert "Description of a" in result + assert "Description of b" in result diff --git a/astropy/io/ascii/tests/test_cds_header_from_readme.py b/astropy/io/ascii/tests/test_cds_header_from_readme.py index 89c397d0c24f..38fe5603b44f 100644 --- a/astropy/io/ascii/tests/test_cds_header_from_readme.py +++ b/astropy/io/ascii/tests/test_cds_header_from_readme.py @@ -1,132 +1,281 @@ -from ... import ascii as asciitable +# Licensed under a 3-clause BSD style license - see LICENSE.rst -from .common import (raises, - assert_equal, assert_almost_equal, assert_true, - setup_function, teardown_function, has_isnan) +import numpy as np +import pytest +from numpy.testing import assert_allclose + +from astropy import units as u +from astropy.io import ascii +from astropy.table import Table + +from .common import ( + setup_function, # noqa: F401 + teardown_function, # noqa: F401 +) def read_table1(readme, data): - reader = asciitable.Cds(readme) + reader = ascii.Cds(readme) return reader.read(data) def read_table2(readme, data): - reader = asciitable.get_reader(Reader=asciitable.Cds, readme=readme) - reader.outputter = asciitable.TableOutputter() + reader = ascii.get_reader(reader_cls=ascii.Cds, readme=readme) + reader.outputter = ascii.TableOutputter() return reader.read(data) def read_table3(readme, data): - return asciitable.read(data, readme=readme) + return ascii.read(data, readme=readme) + + +def test_description(): + readme = "data/cds/description/ReadMe" + data = "data/cds/description/table.dat" + for read_table in (read_table1, read_table2, read_table3): + table = read_table(readme, data) + assert len(table) == 2 + assert table["Cluster"].description == "Cluster name" + assert table["Star"].description == "" + assert table["Wave"].description == "wave ? Wavelength in Angstroms" + assert table["El"].description == "a" + assert table["ion"].description == "- Ionization stage (1 for neutral element)" + assert table["loggf"].description == ( + "log10 of the gf value - logarithm base 10 of stat. weight times " + "oscillator strength" + ) + assert table["EW"].description == "Equivalent width (in mA)" + assert ( + table["Q"].description + == "DAOSPEC quality parameter Q (large values are bad)" + ) def test_multi_header(): - readme = 't/cds/multi/ReadMe' - data = 't/cds/multi/lhs2065.dat' + readme = "data/cds/multi/ReadMe" + data = "data/cds/multi/lhs2065.dat" + for read_table in (read_table1, read_table2, read_table3): + table = read_table(readme, data) + assert len(table) == 18 + assert_allclose(table["Lambda"][-1], 6479.32) + assert table["Fnu"][-1] == "0.285937" + data = "data/cds/multi/lp944-20.dat" for read_table in (read_table1, read_table2, read_table3): table = read_table(readme, data) - assert_equal(len(table), 18) - assert_almost_equal(table['Lambda'][-1], 6479.32) - assert_equal(table['Fnu'][-1], '0.285937') + assert len(table) == 18 + assert_allclose(table["Lambda"][0], 6476.09) + assert table["Fnu"][-1] == "0.489005" def test_glob_header(): - readme = 't/cds/glob/ReadMe' - data = 't/cds/glob/lmxbrefs.dat' + readme = "data/cds/glob/ReadMe" + data = "data/cds/glob/lmxbrefs.dat" for read_table in (read_table1, read_table2, read_table3): table = read_table(readme, data) - assert_equal(len(table), 291) - assert_equal(table['Name'][-1], 'J1914+0953') - assert_equal(table['BibCode'][-2], '2005A&A...432..235R') + assert len(table) == 291 + assert table["Name"][-1] == "J1914+0953" + assert table["BibCode"][-2] == "2005A&A...432..235R" def test_header_from_readme(): - r = asciitable.Cds("t/vizier/ReadMe") - table = r.read("t/vizier/table1.dat") + """Test reading VizieR data with accompanying ReadMe file. + + We test several things here to make the best use of the included files: + - "table1.dat" is a standard table. + - "table5.dat.gz" is a gzipped table. When downloaded from VizieR, large tables come + gzipped by default, but the name in the ReadMe does not include the ".gz" suffix. + So, we check here that the zipped table is found. Regression test for #6549. + """ + r = ascii.Cds("data/vizier/ReadMe") + table = r.read("data/vizier/table1.dat") assert len(r.data.data_lines) == 15 assert len(table) == 15 assert len(table.keys()) == 18 - Bmag = [14.79, - 15.00, - 14.80, - 12.38, - 12.36, - 12.24, - 13.75, - 13.65, - 13.41, - 11.59, - 11.68, - 11.53, - 13.92, - 14.03, - 14.18] - for i, val in enumerate(table.field('Bmag')): + Bmag = [ + 14.79, + 15.00, + 14.80, + 12.38, + 12.36, + 12.24, + 13.75, + 13.65, + 13.41, + 11.59, + 11.68, + 11.53, + 13.92, + 14.03, + 14.18, + ] + for i, val in enumerate(table.field("Bmag")): assert val == Bmag[i] - table = r.read("t/vizier/table5.dat") + table = r.read("data/vizier/table5.dat.gz") assert len(r.data.data_lines) == 49 assert len(table) == 49 assert len(table.keys()) == 10 - Q = [ 0.289, - 0.325, - 0.510, - 0.577, - 0.539, - 0.390, - 0.957, - 0.736, - 1.435, - 1.117, - 1.473, - 0.808, - 1.416, - 2.209, - 0.617, - 1.046, - 1.604, - 1.419, - 1.431, - 1.183, - 1.210, - 1.005, - 0.706, - 0.665, - 0.340, - 0.323, - 0.391, - 0.280, - 0.343, - 0.369, - 0.495, - 0.828, - 1.113, - 0.499, - 1.038, - 0.260, - 0.863, - 1.638, - 0.479, - 0.232, - 0.627, - 0.671, - 0.371, - 0.851, - 0.607, - -9.999, - 1.958, - 1.416, - 0.949] - if has_isnan: - from .common import isnan - for i, val in enumerate(table.field('Q')): - if isnan(val): - assert Q[i] == -9.999 # text value for a missing value in that - # table - else: - assert val == Q[i] - -if __name__ == "__main__": # run from main directory; not from test/ - test_header_from_readme() - test_multi_header() - test_glob_header() + Q = [ + 0.289, + 0.325, + 0.510, + 0.577, + 0.539, + 0.390, + 0.957, + 0.736, + 1.435, + 1.117, + 1.473, + 0.808, + 1.416, + 2.209, + 0.617, + 1.046, + 1.604, + 1.419, + 1.431, + 1.183, + 1.210, + 1.005, + 0.706, + 0.665, + 0.340, + 0.323, + 0.391, + 0.280, + 0.343, + 0.369, + 0.495, + 0.828, + 1.113, + 0.499, + 1.038, + 0.260, + 0.863, + 1.638, + 0.479, + 0.232, + 0.627, + 0.671, + 0.371, + 0.851, + 0.607, + -9.999, + 1.958, + 1.416, + 0.949, + ] + for i, val in enumerate(table.field("Q")): + if val is np.ma.masked: + # text value for a missing value in that table + assert Q[i] == -9.999 + else: + assert val == Q[i] + + +@pytest.mark.parametrize("reader_cls", (ascii.Cds, ascii.Mrt)) +def test_cds_units(reader_cls): + from astropy import units + + data_and_readme = "data/cds.dat" + reader = ascii.get_reader(reader_cls) + table = reader.read(data_and_readme) + # column unit is GMsun (giga solar masses) + # make sure this is parsed correctly, not as a "string" unit + assert table["Fit"].to(units.solMass).unit == units.solMass + + +@pytest.mark.parametrize("reader_cls", (ascii.Cds, ascii.Mrt)) +def test_cds_function_units(reader_cls): + data_and_readme = "data/cdsFunctional.dat" + reader = ascii.get_reader(reader_cls) + table = reader.read(data_and_readme) + assert table["logg"].unit == u.dex(u.cm / u.s**2) + assert table["logTe"].unit == u.dex(u.K) + assert table["Mass"].unit == u.Msun + assert table["e_Mass"].unit == u.Msun + assert table["Age"].unit == u.Myr + assert table["e_Age"].unit == u.Myr + + +@pytest.mark.parametrize("reader_cls", (ascii.Cds, ascii.Mrt)) +def test_cds_function_units2(reader_cls): + # This one includes some dimensionless dex. + data_and_readme = "data/cdsFunctional2.dat" + reader = ascii.get_reader(reader_cls) + table = reader.read(data_and_readme) + assert table["Teff"].unit == u.K + assert table["logg"].unit == u.dex(u.cm / u.s**2) + assert table["vturb"].unit == u.km / u.s + assert table["[Fe/H]"].unit == u.dex(u.one) + assert table["e_[Fe/H]"].unit == u.dex(u.one) + assert_allclose( + table["[Fe/H]"].to(u.one), 10.0 ** (np.array([-2.07, -1.50, -2.11, -1.64])) + ) + + +def test_cds_ignore_nullable(): + # Make sure CDS reader_cls does not ignore nullabilty for columns + # with a limit specifier + readme = "data/cds/null/ReadMe" + data = "data/cds/null/table.dat" + r = ascii.Cds(readme) + r.read(data) + assert r.header.cols[6].description == "Temperature class codified (10)" + assert r.header.cols[8].description == "Luminosity class codified (11)" + assert r.header.cols[5].description == "Pericenter position angle (18)" + + +def test_cds_no_whitespace(): + # Make sure CDS reader_cls only checks null values when an '=' symbol is present, + # and read description text even if there is no whitespace after '?'. + readme = "data/cds/null/ReadMe1" + data = "data/cds/null/table1.dat" + r = ascii.Cds(readme) + r.read(data) + assert r.header.cols[6].description == "Temperature class codified (10)" + assert r.header.cols[6].null == "" + assert r.header.cols[7].description == "Equivalent width (in mA)" + assert r.header.cols[7].null == "-9.9" + assert r.header.cols[10].description == ( + "DAOSPEC quality parameter Q (large values are bad)" + ) + assert r.header.cols[10].null == "-9.999" + + +def test_cds_order(): + # Make sure CDS reader_cls does not ignore order specifier that maybe present after + # the null specifier '?' + readme = "data/cds/null/ReadMe1" + data = "data/cds/null/table1.dat" + r = ascii.Cds(readme) + r.read(data) + assert r.header.cols[5].description == "Catalogue Identification Number" + assert r.header.cols[8].description == "Another equivalent width (in mA)" + assert r.header.cols[9].description == "Luminosity class codified (11)" + + +@pytest.mark.parametrize("input_dir", ["datasplitcomplete", "datasplitcompletegz"]) +def test_cds_readme_datafiles_split(input_dir): + """CDS has the option to split datafiles for keep them small. + + This tests checks that the reader finds and merges the datafiles. + """ + dat = f"data/cds/{input_dir}/table1.dat" + readme = f"data/cds/{input_dir}/ReadMe" + t = Table.read(dat, format="ascii.cds", readme=readme) + assert len(t) == 4 + assert t["BJD"] == pytest.approx([4730.5481, 4734.5653, 4739.5092, 4754.5002]) + assert t["BJD"].description == "Barycentric Julian date (BJD-2450000)" + + +def test_cds_readme_datafiles_incomplete(): + """While we cannot detect if the last file is missing, we do raise an + error if the numbering of the files to be read is not continuous. + """ + dat = "data/cds/datasplitincomplete/table1.dat" + readme = "data/cds/datasplitincomplete/ReadMe" + with pytest.raises(ascii.InconsistentTableError) as exc: + Table.read(dat, format="ascii.cds", readme=readme) + assert "numbering is not consecutive for filenames" in str(exc.value) diff --git a/astropy/io/ascii/tests/test_compressed.py b/astropy/io/ascii/tests/test_compressed.py new file mode 100644 index 000000000000..c70b61be9e2b --- /dev/null +++ b/astropy/io/ascii/tests/test_compressed.py @@ -0,0 +1,47 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +import numpy as np +import pytest +from numpy.testing import assert_array_equal + +from astropy.io.ascii import read + +# NOTE: Python can be built without bz2 or lzma +from astropy.utils.compat.optional_deps import HAS_BZ2, HAS_LZMA, HAS_UNCOMPRESSPY +from astropy.utils.data import get_pkg_data_filename + + +@pytest.mark.parametrize( + "filename", ["data/daophot.dat.gz", "data/latex1.tex.gz", "data/short.rdb.gz"] +) +def test_gzip(filename): + t_comp = read(get_pkg_data_filename(filename)) + t_uncomp = read(get_pkg_data_filename(filename.replace(".gz", ""))) + assert t_comp.dtype.names == t_uncomp.dtype.names + assert np.all(t_comp.as_array() == t_uncomp.as_array()) + + +@pytest.mark.xfail(not HAS_BZ2, reason="requires bz2") +@pytest.mark.parametrize("filename", ["data/short.rdb.bz2", "data/ipac.dat.bz2"]) +def test_bzip2(filename): + t_comp = read(get_pkg_data_filename(filename)) + t_uncomp = read(get_pkg_data_filename(filename.replace(".bz2", ""))) + assert t_comp.dtype.names == t_uncomp.dtype.names + assert np.all(t_comp.as_array() == t_uncomp.as_array()) + + +@pytest.mark.xfail(not HAS_LZMA, reason="requires lzma") +@pytest.mark.parametrize("filename", ["data/short.rdb.xz", "data/ipac.dat.xz"]) +def test_xz(filename): + t_comp = read(get_pkg_data_filename(filename)) + t_uncomp = read(get_pkg_data_filename(filename.replace(".xz", ""))) + assert t_comp.dtype.names == t_uncomp.dtype.names + assert np.all(t_comp.as_array() == t_uncomp.as_array()) + + +@pytest.mark.xfail(not HAS_UNCOMPRESSPY, reason="requires uncompresspy") +@pytest.mark.parametrize("filename", ["data/short.rdb.Z", "data/ipac.dat.Z"]) +def test_lzw(filename): + t_comp = read(get_pkg_data_filename(filename)) + t_uncomp = read(get_pkg_data_filename(filename.removesuffix(".Z"))) + assert t_comp.dtype.names == t_uncomp.dtype.names + assert_array_equal(t_comp.as_array(), t_uncomp.as_array()) diff --git a/astropy/io/ascii/tests/test_connect.py b/astropy/io/ascii/tests/test_connect.py new file mode 100644 index 000000000000..498be414130c --- /dev/null +++ b/astropy/io/ascii/tests/test_connect.py @@ -0,0 +1,146 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +import numpy as np +import pytest + +from astropy.table import Column, Table +from astropy.table.table_helpers import simple_table +from astropy.utils.compat.optional_deps import HAS_BS4 +from astropy.utils.data import get_pkg_data_filename + +files = [ + "data/cds.dat", + "data/ipac.dat", + "data/daophot.dat", + "data/latex1.tex", + "data/simple_csv.csv", +] + + +if HAS_BS4: + files.append("data/html.html") + + +@pytest.mark.parametrize("filename", files) +def test_read_generic(filename): + Table.read(get_pkg_data_filename(filename), format="ascii") + + +def test_write_generic(tmp_path): + t = Table() + t.add_column(Column(name="a", data=[1, 2, 3])) + t.add_column(Column(name="b", data=["a", "b", "c"])) + t.write(tmp_path / "test", format="ascii") + + +def test_read_ipac(): + Table.read(get_pkg_data_filename("data/ipac.dat"), format="ipac") + + +def test_read_cds(): + Table.read(get_pkg_data_filename("data/cds.dat"), format="cds") + + +def test_read_dapphot(): + Table.read(get_pkg_data_filename("data/daophot.dat"), format="daophot") + + +def test_read_latex(): + Table.read(get_pkg_data_filename("data/latex1.tex"), format="latex") + + +def test_read_latex_noformat(): + Table.read(get_pkg_data_filename("data/latex1.tex")) + + +def test_write_latex(tmp_path): + t = Table() + t.add_column(Column(name="a", data=[1, 2, 3])) + t.add_column(Column(name="b", data=["a", "b", "c"])) + path = tmp_path / "data.tex" + t.write(path, format="latex") + + +def test_write_latex_noformat(tmp_path): + t = Table() + t.add_column(Column(name="a", data=[1, 2, 3])) + t.add_column(Column(name="b", data=["a", "b", "c"])) + path = tmp_path / "data.tex" + t.write(path) + + +@pytest.mark.skipif(not HAS_BS4, reason="requires BeautifulSoup4") +def test_read_html(): + Table.read(get_pkg_data_filename("data/html.html"), format="html") + + +@pytest.mark.skipif(not HAS_BS4, reason="requires BeautifulSoup4") +def test_read_html_noformat(): + Table.read(get_pkg_data_filename("data/html.html")) + + +def test_write_html(tmp_path): + t = Table() + t.add_column(Column(name="a", data=[1, 2, 3])) + t.add_column(Column(name="b", data=["a", "b", "c"])) + path = tmp_path / "data.html" + t.write(path, format="html") + + +def test_write_html_noformat(tmp_path): + t = Table() + t.add_column(Column(name="a", data=[1, 2, 3])) + t.add_column(Column(name="b", data=["a", "b", "c"])) + path = tmp_path / "data.html" + t.write(path) + + +def test_read_rdb(): + Table.read(get_pkg_data_filename("data/short.rdb"), format="rdb") + + +def test_read_rdb_noformat(): + Table.read(get_pkg_data_filename("data/short.rdb")) + + +def test_write_rdb(tmp_path): + t = Table() + t.add_column(Column(name="a", data=[1, 2, 3])) + t.add_column(Column(name="b", data=["a", "b", "c"])) + path = tmp_path / "data.rdb" + t.write(path, format="rdb") + + +def test_write_rdb_noformat(tmp_path): + t = Table() + t.add_column(Column(name="a", data=[1, 2, 3])) + t.add_column(Column(name="b", data=["a", "b", "c"])) + path = tmp_path / "data.rdb" + t.write(path) + + +def test_read_csv(): + """If properly registered, filename should be sufficient to specify format + + #3189 + """ + Table.read(get_pkg_data_filename("data/simple_csv.csv")) + + +def test_write_csv(tmp_path): + """If properly registered, filename should be sufficient to specify format + + #3189 + """ + t = Table() + t.add_column(Column(name="a", data=[1, 2, 3])) + t.add_column(Column(name="b", data=["a", "b", "c"])) + path = tmp_path / "data.csv" + t.write(path) + + +def test_auto_identify_ecsv(tmp_path): + tbl = simple_table() + tmpfile = tmp_path / "tmpFile.ecsv" + tbl.write(tmpfile) + tbl2 = Table.read(tmpfile) + assert np.all(tbl == tbl2) diff --git a/astropy/io/ascii/tests/test_ecsv.py b/astropy/io/ascii/tests/test_ecsv.py new file mode 100644 index 000000000000..adcf219e17ac --- /dev/null +++ b/astropy/io/ascii/tests/test_ecsv.py @@ -0,0 +1,1381 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +""" +This module tests some of the methods related to the ``ECSV`` +reader/writer. +""" + +import copy +import io +import os +import sys +from contextlib import nullcontext +from io import StringIO +from pprint import pformat + +import numpy as np +import pytest +import yaml +from numpy.testing import assert_array_equal + +from astropy import units as u +from astropy.io import ascii +from astropy.io.ascii.ecsv import DELIMITERS, InvalidEcsvDatatypeWarning +from astropy.io.ascii.tests.common import TEST_DIR +from astropy.io.misc.ecsv import ECSVEngine +from astropy.io.tests.mixin_columns import compare_attrs, mixin_cols, serialized_names +from astropy.table import Column, QTable, Table +from astropy.table.column import MaskedColumn +from astropy.table.table_helpers import simple_table +from astropy.time import Time +from astropy.units import QuantityInfo +from astropy.units import allclose as quantity_allclose +from astropy.utils.compat.optional_deps import ( + HAS_BZ2, + HAS_LZMA, + HAS_PANDAS, + HAS_PYARROW, +) +from astropy.utils.masked import Masked + +ENGINE_PARAMS = [ + {"format": "ascii.ecsv"}, + {"format": "ecsv", "engine": "io.ascii"}, +] +if HAS_PANDAS: + ENGINE_PARAMS.append({"format": "ecsv", "engine": "pandas"}) +if HAS_PYARROW: + ENGINE_PARAMS.append({"format": "ecsv", "engine": "pyarrow"}) + + +@pytest.fixture(scope="module", params=ENGINE_PARAMS) +def format_engine(request): + return request.param + + +def patch_format_write(format_engine): + """Return a compatible format_engine dict for available writers""" + out = format_engine.copy() + if out["format"] == "ecsv": + # "io.ascii" is the only supported engine for writing via "ecsv" format. + out["engine"] = "io.ascii" + return out + + +DTYPES = [ + "bool", + "int8", + "int16", + "int32", + "int64", + "uint8", + "uint16", + "uint32", + "uint64", + "float16", + "float32", + "float64", + "float128", + "str", +] +if not hasattr(np, "float128") or os.name == "nt" or sys.maxsize <= 2**32: + DTYPES.remove("float128") + +T_DTYPES = Table() + +for dtype in DTYPES: + if dtype == "bool": + data = np.array([False, True, False]) + elif dtype == "str": + data = np.array(["ab 0", "ab, 1", "ab2"]) + else: + data = np.arange(3, dtype=dtype) + c = Column(data, unit="m / s", description="descr_" + dtype) + # Add meta in way that uses the default_factory type and not in alphabetical order + c.meta["meta " + dtype] = 1 + c.meta["a"] = 2 + T_DTYPES[dtype] = c + +# Add meta in way that uses the default_factory type and not in alphabetical order +T_DTYPES.meta["comments"] = ["comment1", "comment2"] +T_DTYPES.meta["a"] = 3 + +# Corresponds to simple_table() +SIMPLE_LINES = [ + "# %ECSV 1.0", + "# ---", + "# datatype:", + "# - {name: a, datatype: int64}", + "# - {name: b, datatype: float64}", + "# - {name: c, datatype: string}", + "# schema: astropy-2.0", + "a b c", + "1 1.0 c", + "2 2.0 d", + "3 3.0 e", +] + + +def test_write_simple(): + """ + Write a simple table with common types. This shows the compact version + of serialization with one line per column. + """ + t = simple_table() + + out = StringIO() + t.write(out, format="ascii.ecsv") + assert out.getvalue().splitlines() == SIMPLE_LINES + + +def test_write_full(): + """ + Write a full-featured table with common types and explicitly checkout output + """ + t = T_DTYPES["bool", "int64", "float64", "str"] + lines = [ + "# %ECSV 1.0", + "# ---", + "# datatype:", + "# - name: bool", + "# unit: m / s", + "# datatype: bool", + "# description: descr_bool", + "# meta: !!omap", + "# - {meta bool: 1}", + "# - {a: 2}", + "# - name: int64", + "# unit: m / s", + "# datatype: int64", + "# description: descr_int64", + "# meta: !!omap", + "# - {meta int64: 1}", + "# - {a: 2}", + "# - name: float64", + "# unit: m / s", + "# datatype: float64", + "# description: descr_float64", + "# meta: !!omap", + "# - {meta float64: 1}", + "# - {a: 2}", + "# - name: str", + "# unit: m / s", + "# datatype: string", + "# description: descr_str", + "# meta: !!omap", + "# - {meta str: 1}", + "# - {a: 2}", + "# meta: !!omap", + "# - comments: [comment1, comment2]", + "# - {a: 3}", + "# schema: astropy-2.0", + "bool int64 float64 str", + 'False 0 0.0 "ab 0"', + 'True 1 1.0 "ab, 1"', + "False 2 2.0 ab2", + ] + + out = StringIO() + t.write(out, format="ascii.ecsv") + assert out.getvalue().splitlines() == lines + + +def test_read_float128_pyarrow_fail(): + text = """ +# %ECSV 1.0 +# --- +# datatype: +# - {name: col0, datatype: float128} +# schema: astropy-2.0 +col0 +1.5 +""" + with pytest.raises( + TypeError, + match="pyarrow engine does not support float128, choose a different engine", + ): + Table.read(text, format="ecsv", engine="pyarrow") + + +def test_write_read_roundtrip(format_engine): + """ + Write a full-featured table with all types and see that it round-trips on + readback. Use both space and comma delimiters. + """ + t = T_DTYPES.copy() + + # pyarrow does not support float128 and there is no workaround + if format_engine.get("engine") == "pyarrow" and "float128" in t.colnames: + del t["float128"] + + for delimiter in DELIMITERS: + out = StringIO() + t.write(out, delimiter=delimiter, **patch_format_write(format_engine)) + + t2s = [ + Table.read(out.getvalue(), **format_engine), + ] + if format_engine["format"] == "ascii.ecsv": + t2s.extend( + [ + Table.read(out.getvalue(), format="ascii"), + ascii.read(out.getvalue()), + ascii.read(out.getvalue(), format="ecsv", guess=False), + ascii.read(out.getvalue(), format="ecsv"), + ] + ) + for t2 in t2s: + assert t.meta == t2.meta + for name in t.colnames: + assert t[name].attrs_equal(t2[name]) + assert np.all(t[name] == t2[name]) + + +def test_write_read_roundtrip_empty_table(tmp_path): + # see https://github.com/astropy/astropy/issues/13191 + sfile = tmp_path / "x.ecsv" + Table().write(sfile) + t = Table.read(sfile) + assert len(t) == 0 + assert len(t.colnames) == 0 + + +def test_bad_delimiter(): + """ + Passing a delimiter other than space or comma gives an exception + """ + out = StringIO() + with pytest.raises(ValueError) as err: + T_DTYPES.write(out, format="ascii.ecsv", delimiter="|") + assert "only space and comma are allowed" in str(err.value) + + +@pytest.mark.parametrize("name", ["# name", ' #name " ']) +@pytest.mark.parametrize("delimiter", [" ", ","]) +def test_stressing_colname_starts_with_hash_etc(format_engine, name, delimiter): + """Column name starting with # that looks like a comment, see #18710. + + Also names that contain leading/trailing whitespace and a quote character. + """ + out = io.StringIO() + t = Table() + t[name] = [1, 2] + t["a"] = [3, 4] + t.write(out, delimiter=delimiter, **patch_format_write(format_engine)) + out.seek(0) + t2 = Table.read(out.getvalue(), **format_engine) + assert t2.colnames == [name, "a"] + + +def test_unavailable_ecsv_engine_for_writing(): + out = io.StringIO() + t = Table() + with pytest.raises( + ValueError, + match=r"^engine='pyarrow' is not a supported engine for writing, use 'io.ascii'$", + ): + t.write(out, format="ecsv", engine="pyarrow") + + +def test_bad_header_start(format_engine): + """ + Bad header without initial # %ECSV x.x + """ + lines = copy.copy(SIMPLE_LINES) + lines[0] = "# %ECV 0.9" + kwargs = {"guess": False} if format_engine["format"] == "ascii.ecsv" else {} + with pytest.raises(ascii.InconsistentTableError): + Table.read("\n".join(lines), **format_engine, **kwargs) + + +def test_bad_delimiter_input(format_engine): + """ + Illegal delimiter in input + """ + lines = copy.copy(SIMPLE_LINES) + lines.insert(2, "# delimiter: |") + kwargs = {"guess": False} if format_engine["format"] == "ascii.ecsv" else {} + with pytest.raises(ValueError) as err: + Table.read("\n".join(lines), **format_engine, **kwargs) + assert "only space and comma are allowed" in str(err.value) + + +def test_multidim_only_masked(format_engine): + """Multi-dimensional column with one masked entry + + This hits code in ``get_str_vals`` that requires a pure Python representation of + data instead of a numpy string array, because later on there can be substitution + of a longer value into the array element. + """ + txt = """ +# %ECSV 1.0 +# --- +# datatype: +# - {name: array3x2, datatype: string, subtype: 'float64[3,2]'} +array3x2 +"" +""" + t = Table.read(txt, **format_engine) + assert len(t) == 1 + assert np.all(t["array3x2"].mask == [[[True, True], [True, True], [True, True]]]) + + +def test_multidim_input(format_engine): + """ + Multi-dimensional column in input + """ + t = Table() + t["a"] = np.arange(24).reshape(2, 3, 4) + t["a"].info.description = "description" + t["a"].info.meta = {1: 2} + t["b"] = [1, 2] + + out = StringIO() + t.write(out, format="ascii.ecsv") + t2 = Table.read(out.getvalue(), **format_engine) + + assert np.all(t2["a"] == t["a"]) + assert t2["a"].shape == t["a"].shape + assert t2["a"].dtype == t["a"].dtype + assert t2["a"].info.description == t["a"].info.description + assert t2["a"].info.meta == t["a"].info.meta + + assert np.all(t2["b"] == t["b"]) + + +def test_structured_input(format_engine): + """ + Structured column in input. + """ + t = Table() + # Add unit, description and meta to make sure that round-trips as well. + t["a"] = Column( + [("B", (1.0, [2.0, 3.0])), ("A", (9.0, [8.0, 7.0]))], + dtype=[("s", "U1"), ("v", [("p0", "f8"), ("p1", "2f8")])], + description="description", + format=">", # Most formats do not work with structured! + unit="m", # Overall unit should round-trip. + meta={1: 2}, + ) + t["b"] = Column( + [[(1.0, 2.0), (9.0, 8.0)], [(3.0, 4.0), (7.0, 6.0)]], + dtype="f8,f8", + unit=u.Unit("m,s"), # Per part unit should round-trip too. + ) + + out = StringIO() + t.write(out, format="ascii.ecsv") + t2 = Table.read(out.getvalue(), **format_engine) + + for col in t.colnames: + assert np.all(t2[col] == t[col]) + assert t2[col].shape == t[col].shape + assert t2[col].dtype == t[col].dtype + assert t2[col].unit == t[col].unit + assert t2[col].format == t[col].format + assert t2[col].info.description == t[col].info.description + assert t2[col].info.meta == t[col].info.meta + + +def test_round_trip_empty_table(format_engine): + """Test fix in #5010 for issue #5009 (ECSV fails for empty type with bool type)""" + t = Table(dtype=[bool, "i", "f"], names=["a", "b", "c"]) + out = StringIO() + t.write(out, format="ascii.ecsv") + t2 = Table.read(out.getvalue(), **format_engine) + assert t.dtype == t2.dtype + assert len(t2) == 0 + + +def test_csv_ecsv_colnames_mismatch(format_engine): + """ + Test that mismatch in column names from normal CSV header vs. + ECSV YAML header raises the expected exception. + """ + lines = copy.copy(SIMPLE_LINES) + header_index = lines.index("a b c") + lines[header_index] = "a b d" + with pytest.raises(ValueError) as err: + Table.read(lines, **format_engine) + assert "column names from ECSV header ['a', 'b', 'c']" in str(err.value) + + +def test_regression_5604(): + """ + See https://github.com/astropy/astropy/issues/5604 for more. + """ + t = Table() + t.meta = {"foo": 5 * u.km, "foo2": u.s} + t["bar"] = [7] * u.km + + out = StringIO() + t.write(out, format="ascii.ecsv") + + assert "!astropy.units.Unit" in out.getvalue() + assert "!astropy.units.Quantity" in out.getvalue() + + +def assert_objects_equal(obj1, obj2, attrs, compare_class=True): + if compare_class: + assert obj1.__class__ is obj2.__class__ + + assert obj1.shape == obj2.shape + + info_attrs = [ + "info.name", + "info.format", + "info.unit", + "info.description", + "info.dtype", + ] + for attr in attrs + info_attrs: + a1 = obj1 + a2 = obj2 + for subattr in attr.split("."): + try: + a1 = getattr(a1, subattr) + a2 = getattr(a2, subattr) + except AttributeError: + a1 = a1[subattr] + a2 = a2[subattr] + + if isinstance(a1, np.ndarray) and a1.dtype.kind == "f": + assert quantity_allclose(a1, a2, rtol=1e-10) + else: + assert np.all(a1 == a2) + + # Check meta values and key order but allow None to be equivalent to {} + meta1 = obj1.info.meta or {} + meta2 = obj2.info.meta or {} + assert meta1 == meta2 + assert meta1.keys() == meta2.keys() + + # For no attrs that means we just compare directly. + if not attrs: + if isinstance(obj1, np.ndarray) and obj1.dtype.kind == "f": + assert quantity_allclose(obj1, obj2, rtol=1e-15) + else: + assert np.all(obj1 == obj2) + + +def test_ecsv_mixins_ascii_read_class(): + """Ensure that ascii.read(ecsv_file) returns the correct class + (QTable if any Quantity subclasses, Table otherwise). + """ + # Make a table with every mixin type except Quantities + t = QTable( + { + name: col + for name, col in mixin_cols.items() + if not isinstance(col.info, QuantityInfo) + } + ) + out = StringIO() + t.write(out, format="ascii.ecsv") + t2 = ascii.read(out.getvalue(), format="ecsv") + assert type(t2) is Table + + # Add a single quantity column + t["lon"] = mixin_cols["lon"] + + out = StringIO() + t.write(out, format="ascii.ecsv") + t2 = ascii.read(out.getvalue(), format="ecsv") + assert type(t2) is QTable + + +def test_ecsv_mixins_qtable_to_table(format_engine): + """Test writing as QTable and reading as Table. Ensure correct classes + come out. + """ + names = sorted(mixin_cols) + + t = QTable([mixin_cols[name] for name in names], names=names) + out = StringIO() + t.write(out, format="ascii.ecsv") + t2 = Table.read(out.getvalue(), **format_engine) + + assert t.colnames == t2.colnames + + for name, col in t.columns.items(): + col2 = t2[name] + attrs = compare_attrs[name] + compare_class = True + + if isinstance(col.info, QuantityInfo): + # Downgrade Quantity to Column + unit + assert type(col2) is Column + # Class-specific attributes like `value` or `wrap_angle` are lost. + attrs = ["unit"] + compare_class = False + # Compare data values here (assert_objects_equal doesn't know how in this case) + assert np.allclose(col.value, col2, rtol=1e-10) + + assert_objects_equal(col, col2, attrs, compare_class) + + +@pytest.mark.parametrize("table_cls", (Table, QTable)) +def test_ecsv_mixins_as_one(table_cls, format_engine): + """Test write/read all cols at once and validate intermediate column names""" + names = sorted(mixin_cols) + all_serialized_names = [] + # ECSV stores times as value by default, so we just get the column back. + # One exception is tm3, which is set to serialize via jd1 and jd2. + for name in names: + s_names = serialized_names[name] + if not name.startswith("tm3"): + s_names = [ + s_name.replace(".jd1", "") + for s_name in s_names + if not s_name.endswith("jd2") + ] + all_serialized_names.extend(s_names) + + t = table_cls([mixin_cols[name] for name in names], names=names) + + out = StringIO() + t.write(out, format="ascii.ecsv") + t2 = table_cls.read(out.getvalue(), **format_engine) + + assert t.colnames == t2.colnames + + # Read as a ascii.basic table (skip all the ECSV junk) + t3 = table_cls.read(out.getvalue(), format="ascii.basic") + assert t3.colnames == all_serialized_names + + +def make_multidim(col, ndim): + """Take a col with length=2 and make it N-d by repeating elements. + + For the special case of ndim==1 just return the original. + + The output has shape [3] * ndim. By using 3 we can be sure that repeating + the two input elements gives an output that is sufficiently unique for + the multidim tests. + """ + if ndim > 1: + idxs = [i % 2 for i in range(3**ndim)] + col = col[idxs].reshape([3] * ndim) + return col + + +@pytest.mark.parametrize("name_col", list(mixin_cols.items())) +@pytest.mark.parametrize("table_cls", (Table, QTable)) +@pytest.mark.parametrize("ndim", (1, 2, 3)) +def test_ecsv_mixins_per_column(table_cls, name_col, ndim, format_engine): + """Test write/read one col at a time and do detailed validation. + This tests every input column type as 1-d, 2-d and 3-d. + """ + name, col = name_col + + c = make_multidim(np.array([1.0, 2.0]), ndim) + col = make_multidim(col, ndim) + t = table_cls([c, col, c], names=["c1", name, "c2"]) + t[name].info.description = "description" + t[name].info.meta = {"b": 2, "a": 1} + + out = StringIO() + t.write(out, format="ascii.ecsv") + t2 = table_cls.read(out.getvalue(), **format_engine) + + assert t.colnames == t2.colnames + + for colname in t.colnames: + assert len(t2[colname].shape) == ndim + if colname in ("c1", "c2"): + compare = ["data"] + else: + # Storing Longitude as Column loses wrap_angle. + compare = [ + attr + for attr in compare_attrs[colname] + if not (attr == "wrap_angle" and table_cls is Table) + ] + assert_objects_equal(t[colname], t2[colname], compare) + + # Special case to make sure Column type doesn't leak into Time class data + if name.startswith("tm"): + assert t2[name]._time.jd1.__class__ is np.ndarray + assert t2[name]._time.jd2.__class__ is np.ndarray + + +def test_round_trip_masked_table_default(tmp_path): + """Test (mostly) round-trip of MaskedColumn through ECSV using default serialization + that uses an empty string "" to mark NULL values. Note: + + >>> simple_table(masked=True) + + a b c + int64 float64 str1 + ----- ------- ---- + -- 1.0 c + 2 2.0 -- + 3 -- e + """ + filename = tmp_path / "test.ecsv" + + t = simple_table(masked=True) # int, float, and str cols with one masked element + t.write(filename) + + t2 = Table.read(filename) + assert t2.masked is False + assert t2.colnames == t.colnames + for name in t2.colnames: + # From formal perspective the round-trip columns are the "same" + assert np.all(t2[name].mask == t[name].mask) + assert np.all(t2[name] == t[name]) + + # But peeking under the mask shows that the underlying data are changed + # because by default ECSV uses "" to represent masked elements. + t[name].mask = False + t2[name].mask = False + assert not np.all(t2[name] == t[name]) # Expected diff + + +def test_round_trip_masked_table_serialize_mask(tmp_path, format_engine): + """ + Same as prev but set the serialize_method to 'data_mask' so mask is written out + """ + filename = tmp_path / "test.ecsv" + + t = simple_table(masked=True) # int, float, and str cols with one masked element + t["c"][0] = "" # This would come back as masked for default "" NULL marker + + # MaskedColumn with no masked elements. See table the MaskedColumnInfo class + # _represent_as_dict() method for info about how we test a column with no masked elements. + t["d"] = [1, 2, 3] + + t.write(filename, serialize_method="data_mask") + + t2 = Table.read(filename, **format_engine) + assert t2.masked is False + assert t2.colnames == t.colnames + for name in t2.colnames: + assert np.all(t2[name].mask == t[name].mask) + assert np.all(t2[name] == t[name]) + + # Data under the mask round-trips also (unmask data to show this). + t[name].mask = False + t2[name].mask = False + assert np.all(t2[name] == t[name]) + + +@pytest.mark.parametrize("table_cls", (Table, QTable)) +def test_ecsv_round_trip_user_defined_unit(table_cls, tmp_path, format_engine): + """Ensure that we can read-back enabled user-defined units.""" + + # Test adapted from #8897, where it was noted that this works + # but was not tested. + filename = tmp_path / "test.ecsv" + unit = u.def_unit("bandpass_sol_lum") + t = table_cls() + t["l"] = np.arange(5) * unit + t.write(filename) + # without the unit enabled, get UnrecognizedUnit + if table_cls is QTable: + ctx = pytest.warns(u.UnitsWarning, match=r"'bandpass_sol_lum' did not parse .*") + else: + ctx = nullcontext() + # Note: The read might also generate ResourceWarning, in addition to UnitsWarning + with ctx: + t2 = table_cls.read(filename, **format_engine) + assert isinstance(t2["l"].unit, u.UnrecognizedUnit) + assert str(t2["l"].unit) == "bandpass_sol_lum" + if table_cls is QTable: + assert np.all(t2["l"].value == t["l"].value) + else: + assert np.all(t2["l"] == t["l"]) + + # But with it enabled, it works. + with u.add_enabled_units(unit): + t3 = table_cls.read(filename) + assert t3["l"].unit is unit + assert np.all(t3["l"] == t["l"]) + + # Just to be sure, also try writing with unit enabled. + filename2 = tmp_path / "test2.ecsv" + t3.write(filename2) + t4 = table_cls.read(filename, **format_engine) + assert t4["l"].unit is unit + assert np.all(t4["l"] == t["l"]) + + +def test_read_masked_bool(format_engine): + txt = """\ +# %ECSV 1.0 +# --- +# datatype: +# - {name: col0, datatype: bool} +# schema: astropy-2.0 +col0 +1 +0 +True +"" +False +""" + dat = Table.read(txt, **format_engine) + col = dat["col0"] + assert isinstance(col, MaskedColumn) + assert np.all(col.mask == [False, False, False, True, False]) + assert np.all(col == [True, False, True, False, False]) + + +@pytest.mark.parametrize("serialize_method", ["null_value", "data_mask"]) +@pytest.mark.parametrize("dtype", [np.int64, np.float64, bool, str]) +@pytest.mark.parametrize("delimiter", [",", " "]) +def test_roundtrip_multidim_masked_array( + serialize_method, dtype, delimiter, format_engine +): + # TODO also test empty string with null value + t = Table() + col = MaskedColumn(np.arange(12).reshape(2, 3, 2), dtype=dtype) + if dtype is str: + # np does something funny and gives a dtype of U21. + col = col.astype("U2") + col.mask[0, 0, 0] = True + col.mask[1, 1, 1] = True + t["a"] = col + t["b"] = ["x", "y"] # Add another column for kicks + out = StringIO() + t.write( + out, format="ascii.ecsv", serialize_method=serialize_method, delimiter=delimiter + ) + t2 = Table.read(out.getvalue(), **format_engine) + + assert t2.masked is False + assert t2.colnames == t.colnames + for name in t2.colnames: + assert t2[name].dtype == t[name].dtype + if hasattr(t[name], "mask"): + assert np.all(t2[name].mask == t[name].mask) + assert np.all(t2[name] == t[name]) + + +@pytest.mark.parametrize("subtype", ["some-user-type", "complex"]) +def test_multidim_unknown_subtype(subtype): + """Test an ECSV file with a string type but unknown subtype""" + txt = f"""\ +# %ECSV 1.0 +# --- +# datatype: +# - name: a +# datatype: string +# subtype: {subtype} +# schema: astropy-2.0 +a +[1,2] +[3,4]""" + with pytest.warns( + InvalidEcsvDatatypeWarning, + match=rf"unexpected subtype '{subtype}' set for column 'a'", + ): + t = Table.read(txt, format="ascii.ecsv") + + assert t["a"].dtype.kind == "U" + assert t["a"][0] == "[1,2]" + + +def test_multidim_bad_shape(format_engine): + """Test a malformed ECSV file""" + txt = """\ +# %ECSV 1.0 +# --- +# datatype: +# - name: a +# datatype: string +# subtype: int64[3] +# schema: astropy-2.0 +a +[1,2] +[3,4]""" + with pytest.raises( + ValueError, match="column 'a' failed to convert: shape mismatch" + ): + Table.read(txt, **format_engine) + + +def test_write_not_json_serializable(): + t = Table() + t["a"] = np.array([{1, 2}, 1], dtype=object) + match = ( + "could not convert column 'a' to string: Object of type set is not JSON" + " serializable" + ) + out = StringIO() + with pytest.raises(TypeError, match=match): + t.write(out, format="ascii.ecsv") + + +def test_read_not_json_serializable(format_engine): + """Test a malformed ECSV file""" + txt = """\ +# %ECSV 1.0 +# --- +# datatype: +# - {name: a, datatype: string, subtype: json} +# schema: astropy-2.0 +a +fail +[3,4]""" + match = "column 'a' failed to convert: column value is not valid JSON" + with pytest.raises(ValueError, match=match): + Table.read(txt, **format_engine) + + +def test_read_bad_datatype(format_engine): + """Test a malformed ECSV file""" + txt = """\ +# %ECSV 1.0 +# --- +# datatype: +# - {name: a, datatype: object} +# schema: astropy-2.0 +a +{"x":1} +[3,4]""" + with pytest.warns( + InvalidEcsvDatatypeWarning, + match="unexpected datatype 'object' of column 'a' is not in allowed", + ): + t = Table.read(txt, **format_engine) + col = t["a"] + if format_engine["format"] == "ascii.ecsv": + # This is the legacy "passing" behavior for io.ascii.ecsv, but it is just wrong. + # Not clear why this was included as expected behavior. + assert col[0] == '{"x":1}' + assert col[1] == "[3,4]" + assert all(type(col[ii]) is str for ii in range(len(col))) + assert col.dtype.kind == "O" + else: + assert col[0] == {"x": 1} + assert type(col[0]) is dict + assert col[1] == [3, 4] + assert type(col[1]) is list + assert col.dtype.kind == "O" + + +def test_read_complex(format_engine): + """Test an ECSV v1.0 file with a complex column""" + txt = """\ +# %ECSV 1.0 +# --- +# datatype: +# - {name: a, datatype: complex} +# schema: astropy-2.0 +a +1+1j +2+2j""" + + with pytest.warns( + InvalidEcsvDatatypeWarning, + match="unexpected datatype 'complex' of column 'a' is not in allowed", + ): + t = Table.read(txt, **format_engine) + assert t["a"].dtype.type is np.complex128 + + +def test_read_str(format_engine): + """Test an ECSV file with a 'str' instead of 'string' datatype""" + txt = """\ +# %ECSV 1.0 +# --- +# datatype: +# - {name: a, datatype: str} +# schema: astropy-2.0 +a +sometext +S""" # also testing single character text + + with pytest.warns( + InvalidEcsvDatatypeWarning, + match="unexpected datatype 'str' of column 'a' is not in allowed", + ): + t = Table.read(txt, **format_engine) + assert isinstance(t["a"][1], str) + assert isinstance(t["a"][0], np.str_) + + +def test_read_bad_datatype_for_object_subtype(format_engine): + """Test a malformed ECSV file""" + txt = """\ +# %ECSV 1.0 +# --- +# datatype: +# - {name: a, datatype: int64, subtype: json} +# schema: astropy-2.0 +a +fail +[3,4]""" + match = "column 'a' failed to convert: datatype of column 'a' must be \"string\"" + with pytest.raises(ValueError, match=match): + Table.read(txt, **format_engine) + + +def test_full_repr_roundtrip(format_engine): + """Test round-trip of float values to full precision even with format + specified""" + t = Table() + t["a"] = np.array([np.pi, 1 / 7], dtype=np.float64) + t["a"].info.format = ".2f" + out = StringIO() + t.write(out, format="ascii.ecsv") + t2 = Table.read(out.getvalue(), **format_engine) + # Very small differences in float64 values with pandas. TODO: check why. + atol = 1e-16 if format_engine.get("engine") == "pandas" else 0.0 + np.testing.assert_allclose(t["a"], t2["a"], atol=atol, rtol=0) + assert t2["a"].info.format == ".2f" + + +############################################################################# +# Define a number of specialized columns for testing and the expected values +# of `datatype` for each column. +############################################################################# + + +# First here is some helper code used to make the expected outputs code. +def _get_ecsv_header_dict(text): + lines = [line.strip() for line in text.splitlines()] + lines = [line[2:] for line in lines if line.startswith("#")] + lines = lines[2:] # Get rid of the header + return yaml.safe_load("\n".join(lines)) + + +def _make_expected_values(cols): + for name, col in cols.items(): + t = Table() + t[name] = col + out = StringIO() + t.write(out, format="ascii.ecsv") + hdr = _get_ecsv_header_dict(out.getvalue()) + fmt_hdr = pformat(hdr["datatype"]) + print(f"exps[{name!r}] =", fmt_hdr[:1]) + print(fmt_hdr[1:]) + print() + + +# Expected values of `datatype` for each column +exps = {} +cols = {} + +# Run of the mill scalar for completeness +cols["scalar"] = np.array([1, 2], dtype=np.int16) +exps["scalar"] = [{"datatype": "int16", "name": "scalar"}] + +# Array of lists that works as a 2-d variable array. This is just treated +# as an object. +cols["2-d variable array lists"] = c = np.empty(shape=(2,), dtype=object) +c[0] = [[1, 2], ["a", 4]] +c[1] = [[1, 2, 3], [4, 5.25, 6]] +exps["2-d variable array lists"] = [ + {"datatype": "string", "name": "2-d variable array lists", "subtype": "json"} +] + +# Array of numpy arrays that is a 2-d variable array +cols["2-d variable array numpy"] = c = np.empty(shape=(2,), dtype=object) +c[0] = np.array([[1, 2], [3, 4]], dtype=np.float32) +c[1] = np.array([[1, 2, 3], [4, 5.5, 6]], dtype=np.float32) +exps["2-d variable array numpy"] = [ + { + "datatype": "string", + "name": "2-d variable array numpy", + "subtype": "float32[2,null]", + } +] + +cols["1-d variable array lists"] = np.array([[1, 2], [3, 4, 5]], dtype=object) +exps["1-d variable array lists"] = [ + {"datatype": "string", "name": "1-d variable array lists", "subtype": "json"} +] + +# Variable-length array +cols["1-d variable array numpy"] = np.array( + [np.array([1, 2], dtype=np.uint8), np.array([3, 4, 5], dtype=np.uint8)], + dtype=object, +) +exps["1-d variable array numpy"] = [ + {"datatype": "string", "name": "1-d variable array numpy", "subtype": "uint8[null]"} +] + +cols["1-d variable array numpy str"] = np.array( + [np.array(["a", "b"]), np.array(["c", "d", "e"])], dtype=object +) +exps["1-d variable array numpy str"] = [ + { + "datatype": "string", + "name": "1-d variable array numpy str", + "subtype": "string[null]", + } +] + +cols["1-d variable array numpy bool"] = np.array( + [np.array([True, False]), np.array([True, False, True])], dtype=object +) +exps["1-d variable array numpy bool"] = [ + { + "datatype": "string", + "name": "1-d variable array numpy bool", + "subtype": "bool[null]", + } +] + +cols["1-d regular array"] = np.array([[1, 2], [3, 4]], dtype=np.int8) +exps["1-d regular array"] = [ + {"datatype": "string", "name": "1-d regular array", "subtype": "int8[2]"} +] + +cols["2-d regular array"] = np.arange(8, dtype=np.float16).reshape(2, 2, 2) +exps["2-d regular array"] = [ + {"datatype": "string", "name": "2-d regular array", "subtype": "float16[2,2]"} +] + +cols["scalar object"] = np.array([{"a": 1}, {"b": 2}], dtype=object) +exps["scalar object"] = [ + {"datatype": "string", "name": "scalar object", "subtype": "json"} +] + +cols["1-d object"] = np.array( + [[{"a": 1}, {"b": 2}], [{"a": 1}, {"b": 2}]], dtype=object +) +exps["1-d object"] = [ + {"datatype": "string", "name": "1-d object", "subtype": "json[2]"} +] + + +@pytest.mark.parametrize("name,col,exp", list(zip(cols, cols.values(), exps.values()))) +def test_specialized_columns(name, col, exp, format_engine): + """Test variable length lists, multidim columns, object columns.""" + t = Table() + t[name] = col + out = StringIO() + t.write(out, format="ascii.ecsv") + hdr = _get_ecsv_header_dict(out.getvalue()) + assert hdr["datatype"] == exp + t2 = Table.read(out.getvalue(), **format_engine) + assert t2.colnames == t.colnames + for colname in t2.colnames: + assert t2[colname].dtype == t[colname].dtype + for val1, val2 in zip(t2[colname], t[colname]): + if isinstance(val1, np.ndarray): + assert val1.dtype == val2.dtype + assert np.all(val1 == val2) + + +def test_full_subtypes(format_engine): + """Read ECSV file created by M. Taylor that includes scalar, fixed array, + variable array for all datatypes. This file has missing values for all + columns as both per-value null and blank entries for the entire column + value. + + Note: original file was modified to include blank values in f_float and + f_double columns. + """ + t = Table.read(os.path.join(TEST_DIR, "data", "subtypes.ecsv"), **format_engine) + colnames = [ + "i_index", + "s_byte", + "s_short", + "s_int", + "s_long", + "s_float", + "s_double", + "s_string", + "s_boolean", + "f_byte", + "f_short", + "f_int", + "f_long", + "f_float", + "f_double", + "f_string", + "f_boolean", + "v_byte", + "v_short", + "v_int", + "v_long", + "v_float", + "v_double", + "v_string", + "v_boolean", + "m_int", + "m_double", + ] + assert t.colnames == colnames + + type_map = { + "byte": "int8", + "short": "int16", + "int": "int32", + "long": "int64", + "float": "float32", + "double": "float64", + "string": "str", + "boolean": "bool", + } + + for col in t.itercols(): + info = col.info + if info.name == "i_index": + continue + + assert isinstance(col, MaskedColumn) + + type_name = info.name[2:] # short, int, etc + subtype = info.name[:1] + + if subtype == "s": # Scalar + assert col.shape == (16,) + + if subtype == "f": # Fixed array + assert col.shape == (16, 3) + + if subtype == "v": # Variable array + assert col.shape == (16,) + assert info.dtype.name == "object" + for val in col: + assert isinstance(val, np.ndarray) + assert val.dtype.name.startswith(type_map[type_name]) + assert len(val) in [0, 1, 2, 3] + else: + assert info.dtype.name.startswith(type_map[type_name]) + + +def test_masked_empty_subtypes(format_engine): + """Test blank field in subtypes. Similar to previous test but with explicit + checks of values""" + txt = """ +# %ECSV 1.0 +# --- +# datatype: +# - {name: o, datatype: string, subtype: json} +# - {name: f, datatype: string, subtype: 'int64[2]'} +# - {name: v, datatype: string, subtype: 'int64[null]'} +# schema: astropy-2.0 +o f v +null [0,1] [1] +"" "" "" +[1,2] [2,3] [2,3] +""" + t = Table.read(txt, **format_engine) + assert np.all(t["o"] == np.array([None, -1, [1, 2]], dtype=object)) + assert np.all(t["o"].mask == [False, True, False]) + + exp = np.ma.array([[0, 1], [-1, -1], [2, 3]], mask=[[0, 0], [1, 1], [0, 0]]) + assert np.all(t["f"] == exp) + assert np.all(t["f"].mask == exp.mask) + + assert np.all(t["v"][0] == [1]) + assert np.all(t["v"][2] == [2, 3]) + assert np.all(t["v"].mask == [False, True, False]) + + +def test_masked_vals_in_array_subtypes(format_engine): + """Test null values in fixed and variable array subtypes.""" + t = Table() + t["f"] = np.ma.array([[1, 2], [3, 4]], mask=[[0, 1], [1, 0]], dtype=np.int64) + t["v"] = np.empty(2, dtype=object) + t["v"][0] = np.ma.array([1, 2], mask=[0, 1], dtype=np.int64) + t["v"][1] = np.ma.array([3, 4, 5], mask=[1, 0, 0], dtype=np.int64) + + out = StringIO() + t.write(out, format="ascii.ecsv") + txt = """ + # %ECSV 1.0 + # --- + # datatype: + # - {name: f, datatype: string, subtype: 'int64[2]'} + # - {name: v, datatype: string, subtype: 'int64[null]'} + # schema: astropy-2.0 + f v + [1,null] [1,null] + [null,4] [null,4,5] + """ + hdr = _get_ecsv_header_dict(out.getvalue()) + hdr_exp = _get_ecsv_header_dict(txt) + assert hdr == hdr_exp + t2 = Table.read(out.getvalue(), **format_engine) + assert t2.colnames == t.colnames + for name in t2.colnames: + assert t2[name].dtype == t[name].dtype + assert type(t2[name]) is type(t[name]) + for val1, val2 in zip(t2[name], t[name]): + if isinstance(val1, np.ndarray): + assert val1.dtype == val2.dtype + if isinstance(val1, np.ma.MaskedArray): + assert np.all(val1.mask == val2.mask) + assert np.all(val1 == val2) + + +def test_guess_ecsv_with_one_column(): + """Except for ECSV, guessing always requires at least 2 columns""" + txt = """ + # %ECSV 1.0 + # --- + # datatype: + # - {name: col, datatype: string, description: hello} + # schema: astropy-2.0 + col + 1 + 2 + """ + t = ascii.read(txt) + assert t["col"].dtype.kind == "U" # would be int with basic format + assert t["col"].description == "hello" + + +@pytest.mark.parametrize("masked", [MaskedColumn, Masked, np.ma.MaskedArray]) +def test_write_structured_masked_column(masked, format_engine): + a = np.array([(1, 2), (3, 4)], dtype="i,i") + mc = masked(a, mask=[(True, False), (False, False)]) + t = Table([mc], names=["mc"]) + out = StringIO() + t.write(out, format="ascii.ecsv") + t2 = Table.read(out.getvalue(), **format_engine) + assert type(t2["mc"]) is type(t["mc"]) + assert (t2["mc"] == mc).all() + assert (t2["mc"].mask == mc.mask).all() + + +def test_write_masked_time_ymdhms_mixin(format_engine): + # Regression test for gh-16370 + # Make a masked time, + t = Time({"year": 2000, "month": 1, "day": [1, 2]}) + t[0] = np.ma.masked + # Create a table and write to a file + qt = QTable([t], names=["t"]) + out = StringIO() + qt.write(out, format="ascii.ecsv") + # Read back and compare. + qt2 = QTable.read(out.getvalue(), **format_engine) + # Note that value under time does not roundtrip + assert (qt2["t"] == t).all() + assert (qt2["t"].mask == t.mask).all() + + +def test_register_bad_engine(): + msg = "Subclasses of ECSVEngine must define a class attribute 'name' as a string, got ." + with pytest.raises(TypeError, match=msg): + + class BadEngine(ECSVEngine): + name = 1 + format = "ascii.ecsv" + + +@pytest.mark.parametrize( + "compressed_filename", + [ + "test.ecsv.gz", + pytest.param( + "test.ecsv.bz2", + marks=pytest.mark.xfail(not HAS_BZ2, reason="no bz2 support"), + ), + pytest.param( + "test.ecsv.xz", + marks=pytest.mark.xfail(not HAS_LZMA, reason="no lzma support"), + ), + ], +) +def test_compressed_files(tmp_path, format_engine, compressed_filename): + filename = tmp_path / "test.ecsv" + t = simple_table() + t.write(filename, format="ascii.ecsv") + + compressed_filename = tmp_path / compressed_filename + if compressed_filename.suffix == ".gz": + import gzip + + opener = gzip.open + elif compressed_filename.suffix == ".bz2": + import bz2 + + opener = bz2.open + elif compressed_filename.suffix == ".xz": + import lzma + + opener = lzma.open + else: + # Shouldn't really happen + opener = open + + # Compress ecsv file + with open(filename, "rb") as f_in: + with opener(compressed_filename, "wb") as f_out: + f_out.writelines(f_in) + + # Open compressed file and compare to ensure it's read correctly + t_comp = Table.read(compressed_filename, **format_engine) + assert_array_equal(t, t_comp) + + +def test_meta_not_omap_but_list(format_engine): + """The ecsv spec allows that header meta to be any valid safe YAML. + + Though astropy's writer writes with an !!omap tag, and all examples + in the ecsv spec (https://github.com/astropy/astropy-APEs/blob/main/APE6.rst) + use that extra (non standard) tag, the spec specifically says + that the header meta + 'an arbitrary data structure consisting purely of data types that can be encoded + and decoded with the YAML "safe" dumper and loader'. + + See one example in https://github.com/astropy/astropy/issues/5990 + """ + txt = """ +# %ECSV 1.0 +# --- +# meta: +# - keywords: +# - {z_key1: val1} +# - {a_key2: val2} +# - comments: [Comment 1, Comment 2, Comment 3] +# datatype: +# - name: fake +# datatype: string +fake +0""" + with pytest.warns( + UserWarning, + match="Found ECSV table meta of type list instead of", + ): + t = Table.read(txt, **format_engine) + assert len(t.meta) == 1 + assert len(t.meta["meta"]) == 2 + assert t.meta["meta"][0] == {"keywords": [{"z_key1": "val1"}, {"a_key2": "val2"}]} + assert t.meta["meta"][1] == {"comments": ["Comment 1", "Comment 2", "Comment 3"]} + + +MAP_BUT_NOT_OMAP_LINES = [ + "# %ECSV 1.0", + "# ---", + "# datatype:", + "# - {name: fake, datatype: string}", + "# meta: !!omap", + "# - {hr: 65}", + "# - {avg: 0.278}", + "# - {rbi: 147}", + "# schema: astropy-2.0", + "fake", + "0", +] + + +def test_meta_not_omap_but_map(format_engine): + """Like test_meta_not_omap_but_list but for a mapping""" + txt = """ +# %ECSV 1.0 +# --- +# datatype: +# - {name: fake, datatype: string} +# meta: +# hr: 65 # Home runs +# avg: 0.278 # Batting average +# rbi: 147 # Runs Batted In +# schema: astropy-2.0 +fake +0""" + t = Table.read(txt, **format_engine) + assert t.meta["hr"] == 65 + assert t.meta["avg"] == 0.278 + assert t.meta["rbi"] == 147 + out = StringIO() + t.write(out, format="ascii.ecsv") + assert out.getvalue().splitlines() == MAP_BUT_NOT_OMAP_LINES diff --git a/astropy/io/ascii/tests/test_fixedwidth.py b/astropy/io/ascii/tests/test_fixedwidth.py index edfc15b9c120..7460f9ae5ebf 100644 --- a/astropy/io/ascii/tests/test_fixedwidth.py +++ b/astropy/io/ascii/tests/test_fixedwidth.py @@ -1,17 +1,16 @@ -import re -import glob -import numpy as np -from ... import ascii as asciitable +# Licensed under a 3-clause BSD style license - see LICENSE.rst + -io = asciitable.core.io +from io import StringIO -from .common import (raises, - assert_equal, assert_almost_equal, assert_true, - setup_function, teardown_function) +import numpy as np +import pytest +from numpy.testing import assert_allclose +from astropy.io import ascii +from astropy.io.ascii.core import InconsistentTableError -def assert_equal_splitlines(arg1, arg2): - assert_equal(arg1.splitlines(), arg2.splitlines()) +from .common import assert_equal_splitlines def test_read_normal(): @@ -22,12 +21,12 @@ def test_read_normal(): | 1.2 | "hello" | | 2.4 |'s worlds| """ - reader = asciitable.get_reader(Reader=asciitable.FixedWidth) + reader = ascii.get_reader(reader_cls=ascii.FixedWidth) dat = reader.read(table) - assert_equal(reader.header.colnames, ('Col1', 'Col2')) - assert_almost_equal(dat[1][0], 2.4) - assert_equal(dat[0][1], '"hello"') - assert_equal(dat[1][1], "'s worlds") + assert dat.colnames == ["Col1", "Col2"] + assert_allclose(dat[1][0], 2.4) + assert dat[0][1] == '"hello"' + assert dat[1][1] == "'s worlds" def test_read_normal_names(): @@ -38,11 +37,10 @@ def test_read_normal_names(): | 1.2 | "hello" | | 2.4 |'s worlds| """ - reader = asciitable.get_reader(Reader=asciitable.FixedWidth, - names=('name1', 'name2')) + reader = ascii.get_reader(reader_cls=ascii.FixedWidth, names=("name1", "name2")) dat = reader.read(table) - assert_equal(reader.header.colnames, ('name1', 'name2')) - assert_almost_equal(dat[1][0], 2.4) + assert dat.colnames == ["name1", "name2"] + assert_allclose(dat[1][0], 2.4) def test_read_normal_names_include(): @@ -53,13 +51,15 @@ def test_read_normal_names_include(): | 1.2 | "hello" | 3 | | 2.4 |'s worlds| 7 | """ - reader = asciitable.get_reader(Reader=asciitable.FixedWidth, - names=('name1', 'name2', 'name3'), - include_names=('name1', 'name3')) + reader = ascii.get_reader( + reader_cls=ascii.FixedWidth, + names=("name1", "name2", "name3"), + include_names=("name1", "name3"), + ) dat = reader.read(table) - assert_equal(reader.header.colnames, ('name1', 'name3')) - assert_almost_equal(dat[1][0], 2.4) - assert_equal(dat[0][1], 3) + assert dat.colnames == ["name1", "name3"] + assert_allclose(dat[1][0], 2.4) + assert dat[0][1] == 3 def test_read_normal_exclude(): @@ -70,26 +70,25 @@ def test_read_normal_exclude(): | 1.2 | "hello" | | 2.4 |'s worlds| """ - reader = asciitable.get_reader(Reader=asciitable.FixedWidth, - exclude_names=('Col1',)) + reader = ascii.get_reader(reader_cls=ascii.FixedWidth, exclude_names=("Col1",)) dat = reader.read(table) - assert_equal(reader.header.colnames, ('Col2',)) - assert_equal(dat[1][0], "'s worlds") + assert dat.colnames == ["Col2"] + assert dat[1][0] == "'s worlds" def test_read_weird(): - """Weird input table with data values chopped by col extent """ + """Weird input table with data values chopped by col extent""" table = """ Col1 | Col2 | 1.2 "hello" 2.4 sdf's worlds """ - reader = asciitable.get_reader(Reader=asciitable.FixedWidth) + reader = ascii.get_reader(reader_cls=ascii.FixedWidth) dat = reader.read(table) - assert_equal(reader.header.colnames, ('Col1', 'Col2')) - assert_almost_equal(dat[1][0], 2.4) - assert_equal(dat[0][1], '"hel') - assert_equal(dat[1][1], "df's wo") + assert dat.colnames == ["Col1", "Col2"] + assert_allclose(dat[1][0], 2.4) + assert dat[0][1] == '"hel' + assert dat[1][1] == "df's wo" def test_read_double(): @@ -100,11 +99,11 @@ def test_read_double(): | Mary | 555-2134 |192.168.1.12X| | Bob | 555-4527 | 192.168.1.9X| """ - dat = asciitable.read(table, Reader=asciitable.FixedWidth, guess=False) - assert_equal(tuple(dat.dtype.names), ('Name', 'Phone', 'TCP')) - assert_equal(dat[1][0], "Mary") - assert_equal(dat[0][1], "555-1234") - assert_equal(dat[2][2], "192.168.1.9") + dat = ascii.read(table, format="fixed_width", guess=False) + assert tuple(dat.dtype.names) == ("Name", "Phone", "TCP") + assert dat[1][0] == "Mary" + assert dat[0][1] == "555-1234" + assert dat[2][2] == "192.168.1.9" def test_read_space_delimiter(): @@ -115,12 +114,11 @@ def test_read_space_delimiter(): Mary 555-2134 192.168.1.12 Bob 555-4527 192.168.1.9 """ - dat = asciitable.read(table, Reader=asciitable.FixedWidth, guess=False, - delimiter=' ') - assert_equal(tuple(dat.dtype.names), ('Name', '--Phone-', '----TCP-----')) - assert_equal(dat[1][0], "Mary") - assert_equal(dat[0][1], "555-1234") - assert_equal(dat[2][2], "192.168.1.9") + dat = ascii.read(table, format="fixed_width", guess=False, delimiter=" ") + assert tuple(dat.dtype.names) == ("Name", "--Phone-", "----TCP-----") + assert dat[1][0] == "Mary" + assert dat[0][1] == "555-1234" + assert dat[2][2] == "192.168.1.9" def test_read_no_header_autocolumn(): @@ -130,12 +128,13 @@ def test_read_no_header_autocolumn(): | Mary | 555-2134 |192.168.1.12| | Bob | 555-4527 | 192.168.1.9| """ - dat = asciitable.read(table, Reader=asciitable.FixedWidth, guess=False, - header_start=None, data_start=0) - assert_equal(tuple(dat.dtype.names), ('col1', 'col2', 'col3')) - assert_equal(dat[1][0], "Mary") - assert_equal(dat[0][1], "555-1234") - assert_equal(dat[2][2], "192.168.1.9") + dat = ascii.read( + table, format="fixed_width", guess=False, header_start=None, data_start=0 + ) + assert tuple(dat.dtype.names) == ("col1", "col2", "col3") + assert dat[1][0] == "Mary" + assert dat[0][1] == "555-1234" + assert dat[2][2] == "192.168.1.9" def test_read_no_header_names(): @@ -143,16 +142,21 @@ def test_read_no_header_names(): and third rows also have hanging spaces after final |.""" table = """ | John | 555-1234 |192.168.1.10| -| Mary | 555-2134 |192.168.1.12| -| Bob | 555-4527 | 192.168.1.9| +| Mary | 555-2134 |192.168.1.12| +| Bob | 555-4527 | 192.168.1.9| """ - dat = asciitable.read(table, Reader=asciitable.FixedWidth, guess=False, - header_start=None, data_start=0, - names=('Name', 'Phone', 'TCP')) - assert_equal(tuple(dat.dtype.names), ('Name', 'Phone', 'TCP')) - assert_equal(dat[1][0], "Mary") - assert_equal(dat[0][1], "555-1234") - assert_equal(dat[2][2], "192.168.1.9") + dat = ascii.read( + table, + format="fixed_width", + guess=False, + header_start=None, + data_start=0, + names=("Name", "Phone", "TCP"), + ) + assert tuple(dat.dtype.names) == ("Name", "Phone", "TCP") + assert dat[1][0] == "Mary" + assert dat[0][1] == "555-1234" + assert dat[2][2] == "192.168.1.9" def test_read_no_header_autocolumn_NoHeader(): @@ -162,11 +166,11 @@ def test_read_no_header_autocolumn_NoHeader(): | Mary | 555-2134 |192.168.1.12| | Bob | 555-4527 | 192.168.1.9| """ - dat = asciitable.read(table, Reader=asciitable.FixedWidthNoHeader) - assert_equal(tuple(dat.dtype.names), ('col1', 'col2', 'col3')) - assert_equal(dat[1][0], "Mary") - assert_equal(dat[0][1], "555-1234") - assert_equal(dat[2][2], "192.168.1.9") + dat = ascii.read(table, format="fixed_width_no_header") + assert tuple(dat.dtype.names) == ("col1", "col2", "col3") + assert dat[1][0] == "Mary" + assert dat[0][1] == "555-1234" + assert dat[2][2] == "192.168.1.9" def test_read_no_header_names_NoHeader(): @@ -174,15 +178,16 @@ def test_read_no_header_names_NoHeader(): and third rows also have hanging spaces after final |.""" table = """ | John | 555-1234 |192.168.1.10| -| Mary | 555-2134 |192.168.1.12| -| Bob | 555-4527 | 192.168.1.9| +| Mary | 555-2134 |192.168.1.12| +| Bob | 555-4527 | 192.168.1.9| """ - dat = asciitable.read(table, Reader=asciitable.FixedWidthNoHeader, - names=('Name', 'Phone', 'TCP')) - assert_equal(tuple(dat.dtype.names), ('Name', 'Phone', 'TCP')) - assert_equal(dat[1][0], "Mary") - assert_equal(dat[0][1], "555-1234") - assert_equal(dat[2][2], "192.168.1.9") + dat = ascii.read( + table, format="fixed_width_no_header", names=("Name", "Phone", "TCP") + ) + assert tuple(dat.dtype.names) == ("Name", "Phone", "TCP") + assert dat[1][0] == "Mary" + assert dat[0][1] == "555-1234" + assert dat[2][2] == "192.168.1.9" def test_read_col_starts(): @@ -194,16 +199,65 @@ def test_read_col_starts(): Mary 555- 2134 192.168.1.12 Bob 555- 4527 192.168.1.9 """ - dat = asciitable.read(table, Reader=asciitable.FixedWidthNoHeader, - names=('Name', 'Phone', 'TCP'), - col_starts=(0, 9, 18), - col_ends=(5, 17, 28), - ) - assert_equal(tuple(dat.dtype.names), ('Name', 'Phone', 'TCP')) - assert_equal(dat[0][1], "555- 1234") - assert_equal(dat[1][0], "Mary") - assert_equal(dat[1][2], "192.168.1.") - assert_equal(dat[2][2], "192.168.1") # col_end=28 cuts this column off + dat = ascii.read( + table, + format="fixed_width_no_header", + names=("Name", "Phone", "TCP"), + col_starts=(0, 9, 18), + col_ends=(5, 17, 28), + ) + assert tuple(dat.dtype.names) == ("Name", "Phone", "TCP") + assert dat[0][1] == "555- 1234" + assert dat[1][0] == "Mary" + assert dat[1][2] == "192.168.1." + assert dat[2][2] == "192.168.1" # col_end=28 cuts this column off + + +def test_read_detect_col_starts_or_ends(): + """Table with no delimiter with only column start or end values specified""" + table = """ +#1 9 19 <== Column start indexes +#| | | <== Column start positions +#<------><--------><-------------> <== Inferred column positions + John 555- 1234 192.168.1.10 + Mary 555- 2134 192.168.1.123 + Bob 555- 4527 192.168.1.9 + Bill 555-9875 192.255.255.255 +""" + for kwargs in ({"col_starts": (1, 9, 19)}, {"col_ends": (8, 18, 33)}): + dat = ascii.read( + table, + format="fixed_width_no_header", + names=("Name", "Phone", "TCP"), + **kwargs, + ) + assert tuple(dat.dtype.names) == ("Name", "Phone", "TCP") + assert dat[0][1] == "555- 1234" + assert dat[1][0] == "Mary" + assert dat[1][2] == "192.168.1.123" + assert dat[3][2] == "192.255.255.255" + + +def test_read_fill_values_and_reassign_colname(): + """Nice, typical fixed format table""" + table = """ +| day | precip | temp | +| Mon | 1.5 | rain | +| Tues | -999.0 | N/A | +| Wed | N/A | snow | +""" + dat = ascii.read( + table, + format="fixed_width", + data_start=1, + fill_values=[("-999.0", "0", "b"), ("N/A", "0", "c"), ("N/A", "0", "b")], + names=["a", "b", "c"], + ) + assert dat.colnames == ["a", "b", "c"] + assert dat[1][1] is np.ma.masked + assert dat[1][2] is np.ma.masked + assert dat[2][1] is np.ma.masked + assert_allclose(dat[0][1], 1.5) table = """\ @@ -211,142 +265,187 @@ def test_read_col_starts(): | 1.2 | "hello" | 1 | a | | 2.4 | 's worlds | 2 | 2 | """ -dat = asciitable.read(table, Reader=asciitable.FixedWidth) +dat = ascii.read(table, format="fixed_width") def test_write_normal(): """Write a table as a normal fixed width table.""" - out = io.StringIO() - asciitable.write(dat, out, Writer=asciitable.FixedWidth) - assert_equal_splitlines(out.getvalue(), """\ + out = StringIO() + ascii.write(dat, out, format="fixed_width") + assert_equal_splitlines( + out.getvalue(), + """\ | Col1 | Col2 | Col3 | Col4 | | 1.2 | "hello" | 1 | a | | 2.4 | 's worlds | 2 | 2 | -""") +""", + ) + + +def test_write_fill_values(): + """Write a table as a normal fixed width table.""" + out = StringIO() + ascii.write(dat, out, format="fixed_width", fill_values=("a", "N/A")) + assert_equal_splitlines( + out.getvalue(), + """\ +| Col1 | Col2 | Col3 | Col4 | +| 1.2 | "hello" | 1 | N/A | +| 2.4 | 's worlds | 2 | 2 | +""", + ) def test_write_no_pad(): """Write a table as a fixed width table with no padding.""" - out = io.StringIO() - asciitable.write(dat, out, Writer=asciitable.FixedWidth, - delimiter_pad=None) - assert_equal_splitlines(out.getvalue(), """\ + out = StringIO() + ascii.write(dat, out, format="fixed_width", delimiter_pad=None) + assert_equal_splitlines( + out.getvalue(), + """\ |Col1| Col2|Col3|Col4| | 1.2| "hello"| 1| a| | 2.4|'s worlds| 2| 2| -""") +""", + ) def test_write_no_bookend(): """Write a table as a fixed width table with no bookend.""" - out = io.StringIO() - asciitable.write(dat, out, Writer=asciitable.FixedWidth, bookend=False) - assert_equal_splitlines(out.getvalue(), """\ + out = StringIO() + ascii.write(dat, out, format="fixed_width", bookend=False) + assert_equal_splitlines( + out.getvalue(), + """\ Col1 | Col2 | Col3 | Col4 1.2 | "hello" | 1 | a 2.4 | 's worlds | 2 | 2 -""") +""", + ) def test_write_no_delimiter(): """Write a table as a fixed width table with no delimiter.""" - out = io.StringIO() - asciitable.write(dat, out, Writer=asciitable.FixedWidth, bookend=False, - delimiter=None) - assert_equal_splitlines(out.getvalue(), """\ + out = StringIO() + ascii.write(dat, out, format="fixed_width", bookend=False, delimiter=None) + assert_equal_splitlines( + out.getvalue(), + """\ Col1 Col2 Col3 Col4 1.2 "hello" 1 a 2.4 's worlds 2 2 -""") +""", + ) def test_write_noheader_normal(): """Write a table as a normal fixed width table.""" - out = io.StringIO() - asciitable.write(dat, out, Writer=asciitable.FixedWidthNoHeader) - assert_equal_splitlines(out.getvalue(), """\ + out = StringIO() + ascii.write(dat, out, format="fixed_width_no_header") + assert_equal_splitlines( + out.getvalue(), + """\ | 1.2 | "hello" | 1 | a | | 2.4 | 's worlds | 2 | 2 | -""") +""", + ) def test_write_noheader_no_pad(): """Write a table as a fixed width table with no padding.""" - out = io.StringIO() - asciitable.write(dat, out, Writer=asciitable.FixedWidthNoHeader, - delimiter_pad=None) - assert_equal_splitlines(out.getvalue(), """\ + out = StringIO() + ascii.write(dat, out, format="fixed_width_no_header", delimiter_pad=None) + assert_equal_splitlines( + out.getvalue(), + """\ |1.2| "hello"|1|a| |2.4|'s worlds|2|2| -""") +""", + ) def test_write_noheader_no_bookend(): """Write a table as a fixed width table with no bookend.""" - out = io.StringIO() - asciitable.write(dat, out, Writer=asciitable.FixedWidthNoHeader, - bookend=False) - assert_equal_splitlines(out.getvalue(), """\ + out = StringIO() + ascii.write(dat, out, format="fixed_width_no_header", bookend=False) + assert_equal_splitlines( + out.getvalue(), + """\ 1.2 | "hello" | 1 | a 2.4 | 's worlds | 2 | 2 -""") +""", + ) def test_write_noheader_no_delimiter(): """Write a table as a fixed width table with no delimiter.""" - out = io.StringIO() - asciitable.write(dat, out, Writer=asciitable.FixedWidthNoHeader, bookend=False, - delimiter=None) - assert_equal_splitlines(out.getvalue(), """\ + out = StringIO() + ascii.write(dat, out, format="fixed_width_no_header", bookend=False, delimiter=None) + assert_equal_splitlines( + out.getvalue(), + """\ 1.2 "hello" 1 a 2.4 's worlds 2 2 -""") +""", + ) def test_write_formats(): """Write a table as a fixed width table with no delimiter.""" - out = io.StringIO() - asciitable.write(dat, out, Writer=asciitable.FixedWidth, - formats={'Col1': '%-8.3f', 'Col2': '%-15s'}) - assert_equal_splitlines(out.getvalue(), """\ + out = StringIO() + ascii.write( + dat, + out, + format="fixed_width", + formats={"Col1": "%-8.3f", "Col2": "%-15s"}, + ) + assert_equal_splitlines( + out.getvalue(), + """\ | Col1 | Col2 | Col3 | Col4 | | 1.200 | "hello" | 1 | a | | 2.400 | 's worlds | 2 | 2 | -""") +""", + ) def test_read_twoline_normal(): """Typical fixed format table with two header lines (with some cruft thrown in to test column positioning""" table = """ - Col1 Col2 - ---- --------- - 1.2xx"hello" + Col1 Col2 + ---- --------- + 1.2xx"hello" 2.4 's worlds """ - dat = asciitable.read(table, Reader=asciitable.FixedWidthTwoLine) - assert_equal(dat.dtype.names, ('Col1', 'Col2')) - assert_almost_equal(dat[1][0], 2.4) - assert_equal(dat[0][1], '"hello"') - assert_equal(dat[1][1], "'s worlds") + dat = ascii.read(table, format="fixed_width_two_line") + assert dat.dtype.names == ("Col1", "Col2") + assert_allclose(dat[1][0], 2.4) + assert dat[0][1] == '"hello"' + assert dat[1][1] == "'s worlds" def test_read_twoline_ReST(): """Read restructured text table""" table = """ ======= =========== - Col1 Col2 + Col1 Col2 ======= =========== - 1.2 "hello" + 1.2 "hello" 2.4 's worlds ======= =========== """ - dat = asciitable.read(table, Reader=asciitable.FixedWidthTwoLine, - header_start=1, position_line=2, data_end=-1) - assert_equal(dat.dtype.names, ('Col1', 'Col2')) - assert_almost_equal(dat[1][0], 2.4) - assert_equal(dat[0][1], '"hello"') - assert_equal(dat[1][1], "'s worlds") + dat = ascii.read( + table, + format="fixed_width_two_line", + header_start=1, + position_line=2, + data_end=-1, + ) + assert dat.dtype.names == ("Col1", "Col2") + assert_allclose(dat[1][0], 2.4) + assert dat[0][1] == '"hello"' + assert dat[1][1] == "'s worlds" def test_read_twoline_human(): @@ -360,49 +459,170 @@ def test_read_twoline_human(): | 2.4 | 's worlds| +------+----------+ """ - dat = asciitable.read(table, Reader=asciitable.FixedWidthTwoLine, - delimiter='+', - header_start=1, position_line=0, - data_start=3, data_end=-1) - assert_equal(dat.dtype.names, ('Col1', 'Col2')) - assert_almost_equal(dat[1][0], 2.4) - assert_equal(dat[0][1], '"hello"') - assert_equal(dat[1][1], "'s worlds") + dat = ascii.read( + table, + format="fixed_width_two_line", + delimiter="+", + header_start=1, + position_line=0, + data_start=3, + data_end=-1, + ) + assert dat.dtype.names == ("Col1", "Col2") + assert_allclose(dat[1][0], 2.4) + assert dat[0][1] == '"hello"' + assert dat[1][1] == "'s worlds" + + +def test_read_twoline_fail(): + """Test failure if too many different character are on position line. + + The position line shall consist of only one character in addition to + the delimiter. + """ + table = """ +| Col1 | Col2 | +|------|==========| +| 1.2 | "hello" | +| 2.4 | 's worlds| +""" + with pytest.raises(InconsistentTableError) as excinfo: + ascii.read(table, format="fixed_width_two_line", delimiter="|", guess=False) + assert ( + "Position line should only contain delimiters and one other character" + in str(excinfo.value) + ) + + +def test_read_twoline_wrong_marker(): + """Test failure when position line uses characters prone to ambiguity + + Characters in position line must be part an allowed set because + normal letters or numbers will lead to ambiguous tables. + """ + table = """ +| Col1 | Col2 | +|aaaaaa|aaaaaaaaaa| +| 1.2 | "hello" | +| 2.4 | 's worlds| +""" + with pytest.raises(InconsistentTableError) as excinfo: + ascii.read(table, format="fixed_width_two_line", delimiter="|", guess=False) + assert "Characters in position line must be part" in str(excinfo.value) def test_write_twoline_normal(): """Write a table as a normal fixed width table.""" - out = io.StringIO() - asciitable.write(dat, out, Writer=asciitable.FixedWidthTwoLine) - assert_equal_splitlines(out.getvalue(), """\ + out = StringIO() + ascii.write(dat, out, format="fixed_width_two_line") + assert_equal_splitlines( + out.getvalue(), + """\ Col1 Col2 Col3 Col4 ---- --------- ---- ---- 1.2 "hello" 1 a 2.4 's worlds 2 2 -""") +""", + ) def test_write_twoline_no_pad(): """Write a table as a fixed width table with no padding.""" - out = io.StringIO() - asciitable.write(dat, out, Writer=asciitable.FixedWidthTwoLine, - delimiter_pad=' ', position_char='=') - assert_equal_splitlines(out.getvalue(), """\ + out = StringIO() + ascii.write( + dat, + out, + format="fixed_width_two_line", + delimiter_pad=" ", + position_char="=", + ) + assert_equal_splitlines( + out.getvalue(), + """\ Col1 Col2 Col3 Col4 ==== ========= ==== ==== 1.2 "hello" 1 a 2.4 's worlds 2 2 -""") +""", + ) def test_write_twoline_no_bookend(): """Write a table as a fixed width table with no bookend.""" - out = io.StringIO() - asciitable.write(dat, out, Writer=asciitable.FixedWidthTwoLine, - bookend=True, delimiter='|') - assert_equal_splitlines(out.getvalue(), """\ + out = StringIO() + ascii.write(dat, out, format="fixed_width_two_line", bookend=True, delimiter="|") + assert_equal_splitlines( + out.getvalue(), + """\ |Col1| Col2|Col3|Col4| |----|---------|----|----| | 1.2| "hello"| 1| a| | 2.4|'s worlds| 2| 2| -""") +""", + ) + + +def test_fixedwidthnoheader_splitting(): + """Test fix in #8511 where data_start is being ignored""" + tbl = """\ +AAA y z +1 2 3 +4 5 6 +7 8 9 +""" + names = ["a", "b", "c"] + dat = ascii.read( + tbl, + data_start=1, + data_end=3, + delimiter=" ", + names=names, + format="fixed_width_no_header", + ) + assert dat.colnames == names + assert np.all(dat["a"] == [1, 4]) + assert np.all(dat["b"] == [2, 5]) + assert np.all(dat["c"] == [3, 6]) + + +def test_fixed_width_header_rows(): + tbl = [ + "| int16 | float32 | `_ +to be installed. +""" + +import os +from io import StringIO +from pathlib import Path + +import numpy as np +import pytest + +from astropy.io import ascii +from astropy.io.ascii import core, html +from astropy.table import Table +from astropy.utils.compat.optional_deps import HAS_BLEACH, HAS_BS4 + +from .common import setup_function, teardown_function # noqa: F401 + +if HAS_BS4: + from bs4 import BeautifulSoup, FeatureNotFound + + +@pytest.mark.skipif(not HAS_BS4, reason="requires BeautifulSoup4") +def test_soupstring(): + """ + Test to make sure the class SoupString behaves properly. + """ + + soup = BeautifulSoup( + "

foo

", "html.parser" + ) + soup_str = html.SoupString(soup) + assert isinstance(soup_str, str) + assert isinstance(soup_str, html.SoupString) + assert soup_str == "

foo

" + assert soup_str.soup is soup + + +def test_listwriter(): + """ + Test to make sure the class ListWriter behaves properly. + """ + + lst = [] + writer = html.ListWriter(lst) + + for i in range(5): + writer.write(i) + for ch in "abcde": + writer.write(ch) + + assert lst == [0, 1, 2, 3, 4, "a", "b", "c", "d", "e"] + + +@pytest.mark.skipif(not HAS_BS4, reason="requires BeautifulSoup4") +def test_identify_table(): + """ + Test to make sure that identify_table() returns whether the + given BeautifulSoup tag is the correct table to process. + """ + + # Should return False on non-
tags and None + soup = BeautifulSoup("", "html.parser") + assert html.identify_table(soup, {}, 0) is False + assert html.identify_table(None, {}, 0) is False + + soup = BeautifulSoup( + '
A
B
', + "html.parser", + ).table + assert html.identify_table(soup, {}, 2) is False + assert html.identify_table(soup, {}, 1) is True # Default index of 1 + + # Same tests, but with explicit parameter + assert html.identify_table(soup, {"table_id": 2}, 1) is False + assert html.identify_table(soup, {"table_id": 1}, 1) is True + + # Test identification by string ID + assert html.identify_table(soup, {"table_id": "bar"}, 1) is False + assert html.identify_table(soup, {"table_id": "foo"}, 1) is True + + +@pytest.mark.skipif(not HAS_BS4, reason="requires BeautifulSoup4") +def test_missing_data(): + """ + Test reading a table with missing data + """ + # First with default where blank => '0' + table_in = [ + "", + "", + "", + "", + "
A
1
", + ] + dat = Table.read(table_in, format="ascii.html") + assert dat.masked is False + assert np.all(dat["A"].mask == [True, False]) + assert dat["A"].dtype.kind == "i" + + # Now with a specific value '...' => missing + table_in = [ + "", + "", + "", + "", + "
A
...
1
", + ] + dat = Table.read(table_in, format="ascii.html", fill_values=[("...", "0")]) + assert dat.masked is False + assert np.all(dat["A"].mask == [True, False]) + assert dat["A"].dtype.kind == "i" + + +@pytest.mark.skipif(not HAS_BS4, reason="requires BeautifulSoup4") +def test_rename_cols(): + """ + Test reading a table and renaming cols + """ + table_in = [ + "", + "", + "", + "
A B
12
", + ] + + # Swap column names + dat = Table.read(table_in, format="ascii.html", names=["B", "A"]) + assert dat.colnames == ["B", "A"] + assert len(dat) == 1 + + # Swap column names and only include A (the renamed version) + dat = Table.read( + table_in, format="ascii.html", names=["B", "A"], include_names=["A"] + ) + assert dat.colnames == ["A"] + assert len(dat) == 1 + assert np.all(dat["A"] == 2) + + +@pytest.mark.skipif(not HAS_BS4, reason="requires BeautifulSoup4") +def test_no_names(): + """ + Test reading a table with no column header + """ + table_in = ["", "", "", "
1
2
"] + dat = Table.read(table_in, format="ascii.html") + assert dat.colnames == ["col1"] + assert len(dat) == 2 + + dat = Table.read(table_in, format="ascii.html", names=["a"]) + assert dat.colnames == ["a"] + assert len(dat) == 2 + + +@pytest.mark.skipif(not HAS_BS4, reason="requires BeautifulSoup4") +def test_identify_table_fail(): + """ + Raise an exception with an informative error message if table_id + is not found. + """ + table_in = ['', "
A
B
"] + + with pytest.raises(core.InconsistentTableError) as err: + Table.read( + table_in, format="ascii.html", htmldict={"table_id": "bad_id"}, guess=False + ) + assert err.match("ERROR: HTML table id 'bad_id' not found$") + + with pytest.raises(core.InconsistentTableError) as err: + Table.read(table_in, format="ascii.html", htmldict={"table_id": 3}, guess=False) + assert err.match("ERROR: HTML table number 3 not found$") + + +@pytest.mark.skipif(not HAS_BS4, reason="requires BeautifulSoup4") +def test_backend_parsers(): + """ + Make sure the user can specify which back-end parser to use + and that an error is raised if the parser is invalid. + """ + for parser in ("lxml", "xml", "html.parser", "html5lib"): + try: + Table.read( + "data/html2.html", + format="ascii.html", + htmldict={"parser": parser}, + guess=False, + ) + except FeatureNotFound: + if parser == "html.parser": + raise + # otherwise ignore if the dependency isn't present + + # reading should fail if the parser is invalid + with pytest.raises(FeatureNotFound): + Table.read( + "data/html2.html", + format="ascii.html", + htmldict={"parser": "foo"}, + guess=False, + ) + + +@pytest.mark.skipif(HAS_BS4, reason="requires no BeautifulSoup4") +def test_htmlinputter_no_bs4(): + """ + This should return an OptionalTableImportError if BeautifulSoup + is not installed. + """ + + inputter = html.HTMLInputter() + with pytest.raises(core.OptionalTableImportError): + inputter.process_lines([]) + + +@pytest.mark.skipif(not HAS_BS4, reason="requires BeautifulSoup4") +def test_htmlinputter(): + """ + Test to ensure that HTMLInputter correctly converts input + into a list of SoupStrings representing table elements. + """ + + f = "data/html.html" + with open(f) as fd: + table = fd.read() + + inputter = html.HTMLInputter() + inputter.html = {} + + # In absence of table_id, defaults to the first table + expected = [ + "
Column 1Column 2Column 3
1a1.05
2b2.75
3c-1.25
Column AColumn BColumn C
4d10.5
5e27.5
6f-12.5
C1C2C3
7g105.0
8h275.0
9i-125.0
Col 1Col 2
", "html.parser" + ).tr + ), + html.SoupString( + BeautifulSoup( + "
Data 1Data 2
", "html.parser" + ).tr + ), + ] + expected_data = [["Col 1", "Col 2"], ["Data 1", "Data 2"]] + assert list(splitter(lines)) == expected_data + + # Make sure the presence of a non-SoupString triggers a TypeError + lines.append("Data 3Data 4") + with pytest.raises(TypeError): + list(splitter(lines)) + + # Make sure that passing an empty list triggers an error + with pytest.raises(core.InconsistentTableError): + list(splitter([])) + + +@pytest.mark.parametrize( + "get_table", + [ + os.fspath, + Path, + lambda path: Path(path).read_text(), + ], +) +@pytest.mark.skipif(not HAS_BS4, reason="requires BeautifulSoup4") +def test_htmlheader_start(get_table): + """ + Test to ensure that the start_line method of HTMLHeader + returns the first line of header data. Uses t/html.html + for sample input. + """ + + table_file = "data/html.html" + table = get_table(table_file) + + inputter = html.HTMLInputter() + inputter.html = {} + header = html.HTMLHeader() + + lines = inputter.get_lines(table) + assert ( + str(lines[header.start_line(lines)]) + == "Column 1Column 2Column 3" + ) + inputter.html["table_id"] = "second" + lines = inputter.get_lines(table) + assert ( + str(lines[header.start_line(lines)]) + == "Column AColumn BColumn C" + ) + inputter.html["table_id"] = 3 + lines = inputter.get_lines(table) + assert ( + str(lines[header.start_line(lines)]) + == "C1C2C3" + ) + + # start_line should return None if no valid header is found + lines = [ + html.SoupString( + BeautifulSoup("
Data
", "html.parser").tr + ), + html.SoupString(BeautifulSoup("

Text

", "html.parser").p), + ] + assert header.start_line(lines) is None + + # Should raise an error if a non-SoupString is present + lines.append("Header") + with pytest.raises(TypeError): + header.start_line(lines) + + +@pytest.mark.skipif(not HAS_BS4, reason="requires BeautifulSoup4") +def test_htmldata(): + """ + Test to ensure that the start_line and end_lines methods + of HTMLData returns the first line of table data. Uses + t/html.html for sample input. + """ + + f = "data/html.html" + with open(f) as fd: + table = fd.read() + + inputter = html.HTMLInputter() + inputter.html = {} + data = html.HTMLData() + + lines = inputter.get_lines(table) + assert ( + str(lines[data.start_line(lines)]) + == "1a1.05" + ) + # end_line returns the index of the last data element + 1 + assert ( + str(lines[data.end_line(lines) - 1]) + == "3c-1.25" + ) + + inputter.html["table_id"] = "second" + lines = inputter.get_lines(table) + assert ( + str(lines[data.start_line(lines)]) + == "4d10.5" + ) + assert ( + str(lines[data.end_line(lines) - 1]) + == "6f-12.5" + ) + + inputter.html["table_id"] = 3 + lines = inputter.get_lines(table) + assert ( + str(lines[data.start_line(lines)]) + == "7g105.0" + ) + assert ( + str(lines[data.end_line(lines) - 1]) + == "9i-125.0" + ) + + # start_line should raise an error if no table data exists + lines = [ + html.SoupString(BeautifulSoup("
", "html.parser").div), + html.SoupString(BeautifulSoup("

Text

", "html.parser").p), + ] + with pytest.raises(core.InconsistentTableError): + data.start_line(lines) + + # end_line should return None if no table data exists + assert data.end_line(lines) is None + + # Should raise an error if a non-SoupString is present + lines.append("Data") + with pytest.raises(TypeError): + data.start_line(lines) + with pytest.raises(TypeError): + data.end_line(lines) + + +def test_multicolumn_write(): + """ + Test to make sure that the HTML writer writes multidimensional + columns (those with iterable elements) using the colspan + attribute of . + """ + + col1 = [1, 2, 3] + col2 = [(1.0, 1.0), (2.0, 2.0), (3.0, 3.0)] + col3 = [("a", "a", "a"), ("b", "b", "b"), ("c", "c", "c")] + table = Table([col1, col2, col3], names=("C1", "C2", "C3")) + expected = """\ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
C1C2C3
11.01.0aaa
22.02.0bbb
33.03.0ccc
+ + + """ + out = html.HTML().write(table)[0].strip() + assert out == expected.strip() + + +@pytest.mark.skipif(not HAS_BLEACH, reason="requires bleach") +def test_multicolumn_write_escape(): + """ + Test to make sure that the HTML writer writes multidimensional + columns (those with iterable elements) using the colspan + attribute of . + """ + + col1 = [1, 2, 3] + col2 = [(1.0, 1.0), (2.0, 2.0), (3.0, 3.0)] + col3 = [("", "", "a"), ("", "b", "b"), ("c", "c", "c")] + table = Table([col1, col2, col3], names=("C1", "C2", "C3")) + expected = """\ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
C1C2C3
11.01.0a
22.02.0bb
33.03.0ccc
+ + + """ + out = html.HTML(htmldict={"raw_html_cols": "C3"}).write(table)[0].strip() + assert out == expected.strip() + + +def test_write_no_multicols(): + """ + Test to make sure that the HTML writer will not use + multi-dimensional columns if the multicol parameter + is False. + """ + + col1 = [1, 2, 3] + col2 = [(1.0, 1.0), (2.0, 2.0), (3.0, 3.0)] + col3 = [("a", "a", "a"), ("b", "b", "b"), ("c", "c", "c")] + table = Table([col1, col2, col3], names=("C1", "C2", "C3")) + expected = """\ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
C1C2C3
11.0 .. 1.0a .. a
22.0 .. 2.0b .. b
33.0 .. 3.0c .. c
+ + + """ + assert html.HTML({"multicol": False}).write(table)[0].strip() == expected.strip() + + +@pytest.mark.skipif(not HAS_BS4, reason="requires BeautifulSoup4") +def test_multicolumn_read(): + """ + Test to make sure that the HTML reader inputs multidimensional + columns (those with iterable elements) using the colspan + attribute of . + + Ensure that any string element within a multidimensional column + casts all elements to string prior to type conversion operations. + """ + + table = Table.read("data/html2.html", format="ascii.html") + str_type = np.dtype((str, 21)) + expected = Table( + np.array( + [(["1", "2.5000000000000000001"], 3), (["1a", "1"], 3.5)], + dtype=[("A", str_type, (2,)), ("B", "x"], ["y"]], names=["a", "b"]) + + # One column contains raw HTML (string input) + out = StringIO() + t.write(out, format="ascii.html", htmldict={"raw_html_cols": "a"}) + expected = """\ + + x + <em>y</em> + """ + assert expected in out.getvalue() + + # One column contains raw HTML (list input) + out = StringIO() + t.write(out, format="ascii.html", htmldict={"raw_html_cols": ["a"]}) + assert expected in out.getvalue() + + # Two columns contains raw HTML (list input) + out = StringIO() + t.write(out, format="ascii.html", htmldict={"raw_html_cols": ["a", "b"]}) + expected = """\ + + x + y + """ + assert expected in out.getvalue() + + +@pytest.mark.skipif(not HAS_BLEACH, reason="requires bleach") +def test_raw_html_write_clean(): + """ + Test that columns can contain raw HTML which is not escaped. + """ + import bleach + + t = Table( + [[""], ["

y

"], ["y"]], names=["a", "b", "c"] + ) + + # Confirm that +""" % dict( # noqa: UP031 + sorting_script1=_SORTING_SCRIPT_PART_1, sorting_script2=_SORTING_SCRIPT_PART_2 +) + +HTML_JS_SCRIPT = ( + _SORTING_SCRIPT_PART_1 + + _SORTING_SCRIPT_PART_2 + + """ +$(document).ready(function() {{ + $('#{tid}').dataTable({{ + order: [], + pageLength: {display_length}, + lengthMenu: {display_length_menu}, + pagingType: "full_numbers", + columnDefs: [{{targets: {sort_columns}, type: "optionalnum"}}] + }}); +}} ); +""" +) + + +# Default CSS for the JSViewer writer +DEFAULT_CSS = """\ +body {font-family: sans-serif;} +table.dataTable {width: auto !important; margin: 0 !important;} +.dataTables_filter, .dataTables_paginate {float: left !important; margin-left:1em} +""" + + +# Default CSS used when rendering a table in the IPython notebook +DEFAULT_CSS_NB = """\ +table.dataTable {clear: both; width: auto !important; margin: 0 !important;} +.dataTables_info, .dataTables_length, .dataTables_filter, .dataTables_paginate{ +display: inline-block; margin-right: 1em; } +.paginate_button { margin-right: 5px; } +""" + + +class JSViewer: + """Provides an interactive HTML export of a Table. + + This class provides an interface to the `DataTables + `_ library, which allow to visualize interactively + an HTML table. It is used by the `~astropy.table.Table.show_in_browser` + method. + + Parameters + ---------- + use_local_files : bool, optional + Use local files or a CDN for JavaScript libraries. Default False. + display_length : int, optional + Number or rows to show. Default to 50. + + """ + + def __init__(self, use_local_files=False, display_length=50): + if use_local_files: + warn( + "`use_local_files` is deprecated and has no effect; for security reasons no static versions of the required js libraries are included in astropy.", + DeprecationWarning, + ) + self.display_length_menu = [ + [10, 25, 50, 100, 500, 1000, -1], + [10, 25, 50, 100, 500, 1000, "All"], + ] + self.display_length = display_length + for L in self.display_length_menu: + if display_length not in L: + L.insert(0, display_length) + + @property + def jquery_urls(self): + return [conf.jquery_url, conf.datatables_url] + + @property + def css_urls(self): + return conf.css_urls + + def _jstable_file(self): + return conf.datatables_url[:-3] + + def ipynb(self, table_id, css=None, sort_columns="[]"): + html = f"" + html += IPYNB_JS_SCRIPT.format( + display_length=self.display_length, + display_length_menu=self.display_length_menu, + datatables_url=self._jstable_file(), + tid=table_id, + sort_columns=sort_columns, + ) + return html + + def html_js(self, table_id="table0", sort_columns="[]"): + return HTML_JS_SCRIPT.format( + display_length=self.display_length, + display_length_menu=self.display_length_menu, + tid=table_id, + sort_columns=sort_columns, + ).strip() + + +def write_table_jsviewer( + table, + filename, + table_id=None, + max_lines=5000, + table_class="display compact", + jskwargs=None, + css=DEFAULT_CSS, + htmldict=None, + overwrite=False, +): + """ + Write an Astropy Table to an HTML file with JavaScript viewer. + + This function uses the JSViewer class to generate the necessary JavaScript + and CSS for displaying the table interactively in a web browser. + + Parameters + ---------- + table : Table + The Astropy Table to be written to an HTML file. + filename : str, Path + The name of the output HTML file. + table_id : str, optional + The HTML id attribute for the table. Defaults to ``f"table({id(table)}"``. + max_lines : int, optional + The maximum number of lines to include in the output table. Default is 5000. + table_class : str, optional + The CSS class for the table. Default is "display compact". + jskwargs : dict, optional + Additional keyword arguments to pass to the JSViewer. + css : str, optional + CSS styles to include in the HTML file. Default is `DEFAULT_CSS`. + htmldict : dict, optional + Additional HTML options passed to :class:`~astropy.io.ascii.HTML`. + overwrite : bool, optional + If True, overwrite the output file if it exists. Default is False. + + Returns + ------- + None + """ + if table_id is None: + table_id = f"table{id(table)}" + + jskwargs = jskwargs or {} + jsv = JSViewer(**jskwargs) + + sortable_columns = [ + i + for i, col in enumerate(table.columns.values()) + if col.info.dtype.kind in "iufc" + ] + html_options = { + "table_id": table_id, + "table_class": table_class, + "css": css, + "cssfiles": jsv.css_urls, + "jsfiles": jsv.jquery_urls, + "js": jsv.html_js(table_id=table_id, sort_columns=sortable_columns), + } + if htmldict: + html_options.update(htmldict) + + if max_lines < len(table): + table = table[:max_lines] + table.write(filename, format="html", htmldict=html_options, overwrite=overwrite) + + +io_registry.register_writer("jsviewer", Table, write_table_jsviewer) diff --git a/astropy/table/meta.py b/astropy/table/meta.py new file mode 100644 index 000000000000..f6d7b553835d --- /dev/null +++ b/astropy/table/meta.py @@ -0,0 +1,421 @@ +import copy +import json +import textwrap +from collections import OrderedDict + +import numpy as np +import yaml + +__all__ = ["get_header_from_yaml", "get_yaml_from_header", "get_yaml_from_table"] + + +class ColumnOrderList(list): + """ + List of tuples that sorts in a specific order that makes sense for + astropy table column attributes. + """ + + def sort(self, *args, **kwargs): + super().sort() + + column_keys = ["name", "unit", "datatype", "format", "description", "meta"] + in_dict = dict(self) + out_list = [] + + for key in column_keys: + if key in in_dict: + out_list.append((key, in_dict[key])) + for key, val in self: + if key not in column_keys: + out_list.append((key, val)) + + # Clear list in-place + del self[:] + + self.extend(out_list) + + +class ColumnDict(dict): + """ + Specialized dict subclass to represent attributes of a Column + and return items() in a preferred order. This is only for use + in generating a YAML map representation that has a fixed order. + """ + + def items(self): + """ + Return items as a ColumnOrderList, which sorts in the preferred + way for column attributes. + """ + return ColumnOrderList(super().items()) + + +def _construct_odict(load, node): + """ + Construct dict from !!omap in yaml safe load. + + See ``get_header_from_yaml()`` for usage. + + Source: https://gist.github.com/weaver/317164 + License: Unspecified + + This is the same as SafeConstructor.construct_yaml_omap(), + except the data type is changed to OrderedDict() and setitem is + used instead of append in the loop + """ + omap = {} + yield omap + if not isinstance(node, yaml.SequenceNode): + raise yaml.constructor.ConstructorError( + "while constructing an ordered map", + node.start_mark, + f"expected a sequence, but found {node.id}", + node.start_mark, + ) + + for subnode in node.value: + if not isinstance(subnode, yaml.MappingNode): + raise yaml.constructor.ConstructorError( + "while constructing an ordered map", + node.start_mark, + f"expected a mapping of length 1, but found {subnode.id}", + subnode.start_mark, + ) + + if len(subnode.value) != 1: + raise yaml.constructor.ConstructorError( + "while constructing an ordered map", + node.start_mark, + f"expected a single mapping item, but found {len(subnode.value)} items", + subnode.start_mark, + ) + + key_node, value_node = subnode.value[0] + key = load.construct_object(key_node) + value = load.construct_object(value_node) + omap[key] = value + + +def _repr_pairs(dump, tag, sequence, flow_style=None): + """ + This is the same code as BaseRepresenter.represent_sequence(), + but the value passed to dump.represent_data() in the loop is a + dictionary instead of a tuple. + + Source: https://gist.github.com/weaver/317164 + License: Unspecified + """ + value = [] + node = yaml.SequenceNode(tag, value, flow_style=flow_style) + if dump.alias_key is not None: + dump.represented_objects[dump.alias_key] = node + best_style = True + for key, val in sequence: + item = dump.represent_data({key: val}) + if not (isinstance(item, yaml.ScalarNode) and not item.style): + best_style = False + value.append(item) + if flow_style is None: + if dump.default_flow_style is not None: + node.flow_style = dump.default_flow_style + else: + node.flow_style = best_style + return node + + +def _repr_odict(dumper, data): + """ + Represent OrderedDict in yaml dump. + + Source: https://gist.github.com/weaver/317164 + License: Unspecified + + >>> data = OrderedDict([('foo', 'bar'), ('mumble', 'quux'), ('baz', 'gorp')]) + >>> yaml.dump(data, default_flow_style=False) # doctest: +SKIP + '!!omap\\n- foo: bar\\n- mumble: quux\\n- baz: gorp\\n' + >>> yaml.dump(data, default_flow_style=True) # doctest: +SKIP + '!!omap [foo: bar, mumble: quux, baz: gorp]\\n' + """ + return _repr_pairs(dumper, "tag:yaml.org,2002:omap", data.items()) + + +def _repr_column_dict(dumper, data): + """ + Represent ColumnDict in yaml dump. + + This is the same as an ordinary mapping except that the keys + are written in a fixed order that makes sense for astropy table + columns. + """ + return dumper.represent_mapping("tag:yaml.org,2002:map", data) + + +def _get_variable_length_array_shape(col): + """Check if object-type ``col`` is really a variable length list. + + That is true if the object consists purely of list of nested lists, where + the shape of every item can be represented as (m, n, ..., *) where the (m, + n, ...) are constant and only the lists in the last axis have variable + shape. If so the returned value of shape will be a tuple in the form (m, n, + ..., None). + + If ``col`` is a variable length array then the return ``dtype`` corresponds + to the type found by numpy for all the individual values. Otherwise it will + be ``np.dtype(object)``. + + Parameters + ---------- + col : column-like + Input table column, assumed to be object-type + + Returns + ------- + shape : tuple + Inferred variable length shape or None + dtype : np.dtype + Numpy dtype that applies to col + """ + + class ConvertError(ValueError): + """Local conversion error used below.""" + + # Numpy types supported as variable-length arrays + np_classes = (np.floating, np.integer, np.bool_, np.str_) + + try: + if len(col) == 0 or not all(isinstance(val, np.ndarray) for val in col): + raise ConvertError + dtype = col[0].dtype + shape = col[0].shape[:-1] + for val in col: + if not issubclass(val.dtype.type, np_classes) or val.shape[:-1] != shape: + raise ConvertError + dtype = np.promote_types(dtype, val.dtype) + shape = shape + (None,) + + except ConvertError: + # `col` is not a variable length array, return shape and dtype to + # the original. Note that this function is only called if + # col.shape[1:] was () and col.info.dtype is object. + dtype = col.info.dtype + shape = () + + return shape, dtype + + +def _get_datatype_from_dtype(dtype): + """Return string version of ``dtype`` for writing to ECSV ``datatype``.""" + datatype = dtype.name + if datatype.startswith(("bytes", "str")): + datatype = "string" + datatype = datatype.removesuffix("_") # string_ and bool_ lose the final _ for ECSV + return datatype + + +def _get_col_attributes(col): + """ + Extract information from a column (apart from the values) that is required + to fully serialize the column. + + Parameters + ---------- + col : column-like + Input Table column + + Returns + ------- + attrs : dict + Dict of ECSV attributes for ``col`` + """ + dtype = col.info.dtype # Type of column values that get written + subtype = None # Type of data for object columns serialized with JSON + shape = col.shape[1:] # Shape of multidim / variable length columns + + if dtype.name == "object": + if shape == (): + # 1-d object type column might be a variable length array + dtype = np.dtype(str) + shape, subtype = _get_variable_length_array_shape(col) + else: + # N-d object column is subtype object but serialized as JSON string + dtype = np.dtype(str) + subtype = np.dtype(object) + elif shape: + # N-d column which is not object is serialized as JSON string + dtype = np.dtype(str) + subtype = col.info.dtype + + datatype = _get_datatype_from_dtype(dtype) + + # Set the output attributes + attrs = ColumnDict() + attrs["name"] = col.info.name + attrs["datatype"] = datatype + for attr, nontrivial, xform in ( + ("unit", lambda x: x is not None, str), + ("format", lambda x: x is not None, None), + ("description", lambda x: x is not None, None), + ("meta", lambda x: x, OrderedDict), + ): + col_attr = getattr(col.info, attr) + if nontrivial(col_attr): + attrs[attr] = xform(col_attr) if xform else col_attr + + if subtype: + attrs["subtype"] = _get_datatype_from_dtype(subtype) + # Numpy 'object' maps to 'subtype' of 'json' in ECSV + if attrs["subtype"] == "object": + attrs["subtype"] = "json" + if shape: + attrs["subtype"] += json.dumps(list(shape), separators=(",", ":")) + + return attrs + + +def get_yaml_from_table(table): + """ + Return lines with a YAML representation of header content from the ``table``. + + Parameters + ---------- + table : `~astropy.table.Table` object + Table for which header content is output + + Returns + ------- + lines : list + List of text lines with YAML header content + """ + header = {"cols": list(table.columns.values())} + if table.meta: + header["meta"] = OrderedDict(table.meta) + + return get_yaml_from_header(header) + + +def get_yaml_from_header(header): + """ + Return lines with a YAML representation of header content from a Table. + + The ``header`` dict must contain these keys: + + - 'cols' : list of table column objects (required) + - 'meta' : table 'meta' attribute (optional) + + Other keys included in ``header`` will be serialized in the output YAML + representation. + + Parameters + ---------- + header : dict + Table header content + + Returns + ------- + lines : list + List of text lines with YAML header content + """ + from astropy.io.misc.yaml import AstropyDumper + + class TableDumper(AstropyDumper): + """ + Custom Dumper that represents OrderedDict as an !!omap object. + """ + + def represent_mapping(self, tag, mapping, flow_style=None): + """ + This is a combination of the Python 2 and 3 versions of this method + in the PyYAML library to allow the required key ordering via the + ColumnOrderList object. The Python 3 version insists on turning the + items() mapping into a list object and sorting, which results in + alphabetical order for the column keys. + """ + value = [] + node = yaml.MappingNode(tag, value, flow_style=flow_style) + if self.alias_key is not None: + self.represented_objects[self.alias_key] = node + best_style = True + if hasattr(mapping, "items"): + mapping = mapping.items() + if hasattr(mapping, "sort"): + mapping.sort() + else: + mapping = list(mapping) + try: + mapping = sorted(mapping) + except TypeError: + pass + + for item_key, item_value in mapping: + node_key = self.represent_data(item_key) + node_value = self.represent_data(item_value) + if not (isinstance(node_key, yaml.ScalarNode) and not node_key.style): + best_style = False + if not ( + isinstance(node_value, yaml.ScalarNode) and not node_value.style + ): + best_style = False + value.append((node_key, node_value)) + if flow_style is None: + if self.default_flow_style is not None: + node.flow_style = self.default_flow_style + else: + node.flow_style = best_style + return node + + TableDumper.add_representer(OrderedDict, _repr_odict) + TableDumper.add_representer(ColumnDict, _repr_column_dict) + + header = copy.copy(header) # Don't overwrite original + header["datatype"] = [_get_col_attributes(col) for col in header["cols"]] + del header["cols"] + + lines = yaml.dump( + header, default_flow_style=None, Dumper=TableDumper, width=130 + ).splitlines() + return lines + + +class YamlParseError(Exception): + pass + + +def get_header_from_yaml(lines): + """ + Get a header dict from input ``lines`` which should be valid YAML. This + input will typically be created by get_yaml_from_header. The output is a + dictionary which describes all the table and column meta. + + The get_cols() method in the io/ascii/ecsv.py file should be used as a + guide to using the information when constructing a table using this + header dict information. + + Parameters + ---------- + lines : list + List of text lines with YAML header content + + Returns + ------- + header : dict + Dictionary describing table and column meta + + """ + from astropy.io.misc.yaml import AstropyLoader + + class TableLoader(AstropyLoader): + """ + Custom Loader that constructs OrderedDict from an !!omap object. + This does nothing but provide a namespace for adding the + custom odict constructor. + """ + + TableLoader.add_constructor("tag:yaml.org,2002:omap", _construct_odict) + # Now actually load the YAML data structure into `meta` + header_yaml = textwrap.dedent("\n".join(lines)) + try: + header = yaml.load(header_yaml, Loader=TableLoader) + except Exception as err: + raise YamlParseError() from err + + return header diff --git a/astropy/table/mixins/__init__.py b/astropy/table/mixins/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/astropy/table/mixins/dask.py b/astropy/table/mixins/dask.py new file mode 100644 index 000000000000..d905daa37bf3 --- /dev/null +++ b/astropy/table/mixins/dask.py @@ -0,0 +1,37 @@ +import dask.array as da + +from astropy.utils.data_info import ParentDtypeInfo + +__all__ = ["as_dask_column"] + + +class DaskInfo(ParentDtypeInfo): + @staticmethod + def default_format(val): + return f"{val.compute()}" + + +class DaskColumn(da.Array): + info = DaskInfo() + + def copy(self): + # Array hard-codes the resulting copied array as Array, so need to + # overload this since Table tries to copy the array. + return as_dask_column(self, info=self.info) + + def __getitem__(self, item): + result = super().__getitem__(item) + if isinstance(item, int): + return result + else: + return as_dask_column(result, info=self.info) + + def insert(self, obj, values, axis=0): + return as_dask_column(da.insert(self, obj, values, axis=axis), info=self.info) + + +def as_dask_column(array, info=None): + result = DaskColumn(array.dask, array.name, array.chunks, meta=array) + if info is not None: + result.info = info + return result diff --git a/astropy/table/mixins/registry.py b/astropy/table/mixins/registry.py new file mode 100644 index 000000000000..71b99fd63657 --- /dev/null +++ b/astropy/table/mixins/registry.py @@ -0,0 +1,80 @@ +# This module handles the definition of mixin 'handlers' which are functions +# that given an arbitrary object (e.g. a dask array) will return an object that +# can be used as a mixin column. This is useful because it means that users can +# then add objects to tables that are not formally mixin columns and where +# adding an info attribute is beyond our control. + +__all__ = ["MixinRegistryError", "get_mixin_handler", "register_mixin_handler"] + +# The internal dictionary of handlers maps fully qualified names of classes +# to a function that can take an object and return a mixin-compatible object. +_handlers = {} + + +class MixinRegistryError(Exception): + pass + + +def register_mixin_handler(fully_qualified_name, handler, force=False): + """ + Register a mixin column 'handler'. + + A mixin column handler is a function that given an arbitrary Python object, + will return an object with the .info attribute that can then be used as a + mixin column (this can be e.g. a copy of the object with a new attribute, + a subclass instance, or a wrapper class - this is left up to the handler). + + The handler will be used on classes that have an exactly matching fully + qualified name. + + Parameters + ---------- + fully_qualified_name : str + The fully qualified name of the class that the handler can operate on, + such as e.g. ``dask.array.core.Array``. + handler : func + The handler function. + force : bool, optional + Whether to overwrite any previous handler if there is already one for + the same fully qualified name. + """ + if fully_qualified_name not in _handlers or force: + _handlers[fully_qualified_name] = handler + else: + raise MixinRegistryError( + f"Handler for class {fully_qualified_name} is already defined" + ) + + +def get_mixin_handler(obj): + """ + Given an arbitrary object, return the matching mixin handler (if any). + + Parameters + ---------- + obj : object or str + The object to find a mixin handler for, or a fully qualified name. + + Returns + ------- + handler : None or func + Then matching handler, if found, or `None` + """ + if isinstance(obj, str): + return _handlers.get(obj) + else: + return _handlers.get(obj.__class__.__module__ + "." + obj.__class__.__name__) + + +# Add built-in handlers to registry. Note that any third-party package imports +# required by the handlers should go inside the handler function to delay +# the imports until they are actually needed. + + +def dask_handler(arr): + from astropy.table.mixins.dask import as_dask_column + + return as_dask_column(arr) + + +register_mixin_handler("dask.array.core.Array", dask_handler) diff --git a/astropy/table/mixins/tests/__init__.py b/astropy/table/mixins/tests/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/astropy/table/mixins/tests/test_dask.py b/astropy/table/mixins/tests/test_dask.py new file mode 100644 index 000000000000..fda88c4e9df7 --- /dev/null +++ b/astropy/table/mixins/tests/test_dask.py @@ -0,0 +1,70 @@ +import numpy as np +import pytest +from numpy.testing import assert_equal + +from astropy.table import Table + +da = pytest.importorskip("dask.array") + + +class TestDaskHandler: + def setup_method(self, method): + self.t = Table() + self.t["a"] = da.arange(10) + + def test_add_row(self): + self.t.add_row(self.t[0]) + assert_equal(self.t["a"].compute(), np.hstack([np.arange(10), 0])) + + def test_get_column(self): + assert isinstance(self.t["a"], da.Array) + assert_equal(self.t["a"].compute(), np.arange(10)) + + def test_slicing_row_single(self): + sub = self.t[5] + assert isinstance(sub["a"], da.Array) + assert not hasattr(sub["a"], "info") # should be a plain dask array + assert sub["a"].compute() == 5 + + def test_slicing_row_range(self): + sub = self.t[5:] + assert isinstance(sub["a"], da.Array) + assert hasattr(sub["a"], "info") # should be a mixin column + assert_equal(sub["a"].compute(), np.arange(5, 10)) + + def test_slicing_column_range(self): + sub = self.t[("a",)] + assert isinstance(sub["a"], da.Array) + assert hasattr(sub["a"], "info") # should be a mixin column + assert_equal(sub["a"].compute(), np.arange(10)) + + def test_pformat(self): + assert self.t.pformat() == [ + " a ", + "---", + " 0", + " 1", + " 2", + " 3", + " 4", + " 5", + " 6", + " 7", + " 8", + " 9", + ] + + def test_info_preserved(self): + self.t["a"].info.description = "A dask column" + + sub = self.t[1:3] + assert sub["a"].info.name == "a" + assert sub["a"].info.description == "A dask column" + + col = self.t["a"].copy() + assert col.info.name == "a" + assert col.info.description == "A dask column" + + self.t.add_row(self.t[0]) + assert self.t["a"].info.name == "a" + assert self.t["a"].info.description == "A dask column" diff --git a/astropy/table/mixins/tests/test_registry.py b/astropy/table/mixins/tests/test_registry.py new file mode 100644 index 000000000000..b4abc2c42022 --- /dev/null +++ b/astropy/table/mixins/tests/test_registry.py @@ -0,0 +1,124 @@ +from copy import copy + +import pytest +from numpy.testing import assert_equal + +from astropy.table import Column, Table +from astropy.table.mixins.registry import ( + MixinRegistryError, + _handlers, + get_mixin_handler, + register_mixin_handler, +) +from astropy.table.table_helpers import ArrayWrapper + +ORIGINAL = {} + + +def setup_function(function): + ORIGINAL["handlers"] = copy(_handlers) + _handlers.clear() + + +def teardown_function(function): + _handlers.clear() + _handlers.update(ORIGINAL["handlers"]) + + +class SpamData: + pass + + +class SpamWrapper(ArrayWrapper): + def __init__(self): + super().__init__([0, 1, 3, 4, 5]) + + +FULL_QUALNAME = "astropy.table.mixins.tests.test_registry.SpamData" + + +def handle_spam(obj): + return SpamWrapper() + + +def handle_spam_alt(obj): + return SpamWrapper() + + +def test_no_handler(): + data = SpamData() + assert get_mixin_handler(data) is None + + +def test_register_handler(): + register_mixin_handler(FULL_QUALNAME, handle_spam) + assert get_mixin_handler(SpamData()) is handle_spam + + +def test_register_handler_override(): + register_mixin_handler(FULL_QUALNAME, handle_spam) + with pytest.raises(MixinRegistryError) as exc: + register_mixin_handler(FULL_QUALNAME, handle_spam_alt) + assert ( + exc.value.args[0] + == "Handler for class astropy.table.mixins.tests.test_registry.SpamData is" + " already defined" + ) + register_mixin_handler(FULL_QUALNAME, handle_spam_alt, force=True) + assert get_mixin_handler(SpamData()) is handle_spam_alt + + +def test_get_mixin_handler_str(): + # Check that we can also pass a fully qualified name to get_mixin_handler + register_mixin_handler(FULL_QUALNAME, handle_spam) + assert get_mixin_handler(FULL_QUALNAME) is handle_spam + + +def test_add_column_to_empty_table(): + t = Table() + t["a"] = SpamData() + # By default, we get an object column. + assert isinstance(t["a"], Column) + assert t["a"].dtype == object + # But after registration, we can get the intended mixin. + register_mixin_handler(FULL_QUALNAME, handle_spam) + t = Table() + t["a"] = SpamData() + + assert len(t) == 5 + assert isinstance(t["a"], SpamWrapper) + assert_equal(t["a"].data, [0, 1, 3, 4, 5]) + + +def test_add_column_to_existing_table(): + # As above, but for a table that already has a column + # (addition used to depend on whether or a table was empty; gh-17102). + t = Table([[5, 6, 7, 8, 9]], names=["x"]) + t["a"] = SpamData() + assert isinstance(t["a"], Column) + assert t["a"].dtype == object + + register_mixin_handler(FULL_QUALNAME, handle_spam) + + t["a"] = SpamData() + + assert len(t) == 5 + assert isinstance(t["a"], SpamWrapper) + assert_equal(t["a"].data, [0, 1, 3, 4, 5]) + + +def invalid_handler(obj): + return "invalid" + + +def test_invalid_handler(): + t = Table() + + register_mixin_handler(FULL_QUALNAME, invalid_handler) + + with pytest.raises(TypeError) as exc: + t["a"] = SpamData() + assert ( + exc.value.args[0] == f"Mixin handler for object of type {FULL_QUALNAME} " + "did not return a valid mixin column" + ) diff --git a/astropy/table/ndarray_mixin.py b/astropy/table/ndarray_mixin.py new file mode 100644 index 000000000000..0084c90ae0a6 --- /dev/null +++ b/astropy/table/ndarray_mixin.py @@ -0,0 +1,65 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +import numpy as np + +from astropy.utils.data_info import ParentDtypeInfo + + +class NdarrayMixinInfo(ParentDtypeInfo): + _represent_as_dict_primary_data = "data" + + def _represent_as_dict(self): + """Represent Column as a dict that can be serialized.""" + col = self._parent + out = {"data": col.view(np.ndarray)} + return out + + def _construct_from_dict(self, map): + """Construct Column from ``map``.""" + data = map.pop("data") + out = self._parent_cls(data, **map) + return out + + +class NdarrayMixin(np.ndarray): + """ + Mixin column class to allow storage of arbitrary numpy + ndarrays within a Table. This is a subclass of numpy.ndarray + and has the same initialization options as ``np.array()``. + """ + + info = NdarrayMixinInfo() + + def __new__(cls, obj, *args, **kwargs): + self = np.array(obj, *args, **kwargs).view(cls) + if "info" in getattr(obj, "__dict__", ()): + self.info = obj.info + return self + + def __array_finalize__(self, obj): + if obj is None: + return + + super().__array_finalize__(obj) + + # Self was created from template (e.g. obj[slice] or (obj * 2)) + # or viewcast e.g. obj.view(Column). In either case we want to + # init Column attributes for self from obj if possible. + if "info" in getattr(obj, "__dict__", ()): + self.info = obj.info + + def __reduce__(self): + # patch to pickle NdArrayMixin objects (ndarray subclasses), see + # http://www.mail-archive.com/numpy-discussion@scipy.org/msg02446.html + + object_state = list(super().__reduce__()) + object_state[2] = (object_state[2], self.__dict__) + return tuple(object_state) + + def __setstate__(self, state): + # patch to unpickle NdarrayMixin objects (ndarray subclasses), see + # http://www.mail-archive.com/numpy-discussion@scipy.org/msg02446.html + + nd_state, own_state = state + super().__setstate__(nd_state) + self.__dict__.update(own_state) diff --git a/astropy/table/notebook_backends.py b/astropy/table/notebook_backends.py new file mode 100644 index 000000000000..855c37acd43b --- /dev/null +++ b/astropy/table/notebook_backends.py @@ -0,0 +1,131 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +"""Backend implementations for `~astropy.table.Table` interface +with Jupyter notebooks. + +""" + +__all__ = ["classic", "ipydatagrid"] + + +# NOTE: The actual deprecation warning is emitted in show_in_notebook +# method in table.py module. +def classic( + table, + tableid=None, + css=None, + display_length=50, + table_class="astropy-default", + show_row_index="idx", +): + """Render the table in HTML and show it in the Jupyter notebook. + + .. deprecated:: 6.1 + Use :func:`ipydatagrid` instead. + + Parameters + ---------- + table : `~astropy.table.Table` + Table to render. + tableid : str or None + An html ID tag for the table. Default is ``table{id}-XXX``, where + id is the unique integer id of the table object, id(table), and XXX + is a random number to avoid conflicts when printing the same table + multiple times. + table_class : str or None + A string with a list of HTML classes used to style the table. + The special default string ('astropy-default') means that the string + will be retrieved from the configuration item + ``astropy.table.default_notebook_table_class``. Note that these + table classes may make use of bootstrap, as this is loaded with the + notebook. See `this page `_ + for the list of classes. + css : str + A valid CSS string declaring the formatting for the table. Defaults + to ``astropy.table.jsviewer.DEFAULT_CSS_NB``. + display_length : int, optional + Number or rows to show. Defaults to 50. + show_row_index : str or False + If this does not evaluate to False, a column with the given name + will be added to the version of the table that gets displayed. + This new column shows the index of the row in the table itself, + even when the displayed table is re-sorted by another column. Note + that if a column with this name already exists, this option will be + ignored. Defaults to "idx". + + Returns + ------- + html : object + An ``IPython.display.HTML`` instance representing the given table. + + Notes + ----- + Currently, unlike :meth:`~astropy.table.Table.show_in_browser` + (with ``jsviewer=True``), this + method needs to access online Javascript code repositories. This is due + to modern browsers' limitations on accessing local files. Hence, if you + call this method while offline (and don't have a cached version of + jquery and jquery.dataTables), you will not get the jsviewer features. + + """ + import numpy as np + from IPython.display import HTML + + from . import conf + from .jsviewer import JSViewer + + if tableid is None: + tableid = f"table{id(table)}-{np.random.randint(1, 1e6)}" + + jsv = JSViewer(display_length=display_length) + if show_row_index: + display_table = table._make_index_row_display_table(show_row_index) + else: + display_table = table + if table_class == "astropy-default": + table_class = conf.default_notebook_table_class + html = display_table._base_repr_( + html=True, + max_width=-1, + tableid=tableid, + max_lines=-1, + show_dtype=False, + tableclass=table_class, + ) + + columns = display_table.columns.values() + sortable_columns = [ + i for i, col in enumerate(columns) if col.info.dtype.kind in "iufc" + ] + html += jsv.ipynb(tableid, css=css, sort_columns=sortable_columns) + return HTML(html) + + +def ipydatagrid(table, **kwargs): + """Render the table in HTML with ``ipydatagrid`` and show it in + the Jupyter notebook. + + This function creates an ``ipydatagrid.DataGrid`` object by converting the input + ``table`` to a ``pandas.DataFrame`` and passing ``**kwargs`` to the constructor. + The available ``DataGrid`` options can be seen in a Jupyter notebook with + ``help(ipydatagrid.DataGrid)``. + + .. note:: + This function requires optional dependencies ``pandas`` and ``ipydatagrid``. + + Parameters + ---------- + table : `~astropy.table.Table` + Table to render. + + **kwargs : dict, optional + Keyword arguments accepted by ``ipydatagrid.DataGrid``. + + Returns + ------- + dg : object + An ``ipydatagrid.DataGrid`` instance representing the given table. + + """ + from ipydatagrid import DataGrid + + return DataGrid(table.to_pandas(), **kwargs) diff --git a/astropy/table/operations.py b/astropy/table/operations.py new file mode 100644 index 000000000000..f1b71da10fb6 --- /dev/null +++ b/astropy/table/operations.py @@ -0,0 +1,1631 @@ +"""High-level table operations. + +- join() +- setdiff() +- hstack() +- vstack() +- dstack() +""" +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +import collections +import itertools +import warnings +from collections import Counter, OrderedDict +from collections.abc import Sequence +from copy import deepcopy + +import numpy as np + +from astropy.units import Quantity +from astropy.utils import metadata +from astropy.utils.compat.optional_deps import HAS_SCIPY +from astropy.utils.masked import Masked + +from . import _np_utils +from .table import Column, MaskedColumn, QTable, Row, Table + +__all__ = [ + "hstack", + "join", + "join_distance", + "join_skycoord", + "setdiff", + "unique", + "vstack", +] + +__doctest_requires__ = {"join_skycoord": ["scipy"], "join_distance": ["scipy"]} + + +class TableMergeError(ValueError): + pass + + +def _merge_table_meta(out, tables, metadata_conflicts="warn"): + out_meta = deepcopy(tables[0].meta) + for table in tables[1:]: + out_meta = metadata.merge( + out_meta, table.meta, metadata_conflicts=metadata_conflicts + ) + out.meta.update(out_meta) + + +def _get_list_of_tables(tables): + """ + Check that tables is a Table or sequence of Tables. Returns the + corresponding list of Tables. + """ + # Make sure we have a list of things + if not isinstance(tables, Sequence): + tables = [tables] + + # Make sure there is something to stack + if len(tables) == 0: + raise ValueError("no values provided to stack.") + + # Convert inputs (Table, Row, or anything column-like) to Tables. + # Special case that Quantity converts to a QTable. + # Do this in a separate list to not modify the original input list + tables = list(tables) + for ii, val in enumerate(tables): + if isinstance(val, Table): + pass + elif isinstance(val, Row): + tables[ii] = Table(val) + elif isinstance(val, Quantity): + tables[ii] = QTable([val]) + else: + try: + tables[ii] = Table([val]) + except (ValueError, TypeError) as err: + raise TypeError(f"Cannot convert {val} to table column.") from err + + return tables + + +def _get_out_class(objs): + """ + From a list of input objects ``objs`` get merged output object class. + + This is just taken as the deepest subclass. This doesn't handle complicated + inheritance schemes, but as a special case, classes which share ``info`` + are taken to be compatible. + """ + out_class = objs[0].__class__ + for obj in objs[1:]: + if issubclass(obj.__class__, out_class): + out_class = obj.__class__ + + if any( + not ( + issubclass(out_class, obj.__class__) or out_class.info is obj.__class__.info + ) + for obj in objs + ): + raise ValueError( + f"unmergeable object classes {[type(obj).__name__ for obj in objs]}" + ) + + return out_class + + +def join_skycoord(distance, distance_func="search_around_sky"): + """Helper function to join on SkyCoord columns using distance matching. + + This function is intended for use in ``table.join()`` to allow performing a + table join where the key columns are both ``SkyCoord`` objects, matched by + computing the distance between points and accepting values below + ``distance``. + + The distance cross-matching is done using either + `~astropy.coordinates.search_around_sky` or + `~astropy.coordinates.search_around_3d`, depending on the value of + ``distance_func``. The default is ``'search_around_sky'``. + + One can also provide a function object for ``distance_func``, in which case + it must be a function that follows the same input and output API as + `~astropy.coordinates.search_around_sky`. In this case the function will + be called with ``(skycoord1, skycoord2, distance)`` as arguments. + + Parameters + ---------- + distance : `~astropy.units.Quantity` ['angle', 'length'] + Maximum distance between points to be considered a join match. + Must have angular or distance units. + distance_func : str or function + Specifies the function for performing the cross-match based on + ``distance``. If supplied as a string this specifies the name of a + function in `astropy.coordinates`. If supplied as a function then that + function is called directly. + + Returns + ------- + join_func : function + Function that accepts two ``SkyCoord`` columns (col1, col2) and returns + the tuple (ids1, ids2) of pair-matched unique identifiers. + + Examples + -------- + This example shows an inner join of two ``SkyCoord`` columns, taking any + sources within 0.2 deg to be a match. Note the new ``sc_id`` column which + is added and provides a unique source identifier for the matches. + + >>> from astropy.coordinates import SkyCoord + >>> import astropy.units as u + >>> from astropy.table import Table, join_skycoord + >>> from astropy import table + + >>> sc1 = SkyCoord([0, 1, 1.1, 2], [0, 0, 0, 0], unit='deg') + >>> sc2 = SkyCoord([0.5, 1.05, 2.1], [0, 0, 0], unit='deg') + + >>> join_func = join_skycoord(0.2 * u.deg) + >>> join_func(sc1, sc2) # Associate each coordinate with unique source ID + (array([3, 1, 1, 2]), array([4, 1, 2])) + + >>> t1 = Table([sc1], names=['sc']) + >>> t2 = Table([sc2], names=['sc']) + >>> t12 = table.join(t1, t2, join_funcs={'sc': join_skycoord(0.2 * u.deg)}) + >>> print(t12) # Note new `sc_id` column with the IDs from join_func() + sc_id sc_1 sc_2 + deg,deg deg,deg + ----- ------- -------- + 1 1.0,0.0 1.05,0.0 + 1 1.1,0.0 1.05,0.0 + 2 2.0,0.0 2.1,0.0 + + """ + if isinstance(distance_func, str): + import astropy.coordinates as coords + + try: + distance_func = getattr(coords, distance_func) + except AttributeError as err: + raise ValueError( + "distance_func must be a function in astropy.coordinates" + ) from err + else: + from inspect import isfunction + + if not isfunction(distance_func): + raise ValueError("distance_func must be a str or function") + + def join_func(sc1, sc2): + # Call the appropriate SkyCoord method to find pairs within distance + idxs1, idxs2, d2d, d3d = distance_func(sc1, sc2, distance) + + # Now convert that into unique identifiers for each near-pair. This is + # taken to be transitive, so that if points 1 and 2 are "near" and points + # 1 and 3 are "near", then 1, 2, and 3 are all given the same identifier. + # This identifier will then be used in the table join matching. + + # Identifiers for each column, initialized to all zero. + ids1 = np.zeros(len(sc1), dtype=int) + ids2 = np.zeros(len(sc2), dtype=int) + + # Start the identifier count at 1 + id_ = 1 + for idx1, idx2 in zip(idxs1, idxs2): + # If this col1 point is previously identified then set corresponding + # col2 point to same identifier. Likewise for col2 and col1. + if ids1[idx1] > 0: + ids2[idx2] = ids1[idx1] + elif ids2[idx2] > 0: + ids1[idx1] = ids2[idx2] + else: + # Not yet seen so set identifier for col1 and col2 + ids1[idx1] = id_ + ids2[idx2] = id_ + id_ += 1 + + # Fill in unique identifiers for points with no near neighbor + for ids in (ids1, ids2): + for idx in np.flatnonzero(ids == 0): + ids[idx] = id_ + id_ += 1 + + # End of enclosure join_func() + return ids1, ids2 + + return join_func + + +def join_distance(distance, kdtree_args=None, query_args=None): + """Helper function to join table columns using distance matching. + + This function is intended for use in ``table.join()`` to allow performing + a table join where the key columns are matched by computing the distance + between points and accepting values below ``distance``. This numerical + "fuzzy" match can apply to 1-D or 2-D columns, where in the latter case + the distance is a vector distance. + + The distance cross-matching is done using `scipy.spatial.KDTree`. If + necessary you can tweak the default behavior by providing ``dict`` values + for the ``kdtree_args`` or ``query_args``. + + Parameters + ---------- + distance : float or `~astropy.units.Quantity` ['length'] + Maximum distance between points to be considered a join match + kdtree_args : dict, None + Optional extra args for `~scipy.spatial.KDTree` + query_args : dict, None + Optional extra args for `~scipy.spatial.KDTree.query_ball_tree` + + Returns + ------- + join_func : function + Function that accepts (skycoord1, skycoord2) and returns the tuple + (ids1, ids2) of pair-matched unique identifiers. + + Examples + -------- + >>> from astropy.table import Table, join_distance + >>> from astropy import table + + >>> c1 = [0, 1, 1.1, 2] + >>> c2 = [0.5, 1.05, 2.1] + + >>> t1 = Table([c1], names=['col']) + >>> t2 = Table([c2], names=['col']) + >>> t12 = table.join(t1, t2, join_type='outer', join_funcs={'col': join_distance(0.2)}) + >>> print(t12) + col_id col_1 col_2 + ------ ----- ----- + 1 1.0 1.05 + 1 1.1 1.05 + 2 2.0 2.1 + 3 0.0 -- + 4 -- 0.5 + + """ + if not HAS_SCIPY: + raise ModuleNotFoundError("scipy is required to use join_distance()") + from scipy.spatial import KDTree + + if kdtree_args is None: + kdtree_args = {} + if query_args is None: + query_args = {} + + def join_func(col1, col2): + if col1.ndim > 2 or col2.ndim > 2: + raise ValueError("columns for isclose_join must be 1- or 2-dimensional") + + if isinstance(distance, Quantity): + # Convert to np.array with common unit + col1 = col1.to_value(distance.unit) + col2 = col2.to_value(distance.unit) + dist = distance.value + else: + # Convert to np.array to allow later in-place shape changing + col1 = np.asarray(col1) + col2 = np.asarray(col2) + dist = distance + + # Ensure columns are pure np.array and are 2-D for use with KDTree + if col1.ndim == 1: + col1 = col1.reshape(col1.shape + (1,)) + if col2.ndim == 1: + col2 = col2.reshape(col2.shape + (1,)) + + # Cross-match col1 and col2 within dist using KDTree + kd1 = KDTree(col1, **kdtree_args) + kd2 = KDTree(col2, **kdtree_args) + nears = kd1.query_ball_tree(kd2, r=dist, **query_args) + + # Output of above is nears which is a list of lists, where the outer + # list corresponds to each item in col1, and where the inner lists are + # indexes into col2 of elements within the distance tolerance. This + # identifies col1 / col2 near pairs. + + # Now convert that into unique identifiers for each near-pair. This is + # taken to be transitive, so that if points 1 and 2 are "near" and points + # 1 and 3 are "near", then 1, 2, and 3 are all given the same identifier. + # This identifier will then be used in the table join matching. + + # Identifiers for each column, initialized to all zero. + ids1 = np.zeros(len(col1), dtype=int) + ids2 = np.zeros(len(col2), dtype=int) + + # Start the identifier count at 1 + id_ = 1 + for idx1, idxs2 in enumerate(nears): + for idx2 in idxs2: + # If this col1 point is previously identified then set corresponding + # col2 point to same identifier. Likewise for col2 and col1. + if ids1[idx1] > 0: + ids2[idx2] = ids1[idx1] + elif ids2[idx2] > 0: + ids1[idx1] = ids2[idx2] + else: + # Not yet seen so set identifier for col1 and col2 + ids1[idx1] = id_ + ids2[idx2] = id_ + id_ += 1 + + # Fill in unique identifiers for points with no near neighbor + for ids in (ids1, ids2): + for idx in np.flatnonzero(ids == 0): + ids[idx] = id_ + id_ += 1 + + # End of enclosure join_func() + return ids1, ids2 + + return join_func + + +def join( + left, + right, + keys=None, + join_type="inner", + *, + keys_left=None, + keys_right=None, + keep_order=False, + uniq_col_name="{col_name}_{table_name}", + table_names=["1", "2"], + metadata_conflicts="warn", + join_funcs=None, +): + """ + Perform a join of the left table with the right table on specified keys. + + Parameters + ---------- + left : `~astropy.table.Table`-like object + Left side table in the join. If not a Table, will call ``Table(left)`` + right : `~astropy.table.Table`-like object + Right side table in the join. If not a Table, will call ``Table(right)`` + keys : str or list of str + Name(s) of column(s) used to match rows of left and right tables. + Default is to use all columns which are common to both tables. + join_type : str + Join type ('inner' | 'outer' | 'left' | 'right' | 'cartesian'), default is 'inner' + keys_left : str or list of str or list of column-like, optional + Left column(s) used to match rows instead of ``keys`` arg. This can be + be a single left table column name or list of column names, or a list of + column-like values with the same lengths as the left table. + keys_right : str or list of str or list of column-like, optional + Same as ``keys_left``, but for the right side of the join. + keep_order: bool, optional + By default, rows are sorted by the join keys. If True, preserve the order of + rows from the left table for "inner" or "left" joins, or from the right table + for "right" joins. For other join types this argument is ignored except that a + warning is issued if ``keep_order=True``. + uniq_col_name : str or None + String generate a unique output column name in case of a conflict. + The default is '{col_name}_{table_name}'. + table_names : list of str or None + Two-element list of table names used when generating unique output + column names. The default is ['1', '2']. + metadata_conflicts : str + How to proceed with metadata conflicts. This should be one of: + * ``'silent'``: silently pick the last conflicting meta-data value + * ``'warn'``: pick the last conflicting meta-data value, but emit a warning (default) + * ``'error'``: raise an exception. + join_funcs : dict, None + Dict of functions to use for matching the corresponding key column(s). + See `~astropy.table.join_skycoord` for an example and details. + + Returns + ------- + joined_table : `~astropy.table.Table` object + New table containing the result of the join operation. + """ + # Try converting inputs to Table as needed + if not isinstance(left, Table): + left = Table(left) + if not isinstance(right, Table): + right = Table(right) + + # Define a magic key that won't conflict with any user column name. This is to + # support the keep_order argument. In this case a temporary column is added to the + # left or right table to keep track of the original row order. After joining, the + # order is restored and the temporary column is removed. + sort_table_index_key = "__astropy_table_keep_order_sort_index__" + sort_table = None + if keep_order: + if join_type not in ["left", "right", "inner"]: + # Keep order is not meaningful for an outer join and cartesian join is + # already ordered by left (primary) then right (secondary). + warnings.warn( + "keep_order=True is only supported for left, right, and inner joins", + UserWarning, + stacklevel=2, + ) + else: + sort_table = right if join_type == "right" else left + sort_table[sort_table_index_key] = np.arange(len(sort_table)) + + # In case keep_order=True we need try/finally to ensure that the temporary column + # is removed even if an exception is raised. + try: + out = _join( + left, + right, + keys, + join_type, + uniq_col_name, + table_names, + metadata_conflicts, + join_funcs, + keys_left=keys_left, + keys_right=keys_right, + ) + if sort_table is not None: + # Sort joined table to the original order and remove the temporary column. + out.sort(sort_table_index_key) + del out[sort_table_index_key] + finally: + if sort_table is not None: + # If sort_table is not None that implies keep_order=True. + del sort_table[sort_table_index_key] + + # Merge the column and table meta data. Table subclasses might override + # these methods for custom merge behavior. + _merge_table_meta(out, [left, right], metadata_conflicts=metadata_conflicts) + + return out + + +def setdiff(table1, table2, keys=None): + """ + Take a set difference of table rows. + + The row set difference will contain all rows in ``table1`` that are not + present in ``table2``. If the keys parameter is not defined, all columns in + ``table1`` will be included in the output table. + + Parameters + ---------- + table1 : `~astropy.table.Table` + ``table1`` is on the left side of the set difference. + table2 : `~astropy.table.Table` + ``table2`` is on the right side of the set difference. + keys : str or list of str + Name(s) of column(s) used to match rows of left and right tables. + Default is to use all columns in ``table1``. + + Returns + ------- + diff_table : `~astropy.table.Table` + New table containing the set difference between tables. If the set + difference is none, an empty table will be returned. + + Examples + -------- + To get a set difference between two tables:: + + >>> from astropy.table import setdiff, Table + >>> t1 = Table({'a': [1, 4, 9], 'b': ['c', 'd', 'f']}, names=('a', 'b')) + >>> t2 = Table({'a': [1, 5, 9], 'b': ['c', 'b', 'f']}, names=('a', 'b')) + >>> print(t1) + a b + --- --- + 1 c + 4 d + 9 f + >>> print(t2) + a b + --- --- + 1 c + 5 b + 9 f + >>> print(setdiff(t1, t2)) + a b + --- --- + 4 d + + >>> print(setdiff(t2, t1)) + a b + --- --- + 5 b + """ + if keys is None: + keys = table1.colnames + + # Check that all keys are in table1 and table2 + for tbl, tbl_str in ((table1, "table1"), (table2, "table2")): + diff_keys = np.setdiff1d(keys, tbl.colnames) + if len(diff_keys) != 0: + raise ValueError( + f"The {diff_keys} columns are missing from {tbl_str}, cannot take " + "a set difference." + ) + + # Make a light internal copy of both tables + t1 = table1.copy(copy_data=False) + t1.meta = {} + t1.keep_columns(keys) + t1["__index1__"] = np.arange(len(table1)) # Keep track of rows indices + + # Make a light internal copy to avoid touching table2 + t2 = table2.copy(copy_data=False) + t2.meta = {} + t2.keep_columns(keys) + # Dummy column to recover rows after join + t2["__index2__"] = np.zeros(len(t2), dtype=np.uint8) # dummy column + + t12 = _join(t1, t2, join_type="left", keys=keys, metadata_conflicts="silent") + + # If t12 index2 is masked then that means some rows were in table1 but not table2. + if hasattr(t12["__index2__"], "mask"): + # Define bool mask of table1 rows not in table2 + diff = t12["__index2__"].mask + # Get the row indices of table1 for those rows + idx = t12["__index1__"][diff] + # Select corresponding table1 rows straight from table1 to ensure + # correct table and column types. + t12_diff = table1[idx] + else: + t12_diff = table1[[]] + + return t12_diff + + +def dstack(tables, join_type="outer", metadata_conflicts="warn"): + """ + Stack columns within tables depth-wise. + + A ``join_type`` of 'exact' means that the tables must all have exactly + the same column names (though the order can vary). If ``join_type`` + is 'inner' then the intersection of common columns will be the output. + A value of 'outer' (default) means the output will have the union of + all columns, with table values being masked where no common values are + available. + + Parameters + ---------- + tables : `~astropy.table.Table` or `~astropy.table.Row` or list thereof + Table(s) to stack along depth-wise with the current table + Table columns should have same shape and name for depth-wise stacking + join_type : str + Join type ('inner' | 'exact' | 'outer'), default is 'outer' + metadata_conflicts : str + How to proceed with metadata conflicts. This should be one of: + * ``'silent'``: silently pick the last conflicting meta-data value + * ``'warn'``: pick the last conflicting meta-data value, but emit a warning (default) + * ``'error'``: raise an exception. + + Returns + ------- + stacked_table : `~astropy.table.Table` object + New table containing the stacked data from the input tables. + + Examples + -------- + To stack two tables along rows do:: + + >>> from astropy.table import dstack, Table + >>> t1 = Table({'a': [1., 2.], 'b': [3., 4.]}, names=('a', 'b')) + >>> t2 = Table({'a': [5., 6.], 'b': [7., 8.]}, names=('a', 'b')) + >>> print(t1) + a b + --- --- + 1.0 3.0 + 2.0 4.0 + >>> print(t2) + a b + --- --- + 5.0 7.0 + 6.0 8.0 + >>> print(dstack([t1, t2])) + a b + ---------- ---------- + 1.0 .. 5.0 3.0 .. 7.0 + 2.0 .. 6.0 4.0 .. 8.0 + """ + _check_join_type(join_type, "dstack") + + tables = _get_list_of_tables(tables) + if len(tables) == 1: + return tables[0] # no point in stacking a single table + + n_rows = {len(table) for table in tables} + if len(n_rows) != 1: + raise ValueError("Table lengths must all match for dstack") + n_row = n_rows.pop() + + out = vstack(tables, join_type, metadata_conflicts) + + for name, col in out.columns.items(): + col = out[name] + + # Reshape to so each original column is now in a row. + # If entries are not 0-dim then those additional shape dims + # are just carried along. + # [x x x y y y] => [[x x x], + # [y y y]] + new_shape = (len(tables), n_row) + col.shape[1:] + col = col.reshape(new_shape) + + # Transpose the table and row axes to get to + # [[x, y], + # [x, y] + # [x, y]] + axes = np.arange(len(col.shape)) + axes[:2] = [1, 0] + + # This temporarily makes `out` be corrupted (columns of different + # length) but it all works out in the end. + out.columns.__setitem__(name, col.transpose(axes), validated=True) + + return out + + +def vstack(tables, join_type="outer", metadata_conflicts="warn"): + """ + Stack tables vertically (along rows). + + A ``join_type`` of 'exact' means that the tables must all have exactly + the same column names (though the order can vary). If ``join_type`` + is 'inner' then the intersection of common columns will be the output. + A value of 'outer' (default) means the output will have the union of + all columns, with table values being masked where no common values are + available. + + Parameters + ---------- + tables : `~astropy.table.Table` or `~astropy.table.Row` or list thereof + Table(s) to stack along rows (vertically) with the current table + join_type : str + Join type ('inner' | 'exact' | 'outer'), default is 'outer' + metadata_conflicts : str + How to proceed with metadata conflicts. This should be one of: + * ``'silent'``: silently pick the last conflicting meta-data value + * ``'warn'``: pick the last conflicting meta-data value, but emit a warning (default) + * ``'error'``: raise an exception. + + Returns + ------- + stacked_table : `~astropy.table.Table` object + New table containing the stacked data from the input tables. + + Examples + -------- + To stack two tables along rows do:: + + >>> from astropy.table import vstack, Table + >>> t1 = Table({'a': [1, 2], 'b': [3, 4]}, names=('a', 'b')) + >>> t2 = Table({'a': [5, 6], 'b': [7, 8]}, names=('a', 'b')) + >>> print(t1) + a b + --- --- + 1 3 + 2 4 + >>> print(t2) + a b + --- --- + 5 7 + 6 8 + >>> print(vstack([t1, t2])) + a b + --- --- + 1 3 + 2 4 + 5 7 + 6 8 + """ + _check_join_type(join_type, "vstack") + + tables = _get_list_of_tables(tables) # validates input + if len(tables) == 1: + return tables[0] # no point in stacking a single table + + out = _vstack(tables, join_type, metadata_conflicts) + + # Merge table metadata + _merge_table_meta(out, tables, metadata_conflicts=metadata_conflicts) + + return out + + +def hstack( + tables, + join_type="outer", + uniq_col_name="{col_name}_{table_name}", + table_names=None, + metadata_conflicts="warn", +): + """ + Stack tables along columns (horizontally). + + A ``join_type`` of 'exact' means that the tables must all + have exactly the same number of rows. If ``join_type`` is 'inner' then + the intersection of rows will be the output. A value of 'outer' (default) + means the output will have the union of all rows, with table values being + masked where no common values are available. + + Parameters + ---------- + tables : `~astropy.table.Table` or `~astropy.table.Row` or list thereof + Tables to stack along columns (horizontally) with the current table + join_type : str + Join type ('inner' | 'exact' | 'outer'), default is 'outer' + uniq_col_name : str or None + String generate a unique output column name in case of a conflict. + The default is '{col_name}_{table_name}'. + table_names : list of str or None + Two-element list of table names used when generating unique output + column names. The default is ['1', '2', ..]. + metadata_conflicts : str + How to proceed with metadata conflicts. This should be one of: + * ``'silent'``: silently pick the last conflicting meta-data value + * ``'warn'``: pick the last conflicting meta-data value, + but emit a warning (default) + * ``'error'``: raise an exception. + + Returns + ------- + stacked_table : `~astropy.table.Table` object + New table containing the stacked data from the input tables. + + See Also + -------- + Table.add_columns, Table.replace_column, Table.update + + Examples + -------- + To stack two tables horizontally (along columns) do:: + + >>> from astropy.table import Table, hstack + >>> t1 = Table({'a': [1, 2], 'b': [3, 4]}, names=('a', 'b')) + >>> t2 = Table({'c': [5, 6], 'd': [7, 8]}, names=('c', 'd')) + >>> print(t1) + a b + --- --- + 1 3 + 2 4 + >>> print(t2) + c d + --- --- + 5 7 + 6 8 + >>> print(hstack([t1, t2])) + a b c d + --- --- --- --- + 1 3 5 7 + 2 4 6 8 + """ + _check_join_type(join_type, "hstack") + + tables = _get_list_of_tables(tables) # validates input + if len(tables) == 1: + return tables[0] # no point in stacking a single table + + out = _hstack(tables, join_type, uniq_col_name, table_names) + + _merge_table_meta(out, tables, metadata_conflicts=metadata_conflicts) + + return out + + +def unique(input_table, keys=None, silent=False, keep="first"): + """ + Return a new table with unique rows, sorted by ``keys``. + + Parameters + ---------- + input_table : table-like + keys : str or list of str + Name(s) of column(s) used to create unique rows. + Default is to use all columns. + keep : {'first', 'last', 'none'} + Whether to keep the first or last row for each set of + duplicates. If 'none', all rows that are duplicate are + removed, leaving only rows that are already unique in + the input. + Default is 'first'. + silent : bool + If `True`, masked value column(s) are silently removed from + ``keys``. If `False`, an exception is raised when ``keys`` + contains masked value column(s). + Default is `False`. + + Returns + ------- + unique_table : `~astropy.table.Table` object + New table containing only the unique rows of ``input_table``. + + Examples + -------- + >>> from astropy.table import unique, Table + >>> import numpy as np + >>> table = Table(data=[[1,2,3,2,3,3], + ... [2,3,4,5,4,6], + ... [3,4,5,6,7,8]], + ... names=['col1', 'col2', 'col3'], + ... dtype=[np.int32, np.int32, np.int32]) + >>> table + + col1 col2 col3 + int32 int32 int32 + ----- ----- ----- + 1 2 3 + 2 3 4 + 3 4 5 + 2 5 6 + 3 4 7 + 3 6 8 + >>> unique(table, keys='col1') +
+ col1 col2 col3 + int32 int32 int32 + ----- ----- ----- + 1 2 3 + 2 3 4 + 3 4 5 + >>> unique(table, keys=['col1'], keep='last') +
+ col1 col2 col3 + int32 int32 int32 + ----- ----- ----- + 1 2 3 + 2 5 6 + 3 6 8 + >>> unique(table, keys=['col1', 'col2']) +
+ col1 col2 col3 + int32 int32 int32 + ----- ----- ----- + 1 2 3 + 2 3 4 + 2 5 6 + 3 4 5 + 3 6 8 + >>> unique(table, keys=['col1', 'col2'], keep='none') +
+ col1 col2 col3 + int32 int32 int32 + ----- ----- ----- + 1 2 3 + 2 3 4 + 2 5 6 + 3 6 8 + >>> unique(table, keys=['col1'], keep='none') +
+ col1 col2 col3 + int32 int32 int32 + ----- ----- ----- + 1 2 3 + + """ + if keep not in ("first", "last", "none"): + raise ValueError("'keep' should be one of 'first', 'last', 'none'") + + if isinstance(keys, str): + keys = [keys] + if keys is None: + keys = input_table.colnames + else: + if len(set(keys)) != len(keys): + raise ValueError("duplicate key names") + + # Check for columns with masked values + for key in keys[:]: + col = input_table[key] + if hasattr(col, "mask") and np.any(col.mask): + if not silent: + raise ValueError( + "cannot use columns with masked values as keys; " + f"remove column '{key}' from keys and rerun " + "unique()" + ) + del keys[keys.index(key)] + if len(keys) == 0: + raise ValueError( + "no column remained in ``keys``; " + "unique() cannot work with masked value " + "key columns" + ) + + grouped_table = input_table.group_by(keys) + indices = grouped_table.groups.indices + if keep == "first": + indices = indices[:-1] + elif keep == "last": + indices = indices[1:] - 1 + else: + indices = indices[:-1][np.diff(indices) == 1] + + return grouped_table[indices] + + +def get_col_name_map( + arrays, common_names, uniq_col_name="{col_name}_{table_name}", table_names=None +): + """ + Find the column names mapping when merging the list of tables + ``arrays``. It is assumed that col names in ``common_names`` are to be + merged into a single column while the rest will be uniquely represented + in the output. The args ``uniq_col_name`` and ``table_names`` specify + how to rename columns in case of conflicts. + + Returns a dict mapping each output column name to the input(s). This takes the form + {outname : (col_name_0, col_name_1, ...), ... }. For key columns all of input names + will be present, while for the other non-key columns the value will be (col_name_0, + None, ..) or (None, col_name_1, ..) etc. + """ + col_name_map = collections.defaultdict(lambda: [None] * len(arrays)) + col_name_list = [] + + if table_names is None: + table_names = [str(ii + 1) for ii in range(len(arrays))] + + for idx, array in enumerate(arrays): + table_name = table_names[idx] + for name in array.colnames: + out_name = name + + if name in common_names: + # If name is in the list of common_names then insert into + # the column name list, but just once. + if name not in col_name_list: + col_name_list.append(name) + else: + # If name is not one of the common column outputs, and it collides + # with the names in one of the other arrays, then rename + others = list(arrays) + others.pop(idx) + if any(name in other.colnames for other in others): + out_name = uniq_col_name.format( + table_name=table_name, col_name=name + ) + col_name_list.append(out_name) + + col_name_map[out_name][idx] = name + + # Check for duplicate output column names + col_name_count = Counter(col_name_list) + repeated_names = [name for name, count in col_name_count.items() if count > 1] + if repeated_names: + raise TableMergeError( + f"Merging column names resulted in duplicates: {repeated_names}. " + "Change uniq_col_name or table_names args to fix this." + ) + + # Convert col_name_map to a regular dict with tuple (immutable) values + col_name_map = OrderedDict((name, col_name_map[name]) for name in col_name_list) + + return col_name_map + + +def get_descrs(arrays, col_name_map): + """ + Find the dtypes descrs resulting from merging the list of arrays' dtypes, + using the column name mapping ``col_name_map``. + + Return a list of descrs for the output. + """ + out_descrs = [] + + for out_name, in_names in col_name_map.items(): + # List of input arrays that contribute to this output column + in_cols = [arr[name] for arr, name in zip(arrays, in_names) if name is not None] + + # List of names of the columns that contribute to this output column. + names = [name for name in in_names if name is not None] + + # Output dtype is the superset of all dtypes in in_arrays + try: + dtype = result_type(in_cols) + except TableMergeError as tme: + # Beautify the error message when we are trying to merge columns with incompatible + # types by including the name of the columns that originated the error. + raise TableMergeError( + f"The '{names[0]}' columns have incompatible types: " + f"{tme._incompat_types}" + ) from tme + + # Make sure all input shapes are the same + uniq_shapes = {col.shape[1:] for col in in_cols} + if len(uniq_shapes) != 1: + raise TableMergeError(f"Key columns {names!r} have different shape") + shape = uniq_shapes.pop() + + if out_name is not None: + out_name = str(out_name) + out_descrs.append((out_name, dtype, shape)) + + return out_descrs + + +def result_type(cols): + """ + Use numpy to find the common dtype for a list of columns. + + Only allow columns within the following fundamental numpy data types: + np.bool_, np.object_, np.number, np.character, np.void + """ + try: + return metadata.utils.result_type(cols) + except metadata.MergeConflictError as err: + tme = TableMergeError(f"Columns have incompatible types {err._incompat_types}") + tme._incompat_types = err._incompat_types + raise tme from err + + +def _get_join_sort_idxs(keys, left, right): + # Go through each of the key columns in order and make columns for + # a new structured array that represents the lexical ordering of those + # key columns. This structured array is then argsort'ed. The trick here + # is that some columns (e.g. Time) may need to be expanded into multiple + # columns for ordering here. + + ii = 0 # Index for uniquely naming the sort columns + # sortable_table dtypes as list of (name, dtype_str, shape) tuples + sort_keys_dtypes = [] + sort_keys = [] # sortable_table (structured ndarray) column names + sort_left = {} # sortable ndarrays from left table + sort_right = {} # sortable ndarray from right table + + for key in keys: + # get_sortable_arrays() returns a list of ndarrays that can be lexically + # sorted to represent the order of the column. In most cases this is just + # a single element of the column itself. + left_sort_cols = left[key].info.get_sortable_arrays() + right_sort_cols = right[key].info.get_sortable_arrays() + + if len(left_sort_cols) != len(right_sort_cols): + # Should never happen because cols are screened beforehand for compatibility + raise RuntimeError("mismatch in sort cols lengths") + + for left_sort_col, right_sort_col in zip(left_sort_cols, right_sort_cols): + # Check for consistency of shapes. Mismatch should never happen. + shape = left_sort_col.shape[1:] + if shape != right_sort_col.shape[1:]: + raise RuntimeError("mismatch in shape of left vs. right sort array") + + if shape != (): + raise ValueError(f"sort key column {key!r} must be 1-d") + + sort_key = str(ii) + sort_keys.append(sort_key) + sort_left[sort_key] = left_sort_col + sort_right[sort_key] = right_sort_col + + # Build up dtypes for the structured array that gets sorted. + dtype_str = result_type([left_sort_col, right_sort_col]) + sort_keys_dtypes.append((sort_key, dtype_str)) + ii += 1 + + # Make the empty sortable table and fill it + len_left = len(left) + sortable_table = np.empty(len_left + len(right), dtype=sort_keys_dtypes) + for key in sort_keys: + sortable_table[key][:len_left] = sort_left[key] + sortable_table[key][len_left:] = sort_right[key] + + # Finally do the (lexical) argsort and make a new sorted version + idx_sort = sortable_table.argsort(order=sort_keys) + sorted_table = sortable_table[idx_sort] + + # Get indexes of unique elements (i.e. the group boundaries) + diffs = np.concatenate(([True], sorted_table[1:] != sorted_table[:-1], [True])) + idxs = np.flatnonzero(diffs) + + return idxs, idx_sort + + +def _apply_join_funcs(left, right, keys, join_funcs): + """Apply join_funcs.""" + # Make light copies of left and right, then add new index columns. + left = left.copy(copy_data=False) + right = right.copy(copy_data=False) + for key, join_func in join_funcs.items(): + ids1, ids2 = join_func(left[key], right[key]) + # Define a unique id_key name, and keep adding underscores until we have + # a name not yet present. + id_key = key + "_id" + while id_key in left.columns or id_key in right.columns: + id_key = id_key[:-2] + "_id" + + keys = tuple(id_key if orig_key == key else orig_key for orig_key in keys) + left.add_column(ids1, index=0, name=id_key) # [id_key] = ids1 + right.add_column(ids2, index=0, name=id_key) # [id_key] = ids2 + + return left, right, keys + + +def _join( + left, + right, + keys=None, + join_type="inner", + uniq_col_name="{col_name}_{table_name}", + table_names=["1", "2"], + metadata_conflicts="warn", + join_funcs=None, + keys_left=None, + keys_right=None, +): + """ + Perform a join of the left and right Tables on specified keys. + + Parameters + ---------- + left : Table + Left side table in the join + right : Table + Right side table in the join + keys : str or list of str + Name(s) of column(s) used to match rows of left and right tables. + Default is to use all columns which are common to both tables. + join_type : str + Join type ('inner' | 'outer' | 'left' | 'right' | 'cartesian'), default is 'inner' + uniq_col_name : str or None + String generate a unique output column name in case of a conflict. + The default is '{col_name}_{table_name}'. + table_names : list of str or None + Two-element list of table names used when generating unique output + column names. The default is ['1', '2']. + metadata_conflicts : str + How to proceed with metadata conflicts. This should be one of: + * ``'silent'``: silently pick the last conflicting meta-data value + * ``'warn'``: pick the last conflicting meta-data value, but emit a warning (default) + * ``'error'``: raise an exception. + join_funcs : dict, None + Dict of functions to use for matching the corresponding key column(s). + See `~astropy.table.join_skycoord` for an example and details. + + Returns + ------- + joined_table : `~astropy.table.Table` object + New table containing the result of the join operation. + """ + # Special column name for cartesian join, should never collide with real column + cartesian_index_name = "__table_cartesian_join_temp_index__" + + if join_type not in ("inner", "outer", "left", "right", "cartesian"): + raise ValueError( + "The 'join_type' argument should be in 'inner', " + "'outer', 'left', 'right', or 'cartesian' " + f"(got '{join_type}' instead)" + ) + + if join_type == "cartesian": + if keys: + raise ValueError("cannot supply keys for a cartesian join") + + if join_funcs: + raise ValueError("cannot supply join_funcs for a cartesian join") + + # Make light copies of left and right, then add temporary index columns + # with all the same value so later an outer join turns into a cartesian join. + left = left.copy(copy_data=False) + right = right.copy(copy_data=False) + left[cartesian_index_name] = np.uint8(0) + right[cartesian_index_name] = np.uint8(0) + keys = (cartesian_index_name,) + + # Handle the case of join key columns that are different between left and + # right via keys_left/keys_right args. This is done by saving the original + # input tables and making new left and right tables that contain only the + # key cols but with common column names ['0', '1', etc]. This sets `keys` to + # those fake key names in the left and right tables + if keys_left is not None or keys_right is not None: + left_orig = left + right_orig = right + left, right, keys = _join_keys_left_right( + left, right, keys, keys_left, keys_right, join_funcs + ) + + if keys is None: + keys = tuple(name for name in left.colnames if name in right.colnames) + if len(keys) == 0: + raise TableMergeError("No keys in common between left and right tables") + elif isinstance(keys, str): + # If we have a single key, put it in a tuple + keys = (keys,) + + # Check the key columns + for arr, arr_label in ((left, "Left"), (right, "Right")): + for name in keys: + if name not in arr.colnames: + raise TableMergeError( + f"{arr_label} table does not have key column {name!r}" + ) + if hasattr(arr[name], "mask") and np.any(arr[name].mask): + raise TableMergeError( + f"{arr_label} key column {name!r} has missing values" + ) + + if join_funcs is not None: + if not all(key in keys for key in join_funcs): + raise ValueError( + f"join_funcs keys {join_funcs.keys()} must be a " + f"subset of join keys {keys}" + ) + left, right, keys = _apply_join_funcs(left, right, keys, join_funcs) + + len_left, len_right = len(left), len(right) + + if len_left == 0 or len_right == 0: + raise ValueError("input tables for join must both have at least one row") + + try: + idxs, idx_sort = _get_join_sort_idxs(keys, left, right) + except NotImplementedError: + raise TypeError("one or more key columns are not sortable") + + # Now that we have idxs and idx_sort, revert to the original table args to + # carry on with making the output joined table. `keys` is set to an empty + # list so that all original left and right columns are included in the + # output table. + if keys_left is not None or keys_right is not None: + keys = [] + left = left_orig + right = right_orig + + # Joined array dtype as a list of descr (name, type_str, shape) tuples + col_name_map = get_col_name_map([left, right], keys, uniq_col_name, table_names) + out_descrs = get_descrs([left, right], col_name_map) + + # Main inner loop in Cython to compute the cartesian product + # indices for the given join type + int_join_type = {"inner": 0, "outer": 1, "left": 2, "right": 3, "cartesian": 1}[ + join_type + ] + masked, n_out, left_out, left_mask, right_out, right_mask = _np_utils.join_inner( + idxs, idx_sort, len_left, int_join_type + ) + + out = _get_out_class([left, right])() + + for out_name, _dtype, _shape in out_descrs: + if out_name == cartesian_index_name: + continue + + left_name, right_name = col_name_map[out_name] + if left_name and right_name: # this is a key which comes from left and right + cols = [left[left_name], right[right_name]] + + col_cls = _get_out_class(cols) + if not hasattr(col_cls.info, "new_like"): + raise NotImplementedError( + f"join unavailable for mixin column type(s): {col_cls.__name__}" + ) + + out[out_name] = col_cls.info.new_like( + cols, n_out, metadata_conflicts, out_name + ) + out[out_name][:] = np.where( + right_mask, + left[left_name].take(left_out), + right[right_name].take(right_out), + ) + continue + elif left_name: # out_name came from the left table + name, array, array_out, array_mask = left_name, left, left_out, left_mask + elif right_name: + name, array, array_out, array_mask = ( + right_name, + right, + right_out, + right_mask, + ) + else: + raise TableMergeError('Unexpected column names (maybe one is ""?)') + + # Select the correct elements from the original table + col = array[name][array_out] + + # If the output column is masked then set the output column masking + # accordingly. Check for columns that don't support a mask attribute. + if masked and np.any(array_mask): + # If col is a Column but not MaskedColumn then upgrade at this point + # because masking is required. + if isinstance(col, Column) and not isinstance(col, MaskedColumn): + col = out.MaskedColumn(col, copy=False) + + if isinstance(col, Quantity) and not isinstance(col, Masked): + col = Masked(col, copy=False) + + # array_mask is 1-d corresponding to length of output column. We need + # make it have the correct shape for broadcasting, i.e. (length, 1, 1, ..). + # Mixin columns might not have ndim attribute so use len(col.shape). + new_shape = (col.shape[0],) + (1,) * (len(col.shape) - 1) + array_mask = array_mask.reshape(new_shape) + + # Now broadcast to the correct final shape + array_mask = np.broadcast_to(array_mask, col.shape) + + try: + col[array_mask] = col.info.mask_val + except Exception as err: # Not clear how different classes will fail here + raise NotImplementedError( + f"join requires masking column '{out_name}' but column" + f" type {col.__class__.__name__} does not support masking" + ) from err + + # Set the output table column to the new joined column + out[out_name] = col + + return out + + +def _join_keys_left_right(left, right, keys, keys_left, keys_right, join_funcs): + """Do processing to handle keys_left / keys_right args for join. + + This takes the keys_left/right inputs and turns them into a list of left/right + columns corresponding to those inputs (which can be column names or column + data values). It also generates the list of fake key column names (strings + of "1", "2", etc.) that correspond to the input keys. + """ + + def _keys_to_cols(keys, table, label): + # Process input `keys`, which is a str or list of str column names in + # `table` or a list of column-like objects. The `label` is just for + # error reporting. + if isinstance(keys, str): + keys = [keys] + cols = [] + for key in keys: + if isinstance(key, str): + try: + cols.append(table[key]) + except KeyError: + raise ValueError(f"{label} table does not have key column {key!r}") + else: + if len(key) != len(table): + raise ValueError( + f"{label} table has different length from key {key}" + ) + cols.append(key) + return cols + + if join_funcs is not None: + raise ValueError("cannot supply join_funcs arg and keys_left / keys_right") + + if keys_left is None or keys_right is None: + raise ValueError("keys_left and keys_right must both be provided") + + if keys is not None: + raise ValueError( + "keys arg must be None if keys_left and keys_right are supplied" + ) + + cols_left = _keys_to_cols(keys_left, left, "left") + cols_right = _keys_to_cols(keys_right, right, "right") + + if len(cols_left) != len(cols_right): + raise ValueError("keys_left and keys_right args must have same length") + + # Make two new temp tables for the join with only the join columns and + # key columns in common. + keys = [f"{ii}" for ii in range(len(cols_left))] + + left = left.__class__(cols_left, names=keys, copy=False) + right = right.__class__(cols_right, names=keys, copy=False) + + return left, right, keys + + +def _check_join_type(join_type, func_name): + """Check join_type arg in hstack and vstack. + + This specifically checks for the common mistake of call vstack(t1, t2) + instead of vstack([t1, t2]). The subsequent check of + ``join_type in ('inner', ..)`` does not raise in this case. + """ + if not isinstance(join_type, str): + msg = "`join_type` arg must be a string" + if isinstance(join_type, Table): + msg += ( + ". Did you accidentally " + f"call {func_name}(t1, t2, ..) instead of " + f"{func_name}([t1, t2], ..)?" + ) + raise TypeError(msg) + + if join_type not in ("inner", "exact", "outer"): + raise ValueError("`join_type` arg must be one of 'inner', 'exact' or 'outer'") + + +def _vstack(arrays, join_type="outer", metadata_conflicts="warn"): + """ + Stack Tables vertically (by rows). + + A ``join_type`` of 'exact' (default) means that the arrays must all + have exactly the same column names (though the order can vary). If + ``join_type`` is 'inner' then the intersection of common columns will + be the output. A value of 'outer' means the output will have the union of + all columns, with array values being masked where no common values are + available. + + Parameters + ---------- + arrays : list of Tables + Tables to stack by rows (vertically) + join_type : str + Join type ('inner' | 'exact' | 'outer'), default is 'outer' + + Returns + ------- + stacked_table : `~astropy.table.Table` object + New table containing the stacked data from the input tables. + """ + # Trivial case of one input array + if len(arrays) == 1: + return arrays[0] + + # Start by assuming an outer match where all names go to output + names = set(itertools.chain(*[arr.colnames for arr in arrays])) + col_name_map = get_col_name_map(arrays, names) + + # If require_match is True then the output must have exactly the same + # number of columns as each input array + if join_type == "exact": + for names in col_name_map.values(): + if any(x is None for x in names): + raise TableMergeError( + "Inconsistent columns in input arrays " + "(use 'inner' or 'outer' join_type to " + "allow non-matching columns)" + ) + join_type = "outer" + + # For an inner join, keep only columns where all input arrays have that column + if join_type == "inner": + col_name_map = OrderedDict( + (name, in_names) + for name, in_names in col_name_map.items() + if all(x is not None for x in in_names) + ) + if len(col_name_map) == 0: + raise TableMergeError("Input arrays have no columns in common") + + lens = [len(arr) for arr in arrays] + n_rows = sum(lens) + out = _get_out_class(arrays)() + + for out_name, in_names in col_name_map.items(): + # List of input arrays that contribute to this output column + cols = [arr[name] for arr, name in zip(arrays, in_names) if name is not None] + + col_cls = _get_out_class(cols) + if not hasattr(col_cls.info, "new_like"): + raise NotImplementedError( + f"vstack unavailable for mixin column type(s): {col_cls.__name__}" + ) + try: + col = col_cls.info.new_like(cols, n_rows, metadata_conflicts, out_name) + except metadata.MergeConflictError as err: + # Beautify the error message when we are trying to merge columns with incompatible + # types by including the name of the columns that originated the error. + raise TableMergeError( + f"The '{out_name}' columns have incompatible types: " + f"{err._incompat_types}" + ) from err + + idx0 = 0 + for name, array in zip(in_names, arrays): + idx1 = idx0 + len(array) + if name in array.colnames: + col[idx0:idx1] = array[name] + else: + # If col is a Column but not MaskedColumn then upgrade at this point + # because masking is required. + if isinstance(col, Column) and not isinstance(col, MaskedColumn): + col = out.MaskedColumn(col, copy=False) + + if isinstance(col, Quantity) and not isinstance(col, Masked): + col = Masked(col, copy=False) + + try: + col[idx0:idx1] = col.info.mask_val + except Exception as err: + raise NotImplementedError( + f"vstack requires masking column '{out_name}' but column" + f" type {col.__class__.__name__} does not support masking" + ) from err + idx0 = idx1 + + out[out_name] = col + + return out + + +def _hstack( + arrays, + join_type="outer", + uniq_col_name="{col_name}_{table_name}", + table_names=None, +): + """ + Stack tables horizontally (by columns). + + A ``join_type`` of 'exact' (default) means that the arrays must all + have exactly the same number of rows. If ``join_type`` is 'inner' then + the intersection of rows will be the output. A value of 'outer' means + the output will have the union of all rows, with array values being + masked where no common values are available. + + Parameters + ---------- + arrays : List of tables + Tables to stack by columns (horizontally) + join_type : str + Join type ('inner' | 'exact' | 'outer'), default is 'outer' + uniq_col_name : str or None + String generate a unique output column name in case of a conflict. + The default is '{col_name}_{table_name}'. + table_names : list of str or None + Two-element list of table names used when generating unique output + column names. The default is ['1', '2', ..]. + + Returns + ------- + stacked_table : `~astropy.table.Table` object + New table containing the stacked data from the input tables. + """ + if table_names is None: + table_names = [f"{ii + 1}" for ii in range(len(arrays))] + if len(arrays) != len(table_names): + raise ValueError("Number of arrays must match number of table_names") + + # Trivial case of one input arrays + if len(arrays) == 1: + return arrays[0] + + col_name_map = get_col_name_map(arrays, [], uniq_col_name, table_names) + + # If require_match is True then all input arrays must have the same length + arr_lens = [len(arr) for arr in arrays] + if join_type == "exact": + if len(set(arr_lens)) > 1: + raise TableMergeError( + "Inconsistent number of rows in input arrays " + "(use 'inner' or 'outer' join_type to allow " + "non-matching rows)" + ) + join_type = "outer" + + # For an inner join, keep only the common rows + if join_type == "inner": + min_arr_len = min(arr_lens) + if len(set(arr_lens)) > 1: + arrays = [arr[:min_arr_len] for arr in arrays] + arr_lens = [min_arr_len for arr in arrays] + + # If there are any output rows where one or more input arrays are missing + # then the output must be masked. If any input arrays are masked then + # output is masked. + + n_rows = max(arr_lens) + out = _get_out_class(arrays)() + + for out_name, in_names in col_name_map.items(): + for name, array, arr_len in zip(in_names, arrays, arr_lens): + if name is None: + continue + + if n_rows > arr_len: + indices = np.arange(n_rows) + indices[arr_len:] = 0 + col = array[name][indices] + + # If col is a Column but not MaskedColumn then upgrade at this point + # because masking is required. + if isinstance(col, Column) and not isinstance(col, MaskedColumn): + col = out.MaskedColumn(col, copy=False) + + if isinstance(col, Quantity) and not isinstance(col, Masked): + col = Masked(col, copy=False) + + try: + col[arr_len:] = col.info.mask_val + except Exception as err: + raise NotImplementedError( + f"hstack requires masking column '{out_name}' but column" + f" type {col.__class__.__name__} does not support masking" + ) from err + else: + col = array[name][:n_rows] + + out[out_name] = col + + return out diff --git a/astropy/table/pandas.py b/astropy/table/pandas.py new file mode 100644 index 000000000000..9a8c65b26b18 --- /dev/null +++ b/astropy/table/pandas.py @@ -0,0 +1,33 @@ +ascii_coded = ( + "Ò♙♙♙♙♙♙♙♙♌♐♐♌♙♙♙♙♙♙♌♌♙♙Ò♙♙♙♙♙♙♙♘♐♐♐♈♙♙♙♙♙♌♐♐♐♔Ò♙♙♌♈♙♙♌♐♈♈♙♙♙♙♙♙♙♙♈♐♐♙Ò♙♐♙♙♙♐♐♙♙♙" + "♙♙♙♙♙♙♙♙♙♙♙♙Ò♐♔♙♙♘♐♐♙♙♌♐♐♔♙♙♌♌♌♙♙♙♌Ò♐♐♙♙♘♐♐♌♙♈♐♈♙♙♙♈♐♐♙♙♘♔Ò♐♐♌♙♘♐♐♐♌♌♙♙♌♌♌♙♈♈♙♌♐" + "♐Ò♘♐♐♐♌♐♐♐♐♐♐♌♙♈♙♌♐♐♐♐♐♔Ò♘♐♐♐♐♐♐♐♐♐♐♐♐♈♈♐♐♐♐♐♐♙Ò♙♘♐♐♐♐♈♐♐♐♐♐♐♙♙♐♐♐♐♐♙♙Ò♙♙♙♈♈♈♙♙♐" + "♐♐♐♐♔♙♐♐♐♐♈♙♙Ò♙♙♙♙♙♙♙♙♙♈♈♐♐♐♙♈♈♈♙♙♙♙Ò" +) +ascii_uncoded = "".join([chr(ord(c) - 200) for c in ascii_coded]) +url = "https://media.giphy.com/media/e24Q8FKE2mxRS/giphy.gif" +message_coded = "ĘĊÄļÄŦÄŠÄģÃˇÄœÄŠÄĒÄ´Ä­Ã¨ÄąÄļÄŧÄ­ÄēÄŠÄĢÄŧĹġÄļ" +message_uncoded = "".join([chr(ord(c) - 200) for c in message_coded]) + +try: + from IPython import display + + html = display.Image(url=url)._repr_html_() + + class HTMLWithBackup(display.HTML): + def __init__(self, data, backup_text): + super().__init__(data) + self.backup_text = backup_text + + def __repr__(self): + if self.backup_text is None: + return super().__repr__() + else: + return self.backup_text + + dhtml = HTMLWithBackup(html, ascii_uncoded) + display.display(dhtml) +except ImportError: + print(ascii_uncoded) +except (UnicodeEncodeError, SyntaxError): + pass diff --git a/astropy/table/pprint.py b/astropy/table/pprint.py index 6f7e108f494b..4d0d59581c91 100644 --- a/astropy/table/pprint.py +++ b/astropy/table/pprint.py @@ -1,364 +1,881 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +import fnmatch import os +import re import sys -from itertools import izip +from shutil import get_terminal_size -from .. import log -from ..utils.console import Getch, color_print +import numpy as np -_format_funcs = {None: lambda format_, val: str(val)} -MAX_LINES = 25 -MAX_WIDTH = 80 +from astropy import log, table +from astropy.utils.console import Getch, color_print, conf +from astropy.utils.data_info import dtype_info_name +__all__ = [] -def _get_pprint_size(max_lines=None, max_width=None): - """Get the output size (number of lines and character width) for Column and - Table pformat/pprint methods. - If no value of ``max_lines`` is supplied then the height of the screen - terminal is used to set ``max_lines``. If the terminal height cannot be - determined then a default of ``astropy.table.pprint.MAX_LINES`` is used. - If a negative value of ``max_lines`` is supplied then there is no line - limit applied. +def default_format_func(format_, val): + if isinstance(val, bytes): + return val.decode("utf-8", errors="replace") + else: + return str(val) - The Same applies for max_width except the default is - ``astropy.table.pprint.MAX_WIDTH``. - Parameters - ---------- - max_lines : int or None - Maximum lines of output (header + data rows) +# The first three functions are helpers for _auto_format_func - max_width : int or None - Maximum width (characters) output - Returns - ------- - max_lines, max_width : int +def _use_str_for_masked_values(format_func): + """Wrap format function to trap masked values. + String format functions and most user functions will not be able to deal + with masked values, so we wrap them to ensure they are passed to str(). """ - if max_lines is None or max_width is None: - try: # Will likely fail on Windows - import termios - import fcntl - import struct - s = struct.pack("HHHH", 0, 0, 0, 0) - fd_stdout = sys.stdout.fileno() - x = fcntl.ioctl(fd_stdout, termios.TIOCGWINSZ, s) - (lines, width, xpixels, ypixels) = struct.unpack("HHHH", x) - if lines > 12: - lines -= 6 - if width > 10: - width -= 1 - except: - lines, width = MAX_LINES, MAX_WIDTH - - if max_lines is None: - max_lines = lines - elif max_lines < 0: - max_lines = sys.maxint - if max_lines < 6: - max_lines = 6 - - if max_width is None: - max_width = width - elif max_width < 0: - max_width = sys.maxint - if max_width < 10: - max_width = 10 - - return max_lines, max_width - - -def _auto_format_func(format_, val): - """Format ``val`` according to ``format_`` for both old- and new- - style format specifications. More importantly, determine and cache - (in _format_funcs) a function that will do this subsequently. In - this way this complicated logic is only done for the first value. - - Returns the formatted value. + return lambda format_, val: ( + str(val) if val is np.ma.masked else format_func(format_, val) + ) + + +def _possible_string_format_functions(format_): + """Iterate through possible string-derived format functions. + + A string can either be a format specifier for the format built-in, + a new-style format string, or an old-style format string. """ - try: - # Convert val to Python object with tolist(). See - # https://github.com/astropy/astropy/issues/148#issuecomment-3930809 - out = format_.format(val.tolist()) - # Require that the format statement actually did something - if out == format_: - raise ValueError - format_func = lambda format_, val: format_.format(val.tolist()) - except: # Not sure what exceptions might be raised - try: - out = format_ % val - if out == format_: - raise ValueError - format_func = lambda format_, val: format_ % val - except: - raise ValueError('Unable to parse format string {0}' - .format(format_)) - _format_funcs[format_] = format_func - return out + yield lambda format_, val: format(val, format_) + yield lambda format_, val: format_.format(val) + yield lambda format_, val: format_ % val + yield lambda format_, val: format_.format(**{k: val[k] for k in val.dtype.names}) -def _pformat_col(col, max_lines=None, show_name=True, show_units=False): - """Return a list of formatted string representation of column values. +def get_auto_format_func( + col=None, possible_string_format_functions=_possible_string_format_functions +): + """ + Return a wrapped ``auto_format_func`` function which is used in + formatting table columns. This is primarily an internal function but + gets used directly in other parts of astropy, e.g. `astropy.io.ascii`. Parameters ---------- - max_lines : int - Maximum lines of output (header + data rows) - - show_name : bool - Include column name (default=True) + col_name : object, optional + Hashable object to identify column like id or name. Default is None. - show_units : bool - Include a header row for units (default=False) + possible_string_format_functions : func, optional + Function that yields possible string formatting functions + (defaults to internal function to do this). Returns ------- - lines : list - List of lines with formatted column values - - n_header : int - Number of lines in the header - + Wrapped ``auto_format_func`` function """ - max_lines, _ = _get_pprint_size(max_lines, -1) - - multidims = col.shape[1:] - if multidims: - multidim0 = tuple(0 for n in multidims) - multidim1 = tuple(n - 1 for n in multidims) - - col_strs = [] # List of formatted column values - i_dashes = None - i_centers = [] # Line indexes where content should be centered - if show_name: - i_centers.append(len(col_strs)) - if multidims: - col_name = col.name + ' [{0}]'.format( - ','.join(str(n) for n in multidims)) - else: - col_name = col.name - col_strs.append(col_name) - max_lines -= 1 - if show_units: - i_centers.append(len(col_strs)) - col_strs.append(col.units or '') - max_lines -= 1 - if show_units or show_name: - i_dashes = len(col_strs) - col_strs.append('---') - max_lines -= 1 - - n_header = len(col_strs) - n_print2 = max_lines // 2 - n_rows = len(col) - - format_func = _format_funcs.get(col.format, _auto_format_func) - if len(col) > max_lines: - i0 = n_print2 - i1 = n_rows - n_print2 - max_lines % 2 - else: - i0 = len(col) - i1 = 0 - - # Add formatted values if within bounds allowed by max_lines - for i in xrange(n_rows): - if i < i0 or i > i1: - if multidims: - col_str = (format_func(col.format, col[(i,) + multidim0]) + - ' .. ' + - format_func(col.format, col[(i,) + multidim1])) - else: - col_str = format_func(col.format, col[i]) - col_strs.append(col_str) - elif i == i0: - col_strs.append('...') - - col_width = max(len(x) for x in col_strs) - # Center line content and generate dashed headerline - for i in i_centers: - col_strs[i] = col_strs[i].center(col_width) - if i_dashes is not None: - col_strs[i_dashes] = '-' * col_width + def _auto_format_func(format_, val): + """Format ``val`` according to ``format_`` for a plain format specifier, + old- or new-style format strings, or using a user supplied function. + More importantly, determine and cache (in _format_funcs) a function + that will do this subsequently. In this way this complicated logic is + only done for the first value. - # Now bring all the column string values to the same fixed width - for i, col_str in enumerate(col_strs): - col_strs[i] = col_str.rjust(col_width) + Returns the formatted value. + """ + if format_ is None: + return default_format_func(format_, val) - return col_strs, n_header + if format_ in col.info._format_funcs: + return col.info._format_funcs[format_](format_, val) + if callable(format_): + format_func = lambda format_, val: format_(val) + try: + out = format_func(format_, val) + if not isinstance(out, str): + raise ValueError( + f"Format function for value {val} returned {type(val)} " + "instead of string type" + ) + except Exception as err: + # For a masked element, the format function call likely failed + # to handle it. Just return the string representation for now, + # and retry when a non-masked value comes along. + if val is np.ma.masked: + return str(val) + + raise ValueError(f"Format function for value {val} failed.") from err + # If the user-supplied function handles formatting masked elements, use + # it directly. Otherwise, wrap it in a function that traps them. + try: + format_func(format_, np.ma.masked) + except Exception: + format_func = _use_str_for_masked_values(format_func) + else: + # For a masked element, we cannot set string-based format functions yet, + # as all tests below will fail. Just return the string representation + # of masked for now, and retry when a non-masked value comes along. + if val is np.ma.masked: + return str(val) + + for format_func in possible_string_format_functions(format_): + try: + # Does this string format method work? + out = format_func(format_, val) + # Require that the format statement actually did something. + if out == format_: + raise ValueError("the format passed in did nothing.") + except Exception: + continue + else: + break + else: + # None of the possible string functions passed muster. + raise ValueError( + f"unable to parse format string {format_} for its column." + ) -def _pformat_table(table, max_lines=None, max_width=None, show_name=True, - show_units=False): - """Return a list of lines for the formatted string representation of - the table. - - Parameters - ---------- - max_lines : int or None - Maximum number of rows to output + # String-based format functions will fail on masked elements; + # wrap them in a function that traps them. + format_func = _use_str_for_masked_values(format_func) - max_width : int or None - Maximum character width of output + col.info._format_funcs[format_] = format_func + return out - show_name : bool - Include a header row for column names (default=True) + return _auto_format_func - show_units : bool - Include a header row for units (default=False) - Returns - ------- - out : str - Formatted table as a single string +def _get_pprint_include_names(table): + """Get the set of names to show in pprint from the table pprint_include_names + and pprint_exclude_names attributes. - n_header : int - Number of lines in the header + These may be fnmatch unix-style globs. """ - # "Print" all the values into temporary lists by column for subsequent - # use and to determine the width - max_lines, max_width = _get_pprint_size(max_lines, max_width) - cols = [] - for col in table.columns.values(): - lines, n_header = _pformat_col(col, max_lines, show_name, - show_units) - cols.append(lines) - - if not cols: - return [] - - n_rows = len(cols[0]) - outwidth = lambda cols: sum(len(c[0]) for c in cols) + len(cols) - 1 - dots_col = ['...'] * n_rows - middle = len(cols) // 2 - while outwidth(cols) > max_width: - if len(cols) == 1: - break - if len(cols) == 2: - cols[1] = dots_col - break - if cols[middle] is dots_col: - cols.pop(middle) - middle = len(cols) // 2 - cols[middle] = dots_col - - # Now "print" the (already-stringified) column values into a - # row-oriented list. - rows = [] - for i in range(n_rows): - row = ' '.join(col[i] for col in cols) - rows.append(row) - - return rows, n_header - - -def _more_tabcol(tabcol, max_lines=None, max_width=None, show_name=True, - show_units=False): - """Interactive "more" of a table or column. - Parameters - ---------- - max_lines : int or None - Maximum number of rows to output + def get_matches(name_globs, default): + match_names = set() + if name_globs: # For None or () use the default + for name in table.colnames: + for name_glob in name_globs: + if fnmatch.fnmatch(name, name_glob): + match_names.add(name) + break + else: + match_names.update(default) + return match_names + + include_names = get_matches(table.pprint_include_names(), table.colnames) + exclude_names = get_matches(table.pprint_exclude_names(), []) + + return include_names - exclude_names + + +class TableFormatter: + @staticmethod + def _get_pprint_size(max_lines=None, max_width=None): + """Get the output size (number of lines and character width) for Column and + Table pformat/pprint methods. + + If no value of ``max_lines`` is supplied then the height of the + screen terminal is used to set ``max_lines``. If the terminal + height cannot be determined then the default will be determined + using the ``astropy.console.max_lines`` console configuration item. If a + negative value of ``max_lines`` is supplied then there is no line + limit applied. + + The same applies for max_width except the configuration item is + ``astropy.console.conf.max_width``. + + Parameters + ---------- + max_lines : int or None + Maximum lines of output (header + data rows) + + max_width : int or None + Maximum width (characters) output + + Returns + ------- + max_lines, max_width : int + + """ + # Declare to keep static type checker happy. + lines = None + width = None + + if max_lines is None: + max_lines = conf.max_lines + + if max_width is None: + max_width = conf.max_width + + if max_lines is None or max_width is None: + width, lines = get_terminal_size() + + if max_lines is None: + max_lines = lines + elif max_lines < 0: + max_lines = sys.maxsize + if max_lines < 8: + max_lines = 8 + + if max_width is None: + max_width = width + elif max_width < 0: + max_width = sys.maxsize + if max_width < 10: + max_width = 10 + + return max_lines, max_width + + def _pformat_col( + self, + col, + max_lines=None, + show_name=True, + show_unit=None, + show_dtype=False, + show_length=None, + html=False, + align=None, + ): + """Return a list of formatted string representation of column values. + + Parameters + ---------- + max_lines : int + Maximum lines of output (header + data rows) + + show_name : bool + Include column name. Default is True. + + show_unit : bool + Include a header row for unit. Default is to show a row + for units only if one or more columns has a defined value + for the unit. + + show_dtype : bool + Include column dtype. Default is False. + + show_length : bool + Include column length at end. Default is to show this only + if the column is not shown completely. + + html : bool + Output column as HTML + + align : str + Left/right alignment of columns. Default is '>' (right) for all + columns. Other allowed values are '<', '^', and '0=' for left, + centered, and 0-padded, respectively. + + Returns + ------- + lines : list + List of lines with formatted column values + + outs : dict + Dict which is used to pass back additional values + defined within the iterator. + + """ + if show_unit is None: + show_unit = col.info.unit is not None + + outs = {} # Some values from _pformat_col_iter iterator that are needed here + col_strs_iter = self._pformat_col_iter( + col, + max_lines, + show_name=show_name, + show_unit=show_unit, + show_dtype=show_dtype, + show_length=show_length, + outs=outs, + ) + + # Replace tab and newline with text representations so they display nicely. + # Newline in particular is a problem in a multicolumn table. + col_strs = [ + val.replace("\t", "\\t").replace("\n", "\\n") for val in col_strs_iter + ] + if len(col_strs) > 0: + col_width = max(len(x) for x in col_strs) + + if html: + from astropy.utils.xml.writer import xml_escape + + n_header = outs["n_header"] + for i, col_str in enumerate(col_strs): + # _pformat_col output has a header line '----' which is not needed here + if i == n_header - 1: + continue + td = "th" if i < n_header else "td" + val = f"<{td}>{xml_escape(col_str.strip())}" + row = "" + val + "" + if i < n_header: + row = "" + row + "" + col_strs[i] = row + + if n_header > 0: + # Get rid of '---' header line + col_strs.pop(n_header - 1) + col_strs.insert(0, "
") + col_strs.append("
") + + # Now bring all the column string values to the same fixed width + else: + col_width = max(len(x) for x in col_strs) if col_strs else 1 + + # Center line header content and generate dashed headerline + for i in outs["i_centers"]: + col_strs[i] = col_strs[i].center(col_width) + if outs["i_dashes"] is not None: + col_strs[outs["i_dashes"]] = "-" * col_width + + # Format columns according to alignment. `align` arg has precedence, otherwise + # use `col.format` if it starts as a legal alignment string. If neither applies + # then right justify. + re_fill_align = re.compile(r"(?P.?)(?P[<^>=])") + match = None + if align: + # If there is an align specified then it must match + match = re_fill_align.match(align) + if not match: + raise ValueError( + "column align must be one of '<', '^', '>', or '='" + ) + elif isinstance(col.info.format, str): + # col.info.format need not match, in which case rjust gets used + match = re_fill_align.match(col.info.format) + + if match: + fill_char = match.group("fill") + align_char = match.group("align") + if align_char == "=": + if fill_char != "0": + raise ValueError("fill character must be '0' for '=' align") + # str.zfill gets used which does not take fill char arg + fill_char = "" + else: + fill_char = "" + align_char = ">" + + justify_methods = {"<": "ljust", "^": "center", ">": "rjust", "=": "zfill"} + justify_method = justify_methods[align_char] + justify_args = (col_width, fill_char) if fill_char else (col_width,) + + for i, col_str in enumerate(col_strs): + col_strs[i] = getattr(col_str, justify_method)(*justify_args) + + if outs["show_length"]: + col_strs.append(f"Length = {len(col)} rows") + + return col_strs, outs + + def _name_and_structure(self, name, dtype, sep=" "): + """Format a column name, including a possible structure. + + Normally, just returns the name, but if it has a structured dtype, + will add the parts in between square brackets. E.g., + "name [f0, f1]" or "name [f0[sf0, sf1], f1]". + """ + if dtype is None or dtype.names is None: + return name + + structure = ", ".join( + [ + self._name_and_structure(name, dt, sep="") + for name, (dt, _) in dtype.fields.items() + ] + ) + return f"{name}{sep}[{structure}]" + + def _pformat_col_iter( + self, + col, + max_lines, + show_name, + show_unit, + outs, + show_dtype=False, + show_length=None, + ): + """Iterator which yields formatted string representation of column values. + + Parameters + ---------- + max_lines : int + Maximum lines of output (header + data rows) + + show_name : bool + Include column name. Default is True. + + show_unit : bool + Include a header row for unit. Default is to show a row + for units only if one or more columns has a defined value + for the unit. + + outs : dict + Must be a dict which is used to pass back additional values + defined within the iterator. + + show_dtype : bool + Include column dtype. Default is False. + + show_length : bool + Include column length at end. Default is to show this only + if the column is not shown completely. + """ + max_lines, _ = self._get_pprint_size(max_lines, -1) + dtype = getattr(col, "dtype", None) + multidims = getattr(col, "shape", [0])[1:] + if multidims: + multidim0 = tuple(0 for n in multidims) + multidim1 = tuple(n - 1 for n in multidims) + multidims_all_ones = np.prod(multidims) == 1 + multidims_has_zero = 0 in multidims + + i_dashes = None + i_centers = [] # Line indexes where content should be centered + n_header = 0 + if show_name: + i_centers.append(n_header) + # Get column name (or 'None' if not set) + col_name = str(col.info.name) + n_header += 1 + yield self._name_and_structure(col_name, dtype) + if show_unit: + i_centers.append(n_header) + n_header += 1 + yield str(col.info.unit or "") + if show_dtype: + i_centers.append(n_header) + n_header += 1 + if dtype is not None: + # For zero-length strings, np.dtype((dtype, ())) does not work; + # see https://github.com/numpy/numpy/issues/27301 + # As a work-around, just omit the shape if there is none. + col_dtype = dtype_info_name((dtype, multidims) if multidims else dtype) + else: + col_dtype = col.__class__.__qualname__ or "object" + yield col_dtype + if show_unit or show_name or show_dtype: + i_dashes = n_header + n_header += 1 + yield "---" + + max_lines -= n_header + n_print2 = max_lines // 2 + try: + n_rows = len(col) + except TypeError: + is_scalar = True + n_rows = 1 + else: + is_scalar = False + + # This block of code is responsible for producing the function that + # will format values for this column. The ``format_func`` function + # takes two args (col_format, val) and returns the string-formatted + # version. Some points to understand: + # + # - col_format could itself be the formatting function, so it will + # actually end up being called with itself as the first arg. In + # this case the function is expected to ignore its first arg. + # + # - auto_format_func is a function that gets called on the first + # column value that is being formatted. It then determines an + # appropriate formatting function given the actual value to be + # formatted. This might be deterministic or it might involve + # try/except. The latter allows for different string formatting + # options like %f or {:5.3f}. When auto_format_func is called it: + + # 1. Caches the function in the _format_funcs dict so for subsequent + # values the right function is called right away. + # 2. Returns the formatted value. + # + # - possible_string_format_functions is a function that yields a + # succession of functions that might successfully format the + # value. There is a default, but Mixin methods can override this. + # See Quantity for an example. + # + # - get_auto_format_func() returns a wrapped version of auto_format_func + # with the column id and possible_string_format_functions as + # enclosed variables. + col_format = col.info.format or getattr(col.info, "default_format", None) + pssf = ( + getattr(col.info, "possible_string_format_functions", None) + or _possible_string_format_functions + ) + auto_format_func = get_auto_format_func(col, pssf) + format_func = col.info._format_funcs.get(col_format, auto_format_func) + + if n_rows > max_lines: + if show_length is None: + show_length = True + i0 = n_print2 - (1 if show_length else 0) + i1 = n_rows - n_print2 - max_lines % 2 + indices = np.concatenate([np.arange(0, i0 + 1), np.arange(i1 + 1, n_rows)]) + else: + i0 = -1 + indices = np.arange(n_rows) + + def format_col_str(idx): + if not multidims: + return format_func(col_format, col if is_scalar else col[idx]) + + # Prevents columns like Column(data=[[(1,)],[(2,)]], name='a') + # with shape (n,1,...,1) from being printed as if there was + # more than one element in a row + if multidims_all_ones: + return format_func(col_format, col[(idx,) + multidim0]) + + if multidims_has_zero: + # Any zero dimension means there is no data to print + return "" + + size = np.prod(multidims) + # Uses astropy.table.conf.format_size_threshold for multidimensional column formatting + threshold = table.conf.format_size_threshold + + # If size > threshold, revert to legacy "first .. last" format + if size > threshold: + left = format_func(col_format, col[(idx,) + multidim0]) + right = format_func(col_format, col[(idx,) + multidim1]) + result = f"{left} .. {right}" + else: - max_width : int or None - Maximum character width of output + def format_multidim(arr): + """Recursively format multidimensional arrays.""" + if arr.ndim == 0: + return format_func(col_format, arr) + else: + return "[" + " ".join(format_multidim(val) for val in arr) + "]" - show_name : bool - Include a header row for column names (default=True) + result = format_multidim(col[idx]) - show_units : bool - Include a header row for units (default=False) - """ - allowed_keys = 'f br<>qhpn' - - # Count the header lines - n_header = 0 - if show_name: - n_header += 1 - if show_units: - n_header += 1 - if show_name or show_units: - n_header += 1 - - # Set up kwargs for pformat call. Only Table gets max_width. - kwargs = dict(max_lines=-1, show_name=show_name, show_units=show_units) - if hasattr(tabcol, 'columns'): # tabcol is a table - kwargs['max_width'] = max_width - - # If max_lines is None (=> query screen size) then increase by 2. - # This is because get_pprint_size leaves 6 extra lines so that in - # ipython you normally see the last input line. - max_lines1, max_width = _get_pprint_size(max_lines, max_width) - if max_lines == None: - max_lines1 += 2 - delta_lines = max_lines1 - n_header - - # Set up a function to get a single character on any platform - inkey = Getch() - - i0 = 0 # First table/column row to show - showlines = True - while True: - i1 = i0 + delta_lines # Last table/col row to show - if showlines: # Don't always show the table (e.g. after help) - try: - os.system('cls' if os.name == 'nt' else 'clear') - except: - pass # No worries if clear screen call fails - lines = tabcol[i0:i1].pformat(**kwargs) - colors = ('red' if i < n_header else 'default' - for i in xrange(len(lines))) - for color, line in izip(colors, lines): - color_print(line, color) + return result + + # Add formatted values if within bounds allowed by max_lines + for idx in indices: + if idx == i0: + yield "..." + else: + try: + yield format_col_str(idx) + except ValueError: + raise ValueError( + f'Unable to parse format string "{col_format}" for ' + f'entry "{col[idx]}" in column "{col.info.name}" ' + f'with datatype "{col.info.dtype}".\n' + "See https://docs.astropy.org/en/stable/table/construct_table.html#format-specifier " + "for possible format specifications." + ) + + outs["show_length"] = show_length + outs["n_header"] = n_header + outs["i_centers"] = i_centers + outs["i_dashes"] = i_dashes + + def _pformat_table( + self, + table, + max_lines=-1, + max_width=-1, + show_name=True, + show_unit=None, + show_dtype=False, + html=False, + tableid=None, + tableclass=None, + align=None, + ): + """Return a list of lines for the formatted string representation of + the table. + + Parameters + ---------- + max_lines : int or None + Maximum number of rows to output + -1 (default) implies no limit, ``None`` implies using the + height of the current terminal. + + max_width : int or None + Maximum character width of output + -1 (default) implies no limit, ``None`` implies using the + width of the current terminal. + + show_name : bool + Include a header row for column names. Default is True. + + show_unit : bool + Include a header row for unit. Default is to show a row + for units only if one or more columns has a defined value + for the unit. + + show_dtype : bool + Include a header row for column dtypes. Default is to False. + + html : bool + Format the output as an HTML table. Default is False. + + tableid : str or None + An ID tag for the table; only used if html is set. Default is + "table{id}", where id is the unique integer id of the table object, + id(table) + + tableclass : str or list of str or None + CSS classes for the table; only used if html is set. Default is + none + + align : str or list or tuple + Left/right alignment of columns. Default is '>' (right) for all + columns. Other allowed values are '<', '^', and '0=' for left, + centered, and 0-padded, respectively. A list of strings can be + provided for alignment of tables with multiple columns. + + Returns + ------- + rows : list + Formatted table as a list of strings + + outs : dict + Dict which is used to pass back additional values + defined within the iterator. + + """ + # "Print" all the values into temporary lists by column for subsequent + # use and to determine the width + max_lines, max_width = self._get_pprint_size(max_lines, max_width) + + if show_unit is None: + show_unit = any(col.info.unit for col in table.columns.values()) + + # Coerce align into a correctly-sized list of alignments (if possible) + n_cols = len(table.columns) + if align is None or isinstance(align, str): + align = [align] * n_cols + + elif isinstance(align, (list, tuple)): + if len(align) != n_cols: + raise ValueError( + f"got {len(align)} alignment values instead of " + f"the number of columns ({n_cols})" + ) + else: + raise TypeError( + f"align keyword must be str or list or tuple (got {type(align)})" + ) + + # Process column visibility from table pprint_include_names and + # pprint_exclude_names attributes and get the set of columns to show. + pprint_include_names = _get_pprint_include_names(table) + + cols = [] + outs = None # Initialize so static type checker is happy + for align_, col in zip(align, table.columns.values()): + if col.info.name not in pprint_include_names: + continue + + lines, outs = self._pformat_col( + col, + max_lines, + show_name=show_name, + show_unit=show_unit, + show_dtype=show_dtype, + align=align_, + ) + if outs["show_length"]: + lines = lines[:-1] + cols.append(lines) + + if not cols: + return [""], {"show_length": False} + + # Use the values for the last column since they are all the same + n_header = outs["n_header"] + + n_rows = len(cols[0]) + + def outwidth(cols): + return sum(len(c[0]) for c in cols) + len(cols) - 1 + + dots_col = ["..."] * n_rows + middle = len(cols) // 2 + while outwidth(cols) > max_width: + if len(cols) == 1: + break + if len(cols) == 2: + cols[1] = dots_col + break + if cols[middle] is dots_col: + cols.pop(middle) + middle = len(cols) // 2 + cols[middle] = dots_col + + # Now "print" the (already-stringified) column values into a + # row-oriented list. + rows = [] + if html: + from astropy.utils.xml.writer import xml_escape + + if tableid is None: + tableid = f"table{id(table)}" + + if tableclass is not None: + if isinstance(tableclass, list): + tableclass = " ".join(tableclass) + rows.append(f'') + else: + rows.append(f'
') + + for i in range(n_rows): + # _pformat_col output has a header line '----' which is not needed here + if i == n_header - 1: + continue + td = "th" if i < n_header else "td" + vals = (f"<{td}>{xml_escape(col[i].strip())}" for col in cols) + row = "" + "".join(vals) + "" + if i < n_header: + row = "" + row + "" + rows.append(row) + rows.append("
") + else: + for i in range(n_rows): + row = " ".join(col[i] for col in cols) + rows.append(row) + + return rows, outs + + def _more_tabcol( + self, + tabcol, + max_lines=None, + max_width=None, + show_name=True, + show_unit=None, + show_dtype=False, + ): + """Interactive "more" of a table or column. + + Parameters + ---------- + max_lines : int or None + Maximum number of rows to output + + max_width : int or None + Maximum character width of output + + show_name : bool + Include a header row for column names. Default is True. + + show_unit : bool + Include a header row for unit. Default is to show a row + for units only if one or more columns has a defined value + for the unit. + + show_dtype : bool + Include a header row for column dtypes. Default is False. + """ + allowed_keys = "f br<>qhpn" + + # Count the header lines + n_header = 0 + if show_name: + n_header += 1 + if show_unit: + n_header += 1 + if show_dtype: + n_header += 1 + if show_name or show_unit or show_dtype: + n_header += 1 + + # Set up kwargs for pformat call. Only Table gets max_width. + kwargs = dict( + max_lines=-1, + show_name=show_name, + show_unit=show_unit, + show_dtype=show_dtype, + ) + if hasattr(tabcol, "columns"): # tabcol is a table + kwargs["max_width"] = max_width + + # If max_lines is None (=> query screen size) then increase by 2. + # This is because get_pprint_size leaves 6 extra lines so that in + # ipython you normally see the last input line. + max_lines1, max_width = self._get_pprint_size(max_lines, max_width) + if max_lines is None: + max_lines1 += 2 + delta_lines = max_lines1 - n_header + + # Set up a function to get a single character on any platform + inkey = Getch() + + i0 = 0 # First table/column row to show showlines = True - print - print "-- f, , b, r, p, n, <, >, q h (help) --", - # Get a valid key while True: - try: - key = inkey().lower() - except: - print "\n" - log.error('Console does not support getting a character' - ' as required by more(). Use pprint() instead.') - return - if key in allowed_keys: + i1 = i0 + delta_lines # Last table/col row to show + if showlines: # Don't always show the table (e.g. after help) + try: + os.system("cls" if os.name == "nt" else "clear") + except Exception: + pass # No worries if clear screen call fails + lines = tabcol[i0:i1].pformat(**kwargs) + colors = ( + "red" if i < n_header else "default" for i in range(len(lines)) + ) + for color, line in zip(colors, lines): + color_print(line, color) + showlines = True + print() + print("-- f, , b, r, p, n, <, >, q h (help) --", end=" ") + # Get a valid key + while True: + try: + key = inkey().lower() + except Exception: + print("\n") + log.error( + "Console does not support getting a character" + " as required by more(). Use pprint() instead." + ) + return + if key in allowed_keys: + break + print(key) + + if key.lower() == "q": break - print key - - if key.lower() == 'q': - break - elif key == ' ' or key == 'f': - i0 += delta_lines - elif key == 'b': - i0 = i0 - delta_lines - elif key == 'r': - pass - elif key == '<': - i0 = 0 - elif key == '>': - i0 = len(tabcol) - elif key == 'p': - i0 -= 1 - elif key == 'n': - i0 += 1 - elif key == 'h': - showlines = False - print """ -Browsing keys: - f, : forward one page - b : back one page - r : refresh same page - n : next row - p : previous row - < : go to beginning - > : go to end - q : quit browsing - h : print this help""", - if i0 < 0: - i0 = 0 - if i0 >= len(tabcol) - delta_lines: - i0 = len(tabcol) - delta_lines - print "\n" + + if key == " " or key == "f": + i0 += delta_lines + elif key == "b": + i0 = i0 - delta_lines + elif key == "r": + pass + elif key == "<": + i0 = 0 + elif key == ">": + i0 = len(tabcol) + elif key == "p": + i0 -= 1 + elif key == "n": + i0 += 1 + elif key == "h": + showlines = False + print( + """ + Browsing keys: + f, : forward one page + b : back one page + r : refresh same page + n : next row + p : previous row + < : go to beginning + > : go to end + q : quit browsing + h : print this help""", + end=" ", + ) + if i0 < 0: + i0 = 0 + if i0 >= len(tabcol) - delta_lines: + i0 = len(tabcol) - delta_lines + print("\n") diff --git a/astropy/table/row.py b/astropy/table/row.py new file mode 100644 index 000000000000..255a765f6247 --- /dev/null +++ b/astropy/table/row.py @@ -0,0 +1,224 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +import collections +from collections import OrderedDict +from operator import index as operator_index + +import numpy as np + + +class Row: + """A class to represent one row of a Table object. + + A Row object is returned when a Table object is indexed with an integer + or when iterating over a table:: + + >>> from astropy.table import Table + >>> table = Table([(1, 2), (3, 4)], names=('a', 'b'), + ... dtype=('int32', 'int32')) + >>> row = table[1] + >>> row + + a b + int32 int32 + ----- ----- + 2 4 + >>> row['a'] + np.int32(2) + >>> row[1] + np.int32(4) + """ + + def __init__(self, table, index): + # Ensure that the row index is a valid index (int) + index = operator_index(index) + + n = len(table) + + if index < -n or index >= n: + raise IndexError( + f"index {index} out of range for table with length {len(table)}" + ) + + # Finally, ensure the index is positive [#8422] and set Row attributes + self._index = index % n + self._table = table + + def __getitem__(self, item): + try: + # Try the most common use case of accessing a single column in the Row. + # Bypass the TableColumns __getitem__ since that does more testing + # and allows a list of tuple or str, which is not the right thing here. + out = OrderedDict.__getitem__(self._table.columns, item)[self._index] + except (KeyError, TypeError): + if self._table._is_list_or_tuple_of_str(item): + cols = [self._table[name] for name in item] + out = self._table.__class__(cols, copy=False)[self._index] + elif isinstance(item, slice): + # https://github.com/astropy/astropy/issues/14007 + out = tuple(self.values())[item] + else: + # This is only to raise an exception + out = self._table.columns[item][self._index] + return out + + def __setitem__(self, item, val): + if self._table._is_list_or_tuple_of_str(item): + self._table._set_row(self._index, colnames=item, vals=val) + else: + self._table.columns[item][self._index] = val + + def _ipython_key_completions_(self): + return self.colnames + + def __eq__(self, other): + if self._table.masked: + # Sent bug report to numpy-discussion group on 2012-Oct-21, subject: + # "Comparing rows in a structured masked array raises exception" + # No response, so this is still unresolved. + raise ValueError( + "Unable to compare rows for masked table due to numpy.ma bug" + ) + return self.as_void() == other + + def __ne__(self, other): + if self._table.masked: + raise ValueError( + "Unable to compare rows for masked table due to numpy.ma bug" + ) + return self.as_void() != other + + def __array__(self, dtype=None, copy=None): + """Support converting Row to np.array via np.array(table). + + Coercion to a different dtype via np.array(table, dtype) is not + supported and will raise a ValueError. + + If the parent table is masked then the mask information is dropped. + """ + if dtype is not None: + raise ValueError("Datatype coercion is not allowed") + + return np.array(self.as_void(), copy=copy) + + def __len__(self): + return len(self._table.columns) + + def __iter__(self): + index = self._index + for col in self._table.columns.values(): + yield col[index] + + def get(self, key, default=None, /): + """Return the value for key if key is in the columns, else default. + + Parameters + ---------- + key : `str`, positional-only + The name of the column to look for. + default : `object`, optional, positional-only + The value to return if the ``key`` is not among the columns. + + Returns + ------- + `object` + The value in the ``key`` column of the row if present, + ``default`` otherwise. + + Examples + -------- + >>> from astropy.table import Table + >>> t = Table({"a": [2., 3., 5.], "b": [7., 11., 13.]}) + >>> t[0].get("a") + np.float64(2.0) + >>> t[1].get("b", 0.) + np.float64(11.0) + >>> t[2].get("c", 0.) + 0.0 + """ + return self[key] if key in self._table.columns else default + + def keys(self): + return self._table.columns.keys() + + def values(self): + return self.__iter__() + + @property + def table(self): + return self._table + + @property + def index(self): + return self._index + + def as_void(self): + """ + Returns a *read-only* copy of the row values in the form of np.void or + np.ma.mvoid objects. This corresponds to the object types returned for + row indexing of a pure numpy structured array or masked array. This + method is slow and its use is discouraged when possible. + + Returns + ------- + void_row : ``numpy.void`` or ``numpy.ma.mvoid`` + Copy of row values. + ``numpy.void`` if unmasked, ``numpy.ma.mvoid`` else. + """ + index = self._index + cols = self._table.columns.values() + vals = tuple(np.asarray(col)[index] for col in cols) + if self._table.masked: + mask = tuple( + col.mask[index] if hasattr(col, "mask") else False for col in cols + ) + void_row = np.ma.array([vals], mask=[mask], dtype=self.dtype)[0] + else: + void_row = np.array([vals], dtype=self.dtype)[0] + return void_row + + @property + def meta(self): + return self._table.meta + + @property + def columns(self): + return self._table.columns + + @property + def colnames(self): + return self._table.colnames + + @property + def dtype(self): + return self._table.dtype + + def _base_repr_(self, html=False): + """ + Display row as a single-line table but with appropriate header line. + """ + index = self.index if (self.index >= 0) else self.index + len(self._table) + table = self._table[index : index + 1] + descr_vals = [self.__class__.__name__, f"index={self.index}"] + if table.masked: + descr_vals.append("masked=True") + + return table._base_repr_( + html, descr_vals, max_width=-1, tableid=f"table{id(self._table)}" + ) + + def _repr_html_(self): + return self._base_repr_(html=True) + + def __repr__(self): + return self._base_repr_(html=False) + + def __str__(self): + index = self.index if (self.index >= 0) else self.index + len(self._table) + return "\n".join(self.table[index : index + 1].pformat(max_width=-1)) + + def __bytes__(self): + return str(self).encode("utf-8") + + +collections.abc.Sequence.register(Row) diff --git a/astropy/table/scripts/__init__.py b/astropy/table/scripts/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/astropy/table/scripts/showtable.py b/astropy/table/scripts/showtable.py new file mode 100644 index 000000000000..fe75511fd51c --- /dev/null +++ b/astropy/table/scripts/showtable.py @@ -0,0 +1,198 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +""" +``showtable-astropy`` is a command-line script based on ``astropy.io`` and +``astropy.table`` for printing ASCII, FITS, HDF5 or VOTable files(s) to the +standard output. + +Example usage of ``showtable-astropy``: + +1. FITS:: + + $ showtable-astropy astropy/io/fits/tests/data/table.fits + + target V_mag + ------- ----- + NGC1001 11.1 + NGC1002 12.3 + NGC1003 15.2 + +2. ASCII:: + + $ showtable-astropy astropy/io/ascii/tests/t/simple_csv.csv + + a b c + --- --- --- + 1 2 3 + 4 5 6 + +3. XML:: + + $ showtable-astropy astropy/io/votable/tests/data/names.xml --max-width 70 + + col1 col2 col3 ... col15 col16 col17 + --- deg deg ... mag mag --- + ------------------------- -------- ------- ... ----- ----- ----- + SSTGLMC G000.0000+00.1611 0.0000 0.1611 ... -- -- AA + + + +4. Print all the FITS tables in the current directory:: + + $ showtable-astropy *.fits + +""" + +import argparse +import textwrap +import warnings + +from astropy import log +from astropy.table import Table +from astropy.utils.decorators import deprecated +from astropy.utils.exceptions import AstropyUserWarning + + +def showtable(filename, args): + """ + Read a table and print to the standard output. + + Parameters + ---------- + filename : str + The path to a FITS file. + + """ + if args.info and args.stats: + warnings.warn("--info and --stats cannot be used together", AstropyUserWarning) + if any((args.max_lines, args.max_width, args.hide_unit, args.show_dtype)) and ( + args.info or args.stats + ): + warnings.warn( + "print parameters are ignored if --info or --stats is used", + AstropyUserWarning, + ) + + # these parameters are passed to Table.read if they are specified in the + # command-line + read_kwargs = ("hdu", "format", "table_id", "delimiter") + kwargs = {k: v for k, v in vars(args).items() if k in read_kwargs and v is not None} + try: + table = Table.read(filename, **kwargs) + if args.info: + table.info("attributes") + elif args.stats: + table.info("stats") + else: + formatter = table.more if args.more else table.pprint + formatter( + max_lines=args.max_lines, + max_width=args.max_width, + show_unit=(False if args.hide_unit else None), + show_dtype=(True if args.show_dtype else None), + ) + except OSError as e: + log.error(str(e)) + + +def main(args=None): + """The main function called by the ``showtable-astropy`` script.""" + parser = argparse.ArgumentParser( + description=textwrap.dedent( + """ + Print tables from ASCII, FITS, HDF5, VOTable file(s). The tables + are read with 'astropy.table.Table.read' and are printed with + 'astropy.table.Table.pprint'. The default behavior is to make the + table output fit onto a single screen page. For a long and wide + table this will mean cutting out inner rows and columns. To print + **all** the rows or columns use ``--max-lines=-1`` or + ``max-width=-1``, respectively. The complete list of supported + formats can be found at + http://astropy.readthedocs.io/en/latest/io/unified.html#built-in-table-readers-writers + """ + ) + ) + # TODO: pass suggest_on_error as kwarg when PYTHON_LT_14 is dropped + parser.suggest_on_error = True + + addarg = parser.add_argument + addarg("filename", nargs="+", help="path to one or more files") + + addarg( + "--format", + help=( + "input table format, should be specified if it " + "cannot be automatically detected" + ), + ) + addarg("--more", action="store_true", help="use the pager mode from Table.more") + addarg( + "--info", action="store_true", help="show information about the table columns" + ) + addarg( + "--stats", action="store_true", help="show statistics about the table columns" + ) + + # pprint arguments + pprint_args = parser.add_argument_group("pprint arguments") + addarg = pprint_args.add_argument + addarg( + "--max-lines", + type=int, + help=( + "maximum number of lines in table output (default=screen " + "length, -1 for no limit)" + ), + ) + addarg( + "--max-width", + type=int, + help="maximum width in table output (default=screen width, -1 for no limit)", + ) + addarg( + "--hide-unit", + action="store_true", + help=( + "hide the header row for unit (which is shown " + "only if one or more columns has a unit)" + ), + ) + addarg( + "--show-dtype", + action="store_true", + help=( + "always include a header row for column dtypes " + "(otherwise shown only if any column is multidimensional)" + ), + ) + + # ASCII-specific arguments + ascii_args = parser.add_argument_group("ASCII arguments") + addarg = ascii_args.add_argument + addarg("--delimiter", help="column delimiter string") + + # FITS-specific arguments + fits_args = parser.add_argument_group("FITS arguments") + addarg = fits_args.add_argument + addarg("--hdu", help="name of the HDU to show") + + # HDF5-specific arguments + hdf5_args = parser.add_argument_group("HDF5 arguments") + addarg = hdf5_args.add_argument + addarg("--path", help="the path from which to read the table") + + # VOTable-specific arguments + votable_args = parser.add_argument_group("VOTable arguments") + addarg = votable_args.add_argument + addarg("--table-id", help="the table to read in") + + args = parser.parse_args(args) + + for idx, filename in enumerate(args.filename): + if idx > 0: + print() + showtable(filename, args) + + +@deprecated("v7.1", name="showtable", alternative="showtable-astropy") +def main_deprecated(args=None): + main(args) diff --git a/astropy/table/serialize.py b/astropy/table/serialize.py new file mode 100644 index 000000000000..048f6ae55e66 --- /dev/null +++ b/astropy/table/serialize.py @@ -0,0 +1,474 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +from collections import OrderedDict +from copy import deepcopy +from importlib import import_module + +import numpy as np + +from astropy.units.quantity import QuantityInfo +from astropy.utils.data_info import MixinInfo + +from .column import Column, MaskedColumn +from .table import QTable, Table, has_info_class + +# TODO: some of this might be better done programmatically, through +# code like +# __construct_mixin_classes += tuple( +# f'astropy.coordinates.representation.{cls.__name__}' +# for cls in (list(coorep.REPRESENTATION_CLASSES.values()) +# + list(coorep.DIFFERENTIAL_CLASSES.values())) +# if cls.__name__ in coorep.__all__) +# However, to avoid very hard to track import issues, the definition +# should then be done at the point where it is actually needed, +# using local imports. See also +# https://github.com/astropy/astropy/pull/10210#discussion_r419087286 +__construct_mixin_classes = ( + "astropy.time.core.Time", + "astropy.time.core.TimeDelta", + "astropy.units.quantity.Quantity", + "astropy.units.function.logarithmic.Magnitude", + "astropy.units.function.logarithmic.Decibel", + "astropy.units.function.logarithmic.Dex", + "astropy.coordinates.distances.Distance", + "astropy.coordinates.earth.EarthLocation", + "astropy.coordinates.sky_coordinate.SkyCoord", + "astropy.coordinates.polarization.StokesCoord", + "astropy.table.ndarray_mixin.NdarrayMixin", + "astropy.table.table_helpers.ArrayWrapper", + "astropy.table.column.Column", + "astropy.table.column.MaskedColumn", + "astropy.utils.masked.core.MaskedNDArray", + "astropy.utils.masked.core.MaskedRecarray", + # Angles + "astropy.coordinates.angles.core.Latitude", + "astropy.coordinates.angles.core.Longitude", + "astropy.coordinates.angles.core.Angle", + # Representations + "astropy.coordinates.representation.cartesian.CartesianRepresentation", + "astropy.coordinates.representation.spherical.UnitSphericalRepresentation", + "astropy.coordinates.representation.spherical.RadialRepresentation", + "astropy.coordinates.representation.spherical.SphericalRepresentation", + "astropy.coordinates.representation.spherical.PhysicsSphericalRepresentation", + "astropy.coordinates.representation.cylindrical.CylindricalRepresentation", + "astropy.coordinates.representation.cartesian.CartesianDifferential", + "astropy.coordinates.representation.spherical.UnitSphericalDifferential", + "astropy.coordinates.representation.spherical.SphericalDifferential", + "astropy.coordinates.representation.spherical.UnitSphericalCosLatDifferential", + "astropy.coordinates.representation.spherical.SphericalCosLatDifferential", + "astropy.coordinates.representation.spherical.RadialDifferential", + "astropy.coordinates.representation.spherical.PhysicsSphericalDifferential", + "astropy.coordinates.representation.cylindrical.CylindricalDifferential", + # Deprecated paths + "astropy.coordinates.representation.CartesianRepresentation", + "astropy.coordinates.representation.UnitSphericalRepresentation", + "astropy.coordinates.representation.RadialRepresentation", + "astropy.coordinates.representation.SphericalRepresentation", + "astropy.coordinates.representation.PhysicsSphericalRepresentation", + "astropy.coordinates.representation.CylindricalRepresentation", + "astropy.coordinates.representation.CartesianDifferential", + "astropy.coordinates.representation.UnitSphericalDifferential", + "astropy.coordinates.representation.SphericalDifferential", + "astropy.coordinates.representation.UnitSphericalCosLatDifferential", + "astropy.coordinates.representation.SphericalCosLatDifferential", + "astropy.coordinates.representation.RadialDifferential", + "astropy.coordinates.representation.PhysicsSphericalDifferential", + "astropy.coordinates.representation.CylindricalDifferential", + "astropy.coordinates.angles.Latitude", + "astropy.coordinates.angles.Longitude", + "astropy.coordinates.angles.Angle", +) + + +class SerializedColumnInfo(MixinInfo): + """ + Minimal info to allow SerializedColumn to be recognized as a mixin Column. + + Used to help create a dict of columns in ColumnInfo for structured data. + """ + + def _represent_as_dict(self): + # SerializedColumn is already a `dict`, so we can return it directly. + return self._parent + + +class SerializedColumn(dict): + """Subclass of dict used to serialize mixin columns. + + It is used in the representation to contain the name and possible + other info for a mixin column or attribute (either primary data or an + array-like attribute) that is serialized as a column in the table. + + """ + + info = SerializedColumnInfo() + + @property + def shape(self): + """Minimal shape implementation to allow use as a mixin column. + + Returns the shape of the first item that has a shape at all, + or ``()`` if none of the values has a shape attribute. + """ + return next( + (value.shape for value in self.values() if hasattr(value, "shape")), () + ) + + def __repr__(self): + """Representation of SerializedColumn + + Examples + -------- + >>> from astropy.table.serialize import SerializedColumn + >>> SerializedColumn({"a": 1}) + SerializedColumn({'a': 1}) + """ + return f"{self.__class__.__name__}({super().__repr__()})" + + +def _represent_mixin_as_column(col, name, new_cols, mixin_cols, exclude_classes=()): + """Carry out processing needed to serialize ``col`` in an output table + consisting purely of plain ``Column`` or ``MaskedColumn`` columns. This + relies on the object determine if any transformation is required and may + depend on the ``serialize_method`` and ``serialize_context`` context + variables. For instance a ``MaskedColumn`` may be stored directly to + FITS, but can also be serialized as separate data and mask columns. + + This function builds up a list of plain columns in the ``new_cols`` arg (which + is passed as a persistent list). This includes both plain columns from the + original table and plain columns that represent data from serialized columns + (e.g. ``jd1`` and ``jd2`` arrays from a ``Time`` column). + + For serialized columns the ``mixin_cols`` dict is updated with required + attributes and information to subsequently reconstruct the table. + + Table mixin columns are always serialized and get represented by one + or more data columns. In earlier versions of the code *only* mixin + columns were serialized, hence the use within this code of "mixin" + to imply serialization. Starting with version 3.1, the non-mixin + ``MaskedColumn`` can also be serialized. + """ + obj_attrs = col.info._represent_as_dict() + + # If serialization is not required (see function docstring above) + # or explicitly specified as excluded, then treat as a normal column. + if not obj_attrs or col.__class__ in exclude_classes: + new_cols.append(col) + return + + # Subtlety here is handling mixin info attributes. The basic list of such + # attributes is: 'name', 'unit', 'dtype', 'format', 'description', 'meta'. + # - name: handled directly [DON'T store] + # - unit: DON'T store if this is a parent attribute + # - dtype: captured in plain Column if relevant [DON'T store] + # - format: possibly irrelevant but settable post-object creation [DO store] + # - description: DO store + # - meta: DO store + info = {} + for attr, nontrivial in ( + ("unit", lambda x: x is not None and x != ""), + ("format", lambda x: x is not None), + ("description", lambda x: x is not None), + ("meta", lambda x: x), + ): + col_attr = getattr(col.info, attr) + if nontrivial(col_attr): + info[attr] = col_attr + + # Find column attributes that have the same length as the column itself. + # These will be stored in the table as new columns (aka "data attributes"). + # Examples include SkyCoord.ra (what is typically considered the data and is + # always an array) and Skycoord.obs_time (which can be a scalar or an + # array). + data_attrs = [ + key + for key, value in obj_attrs.items() + if getattr(value, "shape", ())[:1] == col.shape[:1] + ] + + for data_attr in data_attrs: + data = obj_attrs[data_attr] + + # New column name combines the old name and attribute + # (e.g. skycoord.ra, skycoord.dec).unless it is the primary data + # attribute for the column (e.g. value for Quantity or data for + # MaskedColumn). For primary data, we attempt to store any info on + # the format, etc., on the column, but not for ancillary data (e.g., + # no sense to use a float format for a mask). + is_primary = data_attr == col.info._represent_as_dict_primary_data + if is_primary: + new_name = name + new_info = info + else: + new_name = name + "." + data_attr + new_info = {} + + if not has_info_class(data, MixinInfo): + col_cls = ( + MaskedColumn + if ( + hasattr(data, "mask") + and np.any(data.mask != np.zeros((), data.mask.dtype)) + ) + else Column + ) + data = col_cls(data, name=new_name, **new_info) + if is_primary: + # Don't store info in the __serialized_columns__ dict for this column + # since this is redundant with info stored on the new column. + info = {} + + # Recurse. If this is anything that needs further serialization (i.e., + # a Mixin column, a structured Column, a MaskedColumn for which mask is + # stored, etc.), it will define obj_attrs[new_name]. Otherwise, it will + # just add to new_cols and all we have to do is to link to the new name. + _represent_mixin_as_column(data, new_name, new_cols, obj_attrs) + obj_attrs[data_attr] = SerializedColumn( + obj_attrs.pop(new_name, {"name": new_name}) + ) + + # Strip out from info any attributes defined by the parent, + # and store whatever remains. + for attr in col.info.attrs_from_parent: + if attr in info: + del info[attr] + if info: + obj_attrs["__info__"] = info + + # Store the fully qualified class name + if not isinstance(col, SerializedColumn): + obj_attrs.setdefault("__class__", col.__module__ + "." + col.__class__.__name__) + + mixin_cols[name] = obj_attrs + + +def represent_mixins_as_columns(tbl, exclude_classes=()): + """Represent input Table ``tbl`` using only `~astropy.table.Column` + or `~astropy.table.MaskedColumn` objects. + + This function represents any mixin columns like `~astropy.time.Time` in + ``tbl`` to one or more plain ``~astropy.table.Column`` objects and returns + a new Table. A single mixin column may be split into multiple column + components as needed for fully representing the column. This includes the + possibility of recursive splitting, as shown in the example below. The + new column names are formed as ``.``, e.g. + ``sc.ra`` for a `~astropy.coordinates.SkyCoord` column named ``sc``. + + In addition to splitting columns, this function updates the table ``meta`` + dictionary to include a dict named ``__serialized_columns__`` which provides + additional information needed to construct the original mixin columns from + the split columns. + + This function is used by astropy I/O when writing tables to ECSV, FITS, + HDF5 formats. + + Note that if the table does not include any mixin columns then the original + table is returned with no update to ``meta``. + + Parameters + ---------- + tbl : `~astropy.table.Table` or subclass + Table to represent mixins as Columns + exclude_classes : tuple of class + Exclude any mixin columns which are instannces of any classes in the tuple + + Returns + ------- + tbl : `~astropy.table.Table` + New Table with updated columns, or else the original input ``tbl`` + + Examples + -------- + >>> from astropy.table import Table, represent_mixins_as_columns + >>> from astropy.time import Time + >>> from astropy.coordinates import SkyCoord + + >>> x = [100.0, 200.0] + >>> obstime = Time([1999.0, 2000.0], format='jyear') + >>> sc = SkyCoord([1, 2], [3, 4], unit='deg', obstime=obstime) + >>> tbl = Table([sc, x], names=['sc', 'x']) + >>> represent_mixins_as_columns(tbl) + + sc.ra sc.dec sc.obstime.jd1 sc.obstime.jd2 x + deg deg + float64 float64 float64 float64 float64 + ------- ------- -------------- -------------- ------- + 1.0 3.0 2451180.0 -0.25 100.0 + 2.0 4.0 2451545.0 0.0 200.0 + + """ + # Dict of metadata for serializing each column, keyed by column name. + # Gets filled in place by _represent_mixin_as_column(). + mixin_cols = {} + + # List of columns for the output table. For plain Column objects + # this will just be the original column object. + new_cols = [] + + # Go through table columns and represent each column as one or more + # plain Column objects (in new_cols) + metadata (in mixin_cols). + for col in tbl.itercols(): + _represent_mixin_as_column( + col, col.info.name, new_cols, mixin_cols, exclude_classes=exclude_classes + ) + + # If no metadata was created then just return the original table. + if mixin_cols: + meta = deepcopy(tbl.meta) + meta["__serialized_columns__"] = mixin_cols + out = Table(new_cols, meta=meta, copy=False) + else: + out = tbl + + for col in out.itercols(): + if not isinstance(col, Column) and col.__class__ not in exclude_classes: + # This catches columns for which info has not been set up right and + # therefore were not converted. See the corresponding test in + # test_mixin.py for an example. + raise TypeError( + "failed to represent column " + f"{col.info.name!r} ({col.__class__.__name__}) as one " + "or more Column subclasses. This looks like a mixin class " + "that does not have the correct _represent_as_dict() method " + "in the class `info` attribute." + ) + + return out + + +def _construct_mixin_from_obj_attrs_and_info(obj_attrs, info): + # If this is a supported class then import the class and run + # the _construct_from_col method. Prevent accidentally running + # untrusted code by only importing known astropy classes. + cls_full_name = obj_attrs.pop("__class__", None) + if cls_full_name is None: + # We're dealing with a SerializedColumn holding columns, stored in + # obj_attrs. For this case, info holds the name (and nothing else). + mixin = SerializedColumn(obj_attrs) + mixin.info.name = info["name"] + return mixin + + # We translate locally created skyoffset frames and treat all + # built-in frames as known. + if cls_full_name.startswith("abc.SkyOffset"): + cls_full_name = "astropy.coordinates.SkyOffsetFrame" + elif ( + cls_full_name not in __construct_mixin_classes + and not cls_full_name.startswith("astropy.coordinates.builtin_frames") + ): + raise ValueError(f"unsupported class for construct {cls_full_name}") + + mod_name, _, cls_name = cls_full_name.rpartition(".") + module = import_module(mod_name) + cls = getattr(module, cls_name) + for attr, value in info.items(): + if attr in cls.info.attrs_from_parent: + obj_attrs[attr] = value + mixin = cls.info._construct_from_dict(obj_attrs) + for attr, value in info.items(): + if attr not in obj_attrs: + setattr(mixin.info, attr, value) + return mixin + + +class _TableLite(OrderedDict): + """ + Minimal table-like object for _construct_mixin_from_columns. This allows + manipulating the object like a Table but without the actual overhead + for a full Table. + + More pressing, there is an issue with constructing MaskedColumn, where the + encoded Column components (data, mask) are turned into a MaskedColumn. + When this happens in a real table then all other columns are immediately + Masked and a warning is issued. This is not desirable. + """ + + def add_column(self, col, index=0): + colnames = self.colnames + self[col.info.name] = col + for ii, name in enumerate(colnames): + if ii >= index: + self.move_to_end(name) + + @property + def colnames(self): + return list(self.keys()) + + def itercols(self): + return self.values() + + +def _construct_mixin_from_columns(new_name, obj_attrs, out): + data_attrs_map = {} + for name, val in obj_attrs.items(): + if isinstance(val, SerializedColumn): + # A SerializedColumn can just link to a serialized column using a name + # (e.g., time.jd1), or itself be a mixin (e.g., coord.obstime). Note + # that in principle a mixin could have include a column called 'name', + # hence we check whether the value is actually a string (see gh-13232). + if "name" in val and isinstance(val["name"], str): + data_attrs_map[val["name"]] = name + else: + out_name = f"{new_name}.{name}" + _construct_mixin_from_columns(out_name, val, out) + data_attrs_map[out_name] = name + + for name in data_attrs_map.values(): + del obj_attrs[name] + + # The order of data_attrs_map may not match the actual order, as it is set + # by the yaml description. So, sort names by position in the serialized table. + # Keep the index of the first column, so we can insert the new one there later. + names = sorted(data_attrs_map, key=out.colnames.index) + idx = out.colnames.index(names[0]) + + # Name is the column name in the table (e.g. "coord.ra") and + # data_attr is the object attribute name (e.g. "ra"). A different + # example would be a formatted time object that would have (e.g.) + # "time_col" and "value", respectively. + for name in names: + obj_attrs[data_attrs_map[name]] = out[name] + del out[name] + + info = obj_attrs.pop("__info__", {}) + if len(names) == 1: + # col is the first and only serialized column; in that case, use info + # stored on the column. First step is to get that first column which + # has been moved from `out` to `obj_attrs` above. + col = obj_attrs[data_attrs_map[name]] + + # Now copy the relevant attributes + for attr, nontrivial in ( + ("unit", lambda x: x not in (None, "")), + ("format", lambda x: x is not None), + ("description", lambda x: x is not None), + ("meta", lambda x: x), + ): + col_attr = getattr(col.info, attr) + if nontrivial(col_attr): + info[attr] = col_attr + + info["name"] = new_name + col = _construct_mixin_from_obj_attrs_and_info(obj_attrs, info) + out.add_column(col, index=idx) + + +def _construct_mixins_from_columns(tbl): + if "__serialized_columns__" not in tbl.meta: + return tbl + + meta = tbl.meta.copy() + mixin_cols = meta.pop("__serialized_columns__") + + out = _TableLite(tbl.columns) + + for new_name, obj_attrs in mixin_cols.items(): + _construct_mixin_from_columns(new_name, obj_attrs, out) + + # If no quantity subclasses are in the output then output as Table. + # For instance ascii.read(file, format='ecsv') doesn't specify an + # output class and should return the minimal table class that + # represents the table file. + has_quantities = any(isinstance(col.info, QuantityInfo) for col in out.itercols()) + out_cls = QTable if has_quantities else Table + + return out_cls(list(out.values()), names=out.colnames, copy=False, meta=meta) diff --git a/astropy/table/setup_package.py b/astropy/table/setup_package.py new file mode 100644 index 000000000000..d5e8514a40b5 --- /dev/null +++ b/astropy/table/setup_package.py @@ -0,0 +1,25 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + + +from pathlib import Path + +from numpy import get_include as get_numpy_include +from setuptools import Extension + +ROOT = Path(__file__).parent.relative_to(Path.cwd()) + + +def get_extensions(): + sources = [ROOT / "_np_utils.pyx", ROOT / "_column_mixins.pyx"] + include_dirs = [get_numpy_include()] + + exts = [ + Extension( + name=f"astropy.table.{source.stem}", + sources=[str(source)], + include_dirs=include_dirs, + ) + for source in sources + ] + + return exts diff --git a/astropy/table/soco.py b/astropy/table/soco.py new file mode 100644 index 000000000000..3c7107fbfb62 --- /dev/null +++ b/astropy/table/soco.py @@ -0,0 +1,195 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +""" +The SCEngine class uses the ``sortedcontainers`` package to implement an +Index engine for Tables. +""" + +from collections import OrderedDict +from collections.abc import Hashable, Mapping, Sequence +from numbers import Integral + +from astropy.utils.compat.optional_deps import HAS_SORTEDCONTAINERS + +if HAS_SORTEDCONTAINERS: + from sortedcontainers import SortedList + + +class Node: + __slots__ = ("key", "value") + + def __init__(self, key, value): + self.key = key + self.value = value + + def __lt__(self, other): + if other.__class__ is Node: + return (self.key, self.value) < (other.key, other.value) + return self.key < other + + def __le__(self, other): + if other.__class__ is Node: + return (self.key, self.value) <= (other.key, other.value) + return self.key <= other + + def __eq__(self, other): + if other.__class__ is Node: + return (self.key, self.value) == (other.key, other.value) + return self.key == other + + def __ne__(self, other): + if other.__class__ is Node: + return (self.key, self.value) != (other.key, other.value) + return self.key != other + + def __gt__(self, other): + if other.__class__ is Node: + return (self.key, self.value) > (other.key, other.value) + return self.key > other + + def __ge__(self, other): + if other.__class__ is Node: + return (self.key, self.value) >= (other.key, other.value) + return self.key >= other + + __hash__ = None + + def __repr__(self): + return f"Node({self.key!r}, {self.value!r})" + + +class SCEngine: + """ + Fast tree-based implementation for indexing, using the + ``sortedcontainers`` package. + + Parameters + ---------- + data : Table + Sorted columns of the original table + row_index : Column object + Row numbers corresponding to data columns + unique : bool + Whether the values of the index must be unique. + Defaults to False. + """ + + def __init__(self, data, row_index, unique=False): + if not HAS_SORTEDCONTAINERS: + raise ImportError("sortedcontainers is needed for using SCEngine") + + node_keys = map(tuple, data) + self._nodes = SortedList(map(Node, node_keys, row_index)) + self._unique = unique + + def add(self, key: tuple, row: int) -> None: + """ + Add a key, value pair. + """ + if self.unique and (key in self._nodes): + message = f"duplicate {key!r} in unique index" + raise ValueError(message) + self._nodes.add(Node(key, row)) + + def find(self, key: tuple) -> Sequence[Integral]: + """ + Find rows corresponding to the given key. + """ + return [node.value for node in self._nodes.irange(key, key)] + + def remove(self, key: tuple, data: int) -> bool: + """ + Remove data from the given key. + """ + if data is not None: + item = Node(key, data) + try: + self._nodes.remove(item) + except ValueError: + return False + return True + items = list(self._nodes.irange(key, key)) + for item in items: + self._nodes.remove(item) + return bool(items) + + def shift_left(self, row: int) -> None: + """ + Decrement rows larger than the given row. + """ + for node in self._nodes: + if node.value > row: + node.value -= 1 + + def shift_right(self, row: int) -> None: + """ + Increment rows greater than or equal to the given row. + """ + for node in self._nodes: + if node.value >= row: + node.value += 1 + + def items(self) -> list[tuple[Hashable, list[Integral]]]: + """ + Return a list of key, data tuples. + """ + result = OrderedDict() + for node in self._nodes: + if node.key in result: + result[node.key].append(node.value) + else: + result[node.key] = [node.value] + return list(result.items()) + + def sort(self) -> None: + """ + Make row order align with key order. + """ + for index, node in enumerate(self._nodes): + node.value = index + + def sorted_data(self): + """ + Return a list of rows in order sorted by key. + """ + return [node.value for node in self._nodes] + + def range( + self, + lower: tuple[Hashable, ...] | None, + upper: tuple[Hashable, ...] | None, + bounds: tuple[bool, bool], + ) -> list[int]: + """ + Return row values in the given range. + + A ``None`` value for ``lower`` or ``upper`` corresponds to no limit just like + slicing. + """ + iterator = self._nodes.irange(lower, upper, bounds) + return [node.value for node in iterator] + + def replace_rows(self, row_map: "Mapping[int, int]") -> None: + """ + Replace rows with the values in row_map. + """ + nodes = [node for node in self._nodes if node.value in row_map] + for node in nodes: + node.value = row_map[node.value] + self._nodes.clear() + self._nodes.update(nodes) + + def __repr__(self): + if len(self._nodes) > 6: + nodes = list(self._nodes[:3]) + ["..."] + list(self._nodes[-3:]) + else: + nodes = self._nodes + nodes_str = ", ".join(str(node) for node in nodes) + return f"<{self.__class__.__name__} nodes={nodes_str}>" + + @property + def unique(self): + return self._unique + + def __len__(self) -> int: + return len(self._nodes) diff --git a/astropy/table/sorted_array.py b/astropy/table/sorted_array.py new file mode 100644 index 000000000000..ea179302c5f6 --- /dev/null +++ b/astropy/table/sorted_array.py @@ -0,0 +1,332 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +from collections.abc import Hashable, Mapping, Sequence +from numbers import Integral +from typing import TYPE_CHECKING + +import numpy as np + +if TYPE_CHECKING: + from .table import Column, Table + + +def _searchsorted(array, val, side="left"): + """ + Call np.searchsorted or use a custom binary + search if necessary. + """ + if hasattr(array, "searchsorted"): + return array.searchsorted(val, side=side) + # Python binary search + begin = 0 + end = len(array) + while begin < end: + mid = (begin + end) // 2 + if val > array[mid]: + begin = mid + 1 + elif val < array[mid]: + end = mid + elif side == "right": + begin = mid + 1 + else: + end = mid + return begin + + +class SortedArray: + """ + Implements a sorted array container using + a list of numpy arrays. + + Parameters + ---------- + data : Table + Sorted columns of the original table + row_index : Column object + Row numbers corresponding to data columns + unique : bool + Whether the values of the index must be unique. + Defaults to False. + """ + + def __init__(self, data: "Table", row_index: "Column", unique: bool = False): + self.data = data + self.row_index = row_index + self.num_cols = len(getattr(data, "colnames", [])) + self.unique = unique + + @property + def cols(self) -> list["Column"]: + return list(self.data.columns.values()) + + def add(self, key: tuple, row: int) -> None: + """ + Add a new entry to the sorted array. + + Parameters + ---------- + key : tuple + Column values at the given row + row : int + Row number + """ + pos = self.find_pos(key, row) # first >= key + + if ( + self.unique + and 0 <= pos < len(self.row_index) + and all(self.data[pos][i] == key[i] for i in range(len(key))) + ): + # already exists + raise ValueError(f'Cannot add duplicate value "{key}" in a unique index') + self.data.insert_row(pos, key) + self.row_index = self.row_index.insert(pos, row) + + def _get_key_slice(self, i, begin, end): + """ + Retrieve the ith slice of the sorted array + from begin to end. + """ + if i < self.num_cols: + return self.cols[i][begin:end] + else: + return self.row_index[begin:end] + + def find_pos(self, key, data, exact=False): + """ + Return the index of the largest key in data greater than or + equal to the given key, data pair. + + Parameters + ---------- + key : tuple + Column key + data : int + Row number + exact : bool + If True, return the index of the given key in data + or -1 if the key is not present. + """ + begin = 0 + end = len(self.row_index) + num_cols = self.num_cols + if not self.unique: + # consider the row value as well + key = key + (data,) + num_cols += 1 + + # search through keys in lexicographic order + for i in range(num_cols): + key_slice = self._get_key_slice(i, begin, end) + t = _searchsorted(key_slice, key[i]) + # t is the smallest index >= key[i] + if exact and (t == len(key_slice) or key_slice[t] != key[i]): + # no match + return -1 + elif t == len(key_slice) or ( + t == 0 and len(key_slice) > 0 and key[i] < key_slice[0] + ): + # too small or too large + return begin + t + end = begin + _searchsorted(key_slice, key[i], side="right") + begin += t + if begin >= len(self.row_index): # greater than all keys + return begin + + return begin + + def find(self, key: tuple) -> Sequence[Integral]: + """ + Find all rows matching the given key. + + Parameters + ---------- + key : tuple + Column values + + Returns + ------- + matching_rows : list + List of rows matching the input key + """ + begin = 0 + end = len(self.row_index) + + # search through keys in lexicographic order + for i in range(self.num_cols): + key_slice = self._get_key_slice(i, begin, end) + t = _searchsorted(key_slice, key[i]) + # t is the smallest index >= key[i] + if t == len(key_slice) or key_slice[t] != key[i]: + # no match + return [] + elif t == 0 and len(key_slice) > 0 and key[i] < key_slice[0]: + # too small or too large + return [] + end = begin + _searchsorted(key_slice, key[i], side="right") + begin += t + if begin >= len(self.row_index): # greater than all keys + return [] + + return self.row_index[begin:end] + + def range( + self, + lower: tuple[Hashable, ...] | None, + upper: tuple[Hashable, ...] | None, + bounds: tuple[bool, bool], + ) -> list[int]: + """ + Find values in the given range. + + Parameters + ---------- + lower : tuple, None + Lower search bound (no lower bound if None) + upper : tuple, None + Upper search bound (no upper bound if None) + bounds : (2,) tuple of bool + Indicates whether the search should be inclusive or + exclusive with respect to the endpoints. The first + argument corresponds to an inclusive lower bound, + and the second argument to an inclusive upper bound. + """ + # Find initial positions for lower and upper bounds. Just like a slice object, + # None values for `lower` or `upper` correspond to no bound in that direction. + lower_pos = 0 if lower is None else self.find_pos(lower, 0) + upper_pos = len(self.row_index) if upper is None else self.find_pos(upper, 0) + + if lower_pos == len(self.row_index): + return [] + + lower_bound = tuple(col[lower_pos] for col in self.cols) + if not bounds[0] and lower_bound == lower: + lower_pos += 1 # data[lower_pos] > lower + + # data[lower_pos] >= lower + # data[upper_pos] >= upper + if upper_pos < len(self.row_index): + upper_bound = tuple(col[upper_pos] for col in self.cols) + if not bounds[1] and upper_bound == upper: + upper_pos -= 1 # data[upper_pos] < upper + elif upper_bound > upper: + upper_pos -= 1 # data[upper_pos] <= upper + return self.row_index[lower_pos : upper_pos + 1] + + def remove(self, key: tuple, data: int) -> bool: + """ + Remove the given entry from the sorted array. + + Parameters + ---------- + key : tuple + Column values + data : int + Row number + + Returns + ------- + successful : bool + Whether the entry was successfully removed + """ + pos = self.find_pos(key, data, exact=True) + if pos == -1: # key not found + return False + + self.data.remove_row(pos) + keep_mask = np.ones(len(self.row_index), dtype=bool) + keep_mask[pos] = False + self.row_index = self.row_index[keep_mask] + return True + + def shift_left(self, row: int) -> None: + """ + Decrement all row numbers greater than the input row. + + Parameters + ---------- + row : int + Input row number + """ + self.row_index[self.row_index > row] -= 1 + + def shift_right(self, row: int) -> None: + """ + Increment all row numbers greater than or equal to the input row. + + Parameters + ---------- + row : int + Input row number + """ + self.row_index[self.row_index >= row] += 1 + + def replace_rows(self, row_map: "Mapping[int, int]") -> None: + """ + Replace all rows with the values they map to in the + given dictionary. Any rows not present as keys in + the dictionary will have their entries deleted. + + Parameters + ---------- + row_map : dict + Mapping of row numbers to new row numbers + """ + num_rows = len(row_map) + keep_rows = np.zeros(len(self.row_index), dtype=bool) + tagged = 0 + for i, row in enumerate(self.row_index): + if row in row_map: + keep_rows[i] = True + tagged += 1 + if tagged == num_rows: + break + + self.data = self.data[keep_rows] + self.row_index = np.array([row_map[x] for x in self.row_index[keep_rows]]) + + def items(self) -> list[tuple[Hashable, list[Integral]]]: + """ + Retrieve all array items as a list of pairs of the form + [(key, [row 1, row 2, ...]), ...]. + """ + array = [] + last_key = None + for i, key in enumerate(zip(*self.data.columns.values())): + row = self.row_index[i] + if key == last_key: + array[-1][1].append(row) + else: + last_key = key + array.append((key, [row])) + return array + + def sort(self) -> None: + """ + Make row order align with key order. + """ + self.row_index = np.arange(len(self.row_index)) + + def sorted_data(self) -> None: + """ + Return rows in sorted order. + """ + return self.row_index + + def __getitem__(self, item): + """ + Return a sliced reference to this sorted array. + + Parameters + ---------- + item : slice + Slice to use for referencing + """ + return SortedArray(self.data[item], self.row_index[item]) + + def __repr__(self): + t = self.data.copy() + t["rows"] = self.row_index + return f"<{self.__class__.__name__} length={len(t)}>\n{t}" + + def __len__(self) -> int: + return len(self.row_index) diff --git a/astropy/table/structhelper.py b/astropy/table/structhelper.py deleted file mode 100644 index 728dfa19f5f5..000000000000 --- a/astropy/table/structhelper.py +++ /dev/null @@ -1,36 +0,0 @@ -import numpy as np - - -def _append_field(table, data, dtype=None, position=None): - - newdtype = table.dtype.descr - if position is None: - position = len(newdtype) - - newdtype.insert(position, dtype) - newtable = np.empty(table.shape, dtype=newdtype) - - for field in table.dtype.fields: - newtable[field] = table[field] - - newtable[dtype[0]] = data - - return newtable - - -def _drop_fields(table, names): - - names = set(names) - newdtype = [(name, table.dtype[name]) for name in table.dtype.names - if name not in names] - newdtype = np.dtype(newdtype) - - if newdtype: - newtable = np.empty(table.shape, dtype=newdtype) - else: - return None - - for field in newdtype.fields: - newtable[field] = table[field] - - return newtable diff --git a/astropy/table/table.py b/astropy/table/table.py index fef2c837c420..ecbc8c33c814 100644 --- a/astropy/table/table.py +++ b/astropy/table/table.py @@ -1,25 +1,224 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +import itertools import sys +import types +import warnings +import weakref +from collections import OrderedDict, defaultdict +from collections.abc import Mapping from copy import deepcopy -import collections +from pathlib import Path +from typing import Any import numpy as np +from numpy import ma + +from astropy import log +from astropy.io.registry import UnifiedReadWriteMethod +from astropy.units import Quantity, QuantityInfo +from astropy.utils import deprecated +from astropy.utils.console import color_print +from astropy.utils.data_info import BaseColumnInfo, DataInfo, MixinInfo +from astropy.utils.decorators import format_doc +from astropy.utils.exceptions import AstropyDeprecationWarning, AstropyUserWarning +from astropy.utils.masked import Masked +from astropy.utils.metadata import MetaAttribute, MetaData + +from . import conf, groups +from .column import ( + BaseColumn, + Column, + FalseArray, + MaskedColumn, + _auto_names, + _convert_sequence_data_to_array, + col_copy, +) +from .connect import TableRead, TableWrite +from .index import ( + Index, + SlicedIndex, + TableILoc, + TableIndices, + TableLoc, + TableLocIndices, + _IndexModeContext, + get_index, +) +from .info import TableInfo +from .mixins.registry import get_mixin_handler +from .ndarray_mixin import NdarrayMixin # noqa: F401 +from .pprint import TableFormatter +from .row import Row + +_implementation_notes = """ +This string has informal notes concerning Table implementation for developers. + +Things to remember: + +- Table has customizable attributes ColumnClass, Column, MaskedColumn. + Table.Column is normally just column.Column (same w/ MaskedColumn) + but in theory they can be different. Table.ColumnClass is the default + class used to create new non-mixin columns, and this is a function of + the Table.masked attribute. Column creation / manipulation in a Table + needs to respect these. + +- Column objects that get inserted into the Table.columns attribute must + have the info.parent_table attribute set correctly. Beware just dropping + an object into the columns dict since an existing column may + be part of another Table and have parent_table set to point at that + table. Dropping that column into `columns` of this Table will cause + a problem for the old one so the column object needs to be copied (but + not necessarily the data). + + Currently replace_column is always making a copy of both object and + data if parent_table is set. This could be improved but requires a + generic way to copy a mixin object but not the data. + +- Be aware of column objects that have indices set. + +- `cls.ColumnClass` is a property that effectively uses the `masked` attribute + to choose either `cls.Column` or `cls.MaskedColumn`. +""" + +__doctest_skip__ = [ + "Table.read", + "Table.write", + "Table._read", + "Table.convert_bytestring_to_unicode", + "Table.convert_unicode_to_bytestring", +] + +__doctest_requires__ = { + ("Table.from_df", "Table.to_df"): ["pandas", "polars"], +} + +_pprint_docs = """ + {__doc__} -from ..utils import OrderedDict, isiterable -from .structhelper import _drop_fields -from .pprint import _pformat_table, _pformat_col, _more_tabcol -from ..utils.console import color_print -from .io_registry import get_reader, get_writer, identify_format -# Python 2 and 3 source compatibility -try: - unicode -except NameError: - unicode = basestring = str + Parameters + ---------- + max_lines : int or None + Maximum number of lines in table output. + + max_width : int or None + Maximum character width of output. + + show_name : bool + Include a header row for column names. Default is True. + + show_unit : bool + Include a header row for unit. Default is to show a row + for units only if one or more columns has a defined value + for the unit. + + show_dtype : bool + Include a header row for column dtypes. Default is False. + + align : str or list or tuple or None + Left/right alignment of columns. Default is right (None) for all + columns. Other allowed values are '>', '<', '^', and '0=' for + right, left, centered, and 0-padded, respectively. A list of + strings can be provided for alignment of tables with multiple + columns. + """ + +_pformat_docs = """ + {__doc__} + + Parameters + ---------- + max_lines : int or None + Maximum number of rows to output + + max_width : int or None + Maximum character width of output + + show_name : bool + Include a header row for column names. Default is True. + + show_unit : bool + Include a header row for unit. Default is to show a row + for units only if one or more columns has a defined value + for the unit. + + show_dtype : bool + Include a header row for column dtypes. Default is True. + + html : bool + Format the output as an HTML table. Default is False. + + tableid : str or None + An ID tag for the table; only used if html is set. Default is + "table{id}", where id is the unique integer id of the table object, + id(self) + + align : str or list or tuple or None + Left/right alignment of columns. Default is right (None) for all + columns. Other allowed values are '>', '<', '^', and '0=' for + right, left, centered, and 0-padded, respectively. A list of + strings can be provided for alignment of tables with multiple + columns. + + tableclass : str or list of str or None + CSS classes for the table; only used if html is set. Default is + None. + + Returns + ------- + lines : list + Formatted table as a list of strings. + """ + + +class TableReplaceWarning(UserWarning): + """ + Warning class for cases when a table column is replaced via the + Table.__setitem__ syntax e.g. t['a'] = val. + + This does not inherit from AstropyWarning because we want to use + stacklevel=3 to show the user where the issue occurred in their code. + """ + + +def descr(col): + """Array-interface compliant full description of a column. + + This returns a 3-tuple (name, type, shape) that can always be + used in a structured array dtype definition. + """ + col_dtype = "O" if (col.info.dtype is None) else col.info.dtype + col_shape = col.shape[1:] if hasattr(col, "shape") else () + return (col.info.name, col_dtype, col_shape) + + +def has_info_class(obj, cls): + """Check if the object's info is an instance of cls.""" + # We check info on the class of the instance, since on the instance + # itself accessing 'info' has side effects in that it sets + # obj.__dict__['info'] if it does not exist already. + return isinstance(getattr(obj.__class__, "info", None), cls) -AUTO_COLNAME = 'col{0}' +def _get_names_from_list_of_dict(rows): + """Return list of column names if ``rows`` is a list of dict that + defines table data. -def _auto_names(n_cols): - return [AUTO_COLNAME.format(i) for i in range(n_cols)] + If rows is not a list of dict then return None. + """ + if rows is None: + return None + + names = {} + for row in rows: + if not isinstance(row, (Mapping, Row)): + return None + names.update(row) + return list(names) + + +# Note to future maintainers: when transitioning this to dict +# be sure to change the OrderedDict ref(s) in Row and in __len__(). class TableColumns(OrderedDict): @@ -29,9 +228,9 @@ class TableColumns(OrderedDict): by name or index, including slice access. It also handles renaming of columns. - The initialization argument ``cols`` can be any structure that is valid - for initializing a Python dict. This includes a dict, list of - (key, val) tuple pairs, list of [key, val] lists, etc. + The initialization argument ``cols`` can be a list of ``Column`` objects + or any structure that is valid for initializing a Python dict. This + includes a dict, list of (key, val) tuples or [key, val] lists, etc. Parameters ---------- @@ -41,459 +240,581 @@ class TableColumns(OrderedDict): def __init__(self, cols={}): if isinstance(cols, (list, tuple)): - cols = [(col.name, col) for col in cols] - super(TableColumns, self).__init__(cols) + # `cols` should be a list of two-tuples, but it is allowed to have + # columns (BaseColumn or mixins) in the list. + newcols = [] + for col in cols: + if has_info_class(col, BaseColumnInfo): + newcols.append((col.info.name, col)) + else: + newcols.append(col) + cols = newcols + super().__init__(cols) def __getitem__(self, item): """Get items from a TableColumns object. + :: - tc = TableColumns(cols=[Column('a'), Column('b'), Column('c')]) + tc = TableColumns(cols=[Column(name='a'), Column(name='b'), Column(name='c')]) tc['a'] # Column('a') tc[1] # Column('b') tc['a', 'b'] # tc[1:3] # """ - if isinstance(item, basestring): + if isinstance(item, str): return OrderedDict.__getitem__(self, item) - elif isinstance(item, int): - return self.values()[item] + elif isinstance(item, (int, np.integer)): + return list(self.values())[item] + elif ( + isinstance(item, np.ndarray) and item.shape == () and item.dtype.kind == "i" + ): + return list(self.values())[item.item()] elif isinstance(item, tuple): - return TableColumns([self[x] for x in item]) + return self.__class__([self[x] for x in item]) elif isinstance(item, slice): - return TableColumns([self[x] for x in self.keys()[item]]) + return self.__class__([self[x] for x in list(self)[item]]) else: - raise IndexError('Illegal key or index value for TableColumns ' - 'object') + raise IndexError( + f"Illegal key or index value for {type(self).__name__} object" + ) - def __repr__(self): - names = ("'{0}'".format(x) for x in self.keys()) - return "".format(",".join(names)) + def __setitem__(self, item, value, validated=False): + """ + Set item in this dict instance, but do not allow directly replacing an + existing column unless it is already validated (and thus is certain to + not corrupt the table). - def _rename_column(self, name, new_name): - if new_name in self: - raise KeyError("Column {0} already exists".format(new_name)) + NOTE: it is easily possible to corrupt a table by directly *adding* a new + key to the TableColumns attribute of a Table, e.g. + ``t.columns['jane'] = 'doe'``. - mapper = {name: new_name} - new_names = [mapper.get(name, name) for name in self] - cols = self.values() - self.clear() - self.update(zip(new_names, cols)) + """ + if item in self and not validated: + raise ValueError( + f"Cannot replace column '{item}'. Use Table.replace_column() instead." + ) + super().__setitem__(item, value) - # Define keys and values for Python 2 and 3 source compatibility - def keys(self): - return list(OrderedDict.keys(self)) + def __repr__(self): + names = (f"'{x}'" for x in self.keys()) + return f"<{self.__class__.__name__} names=({','.join(names)})>" - def values(self): - return list(OrderedDict.values(self)) + def _rename_column(self, name: str, new_name: str): + if name == new_name: + return + if new_name in self: + raise KeyError(f"Column {new_name} already exists") -class Column(np.ndarray): - """Define a data column for use in a Table object. + if isinstance(new_name, str): + new_name = str(new_name) + else: + raise TypeError( + f"Expected a str value, got {new_name} with type {type(new_name).__name__}" + ) - Parameters - ---------- - name : str - Column name and key for reference within Table - data : list, ndarray or None - Column data values - dtype : numpy.dtype compatible value - Data type for column - shape : tuple or () - Dimensions of a single row element in the column data - length : int or 0 - Number of row elements in column data - description : str or None - Full description of column - units : str or None - Physical units - format : str or None - Format string for outputting column values. This can be an - "old-style" (``format % value``) or "new-style" (`str.format`) - format specification string. - meta : dict-like or None - Meta-data associated with the column + # Rename column names in pprint include/exclude attributes as needed + parent_table = self[name].info.parent_table + if parent_table is not None: + parent_table.pprint_exclude_names._rename(name, new_name) + parent_table.pprint_include_names._rename(name, new_name) - Examples - -------- - A Column can be created in two different ways: + mapper = {name: new_name} + new_names = [mapper.get(name, name) for name in self] + cols = list(self.values()) + self.clear() + super().update(zip(new_names, cols)) + + def __delitem__(self, name): + # Remove column names from pprint include/exclude attributes as needed. + # __delitem__ also gets called for pop() and popitem(). + parent_table = self[name].info.parent_table + if parent_table is not None: + # _remove() method does not require that `name` is in the attribute + parent_table.pprint_exclude_names._remove(name) + parent_table.pprint_include_names._remove(name) + return super().__delitem__(name) + + def isinstance(self, cls): + """ + Return a list of columns which are instances of the specified classes. - - Provide a ``data`` value and optionally a ``dtype`` value + Parameters + ---------- + cls : class or tuple thereof + Column class (including mixin) or tuple of Column classes. - Examples:: + Returns + ------- + col_list : list of `Column` + List of Column objects which are instances of given classes. + """ + cols = [col for col in self.values() if isinstance(col, cls)] + return cols - col = Column('name', data=[1, 2, 3]) # shape=(3,) - col = Column('name', data=[[1, 2], [3, 4]]) # shape=(2, 2) - col = Column('name', data=[1, 2, 3], dtype=float) - col = Column('name', np.array([1, 2, 3])) - col = Column('name', ['hello', 'world']) + def not_isinstance(self, cls): + """ + Return a list of columns which are not instances of the specified classes. - The ``dtype`` argument can be any value which is an acceptable - fixed-size data-type initializer for the numpy.dtype() method. See - ``_. - Examples include: + Parameters + ---------- + cls : class or tuple thereof + Column class (including mixin) or tuple of Column classes. - - Python non-string type (float, int, bool) - - Numpy non-string type (e.g. np.float32, np.int64, np.bool) - - Numpy.dtype array-protocol type strings (e.g. 'i4', 'f8', 'S15') + Returns + ------- + col_list : list of `Column` + List of Column objects which are not instances of given classes. + """ + cols = [col for col in self.values() if not isinstance(col, cls)] + return cols - If no ``dtype`` value is provide then the type is inferred using - ``np.array(data)``. When ``data`` is provided then the ``shape`` - and ``length`` arguments are ignored. + # When the deprecation period of setdefault() and update() is over then they + # need to be rewritten to raise an error, not removed. - - Provide zero or more of ``dtype``, ``shape``, ``length`` + @deprecated( + since="6.1", alternative="t.setdefault()", name="t.columns.setdefault()" + ) + def setdefault(self, key, default): + return super().setdefault(key, default) - Examples:: + @deprecated(since="6.1", alternative="t.update()", name="t.columns.update()") + def update(self, *args, **kwargs): + return super().update(*args, **kwargs) - col = Column('name') - col = Column('name', dtype=int, length=10, shape=(3,4)) - The default ``dtype`` is ``np.float64`` and the default ``length`` is - zero. The ``shape`` argument is the array shape of a single cell in the - column. The default ``shape`` is () which means a single value in each - element. +class TableAttribute(MetaAttribute): """ + Descriptor to define a custom attribute for a Table subclass. - def __new__(cls, name, data=None, - dtype=None, shape=(), length=0, - description=None, units=None, format=None, meta=None): - - if data is None: - dtype = (np.dtype(dtype).str, shape) - self_data = np.zeros(length, dtype=dtype) - elif isinstance(data, Column): - self_data = np.asarray(data.data, dtype=dtype) - if description is None: - description = data.description - if units is None: - units = units or data.units - if format is None: - format = data.format - if meta is None: - meta = deepcopy(data.meta) - else: - self_data = np.asarray(data, dtype=dtype) - - self = self_data.view(cls) - self._name = name - self.units = units - self.format = format - self.description = description - self.parent_table = None + The value of the ``TableAttribute`` will be stored in a dict named + ``__attributes__`` that is stored in the table ``meta``. The attribute + can be accessed and set in the usual way, and it can be provided when + creating the object. - self.meta = OrderedDict() - if meta is not None: - self.meta.update(meta) + Defining an attribute by this mechanism ensures that it will persist if + the table is sliced or serialized, for example as a pickle or ECSV file. - return self + See the `~astropy.utils.metadata.MetaAttribute` documentation for additional + details. - def __array_finalize__(self, obj): - # Obj will be none for direct call to Column() creator - if obj is None: - return + Parameters + ---------- + default : object + Default value for attribute - # Self was created from template (e.g. obj[slice] or (obj * 2)) - # or viewcast e.g. obj.view(Column). In either case we want to - # init Column attributes for self from obj if possible. - self.parent_table = None - for attr in ('name', 'units', 'format', 'description'): - val = getattr(obj, attr, None) - setattr(self, attr, val) - self.meta = deepcopy(getattr(obj, 'meta', {})) + Examples + -------- + >>> from astropy.table import Table, TableAttribute + >>> class MyTable(Table): + ... identifier = TableAttribute(default=1) + >>> t = MyTable(identifier=10) + >>> t.identifier + 10 + >>> t.meta + {'__attributes__': {'identifier': 10}} + """ - def _get_name(self): - return self._name - def _set_name(self, val): - if self.parent_table is not None: - table = self.parent_table - table.columns._rename_column(self.name, val) - table._data.dtype.names = table.columns.keys() +class PprintIncludeExclude(TableAttribute): + """Maintain tuple that controls table column visibility for print output. - self._name = val + This is a descriptor that inherits from MetaAttribute so that the attribute + value is stored in the table meta['__attributes__']. - name = property(_get_name, _set_name) + This gets used for the ``pprint_include_names`` and ``pprint_exclude_names`` Table + attributes. + """ - @property - def data(self): - return self.view(np.ndarray) + def __get__(self, instance, owner_cls): + """Get the attribute. - def copy(self, data=None, copy_data=True): - """Return a copy of the current Column instance. + This normally returns an instance of this class which is stored on the + owner object. """ - if data is None: - data = self.view(np.ndarray) - if copy_data: - data = data.copy() - - return Column(self.name, data, units=self.units, format=self.format, - description=self.description, meta=deepcopy(self.meta)) - - @property - def descr(self): - """Array-interface compliant full description of the column. + # For getting from class not an instance + if instance is None: + return self + + # If not already stored on `instance`, make a copy of the class + # descriptor object and put it onto the instance. + value = instance.__dict__.get(self.name) + if value is None: + value = deepcopy(self) + instance.__dict__[self.name] = value + + # We set _instance_ref on every call, since if one makes copies of + # instances, this attribute will be copied as well, which will lose the + # reference. + value._instance_ref = weakref.ref(instance) + return value + + def __set__(self, instance, names): + """Set value of ``instance`` attribute to ``names``. - This returns a 3-tuple (name, type, shape) that can always be - used in a structured array dtype definition. + Parameters + ---------- + instance : object + Instance that owns the attribute + names : None, str, list, tuple + Column name(s) to store, or None to clear """ - return (self.name, self.dtype.str, self.shape[1:]) - - def __repr__(self): - if self.name: - out = "\n{4}".format( - repr(self.name), repr(self.units), - repr(self.format), repr(self.description), repr(self.data)) + if isinstance(names, str): + names = [names] + if names is None: + # Remove attribute value from the meta['__attributes__'] dict. + # Subsequent access will just return None. + delattr(instance, self.name) else: - out = repr(self.data) - return out - - def attrs_equal(self, col): - """Compare the column attributes of ``col`` to this object. + # This stores names into instance.meta['__attributes__'] as tuple + return super().__set__(instance, tuple(names)) - The comparison attributes are: name, units, dtype, format, description, - and meta. - - Parameters - ---------- - col: Column - Comparison column + def __call__(self): + """Get the value of the attribute. Returns ------- - equal: boolean - True if all attributes are equal + names : None, tuple + Include/exclude names """ - if not isinstance(col, Column): - raise ValueError('Comparison `col` must be a Column object') + # Get the value from instance.meta['__attributes__'] + instance = self._instance_ref() + return super().__get__(instance, instance.__class__) - attrs = ('name', 'units', 'dtype', 'format', 'description', 'meta') - equal = all(getattr(self, x) == getattr(col, x) for x in attrs) + def __repr__(self): + if hasattr(self, "_instance_ref"): + out = f"<{self.__class__.__name__} name={self.name} value={self()}>" + else: + out = super().__repr__() + return out - return equal + def _add_remove_setup(self, names): + """Common setup for add and remove. - def pformat(self, max_lines=None, show_name=True, show_units=False): - """Return a list of formatted string representation of column values. + - Coerce attribute value to a list + - Coerce names into a list + - Get the parent table instance + """ + names = [names] if isinstance(names, str) else list(names) + # Get the value. This is the same as self() but we need `instance` here. + instance = self._instance_ref() + value = super().__get__(instance, instance.__class__) + value = [] if value is None else list(value) + return instance, names, value - If no value of ``max_lines`` is supplied then the height of the screen - terminal is used to set ``max_lines``. If the terminal height cannot - be determined then a default of ``astropy.table.MAX_LINES`` is used. - If a negative value of ``max_lines`` is supplied then there is no line - limit applied. + def add(self, names): + """Add ``names`` to the include/exclude attribute. Parameters ---------- - max_lines : int - Maximum lines of output (header + data rows) - - show_name : bool - Include column name (default=True) - - show_units : bool - Include a header row for units (default=False) - - Returns - ------- - lines : list - List of lines with header and formatted column values - + names : str, list, tuple + Column name(s) to add """ - lines, n_header = _pformat_col(self, max_lines, show_name, show_units) - return lines + instance, names, value = self._add_remove_setup(names) + value.extend(name for name in names if name not in value) + super().__set__(instance, tuple(value)) - def pprint(self, max_lines=None, show_name=True, show_units=False): - """Print a formatted string representation of column values. - - If no value of ``max_lines`` is supplied then the height of the screen - terminal is used to set ``max_lines``. If the terminal height cannot - be determined then a default of ``astropy.table.MAX_LINES`` is used. - If a negative value of ``max_lines`` is supplied then there is no line - limit applied. + def remove(self, names): + """Remove ``names`` from the include/exclude attribute. Parameters ---------- - max_lines : int - Maximum number of values in output - - show_name : bool - Include column name (default=True) - - show_units : bool - Include a header row for units (default=False) + names : str, list, tuple + Column name(s) to remove """ - lines, n_header = _pformat_col(self, max_lines, show_name, show_units) - for i, line in enumerate(lines): - if i < n_header: - color_print(line, 'red') - else: - print line + self._remove(names, raise_exc=True) - def more(self, max_lines=None, show_name=True, show_units=False): - """Interactively browse column with a paging interface. + def _remove(self, names, raise_exc=False): + """Remove ``names`` with optional checking if they exist.""" + instance, names, value = self._add_remove_setup(names) - Supported keys:: + # Return now if there are no attributes and thus no action to be taken. + if not raise_exc and "__attributes__" not in instance.meta: + return - f, : forward one page - b : back one page - r : refresh same page - n : next row - p : previous row - < : go to beginning - > : go to end - q : quit browsing - h : print this help + # Remove one by one, optionally raising an exception if name is missing. + for name in names: + if name in value: + value.remove(name) # Using the list.remove method + elif raise_exc: + raise ValueError(f"{name} not in {self.name}") + + # Change to either None or a tuple for storing back to attribute + value = None if value == [] else tuple(value) + self.__set__(instance, value) + + def _rename(self, name, new_name): + """Rename ``name`` to ``new_name`` if ``name`` is in the list.""" + names = self() or () + if name in names: + new_names = list(names) + new_names[new_names.index(name)] = new_name + self.set(new_names) + + def set(self, names): + """Set value of include/exclude attribute to ``names``. Parameters ---------- - max_lines : int - Maximum number of lines in table output - - show_name : bool - Include a header row for column names (default=True) + names : None, str, list, tuple + Column name(s) to store, or None to clear + """ - show_units : bool - Include a header row for units (default=False) + class _Context: + def __init__(self, descriptor_self): + self.descriptor_self = descriptor_self + self.names_orig = descriptor_self() - """ - _more_tabcol(self, max_lines=max_lines, show_name=show_name, - show_units=show_units) + def __enter__(self): + pass - def __str__(self): - lines, n_header = _pformat_col(self) - return '\n'.join(lines) - - -class Row(object): - """A class to represent one row of a Table object. - - A Row object is returned when a Table object is indexed with an integer - or when iterating over a table:: - - >>> table = Table([(1, 2), (3, 4)], names=('a', 'b')) - >>> row = table[1] - >>> row - - >>> row['a'] - 2 - >>> row[1] - 4 - """ + def __exit__(self, type, value, tb): + descriptor_self = self.descriptor_self + instance = descriptor_self._instance_ref() + descriptor_self.__set__(instance, self.names_orig) - def __init__(self, table, index): - self._table = table - self._index = index - self._data = table._data[index] + def __repr__(self): + return repr(self.descriptor_self) - def __getitem__(self, item): - return self.data[item] + ctx = _Context(descriptor_self=self) - def __setitem__(self, item, val): - self.data[item] = val + instance = self._instance_ref() + self.__set__(instance, names) - def __eq__(self, other): - return self.data == other + return ctx - def __ne__(self, other): - return self.data != other - def __array__(self, dtype=None): - """Support converting Row to np.array via np.array(table). +class Table: + """A class to represent tables of heterogeneous data. - Coercion to a different dtype via np.array(table, dtype) is not - supported and will raise a ValueError. - """ - if dtype is not None: - raise ValueError('Datatype coercion is not allowed') + `~astropy.table.Table` provides a class for heterogeneous tabular data. + A key enhancement provided by the `~astropy.table.Table` class over + e.g. a `numpy` structured array is the ability to easily modify the + structure of the table by adding or removing columns, or adding new + rows of data. In addition table and column metadata are fully supported. - return np.array(self._data) + `~astropy.table.Table` differs from `~astropy.nddata.NDData` by the + assumption that the input data consists of columns of homogeneous data, + where each column has a unique identifier and may contain additional + metadata such as the data unit, format, and description. - def __len__(self): - return len(self._data) + See also: https://docs.astropy.org/en/stable/table/ - @property - def table(self): - return self._table + Parameters + ---------- + data : numpy ndarray, dict, list, table-like object, optional + Data to initialize table. + masked : bool, optional + Specify whether the table is masked. + names : list, optional + Specify column names. + dtype : list, optional + Specify column data types. + meta : dict, optional + Metadata associated with the table. + copy : bool, optional + Copy the input column data and make a deep copy of the input meta. + Default is True. + rows : numpy ndarray, list of list, optional + Row-oriented data for table instead of ``data`` argument. + copy_indices : bool, optional + Copy any indices in the input data. Default is True. + units : list, dict, optional + List or dict of units to apply to columns. + descriptions : list, dict, optional + List or dict of descriptions to apply to columns. + **kwargs : dict, optional + Additional keyword args when converting table-like object. + """ - @property - def index(self): - return self._index + meta = MetaData(copy=False, default_factory=dict) - @property - def data(self): - return self._data + # Define class attributes for core container objects to allow for subclass + # customization. + Row = Row + Column = Column + MaskedColumn = MaskedColumn + TableColumns = TableColumns + TableFormatter = TableFormatter - @property - def meta(self): - return self.table.meta + # Unified I/O read and write methods from .connect + read = UnifiedReadWriteMethod(TableRead) + write = UnifiedReadWriteMethod(TableWrite) - @property - def columns(self): - return self.table.columns + pprint_exclude_names = PprintIncludeExclude() + pprint_include_names = PprintIncludeExclude() - @property - def colnames(self): - return self.table.colnames + def as_array(self, keep_byteorder=False, names=None): + """ + Return a new copy of the table in the form of a structured np.ndarray or + np.ma.MaskedArray object (as appropriate). - @property - def dtype(self): - return self.data.dtype + Parameters + ---------- + keep_byteorder : bool, optional + By default the returned array has all columns in native byte + order. However, if this option is `True` this preserves the + byte order of all columns (if any are non-native). - def __repr__(self): - return "".format( - self.index, self.data, self.dtype) + names : list, optional: + List of column names to include for returned structured array. + Default is to include all table columns. + Returns + ------- + table_array : array or `~numpy.ma.MaskedArray` + Copy of table as a numpy structured array. + ndarray for unmasked or `~numpy.ma.MaskedArray` for masked. + """ + masked = self.masked or self.has_masked_columns or self.has_masked_values + empty_init = ma.empty if masked else np.empty + if len(self.columns) == 0: + return empty_init(0, dtype=None) -class Table(object): - """A class to represent tables of heterogeneous data. + dtype = [] - `Table` provides a class for heterogeneous tabular data, making use of a - `numpy` structured array internally to store the data values. A key - enhancement provided by the `Table` class is the ability to easily modify - the structure of the table by adding or removing columns, or adding new - rows of data. In addition table and column metadata are fully supported. + cols = self.columns.values() - `Table` differs from `NDData` by the assumption that the input data - consists of columns of homogeneous data, where each column has a unique - identifier and may contain additional metadata such as the data - units, format, and description. + if names is not None: + cols = [col for col in cols if col.info.name in names] - Parameters - ---------- - data : numpy ndarray, dict, list, or Table, optional - Data to initialize table. - names : list, optional - Specify column names - dtypes : list, optional - Specify column data types - meta : dict, optional - Metadata associated with the table - copy : boolean, optional - Copy the input data (default=True). + for col in cols: + col_descr = descr(col) - """ + if not (col.info.dtype.isnative or keep_byteorder): + new_dt = np.dtype(col_descr[1]).newbyteorder("=") + col_descr = (col_descr[0], new_dt, col_descr[2]) - def __init__(self, data=None, names=None, dtypes=None, meta=None, - copy=True): + dtype.append(col_descr) + data = empty_init(len(self), dtype=dtype) + for col in cols: + # When assigning from one array into a field of a structured array, + # Numpy will automatically swap those columns to their destination + # byte order where applicable + data[col.info.name] = col + + # For masked out, masked mixin columns need to set output mask attribute. + if masked and has_info_class(col, MixinInfo) and hasattr(col, "mask"): + data[col.info.name].mask = col.mask + + # Propagate the fill_value from the table column to the output array. + # If this is not done, then the output array will use numpy.ma's default + # fill values (999999 for ints, 1E20 for floats, "N/A" for strings) + if masked and hasattr(col, "fill_value"): + data[col.info.name].fill_value = col.fill_value + + return data + + def __init__( + self, + data=None, + masked=False, + names=None, + dtype=None, + meta=None, + copy=True, + rows=None, + copy_indices=True, + units=None, + descriptions=None, + **kwargs, + ): # Set up a placeholder empty table - self._data = None - self.columns = TableColumns() - self.meta = OrderedDict() if meta is None else deepcopy(meta) - - # Must copy if dtypes are changing - if not copy and dtypes is not None: - raise ValueError('Cannot specify dtypes when copy=False') + self._set_masked(masked) + self.columns = self.TableColumns() + self.formatter = self.TableFormatter() + self._copy_indices = True # copy indices from this Table by default + self._init_indices = copy_indices # whether to copy indices in init + self.primary_key = None + + # Must copy if dtype are changing + if not copy and dtype is not None: + raise ValueError("Cannot specify dtype when copy=False") + + # Specifies list of names found for the case of initializing table with + # a list of dict. If data are not list of dict then this is None. + names_from_list_of_dict = None + + # Row-oriented input, e.g. list of lists or list of tuples, list of + # dict, Row instance. Set data to something that the subsequent code + # will parse correctly. + if rows is not None: + if data is not None: + raise ValueError("Cannot supply both `data` and `rows` values") + if isinstance(rows, types.GeneratorType): + # Without this then the all(..) test below uses up the generator + rows = list(rows) + + # Get column names if `rows` is a list of dict, otherwise this is None + names_from_list_of_dict = _get_names_from_list_of_dict(rows) + if names_from_list_of_dict: + data = rows + elif isinstance(rows, self.Row) or ( + isinstance(rows, np.ndarray) and rows.dtype.names + ): + data = rows + else: + data = list(zip(*rows)) # Infer the type of the input data and set up the initialization # function, number of columns, and potentially the default col names default_names = None + # Handle custom (subclass) table attributes that are stored in meta. + # These are defined as class attributes using the TableAttribute + # descriptor. Any such attributes get removed from kwargs here and + # stored for use after the table is otherwise initialized. Any values + # provided via kwargs will have precedence over existing values from + # meta (e.g. from data as a Table or meta via kwargs). + meta_table_attrs = {} + if kwargs: + for attr in list(kwargs): + descr = getattr(self.__class__, attr, None) + if isinstance(descr, TableAttribute): + meta_table_attrs[attr] = kwargs.pop(attr) + + if hasattr(data, "__astropy_table__"): + # Data object implements the __astropy_table__ interface method. + # Calling that method returns an appropriate instance of + # self.__class__ and respects the `copy` arg. The returned + # Table object should NOT then be copied. + data = data.__astropy_table__(self.__class__, copy, **kwargs) + copy = None + elif kwargs: + raise TypeError( + f"__init__() got unexpected keyword argument {next(iter(kwargs.keys()))!r}" + ) + + # Treat any empty numpy array as None, except for structured arrays since they + # provide column names and dtypes. + # + # Init with rows=[] or data=[] (or tuples) is allowed and taken to mean no data. + # This allows supplying names and dtype if desired. `data=[]` is ambiguous, + # because it could mean no columns, or it could mean no rows for list of dict. + # For compatibility with the latter, interpret data=[] as data=None. + if ( + isinstance(data, np.ndarray) and data.size == 0 and not data.dtype.names + ) or (isinstance(data, (list, tuple)) and len(data) == 0): + data = None + + if isinstance(data, self.Row): + data = data._table[data._index : data._index + 1] + if isinstance(data, (list, tuple)): - init_func = self._init_from_list - n_cols = len(data) + # Get column names from `data` if it is a list of dict, otherwise this is None. + # This might be previously defined if `rows` was supplied as an init arg. + names_from_list_of_dict = ( + names_from_list_of_dict or _get_names_from_list_of_dict(data) + ) + if names_from_list_of_dict: + init_func = self._init_from_list_of_dicts + n_cols = len(names_from_list_of_dict) + else: + init_func = self._init_from_list + n_cols = len(data) elif isinstance(data, np.ndarray): if data.dtype.names: @@ -502,272 +823,1240 @@ def __init__(self, data=None, names=None, dtypes=None, meta=None, default_names = data.dtype.names else: init_func = self._init_from_ndarray # _homog + if data.shape == (): + raise ValueError("Can not initialize a Table with a scalar") + elif len(data.shape) == 1: + data = data[np.newaxis, :] n_cols = data.shape[1] - elif isinstance(data, dict): + elif isinstance(data, Mapping): init_func = self._init_from_dict - n_cols = len(data.keys()) - default_names = data.keys() + default_names = list(data) + n_cols = len(default_names) elif isinstance(data, Table): - init_func = self._init_from_table - n_cols = len(data.colnames) + # If user-input meta is None then use data.meta (if non-trivial) + if meta is None and data.meta: + # At this point do NOT deepcopy data.meta as this will happen after + # table init_func() is called. But for table input the table meta + # gets a key copy here if copy=False because later a direct object ref + # is used. + meta = data.meta if copy else data.meta.copy() + + # Handle indices on input table. Copy primary key and don't copy indices + # if the input Table is in non-copy mode. + self.primary_key = data.primary_key + self._init_indices = self._init_indices and data._copy_indices + + # Extract default names, n_cols, and then overwrite ``data`` to be the + # table columns so we can use _init_from_list. default_names = data.colnames + n_cols = len(default_names) + data = list(data.columns.values()) + + init_func = self._init_from_list elif data is None: if names is None: - return # Empty table - else: - init_func = self._init_from_list - n_cols = len(names) - data = [[]] * n_cols + if dtype is None: + # Table was initialized as `t = Table()`. Set up for empty + # table with names=[], data=[], and n_cols=0. + # self._init_from_list() will simply return, giving the + # expected empty table. + names = [] + else: + try: + # No data nor names but dtype is available. This must be + # valid to initialize a structured array. + dtype = np.dtype(dtype) + names = dtype.names + dtype = [dtype[name] for name in names] + except Exception: + raise ValueError( + "dtype was specified but could not be " + "parsed for column names" + ) + # names is guaranteed to be set at this point + init_func = self._init_from_list + n_cols = len(names) + data = [[]] * n_cols + else: - raise ValueError('Data type {0} not allowed to init Table' - .format(type(data))) + raise ValueError(f"Data type {type(data)} not allowed to init Table") - # Set up defaults if names and/or dtypes are not specified. + # Set up defaults if names and/or dtype are not specified. # A value of None means the actual value will be inferred # within the appropriate initialization routine, either from # existing specification or auto-generated. + if dtype is None: + dtype = [None] * n_cols + elif isinstance(dtype, np.dtype): + if default_names is None: + default_names = dtype.names + # Convert a numpy dtype input to a list of dtypes for later use. + dtype = [dtype[name] for name in dtype.names] + if names is None: names = default_names or [None] * n_cols - if dtypes is None: - dtypes = [None] * n_cols - self._check_names_dtypes(names, dtypes, n_cols) - # Finally do the real initialization - init_func(data, names, dtypes, n_cols, copy) + names = [None if name is None else str(name) for name in names] - def __array__(self, dtype=None): - """Support converting Table to np.array via np.array(table). + self._check_names_dtype(names, dtype, n_cols) - Coercion to a different dtype via np.array(table, dtype) is not - supported and will raise a ValueError. - """ - if dtype is not None: - raise ValueError('Datatype coercion is not allowed') + # Finally do the real initialization + init_func(data, names, dtype, n_cols, copy) - # This limitation is because of the following unexpected result that - # should have made a table copy while changing the column names. - # - # >>> d = astropy.table.Table([[1,2],[3,4]]) - # >>> np.array(d, dtype=[('a', 'i8'), ('b', 'i8')]) - # array([(0, 0), (0, 0)], - # dtype=[('a', '
\n{2}".format( - self.__len__(), ','.join(names), repr(self._data)) - return s + def __array__(self, dtype=None, copy=None): + """Support converting Table to np.array via np.array(table). - def __str__(self): - lines, n_header = _pformat_table(self) - return '\n'.join(lines) + Coercion to a different dtype via np.array(table, dtype) is not + supported and will raise a ValueError. + """ + if dtype is not None: + if np.dtype(dtype) != object: + raise ValueError("Datatype coercion is not allowed") - def pprint(self, max_lines=None, max_width=None, show_name=True, - show_units=False): - """Print a formatted string representation of the table. + out = np.array(None, dtype=object, copy=copy) + out[()] = self + return out + + # This limitation is because of the following unexpected result that + # should have made a table copy while changing the column names. + # + # >>> d = astropy.table.Table([[1,2],[3,4]]) + # >>> np.array(d, dtype=[('a', 'i8'), ('b', 'i8')]) + # array([(0, 0), (0, 0)], + # dtype=[('a', ' 1: + raise ValueError(f"Inconsistent data column lengths: {lengths}") + + # Make sure that all Column-based objects have correct class. For + # plain Table this is self.ColumnClass, but for instance QTable will + # convert columns with units to a Quantity mixin. + newcols = [self._convert_col_for_table(col) for col in cols] + self._make_table_from_cols(self, newcols) + + # Deduplicate indices. It may happen that after pickling or when + # initing from an existing table that column indices which had been + # references to a single index object got *copied* into an independent + # object. This results in duplicates which will cause downstream problems. + index_dict = {} + for col in self.itercols(): + for i, index in enumerate(col.info.indices or []): + names = tuple(ind_col.info.name for ind_col in index.columns) + if names in index_dict: + col.info.indices[i] = index_dict[names] + else: + index_dict[names] = index + + def _new_from_slice(self, slice_): + """Create a new table as a referenced slice from self.""" + table = self.__class__(masked=self.masked) + if self.meta: + table.meta = self.meta.copy() # Shallow copy for slice + table.primary_key = self.primary_key + + newcols = [] + new_indices = {} + for col in self.columns.values(): + newcol = col[slice_] + + # Note in line below, use direct attribute access to col.indices for Column + # instances instead of the generic col.info.indices. This saves about 4 usec + # per column. + if (col if isinstance(col, Column) else col.info).indices: + # TODO : as far as I can tell the only purpose of setting _copy_indices + # here is to communicate that to the initial test in `slice_indices`. + # Why isn't that just sent as an arg to the function? + col.info._copy_indices = self._copy_indices + newcol = col.info.slice_indices(newcol, slice_, len(col)) + + # The line above (unfortunately) makes a new *independent* index in each + # column for a multi-column index. This causes confusion later in + # Table.indices since that property checks for object uniqueness of each + # index. The root cause of making new independent indices should be + # fixed but this is not so easy. Since this is a less-common case, for + # now we do a post-facto fix of simply forcing indices to be one object, + # namely the first instance encountered in processing, keyed by id. + for ii, index in enumerate(newcol.info.indices): + newcol.info.indices[ii] = new_indices.setdefault(index.id, index) + + # Don't understand why this is forcing a value on the original column. + # Normally col.info does not even have a _copy_indices attribute. Tests + # still pass if this line is deleted. (Each col.info attribute access + # is expensive). + col.info._copy_indices = True + + newcols.append(newcol) + + self._make_table_from_cols( + table, newcols, verify=False, names=self.columns.keys() + ) + return table + + @staticmethod + def _make_table_from_cols(table, cols, verify=True, names=None): + """ + Make ``table`` in-place so that it represents the given list of ``cols``. + """ + if names is None: + names = [col.info.name for col in cols] + + # Note: we do not test for len(names) == len(cols) if names is not None. In that + # case the function is being called by from "trusted" source (e.g. right above here) + # that is assumed to provide valid inputs. In that case verify=False. + + if verify: + if None in names: + raise TypeError("Cannot have None for column name") + if len(set(names)) != len(names): + raise ValueError("Duplicate column names") + + table.columns = table.TableColumns( + (name, col) for name, col in zip(names, cols) + ) + + for col in cols: + table._set_col_parent_table_and_mask(col) + + def _set_col_parent_table_and_mask(self, col): + """ + Set ``col.parent_table = self`` and force ``col`` to have ``mask`` + attribute if the table is masked and ``col.mask`` does not exist. + """ + # For Column instances it is much faster to do direct attribute access + # instead of going through .info + col_info = col if isinstance(col, Column) else col.info + col_info.parent_table = self + + # Legacy behavior for masked table + if self.masked and not hasattr(col, "mask"): + col.mask = FalseArray(col.shape) - show_units : bool - Include a header row for units (default=False) + def itercols(self): """ + Iterate over the columns of this table. + + Examples + -------- + To iterate over the columns of a table:: + + >>> t = Table([[1], [2]]) + >>> for col in t.itercols(): + ... print(col) + col0 + ---- + 1 + col1 + ---- + 2 + + Using ``itercols()`` is similar to ``for col in t.columns.values()`` + but is syntactically preferred. + """ + for colname in self.columns: + yield self[colname] + + def _base_repr_( + self, + html=False, + descr_vals=None, + max_width=None, + tableid=None, + show_dtype=True, + max_lines=None, + tableclass=None, + ): + if descr_vals is None: + descr_vals = [self.__class__.__name__] + if self.masked: + descr_vals.append("masked=True") + descr_vals.append(f"length={len(self)}") + + descr = " ".join(descr_vals) + if html: + from astropy.utils.xml.writer import xml_escape + + descr = f"{xml_escape(descr)}\n" + else: + descr = f"<{descr}>\n" + + if tableid is None: + tableid = f"table{id(self)}" + + data_lines, outs = self.formatter._pformat_table( + self, + tableid=tableid, + html=html, + max_width=max_width, + show_name=True, + show_unit=None, + show_dtype=show_dtype, + max_lines=max_lines, + tableclass=tableclass, + ) + + out = descr + "\n".join(data_lines) + + return out + + def _repr_html_(self): + out = self._base_repr_( + html=True, max_width=-1, tableclass=conf.default_notebook_table_class + ) + # Wrap
in
. This follows the pattern in pandas and allows + # table to be scrollable horizontally in VS Code notebook display. + out = f"
{out}
" + return out + + def __repr__(self): + return self._base_repr_(html=False, max_width=None) + + def __str__(self): + return "\n".join(self.pformat(max_lines=None, max_width=None)) + + def __bytes__(self): + return str(self).encode("utf-8") + + @property + def has_mixin_columns(self): + """ + True if table has any mixin columns (defined as columns that are not Column + subclasses). + """ + return any(has_info_class(col, MixinInfo) for col in self.columns.values()) + + @property + def has_masked_columns(self): + """True if table has any ``MaskedColumn`` columns. + + This does not check for mixin columns that may have masked values, use the + ``has_masked_values`` property in that case. + + """ + return any(isinstance(col, MaskedColumn) for col in self.itercols()) + + @property + def has_masked_values(self): + """True if column in the table has values which are masked. + + This may be relatively slow for large tables as it requires checking the mask + values of each column. + """ + return any( + hasattr(col, "mask") and np.any(col.mask != np.zeros((), col.mask.dtype)) + for col in self.itercols() + ) + + def _is_mixin_for_table(self, col): + """ + Determine if ``col`` should be added to the table directly as + a mixin column. + """ + if isinstance(col, BaseColumn): + return False + + # Is it a mixin but not [Masked]Quantity (which gets converted to + # [Masked]Column with unit set). + return has_info_class(col, MixinInfo) and not has_info_class(col, QuantityInfo) + + @format_doc(_pprint_docs) + def pprint( + self, + max_lines=None, + max_width=None, + show_name=True, + show_unit=None, + show_dtype=False, + align=None, + ): + """Print a formatted string representation of the table. + + If no value of ``max_lines`` is supplied then the height of the + screen terminal is used to set ``max_lines``. If the terminal + height cannot be determined then the default is taken from the + configuration item ``astropy.conf.max_lines``. If a negative + value of ``max_lines`` is supplied then there is no line limit + applied. + + The same applies for max_width except the configuration item is + ``astropy.conf.max_width``. + + """ + lines, outs = self.formatter._pformat_table( + self, + max_lines, + max_width, + show_name=show_name, + show_unit=show_unit, + show_dtype=show_dtype, + align=align, + ) + if outs["show_length"]: + lines.append(f"Length = {len(self)} rows") + + n_header = outs["n_header"] - lines, n_header = _pformat_table(self, max_lines, max_width, show_name, - show_units) for i, line in enumerate(lines): if i < n_header: - color_print(line, 'red') + color_print(line, "red") else: - print line + print(line) + + @format_doc(_pprint_docs) + def pprint_all( + self, + max_lines=-1, + max_width=-1, + show_name=True, + show_unit=None, + show_dtype=False, + align=None, + ): + """Print a formatted string representation of the entire table. + + This method is the same as `astropy.table.Table.pprint` except that + the default ``max_lines`` and ``max_width`` are both -1 so that by + default the entire table is printed instead of restricting to the size + of the screen terminal. - def pformat(self, max_lines=None, max_width=None, show_name=True, - show_units=False): - """Return a list of lines for the formatted string representation of - the table. + """ + return self.pprint( + max_lines, max_width, show_name, show_unit, show_dtype, align + ) + + def _make_index_row_display_table(self, index_row_name): + if index_row_name not in self.columns: + idx_col = self.ColumnClass(name=index_row_name, data=np.arange(len(self))) + return self.__class__([idx_col] + list(self.columns.values()), copy=False) + else: + return self - If no value of ``max_lines`` is supplied then the height of the screen - terminal is used to set ``max_lines``. If the terminal height cannot - be determined then a default of ``astropy.table.pprint.MAX_LINES`` is - used. If a negative value of ``max_lines`` is supplied then there is - no line limit applied. + def show_in_notebook(self, *, backend="ipydatagrid", **kwargs): + """Render the table in HTML and show it in the Jupyter notebook. - The Same applies for max_width except the default is - ``astropy.table.pprint.MAX_WIDTH``. + .. note:: The method API was modified in v7.0 to include a ``backend`` + argument and require only keyword arguments. Parameters ---------- - max_lines : int or None - Maximum number of rows to output + backend : {"ipydatagrid", "classic"} + Backend to use for rendering (default="ipydatagrid"). The "classic" backend + is deprecated since v6.1. - max_width : int or None - Maximum character width of output + **kwargs : dict, optional + Keyword arguments as accepted by desired backend. See `astropy.table.notebook_backends` + for the available backends and their respective keyword arguments. - show_name : bool - Include a header row for column names (default=True) + Raises + ------ + NotImplementedError + Requested backend is not supported. - show_units : bool - Include a header row for units (default=False) + See Also + -------- + astropy.table.notebook_backends + + """ + if backend == "ipydatagrid": + try: + import pandas # noqa: F401 + + from astropy.table.notebook_backends import ipydatagrid + except ImportError: + raise ImportError( + "The default option for show_in_notebook now requires pandas " + "and ipydatagrid to also be installed, or please consider using the astropy[jupyter] extras" + ) from None + + func = ipydatagrid + + elif backend == "classic": + from astropy.table.notebook_backends import classic + + # NOTE: The leading whitespace in warning lines is for backward compatibility. + warnings.warn( + "'classic' backend for show_in_notebook() is deprecated as of 6.1. " + "Instead, use the supported backend 'ipydatagrid'.", + AstropyDeprecationWarning, + ) + func = classic + + else: + raise NotImplementedError( + f'"{backend}" backend is not supported for rendering Astropy table ' + "in Jupyter notebook." + ) + + return func(self, **kwargs) + + @deprecated( + "6.1", + pending=True, + message="""We are planning on deprecating show_in_browser in the future. + If you are actively using this method, please let us know + at https://github.com/astropy/astropy/issues/16067""", + ) + def show_in_browser( + self, + max_lines=5000, + jsviewer=False, + browser="default", + jskwargs={"use_local_files": False}, + tableid=None, + table_class="display compact", + css=None, + show_row_index="idx", + ): + """Render the table in HTML and show it in a web browser. + + Parameters + ---------- + max_lines : int + Maximum number of rows to export to the table (set low by default + to avoid memory issues, since the browser view requires duplicating + the table in memory). A negative value of ``max_lines`` indicates + no row limit. + jsviewer : bool + If `True`, prepends some javascript headers so that the table is + rendered as a `DataTables `_ data table. + This allows in-browser searching & sorting, but requires a + connection to the internet to load the necessary javascript + libraries from a CDN. Working offline may work in limited + circumstances, if the browser has cached the necessary libraries + from a previous use of this method. + browser : str + Any legal browser name, e.g. ``'firefox'``, ``'chrome'``, + ``'safari'`` (for mac, you may need to use ``'open -a + "/Applications/Google Chrome.app" {}'`` for Chrome). If + ``'default'``, will use the system default browser. + jskwargs : dict + Passed to the `astropy.table.JSViewer` init. Defaults to + ``{'use_local_files': False}`` which means that the JavaScript + libraries will be loaded from a CDN. + tableid : str or None + An html ID tag for the table. Default is ``table{id}``, where id + is the unique integer id of the table object, id(self). + table_class : str or None + A string with a list of HTML classes used to style the table. + Default is "display compact", and other possible values can be + found in https://www.datatables.net/manual/styling/classes + css : str + A valid CSS string declaring the formatting for the table. Defaults + to ``astropy.table.jsviewer.DEFAULT_CSS``. + show_row_index : str or False + If this does not evaluate to False, a column with the given name + will be added to the version of the table that gets displayed. + This new column shows the index of the row in the table itself, + even when the displayed table is re-sorted by another column. Note + that if a column with this name already exists, this option will be + ignored. Defaults to "idx". + """ + import tempfile + import webbrowser + from urllib.parse import urljoin + from urllib.request import pathname2url + + from .jsviewer import DEFAULT_CSS + + if css is None: + css = DEFAULT_CSS + + # We can't use NamedTemporaryFile here because it gets deleted as + # soon as it gets garbage collected. + tmpdir = tempfile.mkdtemp() + path = Path(tmpdir, "table.html") + + with path.open("w") as tmp: + if jsviewer: + if show_row_index: + display_table = self._make_index_row_display_table(show_row_index) + else: + display_table = self + display_table.write( + tmp, + format="jsviewer", + css=css, + max_lines=max_lines, + jskwargs=jskwargs, + table_id=tableid, + table_class=table_class, + ) + else: + self.write(tmp, format="html") + + try: + br = webbrowser.get(None if browser == "default" else browser) + except webbrowser.Error: + log.error(f"Browser '{browser}' not found.") + else: + br.open(urljoin("file:", pathname2url(str(path)))) + + @format_doc(_pformat_docs, id="{id}") + def pformat( + self, + max_lines=-1, + max_width=-1, + show_name=True, + show_unit=None, + show_dtype=False, + html=False, + tableid=None, + align=None, + tableclass=None, + ): + """Return a list of lines for the formatted string representation of + the table. + + If ``max_lines=None`` is supplied then the height of the + screen terminal is used to set ``max_lines``. If the terminal + height cannot be determined then the default will be + determined using the ``astropy.conf.max_lines`` configuration + item. If a negative value of ``max_lines`` is supplied then + there is no line limit applied (default). + + The same applies for ``max_width`` except the configuration item is + ``astropy.conf.max_width``. - Returns - ------- - lines : list - Formatted table as a list of strings """ - lines, n_header = _pformat_table(self, max_lines, max_width, - show_name, show_units) + lines, outs = self.formatter._pformat_table( + self, + max_lines, + max_width, + show_name=show_name, + show_unit=show_unit, + show_dtype=show_dtype, + html=html, + tableid=tableid, + tableclass=tableclass, + align=align, + ) + + if outs["show_length"]: + lines.append(f"Length = {len(self)} rows") + return lines - def more(self, max_lines=None, max_width=None, show_name=True, - show_units=False): + @deprecated(since="7.0", alternative="Table.pformat") + @format_doc(_pformat_docs, id="{id}") + def pformat_all( + self, + max_lines=-1, + max_width=-1, + show_name=True, + show_unit=None, + show_dtype=False, + html=False, + tableid=None, + align=None, + tableclass=None, + ): + """Return a list of lines for the formatted string representation of + the entire table. + + If ``max_lines=None`` is supplied then the height of the + screen terminal is used to set ``max_lines``. If the terminal + height cannot be determined then the default will be + determined using the ``astropy.conf.max_lines`` configuration + item. If a negative value of ``max_lines`` is supplied then + there is no line limit applied (default). + + The same applies for ``max_width`` except the configuration item is + ``astropy.conf.max_width``. + + """ + return self.pformat( + max_lines, + max_width, + show_name, + show_unit, + show_dtype, + html, + tableid, + align, + tableclass, + ) + + def more( + self, + max_lines=None, + max_width=None, + show_name=True, + show_unit=None, + show_dtype=False, + ): """Interactively browse table with a paging interface. Supported keys:: @@ -791,81 +2080,224 @@ def more(self, max_lines=None, max_width=None, show_name=True, Maximum character width of output show_name : bool - Include a header row for column names (default=True) + Include a header row for column names. Default is True. - show_units : bool - Include a header row for units (default=False) + show_unit : bool + Include a header row for unit. Default is to show a row + for units only if one or more columns has a defined value + for the unit. + + show_dtype : bool + Include a header row for column dtypes. Default is False. """ - _more_tabcol(self, max_lines, max_width, show_name, - show_units) + self.formatter._more_tabcol( + self, + max_lines, + max_width, + show_name=show_name, + show_unit=show_unit, + show_dtype=show_dtype, + ) def __getitem__(self, item): - if isinstance(item, basestring): + if isinstance(item, str): return self.columns[item] - elif isinstance(item, int): - return Row(self, item) - elif isinstance(item, tuple): - if any(x not in set(self.colnames) for x in item): - raise ValueError('Table column slice must contain only valid ' - 'column names') - return Table([self[x] for x in item], meta=deepcopy(self.meta)) - elif (isinstance(item, slice) or isinstance(item, np.ndarray) - or isinstance(item, list)): + elif isinstance(item, (int, np.integer)): + return self.Row(self, item) + elif ( + isinstance(item, np.ndarray) and item.shape == () and item.dtype.kind == "i" + ): + return self.Row(self, item.item()) + elif self._is_list_or_tuple_of_str(item): + out = self.__class__( + [self[x] for x in item], copy_indices=self._copy_indices + ) + out._groups = groups.TableGroups( + out, indices=self.groups._indices, keys=self.groups._keys + ) + out.meta = self.meta.copy() # Shallow copy for meta + return out + elif (isinstance(item, np.ndarray) and item.size == 0) or ( + isinstance(item, (tuple, list)) and not item + ): + # If item is an empty array/list/tuple then return the table with no rows + return self._new_from_slice([]) + elif isinstance(item, (slice, np.ndarray, list)) or ( + isinstance(item, tuple) and all(isinstance(x, np.ndarray) for x in item) + ): + # here for the many ways to give a slice; a tuple of ndarray + # is produced by np.where, as in t[np.where(t['a'] > 2)] + # For all, a new table is constructed with slice of all columns return self._new_from_slice(item) else: - raise ValueError('Illegal type {0} for table item access' - .format(type(item))) + raise ValueError(f"Illegal type {type(item)} for table item access") def __setitem__(self, item, value): - try: - self._data[item] = value - except (ValueError, KeyError, TypeError): - raise KeyError("Column {0} does not exist".format(item)) - except: - raise + # If the item is a string then it must be the name of a column. + # If that column doesn't already exist then create it now. + if isinstance(item, str) and item not in self.colnames: + self.add_column(value, name=item, copy=True) + + else: + n_cols = len(self.columns) + + if isinstance(item, str): + # Set an existing column by first trying to replace, and if + # this fails do an in-place update. See definition of mask + # property for discussion of the _setitem_inplace attribute. + if ( + not getattr(self, "_setitem_inplace", False) + and not conf.replace_inplace + ): + try: + self._replace_column_warnings(item, value) + return + except Exception: + pass + self.columns[item][:] = value + + elif isinstance(item, (int, np.integer)): + self._set_row(idx=item, colnames=self.colnames, vals=value) + + elif isinstance(item, (slice, np.ndarray, list)) or ( + isinstance(item, tuple) and all(isinstance(x, np.ndarray) for x in item) + ): + if isinstance(value, Table): + vals = (col for col in value.columns.values()) + + elif isinstance(value, np.ndarray) and value.dtype.names: + vals = (value[name] for name in value.dtype.names) + + elif np.isscalar(value): + vals = itertools.repeat(value, n_cols) + + else: # Assume this is an iterable that will work + if len(value) != n_cols: + raise ValueError( + f"Right side value needs {n_cols} elements (one for each column)" + ) + vals = value + + for col, val in zip(self.columns.values(), vals): + col[item] = val + + else: + raise ValueError(f"Illegal type {type(item)} for table item access") def __delitem__(self, item): - if isinstance(item, basestring): + if isinstance(item, str): self.remove_column(item) - elif isinstance(item, tuple): + elif isinstance(item, (int, np.integer)): + self.remove_row(item) + elif isinstance(item, (list, tuple, np.ndarray)) and all( + isinstance(x, str) for x in item + ): self.remove_columns(item) - - def __iter__(self): - self._iter_index = 0 - return self - - def __next__(self): - """Python 3 iterator""" - if self._iter_index < len(self._data): - val = self[self._iter_index] - self._iter_index += 1 - return val + elif ( + isinstance(item, (list, np.ndarray)) and np.asarray(item).dtype.kind == "i" + ): + self.remove_rows(item) + elif isinstance(item, slice): + self.remove_rows(item) else: - raise StopIteration + raise IndexError("illegal key or index value") - if sys.version_info[0] < 3: # pragma: py2 - next = __next__ + def _ipython_key_completions_(self): + return self.colnames def field(self, item): """Return column[item] for recarray compatibility.""" return self.columns[item] + @property + def masked(self): + return self._masked + + @masked.setter + def masked(self, masked): + raise Exception( + "Masked attribute is read-only (use t = Table(t, masked=True)" + " to convert to a masked table)" + ) + + def _set_masked(self, masked): + """ + Set the table masked property. + + Parameters + ---------- + masked : bool + State of table masking (`True` or `False`) + """ + if masked in [True, False, None]: + self._masked = masked + else: + raise ValueError("masked should be one of True, False, None") + + self._column_class = self.MaskedColumn if self._masked else self.Column + + @property + def ColumnClass(self): + if self._column_class is None: + return self.Column + else: + return self._column_class + @property def dtype(self): - return self._data.dtype + return np.dtype([descr(col) for col in self.columns.values()]) @property def colnames(self): return list(self.columns.keys()) + @staticmethod + def _is_list_or_tuple_of_str(names): + """Check that ``names`` is a tuple or list of strings.""" + return ( + isinstance(names, (tuple, list)) + and names + and all(isinstance(x, str) for x in names) + ) + def keys(self): return list(self.columns.keys()) + def values(self): + return self.columns.values() + + def items(self): + return self.columns.items() + def __len__(self): - if self._data is None: - return 0 + # For performance reasons (esp. in Row) cache the first column name + # and use that subsequently for the table length. If might not be + # available yet or the column might be gone now, in which case + # try again in the except block. + try: + return len(OrderedDict.__getitem__(self.columns, self._first_colname)) + except (AttributeError, KeyError): + if len(self.columns) == 0: + return 0 + + # Get the first column name + self._first_colname = next(iter(self.columns)) + return len(self.columns[self._first_colname]) + + def __or__(self, other): + if isinstance(other, Table): + updated_table = self.copy() + updated_table.update(other) + return updated_table else: - return len(self._data) + return NotImplemented + + def __ior__(self, other): + try: + self.update(other) + return self + except TypeError: + return NotImplemented def index_column(self, name): """ @@ -880,61 +2312,586 @@ def index_column(self, name): ------- index : int Positional index of column ``name``. + + Examples + -------- + Create a table with three columns 'a', 'b' and 'c':: + + >>> t = Table([[1, 2, 3], [0.1, 0.2, 0.3], ['x', 'y', 'z']], + ... names=('a', 'b', 'c')) + >>> print(t) + a b c + --- --- --- + 1 0.1 x + 2 0.2 y + 3 0.3 z + + Get index of column 'b' of the table:: + + >>> t.index_column('b') + 1 """ try: return self.colnames.index(name) except ValueError: - raise ValueError("Column {0} does not exist".format(name)) - - def add_column(self, col, index=None): + raise ValueError(f"Column {name} does not exist") + + def add_column( + self, + col, + index=None, + name=None, + rename_duplicate=False, + copy=True, + default_name=None, + ): """ - Add a new Column object ``col`` to the table. If ``index`` + Add a new column to the table using ``col`` as input. If ``index`` is supplied then insert column before ``index`` position in the list of columns, otherwise append column to the end of the list. + The ``col`` input can be any data object which is acceptable as a + `~astropy.table.Table` column object or can be converted. This includes + mixin columns and scalar or length=1 objects which get broadcast to match + the table length. + + To add several columns at once use ``add_columns()`` or simply call + ``add_column()`` for each one. There is very little performance difference + in the two approaches. + Parameters ---------- - col : Column - Column object to add. + col : object + Data object for the new column index : int or None - Insert column before this position or at end (default) + Insert column before this position or at end (default). + name : str + Column name + rename_duplicate : bool + Uniquify column name if it already exist. Default is False. + copy : bool + Make a copy of the new column. Default is True. + default_name : str or None + Name to use if both ``name`` and ``col.info.name`` are not available. + Defaults to ``col{number_of_columns}``. + + Examples + -------- + Create a table with two columns 'a' and 'b', then create a third column 'c' + and append it to the end of the table:: + + >>> t = Table([[1, 2], [0.1, 0.2]], names=('a', 'b')) + >>> col_c = Column(name='c', data=['x', 'y']) + >>> t.add_column(col_c) + >>> print(t) + a b c + --- --- --- + 1 0.1 x + 2 0.2 y + + Add column 'd' at position 1. Note that the column is inserted + before the given index:: + + >>> t.add_column(['a', 'b'], name='d', index=1) + >>> print(t) + a d b c + --- --- --- --- + 1 a 0.1 x + 2 b 0.2 y + + Add second column named 'b' with rename_duplicate:: + + >>> t = Table([[1, 2], [0.1, 0.2]], names=('a', 'b')) + >>> t.add_column(1.1, name='b', rename_duplicate=True) + >>> print(t) + a b b_1 + --- --- --- + 1 0.1 1.1 + 2 0.2 1.1 + + Add an unnamed column or mixin object in the table using a default name + or by specifying an explicit name with ``name``. Name can also be overridden:: + + >>> t = Table([[1, 2], [0.1, 0.2]], names=('a', 'b')) + >>> t.add_column(['a', 'b']) + >>> t.add_column(col_c, name='d') + >>> print(t) + a b col2 d + --- --- ---- --- + 1 0.1 a x + 2 0.2 b y """ - if index is None: - index = len(self.columns) - self.add_columns([col], [index]) - - def add_columns(self, cols, indexes=None): + if default_name is None: + default_name = f"col{len(self.columns)}" + + # Convert col data to acceptable object for insertion into self.columns. + # Note that along with the lines above and below, this allows broadcasting + # of scalars to the correct shape for adding to table. + col = self._convert_data_to_col( + col, name=name, copy=copy, default_name=default_name + ) + + # For scalars and for arrays with length 1, allow broadcasting to the + # length of the table. This includes zero-length tables, i.e., we + # broadcast the column to zero length (since astropy 7.0; this + # follows numpy behaviour; see gh-17078 for discussion). + # If the table is not yet initialized, we use the column for its length, + # which for a scalar will be length zero (which is most easily achieved + # by pass-through here). + if col.shape == () or (col.shape[0] == 1 and self.columns): + new_shape = (len(self),) + getattr(col, "shape", ())[1:] + col = np.broadcast_to(col, shape=new_shape, subok=True) + # broadcast_to() just creates a read-only view that looks like the + # broadcasted array, but we want a full, writable version. So copy. + col = col_copy(col) + + name = col.info.name + + # Ensure that new column is the right length + if len(self.columns) > 0 and len(col) != len(self): + raise ValueError("Inconsistent data column lengths") + + if rename_duplicate: + orig_name = name + i = 1 + while name in self.columns: + # Iterate until a unique name is found + name = orig_name + "_" + str(i) + i += 1 + col.info.name = name + + # Set col parent_table weakref and ensure col has mask attribute if table.masked + self._set_col_parent_table_and_mask(col) + + # Add new column as last column + self.columns[name] = col + + if index is not None: + # Move the other cols to the right of the new one + move_names = self.colnames[index:-1] + for move_name in move_names: + self.columns.move_to_end(move_name, last=True) + + def add_columns( + self, cols, indexes=None, names=None, copy=True, rename_duplicate=False + ): """ - Add a list of new Column objects ``cols`` to the table. If a - corresponding list of ``indexes`` is supplied then insert column before - each ``index`` position in the *original* list of columns, otherwise - append columns to the end of the list. + Add a list of new columns the table using ``cols`` data objects. If a + corresponding list of ``indexes`` is supplied then insert column + before each ``index`` position in the *original* list of columns, + otherwise append columns to the end of the list. + + The ``cols`` input can include any data objects which are acceptable as + `~astropy.table.Table` column objects or can be converted. This includes + mixin columns and scalar or length=1 objects which get broadcast to match + the table length. + + From a performance perspective there is little difference between calling + this method once or looping over the new columns and calling ``add_column()`` + for each column. Parameters ---------- - cols : list of Columns - Column objects to add. - indexes : list of ints or None - Insert column before this position or at end (default) + cols : list of object + List of data objects for the new columns + indexes : list of int or None + Insert column before this position or at end (default). + names : list of str + Column names + copy : bool + Make a copy of the new columns. Default is True. + rename_duplicate : bool + Uniquify new column names if they duplicate the existing ones. + Default is False. + + See Also + -------- + astropy.table.hstack, update, replace_column + + Examples + -------- + Create a table with two columns 'a' and 'b', then create columns 'c' and 'd' + and append them to the end of the table:: + + >>> t = Table([[1, 2], [0.1, 0.2]], names=('a', 'b')) + >>> col_c = Column(name='c', data=['x', 'y']) + >>> col_d = Column(name='d', data=['u', 'v']) + >>> t.add_columns([col_c, col_d]) + >>> print(t) + a b c d + --- --- --- --- + 1 0.1 x u + 2 0.2 y v + + Add column 'c' at position 0 and column 'd' at position 1. Note that + the columns are inserted before the given position:: + + >>> t = Table([[1, 2], [0.1, 0.2]], names=('a', 'b')) + >>> t.add_columns([['x', 'y'], ['u', 'v']], names=['c', 'd'], + ... indexes=[0, 1]) + >>> print(t) + c a d b + --- --- --- --- + x 1 u 0.1 + y 2 v 0.2 + + Add second column 'b' and column 'c' with ``rename_duplicate``:: + + >>> t = Table([[1, 2], [0.1, 0.2]], names=('a', 'b')) + >>> t.add_columns([[1.1, 1.2], ['x', 'y']], names=('b', 'c'), + ... rename_duplicate=True) + >>> print(t) + a b b_1 c + --- --- --- --- + 1 0.1 1.1 x + 2 0.2 1.2 y + + Add unnamed columns or mixin objects in the table using default names + or by specifying explicit names with ``names``. Names can also be overridden:: + + >>> t = Table() + >>> col_b = Column(name='b', data=['u', 'v']) + >>> t.add_columns([[1, 2], col_b]) + >>> t.add_columns([[3, 4], col_b], names=['c', 'd']) + >>> print(t) + col0 b c d + ---- --- --- --- + 1 u 3 u + 2 v 4 v """ if indexes is None: indexes = [len(self.columns)] * len(cols) elif len(indexes) != len(cols): - raise ValueError('Number of indexes must match number of cols') + raise ValueError("Number of indexes must match number of cols") + + if names is None: + names = (None,) * len(cols) + elif len(names) != len(cols): + raise ValueError("Number of names must match number of cols") + + default_names = [f"col{ii + len(self.columns)}" for ii in range(len(cols))] + + for ii in reversed(np.argsort(indexes, kind="stable")): + self.add_column( + cols[ii], + index=indexes[ii], + name=names[ii], + default_name=default_names[ii], + rename_duplicate=rename_duplicate, + copy=copy, + ) + + def _replace_column_warnings(self, name, col): + """ + Same as replace_column but issues warnings under various circumstances. + """ + warns = conf.replace_warnings + refcount = None + old_col = None + + # sys.getrefcount is CPython specific and not on PyPy. + if ( + "refcount" in warns + and name in self.colnames + and hasattr(sys, "getrefcount") + ): + refcount = sys.getrefcount(self[name]) + + if name in self.colnames: + old_col = self[name] + + # This may raise an exception (e.g. t['a'] = 1) in which case none of + # the downstream code runs. + self.replace_column(name, col) + + if "always" in warns: + warnings.warn( + f"replaced column '{name}'", TableReplaceWarning, stacklevel=3 + ) + + if "slice" in warns: + try: + # Check for ndarray-subclass slice. An unsliced instance + # has an ndarray for the base while sliced has the same class + # as parent. + if isinstance(old_col.base, old_col.__class__): + msg = ( + f"replaced column '{name}' which looks like an array slice. " + "The new column no longer shares memory with the " + "original array." + ) + warnings.warn(msg, TableReplaceWarning, stacklevel=3) + except AttributeError: + pass + + # sys.getrefcount is CPython specific and not on PyPy. + if "refcount" in warns and hasattr(sys, "getrefcount"): + # Did reference count change? + new_refcount = sys.getrefcount(self[name]) + if refcount != new_refcount: + msg = ( + f"replaced column '{name}' and the number of references " + "to the column changed." + ) + warnings.warn(msg, TableReplaceWarning, stacklevel=3) + + if "attributes" in warns: + # Any of the standard column attributes changed? + changed_attrs = [] + new_col = self[name] + # Check base DataInfo attributes that any column will have + for attr in DataInfo.attr_names: + if getattr(old_col.info, attr) != getattr(new_col.info, attr): + changed_attrs.append(attr) + + if changed_attrs: + msg = ( + f"replaced column '{name}' and column attributes " + f"{changed_attrs} changed." + ) + warnings.warn(msg, TableReplaceWarning, stacklevel=3) + + def replace_column(self, name, col, copy=True): + """ + Replace column ``name`` with the new ``col`` object. + + The behavior of ``copy`` for Column objects is: + - copy=True: new class instance with a copy of data and deep copy of meta + - copy=False: new class instance with same data and a key-only copy of meta + + For mixin columns: + - copy=True: new class instance with copy of data and deep copy of meta + - copy=False: original instance (no copy at all) + + Parameters + ---------- + name : str + Name of column to replace + col : `~astropy.table.Column` or `~numpy.ndarray` or sequence + New column object to replace the existing column. + copy : bool + Make copy of the input ``col``, default=True + + See Also + -------- + add_columns, astropy.table.hstack, update + + Examples + -------- + Replace column 'a' with a float version of itself:: + + >>> t = Table([[1, 2, 3], [0.1, 0.2, 0.3]], names=('a', 'b')) + >>> float_a = t['a'].astype(float) + >>> t.replace_column('a', float_a) + """ + if name not in self.colnames: + raise ValueError(f"column name {name} is not in the table") + + if self[name].info.indices: + raise ValueError("cannot replace a table index column") - if self._data is None: - # No existing table data, init from cols - newcols = cols + col = self._convert_data_to_col(col, name=name, copy=copy) + if col.shape == (): + raise ValueError("cannot replace a column with a scalar.") + + self._set_col_parent_table_and_mask(col) + + # Ensure that new column is the right length, unless it is the only column + # in which case re-sizing is allowed. + if len(self.columns) > 1 and len(col) != len(self[name]): + raise ValueError("length of new column must match table length") + + self.columns.__setitem__(name, col, validated=True) + + def remove_row(self, index): + """ + Remove a row from the table. + + Parameters + ---------- + index : int + Index of row to remove + + Examples + -------- + Create a table with three columns 'a', 'b' and 'c':: + + >>> t = Table([[1, 2, 3], [0.1, 0.2, 0.3], ['x', 'y', 'z']], + ... names=('a', 'b', 'c')) + >>> print(t) + a b c + --- --- --- + 1 0.1 x + 2 0.2 y + 3 0.3 z + + Remove row 1 from the table:: + + >>> t.remove_row(1) + >>> print(t) + a b c + --- --- --- + 1 0.1 x + 3 0.3 z + + To remove several rows at the same time use remove_rows. + """ + # check the index against the types that work with np.delete + if not isinstance(index, (int, np.integer)): + raise TypeError("Row index must be an integer") + self.remove_rows(index) + + def remove_rows(self, row_specifier): + """ + Remove rows from the table. + + Parameters + ---------- + row_specifier : slice or int or array of int + Specification for rows to remove + + Examples + -------- + Create a table with three columns 'a', 'b' and 'c':: + + >>> t = Table([[1, 2, 3], [0.1, 0.2, 0.3], ['x', 'y', 'z']], + ... names=('a', 'b', 'c')) + >>> print(t) + a b c + --- --- --- + 1 0.1 x + 2 0.2 y + 3 0.3 z + + Remove rows 0 and 2 from the table:: + + >>> t.remove_rows([0, 2]) + >>> print(t) + a b c + --- --- --- + 2 0.2 y + + + Note that there are no warnings if the slice operator extends + outside the data:: + + >>> t = Table([[1, 2, 3], [0.1, 0.2, 0.3], ['x', 'y', 'z']], + ... names=('a', 'b', 'c')) + >>> t.remove_rows(slice(10, 20, 1)) + >>> print(t) + a b c + --- --- --- + 1 0.1 x + 2 0.2 y + 3 0.3 z + """ + # If the table has been sliced then each index will have original=False + # indicating that the data are a sliced reference (not from the original table). + sliced = any(not index.original for index in self.indices) + if not sliced: + # For the not-sliced case we can use the remove_rows method to efficiently + # update the existing indices. + for index in self.indices: + index.remove_rows(row_specifier) else: - newcols = list(self.columns.values()) - new_indexes = list(range(len(newcols) + 1)) - for col, index in zip(cols, indexes): - i = new_indexes.index(index) - new_indexes.insert(i, None) - newcols.insert(i, col) + # Removing rows in a sliced table requires fully remaking the indices. Each + # such SlicedIndex has a reference to the original table index and the + # slice, and it is not possible to maintain that if a row is removed. First + # remove all the existing indices but keep track of the index column names + # to later remake the indices. + indices_colnames = [ + tuple(col.info.name for col in index.columns) for index in self.indices + ] + for col in self.itercols(): + # Note - `indices` is a property of BaseColumnInfo and will always exist + # (and be a list) on col.info. + col.info.indices.clear() + + keep_mask = np.ones(len(self), dtype=bool) + keep_mask[row_specifier] = False + + columns = self.TableColumns() + for name, col in self.columns.items(): + newcol = col[keep_mask] + newcol.info.parent_table = self + columns[name] = newcol + + self._replace_cols(columns) + + if sliced: + # For the sliced case, re-create the indices (in order) after row removal. + # This will also preserve the first index as the primary key. + for index_colnames in indices_colnames: + self.add_index(index_colnames) + + # Revert groups to default (ungrouped) state + if hasattr(self, "_groups"): + del self._groups + + def iterrows(self, *names): + """ + Iterate over rows of table returning a tuple of values for each row. + + This method is especially useful when only a subset of columns are needed. + + The ``iterrows`` method can be substantially faster than using the standard + Table row iteration (e.g. ``for row in tbl:``), since that returns a new + ``~astropy.table.Row`` object for each row and accessing a column in that + row (e.g. ``row['col0']``) is slower than tuple access. + + Parameters + ---------- + names : list + List of column names (default to all columns if no names provided) + + Returns + ------- + rows : iterable + Iterator returns tuples of row values - self._init_from_cols(newcols) + Examples + -------- + Create a table with three columns 'a', 'b' and 'c':: + + >>> t = Table({'a': [1, 2, 3], + ... 'b': [1.0, 2.5, 3.0], + ... 'c': ['x', 'y', 'z']}) + + To iterate row-wise using column names:: + + >>> for a, c in t.iterrows('a', 'c'): + ... print(a, c) + 1 x + 2 y + 3 z + + """ + if len(names) == 0: + names = self.colnames + else: + for name in names: + if name not in self.colnames: + raise ValueError(f"{name} is not a valid column name") + + cols = (self[name] for name in names) + out = zip(*cols) + return out + + def _set_of_names_in_colnames(self, names): + """Return ``names`` as a set if valid, or raise a `KeyError`. + + ``names`` is valid if all elements in it are in ``self.colnames``. + If ``names`` is a string then it is interpreted as a single column + name. + """ + names = {names} if isinstance(names, str) else set(names) + invalid_names = names.difference(self.colnames) + if len(invalid_names) == 1: + raise KeyError(f'column "{invalid_names.pop()}" does not exist') + elif len(invalid_names) > 1: + raise KeyError(f"columns {invalid_names} do not exist") + return names def remove_column(self, name): """ @@ -948,59 +2905,193 @@ def remove_column(self, name): ---------- name : str Name of column to remove - """ + Examples + -------- + Create a table with three columns 'a', 'b' and 'c':: + + >>> t = Table([[1, 2, 3], [0.1, 0.2, 0.3], ['x', 'y', 'z']], + ... names=('a', 'b', 'c')) + >>> print(t) + a b c + --- --- --- + 1 0.1 x + 2 0.2 y + 3 0.3 z + + Remove column 'b' from the table:: + + >>> t.remove_column('b') + >>> print(t) + a c + --- --- + 1 x + 2 y + 3 z + + To remove several columns at the same time use remove_columns. + """ self.remove_columns([name]) def remove_columns(self, names): - ''' - Remove several columns from the table + """ + Remove several columns from the table. Parameters ---------- - names : list - A list containing the names of the columns to remove - ''' - - for name in names: - if name not in self.columns: - raise KeyError("Column {0} does not exist".format(name)) - - for name in names: - self.columns.pop(name) - - self._data = _drop_fields(self._data, names) + names : str or iterable of str + Names of the columns to remove + + Examples + -------- + Create a table with three columns 'a', 'b' and 'c':: + + >>> t = Table([[1, 2, 3], [0.1, 0.2, 0.3], ['x', 'y', 'z']], + ... names=('a', 'b', 'c')) + >>> print(t) + a b c + --- --- --- + 1 0.1 x + 2 0.2 y + 3 0.3 z + + Remove columns 'b' and 'c' from the table:: + + >>> t.remove_columns(['b', 'c']) + >>> print(t) + a + --- + 1 + 2 + 3 + + Specifying only a single column also works. Remove column 'b' from the table:: + + >>> t = Table([[1, 2, 3], [0.1, 0.2, 0.3], ['x', 'y', 'z']], + ... names=('a', 'b', 'c')) + >>> t.remove_columns('b') + >>> print(t) + a c + --- --- + 1 x + 2 y + 3 z + + This gives the same as using remove_column. + """ + for name in self._set_of_names_in_colnames(names): + del self.columns[name] - def keep_columns(self, names): - ''' - Keep only the columns specified (remove the others) + def _convert_string_dtype(self, in_kind, out_kind, encode_decode_func): + """ + Convert string-like columns to/from bytestring and unicode (internal only). Parameters ---------- - names : list - A list containing the names of the columns to keep. All other - columns will be removed. - ''' + in_kind : str + Input dtype.kind + out_kind : str + Output dtype.kind + """ + for col in self.itercols(): + if col.dtype.kind == in_kind: + try: + # This requires ASCII and is faster by a factor of up to ~8, so + # try that first. + newcol = col.__class__(col, dtype=out_kind) + except (UnicodeEncodeError, UnicodeDecodeError): + newcol = col.__class__(encode_decode_func(col, "utf-8")) + + # Quasi-manually copy info attributes. Unfortunately + # DataInfo.__set__ does not do the right thing in this case + # so newcol.info = col.info does not get the old info attributes. + for attr in ( + col.info.attr_names - col.info._attrs_no_copy - {"dtype"} + ): + value = deepcopy(getattr(col.info, attr)) + setattr(newcol.info, attr, value) + + self[col.name] = newcol + + def convert_bytestring_to_unicode(self): + """ + Convert bytestring columns (dtype.kind='S') to unicode (dtype.kind='U') + using UTF-8 encoding. - if isinstance(names, basestring): - names = [names] + Internally this changes string columns to represent each character + in the string with a 4-byte UCS-4 equivalent, so it is inefficient + for memory but allows scripts to manipulate string arrays with + natural syntax. + """ + self._convert_string_dtype("S", "U", np.strings.decode) - for name in names: - if name not in self.columns: - raise KeyError("Column {0} does not exist".format(name)) + def convert_unicode_to_bytestring(self): + """ + Convert unicode columns (dtype.kind='U') to bytestring (dtype.kind='S') + using UTF-8 encoding. + + When exporting a unicode string array to a file, it may be desirable + to encode unicode columns as bytestrings. + """ + self._convert_string_dtype("U", "S", np.strings.encode) - remove = list(set(self.keys()) - set(names)) + def keep_columns(self, names): + """ + Keep only the columns specified (remove the others). - self.remove_columns(remove) + Parameters + ---------- + names : str or iterable of str + The columns to keep. All other columns will be removed. + + Examples + -------- + Create a table with three columns 'a', 'b' and 'c':: + + >>> t = Table([[1, 2, 3],[0.1, 0.2, 0.3],['x', 'y', 'z']], + ... names=('a', 'b', 'c')) + >>> print(t) + a b c + --- --- --- + 1 0.1 x + 2 0.2 y + 3 0.3 z + + Keep only column 'a' of the table:: + + >>> t.keep_columns('a') + >>> print(t) + a + --- + 1 + 2 + 3 + + Keep columns 'a' and 'c' of the table:: + + >>> t = Table([[1, 2, 3],[0.1, 0.2, 0.3],['x', 'y', 'z']], + ... names=('a', 'b', 'c')) + >>> t.keep_columns(['a', 'c']) + >>> print(t) + a c + --- --- + 1 x + 2 y + 3 z + """ + names = self._set_of_names_in_colnames(names) + for colname in self.colnames: + if colname not in names: + del self.columns[colname] def rename_column(self, name, new_name): - ''' + """ Rename a column. - This can also be done directly with by setting the ``name`` attribute - for a column:: + This can also be done directly by setting the ``name`` attribute + of the ``info`` property of the column:: - table[name].name = new_name + table[name].info.name = new_name Parameters ---------- @@ -1008,14 +3099,106 @@ def rename_column(self, name, new_name): The current name of the column. new_name : str The new name for the column - ''' + Examples + -------- + Create a table with three columns 'a', 'b' and 'c':: + + >>> t = Table([[1,2],[3,4],[5,6]], names=('a','b','c')) + >>> print(t) + a b c + --- --- --- + 1 3 5 + 2 4 6 + + Renaming column 'a' to 'aa':: + + >>> t.rename_column('a' , 'aa') + >>> print(t) + aa b c + --- --- --- + 1 3 5 + 2 4 6 + """ if name not in self.keys(): - raise KeyError("Column {0} does not exist".format(name)) + raise KeyError(f"Column {name} does not exist") + + self.columns[name].info.name = new_name - self.columns[name].name = new_name + def rename_columns(self, names, new_names): + """ + Rename multiple columns. - def add_row(self, vals=None): + Parameters + ---------- + names : list, tuple + A list or tuple of existing column names. + new_names : list, tuple + A list or tuple of new column names. + + Examples + -------- + Create a table with three columns 'a', 'b', 'c':: + + >>> t = Table([[1,2],[3,4],[5,6]], names=('a','b','c')) + >>> print(t) + a b c + --- --- --- + 1 3 5 + 2 4 6 + + Renaming columns 'a' to 'aa' and 'b' to 'bb':: + + >>> names = ('a','b') + >>> new_names = ('aa','bb') + >>> t.rename_columns(names, new_names) + >>> print(t) + aa bb c + --- --- --- + 1 3 5 + 2 4 6 + """ + if not self._is_list_or_tuple_of_str(names): + raise TypeError("input 'names' must be a tuple or a list of column names") + + if not self._is_list_or_tuple_of_str(new_names): + raise TypeError( + "input 'new_names' must be a tuple or a list of column names" + ) + + if len(names) != len(new_names): + raise ValueError( + "input 'names' and 'new_names' list arguments must be the same length" + ) + + for name, new_name in zip(names, new_names): + self.rename_column(name, new_name) + + def _set_row(self, idx, colnames, vals): + try: + if not len(vals) == len(colnames): + raise Exception + except Exception: + raise ValueError( + "right hand side must be a sequence of values with " + "the same length as the number of selected columns" + ) + + # Keep track of original values before setting each column so that + # setting row can be transactional. + orig_vals = [] + cols = self.columns + try: + for name, val in zip(colnames, vals): + orig_vals.append(cols[name][idx]) + cols[name][idx] = val + except Exception: + # If anything went wrong first revert the row update then raise + for name, val in zip(colnames, orig_vals[:-1]): + cols[name][idx] = val + raise + + def add_row(self, vals=None, mask=None): """Add a new row to the end of the table. The ``vals`` argument can be: @@ -1025,118 +3208,1212 @@ def add_row(self, vals=None): mapping (e.g. dict) Keys corresponding to column names. Missing values will be filled with np.zeros for the column dtype. - None + `None` All values filled with np.zeros for the column dtype. This method requires that the Table object "owns" the underlying array data. In particular one cannot add a row to a Table that was initialized with copy=False from an existing array. + The ``mask`` attribute should give (if desired) the mask for the + values. The type of the mask should match that of the values, i.e. if + ``vals`` is an iterable, then ``mask`` should also be an iterable + with the same length, and if ``vals`` is a mapping, then ``mask`` + should be a dictionary. + Parameters ---------- vals : tuple, list, dict or None Use the specified values in the new row + mask : tuple, list, dict or None + Use the specified mask values in the new row + + Examples + -------- + Create a table with three columns 'a', 'b' and 'c':: + + >>> t = Table([[1,2],[4,5],[7,8]], names=('a','b','c')) + >>> print(t) + a b c + --- --- --- + 1 4 7 + 2 5 8 + + Adding a new row with entries '3' in 'a', '6' in 'b' and '9' in 'c':: + + >>> t.add_row([3,6,9]) + >>> print(t) + a b c + --- --- --- + 1 4 7 + 2 5 8 + 3 6 9 """ - newlen = len(self._data) + 1 - self._data.resize((newlen,), refcheck=False) + self.insert_row(len(self), vals, mask) - if isinstance(vals, collections.Mapping): - row = self._data[-1] - for name, val in vals.items(): - try: - row[name] = val - except IndexError: - raise ValueError("No column {0} in table".format(name)) + def insert_row(self, index, vals=None, mask=None): + """Add a new row before the given ``index`` position in the table. + + The ``vals`` argument can be: + + sequence (e.g. tuple or list) + Column values in the same order as table columns. + mapping (e.g. dict) + Keys corresponding to column names. Missing values will be + filled with np.zeros for the column dtype. + `None` + All values filled with np.zeros for the column dtype. + + The ``mask`` attribute should give (if desired) the mask for the + values. The type of the mask should match that of the values, i.e. if + ``vals`` is an iterable, then ``mask`` should also be an iterable + with the same length, and if ``vals`` is a mapping, then ``mask`` + should be a dictionary. + + Parameters + ---------- + vals : tuple, list, dict or None + Use the specified values in the new row + mask : tuple, list, dict or None + Use the specified mask values in the new row + """ + colnames = self.colnames + + N = len(self) + if index < -N or index > N: + raise IndexError( + f"Index {index} is out of bounds for table with length {N}" + ) + if index < 0: + index += N + + if isinstance(vals, Mapping) or vals is None: + # From the vals and/or mask mappings create the corresponding lists + # that have entries for each table column. + if mask is not None and not isinstance(mask, Mapping): + raise TypeError("Mismatch between type of vals and mask") + + # Now check that the mask is specified for the same keys as the + # values, otherwise things get really confusing. + if mask is not None and set(vals.keys()) != set(mask.keys()): + raise ValueError("keys in mask should match keys in vals") + + if vals and any(name not in colnames for name in vals): + raise ValueError("Keys in vals must all be valid column names") + + vals_list = [] + mask_list = [] + + for name in colnames: + if vals and name in vals: + vals_list.append(vals[name]) + mask_list.append(False if mask is None else mask[name]) + else: + col = self[name] + if hasattr(col, "dtype"): + # Make a placeholder zero element of the right type which is masked. + # This assumes the appropriate insert() method will broadcast a + # numpy scalar to the right shape. + vals_list.append(np.zeros(shape=(), dtype=col.dtype)) + + # For masked table any unsupplied values are masked by default. + mask_list.append(self.masked and vals is not None) + else: + raise ValueError(f"Value must be supplied for column '{name}'") + + vals = vals_list + mask = mask_list + + if np.iterable(vals): + if mask is not None and ( + not np.iterable(mask) or isinstance(mask, Mapping) + ): + raise TypeError("Mismatch between type of vals and mask") - elif isiterable(vals): if len(self.columns) != len(vals): - raise ValueError('Mismatch between number of vals and columns') - if not isinstance(vals, tuple): - vals = tuple(vals) - self._data[-1] = vals - - elif vals is not None: - raise TypeError('Vals must be an iterable or mapping or None') - - # Add_row() probably corrupted the Column views of self._data. Rebuild - # self.columns. Col.copy() takes an optional data reference that it - # uses in the copy. - cols = [c.copy(self._data[c.name]) for c in self.columns.values()] - self.columns = TableColumns(cols) - - def sort(self, keys): - ''' + raise ValueError("Mismatch between number of vals and columns") + + if mask is not None: + if len(self.columns) != len(mask): + raise ValueError("Mismatch between number of masks and columns") + else: + mask = [False] * len(self.columns) + + else: + raise TypeError("Vals must be an iterable or mapping or None") + + if N == 0 and any( + isinstance(column, BaseColumn) and isinstance(v, Quantity) + for column, v in zip(self.columns.values(), vals) + ): + msg = "Units from inserted quantities will be ignored." + + if isinstance(self, QTable): + suggested_units = [] + for column, v in zip(self.columns.values(), vals): + u = column.unit or getattr(v, "unit", None) + suggested_units.append(str(u) if u is not None else None) + del u + + msg += ( + "\nIf you were hoping to fill a QTable row by row, " + "also initialize the units before starting, for instance\n" + f"QTable(names={self.colnames}, units={suggested_units})" + ) + del suggested_units + + warnings.warn(msg, category=UserWarning, stacklevel=2) + del msg + + # Insert val at index for each column + columns = self.TableColumns() + for name, col, val, mask_ in zip(colnames, self.columns.values(), vals, mask): + try: + # If new val is masked and the existing column does not support masking + # then upgrade the column to a mask-enabled type: either the table-level + # default ColumnClass or else MaskedColumn. + if ( + mask_ + and isinstance(col, Column) + and not isinstance(col, MaskedColumn) + ): + col_cls = ( + self.ColumnClass + if issubclass(self.ColumnClass, self.MaskedColumn) + else self.MaskedColumn + ) + col = col_cls(col, copy=False) + + newcol = col.insert(index, val, axis=0) + + if len(newcol) != N + 1: + raise ValueError( + f"Incorrect length for column {name} after inserting {val}" + f" (expected {len(newcol)}, got {N + 1})" + ) + newcol.info.parent_table = self + + # Set mask if needed and possible + if mask_: + if hasattr(newcol, "mask"): + newcol[index] = np.ma.masked + else: + raise TypeError( + f"mask was supplied for column '{col.info.name}' " + "but it does not support masked values" + ) + + columns[name] = newcol + + except Exception as err: + raise ValueError( + f"Unable to insert row because of exception in column '{name}':\n{err}" + ) from err + + for table_index in self.indices: + table_index.insert_row(index, vals, self.columns.values()) + + self._replace_cols(columns) + + # Revert groups to default (ungrouped) state + if hasattr(self, "_groups"): + del self._groups + + def _replace_cols(self, columns): + for col, new_col in zip(self.columns.values(), columns.values()): + new_col.info.indices = [] + for index in col.info.indices: + index.columns[index.col_position(col.info.name)] = new_col + new_col.info.indices.append(index) + + self.columns = columns + + def setdefault(self, name, default): + """Ensure a column named ``name`` exists. + + If ``name`` is already present then ``default`` is ignored. + Otherwise ``default`` can be any data object which is acceptable as + a `~astropy.table.Table` column object or can be converted. This + includes mixin columns and scalar or length=1 objects which get + broadcast to match the table length. + + Parameters + ---------- + name : str + Name of the column. + default : object + Data object for the new column. + + Returns + ------- + `~astropy.table.Column`, `~astropy.table.MaskedColumn` or mixin-column type + The column named ``name`` if it is present already, or the + validated ``default`` converted to a column otherwise. + + Raises + ------ + TypeError + If the table is empty and ``default`` is a scalar object. + + Examples + -------- + Start with a simple table:: + + >>> t0 = Table({"a": ["Ham", "Spam"]}) + >>> t0 +
+ a + str4 + ---- + Ham + Spam + + Trying to add a column that already exists does not modify it:: + + >>> t0.setdefault("a", ["Breakfast"]) + + Ham + Spam + >>> t0 +
+ a + str4 + ---- + Ham + Spam + + But if the column does not exist it will be created with the + default value:: + + >>> t0.setdefault("approved", False) + + False + False + >>> t0 +
+ a approved + str4 bool + ---- -------- + Ham False + Spam False + """ + if name not in self.columns: + self[name] = default + return self[name] + + def update(self, other, copy=True): + """ + Perform a dictionary-style update and merge metadata. + + The argument ``other`` must be a |Table|, or something that can be used + to initialize a table. Columns from (possibly converted) ``other`` are + added to this table. In case of matching column names the column from + this table is replaced with the one from ``other``. If ``other`` is a + |Table| instance then ``|=`` is available as alternate syntax for in-place + update and ``|`` can be used merge data to a new table. + + Parameters + ---------- + other : table-like + Data to update this table with. + copy : bool + Whether the updated columns should be copies of or references to + the originals. + + See Also + -------- + add_columns, astropy.table.hstack, replace_column + + Examples + -------- + Update a table with another table:: + + >>> t1 = Table({'a': ['foo', 'bar'], 'b': [0., 0.]}, meta={'i': 0}) + >>> t2 = Table({'b': [1., 2.], 'c': [7., 11.]}, meta={'n': 2}) + >>> t1.update(t2) + >>> t1 +
+ a b c + str3 float64 float64 + ---- ------- ------- + foo 1.0 7.0 + bar 2.0 11.0 + >>> t1.meta + {'i': 0, 'n': 2} + + Update a table with a dictionary:: + + >>> t = Table({'a': ['foo', 'bar'], 'b': [0., 0.]}) + >>> t.update({'b': [1., 2.]}) + >>> t +
+ a b + str3 float64 + ---- ------- + foo 1.0 + bar 2.0 + """ + from .operations import _merge_table_meta + + if not isinstance(other, Table): + other = self.__class__(other, copy=copy) + common_cols = set(self.colnames).intersection(other.colnames) + for name, col in other.items(): + if name in common_cols: + self.replace_column(name, col, copy=copy) + else: + self.add_column(col, name=name, copy=copy) + _merge_table_meta(self, [self, other], metadata_conflicts="silent") + + def argsort(self, keys=None, kind=None, reverse=False): + """ + Return the indices which would sort the table according to one or + more key columns. This simply calls the `numpy.argsort` function on + the table with the ``order`` parameter set to ``keys``. + + Parameters + ---------- + keys : str or list of str + The column name(s) to order the table by + kind : {'quicksort', 'mergesort', 'heapsort', 'stable'}, optional + Sorting algorithm used by ``numpy.argsort``. + reverse : bool + Sort in reverse order (default=False) + + Returns + ------- + index_array : ndarray, int + Array of indices that sorts the table by the specified key + column(s). + """ + if isinstance(keys, str): + keys = [keys] + + # use index sorted order if possible + if keys is not None: + index = get_index(self, names=keys) + if index is not None: + idx = np.asarray(index.sorted_data()) + return idx[::-1] if reverse else idx + + kwargs = {} + if keys: + # For multiple keys return a structured array which gets sorted, + # while for a single key return a single ndarray. Sorting a + # one-column structured array is slower than ndarray (e.g. a + # factor of ~6 for a 10 million long random array), and much slower + # for in principle sortable columns like Time, which get stored as + # object arrays. + if len(keys) > 1: + kwargs["order"] = keys + data = self.as_array(names=keys) + else: + data = self[keys[0]] + else: + # No keys provided so sort on all columns. + data = self.as_array() + + if kind: + kwargs["kind"] = kind + + # np.argsort will look for a possible .argsort method (e.g., for Time), + # and if that fails cast to an array and try sorting that way. + idx = np.argsort(data, **kwargs) + + return idx[::-1] if reverse else idx + + def sort(self, keys=None, *, kind=None, reverse=False): + """ Sort the table according to one or more keys. This operates on the existing table and does not return a new table. Parameters ---------- keys : str or list of str - The key(s) to order the table by - ''' - if type(keys) is not list: + The key(s) to order the table by. If None, use the + primary index of the Table. + kind : {'quicksort', 'mergesort', 'heapsort', 'stable'}, optional + Sorting algorithm used by ``numpy.argsort``. + reverse : bool + Sort in reverse order (default=False) + + Examples + -------- + Create a table with 3 columns:: + + >>> t = Table([['Max', 'Jo', 'John'], ['Miller', 'Miller', 'Jackson'], + ... [12, 15, 18]], names=('firstname', 'name', 'tel')) + >>> print(t) + firstname name tel + --------- ------- --- + Max Miller 12 + Jo Miller 15 + John Jackson 18 + + Sorting according to standard sorting rules, first 'name' then 'firstname':: + + >>> t.sort(['name', 'firstname']) + >>> print(t) + firstname name tel + --------- ------- --- + John Jackson 18 + Jo Miller 15 + Max Miller 12 + + Sorting according to standard sorting rules, first 'firstname' then 'tel', + in reverse order:: + + >>> t.sort(['firstname', 'tel'], reverse=True) + >>> print(t) + firstname name tel + --------- ------- --- + Max Miller 12 + John Jackson 18 + Jo Miller 15 + """ + if keys is None: + if not self.indices: + raise ValueError("Table sort requires input keys or a table index") + keys = [x.info.name for x in self.indices[0].columns] + + if isinstance(keys, str): keys = [keys] - self._data.sort(order=keys) + + indexes = self.argsort(keys, kind=kind, reverse=reverse) + + with self.index_mode("freeze"): + for col in self.columns.values(): + # Make a new sorted column. This requires that take() also copies + # relevant info attributes for mixin columns. + new_col = col.take(indexes, axis=0) + + # First statement in try: will succeed if the column supports an in-place + # update, and matches the legacy behavior of astropy Table. However, + # some mixin classes may not support this, so in that case just drop + # in the entire new column. See #9553 and #9536 for discussion. + try: + col[:] = new_col + except Exception: + # In-place update failed for some reason, exception class not + # predictable for arbitrary mixin. + self[col.info.name] = new_col def reverse(self): - ''' + """ Reverse the row order of table rows. The table is reversed in place and there are no function arguments. - ''' - self._data[:] = self._data[::-1].copy() - @classmethod - def read(cls, *args, **kwargs): - ''' - Read a table + Examples + -------- + Create a table with three columns:: + + >>> t = Table([['Max', 'Jo', 'John'], ['Miller','Miller','Jackson'], + ... [12,15,18]], names=('firstname','name','tel')) + >>> print(t) + firstname name tel + --------- ------- --- + Max Miller 12 + Jo Miller 15 + John Jackson 18 + + Reversing order:: + + >>> t.reverse() + >>> print(t) + firstname name tel + --------- ------- --- + John Jackson 18 + Jo Miller 15 + Max Miller 12 + """ + for col in self.columns.values(): + # First statement in try: will succeed if the column supports an in-place + # update, and matches the legacy behavior of astropy Table. However, + # some mixin classes may not support this, so in that case just drop + # in the entire new column. See #9836, #9553, and #9536 for discussion. + new_col = col[::-1] + try: + col[:] = new_col + except Exception: + # In-place update failed for some reason, exception class not + # predictable for arbitrary mixin. + self[col.info.name] = new_col + + for index in self.indices: + index.reverse() + + def round(self, decimals=0): + """ + Round numeric columns in-place to the specified number of decimals. + Non-numeric columns will be ignored. + + Examples + -------- + Create three columns with different types: + + >>> t = Table([[1, 4, 5], [-25.55, 12.123, 85], + ... ['a', 'b', 'c']], names=('a', 'b', 'c')) + >>> print(t) + a b c + --- ------ --- + 1 -25.55 a + 4 12.123 b + 5 85.0 c + + Round them all to 0: + + >>> t.round(0) + >>> print(t) + a b c + --- ----- --- + 1 -26.0 a + 4 12.0 b + 5 85.0 c + + Round column 'a' to -1 decimal: + + >>> t.round({'a':-1}) + >>> print(t) + a b c + --- ----- --- + 0 -26.0 a + 0 12.0 b + 0 85.0 c - The arguments passed to this method depend on the format - ''' + Parameters + ---------- + decimals: int, dict + Number of decimals to round the columns to. If a dict is given, + the columns will be rounded to the number specified as the value. + If a certain column is not in the dict given, it will remain the + same. + """ + if isinstance(decimals, Mapping): + decimal_values = decimals.values() + column_names = decimals.keys() + elif isinstance(decimals, int): + decimal_values = itertools.repeat(decimals) + column_names = self.colnames + else: + raise ValueError("'decimals' argument must be an int or a dict") + + for colname, decimal in zip(column_names, decimal_values): + col = self.columns[colname] + if np.issubdtype(col.info.dtype, np.number): + try: + np.around(col, decimals=decimal, out=col) + except TypeError: + # Bug in numpy see https://github.com/numpy/numpy/issues/15438 + col[()] = np.around(col, decimals=decimal) + + def copy(self, copy_data=True): + """ + Return a copy of the table. + + Parameters + ---------- + copy_data : bool + If `True` (the default), copy the underlying data array and make + a deep copy of the ``meta`` attribute. Otherwise, use the same + data array and make a shallow (key-only) copy of ``meta``. + """ + out = self.__class__(self, copy=copy_data) + + # If the current table is grouped then do the same in the copy + if hasattr(self, "_groups"): + out._groups = groups.TableGroups( + out, indices=self._groups._indices, keys=self._groups._keys + ) + return out + + def __deepcopy__(self, memo=None): + out = self.copy(False) + for name in out.colnames: + out.columns.__setitem__(name, deepcopy(self[name]), validated=True) + out.meta = deepcopy(self.meta) + return out - if 'format' in kwargs: - format = kwargs.pop('format') + def __copy__(self): + return self.copy(False) + + def __eq__(self, other): + return self._rows_equal(other) + + def __ne__(self, other): + eq = self.__eq__(other) + if isinstance(eq, bool): + # bitwise operators on bool values not reliable (e.g. `bool(~True) == True`) + # and are deprecated in Python 3.12 + # see https://github.com/python/cpython/pull/103487 + return not eq else: - format = None + return ~eq - if format is None: + def _rows_equal(self, other): + """ + Row-wise comparison of table with any other object. - valid_formats = identify_format('read', args, kwargs) + This is actual implementation for __eq__. - if len(valid_formats) == 0: - raise Exception("Format could not be identified") - elif len(valid_formats) > 1: - raise Exception("Format is ambiguous - options are: {0:s}".format(', '.join(valid_formats))) - else: - format = valid_formats[0] + Returns a 1-D boolean numpy array showing result of row-wise comparison, + or a bool (False) in cases where comparison isn't possible (uncomparable dtypes + or unbroadcastable shapes). Intended to follow legacy numpy's elementwise + comparison rules. - reader = get_reader(format) - table = reader(*args, **kwargs) - if not isinstance(table, cls): - raise TypeError("reader should return a {0:s} instance".format(cls.__name__)) - return table + This is the same as the ``==`` comparison for tables. + + Parameters + ---------- + other : Table or DataFrame or ndarray + An object to compare with table - def write(self, *args, **kwargs): - ''' - Write a table + Examples + -------- + Comparing one Table with other:: - The arguments passed to this method depend on the format - ''' + >>> t1 = Table([[1,2],[4,5],[7,8]], names=('a','b','c')) + >>> t2 = Table([[1,2],[4,5],[7,8]], names=('a','b','c')) + >>> t1._rows_equal(t2) + array([ True, True]) - if 'format' in kwargs: - format = kwargs.pop('format') + """ + if isinstance(other, Table): + other = other.as_array() + + self_is_masked = self.has_masked_columns + other_is_masked = isinstance(other, np.ma.MaskedArray) + + allowed_numpy_exceptions = (TypeError, ValueError) + # One table is masked and the other is not + if self_is_masked ^ other_is_masked: + # remap variables to a and b where a is masked and b isn't + a, b = ( + (self.as_array(), other) if self_is_masked else (other, self.as_array()) + ) + + # If mask is True, then by definition the row doesn't match + # because the other array is not masked. + false_mask = np.zeros(1, dtype=[(n, bool) for n in a.dtype.names]) + try: + result = (a.data == b) & (a.mask == false_mask) + except allowed_numpy_exceptions: + # numpy may complain that structured array are not comparable (TypeError) + # or that operands are not brodcastable (ValueError) + # see https://github.com/astropy/astropy/issues/13421 + result = False else: - format = None + try: + result = self.as_array() == other + except allowed_numpy_exceptions: + result = False + + return result - if format is None: + def values_equal(self, other): + """ + Element-wise comparison of table with another table, list, or scalar. + + Returns a ``Table`` with the same columns containing boolean values + showing result of comparison. - valid_formats = identify_format('write', args, kwargs) + Parameters + ---------- + other : table-like object or list or scalar + Object to compare with table + + Examples + -------- + Compare one Table with other:: + + >>> t1 = Table([[1, 2], [4, 5], [-7, 8]], names=('a', 'b', 'c')) + >>> t2 = Table([[1, 2], [-4, 5], [7, 8]], names=('a', 'b', 'c')) + >>> t1.values_equal(t2) +
+ a b c + bool bool bool + ---- ----- ----- + True False False + True True True - if len(valid_formats) == 0: - raise Exception("Format could not be identified") - elif len(valid_formats) > 1: - raise Exception("Format is ambiguous - options are: {0:s}".format(', '.join(valid_formats))) + """ + if isinstance(other, Table): + names = other.colnames + else: + try: + other = Table(other, copy=False) + names = other.colnames + except Exception: + # Broadcast other into a dict, so e.g. other = 2 will turn into + # other = {'a': 2, 'b': 2} and then equality does a + # column-by-column broadcasting. + names = self.colnames + other = dict.fromkeys(names, other) + + # Require column names match but do not require same column order + if set(self.colnames) != set(names): + raise ValueError("cannot compare tables with different column names") + + eqs = [] + for name in names: + try: + np.broadcast(self[name], other[name]) # Check if broadcast-able + # Catch the numpy FutureWarning related to equality checking, + # "elementwise comparison failed; returning scalar instead, but + # in the future will perform elementwise comparison". Turn this + # into an exception since the scalar answer is not what we want. + with warnings.catch_warnings(record=True) as warns: + warnings.simplefilter("always") + eq = self[name] == other[name] + if ( + warns + and issubclass(warns[-1].category, FutureWarning) + and "elementwise comparison failed" in str(warns[-1].message) + ): + raise FutureWarning(warns[-1].message) + except Exception as err: + raise ValueError(f"unable to compare column {name}") from err + + # Be strict about the result from the comparison. E.g. SkyCoord __eq__ is just + # broken and completely ignores that it should return an array. + if not ( + isinstance(eq, np.ndarray) + and eq.dtype is np.dtype("bool") + and len(eq) == len(self) + ): + raise TypeError( + f"comparison for column {name} returned {eq} " + "instead of the expected boolean ndarray" + ) + + eqs.append(eq) + + out = Table(eqs, names=names) + + return out + + @property + def groups(self): + if not hasattr(self, "_groups"): + self._groups = groups.TableGroups(self) + return self._groups + + def group_by(self, keys): + """ + Group this table by the specified ``keys``. + + This effectively splits the table into groups which correspond to unique + values of the ``keys`` grouping object. The output is a new + `~astropy.table.TableGroups` which contains a copy of this table but + sorted by row according to ``keys``. + + The ``keys`` input to `group_by` can be specified in different ways: + + - String or list of strings corresponding to table column name(s) + - Numpy array (homogeneous or structured) with same length as this table + - `~astropy.table.Table` with same length as this table + + Parameters + ---------- + keys : str, list of str, numpy array, or `~astropy.table.Table` + Key grouping object + + Returns + ------- + out : `~astropy.table.Table` + New table with groups set + + Notes + ----- + The underlying sorting algorithm is guaranteed stable, meaning that the + original table order is preserved within each group. + """ + return groups.table_group_by(self, keys) + + def to_df( + self, + backend: str, + /, + *, + index: bool | str | None = None, + use_nullable_int: bool = True, + ) -> Any: # Returns a Dataframe of type depending on backend + """ + Convert the table to an eager DataFrame using the ``narwhals`` backend. + + This method supports converting Astropy Table instances into a variety of + DataFrame formats via the ``narwhals`` library. The output can be any supported eager + DataFrame type, such as `pandas`, ``polars``, ``pyarrow``, or others, depending on the + specified backend. + + Mixin columns such as ``Quantity``, ``Time``, ``TimeDelta``, or ``SkyCoord`` are automatically + converted to plain Column or MaskedColumn types when necessary. Time-related mixins + will be represented using ``np.datetime64`` or ``np.timedelta64`` as appropriate. All + other mixins are serialized into a flat column structure using standard representations. + + Parameters + ---------- + backend : str + The backend to use for conversion. This should be a string + such as "pandas", "polars", or "pyarrow". + + index : None, bool, str, optional + Specifies the index column in the resulting DataFrame. + + - If None (default), use the table’s primary index if it exists and is a single column. + - If False, no index is set on the DataFrame. + - If a string, use the column with that name as the index. + + use_nullable_int : bool, optional + If True (default), masked integer columns are converted to the backend's nullable integer type. + If False, an error is raised if a masked integer column is encountered. + + Returns + ------- + dataframe : Any + An eager DataFrame instance as specified by the backend. + + Raises + ------ + ValueError + If the backend is not compatible with eager DataFrame conversion, or if the index argument is invalid. + ImportError + If the narwhals library is not installed. + + Examples + -------- + Convert a QTable with mixin columns to a pandas DataFrame: + + >>> from astropy.table import QTable + >>> import astropy.units as u + >>> from astropy.time import Time, TimeDelta + >>> from astropy.coordinates import SkyCoord + + >>> q = [1, 2] * u.m + >>> tm = Time([1998, 2002], format='jyear') + >>> sc = SkyCoord([5, 6], [7, 8], unit='deg') + >>> dt = TimeDelta([3, 200] * u.s) + + >>> t = QTable([q, tm, sc, dt], names=['q', 'tm', 'sc', 'dt']) + >>> df = t.to_df('pandas', index='tm') + >>> print(df) + q sc.ra sc.dec dt + tm + 1998-01-01 1.0 5.0 7.0 0 days 00:00:03 + 2002-01-01 2.0 6.0 8.0 0 days 00:03:20 + + """ + from ._dataframes import to_df + + return to_df( + self, backend=backend, index=index, use_nullable_int=use_nullable_int + ) + + @classmethod + def from_df( + cls, df: Any, /, *, index: bool = False, units: Mapping[str, Any] | None = None + ) -> "Table": + """ + Create a `~astropy.table.Table` from any ``narwhals``-compatible dataframe + (e.g., `pandas`, ``polars``, ``pyarrow``, etc). + + Parameters + ---------- + df : Any + A dataframe-like object (e.g., a `pandas.DataFrame`, ``polars.DataFrame``, + ``pyarrow.Table`` or other ``narwhals`` compatible dataframe). + index : bool, optional + Whether to include the index (if applicable, like in pandas) (default=False). + units : dict, optional + A dict mapping column names to a `~astropy.units.Unit`. + The columns will have the specified unit in the Table. + + Returns + ------- + table : astropy.table.Table + + Raises + ------ + ImportError + If the narwhals library is not installed. + + Examples + -------- + Here we convert a :class:`pandas.DataFrame` instance + to a `~astropy.table.QTable`. + + >>> import numpy as np + >>> import pandas as pd + >>> from astropy.table import QTable + + >>> time = pd.Series(['1998-01-01', '2002-01-01'], dtype='datetime64[ns]') + >>> dt = pd.Series(np.array([1, 300], dtype='timedelta64[s]')) + >>> df = pd.DataFrame({'time': time}) + >>> df['dt'] = dt + >>> df['x'] = [3., 4.] + >>> with pd.option_context('display.max_columns', 20): + ... print(df) + time dt x + 0 1998-01-01 0 days 00:00:01 3.0 + 1 2002-01-01 0 days 00:05:00 4.0 + + >>> QTable.from_pandas(df) + + time dt x + Time TimeDelta float64 + ----------------------- --------- ------- + 1998-01-01T00:00:00.000 1.0 3.0 + 2002-01-01T00:00:00.000 300.0 4.0 + + Here we convert a ``polars.DataFrame`` instance to a `~astropy.table.QTable`. + + >>> import polars as pl + >>> from astropy.table import QTable + >>> df_polars = pl.DataFrame({ + ... 'time': [1998, 2002], + ... 'values': [1.0, 2.0] + ... }) + >>> QTable.from_df(df_polars, units={'values': 'm'}) + + time values + m + int64 float64 + ----- ------- + 1998 1.0 + 2002 2.0 + + """ + from ._dataframes import from_df + + return from_df(cls, df, index=index, units=units) + + def to_pandas( + self, index: bool | str | None = None, use_nullable_int: bool = True + ) -> Any: # Returns pandas.DataFrame but pandas may not be installed + """ + Return a :class:`pandas.DataFrame` instance. + + The index of the created DataFrame is controlled by the ``index`` + argument. For ``index=True`` or the default ``None``, an index will be + specified for the DataFrame if there is a primary key index on the + Table *and* if it corresponds to a single column. If ``index=False`` + then no DataFrame index will be specified. If ``index`` is the name of + a column in the table then that will be the DataFrame index. + + In addition to vanilla columns or masked columns, this supports Table + mixin columns like Quantity, Time, or SkyCoord. In many cases these + objects have no analog in pandas and will be converted to a "encoded" + representation using only Column or MaskedColumn. The exception is + Time or TimeDelta columns, which will be converted to the corresponding + representation in pandas using ``np.datetime64`` or ``np.timedelta64``. + See the example below. + + Parameters + ---------- + index : None, bool, str, optional + Specify DataFrame index mode. If ``None`` (default), use the + table's primary index if it exists and is a single column. + If ``False``, no index is set. If ``True``, use the primary + index (requires single-column primary key). If a string, + use the column with that name as the index. + use_nullable_int : bool, optional + If True (default), masked integer columns are converted to the pandas + nullable integer type. If False, an error is raised if a masked + integer column is encountered. + + Returns + ------- + dataframe : :class:`pandas.DataFrame` + A pandas :class:`pandas.DataFrame` instance + + Raises + ------ + ImportError + If pandas is not installed + ValueError + If the Table has multi-dimensional columns or if index argument is invalid + + Examples + -------- + Here we convert a table with a few mixins to a + :class:`pandas.DataFrame` instance. + + >>> import pandas as pd + >>> from astropy.table import QTable + >>> import astropy.units as u + >>> from astropy.time import Time, TimeDelta + >>> from astropy.coordinates import SkyCoord + + >>> q = [1, 2] * u.m + >>> tm = Time([1998, 2002], format='jyear') + >>> sc = SkyCoord([5, 6], [7, 8], unit='deg') + >>> dt = TimeDelta([3, 200] * u.s) + + >>> t = QTable([q, tm, sc, dt], names=['q', 'tm', 'sc', 'dt']) + + >>> df = t.to_pandas(index='tm') + >>> with pd.option_context('display.max_columns', 20): + ... print(df) + q sc.ra sc.dec dt + tm + 1998-01-01 1.0 5.0 7.0 0 days 00:00:03 + 2002-01-01 2.0 6.0 8.0 0 days 00:03:20 + + """ + from ._dataframes import to_pandas + + return to_pandas(self, index=index, use_nullable_int=use_nullable_int) + + @classmethod + def from_pandas( + cls, dataframe: Any, index: bool = False, units: Mapping[str, Any] | None = None + ) -> "Table": + """ + Create a `~astropy.table.Table` from a :class:`pandas.DataFrame` instance. + + In addition to converting generic numeric or string columns, this supports + conversion of pandas Date and Time delta columns to `~astropy.time.Time` + and `~astropy.time.TimeDelta` columns, respectively. + + Parameters + ---------- + dataframe : :class:`pandas.DataFrame` + A pandas :class:`pandas.DataFrame` instance + index : bool, optional + Include the index column in the returned table (default=False) + units : dict, optional + A dict mapping column names to a `~astropy.units.Unit`. + The columns will have the specified unit in the Table. + + Returns + ------- + table : `~astropy.table.Table` + A `~astropy.table.Table` (or subclass) instance + + Raises + ------ + ImportError + If pandas is not installed + + Examples + -------- + Here we convert a :class:`pandas.DataFrame` instance + to a `~astropy.table.QTable`. + + >>> import numpy as np + >>> import pandas as pd + >>> from astropy.table import QTable + + >>> time = pd.Series(['1998-01-01', '2002-01-01'], dtype='datetime64[ns]') + >>> dt = pd.Series(np.array([1, 300], dtype='timedelta64[s]')) + >>> df = pd.DataFrame({'time': time}) + >>> df['dt'] = dt + >>> df['x'] = [3., 4.] + >>> with pd.option_context('display.max_columns', 20): + ... print(df) + time dt x + 0 1998-01-01 0 days 00:00:01 3.0 + 1 2002-01-01 0 days 00:05:00 4.0 + + >>> QTable.from_pandas(df) + + time dt x + Time TimeDelta float64 + ----------------------- --------- ------- + 1998-01-01T00:00:00.000 1.0 3.0 + 2002-01-01T00:00:00.000 300.0 4.0 + + """ + from ._dataframes import from_pandas + + return from_pandas(cls, dataframe, index=index, units=units) + + info = TableInfo() + + +class QTable(Table): + """A class to represent tables of heterogeneous data. + + `~astropy.table.QTable` provides a class for heterogeneous tabular data + which can be easily modified, for instance adding columns or new rows. + + The `~astropy.table.QTable` class is identical to `~astropy.table.Table` + except that columns with an associated ``unit`` attribute are converted to + `~astropy.units.Quantity` objects. + + For more information see: + + - https://docs.astropy.org/en/stable/table/ + - https://docs.astropy.org/en/stable/table/mixin_columns.html + + Parameters + ---------- + data : numpy ndarray, dict, list, table-like object, optional + Data to initialize table. + masked : bool, optional + Specify whether the table is masked. + names : list, optional + Specify column names. + dtype : list, optional + Specify column data types. + meta : dict, optional + Metadata associated with the table. + copy : bool, optional + Copy the input data. If the input is a (Q)Table the ``meta`` is always + copied regardless of the ``copy`` parameter. + Default is True. + rows : numpy ndarray, list of list, optional + Row-oriented data for table instead of ``data`` argument. + copy_indices : bool, optional + Copy any indices in the input data. Default is True. + units : list, dict, optional + List or dict of units to apply to columns. + descriptions : list, dict, optional + List or dict of descriptions to apply to columns. + **kwargs : dict, optional + Additional keyword args when converting table-like object. + + """ + + def _is_mixin_for_table(self, col): + """ + Determine if ``col`` should be added to the table directly as + a mixin column. + """ + return has_info_class(col, MixinInfo) + + def _convert_col_for_table(self, col): + if isinstance(col, Quantity): + if self.masked and not isinstance(col, Masked): + col = Masked(col) + + elif isinstance(col, Column) and getattr(col, "unit", None) is not None: + # We need to turn the column into a quantity; use subok=True to allow + # Quantity subclasses identified in the unit (such as u.mag()). + q_cls = Masked(Quantity) if isinstance(col, MaskedColumn) else Quantity + try: + qcol = q_cls(col.data, col.unit, copy=None, subok=True) + except Exception as exc: + warnings.warn( + f"column {col.info.name} has a unit but is kept as " + f"a {col.__class__.__name__} as an attempt to " + f"convert it to Quantity failed with:\n{exc!r}", + AstropyUserWarning, + ) else: - format = valid_formats[0] + qcol.info = col.info + qcol.info.indices = col.info.indices + col = qcol + else: + col = super()._convert_col_for_table(col) + + return col + + def _convert_data_to_col( + self, data, copy=True, default_name=None, dtype=None, name=None + ): + if self.masked and isinstance(data, Quantity): + data = Masked(data) - writer = get_writer(format) - writer(self, *args, **kwargs) + return super()._convert_data_to_col( + data, copy=copy, default_name=default_name, dtype=dtype, name=name + ) diff --git a/astropy/table/table_helpers.py b/astropy/table/table_helpers.py new file mode 100644 index 000000000000..f7b2fe3625f9 --- /dev/null +++ b/astropy/table/table_helpers.py @@ -0,0 +1,173 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +""" +Helper functions for table development, mostly creating useful +tables for testing. +""" + +import string +import warnings +from itertools import cycle + +import numpy as np + +from astropy.io.votable.table import parse +from astropy.utils.data import get_pkg_data_filename +from astropy.utils.data_info import ParentDtypeInfo + +from .table import Column, Table + + +def simple_table(size=3, cols=None, kinds="ifS", masked=False): + """ + Return a simple table for testing. + + Example + -------- + :: + + >>> from astropy.table.table_helpers import simple_table + >>> print(simple_table(3, 6, masked=True, kinds='ifOS')) + a b c d e f + --- --- -------- --- --- --- + -- 1.0 {'c': 2} -- 5 5.0 + 2 2.0 -- e 6 -- + 3 -- {'e': 4} f -- 7.0 + + Parameters + ---------- + size : int + Number of table rows + cols : int, optional + Number of table columns. Defaults to number of kinds. + kinds : str + String consisting of the column dtype.kinds. This string + will be cycled through to generate the column dtype. + The allowed values are 'i', 'f', 'S', 'O'. + + Returns + ------- + out : `Table` + New table with appropriate characteristics + """ + if cols is None: + cols = len(kinds) + if cols > 26: + raise ValueError("Max 26 columns in SimpleTable") + + columns = [] + names = [chr(ord("a") + ii) for ii in range(cols)] + letters = np.array(list(string.ascii_letters)) + for jj, kind in zip(range(cols), cycle(kinds)): + if kind == "i": + data = np.arange(1, size + 1, dtype=np.int64) + jj + elif kind == "f": + data = np.arange(size, dtype=np.float64) + jj + elif kind == "S": + indices = (np.arange(size) + jj) % len(letters) + data = letters[indices] + elif kind == "O": + indices = (np.arange(size) + jj) % len(letters) + vals = letters[indices] + data = [{val.item(): index.item()} for val, index in zip(vals, indices)] + else: + raise ValueError("Unknown data kind") + columns.append(Column(data)) + + table = Table(columns, names=names, masked=masked) + if masked: + for ii, col in enumerate(table.columns.values()): + mask = np.array((np.arange(size) + ii) % 3, dtype=bool) + col.mask = ~mask + + return table + + +def complex_table(): + """ + Return a masked table from the io.votable test set that has a wide variety + of stressing types. + """ + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + votable = parse( + get_pkg_data_filename("../io/votable/tests/data/regression.xml"), + pedantic=False, + ) + first_table = votable.get_first_table() + table = first_table.to_table() + + return table + + +class ArrayWrapperInfo(ParentDtypeInfo): + _represent_as_dict_primary_data = "data" + + def _represent_as_dict(self): + """Represent Column as a dict that can be serialized.""" + col = self._parent + out = {"data": col.data} + return out + + def _construct_from_dict(self, map): + """Construct Column from ``map``.""" + data = map.pop("data") + out = self._parent_cls(data, **map) + return out + + +class ArrayWrapper: + """ + Minimal mixin using a simple wrapper around a numpy array. + + TODO: think about the future of this class as it is mostly for demonstration + purposes (of the mixin protocol). Consider taking it out of core and putting + it into a tutorial. One advantage of having this in core is that it is + getting tested in the mixin testing though it doesn't work for multidim + data. + """ + + info = ArrayWrapperInfo() + + def __init__(self, data, copy=True): + if isinstance(data, ArrayWrapper): + # this is done to preserve byteorder through copies + arr = data.data + else: + arr = data + self.data = np.array(arr, copy=copy) + if "info" in getattr(data, "__dict__", ()): + self.info = data.info + + def __getitem__(self, item): + if isinstance(item, (int, np.integer)): + out = self.data[item] + else: + out = self.__class__(self.data[item], copy=False) + if "info" in self.__dict__: + out.info = self.info + return out + + def __setitem__(self, item, value): + self.data[item] = value + + def __len__(self): + return len(self.data) + + def __eq__(self, other): + """Minimal equality testing, mostly for mixin unit tests.""" + if isinstance(other, ArrayWrapper): + return self.data == other.data + else: + return self.data == other + + @property + def dtype(self): + return self.data.dtype + + @property + def shape(self): + return self.data.shape + + def __repr__(self): + return f"<{self.__class__.__name__} name='{self.info.name}' data={self.data}>" diff --git a/astropy/table/tests/conftest.py b/astropy/table/tests/conftest.py new file mode 100644 index 000000000000..db788787d6db --- /dev/null +++ b/astropy/table/tests/conftest.py @@ -0,0 +1,270 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +""" +All of the pytest fixtures used by astropy.table are defined here. + +`conftest.py` is a "special" module name for pytest that is always +imported, but is not looked in for tests, and it is the recommended +place to put fixtures that are shared between modules. These fixtures +can not be defined in a module by a different name and still be shared +between modules. +""" + +import pickle +from collections import OrderedDict +from copy import deepcopy + +import numpy as np +import pytest + +from astropy import coordinates, table, time +from astropy import units as u +from astropy.table import QTable, Table, pprint +from astropy.table.table_helpers import ArrayWrapper + + +@pytest.fixture(params=[table.Column, table.MaskedColumn]) +def Column(request): + # Fixture to run all the Column tests for both an unmasked (ndarray) + # and masked (MaskedArray) column. + return request.param + + +class MaskedTable(table.Table): + def __init__(self, *args, **kwargs): + kwargs["masked"] = True + table.Table.__init__(self, *args, **kwargs) + + +class MyRow(table.Row): + pass + + +class MyColumn(table.Column): + pass + + +class MyMaskedColumn(table.MaskedColumn): + pass + + +class MyTableColumns(table.TableColumns): + pass + + +class MyTableFormatter(pprint.TableFormatter): + pass + + +class MyTable(table.Table): + Row = MyRow + Column = MyColumn + MaskedColumn = MyMaskedColumn + TableColumns = MyTableColumns + TableFormatter = MyTableFormatter + + +# Fixture to run all the Column tests for both an unmasked (ndarray) +# and masked (MaskedArray) column. + + +@pytest.fixture(params=["unmasked", "masked", "subclass"]) +def table_types(request): + class TableTypes: + def __init__(self, request): + if request.param == "unmasked": + self.Table = table.Table + self.Column = table.Column + elif request.param == "masked": + self.Table = MaskedTable + self.Column = table.MaskedColumn + elif request.param == "subclass": + self.Table = MyTable + self.Column = MyColumn + + return TableTypes(request) + + +# Fixture to run all the Column tests for both an unmasked (ndarray) +# and masked (MaskedArray) column. +@pytest.fixture(params=[False, True]) +def table_data(request): + class TableData: + def __init__(self, request): + self.Table = MaskedTable if request.param else table.Table + self.Column = table.MaskedColumn if request.param else table.Column + self.COLS = [ + self.Column( + name="a", + data=[1, 2, 3], + description="da", + format="%i", + meta={"ma": 1}, + unit="ua", + ), + self.Column( + name="b", + data=[4, 5, 6], + description="db", + format="%d", + meta={"mb": 1}, + unit="ub", + ), + self.Column( + name="c", + data=[7, 8, 9], + description="dc", + format="%f", + meta={"mc": 1}, + unit="ub", + ), + ] + self.DATA = self.Table(self.COLS) + + return TableData(request) + + +class SubclassTable(table.Table): + pass + + +@pytest.fixture(params=[True, False]) +def tableclass(request): + return table.Table if request.param else SubclassTable + + +@pytest.fixture(params=list(range(pickle.HIGHEST_PROTOCOL + 1))) +def protocol(request): + """ + Fixture to run all the tests for all available pickle protocols. + """ + return request.param + + +# Fixture to run all tests for both an unmasked (ndarray) and masked +# (MaskedArray) column. +@pytest.fixture(params=[False, True]) +def table_type(request): + return MaskedTable if request.param else table.Table + + +# Stuff for testing mixin columns + +MIXIN_COLS = { + "quantity": [0, 1, 2, 3] * u.m, + "longitude": coordinates.Longitude( + [0.0, 1.0, 5.0, 6.0] * u.deg, wrap_angle=180.0 * u.deg + ), + "latitude": coordinates.Latitude([5.0, 6.0, 10.0, 11.0] * u.deg), + "time": time.Time([2000, 2001, 2002, 2003], format="jyear"), + "timedelta": time.TimeDelta([1, 2, 3, 4], format="jd"), + "skycoord": coordinates.SkyCoord(ra=[0, 1, 2, 3] * u.deg, dec=[0, 1, 2, 3] * u.deg), + "sphericalrep": coordinates.SphericalRepresentation( + [0, 1, 2, 3] * u.deg, [0, 1, 2, 3] * u.deg, 1 * u.kpc + ), + "cartesianrep": coordinates.CartesianRepresentation( + [0, 1, 2, 3] * u.pc, [4, 5, 6, 7] * u.pc, [9, 8, 8, 6] * u.pc + ), + "sphericaldiff": coordinates.SphericalCosLatDifferential( + [0, 1, 2, 3] * u.mas / u.yr, [0, 1, 2, 3] * u.mas / u.yr, 10 * u.km / u.s + ), + "arraywrap": ArrayWrapper([0, 1, 2, 3]), + "arrayswap": ArrayWrapper( + np.arange(4, dtype="i").byteswap().view(np.dtype("i").newbyteorder()) + ), + "ndarraylil": np.array( + [(7, "a"), (8, "b"), (9, "c"), (9, "c")], dtype="\n1\n2\n3" - def test_format(self): + def test_format(self, Column): """Show that the formatted output from str() works""" - MAX_LINES = pprint.MAX_LINES - pprint.MAX_LINES = 7 - c1 = Column(name='a', data=np.arange(2000), dtype=float, - format='%6.2f') - assert str(c1) == (' a \n-------\n 0.00\n' - ' 1.00\n ...\n1998.00\n1999.00') - pprint.MAX_LINES = MAX_LINES + from astropy import conf - def test_convert_numpy_array(self): - d = Column('a', [1, 2, 3], dtype='i8') + with conf.set_temp("max_lines", 8): + c1 = Column(np.arange(2000), name="a", dtype=float, format="%6.2f") + assert str(c1).splitlines() == [ + " a ", + "-------", + " 0.00", + " 1.00", + " ...", + "1998.00", + "1999.00", + "Length = 2000 rows", + ] + + def test_convert_numpy_array(self, Column): + d = Column([1, 2, 3], name="a", dtype="i8") np_data = np.array(d) assert np.all(np_data == d) - np_data = np.array(d, copy=False) + np_data = np.asarray(d) assert np.all(np_data == d) - np_data = np.array(d, dtype='i4') + np_data = np.array(d, dtype="i4") assert np.all(np_data == d) + def test_convert_unit(self, Column): + d = Column([1, 2, 3], name="a", dtype="f8", unit="m") + d.convert_unit_to("km") + assert np.all(d.data == [0.001, 0.002, 0.003]) + + def test_array_wrap(self): + """Test that the __array_wrap__ method converts a reduction ufunc + output that has a different shape into an ndarray view. Without this a + method call like c.mean() returns a Column array object with length=1.""" + # Mean and sum for a 1-d float column + c = table.Column(name="a", data=[1.0, 2.0, 3.0]) + assert np.allclose(c.mean(), 2.0) + assert isinstance(c.mean(), (np.floating, float)) + assert np.allclose(c.sum(), 6.0) + assert isinstance(c.sum(), (np.floating, float)) + + # Non-reduction ufunc preserves Column class + assert isinstance(np.cos(c), table.Column) + + # Sum for a 1-d int column + c = table.Column(name="a", data=[1, 2, 3]) + assert np.allclose(c.sum(), 6) + assert isinstance(c.sum(), (np.integer, int)) + + # Sum for a 2-d int column + c = table.Column(name="a", data=[[1, 2, 3], [4, 5, 6]]) + assert c.sum() == 21 + assert isinstance(c.sum(), (np.integer, int)) + assert np.all(c.sum(axis=0) == [5, 7, 9]) + assert c.sum(axis=0).shape == (3,) + assert isinstance(c.sum(axis=0), np.ndarray) + + # Sum and mean for a 1-d masked column + c = table.MaskedColumn(name="a", data=[1.0, 2.0, 3.0], mask=[0, 0, 1]) + assert np.allclose(c.mean(), 1.5) + assert isinstance(c.mean(), (np.floating, float)) + assert np.allclose(c.sum(), 3.0) + assert isinstance(c.sum(), (np.floating, float)) + + def test_name_none(self, Column): + """Can create a column without supplying name, which defaults to None""" + c = Column([1, 2]) + assert c.name is None + assert np.all(c == np.array([1, 2])) + + def test_quantity_init(self, Column): + c = Column(data=np.array([1, 2, 3]) * u.m) + assert np.all(c.data == np.array([1, 2, 3])) + assert np.all(c.unit == u.m) + + c = Column(data=np.array([1, 2, 3]) * u.m, unit=u.cm) + assert np.all(c.data == np.array([100, 200, 300])) + assert np.all(c.unit == u.cm) + + def test_quantity_with_info_init(self, Column): + q = np.arange(3.0) * u.m + q.info.name = "q" + q.info.description = "an example" + q.info.meta = {"parrot": "dead"} + q.info.format = "3.1f" + c = Column(q) + assert c.name == "q" + assert c.description == "an example" + assert c.meta == q.info.meta + assert c.meta is not q.info.meta + assert c.pformat() == " q \n---\n0.0\n1.0\n2.0".splitlines() + + def test_quantity_comparison(self, Column): + # regression test for gh-6532 + c = Column([1, 2100, 3], unit="Hz") + q = 2 * u.kHz + check = c < q + assert np.all(check == [True, False, True]) + # This already worked, but just in case. + check = q >= c + assert np.all(check == [True, False, True]) + + def test_attrs_survive_getitem_after_change(self, Column): + """ + Test for issue #3023: when calling getitem with a MaskedArray subclass + the original object attributes are not copied. + """ + c1 = Column( + [1, 2, 3], name="a", unit="m", format="%i", description="aa", meta={"a": 1} + ) + c1.name = "b" + c1.unit = "km" + c1.format = "%d" + c1.description = "bb" + c1.meta = {"bbb": 2} + + for item in ( + slice(None, None), + slice(None, 1), + np.array([0, 2]), + np.array([False, True, False]), + ): + c2 = c1[item] + assert c2.name == "b" + assert c2.unit is u.km + assert c2.format == "%d" + assert c2.description == "bb" + assert c2.meta == {"bbb": 2} + + # Make sure that calling getitem resulting in a scalar does + # not copy attributes. + val = c1[1] + for attr in ("name", "unit", "format", "description", "meta"): + assert not hasattr(val, attr) + + def test_to_quantity(self, Column): + d = Column([1, 2, 3], name="a", dtype="f8", unit="m") + + assert np.all(d.quantity == ([1, 2, 3.0] * u.m)) + assert np.all(d.quantity.value == ([1, 2, 3.0] * u.m).value) + assert np.all(d.quantity == d.to("m")) + assert np.all(d.quantity.value == d.to("m").value) + + np.testing.assert_allclose( + d.to(u.km).value, ([0.001, 0.002, 0.003] * u.km).value + ) + np.testing.assert_allclose( + d.to("km").value, ([0.001, 0.002, 0.003] * u.km).value + ) + + np.testing.assert_allclose( + d.to(u.MHz, u.equivalencies.spectral()).value, + [299.792458, 149.896229, 99.93081933], + ) + + d_nounit = Column([1, 2, 3], name="a", dtype="f8", unit=None) + with pytest.raises(u.UnitsError): + d_nounit.to(u.km) + assert np.all(d_nounit.to(u.dimensionless_unscaled) == np.array([1, 2, 3])) + + # make sure the correct copy/no copy behavior is happening + q = [1, 3, 5] * u.km + + # to should always make a copy + d.to(u.km)[:] = q + np.testing.assert_allclose(d, [1, 2, 3]) + + # explicit copying of the quantity should not change the column + d.quantity.copy()[:] = q + np.testing.assert_allclose(d, [1, 2, 3]) + + # but quantity directly is a "view", accessing the underlying column + d.quantity[:] = q + np.testing.assert_allclose(d, [1000, 3000, 5000]) + + # view should also work for integers + d2 = Column([1, 2, 3], name="a", dtype=int, unit="m") + d2.quantity[:] = q + np.testing.assert_allclose(d2, [1000, 3000, 5000]) + + # but it should fail for strings or other non-numeric tables + d3 = Column(["arg", "name", "stuff"], name="a", unit="m") + with pytest.raises(TypeError): + d3.quantity + + def test_to_funcunit_quantity(self, Column): + """ + Tests for #8424, check if function-unit can be retrieved from column. + """ + d = Column([1, 2, 3], name="a", dtype="f8", unit="dex(AA)") + + assert np.all(d.quantity == ([1, 2, 3] * u.dex(u.AA))) + assert np.all(d.quantity.value == ([1, 2, 3] * u.dex(u.AA)).value) + assert np.all(d.quantity == d.to("dex(AA)")) + assert np.all(d.quantity.value == d.to("dex(AA)").value) + + # make sure, casting to linear unit works + q = [10, 100, 1000] * u.AA + np.testing.assert_allclose(d.to(u.AA), q) + + def test_item_access_type(self, Column): + """ + Tests for #3095, which forces integer item access to always return a plain + ndarray or MaskedArray, even in the case of a multi-dim column. + """ + integer_types = (int, np.int32, np.int64) + + for int_type in integer_types: + c = Column([[1, 2], [3, 4]]) + i0 = int_type(0) + i1 = int_type(1) + assert np.all(c[i0] == [1, 2]) + assert type(c[i0]) == ( + np.ma.MaskedArray if hasattr(Column, "mask") else np.ndarray + ) + assert c[i0].shape == (2,) + + c01 = c[i0:i1] + assert np.all(c01 == [[1, 2]]) + assert isinstance(c01, Column) + assert c01.shape == (1, 2) + + c = Column([1, 2]) + assert np.all(c[i0] == 1) + assert isinstance(c[i0], np.integer) + assert c[i0].shape == () + + c01 = c[i0:i1] + assert np.all(c01 == [1]) + assert isinstance(c01, Column) + assert c01.shape == (1,) + + def test_insert_basic(self, Column): + c = Column( + [0, 1, 2], + name="a", + dtype=int, + unit="mJy", + format="%i", + description="test column", + meta={"c": 8, "d": 12}, + ) + + # Basic insert + c1 = c.insert(1, 100) + assert np.all(c1 == [0, 100, 1, 2]) + assert c1.attrs_equal(c) + assert type(c) is type(c1) + if hasattr(c1, "mask"): + assert c1.data.shape == c1.mask.shape + + c1 = c.insert(-1, 100) + assert np.all(c1 == [0, 1, 100, 2]) + + c1 = c.insert(3, 100) + assert np.all(c1 == [0, 1, 2, 100]) + + c1 = c.insert(-3, 100) + assert np.all(c1 == [100, 0, 1, 2]) + + c1 = c.insert(1, [100, 200, 300]) + if hasattr(c1, "mask"): + assert c1.data.shape == c1.mask.shape -class TestAttrEqual(): + # Out of bounds index + with pytest.raises((ValueError, IndexError)): + c1 = c.insert(-4, 100) + with pytest.raises((ValueError, IndexError)): + c1 = c.insert(4, 100) + + def test_insert_axis(self, Column): + """Insert with non-default axis kwarg""" + c = Column([[1, 2], [3, 4]]) + + c1 = c.insert(1, [5, 6], axis=None) + assert np.all(c1 == [1, 5, 6, 2, 3, 4]) + + c1 = c.insert(1, [5, 6], axis=1) + assert np.all(c1 == [[1, 5, 2], [3, 6, 4]]) + + def test_insert_string_expand(self, Column): + c = Column(["a", "b"]) + c1 = c.insert(0, "abc") + assert np.all(c1 == ["abc", "a", "b"]) + + c = Column(["a", "b"]) + c1 = c.insert(0, ["c", "def"]) + assert np.all(c1 == ["c", "def", "a", "b"]) + + def test_insert_string_masked_values(self): + c = table.MaskedColumn(["a", "b"]) + c1 = c.insert(0, np.ma.masked) + assert np.all(c1 == ["", "a", "b"]) + assert np.all(c1.mask == [True, False, False]) + assert c1.dtype == "U1" + c2 = c.insert(1, np.ma.MaskedArray(["ccc", "dd"], mask=[True, False])) + assert np.all(c2 == ["a", "ccc", "dd", "b"]) + assert np.all(c2.mask == [False, True, False, False]) + assert c2.dtype == "U3" + + def test_insert_string_type_error(self, Column): + c = Column([1, 2]) + with pytest.raises(ValueError, match="invalid literal for int"): + c.insert(0, "string") + + c = Column(["a", "b"]) + with pytest.raises(TypeError, match="ufunc 'str_len' did not contain a loop"): + c.insert(0, 1) + + def test_insert_multidim(self, Column): + c = Column([[1, 2], [3, 4]], name="a", dtype=int) + + # Basic insert + c1 = c.insert(1, [100, 200]) + assert np.all(c1 == [[1, 2], [100, 200], [3, 4]]) + + # Broadcast + c1 = c.insert(1, 100) + assert np.all(c1 == [[1, 2], [100, 100], [3, 4]]) + + # Wrong shape + with pytest.raises(ValueError): + c1 = c.insert(1, [100, 200, 300]) + + def test_insert_object(self, Column): + c = Column(["a", 1, None], name="a", dtype=object) + + # Basic insert + c1 = c.insert(1, [100, 200]) + assert np.all(c1 == np.array(["a", [100, 200], 1, None], dtype=object)) + + def test_insert_masked(self): + c = table.MaskedColumn( + [0, 1, 2], name="a", fill_value=9999, mask=[False, True, False] + ) + + # Basic insert + c1 = c.insert(1, 100) + assert np.all(c1.data.data == [0, 100, 1, 2]) + assert c1.fill_value == 9999 + assert np.all(c1.data.mask == [False, False, True, False]) + assert type(c) is type(c1) + + for mask in (False, True): + c1 = c.insert(1, 100, mask=mask) + assert np.all(c1.data.data == [0, 100, 1, 2]) + assert np.all(c1.data.mask == [False, mask, True, False]) + + def test_masked_multidim_as_list(self): + data = np.ma.MaskedArray([1, 2], mask=[True, False]) + c = table.MaskedColumn([data]) + assert c.shape == (1, 2) + assert np.all(c[0].mask == [True, False]) + + def test_masked_multidim_nested_list(self): + data = [[1, np.ma.masked], [3, 4]] + c = _convert_sequence_data_to_array(data) + assert c.shape == (2, 2) + assert np.all(c.mask == [[False, True], [False, False]]) + + def test_contains_ma_masked_scalar(self): + # Regression test: ndim=0 should not raise (scalar input) + assert _contains_ma_masked(np.ma.masked, 0, np.ma.masked) is True + assert _contains_ma_masked(42, 0, np.ma.masked) is False + + def test_insert_masked_multidim(self): + c = table.MaskedColumn([[1, 2], [3, 4]], name="a", dtype=int) + + c1 = c.insert(1, [100, 200], mask=True) + assert np.all(c1.data.data == [[1, 2], [100, 200], [3, 4]]) + assert np.all(c1.data.mask == [[False, False], [True, True], [False, False]]) + + c1 = c.insert(1, [100, 200], mask=[True, False]) + assert np.all(c1.data.data == [[1, 2], [100, 200], [3, 4]]) + assert np.all(c1.data.mask == [[False, False], [True, False], [False, False]]) + + with pytest.raises(ValueError): + c1 = c.insert(1, [100, 200], mask=[True, False, True]) + + def test_mask_on_non_masked_table(self): + """ + When table is not masked and trying to set mask on column then + it's Raise AttributeError. + """ + + t = table.Table([[1, 2], [3, 4]], names=("a", "b"), dtype=("i4", "f8")) + + with pytest.raises(AttributeError): + t["a"].mask = [True, False] + + @pytest.mark.parametrize("scalar", [1, u.Quantity(0.6, "eV")]) + def test_access_scalar(self, scalar): + # see https://github.com/astropy/astropy/pull/15749#issuecomment-1867561072 + c = table.Column(scalar) + if isinstance(scalar, u.Quantity): + assert c.item() == scalar.value + else: + assert c.item() == scalar + + with pytest.raises(IndexError): + c[0] + + +@pytest.mark.parametrize( + "data", + [np.array([object()]), [object()]], +) +def test_deepcopy_object_column(data): + # see https://github.com/astropy/astropy/issues/13435 + c1 = table.Column(data, meta={"test": object()}) + c2 = copy.deepcopy(c1) + assert c2 is not c1 + assert c2[0] is not c1[0] + assert c2.meta["test"] is not c1.meta["test"] + + c3 = table.Column(c1, copy=True) + assert c3 is not c1 + assert c3[0] is c1[0] + assert c3.meta["test"] is not c1.meta["test"] + + +class TestAttrEqual: """Bunch of tests originally from ATpy that test the attrs_equal method.""" - def test_5(self): - c1 = Column(name='a', dtype=int, units='mJy') - c2 = Column(name='a', dtype=int, units='mJy') + def test_5(self, Column): + c1 = Column(name="a", dtype=int, unit="mJy") + c2 = Column(name="a", dtype=int, unit="mJy") assert c1.attrs_equal(c2) - def test_6(self): - c1 = Column(name='a', dtype=int, units='mJy', format='%i', - description='test column', meta={'c': 8, 'd': 12}) - c2 = Column(name='a', dtype=int, units='mJy', format='%i', - description='test column', meta={'c': 8, 'd': 12}) + def test_6(self, Column): + c1 = Column( + name="a", + dtype=int, + unit="mJy", + format="%i", + description="test column", + meta={"c": 8, "d": 12}, + ) + c2 = Column( + name="a", + dtype=int, + unit="mJy", + format="%i", + description="test column", + meta={"c": 8, "d": 12}, + ) assert c1.attrs_equal(c2) - def test_7(self): - c1 = Column(name='a', dtype=int, units='mJy', format='%i', - description='test column', meta={'c': 8, 'd': 12}) - c2 = Column(name='b', dtype=int, units='mJy', format='%i', - description='test column', meta={'c': 8, 'd': 12}) + def test_7(self, Column): + c1 = Column( + name="a", + dtype=int, + unit="mJy", + format="%i", + description="test column", + meta={"c": 8, "d": 12}, + ) + c2 = Column( + name="b", + dtype=int, + unit="mJy", + format="%i", + description="test column", + meta={"c": 8, "d": 12}, + ) assert not c1.attrs_equal(c2) - def test_8(self): - c1 = Column(name='a', dtype=int, units='mJy', format='%i', - description='test column', meta={'c': 8, 'd': 12}) - c2 = Column(name='a', dtype=float, units='mJy', format='%i', - description='test column', meta={'c': 8, 'd': 12}) + def test_8(self, Column): + c1 = Column( + name="a", + dtype=int, + unit="mJy", + format="%i", + description="test column", + meta={"c": 8, "d": 12}, + ) + c2 = Column( + name="a", + dtype=float, + unit="mJy", + format="%i", + description="test column", + meta={"c": 8, "d": 12}, + ) assert not c1.attrs_equal(c2) - def test_9(self): - c1 = Column(name='a', dtype=int, units='mJy', format='%i', - description='test column', meta={'c': 8, 'd': 12}) - c2 = Column(name='a', dtype=int, units='ergs/cm^2/s/Hz', format='%i', - description='test column', meta={'c': 8, 'd': 12}) + def test_9(self, Column): + c1 = Column( + name="a", + dtype=int, + unit="mJy", + format="%i", + description="test column", + meta={"c": 8, "d": 12}, + ) + c2 = Column( + name="a", + dtype=int, + unit="erg.cm-2.s-1.Hz-1", + format="%i", + description="test column", + meta={"c": 8, "d": 12}, + ) assert not c1.attrs_equal(c2) - def test_10(self): - c1 = Column(name='a', dtype=int, units='mJy', format='%i', - description='test column', meta={'c': 8, 'd': 12}) - c2 = Column(name='a', dtype=int, units='mJy', format='%g', - description='test column', meta={'c': 8, 'd': 12}) + def test_10(self, Column): + c1 = Column( + name="a", + dtype=int, + unit="mJy", + format="%i", + description="test column", + meta={"c": 8, "d": 12}, + ) + c2 = Column( + name="a", + dtype=int, + unit="mJy", + format="%g", + description="test column", + meta={"c": 8, "d": 12}, + ) assert not c1.attrs_equal(c2) - def test_11(self): - c1 = Column(name='a', dtype=int, units='mJy', format='%i', - description='test column', meta={'c': 8, 'd': 12}) - c2 = Column(name='a', dtype=int, units='mJy', format='%i', - description='another test column', meta={'c': 8, 'd': 12}) + def test_11(self, Column): + c1 = Column( + name="a", + dtype=int, + unit="mJy", + format="%i", + description="test column", + meta={"c": 8, "d": 12}, + ) + c2 = Column( + name="a", + dtype=int, + unit="mJy", + format="%i", + description="another test column", + meta={"c": 8, "d": 12}, + ) assert not c1.attrs_equal(c2) - def test_12(self): - c1 = Column(name='a', dtype=int, units='mJy', format='%i', - description='test column', meta={'c': 8, 'd': 12}) - c2 = Column(name='a', dtype=int, units='mJy', format='%i', - description='test column', meta={'e': 8, 'd': 12}) + def test_12(self, Column): + c1 = Column( + name="a", + dtype=int, + unit="mJy", + format="%i", + description="test column", + meta={"c": 8, "d": 12}, + ) + c2 = Column( + name="a", + dtype=int, + unit="mJy", + format="%i", + description="test column", + meta={"e": 8, "d": 12}, + ) assert not c1.attrs_equal(c2) - def test_13(self): - c1 = Column(name='a', dtype=int, units='mJy', format='%i', - description='test column', meta={'c': 8, 'd': 12}) - c2 = Column(name='a', dtype=int, units='mJy', format='%i', - description='test column', meta={'c': 9, 'd': 12}) + def test_13(self, Column): + c1 = Column( + name="a", + dtype=int, + unit="mJy", + format="%i", + description="test column", + meta={"c": 8, "d": 12}, + ) + c2 = Column( + name="a", + dtype=int, + unit="mJy", + format="%i", + description="test column", + meta={"c": 9, "d": 12}, + ) assert not c1.attrs_equal(c2) + + def test_col_and_masked_col(self): + c1 = table.Column( + name="a", + dtype=int, + unit="mJy", + format="%i", + description="test column", + meta={"c": 8, "d": 12}, + ) + c2 = table.MaskedColumn( + name="a", + dtype=int, + unit="mJy", + format="%i", + description="test column", + meta={"c": 8, "d": 12}, + ) + assert c1.attrs_equal(c2) + assert c2.attrs_equal(c1) + + +# Check that the meta descriptor is working as expected. The MetaBaseTest class +# takes care of defining all the tests, and we simply have to define the class +# and any minimal set of args to pass. + + +class TestMetaColumn(MetaBaseTest): + test_class = table.Column + args = () + + +class TestMetaMaskedColumn(MetaBaseTest): + test_class = table.MaskedColumn + args = () + + +def test_getitem_metadata_regression(): + """ + Regression test for #1471: MaskedArray does not call __array_finalize__ so + the meta-data was not getting copied over. By overloading _update_from we + are able to work around this bug. + """ + + # Make sure that meta-data gets propagated with __getitem__ + + c = table.Column( + data=[1, 2], name="a", description="b", unit="m", format="%i", meta={"c": 8} + ) + assert c[1:2].name == "a" + assert c[1:2].description == "b" + assert c[1:2].unit == "m" + assert c[1:2].format == "%i" + assert c[1:2].meta["c"] == 8 + + c = table.MaskedColumn( + data=[1, 2], name="a", description="b", unit="m", format="%i", meta={"c": 8} + ) + assert c[1:2].name == "a" + assert c[1:2].description == "b" + assert c[1:2].unit == "m" + assert c[1:2].format == "%i" + assert c[1:2].meta["c"] == 8 + + # As above, but with take() - check the method and the function + + c = table.Column( + data=[1, 2, 3], name="a", description="b", unit="m", format="%i", meta={"c": 8} + ) + for subset in [c.take([0, 1]), np.take(c, [0, 1])]: + assert subset.name == "a" + assert subset.description == "b" + assert subset.unit == "m" + assert subset.format == "%i" + assert subset.meta["c"] == 8 + + # Metadata isn't copied for scalar values + for subset in [c.take(0), np.take(c, 0)]: + assert subset == 1 + assert subset.shape == () + assert not isinstance(subset, table.Column) + + c = table.MaskedColumn( + data=[1, 2, 3], name="a", description="b", unit="m", format="%i", meta={"c": 8} + ) + for subset in [c.take([0, 1]), np.take(c, [0, 1])]: + assert subset.name == "a" + assert subset.description == "b" + assert subset.unit == "m" + assert subset.format == "%i" + assert subset.meta["c"] == 8 + + # Metadata isn't copied for scalar values + for subset in [c.take(0), np.take(c, 0)]: + assert subset == 1 + assert subset.shape == () + assert not isinstance(subset, table.MaskedColumn) + + +def test_unicode_guidelines(): + arr = np.array([1, 2, 3]) + c = table.Column(arr, name="a") + + assert_follows_unicode_guidelines(c) + + +def test_scalar_column(): + """ + Column is not designed to hold scalars, but for numpy 1.6 this can happen: + + >> type(np.std(table.Column([1, 2]))) + astropy.table.column.Column + """ + c = table.Column(1.5) + assert repr(c) == "1.5" + assert str(c) == "1.5" + + +def test_qtable_column_conversion(): + """ + Ensures that a QTable that gets assigned a unit switches to be Quantity-y + """ + qtab = table.QTable([[1, 2], [3, 4.2]], names=["i", "f"]) + + assert isinstance(qtab["i"], table.column.Column) + assert isinstance(qtab["f"], table.column.Column) + + qtab["i"].unit = "km/s" + assert isinstance(qtab["i"], u.Quantity) + assert isinstance(qtab["f"], table.column.Column) + + # should follow from the above, but good to make sure as a #4497 regression test + assert isinstance(qtab["i"][0], u.Quantity) + assert isinstance(qtab[0]["i"], u.Quantity) + assert not isinstance(qtab["f"][0], u.Quantity) + assert not isinstance(qtab[0]["f"], u.Quantity) + + # Regression test for #5342: if a function unit is assigned, the column + # should become the appropriate FunctionQuantity subclass. + qtab["f"].unit = u.dex(u.cm / u.s**2) + assert isinstance(qtab["f"], u.Dex) + + +@pytest.mark.parametrize("masked", [True, False]) +def test_string_truncation_warning(masked): + """ + Test warnings associated with in-place assignment to a string + column that results in truncation of the right hand side. + """ + t = table.Table([["aa", "bb"]], names=["a"], masked=masked) + t["a"][1] = "cc" + t["a"][:] = "dd" + + with pytest.warns( + table.StringTruncateWarning, + match=r"truncated right side string\(s\) longer than 2 character\(s\)", + ) as w: + frameinfo = getframeinfo(currentframe()) + t["a"][0] = "eee" # replace item with string that gets truncated + assert t["a"][0] == "ee" + assert len(w) == 1 + + # Make sure the warning points back to the user code line + assert w[0].lineno == frameinfo.lineno + 1 + assert "test_column" in w[0].filename + + with pytest.warns( + table.StringTruncateWarning, + match=r"truncated right side string\(s\) longer than 2 character\(s\)", + ) as w: + t["a"][:] = ["ff", "ggg"] # replace item with string that gets truncated + assert np.all(t["a"] == ["ff", "gg"]) + assert len(w) == 1 + + # Test the obscure case of assigning from an array that was originally + # wider than any of the current elements (i.e. dtype is U4 but actual + # elements are U1 at the time of assignment). + val = np.array(["ffff", "gggg"]) + val[:] = ["f", "g"] + t["a"][:] = val + assert np.all(t["a"] == ["f", "g"]) + + +def test_string_truncation_warning_masked(): + """ + Test warnings associated with in-place assignment to a string + to a masked column, specifically where the right hand side + contains np.ma.masked. + """ + + # Test for strings, but also cover assignment of np.ma.masked to + # int and float masked column setting. This was previously only + # covered in an unrelated io.ascii test (test_line_endings) which + # showed an unexpected difference between handling of str and numeric + # masked arrays. + for values in (["a", "b"], [1, 2], [1.0, 2.0]): + mc = table.MaskedColumn(values) + + mc[1] = np.ma.masked + assert np.all(mc.mask == [False, True]) + + mc[:] = np.ma.masked + assert np.all(mc.mask == [True, True]) + + mc = table.MaskedColumn(["aa", "bb"]) + + with pytest.warns( + table.StringTruncateWarning, + match=r"truncated right side string\(s\) longer than 2 character\(s\)", + ) as w: + mc[:] = [np.ma.masked, "ggg"] # replace item with string that gets truncated + assert mc[1] == "gg" + assert np.all(mc.mask == [True, False]) + assert len(w) == 1 + + +@pytest.mark.parametrize("Column", (table.Column, table.MaskedColumn)) +def test_col_unicode_sandwich_create_from_str(Column): + """ + Create a bytestring Column from strings (including unicode) in Py3. + """ + # a-umlaut is a 2-byte character in utf-8, test fails with ascii encoding. + # Stress the system by injecting non-ASCII characters. + uba = "bä" + c = Column([uba, "def"], dtype="S") + assert c.dtype.char == "S" + assert c[0] == uba + assert isinstance(c[0], str) + assert isinstance(c[:0], table.Column) + assert np.all(c[:2] == np.array([uba, "def"])) + + +@pytest.mark.parametrize("Column", (table.Column, table.MaskedColumn)) +def test_col_unicode_sandwich_bytes_obj(Column): + """ + Create a Column of dtype object with bytestring in it and make sure + it keeps the bytestring and not convert to str with accessed. + """ + c = Column([None, b"def"]) + assert c.dtype.char == "O" + assert not c[0] + assert c[1] == b"def" + assert isinstance(c[1], bytes) + assert not isinstance(c[1], str) + assert isinstance(c[:0], table.Column) + assert np.all(c[:2] == np.array([None, b"def"])) + assert not np.all(c[:2] == np.array([None, "def"])) + + +@pytest.mark.parametrize("Column", (table.Column, table.MaskedColumn)) +def test_col_unicode_sandwich_bytes(Column): + """ + Create a bytestring Column from bytes and ensure that it works in Python 3 in + a convenient way like in Python 2. + """ + # a-umlaut is a 2-byte character in utf-8, test fails with ascii encoding. + # Stress the system by injecting non-ASCII characters. + uba = "bä" + uba8 = uba.encode("utf-8") + c = Column([uba8, b"def"]) + assert c.dtype.char == "S" + assert c[0] == uba + assert isinstance(c[0], str) + assert isinstance(c[:0], table.Column) + assert np.all(c[:2] == np.array([uba, "def"])) + + assert isinstance(c[:], table.Column) + assert c[:].dtype.char == "S" + + # Array / list comparisons + assert np.all(c == [uba, "def"]) + + ok = c == [uba8, b"def"] + assert type(ok) is type(c.data) + assert ok.dtype.char == "?" + assert np.all(ok) + + assert np.all(c == np.array([uba, "def"])) + assert np.all(c == np.array([uba8, b"def"])) + + # Scalar compare + cmps = (uba, uba8) + for cmp in cmps: + ok = c == cmp + assert type(ok) is type(c.data) + assert np.all(ok == [True, False]) + + +def test_col_unicode_sandwich_unicode(): + """ + Sanity check that Unicode Column behaves normally. + """ + uba = "bä" + uba8 = uba.encode("utf-8") + + c = table.Column([uba, "def"], dtype="U") + assert c[0] == uba + assert isinstance(c[:0], table.Column) + assert isinstance(c[0], str) + assert np.all(c[:2] == np.array([uba, "def"])) + + assert isinstance(c[:], table.Column) + assert c[:].dtype.char == "U" + + ok = c == [uba, "def"] + assert type(ok) == np.ndarray + assert ok.dtype.char == "?" + assert np.all(ok) + + with warnings.catch_warnings(): + # Ignore the FutureWarning in numpy >=1.24 (it is OK). + warnings.filterwarnings("ignore", message=".*elementwise comparison failed.*") + assert np.all(c != [uba8, b"def"]) + + +def test_masked_col_unicode_sandwich(): + """ + Create a bytestring MaskedColumn and ensure that it works in Python 3 in + a convenient way like in Python 2. + """ + c = table.MaskedColumn([b"abc", b"def"]) + c[1] = np.ma.masked + assert isinstance(c[:0], table.MaskedColumn) + assert isinstance(c[0], str) + + assert c[0] == "abc" + assert c[1] is np.ma.masked + + assert isinstance(c[:], table.MaskedColumn) + assert c[:].dtype.char == "S" + + ok = c == ["abc", "def"] + assert ok[0] + assert ok[1] is np.ma.masked + assert np.all(c == [b"abc", b"def"]) + assert np.all(c == np.array(["abc", "def"])) + assert np.all(c == np.array([b"abc", b"def"])) + + for cmp in ("abc", b"abc"): + ok = c == cmp + assert type(ok) is np.ma.MaskedArray + assert ok[0] + assert ok[1] is np.ma.masked + + +@pytest.mark.parametrize("Column", (table.Column, table.MaskedColumn)) +def test_unicode_sandwich_set(Column): + """ + Test setting + """ + uba = "bä" + + c = Column([b"abc", b"def"]) + + c[0] = b"aa" + assert np.all(c == ["aa", "def"]) + + c[0] = uba # ä is a 2-byte character in utf-8, test fails with ascii encoding + assert np.all(c == [uba, "def"]) + assert c.pformat() == ["None", "----", " " + uba, " def"] + + c[:] = b"cc" + assert np.all(c == ["cc", "cc"]) + + c[:] = uba + assert np.all(c == [uba, uba]) + + c[:] = "" + c[:] = [uba, b"def"] + assert np.all(c == [uba, b"def"]) + + +@pytest.mark.parametrize("class1", [table.MaskedColumn, table.Column]) +@pytest.mark.parametrize("class2", [table.MaskedColumn, table.Column, str, list]) +def test_unicode_sandwich_compare(class1, class2): + """Test that comparing a bytestring Column/MaskedColumn with various + str (unicode) object types gives the expected result. Tests #6838. + """ + obj1 = class1([b"a", b"c"]) + if class2 is str: + obj2 = "a" + elif class2 is list: + obj2 = ["a", "b"] + else: + obj2 = class2(["a", "b"]) + + assert np.all((obj1 == obj2) == [True, False]) + assert np.all((obj2 == obj1) == [True, False]) + + assert np.all((obj1 != obj2) == [False, True]) + assert np.all((obj2 != obj1) == [False, True]) + + assert np.all((obj1 > obj2) == [False, True]) + assert np.all((obj2 > obj1) == [False, False]) + + assert np.all((obj1 <= obj2) == [True, False]) + assert np.all((obj2 <= obj1) == [True, True]) + + assert np.all((obj1 < obj2) == [False, False]) + assert np.all((obj2 < obj1) == [False, True]) + + assert np.all((obj1 >= obj2) == [True, True]) + assert np.all((obj2 >= obj1) == [True, False]) + + +def test_unicode_sandwich_masked_compare(): + """Test the fix for #6839 from #6899.""" + c1 = table.MaskedColumn(["a", "b", "c", "d"], mask=[True, False, True, False]) + c2 = table.MaskedColumn([b"a", b"b", b"c", b"d"], mask=[True, True, False, False]) + + for cmp in ((c1 == c2), (c2 == c1)): + assert cmp[0] is np.ma.masked + assert cmp[1] is np.ma.masked + assert cmp[2] is np.ma.masked + assert cmp[3] + + for cmp in ((c1 != c2), (c2 != c1)): + assert cmp[0] is np.ma.masked + assert cmp[1] is np.ma.masked + assert cmp[2] is np.ma.masked + assert not cmp[3] + + # Note: comparisons <, >, >=, <= fail to return a masked array entirely, + # see https://github.com/numpy/numpy/issues/10092. + + +def test_structured_masked_column_roundtrip(): + mc = table.MaskedColumn( + [(1.0, 2.0), (3.0, 4.0)], mask=[(False, False), (False, False)], dtype="f8,f8" + ) + assert len(mc.dtype.fields) == 2 + mc2 = table.MaskedColumn(mc) + assert_array_equal(mc2, mc) + + +@pytest.mark.parametrize("dtype", ["i4,f4", "f4,(2,)f8"]) +def test_structured_empty_column_init(dtype): + dtype = np.dtype(dtype) + c = table.Column(length=5, shape=(2,), dtype=dtype) + assert c.shape == (5, 2) + assert c.dtype == dtype + + +def test_column_value_access(): + """Can a column's underlying data consistently be accessed via `.value`, + whether it is a `Column`, `MaskedColumn`, `Quantity`, or `Time`?""" + data = np.array([1, 2, 3]) + tbl = table.QTable( + { + "a": table.Column(data), + "b": table.MaskedColumn(data), + "c": u.Quantity(data), + "d": time.Time(data, format="mjd"), + } + ) + assert type(tbl["a"].value) == np.ndarray + assert type(tbl["b"].value) == np.ma.MaskedArray + assert type(tbl["c"].value) == np.ndarray + assert type(tbl["d"].value) == np.ndarray + + +def test_masked_column_serialize_method_propagation(): + mc = table.MaskedColumn([1.0, 2.0, 3.0], mask=[True, False, True]) + assert mc.info.serialize_method["ecsv"] == "null_value" + mc.info.serialize_method["ecsv"] = "data_mask" + assert mc.info.serialize_method["ecsv"] == "data_mask" + mc2 = mc.copy() + assert mc2.info.serialize_method["ecsv"] == "data_mask" + mc3 = table.MaskedColumn(mc) + assert mc3.info.serialize_method["ecsv"] == "data_mask" + mc4 = mc.view(table.MaskedColumn) + assert mc4.info.serialize_method["ecsv"] == "data_mask" + mc5 = mc[1:] + assert mc5.info.serialize_method["ecsv"] == "data_mask" + + +def test_masked_column_deepcopy_info_format_funcs(): + """Test the fix for #19412""" + mc = table.MaskedColumn([1.0, 2.0, 3.0], mask=[True, False, True]) + # Set a non-default serialize method to make sure that gets copied over. + mc.info.serialize_method["ecsv"] = "data_mask" + + mc_copy = copy.deepcopy(mc) + + assert mc_copy.info.serialize_method["ecsv"] == "data_mask" + # Prior to the fix, the _format_funcs did not exist on the info object. + assert mc_copy.info._format_funcs == {} + + +@pytest.mark.parametrize("dtype", ["S", "U", "i"]) +def test_searchsorted(Column, dtype): + c = Column([1, 2, 2, 3], dtype=dtype) + if isinstance(Column, table.MaskedColumn): + # Searchsorted seems to ignore the mask + c[2] = np.ma.masked + + if dtype == "i": + vs = (2, [2, 1]) + else: + vs = ("2", ["2", "1"], b"2", [b"2", b"1"]) + for v in vs: + v = np.array(v, dtype=dtype) + exp = np.searchsorted(c.data, v, side="right") + res = c.searchsorted(v, side="right") + assert np.all(res == exp) + res = np.searchsorted(c, v, side="right") + assert np.all(res == exp) + + +def test_masked_unit_conversion(): + # regression test for gh-9521 + c = table.MaskedColumn([3.5, 2.4, 1.7], name="test", unit=u.km) + c.convert_unit_to(u.m) + assert c.unit == (c * 2.0).unit + + +@pytest.mark.parametrize( + "copy", + [ + False, + pytest.param( + True, + marks=pytest.mark.xfail( + reason="See https://github.com/numpy/numpy/issues/27301" + ), + ), + ], +) +def test_zero_length_strings(Column, copy): + # Easiest way to get a zero-sized byte string is with a structured dtype. + data = np.array([("", 12)], dtype=[("a", "S"), ("b", "i4")]) + col = Column(data["a"], name="a", copy=copy) + assert col.dtype.itemsize == 0 + assert col.dtype == data.dtype["a"] + + +def test_setting_column_name_to_with_invalid_type(Column): + # see https://github.com/astropy/astropy/issues/17449 + col = Column([1, 2], name="a") + assert col.info.name == "a" + + col.name = None + assert col.info.name is None + + with pytest.raises( + TypeError, match="Expected a str value, got 2.3 with type float" + ): + col.name = 2.3 diff --git a/astropy/table/tests/test_df.py b/astropy/table/tests/test_df.py new file mode 100644 index 000000000000..47ba7956df43 --- /dev/null +++ b/astropy/table/tests/test_df.py @@ -0,0 +1,750 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +import io +import json + +import numpy as np +import pytest +from numpy.testing import assert_allclose, assert_array_equal + +from astropy import table +from astropy import units as u +from astropy.table import MaskedColumn, Table +from astropy.time import Time, TimeDelta +from astropy.utils import minversion +from astropy.utils.compat.optional_deps import ( + HAS_DASK, + HAS_DUCKDB, + HAS_NARWHALS, + HAS_PANDAS, + HAS_POLARS, + HAS_PYARROW, +) + +from .conftest import MIXIN_COLS + + +@pytest.mark.parametrize( + "backend,use_legacy_pandas_api", + [ + pytest.param( + "pandas", + True, + marks=pytest.mark.skipif(not HAS_PANDAS, reason="requires pandas"), + id="pandas-legacy", + ), + pytest.param( + "pandas", + False, + marks=pytest.mark.skipif( + not HAS_NARWHALS or not HAS_PANDAS, + reason="requires narwhals and pandas", + ), + id="pandas-generic", + ), + pytest.param( + "polars", + False, + marks=pytest.mark.skipif( + not HAS_NARWHALS or not HAS_POLARS, + reason="requires narwhals and polars", + ), + id="polars-generic", + ), + pytest.param( + "pyarrow", + False, + marks=pytest.mark.skipif( + not HAS_NARWHALS or not HAS_PYARROW, + reason="requires narwhals and pyarrow", + ), + id="pyarrow-generic", + ), + pytest.param( + "dask", + False, + marks=pytest.mark.skipif( + not HAS_NARWHALS or not HAS_DASK, + reason="requires narwhals and dask", + ), + id="dask-generic", + ), + pytest.param( + "duckdb", + False, + marks=pytest.mark.skipif( + not HAS_NARWHALS or not HAS_DUCKDB, + reason="requires narwhals and duckdb", + ), + id="duckdb-generic", + ), + ], +) +class TestDataFrameConversion: + """Test DataFrame conversion functionality for both legacy pandas and generic backends.""" + + def _to_dataframe(self, table, backend, use_legacy_pandas_api, **kwargs): + """Convert table to dataframe using appropriate method.""" + if use_legacy_pandas_api: + if backend != "pandas": + raise ValueError( + "Legacy conversion is only supported for the pandas backend." + ) + return table.to_pandas(**kwargs) + + # Lazy backends cannot be exported to + if backend in ("dask", "duckdb"): + pytest.skip("Lazy backends cannot be converted back to Table") + + return table.to_df(backend, **kwargs) + + def _from_dataframe(self, df, backend, use_legacy_pandas_api, **kwargs): + """Convert dataframe to table using appropriate method.""" + if use_legacy_pandas_api: + if backend != "pandas": + raise ValueError( + "Legacy conversion is only supported for the pandas backend." + ) + return table.Table.from_pandas(df, **kwargs) + return table.Table.from_df(df, **kwargs) + + def test_simple(self, backend, use_legacy_pandas_api): + """Test basic endianness and data type handling.""" + t = table.Table() + + for endian in ["<", ">", "="]: + for kind in ["f", "i"]: + for byte in ["2", "4", "8"]: + dtype = np.dtype(endian + kind + byte) + x = np.array([1, 2, 3], dtype=dtype) + t[endian + kind + byte] = x.view(x.dtype.newbyteorder(endian)) + + t["u"] = ["a", "b", "c"] + t["s"] = [b"a", b"b", b"c"] + + d = self._to_dataframe(t, backend, use_legacy_pandas_api) + + # Basic round-trip test + t2 = self._from_dataframe(d, backend, use_legacy_pandas_api) + + if minversion("pandas", "3.0.0.dev"): + # upstream feature of pandas + import pandas as pd + + pandas_string_dtype = pd.StringDtype(na_value=np.nan) + else: + # PANDAS_LT_3_0 + pandas_string_dtype = np.dtype("O") + + for column in t.columns: + original_col = t[column] + roundtrip_col = t2[column] + + assert roundtrip_col.dtype.kind == original_col.dtype.kind + + if original_col.dtype.kind in ("U", "S"): + assert_array_equal(roundtrip_col, original_col) + if original_col.dtype.kind == "U" and backend == "pandas": + # Pandas-specific checks + assert d[column].dtype == pandas_string_dtype + else: + # Generic comparison with tolerance + assert_allclose(roundtrip_col, original_col) + + # Compare dtypes by value, not identity (normalize endianness) + t_dtype = original_col.dtype.newbyteorder("=") + t2_dtype = roundtrip_col.dtype.newbyteorder("=") + + if backend == "polars" and "f2" in column: + # No Polars Float16 support + pass + else: + assert t2_dtype == t_dtype + + # Pandas-specific checks + if backend == "pandas": + # Pandas-specific exact comparison + assert_array_equal(d[column], t[column]) + if t[column].dtype.isnative: + assert d[column].dtype == t[column].dtype + else: + assert d[column].dtype == t[column].dtype.newbyteorder() + + # Pandas-specific endian tests (skip if not pandas) + if not backend == "pandas": + return + + # Regression test for astropy/astropy#1156 - the following code gave a + # ValueError: Big-endian buffer not supported on little-endian + # compiler. We now automatically swap the endian-ness to native order + # upon adding the arrays to the data frame. + # Explicitly testing little/big/native endian separately - + # regression for a case in astropy/astropy#11286 not caught by #3729. + d[["i4"]] + d[["f4"]] + + # Additional round-trip checks + for column in t.columns: + if column in ("u", "s"): + assert_array_equal(t2[column], t[column]) + else: + assert_allclose(t2[column], t[column]) + if t[column].dtype.isnative: + assert t2[column].dtype == t[column].dtype + else: + assert t2[column].dtype == t[column].dtype.newbyteorder() + + def test_from_df_simple(self, backend, use_legacy_pandas_api): + data = {"a": [1, 2, 3], "b": [4.0, 5.0, 6.0], "c": ["x", "y", "z"]} + match backend: + case "pandas": + import pandas as pd + + df = pd.DataFrame(data) + case "polars": + import polars as pl + + df = pl.DataFrame(data) + case "pyarrow": + import pyarrow as pa + + df = pa.Table.from_pydict(data) + case "dask": + import dask.array as da + import dask.dataframe as dd + + df = dd.concat( + [ + dd.from_dask_array( + da.from_array(arr, chunks="auto"), columns=col + ) + for col, arr in data.items() + ], + axis=1, + ) + case "duckdb": + import duckdb + + df = duckdb.read_json( + io.StringIO( + json.dumps( + [ + dict(zip(data.keys(), values, strict=True)) + for values in zip(*data.values(), strict=True) + ] + ) + ) + ) + + case _: + raise ValueError(f"Unknown backend: {backend}") + + # Test if the columns are as expected + tb = self._from_dataframe(df, backend, use_legacy_pandas_api) + + for col in tb.colnames: + assert_array_equal(tb[col], data[col]) + + def test_int128(self, backend, use_legacy_pandas_api): + match backend: + case "polars": + import polars as pl + + df = pl.DataFrame( + [pl.Series(name="x", values=range(10), dtype=pl.Int128)] + ) + + case "pandas" | "pyarrow" | "dask" | "duckdb": + return + + case _: + raise ValueError(f"Unknown backend: {backend}") + + with pytest.raises( + ValueError, + match="Astropy Table does not support narwhals.Int128", + ): + _ = self._from_dataframe(df, backend, use_legacy_pandas_api) + + @pytest.mark.parametrize("use_IndexedTable", [False, True]) + def test_to_df_index(self, backend, use_legacy_pandas_api, use_IndexedTable): + """Test indexing options for both legacy pandas and generic backends.""" + + class IndexedTable(table.QTable): + """Always index the first column""" + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.add_index(self.colnames[0]) + + tm = Time([1998, 2002], format="jyear") + x = [1, 2] + table_cls = IndexedTable if use_IndexedTable else table.QTable + t = table_cls([tm, x], names=["tm", "x"]) + + match backend: + case "pandas": + import pandas as pd + + row_index = pd.RangeIndex(0, 2, 1) + tm_index = pd.DatetimeIndex( + ["1998-01-01", "2002-01-01"], + dtype="datetime64[ns]", + name="tm", + freq=None, + ) + tp = self._to_dataframe(t, backend, use_legacy_pandas_api) + + case "polars" | "pyarrow": + with pytest.raises( + ValueError, + match="Indexing is only supported for pandas-like backends", + ): + self._to_dataframe(t, backend, use_legacy_pandas_api, index="tm") + return + case "dask" | "duckdb": + # Lazy backends will raise ValueError in _to_dataframe + self._to_dataframe(t, backend, use_legacy_pandas_api) + return + case _: + raise ValueError(f"Unknown backend: {backend}") + + if not use_IndexedTable: + assert_array_equal(tp.index, row_index) + tp = self._to_dataframe(t, backend, use_legacy_pandas_api, index="tm") + assert_array_equal(tp.index, tm_index) + t.add_index("tm") + + tp = self._to_dataframe(t, backend, use_legacy_pandas_api) + assert_array_equal(tp.index, tm_index) + # Make sure writing to dataframe didn't hack the original table + assert t["tm"].info.indices + + tp = self._to_dataframe(t, backend, use_legacy_pandas_api, index=True) + assert_array_equal(tp.index, tm_index) + + tp = self._to_dataframe(t, backend, use_legacy_pandas_api, index=False) + assert_array_equal(tp.index, row_index) + + with pytest.raises(ValueError, match="is not in the table columns"): + self._to_dataframe(t, backend, use_legacy_pandas_api, index="not a column") + + def test_to_df_index_numpy_bool_true_without_primary_key( + self, backend, use_legacy_pandas_api + ): + if backend != "pandas": + pytest.skip("requires pandas") + + t = table.QTable([[1, 2], [3, 4]], names=["a", "b"]) + + with pytest.raises( + ValueError, match="index=True requires a single-column primary key" + ): + self._to_dataframe( + t, + backend, + use_legacy_pandas_api, + index=np.bool_(True), + ) + + def test_from_df_index(self, backend, use_legacy_pandas_api): + """Test index handling in from_dataframe conversion.""" + tm = Time([1998, 2002], format="jyear") + x = [1, 2] + t = table.Table([tm, x], names=["tm", "x"]) + match backend: + case "polars" | "pyarrow": + # Non-pandas backends don't support indexing + with pytest.raises( + ValueError, + match="Indexing is only supported for pandas-like backends", + ): + self._to_dataframe(t, backend, use_legacy_pandas_api, index="tm") + return + case "dask" | "duckdb": + # Lazy backends will raise ValueError in _to_dataframe + self._to_dataframe(t, backend, use_legacy_pandas_api, index="tm") + return + case "pandas": + tp = self._to_dataframe(t, backend, use_legacy_pandas_api, index="tm") + case _: + raise ValueError(f"Unknown backend: {backend}") + + t2 = self._from_dataframe(tp, backend, use_legacy_pandas_api) + assert t2.colnames == ["x"] + + t2 = self._from_dataframe(tp, backend, use_legacy_pandas_api, index=True) + assert t2.colnames == ["tm", "x"] + assert np.allclose(t2["tm"].jyear, tm.jyear) + + def test_units(self, backend, use_legacy_pandas_api): + """Test handling of units in from_dataframe conversion.""" + data = {"x": [1, 2, 3], "t": [1.3, 1.2, 1.8]} + match backend: + case "pandas": + import pandas as pd + + df = pd.DataFrame(data) + case "polars": + import polars as pl + + df = pl.DataFrame(data) + case "pyarrow": + import pyarrow as pa + + df = pa.Table.from_pydict(data) + case "dask": + import dask.array as da + import dask.dataframe as dd + + # Need to aggregate data into 2D dask array + df = dd.concat( + [ + dd.from_dask_array( + da.from_array(arr, chunks="auto"), columns=col + ) + for col, arr in data.items() + ], + axis=1, + ) + case "duckdb": + import duckdb + + # DuckDB can't read directly from dict, need to stream via JSON + df = duckdb.read_json( + io.StringIO( + json.dumps( + [ + dict(zip(data.keys(), values), strict=True) + for values in zip(*data.values(), strict=True) + ] + ) + ) + ) + case _: + raise ValueError(f"Unknown backend: {backend}") + + t = self._from_dataframe( + df, backend, use_legacy_pandas_api, units={"x": u.m, "t": u.s} + ) + + assert t["x"].unit == u.m + assert t["t"].unit == u.s + + # test error if not a mapping + with pytest.raises(TypeError): + self._from_dataframe(df, backend, use_legacy_pandas_api, units=[u.m, u.s]) + + # test warning is raised if additional columns in units dict + with pytest.warns(UserWarning, match="{'y'}"): + self._from_dataframe( + df, backend, use_legacy_pandas_api, units={"x": u.m, "t": u.s, "y": u.m} + ) + + @pytest.mark.parametrize("unsigned", ["u", ""]) + @pytest.mark.parametrize("bits", [8, 16, 32, 64]) + def test_nullable_int(self, backend, use_legacy_pandas_api, unsigned, bits): + """Test nullable integer handling.""" + np_dtype = f"{unsigned}int{bits}" + c = MaskedColumn([1, 2], mask=[False, True], dtype=np_dtype) + t = Table([c]) + df = self._to_dataframe(t, backend, use_legacy_pandas_api) + + t2 = self._from_dataframe(df, backend, use_legacy_pandas_api) + assert str(t2["col0"].dtype) == np_dtype + assert_array_equal(t2["col0"].mask, [False, True]) + assert_array_equal(t2["col0"], c) + + @pytest.mark.parametrize("ndim", [1, 2, 3]) + def test_nd_columns(self, backend, use_legacy_pandas_api, ndim): + """Test handling of multidimensional columns.""" + # Add one since we want the dimension of each entry to be ndim + shape = (3,) * ndim + colshape = (10,) + shape + t = table.Table() + t["a"] = np.arange(np.prod(colshape)).reshape(colshape) + + match backend: + # Pandas support multidimensional columns + case "pandas": + if ndim > 1: + df = self._to_dataframe(t, backend, use_legacy_pandas_api) + assert hasattr(df["a"].iloc[0], "__len__") + return + # PyArrow do not support multidimensional columns + case "pyarrow": + if ndim > 1: + with pytest.raises( + ValueError, + match="Cannot convert a table with multidimensional columns", + ): + self._to_dataframe(t, backend, use_legacy_pandas_api) + return + case "dask" | "duckdb": + # Lazy backends will raise ValueError in _to_dataframe + self._to_dataframe(t, backend, use_legacy_pandas_api) + return + case "polars": + df = self._to_dataframe(t, backend, use_legacy_pandas_api) + # Convert and check shape + assert df["a"].dtype.shape == shape + + # Round-trip conversion + t2 = self._from_dataframe(df, backend, use_legacy_pandas_api) + assert t2["a"].shape == colshape + assert_array_equal(t["a"], t2["a"]) + case _: + raise ValueError(f"Unknown backend: {backend}") + + def test_mixin_columns(self, backend, use_legacy_pandas_api): + """Test handling of astropy mixin columns.""" + t = table.QTable() + for name in sorted(MIXIN_COLS): + if not name.startswith("ndarray"): + t[name] = MIXIN_COLS[name] + + t["dt"] = TimeDelta([0, 2, 4, 6], format="sec") + + tp = self._to_dataframe(t, backend, use_legacy_pandas_api) + t2 = self._from_dataframe(tp, backend, use_legacy_pandas_api) + + assert np.allclose(t2["quantity"], [0, 1, 2, 3]) + assert np.allclose(t2["longitude"], [0.0, 1.0, 5.0, 6.0]) + assert np.allclose(t2["latitude"], [5.0, 6.0, 10.0, 11.0]) + assert np.allclose(t2["skycoord.ra"], [0, 1, 2, 3]) + assert np.allclose(t2["skycoord.dec"], [0, 1, 2, 3]) + assert np.allclose(t2["arraywrap"], [0, 1, 2, 3]) + assert np.allclose(t2["arrayswap"], [0, 1, 2, 3]) + assert np.allclose( + t2["earthlocation.y"], [0, 110708, 547501, 654527], rtol=0, atol=1 + ) + + # Time and TimeDelta mixins that round-trip the class + assert type(t2["time"]) is Time + assert np.allclose(t2["time"].jyear, [2000, 2001, 2002, 2003]) + assert np.all( + t2["time"].isot + == [ + "2000-01-01T12:00:00.000", + "2000-12-31T18:00:00.000", + "2002-01-01T00:00:00.000", + "2003-01-01T06:00:00.000", + ] + ) + assert t2["time"].format == "isot" + + # TimeDelta + assert type(t2["dt"]) is TimeDelta + assert np.allclose(t2["dt"].value, [0, 2, 4, 6]) + assert t2["dt"].format == "sec" + + def test_mixin_masked(self, backend, use_legacy_pandas_api): + """Test handling of masked mixin columns.""" + tm = Time([1, 2, 3], format="cxcsec") + dt = TimeDelta([1, 2, 3], format="sec") + tm[1] = np.ma.masked + dt[1] = np.ma.masked + t = table.QTable([tm, dt], names=["tm", "dt"]) + + tp = self._to_dataframe(t, backend, use_legacy_pandas_api) + + match backend: + case "pandas": + tm_nulls = tp["tm"].isnull().to_list() + dt_nulls = tp["dt"].isnull().to_list() + case "polars": + tm_nulls = tp["tm"].is_null().to_list() + dt_nulls = tp["dt"].is_null().to_list() + case "pyarrow": + tm_nulls = tp["tm"].is_null().to_pylist() + dt_nulls = tp["dt"].is_null().to_pylist() + case _: + raise ValueError(f"Unknown backend: {backend}") + + # Common assertion for all backends + expected_nulls = [False, True, False] + assert tm_nulls == expected_nulls + assert dt_nulls == expected_nulls + + # Round-trip conversion + t2 = self._from_dataframe(tp, backend, use_legacy_pandas_api) + + assert_array_equal(t2["tm"].mask, tm.mask) + assert np.ma.allclose(t2["tm"].jd, tm.jd, rtol=1e-14, atol=1e-14) + + assert_array_equal(t2["dt"].mask, dt.mask) + assert np.ma.allclose(t2["dt"].jd, dt.jd, rtol=1e-14, atol=1e-14) + + @pytest.mark.parametrize("use_nullable_int", [True, False]) + def test_masking(self, backend, use_legacy_pandas_api, use_nullable_int): + """Test handling of masked columns.""" + t = table.Table(masked=True) + + t["a"] = [1, 2, 3] + t["a"].mask = [True, False, True] + + t["b"] = [1.0, 2.0, 3.0] + t["b"].mask = [False, False, True] + + t["u"] = ["a", "b", "c"] + t["u"].mask = [False, True, False] + + t["s"] = [b"a", b"b", b"c"] + t["s"].mask = [False, True, False] + + # https://github.com/astropy/astropy/issues/7741 + t["Source"] = [2584290278794471936, 2584290038276303744, 2584288728310999296] + t["Source"].mask = [False, False, False] + + if use_nullable_int: # Default + df = self._to_dataframe( + t, backend, use_legacy_pandas_api, use_nullable_int=use_nullable_int + ) + else: + with pytest.raises( + ValueError, + match="Cannot convert masked integer columns to DataFrame without using nullable integers.", + ): + df = self._to_dataframe( + t, backend, use_legacy_pandas_api, use_nullable_int=use_nullable_int + ) + return + + t2 = self._from_dataframe(df, backend, use_legacy_pandas_api) + for name, column in t.columns.items(): + assert_array_equal(t2[name].data, column.data) + if hasattr(t2[name], "mask"): + assert_array_equal(t2[name].mask, column.mask) + + if column.dtype.kind == "i": + if np.any(column.mask) and not use_nullable_int: + assert t2[name].dtype.kind == "f" + else: + assert t2[name].dtype.kind == "i" + else: + if column.dtype.byteorder in ("=", "|"): + assert t2[name].dtype == column.dtype + else: + assert t2[name].dtype == column.dtype.newbyteorder() + + def test_basic_roundtrip(self, backend, use_legacy_pandas_api): + """Test basic round-trip conversion for different backends.""" + t = table.Table() + t["a"] = [1, 2, 3] + t["b"] = [4.0, 5.0, 6.0] + t["c"] = ["x", "y", "z"] + + # Convert to DataFrame and back + df = self._to_dataframe(t, backend, use_legacy_pandas_api) + t2 = self._from_dataframe(df, backend, use_legacy_pandas_api) + + # Check that data is preserved + assert_allclose(t2["a"], t["a"]) + assert_allclose(t2["b"], t["b"]) + assert_array_equal(t2["c"], t["c"]) + + def test_units_preservation(self, backend, use_legacy_pandas_api): + """Test that units are handled correctly through DataFrame conversion.""" + t = table.QTable() + t["x"] = [1, 2, 3] * u.m + t["y"] = [4.0, 5.0, 6.0] * u.s + + # Test that units are lost in DataFrame conversion (expected behavior) + df = self._to_dataframe(t, backend, use_legacy_pandas_api) + t2 = self._from_dataframe(df, backend, use_legacy_pandas_api) + + # Original table should still have units + assert t["x"].unit == u.m + assert t["y"].unit == u.s + + # Round-trip table should not have units + assert t2["x"].unit is None + assert t2["y"].unit is None + + # But data should be preserved + assert_allclose(t2["x"], t["x"].value) + assert_allclose(t2["y"], t["y"].value) + + def test_masked_int_data(self, backend, use_legacy_pandas_api): + """Test specific masked integer data handling.""" + data = {"data": [0, 1, 2]} + t = table.Table(data=data, masked=True) + t["data"].mask = [1, 1, 0] + + df = self._to_dataframe(t, backend, use_legacy_pandas_api) + + match backend: + case "pandas": + val = df["data"].iloc[2] + nulls_first_two = df["data"].isnull().iloc[:2] + case "polars": + val = df["data"][2] + nulls_first_two = df["data"].is_null()[:2] + case "pyarrow": + val = df["data"][2].as_py() + nulls_first_two = df["data"].is_null()[:2].to_numpy() + case _: + raise ValueError(f"Unknown backend: {backend}") + + assert val == 2 + assert nulls_first_two.all() + + +@pytest.mark.skipif( + not HAS_PANDAS or not HAS_NARWHALS, + reason="requires pandas and narwhals", +) +@pytest.mark.parametrize("method", ["from_df", "from_pandas"]) +def test_from_pandas_df_with_qtable(method): + """Test fix for QTable.from_pandas / from_df returns Table not QTable #18909""" + t = table.QTable() + t["a"] = [1, 2] + t["q"] = [3.0, 4.0] + df = t.to_pandas() + qt = getattr(table.QTable, method)(df) + assert isinstance(qt, table.QTable) + + +@pytest.mark.skipif(not HAS_PANDAS, reason="require pandas") +@pytest.mark.parametrize("use_legacy_pandas_api", [True, False]) +def test_pandas_conversion_multidim_columns(use_legacy_pandas_api): + """Test that Table with multidim columns converts successfully to pandas (#19173). + + This test only uses pandas since other backends do not support multidim columns. + It includes a variety of column types and dimensions (1d to 3d), including + masked columns to verify that all are handled correctly. + """ + if not use_legacy_pandas_api and not HAS_NARWHALS: + pytest.skip("requires narwhals for generic pandas conversion") + + t = Table() + t["a"] = ["foo", "bar"] # 1-d str + t["b"] = [1.5, 2.5] # 1-d float + t["c"] = np.array([[1, 2], [3, 4]]) # 2-d int + t["d"] = np.array([[[1.5, 0], [2, 1]], [[3, 2], [4, 3]]]) # 3-d float + t["e"] = np.array([["a", "b"], ["c", "d"]]) # 2-d str + t["f"] = np.empty((2, 2), dtype=object) + t["f"][:] = [[None, {"a": 1}], ["str", [1, 2]]] # 2-d object type + t["g"] = np.ma.MaskedArray([[1, 2], [3, 4]], mask=[[True, False], [False, True]]) + t["h"] = np.ma.MaskedArray([[1.5, 2], [3, 4]], mask=[[True, False], [False, True]]) + tc = t.copy() + df = t.to_pandas() if use_legacy_pandas_api else t.to_df("pandas") + + # Ensure that conversion process leaves `t` unchanged + assert np.all(t == tc) + + # Check properties of converted dataframe `df`: + # - dtypes are as expected (all object except float64 for column "b"). + # - Dataframe values exactly match table column values via .tolist(). + # - All dataframe Series items for multidim columns are type list. + for name in t.colnames: + assert df[name].dtype == "float64" if name == "b" else "object" + assert df[name].tolist() == t[name].tolist() + if t[name].ndim > 1: + for val in df[name]: + assert type(val) is list + + # Special-case testing for masked column + assert df["g"][0] == [None, 2] + assert df["g"][1] == [3, None] + assert df["h"][0] == [None, 2.0] + assert df["h"][1] == [3.0, None] diff --git a/astropy/table/tests/test_groups.py b/astropy/table/tests/test_groups.py new file mode 100644 index 000000000000..e5ca374a461e --- /dev/null +++ b/astropy/table/tests/test_groups.py @@ -0,0 +1,760 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + + +import numpy as np +import pytest +from hypothesis import given +from hypothesis.extra.numpy import arrays +from hypothesis.strategies import integers + +from astropy import coordinates, time +from astropy import units as u +from astropy.table import Column, NdarrayMixin, QTable, Table, table_helpers, unique +from astropy.time import Time +from astropy.utils.exceptions import AstropyUserWarning + + +def sort_eq(list1, list2): + return sorted(list1) == sorted(list2) + + +def test_column_group_by(T1q): + """Test grouping a Column by various key types.""" + # T1q["a"] could be Column or Quantity, so force the object we want to group to be + # Column. Then later we are using the "a" column as a grouping key. + t1a = Column(T1q["a"]) + unit = T1q["a"].unit or 1 + + # Group by a Column (i.e. numpy array) + t1ag = t1a.group_by(T1q["a"]) + keys = t1ag.groups.keys + assert np.all(t1ag.groups.indices == np.array([0, 1, 4, 8])) + assert np.all(keys == np.array([0, 1, 2]) * unit) + + # Group by a Table and numpy structured array + for t1ag, key_unit in ( + (t1a.group_by(T1q["a", "b"]), unit), + (t1a.group_by(T1q["a", "b"].as_array()), 1), + ): + assert np.all(t1ag.groups.indices == np.array([0, 1, 3, 4, 5, 7, 8])) + keys = t1ag.groups.keys + assert keys.dtype.names == ("a", "b") + assert np.all(keys["a"] == np.array([0, 1, 1, 2, 2, 2]) * key_unit) + assert np.all(keys["b"] == np.array(["a", "a", "b", "a", "b", "c"])) + + +def test_column_group_by_no_argsort(T1b): + t1a = T1b["a"] + with pytest.raises( + TypeError, match=r"keys input \(list\) must have an `argsort` method" + ): + # Pass a Python list with no argsort method + t1a.group_by(list(range(len(t1a)))) + + +def test_table_group_by(T1): + """ + Test basic table group_by functionality for possible key types and for + masked/unmasked tables. + """ + for masked in (False, True): + t1 = QTable(T1, masked=masked) + # Group by a single column key specified by name + tg = t1.group_by("a") + assert np.all(tg.groups.indices == np.array([0, 1, 4, 8])) + assert str(tg.groups) == "" + assert str(tg["a"].groups) == "" + + # Sorted by 'a' and in original order for rest + assert tg.pformat() == [ + " a b c d q ", + " m ", + "--- --- --- --- ---", + " 0 a 0.0 4 4.0", + " 1 b 3.0 5 5.0", + " 1 a 2.0 6 6.0", + " 1 a 1.0 7 7.0", + " 2 c 7.0 0 0.0", + " 2 b 5.0 1 1.0", + " 2 b 6.0 2 2.0", + " 2 a 4.0 3 3.0", + ] + assert tg.meta["ta"] == 1 + assert tg["c"].meta["a"] == 1 + assert tg["c"].description == "column c" + + # Group by a table column + tg2 = t1.group_by(t1["a"]) + assert tg.pformat() == tg2.pformat() + + # Group by two columns spec'd by name + for keys in (["a", "b"], ("a", "b")): + tg = t1.group_by(keys) + assert np.all(tg.groups.indices == np.array([0, 1, 3, 4, 5, 7, 8])) + # Sorted by 'a', 'b' and in original order for rest + assert tg.pformat() == [ + " a b c d q ", + " m ", + "--- --- --- --- ---", + " 0 a 0.0 4 4.0", + " 1 a 2.0 6 6.0", + " 1 a 1.0 7 7.0", + " 1 b 3.0 5 5.0", + " 2 a 4.0 3 3.0", + " 2 b 5.0 1 1.0", + " 2 b 6.0 2 2.0", + " 2 c 7.0 0 0.0", + ] + + # Group by a Table + tg2 = t1.group_by(t1["a", "b"]) + assert tg.pformat() == tg2.pformat() + + # Group by a structured array + tg2 = t1.group_by(t1["a", "b"].as_array()) + assert tg.pformat() == tg2.pformat() + + # Group by a simple ndarray + tg = t1.group_by(np.array([0, 1, 0, 1, 2, 1, 0, 0])) + assert np.all(tg.groups.indices == np.array([0, 4, 7, 8])) + assert tg.pformat() == [ + " a b c d q ", + " m ", + "--- --- --- --- ---", + " 2 c 7.0 0 0.0", + " 2 b 6.0 2 2.0", + " 1 a 2.0 6 6.0", + " 1 a 1.0 7 7.0", + " 2 b 5.0 1 1.0", + " 2 a 4.0 3 3.0", + " 1 b 3.0 5 5.0", + " 0 a 0.0 4 4.0", + ] + + +def test_groups_keys(T1m: QTable): + tg = T1m.group_by("a") + unit = T1m["a"].unit or 1 + keys = tg.groups.keys + assert keys.dtype.names == ("a",) + assert np.all(keys["a"] == np.array([0, 1, 2]) * unit) + + tg = T1m.group_by(["a", "b"]) + keys = tg.groups.keys + assert keys.dtype.names == ("a", "b") + assert np.all(keys["a"] == np.array([0, 1, 1, 2, 2, 2]) * unit) + assert np.all(keys["b"] == np.array(["a", "a", "b", "a", "b", "c"])) + + # Grouping by Column ignores column name + tg = T1m.group_by(T1m["b"]) + keys = tg.groups.keys + assert keys.dtype.names is None + + +def test_groups_keys_time(T1b: QTable): + """Group a table with a time column using that column as a key.""" + T1b = T1b.copy() + T1b["a"] = Time(T1b["a"], format="cxcsec") + + tg = T1b.group_by("a") + keys = tg.groups.keys + assert keys.dtype.names == ("a",) + assert np.all(keys["a"] == Time(np.array([0, 1, 2]), format="cxcsec")) + + tg = T1b.group_by(["a", "b"]) + keys = tg.groups.keys + assert keys.dtype.names == ("a", "b") + assert np.all(keys["a"] == Time(np.array([0, 1, 1, 2, 2, 2]), format="cxcsec")) + assert np.all(keys["b"] == np.array(["a", "a", "b", "a", "b", "c"])) + + +def test_groups_iterator(T1): + tg = T1.group_by("a") + for ii, group in enumerate(tg.groups): + assert group.pformat() == tg.groups[ii].pformat() + assert group["a"][0] == tg["a"][tg.groups.indices[ii]] + + +def test_grouped_copy(T1): + """ + Test that copying a table or column copies the groups properly + """ + for masked in (False, True): + t1 = QTable(T1, masked=masked) + tg = t1.group_by("a") + tgc = tg.copy() + assert np.all(tgc.groups.indices == tg.groups.indices) + assert np.all(tgc.groups.keys == tg.groups.keys) + + tac = tg["a"].copy() + assert np.all(tac.groups.indices == tg["a"].groups.indices) + + c1 = t1["a"].copy() + gc1 = c1.group_by(t1["a"]) + gc1c = gc1.copy() + assert np.all(gc1c.groups.indices == np.array([0, 1, 4, 8])) + + +def test_grouped_slicing(T1): + """ + Test that slicing a table removes previous grouping + """ + + for masked in (False, True): + t1 = QTable(T1, masked=masked) + + # Regular slice of a table + tg = t1.group_by("a") + tg2 = tg[3:5] + assert np.all(tg2.groups.indices == np.array([0, len(tg2)])) + assert tg2.groups.keys is None + + +def test_group_column_from_table(T1): + """ + Group a column that is part of a table + """ + cg = T1["c"].group_by(np.array(T1["a"])) + assert np.all(cg.groups.keys == np.array([0, 1, 2])) + assert np.all(cg.groups.indices == np.array([0, 1, 4, 8])) + + +def test_table_groups_mask_index(T1): + """ + Use boolean mask as item in __getitem__ for groups + """ + for masked in (False, True): + t1 = Table(T1, masked=masked).group_by("a") + + t2 = t1.groups[np.array([True, False, True])] + assert len(t2.groups) == 2 + assert t2.groups[0].pformat() == t1.groups[0].pformat() + assert t2.groups[1].pformat() == t1.groups[2].pformat() + assert np.all(t2.groups.keys["a"] == np.array([0, 2])) + + +def test_table_groups_array_index(T1): + """ + Use numpy array as item in __getitem__ for groups + """ + for masked in (False, True): + t1 = Table(T1, masked=masked).group_by("a") + + t2 = t1.groups[np.array([0, 2])] + assert len(t2.groups) == 2 + assert t2.groups[0].pformat() == t1.groups[0].pformat() + assert t2.groups[1].pformat() == t1.groups[2].pformat() + assert np.all(t2.groups.keys["a"] == np.array([0, 2])) + + +def test_table_groups_slicing(T1): + """ + Test that slicing table groups works + """ + + for masked in (False, True): + t1 = Table(T1, masked=masked).group_by("a") + + # slice(0, 2) + t2 = t1.groups[0:2] + assert len(t2.groups) == 2 + assert t2.groups[0].pformat() == t1.groups[0].pformat() + assert t2.groups[1].pformat() == t1.groups[1].pformat() + assert np.all(t2.groups.keys["a"] == np.array([0, 1])) + + # slice(1, 2) + t2 = t1.groups[1:2] + assert len(t2.groups) == 1 + assert t2.groups[0].pformat() == t1.groups[1].pformat() + assert np.all(t2.groups.keys["a"] == np.array([1])) + + # slice(0, 3, 2) + t2 = t1.groups[0:3:2] + assert len(t2.groups) == 2 + assert t2.groups[0].pformat() == t1.groups[0].pformat() + assert t2.groups[1].pformat() == t1.groups[2].pformat() + assert np.all(t2.groups.keys["a"] == np.array([0, 2])) + + +def test_grouped_item_access(T1): + """ + Test that column slicing preserves grouping + """ + for masked in (False, True): + t1 = Table(T1, masked=masked) + + # Regular slice of a table + tg = t1.group_by("a") + tgs = tg["a", "c", "d"] + assert np.all(tgs.groups.keys == tg.groups.keys) + assert np.all(tgs.groups.indices == tg.groups.indices) + tgsa = tgs.groups.aggregate(np.sum) + assert tgsa.pformat() == [ + " a c d ", + "--- ---- ---", + " 0 0.0 4", + " 1 6.0 18", + " 2 22.0 6", + ] + + tgs = tg["c", "d"] + assert np.all(tgs.groups.keys == tg.groups.keys) + assert np.all(tgs.groups.indices == tg.groups.indices) + tgsa = tgs.groups.aggregate(np.sum) + assert tgsa.pformat() == [ + " c d ", + "---- ---", + " 0.0 4", + " 6.0 18", + "22.0 6", + ] + + +def test_mutable_operations(T1): + """ + Operations like adding or deleting a row should removing grouping, + but adding or removing or renaming a column should retain grouping. + """ + for masked in (False, True): + t1 = QTable(T1, masked=masked) + + # add row + tg = t1.group_by("a") + tg.add_row((0, "a", 3.0, 4, 4 * u.m)) + assert np.all(tg.groups.indices == np.array([0, len(tg)])) + assert tg.groups.keys is None + + # remove row + tg = t1.group_by("a") + tg.remove_row(4) + assert np.all(tg.groups.indices == np.array([0, len(tg)])) + assert tg.groups.keys is None + + # add column + tg = t1.group_by("a") + indices = tg.groups.indices.copy() + tg.add_column(Column(name="e", data=np.arange(len(tg)))) + assert np.all(tg.groups.indices == indices) + assert np.all(tg["e"].groups.indices == indices) + assert np.all(tg["e"].groups.keys == tg.groups.keys) + + # remove column (not key column) + tg = t1.group_by("a") + tg.remove_column("b") + assert np.all(tg.groups.indices == indices) + # Still has original key col names + assert tg.groups.keys.dtype.names == ("a",) + assert np.all(tg["a"].groups.indices == indices) + + # remove key column + tg = t1.group_by("a") + tg.remove_column("a") + assert np.all(tg.groups.indices == indices) + assert tg.groups.keys.dtype.names == ("a",) + assert np.all(tg["b"].groups.indices == indices) + + # rename key column + tg = t1.group_by("a") + tg.rename_column("a", "aa") + assert np.all(tg.groups.indices == indices) + assert tg.groups.keys.dtype.names == ("a",) + assert np.all(tg["aa"].groups.indices == indices) + + +def test_group_by_masked(T1): + t1m = QTable(T1, masked=True) + t1m["c"].mask[4] = True + t1m["d"].mask[5] = True + assert t1m.group_by("a").pformat() == [ + " a b c d q ", + " m ", + "--- --- --- --- ---", + " 0 a -- 4 4.0", + " 1 b 3.0 -- 5.0", + " 1 a 2.0 6 6.0", + " 1 a 1.0 7 7.0", + " 2 c 7.0 0 0.0", + " 2 b 5.0 1 1.0", + " 2 b 6.0 2 2.0", + " 2 a 4.0 3 3.0", + ] + + +def test_group_by_errors(T1): + """ + Appropriate errors get raised. + """ + # Bad column name as string + with pytest.raises(ValueError): + T1.group_by("f") + + # Bad column names in list + with pytest.raises(ValueError): + T1.group_by(["f", "g"]) + + # Wrong length array + with pytest.raises(ValueError): + T1.group_by(np.array([1, 2])) + + # Wrong type + with pytest.raises(TypeError): + T1.group_by(None) + + # Masked key column + t1 = QTable(T1, masked=True) + t1["a"].mask[4] = True + with pytest.raises(ValueError): + t1.group_by("a") + + +def test_groups_keys_meta(T1): + """ + Make sure the keys meta['grouped_by_table_cols'] is working. + """ + # Group by column in this table + tg = T1.group_by("a") + assert tg.groups.keys.meta["grouped_by_table_cols"] is True + assert tg["c"].groups.keys.meta["grouped_by_table_cols"] is True + assert tg.groups[1].groups.keys.meta["grouped_by_table_cols"] is True + assert ( + tg["d"] + .groups[np.array([False, True, True])] + .groups.keys.meta["grouped_by_table_cols"] + is True + ) + + # Group by external Table + tg = T1.group_by(T1["a", "b"]) + assert tg.groups.keys.meta["grouped_by_table_cols"] is False + assert tg["c"].groups.keys.meta["grouped_by_table_cols"] is False + assert tg.groups[1].groups.keys.meta["grouped_by_table_cols"] is False + + # Group by external numpy array + tg = T1.group_by(T1["a", "b"].as_array()) + assert not hasattr(tg.groups.keys, "meta") + assert not hasattr(tg["c"].groups.keys, "meta") + + # Group by Column + tg = T1.group_by(T1["a"]) + assert "grouped_by_table_cols" not in tg.groups.keys.meta + assert "grouped_by_table_cols" not in tg["c"].groups.keys.meta + + +def test_table_aggregate(T1): + """ + Aggregate a table + """ + # Table with only summable cols + t1 = T1["a", "c", "d"] + tg = t1.group_by("a") + tga = tg.groups.aggregate(np.sum) + assert tga.pformat() == [ + " a c d ", + "--- ---- ---", + " 0 0.0 4", + " 1 6.0 18", + " 2 22.0 6", + ] + # Reverts to default groups + assert np.all(tga.groups.indices == np.array([0, 3])) + assert tga.groups.keys is None + + # metadata survives + assert tga.meta["ta"] == 1 + assert tga["c"].meta["a"] == 1 + assert tga["c"].description == "column c" + + # Aggregate with np.sum with masked elements. This results + # in one group with no elements, hence a nan result and conversion + # to float for the 'd' column. + t1m = QTable(T1, masked=True) + t1m["c"].mask[4:6] = True + t1m["d"].mask[4:6] = True + t1m["q"].mask[4:6] = True + tg = t1m.group_by("a") + + with ( + pytest.warns(UserWarning, match="converting a masked element to nan"), + pytest.warns(AstropyUserWarning, match="Cannot aggregate column"), + ): + tga = tg.groups.aggregate(np.sum) + + assert tga.pformat() == [ + " a c d q ", + " m ", + "--- ---- ---- ----", + " 0 -- -- ———", + " 1 3.0 13.0 ———", + " 2 22.0 6.0 6.0", + ] + + # Aggregate with np.sum with masked elements, but where every + # group has at least one remaining (unmasked) element. Then + # the int column stays as an int. + t1m = QTable(t1, masked=True) + t1m["c"].mask[5] = True + t1m["d"].mask[5] = True + tg = t1m.group_by("a") + tga = tg.groups.aggregate(np.sum) + assert tga.pformat() == [ + " a c d ", + "--- ---- ---", + " 0 0.0 4", + " 1 3.0 13", + " 2 22.0 6", + ] + + # Aggregate with a column type that cannot by supplied to the aggregating + # function. This raises a warning but still works. + tg = T1.group_by("a") + with pytest.warns(AstropyUserWarning, match="Cannot aggregate column"): + tga = tg.groups.aggregate(np.sum) + assert tga.pformat() == [ + " a c d q ", + " m ", + "--- ---- --- ----", + " 0 0.0 4 4.0", + " 1 6.0 18 18.0", + " 2 22.0 6 6.0", + ] + + +def test_table_aggregate_reduceat(T1): + """ + Aggregate table with functions which have a reduceat method + """ + + # Comparison functions without reduceat + def np_mean(x): + return np.mean(x) + + def np_sum(x): + return np.sum(x) + + def np_add(x): + return np.add(x) + + # Table with only summable cols + t1 = T1["a", "c", "d"] + tg = t1.group_by("a") + # Comparison + tga_r = tg.groups.aggregate(np.sum) + tga_a = tg.groups.aggregate(np.add) + tga_n = tg.groups.aggregate(np_sum) + + assert np.all(tga_r == tga_n) + assert np.all(tga_a == tga_n) + assert tga_n.pformat() == [ + " a c d ", + "--- ---- ---", + " 0 0.0 4", + " 1 6.0 18", + " 2 22.0 6", + ] + + tga_r = tg.groups.aggregate(np.mean) + tga_n = tg.groups.aggregate(np_mean) + assert np.all(tga_r == tga_n) + assert tga_n.pformat() == [ + " a c d ", + "--- --- ---", + " 0 0.0 4.0", + " 1 2.0 6.0", + " 2 5.5 1.5", + ] + + # Binary ufunc np_add should raise warning without reduceat + t2 = T1["a", "c"] + tg = t2.group_by("a") + + with pytest.warns(AstropyUserWarning, match="Cannot aggregate column"): + tga = tg.groups.aggregate(np_add) + assert tga.pformat() == [" a ", "---", " 0", " 1", " 2"] + + +def test_table_aggregate_reduceat_empty(): + for masked in (False, True): + tg = Table( + { + "action": np.asarray([], dtype=str), + "duration": np.asarray([], dtype=float), + }, + masked=masked, + ) + tga = tg.group_by("action").groups.aggregate(np.sum) + assert tga.pformat() == ["action duration", "------ --------"] + + +def test_column_aggregate(T1): + """ + Aggregate a single table column + """ + for masked in (False, True): + tg = QTable(T1, masked=masked).group_by("a") + tga = tg["c"].groups.aggregate(np.sum) + assert tga.pformat() == [" c ", "----", " 0.0", " 6.0", "22.0"] + + +def test_column_aggregate_f8(): + """https://github.com/astropy/astropy/issues/12706""" + # Just want to make sure it does not crash again. + for masked in (False, True): + tg = Table({"a": np.arange(2, dtype=">f8")}, masked=masked).group_by("a") + tga = tg["a"].groups.aggregate(np.sum) + assert tga.pformat() == [" a ", "---", "0.0", "1.0"] + + +def test_table_filter(): + """ + Table groups filtering + """ + + def all_positive(table, key_colnames): + return all( + np.all(table[colname] >= 0) + for colname in table.colnames + if colname not in key_colnames + ) + + # Negative value in 'a' column should not filter because it is a key col + t = Table.read( + [ + " a c d", + " -2 7.0 0", + " -2 5.0 1", + " 0 0.0 4", + " 1 3.0 5", + " 1 2.0 -6", + " 1 1.0 7", + " 3 3.0 5", + " 3 -2.0 6", + " 3 1.0 7", + ], + format="ascii", + ) + tg = t.group_by("a") + t2 = tg.groups.filter(all_positive) + assert t2.groups[0].pformat() == [ + " a c d ", + "--- --- ---", + " -2 7.0 0", + " -2 5.0 1", + ] + assert t2.groups[1].pformat() == [" a c d ", "--- --- ---", " 0 0.0 4"] + + +def test_column_filter(): + """ + Table groups filtering + """ + + # Negative value in 'a' column should not filter because it is a key col + t = Table.read( + [ + " a c d", + " -2 7.0 0", + " -2 5.0 1", + " 0 0.0 4", + " 1 3.0 5", + " 1 2.0 -6", + " 1 1.0 7", + " 3 3.0 5", + " 3 -2.0 6", + " 3 1.0 7", + ], + format="ascii", + ) + tg = t.group_by("a") + c2 = tg["c"].groups.filter(lambda column: np.all(column >= 0)) + assert len(c2.groups) == 3 + assert c2.groups[0].pformat() == [" c ", "---", "7.0", "5.0"] + assert c2.groups[1].pformat() == [" c ", "---", "0.0"] + assert c2.groups[2].pformat() == [" c ", "---", "3.0", "2.0", "1.0"] + + +def test_group_mixins(): + """ + Test grouping a table with mixin columns + """ + # Setup mixins + idx = np.arange(4) + x = np.array([3.0, 1.0, 2.0, 1.0]) + q = x * u.m + lon = coordinates.Longitude(x * u.deg) + lat = coordinates.Latitude(x * u.deg) + # For Time do J2000.0 + few * 0.1 ns (this requires > 64 bit precision) + tm = time.Time(2000, format="jyear") + time.TimeDelta(x * 1e-10, format="sec") + sc = coordinates.SkyCoord(ra=lon, dec=lat) + aw = table_helpers.ArrayWrapper(x) + nd = np.array([(3, "c"), (1, "a"), (2, "b"), (1, "a")], dtype=" None: + # Check table colnames equal and values equal + vals_eq = t1.values_equal(t2) # this raises if colnames not equal + for col_eq in vals_eq.itercols(): + npt.assert_equal(col_eq, True) + + assert t1.meta == t2.meta + + +@pytest.mark.usefixtures("table_types") +class TestIndex(SetupData): + def _setup(self, main_col, table_types): + super()._setup(table_types) + self.main_col = main_col + if isinstance(main_col, u.Quantity): + self._table_type = QTable + if not isinstance(main_col, list): + self._column_type = lambda x: x # don't change mixin type + self.mutable = isinstance(main_col, (list, u.Quantity)) + + def make_col(self, name, lst): + return self._column_type(lst, name=name) + + def make_val(self, val): + if isinstance(self.main_col, Time): + return Time(val, format="jyear") + return val + + @property + def t(self): + if not hasattr(self, "_t"): + # Note that order of columns is important, and the 'a' column is + # last to ensure that the index column does not need to be the first + # column (as was discovered in #10025). Most testing uses 'a' and + # ('a', 'b') for the columns. + self._t = self._table_type() + self._t["b"] = self._column_type([4.0, 5.1, 6.2, 7.0, 1.1]) + self._t["c"] = self._column_type(["7", "8", "9", "10", "11"]) + self._t["a"] = self._column_type(self.main_col) + return self._t + + @pytest.mark.parametrize("composite", [False, True]) + def test_table_index(self, main_col, table_types, composite, engine): + self._setup(main_col, table_types) + t = self.t + t.add_index(("a", "b") if composite else "a", engine=engine) + assert np.all(t.indices[0].sorted_data() == [0, 1, 2, 3, 4]) + + if not self.mutable: + return + + # test altering table columns + t["a"][0] = 4 + t.add_row((6.0, "7", 6)) + t["a"][3] = 10 + t.remove_row(2) + t.add_row((5.0, "9", 4)) + + assert_col_equal(t["a"], np.array([4, 2, 10, 5, 6, 4])) + assert np.allclose(t["b"], np.array([4.0, 5.1, 7.0, 1.1, 6.0, 5.0])) + assert np.all(t["c"].data == np.array(["7", "8", "10", "11", "7", "9"])) + index = t.indices[0] + ll = list(index.data.items()) + + if composite: + assert np.all( + ll + == [ + ((2, 5.1), [1]), + ((4, 4.0), [0]), + ((4, 5.0), [5]), + ((5, 1.1), [3]), + ((6, 6.0), [4]), + ((10, 7.0), [2]), + ] + ) + else: + assert np.all( + ll + == [((2,), [1]), ((4,), [0, 5]), ((5,), [3]), ((6,), [4]), ((10,), [2])] + ) + t.remove_indices("a") + assert len(t.indices) == 0 + + def test_table_slicing(self, main_col, table_types, engine): + self._setup(main_col, table_types) + t = self.t + t.add_index("a", engine=engine) + assert np.all(t.indices[0].sorted_data() == [0, 1, 2, 3, 4]) + + for slice_ in ([0, 2], np.array([0, 2])): + t2 = t[slice_] + # t2 should retain an index on column 'a' + assert len(t2.indices) == 1 + assert_col_equal(t2["a"], [1, 3]) + + # the index in t2 should reorder row numbers after slicing + assert np.all(t2.indices[0].sorted_data() == [0, 1]) + # however, this index should be a deep copy of t1's index + assert np.all(t.indices[0].sorted_data() == [0, 1, 2, 3, 4]) + + def test_remove_rows(self, main_col, table_types, engine): + self._setup(main_col, table_types) + if not self.mutable: + return + t = self.t + t.add_index("a", engine=engine) + + # remove individual row + t2 = t.copy() + t2.remove_rows(2) + assert_col_equal(t2["a"], [1, 2, 4, 5]) + assert np.all(t2.indices[0].sorted_data() == [0, 1, 2, 3]) + + # remove by list, ndarray, or slice + for cut in ([0, 2, 4], np.array([0, 2, 4]), slice(0, 5, 2)): + t2 = t.copy() + t2.remove_rows(cut) + assert_col_equal(t2["a"], [2, 4]) + assert np.all(t2.indices[0].sorted_data() == [0, 1]) + + with pytest.raises(ValueError): + t.remove_rows((0, 2, 4)) + + def test_col_get_slice(self, main_col, table_types, engine): + self._setup(main_col, table_types) + t = self.t + t.add_index("a", engine=engine) + + # get slice + t2 = t[1:3] # table slice + assert_col_equal(t2["a"], [2, 3]) + assert np.all(t2.indices[0].sorted_data() == [0, 1]) + + col_slice = t["a"][1:3] + assert_col_equal(col_slice, [2, 3]) + # true column slices discard indices + if isinstance(t["a"], BaseColumn): + assert len(col_slice.info.indices) == 0 + + # take slice of slice + t2 = t[::2] + assert_col_equal(t2["a"], np.array([1, 3, 5])) + t3 = t2[::-1] + assert_col_equal(t3["a"], np.array([5, 3, 1])) + assert np.all(t3.indices[0].sorted_data() == [2, 1, 0]) + t3 = t2[:2] + assert_col_equal(t3["a"], np.array([1, 3])) + assert np.all(t3.indices[0].sorted_data() == [0, 1]) + # out-of-bound slices + for t_empty in (t2[3:], t2[2:1], t3[2:]): + assert len(t_empty["a"]) == 0 + assert np.all(t_empty.indices[0].sorted_data() == []) + + if self.mutable: + # get boolean mask + mask = t["a"] % 2 == 1 + t2 = t[mask] + assert_col_equal(t2["a"], [1, 3, 5]) + assert np.all(t2.indices[0].sorted_data() == [0, 1, 2]) + + def test_col_set_slice(self, main_col, table_types, engine): + self._setup(main_col, table_types) + if not self.mutable: + return + t = self.t + t.add_index("a", engine=engine) + + # set slice + t2 = t.copy() + t2["a"][1:3] = np.array([6, 7]) + assert_col_equal(t2["a"], np.array([1, 6, 7, 4, 5])) + assert np.all(t2.indices[0].sorted_data() == [0, 3, 4, 1, 2]) + + # change original table via slice reference + t2 = t.copy() + t3 = t2[1:3] + assert_col_equal(t3["a"], np.array([2, 3])) + assert np.all(t3.indices[0].sorted_data() == [0, 1]) + t3["a"][0] = 5 + assert_col_equal(t3["a"], np.array([5, 3])) + assert_col_equal(t2["a"], np.array([1, 5, 3, 4, 5])) + assert np.all(t3.indices[0].sorted_data() == [1, 0]) + assert np.all(t2.indices[0].sorted_data() == [0, 2, 3, 1, 4]) + + # set boolean mask + t2 = t.copy() + mask = t["a"] % 2 == 1 + t2["a"][mask] = 0.0 + assert_col_equal(t2["a"], [0, 2, 0, 4, 0]) + assert np.all(t2.indices[0].sorted_data() == [0, 2, 4, 1, 3]) + + def test_multiple_slices(self, main_col, table_types, engine): + self._setup(main_col, table_types) + + if not self.mutable: + return + + t = self.t + t.add_index("a", engine=engine) + + for i in range(6, 51): + t.add_row((1.0, "A", i)) + + assert_col_equal(t["a"], list(range(1, 51))) + assert np.all(t.indices[0].sorted_data() == list(range(50))) + + evens = t[::2] + assert np.all(evens.indices[0].sorted_data() == list(range(25))) + reverse = evens[::-1] + index = reverse.indices[0] + assert (index.start, index.stop, index.step) == (48, -2, -2) + assert np.all(index.sorted_data() == list(range(24, -1, -1))) + + # modify slice of slice + reverse[-10:] = 0 + expected = np.array(list(range(1, 51))) + expected[:20][expected[:20] % 2 == 1] = 0 + assert_col_equal(t["a"], expected) + assert_col_equal(evens["a"], expected[::2]) + assert_col_equal(reverse["a"], expected[::2][::-1]) + # first ten evens are now zero + assert np.all( + t.indices[0].sorted_data() + == ( + [0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 1, 3, 5, 7, 9, 11, 13, 15, 17, 19] + + list(range(20, 50)) + ) + ) + assert np.all(evens.indices[0].sorted_data() == list(range(25))) + assert np.all(reverse.indices[0].sorted_data() == list(range(24, -1, -1))) + + # try different step sizes of slice + t2 = t[1:20:2] + assert_col_equal(t2["a"], [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]) + assert np.all(t2.indices[0].sorted_data() == list(range(10))) + t3 = t2[::3] + assert_col_equal(t3["a"], [2, 8, 14, 20]) + assert np.all(t3.indices[0].sorted_data() == [0, 1, 2, 3]) + t4 = t3[2::-1] + assert_col_equal(t4["a"], [14, 8, 2]) + assert np.all(t4.indices[0].sorted_data() == [2, 1, 0]) + + def test_sort(self, main_col, table_types, engine): + self._setup(main_col, table_types) + t = self.t[::-1] # reverse table + assert_col_equal(t["a"], [5, 4, 3, 2, 1]) + t.add_index("a", engine=engine) + assert np.all(t.indices[0].sorted_data() == [4, 3, 2, 1, 0]) + + if not self.mutable: + return + + # sort table by column a + t2 = t.copy() + t2.sort("a") + assert_col_equal(t2["a"], [1, 2, 3, 4, 5]) + assert np.all(t2.indices[0].sorted_data() == [0, 1, 2, 3, 4]) + + # sort table by primary key + t2 = t.copy() + t2.sort() + assert_col_equal(t2["a"], [1, 2, 3, 4, 5]) + assert np.all(t2.indices[0].sorted_data() == [0, 1, 2, 3, 4]) + + def test_insert_row(self, main_col, table_types, engine): + self._setup(main_col, table_types) + + if not self.mutable: + return + + t = self.t + t.add_index("a", engine=engine) + t.insert_row(2, (1.0, "12", 6)) + assert_col_equal(t["a"], [1, 2, 6, 3, 4, 5]) + assert np.all(t.indices[0].sorted_data() == [0, 1, 3, 4, 5, 2]) + t.insert_row(1, (4.0, "13", 0)) + assert_col_equal(t["a"], [1, 0, 2, 6, 3, 4, 5]) + assert np.all(t.indices[0].sorted_data() == [1, 0, 2, 4, 5, 6, 3]) + + def test_index_modes(self, main_col, table_types, engine): + self._setup(main_col, table_types) + t = self.t + t.add_index("a", engine=engine) + + # first, no special mode + assert len(t[[1, 3]].indices) == 1 + assert len(t[::-1].indices) == 1 + assert len(self._table_type(t).indices) == 1 + assert np.all(t.indices[0].sorted_data() == [0, 1, 2, 3, 4]) + t2 = t.copy() + + # non-copy mode + with t.index_mode("discard_on_copy"): + assert len(t[[1, 3]].indices) == 0 + assert len(t[::-1].indices) == 0 + assert len(self._table_type(t).indices) == 0 + assert len(t2.copy().indices) == 1 # mode should only affect t + + # make sure non-copy mode is exited correctly + assert len(t[[1, 3]].indices) == 1 + + if not self.mutable: + return + + # non-modify mode + with t.index_mode("freeze"): + assert np.all(t.indices[0].sorted_data() == [0, 1, 2, 3, 4]) + t["a"][0] = 6 + assert np.all(t.indices[0].sorted_data() == [0, 1, 2, 3, 4]) + t.add_row((1.5, "12", 2)) + assert np.all(t.indices[0].sorted_data() == [0, 1, 2, 3, 4]) + t.remove_rows([1, 3]) + assert np.all(t.indices[0].sorted_data() == [0, 1, 2, 3, 4]) + assert_col_equal(t["a"], [6, 3, 5, 2]) + # mode should only affect t + assert np.all(t2.indices[0].sorted_data() == [0, 1, 2, 3, 4]) + t2["a"][0] = 6 + assert np.all(t2.indices[0].sorted_data() == [1, 2, 3, 4, 0]) + + # make sure non-modify mode is exited correctly + assert np.all(t.indices[0].sorted_data() == [3, 1, 2, 0]) + + if isinstance(t["a"], BaseColumn): + assert len(t["a"][::-1].info.indices) == 0 + with t.index_mode("copy_on_getitem"): + assert len(t["a"][[1, 2]].info.indices) == 1 + # mode should only affect t + assert len(t2["a"][[1, 2]].info.indices) == 0 + + assert len(t["a"][::-1].info.indices) == 0 + assert len(t2["a"][::-1].info.indices) == 0 + + def test_index_retrieval(self, main_col, table_types, engine): + self._setup(main_col, table_types) + t = self.t + t.add_index("a", engine=engine) + t.add_index(["a", "c"], engine=engine) + assert len(t.indices) == 2 + assert len(t.indices["a"].columns) == 1 + assert len(t.indices["a", "c"].columns) == 2 + + with pytest.raises(IndexError): + t.indices["b"] + + def test_col_rename(self, main_col, table_types, engine): + """ + Checks for a previous bug in which copying a Table + with different column names raised an exception. + """ + self._setup(main_col, table_types) + t = self.t + t.add_index("a", engine=engine) + t2 = self._table_type(self.t, names=["d", "e", "f"]) + assert len(t2.indices) == 1 + + def test_table_loc(self, main_col, table_types, engine): + self._setup(main_col, table_types) + t = self.t + + t.add_index("a", engine=engine) + t.add_index("b", engine=engine) + + t2 = t.loc[self.make_val(3)] # single label, with primary key 'a' + assert_col_equal(t2["a"], [3]) + assert isinstance(t2, Row) + + # list search + t2 = t.loc[[self.make_val(1), self.make_val(4), self.make_val(2)]] + assert_col_equal(t2["a"], [1, 4, 2]) # same order as input list + if not isinstance(main_col, Time): + # ndarray search + t2 = t.loc[np.array([1, 4, 2])] + assert_col_equal(t2["a"], [1, 4, 2]) + assert_col_equal(t2["a"], [1, 4, 2]) + t2 = t.loc[self.make_val(3) : self.make_val(5)] # range search + assert_col_equal(t2["a"], [3, 4, 5]) + t2 = t.loc.with_index("b")[5.0:7.0] + assert_col_equal(t2["b"], [5.1, 6.2, 7.0]) + # search by sorted index + t2 = t.iloc[0:2] # two smallest rows by column 'a' + assert_col_equal(t2["a"], [1, 2]) + t2 = t.iloc.with_index("b")[2:] # exclude two smallest rows in column 'b' + assert_col_equal(t2["b"], [5.1, 6.2, 7.0]) + + for t2 in (t.loc[:], t.iloc[:]): + assert_col_equal(t2["a"], [1, 2, 3, 4, 5]) + + def test_table_loc_indices(self, main_col, table_types, engine): + self._setup(main_col, table_types) + t = self.t + + t.add_index("a", engine=engine) + t.add_index("b", engine=engine) + + t2 = t.loc_indices[self.make_val(3)] # single label, with primary key 'a' + assert t2 == 2 + + # list search + t2 = t.loc_indices[[self.make_val(1), self.make_val(4), self.make_val(2)]] + for i, p in zip(t2, [1, 4, 2]): # same order as input list + assert i == p - 1 + + def test_invalid_search(self, main_col, table_types, engine): + # using .loc and .loc_indices with a value not present should raise an exception + self._setup(main_col, table_types) + t = self.t + + t.add_index("a") + with pytest.raises(KeyError): + t.loc[self.make_val(6)] + with pytest.raises(KeyError): + t.loc_indices[self.make_val(6)] + + def test_copy_index_references(self, main_col, table_types, engine): + # check against a bug in which indices were given an incorrect + # column reference when copied + self._setup(main_col, table_types) + t = self.t + + t.add_index("a") + t.add_index("b") + t2 = t.copy() + assert t2.indices["a"].columns[0] is t2["a"] + assert t2.indices["b"].columns[0] is t2["b"] + + def test_unique_index(self, main_col, table_types, engine): + self._setup(main_col, table_types) + t = self.t + + t.add_index("a", engine=engine, unique=True) + assert np.all(t.indices["a"].sorted_data() == [0, 1, 2, 3, 4]) + + if self.mutable: + with pytest.raises(ValueError): + t.add_row((5.0, "9", 5)) + + def test_copy_indexed_table(self, table_types): + self._setup(_col, table_types) + t = self.t + t.add_index("a") + t.add_index(["a", "b"]) + for tp in (self._table_type(t), t.copy()): + assert len(t.indices) == len(tp.indices) + for index, indexp in zip(t.indices, tp.indices): + assert np.all(index.data.data == indexp.data.data) + assert index.data.data.colnames == indexp.data.data.colnames + + def test_updating_row_byindex(self, main_col, table_types, engine): + self._setup(main_col, table_types) + t = Table( + [["a", "b", "c", "d"], [2, 3, 4, 5], [3, 4, 5, 6]], + names=("a", "b", "c"), + meta={"name": "first table"}, + ) + + t.add_index("a", engine=engine) + t.add_index("b", engine=engine) + + t.loc["c"] = ["g", 40, 50] # single label, with primary key 'a' + t2 = t[2] + assert list(t2) == ["g", 40, 50] + + # list search + t.loc[["a", "d", "b"]] = [["a", 20, 30], ["d", 50, 60], ["b", 30, 40]] + t2 = [["a", 20, 30], ["d", 50, 60], ["b", 30, 40]] + for i, p in zip(t2, [1, 4, 2]): # same order as input list + assert list(t[p - 1]) == i + + def test_invalid_updates(self, main_col, table_types, engine): + # using .loc and .loc_indices with a value not present should raise an exception + self._setup(main_col, table_types) + t = Table( + [[1, 2, 3, 4], [2, 3, 4, 5], [3, 4, 5, 6]], + names=("a", "b", "c"), + meta={"name": "first table"}, + ) + + t.add_index("a") + with pytest.raises(ValueError): + t.loc[3] = [[1, 2, 3]] + with pytest.raises(ValueError): + t.loc[[1, 4, 2]] = [[1, 2, 3], [4, 5, 6]] + with pytest.raises(ValueError): + t.loc[[1, 4, 2]] = [[1, 2, 3], [4, 5, 6], [2, 3]] + with pytest.raises(ValueError): + t.loc[[1, 4, 2]] = [[1, 2, 3], [4, 5], [2, 3]] + + +def test_get_index(): + a = [1, 4, 5, 2, 7, 4, 45] + b = [2.0, 5.0, 8.2, 3.7, 4.3, 6.5, 3.3] + t = Table([a, b], names=("a", "b"), meta={"name": "first table"}) + t.add_index(["a"]) + # Getting the values of index using names + x1 = get_index(t, names=["a"]) + + assert isinstance(x1, SlicedIndex) + assert len(x1.columns) == 1 + assert len(x1.columns[0]) == 7 + assert x1.columns[0].info.name == "a" + # Getting the vales of index using table_copy + x2 = get_index(t, table_copy=t[["a"]]) + + assert isinstance(x2, SlicedIndex) + assert len(x2.columns) == 1 + assert len(x2.columns[0]) == 7 + assert x2.columns[0].info.name == "a" + + with pytest.raises(ValueError): + get_index(t, names=["a"], table_copy=t[["a"]]) + with pytest.raises(ValueError): + get_index(t, names=None, table_copy=None) + + +@pytest.mark.parametrize("table_type", [Table, QTable]) +def test_index_loc_with_quantity(engine, table_type): + t = table_type() + t["a"] = [3, 1, 2] * u.m + t["b"] = [1, 2, 3] + t.add_index("a", engine=engine) + + unit = u.m if table_type is QTable else 1 + assert tuple(t.loc[1 * unit]) == (1 * unit, 2) + assert np.all(t.loc_indices[[1 * unit, 3 * unit]] == [1, 0]) + assert tuple(t.iloc[1]) == (2 * unit, 3) + for loc in (t.loc, t.iloc): + t_loc = loc[:] + assert len(t_loc) == 3 + assert np.all(t_loc["a"] == [1, 2, 3] * unit) + assert np.all(t_loc["b"] == [2, 3, 1]) + + +def test_index_loc_with_string(engine): + t = Table() + t["a"] = ["z", "a", "m"] + t["b"] = [1, 2, 3] + t.add_index("a", engine=engine) + + assert tuple(t.loc["a"]) == ("a", 2) + assert np.all(t.loc_indices[["a", "z"]] == [1, 0]) + assert tuple(t.iloc[1]) == ("m", 3) + for loc in (t.loc, t.iloc): + t_loc = loc[:] + assert len(t_loc) == 3 + assert np.all(t_loc["a"] == ["a", "m", "z"]) + assert np.all(t_loc["b"] == [2, 3, 1]) + + +def test_table_index_time_warning(engine): + # Make sure that no ERFA warnings are emitted when indexing a table by + # a Time column with a non-default time scale + tab = Table() + tab["a"] = Time([1, 2, 3], format="jyear", scale="tai") + tab["b"] = [4, 3, 2] + with warnings.catch_warnings(record=True) as wlist: + tab.add_index(("a", "b"), engine=engine) + assert len(wlist) == 0 + + +@pytest.mark.parametrize( + "col", + [ + Column(np.arange(50000, 50005)), + np.arange(50000, 50005) * u.m, + Time(np.arange(50000, 50005), format="mjd"), + ], +) +def test_table_index_does_not_propagate_to_column_slices(col): + # They lost contact to the parent table, so they should also not have + # information on the indices; this helps prevent large memory usage if, + # e.g., a large time column is turned into an object array; see gh-10688. + tab = QTable() + tab["t"] = col + tab.add_index("t") + t = tab["t"] + assert t.info.indices + tx = t[1:] + assert not tx.info.indices + tabx = tab[1:] + t = tabx["t"] + assert t.info.indices + + +def test_hstack_qtable_table(): + # Check in particular that indices are initialized or copied correctly + # for a Column that is being converted to a Quantity. + qtab = QTable([np.arange(5.0) * u.m], names=["s"]) + qtab.add_index("s") + tab = Table([Column(np.arange(5.0), unit=u.s)], names=["t"]) + qstack = hstack([qtab, tab]) + assert qstack["t"].info.indices == [] + assert qstack.indices == [] + + +def test_index_slice_exception(): + with pytest.raises(TypeError, match="index_slice must be tuple or slice"): + SlicedIndex(None, None) + + +@pytest.fixture(scope="module") +def simple_table(): + """Simple table with an index on column 'a'.""" + t = Table() + t["a"] = [3, 1, 2, 3] + t["b"] = ["x", "y", "z", "w"] + t.add_index("a") + return t + + +@pytest.mark.parametrize("key", [None, "a"]) +@pytest.mark.parametrize( + "item,length,cls", + [ + (slice(0, 0), 0, Table), + ([], 0, Table), + ([1], 1, Table), + ([1, 3], 3, Table), + (np.array([]), 0, Table), + (np.array([1]), 1, Table), + (3, 2, Table), # scalar index with multiple rows + (1, None, Row), # scalar index with single row + ], +) +def test_index_zero_slice_or_sequence_or_scalar(simple_table, key, item, length, cls): + """Test that indexing with various types gives the expected result. + + Tests fix for #18037. + """ + loc = simple_table.loc.with_index(key) if key is not None else simple_table.loc + tloc = loc[item] + assert isinstance(tloc, cls) + assert tloc.colnames == simple_table.colnames + + rows = simple_table.loc_indices[item] + if cls is Table: + assert len(tloc) == length + assert len(rows) == length + + +@pytest.mark.parametrize( + "method,item", + [ + ("loc", (2, 5)), + ("iloc", 1), + ("loc_indices", (2, 5)), + ], +) +def test_index_id_item_deprecation_and_with_index(method, item): + """t.loc/iloc/loc_indices[index_id, item] raises a deprecation warning. + + Also test that these methods + """ + t = Table() + t["a"] = [1, 2, 3] + t["b"] = [4, 5, 6] + t["c"] = ["x", "y", "z"] + index_id = ("a", "b") + t.add_index(index_id) + prop = getattr(t, method) + # Test calling like t.loc.with_index("a", "b") and t.loc.with_index(("a", "b")). + out_call_1 = prop.with_index(*index_id)[item] + out_call_2 = prop.with_index(index_id)[item] + with pytest.warns( + AstropyDeprecationWarning, + match=r"Calling `Table.loc/iloc/loc_indices\[index_id, item\]`", + ): + out_depr = prop[index_id, item] + assert type(out_depr) is type(out_call_1) + assert out_depr == out_call_1 + assert type(out_call_1) is type(out_call_2) + assert out_call_1 == out_call_2 + + +def test_engine_type_error(): + t = Table() + t["a"] = [1, 2] + t["b"] = [3, 4] + with pytest.raises( + TypeError, + match=r"engine must be an Engine class or instance, got 'b' instead.", + ): + t.add_index("a", "b") # Easy mistake, too bad engine= is not keyword-only + + +@pytest.mark.parametrize( + "masked", + [pytest.param(False, id="raw-array"), pytest.param(True, id="masked array")], +) +def test_nd_columun_as_index(masked): + # see https://github.com/astropy/astropy/issues/13292 + # and https://github.com/astropy/astropy/pull/16360 + t = Table() + data = np.arange(0, 6) + if masked: + data = np.ma.masked_inside(data, 2, 4) + t.add_column(data.reshape(3, -1), name="arr") + with pytest.raises( + ValueError, match="Multi-dimensional column 'arr' cannot be used as an index." + ): + t.add_index("arr") + + +def test_indices_read_unknown_engine(): + lines = [ + "# %ECSV 1.0", + "# ---", + "# datatype:", + f"# - {{name: a, datatype: {NATIVE_INT_NAME}}}", + f"# - {{name: __index__, datatype: {NATIVE_INT_NAME}}}", + "# meta: !!omap", + "# - __table_indices__:", + "# indices:", + "# - colnames: [a]", + "# engine: Foo", + "# index_colname: __index__", + "# unique: true", + "# primary_key: [a]", + "# schema: astropy-2.0", + "a __index__", + "1 0", + "3 2", + "2 1", + ] + text = "\n".join(lines) + + with pytest.warns( + AstropyWarning, + match=r"Unknown index engine 'Foo', creating index using SortedArray engine", + ): + t = Table.read(text, format="ecsv") + # a==3 at row 1 + assert t.loc_indices[1] == 0 + assert t.loc_indices[3] == 1 + assert t.loc_indices[2] == 2 + + +def test_indices_serialization_unique_representation(): + t = Table() + t["a"] = [1, 3, 2] + t.add_index("a", unique=True) + out = io.StringIO() + t.write(out, format="ecsv", write_indices=True) + assert out.getvalue().splitlines() == [ + "# %ECSV 1.0", + "# ---", + "# datatype:", + f"# - {{name: a, datatype: {NATIVE_INT_NAME}}}", + f"# - {{name: __index__, datatype: {NATIVE_INT_NAME}}}", + "# meta: !!omap", + "# - __table_indices__:", + "# indices:", + "# - colnames: [a]", + "# index_colname: __index__", + "# unique: true", + "# primary_key: [a]", + "# schema: astropy-2.0", + "a __index__", + "1 0", + "3 2", + "2 1", + ] + t2 = Table.read(out.getvalue(), format="ecsv") + assert t2.indices[0].data.unique is True + + +@pytest.mark.parametrize("engine", [SortedArray, SCEngine]) +def test_indices_serialization_representation_single(engine): + """Add explicit test of serialization representation for single-index case. + + The `primary` key is not included in this case. + """ + t = Table() + t["a"] = [1, 3, 2] + t.add_index("a", engine=engine) + out = io.StringIO() + t.write(out, format="ecsv", write_indices=True) + exp = [ + "# %ECSV 1.0", + "# ---", + "# datatype:", + f"# - {{name: a, datatype: {NATIVE_INT_NAME}}}", + f"# - {{name: __index__, datatype: {NATIVE_INT_NAME}}}", + "# meta: !!omap", + "# - __table_indices__:", + "# indices:", + "# - colnames: [a]", + "# index_colname: __index__", + "# primary_key: [a]", + "# schema: astropy-2.0", + "a __index__", + "1 0", + "3 2", + "2 1", + ] + + if engine is SCEngine: + exp.insert(9, "# engine: SCEngine") + + assert out.getvalue().splitlines() == exp + + +def test_indices_serialization_representation_multiple(): + """Add explicit test of serialization representation for single-index case. + + This includes the `primary` key and a collision. + """ + t = Table() + t["a"] = [1, 3, 2] + t["__index__1"] = [5, 4, 3] + t.add_index(["a", "__index__1"]) + t.add_index("a") + out = io.StringIO() + t.write(out, format="ecsv", write_indices=True) + + exp = [ + "# %ECSV 1.0", + "# ---", + "# datatype:", + f"# - {{name: a, datatype: {NATIVE_INT_NAME}}}", + f"# - {{name: __index__1, datatype: {NATIVE_INT_NAME}}}", + f"# - {{name: __index__, datatype: {NATIVE_INT_NAME}}}", + f"# - {{name: __index__2, datatype: {NATIVE_INT_NAME}}}", + "# meta: !!omap", + "# - __table_indices__:", + "# indices:", + "# - colnames: [a, __index__1]", + "# index_colname: __index__", + "# - colnames: [a]", + "# index_colname: __index__2", + "# primary_key: [a, __index__1]", + "# schema: astropy-2.0", + "a __index__1 __index__ __index__2", + "1 5 0 0", + "3 4 2 2", + "2 3 1 1", + ] + assert out.getvalue().splitlines() == exp + + +@pytest.mark.parametrize("dtype", [np.int16, np.float32, np.int64, np.float64]) +def test_indices_roundtrip_various_dtypes(dtype): + """Test that serialization round-trip works for various index dtypes.""" + t = Table() + t["a"] = np.array([1, 3, 2], dtype=dtype) + t["b"] = np.array([5, 6, 7], dtype=dtype) + t.add_index("a") + t.add_index(["a", "b"]) + out = io.StringIO() + t.write(out, format="ecsv", write_indices=True) + t2 = Table.read(out.getvalue(), format="ecsv") + + assert_tables_equal(t, t2) + assert_indices_equal(t, t2, [("a",), ("a", "b")]) + + +@pytest.mark.parametrize("single_index", [True, False]) +@pytest.mark.parametrize("engine", [SortedArray, SCEngine]) +@pytest.mark.parametrize("fmt", ["fits", "ecsv", "hdf5"]) +def test_indices_roundtrip_through_file(single_index, fmt, engine, tmp_path): + if single_index and fmt != "ecsv": + # Save a few compute cycles, since single_index is really impacting just the + # serialization data and the engine and fmt don't matter. + pytest.skip() + + if not HAS_H5PY and fmt == "hdf5": + pytest.skip("hdf5 tests require h5py") + + t = QTable() + t["a"] = Time([1, 3, 2, 2], format="cxcsec") + t["b"] = [3, 2, 2, 1] + t["__index__"] = [3, 1, 4, 2] # Force a collision + indices_colnames = [ + ["a"], + ["b", "a"], + ["a", "b", "__index__"], + ["__index__"], + ] + if single_index: + indices_colnames = indices_colnames[:1] + + for colnames in indices_colnames: + t.add_index(colnames, engine=engine) + + path = tmp_path / f"out.{fmt}" + kwargs = {"serialize_meta": True, "path": "root"} if fmt == "hdf5" else {} + t.write(path, format=fmt, write_indices=True, **kwargs) + + kwargs = {"astropy_native": True} if fmt == "fits" else {} + t2 = QTable.read(path, format=fmt, **kwargs) + if fmt == "fits": + # FITS does not round-trip the format + t2["a"].format = "cxcsec" + + assert_tables_equal(t, t2) + assert_indices_equal(t, t2, indices_colnames) + + +def assert_indices_equal(t, t2, indices_colnames): + assert len(t.indices) == len(t2.indices) + assert t.primary_key == t2.primary_key + + for colnames in indices_colnames: + index = t.indices[colnames] + index2 = t2.indices[colnames] + assert index.id == index2.id + # Table rows sorted in index order + assert_tables_equal( + t.iloc.with_index(colnames)[:], t2.iloc.with_index(colnames)[:] + ) + # Check that the engine row_index column/list is identical + assert np.all(index.data.sorted_data() == index2.data.sorted_data()) + # Check engine items as a list of pairs of the form + # [(key, [row 1, row 2, ...]), ...]. + assert index.data.items() == index2.data.items() + + key0 = tuple(t.iloc.with_index(colnames)[0][colnames]) + key1 = tuple(t.iloc.with_index(colnames)[-1][colnames]) + assert t.loc.with_index(colnames)[key0] == t2.loc.with_index(colnames)[key0] + assert t.loc.with_index(colnames)[key1] == t2.loc.with_index(colnames)[key1] + assert ( + t.loc_indices.with_index(colnames)[key0] + == t2.loc_indices.with_index(colnames)[key0] + ) + assert ( + t.loc_indices.with_index(colnames)[key1] + == t2.loc_indices.with_index(colnames)[key1] + ) + + +@pytest.mark.parametrize("index_first", [True, False]) +def test_slice_an_indexed_table(index_first): + """Test slicing a table that is already indexed. + + Test of fix for https://github.com/astropy/astropy/issues/10732. + + #10732 is the case index_first=True, but also test slicing first (index_first=False) + since we're at it. + """ + t = Table() + t["a"] = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0] + t["b"] = [0, 0, 0, 0, 0, 1, 1, 1, 1, 1] + t["c"] = ["e", "f", "g", "h", "i", "j", "k", "a", "b", "c"] + + if index_first: + t.add_index("a") + t.add_index(["b", "c"]) + ts = t[::2] + else: + ts = t[::2] + ts.add_index("a") + ts.add_index(["b", "c"]) + + assert ts.pformat() == [ + " a b c ", + "--- --- ---", + " 9 0 e", + " 7 0 g", + " 5 0 i", + " 3 1 k", + " 1 1 b", + ] + # Index access works + assert str(ts.loc[5]).splitlines() == [ + " a b c ", + "--- --- ---", + " 5 0 i", + ] + + # Remove row 2 (a==5), check index access still works + ts.remove_row(2) + assert ts.pformat() == [ + " a b c ", + "--- --- ---", + " 9 0 e", + " 7 0 g", + " 3 1 k", + " 1 1 b", + ] + assert str(ts.loc[1]).splitlines() == [ + " a b c ", + "--- --- ---", + " 1 1 b", + ] + + # Remove row 2 (now a==3), check index access still works + ts.remove_row(2) + assert ts.pformat() == [ + " a b c ", + "--- --- ---", + " 9 0 e", + " 7 0 g", + " 1 1 b", + ] + assert str(ts.loc[7]).splitlines() == [ + " a b c ", + "--- --- ---", + " 7 0 g", + ] + + # Make sure primary index and secondary index look right (with original=True) + assert str(ts.indices[0]).splitlines() == [ + "", + " a rows", + "--- ----", + " 1 2", + " 7 1", + " 9 0>>", + ] + assert str(ts.indices[1]).splitlines() == [ + "", + " b c rows", + "--- --- ----", + " 0 e 0", + " 0 g 1", + " 1 b 2>>", + ] + + +def test_unique_indices_after_multicol_index_slice(): + """Test that table indices after slicing are correct. + + This tests code in Table._new_from_slice() that ensures uniqueness of table index + objects when slicing (via slice, ndarray, list etc) a table with a multi-column + index. + """ + t = Table() + t["a"] = [2, 3] + t["b"] = [3, 5] + t.add_index(["a", "b"]) + t2 = t[:1] + assert len(t2.indices) == 1 # without fix would be 2, both with id ("a", "b"). + assert t2.indices[0].id == ("a", "b") + + +def test_index_not_corrupted_on_failed_row_assignment(engine): + """Regression test: index must survive a failed row assignment. + + When ``table[row] = values`` raises because one of the values is + incompatible with its column dtype, the table index was left in an + inconsistent state. Specifically, ``Index.replace`` removed the + existing key from the sorted array *before* trying to insert the new + one; if the insert failed the old key was permanently gone even though + the column data was never changed. + + After the fix the index must round-trip correctly: the original key is + still findable and no ghost key is present. The test is run for all + three available index engines (BST, SortedArray, SCEngine). + """ + t = Table({"x": [1, 2, 3], "y": [4, 5, 6]}) + t.add_index("y", engine=engine) + + with pytest.raises(ValueError): + # "bad" is not convertible to the int64 dtype of column y + t[0] = (99, "bad") + + # Data must be unchanged + assert t[0]["x"] == 1 + assert t[0]["y"] == 4 + + # Index must still find the original key + result = t.loc[4] + assert result["x"] == 1 + assert result["y"] == 4 + + # No ghost entry for the attempted new value + with pytest.raises(KeyError): + t.loc[99] diff --git a/astropy/table/tests/test_info.py b/astropy/table/tests/test_info.py new file mode 100644 index 000000000000..d7f6858dd302 --- /dev/null +++ b/astropy/table/tests/test_info.py @@ -0,0 +1,400 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + + +import warnings +from collections import OrderedDict +from copy import deepcopy +from io import StringIO + +import numpy as np +import pytest + +from astropy import coordinates, table, time +from astropy import units as u +from astropy.table.info import serialize_method_as +from astropy.table.table_helpers import simple_table +from astropy.utils.data_info import data_info_factory, dtype_info_name + + +def test_table_info_attributes(table_types): + """ + Test the info() method of printing a summary of table column attributes + """ + a = np.array([1, 2, 3], dtype="int32") + b = np.array([1, 2, 3], dtype="float32") + c = np.array(["a", "c", "e"], dtype="|S1") + t = table_types.Table([a, b, c], names=["a", "b", "c"]) + + # Minimal output for a typical table + tinfo = t.info(out=None) + subcls = ["class"] if table_types.Table.__name__ == "MyTable" else [] + assert tinfo.colnames == [ + "name", + "dtype", + "shape", + "unit", + "format", + "description", + "class", + "n_bad", + "length", + ] + assert np.all(tinfo["name"] == ["a", "b", "c"]) + assert np.all(tinfo["dtype"] == ["int32", "float32", dtype_info_name("S1")]) + if subcls: + assert np.all(tinfo["class"] == ["MyColumn"] * 3) + + # All output fields including a mixin column + t["d"] = [1, 2, 3] * u.m + t["d"].description = "quantity" + t["a"].format = "%02d" + t["e"] = time.Time([1, 2, 3], format="mjd") + t["e"].info.description = "time" + t["f"] = coordinates.SkyCoord([1, 2, 3], [1, 2, 3], unit="deg") + t["f"].info.description = "skycoord" + + tinfo = t.info(out=None) + assert np.all(tinfo["name"] == ["a", "b", "c", "d", "e", "f"]) + assert np.all( + tinfo["dtype"] + == ["int32", "float32", dtype_info_name("S1"), "float64", "object", "object"] + ) + assert np.all(tinfo["unit"] == ["", "", "", "m", "", "deg,deg"]) + assert np.all(tinfo["format"] == ["%02d", "", "", "", "", ""]) + assert np.all(tinfo["description"] == ["", "", "", "quantity", "time", "skycoord"]) + cls = t.ColumnClass.__name__ + assert np.all(tinfo["class"] == [cls, cls, cls, cls, "Time", "SkyCoord"]) + + # Test that repr(t.info) is same as t.info() + out = StringIO() + t.info(out=out) + assert repr(t.info) == out.getvalue() + + +def test_table_info_stats(table_types): + """ + Test the info() method of printing a summary of table column statistics + """ + a = np.array([1, 2, 1, 2], dtype="int32") + b = np.array([1, 2, 1, 2], dtype="float32") + c = np.array(["a", "c", "e", "f"], dtype="|S1") + d = time.Time([1, 2, 1, 2], format="mjd", scale="tai") + t = table_types.Table([a, b, c, d], names=["a", "b", "c", "d"]) + + # option = 'stats' + masked = "masked=True " if t.masked else "" + out = StringIO() + t.info("stats", out=out) + table_header_line = f"<{t.__class__.__name__} {masked}length=4>" + exp = [ + table_header_line, + "name mean std min max", + "---- ---- --- --- ---", + " a 1.5 0.5 1 2", + " b 1.5 0.5 1 2", + " c -- -- -- --", + " d 1.5 -- 1.0 2.0", + ] + assert out.getvalue().splitlines() == exp + + # option = ['attributes', 'stats'] + tinfo = t.info(["attributes", "stats"], out=None) + assert tinfo.colnames == [ + "name", + "dtype", + "shape", + "unit", + "format", + "description", + "class", + "mean", + "std", + "min", + "max", + "n_bad", + "length", + ] + assert np.all(tinfo["mean"] == ["1.5", "1.5", "--", "1.5"]) + assert np.all(tinfo["std"] == ["0.5", "0.5", "--", "--"]) + assert np.all(tinfo["min"] == ["1", "1", "--", "1.0"]) + assert np.all(tinfo["max"] == ["2", "2", "--", "2.0"]) + + out = StringIO() + t.info("stats", out=out) + exp = [ + table_header_line, + "name mean std min max", + "---- ---- --- --- ---", + " a 1.5 0.5 1 2", + " b 1.5 0.5 1 2", + " c -- -- -- --", + " d 1.5 -- 1.0 2.0", + ] + assert out.getvalue().splitlines() == exp + + # option = ['attributes', custom] + custom = data_info_factory( + names=["sum", "first"], funcs=[np.sum, lambda col: col[0]] + ) + out = StringIO() + tinfo = t.info(["attributes", custom], out=None) + assert tinfo.colnames == [ + "name", + "dtype", + "shape", + "unit", + "format", + "description", + "class", + "sum", + "first", + "n_bad", + "length", + ] + assert np.all(tinfo["name"] == ["a", "b", "c", "d"]) + assert np.all( + tinfo["dtype"] == ["int32", "float32", dtype_info_name("S1"), "object"] + ) + assert np.all(tinfo["sum"] == ["6", "6", "--", "--"]) + assert np.all(tinfo["first"] == ["1", "1", "a", "1.0"]) + + +def test_data_info(): + """ + Test getting info for just a column. + """ + cols = [ + table.Column( + [1.0, 2.0, np.nan], name="name", description="description", unit="m/s" + ), + table.MaskedColumn( + [1.0, 2.0, 3.0], + name="name", + description="description", + unit="m/s", + mask=[False, False, True], + ), + ] + for c in cols: + # Test getting the full ordered dict + cinfo = c.info(out=None) + assert cinfo == OrderedDict( + [ + ("name", "name"), + ("dtype", "float64"), + ("shape", ""), + ("unit", "m / s"), + ("format", ""), + ("description", "description"), + ("class", type(c).__name__), + ("n_bad", 1), + ("length", 3), + ] + ) + + # Test the console (string) version which omits trivial values + out = StringIO() + c.info(out=out) + exp = [ + "name = name", + "dtype = float64", + "unit = m / s", + "description = description", + f"class = {type(c).__name__}", + "n_bad = 1", + "length = 3", + ] + assert out.getvalue().splitlines() == exp + + # repr(c.info) gives the same as c.info() + assert repr(c.info) == out.getvalue() + + # Test stats info + cinfo = c.info("stats", out=None) + assert cinfo == OrderedDict( + [ + ("name", "name"), + ("mean", "1.5"), + ("std", "0.5"), + ("min", "1"), + ("max", "2"), + ("n_bad", 1), + ("length", 3), + ] + ) + + +def test_data_info_subclass(): + class Column(table.Column): + """ + Confusingly named Column on purpose, but that is legal. + """ + + for data in ([], [1, 2]): + c = Column(data, dtype="int64") + cinfo = c.info(out=None) + assert cinfo == OrderedDict( + [ + ("dtype", "int64"), + ("shape", ""), + ("unit", ""), + ("format", ""), + ("description", ""), + ("class", "Column"), + ("n_bad", 0), + ("length", len(data)), + ] + ) + + +def test_scalar_info(): + """ + Make sure info works with scalar values + """ + c = time.Time("2000:001") + cinfo = c.info(out=None) + assert cinfo["n_bad"] == 0 + assert "length" not in cinfo + + +def test_empty_table(): + t = table.Table() + out = StringIO() + t.info(out=out) + exp = ["
", ""] + assert out.getvalue().splitlines() == exp + + +def test_class_attribute(): + """ + Test that class info column is suppressed only for identical non-mixin + columns. + """ + vals = [[1] * u.m, [2] * u.m] + + texp = [ + "
", + "name dtype unit", + "---- ------- ----", + "col0 float64 m", + "col1 float64 m", + ] + + qexp = [ + "", + "name dtype unit class ", + "---- ------- ---- --------", + "col0 float64 m Quantity", + "col1 float64 m Quantity", + ] + + for table_cls, exp in ((table.Table, texp), (table.QTable, qexp)): + t = table_cls(vals) + out = StringIO() + t.info(out=out) + assert out.getvalue().splitlines() == exp + + +def test_ignore_warnings(): + t = table.Table([[np.nan, np.nan]]) + with warnings.catch_warnings(record=True) as warns: + t.info("stats", out=None) + assert len(warns) == 0 + + +def test_no_deprecation_warning(): + # regression test for #5459, where numpy deprecation warnings were + # emitted unnecessarily. + t = simple_table() + with warnings.catch_warnings(record=True) as warns: + t.info() + assert len(warns) == 0 + + +def test_lost_parent_error(): + c = table.Column([1, 2, 3], name="a") + with pytest.raises(AttributeError, match='failed to access "info" attribute'): + c[:].info.name + + +def test_info_serialize_method(): + """ + Unit test of context manager to set info.serialize_method. Normally just + used to set this for writing a Table to file (FITS, ECSV, HDF5). + """ + t = table.Table( + { + "tm": time.Time([1, 2], format="cxcsec"), + "sc": coordinates.SkyCoord([1, 2], [1, 2], unit="deg"), + "mc": table.MaskedColumn([1, 2], mask=[True, False]), + "mc2": table.MaskedColumn([1, 2], mask=[True, False]), + } + ) + + origs = {} + for name in ("tm", "mc", "mc2"): + origs[name] = deepcopy(t[name].info.serialize_method) + + # Test setting by name and getting back to originals + with serialize_method_as(t, {"tm": "test_tm", "mc": "test_mc"}): + for name in ("tm", "mc"): + assert all( + t[name].info.serialize_method[key] == "test_" + name + for key in t[name].info.serialize_method + ) + assert t["mc2"].info.serialize_method == origs["mc2"] + assert not hasattr(t["sc"].info, "serialize_method") + + for name in ("tm", "mc", "mc2"): + assert t[name].info.serialize_method == origs[name] # dict compare + assert not hasattr(t["sc"].info, "serialize_method") + + # Test setting by name and class, where name takes precedence. Also + # test that it works for subclasses. + with serialize_method_as( + t, {"tm": "test_tm", "mc": "test_mc", table.Column: "test_mc2"} + ): + for name in ("tm", "mc", "mc2"): + assert all( + t[name].info.serialize_method[key] == "test_" + name + for key in t[name].info.serialize_method + ) + assert not hasattr(t["sc"].info, "serialize_method") + + for name in ("tm", "mc", "mc2"): + assert t[name].info.serialize_method == origs[name] # dict compare + assert not hasattr(t["sc"].info, "serialize_method") + + # Test supplying a single string that all applies to all columns with + # a serialize_method. + with serialize_method_as(t, "test"): + for name in ("tm", "mc", "mc2"): + assert all( + t[name].info.serialize_method[key] == "test" + for key in t[name].info.serialize_method + ) + assert not hasattr(t["sc"].info, "serialize_method") + + for name in ("tm", "mc", "mc2"): + assert t[name].info.serialize_method == origs[name] # dict compare + assert not hasattr(t["sc"].info, "serialize_method") + + +def test_info_serialize_method_exception(): + """ + Unit test of context manager to set info.serialize_method. Normally just + used to set this for writing a Table to file (FITS, ECSV, HDF5). + """ + t = simple_table(masked=True) + origs = deepcopy(t["a"].info.serialize_method) + try: + with serialize_method_as(t, "test"): + assert all( + t["a"].info.serialize_method[key] == "test" + for key in t["a"].info.serialize_method + ) + raise ZeroDivisionError() + except ZeroDivisionError: + pass + + assert t["a"].info.serialize_method == origs # dict compare diff --git a/astropy/table/tests/test_init_table.py b/astropy/table/tests/test_init_table.py index 0509eb7880f6..c4aa058df243 100644 --- a/astropy/table/tests/test_init_table.py +++ b/astropy/table/tests/test_init_table.py @@ -1,276 +1,665 @@ -from __future__ import print_function # For print debugging with python 2 or 3 +# Licensed under a 3-clause BSD style license - see LICENSE.rst -import pytest -import numpy as np - -from .. import Table, Column -from astropy.utils import OrderedDict - - -class BaseInitFrom(): +from collections import OrderedDict, UserDict +from collections.abc import Mapping - def test_basic_init(self): - t = Table(self.data, names=('a', 'b', 'c')) - assert t.colnames == ['a', 'b', 'c'] - assert np.all(t['a'] == np.array([1, 3])) - assert np.all(t['b'] == np.array([2, 4])) - assert np.all(t['c'] == np.array([3, 5])) +import numpy as np +import pytest +from numpy.testing import assert_array_equal + +import astropy.units as u +from astropy.table import Column, MaskedColumn, QTable, Table, TableColumns +from astropy.utils.masked import Masked + + +class DictLike(Mapping): + """A minimal mapping-like object that does not subclass dict. + + This is used to test code that expects dict-like but without actually + inheriting from dict. + """ + + def __init__(self, *args, **kwargs): + self._data = dict(*args, **kwargs) + + def __getitem__(self, item): + return self._data[item] + + def __setitem__(self, item, value): + self._data[item] = value + + def __iter__(self): + return iter(self._data) + + def __len__(self): + return len(self._data) + + +class TestTableColumnsInit: + def test_init(self): + """Test initialisation with lists, tuples, dicts of arrays + rather than Columns [regression test for #2647]""" + x1 = np.arange(10.0) + x2 = np.arange(5.0) + x3 = np.arange(7.0) + col_list = [("x1", x1), ("x2", x2), ("x3", x3)] + tc_list = TableColumns(col_list) + for col in col_list: + assert col[0] in tc_list + assert tc_list[col[0]] is col[1] + + col_tuple = (("x1", x1), ("x2", x2), ("x3", x3)) + tc_tuple = TableColumns(col_tuple) + for col in col_tuple: + assert col[0] in tc_tuple + assert tc_tuple[col[0]] is col[1] + + col_dict = {"x1": x1, "x2": x2, "x3": x3} + tc_dict = TableColumns(col_dict) + for col in tc_dict.keys(): + assert col in tc_dict + assert tc_dict[col] is col_dict[col] + + columns = [Column(col[1], name=col[0]) for col in col_list] + tc = TableColumns(columns) + for col in columns: + assert col.name in tc + assert tc[col.name] is col + + +# pytest.mark.usefixtures('table_type') +class BaseInitFrom: + def _setup(self, table_type): + pass + + def test_basic_init(self, table_type): + self._setup(table_type) + t = table_type(self.data, names=("a", "b", "c")) + assert t.colnames == ["a", "b", "c"] + assert np.all(t["a"] == np.array([1, 3])) + assert np.all(t["b"] == np.array([2, 4])) + assert np.all(t["c"] == np.array([3, 5])) assert all(t[name].name == name for name in t.colnames) - def test_set_dtypes(self): - t = Table(self.data, names=('a', 'b', 'c'), dtypes=('i4', 'f4', 'f8')) - assert t.colnames == ['a', 'b', 'c'] - assert np.all(t['a'] == np.array([1, 3], dtype='i4')) - assert np.all(t['b'] == np.array([2, 4], dtype='f4')) - assert np.all(t['c'] == np.array([3, 5], dtype='f8')) - assert t['a'].dtype.type == np.int32 - assert t['b'].dtype.type == np.float32 - assert t['c'].dtype.type == np.float64 + def test_set_dtype(self, table_type): + self._setup(table_type) + t = table_type(self.data, names=("a", "b", "c"), dtype=("i4", "f4", "f8")) + assert t.colnames == ["a", "b", "c"] + assert np.all(t["a"] == np.array([1, 3], dtype="i4")) + assert np.all(t["b"] == np.array([2, 4], dtype="f4")) + assert np.all(t["c"] == np.array([3, 5], dtype="f8")) + assert t["a"].dtype.type == np.int32 + assert t["b"].dtype.type == np.float32 + assert t["c"].dtype.type == np.float64 assert all(t[name].name == name for name in t.colnames) - def test_names_dtypes_mismatch(self): + def test_names_dtype_mismatch(self, table_type): + self._setup(table_type) with pytest.raises(ValueError): - Table(self.data, names=('a',), dtypes=('i4', 'f4', 'i4')) + table_type(self.data, names=("a",), dtype=("i4", "f4", "i4")) - def test_names_cols_mismatch(self): + def test_names_cols_mismatch(self, table_type): + self._setup(table_type) with pytest.raises(ValueError): - Table(self.data, names=('a',), dtypes=('i4')) + table_type(self.data, names=("a",), dtype="i4") +@pytest.mark.usefixtures("table_type") class BaseInitFromListLike(BaseInitFrom): - - def test_names_cols_mismatch(self): + def test_names_cols_mismatch(self, table_type): + self._setup(table_type) with pytest.raises(ValueError): - Table(self.data, names=['a'], dtypes=[int]) + table_type(self.data, names=["a"], dtype=[int]) - def test_names_copy_false(self): + def test_names_copy_false(self, table_type): + self._setup(table_type) with pytest.raises(ValueError): - Table(self.data, names=['a'], dtypes=[int], copy=False) + table_type(self.data, names=["a"], dtype=[int], copy=False) +@pytest.mark.usefixtures("table_type") class BaseInitFromDictLike(BaseInitFrom): pass +@pytest.mark.usefixtures("table_type") class TestInitFromNdarrayHomo(BaseInitFromListLike): - def setup_method(self, method): - self.data = np.array([(1, 2, 3), - (3, 4, 5)], - dtype='i4') + self.data = np.array([(1, 2, 3), (3, 4, 5)], dtype="i4") - def test_default_names(self): - t = Table(self.data) - assert t.colnames == ['col0', 'col1', 'col2'] + def test_default_names(self, table_type): + self._setup(table_type) + t = table_type(self.data) + assert t.colnames == ["col0", "col1", "col2"] - def test_ndarray_ref(self): - """Init with ndarray and copy=False and show that ValueError is raised + def test_ndarray_ref(self, table_type): + """Init with ndarray and copy=False and show that this is a reference to input ndarray""" - t = Table(self.data, copy=False) - t['col1'][1] = 0 - assert t._data['col1'][1] == 0 - assert t['col1'][1] == 0 + self._setup(table_type) + t = table_type(self.data, copy=False) + t["col1"][1] = 0 + assert t.as_array()["col1"][1] == 0 + assert t["col1"][1] == 0 assert self.data[1][1] == 0 - # NOTE: assert np.all(t._data == self.data) fails because when - # homogenous array is viewcast to structured then the == is False - - def test_partial_names_dtypes(self): - t = Table(self.data, names=['a', None, 'c'], dtypes=[None, None, 'f8']) - assert t.colnames == ['a', 'col1', 'c'] - assert t['a'].dtype.type == np.int32 - assert t['col1'].dtype.type == np.int32 - assert t['c'].dtype.type == np.float64 + + def test_partial_names_dtype(self, table_type): + self._setup(table_type) + t = table_type(self.data, names=["a", None, "c"], dtype=[None, None, "f8"]) + assert t.colnames == ["a", "col1", "c"] + assert t["a"].dtype.type == np.int32 + assert t["col1"].dtype.type == np.int32 + assert t["c"].dtype.type == np.float64 assert all(t[name].name == name for name in t.colnames) - def test_partial_names_ref(self): - t = Table(self.data, names=['a', None, 'c']) - assert t.colnames == ['a', 'col1', 'c'] - assert t['a'].dtype.type == np.int32 - assert t['col1'].dtype.type == np.int32 - assert t['c'].dtype.type == np.int32 + def test_partial_names_ref(self, table_type): + self._setup(table_type) + t = table_type(self.data, names=["a", None, "c"]) + assert t.colnames == ["a", "col1", "c"] + assert t["a"].dtype.type == np.int32 + assert t["col1"].dtype.type == np.int32 + assert t["c"].dtype.type == np.int32 assert all(t[name].name == name for name in t.colnames) +@pytest.mark.usefixtures("table_type") class TestInitFromListOfLists(BaseInitFromListLike): - - def setup_method(self, method): - self.data = [(np.int32(1), np.int32(3)), - Column('col1', [2, 4], dtype=np.int32), - np.array([3, 5], dtype=np.int32)] - - def test_default_names(self): - t = Table(self.data) - assert t.colnames == ['col0', 'col1', 'col2'] + def setup_method(self, table_type): + self._setup(table_type) + self.data = [ + (np.int32(1), np.int32(3)), + Column(name="col1", data=[2, 4], dtype=np.int32), + np.array([3, 5], dtype=np.int32), + ] + + def test_default_names(self, table_type): + self._setup(table_type) + t = table_type(self.data) + assert t.colnames == ["col0", "col1", "col2"] assert all(t[name].name == name for name in t.colnames) - def test_partial_names_dtypes(self): - t = Table(self.data, names=['b', None, 'c'], - dtypes=['f4', None, 'f8']) - assert t.colnames == ['b', 'col1', 'c'] - assert t['b'].dtype.type == np.float32 - assert t['col1'].dtype.type == np.int32 - assert t['c'].dtype.type == np.float64 + def test_partial_names_dtype(self, table_type): + self._setup(table_type) + t = table_type(self.data, names=["b", None, "c"], dtype=["f4", None, "f8"]) + assert t.colnames == ["b", "col1", "c"] + assert t["b"].dtype.type == np.float32 + assert t["col1"].dtype.type == np.int32 + assert t["c"].dtype.type == np.float64 assert all(t[name].name == name for name in t.colnames) - def test_bad_data(self): + def test_bad_data(self, table_type): + self._setup(table_type) with pytest.raises(ValueError): - Table([[1, 2], - [3, 4, 5]]) - - + table_type([[1, 2], [3, 4, 5]]) + + +@pytest.mark.usefixtures("table_type") +class TestInitFromListOfDicts(BaseInitFromListLike): + def _setup(self, table_type): + self.data = [{"a": 1, "b": 2, "c": 3}, {"a": 3, "b": 4, "c": 5}] + self.data_ragged = [{"a": 1, "b": 2}, {"a": 2, "c": 4}] + self.data_acb = [{"a": 2, "c": 4}, {"a": 1, "b": 2}] + + def test_names(self, table_type): + self._setup(table_type) + t = table_type(self.data) + assert all(colname in {"a", "b", "c"} for colname in t.colnames) + + def test_names_ordered(self, table_type): + self._setup(table_type) + t = table_type(self.data, names=("c", "b", "a")) + assert t.colnames == ["c", "b", "a"] + + def test_rows_without_names_args(self, table_type): + # see https://github.com/astropy/astropy/pull/15735 + self._setup(table_type) + t1 = table_type(rows=self.data) + assert t1.colnames == ["a", "b", "c"] + t2 = table_type(rows=self.data_acb) + assert t2.colnames == ["a", "c", "b"] + + def test_missing_data_init_from_dict(self, table_type): + self._setup(table_type) + dat = self.data_ragged + for rows in [False, True]: + t = table_type(rows=dat) if rows else table_type(dat) + + assert np.all(t["a"] == [1, 2]) + assert np.all(t["b"].mask == [False, True]) + assert np.all(t["b"].data == [2, 2]) + assert np.all(t["c"].mask == [True, False]) + assert np.all(t["c"].data == [4, 4]) + + assert type(t["a"]) is (MaskedColumn if t.masked else Column) + assert type(t["b"]) is MaskedColumn + assert type(t["c"]) is MaskedColumn + + +def test_qtable_uses_masked_quantity_as_needed(): + data = [{"a": 1 * u.m, "b": 1}, {"a": 2 * u.Mm, "b": 2}] + data_ragged = [{"a": 1 * u.m, "b": 1}, {"a": 2 * u.Mm}, {"b": 3}] + t = QTable(data) + assert t.colnames == ["a", "b"] + assert isinstance(t["a"], u.Quantity) + assert isinstance(t["b"], Column) + assert t["a"].unit == u.m + assert_array_equal(t["a"], [1, 2000000] * u.m) + assert not t.masked + + t2 = QTable(data_ragged) + assert t2.colnames == ["a", "b"] + assert isinstance(t2["a"], Masked(u.Quantity)) + assert isinstance(t2["b"], MaskedColumn) + assert t2["a"].unit == u.m + assert np.all(t2["a"] == [1, 2000000, 0] * u.m) + assert_array_equal(t2["a"].mask, [False, False, True]) + assert_array_equal(t2["b"].mask, [False, True, False]) + + +class TestInitFromListOfMapping(TestInitFromListOfDicts): + """Test that init from a Mapping that is not a dict subclass works""" + + def _setup(self, table_type): + self.data = [DictLike(a=1, b=2, c=3), DictLike(a=3, b=4, c=5)] + self.data_ragged = [DictLike(a=1, b=2), DictLike(a=2, c=4)] + self.data_acb = [DictLike(a=2, c=4), DictLike(a=1, b=2)] + # Make sure data rows are not a dict subclass + assert not isinstance(self.data[0], dict) + + +@pytest.mark.usefixtures("table_type") class TestInitFromColsList(BaseInitFromListLike): - - def setup_method(self, method): - self.data = [Column('x', [1, 3], dtype=np.int32), - np.array([2, 4], dtype=np.int32), - np.array([3, 5], dtype='i8')] - - def test_default_names(self): - t = Table(self.data) - assert t.colnames == ['x', 'col1', 'col2'] + def _setup(self, table_type): + self.data = [ + Column([1, 3], name="x", dtype=np.int32), + np.array([2, 4], dtype=np.int32), + np.array([3, 5], dtype="i8"), + ] + + def test_default_names(self, table_type): + self._setup(table_type) + t = table_type(self.data) + assert t.colnames == ["x", "col1", "col2"] assert all(t[name].name == name for name in t.colnames) - def test_partial_names_dtypes(self): - t = Table(self.data, names=['b', None, 'c'], dtypes=['f4', None, 'f8']) - assert t.colnames == ['b', 'col1', 'c'] - assert t['b'].dtype.type == np.float32 - assert t['col1'].dtype.type == np.int32 - assert t['c'].dtype.type == np.float64 + def test_partial_names_dtype(self, table_type): + self._setup(table_type) + t = table_type(self.data, names=["b", None, "c"], dtype=["f4", None, "f8"]) + assert t.colnames == ["b", "col1", "c"] + assert t["b"].dtype.type == np.float32 + assert t["col1"].dtype.type == np.int32 + assert t["c"].dtype.type == np.float64 assert all(t[name].name == name for name in t.colnames) - def test_ref(self): - with pytest.raises(ValueError): - Table(self.data, copy=False) + def test_ref(self, table_type): + """Test that initializing from a list of columns can be done by reference""" + self._setup(table_type) + t = table_type(self.data, copy=False) + t["x"][0] = 100 + assert self.data[0][0] == 100 +@pytest.mark.usefixtures("table_type") class TestInitFromNdarrayStruct(BaseInitFromDictLike): + def _setup(self, table_type): + self.data = np.array( + [(1, 2, 3), (3, 4, 5)], dtype=[("x", "i8"), ("y", "i4"), ("z", "i8")] + ) - def setup_method(self, method): - self.data = np.array([(1, 2, 3), - (3, 4, 5)], - dtype=[('x', 'i8'), ('y', 'i4'), ('z', 'i8')]) - - def test_ndarray_ref(self): + def test_ndarray_ref(self, table_type): """Init with ndarray and copy=False and show that table uses reference to input ndarray""" - t = Table(self.data, copy=False) - assert np.all(t._data == self.data) - t['x'][1] = 0 - assert t._data['x'][1] == 0 - assert self.data['x'][1] == 0 - assert np.all(t._data == self.data) + self._setup(table_type) + t = table_type(self.data, copy=False) + + t["x"][1] = 0 # Column-wise assignment + t[0]["y"] = 0 # Row-wise assignment + assert self.data["x"][1] == 0 + assert self.data["y"][0] == 0 + assert np.all(np.array(t) == self.data) assert all(t[name].name == name for name in t.colnames) - def test_partial_names_dtypes(self): - t = Table(self.data, names=['e', None, 'd'], dtypes=['f4', None, 'f8']) - assert t.colnames == ['e', 'y', 'd'] - assert t['e'].dtype.type == np.float32 - assert t['y'].dtype.type == np.int32 - assert t['d'].dtype.type == np.float64 + def test_partial_names_dtype(self, table_type): + self._setup(table_type) + t = table_type(self.data, names=["e", None, "d"], dtype=["f4", None, "f8"]) + assert t.colnames == ["e", "y", "d"] + assert t["e"].dtype.type == np.float32 + assert t["y"].dtype.type == np.int32 + assert t["d"].dtype.type == np.float64 assert all(t[name].name == name for name in t.colnames) - def test_partial_names_ref(self): - t = Table(self.data, names=['e', None, 'd'], copy=False) - assert t.colnames == ['e', 'y', 'd'] - assert t['e'].dtype.type == np.int64 - assert t['y'].dtype.type == np.int32 - assert t['d'].dtype.type == np.int64 + def test_partial_names_ref(self, table_type): + self._setup(table_type) + t = table_type(self.data, names=["e", None, "d"], copy=False) + assert t.colnames == ["e", "y", "d"] + assert t["e"].dtype.type == np.int64 + assert t["y"].dtype.type == np.int32 + assert t["d"].dtype.type == np.int64 assert all(t[name].name == name for name in t.colnames) +@pytest.mark.usefixtures("table_type") class TestInitFromDict(BaseInitFromDictLike): - - def setup_method(self, method): - self.data = dict([('a', Column('x', [1, 3])), - ('b', [2, 4]), - ('c', np.array([3, 5], dtype='i8'))]) - - + def _setup(self, table_type): + self.data = { + "a": Column([1, 3], name="x"), + "b": [2, 4], + "c": np.array([3, 5], dtype="i8"), + } + + +@pytest.mark.usefixtures("table_type") +class TestInitFromMapping(BaseInitFromDictLike): + def _setup(self, table_type): + self.data = UserDict( + [ + ("a", Column([1, 3], name="x")), + ("b", [2, 4]), + ("c", np.array([3, 5], dtype="i8")), + ] + ) + assert isinstance(self.data, Mapping) + assert not isinstance(self.data, dict) + + +@pytest.mark.usefixtures("table_type") class TestInitFromOrderedDict(BaseInitFromDictLike): + def _setup(self, table_type): + self.data = OrderedDict( + [ + ("a", Column(name="x", data=[1, 3])), + ("b", [2, 4]), + ("c", np.array([3, 5], dtype="i8")), + ] + ) + + def test_col_order(self, table_type): + self._setup(table_type) + t = table_type(self.data) + assert t.colnames == ["a", "b", "c"] + + +@pytest.mark.usefixtures("table_type") +class TestInitFromRow(BaseInitFromDictLike): + def _setup(self, table_type): + arr = np.array( + [(1, 2, 3), (3, 4, 5)], dtype=[("x", "i8"), ("y", "i8"), ("z", "f8")] + ) + self.data = table_type(arr, meta={"comments": ["comment1", "comment2"]}) + + def test_init_from_row(self, table_type): + self._setup(table_type) + t = table_type(self.data[0]) + + # Values and meta match original + assert t.meta["comments"][0] == "comment1" + for name in t.colnames: + assert np.all(t[name] == self.data[name][0:1]) + assert all(t[name].name == name for name in t.colnames) - def setup_method(self, method): - self.data = OrderedDict([('a', Column('x', [1, 3])), - ('b', [2, 4]), - ('c', np.array([3, 5], dtype='i8'))]) - - def test_col_order(self): - t = Table(self.data) - assert t.colnames == ['a', 'b', 'c'] + # Change value in new instance and check that original is the same + t["x"][0] = 8 + t.meta["comments"][1] = "new comment2" + assert np.all(t["x"] == np.array([8])) + assert np.all(self.data["x"] == np.array([1, 3])) + assert self.data.meta["comments"][1] == "comment2" +@pytest.mark.usefixtures("table_type") class TestInitFromTable(BaseInitFromDictLike): - - def setup_method(self, method): - arr = np.array([(1, 2, 3), - (3, 4, 5)], - dtype=[('x', 'i8'), ('y', 'i8'), ('z', 'f8')]) - self.data = Table(arr, meta={'comments': ['comment1', 'comment2']}) - - def test_data_meta_copy(self): - t = Table(self.data) - assert t.meta['comments'][0] == 'comment1' - t['x'][1] = 8 - t.meta['comments'][1] = 'new comment2' - assert self.data.meta['comments'][1] == 'comment2' - assert np.all(t['x'] == np.array([1, 8])) - assert np.all(self.data['x'] == np.array([1, 3])) - assert t['z'].name == 'z' + def _setup(self, table_type): + arr = np.array( + [(1, 2, 3), (3, 4, 5)], dtype=[("x", "i8"), ("y", "i8"), ("z", "f8")] + ) + self.data = table_type(arr, meta={"comments": ["comment1", "comment2"]}) + + def test_data_meta_copy(self, table_type): + self._setup(table_type) + t = table_type(self.data) + assert t.meta["comments"][0] == "comment1" + t["x"][1] = 8 + t.meta["comments"][1] = "new comment2" + assert self.data.meta["comments"][1] == "comment2" + assert np.all(t["x"] == np.array([1, 8])) + assert np.all(self.data["x"] == np.array([1, 3])) + assert t["z"].name == "z" assert all(t[name].name == name for name in t.colnames) - def test_table_ref(self): - t = Table(self.data, copy=False) - assert np.all(t._data == self.data._data) - t['x'][1] = 0 - assert t._data['x'][1] == 0 - assert self.data._data['x'][1] == 0 - assert np.all(t._data == self.data._data) + def test_table_ref(self, table_type): + self._setup(table_type) + t = table_type(self.data, copy=False) + t["x"][1] = 0 + assert t["x"][1] == 0 + assert self.data["x"][1] == 0 + assert np.all(t.as_array() == self.data.as_array()) assert all(t[name].name == name for name in t.colnames) - def test_partial_names_dtypes(self): - t = Table(self.data, names=['e', None, 'd'], dtypes=['f4', None, 'i8']) - assert t.colnames == ['e', 'y', 'd'] - assert t['e'].dtype.type == np.float32 - assert t['y'].dtype.type == np.int64 - assert t['d'].dtype.type == np.int64 + def test_partial_names_dtype(self, table_type): + self._setup(table_type) + t = table_type(self.data, names=["e", None, "d"], dtype=["f4", None, "i8"]) + assert t.colnames == ["e", "y", "d"] + assert t["e"].dtype.type == np.float32 + assert t["y"].dtype.type == np.int64 + assert t["d"].dtype.type == np.int64 assert all(t[name].name == name for name in t.colnames) - def test_partial_names_ref(self): - t = Table(self.data, names=['e', None, 'd'], copy=False) - assert t.colnames == ['e', 'y', 'd'] - assert t['e'].dtype.type == np.int64 - assert t['y'].dtype.type == np.int64 - assert t['d'].dtype.type == np.float64 + def test_partial_names_ref(self, table_type): + self._setup(table_type) + t = table_type(self.data, names=["e", None, "d"], copy=False) + assert t.colnames == ["e", "y", "d"] + assert t["e"].dtype.type == np.int64 + assert t["y"].dtype.type == np.int64 + assert t["d"].dtype.type == np.float64 assert all(t[name].name == name for name in t.colnames) - def test_init_from_columns(self): - t = Table(self.data) - t2 = Table(t.columns['z', 'x', 'y']) - assert t2.colnames == ['z', 'x', 'y'] - assert t2._data.dtype.names == ('z', 'x', 'y') - - def test_init_from_columns_slice(self): - t = Table(self.data) - t2 = Table(t.columns[0:2]) - assert t2.colnames == ['x', 'y'] - assert t2._data.dtype.names == ('x', 'y') - - def test_init_from_columns_mix(self): - t = Table(self.data) - t2 = Table([t.columns[0], t.columns['z']]) - assert t2.colnames == ['x', 'z'] - assert t2._data.dtype.names == ('x', 'z') - - -class TestInitFromNone(): + def test_init_from_columns(self, table_type): + self._setup(table_type) + t = table_type(self.data) + t2 = table_type(t.columns["z", "x", "y"]) + assert t2.colnames == ["z", "x", "y"] + assert t2.dtype.names == ("z", "x", "y") + + def test_init_from_columns_slice(self, table_type): + self._setup(table_type) + t = table_type(self.data) + t2 = table_type(t.columns[0:2]) + assert t2.colnames == ["x", "y"] + assert t2.dtype.names == ("x", "y") + + def test_init_from_columns_mix(self, table_type): + self._setup(table_type) + t = table_type(self.data) + t2 = table_type([t.columns[0], t.columns["z"]]) + assert t2.colnames == ["x", "z"] + assert t2.dtype.names == ("x", "z") + + +@pytest.mark.usefixtures("table_type") +class TestInitFromNone: # Note table_table.TestEmptyData tests initializing a completely empty # table and adding data. - def test_data_none_with_cols(self): - t = Table(names=('a', 'b')) - assert len(t['a']) == 0 - assert len(t['b']) == 0 - assert t.colnames == ['a', 'b'] - t = Table(names=('a', 'b'), dtypes=('f4', 'i4')) - assert t['a'].dtype.type == np.float32 - assert t['b'].dtype.type == np.int32 - assert t.colnames == ['a', 'b'] + def test_data_none_with_cols(self, table_type): + """ + Test different ways of initing an empty table + """ + np_t = np.empty(0, dtype=[("a", "f4", (2,)), ("b", "i4")]) + for kwargs in ( + {"names": ("a", "b")}, + {"names": ("a", "b"), "dtype": (("f4", (2,)), "i4")}, + {"dtype": [("a", "f4", (2,)), ("b", "i4")]}, + {"dtype": np_t.dtype}, + ): + t = table_type(**kwargs) + assert t.colnames == ["a", "b"] + assert len(t["a"]) == 0 + assert len(t["b"]) == 0 + if "dtype" in kwargs: + assert t["a"].dtype.type == np.float32 + assert t["b"].dtype.type == np.int32 + assert t["a"].shape[1:] == (2,) + + +@pytest.mark.usefixtures("table_types") +class TestInitFromRows: + def test_init_with_rows(self, table_type): + for rows in ([[1, "a"], [2, "b"]], [(1, "a"), (2, "b")], ((1, "a"), (2, "b"))): + t = table_type(rows=rows, names=("a", "b")) + assert np.all(t["a"] == [1, 2]) + assert np.all(t["b"] == ["a", "b"]) + assert t.colnames == ["a", "b"] + assert t["a"].dtype.kind == "i" + assert t["b"].dtype.kind in ("S", "U") + # Regression test for + # https://github.com/astropy/astropy/issues/3052 + assert t["b"].dtype.str.endswith("1") + + rows = np.arange(6).reshape(2, 3) + t = table_type(rows=rows, names=("a", "b", "c"), dtype=["f8", "f4", "i8"]) + assert np.all(t["a"] == [0, 3]) + assert np.all(t["b"] == [1, 4]) + assert np.all(t["c"] == [2, 5]) + assert t.colnames == ["a", "b", "c"] + assert t["a"].dtype.str.endswith("f8") + assert t["b"].dtype.str.endswith("f4") + assert t["c"].dtype.str.endswith("i8") + + def test_init_with_rows_and_data(self, table_type): + with pytest.raises(ValueError) as err: + table_type(data=[[1]], rows=[[1]]) + assert "Cannot supply both `data` and `rows` values" in str(err.value) + + +@pytest.mark.parametrize("has_data", [True, False]) +def test_init_table_with_names_and_structured_dtype(has_data): + """Test fix for #10393""" + arr = np.ones(2, dtype=np.dtype([("a", "i4"), ("b", "f4")])) + data_args = [arr] if has_data else [] + t = Table(*data_args, names=["x", "y"], dtype=arr.dtype) + assert t.colnames == ["x", "y"] + assert str(t["x"].dtype) == "int32" + assert str(t["y"].dtype) == "float32" + assert len(t) == (2 if has_data else 0) + + +@pytest.mark.usefixtures("table_type") +def test_init_and_ref_from_multidim_ndarray(table_type): + """ + Test that initializing from an ndarray structured array with + a multi-dim column works for both copy=False and True and that + the referencing is as expected. + """ + for copy in (False, True): + nd = np.array( + [(1, [10, 20]), (3, [30, 40])], dtype=[("a", "i8"), ("b", "i8", (2,))] + ) + t = table_type(nd, copy=copy) + assert t.colnames == ["a", "b"] + assert t["a"].shape == (2,) + assert t["b"].shape == (2, 2) + t["a"][0] = -200 + t["b"][1][1] = -100 + if copy: + assert nd["a"][0] == 1 + assert nd["b"][1][1] == 40 + else: + assert nd["a"][0] == -200 + assert nd["b"][1][1] == -100 + + +@pytest.mark.usefixtures("table_type") +@pytest.mark.parametrize("copy", [False, True]) +def test_init_and_ref_from_dict(table_type, copy): + """ + Test that initializing from a dict works for both copy=False and True and that + the referencing is as expected. + """ + x1 = np.arange(10.0) + x2 = np.zeros(10) + col_dict = {"x1": x1, "x2": x2} + t = table_type(col_dict, copy=copy) + assert set(t.colnames) == {"x1", "x2"} + assert t["x1"].shape == (10,) + assert t["x2"].shape == (10,) + t["x1"][0] = -200 + t["x2"][1] = -100 + if copy: + assert x1[0] == 0.0 + assert x2[1] == 0.0 + else: + assert x1[0] == -200 + assert x2[1] == -100 + + +def test_add_none_object_column(): + """Test fix for a problem introduced in #10636 (see + https://github.com/astropy/astropy/pull/10636#issuecomment-676847515) + """ + t = Table(data={"a": [1, 2, 3]}) + t["b"] = None + assert all(val is None for val in t["b"]) + assert t["b"].dtype.kind == "O" + + +@pytest.mark.usefixtures("table_type") +def test_init_from_row_OrderedDict(table_type): + row1 = OrderedDict([("b", 1), ("a", 0)]) + row2 = {"a": 10, "b": 20} + rows12 = [row1, row2] + row3 = {"b": 1, "a": 0} + row4 = {"b": 11, "a": 10} + rows34 = [row3, row4] + t1 = table_type(rows=rows12) + t2 = table_type(rows=rows34) + t3 = t2[sorted(t2.colnames)] + assert t1.colnames == ["b", "a"] + assert t2.colnames == ["b", "a"] + assert t3.colnames == ["a", "b"] + + +def test_init_from_rows_as_generator(): + rows = ((1 + ii, 2 + ii) for ii in range(2)) + t = Table(rows=rows) + assert np.all(t["col0"] == [1, 2]) + assert np.all(t["col1"] == [2, 3]) + + +@pytest.mark.parametrize("dtype", ["fail", "i4"]) +def test_init_bad_dtype_in_empty_table(dtype): + with pytest.raises( + ValueError, match="type was specified but could not be parsed for column names" + ): + Table(dtype=dtype) + + +def test_init_data_type_not_allowed_to_init_table(): + with pytest.raises( + ValueError, match="Data type not allowed to init Table" + ): + Table("hello") + + +def test_init_Table_from_list_of_quantity(): + """Test fix for #11327""" + # Variation on original example in #11327 at the Table level + data = [{"x": 5 * u.m, "y": 1 * u.m}, {"x": 10 * u.m, "y": 3}] + t = Table(data) + assert t["x"].unit is u.m + assert t["y"].unit is None + assert t["x"].dtype.kind == "f" + assert t["y"].dtype.kind == "O" + assert np.all(t["x"] == [5, 10]) + assert t["y"][0] == 1 * u.m + assert t["y"][1] == 3 + + +def test_init_QTable_and_set_units(): + """ + Test fix for #14336 where providing units to QTable init fails. + + This applies when the input is a Quantity. + """ + t = QTable([[1, 2] * u.km, [1, 2]], units={"col0": u.m, "col1": u.s}) + assert t["col0"].unit == u.m + assert np.all(t["col0"].value == [1000, 2000]) + assert t["col1"].unit == u.s + assert np.all(t["col1"].value == [1, 2]) diff --git a/astropy/table/tests/test_item_access.py b/astropy/table/tests/test_item_access.py index a017a3484a70..6de4f40e83e1 100644 --- a/astropy/table/tests/test_item_access.py +++ b/astropy/table/tests/test_item_access.py @@ -1,153 +1,261 @@ -""" Verify item access API in: +# Licensed under a 3-clause BSD style license - see LICENSE.rst + + +"""Verify item access API in: https://github.com/astropy/astropy/wiki/Table-item-access-definition """ -from .. import Table, Column + import numpy as np import pytest -COLS = [Column('a', [1, 2, 3], description='da', - format='fa', meta={'ma': 1}, units='ua'), - Column('b', [4, 5, 6], description='db', - format='fb', meta={'mb': 1}, units='ub'), - Column('c', [7, 8, 9], description='dc', - format='fc', meta={'mc': 1}, units='ub')] -DATA = Table(COLS) +from astropy.table import Row -class BaseTestItems(): - - def setup_method(self, method): - self.t = Table(COLS) - self.tc = self.t.columns +@pytest.mark.usefixtures("table_data") +class BaseTestItems: + pass +@pytest.mark.usefixtures("table_data") class TestTableColumnsItems(BaseTestItems): - - def test_by_name(self): + def test_by_name(self, table_data): """Access TableColumns by name and show that item access returns a Column that refers to underlying table data""" - assert self.tc['a'].name == 'a' - assert self.tc['a'][1] == 2 - assert self.tc['a'].description == 'da' - assert self.tc['a'].format == 'fa' - assert self.tc['a'].meta == {'ma': 1} - assert self.tc['a'].units == 'ua' - assert self.tc['a'].attrs_equal(COLS[0]) - assert isinstance(self.tc['a'], Column) - - self.tc['b'][1] = 0 - assert self.t['b'][1] == 0 - - def test_by_position(self): + self.t = table_data.Table(table_data.COLS) + self.tc = self.t.columns + + assert self.tc["a"].name == "a" + assert self.tc["a"][1] == 2 + assert self.tc["a"].description == "da" + assert self.tc["a"].format == "%i" + assert self.tc["a"].meta == {"ma": 1} + assert self.tc["a"].unit == "ua" + assert self.tc["a"].attrs_equal(table_data.COLS[0]) + assert isinstance(self.tc["a"], table_data.Column) + + self.tc["b"][1] = 0 + assert self.t["b"][1] == 0 + + def test_by_position(self, table_data): """Access TableColumns by position and show that item access returns a Column that refers to underlying table data""" - assert self.tc[1].name == 'b' - assert np.all(self.tc[1].data == COLS[1].data) - assert self.tc[1].description == 'db' - assert self.tc[1].format == 'fb' - assert self.tc[1].meta == {'mb': 1} - assert self.tc[1].units == 'ub' - assert self.tc[1].attrs_equal(COLS[1]) - assert isinstance(self.tc[1], Column) + self.t = table_data.Table(table_data.COLS) + self.tc = self.t.columns + + assert self.tc[1].name == "b" + assert np.all(self.tc[1].data == table_data.COLS[1].data) + assert self.tc[1].description == "db" + assert self.tc[1].format == "%d" + assert self.tc[1].meta == {"mb": 1} + assert self.tc[1].unit == "ub" + assert self.tc[1].attrs_equal(table_data.COLS[1]) + assert isinstance(self.tc[1], table_data.Column) + + assert self.tc[2].unit == "ub" self.tc[1][1] = 0 - assert self.t['b'][1] == 0 + assert self.t["b"][1] == 0 - def test_mult_columns(self): + def test_mult_columns(self, table_data): """Access TableColumns with "fancy indexing" and showed that returned TableColumns object still references original data""" - tc2 = self.tc['b', 'c'] - assert tc2[1].name == 'c' + self.t = table_data.Table(table_data.COLS) + self.tc = self.t.columns + + tc2 = self.tc["b", "c"] + assert tc2[1].name == "c" assert tc2[1][1] == 8 - assert tc2[0].name == 'b' + assert tc2[0].name == "b" assert tc2[0][1] == 5 - tc2['c'][1] = 0 - assert self.tc['c'][1] == 0 - assert self.t['c'][1] == 0 + tc2["c"][1] = 0 + assert self.tc["c"][1] == 0 + assert self.t["c"][1] == 0 - def test_column_slice(self): + def test_column_slice(self, table_data): """Access TableColumns with slice and showed that returned TableColumns object still references original data""" + self.t = table_data.Table(table_data.COLS) + self.tc = self.t.columns + tc2 = self.tc[1:3] - assert tc2[1].name == 'c' + assert tc2[1].name == "c" assert tc2[1][1] == 8 - assert tc2[0].name == 'b' + assert tc2[0].name == "b" assert tc2[0][1] == 5 - tc2['c'][1] = 0 - assert self.tc['c'][1] == 0 - assert self.t['c'][1] == 0 + tc2["c"][1] = 0 + assert self.tc["c"][1] == 0 + assert self.t["c"][1] == 0 +@pytest.mark.usefixtures("table_data") class TestTableItems(BaseTestItems): - - def test_column(self): + @pytest.mark.parametrize("idx", [1, np.int64(1), np.array(1)]) + def test_column(self, table_data, idx): """Column access returns REFERENCE to data""" - a = self.t['a'] - assert a[1] == 2 - a[1] = 0 - assert self.t['a'][1] == 0 + self.t = table_data.Table(table_data.COLS) + self.tc = self.t.columns + + a = self.t["a"] + assert a[idx] == 2 + a[idx] = 0 + assert self.t["a"][idx] == 0 - def test_row(self): + @pytest.mark.parametrize("idx", [1, np.int64(1), np.array(1)]) + def test_row(self, table_data, idx): """Row access returns REFERENCE to data""" - row = self.t[1] - assert row['a'] == 2 - assert row[1] == 5 - assert row.columns['a'].attrs_equal(COLS[0]) - assert row.columns['b'].attrs_equal(COLS[1]) - assert row.columns['c'].attrs_equal(COLS[2]) - row[1] = 0 - assert row[1] == 0 - assert self.t['b'][1] == 0 - - def test_table_slice(self): + self.t = table_data.Table(table_data.COLS) + self.tc = self.t.columns + + row = self.t[idx] + assert row["a"] == 2 + assert row[idx] == 5 + assert row.columns["a"].attrs_equal(table_data.COLS[0]) + assert row.columns["b"].attrs_equal(table_data.COLS[1]) + assert row.columns["c"].attrs_equal(table_data.COLS[2]) + + # Check that setting by col index sets the table and row value + row[idx] = 0 + assert row[idx] == 0 + assert row["b"] == 0 + assert self.t["b"][idx] == 0 + assert self.t[idx]["b"] == 0 + + # Check that setting by col name sets the table and row value + row["a"] = 0 + assert row[0] == 0 + assert row["a"] == 0 + assert self.t["a"][1] == 0 + assert self.t[1]["a"] == 0 + + def test_empty_iterable_item(self, table_data): + """ + Table item access with [], (), or np.array([]) returns the same table + with no rows. + """ + self.t = table_data.Table(table_data.COLS) + for item in [], (), np.array([]): + t2 = self.t[item] + assert not t2 + assert len(t2) == 0 + assert t2["a"].attrs_equal(table_data.COLS[0]) + assert t2["b"].attrs_equal(table_data.COLS[1]) + assert t2["c"].attrs_equal(table_data.COLS[2]) + + def test_table_slice(self, table_data): """Table slice returns REFERENCE to data""" + self.t = table_data.Table(table_data.COLS) + self.tc = self.t.columns + t2 = self.t[1:3] - assert np.all(t2['a'] == DATA['a'][1:3]) - assert t2['a'].attrs_equal(COLS[0]) - assert t2['b'].attrs_equal(COLS[1]) - assert t2['c'].attrs_equal(COLS[2]) - t2['a'][0] = 0 - assert np.all(self.t['a'] == np.array([1, 0, 3])) - - def test_fancy_index_slice(self): + assert np.all(t2["a"] == table_data.DATA["a"][1:3]) + assert t2["a"].attrs_equal(table_data.COLS[0]) + assert t2["b"].attrs_equal(table_data.COLS[1]) + assert t2["c"].attrs_equal(table_data.COLS[2]) + t2["a"][0] = 0 + assert np.all(self.t["a"] == np.array([1, 0, 3])) + assert t2.masked == self.t.masked + assert t2._column_class == self.t._column_class + assert isinstance(t2, table_data.Table) + + def test_fancy_index_slice(self, table_data): """Table fancy slice returns COPY of data""" + self.t = table_data.Table(table_data.COLS) + self.tc = self.t.columns + slice = np.array([0, 2]) t2 = self.t[slice] - assert np.all(t2['a'] == DATA['a'][slice]) - assert t2['a'].attrs_equal(COLS[0]) - assert t2['b'].attrs_equal(COLS[1]) - assert t2['c'].attrs_equal(COLS[2]) - t2['a'][0] = 0 - assert np.all(self.t._data == DATA) - assert np.any(t2['a'] != DATA['a']) - - def test_list_index_slice(self): + assert np.all(t2["a"] == table_data.DATA["a"][slice]) + assert t2["a"].attrs_equal(table_data.COLS[0]) + assert t2["b"].attrs_equal(table_data.COLS[1]) + assert t2["c"].attrs_equal(table_data.COLS[2]) + t2["a"][0] = 0 + + assert np.all(self.t.as_array() == table_data.DATA) + assert np.any(t2["a"] != table_data.DATA["a"][slice]) + assert t2.masked == self.t.masked + assert t2._column_class == self.t._column_class + assert isinstance(t2, table_data.Table) + + def test_list_index_slice(self, table_data): """Table list index slice returns COPY of data""" + self.t = table_data.Table(table_data.COLS) + self.tc = self.t.columns + slice = [0, 2] t2 = self.t[slice] - assert np.all(t2['a'] == DATA['a'][slice]) - assert t2['a'].attrs_equal(COLS[0]) - assert t2['b'].attrs_equal(COLS[1]) - assert t2['c'].attrs_equal(COLS[2]) - t2['a'][0] = 0 - assert np.all(self.t._data == DATA) - assert np.any(t2['a'] != DATA['a']) - - def test_select_columns(self): + assert np.all(t2["a"] == table_data.DATA["a"][slice]) + assert t2["a"].attrs_equal(table_data.COLS[0]) + assert t2["b"].attrs_equal(table_data.COLS[1]) + assert t2["c"].attrs_equal(table_data.COLS[2]) + t2["a"][0] = 0 + + assert np.all(self.t.as_array() == table_data.DATA) + assert np.any(t2["a"] != table_data.DATA["a"][slice]) + assert t2.masked == self.t.masked + assert t2._column_class == self.t._column_class + assert isinstance(t2, table_data.Table) + + def test_select_columns(self, table_data): """Select columns returns COPY of data and all column attributes""" - t2 = self.t['a', 'c'] - assert np.all(t2['a'] == DATA['a']) - assert np.all(t2['c'] == DATA['c']) - assert t2['a'].attrs_equal(COLS[0]) - assert t2['c'].attrs_equal(COLS[2]) - t2['a'][0] = 0 - assert np.all(self.t._data == DATA) - assert np.any(t2['a'] != DATA['a']) - - def test_select_bad_column(self): + self.t = table_data.Table(table_data.COLS) + self.tc = self.t.columns + + # try both lists and tuples + for columns in (("a", "c"), ["a", "c"]): + t2 = self.t[columns] + assert np.all(t2["a"] == table_data.DATA["a"]) + assert np.all(t2["c"] == table_data.DATA["c"]) + assert t2["a"].attrs_equal(table_data.COLS[0]) + assert t2["c"].attrs_equal(table_data.COLS[2]) + t2["a"][0] = 0 + assert np.all(self.t.as_array() == table_data.DATA) + assert np.any(t2["a"] != table_data.DATA["a"]) + assert t2.masked == self.t.masked + assert t2._column_class == self.t._column_class + + def test_select_columns_fail(self, table_data): + """Selecting a column that doesn't exist fails""" + self.t = table_data.Table(table_data.COLS) + + with pytest.raises(KeyError) as err: + self.t[["xxxx"]] + assert "'xxxx'" in str(err.value) + + with pytest.raises(KeyError) as err: + self.t[["xxxx", "yyyy"]] + assert "'xxxx'" in str(err.value) + + def test_np_where(self, table_data): + """Select rows using output of np.where""" + t = table_data.Table(table_data.COLS) + # Select last two rows + rows = np.where(t["a"] > 1.5) + t2 = t[rows] + assert np.all(t2["a"] == [2, 3]) + assert np.all(t2["b"] == [5, 6]) + assert isinstance(t2, table_data.Table) + + # Select no rows + rows = np.where(t["a"] > 100) + t2 = t[rows] + assert len(t2) == 0 + assert isinstance(t2, table_data.Table) + + def test_np_integers(self, table_data): + """ + Select rows using numpy integers. This is a regression test for a + py 3.3 failure mode + """ + t = table_data.Table(table_data.COLS) + assert type(t[np.int64(0)]) is Row + + def test_select_bad_column(self, table_data): """Select column name that does not exist""" + self.t = table_data.Table(table_data.COLS) + self.tc = self.t.columns + with pytest.raises(ValueError): - self.t['a', 1] + self.t["a", 1] diff --git a/astropy/table/tests/test_jsviewer.py b/astropy/table/tests/test_jsviewer.py new file mode 100644 index 000000000000..4259df8af016 --- /dev/null +++ b/astropy/table/tests/test_jsviewer.py @@ -0,0 +1,254 @@ +import textwrap +from os.path import abspath, dirname, join + +import pytest + +from astropy import extern +from astropy.coordinates import SkyCoord +from astropy.table.table import Table +from astropy.time import Time +from astropy.utils.compat.optional_deps import ( + HAS_BLEACH, + HAS_IPYDATAGRID, + HAS_IPYTHON, + HAS_PANDAS, +) +from astropy.utils.exceptions import AstropyDeprecationWarning +from astropy.utils.misc import _NOT_OVERWRITING_MSG_MATCH + +EXTERN_DIR = abspath(join(dirname(extern.__file__), "jquery", "data")) +JQUERY_MIN_JS = "jquery-3.6.0.min.js" + + +REFERENCE = """ + + + + + + + + + + + +
+ + + + + + +%(lines)s +
ab
+ + +""" + +TPL = " \n {0}\n {1}\n " + + +def format_lines(col1, col2): + col1_format = getattr(col1.info, "default_format", lambda x: x) + col2_format = getattr(col2.info, "default_format", lambda x: x) + return "\n".join( + TPL.format(col1_format(v1), col2_format(v2)) for v1, v2 in zip(col1, col2) + ) + + +def test_write_jsviewer_default(tmp_path): + t = Table() + t["a"] = [1, 2, 3, 4, 5] + t["b"] = ["a", "b", "c", "d", "e"] + t["a"].unit = "m" + + tmpfile = tmp_path / "test.html" + + t.write(tmpfile, format="jsviewer") + ref = REFERENCE % dict( + lines=format_lines(t["a"], t["b"]), + table_class="display compact", + table_id=f"table{id(t)}", + length="50", + display_length="10, 25, 50, 100, 500, 1000", + datatables_css_url=( + "https://cdn.datatables.net/2.1.8/css/dataTables.dataTables.min.css" + ), + datatables_js_url=("https://cdn.datatables.net/2.1.8/js/dataTables.min.js"), + jquery_url="https://code.jquery.com/" + JQUERY_MIN_JS, + ) + with open(tmpfile) as f: + assert f.read().strip() == ref.strip() + + +def test_write_jsviewer_overwrite(tmp_path): + t = Table() + t["a"] = [1, 2, 3, 4, 5] + t["b"] = ["a", "b", "c", "d", "e"] + t["a"].unit = "m" + tmpfile = tmp_path / "test.html" + + # normal write + t.write(tmpfile, format="jsviewer") + # errors on overwrite + with pytest.raises(OSError, match=_NOT_OVERWRITING_MSG_MATCH): + t.write(tmpfile, format="jsviewer") + # unless specified + t.write(tmpfile, format="jsviewer", overwrite=True) + + +@pytest.mark.parametrize( + "mixin", + [ + Time(["J2000", "J2001"]), + Time([50000.0, 50001.0001], format="mjd"), + SkyCoord(ra=[100.0, 110.0], dec=[-10.0, 10.0], unit="deg"), + ], +) +def test_write_jsviewer_mixin(tmp_path, mixin): + t = Table() + t["a"] = [1, 2] + t["b"] = mixin + t["a"].unit = "m" + + tmpfile = tmp_path / "test.html" + + t.write(tmpfile, format="jsviewer") + ref = REFERENCE % dict( + lines=format_lines(t["a"], t["b"]), + table_class="display compact", + table_id=f"table{id(t)}", + length="50", + display_length="10, 25, 50, 100, 500, 1000", + datatables_css_url=( + "https://cdn.datatables.net/2.1.8/css/dataTables.dataTables.min.css" + ), + datatables_js_url=("https://cdn.datatables.net/2.1.8/js/dataTables.min.js"), + jquery_url="https://code.jquery.com/" + JQUERY_MIN_JS, + ) + with open(tmpfile) as f: + assert f.read().strip() == ref.strip() + + +@pytest.mark.skipif(not HAS_BLEACH, reason="requires bleach") +def test_write_jsviewer_options(tmp_path): + t = Table() + t["a"] = [1, 2, 3, 4, 5] + t["b"] = ["a", "b", "c", "d", "e"] + t["a"].unit = "m" + + tmpfile = tmp_path / "test.html" + t.write( + tmpfile, + format="jsviewer", + table_id="test", + max_lines=3, + jskwargs={"display_length": 5}, + table_class="display hover", + htmldict=dict(raw_html_cols="b"), + ) + + ref = REFERENCE % dict( + lines=format_lines(t["a"][:3], t["b"][:3]), + table_class="display hover", + table_id="test", + length="5", + display_length="5, 10, 25, 50, 100, 500, 1000", + datatables_css_url=( + "https://cdn.datatables.net/2.1.8/css/dataTables.dataTables.min.css" + ), + datatables_js_url=("https://cdn.datatables.net/2.1.8/js/dataTables.min.js"), + jquery_url="https://code.jquery.com/" + JQUERY_MIN_JS, + ) + with open(tmpfile) as f: + assert f.read().strip() == ref.strip() + + +@pytest.mark.skipif(not HAS_IPYTHON, reason="requires IPython") +def test_show_in_notebook_classic(): + t = Table() + t["a"] = [1, 2, 3, 4, 5] + t["b"] = ["b", "c", "a", "d", "e"] + + with pytest.warns(AstropyDeprecationWarning): + htmlstr_windx = t.show_in_notebook( + backend="classic" + ).data # should default to 'idx' + htmlstr_windx_named = t.show_in_notebook( + backend="classic", show_row_index="realidx" + ).data + htmlstr_woindx = t.show_in_notebook( + backend="classic", show_row_index=False + ).data + + assert ( + textwrap.dedent( + """ + idxab + 01b + 12c + 23a + 34d + 45e + """ + ).strip() + in htmlstr_windx + ) + + assert ( + "realidxab" + in htmlstr_windx_named + ) + + assert "ab" in htmlstr_woindx + + +@pytest.mark.skipif( + not HAS_IPYDATAGRID or not HAS_PANDAS, reason="requires ipydatagrid and pandas" +) +# https://github.com/bqplot/bqplot/issues/1624 and such +@pytest.mark.filterwarnings(r"ignore:((.|\n)*)traitlets((.|\n)*):DeprecationWarning") +def test_show_in_notebook_ipydatagrid(): + from ipydatagrid import DataGrid + + t = Table() + dg = t.show_in_notebook() + assert isinstance(dg, DataGrid) + + +def test_show_in_notebook_invalid_backend(): + t = Table() + with pytest.raises(NotImplementedError, match=".* backend is not supported"): + t.show_in_notebook(backend="foo") diff --git a/astropy/table/tests/test_masked.py b/astropy/table/tests/test_masked.py new file mode 100644 index 000000000000..b9d8fb6b4366 --- /dev/null +++ b/astropy/table/tests/test_masked.py @@ -0,0 +1,696 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +"""Test behavior related to masked tables""" + +import numpy as np +import numpy.ma as ma +import pytest +from numpy.testing import assert_array_equal + +import astropy.units as u +from astropy.table import Column, MaskedColumn, QTable, Table +from astropy.table.column import BaseColumn +from astropy.time import Time +from astropy.utils.masked import Masked + + +class SetupData: + def setup_method(self, method): + self.a = MaskedColumn(name="a", data=[1, 2, 3], fill_value=1) + self.b = MaskedColumn(name="b", data=[4, 5, 6], mask=True) + self.c = MaskedColumn(name="c", data=[7, 8, 9], mask=False) + self.d_mask = np.array([False, True, False]) + self.d = MaskedColumn(name="d", data=[7, 8, 7], mask=self.d_mask) + self.t = Table([self.a, self.b], masked=True) + self.ca = Column(name="ca", data=[1, 2, 3]) + self.sc = MaskedColumn( + name="sc", + data=[(1, 1.0), (2, 2.0), (3, 3.0)], + dtype="i8,f8", + fill_value=(0, -1.0), + ) + + +class TestPprint(SetupData): + def test_pformat(self): + assert self.t.pformat() == [ + " a b ", + "--- ---", + " 1 --", + " 2 --", + " 3 --", + ] + + +class TestFilled: + """Test the filled method in MaskedColumn and Table""" + + def setup_method(self, method): + mask = [True, False, False] + self.meta = {"a": 1, "b": [2, 3]} + self.a = MaskedColumn( + name="a", data=[1, 2, 3], fill_value=10, mask=mask, meta={"a": 1} + ) + self.b = MaskedColumn( + name="b", data=[4.0, 5.0, 6.0], fill_value=10.0, mask=mask + ) + self.c = MaskedColumn(name="c", data=["7", "8", "9"], fill_value="1", mask=mask) + + def test_filled_column(self): + f = self.a.filled() + assert np.all(f == [10, 2, 3]) + assert isinstance(f, Column) + assert not isinstance(f, MaskedColumn) + + # Confirm copy, not ref + assert f.meta["a"] == 1 + f.meta["a"] = 2 + f[1] = 100 + assert self.a[1] == 2 + assert self.a.meta["a"] == 1 + + # Fill with arg fill_value not column fill_value + f = self.a.filled(20) + assert np.all(f == [20, 2, 3]) + + f = self.b.filled() + assert np.all(f == [10.0, 5.0, 6.0]) + assert isinstance(f, Column) + + f = self.c.filled() + assert np.all(f == ["1", "8", "9"]) + assert isinstance(f, Column) + + def test_filled_masked_table(self, tableclass): + t = tableclass([self.a, self.b, self.c], meta=self.meta) + + f = t.filled() + assert isinstance(f, Table) + assert f.masked is False + assert np.all(f["a"] == [10, 2, 3]) + assert np.allclose(f["b"], [10.0, 5.0, 6.0]) + assert np.all(f["c"] == ["1", "8", "9"]) + + # Confirm copy, not ref + assert f.meta["b"] == [2, 3] + f.meta["b"][0] = 20 + assert t.meta["b"] == [2, 3] + f["a"][2] = 100 + assert t["a"][2] == 3 + + def test_filled_unmasked_table(self, tableclass): + t = tableclass([(1, 2), ("3", "4")], names=("a", "b"), meta=self.meta) + f = t.filled() + assert isinstance(f, Table) + assert f.masked is False + assert np.all(f["a"] == t["a"]) + assert np.all(f["b"] == t["b"]) + + # Confirm copy, not ref + assert f.meta["b"] == [2, 3] + f.meta["b"][0] = 20 + assert t.meta["b"] == [2, 3] + f["a"][1] = 100 + assert t["a"][1] == 2 + + +class TestFillValue(SetupData): + """Test setting and getting fill value in MaskedColumn and Table""" + + def test_init_set_fill_value(self): + """Check that setting fill_value in the MaskedColumn init works""" + assert self.a.fill_value == 1 + c = MaskedColumn(name="c", data=["xxxx", "yyyy"], fill_value="none") + assert c.fill_value == "none" + + def test_set_get_fill_value_for_bare_column(self): + """Check set and get of fill value works for bare Column""" + self.d.fill_value = -999 + assert self.d.fill_value == -999 + assert np.all(self.d.filled() == [7, -999, 7]) + + def test_set_get_fill_value_for_str_column(self): + c = MaskedColumn(name="c", data=["xxxx", "yyyy"], mask=[True, False]) + # assert np.all(c.filled() == ['N/A', 'yyyy']) + c.fill_value = "ABCDEF" + assert c.fill_value == "ABCD" # string truncated to dtype length + assert np.all(c.filled() == ["ABCD", "yyyy"]) + assert np.all(c.filled("XY") == ["XY", "yyyy"]) + + def test_set_get_fill_value_for_structured_column(self): + assert self.sc.fill_value == np.array((0, -1.0), self.sc.dtype) + sc = self.sc.copy() + assert sc.fill_value.item() == (0, -1.0) + sc.fill_value = (-1, np.inf) + assert sc.fill_value == np.array((-1, np.inf), self.sc.dtype) + sc2 = MaskedColumn(sc, fill_value=(-2, -np.inf)) + assert sc2.fill_value == np.array((-2, -np.inf), sc2.dtype) + + def test_table_column_mask_not_ref(self): + """Table column mask is not ref of original column mask""" + self.b.fill_value = -999 + assert self.t["b"].fill_value != -999 + + def test_set_get_fill_value_for_table_column(self): + """Check set and get of fill value works for Column in a Table""" + self.t["b"].fill_value = 1 + assert self.t["b"].fill_value == 1 + assert np.all(self.t["b"].filled() == [1, 1, 1]) + + def test_data_attribute_fill_and_mask(self): + """Check that .data attribute preserves fill_value and mask""" + self.t["b"].fill_value = 1 + self.t["b"].mask = [True, False, True] + assert self.t["b"].data.fill_value == 1 + assert np.all(self.t["b"].data.mask == [True, False, True]) + + +class TestMaskedColumnInit(SetupData): + """Initialization of a masked column""" + + def test_set_mask_and_not_ref(self): + """Check that mask gets set properly and that it is a copy, not ref""" + assert np.all(~self.a.mask) + assert np.all(self.b.mask) + assert np.all(~self.c.mask) + assert np.all(self.d.mask == self.d_mask) + self.d.mask[0] = True + assert not np.all(self.d.mask == self.d_mask) + + def test_set_mask_from_list(self): + """Set mask from a list""" + mask_list = [False, True, False] + a = MaskedColumn(name="a", data=[1, 2, 3], mask=mask_list) + assert np.all(a.mask == mask_list) + + def test_override_existing_mask(self): + """Override existing mask values""" + mask_list = [False, True, False] + b = MaskedColumn(name="b", data=self.b, mask=mask_list) + assert np.all(b.mask == mask_list) + + def test_incomplete_mask_spec(self): + """Incomplete mask specification raises MaskError""" + mask_list = [False, True] + with pytest.raises(ma.MaskError): + MaskedColumn(name="b", length=4, mask=mask_list) + + +DTYPES_TEST_INIT = ["?", "b", "i2", "f4", "c8", "S", "U", "O"] + [ + # new in numpy 2 + np.dtypes.StrDType, # same as "U" + np.dtypes.BytesDType, # same as "S" + np.dtypes.StringDType, +] + + +class TestTableInit(SetupData): + """Initializing a table""" + + @pytest.mark.parametrize("dtype", DTYPES_TEST_INIT) + @pytest.mark.parametrize("shape", ((8,), (4, 2), (2, 2, 2))) + def test_init_from_sequence_data_numeric_typed(self, dtype, shape): + """Test init from list or list of lists with dtype specified, optionally + including an np.ma.masked element. + """ + # Make data of correct dtype and shape, then turn into a list, + # then use that to init Table with spec'd dtype. + data = list(range(8)) + np_data = np.array(data, dtype=dtype).reshape(shape) + np_data_list = np_data.tolist() + t = Table([np_data_list], dtype=[dtype]) + col = t["col0"] + assert col.dtype == np_data.dtype + assert np.all(col == np_data) + assert type(col) is Column + + # Introduce np.ma.masked in the list input and confirm dtype still OK. + if len(shape) == 1: + np_data_list[-1] = np.ma.masked + elif len(shape) == 2: + np_data_list[-1][-1] = np.ma.masked + else: + np_data_list[-1][-1][-1] = np.ma.masked + last_idx = tuple(-1 for _ in shape) + t = Table([np_data_list], dtype=[dtype]) + col = t["col0"] + assert col.dtype == np_data.dtype + assert np.all(col == np_data) + assert col.mask[last_idx] + assert type(col) is MaskedColumn + + @pytest.mark.parametrize("dtype", DTYPES_TEST_INIT) + @pytest.mark.parametrize("shape", ((8,), (4, 2), (2, 2, 2))) + def test_init_from_sequence_data_numeric_untyped(self, dtype, shape): + """Test init from list or list of lists with dtype NOT specified, + optionally including an np.ma.masked element. + """ + data = list(range(8)) + np_data = np.array(data, dtype=dtype).reshape(shape) + np_data_list = np_data.tolist() + t = Table([np_data_list]) + # Grab the dtype that numpy assigns for the Python list inputs + dtype_expected = t["col0"].dtype + + # Introduce np.ma.masked in the list input and confirm dtype still OK. + if len(shape) == 1: + np_data_list[-1] = np.ma.masked + elif len(shape) == 2: + np_data_list[-1][-1] = np.ma.masked + else: + np_data_list[-1][-1][-1] = np.ma.masked + last_idx = tuple(-1 for _ in shape) + t = Table([np_data_list]) + col = t["col0"] + + # Confirm dtype is same as for untype list input w/ no mask + assert col.dtype == dtype_expected + assert np.all(col == np_data) + assert col.mask[last_idx] + assert type(col) is MaskedColumn + + def test_initialization_with_all_columns(self): + t1 = Table([self.a, self.b, self.c, self.d, self.ca, self.sc]) + assert t1.colnames == ["a", "b", "c", "d", "ca", "sc"] + # Check we get the same result by passing in as list of dict. + # (Regression test for error uncovered by scintillometry package.) + lofd = [{k: row[k] for k in t1.colnames} for row in t1] + t2 = Table(lofd) + for k in t1.colnames: + assert t1[k].dtype == t2[k].dtype + assert np.all(t1[k] == t2[k]) in (True, np.ma.masked) + assert np.all( + getattr(t1[k], "mask", False) == getattr(t2[k], "mask", False) + ) + + def test_mask_false_if_input_mask_not_true(self): + """Masking is always False if initial masked arg is not True""" + t = Table([self.ca, self.a]) + assert t.masked is False # True before astropy 4.0 + t = Table([self.ca]) + assert t.masked is False + t = Table([self.ca, ma.array([1, 2, 3])]) + assert t.masked is False # True before astropy 4.0 + + def test_mask_false_if_no_input_masked(self): + """Masking not true if not (requested or input requires mask)""" + t0 = Table([[3, 4]], masked=False) + t1 = Table(t0, masked=True) + t2 = Table(t1, masked=False) + assert not t0.masked + assert t1.masked + assert not t2.masked + + def test_mask_property(self): + t = self.t + # Access table mask (boolean structured array) by column name + assert np.all(t.mask["a"] == np.array([False, False, False])) + assert np.all(t.mask["b"] == np.array([True, True, True])) + # Check that setting mask from table mask has the desired effect on column + t.mask["b"] = np.array([False, True, False]) + assert np.all(t["b"].mask == np.array([False, True, False])) + # Non-masked table returns None for mask attribute + t2 = Table([self.ca], masked=False) + assert t2.mask is None + # Set mask property globally and verify local correctness + for mask in (True, False): + t.mask = mask + for name in ("a", "b"): + assert np.all(t[name].mask == mask) + + +class TestAddColumn: + def test_add_masked_column_to_masked_table(self): + t = Table(masked=True) + assert t.masked + t.add_column(MaskedColumn(name="a", data=[1, 2, 3], mask=[0, 1, 0])) + assert t.masked + t.add_column(MaskedColumn(name="b", data=[4, 5, 6], mask=[1, 0, 1])) + assert t.masked + assert isinstance(t["a"], MaskedColumn) + assert isinstance(t["b"], MaskedColumn) + assert np.all(t["a"] == np.array([1, 2, 3])) + assert np.all(t["a"].mask == np.array([0, 1, 0], bool)) + assert np.all(t["b"] == np.array([4, 5, 6])) + assert np.all(t["b"].mask == np.array([1, 0, 1], bool)) + + def test_add_masked_column_to_non_masked_table(self): + t = Table(masked=False) + assert not t.masked + t.add_column(Column(name="a", data=[1, 2, 3])) + assert not t.masked + t.add_column(MaskedColumn(name="b", data=[4, 5, 6], mask=[1, 0, 1])) + assert not t.masked # Changed in 4.0, table no longer auto-upgrades + assert isinstance(t["a"], Column) # Was MaskedColumn before 4.0 + assert isinstance(t["b"], MaskedColumn) + assert np.all(t["a"] == np.array([1, 2, 3])) + assert not hasattr(t["a"], "mask") + assert np.all(t["b"] == np.array([4, 5, 6])) + assert np.all(t["b"].mask == np.array([1, 0, 1], bool)) + + def test_add_non_masked_column_to_masked_table(self): + t = Table(masked=True) + assert t.masked + t.add_column(Column(name="a", data=[1, 2, 3])) + assert t.masked + t.add_column(MaskedColumn(name="b", data=[4, 5, 6], mask=[1, 0, 1])) + assert t.masked + assert isinstance(t["a"], MaskedColumn) + assert isinstance(t["b"], MaskedColumn) + assert np.all(t["a"] == np.array([1, 2, 3])) + assert np.all(t["a"].mask == np.array([0, 0, 0], bool)) + assert np.all(t["b"] == np.array([4, 5, 6])) + assert np.all(t["b"].mask == np.array([1, 0, 1], bool)) + + def test_convert_to_masked_table_only_if_necessary(self): + # Do not convert to masked table, if new column has no masked value. + # See #1185 for details. + t = Table(masked=False) + assert not t.masked + t.add_column(Column(name="a", data=[1, 2, 3])) + assert not t.masked + t.add_column(MaskedColumn(name="b", data=[4, 5, 6], mask=[0, 0, 0])) + assert not t.masked + assert np.all(t["a"] == np.array([1, 2, 3])) + assert np.all(t["b"] == np.array([4, 5, 6])) + + +class TestRenameColumn: + def test_rename_masked_column(self): + t = Table(masked=True) + t.add_column(MaskedColumn(name="a", data=[1, 2, 3], mask=[0, 1, 0])) + t["a"].fill_value = 42 + t.rename_column("a", "b") + assert t.masked + assert np.all(t["b"] == np.array([1, 2, 3])) + assert np.all(t["b"].mask == np.array([0, 1, 0], bool)) + assert t["b"].fill_value == 42 + assert t.colnames == ["b"] + + +class TestRemoveColumn: + def test_remove_masked_column(self): + t = Table(masked=True) + t.add_column(MaskedColumn(name="a", data=[1, 2, 3], mask=[0, 1, 0])) + t["a"].fill_value = 42 + t.add_column(MaskedColumn(name="b", data=[4, 5, 6], mask=[1, 0, 1])) + t.remove_column("b") + assert t.masked + assert np.all(t["a"] == np.array([1, 2, 3])) + assert np.all(t["a"].mask == np.array([0, 1, 0], bool)) + assert t["a"].fill_value == 42 + assert t.colnames == ["a"] + + +class TestAddRow: + def test_add_masked_row_to_masked_table_iterable(self): + t = Table(masked=True) + t.add_column(MaskedColumn(name="a", data=[1], mask=[0])) + t.add_column(MaskedColumn(name="b", data=[4], mask=[1])) + t.add_row([2, 5], mask=[1, 0]) + t.add_row([3, 6], mask=[0, 1]) + assert t.masked + assert np.all(np.array(t["a"]) == np.array([1, 2, 3])) + assert np.all(t["a"].mask == np.array([0, 1, 0], bool)) + assert np.all(np.array(t["b"]) == np.array([4, 5, 6])) + assert np.all(t["b"].mask == np.array([1, 0, 1], bool)) + + def test_add_masked_row_to_masked_table_mapping1(self): + t = Table(masked=True) + t.add_column(MaskedColumn(name="a", data=[1], mask=[0])) + t.add_column(MaskedColumn(name="b", data=[4], mask=[1])) + t.add_row({"b": 5, "a": 2}, mask={"a": 1, "b": 0}) + t.add_row({"a": 3, "b": 6}, mask={"b": 1, "a": 0}) + assert t.masked + assert np.all(np.array(t["a"]) == np.array([1, 2, 3])) + assert np.all(t["a"].mask == np.array([0, 1, 0], bool)) + assert np.all(np.array(t["b"]) == np.array([4, 5, 6])) + assert np.all(t["b"].mask == np.array([1, 0, 1], bool)) + + def test_add_masked_row_to_masked_table_mapping2(self): + # When adding values to a masked table, if the mask is specified as a + # dict, then values not specified will have mask values set to True + t = Table(masked=True) + t.add_column(MaskedColumn(name="a", data=[1], mask=[0])) + t.add_column(MaskedColumn(name="b", data=[4], mask=[1])) + t.add_row({"b": 5}, mask={"b": 0}) + t.add_row({"a": 3}, mask={"a": 0}) + assert t.masked + assert t["a"][0] == 1 and t["a"][2] == 3 + assert np.all(t["a"].mask == np.array([0, 1, 0], bool)) + assert t["b"][1] == 5 + assert np.all(t["b"].mask == np.array([1, 0, 1], bool)) + + def test_add_masked_row_to_masked_table_mapping3(self): + # When adding values to a masked table, if mask is not passed to + # add_row, then the mask should be set to False if values are present + # and True if not. + t = Table(masked=True) + t.add_column(MaskedColumn(name="a", data=[1], mask=[0])) + t.add_column(MaskedColumn(name="b", data=[4], mask=[1])) + t.add_row({"b": 5}) + t.add_row({"a": 3}) + assert t.masked + assert t["a"][0] == 1 and t["a"][2] == 3 + assert np.all(t["a"].mask == np.array([0, 1, 0], bool)) + assert t["b"][1] == 5 + assert np.all(t["b"].mask == np.array([1, 0, 1], bool)) + + def test_add_masked_row_to_masked_table_mapping4(self): + # When adding values to a masked table, if the mask is specified as a + # dict, then keys in values should match keys in mask + t = Table(masked=True) + t.add_column(MaskedColumn(name="a", data=[1], mask=[0])) + t.add_column(MaskedColumn(name="b", data=[4], mask=[1])) + with pytest.raises(ValueError) as exc: + t.add_row({"b": 5}, mask={"a": True}) + assert exc.value.args[0] == "keys in mask should match keys in vals" + + def test_add_masked_row_to_masked_table_mismatch(self): + t = Table(masked=True) + t.add_column(MaskedColumn(name="a", data=[1], mask=[0])) + t.add_column(MaskedColumn(name="b", data=[4], mask=[1])) + with pytest.raises(TypeError) as exc: + t.add_row([2, 5], mask={"a": 1, "b": 0}) + assert exc.value.args[0] == "Mismatch between type of vals and mask" + with pytest.raises(TypeError) as exc: + t.add_row({"b": 5, "a": 2}, mask=[1, 0]) + assert exc.value.args[0] == "Mismatch between type of vals and mask" + + def test_add_masked_row_to_non_masked_table_iterable(self): + t = Table(masked=False) + t["a"] = [1] + t["b"] = [4] + t["c"] = Time([1], format="cxcsec") + + tm = Time(2, format="cxcsec") + assert not t.masked + t.add_row([2, 5, tm]) + assert not t.masked + t.add_row([3, 6, tm], mask=[0, 1, 1]) + assert not t.masked + + assert type(t["a"]) is Column + assert type(t["b"]) is MaskedColumn + assert type(t["c"]) is Time + + assert np.all(t["a"] == [1, 2, 3]) + assert np.all(t["b"].data == [4, 5, 6]) + assert np.all(t["b"].mask == [False, False, True]) + assert np.all(t["c"][:2] == Time([1, 2], format="cxcsec")) + assert np.all(t["c"].mask == [False, False, True]) + + def test_add_row_cannot_mask_column_raises_typeerror(self): + t = QTable() + t["a"] = [1, 2] * u.m + t.add_row((3 * u.m,)) # No problem + with pytest.raises(ValueError) as exc: + t.add_row((3 * u.m,), mask=(True,)) + assert exc.value.args[0].splitlines() == [ + "Unable to insert row because of exception in column 'a':", + "mask was supplied for column 'a' but it does not support masked values", + ] + + +def test_setting_from_masked_column(): + """Test issue in #2997""" + mask_b = np.array([True, True, False, False]) + for select in (mask_b, slice(0, 2)): + t = Table(masked=True) + t["a"] = Column([1, 2, 3, 4]) + t["b"] = MaskedColumn([11, 22, 33, 44], mask=mask_b) + t["c"] = MaskedColumn([111, 222, 333, 444], mask=[True, False, True, False]) + + t["b"][select] = t["c"][select] + assert t["b"][1] == t[1]["b"] + assert t["b"][0] is np.ma.masked # Original state since t['c'][0] is masked + assert t["b"][1] == 222 # New from t['c'] since t['c'][1] is unmasked + assert t["b"][2] == 33 + assert t["b"][3] == 44 + assert np.all( + t["b"].mask == t.mask["b"] + ) # Avoid t.mask in general, this is for testing + + mask_before_add = t.mask.copy() + t["d"] = np.arange(len(t)) + assert np.all(t.mask["b"] == mask_before_add["b"]) + + +def test_coercing_fill_value_type(): + """ + Test that masked column fill_value is coerced into the correct column type. + """ + # This is the original example posted on the astropy@scipy mailing list + t = Table({"a": ["1"]}, masked=True) + t["a"].set_fill_value("0") + t2 = Table(t, names=["a"], dtype=[np.int32]) + assert isinstance(t2["a"].fill_value, np.int32) + + # Unit test the same thing. + c = MaskedColumn(["1"]) + c.set_fill_value("0") + c2 = MaskedColumn(c, dtype=np.int32) + assert isinstance(c2.fill_value, np.int32) + + +def test_mask_copy(): + """Test that the mask is copied when copying a table (issue #7362).""" + + c = MaskedColumn([1, 2], mask=[False, True]) + c2 = MaskedColumn(c, copy=True) + c2.mask[0] = True + assert np.all(c.mask == [False, True]) + assert np.all(c2.mask == [True, True]) + + +def test_masked_as_array_with_mixin(): + """Test that as_array() and Table.mask attr work with masked mixin columns""" + t = Table() + t["a"] = Time([1, 2], format="cxcsec") + t["b"] = [3, 4] + t["c"] = [5, 6] * u.m + + # With no mask, the output should be ndarray + ta = t.as_array() + assert isinstance(ta, np.ndarray) and not isinstance(ta, np.ma.MaskedArray) + + # With a mask, output is MaskedArray + t["a"][1] = np.ma.masked + ta = t.as_array() + assert isinstance(ta, np.ma.MaskedArray) + assert np.all(ta["a"].mask == [False, True]) + assert np.isclose(ta["a"][0].cxcsec, 1.0) + assert not np.any(ta["b"].mask) + assert not np.any(ta["c"].mask) + + # Check table ``mask`` property + tm = t.mask + assert np.all(tm["a"] == [False, True]) + assert not np.any(tm["b"]) + assert not np.any(tm["c"]) + + +def test_masked_column_with_unit_in_qtable(): + """Test that adding a MaskedColumn with a unit to QTable creates a MaskedQuantity.""" + MaskedQuantity = Masked(u.Quantity) + + t = QTable() + t["a"] = MaskedColumn([1, 2]) + assert isinstance(t["a"], MaskedColumn) + + t["b"] = MaskedColumn([1, 2], unit=u.m) + assert isinstance(t["b"], MaskedQuantity) + assert not np.any(t["b"].mask) + + t["c"] = MaskedColumn([1, 2], unit=u.m, mask=[True, False]) + assert isinstance(t["c"], MaskedQuantity) + assert np.all(t["c"].mask == [True, False]) + # Regular Column is still converted to regular Quantity + t["d"] = Column([1, 2], unit=u.cm) + assert not isinstance(t["d"], MaskedQuantity) + assert isinstance(t["d"], u.Quantity) + # But not if the table is masked. + t2 = QTable(t, masked=True) + assert isinstance(t2["d"], MaskedQuantity) + t2["e"] = Column([1, 2], unit=u.cm) + assert isinstance(t2["e"], MaskedQuantity) + assert not np.any(t2["e"].mask) + + +def test_masked_quantity_in_table(): + MaskedQuantity = Masked(u.Quantity) + t = Table() + t["b"] = MaskedQuantity([1, 2], unit=u.m) + assert isinstance(t["b"], MaskedColumn) + assert not np.any(t["b"].mask) + + t["c"] = MaskedQuantity([1, 2], unit=u.m, mask=[True, False]) + assert isinstance(t["c"], MaskedColumn) + assert np.all(t["c"].mask == [True, False]) + + t2 = Table(t, masked=True) + t2["d"] = u.Quantity([1, 2], unit=u.cm) + assert isinstance(t2["d"], MaskedColumn) + assert not np.any(t2["d"].mask) + + +def test_masked_column_data_attribute_is_plain_masked_array(): + c = MaskedColumn([1, 2], mask=[False, True]) + c_data = c.data + assert type(c_data) is np.ma.MaskedArray + assert type(c_data.data) is np.ndarray + + +def test_mask_slicing_count_array_finalize(): + """Check that we don't finalize MaskedColumn too often. + + Regression test for gh-6721. + """ + + # Create a new BaseColumn class that counts how often + # ``__array_finalize__`` is called. + class MyBaseColumn(BaseColumn): + counter = 0 + + def __array_finalize__(self, obj): + super().__array_finalize__(obj) + MyBaseColumn.counter += 1 + + # Base a new MaskedColumn class on it. The normal MaskedColumn + # hardcodes the initialization to BaseColumn, so we exchange that. + class MyMaskedColumn(MaskedColumn, Column, MyBaseColumn): + def __new__(cls, *args, **kwargs): + self = super().__new__(cls, *args, **kwargs) + self._baseclass = MyBaseColumn + return self + + # Creation really needs 2 finalizations (once for the BaseColumn + # call inside ``__new__`` and once when the view as a MaskedColumn + # is taken), but since the first is hardcoded, we do not capture it + # and thus the count is only 1. + c = MyMaskedColumn([1, 2], mask=[False, True]) + assert MyBaseColumn.counter == 1 + # slicing should need only one ``__array_finalize__`` (used to be 3). + c0 = c[:] + assert MyBaseColumn.counter == 2 + # repr should need none (used to be 2!!) + repr(c0) + assert MyBaseColumn.counter == 2 + + +def test_set_masked_bytes_column(): + mask = [True, False, True] + mc = MaskedColumn([b"a", b"b", b"c"], mask=mask) + mc[:] = mc + assert (mc.mask == mask).all() + + +def test_qtable_masked_true_basics(): + # Explicit regression test for gh-16495. + tab = QTable([[1, 1] * u.mJy], names=["test"], masked=True) + assert isinstance(tab["test"], Masked) + assert isinstance(tab["test"], u.Quantity) + assert not np.any(tab["test"].mask) + tab["test"].mask[0] = True + assert_array_equal(tab["test"].mask, [True, False]) + tab["test"].mask |= [True, True] + assert_array_equal(tab["test"].mask, [True, True]) diff --git a/astropy/table/tests/test_mixin.py b/astropy/table/tests/test_mixin.py new file mode 100644 index 000000000000..d3d43d53f684 --- /dev/null +++ b/astropy/table/tests/test_mixin.py @@ -0,0 +1,1061 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + + +import copy +import pickle +from io import StringIO + +import numpy as np +import pytest + +from astropy import coordinates, time +from astropy import units as u +from astropy.coordinates import EarthLocation, SkyCoord +from astropy.coordinates.tests.test_representation import representation_equal +from astropy.io.ascii.connect import _get_connectors_table +from astropy.table import ( + Column, + NdarrayMixin, + QTable, + Table, + hstack, + join, + serialize, + table_helpers, + vstack, +) +from astropy.table.column import BaseColumn +from astropy.table.serialize import represent_mixins_as_columns +from astropy.table.table_helpers import ArrayWrapper +from astropy.utils.data_info import ParentDtypeInfo +from astropy.utils.exceptions import AstropyUserWarning +from astropy.utils.metadata import MergeConflictWarning + +from .conftest import MIXIN_COLS + + +def test_attributes(mixin_cols): + """ + Required attributes for a column can be set. + """ + m = mixin_cols["m"] + m.info.name = "a" + assert m.info.name == "a" + + m.info.description = "a" + assert m.info.description == "a" + + # Cannot set unit for these classes + if isinstance( + m, + ( + u.Quantity, + coordinates.SkyCoord, + time.Time, + time.TimeDelta, + coordinates.BaseRepresentationOrDifferential, + coordinates.StokesCoord, + ), + ): + with pytest.raises(AttributeError): + m.info.unit = u.m + else: + m.info.unit = u.m + assert m.info.unit is u.m + + m.info.format = "a" + assert m.info.format == "a" + + m.info.meta = {"a": 1} + assert m.info.meta == {"a": 1} + + with pytest.raises(AttributeError): + m.info.bad_attr = 1 + + with pytest.raises(AttributeError): + m.info.bad_attr + + +def check_mixin_type(table, table_col, in_col): + # We check for QuantityInfo rather than just isinstance(col, u.Quantity) + # since we want to treat EarthLocation as a mixin, even though it is + # a Quantity subclass. + if ( + isinstance(in_col.info, u.QuantityInfo) and type(table) is not QTable + ) or isinstance(in_col, Column): + assert type(table_col) is table.ColumnClass + else: + assert type(table_col) is type(in_col) + + # Make sure in_col got copied and creating table did not touch it + assert in_col.info.name is None + + +def test_make_table(table_types, mixin_cols): + """ + Make a table with the columns in mixin_cols, which is an ordered dict of + three cols: 'a' and 'b' are table_types.Column type, and 'm' is a mixin. + """ + t = table_types.Table(mixin_cols) + check_mixin_type(t, t["m"], mixin_cols["m"]) + + cols = list(mixin_cols.values()) + t = table_types.Table(cols, names=("i", "a", "b", "m")) + check_mixin_type(t, t["m"], mixin_cols["m"]) + + t = table_types.Table(cols) + check_mixin_type(t, t["col3"], mixin_cols["m"]) + + +def test_io_ascii_write(): + """ + Test that table with mixin column can be written by io.ascii for + every pure Python writer. No validation of the output is done, + this just confirms no exceptions. + """ + t = QTable(MIXIN_COLS) + for fmt in _get_connectors_table(): + if ( + fmt["Write"] + and ".fast_" not in fmt["Format"] + and fmt["Format"] != "ascii.tdat" + ): + out = StringIO() + t.write(out, format=fmt["Format"]) + + +def test_votable_quantity_write(tmp_path): + """ + Test that table with Quantity mixin column can be round-tripped by + io.votable. Note that FITS and HDF5 mixin support are tested (much more + thoroughly) in their respective subpackage tests + (io/fits/tests/test_connect.py and io/misc/tests/test_hdf5.py). + """ + t = QTable() + t["a"] = u.Quantity([1, 2, 4], unit="nm") + + filename = tmp_path / "table-tmp" + t.write(filename, format="votable", overwrite=True) + qt = QTable.read(filename, format="votable") + assert isinstance(qt["a"], u.Quantity) + assert qt["a"].unit == "nm" + + +@pytest.mark.remote_data +@pytest.mark.parametrize("table_types", (Table, QTable)) +def test_io_time_write_fits_standard(tmp_path, table_types): + """ + Test that table with Time mixin columns can be written by io.fits. + Validation of the output is done. Test that io.fits writes a table + containing Time mixin columns that can be partially round-tripped + (metadata scale, location). + + Note that we postpone checking the "local" scale, since that cannot + be done with format 'cxcsec', as it requires an epoch. + """ + t = table_types([[1, 2], ["string", "column"]]) + for scale in time.STANDARD_TIME_SCALES: + t["a" + scale] = time.Time( + [[1, 2], [3, 4]], + format="cxcsec", + scale=scale, + location=EarthLocation(-2446354, 4237210, 4077985, unit="m"), + ) + t["b" + scale] = time.Time( + ["1999-01-01T00:00:00.123456789", "2010-01-01T00:00:00"], scale=scale + ) + t["c"] = [3.0, 4.0] + + filename = tmp_path / "table-tmp" + + # Show that FITS format succeeds + with pytest.warns(AstropyUserWarning) as record: + t.write(filename, format="fits", overwrite=True) + + # The exact sequence probably + # does not matter too much, so we'll just try to match the *set* of warnings + warnings = {wm.message.args[0] for wm in record} + + expected = { + ( + 'Time Column "btai" has no specified location, ' + "but global Time Position is present, " + "which will be the default for this column in FITS specification." + ), + ( + 'Time Column "btdb" has no specified location, ' + "but global Time Position is present, " + "which will be the default for this column in FITS specification." + ), + ( + 'Time Column "btcg" has no specified location, ' + "but global Time Position is present, " + "which will be the default for this column in FITS specification." + ), + ( + 'Time Column "btt" has no specified location, ' + "but global Time Position is present, which will be the default " + "for this column in FITS specification." + ), + ( + 'Time Column "butc" has no specified location, ' + "but global Time Position is present, " + "which will be the default for this column in FITS specification." + ), + ( + 'Earth Location "TOPOCENTER" for Time Column "atdb"' + ' is incompatible with scale "TDB".' + ), + ( + 'Earth Location "TOPOCENTER" for Time Column "atcb"' + ' is incompatible with scale "TCB".' + ), + ( + 'Time Column "but1" has no specified location, ' + "but global Time Position is present, " + "which will be the default for this column in FITS specification." + ), + ( + 'Time Column "btcb" has no specified location, ' + "but global Time Position is present, " + "which will be the default for this column in FITS specification." + ), + } + assert warnings == expected, f"Got some unexpected warnings\n{warnings - expected}" + with pytest.warns( + AstropyUserWarning, + match='Time column reference position "TRPOSn" is not specified', + ): + tm = table_types.read(filename, format="fits", astropy_native=True) + + for scale in time.STANDARD_TIME_SCALES: + for ab in ("a", "b"): + name = ab + scale + + # Assert that the time columns are read as Time + assert isinstance(tm[name], time.Time) + + # Assert that the scales round-trip + assert tm[name].scale == t[name].scale + + # Assert that the format is jd + assert tm[name].format == "jd" + + # Assert that the location round-trips + assert tm[name].location == t[name].location + + # Finally assert that the column data round-trips + assert (tm[name] == t[name]).all() + + for name in ("col0", "col1", "c"): + # Assert that the non-time columns are read as Column + assert isinstance(tm[name], Column) + + # Assert that the non-time columns' data round-trips + assert (tm[name] == t[name]).all() + + # Test for conversion of time data to its value, as defined by its format + for scale in time.STANDARD_TIME_SCALES: + for ab in ("a", "b"): + name = ab + scale + t[name].info.serialize_method["fits"] = "formatted_value" + + t.write(filename, format="fits", overwrite=True) + tm = table_types.read(filename, format="fits") + + for scale in time.STANDARD_TIME_SCALES: + for ab in ("a", "b"): + name = ab + scale + + assert not isinstance(tm[name], time.Time) + assert (tm[name] == t[name].value).all() + + +@pytest.mark.parametrize("table_types", (Table, QTable)) +def test_io_time_write_fits_local(tmp_path, table_types): + """ + Test that table with a Time mixin with scale local can also be written + by io.fits. Like ``test_io_time_write_fits_standard`` above, but avoiding + ``cxcsec`` format, which requires an epoch and thus cannot be used for a + local time scale. + """ + t = table_types([[1, 2], ["string", "column"]]) + t["a_local"] = time.Time( + [[50001, 50002], [50003, 50004]], + format="mjd", + scale="local", + location=EarthLocation(-2446354, 4237210, 4077985, unit="m"), + ) + t["b_local"] = time.Time( + ["1999-01-01T00:00:00.123456789", "2010-01-01T00:00:00"], scale="local" + ) + t["c"] = [3.0, 4.0] + + filename = tmp_path / "table-tmp" + + # Show that FITS format succeeds + + with pytest.warns( + AstropyUserWarning, match='Time Column "b_local" has no specified location' + ): + t.write(filename, format="fits", overwrite=True) + + with pytest.warns( + AstropyUserWarning, + match='Time column reference position "TRPOSn" is not specified.', + ): + tm = table_types.read(filename, format="fits", astropy_native=True) + + for ab in ("a", "b"): + name = ab + "_local" + + # Assert that the time columns are read as Time + assert isinstance(tm[name], time.Time) + + # Assert that the scales round-trip + assert tm[name].scale == t[name].scale + + # Assert that the format is jd + assert tm[name].format == "jd" + + # Assert that the location round-trips + assert tm[name].location == t[name].location + + # Finally assert that the column data round-trips + assert (tm[name] == t[name]).all() + + for name in ("col0", "col1", "c"): + # Assert that the non-time columns are read as Column + assert isinstance(tm[name], Column) + + # Assert that the non-time columns' data round-trips + assert (tm[name] == t[name]).all() + + # Test for conversion of time data to its value, as defined by its format. + for ab in ("a", "b"): + name = ab + "_local" + t[name].info.serialize_method["fits"] = "formatted_value" + + t.write(filename, format="fits", overwrite=True) + tm = table_types.read(filename, format="fits") + + for ab in ("a", "b"): + name = ab + "_local" + + assert not isinstance(tm[name], time.Time) + assert (tm[name] == t[name].value).all() + + +def test_votable_mixin_write_fail(mixin_cols): + """ + Test that table with mixin columns (excluding Quantity) cannot be written by + io.votable. + """ + t = QTable(mixin_cols) + # Only do this test if there are unsupported column types (i.e. anything besides + # BaseColumn and Quantity class instances). + unsupported_cols = t.columns.not_isinstance((BaseColumn, u.Quantity)) + + if not unsupported_cols: + pytest.skip("no unsupported column types") + + out = StringIO() + with pytest.raises(ValueError) as err: + t.write(out, format="votable") + assert "cannot write table with mixin column(s)" in str(err.value) + + +def test_join(table_types): + """ + Join tables with mixin cols. Use column "i" as proxy for what the + result should be for each mixin. + """ + t1 = table_types.Table() + t1["a"] = table_types.Column(["a", "b", "b", "c"]) + t1["i"] = table_types.Column([0, 1, 2, 3]) + for name, col in MIXIN_COLS.items(): + t1[name] = col + + t2 = table_types.Table(t1) + t2["a"] = ["b", "c", "a", "d"] + + for name in MIXIN_COLS: + t1[name].info.description = name + t2[name].info.description = name + "2" + + for join_type in ("inner", "left"): + t12 = join(t1, t2, keys="a", join_type=join_type) + idx1 = t12["i_1"] + idx2 = t12["i_2"] + for name, col in MIXIN_COLS.items(): + name1 = name + "_1" + name2 = name + "_2" + assert_table_name_col_equal(t12, name1, col[idx1]) + assert_table_name_col_equal(t12, name2, col[idx2]) + assert t12[name1].info.description == name + assert t12[name2].info.description == name + "2" + + for join_type in ("outer", "right"): + with pytest.raises(NotImplementedError) as exc: + t12 = join(t1, t2, keys="a", join_type=join_type) + assert "join requires masking column" in str(exc.value) + + with pytest.raises(TypeError) as exc: + t12 = join(t1, t2, keys=["a", "skycoord"]) + assert "one or more key columns are not sortable" in str(exc.value) + + # Join does work for a mixin which is a subclass of np.ndarray + with pytest.warns( + MergeConflictWarning, + match="In merged column 'quantity' the 'description' attribute does not match", + ): + t12 = join(t1, t2, keys=["quantity"]) + assert np.all(t12["a_1"] == t1["a"]) + + +def test_hstack(table_types): + """ + Hstack tables with mixin cols. Use column "i" as proxy for what the + result should be for each mixin. + """ + t1 = table_types.Table() + t1["i"] = table_types.Column([0, 1, 2, 3]) + for name, col in MIXIN_COLS.items(): + t1[name] = col + t1[name].info.description = name + t1[name].info.meta = {"a": 1} + + for join_type in ("inner", "outer"): + for chop in (True, False): + t2 = table_types.Table(t1) + if chop: + t2 = t2[:-1] + if join_type == "outer": + with pytest.raises(NotImplementedError) as exc: + t12 = hstack([t1, t2], join_type=join_type) + assert "hstack requires masking column" in str(exc.value) + continue + + t12 = hstack([t1, t2], join_type=join_type) + idx1 = t12["i_1"] + idx2 = t12["i_2"] + for name, col in MIXIN_COLS.items(): + name1 = name + "_1" + name2 = name + "_2" + assert_table_name_col_equal(t12, name1, col[idx1]) + assert_table_name_col_equal(t12, name2, col[idx2]) + for attr in ("description", "meta"): + assert getattr(t1[name].info, attr) == getattr( + t12[name1].info, attr + ) + assert getattr(t2[name].info, attr) == getattr( + t12[name2].info, attr + ) + + +def assert_table_name_col_equal(t, name, col): + """ + Assert all(t[name] == col), with special handling for known mixin cols. + """ + __tracebackhide__ = True + if isinstance(col, coordinates.SkyCoord): + assert np.all(t[name].ra == col.ra) + assert np.all(t[name].dec == col.dec) + elif isinstance(col, coordinates.BaseRepresentationOrDifferential): + assert np.all(representation_equal(t[name], col)) + elif isinstance(col, u.Quantity): + if type(t) is QTable: + assert np.all(t[name] == col) + elif isinstance(col, table_helpers.ArrayWrapper): + assert np.all(t[name].data == col.data) + else: + assert np.all(t[name] == col) + + +def test_get_items(mixin_cols): + """ + Test that slicing / indexing table gives right values and col attrs inherit + """ + attrs = ("name", "unit", "dtype", "format", "description", "meta") + m = mixin_cols["m"] + m.info.name = "m" + m.info.format = "{0}" + m.info.description = "d" + m.info.meta = {"a": 1} + t = QTable([m]) + for item in ([1, 3], np.array([0, 2]), slice(1, 3)): + t2 = t[item] + m2 = m[item] + assert_table_name_col_equal(t2, "m", m[item]) + for attr in attrs: + assert getattr(t2["m"].info, attr) == getattr(m.info, attr) + assert getattr(m2.info, attr) == getattr(m.info, attr) + + +def test_info_preserved_pickle_copy_init(mixin_cols): + """ + Test copy, pickle, and init from class roundtrip preserve info. This + tests not only the mixin classes but a regular column as well. + """ + + def pickle_roundtrip(c): + # protocol=5 matches the default for Python 3.14 and later + # and is needed to preserve byteorder (available since Python 3.8) + return pickle.loads(pickle.dumps(c, protocol=5)) + + def init_from_class(c): + return c.__class__(c) + + attrs = ("name", "unit", "dtype", "format", "description", "meta") + for colname in ("i", "m"): + m = mixin_cols[colname] + m.info.name = colname + m.info.format = "{0}" + m.info.description = "d" + m.info.meta = {"a": 1} + for func in (copy.copy, copy.deepcopy, pickle_roundtrip, init_from_class): + m2 = func(m) + for attr in attrs: + assert getattr(m2.info, attr) == getattr(m.info, attr) + + +def check_share_memory(col1, col2, copy): + """Check whether data attributes in col1 and col2 share memory. + + If copy=True, this should not be the case for any, while + if copy=False, all should share memory. + """ + if isinstance(col1, SkyCoord): + # For SkyCoord, .info does not access actual data by default, + # but rather attributes like .ra, which are copies. + map1 = col1.data.info._represent_as_dict() + map2 = col2.data.info._represent_as_dict() + else: + map1 = col1.info._represent_as_dict() + map2 = col2.info._represent_as_dict() + + # Check array attributes only (in principle, could iterate on, e.g., + # differentials in representations, but this is enough for table). + shared = [ + np.may_share_memory(v1, v2) + for (v1, v2) in zip(map1.values(), map2.values()) + if isinstance(v1, np.ndarray) and v1.shape + ] + if copy: + assert not any(shared) + else: + assert all(shared) + + +@pytest.mark.parametrize("copy", [True, False]) +def test_add_column(mixin_cols, copy): + """ + Test that adding a column preserves values and attributes. + For copy=True, the data should be independent; + for copy=False, the data should be shared, but the instance independent. + """ + attrs = ("name", "unit", "dtype", "format", "description", "meta") + m = mixin_cols["m"] + assert m.info.name is None + + # Make sure adding column in various ways doesn't touch info. + t = QTable([m], names=["a"], copy=copy) + assert m.info.name is None + check_share_memory(m, t["a"], copy=copy) + + t["new"] = m + assert m.info.name is None + check_share_memory(m, t["new"], copy=True) + + m.info.name = "m" + m.info.format = "{0}" + m.info.description = "d" + m.info.meta = {"a": 1} + t = QTable([m], copy=copy) + assert t.colnames == ["m"] + check_share_memory(m, t["m"], copy=copy) + + t = QTable([m], names=["m1"], copy=copy) + assert m.info.name == "m" + assert t.colnames == ["m1"] + check_share_memory(m, t["m1"], copy=copy) + + # Add columns m2, m3, m4 by two different methods and test expected equality + t["m2"] = m + check_share_memory(m, t["m2"], copy=True) + m.info.name = "m3" + t.add_columns([m], copy=copy) + check_share_memory(m, t["m3"], copy=copy) + for name in ("m2", "m3"): + assert_table_name_col_equal(t, name, m) + for attr in attrs: + if attr != "name": + assert getattr(t["m1"].info, attr) == getattr(t[name].info, attr) + # Also check that one can set using a scalar. + s = m[0] + if type(s) is type(m) and "info" in s.__dict__: + # We're not going to worry about testing classes for which scalars + # are a different class than the real array, or where info is not copied. + t["s"] = m[0] + assert_table_name_col_equal(t, "s", m[0]) + check_share_memory(m, t["s"], copy=True) + for attr in attrs: + if attr != "name": + assert getattr(t["m1"].info, attr) == getattr(t["s"].info, attr) + + # While we're add it, also check a length-1 table. + t = QTable([m[1:2]], names=["m"], copy=copy) + check_share_memory(m, t["m"], copy=copy) + if type(s) is type(m) and "info" in s.__dict__: + t["s"] = m[0] + assert_table_name_col_equal(t, "s", m[0]) + for attr in attrs: + if attr != "name": + assert getattr(t["m1"].info, attr) == getattr(t["s"].info, attr) + + +def test_vstack(): + """ + Vstack tables with mixin cols. + """ + t1 = QTable(MIXIN_COLS) + t2 = QTable(MIXIN_COLS) + with pytest.raises(NotImplementedError): + vstack([t1, t2]) + + +@pytest.mark.parametrize("empty_table", [False, True]) +def test_insert_row(mixin_cols, empty_table): + """ + Test inserting a row, which works for Column, Quantity, Time and SkyCoord. + """ + t0 = QTable(mixin_cols) + if empty_table: + t = t0[:0].copy() + insert_index = 0 + result_indices = [-1] + else: + t = t0.copy() + insert_index = 1 + result_indices = [0, -1, 1, 2, 3] + t["m"].info.description = "d" + if isinstance( + t["m"], (u.Quantity, Column, time.Time, time.TimeDelta, coordinates.SkyCoord) + ): + t.insert_row(insert_index, t0[-1]) + + for name in t.colnames: + assert np.all(t[name] == t0[name][result_indices]) + + assert np.all(t == t0[result_indices]) + assert t["m"].info.description == "d" + else: + with pytest.raises(ValueError) as exc: + t.insert_row(insert_index, t0[-1]) + assert "Unable to insert row" in str(exc.value) + + +def test_insert_row_bad_unit(): + """ + Insert a row into a QTable with the wrong unit + """ + t = QTable([[1] * u.m]) + with pytest.raises(ValueError) as exc: + t.insert_row(0, (2 * u.m / u.s,)) + assert "'m / s' (speed/velocity) and 'm' (length) are not convertible" in str( + exc.value + ) + + +def test_convert_np_array(mixin_cols): + """ + Test that converting to numpy array creates an object dtype and that + each instance in the array has the expected type. + """ + t = QTable(mixin_cols) + ta = t.as_array() + m = mixin_cols["m"] + dtype_kind = m.dtype.kind if hasattr(m, "dtype") else "O" + assert ta["m"].dtype.kind == dtype_kind + + +def test_assignment_and_copy(): + """ + Test that assignment of an int, slice, and fancy index works. + Along the way test that copying table works. + """ + for name in ("quantity", "arraywrap"): + m = MIXIN_COLS[name] + t0 = QTable([m], names=["m"]) + for i0, i1 in ( + (1, 2), + (slice(0, 2), slice(1, 3)), + (np.array([1, 2]), np.array([2, 3])), + ): + t = t0.copy() + t["m"][i0] = m[i1] + if name == "arraywrap": + assert np.all(t["m"].data[i0] == m.data[i1]) + assert np.all(t0["m"].data[i0] == m.data[i0]) + assert np.all(t0["m"].data[i0] != t["m"].data[i0]) + else: + assert np.all(t["m"][i0] == m[i1]) + assert np.all(t0["m"][i0] == m[i0]) + assert np.all(t0["m"][i0] != t["m"][i0]) + + +def test_conversion_qtable_table(): + """ + Test that a table round trips from QTable => Table => QTable + """ + qt = QTable(MIXIN_COLS) + names = qt.colnames + for name in names: + qt[name].info.description = name + + t = Table(qt) + for name in names: + assert t[name].info.description == name + if name == "quantity": + assert np.all(t["quantity"] == qt["quantity"].value) + assert np.all(t["quantity"].unit is qt["quantity"].unit) + assert isinstance(t["quantity"], t.ColumnClass) + else: + assert_table_name_col_equal(t, name, qt[name]) + + qt2 = QTable(qt) + for name in names: + assert qt2[name].info.description == name + assert_table_name_col_equal(qt2, name, qt[name]) + + +def test_setitem_as_column_name(): + """ + Test for mixin-related regression described in #3321. + """ + t = Table() + t["a"] = ["x", "y"] + t["b"] = "b" # Previously was failing with KeyError + assert np.all(t["a"] == ["x", "y"]) + assert np.all(t["b"] == ["b", "b"]) + + +def test_quantity_representation(): + """ + Test that table representation of quantities does not have unit + """ + t = QTable([[1, 2] * u.m]) + assert t.pformat() == [ + "col0", + " m ", + "----", + " 1.0", + " 2.0", + ] + + +@pytest.mark.parametrize( + "c, expected_pformat", + [ + pytest.param( + coordinates.CartesianRepresentation([0], [1], [0], unit=u.one), + # With no unit we get "None" in the unit row + [ + " col0 ", + "---------------", + "(0.0, 1.0, 0.0)", + ], + id="cartesian_wo_unit", + ), + pytest.param( + coordinates.CartesianRepresentation([0], [1], [0], unit="m"), + [ + " col0 ", + " m ", + "---------------", + "(0.0, 1.0, 0.0)", + ], + id="cartesian_w_unit", + ), + pytest.param( + coordinates.SphericalRepresentation([10] * u.deg, [20] * u.deg, [1] * u.pc), + [ + " col0 ", + " deg, deg, pc ", + "-----------------", + "(10.0, 20.0, 1.0)", + ], + id="spherical", + ), + pytest.param( + coordinates.UnitSphericalRepresentation([10] * u.deg, [20] * u.deg), + [ + " col0 ", + " deg ", + "------------", + "(10.0, 20.0)", + ], + id="unitspherical", + ), + pytest.param( + coordinates.SphericalCosLatDifferential( + [10] * u.mas / u.yr, [2] * u.mas / u.yr, [10] * u.km / u.s + ), + [ + " col0 ", + "mas / yr, mas / yr, km / s", + "--------------------------", + " (10.0, 2.0, 10.0)", + ], + id="sphericalcoslatdifferential", + ), + ], +) +def test_representation_representation(c, expected_pformat): + """ + Test that Representations are represented correctly. + """ + t = Table([c]) + assert t.pformat() == expected_pformat + + +def test_skycoord_representation(): + """ + Test that skycoord representation works, both in the way that the + values are output and in changing the frame representation. + """ + # With no unit we get "None" in the unit row + c = coordinates.SkyCoord([0], [1], [0], representation_type="cartesian") + t = Table([c]) + assert t.pformat() == [ + " col0 ", + "None,None,None", + "--------------", + " 0.0,1.0,0.0", + ] + + # Test that info works with a dynamically changed representation + c = coordinates.SkyCoord([0], [1], [0], unit="m", representation_type="cartesian") + t = Table([c]) + assert t.pformat() == [ + " col0 ", + " m,m,m ", + "-----------", + "0.0,1.0,0.0", + ] + + t["col0"].representation_type = "unitspherical" + assert t.pformat() == [ + " col0 ", + "deg,deg ", + "--------", + "90.0,0.0", + ] + + t["col0"].representation_type = "cylindrical" + assert t.pformat() == [ + " col0 ", + " m,deg,m ", + "------------", + "1.0,90.0,0.0", + ] + + +@pytest.mark.parametrize("as_ndarray_mixin", [True, False]) +def test_ndarray_mixin(as_ndarray_mixin): + """ + Test directly adding various forms of structured ndarray columns to a table. + Adding as NdarrayMixin is expected to be somewhat unusual after #12644 + (which provides full support for structured array Column's). This test shows + that the end behavior is the same in both cases. + """ + a = np.array([(1, "a"), (2, "b"), (3, "c"), (4, "d")], dtype=" list of Rows + rows0 = rows[0] + t2 = table.vstack(rows) + assert rows[0] is rows0 + + tables = [t1, t1] + t3 = table.vstack(tables) + assert tables[0] is t1 + + +class TestDStack: + def _setup(self, t_cls=Table): + self.t1 = t_cls.read( + [ + " a b", + " 0. foo", + " 1. bar", + ], + format="ascii", + ) + + self.t2 = t_cls.read( + [ + " a b c", + " 2. pez 4", + " 3. sez 5", + ], + format="ascii", + ) + self.t2["d"] = Time([1, 2], format="cxcsec") + + self.t3 = t_cls( + { + "a": [[5.0, 6.0], [4.0, 3.0]], + "b": [["foo", "bar"], ["pez", "sez"]], + }, + names=("a", "b"), + ) + + self.t4 = t_cls(self.t1, copy=True, masked=t_cls is Table) + + self.t5 = t_cls( + { + "a": [[4.0, 2.0], [1.0, 6.0]], + "b": [["foo", "pez"], ["bar", "sez"]], + }, + names=("a", "b"), + ) + self.t6 = t_cls.read( + [ + " a b c", + " 7. pez 2", + " 4. sez 6", + " 6. foo 3", + ], + format="ascii", + ) + + def test_validate_join_type(self): + self._setup() + with pytest.raises(TypeError, match="Did you accidentally call dstack"): + table.dstack(self.t1, self.t2) + + @staticmethod + def compare_dstack(tables, out): + for ii, tbl in enumerate(tables): + for name in out.columns: + if name in tbl.colnames: + # Columns always compare equal + assert np.all(tbl[name] == out[name][:, ii]) + + # If input has a mask then output must have same mask + if hasattr(tbl[name], "mask"): + assert np.all(tbl[name].mask == out[name].mask[:, ii]) + + # If input has no mask then output might have a mask (if other table + # is missing that column). If so then all mask values should be False. + elif hasattr(out[name], "mask"): + assert not np.any(out[name].mask[:, ii]) + + else: + # Column missing for this table, out must have a mask with all True. + assert np.all(out[name].mask[:, ii]) + + def test_dstack_table_column(self, operation_table_type): + """Stack a table with 3 cols and one column (gets auto-converted to Table).""" + self._setup(operation_table_type) + t2 = self.t1.copy() + out = table.dstack([self.t1, t2["a"]]) + self.compare_dstack([self.t1, t2[("a",)]], out) + + def test_dstack_basic_outer(self, operation_table_type): + if operation_table_type is QTable: + pytest.xfail("Quantity columns do not support masking.") + self._setup(operation_table_type) + t1 = self.t1 + t2 = self.t2 + t4 = self.t4 + t4["a"].mask[0] = True + # Test for non-masked table + t12 = table.dstack([t1, t2], join_type="outer") + assert type(t12) is operation_table_type + assert type(t12["a"]) is type(t1["a"]) + assert type(t12["b"]) is type(t1["b"]) + self.compare_dstack([t1, t2], t12) + + # Test for masked table + t124 = table.dstack([t1, t2, t4], join_type="outer") + assert type(t124) is operation_table_type + assert type(t124["a"]) is type(t4["a"]) + assert type(t124["b"]) is type(t4["b"]) + self.compare_dstack([t1, t2, t4], t124) + + def test_dstack_basic_inner(self, operation_table_type): + self._setup(operation_table_type) + t1 = self.t1 + t2 = self.t2 + t4 = self.t4 + + # Test for masked table + t124 = table.dstack([t1, t2, t4], join_type="inner") + assert type(t124) is operation_table_type + assert type(t124["a"]) is type(t4["a"]) + assert type(t124["b"]) is type(t4["b"]) + self.compare_dstack([t1, t2, t4], t124) + + def test_dstack_multi_dimension_column(self, operation_table_type): + self._setup(operation_table_type) + t3 = self.t3 + t5 = self.t5 + t2 = self.t2 + t35 = table.dstack([t3, t5]) + assert type(t35) is operation_table_type + assert type(t35["a"]) is type(t3["a"]) + assert type(t35["b"]) is type(t3["b"]) + self.compare_dstack([t3, t5], t35) + + with pytest.raises(TableMergeError): + table.dstack([t2, t3]) + + def test_dstack_different_length_table(self, operation_table_type): + self._setup(operation_table_type) + t2 = self.t2 + t6 = self.t6 + with pytest.raises(ValueError): + table.dstack([t2, t6]) + + def test_dstack_single_table(self): + self._setup(Table) + out = table.dstack(self.t1) + assert np.all(out == self.t1) + + def test_dstack_representation(self): + rep1 = SphericalRepresentation([1, 2] * u.deg, [3, 4] * u.deg, 1 * u.kpc) + rep2 = SphericalRepresentation([10, 20] * u.deg, [30, 40] * u.deg, 10 * u.kpc) + t1 = Table([rep1]) + t2 = Table([rep2]) + t12 = table.dstack([t1, t2]) + assert np.all(representation_equal(t12["col0"][:, 0], rep1)) + assert np.all(representation_equal(t12["col0"][:, 1], rep2)) + + def test_dstack_skycoord(self): + sc1 = SkyCoord([1, 2] * u.deg, [3, 4] * u.deg) + sc2 = SkyCoord([10, 20] * u.deg, [30, 40] * u.deg) + t1 = Table([sc1]) + t2 = Table([sc2]) + t12 = table.dstack([t1, t2]) + assert skycoord_equal(sc1, t12["col0"][:, 0]) + assert skycoord_equal(sc2, t12["col0"][:, 1]) + + def test_dstack_structured_column(self): + """Regression tests for gh-13271.""" + # Two tables with matching names, including a structured column. + t1 = Table( + [ + np.array([(1.0, 1), (2.0, 2)], dtype=[("f", "f8"), ("i", "i8")]), + ["one", "two"], + ], + names=["structured", "string"], + ) + t2 = Table( + [ + np.array([(3.0, 3), (4.0, 4)], dtype=[("f", "f8"), ("i", "i8")]), + ["three", "four"], + ], + names=["structured", "string"], + ) + t12 = table.dstack([t1, t2]) + assert t12.pformat() == ( + [ + " structured [f, i] string ", + "-------------------- ------------", + "(1.0, 1) .. (3.0, 3) one .. three", + "(2.0, 2) .. (4.0, 4) two .. four", + ] + ) + + # One table without the structured column. + t3 = t2[("string",)] + t13 = table.dstack([t1, t3]) + assert t13.pformat() == [ + "structured [f, i] string ", + "----------------- ------------", + " (1.0, 1) .. -- one .. three", + " (2.0, 2) .. -- two .. four", + ] + + +class TestHStack: + def _setup(self, t_cls=Table): + self.t1 = t_cls.read( + [ + " a b", + " 0. foo", + " 1. bar", + ], + format="ascii", + ) + + self.t2 = t_cls.read( + [ + " a b c", + " 2. pez 4", + " 3. sez 5", + ], + format="ascii", + ) + + self.t3 = t_cls.read( + [ + " d e", + " 4. 7", + " 5. 8", + " 6. 9", + ], + format="ascii", + ) + self.t4 = t_cls(self.t1, copy=True, masked=True) + self.t4["a"].name = "f" + self.t4["b"].name = "g" + + # The following table has meta-data that conflicts with t1 + self.t5 = t_cls(self.t1, copy=True) + + self.t1.meta.update(OrderedDict([("b", [1, 2]), ("c", {"a": 1}), ("d", 1)])) + self.t2.meta.update(OrderedDict([("b", [3, 4]), ("c", {"b": 1}), ("a", 1)])) + self.t4.meta.update(OrderedDict([("b", [5, 6]), ("c", {"c": 1}), ("e", 1)])) + self.t5.meta.update(OrderedDict([("b", 3), ("c", "k"), ("d", 1)])) + self.meta_merge = OrderedDict( + [ + ("b", [1, 2, 3, 4, 5, 6]), + ("c", {"a": 1, "b": 1, "c": 1}), + ("d", 1), + ("a", 1), + ("e", 1), + ] + ) + + def test_validate_join_type(self): + self._setup() + with pytest.raises(TypeError, match="Did you accidentally call hstack"): + table.hstack(self.t1, self.t2) + + def test_stack_same_table(self, operation_table_type): + """ + From #2995, test that hstack'ing references to the same table has the + expected output. + """ + self._setup(operation_table_type) + out = table.hstack([self.t1, self.t1]) + assert out.masked is False + assert out.pformat() == [ + "a_1 b_1 a_2 b_2", + "--- --- --- ---", + "0.0 foo 0.0 foo", + "1.0 bar 1.0 bar", + ] + + def test_stack_rows(self, operation_table_type): + self._setup(operation_table_type) + out = table.hstack([self.t1[0], self.t2[1]]) + assert out.masked is False + assert out.pformat() == [ + "a_1 b_1 a_2 b_2 c ", + "--- --- --- --- ---", + "0.0 foo 3.0 sez 5", + ] + + def test_stack_columns(self, operation_table_type): + self._setup(operation_table_type) + out = table.hstack([self.t1, self.t2["c"]]) + assert type(out["a"]) is type(self.t1["a"]) + assert type(out["b"]) is type(self.t1["b"]) + assert type(out["c"]) is type(self.t2["c"]) + assert out.pformat() == [ + " a b c ", + "--- --- ---", + "0.0 foo 4", + "1.0 bar 5", + ] + + def test_table_meta_merge(self, operation_table_type): + self._setup(operation_table_type) + out = table.hstack([self.t1, self.t2, self.t4], join_type="inner") + assert out.meta == self.meta_merge + + def test_table_meta_merge_conflict(self, operation_table_type): + self._setup(operation_table_type) + + with pytest.warns(metadata.MergeConflictWarning) as w: + out = table.hstack([self.t1, self.t5], join_type="inner") + assert len(w) == 2 + + assert out.meta == self.t5.meta + + with pytest.warns(metadata.MergeConflictWarning) as w: + out = table.hstack( + [self.t1, self.t5], join_type="inner", metadata_conflicts="warn" + ) + assert len(w) == 2 + + assert out.meta == self.t5.meta + + out = table.hstack( + [self.t1, self.t5], join_type="inner", metadata_conflicts="silent" + ) + + assert out.meta == self.t5.meta + + with pytest.raises(MergeConflictError): + out = table.hstack( + [self.t1, self.t5], join_type="inner", metadata_conflicts="error" + ) + + with pytest.raises(ValueError): + out = table.hstack( + [self.t1, self.t5], join_type="inner", metadata_conflicts="nonsense" + ) + + def test_bad_input_type(self, operation_table_type): + self._setup(operation_table_type) + with pytest.raises(ValueError): + table.hstack([]) + with pytest.raises(TypeError): + table.hstack(1) + with pytest.raises(TypeError): + table.hstack([self.t2, 1]) + with pytest.raises(ValueError): + table.hstack([self.t1, self.t2], join_type="invalid join type") + + def test_stack_basic(self, operation_table_type): + self._setup(operation_table_type) + t1 = self.t1 + t2 = self.t2 + t3 = self.t3 + t4 = self.t4 + + out = table.hstack([t1, t2], join_type="inner") + assert out.masked is False + assert type(out) is operation_table_type + assert type(out["a_1"]) is type(t1["a"]) + assert type(out["b_1"]) is type(t1["b"]) + assert type(out["a_2"]) is type(t2["a"]) + assert type(out["b_2"]) is type(t2["b"]) + assert out.pformat() == [ + "a_1 b_1 a_2 b_2 c ", + "--- --- --- --- ---", + "0.0 foo 2.0 pez 4", + "1.0 bar 3.0 sez 5", + ] + + # stacking as a list gives same result + out_list = table.hstack([t1, t2], join_type="inner") + assert out.pformat() == out_list.pformat() + + out = table.hstack([t1, t2], join_type="outer") + assert out.pformat() == out_list.pformat() + + out = table.hstack([t1, t2, t3, t4], join_type="outer") + assert out.masked is False + assert out.pformat() == [ + "a_1 b_1 a_2 b_2 c d e f g ", + "--- --- --- --- --- --- --- --- ---", + "0.0 foo 2.0 pez 4 4.0 7 0.0 foo", + "1.0 bar 3.0 sez 5 5.0 8 1.0 bar", + " -- -- -- -- -- 6.0 9 -- --", + ] + + out = table.hstack([t1, t2, t3, t4], join_type="inner") + assert out.masked is False + assert out.pformat() == [ + "a_1 b_1 a_2 b_2 c d e f g ", + "--- --- --- --- --- --- --- --- ---", + "0.0 foo 2.0 pez 4 4.0 7 0.0 foo", + "1.0 bar 3.0 sez 5 5.0 8 1.0 bar", + ] + + def test_stack_incompatible(self, operation_table_type): + self._setup(operation_table_type) + # For join_type exact, which will fail here because n_rows + # does not match + with pytest.raises(TableMergeError): + table.hstack([self.t1, self.t3], join_type="exact") + + def test_hstack_one_masked(self, operation_table_type): + if operation_table_type is QTable: + pytest.xfail() + self._setup(operation_table_type) + t1 = self.t1 + t2 = operation_table_type(t1, copy=True, masked=True) + t2.meta.clear() + t2["b"].mask[1] = True + out = table.hstack([t1, t2]) + assert out.pformat() == [ + "a_1 b_1 a_2 b_2", + "--- --- --- ---", + "0.0 foo 0.0 foo", + "1.0 bar 1.0 --", + ] + + def test_table_col_rename(self, operation_table_type): + self._setup(operation_table_type) + out = table.hstack( + [self.t1, self.t2], + join_type="inner", + uniq_col_name="{table_name}_{col_name}", + table_names=("left", "right"), + ) + assert out.masked is False + assert out.pformat() == [ + "left_a left_b right_a right_b c ", + "------ ------ ------- ------- ---", + " 0.0 foo 2.0 pez 4", + " 1.0 bar 3.0 sez 5", + ] + + def test_col_meta_merge(self, operation_table_type): + self._setup(operation_table_type) + t1 = self.t1 + t3 = self.t3[:2] + t4 = self.t4 + + # Just set a bunch of meta and make sure it is the same in output + meta1 = OrderedDict([("b", [1, 2]), ("c", {"a": 1}), ("d", 1)]) + t1["a"].unit = "cm" + t1["b"].info.description = "t1_b" + t4["f"].info.format = "%6s" + t1["b"].info.meta.update(meta1) + t3["d"].info.meta.update( + OrderedDict([("b", [3, 4]), ("c", {"b": 1}), ("a", 1)]) + ) + t4["g"].info.meta.update( + OrderedDict([("b", [5, 6]), ("c", {"c": 1}), ("e", 1)]) + ) + t3["e"].info.meta.update( + OrderedDict([("b", [3, 4]), ("c", {"b": 1}), ("a", 1)]) + ) + t3["d"].unit = "m" + t3["d"].info.format = "%6s" + t3["d"].info.description = "t3_c" + + out = table.hstack([t1, t3, t4], join_type="exact") + + for t in [t1, t3, t4]: + for name in t.colnames: + for attr in ("meta", "unit", "format", "description"): + assert getattr(out[name].info, attr) == getattr(t[name].info, attr) + + # Make sure we got a copy of meta, not ref + t1["b"].info.meta["b"] = None + assert out["b"].info.meta["b"] == [1, 2] + + def test_hstack_one_table(self, operation_table_type): + self._setup(operation_table_type) + """Regression test for issue #3313""" + assert (self.t1 == table.hstack(self.t1)).all() + assert (self.t1 == table.hstack([self.t1])).all() + + def test_mixin_functionality(self, mixin_cols): + col1 = mixin_cols["m"] + col2 = col1[2:4] # Shorter version of col1 + t1 = table.QTable([col1]) + t2 = table.QTable([col2]) + + cls_name = type(col1).__name__ + + out = table.hstack([t1, t2], join_type="inner") + assert type(out["col0_1"]) is type(out["col0_2"]) + assert len(out) == len(col2) + + # Check that columns are as expected. + if cls_name == "SkyCoord": + assert skycoord_equal(out["col0_1"], col1[: len(col2)]) + assert skycoord_equal(out["col0_2"], col2) + elif "Repr" in cls_name or "Diff" in cls_name: + assert np.all(representation_equal(out["col0_1"], col1[: len(col2)])) + assert np.all(representation_equal(out["col0_2"], col2)) + else: + assert np.all(out["col0_1"] == col1[: len(col2)]) + assert np.all(out["col0_2"] == col2) + + # Check mixin classes that support masking (and that we raise for + # those that do not). + if isinstance(col1, MIXINS_WITH_FULL_MASK_SUPPORT): + out = table.hstack([t1, t2], join_type="outer") + assert len(out) == len(t1) + assert np.all(out["col0_1"] == col1) + assert np.all(out["col0_2"][: len(col2)] == col2) + assert check_mask(out["col0_2"], [False, False, True, True]) + + # check directly stacking mixin columns: + out2 = table.hstack([t1, t2["col0"]], join_type="outer") + assert np.all(out["col0_1"] == out2["col0_1"]) + assert np.all(out["col0_2"] == out2["col0_2"]) + else: + with pytest.raises(NotImplementedError) as err: + table.hstack([t1, t2], join_type="outer") + assert "hstack requires masking" in str(err.value) + + +def test_unique(operation_table_type): + t = operation_table_type.read( + [ + " a b c d", + " 2 b 7.0 0", + " 1 c 3.0 5", + " 2 b 6.0 2", + " 2 a 4.0 3", + " 1 a 1.0 7", + " 2 b 5.0 1", + " 0 a 0.0 4", + " 1 a 2.0 6", + " 1 c 3.0 5", + ], + format="ascii", + ) + + tu = operation_table_type(np.sort(t[:-1])) + + t_all = table.unique(t) + assert sort_eq(t_all.pformat(), tu.pformat()) + t_s = t.copy() + del t_s["b", "c", "d"] + t_all = table.unique(t_s) + assert sort_eq( + t_all.pformat(), + [ + " a ", + "---", + " 0", + " 1", + " 2", + ], + ) + + key1 = "a" + t1a = table.unique(t, key1) + assert sort_eq( + t1a.pformat(), + [ + " a b c d ", + "--- --- --- ---", + " 0 a 0.0 4", + " 1 c 3.0 5", + " 2 b 7.0 0", + ], + ) + t1b = table.unique(t, key1, keep="last") + assert sort_eq( + t1b.pformat(), + [ + " a b c d ", + "--- --- --- ---", + " 0 a 0.0 4", + " 1 c 3.0 5", + " 2 b 5.0 1", + ], + ) + t1c = table.unique(t, key1, keep="none") + assert sort_eq( + t1c.pformat(), + [ + " a b c d ", + "--- --- --- ---", + " 0 a 0.0 4", + ], + ) + + key2 = ["a", "b"] + t2a = table.unique(t, key2) + assert sort_eq( + t2a.pformat(), + [ + " a b c d ", + "--- --- --- ---", + " 0 a 0.0 4", + " 1 a 1.0 7", + " 1 c 3.0 5", + " 2 a 4.0 3", + " 2 b 7.0 0", + ], + ) + + t2b = table.unique(t, key2, keep="last") + assert sort_eq( + t2b.pformat(), + [ + " a b c d ", + "--- --- --- ---", + " 0 a 0.0 4", + " 1 a 2.0 6", + " 1 c 3.0 5", + " 2 a 4.0 3", + " 2 b 5.0 1", + ], + ) + t2c = table.unique(t, key2, keep="none") + assert sort_eq( + t2c.pformat(), + [ + " a b c d ", + "--- --- --- ---", + " 0 a 0.0 4", + " 2 a 4.0 3", + ], + ) + + key2 = ["a", "a"] + with pytest.raises(ValueError) as exc: + t2a = table.unique(t, key2) + assert exc.value.args[0] == "duplicate key names" + + with pytest.raises(ValueError) as exc: + table.unique(t, key2, keep=True) + assert exc.value.args[0] == "'keep' should be one of 'first', 'last', 'none'" + + t1_m = operation_table_type(t1a, masked=True) + t1_m["a"].mask[1] = True + + with pytest.raises(ValueError) as exc: + t1_mu = table.unique(t1_m) + assert ( + exc.value.args[0] == "cannot use columns with masked values as keys; " + "remove column 'a' from keys and rerun unique()" + ) + + t1_mu = table.unique(t1_m, silent=True) + assert t1_mu.masked is False + assert t1_mu.pformat() == [ + " a b c d ", + "--- --- --- ---", + " 0 a 0.0 4", + " 2 b 7.0 0", + " -- c 3.0 5", + ] + + with pytest.raises(ValueError): + t1_mu = table.unique(t1_m, silent=True, keys="a") + + t1_m = operation_table_type(t, masked=True) + t1_m["a"].mask[1] = True + t1_m["d"].mask[3] = True + + # Test that multiple masked key columns get removed in the correct + # order + t1_mu = table.unique(t1_m, keys=["d", "a", "b"], silent=True) + assert t1_mu.masked is False + assert t1_mu.pformat() == [ + " a b c d ", + "--- --- --- ---", + " 2 a 4.0 --", + " 2 b 7.0 0", + " -- c 3.0 5", + ] + + +@pytest.mark.parametrize("join_type", ["inner", "outer", "left", "right", "cartesian"]) +def test_join_keep_sort_order(join_type): + """Test the keep_order argument for table.join. + + See https://github.com/astropy/astropy/issues/11619. + + This defines a left and right table which have an ``id`` column that is not sorted + and not unique. Each table has common and unique ``id`` key values along with an + ``order`` column to keep track of the original order. + """ + keep_supported = join_type in ["left", "right", "inner"] + t1 = Table() + t1["id"] = [2, 8, 2, 0, 0, 1] # Join key + t1["order"] = np.arange(len(t1)) # Original table order + + t2 = Table() + t2["id"] = [2, 0, 1, 9, 0, 1] # Join key + t2["order"] = np.arange(len(t2)) # Original table order + + # No keys arg is allowed for cartesian join. + keys_kwarg = {} if join_type == "cartesian" else {"keys": "id"} + + # Now do table joints with keep_order=False and keep_order=True. + t12f = table.join(t1, t2, join_type=join_type, keep_order=False, **keys_kwarg) + # For keep_order=True there should be a warning if keep_order is not supported for + # the join type. + ctx = ( + nullcontext() + if keep_supported + else pytest.warns( + UserWarning, + match=r"keep_order=True is only supported for left, right, and inner joins", + ) + ) + with ctx: + t12t = table.join(t1, t2, join_type=join_type, keep_order=True, **keys_kwarg) + + assert len(t12f) == len(t12t) + assert t12f.colnames == t12t.colnames + + # Define expected sorting of join table for keep_order=False. Cartesian joins are + # always sorted by the native order of the left table, otherwise the table is sorted + # by the sort key ``id``. + sort_key_false = "order_1" if join_type == "cartesian" else "id" + + # For keep_order=True the "order" column is sorted if keep is supported otherwise + # the table is sorted as for keep_order=False. + if keep_supported: + sort_key_true = "order_2" if join_type == "right" else "order_1" + else: + sort_key_true = sort_key_false + + assert np.all(t12f[sort_key_false] == sorted(t12f[sort_key_false])) + assert np.all(t12t[sort_key_true] == sorted([t12t[sort_key_true]])) + + +def test_join_keep_sort_order_exception(): + """Test that exception in join(..., keep_order=True) leaves table unchanged""" + t1 = Table([[1, 2]], names=["id"]) + t2 = Table([[2, 3]], names=["id"]) + with pytest.raises( + TableMergeError, match=r"Left table does not have key column 'not-a-key'" + ): + table.join(t1, t2, keys="not-a-key", join_type="inner", keep_order=True) + assert t1.colnames == ["id"] + assert t2.colnames == ["id"] + + +def test_vstack_bytes(operation_table_type): + """ + Test for issue #5617 when vstack'ing bytes columns in Py3. + This is really an upstream numpy issue numpy/numpy/#8403. + """ + t = operation_table_type([[b"a"]], names=["a"]) + assert t["a"].itemsize == 1 + + t2 = table.vstack([t, t]) + assert len(t2) == 2 + assert t2["a"].itemsize == 1 + + +def test_vstack_unicode(): + """ + Test for problem related to issue #5617 when vstack'ing *unicode* + columns. In this case the character size gets multiplied by 4. + """ + t = table.Table([["a"]], names=["a"]) + assert t["a"].itemsize == 4 # 4-byte / char for U dtype + + t2 = table.vstack([t, t]) + assert len(t2) == 2 + assert t2["a"].itemsize == 4 + + +def test_join_mixins_time_quantity(): + """ + Test for table join using non-ndarray key columns. + """ + tm1 = Time([2, 1, 2], format="cxcsec") + q1 = [2, 1, 1] * u.m + idx1 = [1, 2, 3] + tm2 = Time([2, 3], format="cxcsec") + q2 = [2, 3] * u.m + idx2 = [10, 20] + t1 = Table([tm1, q1, idx1], names=["tm", "q", "idx"]) + t2 = Table([tm2, q2, idx2], names=["tm", "q", "idx"]) + # Output: + # + # + # tm q idx_1 idx_2 + # m + # object float64 int64 int64 + # ------------------ ------- ----- ----- + # 0.9999999999969589 1.0 2 -- + # 2.00000000000351 1.0 3 -- + # 2.00000000000351 2.0 1 10 + # 3.000000000000469 3.0 -- 20 + + t12 = table.join(t1, t2, join_type="outer", keys=["tm", "q"]) + # Key cols are lexically sorted + assert np.all(t12["tm"] == Time([1, 2, 2, 3], format="cxcsec")) + assert np.all(t12["q"] == [1, 1, 2, 3] * u.m) + assert np.all(t12["idx_1"] == np.ma.array([2, 3, 1, 0], mask=[0, 0, 0, 1])) + assert np.all(t12["idx_2"] == np.ma.array([0, 0, 10, 20], mask=[1, 1, 0, 0])) + + +def test_join_mixins_not_sortable(): + """ + Test for table join using non-ndarray key columns that are not sortable. + """ + sc = SkyCoord([1, 2], [3, 4], unit="deg,deg") + t1 = Table([sc, [1, 2]], names=["sc", "idx1"]) + t2 = Table([sc, [10, 20]], names=["sc", "idx2"]) + + with pytest.raises(TypeError, match="one or more key columns are not sortable"): + table.join(t1, t2, keys="sc") + + +def test_join_non_1d_key_column(): + c1 = [[1, 2], [3, 4]] + c2 = [1, 2] + t1 = Table([c1, c2], names=["a", "b"]) + t2 = t1.copy() + with pytest.raises(ValueError, match="key column 'a' must be 1-d"): + table.join(t1, t2, keys="a") + + +def test_argsort_time_column(): + """Regression test for #10823.""" + times = Time(["2016-01-01", "2018-01-01", "2017-01-01"]) + t = Table([times], names=["time"]) + i = t.argsort("time") + assert np.all(i == times.argsort()) + + +def test_sort_indexed_table(): + """Test fix for #9473 and #6545 - and another regression test for #10823.""" + t = Table([[1, 3, 2], [6, 4, 5]], names=("a", "b")) + t.add_index("a") + t.sort("a") + assert np.all(t["a"] == [1, 2, 3]) + assert np.all(t["b"] == [6, 5, 4]) + t.sort("b") + assert np.all(t["b"] == [4, 5, 6]) + assert np.all(t["a"] == [3, 2, 1]) + + times = ["2016-01-01", "2018-01-01", "2017-01-01"] + tm = Time(times) + t2 = Table([tm, [3, 2, 1]], names=["time", "flux"]) + t2.sort("flux") + assert np.all(t2["flux"] == [1, 2, 3]) + t2.sort("time") + assert np.all(t2["flux"] == [3, 1, 2]) + assert np.all(t2["time"] == tm[[0, 2, 1]]) + + # Using the table as a TimeSeries implicitly sets the index, so + # this test is a bit different from the above. + ts = TimeSeries(time=times) + ts["flux"] = [3, 2, 1] + ts.sort("flux") + assert np.all(ts["flux"] == [1, 2, 3]) + ts.sort("time") + assert np.all(ts["flux"] == [3, 1, 2]) + assert np.all(ts["time"] == tm[[0, 2, 1]]) + + +def test_get_out_class(): + c = table.Column([1, 2]) + mc = table.MaskedColumn([1, 2]) + q = [1, 2] * u.m + + assert _get_out_class([c, mc]) is mc.__class__ + assert _get_out_class([mc, c]) is mc.__class__ + assert _get_out_class([c, c]) is c.__class__ + assert _get_out_class([c]) is c.__class__ + + with pytest.raises(ValueError): + _get_out_class([c, q]) + + with pytest.raises(ValueError): + _get_out_class([q, c]) + + +def test_masking_required_exception(): + """ + Test that outer join, hstack and vstack fail for a mixin column which + does not support masking. + """ + col = table.NdarrayMixin([0, 1, 2, 3]) + t1 = table.QTable([[1, 2, 3, 4], col], names=["a", "b"]) + t2 = table.QTable([[1, 2], col[:2]], names=["a", "c"]) + + with pytest.raises(NotImplementedError) as err: + table.vstack([t1, t2], join_type="outer") + assert "vstack unavailable" in str(err.value) + + with pytest.raises(NotImplementedError) as err: + table.hstack([t1, t2], join_type="outer") + assert "hstack requires masking" in str(err.value) + + with pytest.raises(NotImplementedError) as err: + table.join(t1, t2, join_type="outer") + assert "join requires masking" in str(err.value) + + +def test_stack_columns(): + c = table.Column([1, 2]) + mc = table.MaskedColumn([1, 2]) + q = [1, 2] * u.m + time = Time(["2001-01-02T12:34:56", "2001-02-03T00:01:02"]) + sc = SkyCoord([1, 2], [3, 4], unit="deg") + cq = table.Column([11, 22], unit=u.m) + + t = table.hstack([c, q]) + assert t.__class__ is table.QTable + assert t.masked is False + t = table.hstack([q, c]) + assert t.__class__ is table.QTable + assert t.masked is False + + t = table.hstack([mc, q]) + assert t.__class__ is table.QTable + assert t.masked is False + + t = table.hstack([c, mc]) + assert t.__class__ is table.Table + assert t.masked is False + + t = table.vstack([q, q]) + assert t.__class__ is table.QTable + + t = table.vstack([c, c]) + assert t.__class__ is table.Table + + t = table.hstack([c, time]) + assert t.__class__ is table.Table + t = table.hstack([c, sc]) + assert t.__class__ is table.Table + t = table.hstack([q, time, sc]) + assert t.__class__ is table.QTable + + with pytest.raises(ValueError): + table.vstack([c, q]) + + with pytest.raises(ValueError): + t = table.vstack([q, cq]) + + +def test_mixin_join_regression(): + # This used to trigger a ValueError: + # ValueError: NumPy boolean array indexing assignment cannot assign + # 6 input values to the 4 output values where the mask is true + + t1 = QTable() + t1["index"] = [1, 2, 3, 4, 5] + t1["flux1"] = [2, 3, 2, 1, 1] * u.Jy + t1["flux2"] = [2, 3, 2, 1, 1] * u.Jy + + t2 = QTable() + t2["index"] = [3, 4, 5, 6] + t2["flux1"] = [2, 1, 1, 3] * u.Jy + t2["flux2"] = [2, 1, 1, 3] * u.Jy + + t12 = table.join(t1, t2, keys=("index", "flux1", "flux2"), join_type="outer") + + assert len(t12) == 6 + + +@pytest.mark.parametrize( + "t1, t2", + [ + # different names + ( + Table([np.array([1])], names=["a"]), + Table([np.array([1])], names=["b"]), + ), + # different data (broadcastable) + ( + Table([np.array([])], names=["a"]), + Table([np.array([1])], names=["a"]), + ), + # different data (not broadcastable) + ( + Table([np.array([1, 2])], names=["a"]), + Table([np.array([1, 2, 3])], names=["a"]), + ), + # different names and data (broadcastable) + ( + Table([np.array([])], names=["a"]), + Table([np.array([1])], names=["b"]), + ), + # different names and data (not broadcastable) + ( + Table([np.array([1, 2])], names=["a"]), + Table([np.array([1, 2, 3])], names=["b"]), + ), + # different data and array type (broadcastable) + ( + Table([np.array([])], names=["a"]), + Table([np.ma.MaskedArray([1])], names=["a"]), + ), + # different data and array type (not broadcastable) + ( + Table([np.array([1, 2])], names=["a"]), + Table([np.ma.MaskedArray([1, 2, 3])], names=["a"]), + ), + ], +) +def test_table_comp(t1, t2): + # see https://github.com/astropy/astropy/issues/13421 + try: + np.result_type(t1.dtype, t2.dtype) + np.broadcast_shapes((len(t1),), (len(t2),)) + except (TypeError, ValueError): + # dtypes are not comparable or arrays can't be broadcasted: + # a simple bool should be returned + assert not t1 == t2 + assert not t2 == t1 + assert t1 != t2 + assert t2 != t1 + else: + # otherwise, the general case is to return a 1D array with dtype=bool + assert not any(t1 == t2) + assert not any(t2 == t1) + assert all(t1 != t2) + assert all(t2 != t1) + + +def test_empty_skycoord_vstack(): + # Explicit regression test for gh-17378 + table1 = Table({"foo": SkyCoord([], [], unit="deg")}) + table2 = table.vstack([table1, table1]) # Used to fail. + assert len(table2) == 0 + assert isinstance(table2["foo"], SkyCoord) + + +@pytest.mark.skipif(not HAS_NUMPY_QUADDTYPE, reason="Tests QuadDtype") +def test_user_dtype_vstack(): + # Regression test for gh-19197 + from numpy_quaddtype import QuadPrecDType + + c = Column([1, 2], dtype=QuadPrecDType()) + 1e-25 + t = Table([c], names=["c"]) + t2 = table.vstack([t, t]) # This used to fail + assert t2["c"].dtype == c.dtype + assert_array_equal(t2[:2]["c"], c) + assert_array_equal(t2[2:]["c"], c) diff --git a/astropy/table/tests/test_pickle.py b/astropy/table/tests/test_pickle.py new file mode 100644 index 000000000000..8c670ff66101 --- /dev/null +++ b/astropy/table/tests/test_pickle.py @@ -0,0 +1,180 @@ +import pickle + +import numpy as np + +from astropy.coordinates import Angle, SkyCoord +from astropy.table import Column, MaskedColumn, QTable, Table +from astropy.table.table_helpers import simple_table +from astropy.time import Time +from astropy.units import Quantity, deg + + +def test_pickle_column(protocol): + c = Column( + data=[1, 2], + name="a", + format="%05d", + description="col a", + unit="cm", + meta={"a": 1}, + ) + cs = pickle.dumps(c, protocol=protocol) + cp = pickle.loads(cs) + assert np.all(cp == c) + assert cp.attrs_equal(c) + assert cp._parent_table is None + assert repr(c) == repr(cp) + + +def test_pickle_masked_column(protocol): + c = MaskedColumn( + data=[1, 2], + name="a", + format="%05d", + description="col a", + unit="cm", + meta={"a": 1}, + ) + c.mask[1] = True + c.fill_value = -99 + + cs = pickle.dumps(c, protocol=protocol) + cp = pickle.loads(cs) + + assert np.all(cp._data == c._data) + assert np.all(cp.mask == c.mask) + assert cp.attrs_equal(c) + assert cp.fill_value == -99 + assert cp._parent_table is None + assert repr(c) == repr(cp) + + +def test_pickle_multidimensional_column(protocol): + """Regression test for https://github.com/astropy/astropy/issues/4098""" + + a = np.zeros((3, 2)) + c = Column(a, name="a") + cs = pickle.dumps(c, protocol=protocol) + cp = pickle.loads(cs) + + assert np.all(c == cp) + assert c.shape == cp.shape + assert cp.attrs_equal(c) + assert repr(c) == repr(cp) + + +def test_pickle_table(protocol): + a = Column( + data=[1, 2], + name="a", + format="%05d", + description="col a", + unit="cm", + meta={"a": 1}, + ) + b = Column( + data=[3.0, 4.0], + name="b", + format="%05d", + description="col b", + unit="cm", + meta={"b": 1}, + ) + + for table_class in Table, QTable: + t = table_class([a, b], meta={"a": 1, "b": Quantity(10, unit="s")}) + t["c"] = Quantity([1, 2], unit="m") + t["d"] = Time(["2001-01-02T12:34:56", "2001-02-03T00:01:02"]) + t["e"] = SkyCoord([125.0, 180.0] * deg, [-45.0, 36.5] * deg) + + ts = pickle.dumps(t, protocol=protocol) + tp = pickle.loads(ts) + + assert tp.__class__ is table_class + assert np.all(tp["a"] == t["a"]) + assert np.all(tp["b"] == t["b"]) + + # test mixin columns + assert np.all(tp["c"] == t["c"]) + assert np.all(tp["d"] == t["d"]) + assert np.all(tp["e"].ra == t["e"].ra) + assert np.all(tp["e"].dec == t["e"].dec) + assert type(tp["c"]) is type(t["c"]) # nopep8 + assert type(tp["d"]) is type(t["d"]) # nopep8 + assert type(tp["e"]) is type(t["e"]) # nopep8 + assert tp.meta == t.meta + assert type(tp) is type(t) + + assert isinstance(tp["c"], Quantity if (table_class is QTable) else Column) + + +def test_pickle_masked_table(protocol): + a = Column( + data=[1, 2], + name="a", + format="%05d", + description="col a", + unit="cm", + meta={"a": 1}, + ) + b = Column( + data=[3.0, 4.0], + name="b", + format="%05d", + description="col b", + unit="cm", + meta={"b": 1}, + ) + t = Table([a, b], meta={"a": 1}, masked=True) + t["a"].mask[1] = True + t["a"].fill_value = -99 + + ts = pickle.dumps(t, protocol=protocol) + tp = pickle.loads(ts) + + for colname in ("a", "b"): + for attr in ("_data", "mask", "fill_value"): + assert np.all(getattr(tp[colname], attr) == getattr(tp[colname], attr)) + + assert tp["a"].attrs_equal(t["a"]) + assert tp["b"].attrs_equal(t["b"]) + assert tp.meta == t.meta + + +def test_pickle_masked_qtable(protocol): + t = QTable(meta={"a": 1}, masked=True) + t["a"] = Quantity([1, 2], unit="m") + t["b"] = Angle([1, 2], unit=deg) + + t["a"].mask[0] = True + t["b"].mask[1] = True + + ts = pickle.dumps(t, protocol=protocol) + tp = pickle.loads(ts) + + assert tp.__class__ is QTable + + assert np.all(tp["a"].mask == t["a"].mask) + assert np.all(tp["a"].unmasked == t["a"].unmasked) + assert np.all(tp["b"].mask == t["b"].mask) + assert np.all(tp["b"].unmasked == t["b"].unmasked) + assert type(tp["a"]) is type(t["a"]) # nopep8 + assert type(tp["b"]) is type(t["b"]) # nopep8 + assert tp.meta == t.meta + assert type(tp) is type(t) + + +def test_pickle_indexed_table(protocol): + """ + Ensure that any indices that have been added will survive pickling. + """ + t = simple_table() + t.add_index("a") + t.add_index(["a", "b"]) + ts = pickle.dumps(t, protocol=protocol) + tp = pickle.loads(ts) + + assert len(t.indices) == len(tp.indices) + for index, indexp in zip(t.indices, tp.indices): + assert np.all(index.data.data == indexp.data.data) + assert index.data.data.colnames == indexp.data.data.colnames diff --git a/astropy/table/tests/test_pprint.py b/astropy/table/tests/test_pprint.py index 5ccec4151714..0df67673288b 100644 --- a/astropy/table/tests/test_pprint.py +++ b/astropy/table/tests/test_pprint.py @@ -1,184 +1,1265 @@ -import pytest -import numpy as np +# Licensed under a 3-clause BSD style license - see LICENSE.rst -from .. import Table -from .. import pprint -BIG_WIDE_ARR = np.arange(2000, dtype=np.float).reshape(100, 20) -SMALL_ARR = np.arange(12, dtype=np.int).reshape(4, 3) +import sys +from io import StringIO +from shutil import get_terminal_size +import numpy as np +import pytest -class TestMultiD(): +from astropy import table +from astropy import units as u +from astropy.io import ascii +from astropy.table import Column, QTable, Table +from astropy.table.table_helpers import simple_table +from astropy.utils.console import conf as console_conf +from astropy.utils.exceptions import AstropyDeprecationWarning - def test_multidim(self): +BIG_WIDE_ARR = np.arange(2000, dtype=np.float64).reshape(100, 20) +SMALL_ARR = np.arange(18, dtype=np.int64).reshape(6, 3) + + +@pytest.mark.usefixtures("table_type") +class TestMultiD: + def test_multidim(self, table_type): """Test printing with multidimensional column""" - arr = [np.array([[1, 2], - [10, 20]]), - np.array([[3, 4], - [30, 40]]), - np.array([[5, 6], - [50, 60]])] - t = Table(arr) - lines = t.pformat() - print lines - assert lines == ['col0 [2] col1 [2] col2 [2]', - '-------- -------- --------', - ' 1 .. 2 3 .. 4 5 .. 6', - '10 .. 20 30 .. 40 50 .. 60'] - t = Table([arr]) - lines = t.pformat() - print lines - assert lines == ['col0 [2,2]', - '----------', - ' 1 .. 20', - ' 3 .. 40', - ' 5 .. 60'] + arr = [ + np.array([[1, 2], [10, 20]], dtype=np.int64), + np.array([[3, 4], [30, 40]], dtype=np.int64), + np.array([[5, 6], [50, 60]], dtype=np.int64), + ] + t = table_type(arr) + lines = t.pformat(show_dtype=True) + assert lines == [ + " col0 col1 col2 ", + "int64[2] int64[2] int64[2]", + "-------- -------- --------", + " 1 .. 2 3 .. 4 5 .. 6", + "10 .. 20 30 .. 40 50 .. 60", + ] + + lines = t.pformat(html=True, show_dtype=True) + assert lines == [ + f'
', + "", + "", + "", + "", + "
col0col1col2
int64[2]int64[2]int64[2]
1 .. 23 .. 45 .. 6
10 .. 2030 .. 4050 .. 60
", + ] + nbclass = table.conf.default_notebook_table_class + masked = "masked=True " if t.masked else "" + assert t._repr_html_().splitlines() == [ + f"
{table_type.__name__} {masked}length=2", + f'', + "", + "", + "", + "", + "
col0col1col2
int64[2]int64[2]int64[2]
1 .. 23 .. 45 .. 6
10 .. 2030 .. 4050 .. 60
", + ] + + t = table_type([arr]) + lines = t.pformat(show_dtype=True) + assert lines == [ + " col0 ", + "int64[2,2]", + "----------", + " 1 .. 20", + " 3 .. 40", + " 5 .. 60", + ] + + def test_fake_multidim(self, table_type): + """Test printing with 'fake' multidimensional column""" + arr = [ + np.array([[(1,)], [(10,)]], dtype=np.int64), + np.array([[(3,)], [(30,)]], dtype=np.int64), + np.array([[(5,)], [(50,)]], dtype=np.int64), + ] + t = table_type(arr) + lines = t.pformat(show_dtype=True) + assert lines == [ + " col0 col1 col2 ", + "int64[1,1] int64[1,1] int64[1,1]", + "---------- ---------- ----------", + " 1 3 5", + " 10 30 50", + ] + + lines = t.pformat(html=True, show_dtype=True) + assert lines == [ + f'', + "", + "", + "", + "", + "
col0col1col2
int64[1,1]int64[1,1]int64[1,1]
135
103050
", + ] + nbclass = table.conf.default_notebook_table_class + masked = "masked=True " if t.masked else "" + assert t._repr_html_().splitlines() == [ + f"
{table_type.__name__} {masked}length=2", + f'', + "", + "", + "", + "", + "
col0col1col2
int64[1,1]int64[1,1]int64[1,1]
135
103050
", + ] + + t = table_type([arr]) + lines = t.pformat(show_dtype=True) + assert lines == [ + " col0 ", + "int64[2,1,1]", + "------------", + " 1 .. 10", + " 3 .. 30", + " 5 .. 50", + ] -class TestPprint(): +def test_html_escaping(): + t = table.Table([('', 2, 3)]) + nbclass = table.conf.default_notebook_table_class + assert t._repr_html_().splitlines() == [ + "
Table length=3", + f'', + "", + "", + "", + "", + "", + "
col0
str33
<script>alert("gotcha");</script>
2
3
", + ] - def setup_method(self, method): - self.tb = Table(BIG_WIDE_ARR) - self.tb['col0'].format = '%e' - self.tb['col1'].format = '%.6f' - self.tb['col0'].units = 'km**2' - self.tb['col19'].units = 'kg sec m**-2' - self.ts = Table(SMALL_ARR) - def test_format0(self): +@pytest.mark.usefixtures("table_type") +class TestPprint: + def _setup(self, table_type): + self.tb = table_type(BIG_WIDE_ARR) + self.tb["col0"].format = "e" + self.tb["col1"].format = ".6f" + + self.tb["col0"].unit = "km**2" + self.tb["col19"].unit = "kg s m**-2" + self.ts = table_type(SMALL_ARR) + + def test_empty_table(self, table_type): + t = table_type() + lines = t.pformat() + assert lines == [""] + c = repr(t) + masked = "masked=True " if t.masked else "" + assert c.splitlines() == [ + f"<{table_type.__name__} {masked}length=0>", + "", + ] + + def test_format0(self, table_type): """Try getting screen size but fail to defaults because testing doesn't have access to screen (fcntl.ioctl fails). """ - arr = np.arange(4000, dtype=np.float).reshape(100, 40) - lines = Table(arr).pformat() - assert len(lines) == pprint.MAX_LINES - for line in lines: - assert (len(line) > pprint.MAX_WIDTH - 10 and - len(line) <= pprint.MAX_WIDTH) - - def test_format1(self): - """Basic test of formatting""" + self._setup(table_type) + arr = np.arange(4000, dtype=np.float64).reshape(100, 40) + with ( + console_conf.set_temp("max_width", None), + console_conf.set_temp("max_lines", None), + ): + lines = table_type(arr).pformat(max_lines=None, max_width=None) + width, nlines = get_terminal_size() + assert len(lines) == nlines + for line in lines[:-1]: # skip last "Length = .. rows" line + assert width - 10 < len(line) <= width + + def test_format1(self, table_type): + """Basic test of formatting, unit header row included""" + self._setup(table_type) lines = self.tb.pformat(max_lines=8, max_width=40) - assert lines == [' col0 col1 ... col19 ', - '------------ ----------- ... ------', - '0.000000e+00 1.000000 ... 19.0', - '2.000000e+01 21.000000 ... 39.0', - '4.000000e+01 41.000000 ... 59.0', - ' ... ... ... ...', - '1.960000e+03 1961.000000 ... 1979.0', - '1.980000e+03 1981.000000 ... 1999.0'] - - def test_format2(self): - """Include the units header row""" - lines = self.tb.pformat(max_lines=8, max_width=40, show_units=True) - assert lines == [' col0 ... col19 ', - ' km**2 ... kg sec m**-2', - '------------ ... ------------', - '0.000000e+00 ... 19.0', - '2.000000e+01 ... 39.0', - ' ... ... ...', - '1.960000e+03 ... 1979.0', - '1.980000e+03 ... 1999.0'] - - def test_format3(self): + assert lines == [ + " col0 col1 ... col19 ", + " km2 ... kg s / m2", + "------------ ----------- ... ---------", + "0.000000e+00 1.000000 ... 19.0", + " ... ... ... ...", + "1.960000e+03 1961.000000 ... 1979.0", + "1.980000e+03 1981.000000 ... 1999.0", + "Length = 100 rows", + ] + + def test_format2(self, table_type): + """Basic test of formatting, unit header row excluded""" + self._setup(table_type) + lines = self.tb.pformat(max_lines=8, max_width=40, show_unit=False) + assert lines == [ + " col0 col1 ... col19 ", + "------------ ----------- ... ------", + "0.000000e+00 1.000000 ... 19.0", + "2.000000e+01 21.000000 ... 39.0", + " ... ... ... ...", + "1.960000e+03 1961.000000 ... 1979.0", + "1.980000e+03 1981.000000 ... 1999.0", + "Length = 100 rows", + ] + + def test_format3(self, table_type): + """Include the unit header row""" + self._setup(table_type) + lines = self.tb.pformat(max_lines=8, max_width=40, show_unit=True) + + assert lines == [ + " col0 col1 ... col19 ", + " km2 ... kg s / m2", + "------------ ----------- ... ---------", + "0.000000e+00 1.000000 ... 19.0", + " ... ... ... ...", + "1.960000e+03 1961.000000 ... 1979.0", + "1.980000e+03 1981.000000 ... 1999.0", + "Length = 100 rows", + ] + + def test_format4(self, table_type): """Do not include the name header row""" + self._setup(table_type) lines = self.tb.pformat(max_lines=8, max_width=40, show_name=False) - assert lines == ['0.000000e+00 1.000000 ... 19.0', - '2.000000e+01 21.000000 ... 39.0', - '4.000000e+01 41.000000 ... 59.0', - '6.000000e+01 61.000000 ... 79.0', - ' ... ... ... ...', - '1.940000e+03 1941.000000 ... 1959.0', - '1.960000e+03 1961.000000 ... 1979.0', - '1.980000e+03 1981.000000 ... 1999.0'] - - def test_noclip(self): + assert lines == [ + " km2 ... kg s / m2", + "------------ ----------- ... ---------", + "0.000000e+00 1.000000 ... 19.0", + "2.000000e+01 21.000000 ... 39.0", + " ... ... ... ...", + "1.960000e+03 1961.000000 ... 1979.0", + "1.980000e+03 1981.000000 ... 1999.0", + "Length = 100 rows", + ] + + def test_noclip(self, table_type): """Basic table print""" + self._setup(table_type) lines = self.ts.pformat(max_lines=-1, max_width=-1) - assert lines == ['col0 col1 col2', - '---- ---- ----', - ' 0 1 2', - ' 3 4 5', - ' 6 7 8', - ' 9 10 11'] - - def test_clip1(self): - """max lines below hard limit of 6 - """ + assert lines == [ + "col0 col1 col2", + "---- ---- ----", + " 0 1 2", + " 3 4 5", + " 6 7 8", + " 9 10 11", + " 12 13 14", + " 15 16 17", + ] + + def test_clip1(self, table_type): + """max lines below hard limit of 8""" + self._setup(table_type) lines = self.ts.pformat(max_lines=3, max_width=-1) - assert lines == ['col0 col1 col2', - '---- ---- ----', - ' 0 1 2', - ' 3 4 5', - ' 6 7 8', - ' 9 10 11'] - - def test_clip2(self): - """max lines below hard limit of 6 and output longer than 6 - """ - lines = self.ts.pformat(max_lines=3, max_width=-1, show_units=True) - assert lines == ['col0 col1 col2', - ' ', - '---- ---- ----', - ' 0 1 2', - ' ... ... ...', - ' 9 10 11'] - - def test_clip3(self): - """Max lines below hard limit of 6 and max width below hard limit + assert lines == [ + "col0 col1 col2", + "---- ---- ----", + " 0 1 2", + " 3 4 5", + " 6 7 8", + " 9 10 11", + " 12 13 14", + " 15 16 17", + ] + + def test_clip2(self, table_type): + """max lines below hard limit of 8 and output longer than 8""" + self._setup(table_type) + lines = self.ts.pformat( + max_lines=3, max_width=-1, show_unit=True, show_dtype=True + ) + assert lines == [ + " col0 col1 col2", + " ", + "int64 int64 int64", + "----- ----- -----", + " 0 1 2", + " ... ... ...", + " 15 16 17", + "Length = 6 rows", + ] + + def test_clip3(self, table_type): + """Max lines below hard limit of 8 and max width below hard limit of 10 """ - lines = self.ts.pformat(max_lines=3, max_width=1, show_units=True) - assert lines == ['col0 ...', - ' ...', - '---- ...', - ' 0 ...', - ' ... ...', - ' 9 ...'] - - def test_clip4(self): + self._setup(table_type) + lines = self.ts.pformat(max_lines=3, max_width=1, show_unit=True) + assert lines == [ + "col0 ...", + " ...", + "---- ...", + " 0 ...", + " ... ...", + " 12 ...", + " 15 ...", + "Length = 6 rows", + ] + + def test_clip4(self, table_type): """Test a range of max_lines""" - for max_lines in range(130): - lines = self.tb.pformat(max_lines=max_lines) - assert len(lines) == max(6, min(102, max_lines)) + self._setup(table_type) + for max_lines in (0, 1, 4, 5, 6, 7, 8, 100, 101, 102, 103, 104, 130): + lines = self.tb.pformat(max_lines=max_lines, show_unit=False) + assert len(lines) == max(8, min(102, max_lines)) + def test_pformat_all(self, table_type): + """Test that all rows are printed by default""" + self._setup(table_type) + with pytest.warns( + AstropyDeprecationWarning, + match=( + r"The pformat_all function is deprecated " + r"and may be removed in a future version\." + ), + ): + lines = self.tb.pformat_all() + # +3 accounts for the three header lines in this table + assert len(lines) == BIG_WIDE_ARR.shape[0] + 3 -class TestFormat(): + def test_pprint_all(self, table_type, capsys): + """Test that all rows are printed by default""" + self._setup(table_type) + self.tb.pprint_all() + (out, err) = capsys.readouterr() + # +3 accounts for the three header lines in this table + assert len(out.splitlines()) == BIG_WIDE_ARR.shape[0] + 3 - def test_column_format(self): - t = Table([[1, 2], [3, 4]], names=('a', 'b')) + +class TestPprintColumn: + @pytest.mark.parametrize( + "scalar, exp", + [ + ( + 1, + [ + "None", + "----", + " 1", + ], + ), + ( + u.Quantity(0.6, "eV"), + [ + "None", + "----", + " 0.6", + ], + ), + ], + ) + def test_pprint_scalar(self, scalar, exp): + # see https://github.com/astropy/astropy/issues/12584 + c = Column(scalar) + + # Make sure pprint() does not raise an exception + c.pprint() + + # Check actual output + out = c.pformat() + assert out == exp + + +@pytest.mark.usefixtures("table_type") +class TestFormat: + def test_column_format(self, table_type): + t = table_type([[1, 2], [3, 4]], names=("a", "b")) # default (format=None) - assert str(t['a']) == ' a \n---\n 1\n 2' + assert str(t["a"]) == " a \n---\n 1\n 2" + + # just a plain format string + t["a"].format = "5.2f" + assert str(t["a"]) == " a \n-----\n 1.00\n 2.00" # Old-style that is almost new-style - t['a'].format = '{ %4.2f }' - assert str(t['a']) == ' a \n--------\n{ 1.00 }\n{ 2.00 }' + t["a"].format = "{ %4.2f }" + assert str(t["a"]) == " a \n--------\n{ 1.00 }\n{ 2.00 }" # New-style that is almost old-style - t['a'].format = '%{0:}' - assert str(t['a']) == ' a \n---\n %1\n %2' + t["a"].format = "%{0:}" + assert str(t["a"]) == " a \n---\n %1\n %2" # New-style with extra spaces - t['a'].format = ' {0:05d} ' - assert str(t['a']) == ' a \n-------\n 00001 \n 00002 ' + t["a"].format = " {0:05d} " + assert str(t["a"]) == " a \n-------\n 00001 \n 00002 " # New-style has precedence - t['a'].format = '%4.2f {0:}' - assert str(t['a']) == ' a \n-------\n%4.2f 1\n%4.2f 2' + t["a"].format = "%4.2f {0:}" + assert str(t["a"]) == " a \n-------\n%4.2f 1\n%4.2f 2" # Invalid format spec - t['a'].format = 'fail' with pytest.raises(ValueError): - str(t['a']) - - def test_column_format_with_threshold(self): - import astropy.table.pprint - MAX_LINES = pprint.MAX_LINES - pprint.MAX_LINES = 6 - t = Table([np.arange(20)], names=['a']) - t['a'].format = '%{0:}' - assert str(t['a']) == ' a \n---\n %0\n %1\n...\n%19' - t['a'].format = '{ %4.2f }' - assert str(t['a']) == ' a \n---------\n { 0.00 }\n' \ - ' { 1.00 }\n ...\n{ 19.00 }' - pprint.MAX_LINES = MAX_LINES + t["a"].format = "fail" + assert t["a"].format == "%4.2f {0:}" # format did not change + + def test_column_format_with_threshold(self, table_type): + with console_conf.set_temp("max_lines", 8): + t = table_type([np.arange(20)], names=["a"]) + t["a"].format = "%{0:}" + assert str(t["a"]).splitlines() == [ + " a ", + "---", + " %0", + " %1", + "...", + "%18", + "%19", + "Length = 20 rows", + ] + t["a"].format = "{ %4.2f }" + assert str(t["a"]).splitlines() == [ + " a ", + "---------", + " { 0.00 }", + " { 1.00 }", + " ...", + "{ 18.00 }", + "{ 19.00 }", + "Length = 20 rows", + ] + + def test_column_format_func(self, table_type): + # run most of functions twice + # 1) astropy.table.pprint._format_funcs gets populated + # 2) astropy.table.pprint._format_funcs gets used + + t = table_type([[1.0, 2.0], [3, 4]], names=("a", "b")) + + # mathematical function + t["a"].format = lambda x: str(x * 3.0) + assert str(t["a"]) == " a \n---\n3.0\n6.0" + assert str(t["a"]) == " a \n---\n3.0\n6.0" + + def test_column_format_callable(self, table_type): + # run most of functions twice + # 1) astropy.table.pprint._format_funcs gets populated + # 2) astropy.table.pprint._format_funcs gets used + + t = table_type([[1.0, 2.0], [3, 4]], names=("a", "b")) + + # mathematical function + class format: + def __call__(self, x): + return str(x * 3.0) + + t["a"].format = format() + assert str(t["a"]) == " a \n---\n3.0\n6.0" + assert str(t["a"]) == " a \n---\n3.0\n6.0" + + def test_column_format_func_wrong_number_args(self, table_type): + t = table_type([[1.0, 2.0], [3, 4]], names=("a", "b")) + + # function that expects wrong number of arguments + def func(a, b): + pass + + with pytest.raises(ValueError): + t["a"].format = func + + def test_column_format_func_multiD(self, table_type): + arr = [np.array([[1, 2], [10, 20]], dtype="i8")] + t = table_type(arr, names=["a"]) + + # mathematical function + t["a"].format = lambda x: str(x * 3.0) + outstr = [ + " a ", + "------------", + " 3.0 .. 6.0", + "30.0 .. 60.0", + ] + assert str(t["a"]).splitlines() == outstr + + def test_column_format_func_not_str(self, table_type): + t = table_type([[1.0, 2.0], [3, 4]], names=("a", "b")) + + # mathematical function + with pytest.raises(ValueError): + t["a"].format = lambda x: x * 3 + + def test_column_alignment(self, table_type): + t = table_type( + [[1], [2], [3], [4]], + names=("long title a", "long title b", "long title c", "long title d"), + ) + t["long title a"].format = "<" + t["long title b"].format = "^" + t["long title c"].format = ">" + t["long title d"].format = "0=" + assert str(t["long title a"]) == "long title a\n------------\n1 " + assert str(t["long title b"]) == "long title b\n------------\n 2 " + assert str(t["long title c"]) == "long title c\n------------\n 3" + assert str(t["long title d"]) == "long title d\n------------\n000000000004" + + +class TestFormatWithMaskedElements: + def test_column_format(self): + t = Table([[1, 2, 3], [3, 4, 5]], names=("a", "b"), masked=True) + t["a"].mask = [True, False, True] + # default (format=None) + assert str(t["a"]) == " a \n---\n --\n 2\n --" + + # just a plain format string + t["a"].format = "5.2f" + assert str(t["a"]) == " a \n-----\n --\n 2.00\n --" + + # Old-style that is almost new-style + t["a"].format = "{ %4.2f }" + assert str(t["a"]) == " a \n--------\n --\n{ 2.00 }\n --" + + # New-style that is almost old-style + t["a"].format = "%{0:}" + assert str(t["a"]) == " a \n---\n --\n %2\n --" + + # New-style with extra spaces + t["a"].format = " {0:05d} " + assert str(t["a"]) == " a \n-------\n --\n 00002 \n --" + + # New-style has precedence + t["a"].format = "%4.2f {0:}" + assert str(t["a"]) == " a \n-------\n --\n%4.2f 2\n --" + + def test_column_format_with_threshold_masked_table(self): + with console_conf.set_temp("max_lines", 8): + t = Table([np.arange(20)], names=["a"], masked=True) + t["a"].format = "%{0:}" + t["a"].mask[0] = True + t["a"].mask[-1] = True + assert str(t["a"]).splitlines() == [ + " a ", + "---", + " --", + " %1", + "...", + "%18", + " --", + "Length = 20 rows", + ] + t["a"].format = "{ %4.2f }" + assert str(t["a"]).splitlines() == [ + " a ", + "---------", + " --", + " { 1.00 }", + " ...", + "{ 18.00 }", + " --", + "Length = 20 rows", + ] + + def test_column_format_func(self): + # run most of functions twice + # 1) astropy.table.pprint._format_funcs gets populated + # 2) astropy.table.pprint._format_funcs gets used + + t = Table([[1.0, 2.0, 3.0], [3, 4, 5]], names=("a", "b"), masked=True) + t["a"].mask = [True, False, True] + # mathematical function + t["a"].format = lambda x: str(x * 3.0) + assert str(t["a"]) == " a \n---\n --\n6.0\n --" + assert str(t["a"]) == " a \n---\n --\n6.0\n --" + + def test_column_format_func_with_special_masked(self): + # run most of functions twice + # 1) astropy.table.pprint._format_funcs gets populated + # 2) astropy.table.pprint._format_funcs gets used + + t = Table([[1.0, 2.0, 3.0], [3, 4, 5]], names=("a", "b"), masked=True) + t["a"].mask = [True, False, True] + # mathematical function + + def format_func(x): + if x is np.ma.masked: + return "!!" + else: + return str(x * 3.0) + + t["a"].format = format_func + assert str(t["a"]) == " a \n---\n !!\n6.0\n !!" + assert str(t["a"]) == " a \n---\n !!\n6.0\n !!" + + def test_column_format_callable(self): + # run most of functions twice + # 1) astropy.table.pprint._format_funcs gets populated + # 2) astropy.table.pprint._format_funcs gets used + + t = Table([[1.0, 2.0, 3.0], [3, 4, 5]], names=("a", "b"), masked=True) + t["a"].mask = [True, False, True] + + # mathematical function + class format: + def __call__(self, x): + return str(x * 3.0) + + t["a"].format = format() + assert str(t["a"]) == " a \n---\n --\n6.0\n --" + assert str(t["a"]) == " a \n---\n --\n6.0\n --" + + def test_column_format_func_wrong_number_args(self): + t = Table([[1.0, 2.0], [3, 4]], names=("a", "b"), masked=True) + t["a"].mask = [True, False] + + # function that expects wrong number of arguments + def func(a, b): + pass + + with pytest.raises(ValueError): + t["a"].format = func + + # but if all are masked, it never gets called + t["a"].mask = [True, True] + assert str(t["a"]) == " a \n---\n --\n --" + + def test_column_format_func_multiD(self): + arr = [np.array([[1, 2], [10, 20]], dtype="i8")] + t = Table(arr, names=["a"], masked=True) + t["a"].mask[0, 1] = True + t["a"].mask[1, 1] = True + # mathematical function + t["a"].format = lambda x: str(x * 3.0) + outstr = [ + " a ", + "----------", + " 3.0 .. --", + "30.0 .. --", + ] + assert str(t["a"]).splitlines() == outstr + assert str(t["a"]).splitlines() == outstr + + +def test_pprint_npfloat32(): + """ + Test for #148, that np.float32 cannot by itself be formatted as float, + but has to be converted to a python float. + """ + dat = np.array([1.0, 2.0], dtype=np.float32) + t = Table([dat], names=["a"]) + t["a"].format = "5.2f" + assert str(t["a"]) == " a \n-----\n 1.00\n 2.00" + + +def test_pprint_py3_bytes(): + """ + Test for #1346 and #4944. Make sure a bytestring (dtype=S) in Python 3 + is printed correctly (without the "b" prefix like b'string'). + """ + val = bytes("val", encoding="utf-8") + blah = "bläh".encode() + dat = np.array([val, blah], dtype=[("col", "S10")]) + t = table.Table(dat) + assert t["col"].pformat() == ["col ", "----", " val", "bläh"] + + +def test_pprint_structured(): + su = table.Column( + [ + (1, (1.5, [1.6, 1.7])), + (2, (2.5, [2.6, 2.7])), + ], + name="su", + dtype=[ + ("i", np.int64), + ("f", [("p0", np.float64), ("p1", np.float64, (2,))]), + ], + ) + assert su.pformat() == [ + " su [i, f[p0, p1]] ", + "----------------------", + "(1, (1.5, [1.6, 1.7]))", + "(2, (2.5, [2.6, 2.7]))", + ] + t = table.Table([su]) + assert t.pformat() == su.pformat() + assert repr(t).splitlines() == [ + "", + " su [i, f[p0, p1]] ", + "(int64, (float64, float64[2]))", + "------------------------------", + " (1, (1.5, [1.6, 1.7]))", + " (2, (2.5, [2.6, 2.7]))", + ] + + +def test_pprint_structured_with_format(): + dtype = np.dtype([("par", "f8"), ("min", "f8"), ("id", "i4"), ("name", "U4")]) + c = table.Column( + [ + (1.2345678, -20, 3, "bar"), + (12.345678, 4.5678, 33, "foo"), + ], + dtype=dtype, + ) + t = table.Table() + t["a"] = [1, 2] + t["c"] = c + t["c"].info.format = "{par:6.2f} {min:5.1f} {id:03d} {name:4s}" + exp = [ + " a c [par, min, id, name]", + "--- ----------------------", + " 1 1.23 -20.0 003 bar ", + " 2 12.35 4.6 033 foo ", + ] + assert t.pformat() == exp + + +def test_pprint_nameless_col(): + """Regression test for #2213, making sure a nameless column can be printed + using None as the name. + """ + col = table.Column([1.0, 2.0]) + assert str(col).startswith("None") + + +def test_html(): + """Test HTML printing""" + dat = np.array([1.0, 2.0], dtype=np.float32) + t = Table([dat], names=["a"]) + + lines = t.pformat(html=True) + assert lines == [ + f'
', + "", + "", + "", + "
a
1.0
2.0
", + ] + + lines = t.pformat(html=True, tableclass="table-striped") + assert lines == [ + f'', + "", + "", + "", + "
a
1.0
2.0
", + ] + + lines = t.pformat(html=True, tableclass=["table", "table-striped"]) + assert lines == [ + f'', + "", + "", + "", + "
a
1.0
2.0
", + ] + + +def test_align(): + t = simple_table(2, kinds="iS") + assert t.pformat() == [ + " a b ", + "--- ---", + " 1 b", + " 2 c", + ] + # Use column format attribute + t["a"].format = "<" + assert t.pformat() == [ + " a b ", + "--- ---", + "1 b", + "2 c", + ] + + # Now override column format attribute with various combinations of align + tpf = [" a b ", "--- ---", " 1 b ", " 2 c "] + for align in ("^", ["^", "^"], ("^", "^")): + assert tpf == t.pformat(align=align) + + assert t.pformat(align="<") == [ + " a b ", + "--- ---", + "1 b ", + "2 c ", + ] + assert t.pformat(align="0=") == [ + " a b ", + "--- ---", + "001 00b", + "002 00c", + ] + + assert t.pformat(align=["<", "^"]) == [ + " a b ", + "--- ---", + "1 b ", + "2 c ", + ] + + # Now use fill characters. Stress the system using a fill + # character that is the same as an align character. + t = simple_table(2, kinds="iS") + + assert t.pformat(align="^^") == [ + " a b ", + "--- ---", + "^1^ ^b^", + "^2^ ^c^", + ] + + assert t.pformat(align="^>") == [ + " a b ", + "--- ---", + "^^1 ^^b", + "^^2 ^^c", + ] + + assert t.pformat(align="^<") == [ + " a b ", + "--- ---", + "1^^ b^^", + "2^^ c^^", + ] + + # Complicated interaction (same as narrative docs example) + t1 = Table([[1.0, 2.0], [1, 2]], names=["column1", "column2"]) + t1["column1"].format = "#^.2f" + + assert t1.pformat() == [ + "column1 column2", + "------- -------", + "##1.00# 1", + "##2.00# 2", + ] + + assert t1.pformat(align="!<") == [ + "column1 column2", + "------- -------", + "1.00!!! 1!!!!!!", + "2.00!!! 2!!!!!!", + ] + + assert t1.pformat(align=[None, "!<"]) == [ + "column1 column2", + "------- -------", + "##1.00# 1!!!!!!", + "##2.00# 2!!!!!!", + ] + + # Zero fill + t["a"].format = "+d" + assert t.pformat(align="0=") == [ + " a b ", + "--- ---", + "+01 00b", + "+02 00c", + ] + + with pytest.raises(ValueError): + t.pformat(align=["fail"]) + + with pytest.raises(TypeError): + t.pformat(align=0) + + with pytest.raises(TypeError): + t.pprint(align=0) + + # Make sure pprint() does not raise an exception + t.pprint() + + with pytest.raises(ValueError): + t.pprint(align=["<", "<", "<"]) + + with pytest.raises(ValueError): + t.pprint(align="x=") + + +def test_auto_format_func(): + """Test for #5802 (fix for #5800 where format_func key is not unique)""" + t = Table([[1, 2] * u.m]) + t["col0"].format = "%f" + t.pformat() # Force caching of format function + + qt = QTable(t) + qt.pformat() # Generates exception prior to #5802 + + +def test_decode_replace(): + """ + Test printing a bytestring column with a value that fails + decoding to utf-8 and gets replaced by U+FFFD. See + https://docs.python.org/3/library/codecs.html#codecs.replace_errors + """ + t = Table([[b"Z\xf0"]]) + assert t.pformat() == [ + "col0", + "----", + " Z\ufffd", + ] + + +class TestColumnsShowHide: + """Tests of show and hide table columns""" + + def setup_method(self): + self.t = simple_table(size=1, cols=4, kinds="i") + + @pytest.mark.parametrize("attr", ("pprint_exclude_names", "pprint_include_names")) + def test_basic(self, attr): + t = self.t + assert ( + repr(getattr(Table, attr)) + == f"" + ) + + t_show_hide = getattr(t, attr) + assert repr(t_show_hide) == f"" + + # Default value is None + assert t_show_hide() is None + + def test_slice(self): + t = self.t + t.pprint_include_names = "a" + t.pprint_exclude_names = "b" + t2 = t[0:1] + assert t2.pprint_include_names() == ("a",) + assert t2.pprint_exclude_names() == ("b",) + + def test_copy(self): + t = self.t + t.pprint_include_names = "a" + t.pprint_exclude_names = "b" + + t2 = t.copy() + assert t2.pprint_include_names() == ("a",) + assert t2.pprint_exclude_names() == ("b",) + + t2.pprint_include_names = "c" + t2.pprint_exclude_names = "d" + assert t.pprint_include_names() == ("a",) + assert t.pprint_exclude_names() == ("b",) + assert t2.pprint_include_names() == ("c",) + assert t2.pprint_exclude_names() == ("d",) + + @pytest.mark.parametrize("attr", ("pprint_exclude_names", "pprint_include_names")) + @pytest.mark.parametrize("value", ("z", ["a", "z"])) + def test_setting(self, attr, value): + t = self.t + t_show_hide = getattr(t, attr) + + # Expected attribute value ('z',) or ('a', 'z') + exp = (value,) if isinstance(value, str) else tuple(value) + + # Context manager, can include column names that do not exist + with t_show_hide.set(value): + assert t_show_hide() == exp + assert t.meta["__attributes__"] == {attr: exp} + assert t_show_hide() is None + + # Setting back to None clears out meta + assert t.meta == {} + + # Do `t.pprint_include_names/hide = value` + setattr(t, attr, value) + assert t_show_hide() == exp + + # Clear attribute + t_show_hide.set(None) + assert t_show_hide() is None + + # Now use set() method + t_show_hide.set(value) + assert t_show_hide() == exp + + with t_show_hide.set(None): + assert t_show_hide() is None + assert t.meta == {} + assert t_show_hide() == exp + + @pytest.mark.parametrize("attr", ("pprint_exclude_names", "pprint_include_names")) + @pytest.mark.parametrize("value", ("z", ["a", "z"], ("a", "z"))) + def test_add_remove(self, attr, value): + t = self.t + t_show_hide = getattr(t, attr) + + # Expected attribute value ('z') or ('a', 'z') + exp = (value,) if isinstance(value, str) else tuple(value) + + # add() method for str or list of str + t_show_hide.add(value) + assert t_show_hide() == exp + + # Adding twice has no effect + t_show_hide.add(value) + assert t_show_hide() == exp + + # Remove values (str or list of str). Reverts to None if all names are + # removed. + t_show_hide.remove(value) + assert t_show_hide() is None + + # Remove just one name, possibly leaving a name. + t_show_hide.add(value) + t_show_hide.remove("z") + assert t_show_hide() == (None if value == "z" else ("a",)) + + # Cannot remove name not in the list + t_show_hide.set(["a", "z"]) + with pytest.raises(ValueError, match=f"x not in {attr}"): + t_show_hide.remove(("x", "z")) + + @pytest.mark.parametrize("attr", ("pprint_exclude_names", "pprint_include_names")) + def test_rename(self, attr): + t = self.t + t_hide_show = getattr(t, attr) + t_hide_show.set(["a", "b"]) + t.rename_column("a", "aa") + assert t_hide_show() == ("aa", "b") + + @pytest.mark.parametrize("attr", ("pprint_exclude_names", "pprint_include_names")) + def test_remove(self, attr): + t = self.t + t_hide_show = getattr(t, attr) + t_hide_show.set(["a", "b"]) + del t["a"] + assert t_hide_show() == ("b",) + + def test_serialization(self): + # Serialization works for ECSV. Currently fails for FITS, works with + # HDF5. + t = self.t + t.pprint_exclude_names = ["a", "y"] + t.pprint_include_names = ["b", "z"] + + out = StringIO() + ascii.write(t, out, format="ecsv") + t2 = ascii.read(out.getvalue(), format="ecsv") + + assert t2.pprint_exclude_names() == ("a", "y") + assert t2.pprint_include_names() == ("b", "z") + + def test_output(self): + """Test that pprint_include/exclude_names actually changes the print output""" + t = self.t + exp = [ + " b d ", + "--- ---", + " 2 4", + ] + + with t.pprint_exclude_names.set(["a", "c"]): + out = t.pformat() + assert out == exp + + with t.pprint_include_names.set(["b", "d"]): + out = t.pformat() + assert out == exp + + with t.pprint_exclude_names.set(["a", "c"]): + out = t.pformat() + assert out == exp + + with t.pprint_include_names.set(["b", "d"]): + out = t.pformat() + assert out == exp + + with ( + t.pprint_include_names.set(["b", "c", "d"]), + t.pprint_exclude_names.set(["c"]), + ): + out = t.pformat() + assert out == exp + + def test_output_globs(self): + """Test that pprint_include/exclude_names works with globs (fnmatch)""" + t = self.t + t["a2"] = 1 + t["a23"] = 2 + + # Show only the a* columns + exp = [ + " a a2 a23", + "--- --- ---", + " 1 1 2", + ] + with t.pprint_include_names.set("a*"): + out = t.pformat() + assert out == exp + + # Show a* but exclude a?? + exp = [ + " a a2", + "--- ---", + " 1 1", + ] + with t.pprint_include_names.set("a*"), t.pprint_exclude_names.set("a??"): + out = t.pformat() + assert out == exp + + # Exclude a?? + exp = [ + " a b c d a2", + "--- --- --- --- ---", + " 1 2 3 4 1", + ] + with t.pprint_exclude_names.set("a??"): + out = t.pformat() + assert out == exp + + +def test_embedded_newline_tab(): + """Newlines and tabs are escaped in table repr""" + t = Table( + rows=[ + ["a", "b \n c \t \n d"], + ["x", "y\n"], + ] + ) + exp = [ + r"col0 col1 ", + r"---- --------------", + r" a b \n c \t \n d", + r" x y\n", + ] + assert t.pformat() == exp + + +def test_multidims_with_zero_dim(): + """Test of fix for #13836 when a zero-dim column is present""" + t = Table() + t["a"] = ["a", "b"] + t["b"] = np.ones(shape=(2, 0, 1), dtype=np.float64) + exp = [ + " a b ", + "str1 float64[0,1]", + "---- ------------", + " a ", + " b ", + ] + assert t.pformat(show_dtype=True) == exp + + +def test_zero_length_string(): + data = np.array([("", 12)], dtype=[("a", "S"), ("b", "i4")]) + t = Table(data, copy=False) + exp = [ + " a b ", + "bytes0 int32", + "------ -----", + " 12", + ] + assert t.pformat(show_dtype=True) == exp + + +def test_multidim_threshold_default(): + """Test default behavior (threshold=1) shows only first and last elements""" + # Default threshold is 1, so size > 1 will show "first .. last" + # Uses astropy.table.conf.format_size_threshold + with table.conf.set_temp("format_size_threshold", 1): + data = np.array([[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]]) + col = Column(data, name="test") + lines = col.pformat(show_name=False, show_unit=False) + assert [line.strip() for line in lines] == ["1 .. 5", "6 .. 10"] + + +def test_multidim_threshold_show_all(): + """Test large threshold shows all elements""" + # Use sys.maxsize or a large value to show all elements + # Uses astropy.table.conf.format_size_threshold + with table.conf.set_temp("format_size_threshold", sys.maxsize): + data = np.array([[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]]) + col = Column(data, name="test") + lines = col.pformat(show_name=False, show_unit=False) + assert [line.strip() for line in lines] == ["[1 2 3 4 5]", "[6 7 8 9 10]"] + + +def test_multidim_threshold_partial(): + """Test threshold shows elements with ellipsis when size > threshold""" + # Size is 10 (10 elements in the array), threshold is 4 + # Since 10 > 4, should show "first .. last" + # Uses astropy.table.conf.format_size_threshold + with table.conf.set_temp("format_size_threshold", 4): + data = np.array([[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]]) + col = Column(data, name="test") + lines = col.pformat(show_name=False, show_unit=False) + assert [line.strip() for line in lines] == ["0 .. 9"] + + +def test_multidim_threshold_three_vectors(): + """Test threshold=3 for 3-element vectors (common use case)""" + # Uses astropy.table.conf.format_size_threshold + with table.conf.set_temp("format_size_threshold", 3): + data = np.array([[1, 2, 3], [4, 5, 6]]) + col = Column(data, name="position") + lines = col.pformat(show_name=False, show_unit=False) + assert lines == ["[1 2 3]", "[4 5 6]"] + + +def test_multidim_threshold_with_formatting(): + """Test threshold works with column formatting""" + # Uses astropy.table.conf.format_size_threshold + with table.conf.set_temp("format_size_threshold", sys.maxsize): + data = np.array([[1.23456, 2.34567, 3.45678]], dtype=float) + col = Column(data, name="float", format="%.2f") + lines = col.pformat(show_name=False, show_unit=False) + assert lines == ["[1.23 2.35 3.46]"] + + +def test_multidim_threshold_2x3_array(): + """Test 2x3 array display with different thresholds""" + # 2x3 array has size = 2*3 = 6 + data = np.array([[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]]) + col = Column(data, name="test") + + # With threshold=1, size=6 > 1, so show "first .. last" + # Uses astropy.table.conf.format_size_threshold + with table.conf.set_temp("format_size_threshold", 1): + lines = col.pformat(show_name=False, show_unit=False) + assert [line.strip() for line in lines] == ["1 .. 6", "7 .. 12"] + + # With threshold=6, size=6 <= 6, so show full element + # Uses astropy.table.conf.format_size_threshold + with table.conf.set_temp("format_size_threshold", 6): + lines = col.pformat(show_name=False, show_unit=False) + assert [line.strip() for line in lines] == [ + "[[1 2 3] [4 5 6]]", + "[[7 8 9] [10 11 12]]", + ] + + # With threshold=5, size=6 > 5, so show "first .. last" + # Uses astropy.table.conf.format_size_threshold + with table.conf.set_temp("format_size_threshold", 5): + lines = col.pformat(show_name=False, show_unit=False) + assert [line.strip() for line in lines] == ["1 .. 6", "7 .. 12"] + + +def test_multidim_threshold_3d_array(): + """Test 3D array display. + + Each table element has shape (2, 3, 2); the full column shape is (2, 2, 3, 2) + where the first dimension (2) is the number of rows, and the remaining dimensions + (2, 3, 2) define the shape of each element. Size = 2*3*2 = 12. + """ + data = np.arange(2 * 2 * 3 * 2).reshape(2, 2, 3, 2) + col = Column(data, name="test") + + # With threshold=1, size=12 > 1, so show "first .. last" + # Uses astropy.table.conf.format_size_threshold + with table.conf.set_temp("format_size_threshold", 1): + lines = col.pformat(show_name=False, show_unit=False) + assert [line.strip() for line in lines] == ["0 .. 11", "12 .. 23"] + + # With threshold=12, size=12 <= 12, so show full element with brackets + # Uses astropy.table.conf.format_size_threshold + with table.conf.set_temp("format_size_threshold", 12): + lines = col.pformat(show_name=False, show_unit=False) + # Each row has shape (2, 3, 2) = [[[0,1] [2,3] [4,5]] [[6,7] [8,9] [10,11]]] + assert [line.strip() for line in lines] == [ + "[[[0 1] [2 3] [4 5]] [[6 7] [8 9] [10 11]]]", + "[[[12 13] [14 15] [16 17]] [[18 19] [20 21] [22 23]]]", + ] + + +def test_multidim_threshold_2x2_array_with_threshold_4(): + """Test that 2x2 array (size=4) is fully shown when threshold=4""" + data = np.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]]) + col = Column(data, name="test") + + # With threshold=4, size=4 <= 4, so show full element + # Uses astropy.table.conf.format_size_threshold + with table.conf.set_temp("format_size_threshold", 4): + lines = col.pformat(show_name=False, show_unit=False) + assert lines == ["[[1 2] [3 4]]", "[[5 6] [7 8]]"] diff --git a/astropy/table/tests/test_row.py b/astropy/table/tests/test_row.py index 0e0d47fbb111..d697d8f069d7 100644 --- a/astropy/table/tests/test_row.py +++ b/astropy/table/tests/test_row.py @@ -1,26 +1,62 @@ -import pytest +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +import sys + import numpy as np -from .. import Column, Row, Table +import pytest +from numpy.testing import assert_array_equal + +from astropy import table +from astropy import units as u +from astropy.table import Row + +from .conftest import MaskedTable + +def test_masked_row_with_object_col(): + """ + Numpy < 1.8 has a bug in masked array that prevents access a row if there is + a column with object type. + """ + t = table.Table([[1]], dtype=["O"], masked=True) + t["col0"].mask = False + assert t[0]["col0"] == 1 + t["col0"].mask = True + assert t[0]["col0"] is np.ma.masked -class TestRow(): - def setup_method(self, method): - self.a = Column('a', [1, 2, 3], dtype='i8') - self.b = Column('b', [4, 5, 6], dtype='i8') - self.t = Table([self.a, self.b]) +@pytest.mark.usefixtures("table_types") +class TestRow: + def _setup(self, table_types): + self._table_type = table_types.Table + self._column_type = table_types.Column - def test_subclass(self): + @property + def t(self): + # pytest wants to run this method once before table_types is run + # to set Table and Column. In this case just return None, which would + # cause any downstream test to fail if this happened in any other context. + if self._column_type is None: + return None + if not hasattr(self, "_t"): + a = self._column_type(name="a", data=[1, 2, 3], dtype="i8") + b = self._column_type(name="b", data=[4, 5, 6], dtype="i8") + self._t = self._table_type([a, b]) + return self._t + + def test_subclass(self, table_types): """Row is subclass of ndarray and Row""" + self._setup(table_types) c = Row(self.t, 2) assert isinstance(c, Row) - def test_values(self): + def test_values(self, table_types): """Row accurately reflects table values and attributes""" + self._setup(table_types) table = self.t row = table[1] - assert row['a'] == 2 - assert row['b'] == 5 + assert row["a"] == 2 + assert row["b"] == 5 assert row[0] == 2 assert row[1] == 5 assert row.meta is table.meta @@ -28,61 +64,325 @@ def test_values(self): assert row.columns is table.columns with pytest.raises(IndexError): row[2] - assert str(row.dtype) == "[('a', 'i8'), ('b', '>i8')]" - def test_ref(self): + def test_ref(self, table_types): """Row is a reference into original table data""" + self._setup(table_types) table = self.t row = table[1] - row['a'] = 10 - assert table['a'][1] == 10 + row["a"] = 10 + if table_types.Table is not MaskedTable: + assert table["a"][1] == 10 - def test_left_equal(self): + def test_left_equal(self, table_types): """Compare a table row to the corresponding structured array row""" - np_t = self.t._data.copy() - for row, np_row in zip(self.t, np_t): - assert np.all(row == np_row) + self._setup(table_types) + np_t = self.t.as_array() + if table_types.Table is MaskedTable: + with pytest.raises(ValueError): + self.t[0] == np_t[0] # noqa: B015 + else: + for row, np_row in zip(self.t, np_t): + assert np.all(row == np_row) - def test_left_not_equal(self): + def test_left_not_equal(self, table_types): """Compare a table row to the corresponding structured array row""" - np_t = self.t._data.copy() - np_t['a'] = [0, 0, 0] - for row, np_row in zip(self.t, np_t): - assert np.all(row != np_row) + self._setup(table_types) + np_t = self.t.as_array() + np_t["a"] = [0, 0, 0] + if table_types.Table is MaskedTable: + with pytest.raises(ValueError): + self.t[0] == np_t[0] # noqa: B015 + else: + for row, np_row in zip(self.t, np_t): + assert np.all(row != np_row) - def test_right_equal(self): + def test_right_equal(self, table_types): """Test right equal""" - np_t = self.t._data.copy() - for row, np_row in zip(self.t, np_t): - assert np.all(np_row == row) - - @pytest.mark.xfail - def test_set_slice(self): - """Set row elements with a slice + self._setup(table_types) + np_t = self.t.as_array() + if table_types.Table is MaskedTable: + with pytest.raises(ValueError): + self.t[0] == np_t[0] # noqa: B015 + else: + for row, np_row in zip(self.t, np_t): + assert np.all(np_row == row) - This currently fails because the underlying np.void object - row.data = table._data[index] does not support slice assignment. - """ - table = self.t - row = table[0] - row[:] = [-1, -1] - row[:1] = np.array([-2]) - assert np.all(table._data == np.array([[-1, -1], - [-2, 5], - [3, 6]])) - - def test_convert_numpy_array(self): + def test_convert_numpy_array(self, table_types): + self._setup(table_types) d = self.t[1] np_data = np.array(d) - assert np.all(np_data == d._data) - assert not np_data is d._data + if table_types.Table is not MaskedTable: + assert np.all(np_data == d.as_void()) + assert np_data is not d.as_void() assert d.colnames == list(np_data.dtype.names) - np_data = np.array(d, copy=False) - assert np.all(np_data == d._data) - assert not np_data is d._data + np_data = np.asarray(d) + if table_types.Table is not MaskedTable: + assert np.all(np_data == d.as_void()) + assert np_data is not d.as_void() assert d.colnames == list(np_data.dtype.names) with pytest.raises(ValueError): - np_data = np.array(d, dtype=[('c', 'i8'), ('d', 'i8')]) + np_data = np.array(d, dtype=[("c", "i8"), ("d", "i8")]) + + def test_format_row(self, table_types): + """Test formatting row""" + self._setup(table_types) + table = self.t + row = table[0] + assert repr(row).splitlines() == [ + "<{} {}{}>".format( + row.__class__.__name__, + "index=0", + " masked=True" if table.masked else "", + ), + " a b ", + "int64 int64", + "----- -----", + " 1 4", + ] + assert str(row).splitlines() == [" a b ", "--- ---", " 1 4"] + + assert row._repr_html_().splitlines() == [ + "{} {}{}".format( + row.__class__.__name__, + "index=0", + " masked=True" if table.masked else "", + ), + f'', + "", + "", + "", + "
ab
int64int64
14
", + ] + + def test_as_void(self, table_types): + """Test the as_void() method""" + self._setup(table_types) + table = self.t + row = table[0] + + # If masked then with no masks, issue numpy/numpy#483 should come + # into play. Make sure as_void() code is working. + row_void = row.as_void() + if table.masked: + assert isinstance(row_void, np.ma.mvoid) + else: + assert isinstance(row_void, np.void) + assert row_void["a"] == 1 + assert row_void["b"] == 4 + + # Confirm row is a view of table but row_void is not. + table["a"][0] = -100 + assert row["a"] == -100 + assert row_void["a"] == 1 + + # Make sure it works for a table that has masked elements + if table.masked: + table["a"].mask = True + + # row_void is not a view, need to re-make + assert row_void["a"] == 1 + row_void = row.as_void() # but row is a view + assert row["a"] is np.ma.masked + + def test_row_and_as_void_with_objects(self, table_types): + """Test the deprecated data property and as_void() method""" + t = table_types.Table([[{"a": 1}, {"b": 2}]], names=("a",)) + assert t[0][0] == {"a": 1} + assert t[0]["a"] == {"a": 1} + assert t[0].as_void()[0] == {"a": 1} + assert t[0].as_void()["a"] == {"a": 1} + + def test_bounds_checking(self, table_types): + """Row gives index error upon creation for out-of-bounds index""" + self._setup(table_types) + for ibad in (-5, -4, 3, 4): + with pytest.raises(IndexError): + self.t[ibad] + + def test_create_rows_from_list(self, table_types): + """https://github.com/astropy/astropy/issues/8976""" + orig_tab = table_types.Table([[1, 2, 3], [4, 5, 6]], names=("a", "b")) + new_tab = type(orig_tab)(rows=list(orig_tab), names=orig_tab.dtype.names) + assert np.all(orig_tab == new_tab) + + def test_row_keys_values(self, table_types): + self._setup(table_types) + row = self.t[0] + for row_key, col_key in zip(row.keys(), self.t.columns.keys()): + assert row_key == col_key + + for row_value, col in zip(row.values(), self.t.columns.values()): + assert row_value == col[0] + + def test_row_as_mapping(self, table_types): + self._setup(table_types) + row = self.t[0] + row_dict = dict(row) + for key, value in row_dict.items(): + assert row[key] == value + + def f(**kwargs): + return kwargs + + row_splatted = f(**row) + for key, value in row_splatted.items(): + assert row[key] == value + + def test_row_as_sequence(self, table_types): + self._setup(table_types) + row = self.t[0] + row_tuple = tuple(row) + keys = tuple(row.keys()) + for key, value in zip(keys, row_tuple): + assert row[key] == value + + def f(*args): + return args + + row_splatted = f(*row) + for key, value in zip(keys, row_splatted): + assert row[key] == value + + +def test_row_tuple_column_slice(): + """ + Test getting and setting a row using a tuple or list of column names + """ + t = table.QTable( + [ + [1, 2, 3] * u.m, + [10.0, 20.0, 30.0], + [100.0, 200.0, 300.0], + ["x", "y", "z"], + ], + names=["a", "b", "c", "d"], + ) + # Get a row for index=1 + r1 = t[1] + # Column slice with tuple of col names + r1_abc = r1["a", "b", "c"] # Row object for these cols + r1_abc_repr = [ + "", + " a b c ", + " m ", + "float64 float64 float64", + "------- ------- -------", + " 2.0 20.0 200.0", + ] + assert repr(r1_abc).splitlines() == r1_abc_repr + + # Column slice with list of col names + r1_abc = r1[["a", "b", "c"]] + assert repr(r1_abc).splitlines() == r1_abc_repr + + # Make sure setting on a tuple or slice updates parent table and row + r1["c"] = 1000 + r1["a", "b"] = 1000 * u.cm, 100.0 + assert r1["a"] == 10 * u.m + assert r1["b"] == 100 + assert t["a"][1] == 10 * u.m + assert t["b"][1] == 100.0 + assert t["c"][1] == 1000 + + # Same but using a list of column names instead of tuple + r1[["a", "b"]] = 2000 * u.cm, 200.0 + assert r1["a"] == 20 * u.m + assert r1["b"] == 200 + assert t["a"][1] == 20 * u.m + assert t["b"][1] == 200.0 + + # Set column slice of column slice + r1_abc["a", "c"] = -1 * u.m, -10 + assert t["a"][1] == -1 * u.m + assert t["b"][1] == 200.0 + assert t["c"][1] == -10.0 + + # Bad column name + with pytest.raises(KeyError) as err: + t[1]["a", "not_there"] + assert "'not_there'" in str(err.value) + + # Too many values + with pytest.raises(ValueError) as err: + t[1]["a", "b"] = 1 * u.m, 2, 3 + assert "right hand side must be a sequence" in str(err.value) + + # Something without a length + with pytest.raises(ValueError) as err: + t[1]["a", "b"] = 1 + assert "right hand side must be a sequence" in str(err.value) + + +def test_row_tuple_column_slice_transaction(): + """ + Test that setting a row that fails part way through does not + change the table at all. + """ + t = table.QTable( + [ + [10.0, 20.0, 30.0], + [1, 2, 3] * u.m, + ], + names=["a", "b"], + ) + tc = t.copy() + + # First one succeeds but second fails. + with pytest.raises(ValueError) as err: + t[1]["a", "b"] = (-1, -1 * u.s) # Bad unit + assert "'s' (time) and 'm' (length) are not convertible" in str(err.value) + assert t[1] == tc[1] + + +def test_uint_indexing(): + """ + Test that accessing a row with an unsigned integer + works as with a signed integer. Similarly tests + that printing such a row works. + + This is non-trivial: adding a signed and unsigned + 64 bit integer in numpy results in a float, which is an + invalid slice index. + + Regression test for gh-7464. + """ + t = table.Table([[1.0, 2.0, 3.0]], names="a") + assert t["a"][1] == 2.0 + assert t["a"][np.int64(1)] == 2.0 + assert t["a"][np.uint64(1)] == 2.0 + assert t[np.uint64(1)]["a"] == 2.0 + + trepr = [ + "", + " a ", + "float64", + "-------", + " 2.0", + ] + + assert repr(t[1]).splitlines() == trepr + assert repr(t[np.int64(1)]).splitlines() == trepr + assert repr(t[np.uint64(1)]).splitlines() == trepr + + +def test_row_get(): + row = table.Table({"a": [2, 4], "b": [3, 9]})[0] + assert row.get("a") == 2 + assert row.get("x") is None + assert row.get("b", -1) == 3 + assert row.get("y", -1) == -1 + + +def test_table_row_slicing(): + # see https://github.com/astropy/astropy/issues/14007 + t = table.Table({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9]}) + first_row = t[0] + assert_array_equal(first_row[1:], [4, 7]) diff --git a/astropy/table/tests/test_showtable.py b/astropy/table/tests/test_showtable.py new file mode 100644 index 000000000000..622892ffeb4d --- /dev/null +++ b/astropy/table/tests/test_showtable.py @@ -0,0 +1,216 @@ +import os +import re + +import numpy as np +import pytest + +from astropy.table.scripts import showtable +from astropy.units import UnitsWarning + +ROOT = os.path.abspath(os.path.dirname(__file__)) +ASCII_ROOT = os.path.join(ROOT, "..", "..", "io", "ascii", "tests") +FITS_ROOT = os.path.join(ROOT, "..", "..", "io", "fits", "tests") +VOTABLE_ROOT = os.path.join(ROOT, "..", "..", "io", "votable", "tests") + + +def test_missing_file(capsys): + showtable.main(["foobar.fits"]) + out, err = capsys.readouterr() + assert err.startswith("ERROR: [Errno 2] No such file or directory: 'foobar.fits'") + + +def test_info(capsys): + showtable.main([os.path.join(FITS_ROOT, "data/table.fits"), "--info"]) + out, err = capsys.readouterr() + assert out.splitlines() == [ + "", + " name dtype ", + "------ -------", + "target bytes20", + " V_mag float32", + ] + + +def test_stats(capsys): + showtable.main([os.path.join(FITS_ROOT, "data/table.fits"), "--stats"]) + out, err = capsys.readouterr() + expected = [ + "
", + " name mean std min max ", + "------ ------- ------- ---- ----", + "target -- -- -- --", + " V_mag 12.866[0-9]? 1.72111 11.1 15.2", + ] + + out = out.splitlines() + assert out[:4] == expected[:4] + # Here we use re.match as in some cases one of the values above is + # platform-dependent. + assert re.match(expected[4], out[4]) is not None + + +def test_fits(capsys): + showtable.main([os.path.join(FITS_ROOT, "data/table.fits")]) + out, err = capsys.readouterr() + assert out.splitlines() == [ + " target V_mag", + "------- -----", + "NGC1001 11.1", + "NGC1002 12.3", + "NGC1003 15.2", + ] + + +def test_fits_hdu(capsys): + with pytest.warns(UnitsWarning): + showtable.main( + [ + os.path.join(FITS_ROOT, "data/zerowidth.fits"), + "--hdu", + "AIPS OF", + ] + ) + + out, err = capsys.readouterr() + assert out.startswith( + " TIME SOURCE ID ANTENNA NO. SUBARRAY FREQ ID ANT FLAG STATUS 1\n" + " DAYS \n" + "---------- --------- ----------- -------- ------- -------- --------\n" + "0.14438657 1 10 1 1 4 4\n" + ) + + +def test_csv(capsys): + showtable.main([os.path.join(ASCII_ROOT, "data/simple_csv.csv")]) + out, err = capsys.readouterr() + assert out.splitlines() == [ + " a b c ", + "--- --- ---", + " 1 2 3", + " 4 5 6", + ] + + +def test_ascii_format(capsys): + showtable.main( + [ + os.path.join(ASCII_ROOT, "data/commented_header.dat"), + "--format", + "ascii.commented_header", + ] + ) + out, err = capsys.readouterr() + assert out.splitlines() == [ + " a b c ", + "--- --- ---", + " 1 2 3", + " 4 5 6", + ] + + +def test_ascii_delimiter(capsys): + showtable.main( + [ + os.path.join(ASCII_ROOT, "data/simple2.txt"), + "--format", + "ascii", + "--delimiter", + "|", + ] + ) + out, err = capsys.readouterr() + assert out.splitlines() == [ + "obsid redshift X Y object rad ", + "----- -------- ---- ---- ----------- ----", + " 3102 0.32 4167 4085 Q1250+568-A 9.0", + " 3102 0.32 4706 3916 Q1250+568-B 14.0", + " 877 0.22 4378 3892 'Source 82' 12.5", + ] + + +def test_votable(capsys): + with np.errstate(over="ignore"): + # https://github.com/astropy/astropy/issues/13341 + showtable.main( + [ + os.path.join(VOTABLE_ROOT, "data/regression.xml"), + "--table-id", + "main_table", + "--max-width", + "50", + ] + ) + out, err = capsys.readouterr() + assert out.splitlines() == [ + " string_test string_test_2 ... bitarray2 ", + "----------------- ------------- ... -------------", + " String & test Fixed stri ... True .. False", + "String & test 0123456789 ... -- .. --", + " XXXX XXXX ... -- .. --", + " ... -- .. --", + " ... -- .. --", + ] + + +def test_max_lines(capsys): + showtable.main( + [ + os.path.join(ASCII_ROOT, "data/cds2.dat"), + "--format", + "ascii.cds", + "--max-lines", + "7", + "--max-width", + "30", + ] + ) + out, err = capsys.readouterr() + assert out.splitlines() == [ + " SST ... Note", + " ... ", + "--------------- ... ----", + "041314.1+281910 ... --", + " ... ... ...", + "044427.1+251216 ... --", + "044642.6+245903 ... --", + "Length = 215 rows", + ] + + +def test_show_dtype(capsys): + showtable.main([os.path.join(FITS_ROOT, "data/table.fits"), "--show-dtype"]) + out, err = capsys.readouterr() + assert out.splitlines() == [ + " target V_mag ", + "bytes20 float32", + "------- -------", + "NGC1001 11.1", + "NGC1002 12.3", + "NGC1003 15.2", + ] + + +def test_hide_unit(capsys): + showtable.main([os.path.join(ASCII_ROOT, "data/cds.dat"), "--format", "ascii.cds"]) + out, err = capsys.readouterr() + assert out.splitlines() == [ + "Index RAh RAm RAs DE- DEd DEm DEs Match Class AK Fit ", + " h min s deg arcmin arcsec mag GsolMass", + "----- --- --- ----- --- --- ------ ------ ----- ----- --- --------", + " 1 3 28 39.09 + 31 6 1.9 -- I* -- 1.35", + ] + + showtable.main( + [ + os.path.join(ASCII_ROOT, "data/cds.dat"), + "--format", + "ascii.cds", + "--hide-unit", + ] + ) + out, err = capsys.readouterr() + assert out.splitlines() == [ + "Index RAh RAm RAs DE- DEd DEm DEs Match Class AK Fit ", + "----- --- --- ----- --- --- --- --- ----- ----- --- ----", + " 1 3 28 39.09 + 31 6 1.9 -- I* -- 1.35", + ] diff --git a/astropy/table/tests/test_subclass.py b/astropy/table/tests/test_subclass.py new file mode 100644 index 000000000000..fd2bfd6fb142 --- /dev/null +++ b/astropy/table/tests/test_subclass.py @@ -0,0 +1,97 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + + +from astropy import table +from astropy.table import pprint + + +class MyRow(table.Row): + def __str__(self): + return str(self.as_void()) + + +class MyColumn(table.Column): + pass + + +class MyMaskedColumn(table.MaskedColumn): + pass + + +class MyTableColumns(table.TableColumns): + pass + + +class MyTableFormatter(pprint.TableFormatter): + pass + + +class MyTable(table.Table): + Row = MyRow + Column = MyColumn + MaskedColumn = MyMaskedColumn + TableColumns = MyTableColumns + TableFormatter = MyTableFormatter + + +def test_simple_subclass(): + t = MyTable([[1, 2], [3, 4]]) + row = t[0] + assert isinstance(row, MyRow) + assert isinstance(t["col0"], MyColumn) + assert isinstance(t.columns, MyTableColumns) + assert isinstance(t.formatter, MyTableFormatter) + + t2 = MyTable(t) + row = t2[0] + assert isinstance(row, MyRow) + assert str(row) == "(1, 3)" + + t3 = table.Table(t) + row = t3[0] + assert not isinstance(row, MyRow) + assert str(row) != "(1, 3)" + + t = MyTable([[1, 2], [3, 4]], masked=True) + row = t[0] + assert isinstance(row, MyRow) + assert str(row) == "(1, 3)" + assert isinstance(t["col0"], MyMaskedColumn) + assert isinstance(t.formatter, MyTableFormatter) + + +class ParamsRow(table.Row): + """ + Row class that allows access to an arbitrary dict of parameters + stored as a dict object in the ``params`` column. + """ + + def __getitem__(self, item): + if item not in self.colnames: + return super().__getitem__("params")[item] + else: + return super().__getitem__(item) + + def keys(self): + out = [name for name in self.colnames if name != "params"] + params = [key.lower() for key in sorted(self["params"])] + return out + params + + def values(self): + return [self[key] for key in self.keys()] + + +class ParamsTable(table.Table): + Row = ParamsRow + + +def test_params_table(): + t = ParamsTable(names=["a", "b", "params"], dtype=["i", "f", "O"]) + t.add_row((1, 2.0, {"x": 1.5, "y": 2.5})) + t.add_row((2, 3.0, {"z": "hello", "id": 123123})) + assert t["params"][0] == {"x": 1.5, "y": 2.5} + assert t[0]["params"] == {"x": 1.5, "y": 2.5} + assert t[0]["y"] == 2.5 + assert t[1]["id"] == 123123 + assert list(t[1].keys()) == ["a", "b", "id", "z"] + assert list(t[1].values()) == [2, 3.0, 123123, "hello"] diff --git a/astropy/table/tests/test_table.py b/astropy/table/tests/test_table.py index f934dfc96e40..ed74eda09a0e 100644 --- a/astropy/table/tests/test_table.py +++ b/astropy/table/tests/test_table.py @@ -1,530 +1,3363 @@ -import pytest -import numpy as np - -from .. import Table, Column - - -class TestEmptyData(): - - def test_1(self): - t = Table() - t.add_column(Column('a', dtype=int, length=100)) - assert len(t['a']) == 100 - - def test_2(self): - t = Table() - t.add_column(Column('a', dtype=int, shape=(3, ), length=100)) - assert len(t['a']) == 100 - - def test_3(self): - t = Table() # length is not given - t.add_column(Column('a', dtype=int)) - assert len(t['a']) == 0 - - def test_4(self): - t = Table() # length is not given - t.add_column(Column('a', dtype=int, shape=(3, 4))) - assert len(t['a']) == 0 - - def test_5(self): - t = Table() - t.add_column(Column('a')) # dtype is not specified - assert len(t['a']) == 0 - - -class TestNewFromColumns(): - - def test_simple(self): - cols = [Column('a', [1, 2, 3]), - Column('b', [4, 5, 6], dtype=np.float32)] - t = Table(cols) - assert np.all(t['a'].data == np.array([1, 2, 3])) - assert np.all(t['b'].data == np.array([4, 5, 6], dtype=np.float32)) - assert type(t['b'][1]) == np.float32 - - def test_from_np_array(self): - cols = [Column('a', np.array([1, 2, 3], dtype=np.int64), - dtype=np.float64), - Column('b', np.array([4, 5, 6], dtype=np.float32))] - t = Table(cols) - assert np.all(t['a'] == np.array([1, 2, 3], dtype=np.float64)) - assert np.all(t['b'] == np.array([4, 5, 6], dtype=np.float32)) - assert type(t['a'][1]) == np.float64 - assert type(t['b'][1]) == np.float32 +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +import copy +import gc +import os +import pathlib +import pickle +import sys +from collections import OrderedDict +from contextlib import nullcontext +from inspect import currentframe, getframeinfo +from io import StringIO - def test_size_mismatch(self): - cols = [Column('a', [1, 2, 3]), - Column('b', [4, 5, 6, 7])] +import numpy as np +import pytest +from numpy.testing import assert_array_equal + +from astropy import table +from astropy import units as u +from astropy.coordinates import SkyCoord +from astropy.io import fits +from astropy.table import ( + Column, + MaskedColumn, + QTable, + Table, + TableAttribute, + TableReplaceWarning, +) +from astropy.tests.helper import assert_follows_unicode_guidelines +from astropy.time import Time +from astropy.utils.data import get_pkg_data_filename +from astropy.utils.exceptions import AstropyDeprecationWarning, AstropyUserWarning +from astropy.utils.metadata.tests.test_metadata import MetaBaseTest + +from .conftest import MaskedTable + + +@pytest.fixture +def home_is_tmpdir(monkeypatch, tmp_path): + """ + Pytest fixture to run a test case with tilde-prefixed paths. + + In the tilde-path case, environment variables are temporarily + modified so that '~' resolves to the temp directory. + """ + # For Unix + monkeypatch.setenv("HOME", str(tmp_path)) + # For Windows + monkeypatch.setenv("USERPROFILE", str(tmp_path)) + + +class SetupData: + def _setup(self, table_types): + self._table_type = table_types.Table + self._column_type = table_types.Column + + @property + def a(self): + if self._column_type is not None: + if not hasattr(self, "_a"): + self._a = self._column_type( + [1, 2, 3], name="a", format="%d", meta={"aa": [0, 1, 2, 3, 4]} + ) + return self._a + + @property + def b(self): + if self._column_type is not None: + if not hasattr(self, "_b"): + self._b = self._column_type( + [4, 5, 6], name="b", format="%d", meta={"aa": 1} + ) + return self._b + + @property + def c(self): + if self._column_type is not None: + if not hasattr(self, "_c"): + self._c = self._column_type([7, 8, 9], "c") + return self._c + + @property + def d(self): + if self._column_type is not None: + if not hasattr(self, "_d"): + self._d = self._column_type([7, 8, 7], "d") + return self._d + + @property + def obj(self): + if self._column_type is not None: + if not hasattr(self, "_obj"): + self._obj = self._column_type([1, "string", 3], "obj", dtype="O") + return self._obj + + @property + def t(self): + if self._table_type is not None: + if not hasattr(self, "_t"): + self._t = self._table_type([self.a, self.b]) + return self._t + + +@pytest.mark.usefixtures("table_types") +class TestSetTableColumn(SetupData): + def test_set_row(self, table_types): + """Set a row from a tuple of values""" + self._setup(table_types) + t = table_types.Table([self.a, self.b]) + t[1] = (20, 21) + assert t["a"][0] == 1 + assert t["a"][1] == 20 + assert t["a"][2] == 3 + assert t["b"][0] == 4 + assert t["b"][1] == 21 + assert t["b"][2] == 6 + + def test_set_row_existing(self, table_types): + """Set a row from another existing row""" + self._setup(table_types) + t = table_types.Table([self.a, self.b]) + t[0] = t[1] + assert t[0][0] == 2 + assert t[0][1] == 5 + + def test_set_row_fail_1(self, table_types): + """Set a row from an incorrectly-sized or typed set of values""" + self._setup(table_types) + t = table_types.Table([self.a, self.b]) with pytest.raises(ValueError): - Table(cols) - - -class TestReverse(): + t[1] = (20, 21, 22) + with pytest.raises(ValueError): + t[1] = 0 - def test_reverse(self): - t = Table([[1, 2, 3], - ['a', 'b', 'cc']]) + def test_set_row_fail_2(self, table_types): + """Set a row from an incorrectly-typed tuple of values""" + self._setup(table_types) + t = table_types.Table([self.a, self.b]) + with pytest.raises(ValueError): + t[1] = ("abc", "def") + + def test_set_new_col_new_table(self, table_types): + """Create a new column in empty table using the item access syntax""" + self._setup(table_types) + t = table_types.Table() + t["aa"] = self.a + # Test that the new column name is 'aa' and that the values match + assert np.all(t["aa"] == self.a) + assert t.colnames == ["aa"] + + def test_set_new_col_new_table_quantity(self, table_types): + """Create a new column (from a quantity) in empty table using the item access syntax""" + self._setup(table_types) + t = table_types.Table() + + t["aa"] = np.array([1, 2, 3]) * u.m + assert np.all(t["aa"] == np.array([1, 2, 3])) + assert t["aa"].unit == u.m + + t["bb"] = 3 * u.m + assert np.all(t["bb"] == 3) + assert t["bb"].unit == u.m + + def test_set_new_col_existing_table(self, table_types): + """Create a new column in an existing table using the item access syntax""" + self._setup(table_types) + t = table_types.Table([self.a]) + + # Add a column + t["bb"] = self.b + assert np.all(t["bb"] == self.b) + assert t.colnames == ["a", "bb"] + assert t["bb"].meta == self.b.meta + assert t["bb"].format == self.b.format + + # Add another column + t["c"] = t["a"] + assert np.all(t["c"] == t["a"]) + assert t.colnames == ["a", "bb", "c"] + assert t["c"].meta == t["a"].meta + assert t["c"].format == t["a"].format + + # Add a multi-dimensional column + t["d"] = table_types.Column(np.arange(12).reshape(3, 2, 2)) + assert t["d"].shape == (3, 2, 2) + assert t["d"][0, 0, 1] == 1 + + # Add column from a list + t["e"] = ["hello", "the", "world"] + assert np.all(t["e"] == np.array(["hello", "the", "world"])) + + # Make sure setting existing column still works + t["e"] = ["world", "hello", "the"] + assert np.all(t["e"] == np.array(["world", "hello", "the"])) + + # Add a column via broadcasting + t["f"] = 10 + assert np.all(t["f"] == 10) + + # Add a column from a Quantity + t["g"] = np.array([1, 2, 3]) * u.m + assert np.all(t["g"].data == np.array([1, 2, 3])) + assert t["g"].unit == u.m + + # Add a column from a (scalar) Quantity + t["g"] = 3 * u.m + assert np.all(t["g"].data == 3) + assert t["g"].unit == u.m + + def test_set_new_unmasked_col_existing_table(self, table_types): + """Create a new column in an existing table using the item access syntax""" + self._setup(table_types) + t = table_types.Table([self.a]) # masked or unmasked + b = table.Column(name="b", data=[1, 2, 3]) # unmasked + t["b"] = b + assert np.all(t["b"] == b) + + def test_set_new_masked_col_existing_table(self, table_types): + """Create a new column in an existing table using the item access syntax""" + self._setup(table_types) + t = table_types.Table([self.a]) # masked or unmasked + b = table.MaskedColumn(name="b", data=[1, 2, 3]) # masked + t["b"] = b + assert np.all(t["b"] == b) + + def test_set_new_col_existing_table_fail(self, table_types): + """Generate failure when creating a new column using the item access syntax""" + self._setup(table_types) + t = table_types.Table([self.a]) + # Wrong size + with pytest.raises(ValueError): + t["b"] = [1, 2] + + +@pytest.mark.usefixtures("table_types") +class TestEmptyData: + def test_1(self, table_types): + t = table_types.Table() + t.add_column(table_types.Column(name="a", dtype=int, length=100)) + assert len(t["a"]) == 100 + + def test_2(self, table_types): + t = table_types.Table() + t.add_column(table_types.Column(name="a", dtype=int, shape=(3,), length=100)) + assert len(t["a"]) == 100 + + def test_3(self, table_types): + t = table_types.Table() # length is not given + t.add_column(table_types.Column(name="a", dtype=int)) + assert len(t["a"]) == 0 + + def test_4(self, table_types): + t = table_types.Table() # length is not given + t.add_column(table_types.Column(name="a", dtype=int, shape=(3, 4))) + assert len(t["a"]) == 0 + + def test_5(self, table_types): + t = table_types.Table() + t.add_column(table_types.Column(name="a")) # dtype is not specified + assert len(t["a"]) == 0 + + def test_scalar(self, table_types): + """Test related to #3811 and #17078: we used to have setting + empty tables succeed, but raise on access. Then, we ensured they + raised on setting. But now we let setting and accessing succeed: + the table stays empty.""" + t = table_types.Table() + t.add_column(0, name="a") + assert len(t) == 0 + assert isinstance(t["a"], Column) + assert len(t["a"]) == 0 + assert t["a"].dtype == int + t["b"] = SkyCoord(1.0, 2.0, unit="hourangle,deg") + assert isinstance(t["b"], SkyCoord) + assert len(t["b"]) == 0 + assert t["b"].data.lon.unit == u.hourangle + # For this case, broadcasting still works. + t["c"] = [1.0] + # But not here. + with pytest.raises(ValueError, match="data column length"): + t["d"] = [1.0, 2.0] + + def test_scalar_double_assignment(self, table_types): + # Following example given by @taldcroft in + # https://github.com/astropy/astropy/pull/17102#issuecomment-2386767337 + t = table_types.Table() + t["a"] = 1.5 + assert len(t) == 0 + assert isinstance(t["a"], Column) + assert t["a"].shape == (0,) + t["a"] = 1.5 + assert len(t) == 0 + assert isinstance(t["a"], Column) + assert t["a"].shape == (0,) + + def test_add_via_setitem_and_slice(self, table_types): + """Test related to #3023 where a MaskedColumn is created with name=None + and then gets changed to name='a'. After PR #2790 this test fails + without the #3023 fix.""" + t = table_types.Table() + t["a"] = table_types.Column([1, 2, 3]) + t2 = t[:] + assert t2.colnames == t.colnames + + +@pytest.mark.usefixtures("table_types") +class TestNewFromColumns: + def test_simple(self, table_types): + cols = [ + table_types.Column(name="a", data=[1, 2, 3]), + table_types.Column(name="b", data=[4, 5, 6], dtype=np.float32), + ] + t = table_types.Table(cols) + assert np.all(t["a"].data == np.array([1, 2, 3])) + assert np.all(t["b"].data == np.array([4, 5, 6], dtype=np.float32)) + assert type(t["b"][1]) is np.float32 + + def test_from_np_array(self, table_types): + cols = [ + table_types.Column( + name="a", data=np.array([1, 2, 3], dtype=np.int64), dtype=np.float64 + ), + table_types.Column(name="b", data=np.array([4, 5, 6], dtype=np.float32)), + ] + t = table_types.Table(cols) + assert np.all(t["a"] == np.array([1, 2, 3], dtype=np.float64)) + assert np.all(t["b"] == np.array([4, 5, 6], dtype=np.float32)) + assert type(t["a"][1]) is np.float64 + assert type(t["b"][1]) is np.float32 + + def test_size_mismatch(self, table_types): + cols = [ + table_types.Column(name="a", data=[1, 2, 3]), + table_types.Column(name="b", data=[4, 5, 6, 7]), + ] + with pytest.raises(ValueError): + table_types.Table(cols) + + def test_name_none(self, table_types): + """Column with name=None can init a table whether or not names are supplied""" + c = table_types.Column(data=[1, 2], name="c") + d = table_types.Column(data=[3, 4]) + t = table_types.Table([c, d], names=(None, "d")) + assert t.colnames == ["c", "d"] + t = table_types.Table([c, d]) + assert t.colnames == ["c", "col1"] + + +@pytest.mark.usefixtures("table_types") +class TestReverse: + def test_reverse(self, table_types): + t = table_types.Table( + [ + [1, 2, 3], + ["a", "b", "cc"], + ] + ) t.reverse() - assert np.all(t['col0'] == np.array([3, 2, 1])) - assert np.all(t['col1'] == np.array(['cc', 'b', 'a'])) - - t2 = Table(t, copy=False) - assert np.all(t2['col0'] == np.array([3, 2, 1])) - assert np.all(t2['col1'] == np.array(['cc', 'b', 'a'])) + assert np.all(t["col0"] == np.array([3, 2, 1])) + assert np.all(t["col1"] == np.array(["cc", "b", "a"])) - t2 = Table(t, copy=True) - assert np.all(t2['col0'] == np.array([3, 2, 1])) - assert np.all(t2['col1'] == np.array(['cc', 'b', 'a'])) + t2 = table_types.Table(t, copy=False) + assert np.all(t2["col0"] == np.array([3, 2, 1])) + assert np.all(t2["col1"] == np.array(["cc", "b", "a"])) - t2.sort('col0') - assert np.all(t2['col0'] == np.array([1, 2, 3])) - assert np.all(t2['col1'] == np.array(['a', 'b', 'cc'])) + t2 = table_types.Table(t, copy=True) + assert np.all(t2["col0"] == np.array([3, 2, 1])) + assert np.all(t2["col1"] == np.array(["cc", "b", "a"])) + t2.sort("col0") + assert np.all(t2["col0"] == np.array([1, 2, 3])) + assert np.all(t2["col1"] == np.array(["a", "b", "cc"])) -class TestColumnAccess(): + def test_reverse_big(self, table_types): + x = np.arange(10000) + y = x + 1 + t = table_types.Table([x, y], names=("x", "y")) + t.reverse() + assert np.all(t["x"] == x[::-1]) + assert np.all(t["y"] == y[::-1]) - def test_1(self): - t = Table() + def test_reverse_mixin(self): + """Test reverse for a mixin with no item assignment, fix for #9836""" + sc = SkyCoord([1, 2], [3, 4], unit="deg") + t = Table([[2, 1], sc], names=["a", "sc"]) + t.reverse() + assert np.all(t["a"] == [1, 2]) + assert np.allclose(t["sc"].ra.to_value("deg"), [2, 1]) + + +@pytest.mark.usefixtures("table_types") +class TestRound: + def test_round_int(self, table_types): + t = table_types.Table( + [ + ["a", "b", "c"], + [1.11, 2.3, 3.0], + [1.123456, 2.9876, 3.901], + ] + ) + t.round() + assert np.all(t["col0"] == ["a", "b", "c"]) + assert np.all(t["col1"] == [1.0, 2.0, 3.0]) + assert np.all(t["col2"] == [1.0, 3.0, 4.0]) + + def test_round_dict(self, table_types): + t = table_types.Table( + [ + ["a", "b", "c"], + [1.5, 2.5, 3.0111], + [1.123456, 2.9876, 3.901], + ] + ) + + t.round({"col1": 0, "col2": 3}) + assert np.all(t["col0"] == ["a", "b", "c"]) + assert np.all(t["col1"] == [2.0, 2.0, 3.0]) + assert np.all(t["col2"] == [1.123, 2.988, 3.901]) + + def test_round_invalid(self, table_types): + t = table_types.Table([[1, 2, 3]]) + with pytest.raises( + ValueError, match="'decimals' argument must be an int or a dict" + ): + t.round(0.5) + + def test_round_kind(self, table_types): + for typecode in "bBhHiIlLqQpPefdgFDG": # AllInteger, AllFloat + arr = np.array([4, 16], dtype=typecode) + t = Table([arr]) + col0 = t["col0"] + t.round(decimals=-1) # Round to nearest 10 + assert np.all(t["col0"] == [0, 20]) + assert t["col0"] is col0 + + +@pytest.mark.usefixtures("table_types") +class TestColumnAccess: + def test_1(self, table_types): + t = table_types.Table() with pytest.raises(KeyError): - t['a'] + t["a"] - def test_2(self): - t = Table() - t.add_column(Column('a', [1, 2, 3])) - assert np.all(t['a'] == np.array([1, 2, 3])) + def test_2(self, table_types): + t = table_types.Table() + t.add_column(table_types.Column(name="a", data=[1, 2, 3])) + assert np.all(t["a"] == np.array([1, 2, 3])) with pytest.raises(KeyError): - t['b'] # column does not exist + t["b"] # column does not exist + def test_itercols(self, table_types): + names = ["a", "b", "c"] + t = table_types.Table([[1], [2], [3]], names=names) + for name, col in zip(names, t.itercols()): + assert name == col.name + assert isinstance(col, table_types.Column) -class TestAddLength(): - def setup_method(self, method): - self.a = Column('a', [1, 2, 3]) - self.b = Column('b', [4, 5, 6]) - def test_right_length(self): - t = Table([self.a]) +@pytest.mark.usefixtures("table_types") +class TestAddLength(SetupData): + def test_right_length(self, table_types): + self._setup(table_types) + t = table_types.Table([self.a]) t.add_column(self.b) - def test_too_long(self): - t = Table([self.a]) + def test_too_long(self, table_types): + self._setup(table_types) + t = table_types.Table([self.a]) with pytest.raises(ValueError): - t.add_column(Column('b', [4, 5, 6, 7])) # data too long + t.add_column( + table_types.Column(name="b", data=[4, 5, 6, 7]) + ) # data too long - def test_too_short(self): - t = Table([self.a]) + def test_too_short(self, table_types): + self._setup(table_types) + t = table_types.Table([self.a]) with pytest.raises(ValueError): - t.add_column(Column('b', [4, 5])) # data too short + t.add_column(table_types.Column(name="b", data=[4, 5])) # data too short -class TestAddPosition(): - def setup_method(self, method): - self.a = Column('a', [1, 2, 3]) - self.b = Column('b', [4, 5, 6]) - self.c = Column('c', [7, 8, 9]) - - def test_1(self): - t = Table() +@pytest.mark.usefixtures("table_types") +class TestAddPosition(SetupData): + def test_1(self, table_types): + self._setup(table_types) + t = table_types.Table() t.add_column(self.a, 0) - def test_2(self): - t = Table() + def test_2(self, table_types): + self._setup(table_types) + t = table_types.Table() t.add_column(self.a, 1) - def test_3(self): - t = Table() + def test_3(self, table_types): + self._setup(table_types) + t = table_types.Table() t.add_column(self.a, -1) - def test_5(self): - t = Table() + def test_5(self, table_types): + self._setup(table_types) + t = table_types.Table() with pytest.raises(ValueError): - t.index_column('b') + t.index_column("b") - def test_6(self): - t = Table() + def test_6(self, table_types): + self._setup(table_types) + t = table_types.Table() t.add_column(self.a) t.add_column(self.b) - assert t.columns.keys() == ['a', 'b'] - - def test_7(self): - t = Table([self.a]) - t.add_column(self.b, t.index_column('a')) - assert t.columns.keys() == ['b', 'a'] - - def test_8(self): - t = Table([self.a]) - t.add_column(self.b, t.index_column('a') + 1) - assert t.columns.keys() == ['a', 'b'] - - def test_9(self): - t = Table() + assert t.colnames == ["a", "b"] + + def test_7(self, table_types): + self._setup(table_types) + t = table_types.Table([self.a]) + t.add_column(self.b, t.index_column("a")) + assert t.colnames == ["b", "a"] + + def test_8(self, table_types): + self._setup(table_types) + t = table_types.Table([self.a]) + t.add_column(self.b, t.index_column("a") + 1) + assert t.colnames == ["a", "b"] + + def test_9(self, table_types): + self._setup(table_types) + t = table_types.Table() t.add_column(self.a) - t.add_column(self.b, t.index_column('a') + 1) - t.add_column(self.c, t.index_column('b')) - assert t.columns.keys() == ['a', 'c', 'b'] + t.add_column(self.b, t.index_column("a") + 1) + t.add_column(self.c, t.index_column("b")) + assert t.colnames == ["a", "c", "b"] - def test_10(self): - t = Table() + def test_10(self, table_types): + self._setup(table_types) + t = table_types.Table() t.add_column(self.a) - ia = t.index_column('a') + ia = t.index_column("a") t.add_column(self.b, ia + 1) t.add_column(self.c, ia) - assert t.columns.keys() == ['c', 'a', 'b'] - - -class TestInitFromTable(): - - def setup_method(self, method): - self.a = Column('a', [1, 2, 3], meta={'aa': np.arange(5)}) - self.b = Column('b', [4, 5, 6]) - self.c = Column('c', [7, 8, 9]) - self.t = Table([self.a, self.b]) - - def test_from_table_cols(self): + assert t.colnames == ["c", "a", "b"] + + +@pytest.mark.usefixtures("table_types") +class TestAddName(SetupData): + def test_override_name(self, table_types): + self._setup(table_types) + t = table_types.Table() + + # Check that we can override the name of the input column in the Table + t.add_column(self.a, name="b") + t.add_column(self.b, name="a") + assert t.colnames == ["b", "a"] + # Check that we did not change the name of the input column + assert self.a.info.name == "a" + assert self.b.info.name == "b" + + # Now test with an input column from another table + t2 = table_types.Table() + t2.add_column(t["a"], name="c") + assert t2.colnames == ["c"] + # Check that we did not change the name of the input column + assert t.colnames == ["b", "a"] + + # Check that we can give a name if none was present + col = table_types.Column([1, 2, 3]) + t.add_column(col, name="c") + assert t.colnames == ["b", "a", "c"] + + def test_default_name(self, table_types): + t = table_types.Table() + col = table_types.Column([1, 2, 3]) + t.add_column(col) + assert t.colnames == ["col0"] + + def test_setting_column_name_to_with_invalid_type(self, table_types): + t = table_types.Table() + t["a"] = [1, 2] + with pytest.raises( + TypeError, match=r"Expected a str value, got None with type NoneType" + ): + t["a"].name = None + assert t["a"].name == "a" + + +@pytest.mark.usefixtures("table_types") +class TestInitFromTable(SetupData): + def test_from_table_cols(self, table_types): """Ensure that using cols from an existing table gives a clean copy. """ + self._setup(table_types) t = self.t cols = t.columns # Construct Table with cols via Table._new_from_cols - t2a = Table([cols['a'], cols['b'], self.c]) + t2a = table_types.Table([cols["a"], cols["b"], self.c]) # Construct with add_column - t2b = Table() - t2b.add_column(cols['a']) - t2b.add_column(cols['b']) + t2b = table_types.Table() + t2b.add_column(cols["a"]) + t2b.add_column(cols["b"]) t2b.add_column(self.c) - t['a'][1] = 20 - t['b'][1] = 21 + t["a"][1] = 20 + t["b"][1] = 21 for t2 in [t2a, t2b]: - t2['a'][2] = 10 - t2['b'][2] = 11 - t2['c'][2] = 12 - t2.columns['a'].meta['aa'][3] = 10 - assert np.all(t['a'] == np.array([1, 20, 3])) - assert np.all(t['b'] == np.array([4, 21, 6])) - assert np.all(t2['a'] == np.array([1, 2, 10])) - assert np.all(t2['b'] == np.array([4, 5, 11])) - assert np.all(t2['c'] == np.array([7, 8, 12])) - assert t2['a'].name == 'a' - assert t2.columns['a'].meta['aa'][3] == 10 - assert t.columns['a'].meta['aa'][3] == 3 - - -class TestAddColumns(): - - def setup_method(self, method): - self.a = Column('a', [1, 2, 3]) - self.b = Column('b', [4, 5, 6]) - self.c = Column('c', [7, 8, 9]) - self.d = Column('d', [7, 8, 7]) - - def test_add_columns1(self): - t = Table() + t2["a"][2] = 10 + t2["b"][2] = 11 + t2["c"][2] = 12 + t2.columns["a"].meta["aa"][3] = 10 + assert np.all(t["a"] == np.array([1, 20, 3])) + assert np.all(t["b"] == np.array([4, 21, 6])) + assert np.all(t2["a"] == np.array([1, 2, 10])) + assert np.all(t2["b"] == np.array([4, 5, 11])) + assert np.all(t2["c"] == np.array([7, 8, 12])) + assert t2["a"].name == "a" + assert t2.columns["a"].meta["aa"][3] == 10 + assert t.columns["a"].meta["aa"][3] == 3 + + +@pytest.mark.usefixtures("table_types") +class TestAddColumns(SetupData): + def test_add_columns1(self, table_types): + self._setup(table_types) + t = table_types.Table() t.add_columns([self.a, self.b, self.c]) - assert t.colnames == ['a', 'b', 'c'] + assert t.colnames == ["a", "b", "c"] - def test_add_columns2(self): - t = Table([self.a, self.b]) + def test_add_columns2(self, table_types): + self._setup(table_types) + t = table_types.Table([self.a, self.b]) t.add_columns([self.c, self.d]) - assert t.colnames == ['a', 'b', 'c', 'd'] - assert np.all(t['c'] == np.array([7, 8, 9])) + assert t.colnames == ["a", "b", "c", "d"] + assert np.all(t["c"] == np.array([7, 8, 9])) - def test_add_columns3(self): - t = Table([self.a, self.b]) + def test_add_columns3(self, table_types): + self._setup(table_types) + t = table_types.Table([self.a, self.b]) t.add_columns([self.c, self.d], indexes=[1, 0]) - assert t.colnames == ['d', 'a', 'c', 'b'] + assert t.colnames == ["d", "a", "c", "b"] - def test_add_columns4(self): - t = Table([self.a, self.b]) + def test_add_columns4(self, table_types): + self._setup(table_types) + t = table_types.Table([self.a, self.b]) t.add_columns([self.c, self.d], indexes=[0, 0]) - assert t.colnames == ['c', 'd', 'a', 'b'] + assert t.colnames == ["c", "d", "a", "b"] - def test_add_columns5(self): - t = Table([self.a, self.b]) + def test_add_columns5(self, table_types): + self._setup(table_types) + t = table_types.Table([self.a, self.b]) t.add_columns([self.c, self.d], indexes=[2, 2]) - assert t.colnames == ['a', 'b', 'c', 'd'] - - def test_add_duplicate_column(self): - t = Table() + assert t.colnames == ["a", "b", "c", "d"] + + def test_add_columns6(self, table_types): + """Check that we can override column names.""" + self._setup(table_types) + t = table_types.Table() + t.add_columns([self.a, self.b, self.c], names=["b", "c", "a"]) + assert t.colnames == ["b", "c", "a"] + + def test_add_columns7(self, table_types): + """Check that default names are used when appropriate.""" + t = table_types.Table() + col0 = table_types.Column([1, 2, 3]) + col1 = table_types.Column([4, 5, 3]) + t.add_columns([col0, col1]) + assert t.colnames == ["col0", "col1"] + + def test_add_duplicate_column(self, table_types): + self._setup(table_types) + t = table_types.Table() t.add_column(self.a) with pytest.raises(ValueError): - t.add_column(Column('a', [0, 1, 2])) + t.add_column(table_types.Column(name="a", data=[0, 1, 2])) + t.add_column( + table_types.Column(name="a", data=[0, 1, 2]), rename_duplicate=True + ) t.add_column(self.b) t.add_column(self.c) - assert t.colnames == ['a', 'b', 'c'] - - def test_add_duplicate_columns(self): - t = Table([self.a, self.b, self.c]) + assert t.colnames == ["a", "a_1", "b", "c"] + t.add_column( + table_types.Column(name="a", data=[0, 1, 2]), rename_duplicate=True + ) + assert t.colnames == ["a", "a_1", "b", "c", "a_2"] + + # test adding column from a separate Table + t1 = table_types.Table() + t1.add_column(self.a) with pytest.raises(ValueError): - t.add_columns([Column('a', [0, 1, 2]), Column('b', [0, 1, 2])]) - t.add_column(self.d) - assert t.colnames == ['a', 'b', 'c', 'd'] + t.add_column(t1["a"]) + t.add_column(t1["a"], rename_duplicate=True) + + t1["a"][0] = 100 # Change original column + assert t.colnames == ["a", "a_1", "b", "c", "a_2", "a_3"] + assert t1.colnames == ["a"] + # Check new column didn't change (since name conflict forced a copy) + assert t["a_3"][0] == self.a[0] -class TestAddRow(): + # Check that rename_duplicate=True is ok if there are no duplicates + t.add_column( + table_types.Column(name="q", data=[0, 1, 2]), rename_duplicate=True + ) + assert t.colnames == ["a", "a_1", "b", "c", "a_2", "a_3", "q"] - def setup_method(self, method): - self.a = Column('a', [1, 2, 3]) - self.b = Column('b', [4.0, 5.1, 6.2]) - self.c = Column('c', ['7', '8', '9']) - self.t = Table([self.a, self.b, self.c]) + def test_add_duplicate_columns(self, table_types): + self._setup(table_types) + t = table_types.Table([self.a, self.b, self.c]) + with pytest.raises(ValueError): + t.add_columns( + [ + table_types.Column(name="a", data=[0, 1, 2]), + table_types.Column(name="b", data=[0, 1, 2]), + ] + ) + t.add_columns( + [ + table_types.Column(name="a", data=[0, 1, 2]), + table_types.Column(name="b", data=[0, 1, 2]), + ], + rename_duplicate=True, + ) + t.add_column(self.d) + assert t.colnames == ["a", "b", "c", "a_1", "b_1", "d"] + + +@pytest.mark.usefixtures("table_types") +class TestAddRow(SetupData): + @property + def b(self): + if self._column_type is not None: + if not hasattr(self, "_b"): + self._b = self._column_type(name="b", data=[4.0, 5.1, 6.2]) + return self._b + + @property + def c(self): + if self._column_type is not None: + if not hasattr(self, "_c"): + self._c = self._column_type(name="c", data=["7", "8", "9"]) + return self._c + + @property + def d(self): + if self._column_type is not None: + if not hasattr(self, "_d"): + self._d = self._column_type(name="d", data=[[1, 2], [3, 4], [5, 6]]) + return self._d + + @property + def t(self): + if self._table_type is not None: + if not hasattr(self, "_t"): + self._t = self._table_type([self.a, self.b, self.c]) + return self._t + + def test_add_none_to_empty_table(self, table_types): + self._setup(table_types) + t = table_types.Table(names=("a", "b", "c"), dtype=("(2,)i", "S4", "O")) + t.add_row() + assert np.all(t["a"][0] == [0, 0]) + assert t["b"][0] == "" + assert t["c"][0] == 0 + t.add_row() + assert np.all(t["a"][1] == [0, 0]) + assert t["b"][1] == "" + assert t["c"][1] == 0 + + def test_add_stuff_to_empty_table(self, table_types): + self._setup(table_types) + t = table_types.Table(names=("a", "b", "obj"), dtype=("(2,)i", "S8", "O")) + t.add_row([[1, 2], "hello", "world"]) + assert np.all(t["a"][0] == [1, 2]) + assert t["b"][0] == "hello" + assert t["obj"][0] == "world" + # Make sure it is not repeating last row but instead + # adding zeros (as documented) + t.add_row() + assert np.all(t["a"][1] == [0, 0]) + assert t["b"][1] == "" + assert t["obj"][1] == 0 - def test_add_with_tuple(self): + def test_add_table_row(self, table_types): + self._setup(table_types) t = self.t - t.add_row((4, 7.2, '1')) + t["d"] = self.d + t2 = table_types.Table([self.a, self.b, self.c, self.d]) + t.add_row(t2[0]) assert len(t) == 4 - assert np.all(t['a'] == np.array([1, 2, 3, 4])) - assert np.allclose(t['b'], np.array([4.0, 5.1, 6.2, 7.2])) - assert np.all(t['c'] == np.array(['7', '8', '9', '1'])) - - def test_add_with_list(self): + assert np.all(t["a"] == np.array([1, 2, 3, 1])) + assert np.allclose(t["b"], np.array([4.0, 5.1, 6.2, 4.0])) + assert np.all(t["c"] == np.array(["7", "8", "9", "7"])) + assert np.all(t["d"] == np.array([[1, 2], [3, 4], [5, 6], [1, 2]])) + + def test_add_table_row_obj(self, table_types): + self._setup(table_types) + t = table_types.Table([self.a, self.b, self.obj]) + t.add_row([1, 4.0, [10]]) + assert len(t) == 4 + assert np.all(t["a"] == np.array([1, 2, 3, 1])) + assert np.allclose(t["b"], np.array([4.0, 5.1, 6.2, 4.0])) + assert np.all(t["obj"] == np.array([1, "string", 3, [10]], dtype="O")) + + def test_add_qtable_row_multidimensional(self): + q = [[1, 2], [3, 4]] * u.m + qt = table.QTable([q]) + qt.add_row(([5, 6] * u.km,)) + assert np.all(qt["col0"] == [[1, 2], [3, 4], [5000, 6000]] * u.m) + + def test_add_with_tuple(self, table_types): + self._setup(table_types) t = self.t - t.add_row([4, 7.2, '10']) + t.add_row((4, 7.2, "1")) assert len(t) == 4 - assert np.all(t['a'] == np.array([1, 2, 3, 4])) - assert np.allclose(t['b'], np.array([4.0, 5.1, 6.2, 7.2])) - assert np.all(t['c'] == np.array(['7', '8', '9', '1'])) + assert np.all(t["a"] == np.array([1, 2, 3, 4])) + assert np.allclose(t["b"], np.array([4.0, 5.1, 6.2, 7.2])) + assert np.all(t["c"] == np.array(["7", "8", "9", "1"])) - def test_add_with_dict(self): + def test_add_with_list(self, table_types): + self._setup(table_types) t = self.t - t.add_row({'a': 4, 'b': 7.2}) + t.add_row([4, 7.2, "10"]) assert len(t) == 4 - assert np.all(t['a'] == np.array([1, 2, 3, 4])) - assert np.allclose(t['b'], np.array([4.0, 5.1, 6.2, 7.2])) - assert np.all(t['c'] == np.array(['7', '8', '9', ''])) + assert np.all(t["a"] == np.array([1, 2, 3, 4])) + assert np.allclose(t["b"], np.array([4.0, 5.1, 6.2, 7.2])) + assert np.all(t["c"] == np.array(["7", "8", "9", "10"])) - def test_add_with_none(self): + def test_add_with_dict(self, table_types): + self._setup(table_types) + t = self.t + t.add_row({"a": 4, "b": 7.2}) + assert len(t) == 4 + assert np.all(t["a"] == np.array([1, 2, 3, 4])) + assert np.allclose(t["b"], np.array([4.0, 5.1, 6.2, 7.2])) + if t.masked: + assert np.all(t["c"] == np.array(["7", "8", "9", "7"])) + else: + assert np.all(t["c"] == np.array(["7", "8", "9", ""])) + + def test_add_with_none(self, table_types): + self._setup(table_types) t = self.t t.add_row() assert len(t) == 4 - assert np.all(t['a'].data == np.array([1, 2, 3, 0])) - assert np.allclose(t['b'], np.array([4.0, 5.1, 6.2, 0.0])) - assert np.all(t['c'].data == np.array(['7', '8', '9', ''])) + assert np.all(t["a"].data == np.array([1, 2, 3, 0])) + assert np.allclose(t["b"], np.array([4.0, 5.1, 6.2, 0.0])) + assert np.all(t["c"].data == np.array(["7", "8", "9", ""])) - def test_add_missing_column(self): + def test_add_missing_column(self, table_types): + self._setup(table_types) t = self.t with pytest.raises(ValueError): - t.add_row({'bad_column': 1}) + t.add_row({"bad_column": 1}) - def test_wrong_size_tuple(self): + def test_wrong_size_tuple(self, table_types): + self._setup(table_types) t = self.t with pytest.raises(ValueError): t.add_row((1, 2)) - def test_wrong_vals_type(self): + def test_wrong_vals_type(self, table_types): + self._setup(table_types) t = self.t with pytest.raises(TypeError): t.add_row(1) - def test_add_without_own_fails(self): - """Add row to a table that doesn't own the data""" - data = np.array([(1, 2, 3), - (3, 4, 5)], - dtype='i4') - t = Table(data, copy=False) - with pytest.raises(ValueError): - t.add_row([6, 7, 8]) - - -class TestTableColumn(): - - def setup_method(self, method): - self.a = Column('a', [1, 2, 3], meta={'aa': np.arange(5)}) - self.b = Column('b', [4, 5, 6]) - self.c = Column('c', [7, 8, 9]) - self.t = Table([self.a, self.b]) - - def test_column_view(self): + def test_add_row_failures(self, table_types): + self._setup(table_types) t = self.t - a = t.columns['a'] + t_copy = table_types.Table(t, copy=True) + # Wrong number of columns + try: + t.add_row([1, 2, 3, 4]) + except ValueError: + pass + assert len(t) == 3 + assert np.all(t.as_array() == t_copy.as_array()) + # Wrong data type + try: + t.add_row(["one", 2, 3]) + except ValueError: + pass + assert len(t) == 3 + assert np.all(t.as_array() == t_copy.as_array()) + + def test_insert_table_row(self, table_types): + """ + Light testing of Table.insert_row() method. The deep testing is done via + the add_row() tests which calls insert_row(index=len(self), ...), so + here just test that the added index parameter is handled correctly. + """ + self._setup(table_types) + row = (10, 40.0, "x", [10, 20]) + for index in range(-3, 4): + indices = np.insert(np.arange(3), index, 3) + t = table_types.Table([self.a, self.b, self.c, self.d]) + t2 = t.copy() + t.add_row(row) # By now we know this works + t2.insert_row(index, row) + for name in t.colnames: + if t[name].dtype.kind == "f": + assert np.allclose(t[name][indices], t2[name]) + else: + assert np.all(t[name][indices] == t2[name]) + + for index in (-4, 4): + t = table_types.Table([self.a, self.b, self.c, self.d]) + with pytest.raises(IndexError): + t.insert_row(index, row) + + +@pytest.mark.parametrize( + "table_type, table_inputs, expected_column_type, expected_pformat, insert_ctx", + [ + pytest.param( + table.Table, + dict(names=["a", "b", "c"]), + table.Column, + [ + " a b c ", + "--- --- ---", + "1.0 2.0 3.0", + ], + pytest.warns( + UserWarning, match="Units from inserted quantities will be ignored." + ), + id="Table-Column", + ), + pytest.param( + table.QTable, + dict(names=["a", "b", "c"]), + table.Column, + [ + " a b c ", + "--- --- ---", + "1.0 2.0 3.0", + ], + pytest.warns( + UserWarning, + match=( + "Units from inserted quantities will be ignored.\n" + "If you were hoping to fill a QTable row by row, " + "also initialize the units before starting, for instance\n" + r"QTable\(names=\['a', 'b', 'c'\], units=\['m', 'kg', None\]\)" + ), + ), + id="QTable-Column", + ), + pytest.param( + table.QTable, + dict(names=["a", "b", "c"], units=["m", "kg", None]), + u.Quantity, + [ + " a b c ", + " m kg ", + "--- --- ---", + "1.0 2.0 3.0", + ], + nullcontext(), + id="QTable-Quantity", + ), + pytest.param( + table.QTable, + dict(names=["a", "b", "c"], units=["cm", "g", None]), + u.Quantity, + [ + " a b c ", + " cm g ", + "----- ------ ---", + "100.0 2000.0 3.0", + ], + nullcontext(), + id="QTable-Quantity-other_units", + ), + ], +) +def test_inserting_quantity_row_in_empty_table( + table_type, table_inputs, expected_column_type, expected_pformat, insert_ctx +): + # see https://github.com/astropy/astropy/issues/15964 + table = table_type(**table_inputs) + pre_unit_a = copy.copy(table["a"].unit) + pre_unit_b = copy.copy(table["b"].unit) + pre_unit_c = copy.copy(table["c"].unit) + assert type(table["a"]) is expected_column_type + assert type(table["b"]) is expected_column_type + assert type(table["c"]) is Column + + with insert_ctx: + table.add_row([1 * u.m, 2 * u.kg, 3]) + + assert table["a"].unit == pre_unit_a + assert table["b"].unit == pre_unit_b + assert table["c"].unit == pre_unit_c + assert type(table["a"]) is expected_column_type + assert type(table["b"]) is expected_column_type + assert type(table["c"]) is Column + + assert table.pformat() == expected_pformat + + +@pytest.mark.usefixtures("table_types") +class TestTableColumn(SetupData): + def test_column_view(self, table_types): + self._setup(table_types) + t = self.t + a = t.columns["a"] a[2] = 10 - assert t._data['a'][2] == 10 - + assert t["a"][2] == 10 -class TestArrayColumns(): - def setup_method(self, method): - self.a = Column('a', [1, 2, 3]) - - def test_1d(self): - b = Column('b', dtype=int, shape=(2, ), length=3) - t = Table([self.a]) +@pytest.mark.usefixtures("table_types") +class TestArrayColumns(SetupData): + def test_1d(self, table_types): + self._setup(table_types) + b = table_types.Column(name="b", dtype=int, shape=(2,), length=3) + t = table_types.Table([self.a]) t.add_column(b) - assert t['b'].shape == (3, 2) - assert t['b'][0].shape == (2, ) + assert t["b"].shape == (3, 2) + assert t["b"][0].shape == (2,) - def test_2d(self): - b = Column('b', dtype=int, shape=(2, 4), length=3) - t = Table([self.a]) + def test_2d(self, table_types): + self._setup(table_types) + b = table_types.Column(name="b", dtype=int, shape=(2, 4), length=3) + t = table_types.Table([self.a]) t.add_column(b) - assert t['b'].shape == (3, 2, 4) - assert t['b'][0].shape == (2, 4) + assert t["b"].shape == (3, 2, 4) + assert t["b"][0].shape == (2, 4) - def test_3d(self): - t = Table([self.a]) - b = Column('b', dtype=int, shape=(2, 4, 6), length=3) + def test_3d(self, table_types): + self._setup(table_types) + t = table_types.Table([self.a]) + b = table_types.Column(name="b", dtype=int, shape=(2, 4, 6), length=3) t.add_column(b) - assert t['b'].shape == (3, 2, 4, 6) - assert t['b'][0].shape == (2, 4, 6) + assert t["b"].shape == (3, 2, 4, 6) + assert t["b"][0].shape == (2, 4, 6) + + +@pytest.mark.usefixtures("table_types") +class TestRemove(SetupData): + @property + def t(self): + if self._table_type is not None: + if not hasattr(self, "_t"): + self._t = self._table_type([self.a]) + return self._t + + @property + def t2(self): + if self._table_type is not None: + if not hasattr(self, "_t2"): + self._t2 = self._table_type([self.a, self.b, self.c]) + return self._t2 + + def test_1(self, table_types): + self._setup(table_types) + self.t.remove_columns("a") + assert self.t.colnames == [] + assert self.t.as_array().size == 0 + # Regression test for gh-8640 + assert not self.t + assert isinstance(self.t == None, np.ndarray) + assert (self.t == None).size == 0 + + def test_2(self, table_types): + self._setup(table_types) + self.t.add_column(self.b) + self.t.remove_columns("a") + assert self.t.colnames == ["b"] + assert self.t.dtype.names == ("b",) + assert np.all(self.t["b"] == np.array([4, 5, 6])) + + def test_3(self, table_types): + """Check remove_columns works for a single column with a name of + more than one character. Regression test against #2699""" + self._setup(table_types) + self.t["new_column"] = self.t["a"] + assert "new_column" in self.t.columns.keys() + self.t.remove_columns("new_column") + assert "new_column" not in self.t.columns.keys() + + def test_remove_nonexistent_row(self, table_types): + self._setup(table_types) + with pytest.raises(IndexError): + self.t.remove_row(4) + + def test_remove_row_0(self, table_types): + self._setup(table_types) + self.t.add_column(self.b) + self.t.add_column(self.c) + self.t.remove_row(0) + assert self.t.colnames == ["a", "b", "c"] + assert np.all(self.t["b"] == np.array([5, 6])) + + def test_remove_row_1(self, table_types): + self._setup(table_types) + self.t.add_column(self.b) + self.t.add_column(self.c) + self.t.remove_row(1) + assert self.t.colnames == ["a", "b", "c"] + assert np.all(self.t["a"] == np.array([1, 3])) + + def test_remove_row_2(self, table_types): + self._setup(table_types) + self.t.add_column(self.b) + self.t.add_column(self.c) + self.t.remove_row(2) + assert self.t.colnames == ["a", "b", "c"] + assert np.all(self.t["c"] == np.array([7, 8])) + def test_remove_row_slice(self, table_types): + self._setup(table_types) + self.t.add_column(self.b) + self.t.add_column(self.c) + self.t.remove_rows(slice(0, 2, 1)) + assert self.t.colnames == ["a", "b", "c"] + assert np.all(self.t["c"] == np.array([9])) -class TestRemove(): + def test_remove_row_list(self, table_types): + self._setup(table_types) + self.t.add_column(self.b) + self.t.add_column(self.c) + self.t.remove_rows([0, 2]) + assert self.t.colnames == ["a", "b", "c"] + assert np.all(self.t["c"] == np.array([8])) - def setup_method(self, method): - self.a = Column('a', [1, 2, 3]) - self.b = Column('b', [4, 5, 6]) - self.c = Column('c', [7, 8, 9]) - self.t = Table([self.a]) - self.t2 = Table([self.a, self.b, self.c]) + def test_remove_row_preserves_meta(self, table_types): + self._setup(table_types) + self.t.add_column(self.b) + self.t.remove_rows([0, 2]) + assert self.t["a"].meta == {"aa": [0, 1, 2, 3, 4]} + assert self.t.dtype == np.dtype([("a", "int"), ("b", "int")]) - def test_1(self): - self.t.remove_columns('a') - assert self.t.columns.keys() == [] - assert self.t._data is None + def test_delitem_row(self, table_types): + self._setup(table_types) + self.t.add_column(self.b) + self.t.add_column(self.c) + del self.t[1] + assert self.t.colnames == ["a", "b", "c"] + assert np.all(self.t["a"] == np.array([1, 3])) + + @pytest.mark.parametrize("idx", [[0, 2], np.array([0, 2])]) + def test_delitem_row_list(self, table_types, idx): + self._setup(table_types) + self.t.add_column(self.b) + self.t.add_column(self.c) + del self.t[idx] + assert self.t.colnames == ["a", "b", "c"] + assert np.all(self.t["c"] == np.array([8])) - def test_2(self): + def test_delitem_row_slice(self, table_types): + self._setup(table_types) self.t.add_column(self.b) - self.t.remove_columns('a') - assert self.t.columns.keys() == ['b'] - assert self.t._data.dtype.names == ('b',) - assert np.all(self.t['b'] == np.array([4, 5, 6])) + self.t.add_column(self.c) + del self.t[0:2] + assert self.t.colnames == ["a", "b", "c"] + assert np.all(self.t["c"] == np.array([9])) + + def test_delitem_row_fail(self, table_types): + self._setup(table_types) + with pytest.raises(IndexError): + del self.t[4] + + def test_delitem_row_float(self, table_types): + self._setup(table_types) + with pytest.raises(IndexError): + del self.t[1.0] + + def test_delitem1(self, table_types): + self._setup(table_types) + del self.t["a"] + assert self.t.colnames == [] + assert self.t.as_array().size == 0 + # Regression test for gh-8640 + assert not self.t + assert isinstance(self.t == None, np.ndarray) + assert (self.t == None).size == 0 + + def test_delitem2(self, table_types): + self._setup(table_types) + del self.t2["b"] + assert self.t2.colnames == ["a", "c"] + + def test_delitems(self, table_types): + self._setup(table_types) + del self.t2["a", "b"] + assert self.t2.colnames == ["c"] + + def test_delitem_fail(self, table_types): + self._setup(table_types) + with pytest.raises(KeyError): + del self.t["d"] - def test_delitem1(self): - del self.t['a'] - assert self.t.columns.keys() == [] - assert self.t._data is None - def test_delitem2(self): - del self.t2['b'] - assert self.t2.colnames == ['a', 'c'] +@pytest.mark.usefixtures("table_types") +class TestKeep(SetupData): + def test_1(self, table_types): + self._setup(table_types) + t = table_types.Table([self.a, self.b]) + t.keep_columns([]) + assert t.colnames == [] + assert t.as_array().size == 0 + # Regression test for gh-8640 + assert not t + assert isinstance(t == None, np.ndarray) + assert (t == None).size == 0 + + def test_2(self, table_types): + self._setup(table_types) + t = table_types.Table([self.a, self.b]) + t.keep_columns("b") + assert t.colnames == ["b"] + assert t.dtype.names == ("b",) + assert np.all(t["b"] == np.array([4, 5, 6])) + + +@pytest.mark.usefixtures("table_types") +class TestRename(SetupData): + def test_1(self, table_types): + self._setup(table_types) + t = table_types.Table([self.a]) + t.rename_column("a", "b") + assert t.colnames == ["b"] + assert t.dtype.names == ("b",) + assert np.all(t["b"] == np.array([1, 2, 3])) + + def test_2(self, table_types): + self._setup(table_types) + t = table_types.Table([self.a, self.b]) + t.rename_column("a", "c") + t.rename_column("b", "a") + assert t.colnames == ["c", "a"] + assert t.dtype.names == ("c", "a") + if t.masked: + assert t.mask.dtype.names == ("c", "a") + assert np.all(t["c"] == np.array([1, 2, 3])) + assert np.all(t["a"] == np.array([4, 5, 6])) + + def test_rename_by_attr(self, table_types): + self._setup(table_types) + t = table_types.Table([self.a, self.b]) + t["a"].name = "c" + t["b"].name = "a" + assert t.colnames == ["c", "a"] + assert t.dtype.names == ("c", "a") + assert np.all(t["c"] == np.array([1, 2, 3])) + assert np.all(t["a"] == np.array([4, 5, 6])) + + def test_rename_columns(self, table_types): + self._setup(table_types) + t = table_types.Table([self.a, self.b, self.c]) + t.rename_columns(("a", "b", "c"), ("aa", "bb", "cc")) + assert t.colnames == ["aa", "bb", "cc"] + t.rename_columns(["bb", "cc"], ["b", "c"]) + assert t.colnames == ["aa", "b", "c"] + with pytest.raises(TypeError): + t.rename_columns("aa", ["a"]) + with pytest.raises(ValueError): + t.rename_columns(["a"], ["b", "c"]) + + +@pytest.mark.usefixtures("table_types") +class TestSort: + def test_single(self, table_types): + t = table_types.Table() + t.add_column(table_types.Column(name="a", data=[2, 1, 3])) + t.add_column(table_types.Column(name="b", data=[6, 5, 4])) + t.add_column( + table_types.Column( + name="c", + data=[ + (1, 2), + (3, 4), + (4, 5), + ], + ) + ) + assert np.all(t["a"] == np.array([2, 1, 3])) + assert np.all(t["b"] == np.array([6, 5, 4])) + t.sort("a") + assert np.all(t["a"] == np.array([1, 2, 3])) + assert np.all(t["b"] == np.array([5, 6, 4])) + assert np.all( + t["c"] + == np.array( + [ + [3, 4], + [1, 2], + [4, 5], + ] + ) + ) + t.sort("b") + assert np.all(t["a"] == np.array([3, 1, 2])) + assert np.all(t["b"] == np.array([4, 5, 6])) + assert np.all( + t["c"] + == np.array( + [ + [4, 5], + [3, 4], + [1, 2], + ] + ) + ) + + @pytest.mark.parametrize("create_index", [False, True]) + def test_single_reverse(self, table_types, create_index): + t = table_types.Table() + t.add_column(table_types.Column(name="a", data=[2, 1, 3])) + t.add_column(table_types.Column(name="b", data=[6, 5, 4])) + t.add_column(table_types.Column(name="c", data=[(1, 2), (3, 4), (4, 5)])) + assert np.all(t["a"] == np.array([2, 1, 3])) + assert np.all(t["b"] == np.array([6, 5, 4])) + t.sort("a", reverse=True) + assert np.all(t["a"] == np.array([3, 2, 1])) + assert np.all(t["b"] == np.array([4, 6, 5])) + assert np.all(t["c"] == np.array([[4, 5], [1, 2], [3, 4]])) + t.sort("b", reverse=True) + assert np.all(t["a"] == np.array([2, 1, 3])) + assert np.all(t["b"] == np.array([6, 5, 4])) + assert np.all(t["c"] == np.array([[1, 2], [3, 4], [4, 5]])) + + def test_single_big(self, table_types): + """Sort a big-ish table with a non-trivial sort order""" + x = np.arange(10000) + y = np.sin(x) + t = table_types.Table([x, y], names=("x", "y")) + t.sort("y") + idx = np.argsort(y) + assert np.all(t["x"] == x[idx]) + assert np.all(t["y"] == y[idx]) + + @pytest.mark.parametrize("reverse", [True, False]) + def test_empty_reverse(self, table_types, reverse): + t = table_types.Table([[], []], dtype=["f4", "U1"]) + t.sort("col1", reverse=reverse) + + def test_multiple(self, table_types): + t = table_types.Table() + t.add_column(table_types.Column(name="a", data=[2, 1, 3, 2, 3, 1])) + t.add_column(table_types.Column(name="b", data=[6, 5, 4, 3, 5, 4])) + assert np.all(t["a"] == np.array([2, 1, 3, 2, 3, 1])) + assert np.all(t["b"] == np.array([6, 5, 4, 3, 5, 4])) + t.sort(["a", "b"]) + assert np.all(t["a"] == np.array([1, 1, 2, 2, 3, 3])) + assert np.all(t["b"] == np.array([4, 5, 3, 6, 4, 5])) + t.sort(["b", "a"]) + assert np.all(t["a"] == np.array([2, 1, 3, 1, 3, 2])) + assert np.all(t["b"] == np.array([3, 4, 4, 5, 5, 6])) + t.sort(("a", "b")) + assert np.all(t["a"] == np.array([1, 1, 2, 2, 3, 3])) + assert np.all(t["b"] == np.array([4, 5, 3, 6, 4, 5])) + + def test_multiple_reverse(self, table_types): + t = table_types.Table() + t.add_column(table_types.Column(name="a", data=[2, 1, 3, 2, 3, 1])) + t.add_column(table_types.Column(name="b", data=[6, 5, 4, 3, 5, 4])) + assert np.all(t["a"] == np.array([2, 1, 3, 2, 3, 1])) + assert np.all(t["b"] == np.array([6, 5, 4, 3, 5, 4])) + t.sort(["a", "b"], reverse=True) + assert np.all(t["a"] == np.array([3, 3, 2, 2, 1, 1])) + assert np.all(t["b"] == np.array([5, 4, 6, 3, 5, 4])) + t.sort(["b", "a"], reverse=True) + assert np.all(t["a"] == np.array([2, 3, 1, 3, 1, 2])) + assert np.all(t["b"] == np.array([6, 5, 5, 4, 4, 3])) + t.sort(("a", "b"), reverse=True) + assert np.all(t["a"] == np.array([3, 3, 2, 2, 1, 1])) + assert np.all(t["b"] == np.array([5, 4, 6, 3, 5, 4])) + + def test_multiple_with_bytes(self, table_types): + t = table_types.Table() + t.add_column( + table_types.Column(name="firstname", data=[b"Max", b"Jo", b"John"]) + ) + t.add_column( + table_types.Column(name="name", data=[b"Miller", b"Miller", b"Jackson"]) + ) + t.add_column(table_types.Column(name="tel", data=[12, 15, 19])) + t.sort(["name", "firstname"]) + assert np.all([t["firstname"] == np.array([b"John", b"Jo", b"Max"])]) + assert np.all([t["name"] == np.array([b"Jackson", b"Miller", b"Miller"])]) + assert np.all([t["tel"] == np.array([19, 15, 12])]) + + def test_multiple_with_unicode(self, table_types): + # Before Numpy 1.6.2, sorting with multiple column names + # failed when a unicode column was present. + t = table_types.Table() + t.add_column( + table_types.Column( + name="firstname", data=[str(x) for x in ["Max", "Jo", "John"]] + ) + ) + t.add_column( + table_types.Column( + name="name", data=[str(x) for x in ["Miller", "Miller", "Jackson"]] + ) + ) + t.add_column(table_types.Column(name="tel", data=[12, 15, 19])) + t.sort(["name", "firstname"]) + assert np.all( + [t["firstname"] == np.array([str(x) for x in ["John", "Jo", "Max"]])] + ) + assert np.all( + [t["name"] == np.array([str(x) for x in ["Jackson", "Miller", "Miller"]])] + ) + assert np.all([t["tel"] == np.array([19, 15, 12])]) + + def test_argsort(self, table_types): + t = table_types.Table() + t.add_column(table_types.Column(name="a", data=[2, 1, 3, 2, 3, 1])) + t.add_column(table_types.Column(name="b", data=[6, 5, 4, 3, 5, 4])) + assert np.all(t.argsort() == t.as_array().argsort()) + i0 = t.argsort("a") + i1 = t.as_array().argsort(order=["a"]) + assert np.all(t["a"][i0] == t["a"][i1]) + i0 = t.argsort(["a", "b"]) + i1 = t.as_array().argsort(order=["a", "b"]) + assert np.all(t["a"][i0] == t["a"][i1]) + assert np.all(t["b"][i0] == t["b"][i1]) + + @pytest.mark.parametrize("add_index", [False, True]) + def test_argsort_reverse(self, table_types, add_index): + t = table_types.Table() + t.add_column(table_types.Column(name="a", data=[2, 1, 3, 2, 3, 1])) + t.add_column(table_types.Column(name="b", data=[6, 5, 4, 3, 5, 4])) + if add_index: + t.add_index("a") + assert np.all(t.argsort(reverse=True) == np.array([4, 2, 0, 3, 1, 5])) + i0 = t.argsort("a", reverse=True) + i1 = np.array([4, 2, 3, 0, 5, 1]) + assert np.all(t["a"][i0] == t["a"][i1]) + i0 = t.argsort(["a", "b"], reverse=True) + i1 = np.array([4, 2, 0, 3, 1, 5]) + assert np.all(t["a"][i0] == t["a"][i1]) + assert np.all(t["b"][i0] == t["b"][i1]) + + def test_argsort_bytes(self, table_types): + t = table_types.Table() + t.add_column( + table_types.Column(name="firstname", data=[b"Max", b"Jo", b"John"]) + ) + t.add_column( + table_types.Column(name="name", data=[b"Miller", b"Miller", b"Jackson"]) + ) + t.add_column(table_types.Column(name="tel", data=[12, 15, 19])) + assert np.all(t.argsort(["name", "firstname"]) == np.array([2, 1, 0])) + + def test_argsort_unicode(self, table_types): + # Before Numpy 1.6.2, sorting with multiple column names + # failed when a unicode column was present. + t = table_types.Table() + t.add_column( + table_types.Column( + name="firstname", data=[str(x) for x in ["Max", "Jo", "John"]] + ) + ) + t.add_column( + table_types.Column( + name="name", data=[str(x) for x in ["Miller", "Miller", "Jackson"]] + ) + ) + t.add_column(table_types.Column(name="tel", data=[12, 15, 19])) + assert np.all(t.argsort(["name", "firstname"]) == np.array([2, 1, 0])) + + def test_rebuild_column_view_then_rename(self, table_types): + """ + Issue #2039 where renaming fails after any method that calls + _rebuild_table_column_view (this includes sort and add_row). + """ + t = table_types.Table([[1]], names=("a",)) + assert t.colnames == ["a"] + assert t.dtype.names == ("a",) + + t.add_row((2,)) + assert t.colnames == ["a"] + assert t.dtype.names == ("a",) + + t.rename_column("a", "b") + assert t.colnames == ["b"] + assert t.dtype.names == ("b",) + + t.sort("b") + assert t.colnames == ["b"] + assert t.dtype.names == ("b",) + + t.rename_column("b", "c") + assert t.colnames == ["c"] + assert t.dtype.names == ("c",) + + +@pytest.mark.parametrize("kwargs", [{}, {"kind": "stable"}, {"kind": "quicksort"}]) +def test_sort_kind(kwargs): + t = Table() + t["a"] = [2, 1, 3, 2, 3, 1] + t["b"] = [6, 5, 4, 3, 5, 4] + t_struct = t.as_array() + # Since sort calls Table.argsort this covers `kind` for both methods + t.sort(["a", "b"], **kwargs) + assert np.all(t.as_array() == np.sort(t_struct, **kwargs)) + + +@pytest.mark.usefixtures("table_types") +class TestIterator: + def test_iterator(self, table_types): + d = np.array( + [ + (2, 1), + (3, 6), + (4, 5), + ], + dtype=[("a", "i4"), ("b", "i4")], + ) + t = table_types.Table(d) + if t.masked: + with pytest.raises(ValueError): + t[0] == d[0] # noqa: B015 + else: + for row, np_row in zip(t, d): + assert np.all(row == np_row) + + +@pytest.mark.usefixtures("table_types") +class TestSetMeta: + def test_set_meta(self, table_types): + d = table_types.Table(names=("a", "b")) + d.meta["a"] = 1 + d.meta["b"] = 1 + d.meta["c"] = 1 + d.meta["d"] = 1 + assert list(d.meta.keys()) == ["a", "b", "c", "d"] + + +@pytest.mark.usefixtures("table_types") +class TestConvertNumpyArray: + def test_convert_numpy_array(self, table_types): + d = table_types.Table([[1, 2], [3, 4]], names=("a", "b")) - def test_delitems(self): - del self.t2['a', 'b'] - assert self.t2.colnames == ['c'] + np_data = np.array(d) + if table_types.Table is not MaskedTable: + assert np.all(np_data == d.as_array()) + assert np_data is not d.as_array() + assert d.colnames == list(np_data.dtype.names) - def test_delitem_fail(self): - with pytest.raises(KeyError): - del self.t['d'] + np_data = np.asarray(d) + if table_types.Table is not MaskedTable: + assert np.all(np_data == d.as_array()) + assert d.colnames == list(np_data.dtype.names) + with pytest.raises(ValueError): + np_data = np.array(d, dtype=[("c", "i8"), ("d", "i8")]) + + def test_as_array_byteswap(self, table_types): + """Test for https://github.com/astropy/astropy/pull/4080""" + + byte_orders = (">", "<") + native_order = byte_orders[sys.byteorder == "little"] + + for order in byte_orders: + col = table_types.Column([1.0, 2.0], name="a", dtype=order + "f8") + t = table_types.Table([col]) + arr = t.as_array() + assert arr["a"].dtype.byteorder in (native_order, "=") + arr = t.as_array(keep_byteorder=True) + if order == native_order: + assert arr["a"].dtype.byteorder in (order, "=") + else: + assert arr["a"].dtype.byteorder == order + + def test_byteswap_fits_array(self, table_types): + """ + Test for https://github.com/astropy/astropy/pull/4080, demonstrating + that FITS tables are converted to native byte order. + """ -class TestKeep(): + non_native_order = (">", "<")[sys.byteorder != "little"] - def setup_method(self, method): - self.a = Column('a', [1, 2, 3]) - self.b = Column('b', [4, 5, 6]) + filename = get_pkg_data_filename("data/tb.fits", "astropy.io.fits.tests") + t = table_types.Table.read(filename) + arr = t.as_array() - def test_1(self): - t = Table([self.a, self.b]) - t.keep_columns([]) - assert t.columns.keys() == [] - assert t._data is None + for idx in range(len(arr.dtype)): + assert arr.dtype[idx].byteorder != non_native_order - def test_2(self): - t = Table([self.a, self.b]) - t.keep_columns('b') - assert t.columns.keys() == ['b'] - assert t._data.dtype.names == ('b',) - assert np.all(t['b'] == np.array([4, 5, 6])) + with fits.open(filename, character_as_bytes=True) as hdul: + data = hdul[1].data + for colname in data.columns.names: + assert np.all(data[colname] == arr[colname]) + arr2 = t.as_array(keep_byteorder=True) + for colname in data.columns.names: + assert data[colname].dtype.byteorder == arr2[colname].dtype.byteorder -class TestRename(): + def test_convert_numpy_object_array(self, table_types): + d = table_types.Table([[1, 2], [3, 4]], names=("a", "b")) - def setup_method(self, method): - self.a = Column('a', [1, 2, 3]) - self.b = Column('b', [4, 5, 6]) + # Single table + np_d = np.array(d, dtype=object) + assert isinstance(np_d, np.ndarray) + assert np_d[()] is d - def test_1(self): - t = Table([self.a]) - t.rename_column('a', 'b') - assert t.columns.keys() == ['b'] - assert t._data.dtype.names == ('b',) - assert np.all(t['b'] == np.array([1, 2, 3])) - - def test_2(self): - t = Table([self.a, self.b]) - t.rename_column('a', 'c') - t.rename_column('b', 'a') - assert t.columns.keys() == ['c', 'a'] - assert t._data.dtype.names == ('c', 'a') - assert np.all(t['c'] == np.array([1, 2, 3])) - assert np.all(t['a'] == np.array([4, 5, 6])) - - def test_rename_by_attr(self): - t = Table([self.a, self.b]) - t['a'].name = 'c' - t['b'].name = 'a' - assert t.columns.keys() == ['c', 'a'] - assert t._data.dtype.names == ('c', 'a') - assert np.all(t['c'] == np.array([1, 2, 3])) - assert np.all(t['a'] == np.array([4, 5, 6])) - - -class TestSort(): - - def test_single(self): - t = Table() - t.add_column(Column('a', [2, 1, 3])) - t.add_column(Column('b', [6, 5, 4])) - assert np.all(t['a'] == np.array([2, 1, 3])) - assert np.all(t['b'] == np.array([6, 5, 4])) - t.sort('a') - assert np.all(t['a'] == np.array([1, 2, 3])) - assert np.all(t['b'] == np.array([5, 6, 4])) - t.sort('b') - assert np.all(t['a'] == np.array([3, 1, 2])) - assert np.all(t['b'] == np.array([4, 5, 6])) - - def test_multiple(self): - t = Table() - t.add_column(Column('a', [2, 1, 3, 2, 3, 1])) - t.add_column(Column('b', [6, 5, 4, 3, 5, 4])) - assert np.all(t['a'] == np.array([2, 1, 3, 2, 3, 1])) - assert np.all(t['b'] == np.array([6, 5, 4, 3, 5, 4])) - t.sort(['a', 'b']) - assert np.all(t['a'] == np.array([1, 1, 2, 2, 3, 3])) - assert np.all(t['b'] == np.array([4, 5, 3, 6, 4, 5])) - t.sort(['b', 'a']) - assert np.all(t['a'] == np.array([2, 1, 3, 1, 3, 2])) - assert np.all(t['b'] == np.array([3, 4, 4, 5, 5, 6])) - - -class TestIterator(): - - def test_iterator(self): - d = np.array([(2, 1), - (3, 6), - (4, 5)], dtype=[('a', 'i4'), ('b', 'i4')]) - t = Table(d) - for row, np_row in zip(t, d): - assert np.all(row == np_row) - - -class TestSetMeta(): - - def test_set_meta(self): - d = Table(names=('a', 'b')) - d.meta['a'] = 1 - d.meta['b'] = 1 - d.meta['c'] = 1 - d.meta['d'] = 1 - assert list(d.meta.keys()) == ['a', 'b', 'c', 'd'] - - -class TestConvertNumpyArray(): - - def test_convert_numpy_array(self): - d = Table([[1, 2], [3, 4]], names=('a', 'b')) + def test_convert_list_numpy_object_array(self, table_types): + d = table_types.Table([[1, 2], [3, 4]], names=("a", "b")) + ds = [d, d, d] + np_ds = np.array(ds, dtype=object) + assert all(isinstance(t, table_types.Table) for t in np_ds) + assert all(np.array_equal(t, d) for t in np_ds) - np_data = np.array(d) - assert np.all(np_data == d._data) - assert not np_data is d._data - assert d.colnames == list(np_data.dtype.names) - np_data = np.array(d, copy=False) - assert np.all(np_data == d._data) - assert np_data is d._data - assert d.colnames == list(np_data.dtype.names) +def _assert_copies(t, t2, deep=True): + assert t.colnames == t2.colnames + np.testing.assert_array_equal(t.as_array(), t2.as_array()) + assert t.meta == t2.meta + + for col, col2 in zip(t.columns.values(), t2.columns.values()): + if deep: + assert not np.may_share_memory(col, col2) + else: + assert np.may_share_memory(col, col2) + + +def test_copy(): + t = table.Table([[1, 2, 3], [2, 3, 4]], names=["x", "y"]) + t2 = t.copy() + _assert_copies(t, t2) + + +def test_copy_masked(): + t = table.Table( + [[1, 2, 3], [2, 3, 4]], names=["x", "y"], masked=True, meta={"name": "test"} + ) + t["x"].mask = [True, False, True] + t2 = t.copy() + _assert_copies(t, t2) + +def test_copy_protocol(): + t = table.Table([[1, 2, 3], [2, 3, 4]], names=["x", "y"]) + + t2 = copy.copy(t) + t3 = copy.deepcopy(t) + + _assert_copies(t, t2, deep=False) + _assert_copies(t, t3) + + +def test_disallow_inequality_comparisons(): + """ + Regression test for #828 - disallow comparison operators on whole Table + """ + + t = table.Table() + + with pytest.raises(TypeError): + t > 2 # noqa: B015 + + with pytest.raises(TypeError): + t < 1.1 # noqa: B015 + + with pytest.raises(TypeError): + t >= 5.5 # noqa: B015 + + with pytest.raises(TypeError): + t <= -1.1 # noqa: B015 + + +def test_values_equal_part1(): + col1 = [1, 2] + col2 = [1.0, 2.0] + col3 = ["a", "b"] + t1 = table.Table([col1, col2, col3], names=["a", "b", "c"]) + t2 = table.Table([col1, col2], names=["a", "b"]) + t3 = table.table_helpers.simple_table() + tm = t1.copy() + tm["time"] = Time([1, 2], format="cxcsec") + tm1 = tm.copy() + tm1["time"][0] = np.ma.masked + + tq = table.table_helpers.simple_table() + tq["quantity"] = [1.0, 2.0, 3.0] * u.m + + tsk = table.table_helpers.simple_table() + tsk["sk"] = SkyCoord(1, 2, unit="deg") + eqsk = tsk.values_equal(tsk) + for col in eqsk.itercols(): + assert np.all(col) + + with pytest.raises( + ValueError, match="cannot compare tables with different column names" + ): + t2.values_equal(t1) + + with pytest.raises(ValueError, match="unable to compare column a"): + # Shape mismatch + t3.values_equal(t1) + + eq = t2.values_equal(2) + for col in eq.colnames: + assert np.all(eq[col] == [False, True]) + + eq = t2.values_equal([1, 2]) + for col in eq.colnames: + assert np.all(eq[col] == [True, True]) + + eq = t2.values_equal(t2) + for col in eq.colnames: + assert np.all(eq[col] == [True, True]) + + eq1 = tm1.values_equal(tm) + for col in eq1.colnames: + assert np.all(eq1[col] == [True, True]) + + eq2 = tq.values_equal(tq) + for col in eq2.colnames: + assert np.all(eq2[col] == [True, True, True]) + + eq3 = t2.values_equal(2) + for col in eq3.colnames: + assert np.all(eq3[col] == [False, True]) + + eq4 = t2.values_equal([1, 2]) + for col in eq4.colnames: + assert np.all(eq4[col] == [True, True]) + + # Compare table to its first row + t = table.Table(rows=[(1, "a"), (1, "b")]) + eq = t.values_equal(t[0]) + assert np.all(eq["col0"] == [True, True]) + assert np.all(eq["col1"] == [True, False]) + + +def test_rows_equal(): + t = table.Table.read( + [ + " a b c d", + " 2 c 7.0 0", + " 2 b 5.0 1", + " 2 b 6.0 2", + " 2 a 4.0 3", + " 0 a 0.0 4", + " 1 b 3.0 5", + " 1 a 2.0 6", + " 1 a 1.0 7", + ], + format="ascii", + ) + + # All rows are equal + assert np.all(t == t) + + # Assert no rows are different + assert not np.any(t != t) + + # Check equality result for a given row + assert np.all((t == t[3]) == np.array([0, 0, 0, 1, 0, 0, 0, 0], dtype=bool)) + + # Check inequality result for a given row + assert np.all((t != t[3]) == np.array([1, 1, 1, 0, 1, 1, 1, 1], dtype=bool)) + + t2 = table.Table.read( + [ + " a b c d", + " 2 c 7.0 0", + " 2 b 5.0 1", + " 3 b 6.0 2", + " 2 a 4.0 3", + " 0 a 1.0 4", + " 1 b 3.0 5", + " 1 c 2.0 6", + " 1 a 1.0 7", + ], + format="ascii", + ) + + # In the above cases, Row.__eq__ gets called, but now need to make sure + # Table.__eq__ also gets called. + assert np.all((t == t2) == np.array([1, 1, 0, 1, 0, 1, 0, 1], dtype=bool)) + assert np.all((t != t2) == np.array([0, 0, 1, 0, 1, 0, 1, 0], dtype=bool)) + + # Check that comparing to a structured array works + assert np.all( + (t == t2.as_array()) == np.array([1, 1, 0, 1, 0, 1, 0, 1], dtype=bool) + ) + assert np.all( + (t.as_array() == t2) == np.array([1, 1, 0, 1, 0, 1, 0, 1], dtype=bool) + ) + + +def test_table_from_rows(): + # see https://github.com/astropy/astropy/issues/5923 + t1 = Table() + t1["a"] = [1, 2, 3] + t1["b"] = [2.0, 3.0, 4.0] + + rows = [row for row in t1] # noqa: C416 + t2 = Table(rows=rows) + assert_array_equal(t2.colnames, t1.colnames) + + +def test_equality_masked(): + t = table.Table.read( + [ + " a b c d", + " 2 c 7.0 0", + " 2 b 5.0 1", + " 2 b 6.0 2", + " 2 a 4.0 3", + " 0 a 0.0 4", + " 1 b 3.0 5", + " 1 a 2.0 6", + " 1 a 1.0 7", + ], + format="ascii", + ) + + # Make into masked table + t = table.Table(t, masked=True) + + # All rows are equal + assert np.all(t == t) + + # Assert no rows are different + assert not np.any(t != t) + + # Check equality result for a given row + assert np.all((t == t[3]) == np.array([0, 0, 0, 1, 0, 0, 0, 0], dtype=bool)) + + # Check inequality result for a given row + assert np.all((t != t[3]) == np.array([1, 1, 1, 0, 1, 1, 1, 1], dtype=bool)) + + t2 = table.Table.read( + [ + " a b c d", + " 2 c 7.0 0", + " 2 b 5.0 1", + " 3 b 6.0 2", + " 2 a 4.0 3", + " 0 a 1.0 4", + " 1 b 3.0 5", + " 1 c 2.0 6", + " 1 a 1.0 7", + ], + format="ascii", + ) + + # In the above cases, Row.__eq__ gets called, but now need to make sure + # Table.__eq__ also gets called. + assert np.all((t == t2) == np.array([1, 1, 0, 1, 0, 1, 0, 1], dtype=bool)) + assert np.all((t != t2) == np.array([0, 0, 1, 0, 1, 0, 1, 0], dtype=bool)) + + # Check that masking a value causes the row to differ + t.mask["a"][0] = True + assert np.all((t == t2) == np.array([0, 1, 0, 1, 0, 1, 0, 1], dtype=bool)) + assert np.all((t != t2) == np.array([1, 0, 1, 0, 1, 0, 1, 0], dtype=bool)) + + # Check that comparing to a structured array works + assert np.all( + (t == t2.as_array()) == np.array([0, 1, 0, 1, 0, 1, 0, 1], dtype=bool) + ) + + +@pytest.mark.xfail +def test_equality_masked_bug(): + """ + This highlights a Numpy bug. Once it works, it can be moved into the + test_equality_masked test. Related Numpy bug report: + + https://github.com/numpy/numpy/issues/3840 + """ + + t = table.Table.read( + [ + " a b c d", + " 2 c 7.0 0", + " 2 b 5.0 1", + " 2 b 6.0 2", + " 2 a 4.0 3", + " 0 a 0.0 4", + " 1 b 3.0 5", + " 1 a 2.0 6", + " 1 a 1.0 7", + ], + format="ascii", + ) + + t = table.Table(t, masked=True) + + t2 = table.Table.read( + [ + " a b c d", + " 2 c 7.0 0", + " 2 b 5.0 1", + " 3 b 6.0 2", + " 2 a 4.0 3", + " 0 a 1.0 4", + " 1 b 3.0 5", + " 1 c 2.0 6", + " 1 a 1.0 7", + ], + format="ascii", + ) + + assert np.all( + (t.as_array() == t2) == np.array([0, 1, 0, 1, 0, 1, 0, 1], dtype=bool) + ) + + +# Check that the meta descriptor is working as expected. The MetaBaseTest class +# takes care of defining all the tests, and we simply have to define the class +# and any minimal set of args to pass. + + +class TestMetaTable(MetaBaseTest): + test_class = table.Table + args = () + + +def test_unicode_content(): + # If we don't have unicode literals then return + if isinstance("", bytes): + return + + # Define unicode literals + string_a = "Đ°ŅŅ‚Ņ€ĐžĐŊĐžĐŧĐ¸Ņ‡ĐĩҁĐēĐ°Ņ ĐŋĐ¸Ņ‚ĐžĐŊа" + string_b = "ĐŧиĐģĐģĐ¸Đ°Ņ€Đ´Ņ‹ ŅĐ˛ĐĩŅ‚ĐžĐ˛Ņ‹Ņ… ĐģĐĩŅ‚" + + a = table.Table([[string_a, 2], [string_b, 3]], names=("a", "b")) + + assert string_a in str(a) + # This only works because the coding of this file is utf-8, which + # matches the default encoding of Table.__str__ + assert string_a.encode("utf-8") in bytes(a) + + +def test_unicode_policy(): + t = table.Table.read( + [ + " a b c d", + " 2 c 7.0 0", + " 2 b 5.0 1", + " 2 b 6.0 2", + " 2 a 4.0 3", + " 0 a 0.0 4", + " 1 b 3.0 5", + " 1 a 2.0 6", + " 1 a 1.0 7", + ], + format="ascii", + ) + assert_follows_unicode_guidelines(t) + + +@pytest.mark.parametrize("uni", ["ĐŋĐ¸Ņ‚ĐžĐŊа", "ascii"]) +def test_unicode_bytestring_conversion(table_types, uni): + """ + Test converting columns to all unicode or all bytestring. This + makes two columns, one which is unicode (str in Py3) and one which + is bytes (UTF-8 encoded). There are two code paths in the conversions, + a faster one where the data are actually ASCII and a slower one where + UTF-8 conversion is required. This tests both via the ``uni`` param. + """ + byt = uni.encode("utf-8") + t = table_types.Table([[byt], [uni], [1]], dtype=("S", "U", "i")) + assert t["col0"].dtype.kind == "S" + assert t["col1"].dtype.kind == "U" + assert t["col2"].dtype.kind == "i" + t["col0"].description = "col0" + t["col1"].description = "col1" + t["col0"].meta["val"] = "val0" + t["col1"].meta["val"] = "val1" + + # Unicode to bytestring + t1 = t.copy() + t1.convert_unicode_to_bytestring() + assert t1["col0"].dtype.kind == "S" + assert t1["col1"].dtype.kind == "S" + assert t1["col2"].dtype.kind == "i" + + # Meta made it through + assert t1["col0"].description == "col0" + assert t1["col1"].description == "col1" + assert t1["col0"].meta["val"] == "val0" + assert t1["col1"].meta["val"] == "val1" + + # Need to de-fang the automatic unicode sandwiching of Table + assert np.array(t1["col0"])[0] == byt + assert np.array(t1["col1"])[0] == byt + assert np.array(t1["col2"])[0] == 1 + + # Bytestring to unicode + t1 = t.copy() + t1.convert_bytestring_to_unicode() + assert t1["col0"].dtype.kind == "U" + assert t1["col1"].dtype.kind == "U" + assert t1["col2"].dtype.kind == "i" + + # Meta made it through + assert t1["col0"].description == "col0" + assert t1["col1"].description == "col1" + assert t1["col0"].meta["val"] == "val0" + assert t1["col1"].meta["val"] == "val1" + + # No need to de-fang the automatic unicode sandwiching of Table here, but + # do just for consistency to prove things are working. + assert np.array(t1["col0"])[0] == uni + assert np.array(t1["col1"])[0] == uni + assert np.array(t1["col2"])[0] == 1 + + +def test_table_deletion(): + """ + Regression test for the reference cycle discussed in + https://github.com/astropy/astropy/issues/2877 + """ + + deleted = set() + + # A special table subclass which leaves a record when it is finalized + class TestTable(table.Table): + def __del__(self): + deleted.add(id(self)) + + t = TestTable({"a": [1, 2, 3]}) + the_id = id(t) + assert t["a"].parent_table is t + + del t + + # Cleanup + gc.collect() + + assert the_id in deleted + + +def test_nested_iteration(): + """ + Regression test for issue 3358 where nested iteration over a single table fails. + """ + t = table.Table([[0, 1]], names=["a"]) + out = [] + for r1 in t: + for r2 in t: + out.append((r1["a"], r2["a"])) + assert out == [(0, 0), (0, 1), (1, 0), (1, 1)] + + +def test_table_init_from_degenerate_arrays(table_types): + t = table_types.Table(np.array([])) + assert len(t.columns) == 0 + + with pytest.raises(ValueError): + t = table_types.Table(np.array(0)) + + t = table_types.Table(np.array([1, 2, 3])) + assert len(t.columns) == 3 + + +@pytest.mark.usefixtures("table_types") +class TestReplaceColumn(SetupData): + def test_fail_replace_column(self, table_types): + """Raise exception when trying to replace column via table.columns object""" + self._setup(table_types) + t = table_types.Table([self.a, self.b]) + + with pytest.raises( + ValueError, + match=r"Cannot replace column 'a'. Use Table.replace_column.. instead.", + ): + t.columns["a"] = [1, 2, 3] + + with pytest.raises( + ValueError, match=r"column name not there is not in the table" + ): + t.replace_column("not there", [1, 2, 3]) + + with pytest.raises( + ValueError, match=r"length of new column must match table length" + ): + t.replace_column("a", [1, 2]) + + def test_replace_column(self, table_types): + """Replace existing column with a new column""" + self._setup(table_types) + t = table_types.Table([self.a, self.b]) + ta = t["a"] + tb = t["b"] + + vals = [1.2, 3.4, 5.6] + for col in ( + vals, + table_types.Column(vals), + table_types.Column(vals, name="a"), + table_types.Column(vals, name="b"), + ): + t.replace_column("a", col) + assert np.all(t["a"] == vals) + assert t["a"] is not ta # New a column + assert t["b"] is tb # Original b column unchanged + assert t.colnames == ["a", "b"] + assert t["a"].meta == {} + assert t["a"].format is None + + # Special case: replacing the only column can resize table + del t["b"] + assert len(t) == 3 + t["a"] = [1, 2] + assert len(t) == 2 + + def test_replace_index_column(self, table_types): + """Replace index column and generate expected exception""" + self._setup(table_types) + t = table_types.Table([self.a, self.b]) + t.add_index("a") + + with pytest.raises(ValueError) as err: + t.replace_column("a", [1, 2, 3]) + assert err.value.args[0] == "cannot replace a table index column" + + def test_replace_column_no_copy(self): + t = Table([[1, 2], [3, 4]], names=["a", "b"]) + a = np.array([1.5, 2.5]) + t.replace_column("a", a, copy=False) + assert t["a"][0] == a[0] + t["a"][0] = 10 + assert t["a"][0] == a[0] + + +class TestQTableColumnConversionCornerCases: + def test_replace_with_masked_col_with_units_in_qtable(self): + """This is a small regression from #8902""" + t = QTable([[1, 2], [3, 4]], names=["a", "b"]) + t["a"] = MaskedColumn([5, 6], unit="m") + assert isinstance(t["a"], u.Quantity) + + def test_do_not_replace_string_column_with_units_in_qtable(self): + t = QTable([[1 * u.m]]) + with pytest.warns(AstropyUserWarning, match="convert it to Quantity failed"): + t["a"] = Column(["a"], unit=u.m) + assert isinstance(t["a"], Column) + + +class Test__Astropy_Table__: + """ + Test initializing a Table subclass from a table-like object that + implements the __astropy_table__ interface method. + """ + + class SimpleTable: + def __init__(self): + self.columns = [[1, 2, 3], [4, 5, 6], [7, 8, 9] * u.m] + self.names = ["a", "b", "c"] + self.meta = OrderedDict([("a", 1), ("b", 2)]) + + def __astropy_table__(self, cls, copy, **kwargs): + a, b, c = self.columns + c.info.name = "c" + cols = [table.Column(a, name="a"), table.MaskedColumn(b, name="b"), c] + names = [col.info.name for col in cols] + return cls(cols, names=names, copy=copy, meta=kwargs or self.meta) + + def test_simple_1(self): + """Make a SimpleTable and convert to Table, QTable with copy=False, True""" + for table_cls in (table.Table, table.QTable): + col_c_class = u.Quantity if table_cls is table.QTable else table.Column + for cpy in (False, True): + st = self.SimpleTable() + # Test putting in a non-native kwarg `extra_meta` to Table initializer + t = table_cls(st, copy=cpy, extra_meta="extra!") + assert t.colnames == ["a", "b", "c"] + assert t.meta == {"extra_meta": "extra!"} + assert np.all(t["a"] == st.columns[0]) + assert np.all(t["b"] == st.columns[1]) + vals = t["c"].value if table_cls is table.QTable else t["c"] + assert np.all(st.columns[2].value == vals) + + assert isinstance(t["a"], table.Column) + assert isinstance(t["b"], table.MaskedColumn) + assert isinstance(t["c"], col_c_class) + assert t["c"].unit is u.m + assert type(t) is table_cls + + # Copy being respected? + t["a"][0] = 10 + assert st.columns[0][0] == 1 if cpy else 10 + + def test_simple_2(self): + """Test converting a SimpleTable and changing column names and types""" + st = self.SimpleTable() + dtypes = [np.int32, np.float32, np.float16] + names = ["a", "b", "c"] + meta = OrderedDict([("c", 3)]) + t = table.Table(st, dtype=dtypes, names=names, meta=meta) + assert t.colnames == names + assert all( + col.dtype.type is dtype for col, dtype in zip(t.columns.values(), dtypes) + ) + + # The supplied meta is overrides the existing meta. Changed in astropy 3.2. + assert t.meta != st.meta + assert t.meta == meta + + def test_kwargs_exception(self): + """If extra kwargs provided but without initializing with a table-like + object, exception is raised""" + with pytest.raises(TypeError) as err: + table.Table([[1]], extra_meta="extra!") + assert "__init__() got unexpected keyword argument" in str(err.value) + + +class TestUpdate: + def _setup(self): + self.a = Column((1, 2, 3), name="a") + self.b = Column((4, 5, 6), name="b") + self.c = Column((7, 8, 9), name="c") + self.d = Column((10, 11, 12), name="d") + + def test_different_lengths(self): + self._setup() + t1 = Table([self.a]) + t2 = Table([self.b[:-1]]) + msg = "Inconsistent data column lengths" + with pytest.raises(ValueError, match=msg): + t1.update(t2) + # If update didn't succeed then t1 and t2 should not have changed. + assert t1.colnames == ["a"] + assert np.all(t1["a"] == self.a) + assert t2.colnames == ["b"] + assert np.all(t2["b"] == self.b[:-1]) + + def test_invalid_inputs(self): + # If input is invalid then nothing should be modified. + self._setup() + t = Table([self.a]) + d = {"b": self.b, "c": [0]} + msg = "Inconsistent data column lengths: {1, 3}" + with pytest.raises(ValueError, match=msg): + t.update(d) + assert t.colnames == ["a"] + assert np.all(t["a"] == self.a) + assert d == {"b": self.b, "c": [0]} + + def test_metadata_conflict(self): + self._setup() + t1 = Table([self.a], meta={"a": 0, "b": [0], "c": True}) + t2 = Table([self.b], meta={"a": 1, "b": [1]}) + t2meta = copy.deepcopy(t2.meta) + t1.update(t2) + assert t1.meta == {"a": 1, "b": [0, 1], "c": True} + # t2 metadata should not have changed. + assert t2.meta == t2meta + + def test_update(self): + self._setup() + t1 = Table([self.a, self.b]) + t2 = Table([self.b, self.c]) + t2["b"] += 1 + t1.update(t2) + assert t1.colnames == ["a", "b", "c"] + assert np.all(t1["a"] == self.a) + assert np.all(t1["b"] == self.b + 1) + assert np.all(t1["c"] == self.c) + # t2 should not have changed. + assert t2.colnames == ["b", "c"] + assert np.all(t2["b"] == self.b + 1) + assert np.all(t2["c"] == self.c) + + d = {"b": list(self.b), "d": list(self.d)} + dc = copy.deepcopy(d) + t2.update(d) + assert t2.colnames == ["b", "c", "d"] + assert np.all(t2["b"] == self.b) + assert np.all(t2["c"] == self.c) + assert np.all(t2["d"] == self.d) + # d should not have changed. + assert d == dc + + # Columns were copied, so changing t2 shouldn't have affected t1. + assert t1.colnames == ["a", "b", "c"] + assert np.all(t1["a"] == self.a) + assert np.all(t1["b"] == self.b + 1) + assert np.all(t1["c"] == self.c) + + def test_update_without_copy(self): + self._setup() + t1 = Table([self.a, self.b]) + t2 = Table([self.b, self.c]) + t1.update(t2, copy=False) + t2["b"] -= 1 + assert t1.colnames == ["a", "b", "c"] + assert np.all(t1["a"] == self.a) + assert np.all(t1["b"] == self.b - 1) + assert np.all(t1["c"] == self.c) + + d = {"b": np.array(self.b), "d": np.array(self.d)} + t2.update(d, copy=False) + d["b"] *= 2 + assert t2.colnames == ["b", "c", "d"] + assert np.all(t2["b"] == 2 * self.b) + assert np.all(t2["c"] == self.c) + assert np.all(t2["d"] == self.d) + + def test_merge_operator(self): + self._setup() + t1 = Table([self.a, self.b]) + t2 = Table([self.b, self.c]) + with pytest.raises(TypeError): + _ = 1 | t1 + with pytest.raises(TypeError): + _ = t1 | 1 + + t1_copy = t1.copy(True) + t3 = t1 | t2 + assert t1.colnames == ["a", "b"] # t1 should remain unchanged + assert np.all(t1["a"] == self.a) + assert np.all(t1["b"] == self.b) + + t1_copy.update(t2) + assert t3.colnames == ["a", "b", "c"] + assert np.all(t3["a"] == t1_copy["a"]) + assert np.all(t3["b"] == t1_copy["b"]) + assert np.all(t3["c"] == t1_copy["c"]) + + def test_update_operator(self): + self._setup() + t1 = Table([self.a, self.b]) + t2 = Table([self.b, self.c]) with pytest.raises(ValueError): - np_data = np.array(d, dtype=[('c', 'i8'), ('d', 'i8')]) + t1 |= 1 + + t1_copy = t1.copy(True) + t1 |= t2 + t1_copy.update(t2) + assert t1.colnames == ["a", "b", "c"] + assert np.all(t1["a"] == t1_copy["a"]) + assert np.all(t1["b"] == t1_copy["b"]) + assert np.all(t1["c"] == t1_copy["c"]) + + +@pytest.mark.parametrize( + "name,expected", + [ + pytest.param("a", [1, 2], id="existing_column"), + pytest.param("d", [9, 6], id="new_column"), + ], +) +def test_table_setdefault(name, expected): + t = table.table_helpers.simple_table(2) + np.testing.assert_array_equal(t.setdefault(name, [9, 6]), expected) + np.testing.assert_array_equal(t[name], expected) + assert name in t.columns + assert type(t[name]) is Column + + +def test_table_setdefault_wrong_shape(): + t = table.table_helpers.simple_table(2) + with pytest.raises(ValueError, match="^Inconsistent data column lengths$"): + t.setdefault("f", [1, 2, 3]) + assert "f" not in t.columns + + +@pytest.mark.parametrize("value", ([9], [9, 6]), ids=lambda x: f"len_{len(x)}_default") +def test_empty_table_setdefault(value): + t = Table() + np.testing.assert_array_equal(t.setdefault("a", value), value) + np.testing.assert_array_equal(t["a"], value) + assert t.colnames == ["a"] + assert type(t["a"]) is Column + + +def test_empty_table_setdefault_scalar(): + t = Table() + t.setdefault("a", 9) + assert len(t) == 0 + assert t.colnames == ["a"] + assert type(t["a"]) is Column + assert t["a"].dtype == int + + +def test_table_meta_copy(): + """ + Test no copy vs light (key) copy vs deep copy of table meta for different + situations. #8404. + """ + t = table.Table([[1]]) + meta = {1: [1, 2]} + + # Assigning meta directly implies using direct object reference + t.meta = meta + assert t.meta is meta + + # Table slice implies key copy, so values are unchanged + t2 = t[:] + assert t2.meta is not t.meta # NOT the same OrderedDict object but equal + assert t2.meta == t.meta + assert t2.meta[1] is t.meta[1] # Value IS the list same object + + # Table init with copy=False implies key copy + t2 = table.Table(t, copy=False) + assert t2.meta is not t.meta # NOT the same OrderedDict object but equal + assert t2.meta == t.meta + assert t2.meta[1] is t.meta[1] # Value IS the same list object + + # Table init with copy=True implies deep copy + t2 = table.Table(t, copy=True) + assert t2.meta is not t.meta # NOT the same OrderedDict object but equal + assert t2.meta == t.meta + assert t2.meta[1] is not t.meta[1] # Value is NOT the same list object + + +def test_table_meta_copy_with_meta_arg(): + """ + Test no copy vs light (key) copy vs deep copy of table meta when meta is + supplied as a table init argument. #8404. + """ + meta = {1: [1, 2]} + meta2 = {2: [3, 4]} + t = table.Table([[1]], meta=meta, copy=False) + assert t.meta is meta + + t = table.Table([[1]], meta=meta) # default copy=True + assert t.meta is not meta + assert t.meta == meta + + # Test initializing from existing table with meta with copy=False + t2 = table.Table(t, meta=meta2, copy=False) + assert t2.meta is meta2 + assert t2.meta != t.meta # Change behavior in #8404 + + # Test initializing from existing table with meta with default copy=True + t2 = table.Table(t, meta=meta2) + assert t2.meta is not meta2 + assert t2.meta != t.meta # Change behavior in #8404 + + # Table init with copy=True and empty dict meta gets that empty dict + t2 = table.Table(t, copy=True, meta={}) + assert t2.meta == {} + + # Table init with copy=True and kwarg meta=None gets the original table dict. + # This is a somewhat ambiguous case because it could be interpreted as the + # user wanting NO meta set on the output. This could be implemented by inspecting + # call args. + t2 = table.Table(t, copy=True, meta=None) + assert t2.meta == t.meta + + # Test initializing empty table with meta with copy=False + t = table.Table(meta=meta, copy=False) + assert t.meta is meta + assert t.meta[1] is meta[1] + + # Test initializing empty table with meta with default copy=True (deepcopy meta) + t = table.Table(meta=meta) + assert t.meta is not meta + assert t.meta == meta + assert t.meta[1] is not meta[1] + + +@pytest.mark.parametrize( + "data", + [np.array([object()]), [object()]], +) +def test_deepcopy_object_column(data): + # see https://github.com/astropy/astropy/issues/13435 + t1 = Table({"a": data}, meta={"test": object()}) + t2 = copy.deepcopy(t1) + c1 = t1["a"] + c2 = t2["a"] + assert c2 is not c1 + assert c2[0] is not c1[0] + assert t2.meta["test"] is not t1.meta["test"] + + t3 = Table(t1, copy=True) + c3 = t3["a"] + assert c3 is not c1 + assert c3[0] is c1[0] + assert t3.meta["test"] is not t1.meta["test"] + + +def test_replace_column_qtable(): + """Replace existing Quantity column with a new column in a QTable""" + a = [1, 2, 3] * u.m + b = [4, 5, 6] + t = table.QTable([a, b], names=["a", "b"]) + + ta = t["a"] + tb = t["b"] + ta.info.meta = {"aa": [0, 1, 2, 3, 4]} + ta.info.format = "%f" + + t.replace_column("a", a.to("cm")) + assert np.all(t["a"] == ta) + assert t["a"] is not ta # New a column + assert t["b"] is tb # Original b column unchanged + assert t.colnames == ["a", "b"] + assert t["a"].info.meta is None + assert t["a"].info.format is None + + +def test_replace_update_column_via_setitem(): + """ + Test table update like ``t['a'] = value``. This leverages off the + already well-tested ``replace_column`` and in-place update + ``t['a'][:] = value``, so this testing is fairly light. + """ + a = [1, 2] * u.m + b = [3, 4] + t = table.QTable([a, b], names=["a", "b"]) + assert isinstance(t["a"], u.Quantity) + + # Inplace update + ta = t["a"] + t["a"] = 5 * u.m + assert np.all(t["a"] == [5, 5] * u.m) + assert t["a"] is ta + + # Replace + t["a"] = [5, 6] + assert np.all(t["a"] == [5, 6]) + assert isinstance(t["a"], table.Column) + assert t["a"] is not ta + + +def test_replace_update_column_via_setitem_warnings_normal(): + """ + Test warnings related to table replace change in #5556: + Normal warning-free replace + """ + t = table.Table([[1, 2, 3], [4, 5, 6]], names=["a", "b"]) + with table.conf.set_temp("replace_warnings", ["refcount", "attributes", "slice"]): + t["a"] = 0 # in-place update + t["a"] = [10, 20, 30] # replace column + + +def test_replace_update_column_via_setitem_warnings_slice(): + """ + Test warnings related to table replace change in #5556: + Replace a slice, one warning. + """ + t = table.Table([[1, 2, 3], [4, 5, 6]], names=["a", "b"]) + with table.conf.set_temp("replace_warnings", ["refcount", "attributes", "slice"]): + t2 = t[:2] + + t2["a"] = 0 # in-place slice update + assert np.all(t["a"] == [0, 0, 3]) + + with pytest.warns( + TableReplaceWarning, + match="replaced column 'a' which looks like an array slice", + ) as w: + t2["a"] = [10, 20] # replace slice + assert len(w) == 1 + + +def test_replace_update_column_via_setitem_warnings_attributes(): + """ + Test warnings related to table replace change in #5556: + Lost attributes. + """ + t = table.Table([[1, 2, 3], [4, 5, 6]], names=["a", "b"]) + t["a"].unit = "m" + + with pytest.warns( + TableReplaceWarning, + match=r"replaced column 'a' and column attributes \['unit'\]", + ) as w: + with table.conf.set_temp( + "replace_warnings", ["refcount", "attributes", "slice"] + ): + t["a"] = [10, 20, 30] + assert len(w) == 1 + + +def test_replace_update_column_via_setitem_warnings_refcount(): + """ + Test warnings related to table replace change in #5556: + Reference count changes. + """ + t = table.Table([[1, 2, 3], [4, 5, 6]], names=["a", "b"]) + ta = t["a"] # Generate an extra reference to original column + + with pytest.warns( + TableReplaceWarning, match="replaced column 'a' and the number of references" + ) as w: + with table.conf.set_temp( + "replace_warnings", ["refcount", "attributes", "slice"] + ): + t["a"] = [10, 20, 30] + assert len(w) == 1 + + +def test_replace_update_column_via_setitem_warnings_always(): + """ + Test warnings related to table replace change in #5556: + Test 'always' setting that raises warning for any replace. + """ + t = table.Table([[1, 2, 3], [4, 5, 6]], names=["a", "b"]) + + with table.conf.set_temp("replace_warnings", ["always"]): + t["a"] = 0 # in-place slice update + + with pytest.warns(TableReplaceWarning, match="replaced column 'a'") as w: + frameinfo = getframeinfo(currentframe()) + t["a"] = [10, 20, 30] # replace column + assert len(w) == 1 + + # Make sure the warning points back to the user code line + assert w[0].lineno == frameinfo.lineno + 1 + assert "test_table" in w[0].filename + + +def test_replace_update_column_via_setitem_replace_inplace(): + """ + Test the replace_inplace config option related to #5556. In this + case no replace is done. + """ + t = table.Table([[1, 2, 3], [4, 5, 6]], names=["a", "b"]) + ta = t["a"] + t["a"].unit = "m" + + with table.conf.set_temp("replace_inplace", True): + with table.conf.set_temp( + "replace_warnings", ["always", "refcount", "attributes", "slice"] + ): + t["a"] = 0 # in-place update + assert ta is t["a"] + + t["a"] = [10, 20, 30] # normally replaces column, but not now + assert ta is t["a"] + assert np.all(t["a"] == [10, 20, 30]) + + +def test_primary_key_is_inherited(): + """Test whether a new Table inherits the primary_key attribute from + its parent Table. Issue #4672""" + + t = table.Table([(2, 3, 2, 1), (8, 7, 6, 5)], names=("a", "b")) + t.add_index("a") + original_key = t.primary_key + + # can't test if tuples are equal, so just check content + assert original_key[0] == "a" + + t2 = t[:] + t3 = t.copy() + t4 = table.Table(t) + + # test whether the reference is the same in the following + assert original_key == t2.primary_key + assert original_key == t3.primary_key + assert original_key == t4.primary_key + + # just test one element, assume rest are equal if assert passes + assert t.loc[1] == t2.loc[1] + assert t.loc[1] == t3.loc[1] + assert t.loc[1] == t4.loc[1] + + +def test_qtable_read_for_ipac_table_with_char_columns(): + """Test that a char column of a QTable is assigned no unit and not + a dimensionless unit, otherwise conversion of reader output to + QTable fails.""" + t1 = table.QTable([["A"]], names="B") + out = StringIO() + t1.write(out, format="ascii.ipac") + t2 = table.QTable.read(out.getvalue(), format="ascii.ipac", guess=False) + assert t2["B"].unit is None + + +def test_create_table_from_final_row(): + """Regression test for issue #8422: passing the last row of a table into + Table should return a new table containing that row.""" + t1 = table.Table([(1, 2)], names=["col"]) + row = t1[-1] + t2 = table.Table(row)["col"] + assert t2[0] == 2 + + +def test_key_values_in_as_array(): + # Test for checking column slicing using key_values in Table.as_array() + data_rows = [(1, 2.0, "x"), (4, 5.0, "y"), (5, 8.2, "z")] + # Creating a table with three columns + t1 = table.Table( + rows=data_rows, + names=("a", "b", "c"), + meta={"name": "first table"}, + dtype=("i4", "f8", "S1"), + ) + # Values of sliced column a,b is stored in a numpy array + a = np.array([(1, 2.0), (4, 5.0), (5, 8.2)], dtype=[("a", "" + + t = MyTable([[1, 2]]) + # __attributes__ created on the fly on the first access of an attribute + # that has a non-None default. + assert "__attributes__" not in t.meta + assert t.foo is None + assert "__attributes__" not in t.meta + assert t.baz == 1 + assert "__attributes__" in t.meta + t.bar.append(2.0) + assert t.bar == [2.0] + assert t.baz == 1 + + t.baz = "baz" + assert t.baz == "baz" + + # Table attributes round-trip through pickle + tp = pickle.loads(pickle.dumps(t)) + assert tp.foo is None + assert tp.baz == "baz" + assert tp.bar == [2.0] + + # Allow initialization of attributes in table creation, with / without data + for data in None, [[1, 2]]: + t2 = MyTable(data, foo=3, bar="bar", baz="baz") + assert t2.foo == 3 + assert t2.bar == "bar" + assert t2.baz == "baz" + + # Initializing from an existing MyTable works, with and without kwarg attrs + t3 = MyTable(t2) + assert t3.foo == 3 + assert t3.bar == "bar" + assert t3.baz == "baz" + + t3 = MyTable(t2, foo=5, bar="fubar") + assert t3.foo == 5 + assert t3.bar == "fubar" + assert t3.baz == "baz" + + # Deleting attributes removes it from attributes + del t.baz + assert "baz" not in t.meta["__attributes__"] + del t.bar + assert "__attributes__" not in t.meta + + +def test_table_attribute_ecsv(): + # Table attribute round-trip through ECSV + t = MyTable([[1, 2]], bar=[2.0], baz="baz") + out = StringIO() + t.write(out, format="ascii.ecsv") + t2 = MyTable.read(out.getvalue(), format="ascii.ecsv") + assert t2.foo is None + assert t2.bar == [2.0] + assert t2.baz == "baz" + + +def test_table_attribute_fail(): + if sys.version_info[:2] >= (3, 12): + ctx = pytest.raises(ValueError, match=".* not allowed as TableAttribute") + else: + # Code raises ValueError(f'{attr} not allowed as TableAttribute') but in this + # context it gets re-raised as a RuntimeError during class definition. + ctx = pytest.raises(RuntimeError, match="Error calling __set_name__") + + with ctx: + + class MyTable2(Table): + descriptions = TableAttribute() # Conflicts with init arg + + with ctx: + + class MyTable3(Table): + colnames = TableAttribute() # Conflicts with built-in property + + +def test_set_units_and_descriptions(): + dat = [[1.0, 2.0], ["aa", "bb"]] + + # Wrong number of units should raise ValueError + with pytest.raises( + ValueError, match="sequence of unit values must match number of columns" + ): + Table(dat, units=[u.m]) + + # Now setting a unit for a non-existing column should NOT raise an error + t = Table( + dat, + names=["x", "y"], + units={"x": u.m, "c": u.kg}, + descriptions={"y": "Y", "d": "D"}, + ) + + assert t["x"].unit == u.m + assert t["y"].description == "Y" + + # Column 'c' and 'd' should not exist in the table + assert "c" not in t.colnames + assert "d" not in t.colnames + + +def test_set_units(): + dat = [[1.0, 2.0], ["aa", "bb"], [3, 4]] + exp_units = (u.m, None, None) + for cls in Table, QTable: + for units in ({"a": u.m, "c": ""}, exp_units): + qt = cls(dat, units=units, names=["a", "b", "c"]) + if cls is QTable: + assert isinstance(qt["a"], u.Quantity) + assert isinstance(qt["b"], table.Column) + assert isinstance(qt["c"], table.Column) + for col, unit in zip(qt.itercols(), exp_units): + assert col.info.unit is unit + + +def test_set_descriptions(): + dat = [[1.0, 2.0], ["aa", "bb"]] + exp_descriptions = ("my description", None) + for cls in Table, QTable: + for descriptions in ({"a": "my description"}, exp_descriptions): + qt = cls(dat, descriptions=descriptions, names=["a", "b"]) + for col, description in zip(qt.itercols(), exp_descriptions): + assert col.info.description == description + + +def test_set_units_from_row(): + text = ["a,b", ",s", "1,2", "3,4"] + units = Table.read(text, format="ascii", data_start=1, data_end=2)[0] + t = Table.read(text, format="ascii", data_start=2, units=units) + assert isinstance(units, table.Row) + assert t["a"].info.unit is None + assert t["b"].info.unit is u.s + + +def test_set_units_descriptions_read(): + """Test setting units and descriptions via Table.read. The test here + is less comprehensive because the implementation is exactly the same + as for Table.__init__ (calling Table._set_column_attribute)""" + for cls in Table, QTable: + t = cls.read( + ["a b", "1 2"], + format="ascii", + units=[u.m, u.s], + descriptions=["hi", "there"], + ) + assert t["a"].info.unit is u.m + assert t["b"].info.unit is u.s + assert t["a"].info.description == "hi" + assert t["b"].info.description == "there" + + +def test_broadcasting_8933(): + """Explicitly check re-work of code related to broadcasting in #8933""" + t = table.Table([[1, 2]]) # Length=2 table + t["a"] = [[3, 4]] # Can broadcast if ndim > 1 and shape[0] == 1 + t["b"] = 5 + t["c"] = [1] # Treat as broadcastable scalar, not length=1 array (which would fail) + assert np.all(t["a"] == [[3, 4], [3, 4]]) + assert np.all(t["b"] == [5, 5]) + assert np.all(t["c"] == [1, 1]) + + # Test that broadcasted column is writeable + t["c"][1] = 10 + assert np.all(t["c"] == [1, 10]) + + +def test_custom_masked_column_in_nonmasked_table(): + """Test the refactor and change in column upgrades introduced + in 95902650f. This fixes a regression introduced by #8789 + (Change behavior of Table regarding masked columns).""" + + class MyMaskedColumn(table.MaskedColumn): + pass + + class MySubMaskedColumn(MyMaskedColumn): + pass + + class MyColumn(table.Column): + pass + + class MySubColumn(MyColumn): + pass + + class MyTable(table.Table): + Column = MyColumn + MaskedColumn = MyMaskedColumn + + a = table.Column([1]) + b = table.MaskedColumn([2], mask=[True]) + c = MyMaskedColumn([3], mask=[True]) + d = MySubColumn([4]) + e = MySubMaskedColumn([5], mask=[True]) + + # Two different pathways for making table + t1 = MyTable([a, b, c, d, e], names=["a", "b", "c", "d", "e"]) + t2 = MyTable() + t2["a"] = a + t2["b"] = b + t2["c"] = c + t2["d"] = d + t2["e"] = e + + for t in (t1, t2): + assert type(t["a"]) is MyColumn + assert type(t["b"]) is MyMaskedColumn # upgrade + assert type(t["c"]) is MyMaskedColumn + assert type(t["d"]) is MySubColumn + assert type(t["e"]) is MySubMaskedColumn # sub-class not downgraded + + +def test_sort_with_mutable_skycoord(): + """Test sorting a table that has a mutable column such as SkyCoord. + + In this case the sort is done in-place + """ + t = Table([[2, 1], SkyCoord([4, 3], [6, 5], unit="deg,deg")], names=["a", "sc"]) + meta = {"a": [1, 2]} + ta = t["a"] + tsc = t["sc"] + t["sc"].info.meta = meta + t.sort("a") + assert np.all(t["a"] == [1, 2]) + assert np.allclose(t["sc"].ra.to_value(u.deg), [3, 4]) + assert np.allclose(t["sc"].dec.to_value(u.deg), [5, 6]) + assert t["a"] is ta + assert t["sc"] is tsc + + # Prior to astropy 4.1 this was a deep copy of SkyCoord column; after 4.1 + # it is a reference. + t["sc"].info.meta["a"][0] = 100 + assert meta["a"][0] == 100 + + +def test_sort_with_non_mutable(): + """Test sorting a table that has a non-mutable column.""" + t = Table([[2, 1], [3, 4]], names=["a", "b"]) + ta = t["a"] + tb = t["b"] + t["b"].setflags(write=False) + meta = {"a": [1, 2]} + t["b"].info.meta = meta + t.sort("a") + assert np.all(t["a"] == [1, 2]) + assert np.all(t["b"] == [4, 3]) + assert ta is t["a"] + assert tb is not t["b"] + + # Prior to astropy 4.1 this was a deep copy of SkyCoord column; after 4.1 + # it is a reference. + t["b"].info.meta["a"][0] = 100 + assert meta["a"][0] == 1 + + +def test_init_with_list_of_masked_arrays(): + """Test the fix for #8977""" + m0 = np.ma.array([0, 1, 2], mask=[True, False, True]) + m1 = np.ma.array([3, 4, 5], mask=[False, True, False]) + mc = [m0, m1] + + # Test _init_from_list + t = table.Table([mc], names=["a"]) + + # Test add_column + t["b"] = [m1, m0] + + assert t["a"].shape == (2, 3) + assert np.all(t["a"][0] == m0) + assert np.all(t["a"][1] == m1) + assert np.all(t["a"][0].mask == m0.mask) + assert np.all(t["a"][1].mask == m1.mask) + + assert t["b"].shape == (2, 3) + assert np.all(t["b"][0] == m1) + assert np.all(t["b"][1] == m0) + assert np.all(t["b"][0].mask == m1.mask) + assert np.all(t["b"][1].mask == m0.mask) + + +def test_data_to_col_convert_strategy(): + """Test the update to how data_to_col works (#8972), using the regression + example from #8971. + """ + t = table.Table([[0, 1]]) + t["a"] = 1 + t["b"] = np.int64(2) # Failed previously + assert np.all(t["a"] == [1, 1]) + assert np.all(t["b"] == [2, 2]) + + +def test_structured_masked_column(): + """Test that adding a masked ndarray with a structured dtype works""" + dtype = np.dtype([("z", "f8"), ("x", "f8"), ("y", "i4")]) + t = Table() + t["a"] = np.ma.array( + [ + (1, 2, 3), + (4, 5, 6), + ], + mask=[ + (False, False, True), + (False, True, False), + ], + dtype=dtype, + ) + assert np.all(t["a"]["z"].mask == [False, False]) + assert np.all(t["a"]["x"].mask == [False, True]) + assert np.all(t["a"]["y"].mask == [True, False]) + assert isinstance(t["a"], MaskedColumn) + + +def test_rows_with_mixins(): + """Test for #9165 to allow adding a list of mixin objects. + Also test for fix to #9357 where group_by() failed due to + mixin object not having info.indices set to []. + """ + tm = Time([1, 2], format="cxcsec") + q = [1, 2] * u.m + mixed1 = [1 * u.m, 2] # Mixed input, fails to convert to Quantity + mixed2 = [2, 1 * u.m] # Mixed input, not detected as potential mixin + rows = [ + (1, q[0], tm[0]), + (2, q[1], tm[1]), + ] + t = table.QTable(rows=rows) + t["a"] = [q[0], q[1]] + t["b"] = [tm[0], tm[1]] + t["m1"] = mixed1 + t["m2"] = mixed2 + + assert np.all(t["col1"] == q) + assert np.all(t["col2"] == tm) + assert np.all(t["a"] == q) + assert np.all(t["b"] == tm) + assert np.all(t["m1"][ii] == mixed1[ii] for ii in range(2)) + assert np.all(t["m2"][ii] == mixed2[ii] for ii in range(2)) + assert type(t["m1"]) is table.Column + assert t["m1"].dtype is np.dtype(object) + assert type(t["m2"]) is table.Column + assert t["m2"].dtype is np.dtype(object) + + # Ensure group_by() runs without failing for sortable columns. + # The columns 'm1', and 'm2' are object dtype and not sortable. + for name in ["col0", "col1", "col2", "a", "b"]: + t.group_by(name) + + # For good measure include exactly the failure in #9357 in which the + # list of Time() objects is in the Table initializer. + mjds = [Time(58000, format="mjd")] + t = Table([mjds, ["gbt"]], names=("mjd", "obs")) + t.group_by("obs") + + +def test_group_by_empty_table(): + # see https://github.com/astropy/astropy/issues/11884 + t = Table(names=["a", "b"]) + tg = t.group_by("a") + + assert len(tg.groups.indices) == 0 + + keys = tg.groups.keys + assert isinstance(keys, Table) + assert keys.colnames == ["a"] + assert len(keys) == 0 + + +def test_iterrows(): + dat = [ + (1, 2, 3), + (4, 5, 6), + (7, 8, 6), + ] + t = table.Table(rows=dat, names=("a", "b", "c")) + c_s = [] + a_s = [] + for c, a in t.iterrows("c", "a"): + a_s.append(a) + c_s.append(c) + assert np.all(t["a"] == a_s) + assert np.all(t["c"] == c_s) + + rows = list(t.iterrows()) + assert rows == dat + + with pytest.raises(ValueError, match="d is not a valid column name"): + t.iterrows("d") + + +def test_values_and_types(): + dat = [ + (1, 2, 3), + (4, 5, 6), + (7, 8, 6), + ] + t = table.Table(rows=dat, names=("a", "b", "c")) + assert isinstance(t.values(), type(OrderedDict().values())) + assert isinstance(t.columns.values(), type(OrderedDict().values())) + assert isinstance(t.columns.keys(), type(OrderedDict().keys())) + for i in t.values(): + assert isinstance(i, table.column.Column) + + +def test_items(): + dat = [ + (1, 2, 3), + (4, 5, 6), + (7, 8, 9), + ] + t = table.Table(rows=dat, names=("a", "b", "c")) + + assert isinstance(t.items(), type(OrderedDict({}).items())) + + for i in list(t.items()): + assert isinstance(i, tuple) + + +def test_read_write_not_replaceable(): + t = table.Table() + with pytest.raises(AttributeError): + t.read = "fake_read" + + with pytest.raises(AttributeError): + t.write = "fake_write" + + +def test_keep_columns_with_generator(): + # Regression test for #12529 + t = table.table_helpers.simple_table(1) + t.keep_columns(col for col in t.colnames if col == "a") + assert t.colnames == ["a"] + + +def test_remove_columns_with_generator(): + # Regression test for #12529 + t = table.table_helpers.simple_table(1) + t.remove_columns(col for col in t.colnames if col == "a") + assert t.colnames == ["b", "c"] + + +def test_keep_columns_invalid_names_messages(): + t = table.table_helpers.simple_table(1) + with pytest.raises(KeyError, match='column "d" does not exist'): + t.keep_columns(["c", "d"]) + with pytest.raises(KeyError, match="columns {'[de]', '[de]'} do not exist"): + t.keep_columns(["c", "d", "e"]) + + +def test_remove_columns_invalid_names_messages(): + t = table.table_helpers.simple_table(1) + with pytest.raises(KeyError, match='column "d" does not exist'): + t.remove_columns(["c", "d"]) + with pytest.raises(KeyError, match="columns {'[de]', '[de]'} do not exist"): + t.remove_columns(["c", "d", "e"]) + + +@pytest.mark.parametrize("path_type", ["str", "Path"]) +def test_read_write_tilde_path(path_type, home_is_tmpdir): + if path_type == "str": + test_file = os.path.join("~", "test.csv") + else: + test_file = pathlib.Path("~", "test.csv") + t1 = Table() + t1["a"] = [1, 2, 3] + t1.write(test_file) + t2 = Table.read(test_file) + assert np.all(t2["a"] == [1, 2, 3]) + # Ensure the data wasn't written to the literal tilde-prefixed path + assert not os.path.exists(test_file) + + +def test_add_list_order(): + t = Table() + names = list(map(str, range(20))) + array = np.empty((20, 1)) + t.add_columns(array, names=names) + assert t.colnames == names + + +def test_table_write_preserves_nulls(tmp_path): + """Ensures that upon writing a table, the fill_value attribute of a + masked (integer) column is correctly propagated into the TNULL parameter + in the FITS header""" + + # Could be anything except for 999999, which is the "default" fill_value + # for masked int arrays + NULL_VALUE = -1 + + # Create table with an integer MaskedColumn with custom fill_value + c1 = MaskedColumn( + name="a", + data=np.asarray([1, 2, 3], dtype=np.int32), + mask=[True, False, True], + fill_value=NULL_VALUE, + ) + t = Table([c1]) + + table_filename = tmp_path / "nultable.fits" + + # Write the table out with Table.write() + t.write(table_filename) + + # Open the output file, and check the TNULL parameter is NULL_VALUE + with fits.open(table_filename) as hdul: + header = hdul[1].header + + assert header["TNULL1"] == NULL_VALUE + + +def test_as_array_preserve_fill_value(): + """Ensures that Table.as_array propagates a MaskedColumn's fill_value to + the output array""" + + INT_FILL = 123 + FLOAT_FILL = 123.0 + STR_FILL = "xyz" + CMPLX_FILL = complex(3.14, 2.71) + + # set up a table with some columns with different data types + c_int = MaskedColumn(name="int", data=[1, 2, 3], fill_value=INT_FILL) + c_float = MaskedColumn(name="float", data=[1.0, 2.0, 3.0], fill_value=FLOAT_FILL) + c_str = MaskedColumn(name="str", data=["abc", "def", "ghi"], fill_value=STR_FILL) + c_cmplx = MaskedColumn( + name="cmplx", + data=[complex(1, 0), complex(0, 1), complex(1, 1)], + fill_value=CMPLX_FILL, + ) + + t = Table([c_int, c_float, c_str, c_cmplx]) + + tn = t.as_array() + + assert tn["int"].fill_value == INT_FILL + assert tn["float"].fill_value == FLOAT_FILL + assert tn["str"].fill_value == STR_FILL + assert tn["cmplx"].fill_value == CMPLX_FILL + + +def test_table_hasattr_iloc(): + """Regression test for astropy issues #15911 and #5973""" + t = Table({"a": [1, 2, 3]}) + + assert hasattr(t, "iloc") + assert hasattr(t, "loc") + + with pytest.raises(ValueError, match="for a table with indices"): + t.iloc[0] + + with pytest.raises(ValueError, match="for a table with indices"): + t.loc[0] + + +def test_table_columns_setdefault_deprecation(): + with pytest.warns( + AstropyDeprecationWarning, + match=( + r"^The t\.columns\.setdefault\(\) function is deprecated and may be " + "removed in a future version.\n" + r" Use t\.setdefault\(\) instead\.$" + ), + ): + Table().columns.setdefault("a", [0]) + + +def test_table_columns_update_deprecation(): + with pytest.warns( + AstropyDeprecationWarning, + match=( + r"^The t\.columns\.update\(\) function is deprecated and may be " + "removed in a future version.\n" + r" Use t\.update\(\) instead\.$" + ), + ): + Table().columns.update({"a": [0]}) + + +def test_qtable_with_explicit_units(): + # Regression test for gh-17047; the problem was that the dimensionless + # unit ended up being compared to np.ma.masked. See also + # astropy/units/tests/test_units.py::test_comparison_dimensionless_with_np_ma_masked + tt = QTable(data=[[1.0, 2.0, 3.0]], names=["weight"], units={"weight": u.one}) + assert tt["weight"].unit == u.dimensionless_unscaled + + +@pytest.mark.parametrize("empty_table", [True, False]) +def test_table_replace_column_with_scalar(empty_table): + # Regression test for bug mentioned in + # https://github.com/astropy/astropy/pull/17102#issuecomment-2386963846 + t = QTable() if empty_table else QTable([[5, 6, 7]], names=["0"]) + t["a"] = np.arange(3.0) + t["a"] = 5.0 + assert len(t) == 3 + assert t["a"].shape == (3,) + assert np.all(t["a"] == 5.0) + # Direct replacement should never work. + with pytest.raises(ValueError, match="cannot replace.*with a scalar"): + t.replace_column("a", 2) + + +@pytest.mark.parametrize( + "arr", [None, [], [[], []], (), ((), ()), np.array([]), np.array([[], []])] +) +@pytest.mark.parametrize("arg_type", ["rows", "data"]) +def test_table_create_no_rows_various_inputs(arg_type, arr): + kwargs = {arg_type: arr} + t = Table(names=["foo", "bar"], dtype=[int, int], **kwargs) + assert len(t) == 0 + assert t.colnames == ["foo", "bar"] + + +@pytest.mark.parametrize("arg_type", ["rows", "data"]) +def test_table_create_no_rows_recarray(arg_type): + arr = np.array([], dtype=[("foo", int), ("bar", int)]) + kwargs = {arg_type: arr} + t = Table(**kwargs) + assert len(t) == 0 + assert t.colnames == ["foo", "bar"] + + +def test_table_from_records_nd_quantity(): + """Regression test for #17930""" + + data = [ + {"q0d": 5 * u.m, "q1d": [1, 2, 3] * u.s, "q2d": [[0, 1, 2], [3, 4, 5]] * u.TeV}, + ] + + t = Table(data) + assert t["q0d"].unit == u.m + assert t["q1d"].unit == u.s + assert t["q2d"].unit == u.TeV + + +def test_meta_writes_npstr_ecsv(tmp_path): + """Regression test for #18235""" + t = Table(dict(a=[1, 2, 3], b=["a", "b", "c"])) + t.meta["foo"] = np.str_("hello") + table_filename = tmp_path / "foo.ecsv" + # Write the table out with Table.write() + t.write(table_filename, overwrite=True) + t2 = Table.read(table_filename) + assert t2.meta["foo"] == t.meta["foo"] diff --git a/astropy/table/tests/test_table_io.py b/astropy/table/tests/test_table_io.py deleted file mode 100644 index 54bdb2ef1163..000000000000 --- a/astropy/table/tests/test_table_io.py +++ /dev/null @@ -1,222 +0,0 @@ -from copy import copy - -import pytest -import numpy as np - -from ..io_registry import _readers, _writers, _identifiers -from .. import Table -from .. import io_registry - -_READERS_ORIGINAL = copy(_readers) -_WRITERS_ORIGINAL = copy(_writers) -_IDENTIFIERS_ORIGINAL = copy(_identifiers) - - -def setup_function(function): - _readers.clear() - _writers.clear() - _identifiers.clear() - - -def empty_reader(*args, **kwargs): - return Table() - - -def empty_writer(table, *args, **kwargs): - pass - - -def empty_identifier(args, kwargs): - return True - - -def test_get_reader_invalid(): - with pytest.raises(Exception) as exc: - io_registry.get_reader('test') - assert exc.value.args[0] == "No reader defined for format 'test'" - - -def test_get_writer_invalid(): - with pytest.raises(Exception) as exc: - io_registry.get_writer('test') - assert exc.value.args[0] == "No writer defined for format 'test'" - - -def test_register_reader(): - io_registry.register_reader('test1', empty_reader) - io_registry.register_reader('test2', empty_reader) - assert io_registry.get_reader('test1') == empty_reader - assert io_registry.get_reader('test2') == empty_reader - - -def test_register_writer(): - io_registry.register_writer('test1', empty_writer) - io_registry.register_writer('test2', empty_writer) - assert io_registry.get_writer('test1') == empty_writer - assert io_registry.get_writer('test2') == empty_writer - - -def test_register_identifier(): - io_registry.register_identifier('test1', empty_identifier) - io_registry.register_identifier('test2', empty_identifier) - - -def test_register_reader_invalid(): - io_registry.register_reader('test', empty_reader) - with pytest.raises(Exception) as exc: - io_registry.register_reader('test', empty_reader) - assert exc.value.args[0] == "Reader for format test is already defined" - - -def test_register_writer_invalid(): - io_registry.register_writer('test', empty_writer) - with pytest.raises(Exception) as exc: - io_registry.register_writer('test', empty_writer) - assert exc.value.args[0] == "Writer for format test is already defined" - - -def test_register_identifier_invalid(): - io_registry.register_identifier('test', empty_identifier) - with pytest.raises(Exception) as exc: - io_registry.register_identifier('test', empty_identifier) - assert exc.value.args[0] == "Identifier for format test is already defined" - - -def test_register_reader_force(): - io_registry.register_reader('test', empty_reader) - io_registry.register_reader('test', empty_reader, force=True) - - -def test_register_writer_force(): - io_registry.register_writer('test', empty_writer) - io_registry.register_writer('test', empty_writer, force=True) - - -def test_register_identifier_force(): - io_registry.register_identifier('test', empty_identifier) - io_registry.register_identifier('test', empty_identifier, force=True) - - -def test_read_noformat(): - with pytest.raises(Exception) as exc: - Table.read() - assert exc.value.args[0] == "Format could not be identified" - - -def test_write_noformat(): - with pytest.raises(Exception) as exc: - Table().write() - assert exc.value.args[0] == "Format could not be identified" - - -def test_read_toomanyformats(): - io_registry.register_identifier('test1', lambda o, x, y: True) - io_registry.register_identifier('test2', lambda o, x, y: True) - with pytest.raises(Exception) as exc: - Table.read() - assert exc.value.args[0] == "Format is ambiguous - options are: test1, test2" - - -def test_write_toomanyformats(): - io_registry.register_identifier('test1', lambda o, x, y: True) - io_registry.register_identifier('test2', lambda o, x, y: True) - with pytest.raises(Exception) as exc: - Table().write() - assert exc.value.args[0] == "Format is ambiguous - options are: test1, test2" - - -def test_read_format_noreader(): - with pytest.raises(Exception) as exc: - Table.read(format='test') - assert exc.value.args[0] == "No reader defined for format 'test'" - - -def test_write_format_nowriter(): - with pytest.raises(Exception) as exc: - Table().write(format='test') - assert exc.value.args[0] == "No writer defined for format 'test'" - - -def test_read_identifier(): - - io_registry.register_identifier('test1', lambda o, x, y: x[0].startswith('a')) - io_registry.register_identifier('test2', lambda o, x, y: x[0].startswith('b')) - - # Now check that we got past the identifier and are trying to get - # the reader. The io_registry.get_reader will fail but the error message will - # tell us if the identifier worked. - - with pytest.raises(Exception) as exc: - Table.read('abc') - assert exc.value.args[0] == "No reader defined for format 'test1'" - - with pytest.raises(Exception) as exc: - Table.read('bac') - assert exc.value.args[0] == "No reader defined for format 'test2'" - - -def test_write_identifier(): - - io_registry.register_identifier('test1', lambda o, x, y: x[0].startswith('a')) - io_registry.register_identifier('test2', lambda o, x, y: x[0].startswith('b')) - - # Now check that we got past the identifier and are trying to get - # the reader. The io_registry.get_writer will fail but the error message will - # tell us if the identifier worked. - - with pytest.raises(Exception) as exc: - Table().write('abc') - assert exc.value.args[0] == "No writer defined for format 'test1'" - - with pytest.raises(Exception) as exc: - Table().write('bac') - assert exc.value.args[0] == "No writer defined for format 'test2'" - - -def test_identifier_origin(): - - io_registry.register_identifier('test1', lambda o, x, y: o == 'read') - io_registry.register_identifier('test2', lambda o, x, y: o == 'write') - io_registry.register_reader('test1', empty_reader) - io_registry.register_writer('test2', empty_writer) - - # There should not be too many formats defined - Table.read() - Table().write() - - with pytest.raises(Exception) as exc: - Table.read(format='test2') - assert exc.value.args[0] == "No reader defined for format 'test2'" - - with pytest.raises(Exception) as exc: - Table().write(format='test1') - assert exc.value.args[0] == "No writer defined for format 'test1'" - - -def test_read_valid_return(): - io_registry.register_reader('test', lambda: Table()) - t = Table.read(format='test') - assert isinstance(t, Table) - - -def test_read_invalid_return(): - io_registry.register_reader('test', lambda: 'spam') - with pytest.raises(TypeError) as exc: - Table.read(format='test') - assert exc.value.args[0] == "reader should return a Table instance" - - -def test_read_basic(): - data = np.array(zip([1, 2, 3], ['a', 'b', 'c']), dtype=[('A', int), ('B', '|S1')]) - io_registry.register_reader('test', lambda x: Table(x)) - t = Table.read(data, format='test') - assert t.keys() == ['A', 'B'] - for i in range(3): - assert t['A'][i] == data['A'][i] - assert t['B'][i] == data['B'][i] - - -def teardown_function(function): - _readers.update(_READERS_ORIGINAL) - _writers.update(_WRITERS_ORIGINAL) - _identifiers.update(_IDENTIFIERS_ORIGINAL) diff --git a/astropy/tests/__init__.py b/astropy/tests/__init__.py index 2728b287fc5d..9850ae21118f 100644 --- a/astropy/tests/__init__.py +++ b/astropy/tests/__init__.py @@ -4,5 +4,3 @@ for writing tests, and general tests that are not associated with a particular package. """ - -from . import helper diff --git a/astropy/tests/coveragerc b/astropy/tests/coveragerc deleted file mode 100644 index f18e24e83a49..000000000000 --- a/astropy/tests/coveragerc +++ /dev/null @@ -1,30 +0,0 @@ -[run] -source = astropy -omit = - astropy/__init__* - astropy/conftest.py - astropy/*setup* - astropy/*tests/* - astropy/extern/* - astropy/sphinx/* - astropy/utils/compat/* - astropy/version* - astropy/wcs/docstrings* - -[report] -exclude_lines = - # Have to re-enable the standard pragma - pragma: no cover - - # Don't complain about packages we have installed - except ImportError - - # Don't complain if tests don't hit assertions - raise AssertionError - raise NotImplementedError - - # Don't complain about script hooks - def main\(.*\): - - # Ignore branches that don't pertain to this version of Python - pragma: py{ignore_python_version} \ No newline at end of file diff --git a/astropy/tests/figures/__init__.py b/astropy/tests/figures/__init__.py new file mode 100644 index 000000000000..6a6b61b103f7 --- /dev/null +++ b/astropy/tests/figures/__init__.py @@ -0,0 +1,3 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +from .helpers import figure_test diff --git a/astropy/tests/figures/helpers.py b/astropy/tests/figures/helpers.py new file mode 100644 index 000000000000..db6a6f442d49 --- /dev/null +++ b/astropy/tests/figures/helpers.py @@ -0,0 +1,46 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +from functools import wraps + +import pytest + +from astropy.utils.compat.optional_deps import HAS_PYTEST_MPL + + +def figure_test(*args, **kwargs): + """ + A decorator that defines a figure test. + + This automatically decorates tests with mpl_image_compare with common + options used by all figure tests in astropy, and also adds the decorator + to allow remote data to be accessed. + """ + # NOTE: the savefig_kwargs option below is to avoid using PNG files with + # the matplotlib version embedded since this changes for every developer + # version. + + tolerance = kwargs.pop("tolerance", 0) + style = kwargs.pop("style", {}) + savefig_kwargs = kwargs.pop("savefig_kwargs", {}) + savefig_kwargs["metadata"] = {"Software": None} + + def decorator(test_function): + @pytest.mark.remote_data + @pytest.mark.mpl_image_compare( + tolerance=tolerance, style=style, savefig_kwargs=savefig_kwargs, **kwargs + ) + @pytest.mark.skipif( + not HAS_PYTEST_MPL, reason="pytest-mpl is required for the figure tests" + ) + @wraps(test_function) + def test_wrapper(*args, **kwargs): + return test_function(*args, **kwargs) + + return test_wrapper + + # If the decorator was used without any arguments, the only positional + # argument will be the test to decorate so we do the following: + if len(args) == 1: + return decorator(*args) + + return decorator diff --git a/astropy/tests/figures/py311-test-image-mpl380-cov.json b/astropy/tests/figures/py311-test-image-mpl380-cov.json new file mode 100644 index 000000000000..2fccead9d1fa --- /dev/null +++ b/astropy/tests/figures/py311-test-image-mpl380-cov.json @@ -0,0 +1,62 @@ +{ + "astropy.visualization.wcsaxes.tests.test_frame.TestFrame.test_custom_frame": "cceb87beabe25ac4187b2ade40b1d4af551dbb77a35f71b59d41cbfb85a2c769", + "astropy.visualization.wcsaxes.tests.test_frame.TestFrame.test_update_clip_path_rectangular": "30e13643c770a26b2707745143f50a735daa6f37a6a8258e733ae35338a4a1bb", + "astropy.visualization.wcsaxes.tests.test_frame.TestFrame.test_update_clip_path_nonrectangular": "37de34740cef2897effc9de5e6726ef3955a3449d0fa6a929350301500557b8e", + "astropy.visualization.wcsaxes.tests.test_frame.TestFrame.test_update_clip_path_change_wcs": "c68e961f0a21cc0dcc43c523cee1c564d94bd96c795f976d51e5198fc52f83cc", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_tight_layout": "80f167d1d14c8c22f1ffc2f403951e60397a4dcf35b9ecfd3ebba297d3fe0ff7", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_image_plot": "4fe4c89d089e8f1f9584584826f25d3c884d4914f76a863e1a624f9ef2295b07", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_axes_off": "3580258468fc0ff0fa6d140299b79f1f05c16c3e222447de38228e01983fbdd1", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_axisbelow[True]": "5364e39cab14f28b08073b31cce3dae8501c0431b1a49985810b764902747c8e", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_axisbelow[False]": "ee1e46856a19ba472ae9ea4588c614fbbd1b3a4a0787ef40e33f991a8f79d6c0", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_axisbelow[line]": "516c3882bbaa31a0d241eec2c70aca45b27b44929662371718b10025735db772", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_contour_overlay": "8679305e8a8b5b3f5b896343f4e16dfc65e9289c31505e3b35ef773d7837c8ea", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_contourf_overlay": "fa928ab57291e8a128009320323ffb8955cd75d28b2a589074defb7b0889b3eb", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_overlay_features_image": "30d5562cb7a2484db0b898bc886253829de884fbf92bc0e6575f091438ec5f32", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_curvilinear_grid_patches_image": "ad0a985a324a73b5bea839a231f7537075306c874c279c6a6ea2cfe16529d815", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_cube_slice_image": "822d96a93eb4ab59b1a0095384bb315b025b481a18821b3c558e4a937c77f489", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_cube_slice_image_lonlat": "224f7e2f7d106ab028584bdc51c73296e277a211b7f3ed36a0452eb4842afa5b", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_plot_coord": "9352d254af8add2918f98fb8a8d6d220bb95ab8096a1c56584d6c80ff3d5de39", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_scatter_coord": "9352d254af8add2918f98fb8a8d6d220bb95ab8096a1c56584d6c80ff3d5de39", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_text_coord": "c6e7575becb6381f45fe549ea7415bd038778406e43b44e8a72f8c6bce82e466", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_plot_line": "74ce73bcbb1dd170ae36d38a1ad4a62c626517a84e05922dab0f989a12a27758", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_changed_axis_units": "056ec7123c67fa7ee6269bec47641bfe4932630a79d02029e71164b522fadacd", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_minor_ticks": "5224f8fa725901ff74a4558987ceaae0363f98894d2c6a12d8c557db500ff7fe", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_ticks_labels": "f4944296c37a6e8ea9d30126709da3567a25908c1665e8cad10e894dd87cd54d", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_no_ticks": "5836f6d36ce6ce89156b251055e5b00b6063a35bff4b7ca1b2da725da14a67ad", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_rcparams": "1af0c66ba343df4f03efc18e707f5b0c2a54dbce7351e0f9024154937a30b371", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_tick_angles": "11ef218080920301ada1ef9cea558599ca110ba49e3dc4e9dc547c013b87fcc7", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_tick_angles_non_square_axes": "2ffe1157db233bc80e0eda08a30b916cc56a8ff0a7f34c3a0a1b1037695fe1a5", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_set_coord_type": "ee5e873fd467292f7e76f0890051f33762b399fa3936357797e7338f80e914eb", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_ticks_regression": "d295b08d88c6f8cd0eb56f9aa9c1892b6e72e6819a0f5b6265f3a29acb0a1545", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_axislabels_regression": "431b65a14f107dd47c3ea56240b92e246ba25be682f6487c8e8565b78f1fc0c8", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_noncelestial_angular": "cafe2fe3ca40e358ad8e92040c698c1e7f2913968f226a12b635fc47c0acc063", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_patches_distortion": "704af668b56837628f11b3dedbba60c0d11fda2e896ddbba9dee0e8acb5b99d7", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_quadrangle": "0223dba7d207f37c644c5c0860f6ef18b11b8013438030c89d88d700ea2ed437", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_beam_shape_from_args": "5bb6436777ab08013e747807f7db670b068229d6fb2b6b250787e44eaac36dab", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_beam_shape_from_header": "d7309b91db792bd42fdccd53ebe0738611c7cec56adb2972f0d684d83c8c75f6", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_scalebar": "29931954f6c7c5131a63bdfabbe4c220ee255d2efde756e31977d13a3c69af98", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_elliptical_frame": "3aee6e8bcb0b1283993d6743683f13077aab179a85540e3dcb301fef8c66aaf7", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_hms_labels": "1047e944e3cb798a39702be44c9612cb27a26e4fd246a5cf0abe047a51985d5c", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_latex_labels": "22c900ff73a60d58fbc29ee61485cad76aa483e3c8efa89bffbdf6d660d0bf9f", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_tick_params": "78bdce5072b3e9ac87b7e76832f3fc889c115aef3f894004a50b7eeba0d32ebd", + "astropy.visualization.wcsaxes.tests.test_images.test_1d_plot_1d_wcs": "190a14699654f7eae01556013f4fee1bbcddd3b34e62e9e3d92a92f7ce9a7695", + "astropy.visualization.wcsaxes.tests.test_images.test_1d_plot_1d_wcs_format_unit": "e0eea94f0ca0e37e96d903c3f441ecf0eb20b6870bfbd8a3426ad3e8305d1cee", + "astropy.visualization.wcsaxes.tests.test_images.test_1d_plot_1d_wcs_get_transform": "73e65438281efd42cda9e459467b18f7695aa50589782d0ce0cfbd298b69cb14", + "astropy.visualization.wcsaxes.tests.test_images.test_1d_plot_2d_wcs_correlated": "c2fd3acaa4739844288e1ce23dcb0a70b71aaf190d85f4a9164e7ab90836a76b", + "astropy.visualization.wcsaxes.tests.test_images.test_1d_plot_1d_sliced_low_level_wcs[slices0-custom:pos.helioprojective.lon]": "60c967eef03cc2f038d7915f1b988dfa60852fa7b950b83c6d3f49054862d5d1", + "astropy.visualization.wcsaxes.tests.test_images.test_1d_plot_1d_sliced_low_level_wcs[slices1-custom:pos.helioprojective.lat]": "0a14e5153eb91e2a3a96501f02a21581effc7d6b7ae31e29a402a3abf30d5b12", + "astropy.visualization.wcsaxes.tests.test_images.test_1d_plot_put_varying_axis_on_bottom_lon[slices0-hpln]": "60c967eef03cc2f038d7915f1b988dfa60852fa7b950b83c6d3f49054862d5d1", + "astropy.visualization.wcsaxes.tests.test_images.test_1d_plot_put_varying_axis_on_bottom_lon[slices1-hplt]": "0a14e5153eb91e2a3a96501f02a21581effc7d6b7ae31e29a402a3abf30d5b12", + "astropy.visualization.wcsaxes.tests.test_images.test_allsky_labels_wrap": "8908818b526d4a506505da890eff47121b23ee41656dc15dff3e19f7d03094af", + "astropy.visualization.wcsaxes.tests.test_images.test_tickable_gridlines": "9d91bcf571af6dcc1425b6dfd76fb92e3e0180fb9fd0c852f9eba3fb06e079fc", + "astropy.visualization.wcsaxes.tests.test_images.test_overlay_nondegree_unit": "aa9b85520da54dc61d4db2988883ff184fb59cd27c52a04899cb33599fcab4b7", + "astropy.visualization.wcsaxes.tests.test_images.test_nosimplify": "565b8bf068323147ae0fcc72263f258c552285aadcf2fc67786818fccc0b812d", + "astropy.visualization.wcsaxes.tests.test_images.test_custom_formatter": "e04eb07365c6bbc4774a20150c13dadcf72b9c5a664805aff2fffd106fdecd87", + "astropy.visualization.wcsaxes.tests.test_images.test_equatorial_arcsec": "2e2ad683a3acdda19dd4353654fa6d399f4b5cd528cd41591a7bcba8776a67ce", + "astropy.visualization.wcsaxes.tests.test_images.test_wcs_preserve_units": "464a0503099d3f7381479ddbba0e417011adb6e24301692056b124d608b1067b", + "astropy.visualization.wcsaxes.tests.test_transform_coord_meta.TestTransformCoordMeta.test_coords_overlay": "0a87473ff8e5b5610f4adac1518c608afcd2178b25584fb42f77bcc594c4f47a", + "astropy.visualization.wcsaxes.tests.test_transform_coord_meta.TestTransformCoordMeta.test_coords_overlay_auto_coord_meta": "2f737bb70fb1a5452cb0efa79010376614dc559e9aff607f638f044ac6b04448", + "astropy.visualization.wcsaxes.tests.test_transform_coord_meta.TestTransformCoordMeta.test_direct_init": "1f24c5243bfdf0f30e88afc4f2f5d66db85954cea50a2910af94975ff8765b45", + "astropy.visualization.wcsaxes.tests.test_wcsapi.test_wcsapi_5d_with_names": "c90ae6f3b0f9f407ca7a866fa648dc3ef4bea78369315292bc82f16104ed5736", + "astropy.visualization.wcsaxes.tests.test_wcsapi.test_wcsapi_2d_celestial_arcsec": "4b743d645a85d7516decbcf4a831c127af5a1800072597c2a1299d17fb186adb" +} diff --git a/astropy/tests/figures/py311-test-image-mpldev-cov.json b/astropy/tests/figures/py311-test-image-mpldev-cov.json new file mode 100644 index 000000000000..5c5bfba140b9 --- /dev/null +++ b/astropy/tests/figures/py311-test-image-mpldev-cov.json @@ -0,0 +1,62 @@ +{ + "astropy.visualization.wcsaxes.tests.test_frame.TestFrame.test_custom_frame": "5ba2b23bc4107e33eb9ffcfb656dc5b487969443f847497c0a9ab6ad6c4624bf", + "astropy.visualization.wcsaxes.tests.test_frame.TestFrame.test_update_clip_path_rectangular": "45669dd577a0750602555da53c590da0b70a64d899e38a6b3be1c8c0c393dc52", + "astropy.visualization.wcsaxes.tests.test_frame.TestFrame.test_update_clip_path_nonrectangular": "38145a9457711720f12888ba62aeb28ac06f0c05425856af415949e49ecc0b97", + "astropy.visualization.wcsaxes.tests.test_frame.TestFrame.test_update_clip_path_change_wcs": "6a3b3e21d12fb83e24c3307c24fe300e520db4b158d407f6df7fc79dfa2210cf", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_tight_layout": "5bd7b10da254a8cee16b4d2304faf3a9d876f9f512f2778f1326aec2d9c10371", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_image_plot": "a10da5ff6a071bb278b51fe811887e978773cf9f69717eeae9ded395e37bccfd", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_axes_off": "3580258468fc0ff0fa6d140299b79f1f05c16c3e222447de38228e01983fbdd1", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_axisbelow[True]": "3492f9ddb93014073086269f862aa7c6183da0c28a3a98afd330aa9e0f27cf09", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_axisbelow[False]": "8019d129a842be8d6c91543790f1e7b1933b2b91edfa7885a0d71b0ca8ef00d7", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_axisbelow[line]": "75516135332695601d2fe25381c360b03ebf5526c9376bdeab0e618069b1516a", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_contour_overlay": "45eee6ea3948c70ffd6e8758423dea0c90a83bd525420f34687f7981b451e911", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_contourf_overlay": "44d57ebdd3216650be39b019729b532a88ceb95f83f1f8dc664377f4f64a631e", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_overlay_features_image": "bf160601fe1e0bf8996f71c2907422cffaa468eb2755f4fd065dd2b5694af848", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_curvilinear_grid_patches_image": "3af87106f1b0fcf6070d33b748557d897be80531ab39192b227c82a409bef835", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_cube_slice_image": "bd4013e426adde9461a46b2c88b26020fb198bb14d7b4835e4cdeed8ca185678", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_cube_slice_image_lonlat": "eff17af9665669826c8924cb0686f9ef313bdad8a477e12622d8d7d07422efb3", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_plot_coord": "6d5cf6dae41a88239870282530c60f771f6369b600856c81b4991610821c3b3b", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_scatter_coord": "6d5cf6dae41a88239870282530c60f771f6369b600856c81b4991610821c3b3b", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_text_coord": "5ddca4e5ee5a86b6cb29588e5826ab271268f9502be440af5852bf0aa2724d2a", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_plot_line": "2bae09e3199778dcbea8f058e200a245367f7b70072b285cd7fb82635fcd7482", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_changed_axis_units": "c8b0ac1c48f9beba0261caea8aae466574c296ea088f4439e54497f04f7940f5", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_minor_ticks": "c314eb4ef0f20621c2fc62ba09d0d71afe67baafbdb289d1b79308ea6164291d", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_ticks_labels": "e1cefee39eedc7320e17e64e2c4e711f574c833c9f0155aa431944aa2f9ca899", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_no_ticks": "7ac52e09ffde02327bf01d233fd7947a74a1a36e9ec273f94ffd04a5d3f699d5", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_rcparams": "132410d077bf5d6ff682f285961b4a3b8b6750c344ffd4660f1fb176aa424680", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_tick_angles": "11ef218080920301ada1ef9cea558599ca110ba49e3dc4e9dc547c013b87fcc7", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_tick_angles_non_square_axes": "19b6034dc4f1e350efe12eb2e5a6add1bf47ffe37d79bd05f1d15cff684af365", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_set_coord_type": "c66c6870631760871da3d722cf4739f2e542f6d10937f5c1e87054ea67121ae4", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_ticks_regression": "e1cbb0d489ecc3ca72258e87bbf0006bbc1bd2979df464cb61c68f5f38615987", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_axislabels_regression": "72598fe74fb8fc1d0ab83df6ef4886accd08c8b8f47938408fa9327c8a37e2eb", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_noncelestial_angular": "191e3eab7bef77a5ee6ea956a5fb1bc15b95769dfef472a1cad4a8042f6136d4", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_patches_distortion": "704af668b56837628f11b3dedbba60c0d11fda2e896ddbba9dee0e8acb5b99d7", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_quadrangle": "0223dba7d207f37c644c5c0860f6ef18b11b8013438030c89d88d700ea2ed437", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_beam_shape_from_args": "01dffa3f79ddbfa5c119a49fc1282fee63f2792ccebc189ad44ab644b59f1559", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_beam_shape_from_header": "c4b34fab02c5be0d9d69b2615a1b05c3b73ddf9e40031c6873abe26ab3090472", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_scalebar": "4b5c3fc70a8bbd7aaeed0d15d78bc491c8253ca2cc7d239c343306e0a71f44c5", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_elliptical_frame": "3cf0c18fa653fafaaba2287b4a8c3e125eb29c2ba6a120918d0e088cbc8467d2", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_hms_labels": "507fb9485ce61050323d021347ce43acd1929f44249cfc98bd684ce685b4315b", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_latex_labels": "66a902d8e9ed0f6e95a5433d4842ede2163ff11387804bf93ed810632e1cd373", + "astropy.visualization.wcsaxes.tests.test_images.TestBasic.test_tick_params": "b197947dedc38821e02f31edb4e259a2d87439470947aa5c53bdfc89faa9d9b3", + "astropy.visualization.wcsaxes.tests.test_images.test_1d_plot_1d_wcs": "bf85a4cc85935869d4c26a39d155d90f68a9d266adcf2bac0d10b5da384d7045", + "astropy.visualization.wcsaxes.tests.test_images.test_1d_plot_1d_wcs_format_unit": "f632e2eddb9d683a085558339c23995049a7463644d6d6f997d923f5c6aaaa42", + "astropy.visualization.wcsaxes.tests.test_images.test_1d_plot_1d_wcs_get_transform": "0bcd3100d9d347cb14ae767107ea39885473964318d8fb14066c6217ee38a23b", + "astropy.visualization.wcsaxes.tests.test_images.test_1d_plot_2d_wcs_correlated": "47ffb96cf60b000028f26ae80e1ce9c35d8d43bf75d32f00b6e00e74656fcea3", + "astropy.visualization.wcsaxes.tests.test_images.test_1d_plot_1d_sliced_low_level_wcs[slices0-custom:pos.helioprojective.lon]": "24af7845ef230f2be4949377f8d2a8e7b3bccd2622d8f488442ed09fc07b2b5d", + "astropy.visualization.wcsaxes.tests.test_images.test_1d_plot_1d_sliced_low_level_wcs[slices1-custom:pos.helioprojective.lat]": "03708033709fe9b63375f96fbf5ea44b4e17cae968d385f5899d4b2e42be1f6c", + "astropy.visualization.wcsaxes.tests.test_images.test_1d_plot_put_varying_axis_on_bottom_lon[slices0-hpln]": "24af7845ef230f2be4949377f8d2a8e7b3bccd2622d8f488442ed09fc07b2b5d", + "astropy.visualization.wcsaxes.tests.test_images.test_1d_plot_put_varying_axis_on_bottom_lon[slices1-hplt]": "03708033709fe9b63375f96fbf5ea44b4e17cae968d385f5899d4b2e42be1f6c", + "astropy.visualization.wcsaxes.tests.test_images.test_allsky_labels_wrap": "4697fefe84a8b0e5b58cc35bc250921ec3fffc7cbdee85836427af90fe4ac475", + "astropy.visualization.wcsaxes.tests.test_images.test_tickable_gridlines": "5ce7b377d7cf15d3be97a922929536413a36fc2c1a34423ecee8f04d361d35fd", + "astropy.visualization.wcsaxes.tests.test_images.test_overlay_nondegree_unit": "4f5819ad0dc6cc6c5a4776265f114f81b192bfd11ce1ddbb3d8b43da6b56e642", + "astropy.visualization.wcsaxes.tests.test_images.test_nosimplify": "336423447a5066d39a60ac4b0839dbe8eb2fab6a742830a71464f941c6307c66", + "astropy.visualization.wcsaxes.tests.test_images.test_custom_formatter": "1142e72415e9affaa97c7e763d1274632fe1d9cf8a9eae2a579a179fd14d6b9d", + "astropy.visualization.wcsaxes.tests.test_images.test_equatorial_arcsec": "f048285e01f99235e711068e06ea5729b1ff5258604a944a5fa26042e613dae6", + "astropy.visualization.wcsaxes.tests.test_images.test_wcs_preserve_units": "4111f2731c268bbe8ff57551b011afd6dd918efe07236507625f6e99e69b7263", + "astropy.visualization.wcsaxes.tests.test_transform_coord_meta.TestTransformCoordMeta.test_coords_overlay": "f7635158075969632bc3cabd6cfa9798a3d4227f173aed32a546c540c65d9b4d", + "astropy.visualization.wcsaxes.tests.test_transform_coord_meta.TestTransformCoordMeta.test_coords_overlay_auto_coord_meta": "9a3cc14c87a8349487888c69bab345748894be4295598eaed2475804f97b5f8a", + "astropy.visualization.wcsaxes.tests.test_transform_coord_meta.TestTransformCoordMeta.test_direct_init": "021364576662b07f1eedf58251d657244fd1b67bb16ec4c99a0506b1dde0d66e", + "astropy.visualization.wcsaxes.tests.test_wcsapi.test_wcsapi_5d_with_names": "b243aec60020235d04c816743c0fcc643495f58d4d4d5086588c59628d9cea76", + "astropy.visualization.wcsaxes.tests.test_wcsapi.test_wcsapi_2d_celestial_arcsec": "ecb11a595e1352b3b8e6967494fa5eac63992fc878bf15a934329bb28aeb61d6" +} diff --git a/astropy/tests/helper.py b/astropy/tests/helper.py index 5efb936c04ef..e908ace953c3 100644 --- a/astropy/tests/helper.py +++ b/astropy/tests/helper.py @@ -1,301 +1,150 @@ # Licensed under a 3-clause BSD style license - see LICENSE.rst """ -This module prvoides the tools used to internally run the astropy test suite -from the installed astropy. It makes use of the `pytest` testing framework. +This module provides the tools used to internally run the astropy test suite +from the installed astropy. It makes use of the |pytest| testing framework. """ -import shlex -import sys -import base64 -import zlib -import functools import os -import subprocess -import shutil -import tempfile - -from distutils.core import Command - -from .. import test - -if os.environ.get('ASTROPY_USE_SYSTEM_PYTEST'): - import pytest - -else: - from ..extern import pytest as extern_pytest - - if sys.version_info >= (3, 0): - exec("def do_exec_def(co, loc): exec(co, loc)\n") - extern_pytest.do_exec = do_exec_def - - import pickle - unpacked_sources = extern_pytest.sources.encode("ascii") - unpacked_sources = pickle.loads( - zlib.decompress(base64.decodebytes(unpacked_sources))) - else: - exec("def do_exec_def(co, loc): exec co in loc\n") - extern_pytest.do_exec = do_exec_def - - import cPickle as pickle - unpacked_sources = pickle.loads( - zlib.decompress(base64.decodestring(extern_pytest.sources))) - - importer = extern_pytest.DictImporter(unpacked_sources) - sys.meta_path.append(importer) - - pytest = importer.load_module('pytest') - - -# pytest marker to mark tests which get data from the web -remote_data = pytest.mark.remote_data - +import pickle +import sys -class TestRunner(object): - def __init__(self, base_path): - self.base_path = base_path +import pytest - def run_tests(self, package=None, test_path=None, args=None, plugins=None, - verbose=False, pastebin=None, remote_data=False, pep8=False, - pdb=False, coverage=False): - """ - The docstring for this method lives in astropy/__init__.py:test - """ - if package is None: - package_path = self.base_path - else: - package_path = os.path.join(self.base_path, - package.replace('.', os.path.sep)) +from astropy.units import allclose as quantity_allclose # noqa: F401 - if not os.path.isdir(package_path): - raise ValueError('Package not found: {0}'.format(package)) +# For backward-compatibility with affiliated packages +from .runner import TestRunner # noqa: F401 - if test_path: - package_path = os.path.join(package_path, - os.path.abspath(test_path)) +__all__ = [ + "assert_follows_unicode_guidelines", + "assert_quantity_allclose", + "check_pickling_recovery", + "generic_recursive_equality_test", + "pickle_protocol", +] - all_args = package_path - # add any additional args entered by the user - if args is not None: - all_args += ' {0}'.format(args) +# https://docs.github.com/en/actions/learn-github-actions/variables#default-environment-variables +CI = os.environ.get("CI", "false") == "true" +IS_CRON = os.environ.get("IS_CRON", "false") == "true" - # add verbosity flag - if verbose: - all_args += ' -v' - # turn on pastebin output - if pastebin is not None: - if pastebin in ['failed', 'all']: - all_args += ' --pastebin={0}'.format(pastebin) - else: - raise ValueError("pastebin should be 'failed' or 'all'") +def assert_follows_unicode_guidelines(x, roundtrip=None): + """ + Test that an object follows our Unicode policy. See + "Unicode guidelines" in the coding guidelines. + + Parameters + ---------- + x : object + The instance to test + + roundtrip : module, optional + When provided, this namespace will be used to evaluate + ``repr(x)`` and ensure that it roundtrips. It will also + ensure that ``__bytes__(x)`` roundtrip. + If not provided, no roundtrip testing will be performed. + """ + from astropy import conf + + with conf.set_temp("unicode_output", False): + assert format(x, "").isascii() + str_x = str(x) + assert str_x.isascii() + repr_x = repr(x) + assert repr_x.isascii() + if roundtrip is not None: + assert type(x)(str_x) == x + assert eval(repr_x, roundtrip) == x + + with conf.set_temp("unicode_output", True): + assert repr(x) == repr_x + if roundtrip is not None: + assert type(x)(str(x)) == x + + +@pytest.fixture(params=[0, 1, 4, -1]) +def pickle_protocol(request): + """ + Fixture to run all the tests for protocols 0, 1, 4 and -1 (most advanced). - # run @remote_data tests - if remote_data: - all_args += ' --remote-data' + * 0 is the original "human-readable" protocol + * 1 is the oldest binary format + * 4 introduced in Python 3.4, has been the default in 3.8-3.13 + * -1 is the most advanced, which is 5 since Python 3.8, default from 3.14, + and the first binary format to preserve byteorder. - if pep8: - try: - import pytest_pep8 - except ImportError: - raise ImportError('PEP8 checking requires pytest-pep8 plugin: ' - 'http://pypi.python.org/pypi/pytest-pep8') - else: - all_args += ' --pep8 -k pep8' + (Originally from astropy.table.tests.test_pickle). + """ + return request.param - # activate post-mortem PDB for failing tests - if pdb: - all_args += ' --pdb' - if coverage: - try: - import pytest_cov - except ImportError: - raise ImportError( - 'Coverage reporting requires pytest-cov plugin: ' - 'http://pypi.python.org/pypi/pytest-cov') +def generic_recursive_equality_test(a, b, class_history): + """ + Check if the attributes of a and b are equal. Then, + check if the attributes of the attributes are equal. + """ + # NOTE: The call may need to be adapted if other objects implementing a __getstate__ + # with required argument(s) are passed to this function. + # For a class with `__slots__` the default state is not a `dict`; + # with neither `__dict__` nor `__slots__` it is `None`. + state = a.__getstate__(a) if isinstance(a, type) else a.__getstate__() + dict_a = state if isinstance(state, dict) else getattr(a, "__dict__", {}) + dict_b = b.__dict__ + for key in dict_a: + assert key in dict_b, f"Did not pickle {key}" + + if dict_a[key].__class__.__eq__ is not object.__eq__: + # Only compare if the class defines a proper equality test. + # E.g., info does not define __eq__, and hence defers to + # object.__eq__, which is equivalent to checking that two + # instances are the same. This will generally not be true + # after pickling. + eq = dict_a[key] == dict_b[key] + if "__iter__" in dir(eq): + eq = False not in eq + assert eq, f"Value of {key} changed by pickling" + + if hasattr(dict_a[key], "__dict__"): + if dict_a[key].__class__ in class_history: + # attempt to prevent infinite recursion + pass else: - # Don't use get_data_filename here, because it - # requires importing astropy.config and thus screwing - # up coverage results for those packages. - coveragerc = os.path.join( - os.path.dirname(__file__), 'coveragerc') + new_class_history = [dict_a[key].__class__] + new_class_history.extend(class_history) + generic_recursive_equality_test( + dict_a[key], dict_b[key], new_class_history + ) - # We create a coveragerc that is specific to the version - # of Python we're running, so that we can mark branches - # as being specifically for Python 2 or Python 3 - with open(coveragerc, 'r') as fd: - coveragerc_content = fd.read() - if sys.version_info[0] >= 3: - ignore_python_version = '2' - else: - ignore_python_version = '3' - coveragerc_content = coveragerc_content.replace( - "{ignore_python_version}", ignore_python_version) - with tempfile.NamedTemporaryFile(delete=False) as tmp: - tmp.write(coveragerc_content) - all_args += ( - ' --cov-report html --cov astropy' - ' --cov-config {0}'.format(tmp.name)) - - # check if we're inside the distutils test command, which sets the - # _ASTROPY_TEST_ builtin - try: - _ASTROPY_TEST_ - insidetestcmd = True - except NameError: - insidetestcmd = False - - #set up environment variables to fake the config and cache systems, - #unless this has already been done by the distutils test command - if not insidetestcmd: - oldconfigdir = os.environ.get('XDG_CONFIG_HOME') - oldcachedir = os.environ.get('XDG_CACHE_HOME') - os.environ['XDG_CONFIG_HOME'] = tempfile.mkdtemp('astropy_config') - os.environ['XDG_CACHE_HOME'] = tempfile.mkdtemp('astropy_cache') - os.mkdir(os.path.join(os.environ['XDG_CONFIG_HOME'], 'astropy')) - os.mkdir(os.path.join(os.environ['XDG_CACHE_HOME'], 'astropy')) - - try: - all_args = shlex.split( - all_args, posix=not sys.platform.startswith('win')) - - result = pytest.main(args=all_args, plugins=plugins) - finally: - if coverage: - if not tmp.closed: - tmp.close() - os.remove(tmp.name) - - if not insidetestcmd: - #wipe the config/cache tmpdirs and restore the envars - shutil.rmtree(os.environ['XDG_CONFIG_HOME']) - shutil.rmtree(os.environ['XDG_CACHE_HOME']) - if oldconfigdir is None: - del os.environ['XDG_CONFIG_HOME'] - else: - os.environ['XDG_CONFIG_HOME'] = oldconfigdir - if oldcachedir is None: - del os.environ['XDG_CACHE_HOME'] - else: - os.environ['XDG_CACHE_HOME'] = oldcachedir - - return result - run_tests.__doc__ = test.__doc__ - - -class astropy_test(Command, object): - user_options = [ - ('package=', 'P', - "The name of a specific package to test, e.g. 'io.fits' or 'utils'. " - "If nothing is specified all default Astropy tests are run."), - ('test-path=', 't', 'Specify a test location by path. Must be ' - 'specified absolutely or relative to the current directory. ' - 'May be a single file or directory.'), - ('verbose-results', 'V', - 'Turn on verbose output from pytest. Same as specifying `-v` in ' - '`args`.'), - ('plugins=', 'p', - 'Plugins to enable when running pytest. Same as specifying `-p` in ' - '`args`.'), - ('pastebin=', 'b', - "Enable pytest pastebin output. Either 'all' or 'failed'."), - ('args=', 'a', 'Additional arguments to be passed to pytest'), - ('remote-data', 'R', 'Run tests that download remote data'), - ('pep8', '8', 'Enable PEP8 checking and disable regular tests. ' - 'Same as specifying `--pep8 -k pep8` in `args`. Requires the ' - 'pytest-pep8 plugin.'), - ('pdb', 'd', 'Turn on PDB post-mortem analysis for failing tests. ' - 'Same as specifying `--pdb` in `args`.'), - ('coverage', 'c', 'Create a coverage report. Requires the pytest-cov ' - 'plugin is installed') - ] - - package_name = None - - def initialize_options(self): - self.package = None - self.test_path = None - self.verbose_results = False - self.plugins = None - self.pastebin = None - self.args = None - self.remote_data = False - self.pep8 = False - self.pdb = False - self.coverage = False - - def finalize_options(self): - # Normally we would validate the options here, but that's handled in - # run_tests - pass - - def run(self): - self.reinitialize_command('build', inplace=False) - self.run_command('build') - build_cmd = self.get_finalized_command('build') - new_path = os.path.abspath(build_cmd.build_lib) - - # Run the tests in a subprocess--this is necessary since new extension - # modules may have appeared, and this is the easiest way to set up a - # new environment - - # We need to set a flag in the child's environment so that - # unnecessary code is not imported before py.test can start - # up, otherwise the coverage results will be artifically low. - if sys.version_info[0] >= 3: - set_flag = "import builtins; builtins._ASTROPY_TEST_ = True" - else: - set_flag = "import __builtin__; __builtin__._ASTROPY_TEST_ = True" - - cmd = ('{0}; import {1.package_name}, sys; sys.exit(' - '{1.package_name}.test({1.package!r}, {1.test_path!r}, ' - '{1.args!r}, {1.plugins!r}, {1.verbose_results!r}, ' - '{1.pastebin!r}, {1.remote_data!r}, {1.pep8!r}, {1.pdb!r}, ' - '{1.coverage!r}))') - cmd = cmd.format(set_flag, self) - - #override the config locations to not make a new directory nor use - #existing cache or config - os.environ['XDG_CONFIG_HOME'] = tempfile.mkdtemp('astropy_config') - os.environ['XDG_CACHE_HOME'] = tempfile.mkdtemp('astropy_cache') - os.mkdir(os.path.join(os.environ['XDG_CONFIG_HOME'], 'astropy')) - os.mkdir(os.path.join(os.environ['XDG_CACHE_HOME'], 'astropy')) +def check_pickling_recovery(original, protocol): + """ + Try to pickle an object. If successful, make sure + the object's attributes survived pickling and unpickling. + """ + f = pickle.dumps(original, protocol=protocol) + unpickled = pickle.loads(f) + class_history = [original.__class__] + generic_recursive_equality_test(original, unpickled, class_history) - try: - retcode = subprocess.call([sys.executable, '-c', cmd], - cwd=new_path, close_fds=False) - finally: - # kill the temporary dirs - shutil.rmtree(os.environ['XDG_CONFIG_HOME']) - shutil.rmtree(os.environ['XDG_CACHE_HOME']) - if self.coverage and retcode == 0: - # Copy the htmlcov from build/lib.../htmlcov to a more - # obvious place - if os.path.exists('htmlcov'): - shutil.rmtree('htmlcov') - shutil.copytree(os.path.join(new_path, 'htmlcov'), 'htmlcov') +def assert_quantity_allclose(actual, desired, rtol=1.0e-7, atol=None, **kwargs): + """ + Raise an assertion if two objects are not equal up to desired tolerance. - raise SystemExit(retcode) + This is a :class:`~astropy.units.Quantity`-aware version of + :func:`numpy.testing.assert_allclose`. + """ + import numpy as np + from astropy.units.quantity import _unquantify_allclose_arguments -class raises: - """ - A decorator to mark that a test should raise a given exception. - Use as follows:: + __tracebackhide__ = True + np.testing.assert_allclose( + *_unquantify_allclose_arguments(actual, desired, rtol, atol), **kwargs + ) - @raises(ZeroDivisionError) - def test_foo(): - x = 1/0 - """ - # pep-8 naming exception -- this is a decorator class - def __init__(self, exc): - self._exc = exc - def __call__(self, func): - @functools.wraps(func) - def run_raises_test(*args, **kwargs): - pytest.raises(self._exc, func, *args, **kwargs) - return run_raises_test +_skip_docstring_tests_with_optimized_python = pytest.mark.skipif( + sys.flags.optimize >= 2, reason="docstrings are not testable in optimized mode" +) diff --git a/astropy/tests/pytest_plugins.py b/astropy/tests/pytest_plugins.py deleted file mode 100644 index 886a27e13440..000000000000 --- a/astropy/tests/pytest_plugins.py +++ /dev/null @@ -1,35 +0,0 @@ -# Licensed under a 3-clause BSD style license - see LICENSE.rst -""" -These plugins modify the behavior of py.test and are meant to be imported -into conftest.py in the root directory. -""" - -from .helper import pytest - -# these pytest hooks allow us to mark tests and run the marked tests with -# specific command line options. -def pytest_addoption(parser): - parser.addoption("--remote-data", action="store_true", - help="run tests with online data") - - -def pytest_runtest_setup(item): - if ('remote_data' in item.keywords and - not item.config.getvalue("remote_data")): - pytest.skip("need --remote-data option to run") - - -def pytest_report_header(config): - from .. import __version__ - s = "\nTesting Astropy version {0}.\n".format(__version__) - s += "Running tests in {0}.\n".format(" ".join(config.args)) - - special_opts = ["remote_data", "pep8"] - opts = [] - for op in special_opts: - if getattr(config.option, op, None): - opts.append(op) - if opts: - s += "Using Astropy options: {0}.\n".format(" ".join(opts)) - - return s diff --git a/astropy/tests/runner.py b/astropy/tests/runner.py new file mode 100644 index 000000000000..0e046d7f65f4 --- /dev/null +++ b/astropy/tests/runner.py @@ -0,0 +1,593 @@ +"""Implements the Astropy TestRunner which is a thin wrapper around pytest.""" + +import copy +import glob +import inspect +import os +import shlex +import sys +import tempfile +import warnings +from functools import wraps +from importlib.util import find_spec + +from astropy.utils import deprecated, find_current_module +from astropy.utils.exceptions import ( + AstropyDeprecationWarning, + AstropyWarning, +) + +__all__ = ["TestRunner", "TestRunnerBase"] + + +class keyword: + """ + A decorator to mark a method as keyword argument for the ``TestRunner``. + + Parameters + ---------- + default_value : `object` + The default value for the keyword argument. (Default: `None`) + + priority : `int` + keyword argument methods are executed in order of descending priority. + """ + + def __init__(self, default_value=None, priority=0): + self.default_value = default_value + self.priority = priority + + def __call__(self, f): + def keyword(*args, **kwargs): + return f(*args, **kwargs) + + keyword._default_value = self.default_value + keyword._priority = self.priority + # Set __doc__ explicitly here rather than using wraps because we want + # to keep the function name as keyword so we can inspect it later. + keyword.__doc__ = f.__doc__ + + return keyword + + +@deprecated("8.0", alternative="pytest") +class TestRunnerBase: + """ + The base class for the TestRunner. + + A test runner can be constructed by creating a subclass of this class and + defining 'keyword' methods. These are methods that have the + ``astropy.tests.runner.keyword`` decorator, these methods are used to + construct allowed keyword arguments to the + `~astropy.tests.runner.TestRunnerBase.run_tests` method as a way to allow + customization of individual keyword arguments (and associated logic) + without having to re-implement the whole + `~astropy.tests.runner.TestRunnerBase.run_tests` method. + + Examples + -------- + A simple keyword method:: + + class MyRunner(TestRunnerBase): + + @keyword('default_value'): + def spam(self, spam, kwargs): + \"\"\" + spam : `str` + The parameter description for the run_tests docstring. + \"\"\" + # Return value must be a list with a CLI parameter for pytest. + return ['--spam={}'.format(spam)] + """ + + def __init__(self, base_path): + self.base_path = os.path.abspath(base_path) + + def __new__(cls, *args, **kwargs): + # Before constructing the class parse all the methods that have been + # decorated with ``keyword``. + + # The objective of this method is to construct a default set of keyword + # arguments to the ``run_tests`` method. It does this by inspecting the + # methods of the class for functions with the name ``keyword`` which is + # the name of the decorator wrapping function. Once it has created this + # dictionary, it also formats the docstring of ``run_tests`` to be + # comprised of the docstrings for the ``keyword`` methods. + + # To add a keyword argument to the ``run_tests`` method, define a new + # method decorated with ``@keyword`` and with the ``self, name, kwargs`` + # signature. + # Get all 'function' members as the wrapped methods are functions + functions = inspect.getmembers(cls, predicate=inspect.isfunction) + + # Filter out anything that's not got the name 'keyword' + keywords = filter(lambda func: func[1].__name__ == "keyword", functions) + # Sort all keywords based on the priority flag. + sorted_keywords = sorted(keywords, key=lambda x: x[1]._priority, reverse=True) + + cls.keywords = {} + doc_keywords = "" + for name, func in sorted_keywords: + # Here we test if the function has been overloaded to return + # NotImplemented which is the way to disable arguments on + # subclasses. If it has been disabled we need to remove it from the + # default keywords dict. We do it in the try except block because + # we do not have access to an instance of the class, so this is + # going to error unless the method is just doing `return + # NotImplemented`. + try: + # Second argument is False, as it is normally a bool. + # The other two are placeholders for objects. + if func(None, False, None) is NotImplemented: + continue + except Exception: + pass + + # Construct the default kwargs dict and docstring + cls.keywords[name] = func._default_value + if func.__doc__: + doc_keywords += " " * 8 + doc_keywords += func.__doc__.strip() + doc_keywords += "\n\n" + + cls.run_tests.__doc__ = cls.RUN_TESTS_DOCSTRING.format(keywords=doc_keywords) + + return super().__new__(cls) + + def _generate_args(self, **kwargs): + # Update default values with passed kwargs + # but don't modify the defaults + keywords = copy.deepcopy(self.keywords) + keywords.update(kwargs) + # Iterate through the keywords (in order of priority) + args = [] + for keyword in keywords.keys(): + func = getattr(self, keyword) + result = func(keywords[keyword], keywords) + + # Allow disabling of options in a subclass + if result is NotImplemented: + raise TypeError( + f"run_tests() got an unexpected keyword argument {keyword}" + ) + + # keyword methods must return a list + if not isinstance(result, list): + raise TypeError(f"{keyword} keyword method must return a list") + + args += result + + return args + + RUN_TESTS_DOCSTRING = """ + Run the tests for the package. + + This method builds arguments for and then calls ``pytest.main``. + + .. deprecated:: 8.0 + Use pytest instead. + + Parameters + ---------- +{keywords} + + """ + + _required_dependencies = [ + "pytest", + "pytest_remotedata", + "pytest_doctestplus", + "pytest_astropy_header", + ] + _missing_dependancy_error = ( + "Test dependencies are missing: {}. You should install the " + "'pytest-astropy' package (you may need to update the package if you " + "have a previous version installed, e.g., " + "'pip install pytest-astropy --upgrade' or the equivalent with conda)." + ) + + @classmethod + def _has_test_dependencies(cls): # pragma: no cover + # Using the test runner will not work without these dependencies. + for module in cls._required_dependencies: + spec = find_spec(module) + # Checking loader accounts for packages that were uninstalled. + # pytest plugins are special, it's enough if they are picked up the + # pytest independently of how they are installed. + if spec is None or spec.loader is None: + # Don't import pytest until it's actually needed + import pytest + + pluginmanager = pytest.PytestPluginManager() + try: + pluginmanager.import_plugin(module) + except ImportError: + raise RuntimeError(cls._missing_dependancy_error.format(module)) + + def run_tests(self, **kwargs): + # This method is weirdly hooked into various things with docstring + # overrides, so we keep it simple and not use @deprecated here. + warnings.warn( + "The test runner is deprecated in v8.0 and may be removed in a future version.\n Use pytest instead.", + AstropyDeprecationWarning, + ) + + # The following option will include eggs inside a .eggs folder in + # sys.path when running the tests. This is possible so that when + # running pytest, test dependencies installed via e.g. + # tests_requires are available here. This is not an advertised option + # since it is only for internal use + if kwargs.pop("add_local_eggs_to_path", False): + # Add each egg to sys.path individually + for egg in glob.glob(os.path.join(".eggs", "*.egg")): + sys.path.insert(0, egg) + + self._has_test_dependencies() # pragma: no cover + + # The docstring for this method is defined as a class variable. + # This allows it to be built for each subclass in __new__. + + # Don't import pytest until it's actually needed to run the tests + import pytest + + # Raise error for undefined kwargs + allowed_kwargs = set(self.keywords.keys()) + passed_kwargs = set(kwargs.keys()) + if not passed_kwargs.issubset(allowed_kwargs): + wrong_kwargs = list(passed_kwargs.difference(allowed_kwargs)) + raise TypeError( + f"run_tests() got an unexpected keyword argument {wrong_kwargs[0]}" + ) + + args = self._generate_args(**kwargs) + + if kwargs.get("plugins") is not None: + plugins = kwargs.pop("plugins") + elif self.keywords.get("plugins", None) is not None: + plugins = self.keywords["plugins"] + else: + plugins = [] + + # Avoid the existing config. Note that we need to do this here in + # addition to in conftest.py - for users running tests interactively + # in e.g. IPython, conftest.py would get read in too late, so we need + # to do it here - but at the same time the code here doesn't work when + # running tests in parallel mode because this uses subprocesses which + # don't know about the temporary config/cache. + # Note, this is superfluous if the config_dir option to pytest is in use, + # but it's also harmless + orig_astropy_config = os.environ.get("ASTROPY_CONFIG_DIR") + with tempfile.TemporaryDirectory("astropy_config") as astropy_config: + os.environ["ASTROPY_CONFIG_DIR"] = astropy_config + try: + return pytest.main(args=args, plugins=plugins) + finally: + if orig_astropy_config is None: + os.environ.pop("ASTROPY_CONFIG_DIR", None) + else: + os.environ["ASTROPY_CONFIG_DIR"] = orig_astropy_config + + @classmethod + def make_test_runner_in(cls, path): + """ + Constructs a `TestRunner` to run in the given path, and returns a + ``test()`` function which takes the same arguments as + `~astropy.tests.runner.TestRunner.run_tests`. + + The returned ``test()`` function will be defined in the module this + was called from. This is used to implement the ``astropy.test()`` + function (or the equivalent for affiliated packages). + """ + runner = cls(path) + + @wraps(runner.run_tests, ("__doc__",)) + def test(**kwargs): + return runner.run_tests(**kwargs) + + module = find_current_module(2) + if module is not None: + test.__module__ = module.__name__ + + # A somewhat unusual hack, but delete the attached __wrapped__ + # attribute--although this is normally used to tell if the function + # was wrapped with wraps, on some version of Python this is also + # used to determine the signature to display in help() which is + # not useful in this case. We don't really care in this case if the + # function was wrapped either + if hasattr(test, "__wrapped__"): + del test.__wrapped__ + + test.__test__ = False + return test + + +@deprecated("8.0", alternative="pytest") +class TestRunner(TestRunnerBase): + """ + A test runner for astropy tests. + """ + + def packages_path(self, packages, base_path, error=None, warning=None): + """ + Generates the path for multiple packages. + + Parameters + ---------- + packages : str + Comma separated string of packages. + base_path : str + Base path to the source code or documentation. + error : str + Error message to be raised as ``ValueError``. Individual package + name and path can be accessed by ``{name}`` and ``{path}`` + respectively. No error is raised if `None`. (Default: `None`) + warning : str + Warning message to be issued. Individual package + name and path can be accessed by ``{name}`` and ``{path}`` + respectively. No warning is issues if `None`. (Default: `None`) + + Returns + ------- + paths : list of str + List of strings of existing package paths. + """ + packages = packages.split(",") + + paths = [] + for package in packages: + path = os.path.join(base_path, package.replace(".", os.path.sep)) + if not os.path.isdir(path): + info = {"name": package, "path": path} + if error is not None: + raise ValueError(error.format(**info)) + if warning is not None: + warnings.warn(warning.format(**info)) + else: + paths.append(path) + + return paths + + # Increase priority so this warning is displayed first. + @keyword(priority=1000) + def coverage(self, coverage, kwargs): + if coverage: + warnings.warn( + "The coverage option is ignored on run_tests, since it " + "can not be made to work in that context. Use " + "'python setup.py test --coverage' instead.", + AstropyWarning, + ) + + return [] + + # test_path depends on self.package_path so make sure this runs before + # test_path. + @keyword(priority=1) + def package(self, package, kwargs): + """ + package : str, optional + The name of a specific package to test, e.g. 'io.fits' or + 'utils'. Accepts comma separated string to specify multiple + packages. If nothing is specified all default tests are run. + """ + if package is None: + self.package_path = [self.base_path] + else: + error_message = "package to test is not found: {name} (at path {path})." + self.package_path = self.packages_path( + package, self.base_path, error=error_message + ) + + if not kwargs["test_path"]: + return self.package_path + + return [] + + @keyword() + def test_path(self, test_path, kwargs): + """ + test_path : str, optional + Specify location to test by path. May be a single file or + directory. Must be specified absolutely or relative to the + calling directory. + """ + all_args = [] + # Ensure that the package kwarg has been run. + self.package(kwargs["package"], kwargs) + if test_path: + base, ext = os.path.splitext(test_path) + + if ext in (".rst", ""): + if kwargs["docs_path"] is None: + # This shouldn't happen from "python setup.py test" + raise ValueError( + "Can not test .rst files without a docs_path specified." + ) + + abs_docs_path = os.path.abspath(kwargs["docs_path"]) + abs_test_path = os.path.abspath( + os.path.join(abs_docs_path, os.pardir, test_path) + ) + + common = os.path.commonprefix((abs_docs_path, abs_test_path)) + + if os.path.exists(abs_test_path) and common == abs_docs_path: + # Turn on the doctest_rst plugin + all_args.append("--doctest-rst") + test_path = abs_test_path + + # Check that the extensions are in the path and not at the end to + # support specifying the name of the test, i.e. + # test_quantity.py::test_unit + if not ( + os.path.isdir(test_path) or (".py" in test_path or ".rst" in test_path) + ): + raise ValueError( + "Test path must be a directory or a path to a .py or .rst file" + ) + + return all_args + [test_path] + + return [] + + @keyword() + def args(self, args, kwargs): + """ + args : str, optional + Additional arguments to be passed to ``pytest.main`` in the ``args`` + keyword argument. + """ + if args: + return shlex.split(args, posix=not sys.platform.startswith("win")) + + return [] + + @keyword(default_value=[]) + def plugins(self, plugins, kwargs): + """ + plugins : list, optional + Plugins to be passed to ``pytest.main`` in the ``plugins`` keyword + argument. + """ + # Plugins are handled independently by `run_tests` so we define this + # keyword just for the docstring + return [] + + @keyword() + def verbose(self, verbose, kwargs): + """ + verbose : bool, optional + Convenience option to turn on verbose output from pytest. Passing + True is the same as specifying ``-v`` in ``args``. + """ + if verbose: + return ["-v"] + + return [] + + @keyword() + def pastebin(self, pastebin, kwargs): + """ + pastebin : ('failed', 'all', None), optional + Convenience option for turning on pytest pastebin output. Set to + 'failed' to upload info for failed tests, or 'all' to upload info + for all tests. + """ + if pastebin is not None: + if pastebin in ["failed", "all"]: + return [f"--pastebin={pastebin}"] + else: + raise ValueError("pastebin should be 'failed' or 'all'") + + return [] + + @keyword(default_value="none") + def remote_data(self, remote_data, kwargs): + """ + remote_data : {'none', 'astropy', 'any'}, optional + Controls whether to run tests marked with @pytest.mark.remote_data. This can be + set to run no tests with remote data (``none``), only ones that use + data from http://data.astropy.org (``astropy``), or all tests that + use remote data (``any``). The default is ``none``. + """ + if remote_data is True: + remote_data = "any" + elif remote_data is False: + remote_data = "none" + elif remote_data not in ("none", "astropy", "any"): + warnings.warn( + "The remote_data option should be one of " + f"none/astropy/any (found {remote_data}). For backward-compatibility, " + "assuming 'any', but you should change the option to be " + "one of the supported ones to avoid issues in " + "future.", + AstropyDeprecationWarning, + ) + remote_data = "any" + + return [f"--remote-data={remote_data}"] + + @keyword() + def pdb(self, pdb, kwargs): + """ + pdb : bool, optional + Turn on PDB post-mortem analysis for failing tests. Same as + specifying ``--pdb`` in ``args``. + """ + if pdb: + return ["--pdb"] + return [] + + @keyword(0) + def parallel(self, parallel, kwargs): + """ + parallel : int or 'auto', optional + When provided, run the tests in parallel on the specified + number of CPUs. If parallel is ``'auto'``, it will use the all + the cores on the machine. Requires the ``pytest-xdist`` plugin. + """ + if parallel != 0: + try: + from xdist import plugin # noqa: F401 + except ImportError: + raise SystemError( + "running tests in parallel requires the pytest-xdist package" + ) + + return ["-n", str(parallel)] + + return [] + + @keyword() + def docs_path(self, docs_path, kwargs): + """ + docs_path : str, optional + The path to the documentation .rst files. + """ + paths = [] + if docs_path is not None and not kwargs["skip_docs"]: + if kwargs["package"] is not None: + warning_message = ( + "Can not test .rst docs for {name}, since " + "docs path ({path}) does not exist." + ) + paths = self.packages_path( + kwargs["package"], docs_path, warning=warning_message + ) + elif not kwargs["test_path"]: + paths = [docs_path] + + if paths and not kwargs["test_path"]: + paths.append("--doctest-rst") + + return paths + + @keyword() + def skip_docs(self, skip_docs, kwargs): + """ + skip_docs : `bool`, optional + When `True`, skips running the doctests in the .rst files. + """ + # Skip docs is a bool used by docs_path only. + return [] + + @keyword() + def repeat(self, repeat, kwargs): + """ + repeat : `int`, optional + If set, specifies how many times each test should be run. This is + useful for diagnosing sporadic failures. + """ + if repeat: + return [f"--repeat={repeat}"] + + return [] + + # Override run_tests for astropy-specific fixes + def run_tests(self, **kwargs): + # This prevents cyclical import problems that make it + # impossible to test packages that define Table types on their + # own. + from astropy.table import Table # noqa: F401 + + return super().run_tests(**kwargs) diff --git a/astropy/tests/setup_package.py b/astropy/tests/setup_package.py deleted file mode 100644 index 4c861fc90495..000000000000 --- a/astropy/tests/setup_package.py +++ /dev/null @@ -1,5 +0,0 @@ -# Licensed under a 3-clause BSD style license - see LICENSE.rst - -def get_package_data(): - return { - 'astropy.tests': ['coveragerc']} diff --git a/astropy/tests/test_logger.py b/astropy/tests/test_logger.py index 334c026c68e7..4b01bf5393c3 100644 --- a/astropy/tests/test_logger.py +++ b/astropy/tests/test_logger.py @@ -1,10 +1,16 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +import importlib +import locale +import logging import sys import warnings import pytest -from .. import log -from ..logger import LoggingError +from astropy import log +from astropy.logger import _WITHIN_IPYTHON, LoggingError, conf +from astropy.utils.exceptions import AstropyUserWarning, AstropyWarning # Save original values of hooks. These are not the system values, but the # already overwritten values since the logger already gets imported before @@ -13,17 +19,10 @@ _showwarning = warnings.showwarning -try: - ip = get_ipython() -except NameError: - ip = None - - def setup_function(function): - - # Reset hooks to original values - sys.excepthook = _excepthook - warnings.showwarning = _showwarning + # Reset modules to default + importlib.reload(warnings) + importlib.reload(sys) # Reset internal original hooks log._showwarning_orig = None @@ -39,136 +38,178 @@ def setup_function(function): log.disable_exception_logging() -def teardown_module(function): - - # Ensure that hooks are restored to original values - sys.excepthook = _excepthook - warnings.showwarning = _showwarning +teardown_module = setup_function def test_warnings_logging_disable_no_enable(): - with pytest.raises(LoggingError) as e: + with pytest.raises(LoggingError, match=r"Warnings logging has not been enabled"): log.disable_warnings_logging() - assert e.value.args[0] == 'Warnings logging has not been enabled' def test_warnings_logging_enable_twice(): log.enable_warnings_logging() - with pytest.raises(LoggingError) as e: + with pytest.raises( + LoggingError, match=r"Warnings logging has already been enabled" + ): log.enable_warnings_logging() - assert e.value.args[0] == 'Warnings logging has already been enabled' def test_warnings_logging_overridden(): log.enable_warnings_logging() warnings.showwarning = lambda: None - with pytest.raises(LoggingError) as e: + with pytest.raises( + LoggingError, + match=r"Cannot disable warnings logging: " + r"warnings\.showwarning was not set by this logger, or has been overridden", + ): log.disable_warnings_logging() - assert e.value.args[0] == 'Cannot disable warnings logging: warnings.showwarning was not set by this logger, or has been overridden' def test_warnings_logging(): - # Without warnings logging - with warnings.catch_warnings(record=True) as warn_list: + with pytest.warns(AstropyUserWarning, match="This is a warning") as warn_list: with log.log_to_list() as log_list: - warnings.warn("This is a warning") + warnings.warn("This is a warning", AstropyUserWarning) assert len(log_list) == 0 assert len(warn_list) == 1 - assert warn_list[0].message.args[0] == "This is a warning" # With warnings logging with warnings.catch_warnings(record=True) as warn_list: log.enable_warnings_logging() with log.log_to_list() as log_list: - warnings.warn("This is a warning") + warnings.warn("This is a warning", AstropyUserWarning) log.disable_warnings_logging() assert len(log_list) == 1 assert len(warn_list) == 0 - assert log_list[0].levelname == 'WARNING' - assert log_list[0].message == 'This is a warning' - assert log_list[0].origin == 'astropy.tests.test_logger' + assert log_list[0].levelname == "WARNING" + assert log_list[0].message.startswith("This is a warning") + assert log_list[0].origin == "astropy.tests.test_logger" + + # With warnings logging (differentiate between Astropy and non-Astropy) + with pytest.warns( + UserWarning, match="This is another warning, not from Astropy" + ) as warn_list: + log.enable_warnings_logging() + with log.log_to_list() as log_list: + warnings.warn("This is a warning", AstropyUserWarning) + warnings.warn("This is another warning, not from Astropy") + log.disable_warnings_logging() + assert len(log_list) == 1 + assert len(warn_list) == 1 + assert log_list[0].levelname == "WARNING" + assert log_list[0].message.startswith("This is a warning") + assert log_list[0].origin == "astropy.tests.test_logger" # Without warnings logging - with warnings.catch_warnings(record=True) as warn_list: + with pytest.warns(AstropyUserWarning, match="This is a warning") as warn_list: with log.log_to_list() as log_list: - warnings.warn("This is a warning") + warnings.warn("This is a warning", AstropyUserWarning) assert len(log_list) == 0 assert len(warn_list) == 1 - assert warn_list[0].message.args[0] == "This is a warning" def test_warnings_logging_with_custom_class(): - class CustomWarningClass(Warning): + class CustomAstropyWarningClass(AstropyWarning): pass # With warnings logging with warnings.catch_warnings(record=True) as warn_list: log.enable_warnings_logging() with log.log_to_list() as log_list: - warnings.warn("This is a warning", CustomWarningClass) + warnings.warn("This is a warning", CustomAstropyWarningClass) log.disable_warnings_logging() assert len(log_list) == 1 assert len(warn_list) == 0 - assert log_list[0].levelname == 'WARNING' - assert log_list[0].message == 'CustomWarningClass: This is a warning' - assert log_list[0].origin == 'astropy.tests.test_logger' + assert log_list[0].levelname == "WARNING" + assert log_list[0].message.startswith( + "CustomAstropyWarningClass: This is a warning" + ) + assert log_list[0].origin == "astropy.tests.test_logger" -def test_warning_logging_with_io_vo_warning(): - from ..io.vo.exceptions import W02, vo_warn +def test_warning_logging_with_io_votable_warning(): + from astropy.io.votable.exceptions import W02, vo_warn with warnings.catch_warnings(record=True) as warn_list: log.enable_warnings_logging() with log.log_to_list() as log_list: - vo_warn(W02, ('a', 'b')) + vo_warn(W02, ("a", "b")) log.disable_warnings_logging() assert len(log_list) == 1 assert len(warn_list) == 0 - assert log_list[0].levelname == 'WARNING' - assert log_list[0].message == ("W02: ?:?:?: W02: a attribute 'b' is " - "invalid. Must be a standard XML id") - assert log_list[0].origin == 'astropy.tests.test_logger' + assert log_list[0].levelname == "WARNING" + x = log_list[0].message.startswith( + "W02: ?:?:?: W02: a attribute 'b' is invalid. Must be a standard XML id" + ) + assert x + assert log_list[0].origin == "astropy.tests.test_logger" + + +def test_import_error_in_warning_logging(): + """ + Regression test for https://github.com/astropy/astropy/issues/2671 + + This test actually puts a goofy fake module into ``sys.modules`` to test + this problem. + """ + + class FakeModule: + def __getattr__(self, attr): + raise ImportError("_showwarning should ignore any exceptions here") + + log.enable_warnings_logging() + + sys.modules[""] = FakeModule() + try: + warnings.showwarning( + AstropyWarning("Regression test for #2671"), + AstropyWarning, + "", + 1, + ) + finally: + del sys.modules[""] def test_exception_logging_disable_no_enable(): - with pytest.raises(LoggingError) as e: + with pytest.raises(LoggingError, match=r"Exception logging has not been enabled"): log.disable_exception_logging() - assert e.value.args[0] == 'Exception logging has not been enabled' def test_exception_logging_enable_twice(): log.enable_exception_logging() - with pytest.raises(LoggingError) as e: + with pytest.raises( + LoggingError, match=r"Exception logging has already been enabled" + ): log.enable_exception_logging() - assert e.value.args[0] == 'Exception logging has already been enabled' -# You can't really override the exception handler in IPython this way, so -# this test doesn't really make sense in the IPython context. -@pytest.mark.skipif("ip is not None") +@pytest.mark.skipif( + _WITHIN_IPYTHON, reason="Cannot override exception handler in IPython" +) def test_exception_logging_overridden(): log.enable_exception_logging() - sys.excepthook = lambda: None - with pytest.raises(LoggingError) as e: + sys.excepthook = lambda etype, evalue, tb: None + with pytest.raises( + LoggingError, + match=( + "Cannot disable exception logging: " + "sys.excepthook was not set by this logger, or has been overridden" + ), + ): log.disable_exception_logging() - assert e.value.args[0] == 'Cannot disable exception logging: sys.excepthook was not set by this logger, or has been overridden' +@pytest.mark.xfail("_WITHIN_IPYTHON") def test_exception_logging(): - # Without exception logging - try: + with pytest.raises(Exception, match="This is an Exception"): with log.log_to_list() as log_list: raise Exception("This is an Exception") - except Exception as exc: - sys.excepthook(*sys.exc_info()) - assert exc.args[0] == "This is an Exception" - else: - assert False # exception should have been raised assert len(log_list) == 0 - # With exception logging + # With exception logging. Note that this test can't use `pytest.raises` because it + # cleans the exception chain, so the exception is not logged. try: log.enable_exception_logging() with log.log_to_list() as log_list: @@ -177,13 +218,14 @@ def test_exception_logging(): sys.excepthook(*sys.exc_info()) assert exc.args[0] == "This is an Exception" else: - assert False # exception should have been raised + raise AssertionError() # exception should have been raised assert len(log_list) == 1 - assert log_list[0].levelname == 'ERROR' - assert log_list[0].message == 'Exception: This is an Exception' - assert log_list[0].origin == 'astropy.tests.test_logger' + assert log_list[0].levelname == "ERROR" + assert log_list[0].message.startswith("Exception: This is an Exception") + assert log_list[0].origin == "astropy.tests.test_logger" - # Without exception logging + # Without exception logging. Note that this test can't use `pytest.raises` because + # it cleans the exception chain, so any exception is not logged, regardless. log.disable_exception_logging() try: with log.log_to_list() as log_list: @@ -192,203 +234,276 @@ def test_exception_logging(): sys.excepthook(*sys.exc_info()) assert exc.args[0] == "This is an Exception" else: - assert False # exception should have been raised + raise AssertionError() # exception should have been raised assert len(log_list) == 0 +@pytest.mark.xfail("_WITHIN_IPYTHON") def test_exception_logging_origin(): # The point here is to get an exception raised from another location # and make sure the error's origin is reported correctly - from ..utils.collections import HomogeneousList + from astropy.utils.collections import HomogeneousList - l = HomogeneousList(int) + lst = HomogeneousList(int) try: log.enable_exception_logging() with log.log_to_list() as log_list: - l.append('foo') + lst.append("foo") except TypeError as exc: sys.excepthook(*sys.exc_info()) assert exc.args[0].startswith( - "homogeneous list must contain only objects of type ") + "homogeneous list must contain only objects of type " + ) else: - assert False + raise AssertionError() assert len(log_list) == 1 - assert log_list[0].levelname == 'ERROR' + assert log_list[0].levelname == "ERROR" assert log_list[0].message.startswith( - "TypeError: homogeneous list must contain only objects of type ") - assert log_list[0].origin == 'astropy.utils.collections' + "TypeError: homogeneous list must contain only objects of type " + ) + assert log_list[0].origin == "astropy.utils.collections" + + +@pytest.mark.skip(reason="Infinite recursion on Python 3.5+, probably a real issue") +# @pytest.mark.xfail("ip is not None") +def test_exception_logging_argless_exception(): + """ + Regression test for a crash that occurred on Python 3 when logging an + exception that was instantiated with no arguments (no message, etc.) + Regression test for https://github.com/astropy/astropy/pull/4056 + """ -@pytest.mark.parametrize(('level'), [None, 'DEBUG', 'INFO', 'WARN', 'ERROR']) + try: + log.enable_exception_logging() + with log.log_to_list() as log_list: + raise Exception() + except Exception: + sys.excepthook(*sys.exc_info()) + else: + raise AssertionError() # exception should have been raised + assert len(log_list) == 1 + assert log_list[0].levelname == "ERROR" + assert log_list[0].message == "Exception [astropy.tests.test_logger]" + assert log_list[0].origin == "astropy.tests.test_logger" + + +@pytest.mark.parametrize("level", [None, "DEBUG", "INFO", "WARN", "ERROR"]) def test_log_to_list(level): + orig_level = log.level - if level is not None: - log.setLevel(level) + try: + if level is not None: + log.setLevel(level) - with log.log_to_list() as log_list: - log.error("Error message") - log.warn("Warning message") - log.info("Information message") - log.debug("Debug message") + with log.log_to_list() as log_list: + log.error("Error message") + log.warning("Warning message") + log.info("Information message") + log.debug("Debug message") + finally: + log.setLevel(orig_level) + + if level is None: + # The log level *should* be set to whatever it was in the config + level = conf.log_level # Check list length - if level == 'DEBUG': + if level == "DEBUG": assert len(log_list) == 4 - elif level is None or level == 'INFO': + elif level == "INFO": assert len(log_list) == 3 - elif level == 'WARN': + elif level == "WARN": assert len(log_list) == 2 - elif level == 'ERROR': + elif level == "ERROR": assert len(log_list) == 1 # Check list content - assert log_list[0].levelname == 'ERROR' - assert log_list[0].message == 'Error message' - assert log_list[0].origin == 'astropy.tests.test_logger' + assert log_list[0].levelname == "ERROR" + assert log_list[0].message.startswith("Error message") + assert log_list[0].origin == "astropy.tests.test_logger" if len(log_list) >= 2: - assert log_list[1].levelname == 'WARNING' - assert log_list[1].message == 'Warning message' - assert log_list[1].origin == 'astropy.tests.test_logger' + assert log_list[1].levelname == "WARNING" + assert log_list[1].message.startswith("Warning message") + assert log_list[1].origin == "astropy.tests.test_logger" if len(log_list) >= 3: - assert log_list[2].levelname == 'INFO' - assert log_list[2].msg == 'Information message' - assert log_list[2].origin == 'astropy.tests.test_logger' + assert log_list[2].levelname == "INFO" + assert log_list[2].message.startswith("Information message") + assert log_list[2].origin == "astropy.tests.test_logger" if len(log_list) >= 4: - assert log_list[3].levelname == 'DEBUG' - assert log_list[3].msg == 'Debug message' - assert log_list[3].origin == 'astropy.tests.test_logger' + assert log_list[3].levelname == "DEBUG" + assert log_list[3].message.startswith("Debug message") + assert log_list[3].origin == "astropy.tests.test_logger" def test_log_to_list_level(): - - with log.log_to_list(filter_level='ERROR') as log_list: + with log.log_to_list(filter_level="ERROR") as log_list: log.error("Error message") - log.warn("Warning message") + log.warning("Warning message") - assert len(log_list) == 1 and log_list[0].levelname == 'ERROR' + assert len(log_list) == 1 and log_list[0].levelname == "ERROR" def test_log_to_list_origin1(): - - with log.log_to_list(filter_origin='astropy.tests') as log_list: + with log.log_to_list(filter_origin="astropy.tests") as log_list: log.error("Error message") - log.warn("Warning message") + log.warning("Warning message") assert len(log_list) == 2 def test_log_to_list_origin2(): - - with log.log_to_list(filter_origin='astropy.wcs') as log_list: + with log.log_to_list(filter_origin="astropy.wcs") as log_list: log.error("Error message") - log.warn("Warning message") + log.warning("Warning message") assert len(log_list) == 0 -@pytest.mark.parametrize(('level'), [None, 'DEBUG', 'INFO', 'WARN', 'ERROR']) -def test_log_to_file(tmpdir, level): +@pytest.mark.parametrize("level", [None, "DEBUG", "INFO", "WARN", "ERROR"]) +def test_log_to_file(tmp_path, level): + local_path = tmp_path / "test.log" + log_file = local_path.open("wb") + log_path = str(local_path.resolve()) + orig_level = log.level - local_path = tmpdir.join('test.log') - log_file = local_path.open('wb') - log_path = str(local_path.realpath()) - - if level is not None: - log.setLevel(level) + try: + if level is not None: + log.setLevel(level) - with log.log_to_file(log_path): - log.error("Error message") - log.warn("Warning message") - log.info("Information message") - log.debug("Debug message") + with log.log_to_file(log_path): + log.error("Error message") + log.warning("Warning message") + log.info("Information message") + log.debug("Debug message") - log_file.close() + log_file.close() + finally: + log.setLevel(orig_level) - log_file = local_path.open('rb') + log_file = local_path.open("rb") log_entries = log_file.readlines() log_file.close() + if level is None: + # The log level *should* be set to whatever it was in the config + level = conf.log_level + # Check list length - if level == 'DEBUG': + if level == "DEBUG": assert len(log_entries) == 4 - elif level is None or level == 'INFO': + elif level == "INFO": assert len(log_entries) == 3 - elif level == 'WARN': + elif level == "WARN": assert len(log_entries) == 2 - elif level == 'ERROR': + elif level == "ERROR": assert len(log_entries) == 1 # Check list content - assert log_entries[0].strip().endswith(b"'astropy.tests.test_logger', 'ERROR', 'Error message'") + assert eval(log_entries[0].strip())[-3:] == ( + "astropy.tests.test_logger", + "ERROR", + "Error message", + ) if len(log_entries) >= 2: - assert log_entries[1].strip().endswith(b"'astropy.tests.test_logger', 'WARNING', 'Warning message'") + assert eval(log_entries[1].strip())[-3:] == ( + "astropy.tests.test_logger", + "WARNING", + "Warning message", + ) if len(log_entries) >= 3: - assert log_entries[2].strip().endswith(b"'astropy.tests.test_logger', 'INFO', 'Information message'") + assert eval(log_entries[2].strip())[-3:] == ( + "astropy.tests.test_logger", + "INFO", + "Information message", + ) if len(log_entries) >= 4: - assert log_entries[3].strip().endswith(b"'astropy.tests.test_logger', 'DEBUG', 'Debug message'") + assert eval(log_entries[3].strip())[-3:] == ( + "astropy.tests.test_logger", + "DEBUG", + "Debug message", + ) -def test_log_to_file_level(tmpdir): +def test_log_to_file_level(tmp_path): + local_path = tmp_path / "test.log" + log_file = local_path.open("wb") + log_path = str(local_path.resolve()) - local_path = tmpdir.join('test.log') - log_file = local_path.open('wb') - log_path = str(local_path.realpath()) - - with log.log_to_file(log_path, filter_level='ERROR'): + with log.log_to_file(log_path, filter_level="ERROR"): log.error("Error message") - log.warn("Warning message") + log.warning("Warning message") log_file.close() - log_file = local_path.open('rb') + log_file = local_path.open("rb") log_entries = log_file.readlines() log_file.close() - assert len(log_entries) == 1 and log_entries[0].strip().endswith(b"'ERROR', 'Error message'") - + assert len(log_entries) == 1 + assert eval(log_entries[0].strip())[-2:] == ("ERROR", "Error message") -def test_log_to_file_origin1(tmpdir): - local_path = tmpdir.join('test.log') - log_file = local_path.open('wb') - log_path = str(local_path.realpath()) +def test_log_to_file_origin1(tmp_path): + local_path = tmp_path / "test.log" + log_file = local_path.open("wb") + log_path = str(local_path.resolve()) - with log.log_to_file(log_path, filter_origin='astropy.tests'): + with log.log_to_file(log_path, filter_origin="astropy.tests"): log.error("Error message") - log.warn("Warning message") + log.warning("Warning message") log_file.close() - log_file = local_path.open('rb') + log_file = local_path.open("rb") log_entries = log_file.readlines() log_file.close() assert len(log_entries) == 2 -def test_log_to_file_origin2(tmpdir): +def test_log_to_file_origin2(tmp_path): + local_path = tmp_path / "test.log" + log_file = local_path.open("wb") + log_path = str(local_path.resolve()) - local_path = tmpdir.join('test.log') - log_file = local_path.open('wb') - log_path = str(local_path.realpath()) - - with log.log_to_file(log_path, filter_origin='astropy.wcs'): + with log.log_to_file(log_path, filter_origin="astropy.wcs"): log.error("Error message") - log.warn("Warning message") + log.warning("Warning message") log_file.close() - log_file = local_path.open('rb') + log_file = local_path.open("rb") log_entries = log_file.readlines() log_file.close() assert len(log_entries) == 0 + + +@pytest.mark.parametrize("encoding", ["", "utf-8", "cp1252"]) +def test_log_to_file_encoding(tmp_path, encoding): + local_path = tmp_path / "test.log" + log_path = str(local_path.resolve()) + + orig_encoding = conf.log_file_encoding + + conf.log_file_encoding = encoding + + with log.log_to_file(log_path): + for handler in log.handlers: + if isinstance(handler, logging.FileHandler): + if encoding: + assert handler.stream.encoding == encoding + else: + assert handler.stream.encoding == locale.getpreferredencoding() + + conf.log_file_encoding = orig_encoding diff --git a/astropy/tests/tests/run_after_2to3.py b/astropy/tests/tests/run_after_2to3.py deleted file mode 100644 index ecd8c6001b7d..000000000000 --- a/astropy/tests/tests/run_after_2to3.py +++ /dev/null @@ -1,10 +0,0 @@ -# Licensed under a 3-clause BSD style license - see LICENSE.rst - - -# This module is not a test module, but is used as part of the -# test_run_after_2to3 test in test_run_tests.py -def test_run_after_2to3(): - try: - 1 / 0 - except ZeroDivisionError, e: - pass diff --git a/astropy/tests/tests/test_imports.py b/astropy/tests/tests/test_imports.py new file mode 100644 index 000000000000..47db46754984 --- /dev/null +++ b/astropy/tests/tests/test_imports.py @@ -0,0 +1,69 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +import pkgutil +import subprocess +import sys +from pathlib import Path +from textwrap import dedent +from types import ModuleType + +import pytest + +import astropy + + +@pytest.mark.parametrize( + "subpkg", + [subpkg.name for subpkg in pkgutil.walk_packages(astropy.__path__) if subpkg.ispkg], +) +def test_imports(subpkg): + """ + This just imports all top-level sub-packages in astropy, making sure they don't have any + dependencies that sneak through + """ + subprocess.run([sys.executable, "-c", f"from astropy import {subpkg}"], check=True) + + +def test_toplevel_namespace(): + import astropy + + d = dir(astropy) + assert "os" not in d + assert "log" in d + assert "test" in d + assert "sys" not in d + + +def test_toplevel_lazy_imports(): + # Check that subpackages are loaded on demand. + cmd = dedent(""" + import astropy, sys + assert 'astropy.units' not in sys.modules + astropy.units + assert 'astropy.units' in sys.modules + """) + cp = subprocess.check_call([sys.executable, "-c", cmd]) + assert cp == 0 + + +def test_toplevel_attribute_error(): + # Ensure that our __getattr__ does not leak an import error or so. + with pytest.raises(AttributeError, match="module 'astropy' has no"): + astropy.nonsense + + +def test_completeness_toplevel__all__(): + # Implicitly check that all items in __all__ exist. + all_items = {getattr(astropy, attr) for attr in astropy.__all__} + # Verify that the list of modules in __all__ is complete. + module_names = { + item.__name__.partition(".")[2] + for item in all_items + if isinstance(item, ModuleType) + } + module_dirs = { + f.name + for f in Path(astropy.__file__).parent.glob("[a-z]*") + if f.is_dir() and f.name != "extern" + } + assert module_names == module_dirs diff --git a/astropy/tests/tests/test_quantity_helpers.py b/astropy/tests/tests/test_quantity_helpers.py new file mode 100644 index 000000000000..497c0f0f5a05 --- /dev/null +++ b/astropy/tests/tests/test_quantity_helpers.py @@ -0,0 +1,45 @@ +import pytest + +from astropy import units as u +from astropy.tests.helper import assert_quantity_allclose + + +def test_assert_quantity_allclose(): + assert_quantity_allclose([1, 2], [1, 2]) + + assert_quantity_allclose([1, 2] * u.m, [100, 200] * u.cm) + + assert_quantity_allclose([1, 2] * u.m, [101, 201] * u.cm, atol=2 * u.cm) + + with pytest.raises(AssertionError, match=r"\nNot equal to tolerance"): + assert_quantity_allclose([1, 2] * u.m, [90, 200] * u.cm) + + with pytest.raises(AssertionError): + assert_quantity_allclose([1, 2] * u.m, [101, 201] * u.cm, atol=0.5 * u.cm) + + with pytest.raises( + u.UnitsError, + match=r"Units for 'desired' \(\) and 'actual' \(m\) are not convertible", + ): + assert_quantity_allclose([1, 2] * u.m, [100, 200]) + + with pytest.raises( + u.UnitsError, + match=r"Units for 'desired' \(cm\) and 'actual' \(\) are not convertible", + ): + assert_quantity_allclose([1, 2], [100, 200] * u.cm) + + with pytest.raises( + u.UnitsError, + match=r"Units for 'atol' \(\) and 'actual' \(m\) are not convertible", + ): + assert_quantity_allclose([1, 2] * u.m, [100, 200] * u.cm, atol=0.3) + + with pytest.raises( + u.UnitsError, + match=r"Units for 'atol' \(m\) and 'actual' \(\) are not convertible", + ): + assert_quantity_allclose([1, 2], [1, 2], atol=0.3 * u.m) + + with pytest.raises(u.UnitsError, match=r"'rtol' should be dimensionless"): + assert_quantity_allclose([1, 2], [1, 2], rtol=0.3 * u.m) diff --git a/astropy/tests/tests/test_run_tests.py b/astropy/tests/tests/test_run_tests.py index c58c0d24b337..3798d14efff9 100644 --- a/astropy/tests/tests/test_run_tests.py +++ b/astropy/tests/tests/test_run_tests.py @@ -1,27 +1,24 @@ # Licensed under a 3-clause BSD style license - see LICENSE.rst +import pytest # test helper.run_tests function - -from .. import helper -from ... import _get_test_runner +from astropy import test as run_tests +from astropy.utils.exceptions import AstropyDeprecationWarning # run_tests should raise ValueError when asked to run on a module it can't find def test_module_not_found(): - with helper.pytest.raises(ValueError): - _get_test_runner().run_tests('fake.module') + with ( + pytest.raises(ValueError), + pytest.warns(AstropyDeprecationWarning, match="The test runner"), + ): + run_tests(package="fake.module") # run_tests should raise ValueError when passed an invalid pastebin= option def test_pastebin_keyword(): - with helper.pytest.raises(ValueError): - _get_test_runner().run_tests(pastebin='not_an_option') - - -# tests that tests are only run in Python 3 out of the 2to3'd build (otherwise -# a syntax error would occur) -try: - from .run_after_2to3 import test_run_after_2to3 -except SyntaxError: - def test_run_after_2to3(): - helper.pytest.fail("Not running the 2to3'd tests!") + with ( + pytest.raises(ValueError), + pytest.warns(AstropyDeprecationWarning, match="The test runner"), + ): + run_tests(pastebin="not_an_option") diff --git a/astropy/tests/tests/test_runner.py b/astropy/tests/tests/test_runner.py new file mode 100644 index 000000000000..0100b8349869 --- /dev/null +++ b/astropy/tests/tests/test_runner.py @@ -0,0 +1,107 @@ +import pytest + +from astropy.tests.helper import _skip_docstring_tests_with_optimized_python + +# Renamed these imports so that them being in the namespace will not +# cause pytest 3 to discover them as tests and then complain that +# they have __init__ defined. +from astropy.tests.runner import TestRunner as _TestRunner +from astropy.tests.runner import TestRunnerBase as _TestRunnerBase +from astropy.tests.runner import keyword +from astropy.utils.exceptions import AstropyDeprecationWarning + + +def test_disable_kwarg(): + class no_remote_data(_TestRunner): + @keyword() + def remote_data(self, remote_data, kwargs): + return NotImplemented + + with pytest.warns(AstropyDeprecationWarning, match="The TestRunner"): + r = no_remote_data(".") + with ( + pytest.raises(TypeError), + pytest.warns(AstropyDeprecationWarning, match="The test runner"), + ): + r.run_tests(remote_data="bob") + + +def test_wrong_kwarg(): + with pytest.warns(AstropyDeprecationWarning, match="The TestRunner"): + r = _TestRunner(".") + with ( + pytest.raises(TypeError), + pytest.warns(AstropyDeprecationWarning, match="The test runner"), + ): + r.run_tests(spam="eggs") + + +def test_invalid_kwarg(): + class bad_return(_TestRunnerBase): + @keyword() + def remote_data(self, remote_data, kwargs): + return "bob" + + with pytest.warns(AstropyDeprecationWarning, match="The TestRunner"): + r = bad_return(".") + with ( + pytest.raises(TypeError), + pytest.warns(AstropyDeprecationWarning, match="The test runner"), + ): + r.run_tests(remote_data="bob") + + +def test_new_kwarg(): + class Spam(_TestRunnerBase): + @keyword() + def spam(self, spam, kwargs): + return [spam] + + with pytest.warns(AstropyDeprecationWarning, match="The TestRunner"): + r = Spam(".") + + args = r._generate_args(spam="spam") + + assert ["spam"] == args + + +def test_priority(): + class Spam(_TestRunnerBase): + @keyword() + def spam(self, spam, kwargs): + return [spam] + + @keyword(priority=1) + def eggs(self, eggs, kwargs): + return [eggs] + + with pytest.warns(AstropyDeprecationWarning, match="The TestRunner"): + r = Spam(".") + + args = r._generate_args(spam="spam", eggs="eggs") + + assert ["eggs", "spam"] == args + + +@_skip_docstring_tests_with_optimized_python +def test_docs(): + class Spam(_TestRunnerBase): + @keyword() + def spam(self, spam, kwargs): + """ + Spam Spam Spam + """ + return [spam] + + @keyword() + def eggs(self, eggs, kwargs): + """ + eggs asldjasljd + """ + return [eggs] + + with pytest.warns(AstropyDeprecationWarning, match="The TestRunner"): + r = Spam(".") + assert "deprecated" in r.run_tests.__doc__ + assert "eggs" in r.run_tests.__doc__ + assert "Spam Spam Spam" in r.run_tests.__doc__ diff --git a/astropy/tests/tests/test_skip_remote_data.py b/astropy/tests/tests/test_skip_remote_data.py deleted file mode 100644 index 3a26dfd35485..000000000000 --- a/astropy/tests/tests/test_skip_remote_data.py +++ /dev/null @@ -1,23 +0,0 @@ -# Licensed under a 3-clause BSD style license - see LICENSE.rst -# this test doesn't actually use any online data, it should just be skipped -# by run_tests because it has the remote_data decorator. - -from ..helper import remote_data -from ..helper import pytest - - -@remote_data -def test_skip_remote_data(pytestconfig): - # this test was called from the command line and it should behave as if - # astropy.test() has remote_data=True - if not hasattr(pytestconfig.option, 'remotedata'): - assert True - - # astropy.test() has remote_data=False but we still got here somehow, - # so fail with a helpful message - elif not getattr(pytestconfig.option, 'remotedata'): - pytest.fail('@remote_data was not skipped with remote_data=False') - - # astropy.test() has remote_data=True, so pass - elif getattr(pytestconfig.option, 'remotedata'): - assert True diff --git a/astropy/time/__init__.py b/astropy/time/__init__.py new file mode 100644 index 000000000000..825b8c074fd5 --- /dev/null +++ b/astropy/time/__init__.py @@ -0,0 +1,50 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +from astropy import config as _config + + +class Conf(_config.ConfigNamespace): + """ + Configuration parameters for `astropy.time`. + """ + + use_fast_parser = _config.ConfigItem( + ["True", "False", "force"], + "Use fast C parser for supported time strings formats, including ISO, " + "ISOT, and YearDayTime. Allowed values are the 'False' (use Python parser)," + "'True' (use C parser and fall through to Python parser if fails), and " + "'force' (use C parser and raise exception if it fails). Note that the" + "options are all strings.", + ) + + masked_array_type = _config.ConfigItem( + ["astropy", "numpy"], + 'The type of masked array used for masked output data. Can be "astropy" ' + 'for `astropy.utils.masked.Masked` or "numpy" to use `numpy.ma.MaskedArray`. ' + "Note that if `astropy.units.Quantity` is produced, the output always " + "uses `astropy.utils.masked.Masked`, since `numpy.ma.MaskedArray` does not " + "work with quantities.", + ) + # Create a dict of available masked classes for speed. + # Use local imports so we do not pollute the module namespace. + from numpy.ma import MaskedArray + + from astropy.utils.masked import Masked + + _MASKED_CLASSES = {"astropy": Masked, "numpy": MaskedArray} + + @property + def _masked_cls(self): + """The masked class set by ``masked_array_type``. + + This is |Masked| for "astropy", `numpy.ma.MaskedArray` for "numpy". + """ + return self._MASKED_CLASSES[self.masked_array_type] + + +conf = Conf() + +# isort: off +from .formats import * +from .core import * + +# isort: on diff --git a/astropy/time/core.py b/astropy/time/core.py new file mode 100644 index 000000000000..1e0b13c42a1f --- /dev/null +++ b/astropy/time/core.py @@ -0,0 +1,3422 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +""" +The astropy.time package provides functionality for manipulating times and +dates. Specific emphasis is placed on supporting time scales (e.g. UTC, TAI, +UT1) and time representations (e.g. JD, MJD, ISO 8601) that are used in +astronomy. +""" + +import copy +import enum +import operator +import os +import threading +from collections import defaultdict +from datetime import UTC, date, datetime +from itertools import pairwise +from time import strftime +from typing import TYPE_CHECKING, Union +from warnings import warn +from weakref import WeakValueDictionary + +import erfa +import numpy as np + +from astropy import constants as const +from astropy import units as u +from astropy.extern import _strptime +from astropy.units import UnitConversionError +from astropy.utils import lazyproperty +from astropy.utils.compat import NUMPY_LT_2_5 +from astropy.utils.data_info import MixinInfo, data_info_factory +from astropy.utils.decorators import deprecated +from astropy.utils.exceptions import AstropyDeprecationWarning, AstropyWarning +from astropy.utils.masked import ( + MaskableShapedLikeNDArray, + Masked, + combine_masks, + get_data_and_mask, +) + +# Below, import TimeFromEpoch to avoid breaking code that followed the old +# example of making a custom timescale in the documentation. +from . import conf +from .formats import ( + TIME_DELTA_FORMATS, + TIME_FORMATS, + TimeAstropyTime, + TimeDatetime, + TimeDeltaNumeric, + TimeFromEpoch, # noqa: F401 + TimeJD, + TimeUnique, +) +from .time_helper.function_helpers import CUSTOM_FUNCTIONS, UNSUPPORTED_FUNCTIONS +from .utils import day_frac + +if TYPE_CHECKING: + import astropy.coordinates + +__all__ = [ + "STANDARD_TIME_SCALES", + "TIME_DELTA_SCALES", + "TIME_SCALES", + "OperandTypeError", + "ScaleValueError", + "Time", + "TimeBase", + "TimeDelta", + "TimeDeltaMissingUnitWarning", + "TimeInfo", + "TimeInfoBase", + "update_leap_seconds", +] + + +STANDARD_TIME_SCALES = ("tai", "tcb", "tcg", "tdb", "tt", "ut1", "utc") +LOCAL_SCALES = ("local",) +TIME_TYPES = { + scale: scales for scales in (STANDARD_TIME_SCALES, LOCAL_SCALES) for scale in scales +} +TIME_SCALES = STANDARD_TIME_SCALES + LOCAL_SCALES +MULTI_HOPS = { + ("tai", "tcb"): ("tt", "tdb"), + ("tai", "tcg"): ("tt",), + ("tai", "ut1"): ("utc",), + ("tai", "tdb"): ("tt",), + ("tcb", "tcg"): ("tdb", "tt"), + ("tcb", "tt"): ("tdb",), + ("tcb", "ut1"): ("tdb", "tt", "tai", "utc"), + ("tcb", "utc"): ("tdb", "tt", "tai"), + ("tcg", "tdb"): ("tt",), + ("tcg", "ut1"): ("tt", "tai", "utc"), + ("tcg", "utc"): ("tt", "tai"), + ("tdb", "ut1"): ("tt", "tai", "utc"), + ("tdb", "utc"): ("tt", "tai"), + ("tt", "ut1"): ("tai", "utc"), + ("tt", "utc"): ("tai",), +} +GEOCENTRIC_SCALES = ("tai", "tt", "tcg") +BARYCENTRIC_SCALES = ("tcb", "tdb") +ROTATIONAL_SCALES = ("ut1",) +TIME_DELTA_TYPES = { + scale: scales + for scales in ( + GEOCENTRIC_SCALES, + BARYCENTRIC_SCALES, + ROTATIONAL_SCALES, + LOCAL_SCALES, + ) + for scale in scales +} +TIME_DELTA_SCALES = ( + GEOCENTRIC_SCALES + BARYCENTRIC_SCALES + ROTATIONAL_SCALES + LOCAL_SCALES +) +# For time scale changes, we need L_G and L_B, which are stored in erfam.h as +# /* L_G = 1 - d(TT)/d(TCG) */ +# define ERFA_ELG (6.969290134e-10) +# /* L_B = 1 - d(TDB)/d(TCB), and TDB (s) at TAI 1977/1/1.0 */ +# define ERFA_ELB (1.550519768e-8) +# These are exposed in erfa as erfa.ELG and erfa.ELB. +# Implied: d(TT)/d(TCG) = 1-L_G +# and d(TCG)/d(TT) = 1/(1-L_G) = 1 + (1-(1-L_G))/(1-L_G) = 1 + L_G/(1-L_G) +# scale offsets as second = first + first * scale_offset[(first,second)] +SCALE_OFFSETS = { + ("tt", "tai"): None, + ("tai", "tt"): None, + ("tcg", "tt"): -erfa.ELG, + ("tt", "tcg"): erfa.ELG / (1.0 - erfa.ELG), + ("tcg", "tai"): -erfa.ELG, + ("tai", "tcg"): erfa.ELG / (1.0 - erfa.ELG), + ("tcb", "tdb"): -erfa.ELB, + ("tdb", "tcb"): erfa.ELB / (1.0 - erfa.ELB), +} + +# triple-level dictionary, yay! +SIDEREAL_TIME_MODELS = { + "mean": { + "IAU2006": {"function": erfa.gmst06, "scales": ("ut1", "tt")}, + "IAU2000": {"function": erfa.gmst00, "scales": ("ut1", "tt")}, + "IAU1982": {"function": erfa.gmst82, "scales": ("ut1",), "include_tio": False}, + }, + "apparent": { + "IAU2006A": {"function": erfa.gst06a, "scales": ("ut1", "tt")}, + "IAU2000A": {"function": erfa.gst00a, "scales": ("ut1", "tt")}, + "IAU2000B": {"function": erfa.gst00b, "scales": ("ut1",)}, + "IAU1994": {"function": erfa.gst94, "scales": ("ut1",), "include_tio": False}, + }, +} + + +class _LeapSecondsCheck(enum.Enum): + NOT_STARTED = 0 # No thread has reached the check + RUNNING = 1 # A thread is running update_leap_seconds (_LEAP_SECONDS_LOCK is held) + DONE = 2 # update_leap_seconds has completed + + +_LEAP_SECONDS_CHECK = _LeapSecondsCheck.NOT_STARTED +_LEAP_SECONDS_LOCK = threading.RLock() + + +def _compress_array_dims(arr): + """Compress array by allowing at most 2 * edgeitems + 1 in each dimension. + + Parameters + ---------- + arr : array-like + Array to compress. + + Returns + ------- + out : array-like + Compressed array. + """ + idxs = [] + edgeitems = np.get_printoptions()["edgeitems"] + + # Build up a list of index arrays for each dimension, allowing no more than + # 2 * edgeitems + 1 elements in each dimension. + for dim in range(arr.ndim): + if arr.shape[dim] > 2 * edgeitems: + # The middle [edgeitems] value does not matter as it gets replaced + # by ... in the output. + idxs.append( + np.concatenate( + [np.arange(edgeitems), [edgeitems], np.arange(-edgeitems, 0)] + ) + ) + else: + idxs.append(np.arange(arr.shape[dim])) + + # Use the magic np.ix_ function to effectively treat each index array as a + # slicing operator. + idxs_ix = np.ix_(*idxs) + out = arr[idxs_ix] + return out + + +class TimeInfoBase(MixinInfo): + """ + Container for meta information like name, description, format. This is + required when the object is used as a mixin column within a table, but can + be used as a general way to store meta information. + + This base class is common between TimeInfo and TimeDeltaInfo. + """ + + attr_names = MixinInfo.attr_names | {"serialize_method"} + _supports_indexing = True + + # The usual tuple of attributes needed for serialization is replaced + # by a property, since Time can be serialized different ways. + _represent_as_dict_extra_attrs = ( + "format", + "scale", + "precision", + "in_subfmt", + "out_subfmt", + "location", + "_delta_ut1_utc", + "_delta_tdb_tt", + ) + + # When serializing, write out the `value` attribute using the column name. + _represent_as_dict_primary_data = "value" + + mask_val = np.ma.masked + + @property + def _represent_as_dict_attrs(self): + method = self.serialize_method[self._serialize_context] + + if method == "formatted_value": + out = ("value",) + elif method == "jd1_jd2": + out = ("jd1", "jd2") + else: + raise ValueError("serialize method must be 'formatted_value' or 'jd1_jd2'") + + return out + self._represent_as_dict_extra_attrs + + def __init__(self, bound=False): + super().__init__(bound) + + # If bound to a data object instance then create the dict of attributes + # which stores the info attribute values. + if bound: + # Specify how to serialize this object depending on context. + # If ``True`` for a context, then use formatted ``value`` attribute + # (e.g. the ISO time string). If ``False`` then use float jd1 and jd2. + self.serialize_method = { + "fits": "jd1_jd2", + "ecsv": "formatted_value", + "hdf5": "jd1_jd2", + "yaml": "jd1_jd2", + "parquet": "jd1_jd2", + None: "jd1_jd2", + } + + def get_sortable_arrays(self): + """ + Return a list of arrays which can be lexically sorted to represent + the order of the parent column. + + Returns + ------- + arrays : list of ndarray + """ + parent = self._parent + jd_approx = parent.jd + jd_remainder = (parent - parent.__class__(jd_approx, format="jd")).jd + return [jd_approx, jd_remainder] + + @property + def unit(self): + return None + + info_summary_stats = staticmethod( + data_info_factory( + names=MixinInfo._stats, + funcs=[getattr(np, stat) for stat in MixinInfo._stats], + ) + ) + # When Time has mean, std, min, max methods: + # funcs = [lambda x: getattr(x, stat)() for stat_name in MixinInfo._stats]) + + def _construct_from_dict(self, map): + if "jd1" in map and "jd2" in map: + # Initialize as JD but revert to desired format and out_subfmt (if needed) + format = map.pop("format") + out_subfmt = map.pop("out_subfmt", None) + map["format"] = "jd" + map["val"] = map.pop("jd1") + map["val2"] = map.pop("jd2") + out = self._parent_cls(**map) + out.format = format + if out_subfmt is not None: + out.out_subfmt = out_subfmt + + else: + map["val"] = map.pop("value") + out = self._parent_cls(**map) + + return out + + def new_like(self, cols, length, metadata_conflicts="warn", name=None): + """ + Return a new Time instance which is consistent with the input Time objects + ``cols`` and has ``length`` rows. + + This is intended for creating an empty Time instance whose elements can + be set in-place for table operations like join or vstack. It checks + that the input locations and attributes are consistent. This is used + when a Time object is used as a mixin column in an astropy Table. + + Parameters + ---------- + cols : list + List of input columns (Time objects) + length : int + Length of the output column object + metadata_conflicts : str ('warn'|'error'|'silent') + How to handle metadata conflicts + name : str + Output column name + + Returns + ------- + col : Time (or subclass) + Empty instance of this class consistent with ``cols`` + + """ + # Get merged info attributes like shape, dtype, format, description, etc. + attrs = self.merge_cols_attributes( + cols, metadata_conflicts, name, ("meta", "description") + ) + attrs.pop("dtype") # Not relevant for Time + col0 = cols[0] + + # Check that location is consistent for all Time objects + for col in cols[1:]: + # This is the method used by __setitem__ to ensure that the right side + # has a consistent location (and coerce data if necessary, but that does + # not happen in this case since `col` is already a Time object). If this + # passes then any subsequent table operations via setitem will work. + try: + col0._make_value_equivalent(slice(None), col) + except ValueError: + raise ValueError("input columns have inconsistent locations") + + # Make a new Time object with the desired shape and attributes + shape = (length,) + attrs.pop("shape") + jd2000 = 2451544.5 # Arbitrary JD value J2000.0 that will work with ERFA + jd1 = np.full(shape, jd2000, dtype="f8") + jd2 = np.zeros(shape, dtype="f8") + tm_attrs = { + attr: getattr(col0, attr) for attr in ("scale", "location", "precision") + } + out = self._parent_cls(jd1, jd2, format="jd", **tm_attrs) + out.format = col0.format + out.out_subfmt = col0.out_subfmt + out.in_subfmt = col0.in_subfmt + + # Set remaining info attributes + for attr, value in attrs.items(): + setattr(out.info, attr, value) + + return out + + +class TimeInfo(TimeInfoBase): + """ + Container for meta information like name, description, format. This is + required when the object is used as a mixin column within a table, but can + be used as a general way to store meta information. + """ + + def _represent_as_dict(self, attrs=None): + """Get the values for the parent ``attrs`` and return as a dict. + + By default, uses '_represent_as_dict_attrs'. + """ + map = super()._represent_as_dict(attrs=attrs) + + # TODO: refactor these special cases into the TimeFormat classes? + + # The datetime64 format requires special handling for ECSV (see #12840). + # The `value` has numpy dtype datetime64 but this is not an allowed + # datatype for ECSV. Instead convert to a string representation. + if ( + self._serialize_context == "ecsv" + and map["format"] == "datetime64" + and "value" in map + ): + map["value"] = map["value"].astype("U") + + # The datetime format is serialized as ISO with no loss of precision. + if map["format"] == "datetime" and "value" in map: + map["value"] = np.vectorize(lambda x: x.isoformat())(map["value"]) + + return map + + def _construct_from_dict(self, map): + # See comment above. May need to convert string back to datetime64. + # Note that _serialize_context is not set here so we just look for the + # string value directly. + if ( + map["format"] == "datetime64" + and "value" in map + and map["value"].dtype.kind == "U" + ): + map["value"] = map["value"].astype("datetime64") + + # Convert back to datetime objects for datetime format. + if map["format"] == "datetime" and "value" in map: + from datetime import datetime + + map["value"] = np.vectorize(datetime.fromisoformat)(map["value"]) + + delta_ut1_utc = map.pop("_delta_ut1_utc", None) + delta_tdb_tt = map.pop("_delta_tdb_tt", None) + + out = super()._construct_from_dict(map) + + if delta_ut1_utc is not None: + out._delta_ut1_utc = delta_ut1_utc + if delta_tdb_tt is not None: + out._delta_tdb_tt = delta_tdb_tt + + return out + + +class TimeDeltaInfo(TimeInfoBase): + """ + Container for meta information like name, description, format. This is + required when the object is used as a mixin column within a table, but can + be used as a general way to store meta information. + """ + + _represent_as_dict_extra_attrs = ("format", "scale") + + def new_like(self, cols, length, metadata_conflicts="warn", name=None): + """ + Return a new TimeDelta instance which is consistent with the input Time objects + ``cols`` and has ``length`` rows. + + This is intended for creating an empty Time instance whose elements can + be set in-place for table operations like join or vstack. It checks + that the input locations and attributes are consistent. This is used + when a Time object is used as a mixin column in an astropy Table. + + Parameters + ---------- + cols : list + List of input columns (Time objects) + length : int + Length of the output column object + metadata_conflicts : str ('warn'|'error'|'silent') + How to handle metadata conflicts + name : str + Output column name + + Returns + ------- + col : Time (or subclass) + Empty instance of this class consistent with ``cols`` + + """ + # Get merged info attributes like shape, dtype, format, description, etc. + attrs = self.merge_cols_attributes( + cols, metadata_conflicts, name, ("meta", "description") + ) + attrs.pop("dtype") # Not relevant for Time + col0 = cols[0] + + # Make a new Time object with the desired shape and attributes + shape = (length,) + attrs.pop("shape") + jd1 = np.zeros(shape, dtype="f8") + jd2 = np.zeros(shape, dtype="f8") + out = self._parent_cls(jd1, jd2, format="jd", scale=col0.scale) + out.format = col0.format + + # Set remaining info attributes + for attr, value in attrs.items(): + setattr(out.info, attr, value) + + return out + + +class TimeBase(MaskableShapedLikeNDArray): + """Base time class from which Time and TimeDelta inherit.""" + + # Make sure that reverse arithmetic (e.g., TimeDelta.__rmul__) + # gets called over the __mul__ of Numpy arrays. + __array_priority__ = 20000 + + # Declare that Time can be used as a Table column by defining the + # attribute where column attributes will be stored. + _astropy_column_attrs = None + + def __getnewargs__(self): + return (self._time,) + + def __getstate__(self): + # For pickling, we remove the cache from what's pickled + state = super().__getstate__().copy() + state.pop("_id_cache", None) + state.pop("cache", None) + return state + + def _init_from_vals( + self, + val, + val2, + format, + scale, + copy, + precision=None, + in_subfmt=None, + out_subfmt=None, + ): + """ + Set the internal _format, scale, and _time attrs from user + inputs. This handles coercion into the correct shapes and + some basic input validation. + """ + if in_subfmt is None: + in_subfmt = "*" + if out_subfmt is None: + out_subfmt = "*" + + # Coerce val into an array + val = _make_array(val, copy) + + # If val2 is not None, ensure consistency + if val2 is not None: + val2 = _make_array(val2, copy) + try: + np.broadcast(val, val2) + except ValueError: + raise ValueError( + "Input val and val2 have inconsistent shape; " + "they cannot be broadcast together." + ) + + if scale is not None: + if not (isinstance(scale, str) and scale.lower() in self.SCALES): + raise ScaleValueError( + f"Scale {scale!r} is not in the allowed scales " + f"{sorted(self.SCALES)}" + ) + + # If either of the input val, val2 are masked arrays then + # find the masked elements and fill them. + data1, mask1 = get_data_and_mask(val) + data2, mask2 = get_data_and_mask(val2) + mask = combine_masks([mask1, mask2]) + + # Parse / convert input values into internal jd1, jd2 based on format + self._time = self._get_time_fmt( + data1, data2, format, scale, precision, in_subfmt, out_subfmt, mask + ) + self._format = self._time.name + + # Hack from #9969 to allow passing the location value that has been + # collected by the TimeAstropyTime format class up to the Time level. + # TODO: find a nicer way. + if hasattr(self._time, "_location"): + self._location = self._time._location + del self._time._location + + # If any inputs were masked then mask both jd1 and jd2 accordingly, + # using a shared mask. From above, ``mask`` must be either Python + # bool False or an bool ndarray with the correct shape. + if mask is not False and np.any(mask): + # Ensure that if the class is already masked, we do not lose it. + self._time.jd1 = Masked(self._time.jd1, copy=False) + self._time.jd1.mask |= mask + # Ensure we share the mask (it may have been broadcast). + self._time.jd2 = Masked( + self._time.jd2, mask=self._time.jd1.mask, copy=False + ) + + def _get_time_fmt( + self, val, val2, format, scale, precision, in_subfmt, out_subfmt, mask + ): + """ + Given the supplied val, val2, format and scale try to instantiate + the corresponding TimeFormat class to convert the input values into + the internal jd1 and jd2. + + If format is `None` and the input is a string-type or object array then + guess available formats and stop when one matches. + """ + if format is None: + # If val and val2 broadcasted shape is (0,) (i.e. empty array input) then we + # cannot guess format from the input values. But a quantity is fine (as + # long as it has time units, but that will be checked later). + empty_array = val.size == 0 and (val2 is None or val2.size == 0) + if not (isinstance(self, TimeDelta) and isinstance(val, u.Quantity)) and ( + empty_array or np.all(mask) + ): + raise ValueError( + "cannot guess format from input values with zero-size array" + " or all elements masked" + ) + formats = [ + (name, cls) + for name, cls in self.FORMATS.items() + if issubclass(cls, TimeUnique) + ] + + # AstropyTime is a pseudo-format that isn't in the TIME_FORMATS registry, + # but try to guess it at the end. + if isinstance(self, Time): + formats.append(("astropy_time", TimeAstropyTime)) + + elif not isinstance(format, str): + raise TypeError("format must be a string") + + elif format.lower() not in self.FORMATS: + raise ValueError( + f"Format {format!r} is not one of the allowed formats " + f"{sorted(self.FORMATS)}" + ) + else: + formats = [(format, self.FORMATS[format])] + + masked = np.any(mask) + oval, oval2 = val, val2 + problems = {} + for name, cls in formats: + try: + if masked: + val, val2 = cls._fill_masked_values(oval, oval2, mask, in_subfmt) + return cls(val, val2, scale, precision, in_subfmt, out_subfmt) + except UnitConversionError: + raise + except (ValueError, TypeError) as err: + # If ``format`` specified then there is only one possibility, so raise + # immediately and include the upstream exception message to make it + # easier for user to see what is wrong. + if len(formats) == 1: + raise ValueError( + f"Input values did not match the format class {format}:" + + os.linesep + + f"{err.__class__.__name__}: {err}" + ) from err + else: + problems[name] = err + + message = ( + "Input values did not match any of the formats where the format " + "keyword is optional:\n" + ) + "\n".join(f"- '{name}': {err}" for name, err in problems.items()) + raise ValueError(message) + + @property + def writeable(self): + return self._time.jd1.flags.writeable & self._time.jd2.flags.writeable + + @writeable.setter + def writeable(self, value): + self._time.jd1.flags.writeable = value + self._time.jd2.flags.writeable = value + + @property + def format(self): + """ + Get or set time format. + + The format defines the way times are represented when accessed via the + ``.value`` attribute. By default it is the same as the format used for + initializing the `Time` instance, but it can be set to any other value + that could be used for initialization. These can be listed with:: + + >>> list(Time.FORMATS) + ['jd', 'mjd', 'decimalyear', 'unix', 'unix_tai', 'cxcsec', 'galexsec', 'gps', + 'plot_date', 'stardate', 'datetime', 'ymdhms', 'iso', 'isot', 'yday', + 'datetime64', 'fits', 'byear', 'jyear', 'byear_str', 'jyear_str'] + """ + return self._format + + @format.setter + def format(self, format): + """Set time format.""" + if format not in self.FORMATS: + raise ValueError(f"format must be one of {list(self.FORMATS)}") + format_cls = self.FORMATS[format] + + # Get the new TimeFormat object to contain time in new format. Possibly + # coerce in/out_subfmt to '*' (default) if existing subfmt values are + # not valid in the new format. + self._time = format_cls( + self._time.jd1, + self._time.jd2, + self._time._scale, + self.precision, + in_subfmt=format_cls._get_allowed_subfmt(self.in_subfmt), + out_subfmt=format_cls._get_allowed_subfmt(self.out_subfmt), + from_jd=True, + ) + + self._format = format + + def to_string(self): + """Output a string representation of the Time or TimeDelta object. + + Similar to ``str(self.value)`` (which uses numpy array formatting) but + array values are evaluated only for the items that actually are output. + For large arrays this can be a substantial performance improvement. + + Returns + ------- + out : str + String representation of the time values. + + """ + npo = np.get_printoptions() + if self.size < npo["threshold"]: + out = str(self.value) + else: + # Compress time object by allowing at most 2 * npo["edgeitems"] + 1 + # in each dimension. Then force numpy to use "summary mode" of + # showing only the edge items by setting the size threshold to 0. + # TODO: use np.core.arrayprint._leading_trailing if we have support for + # np.concatenate. See #8610. + tm = _compress_array_dims(self) + with np.printoptions(threshold=0): + out = str(tm.value) + return out + + def __repr__(self): + return ( + f"<{type(self).__name__} object: scale='{self.scale}' " + f"format='{self.format}' value={self.to_string()}>" + ) + + def __str__(self): + return self.to_string() + + def __hash__(self): + try: + loc = getattr(self, "location", None) + if loc is not None: + loc = loc.x.to_value(u.m), loc.y.to_value(u.m), loc.z.to_value(u.m) + + return hash((self.jd1, self.jd2, self.scale, loc)) + + except TypeError: + if self.ndim != 0: + reason = "(must be scalar)" + elif self.masked: + reason = "(value is masked)" + else: + raise + + raise TypeError(f"unhashable type: '{self.__class__.__name__}' {reason}") + + @property + def location(self) -> Union["astropy.coordinates.EarthLocation", None]: + return self._location + + @location.setter + def location(self, value): + if hasattr(self, "_location"): + # since astropy 6.1.0 + warn( + "Setting the location attribute post initialization will be " + "disallowed in a future version of Astropy. " + "Instead you should set the location when creating the Time object. " + "In the future, this will raise an AttributeError.", + category=FutureWarning, + stacklevel=2, + ) + self._location = value + + @property + def scale(self): + """Time scale.""" + return self._time.scale + + def _set_scale(self, scale): + """ + This is the key routine that actually does time scale conversions. + This is not public and not connected to the read-only scale property. + """ + if scale == self.scale: + return + if scale not in self.SCALES: + raise ValueError( + f"Scale {scale!r} is not in the allowed scales {sorted(self.SCALES)}" + ) + + if scale == "utc" or self.scale == "utc": + # If doing a transform involving UTC then check that the leap + # seconds table is up to date. + _check_leapsec() + + # Determine the chain of scale transformations to get from the current + # scale to the new scale. MULTI_HOPS contains a dict of all + # transformations (xforms) that require intermediate xforms. + # The MULTI_HOPS dict is keyed by (sys1, sys2) in alphabetical order. + xform = (self.scale, scale) + xform_sort = tuple(sorted(xform)) + multi = MULTI_HOPS.get(xform_sort, ()) + xforms = xform_sort[:1] + multi + xform_sort[-1:] + # If we made the reverse xform then reverse it now. + if xform_sort != xform: + xforms = tuple(reversed(xforms)) + + # Transform the jd1,2 pairs through the chain of scale xforms. + jd1, jd2 = self._time.jd1, self._time.jd2 + for sys1, sys2 in pairwise(xforms): + # Some xforms require an additional delta_ argument that is + # provided through Time methods. These values may be supplied by + # the user or computed based on available approximations. The + # get_delta_ methods are available for only one combination of + # sys1, sys2 though the property applies for both xform directions. + args = [jd1, jd2] + for sys12 in ((sys1, sys2), (sys2, sys1)): + dt_method = "_get_delta_{}_{}".format(*sys12) + try: + get_dt = getattr(self, dt_method) + except AttributeError: + pass + else: + args.append(get_dt(jd1, jd2)) + break + + conv_func = getattr(erfa, sys1 + sys2) + jd1, jd2 = conv_func(*args) + + jd1, jd2 = day_frac(jd1, jd2) + + self._time = self.FORMATS[self.format]( + jd1, + jd2, + scale, + self.precision, + self.in_subfmt, + self.out_subfmt, + from_jd=True, + ) + + @property + def precision(self): + """ + Decimal precision when outputting seconds as floating point (int + value between 0 and 9 inclusive). + """ + return self._time.precision + + @precision.setter + def precision(self, val): + del self.cache + self._time.precision = val + + @property + def in_subfmt(self): + """ + Unix wildcard pattern to select subformats for parsing string input + times. + """ + return self._time.in_subfmt + + @in_subfmt.setter + def in_subfmt(self, val): + self._time.in_subfmt = val + del self.cache + + @property + def out_subfmt(self): + """ + Unix wildcard pattern to select subformats for outputting times. + """ + return self._time.out_subfmt + + @out_subfmt.setter + def out_subfmt(self, val): + # Setting the out_subfmt property here does validation of ``val`` + self._time.out_subfmt = val + del self.cache + + @property + def shape(self): + """The shape of the time instances. + + Like `~numpy.ndarray.shape`, can be set to a new shape by assigning a + tuple. Note that if different instances share some but not all + underlying data, setting the shape of one instance can make the other + instance unusable. Hence, it is strongly recommended to get new, + reshaped instances with the ``reshape`` method. + + Raises + ------ + ValueError + If the new shape has the wrong total number of elements. + AttributeError + If the shape of the ``jd1``, ``jd2``, ``location``, + ``delta_ut1_utc``, or ``delta_tdb_tt`` attributes cannot be changed + without the arrays being copied. For these cases, use the + `Time.reshape` method (which copies any arrays that cannot be + reshaped in-place). + """ + return self._time.jd1.shape + + @shape.setter + def shape(self, shape): + del self.cache + + # We have to keep track of arrays that were already reshaped, + # since we may have to return those to their original shape if a later + # shape-setting fails. + reshaped = [] + oldshape = self.shape + + # In-place reshape of data/attributes. Need to access _time.jd1/2 not + # self.jd1/2 because the latter are not guaranteed to be the actual + # data, and in fact should not be directly changeable from the public + # API. + for obj, attr in ( + (self._time, "jd1"), + (self._time, "jd2"), + (self, "_delta_ut1_utc"), + (self, "_delta_tdb_tt"), + (self, "location"), + ): + val = getattr(obj, attr, None) + if val is None or val.size <= 1: + continue + try: + if NUMPY_LT_2_5: + val.shape = shape + else: + val._set_shape(shape) + except Exception: + for val2 in reshaped: + if NUMPY_LT_2_5: + val2.shape = oldshape + else: + val2._set_shape(oldshape) + raise + else: + reshaped.append(val) + + def _shaped_like_input(self, value): + if self.masked: + # Ensure the mask is independent. + value = conf._masked_cls(value, mask=self.mask.copy()) + # For new-style, we do not treat masked scalars differently from arrays. + if isinstance(value, Masked): + return value + + if self._time.jd1.shape: + if isinstance(value, np.ndarray): + return value + else: + raise TypeError( + f"JD is an array ({self._time.jd1!r}) but value is not ({value!r})" + ) + else: + # zero-dimensional array, is it safe to unbox? The tricky comparison + # of the mask is for the case that value is structured; otherwise, we + # could just use np.ma.is_masked(value). + if ( + isinstance(value, np.ndarray) + and not value.shape + and ( + (mask := getattr(value, "mask", np.False_)) == np.zeros_like(mask) + ).all() + ): + if value.dtype.kind == "M": + # existing test doesn't want datetime64 converted + return value[()] + elif value.dtype.fields: + # Unpack but keep field names; .item() doesn't + # Still don't get python types in the fields + return value[()] + else: + return value.item() + else: + return value + + @property + def jd1(self): + """ + First of the two doubles that internally store time value(s) in JD. + """ + return self._shaped_like_input(self._time.jd1) + + @property + def jd2(self): + """ + Second of the two doubles that internally store time value(s) in JD. + """ + return self._shaped_like_input(self._time.jd2) + + def to_value(self, format, subfmt="*"): + """Get time values expressed in specified output format. + + This method allows representing the ``Time`` object in the desired + output ``format`` and optional sub-format ``subfmt``. Available + built-in formats include ``jd``, ``mjd``, ``iso``, and so forth. Each + format can have its own sub-formats + + For built-in numerical formats like ``jd`` or ``unix``, ``subfmt`` can + be one of 'float', 'long', 'decimal', 'str', or 'bytes'. Here, 'long' + uses ``numpy.longdouble`` for somewhat enhanced precision (with + the enhancement depending on platform), and 'decimal' + :class:`decimal.Decimal` for full precision. For 'str' and 'bytes', the + number of digits is also chosen such that time values are represented + accurately. + + For built-in date-like string formats, one of 'date_hms', 'date_hm', or + 'date' (or 'longdate_hms', etc., for 5-digit years in + `~astropy.time.TimeFITS`). For sub-formats including seconds, the + number of digits used for the fractional seconds is as set by + `~astropy.time.Time.precision`. + + Parameters + ---------- + format : str + The format in which one wants the time values. Default: the current + format. + subfmt : str or None, optional + Value or wildcard pattern to select the sub-format in which the + values should be given. The default of '*' picks the first + available for a given format, i.e., 'float' or 'date_hms'. + If `None`, use the instance's ``out_subfmt``. + + """ + # TODO: add a precision argument (but ensure it is keyword argument + # only, to make life easier for TimeDelta.to_value()). + if format not in self.FORMATS: + raise ValueError(f"format must be one of {list(self.FORMATS)}") + + if subfmt is None: + if format == self.format: + subfmt = self.out_subfmt + else: + subfmt = self.FORMATS[format]._get_allowed_subfmt(self.out_subfmt) + + cache = self.cache["format"] + key = format, subfmt, conf.masked_array_type + value = cache.get(key) + if value is None: + if format == self.format: + tm = self + else: + tm = self.replicate(format=format) + + # Some TimeFormat subclasses may not be able to handle being passes + # on a out_subfmt. This includes some core classes like + # TimeBesselianEpochString that do not have any allowed subfmts. But + # those do deal with `self.out_subfmt` internally, so if subfmt is + # the same, we do not pass it on. + kwargs = {} + if subfmt is not None and subfmt != tm.out_subfmt: + kwargs["out_subfmt"] = subfmt + try: + value = tm._time.to_value(parent=tm, **kwargs) + except TypeError as exc: + # Try validating subfmt, e.g. for formats like 'jyear_str' that + # do not implement out_subfmt in to_value() (because there are + # no allowed subformats). If subfmt is not valid this gives the + # same exception as would have occurred if the call to + # `to_value()` had succeeded. + tm._time._select_subfmts(subfmt) + + # Subfmt was valid, so fall back to the original exception to see + # if it was lack of support for out_subfmt as a call arg. + if "unexpected keyword argument 'out_subfmt'" in str(exc): + raise ValueError( + f"to_value() method for format {format!r} does not " + "support passing a 'subfmt' argument" + ) from None + else: + # Some unforeseen exception so raise. + raise + + value = tm._shaped_like_input(value) + cache[key] = value + + return value + + @property + def value(self): + """Time value(s) in current format.""" + return self.to_value(self.format, None) + + @property + def mask(self): + if "mask" not in self.cache: + mask = getattr(self._time.jd2, "mask", None) + if mask is None: + mask = np.broadcast_to(np.False_, self._time.jd2.shape) + else: + # Take a view of any existing mask, so we can set it to readonly. + mask = mask.view() + mask.flags.writeable = False + self.cache["mask"] = mask + return self.cache["mask"] + + @property + def masked(self): + return isinstance(self._time.jd1, Masked) + + def insert(self, obj, values, axis=0): + """ + Insert values before the given indices in the column and return + a new `~astropy.time.Time` or `~astropy.time.TimeDelta` object. + + The values to be inserted must conform to the rules for in-place setting + of ``Time`` objects (see ``Get and set values`` in the ``Time`` + documentation). + + The API signature matches the ``np.insert`` API, but is more limited. + The specification of insert index ``obj`` must be a single integer, + and the ``axis`` must be ``0`` for simple row insertion before the + index. + + Parameters + ---------- + obj : int + Integer index before which ``values`` is inserted. + values : array-like + Value(s) to insert. If the type of ``values`` is different + from that of quantity, ``values`` is converted to the matching type. + axis : int, optional + Axis along which to insert ``values``. Default is 0, which is the + only allowed value and will insert a row. + + Returns + ------- + out : `~astropy.time.Time` subclass + New time object with inserted value(s) + + """ + # Validate inputs: obj arg is integer, axis=0, self is not a scalar, and + # input index is in bounds. + try: + idx0 = operator.index(obj) + except TypeError: + raise TypeError("obj arg must be an integer") + + if axis != 0: + raise ValueError("axis must be 0") + + if not self.shape: + raise TypeError( + f"cannot insert into scalar {self.__class__.__name__} object" + ) + + if abs(idx0) > len(self): + raise IndexError( + f"index {idx0} is out of bounds for axis 0 with size {len(self)}" + ) + + # Turn negative index into positive + if idx0 < 0: + idx0 = len(self) + idx0 + + # For non-Time object, use numpy to help figure out the length. (Note annoying + # case of a string input that has a length which is not the length we want). + if not isinstance(values, self.__class__): + values = np.asarray(values) + n_values = len(values) if values.shape else 1 + + # Finally make the new object with the correct length and set values for the + # three sections, before insert, the insert, and after the insert. + out = self.__class__.info.new_like( + [self], len(self) + n_values, name=self.info.name + ) + + out._time.jd1[:idx0] = self._time.jd1[:idx0] + out._time.jd2[:idx0] = self._time.jd2[:idx0] + + # This uses the Time setting machinery to coerce and validate as necessary. + out[idx0 : idx0 + n_values] = values + + out._time.jd1[idx0 + n_values :] = self._time.jd1[idx0:] + out._time.jd2[idx0 + n_values :] = self._time.jd2[idx0:] + + return out + + def __setitem__(self, item, value): + if not self.writeable: + if self.shape: + raise ValueError( + f"{self.__class__.__name__} object is read-only. Make a " + 'copy() or set "writeable" attribute to True.' + ) + else: + raise ValueError( + f"scalar {self.__class__.__name__} object is read-only." + ) + + # Any use of setitem results in immediate cache invalidation + del self.cache + + # Setting invalidates transform deltas + for attr in ("_delta_tdb_tt", "_delta_ut1_utc"): + if hasattr(self, attr): + delattr(self, attr) + + if value is np.ma.masked or value is np.nan: # noqa: PLW0177, RUF100 + if not isinstance(self._time.jd2, Masked): + self._time.jd1 = Masked(self._time.jd1, copy=False) + self._time.jd2 = Masked( + self._time.jd2, mask=self._time.jd1.mask, copy=False + ) + self._time.jd2.mask[item] = True + return + + elif value is np.ma.nomask: + if isinstance(self._time.jd2, Masked): + self._time.jd2.mask[item] = False + return + + value = self._make_value_equivalent(item, value) + + # Finally directly set the jd1/2 values. Locations are known to match. + if self.scale is not None: + value = getattr(value, self.scale) + self._time.jd1[item] = value._time.jd1 + self._time.jd2[item] = value._time.jd2 + + def isclose(self, other, atol=None): + """Returns a boolean or boolean array where two Time objects are + element-wise equal within a time tolerance. + + This evaluates the expression below:: + + abs(self - other) <= atol + + Parameters + ---------- + other : `~astropy.time.Time` + Time object for comparison. + atol : `~astropy.units.Quantity` or `~astropy.time.TimeDelta` + Absolute tolerance for equality with units of time (e.g. ``u.s`` or + ``u.day``). Default is two bits in the 128-bit JD time representation, + equivalent to about 40 picosecs. + """ + if atol is None: + # Note: use 2 bits instead of 1 bit based on experience in precision + # tests, since taking the difference with a UTC time means one has + # to do a scale change. + atol = 2 * np.finfo(float).eps * u.day + + if not isinstance(atol, (u.Quantity, TimeDelta)): + raise TypeError( + "'atol' argument must be a Quantity or TimeDelta instance, got " + f"{atol.__class__.__name__} instead" + ) + + try: + # Separate these out so user sees where the problem is + dt = self - other + dt = abs(dt) + out = dt <= atol + except Exception as err: + raise TypeError( + "'other' argument must support subtraction with Time " + "and return a value that supports comparison with " + f"{atol.__class__.__name__}: {err}" + ) from err + + return out + + def copy(self, format=None): + """ + Return a fully independent copy the Time object, optionally changing + the format. + + If ``format`` is supplied then the time format of the returned Time + object will be set accordingly, otherwise it will be unchanged from the + original. + + In this method a full copy of the internal time arrays will be made. + The internal time arrays are normally not changeable by the user so in + most cases the ``replicate()`` method should be used. + + Parameters + ---------- + format : str, optional + Time format of the copy. + + Returns + ------- + tm : Time object + Copy of this object + """ + return self._apply("copy", format=format) + + def replicate(self, format=None, copy=False, cls=None): + """ + Return a replica of the Time object, optionally changing the format. + + If ``format`` is supplied then the time format of the returned Time + object will be set accordingly, otherwise it will be unchanged from the + original. + + If ``copy`` is set to `True` then a full copy of the internal time arrays + will be made. By default the replica will use a reference to the + original arrays when possible to save memory. The internal time arrays + are normally not changeable by the user so in most cases it should not + be necessary to set ``copy`` to `True`. + + The convenience method copy() is available in which ``copy`` is `True` + by default. + + Parameters + ---------- + format : str, optional + Time format of the replica. + copy : bool, optional + Return a true copy instead of using references where possible. + + Returns + ------- + tm : Time object + Replica of this object + """ + return self._apply("copy" if copy else "replicate", format=format, cls=cls) + + def _apply(self, method, *args, format=None, cls=None, **kwargs): + """Create a new time object, possibly applying a method to the arrays. + + Parameters + ---------- + method : str or callable + If string, can be 'replicate' or the name of a relevant + `~numpy.ndarray` method. In the former case, a new time instance + with unchanged internal data is created, while in the latter the + method is applied to the internal ``jd1`` and ``jd2`` arrays, as + well as to possible ``location``, ``_delta_ut1_utc``, and + ``_delta_tdb_tt`` arrays. + If a callable, it is directly applied to the above arrays. + Examples: 'copy', '__getitem__', 'reshape', `~numpy.broadcast_to`. + args : tuple + Any positional arguments for ``method``. + kwargs : dict + Any keyword arguments for ``method``. If the ``format`` keyword + argument is present, this will be used as the Time format of the + replica. + + Examples + -------- + Some ways this is used internally:: + + copy : ``_apply('copy')`` + replicate : ``_apply('replicate')`` + reshape : ``_apply('reshape', new_shape)`` + index or slice : ``_apply('__getitem__', item)`` + broadcast : ``_apply(np.broadcast, shape=new_shape)`` + """ + new_format = self.format if format is None else format + + if callable(method): + apply_method = lambda array: method(array, *args, **kwargs) + + else: + if method == "replicate": + apply_method = None + else: + apply_method = operator.methodcaller(method, *args, **kwargs) + + jd1, jd2 = self._time.jd1, self._time.jd2 + if apply_method: + jd1 = apply_method(jd1) + jd2 = apply_method(jd2) + + # Get a new instance of our class and set its attributes directly. + tm = super().__new__(cls or self.__class__) + tm._time = TimeJD( + jd1, + jd2, + self.scale, + precision=None, + in_subfmt="*", + out_subfmt="*", + from_jd=True, + ) + + # Optional ndarray attributes. + for attr in ("_delta_ut1_utc", "_delta_tdb_tt", "_location"): + try: + val = getattr(self, attr) + except AttributeError: + continue + + if apply_method: + # Apply the method to any value arrays (though skip if there is + # only an array scalar and the method would return a view, + # since in that case nothing would change). + if getattr(val, "shape", ()): + val = apply_method(val) + elif method == "copy" or method == "flatten": + # flatten should copy also for a single element array, but + # we cannot use it directly for array scalars, since it + # always returns a one-dimensional array. So, just copy. + val = copy.copy(val) + + setattr(tm, attr, val) + + # Copy other 'info' attr only if it has actually been defined and the + # time object is not a scalar (issue #10688). + # See PR #3898 for further explanation and justification, along + # with Quantity.__array_finalize__ + if "info" in self.__dict__: + tm.info = self.info + + # Make the new internal _time object corresponding to the format + # in the copy. If the format is unchanged this process is lightweight + # and does not create any new arrays. + if new_format not in tm.FORMATS: + raise ValueError(f"format must be one of {list(tm.FORMATS)}") + + NewFormat = tm.FORMATS[new_format] + + tm._time = NewFormat( + tm._time.jd1, + tm._time.jd2, + tm._time._scale, + precision=self.precision, + in_subfmt=NewFormat._get_allowed_subfmt(self.in_subfmt), + out_subfmt=NewFormat._get_allowed_subfmt(self.out_subfmt), + from_jd=True, + ) + tm._format = new_format + tm.SCALES = self.SCALES + + # Finally, if we do not own our data, we link caches, so that + # those can be cleared as needed if any instance is written to. + if not (tm._time.jd1.base if tm.masked else tm._time.jd1).flags["OWNDATA"]: + tm._id_cache = self._id_cache + + return tm + + def __copy__(self): + """ + Overrides the default behavior of the `copy.copy` function in + the python stdlib to behave like `Time.copy`. Does *not* make a + copy of the JD arrays - only copies by reference. + """ + return self.replicate() + + def __deepcopy__(self, memo): + """ + Overrides the default behavior of the `copy.deepcopy` function + in the python stdlib to behave like `Time.copy`. Does make a + copy of the JD arrays. + """ + return self.copy() + + def _advanced_index(self, indices, axis=None, keepdims=False): + """Turn argmin, argmax output into an advanced index. + + Argmin, argmax output contains indices along a given axis in an array + shaped like the other dimensions. To use this to get values at the + correct location, a list is constructed in which the other axes are + indexed sequentially. For ``keepdims`` is ``True``, the net result is + the same as constructing an index grid with ``np.ogrid`` and then + replacing the ``axis`` item with ``indices`` with its shaped expanded + at ``axis``. For ``keepdims`` is ``False``, the result is the same but + with the ``axis`` dimension removed from all list entries. + + For ``axis`` is ``None``, this calls :func:`~numpy.unravel_index`. + + Parameters + ---------- + indices : array + Output of argmin or argmax. + axis : int or None + axis along which argmin or argmax was used. + keepdims : bool + Whether to construct indices that keep or remove the axis along + which argmin or argmax was used. Default: ``False``. + + Returns + ------- + advanced_index : list of arrays + Suitable for use as an advanced index. + """ + if axis is None: + return np.unravel_index(indices, self.shape) + + ndim = self.ndim + if axis < 0: + axis = axis + ndim + + if keepdims and indices.ndim < self.ndim: + indices = np.expand_dims(indices, axis) + + index = [ + indices + if i == axis + else np.arange(s).reshape( + (1,) * (i if keepdims or i < axis else i - 1) + + (s,) + + (1,) * (ndim - i - (1 if keepdims or i > axis else 2)) + ) + for i, s in enumerate(self.shape) + ] + + return tuple(index) + + def argmin(self, axis=None, out=None): + """Return indices of the minimum values along the given axis. + + This is similar to :meth:`~numpy.ndarray.argmin`, but adapted to ensure + that the full precision given by the two doubles ``jd1`` and ``jd2`` + is used. See :func:`~numpy.argmin` for detailed documentation. + """ + # First get the minimum at normal precision. + jd1, jd2 = self._time.jd1, self._time.jd2 + approx = np.min(jd1 + jd2, axis, keepdims=True) + + # Approx is very close to the true minimum, and by subtracting it at + # full precision, all numbers near 0 can be represented correctly, + # so we can be sure we get the true minimum. + # The below is effectively what would be done for + # dt = (self - self.__class__(approx, format='jd')).jd + # which translates to: + # approx_jd1, approx_jd2 = day_frac(approx, 0.) + # dt = (self.jd1 - approx_jd1) + (self.jd2 - approx_jd2) + dt = (jd1 - approx) + jd2 + + return dt.argmin(axis, out) + + def argmax(self, axis=None, out=None): + """Return indices of the maximum values along the given axis. + + This is similar to :meth:`~numpy.ndarray.argmax`, but adapted to ensure + that the full precision given by the two doubles ``jd1`` and ``jd2`` + is used. See :func:`~numpy.argmax` for detailed documentation. + """ + # For procedure, see comment on argmin. + jd1, jd2 = self._time.jd1, self._time.jd2 + approx = np.max(jd1 + jd2, axis, keepdims=True) + + dt = (jd1 - approx) + jd2 + + return dt.argmax(axis, out) + + def argsort(self, axis=-1, kind="stable"): + """Returns the indices that would sort the time array. + + This is similar to :meth:`~numpy.ndarray.argsort`, but adapted to ensure that + the full precision given by the two doubles ``jd1`` and ``jd2`` is used, and + that corresponding attributes are copied. Internally, it uses + :func:`~numpy.lexsort`, and hence no sort method can be chosen. + + Parameters + ---------- + axis : int, optional + Axis along which to sort. Default is -1, which means sort along the last + axis. + kind : 'stable', optional + Sorting is done with :func:`~numpy.lexsort` so this argument is ignored, but + kept for compatibility with :func:`~numpy.argsort`. The sorting is stable, + meaning that the order of equal elements is preserved. + + Returns + ------- + indices : ndarray + An array of indices that sort the time array. + """ + # For procedure, see comment on argmin. + jd1, jd2 = self._time.jd1, self._time.jd2 + approx = jd1 + jd2 + remainder = (jd1 - approx) + jd2 + + if axis is None: + return np.lexsort((remainder.ravel(), approx.ravel())) + else: + return np.lexsort(keys=(remainder, approx), axis=axis) + + def min(self, axis=None, out=None, keepdims=False): + """Minimum along a given axis. + + This is similar to :meth:`~numpy.ndarray.min`, but adapted to ensure + that the full precision given by the two doubles ``jd1`` and ``jd2`` + is used, and that corresponding attributes are copied. + + Note that the ``out`` argument is present only for compatibility with + ``np.min``; since `Time` instances are immutable, it is not possible + to have an actual ``out`` to store the result in. + """ + if out is not None: + raise ValueError( + "Since `Time` instances are immutable, ``out`` " + "cannot be set to anything but ``None``." + ) + return self[self._advanced_index(self.argmin(axis), axis, keepdims)] + + def max(self, axis=None, out=None, keepdims=False): + """Maximum along a given axis. + + This is similar to :meth:`~numpy.ndarray.max`, but adapted to ensure + that the full precision given by the two doubles ``jd1`` and ``jd2`` + is used, and that corresponding attributes are copied. + + Note that the ``out`` argument is present only for compatibility with + ``np.max``; since `Time` instances are immutable, it is not possible + to have an actual ``out`` to store the result in. + """ + if out is not None: + raise ValueError( + "Since `Time` instances are immutable, ``out`` " + "cannot be set to anything but ``None``." + ) + return self[self._advanced_index(self.argmax(axis), axis, keepdims)] + + def _ptp_impl(self, axis=None, out=None, keepdims=False): + if out is not None: + raise ValueError( + "Since `Time` instances are immutable, ``out`` " + "cannot be set to anything but ``None``." + ) + return self.max(axis, keepdims=keepdims) - self.min(axis, keepdims=keepdims) + + def __array_function__(self, function, types, args, kwargs): + if function is np.ptp: + return self._ptp_impl(*args[1:], **kwargs) + else: + return super().__array_function__(function, types, args, kwargs) + + @deprecated("7.0", alternative="np.ptp") + def ptp(self, axis=None, out=None, keepdims=False): + """Peak to peak (maximum - minimum) along a given axis. + + This method is similar to the :func:`numpy.ptp` function, but + adapted to ensure that the full precision given by the two doubles + ``jd1`` and ``jd2`` is used. + + Note that the ``out`` argument is present only for compatibility with + `~numpy.ptp`; since `Time` instances are immutable, it is not possible + to have an actual ``out`` to store the result in. + """ + return self._ptp_impl(axis, out, keepdims) + + def sort(self, axis=-1): + """Return a copy sorted along the specified axis. + + This is similar to :meth:`~numpy.ndarray.sort`, but internally uses + indexing with :func:`~numpy.lexsort` to ensure that the full precision + given by the two doubles ``jd1`` and ``jd2`` is kept, and that + corresponding attributes are properly sorted and copied as well. + + Parameters + ---------- + axis : int or None + Axis to be sorted. If ``None``, the flattened array is sorted. + By default, sort over the last axis. + """ + return self[self._advanced_index(self.argsort(axis), axis, keepdims=True)] + + def mean(self, axis=None, dtype=None, out=None, keepdims=False, *, where=True): + """Mean along a given axis. + + This is similar to :meth:`~numpy.ndarray.mean`, but adapted to ensure + that the full precision given by the two doubles ``jd1`` and ``jd2`` is + used, and that corresponding attributes are copied. + + Note that the ``out`` argument is present only for compatibility with + ``np.mean``; since `Time` instances are immutable, it is not possible + to have an actual ``out`` to store the result in. + + Similarly, the ``dtype`` argument is also present for compatibility + only; it has no meaning for `Time`. + + Parameters + ---------- + axis : None or int or tuple of ints, optional + Axis or axes along which the means are computed. The default is to + compute the mean of the flattened array. + dtype : None + Only present for compatibility with :meth:`~numpy.ndarray.mean`, + must be `None`. + out : None + Only present for compatibility with :meth:`~numpy.ndarray.mean`, + must be `None`. + keepdims : bool, optional + If this is set to True, the axes which are reduced are left + in the result as dimensions with size one. With this option, + the result will broadcast correctly against the input array. + where : array_like of bool, optional + Elements to include in the mean. See `~numpy.ufunc.reduce` for + details. + + Returns + ------- + m : Time + A new Time instance containing the mean values + """ + if dtype is not None: + raise ValueError("Cannot set ``dtype`` on `Time` instances") + if out is not None: + raise ValueError( + "Since `Time` instances are immutable, ``out`` " + "cannot be set to anything but ``None``." + ) + + where = where & ~self.mask + where_broadcasted = np.broadcast_to(where, self.shape) + + kwargs = dict( + axis=axis, + keepdims=keepdims, + where=where, + ) + + divisor = np.sum(where_broadcasted, axis=axis, keepdims=keepdims) + if np.any(divisor == 0): + raise ValueError( + "Mean over zero elements is not supported as it would give an undefined" + " time;see issue https://github.com/astropy/astropy/issues/6509" + ) + + jd1, jd2 = day_frac( + val1=np.sum(np.ma.getdata(self.jd1), **kwargs), + val2=np.sum(np.ma.getdata(self.jd2), **kwargs), + divisor=divisor, + ) + + result = type(self)( + val=jd1, + val2=jd2, + format="jd", + scale=self.scale, + copy=None, + ) + result.format = self.format + return result + + @lazyproperty + def _id_cache(self): + """Cache of all instances that share underlying data. + + Helps to ensure all cached data can be deleted if the + underlying data is changed. + """ + return WeakValueDictionary({id(self): self}) + + @_id_cache.setter + def _id_cache(self, _id_cache): + _id_cache[id(self)] = self + # lazyproperty will do the actual storing of the result. + + @lazyproperty + def cache(self): + """ + Return the cache associated with this instance. + """ + return defaultdict(dict) + + @cache.deleter + def cache(self): + for instance in self._id_cache.values(): + instance.cache.clear() + + def __getattr__(self, attr): + """ + Get dynamic attributes to output format or do timescale conversion. + """ + if attr in self.SCALES and self.scale is not None: + cache = self.cache["scale"] + if attr not in cache: + if attr == self.scale: + tm = self + else: + tm = self.replicate() + tm._set_scale(attr) + if tm.shape: + # Prevent future modification of cached array-like object + tm.writeable = False + cache[attr] = tm + return cache[attr] + + elif attr in self.FORMATS: + return self.to_value(attr, subfmt=None) + + elif attr in TIME_SCALES: # allowed ones done above (self.SCALES) + if self.scale is None: + raise ScaleValueError( + "Cannot convert TimeDelta with " + "undefined scale to any defined scale." + ) + else: + raise ScaleValueError( + f"Cannot convert {self.__class__.__name__} with scale " + f"'{self.scale}' to scale '{attr}'" + ) + + else: + # Should raise AttributeError + return self.__getattribute__(attr) + + def __dir__(self): + return sorted(set(super().__dir__()) | set(self.SCALES) | set(self.FORMATS)) + + def _match_shape(self, val): + """ + Ensure that `val` is matched to length of self. If val has length 1 + then broadcast, otherwise cast to double and make sure shape matches. + """ + val = _make_array(val, copy=True) # be conservative and copy + if val.size > 1 and val.shape != self.shape: + try: + # check the value can be broadcast to the shape of self. + val = np.broadcast_to(val, self.shape, subok=True) + except Exception as err: + raise ValueError( + "Attribute shape must match or be broadcastable to that of " + "Time object. Typically, give either a single value or " + "one for each time." + ) from err + + return val + + def _time_comparison(self, other, op): + """If other is of same class as self, compare difference in self.scale. + Otherwise, return NotImplemented. + """ + if other.__class__ is not self.__class__: + try: + other = self.__class__(other, scale=self.scale) + except Exception: + # Let other have a go. + return NotImplemented + + if (self.scale is not None and self.scale not in other.SCALES) or ( + other.scale is not None and other.scale not in self.SCALES + ): + # Other will also not be able to do it, so raise a TypeError + # immediately, allowing us to explain why it doesn't work. + raise TypeError( + f"Cannot compare {self.__class__.__name__} instances with " + f"scales '{self.scale}' and '{other.scale}'" + ) + + if self.scale is not None and other.scale is not None: + other = getattr(other, self.scale) + + return op((self.jd1 - other.jd1) + (self.jd2 - other.jd2), 0.0) + + def __lt__(self, other): + return self._time_comparison(other, operator.lt) + + def __le__(self, other): + return self._time_comparison(other, operator.le) + + def __eq__(self, other): + """ + If other is an incompatible object for comparison, return `False`. + Otherwise, return `True` if the time difference between self and + other is zero. + """ + return self._time_comparison(other, operator.eq) + + def __ne__(self, other): + """ + If other is an incompatible object for comparison, return `True`. + Otherwise, return `False` if the time difference between self and + other is zero. + """ + return self._time_comparison(other, operator.ne) + + def __gt__(self, other): + return self._time_comparison(other, operator.gt) + + def __ge__(self, other): + return self._time_comparison(other, operator.ge) + + +class Time(TimeBase): + """ + Represent and manipulate times and dates for astronomy. + + A `Time` object is initialized with one or more times in the ``val`` + argument. The input times in ``val`` must conform to the specified + ``format`` and must correspond to the specified time ``scale``. The + optional ``val2`` time input should be supplied only for numeric input + formats (e.g. JD) where very high precision (better than 64-bit precision) + is required. + + The allowed values for ``format`` can be listed with:: + + >>> list(Time.FORMATS) + ['jd', 'mjd', 'decimalyear', 'unix', 'unix_tai', 'cxcsec', 'galexsec', 'gps', + 'plot_date', 'stardate', 'datetime', 'ymdhms', 'iso', 'isot', 'yday', 'datetime64', + 'fits', 'byear', 'jyear', 'byear_str', 'jyear_str'] + + + See also: http://docs.astropy.org/en/stable/time/ + + Parameters + ---------- + val : sequence, ndarray, number, str, bytes, or `~astropy.time.Time` object + Value(s) to initialize the time or times. Bytes are decoded as ascii. + val2 : sequence, ndarray, or number; optional + Value(s) to initialize the time or times. Only used for numerical + input, to help preserve precision. + format : str, optional + Format of input value(s), specifying how to interpret them (e.g., ISO, JD, or + Unix time). By default, the same format will be used for output representation. + scale : str, optional + Time scale of input value(s), must be one of the following: + ('tai', 'tcb', 'tcg', 'tdb', 'tt', 'ut1', 'utc') + precision : int, optional + Digits of precision in string representation of time + in_subfmt : str, optional + Unix glob to select subformats for parsing input times + out_subfmt : str, optional + Unix glob to select subformat for outputting times + location : `~astropy.coordinates.EarthLocation` or tuple, optional + If given as an tuple, it should be able to initialize an + an EarthLocation instance, i.e., either contain 3 items with units of + length for geocentric coordinates, or contain a longitude, latitude, + and an optional height for geodetic coordinates. + Can be a single location, or one for each input time. + If not given, assumed to be the center of the Earth for time scale + transformations to and from the solar-system barycenter. + copy : bool, optional + Make a copy of the input values + """ + + SCALES = TIME_SCALES + """List of time scales""" + + FORMATS = TIME_FORMATS + """Dict of time formats""" + + def __new__( + cls, + val, + val2=None, + format=None, + scale=None, + precision=None, + in_subfmt=None, + out_subfmt=None, + location=None, + copy=False, + ): + if isinstance(val, Time): + self = val.replicate(format=format, copy=copy, cls=cls) + else: + self = super().__new__(cls) + + return self + + def __init__( + self, + val, + val2=None, + format=None, + scale=None, + precision=None, + in_subfmt=None, + out_subfmt=None, + location=None, + copy=None, + ): + if location is not None: + from astropy.coordinates import EarthLocation + + if isinstance(location, EarthLocation): + self._location = location + else: + self._location = EarthLocation(*location) + if self._location.size == 1: + self._location = self._location.squeeze() + elif not hasattr(self, "_location"): + self._location = None + + if isinstance(val, Time): + # Update _time formatting parameters if explicitly specified + if precision is not None: + self._time.precision = precision + if in_subfmt is not None: + self._time.in_subfmt = in_subfmt + if out_subfmt is not None: + self._time.out_subfmt = out_subfmt + self.SCALES = TIME_TYPES[self.scale] + if scale is not None: + self._set_scale(scale) + else: + self._init_from_vals( + val, val2, format, scale, copy, precision, in_subfmt, out_subfmt + ) + self.SCALES = TIME_TYPES[self.scale] + + if self.location is not None and ( + self.location.size > 1 and self.location.shape != self.shape + ): + try: + # check the location can be broadcast to self's shape. + self._location = np.broadcast_to(self._location, self.shape, subok=True) + except Exception as err: + raise ValueError( + f"The location with shape {self.location.shape} cannot be " + f"broadcast against time with shape {self.shape}. " + "Typically, either give a single location or one for each time." + ) from err + + def _make_value_equivalent(self, item, value): + """Coerce setitem value into an equivalent Time object.""" + # If there is a vector location then broadcast to the Time shape + # and then select with ``item`` + if self.location is not None and self.location.shape: + self_location = np.broadcast_to(self.location, self.shape, subok=True)[item] + else: + self_location = self.location + + if isinstance(value, Time): + # Make sure locations are compatible. Location can be either None or + # a Location object. + if self_location is None and value.location is None: + match = True + elif (self_location is None and value.location is not None) or ( + self_location is not None and value.location is None + ): + match = False + else: + match = np.all(self_location == value.location) + if not match: + raise ValueError( + "cannot set to Time with different location: expected " + f"location={self_location} and got location={value.location}" + ) + else: + try: + value = self.__class__(value, scale=self.scale, location=self_location) + except Exception: + try: + value = self.__class__( + value, + scale=self.scale, + format=self.format, + location=self_location, + ) + except Exception as err: + raise ValueError( + f"cannot convert value to a compatible Time object: {err}" + ) from err + return value + + @classmethod + def now(cls): + """ + Creates a new object corresponding to the instant in time this + method is called. + + .. note:: + "Now" is determined using the `~datetime.datetime.now` + function, so its accuracy and precision is determined by that + function. Generally that means it is set by the accuracy of + your system clock. The timezone is set to UTC. + + Returns + ------- + nowtime : :class:`~astropy.time.Time` + A new `Time` object (or a subclass of `Time` if this is called from + such a subclass) at the current time. + """ + # call `now` immediately to be sure it's ASAP + dtnow = datetime.now(tz=UTC) + return cls(val=dtnow, format="datetime", scale="utc") + + info = TimeInfo() + + @classmethod + def strptime(cls, time_string, format_string, **kwargs): + """ + Parse a string to a Time according to a format specification. + See `time.strptime` documentation for format specification. + + >>> Time.strptime('2012-Jun-30 23:59:60', '%Y-%b-%d %H:%M:%S') + " for name in self.HEADING_NAMES) + rows = ( + f"{''.join(f'' for elem in row)}" + for row in self._process_units() + ) + # The HTML will be rendered & the table is simple, so don't + # bother to include newlines & indentation for the HTML code. + return f'
{name}
{elem}
{heading}{"".join(rows)}
' + + def _process_units(self) -> list[tuple[str, str, str]]: + """ + Extract attributes, and sort, the equivalent units pre-formatting. + """ + return sorted( + ( + unit.name, + "irreducible" if (s := str(unit.decompose())) == unit.name else s, + ", ".join(unit.aliases), + ) + for unit in self + ) + + def find_equivalent_units( + self, equivalencies=[], units=None, include_prefix_units=False + ): + """ + Return a list of all the units that are the same type as ``self``. + + Parameters + ---------- + equivalencies : list of tuple + A list of equivalence pairs to also list. See + :ref:`astropy:unit_equivalencies`. + Any list given, including an empty one, supersedes global defaults + that may be in effect (as set by `set_enabled_equivalencies`) + + units : set of `~astropy.units.Unit`, optional + If not provided, all defined units will be searched for + equivalencies. Otherwise, may be a dict, module or + sequence containing the units to search for equivalencies. + + include_prefix_units : bool, optional + When `True`, include prefixed units in the result. + Default is `False`. + + Returns + ------- + units : list of `UnitBase` + A list of unit objects that match ``u``. A subclass of + `list` (``EquivalentUnitsList``) is returned that + pretty-prints the list of units when output. + """ + results = self.compose( + equivalencies=equivalencies, + units=units, + max_depth=1, + include_prefix_units=include_prefix_units, + ) + results = { + x.bases[0] for x in results if len(x.bases) == 1 and x.powers[0] == 1 + } + return self.EquivalentUnitsList(results) + + def is_unity(self) -> bool: + """Check whether the unit is unscaled and dimensionless.""" + return False + + +def _flatten_units_collection(items: object) -> set[UnitBase]: + """ + Given a list of sequences, modules or dictionaries of units, or + single units, return a flat set of all the units found. + """ + if not isinstance(items, list): + items = [items] + + result = set() + for item in items: + if isinstance(item, UnitBase): + result.add(item) + else: + if isinstance(item, dict): + units = item.values() + elif inspect.ismodule(item): + units = vars(item).values() + elif np.iterable(item): + units = item + else: + continue + + for unit in units: + if isinstance(unit, UnitBase): + result.add(unit) + + return result + + +def _normalize_equivalencies(equivalencies): + """Normalizes equivalencies ensuring each is a 4-tuple. + + The resulting tuple is of the form:: + + (from_unit, to_unit, forward_func, backward_func) + + Parameters + ---------- + equivalencies : list of equivalency pairs + + Raises + ------ + ValueError if an equivalency cannot be interpreted + """ + if equivalencies is None: + return [] + + normalized = [] + + for i, equiv in enumerate(equivalencies): + if len(equiv) == 2: + funit, tunit = equiv + a = b = lambda x: x + elif len(equiv) == 3: + funit, tunit, a = equiv + b = a + elif len(equiv) == 4: + funit, tunit, a, b = equiv + else: + raise ValueError(f"Invalid equivalence entry {i}: {equiv!r}") + if not ( + funit is Unit(funit) + and (tunit is None or tunit is Unit(tunit)) + and callable(a) + and callable(b) + ): + raise ValueError(f"Invalid equivalence entry {i}: {equiv!r}") + normalized.append((funit, tunit, a, b)) + + return normalized + + +class _UnitRegistry: + """ + Manages a registry of the enabled units. + """ + + def __init__(self, init=[], equivalencies=[], aliases={}): + if isinstance(init, _UnitRegistry): + # If passed another registry we don't need to rebuild everything. + # but because these are mutable types we don't want to create + # conflicts so everything needs to be copied. + self._equivalencies = init._equivalencies.copy() + self._aliases = init._aliases.copy() + self._all_units = init._all_units.copy() + self._registry = init._registry.copy() + self._non_prefix_units = init._non_prefix_units.copy() + # The physical type is a dictionary containing sets as values. + # All of these must be copied otherwise we could alter the old + # registry. + self._by_physical_type = { + k: v.copy() for k, v in init._by_physical_type.items() + } + + else: + self._reset_units() + self._reset_equivalencies() + self._reset_aliases() + self.add_enabled_units(init) + self.add_enabled_equivalencies(equivalencies) + self.add_enabled_aliases(aliases) + + def _reset_units(self) -> None: + self._all_units = set() + self._non_prefix_units = set() + self._registry = {} + self._by_physical_type = {} + + def _reset_equivalencies(self) -> None: + self._equivalencies = set() + + def _reset_aliases(self) -> None: + self._aliases = {} + + @property + def registry(self) -> dict[str, UnitBase]: + return self._registry + + @property + def all_units(self) -> set[UnitBase]: + return self._all_units + + @property + def non_prefix_units(self) -> set[UnitBase]: + return self._non_prefix_units + + def set_enabled_units(self, units: object) -> None: + """ + Sets the units enabled in the unit registry. + + These units are searched when using + `UnitBase.find_equivalent_units`, for example. + + Parameters + ---------- + units : list of sequence, dict, or module + This is a list of things in which units may be found + (sequences, dicts or modules), or units themselves. The + entire set will be "enabled" for searching through by + methods like `UnitBase.find_equivalent_units` and + `UnitBase.compose`. + """ + self._reset_units() + return self.add_enabled_units(units) + + def add_enabled_units(self, units: object) -> None: + """ + Adds to the set of units enabled in the unit registry. + + These units are searched when using + `UnitBase.find_equivalent_units`, for example. + + Parameters + ---------- + units : list of sequence, dict, or module + This is a list of things in which units may be found + (sequences, dicts or modules), or units themselves. The + entire set will be added to the "enabled" set for + searching through by methods like + `UnitBase.find_equivalent_units` and `UnitBase.compose`. + """ + units = _flatten_units_collection(units) + + for unit in units: + # Loop through all of the names first, to ensure all of them + # are new, then add them all as a single "transaction" below. + for st in unit._names: + if st in self._registry and unit != self._registry[st]: + raise ValueError( + f"Object with name {st!r} already exists in namespace. " + "Filter the set of units to avoid name clashes before " + "enabling them." + ) + + for st in unit._names: + self._registry[st] = unit + + self._all_units.add(unit) + if not isinstance(unit, PrefixUnit): + self._non_prefix_units.add(unit) + + self._by_physical_type.setdefault(unit._physical_type_id, set()).add(unit) + + def get_units_with_physical_type(self, unit: UnitBase) -> set[UnitBase]: + """ + Get all units in the registry with the same physical type as + the given unit. + + Parameters + ---------- + unit : UnitBase instance + """ + return self._by_physical_type.get(unit._physical_type_id, set()) + + @property + def equivalencies(self): + return list(self._equivalencies) + + def set_enabled_equivalencies(self, equivalencies): + """ + Sets the equivalencies enabled in the unit registry. + + These equivalencies are used if no explicit equivalencies are given, + both in unit conversion and in finding equivalent units. + + This is meant in particular for allowing angles to be dimensionless. + Use with care. + + Parameters + ---------- + equivalencies : list of tuple + List of equivalent pairs, e.g., as returned by + `~astropy.units.dimensionless_angles`. + """ + self._reset_equivalencies() + return self.add_enabled_equivalencies(equivalencies) + + def add_enabled_equivalencies(self, equivalencies): + """ + Adds to the set of equivalencies enabled in the unit registry. + + These equivalencies are used if no explicit equivalencies are given, + both in unit conversion and in finding equivalent units. + + This is meant in particular for allowing angles to be dimensionless. + Use with care. + + Parameters + ---------- + equivalencies : list of tuple + List of equivalent pairs, e.g., as returned by + `~astropy.units.dimensionless_angles`. + """ + # pre-normalize list to help catch mistakes + equivalencies = _normalize_equivalencies(equivalencies) + self._equivalencies |= set(equivalencies) + + @property + def aliases(self) -> dict[str, UnitBase]: + return self._aliases + + def set_enabled_aliases(self, aliases: dict[str, UnitBase]) -> None: + """ + Set aliases for units. + + Parameters + ---------- + aliases : dict of str, Unit + The aliases to set. The keys must be the string aliases, and values + must be the `astropy.units.Unit` that the alias will be mapped to. + + Raises + ------ + ValueError + If the alias already defines a different unit. + + """ + self._reset_aliases() + self.add_enabled_aliases(aliases) + + def add_enabled_aliases(self, aliases: dict[str, UnitBase]) -> None: + """ + Add aliases for units. + + Parameters + ---------- + aliases : dict of str, Unit + The aliases to add. The keys must be the string aliases, and values + must be the `astropy.units.Unit` that the alias will be mapped to. + + Raises + ------ + ValueError + If the alias already defines a different unit. + + """ + for alias, unit in aliases.items(): + if alias in self._registry and unit != self._registry[alias]: + raise ValueError( + f"{alias} already means {self._registry[alias]}, so " + f"cannot be used as an alias for {unit}." + ) + if alias in self._aliases and unit != self._aliases[alias]: + raise ValueError( + f"{alias} already is an alias for {self._aliases[alias]}, so " + f"cannot be used as an alias for {unit}." + ) + + for alias, unit in aliases.items(): + if alias not in self._registry and alias not in self._aliases: + self._aliases[alias] = unit + + +class _UnitContext: + def __init__(self, init=[], equivalencies=[]): + _unit_registries.append(_UnitRegistry(init=init, equivalencies=equivalencies)) + + def __enter__(self) -> None: + pass + + def __exit__( + self, + type: type[BaseException] | None, + value: BaseException | None, + tb: TracebackType | None, + ) -> None: + _unit_registries.pop() + + +_unit_registries: Final = [_UnitRegistry()] + + +def get_current_unit_registry() -> _UnitRegistry: + return _unit_registries[-1] + + +def set_enabled_units(units: object) -> _UnitContext: + """ + Sets the units enabled in the unit registry. + + These units are searched when using + `UnitBase.find_equivalent_units`, for example. + + This may be used either permanently, or as a context manager using + the ``with`` statement (see example below). + + Parameters + ---------- + units : list of sequence, dict, or module + This is a list of things in which units may be found + (sequences, dicts or modules), or units themselves. The + entire set will be "enabled" for searching through by methods + like `UnitBase.find_equivalent_units` and `UnitBase.compose`. + + Examples + -------- + >>> from astropy import units as u + >>> with u.set_enabled_units([u.pc]): + ... u.m.find_equivalent_units() + ... + Primary name | Unit definition | Aliases + [ + pc | 3.08568e+16 m | parsec , + ] + >>> u.m.find_equivalent_units() + Primary name | Unit definition | Aliases + [ + AU | 1.49598e+11 m | au, astronomical_unit , + Angstrom | 1e-10 m | AA, angstrom, Å , + cm | 0.01 m | centimeter , + earthRad | 6.3781e+06 m | R_earth, Rearth , + jupiterRad | 7.1492e+07 m | R_jup, Rjup, R_jupiter, Rjupiter , + lsec | 2.99792e+08 m | lightsecond , + lyr | 9.46073e+15 m | lightyear , + m | irreducible | meter , + micron | 1e-06 m | , + pc | 3.08568e+16 m | parsec , + solRad | 6.957e+08 m | R_sun, Rsun , + ] + """ + # get a context with a new registry, using equivalencies of the current one + context = _UnitContext(equivalencies=get_current_unit_registry().equivalencies) + # in this new current registry, enable the units requested + get_current_unit_registry().set_enabled_units(units) + return context + + +def add_enabled_units(units: object) -> _UnitContext: + """ + Adds to the set of units enabled in the unit registry. + + These units are searched when using + `UnitBase.find_equivalent_units`, for example. + + This may be used either permanently, or as a context manager using + the ``with`` statement (see example below). + + Parameters + ---------- + units : list of sequence, dict, or module + This is a list of things in which units may be found + (sequences, dicts or modules), or units themselves. The + entire set will be added to the "enabled" set for searching + through by methods like `UnitBase.find_equivalent_units` and + `UnitBase.compose`. + + Examples + -------- + >>> from astropy import units as u + >>> from astropy.units import imperial + >>> with u.add_enabled_units(imperial): + ... u.m.find_equivalent_units() + ... + Primary name | Unit definition | Aliases + [ + AU | 1.49598e+11 m | au, astronomical_unit , + Angstrom | 1e-10 m | AA, angstrom, Å , + cm | 0.01 m | centimeter , + earthRad | 6.3781e+06 m | R_earth, Rearth , + ft | 0.3048 m | foot , + fur | 201.168 m | furlong , + inch | 0.0254 m | , + jupiterRad | 7.1492e+07 m | R_jup, Rjup, R_jupiter, Rjupiter , + lsec | 2.99792e+08 m | lightsecond , + lyr | 9.46073e+15 m | lightyear , + m | irreducible | meter , + mi | 1609.34 m | mile , + micron | 1e-06 m | , + mil | 2.54e-05 m | thou , + nmi | 1852 m | nauticalmile, NM , + pc | 3.08568e+16 m | parsec , + solRad | 6.957e+08 m | R_sun, Rsun , + yd | 0.9144 m | yard , + ] + """ + # get a context with a new registry, which is a copy of the current one + context = _UnitContext(get_current_unit_registry()) + # in this new current registry, enable the further units requested + get_current_unit_registry().add_enabled_units(units) + return context + + +def set_enabled_equivalencies(equivalencies): + """ + Sets the equivalencies enabled in the unit registry. + + These equivalencies are used if no explicit equivalencies are given, + both in unit conversion and in finding equivalent units. + + This is meant in particular for allowing angles to be dimensionless. + Use with care. + + Parameters + ---------- + equivalencies : list of tuple + list of equivalent pairs, e.g., as returned by + `~astropy.units.dimensionless_angles`. + + Examples + -------- + Exponentiation normally requires dimensionless quantities. To avoid + problems with complex phases:: + + >>> from astropy import units as u + >>> with u.set_enabled_equivalencies(u.dimensionless_angles()): + ... phase = 0.5 * u.cycle + ... np.exp(1j*phase) # doctest: +FLOAT_CMP + + """ + # get a context with a new registry, using all units of the current one + context = _UnitContext(get_current_unit_registry()) + # in this new current registry, enable the equivalencies requested + get_current_unit_registry().set_enabled_equivalencies(equivalencies) + return context + + +def add_enabled_equivalencies(equivalencies): + """ + Adds to the equivalencies enabled in the unit registry. + + These equivalencies are used if no explicit equivalencies are given, + both in unit conversion and in finding equivalent units. + + This is meant in particular for allowing angles to be dimensionless. + Since no equivalencies are enabled by default, generally it is recommended + to use `set_enabled_equivalencies`. + + Parameters + ---------- + equivalencies : list of tuple + list of equivalent pairs, e.g., as returned by + `~astropy.units.dimensionless_angles`. + """ + # get a context with a new registry, which is a copy of the current one + context = _UnitContext(get_current_unit_registry()) + # in this new current registry, enable the further equivalencies requested + get_current_unit_registry().add_enabled_equivalencies(equivalencies) + return context + + +def set_enabled_aliases(aliases: dict[str, UnitBase]) -> _UnitContext: + """ + Set aliases for units. + + This is useful for handling alternate spellings for units, or + misspelled units in files one is trying to read. + + Parameters + ---------- + aliases : dict of str, Unit + The aliases to set. The keys must be the string aliases, and values + must be the `astropy.units.Unit` that the alias will be mapped to. + + Raises + ------ + ValueError + If the alias already defines a different unit. + + Examples + -------- + To temporarily allow for a misspelled 'Angstroem' unit:: + + >>> from astropy import units as u + >>> with u.set_enabled_aliases({'Angstroem': u.Angstrom}): + ... print(u.Unit("Angstroem", parse_strict="raise") == u.Angstrom) + True + + """ + # get a context with a new registry, which is a copy of the current one + context = _UnitContext(get_current_unit_registry()) + # in this new current registry, enable the further equivalencies requested + get_current_unit_registry().set_enabled_aliases(aliases) + return context + + +def add_enabled_aliases(aliases: dict[str, UnitBase]) -> _UnitContext: + """ + Add aliases for units. + + This is useful for handling alternate spellings for units, or + misspelled units in files one is trying to read. + + Since no aliases are enabled by default, generally it is recommended + to use `set_enabled_aliases`. + + Parameters + ---------- + aliases : dict of str, Unit + The aliases to add. The keys must be the string aliases, and values + must be the `astropy.units.Unit` that the alias will be mapped to. + + Raises + ------ + ValueError + If the alias already defines a different unit. + + Examples + -------- + To temporarily allow for a misspelled 'Angstroem' unit:: + + >>> from astropy import units as u + >>> with u.add_enabled_aliases({'Angstroem': u.Angstrom}): + ... print(u.Unit("Angstroem", parse_strict="raise") == u.Angstrom) + True + + """ + # get a context with a new registry, which is a copy of the current one + context = _UnitContext(get_current_unit_registry()) + # in this new current registry, enable the further equivalencies requested + get_current_unit_registry().add_enabled_aliases(aliases) + return context + + +class NamedUnit(UnitBase): + """ + The base class of units that have a name. + + Parameters + ---------- + st : str, list of str, 2-tuple + The name of the unit. If a list of strings, the first element + is the canonical (short) name, and the rest of the elements + are aliases. If a tuple of lists, the first element is a list + of short names, and the second element is a list of long + names; all but the first short name are considered "aliases". + Each name *should* be a valid Python identifier to make it + easy to access, but this is not required. + + namespace : dict, optional + When provided, inject the unit, and all of its aliases, in the + given namespace dictionary. If a unit by the same name is + already in the namespace, a ValueError is raised. + + doc : str, optional + A docstring describing the unit. + + format : dict, optional + A mapping to format-specific representations of this unit. + For example, for the ``Ohm`` unit, it might be nice to have it + displayed as ``\\Omega`` by the ``latex`` formatter. In that + case, `format` argument should be set to:: + + {'latex': r'\\Omega'} + + Raises + ------ + ValueError + If any of the given unit names are already in the registry. + + ValueError + If any of the given unit names are not valid Python tokens. + """ + + def __init__( + self, + st: str | list[str] | tuple[list[str], list[str]], + doc: str | None = None, + format: Mapping[str, str] | None = None, + namespace: MutableMapping[str, object] | None = None, + ) -> None: + if isinstance(st, (bytes, str)): + self._names = [st] + self._short_names = [st] + self._long_names = [] + elif isinstance(st, tuple): + if not len(st) == 2: + raise ValueError("st must be string, list or 2-tuple") + self._names = st[0] + [n for n in st[1] if n not in st[0]] + if not len(self._names): + raise ValueError("must provide at least one name") + self._short_names = st[0][:] + self._long_names = st[1][:] + else: + if len(st) == 0: + raise ValueError("st list must have at least one entry") + self._names = st[:] + self._short_names = [st[0]] + self._long_names = st[1:] + + self._format = {} if format is None else format + self.__doc__ = ( + self._generate_doc() if doc is None else textwrap.fill(textwrap.dedent(doc)) + ) + self._inject(namespace) + + def _generate_doc(self) -> str: + """ + Generate a docstring for the unit if the user didn't supply + one. This is only used from the constructor and may be + overridden in subclasses. + """ + names = self.names + if len(self.names) > 1: + return f"{names[1]} ({names[0]})" + else: + return names[0] + + @deprecated(since="7.0", alternative="to_string()") + def get_format_name(self, format): + """ + Get a name for this unit that is specific to a particular + format. + + Uses the dictionary passed into the `format` kwarg in the + constructor. + + Parameters + ---------- + format : str + The name of the format + + Returns + ------- + name : str + The name of the unit for the given format. + """ + return self._get_format_name(format) + + def _get_format_name(self, format: str) -> str: + return self._format.get(format, self.name) + + @property + def names(self) -> list[str]: + """All the names associated with the unit.""" + return self._names + + @property + def name(self) -> str: + """The canonical (short) name associated with the unit.""" + return self._names[0] + + @property + def aliases(self) -> list[str]: + """The aliases (long) names for the unit.""" + return self._names[1:] + + @property + def short_names(self) -> list[str]: + """All the short names associated with the unit.""" + return self._short_names + + @property + def long_names(self) -> list[str]: + """All the long names associated with the unit.""" + return self._long_names + + def _inject(self, namespace: MutableMapping[str, object] | None = None) -> None: + """ + Injects the unit, and all of its aliases, in the given + namespace dictionary. + """ + if namespace is None: + return + + # Loop through all of the names first, to ensure all of them + # are new, then add them all as a single "transaction" below. + for name in self._names: + nfkc_name = unicodedata.normalize("NFKC", name) + if nfkc_name in namespace and self != (obj := namespace[nfkc_name]): + msg = ( + f"the namespace already uses the name {name!r} for {obj!r}" + if name == nfkc_name + else ( + "the namespace already uses the NFKC normalized name " + f"{nfkc_name!r} for {obj!r}\n\nSee " + "https://docs.python.org/3/reference/lexical_analysis.html#identifiers " + "for more information." + ) + ) + raise ValueError(msg) + + for name in self._names: + namespace[name] = self + + +def _recreate_irreducible_unit(cls, names, registered): + """ + This is used to reconstruct units when passed around by + multiprocessing. + """ + registry = get_current_unit_registry().registry + if names[0] in registry: + # If in local registry return that object. + return registry[names[0]] + else: + # otherwise, recreate the unit. + unit = cls(names) + if registered: + # If not in local registry but registered in origin registry, + # enable unit in local registry. + get_current_unit_registry().add_enabled_units([unit]) + + return unit + + +class IrreducibleUnit(NamedUnit): + """ + Irreducible units are the units that all other units are defined + in terms of. + + Examples are meters, seconds, kilograms, amperes, etc. There is + only once instance of such a unit per type. + """ + + def __reduce__(self): + # When IrreducibleUnit objects are passed to other processes + # over multiprocessing, they need to be recreated to be the + # ones already in the subprocesses' namespace, not new + # objects, or they will be considered "unconvertible". + # Therefore, we have a custom pickler/unpickler that + # understands how to recreate the Unit on the other side. + registry = get_current_unit_registry().registry + return ( + _recreate_irreducible_unit, + (self.__class__, list(self.names), self.name in registry), + self.__getstate__(), + ) + + @property + def represents(self) -> Self: + """The unit that this named unit represents. + + For an irreducible unit, that is always itself. + """ + return self + + def decompose(self, bases: Collection[UnitBase] = ()) -> UnitBase: + if len(bases) and self not in bases: + for base in bases: + try: + scale = self._to(base) + except UnitsError: + pass + else: + if is_effectively_unity(scale): + return base + else: + return CompositeUnit(scale, [base], [1], _error_check=False) + + raise UnitConversionError( + f"Unit {self} can not be decomposed into the requested bases" + ) + + return self + + +class UnrecognizedUnit(IrreducibleUnit): + """ + A unit that did not parse correctly. This allows for + round-tripping it as a string, but no unit operations actually work + on it. + + Parameters + ---------- + st : str + The name of the unit. + """ + + # For UnrecognizedUnits, we want to use "standard" Python + # pickling, not the special case that is used for + # IrreducibleUnits. + __reduce__ = object.__reduce__ + + def __repr__(self) -> str: + return f"UnrecognizedUnit({self})" + + def __bytes__(self) -> bytes: + return self.name.encode("ascii", "replace") + + def __str__(self) -> str: + return self.name + + def to_string(self, format=None): + return self.name + + def _unrecognized_operator(self, *args, **kwargs): + raise ValueError( + f"The unit {self.name!r} is unrecognized, so all arithmetic operations " + "with it are invalid." + ) + + __pow__ = __truediv__ = __rtruediv__ = __mul__ = __rmul__ = _unrecognized_operator + __lt__ = __gt__ = __le__ = __ge__ = __neg__ = _unrecognized_operator + + def __hash__(self): + # __hash__ isn't inherited in classes with a custom __eq__ method + return self._hash + + def __eq__(self, other): + try: + other = Unit(other, parse_strict="silent") + except (ValueError, UnitsError, TypeError): + return NotImplemented + + return isinstance(other, type(self)) and self.name == other.name + + def __ne__(self, other): + return not (self == other) + + def is_equivalent(self, other, equivalencies=None): + self._normalize_equivalencies(equivalencies) + return self == other + + def get_converter(self, other, equivalencies=None): + self._normalize_equivalencies(equivalencies) + raise ValueError( + f"The unit {self.name!r} is unrecognized. It can not be converted " + "to other units." + ) + + def _get_format_name(self, format: str) -> str: + return self.name + + def is_unity(self) -> Literal[False]: + return False + + +class _UnitMetaClass(type): + """ + This metaclass exists because the Unit constructor should + sometimes return instances that already exist. This "overrides" + the constructor before the new instance is actually created, so we + can return an existing one. + """ + + def __call__( + cls, + s="", + represents=None, + format=None, + namespace=None, + doc=None, + parse_strict="raise", + ): + # Short-circuit if we're already a unit + if hasattr(s, "_physical_type_id"): + return s + + if represents is not None: + # This has the effect of calling the real __new__ and + # __init__ on the Unit class. + return super().__call__( + s, represents, format=format, namespace=namespace, doc=doc + ) + + if isinstance(s, (str, bytes)): + if len(s.strip()) == 0: + # Return the NULL unit + return dimensionless_unscaled + + from .format import Generic, get_format + + try: + f = get_format(format) + except (TypeError, ValueError) as err: + from .format import known_parsers + + err.add_note(known_parsers()) + raise err + if isinstance(s, bytes): + s = s.decode("ascii") + + try: + return f._validate_unit(s) # Try a shortcut + except (AttributeError, KeyError): + # No `f._validate_unit()` (AttributeError) + # or `s` was a composite unit (KeyError). + pass + + try: + with ( + _WARNING_LOCK, + warnings.catch_warnings( + action=_WARNING_ACTIONS[parse_strict], + category=UnitParserWarning, + ), + ): + return f.parse(s) + except NotImplementedError: + raise + except UnitParserWarning as err: + new_err = ValueError(err) + new_err.add_note( + "If you cannot change the unit string then try specifying the " + "'parse_strict' argument." + ) + raise new_err from err + except KeyError as err: + if parse_strict in _WARNING_ACTIONS: + raise + raise ValueError( + "'parse_strict' must be 'warn', 'raise' or 'silent'" + ) from None + except Exception as e: + if parse_strict != "silent": + # Deliberately not issubclass here. Subclasses + # should use their name. + format_clause = "" if f is Generic else f.name + " " + msg = ( + f"'{s}' did not parse as {format_clause}unit: {str(e)} " + "If this is meant to be a custom unit, " + "define it with 'u.def_unit'. To have it " + "recognized inside a file reader or other code, " + "enable it with 'u.add_enabled_units'. " + "For details, see " + "https://docs.astropy.org/en/latest/units/combining_and_defining.html" + ) + if parse_strict == "raise": + raise ValueError(msg) + warnings.warn(msg, UnitsWarning) + return UnrecognizedUnit(s) + + from .quantity import Quantity + + if isinstance(s, Quantity): + if is_effectively_unity(s.value): + return s.unit + return CompositeUnit( + sanitize_scale(s.value) * s.unit.scale, + bases=s.unit.bases, + powers=s.unit.powers, + _error_check=False, + ) + + from .typing import UnitScaleLike + + if isinstance(s, UnitScaleLike): + return CompositeUnit(s, [], []) + + if isinstance(s, tuple): + from .structured import StructuredUnit + + return StructuredUnit(s) + + raise TypeError(f"{s!r} cannot be converted to a Unit") + + +class Unit(NamedUnit, metaclass=_UnitMetaClass): + """ + The main unit class. + + There are a number of different ways to construct a Unit, but + always returns a `UnitBase` instance. If the arguments refer to + an already-existing unit, that existing unit instance is returned, + rather than a new one. + + - From a string:: + + Unit(s, format=None, parse_strict='silent') + + Construct from a string representing a (possibly compound) unit. + + The optional `format` keyword argument specifies the format the + string is in, by default ``"generic"``. For a description of + the available formats, see `astropy.units.format`. + + The optional ``parse_strict`` keyword argument controls what happens + when the string does not comply with the specified format. It may be + one of the following: + + - ``'raise'``: (default) raise a `ValueError` exception. + + - ``'warn'``: emit a `UnitParserWarning`, and return a unit. + + - ``'silent'``: return a unit silently. + + With ``'warn'`` or ``'silent'`` the parser might be able to parse the + string and return a normal unit, but if it fails then an + `UnrecognizedUnit` instance is returned. + + - From a number:: + + Unit(number) + + Creates a dimensionless unit. + + - From a `UnitBase` instance:: + + Unit(unit) + + Returns the given unit unchanged. + + - From no arguments:: + + Unit() + + Returns the dimensionless unit. + + - The last form, which creates a new `Unit` is described in detail + below. + + See also: https://docs.astropy.org/en/stable/units/ + + Parameters + ---------- + st : str or list of str + The name of the unit. If a list, the first element is the + canonical (short) name, and the rest of the elements are + aliases. + + represents : unit-like, optional + The unit that this named unit represents. + + doc : str, optional + A docstring describing the unit. + + format : dict, optional + A mapping to format-specific representations of this unit. + For example, for the ``Ohm`` unit, it might be nice to have it + displayed as ``\\Omega`` by the ``latex`` formatter. In that + case, `format` argument should be set to:: + + {'latex': r'\\Omega'} + + namespace : dict, optional + When provided, inject the unit (and all of its aliases) into + the given namespace. + + Raises + ------ + ValueError + If any of the given unit names are already in the registry. + + ValueError + If any of the given unit names are not valid Python tokens. + + ValueError + If ``represents`` cannot be parsed as a unit, e.g., because it is + a malformed string or a |Quantity| that is not a scalar. + + """ + + def __init__(self, st, represents=None, doc=None, format=None, namespace=None): + represents = Unit(represents) + self._represents = represents + + NamedUnit.__init__(self, st, namespace=namespace, doc=doc, format=format) + + @property + def represents(self) -> UnitBase: + """The unit that this named unit represents.""" + return self._represents + + def decompose(self, bases: Collection[UnitBase] = ()) -> UnitBase: + return self._represents.decompose(bases=bases) + + def is_unity(self) -> bool: + return self._represents.is_unity() + + @cached_property + def _hash(self) -> int: + return hash((self.name, self._represents)) + + @classmethod + def _from_physical_type_id(cls, physical_type_id: PhysicalTypeID) -> UnitBase: + if len(physical_type_id) == 1 and physical_type_id[0][1] == 1: + return cls(physical_type_id[0][0]) + # get string bases and powers from the ID tuple + bases = [cls(base) for base, _ in physical_type_id] + powers = [power for _, power in physical_type_id] + return CompositeUnit(1, bases, powers, _error_check=False) + + +class PrefixUnit(Unit): + """ + A unit that is simply a SI-prefixed version of another unit. + + For example, ``mm`` is a `PrefixUnit` of ``.001 * m``. + + The constructor is the same as for `Unit`. + """ + + +class CompositeUnit(UnitBase): + """ + Create a composite unit using expressions of previously defined + units. + + Direct use of this class is not recommended. Instead use the + factory function `Unit` and arithmetic operators to compose + units. + + Parameters + ---------- + scale : number + A scaling factor for the unit. + + bases : sequence of `UnitBase` + A sequence of units this unit is composed of. + + powers : sequence of numbers + A sequence of powers (in parallel with ``bases``) for each + of the base units. + + Raises + ------ + UnitScaleError + If the scale is zero. + """ + + _decomposed_cache: Union["CompositeUnit", None] = None + + # _error_check can switch off runtime validation of scale, bases and powers. + # These overloads enable type checkers to validate statically. + @overload + def __init__( + self, + scale: UnitScaleLike, + bases: Sequence[UnitBase], + powers: Sequence[UnitPowerLike], + decompose: bool = False, + decompose_bases: Collection[UnitBase] = (), + _error_check: Literal[True] = True, + ) -> None: ... + @overload + def __init__( + self, + scale: UnitScale, + bases: Sequence[UnitBase], + powers: Sequence[UnitPower], + decompose: bool = False, + decompose_bases: Collection[UnitBase] = (), + _error_check: Literal[False] = False, + ) -> None: ... + + def __init__( + self, + scale, + bases, + powers, + decompose=False, + decompose_bases=(), + _error_check=True, + ): + # There are many cases internal to astropy.units where we + # already know that all the bases are Unit objects, and the + # powers have been validated. In those cases, we can skip the + # error checking for performance reasons. When the private + # kwarg `_error_check` is False, the error checking is turned + # off. + if _error_check: + scale = sanitize_scale(scale) + for base in bases: + if not isinstance(base, UnitBase): + raise TypeError("bases must be sequence of UnitBase instances") + powers = [sanitize_power(p) for p in powers] + + if not decompose and len(bases) == 1 and powers[0] >= 0: + # Short-cut; with one unit there's nothing to expand and gather, + # as that has happened already when creating the unit. But do only + # positive powers, since for negative powers we need to re-sort. + unit = bases[0] + power = powers[0] + if power == 1: + scale *= unit.scale + self._bases = unit.bases + self._powers = unit.powers + elif power == 0: + self._bases = [] + self._powers = [] + else: + scale *= unit.scale**power + self._bases = unit.bases + self._powers = [ + sanitize_power(operator.mul(*resolve_fractions(p, power))) + for p in unit.powers + ] + + self._scale = sanitize_scale(scale) + else: + # Regular case: use inputs as preliminary scale, bases, and powers, + # then "expand and gather" identical bases, sanitize the scale, &c. + self._scale = scale + self._bases = bases + self._powers = powers + self._expand_and_gather(decompose=decompose, bases=decompose_bases) + + def __repr__(self) -> str: + if len(self._bases): + return super().__repr__() + else: + if self._scale != 1.0: + return f"Unit(dimensionless with a scale of {self._scale})" + else: + return "Unit(dimensionless)" + + @property + def scale(self) -> UnitScale: + """The scale of the composite unit.""" + return self._scale + + @property + def bases(self) -> list[NamedUnit]: + """The bases of the composite unit.""" + return self._bases + + @property + def powers(self) -> list[UnitPower]: + """The powers of the bases of the composite unit.""" + return self._powers + + def _expand_and_gather( + self, decompose: bool = False, bases: Collection[UnitBase] = () + ): + def add_unit(unit, power, scale): + if bases and unit not in bases: + for base in bases: + try: + scale *= unit._to(base) ** power + except UnitsError: + pass + else: + unit = base + break + + if unit in new_parts: + a, b = resolve_fractions(new_parts[unit], power) + new_parts[unit] = a + b + else: + new_parts[unit] = power + return scale + + new_parts = {} + scale = self._scale + + for b, p in zip(self._bases, self._powers): + if decompose and b not in bases: + b = b.decompose(bases=bases) + + if isinstance(b, CompositeUnit): + scale *= b._scale**p + for b_sub, p_sub in zip(b._bases, b._powers): + a, b = resolve_fractions(p_sub, p) + scale = add_unit(b_sub, a * b, scale) + else: + scale = add_unit(b, p, scale) + + new_parts = [x for x in new_parts.items() if x[1] != 0] + new_parts.sort(key=lambda x: (-x[1], getattr(x[0], "name", ""))) + + self._bases = [x[0] for x in new_parts] + self._powers = [sanitize_power(x[1]) for x in new_parts] + self._scale = sanitize_scale(scale) + + def __copy__(self) -> "CompositeUnit": + return CompositeUnit(self._scale, self._bases[:], self._powers[:]) + + def decompose(self, bases: Collection[UnitBase] = ()) -> "CompositeUnit": + if len(bases) == 0 and self._decomposed_cache is not None: + return self._decomposed_cache + + for base in self.bases: + if not isinstance(base, IrreducibleUnit) or ( + len(bases) and base not in bases + ): + break + else: + if len(bases) == 0: + self._decomposed_cache = self + return self + + x = CompositeUnit( + self.scale, self.bases, self.powers, decompose=True, decompose_bases=bases + ) + if len(bases) == 0: + self._decomposed_cache = x + return x + + def is_unity(self) -> bool: + unit = self.decompose() + return len(unit.bases) == 0 and unit.scale == 1.0 + + +class UnitPrefix(NamedTuple): + """Prefix for representing multiples or sub-multiples of units. + + Parameters + ---------- + symbols : tuple of str + The symbols of the prefix, to be combined with symbols of units. + If multiple are specified then they will be treated as aliases. + names : tuple of str + The names of the prefix, to be combined with names of units. + If multiple are specified then they will be treated as aliases. + factor : `~astropy.units.typing.UnitScale` + The multiplicative factor represented by the prefix. + + Examples + -------- + >>> UnitPrefix(("k",), ("kilo",), 1e3) # Simple prefix + UnitPrefix(symbols=('k',), names=('kilo',), factor=1000.0) + >>> UnitPrefix(("da",), ("deca", "deka"), 1e1) # Multiple names + UnitPrefix(symbols=('da',), names=('deca', 'deka'), factor=10.0) + >>> UnitPrefix(("u", "\N{MICRO SIGN}"), ("micro",), 1e-6) # Multiple symbols + UnitPrefix(symbols=('u', 'Âĩ'), names=('micro',), factor=1e-06) + """ + + symbols: tuple[str, ...] + "The symbols of the prefix, to be combined with symbols of units." + names: tuple[str, ...] + "The names of the prefix, to be combined with names of units." + factor: UnitScale + "The multiplicative factor represented by the prefix." + + +si_prefixes: Final = tuple( + UnitPrefix(symbols, names, factor) + for symbols, names, factor in ( + (("Q",), ("quetta",), 1e30), + (("R",), ("ronna",), 1e27), + (("Y",), ("yotta",), 1e24), + (("Z",), ("zetta",), 1e21), + (("E",), ("exa",), 1e18), + (("P",), ("peta",), 1e15), + (("T",), ("tera",), 1e12), + (("G",), ("giga",), 1e9), + (("M",), ("mega",), 1e6), + (("k",), ("kilo",), 1e3), + (("h",), ("hecto",), 1e2), + (("da",), ("deka", "deca"), 1e1), + (("d",), ("deci",), 1e-1), + (("c",), ("centi",), 1e-2), + (("m",), ("milli",), 1e-3), + (("u", "\N{MICRO SIGN}", "\N{GREEK SMALL LETTER MU}"), ("micro",), 1e-6), + (("n",), ("nano",), 1e-9), + (("p",), ("pico",), 1e-12), + (("f",), ("femto",), 1e-15), + (("a",), ("atto",), 1e-18), + (("z",), ("zepto",), 1e-21), + (("y",), ("yocto",), 1e-24), + (("r",), ("ronto",), 1e-27), + (("q",), ("quecto",), 1e-30), + ) +) + + +binary_prefixes: Final = tuple( + UnitPrefix(symbols, names, factor) + for symbols, names, factor in ( + (("Ki",), ("kibi",), 2**10), + (("Mi",), ("mebi",), 2**20), + (("Gi",), ("gibi",), 2**30), + (("Ti",), ("tebi",), 2**40), + (("Pi",), ("pebi",), 2**50), + (("Ei",), ("exbi",), 2**60), + (("Zi",), ("zebi",), 2**70), + (("Yi",), ("yobi",), 2**80), + ) +) + + +def _add_prefixes( + u: NamedUnit, + excludes: Collection[str] = (), + namespace: MutableMapping[str, object] | None = None, + prefixes: bool | Iterable[UnitPrefix] = False, +) -> None: + """ + Set up all of the standard metric prefixes for a unit. This + function should not be used directly, but instead use the + `prefixes` kwarg on `def_unit` See the documentation of that function + for the description of the parameters. + """ + if prefixes is True: + prefixes = si_prefixes + elif prefixes is False: + prefixes = [] + + for short, full, factor in prefixes: + names = [] + format = {} + for prefix in short: + if prefix in excludes: + continue + + for alias in u.short_names: + names.append(prefix + alias) + + # This is a hack to use Greek mu as a prefix + # for some formatters. + if prefix == "u": + format["latex"] = r"\mu " + u._get_format_name("latex") + format["unicode"] = "\N{MICRO SIGN}" + u._get_format_name("unicode") + + for key, val in u._format.items(): + format.setdefault(key, prefix + val) + + for prefix in full: + if prefix in excludes: + continue + + for alias in u.long_names: + names.append(prefix + alias) + + if names: + PrefixUnit( + names, + CompositeUnit(factor, [u], [1], _error_check=False), + namespace=namespace, + format=format, + ) + + +@overload +def def_unit( + s: str | list[str], + represents: UnitLike, + doc: str | None = None, + format: Mapping[str, str] | None = None, + prefixes: bool | Iterable[UnitPrefix] = False, + exclude_prefixes: Collection[str] = (), + namespace: MutableMapping[str, object] | None = None, +) -> Unit: ... +@overload +def def_unit( + s: str | list[str], + represents: None = None, + doc: str | None = None, + format: Mapping[str, str] | None = None, + prefixes: bool | Iterable[UnitPrefix] = False, + exclude_prefixes: Collection[str] = (), + namespace: MutableMapping[str, object] | None = None, +) -> IrreducibleUnit: ... +def def_unit( + s: str | list[str], + represents: UnitLike | None = None, + doc: str | None = None, + format: Mapping[str, str] | None = None, + prefixes: bool | Iterable[UnitPrefix] = False, + exclude_prefixes: Collection[str] = (), + namespace: MutableMapping[str, object] | None = None, +) -> NamedUnit: + """Define a new unit. + + This function differs from creating units directly with `Unit` or + `IrreducibleUnit` because it can also automatically generate prefixed + units in the given namespace. + + Parameters + ---------- + s : str or list of str + The name of the unit. If a list, the first element is the + canonical (short) name, and the rest of the elements are + aliases. + + represents : unit-like, optional + The unit that this named unit represents. If not provided, + a new `IrreducibleUnit` is created. + + doc : str, optional + A docstring describing the unit. + + format : dict, optional + A mapping to format-specific representations of this unit. + For example, for the ``Ohm`` unit, it might be nice to + have it displayed as ``\\Omega`` by the ``latex`` + formatter. In that case, `format` argument should be set + to:: + + {'latex': r'\\Omega'} + + prefixes : bool or iterable of UnitPrefix, optional + When `True`, generate all of the SI prefixed versions of the + unit as well. For example, for a given unit ``m``, will + generate ``mm``, ``cm``, ``km``, etc. If only a few prefixed + versions should be created then an iterable of `UnitPrefix` + instances can be specified instead. Default is `False`, which + means no prefixed versions will be generated. + + This function always returns the base unit object, even if + multiple scaled versions of the unit were created. + + exclude_prefixes : `~collections.abc.Collection` of str, optional + If any of the SI prefixes need to be excluded, they may be + listed here. For example, when defining the prefixes for ``a``, + ``exclude_prefixes`` should be set to ``["P"]`` so that ``Pa`` + would still refer to the pascal. + + If a bare `str` is used then the prefixes that will be excluded are + the substrings of the `str`, not just its individual characters. + + namespace : dict, optional + When provided, inject the unit (and all of its aliases and + prefixes), into the given namespace dictionary. + + Returns + ------- + unit : `~astropy.units.NamedUnit` + The newly-defined unit, or a matching unit that was already + defined. + + Raises + ------ + ValueError + If ``represents`` cannot be parsed as a unit, e.g., because it is + a malformed string or a |Quantity| that is not a scalar. + + """ + if represents is not None: + result = Unit(s, represents, namespace=namespace, doc=doc, format=format) + else: + result = IrreducibleUnit(s, namespace=namespace, doc=doc, format=format) + + if prefixes: + _add_prefixes( + result, excludes=exclude_prefixes, namespace=namespace, prefixes=prefixes + ) + return result + + +KNOWN_GOOD = np.ndarray | float | int | complex + + +def _condition_arg(value): + """Validate value is acceptable for conversion purposes. + + Will convert into an array if not a scalar or array-like, where scalars + and arrays can be python and numpy types, anything that defines + ``__array_namespace__`` or anything that has a ``.dtype`` attribute. + + Parameters + ---------- + value : scalar or array-like + + Returns + ------- + Scalar value or array + + Raises + ------ + ValueError + If value is not as expected + + """ + if ( + isinstance(value, KNOWN_GOOD) + or hasattr(value, "dtype") + or hasattr(value, "__array_namespace__") + ): + return value + + value = np.array(value) + if value.dtype.kind not in "ifc": + raise ValueError( + "Value not scalar compatible or convertible to " + "an int, float, or complex array" + ) + return value + + +def unit_scale_converter(val): + """Function that just multiplies the value by unity. + + This is a separate function so it can be recognized and + discarded in unit conversion. + """ + return 1.0 * _condition_arg(val) + + +dimensionless_unscaled: Final[CompositeUnit] = CompositeUnit( + 1, [], [], _error_check=False +) +# Abbreviation of the above, see #1980 +one: Final[CompositeUnit] = dimensionless_unscaled diff --git a/astropy/units/decorators.py b/astropy/units/decorators.py new file mode 100644 index 000000000000..e56e57600081 --- /dev/null +++ b/astropy/units/decorators.py @@ -0,0 +1,347 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +__all__ = ["quantity_input"] + +import contextlib +import inspect +import typing as T +from collections.abc import Sequence +from functools import wraps +from numbers import Number + +import numpy as np + +from .core import Unit, UnitBase, add_enabled_equivalencies, dimensionless_unscaled +from .errors import UnitsError +from .physical import PhysicalType, get_physical_type +from .quantity import Quantity + +NoneType = type(None) + + +def _get_allowed_units(targets): + """ + From a list of target units (either as strings or unit objects) and physical + types, return a list of Unit objects. + """ + allowed_units = [] + for target in targets: + try: + unit = Unit(target) + except (TypeError, ValueError): + try: + unit = get_physical_type(target)._unit + except (TypeError, ValueError, KeyError): # KeyError for Enum + raise ValueError(f"Invalid unit or physical type {target!r}.") from None + + allowed_units.append(unit) + + return allowed_units + + +def _validate_arg_value( + param_name, func_name, arg, targets, equivalencies, strict_dimensionless=False +): + """ + Validates the object passed in to the wrapped function, ``arg``, with target + unit or physical type, ``target``. + """ + if len(targets) == 0: + return + + allowed_units = _get_allowed_units(targets) + + # If dimensionless is an allowed unit and the argument is unit-less, + # allow numbers or numpy arrays with numeric dtypes + if ( + not strict_dimensionless + and not hasattr(arg, "unit") + and dimensionless_unscaled in allowed_units + ): + if isinstance(arg, Number): + return + + elif isinstance(arg, np.ndarray) and np.issubdtype(arg.dtype, np.number): + return + + for allowed_unit in allowed_units: + try: + if arg.unit.is_equivalent(allowed_unit, equivalencies=equivalencies): + break + + except AttributeError: # Either there is no .unit or no .is_equivalent + if hasattr(arg, "unit"): + error_msg = "a 'unit' attribute without an 'is_equivalent' method" + else: + error_msg = "no 'unit' attribute" + + raise TypeError( + f"Argument '{param_name}' to function '{func_name}'" + f" has {error_msg}. You should pass in an astropy " + "Quantity instead." + ) + + else: + error_msg = ( + f"Argument '{param_name}' to function '{func_name}' must " + "be in units convertible to" + ) + if len(targets) > 1: + targ_names = ", ".join([f"'{targ}'" for targ in targets]) + raise UnitsError(f"{error_msg} one of: {targ_names}.") + else: + raise UnitsError(f"{error_msg} '{targets[0]}'.") + + +def _parse_annotation(target): + if target in (None, NoneType, inspect._empty): + return target + + # check if unit-like + try: + unit = Unit(target) + except (TypeError, ValueError): + try: + ptype = get_physical_type(target) + except (TypeError, ValueError, KeyError): # KeyError for Enum + if isinstance(target, str): + raise ValueError(f"invalid unit or physical type {target!r}.") from None + else: + return ptype + else: + return unit + + # could be a type hint + origin = T.get_origin(target) + if origin is T.Union: + return [_parse_annotation(t) for t in T.get_args(target)] + elif origin is not T.Annotated: # can't be Quantity[] + return False + + # parse type hint + cls, *annotations = T.get_args(target) + if not issubclass(cls, Quantity) or not annotations: + return False + + # get unit from type hint + unit, *rest = annotations + if not isinstance(unit, (UnitBase, PhysicalType)): + return False + + return unit + + +class QuantityInput: + @classmethod + def as_decorator(cls, func=None, **kwargs): + r""" + A decorator for validating the units of arguments to functions. + + Unit specifications can be provided as keyword arguments to the + decorator, or by using function annotation syntax. Arguments to the + decorator take precedence over any function annotations present. + + A `~astropy.units.UnitsError` will be raised if the unit attribute of + the argument is not equivalent to the unit specified to the decorator or + in the annotation. If the argument has no unit attribute, i.e. it is not + a Quantity object, a `ValueError` will be raised unless the argument is + an annotation. This is to allow non Quantity annotations to pass + through. + + Where an equivalency is specified in the decorator, the function will be + executed with that equivalency in force. + + Notes + ----- + The checking of arguments inside variable arguments to a function is not + supported (i.e. \*arg or \**kwargs). + + The original function is accessible by the attributed ``__wrapped__``. + See :func:`functools.wraps` for details. + + Examples + -------- + .. code-block:: python + + import astropy.units as u + @u.quantity_input(myangle=u.arcsec) + def myfunction(myangle): + return myangle**2 + + + .. code-block:: python + + import astropy.units as u + @u.quantity_input + def myfunction(myangle: u.arcsec): + return myangle**2 + + Or using a unit-aware Quantity annotation. + + .. code-block:: python + + @u.quantity_input + def myfunction(myangle: u.Quantity[u.arcsec]): + return myangle**2 + + Also you can specify a return value annotation, which will + cause the function to always return a `~astropy.units.Quantity` in that + unit. + + .. code-block:: python + + import astropy.units as u + @u.quantity_input + def myfunction(myangle: u.arcsec) -> u.deg**2: + return myangle**2 + + Using equivalencies:: + + import astropy.units as u + @u.quantity_input(myenergy=u.eV, equivalencies=u.mass_energy()) + def myfunction(myenergy): + return myenergy**2 + + """ + self = cls(**kwargs) + if func is not None and not kwargs: + return self(func) + else: + return self + + def __init__(self, func=None, strict_dimensionless=False, **kwargs): + self.equivalencies = kwargs.pop("equivalencies", []) + self.decorator_kwargs = kwargs + self.strict_dimensionless = strict_dimensionless + + def __call__(self, wrapped_function): + # Extract the function signature for the function we are wrapping. + wrapped_signature = inspect.signature(wrapped_function) + + # Define a new function to return in place of the wrapped one + @wraps(wrapped_function) + def wrapper(*func_args, **func_kwargs): + # Bind the arguments to our new function to the signature of the original. + bound_args = wrapped_signature.bind(*func_args, **func_kwargs) + + # Iterate through the parameters of the original signature + for param in wrapped_signature.parameters.values(): + # We do not support variable arguments (*args, **kwargs) + if param.kind in ( + inspect.Parameter.VAR_KEYWORD, + inspect.Parameter.VAR_POSITIONAL, + ): + continue + + # Catch the (never triggered) case where bind relied on a default value. + if ( + param.name not in bound_args.arguments + and param.default is not param.empty + ): + bound_args.arguments[param.name] = param.default + + # Get the value of this parameter (argument to new function) + arg = bound_args.arguments[param.name] + + # Get target unit or physical type, either from decorator kwargs + # or annotations + if param.name in self.decorator_kwargs: + targets = self.decorator_kwargs[param.name] + is_annotation = False + else: + targets = param.annotation + is_annotation = True + + # parses to unit if it's an annotation (or list thereof) + targets = _parse_annotation(targets) + + # If the targets is empty, then no target units or physical + # types were specified so we can continue to the next arg + if targets is inspect.Parameter.empty: + continue + + # If the argument value is None, and the default value is None, + # pass through the None even if there is a target unit + if arg is None and param.default is None: + continue + + # Here, we check whether multiple target unit/physical type's + # were specified in the decorator/annotation, or whether a + # single string (unit or physical type) or a Unit object was + # specified + if isinstance(targets, str) or not isinstance(targets, Sequence): + valid_targets = [targets] + + # Check for None in the supplied list of allowed units and, if + # present and the passed value is also None, ignore. + elif None in targets or NoneType in targets: + if arg is None: + continue + else: + valid_targets = [t for t in targets if t is not None] + + else: + valid_targets = targets + + # If we're dealing with an annotation, skip all the targets that + # are not strings or subclasses of Unit. This is to allow + # non unit related annotations to pass through + if is_annotation: + valid_targets = [ + t + for t in valid_targets + if isinstance(t, (str, UnitBase, PhysicalType)) + ] + + # Now we loop over the allowed units/physical types and validate + # the value of the argument: + _validate_arg_value( + param.name, + wrapped_function.__name__, + arg, + valid_targets, + self.equivalencies, + self.strict_dimensionless, + ) + + if self.equivalencies: + equiv_context = add_enabled_equivalencies(self.equivalencies) + else: + # Avoid creating a duplicate registry if we don't have + # equivalencies to add. (If we're wrapping a short function, + # the time spent duplicating the registry is quite noticeable.) + equiv_context = contextlib.nullcontext() + # Call the original function with any equivalencies in force. + with equiv_context: + return_ = wrapped_function(*func_args, **func_kwargs) + + # Return + ra = wrapped_signature.return_annotation + valid_empty = (inspect.Signature.empty, None, NoneType, T.NoReturn) + if ra not in valid_empty: + target = ( + ra + if T.get_origin(ra) not in (T.Annotated, T.Union) + else _parse_annotation(ra) + ) + if isinstance(target, str) or not isinstance(target, Sequence): + target = [target] + valid_targets = [ + t for t in target if isinstance(t, (str, UnitBase, PhysicalType)) + ] + _validate_arg_value( + "return", + wrapped_function.__name__, + return_, + valid_targets, + self.equivalencies, + self.strict_dimensionless, + ) + if len(valid_targets) > 0: + return_ <<= valid_targets[0] + return return_ + + return wrapper + + +quantity_input = QuantityInput.as_decorator diff --git a/astropy/units/deprecated.py b/astropy/units/deprecated.py new file mode 100644 index 000000000000..3d45ab1f3cba --- /dev/null +++ b/astropy/units/deprecated.py @@ -0,0 +1,71 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +""" +This package defines deprecated units. + +These units are not available in the top-level `astropy.units` +namespace. To use these units, you must import the `astropy.units.deprecated` +module:: + + >>> from astropy.units import deprecated + >>> q = 10. * deprecated.emu # doctest: +SKIP + +To include them in `~astropy.units.UnitBase.compose` and the results of +`~astropy.units.UnitBase.find_equivalent_units`, do:: + + >>> from astropy.units import deprecated + >>> deprecated.enable() # doctest: +SKIP + +""" + +import warnings + +from astropy.utils.decorators import deprecated +from astropy.utils.exceptions import AstropyDeprecationWarning + +from . import astrophys, cgs +from .core import _add_prefixes, add_enabled_units, def_unit +from .docgen import ( + generate_dunder_all, + generate_prefixonly_unit_summary, + generate_unit_summary, +) + +local_units = {} + +def_unit(["emu"], cgs.Bi, namespace=local_units, doc="Biot: CGS (EMU) unit of current") +# Add only some *prefixes* as deprecated units. +_add_prefixes(astrophys.jupiterMass, namespace=local_units, prefixes=True) +_add_prefixes(astrophys.earthMass, namespace=local_units, prefixes=True) +_add_prefixes(astrophys.jupiterRad, namespace=local_units, prefixes=True) +_add_prefixes(astrophys.earthRad, namespace=local_units, prefixes=True) + +__all__ = generate_dunder_all(local_units) # noqa: PLE0605 +__all__ += ["enable"] + +if __doc__ is not None: + # This generates a docstring for this module that describes all of the + # standard units defined here. + __doc__ += generate_unit_summary(local_units) + __doc__ += generate_prefixonly_unit_summary(local_units) + + +def __getattr__(name): + if unit := local_units.get(name): + warnings.warn( + f"{name!r} is deprecated since version 7.1", AstropyDeprecationWarning + ) + return unit + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + + +@deprecated(since="7.1") +def enable(): + """ + Enable deprecated units so they appear in results of + `~astropy.units.UnitBase.find_equivalent_units` and + `~astropy.units.UnitBase.compose`. + + This may be used with the ``with`` statement to enable deprecated + units only temporarily. + """ + return add_enabled_units(local_units) diff --git a/astropy/units/docgen.py b/astropy/units/docgen.py new file mode 100644 index 000000000000..36a93123b421 --- /dev/null +++ b/astropy/units/docgen.py @@ -0,0 +1,147 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +""" +Utilities for generating documentation for unit definition modules. + +None of the functions in the module are meant for use outside of the +package. +""" + +import keyword +import re +import unicodedata +from collections.abc import Iterable, Mapping +from io import StringIO + +from .core import NamedUnit, PrefixUnit, Unit, UnitBase + + +def _get_first_sentence(s: str) -> str: + """ + Get the first sentence from a string and remove any carriage + returns. + """ + x = re.match(r".*?\S\.\s", s) + if x is not None: + s = x.group(0) + return s.replace("\n", " ") + + +def _summarize_units( + units: Iterable[NamedUnit], + have_prefixes: set[str], + docstring: StringIO, + template: str, +) -> str: + for unit in sorted(units, key=lambda x: x.name.lower()): + represents = "" + if isinstance(unit, Unit): + represents = f":math:`{unit._represents.to_string('latex')[1:-1]}`" + docstring.write( + template.format( + unit, + _get_first_sentence(unit.__doc__).strip(), + represents, + ", ".join(f"``{x}``" for x in unit.aliases), + "Yes" if unit.name in have_prefixes else "No", + ) + ) + return docstring.getvalue() + + +def generate_unit_summary(namespace: Mapping[str, object]) -> str: + """ + Generates a summary of units from a given namespace. This is used + to generate the docstring for the modules that define the actual + units. + + Parameters + ---------- + namespace : dict + A namespace containing units. + + Returns + ------- + docstring : str + A docstring containing a summary table of the units. + """ + units = [] + have_prefixes = set() + for key, val in namespace.items(): + if not isinstance(val, UnitBase): + continue + if not isinstance(val, NamedUnit): + raise TypeError(f"{key!r} must be defined with 'def_unit()'") + if key == val.name: # Skip aliases + if isinstance(val, PrefixUnit): + # This will return the root unit that is scaled by the prefix + # attached to it + have_prefixes.add(val._represents.bases[0].name) + else: + units.append(val) + docstring = StringIO() + docstring.write( + """ +.. list-table:: Available Units + :header-rows: 1 + :widths: 10 20 20 20 1 + + * - Unit + - Description + - Represents + - Aliases + - SI Prefixes +""" + ) + template = """ + * - ``{}`` + - {} + - {} + - {} + - {} +""" + return _summarize_units(units, have_prefixes, docstring, template) + + +def generate_prefixonly_unit_summary(namespace: Mapping[str, object]) -> str: + """ + Generates table entries for units in a namespace that are just prefixes + without the base unit. Note that this is intended to be used *after* + `generate_unit_summary` and therefore does not include the table header. + + Parameters + ---------- + namespace : dict + A namespace containing units that are prefixes but do *not* have the + base unit in their namespace. + + Returns + ------- + docstring : str + A docstring containing a summary table of the units. + """ + non_prefixed_units = { + unit.represents.bases[0] + for unit in namespace.values() + if isinstance(unit, PrefixUnit) + } + template = """ + * - Prefixes for ``{}`` + - {} prefixes + - {} + - {} + - Only +""" + return _summarize_units(non_prefixed_units, set(), StringIO(), template) + + +def generate_dunder_all(namespace: Mapping[str, object]) -> list[str]: + return [ + name + for name, value in namespace.items() + if ( + isinstance(value, UnitBase) + and name.isidentifier() + and not keyword.iskeyword(name) + and name == unicodedata.normalize("NFKC", name) + ) + ] diff --git a/astropy/units/enums.py b/astropy/units/enums.py new file mode 100644 index 000000000000..09b727bf22cd --- /dev/null +++ b/astropy/units/enums.py @@ -0,0 +1,18 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +from enum import StrEnum, auto +from typing import Self + + +class DeprecatedUnitAction(StrEnum): + SILENT = auto() + WARN = auto() + RAISE = auto() + CONVERT = auto() + + @classmethod + def _missing_(cls, value) -> Self | None: + raise ValueError( + f"invalid deprecation handling option: {value!r}. Valid options are " + f"{', '.join(repr(opt.value) for opt in cls)}." + ) diff --git a/astropy/units/equivalencies.py b/astropy/units/equivalencies.py new file mode 100644 index 000000000000..b4c7be4202ea --- /dev/null +++ b/astropy/units/equivalencies.py @@ -0,0 +1,910 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +""" +A set of standard astronomical equivalencies. + +The equivalency class and all equivalency functions defined here are also +available in (and should be used through) the `astropy.units` namespace. + +""" + +import functools +from typing import Final + +# THIRD-PARTY +import numpy as np + +# LOCAL +from astropy.constants import si as _si +from astropy.utils import deprecated_renamed_argument + +from . import astrophys, cgs, dimensionless_unscaled, misc, si +from .core import Unit +from .errors import UnitsError +from .function import units as function_units +from .photometric import maggy + +__all__ = [ + "Equivalency", + "beam_angular_area", + "brightness_temperature", + "dimensionless_angles", + "doppler_optical", + "doppler_radio", + "doppler_redshift", + "doppler_relativistic", + "logarithmic", + "magnetic_flux_field", + "mass_energy", + "molar_mass_amu", + "parallax", + "pixel_scale", + "plate_scale", + "spectral", + "spectral_density", + "temperature", + "temperature_energy", + "thermodynamic_temperature", + "zero_point_flux", +] + +km_per_s: Final = si.km / si.s +ckms: Final = _si.c.to_value(km_per_s) + + +class Equivalency(list): + """ + A container for a units equivalency. + + Attributes + ---------- + name: `str` + The name of the equivalency. + kwargs: `dict` + Any positional or keyword arguments used to make the equivalency. + """ + + def __init__(self, equiv_list, name="", kwargs=None): + super().__init__(equiv_list) + self.name = [name] + self.kwargs = [kwargs] if kwargs is not None else [{}] + + def __add__(self, other): + if isinstance(other, Equivalency): + # The super() returns a list, which is really a bit weird, + # but that means we have to pass it back through the initializer. + new = self.__class__(super().__add__(other)) + # Avoid the change to list of the name and kwargs arguments. + new.name = self.name + other.name + new.kwargs = self.kwargs + other.kwargs + return new + else: + return super().__add__(other) # Let list take care. + + def __eq__(self, other): + return ( + isinstance(other, self.__class__) + and self.name == other.name + and self.kwargs == other.kwargs + ) + + +@functools.cache +def dimensionless_angles(): + """Allow angles to be equivalent to dimensionless (with 1 rad = 1 m/m = 1). + + It is special compared to other equivalency pairs in that it + allows this independent of the power to which the angle is raised, + and independent of whether it is part of a more complicated unit. + """ + return Equivalency([(si.radian, None)], "dimensionless_angles") + + +@functools.cache +def logarithmic(): + """Allow logarithmic units to be converted to dimensionless fractions.""" + return Equivalency( + [(dimensionless_unscaled, function_units.dex, np.log10, lambda x: 10.0**x)], + "logarithmic", + ) + + +@functools.cache +def parallax(): + """ + Returns a list of equivalence pairs that handle the conversion + between parallax angle and distance. + """ + + def parallax_converter(x): + x = np.asanyarray(x) + d = 1 / x + + if np.iterable(d): + d[d < 0] = np.nan + return d + + else: + if d < 0: + return np.array(np.nan) + else: + return d + + return Equivalency( + [(si.arcsecond, astrophys.parsec, parallax_converter)], "parallax" + ) + + +@functools.cache +def spectral(): + """ + Returns a list of equivalence pairs that handle spectral + wavelength, wave number, frequency, and energy equivalencies. + + Allows conversions between wavelength units, wave number units, + frequency units, and energy units as they relate to light. + + There are two types of wave number: + + * spectroscopic - :math:`1 / \\lambda` (per meter) + * angular - :math:`2 \\pi / \\lambda` (radian per meter) + + """ + c = _si.c.value + h = _si.h.value + hc = h * c + two_pi = 2.0 * np.pi + inv_m_spec = si.m**-1 + inv_m_ang = si.radian / si.m + + return Equivalency( + [ + (si.m, si.Hz, lambda x: c / x), + (si.m, si.J, lambda x: hc / x), + (si.Hz, si.J, lambda x: h * x, lambda x: x / h), + (si.m, inv_m_spec, lambda x: 1.0 / x), + (si.Hz, inv_m_spec, lambda x: x / c, lambda x: c * x), + (si.J, inv_m_spec, lambda x: x / hc, lambda x: hc * x), + (inv_m_spec, inv_m_ang, lambda x: x * two_pi, lambda x: x / two_pi), + (si.m, inv_m_ang, lambda x: two_pi / x), + (si.Hz, inv_m_ang, lambda x: two_pi * x / c, lambda x: c * x / two_pi), + (si.J, inv_m_ang, lambda x: x * two_pi / hc, lambda x: hc * x / two_pi), + ], + "spectral", + ) + + +@deprecated_renamed_argument( + "factor", None, since="7.0", alternative='"wav" as a "Quantity"' +) +def spectral_density(wav, factor=None): + """ + Returns a list of equivalence pairs that handle spectral density + with regard to wavelength and frequency. + + Parameters + ---------- + wav : `~astropy.units.Quantity` + `~astropy.units.Quantity` associated with values being converted + (e.g., wavelength or frequency). + factor : array_like + If ``wav`` is a |Unit| instead of a |Quantity| then ``factor`` + is the value ``wav`` will be multiplied with to convert it to + a |Quantity|. + + .. deprecated:: 7.0 + + ``factor`` is deprecated. Pass in ``wav`` as a |Quantity|, + not as a |Unit|. + """ + from .core import UnitBase + + if isinstance(wav, UnitBase): + if factor is None: + raise ValueError("If `wav` is specified as a unit, `factor` should be set") + wav = factor * wav # Convert to Quantity + c_Aps = _si.c.to_value(si.AA / si.s) # Angstrom/s + h_cgs = _si.h.cgs.value # erg * s + hc = c_Aps * h_cgs + + # flux density + f_la = cgs.erg / si.angstrom / si.cm**2 / si.s + f_nu = cgs.erg / si.Hz / si.cm**2 / si.s + nu_f_nu = cgs.erg / si.cm**2 / si.s + la_f_la = nu_f_nu + phot_f_la = astrophys.photon / (si.cm**2 * si.s * si.AA) + phot_f_nu = astrophys.photon / (si.cm**2 * si.s * si.Hz) + la_phot_f_la = astrophys.photon / (si.cm**2 * si.s) + + # luminosity density + L_nu = cgs.erg / si.s / si.Hz + L_la = cgs.erg / si.s / si.angstrom + nu_L_nu = cgs.erg / si.s + la_L_la = nu_L_nu + phot_L_la = astrophys.photon / (si.s * si.AA) + phot_L_nu = astrophys.photon / (si.s * si.Hz) + + # surface brightness (flux equiv) + S_la = cgs.erg / si.angstrom / si.cm**2 / si.s / si.sr + S_nu = cgs.erg / si.Hz / si.cm**2 / si.s / si.sr + nu_S_nu = cgs.erg / si.cm**2 / si.s / si.sr + la_S_la = nu_S_nu + phot_S_la = astrophys.photon / (si.cm**2 * si.s * si.AA * si.sr) + phot_S_nu = astrophys.photon / (si.cm**2 * si.s * si.Hz * si.sr) + + # surface brightness (luminosity equiv) + SL_nu = cgs.erg / si.s / si.Hz / si.sr + SL_la = cgs.erg / si.s / si.angstrom / si.sr + nu_SL_nu = cgs.erg / si.s / si.sr + la_SL_la = nu_SL_nu + phot_SL_la = astrophys.photon / (si.s * si.AA * si.sr) + phot_SL_nu = astrophys.photon / (si.s * si.Hz * si.sr) + + def f_la_to_f_nu(x): + return x * (wav.to_value(si.AA, spectral()) ** 2 / c_Aps) + + def f_la_from_f_nu(x): + return x / (wav.to_value(si.AA, spectral()) ** 2 / c_Aps) + + def f_nu_to_nu_f_nu(x): + return x * wav.to_value(si.Hz, spectral()) + + def f_nu_from_nu_f_nu(x): + return x / wav.to_value(si.Hz, spectral()) + + def f_la_to_la_f_la(x): + return x * wav.to_value(si.AA, spectral()) + + def f_la_from_la_f_la(x): + return x / wav.to_value(si.AA, spectral()) + + def phot_f_la_to_f_la(x): + return hc * x / wav.to_value(si.AA, spectral()) + + def phot_f_la_from_f_la(x): + return x * wav.to_value(si.AA, spectral()) / hc + + def phot_f_la_to_f_nu(x): + return h_cgs * x * wav.to_value(si.AA, spectral()) + + def phot_f_la_from_f_nu(x): + return x / (wav.to_value(si.AA, spectral()) * h_cgs) + + def phot_f_la_to_phot_f_nu(x): + return x * wav.to_value(si.AA, spectral()) ** 2 / c_Aps + + def phot_f_la_from_phot_f_nu(x): + return c_Aps * x / wav.to_value(si.AA, spectral()) ** 2 + + phot_f_nu_to_f_nu = phot_f_la_to_f_la + phot_f_nu_from_f_nu = phot_f_la_from_f_la + + def phot_f_nu_to_f_la(x): + return x * hc * c_Aps / wav.to_value(si.AA, spectral()) ** 3 + + def phot_f_nu_from_f_la(x): + return x * wav.to_value(si.AA, spectral()) ** 3 / (hc * c_Aps) + + # for luminosity density + L_nu_to_nu_L_nu = f_nu_to_nu_f_nu + L_nu_from_nu_L_nu = f_nu_from_nu_f_nu + L_la_to_la_L_la = f_la_to_la_f_la + L_la_from_la_L_la = f_la_from_la_f_la + + phot_L_la_to_L_la = phot_f_la_to_f_la + phot_L_la_from_L_la = phot_f_la_from_f_la + phot_L_la_to_L_nu = phot_f_la_to_f_nu + phot_L_la_from_L_nu = phot_f_la_from_f_nu + phot_L_la_to_phot_L_nu = phot_f_la_to_phot_f_nu + phot_L_la_from_phot_L_nu = phot_f_la_from_phot_f_nu + phot_L_nu_to_L_nu = phot_f_nu_to_f_nu + phot_L_nu_from_L_nu = phot_f_nu_from_f_nu + phot_L_nu_to_L_la = phot_f_nu_to_f_la + phot_L_nu_from_L_la = phot_f_nu_from_f_la + + return Equivalency( + [ + # flux + (f_la, f_nu, f_la_to_f_nu, f_la_from_f_nu), + (f_nu, nu_f_nu, f_nu_to_nu_f_nu, f_nu_from_nu_f_nu), + (f_la, la_f_la, f_la_to_la_f_la, f_la_from_la_f_la), + (phot_f_la, f_la, phot_f_la_to_f_la, phot_f_la_from_f_la), + (phot_f_la, f_nu, phot_f_la_to_f_nu, phot_f_la_from_f_nu), + (phot_f_la, phot_f_nu, phot_f_la_to_phot_f_nu, phot_f_la_from_phot_f_nu), + (phot_f_nu, f_nu, phot_f_nu_to_f_nu, phot_f_nu_from_f_nu), + (phot_f_nu, f_la, phot_f_nu_to_f_la, phot_f_nu_from_f_la), + # integrated flux + (la_phot_f_la, la_f_la, phot_f_la_to_f_la, phot_f_la_from_f_la), + # luminosity + (L_la, L_nu, f_la_to_f_nu, f_la_from_f_nu), + (L_nu, nu_L_nu, L_nu_to_nu_L_nu, L_nu_from_nu_L_nu), + (L_la, la_L_la, L_la_to_la_L_la, L_la_from_la_L_la), + (phot_L_la, L_la, phot_L_la_to_L_la, phot_L_la_from_L_la), + (phot_L_la, L_nu, phot_L_la_to_L_nu, phot_L_la_from_L_nu), + (phot_L_la, phot_L_nu, phot_L_la_to_phot_L_nu, phot_L_la_from_phot_L_nu), + (phot_L_nu, L_nu, phot_L_nu_to_L_nu, phot_L_nu_from_L_nu), + (phot_L_nu, L_la, phot_L_nu_to_L_la, phot_L_nu_from_L_la), + # surface brightness (flux equiv) + (S_la, S_nu, f_la_to_f_nu, f_la_from_f_nu), + (S_nu, nu_S_nu, f_nu_to_nu_f_nu, f_nu_from_nu_f_nu), + (S_la, la_S_la, f_la_to_la_f_la, f_la_from_la_f_la), + (phot_S_la, S_la, phot_f_la_to_f_la, phot_f_la_from_f_la), + (phot_S_la, S_nu, phot_f_la_to_f_nu, phot_f_la_from_f_nu), + (phot_S_la, phot_S_nu, phot_f_la_to_phot_f_nu, phot_f_la_from_phot_f_nu), + (phot_S_nu, S_nu, phot_f_nu_to_f_nu, phot_f_nu_from_f_nu), + (phot_S_nu, S_la, phot_f_nu_to_f_la, phot_f_nu_from_f_la), + # surface brightness (luminosity equiv) + (SL_la, SL_nu, f_la_to_f_nu, f_la_from_f_nu), + (SL_nu, nu_SL_nu, L_nu_to_nu_L_nu, L_nu_from_nu_L_nu), + (SL_la, la_SL_la, L_la_to_la_L_la, L_la_from_la_L_la), + (phot_SL_la, SL_la, phot_L_la_to_L_la, phot_L_la_from_L_la), + (phot_SL_la, SL_nu, phot_L_la_to_L_nu, phot_L_la_from_L_nu), + (phot_SL_la, phot_SL_nu, phot_L_la_to_phot_L_nu, phot_L_la_from_phot_L_nu), + (phot_SL_nu, SL_nu, phot_L_nu_to_L_nu, phot_L_nu_from_L_nu), + (phot_SL_nu, SL_la, phot_L_nu_to_L_la, phot_L_nu_from_L_la), + ], + "spectral_density", + {"wav": wav, "factor": factor}, + ) + + +def doppler_radio(rest): + r""" + Return the equivalency pairs for the radio convention for velocity. + + The radio convention for the relation between velocity and frequency is: + + :math:`V = c \frac{f_0 - f}{f_0} ; f(V) = f_0 ( 1 - V/c )` + + Parameters + ---------- + rest : `~astropy.units.Quantity` + Any quantity supported by the standard spectral equivalencies + (wavelength, energy, frequency, wave number). + + References + ---------- + `NRAO site defining the conventions `_ + + Examples + -------- + >>> import astropy.units as u + >>> CO_restfreq = 115.27120*u.GHz # rest frequency of 12 CO 1-0 in GHz + >>> radio_CO_equiv = u.doppler_radio(CO_restfreq) + >>> measured_freq = 115.2832*u.GHz + >>> radio_velocity = measured_freq.to(u.km/u.s, equivalencies=radio_CO_equiv) + >>> radio_velocity # doctest: +FLOAT_CMP + + """ + assert_is_spectral_unit(rest) + rest_in = functools.partial(rest.to_value, equivalencies=spectral()) + to_funcs = { + misc.eV: lambda x: (1 - x / rest_in(misc.eV)) * ckms, + si.Hz: lambda x: (1 - x / rest_in(si.Hz)) * ckms, + si.AA: lambda x: (1 - rest_in(si.AA) / x) * ckms, + } + from_funcs = { + misc.eV: lambda x: rest_in(misc.eV) * (1 - x / ckms), + si.Hz: lambda x: rest_in(si.Hz) * (1 - x / ckms), + si.AA: lambda x: rest_in(si.AA) / (1 - x / ckms), + } + return Equivalency( + [ + (unit, km_per_s, to_func, from_funcs[unit]) + for unit, to_func in to_funcs.items() + ], + "doppler_radio", + {"rest": rest}, + ) + + +def doppler_optical(rest): + r""" + Return the equivalency pairs for the optical convention for velocity. + + The optical convention for the relation between velocity and frequency is: + + :math:`V = c \frac{f_0 - f}{f } ; f(V) = f_0 ( 1 + V/c )^{-1}` + + Parameters + ---------- + rest : `~astropy.units.Quantity` + Any quantity supported by the standard spectral equivalencies + (wavelength, energy, frequency, wave number). + + References + ---------- + `NRAO site defining the conventions `_ + + Examples + -------- + >>> import astropy.units as u + >>> CO_restfreq = 115.27120*u.GHz # rest frequency of 12 CO 1-0 in GHz + >>> optical_CO_equiv = u.doppler_optical(CO_restfreq) + >>> measured_freq = 115.2832*u.GHz + >>> optical_velocity = measured_freq.to(u.km/u.s, equivalencies=optical_CO_equiv) + >>> optical_velocity # doctest: +FLOAT_CMP + + """ + assert_is_spectral_unit(rest) + rest_in = functools.partial(rest.to_value, equivalencies=spectral()) + to_funcs = { + misc.eV: lambda x: (rest_in(misc.eV) / x - 1) * ckms, + si.Hz: lambda x: (rest_in(si.Hz) / x - 1) * ckms, + si.AA: lambda x: (x / rest_in(si.AA) - 1) * ckms, + } + from_funcs = { + misc.eV: lambda x: rest_in(misc.eV) / (1 + x / ckms), + si.Hz: lambda x: rest_in(si.Hz) / (1 + x / ckms), + si.AA: lambda x: rest_in(si.AA) * (1 + x / ckms), + } + return Equivalency( + [ + (unit, km_per_s, to_func, from_funcs[unit]) + for unit, to_func in to_funcs.items() + ], + "doppler_optical", + {"rest": rest}, + ) + + +def doppler_relativistic(rest): + r""" + Return the equivalency pairs for the relativistic convention for velocity. + + The full relativistic convention for the relation between velocity and frequency is: + + :math:`V = c \frac{f_0^2 - f^2}{f_0^2 + f^2} ; f(V) = f_0 \frac{\left(1 - (V/c)^2\right)^{1/2}}{(1+V/c)}` + + Parameters + ---------- + rest : `~astropy.units.Quantity` + Any quantity supported by the standard spectral equivalencies + (wavelength, energy, frequency, wave number). + + References + ---------- + `NRAO site defining the conventions `_ + + Examples + -------- + >>> import astropy.units as u + >>> CO_restfreq = 115.27120*u.GHz # rest frequency of 12 CO 1-0 in GHz + >>> relativistic_CO_equiv = u.doppler_relativistic(CO_restfreq) + >>> measured_freq = 115.2832*u.GHz + >>> relativistic_velocity = measured_freq.to(u.km/u.s, equivalencies=relativistic_CO_equiv) + >>> relativistic_velocity # doctest: +FLOAT_CMP + + >>> measured_velocity = 1250 * u.km/u.s + >>> relativistic_frequency = measured_velocity.to(u.GHz, equivalencies=relativistic_CO_equiv) + >>> relativistic_frequency # doctest: +FLOAT_CMP + + >>> relativistic_wavelength = measured_velocity.to(u.mm, equivalencies=relativistic_CO_equiv) + >>> relativistic_wavelength # doctest: +FLOAT_CMP + + """ + assert_is_spectral_unit(rest) + rest_in = functools.partial(rest.to_value, equivalencies=spectral()) + + def to_vel_freq(x): + restfreq2 = rest_in(si.Hz) ** 2 + return (restfreq2 - x**2) / (restfreq2 + x**2) * ckms + + def from_vel_freq(x): + voverc = x / ckms + return rest_in(si.Hz) * ((1 - voverc) / (1 + voverc)) ** 0.5 + + def to_vel_wav(x): + restwav2 = rest_in(si.AA) ** 2 + return (x**2 - restwav2) / (restwav2 + x**2) * ckms + + def from_vel_wav(x): + voverc = x / ckms + return rest_in(si.AA) * ((1 + voverc) / (1 - voverc)) ** 0.5 + + def to_vel_en(x): + resten2 = rest_in(misc.eV) ** 2 + return (resten2 - x**2) / (resten2 + x**2) * ckms + + def from_vel_en(x): + voverc = x / ckms + return rest_in(misc.eV) * ((1 - voverc) / (1 + voverc)) ** 0.5 + + return Equivalency( + [ + (si.Hz, km_per_s, to_vel_freq, from_vel_freq), + (si.AA, km_per_s, to_vel_wav, from_vel_wav), + (misc.eV, km_per_s, to_vel_en, from_vel_en), + ], + "doppler_relativistic", + {"rest": rest}, + ) + + +@functools.cache +def doppler_redshift(): + """ + Returns the equivalence between Doppler redshift (unitless) and radial velocity. + + .. note:: + + This equivalency is not compatible with cosmological + redshift in `astropy.cosmology.units`. + + """ + rv_unit = si.km / si.s + C_KMS = _si.c.to_value(rv_unit) + + def convert_z_to_rv(z): + zponesq = (1 + z) ** 2 + return C_KMS * (zponesq - 1) / (zponesq + 1) + + def convert_rv_to_z(rv): + beta = rv / C_KMS + return np.sqrt((1 + beta) / (1 - beta)) - 1 + + return Equivalency( + [(dimensionless_unscaled, rv_unit, convert_z_to_rv, convert_rv_to_z)], + "doppler_redshift", + ) + + +@functools.cache +def molar_mass_amu(): + """ + Returns the equivalence between amu and molar mass. + """ + return Equivalency([(si.g / si.mol, misc.u)], "molar_mass_amu") + + +@functools.cache +def mass_energy(): + """ + Returns a list of equivalence pairs that handle the conversion + between mass and energy. + """ + c2 = _si.c.value**2 + return Equivalency( + [ + (si.kg, si.J, lambda x: x * c2, lambda x: x / c2), + (si.kg / si.m**2, si.J / si.m**2, lambda x: x * c2, lambda x: x / c2), + (si.kg / si.m**3, si.J / si.m**3, lambda x: x * c2, lambda x: x / c2), + (si.kg / si.s, si.J / si.s, lambda x: x * c2, lambda x: x / c2), + ], + "mass_energy", + ) + + +def brightness_temperature(frequency, beam_area=None): + r""" + Defines the conversion between Jy/sr and "brightness temperature", + :math:`T_B`, in Kelvins. The brightness temperature is a unit very + commonly used in radio astronomy. See, e.g., "Tools of Radio Astronomy" + (Wilson 2009) eqn 8.16 and eqn 8.19 (these pages are available on `google + books + `__). + + :math:`T_B \equiv S_\nu / \left(2 k \nu^2 / c^2 \right)` + + If the input is in Jy/beam or Jy (assuming it came from a single beam), the + beam area is essential for this computation: the brightness temperature is + inversely proportional to the beam area. + + Parameters + ---------- + frequency : `~astropy.units.Quantity` + The observed ``spectral`` equivalent `~astropy.units.Unit` (e.g., + frequency or wavelength). The variable is named 'frequency' because it + is more commonly used in radio astronomy. + BACKWARD COMPATIBILITY NOTE: previous versions of the brightness + temperature equivalency used the keyword ``disp``, which is no longer + supported. + beam_area : `~astropy.units.Quantity` ['solid angle'] + Beam area in angular units, i.e. steradian equivalent + + Examples + -------- + Arecibo C-band beam:: + + >>> import numpy as np + >>> from astropy import units as u + >>> beam_sigma = 50*u.arcsec + >>> beam_area = 2*np.pi*(beam_sigma)**2 + >>> freq = 5*u.GHz + >>> equiv = u.brightness_temperature(freq) + >>> (1*u.Jy/beam_area).to(u.K, equivalencies=equiv) # doctest: +FLOAT_CMP + + + VLA synthetic beam:: + + >>> bmaj = 15*u.arcsec + >>> bmin = 15*u.arcsec + >>> fwhm_to_sigma = 1./(8*np.log(2))**0.5 + >>> beam_area = 2.*np.pi*(bmaj*bmin*fwhm_to_sigma**2) + >>> freq = 5*u.GHz + >>> equiv = u.brightness_temperature(freq) + >>> (u.Jy/beam_area).to(u.K, equivalencies=equiv) # doctest: +FLOAT_CMP + + + Any generic surface brightness: + + >>> surf_brightness = 1e6*u.MJy/u.sr + >>> surf_brightness.to(u.K, equivalencies=u.brightness_temperature(500*u.GHz)) # doctest: +FLOAT_CMP + + """ + nu = frequency.to(si.GHz, spectral()) + factor_Jy = (2 * _si.k_B * si.K * nu**2 / _si.c**2).to(astrophys.Jy).value + factor_K = (astrophys.Jy / (2 * _si.k_B * nu**2 / _si.c**2)).to(si.K).value + + if beam_area is not None: + beam = beam_area.to_value(si.sr) + + def convert_Jy_to_K(x_jybm): + return x_jybm / beam / factor_Jy + + def convert_K_to_Jy(x_K): + return x_K * beam / factor_K + + return Equivalency( + [ + (astrophys.Jy, si.K, convert_Jy_to_K, convert_K_to_Jy), + (astrophys.Jy / astrophys.beam, si.K, convert_Jy_to_K, convert_K_to_Jy), + ], + "brightness_temperature", + {"frequency": frequency, "beam_area": beam_area}, + ) + else: + + def convert_JySr_to_K(x_jysr): + return x_jysr / factor_Jy + + def convert_K_to_JySr(x_K): + return x_K / factor_K # multiplied by 1x for 1 steradian + + return Equivalency( + [(astrophys.Jy / si.sr, si.K, convert_JySr_to_K, convert_K_to_JySr)], + "brightness_temperature", + {"frequency": frequency, "beam_area": beam_area}, + ) + + +def beam_angular_area(beam_area): + """ + Convert between the ``beam`` unit, which is commonly used to express the area + of a radio telescope resolution element, and an area on the sky. + This equivalency also supports direct conversion between ``Jy/beam`` and + ``Jy/steradian`` units, since that is a common operation. + + Parameters + ---------- + beam_area : unit-like + The area of the beam in angular area units (e.g., steradians) + Must have angular area equivalent units. + """ + return Equivalency( + [ + (astrophys.beam, Unit(beam_area)), + (astrophys.beam**-1, Unit(beam_area) ** -1), + (astrophys.Jy / astrophys.beam, astrophys.Jy / Unit(beam_area)), + ], + "beam_angular_area", + {"beam_area": beam_area}, + ) + + +def thermodynamic_temperature(frequency, T_cmb=None): + r"""Defines the conversion between Jy/sr and "thermodynamic temperature", + :math:`T_{CMB}`, in Kelvins. The thermodynamic temperature is a unit very + commonly used in cosmology. See eqn 8 in [1]. + + :math:`K_{CMB} \equiv I_\nu / \left(2 k \nu^2 / c^2 f(\nu) \right)` + + with :math:`f(\nu) = \frac{ x^2 e^x}{(e^x - 1 )^2}` + where :math:`x = h \nu / k T` + + Parameters + ---------- + frequency : `~astropy.units.Quantity` + The observed `spectral` equivalent `~astropy.units.Unit` (e.g., + frequency or wavelength). Must have spectral units. + T_cmb : `~astropy.units.Quantity` ['temperature'] or None + The CMB temperature at z=0. If `None`, the default cosmology will be + used to get this temperature. Must have units of temperature. + + Notes + ----- + For broad band receivers, this conversion do not hold + as it highly depends on the frequency + + References + ---------- + .. [1] Planck 2013 results. IX. HFI spectral response + https://arxiv.org/abs/1303.5070 + + Examples + -------- + Planck HFI 143 GHz:: + + >>> from astropy import units as u + >>> from astropy.cosmology import Planck15 + >>> freq = 143 * u.GHz + >>> equiv = u.thermodynamic_temperature(freq, Planck15.Tcmb0) + >>> (1. * u.mK).to(u.MJy / u.sr, equivalencies=equiv) # doctest: +FLOAT_CMP + + + """ + nu = frequency.to(si.GHz, spectral()) + + if T_cmb is None: + from astropy.cosmology import default_cosmology + + T_cmb = default_cosmology.get().Tcmb0 + + def f(nu, T_cmb=T_cmb): + x = _si.h * nu / _si.k_B / T_cmb + return x**2 * np.exp(x) / np.expm1(x) ** 2 + + def convert_Jy_to_K(x_jybm): + factor = (f(nu) * 2 * _si.k_B * si.K * nu**2 / _si.c**2).to_value(astrophys.Jy) + return x_jybm / factor + + def convert_K_to_Jy(x_K): + factor = (astrophys.Jy / (f(nu) * 2 * _si.k_B * nu**2 / _si.c**2)).to_value( + si.K + ) + return x_K / factor + + return Equivalency( + [(astrophys.Jy / si.sr, si.K, convert_Jy_to_K, convert_K_to_Jy)], + "thermodynamic_temperature", + {"frequency": frequency, "T_cmb": T_cmb}, + ) + + +@functools.cache +def temperature(): + """Convert degrees Celsius and degrees Fahrenheit here because + Unit and CompositeUnit cannot do addition or subtraction properly. + """ + from .imperial import deg_F as F + + K = si.K + C = si.deg_C + + return Equivalency( + [ + (K, C, lambda x: x - 273.15, lambda x: x + 273.15), + (C, F, lambda x: x * 1.8 + 32.0, lambda x: (x - 32.0) / 1.8), + (K, F, lambda x: x * 1.8 - 459.67, lambda x: (x + 459.67) / 1.8), + ], + "temperature", + ) + + +@functools.cache +def temperature_energy(): + """Convert between Kelvin and keV(eV) to an equivalent amount.""" + e = _si.e.value + k_B = _si.k_B.value + return Equivalency( + [(si.K, misc.eV, lambda x: x / (e / k_B), lambda x: x * (e / k_B))], + "temperature_energy", + ) + + +def assert_is_spectral_unit(value): + try: + value.to(si.Hz, spectral()) + except (AttributeError, UnitsError) as ex: + raise UnitsError( + "The 'rest' value must be a spectral equivalent " + "(frequency, wavelength, or energy)." + ) + + +def pixel_scale(pixscale): + """ + Convert between pixel distances (in units of ``pix``) and other units, + given a particular ``pixscale``. + + Parameters + ---------- + pixscale : `~astropy.units.Quantity` + The pixel scale either in units of /pixel or pixel/. + """ + decomposed = pixscale.unit.decompose() + dimensions = dict(zip(decomposed.bases, decomposed.powers)) + pix_power = dimensions.get(misc.pix, 0) + + if pix_power == -1: + physical_unit = Unit(pixscale * misc.pix) + elif pix_power == 1: + physical_unit = Unit(misc.pix / pixscale) + else: + raise UnitsError( + "The pixel scale unit must have pixel dimensionality of 1 or -1." + ) + + return Equivalency( + [(misc.pix, physical_unit)], "pixel_scale", {"pixscale": pixscale} + ) + + +def plate_scale(platescale): + """ + Convert between lengths (to be interpreted as lengths in the focal plane) + and angular units with a specified ``platescale``. + + Parameters + ---------- + platescale : `~astropy.units.Quantity` + The pixel scale either in units of distance/pixel or distance/angle. + """ + if platescale.unit.is_equivalent(si.arcsec / si.m): + platescale_val = platescale.to_value(si.radian / si.m) + elif platescale.unit.is_equivalent(si.m / si.arcsec): + platescale_val = (1 / platescale).to_value(si.radian / si.m) + else: + raise UnitsError("The pixel scale must be in angle/distance or distance/angle") + + return Equivalency( + [(si.m, si.radian, lambda d: d * platescale_val, lambda a: a / platescale_val)], + "plate_scale", + {"platescale": platescale}, + ) + + +def magnetic_flux_field(mu_r=1): + r""" + Convert magnetic field between magnetic field strength :math:`(\mathbf{H})` and + magnetic flux density :math:`(\mathbf{B})` using the relationship: + + .. math:: + + \mathbf{B} = \mu_r \mu_0 \mathbf{H} + + where: + - :math:`\mu_0` is the vacuum permeability, a physical constant. + - :math:`\mu_r` is the relative permeability of the medium, a dimensionless + quantity. + + The default setting (:math:`\mu_r=1`) represents conditions in a vacuum. + + Parameters + ---------- + mu_r : float, optional + The relative magnetic permeability of the medium. This is a dimensionless quantity + and has a default value of :math:`\mu_r=1` which corresponds to free space (vacuum). + + Examples + -------- + >>> import astropy.units as u + >>> H = 1 * u.Oe + >>> H.to(u.G, equivalencies=u.magnetic_flux_field()) # doctest: +FLOAT_CMP + + >>> H.to(u.G, equivalencies=u.magnetic_flux_field(mu_r=0.8)) # doctest: +FLOAT_CMP + + >>> B = 1 * u.T + >>> B.to(u.A / u.m, equivalencies=u.magnetic_flux_field()) # doctest: +FLOAT_CMP + + >>> B.to(u.A / u.m, equivalencies=u.magnetic_flux_field(mu_r=0.8)) # doctest: +FLOAT_CMP + + + """ + mu0 = _si.mu0.value + return Equivalency( + [(si.T, si.A / si.m, lambda x: x / (mu_r * mu0), lambda x: x * mu_r * mu0)], + "magnetic_flux_field", + ) + + +def zero_point_flux(flux0): + """ + An equivalency for converting linear flux units ("maggys") defined relative + to a standard source into a standardized system. + + Parameters + ---------- + flux0 : `~astropy.units.Quantity` + The flux of a magnitude-0 object in the "maggy" system. + """ + return Equivalency([(maggy, Unit(flux0))], "zero_point_flux") diff --git a/astropy/units/errors.py b/astropy/units/errors.py new file mode 100644 index 000000000000..4d1bf5ba36bd --- /dev/null +++ b/astropy/units/errors.py @@ -0,0 +1,53 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +"""Custom errors and exceptions for astropy.units.""" + +__all__ = [ + "UnitConversionError", + "UnitParserWarning", + "UnitScaleError", + "UnitTypeError", + "UnitsError", + "UnitsWarning", +] + +from astropy.utils.exceptions import AstropyWarning + + +class UnitsError(Exception): + """ + The base class for unit-specific exceptions. + """ + + +class UnitConversionError(UnitsError, ValueError): + """ + Used specifically for errors related to converting between units or + interpreting units in terms of other units. + """ + + +class UnitScaleError(UnitsError, ValueError): + """ + Used to catch the errors involving scaled units, + which are not recognized by FITS format. + """ + + +class UnitTypeError(UnitsError, TypeError): + """ + Used specifically for errors in setting to units not allowed by a class. + + E.g., would be raised if the unit of an `~astropy.coordinates.Angle` + instances were set to a non-angular unit. + """ + + +class UnitsWarning(AstropyWarning): + """ + The base class for unit-specific warnings. + """ + + +class UnitParserWarning(UnitsWarning): + """Unit parser warnings""" diff --git a/astropy/units/format/__init__.py b/astropy/units/format/__init__.py new file mode 100644 index 000000000000..8fd8f2016c34 --- /dev/null +++ b/astropy/units/format/__init__.py @@ -0,0 +1,88 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +""" +A collection of different unit formats. + +General usage is by their name in the |Unit| constructor or +in the :meth:`~astropy.units.UnitBase.to_string` method, i.e., +these classes rarely if ever need to be imported directly. +""" + +import warnings + +from astropy.utils.exceptions import AstropyDeprecationWarning + +from .base import Base +from .cds import CDS +from .console import Console +from .fits import FITS +from .generic import Generic +from .latex import Latex, LatexInline +from .ogip import OGIP +from .unicode_format import Unicode +from .vounit import VOUnit + +__all__ = [ + "CDS", + "FITS", + "OGIP", + "Base", + "Console", + "Generic", + "Latex", + "LatexInline", + "Unicode", + "VOUnit", + "get_format", +] + + +def __getattr__(name): + if name == "Fits": + warnings.warn( + AstropyDeprecationWarning( + 'The class "Fits" has been renamed to "FITS" in version 7.0. The old ' + "name is deprecated and may be removed in a future version.\n" + " Use FITS instead." + ) + ) + return FITS + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + + +def known_formats() -> str: + return "Valid formatter names are: " + ", ".join(map(repr, Base.registry)) + + +def known_parsers() -> str: + return "Valid parser names are: " + ", ".join( + repr(name) + for name, cls in Base.registry.items() + if cls.parse.__func__ is not Base.parse.__func__ + ) + + +def get_format(format: str | type[Base] | None = None) -> type[Base]: + """ + Get a formatter by name. + + Parameters + ---------- + format : str or `astropy.units.format.Base` subclass + The name of the format, or the formatter class itself. + + Returns + ------- + format : `astropy.units.format.Base` subclass + The requested formatter. + """ + if format is None: + return Generic + if isinstance(format, str): + try: + return Base.registry[format.lower()] + except KeyError: + raise ValueError(f"Unknown format {format!r}.") from None + if isinstance(format, type) and issubclass(format, Base): + return format + raise TypeError(f"Expected a formatter name, not {format!r}.") diff --git a/astropy/units/format/base.py b/astropy/units/format/base.py new file mode 100644 index 000000000000..b9987c23ae61 --- /dev/null +++ b/astropy/units/format/base.py @@ -0,0 +1,334 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +import warnings +from collections.abc import Iterable +from typing import ClassVar, Literal, assert_never + +import numpy as np + +from astropy.extern.ply.lex import LexToken +from astropy.units.core import ( + CompositeUnit, + NamedUnit, + Unit, + UnitBase, + get_current_unit_registry, +) +from astropy.units.enums import DeprecatedUnitAction +from astropy.units.errors import UnitsError, UnitsWarning +from astropy.units.typing import UnitPower, UnitScale +from astropy.units.utils import maybe_simple_fraction +from astropy.utils.misc import did_you_mean + + +class Base: + """ + The abstract base class of all unit formats. + """ + + registry: ClassVar[dict[str, type["Base"]]] = {} + _space: ClassVar[str] = " " + _scale_unit_separator: ClassVar[str] = " " + _times: ClassVar[str] = "*" + name: ClassVar[str] # Set by __init_subclass__ by the latest + + def __new__(cls, *args, **kwargs): + # This __new__ is to make it clear that there is no reason to + # instantiate a Formatter--if you try to you'll just get back the + # class + return cls + + def __init_subclass__(cls, **kwargs): + # Keep a registry of all formats. Key by the class name unless a name + # is explicitly set (i.e., one *not* inherited from a superclass). + if "name" not in cls.__dict__: + cls.name = cls.__name__.lower() + + Base.registry[cls.name] = cls + super().__init_subclass__(**kwargs) + + @classmethod + def format_exponential_notation( + cls, val: UnitScale | np.number, format_spec: str = ".8g" + ) -> str: + """ + Formats a value in exponential notation. + + Parameters + ---------- + val : number + The value to be formatted + + format_spec : str, optional + Format used to split up mantissa and exponent + + Returns + ------- + str + The value in exponential notation in a this class's format. + """ + x = format(val, format_spec).split("e") + if len(x) != 2: + return cls._format_mantissa(x[0]) # no exponent + ex = x[1].lstrip("0+") + if not ex: + return cls._format_mantissa(x[0]) # exponent was zero + if ex.startswith("-"): + ex = "-" + ex[1:].lstrip("0") + ex = f"10{cls._format_superscript(ex)}" + m = cls._format_mantissa("" if x[0].rstrip("0") == "1." else x[0]) + return f"{m}{cls._times}{ex}" if m else ex + + @classmethod + def _format_mantissa(cls, m: str) -> str: + return m + + @classmethod + def _format_superscript(cls, number: str) -> str: + return f"({number})" if "/" in number or "." in number else number + + @classmethod + def _format_unit_power(cls, unit: NamedUnit, power: UnitPower = 1) -> str: + """Format the unit for this format class raised to the given power. + + This is overridden in Latex where the name of the unit can depend on the power + (e.g., for degrees). + """ + name = unit._get_format_name(cls.name) + return name if power == 1 else name + cls._format_power(power) + + @classmethod + def _format_power(cls, power: UnitPower) -> str: + # If the denominator of `power` is a power of 2 then `power` is stored + # as a `float` (see `units.utils.sanitize_power()`), but we still want + # to display it as a fraction. + return cls._format_superscript( + str(maybe_simple_fraction(power) if isinstance(power, float) else power) + ) + + @classmethod + def _format_unit_list(cls, units: Iterable[tuple[NamedUnit, UnitPower]]) -> str: + return cls._space.join( + cls._format_unit_power(base_, power) for base_, power in units + ) + + @classmethod + def _format_inline_fraction( + cls, scale: str, numerator: str, denominator: str + ) -> str: + if cls._space in denominator: + denominator = f"({denominator})" + if scale and numerator == "1": + return f"{scale}/ {denominator}" + return f"{scale}{numerator} / {denominator}" + + @classmethod + def _format_multiline_fraction( + cls, scale: str, numerator: str, denominator: str + ) -> str: + # By default, we just warn that we do not have a multiline format. + warnings.warn( + f"{cls.name!r} format does not support multiline " + "fractions; using inline instead.", + UnitsWarning, + ) + return cls._format_inline_fraction(scale, numerator, denominator) + + @classmethod + def to_string( + cls, + unit: UnitBase, + *, + deprecations: DeprecatedUnitAction = DeprecatedUnitAction.WARN, + fraction: bool | Literal["inline", "multiline"] = True, + ) -> str: + """Convert a unit to its string representation. + + Implementation for `~astropy.units.UnitBase.to_string`. + + Parameters + ---------- + unit : |Unit| + The unit to convert. + deprecations : {"warn", "silent", "raise", "convert"}, optional, keyword-only + Whether deprecated units should emit a warning, be handled + silently or raise an error. The "convert" option replaces + the deprecated unit if possible and emits a warning otherwise. + fraction : {False|True|'inline'|'multiline'}, optional + Options are as follows: + + - `False` : display unit bases with negative powers as they are + (e.g., ``km s-1``); + - 'inline' or `True` : use a single-line fraction (e.g., ``km / s``); + - 'multiline' : use a multiline fraction if possible (available for + the ``latex``, ``console`` and ``unicode`` formats; e.g., + ``$\\mathrm{\\frac{km}{s}}$``). If not possible, use 'inline'. + + Raises + ------ + ValueError + If ``fraction`` is not recognized. + """ + # First the scale. Normally unity, in which case we omit + # it, but non-unity scale can happen, e.g., in decompositions + # like u.Ry.decompose(), which gives "2.17987e-18 kg m2 / s2". + s = "" if unit.scale == 1.0 else cls.format_exponential_notation(unit.scale) + + # dimensionless does not have any bases, but can have a scale; + # e.g., u.percent.decompose() gives "0.01". + if not unit.bases: + return s + + if s: + s += cls._scale_unit_separator + # Unit powers are monotonically decreasing + if not fraction or unit.powers[-1] > 0: + return s + cls._format_unit_list(zip(unit.bases, unit.powers, strict=True)) + + if fraction is True or fraction == "inline": + formatter = cls._format_inline_fraction + elif fraction == "multiline": + formatter = cls._format_multiline_fraction + else: + raise ValueError( + "fraction can only be False, 'inline', or 'multiline', " + f"not {fraction!r}." + ) + + positive = [] + negative = [] + for base, power in zip(unit.bases, unit.powers, strict=True): + if power > 0: + positive.append((base, power)) + else: + negative.append((base, -power)) + return formatter( + s, cls._format_unit_list(positive) or "1", cls._format_unit_list(negative) + ) + + @classmethod + def parse(cls, s: str) -> UnitBase: + """ + Convert a string to a unit object. + """ + raise NotImplementedError(f"Cannot parse with {cls.__name__} format") + + +class _ParsingFormatMixin: + """Provides private methods used in the formats that parse units.""" + + _deprecated_units: ClassVar[frozenset[str]] = frozenset() + + @classmethod + def _do_parse(cls, s: str, debug: bool = False) -> UnitBase: + try: + return cls._parser.parse(s, lexer=cls._lexer, debug=debug) + except ValueError as e: + if str(e): + raise + else: + raise ValueError(f"Syntax error parsing unit '{s}'") + + @classmethod + def _get_unit(cls, t: LexToken) -> UnitBase: + try: + return cls._validate_unit(t.value) + except KeyError: + registry = get_current_unit_registry() + if t.value in registry.aliases: + return registry.aliases[t.value] + + raise ValueError( + f"At col {t.lexpos}, {cls._invalid_unit_error_message(t.value)}" + ) from None + + @classmethod + def _fix_deprecated(cls, x: str) -> list[str]: + return [x + " (deprecated)" if x in cls._deprecated_units else x] + + @classmethod + def _did_you_mean_units(cls, unit: str) -> str: + """ + A wrapper around `astropy.utils.misc.did_you_mean` that deals with + the display of deprecated units. + + Parameters + ---------- + unit : str + The invalid unit string + + Returns + ------- + msg : str + A message with alternatives, or the empty string. + """ + return did_you_mean(unit, cls._units, fix=cls._fix_deprecated) + + @classmethod + def _validate_unit( + cls, s: str, deprecations: DeprecatedUnitAction = DeprecatedUnitAction.WARN + ) -> UnitBase: + if s in cls._deprecated_units: + alternative = ( + unit.represents if isinstance(unit := cls._units[s], Unit) else None + ) + msg = f"The unit {s!r} has been deprecated in the {cls.__name__} standard." + if alternative: + msg += f" Suggested: {cls.to_string(alternative)}." + + match DeprecatedUnitAction(deprecations): + case DeprecatedUnitAction.CONVERT: + if alternative: + return alternative + warnings.warn( + msg + " It cannot be automatically converted.", UnitsWarning + ) + case DeprecatedUnitAction.WARN: + warnings.warn(msg, UnitsWarning) + case DeprecatedUnitAction.RAISE: + raise UnitsError(msg) + case DeprecatedUnitAction.SILENT: + pass + case _: + assert_never(deprecations) + + return cls._units[s] + + @classmethod + def _invalid_unit_error_message(cls, unit: str) -> str: + return ( + f"Unit '{unit}' not supported by the {cls.__name__} standard. " + + cls._did_you_mean_units(unit) + ) + + @classmethod + def _decompose_to_known_units( + cls, + unit: CompositeUnit | NamedUnit, + deprecations: DeprecatedUnitAction = DeprecatedUnitAction.WARN, + ) -> UnitBase: + """ + Partially decomposes a unit so it is only composed of units that + are "known" to a given format. + """ + if isinstance(unit, CompositeUnit): + return CompositeUnit( + unit.scale, + [ + cls._decompose_to_known_units(base, deprecations) + for base in unit.bases + ], + unit.powers, + _error_check=False, + ) + if isinstance(unit, NamedUnit): + name = unit._get_format_name(cls.name) + try: + return cls._validate_unit(name, deprecations=deprecations) + except KeyError: + if isinstance(unit, Unit): + return cls._decompose_to_known_units(unit._represents, deprecations) + raise ValueError(cls._invalid_unit_error_message(name)) from None + raise TypeError( + f"unit argument must be a 'NamedUnit' or 'CompositeUnit', not {type(unit)}" + ) diff --git a/astropy/units/format/cds.py b/astropy/units/format/cds.py new file mode 100644 index 000000000000..eebc9bdbce23 --- /dev/null +++ b/astropy/units/format/cds.py @@ -0,0 +1,252 @@ +# Licensed under a 3-clause BSD style license - see LICNSE.rst + +# This module includes files automatically generated from ply (these end in +# _lextab.py and _parsetab.py). To generate these files, remove them from this +# folder, then build astropy and run the tests in-place: +# +# python setup.py build_ext --inplace +# pytest astropy/units +# +# You can then commit the changes to the re-generated _lextab.py and +# _parsetab.py files. + +"""Handles the CDS string format for units.""" + +import re +from typing import ClassVar, Literal + +from astropy.extern.ply.lex import Lexer +from astropy.units.core import CompositeUnit, Unit, UnitBase +from astropy.units.enums import DeprecatedUnitAction +from astropy.units.utils import is_effectively_unity +from astropy.utils import classproperty, parsing +from astropy.utils.parsing import ThreadSafeParser + +from .base import Base, _ParsingFormatMixin + + +class CDS(Base, _ParsingFormatMixin): + """ + Support the `Centre de DonnÊes astronomiques de Strasbourg + `_ `Standards for Astronomical + Catalogues 2.0 `_ + format, and the `complete set of supported units + `_. This format is used + by VOTable up to version 1.2. + """ + + _space: ClassVar[str] = "." + _times: ClassVar[str] = "x" + _scale_unit_separator: ClassVar[str] = "" + + _tokens: ClassVar[tuple[str, ...]] = ( + "PRODUCT", + "DIVISION", + "OPEN_PAREN", + "CLOSE_PAREN", + "OPEN_BRACKET", + "CLOSE_BRACKET", + "X", + "INT", + "FLOAT", + "UNIT", + "DIMENSIONLESS", + ) + + @classproperty(lazy=True) + def _units(cls) -> dict[str, UnitBase]: + from astropy import units as u + from astropy.units import cds + + return {k: v for k, v in cds.__dict__.items() if isinstance(v, u.UnitBase)} + + @classproperty(lazy=True) + def _lexer(cls) -> Lexer: + tokens = cls._tokens + + t_PRODUCT = r"\." + t_DIVISION = r"/" + t_OPEN_PAREN = r"\(" + t_CLOSE_PAREN = r"\)" + t_OPEN_BRACKET = r"\[" + t_CLOSE_BRACKET = r"\]" + + # NOTE THE ORDERING OF THESE RULES IS IMPORTANT!! + # Regular expression rules for simple tokens + + def t_FLOAT(t): + r"[+-]?((\d+\.?\d+)|(\.\d+))([eE][+-]?\d+)?" + if not re.search(r"[eE\.]", t.value): + t.type = "INT" + t.value = int(t.value) + else: + t.value = float(t.value) + return t + + def t_INT(t): + r"[+-]?\d+" + t.value = int(t.value) + return t + + def t_X(t): # multiplication for factor in front of unit + r"[x×]" + return t + + # Most units are just combinations of letters with no numbers, but there + # are a few special ones (\h is Planch constant) and three that end in 0. + def t_UNIT(t): + r"%|°|\\h|(a|eps|mu)0|((?!\d)\w)+" + t.value = cls._get_unit(t) + return t + + def t_DIMENSIONLESS(t): + r"---|-" + # These are separate from t_UNIT since they cannot have a prefactor. + t.value = cls._get_unit(t) + return t + + t_ignore = "" + + # Error handling rule + def t_error(t): + raise ValueError(f"Invalid character at col {t.lexpos}") + + return parsing.lex( + lextab="cds_lextab", package="astropy/units", reflags=int(re.UNICODE) + ) + + @classproperty(lazy=True) + def _parser(cls) -> ThreadSafeParser: + """ + The grammar here is based on the description in the `Standards + for Astronomical Catalogues 2.0 + `_, which is not + terribly precise. The exact grammar is here is based on the + YACC grammar in the `unity library `_. + """ + tokens = cls._tokens + + def p_main(p): + """ + main : factor combined_units + | combined_units + | DIMENSIONLESS + | OPEN_BRACKET combined_units CLOSE_BRACKET + | OPEN_BRACKET DIMENSIONLESS CLOSE_BRACKET + | factor + """ + from astropy.units import dex + + if len(p) == 3: + p[0] = CompositeUnit(p[1] * p[2].scale, p[2].bases, p[2].powers) + elif len(p) == 4: + p[0] = dex(p[2]) + else: + p[0] = Unit(p[1]) + + def p_combined_units(p): + """ + combined_units : product_of_units + | division_of_units + """ + p[0] = p[1] + + def p_product_of_units(p): + """ + product_of_units : unit_expression PRODUCT combined_units + | unit_expression + """ + if len(p) == 4: + p[0] = p[1] * p[3] + else: + p[0] = p[1] + + def p_division_of_units(p): + """ + division_of_units : DIVISION unit_expression + | combined_units DIVISION unit_expression + """ + if len(p) == 3: + p[0] = p[2] ** -1 + else: + p[0] = p[1] / p[3] + + def p_unit_expression(p): + """ + unit_expression : unit_with_power + | OPEN_PAREN combined_units CLOSE_PAREN + """ + if len(p) == 2: + p[0] = p[1] + else: + p[0] = p[2] + + def p_factor(p): + """ + factor : FLOAT X INT INT + | INT X INT INT + | INT INT + | INT + | FLOAT + """ + match p[1:]: + case factor, _, 10, exponent: + p[0] = factor * 10.0**exponent + case _, _, _, _: + raise ValueError("Only base ten exponents are allowed in CDS") + case 10, exponent: + p[0] = 10.0**exponent + case _, _: + raise ValueError("Only base ten exponents are allowed in CDS") + case _: + p[0] = p[1] + + def p_unit_with_power(p): + """ + unit_with_power : UNIT INT + | UNIT + """ + if len(p) == 2: + p[0] = p[1] + else: + p[0] = p[1] ** p[2] + + def p_error(p): + raise ValueError() + + return parsing.yacc(tabmodule="cds_parsetab", package="astropy/units") + + @classmethod + def parse(cls, s: str, debug: bool = False) -> UnitBase: + if " " in s: + raise ValueError("CDS unit must not contain whitespace") + if not isinstance(s, str): + s = s.decode("ascii") + + return cls._do_parse(s, debug) + + @classmethod + def _format_mantissa(cls, m: str) -> str: + return "" if m == "1" else m + + @classmethod + def _format_superscript(cls, number: str) -> str: + return number if number.startswith("-") else "+" + number + + @classmethod + def to_string( + cls, + unit: UnitBase, + fraction: bool | Literal["inline", "multiline"] = False, + deprecations: DeprecatedUnitAction = DeprecatedUnitAction.WARN, + ) -> str: + # Remove units that aren't known to the format + unit = cls._decompose_to_known_units(unit) + + if not unit.bases: + if unit.scale == 1: + return "---" + elif is_effectively_unity(unit.scale * 100.0): + return "%" + + return super().to_string(unit, fraction=fraction) diff --git a/astropy/units/format/cds_lextab.py b/astropy/units/format/cds_lextab.py new file mode 100644 index 000000000000..d2a62e6b98fb --- /dev/null +++ b/astropy/units/format/cds_lextab.py @@ -0,0 +1,20 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +# This file was automatically generated from ply. To re-generate this file, +# remove it from this folder, then build astropy and run the tests in-place: +# +# python setup.py build_ext --inplace +# pytest astropy/units +# +# You can then commit the changes to this file. + +# cds_lextab.py. This file automatically created by PLY (version 3.11). Don't edit! +_tabversion = '3.10' +_lextokens = set(('CLOSE_BRACKET', 'CLOSE_PAREN', 'DIMENSIONLESS', 'DIVISION', 'FLOAT', 'INT', 'OPEN_BRACKET', 'OPEN_PAREN', 'PRODUCT', 'UNIT', 'X')) +_lexreflags = 32 +_lexliterals = '' +_lexstateinfo = {'INITIAL': 'inclusive'} +_lexstatere = {'INITIAL': [('(?P[+-]?((\\d+\\.?\\d+)|(\\.\\d+))([eE][+-]?\\d+)?)|(?P[+-]?\\d+)|(?P[x×])|(?P%|°|\\\\h|(a|eps|mu)0|((?!\\d)\\w)+)|(?P---|-)|(?P\\.)|(?P\\()|(?P\\))|(?P\\[)|(?P\\])|(?P/)', [None, ('t_FLOAT', 'FLOAT'), None, None, None, None, ('t_INT', 'INT'), ('t_X', 'X'), ('t_UNIT', 'UNIT'), None, None, ('t_DIMENSIONLESS', 'DIMENSIONLESS'), (None, 'PRODUCT'), (None, 'OPEN_PAREN'), (None, 'CLOSE_PAREN'), (None, 'OPEN_BRACKET'), (None, 'CLOSE_BRACKET'), (None, 'DIVISION')])]} +_lexstateignore = {'INITIAL': ''} +_lexstateerrorf = {'INITIAL': 't_error'} +_lexstateeoff = {} diff --git a/astropy/units/format/cds_parsetab.py b/astropy/units/format/cds_parsetab.py new file mode 100644 index 000000000000..059fa5b43a54 --- /dev/null +++ b/astropy/units/format/cds_parsetab.py @@ -0,0 +1,61 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +# This file was automatically generated from ply. To re-generate this file, +# remove it from this folder, then build astropy and run the tests in-place: +# +# python setup.py build_ext --inplace +# pytest astropy/units +# +# You can then commit the changes to this file. + + +# cds_parsetab.py +# This file is automatically generated. Do not edit. +# pylint: disable=W,C,R +_tabversion = '3.10' + +_lr_method = 'LALR' + +_lr_signature = 'CLOSE_BRACKET CLOSE_PAREN DIMENSIONLESS DIVISION FLOAT INT OPEN_BRACKET OPEN_PAREN PRODUCT UNIT X\n main : factor combined_units\n | combined_units\n | DIMENSIONLESS\n | OPEN_BRACKET combined_units CLOSE_BRACKET\n | OPEN_BRACKET DIMENSIONLESS CLOSE_BRACKET\n | factor\n \n combined_units : product_of_units\n | division_of_units\n \n product_of_units : unit_expression PRODUCT combined_units\n | unit_expression\n \n division_of_units : DIVISION unit_expression\n | combined_units DIVISION unit_expression\n \n unit_expression : unit_with_power\n | OPEN_PAREN combined_units CLOSE_PAREN\n \n factor : FLOAT X INT INT\n | INT X INT INT\n | INT INT\n | INT\n | FLOAT\n \n unit_with_power : UNIT INT\n | UNIT\n ' + +_lr_action_items = {'DIMENSIONLESS':([0,5,],[4,18,]),'OPEN_BRACKET':([0,],[5,]),'FLOAT':([0,],[6,]),'INT':([0,7,14,19,21,29,30,],[7,20,25,29,30,33,34,]),'DIVISION':([0,2,3,5,6,7,8,9,10,12,13,14,15,17,20,22,23,24,25,26,31,32,33,34,],[11,11,16,11,-19,-18,-7,-8,-10,-13,11,-21,16,16,-17,11,-11,16,-20,-12,16,-14,-15,-16,]),'OPEN_PAREN':([0,2,5,6,7,11,13,16,20,22,33,34,],[13,13,13,-19,-18,13,13,13,-17,13,-15,-16,]),'UNIT':([0,2,5,6,7,11,13,16,20,22,33,34,],[14,14,14,-19,-18,14,14,14,-17,14,-15,-16,]),'$end':([1,2,3,4,6,7,8,9,10,12,14,15,20,23,25,26,27,28,31,32,33,34,],[0,-6,-2,-3,-19,-18,-7,-8,-10,-13,-21,-1,-17,-11,-20,-12,-4,-5,-9,-14,-15,-16,]),'X':([6,7,],[19,21,]),'CLOSE_BRACKET':([8,9,10,12,14,17,18,23,25,26,31,32,],[-7,-8,-10,-13,-21,27,28,-11,-20,-12,-9,-14,]),'CLOSE_PAREN':([8,9,10,12,14,23,24,25,26,31,32,],[-7,-8,-10,-13,-21,-11,32,-20,-12,-9,-14,]),'PRODUCT':([10,12,14,25,32,],[22,-13,-21,-20,-14,]),} + +_lr_action = {} +for _k, _v in _lr_action_items.items(): + for _x,_y in zip(_v[0],_v[1]): + if not _x in _lr_action: _lr_action[_x] = {} + _lr_action[_x][_k] = _y +del _lr_action_items + +_lr_goto_items = {'main':([0,],[1,]),'factor':([0,],[2,]),'combined_units':([0,2,5,13,22,],[3,15,17,24,31,]),'product_of_units':([0,2,5,13,22,],[8,8,8,8,8,]),'division_of_units':([0,2,5,13,22,],[9,9,9,9,9,]),'unit_expression':([0,2,5,11,13,16,22,],[10,10,10,23,10,26,10,]),'unit_with_power':([0,2,5,11,13,16,22,],[12,12,12,12,12,12,12,]),} + +_lr_goto = {} +for _k, _v in _lr_goto_items.items(): + for _x, _y in zip(_v[0], _v[1]): + if not _x in _lr_goto: _lr_goto[_x] = {} + _lr_goto[_x][_k] = _y +del _lr_goto_items +_lr_productions = [ + ("S' -> main","S'",1,None,None,None), + ('main -> factor combined_units','main',2,'p_main','cds.py',131), + ('main -> combined_units','main',1,'p_main','cds.py',132), + ('main -> DIMENSIONLESS','main',1,'p_main','cds.py',133), + ('main -> OPEN_BRACKET combined_units CLOSE_BRACKET','main',3,'p_main','cds.py',134), + ('main -> OPEN_BRACKET DIMENSIONLESS CLOSE_BRACKET','main',3,'p_main','cds.py',135), + ('main -> factor','main',1,'p_main','cds.py',136), + ('combined_units -> product_of_units','combined_units',1,'p_combined_units','cds.py',149), + ('combined_units -> division_of_units','combined_units',1,'p_combined_units','cds.py',150), + ('product_of_units -> unit_expression PRODUCT combined_units','product_of_units',3,'p_product_of_units','cds.py',156), + ('product_of_units -> unit_expression','product_of_units',1,'p_product_of_units','cds.py',157), + ('division_of_units -> DIVISION unit_expression','division_of_units',2,'p_division_of_units','cds.py',166), + ('division_of_units -> combined_units DIVISION unit_expression','division_of_units',3,'p_division_of_units','cds.py',167), + ('unit_expression -> unit_with_power','unit_expression',1,'p_unit_expression','cds.py',176), + ('unit_expression -> OPEN_PAREN combined_units CLOSE_PAREN','unit_expression',3,'p_unit_expression','cds.py',177), + ('factor -> FLOAT X INT INT','factor',4,'p_factor','cds.py',186), + ('factor -> INT X INT INT','factor',4,'p_factor','cds.py',187), + ('factor -> INT INT','factor',2,'p_factor','cds.py',188), + ('factor -> INT','factor',1,'p_factor','cds.py',189), + ('factor -> FLOAT','factor',1,'p_factor','cds.py',190), + ('unit_with_power -> UNIT INT','unit_with_power',2,'p_unit_with_power','cds.py',206), + ('unit_with_power -> UNIT','unit_with_power',1,'p_unit_with_power','cds.py',207), +] diff --git a/astropy/units/format/console.py b/astropy/units/format/console.py new file mode 100644 index 000000000000..68dec5719176 --- /dev/null +++ b/astropy/units/format/console.py @@ -0,0 +1,64 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +""" +Handles the "Console" unit format. +""" + +from typing import ClassVar, Literal + +from astropy.units.core import UnitBase +from astropy.units.enums import DeprecatedUnitAction + +from . import base + + +class Console(base.Base): + """ + Output-only format for to display pretty formatting at the + console. + + For example:: + + >>> import astropy.units as u + >>> print(u.Ry.decompose().to_string('console')) # doctest: +FLOAT_CMP + 2.1798721*10^-18 m^2 kg s^-2 + >>> print(u.Ry.decompose().to_string('console', fraction='multiline')) # doctest: +FLOAT_CMP + m^2 kg + 2.1798721*10^-18 ------ + s^2 + >>> print(u.Ry.decompose().to_string('console', fraction='inline')) # doctest: +FLOAT_CMP + 2.1798721*10^-18 m^2 kg / s^2 + """ + + _line: ClassVar[str] = "-" + _space: ClassVar[str] = " " + + @classmethod + def _format_superscript(cls, number: str) -> str: + return f"^{number}" + + @classmethod + def _format_multiline_fraction( + cls, scale: str, numerator: str, denominator: str + ) -> str: + fraclength = max(len(numerator), len(denominator)) + f = f"{{0:<{len(scale)}s}}{{1:^{fraclength}s}}" + + return "\n".join( + ( + f.format("", numerator), + f.format(scale, cls._line * fraclength), + f.format("", denominator), + ) + ) + + @classmethod + def to_string( + cls, + unit: UnitBase, + fraction: bool | Literal["inline", "multiline"] = False, + deprecations: DeprecatedUnitAction = DeprecatedUnitAction.WARN, + ) -> str: + # Change default of fraction to False, i.e., we typeset + # without a fraction by default. + return super().to_string(unit, fraction=fraction) diff --git a/astropy/units/format/fits.py b/astropy/units/format/fits.py new file mode 100644 index 000000000000..50663e0a95f9 --- /dev/null +++ b/astropy/units/format/fits.py @@ -0,0 +1,100 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +""" +Handles the "FITS" unit format. +""" + +from typing import Literal + +import numpy as np + +from astropy.units.core import CompositeUnit, UnitBase +from astropy.units.enums import DeprecatedUnitAction +from astropy.units.errors import UnitScaleError +from astropy.utils import classproperty + +from . import Base +from .generic import _GenericParserMixin + + +class FITS(Base, _GenericParserMixin): + """ + The FITS standard unit format. + + This supports the format defined in the Units section of the `FITS + Standard `_. + """ + + @classproperty(lazy=True) + def _units(cls) -> dict[str, UnitBase]: + from astropy import units as u + + names = { + "Celsius": u.deg_C, + "deg C": u.deg_C, + "dbyte": u.Unit("dbyte", 0.1 * u.byte), + "as": u.attosecond, + } + bases = [ + "m", "g", "s", "rad", "sr", "K", "A", "mol", "cd", + "Hz", "J", "W", "V", "N", "Pa", "C", "Ohm", "S", + "F", "Wb", "T", "H", "lm", "lx", "a", "yr", "eV", + "pc", "Jy", "mag", "R", "bit", "byte", "G", "barn", + ] # fmt: skip + prefixes = [ + "y", "z", "a", "f", "p", "n", "u", "m", "c", "d", + "", "da", "h", "k", "M", "G", "T", "P", "E", "Z", "Y", + ] # fmt: skip + + for key in (prefix + base for base in bases for prefix in prefixes): + if key not in names: + names[key] = getattr(u, key) + simple_units = [ + "deg", "arcmin", "arcsec", "mas", "min", "h", "d", "Ry", + "solMass", "u", "solLum", "solRad", "AU", "lyr", "count", + "ct", "photon", "ph", "pixel", "pix", "D", "Sun", "chan", + "bin", "voxel", "adu", "beam", "erg", "Angstrom", "angstrom", + ] # fmt: skip + names.update((unit, getattr(u, unit)) for unit in simple_units) + + return names + + @classmethod + def to_string( + cls, + unit: UnitBase, + fraction: bool | Literal["inline", "multiline"] = False, + deprecations: DeprecatedUnitAction = DeprecatedUnitAction.WARN, + ) -> str: + # Remove units that aren't known to the format + unit = cls._decompose_to_known_units(unit) + + parts = [] + + base = np.log10(unit.scale) + + if base % 1.0 != 0.0: + raise UnitScaleError( + "The FITS unit format is not able to represent scales " + "that are not powers of 10. Multiply your data by " + f"{unit.scale:e}." + ) + elif unit.scale != 1.0: + # We could override format_exponential_notation to set the + # scale factor but that would give the wrong impression that + # all values in FITS are set that way. So, instead do it + # here, and use a unity-scale unit for the rest. + parts.append(f"10**{int(base)}") + unit = CompositeUnit(1, unit.bases, unit.powers) + + if unit.bases: + parts.append(super().to_string(unit, fraction=fraction)) + + return cls._scale_unit_separator.join(parts) + + @classmethod + def parse(cls, s: str, debug: bool = False) -> UnitBase: + result = cls._do_parse(s, debug) + if hasattr(result, "function_unit"): + raise ValueError("Function units are not yet supported for FITS units.") + return result diff --git a/astropy/units/format/generic.py b/astropy/units/format/generic.py new file mode 100644 index 000000000000..18e04fbad8c1 --- /dev/null +++ b/astropy/units/format/generic.py @@ -0,0 +1,517 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +# This module includes files automatically generated from ply (these end in +# _lextab.py and _parsetab.py). To generate these files, remove them from this +# folder, then build astropy and run the tests in-place: +# +# python setup.py build_ext --inplace +# pytest astropy/units +# +# You can then commit the changes to the re-generated _lextab.py and +# _parsetab.py files. + +""" +Handles a "generic" string format for units +""" + +import re +import unicodedata +import warnings +from fractions import Fraction +from re import Match, Pattern +from typing import ClassVar, Final + +import numpy as np + +from astropy.extern.ply.lex import Lexer +from astropy.units.core import CompositeUnit, Unit, UnitBase, get_current_unit_registry +from astropy.units.enums import DeprecatedUnitAction +from astropy.units.errors import UnitsWarning +from astropy.units.typing import UnitScale +from astropy.utils import classproperty, parsing +from astropy.utils.parsing import ThreadSafeParser + +from .base import Base, _ParsingFormatMixin + + +class _GenericParserMixin(_ParsingFormatMixin): + """Provide the parser used by Generic, FITS and VOUnit.""" + + _tokens: ClassVar[tuple[str, ...]] = ( + "COMMA", + "POWER", + "PRODUCT", + "DIVISION", + "OPEN_PAREN", + "CLOSE_PAREN", + "FUNCNAME", + "UNIT", + "SIGN", + "UINT", + "UFLOAT", + ) + + @classproperty(lazy=True) + def _lexer(cls) -> Lexer: + tokens = cls._tokens + + t_COMMA = r"\," + t_PRODUCT = "[*.]" + t_DIVISION = "/" + t_POWER = r"\^|(\*\*)" + t_OPEN_PAREN = r"\(" + t_CLOSE_PAREN = r"\)" + + # NOTE THE ORDERING OF THESE RULES IS IMPORTANT!! + # Regular expression rules for simple tokens + def t_UFLOAT(t): + r"((\d+\.?\d*)|(\.\d+))([eE][+-]?\d+)?" + if not re.search(r"[eE\.]", t.value): + t.type = "UINT" + t.value = int(t.value) + elif t.value.endswith("."): + t.type = "UINT" + t.value = int(t.value[:-1]) + else: + t.value = float(t.value) + return t + + def t_UINT(t): + r"\d+" + t.value = int(t.value) + return t + + def t_SIGN(t): + r"[+-](?=\d)" + t.value = int(t.value + "1") + return t + + # This needs to be a function so we can force it to happen + # before t_UNIT + def t_FUNCNAME(t): + r"((sqrt)|(ln)|(exp)|(log)|(mag)|(dB)|(dex))(?=\ *\()" + return t + + # A possible unit is something that consists of characters not used + # for anything else: no spaces, no digits, signs, periods, stars, + # carets, parentheses or commas. + def t_UNIT(t): + r"[^\s\d+\-\./\*\^\(\)\,]+" + t.value = cls._get_unit(t) + return t + + t_ignore = " " + + # Error handling rule + def t_error(t): + raise ValueError(f"Invalid character at col {t.lexpos}") + + return parsing.lex( + lextab="generic_lextab", package="astropy/units", reflags=int(re.UNICODE) + ) + + @classproperty(lazy=True) + def _parser(cls) -> ThreadSafeParser: + """ + The grammar here is based on the description in the `FITS + standard + `_, + Section 4.3, which is not terribly precise. The exact grammar + is here is based on the YACC grammar in the `unity library + `_. + + This same grammar is used by the `"fits"` and `"vounit"` + formats, the only difference being the set of available unit + strings. + """ + tokens = cls._tokens + + def p_main(p): + """ + main : unit + | structured_unit + | structured_subunit + """ + if isinstance(p[1], tuple): + # Unpack possible StructuredUnit inside a tuple, ie., + # ignore any set of very outer parentheses. + p[0] = p[1][0] + else: + p[0] = p[1] + + def p_structured_subunit(p): + """ + structured_subunit : OPEN_PAREN structured_unit CLOSE_PAREN + """ + # We hide a structured unit enclosed by parentheses inside + # a tuple, so that we can easily distinguish units like + # "(au, au/day), yr" from "au, au/day, yr". + p[0] = (p[2],) + + def p_structured_unit(p): + """ + structured_unit : subunit COMMA + | subunit COMMA subunit + """ + from astropy.units.structured import StructuredUnit + + inputs = (p[1],) if len(p) == 3 else (p[1], p[3]) + units = () + for subunit in inputs: + if isinstance(subunit, tuple): + # Structured unit that should be its own entry in the + # new StructuredUnit (was enclosed in parentheses). + units += subunit + elif isinstance(subunit, StructuredUnit): + # Structured unit whose entries should be + # individually added to the new StructuredUnit. + units += subunit.values() + else: + # Regular unit to be added to the StructuredUnit. + units += (subunit,) + + p[0] = StructuredUnit(units) + + def p_subunit(p): + """ + subunit : unit + | structured_unit + | structured_subunit + """ + p[0] = p[1] + + def p_unit(p): + """ + unit : product_of_units + | factor product_of_units + | factor PRODUCT product_of_units + | division_product_of_units + | factor division_product_of_units + | factor PRODUCT division_product_of_units + | inverse_unit + | factor inverse_unit + | factor PRODUCT inverse_unit + | factor + """ + if len(p) == 2: + p[0] = Unit(p[1]) + elif len(p) == 3: + p[0] = CompositeUnit(p[1] * p[2].scale, p[2].bases, p[2].powers) + elif len(p) == 4: + p[0] = CompositeUnit(p[1] * p[3].scale, p[3].bases, p[3].powers) + + def p_division_product_of_units(p): + """ + division_product_of_units : division_product_of_units DIVISION product_of_units + | product_of_units + """ + if len(p) == 4: + p[0] = Unit(p[1] / p[3]) + else: + p[0] = p[1] + + def p_inverse_unit(p): + """ + inverse_unit : DIVISION unit_expression + """ + p[0] = p[2] ** -1 + + def p_factor(p): + """ + factor : factor_fits + | factor_float + | factor_int + """ + p[0] = p[1] + + def p_factor_float(p): + """ + factor_float : signed_float + | signed_float UINT signed_int + | signed_float UINT POWER numeric_power + """ + if cls.name == "fits": + raise ValueError("Numeric factor not supported by FITS") + if len(p) == 4: + p[0] = p[1] * p[2] ** float(p[3]) + elif len(p) == 5: + p[0] = p[1] * p[2] ** float(p[4]) + elif len(p) == 2: + p[0] = p[1] + + def p_factor_int(p): + """ + factor_int : UINT + | UINT signed_int + | UINT POWER numeric_power + | UINT UINT signed_int + | UINT UINT POWER numeric_power + """ + if cls.name == "fits": + raise ValueError("Numeric factor not supported by FITS") + if len(p) == 2: + p[0] = p[1] + elif len(p) == 3: + p[0] = p[1] ** float(p[2]) + elif len(p) == 4: + if isinstance(p[2], int): + p[0] = p[1] * p[2] ** float(p[3]) + else: + p[0] = p[1] ** float(p[3]) + elif len(p) == 5: + p[0] = p[1] * p[2] ** p[4] + + def p_factor_fits(p): + """ + factor_fits : UINT POWER OPEN_PAREN signed_int CLOSE_PAREN + | UINT POWER OPEN_PAREN UINT CLOSE_PAREN + | UINT POWER signed_int + | UINT POWER UINT + | UINT SIGN UINT + | UINT OPEN_PAREN signed_int CLOSE_PAREN + """ + if p[1] != 10: + if cls.name == "fits": + raise ValueError("Base must be 10") + else: + return + if len(p) == 4: + if p[2] in ("**", "^"): + p[0] = 10 ** p[3] + else: + p[0] = 10 ** (p[2] * p[3]) + elif len(p) == 5: + p[0] = 10 ** p[3] + elif len(p) == 6: + p[0] = 10 ** p[4] + + def p_product_of_units(p): + """ + product_of_units : unit_expression PRODUCT product_of_units + | unit_expression product_of_units + | unit_expression + """ + if len(p) == 2: + p[0] = p[1] + elif len(p) == 3: + p[0] = p[1] * p[2] + else: + p[0] = p[1] * p[3] + + def p_unit_expression(p): + """ + unit_expression : function + | unit_with_power + | OPEN_PAREN product_of_units CLOSE_PAREN + """ + if len(p) == 2: + p[0] = p[1] + else: + p[0] = p[2] + + def p_unit_with_power(p): + """ + unit_with_power : UNIT POWER numeric_power + | UNIT numeric_power + | UNIT + """ + if len(p) == 2: + p[0] = p[1] + elif len(p) == 3: + p[0] = p[1] ** p[2] + else: + p[0] = p[1] ** p[3] + + def p_numeric_power(p): + """ + numeric_power : sign UINT + | OPEN_PAREN paren_expr CLOSE_PAREN + """ + if len(p) == 3: + p[0] = p[1] * p[2] + elif len(p) == 4: + p[0] = p[2] + + def p_paren_expr(p): + """ + paren_expr : sign UINT + | signed_float + | frac + """ + if len(p) == 3: + p[0] = p[1] * p[2] + else: + p[0] = p[1] + + def p_frac(p): + """ + frac : sign UINT DIVISION sign UINT + """ + p[0] = Fraction(p[1] * p[2], p[4] * p[5]) + + def p_sign(p): + """ + sign : SIGN + | + """ + if len(p) == 2: + p[0] = p[1] + else: + p[0] = 1 + + def p_signed_int(p): + """ + signed_int : SIGN UINT + """ + p[0] = p[1] * p[2] + + def p_signed_float(p): + """ + signed_float : sign UINT + | sign UFLOAT + """ + p[0] = p[1] * p[2] + + def p_function(p): + """ + function : FUNCNAME OPEN_PAREN main CLOSE_PAREN + """ + if p[1] == "sqrt": + p[0] = p[3] ** 0.5 + return + elif p[1] in ("mag", "dB", "dex"): + try: + function_unit = cls._validate_unit(p[1]) + except KeyError: + raise ValueError(cls._invalid_unit_error_message(p[1])) from None + # In Generic, this is callable, but that does not have to + # be the case in subclasses (e.g., in VOUnit it is not). + if callable(function_unit): + p[0] = function_unit(p[3]) + return + + raise ValueError(f"'{p[1]}' is not a recognized function") + + def p_error(p): + raise ValueError() + + return parsing.yacc(tabmodule="generic_parsetab", package="astropy/units") + + +class Generic(Base, _GenericParserMixin): + """ + A "generic" format. + + The syntax of the format is based directly on the FITS standard, + but instead of only supporting the units that FITS knows about, it + supports any unit available in the `astropy.units` namespace. + """ + + @classproperty + def _units(cls) -> dict[str, UnitBase]: + return get_current_unit_registry().registry + + @classmethod + def _validate_unit( + cls, s: str, deprecations: DeprecatedUnitAction = DeprecatedUnitAction.WARN + ) -> UnitBase: + if s in cls._unit_symbols: + s = cls._unit_symbols[s] + + elif not s.isascii(): + if s[0].startswith("°"): + s = "deg" if len(s) == 1 else "deg_" + s[1:] + if len(s) > 1 and s[-1] in cls._unit_suffix_symbols: + s = s[:-1] + cls._unit_suffix_symbols[s[-1]] + elif s.endswith("R\N{INFINITY}"): + s = s[:-2] + "Ry" + + return cls._units[s] + + @classmethod + def _invalid_unit_error_message(cls, unit: str) -> str: + return f"{unit} is not a valid unit. {cls._did_you_mean_units(unit)}" + + _unit_symbols: ClassVar[dict[str, str]] = { + "%": "percent", + "\N{PRIME}": "arcmin", + "\N{DOUBLE PRIME}": "arcsec", + "\N{MODIFIER LETTER SMALL H}": "hourangle", + "e\N{SUPERSCRIPT MINUS}": "electron", + } + + _unit_suffix_symbols: ClassVar[dict[str, str]] = { + "\N{CIRCLED DOT OPERATOR}": "sun", + "\N{SUN}": "sun", + "\N{CIRCLED PLUS}": "earth", + "\N{EARTH}": "earth", + "\N{JUPITER}": "jupiter", + "\N{LATIN SUBSCRIPT SMALL LETTER E}": "_e", + "\N{LATIN SUBSCRIPT SMALL LETTER P}": "_p", + } + + _translations: ClassVar[dict[int, str]] = str.maketrans({"\N{MINUS SIGN}": "-"}) + """Character translations that should be applied before parsing a string.""" + + _superscripts: Final[str] = ( + "\N{SUPERSCRIPT MINUS}" + "\N{SUPERSCRIPT PLUS SIGN}" + "\N{SUPERSCRIPT ZERO}" + "\N{SUPERSCRIPT ONE}" + "\N{SUPERSCRIPT TWO}" + "\N{SUPERSCRIPT THREE}" + "\N{SUPERSCRIPT FOUR}" + "\N{SUPERSCRIPT FIVE}" + "\N{SUPERSCRIPT SIX}" + "\N{SUPERSCRIPT SEVEN}" + "\N{SUPERSCRIPT EIGHT}" + "\N{SUPERSCRIPT NINE}" + ) + + _superscript_translations: ClassVar[dict[int, int]] = str.maketrans( + _superscripts, "-+0123456789" + ) + _regex_superscript: ClassVar[Pattern[str]] = re.compile( + f"[{_superscripts}]?[{_superscripts[2:]}]+" + ) + + @classmethod + def _convert_superscript(cls, m: Match[str]) -> str: + return f"({m.group().translate(cls._superscript_translations)})" + + @classmethod + def parse(cls, s: str, debug: bool = False) -> UnitBase: + if not isinstance(s, str): + s = s.decode("ascii") + elif not s.isascii(): + # common normalization of unicode strings to avoid + # having to deal with multiple representations of + # the same character. This normalizes to "composed" form + # and will e.g. convert OHM SIGN to GREEK CAPITAL LETTER OMEGA + s = unicodedata.normalize("NFC", s) + # Translate some basic unicode items that we'd like to support on + # input but are not standard. + s = s.translate(cls._translations) + + # TODO: might the below be better done in the parser/lexer? + # Translate superscripts to parenthesized numbers; this ensures + # that mixes of superscripts and regular numbers fail. + s = cls._regex_superscript.sub(cls._convert_superscript, s) + + result = cls._do_parse(s, debug) + + # Check for excess solidi, but exclude fractional exponents (accepted) + n_slashes = s.count("/") + if n_slashes > 1 and (n_slashes - len(re.findall(r"\(\d+/\d+\)", s))) > 1: + warnings.warn( + f"'{s}' contains multiple slashes, which is " + "discouraged by the FITS standard", + UnitsWarning, + ) + return result + + @classmethod + def format_exponential_notation( + cls, val: UnitScale | np.number, format_spec: str = "g" + ) -> str: + return format(val, format_spec) diff --git a/astropy/units/format/generic_lextab.py b/astropy/units/format/generic_lextab.py new file mode 100644 index 000000000000..9bb4cd0da4fb --- /dev/null +++ b/astropy/units/format/generic_lextab.py @@ -0,0 +1,20 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +# This file was automatically generated from ply. To re-generate this file, +# remove it from this folder, then build astropy and run the tests in-place: +# +# python setup.py build_ext --inplace +# pytest astropy/units +# +# You can then commit the changes to this file. + +# generic_lextab.py. This file automatically created by PLY (version 3.11). Don't edit! +_tabversion = '3.10' +_lextokens = set(('CLOSE_PAREN', 'COMMA', 'DIVISION', 'FUNCNAME', 'OPEN_PAREN', 'POWER', 'PRODUCT', 'SIGN', 'UFLOAT', 'UINT', 'UNIT')) +_lexreflags = 32 +_lexliterals = '' +_lexstateinfo = {'INITIAL': 'inclusive'} +_lexstatere = {'INITIAL': [('(?P((\\d+\\.?\\d*)|(\\.\\d+))([eE][+-]?\\d+)?)|(?P\\d+)|(?P[+-](?=\\d))|(?P((sqrt)|(ln)|(exp)|(log)|(mag)|(dB)|(dex))(?=\\ *\\())|(?P[^\\s\\d+\\-\\./\\*\\^\\(\\)\\,]+)|(?P\\^|(\\*\\*))|(?P[*.])|(?P\\,)|(?P\\()|(?P\\))|(?P/)', [None, ('t_UFLOAT', 'UFLOAT'), None, None, None, None, ('t_UINT', 'UINT'), ('t_SIGN', 'SIGN'), ('t_FUNCNAME', 'FUNCNAME'), None, None, None, None, None, None, None, None, ('t_UNIT', 'UNIT'), (None, 'POWER'), None, (None, 'PRODUCT'), (None, 'COMMA'), (None, 'OPEN_PAREN'), (None, 'CLOSE_PAREN'), (None, 'DIVISION')])]} +_lexstateignore = {'INITIAL': ' '} +_lexstateerrorf = {'INITIAL': 't_error'} +_lexstateeoff = {} diff --git a/astropy/units/format/generic_parsetab.py b/astropy/units/format/generic_parsetab.py new file mode 100644 index 000000000000..a2f3c95205d4 --- /dev/null +++ b/astropy/units/format/generic_parsetab.py @@ -0,0 +1,100 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +# This file was automatically generated from ply. To re-generate this file, +# remove it from this folder, then build astropy and run the tests in-place: +# +# python setup.py build_ext --inplace +# pytest astropy/units +# +# You can then commit the changes to this file. + + +# generic_parsetab.py +# This file is automatically generated. Do not edit. +# pylint: disable=W,C,R +_tabversion = '3.10' + +_lr_method = 'LALR' + +_lr_signature = 'CLOSE_PAREN COMMA DIVISION FUNCNAME OPEN_PAREN POWER PRODUCT SIGN UFLOAT UINT UNIT\n main : unit\n | structured_unit\n | structured_subunit\n \n structured_subunit : OPEN_PAREN structured_unit CLOSE_PAREN\n \n structured_unit : subunit COMMA\n | subunit COMMA subunit\n \n subunit : unit\n | structured_unit\n | structured_subunit\n \n unit : product_of_units\n | factor product_of_units\n | factor PRODUCT product_of_units\n | division_product_of_units\n | factor division_product_of_units\n | factor PRODUCT division_product_of_units\n | inverse_unit\n | factor inverse_unit\n | factor PRODUCT inverse_unit\n | factor\n \n division_product_of_units : division_product_of_units DIVISION product_of_units\n | product_of_units\n \n inverse_unit : DIVISION unit_expression\n \n factor : factor_fits\n | factor_float\n | factor_int\n \n factor_float : signed_float\n | signed_float UINT signed_int\n | signed_float UINT POWER numeric_power\n \n factor_int : UINT\n | UINT signed_int\n | UINT POWER numeric_power\n | UINT UINT signed_int\n | UINT UINT POWER numeric_power\n \n factor_fits : UINT POWER OPEN_PAREN signed_int CLOSE_PAREN\n | UINT POWER OPEN_PAREN UINT CLOSE_PAREN\n | UINT POWER signed_int\n | UINT POWER UINT\n | UINT SIGN UINT\n | UINT OPEN_PAREN signed_int CLOSE_PAREN\n \n product_of_units : unit_expression PRODUCT product_of_units\n | unit_expression product_of_units\n | unit_expression\n \n unit_expression : function\n | unit_with_power\n | OPEN_PAREN product_of_units CLOSE_PAREN\n \n unit_with_power : UNIT POWER numeric_power\n | UNIT numeric_power\n | UNIT\n \n numeric_power : sign UINT\n | OPEN_PAREN paren_expr CLOSE_PAREN\n \n paren_expr : sign UINT\n | signed_float\n | frac\n \n frac : sign UINT DIVISION sign UINT\n \n sign : SIGN\n |\n \n signed_int : SIGN UINT\n \n signed_float : sign UINT\n | sign UFLOAT\n \n function : FUNCNAME OPEN_PAREN main CLOSE_PAREN\n ' + +_lr_action_items = {'OPEN_PAREN':([0,6,10,11,12,13,14,15,16,17,18,20,21,22,25,28,29,30,31,36,40,42,45,46,47,50,51,60,62,63,65,67,68,71,72,73,75,76,81,82,85,86,87,88,90,91,],[10,28,31,28,-23,-24,-25,28,-43,-44,41,-26,45,49,28,28,28,10,31,28,66,-30,10,49,-47,-58,-59,-45,-32,49,-37,-36,-31,-38,-27,49,-46,-49,-33,-57,-39,-28,-60,-50,-35,-34,]),'DIVISION':([0,5,6,7,10,11,12,13,14,16,17,18,20,22,24,25,26,30,31,33,37,42,45,47,50,51,52,53,56,60,61,62,65,67,68,71,72,75,76,81,82,85,86,87,88,89,90,91,],[15,-21,15,29,15,-42,-23,-24,-25,-43,-44,-29,-26,-48,-21,15,29,15,15,-21,-41,-30,15,-47,-58,-59,-21,29,-20,-45,-40,-32,-37,-36,-31,-38,-27,-46,-49,-33,-57,-39,-28,-60,-50,92,-35,-34,]),'UINT':([0,10,18,19,20,22,23,30,31,40,43,45,46,48,49,50,51,63,64,66,69,73,78,92,93,],[18,18,39,-55,44,-56,50,18,18,65,71,18,-56,76,-56,-58,-59,-56,82,83,82,-56,89,-56,94,]),'FUNCNAME':([0,6,10,11,12,13,14,15,16,17,18,20,22,25,28,29,30,31,36,42,45,47,50,51,60,62,65,67,68,71,72,75,76,81,82,85,86,87,88,90,91,],[21,21,21,21,-23,-24,-25,21,-43,-44,-29,-26,-48,21,21,21,21,21,21,-30,21,-47,-58,-59,-45,-32,-37,-36,-31,-38,-27,-46,-49,-33,-57,-39,-28,-60,-50,-35,-34,]),'UNIT':([0,6,10,11,12,13,14,15,16,17,18,20,22,25,28,29,30,31,36,42,45,47,50,51,60,62,65,67,68,71,72,75,76,81,82,85,86,87,88,90,91,],[22,22,22,22,-23,-24,-25,22,-43,-44,-29,-26,-48,22,22,22,22,22,22,-30,22,-47,-58,-59,-45,-32,-37,-36,-31,-38,-27,-46,-49,-33,-57,-39,-28,-60,-50,-35,-34,]),'SIGN':([0,10,18,22,30,31,39,40,41,44,45,46,49,63,66,73,92,],[19,19,43,19,19,19,64,69,64,64,19,19,19,19,69,19,19,]),'UFLOAT':([0,10,19,23,30,31,45,49,66,69,78,],[-56,-56,-55,51,-56,-56,-56,-56,-56,-55,51,]),'$end':([1,2,3,4,5,6,7,8,11,12,13,14,16,17,18,20,22,24,26,27,30,34,35,37,38,42,47,50,51,52,53,54,56,57,58,59,60,61,62,65,67,68,71,72,75,76,81,82,85,86,87,88,90,91,],[0,-1,-2,-3,-10,-19,-13,-16,-42,-23,-24,-25,-43,-44,-29,-26,-48,-11,-14,-17,-5,-7,-9,-41,-22,-30,-47,-58,-59,-12,-15,-18,-20,-6,-8,-4,-45,-40,-32,-37,-36,-31,-38,-27,-46,-49,-33,-57,-39,-28,-60,-50,-35,-34,]),'CLOSE_PAREN':([2,3,4,5,6,7,8,11,12,13,14,16,17,18,20,22,24,26,27,30,32,33,34,35,37,38,42,47,50,51,52,53,54,55,56,57,58,59,60,61,62,65,67,68,70,71,72,74,75,76,77,79,80,81,82,83,84,85,86,87,88,89,90,91,94,],[-1,-2,-3,-10,-19,-13,-16,-42,-23,-24,-25,-43,-44,-29,-26,-48,-11,-14,-17,-5,59,60,-7,-9,-41,-22,-30,-47,-58,-59,-12,-15,-18,60,-20,-6,-8,-4,-45,-40,-32,-37,-36,-31,85,-38,-27,87,-46,-49,88,-52,-53,-33,-57,90,91,-39,-28,-60,-50,-51,-35,-34,-54,]),'COMMA':([2,3,4,5,6,7,8,9,11,12,13,14,16,17,18,20,22,24,26,27,30,32,33,34,35,37,38,42,47,50,51,52,53,54,56,57,58,59,60,61,62,65,67,68,71,72,75,76,81,82,85,86,87,88,90,91,],[-7,-8,-9,-10,-19,-13,-16,30,-42,-23,-24,-25,-43,-44,-29,-26,-48,-11,-14,-17,-5,-8,-10,-7,-9,-41,-22,-30,-47,-58,-59,-12,-15,-18,-20,30,-8,-4,-45,-40,-32,-37,-36,-31,-38,-27,-46,-49,-33,-57,-39,-28,-60,-50,-35,-34,]),'PRODUCT':([6,11,12,13,14,16,17,18,20,22,42,47,50,51,60,62,65,67,68,71,72,75,76,81,82,85,86,87,88,90,91,],[25,36,-23,-24,-25,-43,-44,-29,-26,-48,-30,-47,-58,-59,-45,-32,-37,-36,-31,-38,-27,-46,-49,-33,-57,-39,-28,-60,-50,-35,-34,]),'POWER':([18,22,39,44,],[40,46,63,73,]),} + +_lr_action = {} +for _k, _v in _lr_action_items.items(): + for _x,_y in zip(_v[0],_v[1]): + if not _x in _lr_action: _lr_action[_x] = {} + _lr_action[_x][_k] = _y +del _lr_action_items + +_lr_goto_items = {'main':([0,45,],[1,74,]),'unit':([0,10,30,31,45,],[2,34,34,34,2,]),'structured_unit':([0,10,30,31,45,],[3,32,58,32,3,]),'structured_subunit':([0,10,30,31,45,],[4,35,35,35,4,]),'product_of_units':([0,6,10,11,25,28,29,30,31,36,45,],[5,24,33,37,52,55,56,5,33,61,5,]),'factor':([0,10,30,31,45,],[6,6,6,6,6,]),'division_product_of_units':([0,6,10,25,30,31,45,],[7,26,7,53,7,7,7,]),'inverse_unit':([0,6,10,25,30,31,45,],[8,27,8,54,8,8,8,]),'subunit':([0,10,30,31,45,],[9,9,57,9,9,]),'unit_expression':([0,6,10,11,15,25,28,29,30,31,36,45,],[11,11,11,11,38,11,11,11,11,11,11,11,]),'factor_fits':([0,10,30,31,45,],[12,12,12,12,12,]),'factor_float':([0,10,30,31,45,],[13,13,13,13,13,]),'factor_int':([0,10,30,31,45,],[14,14,14,14,14,]),'function':([0,6,10,11,15,25,28,29,30,31,36,45,],[16,16,16,16,16,16,16,16,16,16,16,16,]),'unit_with_power':([0,6,10,11,15,25,28,29,30,31,36,45,],[17,17,17,17,17,17,17,17,17,17,17,17,]),'signed_float':([0,10,30,31,45,49,66,],[20,20,20,20,20,79,79,]),'sign':([0,10,22,30,31,40,45,46,49,63,66,73,92,],[23,23,48,23,23,48,23,48,78,48,78,48,93,]),'signed_int':([18,39,40,41,44,66,],[42,62,67,70,72,84,]),'numeric_power':([22,40,46,63,73,],[47,68,75,81,86,]),'paren_expr':([49,66,],[77,77,]),'frac':([49,66,],[80,80,]),} + +_lr_goto = {} +for _k, _v in _lr_goto_items.items(): + for _x, _y in zip(_v[0], _v[1]): + if not _x in _lr_goto: _lr_goto[_x] = {} + _lr_goto[_x][_k] = _y +del _lr_goto_items +_lr_productions = [ + ("S' -> main","S'",1,None,None,None), + ('main -> unit','main',1,'p_main','generic.py',138), + ('main -> structured_unit','main',1,'p_main','generic.py',139), + ('main -> structured_subunit','main',1,'p_main','generic.py',140), + ('structured_subunit -> OPEN_PAREN structured_unit CLOSE_PAREN','structured_subunit',3,'p_structured_subunit','generic.py',151), + ('structured_unit -> subunit COMMA','structured_unit',2,'p_structured_unit','generic.py',160), + ('structured_unit -> subunit COMMA subunit','structured_unit',3,'p_structured_unit','generic.py',161), + ('subunit -> unit','subunit',1,'p_subunit','generic.py',184), + ('subunit -> structured_unit','subunit',1,'p_subunit','generic.py',185), + ('subunit -> structured_subunit','subunit',1,'p_subunit','generic.py',186), + ('unit -> product_of_units','unit',1,'p_unit','generic.py',192), + ('unit -> factor product_of_units','unit',2,'p_unit','generic.py',193), + ('unit -> factor PRODUCT product_of_units','unit',3,'p_unit','generic.py',194), + ('unit -> division_product_of_units','unit',1,'p_unit','generic.py',195), + ('unit -> factor division_product_of_units','unit',2,'p_unit','generic.py',196), + ('unit -> factor PRODUCT division_product_of_units','unit',3,'p_unit','generic.py',197), + ('unit -> inverse_unit','unit',1,'p_unit','generic.py',198), + ('unit -> factor inverse_unit','unit',2,'p_unit','generic.py',199), + ('unit -> factor PRODUCT inverse_unit','unit',3,'p_unit','generic.py',200), + ('unit -> factor','unit',1,'p_unit','generic.py',201), + ('division_product_of_units -> division_product_of_units DIVISION product_of_units','division_product_of_units',3,'p_division_product_of_units','generic.py',212), + ('division_product_of_units -> product_of_units','division_product_of_units',1,'p_division_product_of_units','generic.py',213), + ('inverse_unit -> DIVISION unit_expression','inverse_unit',2,'p_inverse_unit','generic.py',222), + ('factor -> factor_fits','factor',1,'p_factor','generic.py',228), + ('factor -> factor_float','factor',1,'p_factor','generic.py',229), + ('factor -> factor_int','factor',1,'p_factor','generic.py',230), + ('factor_float -> signed_float','factor_float',1,'p_factor_float','generic.py',236), + ('factor_float -> signed_float UINT signed_int','factor_float',3,'p_factor_float','generic.py',237), + ('factor_float -> signed_float UINT POWER numeric_power','factor_float',4,'p_factor_float','generic.py',238), + ('factor_int -> UINT','factor_int',1,'p_factor_int','generic.py',251), + ('factor_int -> UINT signed_int','factor_int',2,'p_factor_int','generic.py',252), + ('factor_int -> UINT POWER numeric_power','factor_int',3,'p_factor_int','generic.py',253), + ('factor_int -> UINT UINT signed_int','factor_int',3,'p_factor_int','generic.py',254), + ('factor_int -> UINT UINT POWER numeric_power','factor_int',4,'p_factor_int','generic.py',255), + ('factor_fits -> UINT POWER OPEN_PAREN signed_int CLOSE_PAREN','factor_fits',5,'p_factor_fits','generic.py',273), + ('factor_fits -> UINT POWER OPEN_PAREN UINT CLOSE_PAREN','factor_fits',5,'p_factor_fits','generic.py',274), + ('factor_fits -> UINT POWER signed_int','factor_fits',3,'p_factor_fits','generic.py',275), + ('factor_fits -> UINT POWER UINT','factor_fits',3,'p_factor_fits','generic.py',276), + ('factor_fits -> UINT SIGN UINT','factor_fits',3,'p_factor_fits','generic.py',277), + ('factor_fits -> UINT OPEN_PAREN signed_int CLOSE_PAREN','factor_fits',4,'p_factor_fits','generic.py',278), + ('product_of_units -> unit_expression PRODUCT product_of_units','product_of_units',3,'p_product_of_units','generic.py',297), + ('product_of_units -> unit_expression product_of_units','product_of_units',2,'p_product_of_units','generic.py',298), + ('product_of_units -> unit_expression','product_of_units',1,'p_product_of_units','generic.py',299), + ('unit_expression -> function','unit_expression',1,'p_unit_expression','generic.py',310), + ('unit_expression -> unit_with_power','unit_expression',1,'p_unit_expression','generic.py',311), + ('unit_expression -> OPEN_PAREN product_of_units CLOSE_PAREN','unit_expression',3,'p_unit_expression','generic.py',312), + ('unit_with_power -> UNIT POWER numeric_power','unit_with_power',3,'p_unit_with_power','generic.py',321), + ('unit_with_power -> UNIT numeric_power','unit_with_power',2,'p_unit_with_power','generic.py',322), + ('unit_with_power -> UNIT','unit_with_power',1,'p_unit_with_power','generic.py',323), + ('numeric_power -> sign UINT','numeric_power',2,'p_numeric_power','generic.py',334), + ('numeric_power -> OPEN_PAREN paren_expr CLOSE_PAREN','numeric_power',3,'p_numeric_power','generic.py',335), + ('paren_expr -> sign UINT','paren_expr',2,'p_paren_expr','generic.py',344), + ('paren_expr -> signed_float','paren_expr',1,'p_paren_expr','generic.py',345), + ('paren_expr -> frac','paren_expr',1,'p_paren_expr','generic.py',346), + ('frac -> sign UINT DIVISION sign UINT','frac',5,'p_frac','generic.py',355), + ('sign -> SIGN','sign',1,'p_sign','generic.py',361), + ('sign -> ','sign',0,'p_sign','generic.py',362), + ('signed_int -> SIGN UINT','signed_int',2,'p_signed_int','generic.py',371), + ('signed_float -> sign UINT','signed_float',2,'p_signed_float','generic.py',377), + ('signed_float -> sign UFLOAT','signed_float',2,'p_signed_float','generic.py',378), + ('function -> FUNCNAME OPEN_PAREN main CLOSE_PAREN','function',4,'p_function','generic.py',384), +] diff --git a/astropy/units/format/latex.py b/astropy/units/format/latex.py new file mode 100644 index 000000000000..eb959c1e8be2 --- /dev/null +++ b/astropy/units/format/latex.py @@ -0,0 +1,92 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +""" +Handles the "LaTeX" unit format. +""" + +import re +from typing import ClassVar, Literal + +from astropy.units.core import NamedUnit, UnitBase +from astropy.units.enums import DeprecatedUnitAction +from astropy.units.typing import UnitPower + +from . import console + + +class Latex(console.Console): + """ + Output LaTeX to display the unit based on IAU style guidelines. + + Attempts to follow the `IAU Style Manual + `_. + """ + + _space: ClassVar[str] = r"\," + _scale_unit_separator: ClassVar[str] = r"\," + _times: ClassVar[str] = r" \times " + + @classmethod + def _format_mantissa(cls, m: str) -> str: + return m.replace("nan", r"{\rm NaN}").replace("inf", r"\infty") + + @classmethod + def _format_superscript(cls, number: str) -> str: + return f"^{{{number}}}" + + @classmethod + def _format_unit_power(cls, unit: NamedUnit, power: UnitPower = 1) -> str: + name = unit._get_format_name("latex") + if name == unit.name: + # This doesn't escape arbitrary LaTeX strings, but it should + # be good enough for unit names which are required to be alpha + # + "_" anyway. + name = name.replace("_", r"\_") + if power != 1: + # If the LaTeX representation of the base unit already ends with + # a superscript, we need to spell out the unit to avoid double + # superscripts. For example, the logic below ensures that + # `u.deg**2` returns `deg^{2}` instead of `{}^{\circ}^{2}`. + if re.match(r".*\^{[^}]*}$", name): # ends w/ superscript? + name = unit.short_names[0] + name += cls._format_power(power) + return name + + @classmethod + def _format_multiline_fraction( + cls, scale: str, numerator: str, denominator: str + ) -> str: + return rf"{scale}\frac{{{numerator}}}{{{denominator}}}" + + @classmethod + def to_string( + cls, + unit: UnitBase, + fraction: bool | Literal["inline", "multiline"] = "multiline", + deprecations: DeprecatedUnitAction = DeprecatedUnitAction.WARN, + ) -> str: + s = super().to_string(unit, fraction=fraction) + return rf"$\mathrm{{{s}}}$" + + +class LatexInline(Latex): + """ + Output LaTeX to display the unit based on IAU style guidelines with negative + powers. + + Attempts to follow the `IAU Style Manual + `_ and the + `ApJ and AJ style guide + `_. + """ + + name: ClassVar[str] = "latex_inline" + + @classmethod + def to_string( + cls, + unit: UnitBase, + fraction: bool | Literal["inline", "multiline"] = False, + deprecations: DeprecatedUnitAction = DeprecatedUnitAction.WARN, + ) -> str: + return super().to_string(unit, fraction=fraction) diff --git a/astropy/units/format/ogip.py b/astropy/units/format/ogip.py new file mode 100644 index 000000000000..0e516cfad5d2 --- /dev/null +++ b/astropy/units/format/ogip.py @@ -0,0 +1,338 @@ +# Licensed under a 3-clause BSD style license - see LICNSE.rst + +# This module includes files automatically generated from ply (these end in +# _lextab.py and _parsetab.py). To generate these files, remove them from this +# folder, then build astropy and run the tests in-place: +# +# python setup.py build_ext --inplace +# pytest astropy/units +# +# You can then commit the changes to the re-generated _lextab.py and +# _parsetab.py files. + +""" +Handles units in `Office of Guest Investigator Programs (OGIP) +FITS files +`__. +""" + +import math +import warnings +from fractions import Fraction +from typing import ClassVar, Literal + +import numpy as np + +from astropy.extern.ply.lex import Lexer +from astropy.units.core import CompositeUnit, UnitBase +from astropy.units.enums import DeprecatedUnitAction +from astropy.units.errors import UnitParserWarning, UnitsWarning +from astropy.units.typing import UnitScale +from astropy.utils import classproperty, parsing +from astropy.utils.parsing import ThreadSafeParser + +from .base import Base, _ParsingFormatMixin + + +class OGIP(Base, _ParsingFormatMixin): + """ + Support the units in `Office of Guest Investigator Programs (OGIP) + FITS files + `__. + """ + + _tokens: ClassVar[tuple[str, ...]] = ( + "DIVISION", + "OPEN_PAREN", + "CLOSE_PAREN", + "WHITESPACE", + "POWER", + "STAR", + "FLOAT", + "LIT10", + "INT", + "UNKNOWN", + "FUNCNAME", + "UNIT", + ) + + _deprecated_units: ClassVar[frozenset[str]] = frozenset(("Crab", "mCrab")) + + @classproperty(lazy=True) + def _units(cls) -> dict[str, UnitBase]: + from astropy import units as u + + names = {"as": u.attosecond} + for non_prefixed_unit in [ + "angstrom", "arcmin", "arcsec", "AU", "barn", "bin", + "byte", "chan", "count", "d", "deg", "erg", "G", + "h", "lyr", "mag", "min", "photon", "pixel", + "voxel", "yr", + ]: # fmt: skip + names[non_prefixed_unit] = getattr(u, non_prefixed_unit) + + bases = [ + "A", "C", "cd", "eV", "F", "g", "H", "Hz", "J", + "Jy", "K", "lm", "lx", "m", "mol", "N", "ohm", "Pa", + "pc", "rad", "s", "S", "sr", "T", "V", "W", "Wb", + ] # fmt: skip + prefixes = [ + "y", "z", "a", "f", "p", "n", "u", "m", "c", "d", + "", "da", "h", "k", "M", "G", "T", "P", "E", "Z", "Y", + ] # fmt: skip + + for name in (prefix + base for base in bases for prefix in prefixes): + if name not in names: + names[name] = getattr(u, name) + + # Create a separate, disconnected unit for the special case of + # Crab and mCrab, since OGIP doesn't define their quantities. + names["Crab"] = u.def_unit(["Crab"], prefixes=False, doc="Crab (X-ray flux)") + names["mCrab"] = u.Unit(10**-3 * names["Crab"]) + + return names + + @classproperty(lazy=True) + def _lexer(cls) -> Lexer: + tokens = cls._tokens + + t_DIVISION = "[ \t]*/[ \t]*" + t_OPEN_PAREN = r"\(" + t_CLOSE_PAREN = r"\)" + t_WHITESPACE = "[ \t]+" + t_POWER = r"\*\*" + t_STAR = r"\*" + + # NOTE THE ORDERING OF THESE RULES IS IMPORTANT!! + # Regular expression rules for simple tokens + def t_FLOAT(t): + r"[+-]?((((\d+\.?\d*)|(\.\d+))([eE][+-]?\d+))|(((\d+\.\d*)|(\.\d+))([eE][+-]?\d+)?))" + t.value = float(t.value) + return t + + def t_INT(t): + r"[+-]?\d+" + t.value = int(t.value) + return t + + def t_LIT10(t): + r"10" + return 10 + + def t_UNKNOWN(t): + r"[Uu][Nn][Kk][Nn][Oo][Ww][Nn]" + return None + + def t_FUNCNAME(t): + r"((sqrt)|(ln)|(exp)|(log)|(sin)|(cos)|(tan)|(asin)|(acos)|(atan)|(sinh)|(cosh)|(tanh))(?=\ *\()" + return t + + def t_UNIT(t): + r"[a-zA-Z][a-zA-Z_]*" + t.value = cls._get_unit(t) + return t + + # Don't ignore whitespace + t_ignore = "" + + # Error handling rule + def t_error(t): + raise ValueError(f"Invalid character at col {t.lexpos}") + + return parsing.lex(lextab="ogip_lextab", package="astropy/units") + + @classproperty(lazy=True) + def _parser(cls) -> ThreadSafeParser: + """ + The grammar here is based on the description in the + `Specification of Physical Units within OGIP FITS files + `__, + which is not terribly precise. The exact grammar is here is + based on the YACC grammar in the `unity library + `_. + """ + tokens = cls._tokens + + def p_main(p): + """ + main : UNKNOWN + | complete_expression + | scale_factor complete_expression + | scale_factor WHITESPACE complete_expression + """ + match p[1:]: + case (factor, unit) | (factor, _, unit): + p[0] = CompositeUnit(factor * unit.scale, unit.bases, unit.powers) + case _: + p[0] = p[1] + + def p_complete_expression(p): + """ + complete_expression : unit_expression + | product_of_units + | division_of_units + """ + # product_of_units is not in unit_expression for performance + # division_of_units is separate to enforce the correct order of operations + p[0] = p[1] + + def p_product_of_units(p): + """ + product_of_units : complete_expression product unit_expression + """ + p[0] = p[1] * p[3] + + def p_division_of_units(p): + """ + division_of_units : DIVISION unit_expression + | complete_expression DIVISION unit_expression + """ + match p[1:]: + case _, unit: + p[0] = unit**-1 + case num, _, denom: + p[0] = num / denom + + def p_unit_expression(p): + """ + unit_expression : UNIT + | function + | UNIT POWER numeric_power + | UNIT OPEN_PAREN complete_expression CLOSE_PAREN + | OPEN_PAREN complete_expression CLOSE_PAREN + | UNIT OPEN_PAREN complete_expression CLOSE_PAREN POWER numeric_power + | OPEN_PAREN complete_expression CLOSE_PAREN POWER numeric_power + """ + bad_multiplication_message = ( + "if '{0}{1}' was meant to be a multiplication, " + "it should have been written as '{0} {1}'." + ) + + match p[1:]: + case factor, _, unit, _, _, power: + warnings.warn( + bad_multiplication_message.format(factor, f"({unit})**{power}"), + UnitParserWarning, + ) + p[0] = factor * unit**power + case (_, unit, _, _, power) | (unit, "**", power): + p[0] = unit**power + case left, _, right, _: + warnings.warn( + bad_multiplication_message.format(left, f"({right})"), + UnitParserWarning, + ) + p[0] = left * right + case _, unit, _: + p[0] = unit + case _: + p[0] = p[1] + + def p_function(p): + """ + function : FUNCNAME OPEN_PAREN complete_expression CLOSE_PAREN + | FUNCNAME OPEN_PAREN complete_expression CLOSE_PAREN POWER numeric_power + """ + match p[1:]: + case "sqrt", _, unit, _: + p[0] = unit**0.5 + case "sqrt", _, unit, _, _, numeric_power: + p[0] = unit ** (0.5 * numeric_power) + case func, *_: + raise ValueError( + f"The function '{func}' is valid in OGIP, but not understood " + "by astropy.units." + ) + + def p_scale_factor(p): + """ + scale_factor : LIT10 POWER numeric_power + | LIT10 + | number + | number POWER numeric_power + """ + if len(p) == 4: + p[0] = 10 ** p[3] + else: + p[0] = p[1] + # Can't use np.log10 here, because p[0] may be a Python long. + if math.log10(p[0]) % 1.0 != 0.0: + warnings.warn( + f"'{p[0]}' scale should be a power of 10 in OGIP format", + UnitsWarning, + ) + + def p_product(p): + """ + product : WHITESPACE + | STAR + | WHITESPACE STAR + | WHITESPACE STAR WHITESPACE + | STAR WHITESPACE + """ + + def p_numeric_power(p): + """ + numeric_power : number + | OPEN_PAREN number CLOSE_PAREN + | OPEN_PAREN INT DIVISION INT CLOSE_PAREN + """ + if len(p) == 6: + p[0] = Fraction(int(p[2]), int(p[4])) + elif len(p) == 4: + p[0] = p[2] + else: + p[0] = p[1] + if p[1] < 0: + warnings.warn( + UnitParserWarning( + "negative exponents must be enclosed in parenthesis. " + f"Expected '**({p[1]})' instead of '**{p[1]}'." + ) + ) + + def p_number(p): + """ + number : INT + | FLOAT + """ + p[0] = p[1] + + def p_error(p): + raise ValueError() + + return parsing.yacc(tabmodule="ogip_parsetab", package="astropy/units") + + @classmethod + def parse(cls, s: str, debug: bool = False) -> UnitBase: + return cls._do_parse(s.strip(), debug) + + @classmethod + def _format_superscript(cls, number: str) -> str: + return f"**({number})" if "/" in number else f"**{number}" + + @classmethod + def to_string( + cls, + unit: UnitBase, + fraction: bool | Literal["inline", "multiline"] = "inline", + deprecations: DeprecatedUnitAction = DeprecatedUnitAction.WARN, + ) -> str: + # Remove units that aren't known to the format + unit = cls._decompose_to_known_units(unit, deprecations) + + if isinstance(unit, CompositeUnit): + # Can't use np.log10 here, because p[0] may be a Python long. + if math.log10(unit.scale) % 1.0 != 0.0: + warnings.warn( + f"'{unit.scale}' scale should be a power of 10 in OGIP format", + UnitsWarning, + ) + + return super().to_string(unit, fraction=fraction) + + @classmethod + def format_exponential_notation( + cls, val: UnitScale | np.number, format_spec: str = "g" + ) -> str: + return format(val, format_spec) diff --git a/astropy/units/format/ogip_lextab.py b/astropy/units/format/ogip_lextab.py new file mode 100644 index 000000000000..bcbac34f1728 --- /dev/null +++ b/astropy/units/format/ogip_lextab.py @@ -0,0 +1,20 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +# This file was automatically generated from ply. To re-generate this file, +# remove it from this folder, then build astropy and run the tests in-place: +# +# python setup.py build_ext --inplace +# pytest astropy/units +# +# You can then commit the changes to this file. + +# ogip_lextab.py. This file automatically created by PLY (version 3.11). Don't edit! +_tabversion = '3.10' +_lextokens = set(('CLOSE_PAREN', 'DIVISION', 'FLOAT', 'FUNCNAME', 'INT', 'LIT10', 'OPEN_PAREN', 'POWER', 'STAR', 'UNIT', 'UNKNOWN', 'WHITESPACE')) +_lexreflags = 64 +_lexliterals = '' +_lexstateinfo = {'INITIAL': 'inclusive'} +_lexstatere = {'INITIAL': [('(?P[+-]?((((\\d+\\.?\\d*)|(\\.\\d+))([eE][+-]?\\d+))|(((\\d+\\.\\d*)|(\\.\\d+))([eE][+-]?\\d+)?)))|(?P[+-]?\\d+)|(?P10)|(?P[Uu][Nn][Kk][Nn][Oo][Ww][Nn])|(?P((sqrt)|(ln)|(exp)|(log)|(sin)|(cos)|(tan)|(asin)|(acos)|(atan)|(sinh)|(cosh)|(tanh))(?=\\ *\\())|(?P[a-zA-Z][a-zA-Z_]*)|(?P[ \t]*/[ \t]*)|(?P[ \t]+)|(?P\\*\\*)|(?P\\()|(?P\\))|(?P\\*)', [None, ('t_FLOAT', 'FLOAT'), None, None, None, None, None, None, None, None, None, None, None, ('t_INT', 'INT'), ('t_LIT10', 'LIT10'), ('t_UNKNOWN', 'UNKNOWN'), ('t_FUNCNAME', 'FUNCNAME'), None, None, None, None, None, None, None, None, None, None, None, None, None, None, ('t_UNIT', 'UNIT'), (None, 'DIVISION'), (None, 'WHITESPACE'), (None, 'POWER'), (None, 'OPEN_PAREN'), (None, 'CLOSE_PAREN'), (None, 'STAR')])]} +_lexstateignore = {'INITIAL': ''} +_lexstateerrorf = {'INITIAL': 't_error'} +_lexstateeoff = {} diff --git a/astropy/units/format/ogip_parsetab.py b/astropy/units/format/ogip_parsetab.py new file mode 100644 index 000000000000..57d28371c9a8 --- /dev/null +++ b/astropy/units/format/ogip_parsetab.py @@ -0,0 +1,73 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +# This file was automatically generated from ply. To re-generate this file, +# remove it from this folder, then build astropy and run the tests in-place: +# +# python setup.py build_ext --inplace +# pytest astropy/units +# +# You can then commit the changes to this file. + + +# ogip_parsetab.py +# This file is automatically generated. Do not edit. +# pylint: disable=W,C,R +_tabversion = '3.10' + +_lr_method = 'LALR' + +_lr_signature = 'CLOSE_PAREN DIVISION FLOAT FUNCNAME INT LIT10 OPEN_PAREN POWER STAR UNIT UNKNOWN WHITESPACE\n main : UNKNOWN\n | complete_expression\n | scale_factor complete_expression\n | scale_factor WHITESPACE complete_expression\n \n complete_expression : unit_expression\n | product_of_units\n | division_of_units\n \n product_of_units : complete_expression product unit_expression\n \n division_of_units : DIVISION unit_expression\n | complete_expression DIVISION unit_expression\n \n unit_expression : UNIT\n | function\n | UNIT POWER numeric_power\n | UNIT OPEN_PAREN complete_expression CLOSE_PAREN\n | OPEN_PAREN complete_expression CLOSE_PAREN\n | UNIT OPEN_PAREN complete_expression CLOSE_PAREN POWER numeric_power\n | OPEN_PAREN complete_expression CLOSE_PAREN POWER numeric_power\n \n function : FUNCNAME OPEN_PAREN complete_expression CLOSE_PAREN\n | FUNCNAME OPEN_PAREN complete_expression CLOSE_PAREN POWER numeric_power\n \n scale_factor : LIT10 POWER numeric_power\n | LIT10\n | number\n | number POWER numeric_power\n \n product : WHITESPACE\n | STAR\n | WHITESPACE STAR\n | WHITESPACE STAR WHITESPACE\n | STAR WHITESPACE\n \n numeric_power : number\n | OPEN_PAREN number CLOSE_PAREN\n | OPEN_PAREN INT DIVISION INT CLOSE_PAREN\n \n number : INT\n | FLOAT\n ' + +_lr_action_items = {'UNKNOWN':([0,],[2,]),'LIT10':([0,],[8,]),'UNIT':([0,4,8,9,12,13,14,15,17,18,19,20,22,26,29,32,33,35,36,38,43,49,57,],[10,10,-21,-22,10,10,-32,-33,10,10,-24,-25,10,10,10,-26,-28,-20,-29,-23,-27,-30,-31,]),'OPEN_PAREN':([0,4,8,9,10,12,13,14,15,16,17,18,19,20,22,23,24,25,26,29,32,33,35,36,38,43,47,49,51,53,57,],[12,12,-21,-22,26,12,12,-32,-33,29,12,12,-24,-25,12,37,37,37,12,12,-26,-28,-20,-29,-23,-27,37,-30,37,37,-31,]),'DIVISION':([0,3,4,5,6,7,8,9,10,11,12,14,15,21,22,26,27,28,29,30,31,34,35,36,38,39,40,41,42,45,46,48,49,52,55,56,57,],[13,18,13,-5,-6,-7,-21,-22,-11,-12,13,-32,-33,18,13,13,18,-9,13,-8,-10,18,-20,-29,-23,-13,18,-15,18,50,-14,-18,-30,-17,-16,-19,-31,]),'INT':([0,23,24,25,37,47,50,51,53,],[14,14,14,14,45,14,54,14,14,]),'FLOAT':([0,23,24,25,37,47,51,53,],[15,15,15,15,15,15,15,15,]),'FUNCNAME':([0,4,8,9,12,13,14,15,17,18,19,20,22,26,29,32,33,35,36,38,43,49,57,],[16,16,-21,-22,16,16,-32,-33,16,16,-24,-25,16,16,16,-26,-28,-20,-29,-23,-27,-30,-31,]),'$end':([1,2,3,5,6,7,10,11,14,15,21,28,30,31,34,36,39,41,46,48,49,52,55,56,57,],[0,-1,-2,-5,-6,-7,-11,-12,-32,-33,-3,-9,-8,-10,-4,-29,-13,-15,-14,-18,-30,-17,-16,-19,-31,]),'WHITESPACE':([3,4,5,6,7,8,9,10,11,14,15,20,21,27,28,30,31,32,34,35,36,38,39,40,41,42,46,48,49,52,55,56,57,],[19,22,-5,-6,-7,-21,-22,-11,-12,-32,-33,33,19,19,-9,-8,-10,43,19,-20,-29,-23,-13,19,-15,19,-14,-18,-30,-17,-16,-19,-31,]),'STAR':([3,5,6,7,10,11,14,15,19,21,27,28,30,31,34,36,39,40,41,42,46,48,49,52,55,56,57,],[20,-5,-6,-7,-11,-12,-32,-33,32,20,20,-9,-8,-10,20,-29,-13,20,-15,20,-14,-18,-30,-17,-16,-19,-31,]),'CLOSE_PAREN':([5,6,7,10,11,14,15,27,28,30,31,36,39,40,41,42,44,45,46,48,49,52,54,55,56,57,],[-5,-6,-7,-11,-12,-32,-33,41,-9,-8,-10,-29,-13,46,-15,48,49,-32,-14,-18,-30,-17,57,-16,-19,-31,]),'POWER':([8,9,10,14,15,41,46,48,],[23,24,25,-32,-33,47,51,53,]),} + +_lr_action = {} +for _k, _v in _lr_action_items.items(): + for _x,_y in zip(_v[0],_v[1]): + if not _x in _lr_action: _lr_action[_x] = {} + _lr_action[_x][_k] = _y +del _lr_action_items + +_lr_goto_items = {'main':([0,],[1,]),'complete_expression':([0,4,12,22,26,29,],[3,21,27,34,40,42,]),'scale_factor':([0,],[4,]),'unit_expression':([0,4,12,13,17,18,22,26,29,],[5,5,5,28,30,31,5,5,5,]),'product_of_units':([0,4,12,22,26,29,],[6,6,6,6,6,6,]),'division_of_units':([0,4,12,22,26,29,],[7,7,7,7,7,7,]),'number':([0,23,24,25,37,47,51,53,],[9,36,36,36,44,36,36,36,]),'function':([0,4,12,13,17,18,22,26,29,],[11,11,11,11,11,11,11,11,11,]),'product':([3,21,27,34,40,42,],[17,17,17,17,17,17,]),'numeric_power':([23,24,25,47,51,53,],[35,38,39,52,55,56,]),} + +_lr_goto = {} +for _k, _v in _lr_goto_items.items(): + for _x, _y in zip(_v[0], _v[1]): + if not _x in _lr_goto: _lr_goto[_x] = {} + _lr_goto[_x][_k] = _y +del _lr_goto_items +_lr_productions = [ + ("S' -> main","S'",1,None,None,None), + ('main -> UNKNOWN','main',1,'p_main','ogip.py',158), + ('main -> complete_expression','main',1,'p_main','ogip.py',159), + ('main -> scale_factor complete_expression','main',2,'p_main','ogip.py',160), + ('main -> scale_factor WHITESPACE complete_expression','main',3,'p_main','ogip.py',161), + ('complete_expression -> unit_expression','complete_expression',1,'p_complete_expression','ogip.py',171), + ('complete_expression -> product_of_units','complete_expression',1,'p_complete_expression','ogip.py',172), + ('complete_expression -> division_of_units','complete_expression',1,'p_complete_expression','ogip.py',173), + ('product_of_units -> complete_expression product unit_expression','product_of_units',3,'p_product_of_units','ogip.py',181), + ('division_of_units -> DIVISION unit_expression','division_of_units',2,'p_division_of_units','ogip.py',187), + ('division_of_units -> complete_expression DIVISION unit_expression','division_of_units',3,'p_division_of_units','ogip.py',188), + ('unit_expression -> UNIT','unit_expression',1,'p_unit_expression','ogip.py',198), + ('unit_expression -> function','unit_expression',1,'p_unit_expression','ogip.py',199), + ('unit_expression -> UNIT POWER numeric_power','unit_expression',3,'p_unit_expression','ogip.py',200), + ('unit_expression -> UNIT OPEN_PAREN complete_expression CLOSE_PAREN','unit_expression',4,'p_unit_expression','ogip.py',201), + ('unit_expression -> OPEN_PAREN complete_expression CLOSE_PAREN','unit_expression',3,'p_unit_expression','ogip.py',202), + ('unit_expression -> UNIT OPEN_PAREN complete_expression CLOSE_PAREN POWER numeric_power','unit_expression',6,'p_unit_expression','ogip.py',203), + ('unit_expression -> OPEN_PAREN complete_expression CLOSE_PAREN POWER numeric_power','unit_expression',5,'p_unit_expression','ogip.py',204), + ('function -> FUNCNAME OPEN_PAREN complete_expression CLOSE_PAREN','function',4,'p_function','ogip.py',233), + ('function -> FUNCNAME OPEN_PAREN complete_expression CLOSE_PAREN POWER numeric_power','function',6,'p_function','ogip.py',234), + ('scale_factor -> LIT10 POWER numeric_power','scale_factor',3,'p_scale_factor','ogip.py',249), + ('scale_factor -> LIT10','scale_factor',1,'p_scale_factor','ogip.py',250), + ('scale_factor -> number','scale_factor',1,'p_scale_factor','ogip.py',251), + ('scale_factor -> number POWER numeric_power','scale_factor',3,'p_scale_factor','ogip.py',252), + ('product -> WHITESPACE','product',1,'p_product','ogip.py',267), + ('product -> STAR','product',1,'p_product','ogip.py',268), + ('product -> WHITESPACE STAR','product',2,'p_product','ogip.py',269), + ('product -> WHITESPACE STAR WHITESPACE','product',3,'p_product','ogip.py',270), + ('product -> STAR WHITESPACE','product',2,'p_product','ogip.py',271), + ('numeric_power -> number','numeric_power',1,'p_numeric_power','ogip.py',276), + ('numeric_power -> OPEN_PAREN number CLOSE_PAREN','numeric_power',3,'p_numeric_power','ogip.py',277), + ('numeric_power -> OPEN_PAREN INT DIVISION INT CLOSE_PAREN','numeric_power',5,'p_numeric_power','ogip.py',278), + ('number -> INT','number',1,'p_number','ogip.py',296), + ('number -> FLOAT','number',1,'p_number','ogip.py',297), +] diff --git a/astropy/units/format/unicode_format.py b/astropy/units/format/unicode_format.py new file mode 100644 index 000000000000..78eaf5d6a372 --- /dev/null +++ b/astropy/units/format/unicode_format.py @@ -0,0 +1,71 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +""" +Handles the "Unicode" unit format. +""" + +from typing import ClassVar + +from astropy.units.core import NamedUnit +from astropy.units.typing import UnitPower + +from . import console + + +class Unicode(console.Console): + """ + Output-only format to display pretty formatting at the console + using Unicode characters. + + For example:: + + >>> import astropy.units as u + >>> print(u.bar.decompose().to_string('unicode')) + 100000 kg mâģš sâģ² + >>> print(u.bar.decompose().to_string('unicode', fraction='multiline')) + kg + 100000 ──── + m s² + >>> print(u.bar.decompose().to_string('unicode', fraction='inline')) + 100000 kg / (m s²) + """ + + _times: ClassVar[str] = "×" + _line: ClassVar[str] = "─" + + @classmethod + def _format_mantissa(cls, m: str) -> str: + return m.replace("-", "−") + + @classmethod + def _format_unit_power(cls, unit: NamedUnit, power: UnitPower = 1) -> str: + name = unit._get_format_name(cls.name) + # Check for superscript units + if power != 1: + if name in ("°", "eâģ", "â€ŗ", "′", "ʰ"): + name = unit.short_names[0] + name += cls._format_power(power) + return name + + @classmethod + def _format_superscript(cls, number: str) -> str: + mapping = str.maketrans( + { + "0": "⁰", + "1": "š", + "2": "²", + "3": "Âŗ", + "4": "⁴", + "5": "âĩ", + "6": "âļ", + "7": "⁡", + "8": "⁸", + "9": "⁚", + "-": "âģ", + "−": "âģ", + # This is actually a "raised omission bracket", but it's + # the closest thing I could find to a superscript solidus. + "/": "⸍", + } + ) + return number.translate(mapping) diff --git a/astropy/units/format/vounit.py b/astropy/units/format/vounit.py new file mode 100644 index 000000000000..139b4d464145 --- /dev/null +++ b/astropy/units/format/vounit.py @@ -0,0 +1,222 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +""" +Handles the "VOUnit" unit format. +""" + +import re +import warnings +from re import Pattern +from typing import ClassVar, Literal + +import numpy as np + +from astropy.extern.ply.lex import LexToken +from astropy.units.core import ( + CompositeUnit, + NamedUnit, + PrefixUnit, + UnitBase, + def_unit, + dimensionless_unscaled, + si_prefixes, +) +from astropy.units.enums import DeprecatedUnitAction +from astropy.units.errors import UnitParserWarning, UnitScaleError, UnitsError +from astropy.units.typing import UnitScale +from astropy.utils import classproperty + +from . import Base +from .generic import _GenericParserMixin + + +class VOUnit(Base, _GenericParserMixin): + """ + The IVOA standard for units used by the VO. + + This is an implementation of `Units in the VO 1.0 + `_. + """ + + _explicit_custom_unit_regex: ClassVar[Pattern[str]] = re.compile( + r"^[YZEPTGMkhdcmunpfazy]?'((?!\d)\w)+'$" + ) + _custom_unit_regex: ClassVar[Pattern[str]] = re.compile(r"^((?!\d)\w)+$") + _custom_units: ClassVar[dict[str, UnitBase]] = {} + _space: ClassVar[str] = "." + _scale_unit_separator: ClassVar[str] = "" + + @classproperty(lazy=True) + def _all_units(cls) -> tuple[dict[str, UnitBase], frozenset[str]]: + from astropy import units as u + from astropy.units import required_by_vounit as uvo + + names = {"as": u.attosecond} + deprecated_names = set() + # The tropical year is missing here compared to the standard + bases = [ + "A", "a", "adu", "arcmin", "arcsec", "barn", "beam", "bin", + "C", "cd", "chan", "count", "ct", "d", "D", "deg", "erg", "eV", + "F", "g", "G", "H", "h", "Hz", "J", "Jy", "K", "lm", "lx", "lyr", + "m", "mag", "min", "mol", "N", "Ohm", "Pa", "pc", "ph", "photon", + "pix", "pixel", "R", "rad", "Ry", "s", "S", "solLum", "solMass", + "solRad", "sr", "T", "u", "V", "voxel", "W", "Wb", "yr", + ] # fmt: skip + binary_bases = ["bit", "byte", "B"] + simple_units = ["Angstrom", "angstrom", "AU", "au", "Ba", "dB", "mas", "Sun"] + si_prefixes = [ + "y", "z", "a", "f", "p", "n", "u", "m", "c", "d", + "", "da", "h", "k", "M", "G", "T", "P", "E", "Z", "Y" + ] # fmt: skip + binary_prefixes = ["Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi", "Yi"] + deprecated_units = {"angstrom", "Angstrom", "Ba", "barn", "erg", "G", "ta"} + + def do_defines(bases, prefixes, skips=[]): + for base in bases: + for prefix in prefixes: + if (key := prefix + base) not in skips: + names[key] = getattr(u if hasattr(u, key) else uvo, key) + if base in deprecated_units: + deprecated_names.add(key) + + do_defines(bases, si_prefixes, ["pct", "pcount", "yd", "as"]) + do_defines(binary_bases, si_prefixes + binary_prefixes, ["dB", "dbyte"]) + do_defines(simple_units, [""]) + + return names, frozenset(deprecated_names) + + @classproperty(lazy=True) + def _units(cls) -> dict[str, UnitBase]: + return cls._all_units[0] + + @classproperty(lazy=True) + def _deprecated_units(cls) -> frozenset[str]: + return cls._all_units[1] + + @classmethod + def parse(cls, s: str, debug: bool = False) -> UnitBase: + if s in ("unknown", "UNKNOWN"): + return None + if s == "": + return dimensionless_unscaled + # Check for excess solidi, but exclude fractional exponents (allowed) + if s.count("/") > 1 and s.count("/") - len(re.findall(r"\(\d+/\d+\)", s)) > 1: + raise UnitsError( + f"'{s}' contains multiple slashes, which is " + "disallowed by the VOUnit standard." + ) + result = cls._do_parse(s, debug) + if hasattr(result, "function_unit"): + raise ValueError("Function units are not yet supported in VOUnit.") + return result + + @classmethod + def _get_unit(cls, t: LexToken) -> UnitBase: + try: + return super()._get_unit(t) + except ValueError: + if cls._explicit_custom_unit_regex.match(t.value): + return cls._def_custom_unit(t.value) + + if cls._custom_unit_regex.match(t.value): + warnings.warn( + cls._invalid_unit_error_message(t.value), UnitParserWarning + ) + return cls._def_custom_unit(t.value) + + raise + + @classmethod + def _decompose_to_known_units( + cls, + unit: CompositeUnit | NamedUnit, + deprecations: DeprecatedUnitAction = DeprecatedUnitAction.WARN, + ) -> UnitBase: + # The da- and d- prefixes are discouraged. This has the + # effect of adding a scale to value in the result. + if isinstance(unit, PrefixUnit) and unit._represents.scale in (0.1, 10.0): + return cls._decompose_to_known_units(unit._represents) + if ( + isinstance(unit, NamedUnit) + and unit._get_format_name(cls.name) in cls._custom_units + ): + return unit + return super()._decompose_to_known_units(unit, deprecations) + + @classmethod + def _def_custom_unit(cls, unit: str) -> UnitBase: + def def_base(name): + if name in cls._custom_units: + return cls._custom_units[name] + + if name.startswith("'"): + return def_unit( + [name[1:-1], name], + format={"vounit": name}, + namespace=cls._custom_units, + ) + else: + return def_unit(name, namespace=cls._custom_units) + + if unit in cls._custom_units: + return cls._custom_units[unit] + + for short, _, factor in si_prefixes: + for prefix in short: + if unit.startswith(prefix): + base_name = unit[len(prefix) :] + base_unit = def_base(base_name) + return PrefixUnit( + [prefix + x for x in base_unit.names], + CompositeUnit(factor, [base_unit], [1], _error_check=False), + format={"vounit": prefix + base_unit.names[-1]}, + namespace=cls._custom_units, + ) + + return def_base(unit) + + @classmethod + def _format_superscript(cls, number: str) -> str: + return f"**({number})" if "/" in number or "." in number else f"**{number}" + + @classmethod + def format_exponential_notation( + cls, val: UnitScale | np.number, format_spec: str = ".8g" + ) -> str: + return format(val, format_spec) + + @classmethod + def _format_inline_fraction( + cls, scale: str, numerator: str, denominator: str + ) -> str: + if cls._space in denominator: + denominator = f"({denominator})" + if scale and numerator == "1": + return f"{scale}/{denominator}" + return f"{scale}{numerator}/{denominator}" + + @classmethod + def to_string( + cls, + unit: UnitBase, + fraction: bool | Literal["inline", "multiline"] = False, + deprecations: DeprecatedUnitAction = DeprecatedUnitAction.WARN, + ) -> str: + # Remove units that aren't known to the format + unit = cls._decompose_to_known_units(unit, deprecations) + + if unit.physical_type == "dimensionless" and unit.scale != 1: + raise UnitScaleError( + "The VOUnit format is not able to " + "represent scale for dimensionless units. " + f"Multiply your data by {unit.scale:e}." + ) + + return super().to_string(unit, fraction=fraction) + + @classmethod + def _fix_deprecated(cls, x: str) -> list[str]: + return ( + [f"{x} (deprecated)", cls.to_string(cls._units[x]._represents)] + if x in cls._deprecated_units + else [x] + ) diff --git a/astropy/units/function/__init__.py b/astropy/units/function/__init__.py new file mode 100644 index 000000000000..8eed292cd7f4 --- /dev/null +++ b/astropy/units/function/__init__.py @@ -0,0 +1,17 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +""" +This subpackage contains classes and functions for defining and converting +between different function units and quantities, i.e., using units which +are some function of a physical unit, such as magnitudes and decibels. +""" + +from . import core, logarithmic, units +from .core import * +from .logarithmic import * +from .units import * + +__all__: list[str] = [] +__all__ += core.__all__ +__all__ += logarithmic.__all__ +__all__ += units.__all__ diff --git a/astropy/units/function/core.py b/astropy/units/function/core.py new file mode 100644 index 000000000000..3c99bc1fce6d --- /dev/null +++ b/astropy/units/function/core.py @@ -0,0 +1,797 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +"""Function Units and Quantities.""" + +from abc import ABCMeta, abstractmethod +from collections.abc import Collection +from functools import cached_property +from typing import Self + +import numpy as np +from numpy._core import umath as np_umath + +from astropy.units import ( + Quantity, + Unit, + UnitBase, + UnitConversionError, + UnitsError, + UnitTypeError, + dimensionless_unscaled, +) +from astropy.units.typing import PhysicalTypeID + +__all__ = ["FunctionQuantity", "FunctionUnitBase"] + +SUPPORTED_UFUNCS = { + getattr(np_umath, ufunc) + for ufunc in ( + "isfinite", + "isinf", + "isnan", + "sign", + "signbit", + "rint", + "floor", + "ceil", + "trunc", + "_ones_like", + "ones_like", + "positive", + ) + if hasattr(np_umath, ufunc) +} + +# TODO: the following could work if helper changed relative to Quantity: +# - spacing should return dimensionless, not same unit +# - negative should negate unit too, +# - add, subtract, comparisons can work if units added/subtracted + +SUPPORTED_FUNCTIONS = { + getattr(np, function) + for function in ("clip", "trace", "mean", "min", "max", "round") +} + + +# subclassing UnitBase or CompositeUnit was found to be problematic, requiring +# a large number of overrides. Hence, define new class. +class FunctionUnitBase(metaclass=ABCMeta): + """Abstract base class for function units. + + Function units are functions containing a physical unit, such as dB(mW). + Most of the arithmetic operations on function units are defined in this + base class. + + While instantiation is defined, this class should not be used directly. + Rather, subclasses should be used that override the abstract properties + `_default_function_unit` and `_quantity_class`, and the abstract methods + `from_physical`, and `to_physical`. + + Parameters + ---------- + physical_unit : `~astropy.units.Unit` or `string` + Unit that is encapsulated within the function unit. + If not given, dimensionless. + + function_unit : `~astropy.units.Unit` or `string` + By default, the same as the function unit set by the subclass. + """ + + # ↓↓↓ the following four need to be set by subclasses + # Make this a property so we can ensure subclasses define it. + @property + @abstractmethod + def _default_function_unit(self): + """Default function unit corresponding to the function. + + This property should be overridden by subclasses, with, e.g., + `~astropy.unit.MagUnit` returning `~astropy.unit.mag`. + """ + + # This has to be a property because the function quantity will not be + # known at unit definition time, as it gets defined after. + @property + @abstractmethod + def _quantity_class(self): + """Function quantity class corresponding to this function unit. + + This property should be overridden by subclasses, with, e.g., + `~astropy.unit.MagUnit` returning `~astropy.unit.Magnitude`. + """ + + @abstractmethod + def from_physical(self, x): + """Transformation from value in physical to value in function units. + + This method should be overridden by subclasses. It is used to + provide automatic transformations using an equivalency. + """ + + @abstractmethod + def to_physical(self, x): + """Transformation from value in function to value in physical units. + + This method should be overridden by subclasses. It is used to + provide automatic transformations using an equivalency. + """ + + # ↑↑↑ the above four need to be set by subclasses + + # have priority over arrays, regular units, and regular quantities + __array_priority__ = 30000 + + def __init__(self, physical_unit=None, function_unit=None): + if physical_unit is None: + physical_unit = dimensionless_unscaled + else: + physical_unit = Unit(physical_unit) + if not isinstance(physical_unit, UnitBase) or physical_unit.is_equivalent( + self._default_function_unit + ): + raise UnitConversionError(f"{physical_unit} is not a physical unit.") + + if function_unit is None: + function_unit = self._default_function_unit + else: + # any function unit should be equivalent to subclass default + function_unit = Unit(getattr(function_unit, "function_unit", function_unit)) + if not function_unit.is_equivalent(self._default_function_unit): + raise UnitConversionError( + f"Cannot initialize '{self.__class__.__name__}' instance with " + f"function unit '{function_unit}', as it is not equivalent to " + f"default function unit '{self._default_function_unit}'." + ) + self._physical_unit = physical_unit + self._function_unit = function_unit + + def _copy(self, physical_unit=None): + """Copy oneself, possibly with a different physical unit.""" + if physical_unit is None: + physical_unit = self.physical_unit + return self.__class__(physical_unit, self.function_unit) + + @property + def physical_unit(self): + return self._physical_unit + + @property + def function_unit(self): + return self._function_unit + + @property + def equivalencies(self): + """List of equivalencies between function and physical units. + + Uses the `from_physical` and `to_physical` methods. + """ + return [(self, self.physical_unit, self.to_physical, self.from_physical)] + + # ↓↓↓ properties/methods required to behave like a unit + def decompose(self, bases: Collection[UnitBase] = ()) -> Self: + """Copy the current unit with the physical unit decomposed. + + For details, see `~astropy.units.UnitBase.decompose`. + """ + return self._copy(self.physical_unit.decompose(bases)) + + @property + def si(self): + """Copy the current function unit with the physical unit in SI.""" + return self._copy(self.physical_unit.si) + + @property + def cgs(self): + """Copy the current function unit with the physical unit in CGS.""" + return self._copy(self.physical_unit.cgs) + + @cached_property + def _physical_type_id(self) -> PhysicalTypeID: + """Get physical type corresponding to physical unit.""" + return self.physical_unit._physical_type_id + + @property + def physical_type(self): + """Return the physical type of the physical unit (e.g., 'length').""" + return self.physical_unit.physical_type + + def is_equivalent(self, other, equivalencies=[]): + """ + Returns `True` if this unit is equivalent to ``other``. + + Parameters + ---------- + other : `~astropy.units.Unit`, string, or tuple + The unit to convert to. If a tuple of units is specified, this + method returns true if the unit matches any of those in the tuple. + + equivalencies : list of tuple + A list of equivalence pairs to try if the units are not + directly convertible. See :ref:`astropy:unit_equivalencies`. + This list is in addition to the built-in equivalencies between the + function unit and the physical one, as well as possible global + defaults set by, e.g., `~astropy.units.set_enabled_equivalencies`. + Use `None` to turn off any global equivalencies. + + Returns + ------- + bool + """ + if isinstance(other, tuple): + return any(self.is_equivalent(u, equivalencies) for u in other) + + other_physical_unit = getattr( + other, + "physical_unit", + ( + dimensionless_unscaled + if self.function_unit.is_equivalent(other) + else other + ), + ) + + return self.physical_unit.is_equivalent(other_physical_unit, equivalencies) + + def to(self, other, value=1.0, equivalencies=[]): + """ + Return the converted values in the specified unit. + + Parameters + ---------- + other : `~astropy.units.Unit`, `~astropy.units.FunctionUnitBase`, or str + The unit to convert to. + + value : int, float, or scalar array-like, optional + Value(s) in the current unit to be converted to the specified unit. + If not provided, defaults to 1.0. + + equivalencies : list of tuple + A list of equivalence pairs to try if the units are not + directly convertible. See :ref:`astropy:unit_equivalencies`. + This list is in meant to treat only equivalencies between different + physical units; the built-in equivalency between the function + unit and the physical one is automatically taken into account. + + Returns + ------- + values : scalar or array + Converted value(s). Input value sequences are returned as + numpy arrays. + + Raises + ------ + `~astropy.units.UnitsError` + If units are inconsistent. + """ + # conversion to one's own physical unit should be fastest + if other is self.physical_unit: + return self.to_physical(value) + + other_function_unit = getattr(other, "function_unit", other) + if self.function_unit.is_equivalent(other_function_unit): + # when other is an equivalent function unit: + # first convert physical units to other's physical units + other_physical_unit = getattr( + other, "physical_unit", dimensionless_unscaled + ) + if self.physical_unit != other_physical_unit: + value_other_physical = self.physical_unit.to( + other_physical_unit, self.to_physical(value), equivalencies + ) + # make function unit again, in own system + value = self.from_physical(value_other_physical) + + # convert possible difference in function unit (e.g., dex->dB) + return self.function_unit.to(other_function_unit, value) + + else: + try: + # when other is not a function unit + return self.physical_unit.to( + other, self.to_physical(value), equivalencies + ) + except UnitConversionError as e: + if self.function_unit == Unit("mag"): + # One can get to raw magnitudes via math that strips the dimensions off. + # Include extra information in the exception to remind users of this. + msg = "Did you perhaps subtract magnitudes so the unit got lost?" + e.args += (msg,) + raise e + else: + raise + + def is_unity(self): + return False + + def __eq__(self, other): + return self.physical_unit == getattr( + other, "physical_unit", dimensionless_unscaled + ) and self.function_unit == getattr(other, "function_unit", other) + + def __ne__(self, other): + return not self.__eq__(other) + + def __rlshift__(self, other): + """Unit conversion operator ``<<``.""" + try: + return self._quantity_class(other, self, copy=None, subok=True) + except Exception: + return NotImplemented + + def __mul__(self, other): + if isinstance(other, (str, UnitBase, FunctionUnitBase)): + if self.physical_unit == dimensionless_unscaled: + # If dimensionless, drop back to normal unit and retry. + return self.function_unit * other + else: + raise UnitsError( + "Cannot multiply a function unit with a physical dimension " + "with any unit." + ) + else: + # Anything not like a unit, try initialising as a function quantity. + try: + return self._quantity_class(other, unit=self) + except Exception: + return NotImplemented + + def __rmul__(self, other): + return self.__mul__(other) + + def __truediv__(self, other): + if isinstance(other, (str, UnitBase, FunctionUnitBase)): + if self.physical_unit == dimensionless_unscaled: + # If dimensionless, drop back to normal unit and retry. + return self.function_unit / other + else: + raise UnitsError( + "Cannot divide a function unit with a physical dimension " + "by any unit." + ) + else: + # Anything not like a unit, try initialising as a function quantity. + try: + return self._quantity_class(1.0 / other, unit=self) + except Exception: + return NotImplemented + + def __rtruediv__(self, other): + if isinstance(other, (str, UnitBase, FunctionUnitBase)): + if self.physical_unit == dimensionless_unscaled: + # If dimensionless, drop back to normal unit and retry. + return other / self.function_unit + else: + raise UnitsError( + "Cannot divide a function unit with a physical dimension " + "into any unit" + ) + else: + # Don't know what to do with anything not like a unit. + return NotImplemented + + def __pow__(self, power): + if power == 0: + return dimensionless_unscaled + elif power == 1: + return self._copy() + + if self.physical_unit == dimensionless_unscaled: + return self.function_unit**power + + raise UnitsError( + "Cannot raise a function unit with a physical dimension " + "to any power but 0 or 1." + ) + + def __pos__(self): + return self._copy() + + def to_string(self, format="generic", **kwargs): + """ + Output the unit in the given format as a string. + + The physical unit is appended, within parentheses, to the function + unit, as in "dB(mW)", with both units set using the given format + + Parameters + ---------- + format : `astropy.units.format.Base` subclass or str + The name of a format or a formatter class. If not + provided, defaults to the generic format. + """ + supported_formats = ( + "generic", + "latex", + "latex_inline", + "unicode", + "console", + ) + if format not in supported_formats: + raise ValueError( + f"Function units cannot be written in {format} " + f"format. Only {', '.join(supported_formats)} are supported." + ) + self_str = self.function_unit.to_string(format, **kwargs) + pu_str = self.physical_unit.to_string(format, **kwargs) + if pu_str == "": + pu_str = "1" + if format.startswith("latex"): + # Add the physical unit with parentheses, removing its latex + # initialization stuff ("$\mathrm{" and "}$"). + # For self_str, remove trailing "}$" and put it back at the end. + self_str = rf"{self_str[:-2]}\left({pu_str[9:-2]}\right)}}$" + else: + pu_lines = pu_str.splitlines() + if len(pu_lines) == 1: + self_str += f"({pu_str})" + else: + # If the physical unit is formatted into a multiline + # string, the lines need to be adjusted so that the + # functional string is aligned with the fraction line + # (second one), and all other lines are indented + # accordingly. + f = f"{{0:^{len(self_str) + 1}s}}{{1:s}}" + lines = [ + f.format("", pu_lines[0]), + f.format(f"{self_str}(", f"{pu_lines[1]})"), + ] + [f.format("", line) for line in pu_lines[2:]] + self_str = "\n".join(lines) + return self_str + + def __format__(self, format_spec): + """Try to format units using a formatter.""" + try: + return self.to_string(format=format_spec) + except ValueError: + return format(str(self), format_spec) + + def __str__(self): + """Return string representation for unit.""" + self_str = str(self.function_unit) + pu_str = str(self.physical_unit) + if pu_str: + self_str += f"({pu_str})" + return self_str + + def __repr__(self): + # By default, try to give a representation using `Unit()`, + # with string such that parsing it would give the correct FunctionUnit. + if callable(self.function_unit): + return f'Unit("{self.to_string()}")' + + else: + return '{}("{}"{})'.format( + self.__class__.__name__, + self.physical_unit, + "" + if self.function_unit is self._default_function_unit + else f', unit="{self.function_unit}"', + ) + + def _repr_latex_(self): + """ + Generate latex representation of unit name. This is used by + the IPython notebook to print a unit with a nice layout. + + Returns + ------- + Latex string + """ + return self.to_string("latex") + + def __hash__(self): + return hash((self.function_unit, self.physical_unit)) + + +class FunctionQuantity(Quantity): + """A representation of a (scaled) function of a number with a unit. + + Function quantities are quantities whose units are functions containing a + physical unit, such as dB(mW). Most of the arithmetic operations on + function quantities are defined in this base class. + + While instantiation is also defined here, this class should not be + instantiated directly. Rather, subclasses should be made which have + ``_unit_class`` pointing back to the corresponding function unit class. + + Parameters + ---------- + value : number, quantity-like, or sequence thereof + The numerical value of the function quantity. If a number or + a `~astropy.units.Quantity` with a function unit, it will be converted + to ``unit`` and the physical unit will be inferred from ``unit``. + If a `~astropy.units.Quantity` with just a physical unit, it will + converted to the function unit, after, if necessary, converting it to + the physical unit inferred from ``unit``. + + unit : str, `~astropy.units.UnitBase`, or `~astropy.units.FunctionUnitBase`, optional + For an `~astropy.units.FunctionUnitBase` instance, the + physical unit will be taken from it; for other input, it will be + inferred from ``value``. By default, ``unit`` is set by the subclass. + + dtype : `~numpy.dtype`, optional + The dtype of the resulting Numpy array or scalar that will + hold the value. If not provided, it is determined from the input, + except that any input that cannot represent float (integer and bool) + is converted to float. + + copy : bool, optional + If `True` (default), then the value is copied. Otherwise, a copy will + only be made if ``__array__`` returns a copy, if value is a nested + sequence, or if a copy is needed to satisfy an explicitly given + ``dtype``. (The `False` option is intended mostly for internal use, + to speed up initialization where a copy is known to have been made. + Use with care.) + + order : {'C', 'F', 'A'}, optional + Specify the order of the array. As in `~numpy.array`. Ignored + if the input does not need to be converted and ``copy=False``. + + subok : bool, optional + If `False` (default), the returned array will be forced to be of the + class used. Otherwise, subclasses will be passed through. + + ndmin : int, optional + Specifies the minimum number of dimensions that the resulting array + should have. Ones will be prepended to the shape as needed to meet + this requirement. This parameter is ignored if the input is a + `~astropy.units.Quantity` and ``copy=False``. + + Raises + ------ + TypeError + If the value provided is not a Python numeric type. + TypeError + If the unit provided is not a `~astropy.units.FunctionUnitBase` + or `~astropy.units.Unit` object, or a parseable string unit. + """ + + _unit_class = None + """Default `~astropy.units.FunctionUnitBase` subclass. + + This should be overridden by subclasses. + """ + + # Ensure priority over ndarray, regular Unit & Quantity, and FunctionUnit. + __array_priority__ = 40000 + + # Define functions that work on FunctionQuantity. + _supported_ufuncs = SUPPORTED_UFUNCS + _supported_functions = SUPPORTED_FUNCTIONS + + def __new__( + cls, + value, + unit=None, + dtype=np.inexact, + copy=True, + order=None, + subok=False, + ndmin=0, + ): + if unit is not None: + # Convert possible string input to a (function) unit. + unit = Unit(unit) + + if not isinstance(unit, FunctionUnitBase): + # By default, use value's physical unit. + value_unit = getattr(value, "unit", None) + if value_unit is None: + # if iterable, see if first item has a unit + # (mixed lists fail in super call below). + try: + value_unit = value[0].unit + except Exception: + pass + physical_unit = getattr(value_unit, "physical_unit", value_unit) + unit = cls._unit_class(physical_unit, function_unit=unit) + + # initialise! + return super().__new__( + cls, + value, + unit, + dtype=dtype, + copy=copy, + order=order, + subok=subok, + ndmin=ndmin, + ) + + # ↓↓↓ properties not found in Quantity + @property + def physical(self): + """The physical quantity corresponding the function one.""" + return self.to(self.unit.physical_unit) + + @property + def _function_view(self): + """View as Quantity with function unit, dropping the physical unit. + + Use `~astropy.units.quantity.Quantity.value` for just the value. + """ + return self._new_view(unit=self.unit.function_unit) + + # ↓↓↓ methods overridden to change the behavior + @property + def si(self): + """Return a copy with the physical unit in SI units.""" + return self.__class__(self.physical.si) + + @property + def cgs(self): + """Return a copy with the physical unit in CGS units.""" + return self.__class__(self.physical.cgs) + + def decompose(self, bases: Collection[UnitBase] = ()) -> Self: + """Generate a new instance with the physical unit decomposed. + + For details, see `~astropy.units.Quantity.decompose`. + """ + return self.__class__(self.physical.decompose(bases)) + + # ↓↓↓ methods overridden to add additional behavior + def __quantity_subclass__(self, unit): + if isinstance(unit, FunctionUnitBase): + return self.__class__, True + else: + return super().__quantity_subclass__(unit)[0], False + + def _set_unit(self, unit): + if not isinstance(unit, self._unit_class): + # Have to take care of, e.g., (10*u.mag).view(u.Magnitude) + try: + # "or 'nonsense'" ensures `None` breaks, just in case. + unit = self._unit_class(function_unit=unit or "nonsense") + except Exception: + raise UnitTypeError( + f"{type(self).__name__} instances require" + f" {self._unit_class.__name__} function units, so cannot set it to" + f" '{unit}'." + ) + + self._unit = unit + + def __array_ufunc__(self, function, method, *inputs, **kwargs): + # TODO: it would be more logical to have this in Quantity already, + # instead of in UFUNC_HELPERS, where it cannot be overridden. + # And really it should just return NotImplemented, since possibly + # another argument might know what to do. + if function not in self._supported_ufuncs: + raise UnitTypeError( + f"Cannot use ufunc '{function.__name__}' with function quantities" + ) + + return super().__array_ufunc__(function, method, *inputs, **kwargs) + + def _maybe_new_view(self, result): + """View as function quantity if the unit is unchanged. + + Used for the case that self.unit.physical_unit is dimensionless, + where multiplication and division is done using the Quantity + equivalent, to transform them back to a FunctionQuantity if possible. + """ + if isinstance(result, Quantity) and result.unit == self.unit: + return self._new_view(result) + else: + return result + + # ↓↓↓ methods overridden to change behavior + def __mul__(self, other): + if self.unit.physical_unit == dimensionless_unscaled: + return self._maybe_new_view(self._function_view * other) + + raise UnitTypeError( + "Cannot multiply function quantities which are not dimensionless " + "with anything." + ) + + def __truediv__(self, other): + if self.unit.physical_unit == dimensionless_unscaled: + return self._maybe_new_view(self._function_view / other) + + raise UnitTypeError( + "Cannot divide function quantities which are not dimensionless by anything." + ) + + def __rtruediv__(self, other): + if self.unit.physical_unit == dimensionless_unscaled: + return self._maybe_new_view(self._function_view.__rtruediv__(other)) + + raise UnitTypeError( + "Cannot divide function quantities which are not dimensionless " + "into anything." + ) + + def _comparison(self, other, comparison_func): + """Do a comparison between self and other, raising UnitsError when + other cannot be converted to self because it has different physical + unit, and returning NotImplemented when there are other errors. + """ + try: + # will raise a UnitsError if physical units not equivalent + other_in_own_unit = self._to_own_unit(other, check_precision=False) + except UnitsError as exc: + if self.unit.physical_unit != dimensionless_unscaled: + raise exc + + try: + other_in_own_unit = self._function_view._to_own_unit( + other, check_precision=False + ) + except Exception: + raise exc + + except Exception: + return NotImplemented + + return comparison_func(other_in_own_unit) + + def __eq__(self, other): + try: + return self._comparison(other, self.value.__eq__) + except UnitsError: + return False + + def __ne__(self, other): + try: + return self._comparison(other, self.value.__ne__) + except UnitsError: + return True + + def __gt__(self, other): + return self._comparison(other, self.value.__gt__) + + def __ge__(self, other): + return self._comparison(other, self.value.__ge__) + + def __lt__(self, other): + return self._comparison(other, self.value.__lt__) + + def __le__(self, other): + return self._comparison(other, self.value.__le__) + + def __lshift__(self, other): + """Unit conversion operator `<<`.""" + try: + other = Unit(other, parse_strict="silent") + except UnitTypeError: + return NotImplemented + + return self.__class__(self, other, copy=False, subok=True) + + # Ensure Quantity methods are used only if they make sense. + def _wrap_function(self, function, *args, **kwargs): + if function in self._supported_functions: + return super()._wrap_function(function, *args, **kwargs) + + # For dimensionless, we can convert to regular quantities. + if all( + arg.unit.physical_unit == dimensionless_unscaled + for arg in (self,) + args + if (hasattr(arg, "unit") and hasattr(arg.unit, "physical_unit")) + ): + args = tuple(getattr(arg, "_function_view", arg) for arg in args) + return self._function_view._wrap_function(function, *args, **kwargs) + + raise TypeError( + f"Cannot use method that uses function '{function.__name__}' with " + "function quantities that are not dimensionless." + ) + + # Override functions that are supported but do not use _wrap_function + # in Quantity. + def max(self, axis=None, out=None, keepdims=False): + return self._wrap_function(np.max, axis, out=out, keepdims=keepdims) + + def min(self, axis=None, out=None, keepdims=False): + return self._wrap_function(np.min, axis, out=out, keepdims=keepdims) + + def sum(self, axis=None, dtype=None, out=None, keepdims=False): + return self._wrap_function(np.sum, axis, dtype, out=out, keepdims=keepdims) + + def cumsum(self, axis=None, dtype=None, out=None): + return self._wrap_function(np.cumsum, axis, dtype, out=out) + + def clip(self, a_min, a_max, out=None): + return self._wrap_function( + np.clip, self._to_own_unit(a_min), self._to_own_unit(a_max), out=out + ) diff --git a/astropy/units/function/logarithmic.py b/astropy/units/function/logarithmic.py new file mode 100644 index 000000000000..5c8449075c73 --- /dev/null +++ b/astropy/units/function/logarithmic.py @@ -0,0 +1,433 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +import numbers +from functools import cached_property + +import numpy as np + +from astropy.units import ( + CompositeUnit, + Unit, + UnitConversionError, + UnitsError, + UnitTypeError, + dimensionless_unscaled, +) + +from .core import FunctionQuantity, FunctionUnitBase + +__all__ = [ + "Decibel", + "DecibelUnit", + "Dex", + "DexUnit", + "LogQuantity", + "LogUnit", + "MagUnit", + "Magnitude", +] + + +class LogUnit(FunctionUnitBase): + """Logarithmic unit containing a physical one. + + Usually, logarithmic units are instantiated via specific subclasses + such `~astropy.units.MagUnit`, `~astropy.units.DecibelUnit`, and + `~astropy.units.DexUnit`. + + Parameters + ---------- + physical_unit : `~astropy.units.Unit` or `string` + Unit that is encapsulated within the logarithmic function unit. + If not given, dimensionless. + + function_unit : `~astropy.units.Unit` or `string` + By default, the same as the logarithmic unit set by the subclass. + + """ + + # the four essential overrides of FunctionUnitBase + @cached_property + def _default_function_unit(self): + from .units import dex + + return dex + + @property + def _quantity_class(self): + return LogQuantity + + def from_physical(self, x): + """Transformation from value in physical to value in logarithmic units. + Used in equivalency. + """ + # Local import to avoid circular dependency. + from .units import dex + + return dex.to(self._function_unit, np.log10(x)) + + def to_physical(self, x): + """Transformation from value in logarithmic to value in physical units. + Used in equivalency. + """ + from .units import dex + + return 10 ** self._function_unit.to(dex, x) + + # ^^^^ the four essential overrides of FunctionUnitBase + + # add addition and subtraction, which imply multiplication/division of + # the underlying physical units + def _add_and_adjust_physical_unit(self, other, sign_self, sign_other): + """Add/subtract LogUnit to/from another unit, and adjust physical unit. + + self and other are multiplied by sign_self and sign_other, resp. + + We wish to do: Âąlu_1 + Âąlu_2 -> lu_f (lu=logarithmic unit) + and pu_1^(Âą1) * pu_2^(Âą1) -> pu_f (pu=physical unit) + + Raises + ------ + UnitsError + If function units are not equivalent. + """ + # First, insist on compatible logarithmic type. Here, plain u.mag, + # u.dex, and u.dB are OK, i.e., other does not have to be LogUnit + # (this will indirectly test whether other is a unit at all). + try: + getattr(other, "function_unit", other)._to(self._function_unit) + except AttributeError: + # if other is not a unit (i.e., does not have _to). + return NotImplemented + except UnitsError: + raise UnitsError( + "Can only add/subtract logarithmic units of compatible type." + ) + + other_physical_unit = getattr(other, "physical_unit", dimensionless_unscaled) + physical_unit = CompositeUnit( + 1, [self._physical_unit, other_physical_unit], [sign_self, sign_other] + ) + + return self._copy(physical_unit) + + def __neg__(self): + return self._copy(self.physical_unit ** (-1)) + + def __add__(self, other): + # Only know how to add to a logarithmic unit with compatible type, + # be it a plain one (u.mag, etc.,) or another LogUnit + return self._add_and_adjust_physical_unit(other, +1, +1) + + def __radd__(self, other): + return self._add_and_adjust_physical_unit(other, +1, +1) + + def __sub__(self, other): + return self._add_and_adjust_physical_unit(other, +1, -1) + + def __rsub__(self, other): + # here, in normal usage other cannot be LogUnit; only equivalent one + # would be u.mag,u.dB,u.dex. But might as well use common routine. + return self._add_and_adjust_physical_unit(other, -1, +1) + + +class MagUnit(LogUnit): + """Logarithmic physical units expressed in magnitudes. + + Parameters + ---------- + physical_unit : `~astropy.units.Unit` or `string` + Unit that is encapsulated within the magnitude function unit. + If not given, dimensionless. + + function_unit : `~astropy.units.Unit` or `string` + By default, this is ``mag``, but this allows one to use an equivalent + unit such as ``2 mag``. + """ + + @cached_property + def _default_function_unit(self): + from .units import mag + + return mag + + @property + def _quantity_class(self): + return Magnitude + + +class DexUnit(LogUnit): + """Logarithmic physical units expressed in magnitudes. + + Parameters + ---------- + physical_unit : `~astropy.units.Unit` or `string` + Unit that is encapsulated within the magnitude function unit. + If not given, dimensionless. + + function_unit : `~astropy.units.Unit` or `string` + By default, this is ``dex``, but this allows one to use an equivalent + unit such as ``0.5 dex``. + """ + + @cached_property + def _default_function_unit(self): + from .units import dex + + return dex + + @property + def _quantity_class(self): + return Dex + + def to_string(self, format="generic"): + if format == "cds": + if self.physical_unit == dimensionless_unscaled: + return "[-]" # by default, would get "[---]". + else: + return f"[{self.physical_unit.to_string(format=format)}]" + else: + return super().to_string(format=format) + + +class DecibelUnit(LogUnit): + """Logarithmic physical units expressed in dB. + + Parameters + ---------- + physical_unit : `~astropy.units.Unit` or `string` + Unit that is encapsulated within the decibel function unit. + If not given, dimensionless. + + function_unit : `~astropy.units.Unit` or `string` + By default, this is ``dB``, but this allows one to use an equivalent + unit such as ``2 dB``. + """ + + @cached_property + def _default_function_unit(self): + from .units import dB + + return dB + + @property + def _quantity_class(self): + return Decibel + + +class LogQuantity(FunctionQuantity): + """A representation of a (scaled) logarithm of a number with a unit. + + Parameters + ---------- + value : number, `~astropy.units.Quantity`, `~astropy.units.LogQuantity`, or sequence of quantity-like. + The numerical value of the logarithmic quantity. If a number or + a `~astropy.units.Quantity` with a logarithmic unit, it will be + converted to ``unit`` and the physical unit will be inferred from + ``unit``. If a `~astropy.units.Quantity` with just a physical unit, + it will converted to the logarithmic unit, after, if necessary, + converting it to the physical unit inferred from ``unit``. + + unit : str, `~astropy.units.UnitBase`, or `~astropy.units.FunctionUnitBase`, optional + For an `~astropy.units.FunctionUnitBase` instance, the + physical unit will be taken from it; for other input, it will be + inferred from ``value``. By default, ``unit`` is set by the subclass. + + dtype : `~numpy.dtype`, optional + The ``dtype`` of the resulting Numpy array or scalar that will + hold the value. If not provided, is is determined automatically + from the input value. + + copy : bool, optional + If `True` (default), then the value is copied. Otherwise, a copy will + only be made if ``__array__`` returns a copy, if value is a nested + sequence, or if a copy is needed to satisfy an explicitly given + ``dtype``. (The `False` option is intended mostly for internal use, + to speed up initialization where a copy is known to have been made. + Use with care.) + + Examples + -------- + Typically, use is made of an `~astropy.units.FunctionQuantity` + subclass, as in:: + + >>> import astropy.units as u + >>> u.Magnitude(-2.5) + + >>> u.Magnitude(10.*u.count/u.second) + + >>> u.Decibel(1.*u.W, u.DecibelUnit(u.mW)) # doctest: +FLOAT_CMP + + + """ + + # only override of FunctionQuantity + _unit_class = LogUnit + + # additions that work just for logarithmic units + def __add__(self, other): + # Add function units, thus multiplying physical units. If no unit is + # given, assume dimensionless_unscaled; this will give the appropriate + # exception in LogUnit.__add__. + new_unit = self.unit + getattr(other, "unit", dimensionless_unscaled) + # Add actual logarithmic values, rescaling, e.g., dB -> dex. + result = self._function_view + getattr(other, "_function_view", other) + return self._new_view(result, new_unit) + + def __radd__(self, other): + return self.__add__(other) + + def __iadd__(self, other): + new_unit = self.unit + getattr(other, "unit", dimensionless_unscaled) + # Do calculation in-place using _function_view of array. + function_view = self._function_view + function_view += getattr(other, "_function_view", other) + self._set_unit(new_unit) + return self + + def __sub__(self, other): + # Subtract function units, thus dividing physical units. + new_unit = self.unit - getattr(other, "unit", dimensionless_unscaled) + # Subtract actual logarithmic values, rescaling, e.g., dB -> dex. + result = self._function_view - getattr(other, "_function_view", other) + return self._new_view(result, new_unit) + + def __rsub__(self, other): + new_unit = self.unit.__rsub__(getattr(other, "unit", dimensionless_unscaled)) + result = self._function_view.__rsub__(getattr(other, "_function_view", other)) + # Ensure the result is in right function unit scale + # (with rsub, this does not have to be one's own). + result = result.to(new_unit.function_unit) + return self._new_view(result, new_unit) + + def __isub__(self, other): + new_unit = self.unit - getattr(other, "unit", dimensionless_unscaled) + # Do calculation in-place using _function_view of array. + function_view = self._function_view + function_view -= getattr(other, "_function_view", other) + self._set_unit(new_unit) + return self + + def __mul__(self, other): + # Multiply by a float or a dimensionless quantity + if isinstance(other, numbers.Number): + # Multiplying a log means putting the factor into the exponent + # of the unit + new_physical_unit = self.unit.physical_unit**other + result = self.view(np.ndarray) * other + return self._new_view(result, self.unit._copy(new_physical_unit)) + else: + return super().__mul__(other) + + def __rmul__(self, other): + return self.__mul__(other) + + def __imul__(self, other): + if isinstance(other, numbers.Number): + new_physical_unit = self.unit.physical_unit**other + function_view = self._function_view + function_view *= other + self._set_unit(self.unit._copy(new_physical_unit)) + return self + else: + return super().__imul__(other) + + def __truediv__(self, other): + # Divide by a float or a dimensionless quantity + if isinstance(other, numbers.Number): + # Dividing a log means putting the denominator into the exponent + # of the unit + new_physical_unit = self.unit.physical_unit ** (1 / other) + result = self.view(np.ndarray) / other + return self._new_view(result, self.unit._copy(new_physical_unit)) + else: + return super().__truediv__(other) + + def __itruediv__(self, other): + if isinstance(other, numbers.Number): + new_physical_unit = self.unit.physical_unit ** (1 / other) + function_view = self._function_view + function_view /= other + self._set_unit(self.unit._copy(new_physical_unit)) + return self + else: + return super().__itruediv__(other) + + def __pow__(self, other): + # We check if this power is OK by applying it first to the unit. + try: + other = float(other) + except TypeError: + return NotImplemented + new_unit = self.unit**other + new_value = self.view(np.ndarray) ** other + return self._new_view(new_value, new_unit) + + def __ilshift__(self, other): + try: + other = Unit(other) + except UnitTypeError: + return NotImplemented + + if not isinstance(other, self._unit_class): + return NotImplemented + + try: + factor = self.unit.physical_unit._to(other.physical_unit) + except UnitConversionError: + # Maybe via equivalencies? Now we do make a temporary copy. + try: + value = self._to_value(other) + except UnitConversionError: + return NotImplemented + + self.view(np.ndarray)[...] = value + else: + self.view(np.ndarray)[...] += self.unit.from_physical(factor) + + self._set_unit(other) + return self + + # Methods that do not work for function units generally but are OK for + # logarithmic units as they imply differences and independence of + # physical unit. + def var(self, axis=None, dtype=None, out=None, ddof=0): + unit = self.unit.function_unit**2 + return self._wrap_function(np.var, axis, dtype, out=out, ddof=ddof, unit=unit) + + def std(self, axis=None, dtype=None, out=None, ddof=0): + unit = self.unit._copy(dimensionless_unscaled) + return self._wrap_function(np.std, axis, dtype, out=out, ddof=ddof, unit=unit) + + def __array_function__(self, function, types, args, kwargs): + # TODO: generalize this to all supported functions! + if function is np.ptp: + unit = self.unit._copy(dimensionless_unscaled) + return self._wrap_function(np.ptp, *args[1:], unit=unit, **kwargs) + elif function is np.diff: + return self.diff(*args[1:], **kwargs) + else: + return super().__array_function__(function, types, args, kwargs) + + def diff(self, n=1, axis=-1): + unit = self.unit._copy(dimensionless_unscaled) + return self._wrap_function(np.diff, n, axis, unit=unit) + + def ediff1d(self, to_end=None, to_begin=None): + unit = self.unit._copy(dimensionless_unscaled) + return self._wrap_function(np.ediff1d, to_end, to_begin, unit=unit) + + _supported_functions = FunctionQuantity._supported_functions | { + getattr(np, function) for function in ("var", "std", "ptp", "diff", "ediff1d") + } + + +class Dex(LogQuantity): + _unit_class = DexUnit + + +class Decibel(LogQuantity): + _unit_class = DecibelUnit + + +class Magnitude(LogQuantity): + _unit_class = MagUnit diff --git a/astropy/units/function/mixin.py b/astropy/units/function/mixin.py new file mode 100644 index 000000000000..363807c712cb --- /dev/null +++ b/astropy/units/function/mixin.py @@ -0,0 +1,24 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +from astropy.units.core import IrreducibleUnit, Unit + + +class FunctionMixin: + """Mixin class that makes UnitBase subclasses callable. + + Provides a __call__ method that passes on arguments to a FunctionUnit. + Instances of this class should define ``_function_unit_class`` pointing + to the relevant class. + + See units.py and logarithmic.py for usage. + """ + + def __call__(self, unit=None): + return self._function_unit_class(physical_unit=unit, function_unit=self) + + +class IrreducibleFunctionUnit(FunctionMixin, IrreducibleUnit): + pass + + +class RegularFunctionUnit(FunctionMixin, Unit): + pass diff --git a/astropy/units/function/units.py b/astropy/units/function/units.py new file mode 100644 index 000000000000..a9e57cf35ce1 --- /dev/null +++ b/astropy/units/function/units.py @@ -0,0 +1,119 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +""" +This package defines units that can also be used as functions of other units. +If called, their arguments are used to initialize the corresponding function +unit (e.g., ``u.mag(u.ct/u.s)``). Note that the prefixed versions cannot be +called, as it would be unclear what, e.g., ``u.mmag(u.ct/u.s)`` would mean. + +It also defines a few commonly used magnitude unit instances, like STmag, +for which the physical units are defined in `astropy.units.photometric`. + +All units are also available in (and should be used through) the +`astropy.units` namespace. +""" + +import unicodedata + +from astropy.units import photometric +from astropy.units.core import CompositeUnit, UnitBase, _add_prefixes + +from .logarithmic import DecibelUnit, DexUnit, MagUnit +from .mixin import IrreducibleFunctionUnit, RegularFunctionUnit + +__all__: list[str] = [] # Units are added at the end + +_ns = globals() + +########################################################################### +# Logarithmic units + +# These calls are what core.def_unit would do, but we need to use the callable +# unit versions. The actual function unit classes get added in logarithmic. + +dex = IrreducibleFunctionUnit( + ["dex"], namespace=_ns, doc="Dex: Base 10 logarithmic unit" +) +dex._function_unit_class = DexUnit + +dB = RegularFunctionUnit( + ["dB", "decibel"], + 0.1 * dex, + namespace=_ns, + doc="Decibel: ten per base 10 logarithmic unit", +) +dB._function_unit_class = DecibelUnit + +mag = RegularFunctionUnit( + ["mag"], + -0.4 * dex, + namespace=_ns, + doc="Astronomical magnitude: -2.5 per base 10 logarithmic unit", +) +_add_prefixes(mag, namespace=_ns, prefixes=True) +mag._function_unit_class = MagUnit + +STmag = mag(photometric.STflux) +STmag.__doc__ = "ST magnitude: STmag=-21.1 corresponds to 1 erg/s/cm2/A" + +ABmag = mag(photometric.ABflux) +ABmag.__doc__ = "AB magnitude: ABmag=-48.6 corresponds to 1 erg/s/cm2/Hz" + +M_bol = mag(photometric.Bol) +M_bol.__doc__ = ( + f"Absolute bolometric magnitude: M_bol=0 corresponds to {photometric.Bol}" +) + +m_bol = mag(photometric.bol) +m_bol.__doc__ = ( + f"Apparent bolometric magnitude: m_bol=0 corresponds to {photometric.bol}" +) + + +########################################################################### +# DOCSTRING + +__all__ += [ + n + for n, v in _ns.items() + if isinstance(v, (UnitBase, MagUnit)) and unicodedata.normalize("NFKC", n) == n +] + +if __doc__ is not None: + # This generates a docstring for this module that describes all of the + # standard units defined here. + from astropy.units.docgen import generate_unit_summary as _generate_unit_summary + + def _description(unit): + pu = unit.physical_unit.represents + if unit.__doc__[:2] in {"AB", "ST"}: + pu = 1.0 * CompositeUnit(1.0, pu.bases, pu.powers) + return "".join( + unit.__doc__.partition("corresponds to ")[:-1] + + (f":math:`{pu.to_string(format='latex')[1:-1]}`",) + ) + + template = """ + * - ``{}`` + - {} + - {} +""" + + __doc__ += ( + _generate_unit_summary(globals()) + + """ +.. list-table:: Available Magnitude Units + :header-rows: 1 + :widths: 10 50 10 + + * - Unit + - Description + - Represents +""" + + "".join( + [ + template.format(key, _description(val), val) + for key, val in globals().items() + if isinstance(val, MagUnit) + ] + ) + ) diff --git a/astropy/units/imperial.py b/astropy/units/imperial.py new file mode 100644 index 000000000000..cbe4263a063d --- /dev/null +++ b/astropy/units/imperial.py @@ -0,0 +1,177 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +"""Colloquially used Imperial units. + +These units are available in the `astropy.units.imperial` namespace, but not in the +top-level `astropy.units` namespace, e.g.:: + + >>> import astropy.units as u + >>> mph = u.imperial.mile / u.hour + >>> mph + Unit("mi / h") + +To include them in `~astropy.units.UnitBase.compose` and the results of +`~astropy.units.UnitBase.find_equivalent_units`, do:: + + >>> import astropy.units as u + >>> u.imperial.enable() # doctest: +SKIP +""" +# avoid ruff complaints about undefined names defined by def_unit +# ruff: noqa: F821 + +__all__: list[str] = ["enable"] # Units are added at the end + +from . import si +from .core import _UnitContext, add_enabled_units, def_unit +from .docgen import generate_dunder_all, generate_unit_summary + +_ns = globals() + +########################################################################### +# LENGTH + +def_unit(["inch"], 2.54 * si.cm, namespace=_ns, doc="International inch") +def_unit(["ft", "foot"], 12 * inch, namespace=_ns, doc="International foot") +def_unit(["yd", "yard"], 3 * ft, namespace=_ns, doc="International yard") +def_unit(["mi", "mile"], 5280 * ft, namespace=_ns, doc="International mile") +def_unit(["mil", "thou"], 0.001 * inch, namespace=_ns, doc="Thousandth of an inch") +def_unit(["nmi", "nauticalmile", "NM"], 1852 * si.m, namespace=_ns, doc="Nautical mile") +def_unit(["fur", "furlong"], 660 * ft, namespace=_ns, doc="Furlong") + + +########################################################################### +# AREAS + +def_unit(["ac", "acre"], 43560 * ft**2, namespace=_ns, doc="International acre") + + +########################################################################### +# VOLUMES + +def_unit(["gallon"], si.liter / 0.264172052, namespace=_ns, doc="U.S. liquid gallon") +def_unit(["quart"], gallon / 4, namespace=_ns, doc="U.S. liquid quart") +def_unit(["pint"], quart / 2, namespace=_ns, doc="U.S. liquid pint") +def_unit(["cup"], pint / 2, namespace=_ns, doc="U.S. customary cup") +def_unit( + ["foz", "fluid_oz", "fluid_ounce"], cup / 8, namespace=_ns, doc="U.S. fluid ounce" +) +def_unit( + ["tbsp", "tablespoon"], foz / 2, namespace=_ns, doc="U.S. customary tablespoon" +) +def_unit(["tsp", "teaspoon"], tbsp / 3, namespace=_ns, doc="U.S. customary teaspoon") + + +########################################################################### +# MASS + +def_unit( + ["oz", "ounce"], + 28.349523125 * si.g, + namespace=_ns, + doc="International avoirdupois ounce: mass", +) +def_unit( + ["lb", "lbm", "pound"], + 16 * oz, + namespace=_ns, + doc="International avoirdupois pound: mass", +) +def_unit( + ["st", "stone"], 14 * lb, namespace=_ns, doc="International avoirdupois stone: mass" +) +def_unit(["ton"], 2000 * lb, namespace=_ns, doc="International avoirdupois ton: mass") +def_unit(["slug"], 32.174049 * lb, namespace=_ns, doc="slug: mass") + + +########################################################################### +# SPEED + +def_unit( + ["kn", "kt", "knot", "NMPH"], + nmi / si.h, + namespace=_ns, + doc="nautical unit of speed: 1 nmi per hour", +) + + +########################################################################### +# FORCE + +def_unit("lbf", slug * ft * si.s**-2, namespace=_ns, doc="Pound: force") +def_unit(["kip", "kilopound"], 1000 * lbf, namespace=_ns, doc="Kilopound: force") + + +########################################################################## +# ENERGY + +def_unit(["BTU", "btu"], 1.05505585 * si.kJ, namespace=_ns, doc="British thermal unit") +def_unit( + ["cal", "calorie"], + 4.184 * si.J, + namespace=_ns, + doc="Thermochemical calorie: pre-SI metric unit of energy", +) +def_unit( + ["kcal", "Cal", "Calorie", "kilocal", "kilocalorie"], + 1000 * cal, + namespace=_ns, + doc="Calorie: colloquial definition of Calorie", +) + + +########################################################################## +# PRESSURE + +def_unit("psi", lbf * inch**-2, namespace=_ns, doc="Pound per square inch: pressure") + + +########################################################################### +# POWER + +# Imperial units +def_unit( + ["hp", "horsepower"], + si.W / 0.00134102209, + namespace=_ns, + doc="Electrical horsepower", +) + + +########################################################################### +# TEMPERATURE + +def_unit( + ["deg_F", "Fahrenheit"], + namespace=_ns, + doc="Degrees Fahrenheit", + format={"latex": r"{}^{\circ}F", "unicode": "°F"}, +) +def_unit( + ["deg_R", "Rankine"], + 5 / 9 * si.K, + namespace=_ns, + doc="Rankine scale: absolute scale of thermodynamic temperature", + format={"latex": r"{}^{\circ}R", "unicode": "°R"}, +) + +########################################################################### +# ALL & DOCSTRING + +__all__ += generate_dunder_all(globals()) # noqa: PLE0605 + +if __doc__ is not None: + # This generates a docstring for this module that describes all of the + # standard units defined here. + __doc__ += generate_unit_summary(globals()) + + +def enable() -> _UnitContext: + """ + Enable Imperial units so they appear in results of + `~astropy.units.UnitBase.find_equivalent_units` and + `~astropy.units.UnitBase.compose`. + + This may be used with the ``with`` statement to enable Imperial + units only temporarily. + """ + return add_enabled_units(globals()) diff --git a/astropy/units/misc.py b/astropy/units/misc.py new file mode 100644 index 000000000000..ac09a604e3dc --- /dev/null +++ b/astropy/units/misc.py @@ -0,0 +1,163 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +""" +This package defines miscellaneous units. They are also available in +(and should be used through) the `astropy.units` namespace. +""" +# avoid ruff complaints about undefined names defined by def_unit +# ruff: noqa: F821 + +import numpy as np + +from astropy.constants import si as _si + +from . import si +from .core import binary_prefixes, def_unit, si_prefixes +from .docgen import generate_dunder_all, generate_unit_summary + +__all__: list[str] = [] # Units are added at the end + +_ns = globals() + +########################################################################### +# AREAS + +def_unit( + ["barn", "barn"], + 10**-28 * si.m**2, + namespace=_ns, + prefixes=True, + doc="barn: unit of area used in HEP", +) + + +########################################################################### +# ANGULAR MEASUREMENTS + +def_unit( + ["cycle", "cy"], + 2.0 * np.pi * si.rad, + namespace=_ns, + prefixes=False, + doc="cycle: angular measurement, a full turn or rotation", +) +def_unit( + ["spat", "sp"], + 4.0 * np.pi * si.sr, + namespace=_ns, + prefixes=False, + doc="spat: the solid angle of the sphere, 4pi sr", +) + +########################################################################## +# PRESSURE + +def_unit( + ["bar"], + 1e5 * si.Pa, + namespace=_ns, + prefixes=[(["m"], ["milli"], 1.0e-3)], + doc="bar: pressure", +) +# The torr is almost the same as mmHg but not quite. +# See https://en.wikipedia.org/wiki/Torr +def_unit( + ["Torr", "torr"], + _si.atm.value / 760.0 * si.Pa, + namespace=_ns, + prefixes=[(["m"], ["milli"], 1.0e-3)], + doc=( + "Unit of pressure based on an absolute scale, now defined as " + "exactly 1/760 of a standard atmosphere" + ), +) + +########################################################################### +# MASS + +def_unit( + ["M_p"], + _si.m_p, + namespace=_ns, + doc="Proton mass", + format={"latex": r"M_{p}", "unicode": "Mₚ"}, +) +def_unit( + ["M_e"], + _si.m_e, + namespace=_ns, + doc="Electron mass", + format={"latex": r"M_{e}", "unicode": "Mₑ"}, +) +def_unit( + ["u", "Da", "Dalton"], + _si.u, + namespace=_ns, + prefixes=True, + exclude_prefixes=["a", "da"], + doc="Unified atomic mass unit", +) + +########################################################################## +# ENERGY + +def_unit( + ["eV", "electronvolt"], + _si.e.value * si.J, + namespace=_ns, + prefixes=True, + doc="Electron Volt", +) + +# Here, explicitly convert the planck constant to 'eV s' since the constant +# can override that to give a more precise value that takes into account +# covariances between e and h. Eventually, this may also be replaced with +# just `_si.Ryd.to(eV)`. +def_unit( + ["Ry", "rydberg"], + (_si.Ryd * _si.c * _si.h.to(eV * si.s)).to(eV), + namespace=_ns, + prefixes=True, + doc="Rydberg: Energy of a photon whose wavenumber is the Rydberg constant", + format={"latex": r"R_{\infty}", "unicode": "R∞"}, +) + + +########################################################################### +# COMPUTER + +def_unit( + (["bit", "b"], ["bit"]), + namespace=_ns, + prefixes=si_prefixes + binary_prefixes, +) +def_unit( + (["byte", "B"], ["byte"]), + 8 * bit, + namespace=_ns, + prefixes=si_prefixes + binary_prefixes, + exclude_prefixes=["d"], +) +def_unit( + (["pix", "pixel"], ["pixel"]), + format={"ogip": "pixel", "vounit": "pixel"}, + namespace=_ns, + prefixes=True, +) +def_unit( + (["vox", "voxel"], ["voxel"]), + format={"fits": "voxel", "ogip": "voxel", "vounit": "voxel"}, + namespace=_ns, + prefixes=True, +) + +########################################################################### +# ALL & DOCSTRING + +__all__ += generate_dunder_all(globals()) # noqa: PLE0605 + + +if __doc__ is not None: + # This generates a docstring for this module that describes all of the + # standard units defined here. + __doc__ += generate_unit_summary(globals()) diff --git a/astropy/units/photometric.py b/astropy/units/photometric.py new file mode 100644 index 000000000000..99143a44f496 --- /dev/null +++ b/astropy/units/photometric.py @@ -0,0 +1,81 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +""" +This module defines magnitude zero points and related photometric quantities. + +The corresponding magnitudes are given in the description of each unit. +(the actual definitions are in `astropy.units.function.units`). + +Both the units and magnitudes are available in (and should be used +through) the `astropy.units` namespace. + +""" + +import numpy as np + +from astropy.constants.si import L_bol0 + +from . import astrophys, cgs, si +from .core import def_unit +from .docgen import generate_dunder_all, generate_unit_summary + +__all__ = [] # Units are added at the end + +_ns = globals() + +def_unit( + ["Bol", "L_bol"], + L_bol0, + namespace=_ns, + prefixes=False, + doc=( + "Luminosity corresponding to absolute bolometric magnitude zero " + "(magnitude ``M_bol``)." + ), +) +def_unit( + ["bol", "f_bol"], + L_bol0 / (4 * np.pi * (10.0 * astrophys.pc) ** 2), + namespace=_ns, + prefixes=False, + doc=( + "Irradiance corresponding to apparent bolometric magnitude zero " + "(magnitude ``m_bol``)." + ), +) +def_unit( + ["AB", "ABflux"], + 10.0 ** (48.6 / -2.5) * cgs.erg * cgs.cm**-2 / si.s / si.Hz, + namespace=_ns, + prefixes=False, + doc="AB magnitude zero flux density (magnitude ``ABmag``).", +) +def_unit( + ["ST", "STflux"], + 10.0 ** (21.1 / -2.5) * cgs.erg * cgs.cm**-2 / si.s / si.AA, + namespace=_ns, + prefixes=False, + doc="ST magnitude zero flux density (magnitude ``STmag``).", +) +def_unit( + ["mgy", "maggy"], + namespace=_ns, + prefixes=[(["n"], ["nano"], 1e-9)], + doc=( + "Maggies - a linear flux unit that is the flux for a mag=0 object." + "To tie this onto a specific calibrated unit system, the " + "zero_point_flux equivalency should be used." + ), +) + + +########################################################################### +# ALL & DOCSTRING + +__all__ += generate_dunder_all(globals()) # noqa: PLE0605 + + +if __doc__ is not None: + # This generates a docstring for this module that describes all of the + # standard units defined here. + __doc__ += generate_unit_summary(globals()) diff --git a/astropy/units/physical.py b/astropy/units/physical.py new file mode 100644 index 000000000000..c3f49a658b59 --- /dev/null +++ b/astropy/units/physical.py @@ -0,0 +1,610 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +"""Defines the physical types that correspond to different units. + +The classes and functions defined here are also available in +(and should be used through) the `astropy.units` namespace. +""" + +import numbers +from collections.abc import Iterator +from typing import Final, Union + +from . import astrophys, cgs, core, misc, quantity, si +from .typing import PhysicalTypeID, QuantityLike, UnitPowerLike + +__all__: Final = ["PhysicalType", "def_physical_type", "get_physical_type"] + + +_units_and_physical_types: Final[list[tuple[core.UnitBase, str | set[str]]]] = [ + (core.dimensionless_unscaled, "dimensionless"), + (si.m, "length"), + (si.m**2, "area"), + (si.m**3, "volume"), + (si.s, "time"), + (si.rad, "angle"), + (si.sr, "solid angle"), + (si.m / si.s, {"speed", "velocity"}), + (si.m / si.s**2, "acceleration"), + (si.Hz, "frequency"), + (si.g, "mass"), + (si.mol, "amount of substance"), + (si.K, "temperature"), + (si.W * si.m**-1 * si.K**-1, "thermal conductivity"), + (si.J * si.K**-1, {"heat capacity", "entropy"}), + (si.J * si.K**-1 * si.kg**-1, {"specific heat capacity", "specific entropy"}), + (si.N, "force"), + (si.J, {"energy", "work", "torque"}), + (si.J * si.m**-2 * si.s**-1, {"energy flux", "irradiance"}), + (si.Pa, {"pressure", "energy density", "stress"}), + (si.W, {"power", "radiant flux"}), + (si.kg * si.m**-3, "mass density"), + (si.m**3 / si.kg, "specific volume"), + (si.mol / si.m**3, "molar concentration"), + (si.m**3 / si.mol, "molar volume"), + (si.kg * si.m / si.s, {"momentum", "impulse"}), + (si.kg * si.m**2 / si.s, {"angular momentum", "action"}), + (si.rad / si.s, {"angular speed", "angular velocity", "angular frequency"}), + (si.rad / si.s**2, "angular acceleration"), + (si.rad / si.m, "plate scale"), + (si.g / (si.m * si.s), "dynamic viscosity"), + (si.m**2 / si.s, {"diffusivity", "kinematic viscosity"}), + (si.m**-1, "wavenumber"), + (si.m**-2, "column density"), + (si.A, "electrical current"), + (si.C, "electrical charge"), + (si.V, "electrical potential"), + (si.Ohm, {"electrical resistance", "electrical impedance", "electrical reactance"}), + (si.Ohm * si.m, "electrical resistivity"), + (si.S, "electrical conductance"), + (si.S / si.m, "electrical conductivity"), + (si.F, "electrical capacitance"), + (si.C * si.m, "electrical dipole moment"), + (si.A / si.m**2, "electrical current density"), + (si.V / si.m, "electrical field strength"), + ( + si.C / si.m**2, + {"electrical flux density", "surface charge density", "polarization density"}, + ), + (si.C / si.m**3, "electrical charge density"), + (si.F / si.m, "permittivity"), + (si.Wb, "magnetic flux"), + (si.Wb**2, "magnetic helicity"), + (si.T, "magnetic flux density"), + (si.A / si.m, "magnetic field strength"), + (si.m**2 * si.A, "magnetic moment"), + (si.H / si.m, {"electromagnetic field strength", "permeability"}), + (si.H, "inductance"), + (si.cd, "luminous intensity"), + (si.lm, "luminous flux"), + (si.lx, {"luminous emittance", "illuminance"}), + (si.W / si.sr, "radiant intensity"), + (si.cd / si.m**2, "luminance"), + (si.m**-3 * si.s**-1, "volumetric rate"), + (astrophys.Jy, "spectral flux density"), + (astrophys.Jy / si.sr, "surface brightness"), + (si.W * si.m**2 * si.Hz**-1, "surface tension"), + (si.J * si.m**-3 * si.s**-1, {"spectral flux density wav", "power density"}), + (si.J * si.m**-3 * si.s**-1 * si.sr**-1, "surface brightness wav"), + (astrophys.photon / si.Hz / si.cm**2 / si.s, "photon flux density"), + (astrophys.photon / si.AA / si.cm**2 / si.s, "photon flux density wav"), + (astrophys.photon / si.Hz / si.cm**2 / si.s / si.sr, "photon surface brightness"), + ( + astrophys.photon / si.AA / si.cm**2 / si.s / si.sr, + "photon surface brightness wav", + ), + (astrophys.R, "photon flux"), + (misc.bit, "data quantity"), + (misc.bit / si.s, "bandwidth"), + (cgs.Franklin, "electrical charge (ESU)"), + (cgs.statampere, "electrical current (ESU)"), + (cgs.Biot, "electrical current (EMU)"), + (cgs.abcoulomb, "electrical charge (EMU)"), + (si.m * si.s**-3, {"jerk", "jolt"}), + (si.m * si.s**-4, {"snap", "jounce"}), + (si.m * si.s**-5, "crackle"), + (si.m * si.s**-6, {"pop", "pounce"}), + (si.K / si.m, "temperature gradient"), + (si.J / si.kg, {"specific energy", "dose of ionizing radiation"}), + (si.mol * si.m**-3 * si.s**-1, "reaction rate"), + (si.kg * si.m**2, "moment of inertia"), + (si.mol / si.s, "catalytic activity"), + (si.J * si.K**-1 * si.mol**-1, "molar heat capacity"), + (si.mol / si.kg, "molality"), + (si.m * si.s, "absement"), + (si.m * si.s**2, "absity"), + (si.m**3 / si.s, "volumetric flow rate"), + (si.s**-2, "frequency drift"), + (si.Pa**-1, "compressibility"), + (astrophys.electron * si.m**-3, "electron density"), + (astrophys.electron * si.m**-2 * si.s**-1, "electron flux"), + (si.kg / si.m**2, "surface mass density"), + (si.W / si.m**2 / si.sr, "radiance"), + (si.J / si.mol, "chemical potential"), + (si.kg / si.m, "linear density"), + (si.H**-1, "magnetic reluctance"), + (si.W / si.K, "thermal conductance"), + (si.K / si.W, "thermal resistance"), + (si.K * si.m / si.W, "thermal resistivity"), + (si.N / si.s, "yank"), + (si.S * si.m**2 / si.mol, "molar conductivity"), + (si.m**2 / si.V / si.s, "electrical mobility"), + (si.lumen / si.W, "luminous efficacy"), + (si.m**2 / si.kg, {"opacity", "mass attenuation coefficient"}), + (si.kg * si.m**-2 * si.s**-1, {"mass flux", "momentum density"}), + (si.m**-3, "number density"), + (si.m**-2 * si.s**-1, "particle flux"), +] + + +class PhysicalType: + """ + Represents the physical type(s) that are dimensionally compatible + with a set of units. + + Instances of this class should be accessed through either + `get_physical_type` or by using the + `~astropy.units.core.UnitBase.physical_type` attribute of units. + This class is not intended to be instantiated directly in user code. + + For a list of physical types, see `astropy.units.physical`. + + Parameters + ---------- + unit : `~astropy.units.Unit` + The unit to be represented by the physical type. + + physical_types : `str` or `set` of `str` + A `str` representing the name of the physical type of the unit, + or a `set` containing strings that represent one or more names + of physical types. + + Notes + ----- + A physical type will be considered equal to an equivalent + `PhysicalType` instance (recommended) or a string that contains a + name of the physical type. The latter method is not recommended + in packages, as the names of some physical types may change in the + future. + + To maintain backwards compatibility, two physical type names may be + included in one string if they are separated with a slash (e.g., + ``"momentum/impulse"``). String representations of physical types + may include underscores instead of spaces. + + Examples + -------- + `PhysicalType` instances may be accessed via the + `~astropy.units.core.UnitBase.physical_type` attribute of units. + + >>> import astropy.units as u + >>> u.meter.physical_type + PhysicalType('length') + + `PhysicalType` instances may also be accessed by calling + `get_physical_type`. This function will accept a unit, a string + containing the name of a physical type, or the number one. + + >>> u.get_physical_type(u.m ** -3) + PhysicalType('number density') + >>> u.get_physical_type("volume") + PhysicalType('volume') + >>> u.get_physical_type(1) + PhysicalType('dimensionless') + + Some units are dimensionally compatible with multiple physical types. + A pascal is intended to represent pressure and stress, but the unit + decomposition is equivalent to that of energy density. + + >>> pressure = u.get_physical_type("pressure") + >>> pressure + PhysicalType({'energy density', 'pressure', 'stress'}) + >>> 'energy density' in pressure + True + + Physical types can be tested for equality against other physical + type objects or against strings that may contain the name of a + physical type. + + >>> area = (u.m ** 2).physical_type + >>> area == u.barn.physical_type + True + >>> area == "area" + True + + Multiplication, division, and exponentiation are enabled so that + physical types may be used for dimensional analysis. + + >>> length = u.pc.physical_type + >>> area = (u.cm ** 2).physical_type + >>> length * area + PhysicalType('volume') + >>> area / length + PhysicalType('length') + >>> length ** 3 + PhysicalType('volume') + + may also be performed using a string that contains the name of a + physical type. + + >>> "length" * area + PhysicalType('volume') + >>> "area" / length + PhysicalType('length') + + Unknown physical types are labelled as ``"unknown"``. + + >>> (u.s ** 13).physical_type + PhysicalType('unknown') + + Dimensional analysis may be performed for unknown physical types too. + + >>> length_to_19th_power = (u.m ** 19).physical_type + >>> length_to_20th_power = (u.m ** 20).physical_type + >>> length_to_20th_power / length_to_19th_power + PhysicalType('length') + """ + + def __init__(self, unit: core.UnitBase, physical_types: str | set[str]) -> None: + self._unit = _replace_temperatures_with_kelvin(unit) + self._physical_type = sorted(_standardize_physical_type_names(physical_types)) + + def __iter__(self) -> Iterator[str]: + yield from self._physical_type + + def __eq__(self, other: object) -> bool: + """ + Return `True` if ``other`` represents a physical type that is + consistent with the physical type of the `PhysicalType` instance. + """ + if self is other: + return True + if isinstance(other, PhysicalType): + return self._unit._physical_type_id == other._unit._physical_type_id + elif isinstance(other, str): + other = _standardize_physical_type_names(other) + return other.issubset(self._physical_type) + else: + return NotImplemented + + def __repr__(self) -> str: + if len(self._physical_type) == 1: + names = "'" + self._physical_type[0] + "'" + else: + names = "{" + str(self._physical_type)[1:-1] + "}" + return f"PhysicalType({names})" + + def __str__(self) -> str: + return "/".join(self._physical_type) + + @staticmethod + def _dimensionally_compatible_unit(obj: object) -> core.UnitBase | None: + """ + Return a unit that corresponds to the provided argument. + """ + if isinstance(obj, core.UnitBase): + return _replace_temperatures_with_kelvin(obj) + elif isinstance(obj, PhysicalType): + return obj._unit + elif isinstance(obj, numbers.Real) and obj == 1: + return core.dimensionless_unscaled + elif isinstance(obj, str): + return _physical_type_from_str(obj)._unit + return None + + def __mul__( + self, other: Union["PhysicalType", core.UnitBase, numbers.Real, str] + ) -> "PhysicalType": + if other_unit := self._dimensionally_compatible_unit(other): + return (self._unit * other_unit).physical_type + return NotImplemented + + def __rmul__( + self, other: Union["PhysicalType", core.UnitBase, str] + ) -> "PhysicalType": + return self.__mul__(other) + + def __truediv__( + self, other: Union["PhysicalType", core.UnitBase, numbers.Real, str] + ) -> "PhysicalType": + if other_unit := self._dimensionally_compatible_unit(other): + return (self._unit / other_unit).physical_type + return NotImplemented + + def __rtruediv__( + self, other: Union["PhysicalType", core.UnitBase, numbers.Real, str] + ) -> "PhysicalType": + if other_unit := self._dimensionally_compatible_unit(other): + return (other_unit / self._unit).physical_type + return NotImplemented + + def __pow__(self, power: UnitPowerLike) -> "PhysicalType": + return (self._unit**power).physical_type + + def __hash__(self) -> int: + return hash(self._unit._physical_type_id) + + def __len__(self) -> int: + return len(self._physical_type) + + # We need to prevent operations like where a Unit instance left + # multiplies a PhysicalType instance from returning a `Quantity` + # instance with a PhysicalType as the value. We can do this by + # preventing np.array from casting a PhysicalType instance as + # an object array. + __array__: Final = None + + +_physical_unit_mapping: Final[dict[PhysicalTypeID, PhysicalType]] = {} +_unit_physical_mapping: Final[dict[str, PhysicalTypeID]] = {} +_name_physical_mapping: Final[dict[str, PhysicalType]] = {} +# mapping from attribute-accessible name (no spaces, etc.) to the actual name. +_attrname_physical_mapping: Final[dict[str, PhysicalType]] = {} + + +def _physical_type_from_str(name: str) -> PhysicalType: + """ + Return the `PhysicalType` instance associated with the name of a + physical type. + """ + if name == "unknown": + raise ValueError("cannot uniquely identify an 'unknown' physical type.") + + elif name in _attrname_physical_mapping: + return _attrname_physical_mapping[name] # convert attribute-accessible + elif name in _name_physical_mapping: + return _name_physical_mapping[name] + else: + raise ValueError(f"{name!r} is not a known physical type.") + + +def _replace_temperatures_with_kelvin(unit: core.UnitBase) -> core.UnitBase: + """Replace °F, and °C in the bases of `unit` with K. + + The Kelvin, Celsius and Fahrenheit scales have different zero points, + which is a problem for the unit conversion machinery (without the + `temperature` equivalency). Replacing °F, and °C with kelvin allows the + physical type to be treated consistently. The Rankine scale has the + same zero point as the Kelvin scale, so degrees Rankine do not have to + be special-cased. + """ + physical_type_id = unit._physical_type_id + + physical_type_id_components = [] + substitution_was_made = False + + for base, power in physical_type_id: + if base in ["deg_F", "deg_C"]: + base = "K" + substitution_was_made = True + physical_type_id_components.append((base, power)) + + if substitution_was_made: + return core.Unit._from_physical_type_id(tuple(physical_type_id_components)) + else: + return unit + + +def _standardize_physical_type_names(physical_type_input: str | set[str]) -> set[str]: + """ + Convert a string or `set` of strings into a `set` containing + string representations of physical types. + + The strings provided in ``physical_type_input`` can each contain + multiple physical types that are separated by a regular slash. + Underscores are treated as spaces so that variable names could + be identical to physical type names. + """ + if isinstance(physical_type_input, str): + physical_type_input = {physical_type_input} + + standardized_physical_types = set() + + for ptype_input in physical_type_input: + if not isinstance(ptype_input, str): + raise ValueError(f"expecting a string, but got {ptype_input}") + input_set = set(ptype_input.split("/")) + processed_set = {s.strip().replace("_", " ") for s in input_set} + standardized_physical_types |= processed_set + + return standardized_physical_types + + +def def_physical_type(unit: core.UnitBase, name: str | set[str]) -> None: + """ + Add a mapping between a unit and the corresponding physical type(s). + + If a physical type already exists for a unit, add new physical type + names so long as those names are not already in use for other + physical types. + + Parameters + ---------- + unit : `~astropy.units.Unit` + The unit to be represented by the physical type. + + name : `str` or `set` of `str` + A `str` representing the name of the physical type of the unit, + or a `set` containing strings that represent one or more names + of physical types. + + Raises + ------ + ValueError + If a physical type name is already in use for another unit, or + if attempting to name a unit as ``"unknown"``. + + Notes + ----- + For a list of physical types, see `astropy.units.physical`. + """ + physical_type_id = unit._physical_type_id + physical_type_names = _standardize_physical_type_names(name) + + if "unknown" in physical_type_names: + raise ValueError("cannot uniquely define an unknown physical type") + + names_for_other_units = set(_unit_physical_mapping.keys()).difference( + _physical_unit_mapping.get(physical_type_id, {}) + ) + names_already_in_use = physical_type_names & names_for_other_units + if names_already_in_use: + raise ValueError( + "the following physical type names are already in use: " + f"{names_already_in_use}." + ) + + unit_already_in_use = physical_type_id in _physical_unit_mapping + if unit_already_in_use: + physical_type = _physical_unit_mapping[physical_type_id] + physical_type._physical_type = sorted(physical_type_names | set(physical_type)) + else: + physical_type = PhysicalType(unit, physical_type_names) + _physical_unit_mapping[physical_type_id] = physical_type + + for ptype in physical_type: + _unit_physical_mapping[ptype] = physical_type_id + _name_physical_mapping[ptype] = physical_type + # attribute-accessible name + attr_name = ptype.replace(" ", "_").replace("(", "").replace(")", "") + _attrname_physical_mapping[attr_name] = physical_type + + +def get_physical_type( + obj: PhysicalType | str | core.UnitBase | QuantityLike, +) -> PhysicalType: + """ + Return the physical type that corresponds to a unit (or another + physical type representation). + + Parameters + ---------- + obj : quantity-like or `~astropy.units.PhysicalType`-like + An object that (implicitly or explicitly) has a corresponding + physical type. This object may be a unit, a + `~astropy.units.Quantity`, an object that can be converted to a + `~astropy.units.Quantity` (such as a number or array), a string + that contains a name of a physical type, or a + `~astropy.units.PhysicalType` instance. + + Returns + ------- + `~astropy.units.PhysicalType` + A representation of the physical type(s) of the unit. + + Notes + ----- + For a list of physical types, see `astropy.units.physical`. + + Examples + -------- + The physical type may be retrieved from a unit or a + `~astropy.units.Quantity`. + + >>> import astropy.units as u + >>> u.get_physical_type(u.meter ** -2) + PhysicalType('column density') + >>> u.get_physical_type(0.62 * u.barn * u.Mpc) + PhysicalType('volume') + + The physical type may also be retrieved by providing a `str` that + contains the name of a physical type. + + >>> u.get_physical_type("energy") + PhysicalType({'energy', 'torque', 'work'}) + + Numbers and arrays of numbers correspond to a dimensionless physical + type. + + >>> u.get_physical_type(1) + PhysicalType('dimensionless') + """ + if isinstance(obj, PhysicalType): + return obj + + if isinstance(obj, str): + return _physical_type_from_str(obj) + + if isinstance(obj, core.UnitBase): + unit = obj + else: + try: + unit = quantity.Quantity(obj, copy=None).unit + except TypeError as exc: + raise TypeError(f"{obj} does not correspond to a physical type.") from exc + + unit = _replace_temperatures_with_kelvin(unit) + physical_type_id = unit._physical_type_id + unit_has_known_physical_type = physical_type_id in _physical_unit_mapping + + if unit_has_known_physical_type: + return _physical_unit_mapping[physical_type_id] + else: + return PhysicalType(unit, "unknown") + + +# ------------------------------------------------------------------------------ +# Script section creating the physical types and the documentation + +# define the physical types +for unit, physical_type in _units_and_physical_types: + def_physical_type(unit, physical_type) +del unit, physical_type + + +# For getting the physical types. +def __getattr__(name): + """Checks for physical types using lazy import. + + This also allows user-defined physical types to be accessible from the + :mod:`astropy.units.physical` module. + See `PEP 562 `_ + + Parameters + ---------- + name : str + The name of the attribute in this module. If it is already defined, + then this function is not called. + + Returns + ------- + ptype : `~astropy.units.physical.PhysicalType` + + Raises + ------ + AttributeError + If the ``name`` does not correspond to a physical type + """ + if name in _attrname_physical_mapping: + return _attrname_physical_mapping[name] + + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + + +def __dir__() -> list[str]: + """Return contents directory (__all__ + all physical type names).""" + return list(set(__all__) | set(_attrname_physical_mapping.keys())) + + +# This generates a docstring addition for this module that describes all of the +# standard physical types defined here. +if __doc__ is not None: + doclines = [ + ".. list-table:: Defined Physical Types", + " :header-rows: 1", + " :widths: 30 10 50", + "", + " * - Physical type", + " - Unit", + " - Other physical type(s) with same unit", + ] + + for name in sorted(_name_physical_mapping.keys()): + ptype = _name_physical_mapping[name] + doclines += [ + f" * - _`{name}`", + f" - :math:`{ptype._unit.to_string('latex')[1:-1]}`", + f" - {', '.join([n for n in ptype if n != name])}", + ] + + __doc__ += "\n\n" + "\n".join(doclines) diff --git a/astropy/units/quantity.py b/astropy/units/quantity.py new file mode 100644 index 000000000000..3a826895797e --- /dev/null +++ b/astropy/units/quantity.py @@ -0,0 +1,2294 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +""" +This module defines the `Quantity` object, which represents a number with some +associated units. `Quantity` objects support operations like ordinary numbers, +but will deal with unit conversions internally. +""" + +import builtins +import numbers +import operator +import re +import warnings +from collections.abc import Collection +from fractions import Fraction +from typing import ClassVar, Final, Self + +import numpy as np + +from astropy import config as _config +from astropy.utils.data_info import ParentDtypeInfo +from astropy.utils.exceptions import AstropyWarning + +from .core import Unit, UnitBase, dimensionless_unscaled, get_current_unit_registry +from .errors import UnitConversionError, UnitsError, UnitTypeError +from .format import Base, Latex +from .quantity_helper import can_have_arbitrary_unit, check_output, converters_and_unit +from .quantity_helper.function_helpers import ( + DISPATCHED_FUNCTIONS, + FUNCTION_HELPERS, + SUBCLASS_SAFE_FUNCTIONS, + UNIT_FROM_LIKE_ARG, + UNSUPPORTED_FUNCTIONS, +) +from .structured import StructuredUnit, _structured_unit_like_dtype +from .typing import QuantityLike +from .utils import is_effectively_unity + +__all__ = [ + "Quantity", + "QuantityInfo", + "QuantityInfoBase", + "SpecificTypeQuantity", + "allclose", + "isclose", +] + + +# We don't want to run doctests in the docstrings we inherit from Numpy +__doctest_skip__ = ["Quantity.*"] + +_UNIT_NOT_INITIALISED = "(Unit not initialised)" +_UFUNCS_FILTER_WARNINGS = {np.arcsin, np.arccos, np.arccosh, np.arctanh} + + +class Conf(_config.ConfigNamespace): + """ + Configuration parameters for Quantity. + """ + + latex_array_threshold = _config.ConfigItem( + 100, + "The maximum size an array Quantity can be before its LaTeX " + 'representation for IPython gets "summarized" (meaning only the first ' + 'and last few elements are shown with "..." between). Setting this to a ' + "negative number means that the value will instead be whatever numpy " + "gets from get_printoptions.", + ) + + +conf = Conf() + + +class QuantityIterator: + """ + Flat iterator object to iterate over Quantities. + + A `QuantityIterator` iterator is returned by ``q.flat`` for any Quantity + ``q``. It allows iterating over the array as if it were a 1-D array, + either in a for-loop or by calling its `next` method. + + Iteration is done in C-contiguous style, with the last index varying the + fastest. The iterator can also be indexed using basic slicing or + advanced indexing. + + See Also + -------- + Quantity.flatten : Returns a flattened copy of an array. + + Notes + ----- + `QuantityIterator` is inspired by `~numpy.ma.core.MaskedIterator`. It + is not exported by the `~astropy.units` module. Instead of + instantiating a `QuantityIterator` directly, use `Quantity.flat`. + """ + + def __init__(self, q): + self._quantity = q + self._dataiter = q.view(np.ndarray).flat + + def __iter__(self): + return self + + def __getitem__(self, index): + out = self._dataiter.__getitem__(index) + # For single elements, ndarray.flat.__getitem__ returns scalars; these + # need a new view as a Quantity. + if isinstance(out, type(self._quantity)): + return out + else: + return self._quantity._new_view(out) + + def __setitem__(self, index, value): + self._dataiter[index] = self._quantity._to_own_unit(value) + + def __next__(self): + out = next(self._dataiter) + # ndarray.flat._dataiter returns scalars, so need a view as a Quantity. + return self._quantity._new_view(out) + + next = __next__ + + def __len__(self): + return len(self._dataiter) + + # Properties and methods to match `numpy.ndarray.flatiter` + + @property + def base(self): + """A reference to the array that is iterated over.""" + return self._quantity + + @property + def coords(self): + """An N-dimensional tuple of current coordinates.""" + return self._dataiter.coords + + @property + def index(self): + """Current flat index into the array.""" + return self._dataiter.index + + def copy(self): + """Get a copy of the iterator as a 1-D array.""" + return self._quantity.flatten() + + +class QuantityInfoBase(ParentDtypeInfo): + # This is on a base class rather than QuantityInfo directly, so that + # it can be used for EarthLocationInfo yet make clear that that class + # should not be considered a typical Quantity subclass by Table. + attrs_from_parent = {"dtype", "unit"} # dtype and unit taken from parent + _supports_indexing = True + + @staticmethod + def default_format(val): + return f"{val.value}" + + @staticmethod + def possible_string_format_functions(format_): + """Iterate through possible string-derived format functions. + + A string can either be a format specifier for the format built-in, + a new-style format string, or an old-style format string. + + This method is overridden in order to suppress printing the unit + in each row since it is already at the top in the column header. + """ + yield lambda format_, val: format(val.value, format_) + yield lambda format_, val: format_.format(val.value) + yield lambda format_, val: format_ % val.value + + +class QuantityInfo(QuantityInfoBase): + """ + Container for meta information like name, description, format. This is + required when the object is used as a mixin column within a table, but can + be used as a general way to store meta information. + """ + + _represent_as_dict_attrs: tuple[str, ...] = ("value", "unit") + _construct_from_dict_args = ["value"] + _represent_as_dict_primary_data = "value" + + def new_like(self, cols, length, metadata_conflicts="warn", name=None): + """ + Return a new Quantity instance which is consistent with the + input ``cols`` and has ``length`` rows. + + This is intended for creating an empty column object whose elements can + be set in-place for table operations like join or vstack. + + Parameters + ---------- + cols : list + List of input columns + length : int + Length of the output column object + metadata_conflicts : str ('warn'|'error'|'silent') + How to handle metadata conflicts + name : str or None + Output column name + + Returns + ------- + col : `~astropy.units.Quantity` (or subclass) + Empty instance of this class consistent with ``cols`` + + """ + # Get merged info attributes like shape, dtype, format, description, etc. + attrs = self.merge_cols_attributes( + cols, metadata_conflicts, name, ("meta", "format", "description") + ) + + # Make an empty quantity using the unit of the last one. + shape = (length,) + attrs.pop("shape") + dtype = attrs.pop("dtype") + # Use zeros so we do not get problems for Quantity subclasses such + # as Longitude and Latitude, which cannot take arbitrary values. + data = np.zeros(shape=shape, dtype=dtype) + # Get arguments needed to reconstruct class + map = { + key: (data if key == "value" else getattr(cols[-1], key)) + for key in self._represent_as_dict_attrs + } + map["copy"] = None + out = self._construct_from_dict(map) + + # Set remaining info attributes + for attr, value in attrs.items(): + setattr(out.info, attr, value) + + return out + + def get_sortable_arrays(self): + """ + Return a list of arrays which can be lexically sorted to represent + the order of the parent column. + + For Quantity this is just the quantity itself. + + + Returns + ------- + arrays : list of ndarray + """ + return [self._parent] + + +# For parsing a string with a number or list of numbers and a unit. The first +# part of the regex string matches any integer/float; the second parts adds +# possible trailing .+-, which will break the float function in +# _parse_quantity_string and ensure things like 1.2.3deg won't work. +NUM: Final = r""" + [+-]? + ((\d+\.?\d*)|(\.\d+)|([nN][aA][nN])| + ([iI][nN][fF]([iI][nN][iI][tT][yY]){0,1})) + ([eE][+-]?\d+)? + [.+-]? +""" +# List of numbers separated by "," or whitespace. +VECTOR_COMMA: Final = rf""" + \[\s* + {NUM} + (?: (\s*,\s*){NUM})* + (\s*,\s*)? + \s*\] +""" +VECTOR_WSPACE: Final = rf""" + \[\s* + {NUM} + (?: (\s+){NUM})* + \s*\] +""" +VECTOR_1D: Final = rf"{VECTOR_COMMA} | {VECTOR_WSPACE}" +NUMBER_PATTERN: Final = re.compile(rf"\s*(?:{NUM}|{VECTOR_1D})\s*", re.VERBOSE) + + +def _parse_quantity_string(string: str) -> tuple[float | list[float], Unit]: + """Parse a string as a number or list of numbers. + + Returns a tuple of value (float or array) and unit. + Raises if not possible. + """ + v = re.match(NUMBER_PATTERN, string) + items = v.group().replace(",", " ").strip().strip("[]").split() + value = [float(a) for a in items] if "[" in string else float(items[0]) + unit = Unit(unit_str) if (unit_str := v.string[v.end() :].strip()) else None + return value, unit + + +class Quantity(np.ndarray): + """A `~astropy.units.Quantity` represents a number with some associated unit. + + See also: https://docs.astropy.org/en/stable/units/quantity.html + + Parameters + ---------- + value : number, `~numpy.ndarray`, `~astropy.units.Quantity` (sequence), or str + The numerical value of this quantity in the units given by unit. If a + `Quantity` or sequence of them (or any other valid object with a + ``unit`` attribute), creates a new `Quantity` object, converting to + `unit` units as needed. If a string, it is converted to a number or + `Quantity`, depending on whether a unit is present. + + unit : unit-like + An object that represents the unit associated with the input value. + Must be an `~astropy.units.UnitBase` object or a string parseable by + the :mod:`~astropy.units` package. + + dtype : ~numpy.dtype, optional + The dtype of the resulting Numpy array or scalar that will + hold the value. If not provided, it is determined from the input, + except that any integer and (non-Quantity) object inputs are converted + to float by default. + If `None`, the normal `numpy.dtype` introspection is used, e.g. + preventing upcasting of integers. + + copy : bool, optional + If `True` (default), then the value is copied. Otherwise, a copy will + only be made if ``__array__`` returns a copy, if value is a nested + sequence, or if a copy is needed to satisfy an explicitly given + ``dtype``. (The `False` option is intended mostly for internal use, + to speed up initialization where a copy is known to have been made. + Use with care.) + + order : {'C', 'F', 'A'}, optional + Specify the order of the array. As in `~numpy.array`. This parameter + is ignored if the input is a `Quantity` and ``copy=False``. + + subok : bool, optional + If `False` (default), the returned array will be forced to be a + `Quantity`. Otherwise, `Quantity` subclasses will be passed through, + or a subclass appropriate for the unit will be used (such as + `~astropy.units.Dex` for ``u.dex(u.AA)``). + + ndmin : int, optional + Specifies the minimum number of dimensions that the resulting array + should have. Ones will be prepended to the shape as needed to meet + this requirement. This parameter is ignored if the input is a + `Quantity` and ``copy=False``. + + Raises + ------ + TypeError + If the value provided is not a Python numeric type. + TypeError + If the unit provided is not either a :class:`~astropy.units.Unit` + object or a parseable string unit. + + Notes + ----- + Quantities can also be created by multiplying a number or array with a + :class:`~astropy.units.Unit`. See https://docs.astropy.org/en/latest/units/ + + Unless the ``dtype`` argument is explicitly specified, integer + or (non-Quantity) object inputs are converted to `float` by default. + """ + + # Need to set a class-level default for _equivalencies, or + # Constants can not initialize properly + _equivalencies = [] + + # Default unit for initialization; can be overridden by subclasses, + # possibly to `None` to indicate there is no default unit. + _default_unit = dimensionless_unscaled + + # Ensures views have an undefined unit. + _unit = None + + __array_priority__ = 10000 + + def __class_getitem__(cls, unit_shape_dtype): + """Quantity Type Hints. + + Unit-aware type hints are ``Annotated`` objects that encode the class, + the unit, and possibly shape and dtype information, depending on the + python and :mod:`numpy` versions. + + Schematically, ``Annotated[cls[shape, dtype], unit]`` + + As a classmethod, the type is the class, ie ``Quantity`` + produces an ``Annotated[Quantity, ...]`` while a subclass + like :class:`~astropy.coordinates.Angle` returns + ``Annotated[Angle, ...]``. + + Parameters + ---------- + unit_shape_dtype : :class:`~astropy.units.UnitBase`, str, `~astropy.units.PhysicalType`, or tuple + Unit specification, can be the physical type (ie str or class). + If tuple, then the first element is the unit specification + and all other elements are for `numpy.ndarray` type annotations. + Whether they are included depends on the python and :mod:`numpy` + versions. + + Returns + ------- + `typing.Annotated`, `astropy.units.Unit`, or `astropy.units.PhysicalType` + Return type in this preference order: + * `typing.Annotated` + * `astropy.units.Unit` or `astropy.units.PhysicalType` + + Raises + ------ + TypeError + If the unit/physical_type annotation is not Unit-like or + PhysicalType-like. + + Examples + -------- + Create a unit-aware Quantity type annotation + + >>> Quantity[Unit("s")] + Annotated[Quantity, Unit("s")] + + See Also + -------- + `~astropy.units.quantity_input` + Use annotations for unit checks on function arguments and results. + + Notes + ----- + |Quantity| types are also static-type compatible. + """ + from typing import Annotated + + # process whether [unit] or [unit, shape, ptype] + if isinstance(unit_shape_dtype, tuple): # unit, shape, dtype + target = unit_shape_dtype[0] + shape_dtype = unit_shape_dtype[1:] + else: # just unit + target = unit_shape_dtype + shape_dtype = () + + # Allowed unit/physical types. Errors if neither. + try: + unit = Unit(target) + except (TypeError, ValueError): + from astropy.units.physical import get_physical_type + + try: + unit = get_physical_type(target) + except (TypeError, ValueError, KeyError): # KeyError for Enum + raise TypeError( + "unit annotation is not a Unit or PhysicalType" + ) from None + + # Quantity does not (yet) properly extend the NumPy generics types, + # introduced in numpy v1.22+, instead just including the unit info as + # metadata using Annotated. + # TODO: ensure we do interact with NDArray.__class_getitem__. + return Annotated[cls, unit] + + def __new__( + cls: type[Self], + value: QuantityLike, + unit=None, + dtype=np.inexact, + copy=True, + order=None, + subok=False, + ndmin=0, + ) -> Self: + if unit is not None: + # convert unit first, to avoid multiple string->unit conversions + unit = Unit(unit) + + # inexact -> upcast to float dtype + float_default = dtype is np.inexact + if float_default: + dtype = None + + # optimize speed for Quantity with no dtype given, copy=None + if isinstance(value, Quantity): + if unit is not None and unit is not value.unit: + value = value.to(unit) + # the above already makes a copy (with float dtype) + copy = None + + if type(value) is not cls and not (subok and isinstance(value, cls)): + value = value.view(cls) + + if float_default and value.dtype.kind in "iu": + dtype = float + + return np.array( + value, dtype=dtype, copy=copy, order=order, subok=True, ndmin=ndmin + ) + + # Maybe str, or list/tuple of Quantity? If so, this may set value_unit. + # To ensure array remains fast, we short-circuit it. + value_unit = None + if not isinstance(value, np.ndarray): + if isinstance(value, str): + # A string with a number or list of numbers and possible unit? + try: + value, value_unit = _parse_quantity_string(value) + except Exception as exc: + # Parsing of values and units can lead to same class of + # exception (e.g., ValueError). Pass on units related ones. + if "unit" in str(exc).lower(): + raise + msg = f'Cannot parse "{value}" as a {cls.__name__}.' + if "[" not in value: + msg += " It does not start with a number." + raise TypeError(msg) + + if unit is None: + unit = value_unit # signal no conversion needed below. + + elif isinstance(value, (list, tuple)) and len(value) > 0: + if all(isinstance(v, Quantity) for v in value): + # If a list/tuple contains only quantities, stack them, + # which also converts them to the same unit. + value = np.stack(value) + copy = False + + elif ( + dtype is None + and not hasattr(value, "dtype") + and isinstance(unit, StructuredUnit) + ): + # Special case for list/tuple of values and a structured unit: + # ``np.array(value, dtype=None)`` would treat tuples as lower + # levels of the array, rather than as elements of a structured + # array, so we use the structure of the unit to help infer the + # structured dtype of the value. + dtype = unit._recursively_get_dtype(value) + + using_default_unit = False + if value_unit is None: + # If the value has a `unit` attribute and if not None + # (for Columns with uninitialized unit), treat it like a quantity. + value_unit = getattr(value, "unit", None) + if value_unit is None: + # Default to dimensionless for no (initialized) unit attribute. + if unit is None: + using_default_unit = True + unit = cls._default_unit + value_unit = unit # signal below that no conversion is needed + else: + try: + value_unit = Unit(value_unit) + except Exception as exc: + raise TypeError( + f"The unit attribute {value.unit!r} of the input could " + "not be parsed as an astropy Unit." + ) from exc + + if unit is None: + unit = value_unit + elif unit is not value_unit: + copy = None # copy will be made in conversion at end + + value = np.array( + value, dtype=dtype, copy=copy, order=order, subok=True, ndmin=ndmin + ) + + # For no-user-input unit, make sure the constructed unit matches the + # structure of the data. + if using_default_unit and value.dtype.names is not None: + unit = value_unit = _structured_unit_like_dtype(value_unit, value.dtype) + + # check that array contains numbers or long int objects + if value.dtype.kind in "OSU" and not ( + value.dtype.kind == "O" and isinstance(value.item(0), numbers.Number) + ): + raise TypeError("The value must be a valid Python or Numpy numeric type.") + + # by default, cast any integer, boolean, etc., to float + if float_default and value.dtype.kind in "iuO": + value = value.astype(float) + + # if we allow subclasses, allow a class from the unit. + if subok: + qcls = getattr(unit, "_quantity_class", cls) + if issubclass(qcls, cls): + cls = qcls + + value = value.view(cls) + value._set_unit(value_unit) + if unit is value_unit: + return value + else: + # here we had non-Quantity input that had a "unit" attribute + # with a unit different from the desired one. So, convert. + return value.to(unit) + + def __array_finalize__(self, obj): + super().__array_finalize__(obj) + + # If we're a new object or viewing an ndarray, nothing has to be done. + if obj is None or obj.__class__ is np.ndarray: + return + + # Copy over the unit and possibly info. Note that the only way the + # unit can already be set is if one enters via _new_view(), where the + # unit is often different from that of self, and where propagation of + # info is not always desirable. + if self._unit is None: + unit = getattr(obj, "_unit", None) + if unit is not None: + self._set_unit(unit) + + # Copy info if the original had `info` defined. Because of the way the + # DataInfo works, `'info' in obj.__dict__` is False until the + # `info` attribute is accessed or set. + if "info" in obj.__dict__: + self.info = obj.info + + def __array_wrap__(self, obj, context=None, return_scalar=False): + if context is None: + # Methods like .squeeze() created a new `ndarray` and then call + # __array_wrap__ to turn the array into self's subclass. + return self._new_view(obj) + + raise NotImplementedError( + "__array_wrap__ should not be used with a context any more since all " + "use should go through array_function. Please raise an issue on " + "https://github.com/astropy/astropy" + ) + + def __array_ufunc__(self, function, method, *inputs, **kwargs): + """Wrap numpy ufuncs, taking care of units. + + Parameters + ---------- + function : callable + ufunc to wrap. + method : str + Ufunc method: ``__call__``, ``at``, ``reduce``, etc. + inputs : tuple + Input arrays. + kwargs : keyword arguments + As passed on, with ``out`` containing possible quantity output. + + Returns + ------- + result : `~astropy.units.Quantity` or `NotImplemented` + Results of the ufunc, with the unit set properly. + """ + # Determine required conversion functions -- to bring the unit of the + # input to that expected (e.g., radian for np.sin), or to get + # consistent units between two inputs (e.g., in np.add) -- + # and the unit of the result (or tuple of units for nout > 1). + try: + converters, unit = converters_and_unit(function, method, *inputs) + + out = kwargs.get("out") + # Avoid loop back by turning any Quantity output into array views. + if out is not None: + # If pre-allocated output is used, check it is suitable. + # This also returns array view, to ensure we don't loop back. + if function.nout == 1: + out = out[0] + out_array = check_output(out, unit, inputs, function=function) + # Ensure output argument remains a tuple. + kwargs["out"] = (out_array,) if function.nout == 1 else out_array + + if method == "reduce" and "initial" in kwargs and unit is not None: + # Special-case for initial argument for reductions like + # np.add.reduce. This should be converted to the output unit as + # well, which is typically the same as the input unit (but can + # in principle be different: unitless for np.equal, radian + # for np.arctan2, though those are not necessarily useful!) + kwargs["initial"] = self._to_own_unit( + kwargs["initial"], check_precision=False, unit=unit + ) + + # Same for inputs, but here also convert if necessary. + arrays = [] + for input_, converter in zip(inputs, converters): + input_ = getattr(input_, "value", input_) + arrays.append(converter(input_) if converter else input_) + + # Call our superclass's __array_ufunc__ + result = super().__array_ufunc__(function, method, *arrays, **kwargs) + # If unit is None, a plain array is expected (e.g., comparisons), which + # means we're done. + # We're also done if the result was None (for method 'at') or + # NotImplemented, which can happen if other inputs/outputs override + # __array_ufunc__; hopefully, they can then deal with us. + if unit is None or result is None or result is NotImplemented: + return result + + return self._result_as_quantity(result, unit, out) + + except (TypeError, ValueError, AttributeError) as e: + out_normalized = kwargs.get("out", ()) + inputs_and_outputs = inputs + out_normalized + ignored_ufunc = ( + None, + np.ndarray.__array_ufunc__, + type(self).__array_ufunc__, + ) + if not all( + getattr(type(io), "__array_ufunc__", None) in ignored_ufunc + for io in inputs_and_outputs + ): + return NotImplemented + else: + raise e + + def _result_as_quantity(self, result, unit, out): + """Turn result into a quantity with the given unit. + + If no output is given, it will take a view of the array as a quantity, + and set the unit. If output is given, those should be quantity views + of the result arrays, and the function will just set the unit. + + Parameters + ---------- + result : ndarray or tuple thereof + Array(s) which need to be turned into quantity. + unit : `~astropy.units.Unit` + Unit for the quantities to be returned (or `None` if the result + should not be a quantity). Should be tuple if result is a tuple. + out : `~astropy.units.Quantity` or None + Possible output quantity. Should be `None` or a tuple if result + is a tuple. + + Returns + ------- + out : `~astropy.units.Quantity` + With units set. + """ + if isinstance(result, (tuple, list)): + if out is None: + out = (None,) * len(result) + # Some np.linalg functions return namedtuple, which is handy to access + # elements by name, but cannot be directly initialized with an iterator. + result_cls = getattr(result, "_make", result.__class__) + return result_cls( + self._result_as_quantity(result_, unit_, out_) + for (result_, unit_, out_) in zip(result, unit, out) + ) + + if out is None: + # View the result array as a Quantity with the proper unit. + return ( + result + if unit is None + else self._new_view(result, unit, propagate_info=False) + ) + + elif isinstance(out, Quantity): + # For given Quantity output, just set the unit. We know the unit + # is not None and the output is of the correct Quantity subclass, + # as it was passed through check_output. + # (We cannot do this unconditionally, though, since it is possible + # for out to be ndarray and the unit to be dimensionless.) + out._set_unit(unit) + + return out + + def __quantity_subclass__(self, unit): + """ + Overridden by subclasses to change what kind of view is + created based on the output unit of an operation. + + Parameters + ---------- + unit : UnitBase + The unit for which the appropriate class should be returned + + Returns + ------- + tuple : + - `~astropy.units.Quantity` subclass + - bool: True if subclasses of the given class are ok + """ + return Quantity, True + + def _new_view(self, obj=None, unit=None, propagate_info=True): + """Create a Quantity view of some array-like input, and set the unit. + + By default, return a view of ``obj`` of the same class as ``self`` and + with the same unit. Subclasses can override the type of class for a + given unit using ``__quantity_subclass__``, and can ensure properties + other than the unit are copied using ``__array_finalize__``. + + If the given unit defines a ``_quantity_class`` of which ``self`` + is not an instance, a view using this class is taken. + + Parameters + ---------- + obj : ndarray or scalar, optional + The array to create a view of. If obj is a numpy or python scalar, + it will be converted to an array scalar. By default, ``self`` + is converted. + + unit : unit-like, optional + The unit of the resulting object. It is used to select a + subclass, and explicitly assigned to the view if given. + If not given, the subclass and unit will be that of ``self``. + + propagate_info : bool, optional + Whether to transfer ``info`` if present. Default: `True`, as + appropriate for, e.g., unit conversions or slicing, where the + nature of the object does not change. + + Returns + ------- + view : `~astropy.units.Quantity` subclass + + """ + # Determine the unit and quantity subclass that we need for the view. + if unit is None: + unit = self.unit + quantity_subclass = self.__class__ + elif unit is self.unit and self.__class__ is Quantity: + # The second part is because we should not presume what other + # classes want to do for the same unit. E.g., Constant will + # always want to fall back to Quantity, and relies on going + # through `__quantity_subclass__`. + quantity_subclass = Quantity + else: + unit = Unit(unit) + quantity_subclass = getattr(unit, "_quantity_class", Quantity) + if isinstance(self, quantity_subclass): + quantity_subclass, subok = self.__quantity_subclass__(unit) + if subok: + quantity_subclass = self.__class__ + + # We only want to propagate information from ``self`` to our new view, + # so obj should be a regular array. By using ``np.array``, we also + # convert python and numpy scalars, which cannot be viewed as arrays + # and thus not as Quantity either, to zero-dimensional arrays. + # (These are turned back into scalar in `.value`) + # Note that for an ndarray input, the np.array call takes only double + # ``obj.__class is np.ndarray``. So, not worth special-casing. + if obj is None: + obj = self.view(np.ndarray) + else: + obj = np.asanyarray(obj) + + # Take the view, set the unit, and update possible other properties + # such as ``info``, ``wrap_angle`` in `Longitude`, etc. + view = obj.view(quantity_subclass) + view._set_unit(unit) + view.__array_finalize__(self) + if propagate_info and "info" in self.__dict__: + view.info = self.info + return view + + def _set_unit(self, unit): + """Set the unit. + + This is used anywhere the unit is set or modified, i.e., in the + initializer, in ``__imul__`` and ``__itruediv__`` for in-place + multiplication and division by another unit, as well as in + ``__array_finalize__`` for wrapping up views. For Quantity, it just + sets the unit, but subclasses can override it to check that, e.g., + a unit is consistent. + """ + if not isinstance(unit, UnitBase): + if isinstance(self._unit, StructuredUnit) or isinstance( + unit, StructuredUnit + ): + unit = StructuredUnit(unit, self.dtype) + else: + # Trying to go through a string ensures that, e.g., Magnitudes with + # dimensionless physical unit become Quantity with units of mag. + unit = Unit(str(unit), parse_strict="silent") + if not isinstance(unit, (UnitBase, StructuredUnit)): + raise UnitTypeError( + f"{self.__class__.__name__} instances require normal units, " + f"not {unit.__class__} instances." + ) + + self._unit = unit + + def __deepcopy__(self, memo): + # If we don't define this, ``copy.deepcopy(quantity)`` will + # return a bare Numpy array. + return self.copy() + + def __reduce__(self): + # patch to pickle Quantity objects (ndarray subclasses), see + # http://www.mail-archive.com/numpy-discussion@scipy.org/msg02446.html + + object_state = list(super().__reduce__()) + object_state[2] = (object_state[2], self.__dict__) + return tuple(object_state) + + def __setstate__(self, state): + # patch to unpickle Quantity objects (ndarray subclasses), see + # http://www.mail-archive.com/numpy-discussion@scipy.org/msg02446.html + + nd_state, own_state = state + super().__setstate__(nd_state) + self.__dict__.update(own_state) + + info: QuantityInfoBase = QuantityInfo() + + def _to_value(self, unit, equivalencies=[]): + """Helper method for to and to_value.""" + if equivalencies == []: + equivalencies = self._equivalencies + if not self.dtype.names or isinstance(self.unit, StructuredUnit): + # Standard path, let unit to do work. + return self.unit.to( + unit, self.view(np.ndarray), equivalencies=equivalencies + ) + + else: + # The .to() method of a simple unit cannot convert a structured + # dtype, so we work around it, by recursing. + # TODO: deprecate this? + # Convert simple to Structured on initialization? + result = np.empty_like(self.view(np.ndarray)) + for name in self.dtype.names: + result[name] = self[name]._to_value(unit, equivalencies) + return result + + def to(self, unit, equivalencies=[], copy=True): + """ + Return a new `~astropy.units.Quantity` object with the specified unit. + + Parameters + ---------- + unit : unit-like + An object that represents the unit to convert to. Must be + an `~astropy.units.UnitBase` object or a string parseable + by the `~astropy.units` package. + + equivalencies : list of tuple + A list of equivalence pairs to try if the units are not + directly convertible. See :ref:`astropy:unit_equivalencies`. + If not provided or ``[]``, class default equivalencies will be used + (none for `~astropy.units.Quantity`, but may be set for subclasses) + If `None`, no equivalencies will be applied at all, not even any + set globally or within a context. + + copy : bool, optional + If `True` (default), then the value is copied. Otherwise, a copy + will only be made if necessary. + + See Also + -------- + to_value : get the numerical value in a given unit. + """ + # We don't use `to_value` below since we always want to make a copy + # and don't want to slow down this method (esp. the scalar case). + unit = Unit(unit) + if copy: + # Avoid using to_value to ensure that we make a copy. We also + # don't want to slow down this method (esp. the scalar case). + value = self._to_value(unit, equivalencies) + else: + # to_value only copies if necessary + value = self.to_value(unit, equivalencies) + return self._new_view(value, unit) + + def to_value(self, unit=None, equivalencies=[]): + """ + The numerical value, possibly in a different unit. + + Parameters + ---------- + unit : unit-like, optional + The unit in which the value should be given. If not given or `None`, + use the current unit. + + equivalencies : list of tuple, optional + A list of equivalence pairs to try if the units are not directly + convertible (see :ref:`astropy:unit_equivalencies`). If not provided + or ``[]``, class default equivalencies will be used (none for + `~astropy.units.Quantity`, but may be set for subclasses). + If `None`, no equivalencies will be applied at all, not even any + set globally or within a context. + + Returns + ------- + value : ndarray or scalar + The value in the units specified. For arrays, this will be a view + of the data if no unit conversion was necessary. + + See Also + -------- + to : Get a new instance in a different unit. + """ + if unit is None or unit is self.unit: + value = self.view(np.ndarray) + elif not self.dtype.names: + # For non-structured, we attempt a short-cut, where we just get + # the scale. If that is 1, we do not have to do anything. + unit = Unit(unit) + # We want a view if the unit does not change. One could check + # with "==", but that calculates the scale that we need anyway. + # TODO: would be better for `unit.to` to have an in-place flag. + try: + scale = self.unit._to(unit) + except Exception: + # Short-cut failed; try default (maybe equivalencies help). + value = self._to_value(unit, equivalencies) + else: + value = self.view(np.ndarray) + if not is_effectively_unity(scale): + # not in-place! + value = value * scale + else: + # For structured arrays, we go the default route. + value = self._to_value(unit, equivalencies) + + # Index with empty tuple to decay array scalars in to numpy scalars. + return value if value.shape else value[()] + + value = property( + to_value, + doc="""The numerical value of this instance. + + See also + -------- + to_value : Get the numerical value in a given unit. + """, + ) + + @property + def unit(self): + """ + A `~astropy.units.UnitBase` object representing the unit of this + quantity. + """ + return self._unit + + @property + def equivalencies(self): + """ + A list of equivalencies that will be applied by default during + unit conversions. + """ + return self._equivalencies + + def _recursively_apply(self, func): + """Apply function recursively to every field. + + Returns a copy with the result. + """ + result = np.empty_like(self) + result_value = result.view(np.ndarray) + result_unit = () + for name in self.dtype.names: + part = func(self[name]) + result_value[name] = part.value + result_unit += (part.unit,) + + result._set_unit(result_unit) + return result + + @property + def si(self): + """ + Returns a copy of the current `Quantity` instance with SI units. The + value of the resulting object will be scaled. + """ + if self.dtype.names: + return self._recursively_apply(operator.attrgetter("si")) + si_unit = self.unit.si + return self._new_view(self.value * si_unit.scale, si_unit / si_unit.scale) + + @property + def cgs(self): + """ + Returns a copy of the current `Quantity` instance with CGS units. The + value of the resulting object will be scaled. + """ + if self.dtype.names: + return self._recursively_apply(operator.attrgetter("cgs")) + cgs_unit = self.unit.cgs + return self._new_view(self.value * cgs_unit.scale, cgs_unit / cgs_unit.scale) + + @property + def isscalar(self): + """ + True if the `value` of this quantity is a scalar, or False if it + is an array-like object. + + .. note:: + This is subtly different from `numpy.isscalar` in that + `numpy.isscalar` returns False for a zero-dimensional array + (e.g. ``np.array(1)``), while this is True for quantities, + since quantities cannot represent true numpy scalars. + """ + return not self.shape + + # This flag controls whether convenience conversion members, such + # as `q.m` equivalent to `q.to_value(u.m)` are available. This is + # not turned on on Quantity itself, but is on some subclasses of + # Quantity, such as `astropy.coordinates.Angle`. + _include_easy_conversion_members = False + + def __dir__(self): + """ + Quantities are able to directly convert to other units that + have the same physical type. This function is implemented in + order to make autocompletion still work correctly in IPython. + """ + if not self._include_easy_conversion_members: + return super().__dir__() + + dir_values = set(super().__dir__()) + equivalencies = Unit._normalize_equivalencies(self.equivalencies) + for equivalent in self.unit._get_units_with_same_physical_type(equivalencies): + dir_values.update(equivalent.names) + return sorted(dir_values) + + def __getattr__(self, attr): + """ + Quantities are able to directly convert to other units that + have the same physical type. + """ + if not self._include_easy_conversion_members: + raise AttributeError( + f"'{self.__class__.__name__}' object has no '{attr}' member" + ) + + def get_virtual_unit_attribute(): + registry = get_current_unit_registry().registry + to_unit = registry.get(attr, None) + if to_unit is None: + return None + + try: + return self.unit.to( + to_unit, self.value, equivalencies=self.equivalencies + ) + except UnitsError: + return None + + value = get_virtual_unit_attribute() + + if value is None: + raise AttributeError( + f"{self.__class__.__name__} instance has no attribute '{attr}'" + ) + else: + return value + + # Equality needs to be handled explicitly as ndarray.__eq__ gives + # DeprecationWarnings on any error, which is distracting, and does not + # deal well with structured arrays (nor does the ufunc). + def __eq__(self, other): + try: + other_value = self._to_own_unit(other) + except UnitsError: + return False + except Exception: + return NotImplemented + return self.value.__eq__(other_value) + + def __ne__(self, other): + try: + other_value = self._to_own_unit(other) + except UnitsError: + return True + except Exception: + return NotImplemented + return self.value.__ne__(other_value) + + # Unit conversion operator (<<). + def __lshift__(self, other): + try: + other = Unit(other, parse_strict="silent") + except UnitTypeError: + return NotImplemented + + return self.__class__(self, other, copy=False, subok=True) + + def __ilshift__(self, other): + try: + other = Unit(other, parse_strict="silent") + except UnitTypeError: + return NotImplemented # try other.__rlshift__(self) + + try: + factor = self.unit._to(other) + except UnitConversionError: # incompatible, or requires an Equivalency + return NotImplemented + except AttributeError: # StructuredUnit does not have `_to` + # In principle, in-place might be possible. + return NotImplemented + + view = self.view(np.ndarray) + try: + view *= factor # operates on view + except TypeError: + # The error is `numpy.core._exceptions._UFuncOutputCastingError`, + # which inherits from `TypeError`. + return NotImplemented + + self._set_unit(other) + return self + + def __rlshift__(self, other): + if not self.isscalar: + return NotImplemented + return Unit(self).__rlshift__(other) + + # Give warning for other >> self, since probably other << self was meant. + def __rrshift__(self, other): + warnings.warn( + ">> is not implemented. Did you mean to convert " + "something to this quantity as a unit using '<<'?", + AstropyWarning, + ) + return NotImplemented + + # Also define __rshift__ and __irshift__ so we override default ndarray + # behaviour, but instead of emitting a warning here, let it be done by + # other (which likely is a unit if this was a mistake). + def __rshift__(self, other): + return NotImplemented + + def __irshift__(self, other): + return NotImplemented + + # Arithmetic operations + def __mul__(self, other): + if isinstance(other, (UnitBase, str)): + try: + return self._new_view( + self.value.copy(), other * self.unit, propagate_info=False + ) + except UnitsError: # let other try to deal with it + return NotImplemented + + return super().__mul__(other) + + def __imul__(self, other): + if isinstance(other, (UnitBase, str)): + self._set_unit(other * self.unit) + return self + + return super().__imul__(other) + + def __rmul__(self, other): + return self.__mul__(other) + + def __truediv__(self, other): + if isinstance(other, (UnitBase, str)): + try: + return self._new_view( + self.value.copy(), self.unit / other, propagate_info=False + ) + except UnitsError: # let other try to deal with it + return NotImplemented + + return super().__truediv__(other) + + def __itruediv__(self, other): + if isinstance(other, (UnitBase, str)): + self._set_unit(self.unit / other) + return self + + return super().__itruediv__(other) + + def __rtruediv__(self, other): + if isinstance(other, (UnitBase, str)): + return self._new_view( + 1.0 / self.value, other / self.unit, propagate_info=False + ) + + return super().__rtruediv__(other) + + def __pow__(self, other): + if isinstance(other, Fraction): + # Avoid getting object arrays by raising the value to a Fraction. + return self._new_view( + self.value ** float(other), self.unit**other, propagate_info=False + ) + + return super().__pow__(other) + + # other overrides of special functions + def __hash__(self): + return hash(self.value) ^ hash(self.unit) + + def __iter__(self): + if self.isscalar: + raise TypeError( + f"'{self.__class__.__name__}' object with a scalar value is not" + " iterable" + ) + return map(self._new_view, self.value) + + def __getitem__(self, key): + if isinstance(key, str) and isinstance(self.unit, StructuredUnit): + return self._new_view( + self.view(np.ndarray)[key], self.unit[key], propagate_info=False + ) + + try: + out = super().__getitem__(key) + except IndexError: + # We want zero-dimensional Quantity objects to behave like scalars, + # so they should raise a TypeError rather than an IndexError. + if self.isscalar: + raise TypeError( + f"'{self.__class__.__name__}' object with a scalar value " + "does not support indexing" + ) + else: + raise + # For single elements, ndarray.__getitem__ returns scalars; these + # need a new view as a Quantity. + if not isinstance(out, np.ndarray): + out = self._new_view(out) + return out + + def __setitem__(self, i, value): + if isinstance(i, str): + # Indexing will cause a different unit, so by doing this in + # two steps we effectively try with the right unit. + self[i][...] = value + return + + # update indices in info if the info property has been accessed + # (in which case 'info' in self.__dict__ is True; this is guaranteed + # to be the case if we're part of a table). + if not self.isscalar and "info" in self.__dict__: + self.info.adjust_indices(i, value, len(self)) + self.view(np.ndarray).__setitem__(i, self._to_own_unit(value)) + + # __contains__ is OK + + def __bool__(self): + """This method raises ValueError, since truthiness of quantities is ambiguous, + especially for logarithmic units and temperatures. Use explicit comparisons. + """ + raise ValueError( + f"{type(self).__name__} truthiness is ambiguous, especially for logarithmic units" + " and temperatures. Use explicit comparisons." + ) + + def __len__(self): + if self.isscalar: + raise TypeError( + f"'{self.__class__.__name__}' object with a scalar value has no len()" + ) + else: + return len(self.value) + + # Numerical types + def __float__(self): + try: + return float(self.to_value(dimensionless_unscaled)) + except (UnitsError, TypeError): + raise TypeError( + "only dimensionless scalar quantities can be " + "converted to Python scalars" + ) + + def __int__(self): + try: + return int(self.to_value(dimensionless_unscaled)) + except (UnitsError, TypeError): + raise TypeError( + "only dimensionless scalar quantities can be " + "converted to Python scalars" + ) + + def __round__(self, ndigits=0): + return self.round(decimals=ndigits) + + def __index__(self): + # for indices, we do not want to mess around with scaling at all, + # so unlike for float, int, we insist here on unscaled dimensionless + if self.unit.is_unity(): + try: + return self.value.__index__() + except AttributeError: + pass + + raise TypeError( + "only integer dimensionless scalar quantities " + "can be converted to a Python index" + ) + + # TODO: we may want to add a hook for dimensionless quantities? + @property + def _unitstr(self): + if self.unit is None: + unitstr = _UNIT_NOT_INITIALISED + else: + unitstr = str(self.unit) + + if unitstr: + unitstr = " " + unitstr + + return unitstr + + def to_string( + self, unit=None, precision=None, format=None, subfmt=None, *, formatter=None + ): + """ + Generate a string representation of the quantity and its unit. + + The behavior of this function can be altered via the + `numpy.set_printoptions` function and its various keywords. The + exception to this is the ``threshold`` keyword, which is controlled via + the ``[units.quantity]`` configuration item ``latex_array_threshold``. + This is treated separately because the numpy default of 1000 is too big + for most browsers to handle. + + Parameters + ---------- + unit : unit-like, optional + Specifies the unit. If not provided, + the unit used to initialize the quantity will be used. + + precision : number, optional + The level of decimal precision. If `None`, or not provided, + it will be determined from NumPy print options. + + format : str, optional + The format of the result. If not provided, an unadorned + string is returned. Supported values are: + + - 'latex': Return a LaTeX-formatted string + + - 'latex_inline': Return a LaTeX-formatted string that uses + negative exponents instead of fractions + + formatter : str, callable, dict, optional + The formatter to use for the value. If a string, it should be a + valid format specifier using Python's mini-language. If a callable, + it will be treated as the default formatter for all values and will + overwrite default Latex formatting for exponential notation and complex + numbers. If a dict, it should map a specific type to a callable to be + directly passed into `numpy.array2string`. If not provided, the default + formatter will be used. + + subfmt : str, optional + Subformat of the result. For the moment, only used for + ``format='latex'`` and ``format='latex_inline'``. Supported + values are: + + - 'inline': Use ``$ ... $`` as delimiters. + + - 'display': Use ``$\\displaystyle ... $`` as delimiters. + + Returns + ------- + str + A string with the contents of this Quantity + """ + if unit is not None and unit != self.unit: + return self.to(unit).to_string( + unit=None, + precision=precision, + format=format, + subfmt=subfmt, + formatter=formatter, + ) + + if format is None and formatter is None and precision is None: + # Use default formatting settings + return f"{self.value}{self._unitstr:s}" + + formats = { + None: None, + "latex": { + None: ("$", "$"), + "inline": ("$", "$"), + "display": (r"$\displaystyle ", r"$"), + }, + } + formats["latex_inline"] = formats["latex"] + + if format not in formats: + raise ValueError(f"Unknown format '{format}'") + + format_spec = formatter if isinstance(formatter, str) else None + + if format is None: + if format_spec is not None: + + def formatter(value): + return builtins.format(value, format_spec) + + if callable(formatter): + formatter = {"all": formatter} + + return ( + np.array2string( + self.value, + precision=precision, + floatmode="fixed", + formatter=formatter, + ) + + self._unitstr + ) + + # else, for the moment we assume format="latex" or "latex_inline". + + # Set the precision if set, otherwise use numpy default + pops = np.get_printoptions() + if format_spec is None: + format_spec = ( + f".{precision if precision is not None else pops['precision']}g" + ) + + # Use default formatters + if formatter is None or isinstance(formatter, str): + # Filter width and alignment operations for latex + # [[fill]align][sign]["z"]["#"]["0"][width][grouping_option]["." precision][type] + format_spec = re.sub( + r"(.*?)([+\- ]?)(\d+)?(,)?(\.\d+)?([a-zA-Z%]+)?$", + r"\2\5\6", + format_spec, + ) + + if self.dtype.kind == "c": # Complex default latex formatter + # Disallow sign operations for the imaginary part + imag_format_spec = re.sub(r"[+\- ]", "", format_spec) + + def formatter(value): + return "({}{}i)".format( + Latex.format_exponential_notation( + value.real, format_spec=format_spec + ), + Latex.format_exponential_notation( + value.imag, format_spec="+" + imag_format_spec + ), + ) + + else: # Float default latex formatter + + def formatter(value): + return Latex.format_exponential_notation( + value, format_spec=format_spec + ) + + if callable(formatter): + formatter = {"all": formatter} + + # The view is needed for the scalar case - self.value might be float. + latex_value = np.array2string( + self.view(np.ndarray), + threshold=( + conf.latex_array_threshold + if conf.latex_array_threshold > -1 + else pops["threshold"] + ), + formatter=formatter, + max_line_width=np.inf, + separator=",~", + ) + + latex_value = latex_value.replace("...", r"\dots") + + # Format unit + # [1:-1] strips the '$' on either side needed for math mode + if self.unit is None: + latex_unit = _UNIT_NOT_INITIALISED + elif format == "latex": + latex_unit = self.unit._repr_latex_()[1:-1] # note this is unicode + elif format == "latex_inline": + latex_unit = self.unit.to_string(format="latex_inline")[1:-1] + + delimiter_left, delimiter_right = formats[format][subfmt] + + # Add a space in front except for super-script units like degrees. + if not latex_unit.removeprefix("\\mathrm{").startswith("{}^"): + latex_unit = rf" \; {latex_unit}" + + return rf"{delimiter_left}{latex_value}{latex_unit}{delimiter_right}" + + def __str__(self): + return self.to_string() + + def __repr__(self): + prefixstr = "<" + self.__class__.__name__ + " " + arrstr = np.array2string( + self.view(np.ndarray), separator=", ", prefix=prefixstr + ) + return f"{prefixstr}{arrstr}{self._unitstr:s}>" + + def _repr_latex_(self): + """ + Generate a latex representation of the quantity and its unit. + + Returns + ------- + lstr + A LaTeX string with the contents of this Quantity + """ + # NOTE: This should change to display format in a future release + return self.to_string(format="latex", subfmt="inline") + + def __format__(self, format_spec): + try: + return self.to_string(format=format_spec) + except ValueError: + # We might have a unit format not implemented in `to_string()`. + if format_spec in Base.registry: + if self.unit is dimensionless_unscaled: + return f"{self.value}" + else: + return f"{self.value} {format(self.unit, format_spec)}" + # Can the value be formatted on its own? + try: + return f"{format(self.value, format_spec)}{self._unitstr:s}" + except ValueError: + # Format the whole thing as a single string. + return format(f"{self.value}{self._unitstr:s}", format_spec) + + def decompose(self, bases: Collection[UnitBase] = ()) -> Self: + """ + Generates a new `Quantity` with the units + decomposed. Decomposed units have only irreducible units in + them (see `astropy.units.UnitBase.decompose`). + + Parameters + ---------- + bases : sequence of `~astropy.units.UnitBase`, optional + The bases to decompose into. When not provided, + decomposes down to any irreducible units. When provided, + the decomposed result will only contain the given units. + This will raises a `~astropy.units.UnitsError` if it's not possible + to do so. + + Returns + ------- + newq : `~astropy.units.Quantity` + A new object equal to this quantity with units decomposed. + """ + return self._decompose(False, bases=bases) + + def _decompose( + self, allowscaledunits: bool = False, bases: Collection[UnitBase] = () + ) -> Self: + """ + Generates a new `Quantity` with the units decomposed. Decomposed + units have only irreducible units in them (see + `astropy.units.UnitBase.decompose`). + + Parameters + ---------- + allowscaledunits : bool + If True, the resulting `Quantity` may have a scale factor + associated with it. If False, any scaling in the unit will + be subsumed into the value of the resulting `Quantity` + + bases : sequence of UnitBase, optional + The bases to decompose into. When not provided, + decomposes down to any irreducible units. When provided, + the decomposed result will only contain the given units. + This will raises a `~astropy.units.UnitsError` if it's not possible + to do so. + + Returns + ------- + newq : `~astropy.units.Quantity` + A new object equal to this quantity with units decomposed. + + """ + new_unit = self.unit.decompose(bases=bases) + + # Be careful here because self.value usually is a view of self; + # be sure that the original value is not being modified. + if not allowscaledunits and hasattr(new_unit, "scale"): + new_value = self.value * new_unit.scale + new_unit = new_unit / new_unit.scale + return self._new_view(new_value, new_unit) + else: + return self._new_view(self.copy(), new_unit) + + # These functions need to be overridden to take into account the units + # Array conversion + # https://numpy.org/doc/stable/reference/arrays.ndarray.html#array-conversion + + def item(self, *args): + """Copy an element of an array to a scalar Quantity and return it. + + Like :meth:`~numpy.ndarray.item` except that it always + returns a `Quantity`, not a Python scalar. + + """ + return self._new_view(super().item(*args)) + + def tolist(self): + raise NotImplementedError( + "cannot make a list of Quantities. Get list of values with" + " q.value.tolist()." + ) + + def _to_own_unit(self, value, check_precision=True, *, unit=None): + """Convert value to one's own unit (or that given). + + Here, non-quantities are treated as dimensionless, and care is taken + for values of 0, infinity or nan, which are allowed to have any unit. + + Parameters + ---------- + value : anything convertible to `~astropy.units.Quantity` + The value to be converted to the requested unit. + check_precision : bool + Whether to forbid conversion of float to integer if that changes + the input number. Default: `True`. + unit : `~astropy.units.Unit` or None + The unit to convert to. By default, the unit of ``self``. + + Returns + ------- + value : number or `~numpy.ndarray` + In the requested units. + + """ + if unit is None: + unit = self.unit + try: + _value = value.to_value(unit) + except AttributeError: + # We're not a Quantity. + # First remove two special cases (with a fast test): + # 1) Maybe masked printing? MaskedArray with quantities does not + # work very well, but no reason to break even repr and str. + # 2) np.ma.masked? useful if we're a MaskedQuantity. + if value is np.ma.masked or ( + value is np.ma.masked_print_option and self.dtype.kind == "O" + ): + return value + # Now, let's try a more general conversion. + # Plain arrays will be converted to dimensionless in the process, + # but anything with a unit attribute will use that. + try: + as_quantity = Quantity(value) + _value = as_quantity.to_value(unit) + except UnitsError: + # last chance: if this was not something with a unit + # and is all 0, inf, or nan, we treat it as arbitrary unit. + if not hasattr(value, "unit") and can_have_arbitrary_unit( + as_quantity.value + ): + _value = as_quantity.value + else: + raise + + if self.dtype.kind == "i" and check_precision: + # If, e.g., we are casting float to int, we want to fail if + # precision is lost, but let things pass if it works. + _value = np.array(_value, copy=None, subok=True) + if not np.can_cast(_value.dtype, self.dtype): + self_dtype_array = np.array(_value, self.dtype, subok=True) + if not np.all((self_dtype_array == _value) | np.isnan(_value)): + raise TypeError( + "cannot convert value type to array type without precision loss" + ) + + # Setting names to ensure things like equality work (note that + # above will have failed already if units did not match). + # TODO: is this the best place to do this? + if _value.dtype.names is not None: + _value = _value.astype(self.dtype, copy=False) + return _value + + def tostring(self, order="C"): + """Not implemented, use ``.value.tostring()`` instead.""" + raise NotImplementedError( + "cannot write Quantities to string. Write array with" + " q.value.tostring(...)." + ) + + def tobytes(self, order="C"): + """Not implemented, use ``.value.tobytes()`` instead.""" + raise NotImplementedError( + "cannot write Quantities to bytes. Write array with q.value.tobytes(...)." + ) + + def tofile(self, fid, sep="", format="%s"): + """Not implemented, use ``.value.tofile()`` instead.""" + raise NotImplementedError( + "cannot write Quantities to file. Write array with q.value.tofile(...)" + ) + + def dump(self, file): + """Not implemented, use ``.value.dump()`` instead.""" + raise NotImplementedError( + "cannot dump Quantities to file. Write array with q.value.dump()" + ) + + def dumps(self): + """Not implemented, use ``.value.dumps()`` instead.""" + raise NotImplementedError( + "cannot dump Quantities to string. Write array with q.value.dumps()" + ) + + # astype, byteswap, copy, view, getfield, setflags OK as is + + def fill(self, value): + self.view(np.ndarray).fill(self._to_own_unit(value)) + + # Shape manipulation: resize cannot be done (does not own data), but + # shape, transpose, swapaxes, flatten, ravel, squeeze all OK. Only + # the flat iterator needs to be overwritten, otherwise single items are + # returned as numbers. + @property + def flat(self): + """A 1-D iterator over the Quantity array. + + This returns a ``QuantityIterator`` instance, which behaves the same + as the `~numpy.flatiter` instance returned by `~numpy.ndarray.flat`, + and is similar to, but not a subclass of, Python's built-in iterator + object. + """ + return QuantityIterator(self) + + @flat.setter + def flat(self, value): + y = self.ravel() + y[:] = value + + # Item selection and manipulation + # repeat, sort, compress, diagonal OK + def take(self, indices, axis=None, out=None, mode="raise"): + out = super().take(indices, axis=axis, out=out, mode=mode) + # For single elements, ndarray.take returns scalars; these + # need a new view as a Quantity. + if type(out) is not type(self): + out = self._new_view(out) + return out + + def put(self, indices, values, mode="raise"): + self.view(np.ndarray).put(indices, self._to_own_unit(values), mode) + + def choose(self, choices, out=None, mode="raise"): + raise NotImplementedError( + "cannot choose based on quantity. Choose using array with" + " q.value.choose(...)" + ) + + # ensure we do not return indices as quantities + def argsort(self, axis=-1, kind=None, order=None, *, stable=None): + return self.view(np.ndarray).argsort( + axis=axis, kind=kind, order=order, stable=stable + ) + + def searchsorted(self, v, *args, **kwargs): + return np.searchsorted( + np.array(self), self._to_own_unit(v, check_precision=False), *args, **kwargs + ) # avoid numpy 1.6 problem + + def argmax(self, axis=None, out=None, *, keepdims=False): + return self.view(np.ndarray).argmax(axis=axis, out=out, keepdims=keepdims) + + def argmin(self, axis=None, out=None, *, keepdims=False): + return self.view(np.ndarray).argmin(axis=axis, out=out, keepdims=keepdims) + + def __array_function__(self, function, types, args, kwargs): + """Wrap numpy functions, taking care of units. + + Parameters + ---------- + function : callable + Numpy function to wrap + types : iterable of classes + Classes that provide an ``__array_function__`` override. Can + in principle be used to interact with other classes. Below, + mostly passed on to `~numpy.ndarray`, which can only interact + with subclasses. + args : tuple + Positional arguments provided in the function call. + kwargs : dict + Keyword arguments provided in the function call. + + Returns + ------- + result: `~astropy.units.Quantity`, `~numpy.ndarray` + As appropriate for the function. If the function is not + supported, `NotImplemented` is returned, which will lead to + a `TypeError` unless another argument overrode the function. + + Raises + ------ + ~astropy.units.UnitsError + If operands have incompatible units. + """ + # A function should be in one of the following sets or dicts: + # 1. SUBCLASS_SAFE_FUNCTIONS (set), if the numpy implementation + # supports Quantity; we pass on to ndarray.__array_function__. + # 2. FUNCTION_HELPERS (dict), if the numpy implementation is usable + # after converting quantities to arrays with suitable units, + # and possibly setting units on the result. + # 3. DISPATCHED_FUNCTIONS (dict), if the function makes sense but + # requires a Quantity-specific implementation. + # 4. UNSUPPORTED_FUNCTIONS (set), if the function does not make sense. + # For now, since we may not yet have complete coverage, if a + # function is in none of the above, we simply call the numpy + # implementation. + if function in SUBCLASS_SAFE_FUNCTIONS: + return super().__array_function__(function, types, args, kwargs) + + elif function in FUNCTION_HELPERS: + function_helper = FUNCTION_HELPERS[function] + try: + args, kwargs, unit, out = function_helper(*args, **kwargs) + except NotImplementedError: + return self._not_implemented_or_raise(function, types) + + try: + result = super().__array_function__(function, types, args, kwargs) + except AttributeError as e: + # this exception handling becomes unneeded in numpy 2.2 (not NUMPY_LT_2_2) + # see https://github.com/numpy/numpy/issues/27500 + if "_implementation" not in str(e): + raise + result = function(*args, **kwargs) + + # Fall through to return section + + elif function in DISPATCHED_FUNCTIONS: + dispatched_function = DISPATCHED_FUNCTIONS[function] + try: + result, unit, out = dispatched_function(*args, **kwargs) + except NotImplementedError: + return self._not_implemented_or_raise(function, types) + + # Fall through to return section + + elif function in UNSUPPORTED_FUNCTIONS: + return NotImplemented + + else: + warnings.warn( + f"function '{function.__name__}' is not known to astropy's Quantity." + " Will run it anyway, hoping it will treat ndarray subclasses" + " correctly. Please raise an issue at" + " https://github.com/astropy/astropy/issues.", + AstropyWarning, + ) + return super().__array_function__(function, types, args, kwargs) + + if unit is UNIT_FROM_LIKE_ARG: + # fallback mechanism for NEP 35 functions that dispatch on the 'like' + # argument (i.e. self, in this context), in cases where no other + # argument provides a unit + unit = self.unit + + # If unit is None, a plain array is expected (e.g., boolean), which + # means we're done. + # We're also done if the result was NotImplemented, which can happen + # if other inputs/outputs override __array_function__; + # hopefully, they can then deal with us. + if unit is None or result is NotImplemented: + return result + + return self._result_as_quantity(result, unit, out=out) + + def _not_implemented_or_raise(self, function, types): + # Our function helper or dispatcher found that the function does not + # work with Quantity. In principle, there may be another class that + # knows what to do with us, for which we should return NotImplemented. + # But if there is ndarray (or a non-Quantity subclass of it) around, + # it quite likely coerces, so we should just break. + if any( + issubclass(t, np.ndarray) and not issubclass(t, Quantity) for t in types + ): + raise TypeError( + f"the Quantity implementation cannot handle {function} " + "with the given arguments." + ) from None + else: + return NotImplemented + + # Calculation -- override ndarray methods to take into account units. + # We use the corresponding numpy functions to evaluate the results, since + # the methods do not always allow calling with keyword arguments. + # For instance, np.array([0.,2.]).clip(a_min=0., a_max=1.) gives + # TypeError: 'a_max' is an invalid keyword argument for this function. + def _wrap_function(self, function, *args, unit=None, out=None, **kwargs): + """Wrap a numpy function that processes self, returning a Quantity. + + Parameters + ---------- + function : callable + Numpy function to wrap. + args : positional arguments + Any positional arguments to the function beyond the first argument + (which will be set to ``self``). + kwargs : keyword arguments + Keyword arguments to the function. + + If present, the following arguments are treated specially: + + unit : `~astropy.units.Unit` + Unit of the output result. If not given, the unit of ``self``. + out : `~astropy.units.Quantity` + A Quantity instance in which to store the output. + + Notes + ----- + Output should always be assigned via a keyword argument, otherwise + no proper account of the unit is taken. + + Returns + ------- + out : `~astropy.units.Quantity` + Result of the function call, with the unit set properly. + """ + if unit is None: + unit = self.unit + # Ensure we don't loop back by turning any Quantity into array views. + args = (self.value,) + tuple( + (arg.value if isinstance(arg, Quantity) else arg) for arg in args + ) + if out is not None: + # If pre-allocated output is used, check it is suitable. + # This also returns array view, to ensure we don't loop back. + arrays = tuple(arg for arg in args if isinstance(arg, np.ndarray)) + kwargs["out"] = check_output(out, unit, arrays, function=function) + # Apply the function and turn it back into a Quantity. + result = function(*args, **kwargs) + return self._result_as_quantity(result, unit, out) + + def trace(self, offset=0, axis1=0, axis2=1, dtype=None, out=None): + return self._wrap_function(np.trace, offset, axis1, axis2, dtype, out=out) + + def var( + self, axis=None, dtype=None, out=None, ddof=0, keepdims=False, *, where=True + ): + return self._wrap_function( + np.var, + axis, + dtype, + out=out, + ddof=ddof, + keepdims=keepdims, + where=where, + unit=self.unit**2, + ) + + def std( + self, axis=None, dtype=None, out=None, ddof=0, keepdims=False, *, where=True + ): + return self._wrap_function( + np.std, axis, dtype, out=out, ddof=ddof, keepdims=keepdims, where=where + ) + + def mean(self, axis=None, dtype=None, out=None, keepdims=False, *, where=True): + return self._wrap_function( + np.mean, axis, dtype, out=out, keepdims=keepdims, where=where + ) + + def round(self, decimals=0, out=None): + return self._wrap_function(np.round, decimals, out=out) + + def dot(self, b, out=None): + result_unit = self.unit * getattr(b, "unit", dimensionless_unscaled) + return self._wrap_function(np.dot, b, out=out, unit=result_unit) + + # Calculation: override methods that do not make sense. + + def all(self, axis=None, out=None): + raise TypeError( + "cannot evaluate truth value of quantities. " + "Evaluate array with q.value.all(...)" + ) + + def any(self, axis=None, out=None): + raise TypeError( + "cannot evaluate truth value of quantities. " + "Evaluate array with q.value.any(...)" + ) + + # Calculation: numpy functions that can be overridden with methods. + + def diff(self, n=1, axis=-1): + return self._wrap_function(np.diff, n, axis) + + def ediff1d(self, to_end=None, to_begin=None): + return self._wrap_function(np.ediff1d, to_end, to_begin) + + def insert(self, obj, values, axis=None): + """ + Insert values along the given axis before the given indices and return + a new `~astropy.units.Quantity` object. + + This is a thin wrapper around the `numpy.insert` function. + + Parameters + ---------- + obj : int, slice or sequence of int + Object that defines the index or indices before which ``values`` is + inserted. + values : array-like + Values to insert. If the type of ``values`` is different + from that of quantity, ``values`` is converted to the matching type. + ``values`` should be shaped so that it can be broadcast appropriately + The unit of ``values`` must be consistent with this quantity. + axis : int, optional + Axis along which to insert ``values``. If ``axis`` is None then + the quantity array is flattened before insertion. + + Returns + ------- + out : `~astropy.units.Quantity` + A copy of quantity with ``values`` inserted. Note that the + insertion does not occur in-place: a new quantity array is returned. + + Examples + -------- + >>> import astropy.units as u + >>> q = [1, 2] * u.m + >>> q.insert(0, 50 * u.cm) + + + >>> q = [[1, 2], [3, 4]] * u.m + >>> q.insert(1, [10, 20] * u.m, axis=0) + + + >>> q.insert(1, 10 * u.m, axis=1) + + + """ + out_array = np.insert(self.value, obj, self._to_own_unit(values), axis) + return self._new_view(out_array) + + +class SpecificTypeQuantity(Quantity): + """Superclass for Quantities of specific physical type. + + Subclasses of these work just like :class:`~astropy.units.Quantity`, except + that they are for specific physical types (and may have methods that are + only appropriate for that type). Astropy examples are + :class:`~astropy.coordinates.Angle` and + :class:`~astropy.coordinates.Distance` + + At a minimum, subclasses should set ``_equivalent_unit`` to the unit + associated with the physical type. + """ + + # The unit for the specific physical type. Instances can only be created + # with units that are equivalent to this. + _equivalent_unit: ClassVar[UnitBase | tuple[UnitBase, ...] | None] = None + + # The default unit used for views. Even with `None`, views of arrays + # without units are possible, but will have an uninitialized unit. + _unit = None + + # Default unit for initialization through the constructor. + _default_unit = None + + # ensure that we get precedence over our superclass. + __array_priority__ = Quantity.__array_priority__ + 10 + + def __quantity_subclass__(self, unit): + if unit.is_equivalent(self._equivalent_unit): + return type(self), True + else: + return super().__quantity_subclass__(unit)[0], False + + def _set_unit(self, unit): + if unit is None or not unit.is_equivalent(self._equivalent_unit): + raise UnitTypeError( + ( + f"{type(self).__name__} instances require units equivalent to " + f"'{self._equivalent_unit}'" + ) + + ( + ", but no unit was given." + if unit is None + else f", so cannot set it to '{unit}'." + ) + ) + + super()._set_unit(unit) + + +def isclose(a, b, rtol=1.0e-5, atol=None, equal_nan=False): + """ + Return a boolean array where two arrays are element-wise equal + within a tolerance. + + Parameters + ---------- + a, b : array-like or `~astropy.units.Quantity` + Input values or arrays to compare + rtol : array-like or `~astropy.units.Quantity` + The relative tolerance for the comparison, which defaults to + ``1e-5``. If ``rtol`` is a :class:`~astropy.units.Quantity`, + then it must be dimensionless. + atol : number or `~astropy.units.Quantity` + The absolute tolerance for the comparison. The units (or lack + thereof) of ``a``, ``b``, and ``atol`` must be consistent with + each other. If `None`, ``atol`` defaults to zero in the + appropriate units. + equal_nan : `bool` + Whether to compare NaN’s as equal. If `True`, NaNs in ``a`` will + be considered equal to NaN’s in ``b``. + + Notes + ----- + This is a :class:`~astropy.units.Quantity`-aware version of + :func:`numpy.isclose`. However, this differs from the `numpy` function in + that the default for the absolute tolerance here is zero instead of + ``atol=1e-8`` in `numpy`, as there is no natural way to set a default + *absolute* tolerance given two inputs that may have differently scaled + units. + + Raises + ------ + `~astropy.units.UnitsError` + If the dimensions of ``a``, ``b``, or ``atol`` are incompatible, + or if ``rtol`` is not dimensionless. + + See Also + -------- + allclose + """ + return np.isclose(*_unquantify_allclose_arguments(a, b, rtol, atol), equal_nan) + + +def allclose(a, b, rtol=1.0e-5, atol=None, equal_nan=False) -> bool: + """ + Whether two arrays are element-wise equal within a tolerance. + + Parameters + ---------- + a, b : array-like or `~astropy.units.Quantity` + Input values or arrays to compare + rtol : array-like or `~astropy.units.Quantity` + The relative tolerance for the comparison, which defaults to + ``1e-5``. If ``rtol`` is a :class:`~astropy.units.Quantity`, + then it must be dimensionless. + atol : number or `~astropy.units.Quantity` + The absolute tolerance for the comparison. The units (or lack + thereof) of ``a``, ``b``, and ``atol`` must be consistent with + each other. If `None`, ``atol`` defaults to zero in the + appropriate units. + equal_nan : `bool` + Whether to compare NaN’s as equal. If `True`, NaNs in ``a`` will + be considered equal to NaN’s in ``b``. + + Notes + ----- + This is a :class:`~astropy.units.Quantity`-aware version of + :func:`numpy.allclose`. However, this differs from the `numpy` function in + that the default for the absolute tolerance here is zero instead of + ``atol=1e-8`` in `numpy`, as there is no natural way to set a default + *absolute* tolerance given two inputs that may have differently scaled + units. + + Raises + ------ + `~astropy.units.UnitsError` + If the dimensions of ``a``, ``b``, or ``atol`` are incompatible, + or if ``rtol`` is not dimensionless. + + See Also + -------- + isclose + """ + return np.allclose(*_unquantify_allclose_arguments(a, b, rtol, atol), equal_nan) + + +def _unquantify_allclose_arguments(actual, desired, rtol, atol): + actual = Quantity(actual, subok=True, copy=None) + + desired = Quantity(desired, subok=True, copy=None) + try: + desired = desired.to(actual.unit) + except UnitsError: + raise UnitsError( + f"Units for 'desired' ({desired.unit}) and 'actual' " + f"({actual.unit}) are not convertible" + ) + + if atol is None: + # By default, we assume an absolute tolerance of zero in the + # appropriate units. The default value of None for atol is + # needed because the units of atol must be consistent with the + # units for a and b. + atol = Quantity(0) + else: + atol = Quantity(atol, subok=True, copy=None) + try: + atol = atol.to(actual.unit) + except UnitsError: + raise UnitsError( + f"Units for 'atol' ({atol.unit}) and 'actual' " + f"({actual.unit}) are not convertible" + ) + + rtol = Quantity(rtol, subok=True, copy=None) + try: + rtol = rtol.to(dimensionless_unscaled) + except Exception: + raise UnitsError("'rtol' should be dimensionless") + + return actual.value, desired.value, rtol.value, atol.value diff --git a/astropy/units/quantity_helper/__init__.py b/astropy/units/quantity_helper/__init__.py new file mode 100644 index 000000000000..e2a3fa54ddc7 --- /dev/null +++ b/astropy/units/quantity_helper/__init__.py @@ -0,0 +1,16 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +"""Helper functions for Quantity. + +In particular, this implements the logic that determines scaling and result +units for a given ufunc, given input units. +""" + +from .converters import * + +# isort: split +# By importing helpers, all the unit conversion functions needed for +# numpy ufuncs and functions are defined. +# For scipy.special and erfa, importing the helper modules ensures +# the definitions are added as modules to UFUNC_HELPERS, to be loaded +# on demand. +from . import erfa, function_helpers, helpers, scipy_special diff --git a/astropy/units/quantity_helper/converters.py b/astropy/units/quantity_helper/converters.py new file mode 100644 index 000000000000..56e4783e580d --- /dev/null +++ b/astropy/units/quantity_helper/converters.py @@ -0,0 +1,388 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +"""Converters for Quantity.""" + +import threading + +import numpy as np + +from astropy.units.core import dimensionless_unscaled +from astropy.units.errors import UnitConversionError, UnitsError, UnitTypeError + +__all__ = [ + "UFUNC_HELPERS", + "UNSUPPORTED_UFUNCS", + "can_have_arbitrary_unit", + "check_output", + "converters_and_unit", +] + + +class UfuncHelpers(dict): + """Registry of unit conversion functions to help ufunc evaluation. + + Based on dict for quick access, but with a missing method to load + helpers for additional modules such as scipy.special and erfa. + + Such modules should be registered using ``register_module``. + """ + + def __init__(self, *args, **kwargs): + self.modules = {} + self.UNSUPPORTED = set() # Upper-case for backwards compatibility + self._lock = threading.RLock() + super().__init__(*args, **kwargs) + + def register_module(self, module, names, importer): + """Register (but do not import) a set of ufunc helpers. + + Parameters + ---------- + module : str + Name of the module with the ufuncs (e.g., 'scipy.special'). + names : iterable of str + Names of the module ufuncs for which helpers are available. + importer : callable + Function that imports the ufuncs and returns a dict of helpers + keyed by those ufuncs. If the value is `None`, the ufunc is + explicitly *not* supported. + """ + with self._lock: + self.modules[module] = {"names": names, "importer": importer} + + def import_module(self, module): + """Import the helpers from the given module using its helper function. + + Parameters + ---------- + module : str + Name of the module. Has to have been registered beforehand. + """ + with self._lock: + module_info = self.modules.pop(module) + self.update(module_info["importer"]()) + + def __missing__(self, ufunc): + """Called if a ufunc is not found. + + Check if the ufunc is in any of the available modules, and, if so, + import the helpers for that module. + """ + with self._lock: + # Check if it was loaded while we waited for the lock + if ufunc in self: + return self[ufunc] + + if ufunc in self.UNSUPPORTED: + raise TypeError(f"Cannot use ufunc '{ufunc.__name__}' with quantities") + + for module, module_info in list(self.modules.items()): + if ufunc.__name__ in module_info["names"]: + # A ufunc with the same name is supported by this module. + # Of course, this doesn't necessarily mean it is the + # right module. So, we try let the importer do its work. + # If it fails (e.g., for `scipy.special`), then that's + # fine, just raise the TypeError. If it succeeds, but + # the ufunc is not found, that is also fine: we will + # enter __missing__ again and either find another + # module or get the TypeError there. + try: + self.import_module(module) + except ImportError: # pragma: no cover + pass + else: + return self[ufunc] + + raise TypeError( + f"unknown ufunc {ufunc.__name__}. If you believe this ufunc " + "should be supported, please raise an issue on " + "https://github.com/astropy/astropy" + ) + + def __setitem__(self, key, value): + # Implementation note: in principle, we could just let `None` + # mean that something is not implemented, but this means an + # extra if clause for the output, slowing down the common + # path where a ufunc is supported. + with self._lock: + if value is None: + self.UNSUPPORTED |= {key} + self.pop(key, None) + else: + super().__setitem__(key, value) + self.UNSUPPORTED -= {key} + + +UFUNC_HELPERS = UfuncHelpers() +UNSUPPORTED_UFUNCS = UFUNC_HELPERS.UNSUPPORTED + + +def can_have_arbitrary_unit(value): + """Test whether the items in value can have arbitrary units. + + Numbers whose value does not change upon a unit change, i.e., + zero, infinity, or not-a-number + + Parameters + ---------- + value : number or array + + Returns + ------- + bool + `True` if each member is either zero or not finite, `False` otherwise + """ + return np.all(np.logical_or(np.equal(value, 0.0), ~np.isfinite(value))) + + +def converters_and_unit(function, method, *args): + """Determine the required converters and the unit of the ufunc result. + + Converters are functions required to convert to a ufunc's expected unit, + e.g., radian for np.sin; or to ensure units of two inputs are consistent, + e.g., for np.add. In these examples, the unit of the result would be + dimensionless_unscaled for np.sin, and the same consistent unit for np.add. + + Parameters + ---------- + function : `~numpy.ufunc` + Numpy universal function + method : str + Method with which the function is evaluated, e.g., + '__call__', 'reduce', etc. + *args : `~astropy.units.Quantity` or ndarray subclass + Input arguments to the function + + Raises + ------ + TypeError : when the specified function cannot be used with Quantities + (e.g., np.logical_or), or when the routine does not know how to handle + the specified function (in which case an issue should be raised on + https://github.com/astropy/astropy). + UnitTypeError : when the conversion to the required (or consistent) units + is not possible. + """ + # Check whether we support this ufunc, by getting the helper function + # (defined in helpers) which returns a list of function(s) that convert the + # input(s) to the unit required for the ufunc, as well as the unit the + # result will have (a tuple of units if there are multiple outputs). + ufunc_helper = UFUNC_HELPERS[function] + + if method == "__call__" or (method == "outer" and function.nin == 2): + # Find out the units of the arguments passed to the ufunc; usually, + # at least one is a quantity, but for two-argument ufuncs, the second + # could also be a Numpy array, etc. These are given unit=None. + units = [getattr(arg, "unit", None) for arg in args] + + # Determine possible conversion functions, and the result unit. + converters, result_unit = ufunc_helper(function, *units) + + if any(converter is False for converter in converters): + # for multi-argument ufuncs with a quantity and a non-quantity, + # the quantity normally needs to be dimensionless, *except* + # if the non-quantity can have arbitrary unit, i.e., when it + # is all zero, infinity or NaN. In that case, the non-quantity + # can just have the unit of the quantity + # (this allows, e.g., `q > 0.` independent of unit) + try: + # Don't fold this loop in the test above: this rare case + # should not make the common case slower. + for i, converter in enumerate(converters): + if converter is not False: + continue + if can_have_arbitrary_unit(args[i]): + converters[i] = None + else: + raise UnitConversionError( + f"Can only apply '{function.__name__}' function to " + "dimensionless quantities when other argument is not " + "a quantity (unless the latter is all zero/infinity/nan)." + ) + except TypeError: + # _can_have_arbitrary_unit failed: arg could not be compared + # with zero or checked to be finite. Then, ufunc will fail too. + raise TypeError( + "Unsupported operand type(s) for ufunc {}: '{}'".format( + function.__name__, + ",".join([arg.__class__.__name__ for arg in args]), + ) + ) + + # In the case of np.power and np.float_power, the unit itself needs to + # be modified by an amount that depends on one of the input values, + # so we need to treat this as a special case. + # TODO: find a better way to deal with this. + if result_unit is False: + if units[0] is None or units[0] == dimensionless_unscaled: + result_unit = dimensionless_unscaled + else: + if units[1] is None: + p = args[1] + else: + p = args[1].to(dimensionless_unscaled).value + + try: + result_unit = units[0] ** p + except ValueError as exc: + # Changing the unit does not work for, e.g., array-shaped + # power, but this is OK if we're (scaled) dimensionless. + try: + converters[0] = units[0].get_converter(dimensionless_unscaled) + except UnitConversionError: + raise exc + else: + result_unit = dimensionless_unscaled + + else: # methods for which the unit should stay the same + nin = function.nin + unit = getattr(args[0], "unit", None) + if method == "at" and nin <= 2: + if nin == 1: + units = [unit] + else: + units = [unit, getattr(args[2], "unit", None)] + + converters, result_unit = ufunc_helper(function, *units) + + # ensure there is no 'converter' for indices (2nd argument) + converters.insert(1, None) + + elif method in {"reduce", "accumulate", "reduceat"} and nin == 2: + converters, result_unit = ufunc_helper(function, unit, unit) + converters = converters[:1] + if method == "reduceat": + # add 'scale' for indices (2nd argument) + converters += [None] + + else: + if method in {"reduce", "accumulate", "reduceat", "outer"} and nin != 2: + raise ValueError(f"{method} only supported for binary functions") + + raise TypeError( + f"Unexpected ufunc method {method}. If this should work, please " + "raise an issue on https://github.com/astropy/astropy" + ) + + # for all but __call__ method, scaling is not allowed + if unit is not None and result_unit is None: + raise TypeError( + f"Cannot use '{method}' method on ufunc {function.__name__} with a " + "Quantity instance as the result is not a Quantity." + ) + + if converters[0] is not None or ( + unit is not None + and unit is not result_unit + and (not result_unit.is_equivalent(unit) or result_unit.to(unit) != 1.0) + ): + # NOTE: this cannot be the more logical UnitTypeError, since + # then things like np.cumprod will not longer fail (they check + # for TypeError). + raise UnitsError( + f"Cannot use '{method}' method on ufunc {function.__name__} with a " + "Quantity instance as it would change the unit." + ) + + return converters, result_unit + + +def check_output(output, unit, inputs, function=None): + """Check that function output can be stored in the output array given. + + Parameters + ---------- + output : array or `~astropy.units.Quantity` or tuple + Array that should hold the function output (or tuple of such arrays). + unit : `~astropy.units.Unit` or None, or tuple + Unit that the output will have, or `None` for pure numbers (should be + tuple of same if output is a tuple of outputs). + inputs : tuple + Any input arguments. These should be castable to the output. + function : callable + The function that will be producing the output. If given, used to + give a more informative error message. + + Returns + ------- + arrays : ndarray view or tuple thereof + The view(s) is of ``output``. + + Raises + ------ + UnitTypeError : If ``unit`` is inconsistent with the class of ``output`` + + TypeError : If the ``inputs`` cannot be cast safely to ``output``. + """ + if isinstance(output, tuple): + return tuple( + check_output(output_, unit_, inputs, function) + for output_, unit_ in zip(output, unit) + ) + + # ``None`` indicates no actual array is needed. This can happen, e.g., + # with np.modf(a, out=(None, b)). + if output is None: + return None + + if hasattr(output, "__quantity_subclass__"): + # Check that we're not trying to store a plain Numpy array or a + # Quantity with an inconsistent unit (e.g., not angular for Angle). + if unit is None: + raise TypeError( + "Cannot store non-quantity output{} in {} instance".format( + ( + f" from {function.__name__} function" + if function is not None + else "" + ), + type(output), + ) + ) + + q_cls, subok = output.__quantity_subclass__(unit) + if not (subok or q_cls is type(output)): + raise UnitTypeError( + "Cannot store output with unit '{}'{} " + "in {} instance. Use {} instance instead.".format( + unit, + ( + f" from {function.__name__} function" + if function is not None + else "" + ), + type(output), + q_cls, + ) + ) + + # check we can handle the dtype (e.g., that we are not int + # when float is required). Note that we only do this for Quantity + # output; for array output, we defer to numpy's default handling. + # Also, any structured dtype are ignored (likely erfa ufuncs). + # TODO: make more logical; is this necessary at all? + if inputs and not output.dtype.names: + result_type = np.result_type(*inputs) + if not ( + result_type.names + or np.can_cast(result_type, output.dtype, casting="same_kind") + ): + raise TypeError( + "Arguments cannot be cast safely to inplace " + f"output with dtype={output.dtype}" + ) + # Turn into ndarray, so we do not loop into array_wrap/array_ufunc + # if the output is used to store results of a function. + return output.view(np.ndarray) + + else: + # output is not a Quantity, so cannot obtain a unit. + if not (unit is None or unit == dimensionless_unscaled): + raise UnitTypeError( + "Cannot store quantity with dimension " + "{}in a non-Quantity instance.".format( + f"resulting from {function.__name__} function " + if function is not None + else "" + ) + ) + + return output diff --git a/astropy/units/quantity_helper/erfa.py b/astropy/units/quantity_helper/erfa.py new file mode 100644 index 000000000000..359071ad563a --- /dev/null +++ b/astropy/units/quantity_helper/erfa.py @@ -0,0 +1,533 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +"""Quantity helpers for the ERFA ufuncs.""" +# Tests for these are in coordinates, not in units. +# Note that when this is imported, the unit definitions have not yet +# been completed, so we import units locally. +# We also avoid importing erfa up front since otherwise type stub generation +# in the build process can only run with erfa as a build requirement. +# TODO: solve both of the above by defining inside get_erfa_helpers? + +from astropy.units.core import dimensionless_unscaled +from astropy.units.errors import UnitsError, UnitTypeError +from astropy.units.structured import StructuredUnit + +from . import UFUNC_HELPERS +from .helpers import ( + _d, + get_converter, + helper_invariant, + helper_multiplication, + helper_twoarg_invariant, +) + +erfa_ufuncs = ( + "s2c", "s2p", "c2s", "p2s", "pm", "pdp", "pxp", "rxp", "cpv", "p2pv", "pv2p", + "pv2s", "pvdpv", "pvm", "pvmpv", "pvppv", "pvstar", "pvtob", "pvu", "pvup", + "pvxpv", "rxpv", "s2pv", "s2xpv", "starpv", "sxpv", "trxpv", "gd2gc", "gd2gce", + "gc2gd", "gc2gde", "ldn", "apco13", "aper", "apio", + "atciq", "atciqn", "atciqz", "aticq", "atioq", "atoiq", +) # fmt: skip + + +def has_matching_structure(unit, dtype): + dtype_fields = dtype.fields + if dtype_fields: + return ( + isinstance(unit, StructuredUnit) + and len(unit) == len(dtype_fields) + and all( + has_matching_structure(u, df_v[0]) + for (u, df_v) in zip(unit.values(), dtype_fields.values()) + ) + ) + else: + return not isinstance(unit, StructuredUnit) + + +def check_structured_unit(unit, dtype_name): + # TODO: short-cut "dt_pv". + import erfa + + dtype = getattr(erfa, dtype_name) + if not has_matching_structure(unit, dtype): + name = dtype_name.removeprefix("dt_").removeprefix("era").lower() + raise UnitTypeError(f"{name} input needs unit matching dtype={dtype}.") + + +def check_no_time_units(unit1, unit2, f): + if unit1 is not None or unit2 is not None: + raise TypeError(f"cannot pass in units for 2-part time in {f.__name__}.") + + +def helper_s2c(f, unit1, unit2): + from astropy.units.si import radian + + try: + return [ + get_converter(unit1, radian), + get_converter(unit2, radian), + ], dimensionless_unscaled + except UnitsError: + raise UnitTypeError( + f"Can only apply '{f.__name__}' function to quantities with angle units" + ) + + +def helper_s2p(f, unit1, unit2, unit3): + from astropy.units.si import radian + + try: + return [get_converter(unit1, radian), get_converter(unit2, radian), None], unit3 + except UnitsError: + raise UnitTypeError( + f"Can only apply '{f.__name__}' function to quantities with angle units" + ) + + +def helper_c2s(f, unit1): + from astropy.units.si import radian + + return [None], (radian, radian) + + +def helper_p2s(f, unit1): + from astropy.units.si import radian + + return [None], (radian, radian, unit1) + + +def helper_gc2gd(f, nounit, unit1): + from astropy.units.si import m, radian + + if nounit is not None: + raise UnitTypeError("ellipsoid cannot be a quantity.") + try: + return [None, get_converter(unit1, m)], (radian, radian, m, None) + except UnitsError: + raise UnitTypeError( + f"Can only apply '{f.__name__}' function to quantities with length units" + ) + + +def helper_gc2gde(f, unit_r, unit_flat, unit_xyz): + from astropy.units.si import m, radian + + return [ + get_converter(unit_r, m), + get_converter(_d(unit_flat), dimensionless_unscaled), + get_converter(unit_xyz, m), + ], ( + radian, + radian, + m, + None, + ) + + +def helper_gd2gc(f, nounit, unit1, unit2, unit3): + from astropy.units.si import m, radian + + if nounit is not None: + raise UnitTypeError("ellipsoid cannot be a quantity.") + try: + return [ + None, + get_converter(unit1, radian), + get_converter(unit2, radian), + get_converter(unit3, m), + ], (m, None) + except UnitsError: + raise UnitTypeError( + f"Can only apply '{f.__name__}' function to lon, lat " + "with angle and height with length units" + ) + + +def helper_gd2gce(f, unit_r, unit_flat, unit_long, unit_lat, unit_h): + from astropy.units.si import m, radian + + return [ + get_converter(unit_r, m), + get_converter(_d(unit_flat), dimensionless_unscaled), + get_converter(unit_long, radian), + get_converter(unit_lat, radian), + get_converter(unit_h, m), + ], (m, None) + + +def helper_p2pv(f, unit1): + from astropy.units.si import s + + if isinstance(unit1, StructuredUnit): + raise UnitTypeError("p vector unit cannot be a structured unit.") + return [None], StructuredUnit((unit1, unit1 / s)) + + +def helper_pv2p(f, unit1): + check_structured_unit(unit1, "dt_pv") + return [None], unit1[0] + + +def helper_pv2s(f, unit_pv): + from astropy.units.si import radian + + check_structured_unit(unit_pv, "dt_pv") + ang_unit = radian * unit_pv[1] / unit_pv[0] + return [None], (radian, radian, unit_pv[0], ang_unit, ang_unit, unit_pv[1]) + + +def helper_s2pv(f, unit_theta, unit_phi, unit_r, unit_td, unit_pd, unit_rd): + from astropy.units.si import radian + + time_unit = unit_r / unit_rd + return [ + get_converter(unit_theta, radian), + get_converter(unit_phi, radian), + None, + get_converter(unit_td, radian / time_unit), + get_converter(unit_pd, radian / time_unit), + None, + ], StructuredUnit((unit_r, unit_rd)) + + +def helper_pv_multiplication(f, unit1, unit2): + check_structured_unit(unit1, "dt_pv") + check_structured_unit(unit2, "dt_pv") + result_unit = StructuredUnit((unit1[0] * unit2[0], unit1[1] * unit2[0])) + converter = get_converter( + unit2, StructuredUnit((unit2[0], unit1[1] * unit2[0] / unit1[0])) + ) + return [None, converter], result_unit + + +def helper_pvm(f, unit1): + check_structured_unit(unit1, "dt_pv") + return [None], (unit1[0], unit1[1]) + + +def helper_pvstar(f, unit1): + from astropy.units.astrophys import AU + from astropy.units.si import arcsec, day, km, radian, s, year + + return [get_converter(unit1, StructuredUnit((AU, AU / day)))], ( + radian, + radian, + radian / year, + radian / year, + arcsec, + km / s, + None, + ) + + +def helper_starpv(f, unit_ra, unit_dec, unit_pmr, unit_pmd, unit_px, unit_rv): + from astropy.units.astrophys import AU + from astropy.units.si import arcsec, day, km, radian, s, year + + return [ + get_converter(unit_ra, radian), + get_converter(unit_dec, radian), + get_converter(unit_pmr, radian / year), + get_converter(unit_pmd, radian / year), + get_converter(unit_px, arcsec), + get_converter(unit_rv, km / s), + ], (StructuredUnit((AU, AU / day)), None) + + +def helper_pvtob( + f, unit_elong, unit_phi, unit_hm, unit_xp, unit_yp, unit_sp, unit_theta +): + from astropy.units.si import m, radian, s + + return [ + get_converter(unit_elong, radian), + get_converter(unit_phi, radian), + get_converter(unit_hm, m), + get_converter(unit_xp, radian), + get_converter(unit_yp, radian), + get_converter(unit_sp, radian), + get_converter(unit_theta, radian), + ], StructuredUnit((m, m / s)) + + +def helper_pvu(f, unit_t, unit_pv): + check_structured_unit(unit_pv, "dt_pv") + return [get_converter(unit_t, unit_pv[0] / unit_pv[1]), None], unit_pv + + +def helper_pvup(f, unit_t, unit_pv): + check_structured_unit(unit_pv, "dt_pv") + return [get_converter(unit_t, unit_pv[0] / unit_pv[1]), None], unit_pv[0] + + +def helper_s2xpv(f, unit1, unit2, unit_pv): + check_structured_unit(unit_pv, "dt_pv") + return [None, None, None], StructuredUnit( + (_d(unit1) * unit_pv[0], _d(unit2) * unit_pv[1]) + ) + + +def ldbody_unit(): + from erfa import dt_eraLDBODY + + from astropy.units.astrophys import AU, Msun + from astropy.units.si import day, radian + + return StructuredUnit((Msun, radian, (AU, AU / day)), dt_eraLDBODY) + + +def astrom_unit(): + from erfa import dt_eraASTROM + + from astropy.units.astrophys import AU + from astropy.units.si import rad, year + + one = rel2c = dimensionless_unscaled + + return StructuredUnit( + ( + year, + AU, + one, + AU, + rel2c, + one, + one, + rad, + rad, + rad, + rad, + one, + one, + rel2c, + rad, + rad, + rad, + ), + dt_eraASTROM, + ) + + +def helper_ldn(f, unit_b, unit_ob, unit_sc): + from astropy.units.astrophys import AU + + return [ + get_converter(unit_b, ldbody_unit()), + get_converter(unit_ob, AU), + get_converter(_d(unit_sc), dimensionless_unscaled), + ], dimensionless_unscaled + + +def helper_aper(f, unit_theta, unit_astrom): + check_structured_unit(unit_astrom, "dt_eraASTROM") + unit_along = unit_astrom[7] # along + + if unit_astrom[14] is unit_along: # eral + result_unit = unit_astrom + else: + result_units = tuple( + (unit_along if i == 14 else v) for i, v in enumerate(unit_astrom.values()) + ) + result_unit = unit_astrom.__class__(result_units, names=unit_astrom) + return [get_converter(unit_theta, unit_along), None], result_unit + + +def helper_apco13( + f, + unit_utc1, + unit_utc2, + unit_dut1, + unit_elong, + unit_phi, + unit_hm, + unit_xp, + unit_yp, + unit_phpa, + unit_tc, + unit_rh, + unit_wl, +): + from astropy.units import ( + add_enabled_equivalencies, + deg_C, + hPa, + m, + micron, + one, + radian, + second, + temperature, + ) + + check_no_time_units(unit_utc1, unit_utc2, f) + + with add_enabled_equivalencies(temperature()): + return [ + None, + None, + get_converter(unit_dut1, second), + get_converter(unit_elong, radian), + get_converter(unit_phi, radian), + get_converter(unit_hm, m), + get_converter(unit_xp, radian), + get_converter(unit_yp, radian), + get_converter(unit_phpa, hPa), + get_converter(unit_tc, deg_C), + get_converter(_d(unit_rh), one), + get_converter(unit_wl, micron), + ], (astrom_unit(), radian, None) + + +def helper_apio( + f, + unit_sp, + unit_theta, + unit_elong, + unit_phi, + unit_hm, + unit_xp, + unit_yp, + unit_refa, + unit_refb, +): + from astropy.units.si import m, radian + + return [ + get_converter(unit_sp, radian), + get_converter(unit_theta, radian), + get_converter(unit_elong, radian), + get_converter(unit_phi, radian), + get_converter(unit_hm, m), + get_converter(unit_xp, radian), + get_converter(unit_yp, radian), + get_converter(unit_refa, radian), + get_converter(unit_refb, radian), + ], astrom_unit() + + +def helper_atciq(f, unit_rc, unit_dc, unit_pr, unit_pd, unit_px, unit_rv, unit_astrom): + from astropy.units.si import arcsec, km, radian, s, year + + return [ + get_converter(unit_rc, radian), + get_converter(unit_dc, radian), + get_converter(unit_pr, radian / year), + get_converter(unit_pd, radian / year), + get_converter(unit_px, arcsec), + get_converter(unit_rv, km / s), + get_converter(unit_astrom, astrom_unit()), + ], (radian, radian) + + +def helper_atciqn( + f, unit_rc, unit_dc, unit_pr, unit_pd, unit_px, unit_rv, unit_astrom, unit_b +): + from astropy.units.si import arcsec, km, radian, s, year + + return [ + get_converter(unit_rc, radian), + get_converter(unit_dc, radian), + get_converter(unit_pr, radian / year), + get_converter(unit_pd, radian / year), + get_converter(unit_px, arcsec), + get_converter(unit_rv, km / s), + get_converter(unit_astrom, astrom_unit()), + get_converter(unit_b, ldbody_unit()), + ], (radian, radian) + + +def helper_atciqz_aticq(f, unit_rc, unit_dc, unit_astrom): + from astropy.units.si import radian + + return [ + get_converter(unit_rc, radian), + get_converter(unit_dc, radian), + get_converter(unit_astrom, astrom_unit()), + ], (radian, radian) + + +def helper_aticqn(f, unit_rc, unit_dc, unit_astrom, unit_b): + from astropy.units.si import radian + + return [ + get_converter(unit_rc, radian), + get_converter(unit_dc, radian), + get_converter(unit_astrom, astrom_unit()), + get_converter(unit_b, ldbody_unit()), + ], (radian, radian) + + +def helper_atioq(f, unit_rc, unit_dc, unit_astrom): + from astropy.units.si import radian + + return [ + get_converter(unit_rc, radian), + get_converter(unit_dc, radian), + get_converter(unit_astrom, astrom_unit()), + ], (radian,) * 5 + + +def helper_atoiq(f, unit_type, unit_ri, unit_di, unit_astrom): + from astropy.units.si import radian + + if unit_type is not None: + raise UnitTypeError("argument 'type' should not have a unit") + + return [ + None, + get_converter(unit_ri, radian), + get_converter(unit_di, radian), + get_converter(unit_astrom, astrom_unit()), + ], (radian, radian) + + +def get_erfa_helpers(): + from erfa import ufunc as erfa_ufunc + + return { + erfa_ufunc.apco13: helper_apco13, + erfa_ufunc.aper: helper_aper, + erfa_ufunc.apio: helper_apio, + erfa_ufunc.atciq: helper_atciq, + erfa_ufunc.atciqn: helper_atciqn, + erfa_ufunc.atciqz: helper_atciqz_aticq, + erfa_ufunc.aticq: helper_atciqz_aticq, + erfa_ufunc.aticqn: helper_aticqn, + erfa_ufunc.atioq: helper_atioq, + erfa_ufunc.atoiq: helper_atoiq, + erfa_ufunc.c2s: helper_c2s, + erfa_ufunc.cpv: helper_invariant, + erfa_ufunc.gc2gd: helper_gc2gd, + erfa_ufunc.gc2gde: helper_gc2gde, + erfa_ufunc.gd2gc: helper_gd2gc, + erfa_ufunc.gd2gce: helper_gd2gce, + erfa_ufunc.ldn: helper_ldn, + erfa_ufunc.p2pv: helper_p2pv, + erfa_ufunc.p2s: helper_p2s, + erfa_ufunc.pdp: helper_multiplication, + erfa_ufunc.pm: helper_invariant, + erfa_ufunc.pv2p: helper_pv2p, + erfa_ufunc.pv2s: helper_pv2s, + erfa_ufunc.pvdpv: helper_pv_multiplication, + erfa_ufunc.pvm: helper_pvm, + erfa_ufunc.pvmpv: helper_twoarg_invariant, + erfa_ufunc.pvppv: helper_twoarg_invariant, + erfa_ufunc.pvstar: helper_pvstar, + erfa_ufunc.pvtob: helper_pvtob, + erfa_ufunc.pvu: helper_pvu, + erfa_ufunc.pvup: helper_pvup, + erfa_ufunc.pvxpv: helper_pv_multiplication, + erfa_ufunc.pxp: helper_multiplication, + erfa_ufunc.rxp: helper_multiplication, + erfa_ufunc.rxpv: helper_multiplication, + erfa_ufunc.s2c: helper_s2c, + erfa_ufunc.s2p: helper_s2p, + erfa_ufunc.s2pv: helper_s2pv, + erfa_ufunc.s2xpv: helper_s2xpv, + erfa_ufunc.starpv: helper_starpv, + erfa_ufunc.sxpv: helper_multiplication, + erfa_ufunc.trxpv: helper_multiplication, + } + + +UFUNC_HELPERS.register_module("erfa.ufunc", erfa_ufuncs, get_erfa_helpers) diff --git a/astropy/units/quantity_helper/function_helpers.py b/astropy/units/quantity_helper/function_helpers.py new file mode 100644 index 000000000000..34958309ac1c --- /dev/null +++ b/astropy/units/quantity_helper/function_helpers.py @@ -0,0 +1,1635 @@ +# Licensed under a 3-clause BSD style license. See LICENSE.rst except +# for parts explicitly labelled as being (largely) copies of numpy +# implementations; for those, see licenses/NUMPY_LICENSE.rst. +"""Helpers for overriding numpy functions. + +We override numpy functions in `~astropy.units.Quantity.__array_function__`. +In this module, the numpy functions are split in four groups, each of +which has an associated `set` or `dict`: + +1. SUBCLASS_SAFE_FUNCTIONS (set), if the numpy implementation + supports Quantity; we pass on to ndarray.__array_function__. +2. FUNCTION_HELPERS (dict), if the numpy implementation is usable + after converting quantities to arrays with suitable units, + and possibly setting units on the result. +3. DISPATCHED_FUNCTIONS (dict), if the function makes sense but + requires a Quantity-specific implementation +4. UNSUPPORTED_FUNCTIONS (set), if the function does not make sense. + +For the FUNCTION_HELPERS `dict`, the value is a function that does the +unit conversion. It should take the same arguments as the numpy +function would (though one can use ``*args`` and ``**kwargs``) and +return a tuple of ``args, kwargs, unit, out``, where ``args`` and +``kwargs`` will be will be passed on to the numpy implementation, +``unit`` is a possible unit of the result (`None` if it should not be +converted to Quantity), and ``out`` is a possible output Quantity passed +in, which will be filled in-place. + +For the DISPATCHED_FUNCTIONS `dict`, the value is a function that +implements the numpy functionality for Quantity input. It should +return a tuple of ``result, unit, out``, where ``result`` is generally +a plain array with the result, and ``unit`` and ``out`` are as above. +If unit is `None`, result gets returned directly, so one can also +return a Quantity directly using ``quantity_result, None, None``. + +""" + +import functools +import operator + +import numpy as np +import numpy._core as np_core +from numpy.lib import recfunctions as rfn + +from astropy.units.core import dimensionless_unscaled +from astropy.units.errors import UnitConversionError, UnitsError, UnitTypeError +from astropy.utils.compat import ( + NUMPY_LT_2_1, + NUMPY_LT_2_2, + NUMPY_LT_2_4, + NUMPY_LT_2_4_1, +) + +SUBCLASS_SAFE_FUNCTIONS = set() +"""Functions with implementations supporting subclasses like Quantity.""" +FUNCTION_HELPERS = {} +"""Functions with implementations usable with proper unit conversion.""" +DISPATCHED_FUNCTIONS = {} +"""Functions for which we provide our own implementation.""" + +if NUMPY_LT_2_2: + # in numpy 2.2 these are auto detected by numpy itself + # xref https://github.com/numpy/numpy/issues/27451 + SUPPORTED_NEP35_FUNCTIONS = { + np.arange, + np.empty, np.ones, np.zeros, np.full, + np.array, np.asarray, np.asanyarray, np.ascontiguousarray, np.asfortranarray, + np.frombuffer, np.fromfile, np.fromfunction, np.fromiter, np.fromstring, + np.require, np.identity, np.eye, np.tri, np.genfromtxt, np.loadtxt, + } # fmt: skip + """Functions that support a 'like' keyword argument and dispatch on it (NEP 35)""" +else: + # When our minimum becomes numpy>=2.2, this can be removed, here and in the tests + SUPPORTED_NEP35_FUNCTIONS = set() + +"""Functions that support a 'like' keyword argument and dispatch on it (NEP 35)""" +UNSUPPORTED_FUNCTIONS = set() +"""Functions that cannot sensibly be used with quantities.""" +SUBCLASS_SAFE_FUNCTIONS |= { + np.shape, np.size, np.ndim, + np.reshape, np.ravel, np.moveaxis, np.rollaxis, np.swapaxes, + np.transpose, np.atleast_1d, np.atleast_2d, np.atleast_3d, + np.expand_dims, np.squeeze, np.broadcast_to, np.broadcast_arrays, + np.flip, np.fliplr, np.flipud, np.rot90, + np.argmin, np.argmax, np.argsort, np.lexsort, np.searchsorted, + np.nonzero, np.argwhere, np.flatnonzero, + np.diag_indices_from, np.triu_indices_from, np.tril_indices_from, + np.real, np.imag, np.diagonal, np.diagflat, np.empty_like, + np.compress, np.extract, np.delete, np.trim_zeros, np.roll, np.take, + np.put, np.fill_diagonal, np.tile, np.repeat, + np.split, np.array_split, np.hsplit, np.vsplit, np.dsplit, + np.stack, np.column_stack, np.hstack, np.vstack, np.dstack, + np.max, np.min, np.amax, np.amin, np.ptp, np.sum, np.cumsum, + np.prod, np.cumprod, + np.round, np.around, + np.fix, np.angle, np.i0, np.clip, + np.isposinf, np.isneginf, np.isreal, np.iscomplex, + np.mean, np.std, np.var, np.trace, + np.nanmax, np.nanmin, np.nanargmin, np.nanargmax, np.nanmean, + np.nansum, np.nancumsum, np.nanprod, np.nancumprod, + np.einsum_path, np.linspace, + np.sort, np.partition, np.meshgrid, + np.common_type, np.result_type, np.can_cast, np.min_scalar_type, + np.iscomplexobj, np.isrealobj, + np.shares_memory, np.may_share_memory, + np.apply_along_axis, np.take_along_axis, np.put_along_axis, + np.linalg.cond, np.linalg.multi_dot, +} # fmt: skip + +SUBCLASS_SAFE_FUNCTIONS |= {np.median} + +# Array-API compatible versions (matrix axes always at end). +SUBCLASS_SAFE_FUNCTIONS |= { + np.matrix_transpose, np.linalg.matrix_transpose, + np.linalg.diagonal, np.linalg.trace, + np.linalg.matrix_norm, np.linalg.vector_norm, np.linalg.vecdot, +} # fmt: skip + +# these work out of the box (and are tested), because they +# delegate to other, already wrapped functions from the np namespace +SUBCLASS_SAFE_FUNCTIONS |= { + np.linalg.cross, np.linalg.svdvals, np.linalg.tensordot, np.linalg.matmul, + np.unique_all, np.unique_counts, np.unique_inverse, np.unique_values, + np.astype, +} # fmt: skip + +# trapz was renamed to trapezoid +SUBCLASS_SAFE_FUNCTIONS |= {np.trapezoid} +if not NUMPY_LT_2_1: + SUBCLASS_SAFE_FUNCTIONS |= {np.unstack, np.cumulative_prod, np.cumulative_sum} +if not NUMPY_LT_2_4_1: + SUBCLASS_SAFE_FUNCTIONS |= {np.average} + +# Implemented as methods on Quantity: +# np.ediff1d is from setops, but we support it anyway; the others +# currently return NotImplementedError. +# TODO: move latter to UNSUPPORTED? Would raise TypeError instead. +SUBCLASS_SAFE_FUNCTIONS |= {np.ediff1d} +UNSUPPORTED_FUNCTIONS |= { + np.packbits, np.unpackbits, np.unravel_index, + np.ravel_multi_index, np.ix_, np.cov, np.corrcoef, + np.busday_count, np.busday_offset, np.datetime_as_string, + np.is_busday, np.all, np.any, +} # fmt: skip + +# Could be supported if we had a natural logarithm unit. +UNSUPPORTED_FUNCTIONS |= {np.linalg.slogdet} +TBD_FUNCTIONS = { + rfn.drop_fields, rfn.rename_fields, rfn.append_fields, rfn.join_by, + rfn.apply_along_fields, rfn.assign_fields_by_name, + rfn.find_duplicates, rfn.recursive_fill_fields, rfn.require_fields, + rfn.repack_fields, rfn.stack_arrays, +} # fmt: skip +UNSUPPORTED_FUNCTIONS |= TBD_FUNCTIONS +IGNORED_FUNCTIONS = { + # I/O - useless for Quantity, since no way to store the unit. + np.save, np.savez, np.savetxt, np.savez_compressed, + # Polynomials + np.poly, np.polyadd, np.polyder, np.polydiv, np.polyfit, np.polyint, + np.polymul, np.polysub, np.polyval, np.roots, np.vander, + # functions taking record arrays (which are deprecated) + rfn.rec_append_fields, rfn.rec_drop_fields, rfn.rec_join, +} # fmt: skip +UNSUPPORTED_FUNCTIONS |= IGNORED_FUNCTIONS + + +class FunctionAssigner: + def __init__(self, assignments): + self.assignments = assignments + + def __call__(self, f=None, helps=None, module=np): + """Add a helper to a numpy function. + + Normally used as a decorator. + + If ``helps`` is given, it should be the numpy function helped (or an + iterable of numpy functions helped). + + If ``helps`` is not given, it is assumed the function helped is the + numpy function with the same name as the decorated function. + """ + if f is not None: + if helps is None: + helps = getattr(module, f.__name__) + if not np.iterable(helps): + helps = (helps,) + for h in helps: + self.assignments[h] = f + return f + elif helps is not None or module is not np: + return functools.partial(self.__call__, helps=helps, module=module) + else: # pragma: no cover + raise ValueError("function_helper requires at least one argument.") + + +function_helper = FunctionAssigner(FUNCTION_HELPERS) + +dispatched_function = FunctionAssigner(DISPATCHED_FUNCTIONS) + + +@function_helper( + helps={ + np.copy, np.real_if_close, np.sort_complex, np.resize, + np.fft.fft, np.fft.ifft, np.fft.rfft, np.fft.irfft, + np.fft.fft2, np.fft.ifft2, np.fft.rfft2, np.fft.irfft2, + np.fft.fftn, np.fft.ifftn, np.fft.rfftn, np.fft.irfftn, + np.fft.hfft, np.fft.ihfft, + np.linalg.eigvals, np.linalg.eigvalsh, + } +) # fmt: skip +def invariant_a_helper(a, *args, **kwargs): + return (a.view(np.ndarray),) + args, kwargs, a.unit, None + + +@function_helper(helps={np.tril, np.triu}) +def invariant_m_helper(m, *args, **kwargs): + return (m.view(np.ndarray),) + args, kwargs, m.unit, None + + +@function_helper(helps={np.fft.fftshift, np.fft.ifftshift}) +def invariant_x_helper(x, *args, **kwargs): + return (x.view(np.ndarray),) + args, kwargs, x.unit, None + + +# Note that ones_like does *not* work by default since if one creates an empty +# array with a unit, one cannot just fill it with unity. Indeed, in this +# respect, it is a bit of an odd function for Quantity. On the other hand, it +# matches the idea that a unit is the same as the quantity with that unit and +# value of 1. Also, it used to work without __array_function__. +# zeros_like does work by default for regular quantities, because numpy first +# creates an empty array with the unit and then fills it with 0 (which can have +# any unit), but for structured dtype this fails (0 cannot have an arbitrary +# structured unit), so we include it here too. +@function_helper(helps={np.ones_like, np.zeros_like}) +def like_helper(a, *args, **kwargs): + subok = args[2] if len(args) > 2 else kwargs.pop("subok", True) + unit = a.unit if subok else None + return (a.view(np.ndarray),) + args, kwargs, unit, None + + +def _quantity_out_as_array(out): + from astropy.units import Quantity + + if isinstance(out, Quantity): + return out.view(np.ndarray) + else: + # TODO: for an ndarray output, one could in principle + # try converting the input to dimensionless. + raise NotImplementedError + + +# nanvar is safe for Quantity and was previously in SUBCLASS_FUNCTIONS, but it +# is not safe for Angle, since the resulting unit is inconsistent with being +# an Angle. By using FUNCTION_HELPERS, the unit gets passed through +# _result_as_quantity, which will correctly drop to Quantity. +# A side effect would be that np.nanstd then also produces Quantity; this +# is avoided by it being helped below. +@function_helper +def nanvar(a, axis=None, dtype=None, out=None, ddof=0, keepdims=np._NoValue, **kwargs): + a = _as_quantity(a) + out_array = None if out is None else _quantity_out_as_array(out) + return ( + (a.view(np.ndarray), axis, dtype, out_array, ddof, keepdims), + kwargs, + a.unit**2, + out, + ) + + +@function_helper +def nanstd(a, axis=None, dtype=None, out=None, ddof=0, keepdims=np._NoValue, **kwargs): + a = _as_quantity(a) + out_array = None if out is None else _quantity_out_as_array(out) + return ( + (a.view(np.ndarray), axis, dtype, out_array, ddof, keepdims), + kwargs, + a.unit, + out, + ) + + +@function_helper +def sinc(x): + from astropy.units.si import radian + + try: + x = x.to_value(radian) + except UnitsError: + raise UnitTypeError( + "Can only apply 'sinc' function to quantities with angle units" + ) + return (x,), {}, dimensionless_unscaled, None + + +@dispatched_function +def unwrap(p, discont=None, axis=-1, *, period=2 * np.pi): + from astropy.units.si import radian + + if discont is None: + discont = np.pi << radian + + if period == 2 * np.pi: + period <<= radian + + p, discont, period = _as_quantities(p, discont, period) + result = np.unwrap.__wrapped__( + p.to_value(radian), + discont.to_value(radian), + axis=axis, + period=period.to_value(radian), + ) + result = radian.to(p.unit, result) + return result, p.unit, None + + +@function_helper +def argpartition(a, *args, **kwargs): + return (a.view(np.ndarray),) + args, kwargs, None, None + + +@function_helper +def full_like(a, fill_value, *args, **kwargs): + unit = a.unit if kwargs.get("subok", True) else None + return (a.view(np.ndarray), a._to_own_unit(fill_value)) + args, kwargs, unit, None + + +def putmask_impl(a, /, mask, values): + from astropy.units import Quantity + + if isinstance(a, Quantity): + return (a.view(np.ndarray), mask, a._to_own_unit(values)), {}, a.unit, None + elif isinstance(values, Quantity): + return (a, mask, values.to_value(dimensionless_unscaled)), {}, None, None + else: + raise NotImplementedError + + +if not NUMPY_LT_2_4: + + @function_helper + def putmask(a, /, mask, values): + return putmask_impl(a, mask=mask, values=values) + +else: + + @function_helper + def putmask(a, mask, values): + return putmask_impl(a, mask=mask, values=values) + + +@function_helper +def place(arr, mask, vals): + from astropy.units import Quantity + + if isinstance(arr, Quantity): + return (arr.view(np.ndarray), mask, arr._to_own_unit(vals)), {}, arr.unit, None + elif isinstance(vals, Quantity): + return (arr, mask, vals.to_value(dimensionless_unscaled)), {}, None, None + else: + raise NotImplementedError + + +@function_helper +def copyto(dst, src, *args, **kwargs): + from astropy.units import Quantity + + if isinstance(dst, Quantity): + return (dst.view(np.ndarray), dst._to_own_unit(src)) + args, kwargs, None, None + elif isinstance(src, Quantity): + return (dst, src.to_value(dimensionless_unscaled)) + args, kwargs, None, None + else: + raise NotImplementedError + + +@function_helper +def nan_to_num(x, copy=True, nan=0.0, posinf=None, neginf=None): + nan = x._to_own_unit(nan) + if posinf is not None: + posinf = x._to_own_unit(posinf) + if neginf is not None: + neginf = x._to_own_unit(neginf) + return ( + (x.view(np.ndarray),), + dict(copy=True, nan=nan, posinf=posinf, neginf=neginf), + x.unit, + None, + ) + + +def _as_quantity(a): + """Convert argument to a Quantity (or raise NotImplementedError).""" + from astropy.units import Quantity + + try: + return Quantity(a, copy=None, subok=True) + except Exception: + # If we cannot convert to Quantity, we should just bail. + raise NotImplementedError + + +def _as_quantities(*args): + """Convert arguments to Quantity (or raise NotImplentedError).""" + from astropy.units import Quantity + + try: + # Note: this should keep the dtype the same + return tuple(Quantity(a, copy=None, subok=True, dtype=None) for a in args) + except Exception: + # If we cannot convert to Quantity, we should just bail. + raise NotImplementedError + + +def _quantities2arrays(*args, unit_from_first=False): + """Convert to arrays in units of the first argument that has a unit. + + If unit_from_first, take the unit of the first argument regardless + whether it actually defined a unit (e.g., dimensionless for arrays). + """ + # Turn first argument into a quantity. + q = _as_quantity(args[0]) + if len(args) == 1: + return (q.value,), q.unit + + # If we care about the unit being explicit, then check whether this + # argument actually had a unit, or was likely inferred. + if not unit_from_first and ( + q.unit is q._default_unit and not hasattr(args[0], "unit") + ): + # Here, the argument could still be things like [10*u.one, 11.*u.one]), + # i.e., properly dimensionless. So, we only override with anything + # that has a unit not equivalent to dimensionless (fine to ignore other + # dimensionless units pass, even if explicitly given). + for arg in args[1:]: + trial = _as_quantity(arg) + if not trial.unit.is_equivalent(q.unit): + # Use any explicit unit not equivalent to dimensionless. + q = trial + break + + # We use the private _to_own_unit method here instead of just + # converting everything to quantity and then do .to_value(qs0.unit) + # as we want to allow arbitrary unit for 0, inf, and nan. + try: + arrays = tuple((q._to_own_unit(arg)) for arg in args) + except TypeError: + raise NotImplementedError + + return arrays, q.unit + + +if NUMPY_LT_2_4_1: + + @function_helper + def average(a, axis=None, weights=None, returned=False, *, keepdims=np._NoValue): + a_value, a_unit = (_a := _as_quantity(a)).value, _a.unit + w_value, w_unit = ( + (None, dimensionless_unscaled) + if weights is None + else ((_w := _as_quantity(weights)).value, _w.unit) + ) + return ( + (a_value,), + { + "axis": axis, + "weights": w_value, + "returned": returned, + "keepdims": keepdims, + }, + ((a_unit, w_unit) if returned else a_unit), + None, + ) + + +def _iterable_helper(*args, out=None, **kwargs): + """Convert arguments to Quantity, and treat possible 'out'.""" + if out is not None: + kwargs["out"] = _quantity_out_as_array(out) # raises if not Quantity. + + arrays, unit = _quantities2arrays(*args) + return arrays, kwargs, unit, out + + +if NUMPY_LT_2_4: + + @function_helper + def concatenate(arrays, axis=0, out=None, **kwargs): + # TODO: make this smarter by creating an appropriately shaped + # empty output array and just filling it. + arrays, kwargs, unit, out = _iterable_helper( + *arrays, out=out, axis=axis, **kwargs + ) + return (arrays,), kwargs, unit, out + +else: + + @function_helper + def concatenate(arrays, /, axis=0, out=None, **kwargs): + # TODO: make this smarter by creating an appropriately shaped + # empty output array and just filling it. + arrays, kwargs, unit, out = _iterable_helper( + *arrays, out=out, axis=axis, **kwargs + ) + return (arrays,), kwargs, unit, out + + +def _block(arrays, max_depth, result_ndim, depth=0): + # Block by concatenation, copied from np._core.shape_base, + # but ensuring that we call regular concatenate. + if depth < max_depth: + arrs = [_block(arr, max_depth, result_ndim, depth + 1) for arr in arrays] + # The one difference with the numpy code. + return np.concatenate(arrs, axis=-(max_depth - depth)) + else: + return np_core.shape_base._atleast_nd(arrays, result_ndim) + + +UNIT_FROM_LIKE_ARG = object() + + +def unwrap_arange_args(*, start_or_stop, stop_, step_): + # handle the perilous task of disentangling original arguments + # This isn't trivial because start_or_stop may actually bind to two + # different inputs, as the name suggests. + # We (ab)use structural pattern matching here to bind output variables + # (start, stop, step), so no additional logic is actually needed after + # a match is found. + match (start_or_stop, stop_, step_): + case (stop, None as start, step): + pass + case (start, stop, step): + pass + + # purely defensive programming + assert stop is not None, "Please report this." + return start, stop, step + + +def wrap_arange_args(*, start, stop, step, expected_out_unit): + # do the reverse operation than unwrap_arange_args + # this is needed because start_or_stop *must* be passed as positional + + # purely defensive programming + assert stop is not None, "Please report this." + + match start, stop: + case (None, _): + qty_args = (stop,) + case _: + qty_args = (start, stop) + + step_val = step.to_value(expected_out_unit) if hasattr(step, "unit") else step + + kwargs = {} if step == 1 else {"step": step_val} + + # reverse positional arguments so `stop` always comes first + # this is done to ensure that the arrays are first converted to the + # expected unit, which we guarantee should be stop's + args_rev, out_unit = _quantities2arrays(*qty_args[::-1]) + if expected_out_unit is not UNIT_FROM_LIKE_ARG: + assert out_unit == expected_out_unit + if hasattr(stop, "unit"): + assert out_unit == stop.unit + + # reverse args again to restore initial order + args = args_rev[::-1] + + if "step" in kwargs: + kwargs["step"] = step_val + return args, kwargs + + +@function_helper +def arange( + start_or_stop, + /, + stop=None, + step=1, + *, + dtype=None, + device=None, +): + # Because this wrapper requires exceptional amounts of additional logic + # to unwrap/wrap its complicated signature, we'll sprinkle a few + # sanity checks in the form of `assert` statements, which should help making + # heads or tails of what's happening in the event of an unexpected exception. + # + # also note that we intentionally choose to match numpy.arange's signature + # at typecheck time, as opposed to its actual runtime signature, which is + # even richer (as of numpy 2.4). For instance, this means we don't support + # `start` being passed as keyword, or `dtype` being passed as positional. + # This is done to improve the overall stability and maintainability of this + # complicated wrapper function. + start, stop, step = unwrap_arange_args( + start_or_stop=start_or_stop, stop_=stop, step_=step + ) + out_unit = getattr(stop, "unit", UNIT_FROM_LIKE_ARG) + + if out_unit is UNIT_FROM_LIKE_ARG and ( + hasattr(start, "unit") or hasattr(step, "unit") + ): + raise TypeError( + "stop without a unit cannot be combined with start or step with a unit." + ) + + args, kwargs = wrap_arange_args( + start=start, stop=stop, step=step, expected_out_unit=out_unit + ) + + kwargs["dtype"] = dtype + kwargs["device"] = device + + return args, kwargs, out_unit, None + + +@function_helper(helps={np.empty, np.ones, np.zeros}) +def creation_helper(shape, dtype=None, order="C", *, device=None): + return (shape, dtype, order), {"device": device}, UNIT_FROM_LIKE_ARG, None + + +@function_helper +def full(shape, fill_value, dtype=None, order="C", *, device=None): + out_unit = getattr(fill_value, "unit", UNIT_FROM_LIKE_ARG) + if out_unit is not UNIT_FROM_LIKE_ARG: + fill_value = _as_quantity(fill_value).value + return (shape, fill_value, dtype, order), {"device": device}, out_unit, None + + +@function_helper +def require(a, dtype=None, requirements=None): + out_unit = getattr(a, "unit", UNIT_FROM_LIKE_ARG) + if out_unit is not UNIT_FROM_LIKE_ARG: + a = _as_quantity(a).value + return (a, dtype, requirements), {}, out_unit, None + + +if not NUMPY_LT_2_4: + + @function_helper + def array( + object, dtype=None, *, copy=True, order="K", subok=False, ndmin=0, ndmax=0 + ): + return array_impl( + object, + dtype=dtype, + copy=copy, + order=order, + subok=subok, + ndmin=ndmin, + ndmax=ndmax, + ) + +else: + + @function_helper + def array(object, dtype=None, *, copy=True, order="K", subok=False, ndmin=0): + return array_impl( + object, dtype=dtype, copy=copy, order=order, subok=subok, ndmin=ndmin + ) + + +def array_impl(object, *, dtype, copy, order, subok, ndmin, ndmax=0): + out_unit = getattr(object, "unit", UNIT_FROM_LIKE_ARG) + if out_unit is not UNIT_FROM_LIKE_ARG: + object = _as_quantity(object).value + kwargs = {"copy": copy, "order": order, "subok": subok, "ndmin": ndmin} + if not NUMPY_LT_2_4: + kwargs |= {"ndmax": ndmax} + return (object, dtype), kwargs, out_unit, None + + +if NUMPY_LT_2_1: + asarray_impl_1_helps = {np.asanyarray} + asarray_impl_2_helps = {np.asarray} +else: + asarray_impl_1_helps = set() + asarray_impl_2_helps = {np.asarray, np.asanyarray} + + +@function_helper(helps=asarray_impl_1_helps) +def asarray_impl_1(a, dtype=None, order=None): + out_unit = getattr(a, "unit", UNIT_FROM_LIKE_ARG) + if out_unit is not UNIT_FROM_LIKE_ARG: + a = _as_quantity(a).value + return (a, dtype, order), {}, out_unit, None + + +@function_helper(helps=asarray_impl_2_helps) +def asarray_impl_2(a, dtype=None, order=None, *, device=None, copy=None): + out_unit = getattr(a, "unit", UNIT_FROM_LIKE_ARG) + if out_unit is not UNIT_FROM_LIKE_ARG: + a = _as_quantity(a).value + return (a, dtype, order), {"device": device, "copy": copy}, out_unit, None + + +@function_helper(helps={np.ascontiguousarray, np.asfortranarray}) +def aslayoutarray_helper(a, dtype=None): + out_unit = getattr(a, "unit", UNIT_FROM_LIKE_ARG) + if out_unit is not UNIT_FROM_LIKE_ARG: + a = _as_quantity(a).value + return (a, dtype), {}, out_unit, None + + +@function_helper +def fromfunction(function, shape, *, dtype=float, **kwargs): + zero_arg = np.zeros(len(shape), dtype) + try: + out_unit = function(*zero_arg).unit + except Exception: + out_unit = UNIT_FROM_LIKE_ARG + return (function, shape), {"dtype": dtype, **kwargs}, out_unit, None + + +@function_helper(helps={ + np.frombuffer, np.fromfile, np.fromiter, np.fromstring, + np.identity, np.eye, np.tri, + np.genfromtxt, np.loadtxt, + } +) # fmt: skip +def generic_like_array_function_helper(*args, **kwargs): + return args, kwargs, UNIT_FROM_LIKE_ARG, None + + +@dispatched_function +def block(arrays): + # We need to override block since the numpy implementation can take two + # different paths, one for concatenation, one for creating a large empty + # result array in which parts are set. Each assumes array input and + # cannot be used directly. Since it would be very costly to inspect all + # arrays and then turn them back into a nested list, we just copy here the + # first implementation, np.core.shape_base._block, which is the easiest to + # adjust while making sure that both units and class are properly kept. + (arrays, list_ndim, result_ndim, final_size) = np_core.shape_base._block_setup( + arrays + ) + result = _block(arrays, list_ndim, result_ndim) + if list_ndim == 0: + result = result.copy() + return result, None, None + + +@function_helper +def choose(a, choices, out=None, mode="raise"): + choices, kwargs, unit, out = _iterable_helper(*choices, out=out, mode=mode) + return (a, choices), kwargs, unit, out + + +@function_helper +def select(condlist, choicelist, default=0): + choicelist, kwargs, unit, out = _iterable_helper(*choicelist) + if default != 0: + default = (1 * unit)._to_own_unit(default) + return (condlist, choicelist, default), kwargs, unit, out + + +@dispatched_function +def piecewise(x, condlist, funclist, *args, **kw): + from astropy.units import Quantity + + # Copied implementation from numpy.lib._function_base_impl.piecewise, + # taking care of units of function outputs. + n2 = len(funclist) + # undocumented: single condition is promoted to a list of one condition + if np.isscalar(condlist) or ( + not isinstance(condlist[0], (list, np.ndarray)) and x.ndim != 0 + ): + condlist = [condlist] + + if any(isinstance(c, Quantity) for c in condlist): + raise NotImplementedError + + condlist = np.array(condlist, dtype=bool) + n = len(condlist) + + if n == n2 - 1: # compute the "otherwise" condition. + condelse = ~np.any(condlist, axis=0, keepdims=True) + condlist = np.concatenate([condlist, condelse], axis=0) + n += 1 + elif n != n2: + raise ValueError( + f"with {n} condition(s), either {n} or {n + 1} functions are expected" + ) + + y = np.zeros(x.shape, x.dtype) + where = [] + what = [] + for k in range(n): + item = funclist[k] + if not callable(item): + where.append(condlist[k]) + what.append(item) + else: + vals = x[condlist[k]] + if vals.size > 0: + where.append(condlist[k]) + what.append(item(vals, *args, **kw)) + + what, unit = _quantities2arrays(*what) + for item, value in zip(where, what): + y[item] = value + + return y, unit, None + + +@function_helper +def append(arr, values, *args, **kwargs): + arrays, unit = _quantities2arrays(arr, values, unit_from_first=True) + return arrays + args, kwargs, unit, None + + +@function_helper +def insert(arr, obj, values, *args, **kwargs): + from astropy.units import Quantity + + if isinstance(obj, Quantity): + raise NotImplementedError + + (arr, values), unit = _quantities2arrays(arr, values, unit_from_first=True) + return (arr, obj, values) + args, kwargs, unit, None + + +@function_helper +def pad(array, pad_width, mode="constant", **kwargs): + # pad dispatches only on array, so that must be a Quantity. + for key in "constant_values", "end_values": + value = kwargs.pop(key, None) + if value is None: + continue + if not isinstance(value, tuple): + value = (value,) + + new_value = [] + for v in value: + new_value.append( + tuple(array._to_own_unit(_v) for _v in v) + if isinstance(v, tuple) + else array._to_own_unit(v) + ) + kwargs[key] = new_value + + return (array.view(np.ndarray), pad_width, mode), kwargs, array.unit, None + + +@function_helper +def where(condition, /, *args): + from astropy.units import Quantity + + if isinstance(condition, Quantity) or len(args) != 2: + raise NotImplementedError + + args, unit = _quantities2arrays(*args) + return (condition,) + args, {}, unit, None + + +@function_helper(helps=({np.quantile, np.nanquantile})) +def quantile(a, q, *args, _q_unit=dimensionless_unscaled, **kwargs): + if len(args) >= 2: + out = args[1] + args = args[:1] + args[2:] + else: + out = kwargs.pop("out", None) + + from astropy.units import Quantity + + if isinstance(q, Quantity): + q = q.to_value(_q_unit) + + (a,), kwargs, unit, out = _iterable_helper(a, out=out, **kwargs) + + return (a, q) + args, kwargs, unit, out + + +@function_helper(helps={np.percentile, np.nanpercentile}) +def percentile(a, q, *args, **kwargs): + from astropy.units import percent + + return quantile(a, q, *args, _q_unit=percent, **kwargs) + + +@function_helper +def nanmedian(a, axis=None, out=None, overwrite_input=False, keepdims=np._NoValue): + return _iterable_helper( + a, axis=axis, out=out, overwrite_input=overwrite_input, keepdims=keepdims + ) + + +@function_helper +def count_nonzero(a, *args, **kwargs): + return (a.value,) + args, kwargs, None, None + + +@function_helper(helps={np.isclose, np.allclose}) +def close(a, b, rtol=1e-05, atol=1e-08, *args, **kwargs): + from astropy.units import Quantity + + (a, b), unit = _quantities2arrays(a, b, unit_from_first=True) + # Allow number without a unit as having the unit. + atol = Quantity(atol, unit).value + + return (a, b, rtol, atol) + args, kwargs, None, None + + +@dispatched_function +def array_equal(a1, a2, equal_nan=False): + try: + args, unit = _quantities2arrays(a1, a2) + except UnitConversionError: + return False, None, None + return np.array_equal(*args, equal_nan=equal_nan), None, None + + +@dispatched_function +def array_equiv(a1, a2): + try: + args, unit = _quantities2arrays(a1, a2) + except UnitConversionError: + return False, None, None + return np.array_equiv(*args), None, None + + +@function_helper(helps={np.dot, np.outer}) +def dot_like(a, b, out=None): + from astropy.units import Quantity + + a, b = _as_quantities(a, b) + unit = a.unit * b.unit + if out is not None: + if not isinstance(out, Quantity): + raise NotImplementedError + return tuple(x.view(np.ndarray) for x in (a, b, out)), {}, unit, out + else: + return (a.view(np.ndarray), b.view(np.ndarray)), {}, unit, None + + +@function_helper( + helps={ + np.cross, + np.kron, + np.tensordot, + } +) +def cross_like_a_b(a, b, *args, **kwargs): + a, b = _as_quantities(a, b) + unit = a.unit * b.unit + return (a.view(np.ndarray), b.view(np.ndarray)) + args, kwargs, unit, None + + +@function_helper(helps={np.inner, np.vdot}) +def cross_like_a_b_posonly(a, b, /): + a, b = _as_quantities(a, b) + unit = a.unit * b.unit + return (a.view(np.ndarray), b.view(np.ndarray)), {}, unit, None + + +@function_helper(helps={np.correlate, np.convolve}) +def cross_like_a_v(a, v, *args, **kwargs): + a, v = _as_quantities(a, v) + unit = a.unit * v.unit + return (a.view(np.ndarray), v.view(np.ndarray)) + args, kwargs, unit, None + + +@function_helper +def einsum(*operands, out=None, **kwargs): + subscripts, *operands = operands + + if not isinstance(subscripts, str): + raise ValueError('only "subscripts" string mode supported for einsum.') + + if out is not None: + kwargs["out"] = _quantity_out_as_array(out) + + qs = _as_quantities(*operands) + unit = functools.reduce(operator.mul, (q.unit for q in qs), dimensionless_unscaled) + arrays = tuple(q.view(np.ndarray) for q in qs) + return (subscripts,) + arrays, kwargs, unit, out + + +@function_helper +def bincount(x, /, weights=None, minlength=0): + from astropy.units import Quantity + + if isinstance(x, Quantity): + raise NotImplementedError + return (x, weights.value, minlength), {}, weights.unit, None + + +@function_helper +def digitize(x, bins, *args, **kwargs): + arrays, unit = _quantities2arrays(x, bins, unit_from_first=True) + return arrays + args, kwargs, None, None + + +def _check_bins(bins, unit): + from astropy.units import Quantity + + check = _as_quantity(bins) + if check.ndim > 0: + return check.to_value(unit) + elif isinstance(bins, Quantity): + # bins should be an integer (or at least definitely not a Quantity). + raise NotImplementedError + else: + return bins + + +def _check_range(range, unit): + range = _as_quantity(range) + range = range.to_value(unit) + return range + + +@function_helper +def histogram_bin_edges(a, bins=10, range=None, weights=None): + # weights is currently unused + a = _as_quantity(a) + if not isinstance(bins, str): + bins = _check_bins(bins, a.unit) + + if range is not None: + range = _check_range(range, a.unit) + + return (a.value, bins, range, weights), {}, a.unit, None + + +@function_helper +def histogram(a, bins=10, range=None, density=None, weights=None): + if weights is not None: + weights = _as_quantity(weights) + unit = weights.unit + weights = weights.value + else: + unit = None + + a = _as_quantity(a) + if not isinstance(bins, str): + bins = _check_bins(bins, a.unit) + + if range is not None: + range = _check_range(range, a.unit) + + if density: + unit = (unit or 1) / a.unit + + return ( + (a.value, bins, range), + {"weights": weights, "density": density}, + (unit, a.unit), + None, + ) + + +@function_helper +def histogram2d(x, y, bins=10, range=None, density=None, weights=None): + from astropy.units import Quantity + + if weights is not None: + weights = _as_quantity(weights) + unit = weights.unit + weights = weights.value + else: + unit = None + + x, y = _as_quantities(x, y) + try: + n = len(bins) + except TypeError: + # bins should be an integer (or at least definitely not a Quantity). + if isinstance(bins, Quantity): + raise NotImplementedError + + else: + if n == 1: + raise NotImplementedError + elif n == 2 and not isinstance(bins, Quantity): + bins = [_check_bins(b, unit) for (b, unit) in zip(bins, (x.unit, y.unit))] + else: + bins = _check_bins(bins, x.unit) + y = y.to(x.unit) + + if range is not None: + range = tuple( + _check_range(r, unit) for (r, unit) in zip(range, (x.unit, y.unit)) + ) + + if density: + unit = (unit or 1) / x.unit / y.unit + + return ( + (x.value, y.value, bins, range), + {"weights": weights, "density": density}, + (unit, x.unit, y.unit), + None, + ) + + +@function_helper +def histogramdd(sample, bins=10, range=None, density=None, weights=None): + if weights is not None: + weights = _as_quantity(weights) + unit = weights.unit + weights = weights.value + else: + unit = None + + try: + # Sample is an ND-array. + _, D = sample.shape + except (AttributeError, ValueError): + # Sample is a sequence of 1D arrays. + sample = _as_quantities(*sample) + sample_units = [s.unit for s in sample] + sample = [s.value for s in sample] + D = len(sample) + else: + sample = _as_quantity(sample) + sample_units = [sample.unit] * D + + try: + M = len(bins) + except TypeError: + # bins should be an integer + from astropy.units import Quantity + + if isinstance(bins, Quantity): + raise NotImplementedError + else: + if M != D: + raise ValueError( + "The dimension of bins must be equal to the dimension of the sample x." + ) + bins = [_check_bins(b, unit) for (b, unit) in zip(bins, sample_units)] + + if range is not None: + range = tuple(_check_range(r, unit) for (r, unit) in zip(range, sample_units)) + + if density: + unit = functools.reduce(operator.truediv, sample_units, (unit or 1)) + + return ( + (sample, bins, range), + {"weights": weights, "density": density}, + (unit, sample_units), + None, + ) + + +@function_helper +def diff(a, n=1, axis=-1, prepend=np._NoValue, append=np._NoValue): + a = _as_quantity(a) + if prepend is not np._NoValue: + prepend = _as_quantity(prepend).to_value(a.unit) + if append is not np._NoValue: + append = _as_quantity(append).to_value(a.unit) + return (a.value, n, axis, prepend, append), {}, a.unit, None + + +@function_helper +def gradient(f, *varargs, **kwargs): + f = _as_quantity(f) + axis = kwargs.get("axis") + if axis is None: + n_axis = f.ndim + elif isinstance(axis, tuple): + n_axis = len(axis) + else: + n_axis = 1 + + if varargs: + varargs = _as_quantities(*varargs) + if len(varargs) == 1 and n_axis > 1: + varargs = varargs * n_axis + + if varargs: + units = [f.unit / q.unit for q in varargs] + varargs = tuple(q.value for q in varargs) + else: + units = [f.unit] * n_axis + + if len(units) == 1: + units = units[0] + + return (f.value,) + varargs, kwargs, units, None + + +@function_helper +def logspace(start, stop, *args, **kwargs): + from astropy.units import LogQuantity, dex + + if not isinstance(start, LogQuantity) or not isinstance(stop, LogQuantity): + raise NotImplementedError + + # Get unit from end point as for linspace. + stop = stop.to(dex(stop.unit.physical_unit)) + start = start.to(stop.unit) + unit = stop.unit.physical_unit + return (start.value, stop.value) + args, kwargs, unit, None + + +@function_helper +def geomspace(start, stop, *args, **kwargs): + # Get unit from end point as for linspace. + (stop, start), unit = _quantities2arrays(stop, start) + return (start, stop) + args, kwargs, unit, None + + +@function_helper +def interp(x, xp, fp, *args, **kwargs): + from astropy.units import Quantity + + (x, xp), _ = _quantities2arrays(x, xp) + if isinstance(fp, Quantity): + unit = fp.unit + fp = fp.value + else: + unit = None + + return (x, xp, fp) + args, kwargs, unit, None + + +@function_helper +def unique( + ar, + return_index=False, + return_inverse=False, + return_counts=False, + axis=None, + *, + equal_nan=True, + **kwargs, +): + # having **kwargs allows to support sorted (for not NUMPY_LT_2_3) without + # introducing it pre-maturely in older supported numpy versions + unit = ar.unit + n_index = sum(bool(i) for i in (return_index, return_inverse, return_counts)) + if n_index: + unit = [unit] + n_index * [None] + + return ( + (ar.value, return_index, return_inverse, return_counts, axis), + kwargs | {"equal_nan": equal_nan}, + unit, + None, + ) + + +@function_helper +def intersect1d(ar1, ar2, assume_unique=False, return_indices=False): + (ar1, ar2), unit = _quantities2arrays(ar1, ar2) + if return_indices: + unit = [unit, None, None] + return (ar1, ar2, assume_unique, return_indices), {}, unit, None + + +@function_helper(helps=(np.setxor1d, np.union1d, np.setdiff1d)) +def twosetop(ar1, ar2, *args, **kwargs): + (ar1, ar2), unit = _quantities2arrays(ar1, ar2) + return (ar1, ar2) + args, kwargs, unit, None + + +@function_helper +def isin(element, test_elements, *args, **kwargs): + # This tests whether element is in test_elements, so we should change the unit of + # element to that of test_elements. + (ar1, ar2), unit = _quantities2arrays(element, test_elements) + return (ar1, ar2) + args, kwargs, None, None + + +if NUMPY_LT_2_4: + # np.in1d was removed numpy 2.4 + @function_helper + def in1d(ar1, ar2, *args, **kwargs): + # This tests whether ar1 is in ar2, so we should change the unit of + # ar1 to that of ar2. + (ar2, ar1), unit = _quantities2arrays(ar2, ar1) + return (ar1, ar2) + args, kwargs, None, None + + +@dispatched_function +def apply_over_axes(func, a, axes): + # Copied straight from numpy/lib/shape_base, just to omit its + # val = asarray(a); if only it had been asanyarray, or just not there + # since a is assumed to an an array in the next line... + # Which is what we do here - we can only get here if it is a Quantity. + val = a + N = a.ndim + if np.array(axes).ndim == 0: + axes = (axes,) + for axis in axes: + if axis < 0: + axis = N + axis + args = (val, axis) + res = func(*args) + if res.ndim == val.ndim: + val = res + else: + res = np.expand_dims(res, axis) + if res.ndim == val.ndim: + val = res + else: + raise ValueError( + "function is not returning an array of the correct shape" + ) + # Returning unit is None to signal nothing should happen to + # the output. + return val, None, None + + +@dispatched_function +def array_repr(arr, *args, **kwargs): + # TODO: The addition of "unit='...'" doesn't worry about line + # length. Could copy & adapt _array_repr_implementation from + # numpy.core.arrayprint.py + cls_name = arr.__class__.__name__ + fake_name = "_" * len(cls_name) + fake_cls = type(fake_name, (np.ndarray,), {}) + no_unit = np.array_repr(arr.view(fake_cls), *args, **kwargs).replace( + fake_name, cls_name + ) + unit_part = f"unit='{arr.unit}'" + pre, dtype, post = no_unit.rpartition("dtype") + if dtype: + return f"{pre}{unit_part}, {dtype}{post}", None, None + else: + return f"{no_unit[:-1]}, {unit_part})", None, None + + +@dispatched_function +def array_str(a, *args, **kwargs): + # TODO: The addition of the unit doesn't worry about line length. + # Could copy & adapt _array_repr_implementation from + # numpy.core.arrayprint.py + no_unit = np.array_str(a.value, *args, **kwargs) + return no_unit + a._unitstr, None, None + + +@function_helper +def array2string(a, *args, **kwargs): + # array2string breaks on quantities as it tries to turn individual + # items into float, which works only for dimensionless. Since the + # defaults would not keep any unit anyway, this is rather pointless - + # we're better off just passing on the array view. However, one can + # also work around this by passing on a formatter (as is done in Angle). + # So, we do nothing if the formatter argument is present and has the + # relevant formatter for our dtype. + if NUMPY_LT_2_4: + formatter = args[6] if len(args) >= 7 else kwargs.get("formatter") + else: + formatter = kwargs.get("formatter") + + if formatter is None: + a = a.value + else: + # See whether it covers our dtype. + from numpy._core.arrayprint import _get_format_function, _make_options_dict + + with np.printoptions(formatter=formatter) as options: + options = _make_options_dict(**options) + try: + ff = _get_format_function(a.value, **options) + except Exception: + # Shouldn't happen, but possibly we're just not being smart + # enough, so let's pass things on as is. + pass + else: + # If the selected format function is that of numpy, we know + # things will fail if we pass in the Quantity, so use .value. + if "numpy" in ff.__module__: + a = a.value + + return (a,) + args, kwargs, None, None + + +@function_helper +def diag(v, *args, **kwargs): + # Function works for *getting* the diagonal, but not *setting*. + # So, override always. + return (v.value,) + args, kwargs, v.unit, None + + +@function_helper(module=np.linalg) +def svd(a, full_matrices=True, compute_uv=True, hermitian=False): + unit = a.unit + if compute_uv: + unit = (None, unit, None) + + return ((a.view(np.ndarray), full_matrices, compute_uv, hermitian), {}, unit, None) + + +def _interpret_tol(tol, unit): + from astropy.units import Quantity + + return Quantity(tol, unit).value + + +@function_helper(module=np.linalg) +def matrix_rank(A, tol=None, *args, **kwargs): + if tol is not None: + tol = _interpret_tol(tol, A.unit) + + if (atol := kwargs.get("atol")) is not None: + kwargs["atol"] = _interpret_tol(atol, A.unit) + + if (rtol := kwargs.get("rtol")) is not None: + # Handle relative tolerance (should be dimensionless) + kwargs["rtol"] = _interpret_tol(rtol, dimensionless_unscaled) + + return (A.view(np.ndarray), tol) + args, kwargs, None, None + + +@function_helper(helps={np.linalg.inv, np.linalg.tensorinv}) +def inv(a, *args, **kwargs): + return (a.view(np.ndarray),) + args, kwargs, 1 / a.unit, None + + +@function_helper(module=np.linalg) +def pinv(a, rcond=None, hermitian=False, *, rtol=np._NoValue): + if rcond is not None: + rcond = _interpret_tol(rcond, a.unit) + if rtol is not np._NoValue and rtol is not None: + rtol = _interpret_tol(rtol, a.unit) + + return ( + (a.view(np.ndarray),), + dict(rcond=rcond, hermitian=hermitian, rtol=rtol), + 1 / a.unit, + None, + ) + + +@function_helper(module=np.linalg) +def det(a): + return (a.view(np.ndarray),), {}, a.unit ** a.shape[-1], None + + +@function_helper(helps={np.linalg.solve, np.linalg.tensorsolve}) +def solve(a, b, *args, **kwargs): + a, b = _as_quantities(a, b) + + return ( + (a.view(np.ndarray), b.view(np.ndarray)) + args, + kwargs, + b.unit / a.unit, + None, + ) + + +@function_helper(module=np.linalg) +def lstsq(a, b, rcond=None): + a, b = _as_quantities(a, b) + + if rcond not in (None, "warn", -1): + rcond = _interpret_tol(rcond, a.unit) + + return ( + (a.view(np.ndarray), b.view(np.ndarray), rcond), + {}, + (b.unit / a.unit, b.unit**2, None, a.unit), + None, + ) + + +@function_helper(module=np.linalg) +def norm(x, ord=None, *args, **kwargs): + if ord == 0: + from astropy.units import dimensionless_unscaled + + unit = dimensionless_unscaled + else: + unit = x.unit + return (x.view(np.ndarray), ord) + args, kwargs, unit, None + + +@function_helper(module=np.linalg) +def matrix_power(a, n): + return (a.value, n), {}, a.unit**n, None + + +@function_helper(module=np.linalg) +def cholesky(a, /, *, upper=False): + return (a.value,), {"upper": upper}, a.unit**0.5, None + + +@function_helper(module=np.linalg) +def qr(a, mode="reduced"): + if mode.startswith("e"): + units = None + elif mode == "r": + units = a.unit + else: + from astropy.units import dimensionless_unscaled + + units = (dimensionless_unscaled, a.unit) + + return (a.value, mode), {}, units, None + + +@function_helper(helps={np.linalg.eig, np.linalg.eigh}) +def eig(a, *args, **kwargs): + from astropy.units import dimensionless_unscaled + + return (a.value,) + args, kwargs, (a.unit, dimensionless_unscaled), None + + +@function_helper(module=np.linalg) +def outer(x1, x2, /): + # maybe this one can be marked as subclass-safe in the near future ? + # see https://github.com/numpy/numpy/pull/25101#discussion_r1419879122 + x1, x2 = _as_quantities(x1, x2) + return (x1.view(np.ndarray), x2.view(np.ndarray)), {}, x1.unit * x2.unit, None + + +# ======================= np.lib.recfunctions ======================= + + +@function_helper(module=np.lib.recfunctions) +def structured_to_unstructured(arr, *args, **kwargs): + """ + Convert a structured quantity to an unstructured one. + This only works if all the units are compatible. + + """ + from astropy.units import StructuredUnit + + target_unit = arr.unit.values()[0] + + def replace_unit(x): + if isinstance(x, StructuredUnit): + return x._recursively_apply(replace_unit) + else: + return target_unit + + to_unit = arr.unit._recursively_apply(replace_unit) + return (arr.to_value(to_unit),) + args, kwargs, target_unit, None + + +def _build_structured_unit(dtype, unit): + """Build structured unit from dtype. + + Parameters + ---------- + dtype : `numpy.dtype` + unit : `astropy.units.Unit` + + Returns + ------- + `astropy.units.Unit` or tuple + """ + if dtype.fields is None: + return unit + + return tuple(_build_structured_unit(v[0], unit) for v in dtype.fields.values()) + + +@function_helper(module=np.lib.recfunctions) +def unstructured_to_structured(arr, dtype=None, *args, **kwargs): + from astropy.units import StructuredUnit + + target_unit = StructuredUnit(_build_structured_unit(dtype, arr.unit)) + + return (arr.to_value(arr.unit), dtype) + args, kwargs, target_unit, None + + +def _izip_units_flat(iterable): + """Returns an iterator of collapsing any nested unit structure. + + Parameters + ---------- + iterable : Iterable[StructuredUnit | Unit] or StructuredUnit + A structured unit or iterable thereof. + + Yields + ------ + unit + """ + from astropy.units import StructuredUnit + + # Make Structured unit (pass-through if it is already). + units = StructuredUnit(iterable) + + # Yield from structured unit. + for v in units.values(): + if isinstance(v, StructuredUnit): + yield from _izip_units_flat(v) + else: + yield v + + +@function_helper(helps=rfn.merge_arrays) +def merge_arrays( + seqarrays, + fill_value=-1, + flatten=False, + usemask=False, + asrecarray=False, +): + """Merge structured Quantities field by field. + + Like :func:`numpy.lib.recfunctions.merge_arrays`. Note that ``usemask`` and + ``asrecarray`` are not supported at this time and will raise a ValueError if + not `False`. + """ + from astropy.units import Quantity, StructuredUnit + + if asrecarray: + # TODO? implement if Quantity ever supports rec.array + raise ValueError("asrecarray=True is not supported.") + if usemask: + # TODO: use MaskedQuantity for this case + raise ValueError("usemask=True is not supported.") + + # Do we have a single Quantity as input? + if isinstance(seqarrays, Quantity): + seqarrays = (seqarrays,) + + # Note: this also converts ndarray -> Quantity[dimensionless] + seqarrays = _as_quantities(*seqarrays) + arrays = tuple(q.value for q in seqarrays) + units = tuple(q.unit for q in seqarrays) + + if flatten: + unit = StructuredUnit(tuple(_izip_units_flat(units))) + elif len(arrays) == 1: + unit = StructuredUnit(units[0]) + else: + unit = StructuredUnit(units) + + return ( + (arrays,), + dict( + fill_value=fill_value, + flatten=flatten, + usemask=usemask, + asrecarray=asrecarray, + ), + unit, + None, + ) diff --git a/astropy/units/quantity_helper/helpers.py b/astropy/units/quantity_helper/helpers.py new file mode 100644 index 000000000000..0575cc3cca3e --- /dev/null +++ b/astropy/units/quantity_helper/helpers.py @@ -0,0 +1,585 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +# The idea for this module (but no code) was borrowed from the +# quantities (http://pythonhosted.org/quantities/) package. +"""Helper functions for Quantity. + +In particular, this implements the logic that determines scaling and result +units for a given ufunc, given input units. +""" + +from fractions import Fraction + +import numpy as np +from numpy._core import umath as np_umath + +from astropy.units.core import dimensionless_unscaled, unit_scale_converter +from astropy.units.errors import UnitConversionError, UnitsError, UnitTypeError +from astropy.utils.compat.numpycompat import ( + NUMPY_LT_2_1, + NUMPY_LT_2_2, + NUMPY_LT_2_3, + NUMPY_LT_2_5, +) + +from . import UFUNC_HELPERS, UNSUPPORTED_UFUNCS + + +def _d(unit): + if unit is None: + return dimensionless_unscaled + else: + return unit + + +def get_converter(from_unit, to_unit): + """Like Unit.get_converter, except returns None if no scaling is needed, + i.e., if the inferred scale is unity. + """ + try: + converter = from_unit.get_converter(to_unit) + except AttributeError as exc: + # Check for lack of unit only now, to avoid delay for cases where a unit + # was present. Note that cases where dimensionless is expected are + # already short-circuited; here, we cover just the case where, e.g., the + # user has done u.add_enabled_equivalencies(u.dimensionless_angles()). + if from_unit is not None: # pragma: no cover + raise + try: + converter = dimensionless_unscaled.get_converter(to_unit) + except UnitsError: + exc.add_note( + "Input without a 'unit' attribute? Such input is treated " + f"as dimensionless and cannot be converted to {to_unit}." + ) + raise exc + + return None if converter is unit_scale_converter else converter + + +def get_converters_and_unit(f, unit1, unit2): + converters = [None, None] + # By default, we try adjusting unit2 to unit1, so that the result will + # be unit1 as well. But if there is no second unit, we have to try + # adjusting unit1 (to dimensionless, see below). + if unit2 is None: + if unit1 is None: + # No units for any input -- e.g., np.add(a1, a2, out=q) + return converters, dimensionless_unscaled + changeable = 0 + # swap units. + unit2 = unit1 + unit1 = None + elif unit2 is unit1: + # ensure identical units is fast ("==" is slow, so avoid that). + return converters, unit1 + else: + changeable = 1 + + # Try to get a converter from unit2 to unit1. + if unit1 is None: + try: + converters[changeable] = get_converter(unit2, dimensionless_unscaled) + except UnitsError: + # special case: would be OK if unitless number is zero, inf, nan + converters[1 - changeable] = False + return converters, unit2 + else: + return converters, dimensionless_unscaled + else: + try: + converters[changeable] = get_converter(unit2, unit1) + except UnitsError: + raise UnitConversionError( + f"Can only apply '{f.__name__}' function to quantities " + "with compatible dimensions" + ) + + return converters, unit1 + + +# SINGLE ARGUMENT UFUNC HELPERS +# +# The functions below take a single argument, which is the quantity upon which +# the ufunc is being used. The output of the helper function should be two +# values: a list with a single converter to be used to scale the input before +# it is being passed to the ufunc (or None if no conversion is needed), and +# the unit the output will be in. + + +def helper_onearg_test(f, unit): + return ([None], None) + + +def helper_invariant(f, unit): + return ([None], _d(unit)) + + +def helper_square(f, unit): + return ([None], unit**2 if unit is not None else dimensionless_unscaled) + + +def helper_reciprocal(f, unit): + return ([None], unit**-1 if unit is not None else dimensionless_unscaled) + + +one_half = 0.5 # faster than Fraction(1, 2) +one_third = Fraction(1, 3) + + +def helper_sqrt(f, unit): + return ([None], unit**one_half if unit is not None else dimensionless_unscaled) + + +def helper_cbrt(f, unit): + return ([None], (unit**one_third if unit is not None else dimensionless_unscaled)) + + +def helper_modf(f, unit): + if unit is None: + return [None], (dimensionless_unscaled, dimensionless_unscaled) + + try: + return ( + [get_converter(unit, dimensionless_unscaled)], + (dimensionless_unscaled, dimensionless_unscaled), + ) + except UnitsError: + raise UnitTypeError( + f"Can only apply '{f.__name__}' function to dimensionless quantities" + ) + + +def helper__ones_like(f, unit): + return [None], dimensionless_unscaled + + +def helper_dimensionless_to_dimensionless(f, unit): + if unit is None: + return [None], dimensionless_unscaled + + try: + return ([get_converter(unit, dimensionless_unscaled)], dimensionless_unscaled) + except UnitsError: + raise UnitTypeError( + f"Can only apply '{f.__name__}' function to dimensionless quantities" + ) + + +def helper_dimensionless_to_radian(f, unit): + from astropy.units.si import radian + + if unit is None: + return [None], radian + + try: + return [get_converter(unit, dimensionless_unscaled)], radian + except UnitsError: + raise UnitTypeError( + f"Can only apply '{f.__name__}' function to dimensionless quantities" + ) + + +def helper_degree_to_radian(f, unit): + from astropy.units.si import degree, radian + + try: + return [get_converter(unit, degree)], radian + except UnitsError: + raise UnitTypeError( + f"Can only apply '{f.__name__}' function to quantities with angle units" + ) + + +def helper_radian_to_degree(f, unit): + from astropy.units.si import degree, radian + + try: + return [get_converter(unit, radian)], degree + except UnitsError: + raise UnitTypeError( + f"Can only apply '{f.__name__}' function to quantities with angle units" + ) + + +def helper_radian_to_dimensionless(f, unit): + from astropy.units.si import radian + + try: + return [get_converter(unit, radian)], dimensionless_unscaled + except UnitsError: + raise UnitTypeError( + f"Can only apply '{f.__name__}' function to quantities with angle units" + ) + + +def helper_frexp(f, unit): + if not unit.is_unity(): + raise UnitTypeError( + f"Can only apply '{f.__name__}' function to unscaled dimensionless" + " quantities" + ) + return [None], (None, None) + + +# TWO ARGUMENT UFUNC HELPERS +# +# The functions below take a two arguments. The output of the helper function +# should be two values: a tuple of two converters to be used to scale the +# inputs before being passed to the ufunc (None if no conversion is needed), +# and the unit the output will be in. + + +def helper_multiplication(f, unit1, unit2): + match unit1, unit2: + case None, None: + return [None, None], dimensionless_unscaled + case (unit, None) | (None, unit): + return [None, None], unit + case _: + return [None, None], unit1 * unit2 + + +def helper_division(f, unit1, unit2): + if unit1 is unit2: + unit = dimensionless_unscaled + elif unit1 is None: + unit = unit2**-1 + elif unit2 is None: + unit = unit1 + else: + unit = unit1 / unit2 + return [None, None], unit + + +def helper_power(f, unit1, unit2): + # TODO: find a better way to do this, currently need to signal that one + # still needs to raise power of unit1 in main code + if unit2 is None: + return [None, None], False + + try: + return [None, get_converter(unit2, dimensionless_unscaled)], False + except UnitsError: + raise UnitTypeError("Can only raise something to a dimensionless quantity") + + +def helper_ldexp(f, unit1, unit2): + if unit2 is not None: + raise TypeError("Cannot use ldexp with a quantity as second argument.") + else: + return [None, None], _d(unit1) + + +def helper_copysign(f, unit1, unit2): + # if first arg is not a quantity, just return plain array + if unit1 is None: + return [None, None], None + else: + return [None, None], unit1 + + +def helper_heaviside(f, unit1, unit2): + try: + converter2 = ( + get_converter(unit2, dimensionless_unscaled) if unit2 is not None else None + ) + except UnitsError: + raise UnitTypeError( + "Can only apply 'heaviside' function with a dimensionless second argument." + ) + return ([None, converter2], dimensionless_unscaled) + + +def helper_two_arg_dimensionless(f, unit1, unit2): + try: + converter1 = ( + get_converter(unit1, dimensionless_unscaled) if unit1 is not None else None + ) + converter2 = ( + get_converter(unit2, dimensionless_unscaled) if unit2 is not None else None + ) + except UnitsError: + raise UnitTypeError( + f"Can only apply '{f.__name__}' function to dimensionless quantities" + ) + return ([converter1, converter2], dimensionless_unscaled) + + +# This used to be a separate function that just called get_converters_and_unit. +# Using it directly saves a few us; keeping the clearer name. +helper_twoarg_invariant = get_converters_and_unit + + +def helper_twoarg_comparison(f, unit1, unit2): + converters, _ = get_converters_and_unit(f, unit1, unit2) + return converters, None + + +def helper_twoarg_invtrig(f, unit1, unit2): + from astropy.units.si import radian + + converters, _ = get_converters_and_unit(f, unit1, unit2) + return converters, radian + + +def helper_twoarg_floor_divide(f, unit1, unit2): + converters, _ = get_converters_and_unit(f, unit1, unit2) + return converters, dimensionless_unscaled + + +def helper_divmod(f, unit1, unit2): + converters, result_unit = get_converters_and_unit(f, unit1, unit2) + return converters, (dimensionless_unscaled, result_unit) + + +def helper_clip(f, unit1, unit2, unit3): + # Treat the array being clipped as primary. + converters = [None] + if unit1 is None: + result_unit = dimensionless_unscaled + try: + converters += [ + (None if unit is None else get_converter(unit, dimensionless_unscaled)) + for unit in (unit2, unit3) + ] + except UnitsError: + raise UnitConversionError( + f"Can only apply '{f.__name__}' function to quantities with " + "compatible dimensions" + ) + + else: + result_unit = unit1 + for unit in unit2, unit3: + try: + converter = get_converter(_d(unit), result_unit) + except UnitsError: + if unit is None: + # special case: OK if unitless number is zero, inf, nan + converters.append(False) + else: + raise UnitConversionError( + f"Can only apply '{f.__name__}' function to quantities with " + "compatible dimensions" + ) + else: + converters.append(converter) + + return converters, result_unit + + +# list of ufuncs: +# https://numpy.org/doc/stable/reference/ufuncs.html#available-ufuncs + +UNSUPPORTED_UFUNCS |= { + np.bitwise_and, + np.bitwise_or, + np.bitwise_xor, + np.invert, + np.left_shift, + np.right_shift, + np.logical_and, + np.logical_or, + np.logical_xor, + np.logical_not, + np.isnat, + np.gcd, + np.lcm, +} + +# string utilities - make no sense for Quantity. +UNSUPPORTED_UFUNCS |= { + np.bitwise_count, + np._core.umath.count, + np._core.umath.isalpha, + np._core.umath.isdigit, + np._core.umath.isspace, + np._core.umath.isnumeric, + np._core.umath.isdecimal, + np._core.umath.isalnum, + np._core.umath.istitle, + np._core.umath.islower, + np._core.umath.isupper, + np._core.umath.index, + np._core.umath.rindex, + np._core.umath.startswith, + np._core.umath.endswith, + np._core.umath.find, + np._core.umath.rfind, + np._core.umath.str_len, + np._core.umath._strip_chars, + np._core.umath._lstrip_chars, + np._core.umath._rstrip_chars, + np._core.umath._strip_whitespace, + np._core.umath._lstrip_whitespace, + np._core.umath._rstrip_whitespace, + np._core.umath._replace, + np._core.umath._expandtabs, + np._core.umath._expandtabs_length, +} +if not NUMPY_LT_2_1: + UNSUPPORTED_UFUNCS |= { + np._core.umath._ljust, + np._core.umath._rjust, + np._core.umath._center, + np._core.umath._zfill, + np._core.umath._partition_index, + np._core.umath._rpartition, + np._core.umath._rpartition_index, + np._core.umath._partition, + } +if not NUMPY_LT_2_3: + UNSUPPORTED_UFUNCS |= { + np._core.umath._slice, + } + +# SINGLE ARGUMENT UFUNCS + +# ufuncs that do not care about the unit and do not return a Quantity +# (but rather a boolean, or -1, 0, or +1 for np.sign). +onearg_test_ufuncs = (np.isfinite, np.isinf, np.isnan, np.sign, np.signbit) +for ufunc in onearg_test_ufuncs: + UFUNC_HELPERS[ufunc] = helper_onearg_test + +# ufuncs that return a value with the same unit as the input +invariant_ufuncs = ( + np.absolute, + np.fabs, + np.conj, + np.conjugate, + np.negative, + np.spacing, + np.rint, + np.floor, + np.ceil, + np.trunc, + np.positive, +) +if not NUMPY_LT_2_5: + invariant_ufuncs += (np_umath.imag, np_umath.real) + +for ufunc in invariant_ufuncs: + UFUNC_HELPERS[ufunc] = helper_invariant + +# ufuncs that require dimensionless input and and give dimensionless output +dimensionless_to_dimensionless_ufuncs = ( + np.exp, + np.expm1, + np.exp2, + np.log, + np.log10, + np.log2, + np.log1p, +) +# Default numpy does not ship an "erf" ufunc, but some versions hacked by +# intel do. This is bad, since it means code written for that numpy will +# not run on non-hacked numpy. But still, we might as well support it. +if isinstance(getattr(np_umath, "erf", None), np.ufunc): + dimensionless_to_dimensionless_ufuncs += (np_umath.erf,) + +for ufunc in dimensionless_to_dimensionless_ufuncs: + UFUNC_HELPERS[ufunc] = helper_dimensionless_to_dimensionless + +# ufuncs that require dimensionless input and give output in radians +dimensionless_to_radian_ufuncs = ( + np.arccos, + np.arcsin, + np.arctan, + np.arccosh, + np.arcsinh, + np.arctanh, +) +for ufunc in dimensionless_to_radian_ufuncs: + UFUNC_HELPERS[ufunc] = helper_dimensionless_to_radian + +# ufuncs that require input in degrees and give output in radians +degree_to_radian_ufuncs = (np.radians, np.deg2rad) +for ufunc in degree_to_radian_ufuncs: + UFUNC_HELPERS[ufunc] = helper_degree_to_radian + +# ufuncs that require input in radians and give output in degrees +radian_to_degree_ufuncs = (np.degrees, np.rad2deg) +for ufunc in radian_to_degree_ufuncs: + UFUNC_HELPERS[ufunc] = helper_radian_to_degree + +# ufuncs that require input in radians and give dimensionless output +radian_to_dimensionless_ufuncs = (np.cos, np.sin, np.tan, np.cosh, np.sinh, np.tanh) +for ufunc in radian_to_dimensionless_ufuncs: + UFUNC_HELPERS[ufunc] = helper_radian_to_dimensionless + +# ufuncs handled as special cases +UFUNC_HELPERS[np.sqrt] = helper_sqrt +UFUNC_HELPERS[np.square] = helper_square +UFUNC_HELPERS[np.reciprocal] = helper_reciprocal +UFUNC_HELPERS[np.cbrt] = helper_cbrt +UFUNC_HELPERS[np_umath._ones_like] = helper__ones_like +UFUNC_HELPERS[np.modf] = helper_modf +UFUNC_HELPERS[np.frexp] = helper_frexp + + +# TWO ARGUMENT UFUNCS + +# two argument ufuncs that require dimensionless input and and give +# dimensionless output +two_arg_dimensionless_ufuncs = (np.logaddexp, np.logaddexp2) +for ufunc in two_arg_dimensionless_ufuncs: + UFUNC_HELPERS[ufunc] = helper_two_arg_dimensionless + +# two argument ufuncs that return a value with the same unit as the input +twoarg_invariant_ufuncs = ( + np.add, + np.subtract, + np.hypot, + np.maximum, + np.minimum, + np.fmin, + np.fmax, + np.nextafter, + np.remainder, + np.mod, + np.fmod, +) +for ufunc in twoarg_invariant_ufuncs: + UFUNC_HELPERS[ufunc] = helper_twoarg_invariant + +# two argument ufuncs that need compatible inputs and return a boolean +twoarg_comparison_ufuncs = ( + np.greater, + np.greater_equal, + np.less, + np.less_equal, + np.not_equal, + np.equal, +) +for ufunc in twoarg_comparison_ufuncs: + UFUNC_HELPERS[ufunc] = helper_twoarg_comparison + +# two argument ufuncs that do inverse trigonometry +twoarg_invtrig_ufuncs = (np.arctan2,) +# another private function in numpy; use getattr in case it disappears +if isinstance(getattr(np_umath, "_arg", None), np.ufunc): + twoarg_invtrig_ufuncs += (np_umath._arg,) +for ufunc in twoarg_invtrig_ufuncs: + UFUNC_HELPERS[ufunc] = helper_twoarg_invtrig + +# ufuncs handled as special cases +UFUNC_HELPERS[np.multiply] = helper_multiplication +UFUNC_HELPERS[np.matmul] = helper_multiplication +UFUNC_HELPERS[np.vecdot] = helper_multiplication +if not NUMPY_LT_2_2: + UFUNC_HELPERS[np.vecmat] = helper_multiplication + UFUNC_HELPERS[np.matvec] = helper_multiplication +UFUNC_HELPERS[np.divide] = helper_division +UFUNC_HELPERS[np.true_divide] = helper_division +UFUNC_HELPERS[np.power] = helper_power +UFUNC_HELPERS[np.ldexp] = helper_ldexp +UFUNC_HELPERS[np.copysign] = helper_copysign +UFUNC_HELPERS[np.floor_divide] = helper_twoarg_floor_divide +UFUNC_HELPERS[np.heaviside] = helper_heaviside +UFUNC_HELPERS[np.float_power] = helper_power +UFUNC_HELPERS[np.divmod] = helper_divmod +# Check for clip ufunc; note that np.clip is a wrapper function, not the ufunc. +if isinstance(getattr(np_umath, "clip", None), np.ufunc): + UFUNC_HELPERS[np_umath.clip] = helper_clip + +del ufunc diff --git a/astropy/units/quantity_helper/scipy_special.py b/astropy/units/quantity_helper/scipy_special.py new file mode 100644 index 000000000000..3692d94332e4 --- /dev/null +++ b/astropy/units/quantity_helper/scipy_special.py @@ -0,0 +1,90 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +"""Quantity helpers for the scipy.special ufuncs. + +Available ufuncs in this module are at +https://docs.scipy.org/doc/scipy/reference/special.html +""" + +from astropy.units.core import dimensionless_unscaled +from astropy.units.errors import UnitsError, UnitTypeError + +from . import UFUNC_HELPERS +from .helpers import ( + get_converter, + helper_cbrt, + helper_dimensionless_to_dimensionless, + helper_two_arg_dimensionless, +) + +dimensionless_to_dimensionless_sps_ufuncs = ( + "erf", "erfc", "erfcx", "erfi", "erfinv", "erfcinv", + "gamma", "gammaln", "loggamma", "gammasgn", "psi", "rgamma", "digamma", + "wofz", "dawsn", "entr", "exprel", "expm1", "log1p", "exp2", "exp10", + "j0", "j1", "y0", "y1", "i0", "i0e", "i1", "i1e", + "k0", "k0e", "k1", "k1e", "itj0y0", "it2j0y0", "iti0k0", "it2i0k0", + "ndtr", "ndtri", +) # fmt: skip + + +scipy_special_ufuncs = dimensionless_to_dimensionless_sps_ufuncs +# ufuncs that require input in degrees and give dimensionless output. +degree_to_dimensionless_sps_ufuncs = ("cosdg", "sindg", "tandg", "cotdg") +scipy_special_ufuncs += degree_to_dimensionless_sps_ufuncs +two_arg_dimensionless_sps_ufuncs = ( + "jv", "jn", "jve", "yn", "yv", "yve", "kn", "kv", "kve", "iv", "ive", + "hankel1", "hankel1e", "hankel2", "hankel2e", +) # fmt: skip +scipy_special_ufuncs += two_arg_dimensionless_sps_ufuncs +# ufuncs handled as special cases +scipy_special_ufuncs += ("cbrt", "radian") + + +def helper_degree_to_dimensionless(f, unit): + from astropy.units.si import degree + + try: + return [get_converter(unit, degree)], dimensionless_unscaled + except UnitsError: + raise UnitTypeError( + f"Can only apply '{f.__name__}' function to quantities with angle units" + ) + + +def helper_degree_minute_second_to_radian(f, unit1, unit2, unit3): + from astropy.units.si import arcmin, arcsec, degree, radian + + try: + return [ + get_converter(unit1, degree), + get_converter(unit2, arcmin), + get_converter(unit3, arcsec), + ], radian + except UnitsError: + raise UnitTypeError( + f"Can only apply '{f.__name__}' function to quantities with angle units" + ) + + +def get_scipy_special_helpers(): + import scipy.special as sps + + SCIPY_HELPERS = {} + for name in dimensionless_to_dimensionless_sps_ufuncs: + ufunc = getattr(sps, name, None) + SCIPY_HELPERS[ufunc] = helper_dimensionless_to_dimensionless + + for ufunc in degree_to_dimensionless_sps_ufuncs: + SCIPY_HELPERS[getattr(sps, ufunc)] = helper_degree_to_dimensionless + + for ufunc in two_arg_dimensionless_sps_ufuncs: + SCIPY_HELPERS[getattr(sps, ufunc)] = helper_two_arg_dimensionless + + # ufuncs handled as special cases + SCIPY_HELPERS[sps.cbrt] = helper_cbrt + SCIPY_HELPERS[sps.radian] = helper_degree_minute_second_to_radian + return SCIPY_HELPERS + + +UFUNC_HELPERS.register_module( + "scipy.special", scipy_special_ufuncs, get_scipy_special_helpers +) diff --git a/astropy/units/required_by_vounit.py b/astropy/units/required_by_vounit.py new file mode 100644 index 000000000000..6d25ba36445c --- /dev/null +++ b/astropy/units/required_by_vounit.py @@ -0,0 +1,29 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +""" +This package defines SI prefixed units that are required by the VOUnit standard +but that are rarely used in practice and liable to lead to confusion (such as +``msolMass`` for milli-solar mass). The units here are enabled so, e.g., +``Unit('msolMass')`` will just work, but to access the unit directly, use +``astropy.units.required_by_vounit.msolMass`` instead of the more typical idiom +possible for the non-prefixed unit, ``astropy.units.solMass``. +""" + +from . import astrophys +from .core import _add_prefixes +from .docgen import ( + generate_dunder_all, + generate_prefixonly_unit_summary, + generate_unit_summary, +) + +_add_prefixes(astrophys.solMass, namespace=globals(), prefixes=True) +_add_prefixes(astrophys.solRad, namespace=globals(), prefixes=True) +_add_prefixes(astrophys.solLum, namespace=globals(), prefixes=True) + +__all__ = generate_dunder_all(globals()) # noqa: PLE0605 + +if __doc__ is not None: + # This generates a docstring for this module that describes all of the + # standard units defined here. + __doc__ += generate_unit_summary(globals()) + __doc__ += generate_prefixonly_unit_summary(globals()) diff --git a/astropy/units/si.py b/astropy/units/si.py new file mode 100644 index 000000000000..c4ddf60d0fe1 --- /dev/null +++ b/astropy/units/si.py @@ -0,0 +1,485 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +""" +This package defines the SI units. They are also available in +(and should be used through) the `astropy.units` namespace. +""" +# avoid ruff complaints about undefined names defined by def_unit +# ruff: noqa: F821 + +import numpy as np + +from .core import CompositeUnit, def_unit +from .docgen import generate_dunder_all, generate_unit_summary + +__all__: list[str] = [] # Units are added at the end + +_ns = globals() + + +########################################################################### +# DIMENSIONLESS + +def_unit( + ["percent", "pct"], + CompositeUnit(0.01, [], []), + namespace=_ns, + prefixes=False, + doc="percent: one hundredth of unity, factor 0.01", + format={"generic": "%", "console": "%", "cds": "%", "latex": r"\%", "unicode": "%"}, +) + +########################################################################### +# LENGTH + +# We exclude c as a prefix so we can define cm as a derived unit, +# not a PrefixUnit. That way it can be used in cgs as a base unit. +def_unit( + ["m", "meter"], + namespace=_ns, + prefixes=True, + exclude_prefixes=["c"], + doc="meter: base unit of length in SI", +) +def_unit( + ["cm", "centimeter"], + 0.01 * m, + namespace=_ns, + prefixes=False, + doc="cm: 0.01 m in SI, base unit of length in cgs", +) +def_unit( + ["micron"], + um, + namespace=_ns, + doc="micron: alias for micrometer (um)", + format={"latex": r"\mu m", "unicode": "\N{MICRO SIGN}m"}, +) +def_unit( + ["Angstrom", "AA", "angstrom", "Å"], + 0.1 * nm, + namespace=_ns, + doc="ÃĨngstrÃļm: 10 ** -10 m", + prefixes=[(["m", "milli"], ["milli", "m"], 1.0e-3)], + format={ + "latex": r"\mathring{A}", + "ogip": "angstrom", + "unicode": "Å", + "vounit": "Angstrom", + }, +) + + +########################################################################### +# AREA + +def_unit( + ["ha", "hectare"], + 1e4 * m**2, + namespace=_ns, + prefixes=False, + doc="hectare: unit of area, used to express area of land", +) + + +########################################################################### +# VOLUMES + +def_unit( + (["l", "L", "ℓ"], ["liter"]), + 1000 * cm**3.0, + namespace=_ns, + prefixes=True, + doc="liter: metric unit of volume", +) + + +########################################################################### +# ANGULAR MEASUREMENTS + +def_unit( + ["rad", "radian"], + namespace=_ns, + prefixes=True, + doc=( + "radian: angular measurement of the ratio between the length " + "on an arc and its radius" + ), +) +def_unit( + ["deg", "degree"], + np.pi / 180.0 * rad, + namespace=_ns, + prefixes=True, + doc="degree: angular measurement 1/360 of full rotation", +) +def_unit( + ["hourangle"], + 15.0 * deg, + namespace=_ns, + prefixes=False, + doc="hour angle: angular measurement with 24 in a full circle", + format={"latex": r"{}^{h}", "unicode": "ʰ"}, +) +def_unit( + ["arcmin", "arcminute"], + 1.0 / 60.0 * deg, + namespace=_ns, + prefixes=True, + doc="arc minute: angular measurement", +) +def_unit( + ["arcsec", "arcsecond"], + 1.0 / 3600.0 * deg, + namespace=_ns, + prefixes=True, + doc="arc second: angular measurement", +) +# These special formats should only be used for the non-prefix versions +deg._format = {"latex": r"{}^{\circ}", "unicode": "°"} +arcmin._format = {"latex": r"{}^{\prime}", "unicode": "′"} +arcsec._format = {"latex": r"{}^{\prime\prime}", "unicode": "â€ŗ"} +def_unit( + ["mas"], + 0.001 * arcsec, + namespace=_ns, + doc="milli arc second: angular measurement", +) +def_unit( + ["uas", "\N{MICRO SIGN}as", "\N{GREEK SMALL LETTER MU}as"], + 0.000001 * arcsec, + namespace=_ns, + doc="micro arc second: angular measurement", + format={"latex": r"\mu as", "unicode": "Îŧas"}, +) +def_unit( + ["sr", "steradian"], + rad**2, + namespace=_ns, + prefixes=True, + doc="steradian: unit of solid angle in SI", +) + + +########################################################################### +# TIME + +def_unit( + ["s", "second"], + namespace=_ns, + prefixes=True, + doc="second: base unit of time in SI.", +) +def_unit( + ["min", "minute"], + 60 * s, + prefixes=True, + namespace=_ns, +) +def_unit( + ["h", "hour", "hr"], + 3600 * s, + namespace=_ns, + prefixes=True, + exclude_prefixes=["p"], +) +def_unit( + ["d", "day"], + 24 * h, + namespace=_ns, + prefixes=True, + exclude_prefixes=["c", "y"], +) +def_unit( + ["sday"], + 86164.09053 * s, + namespace=_ns, + doc="Sidereal day (sday) is the time of one rotation of the Earth.", +) +def_unit( + ["wk", "week"], + 7 * day, + namespace=_ns, +) +def_unit( + ["fortnight"], + 2 * wk, + namespace=_ns, +) +def_unit( + ["a", "annum"], + 365.25 * d, + namespace=_ns, + prefixes=True, + exclude_prefixes=["P", "h"], # Avoid possible confusion with Pascal and hectare +) +def_unit( + ["yr", "year"], + 365.25 * d, + namespace=_ns, + prefixes=True, +) + + +########################################################################### +# FREQUENCY + +def_unit( + ["Hz", "Hertz", "hertz"], + 1 / s, + namespace=_ns, + prefixes=True, + doc="Frequency", +) + + +########################################################################### +# MASS + +def_unit( + ["kg", "kilogram"], + namespace=_ns, + doc="kilogram: base unit of mass in SI.", +) +def_unit( + ["g", "gram"], + 1.0e-3 * kg, + namespace=_ns, + prefixes=True, + exclude_prefixes=["k", "kilo"], +) +def_unit( + ["t", "tonne"], + 1000 * kg, + namespace=_ns, + doc="Metric tonne", +) + + +########################################################################### +# AMOUNT OF SUBSTANCE + +def_unit( + ["mol", "mole"], + namespace=_ns, + prefixes=True, + doc="mole: amount of a chemical substance in SI.", +) +def_unit( + ["kat", "katal"], + mol * s**-1, + namespace=_ns, + prefixes=True, + doc="katal: catalytic activity.", +) + + +########################################################################### +# TEMPERATURE + +def_unit( + ["K", "Kelvin"], + namespace=_ns, + prefixes=True, + doc="Kelvin: temperature with a null point at absolute zero.", +) +def_unit( + ["deg_C", "Celsius"], + namespace=_ns, + doc="Degrees Celsius", + format={"latex": r"{}^{\circ}C", "unicode": "°C", "fits": "Celsius"}, +) + + +########################################################################### +# FORCE + +def_unit( + ["N", "Newton", "newton"], + kg * m * s**-2, + namespace=_ns, + prefixes=True, + doc="Newton: force", +) + + +########################################################################## +# ENERGY + +def_unit( + ["J", "Joule", "joule"], + N * m, + namespace=_ns, + prefixes=True, + doc="Joule: energy", +) + + +########################################################################## +# PRESSURE + +def_unit( + ["Pa", "Pascal", "pascal"], + J * m**-3, + namespace=_ns, + prefixes=True, + doc="Pascal: pressure", +) + + +########################################################################### +# POWER + +def_unit( + ["W", "Watt", "watt"], + J / s, + namespace=_ns, + prefixes=True, + doc="Watt: power", +) + + +########################################################################### +# ELECTRICAL + +def_unit( + ["A", "ampere", "amp"], + namespace=_ns, + prefixes=True, + doc="ampere: base unit of electric current in SI", +) +def_unit( + ["C", "coulomb"], + A * s, + namespace=_ns, + prefixes=True, + doc="coulomb: electric charge", +) +def_unit( + ["V", "Volt", "volt"], + J * C**-1, + namespace=_ns, + prefixes=True, + doc="Volt: electric potential or electromotive force", +) +def_unit( + (["Ohm", "ohm", "Ί"], ["Ohm"]), + V * A**-1, + namespace=_ns, + prefixes=True, + doc="Ohm: electrical resistance", + format={"latex": r"\Omega", "ogip": "ohm", "unicode": "Ί"}, +) +def_unit( + ["S", "Siemens", "siemens"], + A * V**-1, + namespace=_ns, + prefixes=True, + doc="Siemens: electrical conductance", +) +def_unit( + ["F", "Farad", "farad"], + C * V**-1, + namespace=_ns, + prefixes=True, + doc="Farad: electrical capacitance", +) + + +########################################################################### +# MAGNETIC + +def_unit( + ["Wb", "Weber", "weber"], + V * s, + namespace=_ns, + prefixes=True, + doc="Weber: magnetic flux", +) +def_unit( + ["T", "Tesla", "tesla"], + Wb * m**-2, + namespace=_ns, + prefixes=True, + doc="Tesla: magnetic flux density", +) +def_unit( + ["H", "Henry", "henry"], + Wb * A**-1, + namespace=_ns, + prefixes=True, + doc="Henry: inductance", +) + + +########################################################################### +# ILLUMINATION + +def_unit( + ["cd", "candela"], + namespace=_ns, + prefixes=True, + doc="candela: base unit of luminous intensity in SI", +) +def_unit( + ["lm", "lumen"], + cd * sr, + namespace=_ns, + prefixes=True, + doc="lumen: luminous flux", +) +def_unit( + ["lx", "lux"], + lm * m**-2, + namespace=_ns, + prefixes=True, + doc="lux: luminous emittance", +) + +########################################################################### +# RADIOACTIVITY + +def_unit( + ["Bq", "becquerel"], + 1 / s, + namespace=_ns, + prefixes=True, + doc="becquerel: unit of radioactivity", +) +def_unit( + ["Ci", "curie"], + Bq * 3.7e10, + namespace=_ns, + prefixes=True, + doc="curie: unit of radioactivity", +) +def_unit( + ["Gy", "gray"], + J * kg**-1, + namespace=_ns, + prefixes=True, + doc="gray: absorbed dose of ionizing radiation or kinetic energy released per unit mass (kerma)", +) +def_unit( + ["Sv", "sievert"], + J * kg**-1, + namespace=_ns, + prefixes=True, + doc="sievert: equivalent dose of ionizing radiation", +) + + +########################################################################### +# BASES + +bases = {m, s, kg, A, cd, rad, K, mol} + + +########################################################################### +# ALL & DOCSTRING + +__all__ += generate_dunder_all(globals()) # noqa: PLE0605 + +if __doc__ is not None: + # This generates a docstring for this module that describes all of the + # standard units defined here. + __doc__ += generate_unit_summary(globals()) diff --git a/astropy/units/structured.py b/astropy/units/structured.py new file mode 100644 index 000000000000..bcf1a4a4b7a3 --- /dev/null +++ b/astropy/units/structured.py @@ -0,0 +1,576 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +""" +This module defines structured units and quantities. +""" + +# Standard library +import operator +from collections.abc import Collection +from functools import cached_property +from typing import Self + +import numpy as np + +from .core import UNITY, Unit, UnitBase + +__all__ = ["StructuredUnit"] + + +DTYPE_OBJECT = np.dtype("O") + + +def _names_from_dtype(dtype): + """Recursively extract field names from a dtype.""" + names = [] + for name in dtype.names: + subdtype = dtype.fields[name][0].base + if subdtype.names: + names.append([name, _names_from_dtype(subdtype)]) + else: + names.append(name) + return tuple(names) + + +def _normalize_names(names): + """Recursively normalize, inferring upper level names for unadorned tuples. + + Generally, we want the field names to be organized like dtypes, as in + ``(['pv', ('p', 'v')], 't')``. But we automatically infer upper + field names if the list is absent from items like ``(('p', 'v'), 't')``, + by concatenating the names inside the tuple. + """ + result = [] + for name in names: + if isinstance(name, str) and len(name) > 0: + result.append(name) + elif ( + isinstance(name, list) + and len(name) == 2 + and isinstance(name[0], str) + and len(name[0]) > 0 + and isinstance(name[1], tuple) + and len(name[1]) > 0 + ): + result.append([name[0], _normalize_names(name[1])]) + elif isinstance(name, tuple) and len(name) > 0: + new_tuple = _normalize_names(name) + name = "".join([(i[0] if isinstance(i, list) else i) for i in new_tuple]) + result.append([name, new_tuple]) + else: + raise ValueError( + f"invalid entry {name!r}. Should be a name, " + "tuple of names, or 2-element list of the " + "form [name, tuple of names]." + ) + + return tuple(result) + + +class StructuredUnit: + """Container for units for a structured Quantity. + + Parameters + ---------- + units : unit-like, tuple of unit-like, or `~astropy.units.StructuredUnit` + Tuples can be nested. If a `~astropy.units.StructuredUnit` is passed + in, it will be returned unchanged unless different names are requested. + names : tuple of str, tuple or list; `~numpy.dtype`; or `~astropy.units.StructuredUnit`, optional + Field names for the units, possibly nested. Can be inferred from a + structured `~numpy.dtype` or another `~astropy.units.StructuredUnit`. + For nested tuples, by default the name of the upper entry will be the + concatenation of the names of the lower levels. One can pass in a + list with the upper-level name and a tuple of lower-level names to + avoid this. For tuples, not all levels have to be given; for any level + not passed in, default field names of 'f0', 'f1', etc., will be used. + + Notes + ----- + It is recommended to initialize the class indirectly, using + `~astropy.units.Unit`. E.g., ``u.Unit('AU,AU/day')``. + + When combined with a structured array to produce a structured + `~astropy.units.Quantity`, array field names will take precedence. + Generally, passing in ``names`` is needed only if the unit is used + unattached to a `~astropy.units.Quantity` and one needs to access its + fields. + + Examples + -------- + Various ways to initialize a `~astropy.units.StructuredUnit`:: + + >>> import astropy.units as u + >>> su = u.Unit('(AU,AU/day),yr') + >>> su + Unit("((AU, AU / d), yr)") + >>> su.field_names + (['f0', ('f0', 'f1')], 'f1') + >>> su['f1'] + Unit("yr") + >>> su2 = u.StructuredUnit(((u.AU, u.AU/u.day), u.yr), names=(('p', 'v'), 't')) + >>> su2 == su + True + >>> su2.field_names + (['pv', ('p', 'v')], 't') + >>> su3 = u.StructuredUnit((su2['pv'], u.day), names=(['p_v', ('p', 'v')], 't')) + >>> su3.field_names + (['p_v', ('p', 'v')], 't') + >>> su3.keys() + ('p_v', 't') + >>> su3.values() + (Unit("(AU, AU / d)"), Unit("d")) + + Structured units share most methods with regular units:: + + >>> su.physical_type + astropy.units.structured.Structure((astropy.units.structured.Structure((PhysicalType('length'), PhysicalType({'speed', 'velocity'})), dtype=[('f0', 'O'), ('f1', 'O')]), PhysicalType('time')), dtype=[('f0', 'O'), ('f1', 'O')]) + >>> su.si + Unit("((1.49598e+11 m, 1.73146e+06 m / s), 3.15576e+07 s)") + + """ + + def __new__(cls, units, names=None): + dtype = None + if names is not None: + if isinstance(names, StructuredUnit): + dtype = names._units.dtype + names = names.field_names + elif isinstance(names, np.dtype): + if not names.fields: + raise ValueError("dtype should be structured, with fields.") + dtype = np.dtype([(name, DTYPE_OBJECT) for name in names.names]) + names = _names_from_dtype(names) + else: + if not isinstance(names, tuple): + names = (names,) + names = _normalize_names(names) + + if not isinstance(units, tuple): + units = Unit(units) + if isinstance(units, StructuredUnit): + # Avoid constructing a new StructuredUnit if no field names + # are given, or if all field names are the same already anyway. + if names is None or units.field_names == names: + return units + + # Otherwise, turn (the upper level) into a tuple, for renaming. + units = units.values() + else: + # Single regular unit: make a tuple for iteration below. + units = (units,) + + if names is None: + names = tuple(f"f{i}" for i in range(len(units))) + + elif len(units) != len(names): + raise ValueError("lengths of units and field names must match.") + + converted = [] + for unit, name in zip(units, names): + if isinstance(name, list): + # For list, the first item is the name of our level, + # and the second another tuple of names, i.e., we recurse. + unit = cls(unit, name[1]) + name = name[0] + else: + # We are at the lowest level. Check unit. + unit = Unit(unit) + if dtype is not None and isinstance(unit, StructuredUnit): + raise ValueError( + "units do not match in depth with field " + "names from dtype or structured unit." + ) + + converted.append(unit) + + self = super().__new__(cls) + if dtype is None: + dtype = np.dtype( + [ + ((name[0] if isinstance(name, list) else name), DTYPE_OBJECT) + for name in names + ] + ) + # Decay array to void so we can access by field name and number. + self._units = np.array(tuple(converted), dtype)[()] + return self + + def __getnewargs__(self): + """When de-serializing, e.g. pickle, start with a blank structure.""" + return (), None + + @property + def field_names(self): + """Possibly nested tuple of the field names of the parts.""" + return tuple( + ([name, unit.field_names] if isinstance(unit, StructuredUnit) else name) + for name, unit in self.items() + ) + + # Allow StructuredUnit to be treated as an (ordered) mapping. + def __len__(self): + return len(self._units.dtype.names) + + def __getitem__(self, item): + # Since we are based on np.void, indexing by field number works too. + return self._units[item] + + def values(self): + return self._units.item() + + def keys(self): + return self._units.dtype.names + + def items(self): + return tuple(zip(self._units.dtype.names, self._units.item())) + + def __iter__(self): + yield from self._units.dtype.names + + # Helpers for methods below. + def _recursively_apply(self, func, cls=None): + """Apply func recursively. + + Parameters + ---------- + func : callable + Function to apply to all parts of the structured unit, + recursing as needed. + cls : type, optional + If given, should be a subclass of `~numpy.void`. By default, + will return a new `~astropy.units.StructuredUnit` instance. + """ + results = np.void(tuple(map(func, self.values())), self._units.dtype) + if cls is not None: + return results.view((cls, results.dtype)) + + # Short-cut; no need to interpret field names, etc. + result = super().__new__(self.__class__) + result._units = results + return result + + def _recursively_get_dtype(self, value, enter_lists=True): + """Get structured dtype according to value, using our field names. + + This is useful since ``np.array(value)`` would treat tuples as lower + levels of the array, rather than as elements of a structured array. + The routine does presume that the type of the first tuple is + representative of the rest. Used in ``get_converter``. + + For the special value of ``UNITY``, all fields are assumed to be 1.0, + and hence this will return an all-float dtype. + + """ + if enter_lists: + while isinstance(value, list): + value = value[0] + if value is UNITY: + value = (UNITY,) * len(self) + elif not isinstance(value, tuple) or len(self) != len(value): + raise ValueError(f"cannot interpret value {value} for unit {self}.") + descr = [] + for (name, unit), part in zip(self.items(), value): + if isinstance(unit, StructuredUnit): + descr.append( + (name, unit._recursively_get_dtype(part, enter_lists=False)) + ) + else: + # Got a part associated with a regular unit. Gets its dtype. + # Like for Quantity, we cast integers to float. + part = np.array(part) + part_dtype = part.dtype + if part_dtype.kind in "iu": + part_dtype = np.dtype(float) + descr.append((name, part_dtype, part.shape)) + return np.dtype(descr) + + @property + def si(self): + """The `StructuredUnit` instance in SI units.""" + return self._recursively_apply(operator.attrgetter("si")) + + @property + def cgs(self): + """The `StructuredUnit` instance in cgs units.""" + return self._recursively_apply(operator.attrgetter("cgs")) + + # Needed to pass through Unit initializer, so might as well use it. + @cached_property + def _physical_type_id(self): + return self._recursively_apply( + operator.attrgetter("_physical_type_id"), cls=Structure + ) + + @property + def physical_type(self): + """Physical types of all the fields.""" + return self._recursively_apply( + operator.attrgetter("physical_type"), cls=Structure + ) + + def decompose(self, bases: Collection[UnitBase] = ()) -> Self: + """The `StructuredUnit` composed of only irreducible units. + + Parameters + ---------- + bases : sequence of `~astropy.units.UnitBase`, optional + The bases to decompose into. When not provided, + decomposes down to any irreducible units. When provided, + the decomposed result will only contain the given units. + This will raises a `UnitsError` if it's not possible + to do so. + + Returns + ------- + `~astropy.units.StructuredUnit` + With the unit for each field containing only irreducible units. + """ + return self._recursively_apply(operator.methodcaller("decompose", bases=bases)) + + def is_equivalent(self, other, equivalencies=[]): + """`True` if all fields are equivalent to the other's fields. + + Parameters + ---------- + other : `~astropy.units.StructuredUnit` + The structured unit to compare with, or what can initialize one. + equivalencies : list of tuple, optional + A list of equivalence pairs to try if the units are not + directly convertible. See :ref:`unit_equivalencies`. + The list will be applied to all fields. + + Returns + ------- + bool + """ + try: + other = StructuredUnit(other) + except Exception: + return False + + if len(self) != len(other): + return False + + for self_part, other_part in zip(self.values(), other.values()): + if not self_part.is_equivalent(other_part, equivalencies=equivalencies): + return False + + return True + + def get_converter(self, other, equivalencies=[]): + if not isinstance(other, type(self)): + other = self.__class__(other, names=self) + + converters = [ + self_part.get_converter(other_part, equivalencies=equivalencies) + for (self_part, other_part) in zip(self.values(), other.values()) + ] + + def converter(value): + if not hasattr(value, "dtype"): + value = np.array(value, self._recursively_get_dtype(value)) + result = np.empty_like(value) + for name, converter_ in zip(result.dtype.names, converters): + result[name] = converter_(value[name]) + # Index with empty tuple to decay array scalars to numpy void. + return result if result.shape else result[()] + + return converter + + get_converter.__doc__ = UnitBase.get_converter.__doc__ + + def to(self, other, value=np._NoValue, equivalencies=[]): + """Return values converted to the specified unit. + + Parameters + ---------- + other : `~astropy.units.StructuredUnit` + The unit to convert to. If necessary, will be converted to + a `~astropy.units.StructuredUnit` using the dtype of ``value``. + value : array-like, optional + Value(s) in the current unit to be converted to the + specified unit. If a sequence, the first element must have + entries of the correct type to represent all elements (i.e., + not have, e.g., a ``float`` where other elements have ``complex``). + If not given, assumed to have 1. in all fields. + equivalencies : list of tuple, optional + A list of equivalence pairs to try if the units are not + directly convertible. See :ref:`unit_equivalencies`. + This list is in addition to possible global defaults set by, e.g., + `set_enabled_equivalencies`. + Use `None` to turn off all equivalencies. + + Returns + ------- + values : scalar or array + Converted value(s). + + Raises + ------ + UnitsError + If units are inconsistent + """ + if value is np._NoValue: + # We do not have UNITY as a default, since then the docstring + # would list 1.0 as default, yet one could not pass that in. + value = UNITY + return self.get_converter(other, equivalencies=equivalencies)(value) + + def to_string(self, format="generic"): + """Output the unit in the given format as a string. + + Units are separated by commas. + + Parameters + ---------- + format : `astropy.units.format.Base` subclass or str + The name of a format or a formatter class. If not + provided, defaults to the generic format. + + Notes + ----- + Structured units can be written to all formats, but can be + re-read only with 'generic'. + + """ + parts = [part.to_string(format) for part in self.values()] + out_fmt = "({})" if len(self) > 1 else "({},)" + if format.startswith("latex"): + # Strip $ from parts and add them on the outside. + parts = [part[1:-1] for part in parts] + out_fmt = "$" + out_fmt + "$" + return out_fmt.format(", ".join(parts)) + + def _repr_latex_(self): + return self.to_string("latex") + + __array_ufunc__ = None + + def __mul__(self, other): + if isinstance(other, str): + try: + other = Unit(other, parse_strict="silent") + except Exception: + return NotImplemented + if isinstance(other, UnitBase): + new_units = tuple(part * other for part in self.values()) + return self.__class__(new_units, names=self) + if isinstance(other, StructuredUnit): + return NotImplemented + + # Anything not like a unit, try initialising as a structured quantity. + try: + from .quantity import Quantity + + return Quantity(other, unit=self) + except Exception: + return NotImplemented + + def __rmul__(self, other): + return self.__mul__(other) + + def __truediv__(self, other): + if isinstance(other, str): + try: + other = Unit(other, parse_strict="silent") + except Exception: + return NotImplemented + + if isinstance(other, UnitBase): + new_units = tuple(part / other for part in self.values()) + return self.__class__(new_units, names=self) + return NotImplemented + + def __rlshift__(self, m): + try: + from .quantity import Quantity + + return Quantity(m, self, copy=False, subok=True) + except Exception: + return NotImplemented + + def __str__(self): + return self.to_string() + + def __repr__(self): + return f'Unit("{self.to_string()}")' + + def __hash__(self): + return hash(self.values()) + + def __eq__(self, other): + try: + other = StructuredUnit(other) + except Exception: + return NotImplemented + + return self.values() == other.values() + + def __ne__(self, other): + if not isinstance(other, type(self)): + try: + other = StructuredUnit(other) + except Exception: + return NotImplemented + + return self.values() != other.values() + + +class Structure(np.void): + """Single element structure for physical type IDs, etc. + + Behaves like a `~numpy.void` and thus mostly like a tuple which can also + be indexed with field names, but overrides ``__eq__`` and ``__ne__`` to + compare only the contents, not the field names. Furthermore, this way no + `FutureWarning` about comparisons is given. + + """ + + # Note that it is important for physical type IDs to not be stored in a + # tuple, since then the physical types would be treated as alternatives in + # :meth:`~astropy.units.UnitBase.is_equivalent`. (Of course, in that + # case, they could also not be indexed by name.) + + def __eq__(self, other): + if isinstance(other, np.void): + other = other.item() + + return self.item() == other + + def __ne__(self, other): + if isinstance(other, np.void): + other = other.item() + + return self.item() != other + + +def _structured_unit_like_dtype( + unit: UnitBase | StructuredUnit, dtype: np.dtype +) -> StructuredUnit: + """Make a `StructuredUnit` of one unit, with the structure of a `numpy.dtype`. + + Parameters + ---------- + unit : UnitBase + The unit that will be filled into the structure. + dtype : `numpy.dtype` + The structure for the StructuredUnit. + + Returns + ------- + StructuredUnit + """ + if isinstance(unit, StructuredUnit): + # If unit is structured, it should match the dtype. This function is + # only used in Quantity, which performs this check, so it's fine to + # return as is. + return unit + + # Make a structured unit + units = [] + for name in dtype.names: + subdtype = dtype.fields[name][0] + if subdtype.names is not None: + units.append(_structured_unit_like_dtype(unit, subdtype)) + else: + units.append(unit) + return StructuredUnit(tuple(units), names=dtype.names) diff --git a/astropy/units/tests/__init__.py b/astropy/units/tests/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/astropy/units/tests/test_aliases.py b/astropy/units/tests/test_aliases.py new file mode 100644 index 000000000000..95c98f9e4328 --- /dev/null +++ b/astropy/units/tests/test_aliases.py @@ -0,0 +1,89 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +"""Test setting and adding unit aliases.""" + +import pytest + +import astropy.units as u + +trials = [ + ({"Angstroms": u.AA}, "Angstroms", u.AA), + ({"counts": u.count}, "counts/s", u.count / u.s), + ( + {"ergs": u.erg, "Angstroms": u.AA}, + "ergs/(s cm**2 Angstroms)", + u.erg / (u.s * u.cm**2 * u.AA), + ), +] + + +class TestAliases: + def teardown_method(self): + u.set_enabled_aliases({}) + + def teardown_class(self): + assert u.get_current_unit_registry().aliases == {} + + @pytest.mark.parametrize("format_", [None, "fits", "ogip", "vounit", "cds"]) + @pytest.mark.parametrize("aliases,bad,unit", trials) + def test_set_enabled_aliases_context_manager(self, aliases, bad, unit, format_): + if format_ == "cds": + bad = bad.replace(" ", ".").replace("**", "") + + with u.set_enabled_aliases(aliases): + assert u.get_current_unit_registry().aliases == aliases + assert u.Unit(bad) == unit + + assert u.get_current_unit_registry().aliases == {} + with pytest.raises(ValueError): + u.Unit(bad) + + @pytest.mark.parametrize("aliases,bad,unit", trials) + def test_add_enabled_aliases_context_manager(self, aliases, bad, unit): + with u.add_enabled_aliases(aliases): + assert u.get_current_unit_registry().aliases == aliases + assert u.Unit(bad) == unit + + assert u.get_current_unit_registry().aliases == {} + with pytest.raises(ValueError): + u.Unit(bad) + + def test_set_enabled_aliases(self): + for aliases, bad, unit in trials: + u.set_enabled_aliases(aliases) + + assert u.get_current_unit_registry().aliases == aliases + + assert u.Unit(bad) == unit + + for _, bad2, unit2 in trials: + if bad2 == bad or bad2 in aliases: + assert u.Unit(bad2) == unit2 + else: + with pytest.raises(ValueError): + u.Unit(bad2) + + def test_add_enabled_aliases(self): + expected_aliases = {} + for i, (aliases, bad, unit) in enumerate(trials): + u.add_enabled_aliases(aliases) + + expected_aliases.update(aliases) + assert u.get_current_unit_registry().aliases == expected_aliases + + assert u.Unit(bad) == unit + + for j, (_, bad2, unit2) in enumerate(trials): + if j <= i: + assert u.Unit(bad2) == unit2 + else: + with pytest.raises(ValueError): + u.Unit(bad2) + + def test_cannot_alias_existing_unit(self): + with pytest.raises(ValueError, match="already means"): + u.set_enabled_aliases({"pct": u.Unit(1e-12 * u.count)}) + + def test_cannot_alias_existing_alias_to_another_unit(self): + u.set_enabled_aliases({"counts": u.count}) + with pytest.raises(ValueError, match="already is an alias"): + u.add_enabled_aliases({"counts": u.adu}) diff --git a/astropy/units/tests/test_deprecated.py b/astropy/units/tests/test_deprecated.py new file mode 100644 index 000000000000..a802729b1fc0 --- /dev/null +++ b/astropy/units/tests/test_deprecated.py @@ -0,0 +1,70 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +import warnings + +import pytest + +from astropy import units as u +from astropy.units import deprecated +from astropy.utils.exceptions import AstropyDeprecationWarning + +with warnings.catch_warnings(action="ignore", category=AstropyDeprecationWarning): + emu = deprecated.emu + GearthRad = deprecated.GearthRad + MjupiterMass = deprecated.MjupiterMass + mjupiterRad = deprecated.MjupiterRad + nearthMass = deprecated.nearthMass + + +def test_enable(): + with pytest.warns(AstropyDeprecationWarning, match="enable function is deprecated"): + with deprecated.enable(): + # `unit in u.Bi.compose()` would use `==` for comparison, but we really + # do want to check identity, not just equality. + assert any(unit is emu for unit in u.Bi.compose()) + + +def test_emu(): + assert emu == u.Bi + + +@pytest.mark.parametrize( + "unit", + [emu, GearthRad, MjupiterMass, mjupiterRad, nearthMass], + ids=lambda x: x.name, +) +def test_deprecated_unit_not_in_main_namespace(unit): + with pytest.raises(AttributeError): + getattr(u, unit.name) + + +@pytest.mark.parametrize( + "prefixed_unit,base_unit", + [ + pytest.param(prefixed_unit, base_unit, id=prefixed_unit.name) + for prefixed_unit, base_unit in [ + (GearthRad, u.earthRad), + (MjupiterMass, u.jupiterMass), + (mjupiterRad, u.jupiterRad), + (nearthMass, u.earthMass), + ] + ], +) +def test_deprecated_unit_definition(prefixed_unit, base_unit): + assert prefixed_unit.represents.bases[0] is base_unit + + +def test_deprecated_units_are_deprecated(): + with pytest.warns( + AstropyDeprecationWarning, + match=r"^'gigaM_jupiter' is deprecated since version 7\.1$", + ): + deprecated.gigaM_jupiter + + +def test_invalid_name_raises_attribute_error(): + with pytest.raises( + AttributeError, + match=r"^module 'astropy\.units\.deprecated' has no attribute 'fifaRearth'$", + ): + deprecated.fifaRearth diff --git a/astropy/units/tests/test_docgen.py b/astropy/units/tests/test_docgen.py new file mode 100644 index 000000000000..ef3127e7bf8c --- /dev/null +++ b/astropy/units/tests/test_docgen.py @@ -0,0 +1,139 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +# ruff: noqa: FLY002 + +import textwrap +from importlib.util import module_from_spec, spec_from_file_location + +import pytest + +from astropy.tests.helper import _skip_docstring_tests_with_optimized_python + + +def test_composite_unit_definition(tmp_path): + # Regression test for #17781 - error message was not informative + module_path = tmp_path / "bad_module.py" + module_path.write_text( + textwrap.dedent(""" + from astropy import units as u + from astropy.units.docgen import generate_unit_summary + + km_per_h = u.km / u.h + + __doc__ = generate_unit_summary(globals()) + """) + ) + spec = spec_from_file_location("bad_module", module_path) + module = module_from_spec(spec) + + with pytest.raises( + TypeError, match=r"^'km_per_h' must be defined with 'def_unit\(\)'$" + ): + # emulate an import statement + spec.loader.exec_module(module) + + +@_skip_docstring_tests_with_optimized_python +def test_unit_definition_module_summary_header(): + from astropy.units import si + + entries = si.__doc__.split("\n\n") + assert len(entries) == 55 + assert entries[1] == "\n".join( + ( + ".. list-table:: Available Units", + " :header-rows: 1", + " :widths: 10 20 20 20 1", + ) + ) + assert entries[2] == "\n".join( + ( + " * - Unit", + " - Description", + " - Represents", + " - Aliases", + " - SI Prefixes", + ) + ) + + +@_skip_docstring_tests_with_optimized_python +def test_unit_definition_module_prefix_only_summary(): + from astropy.units import required_by_vounit + + entries = required_by_vounit.__doc__.split("\n\n") + assert len(entries) == 6 + assert entries[3] == "\n".join( + ( + " * - Prefixes for ``solLum``", + " - Solar luminance prefixes", + " - :math:`\\mathrm{3.828 \\times 10^{26}\\,W}`", + " - ``L_sun``, ``Lsun``", + " - Only", + ) + ) + assert entries[5] == "\n".join( + ( + " * - Prefixes for ``solRad``", + " - Solar radius prefixes", + " - :math:`\\mathrm{6.957 \\times 10^{8}\\,m}`", + " - ``R_sun``, ``Rsun``", + " - Only", + "", + ) + ) + + +@_skip_docstring_tests_with_optimized_python +def test_unit_definition_module_normal_summary(): + from astropy.units import photometric + + entries = photometric.__doc__.split("\n\n") + assert len(entries) == 10 + assert entries[5] == "\n".join( + ( + " * - ``AB``", + " - AB magnitude zero flux density (magnitude ``ABmag``).", + " - :math:`\\mathrm{3.6307805 \\times 10^{-20}\\,\\frac{erg}{Hz\\,s\\,cm^{2}}}`", + " - ``ABflux``", + " - No", + ) + ) + assert entries[8] == "\n".join( + ( + " * - ``mgy``", + " - Maggies - a linear flux unit that is the flux for a mag=0 object.To tie this onto a specific calibrated unit system, the zero_point_flux equivalency should be used.", + " - ", + " - ``maggy``", + " - Yes", + ) + ) + + +@_skip_docstring_tests_with_optimized_python +def test_unit_definition_module_function_units_summary(): + from astropy.units.function import units + + entries = units.__doc__.split("\n\n") + assert len(entries) == 14 + assert entries[8] == "\n".join( + ( + ".. list-table:: Available Magnitude Units", + " :header-rows: 1", + " :widths: 10 50 10", + ) + ) + assert entries[9] == "\n".join( + ( + " * - Unit", + " - Description", + " - Represents", + ) + ) + assert entries[12] == "\n".join( + ( + " * - ``M_bol``", + " - Absolute bolometric magnitude: M_bol=0 corresponds to :math:`\\mathrm{3.0128 \\times 10^{28}\\,W}`", + " - mag(Bol)", + ) + ) diff --git a/astropy/units/tests/test_equivalencies.py b/astropy/units/tests/test_equivalencies.py new file mode 100644 index 000000000000..35c794c148a4 --- /dev/null +++ b/astropy/units/tests/test_equivalencies.py @@ -0,0 +1,1025 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +"""Separate tests specifically for equivalencies.""" + +import numpy as np + +# THIRD-PARTY +import pytest +from numpy.testing import assert_allclose + +# LOCAL +from astropy import constants +from astropy import units as u +from astropy.tests.helper import assert_quantity_allclose +from astropy.units.equivalencies import Equivalency +from astropy.utils.exceptions import AstropyDeprecationWarning + + +def test_find_equivalent_units(): + # Only finds units in the namespace + e1 = (u.m**3).find_equivalent_units() + assert len(e1) == 1 + assert e1[0] == u.l + # Regression test for gh-16124 + e2 = (u.m**-3).find_equivalent_units() + assert len(e2) == 0 + + +def test_dimensionless_angles(): + # test that the angles_dimensionless option allows one to change + # by any order in radian in the unit (#1161) + rad1 = u.dimensionless_angles() + assert u.radian.to(1, equivalencies=rad1) == 1.0 + assert u.deg.to(1, equivalencies=rad1) == u.deg.to(u.rad) + assert u.steradian.to(1, equivalencies=rad1) == 1.0 + assert u.dimensionless_unscaled.to(u.steradian, equivalencies=rad1) == 1.0 + # now quantities + assert (1.0 * u.radian).to_value(1, equivalencies=rad1) == 1.0 + assert (1.0 * u.deg).to_value(1, equivalencies=rad1) == u.deg.to(u.rad) + assert (1.0 * u.steradian).to_value(1, equivalencies=rad1) == 1.0 + # more complicated example + I = 1.0e45 * u.g * u.cm**2 + Omega = u.cycle / (1.0 * u.s) + Erot = 0.5 * I * Omega**2 + # check that equivalency makes this work + Erot_in_erg1 = Erot.to(u.erg, equivalencies=rad1) + # and check that value is correct + assert_allclose(Erot_in_erg1.value, (Erot / u.radian**2).to_value(u.erg)) + + # test built-in equivalency in subclass + class MyRad1(u.Quantity): + _equivalencies = rad1 + + phase = MyRad1(1.0, u.cycle) + assert phase.to_value(1) == u.cycle.to(u.radian) + + +@pytest.mark.parametrize("log_unit", (u.mag, u.dex, u.dB)) +def test_logarithmic(log_unit): + # check conversion of mag, dB, and dex to dimensionless and vice versa + with pytest.raises(u.UnitsError): + log_unit.to(1, 0.0) + with pytest.raises(u.UnitsError): + u.dimensionless_unscaled.to(log_unit) + + assert log_unit.to(1, 0.0, equivalencies=u.logarithmic()) == 1.0 + assert u.dimensionless_unscaled.to(log_unit, equivalencies=u.logarithmic()) == 0.0 + # also try with quantities + + q_dex = np.array([0.0, -1.0, 1.0, 2.0]) * u.dex + q_expected = 10.0**q_dex.value * u.dimensionless_unscaled + q_log_unit = q_dex.to(log_unit) + assert np.all(q_log_unit.to(1, equivalencies=u.logarithmic()) == q_expected) + assert np.all(q_expected.to(log_unit, equivalencies=u.logarithmic()) == q_log_unit) + with u.set_enabled_equivalencies(u.logarithmic()): + assert np.all(np.abs(q_log_unit - q_expected.to(log_unit)) < 1.0e-10 * log_unit) + + +doppler_functions = [u.doppler_optical, u.doppler_radio, u.doppler_relativistic] + + +@pytest.mark.parametrize("function", doppler_functions) +def test_doppler_frequency_0(function): + rest = 105.01 * u.GHz + velo0 = rest.to(u.km / u.s, equivalencies=function(rest)) + assert velo0.value == 0 + + +@pytest.mark.parametrize("function", doppler_functions) +def test_doppler_wavelength_0(function): + rest = 105.01 * u.GHz + q1 = 0.00285489437196 * u.m + velo0 = q1.to(u.km / u.s, equivalencies=function(rest)) + np.testing.assert_almost_equal(velo0.value, 0, decimal=6) + + +@pytest.mark.parametrize("function", doppler_functions) +def test_doppler_energy_0(function): + rest = 105.01 * u.GHz + q1 = 0.0004342864648539744 * u.eV + velo0 = q1.to(u.km / u.s, equivalencies=function(rest)) + np.testing.assert_almost_equal(velo0.value, 0, decimal=6) + + +@pytest.mark.parametrize("function", doppler_functions) +def test_doppler_frequency_circle(function): + rest = 105.01 * u.GHz + shifted = 105.03 * u.GHz + velo = shifted.to(u.km / u.s, equivalencies=function(rest)) + freq = velo.to(u.GHz, equivalencies=function(rest)) + np.testing.assert_almost_equal(freq.value, shifted.value, decimal=7) + + +@pytest.mark.parametrize("function", doppler_functions) +def test_doppler_wavelength_circle(function): + rest = 105.01 * u.nm + shifted = 105.03 * u.nm + velo = shifted.to(u.km / u.s, equivalencies=function(rest)) + wav = velo.to(u.nm, equivalencies=function(rest)) + np.testing.assert_almost_equal(wav.value, shifted.value, decimal=7) + + +@pytest.mark.parametrize("function", doppler_functions) +def test_doppler_energy_circle(function): + rest = 1.0501 * u.eV + shifted = 1.0503 * u.eV + velo = shifted.to(u.km / u.s, equivalencies=function(rest)) + en = velo.to(u.eV, equivalencies=function(rest)) + np.testing.assert_almost_equal(en.value, shifted.value, decimal=7) + + +values_ghz = (999.899940784289, 999.8999307714406, 999.8999357778647) + + +@pytest.mark.parametrize( + ("function", "value"), list(zip(doppler_functions, values_ghz)) +) +def test_30kms(function, value): + rest = 1000 * u.GHz + velo = 30 * u.km / u.s + shifted = velo.to(u.GHz, equivalencies=function(rest)) + np.testing.assert_almost_equal(shifted.value, value, decimal=7) + + +bad_values = (5, 5 * u.Jy, None) + + +@pytest.mark.parametrize( + ("function", "value"), list(zip(doppler_functions, bad_values)) +) +def test_bad_restfreqs(function, value): + with pytest.raises(u.UnitsError): + function(value) + + +@pytest.mark.parametrize( + ("z", "rv_ans"), + [ + (0, 0 * (u.km / u.s)), + (0.001, 299642.56184583 * (u.m / u.s)), + (-1, -2.99792458e8 * (u.m / u.s)), + ], +) +def test_doppler_redshift(z, rv_ans): + z_in = z * u.dimensionless_unscaled + rv_out = z_in.to(u.km / u.s, u.doppler_redshift()) + z_out = rv_out.to(u.dimensionless_unscaled, u.doppler_redshift()) + assert_quantity_allclose(rv_out, rv_ans) + assert_quantity_allclose(z_out, z_in) # Check roundtrip + + +def test_doppler_redshift_no_cosmology(): + from astropy.cosmology.units import redshift + + with pytest.raises(u.UnitConversionError, match="not convertible"): + (0 * (u.km / u.s)).to(redshift, u.doppler_redshift()) + + +def test_massenergy(): + # The relative tolerance of these tests is set by the uncertainties + # in the charge of the electron, which is known to about + # 3e-9 (relative tolerance). Therefore, we limit the + # precision of the tests to 1e-7 to be safe. The masses are + # (loosely) known to ~ 5e-8 rel tolerance, so we couldn't test to + # 1e-7 if we used the values from astropy.constants; that is, + # they might change by more than 1e-7 in some future update, so instead + # they are hardwired here. + + # Electron, proton, neutron, muon, 1g + mass_eV = u.Quantity( + [510.998928e3, 938.272046e6, 939.565378e6, 105.6583715e6, 5.60958884539e32], + u.eV, + ) + mass_g = u.Quantity( + [9.10938291e-28, 1.672621777e-24, 1.674927351e-24, 1.88353147e-25, 1], u.g + ) + # Test both ways + assert np.allclose( + mass_eV.to_value(u.g, equivalencies=u.mass_energy()), mass_g.value, rtol=1e-7 + ) + assert np.allclose( + mass_g.to_value(u.eV, equivalencies=u.mass_energy()), mass_eV.value, rtol=1e-7 + ) + + # Basic tests of 'derived' equivalencies + # Surface density + sdens_eV = u.Quantity(5.60958884539e32, u.eV / u.m**2) + sdens_g = u.Quantity(1e-4, u.g / u.cm**2) + assert np.allclose( + sdens_eV.to_value(u.g / u.cm**2, equivalencies=u.mass_energy()), + sdens_g.value, + rtol=1e-7, + ) + assert np.allclose( + sdens_g.to_value(u.eV / u.m**2, equivalencies=u.mass_energy()), + sdens_eV.value, + rtol=1e-7, + ) + + # Density + dens_eV = u.Quantity(5.60958884539e32, u.eV / u.m**3) + dens_g = u.Quantity(1e-6, u.g / u.cm**3) + assert np.allclose( + dens_eV.to_value(u.g / u.cm**3, equivalencies=u.mass_energy()), + dens_g.value, + rtol=1e-7, + ) + assert np.allclose( + dens_g.to_value(u.eV / u.m**3, equivalencies=u.mass_energy()), + dens_eV.value, + rtol=1e-7, + ) + + # Power + pow_eV = u.Quantity(5.60958884539e32, u.eV / u.s) + pow_g = u.Quantity(1, u.g / u.s) + assert np.allclose( + pow_eV.to_value(u.g / u.s, equivalencies=u.mass_energy()), + pow_g.value, + rtol=1e-7, + ) + assert np.allclose( + pow_g.to_value(u.eV / u.s, equivalencies=u.mass_energy()), + pow_eV.value, + rtol=1e-7, + ) + + +def test_is_equivalent(): + assert u.m.is_equivalent(u.pc) + assert u.cycle.is_equivalent(u.mas) + assert not u.cycle.is_equivalent(u.dimensionless_unscaled) + assert u.cycle.is_equivalent(u.dimensionless_unscaled, u.dimensionless_angles()) + assert not (u.Hz.is_equivalent(u.J)) + assert u.Hz.is_equivalent(u.J, u.spectral()) + assert u.J.is_equivalent(u.Hz, u.spectral()) + assert u.pc.is_equivalent(u.arcsecond, u.parallax()) + assert u.arcminute.is_equivalent(u.au, u.parallax()) + + # Pass a tuple for multiple possibilities + assert u.cm.is_equivalent((u.m, u.s, u.kg)) + assert u.ms.is_equivalent((u.m, u.s, u.kg)) + assert u.g.is_equivalent((u.m, u.s, u.kg)) + assert not u.L.is_equivalent((u.m, u.s, u.kg)) + assert not (u.km / u.s).is_equivalent((u.m, u.s, u.kg)) + + +def test_parallax(): + a = u.arcsecond.to(u.pc, 10, u.parallax()) + assert_allclose(a, 0.10, rtol=1.0e-12) + b = u.pc.to(u.arcsecond, a, u.parallax()) + assert_allclose(b, 10, rtol=1.0e-12) + + a = u.arcminute.to(u.au, 1, u.parallax()) + assert_allclose(a, 3437.746770785, rtol=1.0e-12) + b = u.au.to(u.arcminute, a, u.parallax()) + assert_allclose(b, 1, rtol=1.0e-12) + + val = (-1 * u.mas).to(u.pc, u.parallax()) + assert np.isnan(val.value) + + val = (-1 * u.mas).to_value(u.pc, u.parallax()) + assert np.isnan(val) + + +def test_parallax2(): + a = u.arcsecond.to(u.pc, [0.1, 2.5], u.parallax()) + assert_allclose(a, [10, 0.4], rtol=1.0e-12) + + +def test_spectral(): + a = u.AA.to(u.Hz, 1, u.spectral()) + assert_allclose(a, 2.9979245799999995e18) + b = u.Hz.to(u.AA, a, u.spectral()) + assert_allclose(b, 1) + + a = u.AA.to(u.MHz, 1, u.spectral()) + assert_allclose(a, 2.9979245799999995e12) + b = u.MHz.to(u.AA, a, u.spectral()) + assert_allclose(b, 1) + + a = u.m.to(u.Hz, 1, u.spectral()) + assert_allclose(a, 2.9979245799999995e8) + b = u.Hz.to(u.m, a, u.spectral()) + assert_allclose(b, 1) + + +def test_spectral2(): + a = u.nm.to(u.J, 500, u.spectral()) + assert_allclose(a, 3.972891366538605e-19) + b = u.J.to(u.nm, a, u.spectral()) + assert_allclose(b, 500) + + a = u.AA.to(u.Hz, 1, u.spectral()) + b = u.Hz.to(u.J, a, u.spectral()) + c = u.AA.to(u.J, 1, u.spectral()) + assert_allclose(b, c) + + c = u.J.to(u.Hz, b, u.spectral()) + assert_allclose(a, c) + + +def test_spectral3(): + a = u.nm.to(u.Hz, [1000, 2000], u.spectral()) + assert_allclose(a, [2.99792458e14, 1.49896229e14]) + + +@pytest.mark.parametrize( + ("in_val", "in_unit"), + [ + ([0.1, 5000.0, 10000.0], u.AA), + ([1e5, 2.0, 1.0], u.micron**-1), + ([2.99792458e19, 5.99584916e14, 2.99792458e14], u.Hz), + ([1.98644568e-14, 3.97289137e-19, 1.98644568e-19], u.J), + ], +) +def test_spectral4(in_val, in_unit): + """Wave number conversion w.r.t. wavelength, freq, and energy.""" + # Spectroscopic and angular + out_units = [u.micron**-1, u.radian / u.micron] + answers = [[1e5, 2.0, 1.0], [6.28318531e05, 12.5663706, 6.28318531]] + + for out_unit, ans in zip(out_units, answers): + # Forward + a = in_unit.to(out_unit, in_val, u.spectral()) + assert_allclose(a, ans) + + # Backward + b = out_unit.to(in_unit, ans, u.spectral()) + assert_allclose(b, in_val) + + +@pytest.mark.parametrize( + "wav", (3500 * u.AA, 8.5654988e14 * u.Hz, 1 / (3500 * u.AA), 5.67555959e-19 * u.J) +) +def test_spectraldensity2(wav): + # flux density + flambda = u.erg / u.angstrom / u.cm**2 / u.s + fnu = u.erg / u.Hz / u.cm**2 / u.s + + a = flambda.to(fnu, 1, u.spectral_density(wav)) + assert_allclose(a, 4.086160166177361e-12) + + # integrated flux + f_int = u.erg / u.cm**2 / u.s + phot_int = u.ph / u.cm**2 / u.s + + a = f_int.to(phot_int, 1, u.spectral_density(wav)) + assert_allclose(a, 1.7619408e11) + + a = phot_int.to(f_int, 1, u.spectral_density(wav)) + assert_allclose(a, 5.67555959e-12) + + # luminosity density + llambda = u.erg / u.angstrom / u.s + lnu = u.erg / u.Hz / u.s + + a = llambda.to(lnu, 1, u.spectral_density(wav)) + assert_allclose(a, 4.086160166177361e-12) + + a = lnu.to(llambda, 1, u.spectral_density(wav)) + assert_allclose(a, 2.44728537142857e11) + + +def test_spectraldensity3(): + # Define F_nu in Jy + f_nu = u.Jy + + # Define F_lambda in ergs / cm^2 / s / micron + f_lambda = u.erg / u.cm**2 / u.s / u.micron + + # 1 GHz + one_ghz = u.Quantity(1, u.GHz) + + # Convert to ergs / cm^2 / s / Hz + assert_allclose(f_nu.to(u.erg / u.cm**2 / u.s / u.Hz, 1.0), 1.0e-23, 10) + + # Convert to ergs / cm^2 / s at 10 Ghz + assert_allclose( + f_nu.to( + u.erg / u.cm**2 / u.s, 1.0, equivalencies=u.spectral_density(one_ghz * 10) + ), + 1.0e-13, + ) + + # Convert to F_lambda at 1 Ghz + assert_allclose( + f_nu.to(f_lambda, 1.0, equivalencies=u.spectral_density(one_ghz)), + 3.335640951981521e-20, + ) + + # Convert to Jy at 1 Ghz + assert_allclose( + f_lambda.to(u.Jy, 1.0, equivalencies=u.spectral_density(one_ghz)), + 1.0 / 3.335640951981521e-20, + ) + + # Convert to ergs / cm^2 / s at 10 microns + assert_allclose( + f_lambda.to( + u.erg / u.cm**2 / u.s, + 1.0, + equivalencies=u.spectral_density(u.Quantity(10, u.micron)), + ), + 10.0, + ) + + +def test_spectraldensity4(): + """PHOTLAM and PHOTNU conversions.""" + flam = u.erg / (u.cm**2 * u.s * u.AA) + fnu = u.erg / (u.cm**2 * u.s * u.Hz) + photlam = u.photon / (u.cm**2 * u.s * u.AA) + photnu = u.photon / (u.cm**2 * u.s * u.Hz) + + wave = u.Quantity([4956.8, 4959.55, 4962.3], u.AA) + flux_photlam = [9.7654e-3, 1.003896e-2, 9.78473e-3] + flux_photnu = [8.00335589e-14, 8.23668949e-14, 8.03700310e-14] + flux_flam = [3.9135e-14, 4.0209e-14, 3.9169e-14] + flux_fnu = [3.20735792e-25, 3.29903646e-25, 3.21727226e-25] + flux_jy = [3.20735792e-2, 3.29903646e-2, 3.21727226e-2] + flux_stmag = [12.41858665, 12.38919182, 12.41764379] + flux_abmag = [12.63463143, 12.60403221, 12.63128047] + + # PHOTLAM <--> FLAM + assert_allclose( + photlam.to(flam, flux_photlam, u.spectral_density(wave)), flux_flam, rtol=1e-6 + ) + assert_allclose( + flam.to(photlam, flux_flam, u.spectral_density(wave)), flux_photlam, rtol=1e-6 + ) + + # PHOTLAM <--> FNU + assert_allclose( + photlam.to(fnu, flux_photlam, u.spectral_density(wave)), flux_fnu, rtol=1e-6 + ) + assert_allclose( + fnu.to(photlam, flux_fnu, u.spectral_density(wave)), flux_photlam, rtol=1e-6 + ) + + # PHOTLAM <--> Jy + assert_allclose( + photlam.to(u.Jy, flux_photlam, u.spectral_density(wave)), flux_jy, rtol=1e-6 + ) + assert_allclose( + u.Jy.to(photlam, flux_jy, u.spectral_density(wave)), flux_photlam, rtol=1e-6 + ) + + # PHOTLAM <--> PHOTNU + assert_allclose( + photlam.to(photnu, flux_photlam, u.spectral_density(wave)), + flux_photnu, + rtol=1e-6, + ) + assert_allclose( + photnu.to(photlam, flux_photnu, u.spectral_density(wave)), + flux_photlam, + rtol=1e-6, + ) + + # PHOTNU <--> FNU + assert_allclose( + photnu.to(fnu, flux_photnu, u.spectral_density(wave)), flux_fnu, rtol=1e-6 + ) + assert_allclose( + fnu.to(photnu, flux_fnu, u.spectral_density(wave)), flux_photnu, rtol=1e-6 + ) + + # PHOTNU <--> FLAM + assert_allclose( + photnu.to(flam, flux_photnu, u.spectral_density(wave)), flux_flam, rtol=1e-6 + ) + assert_allclose( + flam.to(photnu, flux_flam, u.spectral_density(wave)), flux_photnu, rtol=1e-6 + ) + + # PHOTLAM <--> STMAG + assert_allclose( + photlam.to(u.STmag, flux_photlam, u.spectral_density(wave)), + flux_stmag, + rtol=1e-6, + ) + assert_allclose( + u.STmag.to(photlam, flux_stmag, u.spectral_density(wave)), + flux_photlam, + rtol=1e-6, + ) + + # PHOTLAM <--> ABMAG + assert_allclose( + photlam.to(u.ABmag, flux_photlam, u.spectral_density(wave)), + flux_abmag, + rtol=1e-6, + ) + assert_allclose( + u.ABmag.to(photlam, flux_abmag, u.spectral_density(wave)), + flux_photlam, + rtol=1e-6, + ) + + +def test_spectraldensity5(): + """Test photon luminosity density conversions.""" + L_la = u.erg / (u.s * u.AA) + L_nu = u.erg / (u.s * u.Hz) + phot_L_la = u.photon / (u.s * u.AA) + phot_L_nu = u.photon / (u.s * u.Hz) + + wave = u.Quantity([4956.8, 4959.55, 4962.3], u.AA) + flux_phot_L_la = [9.7654e-3, 1.003896e-2, 9.78473e-3] + flux_phot_L_nu = [8.00335589e-14, 8.23668949e-14, 8.03700310e-14] + flux_L_la = [3.9135e-14, 4.0209e-14, 3.9169e-14] + flux_L_nu = [3.20735792e-25, 3.29903646e-25, 3.21727226e-25] + + # PHOTLAM <--> FLAM + assert_allclose( + phot_L_la.to(L_la, flux_phot_L_la, u.spectral_density(wave)), + flux_L_la, + rtol=1e-6, + ) + assert_allclose( + L_la.to(phot_L_la, flux_L_la, u.spectral_density(wave)), + flux_phot_L_la, + rtol=1e-6, + ) + + # PHOTLAM <--> FNU + assert_allclose( + phot_L_la.to(L_nu, flux_phot_L_la, u.spectral_density(wave)), + flux_L_nu, + rtol=1e-6, + ) + assert_allclose( + L_nu.to(phot_L_la, flux_L_nu, u.spectral_density(wave)), + flux_phot_L_la, + rtol=1e-6, + ) + + # PHOTLAM <--> PHOTNU + assert_allclose( + phot_L_la.to(phot_L_nu, flux_phot_L_la, u.spectral_density(wave)), + flux_phot_L_nu, + rtol=1e-6, + ) + assert_allclose( + phot_L_nu.to(phot_L_la, flux_phot_L_nu, u.spectral_density(wave)), + flux_phot_L_la, + rtol=1e-6, + ) + + # PHOTNU <--> FNU + assert_allclose( + phot_L_nu.to(L_nu, flux_phot_L_nu, u.spectral_density(wave)), + flux_L_nu, + rtol=1e-6, + ) + assert_allclose( + L_nu.to(phot_L_nu, flux_L_nu, u.spectral_density(wave)), + flux_phot_L_nu, + rtol=1e-6, + ) + + # PHOTNU <--> FLAM + assert_allclose( + phot_L_nu.to(L_la, flux_phot_L_nu, u.spectral_density(wave)), + flux_L_la, + rtol=1e-6, + ) + assert_allclose( + L_la.to(phot_L_nu, flux_L_la, u.spectral_density(wave)), + flux_phot_L_nu, + rtol=1e-6, + ) + + +def test_spectraldensity6(): + """Test surface brightness conversions.""" + slam = u.erg / (u.cm**2 * u.s * u.AA * u.sr) + snu = u.erg / (u.cm**2 * u.s * u.Hz * u.sr) + + wave = u.Quantity([4956.8, 4959.55, 4962.3], u.AA) + sb_flam = [3.9135e-14, 4.0209e-14, 3.9169e-14] + sb_fnu = [3.20735792e-25, 3.29903646e-25, 3.21727226e-25] + + # S(nu) <--> S(lambda) + assert_allclose(snu.to(slam, sb_fnu, u.spectral_density(wave)), sb_flam, rtol=1e-6) + assert_allclose(slam.to(snu, sb_flam, u.spectral_density(wave)), sb_fnu, rtol=1e-6) + + +@pytest.mark.parametrize( + ("from_unit", "to_unit"), + [ + (u.ph / u.cm**2 / u.s, (u.cm * u.cm * u.s) ** -1), + (u.ph / u.cm**2 / u.s, u.erg / (u.cm * u.cm * u.s * u.keV)), + (u.erg / u.cm**2 / u.s, (u.cm * u.cm * u.s) ** -1), + (u.erg / u.cm**2 / u.s, u.erg / (u.cm * u.cm * u.s * u.keV)), + ], +) +def test_spectraldensity_not_allowed(from_unit, to_unit): + """Not allowed to succeed as + per https://github.com/astropy/astropy/pull/10015 + """ + with pytest.raises(u.UnitConversionError, match="not convertible"): + from_unit.to(to_unit, 1, u.spectral_density(1 * u.AA)) + + # The other way + with pytest.raises(u.UnitConversionError, match="not convertible"): + to_unit.to(from_unit, 1, u.spectral_density(1 * u.AA)) + + +def test_equivalent_units(): + from astropy.units import imperial + + with u.add_enabled_units(imperial): + units = u.g.find_equivalent_units() + units_set = set(units) + match = { + u.M_e, u.M_p, u.g, u.kg, u.solMass, u.t, u.u, u.M_earth, + u.M_jup, imperial.oz, imperial.lb, imperial.st, imperial.ton, + imperial.slug, + } # fmt: skip + assert units_set == match + + r = repr(units) + assert r.count("\n") == len(units) + 2 + + +def test_equivalent_units2(): + units = set(u.Hz.find_equivalent_units(u.spectral())) + match = { + u.AU, u.Angstrom, u.Hz, u.J, u.Ry, u.cm, u.eV, u.erg, u.lyr, u.lsec, + u.m, u.micron, u.pc, u.solRad, u.Bq, u.Ci, u.k, u.earthRad, + u.jupiterRad, u.foe, + } # fmt: skip + assert units == match + + from astropy.units import imperial + + with u.add_enabled_units(imperial): + units = set(u.Hz.find_equivalent_units(u.spectral())) + match = { + u.AU, u.Angstrom, imperial.BTU, u.Hz, u.J, u.Ry, + imperial.cal, u.cm, u.eV, u.erg, imperial.ft, imperial.fur, + imperial.inch, imperial.kcal, u.lyr, u.m, imperial.mi, u.lsec, + imperial.mil, u.micron, u.pc, u.solRad, imperial.yd, u.Bq, u.Ci, + imperial.nmi, u.k, u.earthRad, u.jupiterRad, u.foe, + } # fmt: skip + assert units == match + + units = set(u.Hz.find_equivalent_units(u.spectral())) + match = { + u.AU, u.Angstrom, u.Hz, u.J, u.Ry, u.cm, u.eV, u.erg, u.lyr, u.lsec, + u.m, u.micron, u.pc, u.solRad, u.Bq, u.Ci, u.k, u.earthRad, + u.jupiterRad, u.foe, + } # fmt: skip + assert units == match + + +def test_trivial_equivalency(): + assert u.m.to(u.kg, equivalencies=[(u.m, u.kg)]) == 1.0 + + +def test_invalid_equivalency(): + with pytest.raises(ValueError): + u.m.to(u.kg, equivalencies=[(u.m,)]) + + with pytest.raises(ValueError): + u.m.to(u.kg, equivalencies=[(u.m, 5.0)]) + + +def test_irrelevant_equivalency(): + with pytest.raises(u.UnitsError): + u.m.to(u.kg, equivalencies=[(u.m, u.l)]) + + +def test_brightness_temperature(): + omega_B = np.pi * (50 * u.arcsec) ** 2 + nu = u.GHz * 5 + tb = 7.052587837212582 * u.K + np.testing.assert_almost_equal( + tb.value, + (1 * u.Jy).to_value( + u.K, equivalencies=u.brightness_temperature(nu, beam_area=omega_B) + ), + ) + np.testing.assert_almost_equal( + 1.0, + tb.to_value( + u.Jy, equivalencies=u.brightness_temperature(nu, beam_area=omega_B) + ), + ) + + +def test_surfacebrightness(): + sb = 50 * u.MJy / u.sr + k = sb.to(u.K, u.brightness_temperature(50 * u.GHz)) + np.testing.assert_almost_equal(k.value, 0.650965, 5) + assert k.unit.is_equivalent(u.K) + + +def test_beam(): + # pick a beam area: 2 pi r^2 = area of a Gaussina with sigma=50 arcsec + omega_B = 2 * np.pi * (50 * u.arcsec) ** 2 + new_beam = (5 * u.beam).to(u.sr, u.equivalencies.beam_angular_area(omega_B)) + np.testing.assert_almost_equal(omega_B.to(u.sr).value * 5, new_beam.value) + assert new_beam.unit.is_equivalent(u.sr) + + # make sure that it's still consistent with 5 beams + nbeams = new_beam.to(u.beam, u.equivalencies.beam_angular_area(omega_B)) + np.testing.assert_almost_equal(nbeams.value, 5) + + # test inverse beam equivalency + # (this is just a sanity check that the equivalency is defined; + # it's not for testing numerical consistency) + (5 / u.beam).to(1 / u.sr, u.equivalencies.beam_angular_area(omega_B)) + + # test practical case + # (this is by far the most important one) + flux_density = (5 * u.Jy / u.beam).to( + u.MJy / u.sr, u.equivalencies.beam_angular_area(omega_B) + ) + + np.testing.assert_almost_equal(flux_density.value, 13.5425483146382) + + +def test_thermodynamic_temperature(): + nu = 143 * u.GHz + tb = 0.0026320501262630277 * u.K + eq = u.thermodynamic_temperature(nu, T_cmb=2.7255 * u.K) + np.testing.assert_almost_equal( + tb.value, (1 * (u.MJy / u.sr)).to_value(u.K, equivalencies=eq) + ) + np.testing.assert_almost_equal(1.0, tb.to_value(u.MJy / u.sr, equivalencies=eq)) + + +def test_equivalency_context(): + with u.set_enabled_equivalencies(u.dimensionless_angles()): + phase = u.Quantity(1.0, u.cycle) + assert_allclose(np.exp(1j * phase), 1.0) + Omega = u.cycle / (1.0 * u.minute) + assert_allclose(np.exp(1j * Omega * 60.0 * u.second), 1.0) + # ensure we can turn off equivalencies even within the scope + with pytest.raises(u.UnitsError): + phase.to(1, equivalencies=None) + + # test the manager also works in the Quantity constructor. + q1 = u.Quantity(phase, u.dimensionless_unscaled) + assert_allclose(q1.value, u.cycle.to(u.radian)) + + # and also if we use a class that happens to have a unit attribute. + class MyQuantityLookalike(np.ndarray): + pass + + mylookalike = np.array(1.0).view(MyQuantityLookalike) + mylookalike.unit = "cycle" + # test the manager also works in the Quantity constructor. + q2 = u.Quantity(mylookalike, u.dimensionless_unscaled) + assert_allclose(q2.value, u.cycle.to(u.radian)) + + with u.set_enabled_equivalencies(u.spectral()): + u.GHz.to(u.cm) + eq_on = u.GHz.find_equivalent_units() + with pytest.raises(u.UnitsError): + u.GHz.to(u.cm, equivalencies=None) + + # without equivalencies, we should find a smaller (sub)set + eq_off = u.GHz.find_equivalent_units() + assert all(eq in set(eq_on) for eq in eq_off) + assert set(eq_off) < set(eq_on) + + # Check the equivalency manager also works in ufunc evaluations, + # not just using (wrong) scaling. [#2496] + l2v = u.doppler_optical(6000 * u.angstrom) + l1 = 6010 * u.angstrom + assert l1.to(u.km / u.s, equivalencies=l2v) > 100.0 * u.km / u.s + with u.set_enabled_equivalencies(l2v): + assert l1 > 100.0 * u.km / u.s + assert abs((l1 - 500.0 * u.km / u.s).to(u.angstrom)) < 1.0 * u.km / u.s + + +def test_equivalency_context_manager(): + base_registry = u.get_current_unit_registry() + + def just_to_from_units(equivalencies): + return [(equiv[0], equiv[1]) for equiv in equivalencies] + + tf_dimensionless_angles = just_to_from_units(u.dimensionless_angles()) + tf_spectral = just_to_from_units(u.spectral()) + + # <=1 b/c might have the dimensionless_redshift equivalency enabled. + assert len(base_registry.equivalencies) <= 1 + + with u.set_enabled_equivalencies(u.dimensionless_angles()): + new_registry = u.get_current_unit_registry() + assert set(just_to_from_units(new_registry.equivalencies)) == set( + tf_dimensionless_angles + ) + assert set(new_registry.all_units) == set(base_registry.all_units) + with u.set_enabled_equivalencies(u.spectral()): + newer_registry = u.get_current_unit_registry() + assert set(just_to_from_units(newer_registry.equivalencies)) == set( + tf_spectral + ) + assert set(newer_registry.all_units) == set(base_registry.all_units) + + assert set(just_to_from_units(new_registry.equivalencies)) == set( + tf_dimensionless_angles + ) + assert set(new_registry.all_units) == set(base_registry.all_units) + with u.add_enabled_equivalencies(u.spectral()): + newer_registry = u.get_current_unit_registry() + assert set(just_to_from_units(newer_registry.equivalencies)) == set( + tf_dimensionless_angles + ) | set(tf_spectral) + assert set(newer_registry.all_units) == set(base_registry.all_units) + + assert base_registry is u.get_current_unit_registry() + + +def test_temperature(): + from astropy.units.imperial import deg_F, deg_R + + t_k = 0 * u.K + assert_allclose(t_k.to_value(u.deg_C, u.temperature()), -273.15) + assert_allclose(t_k.to_value(deg_F, u.temperature()), -459.67) + t_k = 20 * deg_F + assert_allclose(t_k.to_value(deg_R, u.temperature()), 479.67) + t_k = 20 * deg_R + assert_allclose(t_k.to_value(deg_F, u.temperature()), -439.67) + t_k = 20 * u.deg_C + assert_allclose(t_k.to_value(deg_R, u.temperature()), 527.67) + t_k = 20 * deg_R + assert_allclose(t_k.to_value(u.deg_C, u.temperature()), -262.039, atol=0.01) + + +def test_temperature_energy(): + x = 1000 * u.K + y = (x * constants.k_B).to(u.keV) + assert_allclose(x.to_value(u.keV, u.temperature_energy()), y.value) + assert_allclose(y.to_value(u.K, u.temperature_energy()), x.value) + + +def test_molar_mass_amu(): + x = 1 * (u.g / u.mol) + y = 1 * u.u + assert_allclose(x.to_value(u.u, u.molar_mass_amu()), y.value) + assert_allclose(y.to_value(u.g / u.mol, u.molar_mass_amu()), x.value) + with pytest.raises(u.UnitsError): + x.to(u.u) + + +def test_compose_equivalencies(): + x = u.Unit("arcsec").compose(units=(u.pc,), equivalencies=u.parallax()) + assert x[0] == u.pc + + x = u.Unit("2 arcsec").compose(units=(u.pc,), equivalencies=u.parallax()) + assert x[0] == u.Unit(0.5 * u.pc) + + x = u.degree.compose(equivalencies=u.dimensionless_angles()) + assert u.Unit(u.degree.to(u.radian)) in x + + x = (u.nm).compose( + units=(u.m, u.s), equivalencies=u.doppler_optical(0.55 * u.micron) + ) + for y in x: + if y.bases == [u.m, u.s]: + assert y.powers == [1, -1] + assert_allclose( + y.scale, + u.nm.to(u.m / u.s, equivalencies=u.doppler_optical(0.55 * u.micron)), + ) + break + else: + raise AssertionError("Didn't find speed in compose results") + + +def test_pixel_scale(): + pix = 75 * u.pix + asec = 30 * u.arcsec + + pixscale = 0.4 * u.arcsec / u.pix + pixscale2 = 2.5 * u.pix / u.arcsec + + assert_quantity_allclose(pix.to(u.arcsec, u.pixel_scale(pixscale)), asec) + assert_quantity_allclose(pix.to(u.arcmin, u.pixel_scale(pixscale)), asec) + + assert_quantity_allclose(pix.to(u.arcsec, u.pixel_scale(pixscale2)), asec) + assert_quantity_allclose(pix.to(u.arcmin, u.pixel_scale(pixscale2)), asec) + + assert_quantity_allclose(asec.to(u.pix, u.pixel_scale(pixscale)), pix) + assert_quantity_allclose(asec.to(u.pix, u.pixel_scale(pixscale2)), pix) + + +def test_pixel_scale_invalid_scale_unit(): + pixscale = 0.4 * u.arcsec + pixscale2 = 0.4 * u.arcsec / u.pix**2 + + with pytest.raises(u.UnitsError, match="pixel dimension"): + u.pixel_scale(pixscale) + with pytest.raises(u.UnitsError, match="pixel dimension"): + u.pixel_scale(pixscale2) + + +def test_pixel_scale_acceptable_scale_unit(): + pix = 75 * u.pix + v = 3000 * (u.cm / u.s) + + pixscale = 0.4 * (u.m / u.s / u.pix) + pixscale2 = 2.5 * (u.pix / (u.m / u.s)) + + assert_quantity_allclose(pix.to(u.m / u.s, u.pixel_scale(pixscale)), v) + assert_quantity_allclose(pix.to(u.km / u.s, u.pixel_scale(pixscale)), v) + + assert_quantity_allclose(pix.to(u.m / u.s, u.pixel_scale(pixscale2)), v) + assert_quantity_allclose(pix.to(u.km / u.s, u.pixel_scale(pixscale2)), v) + + assert_quantity_allclose(v.to(u.pix, u.pixel_scale(pixscale)), pix) + assert_quantity_allclose(v.to(u.pix, u.pixel_scale(pixscale2)), pix) + + +def test_plate_scale(): + mm = 1.5 * u.mm + asec = 30 * u.arcsec + + platescale = 20 * u.arcsec / u.mm + platescale2 = 0.05 * u.mm / u.arcsec + + assert_quantity_allclose(mm.to(u.arcsec, u.plate_scale(platescale)), asec) + assert_quantity_allclose(mm.to(u.arcmin, u.plate_scale(platescale)), asec) + + assert_quantity_allclose(mm.to(u.arcsec, u.plate_scale(platescale2)), asec) + assert_quantity_allclose(mm.to(u.arcmin, u.plate_scale(platescale2)), asec) + + assert_quantity_allclose(asec.to(u.mm, u.plate_scale(platescale)), mm) + assert_quantity_allclose(asec.to(u.mm, u.plate_scale(platescale2)), mm) + + +def test_equivelency(): + ps = u.pixel_scale(10 * u.arcsec / u.pix) + assert isinstance(ps, Equivalency) + assert isinstance(ps.name, list) + assert len(ps.name) == 1 + assert ps.name[0] == "pixel_scale" + assert isinstance(ps.kwargs, list) + assert len(ps.kwargs) == 1 + assert ps.kwargs[0] == {"pixscale": 10 * u.arcsec / u.pix} + + +def test_add_equivelencies(): + e1 = u.pixel_scale(10 * u.arcsec / u.pixel) + u.temperature_energy() + assert isinstance(e1, Equivalency) + assert e1.name == ["pixel_scale", "temperature_energy"] + assert isinstance(e1.kwargs, list) + assert e1.kwargs == [{"pixscale": 10 * u.arcsec / u.pix}, {}] + + e2 = u.pixel_scale(10 * u.arcsec / u.pixel) + [1, 2, 3] + assert isinstance(e2, list) + + +def test_pprint(): + pprint_class = u.UnitBase.EquivalentUnitsList + equiv_units_to_Hz = u.Hz.find_equivalent_units() + assert pprint_class.__repr__(equiv_units_to_Hz).splitlines() == [ + " Primary name | Unit definition | Aliases ", + "[", + " Bq | 1 / s | becquerel ,", + " Ci | 3.7e+10 / s | curie ,", + " Hz | 1 / s | Hertz, hertz ,", + "]", + ] + assert ( + pprint_class._repr_html_(equiv_units_to_Hz) == '' + "" + "" + "" + "" + "
Primary nameUnit definitionAliases
Bq1 / sbecquerel
Ci3.7e+10 / scurie
Hz1 / sHertz, hertz
" + ) + + +def test_spectral_density_factor_deprecation(): + with pytest.warns( + AstropyDeprecationWarning, + match=( + r'^"factor" was deprecated in version 7\.0 and will be removed in a future ' + r'version\. \n Use "wav" as a "Quantity" instead\.$' + ), + ): + a = (u.erg / u.angstrom / u.cm**2 / u.s).to( + u.erg / u.Hz / u.cm**2 / u.s, 1, u.spectral_density(u.AA, factor=3500) + ) + assert_quantity_allclose(a, 4.086160166177361e-12) + + +def test_magnetic_flux_field(): + H = 1 * u.A / u.m + B = (H * constants.mu0).to(u.T) + assert_allclose(H.to_value(u.T, u.magnetic_flux_field()), B.value) + assert_allclose(B.to_value(u.A / u.m, u.magnetic_flux_field()), H.value) + + H = 1 * u.Oe + B = 1 * u.G + assert_allclose(H.to_value(u.G, u.magnetic_flux_field()), 1) + assert_allclose(B.to_value(u.Oe, u.magnetic_flux_field()), 1) + assert_allclose(H.to_value(u.G, u.magnetic_flux_field(mu_r=0.8)), 0.8) + assert_allclose(B.to_value(u.Oe, u.magnetic_flux_field(mu_r=0.8)), 1.25) diff --git a/astropy/units/tests/test_format.py b/astropy/units/tests/test_format.py new file mode 100644 index 000000000000..eaa84725cf75 --- /dev/null +++ b/astropy/units/tests/test_format.py @@ -0,0 +1,1220 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +""" +Regression tests for the units.format package +""" + +import re +import warnings +from collections.abc import Iterable +from contextlib import nullcontext +from fractions import Fraction +from typing import NamedTuple + +import numpy as np +import pytest +from numpy.testing import assert_allclose + +from astropy import units as u +from astropy.constants import si +from astropy.units import ( + PrefixUnit, + Unit, + UnitBase, + UnitParserWarning, + UnitsError, + UnitsWarning, + cds, + dex, +) +from astropy.units import format as u_format +from astropy.units.utils import is_effectively_unity +from astropy.utils.exceptions import AstropyDeprecationWarning + + +class FormatStringPair(NamedTuple): + format: str + string: str + + +class StringUnitPair(NamedTuple): + string: str + unit: UnitBase + + +def list_format_string_pairs(*test_cases: tuple[str, str]) -> list[FormatStringPair]: + return [FormatStringPair(format, string) for format, string in test_cases] + + +def list_string_unit_pairs( + *test_cases: tuple[Iterable[str], UnitBase], +) -> list[StringUnitPair]: + return [ + StringUnitPair(string, unit) + for strings, unit in test_cases + for string in strings + ] + + +@pytest.mark.parametrize( + "test_pair", + list_string_unit_pairs( + (["m s", "m*s", "m.s"], u.m * u.s), + (["m/s", "m*s**-1", "m /s", "m / s", "m/ s"], u.m / u.s), + (["m**2", "m2", "m**(2)", "m**+2", "m+2", "m^(+2)"], u.m**2), + (["m**-3", "m-3", "m^(-3)", "/m3"], u.m**-3), + (["m**(1.5)", "m(3/2)", "m**(3/2)", "m^(3/2)"], u.m**1.5), + (["2.54 cm"], u.Unit(u.cm * 2.54)), + (["10+8m"], u.Unit(u.m * 1e8)), + # This is the VOUnits documentation, but doesn't seem to follow the + # unity grammar (["3.45 10**(-4)Jy"], 3.45 * 1e-4 * u.Jy) + (["sqrt(m)"], u.m**0.5), + (["dB(mW)", "dB (mW)"], u.DecibelUnit(u.mW)), + (["mag"], u.mag), + (["mag(ct/s)"], u.MagUnit(u.ct / u.s)), + (["dex"], u.dex), + (["dex(cm s**-2)", "dex(cm/s2)"], u.DexUnit(u.cm / u.s**2)), + ), + ids=lambda x: x.string, +) +def test_unit_grammar(test_pair: StringUnitPair): + assert u_format.Generic.parse(test_pair.string) == test_pair.unit + + +@pytest.mark.parametrize( + "string", ["sin( /pixel /s)", "mag(mag)", "dB(dB(mW))", "dex()"] +) +def test_unit_grammar_fail(string): + with pytest.raises(ValueError): + u_format.Generic.parse(string) + + +@pytest.mark.parametrize( + "test_pair", + list_string_unit_pairs( + (["0.1nm"], u.AA), + (["mW/m2"], u.Unit(u.erg / u.cm**2 / u.s)), + (["mW/(m2)"], u.Unit(u.erg / u.cm**2 / u.s)), + (["km/s", "km.s-1"], u.km / u.s), + (["km/s/Mpc"], u.km / u.s / u.Mpc), + (["km/(s.Mpc)"], u.km / u.s / u.Mpc), + (["10+3J/m/s/kpc2"], u.Unit(1e3 * u.W / (u.m * u.kpc**2))), + (["10pix/nm"], u.Unit(10 * u.pix / u.nm)), + (["1.5x10+11m"], u.Unit(1.5e11 * u.m)), + (["1.5×10+11/m"], u.Unit(1.5e11 / u.m)), + (["/s"], u.s**-1), + (["m2"], u.m**2), + (["10+21m"], u.Unit(u.m * 1e21)), + (["2.54cm"], u.Unit(u.cm * 2.54)), + (["20%"], 0.20 * u.dimensionless_unscaled), + (["10+9"], 1.0e9 * u.dimensionless_unscaled), + (["2x10-9"], 2.0e-9 * u.dimensionless_unscaled), + (["---"], u.dimensionless_unscaled), + (["ma"], u.ma), + (["mAU"], u.mAU), + (["uarcmin"], u.uarcmin), + (["uarcsec"], u.uarcsec), + (["kbarn"], u.kbarn), + (["Gbit"], u.Gbit), + (["Gibit"], 2**30 * u.bit), + (["kbyte"], u.kbyte), + (["mRy"], 0.001 * u.Ry), + (["mmag"], u.mmag), + (["Mpc"], u.Mpc), + (["Gyr"], u.Gyr), + (["°"], u.degree), + (["°/s"], u.degree / u.s), + (["Å"], u.AA), + (["Å/s"], u.AA / u.s), + (["\\h"], si.h), + (["[cm/s2]"], dex(u.cm / u.s**2)), + (["[K]"], dex(u.K)), + (["[-]"], dex(u.dimensionless_unscaled)), + (["eps0/mu0"], cds.eps0 / cds.mu0), + (["a0.s"], cds.a0 * u.s), + ), + ids=lambda x: x.string, +) +def test_cds_grammar(test_pair: StringUnitPair): + assert u_format.CDS.parse(test_pair.string) == test_pair.unit + + +@pytest.mark.parametrize( + "string", + [ + "0.1 nm", + "solMass(3/2)", + "km / s", + "km s-1", + "km/s.Mpc-1", + "/s.Mpc", + "pix0.1nm", + "pix/(0.1nm)", + "km*s", + "km**2", + "5x8+3m", + "0.1---", + "---m", + "m---", + "--", + "0.1-", + "-m", + "m-", + "mag(s-1)", + "dB(mW)", + "dex(cm s-2)", + "[--]", + ], +) +def test_cds_grammar_fail(string): + with pytest.raises(ValueError): + u_format.CDS.parse(string) + + +def test_cds_dimensionless(): + assert u.Unit("---", format="cds") == u.dimensionless_unscaled + assert u.dimensionless_unscaled.to_string(format="cds") == "---" + + +def test_cds_log10_dimensionless(): + assert u.Unit("[-]", format="cds") == u.dex(u.dimensionless_unscaled) + assert u.dex(u.dimensionless_unscaled).to_string(format="cds") == "[-]" + + +def test_cds_angstrom_str(): + # Regression test for a problem noticed in + # https://github.com/astropy/astropy/pull/17527#discussion_r1880555481 + # that the string representation of the cds version of Angstrom was "AA". + assert str(u.cds.Angstrom) == str(u.Angstrom) == "Angstrom" + # Since this is a NamedUnit, let's check the name for completeness. + assert u.cds.Angstrom.name == "Angstrom" + + +def test_cds_solMass_str(): + # CDS allows writing solar mass as Msun or solMass, + # but cds.solMass and u.solMass should be consistent. + assert u.solMass.to_string("cds") == "solMass" + assert u.cds.solMass.to_string("cds") == "solMass" + + +# These examples are taken from the EXAMPLES section of +# https://heasarc.gsfc.nasa.gov/docs/heasarc/ofwg/docs/general/ogip_93_001/ +@pytest.mark.parametrize( + "test_pair", + list_string_unit_pairs( + ( + ["count /s", "count/s", "count s**(-1)", "count / s", "count /s "], + u.count / u.s, + ), + ( + ["/pixel /s", "/(pixel * s)"], + (u.pixel * u.s) ** -1, + ), + ( + [ + "count /m**2 /s /eV", + "count m**(-2) * s**(-1) * eV**(-1)", + "count /(m**2 * s * eV)", + ], + u.count * u.m**-2 * u.s**-1 * u.eV**-1, + ), + ( + ["erg /pixel /s /GHz", "erg /s /GHz /pixel", "erg /pixel /(s * GHz)"], + u.erg / (u.s * u.GHz * u.pixel), + ), + ( + ["keV**2 /yr /angstrom", "10**(10) keV**2 /yr /m"], + # Though this is given as an example, it seems to violate the rules + # of not raising scales to powers, so I'm just excluding it + # "(10**2 MeV)**2 /yr /m" + u.keV**2 / (u.yr * u.angstrom), + ), + ( + [ + "10**(46) erg /s", + "10**46 erg /s", + "10**(39) J /s", + "10**(39) W", + "10**(15) YW", + "YJ /fs", + ], + 10**46 * u.erg / u.s, + ), + ( + [ + "10**(-7) J /cm**2 /MeV", + "10**(-9) J m**(-2) eV**(-1)", + "nJ m**(-2) eV**(-1)", + "nJ /m**2 /eV", + ], + 10**-7 * u.J * u.cm**-2 * u.MeV**-1, + ), + ( + [ + "sqrt(erg /pixel /s /GHz)", + "(erg /pixel /s /GHz)**(0.5)", + "(erg /pixel /s /GHz)**(1/2)", + "erg**(0.5) pixel**(-0.5) s**(-0.5) GHz**(-0.5)", + ], + (u.erg * u.pixel**-1 * u.s**-1 * u.GHz**-1) ** 0.5, + ), + ( + [ + "(count /s) (/pixel /s)", + "(count /s) * (/pixel /s)", + "count /pixel /s**2", + ], + (u.count / u.s) * (1.0 / (u.pixel * u.s)), + ), + ), + ids=lambda x: x.string, +) +def test_ogip_grammar(test_pair: StringUnitPair): + assert u_format.OGIP.parse(test_pair.string) == test_pair.unit + + +@pytest.mark.parametrize( + "string", + [ + "log(photon /m**2 /s /Hz)", + "sin( /pixel /s)", + "log(photon /cm**2 /s /Hz) /(sin( /pixel /s))", + "log(photon /cm**2 /s /Hz) (sin( /pixel /s))**(-1)", + "dB(mW)", + "dex(cm/s**2)", + ], +) +def test_ogip_grammar_fail(string): + with pytest.raises(ValueError): + u_format.OGIP.parse(string) + + +@pytest.mark.xfail(reason="'acos' is not understood by astropy", raises=ValueError) +def test_ogip_unusable_function(): + u_format.OGIP.parse("acos(m)**2") + + +@pytest.mark.parametrize("string", ["sqrt(m)**3", "sqrt(m**3)", "(sqrt(m))**3"]) +def test_ogip_sqrt(string): + # Regression test for #16743 - sqrt(m)**3 caused a ValueError + assert u_format.OGIP.parse(string) == u.m ** Fraction(3, 2) + + +@pytest.mark.parametrize( + "string,message,unit", + [ + pytest.param( + "m(s)**2", + ( + r"^if 'm\(s\)\*\*2' was meant to be a multiplication, " + r"it should have been written as 'm \(s\)\*\*2'.$" + ), + u.m * u.s**2, + id="m(s)**2", + ), + pytest.param( + "m(s)", + ( + r"^if 'm\(s\)' was meant to be a multiplication, " + r"it should have been written as 'm \(s\)'.$" + ), + u.m * u.s, + id="m(s)", + ), + ], +) +def test_ogip_invalid_multiplication(string, message, unit): + # Regression test for #16749 + with pytest.warns(UnitParserWarning, match=message): + assert u_format.OGIP.parse(string) == unit + + +@pytest.mark.parametrize( + "string,unit,power", + [ + pytest.param("s**-1", u.s**-1, "-1", id="int_unit_power"), + pytest.param("m**-2.0", u.m**-2, "-2.0", id="float_unit_power"), + pytest.param("10**-3 kg", u.g, "-3", id="int_scale_power"), + ], +) +def test_ogip_negative_exponent_parenthesis(string, unit, power): + # Regression test for #16788 - negative powers require parenthesis + with pytest.warns( + UnitParserWarning, + match=( + r"^negative exponents must be enclosed in parenthesis\. " + rf"Expected '\*\*\({power}\)' instead of '\*\*{power}'\.$" + ), + ): + assert u_format.OGIP.parse(string) == unit + + +def test_ogip_ohm(): + # Regression test for #17200 - OGIP converted u.ohm to 'V / A' + assert u_format.OGIP.to_string(u.ohm) == "ohm" + + +@pytest.mark.parametrize( + "string", + [pytest.param("m**(-0.5)", id="float"), pytest.param("m**(-1/2)", id="fraction")], +) +def test_ogip_negative_powers(string): + # Regression test for #18776 - negative fractions were not recognized + assert u_format.OGIP.parse(string) == u.m**-0.5 + + +class RoundtripBase: + def check_roundtrip(self, unit, output_format=None, **kwargs): + if output_format is None: + output_format = self.format_.name + s = unit.to_string(output_format, deprecations="silent", **kwargs) + if s in self.format_._deprecated_units: + with pytest.raises(UnitsError, match="deprecated"): + unit.to_string(output_format, deprecations="raise", **kwargs) + with pytest.warns(UnitsWarning, match="deprecated"): + assert unit.to_string(output_format, **kwargs) == s + with pytest.warns(UnitsWarning, match="deprecated") as w: + a = Unit(s, format=self.format_) + assert len(w) == 1 + else: + a = Unit(s, format=self.format_) # No warning + + assert_allclose(a.decompose().scale, unit.decompose().scale, rtol=1e-9) + + def check_roundtrip_decompose(self, unit): + ud = unit.decompose() + s = ud.to_string(self.format_) + assert " " not in s + a = Unit(s, format=self.format_) + assert_allclose(a.decompose().scale, ud.scale, rtol=1e-5) + + +class TestRoundtripGeneric(RoundtripBase): + format_ = u_format.Generic + + @pytest.mark.parametrize( + "unit", + [ + unit + for unit in u.__dict__.values() + if (isinstance(unit, UnitBase) and not isinstance(unit, PrefixUnit)) + ], + ids=str, + ) + def test_roundtrip(self, unit): + self.check_roundtrip(unit) + self.check_roundtrip(unit, output_format="console") + self.check_roundtrip(unit, output_format="unicode") + self.check_roundtrip_decompose(unit) + + @pytest.mark.parametrize("fraction", [None, "inline"]) + @pytest.mark.parametrize("output_format", [None, "console", "unicode"]) + def test_composite_roundtrip(self, output_format, fraction): + fluxunit = u.erg / (u.cm**2 * u.s) + self.check_roundtrip(fluxunit, output_format=output_format, fraction=fraction) + + +class TestRoundtripVOUnit(RoundtripBase): + format_ = u_format.VOUnit + + @pytest.mark.parametrize( + "unit", + [u for u in u_format.VOUnit._units.values() if not isinstance(u, PrefixUnit)], + ids=str, + ) + def test_roundtrip(self, unit): + self.check_roundtrip(unit) + if unit not in (u.mag, u.dB): + self.check_roundtrip_decompose(unit) + + +class TestRoundtripFITS(RoundtripBase): + format_ = u_format.FITS + + @pytest.mark.parametrize( + "unit", + [u for u in u_format.FITS._units.values() if not isinstance(u, PrefixUnit)], + ids=str, + ) + def test_roundtrip(self, unit): + self.check_roundtrip(unit) + + +class TestRoundtripCDS(RoundtripBase): + format_ = u_format.CDS + + @pytest.mark.parametrize( + "unit", + [u for u in u_format.CDS._units.values() if not isinstance(u, PrefixUnit)], + ids=str, + ) + def test_roundtrip(self, unit): + self.check_roundtrip(unit) + if unit == u.mag: + # Skip mag: decomposes into dex, which is unknown to CDS. + return + + self.check_roundtrip_decompose(unit) + + @pytest.mark.parametrize( + "unit", [u.dex(unit) for unit in (u.cm / u.s**2, u.K, u.Lsun)], ids=str + ) + def test_roundtrip_dex(self, unit): + string = unit.to_string(format="cds") + recovered = u.Unit(string, format="cds") + assert recovered == unit + + +class TestRoundtripOGIP(RoundtripBase): + format_ = u_format.OGIP + + @pytest.mark.parametrize( + "unit", + [ + unit + for unit in u_format.OGIP._units.values() + if (isinstance(unit, UnitBase) and not isinstance(unit, PrefixUnit)) + ], + ids=str, + ) + def test_roundtrip(self, unit): + if str(unit) == "0.001 Crab": + # Special-case mCrab, which the default check does not recognize + # as a deprecated unit. + with pytest.warns(UnitsWarning): + s = unit.to_string(self.format_) + a = Unit(s, format=self.format_) + assert_allclose(a.decompose().scale, unit.decompose().scale, rtol=1e-9) + else: + self.check_roundtrip(unit) + if str(unit) in ("mag", "byte", "Crab"): + # Skip mag and byte, which decompose into dex and bit, resp., + # both of which are unknown to OGIP, as well as Crab, which does + # not decompose, and thus gives a deprecated unit warning. + return + + power_of_ten = np.log10(unit.decompose().scale) + if abs(power_of_ten - round(power_of_ten)) > 1e-3: + ctx = pytest.warns(UnitsWarning, match="power of 10") + elif str(unit) == "0.001 Crab": + ctx = pytest.warns(UnitsWarning, match="deprecated") + else: + ctx = nullcontext() + with ctx: + self.check_roundtrip_decompose(unit) + + +@pytest.mark.parametrize( + "unit_formatter_class,n_units", + [(u_format.FITS, 766), (u_format.VOUnit, 1304), (u_format.CDS, 3326)], +) +def test_units_available(unit_formatter_class, n_units): + assert len(unit_formatter_class._units) == n_units + + +def test_cds_non_ascii_unit(): + """Regression test for #5350. This failed with a decoding error as + Îŧas could not be represented in ascii.""" + with cds.enable(): + u.radian.find_equivalent_units(include_prefix_units=True) + + +def test_latex(): + fluxunit = u.erg / (u.cm**2 * u.s) + assert fluxunit.to_string("latex") == r"$\mathrm{\frac{erg}{s\,cm^{2}}}$" + + +def test_new_style_latex(): + fluxunit = u.erg / (u.cm**2 * u.s) + assert f"{fluxunit:latex}" == r"$\mathrm{\frac{erg}{s\,cm^{2}}}$" + + +def test_latex_scale(): + fluxunit = u.Unit(1.0e-24 * u.erg / (u.cm**2 * u.s * u.Hz)) + latex = r"$\mathrm{1 \times 10^{-24}\,\frac{erg}{Hz\,s\,cm^{2}}}$" + assert fluxunit.to_string("latex") == latex + + +def test_latex_inline_scale(): + fluxunit = u.Unit(1.0e-24 * u.erg / (u.cm**2 * u.s * u.Hz)) + latex_inline = r"$\mathrm{1 \times 10^{-24}\,erg\,Hz^{-1}\,s^{-1}\,cm^{-2}}$" + assert fluxunit.to_string("latex_inline") == latex_inline + + +@pytest.mark.parametrize( + "format_spec, string, decomposed", + [ + ("generic", "erg / (Angstrom s cm2)", "1e+07 kg / (m s3)"), + ("s", "erg / (Angstrom s cm2)", "1e+07 kg / (m s3)"), + ("console", "erg Angstrom^-1 s^-1 cm^-2", "10000000 kg m^-1 s^-3"), + ( + "latex", + r"$\mathrm{\frac{erg}{\mathring{A}\,s\,cm^{2}}}$", + r"$\mathrm{10000000\,\frac{kg}{m\,s^{3}}}$", + ), + ( + "latex_inline", + r"$\mathrm{erg\,\mathring{A}^{-1}\,s^{-1}\,cm^{-2}}$", + r"$\mathrm{10000000\,kg\,m^{-1}\,s^{-3}}$", + ), + ("unicode", "erg Åâģš sâģš cmâģ²", "10000000 kg mâģš sâģÂŗ"), + (">25s", " erg / (Angstrom s cm2)", " 1e+07 kg / (m s3)"), + ("cds", "erg.Angstrom-1.s-1.cm-2", "10000000kg.m-1.s-3"), + ("ogip", "erg / (angstrom s cm**2)", "1e+07 kg / (m s**3)"), + ("fits", "erg Angstrom-1 s-1 cm-2", "10**7 kg m-1 s-3"), + ("vounit", "erg.Angstrom**-1.s**-1.cm**-2", "10000000kg.m**-1.s**-3"), + # TODO: make fits and vounit less awful! + ], +) +def test_format_styles(format_spec, string, decomposed): + fluxunit = u.erg / (u.cm**2 * u.s * u.Angstrom) + if format_spec == "vounit": + # erg and Angstrom are deprecated in vounit. + with pytest.warns(UnitsWarning, match="deprecated"): + formatted = format(fluxunit, format_spec) + else: + formatted = format(fluxunit, format_spec) + assert formatted == string + # Decomposed mostly to test that scale factors are dealt with properly + # in the various formats. + assert format(fluxunit.decompose(), format_spec) == decomposed + + +@pytest.mark.parametrize( + "format_spec, fraction, string, decomposed", + [ + ("generic", False, "erg s-1 cm-2", "0.001 kg s-3"), + ( + "console", + "multiline", + " erg \n------\ns cm^2", + " kg \n0.001 ---\n s^3", + ), + ("console", "inline", "erg / (s cm^2)", "0.001 kg / s^3"), + ("unicode", "multiline", " erg \n─────\ns cm²", " kg\n0.001 ──\n sÂŗ"), + ("unicode", "inline", "erg / (s cm²)", "0.001 kg / sÂŗ"), + ( + "latex", + False, + r"$\mathrm{erg\,s^{-1}\,cm^{-2}}$", + r"$\mathrm{0.001\,kg\,s^{-3}}$", + ), + ( + "latex", + "inline", + r"$\mathrm{erg / (s\,cm^{2})}$", + r"$\mathrm{0.001\,kg / s^{3}}$", + ), + # TODO: make generic with fraction=False less awful! + ], +) +def test_format_styles_non_default_fraction(format_spec, fraction, string, decomposed): + fluxunit = u.erg / (u.cm**2 * u.s) + assert fluxunit.to_string(format_spec, fraction=fraction) == string + assert fluxunit.decompose().to_string(format_spec, fraction=fraction) == decomposed + + +@pytest.mark.parametrize("format_spec", u_format.Base.registry) +def test_multiline_fraction_different_if_available(format_spec): + fluxunit = u.W / u.m**2 + inline_format = fluxunit.to_string(format_spec, fraction="inline") + if format_spec in ["generic", "cds", "fits", "ogip", "vounit"]: + with pytest.warns(UnitsWarning, match="does not support multiline"): + multiline_format = fluxunit.to_string(format_spec, fraction="multiline") + assert multiline_format == inline_format + else: + multiline_format = fluxunit.to_string(format_spec, fraction="multiline") + assert multiline_format != inline_format + + +@pytest.mark.parametrize("format_spec", u_format.Base.registry) +def test_unknown_fraction_style(format_spec): + fluxunit = u.W / u.m**2 + msg = r"^fraction can only be False, 'inline', or 'multiline', not 'parrot'\.$" + with pytest.raises(ValueError, match=msg): + fluxunit.to_string(format_spec, fraction="parrot") + + +def test_flatten_to_known(): + myunit = u.def_unit("FOOBAR_One", u.erg / u.Hz) + assert myunit.to_string("fits") == "erg Hz-1" + myunit2 = myunit * u.bit**3 + assert myunit2.to_string("fits") == "bit3 erg Hz-1" + + +def test_flatten_impossible(): + myunit = u.def_unit("FOOBAR_Two") + with u.add_enabled_units(myunit), pytest.raises(ValueError): + myunit.to_string("fits") + + +def test_console_out(): + """ + Issue #436. + """ + u.Jy.decompose().to_string("console") + + +@pytest.mark.parametrize( + "test_pair", + list_format_string_pairs( + ("generic", "10"), + ("console", "10"), + ("unicode", "10"), + ("cds", "10"), + ("latex", r"$\mathrm{10}$"), + ), + ids=lambda x: x.format, +) +def test_scale_only(test_pair: FormatStringPair): + assert u.Unit(10).to_string(test_pair.format) == test_pair.string + + +def test_flexible_float(): + assert u.min._represents.to_string("latex") == r"$\mathrm{60\,s}$" + + +def test_fits_to_string_function_error(): + """Test function raises TypeError on bad input. + + This instead of returning None, see gh-11825. + """ + + with pytest.raises(TypeError, match="unit argument must be"): + u_format.FITS.to_string(None) + + +def test_fraction_repr(): + area = u.cm**2.0 + assert "." not in area.to_string("latex") + + fractional = u.cm**2.5 + assert "5/2" in fractional.to_string("latex") + + assert fractional.to_string("unicode") == "cmâĩ⸍²" + + +def test_scale_effectively_unity(): + """Scale just off unity at machine precision level is OK. + Ensures #748 does not recur + """ + a = (3.0 * u.N).cgs + assert is_effectively_unity(a.unit.scale) + assert len(a.__repr__().split()) == 3 + + +def test_percent(): + """Test that the % unit is properly recognized. Since % is a special + symbol, this goes slightly beyond the round-tripping tested above.""" + assert u.Unit("%") == u.percent == u.Unit(0.01) + + assert u.Unit("%", format="cds") == u.Unit(0.01) + assert u.Unit(0.01).to_string("cds") == "%" + + with pytest.raises(ValueError): + u.Unit("%", format="fits") + + with pytest.raises(ValueError): + u.Unit("%", format="vounit") + + +def test_scaled_dimensionless(): + """Test that scaled dimensionless units are properly recognized in generic + and CDS, but not in fits and vounit.""" + assert u.Unit("0.1") == u.Unit(0.1) == 0.1 * u.dimensionless_unscaled + assert u.Unit("1.e-4") == u.Unit(1.0e-4) + + assert u.Unit("10-4", format="cds") == u.Unit(1.0e-4) + assert u.Unit("10+8").to_string("cds") == "10+8" + + with pytest.raises(ValueError): + u.Unit(0.15).to_string("fits") + + assert u.Unit(0.1).to_string("fits") == "10**-1" + + with pytest.raises(ValueError): + u.Unit(0.1).to_string("vounit") + + +def test_deprecated_did_you_mean_units(): + with pytest.raises(ValueError) as exc_info: + u.Unit("ANGSTROM", format="fits") + assert "Did you mean Angstrom or angstrom?" in str(exc_info.value) + + with pytest.raises(ValueError) as exc_info: + u.Unit("crab", format="ogip") + assert "Crab (deprecated)" in str(exc_info.value) + assert "mCrab (deprecated)" in str(exc_info.value) + + with pytest.raises( + ValueError, + match=( + r"Did you mean 0\.1nm, Angstrom \(deprecated\) or angstrom \(deprecated\)\?" + ), + ): + u.Unit("ANGSTROM", format="vounit") + + with pytest.warns(UnitsWarning, match=r".* 0\.1nm\.") as w: + u.Unit("angstrom", format="vounit") + assert len(w) == 1 + + +def test_invalid_deprecated_units_handling(): + with pytest.raises( + ValueError, + match=( + r"^invalid deprecation handling option: 'ignore'\. Valid options are " + r"'silent', 'warn', 'raise', 'convert'\.$" + ), + ): + u.erg.to_string(format="vounit", deprecations="ignore") + + +@pytest.mark.parametrize( + "unit,string", + [ + pytest.param(u.erg, "cm**2.g.s**-2", id="simple unit"), + pytest.param(u.erg / u.s, "cm**2.g.s**-3", id="composite unit"), + ], +) +def test_deprecated_units_conversion_success(unit, string): + assert unit.to_string(format="vounit", deprecations="convert") == string + + +def test_deprecated_units_conversion_failure(): + Crab = u_format.OGIP._units["Crab"] + with pytest.warns( + UnitsWarning, + match=( + r"^The unit 'Crab' has been deprecated in the OGIP standard\. " + r"It cannot be automatically converted\.$" + ), + ): + assert Crab.to_string(format="ogip", deprecations="convert") == "Crab" + + +@pytest.mark.parametrize("string", ["mag(ct/s)", "dB(mW)", "dex(cm s**-2)"]) +def test_fits_function(string): + # Function units cannot be written, so ensure they're not parsed either. + with pytest.raises(ValueError): + u_format.FITS().parse(string) + + +@pytest.mark.parametrize("string", ["mag(ct/s)", "dB(mW)", "dex(cm s**-2)"]) +def test_vounit_function(string): + # Function units cannot be written, so ensure they're not parsed either. + with pytest.raises(ValueError), warnings.catch_warnings(): + # ct, dex also raise warnings - irrelevant here. + warnings.simplefilter("ignore") + u_format.VOUnit().parse(string) + + +def test_vounit_binary_prefix(): + assert u.Unit("KiB", format="vounit") == u.Unit("1024 B") + assert u.Unit("Kibyte", format="vounit") == u.Unit("1024 B") + assert u.Unit("Kibit", format="vounit") == u.Unit("128 B") + with pytest.raises(ValueError, match="not supported by the VOUnit standard"): + u.Unit("kibibyte", format="vounit") + + +def test_vounit_unknown(): + assert u.Unit("unknown", format="vounit") is None + assert u.Unit("UNKNOWN", format="vounit") is None + assert u.Unit("", format="vounit") is u.dimensionless_unscaled + + +def test_vounit_details(): + assert u.Unit("Pa", format="vounit") is u.Pascal + assert u.Unit("ka", format="vounit") == u.Unit("1000 yr") + assert u.Unit("pix", format="vounit") == u.Unit("pixel", format="vounit") + + # Regression test for astropy/astroquery#2480 + assert u.Unit("Sun", format="vounit") is u.Sun + + # Test that adding a prefix to a simple units raises a warning + with pytest.warns( + UnitsWarning, match="Unit 'kdB' not supported by the VOUnit standard.*" + ): + u.Unit("kdB", format="vounit", parse_strict="warn") + + # The da- prefix is not allowed, and the d- prefix is discouraged + assert u.dam.to_string("vounit") == "10m" + assert u.Unit("dam dag").to_string("vounit") == "100g.m" + + # Parse round-trip + with pytest.warns(UnitsWarning, match="deprecated"): + flam = u.erg / u.cm / u.cm / u.s / u.AA + x = u.format.VOUnit.to_string(flam) + assert x == "erg.Angstrom**-1.s**-1.cm**-2" + new_flam = u.format.VOUnit.parse(x) + assert new_flam == flam + + +@pytest.mark.parametrize( + "unit, vounit, number, scale, voscale", + [ + ("nm", "nm", 0.1, "10^-1", "0.1"), + ("fm", "fm", 100.0, "10+2", "100"), + ("m^2", "m**2", 100.0, "100.0", "100"), + ("cm", "cm", 2.54, "2.54", "2.54"), + ("kg", "kg", 1.898124597e27, "1.898124597E27", "1.8981246e+27"), + ("m/s", "m.s**-1", 299792458.0, "299792458", "2.9979246e+08"), + ("cm2", "cm**2", 1.0e-20, "10^(-20)", "1e-20"), + ], +) +def test_vounit_scale_factor(unit, vounit, number, scale, voscale): + x = u.Unit(f"{scale} {unit}") + assert x == number * u.Unit(unit) + assert x.to_string(format="vounit") == voscale + vounit + + +@pytest.mark.parametrize( + "unit, vounit", + [ + ("m s^-1", "m/s"), + ("s^-1", "1/s"), + ("100 s^-2", "100/s**2"), + ("kg m-1 s-2", "kg/(m.s**2)"), + ], +) +@pytest.mark.parametrize("fraction", [True, "inline"]) +def test_vounit_fraction(unit, vounit, fraction): + x = u.Unit(unit) + assert x.to_string(format="vounit", fraction=fraction) == vounit + + +@pytest.mark.parametrize( + "unit, vounit", + [ + ("m^2", "m**2"), + ("s^-1", "s**-1"), + ("s(0.333)", "s**(0.333)"), + ("s(-0.333)", "s**(-0.333)"), + ("s(1/3)", "s**(1/3)"), + ("s(-1/3)", "s**(-1/3)"), + ], +) +def test_vounit_power(unit, vounit): + x = u.Unit(unit) + assert x.to_string(format="vounit") == vounit + + +def test_vounit_custom(): + x = u.Unit("'foo' m", format="vounit") + x_vounit = x.to_string("vounit") + assert x_vounit == "'foo'.m" + x_string = x.to_string() + assert x_string == "foo m" + + x = u.Unit("m'foo' m", format="vounit") + assert x.bases[1]._represents.scale == 0.001 + x_vounit = x.to_string("vounit") + assert x_vounit == "m.m'foo'" + x_string = x.to_string() + assert x_string == "m mfoo" + + +def test_vounit_implicit_custom(): + # Yikes, this becomes "femto-urlong"... But at least there's a warning. + with pytest.warns(UnitsWarning) as w: + x = u.Unit("furlong/week", format="vounit", parse_strict="warn") + assert x.bases[0]._represents.scale == 1e-15 + assert x.bases[0]._represents.bases[0].name == "urlong" + assert len(w) == 2 + assert "furlong" in str(w[0].message) + assert "week" in str(w[1].message) + + +@pytest.mark.parametrize( + "scale, number, string", + [ + ("10+2", 100, "10**2"), + ("10(+2)", 100, "10**2"), + ("10**+2", 100, "10**2"), + ("10**(+2)", 100, "10**2"), + ("10^+2", 100, "10**2"), + ("10^(+2)", 100, "10**2"), + ("10**2", 100, "10**2"), + ("10**(2)", 100, "10**2"), + ("10^2", 100, "10**2"), + ("10^(2)", 100, "10**2"), + ("10-20", 10 ** (-20), "10**-20"), + ("10(-20)", 10 ** (-20), "10**-20"), + ("10**-20", 10 ** (-20), "10**-20"), + ("10**(-20)", 10 ** (-20), "10**-20"), + ("10^-20", 10 ** (-20), "10**-20"), + ("10^(-20)", 10 ** (-20), "10**-20"), + ], +) +def test_fits_scale_factor(scale, number, string): + x = u.Unit(scale + " erg/(s cm**2 Angstrom)", format="fits") + assert x == number * (u.erg / u.s / u.cm**2 / u.Angstrom) + assert x.to_string(format="fits") == string + " erg Angstrom-1 s-1 cm-2" + + x = u.Unit(scale + "*erg/(s cm**2 Angstrom)", format="fits") + assert x == number * (u.erg / u.s / u.cm**2 / u.Angstrom) + assert x.to_string(format="fits") == string + " erg Angstrom-1 s-1 cm-2" + + +def test_fits_scale_factor_errors(): + with pytest.raises(ValueError): + x = u.Unit("1000 erg/(s cm**2 Angstrom)", format="fits") + + with pytest.raises(ValueError): + x = u.Unit("12 erg/(s cm**2 Angstrom)", format="fits") + + x = u.Unit(1.2 * u.erg) + with pytest.raises(ValueError): + x.to_string(format="fits") + + x = u.Unit(100.0 * u.erg) + assert x.to_string(format="fits") == "10**2 erg" + + +@pytest.mark.parametrize( + "unit, latex, unicode", + [ + (u.deg, r"$\mathrm{{}^{\circ}}$", "°"), + (u.deg**2, r"$\mathrm{deg^{2}}$", "deg²"), + (u.arcmin, r"$\mathrm{{}^{\prime}}$", "′"), + (u.arcmin**2, r"$\mathrm{arcmin^{2}}$", "arcmin²"), + (u.arcsec, r"$\mathrm{{}^{\prime\prime}}$", "â€ŗ"), + (u.arcsec**2, r"$\mathrm{arcsec^{2}}$", "arcsec²"), + (u.hourangle, r"$\mathrm{{}^{h}}$", "ʰ"), + (u.hourangle**2, r"$\mathrm{hourangle^{2}}$", "hourangle²"), + (u.electron, r"$\mathrm{e^{-}}$", "eâģ"), + (u.electron**2, r"$\mathrm{electron^{2}}$", "electron²"), + ], +) +def test_double_superscript(unit, latex, unicode): + """Regression test for #5870, #8699, #9218, #14403; avoid double superscripts.""" + assert unit.to_string("latex") == latex + assert unit.to_string("unicode") == unicode + + +def test_no_prefix_superscript(): + """Regression test for gh-911 and #14419.""" + assert u.mdeg.to_string("latex") == r"$\mathrm{mdeg}$" + assert u.narcmin.to_string("latex") == r"$\mathrm{narcmin}$" + assert u.parcsec.to_string("latex") == r"$\mathrm{parcsec}$" + assert u.mdeg.to_string("unicode") == "mdeg" + assert u.narcmin.to_string("unicode") == "narcmin" + assert u.parcsec.to_string("unicode") == "parcsec" + + +@pytest.mark.parametrize( + "power,expected", + ( + (1.0, "m"), + (2.0, "m2"), + (-10, "1 / m10"), + (1.5, "m(3/2)"), + (2 / 3, "m(2/3)"), + (7 / 11, "m(7/11)"), + (-1 / 64, "1 / m(1/64)"), + (1 / 100, "m(1/100)"), + (2 / 101, "m(0.019801980198019802)"), + (Fraction(2, 101), "m(2/101)"), + ), +) +def test_powers(power, expected): + """Regression test for #9279 - powers should not be oversimplified.""" + unit = u.m**power + s = unit.to_string() + assert s == expected + assert unit == s + + +@pytest.mark.parametrize( + "string,unit", + [ + ("\N{MICRO SIGN}g", u.microgram), + ("\N{GREEK SMALL LETTER MU}g", u.microgram), + ("g\N{MINUS SIGN}1", u.g ** (-1)), + ("m\N{SUPERSCRIPT MINUS}\N{SUPERSCRIPT ONE}", u.m**-1), + ("m s\N{SUPERSCRIPT MINUS}\N{SUPERSCRIPT ONE}", u.m / u.s), + ("m\N{SUPERSCRIPT TWO}", u.m**2), + ("m\N{SUPERSCRIPT PLUS SIGN}\N{SUPERSCRIPT TWO}", u.m**2), + ("m\N{SUPERSCRIPT THREE}", u.m**3), + ("m\N{SUPERSCRIPT ONE}\N{SUPERSCRIPT ZERO}", u.m**10), + ("\N{GREEK CAPITAL LETTER OMEGA}", u.ohm), + ("\N{OHM SIGN}", u.ohm), # deprecated but for compatibility + ("\N{MICRO SIGN}\N{GREEK CAPITAL LETTER OMEGA}", u.microOhm), + ("\N{ANGSTROM SIGN}", u.Angstrom), + ("\N{ANGSTROM SIGN} \N{OHM SIGN}", u.Angstrom * u.Ohm), + ("\N{LATIN CAPITAL LETTER A WITH RING ABOVE}", u.Angstrom), + ("\N{LATIN CAPITAL LETTER A}\N{COMBINING RING ABOVE}", u.Angstrom), + ("m\N{ANGSTROM SIGN}", u.milliAngstrom), + ("°C", u.deg_C), + ("°", u.deg), + ("M⊙", u.Msun), # \N{CIRCLED DOT OPERATOR} + ("L☉", u.Lsun), # \N{SUN} + ("M⊕", u.Mearth), # normal earth symbol = \N{CIRCLED PLUS} + ("M♁", u.Mearth), # be generous with \N{EARTH} + ("R♃", u.Rjup), # \N{JUPITER} + ("′", u.arcmin), # \N{PRIME} + ("R∞", u.Ry), + ("Mₚ", u.M_p), + ], +) +def test_unicode(string, unit): + assert u_format.Generic.parse(string) == unit + assert u.Unit(string) == unit + # Should work in composites too. + assert u.Unit(f"{string}/s") == unit / u.s + assert u.Unit(f"m {string}") == u.m * unit + assert u.Unit(f"{string} {string}") == unit**2 + # Not obvious that "°2" should be "deg**2", but not easy to reject, + # and "R♃²" should work. But don't run on examples with a space or that + # already end in a number. + if re.match(r"^\S*[^\dâ°ÂšÂ˛Âŗâ´âĩâļ⁡⁸⁚]$", string): + assert u.Unit(f"{string}2") == unit**2 + assert u.Unit(f"{string}/{string}") == u.dimensionless_unscaled + # Finally, check round-trip + assert u.Unit(unit.to_string("unicode")) == unit + + +@pytest.mark.parametrize( + "string", + [ + "g\N{MICRO SIGN}", + "g\N{MINUS SIGN}", + "m\N{SUPERSCRIPT MINUS}1", + "m+\N{SUPERSCRIPT ONE}", + "m\N{MINUS SIGN}\N{SUPERSCRIPT ONE}", + "k\N{ANGSTROM SIGN}", + ], +) +def test_unicode_failures(string): + with pytest.raises(ValueError): + u.Unit(string) + + +@pytest.mark.parametrize("format_", ("unicode", "latex", "latex_inline")) +def test_parse_error_message_for_output_only_format(format_): + with pytest.raises(NotImplementedError, match="not parse"): + u.Unit("m", format=format_) + + +@pytest.mark.parametrize( + "parser,error_type,err_msg_start", + [ + pytest.param("foo", ValueError, "Unknown format 'foo'", id="ValueError"), + pytest.param( + {}, TypeError, "Expected a formatter name, not {}", id="TypeError" + ), + ], +) +def test_unknown_parser(parser, error_type, err_msg_start): + with pytest.raises( + error_type, + match=( + f"^{err_msg_start}\\.\nValid parser names are: " + "'cds', 'generic', 'fits', 'ogip', 'vounit'$" + ), + ): + u.Unit("m", format=parser) + + +@pytest.mark.parametrize( + "formatter,error_type,err_msg_start", + [ + pytest.param("abc", ValueError, "Unknown format 'abc'", id="ValueError"), + pytest.param( + float, + TypeError, + "Expected a formatter name, not ", + id="TypeError", + ), + ], +) +def test_unknown_output_format(formatter, error_type, err_msg_start): + with pytest.raises( + error_type, + match=( + f"^{err_msg_start}\\.\nValid formatter names are: " + "'cds', 'console', 'generic', 'fits', 'latex', 'latex_inline', 'ogip', " + "'unicode', 'vounit'$" + ), + ): + u.m.to_string(formatter) + + +def test_celsius_fits(): + assert u.Unit("Celsius", format="fits") == u.deg_C + assert u.Unit("deg C", format="fits") == u.deg_C + + # check that compounds do what we expect: what do we expect? + assert u.Unit("deg C kg-1", format="fits") == u.C * u.deg / u.kg + assert u.Unit("Celsius kg-1", format="fits") == u.deg_C / u.kg + assert u.deg_C.to_string("fits") == "Celsius" + + +@pytest.mark.parametrize( + "test_pair", + list_format_string_pairs( + ("generic", "dB(1 / m)"), + ("latex", r"$\mathrm{dB\left(\frac{1}{m}\right)}$"), + ("latex_inline", r"$\mathrm{dB\left(m^{-1}\right)}$"), + ("console", "dB(m^-1)"), + ("unicode", "dB(mâģš)"), + ), + ids=lambda x: x.format, +) +def test_function_format_styles(test_pair: FormatStringPair): + dbunit = u.decibel(u.m**-1) + assert dbunit.to_string(test_pair.format) == test_pair.string + assert f"{dbunit:{test_pair.format}}" == test_pair.string + + +@pytest.mark.parametrize( + "format_spec, fraction, string", + [ + ("console", "multiline", " 1\ndB(-)\n m"), + ("console", "inline", "dB(1 / m)"), + ("unicode", "multiline", " 1\ndB(─)\n m"), + ("unicode", "inline", "dB(1 / m)"), + ("latex", False, r"$\mathrm{dB\left(m^{-1}\right)}$"), + ("latex", "inline", r"$\mathrm{dB\left(1 / m\right)}$"), + ], +) +def test_function_format_styles_non_default_fraction(format_spec, fraction, string): + dbunit = u.decibel(u.m**-1) + assert dbunit.to_string(format_spec, fraction=fraction) == string + + +@pytest.mark.parametrize( + "test_pair", + list_format_string_pairs( + ("", "1"), + (".1g", "1"), + (".3g", "1"), + (".1e", "1.0"), + (".1f", "1.0"), + (".3e", "1.000"), + ), + ids=lambda x: repr(x.format), +) +def test_format_latex_one(test_pair: FormatStringPair): + # see https://github.com/astropy/astropy/issues/12571 + assert ( + u_format.Latex.format_exponential_notation(1, test_pair.format) + == test_pair.string + ) + + +def test_Fits_name_deprecation(): + with pytest.warns( + AstropyDeprecationWarning, + match=( + r'^The class "Fits" has been renamed to "FITS" in version 7\.0\. ' + r"The old name is deprecated and may be removed in a future version\.\n" + r" Use FITS instead\.$" + ), + ): + from astropy.units.format import Fits + assert Fits is u.format.FITS + + +@pytest.mark.parametrize("format_spec", ["generic", "unicode"]) +def test_liter(format_spec): + assert format(u.liter, format_spec) == "l" diff --git a/astropy/units/tests/test_logarithmic.py b/astropy/units/tests/test_logarithmic.py new file mode 100644 index 000000000000..52f92af598cf --- /dev/null +++ b/astropy/units/tests/test_logarithmic.py @@ -0,0 +1,1051 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +""" +Test the Logarithmic Units and Quantities +""" + +import pickle + +import numpy as np +import pytest +from numpy.testing import assert_allclose + +from astropy import constants as c +from astropy import units as u +from astropy.tests.helper import assert_quantity_allclose + +lu_units = [u.dex, u.mag, u.decibel] + +lu_subclasses = [u.DexUnit, u.MagUnit, u.DecibelUnit] + +lq_subclasses = [u.Dex, u.Magnitude, u.Decibel] + +pu_sample = (u.dimensionless_unscaled, u.m, u.g / u.s**2, u.Jy) + +mJy = np.arange(1.0, 5.0).reshape(2, 2) * u.mag(u.Jy) +m1 = np.arange(1.0, 5.5, 0.5).reshape(3, 3) * u.mag() +log_quantity_parametrization = pytest.mark.parametrize( + "mag", [mJy, m1], ids=lambda x: str(x.unit.physical_unit.physical_type) +) + + +class TestLogUnitCreation: + def test_logarithmic_units(self): + """Check logarithmic units are set up correctly.""" + assert u.dB.to(u.dex) == 0.1 + assert u.dex.to(u.mag) == -2.5 + assert u.mag.to(u.dB) == -4 + + @pytest.mark.parametrize("lu_unit, lu_cls", list(zip(lu_units, lu_subclasses))) + def test_callable_units(self, lu_unit, lu_cls): + assert isinstance(lu_unit, u.UnitBase) + assert callable(lu_unit) + assert lu_unit._function_unit_class is lu_cls + + @pytest.mark.parametrize("lu_unit", lu_units) + def test_equality_to_normal_unit_for_dimensionless(self, lu_unit): + lu = lu_unit() + assert lu == lu._default_function_unit # eg, MagUnit() == u.mag + assert lu._default_function_unit == lu # and u.mag == MagUnit() + + @pytest.mark.parametrize("physical_unit", pu_sample) + @pytest.mark.parametrize("lu_unit", lu_units) + def test_call_units(self, lu_unit, physical_unit): + """Create a LogUnit subclass using the callable unit and physical unit, + and do basic check that output is right.""" + lu1 = lu_unit(physical_unit) + assert lu1.physical_unit == physical_unit + assert lu1.function_unit == lu1._default_function_unit + + def test_call_invalid_unit(self): + with pytest.raises(TypeError): + u.mag([]) + with pytest.raises(ValueError): + u.mag(u.mag()) + + @pytest.mark.parametrize("physical_unit", pu_sample) + @pytest.mark.parametrize("lu_cls", lu_subclasses + [u.LogUnit]) + def test_subclass_creation(self, lu_cls, physical_unit): + """Create a LogUnit subclass object for given physical unit, + and do basic check that output is right.""" + lu1 = lu_cls(physical_unit) + assert lu1.physical_unit == physical_unit + assert lu1.function_unit == lu1._default_function_unit + + lu2 = lu_cls(physical_unit, function_unit=2 * lu1._default_function_unit) + assert lu2.physical_unit == physical_unit + assert lu2.function_unit == u.Unit(2 * lu2._default_function_unit) + + with pytest.raises(ValueError): + lu_cls(physical_unit, u.m) + + def test_lshift_magnitude(self): + mag = 1.0 << u.ABmag + assert isinstance(mag, u.Magnitude) + assert mag.unit == u.ABmag + assert mag.value == 1.0 + # same test for an array, which should produce a view + a2 = np.arange(10.0) + q2 = a2 << u.ABmag + assert isinstance(q2, u.Magnitude) + assert q2.unit == u.ABmag + assert np.all(q2.value == a2) + a2[9] = 0.0 + assert np.all(q2.value == a2) + # a different magnitude unit + mag = 10.0 << u.STmag + assert isinstance(mag, u.Magnitude) + assert mag.unit == u.STmag + assert mag.value == 10.0 + + def test_ilshift_magnitude(self): + # test in-place operation and conversion + mag_fnu_cgs = u.mag(u.erg / u.s / u.cm**2 / u.Hz) + m = np.arange(10.0) * u.mag(u.Jy) + jy = m.physical + m2 = m << mag_fnu_cgs + assert np.all(m2 == m.to(mag_fnu_cgs)) + m2 = m + m <<= mag_fnu_cgs + assert m is m2 # Check it was done in-place! + assert np.all(m.value == m2.value) + assert m.unit == mag_fnu_cgs + # Check it works if equivalencies are in-place. + with u.add_enabled_equivalencies(u.spectral_density(5500 * u.AA)): + st = jy.to(u.ST) + m <<= u.STmag + + assert m is m2 + assert_quantity_allclose(m.physical, st) + assert m.unit == u.STmag + + def test_lshift_errors(self): + m = np.arange(10.0) * u.mag(u.Jy) + with pytest.raises(u.UnitsError): + m << u.STmag + + with pytest.raises(u.UnitsError): + m << u.Jy + + with pytest.raises(u.UnitsError): + m <<= u.STmag + + with pytest.raises(u.UnitsError): + m <<= u.Jy + + +def test_predefined_magnitudes(): + assert_quantity_allclose( + (-21.1 * u.STmag).physical, 1.0 * u.erg / u.cm**2 / u.s / u.AA + ) + assert_quantity_allclose( + (-48.6 * u.ABmag).physical, 1.0 * u.erg / u.cm**2 / u.s / u.Hz + ) + + assert_quantity_allclose((0 * u.M_bol).physical, c.L_bol0) + assert_quantity_allclose( + (0 * u.m_bol).physical, c.L_bol0 / (4.0 * np.pi * (10.0 * c.pc) ** 2) + ) + + +def test_predefined_reinitialisation(): + assert u.mag("STflux") == u.STmag + assert u.mag("ABflux") == u.ABmag + assert u.mag("Bol") == u.M_bol + assert u.mag("bol") == u.m_bol + + # required for backwards-compatibility, at least unless deprecated + assert u.mag("ST") == u.STmag + assert u.mag("AB") == u.ABmag + + +def test_predefined_string_roundtrip(): + """Ensure round-tripping; see #5015""" + assert u.Unit(u.STmag.to_string()) == u.STmag + assert u.Unit(u.ABmag.to_string()) == u.ABmag + assert u.Unit(u.M_bol.to_string()) == u.M_bol + assert u.Unit(u.m_bol.to_string()) == u.m_bol + + +def test_inequality(): + """Check __ne__ works (regression for #5342).""" + lu1 = u.mag(u.Jy) + lu2 = u.dex(u.Jy) + lu3 = u.mag(u.Jy**2) + lu4 = lu3 - lu1 + assert lu1 != lu2 + assert lu1 != lu3 + assert lu1 == lu4 + + +class TestLogUnitStrings: + def test_str(self): + """Do some spot checks that str, repr, etc. work as expected.""" + lu1 = u.mag(u.Jy) + assert str(lu1) == "mag(Jy)" + assert repr(lu1) == 'Unit("mag(Jy)")' + assert lu1.to_string("generic") == "mag(Jy)" + with pytest.raises(ValueError): + lu1.to_string("fits") + with pytest.raises(ValueError): + lu1.to_string(format="cds") + + lu2 = u.dex() + assert str(lu2) == "dex" + assert repr(lu2) == 'Unit("dex(1)")' + assert lu2.to_string() == "dex(1)" + + lu3 = u.MagUnit(u.Jy, function_unit=2 * u.mag) + assert str(lu3) == "2 mag(Jy)" + assert repr(lu3) == 'MagUnit("Jy", unit="2 mag")' + assert lu3.to_string() == "2 mag(Jy)" + + lu4 = u.mag(u.ct) + assert lu4.to_string("generic") == "mag(ct)" + latex_str = r"$\mathrm{mag\left(ct\right)}$" + assert lu4.to_string("latex") == latex_str + assert lu4.to_string("latex_inline") == latex_str + assert lu4._repr_latex_() == latex_str + + lu5 = u.mag(u.ct / u.s) + latex_str = r"$\mathrm{mag\left(\frac{ct}{s}\right)}$" + assert lu5.to_string("latex") == latex_str + latex_str = r"$\mathrm{mag\left(ct\,s^{-1}\right)}$" + assert lu5.to_string("latex_inline") == latex_str + + def test_dex_latex_str(self): + # Regression test for gh-18618. + lu = u.dex(u.cm / u.s**2) + latex_str = r"$\mathrm{dex\left(\frac{cm}{s^{2}}\right)}$" + assert lu.to_string(format="latex") == latex_str + assert lu._repr_latex_() == latex_str + + +class TestLogUnitConversion: + @pytest.mark.parametrize("physical_unit", pu_sample) + @pytest.mark.parametrize("lu_unit", lu_units) + def test_physical_unit_conversion(self, lu_unit, physical_unit): + """Check various LogUnit subclasses are equivalent and convertible + to their non-log counterparts.""" + lu1 = lu_unit(physical_unit) + assert lu1.is_equivalent(physical_unit) + assert lu1.to(physical_unit, 0.0) == 1.0 + + assert physical_unit.is_equivalent(lu1) + assert physical_unit.to(lu1, 1.0) == 0.0 + + pu = u.Unit(8.0 * physical_unit) + assert lu1.is_equivalent(physical_unit) + assert lu1.to(pu, 0.0) == 0.125 + + assert pu.is_equivalent(lu1) + assert_allclose(pu.to(lu1, 0.125), 0.0, atol=1.0e-15) + + # Check we round-trip. + value = np.linspace(0.0, 10.0, 6) + assert_allclose(pu.to(lu1, lu1.to(pu, value)), value, atol=1.0e-15) + # And that we're not just returning True all the time. + pu2 = u.g + assert not lu1.is_equivalent(pu2) + with pytest.raises(u.UnitsError): + lu1.to(pu2) + + assert not pu2.is_equivalent(lu1) + with pytest.raises(u.UnitsError): + pu2.to(lu1) + + @pytest.mark.parametrize("lu_unit", lu_units) + def test_container_unit_conversion(self, lu_unit): + """Check that conversion to logarithmic units (u.mag, u.dB, u.dex) + is only possible when the physical unit is dimensionless.""" + values = np.linspace(0.0, 10.0, 6) + lu1 = lu_unit(u.dimensionless_unscaled) + assert lu1.is_equivalent(lu1.function_unit) + assert_allclose(lu1.to(lu1.function_unit, values), values) + + lu2 = lu_unit(u.Jy) + assert not lu2.is_equivalent(lu2.function_unit) + with pytest.raises(u.UnitsError): + lu2.to(lu2.function_unit, values) + + @pytest.mark.parametrize("physical_unit", pu_sample) + @pytest.mark.parametrize("tlu_unit", lu_units) + @pytest.mark.parametrize("flu_unit", lu_units) + def test_subclass_conversion(self, flu_unit, tlu_unit, physical_unit): + """Check various LogUnit subclasses are equivalent and convertible + to each other if they correspond to equivalent physical units.""" + values = np.linspace(0.0, 10.0, 6) + flu = flu_unit(physical_unit) + + tlu = tlu_unit(physical_unit) + assert flu.is_equivalent(tlu) + assert_allclose(flu.to(tlu), flu.function_unit.to(tlu.function_unit)) + assert_allclose( + flu.to(tlu, values), values * flu.function_unit.to(tlu.function_unit) + ) + + tlu2 = tlu_unit(u.Unit(100.0 * physical_unit)) + assert flu.is_equivalent(tlu2) + # Check that we round-trip. + assert_allclose(flu.to(tlu2, tlu2.to(flu, values)), values, atol=1.0e-15) + + tlu3 = tlu_unit(physical_unit.to_system(u.si)[0]) + assert flu.is_equivalent(tlu3) + assert_allclose(flu.to(tlu3, tlu3.to(flu, values)), values, atol=1.0e-15) + + tlu4 = tlu_unit(u.g) + assert not flu.is_equivalent(tlu4) + with pytest.raises(u.UnitsError): + flu.to(tlu4, values) + + def test_unit_decomposition(self): + lu = u.mag(u.Jy) + assert lu.decompose() == u.mag(u.Jy.decompose()) + assert lu.decompose().physical_unit.bases == [u.kg, u.s] + assert lu.si == u.mag(u.Jy.si) + assert lu.si.physical_unit.bases == [u.kg, u.s] + assert lu.cgs == u.mag(u.Jy.cgs) + assert lu.cgs.physical_unit.bases == [u.g, u.s] + + def test_unit_multiple_possible_equivalencies(self): + lu = u.mag(u.Jy) + assert lu.is_equivalent(pu_sample) + + def test_magnitude_conversion_fails_message(self): + """Check that "dimensionless" magnitude units include a message in their + exception text suggesting a possible cause of the problem. + """ + with pytest.raises( + u.UnitConversionError, + match="Did you perhaps subtract magnitudes so the unit got lost?", + ): + (10 * u.ABmag - 2 * u.ABmag).to(u.nJy) + + +class TestLogUnitArithmetic: + def test_multiplication_division(self): + """Check that multiplication/division with other units is only + possible when the physical unit is dimensionless, and that this + turns the unit into a normal one.""" + lu1 = u.mag(u.Jy) + + with pytest.raises(u.UnitsError): + lu1 * u.m + + with pytest.raises(u.UnitsError): + u.m * lu1 + + with pytest.raises(u.UnitsError): + lu1 / lu1 + + for unit in (u.dimensionless_unscaled, u.m, u.mag, u.dex): + with pytest.raises(u.UnitsError): + lu1 / unit + + lu2 = u.mag(u.dimensionless_unscaled) + + with pytest.raises(u.UnitsError): + lu2 * lu1 + + with pytest.raises(u.UnitsError): + lu2 / lu1 + + # But dimensionless_unscaled can be cancelled. + assert lu2 / lu2 == u.dimensionless_unscaled + + # With dimensionless, normal units are OK, but we return a plain unit. + tf = lu2 * u.m + tr = u.m * lu2 + for t in (tf, tr): + assert not isinstance(t, type(lu2)) + assert t == lu2.function_unit * u.m + with u.set_enabled_equivalencies(u.logarithmic()): + with pytest.raises(u.UnitsError): + t.to(lu2.physical_unit) + # Now we essentially have a LogUnit with a prefactor of 100, + # so should be equivalent again. + t = tf / u.cm + with u.set_enabled_equivalencies(u.logarithmic()): + assert t.is_equivalent(lu2.function_unit) + assert_allclose( + t.to(u.dimensionless_unscaled, np.arange(3.0) / 100.0), + lu2.to(lu2.physical_unit, np.arange(3.0)), + ) + + # If we effectively remove lu1, a normal unit should be returned. + t2 = tf / lu2 + assert not isinstance(t2, type(lu2)) + assert t2 == u.m + t3 = tf / lu2.function_unit + assert not isinstance(t3, type(lu2)) + assert t3 == u.m + + # For completeness, also ensure non-sensical operations fail + with pytest.raises(TypeError): + lu1 * object() + with pytest.raises(TypeError): + slice(None) * lu1 + with pytest.raises(TypeError): + lu1 / [] + with pytest.raises(TypeError): + 1 / lu1 + + @pytest.mark.parametrize("power", (2, 0.5, 1, 0)) + def test_raise_to_power(self, power): + """Check that raising LogUnits to some power is only possible when the + physical unit is dimensionless, and that conversion is turned off when + the resulting logarithmic unit (such as mag**2) is incompatible.""" + lu1 = u.mag(u.Jy) + + if power == 0: + assert lu1**power == u.dimensionless_unscaled + elif power == 1: + assert lu1**power == lu1 + else: + with pytest.raises(u.UnitsError): + lu1**power + + # With dimensionless, though, it works, but returns a normal unit. + lu2 = u.mag(u.dimensionless_unscaled) + + t = lu2**power + if power == 0: + assert t == u.dimensionless_unscaled + elif power == 1: + assert t == lu2 + else: + assert not isinstance(t, type(lu2)) + assert t == lu2.function_unit**power + # also check we roundtrip + t2 = t ** (1.0 / power) + assert t2 == lu2.function_unit + with u.set_enabled_equivalencies(u.logarithmic()): + assert_allclose( + t2.to(u.dimensionless_unscaled, np.arange(3.0)), + lu2.to(lu2.physical_unit, np.arange(3.0)), + ) + + @pytest.mark.parametrize("other", pu_sample) + def test_addition_subtraction_to_normal_units_fails(self, other): + lu1 = u.mag(u.Jy) + with pytest.raises(u.UnitsError): + lu1 + other + + with pytest.raises(u.UnitsError): + lu1 - other + + with pytest.raises(u.UnitsError): + other - lu1 + + def test_addition_subtraction_to_non_units_fails(self): + lu1 = u.mag(u.Jy) + with pytest.raises(TypeError): + lu1 + 1.0 + + with pytest.raises(TypeError): + lu1 - [1.0, 2.0, 3.0] + + @pytest.mark.parametrize( + "other", + ( + u.mag, + u.mag(), + u.mag(u.Jy), + u.mag(u.m), + u.Unit(2 * u.mag), + u.MagUnit("", 2.0 * u.mag), + ), + ) + def test_addition_subtraction(self, other): + """Check physical units are changed appropriately""" + lu1 = u.mag(u.Jy) + other_pu = getattr(other, "physical_unit", u.dimensionless_unscaled) + + lu_sf = lu1 + other + assert lu_sf.is_equivalent(lu1.physical_unit * other_pu) + + lu_sr = other + lu1 + assert lu_sr.is_equivalent(lu1.physical_unit * other_pu) + + lu_df = lu1 - other + assert lu_df.is_equivalent(lu1.physical_unit / other_pu) + + lu_dr = other - lu1 + assert lu_dr.is_equivalent(other_pu / lu1.physical_unit) + + def test_complicated_addition_subtraction(self): + """for fun, a more complicated example of addition and subtraction""" + dm0 = u.Unit("DM", 1.0 / (4.0 * np.pi * (10.0 * u.pc) ** 2)) + lu_dm = u.mag(dm0) + lu_absST = u.STmag - lu_dm + assert lu_absST.is_equivalent(u.erg / u.s / u.AA) + + def test_neg_pos(self): + lu1 = u.mag(u.Jy) + neg_lu = -lu1 + assert neg_lu != lu1 + assert neg_lu.physical_unit == u.Jy**-1 + assert -neg_lu == lu1 + pos_lu = +lu1 + assert pos_lu is not lu1 + assert pos_lu == lu1 + + +def test_pickle(): + lu1 = u.dex(u.cm / u.s**2) + s = pickle.dumps(lu1) + lu2 = pickle.loads(s) + assert lu1 == lu2 + + +def test_hashable(): + lu1 = u.dB(u.mW) + lu2 = u.dB(u.m) + lu3 = u.dB(u.mW) + assert hash(lu1) != hash(lu2) + assert hash(lu1) == hash(lu3) + luset = {lu1, lu2, lu3} + assert len(luset) == 2 + + +class TestLogQuantityCreation: + @pytest.mark.parametrize( + "lq, lu", + list(zip(lq_subclasses + [u.LogQuantity], lu_subclasses + [u.LogUnit])), + ) + def test_logarithmic_quantities(self, lq, lu): + """Check logarithmic quantities are all set up correctly""" + assert lq._unit_class == lu + assert type(lu()._quantity_class(1.0)) is lq + + @pytest.mark.parametrize("physical_unit", pu_sample) + @pytest.mark.parametrize("lq_cls", lq_subclasses) + def test_subclass_creation(self, lq_cls, physical_unit): + """Create LogQuantity subclass objects for some physical units, + and basic check on transformations""" + value = np.arange(1.0, 10.0) + log_q = lq_cls(value * physical_unit) + assert log_q.unit.physical_unit == physical_unit + assert log_q.unit.function_unit == log_q.unit._default_function_unit + assert_allclose(log_q.physical.value, value) + with pytest.raises(ValueError): + lq_cls(value, physical_unit) + + @pytest.mark.parametrize( + "unit", + ( + u.mag, + u.mag(), + u.mag(u.Jy), + u.mag(u.m), + u.Unit(2 * u.mag), + u.MagUnit("", 2.0 * u.mag), + u.MagUnit(u.Jy, -1 * u.mag), + u.MagUnit(u.m, -2.0 * u.mag), + ), + ) + def test_different_units(self, unit): + q = u.Magnitude(1.23, unit) + assert q.unit.function_unit == getattr(unit, "function_unit", unit) + assert q.unit.physical_unit is getattr( + unit, "physical_unit", u.dimensionless_unscaled + ) + + @pytest.mark.parametrize( + "value, unit", + ( + (1.0 * u.mag(u.Jy), None), + (1.0 * u.dex(u.Jy), None), + (1.0 * u.mag(u.W / u.m**2 / u.Hz), u.mag(u.Jy)), + (1.0 * u.dex(u.W / u.m**2 / u.Hz), u.mag(u.Jy)), + ), + ) + def test_function_values(self, value, unit): + lq = u.Magnitude(value, unit) + assert lq == value + assert lq.unit.function_unit == u.mag + assert lq.unit.physical_unit == getattr( + unit, "physical_unit", value.unit.physical_unit + ) + + @pytest.mark.parametrize( + "unit", + ( + u.mag(), + u.mag(u.Jy), + u.mag(u.m), + u.MagUnit("", 2.0 * u.mag), + u.MagUnit(u.Jy, -1 * u.mag), + u.MagUnit(u.m, -2.0 * u.mag), + ), + ) + def test_indirect_creation(self, unit): + q1 = 2.5 * unit + assert isinstance(q1, u.Magnitude) + assert q1.value == 2.5 + assert q1.unit == unit + pv = 100.0 * unit.physical_unit + q2 = unit * pv + assert q2.unit == unit + assert q2.unit.physical_unit == pv.unit + assert q2.to_value(unit.physical_unit) == 100.0 + assert (q2._function_view / u.mag).to_value(1) == -5.0 + q3 = unit / 0.4 + assert q3 == q1 + + def test_from_view(self): + # Cannot view a physical quantity as a function quantity, since the + # values would change. + q = [100.0, 1000.0] * u.cm / u.s**2 + with pytest.raises(TypeError): + q.view(u.Dex) + # But fine if we have the right magnitude. + q = [2.0, 3.0] * u.dex + lq = q.view(u.Dex) + assert isinstance(lq, u.Dex) + assert lq.unit.physical_unit == u.dimensionless_unscaled + assert np.all(q == lq) + + def test_using_quantity_class(self): + """Check that we can use Quantity if we have subok=True""" + # following issue #5851 + lu = u.dex(u.AA) + with pytest.raises(u.UnitTypeError): + u.Quantity(1.0, lu) + q = u.Quantity(1.0, lu, subok=True) + assert type(q) is lu._quantity_class + + +def test_conversion_to_and_from_physical_quantities(): + """Ensures we can convert from regular quantities.""" + mst = [10.0, 12.0, 14.0] * u.STmag + flux_lambda = mst.physical + mst_roundtrip = flux_lambda.to(u.STmag) + # check we return a logquantity; see #5178. + assert isinstance(mst_roundtrip, u.Magnitude) + assert mst_roundtrip.unit == mst.unit + assert_allclose(mst_roundtrip.value, mst.value) + wave = [4956.8, 4959.55, 4962.3] * u.AA + flux_nu = mst.to(u.Jy, equivalencies=u.spectral_density(wave)) + mst_roundtrip2 = flux_nu.to(u.STmag, u.spectral_density(wave)) + assert isinstance(mst_roundtrip2, u.Magnitude) + assert mst_roundtrip2.unit == mst.unit + assert_allclose(mst_roundtrip2.value, mst.value) + + +def test_quantity_decomposition(): + lq = 10.0 * u.mag(u.Jy) + assert lq.decompose() == lq + assert lq.decompose().unit.physical_unit.bases == [u.kg, u.s] + assert lq.si == lq + assert lq.si.unit.physical_unit.bases == [u.kg, u.s] + assert lq.cgs == lq + assert lq.cgs.unit.physical_unit.bases == [u.g, u.s] + + +class TestLogQuantityViews: + def setup_method(self): + self.lq = u.Magnitude(np.arange(1.0, 10.0) * u.Jy) + self.lq2 = u.Magnitude(np.arange(1.0, 5.0)) + + def test_value_view(self): + lq_value = self.lq.value + assert type(lq_value) is np.ndarray + lq_value[2] = -1.0 + assert np.all(self.lq.value == lq_value) + + def test_function_view(self): + lq_fv = self.lq._function_view + assert type(lq_fv) is u.Quantity + assert lq_fv.unit is self.lq.unit.function_unit + lq_fv[3] = -2.0 * lq_fv.unit + assert np.all(self.lq.value == lq_fv.value) + + def test_quantity_view(self): + # Cannot view as Quantity, since the unit cannot be represented. + with pytest.raises(TypeError): + self.lq.view(u.Quantity) + # But a dimensionless one is fine. + q2 = self.lq2.view(u.Quantity) + assert q2.unit is u.mag + assert np.all(q2.value == self.lq2.value) + lq3 = q2.view(u.Magnitude) + assert type(lq3.unit) is u.MagUnit + assert lq3.unit.physical_unit == u.dimensionless_unscaled + assert np.all(lq3 == self.lq2) + + +class TestLogQuantitySlicing: + def test_item_get_and_set(self): + lq1 = u.Magnitude(np.arange(1.0, 11.0) * u.Jy) + assert lq1[9] == u.Magnitude(10.0 * u.Jy) + lq1[2] = 100.0 * u.Jy + assert lq1[2] == u.Magnitude(100.0 * u.Jy) + with pytest.raises(u.UnitsError): + lq1[2] = 100.0 * u.m + with pytest.raises(u.UnitsError): + lq1[2] = 100.0 * u.mag + with pytest.raises(u.UnitsError): + lq1[2] = u.Magnitude(100.0 * u.m) + assert lq1[2] == u.Magnitude(100.0 * u.Jy) + + def test_slice_get_and_set(self): + lq1 = u.Magnitude(np.arange(1.0, 10.0) * u.Jy) + lq1[2:4] = 100.0 * u.Jy + assert np.all(lq1[2:4] == u.Magnitude(100.0 * u.Jy)) + with pytest.raises(u.UnitsError): + lq1[2:4] = 100.0 * u.m + with pytest.raises(u.UnitsError): + lq1[2:4] = 100.0 * u.mag + with pytest.raises(u.UnitsError): + lq1[2:4] = u.Magnitude(100.0 * u.m) + assert np.all(lq1[2] == u.Magnitude(100.0 * u.Jy)) + + +class TestLogQuantityArithmetic: + @pytest.mark.parametrize( + "other", + [ + 2.4 * u.mag(), + 12.34 * u.ABmag, + u.Magnitude(3.45 * u.Jy), + u.Dex(3.0), + u.Dex(np.linspace(3000, 5000, 10) * u.Angstrom), + u.Magnitude(6.78, 2.0 * u.mag), + ], + ) + @pytest.mark.parametrize("fac", [1.0, 2, 0.4]) + def test_multiplication_division(self, other, fac): + """Check that multiplication and division work as expected""" + + lq_sf = fac * other + assert lq_sf.unit.physical_unit == other.unit.physical_unit**fac + assert_allclose(lq_sf.physical, other.physical**fac) + + lq_sf = other * fac + assert lq_sf.unit.physical_unit == other.unit.physical_unit**fac + assert_allclose(lq_sf.physical, other.physical**fac) + + lq_sf = other / fac + assert lq_sf.unit.physical_unit**fac == other.unit.physical_unit + assert_allclose(lq_sf.physical**fac, other.physical) + + lq_sf = other.copy() + lq_sf *= fac + assert lq_sf.unit.physical_unit == other.unit.physical_unit**fac + assert_allclose(lq_sf.physical, other.physical**fac) + + lq_sf = other.copy() + lq_sf /= fac + assert lq_sf.unit.physical_unit**fac == other.unit.physical_unit + assert_allclose(lq_sf.physical**fac, other.physical) + + def test_more_multiplication_division(self): + """Check that multiplication/division with other quantities is only + possible when the physical unit is dimensionless, and that this keeps + the result as a LogQuantity if possible.""" + lq = u.Magnitude(np.arange(1.0, 11.0) * u.Jy) + + with pytest.raises(u.UnitsError): + lq * (1.0 * u.m) + + with pytest.raises(u.UnitsError): + (1.0 * u.m) * lq + + with pytest.raises(u.UnitsError): + lq / lq + + for unit in (u.m, u.mag, u.dex): + with pytest.raises(u.UnitsError): + lq / unit + + lq2 = u.Magnitude(np.arange(1, 11.0)) + + with pytest.raises(u.UnitsError): + lq2 * lq + + with pytest.raises(u.UnitsError): + lq2 / lq + + with pytest.raises(u.UnitsError): + lq / lq2 + + lq_sf = lq.copy() + + with pytest.raises(u.UnitsError): + lq_sf *= lq2 + # ensure that nothing changed inside + assert (lq_sf == lq).all() + + with pytest.raises(u.UnitsError): + lq_sf /= lq2 + # ensure that nothing changed inside + assert (lq_sf == lq).all() + + # but dimensionless_unscaled can be cancelled + r = lq2 / u.Magnitude(2.0) + assert r.unit == u.dimensionless_unscaled + assert np.all(r.value == lq2.value / 2.0) + + # And multiplying with a dimensionless array is also OK. + r2 = lq2 * np.arange(10.0) + assert isinstance(r2, u.Magnitude) + assert np.all(r2 == lq2._function_view * np.arange(10.0)) + + # with dimensionless, normal units OK, but return normal quantities + # if the unit no longer is consistent with the logarithmic unit. + tf = lq2 * u.m + tr = u.m * lq2 + for t in (tf, tr): + assert not isinstance(t, type(lq2)) + assert t.unit == lq2.unit.function_unit * u.m + with u.set_enabled_equivalencies(u.logarithmic()): + with pytest.raises(u.UnitsError): + t.to(lq2.unit.physical_unit) + + t = tf / (50.0 * u.cm) + # now we essentially have the same quantity but with a prefactor of 2 + assert t.unit.is_equivalent(lq2.unit.function_unit) + assert_allclose(t.to(lq2.unit.function_unit), lq2._function_view * 2) + + @pytest.mark.parametrize("power", (2, 0.5, 1, 0)) + def test_raise_to_power(self, power): + """Check that raising LogQuantities to some power is only possible when + the physical unit is dimensionless, and that conversion is turned off + when the resulting logarithmic unit (say, mag**2) is incompatible.""" + lq = u.Magnitude(np.arange(1.0, 4.0) * u.Jy) + + if power == 0: + assert np.all(lq**power == 1.0) + elif power == 1: + assert np.all(lq**power == lq) + else: + with pytest.raises(u.UnitsError): + lq**power + + # with dimensionless, it works, but falls back to normal quantity + # (except for power=1) + lq2 = u.Magnitude(np.arange(10.0)) + + t = lq2**power + if power == 0: + assert t.unit is u.dimensionless_unscaled + assert np.all(t.value == 1.0) + elif power == 1: + assert np.all(t == lq2) + else: + assert not isinstance(t, type(lq2)) + assert t.unit == lq2.unit.function_unit**power + with u.set_enabled_equivalencies(u.logarithmic()): + with pytest.raises(u.UnitsError): + t.to(u.dimensionless_unscaled) + + def test_error_on_lq_as_power(self): + lq = u.Magnitude(np.arange(1.0, 4.0) * u.Jy) + with pytest.raises(TypeError): + lq**lq + + @pytest.mark.parametrize("other", pu_sample) + def test_addition_subtraction_to_normal_units_fails(self, other): + lq = u.Magnitude(np.arange(1.0, 10.0) * u.Jy) + q = 1.23 * other + with pytest.raises(u.UnitsError): + lq + q + + with pytest.raises(u.UnitsError): + lq - q + + with pytest.raises(u.UnitsError): + q - lq + + @pytest.mark.parametrize( + "other", + ( + 1.23 * u.mag, + 2.34 * u.mag(), + u.Magnitude(3.45 * u.Jy), + u.Magnitude(4.56 * u.m), + 5.67 * u.Unit(2 * u.mag), + u.Magnitude(6.78, 2.0 * u.mag), + ), + ) + def test_addition_subtraction(self, other): + """Check that addition/subtraction with quantities with magnitude or + MagUnit units works, and that it changes the physical units + appropriately.""" + lq = u.Magnitude(np.arange(1.0, 10.0) * u.Jy) + other_physical = other.to( + getattr(other.unit, "physical_unit", u.dimensionless_unscaled), + equivalencies=u.logarithmic(), + ) + + lq_sf = lq + other + assert_allclose(lq_sf.physical, lq.physical * other_physical) + + lq_sr = other + lq + assert_allclose(lq_sr.physical, lq.physical * other_physical) + + lq_df = lq - other + assert_allclose(lq_df.physical, lq.physical / other_physical) + + lq_dr = other - lq + assert_allclose(lq_dr.physical, other_physical / lq.physical) + + @pytest.mark.parametrize("other", pu_sample) + def test_inplace_addition_subtraction_unit_checks(self, other): + lu1 = u.mag(u.Jy) + lq1 = u.Magnitude(np.arange(1.0, 10.0), lu1) + with pytest.raises(u.UnitsError): + lq1 += other + + assert np.all(lq1.value == np.arange(1.0, 10.0)) + assert lq1.unit == lu1 + + with pytest.raises(u.UnitsError): + lq1 -= other + + assert np.all(lq1.value == np.arange(1.0, 10.0)) + assert lq1.unit == lu1 + + @pytest.mark.parametrize( + "other", + ( + 1.23 * u.mag, + 2.34 * u.mag(), + u.Magnitude(3.45 * u.Jy), + u.Magnitude(4.56 * u.m), + 5.67 * u.Unit(2 * u.mag), + u.Magnitude(6.78, 2.0 * u.mag), + ), + ) + def test_inplace_addition_subtraction(self, other): + """Check that inplace addition/subtraction with quantities with + magnitude or MagUnit units works, and that it changes the physical + units appropriately.""" + lq = u.Magnitude(np.arange(1.0, 10.0) * u.Jy) + other_physical = other.to( + getattr(other.unit, "physical_unit", u.dimensionless_unscaled), + equivalencies=u.logarithmic(), + ) + lq_sf = lq.copy() + lq_sf += other + assert_allclose(lq_sf.physical, lq.physical * other_physical) + + lq_df = lq.copy() + lq_df -= other + assert_allclose(lq_df.physical, lq.physical / other_physical) + + def test_complicated_addition_subtraction(self): + """For fun, a more complicated example of addition and subtraction.""" + dm0 = u.Unit("DM", 1.0 / (4.0 * np.pi * (10.0 * u.pc) ** 2)) + DMmag = u.mag(dm0) + m_st = 10.0 * u.STmag + dm = 5.0 * DMmag + M_st = m_st - dm + assert M_st.unit.is_equivalent(u.erg / u.s / u.AA) + ratio = M_st.physical / (m_st.physical * 4.0 * np.pi * (100.0 * u.pc) ** 2) + assert np.abs(ratio - 1.0) < 1.0e-15 + + +class TestLogQuantityComparisons: + def test_comparison_to_non_quantities_fails(self): + lq = u.Magnitude(np.arange(1.0, 10.0) * u.Jy) + with pytest.raises(TypeError): + lq > "a" # noqa: B015 + + assert not (lq == "a") + assert lq != "a" + + def test_comparison(self): + lq1 = u.Magnitude(np.arange(1.0, 4.0) * u.Jy) + lq2 = u.Magnitude(2.0 * u.Jy) + assert np.all((lq1 > lq2) == np.array([True, False, False])) + assert np.all((lq1 == lq2) == np.array([False, True, False])) + lq3 = u.Dex(2.0 * u.Jy) + assert np.all((lq1 > lq3) == np.array([True, False, False])) + assert np.all((lq1 == lq3) == np.array([False, True, False])) + lq4 = u.Magnitude(2.0 * u.m) + assert not (lq1 == lq4) + assert lq1 != lq4 + with pytest.raises(u.UnitsError): + lq1 < lq4 # noqa: B015 + q5 = 1.5 * u.Jy + assert np.all((lq1 > q5) == np.array([True, False, False])) + assert np.all((q5 < lq1) == np.array([True, False, False])) + with pytest.raises(u.UnitsError): + lq1 >= 2.0 * u.m # noqa: B015 + with pytest.raises(u.UnitsError): + lq1 <= lq1.value * u.mag # noqa: B015 + # For physically dimensionless, we can compare with the function unit. + lq6 = u.Magnitude(np.arange(1.0, 4.0)) + fv6 = lq6.value * u.mag + assert np.all(lq6 == fv6) + # but not some arbitrary unit, of course. + with pytest.raises(u.UnitsError): + lq6 < 2.0 * u.m # noqa: B015 + + +class TestLogQuantityMethods: + @pytest.mark.parametrize( + "method", + ( + "mean", + "min", + "max", + "round", + "trace", + "std", + "var", + "diff", + "ediff1d", + ), + ) + @log_quantity_parametrization + def test_always_ok(self, method, mag): + res = getattr(mag, method)() + assert np.all(res.value == getattr(mag._function_view, method)().value) + if method in ("std", "diff", "ediff1d"): + assert res.unit == u.mag() + elif method == "var": + assert res.unit == u.mag**2 + else: + assert res.unit == mag.unit + # verify numpy function gives same result as method call + if hasattr(np, method): + res2 = getattr(np, method)(mag) + assert_quantity_allclose(res2, res) + + @log_quantity_parametrization + def test_clip(self, mag): + assert np.all( + mag.clip(2.0 * mag.unit, 4.0 * mag.unit).value == mag.value.clip(2.0, 4.0) + ) + + @pytest.mark.parametrize("method", ("sum", "cumsum")) + def test_ok_if_dimensionless(self, method): + res = getattr(m1, method)() + assert np.all(res.value == getattr(m1, method)().value) + assert res.unit == m1.unit + + @pytest.mark.parametrize("method", ("sum", "cumsum")) + def test_not_ok_if_not_dimensionless(self, method): + with pytest.raises(TypeError): + getattr(mJy, method)() + + def test_dot(self): + assert np.all(m1.dot(m1).value == m1.value.dot(m1.value)) + + @pytest.mark.parametrize("method", ("prod", "cumprod")) + @log_quantity_parametrization + def test_never_ok(self, method, mag): + with pytest.raises(TypeError): + getattr(mag, method)() + + +class TestLogQuantityFunctions: + # TODO: add tests for all supported functions! + + @log_quantity_parametrization + def test_ptp(self, mag): + res = np.ptp(mag) + assert np.all(res.value == np.ptp(mag._function_view).value) + assert res.unit == u.mag() diff --git a/astropy/units/tests/test_photometric.py b/astropy/units/tests/test_photometric.py new file mode 100644 index 000000000000..427c956f7e1a --- /dev/null +++ b/astropy/units/tests/test_photometric.py @@ -0,0 +1,44 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +""" +Tests for the photometric module. Note that this is shorter than +might be expected because a lot of the relevant tests that deal +with magnidues are in `test_logarithmic.py` +""" + +from astropy.tests.helper import assert_quantity_allclose +from astropy.units import ( + AA, + ABflux, + Jy, + Magnitude, + STflux, + cm, + erg, + mgy, + nmgy, + s, + zero_point_flux, +) + + +def test_maggies(): + assert_quantity_allclose(1e-9 * mgy, 1 * nmgy) + assert_quantity_allclose(Magnitude((1 * nmgy).to(mgy)).value, 22.5) + + +def test_maggies_zpts(): + assert_quantity_allclose( + (1 * nmgy).to(ABflux, zero_point_flux(1 * ABflux)), 3631e-9 * Jy, rtol=1e-3 + ) + + ST_base_unit = erg * cm**-2 / s / AA + + stmgy = (10 * mgy).to(STflux, zero_point_flux(1 * ST_base_unit)) + assert_quantity_allclose(stmgy, 10 * ST_base_unit) + + mgyst = (2 * ST_base_unit).to(mgy, zero_point_flux(0.5 * ST_base_unit)) + assert_quantity_allclose(mgyst, 4 * mgy) + + nmgyst = (5.0e-10 * ST_base_unit).to(mgy, zero_point_flux(0.5 * ST_base_unit)) + assert_quantity_allclose(nmgyst, 1 * nmgy) diff --git a/astropy/units/tests/test_physical.py b/astropy/units/tests/test_physical.py new file mode 100644 index 000000000000..87f6780688b4 --- /dev/null +++ b/astropy/units/tests/test_physical.py @@ -0,0 +1,528 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +""" +Unit tests for the handling of physical types in `astropy.units`. +""" + +import pickle + +import pytest + +from astropy import units as u +from astropy.constants import hbar +from astropy.units import physical + +unit_physical_type_pairs = [ + (u.m, "length"), + (u.cm**3, "volume"), + (u.km / u.h, "speed"), + (u.barn * u.Mpc, "volume"), + (u.m * u.s**8, "unknown"), + (u.m / u.m, "dimensionless"), + (hbar.unit, "angular momentum"), + (u.erg / (u.cm**2 * u.s * u.AA), "spectral flux density wav"), + (u.photon / (u.cm**2 * u.s * u.AA), "photon flux density wav"), + (u.photon / (u.cm**2 * u.s * u.Hz), "photon flux density"), + (u.Jy / u.sr, "surface brightness"), + (u.J * u.m**-3 * u.s**-1 * u.sr**-1, "surface brightness wav"), + (u.photon / u.Hz / u.cm**2 / u.s / u.sr, "photon surface brightness"), + (u.photon / u.AA / u.cm**2 / u.s / u.sr, "photon surface brightness wav"), + (u.byte, "data quantity"), + (u.bit, "data quantity"), + (u.imperial.mi / u.week, "speed"), + (u.erg / u.s, "power"), + (u.C / u.s, "electrical current"), + (u.C / u.s / u.cm**2, "electrical current density"), + (u.T * u.m**2, "magnetic flux"), + (u.N * u.m, "energy"), + (u.rad / u.ms, "angular speed"), + (u.Unit(1), "dimensionless"), + (u.m**2, "area"), + (u.s, "time"), + (u.rad, "angle"), + (u.sr, "solid angle"), + (u.m / u.s**2, "acceleration"), + (u.Hz, "frequency"), + (u.g, "mass"), + (u.mol, "amount of substance"), + (u.K, "temperature"), + (u.deg_C, "temperature"), + (u.imperial.deg_F, "temperature"), + (u.imperial.deg_R, "temperature"), + (u.imperial.deg_R / u.m, "temperature_gradient"), + (u.N, "force"), + (u.J, "energy"), + (u.Pa, "pressure"), + (u.W, "power"), + (u.kg / u.m**3, "mass density"), + (u.m**3 / u.kg, "specific volume"), + (u.mol / u.m**3, "molar concentration"), + (u.kg * u.m / u.s, "momentum/impulse"), + (u.kg * u.m**2 / u.s, "angular momentum"), + (u.rad / u.s, "angular speed"), + (u.rad / u.s**2, "angular acceleration"), + (u.g / (u.m * u.s), "dynamic viscosity"), + (u.m**2 / u.s, "kinematic viscosity"), + (u.m**-1, "wavenumber"), + (u.A, "electrical current"), + (u.C, "electrical charge"), + (u.V, "electrical potential"), + (u.Ohm, "electrical resistance"), + (u.S, "electrical conductance"), + (u.F, "electrical capacitance"), + (u.C * u.m, "electrical dipole moment"), + (u.A / u.m**2, "electrical current density"), + (u.V / u.m, "electrical field strength"), + (u.C / u.m**2, "electrical flux density"), + (u.C / u.m**3, "electrical charge density"), + (u.F / u.m, "permittivity"), + (u.Wb, "magnetic flux"), + (u.Wb**2, "magnetic helicity"), + (u.T, "magnetic flux density"), + (u.A / u.m, "magnetic field strength"), + (u.H / u.m, "electromagnetic field strength"), + (u.H, "inductance"), + (u.cd, "luminous intensity"), + (u.lm, "luminous flux"), + (u.lx, "luminous emittance/illuminance"), + (u.W / u.sr, "radiant intensity"), + (u.cd / u.m**2, "luminance"), + (u.astrophys.Jy, "spectral flux density"), + (u.astrophys.R, "photon flux"), + (u.misc.bit, "data quantity"), + (u.misc.bit / u.s, "bandwidth"), + (u.cgs.Franklin, "electrical charge (ESU)"), + (u.cgs.statampere, "electrical current (ESU)"), + (u.cgs.Biot, "electrical current (EMU)"), + (u.cgs.abcoulomb, "electrical charge (EMU)"), + (u.imperial.btu / (u.s * u.m * u.imperial.deg_F), "thermal conductivity"), + (u.imperial.cal / u.deg_C, "heat capacity"), + (u.imperial.cal / u.deg_C / u.g, "specific heat capacity"), + (u.J * u.m**-2 * u.s**-1, "energy flux"), + (u.W / u.m**2, "energy flux"), + (u.m**3 / u.mol, "molar volume"), + (u.m / u.S, "electrical resistivity"), + (u.S / u.m, "electrical conductivity"), + (u.A * u.m**2, "magnetic moment"), + (u.J / u.T, "magnetic moment"), + (u.yr**-1 * u.Mpc**-3, "volumetric rate"), + (u.m / u.s**3, "jerk"), + (u.m / u.s**4, "snap"), + (u.m / u.s**5, "crackle"), + (u.m / u.s**6, "pop"), + (u.deg_C / u.m, "temperature gradient"), + (u.imperial.deg_F / u.m, "temperature gradient"), + (u.imperial.deg_R / u.imperial.ft, "temperature gradient"), + (u.imperial.Calorie / u.g, "specific energy"), + (u.mol / u.L / u.s, "reaction rate"), + (u.imperial.lbf * u.imperial.ft * u.s**2, "moment of inertia"), + (u.mol / u.s, "catalytic activity"), + (u.imperial.kcal / u.deg_C / u.mol, "molar heat capacity"), + (u.mol / u.kg, "molality"), + (u.imperial.inch * u.hr, "absement"), + (u.imperial.ft**3 / u.s, "volumetric flow rate"), + (u.Hz / u.s, "frequency drift"), + (u.Pa**-1, "compressibility"), + (u.dimensionless_unscaled, "dimensionless"), +] + + +@pytest.mark.parametrize("unit, physical_type", unit_physical_type_pairs) +def test_physical_type_names(unit, physical_type): + """ + Test that the `physical_type` attribute of `u.Unit` objects provides + the expected physical type for various units. + + Many of these tests are used to test backwards compatibility. + """ + assert unit.physical_type == physical_type, ( + f"{unit!r}.physical_type was expected to return " + f"{physical_type!r}, but instead returned {unit.physical_type!r}." + ) + + +length = u.m.physical_type +time = u.s.physical_type +speed = (u.m / u.s).physical_type +area = (u.m**2).physical_type +wavenumber = (u.m**-1).physical_type +dimensionless = u.dimensionless_unscaled.physical_type +pressure = u.Pa.physical_type +momentum = (u.kg * u.m / u.s).physical_type + + +@pytest.mark.parametrize( + "physical_type_representation, physical_type_name", + [ + (1.0, "dimensionless"), + (u.m, "length"), + ("work", "work"), + (5 * u.m, "length"), + (length, length), + (u.Pa, "energy_density"), # attribute-accessible name + ("energy_density", "energy_density"), # attribute-accessible name + ], +) +def test_getting_physical_type(physical_type_representation, physical_type_name): + """Test different ways of getting a physical type.""" + physical_type = physical.get_physical_type(physical_type_representation) + assert isinstance(physical_type, physical.PhysicalType) + assert physical_type == physical_type_name + + +@pytest.mark.parametrize( + "argument, exception", + [ + ("unknown", ValueError), + ("not a name of a physical type", ValueError), + ({"this set cannot be made into a Quantity"}, TypeError), + ], +) +def test_getting_physical_type_exceptions(argument, exception): + """ + Test that `get_physical_type` raises appropriate exceptions when + provided with invalid arguments. + """ + with pytest.raises(exception): + physical.get_physical_type(argument) + + +def test_physical_type_cannot_become_quantity(): + """ + Test that `PhysicalType` instances cannot be cast into `Quantity` + objects. A failure in this test could be related to failures + in subsequent tests. + """ + with pytest.raises(TypeError): + u.Quantity(u.m.physical_type, u.m) + + +# left term, right term, operator, expected value +operation_parameters = [ + (length, length, "__eq__", True), + (length, area, "__eq__", False), + (length, "length", "__eq__", True), + ("length", length, "__eq__", NotImplemented), + (dimensionless, dimensionless, "__eq__", True), + (momentum, "momentum/impulse", "__eq__", True), # test delimiters in names + (pressure, "energy_density", "__eq__", True), # test underscores in names + ((u.m**8).physical_type, "unknown", "__eq__", True), + ((u.m**8).physical_type, (u.m**9).physical_type, "__eq__", False), + (length, length, "__ne__", False), + (speed, time, "__ne__", True), + (pressure, dimensionless, "__ne__", True), + (length, u.m, "__eq__", NotImplemented), + (length, length, "__mul__", area), + (speed, time, "__mul__", length), + (speed, time, "__rmul__", length), + (length, time, "__truediv__", speed), + (area, length, "__truediv__", length), + (length, area, "__rtruediv__", length), + (dimensionless, dimensionless, "__mul__", dimensionless), + (dimensionless, dimensionless, "__truediv__", dimensionless), + (length, 2, "__pow__", area), + (area, 0.5, "__pow__", length), + (dimensionless, 4, "__pow__", dimensionless), + (u.m, length, "__mul__", NotImplemented), + (3.2, length, "__mul__", NotImplemented), + (u.m, time, "__truediv__", NotImplemented), + (3.2, length, "__truediv__", NotImplemented), + (length, u.m, "__mul__", area), + (length, u.m, "__rmul__", area), + (speed, u.s, "__mul__", length), + (length, 1, "__mul__", length), + (length, 1, "__rmul__", length), + (length, u.s, "__truediv__", speed), + (area, 1, "__truediv__", area), + (time, u.m, "__rtruediv__", speed), + (length, 1.0, "__rtruediv__", wavenumber), + (length, 2, "__pow__", area), + (length, 32, "__mul__", NotImplemented), + (length, 0, "__rmul__", NotImplemented), + (length, 3.2, "__truediv__", NotImplemented), + (length, -1, "__rtruediv__", NotImplemented), + (length, "length", "__mul__", area), + (length, "length", "__rmul__", area), + (area, "length", "__truediv__", length), + (length, "area", "__rtruediv__", length), +] + + +@pytest.mark.parametrize("left, right, operator, expected", operation_parameters) +def test_physical_type_operations(left, right, operator, expected): + """ + Test that `PhysicalType` dunder methods that require another + argument behave as intended. + """ + assert getattr(left, operator)(right) == expected + + +unit_with_physical_type_set = [ + (u.m, {"length"}), + (u.kg * u.m / u.s, {"impulse", "momentum"}), + (u.Pa, {"energy density", "pressure", "stress"}), +] + + +@pytest.mark.parametrize("unit, expected_set", unit_with_physical_type_set) +def test_physical_type_as_set(unit, expected_set): + """Test making a `physical.PhysicalType` instance into a `set`.""" + resulting_set = set(unit.physical_type) + assert resulting_set == expected_set + + +def test_physical_type_iteration(): + """Test iterating through different physical type names.""" + physical_type_names = list(pressure) + assert physical_type_names == ["energy density", "pressure", "stress"] + + +def test_physical_type_in(): + """ + Test that `in` works as expected for `PhysicalType` objects with one + or multiple names. + """ + assert "length" in length + assert "pressure" in pressure + + +equivalent_unit_pairs = [ + (u.m, u.m), + (u.m, u.cm), + (u.N, u.kg * u.m * u.s**-2), + (u.barn * u.Mpc, u.cm**3), + (u.K, u.deg_C), + (u.K, u.imperial.deg_R), + (u.K, u.imperial.deg_F), + (u.deg_C, u.imperial.deg_F), + (u.m**18, u.pc**18), +] + + +@pytest.mark.parametrize("unit1, unit2", equivalent_unit_pairs) +def test_physical_type_instance_equality(unit1, unit2): + """ + Test that `physical.PhysicalType` instances for units of the same + dimensionality are equal. + """ + assert (unit1.physical_type == unit2.physical_type) is True + assert (unit1.physical_type != unit2.physical_type) is False + + +@pytest.mark.parametrize("unit1, unit2", equivalent_unit_pairs) +def test_get_physical_type_equivalent_pairs(unit1, unit2): + """ + Test that `get_physical_type` retrieves the same `PhysicalType` + instances for equivalent physical types, except for unknown types + which are not cataloged. + """ + physical_type1 = physical.get_physical_type(unit1) + physical_type2 = physical.get_physical_type(unit2) + assert physical_type1 == physical_type2 + if physical_type1 != "unknown": + assert physical_type1 is physical_type2 + + +nonequivalent_unit_pairs = [ + (u.m, u.s), + (u.m**18, u.m**19), + (u.N, u.J), + (u.barn, u.imperial.deg_F), +] + + +@pytest.mark.parametrize("unit1, unit2", nonequivalent_unit_pairs) +def test_physical_type_instance_inequality(unit1, unit2): + """ + Test that `physical.PhysicalType` instances for units with different + dimensionality are considered unequal. + """ + physical_type1 = physical.PhysicalType(unit1, "ptype1") + physical_type2 = physical.PhysicalType(unit2, "ptype2") + assert (physical_type1 != physical_type2) is True + assert (physical_type1 == physical_type2) is False + + +physical_type_with_expected_str = [ + (length, "length"), + (speed, "speed/velocity"), + (pressure, "energy density/pressure/stress"), + (u.deg_C.physical_type, "temperature"), + ((u.J / u.K / u.kg).physical_type, "specific entropy/specific heat capacity"), +] + +physical_type_with_expected_repr = [ + (length, "PhysicalType('length')"), + (speed, "PhysicalType({'speed', 'velocity'})"), + (pressure, "PhysicalType({'energy density', 'pressure', 'stress'})"), + (u.deg_C.physical_type, "PhysicalType('temperature')"), + ( + (u.J / u.K / u.kg).physical_type, + "PhysicalType({'specific entropy', 'specific heat capacity'})", + ), +] + + +@pytest.mark.parametrize("physical_type, expected_str", physical_type_with_expected_str) +def test_physical_type_str(physical_type, expected_str): + """Test using `str` on a `PhysicalType` instance.""" + assert str(physical_type) == expected_str + + +@pytest.mark.parametrize( + "physical_type, expected_repr", physical_type_with_expected_repr +) +def physical_type_repr(physical_type, expected_repr): + """Test using `repr` on a `PhysicalType` instance.""" + assert repr(physical_type) == expected_repr + + +def test_physical_type_hash(): + """Test that a `PhysicalType` instance can be used as a dict key.""" + dictionary = {length: 42} + assert dictionary[length] == 42 + + +@pytest.mark.parametrize("multiplicand", [[], 42, 0, -1]) +def test_physical_type_multiplication(multiplicand): + """ + Test that multiplication of a physical type returns `NotImplemented` + when attempted for an invalid type. + """ + with pytest.raises(TypeError): + length * multiplicand + + +def test_unrecognized_unit_physical_type(): + """ + Test basic functionality for the physical type of an unrecognized + unit. + """ + unrecognized_unit = u.Unit("parrot", parse_strict="silent") + physical_type = unrecognized_unit.physical_type + assert isinstance(physical_type, physical.PhysicalType) + assert physical_type == "unknown" + + +invalid_inputs = [(42,), ("valid input", 42)] + + +@pytest.mark.parametrize("invalid_input", invalid_inputs) +def test_invalid_physical_types(invalid_input): + """ + Test that `PhysicalType` cannot be instantiated when one of the + supplied names is not a string, while making sure that the physical + type for the unit remains unknown. + """ + obscure_unit = u.s**87 + with pytest.raises(ValueError): + physical.PhysicalType(obscure_unit, invalid_input) + assert obscure_unit.physical_type == "unknown" + + +class TestDefPhysType: + weird_unit = u.m**99 + strange_unit = u.s**42 + + def test_attempt_to_define_unknown_physical_type(self): + """Test that a unit cannot be defined as unknown.""" + with pytest.raises(ValueError): + physical.def_physical_type(self.weird_unit, "unknown") + assert "unknown" not in physical._unit_physical_mapping + + def test_multiple_same_physical_type_names(self): + """ + Test that `def_physical_type` raises an exception when it tries to + set the physical type of a new unit as the name of an existing + physical type. + """ + with pytest.raises(ValueError): + physical.def_physical_type(self.weird_unit, {"time", "something"}) + assert self.weird_unit.physical_type == "unknown" + + def test_expanding_names_for_physical_type(self): + """ + Test that calling `def_physical_type` on an existing physical + type adds a new physical type name. + """ + weird_name = "weird name" + strange_name = "strange name" + + try: + physical.def_physical_type(self.weird_unit, weird_name) + assert self.weird_unit.physical_type == weird_name, ( + f"unable to set physical type for {self.weird_unit}" + ) + finally: # cleanup added name + physical._attrname_physical_mapping.pop(weird_name.replace(" ", "_"), None) + physical._name_physical_mapping.pop(weird_name, None) + + # add both strange_name and weird_name + try: + physical.def_physical_type(self.weird_unit, strange_name) + assert set((self.weird_unit).physical_type) == { + weird_name, + strange_name, + }, "did not correctly append a new physical type name." + finally: # cleanup added names + physical._attrname_physical_mapping.pop( + strange_name.replace(" ", "_"), None + ) + physical._name_physical_mapping.pop(strange_name, None) + physical._attrname_physical_mapping.pop(weird_name.replace(" ", "_"), None) + physical._name_physical_mapping.pop(weird_name, None) + + def test_redundant_physical_type(self): + """ + Test that a physical type name already in use cannot be assigned + for another unit (excluding `"unknown"`). + """ + with pytest.raises(ValueError): + physical.def_physical_type(self.weird_unit, "length") + + @staticmethod + def _undef_physical_type(unit): + """Reset the physical type of unit to "unknown".""" + for name in list(unit.physical_type): + del physical._unit_physical_mapping[name] + del physical._physical_unit_mapping[unit._physical_type_id] + assert unit.physical_type == "unknown" + + def teardown_method(self): + """ + Remove the definitions of the physical types that were added + using `def_physical_unit` for testing purposes. + """ + for unit in [self.weird_unit, self.strange_unit]: + physical_type = physical.get_physical_type(unit) + if physical_type != "unknown": + self._undef_physical_type(unit) + assert unit.physical_type == "unknown", ( + f"the physical type for {unit}, which was added for" + "testing, was not deleted." + ) + + +@pytest.mark.parametrize("ptype_name", ["length", "speed", "entropy"]) +def test_pickling(ptype_name): + # Regression test for #11685 + ptype = u.get_physical_type(ptype_name) + pkl = pickle.dumps(ptype) + other = pickle.loads(pkl) + assert other == ptype + + +def test_physical_types_module_access(): + # all physical type names in dir + assert set(dir(physical)).issuperset(physical._attrname_physical_mapping.keys()) + assert set(dir(physical)).issuperset(physical.__all__) + + # all physical type can be accessed by name + for pname in physical._attrname_physical_mapping.keys(): + ptype = physical._attrname_physical_mapping[pname] + assert hasattr(physical, pname) # make sure works in lazy load + assert getattr(physical, pname) is ptype + + # a failed access + with pytest.raises(AttributeError, match="has no attribute"): + physical.not_a_valid_physical_type_name diff --git a/astropy/units/tests/test_quantity.py b/astropy/units/tests/test_quantity.py new file mode 100644 index 000000000000..99c42e744c53 --- /dev/null +++ b/astropy/units/tests/test_quantity.py @@ -0,0 +1,2137 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +"""Test the Quantity class and related.""" + +import copy +import decimal +import numbers +import operator +import pickle +from fractions import Fraction + +import numpy as np +import pytest +from numpy.testing import assert_allclose, assert_array_almost_equal, assert_array_equal + +from astropy import units as u +from astropy.units.quantity import _UNIT_NOT_INITIALISED +from astropy.utils.exceptions import AstropyDeprecationWarning, AstropyWarning +from astropy.utils.masked import Masked + +""" The Quantity class will represent a number + unit + uncertainty """ + + +class TestQuantityCreation: + def test_1(self): + # create objects through operations with Unit objects: + + quantity = 11.42 * u.meter # returns a Quantity object + assert isinstance(quantity, u.Quantity) + quantity = u.meter * 11.42 # returns a Quantity object + assert isinstance(quantity, u.Quantity) + + quantity = 11.42 / u.meter + assert isinstance(quantity, u.Quantity) + quantity = u.meter / 11.42 + assert isinstance(quantity, u.Quantity) + + quantity = 11.42 * u.meter / u.second + assert isinstance(quantity, u.Quantity) + + with pytest.raises(TypeError): + quantity = 182.234 + u.meter + + with pytest.raises(TypeError): + quantity = 182.234 - u.meter + + with pytest.raises(TypeError): + quantity = 182.234 % u.meter + + def test_2(self): + # create objects using the Quantity constructor: + _ = u.Quantity(11.412, unit=u.meter) + _ = u.Quantity(21.52, "cm") + q3 = u.Quantity(11.412) + + # By default quantities that don't specify a unit are unscaled + # dimensionless + assert q3.unit == u.Unit(1) + + with pytest.raises(TypeError): + u.Quantity(object(), unit=u.m) + + def test_3(self): + # with pytest.raises(u.UnitsError): + with pytest.raises(ValueError): # Until @mdboom fixes the errors in units + u.Quantity(11.412, unit="testingggg") + + def test_nan_inf(self): + # Not-a-number + q = u.Quantity("nan", unit="cm") + assert np.isnan(q.value) + + q = u.Quantity("NaN", unit="cm") + assert np.isnan(q.value) + + q = u.Quantity("-nan", unit="cm") # float() allows this + assert np.isnan(q.value) + + q = u.Quantity("nan cm") + assert np.isnan(q.value) + assert q.unit == u.cm + + # Infinity + q = u.Quantity("inf", unit="cm") + assert np.isinf(q.value) + + q = u.Quantity("-inf", unit="cm") + assert np.isinf(q.value) + + q = u.Quantity("inf cm") + assert np.isinf(q.value) + assert q.unit == u.cm + + q = u.Quantity("Infinity", unit="cm") # float() allows this + assert np.isinf(q.value) + + # make sure these strings don't parse... + with pytest.raises(TypeError): + q = u.Quantity("", unit="cm") + + with pytest.raises(TypeError): + q = u.Quantity("spam", unit="cm") + + def test_unit_property(self): + # test getting and setting 'unit' attribute + q1 = u.Quantity(11.4, unit=u.meter) + + with pytest.raises(AttributeError): + q1.unit = u.cm + + def test_preserve_dtype(self): + """Test that if an explicit dtype is given, it is used, while if not, + numbers are converted to float (including decimal.Decimal, which + numpy converts to an object; closes #1419) + """ + # If dtype is specified, use it, but if not, convert int, bool to float + q1 = u.Quantity(12, unit=u.m / u.s, dtype=int) + assert q1.dtype == int + + q2 = u.Quantity(q1) + assert q2.dtype == float + assert q2.value == float(q1.value) + assert q2.unit == q1.unit + + # but we should preserve any float32 or even float16 + a3_32 = np.array([1.0, 2.0], dtype=np.float32) + q3_32 = u.Quantity(a3_32, u.yr) + assert q3_32.dtype == a3_32.dtype + a3_16 = np.array([1.0, 2.0], dtype=np.float16) + q3_16 = u.Quantity(a3_16, u.yr) + assert q3_16.dtype == a3_16.dtype + # items stored as objects by numpy should be converted to float + # by default + q4 = u.Quantity(decimal.Decimal("10.25"), u.m) + assert q4.dtype == float + + q5 = u.Quantity(decimal.Decimal("10.25"), u.m, dtype=object) + assert q5.dtype == object + + def test_numpy_style_dtype_inspect(self): + """Test that if ``dtype=None``, NumPy's dtype inspection is used.""" + q2 = u.Quantity(12, dtype=None) + assert np.issubdtype(q2.dtype, np.integer) + + def test_float_dtype_promotion(self): + """Test that if ``dtype=numpy.inexact``, the minimum precision is float64.""" + q1 = u.Quantity(12, dtype=np.inexact) + assert not np.issubdtype(q1.dtype, np.integer) + assert q1.dtype == np.float64 + + q2 = u.Quantity(np.float64(12), dtype=np.inexact) + assert q2.dtype == np.float64 + + q3 = u.Quantity(np.float32(12), dtype=np.inexact) + assert q3.dtype == np.float32 + + if hasattr(np, "float16"): + q3 = u.Quantity(np.float16(12), dtype=np.inexact) + assert q3.dtype == np.float16 + + if hasattr(np, "float128"): + q4 = u.Quantity(np.float128(12), dtype=np.inexact) + assert q4.dtype == np.float128 + + def test_copy(self): + # By default, a new quantity is constructed, but not if copy=False + + a = np.arange(10.0) + + q0 = u.Quantity(a, unit=u.m / u.s) + assert q0.base is not a + + q1 = u.Quantity(a, unit=u.m / u.s, copy=False) + assert q1.base is a + + q2 = u.Quantity(q0) + assert q2 is not q0 + assert q2.base is not q0.base + + q2 = u.Quantity(q0, copy=False) + assert q2 is q0 + assert q2.base is q0.base + + q3 = u.Quantity(q0, q0.unit, copy=False) + assert q3 is q0 + assert q3.base is q0.base + + q4 = u.Quantity(q0, u.cm / u.s, copy=False) + assert q4 is not q0 + assert q4.base is not q0.base + + def test_subok(self): + """Test subok can be used to keep class, or to insist on Quantity""" + + class MyQuantitySubclass(u.Quantity): + pass + + myq = MyQuantitySubclass(np.arange(10.0), u.m) + # try both with and without changing the unit + assert type(u.Quantity(myq)) is u.Quantity + assert type(u.Quantity(myq, subok=True)) is MyQuantitySubclass + assert type(u.Quantity(myq, u.km)) is u.Quantity + assert type(u.Quantity(myq, u.km, subok=True)) is MyQuantitySubclass + + def test_order(self): + """Test that order is correctly propagated to np.array""" + ac = np.array(np.arange(10.0), order="C") + qcc = u.Quantity(ac, u.m, order="C") + assert qcc.flags["C_CONTIGUOUS"] + qcf = u.Quantity(ac, u.m, order="F") + assert qcf.flags["F_CONTIGUOUS"] + qca = u.Quantity(ac, u.m, order="A") + assert qca.flags["C_CONTIGUOUS"] + # check it works also when passing in a quantity + assert u.Quantity(qcc, order="C").flags["C_CONTIGUOUS"] + assert u.Quantity(qcc, order="A").flags["C_CONTIGUOUS"] + assert u.Quantity(qcc, order="F").flags["F_CONTIGUOUS"] + + af = np.array(np.arange(10.0), order="F") + qfc = u.Quantity(af, u.m, order="C") + assert qfc.flags["C_CONTIGUOUS"] + qff = u.Quantity(ac, u.m, order="F") + assert qff.flags["F_CONTIGUOUS"] + qfa = u.Quantity(af, u.m, order="A") + assert qfa.flags["F_CONTIGUOUS"] + assert u.Quantity(qff, order="C").flags["C_CONTIGUOUS"] + assert u.Quantity(qff, order="A").flags["F_CONTIGUOUS"] + assert u.Quantity(qff, order="F").flags["F_CONTIGUOUS"] + + def test_ndmin(self): + """Test that ndmin is correctly propagated to np.array""" + a = np.arange(10.0) + q1 = u.Quantity(a, u.m, ndmin=1) + assert q1.ndim == 1 and q1.shape == (10,) + q2 = u.Quantity(a, u.m, ndmin=2) + assert q2.ndim == 2 and q2.shape == (1, 10) + # check it works also when passing in a quantity + q3 = u.Quantity(q1, u.m, ndmin=3) + assert q3.ndim == 3 and q3.shape == (1, 1, 10) + + # see github issue #10063 + assert u.Quantity(u.Quantity(1, "m"), "m", ndmin=1).ndim == 1 + assert u.Quantity(u.Quantity(1, "cm"), "m", ndmin=1).ndim == 1 + + def test_non_quantity_with_unit(self): + """Test that unit attributes in objects get recognized.""" + + class MyQuantityLookalike(np.ndarray): + pass + + a = np.arange(3.0) + mylookalike = a.copy().view(MyQuantityLookalike) + mylookalike.unit = "m" + q1 = u.Quantity(mylookalike) + assert isinstance(q1, u.Quantity) + assert q1.unit is u.m + assert np.all(q1.value == a) + + q2 = u.Quantity(mylookalike, u.mm) + assert q2.unit is u.mm + assert np.all(q2.value == 1000.0 * a) + + q3 = u.Quantity(mylookalike, copy=False) + assert np.all(q3.value == mylookalike) + q3[2] = 0 + assert q3[2] == 0.0 + assert mylookalike[2] == 0.0 + + mylookalike = a.copy().view(MyQuantityLookalike) + mylookalike.unit = u.m + q4 = u.Quantity(mylookalike, u.mm, copy=False) + q4[2] = 0 + assert q4[2] == 0.0 + assert mylookalike[2] == 2.0 + + mylookalike.unit = "nonsense" + with pytest.raises(TypeError): + u.Quantity(mylookalike) + + def test_creation_via_view(self): + # This works but is no better than 1. * u.m + q1 = 1.0 << u.m + assert isinstance(q1, u.Quantity) + assert q1.unit == u.m + assert q1.value == 1.0 + # With an array, we get an actual view. + a2 = np.arange(10.0) + q2 = a2 << u.m / u.s + assert isinstance(q2, u.Quantity) + assert q2.unit == u.m / u.s + assert np.all(q2.value == a2) + a2[9] = 0.0 + assert np.all(q2.value == a2) + # But with a unit change we get a copy. + q3 = q2 << u.mm / u.s + assert isinstance(q3, u.Quantity) + assert q3.unit == u.mm / u.s + assert np.all(q3.value == a2 * 1000.0) + a2[8] = 0.0 + assert q3[8].value == 8000.0 + # Without a unit change, we do get a view. + q4 = q2 << q2.unit + a2[7] = 0.0 + assert np.all(q4.value == a2) + with pytest.raises(u.UnitsError): + q2 << u.s + # But one can do an in-place unit change. + a2_copy = a2.copy() + q2 <<= u.mm / u.s + assert q2.unit == u.mm / u.s + # Of course, this changes a2 as well. + assert np.all(q2.value == a2) + # Sanity check on the values. + assert np.all(q2.value == a2_copy * 1000.0) + a2[8] = -1.0 + # Using quantities, one can also work with strings. + q5 = q2 << "km/hr" + assert q5.unit == u.km / u.hr + assert np.all(q5 == q2) + # Finally, we can use scalar quantities as units. + not_quite_a_foot = 30.0 * u.cm + a6 = np.arange(5.0) + q6 = a6 << not_quite_a_foot + assert q6.unit == u.Unit(not_quite_a_foot) + assert np.all(q6.to_value(u.cm) == 30.0 * a6) + + def test_rshift_warns(self): + with ( + pytest.raises(TypeError), + pytest.warns(AstropyWarning, match="is not implemented") as warning_lines, + ): + 1 >> u.m + assert len(warning_lines) == 1 + q = 1.0 * u.km + with ( + pytest.raises(TypeError), + pytest.warns(AstropyWarning, match="is not implemented") as warning_lines, + ): + q >> u.m + assert len(warning_lines) == 1 + with ( + pytest.raises(TypeError), + pytest.warns(AstropyWarning, match="is not implemented") as warning_lines, + ): + q >>= u.m + assert len(warning_lines) == 1 + with ( + pytest.raises(TypeError), + pytest.warns(AstropyWarning, match="is not implemented") as warning_lines, + ): + 1.0 >> q + assert len(warning_lines) == 1 + + +class TestQuantityOperations: + q1 = u.Quantity(11.42, u.meter) + q2 = u.Quantity(8.0, u.centimeter) + + def test_addition(self): + # Take units from left object, q1 + new_quantity = self.q1 + self.q2 + assert new_quantity.value == 11.5 + assert new_quantity.unit == u.meter + + # Take units from left object, q2 + new_quantity = self.q2 + self.q1 + assert new_quantity.value == 1150.0 + assert new_quantity.unit == u.centimeter + + new_q = u.Quantity(1500.1, u.m) + u.Quantity(13.5, u.km) + assert new_q.unit == u.m + assert new_q.value == 15000.1 + + def test_subtraction(self): + # Take units from left object, q1 + new_quantity = self.q1 - self.q2 + assert new_quantity.value == 11.34 + assert new_quantity.unit == u.meter + + # Take units from left object, q2 + new_quantity = self.q2 - self.q1 + assert new_quantity.value == -1134.0 + assert new_quantity.unit == u.centimeter + + def test_multiplication(self): + # Take units from left object, q1 + new_quantity = self.q1 * self.q2 + assert new_quantity.value == 91.36 + assert new_quantity.unit == (u.meter * u.centimeter) + + # Take units from left object, q2 + new_quantity = self.q2 * self.q1 + assert new_quantity.value == 91.36 + assert new_quantity.unit == (u.centimeter * u.meter) + + # Multiply with a number + new_quantity = 15.0 * self.q1 + assert new_quantity.value == 171.3 + assert new_quantity.unit == u.meter + + # Multiply with a number + new_quantity = self.q1 * 15.0 + assert new_quantity.value == 171.3 + assert new_quantity.unit == u.meter + + # Multiple with a unit. + new_quantity = self.q1 * u.s + assert new_quantity.value == 11.42 + assert new_quantity.unit == u.Unit("m s") + + # Reverse multiple with a unit. + new_quantity = u.s * self.q1 + assert new_quantity.value == 11.42 + assert new_quantity.unit == u.Unit("m s") + + def test_division(self): + # Take units from left object, q1 + new_quantity = self.q1 / self.q2 + assert_array_almost_equal(new_quantity.value, 1.4275, decimal=5) + assert new_quantity.unit == (u.meter / u.centimeter) + + # Take units from left object, q2 + new_quantity = self.q2 / self.q1 + assert_array_almost_equal(new_quantity.value, 0.70052539404553416, decimal=16) + assert new_quantity.unit == (u.centimeter / u.meter) + + q1 = u.Quantity(11.4, unit=u.meter) + q2 = u.Quantity(10.0, unit=u.second) + new_quantity = q1 / q2 + assert_array_almost_equal(new_quantity.value, 1.14, decimal=10) + assert new_quantity.unit == (u.meter / u.second) + + # divide with a number + new_quantity = self.q1 / 10.0 + assert new_quantity.value == 1.142 + assert new_quantity.unit == u.meter + + # divide with a number + new_quantity = 11.42 / self.q1 + assert new_quantity.value == 1.0 + assert new_quantity.unit == u.Unit("1/m") + + # Divide by a unit. + new_quantity = self.q1 / u.s + assert new_quantity.value == 11.42 + assert new_quantity.unit == u.Unit("m/s") + + # Divide into a unit. + new_quantity = u.s / self.q1 + assert new_quantity.value == 1 / 11.42 + assert new_quantity.unit == u.Unit("s/m") + + def test_commutativity(self): + """Regression test for issue #587.""" + + new_q = u.Quantity(11.42, "m*s") + + assert self.q1 * u.s == u.s * self.q1 == new_q + assert self.q1 / u.s == u.Quantity(11.42, "m/s") + assert u.s / self.q1 == u.Quantity(1 / 11.42, "s/m") + + def test_power(self): + # raise quantity to a power + new_quantity = self.q1**2 + assert_array_almost_equal(new_quantity.value, 130.4164, decimal=5) + assert new_quantity.unit == u.Unit("m^2") + + new_quantity = self.q1**3 + assert_array_almost_equal(new_quantity.value, 1489.355288, decimal=7) + assert new_quantity.unit == u.Unit("m^3") + + @pytest.mark.parametrize( + "exponent_type", + [int, float, np.uint64, np.int32, np.float32, u.Quantity, Masked], + ) + def test_quantity_as_power(self, exponent_type): + # raise unit to a dimensionless Quantity power + # regression test for https://github.com/astropy/astropy/issues/16260 + q = u.m ** exponent_type(2) + assert q == u.m**2 + + def test_matrix_multiplication(self): + a = np.eye(3) + q = a * u.m + result1 = q @ a + assert np.all(result1 == q) + result2 = a @ q + assert np.all(result2 == q) + result3 = q @ q + assert np.all(result3 == a * u.m**2) + q2 = np.array( + [[[1., 0., 0.], + [0., 1., 0.], + [0., 0., 1.]], + [[0., 1., 0.], + [0., 0., 1.], + [1., 0., 0.]], + [[0., 0., 1.], + [1., 0., 0.], + [0., 1., 0.]]] + ) / u.s # fmt: skip + result4 = q @ q2 + assert np.all(result4 == np.matmul(a, q2.value) * q.unit * q2.unit) + + def test_unary(self): + # Test the minus unary operator + + new_quantity = -self.q1 + assert new_quantity.value == -self.q1.value + assert new_quantity.unit == self.q1.unit + + new_quantity = -(-self.q1) # noqa: B002 + assert new_quantity.value == self.q1.value + assert new_quantity.unit == self.q1.unit + + # Test the plus unary operator + + new_quantity = +self.q1 + assert new_quantity.value == self.q1.value + assert new_quantity.unit == self.q1.unit + + def test_abs(self): + q = 1.0 * u.m / u.s + new_quantity = abs(q) + assert new_quantity.value == q.value + assert new_quantity.unit == q.unit + + q = -1.0 * u.m / u.s + new_quantity = abs(q) + assert new_quantity.value == -q.value + assert new_quantity.unit == q.unit + + def test_incompatible_units(self): + """When trying to add or subtract units that aren't compatible, throw an error""" + + q1 = u.Quantity(11.412, unit=u.meter) + q2 = u.Quantity(21.52, unit=u.second) + + with pytest.raises(u.UnitsError): + q1 + q2 + + def test_non_number_type(self): + q1 = u.Quantity(11.412, unit=u.meter) + with pytest.raises( + TypeError, match=r"Unsupported operand type\(s\) for ufunc .*" + ): + q1 + {"a": 1} + + with pytest.raises(TypeError): + q1 + u.meter + + def test_dimensionless_operations(self): + # test conversion to dimensionless + dq = 3.0 * u.m / u.km + dq1 = dq + 1.0 * u.mm / u.km + assert dq1.value == 3.001 + assert dq1.unit == dq.unit + + dq2 = dq + 1.0 + assert dq2.value == 1.003 + assert dq2.unit == u.dimensionless_unscaled + + # this test will check that operations with dimensionless Quantities + # don't work + with pytest.raises(u.UnitsError): + self.q1 + u.Quantity(0.1, unit=u.Unit("")) + + with pytest.raises(u.UnitsError): + self.q1 - u.Quantity(0.1, unit=u.Unit("")) + + # and test that scaling of integers works + q = u.Quantity(np.array([1, 2, 3]), u.m / u.km, dtype=int) + q2 = q + np.array([4, 5, 6]) + assert q2.unit == u.dimensionless_unscaled + assert_allclose(q2.value, np.array([4.001, 5.002, 6.003])) + # but not if doing it inplace + with pytest.raises(TypeError): + q += np.array([1, 2, 3]) + # except if it is actually possible + q = np.array([1, 2, 3]) * u.km / u.m + q += np.array([4, 5, 6]) + assert q.unit == u.dimensionless_unscaled + assert np.all(q.value == np.array([1004, 2005, 3006])) + + def test_complicated_operation(self): + """Perform a more complicated test""" + from astropy.units import imperial + + # Multiple units + distance = u.Quantity(15.0, u.meter) + time = u.Quantity(11.0, u.second) + + velocity = (distance / time).to(imperial.mile / u.hour) + assert_array_almost_equal(velocity.value, 3.05037, decimal=5) + + G = u.Quantity(6.673e-11, u.m**3 / u.kg / u.s**2) + _ = (1.0 / (4.0 * np.pi * G)).to(u.pc**-3 / u.s**-2 * u.kg) + + # Area + side1 = u.Quantity(11.0, u.centimeter) + side2 = u.Quantity(7.0, u.centimeter) + area = side1 * side2 + assert_array_almost_equal(area.value, 77.0, decimal=15) + assert area.unit == u.cm * u.cm + + def test_comparison(self): + # equality/ non-equality is straightforward for quantity objects + assert (1 / (u.cm * u.cm)) == 1 * u.cm**-2 + assert 1 * u.m == 100 * u.cm + assert 1 * u.m != 1 * u.cm + + # when one is a unit, Quantity does not know what to do, + # but unit is fine with it, so it still works + unit = u.cm**3 + q = 1.0 * unit + assert q.__eq__(unit) is NotImplemented + assert unit.__eq__(q) is True + assert q == unit + q = 1000.0 * u.mm**3 + assert q == unit + + # mismatched types should never work + assert not 1.0 * u.cm == 1.0 + assert 1.0 * u.cm != 1.0 + + for quantity in (1.0 * u.cm, 1.0 * u.dimensionless_unscaled): + with pytest.raises(ValueError, match="ambiguous"): + bool(quantity) + + def test_numeric_converters(self): + # float, int, long, and __index__ should only work for single + # quantities, of appropriate type, and only if they are dimensionless. + # for index, this should be unscaled as well + # (Check on __index__ is also a regression test for #1557) + + # quantities with units should never convert, or be usable as an index + q1 = u.Quantity(1, u.m) + + converter_err_msg = ( + "only dimensionless scalar quantities can be converted to Python scalars" + ) + index_err_msg = ( + "only integer dimensionless scalar quantities " + "can be converted to a Python index" + ) + with pytest.raises(TypeError) as exc: + float(q1) + assert exc.value.args[0] == converter_err_msg + + with pytest.raises(TypeError) as exc: + int(q1) + assert exc.value.args[0] == converter_err_msg + + # We used to test `q1 * ['a', 'b', 'c'] here, but that that worked + # at all was a really odd confluence of bugs. Since it doesn't work + # in numpy >=1.10 any more, just go directly for `__index__` (which + # makes the test more similar to the `int`, `long`, etc., tests). + with pytest.raises(TypeError) as exc: + q1.__index__() + assert exc.value.args[0] == index_err_msg + + # dimensionless but scaled is OK, however + q2 = u.Quantity(1.23, u.m / u.km) + + assert float(q2) == float(q2.to_value(u.dimensionless_unscaled)) + assert int(q2) == int(q2.to_value(u.dimensionless_unscaled)) + + with pytest.raises(TypeError) as exc: + q2.__index__() + assert exc.value.args[0] == index_err_msg + + # dimensionless unscaled is OK, though for index needs to be int + q3 = u.Quantity(1.23, u.dimensionless_unscaled) + + assert float(q3) == 1.23 + assert int(q3) == 1 + + with pytest.raises(TypeError) as exc: + q3.__index__() + assert exc.value.args[0] == index_err_msg + + # integer dimensionless unscaled is good for all + q4 = u.Quantity(2, u.dimensionless_unscaled, dtype=int) + + assert float(q4) == 2.0 + assert int(q4) == 2 + + assert q4.__index__() == 2 + + # but arrays are not OK + q5 = u.Quantity([1, 2], u.m) + with pytest.raises(TypeError) as exc: + float(q5) + assert exc.value.args[0] == converter_err_msg + + with pytest.raises(TypeError) as exc: + int(q5) + assert exc.value.args[0] == converter_err_msg + + with pytest.raises(TypeError) as exc: + q5.__index__() + assert exc.value.args[0] == index_err_msg + + def test_array_converters(self): + # Scalar quantity + q = u.Quantity(1.23, u.m) + assert np.all(np.array(q) == np.array([1.23])) + + # Array quantity + q = u.Quantity([1.0, 2.0, 3.0], u.m) + assert np.all(np.array(q) == np.array([1.0, 2.0, 3.0])) + + def test_index(self): + val = 123 + out = operator.index(u.Quantity(val, u.one, dtype=int)) + assert out == val + + with pytest.raises(TypeError): + operator.index(u.Quantity(val, u.m, dtype=int)) + + def test__index_fails_for_list_multiplication(self): + # This used to work for numpy <= 1.10, but that's not coming back. + # See https://github.com/numpy/numpy/issues/5074 + q4 = u.Quantity(2, u.dimensionless_unscaled, dtype=int) + with pytest.raises(TypeError): + q4 * ["a", "b", "c"] + + +def test_quantity_conversion(): + q1 = u.Quantity(0.1, unit=u.meter) + value = q1.value + assert value == 0.1 + value_in_km = q1.to_value(u.kilometer) + assert value_in_km == 0.0001 + new_quantity = q1.to(u.kilometer) + assert new_quantity.value == 0.0001 + + with pytest.raises(u.UnitsError): + q1.to(u.zettastokes) + with pytest.raises(u.UnitsError): + q1.to_value(u.zettastokes) + + +def test_quantity_ilshift(): # in-place conversion + q = u.Quantity(10, unit=u.one) + + # Incompatible units. This goes through ilshift and hits a + # UnitConversionError first in ilshift, then in the unit's rlshift. + with pytest.raises(u.UnitConversionError): + q <<= u.rad + + # unless the equivalency is enabled + with u.add_enabled_equivalencies(u.dimensionless_angles()): + q <<= u.rad + + assert np.isclose(q, 10 * u.rad) + + +def test_quantity_round(): + q = u.Quantity(10.1289, unit=u.s) + assert np.isclose(round(q), 10 * u.s) + assert np.isclose(round(q, 2), 10.13 * u.s) + + +def test_regression_12964(): + # This will fail if the fix to + # https://github.com/astropy/astropy/issues/12964 doesn't work. + x = u.Quantity(10, u.km, dtype=int) + x <<= u.pc + + # We add a test that this worked. + assert x.unit is u.pc + assert x.dtype == np.float64 + + +def test_quantity_value_views(): + q1 = u.Quantity([1.0, 2.0], unit=u.meter) + # views if the unit is the same. + v1 = q1.value + v1[0] = 0.0 + assert np.all(q1 == [0.0, 2.0] * u.meter) + v2 = q1.to_value() + v2[1] = 3.0 + assert np.all(q1 == [0.0, 3.0] * u.meter) + v3 = q1.to_value("m") + v3[0] = 1.0 + assert np.all(q1 == [1.0, 3.0] * u.meter) + q2 = q1.to("m", copy=False) + q2[0] = 2 * u.meter + assert np.all(q1 == [2.0, 3.0] * u.meter) + v4 = q1.to_value("cm") + v4[0] = 0.0 + # copy if different unit. + assert np.all(q1 == [2.0, 3.0] * u.meter) + + +def test_quantity_conversion_with_equiv(): + q1 = u.Quantity(0.1, unit=u.meter) + v2 = q1.to_value(u.Hz, equivalencies=u.spectral()) + assert_allclose(v2, 2997924580.0) + q2 = q1.to(u.Hz, equivalencies=u.spectral()) + assert_allclose(q2.value, v2) + + q1 = u.Quantity(0.4, unit=u.arcsecond) + v2 = q1.to_value(u.au, equivalencies=u.parallax()) + q2 = q1.to(u.au, equivalencies=u.parallax()) + v3 = q2.to_value(u.arcminute, equivalencies=u.parallax()) + q3 = q2.to(u.arcminute, equivalencies=u.parallax()) + + assert_allclose(v2, 515662.015) + assert_allclose(q2.value, v2) + assert q2.unit == u.au + assert_allclose(v3, 0.0066666667) + assert_allclose(q3.value, v3) + assert q3.unit == u.arcminute + + +def test_quantity_conversion_equivalency_passed_on(): + class MySpectral(u.Quantity): + _equivalencies = u.spectral() + + def __quantity_view__(self, obj, unit): + return obj.view(MySpectral) + + def __quantity_instance__(self, *args, **kwargs): + return MySpectral(*args, **kwargs) + + q1 = MySpectral([1000, 2000], unit=u.Hz) + q2 = q1.to(u.nm) + assert q2.unit == u.nm + q3 = q2.to(u.Hz) + assert q3.unit == u.Hz + assert_allclose(q3.value, q1.value) + q4 = MySpectral([1000, 2000], unit=u.nm) + q5 = q4.to(u.Hz).to(u.nm) + assert q5.unit == u.nm + assert_allclose(q4.value, q5.value) + + +def test_self_equivalency(): + assert u.deg.is_equivalent(1 * u.radian) + + +def test_si(): + q1 = 10.0 * u.m * u.s**2 / (200.0 * u.ms) ** 2 # 250 meters + assert q1.si.value == 250 + assert q1.si.unit == u.m + + q = 10.0 * u.m # 10 meters + assert q.si.value == 10 + assert q.si.unit == u.m + + q = 10.0 / u.m # 10 1 / meters + assert q.si.value == 10 + assert q.si.unit == (1 / u.m) + + +def test_cgs(): + q1 = 10.0 * u.cm * u.s**2 / (200.0 * u.ms) ** 2 # 250 centimeters + assert q1.cgs.value == 250 + assert q1.cgs.unit == u.cm + + q = 10.0 * u.m # 10 centimeters + assert q.cgs.value == 1000 + assert q.cgs.unit == u.cm + + q = 10.0 / u.cm # 10 1 / centimeters + assert q.cgs.value == 10 + assert q.cgs.unit == (1 / u.cm) + + q = 10.0 * u.Pa # 10 pascals + assert q.cgs.value == 100 + assert q.cgs.unit == u.barye + + +class TestQuantityComparison: + def test_quantity_equality(self): + assert u.Quantity(1000, unit="m") == u.Quantity(1, unit="km") + assert not (u.Quantity(1, unit="m") == u.Quantity(1, unit="km")) + # for ==, !=, return False, True if units do not match + assert (u.Quantity(1100, unit=u.m) != u.Quantity(1, unit=u.s)) is True + assert (u.Quantity(1100, unit=u.m) == u.Quantity(1, unit=u.s)) is False + assert (u.Quantity(0, unit=u.m) == u.Quantity(0, unit=u.s)) is False + # But allow comparison with 0, +/-inf if latter unitless + assert u.Quantity(0, u.m) == 0.0 + assert u.Quantity(1, u.m) != 0.0 + assert u.Quantity(1, u.m) != np.inf + assert u.Quantity(np.inf, u.m) == np.inf + + def test_quantity_equality_array(self): + a = u.Quantity([0.0, 1.0, 1000.0], u.m) + b = u.Quantity(1.0, u.km) + eq = a == b + ne = a != b + assert np.all(eq == [False, False, True]) + assert np.all(eq != ne) + # For mismatched units, we should just get True, False + c = u.Quantity(1.0, u.s) + eq = a == c + ne = a != c + assert eq is False + assert ne is True + # Constants are treated as dimensionless, so False too. + eq = a == 1.0 + ne = a != 1.0 + assert eq is False + assert ne is True + # But 0 can have any units, so we can compare. + eq = a == 0 + ne = a != 0 + assert np.all(eq == [True, False, False]) + assert np.all(eq != ne) + # But we do not extend that to arrays; they should have the same unit. + d = np.array([0, 1.0, 1000.0]) + eq = a == d + ne = a != d + assert eq is False + assert ne is True + + def test_quantity_comparison(self): + assert u.Quantity(1100, unit=u.meter) > u.Quantity(1, unit=u.kilometer) + assert u.Quantity(900, unit=u.meter) < u.Quantity(1, unit=u.kilometer) + + with pytest.raises(u.UnitsError): + assert u.Quantity(1100, unit=u.meter) > u.Quantity(1, unit=u.second) + + with pytest.raises(u.UnitsError): + assert u.Quantity(1100, unit=u.meter) < u.Quantity(1, unit=u.second) + + assert u.Quantity(1100, unit=u.meter) >= u.Quantity(1, unit=u.kilometer) + assert u.Quantity(1000, unit=u.meter) >= u.Quantity(1, unit=u.kilometer) + + assert u.Quantity(900, unit=u.meter) <= u.Quantity(1, unit=u.kilometer) + assert u.Quantity(1000, unit=u.meter) <= u.Quantity(1, unit=u.kilometer) + + with pytest.raises(u.UnitsError): + assert u.Quantity(1100, unit=u.meter) >= u.Quantity(1, unit=u.second) + + with pytest.raises(u.UnitsError): + assert u.Quantity(1100, unit=u.meter) <= u.Quantity(1, unit=u.second) + + assert u.Quantity(1200, unit=u.meter) != u.Quantity(1, unit=u.kilometer) + + +class TestQuantityDisplay: + scalarintq = u.Quantity(1, unit="m", dtype=int) + scalarfloatq = u.Quantity(1.3, unit="m") + arrq = u.Quantity([1, 2.3, 8.9], unit="m") + + scalar_complex_q = u.Quantity(complex(1.0, 2.0)) + scalar_big_complex_q = u.Quantity(complex(1.0, 2.0e27) * 1e25) + scalar_big_neg_complex_q = u.Quantity(complex(-1.0, -2.0e27) * 1e36) + arr_complex_q = u.Quantity(np.arange(3) * (complex(-1.0, -2.0e27) * 1e36)) + big_arr_complex_q = u.Quantity(np.arange(125) * (complex(-1.0, -2.0e27) * 1e36)) + + def test_dimensionless_quantity_repr(self): + q2 = u.Quantity(1.0, unit="m-1") + q3 = u.Quantity(1, unit="m-1", dtype=int) + assert repr(self.scalarintq * q2) == "" + assert repr(self.arrq * q2) == "" + assert repr(self.scalarintq * q3) == "" + + def test_dimensionless_quantity_str(self): + q2 = u.Quantity(1.0, unit="m-1") + q3 = u.Quantity(1, unit="m-1", dtype=int) + assert str(self.scalarintq * q2) == "1.0" + assert str(self.scalarintq * q3) == "1" + assert str(self.arrq * q2) == "[1. 2.3 8.9]" + + def test_dimensionless_quantity_format(self): + q1 = u.Quantity(3.14) + assert format(q1, ".2f") == "3.14" + assert f"{q1:cds}" == "3.14" + + def test_scalar_quantity_str(self): + assert str(self.scalarintq) == "1 m" + assert str(self.scalarfloatq) == "1.3 m" + + def test_scalar_quantity_repr(self): + assert repr(self.scalarintq) == "" + assert repr(self.scalarfloatq) == "" + + def test_array_quantity_str(self): + assert str(self.arrq) == "[1. 2.3 8.9] m" + + def test_array_quantity_repr(self): + assert repr(self.arrq) == "" + + def test_scalar_quantity_format(self): + assert format(self.scalarintq, "02d") == "01 m" + assert format(self.scalarfloatq, ".1f") == "1.3 m" + assert format(self.scalarfloatq, ".0f") == "1 m" + assert f"{self.scalarintq:cds}" == "1 m" + assert f"{self.scalarfloatq:cds}" == "1.3 m" + + def test_uninitialized_unit_format(self): + bad_quantity = np.arange(10.0).view(u.Quantity) + assert str(bad_quantity).endswith(_UNIT_NOT_INITIALISED) + assert repr(bad_quantity).endswith(_UNIT_NOT_INITIALISED + ">") + + def test_to_string(self): + qscalar = u.Quantity(1.5e14, "m/s") + + # __str__ is the default `format` + assert str(qscalar) == qscalar.to_string() + + res = "Quantity as KMS: 150000000000.0 km / s" + assert f"Quantity as KMS: {qscalar.to_string(unit=u.km / u.s)}" == res + + # With precision set + res = "Quantity as KMS: 1.500e+11 km / s" + assert ( + f"Quantity as KMS: {qscalar.to_string(precision=3, unit=u.km / u.s)}" == res + ) + + # Precision set + formatter (precision should be overwritten) + res = "2e+11 km / s" + assert ( + f"{qscalar.to_string(precision=3, formatter='.0e', unit=u.km / u.s)}" == res + ) + + # Invalid format + with pytest.raises(ValueError): + qscalar.to_string(format="test") + + res = r"$1.5 \times 10^{14} \; \mathrm{\frac{m}{s}}$" + assert qscalar.to_string(format="latex") == res + assert qscalar.to_string(format="latex", subfmt="inline") == res + res = r"$\displaystyle 1.5 \times 10^{14} \; \mathrm{\frac{m}{s}}$" + assert qscalar.to_string(format="latex", subfmt="display") == res + + res = r"$1.5 \times 10^{14} \; \mathrm{m\,s^{-1}}$" + assert qscalar.to_string(format="latex_inline") == res + assert qscalar.to_string(format="latex_inline", subfmt="inline") == res + res = r"$\displaystyle 1.5 \times 10^{14} \; \mathrm{m\,s^{-1}}$" + assert qscalar.to_string(format="latex_inline", subfmt="display") == res + + res = "[0 1 2] (Unit not initialised)" + assert np.arange(3).view(u.Quantity).to_string() == res + + @pytest.mark.parametrize( + "quant, input_unit, format_spec, expected_result", + [ + pytest.param( + u.Quantity(1.5e14, "m/s"), + None, + ".2e", + "1.50e+14 m / s", + id="scientific_notation", + ), + pytest.param( + u.Quantity(0.123, "m/s"), + None, + "0.3f", + "0.123 m / s", + id="float_format", + ), + pytest.param( + u.Quantity(0.000123, "km/s"), + "m/s", + ".2e", + "1.23e-01 m / s", + id="scientific_notation_with_zero", + ), + pytest.param( + u.Quantity(1.23456789e15, "m/s"), + None, + ".2e", + "1.23e+15 m / s", + id="scientific_notation_large_number", + ), + pytest.param( + u.Quantity(123, "m"), + None, + ">10", + " 123.0 m", + id="right_aligned", + ), + pytest.param( + u.Quantity(123, "m"), + "km", + "=+10", + "+ 0.123 km", + id="sign_alignment_positive", + ), + pytest.param( + u.Quantity(-123, "m"), + "cm", + "=+10", + "- 12300.0 cm", + id="sign_alignment_negative", + ), + pytest.param( + u.Quantity(123, "m"), + None, + "^10", + " 123.0 m", + id="center_alignment", + ), + pytest.param( + u.Quantity(123, "m"), + None, + "<10", + "123.0 m", + id="left_aligned", + ), + pytest.param( + u.Quantity(123, "m"), + None, + "010", + "00000123.0 m", + id="zero_padding", + ), + pytest.param( + u.Quantity(1234567, "m"), + None, + ",", + "1,234,567.0 m", + id="thousands_separator", + ), + pytest.param( + u.Quantity(137000000, "lyr"), + None, + ">+30,.2e", + " +1.37e+08 lyr", + id="large_number_complex_format", + ), + pytest.param( + u.Quantity(1234567, "m"), + None, + "_", + "1_234_567.0 m", + id="custom_separator", + ), + pytest.param( + u.Quantity(2.5 - 1.2j), + None, + ".2f", + "2.50-1.20j", + id="complex_number_float_format", + ), + pytest.param( + u.Quantity(2.5 - 1.2j), + None, + ".2e", + "2.50e+00-1.20e+00j", + id="complex_number_scientific_notation", + ), + pytest.param( + u.Quantity(2012, "m/s"), + None, + None, + "2012.0 m / s", + id="default_format", + ), + ], + ) + def test_format_spec(self, quant, input_unit, format_spec, expected_result): + assert ( + quant.to_string(formatter=format_spec, unit=input_unit) == expected_result + ) + + @pytest.mark.parametrize( + "quant, input_unit, format_spec, format, expected_result", + [ + pytest.param( + u.Quantity(2.5 - 1.2j), + None, + None, + "latex", + r"$(2.5-1.2i) \; \mathrm{}$", + id="complex_number_latex_default", + ), + pytest.param( + u.Quantity(1.2e3, "m"), + None, + None, + "latex", + r"$1200 \; \mathrm{m}$", + id="complex_number_latex_default", + ), + pytest.param( + u.Quantity(2.5 - 1.2j), + None, + "+.2f", + "latex", + r"$(+2.50-1.20i) \; \mathrm{}$", + id="complex_number_latex_positive_format", + ), + pytest.param( + u.Quantity(2.5 - 1.2j), + None, + "-.2f", + "latex", + r"$(2.50-1.20i) \; \mathrm{}$", + id="complex_number_latex_negative_format", + ), + pytest.param( + u.Quantity(2.5 - 1.2j), + None, + ">+20.5f", + "latex", + r"$(+2.50000-1.20000i) \; \mathrm{}$", + id="complex_number_latex_positive_alignment", + ), + pytest.param( + u.Quantity(137000000, "lyr"), + None, + ">+30,.2e", + "latex", + r"$+1.37 \times 10^{8} \; \mathrm{lyr}$", + id="large_number_latex_complex_format", + ), + pytest.param( + u.Quantity(2.5 - 1.2j), + None, + " .2f", + "latex", + r"$( 2.50-1.20i) \; \mathrm{}$", + id="complex_number_latex_space_format", + ), + pytest.param( + u.Quantity(1.23456789e15, "m/s"), + None, + ".3e", + "latex", + r"$1.235 \times 10^{15} \; \mathrm{\frac{m}{s}}$", + id="scientific_notation_latex_format", + ), + pytest.param( + u.Quantity(123.456, "km/s"), + None, + ".2f", + "latex", + r"$123.46 \; \mathrm{\frac{km}{s}}$", + id="float_latex_format", + ), + pytest.param( + u.Quantity(123.456, "m/s"), + None, + ".2f", + "latex_inline", + r"$123.46 \; \mathrm{m\,s^{-1}}$", + id="inline_latex_format", + ), + pytest.param( + u.Quantity(123.456, "m/s"), + None, + ".3e", + "latex_inline", + r"$1.235 \times 10^{2} \; \mathrm{m\,s^{-1}}$", + id="scientific_notation_inline_latex_format", + ), + pytest.param( + u.Quantity(1239999123, "m/s"), + None, + None, + "latex", + r"$1.2399991 \times 10^{9} \; \mathrm{\frac{m}{s}}$", + id="default_exponential_latex_format", + ), + pytest.param( + u.Quantity(2.5 - 1.2j), + None, + None, + "latex", + r"$(2.5-1.2i) \; \mathrm{}$", + id="default_complex_latex_format", + ), + ], + ) + def test_format_spec_latex( + self, quant, input_unit, format_spec, format, expected_result + ): + assert ( + quant.to_string(formatter=format_spec, format=format, unit=input_unit) + == expected_result + ) + + @pytest.mark.parametrize( + "quant, formatter, expected_result", + [ + pytest.param( + 1.2345 * u.kg, + lambda x: f"{float(x):.2f}", + r"1.23 kg", + id="explicit_formatting", + ), + pytest.param( + 35.0 * u.lyr, + { + "float": lambda x: f"{float(x):.1f}", + "int": lambda x: f"{float(x):.3f}", + }, + r"35.0 lyr", + id="dictionary_formatters", + ), + ], + ) + def test_formatter(self, quant, formatter, expected_result): + result = quant.to_string(formatter=formatter) + assert result == expected_result + + @pytest.mark.parametrize( + "quant, formatter, format, expected_result", + [ + pytest.param( + 35.0 * u.lyr, + {"all": lambda x: f"{float(x):.3f}"}, + "latex", + r"$35.000 \; \mathrm{lyr}$", + id="dictionary_formatters_latex", + ), + pytest.param( + 1.2345 * u.kg, + lambda x: f"{float(x):.2f}", + "latex", + r"$1.23 \; \mathrm{kg}$", + id="numerical_formatting_latex", + ), + pytest.param( + 35 * u.km / u.s, + lambda x: f"\\approx {float(x):.1f}", + "latex", + r"$\approx 35.0 \; \mathrm{\frac{km}{s}}$", + id="complex_formatting_latex", + ), + pytest.param( + u.Quantity(2.5 - 1.2j), + lambda x: f"({x.real:.2f}{x.imag:+.1f}j)", + "latex", + r"$(2.50-1.2j) \; \mathrm{}$", + id="complex_custom_formatting_latex", + ), + ], + ) + def test_formatter_latex(self, quant, formatter, format, expected_result): + result = quant.to_string(formatter=formatter, format=format) + assert result == expected_result + + @pytest.mark.parametrize("format_spec", ["b", "o", "x", "c", "s"]) + def test_format_spec_prohibition(self, format_spec): + qscalar = u.Quantity(123, "m") + with pytest.raises(ValueError): + qscalar.to_string(formatter=format_spec) + + def test_repr_latex(self): + from astropy.units.quantity import conf + + q2scalar = u.Quantity(1.5e14, "m/s") + assert self.scalarintq._repr_latex_() == r"$1 \; \mathrm{m}$" + assert self.scalarfloatq._repr_latex_() == r"$1.3 \; \mathrm{m}$" + assert ( + q2scalar._repr_latex_() == r"$1.5 \times 10^{14} \; \mathrm{\frac{m}{s}}$" + ) + assert self.arrq._repr_latex_() == r"$[1,~2.3,~8.9] \; \mathrm{m}$" + + # Complex quantities + assert self.scalar_complex_q._repr_latex_() == r"$(1+2i) \; \mathrm{}$" + assert ( + self.scalar_big_complex_q._repr_latex_() + == r"$(1 \times 10^{25}+2 \times 10^{52}i) \; \mathrm{}$" + ) + assert ( + self.scalar_big_neg_complex_q._repr_latex_() + == r"$(-1 \times 10^{36}-2 \times 10^{63}i) \; \mathrm{}$" + ) + assert self.arr_complex_q._repr_latex_() == ( + r"$[(0-0i),~(-1 \times 10^{36}-2 \times 10^{63}i)," + r"~(-2 \times 10^{36}-4 \times 10^{63}i)] \; \mathrm{}$" + ) + assert r"\dots" in self.big_arr_complex_q._repr_latex_() + + qmed = np.arange(100) * u.m + qbig = np.arange(1000) * u.m + qvbig = np.arange(10000) * 1e9 * u.m + + pops = np.get_printoptions() + oldlat = conf.latex_array_threshold + try: + # check precision behavior + q = u.Quantity(987654321.123456789, "m/s") + qa = np.array([7.89123, 123456789.987654321, 0]) * u.cm + np.set_printoptions(precision=8) + assert ( + q._repr_latex_() == r"$9.8765432 \times 10^{8} \; \mathrm{\frac{m}{s}}$" + ) + assert ( + qa._repr_latex_() + == r"$[7.89123,~1.2345679 \times 10^{8},~0] \; \mathrm{cm}$" + ) + np.set_printoptions(precision=2) + assert q._repr_latex_() == r"$9.9 \times 10^{8} \; \mathrm{\frac{m}{s}}$" + assert qa._repr_latex_() == r"$[7.9,~1.2 \times 10^{8},~0] \; \mathrm{cm}$" + + # check thresholding behavior + conf.latex_array_threshold = 100 # should be default + lsmed = qmed._repr_latex_() + assert r"\dots" not in lsmed + lsbig = qbig._repr_latex_() + assert r"\dots" in lsbig + lsvbig = qvbig._repr_latex_() + assert r"\dots" in lsvbig + + conf.latex_array_threshold = 1001 + lsmed = qmed._repr_latex_() + assert r"\dots" not in lsmed + lsbig = qbig._repr_latex_() + assert r"\dots" not in lsbig + lsvbig = qvbig._repr_latex_() + assert r"\dots" in lsvbig + + conf.latex_array_threshold = -1 # means use the numpy threshold + np.set_printoptions(threshold=99) + lsmed = qmed._repr_latex_() + assert r"\dots" in lsmed + lsbig = qbig._repr_latex_() + assert r"\dots" in lsbig + lsvbig = qvbig._repr_latex_() + assert r"\dots" in lsvbig + assert lsvbig.endswith(",~1 \\times 10^{13}] \\; \\mathrm{m}$") + finally: + # prevent side-effects from influencing other tests + np.set_printoptions(**pops) + conf.latex_array_threshold = oldlat + + qinfnan = [np.inf, -np.inf, np.nan] * u.m + assert qinfnan._repr_latex_() == r"$[\infty,~-\infty,~{\rm NaN}] \; \mathrm{m}$" + + @pytest.mark.parametrize( + "q, expected", + [ + pytest.param(0 * u.imperial.deg_R, r"$0\mathrm{{}^{\circ}R}$", id="deg_R"), + pytest.param(5 * u.imperial.deg_F, r"$5\mathrm{{}^{\circ}F}$", id="deg_F"), + pytest.param(10 * u.deg_C, r"$10\mathrm{{}^{\circ}C}$", id="deg_C"), + pytest.param(20 * u.deg, r"$20\mathrm{{}^{\circ}}$", id="deg"), + pytest.param(30 * u.arcmin, r"$30\mathrm{{}^{\prime}}$", id="arcmin"), + pytest.param(40 * u.arcsec, r"$40\mathrm{{}^{\prime\prime}}$", id="arcsec"), + pytest.param(50 * u.hourangle, r"$50\mathrm{{}^{h}}$", id="hourangle"), + ], + ) + def test_repr_latex_superscript_units(self, q, expected): + # see https://github.com/astropy/astropy/issues/14385 + assert q._repr_latex_() == expected + assert q.to_string(format="latex") == expected + + +def test_decompose(): + q1 = 5 * u.N + assert q1.decompose() == (5 * u.kg * u.m * u.s**-2) + + +def test_decompose_regression(): + """ + Regression test for bug #1163 + + If decompose was called multiple times on a Quantity with an array and a + scale != 1, the result changed every time. This is because the value was + being referenced not copied, then modified, which changed the original + value. + """ + q = np.array([1, 2, 3]) * u.m / (2.0 * u.km) + assert np.all(q.decompose().value == np.array([0.0005, 0.001, 0.0015])) + assert np.all(q == np.array([1, 2, 3]) * u.m / (2.0 * u.km)) + assert np.all(q.decompose().value == np.array([0.0005, 0.001, 0.0015])) + + +def test_arrays(): + """ + Test using quantities with array values + """ + + qsec = u.Quantity(np.arange(10), u.second) + assert isinstance(qsec.value, np.ndarray) + assert not qsec.isscalar + + # len and indexing should work for arrays + assert len(qsec) == len(qsec.value) + qsecsub25 = qsec[2:5] + assert qsecsub25.unit == qsec.unit + assert isinstance(qsecsub25, u.Quantity) + assert len(qsecsub25) == 3 + + # make sure isscalar, len, and indexing behave correctly for non-arrays. + qsecnotarray = u.Quantity(10.0, u.second) + assert qsecnotarray.isscalar + with pytest.raises(TypeError): + len(qsecnotarray) + with pytest.raises(TypeError): + qsecnotarray[0] + + qseclen0array = u.Quantity(np.array(10), u.second, dtype=int) + # 0d numpy array should act basically like a scalar + assert qseclen0array.isscalar + with pytest.raises(TypeError): + len(qseclen0array) + with pytest.raises(TypeError): + qseclen0array[0] + assert isinstance(qseclen0array.value, numbers.Integral) + + a = np.array( + [(1.0, 2.0, 3.0), (4.0, 5.0, 6.0), (7.0, 8.0, 9.0)], + dtype=[("x", float), ("y", float), ("z", float)], + ) + qkpc = u.Quantity(a, u.kpc) + assert not qkpc.isscalar + qkpc0 = qkpc[0] + assert qkpc0.value == a[0] + assert qkpc0.unit == qkpc.unit + assert isinstance(qkpc0, u.Quantity) + assert qkpc0.isscalar + qkpcx = qkpc["x"] + assert np.all(qkpcx.value == a["x"]) + assert qkpcx.unit == qkpc.unit + assert isinstance(qkpcx, u.Quantity) + assert not qkpcx.isscalar + qkpcx1 = qkpc["x"][1] + assert qkpcx1.unit == qkpc.unit + assert isinstance(qkpcx1, u.Quantity) + assert qkpcx1.isscalar + qkpc1x = qkpc[1]["x"] + assert qkpc1x.isscalar + assert qkpc1x == qkpcx1 + + # can also create from lists, will auto-convert to arrays + qsec = u.Quantity(list(range(10)), u.second) + assert isinstance(qsec.value, np.ndarray) + + # quantity math should work with arrays + assert_array_equal((qsec * 2).value, (np.arange(10) * 2)) + assert_array_equal((qsec / 2).value, (np.arange(10) / 2)) + # quantity addition/subtraction should *not* work with arrays b/c unit + # ambiguous + with pytest.raises(u.UnitsError): + assert_array_equal((qsec + 2).value, (np.arange(10) + 2)) + with pytest.raises(u.UnitsError): + assert_array_equal((qsec - 2).value, (np.arange(10) + 2)) + + # should create by unit multiplication, too + qsec2 = np.arange(10) * u.second + qsec3 = u.second * np.arange(10) + + assert np.all(qsec == qsec2) + assert np.all(qsec2 == qsec3) + + # make sure numerical-converters fail when arrays are present + with pytest.raises(TypeError): + float(qsec) + with pytest.raises(TypeError): + int(qsec) + + +def test_array_indexing_slicing(): + q = np.array([1.0, 2.0, 3.0]) * u.m + assert q[0] == 1.0 * u.m + assert np.all(q[0:2] == u.Quantity([1.0, 2.0], u.m)) + + +def test_array_setslice(): + q = np.array([1.0, 2.0, 3.0]) * u.m + q[1:2] = np.array([400.0]) * u.cm + assert np.all(q == np.array([1.0, 4.0, 3.0]) * u.m) + + +def test_inverse_quantity(): + """ + Regression test from issue #679 + """ + q = u.Quantity(4.0, u.meter / u.second) + qot = q / 2 + toq = 2 / q + npqot = q / np.array(2) + + assert npqot.value == 2.0 + assert npqot.unit == (u.meter / u.second) + + assert qot.value == 2.0 + assert qot.unit == (u.meter / u.second) + + assert toq.value == 0.5 + assert toq.unit == (u.second / u.meter) + + +def test_quantity_mutability(): + q = u.Quantity(9.8, u.meter / u.second / u.second) + + with pytest.raises(AttributeError): + q.value = 3 + + with pytest.raises(AttributeError): + q.unit = u.kg + + +def test_quantity_initialized_with_quantity(): + q1 = u.Quantity(60, u.second) + + q2 = u.Quantity(q1, u.minute) + assert q2.value == 1 + + q3 = u.Quantity([q1, q2], u.second) + assert q3[0].value == 60 + assert q3[1].value == 60 + + q4 = u.Quantity([q2, q1]) + assert q4.unit == q2.unit + assert q4[0].value == 1 + assert q4[1].value == 1 + + +def test_quantity_string_unit(): + with pytest.warns( + AstropyDeprecationWarning, + match=( + "^divisions involving a unit and a 'str' instance are deprecated since " + r"v7\.1\. Convert 's' to a unit explicitly\.$" + ), + ): + q1 = 1.0 * u.m / "s" + assert q1.value == 1 + assert q1.unit == (u.m / u.s) + + with pytest.warns( + AstropyDeprecationWarning, + match=( + "^products involving a unit and a 'str' instance are deprecated since " + r"v7\.1\. Convert 'm' to a unit explicitly\.$" + ), + ): + q2 = q1 * "m" + assert q2.unit == ((u.m * u.m) / u.s) + + +def test_quantity_invalid_unit_string(): + with ( + pytest.raises(ValueError), + pytest.warns(AstropyDeprecationWarning, match="^products involving .* a 'str'"), + ): + "foo" * u.m + + +def test_implicit_conversion(): + q = u.Quantity(1.0, u.meter) + # Manually turn this on to simulate what might happen in a subclass + q._include_easy_conversion_members = True + assert_allclose(q.centimeter, 100) + assert_allclose(q.cm, 100) + assert_allclose(q.parsec, 3.240779289469756e-17) + + +def test_implicit_conversion_autocomplete(): + q = u.Quantity(1.0, u.meter) + # Manually turn this on to simulate what might happen in a subclass + q._include_easy_conversion_members = True + q.foo = 42 + + attrs = dir(q) + assert "centimeter" in attrs + assert "cm" in attrs + assert "parsec" in attrs + assert "foo" in attrs + assert "to" in attrs + assert "value" in attrs + # Something from the base class, object + assert "__setattr__" in attrs + + with pytest.raises(AttributeError): + q.l + + +def test_quantity_iterability(): + """Regressiont est for issue #878. + + Scalar quantities should not be iterable and should raise a type error on + iteration. + """ + + q1 = [15.0, 17.0] * u.m + assert np.iterable(q1) + + q2 = next(iter(q1)) + assert q2 == 15.0 * u.m + assert not np.iterable(q2) + with pytest.raises(TypeError): + iter(q2) + + +def test_copy(): + q1 = u.Quantity(np.array([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]), unit=u.m) + q2 = q1.copy() + + assert np.all(q1.value == q2.value) + assert q1.unit == q2.unit + assert q1.dtype == q2.dtype + assert q1.value is not q2.value + + q3 = q1.copy(order="F") + assert q3.flags["F_CONTIGUOUS"] + assert np.all(q1.value == q3.value) + assert q1.unit == q3.unit + assert q1.dtype == q3.dtype + assert q1.value is not q3.value + + q4 = q1.copy(order="C") + assert q4.flags["C_CONTIGUOUS"] + assert np.all(q1.value == q4.value) + assert q1.unit == q4.unit + assert q1.dtype == q4.dtype + assert q1.value is not q4.value + + +def test_deepcopy(): + q1 = u.Quantity(np.array([1.0, 2.0, 3.0]), unit=u.m) + q2 = copy.deepcopy(q1) + + assert isinstance(q2, u.Quantity) + assert np.all(q1.value == q2.value) + assert q1.unit == q2.unit + assert q1.dtype == q2.dtype + + assert q1.value is not q2.value + + +def test_equality_numpy_scalar(): + """ + A regression test to ensure that numpy scalars are correctly compared + (which originally failed due to the lack of ``__array_priority__``). + """ + assert 10 != 10.0 * u.m + assert np.int64(10) != 10 * u.m + assert 10 * u.m != np.int64(10) + + +def test_quantity_pickelability(): + """ + Testing pickleability of quantity + """ + + q1 = np.arange(10) * u.m + + q2 = pickle.loads(pickle.dumps(q1)) + + assert np.all(q1.value == q2.value) + assert q1.unit.is_equivalent(q2.unit) + assert q1.unit == q2.unit + + +def test_quantity_initialisation_from_string(): + q = u.Quantity("1") + assert q.unit == u.dimensionless_unscaled + assert q.value == 1.0 + q = u.Quantity("1.5 m/s") + assert q.unit == u.m / u.s + assert q.value == 1.5 + assert u.Unit(q) == u.Unit("1.5 m/s") + q = u.Quantity(".5 m") + assert q == u.Quantity(0.5, u.m) + q = u.Quantity("-1e1km") + assert q == u.Quantity(-10, u.km) + q = u.Quantity("-1e+1km") + assert q == u.Quantity(-10, u.km) + q = u.Quantity("+.5km") + assert q == u.Quantity(0.5, u.km) + q = u.Quantity("+5e-1km") + assert q == u.Quantity(0.5, u.km) + q = u.Quantity("5", u.m) + assert q == u.Quantity(5.0, u.m) + q = u.Quantity("5 km", u.m) + assert q.value == 5000.0 + assert q.unit == u.m + q = u.Quantity("5Em") + assert q == u.Quantity(5.0, u.Em) + + with pytest.raises(TypeError): + u.Quantity("") + with pytest.raises(TypeError): + u.Quantity("m") + with pytest.raises(TypeError): + u.Quantity("1.2.3 deg") + with pytest.raises(TypeError): + u.Quantity("1+deg") + with pytest.raises(TypeError): + u.Quantity("1-2deg") + with pytest.raises(TypeError): + u.Quantity("1.2e-13.3m") + with pytest.raises(TypeError): + u.Quantity(["5"]) + with pytest.raises(TypeError): + u.Quantity(np.array(["5"])) + with pytest.raises(TypeError): + u.Quantity("['1' '5' '8']") + with pytest.raises(TypeError): + u.Quantity("[1, 'two', 9]") + with pytest.raises(TypeError): + u.Quantity("[1, 4 9]") + with pytest.raises(ValueError): + u.Quantity("5E") + with pytest.raises(ValueError): + u.Quantity("5 foo") + + +@pytest.mark.parametrize("unit_str", ["", "eV", " cm"]) +@pytest.mark.parametrize( + "array_str", + ( + "[7, 8, 9]", + "[7,8,9]", + "[7,8,9,]", + "[7, 8, 9,]", + "[7. 8. 9.]", + "[7. 8. 9.]", + "[7 8 9]", + "[7 8 9]", + "[7 8 9]", + ), +) +def test_quantity_initialisation_string_array(array_str, unit_str): + q = u.Quantity(array_str + unit_str) + assert q.unit == unit_str + assert_array_equal(q.value, np.array([7.0, 8.0, 9.0])) + + +def test_unsupported(): + q1 = np.arange(10) * u.m + + with pytest.raises(TypeError): + np.bitwise_and(q1, q1) + + +def test_unit_identity(): + q = 1.0 * u.hour + assert q.unit is u.hour + + +def test_quantity_to_view(): + q1 = np.array([1000, 2000]) * u.m + q2 = q1.to(u.km) + assert q1.value[0] == 1000 + assert q2.value[0] == 1 + + +def test_quantity_tuple_power(): + with pytest.raises(ValueError): + (5.0 * u.m) ** (1, 2) + + +def test_quantity_fraction_power(): + q = (25.0 * u.m**2) ** Fraction(1, 2) + assert q.value == 5.0 + assert q.unit == u.m + # Regression check to ensure we didn't create an object type by raising + # the value of the quantity to a Fraction. [#3922] + assert q.dtype.kind == "f" + + +def test_quantity_from_table(): + """ + Checks that units from tables are respected when converted to a Quantity. + This also generically checks the use of *anything* with a `unit` attribute + passed into Quantity + """ + from astropy.table import Table + + t = Table(data=[np.arange(5), np.arange(5)], names=["a", "b"]) + t["a"].unit = u.kpc + + qa = u.Quantity(t["a"]) + assert qa.unit == u.kpc + assert_array_equal(qa.value, t["a"]) + + qb = u.Quantity(t["b"]) + assert qb.unit == u.dimensionless_unscaled + assert_array_equal(qb.value, t["b"]) + + # This does *not* auto-convert, because it's not necessarily obvious that's + # desired. Instead we revert to standard `Quantity` behavior + qap = u.Quantity(t["a"], u.pc) + assert qap.unit == u.pc + assert_array_equal(qap.value, t["a"] * 1000) + + qbp = u.Quantity(t["b"], u.pc) + assert qbp.unit == u.pc + assert_array_equal(qbp.value, t["b"]) + + # Also check with a function unit (regression test for gh-8430) + t["a"].unit = u.dex(u.cm / u.s**2) + fq = u.Dex(t["a"]) + assert fq.unit == u.dex(u.cm / u.s**2) + assert_array_equal(fq.value, t["a"]) + + fq2 = u.Quantity(t["a"], subok=True) + assert isinstance(fq2, u.Dex) + assert fq2.unit == u.dex(u.cm / u.s**2) + assert_array_equal(fq2.value, t["a"]) + + with pytest.raises(u.UnitTypeError): + u.Quantity(t["a"]) + + +def test_assign_slice_with_quantity_like(): + # Regression tests for gh-5961 + from astropy.table import Column, Table + + # first check directly that we can use a Column to assign to a slice. + c = Column(np.arange(10.0), unit=u.mm) + q = u.Quantity(c) + q[:2] = c[:2] + # next check that we do not fail the original problem. + t = Table() + t["x"] = np.arange(10) * u.mm + t["y"] = np.ones(10) * u.mm + assert type(t["x"]) is Column + + xy = np.vstack([t["x"], t["y"]]).T * u.mm + ii = [0, 2, 4] + + assert xy[ii, 0].unit == t["x"][ii].unit + # should not raise anything + xy[ii, 0] = t["x"][ii] + + +def test_insert(): + """ + Test Quantity.insert method. This does not test the full capabilities + of the underlying np.insert, but hits the key functionality for + Quantity. + """ + q = [1, 2] * u.m + + # Insert a compatible float with different units + q2 = q.insert(0, 1 * u.km) + assert np.all(q2.value == [1000, 1, 2]) + assert q2.unit is u.m + assert q2.dtype.kind == "f" + + q2 = q.insert(1, [1, 2] * u.km) + assert np.all(q2.value == [1, 1000, 2000, 2]) + assert q2.unit is u.m + + # Cannot convert 1.5 * u.s to m + with pytest.raises(u.UnitsError): + q.insert(1, 1.5 * u.s) + + # Tests with multi-dim quantity + q = [[1, 2], [3, 4]] * u.m + q2 = q.insert(1, [10, 20] * u.m, axis=0) + assert np.all(q2.value == [[1, 2], [10, 20], [3, 4]]) + + q2 = q.insert(1, [10, 20] * u.m, axis=1) + assert np.all(q2.value == [[1, 10, 2], [3, 20, 4]]) + + q2 = q.insert(1, 10 * u.m, axis=1) + assert np.all(q2.value == [[1, 10, 2], [3, 10, 4]]) + + +def test_repr_array_of_quantity(): + """ + Test print/repr of object arrays of Quantity objects with different + units. + + Regression test for the issue first reported in + https://github.com/astropy/astropy/issues/3777 + """ + + a = np.array([1 * u.m, 2 * u.s], dtype=object) + assert repr(a) == "array([, ], dtype=object)" + assert str(a) == "[ ]" + + +class TestSpecificTypeQuantity: + def setup_method(self): + class Length(u.SpecificTypeQuantity): + _equivalent_unit = u.m + + class Length2(Length): + _default_unit = u.m + + class Length3(Length): + _unit = u.m + + self.Length = Length + self.Length2 = Length2 + self.Length3 = Length3 + + def test_creation(self): + l = self.Length(np.arange(10.0) * u.km) + assert type(l) is self.Length + with pytest.raises(u.UnitTypeError): + self.Length(np.arange(10.0) * u.hour) + + with pytest.raises(u.UnitTypeError): + self.Length(np.arange(10.0)) + + l2 = self.Length2(np.arange(5.0)) + assert type(l2) is self.Length2 + assert l2._default_unit is self.Length2._default_unit + + with pytest.raises(u.UnitTypeError): + self.Length3(np.arange(10.0)) + + def test_view(self): + l = (np.arange(5.0) * u.km).view(self.Length) + assert type(l) is self.Length + with pytest.raises(u.UnitTypeError): + (np.arange(5.0) * u.s).view(self.Length) + + v = np.arange(5.0).view(self.Length) + assert type(v) is self.Length + assert v._unit is None + + l3 = np.ones((2, 2)).view(self.Length3) + assert type(l3) is self.Length3 + assert l3.unit is self.Length3._unit + + def test_operation_precedence_and_fallback(self): + l = self.Length(np.arange(5.0) * u.cm) + sum1 = l + 1.0 * u.m + assert type(sum1) is self.Length + sum2 = 1.0 * u.km + l + assert type(sum2) is self.Length + sum3 = l + l + assert type(sum3) is self.Length + res1 = l * (1.0 * u.m) + assert type(res1) is u.Quantity + res2 = l * l + assert type(res2) is u.Quantity + + +def test_unit_class_override(): + class MyQuantity(u.Quantity): + pass + + my_unit = u.Unit("my_deg", u.deg) + my_unit._quantity_class = MyQuantity + q1 = u.Quantity(1.0, my_unit) + assert type(q1) is u.Quantity + q2 = u.Quantity(1.0, my_unit, subok=True) + assert type(q2) is MyQuantity + + +class QuantityMimic: + def __init__(self, value, unit): + self.value = value + self.unit = unit + + def __array__(self, dtype=None, copy=None): + return np.array(self.value, dtype=dtype, copy=copy) + + +class QuantityMimic2(QuantityMimic): + def to(self, unit): + return u.Quantity(self.value, self.unit).to(unit) + + def to_value(self, unit): + return u.Quantity(self.value, self.unit).to_value(unit) + + +class TestQuantityMimics: + """Test Quantity Mimics that are not ndarray subclasses.""" + + @pytest.mark.parametrize("Mimic", (QuantityMimic, QuantityMimic2)) + def test_mimic_input(self, Mimic): + value = np.arange(10.0) + mimic = Mimic(value, u.m) + q = u.Quantity(mimic) + assert q.unit == u.m + assert np.all(q.value == value) + q2 = u.Quantity(mimic, u.cm) + assert q2.unit == u.cm + assert np.all(q2.value == 100 * value) + + @pytest.mark.parametrize("Mimic", (QuantityMimic, QuantityMimic2)) + def test_mimic_setting(self, Mimic): + mimic = Mimic([1.0, 2.0], u.m) + q = u.Quantity(np.arange(10.0), u.cm) + q[8:] = mimic + assert np.all(q[:8].value == np.arange(8.0)) + assert np.all(q[8:].value == [100.0, 200.0]) + + def test_mimic_function_unit(self): + mimic = QuantityMimic([1.0, 2.0], u.dex(u.cm / u.s**2)) + d = u.Dex(mimic) + assert isinstance(d, u.Dex) + assert d.unit == u.dex(u.cm / u.s**2) + assert np.all(d.value == [1.0, 2.0]) + q = u.Quantity(mimic, subok=True) + assert isinstance(q, u.Dex) + assert q.unit == u.dex(u.cm / u.s**2) + assert np.all(q.value == [1.0, 2.0]) + with pytest.raises(u.UnitTypeError): + u.Quantity(mimic) + + +def test_masked_quantity_str_repr(): + """Ensure we don't break masked Quantity representation.""" + # Really, masked quantities do not work well, but at least let the + # basics work. + masked_quantity = np.ma.array([1, 2, 3, 4] * u.kg, mask=[True, False, True, False]) + str(masked_quantity) + repr(masked_quantity) + + +class TestQuantitySubclassAboveAndBelow: + @classmethod + def setup_class(cls): + class MyArray(np.ndarray): + def __array_finalize__(self, obj): + super().__array_finalize__(obj) + if hasattr(obj, "my_attr"): + self.my_attr = obj.my_attr + + cls.MyArray = MyArray + cls.MyQuantity1 = type("MyQuantity1", (u.Quantity, MyArray), dict(my_attr="1")) + cls.MyQuantity2 = type("MyQuantity2", (MyArray, u.Quantity), dict(my_attr="2")) + + def test_setup(self): + mq1 = self.MyQuantity1(10, u.m) + assert isinstance(mq1, self.MyQuantity1) + assert mq1.my_attr == "1" + assert mq1.unit is u.m + mq2 = self.MyQuantity2(10, u.m) + assert isinstance(mq2, self.MyQuantity2) + assert mq2.my_attr == "2" + assert mq2.unit is u.m + + def test_attr_propagation(self): + mq1 = self.MyQuantity1(10, u.m) + mq12 = self.MyQuantity2(mq1) + assert isinstance(mq12, self.MyQuantity2) + assert not isinstance(mq12, self.MyQuantity1) + assert mq12.my_attr == "1" + assert mq12.unit is u.m + + mq2 = self.MyQuantity2(10, u.m) + mq21 = self.MyQuantity1(mq2) + assert isinstance(mq21, self.MyQuantity1) + assert not isinstance(mq21, self.MyQuantity2) + assert mq21.my_attr == "2" + assert mq21.unit is u.m diff --git a/astropy/units/tests/test_quantity_annotations.py b/astropy/units/tests/test_quantity_annotations.py new file mode 100644 index 000000000000..267cb1c956b1 --- /dev/null +++ b/astropy/units/tests/test_quantity_annotations.py @@ -0,0 +1,353 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +import pytest + +from astropy import units as u +from astropy.units import Quantity + + +def test_ignore_generic_type_annotations(): + """Test annotations that are not unit related are ignored. + + This test passes if the function works. + """ + + # one unit, one not (should be ignored) + @u.quantity_input + def func(x: u.m, y: str): + return x, y + + i_q, i_str = 2 * u.m, "cool string" + o_q, o_str = func(i_q, i_str) # if this doesn't fail, it worked. + assert i_q == o_q + assert i_str == o_str + + +class TestQuantityUnitAnnotations: + """Test Quantity[Unit] type annotation.""" + + def test_simple_annotation(self): + @u.quantity_input + def func(x: Quantity[u.m], y: str): + return x, y + + i_q, i_str = 2 * u.m, "cool string" + o_q, o_str = func(i_q, i_str) + assert i_q == o_q + assert i_str == o_str + + # checks the input on the 1st arg + with pytest.raises(u.UnitsError): + func(1 * u.s, i_str) + + # but not the second + o_q, o_str = func(i_q, {"not": "a string"}) + assert i_q == o_q + assert i_str != o_str + + def test_multiple_annotation(self): + @u.quantity_input + def multi_func(a: Quantity[u.km]) -> Quantity[u.m]: + return a + + i_q = 2 * u.km + o_q = multi_func(i_q) + assert o_q == i_q + assert o_q.unit == u.m + + def test_optional_and_annotated(self): + @u.quantity_input + def opt_func(x: Quantity[u.m] | None = None) -> Quantity[u.km]: + if x is None: + return 1 * u.km + return x + + i_q = 250 * u.m + o_q = opt_func(i_q) + assert o_q.unit == u.km + assert o_q == i_q + + i_q = None + o_q = opt_func(i_q) + assert o_q == 1 * u.km + + def test_union_and_annotated(self): + # Union and Annotated + @u.quantity_input + def union_func(x: Quantity[u.m] | (Quantity[u.s] | None)): + if x is None: + return None + else: + return 2 * x + + i_q = 1 * u.m + o_q = union_func(i_q) + assert o_q == 2 * i_q + + i_q = 1 * u.s + o_q = union_func(i_q) + assert o_q == 2 * i_q + + i_q = None + o_q = union_func(i_q) + assert o_q is None + + def test_not_unit_or_ptype(self): + with pytest.raises(TypeError, match="unit annotation is not"): + Quantity["definitely not a unit"] + + +@pytest.mark.parametrize( + "solarx_unit,solary_unit", [(u.arcsec, u.arcsec), ("angle", "angle")] +) +def test_args3(solarx_unit, solary_unit): + @u.quantity_input + def myfunc_args(solarx: solarx_unit, solary: solary_unit): + return solarx, solary + + solarx, solary = myfunc_args(1 * u.arcsec, 1 * u.arcsec) + + assert isinstance(solarx, Quantity) + assert isinstance(solary, Quantity) + + assert solarx.unit == u.arcsec + assert solary.unit == u.arcsec + + +@pytest.mark.parametrize( + "solarx_unit,solary_unit", [(u.arcsec, u.arcsec), ("angle", "angle")] +) +def test_args_noconvert3(solarx_unit, solary_unit): + @u.quantity_input() + def myfunc_args(solarx: solarx_unit, solary: solary_unit): + return solarx, solary + + solarx, solary = myfunc_args(1 * u.deg, 1 * u.arcmin) + + assert isinstance(solarx, Quantity) + assert isinstance(solary, Quantity) + + assert solarx.unit == u.deg + assert solary.unit == u.arcmin + + +@pytest.mark.parametrize("solarx_unit", [u.arcsec, "angle"]) +def test_args_nonquantity3(solarx_unit): + @u.quantity_input + def myfunc_args(solarx: solarx_unit, solary): + return solarx, solary + + solarx, solary = myfunc_args(1 * u.arcsec, 100) + + assert isinstance(solarx, Quantity) + assert isinstance(solary, int) + + assert solarx.unit == u.arcsec + + +@pytest.mark.parametrize( + "solarx_unit,solary_unit", [(u.arcsec, u.eV), ("angle", "energy")] +) +def test_arg_equivalencies3(solarx_unit, solary_unit): + @u.quantity_input(equivalencies=u.mass_energy()) + def myfunc_args(solarx: solarx_unit, solary: solary_unit): + return solarx, solary + (10 * u.J) # Add an energy to check equiv is working + + solarx, solary = myfunc_args(1 * u.arcsec, 100 * u.gram) + + assert isinstance(solarx, Quantity) + assert isinstance(solary, Quantity) + + assert solarx.unit == u.arcsec + assert solary.unit == u.gram + + +@pytest.mark.parametrize( + "solarx_unit,solary_unit", [(u.arcsec, u.deg), ("angle", "angle")] +) +def test_wrong_unit3(solarx_unit, solary_unit): + @u.quantity_input + def myfunc_args(solarx: solarx_unit, solary: solary_unit): + return solarx, solary + + with pytest.raises( + u.UnitsError, + match=( + "Argument 'solary' to function 'myfunc_args' must be in units " + f"convertible to '{str(solary_unit)}'." + ), + ): + solarx, solary = myfunc_args(1 * u.arcsec, 100 * u.km) + + +@pytest.mark.parametrize( + "solarx_unit,solary_unit", [(u.arcsec, u.deg), ("angle", "angle")] +) +def test_not_quantity3(solarx_unit, solary_unit): + @u.quantity_input + def myfunc_args(solarx: solarx_unit, solary: solary_unit): + return solarx, solary + + with pytest.raises( + TypeError, + match=( + "Argument 'solary' to function 'myfunc_args' has no 'unit' " + "attribute. You should pass in an astropy Quantity instead." + ), + ): + solarx, solary = myfunc_args(1 * u.arcsec, 100) + + +def test_decorator_override(): + @u.quantity_input(solarx=u.arcsec) + def myfunc_args(solarx: u.km, solary: u.arcsec): + return solarx, solary + + solarx, solary = myfunc_args(1 * u.arcsec, 1 * u.arcsec) + + assert isinstance(solarx, Quantity) + assert isinstance(solary, Quantity) + + assert solarx.unit == u.arcsec + assert solary.unit == u.arcsec + + +@pytest.mark.parametrize( + "solarx_unit,solary_unit", [(u.arcsec, u.deg), ("angle", "angle")] +) +def test_kwargs3(solarx_unit, solary_unit): + @u.quantity_input + def myfunc_args(solarx: solarx_unit, solary, myk: solary_unit = 1 * u.arcsec): + return solarx, solary, myk + + solarx, solary, myk = myfunc_args(1 * u.arcsec, 100, myk=100 * u.deg) + + assert isinstance(solarx, Quantity) + assert isinstance(solary, int) + assert isinstance(myk, Quantity) + + assert myk.unit == u.deg + + +@pytest.mark.parametrize( + "solarx_unit,solary_unit", [(u.arcsec, u.deg), ("angle", "angle")] +) +def test_unused_kwargs3(solarx_unit, solary_unit): + @u.quantity_input + def myfunc_args( + solarx: solarx_unit, solary, myk: solary_unit = 1 * u.arcsec, myk2=1000 + ): + return solarx, solary, myk, myk2 + + solarx, solary, myk, myk2 = myfunc_args(1 * u.arcsec, 100, myk=100 * u.deg, myk2=10) + + assert isinstance(solarx, Quantity) + assert isinstance(solary, int) + assert isinstance(myk, Quantity) + assert isinstance(myk2, int) + + assert myk.unit == u.deg + assert myk2 == 10 + + +@pytest.mark.parametrize("solarx_unit,energy", [(u.arcsec, u.eV), ("angle", "energy")]) +def test_kwarg_equivalencies3(solarx_unit, energy): + @u.quantity_input(equivalencies=u.mass_energy()) + def myfunc_args(solarx: solarx_unit, energy: energy = 10 * u.eV): + return solarx, energy + (10 * u.J) # Add an energy to check equiv is working + + solarx, energy = myfunc_args(1 * u.arcsec, 100 * u.gram) + + assert isinstance(solarx, Quantity) + assert isinstance(energy, Quantity) + + assert solarx.unit == u.arcsec + assert energy.unit == u.gram + + +@pytest.mark.parametrize( + "solarx_unit,solary_unit", [(u.arcsec, u.deg), ("angle", "angle")] +) +def test_kwarg_wrong_unit3(solarx_unit, solary_unit): + @u.quantity_input + def myfunc_args(solarx: solarx_unit, solary: solary_unit = 10 * u.deg): + return solarx, solary + + with pytest.raises( + u.UnitsError, + match=( + "Argument 'solary' to function 'myfunc_args' must be in " + f"units convertible to '{str(solary_unit)}'." + ), + ): + solarx, solary = myfunc_args(1 * u.arcsec, solary=100 * u.km) + + +@pytest.mark.parametrize( + "solarx_unit,solary_unit", [(u.arcsec, u.deg), ("angle", "angle")] +) +def test_kwarg_not_quantity3(solarx_unit, solary_unit): + @u.quantity_input + def myfunc_args(solarx: solarx_unit, solary: solary_unit = 10 * u.deg): + return solarx, solary + + with pytest.raises( + TypeError, + match=( + "Argument 'solary' to function 'myfunc_args' has no 'unit' attribute. " + "You should pass in an astropy Quantity instead." + ), + ): + solarx, solary = myfunc_args(1 * u.arcsec, solary=100) + + +@pytest.mark.parametrize( + "solarx_unit,solary_unit", [(u.arcsec, u.deg), ("angle", "angle")] +) +def test_kwarg_default3(solarx_unit, solary_unit): + @u.quantity_input + def myfunc_args(solarx: solarx_unit, solary: solary_unit = 10 * u.deg): + return solarx, solary + + solarx, solary = myfunc_args(1 * u.arcsec) + + +def test_return_annotation(): + @u.quantity_input + def myfunc_args(solarx: u.arcsec) -> u.deg: + return solarx + + solarx = myfunc_args(1 * u.arcsec) + assert solarx.unit is u.deg + + +def test_return_annotation_none(): + @u.quantity_input + def myfunc_args(solarx: u.arcsec) -> None: + pass + + solarx = myfunc_args(1 * u.arcsec) + assert solarx is None + + +def test_return_annotation_notUnit(): + @u.quantity_input + def myfunc_args(solarx: u.arcsec) -> int: + return 0 + + solarx = myfunc_args(1 * u.arcsec) + assert solarx == 0 + + +def test_enum_annotation(): + # Regression test for gh-9932 + from enum import Enum, auto + + class BasicEnum(Enum): + AnOption = auto() + + @u.quantity_input + def myfunc_args(a: BasicEnum, b: u.arcsec) -> None: + pass + + myfunc_args(BasicEnum.AnOption, 1 * u.arcsec) diff --git a/astropy/units/tests/test_quantity_array_methods.py b/astropy/units/tests/test_quantity_array_methods.py new file mode 100644 index 000000000000..a7be14291ff5 --- /dev/null +++ b/astropy/units/tests/test_quantity_array_methods.py @@ -0,0 +1,608 @@ +# The purpose of these tests are to ensure that calling quantities using +# array methods returns quantities with the right units, or raises exceptions. + +import numpy as np +import pytest +from numpy.testing import assert_array_equal + +from astropy import units as u +from astropy.utils.compat.optional_deps import HAS_ARRAY_API_STRICT + + +class TestQuantityArrayCopy: + """ + Test whether arrays are properly copied/used in place + """ + + def test_copy_on_creation(self): + v = np.arange(1000.0) + q_nocopy = u.Quantity(v, "km/s", copy=False) + q_copy = u.Quantity(v, "km/s", copy=True) + v[0] = -1.0 + assert q_nocopy[0].value == v[0] + assert q_copy[0].value != v[0] + + def test_to_copies(self): + q = u.Quantity(np.arange(1.0, 100.0), "km/s") + q2 = q.to(u.m / u.s) + assert np.all(q.value != q2.value) + q3 = q.to(u.km / u.s) + assert np.all(q.value == q3.value) + q[0] = -1.0 * u.km / u.s + assert q[0].value != q3[0].value + + def test_si_copies(self): + q = u.Quantity(np.arange(100.0), "m/s") + q2 = q.si + assert np.all(q.value == q2.value) + q[0] = -1.0 * u.m / u.s + assert q[0].value != q2[0].value + + def test_getitem_is_view(self): + """Check that [keys] work, and that, like ndarray, it returns + a view, so that changing one changes the other. + + Also test that one can add axes (closes #1422) + """ + q = u.Quantity(np.arange(100.0), "m/s") + q_sel = q[10:20] + q_sel[0] = -1.0 * u.m / u.s + assert q_sel[0] == q[10] + # also check that getitem can do new axes + q2 = q[:, np.newaxis] + q2[10, 0] = -9 * u.m / u.s + assert np.all(q2.flatten() == q) + + def test_flat(self): + q = u.Quantity(np.arange(9.0).reshape(3, 3), "m/s") + q_flat = q.flat + # check that a single item is a quantity (with the right value) + assert q_flat[8] == 8.0 * u.m / u.s + # and that getting a range works as well + assert np.all(q_flat[0:2] == np.arange(2.0) * u.m / u.s) + # as well as getting items via iteration + q_flat_list = list(q.flat) + assert np.all(u.Quantity(q_flat_list) == u.Quantity(list(q.value.flat), q.unit)) + # check that flat works like a view of the real array + q_flat[8] = -1.0 * u.km / u.s + assert q_flat[8] == -1.0 * u.km / u.s + assert q[2, 2] == -1.0 * u.km / u.s + # while if one goes by an iterated item, a copy is made + q_flat_list[8] = -2 * u.km / u.s + assert q_flat_list[8] == -2.0 * u.km / u.s + assert q_flat[8] == -1.0 * u.km / u.s + assert q[2, 2] == -1.0 * u.km / u.s + + +class TestQuantityReshapeFuncs: + """Test different ndarray methods that alter the array shape + + tests: reshape, squeeze, ravel, flatten, transpose, swapaxes + """ + + def test_reshape(self): + q = np.arange(6.0) * u.m + q_reshape = q.reshape(3, 2) + assert isinstance(q_reshape, u.Quantity) + assert q_reshape.unit == q.unit + assert np.all(q_reshape.value == q.value.reshape(3, 2)) + + def test_squeeze(self): + q = np.arange(6.0).reshape(6, 1) * u.m + q_squeeze = q.squeeze() + assert isinstance(q_squeeze, u.Quantity) + assert q_squeeze.unit == q.unit + assert np.all(q_squeeze.value == q.value.squeeze()) + + def test_ravel(self): + q = np.arange(6.0).reshape(3, 2) * u.m + q_ravel = q.ravel() + assert isinstance(q_ravel, u.Quantity) + assert q_ravel.unit == q.unit + assert np.all(q_ravel.value == q.value.ravel()) + + def test_flatten(self): + q = np.arange(6.0).reshape(3, 2) * u.m + q_flatten = q.flatten() + assert isinstance(q_flatten, u.Quantity) + assert q_flatten.unit == q.unit + assert np.all(q_flatten.value == q.value.flatten()) + + def test_transpose(self): + q = np.arange(6.0).reshape(3, 2) * u.m + q_transpose = q.transpose() + assert isinstance(q_transpose, u.Quantity) + assert q_transpose.unit == q.unit + assert np.all(q_transpose.value == q.value.transpose()) + + def test_swapaxes(self): + q = np.arange(6.0).reshape(3, 1, 2) * u.m + q_swapaxes = q.swapaxes(0, 2) + assert isinstance(q_swapaxes, u.Quantity) + assert q_swapaxes.unit == q.unit + assert np.all(q_swapaxes.value == q.value.swapaxes(0, 2)) + + def test_flat_attributes(self): + """While ``flat`` doesn't make a copy, it changes the shape.""" + q = np.arange(6.0).reshape(3, 1, 2) * u.m + qf = q.flat + # flat shape is same as before reshaping + assert len(qf) == 6 + # see TestQuantityArrayCopy.test_flat for tests of iteration + # and slicing and setting. Here we test the properties and methods to + # match `numpy.ndarray.flatiter` + assert qf.base is q + # testing the indices -- flat and full -- into the array + assert qf.coords == (0, 0, 0) # to start + assert qf.index == 0 + # now consume the iterator + endindices = [(qf.index, qf.coords) for x in qf][-2] # next() oversteps + assert endindices[0] == 5 + assert endindices[1] == (2, 0, 1) # shape of q - 1 + + # also check q_flat copies properly + q_flat_copy = qf.copy() + assert all(q_flat_copy == q.flatten()) + assert isinstance(q_flat_copy, u.Quantity) + assert not np.may_share_memory(q_flat_copy, q) + + +class TestQuantityStatsFuncs: + """ + Test statistical functions + """ + + def test_mean(self): + q1 = np.array([1.0, 2.0, 4.0, 5.0, 6.0]) * u.m + assert_array_equal(np.mean(q1), 3.6 * u.m) + assert_array_equal(np.mean(q1, keepdims=True), [3.6] * u.m) + + def test_mean_inplace(self): + q1 = np.array([1.0, 2.0, 4.0, 5.0, 6.0]) * u.m + qi = 1.5 * u.s + qi2 = np.mean(q1, out=qi) + assert qi2 is qi + assert qi == 3.6 * u.m + + def test_mean_where(self): + q1 = np.array([1.0, 2.0, 4.0, 5.0, 6.0, 7.0]) * u.m + assert_array_equal(np.mean(q1, where=q1 < 7 * u.m), 3.6 * u.m) + + def test_std(self): + q1 = np.array([1.0, 2.0]) * u.m + assert_array_equal(np.std(q1), 0.5 * u.m) + assert_array_equal(q1.std(axis=-1, keepdims=True), [0.5] * u.m) + + def test_std_inplace(self): + q1 = np.array([1.0, 2.0]) * u.m + qi = 1.5 * u.s + np.std(q1, out=qi) + assert qi == 0.5 * u.m + + def test_std_where(self): + q1 = np.array([1.0, 2.0, 3.0]) * u.m + assert_array_equal(np.std(q1, where=q1 < 3 * u.m), 0.5 * u.m) + + def test_var(self): + q1 = np.array([1.0, 2.0]) * u.m + assert_array_equal(np.var(q1), 0.25 * u.m**2) + assert_array_equal(q1.var(axis=0, keepdims=True), [0.25] * u.m**2) + + def test_var_inplace(self): + q1 = np.array([1.0, 2.0]) * u.m + qi = 1.5 * u.s + np.var(q1, out=qi) + assert qi == 0.25 * u.m**2 + + def test_var_where(self): + q1 = np.array([1.0, 2.0, 3.0]) * u.m + assert_array_equal(np.var(q1, where=q1 < 3 * u.m), 0.25 * u.m**2) + + def test_median(self): + q1 = np.array([1.0, 2.0, 4.0, 5.0, 6.0]) * u.m + assert np.median(q1) == 4.0 * u.m + + def test_median_inplace(self): + q1 = np.array([1.0, 2.0, 4.0, 5.0, 6.0]) * u.m + qi = 1.5 * u.s + np.median(q1, out=qi) + assert qi == 4 * u.m + + def test_min(self): + q1 = np.array([1.0, 2.0, 4.0, 5.0, 6.0]) * u.m + assert np.min(q1) == 1.0 * u.m + + def test_min_inplace(self): + q1 = np.array([1.0, 2.0, 4.0, 5.0, 6.0]) * u.m + qi = 1.5 * u.s + np.min(q1, out=qi) + assert qi == 1.0 * u.m + + def test_min_where(self): + q1 = np.array([0.0, 1.0, 2.0, 4.0, 5.0, 6.0]) * u.m + assert np.min(q1, initial=10 * u.m, where=q1 > 0 * u.m) == 1.0 * u.m + + def test_argmin(self): + q1 = np.array([6.0, 2.0, 4.0, 5.0, 6.0]) * u.m + assert np.argmin(q1) == 1 + + def test_argmin_keepdims(self): + q1 = np.array([[6.0, 2.0], [4.0, 5.0]]) * u.m + assert_array_equal(q1.argmin(axis=0, keepdims=True), np.array([[1, 0]])) + + def test_max(self): + q1 = np.array([1.0, 2.0, 4.0, 5.0, 6.0]) * u.m + assert np.max(q1) == 6.0 * u.m + + def test_max_inplace(self): + q1 = np.array([1.0, 2.0, 4.0, 5.0, 6.0]) * u.m + qi = 1.5 * u.s + np.max(q1, out=qi) + assert qi == 6.0 * u.m + + def test_max_where(self): + q1 = np.array([1.0, 2.0, 4.0, 5.0, 6.0, 7.0]) * u.m + assert np.max(q1, initial=0 * u.m, where=q1 < 7 * u.m) == 6.0 * u.m + + def test_argmax(self): + q1 = np.array([5.0, 2.0, 4.0, 5.0, 6.0]) * u.m + assert np.argmax(q1) == 4 + + def test_argmax_keepdims(self): + q1 = np.array([[6.0, 2.0], [4.0, 5.0]]) * u.m + assert_array_equal(q1.argmax(axis=0, keepdims=True), np.array([[0, 1]])) + + def test_clip(self): + q1 = np.array([1.0, 2.0, 4.0, 5.0, 6.0]) * u.km / u.m + c1 = q1.clip(1500, 5.5 * u.Mm / u.km) + assert np.all(c1 == np.array([1.5, 2.0, 4.0, 5.0, 5.5]) * u.km / u.m) + + def test_clip_inplace(self): + q1 = np.array([1.0, 2.0, 4.0, 5.0, 6.0]) * u.km / u.m + c1 = q1.clip(1500, 5.5 * u.Mm / u.km, out=q1) + assert np.all(q1 == np.array([1.5, 2.0, 4.0, 5.0, 5.5]) * u.km / u.m) + c1[0] = 10 * u.Mm / u.mm + assert np.all(c1.value == q1.value) + + def test_conj(self): + q1 = np.array([1.0, 2.0, 4.0, 5.0, 6.0]) * u.km / u.m + assert np.all(q1.conj() == q1) + + def test_ptp(self): + q1 = np.array([1.0, 2.0, 4.0, 5.0, 6.0]) * u.m + assert np.ptp(q1) == 5.0 * u.m + + def test_ptp_inplace(self): + q1 = np.array([1.0, 2.0, 4.0, 5.0, 6.0]) * u.m + qi = 1.5 * u.s + np.ptp(q1, out=qi) + assert qi == 5.0 * u.m + + def test_round(self): + q1 = np.array([1.253, 2.253, 3.253]) * u.kg + assert np.all(np.round(q1) == np.array([1, 2, 3]) * u.kg) + assert np.all(np.round(q1, decimals=2) == np.round(q1.value, decimals=2) * u.kg) + assert np.all(q1.round(decimals=2) == q1.value.round(decimals=2) * u.kg) + + def test_round_inplace(self): + q1 = np.array([1.253, 2.253, 3.253]) * u.kg + qi = np.zeros(3) * u.s + a = q1.round(decimals=2, out=qi) + assert a is qi + assert np.all(q1.round(decimals=2) == qi) + + def test_sum(self): + q1 = np.array([1.0, 2.0, 6.0]) * u.m + assert np.all(q1.sum() == 9.0 * u.m) + assert np.all(np.sum(q1) == 9.0 * u.m) + + q2 = np.array([[4.0, 5.0, 9.0], [1.0, 1.0, 1.0]]) * u.s + assert np.all(q2.sum(0) == np.array([5.0, 6.0, 10.0]) * u.s) + assert np.all(np.sum(q2, 0) == np.array([5.0, 6.0, 10.0]) * u.s) + + def test_sum_inplace(self): + q1 = np.array([1.0, 2.0, 6.0]) * u.m + qi = 1.5 * u.s + np.sum(q1, out=qi) + assert qi == 9.0 * u.m + + def test_sum_where(self): + q1 = np.array([1.0, 2.0, 6.0, 7.0]) * u.m + where = q1 < 7 * u.m + assert np.all(q1.sum(where=where) == 9.0 * u.m) + assert np.all(np.sum(q1, where=where) == 9.0 * u.m) + + @pytest.mark.parametrize("initial", [0, 0 * u.m, 1 * u.km]) + def test_sum_initial(self, initial): + q1 = np.array([1.0, 2.0, 6.0, 7.0]) * u.m + expected = 16 * u.m + initial + assert q1.sum(initial=initial) == expected + assert np.sum(q1, initial=initial) == expected + + def test_sum_dimensionless_initial(self): + q1 = np.array([1.0, 2.0, 6.0, 7.0]) * u.one + assert q1.sum(initial=1000) == 1016 * u.one + + @pytest.mark.parametrize("initial", [10, 1 * u.s]) + def test_sum_initial_exception(self, initial): + q1 = np.array([1.0, 2.0, 6.0, 7.0]) * u.m + with pytest.raises(u.UnitsError): + q1.sum(initial=initial) + + def test_cumsum(self): + q1 = np.array([1, 2, 6]) * u.m + assert np.all(q1.cumsum() == np.array([1, 3, 9]) * u.m) + assert np.all(np.cumsum(q1) == np.array([1, 3, 9]) * u.m) + + q2 = np.array([4, 5, 9]) * u.s + assert np.all(q2.cumsum() == np.array([4, 9, 18]) * u.s) + assert np.all(np.cumsum(q2) == np.array([4, 9, 18]) * u.s) + + def test_cumsum_inplace(self): + q1 = np.array([1, 2, 6]) * u.m + qi = np.ones(3) * u.s + np.cumsum(q1, out=qi) + assert np.all(qi == np.array([1, 3, 9]) * u.m) + q2 = q1 + q1.cumsum(out=q1) + assert np.all(q2 == qi) + + def test_prod(self): + q1 = np.array([1, 2, 6]) * u.m + with pytest.raises(u.UnitsError) as exc: + q1.prod() + with pytest.raises(u.UnitsError) as exc: + np.prod(q1) + + q2 = np.array([3.0, 4.0, 5.0]) * u.Unit(1) + assert q2.prod() == 60.0 * u.Unit(1) + assert np.prod(q2) == 60.0 * u.Unit(1) + + def test_cumprod(self): + q1 = np.array([1, 2, 6]) * u.m + with pytest.raises(u.UnitsError) as exc: + q1.cumprod() + with pytest.raises(u.UnitsError) as exc: + np.cumprod(q1) + + q2 = np.array([3, 4, 5]) * u.Unit(1) + assert np.all(q2.cumprod() == np.array([3, 12, 60]) * u.Unit(1)) + assert np.all(np.cumprod(q2) == np.array([3, 12, 60]) * u.Unit(1)) + + def test_diff(self): + q1 = np.array([1.0, 2.0, 4.0, 10.0]) * u.m + assert np.all(q1.diff() == np.array([1.0, 2.0, 6.0]) * u.m) + assert np.all(np.diff(q1) == np.array([1.0, 2.0, 6.0]) * u.m) + + def test_ediff1d(self): + q1 = np.array([1.0, 2.0, 4.0, 10.0]) * u.m + assert np.all(q1.ediff1d() == np.array([1.0, 2.0, 6.0]) * u.m) + assert np.all(np.ediff1d(q1) == np.array([1.0, 2.0, 6.0]) * u.m) + + def test_dot_meth(self): + q1 = np.array([1.0, 2.0, 4.0, 10.0]) * u.m + q2 = np.array([3.0, 4.0, 5.0, 6.0]) * u.s + q3 = q1.dot(q2) + assert q3.value == np.dot(q1.value, q2.value) + assert q3.unit == u.m * u.s + + def test_trace_func(self): + q = np.array([[1.0, 2.0], [3.0, 4.0]]) * u.m + assert np.trace(q) == 5.0 * u.m + + def test_trace_meth(self): + q1 = np.array([[1.0, 2.0], [3.0, 4.0]]) * u.m + assert q1.trace() == 5.0 * u.m + + cont = u.Quantity(4.0, u.s) + + q2 = np.array([[3.0, 4.0], [5.0, 6.0]]) * u.m + q2.trace(out=cont) + assert cont == 9.0 * u.m + + def test_clip_func(self): + q = np.arange(10) * u.m + assert np.all( + np.clip(q, 3 * u.m, 6 * u.m) + == np.array([3.0, 3.0, 3.0, 3.0, 4.0, 5.0, 6.0, 6.0, 6.0, 6.0]) * u.m + ) + + def test_clip_meth(self): + expected = np.array([3.0, 3.0, 3.0, 3.0, 4.0, 5.0, 6.0, 6.0, 6.0, 6.0]) * u.m + + q1 = np.arange(10) * u.m + q3 = q1.clip(3 * u.m, 6 * u.m) + assert np.all(q1.clip(3 * u.m, 6 * u.m) == expected) + + cont = np.zeros(10) * u.s + + q1.clip(3 * u.m, 6 * u.m, out=cont) + + assert np.all(cont == expected) + + +class TestArrayConversion: + """ + Test array conversion methods + """ + + def test_item(self): + q1 = u.Quantity(np.array([1, 2, 3]), u.m / u.km, dtype=int) + assert q1.item(1) == 2 * q1.unit + + q1[1] = 1 + assert q1[1] == 1000 * u.m / u.km + q1[1] = 100 * u.cm / u.km + assert q1[1] == 1 * u.m / u.km + with pytest.raises(TypeError): + q1[1] = 1.5 * u.m / u.km + + def test_take_put(self): + q1 = np.array([1, 2, 3]) * u.m / u.km + assert q1.take(1) == 2 * u.m / u.km + assert all(q1.take((0, 2)) == np.array([1, 3]) * u.m / u.km) + q1.put((1, 2), (3, 4)) + assert np.all(q1.take((1, 2)) == np.array([3000, 4000]) * q1.unit) + q1.put(0, 500 * u.cm / u.km) + assert q1.item(0) == 5 * u.m / u.km + + def test_slice(self): + """Test that setitem changes the unit if needed (or ignores it for + values where that is allowed; viz., #2695)""" + q2 = np.array([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]) * u.km / u.m + q1 = q2.copy() + q2[0, 0] = 10000.0 + assert q2.unit == q1.unit + assert q2[0, 0].value == 10.0 + q2[0] = 9.0 * u.Mm / u.km + assert all(q2.flatten()[:3].value == np.array([9.0, 9.0, 9.0])) + q2[0, :-1] = 8000.0 + assert all(q2.flatten()[:3].value == np.array([8.0, 8.0, 9.0])) + with pytest.raises(u.UnitsError): + q2[1, 1] = 10 * u.s + # just to be sure, repeat with a dimensionfull unit + q3 = u.Quantity(np.arange(10.0), "m/s") + q3[5] = 100.0 * u.cm / u.s + assert q3[5].value == 1.0 + # and check unit is ignored for 0, inf, nan, where that is reasonable + q3[5] = 0.0 + assert q3[5] == 0.0 + q3[5] = np.inf + assert np.isinf(q3[5]) + q3[5] = np.nan + assert np.isnan(q3[5]) + + def test_fill(self): + q1 = np.array([1, 2, 3]) * u.m / u.km + q1.fill(2) + assert np.all(q1 == 2000 * u.m / u.km) + + def test_repeat_compress_diagonal(self): + q1 = np.array([1, 2, 3]) * u.m / u.km + q2 = q1.repeat(2) + assert q2.unit == q1.unit + assert all(q2.value == q1.value.repeat(2)) + q2.sort() + assert q2.unit == q1.unit + q2 = q1.compress(np.array([True, True, False, False])) + assert q2.unit == q1.unit + assert all(q2.value == q1.value.compress(np.array([True, True, False, False]))) + q1 = np.array([[1, 2], [3, 4]]) * u.m / u.km + q2 = q1.diagonal() + assert q2.unit == q1.unit + assert all(q2.value == q1.value.diagonal()) + + def test_view(self): + q1 = np.array([1, 2, 3], dtype=np.int64) * u.m / u.km + q2 = q1.view(np.ndarray) + assert not hasattr(q2, "unit") + q3 = q2.view(u.Quantity) + assert q3._unit is None + # MaskedArray copies and properties assigned in __dict__ + q4 = np.ma.MaskedArray(q1) + assert q4._unit is q1._unit + q5 = q4.view(u.Quantity) + assert q5.unit is q1.unit + + def test_slice_to_quantity(self): + """ + Regression test for https://github.com/astropy/astropy/issues/2003 + """ + + a = np.random.uniform(size=(10, 8)) + x, y, z = a[:, 1:4].T * u.km / u.s + total = np.sum(a[:, 1] * u.km / u.s - x) + + assert isinstance(total, u.Quantity) + assert total == (0.0 * u.km / u.s) + + def test_byte_type_view_field_changes(self): + q1 = np.array([1, 2, 3], dtype=np.int64) * u.m / u.km + q2 = q1.byteswap() + assert q2.unit == q1.unit + assert all(q2.value == q1.value.byteswap()) + q2 = q1.astype(np.float64) + assert all(q2 == q1) + assert q2.dtype == np.float64 + q2a = q1.getfield(np.int32, offset=0) + q2b = q1.byteswap().getfield(np.int32, offset=4) + assert q2a.unit == q1.unit + assert all(q2b.byteswap() == q2a) + + def test_sort(self): + q1 = np.array([1.0, 5.0, 2.0, 4.0]) * u.km / u.m + i = q1.argsort() + assert not hasattr(i, "unit") + q1.sort() + i = q1.searchsorted([1500, 2500]) + assert not hasattr(i, "unit") + assert all( + i == q1.to(u.dimensionless_unscaled).value.searchsorted([1500, 2500]) + ) + + def test_not_implemented(self): + q1 = np.array([1, 2, 3]) * u.m / u.km + + with pytest.raises(NotImplementedError): + q1.choose([0, 0, 1]) + + with pytest.raises(NotImplementedError): + q1.tolist() + with pytest.raises(NotImplementedError): + q1.tostring() + with pytest.raises(NotImplementedError): + q1.tobytes() + with pytest.raises(NotImplementedError): + q1.tofile(0) + with pytest.raises(NotImplementedError): + q1.dump("a.a") + with pytest.raises(NotImplementedError): + q1.dumps() + + +class TestStructuredArray: + """Structured arrays are not specifically supported, but we should not + prevent their use unnecessarily. + + Note that these tests use simple units. Now that structured units are + supported, it may make sense to deprecate this. + """ + + def setup_method(self): + self.ra = ( + np.array(np.arange(12.0).reshape(4, 3)).view(dtype="f8,f8,f8").squeeze() + ) + + def test_creation(self): + qra = u.Quantity(self.ra, u.m) + assert np.all(qra[:2].value == self.ra[:2]) + + def test_equality(self): + qra = u.Quantity(self.ra, u.m) + qra[1] = qra[2] + assert qra[1] == qra[2] + + def test_assignment_with_non_structured(self): + qra = u.Quantity(self.ra, u.m) + qra[1] = 0 + assert qra[1] == np.zeros(3).view(qra.dtype) + + def test_assignment_with_different_names(self): + qra = u.Quantity(self.ra, u.m) + dtype = np.dtype([("x", "f8"), ("y", "f8"), ("z", "f8")]) + value = np.array((-1.0, -2.0, -3.0), dtype) << u.km + qra[1] = value + assert qra[1] == value + assert qra[1].value == np.array((-1000.0, -2000.0, -3000.0), qra.dtype) + # Ensure we do not override dtype names of value. + assert value.dtype.names == ("x", "y", "z") + + +@pytest.mark.skipif(not HAS_ARRAY_API_STRICT, reason="requires array_api_strict") +def test_array_api_init(): + import array_api_strict as xp + + array = xp.asarray([1, 2, 3]) + quantity_array = u.Quantity(array, u.m) + assert type(quantity_array) is u.Quantity + assert_array_equal(quantity_array, [1, 2, 3] * u.m) diff --git a/astropy/units/tests/test_quantity_decorator.py b/astropy/units/tests/test_quantity_decorator.py new file mode 100644 index 000000000000..8eb4ade0c7d8 --- /dev/null +++ b/astropy/units/tests/test_quantity_decorator.py @@ -0,0 +1,489 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +# STDLIB +import typing + +# THIRD PARTY +import numpy as np +import pytest + +# LOCAL +from astropy import units as u + +# list of pairs (target unit/physical type, input unit) +x_inputs = [ + (u.arcsec, u.deg), + ("angle", u.deg), + (u.kpc / u.Myr, u.km / u.s), + ("speed", u.km / u.s), + ([u.arcsec, u.km], u.deg), + ([u.arcsec, u.km], u.km), # multiple allowed + (["angle", "length"], u.deg), + (["angle", "length"], u.km), +] + +y_inputs = [ + (u.m, u.km), + (u.km, u.m), + (u.arcsec, u.deg), + ("angle", u.deg), + (u.kpc / u.Myr, u.km / u.s), + ("speed", u.km / u.s), +] + + +@pytest.fixture(scope="module", params=list(range(len(x_inputs)))) +def x_input(request): + return x_inputs[request.param] + + +@pytest.fixture(scope="module", params=list(range(len(y_inputs)))) +def y_input(request): + return y_inputs[request.param] + + +# ---- Tests that use the fixtures defined above ---- + + +def test_args(x_input, y_input): + x_target, x_unit = x_input + y_target, y_unit = y_input + + @u.quantity_input(x=x_target, y=y_target) + def myfunc_args(x, y): + return x, y + + x, y = myfunc_args(1 * x_unit, 1 * y_unit) + + assert isinstance(x, u.Quantity) + assert isinstance(y, u.Quantity) + + assert x.unit == x_unit + assert y.unit == y_unit + + +def test_args_nonquantity(x_input): + x_target, x_unit = x_input + + @u.quantity_input(x=x_target) + def myfunc_args(x, y): + return x, y + + x, y = myfunc_args(1 * x_unit, 100) + + assert isinstance(x, u.Quantity) + assert isinstance(y, int) + + assert x.unit == x_unit + + +def test_wrong_unit(x_input, y_input): + x_target, x_unit = x_input + y_target, y_unit = y_input + + @u.quantity_input(x=x_target, y=y_target) + def myfunc_args(x, y): + return x, y + + with pytest.raises( + u.UnitsError, + match=( + "Argument 'y' to function 'myfunc_args' must be in units " + f"convertible to '{str(y_target)}'." + ), + ): + x, y = myfunc_args(1 * x_unit, 100 * u.Joule) # has to be an unspecified unit + + +def test_wrong_unit_annotated(x_input, y_input): + x_target, x_unit = x_input + y_target, y_unit = y_input + + @u.quantity_input + def myfunc_args(x: x_target, y: y_target): + return x, y + + with pytest.raises(u.UnitsError, match="Argument 'y' to function 'myfunc_args'"): + x, y = myfunc_args(1 * x_unit, 100 * u.Joule) # has to be an unspecified unit + + +def test_not_quantity(x_input, y_input): + x_target, x_unit = x_input + y_target, y_unit = y_input + + @u.quantity_input(x=x_target, y=y_target) + def myfunc_args(x, y): + return x, y + + with pytest.raises( + TypeError, + match=( + "Argument 'y' to function 'myfunc_args' has no 'unit' attribute. " + "You should pass in an astropy Quantity instead." + ), + ): + x, y = myfunc_args(1 * x_unit, 100) + + +def test_not_quantity_annotated(x_input, y_input): + x_target, x_unit = x_input + y_target, y_unit = y_input + + @u.quantity_input + def myfunc_args(x: x_target, y: y_target): + return x, y + + with pytest.raises( + TypeError, + match=( + "Argument 'y' to function 'myfunc_args' has no 'unit' attribute. " + "You should pass in an astropy Quantity instead." + ), + ): + x, y = myfunc_args(1 * x_unit, 100) + + +def test_kwargs(x_input, y_input): + x_target, x_unit = x_input + y_target, y_unit = y_input + + @u.quantity_input(x=x_target, y=y_target) + def myfunc_args(x, my_arg, y=1 * y_unit): + return x, my_arg, y + + x, my_arg, y = myfunc_args(1 * x_unit, 100, y=100 * y_unit) + + assert isinstance(x, u.Quantity) + assert isinstance(my_arg, int) + assert isinstance(y, u.Quantity) + + assert y.unit == y_unit + + +def test_unused_kwargs(x_input, y_input): + x_target, x_unit = x_input + y_target, y_unit = y_input + + @u.quantity_input(x=x_target, y=y_target) + def myfunc_args(x, my_arg1, y=y_unit, my_arg2=1000): + return x, my_arg1, y, my_arg2 + + x, my_arg1, y, my_arg2 = myfunc_args(1 * x_unit, 100, y=100 * y_unit, my_arg2=10) + + assert isinstance(x, u.Quantity) + assert isinstance(my_arg1, int) + assert isinstance(y, u.Quantity) + assert isinstance(my_arg2, int) + + assert y.unit == y_unit + assert my_arg2 == 10 + + +def test_kwarg_wrong_unit(x_input, y_input): + x_target, x_unit = x_input + y_target, y_unit = y_input + + @u.quantity_input(x=x_target, y=y_target) + def myfunc_args(x, y=10 * y_unit): + return x, y + + with pytest.raises( + u.UnitsError, + match=( + "Argument 'y' to function 'myfunc_args' must be in units " + f"convertible to '{str(y_target)}'." + ), + ): + x, y = myfunc_args(1 * x_unit, y=100 * u.Joule) + + +def test_kwarg_not_quantity(x_input, y_input): + x_target, x_unit = x_input + y_target, y_unit = y_input + + @u.quantity_input(x=x_target, y=y_target) + def myfunc_args(x, y=10 * y_unit): + return x, y + + with pytest.raises( + TypeError, + match=( + "Argument 'y' to function 'myfunc_args' has no 'unit' attribute. " + "You should pass in an astropy Quantity instead." + ), + ): + x, y = myfunc_args(1 * x_unit, y=100) + + +def test_kwarg_default(x_input, y_input): + x_target, x_unit = x_input + y_target, y_unit = y_input + + @u.quantity_input(x=x_target, y=y_target) + def myfunc_args(x, y=10 * y_unit): + return x, y + + x, y = myfunc_args(1 * x_unit) + + assert isinstance(x, u.Quantity) + assert isinstance(y, u.Quantity) + + assert x.unit == x_unit + assert y.unit == y_unit + + +def test_kwargs_input(x_input, y_input): + x_target, x_unit = x_input + y_target, y_unit = y_input + + @u.quantity_input(x=x_target, y=y_target) + def myfunc_args(x=1 * x_unit, y=1 * y_unit): + return x, y + + kwargs = {"x": 10 * x_unit, "y": 10 * y_unit} + x, y = myfunc_args(**kwargs) + + assert isinstance(x, u.Quantity) + assert isinstance(y, u.Quantity) + + assert x.unit == x_unit + assert y.unit == y_unit + + +def test_kwargs_extra(x_input): + x_target, x_unit = x_input + + @u.quantity_input(x=x_target) + def myfunc_args(x, **kwargs): + return x + + x = myfunc_args(1 * x_unit) + + assert isinstance(x, u.Quantity) + + assert x.unit == x_unit + + +# ---- Tests that don't used the fixtures ---- + + +@pytest.mark.parametrize("x_unit,y_unit", [(u.arcsec, u.eV), ("angle", "energy")]) +def test_arg_equivalencies(x_unit, y_unit): + @u.quantity_input(x=x_unit, y=y_unit, equivalencies=u.mass_energy()) + def myfunc_args(x, y): + return x, y + (10 * u.J) # Add an energy to check equiv is working + + x, y = myfunc_args(1 * u.arcsec, 100 * u.gram) + + assert isinstance(x, u.Quantity) + assert isinstance(y, u.Quantity) + + assert x.unit == u.arcsec + assert y.unit == u.gram + + +@pytest.mark.parametrize("x_unit,energy_unit", [(u.arcsec, u.eV), ("angle", "energy")]) +def test_kwarg_equivalencies(x_unit, energy_unit): + @u.quantity_input(x=x_unit, energy=energy_unit, equivalencies=u.mass_energy()) + def myfunc_args(x, energy=10 * u.eV): + return x, energy + (10 * u.J) # Add an energy to check equiv is working + + x, energy = myfunc_args(1 * u.arcsec, 100 * u.gram) + + assert isinstance(x, u.Quantity) + assert isinstance(energy, u.Quantity) + + assert x.unit == u.arcsec + assert energy.unit == u.gram + + +def test_no_equivalent(): + class test_unit: + pass + + class test_quantity: + unit = test_unit() + + @u.quantity_input(x=u.arcsec) + def myfunc_args(x): + return x + + with pytest.raises( + TypeError, + match=( + "Argument 'x' to function 'myfunc_args' has a 'unit' attribute without an" + " 'is_equivalent' method. You should pass in an astropy Quantity instead." + ), + ): + x, y = myfunc_args(test_quantity()) + + +def test_kwarg_invalid_physical_type(): + @u.quantity_input(x="angle", y="africanswallow") + def myfunc_args(x, y=10 * u.deg): + return x, y + + with pytest.raises( + ValueError, match="Invalid unit or physical type 'africanswallow'." + ): + x, y = myfunc_args(1 * u.arcsec, y=100 * u.deg) + + +def test_default_value_check(): + x_target = u.deg + x_unit = u.arcsec + + with pytest.raises(TypeError): + + @u.quantity_input(x=x_target) + def myfunc_args(x=1.0): + return x + + x = myfunc_args() + + x = myfunc_args(1 * x_unit) + assert isinstance(x, u.Quantity) + assert x.unit == x_unit + + +def test_str_unit_typo(): + @u.quantity_input + def myfunc_args(x: "kilograam"): # noqa: F821 + return x + + with pytest.raises(ValueError): + result = myfunc_args(u.kg) + + +class TestTypeAnnotations: + @pytest.mark.parametrize( + "annot", + [u.m, u.Quantity[u.m], u.Quantity[u.m, "more"]], + ) # Note: parametrization is done even if test class is skipped + def test_single_annotation_unit(self, annot): + """Try a variety of valid annotations.""" + + @u.quantity_input + def myfunc_args(x: annot, y: str): + return x, y + + i_q, i_str = 2 * u.m, "cool string" + o_q, o_str = myfunc_args(i_q, i_str) + assert o_q == i_q + assert o_str == i_str + + +def test_args_None(): + x_target = u.deg + x_unit = u.arcsec + y_target = u.km + y_unit = u.kpc + + @u.quantity_input(x=[x_target, None], y=[None, y_target]) + def myfunc_args(x, y): + return x, y + + x, y = myfunc_args(1 * x_unit, None) + assert isinstance(x, u.Quantity) + assert x.unit == x_unit + assert y is None + + x, y = myfunc_args(None, 1 * y_unit) + assert isinstance(y, u.Quantity) + assert y.unit == y_unit + assert x is None + + +def test_args_None_kwarg(): + x_target = u.deg + x_unit = u.arcsec + y_target = u.km + + @u.quantity_input(x=x_target, y=y_target) + def myfunc_args(x, y=None): + return x, y + + x, y = myfunc_args(1 * x_unit) + assert isinstance(x, u.Quantity) + assert x.unit == x_unit + assert y is None + + x, y = myfunc_args(1 * x_unit, None) + assert isinstance(x, u.Quantity) + assert x.unit == x_unit + assert y is None + + with pytest.raises(TypeError): + x, y = myfunc_args(None, None) + + +@pytest.mark.parametrize("val", [1.0, 1, np.arange(10), np.arange(10.0)]) +def test_allow_dimensionless_numeric(val): + """ + When dimensionless_unscaled is an allowed unit, numbers and numeric numpy + arrays are allowed through + """ + + @u.quantity_input(velocity=[u.km / u.s, u.dimensionless_unscaled]) + def myfunc(velocity): + return velocity + + assert np.all(myfunc(val) == val) + + +@pytest.mark.parametrize("val", [1.0, 1, np.arange(10), np.arange(10.0)]) +def test_allow_dimensionless_numeric_strict(val): + """ + When dimensionless_unscaled is an allowed unit, but we are being strict, + don't allow numbers and numeric numpy arrays through + """ + + @u.quantity_input( + velocity=[u.km / u.s, u.dimensionless_unscaled], strict_dimensionless=True + ) + def myfunc(velocity): + return velocity + + with pytest.raises(TypeError): + assert myfunc(val) + + +@pytest.mark.parametrize("val", [1 * u.deg, [1, 2, 3] * u.m]) +def test_dimensionless_with_nondimensionless_input(val): + """ + When dimensionless_unscaled is the only allowed unit, don't let input with + non-dimensionless units through + """ + + @u.quantity_input(x=u.dimensionless_unscaled) + def myfunc(x): + return x + + with pytest.raises(u.UnitsError): + myfunc(val) + + +def test_annotated_not_quantity(): + """Test when annotation looks like a Quantity[X], but isn't.""" + + @u.quantity_input() + def myfunc(x: typing.Annotated[object, u.m]): + return x + + # nothing happens when wrong unit is passed + assert myfunc(1) == 1 + assert myfunc(1 * u.m) == 1 * u.m + assert myfunc(1 * u.s) == 1 * u.s + + +def test_annotated_not_unit(): + """Test when annotation looks like a Quantity[X], but the unit's wrong.""" + + @u.quantity_input() + def myfunc(x: typing.Annotated[u.Quantity, object()]): + return x + + # nothing happens when wrong unit is passed + assert myfunc(1) == 1 + assert myfunc(1 * u.m) == 1 * u.m + assert myfunc(1 * u.s) == 1 * u.s diff --git a/astropy/units/tests/test_quantity_erfa_ufuncs.py b/astropy/units/tests/test_quantity_erfa_ufuncs.py new file mode 100644 index 000000000000..47b50d3709ce --- /dev/null +++ b/astropy/units/tests/test_quantity_erfa_ufuncs.py @@ -0,0 +1,702 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +""" +Test Structured units and quantities specifically with the ERFA ufuncs. +""" + +import numpy as np +import pytest +from erfa import ufunc as erfa_ufunc +from numpy.testing import assert_array_equal + +from astropy import units as u +from astropy.tests.helper import assert_quantity_allclose +from astropy.units.quantity_helper.erfa import astrom_unit + +ASTROM_UNIT = astrom_unit() + + +def vvd(val, valok, dval, func, test, status): + """Mimic routine of erfa/src/t_erfa_c.c (to help copy & paste)""" + assert u.allclose(val, valok * val.unit, atol=dval * val.unit) + + +class TestPVUfuncs: + @classmethod + def setup_class(cls): + cls.pv_unit = u.Unit("AU,AU/day") + cls.pv_value = np.array( + [ + ([1.0, 0.0, 0.0], [0.0, 0.0125, 0.0]), + ([0.0, 1.0, 0.0], [-0.0125, 0.0, 0.0]), + ], + dtype=erfa_ufunc.dt_pv, + ) + cls.pv = cls.pv_value << cls.pv_unit + + def test_cpv(self): + pv_copy = erfa_ufunc.cpv(self.pv) + assert_array_equal(pv_copy, self.pv) + assert not np.may_share_memory(pv_copy, self.pv) + + def test_p2pv(self): + p2pv = erfa_ufunc.p2pv(self.pv["p"]) + assert_array_equal(p2pv["p"], self.pv["p"]) + assert_array_equal( + p2pv["v"], np.zeros(self.pv.shape + (3,), float) << u.m / u.s + ) + + def test_p2pv_inplace(self): + # TODO: fix np.zeros_like. + out = np.zeros_like(self.pv_value) << self.pv_unit + p2pv = erfa_ufunc.p2pv(self.pv["p"], out=out) + assert out is p2pv + assert_array_equal(p2pv["p"], self.pv["p"]) + assert_array_equal( + p2pv["v"], np.zeros(self.pv.shape + (3,), float) << u.m / u.s + ) + + def test_pv2p(self): + p = erfa_ufunc.pv2p(self.pv) + assert_array_equal(p, self.pv["p"]) + out = np.zeros_like(p) + p2 = erfa_ufunc.pv2p(self.pv, out=out) + assert out is p2 + assert_array_equal(p2, self.pv["p"]) + + def test_pv2s(self): + theta, phi, r, td, pd, rd = erfa_ufunc.pv2s(self.pv) + assert theta.unit == u.radian + assert_quantity_allclose(theta, [0, 90] * u.deg) # longitude + assert phi.unit == u.radian + assert_array_equal(phi.value, np.zeros(self.pv.shape)) # latitude + assert r.unit == u.AU + assert_array_equal(r.value, np.ones(self.pv.shape)) + assert td.unit == u.radian / u.day + assert_array_equal(td.value, np.array([0.0125] * 2)) + assert pd.unit == u.radian / u.day + assert_array_equal(pd.value, np.zeros(self.pv.shape)) + assert rd.unit == u.AU / u.day + assert_array_equal(rd.value, np.zeros(self.pv.shape)) + + def test_pv2s_non_standard_units(self): + pv = self.pv_value << u.Unit("Pa,Pa/m") + theta, phi, r, td, pd, rd = erfa_ufunc.pv2s(pv) + assert theta.unit == u.radian + assert_quantity_allclose(theta, [0, 90] * u.deg) # longitude + assert phi.unit == u.radian + assert_array_equal(phi.value, np.zeros(pv.shape)) # latitude + assert r.unit == u.Pa + assert_array_equal(r.value, np.ones(pv.shape)) + assert td.unit == u.radian / u.m + assert_array_equal(td.value, np.array([0.0125] * 2)) + assert pd.unit == u.radian / u.m + assert_array_equal(pd.value, np.zeros(pv.shape)) + assert rd.unit == u.Pa / u.m + assert_array_equal(rd.value, np.zeros(pv.shape)) + + @pytest.mark.xfail( + reason=( + "erfa ufuncs cannot take different names; it is not yet clear whether " + "this is changeable; see https://github.com/liberfa/pyerfa/issues/77" + ) + ) + def test_pv2s_non_standard_names_and_units(self): + pv_value = np.array(self.pv_value, dtype=[("pos", "f8"), ("vel", "f8")]) + pv = pv_value << u.Unit("Pa,Pa/m") + theta, phi, r, td, pd, rd = erfa_ufunc.pv2s(pv) + assert theta.unit == u.radian + assert_quantity_allclose(theta, [0, 90] * u.deg) # longitude + assert phi.unit == u.radian + assert_array_equal(phi.value, np.zeros(pv.shape)) # latitude + assert r.unit == u.Pa + assert_array_equal(r.value, np.ones(pv.shape)) + assert td.unit == u.radian / u.m + assert_array_equal(td.value, np.array([0.0125] * 2)) + assert pd.unit == u.radian / u.m + assert_array_equal(pd.value, np.zeros(pv.shape)) + assert rd.unit == u.Pa / u.m + assert_array_equal(rd.value, np.zeros(pv.shape)) + + def test_s2pv(self): + theta, phi, r, td, pd, rd = erfa_ufunc.pv2s(self.pv) + # On purpose change some of the units away from expected by s2pv. + pv = erfa_ufunc.s2pv( + theta.to(u.deg), phi, r.to(u.m), td.to(u.deg / u.day), pd, rd.to(u.m / u.s) + ) + assert pv.unit == u.StructuredUnit("m, m/s", names=("p", "v")) + assert_quantity_allclose(pv["p"], self.pv["p"], atol=1 * u.m, rtol=0) + assert_quantity_allclose(pv["v"], self.pv["v"], atol=1 * u.mm / u.s, rtol=0) + + def test_s2p_not_all_quantity(self): + # Test for a useful error message - see gh-16873. + # Non-quantity input should be treated as dimensionless and thus cannot + # be converted to radians. + with pytest.raises( + AttributeError, + match=( + "'NoneType' object has no attribute 'get_converter'" + ".*\n.*treated as dimensionless" + ), + ): + erfa_ufunc.s2p(0.5, 0.5, 4 * u.km) + + # Except if we have the right equivalency in place. + with u.add_enabled_equivalencies(u.dimensionless_angles()): + result = erfa_ufunc.s2p(0.5, 0.5, 4 * u.km) + + expected = erfa_ufunc.s2p(0.5 * u.radian, 0.5 * u.radian, 4 * u.km) + assert_array_equal(result, expected) + + def test_pvstar(self): + ra, dec, pmr, pmd, px, rv, stat = erfa_ufunc.pvstar(self.pv) + assert_array_equal(stat, np.zeros(self.pv.shape, dtype="i4")) + assert ra.unit == u.radian + assert_quantity_allclose(ra, [0, 90] * u.deg) + assert dec.unit == u.radian + assert_array_equal(dec.value, np.zeros(self.pv.shape)) # latitude + assert pmr.unit == u.radian / u.year + assert_quantity_allclose(pmr, [0.0125, 0.0125] * u.radian / u.day) + assert pmd.unit == u.radian / u.year + assert_array_equal(pmd.value, np.zeros(self.pv.shape)) + assert px.unit == u.arcsec + assert_quantity_allclose(px, 1 * u.radian) + assert rv.unit == u.km / u.s + # RV is non-zero because proper motion induces a small redshift + # due to second order Doppler shift. + assert_quantity_allclose( + rv, np.zeros(self.pv.shape) << (u.km / u.s), atol=1 * u.m / u.s + ) + + def test_starpv(self): + ra, dec, pmr, pmd, px, rv, stat = erfa_ufunc.pvstar(self.pv) + pv, stat = erfa_ufunc.starpv( + ra.to(u.deg), dec.to(u.deg), pmr, pmd, px, rv.to(u.m / u.s) + ) + assert_array_equal(stat, np.zeros(self.pv.shape, dtype="i4")) + assert pv.unit == self.pv.unit + # Roundtrip is not as good as hoped on 32bit, not clear why. + # But proper motions are ridiculously high... + assert_quantity_allclose(pv["p"], self.pv["p"], atol=1 * u.m, rtol=0) + assert_quantity_allclose(pv["v"], self.pv["v"], atol=1 * u.m / u.s, rtol=0) + + def test_pvtob(self): + pv = erfa_ufunc.pvtob( + [90, 0] * u.deg, + 0.0 * u.deg, + 100 * u.km, + 0 * u.deg, + 0 * u.deg, + 0 * u.deg, + 90 * u.deg, + ) + assert pv.unit == u.StructuredUnit("m, m/s", names=("p", "v")) + assert pv.unit["v"] == u.m / u.s + assert_quantity_allclose( + pv["p"], [[-6478, 0, 0], [0, 6478, 0]] * u.km, atol=2 * u.km + ) + assert_quantity_allclose( + pv["v"], [[0, -0.5, 0], [-0.5, 0, 0]] * u.km / u.s, atol=0.1 * u.km / u.s + ) + + def test_pvdpv(self): + pvdpv = erfa_ufunc.pvdpv(self.pv, self.pv) + assert pvdpv["pdp"].unit == self.pv.unit["p"] ** 2 + assert pvdpv["pdv"].unit == self.pv.unit["p"] * self.pv.unit["v"] + assert_array_equal( + pvdpv["pdp"], np.einsum("...i,...i->...", self.pv["p"], self.pv["p"]) + ) + assert_array_equal( + pvdpv["pdv"], 2 * np.einsum("...i,...i->...", self.pv["p"], self.pv["v"]) + ) + z_axis = u.Quantity(np.array(([0, 0, 1], [0, 0, 0]), erfa_ufunc.dt_pv), "1,1/s") + pvdpv2 = erfa_ufunc.pvdpv(self.pv, z_axis) + assert pvdpv2["pdp"].unit == self.pv.unit["p"] + assert pvdpv2["pdv"].unit == self.pv.unit["v"] + assert_array_equal(pvdpv2["pdp"].value, np.zeros(self.pv.shape)) + assert_array_equal(pvdpv2["pdv"].value, np.zeros(self.pv.shape)) + + def test_pvxpv(self): + pvxpv = erfa_ufunc.pvxpv(self.pv, self.pv) + assert pvxpv["p"].unit == self.pv.unit["p"] ** 2 + assert pvxpv["v"].unit == self.pv.unit["p"] * self.pv.unit["v"] + assert_array_equal(pvxpv["p"].value, np.zeros(self.pv["p"].shape)) + assert_array_equal(pvxpv["v"].value, np.zeros(self.pv["v"].shape)) + z_axis = u.Quantity(np.array(([0, 0, 1], [0, 0, 0]), erfa_ufunc.dt_pv), "1,1/s") + pvxpv2 = erfa_ufunc.pvxpv(self.pv, z_axis) + assert pvxpv2["p"].unit == self.pv.unit["p"] + assert pvxpv2["v"].unit == self.pv.unit["v"] + assert_array_equal(pvxpv2["p"], [[0.0, -1, 0.0], [1.0, 0.0, 0.0]] * u.AU) + assert_array_equal( + pvxpv2["v"], [[0.0125, 0.0, 0.0], [0.0, 0.0125, 0.0]] * u.AU / u.day + ) + + def test_pvm(self): + pm, vm = erfa_ufunc.pvm(self.pv) + assert pm.unit == self.pv.unit["p"] + assert vm.unit == self.pv.unit["v"] + assert_array_equal(pm, np.linalg.norm(self.pv["p"], axis=-1)) + assert_array_equal(vm, np.linalg.norm(self.pv["v"], axis=-1)) + + def test_pvmpv(self): + pvmpv = erfa_ufunc.pvmpv(self.pv, self.pv) + assert pvmpv.unit == self.pv.unit + assert_array_equal(pvmpv["p"], 0 * self.pv["p"]) + assert_array_equal(pvmpv["v"], 0 * self.pv["v"]) + + def test_pvppv(self): + pvppv = erfa_ufunc.pvppv(self.pv, self.pv) + assert pvppv.unit == self.pv.unit + assert_array_equal(pvppv["p"], 2 * self.pv["p"]) + assert_array_equal(pvppv["v"], 2 * self.pv["v"]) + + def test_pvu(self): + pvu = erfa_ufunc.pvu(86400 * u.s, self.pv) + assert pvu.unit == self.pv.unit + assert_array_equal(pvu["p"], self.pv["p"] + 1 * u.day * self.pv["v"]) + assert_array_equal(pvu["v"], self.pv["v"]) + + def test_pvup(self): + pvup = erfa_ufunc.pvup(86400 * u.s, self.pv) + assert pvup.unit == self.pv.unit["p"] + assert_array_equal(pvup, self.pv["p"] + 1 * u.day * self.pv["v"]) + + def test_sxpv(self): + # Not a realistic example!! + sxpv = erfa_ufunc.sxpv(10.0, self.pv) + assert sxpv.unit == self.pv.unit + assert_array_equal(sxpv["p"], self.pv["p"] * 10) + assert_array_equal(sxpv["v"], self.pv["v"] * 10) + sxpv2 = erfa_ufunc.sxpv(30.0 * u.s, self.pv) + assert sxpv2.unit == u.StructuredUnit("AU s,AU s/d", names=("p", "v")) + assert_array_equal(sxpv2["p"], self.pv["p"] * 30 * u.s) + assert_array_equal(sxpv2["v"], self.pv["v"] * 30 * u.s) + + def test_s2xpv(self): + # Not a realistic example!! + s2xpv = erfa_ufunc.s2xpv(10.0, 1 * u.s, self.pv) + assert s2xpv.unit == u.StructuredUnit("AU,AU s/d", names=("p", "v")) + assert_array_equal(s2xpv["p"], self.pv["p"] * 10) + assert_array_equal(s2xpv["v"], self.pv["v"] * u.s) + + @pytest.mark.parametrize( + "r", + [ + np.eye(3), + np.array( + [ + [0.0, -1.0, 0.0], + [1.0, 0.0, 0.0], + [0.0, 0.0, 1.0], + ] + ), + np.eye(3) / u.s, + ], + ) + def test_rxpv(self, r): + result = erfa_ufunc.rxpv(r, self.pv) + assert_array_equal(result["p"], np.einsum("...ij,...j->...i", r, self.pv["p"])) + assert_array_equal(result["v"], np.einsum("...ij,...j->...i", r, self.pv["v"])) + + @pytest.mark.parametrize( + "r", + [ + np.eye(3), + np.array( + [ + [0.0, -1.0, 0.0], + [1.0, 0.0, 0.0], + [0.0, 0.0, 1.0], + ] + ), + np.eye(3) / u.s, + ], + ) + def test_trxpv(self, r): + result = erfa_ufunc.trxpv(r, self.pv) + assert_array_equal( + result["p"], np.einsum("...ij,...j->...i", r.T, self.pv["p"]) + ) + assert_array_equal( + result["v"], np.einsum("...ij,...j->...i", r.T, self.pv["v"]) + ) + + +class TestEraStructUfuncs: + @classmethod + def setup_class(cls): + ldbody = np.array( + [ + (0.00028574, 3e-10, ([-7.81014427, -5.60956681, -1.98079819], + [0.0030723249, -0.00406995477, -0.00181335842])), + (0.00095435, 3e-9, ([0.738098796, 4.63658692, 1.9693136], + [-0.00755816922, 0.00126913722, 0.000727999001])), + (1.0, 6e-6, ([-0.000712174377, -0.00230478303, -0.00105865966], + [6.29235213e-6, -3.30888387e-7, -2.96486623e-7])) + ], + dtype=erfa_ufunc.dt_eraLDBODY + ) # fmt: skip + ldbody_unit = u.StructuredUnit("Msun,radian,(AU,AU/day)", ldbody.dtype) + cls.ldbody = ldbody << ldbody_unit + cls.ob = [-0.974170437, -0.2115201, -0.0917583114] << u.AU + cls.sc = np.array([-0.763276255, -0.608633767, -0.216735543]) + + # From t_atciq in t_erfa_c.c + astrom, eo = erfa_ufunc.apci13(2456165.5, 0.401182685) + cls.astrom = astrom << ASTROM_UNIT + cls.rc = 2.71 * u.rad + cls.dc = 0.174 * u.rad + cls.pr = 1e-5 * u.rad / u.year + cls.pd = 5e-6 * u.rad / u.year + cls.px = 0.1 * u.arcsec + cls.rv = 55.0 * u.km / u.s + + def test_ldn_basic(self): + sn = erfa_ufunc.ldn(self.ldbody, self.ob, self.sc) + assert_quantity_allclose( + sn, + [-0.7632762579693333866, -0.6086337636093002660, -0.2167355420646328159] + * u.one, + atol=1e-12, + rtol=0, + ) + + def test_ldn_in_other_unit(self): + ldbody = self.ldbody.to("kg,rad,(m,m/s)") + ob = self.ob.to("m") + sn = erfa_ufunc.ldn(ldbody, ob, self.sc) + assert_quantity_allclose( + sn, + [-0.7632762579693333866, -0.6086337636093002660, -0.2167355420646328159] + * u.one, + atol=1e-12, + rtol=0, + ) + + def test_ldn_in_SI(self): + sn = erfa_ufunc.ldn(self.ldbody.si, self.ob.si, self.sc) + assert_quantity_allclose( + sn, + [-0.7632762579693333866, -0.6086337636093002660, -0.2167355420646328159] + * u.one, + atol=1e-12, + rtol=0, + ) + + def test_aper(self): + along = self.astrom["along"] + astrom2 = erfa_ufunc.aper(10 * u.deg, self.astrom) + assert astrom2["eral"].unit == u.radian + assert_quantity_allclose(astrom2["eral"], along + 10 * u.deg) + astrom3 = self.astrom.to("s,km,1,km,1,1,1,deg,deg,deg,deg,1,1,1,rad,rad,rad") + astrom4 = erfa_ufunc.aper(10 * u.deg, astrom3) + assert astrom3["eral"].unit == u.rad + assert astrom4["eral"].unit == u.deg + assert astrom4.unit == "s,km,1,km,1,1,1,deg,deg,deg,deg,1,1,1,deg,rad,rad" + assert_quantity_allclose(astrom4["eral"], along + 10 * u.deg) + + def test_atciq_basic(self): + ri, di = erfa_ufunc.atciq( + self.rc, self.dc, self.pr, self.pd, self.px, self.rv, self.astrom + ) + assert_quantity_allclose(ri, 2.710121572968696744 * u.rad) + assert_quantity_allclose(di, 0.1729371367219539137 * u.rad) + + def test_atciq_in_other_unit(self): + astrom = self.astrom.to("s,km,1,km,1,1,1,deg,deg,deg,deg,1,1,1,deg,deg,deg") + ri, di = erfa_ufunc.atciq( + self.rc.to(u.deg), + self.dc.to(u.deg), + self.pr.to(u.mas / u.yr), + self.pd.to(u.mas / u.yr), + self.px, + self.rv.to(u.m / u.s), + astrom, + ) + assert_quantity_allclose(ri, 2.710121572968696744 * u.rad, atol=1e-12 * u.rad) + assert_quantity_allclose(di, 0.1729371367219539137 * u.rad, atol=1e-12 * u.rad) + + def test_atciqn(self): + ri, di = erfa_ufunc.atciqn( + self.rc.to(u.deg), + self.dc.to(u.deg), + self.pr.to(u.mas / u.yr), + self.pd.to(u.mas / u.yr), + self.px, + self.rv.to(u.m / u.s), + self.astrom.si, + self.ldbody.si, + ) + assert_quantity_allclose(ri, 2.710122008104983335 * u.rad, atol=1e-12 * u.rad) + assert_quantity_allclose(di, 0.1729371916492767821 * u.rad, atol=1e-12 * u.rad) + + def test_atciqz(self): + ri, di = erfa_ufunc.atciqz(self.rc.to(u.deg), self.dc.to(u.deg), self.astrom.si) + assert_quantity_allclose(ri, 2.709994899247256984 * u.rad, atol=1e-12 * u.rad) + assert_quantity_allclose(di, 0.1728740720984931891 * u.rad, atol=1e-12 * u.rad) + + def test_aticq(self): + ri = 2.710121572969038991 * u.rad + di = 0.1729371367218230438 * u.rad + rc, dc = erfa_ufunc.aticq(ri.to(u.deg), di.to(u.deg), self.astrom.si) + assert_quantity_allclose(rc, 2.710126504531716819 * u.rad, atol=1e-12 * u.rad) + assert_quantity_allclose(dc, 0.1740632537627034482 * u.rad, atol=1e-12 * u.rad) + + def test_aticqn(self): + ri = 2.709994899247599271 * u.rad + di = 0.1728740720983623469 * u.rad + rc, dc = erfa_ufunc.aticqn( + ri.to(u.deg), di.to(u.deg), self.astrom.si, self.ldbody.si + ) + assert_quantity_allclose(rc, 2.709999575033027333 * u.rad, atol=1e-12 * u.rad) + assert_quantity_allclose(dc, 0.1739999656316469990 * u.rad, atol=1e-12 * u.rad) + + def test_atioq_atoiq(self): + astrom, _ = erfa_ufunc.apio13( + 2456384.5, + 0.969254051, + 0.1550675, + -0.527800806, + -1.2345856, + 2738.0, + 2.47230737e-7, + 1.82640464e-6, + 731.0, + 12.8, + 0.59, + 0.55, + ) + astrom = astrom << ASTROM_UNIT + + ri = 2.710121572969038991 * u.rad + di = 0.1729371367218230438 * u.rad + aob, zob, hob, dob, rob = erfa_ufunc.atioq( + ri.to(u.deg), di.to(u.deg), astrom.si + ) + assert_quantity_allclose( + aob, 0.9233952224895122499e-1 * u.rad, atol=1e-12 * u.rad + ) + assert_quantity_allclose(zob, 1.407758704513549991 * u.rad, atol=1e-12 * u.rad) + assert_quantity_allclose( + hob, -0.9247619879881698140e-1 * u.rad, atol=1e-12 * u.rad + ) + assert_quantity_allclose(dob, 0.1717653435756234676 * u.rad, atol=1e-12 * u.rad) + assert_quantity_allclose(rob, 2.710085107988480746 * u.rad, atol=1e-12 * u.rad) + + # Sadly does not just use the values from above. + ob1 = 2.710085107986886201 * u.rad + ob2 = 0.1717653435758265198 * u.rad + ri2, di2 = erfa_ufunc.atoiq("R", ob1.to(u.deg), ob2.to(u.deg), astrom.si) + assert_quantity_allclose(ri2, 2.710121574447540810 * u.rad, atol=1e-12 * u.rad) + assert_quantity_allclose( + di2, 0.17293718391166087785 * u.rad, atol=1e-12 * u.rad + ) + + +class TestAp: + @classmethod + def setup_class(cls): + # Values from apco13 and apio test cases in t_erfa_c.c + # with units changed so we can check conversion. + cls.utc1 = 2456384.5 + cls.utc2 = 0.969254051 + cls.dut1 = (0.1550675 * u.s).to(u.ms) + cls.sp = (-3.01974337e-11 * u.rad).to(u.deg) + cls.theta = (3.14540971 * u.rad).to(u.mdeg) + cls.elong = (-0.527800806 * u.rad).to(u.Marcsec) + cls.phi = (-1.2345856 * u.rad).to(u.arcmin) + cls.hm = (2738.0 * u.m).to(u.km) + cls.phpa = (731.0 * u.hPa).to(u.Pa) + cls.tc = (12.8 * u.deg_C).to(u.K, equivalencies=u.temperature()) + cls.rh = (0.59 * u.one).to(u.percent) + cls.wl = (0.55 * u.micron).to(u.AA) + # For apio. + cls.xp = (2.47230737e-7 * u.rad).to(u.arcsec) + cls.yp = (1.82640464e-6 * u.rad).to(u.arcmin) + cls.refa = (0.000201418779 * u.rad).to(u.mas) + cls.refb = (-2.36140831e-7 * u.rad).to(u.uas) + cls.apco13_args = [ + getattr(cls, name) + for name in [ + "utc1", + "utc2", + "dut1", + "elong", + "phi", + "hm", + "xp", + "yp", + "phpa", + "tc", + "rh", + "wl", + ] + ] + cls.apio_args = [ + getattr(cls, name) + for name in [ + "sp", + "theta", + "elong", + "phi", + "hm", + "xp", + "yp", + "refa", + "refb", + ] + ] + + def test_apco13(self): + astrom, eo, status = erfa_ufunc.apco13(*self.apco13_args) + assert status == 0 + vvd(eo, -0.003020548354802412839, 1e-14, "eraApco13", "eo", status) + assert astrom.unit == ASTROM_UNIT + for name, expected in { + "pmt": 13.25248468622475727, + "eb": [-0.9741827107320875162, + -0.2115130190489716682, + -0.09179840189496755339], + "eh": [-0.9736425572586935247, + -0.2092452121603336166, + -0.09075578153885665295], + "em": 0.9998233240913898141, + "v": [0.2078704994520489246e-4, + -0.8955360133238868938e-4, + -0.3863338993055887398e-4], + "bm1": 0.9999999950277561004, + "bpn": np.array( + [[ 0.9999991390295147999, + 0.4978650075315529277e-7, + 0.001312227200850293372], + [-0.1136336652812486604e-7, + 0.9999999995713154865, + -0.2928086230975367296e-4], + [-0.001312227201745553566, + 0.2928082218847679162e-4, + 0.9999991386008312212], + ]).T, + "along": -0.5278008060295995733, + "xpl": 0.1133427418130752958e-5, + "ypl": 0.1453347595780646207e-5, + "sphi": -0.9440115679003211329, + "cphi": 0.3299123514971474711, + "diurab": 0, + "eral": 2.617608909189664000, + "refa": 0.2014187785940396921e-3, + "refb": -0.2361408314943696227e-6, + }.items(): # fmt: skip + assert_quantity_allclose(astrom[name], expected * ASTROM_UNIT[name]) + + def test_apco13_no_time_units(self): + msg = "cannot pass in units for 2-part time in apco13" + with pytest.raises(TypeError, match=msg): + erfa_ufunc.apco13(self.utc1 * u.day, *self.apco13_args[1:]) + + with pytest.raises(TypeError, match=msg): + erfa_ufunc.apco13(self.utc1, self.utc2 * u.day, *self.apco13_args[2:]) + + with pytest.raises(TypeError, match=msg): + erfa_ufunc.apco13( + self.utc1 * u.day, self.utc2 * u.day, *self.apco13_args[2:] + ) + + def test_apio(self): + astrom = erfa_ufunc.apio(*self.apio_args) + assert astrom.unit == ASTROM_UNIT + for name, expected in { + "along": -0.5278008060295995734, + "xpl": 0.1133427418130752958e-5, + "ypl": 0.1453347595780646207e-5, + "sphi": -0.9440115679003211329, + "cphi": 0.3299123514971474711, + "diurab": 0.5135843661699913529e-6, + "eral": 2.617608903970400427, + "refa": 0.2014187790000000000e-3, + "refb": -0.2361408310000000000e-6, + }.items(): + assert_quantity_allclose(astrom[name], expected * ASTROM_UNIT[name]) + + +class TestGeodetic: + @classmethod + def setup_class(cls): + cls.ellipsoid = 1 + cls.length_unit = u.Unit("m") + cls.equatorial_radius_value = 6378136.0 + cls.equatorial_radius = (cls.equatorial_radius_value << cls.length_unit).to( + u.km + ) + cls.flattening = 0.0033528 * u.dimensionless_unscaled + cls.lon_value = 0.9827937232473290680 + cls.lon_unit = u.Unit("rad") + cls.lon = (cls.lon_value << cls.lon_unit).to(u.deg) + cls.lat_value = 0.9716018377570411532 + cls.lat_unit = u.Unit("rad") + cls.lat = (cls.lat_value << cls.lat_unit).to(u.deg) + cls.height_value = 332.36862495764397 + cls.height = cls.height_value << cls.length_unit + cls.x_value = 2e6 + cls.x = (cls.x_value << cls.length_unit).to(u.cm) + cls.y_value = 3e6 + cls.y = (cls.y_value << cls.length_unit).to(u.km) + cls.z_value = 5.244e6 + cls.z = (cls.z_value << cls.length_unit).to(u.Mm) + cls.xyz = np.stack([cls.x, cls.y, cls.z]) + + def test_unit_errors(self): + """Test unit errors when dimensionless parameters are used""" + + msg = ( + "'NoneType' object has no attribute 'get_converter'" + ".*\n.*treated as dimensionless" + ) + with pytest.raises(AttributeError, match=msg): + erfa_ufunc.gc2gde(self.equatorial_radius_value, self.flattening, self.xyz) + with pytest.raises(AttributeError, match=msg): + erfa_ufunc.gd2gce( + self.equatorial_radius_value, + self.flattening, + self.lon, + self.lat, + self.height, + ) + with pytest.raises(AttributeError, match=msg): + erfa_ufunc.gc2gde(self.equatorial_radius, self.flattening, self.xyz.value) + with pytest.raises(AttributeError, match=msg): + erfa_ufunc.gd2gce( + self.equatorial_radius, + self.flattening, + self.lon_value, + self.lat, + self.height, + ) + with pytest.raises(AttributeError, match=msg): + erfa_ufunc.gd2gce( + self.equatorial_radius, + self.flattening, + self.lon, + self.lat, + self.height_value, + ) + + def test_gc2gde(self): + """Test that we reproduce erfa/src/t_erfa_c.c t_gc2gd""" + + e, p, h, status = erfa_ufunc.gc2gde( + self.equatorial_radius, self.flattening, self.xyz + ) + assert status == 0 + vvd(e, self.lon_value, 1e-14, "eraGc2gde", "e", status) + vvd(p, self.lat_value, 1e-14, "eraGc2gde", "p", status) + vvd(h, self.height_value, 1e-8, "eraGc2gde", "h", status) + + def test_gd2gce(self): + """Test that we reproduce erfa/src/t_erfa_c.c t_gc2gd""" + + xyz, status = erfa_ufunc.gd2gce( + self.equatorial_radius, self.flattening, self.lon, self.lat, self.height + ) + assert status == 0 + vvd(xyz[0], self.x_value, 1e-7, "eraGd2gce", "e", status) + vvd(xyz[1], self.y_value, 1e-7, "eraGd2gce", "p", status) + vvd(xyz[2], self.z_value, 1e-7, "eraGd2gce", "h", status) diff --git a/astropy/units/tests/test_quantity_helpers.py b/astropy/units/tests/test_quantity_helpers.py new file mode 100644 index 000000000000..b218842e5753 --- /dev/null +++ b/astropy/units/tests/test_quantity_helpers.py @@ -0,0 +1,34 @@ +""" +Test ``allclose`` and ``isclose``. +``allclose`` was ``quantity_allclose`` in ``astropy.tests.helper``. +""" + +import numpy as np +import pytest + +from astropy import units as u + + +@pytest.mark.parametrize( + ("a", "b"), + [ + ([1, 2], [1, 2]), + ([1, 2] * u.m, [100, 200] * u.cm), + (1 * u.s, 1000 * u.ms), + ], +) +def test_allclose_isclose_default(a, b): + assert u.allclose(a, b) + assert np.all(u.isclose(a, b)) + + +def test_allclose_isclose(): + a = [1, 2] * u.m + b = [101, 201] * u.cm + delta = 2 * u.cm + assert u.allclose(a, b, atol=delta) + assert np.all(u.isclose(a, b, atol=delta)) + + c = [90, 200] * u.cm + assert not u.allclose(a, c) + assert not np.all(u.isclose(a, c)) diff --git a/astropy/units/tests/test_quantity_info.py b/astropy/units/tests/test_quantity_info.py new file mode 100644 index 000000000000..17af38292c88 --- /dev/null +++ b/astropy/units/tests/test_quantity_info.py @@ -0,0 +1,179 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +"""Test the propagation of info on Quantity during operations.""" + +import copy + +import numpy as np +import pytest + +from astropy import units as u + + +def assert_info_equal(a, b, ignore=set()): + a_info = a.info + b_info = b.info + for attr in (a_info.attr_names | b_info.attr_names) - ignore: + if attr == "unit": + assert a_info.unit.is_equivalent(b_info.unit) + else: + assert getattr(a_info, attr, None) == getattr(b_info, attr, None) + + +def assert_no_info(a): + __tracebackhide__ = True + assert "info" not in a.__dict__ + + +class TestQuantityInfo: + @classmethod + def setup_class(cls): + cls.q = u.Quantity(np.arange(1.0, 5.0), "m/s") + cls.q.info.name = "v" + cls.q.info.description = "air speed of a african swallow" + + def test_copy(self): + q_copy1 = self.q.copy() + assert_info_equal(q_copy1, self.q) + q_copy2 = copy.copy(self.q) + assert_info_equal(q_copy2, self.q) + q_copy3 = copy.deepcopy(self.q) + assert_info_equal(q_copy3, self.q) + + def test_slice(self): + q_slice = self.q[1:3] + assert_info_equal(q_slice, self.q) + q_take = self.q.take([0, 1]) + assert_info_equal(q_take, self.q) + + def test_item(self): + # Scalars do not get info set (like for Column); TODO: is this OK? + q1 = self.q[1] + assert_no_info(q1) + q_item = self.q.item(1) + assert_no_info(q_item) + + def test_iter(self): + # Scalars do not get info set. + for q in self.q: + assert_no_info(q) + for q in iter(self.q): + assert_no_info(q) + + def test_change_to_equivalent_unit(self): + q1 = self.q.to(u.km / u.hr) + assert_info_equal(q1, self.q) + q2 = self.q.si + assert_info_equal(q2, self.q) + q3 = self.q.cgs + assert_info_equal(q3, self.q) + q4 = self.q.decompose() + assert_info_equal(q4, self.q) + + def test_reshape(self): + q = self.q.reshape(-1, 1, 2) + assert_info_equal(q, self.q) + q2 = q.squeeze() + assert_info_equal(q2, self.q) + + def test_insert(self): + q = self.q.copy() + q.insert(1, 1 * u.cm / u.hr) + assert_info_equal(q, self.q) + + def test_unary_op(self): + q = -self.q + assert_no_info(q) + + def test_binary_op(self): + q = self.q + self.q + assert_no_info(q) + + def test_unit_change(self): + q = self.q * u.s + assert_no_info(q) + q2 = u.s / self.q + assert_no_info(q) + + def test_inplace_unit_change(self): + # Not sure if it is logical to keep info here! + q = self.q.copy() + q *= u.s + assert_info_equal(q, self.q, ignore={"unit"}) + + def test_inplace_info_name_change(self): + # see https://github.com/astropy/astropy/issues/17449 + q = self.q.copy() + + q.info.name = "test" + assert q.info.name == "test" + + q.info.name = None + assert q.info.name is None + + with pytest.raises( + TypeError, match="Expected a str value, got 2.3 with type float" + ): + q.info.name = 2.3 + + +class TestStructuredQuantity: + @classmethod + def setup_class(cls): + value = np.array([(1.0, 2.0), (3.0, 4.0)], dtype=[("p", "f8"), ("v", "f8")]) + cls.q = u.Quantity(value, "m, m/s") + cls.q.info.name = "pv" + cls.q.info.description = "Location and speed" + + def test_keying(self): + q_p = self.q["p"] + assert_no_info(q_p) + + def test_slicing(self): + q = self.q[:1] + assert_info_equal(q, self.q) + + def test_item(self): + # Scalars do not get info set. + q = self.q[1] + assert_no_info(q) + + +class TestQuantitySubclass: + """Regression test for gh-14514: _new_view should __array_finalize__. + + But info should be propagated only for slicing, etc. + """ + + @classmethod + def setup_class(cls): + class MyQuantity(u.Quantity): + def __array_finalize__(self, obj): + super().__array_finalize__(obj) + if hasattr(obj, "swallow"): + self.swallow = obj.swallow + + cls.my_q = MyQuantity([10.0, 20.0], u.m / u.s) + cls.my_q.swallow = "African" + cls.my_q_w_info = cls.my_q.copy() + cls.my_q_w_info.info.name = "swallow" + + def test_setup(self): + assert_no_info(self.my_q) + assert self.my_q_w_info.swallow == self.my_q.swallow + assert self.my_q_w_info.info.name == "swallow" + + def test_slice(self): + slc1 = self.my_q[:1] + assert slc1.swallow == self.my_q.swallow + assert_no_info(slc1) + slc2 = self.my_q_w_info[1:] + assert slc2.swallow == self.my_q.swallow + assert_info_equal(slc2, self.my_q_w_info) + + def test_op(self): + square1 = self.my_q**2 + assert square1.swallow == self.my_q.swallow + assert_no_info(square1) + square2 = self.my_q_w_info**2 + assert square2.swallow == self.my_q.swallow + assert_no_info(square2) diff --git a/astropy/units/tests/test_quantity_non_ufuncs.py b/astropy/units/tests/test_quantity_non_ufuncs.py new file mode 100644 index 000000000000..e9402af91f15 --- /dev/null +++ b/astropy/units/tests/test_quantity_non_ufuncs.py @@ -0,0 +1,2891 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +import inspect +import itertools +from io import StringIO +from types import FunctionType, ModuleType + +import numpy as np +import numpy.lib.recfunctions as rfn +import pytest +from numpy.testing import assert_allclose, assert_array_equal +from numpy.testing.overrides import allows_array_function_override + +from astropy import units as u +from astropy.units.quantity_helper.function_helpers import ( + DISPATCHED_FUNCTIONS, + FUNCTION_HELPERS, + IGNORED_FUNCTIONS, + SUBCLASS_SAFE_FUNCTIONS, + SUPPORTED_NEP35_FUNCTIONS, + TBD_FUNCTIONS, + UNSUPPORTED_FUNCTIONS, +) +from astropy.utils.compat import NUMPY_LT_2_1, NUMPY_LT_2_2, NUMPY_LT_2_4 + +VAR_POSITIONAL = inspect.Parameter.VAR_POSITIONAL +VAR_KEYWORD = inspect.Parameter.VAR_KEYWORD +POSITIONAL_ONLY = inspect.Parameter.POSITIONAL_ONLY +KEYWORD_ONLY = inspect.Parameter.KEYWORD_ONLY +POSITIONAL_OR_KEYWORD = inspect.Parameter.POSITIONAL_OR_KEYWORD +EMPTY_DEFAULT = inspect.Parameter.empty + +ARCSEC_PER_DEGREE = 60 * 60 +ARCSEC_PER_RADIAN = ARCSEC_PER_DEGREE * np.rad2deg(1) + + +# To get the functions that could be covered, we look for those that +# are in modules we care about and have been overridden. +def get_wrapped_functions(*modules: ModuleType) -> set[FunctionType]: + return { + f + for module in modules + for f in module.__dict__.values() + if callable(f) and allows_array_function_override(f) + } + + +def get_covered_functions(ns: dict) -> set[FunctionType]: + """Identify all functions that are covered by tests. + + Looks through the namespace (typically, ``locals()``) for classes that + have ``test_`` members, and returns a set of the functions with + those names. By default, the names are looked up on `numpy`, but each + class can override this by having a ``tested_module`` attribute (e.g., + ``tested_module = np.linalg``). + + """ + covered = set() + for test_cls in filter(inspect.isclass, ns.values()): + module = getattr(test_cls, "tested_module", np) + covered |= { + function + for k, v in test_cls.__dict__.items() + if inspect.isfunction(v) + and k.startswith("test_") + and (function := getattr(module, k.replace("test_", ""), None)) is not None + } + return covered + + +class BasicTestSetup: + """Test setup for functions that should not change the unit. + + Also provides a default Quantity with shape (3, 3) and units of m. + """ + + def setup_method(self): + self.q = np.arange(9.0).reshape(3, 3) / 4.0 * u.m + + +class InvariantUnitTestSetup(BasicTestSetup): + def check(self, func, *args, **kwargs): + o = func(self.q, *args, **kwargs) + expected = func(self.q.value, *args, **kwargs) * self.q.unit + assert o.shape == expected.shape + assert np.all(o == expected) + + +class NoUnitTestSetup(BasicTestSetup): + def check(self, func, *args, **kwargs): + out = func(self.q, *args, **kwargs) + expected = func(self.q.value, *args, *kwargs) + assert type(out) is type(expected) + if isinstance(expected, tuple): + assert all(np.all(o == x) for o, x in zip(out, expected)) + else: + assert np.all(out == expected) + + +class TestShapeInformation(BasicTestSetup): + def test_shape(self): + assert np.shape(self.q) == (3, 3) + + def test_size(self): + assert np.size(self.q) == 9 + + def test_ndim(self): + assert np.ndim(self.q) == 2 + + +class TestShapeManipulation(InvariantUnitTestSetup): + # Note: do not parametrize the below, since test names are used + # to check coverage. + def test_reshape(self): + self.check(np.reshape, (9, 1)) + + def test_ravel(self): + self.check(np.ravel) + + def test_moveaxis(self): + self.check(np.moveaxis, 0, 1) + + def test_rollaxis(self): + self.check(np.rollaxis, 0, 2) + + def test_swapaxes(self): + self.check(np.swapaxes, 0, 1) + + def test_transpose(self): + self.check(np.transpose) + + def test_atleast_1d(self): + q = 1.0 * u.m + o, so = np.atleast_1d(q, self.q) + assert o.shape == (1,) + assert o == q + expected = np.atleast_1d(self.q.value) * u.m + assert np.all(so == expected) + + def test_atleast_2d(self): + q = 1.0 * u.m + o, so = np.atleast_2d(q, self.q) + assert o.shape == (1, 1) + assert o == q + expected = np.atleast_2d(self.q.value) * u.m + assert np.all(so == expected) + + def test_atleast_3d(self): + q = 1.0 * u.m + o, so = np.atleast_3d(q, self.q) + assert o.shape == (1, 1, 1) + assert o == q + expected = np.atleast_3d(self.q.value) * u.m + assert np.all(so == expected) + + def test_expand_dims(self): + self.check(np.expand_dims, 1) + + def test_squeeze(self): + o = np.squeeze(self.q[:, np.newaxis, :]) + assert o.shape == (3, 3) + assert np.all(o == self.q) + + def test_flip(self): + self.check(np.flip) + + def test_fliplr(self): + self.check(np.fliplr) + + def test_flipud(self): + self.check(np.flipud) + + def test_rot90(self): + self.check(np.rot90) + + def test_broadcast_to(self): + # Decided *not* to change default for subok for Quantity, since + # that would be contrary to the docstring and might break code. + self.check(np.broadcast_to, (3, 3, 3), subok=True) + out = np.broadcast_to(self.q, (3, 3, 3)) + assert type(out) is np.ndarray # NOT Quantity + + def test_broadcast_arrays(self): + # Decided *not* to change default for subok for Quantity, since + # that would be contrary to the docstring and might break code. + q2 = np.ones((3, 3, 3)) / u.s + o1, o2 = np.broadcast_arrays(self.q, q2, subok=True) + assert isinstance(o1, u.Quantity) + assert isinstance(o2, u.Quantity) + assert o1.shape == o2.shape == (3, 3, 3) + assert np.all(o1 == self.q) + assert np.all(o2 == q2) + a1, a2 = np.broadcast_arrays(self.q, q2) + assert type(a1) is np.ndarray + assert type(a2) is np.ndarray + + def test_matrix_transpose(self): + self.check(np.matrix_transpose) + + +class TestArgFunctions(NoUnitTestSetup): + def test_argmin(self): + self.check(np.argmin) + + def test_argmax(self): + self.check(np.argmax) + + def test_argsort(self): + self.check(np.argsort) + + def test_lexsort(self): + self.check(np.lexsort) + + def test_searchsorted(self): + q = self.q.ravel() + q2 = np.array([150.0, 350.0]) * u.cm + out = np.searchsorted(q, q2) + expected = np.searchsorted(q.value, q2.to_value(q.unit)) + assert np.all(out == expected) + + def test_nonzero(self): + self.check(np.nonzero) + + def test_argwhere(self): + self.check(np.argwhere) + + def test_argpartition(self): + self.check(np.argpartition, 2) + + def test_flatnonzero(self): + self.check(np.flatnonzero) + + +class TestAlongAxis(BasicTestSetup): + def test_take_along_axis(self): + indices = np.expand_dims(np.argmax(self.q, axis=0), axis=0) + out = np.take_along_axis(self.q, indices, axis=0) + expected = np.take_along_axis(self.q.value, indices, axis=0) * self.q.unit + assert np.all(out == expected) + + def test_put_along_axis(self): + q = self.q.copy() + indices = np.expand_dims(np.argmax(self.q, axis=0), axis=0) + np.put_along_axis(q, indices, axis=0, values=-100 * u.cm) + expected = q.value.copy() + np.put_along_axis(expected, indices, axis=0, values=-1) + expected = expected * q.unit + assert np.all(q == expected) + + @pytest.mark.parametrize("axis", (0, 1)) + def test_apply_along_axis(self, axis): + out = np.apply_along_axis(np.square, axis, self.q) + expected = np.apply_along_axis(np.square, axis, self.q.value) * self.q.unit**2 + assert_array_equal(out, expected) + + @pytest.mark.parametrize("axes", ((1,), (0,), (0, 1))) + def test_apply_over_axes(self, axes): + def function(x, axis): + return np.sum(np.square(x), axis) + + out = np.apply_over_axes(function, self.q, axes) + expected = np.apply_over_axes(function, self.q.value, axes) + expected = expected * self.q.unit ** (2 * len(axes)) + assert_array_equal(out, expected) + + +class TestIndicesFrom(NoUnitTestSetup): + def test_diag_indices_from(self): + self.check(np.diag_indices_from) + + def test_triu_indices_from(self): + self.check(np.triu_indices_from) + + def test_tril_indices_from(self): + self.check(np.tril_indices_from) + + +class TestRealImag(InvariantUnitTestSetup): + def setup_method(self): + self.q = (np.arange(9.0).reshape(3, 3) + 1j) * u.m + + def test_real(self): + self.check(np.real) + + def test_imag(self): + self.check(np.imag) + + +class TestCopyAndCreation(InvariantUnitTestSetup): + def test_copy(self): + self.check(np.copy) + # Also as kwarg + copy = np.copy(a=self.q) + assert_array_equal(copy, self.q) + + def test_empty_like(self): + o = np.empty_like(self.q) + assert o.shape == (3, 3) + assert isinstance(o, u.Quantity) + assert o.unit == self.q.unit + o2 = np.empty_like(prototype=self.q) + assert o2.shape == (3, 3) + assert isinstance(o2, u.Quantity) + assert o2.unit == self.q.unit + o3 = np.empty_like(self.q, subok=False) + assert type(o3) is np.ndarray + + def test_zeros_like(self): + self.check(np.zeros_like) + o2 = np.zeros_like(a=self.q) + assert_array_equal(o2, self.q * 0.0) + + def test_ones_like(self): + self.check(np.ones_like) + + def test_full_like(self): + o = np.full_like(self.q, 0.5 * u.km) + expected = np.empty_like(self.q.value) * u.m + expected[...] = 0.5 * u.km + assert np.all(o == expected) + with pytest.raises(u.UnitsError): + np.full_like(self.q, 0.5 * u.s) + + def test_astype(self): + int32q = self.q.astype("int32") + assert_array_equal(np.astype(int32q, "int32"), int32q) + + @pytest.mark.parametrize( + "args, kwargs, expected", + [ + pytest.param( + (1 * u.radian,), + {}, + np.arange(1, dtype=float), + id="pos: stop", + ), + pytest.param( + (0 * u.degree, 1 * u.radian), + {}, + np.arange(1, dtype=float), + id="pos: start, stop", + ), + pytest.param( + (0 * u.degree, 1 * u.radian, 1 * u.arcsec), + {}, + np.arange(ARCSEC_PER_RADIAN, dtype=float), + id="pos: start, stop, step", + ), + pytest.param( + (0 * u.degree, 1 * u.radian), + {"step": 1 * u.arcsec}, + np.arange(ARCSEC_PER_RADIAN, dtype=float), + id="pos: start, stop; kw: step", + ), + pytest.param( + (0 * u.radian,), + {"stop": 5 * u.radian}, + np.rad2deg(np.arange(5, dtype=float) * ARCSEC_PER_DEGREE), + id="pos: start; kw: stop", + ), + pytest.param( + (10,), + {"step": 2}, + np.arange(10, step=2, dtype=float) * ARCSEC_PER_DEGREE, + id="pos: stop; kw: step; unit from like", + ), + pytest.param( + (10 * u.radian, None), + {}, + np.rad2deg(np.arange(10, dtype=float) * ARCSEC_PER_DEGREE), + id="pos: stop, None", + ), + pytest.param( + (10 * u.radian, None, 1), + {}, + np.rad2deg(np.arange(10, dtype=float) * ARCSEC_PER_DEGREE), + id="pos: stop, None, 1", + ), + ], + ) + def test_arange(self, args, kwargs, expected): + arr = np.arange(*args, **kwargs, like=u.Quantity([], u.degree)) + assert type(arr) is u.Quantity + if any(hasattr(arg, "unit") for arg in itertools.chain(args, kwargs.keys())): + expected_unit = u.radian + else: + expected_unit = u.degree + assert arr.unit == expected_unit + assert arr.dtype == expected.dtype + assert_allclose(arr.to_value(u.arcsec), expected) + + def test_arange_like_quantity_subclass(self): + class AngularUnits(u.SpecificTypeQuantity): + _equivalent_unit = u.radian + + arr = np.arange( + 0 * u.radian, 10 * u.radian, 1 * u.radian, like=AngularUnits([], u.radian) + ) + assert type(arr) is AngularUnits + assert arr.unit == u.radian + assert arr.dtype == np.dtype(float) + assert_array_equal(arr.value, np.arange(10)) + + def test_arange_default_unit(self): + arr = np.arange(10, like=u.Quantity([], u.s)) + assert type(arr) is u.Quantity + assert arr.unit == u.s + + def test_arange_invalid_inputs(self): + with pytest.raises( + TypeError, + match="stop without a unit cannot be combined with start or step", + ): + np.arange(0 * u.radian, 10, like=u.Quantity([], u.s)) + + def test_arange_unit_from_stop(self): + Q = 1 * u.km + start = 1 * u.s + stop = 10 * u.min + a = np.arange(start, stop, like=Q) + b = np.arange(start, stop=stop, like=Q) + assert a.unit == stop.unit + assert b.unit == stop.unit + assert_array_equal(a.value, b.value) + + +class TestArrayCreation(BasicTestSetup): + def check(self, func, *args, skip_equality_check=False, **kwargs): + o = func(*args, **kwargs, like=self.q) + assert type(o) is type(self.q) + assert o.unit == self.q.unit + if skip_equality_check: + return + expected = func(*args, **kwargs) << self.q.unit + assert_array_equal(o, expected) + + def test_empty(self): + self.check(np.empty, (2, 2), skip_equality_check=True) + + def test_ones(self): + self.check(np.ones, (2, 2)) + + def test_zeros(self): + self.check(np.zeros, (2, 2)) + + def test_full(self): + self.check(np.full, (2, 2), 2) + + def test_full_unit_from_fill_value(self): + Q = 1 * u.km + arr1 = np.full((2, 2), 2, like=Q) + arr2 = np.full((2, 2), 2 * u.s, like=Q) + assert type(arr2) is u.Quantity + assert arr2.unit == u.s + assert_array_equal(arr2.value, arr1.value) + + def test_require(self): + self.check(np.require, np.arange(10)) + + def test_array(self): + self.check(np.array, np.arange(10)) + + def test_array_unit_from_data(self): + # check that input unit takes precedence over like arg + Q = np.arange(10) << u.km + arr2 = np.array(Q.value << u.cm, like=Q) + assert type(arr2) is u.Quantity + assert arr2.unit == u.cm + assert_array_equal(arr2.value, Q.value) + + def test_asarray(self): + self.check(np.asarray, np.arange(10)) + + def test_asanyarray(self): + self.check(np.asanyarray, np.arange(10)) + + def test_ascontiguousarray(self): + self.check(np.ascontiguousarray, np.arange(10)) + + def test_asfortranarray(self): + self.check(np.asfortranarray, np.arange(10)) + + def test_genfromtxt(self): + s = StringIO("1.0,2.0,3.0") + self.check(np.genfromtxt, s, delimiter=",", skip_equality_check=True) + + def test_loadtxt(self): + s = StringIO("0 1\n2 3") + self.check(np.loadtxt, s, skip_equality_check=True) + + def test_fromfile(self, tmp_path): + arr = np.arange(10) + test_file = tmp_path / "arr.npy" + arr.tofile(test_file) + self.check(np.fromfile, test_file) + + def test_frombuffer(self): + self.check(np.frombuffer, b"\x01\x02\x03", dtype=np.uint8) + + def test_fromfunction(self): + self.check(np.fromfunction, lambda i, j: i * j, (3, 3)) + + def test_fromfunction_unit_from_retv(self): + Q = [1, 2, 3] << u.cm + arr = np.fromfunction(lambda i, j: (i * j) << u.s, (3, 3), like=Q) + assert type(arr) is u.Quantity + assert arr.unit == u.s + + def test_fromiter(self): + it = (i * i for i in range(3)) + self.check( + np.fromiter, + it, + dtype=np.dtype((int, 3)), + # cannot generate another array after consuming the iterator + skip_equality_check=True, + ) + + def test_fromstring(self): + self.check(np.fromstring, "1 2 3", sep=" ") + + def test_identity(self): + self.check(np.identity, 3) + + def test_eye(self): + self.check(np.eye, 3) + + def test_tri(self): + self.check(np.tri, 3) + + +class TestAccessingParts(InvariantUnitTestSetup): + def test_diag(self): + self.check(np.diag) + + def test_diag_1d_input(self): + # Also check 1-D case; drops unit w/o __array_function__. + q = self.q.ravel() + o = np.diag(q) + expected = np.diag(q.value) << q.unit + assert o.unit == self.q.unit + assert o.shape == expected.shape + assert_array_equal(o, expected) + + def test_diagonal(self): + self.check(np.diagonal) + + def test_diagflat(self): + self.check(np.diagflat) + + def test_compress(self): + o = np.compress([True, False, True], self.q, axis=0) + expected = np.compress([True, False, True], self.q.value, axis=0) * self.q.unit + assert np.all(o == expected) + + def test_extract(self): + o = np.extract([True, False, True], self.q) + expected = np.extract([True, False, True], self.q.value) * self.q.unit + assert np.all(o == expected) + + def test_delete(self): + self.check(np.delete, slice(1, 2), 0) + self.check(np.delete, [0, 2], 1) + + def test_trim_zeros(self): + q = self.q.ravel() + out = np.trim_zeros(q) + expected = np.trim_zeros(q.value) * u.m + assert np.all(out == expected) + + def test_roll(self): + self.check(np.roll, 1) + self.check(np.roll, 1, axis=0) + + def test_take(self): + self.check(np.take, [0, 1], axis=1) + self.check(np.take, 1) + + +class TestSettingParts: + def test_put(self): + q = np.arange(3.0) * u.m + np.put(q, [0, 2], [50, 150] * u.cm) + assert q.unit == u.m + expected = [50, 100, 150] * u.cm + assert np.all(q == expected) + + def test_putmask(self): + q = np.arange(3.0) * u.m + mask = [True, False, True] + values = [50, 0, 150] * u.cm + np.putmask(q, mask, values) + assert q.unit == u.m + expected = [50, 100, 150] * u.cm + assert np.all(q == expected) + with pytest.raises(u.UnitsError): + np.putmask(q, mask, values.value) + with pytest.raises(u.UnitsError): + np.putmask(q.value, mask, values) + a = np.arange(3.0) + values = [50, 0, 150] * u.percent + np.putmask(a, mask, values) + expected = np.array([0.5, 1.0, 1.5]) + assert np.all(a == expected) + + def test_place(self): + q = np.arange(3.0) * u.m + np.place(q, [True, False, True], [50, 150] * u.cm) + assert q.unit == u.m + expected = [50, 100, 150] * u.cm + assert np.all(q == expected) + + a = np.arange(3.0) + np.place(a, [True, False, True], [50, 150] * u.percent) + assert type(a) is np.ndarray + expected = np.array([0.5, 1.0, 1.5]) + assert np.all(a == expected) + + def test_copyto(self): + q = np.arange(3.0) * u.m + np.copyto(q, [50, 0, 150] * u.cm, where=[True, False, True]) + assert q.unit == u.m + expected = [50, 100, 150] * u.cm + assert np.all(q == expected) + + a = np.arange(3.0) + np.copyto(a, [50, 0, 150] * u.percent, where=[True, False, True]) + assert type(a) is np.ndarray + expected = np.array([0.5, 1.0, 1.5]) + assert np.all(a == expected) + + def test_fill_diagonal(self): + q = np.arange(9.0).reshape(3, 3) * u.m + expected = q.value.copy() + np.fill_diagonal(expected, 0.25) + expected = expected * u.m + np.fill_diagonal(q, 25.0 * u.cm) + assert q.unit == u.m + assert np.all(q == expected) + + +class TestRepeat(InvariantUnitTestSetup): + def test_tile(self): + self.check(np.tile, 2) + + def test_repeat(self): + self.check(np.repeat, 2) + + def test_resize(self): + self.check(np.resize, (4, 4)) + + +class TestConcatenate: + def setup_method(self): + self.q1 = np.arange(6.0).reshape(2, 3) * u.m + self.q2 = self.q1.to(u.cm) + + def check(self, func, *args, **kwargs): + q_list = kwargs.pop("q_list", [self.q1, self.q2]) + q_ref = kwargs.pop("q_ref", q_list[0]) + o = func(q_list, *args, **kwargs) + v_list = [q_ref._to_own_unit(q) for q in q_list] + expected = func(v_list, *args, **kwargs) * q_ref.unit + assert o.shape == expected.shape + assert np.all(o == expected) + + def test_concatenate(self): + self.check(np.concatenate) + self.check(np.concatenate, axis=1) + # regression test for gh-13322. + self.check(np.concatenate, dtype="f4") + + self.check( + np.concatenate, + q_list=[np.zeros(self.q1.shape), self.q1, self.q2], + q_ref=self.q1, + ) + + out = np.empty((4, 3)) * u.dimensionless_unscaled + result = np.concatenate([self.q1, self.q2], out=out) + assert out is result + assert out.unit == self.q1.unit + expected = ( + np.concatenate([self.q1.value, self.q2.to_value(self.q1.unit)]) + * self.q1.unit + ) + assert np.all(result == expected) + + with pytest.raises(TypeError): + np.concatenate([self.q1, object()]) + + def test_stack(self): + self.check(np.stack) + + def test_column_stack(self): + self.check(np.column_stack) + + def test_hstack(self): + self.check(np.hstack) + + def test_vstack(self): + self.check(np.vstack) + + def test_dstack(self): + self.check(np.dstack) + + def test_block(self): + self.check(np.block) + + result = np.block([[0.0, 1.0 * u.m], [1.0 * u.cm, 2.0 * u.km]]) + assert np.all(result == np.block([[0, 1.0], [0.01, 2000.0]]) << u.m) + + def test_append(self): + out = np.append(self.q1, self.q2, axis=0) + assert out.unit == self.q1.unit + expected = ( + np.append(self.q1.value, self.q2.to_value(self.q1.unit), axis=0) + * self.q1.unit + ) + assert np.all(out == expected) + + a = np.arange(3.0) + result = np.append(a, 50.0 * u.percent) + assert isinstance(result, u.Quantity) + assert result.unit == u.dimensionless_unscaled + expected = np.append(a, 0.5) * u.dimensionless_unscaled + assert np.all(result == expected) + + def test_insert(self): + # Unit of inserted values is not ignored. + q = np.arange(12.0).reshape(6, 2) * u.m + out = np.insert(q, (3, 5), [50.0, 25.0] * u.cm) + assert isinstance(out, u.Quantity) + assert out.unit == q.unit + expected = np.insert(q.value, (3, 5), [0.5, 0.25]) << q.unit + assert np.all(out == expected) + # 0 can have any unit. + out2 = np.insert(q, (3, 5), 0) + expected2 = np.insert(q.value, (3, 5), 0) << q.unit + assert np.all(out2 == expected2) + + a = np.arange(3.0) + result = np.insert(a, (2,), 50.0 * u.percent) + assert isinstance(result, u.Quantity) + assert result.unit == u.dimensionless_unscaled + expected = np.insert(a, (2,), 0.5) * u.dimensionless_unscaled + assert np.all(result == expected) + + with pytest.raises(TypeError): + np.insert(q, 3 * u.cm, 50.0 * u.cm) + with pytest.raises(u.UnitsError): + np.insert(q, (3, 5), 0.0 * u.s) + + def test_pad(self): + q = np.arange(1.0, 6.0) * u.m + out = np.pad(q, (2, 3), "constant", constant_values=(0.0, 150.0 * u.cm)) + assert out.unit == q.unit + expected = ( + np.pad(q.value, (2, 3), "constant", constant_values=(0.0, 1.5)) * q.unit + ) + assert np.all(out == expected) + out2 = np.pad(q, (2, 3), "constant", constant_values=150.0 * u.cm) + assert out2.unit == q.unit + expected2 = np.pad(q.value, (2, 3), "constant", constant_values=1.5) * q.unit + assert np.all(out2 == expected2) + out3 = np.pad(q, (2, 3), "linear_ramp", end_values=(25.0 * u.cm, 0.0)) + assert out3.unit == q.unit + expected3 = ( + np.pad(q.value, (2, 3), "linear_ramp", end_values=(0.25, 0.0)) * q.unit + ) + assert np.all(out3 == expected3) + + +class TestSplit: + def setup_method(self): + self.q = np.arange(54.0).reshape(3, 3, 6) * u.m + + def check(self, func, *args, **kwargs): + out = func(self.q, *args, **kwargs) + expected = func(self.q.value, *args, **kwargs) + expected = [x * self.q.unit for x in expected] + assert len(out) == len(expected) + assert all(o.shape == x.shape for o, x in zip(out, expected)) + assert all(np.all(o == x) for o, x in zip(out, expected)) + + def test_split(self): + self.check(np.split, [1]) + + def test_array_split(self): + self.check(np.array_split, 2) + + def test_hsplit(self): + self.check(np.hsplit, [1, 4]) + + def test_vsplit(self): + self.check(np.vsplit, [1]) + + def test_dsplit(self): + self.check(np.dsplit, [1]) + + @pytest.mark.skipif(NUMPY_LT_2_1, reason="np.unstack is new in Numpy 2.1") + def test_unstack(self): + self.check(np.unstack) + + +class TestUfuncReductions(InvariantUnitTestSetup): + def test_max(self): + self.check(np.max) + + def test_min(self): + self.check(np.min) + + def test_amax(self): + self.check(np.amax) + + def test_amin(self): + self.check(np.amin) + + def test_sum(self): + self.check(np.sum) + + def test_cumsum(self): + self.check(np.cumsum) + + @pytest.mark.skipif(NUMPY_LT_2_1, reason="np.cumulative_sum is new in NumPy 2.1") + def test_cumulative_sum(self): + self.check(np.cumulative_sum, axis=1) + + def test_any(self): + with pytest.raises(TypeError): + np.any(self.q) + + def test_all(self): + with pytest.raises(TypeError): + np.all(self.q) + + def test_prod(self): + with pytest.raises(u.UnitsError): + np.prod(self.q) + + def test_cumprod(self): + with pytest.raises(u.UnitsError): + np.cumprod(self.q) + + @pytest.mark.skipif(NUMPY_LT_2_1, reason="np.cumulative_prod is new in NumPy 2.1") + def test_cumulative_prod(self): + with pytest.raises(u.UnitsError): + np.cumulative_prod(self.q, axis=1) + + +class TestUfuncLike(InvariantUnitTestSetup): + def test_ptp(self): + self.check(np.ptp) + self.check(np.ptp, axis=0) + + def test_round(self): + self.check(np.round) + + def test_around(self): + self.check(np.around) + + @pytest.mark.filterwarnings("ignore:numpy.fix is deprecated:DeprecationWarning") + def test_fix(self): + self.check(np.fix) + + def test_angle(self): + q = np.array([1 + 0j, 0 + 1j, 1 + 1j, 0 + 0j]) * u.m + out = np.angle(q) + expected = np.angle(q.value) * u.radian + assert np.all(out == expected) + + def test_i0(self): + q = np.array([0.0, 10.0, 20.0]) * u.percent + out = np.i0(q) + expected = np.i0(q.to_value(u.one)) * u.one + assert isinstance(out, u.Quantity) + assert np.all(out == expected) + with pytest.raises(u.UnitsError): + np.i0(self.q) + + def test_clip(self): + qmin = 200 * u.cm + qmax = [270, 280, 290] * u.cm + out = np.clip(self.q, qmin, qmax) + unit = self.q.unit + expected = ( + np.clip(self.q.value, qmin.to_value(unit), qmax.to_value(unit)) * unit + ) + assert np.all(out == expected) + + def test_sinc(self): + q = [0.0, 3690.0, -270.0, 690.0] * u.deg + out = np.sinc(q) + expected = np.sinc(q.to_value(u.radian)) * u.one + assert isinstance(out, u.Quantity) + assert np.all(out == expected) + with pytest.raises(u.UnitsError): + np.sinc(1.0 * u.one) + + def test_where(self): + out = np.where([True, False, True], self.q, 1.0 * u.km) + expected = np.where([True, False, True], self.q.value, 1000.0) * self.q.unit + assert np.all(out == expected) + + def test_choose(self): + # from np.choose docstring + a = np.array([0, 1]).reshape((2, 1, 1)) + q1 = np.array([1, 2, 3]).reshape((1, 3, 1)) * u.cm + q2 = np.array([-1, -2, -3, -4, -5]).reshape((1, 1, 5)) * u.m + out = np.choose(a, (q1, q2)) + # result is 2x3x5, res[0,:,:]=c1, res[1,:,:]=c2 + expected = np.choose(a, (q1.value, q2.to_value(q1.unit))) * u.cm + assert np.all(out == expected) + + def test_select(self): + q = self.q + out = np.select( + [q < 0.55 * u.m, q > 1.0 * u.m], [q, q.to(u.cm)], default=-1.0 * u.km + ) + expected = ( + np.select([q.value < 0.55, q.value > 1], [q.value, q.value], default=-1000) + * u.m + ) + assert np.all(out == expected) + + def test_real_if_close(self): + q = np.array([1 + 0j, 0 + 1j, 1 + 1j, 0 + 0j]) * u.m + out = np.real_if_close(q) + expected = np.real_if_close(q.value) * u.m + assert np.all(out == expected) + + def test_tril(self): + self.check(np.tril) + + def test_triu(self): + self.check(np.triu) + + def test_unwrap(self): + q = [0.0, 3690.0, -270.0, 690.0] * u.deg + out = np.unwrap(q) + expected = (np.unwrap(q.to_value(u.rad)) * u.rad).to(q.unit) + assert out.unit == expected.unit + assert np.allclose(out, expected, atol=1 * u.urad, rtol=0) + with pytest.raises(u.UnitsError): + np.unwrap([1.0, 2.0] * u.m) + with pytest.raises(u.UnitsError): + np.unwrap(q, discont=1.0 * u.m) + + def test_nan_to_num(self): + q = np.array([-np.inf, +np.inf, np.nan, 3.0, 4.0]) * u.m + out = np.nan_to_num(q) + expected = np.nan_to_num(q.value) * q.unit + assert np.all(out == expected) + + def test_nan_to_num_complex(self): + q = np.array([-np.inf, +np.inf, np.nan, 3.0, 4.0]) * u.m + out = np.nan_to_num(q, nan=1.0 * u.km, posinf=2.0 * u.km, neginf=-2 * u.km) + expected = [-2000.0, 2000.0, 1000.0, 3.0, 4.0] * u.m + assert np.all(out == expected) + + +class TestUfuncLikeTests: + def setup_method(self): + self.q = np.array([-np.inf, +np.inf, np.nan, 3.0, 4.0]) * u.m + + def check(self, func): + out = func(self.q) + expected = func(self.q.value) + assert type(out) is np.ndarray + assert out.dtype.kind == "b" + assert np.all(out == expected) + + def test_isposinf(self): + self.check(np.isposinf) + + def test_isneginf(self): + self.check(np.isneginf) + + def test_isreal(self): + self.check(np.isreal) + assert not np.isreal([1.0 + 1j] * u.m) + + def test_iscomplex(self): + self.check(np.iscomplex) + assert np.iscomplex([1.0 + 1j] * u.m) + + def test_isclose(self): + q1 = np.arange(3.0) * u.m + q2 = np.array([0.0, 102.0, 199.0]) * u.cm + atol = 1.5 * u.cm + rtol = 1.0 * u.percent + out = np.isclose(q1, q2, atol=atol) + expected = np.isclose( + q1.value, q2.to_value(q1.unit), atol=atol.to_value(q1.unit) + ) + assert type(out) is np.ndarray + assert out.dtype.kind == "b" + assert np.all(out == expected) + out = np.isclose(q1, q2, atol=0, rtol=rtol) + expected = np.isclose(q1.value, q2.to_value(q1.unit), atol=0, rtol=0.01) + assert type(out) is np.ndarray + assert out.dtype.kind == "b" + assert np.all(out == expected) + + def test_allclose_atol_default_unit(self): + q_cm = self.q.to(u.cm) + out = np.isclose(self.q, q_cm) + expected = np.isclose(self.q.value, q_cm.to_value(u.m)) + assert np.all(out == expected) + q1 = np.arange(3.0) * u.m + q2 = np.array([0.0, 101.0, 198.0]) * u.cm + out = np.isclose(q1, q2, atol=0.011, rtol=0) + expected = np.isclose(q1.value, q2.to_value(q1.unit), atol=0.011, rtol=0) + assert np.all(out == expected) + out2 = np.isclose(q2, q1, atol=0.011, rtol=0) + expected2 = np.isclose(q2.value, q1.to_value(q2.unit), atol=0.011, rtol=0) + assert np.all(out2 == expected2) + + +class TestReductionLikeFunctions(InvariantUnitTestSetup): + def test_average(self): + q1 = np.arange(9.0).reshape(3, 3) * u.m + q2 = np.eye(3) / u.s + o = np.average(q1, weights=q2) + expected = np.average(q1.value, weights=q2.value) * u.m + assert np.all(o == expected) + + def test_average_no_weights(self): + q1 = np.arange(9.0).reshape(3, 3) * u.m + o = np.average(q1) + expected = np.average(q1.value) * u.m + assert np.all(o == expected) + + def test_average_a_and_weights_different_shapes_and_returned(self): + q1 = np.arange(9.0).reshape(3, 3) * u.m + q2 = np.ones(3) / u.s + avg, wsum = np.average(q1, weights=q2, axis=1, returned=True) + expected_avg, expected_wsum = np.average( + q1.value, weights=q2.value, axis=1, returned=True + ) + expected_avg *= u.m + expected_wsum /= u.s + assert np.all(avg == expected_avg) + assert np.all(wsum == expected_wsum) + + def test_mean(self): + self.check(np.mean) + + def test_std(self): + self.check(np.std) + + def test_var(self): + o = np.var(self.q) + expected = np.var(self.q.value) * self.q.unit**2 + assert np.all(o == expected) + + def test_median(self): + self.check(np.median) + + def test_median_nan_scalar(self): + # See gh-12165; this dropped the unit in numpy < 1.22 + data = [1.0, 2, np.nan, 3, 4] << u.km + result = np.median(data) + assert_array_equal(result, np.nan * u.km) + + def test_quantile(self): + self.check(np.quantile, 0.5) + o = np.quantile(self.q, 50 * u.percent) + expected = np.quantile(self.q.value, 0.5) * u.m + assert np.all(o == expected) + # For ndarray input, we return a Quantity. + o2 = np.quantile(self.q.value, 50 * u.percent) + assert o2.unit == u.dimensionless_unscaled + assert np.all(o2 == expected.value) + o3 = 0 * o2 + result = np.quantile(self.q, 50 * u.percent, out=o3) + assert result is o3 + assert np.all(o3 == expected) + o4 = 0 * o2 + result = np.quantile(self.q, 50 * u.percent, None, o4) + assert result is o4 + assert np.all(o4 == expected) + + def test_percentile(self): + self.check(np.percentile, 0.5) + o = np.percentile(self.q, 0.5 * u.one) + expected = np.percentile(self.q.value, 50) * u.m + assert np.all(o == expected) + + def test_trace(self): + self.check(np.trace) + + def test_count_nonzero(self): + q1 = np.arange(9.0).reshape(3, 3) * u.m + o = np.count_nonzero(q1) + assert type(o) is not u.Quantity + assert o == 8 + o = np.count_nonzero(q1, axis=1) + # Returns integer Quantity with units of m + assert type(o) is np.ndarray + assert np.all(o == np.array([2, 3, 3])) + + def test_allclose(self): + q1 = np.arange(3.0) * u.m + q2 = np.array([0.0, 101.0, 199.0]) * u.cm + atol = 2 * u.cm + rtol = 1.0 * u.percent + assert np.allclose(q1, q2, atol=atol) + assert np.allclose(q1, q2, atol=0.0, rtol=rtol) + + def test_allclose_atol_default_unit(self): + q1 = np.arange(3.0) * u.m + q2 = np.array([0.0, 101.0, 199.0]) * u.cm + assert np.allclose(q1, q2, atol=0.011, rtol=0) + assert not np.allclose(q2, q1, atol=0.011, rtol=0) + + def test_allclose_failures(self): + q1 = np.arange(3.0) * u.m + q2 = np.array([0.0, 101.0, 199.0]) * u.cm + with pytest.raises(u.UnitsError): + np.allclose(q1, q2, atol=2 * u.s, rtol=0) + with pytest.raises(u.UnitsError): + np.allclose(q1, q2, atol=0, rtol=1.0 * u.s) + + def test_array_equal(self): + q1 = np.arange(3.0) * u.m + q2 = q1.to(u.cm) + assert np.array_equal(q1, q2) + q3 = q1.value * u.cm + assert not np.array_equal(q1, q3) + + @pytest.mark.parametrize("equal_nan", [False, True]) + def test_array_equal_nan(self, equal_nan): + q1 = np.linspace(0, 1, num=11) * u.m + q1[0] = np.nan + q2 = q1.to(u.cm) + result = np.array_equal(q1, q2, equal_nan=equal_nan) + assert result == equal_nan + + def test_array_equal_incompatible_units(self): + assert not np.array_equal([1, 2] * u.m, [1, 2] * u.s) + + def test_array_equiv(self): + q1 = np.array([[0.0, 1.0, 2.0]] * 3) * u.m + q2 = q1[0].to(u.cm) + assert np.array_equiv(q1, q2) + q3 = q1[0].value * u.cm + assert not np.array_equiv(q1, q3) + + def test_array_equiv_incompatible_units(self): + assert not np.array_equiv([1, 1] * u.m, [1] * u.s) + + +class TestNanFunctions(InvariantUnitTestSetup): + def setup_method(self): + super().setup_method() + self.q[1, 1] = np.nan + + def test_nanmax(self): + self.check(np.nanmax) + + def test_nanmin(self): + self.check(np.nanmin) + + def test_nanargmin(self): + out = np.nanargmin(self.q) + expected = np.nanargmin(self.q.value) + assert out == expected + + def test_nanargmax(self): + out = np.nanargmax(self.q) + expected = np.nanargmax(self.q.value) + assert out == expected + + def test_nanmean(self): + self.check(np.nanmean) + + @pytest.mark.parametrize("axis", [None, 0, 1, -1]) + def test_nanmedian(self, axis): + self.check(np.nanmedian, axis=axis) + + def test_nanmedian_out(self): + out = np.empty_like(self.q) + o = np.nanmedian(self.q, out=out) + assert o is out + assert np.all(o == np.nanmedian(self.q)) + + def test_nansum(self): + self.check(np.nansum) + + def test_nancumsum(self): + self.check(np.nancumsum) + + def test_nanstd(self): + self.check(np.nanstd) + + @pytest.mark.parametrize( + "out_init", + [ + pytest.param(u.Quantity(-1, "m"), id="out with correct unit"), + # this should work too: out.unit will be overridden + pytest.param(u.Quantity(-1), id="out with a different unit"), + ], + ) + def test_nanstd_out(self, out_init): + out = out_init.copy() + o = np.nanstd(self.q, out=out) + assert o is out + assert o == np.nanstd(self.q) + + # Also check array input, Quantity output. + out = out_init.copy() + o2 = np.nanstd(self.q.value, out=out) + assert o2 is out + assert o2.unit == u.dimensionless_unscaled + assert o2 == np.nanstd(self.q.value) + + def test_nanvar(self): + out = np.nanvar(self.q) + expected = np.nanvar(self.q.value) * self.q.unit**2 + assert np.all(out == expected) + + @pytest.mark.parametrize( + "out_init", + [ + pytest.param(u.Quantity(-1, "m"), id="out with correct unit"), + # this should work too: out.unit will be overridden + pytest.param(u.Quantity(-1), id="out with a different unit"), + ], + ) + def test_nanvar_out(self, out_init): + out = out_init.copy() + o = np.nanvar(self.q, out=out) + assert o is out + assert o == np.nanvar(self.q) + + # Also check array input, Quantity output. + out = out_init.copy() + o2 = np.nanvar(self.q.value, out=out) + assert o2 is out + assert o2.unit == u.dimensionless_unscaled + assert o2 == np.nanvar(self.q.value) + + def test_nanprod(self): + with pytest.raises(u.UnitsError): + np.nanprod(self.q) + + def test_nancumprod(self): + with pytest.raises(u.UnitsError): + np.nancumprod(self.q) + + def test_nanquantile(self): + self.check(np.nanquantile, 0.5) + o = np.nanquantile(self.q, 50 * u.percent) + expected = np.nanquantile(self.q.value, 0.5) * u.m + assert np.all(o == expected) + + def test_nanpercentile(self): + self.check(np.nanpercentile, 0.5) + o = np.nanpercentile(self.q, 0.5 * u.one) + expected = np.nanpercentile(self.q.value, 50) * u.m + assert np.all(o == expected) + + +class TestVariousProductFunctions: + """ + Test functions that are similar to gufuncs + """ + + def test_cross(self): + q1 = np.arange(6.0).reshape(2, 3) * u.m + q2 = np.array([4.0, 5.0, 6.0]) / u.s + o = np.cross(q1, q2) + expected = np.cross(q1.value, q2.value) * u.m / u.s + assert np.all(o == expected) + + def test_outer(self): + q1 = np.array([1, 2, 3]) * u.m + q2 = np.array([1, 2]) / u.s + o = np.outer(q1, q2) + assert np.all(o == np.array([[1, 2], [2, 4], [3, 6]]) * u.m / u.s) + + o2 = 0 * o + result = np.outer(q1, q2, out=o2) + assert result is o2 + assert np.all(o2 == o) + + with pytest.raises(TypeError): + np.outer(q1, q2, out=object()) + + def test_inner(self): + q1 = np.array([1, 2, 3]) * u.m + q2 = np.array([4, 5, 6]) / u.s + o = np.inner(q1, q2) + assert o == 32 * u.m / u.s + + def test_dot(self): + q1 = np.array([1.0, 2.0, 3.0]) * u.m + q2 = np.array([4.0, 5.0, 6.0]) / u.s + o = np.dot(q1, q2) + assert o == 32.0 * u.m / u.s + + def test_vdot(self): + q1 = np.array([1j, 2j, 3j]) * u.m + q2 = np.array([4j, 5j, 6j]) / u.s + o = np.vdot(q1, q2) + assert o == (32.0 + 0j) * u.m / u.s + + def test_tensordot(self): + # From the docstring example + a = np.arange(60.0).reshape(3, 4, 5) * u.m + b = np.arange(24.0).reshape(4, 3, 2) / u.s + c = np.tensordot(a, b, axes=([1, 0], [0, 1])) + expected = np.tensordot(a.value, b.value, axes=([1, 0], [0, 1])) * u.m / u.s + assert np.all(c == expected) + + def test_kron(self): + q1 = np.eye(2) * u.m + q2 = np.ones(2) / u.s + o = np.kron(q1, q2) + expected = np.kron(q1.value, q2.value) * u.m / u.s + assert np.all(o == expected) + + def test_einsum(self): + q1 = np.arange(9.0).reshape(3, 3) * u.m + o = np.einsum("...i", q1) + assert np.all(o == q1) + o = np.einsum("ii", q1) + expected = np.einsum("ii", q1.value) * u.m + assert np.all(o == expected) + q2 = np.eye(3) / u.s + o2 = np.einsum("ij,jk", q1, q2) + assert np.all(o2 == q1 / u.s) + o3 = 0 * o2 + result = np.einsum("ij,jk", q1, q2, out=o3) + assert result is o3 + assert np.all(o3 == o2) + + def test_einsum_path(self): + q1 = np.arange(9.0).reshape(3, 3) * u.m + o = np.einsum_path("...i", q1) + assert o[0] == ["einsum_path", (0,)] + o = np.einsum_path("ii", q1) + assert o[0] == ["einsum_path", (0,)] + q2 = np.eye(3) / u.s + o = np.einsum_path("ij,jk", q1, q2) + assert o[0] == ["einsum_path", (0, 1)] + + +class TestIntDiffFunctions: + def check_trapezoid(self, func): + y = np.arange(9.0) * u.m / u.s + out = func(y) + expected = func(y.value) * y.unit + assert np.all(out == expected) + + dx = 10.0 * u.s + out = func(y, dx=dx) + expected = func(y.value, dx=dx.value) * y.unit * dx.unit + assert np.all(out == expected) + + x = np.arange(9.0) * u.s + out = func(y, x) + expected = func(y.value, x.value) * y.unit * x.unit + assert np.all(out == expected) + + def test_trapezoid(self): + self.check_trapezoid(np.trapezoid) + + def test_diff(self): + # Simple diff works out of the box. + x = np.arange(10.0) * u.m + out = np.diff(x) + expected = np.diff(x.value) * u.m + assert np.all(out == expected) + + def test_diff_prepend_append(self): + x = np.arange(10.0) * u.m + out = np.diff(x, prepend=-12.5 * u.cm, append=1 * u.km) + expected = np.diff(x.value, prepend=-0.125, append=1000.0) * x.unit + assert np.all(out == expected) + x = np.arange(10.0) * u.m + out = np.diff(x, prepend=-12.5 * u.cm, append=1 * u.km, n=2) + expected = np.diff(x.value, prepend=-0.125, append=1000.0, n=2) * x.unit + assert np.all(out == expected) + + with pytest.raises(TypeError): + np.diff(x, prepend=object()) + + def test_gradient(self): + # Simple gradient works out of the box. + x = np.arange(10.0) * u.m + out = np.gradient(x) + expected = np.gradient(x.value) * u.m + assert np.all(out == expected) + + def test_gradient_spacing(self): + # Simple gradient works out of the box. + x = np.arange(10.0) * u.m + spacing = 10.0 * u.s + out = np.gradient(x, spacing) + expected = np.gradient(x.value, spacing.value) * (x.unit / spacing.unit) + assert np.all(out == expected) + f = np.array([[1, 2, 6], [3, 4, 5]]) * u.m + dx = 2.0 * u.s + y = [1.0, 1.5, 3.5] * u.GHz + dfdx, dfdy = np.gradient(f, dx, y) + exp_dfdx, exp_dfdy = np.gradient(f.value, dx.value, y.value) + exp_dfdx = exp_dfdx * f.unit / dx.unit + exp_dfdy = exp_dfdy * f.unit / y.unit + assert np.all(dfdx == exp_dfdx) + assert np.all(dfdy == exp_dfdy) + + dfdx2 = np.gradient(f, dx, axis=0) + assert np.all(dfdx2 == exp_dfdx) + dfdy2 = np.gradient(f, y, axis=(1,)) + assert np.all(dfdy2 == exp_dfdy) + + +class TestSpaceFunctions: + def test_linspace(self): + # Note: linspace gets unit of end point, not superlogical. + out = np.linspace(1000.0 * u.m, 10.0 * u.km, 5) + expected = np.linspace(1, 10, 5) * u.km + assert np.all(out == expected) + + q1 = np.arange(6.0).reshape(2, 3) * u.m + q2 = 10000.0 * u.cm + out = np.linspace(q1, q2, 5) + expected = np.linspace(q1.to_value(q2.unit), q2.value, 5) * q2.unit + assert np.all(out == expected) + + def test_logspace(self): + unit = u.m / u.s**2 + out = np.logspace(10.0 * u.dex(unit), 20 * u.dex(unit), 10) + expected = np.logspace(10.0, 20.0, 10) * unit + assert np.all(out == expected) + out = np.logspace(10.0 * u.STmag, 20 * u.STmag, 10) + expected = np.logspace(10.0, 20.0, 10, base=10.0 ** (-0.4)) * u.ST + assert u.allclose(out, expected) + + def test_geomspace(self): + out = np.geomspace(1000.0 * u.m, 10.0 * u.km, 5) + expected = np.geomspace(1, 10, 5) * u.km + assert np.all(out == expected) + + q1 = np.arange(1.0, 7.0).reshape(2, 3) * u.m + q2 = 10000.0 * u.cm + out = np.geomspace(q1, q2, 5) + expected = np.geomspace(q1.to_value(q2.unit), q2.value, 5) * q2.unit + assert np.all(out == expected) + + +class TestInterpolationFunctions: + def test_interp(self): + x = np.array([1250.0, 2750.0]) * u.m + xp = np.arange(5.0) * u.km + yp = np.arange(5.0) * u.day + out = np.interp(x, xp, yp) + expected = np.interp(x.to_value(xp.unit), xp.value, yp.value) * yp.unit + assert np.all(out == expected) + + out = np.interp(x, xp, yp.value) + assert type(out) is np.ndarray + assert np.all(out == expected.value) + + def test_piecewise(self): + x = np.linspace(-2.5, 2.5, 6) * u.m + out = np.piecewise(x, [x < 0, x >= 0], [-1 * u.s, 1 * u.day]) + expected = ( + np.piecewise(x.value, [x.value < 0, x.value >= 0], [-1, 24 * 3600]) * u.s + ) + assert out.unit == expected.unit + assert np.all(out == expected) + + out2 = np.piecewise( + x, [x < 1 * u.m, x >= 0], [-1 * u.s, 1 * u.day, lambda x: 1 * u.hour] + ) + expected2 = ( + np.piecewise(x.value, [x.value < 1, x.value >= 0], [-1, 24 * 3600, 3600]) + * u.s + ) + assert out2.unit == expected2.unit + assert np.all(out2 == expected2) + + out3 = np.piecewise( + x, [x < 1 * u.m, x >= 0], [0, 1 * u.percent, lambda x: 1 * u.one] + ) + expected3 = ( + np.piecewise(x.value, [x.value < 1, x.value >= 0], [0, 0.01, 1]) * u.one + ) + assert out3.unit == expected3.unit + assert np.all(out3 == expected3) + + with pytest.raises(TypeError): # no Quantity in condlist. + np.piecewise(x, [x], [0.0]) + + with pytest.raises(TypeError): # no Quantity in condlist. + np.piecewise(x.value, [x], [0.0]) + + +class TestBincountDigitize: + def test_bincount(self): + i = np.array([1, 1, 2, 3, 2, 4]) + weights = np.arange(len(i)) * u.Jy + out = np.bincount(i, weights) + expected = np.bincount(i, weights.value) * weights.unit + assert_array_equal(out, expected) + + with pytest.raises(TypeError): + np.bincount(weights) + + def test_digitize(self): + x = np.array([1500.0, 2500.0, 4500.0]) * u.m + bins = np.arange(10.0) * u.km + out = np.digitize(x, bins) + expected = np.digitize(x.to_value(bins.unit), bins.value) + assert_array_equal(out, expected) + + +class TestHistogramFunctions: + def setup_method(self): + self.x = np.array([1.1, 1.2, 1.3, 2.1, 5.1]) * u.m + self.y = np.array([1.2, 2.2, 2.4, 3.0, 4.0]) * u.cm + self.weights = np.arange(len(self.x)) / u.s + + def check( + self, + function, + *args, + value_args=None, + value_kwargs=None, + expected_units=None, + **kwargs, + ): + """Check quanties are treated correctly in the histogram function. + Test is done by applying ``function(*args, **kwargs)``, where + the argument can be quantities, and comparing the result to + ``function(*value_args, **value_kwargs)``, with the outputs + converted to quantities using the ``expected_units`` (where `None` + indicates the output is expected to be a regular array). + + For ``**value_kwargs``, any regular ``kwargs`` are treated as + defaults, i.e., non-quantity arguments do not have to be repeated. + """ + if value_kwargs is None: + value_kwargs = kwargs + else: + for k, v in kwargs.items(): + value_kwargs.setdefault(k, v) + # Get the result, using the Quantity override. + out = function(*args, **kwargs) + # Get the comparison, with non-Quantity arguments. + expected = function(*value_args, **value_kwargs) + # All histogram functions return a tuple of the actual histogram + # and the bin edges. First, check the actual histogram. + out_h = out[0] + expected_h = expected[0] + if expected_units[0] is not None: + expected_h = expected_h * expected_units[0] + assert_array_equal(out_h, expected_h) + # Check bin edges. Here, histogramdd returns an iterable of the + # bin edges as the second return argument, while histogram and + # histogram2d return the bin edges directly. + if function is np.histogramdd: + bin_slice = 1 + else: + bin_slice = slice(1, None) + + for o_bin, e_bin, e_unit in zip( + out[bin_slice], expected[bin_slice], expected_units[bin_slice] + ): + if e_unit is not None: + e_bin = e_bin * e_unit + assert_array_equal(o_bin, e_bin) + + def test_histogram(self): + x = self.x + weights = self.weights + # Plain histogram. + self.check( + np.histogram, x, value_args=(x.value,), expected_units=(None, x.unit) + ) + # With bins. + self.check( + np.histogram, + x, + [125, 200] * u.cm, + value_args=(x.value, [1.25, 2.0]), + expected_units=(None, x.unit), + ) + # With density. + self.check( + np.histogram, + x, + [125, 200] * u.cm, + density=True, + value_args=(x.value, [1.25, 2.0]), + expected_units=(1 / x.unit, x.unit), + ) + # With weights. + self.check( + np.histogram, + x, + [125, 200] * u.cm, + weights=weights, + value_args=(x.value, [1.25, 2.0]), + value_kwargs=dict(weights=weights.value), + expected_units=(weights.unit, x.unit), + ) + # With weights and density. + self.check( + np.histogram, + x, + [125, 200] * u.cm, + weights=weights, + density=True, + value_args=(x.value, [1.25, 2.0]), + value_kwargs=dict(weights=weights.value), + expected_units=(weights.unit / x.unit, x.unit), + ) + + with pytest.raises(u.UnitsError): + np.histogram(x, [125, 200] * u.s) + + with pytest.raises(u.UnitsError): + np.histogram(x, [125, 200]) + + with pytest.raises(u.UnitsError): + np.histogram(x.value, [125, 200] * u.s) + + @classmethod + def _range_value(cls, range, unit): + if isinstance(range, u.Quantity): + return range.to_value(unit) + else: + return [cls._range_value(r, unit) for r in range] + + @pytest.mark.parametrize("range", [[2 * u.m, 500 * u.cm], [2, 5] * u.m]) + def test_histogram_range(self, range): + self.check( + np.histogram, + self.x, + range=range, + value_args=[self.x.value], + value_kwargs=dict(range=self._range_value(range, self.x.unit)), + expected_units=(None, self.x.unit), + ) + + def test_histogram_bin_edges(self): + x = np.array([1.1, 1.2, 1.3, 2.1, 5.1]) * u.m + out_b = np.histogram_bin_edges(x) + expected_b = np.histogram_bin_edges(x.value) * x.unit + assert np.all(out_b == expected_b) + # With bins + out2_b = np.histogram_bin_edges(x, [125, 200] * u.cm) + expected2_b = np.histogram_bin_edges(x.value, [1.25, 2.0]) * x.unit + assert np.all(out2_b == expected2_b) + with pytest.raises(u.UnitsError): + np.histogram_bin_edges(x, [125, 200] * u.s) + + with pytest.raises(u.UnitsError): + np.histogram_bin_edges(x, [125, 200]) + + with pytest.raises(u.UnitsError): + np.histogram_bin_edges(x.value, [125, 200] * u.s) + + @pytest.mark.parametrize("range", [[2 * u.m, 500 * u.cm], [2, 5] * u.m]) + def test_histogram_bin_edges_range(self, range): + out_b = np.histogram_bin_edges(self.x, range=range) + expected_b = np.histogram_bin_edges( + self.x.value, range=self._range_value(range, self.x.unit) + ) + assert np.all(out_b.value == expected_b) + + def test_histogram2d(self): + x, y = self.x, self.y + weights = self.weights + # Basic tests with X, Y. + self.check( + np.histogram2d, + x, + y, + value_args=(x.value, y.value), + expected_units=(None, x.unit, y.unit), + ) + # Check units with density. + self.check( + np.histogram2d, + x, + y, + density=True, + value_args=(x.value, y.value), + expected_units=(1 / (x.unit * y.unit), x.unit, y.unit), + ) + # Check units with weights. + self.check( + np.histogram2d, + x, + y, + weights=weights, + value_args=(x.value, y.value), + value_kwargs=dict(weights=weights.value), + expected_units=(weights.unit, x.unit, y.unit), + ) + # Check quantity bin sizes. + inb_y = [0, 0.025, 1.0] * u.m + self.check( + np.histogram2d, + x, + y, + [5, inb_y], + value_args=(x.value, y.value, [5, np.array([0, 2.5, 100.0])]), + expected_units=(None, x.unit, y.unit), + ) + # Check we dispatch on bin sizes (and check kwarg as well). + inb2_y = [0, 250, 10000.0] * u.percent + self.check( + np.histogram2d, + x.value, + y.value, + bins=[5, inb2_y], + value_args=(x.value, y.value), + value_kwargs=dict(bins=[5, np.array([0, 2.5, 100.0])]), + expected_units=(None, u.one, u.one), + ) + + # Single-item bins should be integer, not Quantity. + with pytest.raises(TypeError): + np.histogram2d(x, y, 125 * u.s) + + with pytest.raises(TypeError): + np.histogram2d(x.value, y.value, 125 * u.s) + + # Bin units need to match units of x, y. + with pytest.raises(u.UnitsError): + np.histogram2d(x, y, [125, 200] * u.s) + + with pytest.raises(u.UnitsError): + np.histogram2d(x, y, ([125, 200], [125, 200])) + + with pytest.raises(u.UnitsError): + np.histogram2d(x.value, y.value, [125, 200] * u.s) + + @pytest.mark.parametrize( + argnames="range", + argvalues=[ + [[2 * u.m, 500 * u.cm], [1 * u.cm, 40 * u.mm]], + [[200, 500] * u.cm, [10, 40] * u.mm], + [[200, 500], [1, 4]] * u.cm, + ], + ) + def test_histogram2d_range(self, range): + self.check( + np.histogram2d, + self.x, + self.y, + range=range, + value_args=[self.x.value, self.y.value], + value_kwargs=dict( + range=[ + self._range_value(r, un) + for (r, un) in zip(range, (self.x.unit, self.y.unit)) + ] + ), + expected_units=(None, self.x.unit, self.y.unit), + ) + + def test_histogramdd(self): + # First replicates of the histogram2d tests, but using the + # histogramdd override. Normally takes the sample as a tuple + # with a given number of dimensions, and returns the histogram + # as well as a tuple of bin edges. + sample = self.x, self.y + sample_units = self.x.unit, self.y.unit + sample_values = (self.x.value, self.y.value) + weights = self.weights + # Basic tests with X, Y + self.check( + np.histogramdd, + sample, + value_args=(sample_values,), + expected_units=(None, sample_units), + ) + # Check units with density. + self.check( + np.histogramdd, + sample, + density=True, + value_args=(sample_values,), + expected_units=(1 / (self.x.unit * self.y.unit), sample_units), + ) + # Check units with weights. + self.check( + np.histogramdd, + sample, + weights=weights, + value_args=(sample_values,), + value_kwargs=dict(weights=weights.value), + expected_units=(weights.unit, sample_units), + ) + # Check quantity bin sizes. + inb_y = [0, 0.025, 1.0] * u.m + self.check( + np.histogramdd, + sample, + [5, inb_y], + value_args=(sample_values, [5, np.array([0, 2.5, 100.0])]), + expected_units=(None, sample_units), + ) + # Check we dispatch on bin sizes (and check kwarg as well). + inb2_y = [0, 250, 10000.0] * u.percent + self.check( + np.histogramdd, + sample_values, + bins=[5, inb2_y], + value_args=(sample_values,), + value_kwargs=dict(bins=[5, np.array([0, 2.5, 100.0])]), + expected_units=(None, (u.one, u.one)), + ) + # For quantities, it is probably not that likely one would pass + # in the sample as an array, but check that it works anyway. + # This also gives a 3-D check. + xyz = np.random.normal(size=(10, 3)) * u.m + self.check( + np.histogramdd, + xyz, + value_args=(xyz.value,), + expected_units=(None, (xyz.unit,) * 3), + ) + # Passing it in as a tuple should work just as well; note the + # *last* axis contains the sample dimension. + self.check( + np.histogramdd, + (xyz[:, 0], xyz[:, 1], xyz[:, 2]), + value_args=(xyz.value,), + expected_units=(None, (xyz.unit,) * 3), + ) + + # Single-item bins should be integer, not Quantity. + with pytest.raises(TypeError): + np.histogramdd(sample, 125 * u.s) + + # Sequence of single items should be integer. + with pytest.raises(TypeError): + np.histogramdd(sample, [125, 200] * u.s) + + with pytest.raises(TypeError): + np.histogramdd(sample_values, [125, 200] * u.s) + + # Units of bins should match. + with pytest.raises(u.UnitsError): + np.histogramdd(sample, ([125, 200], [125, 200])) + + with pytest.raises(u.UnitsError): + np.histogramdd(sample_values, ([125, 200] * u.s, [125, 200])) + + @pytest.mark.parametrize( + argnames="range", + argvalues=[ + [[2 * u.m, 500 * u.cm], [1 * u.cm, 40 * u.mm]], + [[200, 500] * u.cm, [10, 40] * u.mm], + [[200, 500], [1, 4]] * u.cm, + ], + ) + def test_histogramdd_range(self, range): + self.check( + np.histogramdd, + (self.x, self.y), + range=range, + value_args=[(self.x.value, self.y.value)], + value_kwargs=dict( + range=[ + self._range_value(r, un) + for (r, un) in zip(range, (self.x.unit, self.y.unit)) + ] + ), + expected_units=(None, (self.x.unit, self.y.unit)), + ) + + def test_correlate(self): + x1 = [1, 2, 3] * u.m + x2 = [0, 1, 0.5] * u.m + out = np.correlate(x1, x2) + expected = np.correlate(x1.value, x2.value) * u.m**2 + assert np.all(out == expected) + + def test_convolve(self): + x1 = [1, 2, 3] * u.m + x2 = [0, 1, 0.5] * u.m + out = np.convolve(x1, x2) + expected = np.convolve(x1.value, x2.value) * u.m**2 + assert np.all(out == expected) + + def test_cov(self): + # Do not see how we can use cov with Quantity + x = np.array([[0, 2], [1, 1], [2, 0]]).T * u.m + with pytest.raises(TypeError): + np.cov(x) + + def test_corrcoef(self): + # Do not see how we can use cov with Quantity + x = np.array([[0, 2], [1, 1], [2, 0]]).T * u.m + with pytest.raises(TypeError): + np.corrcoef(x) + + +class TestSortFunctions(InvariantUnitTestSetup): + def test_sort(self): + self.check(np.sort) + + def test_sort_axis(self): + self.check(np.sort, axis=0) + + def test_sort_complex(self): + self.check(np.sort_complex) + + def test_partition(self): + self.check(np.partition, 2) + + +class TestStringFunctions: + # For these, making behaviour work means deviating only slightly from + # the docstring, and by default they fail miserably. So, might as well. + def setup_method(self): + self.q = np.arange(3.0) * u.Jy + + def test_array2string(self): + # The default formatters cannot handle units, so if we do not pass + # a relevant formatter, we are better off just treating it as an + # array (which happens for all subtypes). + out0 = np.array2string(self.q) + expected0 = str(self.q.value) + assert out0 == expected0 + # Arguments are interpreted as usual. + out1 = np.array2string(self.q, separator=", ") + expected1 = "[0., 1., 2.]" + assert out1 == expected1 + # If we do pass in a formatter, though, it should be used. + out2 = np.array2string(self.q, separator=", ", formatter={"all": str}) + expected2 = "[0.0 Jy, 1.0 Jy, 2.0 Jy]" + assert out2 == expected2 + # Also as positional argument (no, nobody will do this!) + if NUMPY_LT_2_4: + args = (self.q, None, None, None, ", ", "", np._NoValue, {"float": str}) + out3 = np.array2string(*args) + assert out3 == expected2 + # But not if the formatter is not relevant for us. + out4 = np.array2string(self.q, separator=", ", formatter={"int": str}) + assert out4 == expected1 + + def test_array_repr(self): + out = np.array_repr(self.q) + assert out == "Quantity([0., 1., 2.], unit='Jy')" + q2 = self.q.astype("f4") + out2 = np.array_repr(q2) + assert out2 == "Quantity([0., 1., 2.], unit='Jy', dtype=float32)" + + def test_array_str(self): + out = np.array_str(self.q) + expected = str(self.q) + assert out == expected + + +class TestBitAndIndexFunctions: + # Index/bit functions generally fail for floats, so the usual + # float quantity are safe, but the integer ones are not. + def setup_method(self): + self.q = np.arange(3) * u.m + self.uint_q = u.Quantity(np.arange(3), "m", dtype="u1") + + def test_packbits(self): + with pytest.raises(TypeError): + np.packbits(self.q) + with pytest.raises(TypeError): + np.packbits(self.uint_q) + + def test_unpackbits(self): + with pytest.raises(TypeError): + np.unpackbits(self.q) + with pytest.raises(TypeError): + np.unpackbits(self.uint_q) + + def test_unravel_index(self): + with pytest.raises(TypeError): + np.unravel_index(self.q, 3) + with pytest.raises(TypeError): + np.unravel_index(self.uint_q, 3) + + def test_ravel_multi_index(self): + with pytest.raises(TypeError): + np.ravel_multi_index((self.q,), 3) + with pytest.raises(TypeError): + np.ravel_multi_index((self.uint_q,), 3) + + def test_ix_(self): + with pytest.raises(TypeError): + np.ix_(self.q) + with pytest.raises(TypeError): + np.ix_(self.uint_q) + + +class TestDtypeFunctions(NoUnitTestSetup): + def test_common_type(self): + self.check(np.common_type) + + def test_result_type(self): + self.check(np.result_type) + + def test_can_cast(self): + self.check(np.can_cast, self.q.dtype) + self.check(np.can_cast, "f4") + + def test_min_scalar_type(self): + out = np.min_scalar_type(self.q[0]) + expected = np.min_scalar_type(self.q.value[0]) + assert out == expected + + def test_iscomplexobj(self): + self.check(np.iscomplexobj) + + def test_isrealobj(self): + self.check(np.isrealobj) + + +class TestMeshGrid: + def test_meshgrid(self): + q1 = np.arange(3.0) * u.m + q2 = np.arange(5.0) * u.s + o1, o2 = np.meshgrid(q1, q2) + e1, e2 = np.meshgrid(q1.value, q2.value) + assert np.all(o1 == e1 * q1.unit) + assert np.all(o2 == e2 * q2.unit) + + +class TestMemoryFunctions(NoUnitTestSetup): + def test_shares_memory(self): + self.check(np.shares_memory, self.q.value) + + def test_may_share_memory(self): + self.check(np.may_share_memory, self.q.value) + + +class TestSetOpsFunctions: + def setup_method(self): + self.q = np.array([[0.0, 1.0, -1.0], [3.0, 5.0, 3.0], [0.0, 1.0, -1]]) * u.m + self.q2 = np.array([0.0, 100.0, 150.0, 200.0]) * u.cm + + def check(self, function, qs, *args, **kwargs): + unit = kwargs.pop("unit", self.q.unit) + out = function(*qs, *args, **kwargs) + qv = tuple(q.to_value(self.q.unit) for q in qs) + expected = function(*qv, *args, **kwargs) + if isinstance(expected, tuple): + if unit: + expected = (expected[0] * unit,) + expected[1:] + for o, e in zip(out, expected): + assert_array_equal(o, e) + else: + if unit: + expected = expected * unit + assert_array_equal(out, expected) + + def check1(self, function, *args, **kwargs): + self.check(function, (self.q,), *args, **kwargs) + + def check2(self, function, *args, **kwargs): + self.check(function, (self.q, self.q2), *args, **kwargs) + + @pytest.mark.parametrize( + "kwargs", + ( + dict(return_index=True, return_inverse=True), + dict(return_counts=True), + dict(return_index=True, return_inverse=True, return_counts=True), + ), + ) + def test_unique(self, kwargs): + self.check1(np.unique, **kwargs) + + @pytest.mark.parametrize( + "kwargs", + ( + dict(axis=0), + dict(axis=1), + dict(return_counts=True, return_inverse=False, axis=1), + ), + ) + def test_unique_more_complex(self, kwargs): + self.check1(np.unique, **kwargs) + + def test_unique_all(self): + values, indices, inverse_indices, counts = np.unique( + self.q, + return_index=True, + return_inverse=True, + return_counts=True, + equal_nan=False, + ) + res = np.unique_all(self.q) + assert len(res) == 4 + + assert_array_equal(res.values, values) + assert_array_equal(res.indices, indices) + assert_array_equal(res.inverse_indices, inverse_indices) + assert_array_equal(res.counts, counts) + + def test_unique_counts(self): + values, counts = np.unique(self.q, return_counts=True, equal_nan=False) + res = np.unique_counts(self.q) + assert len(res) == 2 + + assert_array_equal(res.values, values) + assert_array_equal(res.counts, counts) + + def test_unique_inverse(self): + values, inverse_indices = np.unique( + self.q, return_inverse=True, equal_nan=False + ) + res = np.unique_inverse(self.q) + assert len(res) == 2 + + assert_array_equal(res.values, values) + assert_array_equal(res.inverse_indices, inverse_indices) + + def test_unique_values(self): + values = np.unique(self.q, equal_nan=False) + res = np.unique_values(self.q) + assert_array_equal(res, values) + + @pytest.mark.parametrize("kwargs", ({}, dict(return_indices=True))) + def test_intersect1d(self, kwargs): + self.check2(np.intersect1d, **kwargs) + + def test_setxor1d(self): + self.check2(np.setxor1d) + + def test_union1d(self): + self.check2(np.union1d) + result = np.union1d(np.array([0.0, np.nan]), np.arange(3) << u.m) + assert result.unit is u.m + assert_array_equal(result.value, np.array([0.0, 1.0, 2.0, np.nan])) + + def test_setdiff1d(self): + self.check2(np.setdiff1d) + + @pytest.mark.skipif(not NUMPY_LT_2_4, reason="in1d was removed in numpy 2.4") + @pytest.mark.filterwarnings("ignore:`in1d` is deprecated. Use `np.isin` instead.") + def test_in1d(self): + self.check2(np.in1d, unit=None) # noqa: NPY201 + # Check zero is treated as having any unit. + assert np.in1d(np.zeros(1), self.q2) # noqa: NPY201 + with pytest.raises(u.UnitsError): + np.in1d(np.ones(1), self.q2) # noqa: NPY201 + + def test_isin(self): + self.check2(np.isin, unit=None) + + def test_ediff1d(self): + # ediff1d works always as it calls the Quantity method. + self.check1(np.ediff1d) + x = np.arange(10.0) * u.m + out = np.ediff1d(x, to_begin=-12.5 * u.cm, to_end=1 * u.km) + expected = np.ediff1d(x.value, to_begin=-0.125, to_end=1000.0) * x.unit + assert_array_equal(out, expected) + + +class TestDatetimeFunctions(BasicTestSetup): + def test_busday_count(self): + with pytest.raises(TypeError): + np.busday_count(self.q, self.q) + + def test_busday_offset(self): + with pytest.raises(TypeError): + np.busday_offset(self.q, self.q) + + def test_datetime_as_string(self): + with pytest.raises(TypeError): + np.datetime_as_string(self.q) + + def test_is_busday(self): + with pytest.raises(TypeError): + np.is_busday(self.q) + + +# These functions always worked; ensure they do not regress. +# Note that they are *not* wrapped so no need to check coverage. +@pytest.mark.parametrize("function", [np.fft.fftfreq, np.fft.rfftfreq]) +def test_fft_frequencies(function): + out = function(128, d=0.1 * u.s) + expected = function(128, d=0.1) / u.s + assert_array_equal(out, expected) + + +class TestFFT(InvariantUnitTestSetup): + tested_module = np.fft + + # These are all trivial, just preserve the unit. + def setup_method(self): + # Use real input; gets turned into complex as needed. + self.q = np.arange(128.0).reshape(8, -1) * u.s + + def test_fft(self): + self.check(np.fft.fft) + + def test_ifft(self): + self.check(np.fft.ifft) + + def test_rfft(self): + self.check(np.fft.rfft) + + def test_irfft(self): + self.check(np.fft.irfft) + + def test_fft2(self): + self.check(np.fft.fft2) + + def test_ifft2(self): + self.check(np.fft.ifft2) + + def test_rfft2(self): + self.check(np.fft.rfft2) + + def test_irfft2(self): + self.check(np.fft.irfft2) + + def test_fftn(self): + self.check(np.fft.fftn) + + def test_ifftn(self): + self.check(np.fft.ifftn) + + def test_rfftn(self): + self.check(np.fft.rfftn) + + def test_irfftn(self): + self.check(np.fft.irfftn) + + def test_hfft(self): + self.check(np.fft.hfft) + + def test_ihfft(self): + self.check(np.fft.ihfft) + + def test_fftshift(self): + self.check(np.fft.fftshift) + + def test_ifftshift(self): + self.check(np.fft.ifftshift) + + +class TestLinAlg(InvariantUnitTestSetup): + tested_module = np.linalg + + def setup_method(self): + self.q = ( + np.array([[1.0, -1.0, 2.0], [0.0, 3.0, -1.0], [-1.0, -1.0, 1.0]]) << u.m + ) + + def test_cond(self): + c = np.linalg.cond(self.q) + expected = np.linalg.cond(self.q.value) + assert c == expected + + def test_matrix_rank(self): + r = np.linalg.matrix_rank(self.q) + x = np.linalg.matrix_rank(self.q.value) + assert r == x + + def test_matrix_rank_with_tol(self): + # Use a matrix that is not so good, so tol=1 and tol=0.01 differ. + q = np.arange(9.0).reshape(3, 3) / 4 * u.m + tol = 1.0 * u.cm + r2 = np.linalg.matrix_rank(q, tol) + x2 = np.linalg.matrix_rank(q.value, tol.to_value(q.unit)) + assert r2 == x2 + + def test_matrix_power(self): + q1 = np.linalg.matrix_power(self.q, 1) + assert_array_equal(q1, self.q) + q2 = np.linalg.matrix_power(self.q, 2) + assert_array_equal(q2, self.q @ self.q) + q2 = np.linalg.matrix_power(self.q, 4) + assert_array_equal(q2, self.q @ self.q @ self.q @ self.q) + + def test_matrix_inv_power(self): + qinv = np.linalg.inv(self.q.value) / self.q.unit + qm1 = np.linalg.matrix_power(self.q, -1) + assert_array_equal(qm1, qinv) + qm3 = np.linalg.matrix_power(self.q, -3) + assert_array_equal(qm3, qinv @ qinv @ qinv) + + def test_multi_dot(self): + q2 = np.linalg.multi_dot([self.q, self.q]) + q2x = self.q @ self.q + assert_array_equal(q2, q2x) + q3 = np.linalg.multi_dot([self.q, self.q, self.q]) + q3x = self.q @ self.q @ self.q + assert_array_equal(q3, q3x) + + def test_svd(self): + m = np.arange(10.0) * np.arange(5.0)[:, np.newaxis] * u.m + svd_u, svd_s, svd_vt = np.linalg.svd(m, full_matrices=False) + svd_ux, svd_sx, svd_vtx = np.linalg.svd(m.value, full_matrices=False) + svd_sx <<= m.unit + assert_array_equal(svd_u, svd_ux) + assert_array_equal(svd_vt, svd_vtx) + assert_array_equal(svd_s, svd_sx) + assert u.allclose(svd_u @ np.diag(svd_s) @ svd_vt, m) + + s2 = np.linalg.svd(m, compute_uv=False) + svd_s2x = np.linalg.svd(m.value, compute_uv=False) << m.unit + assert_array_equal(s2, svd_s2x) + + def test_inv(self): + inv = np.linalg.inv(self.q) + expected = np.linalg.inv(self.q.value) / self.q.unit + assert_array_equal(inv, expected) + + def test_pinv(self): + pinv = np.linalg.pinv(self.q) + expected = np.linalg.pinv(self.q.value) / self.q.unit + assert_array_equal(pinv, expected) + rcond = 0.01 * u.cm + pinv2 = np.linalg.pinv(self.q, rcond) + expected2 = ( + np.linalg.pinv(self.q.value, rcond.to_value(self.q.unit)) / self.q.unit + ) + assert_array_equal(pinv2, expected2) + pinv3 = np.linalg.pinv(self.q, rtol=rcond) + assert_array_equal(pinv3, expected2) + + def test_tensorinv(self): + inv = np.linalg.tensorinv(self.q, ind=1) + expected = np.linalg.tensorinv(self.q.value, ind=1) / self.q.unit + assert_array_equal(inv, expected) + + def test_det(self): + det = np.linalg.det(self.q) + expected = np.linalg.det(self.q.value) + expected <<= self.q.unit ** self.q.shape[-1] + assert_array_equal(det, expected) + with pytest.raises(np.linalg.LinAlgError): + np.linalg.det(self.q[0]) # Not 2-D + with pytest.raises(np.linalg.LinAlgError): + np.linalg.det(self.q[:-1]) # Not square. + + def test_slogdet(self): + # TODO: Could be supported if we had a natural logarithm unit. + with pytest.raises(TypeError): + logdet = np.linalg.slogdet(self.q) + assert hasattr(logdet, "unit") + + def test_solve(self): + b = np.array([1.0, 2.0, 4.0]) * u.m / u.s + x = np.linalg.solve(self.q, b) + xx = np.linalg.solve(self.q.value, b.value) + xx <<= b.unit / self.q.unit + assert_array_equal(x, xx) + assert u.allclose(self.q @ x, b) + + def test_tensorsolve(self): + b = np.array([1.0, 2.0, 4.0]) * u.m / u.s + x = np.linalg.tensorsolve(self.q, b) + xx = np.linalg.tensorsolve(self.q.value, b.value) + xx <<= b.unit / self.q.unit + assert_array_equal(x, xx) + assert u.allclose(self.q @ x, b) + + def test_lstsq(self): + b = np.array([1.0, 2.0, 4.0]) * u.m / u.s + x, residuals, rank, s = np.linalg.lstsq(self.q, b, rcond=None) + xx, residualsx, rankx, sx = np.linalg.lstsq(self.q.value, b.value, rcond=None) + xx <<= b.unit / self.q.unit + residualsx <<= b.unit**2 + sx <<= self.q.unit + + assert_array_equal(x, xx) + assert_array_equal(residuals, residualsx) + assert_array_equal(s, sx) + assert rank == rankx + assert u.allclose(self.q @ x, b) + + # Also do one where we can check the answer... + m = np.eye(3) + b = np.arange(3) * u.m + x, residuals, rank, s = np.linalg.lstsq(m, b, rcond=1.0 * u.percent) + assert_array_equal(x, b) + assert np.all(residuals == 0 * u.m**2) + assert rank == 3 + assert_array_equal(s, np.array([1.0, 1.0, 1.0]) << u.one) + + with pytest.raises(u.UnitsError): + np.linalg.lstsq(m, b, rcond=1.0 * u.s) + + def test_norm(self): + n = np.linalg.norm(self.q) + expected = np.linalg.norm(self.q.value) << self.q.unit + assert_array_equal(n, expected) + # Special case: 1-D, ord=0. + n1 = np.linalg.norm(self.q[0], ord=0) + expected1 = np.linalg.norm(self.q[0].value, ord=0) << u.one + assert_array_equal(n1, expected1) + + def test_cholesky(self): + # Numbers from np.linalg.cholesky docstring. + q = np.array([[1, -2j], [2j, 5]]) * u.m + cd = np.linalg.cholesky(q) + cdx = np.linalg.cholesky(q.value) << q.unit**0.5 + assert_array_equal(cd, cdx) + assert u.allclose(cd @ cd.T.conj(), q) + + def test_qr(self): + # This is not exhaustive... + a = np.array([[1, -2j], [2j, 5]]) * u.m + q, r = np.linalg.qr(a) + qx, rx = np.linalg.qr(a.value) + qx <<= u.one + rx <<= a.unit + assert_array_equal(q, qx) + assert_array_equal(r, rx) + assert u.allclose(q @ r, a) + + def test_eig(self): + w, v = np.linalg.eig(self.q) + wx, vx = np.linalg.eig(self.q.value) + wx <<= self.q.unit + vx <<= u.one + assert_array_equal(w, wx) + assert_array_equal(v, vx) + + # Comprehensible example + q = np.diag((1, 2, 3) * u.m) + w, v = np.linalg.eig(q) + assert_array_equal(w, np.arange(1, 4) * u.m) + assert_array_equal(v, np.eye(3)) + + def test_eigvals(self): + w = np.linalg.eigvals(self.q) + wx = np.linalg.eigvals(self.q.value) << self.q.unit + assert_array_equal(w, wx) + # Comprehensible example + q = np.diag((1, 2, 3) * u.m) + w = np.linalg.eigvals(q) + assert_array_equal(w, np.arange(1, 4) * u.m) + + def test_eigh(self): + w, v = np.linalg.eigh(self.q) + wx, vx = np.linalg.eigh(self.q.value) + wx <<= self.q.unit + vx <<= u.one + assert_array_equal(w, wx) + assert_array_equal(v, vx) + + def test_eigvalsh(self): + w = np.linalg.eigvalsh(self.q) + wx = np.linalg.eigvalsh(self.q.value) << self.q.unit + assert_array_equal(w, wx) + + # Numpy 2.0 added array-api compatible definitions of + # diagonal and trace to np.linalg. Since these have + # name conflicts with the main numpy namespace, they + # are tracked as linalg_diagonal and linalg_trace. + def test_diagonal(self): + self.check(np.linalg.diagonal) + + def test_trace(self): + self.check(np.trace) + + def test_cross(self): + q1 = np.array([1, 2, 3]) << u.m + q2 = np.array([4, 5, 6]) << u.s + assert_array_equal(np.linalg.cross(q1, q2), np.cross(q1, q2)) + assert_array_equal(np.linalg.cross(q1, q2.value), np.cross(q1, q2.value)) + + def test_outer(self): + q = self.q.flatten() + assert_array_equal(np.linalg.outer(q, q), np.outer(q, q)) + assert_array_equal(np.linalg.outer(q, q.value), np.outer(q, q.value)) + + def test_svdvals(self): + _, ref, _ = np.linalg.svd(self.q) + res = np.linalg.svdvals(self.q) + assert_allclose(res, ref, rtol=5e-16) + + def test_vecdot(self): + ref = (self.q * self.q).sum(-1) + res = np.linalg.vecdot(self.q, self.q) + assert_array_equal(res, ref) + + def test_tensordot(self): + ref = np.tensordot(self.q, self.q) + res = np.linalg.tensordot(self.q, self.q) + assert_array_equal(res, ref) + + def test_matmul(self): + ref = np.matmul(self.q, self.q) + res = np.linalg.matmul(self.q, self.q) + assert_array_equal(res, ref) + + def test_matrix_transpose(self): + t = np.linalg.matrix_transpose(self.q) + assert_array_equal(t, self.q.swapaxes(-2, -1)) + + def test_matrix_norm(self): + n = np.linalg.matrix_norm(self.q) + expected = np.linalg.norm(self.q.value) << self.q.unit + assert_array_equal(n, expected) + + def test_vector_norm(self): + n = np.linalg.vector_norm(self.q) + expected = np.linalg.norm(self.q.value.ravel()) << self.q.unit + assert_array_equal(n, expected) + # Special case: 1-D, ord=0. + n1 = np.linalg.vector_norm(self.q[0], ord=0) + expected1 = np.linalg.norm(self.q[0].value.ravel(), ord=0) << u.one + assert_array_equal(n1, expected1) + # Axis combo, just in case + n2 = np.linalg.vector_norm(self.q, axis=(-1, -2)) + expected2 = np.linalg.vector_norm(self.q.value, axis=(-1, -2)) << self.q.unit + assert_array_equal(n2, expected2) + + +class TestRecFunctions: + tested_module = np.lib.recfunctions + + @classmethod + def setup_class(cls): + cls.pv_dtype = np.dtype([("p", "f8"), ("v", "f8")]) + cls.pv_t_dtype = np.dtype( + [("pv", np.dtype([("pp", "f8"), ("vv", "f8")])), ("t", "f8")] + ) + + cls.pv = np.array([(1.0, 0.25), (2.0, 0.5), (3.0, 0.75)], cls.pv_dtype) + cls.pv_t = np.array( + [((4.0, 2.5), 0.0), ((5.0, 5.0), 1.0), ((6.0, 7.5), 2.0)], cls.pv_t_dtype + ) + + cls.pv_unit = u.StructuredUnit((u.km, u.km / u.s), ("p", "v")) + cls.pv_t_unit = u.StructuredUnit((cls.pv_unit, u.s), ("pv", "t")) + + cls.q_pv = cls.pv << cls.pv_unit + cls.q_pv_t = cls.pv_t << cls.pv_t_unit + + def test_structured_to_unstructured(self): + # can't unstructure something with incompatible units + with pytest.raises(u.UnitConversionError, match="'m'"): + rfn.structured_to_unstructured(u.Quantity((0, 0.6), u.Unit("(eV, m)"))) + + # it works if all the units are equal + struct = u.Quantity((0, 0, 0.6), u.Unit("(eV, eV, eV)")) + unstruct = rfn.structured_to_unstructured(struct) + assert_array_equal(unstruct, [0, 0, 0.6] * u.eV) + + # also if the units are convertible + struct = u.Quantity((0, 0, 0.6), u.Unit("(eV, eV, keV)")) + unstruct = rfn.structured_to_unstructured(struct) + assert_array_equal(unstruct, [0, 0, 600] * u.eV) + + struct = u.Quantity((0, 0, 1.7827e-33), u.Unit("(eV, eV, g)")) + with u.add_enabled_equivalencies(u.mass_energy()): + unstruct = rfn.structured_to_unstructured(struct) + u.allclose(unstruct, [0, 0, 1.0000214] * u.eV) + + # and if the dtype is nested + struct = [(5, (400.0, 3e6))] * u.Unit("m, (cm, um)") + unstruct = rfn.structured_to_unstructured(struct) + assert_array_equal(unstruct, [[5, 4, 3]] * u.m) + + # For the other tests of ``structured_to_unstructured``, see + # ``test_structured.TestStructuredQuantityFunctions.test_structured_to_unstructured`` + + def test_unstructured_to_structured(self): + unstruct = [1, 2, 3] * u.m + dtype = np.dtype([("f1", float), ("f2", float), ("f3", float)]) + + # It works. + struct = rfn.unstructured_to_structured(unstruct, dtype=dtype) + assert struct.unit == u.Unit("(m, m, m)") + assert_array_equal(rfn.structured_to_unstructured(struct), unstruct) + + # Can't structure something that's already structured. + with pytest.raises(ValueError, match="arr must have at least one dimension"): + rfn.unstructured_to_structured(struct, dtype=dtype) + + # For the other tests of ``structured_to_unstructured``, see + # ``test_structured.TestStructuredQuantityFunctions.test_unstructured_to_structured`` + + def test_merge_arrays_repeat_dtypes(self): + # Cannot merge things with repeat dtypes. + q1 = u.Quantity([(1,)], dtype=[("f1", float)]) + q2 = u.Quantity([(1,)], dtype=[("f1", float)]) + + with pytest.raises(ValueError, match="field 'f1' occurs more than once"): + rfn.merge_arrays((q1, q2)) + + @pytest.mark.parametrize("flatten", [True, False]) + def test_merge_arrays(self, flatten): + """Test `numpy.lib.recfunctions.merge_arrays`.""" + # Merge single normal array. + arr = rfn.merge_arrays(self.q_pv["p"], flatten=flatten) + assert_array_equal(arr["f0"], self.q_pv["p"]) + assert arr.unit == (u.km,) + + # Merge single structured array. + arr = rfn.merge_arrays(self.q_pv, flatten=flatten) + assert_array_equal(arr, self.q_pv) + assert arr.unit == (u.km, u.km / u.s) + + # Merge 1-element tuple. + arr = rfn.merge_arrays((self.q_pv,), flatten=flatten) + assert np.array_equal(arr, self.q_pv) + assert arr.unit == (u.km, u.km / u.s) + + def test_merge_array_nested_structure(self): + # Merge 2-element tuples without flattening. + arr = rfn.merge_arrays((self.q_pv, self.q_pv_t)) + assert_array_equal(arr["f0"], self.q_pv) + assert_array_equal(arr["f1"], self.q_pv_t) + assert arr.unit == ((u.km, u.km / u.s), ((u.km, u.km / u.s), u.s)) + # For a structured array, all elements should be treated as dimensionless. + arr = rfn.merge_arrays((self.q_pv["p"], self.q_pv.value)) + expected_value = rfn.merge_arrays((self.q_pv["p"].value, self.q_pv.value)) + assert_array_equal(arr.value, expected_value) + assert arr.unit == u.Unit((self.q_pv["p"].unit, (u.one, u.one))) + + def test_merge_arrays_flatten_nested_structure(self): + # Merge 2-element tuple, flattening it. + arr = rfn.merge_arrays((self.q_pv, self.q_pv_t), flatten=True) + assert_array_equal(arr["p"], self.q_pv["p"]) + assert_array_equal(arr["v"], self.q_pv["v"]) + assert_array_equal(arr["pp"], self.q_pv_t["pv"]["pp"]) + assert_array_equal(arr["vv"], self.q_pv_t["pv"]["vv"]) + assert_array_equal(arr["t"], self.q_pv_t["t"]) + assert arr.unit == (u.km, u.km / u.s, u.km, u.km / u.s, u.s) + # For a structured array, all elements should be treated as dimensionless. + arr = rfn.merge_arrays((self.q_pv["p"], self.q_pv.value), flatten=True) + expected_value = rfn.merge_arrays( + (self.q_pv["p"].value, self.q_pv.value), flatten=True + ) + assert_array_equal(arr.value, expected_value) + assert arr.unit == u.Unit((self.q_pv["p"].unit, u.one, u.one)) + + def test_merge_arrays_asrecarray(self): + with pytest.raises(ValueError, match="asrecarray=True is not supported."): + rfn.merge_arrays(self.q_pv, asrecarray=True) + + def test_merge_arrays_usemask(self): + with pytest.raises(ValueError, match="usemask=True is not supported."): + rfn.merge_arrays(self.q_pv, usemask=True) + + @pytest.mark.parametrize("flatten", [True, False]) + def test_merge_arrays_str(self, flatten): + with pytest.raises( + TypeError, match="the Quantity implementation cannot handle" + ): + rfn.merge_arrays((self.q_pv, np.array(["a", "b", "c"])), flatten=flatten) + + +all_wrapped_functions = get_wrapped_functions( + np, np.fft, np.linalg, np.lib.recfunctions +) +if NUMPY_LT_2_2: + # ref https://github.com/numpy/numpy/issues/27451 + all_wrapped_functions |= SUPPORTED_NEP35_FUNCTIONS +tested_functions = get_covered_functions(locals()) +untested_functions = set() +deprecated_functions = set() + +untested_functions |= deprecated_functions +io_functions = {np.save, np.savez, np.savetxt, np.savez_compressed} +untested_functions |= io_functions +poly_functions = { + np.poly, np.polyadd, np.polyder, np.polydiv, np.polyfit, np.polyint, + np.polymul, np.polysub, np.polyval, np.roots, np.vander, +} # fmt: skip +untested_functions |= poly_functions +rec_functions = { + rfn.rec_append_fields, rfn.rec_drop_fields, rfn.rec_join, + rfn.drop_fields, rfn.rename_fields, rfn.append_fields, rfn.join_by, + rfn.repack_fields, rfn.apply_along_fields, rfn.assign_fields_by_name, + rfn.stack_arrays, rfn.find_duplicates, + rfn.recursive_fill_fields, rfn.require_fields, +} # fmt: skip +untested_functions |= rec_functions + + +def test_testing_completeness(): + assert not tested_functions.intersection(untested_functions) + assert all_wrapped_functions == (tested_functions | untested_functions) + + +class TestFunctionHelpersCompleteness: + @pytest.mark.parametrize( + "one, two", + list( + itertools.combinations( + ( + SUBCLASS_SAFE_FUNCTIONS, + UNSUPPORTED_FUNCTIONS, + set(FUNCTION_HELPERS.keys()), + set(DISPATCHED_FUNCTIONS.keys()), + ), + 2, + ), + ), + ) + def test_no_duplicates(self, one, two): + assert not one.intersection(two) + + def test_all_included(self): + included_in_helpers = ( + SUBCLASS_SAFE_FUNCTIONS + | UNSUPPORTED_FUNCTIONS + | set(FUNCTION_HELPERS.keys()) + | set(DISPATCHED_FUNCTIONS.keys()) + ) + assert all_wrapped_functions == included_in_helpers + + def test_ignored_are_untested(self): + assert IGNORED_FUNCTIONS | TBD_FUNCTIONS == untested_functions + + +class CheckSignatureCompatibilityBase: + """ + Check that a helper function's signature is *at least* as flexible + as the helped (target) function's. E.g., any argument that is allowed positionally, + or as keyword, by the target must be re-exposed *somehow* by the helper. + We explicitly allow helper's signature to be *more* flexible than the target signature + by allowing *args and **kwargs catch-all arguments, which we use to limit code + duplication, and also help with forward and backward compatibility. + See https://github.com/astropy/astropy/issues/15703 + """ + + # this is an abstract base test class, meant to allow reuse with minimal + # code duplication. Concrete implementations should be decorated with + # @pytest.mark.parametrize("target, helper", ...) + + @staticmethod + def have_catchall_argument(parameters, kind) -> bool: + return any(p.kind is kind for p in parameters.values()) + + @staticmethod + def get_param_group(parameters, kinds: list) -> list[str]: + return [name for name, p in parameters.items() if p.kind in kinds] + + def test_all_arguments_reexposed(self, target, helper): + try: + sig_target = inspect.signature(target) + except ValueError: + if NUMPY_LT_2_4: + pytest.skip("Non Python function cannot be inspected at runtime") + elif target is np.fromstring: + pytest.skip(f"known case of missing runtime signature ({target})") + else: + raise + + params_target = sig_target.parameters + sig_helper = inspect.signature(helper) + params_helper = sig_helper.parameters + + have_args_helper = self.have_catchall_argument(params_helper, VAR_POSITIONAL) + have_kwargs_helper = self.have_catchall_argument(params_helper, VAR_KEYWORD) + + args_helper = list(params_helper.items()) + + pos_helper = 0 + for nt, pt in params_target.items(): + kt = pt.kind + if kt in (POSITIONAL_ONLY, POSITIONAL_OR_KEYWORD): + assert pos_helper < len(args_helper), ( + "helper's signature is too short; " + "some arguments are not properly re-exposed" + ) + nh, ph = args_helper[pos_helper] + if (kh := ph.kind) is not VAR_POSITIONAL: + assert nh == nt, f"argument {nt!r} isn't re-exposed as positional" + assert kh is kt, ( + f"helper is not re-exposing argument {nt!r} properly:" + f"expected {kt}, got {kh}" + ) + pos_helper += 1 + continue + + if kt in (KEYWORD_ONLY, POSITIONAL_OR_KEYWORD): + if nt in params_helper: + kh = params_helper[nt].kind + assert kh is kt, ( + f"helper is not re-exposing argument {nt!r} properly: " + f"expected {kt}, got {kh}" + ) + elif nt == "like": + # special case for NEP35 functions: + # this argument doesn't need to be re-exposed because + # it is not passed down to dispatched functions + pass + elif kt is KEYWORD_ONLY: + assert have_kwargs_helper, ( + f"argument {nt!r} is not re-exposed as keyword" + ) + elif kt is POSITIONAL_OR_KEYWORD: + assert have_args_helper and have_kwargs_helper, ( + f"argument {nt!r} is not re-exposed as positional-or-keyword" + ) + elif kt is VAR_POSITIONAL: + assert have_args_helper, "helper is missing a catch-all *args argument" + elif kt is VAR_KEYWORD: + assert have_kwargs_helper, ( + "helper is missing a catch-all **kwargs argument" + ) + + def test_known_arguments(self, target, helper): + # validate that all exposed arguments map to something in the target + try: + sig_target = inspect.signature(target) + except ValueError: + if NUMPY_LT_2_4: + pytest.skip("Non Python function cannot be inspected at runtime") + elif target is np.fromstring: + pytest.skip(f"known case of missing runtime signature ({target})") + else: + raise + + params_target = sig_target.parameters + sig_helper = inspect.signature(helper) + params_helper = sig_helper.parameters + + for kind in (POSITIONAL_ONLY, POSITIONAL_OR_KEYWORD): + args_target = self.get_param_group(params_helper, [kind]) + args_helper = self.get_param_group(params_helper, [kind]) + + if (nhelper := len(args_helper)) > (ntarget := len(args_target)): + unknown: list[str] = args_helper[ntarget:] + raise AssertionError( + f"Found unknown {kind} parameter(s) " + "in helper's signature: " + f"{unknown}, at position(s) {list(range(ntarget, nhelper))}" + ) + + # keyword-allowed + keyword_allowed_target = set( + self.get_param_group(params_target, [KEYWORD_ONLY, POSITIONAL_OR_KEYWORD]) + ) + keyword_allowed_helper = set( + self.get_param_group(params_helper, [KEYWORD_ONLY, POSITIONAL_OR_KEYWORD]) + ) + + # additional private keyword-only argument are allowed, as long as they + # have default values. They are only intended for testing purposes. + # For instance, quantile has such a parameter '_q_unit' + private_kwargs = { + name for name in keyword_allowed_helper if name.startswith("_") + } + for pk in private_kwargs: + assert params_helper[pk].default is not EMPTY_DEFAULT, ( + f"private argument {pk} must provide a default value" + ) + + keyword_allowed_helper -= private_kwargs + + diff = keyword_allowed_helper - keyword_allowed_target + assert not diff, ( + "Found some keyword-allowed parameters in helper " + f"that are unknown to target: {diff}" + ) + + # finally, check that default values are correctly replicated + for name, ph in params_helper.items(): + if name not in params_target: + # In a few cases, the helper defines names that are not in + # the target (e.g., a private name like _q_unit in quantile, + # or a *args, **kwargs that captures further arguments + # that do not matter. We let such cases slip by. + continue + pt = params_target[name] + assert ph.default == pt.default, ( + f"Default value mismatch for argument {name!r}. " + f"Helper has {ph.default!r}, target has {pt.default!r}" + ) + + +@pytest.mark.parametrize( + "target, helper", + sorted( + (*FUNCTION_HELPERS.items(), *DISPATCHED_FUNCTIONS.items()), + key=lambda items: items[0].__name__, + ), + ids=lambda func: func.__name__, +) +class TestFunctionHelpersSignatureCompatibility(CheckSignatureCompatibilityBase): + pass diff --git a/astropy/units/tests/test_quantity_typing.py b/astropy/units/tests/test_quantity_typing.py new file mode 100644 index 000000000000..71a701742248 --- /dev/null +++ b/astropy/units/tests/test_quantity_typing.py @@ -0,0 +1,78 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +"""Test the Quantity class and related.""" + +from typing import Annotated, Any, get_args, get_origin, get_type_hints + +import numpy as np + +from astropy import units as u + + +class TestQuantityTyping: + """Test Quantity Typing Annotations.""" + + def test_quantity_typing(self): + """Test type hint creation from Quantity.""" + annot = u.Quantity[u.m] + + assert get_origin(annot) is Annotated + assert get_args(annot) == (u.Quantity, u.m) + + # test usage + def func(x: annot, y: str) -> u.Quantity[u.s]: + return x, y + + annots = get_type_hints(func, include_extras=True) + assert annots["x"] is annot + assert annots["return"].__metadata__[0] == u.s + + def test_metadata_in_annotation(self): + """Test Quantity annotation with added metadata.""" + multi_annot = u.Quantity[u.m, Any, np.dtype] + + def multi_func(x: multi_annot, y: str): + return x, y + + annots = get_type_hints(multi_func, include_extras=True) + assert annots["x"] == multi_annot + + def test_optional_and_annotated(self): + """Test Quantity annotation in an Optional.""" + opt_annot = u.Quantity[u.m] | None + + def opt_func(x: opt_annot, y: str): + return x, y + + annots = get_type_hints(opt_func, include_extras=True) + assert annots["x"] == opt_annot + + def test_union_and_annotated(self): + """Test Quantity annotation in a Union.""" + # double Quantity[] + union_annot1 = u.Quantity[u.m] | u.Quantity[u.s] + # one Quantity, one physical-type + union_annot2 = u.Quantity[u.m] | u.Quantity["time"] + # one Quantity, one general type + union_annot3 = u.Quantity[u.m / u.s] | float + + def union_func(x: union_annot1, y: union_annot2) -> union_annot3: + if isinstance(y, str): # value = time + return x.value # returns + else: + return x / y # returns Quantity[m / s] + + annots = get_type_hints(union_func, include_extras=True) + assert annots["x"] == union_annot1 + assert annots["y"] == union_annot2 + assert annots["return"] == union_annot3 + + def test_quantity_subclass_typing(self): + """Test type hint creation from a Quantity subclasses.""" + + class Length(u.SpecificTypeQuantity): + _equivalent_unit = u.m + + annot = Length[u.km] + + assert get_origin(annot) is Annotated + assert get_args(annot) == (Length, u.km) diff --git a/astropy/units/tests/test_quantity_ufuncs.py b/astropy/units/tests/test_quantity_ufuncs.py new file mode 100644 index 000000000000..cbc40a9312d2 --- /dev/null +++ b/astropy/units/tests/test_quantity_ufuncs.py @@ -0,0 +1,1686 @@ +# The purpose of these tests are to ensure that calling ufuncs with quantities +# returns quantities with the right units, or raises exceptions. + +import concurrent.futures +import dataclasses +import warnings +from collections.abc import Callable +from typing import NamedTuple + +import numpy as np +import pytest +from erfa import ufunc as erfa_ufunc +from numpy._core import umath as np_umath +from numpy.testing import assert_allclose, assert_array_equal + +from astropy import units as u +from astropy.units import quantity_helper as qh +from astropy.units.quantity_helper.converters import UfuncHelpers +from astropy.units.quantity_helper.helpers import helper_sqrt +from astropy.utils.compat.numpycompat import NUMPY_LT_2_3 +from astropy.utils.compat.optional_deps import HAS_SCIPY + + +class testcase(NamedTuple): + """A test case for a ufunc.""" + + f: Callable + """The ufunc to test.""" + + q_in: tuple[u.Quantity] + """The input quantities.""" + + q_out: tuple[u.Quantity] + """The expected output quantities.""" + + +class testexc(NamedTuple): + """A test case for a ufunc that should raise an exception.""" + + f: Callable + """The ufunc to test.""" + + q_in: tuple[u.Quantity] + """The input quantities.""" + + exc: type + """The expected exception type.""" + + msg: str | None + """The expected exception message.""" + + +class testwarn(NamedTuple): + """A test case for a ufunc that should raise a warning.""" + + f: Callable + """The ufunc to test.""" + + q_in: tuple[u.Quantity] + """The input quantities.""" + + wfilter: str + """The expected warning filter.""" + + +@pytest.mark.skip +def test_testcase(tc): + results = tc.f(*tc.q_in) + # careful of the following line, would break on a function returning + # a single tuple (as opposed to tuple of return values) + results = (results,) if not isinstance(results, tuple) else results + for result, expected in zip(results, tc.q_out): + assert result.unit == expected.unit + assert_allclose(result.value, expected.value, atol=1.0e-15) + + +@pytest.mark.skip +def test_testexc(te): + with pytest.raises(te.exc) as exc: + te.f(*te.q_in) + if te.msg is not None: + assert te.msg in exc.value.args[0] + + +@pytest.mark.skip +def test_testwarn(tw): + with warnings.catch_warnings(): + warnings.filterwarnings(tw.wfilter) + tw.f(*tw.q_in) + + +class TestUfuncHelpers: + def test_coverage(self): + """Test that we cover all ufunc's""" + all_np_ufuncs = { + ufunc for ufunc in np_umath.__dict__.values() if isinstance(ufunc, np.ufunc) + } + + all_q_ufuncs = qh.UNSUPPORTED_UFUNCS | set(qh.UFUNC_HELPERS.keys()) + # Check that every numpy ufunc is covered. + assert all_np_ufuncs - all_q_ufuncs == set() + # Check all ufuncs we cover come from numpy, erfa, or scipy. + # (Since coverage for erfa and scipy are incomplete, we do not check + # this the other way). + all_erfa_ufuncs = { + ufunc + for ufunc in erfa_ufunc.__dict__.values() + if isinstance(ufunc, np.ufunc) + } + if HAS_SCIPY: + import scipy.special as sps + + all_sps_ufuncs = { + ufunc for ufunc in sps.__dict__.values() if isinstance(ufunc, np.ufunc) + } + else: + all_sps_ufuncs = set() + + assert all_q_ufuncs - all_np_ufuncs - all_erfa_ufuncs - all_sps_ufuncs == set() + + @pytest.mark.skipif( + HAS_SCIPY, + reason=( + "UFUNC_HELPERS.modules might be in a different state " + "(by design) if scipy.special already registered" + ), + ) + def test_scipy_registered(self): + # Should be registered as existing even if scipy is not available. + assert "scipy.special" in qh.UFUNC_HELPERS.modules + + def test_removal_addition(self): + assert np.add in qh.UFUNC_HELPERS + assert np.add not in qh.UNSUPPORTED_UFUNCS + qh.UFUNC_HELPERS[np.add] = None + assert np.add not in qh.UFUNC_HELPERS + assert np.add in qh.UNSUPPORTED_UFUNCS + qh.UFUNC_HELPERS[np.add] = qh.UFUNC_HELPERS[np.subtract] + assert np.add in qh.UFUNC_HELPERS + assert np.add not in qh.UNSUPPORTED_UFUNCS + + @pytest.mark.slow + def test_thread_safety(self, fast_thread_switching): + def dummy_ufunc(*args, **kwargs): + return np.sqrt(*args, **kwargs) + + def register(): + return {dummy_ufunc: helper_sqrt} + + workers = 8 + with concurrent.futures.ThreadPoolExecutor(max_workers=workers) as executor: + for _ in range(10000): + helpers = UfuncHelpers() + helpers.register_module( + "astropy.units.tests.test_quantity_ufuncs", + ["dummy_ufunc"], + register, + ) + futures = [ + executor.submit(lambda: helpers[dummy_ufunc]) + for i in range(workers) + ] + values = [future.result() for future in futures] + assert values == [helper_sqrt] * workers + + +class TestQuantityTrigonometricFuncs: + """ + Test trigonometric functions + """ + + @pytest.mark.parametrize( + "tc", + ( + testcase( + f=np.sin, + q_in=(30.0 * u.degree,), + q_out=(0.5 * u.dimensionless_unscaled,), + ), + testcase( + f=np.sin, + q_in=(np.array([0.0, np.pi / 4.0, np.pi / 2.0]) * u.radian,), + q_out=(np.array([0.0, 1.0 / np.sqrt(2.0), 1.0]) * u.one,), + ), + testcase( + f=np.arcsin, + q_in=(np.sin(30.0 * u.degree),), + q_out=(np.radians(30.0) * u.radian,), + ), + testcase( + f=np.arcsin, + q_in=(np.sin(np.array([0.0, np.pi / 4.0, np.pi / 2.0]) * u.radian),), + q_out=(np.array([0.0, np.pi / 4.0, np.pi / 2.0]) * u.radian,), + ), + testcase( + f=np.cos, + q_in=(np.pi / 3.0 * u.radian,), + q_out=(0.5 * u.dimensionless_unscaled,), + ), + testcase( + f=np.cos, + q_in=(np.array([0.0, np.pi / 4.0, np.pi / 2.0]) * u.radian,), + q_out=(np.array([1.0, 1.0 / np.sqrt(2.0), 0.0]) * u.one,), + ), + testcase( + f=np.arccos, + q_in=(np.cos(np.pi / 3.0 * u.radian),), + q_out=(np.pi / 3.0 * u.radian,), + ), + testcase( + f=np.arccos, + q_in=(np.cos(np.array([0.0, np.pi / 4.0, np.pi / 2.0]) * u.radian),), + q_out=(np.array([0.0, np.pi / 4.0, np.pi / 2.0]) * u.radian,), + ), + testcase( + f=np.tan, + q_in=(np.pi / 3.0 * u.radian,), + q_out=(np.sqrt(3.0) * u.dimensionless_unscaled,), + ), + testcase( + f=np.tan, + q_in=(np.array([0.0, 45.0, 135.0, 180.0]) * u.degree,), + q_out=(np.array([0.0, 1.0, -1.0, 0.0]) * u.dimensionless_unscaled,), + ), + testcase( + f=np.arctan, + q_in=(np.tan(np.pi / 3.0 * u.radian),), + q_out=(np.pi / 3.0 * u.radian,), + ), + testcase( + f=np.arctan, + q_in=(np.tan(np.array([10.0, 30.0, 70.0, 80.0]) * u.degree),), + q_out=(np.radians(np.array([10.0, 30.0, 70.0, 80.0]) * u.degree),), + ), + testcase( + f=np.arctan2, + q_in=(np.array([10.0, 30.0, 70.0, 80.0]) * u.m, 2.0 * u.km), + q_out=( + np.arctan2(np.array([10.0, 30.0, 70.0, 80.0]), 2000.0) * u.radian, + ), + ), + testcase( + f=np.arctan2, + q_in=((np.array([10.0, 80.0]) * u.m / (2.0 * u.km)).to(u.one), 1.0), + q_out=(np.arctan2(np.array([10.0, 80.0]) / 2000.0, 1.0) * u.radian,), + ), + testcase(f=np.deg2rad, q_in=(180.0 * u.degree,), q_out=(np.pi * u.radian,)), + testcase(f=np.radians, q_in=(180.0 * u.degree,), q_out=(np.pi * u.radian,)), + testcase(f=np.deg2rad, q_in=(3.0 * u.radian,), q_out=(3.0 * u.radian,)), + testcase(f=np.radians, q_in=(3.0 * u.radian,), q_out=(3.0 * u.radian,)), + testcase(f=np.rad2deg, q_in=(60.0 * u.degree,), q_out=(60.0 * u.degree,)), + testcase(f=np.degrees, q_in=(60.0 * u.degree,), q_out=(60.0 * u.degree,)), + testcase(f=np.rad2deg, q_in=(np.pi * u.radian,), q_out=(180.0 * u.degree,)), + testcase(f=np.degrees, q_in=(np.pi * u.radian,), q_out=(180.0 * u.degree,)), + ), + ) + def test_testcases(self, tc): + return test_testcase(tc) + + @pytest.mark.parametrize( + "te", + ( + testexc(f=np.deg2rad, q_in=(3.0 * u.m,), exc=TypeError, msg=None), + testexc(f=np.radians, q_in=(3.0 * u.m,), exc=TypeError, msg=None), + testexc(f=np.rad2deg, q_in=(3.0 * u.m), exc=TypeError, msg=None), + testexc(f=np.degrees, q_in=(3.0 * u.m), exc=TypeError, msg=None), + testexc( + f=np.sin, + q_in=(3.0 * u.m,), + exc=TypeError, + msg="Can only apply 'sin' function to quantities with angle units", + ), + testexc( + f=np.arcsin, + q_in=(3.0 * u.m,), + exc=TypeError, + msg="Can only apply 'arcsin' function to dimensionless quantities", + ), + testexc( + f=np.cos, + q_in=(3.0 * u.s,), + exc=TypeError, + msg="Can only apply 'cos' function to quantities with angle units", + ), + testexc( + f=np.arccos, + q_in=(3.0 * u.s,), + exc=TypeError, + msg="Can only apply 'arccos' function to dimensionless quantities", + ), + testexc( + f=np.tan, + q_in=(np.array([1, 2, 3]) * u.N,), + exc=TypeError, + msg="Can only apply 'tan' function to quantities with angle units", + ), + testexc( + f=np.arctan, + q_in=(np.array([1, 2, 3]) * u.N,), + exc=TypeError, + msg="Can only apply 'arctan' function to dimensionless quantities", + ), + testexc( + f=np.arctan2, + q_in=(np.array([1, 2, 3]) * u.N, 1.0 * u.s), + exc=u.UnitsError, + msg="compatible dimensions", + ), + testexc( + f=np.arctan2, + q_in=(np.array([1, 2, 3]) * u.N, 1.0), + exc=u.UnitsError, + msg="dimensionless quantities when other arg", + ), + ), + ) + def test_testexcs(self, te): + return test_testexc(te) + + @pytest.mark.parametrize( + "tw", + (testwarn(f=np.arcsin, q_in=(27.0 * u.pc / (15 * u.kpc),), wfilter="error"),), + ) + def test_testwarns(self, tw): + return test_testwarn(tw) + + def test_sin_with_quantity_out(self): + # Test for a useful error message - see gh-16873. + # Non-quantity input should be treated as dimensionless and thus cannot + # be converted to radians. + out = u.Quantity(0) + with pytest.raises( + AttributeError, + match=( + "'NoneType' object has no attribute 'get_converter'" + ".*\n.*treated as dimensionless" + ), + ): + np.sin(0.5, out=out) + + # Except if we have the right equivalency in place. + with u.add_enabled_equivalencies(u.dimensionless_angles()): + result = np.sin(0.5, out=out) + + assert result is out + assert result == np.sin(0.5) * u.dimensionless_unscaled + + +class TestQuantityMathFuncs: + """ + Test other mathematical functions + """ + + def test_multiply_scalar(self): + assert np.multiply(4.0 * u.m, 2.0 / u.s) == 8.0 * u.m / u.s + assert np.multiply(4.0 * u.m, 2.0) == 8.0 * u.m + assert np.multiply(4.0, 2.0 / u.s) == 8.0 / u.s + + def test_multiply_array(self): + assert np.all( + np.multiply(np.arange(3.0) * u.m, 2.0 / u.s) + == np.arange(0, 6.0, 2.0) * u.m / u.s + ) + + def test_matmul(self): + q = np.arange(3.0) * u.m + r = np.matmul(q, q) + assert r == 5.0 * u.m**2 + # less trivial case. + q1 = np.eye(3) * u.m + q2 = np.array( + [[[1., 0., 0.], + [0., 1., 0.], + [0., 0., 1.]], + [[0., 1., 0.], + [0., 0., 1.], + [1., 0., 0.]], + [[0., 0., 1.], + [1., 0., 0.], + [0., 1., 0.]]] + ) / u.s # fmt: skip + r2 = np.matmul(q1, q2) + assert np.all(r2 == np.matmul(q1.value, q2.value) * q1.unit * q2.unit) + + def test_vecdot(self): + q1 = np.array([1j, 2j, 3j]) * u.m + q2 = np.array([4j, 5j, 6j]) / u.s + o = np.vecdot(q1, q2) + assert o == (32.0 + 0j) * u.m / u.s + + @pytest.mark.skipif( + NUMPY_LT_2_3, reason="np.matvec and np.vecmat are new in NumPy 2.3" + ) + def test_matvec(self): + vec = np.arange(3) << u.s + mat = ( + np.array( + [ + [1.0, -1.0, 2.0], + [0.0, 3.0, -1.0], + [-1.0, -1.0, 1.0], + ] + ) + << u.m + ) + ref_matvec = (vec * mat).sum(-1) + res_matvec = np.matvec(mat, vec) + assert_array_equal(res_matvec, ref_matvec) + + ref_vecmat = (vec * mat.T).sum(-1) + res_vecmat = np.vecmat(vec, mat) + assert_array_equal(res_vecmat, ref_vecmat) + + @pytest.mark.parametrize("function", (np.divide, np.true_divide)) + def test_divide_scalar(self, function): + assert function(4.0 * u.m, 2.0 * u.s) == function(4.0, 2.0) * u.m / u.s + assert function(4.0 * u.m, 2.0) == function(4.0, 2.0) * u.m + assert function(4.0, 2.0 * u.s) == function(4.0, 2.0) / u.s + + @pytest.mark.parametrize("function", (np.divide, np.true_divide)) + def test_divide_array(self, function): + assert np.all( + function(np.arange(3.0) * u.m, 2.0 * u.s) + == function(np.arange(3.0), 2.0) * u.m / u.s + ) + + def test_floor_divide_remainder_and_divmod(self): + inch = u.Unit(0.0254 * u.m) + dividend = np.array([1.0, 2.0, 3.0]) * u.m + divisor = np.array([3.0, 4.0, 5.0]) * inch + quotient = dividend // divisor + remainder = dividend % divisor + assert_allclose(quotient.value, [13.0, 19.0, 23.0]) + assert quotient.unit == u.dimensionless_unscaled + assert_allclose(remainder.value, [0.0094, 0.0696, 0.079]) + assert remainder.unit == dividend.unit + quotient2 = np.floor_divide(dividend, divisor) + remainder2 = np.remainder(dividend, divisor) + assert np.all(quotient2 == quotient) + assert np.all(remainder2 == remainder) + quotient3, remainder3 = divmod(dividend, divisor) + assert np.all(quotient3 == quotient) + assert np.all(remainder3 == remainder) + + with pytest.raises(TypeError): + divmod(dividend, u.km) + + with pytest.raises(TypeError): + dividend // u.km + + with pytest.raises(TypeError): + dividend % u.km + + quotient4, remainder4 = np.divmod(dividend, divisor) + assert np.all(quotient4 == quotient) + assert np.all(remainder4 == remainder) + with pytest.raises(TypeError): + np.divmod(dividend, u.km) + + def test_sqrt_scalar(self): + assert np.sqrt(4.0 * u.m) == 2.0 * u.m**0.5 + + def test_sqrt_array(self): + assert np.all( + np.sqrt(np.array([1.0, 4.0, 9.0]) * u.m) + == np.array([1.0, 2.0, 3.0]) * u.m**0.5 + ) + + def test_square_scalar(self): + assert np.square(4.0 * u.m) == 16.0 * u.m**2 + + def test_square_array(self): + assert np.all( + np.square(np.array([1.0, 2.0, 3.0]) * u.m) + == np.array([1.0, 4.0, 9.0]) * u.m**2 + ) + + def test_reciprocal_scalar(self): + assert np.reciprocal(4.0 * u.m) == 0.25 / u.m + + def test_reciprocal_array(self): + assert np.all( + np.reciprocal(np.array([1.0, 2.0, 4.0]) * u.m) + == np.array([1.0, 0.5, 0.25]) / u.m + ) + + def test_heaviside_scalar(self): + assert np.heaviside(0.0 * u.m, 0.5) == 0.5 * u.dimensionless_unscaled + assert ( + np.heaviside(0.0 * u.s, 25 * u.percent) == 0.25 * u.dimensionless_unscaled + ) + assert np.heaviside(2.0 * u.J, 0.25) == 1.0 * u.dimensionless_unscaled + + def test_heaviside_array(self): + values = np.array([-1.0, 0.0, 0.0, +1.0]) + halfway = np.array([0.75, 0.25, 0.75, 0.25]) * u.dimensionless_unscaled + assert np.all( + np.heaviside(values * u.m, halfway * u.dimensionless_unscaled) + == [0, 0.25, 0.75, +1.0] * u.dimensionless_unscaled + ) + + @pytest.mark.parametrize("function", (np.cbrt,)) + def test_cbrt_scalar(self, function): + assert function(8.0 * u.m**3) == 2.0 * u.m + + @pytest.mark.parametrize("function", (np.cbrt,)) + def test_cbrt_array(self, function): + # Calculate cbrt on both sides since on Windows the cube root of 64 + # does not exactly equal 4. See 4388. + values = np.array([1.0, 8.0, 64.0]) + assert np.all(function(values * u.m**3) == function(values) * u.m) + + def test_power_scalar(self): + assert np.power(4.0 * u.m, 2.0) == 16.0 * u.m**2 + assert np.power(4.0, 200.0 * u.cm / u.m) == u.Quantity( + 16.0, u.dimensionless_unscaled + ) + # regression check on #1696 + assert np.power(4.0 * u.m, 0.0) == 1.0 * u.dimensionless_unscaled + + def test_power_scalar_filledarray(self): + result = np.power(4.0 * u.m, np.array([2.0, 2.0])) + assert np.all(result == 16.0 * u.m**2) + + def test_power_scalar_strarray(self): + with pytest.raises( + expected_exception=ValueError, + match="could not convert string to float", + ): + np.power(4.0 * u.m, np.array(["foo"])) + + def test_power_array(self): + assert np.all( + np.power(np.array([1.0, 2.0, 3.0]) * u.m, 3.0) + == np.array([1.0, 8.0, 27.0]) * u.m**3 + ) + # regression check on #1696 + assert np.all( + np.power(np.arange(4.0) * u.m, 0.0) == 1.0 * u.dimensionless_unscaled + ) + + def test_float_power_array(self): + assert np.all( + np.float_power(np.array([1.0, 2.0, 3.0]) * u.m, 3.0) + == np.array([1.0, 8.0, 27.0]) * u.m**3 + ) + # regression check on #1696 + assert np.all( + np.float_power(np.arange(4.0) * u.m, 0.0) == 1.0 * u.dimensionless_unscaled + ) + + def test_power_array_array(self): + with pytest.raises(ValueError): + np.power(4.0 * u.m, [2.0, 4.0]) + + def test_power_array_array2(self): + with pytest.raises(ValueError): + np.power([2.0, 4.0] * u.m, [2.0, 4.0]) + + def test_power_array_array3(self): + # Identical unit fractions are converted automatically to dimensionless + # and should be allowed as base for np.power: #4764 + q = [2.0, 4.0] * u.m / u.m + powers = [2.0, 4.0] + res = np.power(q, powers) + assert np.all(res.value == q.value**powers) + assert res.unit == u.dimensionless_unscaled + # The same holds for unit fractions that are scaled dimensionless. + q2 = [2.0, 4.0] * u.m / u.cm + # Test also against different types of exponent + for cls in (list, tuple, np.array, np.ma.array, u.Quantity): + res2 = np.power(q2, cls(powers)) + assert np.all(res2.value == q2.to_value(1) ** powers) + assert res2.unit == u.dimensionless_unscaled + # Though for single powers, we keep the composite unit. + res3 = q2**2 + assert np.all(res3.value == q2.value**2) + assert res3.unit == q2.unit**2 + assert np.all(res3 == q2 ** [2, 2]) + + def test_power_invalid(self): + with pytest.raises(TypeError, match="raise something to a dimensionless"): + np.power(3.0, 4.0 * u.m) + + def test_copysign_scalar(self): + assert np.copysign(3 * u.m, 1.0) == 3.0 * u.m + assert np.copysign(3 * u.m, 1.0 * u.s) == 3.0 * u.m + assert np.copysign(3 * u.m, -1.0) == -3.0 * u.m + assert np.copysign(3 * u.m, -1.0 * u.s) == -3.0 * u.m + + def test_copysign_array(self): + assert np.all( + np.copysign(np.array([1.0, 2.0, 3.0]) * u.s, -1.0) + == -np.array([1.0, 2.0, 3.0]) * u.s + ) + assert np.all( + np.copysign(np.array([1.0, 2.0, 3.0]) * u.s, -1.0 * u.m) + == -np.array([1.0, 2.0, 3.0]) * u.s + ) + assert np.all( + np.copysign( + np.array([1.0, 2.0, 3.0]) * u.s, np.array([-2.0, 2.0, -4.0]) * u.m + ) + == np.array([-1.0, 2.0, -3.0]) * u.s + ) + + q = np.copysign(np.array([1.0, 2.0, 3.0]), -3 * u.m) + assert np.all(q == np.array([-1.0, -2.0, -3.0])) + assert not isinstance(q, u.Quantity) + + def test_ldexp_scalar(self): + assert np.ldexp(4.0 * u.m, 2) == 16.0 * u.m + + def test_ldexp_array(self): + assert np.all( + np.ldexp(np.array([1.0, 2.0, 3.0]) * u.m, [3, 2, 1]) + == np.array([8.0, 8.0, 6.0]) * u.m + ) + + def test_ldexp_invalid(self): + with pytest.raises(TypeError): + np.ldexp(3.0 * u.m, 4.0) + + with pytest.raises(TypeError): + np.ldexp(3.0, u.Quantity(4, u.m, dtype=int)) + + @pytest.mark.parametrize( + "function", (np.exp, np.expm1, np.exp2, np.log, np.log2, np.log10, np.log1p) + ) + def test_exp_scalar(self, function): + q = function(3.0 * u.m / (6.0 * u.m)) + assert q.unit == u.dimensionless_unscaled + assert q.value == function(0.5) + + @pytest.mark.parametrize( + "function", (np.exp, np.expm1, np.exp2, np.log, np.log2, np.log10, np.log1p) + ) + def test_exp_array(self, function): + q = function(np.array([2.0, 3.0, 6.0]) * u.m / (6.0 * u.m)) + assert q.unit == u.dimensionless_unscaled + assert np.all(q.value == function(np.array([1.0 / 3.0, 1.0 / 2.0, 1.0]))) + # should also work on quantities that can be made dimensionless + q2 = function(np.array([2.0, 3.0, 6.0]) * u.m / (6.0 * u.cm)) + assert q2.unit == u.dimensionless_unscaled + assert_allclose(q2.value, function(np.array([100.0 / 3.0, 100.0 / 2.0, 100.0]))) + + @pytest.mark.parametrize( + "function", (np.exp, np.expm1, np.exp2, np.log, np.log2, np.log10, np.log1p) + ) + def test_exp_invalid_units(self, function): + # Can't use exp() with non-dimensionless quantities + with pytest.raises( + TypeError, + match=( + f"Can only apply '{function.__name__}' function " + "to dimensionless quantities" + ), + ): + function(3.0 * u.m / u.s) + + def test_modf_scalar(self): + q = np.modf(9.0 * u.m / (600.0 * u.cm)) + assert q == (0.5 * u.dimensionless_unscaled, 1.0 * u.dimensionless_unscaled) + + def test_modf_array(self): + v = np.arange(10.0) * u.m / (500.0 * u.cm) + q = np.modf(v) + n = np.modf(v.to_value(u.dimensionless_unscaled)) + assert q[0].unit == u.dimensionless_unscaled + assert q[1].unit == u.dimensionless_unscaled + assert all(q[0].value == n[0]) + assert all(q[1].value == n[1]) + + def test_frexp_scalar(self): + q = np.frexp(3.0 * u.m / (6.0 * u.m)) + assert q == (np.array(0.5), np.array(0.0)) + + def test_frexp_array(self): + q = np.frexp(np.array([2.0, 3.0, 6.0]) * u.m / (6.0 * u.m)) + assert all( + (_q0, _q1) == np.frexp(_d) + for _q0, _q1, _d in zip(q[0], q[1], [1.0 / 3.0, 1.0 / 2.0, 1.0]) + ) + + def test_frexp_invalid_units(self): + # Can't use prod() with non-dimensionless quantities + with pytest.raises( + TypeError, + match=( + "Can only apply 'frexp' function to unscaled dimensionless quantities" + ), + ): + np.frexp(3.0 * u.m / u.s) + + # also does not work on quantities that can be made dimensionless + with pytest.raises( + TypeError, + match=( + "Can only apply 'frexp' function to unscaled dimensionless quantities" + ), + ): + np.frexp(np.array([2.0, 3.0, 6.0]) * u.m / (6.0 * u.cm)) + + @pytest.mark.parametrize("function", (np.logaddexp, np.logaddexp2)) + def test_dimensionless_twoarg_array(self, function): + q = function(np.array([2.0, 3.0, 6.0]) * u.m / (6.0 * u.cm), 1.0) + assert q.unit == u.dimensionless_unscaled + assert_allclose( + q.value, function(np.array([100.0 / 3.0, 100.0 / 2.0, 100.0]), 1.0) + ) + + @pytest.mark.parametrize("function", (np.logaddexp, np.logaddexp2)) + def test_dimensionless_twoarg_invalid_units(self, function): + with pytest.raises( + TypeError, + match=( + f"Can only apply '{function.__name__}' function to dimensionless" + " quantities" + ), + ): + function(1.0 * u.km / u.s, 3.0 * u.m / u.s) + + +class TestInvariantUfuncs: + @pytest.mark.parametrize( + "ufunc", + [ + np.absolute, + np.fabs, + np.conj, + np.conjugate, + np.negative, + np.spacing, + np.rint, + np.floor, + np.ceil, + np.positive, + ], + ) + def test_invariant_scalar(self, ufunc): + q_i = 4.7 * u.m + q_o = ufunc(q_i) + assert isinstance(q_o, u.Quantity) + assert q_o.unit == q_i.unit + assert q_o.value == ufunc(q_i.value) + + @pytest.mark.parametrize( + "ufunc", [np.absolute, np.conjugate, np.negative, np.rint, np.floor, np.ceil] + ) + def test_invariant_array(self, ufunc): + q_i = np.array([-3.3, 2.1, 10.2]) * u.kg / u.s + q_o = ufunc(q_i) + assert isinstance(q_o, u.Quantity) + assert q_o.unit == q_i.unit + assert np.all(q_o.value == ufunc(q_i.value)) + + @pytest.mark.parametrize( + "ufunc", + [ + np.add, + np.subtract, + np.hypot, + np.maximum, + np.minimum, + np.nextafter, + np.remainder, + np.mod, + np.fmod, + ], + ) + def test_invariant_twoarg_scalar(self, ufunc): + q_i1 = 4.7 * u.m + q_i2 = 9.4 * u.km + q_o = ufunc(q_i1, q_i2) + assert isinstance(q_o, u.Quantity) + assert q_o.unit == q_i1.unit + assert_allclose(q_o.value, ufunc(q_i1.value, q_i2.to_value(q_i1.unit))) + + @pytest.mark.parametrize( + "ufunc", + [ + np.add, + np.subtract, + np.hypot, + np.maximum, + np.minimum, + np.nextafter, + np.remainder, + np.mod, + np.fmod, + ], + ) + def test_invariant_twoarg_array(self, ufunc): + q_i1 = np.array([-3.3, 2.1, 10.2]) * u.kg / u.s + q_i2 = np.array([10.0, -5.0, 1.0e6]) * u.g / u.us + q_o = ufunc(q_i1, q_i2) + assert isinstance(q_o, u.Quantity) + assert q_o.unit == q_i1.unit + assert_allclose(q_o.value, ufunc(q_i1.value, q_i2.to_value(q_i1.unit))) + + @pytest.mark.parametrize( + ("ufunc", "arbitrary"), + [ + (np.add, 0.0), + (np.subtract, 0.0), + (np.hypot, 0.0), + (np.maximum, 0.0), + (np.minimum, 0.0), + (np.nextafter, 0.0), + (np.remainder, np.inf), + (np.mod, np.inf), + (np.fmod, np.inf), + ], + ) + def test_invariant_twoarg_one_arbitrary(self, ufunc, arbitrary): + q_i1 = np.array([-3.3, 2.1, 10.2]) * u.kg / u.s + q_o = ufunc(q_i1, arbitrary) + assert isinstance(q_o, u.Quantity) + assert q_o.unit == q_i1.unit + assert_allclose(q_o.value, ufunc(q_i1.value, arbitrary)) + + @pytest.mark.parametrize( + "ufunc", + [ + np.add, + np.subtract, + np.hypot, + np.maximum, + np.minimum, + np.nextafter, + np.remainder, + np.mod, + np.fmod, + ], + ) + def test_invariant_twoarg_invalid_units(self, ufunc): + q_i1 = 4.7 * u.m + q_i2 = 9.4 * u.s + with pytest.raises(u.UnitsError, match="compatible dimensions"): + ufunc(q_i1, q_i2) + + +class TestComparisonUfuncs: + @pytest.mark.parametrize( + "ufunc", + [np.greater, np.greater_equal, np.less, np.less_equal, np.not_equal, np.equal], + ) + def test_comparison_valid_units(self, ufunc): + q_i1 = np.array([-3.3, 2.1, 10.2]) * u.kg / u.s + q_i2 = np.array([10.0, -5.0, 1.0e6]) * u.g / u.Ms + q_o = ufunc(q_i1, q_i2) + assert not isinstance(q_o, u.Quantity) + assert q_o.dtype == bool + assert np.all(q_o == ufunc(q_i1.value, q_i2.to_value(q_i1.unit))) + q_o2 = ufunc(q_i1 / q_i2, 2.0) + assert not isinstance(q_o2, u.Quantity) + assert q_o2.dtype == bool + assert np.all( + q_o2 == ufunc((q_i1 / q_i2).to_value(u.dimensionless_unscaled), 2.0) + ) + # comparison with 0., inf, nan is OK even for dimensional quantities + # (though ignore numpy runtime warnings for comparisons with nan). + with warnings.catch_warnings(): + warnings.filterwarnings("ignore", category=RuntimeWarning) + for arbitrary_unit_value in (0.0, np.inf, np.nan): + ufunc(q_i1, arbitrary_unit_value) + ufunc(q_i1, arbitrary_unit_value * np.ones(len(q_i1))) + # and just for completeness + ufunc(q_i1, np.array([0.0, np.inf, np.nan])) + + @pytest.mark.parametrize( + "ufunc", + [np.greater, np.greater_equal, np.less, np.less_equal, np.not_equal, np.equal], + ) + def test_comparison_invalid_units(self, ufunc): + q_i1 = 4.7 * u.m + q_i2 = 9.4 * u.s + with pytest.raises(u.UnitsError, match="compatible dimensions"): + ufunc(q_i1, q_i2) + + @pytest.mark.parametrize("ufunc", (np.isfinite, np.isinf, np.isnan, np.signbit)) + def test_onearg_test_ufuncs(self, ufunc): + q = [1.0, np.inf, -np.inf, np.nan, -1.0, 0.0] * u.m + out = ufunc(q) + assert not isinstance(out, u.Quantity) + assert out.dtype == bool + assert np.all(out == ufunc(q.value)) + + # Ignore RuntimeWarning raised on Windows and s390. + @pytest.mark.filterwarnings("ignore:.*invalid value encountered in sign") + def test_sign(self): + q = [1.0, np.inf, -np.inf, np.nan, -1.0, 0.0] * u.m + + out = np.sign(q) + assert not isinstance(out, u.Quantity) + assert out.dtype == q.dtype + assert np.all((out == np.sign(q.value)) | (np.isnan(out) & np.isnan(q.value))) + + +class TestInplaceUfuncs: + @pytest.mark.parametrize("value", [1.0, np.arange(10.0)]) + def test_one_argument_ufunc_inplace(self, value): + # without scaling + s = value * u.rad + check = s + np.sin(s, out=s) + assert check is s + assert check.unit == u.dimensionless_unscaled + # with scaling + s2 = (value * u.rad).to(u.deg) + check2 = s2 + np.sin(s2, out=s2) + assert check2 is s2 + assert check2.unit == u.dimensionless_unscaled + assert_allclose(s.value, s2.value) + + @pytest.mark.parametrize("value", [1.0, np.arange(10.0)]) + def test_one_argument_ufunc_inplace_2(self, value): + """Check inplace works with non-quantity input and quantity output""" + s = value * u.m + check = s + np.absolute(value, out=s) + assert check is s + assert np.all(check.value == np.absolute(value)) + assert check.unit is u.dimensionless_unscaled + np.sqrt(value, out=s) + assert check is s + assert np.all(check.value == np.sqrt(value)) + assert check.unit is u.dimensionless_unscaled + np.exp(value, out=s) + assert check is s + assert np.all(check.value == np.exp(value)) + assert check.unit is u.dimensionless_unscaled + np.arcsin(value / 10.0, out=s) + assert check is s + assert np.all(check.value == np.arcsin(value / 10.0)) + assert check.unit is u.radian + + @pytest.mark.parametrize("value", [1.0, np.arange(10.0)]) + def test_one_argument_two_output_ufunc_inplace(self, value): + v = 100.0 * value * u.cm / u.m + v_copy = v.copy() + tmp = v.copy() + check = v + np.modf(v, tmp, v) + assert check is v + assert check.unit == u.dimensionless_unscaled + v2 = v_copy.to(u.dimensionless_unscaled) + check2 = v2 + np.modf(v2, tmp, v2) + assert check2 is v2 + assert check2.unit == u.dimensionless_unscaled + # can also replace in last position if no scaling is needed + v3 = v_copy.to(u.dimensionless_unscaled) + check3 = v3 + np.modf(v3, v3, tmp) + assert check3 is v3 + assert check3.unit == u.dimensionless_unscaled + # can also replace input with first output when scaling + v4 = v_copy.copy() + check4 = v4 + np.modf(v4, v4, tmp) + assert check4 is v4 + assert check4.unit == u.dimensionless_unscaled + + @pytest.mark.parametrize("value", [1.0, np.arange(10.0)]) + def test_two_argument_ufunc_inplace_1(self, value): + s = value * u.cycle + check = s + s /= 2.0 + assert check is s + assert np.all(check.value == value / 2.0) + s /= u.s + assert check is s + assert check.unit == u.cycle / u.s + s *= 2.0 * u.s + assert check is s + assert np.all(check == value * u.cycle) + + @pytest.mark.parametrize("value", [1.0, np.arange(10.0)]) + def test_two_argument_ufunc_inplace_2(self, value): + s = value * u.cycle + check = s + np.arctan2(s, s, out=s) + assert check is s + assert check.unit == u.radian + with pytest.raises(u.UnitsError): + s += 1.0 * u.m + assert check is s + assert check.unit == u.radian + np.arctan2(1.0 * u.deg, s, out=s) + assert check is s + assert check.unit == u.radian + np.add(1.0 * u.deg, s, out=s) + assert check is s + assert check.unit == u.deg + np.multiply(2.0 / u.s, s, out=s) + assert check is s + assert check.unit == u.deg / u.s + + def test_two_argument_ufunc_inplace_3(self): + s = np.array([1.0, 2.0, 3.0]) * u.dimensionless_unscaled + np.add(np.array([1.0, 2.0, 3.0]), np.array([1.0, 2.0, 3.0]) * 2.0, out=s) + assert np.all(s.value == np.array([3.0, 6.0, 9.0])) + assert s.unit is u.dimensionless_unscaled + np.arctan2(np.array([1.0, 2.0, 3.0]), np.array([1.0, 2.0, 3.0]) * 2.0, out=s) + assert_allclose(s.value, np.arctan2(1.0, 2.0)) + assert s.unit is u.radian + + @pytest.mark.parametrize("value", [1.0, np.arange(10.0)]) + def test_two_argument_two_output_ufunc_inplace(self, value): + v = value * u.m + divisor = 70.0 * u.cm + v1 = v.copy() + tmp = v.copy() + check = np.divmod(v1, divisor, out=(tmp, v1)) + assert check[0] is tmp and check[1] is v1 + assert tmp.unit == u.dimensionless_unscaled + assert v1.unit == v.unit + v2 = v.copy() + check2 = np.divmod(v2, divisor, out=(v2, tmp)) + assert check2[0] is v2 and check2[1] is tmp + assert v2.unit == u.dimensionless_unscaled + assert tmp.unit == v.unit + v3a = v.copy() + v3b = v.copy() + check3 = np.divmod(v3a, divisor, out=(v3a, v3b)) + assert check3[0] is v3a and check3[1] is v3b + assert v3a.unit == u.dimensionless_unscaled + assert v3b.unit == v.unit + + def test_ufunc_inplace_non_contiguous_data(self): + # ensure inplace works also for non-contiguous data (closes #1834) + s = np.arange(10.0) * u.m + s_copy = s.copy() + s2 = s[::2] + s2 += 1.0 * u.cm + assert np.all(s[::2] > s_copy[::2]) + assert np.all(s[1::2] == s_copy[1::2]) + + def test_ufunc_inplace_non_standard_dtype(self): + """Check that inplace operations check properly for casting. + + First two tests that check that float32 is kept close #3976. + """ + a1 = u.Quantity([1, 2, 3, 4], u.m, dtype=np.float32) + a1 *= np.float32(10) + assert a1.unit is u.m + assert a1.dtype == np.float32 + a2 = u.Quantity([1, 2, 3, 4], u.m, dtype=np.float32) + a2 += 20.0 * u.km + assert a2.unit is u.m + assert a2.dtype == np.float32 + # For integer, in-place only works if no conversion is done. + a3 = u.Quantity([1, 2, 3, 4], u.m, dtype=np.int32) + a3 += u.Quantity(10, u.m, dtype=np.int64) + assert a3.unit is u.m + assert a3.dtype == np.int32 + a4 = u.Quantity([1, 2, 3, 4], u.m, dtype=np.int32) + with pytest.raises(TypeError): + a4 += u.Quantity(10, u.mm, dtype=np.int64) + + @pytest.mark.parametrize("ufunc", (np.equal, np.greater)) + def test_comparison_ufuncs_inplace(self, ufunc): + q_i1 = np.array([-3.3, 2.1, 10.2]) * u.kg / u.s + q_i2 = np.array([10.0, -5.0, 1.0e6]) * u.g / u.Ms + check = np.empty(q_i1.shape, bool) + ufunc(q_i1.value, q_i2.to_value(q_i1.unit), out=check) + + result = np.empty(q_i1.shape, bool) + q_o = ufunc(q_i1, q_i2, out=result) + assert q_o is result + assert type(q_o) is np.ndarray + assert q_o.dtype == bool + assert np.all(q_o == check) + + @pytest.mark.parametrize("ufunc", (np.isfinite, np.signbit)) + def test_onearg_test_ufuncs_inplace(self, ufunc): + q = [1.0, np.inf, -np.inf, np.nan, -1.0, 0.0] * u.m + check = np.empty(q.shape, bool) + ufunc(q.value, out=check) + + result = np.empty(q.shape, bool) + out = ufunc(q, out=result) + assert out is result + assert type(out) is np.ndarray + assert out.dtype == bool + assert np.all(out == ufunc(q.value)) + + # Ignore RuntimeWarning raised on Windows and s390. + @pytest.mark.filterwarnings("ignore:.*invalid value encountered in sign") + def test_sign_inplace(self): + q = [1.0, np.inf, -np.inf, np.nan, -1.0, 0.0] * u.m + check = np.empty(q.shape, q.dtype) + + np.sign(q.value, out=check) + + result = np.empty(q.shape, q.dtype) + out = np.sign(q, out=result) + assert out is result + assert type(out) is np.ndarray + assert out.dtype == q.dtype + assert np.all((out == np.sign(q.value)) | (np.isnan(out) & np.isnan(q.value))) + + def test_ndarray_inplace_op_with_quantity(self): + """Regression test for gh-13911.""" + a = np.arange(3.0) + q = u.Quantity([12.5, 25.0], u.percent) + a[:2] += q # This used to fail + assert_array_equal(a, np.array([0.125, 1.25, 2.0])) + + def test_ndarray_inplace_op_with_dimensionless_quantity(self): + # Regression test for #18866 - multiplying a bare array inplace with + # a dimensionless Quantity required the unit to be u.dimensionless_unscaled. + # Mere equality was not good enough. + arr = np.ones((1,)) + arr *= 1 / np.cos(0 * u.deg) + assert arr[0] == 1 + + +class TestWhere: + """Test the where argument in ufuncs.""" + + def test_where(self): + q = np.arange(4.0) << u.m + out = np.zeros(4) << u.m + result = np.add(q, 1 * u.km, out=out, where=[True, True, True, False]) + assert result is out + assert_array_equal(result, [1000.0, 1001.0, 1002.0, 0.0] << u.m) + + def test_exception_with_where_quantity(self): + a = np.ones(2) + where = np.ones(2, bool) << u.m + with pytest.raises(TypeError, match="all returned NotImplemented"): + np.add(a, a, out=a, where=where) + + +@pytest.mark.skipif(not hasattr(np_umath, "clip"), reason="no clip ufunc available") +class TestClip: + """Test the clip ufunc. + + In numpy, this is hidden behind a function that does not backwards + compatibility checks. We explicitly test the ufunc here. + """ + + def setup_method(self): + self.clip = np_umath.clip + + def test_clip_simple(self): + q = np.arange(-1.0, 10.0) * u.m + q_min = 125 * u.cm + q_max = 0.0055 * u.km + result = self.clip(q, q_min, q_max) + assert result.unit == q.unit + expected = ( + self.clip(q.value, q_min.to_value(q.unit), q_max.to_value(q.unit)) * q.unit + ) + assert np.all(result == expected) + + def test_clip_unitless_parts(self): + q = np.arange(-1.0, 10.0) * u.m + qlim = 0.0055 * u.km + # one-sided + result1 = self.clip(q, -np.inf, qlim) + expected1 = self.clip(q.value, -np.inf, qlim.to_value(q.unit)) * q.unit + assert np.all(result1 == expected1) + result2 = self.clip(q, qlim, np.inf) + expected2 = self.clip(q.value, qlim.to_value(q.unit), np.inf) * q.unit + assert np.all(result2 == expected2) + # Zero + result3 = self.clip(q, np.zeros(q.shape), qlim) + expected3 = self.clip(q.value, 0, qlim.to_value(q.unit)) * q.unit + assert np.all(result3 == expected3) + # Two unitless parts, array-shaped. + result4 = self.clip(q, np.zeros(q.shape), np.full(q.shape, np.inf)) + expected4 = self.clip(q.value, 0, np.inf) * q.unit + assert np.all(result4 == expected4) + + def test_clip_dimensionless(self): + q = np.arange(-1.0, 10.0) * u.dimensionless_unscaled + result = self.clip(q, 200 * u.percent, 5.0) + expected = self.clip(q, 2.0, 5.0) + assert result.unit == u.dimensionless_unscaled + assert np.all(result == expected) + + def test_clip_ndarray(self): + a = np.arange(-1.0, 10.0) + result = self.clip(a, 200 * u.percent, 5.0 * u.dimensionless_unscaled) + assert isinstance(result, u.Quantity) + expected = self.clip(a, 2.0, 5.0) * u.dimensionless_unscaled + assert np.all(result == expected) + + def test_clip_quantity_inplace(self): + q = np.arange(-1.0, 10.0) * u.m + q_min = 125 * u.cm + q_max = 0.0055 * u.km + expected = ( + self.clip(q.value, q_min.to_value(q.unit), q_max.to_value(q.unit)) * q.unit + ) + result = self.clip(q, q_min, q_max, out=q) + assert result is q + assert np.all(result == expected) + + def test_clip_ndarray_dimensionless_output(self): + a = np.arange(-1.0, 10.0) + q = np.zeros_like(a) * u.m + expected = self.clip(a, 2.0, 5.0) * u.dimensionless_unscaled + result = self.clip(a, 200 * u.percent, 5.0 * u.dimensionless_unscaled, out=q) + assert result is q + assert result.unit == u.dimensionless_unscaled + assert np.all(result == expected) + + def test_clip_errors(self): + q = np.arange(-1.0, 10.0) * u.m + with pytest.raises(u.UnitsError): + self.clip(q, 0, 1 * u.s) + with pytest.raises(u.UnitsError): + self.clip(q.value, 0, 1 * u.s) + with pytest.raises(u.UnitsError): + self.clip(q, -1, 0.0) + with pytest.raises(u.UnitsError): + self.clip(q, 0.0, 1.0) + + +class TestUfuncAt: + """Test that 'at' method for ufuncs (calculates in-place at given indices) + + For Quantities, since calculations are in-place, it makes sense only + if the result is still a quantity, and if the unit does not have to change + """ + + def test_one_argument_ufunc_at(self): + q = np.arange(10.0) * u.m + i = np.array([1, 2]) + qv = q.value.copy() + np.negative.at(q, i) + np.negative.at(qv, i) + assert np.all(q.value == qv) + assert q.unit is u.m + + # cannot change from quantity to bool array + with pytest.raises(TypeError): + np.isfinite.at(q, i) + + # for selective in-place, cannot change the unit + with pytest.raises(u.UnitsError): + np.square.at(q, i) + + # except if the unit does not change (i.e., dimensionless) + d = np.arange(10.0) * u.dimensionless_unscaled + dv = d.value.copy() + np.square.at(d, i) + np.square.at(dv, i) + assert np.all(d.value == dv) + assert d.unit is u.dimensionless_unscaled + + d = np.arange(10.0) * u.dimensionless_unscaled + dv = d.value.copy() + np.log.at(d, i) + np.log.at(dv, i) + assert np.all(d.value == dv) + assert d.unit is u.dimensionless_unscaled + + # also for sine it doesn't work, even if given an angle + a = np.arange(10.0) * u.radian + with pytest.raises(u.UnitsError): + np.sin.at(a, i) + + # except, for consistency, if we have made radian equivalent to + # dimensionless (though hopefully it will never be needed) + av = a.value.copy() + with u.add_enabled_equivalencies(u.dimensionless_angles()): + np.sin.at(a, i) + np.sin.at(av, i) + assert_allclose(a.value, av) + + # but we won't do double conversion + ad = np.arange(10.0) * u.degree + with pytest.raises(u.UnitsError): + np.sin.at(ad, i) + + def test_two_argument_ufunc_at(self): + s = np.arange(10.0) * u.m + i = np.array([1, 2]) + check = s.value.copy() + np.add.at(s, i, 1.0 * u.km) + np.add.at(check, i, 1000.0) + assert np.all(s.value == check) + assert s.unit is u.m + + with pytest.raises(u.UnitsError): + np.add.at(s, i, 1.0 * u.s) + + # also raise UnitsError if unit would have to be changed + with pytest.raises(u.UnitsError): + np.multiply.at(s, i, 1 * u.s) + + # but be fine if it does not + s = np.arange(10.0) * u.m + check = s.value.copy() + np.multiply.at(s, i, 2.0 * u.dimensionless_unscaled) + np.multiply.at(check, i, 2) + assert np.all(s.value == check) + s = np.arange(10.0) * u.m + np.multiply.at(s, i, 2.0) + assert np.all(s.value == check) + + # of course cannot change class of data either + with pytest.raises(TypeError): + np.greater.at(s, i, 1.0 * u.km) + + +class TestUfuncReduceReduceatAccumulate: + """Test 'reduce', 'reduceat' and 'accumulate' methods for ufuncs + + For Quantities, it makes sense only if the unit does not have to change + """ + + def test_one_argument_ufunc_reduce_accumulate(self): + # one argument cannot be used + s = np.arange(10.0) * u.radian + i = np.array([0, 5, 1, 6]) + with pytest.raises(ValueError): + np.sin.reduce(s) + with pytest.raises(ValueError): + np.sin.accumulate(s) + with pytest.raises(ValueError): + np.sin.reduceat(s, i) + + def test_two_argument_ufunc_reduce_accumulate(self): + s = np.arange(10.0) * u.m + i = np.array([0, 5, 1, 6]) + check = s.value.copy() + s_add_reduce = np.add.reduce(s) + check_add_reduce = np.add.reduce(check) + assert s_add_reduce.value == check_add_reduce + assert s_add_reduce.unit is u.m + + s_add_accumulate = np.add.accumulate(s) + check_add_accumulate = np.add.accumulate(check) + assert np.all(s_add_accumulate.value == check_add_accumulate) + assert s_add_accumulate.unit is u.m + + s_add_reduceat = np.add.reduceat(s, i) + check_add_reduceat = np.add.reduceat(check, i) + assert np.all(s_add_reduceat.value == check_add_reduceat) + assert s_add_reduceat.unit is u.m + + # reduce(at) or accumulate on comparisons makes no sense, + # as intermediate result is not even a Quantity + with pytest.raises(TypeError): + np.greater.reduce(s) + + with pytest.raises(TypeError): + np.greater.accumulate(s) + + with pytest.raises(TypeError): + np.greater.reduceat(s, i) + + # raise UnitsError if unit would have to be changed + with pytest.raises(u.UnitsError): + np.multiply.reduce(s) + + with pytest.raises(u.UnitsError): + np.multiply.accumulate(s) + + with pytest.raises(u.UnitsError): + np.multiply.reduceat(s, i) + + # but be fine if it does not + s = np.arange(10.0) * u.dimensionless_unscaled + check = s.value.copy() + s_multiply_reduce = np.multiply.reduce(s) + check_multiply_reduce = np.multiply.reduce(check) + assert s_multiply_reduce.value == check_multiply_reduce + assert s_multiply_reduce.unit is u.dimensionless_unscaled + s_multiply_accumulate = np.multiply.accumulate(s) + check_multiply_accumulate = np.multiply.accumulate(check) + assert np.all(s_multiply_accumulate.value == check_multiply_accumulate) + assert s_multiply_accumulate.unit is u.dimensionless_unscaled + s_multiply_reduceat = np.multiply.reduceat(s, i) + check_multiply_reduceat = np.multiply.reduceat(check, i) + assert np.all(s_multiply_reduceat.value == check_multiply_reduceat) + assert s_multiply_reduceat.unit is u.dimensionless_unscaled + + +class TestUfuncOuter: + """Test 'outer' methods for ufuncs + + Just a few spot checks, since it uses the same code as the regular + ufunc call + """ + + def test_one_argument_ufunc_outer(self): + # one argument cannot be used + s = np.arange(10.0) * u.radian + with pytest.raises(ValueError): + np.sin.outer(s) + + def test_two_argument_ufunc_outer(self): + s1 = np.arange(10.0) * u.m + s2 = np.arange(2.0) * u.s + check1 = s1.value + check2 = s2.value + s12_multiply_outer = np.multiply.outer(s1, s2) + check12_multiply_outer = np.multiply.outer(check1, check2) + assert np.all(s12_multiply_outer.value == check12_multiply_outer) + assert s12_multiply_outer.unit == s1.unit * s2.unit + + # raise UnitsError if appropriate + with pytest.raises(u.UnitsError): + np.add.outer(s1, s2) + + # but be fine if it does not + s3 = np.arange(2.0) * s1.unit + check3 = s3.value + s13_add_outer = np.add.outer(s1, s3) + check13_add_outer = np.add.outer(check1, check3) + assert np.all(s13_add_outer.value == check13_add_outer) + assert s13_add_outer.unit is s1.unit + + s13_greater_outer = np.greater.outer(s1, s3) + check13_greater_outer = np.greater.outer(check1, check3) + assert type(s13_greater_outer) is np.ndarray + assert np.all(s13_greater_outer == check13_greater_outer) + + +@dataclasses.dataclass +class DuckQuantity1: + data: u.Quantity + + +@dataclasses.dataclass +class DuckQuantity2(DuckQuantity1): + @property + def unit(self) -> u.UnitBase: + return self.data.unit + + +@dataclasses.dataclass(eq=False) +class DuckQuantity3(DuckQuantity2): + def __array_ufunc__(self, function, method, *inputs, **kwargs): + inputs = [inp.data if isinstance(inp, type(self)) else inp for inp in inputs] + + out = kwargs.get("out") + + kwargs_copy = {} + for k, kwarg in kwargs.items(): + if isinstance(kwarg, type(self)): + kwargs_copy[k] = kwarg.data + elif isinstance(kwarg, (list, tuple)): + kwargs_copy[k] = type(kwarg)( + item.data if isinstance(item, type(self)) else item + for item in kwarg + ) + else: + kwargs_copy[k] = kwarg + kwargs = kwargs_copy + + for inp in inputs: + if isinstance(inp, np.ndarray): + result = inp.__array_ufunc__(function, method, *inputs, **kwargs) + if result is not NotImplemented: + if out is None: + return type(self)(result) + else: + if function.nout == 1: + return out[0] + else: + return out + + return NotImplemented + + +class DuckQuantity4(DuckQuantity3): + @property + def unit(self): + return DuckQuantity1(1 * self.data.unit) + + +class TestUfuncReturnsNotImplemented: + @pytest.mark.parametrize("ufunc", (np.negative, np.abs)) + class TestUnaryUfuncs: + @pytest.mark.parametrize( + "duck_quantity", + [DuckQuantity1(1 * u.mm), DuckQuantity2(1 * u.mm)], + ) + def test_basic(self, ufunc, duck_quantity): + with pytest.raises(TypeError, match="bad operand type for .*"): + ufunc(duck_quantity) + + @pytest.mark.parametrize( + "duck_quantity", + [ + DuckQuantity3(1 * u.mm), + DuckQuantity3([1, 2] * u.mm), + DuckQuantity4(1 * u.mm), + ], + ) + @pytest.mark.parametrize("out", [None, "empty"]) + def test_full(self, ufunc, duck_quantity, out): + out_expected = out + if out == "empty": + out = type(duck_quantity)(np.empty_like(ufunc(duck_quantity.data))) + out_expected = np.empty_like(ufunc(duck_quantity.data)) + + result = ufunc(duck_quantity, out=out) + if out is not None: + assert result is out + + result_expected = ufunc(duck_quantity.data, out=out_expected) + assert np.all(result.data == result_expected) + + @pytest.mark.parametrize("ufunc", (np.add, np.multiply, np.less)) + @pytest.mark.parametrize("quantity", (1 * u.m, [1, 2] * u.m)) + class TestBinaryUfuncs: + @pytest.mark.parametrize( + "duck_quantity", + [DuckQuantity1(1 * u.mm), DuckQuantity2(1 * u.mm)], + ) + def test_basic(self, ufunc, quantity, duck_quantity): + with pytest.raises( + (TypeError, ValueError), + match=( + r"(Unsupported operand type\(s\) for ufunc .*)|" + r"(unsupported operand type\(s\) for .*)|" + r"(Value not scalar compatible or convertible to an int, float, or complex array)" + ), + ): + ufunc(quantity, duck_quantity) + + @pytest.mark.parametrize( + "duck_quantity", + [ + DuckQuantity3(1 * u.mm), + DuckQuantity3([1, 2] * u.mm), + DuckQuantity4(1 * u.mm), + ], + ) + @pytest.mark.parametrize("out", [None, "empty"]) + def test_full(self, ufunc, quantity, duck_quantity, out): + out_expected = out + if out == "empty": + out = type(duck_quantity)( + np.empty_like(ufunc(quantity, duck_quantity.data)) + ) + out_expected = np.empty_like(ufunc(quantity, duck_quantity.data)) + + result = ufunc(quantity, duck_quantity, out=out) + if out is not None: + assert result is out + + result_expected = ufunc(quantity, duck_quantity.data, out=out_expected) + assert np.all(result.data == result_expected) + + +if HAS_SCIPY: + from scipy import special as sps + + erf_like_ufuncs = ( + sps.erf, sps.erfc, sps.erfcx, sps.erfi, + sps.gamma, sps.gammaln, sps.loggamma, sps.gammasgn, sps.psi, + sps.rgamma, sps.digamma, sps.wofz, sps.dawsn, + sps.entr, sps.exprel, sps.expm1, sps.log1p, sps.exp2, sps.exp10, + ) # fmt: skip + + if isinstance(sps.erfinv, np.ufunc): + erf_like_ufuncs += (sps.erfinv, sps.erfcinv) + + def test_scipy_registration(): + """Check that scipy gets loaded upon first use.""" + if sps.erf in qh.UFUNC_HELPERS: + # Generally, scipy will not be loaded here, but in a double run it might. + pytest.skip() + sps.erf(1.0 * u.percent) + assert sps.erf in qh.UFUNC_HELPERS + + class TestScipySpecialUfuncs: + @pytest.mark.parametrize("function", erf_like_ufuncs) + def test_erf_scalar(self, function): + TestQuantityMathFuncs.test_exp_scalar(None, function) + + @pytest.mark.parametrize("function", erf_like_ufuncs) + def test_erf_array(self, function): + TestQuantityMathFuncs.test_exp_array(None, function) + + @pytest.mark.parametrize("function", erf_like_ufuncs) + def test_erf_invalid_units(self, function): + TestQuantityMathFuncs.test_exp_invalid_units(None, function) + + @pytest.mark.parametrize("function", (sps.cbrt,)) + def test_cbrt_scalar(self, function): + TestQuantityMathFuncs.test_cbrt_scalar(None, function) + + @pytest.mark.parametrize("function", (sps.cbrt,)) + def test_cbrt_array(self, function): + TestQuantityMathFuncs.test_cbrt_array(None, function) + + @pytest.mark.parametrize("function", (sps.radian,)) + def test_radian(self, function): + q1 = function(180.0 * u.degree, 0.0 * u.arcmin, 0.0 * u.arcsec) + assert_allclose(q1.value, np.pi) + assert q1.unit == u.radian + + q2 = function(0.0 * u.degree, 30.0 * u.arcmin, 0.0 * u.arcsec) + assert_allclose(q2.value, (30.0 * u.arcmin).to(u.radian).value) + assert q2.unit == u.radian + + q3 = function(0.0 * u.degree, 0.0 * u.arcmin, 30.0 * u.arcsec) + assert_allclose(q3.value, (30.0 * u.arcsec).to(u.radian).value) + + # the following doesn't make much sense in terms of the name of the + # routine, but we check it gives the correct result. + q4 = function(3.0 * u.radian, 0.0 * u.arcmin, 0.0 * u.arcsec) + assert_allclose(q4.value, 3.0) + assert q4.unit == u.radian + + with pytest.raises(TypeError): + function(3.0 * u.m, 2.0 * u.s, 1.0 * u.kg) + + jv_like_ufuncs = ( + sps.jv, sps.jn, sps.jve, sps.yn, sps.yv, sps.yve, sps.kn, sps.kv, + sps.kve, sps.iv, sps.ive, sps.hankel1, sps.hankel1e, sps.hankel2, + sps.hankel2e, + ) # fmt: skip + + @pytest.mark.parametrize("function", jv_like_ufuncs) + def test_jv_scalar(self, function): + q = function(2.0 * u.m / (2.0 * u.m), 3.0 * u.m / (6.0 * u.m)) + assert q.unit == u.dimensionless_unscaled + assert q.value == function(1.0, 0.5) + + @pytest.mark.parametrize("function", jv_like_ufuncs) + def test_jv_array(self, function): + q = function( + np.ones(3) * u.m / (1.0 * u.m), + np.array([2.0, 3.0, 6.0]) * u.m / (6.0 * u.m), + ) + assert q.unit == u.dimensionless_unscaled + assert np.all( + q.value == function(np.ones(3), np.array([1.0 / 3.0, 1.0 / 2.0, 1.0])) + ) + # should also work on quantities that can be made dimensionless + q2 = function( + np.ones(3) * u.m / (1.0 * u.m), + np.array([2.0, 3.0, 6.0]) * u.m / (6.0 * u.cm), + ) + assert q2.unit == u.dimensionless_unscaled + assert_allclose( + q2.value, + function(np.ones(3), np.array([100.0 / 3.0, 100.0 / 2.0, 100.0])), + ) + + @pytest.mark.parametrize("function", jv_like_ufuncs) + def test_jv_invalid_units(self, function): + # Can't use jv() with non-dimensionless quantities + with pytest.raises( + TypeError, + match=( + f"Can only apply '{function.__name__}' function to dimensionless" + " quantities" + ), + ): + function(1.0 * u.kg, 3.0 * u.m / u.s) + + +class TestLinalg: + def test_matrix_rank(self): + q = np.eye(3) * u.m + rank = np.linalg.matrix_rank(q) + assert rank == 3 + # Rank is an integer, typically np.int64, + # but size might vary across architectures + assert isinstance(rank, np.integer) + q2 = np.ones((3, 3)) * u.s + rank2 = np.linalg.matrix_rank(q2) + assert rank2 == 1 + + @pytest.mark.parametrize( + "function, expected_unit", [(np.linalg.det, u.m**2), (np.linalg.norm, u.m)] + ) + def test_other_linalg(self, function, expected_unit): + # Det and Norm are often used with Quantities + q = np.eye(2) * u.m + result = function(q) + assert result.unit == expected_unit diff --git a/astropy/units/tests/test_structured.py b/astropy/units/tests/test_structured.py new file mode 100644 index 000000000000..3967e39cd796 --- /dev/null +++ b/astropy/units/tests/test_structured.py @@ -0,0 +1,768 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +""" +Test Structured units and quantities. +""" + +import copy + +import numpy as np +import numpy.lib.recfunctions as rfn +import pytest +from numpy.testing import assert_array_equal + +from astropy import units as u +from astropy.tests.helper import check_pickling_recovery, pickle_protocol # noqa: F401 +from astropy.units import Quantity, StructuredUnit, Unit, UnitBase +from astropy.units.quantity import _structured_unit_like_dtype +from astropy.utils.masked import Masked + + +class StructuredTestBase: + @classmethod + def setup_class(cls): + cls.pv_dtype = np.dtype([("p", "f8"), ("v", "f8")]) + cls.pv_t_dtype = np.dtype([("pv", cls.pv_dtype), ("t", "f8")]) + cls.p_unit = u.km + cls.v_unit = u.km / u.s + cls.t_unit = u.s + cls.pv_dtype = np.dtype([("p", "f8"), ("v", "f8")]) + cls.pv_t_dtype = np.dtype([("pv", cls.pv_dtype), ("t", "f8")]) + cls.pv = np.array([(1.0, 0.25), (2.0, 0.5), (3.0, 0.75)], cls.pv_dtype) + cls.pv_t = np.array( + [ + ((4.0, 2.5), 0.0), + ((5.0, 5.0), 1.0), + ((6.0, 7.5), 2.0), + ], + cls.pv_t_dtype, + ) + + +class StructuredTestBaseWithUnits(StructuredTestBase): + @classmethod + def setup_class(cls): + super().setup_class() + cls.pv_unit = StructuredUnit((cls.p_unit, cls.v_unit), ("p", "v")) + cls.pv_t_unit = StructuredUnit((cls.pv_unit, cls.t_unit), ("pv", "t")) + + +class TestStructuredUnitBasics(StructuredTestBase): + def test_initialization_and_keying(self): + su = StructuredUnit((self.p_unit, self.v_unit), ("p", "v")) + assert su["p"] is self.p_unit + assert su["v"] is self.v_unit + su2 = StructuredUnit((su, self.t_unit), ("pv", "t")) + assert isinstance(su2["pv"], StructuredUnit) + assert su2["pv"]["p"] is self.p_unit + assert su2["pv"]["v"] is self.v_unit + assert su2["t"] is self.t_unit + assert su2["pv"] == su + su3 = StructuredUnit(("AU", "AU/day"), ("p", "v")) + assert isinstance(su3["p"], UnitBase) + assert isinstance(su3["v"], UnitBase) + su4 = StructuredUnit("AU, AU/day", ("p", "v")) + assert su4["p"] == u.AU + assert su4["v"] == u.AU / u.day + su5 = StructuredUnit(("AU", "AU/day")) + assert su5.field_names == ("f0", "f1") + assert su5["f0"] == u.AU + assert su5["f1"] == u.AU / u.day + + def test_recursive_initialization(self): + su = StructuredUnit( + ((self.p_unit, self.v_unit), self.t_unit), (("p", "v"), "t") + ) + assert isinstance(su["pv"], StructuredUnit) + assert su["pv"]["p"] is self.p_unit + assert su["pv"]["v"] is self.v_unit + assert su["t"] is self.t_unit + su2 = StructuredUnit( + ((self.p_unit, self.v_unit), self.t_unit), (["p_v", ("p", "v")], "t") + ) + assert isinstance(su2["p_v"], StructuredUnit) + assert su2["p_v"]["p"] is self.p_unit + assert su2["p_v"]["v"] is self.v_unit + assert su2["t"] is self.t_unit + su3 = StructuredUnit((("AU", "AU/day"), "yr"), (["p_v", ("p", "v")], "t")) + assert isinstance(su3["p_v"], StructuredUnit) + assert su3["p_v"]["p"] == u.AU + assert su3["p_v"]["v"] == u.AU / u.day + assert su3["t"] == u.yr + su4 = StructuredUnit("(AU, AU/day), yr", (("p", "v"), "t")) + assert isinstance(su4["pv"], StructuredUnit) + assert su4["pv"]["p"] == u.AU + assert su4["pv"]["v"] == u.AU / u.day + assert su4["t"] == u.yr + + def test_extreme_recursive_initialization(self): + su = StructuredUnit( + "(yr,(AU,AU/day,(km,(day,day))),m)", + ("t", ("p", "v", ("h", ("d1", "d2"))), "l"), + ) + assert su.field_names == ( + 't', ['pvhd1d2', + ('p', 'v', + ['hd1d2', + ('h', + ['d1d2', + ('d1', 'd2')])])], + 'l', + ) # fmt: skip + + dt = np.dtype( + [("t", "f8"), + ("pvhd1d2", + ([("p", "f8"), ("v", "f8"), ("hd1d2", + [("h", "f8"), ("d1d2", + [("d1", "f8"), ("d2", "f8")]), + ]), + ], (5, 5))), # Note: structured subarray to improve test! + ("l", "f8") + ]) # fmt: skip + + su2 = StructuredUnit("(yr,(AU,AU/day,(km,(day,day))),m)", dt) + assert su2.field_names == su.field_names + assert su2 == su + + @pytest.mark.parametrize( + "names, invalid", + [ + [("t", ["p", "v"]), "['p', 'v']"], + [("t", ["pv", "p", "v"]), "['pv', 'p', 'v']"], + [("t", ["pv", ["p", "v"]]), "['pv', ['p', 'v']"], + [("t", ()), "()"], + [("t", ("p", None)), "None"], + [("t", ["pv", ("p", "")]), "''"], + ], + ) + def test_initialization_names_invalid_list_errors(self, names, invalid): + with pytest.raises(ValueError) as exc: + StructuredUnit("yr,(AU,AU/day)", names) + assert f"invalid entry {invalid}" in str(exc) + + def test_looks_like_unit(self): + su = StructuredUnit((self.p_unit, self.v_unit), ("p", "v")) + assert Unit(su) is su + + def test_initialize_with_float_dtype(self): + su = StructuredUnit(("AU", "AU/d"), self.pv_dtype) + assert isinstance(su["p"], UnitBase) + assert isinstance(su["v"], UnitBase) + assert su["p"] == u.AU + assert su["v"] == u.AU / u.day + su = StructuredUnit((("km", "km/s"), "yr"), self.pv_t_dtype) + assert isinstance(su["pv"], StructuredUnit) + assert isinstance(su["pv"]["p"], UnitBase) + assert isinstance(su["t"], UnitBase) + assert su["pv"]["v"] == u.km / u.s + su = StructuredUnit("(km, km/s), yr", self.pv_t_dtype) + assert isinstance(su["pv"], StructuredUnit) + assert isinstance(su["pv"]["p"], UnitBase) + assert isinstance(su["t"], UnitBase) + assert su["pv"]["v"] == u.km / u.s + + def test_initialize_with_structured_unit_for_names(self): + su = StructuredUnit(("AU", "AU/d"), names=("p", "v")) + su2 = StructuredUnit(("km", "km/s"), names=su) + assert su2.field_names == ("p", "v") + assert su2["p"] == u.km + assert su2["v"] == u.km / u.s + + def test_initialize_single_field(self): + su = StructuredUnit("AU", "p") + assert isinstance(su, StructuredUnit) + assert isinstance(su["p"], UnitBase) + assert su["p"] == u.AU + su = StructuredUnit("AU") + assert isinstance(su, StructuredUnit) + assert isinstance(su["f0"], UnitBase) + assert su["f0"] == u.AU + + def test_equality(self): + su = StructuredUnit(("AU", "AU/d"), self.pv_dtype) + assert su == StructuredUnit(("AU", "AU/d"), self.pv_dtype) + assert su != StructuredUnit(("m", "AU/d"), self.pv_dtype) + # Names should be ignored. + assert su == StructuredUnit(("AU", "AU/d")) + assert su == StructuredUnit(("AU", "AU/d"), names=("q", "w")) + assert su != StructuredUnit(("m", "m/s")) + + def test_parsing(self): + su = Unit("AU, AU/d") + assert isinstance(su, StructuredUnit) + assert isinstance(su["f0"], UnitBase) + assert isinstance(su["f1"], UnitBase) + assert su["f0"] == u.AU + assert su["f1"] == u.AU / u.day + su2 = Unit("AU, AU/d, yr") + assert isinstance(su2, StructuredUnit) + assert su2 == StructuredUnit(("AU", "AU/d", "yr")) + su2a = Unit("(AU, AU/d, yr)") + assert isinstance(su2a, StructuredUnit) + assert su2a == su2 + su3 = Unit("(km, km/s), yr") + assert isinstance(su3, StructuredUnit) + assert su3 == StructuredUnit((("km", "km/s"), "yr")) + su4 = Unit("km,") + assert isinstance(su4, StructuredUnit) + assert su4 == StructuredUnit((u.km,)) + su5 = Unit("(m,s),") + assert isinstance(su5, StructuredUnit) + assert su5 == StructuredUnit(((u.m, u.s),)) + ldbody_unit = Unit("Msun, 0.5rad^2, (au, au/day)") + assert ldbody_unit == StructuredUnit( + (u.Msun, Unit(u.rad**2 / 2), (u.AU, u.AU / u.day)) + ) + + def test_to_string(self): + su = StructuredUnit((u.km, u.km / u.s)) + latex_str = r"$(\mathrm{km}, \mathrm{\frac{km}{s}})$" + assert su.to_string(format="latex") == latex_str + latex_str = r"$(\mathrm{km}, \mathrm{km\,s^{-1}})$" + assert su.to_string(format="latex_inline") == latex_str + + def test_str(self): + su = StructuredUnit(((u.km, u.km / u.s), u.yr)) + assert str(su) == "((km, km / s), yr)" + assert Unit(str(su)) == su + + def test_repr(self): + su = StructuredUnit(((u.km, u.km / u.s), u.yr)) + assert repr(su) == 'Unit("((km, km / s), yr)")' + assert eval(repr(su)) == su + + +class TestStructuredUnitsCopyPickle(StructuredTestBaseWithUnits): + def test_copy(self): + su_copy = copy.copy(self.pv_t_unit) + assert su_copy is not self.pv_t_unit + assert su_copy == self.pv_t_unit + assert su_copy._units is self.pv_t_unit._units + + def test_deepcopy(self): + su_copy = copy.deepcopy(self.pv_t_unit) + assert su_copy is not self.pv_t_unit + assert su_copy == self.pv_t_unit + assert su_copy._units is not self.pv_t_unit._units + + def test_pickle(self, pickle_protocol): # noqa: F811 + check_pickling_recovery(self.pv_t_unit, pickle_protocol) + + +class TestStructuredUnitAsMapping(StructuredTestBaseWithUnits): + def test_len(self): + assert len(self.pv_unit) == 2 + assert len(self.pv_t_unit) == 2 + + def test_keys(self): + slv = list(self.pv_t_unit.keys()) + assert slv == ["pv", "t"] + + def test_values(self): + values = self.pv_t_unit.values() + assert values == (self.pv_unit, self.t_unit) + + def test_field_names(self): + field_names = self.pv_t_unit.field_names + assert isinstance(field_names, tuple) + assert field_names == (["pv", ("p", "v")], "t") + + @pytest.mark.parametrize("iterable", [list, set]) + def test_as_iterable(self, iterable): + sl = iterable(self.pv_unit) + assert isinstance(sl, iterable) + assert sl == iterable(["p", "v"]) + + def test_as_dict(self): + sd = dict(self.pv_t_unit) + assert sd == {"pv": self.pv_unit, "t": self.t_unit} + + def test_contains(self): + assert "p" in self.pv_unit + assert "v" in self.pv_unit + assert "t" not in self.pv_unit + + def test_setitem_fails(self): + with pytest.raises(TypeError, match="item assignment"): + self.pv_t_unit["t"] = u.Gyr + + def test_hashing(self): + assert hash(self.pv_unit) != hash(self.pv_t_unit) + + +class TestStructuredUnitMethods(StructuredTestBaseWithUnits): + def test_physical_type_id(self): + pv_ptid = self.pv_unit._physical_type_id + assert len(pv_ptid) == 2 + assert pv_ptid.dtype.names == ("p", "v") + p_ptid = self.pv_unit["p"]._physical_type_id + v_ptid = self.pv_unit["v"]._physical_type_id + # Expected should be (subclass of) void, with structured object dtype. + expected = np.array((p_ptid, v_ptid), [("p", "O"), ("v", "O")])[()] + assert pv_ptid == expected + # Names should be ignored in comparison. + assert pv_ptid == np.array((p_ptid, v_ptid), "O,O")[()] + # Should be possible to address by field and by number. + assert pv_ptid["p"] == p_ptid + assert pv_ptid["v"] == v_ptid + assert pv_ptid[0] == p_ptid + assert pv_ptid[1] == v_ptid + # More complicated version. + pv_t_ptid = self.pv_t_unit._physical_type_id + t_ptid = self.t_unit._physical_type_id + assert pv_t_ptid == np.array((pv_ptid, t_ptid), "O,O")[()] + assert pv_t_ptid["pv"] == pv_ptid + assert pv_t_ptid["t"] == t_ptid + assert pv_t_ptid["pv"][1] == v_ptid + + def test_physical_type(self): + pv_pt = self.pv_unit.physical_type + assert pv_pt == np.array(("length", "speed"), "O,O")[()] + + pv_t_pt = self.pv_t_unit.physical_type + assert pv_t_pt == np.array((pv_pt, "time"), "O,O")[()] + + def test_si(self): + pv_t_si = self.pv_t_unit.si + assert pv_t_si == self.pv_t_unit + assert pv_t_si["pv"]["v"].scale == 1000 + + def test_cgs(self): + pv_t_cgs = self.pv_t_unit.cgs + assert pv_t_cgs == self.pv_t_unit + assert pv_t_cgs["pv"]["v"].scale == 100000 + + def test_decompose(self): + pv_t_decompose = self.pv_t_unit.decompose() + assert pv_t_decompose["pv"]["v"].scale == 1000 + + def test_is_equivalent(self): + assert self.pv_unit.is_equivalent(("AU", "AU/day")) + assert not self.pv_unit.is_equivalent("m") + assert not self.pv_unit.is_equivalent(("AU", "AU")) + # Names should be ignored. + pv_alt = StructuredUnit("m,m/s", names=("q", "w")) + assert pv_alt.field_names != self.pv_unit.field_names + assert self.pv_unit.is_equivalent(pv_alt) + # Regular units should work too. + assert not u.m.is_equivalent(self.pv_unit) + + def test_conversion(self): + pv1 = self.pv_unit.to(("AU", "AU/day"), self.pv) + assert isinstance(pv1, np.ndarray) + assert pv1.dtype == self.pv.dtype + assert np.all(pv1["p"] * u.AU == self.pv["p"] * self.p_unit) + assert np.all(pv1["v"] * u.AU / u.day == self.pv["v"] * self.v_unit) + # Names should be from value. + su2 = StructuredUnit((self.p_unit, self.v_unit), ("position", "velocity")) + pv2 = su2.to(("Mm", "mm/s"), self.pv) + assert pv2.dtype.names == ("p", "v") + assert pv2.dtype == self.pv.dtype + # Check recursion. + pv_t1 = self.pv_t_unit.to((("AU", "AU/day"), "Myr"), self.pv_t) + assert isinstance(pv_t1, np.ndarray) + assert pv_t1.dtype == self.pv_t.dtype + assert np.all(pv_t1["pv"]["p"] * u.AU == self.pv_t["pv"]["p"] * self.p_unit) + assert np.all( + pv_t1["pv"]["v"] * u.AU / u.day == self.pv_t["pv"]["v"] * self.v_unit + ) + assert np.all(pv_t1["t"] * u.Myr == self.pv_t["t"] * self.t_unit) + # Passing in tuples should work. + pv_t2 = self.pv_t_unit.to((("AU", "AU/day"), "Myr"), ((1.0, 0.1), 10.0)) + assert pv_t2["pv"]["p"] == self.p_unit.to("AU", 1.0) + assert pv_t2["pv"]["v"] == self.v_unit.to("AU/day", 0.1) + assert pv_t2["t"] == self.t_unit.to("Myr", 10.0) + pv_t3 = self.pv_t_unit.to( + (("AU", "AU/day"), "Myr"), [((1.0, 0.1), 10.0), ((2.0, 0.2), 20.0)] + ) + assert np.all(pv_t3["pv"]["p"] == self.p_unit.to("AU", [1.0, 2.0])) + assert np.all(pv_t3["pv"]["v"] == self.v_unit.to("AU/day", [0.1, 0.2])) + assert np.all(pv_t3["t"] == self.t_unit.to("Myr", [10.0, 20.0])) + + +class TestStructuredUnitArithmatic(StructuredTestBaseWithUnits): + def test_multiplication(self): + pv_times_au = self.pv_unit * u.au + assert isinstance(pv_times_au, StructuredUnit) + assert pv_times_au.field_names == ("p", "v") + assert pv_times_au["p"] == self.p_unit * u.AU + assert pv_times_au["v"] == self.v_unit * u.AU + au_times_pv = u.au * self.pv_unit + assert au_times_pv == pv_times_au + pv_times_au2 = self.pv_unit * "au" + assert pv_times_au2 == pv_times_au + au_times_pv2 = "AU" * self.pv_unit + assert au_times_pv2 == pv_times_au + with pytest.raises(TypeError): + self.pv_unit * self.pv_unit + with pytest.raises(TypeError): + "s,s" * self.pv_unit + + def test_division(self): + pv_by_s = self.pv_unit / u.s + assert isinstance(pv_by_s, StructuredUnit) + assert pv_by_s.field_names == ("p", "v") + assert pv_by_s["p"] == self.p_unit / u.s + assert pv_by_s["v"] == self.v_unit / u.s + pv_by_s2 = self.pv_unit / "s" + assert pv_by_s2 == pv_by_s + with pytest.raises(TypeError): + 1.0 / self.pv_unit + with pytest.raises(TypeError): + u.s / self.pv_unit + + +class TestStructuredQuantity(StructuredTestBaseWithUnits): + def test_initialization_and_keying(self): + q_pv = Quantity(self.pv, self.pv_unit) + q_p = q_pv["p"] + assert isinstance(q_p, Quantity) + assert isinstance(q_p.unit, UnitBase) + assert np.all(q_p == self.pv["p"] * self.pv_unit["p"]) + q_v = q_pv["v"] + assert isinstance(q_v, Quantity) + assert isinstance(q_v.unit, UnitBase) + assert np.all(q_v == self.pv["v"] * self.pv_unit["v"]) + q_pv_t = Quantity(self.pv_t, self.pv_t_unit) + q_t = q_pv_t["t"] + assert np.all(q_t == self.pv_t["t"] * self.pv_t_unit["t"]) + q_pv2 = q_pv_t["pv"] + assert isinstance(q_pv2, Quantity) + assert q_pv2.unit == self.pv_unit + with pytest.raises(ValueError): + Quantity(self.pv, self.pv_t_unit) + with pytest.raises(ValueError): + Quantity(self.pv_t, self.pv_unit) + + def test_initialization_with_unit_tuples(self): + q_pv_t = Quantity(self.pv_t, (("km", "km/s"), "s")) + assert isinstance(q_pv_t.unit, StructuredUnit) + assert q_pv_t.unit == self.pv_t_unit + + def test_initialization_with_string(self): + q_pv_t = Quantity(self.pv_t, "(km, km/s), s") + assert isinstance(q_pv_t.unit, StructuredUnit) + assert q_pv_t.unit == self.pv_t_unit + + def test_initialization_by_multiplication_with_unit(self): + q_pv_t = self.pv_t * self.pv_t_unit + assert q_pv_t.unit is self.pv_t_unit + assert np.all(q_pv_t.value == self.pv_t) + assert not np.may_share_memory(q_pv_t, self.pv_t) + q_pv_t2 = self.pv_t_unit * self.pv_t + assert q_pv_t.unit is self.pv_t_unit + # Not testing equality of structured Quantity here. + assert np.all(q_pv_t2.value == q_pv_t.value) + + def test_initialization_by_shifting_to_unit(self): + q_pv_t = self.pv_t << self.pv_t_unit + assert q_pv_t.unit is self.pv_t_unit + assert np.all(q_pv_t.value == self.pv_t) + assert np.may_share_memory(q_pv_t, self.pv_t) + + def test_initialization_without_unit(self): + q_pv_t = u.Quantity(self.pv_t, unit=None) + + assert np.all(q_pv_t.value == self.pv_t) + + # Test that unit is a structured unit like the dtype + expected_unit = _structured_unit_like_dtype( + u.Quantity._default_unit, self.pv_t.dtype + ) + assert q_pv_t.unit == expected_unit + # A more explicit test + assert q_pv_t.unit == u.StructuredUnit(((u.one, u.one), u.one)) + + def test_getitem(self): + q_pv_t = Quantity(self.pv_t, self.pv_t_unit) + q_pv_t01 = q_pv_t[:2] + assert isinstance(q_pv_t01, Quantity) + assert q_pv_t01.unit == q_pv_t.unit + assert np.all(q_pv_t01["t"] == q_pv_t["t"][:2]) + q_pv_t1 = q_pv_t[1] + assert isinstance(q_pv_t1, Quantity) + assert q_pv_t1.unit == q_pv_t.unit + assert q_pv_t1.shape == () + assert q_pv_t1["t"] == q_pv_t["t"][1] + + def test_value(self): + q_pv_t = Quantity(self.pv_t, self.pv_t_unit) + value = q_pv_t.value + assert type(value) is np.ndarray + assert np.all(value == self.pv_t) + value1 = q_pv_t[1].value + assert type(value1) is np.void + assert np.all(value1 == self.pv_t[1]) + + def test_conversion(self): + q_pv = Quantity(self.pv, self.pv_unit) + q1 = q_pv.to(("AU", "AU/day")) + assert isinstance(q1, Quantity) + assert q1["p"].unit == u.AU + assert q1["v"].unit == u.AU / u.day + assert np.all(q1["p"] == q_pv["p"].to(u.AU)) + assert np.all(q1["v"] == q_pv["v"].to(u.AU / u.day)) + q2 = q_pv.to(self.pv_unit) + assert q2["p"].unit == self.p_unit + assert q2["v"].unit == self.v_unit + assert np.all(q2["p"].value == self.pv["p"]) + assert np.all(q2["v"].value == self.pv["v"]) + assert not np.may_share_memory(q2, q_pv) + pv1 = q_pv.to_value(("AU", "AU/day")) + assert type(pv1) is np.ndarray + assert np.all(pv1["p"] == q_pv["p"].to_value(u.AU)) + assert np.all(pv1["v"] == q_pv["v"].to_value(u.AU / u.day)) + pv11 = q_pv[1].to_value(("AU", "AU/day")) + assert type(pv11) is np.void + assert pv11 == pv1[1] + q_pv_t = Quantity(self.pv_t, self.pv_t_unit) + q2 = q_pv_t.to((("kpc", "kpc/Myr"), "Myr")) + assert q2["pv"]["p"].unit == u.kpc + assert q2["pv"]["v"].unit == u.kpc / u.Myr + assert q2["t"].unit == u.Myr + assert np.all(q2["pv"]["p"] == q_pv_t["pv"]["p"].to(u.kpc)) + assert np.all(q2["pv"]["v"] == q_pv_t["pv"]["v"].to(u.kpc / u.Myr)) + assert np.all(q2["t"] == q_pv_t["t"].to(u.Myr)) + + def test_conversion_via_lshift(self): + q_pv = Quantity(self.pv, self.pv_unit) + q1 = q_pv << StructuredUnit(("AU", "AU/day")) + assert isinstance(q1, Quantity) + assert q1["p"].unit == u.AU + assert q1["v"].unit == u.AU / u.day + assert np.all(q1["p"] == q_pv["p"].to(u.AU)) + assert np.all(q1["v"] == q_pv["v"].to(u.AU / u.day)) + q2 = q_pv << self.pv_unit + assert q2["p"].unit == self.p_unit + assert q2["v"].unit == self.v_unit + assert np.all(q2["p"].value == self.pv["p"]) + assert np.all(q2["v"].value == self.pv["v"]) + assert np.may_share_memory(q2, q_pv) + q_pv_t = Quantity(self.pv_t, self.pv_t_unit) + q2 = q_pv_t << "(kpc,kpc/Myr),Myr" + assert q2["pv"]["p"].unit == u.kpc + assert q2["pv"]["v"].unit == u.kpc / u.Myr + assert q2["t"].unit == u.Myr + assert np.all(q2["pv"]["p"] == q_pv_t["pv"]["p"].to(u.kpc)) + assert np.all(q2["pv"]["v"] == q_pv_t["pv"]["v"].to(u.kpc / u.Myr)) + assert np.all(q2["t"] == q_pv_t["t"].to(u.Myr)) + + def test_inplace_conversion(self): + # In principle, in-place might be possible, in which case this should be + # changed -- ie ``q1 is q_link``. + q_pv = Quantity(self.pv, self.pv_unit) + q1 = q_pv.copy() + q_link = q1 + q1 <<= StructuredUnit(("AU", "AU/day")) + assert q1 is not q_link + assert q1["p"].unit == u.AU + assert q1["v"].unit == u.AU / u.day + assert np.all(q1["p"] == q_pv["p"].to(u.AU)) + assert np.all(q1["v"] == q_pv["v"].to(u.AU / u.day)) + q_pv_t = Quantity(self.pv_t, self.pv_t_unit) + q2 = q_pv_t.copy() + q_link = q2 + q2 <<= "(kpc,kpc/Myr),Myr" + assert q2 is not q_link + assert q2["pv"]["p"].unit == u.kpc + assert q2["pv"]["v"].unit == u.kpc / u.Myr + assert q2["t"].unit == u.Myr + assert np.all(q2["pv"]["p"] == q_pv_t["pv"]["p"].to(u.kpc)) + assert np.all(q2["pv"]["v"] == q_pv_t["pv"]["v"].to(u.kpc / u.Myr)) + assert np.all(q2["t"] == q_pv_t["t"].to(u.Myr)) + + def test_si(self): + q_pv_t = Quantity(self.pv_t, self.pv_t_unit) + q_pv_t_si = q_pv_t.si + assert_array_equal(q_pv_t_si, q_pv_t.to("(m,m/s),s")) + + def test_cgs(self): + q_pv_t = Quantity(self.pv_t, self.pv_t_unit) + q_pv_t_cgs = q_pv_t.cgs + assert_array_equal(q_pv_t_cgs, q_pv_t.to("(cm,cm/s),s")) + + def test_equality(self): + q_pv = Quantity(self.pv, self.pv_unit) + equal = q_pv == q_pv + not_equal = q_pv != q_pv + assert np.all(equal) + assert not np.any(not_equal) + equal2 = q_pv == q_pv[1] + not_equal2 = q_pv != q_pv[1] + assert np.all(equal2 == [False, True, False]) + assert np.all(not_equal2 != equal2) + q1 = q_pv.to(("AU", "AU/day")) + # Ensure same conversion is done, by placing q1 first. + assert np.all(q1 == q_pv) + assert not np.any(q1 != q_pv) + # Check different names in dtype. + assert np.all(q1.value * u.Unit("AU, AU/day") == q_pv) + assert not np.any(q1.value * u.Unit("AU, AU/day") != q_pv) + assert (q_pv == "b") is False + assert ("b" != q_pv) is True + q_pv_t = Quantity(self.pv_t, self.pv_t_unit) + assert np.all((q_pv_t[2] == q_pv_t) == [False, False, True]) + assert np.all((q_pv_t[2] != q_pv_t) != [False, False, True]) + assert (q_pv == q_pv_t) is False + assert (q_pv_t != q_pv) is True + + def test_setitem(self): + q_pv = Quantity(self.pv, self.pv_unit) + q_pv[1] = (2.0, 2.0) * self.pv_unit + assert q_pv[1].value == np.array((2.0, 2.0), self.pv_dtype) + q_pv[1:2] = (1.0, 0.5) * u.Unit("AU, AU/day") + assert q_pv["p"][1] == 1.0 * u.AU + assert q_pv["v"][1] == 0.5 * u.AU / u.day + q_pv["v"] = 1.0 * u.km / u.s + assert np.all(q_pv["v"] == 1.0 * u.km / u.s) + with pytest.raises(u.UnitsError): + q_pv[1] = (1.0, 1.0) * u.Unit("AU, AU") + with pytest.raises(u.UnitsError): + q_pv["v"] = 1.0 * u.km + q_pv_t = Quantity(self.pv_t, self.pv_t_unit) + q_pv_t[1] = ((2.0, 2.0), 3.0) * self.pv_t_unit + assert q_pv_t[1].value == np.array(((2.0, 2.0), 3.0), self.pv_t_dtype) + q_pv_t[1:2] = ((1.0, 0.5), 5.0) * u.Unit("(AU, AU/day), yr") + assert q_pv_t["pv"][1] == (1.0, 0.5) * u.Unit("AU, AU/day") + assert q_pv_t["t"][1] == 5.0 * u.yr + q_pv_t["pv"] = (1.0, 0.5) * self.pv_unit + assert np.all(q_pv_t["pv"] == (1.0, 0.5) * self.pv_unit) + + +class TestStructuredQuantityFunctions(StructuredTestBaseWithUnits): + @classmethod + def setup_class(cls): + super().setup_class() + cls.q_pv = cls.pv << cls.pv_unit + cls.q_pv_t = cls.pv_t << cls.pv_t_unit + + def test_empty_like(self): + z = np.empty_like(self.q_pv) + assert z.dtype == self.pv_dtype + assert z.unit == self.pv_unit + assert z.shape == self.pv.shape + + @pytest.mark.parametrize("func", [np.zeros_like, np.ones_like]) + def test_zeros_ones_like(self, func): + z = func(self.q_pv) + assert z.dtype == self.pv_dtype + assert z.unit == self.pv_unit + assert z.shape == self.pv.shape + assert_array_equal(z, func(self.pv) << self.pv_unit) + + def test_structured_to_unstructured(self): + # can't unstructure something with incompatible units + with pytest.raises(u.UnitConversionError, match="'km / s'"): + rfn.structured_to_unstructured(self.q_pv) + + # For the other tests of ``structured_to_unstructured``, see + # ``test_quantity_non_ufuncs.TestRecFunctions.test_structured_to_unstructured`` + + def test_unstructured_to_structured(self): + # can't structure something that's already structured + dtype = np.dtype([("f1", float), ("f2", float)]) + with pytest.raises(ValueError, match="The length of the last dimension"): + rfn.unstructured_to_structured(self.q_pv, dtype=self.q_pv.dtype) + + # For the other tests of ``structured_to_unstructured``, see + # ``test_quantity_non_ufuncs.TestRecFunctions.test_unstructured_to_structured`` + + +class TestStructuredSpecificTypeQuantity(StructuredTestBaseWithUnits): + def setup_class(self): + super().setup_class() + + class PositionVelocity(u.SpecificTypeQuantity): + _equivalent_unit = self.pv_unit + + self.PositionVelocity = PositionVelocity + + def test_init(self): + pv = self.PositionVelocity(self.pv, self.pv_unit) + assert isinstance(pv, self.PositionVelocity) + assert type(pv["p"]) is u.Quantity + assert_array_equal(pv["p"], self.pv["p"] << self.pv_unit["p"]) + + pv2 = self.PositionVelocity(self.pv, "AU,AU/day") + assert_array_equal(pv2["p"], self.pv["p"] << u.AU) + + def test_error_on_non_equivalent_unit(self): + with pytest.raises(u.UnitsError): + self.PositionVelocity(self.pv, "AU") + with pytest.raises(u.UnitsError): + self.PositionVelocity(self.pv, "AU,yr") + + +class TestStructuredLogUnit: + def setup_class(self): + self.mag_time_dtype = np.dtype([("mag", "f8"), ("t", "f8")]) + self.mag_time = np.array([(20.0, 10.0), (25.0, 100.0)], self.mag_time_dtype) + + def test_unit_initialization(self): + mag_time_unit = StructuredUnit((u.STmag, u.s), self.mag_time_dtype) + assert mag_time_unit["mag"] == u.STmag + assert mag_time_unit["t"] == u.s + + mag_time_unit2 = u.Unit("mag(ST),s") + assert mag_time_unit2 == mag_time_unit + + def test_quantity_initialization(self): + su = u.Unit("mag(ST),s") + mag_time = self.mag_time << su + assert isinstance(mag_time["mag"], u.Magnitude) + assert isinstance(mag_time["t"], u.Quantity) + assert mag_time.unit == su + assert_array_equal(mag_time["mag"], self.mag_time["mag"] << u.STmag) + assert_array_equal(mag_time["t"], self.mag_time["t"] << u.s) + + def test_quantity_si(self): + mag_time = self.mag_time << u.Unit("mag(ST),yr") + mag_time_si = mag_time.si + assert_array_equal(mag_time_si["mag"], mag_time["mag"].si) + assert_array_equal(mag_time_si["t"], mag_time["t"].si) + + +class TestStructuredMaskedQuantity(StructuredTestBaseWithUnits): + """Somewhat minimal tests. Conversion is most stringent.""" + + def setup_class(self): + super().setup_class() + self.qpv = self.pv << self.pv_unit + self.pv_mask = np.array( + [ + (True, False), + (False, False), + (False, True), + ], + [("p", bool), ("v", bool)], + ) + self.mpv = Masked(self.qpv, mask=self.pv_mask) + + def test_init(self): + assert isinstance(self.mpv, Masked) + assert isinstance(self.mpv, Quantity) + assert_array_equal(self.mpv.unmasked, self.qpv) + assert_array_equal(self.mpv.mask, self.pv_mask) + + def test_slicing(self): + mp = self.mpv["p"] + assert isinstance(mp, Masked) + assert isinstance(mp, Quantity) + assert_array_equal(mp.unmasked, self.qpv["p"]) + assert_array_equal(mp.mask, self.pv_mask["p"]) + + def test_conversion(self): + mpv = self.mpv.to("AU,AU/day") + assert isinstance(mpv, Masked) + assert isinstance(mpv, Quantity) + assert_array_equal(mpv.unmasked, self.qpv.to("AU,AU/day")) + assert_array_equal(mpv.mask, self.pv_mask) + assert np.all(mpv == self.mpv) + + def test_si(self): + mpv = self.mpv.si + assert isinstance(mpv, Masked) + assert isinstance(mpv, Quantity) + assert_array_equal(mpv.unmasked, self.qpv.si) + assert_array_equal(mpv.mask, self.pv_mask) + assert np.all(mpv == self.mpv) diff --git a/astropy/units/tests/test_units.py b/astropy/units/tests/test_units.py new file mode 100644 index 000000000000..c5ec95e77c4e --- /dev/null +++ b/astropy/units/tests/test_units.py @@ -0,0 +1,1299 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +"""Regression tests for the units package.""" + +import itertools +import operator +import pickle +from contextlib import nullcontext +from fractions import Fraction + +import numpy as np +import pytest +from numpy.testing import assert_allclose + +from astropy import constants as c +from astropy import units as u +from astropy.units import cds, utils +from astropy.units.required_by_vounit import GsolLum, ksolMass, nsolRad +from astropy.utils.compat.optional_deps import HAS_ARRAY_API_STRICT, HAS_DASK +from astropy.utils.exceptions import AstropyDeprecationWarning + +FLOAT_EPS = np.finfo(float).eps + + +def test_initialisation(): + assert u.Unit(u.m) is u.m + + ten_meter = u.Unit(10.0 * u.m) + assert ten_meter == u.CompositeUnit(10.0, [u.m], [1]) + assert u.Unit(ten_meter) is ten_meter + + assert u.Unit(10.0 * ten_meter) == u.CompositeUnit(100.0, [u.m], [1]) + + foo = u.Unit("foo", (10.0 * ten_meter) ** 2, namespace=locals()) + assert foo == u.CompositeUnit(10000.0, [u.m], [2]) + + assert u.Unit("m") == u.m + assert u.Unit("") == u.dimensionless_unscaled + assert u.one == u.dimensionless_unscaled + assert u.Unit("10 m") == ten_meter + + assert u.Unit() == u.dimensionless_unscaled + + +@pytest.mark.parametrize( + "input_,power_value,power_type", + [ + pytest.param(2.0, 2, int, id="integer_as_float"), + pytest.param(Fraction(2, 1), 2, int, id="numerator_is_one"), + pytest.param(Fraction(1, 2), 0.5, float, id=r"numerator_is_power_of_two"), + pytest.param(np.float64(117 / 1123), 117 / 1123, float, id="float-like"), + pytest.param(np.int64(3), 3, int, id="int-like"), + ], +) +def test_power_types(input_, power_value, power_type): + # Regression test for #16779 - power could sometimes be a numpy number + powers = (u.m**input_).powers + assert list(map(type, powers)) == [power_type] + assert_allclose(powers, power_value) + + +def test_raise_to_power(): + x = u.m ** Fraction(1, 3) + assert isinstance(x.powers[0], Fraction) + + x = u.m ** Fraction(1, 2) + assert isinstance(x.powers[0], float) + + # Test the automatic conversion to a fraction + x = u.m ** (1.0 / 3.0) + assert isinstance(x.powers[0], Fraction) + + # Test power remains integer if possible + x = (u.m**2) ** 0.5 + assert isinstance(x.powers[0], int) + + x = (u.m**-6) ** (1 / 3) + assert isinstance(x.powers[0], int) + + +def test_invalid_compare(): + assert not (u.m == u.s) + + +@pytest.mark.parametrize( + "original,expectation", + [ + pytest.param(1, 3600.0, id="small_int"), + pytest.param(2.0, 7200.0, id="small_float"), + pytest.param(10**25, 3.6e28, id="large_int"), + pytest.param(3e25, 1.08e29, id="large_float"), + ], +) +def test_convert(original, expectation): + result = u.h.get_converter(u.s)(original) + assert_allclose(result, expectation, rtol=1e-15, atol=0) + assert type(result) is float + + +def test_convert_roundtrip(): + c1 = u.cm.get_converter(u.m) + c2 = u.m.get_converter(u.cm) + assert_allclose(c1(c2(10.0)), c2(c1(10.0)), rtol=1e-15, atol=0) + + +def test_convert_roundtrip_with_equivalency(): + c1 = u.arcsec.get_converter(u.pc, u.parallax()) + c2 = u.pc.get_converter(u.arcsec, u.parallax()) + assert_allclose(c1(c2(10.0)), c2(c1(10.0)), rtol=1e-15, atol=0) + + +def test_convert_fail(): + with pytest.raises(u.UnitsError): + u.cm.to(u.s, 1) + with pytest.raises(u.UnitsError): + (u.cm / u.s).to(u.m, 1) + + +def test_composite(): + assert (u.cm / u.s * u.h).get_converter(u.m)(1) == 36 + assert u.cm * u.cm == u.cm**2 + + assert u.cm * u.cm * u.cm == u.cm**3 + + assert u.Hz.to(1000 * u.Hz, 1) == 0.001 + + +def test_str(): + assert str(u.cm) == "cm" + + +def test_repr(): + assert repr(u.cm) == 'Unit("cm")' + + +def test_represents(): + assert u.m.represents is u.m + assert u.km.represents.scale == 1000.0 + assert u.km.represents.bases == [u.m] + assert u.Ry.scale == 1.0 and u.Ry.bases == [u.Ry] + assert_allclose(u.Ry.represents.scale, 13.605692518464949) + assert u.Ry.represents.bases == [u.eV] + bla = u.def_unit("bla", namespace=locals()) + assert bla.represents is bla + blabla = u.def_unit("blabla", 10 * u.hr, namespace=locals()) + assert blabla.represents.scale == 10.0 + assert blabla.represents.bases == [u.hr] + assert blabla.decompose().scale == 10 * 3600 + assert blabla.decompose().bases == [u.s] + + +@pytest.mark.parametrize("func", [u.Unit, u.def_unit]) +@pytest.mark.parametrize( + "represents, match_", + [("not_a_unit", "did not parse"), ([5, 6] * u.hr, "more than one element")], +) +def test_represents_errors(func, represents, match_): + with pytest.raises(ValueError, match=match_): + func("new_unit", represents) + + +def test_units_conversion(): + assert_allclose(u.kpc.to(u.Mpc), 0.001) + assert_allclose(u.Mpc.to(u.kpc), 1000) + assert_allclose(u.yr.to(u.Myr), 1.0e-6) + assert_allclose(u.AU.to(u.pc), 4.84813681e-6) + assert_allclose(u.cycle.to(u.rad), 6.283185307179586) + assert_allclose(u.spat.to(u.sr), 12.56637061435917) + + +def test_decompose(): + assert u.Ry == u.Ry.decompose() + + +def test_dimensionless_to_si(): + """ + Issue #1150: Test for conversion of dimensionless quantities + to the SI system + """ + + testunit = (1.0 * u.kpc) / (1.0 * u.Mpc) + + assert testunit.unit.physical_type == "dimensionless" + assert_allclose(testunit.si, 0.001) + + +def test_dimensionless_to_cgs(): + """ + Issue #1150: Test for conversion of dimensionless quantities + to the CGS system + """ + + testunit = (1.0 * u.m) / (1.0 * u.km) + + assert testunit.unit.physical_type == "dimensionless" + assert_allclose(testunit.cgs, 0.001) + + +def test_unknown_unit(): + with pytest.warns(u.UnitsWarning, match="FOO"): + u.Unit("FOO", parse_strict="warn") + + +def test_multiple_solidus(): + with pytest.warns( + u.UnitsWarning, + match="'m/s/kg' contains multiple slashes, which is discouraged", + ): + assert u.Unit("m/s/kg").to_string() == "m / (kg s)" + + with pytest.raises(ValueError, match="contains multiple slashes"): + u.Unit("m/s/kg", format="vounit") + + # Regression test for #9000: solidi in exponents do not count towards this. + x = u.Unit("kg(3/10) * m(5/2) / s", format="vounit") + assert x.to_string() == "m(5/2) kg(3/10) / s" + + +def test_unknown_unit3(): + unit = u.Unit("FOO", parse_strict="silent") + assert isinstance(unit, u.UnrecognizedUnit) + assert unit.name == "FOO" + + unit2 = u.Unit("FOO", parse_strict="silent") + assert unit == unit2 + assert unit.is_equivalent(unit2) + + unit3 = u.Unit("BAR", parse_strict="silent") + assert unit != unit3 + assert not unit.is_equivalent(unit3) + + # Also test basic (in)equalities. + assert unit == "FOO" + assert unit != u.m + # next two from gh-7603. + assert unit != None + assert unit not in (None, u.m) + + with pytest.raises(ValueError): + unit.get_converter(unit3) + + _ = unit.to_string("latex") + _ = unit2.to_string("cgs") + + with pytest.raises(ValueError): + u.Unit("BAR", parse_strict="strict") + + with pytest.raises(TypeError): + u.Unit(None) + + +@pytest.mark.parametrize( + "parse_strict,expectation", + [ + pytest.param("silent", nullcontext(), id="silent"), + pytest.param( + "warn", + pytest.warns(u.UnitParserWarning, match=r"meant to be a multiplication,"), + id="warn", + ), + pytest.param( + "raise", + pytest.raises(ValueError, match=r"was meant to be a multiplication, "), + id="raise", + ), + pytest.param( + "error", + pytest.raises( + ValueError, + match=r"^'parse_strict' must be 'warn', 'raise' or 'silent'$", + ), + id="invalid", + ), + ], +) +def test_parse_strict_noncritical_error(parse_strict, expectation): + with expectation: + assert u.Unit("m(s)", format="ogip", parse_strict=parse_strict) == u.m * u.s + + +def test_parse_strict_noncritical_error_default(): + with pytest.raises( + ValueError, + match=( + r"^if 'm\(s\)' was meant to be a multiplication, it should have been " + r"written as 'm \(s\)'\.\n" + "If you cannot change the unit string then try specifying the " + r"'parse_strict' argument\.$" + ), + ): + assert u.Unit("m(s)", format="ogip") + + +def test_invalid_scale(): + with pytest.raises(TypeError): + ["a", "b", "c"] * u.m + + +@pytest.mark.parametrize("op", [operator.truediv, operator.lshift, operator.mul]) +def test_invalid_array_op(op): + # see https://github.com/astropy/astropy/issues/12836 + with pytest.raises( + TypeError, match="The value must be a valid Python or Numpy numeric type" + ): + op(np.array(["cat"]), u.one) + + +def test_cds_power(): + unit = u.Unit("10+22/cm2", format="cds", parse_strict="silent") + assert unit.scale == 1e22 + + +def test_register(): + foo = u.def_unit("foo", u.m**3, namespace=locals()) + assert "foo" in locals() + with u.add_enabled_units(foo): + assert "foo" in u.get_current_unit_registry().registry + assert "foo" not in u.get_current_unit_registry().registry + + +def test_in_units_deprecation(): + with pytest.warns(AstropyDeprecationWarning, match=r"Use to\(\) instead\.$"): + assert (u.m / u.s).in_units(u.cm / u.s) == 100 + + +def test_null_unit(): + assert (u.m / u.m) == u.Unit(1) + + +def test_unrecognized_equivalency(): + assert u.m.is_equivalent("foo") is False + assert u.m.is_equivalent("pc") is True + + +def test_convertible_exception(): + with pytest.raises(u.UnitsError, match=r"length.+ are not convertible"): + u.AA.to(u.h * u.s**2) + + +def test_convertible_exception2(): + with pytest.raises(u.UnitsError, match=r"length. and .+time.+ are not convertible"): + u.m.to(u.s) + + +def test_invalid_type(): + class A: + pass + + with pytest.raises(TypeError): + u.Unit(A()) + + +def test_steradian(): + """ + Issue #599 + """ + assert u.sr.is_equivalent(u.rad * u.rad) + + results = u.sr.compose(units=u.cgs.bases) + assert results[0].bases[0] is u.rad + + results = u.sr.compose(units=u.cgs.__dict__) + assert results[0].bases[0] is u.sr + + +def test_decompose_bases(): + """ + From issue #576 + """ + + from astropy.constants import e + from astropy.units import cgs + + d = e.esu.unit.decompose(bases=cgs.bases) + assert d._bases == [u.cm, u.g, u.s] + assert d._powers == [Fraction(3, 2), 0.5, -1] + assert d._scale == 1.0 + + +def test_complex_compose(): + complex = u.cd * u.sr * u.Wb + composed = complex.compose() + + assert set(composed[0]._bases) == {u.lm, u.Wb} + + +def test_equiv_compose(): + composed = u.m.compose(equivalencies=u.spectral()) + assert any([u.Hz] == x.bases for x in composed) + + +def test_empty_compose(): + with pytest.raises(u.UnitsError): + u.m.compose(units=[]) + + +# We use a set to make sure we don't have any duplicates. +COMPOSE_ROUNDTRIP = set() +for val in u.__dict__.values(): + if isinstance(val, u.UnitBase) and not isinstance(val, u.PrefixUnit): + COMPOSE_ROUNDTRIP.add(val) + + +@pytest.mark.parametrize("unit", sorted(COMPOSE_ROUNDTRIP, key=str), ids=repr) +def test_compose_roundtrip(unit): + composed_list = unit.decompose().compose() + found = False + for composed in composed_list: + if len(composed.bases): + if composed.bases[0] is unit: + found = True + break + elif len(unit.bases) == 0: + found = True + break + assert found + + +# We use a set to make sure we don't have any duplicates. +COMPOSE_CGS_TO_SI = set() +for val in u.cgs.__dict__.values(): + # Can't decompose Celsius + if ( + isinstance(val, u.UnitBase) + and not isinstance(val, u.PrefixUnit) + and val != u.cgs.deg_C + ): + COMPOSE_CGS_TO_SI.add(val) + + +@pytest.mark.parametrize("unit", sorted(COMPOSE_CGS_TO_SI, key=str), ids=str) +def test_compose_cgs_to_si(unit): + si = unit.to_system(u.si) + assert [x.is_equivalent(unit) for x in si] + assert si[0] == unit.si + assert np.isclose(si[0].scale, unit.decompose(bases=u.si.bases).scale) + + +# We use a set to make sure we don't have any duplicates. +COMPOSE_SI_TO_CGS = set() +for val in u.si.__dict__.values(): + # Can't decompose Celsius + if ( + isinstance(val, u.UnitBase) + and not isinstance(val, u.PrefixUnit) + and val != u.si.deg_C + ): + COMPOSE_SI_TO_CGS.add(val) + + +@pytest.mark.parametrize("unit", sorted(COMPOSE_SI_TO_CGS, key=str), ids=str) +def test_compose_si_to_cgs(unit): + # Can't convert things with Ampere to CGS without more context + try: + cgs = unit.to_system(u.cgs) + except u.UnitsError: + if u.A in unit.decompose().bases: + pass + else: + raise + else: + assert [x.is_equivalent(unit) for x in cgs] + assert cgs[0] == unit.cgs + assert np.isclose(cgs[0].scale, unit.decompose(bases=u.cgs.bases).scale) + + +def test_to_si(): + """Check units that are not official derived units. + + Should not appear on its own or as part of a composite unit. + """ + # TODO: extend to all units not listed in Tables 1--6 of + # https://physics.nist.gov/cuu/Units/units.html + # See gh-10585. + # This was always the case + assert u.bar.si is not u.bar + # But this used to fail. + assert u.bar not in (u.kg / (u.s**2 * u.sr * u.nm)).si._bases + + +def test_to_cgs(): + assert u.Pa.to_system(u.cgs)[0].bases[0] is u.Ba + assert u.Pa.to_system(u.cgs)[0].scale == 10.0 + + +@pytest.mark.parametrize( + "unit, system, expected_bases", + [ + (u.Pa, "si", [u.Pa]), + (u.sr, "si", [u.rad]), + (u.Gal, "si", [u.m, u.s]), + (u.Gal, "cgs", [u.cm, u.s]), + ], +) +def test_to_system_best_unit(unit, system, expected_bases): + in_system = getattr(unit, system) + assert in_system.bases == expected_bases + + +def test_decompose_to_cgs(): + from astropy.units import cgs + + assert u.m.decompose(bases=cgs.bases)._bases[0] is cgs.cm + + +def test_compose_issue_579(): + unit = u.kg * u.s**2 / u.m + + result = unit.compose(units=[u.N, u.s, u.m]) + + assert len(result) == 1 + assert result[0]._bases == [u.s, u.N, u.m] + assert result[0]._powers == [4, 1, -2] + + +def test_compose_prefix_unit(): + x = u.m.compose(units=(u.m,)) + assert x[0].bases[0] is u.m + assert x[0].scale == 1.0 + x = u.m.compose(units=[u.km], include_prefix_units=True) + assert x[0].bases[0] is u.km + assert x[0].scale == 0.001 + x = u.m.compose(units=[u.km]) + assert x[0].bases[0] is u.km + assert x[0].scale == 0.001 + + x = (u.km / u.s).compose(units=(u.pc, u.Myr)) + assert x[0].bases == [u.pc, u.Myr] + assert_allclose(x[0].scale, 1.0227121650537077) + + with pytest.raises(u.UnitsError): + (u.km / u.s).compose(units=(u.pc, u.Myr), include_prefix_units=False) + + +def test_self_compose(): + unit = u.kg * u.s + + assert len(unit.compose(units=[u.g, u.s])) == 1 + + +def test_compose_failed(): + unit = u.kg + with pytest.raises(u.UnitsError): + unit.compose(units=[u.N]) + + +def test_compose_fractional_powers(): + # Warning: with a complicated unit, this test becomes very slow; + # e.g., x = (u.kg / u.s ** 3 * u.au ** 2.5 / u.yr ** 0.5 / u.sr ** 2) + # takes 3 s + x = u.m**0.5 / u.yr**1.5 + + factored = x.compose() + + for unit in factored: + assert x.decompose() == unit.decompose() + + factored = x.compose(units=u.cgs) + + for unit in factored: + assert x.decompose() == unit.decompose() + + factored = x.compose(units=u.si) + + for unit in factored: + assert x.decompose() == unit.decompose() + + +def test_compose_best_unit_first(): + results = u.l.compose() + assert len(results[0].bases) == 1 + assert results[0].bases[0] is u.l + + results = (u.s**-1).compose() + assert results[0].bases[0] in (u.Hz, u.Bq) + + results = (u.Ry.decompose()).compose() + assert results[0].bases[0] is u.Ry + + +def test_compose_no_duplicates(): + new = u.kg / u.s**3 * u.au**2.5 / u.yr**0.5 / u.sr**2 + composed = new.compose(units=u.cgs.bases) + assert len(composed) == 1 + + +@pytest.mark.parametrize( + "dtype", tuple(map("".join, itertools.product("<>", "if", "48"))) +) +def test_endian_independence(dtype): + """ + Regression test for #744 + + A logic issue in the units code meant that big endian arrays could not be + converted because the dtype is '>f4', not 'float32', and the code was + looking for the strings 'float' or 'int'. + """ + x = np.array([1, 2, 3], dtype=dtype) + assert u.m.to(u.cm, x).tolist() == [100.0, 200.0, 300.0] + + +def test_radian_base(): + """ + Issue #863 + """ + assert (1 * u.degree).si.unit == u.rad + + +def test_no_as(): + # We don't define 'as', since it is a keyword, but we + # do want to define the long form (`attosecond`). + assert not hasattr(u, "as") + assert hasattr(u, "attosecond") + + +def test_no_duplicates_in_names(): + # Regression test for #5036 + assert u.ct.names == ["ct", "count"] + assert u.ct.short_names == ["ct", "count"] + assert u.ct.long_names == ["count"] + assert set(u.ph.names) == set(u.ph.short_names) | set(u.ph.long_names) + + +def test_pickling(): + p = pickle.dumps(u.m) + other = pickle.loads(p) + + assert other is u.m + + new_unit = u.IrreducibleUnit(["foo"], format={"unicode": "bar"}) + # This is local, so the unit should not be registered. + assert "foo" not in u.get_current_unit_registry().registry + + # Test pickling of this unregistered unit. + p = pickle.dumps(new_unit) + new_unit_copy = pickle.loads(p) + assert new_unit_copy is not new_unit + assert new_unit_copy.names == ["foo"] + assert new_unit_copy.to_string("unicode") == "bar" + # It should still not be registered. + assert "foo" not in u.get_current_unit_registry().registry + + # Now try the same with a registered unit. + with u.add_enabled_units([new_unit]): + p = pickle.dumps(new_unit) + assert "foo" in u.get_current_unit_registry().registry + new_unit_copy = pickle.loads(p) + assert new_unit_copy is new_unit + + # Check that a registered unit can be loaded and that it gets re-enabled. + with u.add_enabled_units([]): + assert "foo" not in u.get_current_unit_registry().registry + new_unit_copy = pickle.loads(p) + assert new_unit_copy is not new_unit + assert new_unit_copy.names == ["foo"] + assert new_unit_copy.to_string("unicode") == "bar" + assert "foo" in u.get_current_unit_registry().registry + + # And just to be sure, that it gets removed outside of the context. + assert "foo" not in u.get_current_unit_registry().registry + + +def test_pickle_between_sessions(): + """We cannot really test between sessions easily, so fake it. + + This test can be changed if the pickle protocol or the code + changes enough that it no longer works. + + """ + hash_m = hash(u.m) + unit = pickle.loads( + b"\x80\x04\x95\xd6\x00\x00\x00\x00\x00\x00\x00\x8c\x12" + b"astropy.units.core\x94\x8c\x1a_recreate_irreducible_unit" + b"\x94\x93\x94h\x00\x8c\x0fIrreducibleUnit\x94\x93\x94]\x94" + b"(\x8c\x01m\x94\x8c\x05meter\x94e\x88\x87\x94R\x94}\x94(\x8c\x06" + b"_names\x94]\x94(h\x06h\x07e\x8c\x0c_short_names" + b"\x94]\x94h\x06a\x8c\x0b_long_names\x94]\x94h\x07a\x8c\x07" + b"_format\x94}\x94\x8c\x07__doc__\x94\x8c " + b"meter: base unit of length in SI\x94ub." + ) + assert unit is u.m + assert hash(u.m) == hash_m + + +@pytest.mark.parametrize( + "unit", + [u.IrreducibleUnit(["foo"], format={"baz": "bar"}), u.Unit("m_per_s", u.m / u.s)], +) +def test_pickle_does_not_keep_memoized_hash(unit): + """ + Tests private attribute since the problem with _hash being pickled + and restored only appeared if the unpickling was done in another + session, for which the hash no longer was valid, and it is difficult + to mimic separate sessions in a simple test. See gh-11872. + """ + unit_hash = hash(unit) + assert "_hash" in vars(unit) + unit_copy = pickle.loads(pickle.dumps(unit)) + # unit is not registered so we get a copy. + assert unit_copy is not unit + assert "_hash" not in vars(unit_copy) + assert hash(unit_copy) == unit_hash + with u.add_enabled_units([unit]): + # unit is registered, so we get a reference. + unit_ref = pickle.loads(pickle.dumps(unit)) + if isinstance(unit, u.IrreducibleUnit): + assert unit_ref is unit + else: + assert unit_ref is not unit + # pickle.load used to override the hash, although in this case + # it would be the same anyway, so not clear this tests much. + assert hash(unit) == unit_hash + + +def test_pickle_unrecognized_unit(): + """ + Issue #2047 + """ + a = u.Unit("asdf", parse_strict="silent") + assert isinstance(pickle.loads(pickle.dumps(a)), u.UnrecognizedUnit) + + +@pytest.mark.parametrize( + "name,message", + [ + pytest.param( + "h", + r"^the namespace already uses the name 'h' for Unit\(\"h\"\)$", + id="simple_conflict", + ), + pytest.param( + "ʰ", + ( + "^the namespace already uses the NFKC normalized name 'h' for " + r'Unit\("h"\)' + "\n\nSee " + "https://docs.python.org/3/reference/lexical_analysis.html#identifiers " + r"for more information\.$" + ), + id="NFKC_normalization", + ), + ], +) +def test_duplicate_define(name, message): + namespace = {"h": u.h} + with pytest.raises(ValueError, match=message): + u.def_unit(name, u.hourangle, namespace=namespace) + + +def test_unit_module_dunder_all_nfkc_normalization(): + # Python applies NFKC normalization to identifiers, so inserting a name to __all__ + # that changes with NFKC normalization can only cause trouble. + assert "ℓ" not in u.__all__ + assert u.ℓ is u.liter + + +@pytest.mark.parametrize( + "string", + [ + pytest.param("°", id="invalid characters"), # Regression test for #18606 + pytest.param("as", id="keyword"), # Regression test for #18614 + ], +) +def test_unit_module_dunder_all_only_indentifiers(string): + assert string not in cds.__all__ + + +def test_all_units(): + from astropy.units.core import get_current_unit_registry + + registry = get_current_unit_registry() + assert len(registry.all_units) > len(registry.non_prefix_units) + + +def test_repr_latex(): + assert u.m._repr_latex_() == u.m.to_string("latex") + + +def test_operations_with_strings(): + with pytest.warns( + AstropyDeprecationWarning, + match=( + "^divisions involving a unit and a 'str' instance are deprecated since " + r"v7\.1\. Convert '5s' to a unit explicitly\.$" + ), + ): + assert u.m / "5s" == (u.m / (5.0 * u.s)) + + with pytest.warns( + AstropyDeprecationWarning, + match=( + "^products involving a unit and a 'str' instance are deprecated since " + r"v7\.1\. Convert '5s' to a unit explicitly\.$" + ), + ): + assert u.m * "5s" == (5.0 * u.m * u.s) + + +def test_comparison(): + assert u.m > u.cm + assert u.m >= u.cm + assert u.cm < u.m + assert u.cm <= u.m + + with pytest.raises(u.UnitsError): + u.m > u.kg # noqa: B015 + + +def test_compose_into_arbitrary_units(): + # Issue #1438 + from astropy.constants import G + + G_decomposed = G.decompose([u.kg, u.km, u.Unit("100 s")]) + assert_allclose(G_decomposed.unit.scale, 1e-4) + assert G_decomposed == G + + +def test_unit_multiplication_with_string(): + with pytest.warns( + AstropyDeprecationWarning, + match=( + "^products involving a unit and a 'str' instance are deprecated since " + r"v7\.1\. Convert 'kg' to a unit explicitly\.$" + ), + ): + assert "kg" * u.cm == u.kg * u.cm + with pytest.warns(AstropyDeprecationWarning, match="^products involving .* 'str'"): + assert u.cm * "kg" == u.cm * u.kg + + +def test_unit_division_by_string(): + with pytest.warns( + AstropyDeprecationWarning, + match=( + "^divisions involving a unit and a 'str' instance are deprecated since " + r"v7\.1\. Convert 'kg' to a unit explicitly\.$" + ), + ): + assert "kg" / u.cm == u.kg / u.cm + with pytest.warns(AstropyDeprecationWarning, match="^divisions involving .* 'str'"): + assert u.cm / "kg" == u.cm / u.kg + + +def test_sorted_bases(): + """See #1616.""" + assert (u.m * u.Jy).bases == (u.Jy * u.m).bases + + +def test_megabit(): + """See #1543""" + assert u.Mbit is u.Mb + assert u.megabit is u.Mb + + assert u.Mbyte is u.MB + assert u.megabyte is u.MB + + +def test_composite_unit_get_format_name(): + """See #1576""" + unit1 = u.Unit("nrad/s") + unit2 = u.Unit("Hz(1/2)") + assert str(u.CompositeUnit(1, [unit1, unit2], [1, -1])) == "nrad / (Hz(1/2) s)" + + +def test_unicode_policy(): + from astropy.tests.helper import assert_follows_unicode_guidelines + + assert_follows_unicode_guidelines(u.degree, roundtrip=u.__dict__) + + +def test_suggestions(): + for search, matches in [ + ("microns", "micron"), + ("s/microns", "micron"), + ("M", "m"), + ("metre", "meter"), + ("angstroms", "Angstrom or angstrom"), + ("milimeter", "millimeter"), # codespell:ignore milimeter + ("ÃĨngstrÃļm", "Angstrom, angstrom, mAngstrom or mangstrom"), + ("kev", "EV, eV, kV or keV"), + ]: + with pytest.raises(ValueError, match=f"Did you mean {matches}"): + u.Unit(search) + + +def test_fits_hst_unit(): + """See #1911.""" + with pytest.warns(u.UnitsWarning, match="multiple slashes") as w: + x = u.Unit("erg /s /cm**2 /angstrom") + assert x == u.erg * u.s**-1 * u.cm**-2 * u.angstrom**-1 + assert len(w) == 1 + + +def test_barn_prefixes(): + """Regression test for https://github.com/astropy/astropy/issues/3753""" + + assert u.fbarn is u.femtobarn + assert u.pbarn is u.picobarn + + +def test_fractional_powers(): + """See #2069""" + m = 1e9 * u.Msun + tH = 1.0 / (70.0 * u.km / u.s / u.Mpc) + vc = 200 * u.km / u.s + + x = (c.G**2 * m**2 * tH.cgs) ** Fraction(1, 3) / vc + v1 = x.to("pc") + + x = (c.G**2 * m**2 * tH) ** Fraction(1, 3) / vc + v2 = x.to("pc") + + x = (c.G**2 * m**2 * tH.cgs) ** (1.0 / 3.0) / vc + v3 = x.to("pc") + + x = (c.G**2 * m**2 * tH) ** (1.0 / 3.0) / vc + v4 = x.to("pc") + + assert_allclose(v1, v2) + assert_allclose(v2, v3) + assert_allclose(v3, v4) + + x = u.m ** (1.0 / 101.0) + assert isinstance(x.powers[0], float) + + x = u.m ** (3.0 / 7.0) + assert isinstance(x.powers[0], Fraction) + assert x.powers[0].numerator == 3 + assert x.powers[0].denominator == 7 + + x = u.cm ** Fraction(1, 2) * u.cm ** Fraction(2, 3) + assert isinstance(x.powers[0], Fraction) + assert x.powers[0] == Fraction(7, 6) + + # Regression test for #9258 (avoid fractions with crazy denominators). + x = (u.TeV ** (-2.2)) ** (1 / -2.2) + assert isinstance(x.powers[0], int) + assert x.powers[0] == 1 + x = (u.TeV ** (-2.2)) ** (1 / -6.6) + assert isinstance(x.powers[0], Fraction) + assert x.powers[0] == Fraction(1, 3) + + +def test_large_fractional_powers(): + # Ensure we keep fractions if the user passes them in + # and the powers are themselves simple fractions. + x1 = u.m ** Fraction(10, 11) + assert isinstance(x1.powers[0], Fraction) + assert x1.powers[0] == Fraction(10, 11) + x2 = x1 ** Fraction(10, 11) + assert isinstance(x2.powers[0], Fraction) + assert x2.powers[0] == Fraction(100, 121) + # Check powers that can be represented as simple fractions. + x3 = x2**0.5 + assert isinstance(x3.powers[0], Fraction) + assert x3.powers[0] == Fraction(50, 121) + x4 = x3 ** (5 / 11) + assert isinstance(x4.powers[0], Fraction) + assert x4.powers[0] == Fraction(250, 1331) + x5 = x4**1.1 + assert isinstance(x5.powers[0], Fraction) + assert x5.powers[0] == Fraction(25, 121) + + +def test_sqrt_mag(): + sqrt_mag = u.mag**0.5 + assert hasattr(sqrt_mag.decompose().scale, "imag") + assert (sqrt_mag.decompose()) ** 2 == u.mag + + +def test_composite_compose(): + # Issue #2382 + composite_unit = u.s.compose(units=[u.Unit("s")])[0] + u.s.compose(units=[composite_unit]) + + +def test_data_quantities(): + assert u.byte.is_equivalent(u.bit) + + +def test_compare_with_none(): + # Ensure that equality comparisons with `None` work, and don't + # raise exceptions. We are deliberately not using `is None` here + # because that doesn't trigger the bug. See #3108. + assert not (u.m == None) + assert u.m != None + + +def test_sanitize_power_detect_fraction(): + frac = utils.sanitize_power(1.1666666666666665) + assert isinstance(frac, Fraction) + assert frac.numerator == 7 + assert frac.denominator == 6 + + +def test_sanitize_power_zero_like(): + # Regression test for #16779 - 0 should always be an int after sanitizing + power = utils.sanitize_power(np.float64(0)) + assert type(power) is int + assert power == 0 + + +def test_complex_fractional_rounding_errors(): + # See #3788 + + kappa = 0.34 * u.cm**2 / u.g + r_0 = 886221439924.7849 * u.cm + q = 1.75 + rho_0 = 5e-10 * u.solMass / u.solRad**3 + y = 0.5 + beta = 0.19047619047619049 + a = 0.47619047619047628 + m_h = 1e6 * u.solMass + + t1 = 2 * c.c / (kappa * np.sqrt(np.pi)) + t2 = (r_0**-q) / (rho_0 * y * beta * (a * c.G * m_h) ** 0.5) + + result = (t1 * t2) ** -0.8 + + assert result.unit.physical_type == "length" + result.to(u.solRad) + + +def test_fractional_rounding_errors_simple(): + x = (u.m**1.5) ** Fraction(4, 5) + assert isinstance(x.powers[0], Fraction) + assert x.powers[0].numerator == 6 + assert x.powers[0].denominator == 5 + + +def test_enable_unit_groupings(): + from astropy.units import cds + + with cds.enable(): + assert cds.geoMass in u.kg.find_equivalent_units() + + from astropy.units import imperial + + with imperial.enable(): + assert imperial.inch in u.m.find_equivalent_units() + + +def test_raise_to_negative_power(): + """Test that order of bases is changed when raising to negative power. + + Regression test for https://github.com/astropy/astropy/issues/8260 + """ + m2s2 = u.m**2 / u.s**2 + spm = m2s2 ** (-1 / 2) + assert spm.bases == [u.s, u.m] + assert spm.powers == [1, -1] + assert spm == u.s / u.m + + +@pytest.mark.parametrize( + "name, factor", + [ + pytest.param(name, factor, id=name) + for name, factor in [ + ("quetta", 1e30), + ("ronna", 1e27), + ("yotta", 1e24), + ("zetta", 1e21), + ("exa", 1e18), + ("peta", 1e15), + ("tera", 1e12), + ("giga", 1e9), + ("mega", 1e6), + ("kilo", 1e3), + ("deca", 1e1), + ("deka", 1e1), # American spelling of deca + ("deci", 1e-1), + ("centi", 1e-2), + ("milli", 1e-3), + ("micro", 1e-6), + ("nano", 1e-9), + ("pico", 1e-12), + ("femto", 1e-15), + ("atto", 1e-18), + ("zepto", 1e-21), + ("yocto", 1e-24), + ("ronto", 1e-27), + ("quecto", 1e-30), + ] + ], +) +def test_si_prefix_names(name, factor): + base = 1 * u.s + quantity_from_name = base.to(f"{name}second") + assert u.isclose(quantity_from_name, base) + assert np.isclose(base.value / quantity_from_name.value, factor, atol=0) + + +@pytest.mark.parametrize( + "symbol, factor", + [ + pytest.param(symbol, factor, id=symbol) + for symbol, factor in [ + ("Q", 1e30), + ("R", 1e27), + ("Y", 1e24), + ("Z", 1e21), + ("E", 1e18), + ("P", 1e15), + ("T", 1e12), + ("G", 1e9), + ("M", 1e6), + ("k", 1e3), + ("da", 1e1), + ("d", 1e-1), + ("c", 1e-2), + ("m", 1e-3), + ("\N{MICRO SIGN}", 1e-6), + ("\N{GREEK SMALL LETTER MU}", 1e-6), + ("u", 1e-6), + ("n", 1e-9), + ("p", 1e-12), + ("f", 1e-15), + ("a", 1e-18), + ("z", 1e-21), + ("y", 1e-24), + ("r", 1e-27), + ("q", 1e-30), + ] + ], +) +def test_si_prefix_symbols(symbol, factor): + base = 1 * u.m + quantity_from_symbol = base.to(f"{symbol}m") + assert u.isclose(quantity_from_symbol, base) + assert np.isclose(base.value / quantity_from_symbol.value, factor, atol=0) + + +@pytest.mark.parametrize( + "name,symbol,factor", + [ + pytest.param(name, symbol, factor, id=name) + for name, symbol, factor in [ + ("kibi", "Ki", 2**10), + ("mebi", "Mi", 2**20), + ("gibi", "Gi", 2**30), + ("tebi", "Ti", 2**40), + ("pebi", "Pi", 2**50), + ("exbi", "Ei", 2**60), + # We now switch to float factors because with numpy < 2.0 + # np.isclose() doesn't like ints this large + ("zebi", "Zi", 2.0**70), + ("yobi", "Yi", 2.0**80), + ] + ], +) +def test_si_binary_prefixes(name, symbol, factor): + base = 1 * u.byte + quantity_from_name = base.to(f"{name}byte") + assert u.isclose(quantity_from_name, base) + assert np.isclose(base.value / quantity_from_name.value, factor, atol=0) + + quantity_from_symbol = base.to(f"{symbol}B") + assert u.isclose(quantity_from_symbol, base) + assert np.isclose(base.value / quantity_from_symbol.value, factor, atol=0) + + +def test_cm_uniqueness(): + # Ensure we have defined cm only once; see gh-15200. + assert u.si.cm is u.cgs.cm is u.cm + assert str(u.si.cm / u.cgs.cm) == "" # was cm / cm + + +@pytest.mark.parametrize("unit, power", [(u.m, 2), (u.m, 3), (u.m / u.s, 9)]) +def test_hash_represents_unit(unit, power): + # Regression test for gh-16055 + tu = (unit**power) ** (1 / power) + assert hash(tu) == hash(unit) + tu2 = (unit ** (1 / power)) ** power + assert hash(tu2) == hash(unit) + + +@pytest.mark.skipif(not HAS_ARRAY_API_STRICT, reason="tests array_api_strict") +def test_array_api_strict_arrays(): + # Ensure strict array api arrays can be passed in/out of Unit.to() + # Note that those have non-standard dtype. + import array_api_strict as xp + + data1 = xp.asarray([1.0, 2.0, 3.0]) + data2 = u.m.to(u.km, value=data1) + assert isinstance(data2, type(data1)) + assert_allclose(data2, [0.001, 0.002, 0.003]) + + data3 = u.K.to(u.deg_C, value=data1, equivalencies=u.temperature()) + assert isinstance(data3, type(data1)) + assert_allclose(data3, [-272.15, -271.15, -270.15]) + + +@pytest.mark.skipif(not HAS_DASK, reason="tests dask.array") +def test_dask_arrays(): + # Make sure that dask arrays can be passed in/out of Unit.to() + + from dask import array as da + + data1 = da.from_array([1, 2, 3]) + + data2 = u.m.to(u.km, value=data1) + + assert isinstance(data2, da.core.Array) + + assert_allclose(data2.compute(), [0.001, 0.002, 0.003]) + + data3 = u.K.to(u.deg_C, value=data1, equivalencies=u.temperature()) + + assert isinstance(data3, da.core.Array) + + assert_allclose(data3.compute(), [-272.15, -271.15, -270.15]) + + +def test_get_format_name_deprecation(): + with pytest.warns(AstropyDeprecationWarning, match=r"Use to_string\(\) instead\.$"): + assert u.m.get_format_name("fits") == "m" + + +def test_comparison_dimensionless_with_np_ma_masked(): + # Found to be a problem indirectly in gh-17047; + # The path np.ma.masked.__eq__(u.dimensionless_unscaled) + # used to give a ZeroDivisionError. + comparison = u.dimensionless_unscaled == np.ma.masked + assert comparison is np.ma.masked + + +def test_error_on_conversion_of_zero_to_unit(): + # Found to be a problem indirectly in gh-17047; we allow conversion + # of numbers to units, but should not allow 0. + with pytest.raises(u.UnitScaleError, match="cannot create.*scale of 0"): + u.Unit(0) + with pytest.raises(u.UnitScaleError, match="cannot create.*scale of 0"): + u.dimensionless_unscaled.to(0) + # Also check some that do work. + assert u.dimensionless_unscaled.to(0.125) == 8 + assert u.dimensionless_unscaled.to(8) == 0.125 + + +@pytest.mark.parametrize( + "unsanitized,sanitized", + [ + pytest.param(complex(2, FLOAT_EPS), 2, id="almost_real_complex"), + pytest.param(complex(FLOAT_EPS, 2), 2j, id="almost_imaginary_complex"), + ], +) +def test_scale_sanitization(unsanitized, sanitized): + assert u.CompositeUnit(unsanitized, [u.m], [1]).scale == sanitized + + +@pytest.mark.parametrize( + "scale", + [ + 5, + 10.0, + 7 + 3j, + Fraction(1, 3), + np.int32(100), + np.float32(0.01), + np.complex128(1 - 4j), + ], + ids=type, +) +def test_dimensionless_scale_factor_types(scale): + # Regression test for #17355 - Unit did not accept all scale factor + # types that CompositeUnit accepted + assert u.Unit(scale) == u.CompositeUnit(scale, [], []) + + +# No need to test everything defined in required_by_vounit, the following few are +# representative enough. +required_by_vounit_parametrization = pytest.mark.parametrize( + "unit", [GsolLum, ksolMass, nsolRad], ids=lambda x: x.name +) + + +@required_by_vounit_parametrization +def test_required_by_vounit_not_in_main_namespace(unit): + with pytest.raises( + AttributeError, + match=rf"^module 'astropy\.units' has no attribute '{unit.name}'$", + ): + getattr(u, unit.name) + + +@required_by_vounit_parametrization +def test_required_by_vounit_parsing(unit): + assert u.Unit(unit.name) is unit + + +@required_by_vounit_parametrization +def test_required_by_vounit_not_in_find_equivalent_units(unit): + assert unit not in unit.represents.bases[0].find_equivalent_units() + + +@pytest.mark.parametrize("format_", ["cds", "fits", "generic", "ogip", "vounit"]) +def test_parsing_as(format_): + # The symbol for the attosecond is "as", which can be problematic because it + # happens to be a Python keyword. + assert u.Unit("as", format=format_) == u.attosecond diff --git a/astropy/units/typing.py b/astropy/units/typing.py new file mode 100644 index 000000000000..649a203302cc --- /dev/null +++ b/astropy/units/typing.py @@ -0,0 +1,91 @@ +"""Typing module for supporting type annotations related to :mod:`~astropy.units`.""" + +__all__ = [ + "QuantityLike", + "UnitLike", + "UnitPower", + "UnitPowerLike", + "UnitScale", + "UnitScaleLike", +] + + +from fractions import Fraction +from typing import TYPE_CHECKING, TypeAlias, Union + +import numpy as np +import numpy.typing as npt + +if TYPE_CHECKING: + import astropy.units + +UnitLike: TypeAlias = Union["astropy.units.UnitBase", str, "astropy.units.Quantity"] +"""Type alias for input that can be converted to a Unit. + +See :term:`unit-like`. Note that this includes only scalar quantities. +""" + +# Note: Quantity is technically covered by npt.ArrayLike, but we want to +# explicitly include it here so that it is clear that we are also including +# Quantity objects in the definition of QuantityLike. +QuantityLike: TypeAlias = Union["astropy.units.Quantity", npt.ArrayLike] +"""Type alias for a quantity-like object. + +This is an object that can be converted to a :class:`~astropy.units.Quantity` object +using the :func:`~astropy.units.Quantity` constructor. + +Examples +-------- +We assume the following imports: + + >>> from astropy import units as u + +This is a non-exhaustive list of examples of quantity-like objects: + +Integers and floats: + + >>> u.Quantity(1, u.meter) + + + >>> u.Quantity(1.0, u.meter) + + +Lists and tuples: + + >>> u.Quantity([1.0, 2.0], u.meter) + + + >>> u.Quantity((1.0, 2.0), u.meter) + + +Numpy arrays: + + >>> u.Quantity(np.array([1.0, 2.0]), u.meter) + +:class:`~astropy.units.Quantity` objects: + + >>> u.Quantity(u.Quantity(1.0, u.meter)) + + +Strings: + + >>> u.Quantity('1.0 m') + + +For more examples see the :mod:`numpy.typing` definition of +:obj:`numpy.typing.ArrayLike`. +""" + + +UnitPower: TypeAlias = int | float | Fraction +"""Alias for types that can be powers of the components of a +`~astropy.units.UnitBase` instance""" +UnitPowerLike: TypeAlias = UnitPower | np.integer | np.floating +"""Alias for types that can be used to create powers of the components of a +`~astropy.units.UnitBase` instance""" +UnitScale: TypeAlias = float | complex +"Alias for types that can be scale factors of a `~astropy.units.CompositeUnit`" +UnitScaleLike: TypeAlias = UnitScale | int | Fraction | np.number +"""Alias for types that can be used to create scale factors of a +`~astropy.units.CompositeUnit`""" +PhysicalTypeID: TypeAlias = tuple[tuple[str, UnitPower], ...] diff --git a/astropy/units/typing_utils.py b/astropy/units/typing_utils.py new file mode 100644 index 000000000000..0a0010553e8c --- /dev/null +++ b/astropy/units/typing_utils.py @@ -0,0 +1,74 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +"""Utilities for generating type stubs for unit definition modules. + +This module is meant for internal use only (in setup.py). +""" + +__all__: list[str] = [] + +import inspect +from collections.abc import Iterable +from pathlib import Path +from types import FunctionType, ModuleType +from typing import Final + +from astropy import units as u +from astropy.units import cds + +UNIT_DEFINITION_MODULES: Final = ( + u.astrophys, + cds, + u.cgs, + u.imperial, + u.misc, + u.photometric, + u.required_by_vounit, + u.si, + u.function.units, +) + + +def generate_stub(output_dir: Path, module: ModuleType) -> None: + members = {} + functions = {} + for name in module.__all__: + member = getattr(module, name) + if isinstance(member, FunctionType): + # Neither of the functions in unit definition modules have any parameters. + functions[name] = inspect.get_annotations(member)["return"] + else: + members[name] = type(member) + import_lines = sorted( + f"from {cls.__module__} import {cls.__name__}\n" + for cls in set(members.values()).union(functions.values()) + ) + with ( + output_dir.joinpath(*module.__name__.split(".")) + .with_suffix(".pyi") + .open("wt", encoding="utf-8") as stub_file + ): + stub_file.write("# This stub file was automatically generated.\n\n") + + stub_file.write("__all__ = [\n") + stub_file.writelines(f' "{name}",\n' for name in module.__all__) + stub_file.write("]\n\n") + + stub_file.writelines(sorted(import_lines)) + stub_file.write("\n") + + stub_file.writelines( + f"{member}: {cls.__name__}\n" for member, cls in members.items() + ) + + stub_file.writelines( + f"\ndef {name}() -> {return_type.__name__}: ...\n" + for name, return_type in functions.items() + ) + + +def create_stubs( + output_dir: Path, modules: Iterable[ModuleType] = UNIT_DEFINITION_MODULES +) -> None: + for module in modules: + generate_stub(output_dir, module) diff --git a/astropy/units/utils.py b/astropy/units/utils.py new file mode 100644 index 000000000000..ef741a5b888c --- /dev/null +++ b/astropy/units/utils.py @@ -0,0 +1,142 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +""" +Miscellaneous utilities for `astropy.units`. + +None of the functions in the module are meant for use outside of the +package. +""" + +from fractions import Fraction +from typing import SupportsFloat + +import numpy as np +from numpy import finfo + +from .errors import UnitScaleError +from .typing import UnitPower, UnitPowerLike, UnitScale, UnitScaleLike + +_float_finfo = finfo(float) +# take float here to ensure comparison with another float is fast +# give a little margin since often multiple calculations happened +_JUST_BELOW_UNITY = float(1.0 - 4.0 * _float_finfo.epsneg) +_JUST_ABOVE_UNITY = float(1.0 + 4.0 * _float_finfo.eps) + + +def is_effectively_unity(value: UnitScaleLike) -> bool | np.bool_: + # value is *almost* always real, except, e.g., for u.mag**0.5, when + # it will be complex. Use try/except to ensure normal case is fast + try: + return _JUST_BELOW_UNITY <= value <= _JUST_ABOVE_UNITY + except TypeError: # value is complex + return ( + _JUST_BELOW_UNITY <= value.real <= _JUST_ABOVE_UNITY + and _JUST_BELOW_UNITY <= value.imag + 1 <= _JUST_ABOVE_UNITY + ) + + +def sanitize_scale(scale: UnitScaleLike) -> UnitScale: + if is_effectively_unity(scale): + return 1.0 + if not scale: + raise UnitScaleError("cannot create a unit with a scale of 0.") + if type(scale) is float: # float is very common, so handle it fast + return scale + if isinstance(scale, SupportsFloat): + return float(scale) + + if abs(scale.real) > abs(scale.imag): + if is_effectively_unity(scale.imag / scale.real + 1): + return float(scale.real) + elif is_effectively_unity(scale.real / scale.imag + 1): + return complex(0.0, scale.imag) + return complex(scale) + + +def maybe_simple_fraction(p: UnitPowerLike, max_denominator: int = 100) -> UnitPower: + """Fraction very close to x with denominator at most max_denominator. + + The fraction has to be such that fraction/x is unity to within 4 ulp. + If such a fraction does not exist, returns the float number. + + The algorithm is that of `fractions.Fraction.limit_denominator`, but + sped up by not creating a fraction to start with. + + If the input is zero, an integer or `fractions.Fraction`, just return it. + """ + if p.__class__ is int or p.__class__ is Fraction: + return p + if p == 0: + return 0 # p might be some numpy number, but we want a Python int + n, d = float(p).as_integer_ratio() + a = n // d + # Normally, start with 0,1 and 1,0; here we have applied first iteration. + n0, d0 = 1, 0 + n1, d1 = a, 1 + while d1 <= max_denominator: + if _JUST_BELOW_UNITY <= n1 / (d1 * p) <= _JUST_ABOVE_UNITY: + return Fraction(n1, d1) + n, d = d, n - a * d + a = n // d + n0, n1 = n1, n0 + a * n1 + d0, d1 = d1, d0 + a * d1 + + return float(p) + + +def sanitize_power(p: UnitPowerLike) -> UnitPower: + """Convert the power to a float, an integer, or a Fraction. + + If a fractional power can be represented exactly as a floating point + number, convert it to a float, to make the math much faster; otherwise, + retain it as a `fractions.Fraction` object to avoid losing precision. + Conversely, if the value is indistinguishable from a rational number with a + low-numbered denominator, convert to a Fraction object. + If a power can be represented as an integer, use that. + + Parameters + ---------- + p : float, int, Rational, Fraction + Power to be converted. + """ + if p.__class__ is int: + return p + + p = maybe_simple_fraction(p) + if isinstance(p, float): + return p # If still a float, nothing more to be done. + # Otherwise, check for simplifications. + denom = p.denominator + + if denom == 1: + return int(p.numerator) + + elif (denom & (denom - 1)) == 0: + # Above is a bit-twiddling hack to see if denom is a power of two. + # If so, float does not lose precision and will speed things up. + p = float(p) + + return p + + +def resolve_fractions( + a: UnitPowerLike, b: UnitPowerLike +) -> tuple[UnitPowerLike, UnitPowerLike]: + """ + If either input is a Fraction, convert the other to a Fraction + (at least if it does not have a ridiculous denominator). + This ensures that any operation involving a Fraction will use + rational arithmetic and preserve precision. + """ + # We short-circuit on the most common cases of int and float, since + # isinstance(a, Fraction) is very slow for any non-Fraction instances. + a_is_fraction = ( + a.__class__ is not int and a.__class__ is not float and isinstance(a, Fraction) + ) + b_is_fraction = ( + b.__class__ is not int and b.__class__ is not float and isinstance(b, Fraction) + ) + if a_is_fraction and not b_is_fraction: + b = maybe_simple_fraction(b) + elif not a_is_fraction and b_is_fraction: + a = maybe_simple_fraction(a) + return a, b diff --git a/astropy/utils/__init__.py b/astropy/utils/__init__.py index 560ccdc4d2d4..270e68554113 100644 --- a/astropy/utils/__init__.py +++ b/astropy/utils/__init__.py @@ -4,17 +4,18 @@ Public functions and classes in this subpackage are safe to be used by other packages, but this subpackage is for utilities that are primarily of use for -developers or to implement python hacks. This subpackage also includes the -`astropy.utils.compat` package, which houses utilities that provide -compatibility and bugfixes across all versions of Python that Astropy supports. +developers or to implement python hacks. + +This subpackage also includes the ``astropy.utils.compat`` package, +which houses utilities that provide compatibility and bugfixes across +all versions of Python that Astropy supports. However, the content of this +module is solely for internal use of ``astropy`` and subject to changes +without deprecations. Do not use it in external packages or code. -For astronomy-specific utilities of general use (e.g. not specific to some -other subpackage), see the `astropy.tools` package. """ -from .compat.odict import OrderedDict +from .codegen import * +from .decorators import * +from .introspection import * from .misc import * - -# The location of the online documentation for astropy -# This location will normally point to the current released version of astropy -online_docs_root = 'http://docs.astropy.org' +from .shapes import * diff --git a/astropy/utils/codegen.py b/astropy/utils/codegen.py new file mode 100644 index 000000000000..2978003c6949 --- /dev/null +++ b/astropy/utils/codegen.py @@ -0,0 +1,132 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +"""Utilities for generating new Python code at runtime.""" + +import inspect +import keyword +import os +import re +import textwrap + +from .introspection import find_current_module + +__all__ = ["make_function_with_signature"] + + +_ARGNAME_RE = re.compile(r"^[A-Za-z][A-Za-z_]*") +""" +Regular expression used my make_func which limits the allowed argument +names for the created function. Only valid Python variable names in +the ASCII range and not beginning with '_' are allowed, currently. +""" + + +def make_function_with_signature( + func, args=(), kwargs={}, varargs=None, varkwargs=None, name=None +): + """ + Make a new function from an existing function but with the desired + signature. + + The desired signature must of course be compatible with the arguments + actually accepted by the input function. + + The ``args`` are strings that should be the names of the positional + arguments. ``kwargs`` can map names of keyword arguments to their + default values. It may be either a ``dict`` or a list of ``(keyword, + default)`` tuples. + + If ``varargs`` is a string it is added to the positional arguments as + ``*``. Likewise ``varkwargs`` can be the name for a variable + keyword argument placeholder like ``**``. + + If not specified the name of the new function is taken from the original + function. Otherwise, the ``name`` argument can be used to specify a new + name. + + Note, the names may only be valid Python variable names. + """ + pos_args = [] + key_args = [] + + if isinstance(kwargs, dict): + iter_kwargs = kwargs.items() + else: + iter_kwargs = iter(kwargs) + + # Check that all the argument names are valid + for item in (*args, *iter_kwargs): + if isinstance(item, tuple): + argname = item[0] + key_args.append(item) + else: + argname = item + pos_args.append(item) + + if keyword.iskeyword(argname) or not _ARGNAME_RE.match(argname): + raise SyntaxError(f"invalid argument name: {argname}") + + for item in (varargs, varkwargs): + if item is not None: + if keyword.iskeyword(item) or not _ARGNAME_RE.match(item): + raise SyntaxError(f"invalid argument name: {item}") + + def_signature = [", ".join(pos_args)] + + if varargs: + def_signature.append(f", *{varargs}") + + call_signature = def_signature[:] + + if name is None: + name = func.__name__ + + global_vars = {f"__{name}__func": func} + local_vars = {} + # Make local variables to handle setting the default args + for idx, item in enumerate(key_args): + key, value = item + default_var = f"_kwargs{idx}" + local_vars[default_var] = value + def_signature.append(f", {key}={default_var}") + call_signature.append(f", {key}={key}") + + if varkwargs: + def_signature.append(f", **{varkwargs}") + call_signature.append(f", **{varkwargs}") + + def_signature = "".join(def_signature).lstrip(", ") + call_signature = "".join(call_signature).lstrip(", ") + + mod = find_current_module(2) + frm = inspect.currentframe().f_back + + if mod: + filename = mod.__file__ + modname = mod.__name__ + if filename.endswith(".pyc"): + filename = os.path.splitext(filename)[0] + ".py" + else: + filename = "" + modname = "__main__" + + num_blank_lines = func.__code__.co_firstlineno - 1 + blank_lines = "\n" * num_blank_lines + + # The lstrip is in case there were *no* positional arguments (a rare case) + # in any context this will actually be used... + template = textwrap.dedent( + f"""{blank_lines}\ + def {name}({def_signature}): + return __{name}__func({call_signature}) + """ + ) + + code = compile(template, filename, "single") + + eval(code, global_vars, local_vars) + + new_func = local_vars[name] + new_func.__module__ = modname + new_func.__doc__ = func.__doc__ + + return new_func diff --git a/astropy/utils/collections.py b/astropy/utils/collections.py index b7090d5dc4cf..50c01e7e121e 100644 --- a/astropy/utils/collections.py +++ b/astropy/utils/collections.py @@ -10,6 +10,7 @@ class HomogeneousList(list): types. If an item that is not of the specified type is added to the list, a `TypeError` is raised. """ + def __init__(self, types, values=[]): """ Parameters @@ -21,32 +22,37 @@ def __init__(self, types, values=[]): An initial set of values. """ self._types = types - list.__init__(self, values) + super().__init__() + self.extend(values) def _assert(self, x): if not isinstance(x, self._types): raise TypeError( - "homogeneous list must contain only objects of type '%s'" % - (self._types)) + f"homogeneous list must contain only objects of type '{self._types}'" + ) def __iadd__(self, other): - for x in other: - self._assert(x) - return list.__iadd__(self, other) - - def __setitem__(self, x): - self._assert(x) - return list.__setitem__(self, x) + self.extend(other) + return self + + def __setitem__(self, idx, value): + if isinstance(idx, slice): + value = list(value) + for item in value: + self._assert(item) + else: + self._assert(value) + return super().__setitem__(idx, value) def append(self, x): self._assert(x) - return list.append(self, x) + return super().append(x) def insert(self, i, x): self._assert(x) - return list.insert(self, i, x) + return super().insert(i, x) def extend(self, x): for item in x: self._assert(item) - return list.extend(self, x) + super().append(item) diff --git a/astropy/utils/compat/__init__.py b/astropy/utils/compat/__init__.py index 35db50467fb2..d20a50cb74ef 100644 --- a/astropy/utils/compat/__init__.py +++ b/astropy/utils/compat/__init__.py @@ -1,11 +1,19 @@ # Licensed under a 3-clause BSD style license - see LICENSE.rst """ -This subpackage contains utility modules for compatibility with older/newer -versions of python, as well as including some bugfixes for the stdlib that are -important for Astropy. +The content of this module is solely for internal use of ``astropy`` +and subject to changes without deprecations. Do not use it in external +packages or code. -Note that all public functions in the `astropy.utils.compat.misc` module are -imported here for easier access. """ -from .misc import * +# Importing this module will also install monkey-patches defined in it +from .numpycompat import * + + +def __getattr__(attr): + if attr == "COPY_IF_NEEDED": + from .numpycompat import COPY_IF_NEEDED # Will emit warning + + return COPY_IF_NEEDED + + raise AttributeError(f"module {__name__!r} has no attribute {attr!r}.") diff --git a/astropy/utils/compat/_gzip_27.py b/astropy/utils/compat/_gzip_27.py deleted file mode 100644 index df3d7d4c0f61..000000000000 --- a/astropy/utils/compat/_gzip_27.py +++ /dev/null @@ -1,509 +0,0 @@ -"""Functions that read and write gzipped files. - -The user of the file doesn't have to worry about the compression, -but random access is not allowed.""" - -# based on Andrew Kuchling's minigzip.py distributed with the zlib module - -import struct, sys, time, os -import zlib -import io -import __builtin__ - -__all__ = ["GzipFile","open"] - -FTEXT, FHCRC, FEXTRA, FNAME, FCOMMENT = 1, 2, 4, 8, 16 - -READ, WRITE = 1, 2 - -def write32u(output, value): - # The L format writes the bit pattern correctly whether signed - # or unsigned. - output.write(struct.pack("' - - def _check_closed(self): - """Raises a ValueError if the underlying file object has been closed. - - """ - if self.closed: - raise ValueError('I/O operation on closed file.') - - def _init_write(self, filename): - self.name = filename - self.crc = zlib.crc32("") & 0xffffffffL - self.size = 0 - self.writebuf = [] - self.bufsize = 0 - - def _write_gzip_header(self): - self.fileobj.write('\037\213') # magic header - self.fileobj.write('\010') # compression method - fname = os.path.basename(self.name) - if fname.endswith(".gz"): - fname = fname[:-3] - flags = 0 - if fname: - flags = FNAME - self.fileobj.write(chr(flags)) - mtime = self.mtime - if mtime is None: - mtime = time.time() - write32u(self.fileobj, long(mtime)) - self.fileobj.write('\002') - self.fileobj.write('\377') - if fname: - self.fileobj.write(fname + '\000') - - def _init_read(self): - self.crc = zlib.crc32("") & 0xffffffffL - self.size = 0 - - def _read_gzip_header(self): - magic = self.fileobj.read(2) - if magic != '\037\213': - raise IOError, 'Not a gzipped file' - method = ord( self.fileobj.read(1) ) - if method != 8: - raise IOError, 'Unknown compression method' - flag = ord( self.fileobj.read(1) ) - self.mtime = read32(self.fileobj) - # extraflag = self.fileobj.read(1) - # os = self.fileobj.read(1) - self.fileobj.read(2) - - if flag & FEXTRA: - # Read & discard the extra field, if present - xlen = ord(self.fileobj.read(1)) - xlen = xlen + 256*ord(self.fileobj.read(1)) - self.fileobj.read(xlen) - if flag & FNAME: - # Read and discard a null-terminated string containing the filename - while True: - s = self.fileobj.read(1) - if not s or s=='\000': - break - if flag & FCOMMENT: - # Read and discard a null-terminated string containing a comment - while True: - s = self.fileobj.read(1) - if not s or s=='\000': - break - if flag & FHCRC: - self.fileobj.read(2) # Read & discard the 16-bit header CRC - - def write(self,data): - self._check_closed() - if self.mode != WRITE: - import errno - raise IOError(errno.EBADF, "write() on read-only GzipFile object") - - if self.fileobj is None: - raise ValueError, "write() on closed GzipFile object" - - # Convert data type if called by io.BufferedWriter. - if sys.version_info[:2] != (2, 6) and isinstance(data, memoryview): - data = data.tobytes() - - if len(data) > 0: - self.size = self.size + len(data) - self.crc = zlib.crc32(data, self.crc) & 0xffffffffL - self.fileobj.write( self.compress.compress(data) ) - self.offset += len(data) - - return len(data) - - def read(self, size=-1): - self._check_closed() - if self.mode != READ: - import errno - raise IOError(errno.EBADF, "read() on write-only GzipFile object") - - if self.extrasize <= 0 and self.fileobj is None: - return '' - - readsize = 1024 - if size < 0: # get the whole thing - try: - while True: - self._read(readsize) - readsize = min(self.max_read_chunk, readsize * 2) - except EOFError: - size = self.extrasize - else: # just get some more of it - try: - while size > self.extrasize: - self._read(readsize) - readsize = min(self.max_read_chunk, readsize * 2) - except EOFError: - if size > self.extrasize: - size = self.extrasize - - offset = self.offset - self.extrastart - chunk = self.extrabuf[offset: offset + size] - self.extrasize = self.extrasize - size - - self.offset += size - return chunk - - def _unread(self, buf): - self.extrasize = len(buf) + self.extrasize - self.offset -= len(buf) - - def _read(self, size=1024): - if self.fileobj is None: - raise EOFError, "Reached EOF" - - if self._new_member: - # If the _new_member flag is set, we have to - # jump to the next member, if there is one. - # - # First, check if we're at the end of the file; - # if so, it's time to stop; no more members to read. - pos = self.fileobj.tell() # Save current position - self.fileobj.seek(0, 2) # Seek to end of file - if pos == self.fileobj.tell(): - raise EOFError, "Reached EOF" - else: - self.fileobj.seek( pos ) # Return to original position - - self._init_read() - self._read_gzip_header() - self.decompress = zlib.decompressobj(-zlib.MAX_WBITS) - self._new_member = False - - # Read a chunk of data from the file - buf = self.fileobj.read(size) - - # If the EOF has been reached, flush the decompression object - # and mark this object as finished. - - if buf == "": - uncompress = self.decompress.flush() - self._read_eof() - self._add_read_data( uncompress ) - raise EOFError, 'Reached EOF' - - uncompress = self.decompress.decompress(buf) - self._add_read_data( uncompress ) - - if self.decompress.unused_data != "": - # Ending case: we've come to the end of a member in the file, - # so seek back to the start of the unused data, finish up - # this member, and read a new gzip header. - # (The number of bytes to seek back is the length of the unused - # data, minus 8 because _read_eof() will rewind a further 8 bytes) - self.fileobj.seek( -len(self.decompress.unused_data)+8, 1) - - # Check the CRC and file size, and set the flag so we read - # a new member on the next call - self._read_eof() - self._new_member = True - - def _add_read_data(self, data): - self.crc = zlib.crc32(data, self.crc) & 0xffffffffL - offset = self.offset - self.extrastart - self.extrabuf = self.extrabuf[offset:] + data - self.extrasize = self.extrasize + len(data) - self.extrastart = self.offset - self.size = self.size + len(data) - - def _read_eof(self): - # We've read to the end of the file, so we have to rewind in order - # to reread the 8 bytes containing the CRC and the file size. - # We check the that the computed CRC and size of the - # uncompressed data matches the stored values. Note that the size - # stored is the true file size mod 2**32. - self.fileobj.seek(-8, 1) - crc32 = read32(self.fileobj) - isize = read32(self.fileobj) # may exceed 2GB - if crc32 != self.crc: - raise IOError("CRC check failed %s != %s" % (hex(crc32), - hex(self.crc))) - elif isize != (self.size & 0xffffffffL): - raise IOError, "Incorrect length of data produced" - - # Gzip files can be padded with zeroes and still have archives. - # Consume all zero bytes and set the file position to the first - # non-zero byte. See http://www.gzip.org/#faq8 - c = "\x00" - while c == "\x00": - c = self.fileobj.read(1) - if c: - self.fileobj.seek(-1, 1) - - @property - def closed(self): - return self.fileobj is None - - def close(self): - if self.fileobj is None: - return - if self.mode == WRITE: - self.fileobj.write(self.compress.flush()) - write32u(self.fileobj, self.crc) - # self.size may exceed 2GB, or even 4GB - write32u(self.fileobj, self.size & 0xffffffffL) - self.fileobj = None - elif self.mode == READ: - self.fileobj = None - if self.myfileobj: - self.myfileobj.close() - self.myfileobj = None - - def flush(self,zlib_mode=zlib.Z_SYNC_FLUSH): - self._check_closed() - if self.mode == WRITE: - # Ensure the compressor's buffer is flushed - self.fileobj.write(self.compress.flush(zlib_mode)) - self.fileobj.flush() - - def fileno(self): - """Invoke the underlying file object's fileno() method. - - This will raise AttributeError if the underlying file object - doesn't support fileno(). - """ - return self.fileobj.fileno() - - def rewind(self): - '''Return the uncompressed stream file position indicator to the - beginning of the file''' - if self.mode != READ: - raise IOError("Can't rewind in write mode") - self.fileobj.seek(0) - self._new_member = True - self.extrabuf = "" - self.extrasize = 0 - self.extrastart = 0 - self.offset = 0 - - def readable(self): - return self.mode == READ - - def writable(self): - return self.mode == WRITE - - def seekable(self): - return True - - def seek(self, offset, whence=0): - if whence: - if whence == 1: - offset = self.offset + offset - else: - raise ValueError('Seek from end not supported') - if self.mode == WRITE: - if offset < self.offset: - raise IOError('Negative seek in write mode') - count = offset - self.offset - for i in range(count // 1024): - self.write(1024 * '\0') - self.write((count % 1024) * '\0') - elif self.mode == READ: - if offset < self.offset: - # for negative seek, rewind and do positive seek - self.rewind() - count = offset - self.offset - for i in range(count // 1024): - self.read(1024) - self.read(count % 1024) - - return self.offset - - def readline(self, size=-1): - if size < 0: - # Shortcut common case - newline found in buffer. - offset = self.offset - self.extrastart - i = self.extrabuf.find('\n', offset) + 1 - if i > 0: - self.extrasize -= i - offset - self.offset += i - offset - return self.extrabuf[offset: i] - - size = sys.maxint - readsize = self.min_readsize - else: - readsize = size - bufs = [] - while size != 0: - c = self.read(readsize) - i = c.find('\n') - - # We set i=size to break out of the loop under two - # conditions: 1) there's no newline, and the chunk is - # larger than size, or 2) there is a newline, but the - # resulting line would be longer than 'size'. - if (size <= i) or (i == -1 and len(c) > size): - i = size - 1 - - if i >= 0 or c == '': - bufs.append(c[:i + 1]) # Add portion of last chunk - self._unread(c[i + 1:]) # Push back rest of chunk - break - - # Append chunk to list, decrease 'size', - bufs.append(c) - size = size - len(c) - readsize = min(size, readsize * 2) - if readsize > self.min_readsize: - self.min_readsize = min(readsize, self.min_readsize * 2, 512) - return ''.join(bufs) # Return resulting line - - -def _test(): - # Act like gzip; with -d, act like gunzip. - # The input file is not deleted, however, nor are any other gzip - # options or features supported. - args = sys.argv[1:] - decompress = args and args[0] == "-d" - if decompress: - args = args[1:] - if not args: - args = ["-"] - for arg in args: - if decompress: - if arg == "-": - f = GzipFile(filename="", mode="rb", fileobj=sys.stdin) - g = sys.stdout - else: - if arg[-3:] != ".gz": - print "filename doesn't end in .gz:", repr(arg) - continue - f = open(arg, "rb") - g = __builtin__.open(arg[:-3], "wb") - else: - if arg == "-": - f = sys.stdin - g = GzipFile(filename="", mode="wb", fileobj=sys.stdout) - else: - f = __builtin__.open(arg, "rb") - g = open(arg + ".gz", "wb") - while True: - chunk = f.read(1024) - if not chunk: - break - g.write(chunk) - if g is not sys.stdout: - g.close() - if f is not sys.stdin: - f.close() - -if __name__ == '__main__': - _test() diff --git a/astropy/utils/compat/_gzip_32.py b/astropy/utils/compat/_gzip_32.py deleted file mode 100644 index ba2149ebf970..000000000000 --- a/astropy/utils/compat/_gzip_32.py +++ /dev/null @@ -1,625 +0,0 @@ -"""Functions that read and write gzipped files. - -The user of the file doesn't have to worry about the compression, -but random access is not allowed.""" - -# based on Andrew Kuchling's minigzip.py distributed with the zlib module - -import struct, sys, time, os -import zlib -import builtins -import io - -__all__ = ["GzipFile", "open", "compress", "decompress"] - -FTEXT, FHCRC, FEXTRA, FNAME, FCOMMENT = 1, 2, 4, 8, 16 - -READ, WRITE = 1, 2 - -def U32(i): - """Return i as an unsigned integer, assuming it fits in 32 bits. - If it's >= 2GB when viewed as a 32-bit unsigned int, return a long. - """ - if i < 0: - i += 1 << 32 - return i - -def LOWU32(i): - """Return the low-order 32 bits, as a non-negative int""" - return i & 0xFFFFFFFF - -def write32u(output, value): - # The L format writes the bit pattern correctly whether signed - # or unsigned. - output.write(struct.pack("' - - def _check_closed(self): - """Raises a ValueError if the underlying file object has been closed. - - """ - if self.closed: - raise ValueError('I/O operation on closed file.') - - def _init_write(self, filename): - self.name = filename - self.crc = zlib.crc32(b"") & 0xffffffff - self.size = 0 - self.writebuf = [] - self.bufsize = 0 - - def _write_gzip_header(self): - self.fileobj.write(b'\037\213') # magic header - self.fileobj.write(b'\010') # compression method - try: - # RFC 1952 requires the FNAME field to be Latin-1. Do not - # include filenames that cannot be represented that way. - fname = os.path.basename(self.name) - fname = fname.encode('latin-1') - if fname.endswith(b'.gz'): - fname = fname[:-3] - except UnicodeEncodeError: - fname = b'' - flags = 0 - if fname: - flags = FNAME - self.fileobj.write(chr(flags).encode('latin-1')) - mtime = self.mtime - if mtime is None: - mtime = time.time() - write32u(self.fileobj, int(mtime)) - self.fileobj.write(b'\002') - self.fileobj.write(b'\377') - if fname: - self.fileobj.write(fname + b'\000') - - def _init_read(self): - self.crc = zlib.crc32(b"") & 0xffffffff - self.size = 0 - - def _read_gzip_header(self): - magic = self.fileobj.read(2) - if magic == b'': - raise EOFError("Reached EOF") - - if magic != b'\037\213': - raise IOError('Not a gzipped file') - method = ord( self.fileobj.read(1) ) - if method != 8: - raise IOError('Unknown compression method') - flag = ord( self.fileobj.read(1) ) - self.mtime = read32(self.fileobj) - # extraflag = self.fileobj.read(1) - # os = self.fileobj.read(1) - self.fileobj.read(2) - - if flag & FEXTRA: - # Read & discard the extra field, if present - xlen = ord(self.fileobj.read(1)) - xlen = xlen + 256*ord(self.fileobj.read(1)) - self.fileobj.read(xlen) - if flag & FNAME: - # Read and discard a null-terminated string containing the filename - while True: - s = self.fileobj.read(1) - if not s or s==b'\000': - break - if flag & FCOMMENT: - # Read and discard a null-terminated string containing a comment - while True: - s = self.fileobj.read(1) - if not s or s==b'\000': - break - if flag & FHCRC: - self.fileobj.read(2) # Read & discard the 16-bit header CRC - - unused = self.fileobj.unused() - if unused: - uncompress = self.decompress.decompress(unused) - self._add_read_data(uncompress) - - def write(self,data): - self._check_closed() - if self.mode != WRITE: - import errno - raise IOError(errno.EBADF, "write() on read-only GzipFile object") - - if self.fileobj is None: - raise ValueError("write() on closed GzipFile object") - - # Convert data type if called by io.BufferedWriter. - if isinstance(data, memoryview): - data = data.tobytes() - - if len(data) > 0: - self.size = self.size + len(data) - self.crc = zlib.crc32(data, self.crc) & 0xffffffff - self.fileobj.write( self.compress.compress(data) ) - self.offset += len(data) - - return len(data) - - def read(self, size=-1): - self._check_closed() - if self.mode != READ: - import errno - raise IOError(errno.EBADF, "read() on write-only GzipFile object") - - if self.extrasize <= 0 and self.fileobj is None: - return b'' - - readsize = 1024 - if size < 0: # get the whole thing - try: - while True: - self._read(readsize) - readsize = min(self.max_read_chunk, readsize * 2) - except EOFError: - size = self.extrasize - else: # just get some more of it - try: - while size > self.extrasize: - self._read(readsize) - readsize = min(self.max_read_chunk, readsize * 2) - except EOFError: - if size > self.extrasize: - size = self.extrasize - - offset = self.offset - self.extrastart - chunk = self.extrabuf[offset: offset + size] - self.extrasize = self.extrasize - size - - self.offset += size - return chunk - - def peek(self, n): - if self.mode != READ: - import errno - raise IOError(errno.EBADF, "peek() on write-only GzipFile object") - - # Do not return ridiculously small buffers, for one common idiom - # is to call peek(1) and expect more bytes in return. - if n < 100: - n = 100 - if self.extrasize == 0: - if self.fileobj is None: - return b'' - try: - # 1024 is the same buffering heuristic used in read() - self._read(max(n, 1024)) - except EOFError: - pass - offset = self.offset - self.extrastart - remaining = self.extrasize - assert remaining == len(self.extrabuf) - offset - return self.extrabuf[offset:offset + n] - - def _unread(self, buf): - self.extrasize = len(buf) + self.extrasize - self.offset -= len(buf) - - def _read(self, size=1024): - if self.fileobj is None: - raise EOFError("Reached EOF") - - if self._new_member: - # If the _new_member flag is set, we have to - # jump to the next member, if there is one. - self._init_read() - self._read_gzip_header() - self.decompress = zlib.decompressobj(-zlib.MAX_WBITS) - self._new_member = False - - # Read a chunk of data from the file - buf = self.fileobj.read(size) - - # If the EOF has been reached, flush the decompression object - # and mark this object as finished. - - if buf == b"": - uncompress = self.decompress.flush() - # Prepend the already read bytes to the fileobj to they can be - # seen by _read_eof() - self.fileobj.prepend(self.decompress.unused_data, True) - self._read_eof() - self._add_read_data( uncompress ) - raise EOFError('Reached EOF') - - uncompress = self.decompress.decompress(buf) - self._add_read_data( uncompress ) - - if self.decompress.unused_data != b"": - # Ending case: we've come to the end of a member in the file, - # so seek back to the start of the unused data, finish up - # this member, and read a new gzip header. - # Prepend the already read bytes to the fileobj to they can be - # seen by _read_eof() and _read_gzip_header() - self.fileobj.prepend(self.decompress.unused_data, True) - # Check the CRC and file size, and set the flag so we read - # a new member on the next call - self._read_eof() - self._new_member = True - - def _add_read_data(self, data): - self.crc = zlib.crc32(data, self.crc) & 0xffffffff - offset = self.offset - self.extrastart - self.extrabuf = self.extrabuf[offset:] + data - self.extrasize = self.extrasize + len(data) - self.extrastart = self.offset - self.size = self.size + len(data) - - def _read_eof(self): - # We've read to the end of the file - # We check the that the computed CRC and size of the - # uncompressed data matches the stored values. Note that the size - # stored is the true file size mod 2**32. - crc32 = read32(self.fileobj) - isize = read32(self.fileobj) # may exceed 2GB - if crc32 != self.crc: - raise IOError("CRC check failed %s != %s" % (hex(crc32), - hex(self.crc))) - elif isize != (self.size & 0xffffffff): - raise IOError("Incorrect length of data produced") - - # Gzip files can be padded with zeroes and still have archives. - # Consume all zero bytes and set the file position to the first - # non-zero byte. See http://www.gzip.org/#faq8 - c = b"\x00" - while c == b"\x00": - c = self.fileobj.read(1) - if c: - self.fileobj.prepend(c, True) - - @property - def closed(self): - return self.fileobj is None - - def close(self): - if self.fileobj is None: - return - if self.mode == WRITE: - self.fileobj.write(self.compress.flush()) - write32u(self.fileobj, self.crc) - # self.size may exceed 2GB, or even 4GB - write32u(self.fileobj, self.size & 0xffffffff) - self.fileobj = None - elif self.mode == READ: - self.fileobj = None - if self.myfileobj: - self.myfileobj.close() - self.myfileobj = None - - def flush(self,zlib_mode=zlib.Z_SYNC_FLUSH): - self._check_closed() - if self.mode == WRITE: - # Ensure the compressor's buffer is flushed - self.fileobj.write(self.compress.flush(zlib_mode)) - self.fileobj.flush() - - def fileno(self): - """Invoke the underlying file object's fileno() method. - - This will raise AttributeError if the underlying file object - doesn't support fileno(). - """ - return self.fileobj.fileno() - - def rewind(self): - '''Return the uncompressed stream file position indicator to the - beginning of the file''' - if self.mode != READ: - raise IOError("Can't rewind in write mode") - self.fileobj.seek(0) - self._new_member = True - self.extrabuf = b"" - self.extrasize = 0 - self.extrastart = 0 - self.offset = 0 - - def readable(self): - return self.mode == READ - - def writable(self): - return self.mode == WRITE - - def seekable(self): - return True - - def seek(self, offset, whence=0): - if whence: - if whence == 1: - offset = self.offset + offset - else: - raise ValueError('Seek from end not supported') - if self.mode == WRITE: - if offset < self.offset: - raise IOError('Negative seek in write mode') - count = offset - self.offset - chunk = bytes(1024) - for i in range(count // 1024): - self.write(chunk) - self.write(bytes(count % 1024)) - elif self.mode == READ: - if offset < self.offset: - # for negative seek, rewind and do positive seek - self.rewind() - count = offset - self.offset - for i in range(count // 1024): - self.read(1024) - self.read(count % 1024) - - return self.offset - - def readline(self, size=-1): - if size < 0: - # Shortcut common case - newline found in buffer. - offset = self.offset - self.extrastart - i = self.extrabuf.find(b'\n', offset) + 1 - if i > 0: - self.extrasize -= i - offset - self.offset += i - offset - return self.extrabuf[offset: i] - - size = sys.maxsize - readsize = self.min_readsize - else: - readsize = size - bufs = [] - while size != 0: - c = self.read(readsize) - i = c.find(b'\n') - - # We set i=size to break out of the loop under two - # conditions: 1) there's no newline, and the chunk is - # larger than size, or 2) there is a newline, but the - # resulting line would be longer than 'size'. - if (size <= i) or (i == -1 and len(c) > size): - i = size - 1 - - if i >= 0 or c == b'': - bufs.append(c[:i + 1]) # Add portion of last chunk - self._unread(c[i + 1:]) # Push back rest of chunk - break - - # Append chunk to list, decrease 'size', - bufs.append(c) - size = size - len(c) - readsize = min(size, readsize * 2) - if readsize > self.min_readsize: - self.min_readsize = min(readsize, self.min_readsize * 2, 512) - return b''.join(bufs) # Return resulting line - - -def compress(data, compresslevel=9): - """Compress data in one shot and return the compressed string. - Optional argument is the compression level, in range of 1-9. - """ - buf = io.BytesIO() - with GzipFile(fileobj=buf, mode='wb', compresslevel=compresslevel) as f: - f.write(data) - return buf.getvalue() - -def decompress(data): - """Decompress a gzip compressed string in one shot. - Return the decompressed string. - """ - with GzipFile(fileobj=io.BytesIO(data)) as f: - return f.read() - - -def _test(): - # Act like gzip; with -d, act like gunzip. - # The input file is not deleted, however, nor are any other gzip - # options or features supported. - args = sys.argv[1:] - decompress = args and args[0] == "-d" - if decompress: - args = args[1:] - if not args: - args = ["-"] - for arg in args: - if decompress: - if arg == "-": - f = GzipFile(filename="", mode="rb", fileobj=sys.stdin.buffer) - g = sys.stdout.buffer - else: - if arg[-3:] != ".gz": - print("filename doesn't end in .gz:", repr(arg)) - continue - f = open(arg, "rb") - g = builtins.open(arg[:-3], "wb") - else: - if arg == "-": - f = sys.stdin.buffer - g = GzipFile(filename="", mode="wb", fileobj=sys.stdout.buffer) - else: - f = builtins.open(arg, "rb") - g = open(arg + ".gz", "wb") - while True: - chunk = f.read(1024) - if not chunk: - break - g.write(chunk) - if g is not sys.stdout: - g.close() - if f is not sys.stdin: - f.close() - -if __name__ == '__main__': - _test() diff --git a/astropy/utils/compat/argparse.py b/astropy/utils/compat/argparse.py deleted file mode 100644 index 1973bb0e7304..000000000000 --- a/astropy/utils/compat/argparse.py +++ /dev/null @@ -1,2368 +0,0 @@ -from __future__ import absolute_import - -try: - from argparse import * -except ImportError: - # below here is argparse v1.2.1 from PyPI - # Author: Steven J. Bethard . - - """Command-line parsing library - - This module is an optparse-inspired command-line parsing library that: - - - handles both optional and positional arguments - - produces highly informative usage messages - - supports parsers that dispatch to sub-parsers - - The following is a simple usage example that sums integers from the - command-line and writes the result to a file:: - - parser = argparse.ArgumentParser( - description='sum the integers at the command line') - parser.add_argument( - 'integers', metavar='int', nargs='+', type=int, - help='an integer to be summed') - parser.add_argument( - '--log', default=sys.stdout, type=argparse.FileType('w'), - help='the file where the sum should be written') - args = parser.parse_args() - args.log.write('%s' % sum(args.integers)) - args.log.close() - - The module contains the following public classes: - - - ArgumentParser -- The main entry point for command-line parsing. As the - example above shows, the add_argument() method is used to populate - the parser with actions for optional and positional arguments. Then - the parse_args() method is invoked to convert the args at the - command-line into an object with attributes. - - - ArgumentError -- The exception raised by ArgumentParser objects when - there are errors with the parser's actions. Errors raised while - parsing the command-line are caught by ArgumentParser and emitted - as command-line messages. - - - FileType -- A factory for defining types of files to be created. As the - example above shows, instances of FileType are typically passed as - the type= argument of add_argument() calls. - - - Action -- The base class for parser actions. Typically actions are - selected by passing strings like 'store_true' or 'append_const' to - the action= argument of add_argument(). However, for greater - customization of ArgumentParser actions, subclasses of Action may - be defined and passed as the action= argument. - - - HelpFormatter, RawDescriptionHelpFormatter, RawTextHelpFormatter, - ArgumentDefaultsHelpFormatter -- Formatter classes which - may be passed as the formatter_class= argument to the - ArgumentParser constructor. HelpFormatter is the default, - RawDescriptionHelpFormatter and RawTextHelpFormatter tell the parser - not to change the formatting for help text, and - ArgumentDefaultsHelpFormatter adds information about argument defaults - to the help. - - All other classes in this module are considered implementation details. - (Also note that HelpFormatter and RawDescriptionHelpFormatter are only - considered public as object names -- the API of the formatter objects is - still considered an implementation detail.) - """ - - __version__ = '1.2.1' - __all__ = [ - 'ArgumentParser', - 'ArgumentError', - 'ArgumentTypeError', - 'FileType', - 'HelpFormatter', - 'ArgumentDefaultsHelpFormatter', - 'RawDescriptionHelpFormatter', - 'RawTextHelpFormatter', - 'Namespace', - 'Action', - 'ONE_OR_MORE', - 'OPTIONAL', - 'PARSER', - 'REMAINDER', - 'SUPPRESS', - 'ZERO_OR_MORE', - ] - - - import copy as _copy - import os as _os - import re as _re - import sys as _sys - import textwrap as _textwrap - - from gettext import gettext as _ - - try: - set - except NameError: - # for python < 2.4 compatibility (sets module is there since 2.3): - from sets import Set as set - - try: - basestring - except NameError: - basestring = str - - try: - sorted - except NameError: - # for python < 2.4 compatibility: - def sorted(iterable, reverse=False): - result = list(iterable) - result.sort() - if reverse: - result.reverse() - return result - - - def _callable(obj): - return hasattr(obj, '__call__') or hasattr(obj, '__bases__') - - - SUPPRESS = '==SUPPRESS==' - - OPTIONAL = '?' - ZERO_OR_MORE = '*' - ONE_OR_MORE = '+' - PARSER = 'A...' - REMAINDER = '...' - _UNRECOGNIZED_ARGS_ATTR = '_unrecognized_args' - - # ============================= - # Utility functions and classes - # ============================= - - class _AttributeHolder(object): - """Abstract base class that provides __repr__. - - The __repr__ method returns a string in the format:: - ClassName(attr=name, attr=name, ...) - The attributes are determined either by a class-level attribute, - '_kwarg_names', or by inspecting the instance __dict__. - """ - - def __repr__(self): - type_name = type(self).__name__ - arg_strings = [] - for arg in self._get_args(): - arg_strings.append(repr(arg)) - for name, value in self._get_kwargs(): - arg_strings.append('%s=%r' % (name, value)) - return '%s(%s)' % (type_name, ', '.join(arg_strings)) - - def _get_kwargs(self): - return sorted(self.__dict__.items()) - - def _get_args(self): - return [] - - - def _ensure_value(namespace, name, value): - if getattr(namespace, name, None) is None: - setattr(namespace, name, value) - return getattr(namespace, name) - - - # =============== - # Formatting Help - # =============== - - class HelpFormatter(object): - """Formatter for generating usage messages and argument help strings. - - Only the name of this class is considered a public API. All the methods - provided by the class are considered an implementation detail. - """ - - def __init__(self, - prog, - indent_increment=2, - max_help_position=24, - width=None): - - # default setting for width - if width is None: - try: - width = int(_os.environ['COLUMNS']) - except (KeyError, ValueError): - width = 80 - width -= 2 - - self._prog = prog - self._indent_increment = indent_increment - self._max_help_position = max_help_position - self._width = width - - self._current_indent = 0 - self._level = 0 - self._action_max_length = 0 - - self._root_section = self._Section(self, None) - self._current_section = self._root_section - - self._whitespace_matcher = _re.compile(r'\s+') - self._long_break_matcher = _re.compile(r'\n\n\n+') - - # =============================== - # Section and indentation methods - # =============================== - def _indent(self): - self._current_indent += self._indent_increment - self._level += 1 - - def _dedent(self): - self._current_indent -= self._indent_increment - assert self._current_indent >= 0, 'Indent decreased below 0.' - self._level -= 1 - - class _Section(object): - - def __init__(self, formatter, parent, heading=None): - self.formatter = formatter - self.parent = parent - self.heading = heading - self.items = [] - - def format_help(self): - # format the indented section - if self.parent is not None: - self.formatter._indent() - join = self.formatter._join_parts - for func, args in self.items: - func(*args) - item_help = join([func(*args) for func, args in self.items]) - if self.parent is not None: - self.formatter._dedent() - - # return nothing if the section was empty - if not item_help: - return '' - - # add the heading if the section was non-empty - if self.heading is not SUPPRESS and self.heading is not None: - current_indent = self.formatter._current_indent - heading = '%*s%s:\n' % (current_indent, '', self.heading) - else: - heading = '' - - # join the section-initial newline, the heading and the help - return join(['\n', heading, item_help, '\n']) - - def _add_item(self, func, args): - self._current_section.items.append((func, args)) - - # ======================== - # Message building methods - # ======================== - def start_section(self, heading): - self._indent() - section = self._Section(self, self._current_section, heading) - self._add_item(section.format_help, []) - self._current_section = section - - def end_section(self): - self._current_section = self._current_section.parent - self._dedent() - - def add_text(self, text): - if text is not SUPPRESS and text is not None: - self._add_item(self._format_text, [text]) - - def add_usage(self, usage, actions, groups, prefix=None): - if usage is not SUPPRESS: - args = usage, actions, groups, prefix - self._add_item(self._format_usage, args) - - def add_argument(self, action): - if action.help is not SUPPRESS: - - # find all invocations - get_invocation = self._format_action_invocation - invocations = [get_invocation(action)] - for subaction in self._iter_indented_subactions(action): - invocations.append(get_invocation(subaction)) - - # update the maximum item length - invocation_length = max([len(s) for s in invocations]) - action_length = invocation_length + self._current_indent - self._action_max_length = max(self._action_max_length, - action_length) - - # add the item to the list - self._add_item(self._format_action, [action]) - - def add_arguments(self, actions): - for action in actions: - self.add_argument(action) - - # ======================= - # Help-formatting methods - # ======================= - def format_help(self): - help = self._root_section.format_help() - if help: - help = self._long_break_matcher.sub('\n\n', help) - help = help.strip('\n') + '\n' - return help - - def _join_parts(self, part_strings): - return ''.join([part - for part in part_strings - if part and part is not SUPPRESS]) - - def _format_usage(self, usage, actions, groups, prefix): - if prefix is None: - prefix = _('usage: ') - - # if usage is specified, use that - if usage is not None: - usage = usage % dict(prog=self._prog) - - # if no optionals or positionals are available, usage is just prog - elif usage is None and not actions: - usage = '%(prog)s' % dict(prog=self._prog) - - # if optionals and positionals are available, calculate usage - elif usage is None: - prog = '%(prog)s' % dict(prog=self._prog) - - # split optionals from positionals - optionals = [] - positionals = [] - for action in actions: - if action.option_strings: - optionals.append(action) - else: - positionals.append(action) - - # build full usage string - format = self._format_actions_usage - action_usage = format(optionals + positionals, groups) - usage = ' '.join([s for s in [prog, action_usage] if s]) - - # wrap the usage parts if it's too long - text_width = self._width - self._current_indent - if len(prefix) + len(usage) > text_width: - - # break usage into wrappable parts - part_regexp = r'\(.*?\)+|\[.*?\]+|\S+' - opt_usage = format(optionals, groups) - pos_usage = format(positionals, groups) - opt_parts = _re.findall(part_regexp, opt_usage) - pos_parts = _re.findall(part_regexp, pos_usage) - assert ' '.join(opt_parts) == opt_usage - assert ' '.join(pos_parts) == pos_usage - - # helper for wrapping lines - def get_lines(parts, indent, prefix=None): - lines = [] - line = [] - if prefix is not None: - line_len = len(prefix) - 1 - else: - line_len = len(indent) - 1 - for part in parts: - if line_len + 1 + len(part) > text_width: - lines.append(indent + ' '.join(line)) - line = [] - line_len = len(indent) - 1 - line.append(part) - line_len += len(part) + 1 - if line: - lines.append(indent + ' '.join(line)) - if prefix is not None: - lines[0] = lines[0][len(indent):] - return lines - - # if prog is short, follow it with optionals or positionals - if len(prefix) + len(prog) <= 0.75 * text_width: - indent = ' ' * (len(prefix) + len(prog) + 1) - if opt_parts: - lines = get_lines([prog] + opt_parts, indent, prefix) - lines.extend(get_lines(pos_parts, indent)) - elif pos_parts: - lines = get_lines([prog] + pos_parts, indent, prefix) - else: - lines = [prog] - - # if prog is long, put it on its own line - else: - indent = ' ' * len(prefix) - parts = opt_parts + pos_parts - lines = get_lines(parts, indent) - if len(lines) > 1: - lines = [] - lines.extend(get_lines(opt_parts, indent)) - lines.extend(get_lines(pos_parts, indent)) - lines = [prog] + lines - - # join lines into usage - usage = '\n'.join(lines) - - # prefix with 'usage:' - return '%s%s\n\n' % (prefix, usage) - - def _format_actions_usage(self, actions, groups): - # find group indices and identify actions in groups - group_actions = set() - inserts = {} - for group in groups: - try: - start = actions.index(group._group_actions[0]) - except ValueError: - continue - else: - end = start + len(group._group_actions) - if actions[start:end] == group._group_actions: - for action in group._group_actions: - group_actions.add(action) - if not group.required: - if start in inserts: - inserts[start] += ' [' - else: - inserts[start] = '[' - inserts[end] = ']' - else: - if start in inserts: - inserts[start] += ' (' - else: - inserts[start] = '(' - inserts[end] = ')' - for i in range(start + 1, end): - inserts[i] = '|' - - # collect all actions format strings - parts = [] - for i, action in enumerate(actions): - - # suppressed arguments are marked with None - # remove | separators for suppressed arguments - if action.help is SUPPRESS: - parts.append(None) - if inserts.get(i) == '|': - inserts.pop(i) - elif inserts.get(i + 1) == '|': - inserts.pop(i + 1) - - # produce all arg strings - elif not action.option_strings: - part = self._format_args(action, action.dest) - - # if it's in a group, strip the outer [] - if action in group_actions: - if part[0] == '[' and part[-1] == ']': - part = part[1:-1] - - # add the action string to the list - parts.append(part) - - # produce the first way to invoke the option in brackets - else: - option_string = action.option_strings[0] - - # if the Optional doesn't take a value, format is: - # -s or --long - if action.nargs == 0: - part = '%s' % option_string - - # if the Optional takes a value, format is: - # -s ARGS or --long ARGS - else: - default = action.dest.upper() - args_string = self._format_args(action, default) - part = '%s %s' % (option_string, args_string) - - # make it look optional if it's not required or in a group - if not action.required and action not in group_actions: - part = '[%s]' % part - - # add the action string to the list - parts.append(part) - - # insert things at the necessary indices - for i in sorted(inserts, reverse=True): - parts[i:i] = [inserts[i]] - - # join all the action items with spaces - text = ' '.join([item for item in parts if item is not None]) - - # clean up separators for mutually exclusive groups - open = r'[\[(]' - close = r'[\])]' - text = _re.sub(r'(%s) ' % open, r'\1', text) - text = _re.sub(r' (%s)' % close, r'\1', text) - text = _re.sub(r'%s *%s' % (open, close), r'', text) - text = _re.sub(r'\(([^|]*)\)', r'\1', text) - text = text.strip() - - # return the text - return text - - def _format_text(self, text): - if '%(prog)' in text: - text = text % dict(prog=self._prog) - text_width = self._width - self._current_indent - indent = ' ' * self._current_indent - return self._fill_text(text, text_width, indent) + '\n\n' - - def _format_action(self, action): - # determine the required width and the entry label - help_position = min(self._action_max_length + 2, - self._max_help_position) - help_width = self._width - help_position - action_width = help_position - self._current_indent - 2 - action_header = self._format_action_invocation(action) - - # ho nelp; start on same line and add a final newline - if not action.help: - tup = self._current_indent, '', action_header - action_header = '%*s%s\n' % tup - - # short action name; start on the same line and pad two spaces - elif len(action_header) <= action_width: - tup = self._current_indent, '', action_width, action_header - action_header = '%*s%-*s ' % tup - indent_first = 0 - - # long action name; start on the next line - else: - tup = self._current_indent, '', action_header - action_header = '%*s%s\n' % tup - indent_first = help_position - - # collect the pieces of the action help - parts = [action_header] - - # if there was help for the action, add lines of help text - if action.help: - help_text = self._expand_help(action) - help_lines = self._split_lines(help_text, help_width) - parts.append('%*s%s\n' % (indent_first, '', help_lines[0])) - for line in help_lines[1:]: - parts.append('%*s%s\n' % (help_position, '', line)) - - # or add a newline if the description doesn't end with one - elif not action_header.endswith('\n'): - parts.append('\n') - - # if there are any sub-actions, add their help as well - for subaction in self._iter_indented_subactions(action): - parts.append(self._format_action(subaction)) - - # return a single string - return self._join_parts(parts) - - def _format_action_invocation(self, action): - if not action.option_strings: - metavar, = self._metavar_formatter(action, action.dest)(1) - return metavar - - else: - parts = [] - - # if the Optional doesn't take a value, format is: - # -s, --long - if action.nargs == 0: - parts.extend(action.option_strings) - - # if the Optional takes a value, format is: - # -s ARGS, --long ARGS - else: - default = action.dest.upper() - args_string = self._format_args(action, default) - for option_string in action.option_strings: - parts.append('%s %s' % (option_string, args_string)) - - return ', '.join(parts) - - def _metavar_formatter(self, action, default_metavar): - if action.metavar is not None: - result = action.metavar - elif action.choices is not None: - choice_strs = [str(choice) for choice in action.choices] - result = '{%s}' % ','.join(choice_strs) - else: - result = default_metavar - - def format(tuple_size): - if isinstance(result, tuple): - return result - else: - return (result, ) * tuple_size - return format - - def _format_args(self, action, default_metavar): - get_metavar = self._metavar_formatter(action, default_metavar) - if action.nargs is None: - result = '%s' % get_metavar(1) - elif action.nargs == OPTIONAL: - result = '[%s]' % get_metavar(1) - elif action.nargs == ZERO_OR_MORE: - result = '[%s [%s ...]]' % get_metavar(2) - elif action.nargs == ONE_OR_MORE: - result = '%s [%s ...]' % get_metavar(2) - elif action.nargs == REMAINDER: - result = '...' - elif action.nargs == PARSER: - result = '%s ...' % get_metavar(1) - else: - formats = ['%s' for _ in range(action.nargs)] - result = ' '.join(formats) % get_metavar(action.nargs) - return result - - def _expand_help(self, action): - params = dict(vars(action), prog=self._prog) - for name in list(params): - if params[name] is SUPPRESS: - del params[name] - for name in list(params): - if hasattr(params[name], '__name__'): - params[name] = params[name].__name__ - if params.get('choices') is not None: - choices_str = ', '.join([str(c) for c in params['choices']]) - params['choices'] = choices_str - return self._get_help_string(action) % params - - def _iter_indented_subactions(self, action): - try: - get_subactions = action._get_subactions - except AttributeError: - pass - else: - self._indent() - for subaction in get_subactions(): - yield subaction - self._dedent() - - def _split_lines(self, text, width): - text = self._whitespace_matcher.sub(' ', text).strip() - return _textwrap.wrap(text, width) - - def _fill_text(self, text, width, indent): - text = self._whitespace_matcher.sub(' ', text).strip() - return _textwrap.fill(text, width, initial_indent=indent, - subsequent_indent=indent) - - def _get_help_string(self, action): - return action.help - - - class RawDescriptionHelpFormatter(HelpFormatter): - """Help message formatter which retains any formatting in descriptions. - - Only the name of this class is considered a public API. All the methods - provided by the class are considered an implementation detail. - """ - - def _fill_text(self, text, width, indent): - return ''.join([indent + line for line in text.splitlines(True)]) - - - class RawTextHelpFormatter(RawDescriptionHelpFormatter): - """Help message formatter which retains formatting of all help text. - - Only the name of this class is considered a public API. All the methods - provided by the class are considered an implementation detail. - """ - - def _split_lines(self, text, width): - return text.splitlines() - - - class ArgumentDefaultsHelpFormatter(HelpFormatter): - """Help message formatter which adds default values to argument help. - - Only the name of this class is considered a public API. All the methods - provided by the class are considered an implementation detail. - """ - - def _get_help_string(self, action): - help = action.help - if '%(default)' not in action.help: - if action.default is not SUPPRESS: - defaulting_nargs = [OPTIONAL, ZERO_OR_MORE] - if action.option_strings or action.nargs in defaulting_nargs: - help += ' (default: %(default)s)' - return help - - - # ===================== - # Options and Arguments - # ===================== - - def _get_action_name(argument): - if argument is None: - return None - elif argument.option_strings: - return '/'.join(argument.option_strings) - elif argument.metavar not in (None, SUPPRESS): - return argument.metavar - elif argument.dest not in (None, SUPPRESS): - return argument.dest - else: - return None - - - class ArgumentError(Exception): - """An error from creating or using an argument (optional or positional). - - The string value of this exception is the message, augmented with - information about the argument that caused it. - """ - - def __init__(self, argument, message): - self.argument_name = _get_action_name(argument) - self.message = message - - def __str__(self): - if self.argument_name is None: - format = '%(message)s' - else: - format = 'argument %(argument_name)s: %(message)s' - return format % dict(message=self.message, - argument_name=self.argument_name) - - - class ArgumentTypeError(Exception): - """An error from trying to convert a command line string to a type.""" - pass - - - # ============== - # Action classes - # ============== - - class Action(_AttributeHolder): - """Information about how to convert command line strings to Python objects. - - Action objects are used by an ArgumentParser to represent the information - needed to parse a single argument from one or more strings from the - command line. The keyword arguments to the Action constructor are also - all attributes of Action instances. - - Keyword Arguments: - - - option_strings -- A list of command-line option strings which - should be associated with this action. - - - dest -- The name of the attribute to hold the created object(s) - - - nargs -- The number of command-line arguments that should be - consumed. By default, one argument will be consumed and a single - value will be produced. Other values include: - - N (an integer) consumes N arguments (and produces a list) - - '?' consumes zero or one arguments - - '*' consumes zero or more arguments (and produces a list) - - '+' consumes one or more arguments (and produces a list) - Note that the difference between the default and nargs=1 is that - with the default, a single value will be produced, while with - nargs=1, a list containing a single value will be produced. - - - const -- The value to be produced if the option is specified and the - option uses an action that takes no values. - - - default -- The value to be produced if the option is not specified. - - - type -- The type which the command-line arguments should be converted - to, should be one of 'string', 'int', 'float', 'complex' or a - callable object that accepts a single string argument. If None, - 'string' is assumed. - - - choices -- A container of values that should be allowed. If not None, - after a command-line argument has been converted to the appropriate - type, an exception will be raised if it is not a member of this - collection. - - - required -- True if the action must always be specified at the - command line. This is only meaningful for optional command-line - arguments. - - - help -- The help string describing the argument. - - - metavar -- The name to be used for the option's argument with the - help string. If None, the 'dest' value will be used as the name. - """ - - def __init__(self, - option_strings, - dest, - nargs=None, - const=None, - default=None, - type=None, - choices=None, - required=False, - help=None, - metavar=None): - self.option_strings = option_strings - self.dest = dest - self.nargs = nargs - self.const = const - self.default = default - self.type = type - self.choices = choices - self.required = required - self.help = help - self.metavar = metavar - - def _get_kwargs(self): - names = [ - 'option_strings', - 'dest', - 'nargs', - 'const', - 'default', - 'type', - 'choices', - 'help', - 'metavar', - ] - return [(name, getattr(self, name)) for name in names] - - def __call__(self, parser, namespace, values, option_string=None): - raise NotImplementedError(_('.__call__() not defined')) - - - class _StoreAction(Action): - - def __init__(self, - option_strings, - dest, - nargs=None, - const=None, - default=None, - type=None, - choices=None, - required=False, - help=None, - metavar=None): - if nargs == 0: - raise ValueError('nargs for store actions must be > 0; if you ' - 'have nothing to store, actions such as store ' - 'true or store const may be more appropriate') - if const is not None and nargs != OPTIONAL: - raise ValueError('nargs must be %r to supply const' % OPTIONAL) - super(_StoreAction, self).__init__( - option_strings=option_strings, - dest=dest, - nargs=nargs, - const=const, - default=default, - type=type, - choices=choices, - required=required, - help=help, - metavar=metavar) - - def __call__(self, parser, namespace, values, option_string=None): - setattr(namespace, self.dest, values) - - - class _StoreConstAction(Action): - - def __init__(self, - option_strings, - dest, - const, - default=None, - required=False, - help=None, - metavar=None): - super(_StoreConstAction, self).__init__( - option_strings=option_strings, - dest=dest, - nargs=0, - const=const, - default=default, - required=required, - help=help) - - def __call__(self, parser, namespace, values, option_string=None): - setattr(namespace, self.dest, self.const) - - - class _StoreTrueAction(_StoreConstAction): - - def __init__(self, - option_strings, - dest, - default=False, - required=False, - help=None): - super(_StoreTrueAction, self).__init__( - option_strings=option_strings, - dest=dest, - const=True, - default=default, - required=required, - help=help) - - - class _StoreFalseAction(_StoreConstAction): - - def __init__(self, - option_strings, - dest, - default=True, - required=False, - help=None): - super(_StoreFalseAction, self).__init__( - option_strings=option_strings, - dest=dest, - const=False, - default=default, - required=required, - help=help) - - - class _AppendAction(Action): - - def __init__(self, - option_strings, - dest, - nargs=None, - const=None, - default=None, - type=None, - choices=None, - required=False, - help=None, - metavar=None): - if nargs == 0: - raise ValueError('nargs for append actions must be > 0; if arg ' - 'strings are not supplying the value to append, ' - 'the append const action may be more appropriate') - if const is not None and nargs != OPTIONAL: - raise ValueError('nargs must be %r to supply const' % OPTIONAL) - super(_AppendAction, self).__init__( - option_strings=option_strings, - dest=dest, - nargs=nargs, - const=const, - default=default, - type=type, - choices=choices, - required=required, - help=help, - metavar=metavar) - - def __call__(self, parser, namespace, values, option_string=None): - items = _copy.copy(_ensure_value(namespace, self.dest, [])) - items.append(values) - setattr(namespace, self.dest, items) - - - class _AppendConstAction(Action): - - def __init__(self, - option_strings, - dest, - const, - default=None, - required=False, - help=None, - metavar=None): - super(_AppendConstAction, self).__init__( - option_strings=option_strings, - dest=dest, - nargs=0, - const=const, - default=default, - required=required, - help=help, - metavar=metavar) - - def __call__(self, parser, namespace, values, option_string=None): - items = _copy.copy(_ensure_value(namespace, self.dest, [])) - items.append(self.const) - setattr(namespace, self.dest, items) - - - class _CountAction(Action): - - def __init__(self, - option_strings, - dest, - default=None, - required=False, - help=None): - super(_CountAction, self).__init__( - option_strings=option_strings, - dest=dest, - nargs=0, - default=default, - required=required, - help=help) - - def __call__(self, parser, namespace, values, option_string=None): - new_count = _ensure_value(namespace, self.dest, 0) + 1 - setattr(namespace, self.dest, new_count) - - - class _HelpAction(Action): - - def __init__(self, - option_strings, - dest=SUPPRESS, - default=SUPPRESS, - help=None): - super(_HelpAction, self).__init__( - option_strings=option_strings, - dest=dest, - default=default, - nargs=0, - help=help) - - def __call__(self, parser, namespace, values, option_string=None): - parser.print_help() - parser.exit() - - - class _VersionAction(Action): - - def __init__(self, - option_strings, - version=None, - dest=SUPPRESS, - default=SUPPRESS, - help="show program's version number and exit"): - super(_VersionAction, self).__init__( - option_strings=option_strings, - dest=dest, - default=default, - nargs=0, - help=help) - self.version = version - - def __call__(self, parser, namespace, values, option_string=None): - version = self.version - if version is None: - version = parser.version - formatter = parser._get_formatter() - formatter.add_text(version) - parser.exit(message=formatter.format_help()) - - - class _SubParsersAction(Action): - - class _ChoicesPseudoAction(Action): - - def __init__(self, name, help): - sup = super(_SubParsersAction._ChoicesPseudoAction, self) - sup.__init__(option_strings=[], dest=name, help=help) - - def __init__(self, - option_strings, - prog, - parser_class, - dest=SUPPRESS, - help=None, - metavar=None): - - self._prog_prefix = prog - self._parser_class = parser_class - self._name_parser_map = {} - self._choices_actions = [] - - super(_SubParsersAction, self).__init__( - option_strings=option_strings, - dest=dest, - nargs=PARSER, - choices=self._name_parser_map, - help=help, - metavar=metavar) - - def add_parser(self, name, **kwargs): - # set prog from the existing prefix - if kwargs.get('prog') is None: - kwargs['prog'] = '%s %s' % (self._prog_prefix, name) - - # create a pseudo-action to hold the choice help - if 'help' in kwargs: - help = kwargs.pop('help') - choice_action = self._ChoicesPseudoAction(name, help) - self._choices_actions.append(choice_action) - - # create the parser and add it to the map - parser = self._parser_class(**kwargs) - self._name_parser_map[name] = parser - return parser - - def _get_subactions(self): - return self._choices_actions - - def __call__(self, parser, namespace, values, option_string=None): - parser_name = values[0] - arg_strings = values[1:] - - # set the parser name if requested - if self.dest is not SUPPRESS: - setattr(namespace, self.dest, parser_name) - - # select the parser - try: - parser = self._name_parser_map[parser_name] - except KeyError: - tup = parser_name, ', '.join(self._name_parser_map) - msg = _('unknown parser %r (choices: %s)' % tup) - raise ArgumentError(self, msg) - - # parse all the remaining options into the namespace - # store any unrecognized options on the object, so that the top - # level parser can decide what to do with them - namespace, arg_strings = parser.parse_known_args(arg_strings, namespace) - if arg_strings: - vars(namespace).setdefault(_UNRECOGNIZED_ARGS_ATTR, []) - getattr(namespace, _UNRECOGNIZED_ARGS_ATTR).extend(arg_strings) - - - # ============== - # Type classes - # ============== - - class FileType(object): - """Factory for creating file object types - - Instances of FileType are typically passed as type= arguments to the - ArgumentParser add_argument() method. - - Keyword Arguments: - - mode -- A string indicating how the file is to be opened. Accepts the - same values as the builtin open() function. - - bufsize -- The file's desired buffer size. Accepts the same values as - the builtin open() function. - """ - - def __init__(self, mode='r', bufsize=None): - self._mode = mode - self._bufsize = bufsize - - def __call__(self, string): - # the special argument "-" means sys.std{in,out} - if string == '-': - if 'r' in self._mode: - return _sys.stdin - elif 'w' in self._mode: - return _sys.stdout - else: - msg = _('argument "-" with mode %r' % self._mode) - raise ValueError(msg) - - # all other arguments are used as file names - if self._bufsize: - return open(string, self._mode, self._bufsize) - else: - return open(string, self._mode) - - def __repr__(self): - args = [self._mode, self._bufsize] - args_str = ', '.join([repr(arg) for arg in args if arg is not None]) - return '%s(%s)' % (type(self).__name__, args_str) - - # =========================== - # Optional and Positional Parsing - # =========================== - - class Namespace(_AttributeHolder): - """Simple object for storing attributes. - - Implements equality by attribute names and values, and provides a simple - string representation. - """ - - def __init__(self, **kwargs): - for name in kwargs: - setattr(self, name, kwargs[name]) - - __hash__ = None - - def __eq__(self, other): - return vars(self) == vars(other) - - def __ne__(self, other): - return not (self == other) - - def __contains__(self, key): - return key in self.__dict__ - - - class _ActionsContainer(object): - - def __init__(self, - description, - prefix_chars, - argument_default, - conflict_handler): - super(_ActionsContainer, self).__init__() - - self.description = description - self.argument_default = argument_default - self.prefix_chars = prefix_chars - self.conflict_handler = conflict_handler - - # set up registries - self._registries = {} - - # register actions - self.register('action', None, _StoreAction) - self.register('action', 'store', _StoreAction) - self.register('action', 'store_const', _StoreConstAction) - self.register('action', 'store_true', _StoreTrueAction) - self.register('action', 'store_false', _StoreFalseAction) - self.register('action', 'append', _AppendAction) - self.register('action', 'append_const', _AppendConstAction) - self.register('action', 'count', _CountAction) - self.register('action', 'help', _HelpAction) - self.register('action', 'version', _VersionAction) - self.register('action', 'parsers', _SubParsersAction) - - # raise an exception if the conflict handler is invalid - self._get_handler() - - # action storage - self._actions = [] - self._option_string_actions = {} - - # groups - self._action_groups = [] - self._mutually_exclusive_groups = [] - - # defaults storage - self._defaults = {} - - # determines whether an "option" looks like a negative number - self._negative_number_matcher = _re.compile(r'^-\d+$|^-\d*\.\d+$') - - # whether or not there are any optionals that look like negative - # numbers -- uses a list so it can be shared and edited - self._has_negative_number_optionals = [] - - # ==================== - # Registration methods - # ==================== - def register(self, registry_name, value, object): - registry = self._registries.setdefault(registry_name, {}) - registry[value] = object - - def _registry_get(self, registry_name, value, default=None): - return self._registries[registry_name].get(value, default) - - # ================================== - # Namespace default accessor methods - # ================================== - def set_defaults(self, **kwargs): - self._defaults.update(kwargs) - - # if these defaults match any existing arguments, replace - # the previous default on the object with the new one - for action in self._actions: - if action.dest in kwargs: - action.default = kwargs[action.dest] - - def get_default(self, dest): - for action in self._actions: - if action.dest == dest and action.default is not None: - return action.default - return self._defaults.get(dest, None) - - - # ======================= - # Adding argument actions - # ======================= - def add_argument(self, *args, **kwargs): - """ - add_argument(dest, ..., name=value, ...) - add_argument(option_string, option_string, ..., name=value, ...) - """ - - # if no positional args are supplied or only one is supplied and - # it doesn't look like an option string, parse a positional - # argument - chars = self.prefix_chars - if not args or len(args) == 1 and args[0][0] not in chars: - if args and 'dest' in kwargs: - raise ValueError('dest supplied twice for positional argument') - kwargs = self._get_positional_kwargs(*args, **kwargs) - - # otherwise, we're adding an optional argument - else: - kwargs = self._get_optional_kwargs(*args, **kwargs) - - # if no default was supplied, use the parser-level default - if 'default' not in kwargs: - dest = kwargs['dest'] - if dest in self._defaults: - kwargs['default'] = self._defaults[dest] - elif self.argument_default is not None: - kwargs['default'] = self.argument_default - - # create the action object, and add it to the parser - action_class = self._pop_action_class(kwargs) - if not _callable(action_class): - raise ValueError('unknown action "%s"' % action_class) - action = action_class(**kwargs) - - # raise an error if the action type is not callable - type_func = self._registry_get('type', action.type, action.type) - if not _callable(type_func): - raise ValueError('%r is not callable' % type_func) - - return self._add_action(action) - - def add_argument_group(self, *args, **kwargs): - group = _ArgumentGroup(self, *args, **kwargs) - self._action_groups.append(group) - return group - - def add_mutually_exclusive_group(self, **kwargs): - group = _MutuallyExclusiveGroup(self, **kwargs) - self._mutually_exclusive_groups.append(group) - return group - - def _add_action(self, action): - # resolve any conflicts - self._check_conflict(action) - - # add to actions list - self._actions.append(action) - action.container = self - - # index the action by any option strings it has - for option_string in action.option_strings: - self._option_string_actions[option_string] = action - - # set the flag if any option strings look like negative numbers - for option_string in action.option_strings: - if self._negative_number_matcher.match(option_string): - if not self._has_negative_number_optionals: - self._has_negative_number_optionals.append(True) - - # return the created action - return action - - def _remove_action(self, action): - self._actions.remove(action) - - def _add_container_actions(self, container): - # collect groups by titles - title_group_map = {} - for group in self._action_groups: - if group.title in title_group_map: - msg = _('cannot merge actions - two groups are named %r') - raise ValueError(msg % (group.title)) - title_group_map[group.title] = group - - # map each action to its group - group_map = {} - for group in container._action_groups: - - # if a group with the title exists, use that, otherwise - # create a new group matching the container's group - if group.title not in title_group_map: - title_group_map[group.title] = self.add_argument_group( - title=group.title, - description=group.description, - conflict_handler=group.conflict_handler) - - # map the actions to their new group - for action in group._group_actions: - group_map[action] = title_group_map[group.title] - - # add container's mutually exclusive groups - # NOTE: if add_mutually_exclusive_group ever gains title= and - # description= then this code will need to be expanded as above - for group in container._mutually_exclusive_groups: - mutex_group = self.add_mutually_exclusive_group( - required=group.required) - - # map the actions to their new mutex group - for action in group._group_actions: - group_map[action] = mutex_group - - # add all actions to this container or their group - for action in container._actions: - group_map.get(action, self)._add_action(action) - - def _get_positional_kwargs(self, dest, **kwargs): - # make sure required is not specified - if 'required' in kwargs: - msg = _("'required' is an invalid argument for positionals") - raise TypeError(msg) - - # mark positional arguments as required if at least one is - # always required - if kwargs.get('nargs') not in [OPTIONAL, ZERO_OR_MORE]: - kwargs['required'] = True - if kwargs.get('nargs') == ZERO_OR_MORE and 'default' not in kwargs: - kwargs['required'] = True - - # return the keyword arguments with no option strings - return dict(kwargs, dest=dest, option_strings=[]) - - def _get_optional_kwargs(self, *args, **kwargs): - # determine short and long option strings - option_strings = [] - long_option_strings = [] - for option_string in args: - # error on strings that don't start with an appropriate prefix - if not option_string[0] in self.prefix_chars: - msg = _('invalid option string %r: ' - 'must start with a character %r') - tup = option_string, self.prefix_chars - raise ValueError(msg % tup) - - # strings starting with two prefix characters are long options - option_strings.append(option_string) - if option_string[0] in self.prefix_chars: - if len(option_string) > 1: - if option_string[1] in self.prefix_chars: - long_option_strings.append(option_string) - - # infer destination, '--foo-bar' -> 'foo_bar' and '-x' -> 'x' - dest = kwargs.pop('dest', None) - if dest is None: - if long_option_strings: - dest_option_string = long_option_strings[0] - else: - dest_option_string = option_strings[0] - dest = dest_option_string.lstrip(self.prefix_chars) - if not dest: - msg = _('dest= is required for options like %r') - raise ValueError(msg % option_string) - dest = dest.replace('-', '_') - - # return the updated keyword arguments - return dict(kwargs, dest=dest, option_strings=option_strings) - - def _pop_action_class(self, kwargs, default=None): - action = kwargs.pop('action', default) - return self._registry_get('action', action, action) - - def _get_handler(self): - # determine function from conflict handler string - handler_func_name = '_handle_conflict_%s' % self.conflict_handler - try: - return getattr(self, handler_func_name) - except AttributeError: - msg = _('invalid conflict_resolution value: %r') - raise ValueError(msg % self.conflict_handler) - - def _check_conflict(self, action): - - # find all options that conflict with this option - confl_optionals = [] - for option_string in action.option_strings: - if option_string in self._option_string_actions: - confl_optional = self._option_string_actions[option_string] - confl_optionals.append((option_string, confl_optional)) - - # resolve any conflicts - if confl_optionals: - conflict_handler = self._get_handler() - conflict_handler(action, confl_optionals) - - def _handle_conflict_error(self, action, conflicting_actions): - message = _('conflicting option string(s): %s') - conflict_string = ', '.join([option_string - for option_string, action - in conflicting_actions]) - raise ArgumentError(action, message % conflict_string) - - def _handle_conflict_resolve(self, action, conflicting_actions): - - # remove all conflicting options - for option_string, action in conflicting_actions: - - # remove the conflicting option - action.option_strings.remove(option_string) - self._option_string_actions.pop(option_string, None) - - # if the option now has no option string, remove it from the - # container holding it - if not action.option_strings: - action.container._remove_action(action) - - - class _ArgumentGroup(_ActionsContainer): - - def __init__(self, container, title=None, description=None, **kwargs): - # add any missing keyword arguments by checking the container - update = kwargs.setdefault - update('conflict_handler', container.conflict_handler) - update('prefix_chars', container.prefix_chars) - update('argument_default', container.argument_default) - super_init = super(_ArgumentGroup, self).__init__ - super_init(description=description, **kwargs) - - # group attributes - self.title = title - self._group_actions = [] - - # share most attributes with the container - self._registries = container._registries - self._actions = container._actions - self._option_string_actions = container._option_string_actions - self._defaults = container._defaults - self._has_negative_number_optionals = \ - container._has_negative_number_optionals - - def _add_action(self, action): - action = super(_ArgumentGroup, self)._add_action(action) - self._group_actions.append(action) - return action - - def _remove_action(self, action): - super(_ArgumentGroup, self)._remove_action(action) - self._group_actions.remove(action) - - - class _MutuallyExclusiveGroup(_ArgumentGroup): - - def __init__(self, container, required=False): - super(_MutuallyExclusiveGroup, self).__init__(container) - self.required = required - self._container = container - - def _add_action(self, action): - if action.required: - msg = _('mutually exclusive arguments must be optional') - raise ValueError(msg) - action = self._container._add_action(action) - self._group_actions.append(action) - return action - - def _remove_action(self, action): - self._container._remove_action(action) - self._group_actions.remove(action) - - - class ArgumentParser(_AttributeHolder, _ActionsContainer): - """Object for parsing command line strings into Python objects. - - Keyword Arguments: - - prog -- The name of the program (default: sys.argv[0]) - - usage -- A usage message (default: auto-generated from arguments) - - description -- A description of what the program does - - epilog -- Text following the argument descriptions - - parents -- Parsers whose arguments should be copied into this one - - formatter_class -- HelpFormatter class for printing help messages - - prefix_chars -- Characters that prefix optional arguments - - fromfile_prefix_chars -- Characters that prefix files containing - additional arguments - - argument_default -- The default value for all arguments - - conflict_handler -- String indicating how to handle conflicts - - add_help -- Add a -h/-help option - """ - - def __init__(self, - prog=None, - usage=None, - description=None, - epilog=None, - version=None, - parents=[], - formatter_class=HelpFormatter, - prefix_chars='-', - fromfile_prefix_chars=None, - argument_default=None, - conflict_handler='error', - add_help=True): - - if version is not None: - import warnings - warnings.warn( - """The "version" argument to ArgumentParser is deprecated. """ - """Please use """ - """"add_argument(..., action='version', version="N", ...)" """ - """instead""", DeprecationWarning) - - superinit = super(ArgumentParser, self).__init__ - superinit(description=description, - prefix_chars=prefix_chars, - argument_default=argument_default, - conflict_handler=conflict_handler) - - # default setting for prog - if prog is None: - prog = _os.path.basename(_sys.argv[0]) - - self.prog = prog - self.usage = usage - self.epilog = epilog - self.version = version - self.formatter_class = formatter_class - self.fromfile_prefix_chars = fromfile_prefix_chars - self.add_help = add_help - - add_group = self.add_argument_group - self._positionals = add_group(_('positional arguments')) - self._optionals = add_group(_('optional arguments')) - self._subparsers = None - - # register types - def identity(string): - return string - self.register('type', None, identity) - - # add help and version arguments if necessary - # (using explicit default to override global argument_default) - if '-' in prefix_chars: - default_prefix = '-' - else: - default_prefix = prefix_chars[0] - if self.add_help: - self.add_argument( - default_prefix+'h', default_prefix*2+'help', - action='help', default=SUPPRESS, - help=_('show this help message and exit')) - if self.version: - self.add_argument( - default_prefix+'v', default_prefix*2+'version', - action='version', default=SUPPRESS, - version=self.version, - help=_("show program's version number and exit")) - - # add parent arguments and defaults - for parent in parents: - self._add_container_actions(parent) - try: - defaults = parent._defaults - except AttributeError: - pass - else: - self._defaults.update(defaults) - - # ======================= - # Pretty __repr__ methods - # ======================= - def _get_kwargs(self): - names = [ - 'prog', - 'usage', - 'description', - 'version', - 'formatter_class', - 'conflict_handler', - 'add_help', - ] - return [(name, getattr(self, name)) for name in names] - - # ================================== - # Optional/Positional adding methods - # ================================== - def add_subparsers(self, **kwargs): - if self._subparsers is not None: - self.error(_('cannot have multiple subparser arguments')) - - # add the parser class to the arguments if it's not present - kwargs.setdefault('parser_class', type(self)) - - if 'title' in kwargs or 'description' in kwargs: - title = _(kwargs.pop('title', 'subcommands')) - description = _(kwargs.pop('description', None)) - self._subparsers = self.add_argument_group(title, description) - else: - self._subparsers = self._positionals - - # prog defaults to the usage message of this parser, skipping - # optional arguments and with no "usage:" prefix - if kwargs.get('prog') is None: - formatter = self._get_formatter() - positionals = self._get_positional_actions() - groups = self._mutually_exclusive_groups - formatter.add_usage(self.usage, positionals, groups, '') - kwargs['prog'] = formatter.format_help().strip() - - # create the parsers action and add it to the positionals list - parsers_class = self._pop_action_class(kwargs, 'parsers') - action = parsers_class(option_strings=[], **kwargs) - self._subparsers._add_action(action) - - # return the created parsers action - return action - - def _add_action(self, action): - if action.option_strings: - self._optionals._add_action(action) - else: - self._positionals._add_action(action) - return action - - def _get_optional_actions(self): - return [action - for action in self._actions - if action.option_strings] - - def _get_positional_actions(self): - return [action - for action in self._actions - if not action.option_strings] - - # ===================================== - # Command line argument parsing methods - # ===================================== - def parse_args(self, args=None, namespace=None): - args, argv = self.parse_known_args(args, namespace) - if argv: - msg = _('unrecognized arguments: %s') - self.error(msg % ' '.join(argv)) - return args - - def parse_known_args(self, args=None, namespace=None): - # args default to the system args - if args is None: - args = _sys.argv[1:] - - # default Namespace built from parser defaults - if namespace is None: - namespace = Namespace() - - # add any action defaults that aren't present - for action in self._actions: - if action.dest is not SUPPRESS: - if not hasattr(namespace, action.dest): - if action.default is not SUPPRESS: - default = action.default - if isinstance(action.default, basestring): - default = self._get_value(action, default) - setattr(namespace, action.dest, default) - - # add any parser defaults that aren't present - for dest in self._defaults: - if not hasattr(namespace, dest): - setattr(namespace, dest, self._defaults[dest]) - - # parse the arguments and exit if there are any errors - try: - namespace, args = self._parse_known_args(args, namespace) - if hasattr(namespace, _UNRECOGNIZED_ARGS_ATTR): - args.extend(getattr(namespace, _UNRECOGNIZED_ARGS_ATTR)) - delattr(namespace, _UNRECOGNIZED_ARGS_ATTR) - return namespace, args - except ArgumentError: - err = _sys.exc_info()[1] - self.error(str(err)) - - def _parse_known_args(self, arg_strings, namespace): - # replace arg strings that are file references - if self.fromfile_prefix_chars is not None: - arg_strings = self._read_args_from_files(arg_strings) - - # map all mutually exclusive arguments to the other arguments - # they can't occur with - action_conflicts = {} - for mutex_group in self._mutually_exclusive_groups: - group_actions = mutex_group._group_actions - for i, mutex_action in enumerate(mutex_group._group_actions): - conflicts = action_conflicts.setdefault(mutex_action, []) - conflicts.extend(group_actions[:i]) - conflicts.extend(group_actions[i + 1:]) - - # find all option indices, and determine the arg_string_pattern - # which has an 'O' if there is an option at an index, - # an 'A' if there is an argument, or a '-' if there is a '--' - option_string_indices = {} - arg_string_pattern_parts = [] - arg_strings_iter = iter(arg_strings) - for i, arg_string in enumerate(arg_strings_iter): - - # all args after -- are non-options - if arg_string == '--': - arg_string_pattern_parts.append('-') - for arg_string in arg_strings_iter: - arg_string_pattern_parts.append('A') - - # otherwise, add the arg to the arg strings - # and note the index if it was an option - else: - option_tuple = self._parse_optional(arg_string) - if option_tuple is None: - pattern = 'A' - else: - option_string_indices[i] = option_tuple - pattern = 'O' - arg_string_pattern_parts.append(pattern) - - # join the pieces together to form the pattern - arg_strings_pattern = ''.join(arg_string_pattern_parts) - - # converts arg strings to the appropriate and then takes the action - seen_actions = set() - seen_non_default_actions = set() - - def take_action(action, argument_strings, option_string=None): - seen_actions.add(action) - argument_values = self._get_values(action, argument_strings) - - # error if this argument is not allowed with other previously - # seen arguments, assuming that actions that use the default - # value don't really count as "present" - if argument_values is not action.default: - seen_non_default_actions.add(action) - for conflict_action in action_conflicts.get(action, []): - if conflict_action in seen_non_default_actions: - msg = _('not allowed with argument %s') - action_name = _get_action_name(conflict_action) - raise ArgumentError(action, msg % action_name) - - # take the action if we didn't receive a SUPPRESS value - # (e.g. from a default) - if argument_values is not SUPPRESS: - action(self, namespace, argument_values, option_string) - - # function to convert arg_strings into an optional action - def consume_optional(start_index): - - # get the optional identified at this index - option_tuple = option_string_indices[start_index] - action, option_string, explicit_arg = option_tuple - - # identify additional optionals in the same arg string - # (e.g. -xyz is the same as -x -y -z if no args are required) - match_argument = self._match_argument - action_tuples = [] - while True: - - # if we found no optional action, skip it - if action is None: - extras.append(arg_strings[start_index]) - return start_index + 1 - - # if there is an explicit argument, try to match the - # optional's string arguments to only this - if explicit_arg is not None: - arg_count = match_argument(action, 'A') - - # if the action is a single-dash option and takes no - # arguments, try to parse more single-dash options out - # of the tail of the option string - chars = self.prefix_chars - if arg_count == 0 and option_string[1] not in chars: - action_tuples.append((action, [], option_string)) - char = option_string[0] - option_string = char + explicit_arg[0] - new_explicit_arg = explicit_arg[1:] or None - optionals_map = self._option_string_actions - if option_string in optionals_map: - action = optionals_map[option_string] - explicit_arg = new_explicit_arg - else: - msg = _('ignored explicit argument %r') - raise ArgumentError(action, msg % explicit_arg) - - # if the action expect exactly one argument, we've - # successfully matched the option; exit the loop - elif arg_count == 1: - stop = start_index + 1 - args = [explicit_arg] - action_tuples.append((action, args, option_string)) - break - - # error if a double-dash option did not use the - # explicit argument - else: - msg = _('ignored explicit argument %r') - raise ArgumentError(action, msg % explicit_arg) - - # if there is no explicit argument, try to match the - # optional's string arguments with the following strings - # if successful, exit the loop - else: - start = start_index + 1 - selected_patterns = arg_strings_pattern[start:] - arg_count = match_argument(action, selected_patterns) - stop = start + arg_count - args = arg_strings[start:stop] - action_tuples.append((action, args, option_string)) - break - - # add the Optional to the list and return the index at which - # the Optional's string args stopped - assert action_tuples - for action, args, option_string in action_tuples: - take_action(action, args, option_string) - return stop - - # the list of Positionals left to be parsed; this is modified - # by consume_positionals() - positionals = self._get_positional_actions() - - # function to convert arg_strings into positional actions - def consume_positionals(start_index): - # match as many Positionals as possible - match_partial = self._match_arguments_partial - selected_pattern = arg_strings_pattern[start_index:] - arg_counts = match_partial(positionals, selected_pattern) - - # slice off the appropriate arg strings for each Positional - # and add the Positional and its args to the list - for action, arg_count in zip(positionals, arg_counts): - args = arg_strings[start_index: start_index + arg_count] - start_index += arg_count - take_action(action, args) - - # slice off the Positionals that we just parsed and return the - # index at which the Positionals' string args stopped - positionals[:] = positionals[len(arg_counts):] - return start_index - - # consume Positionals and Optionals alternately, until we have - # passed the last option string - extras = [] - start_index = 0 - if option_string_indices: - max_option_string_index = max(option_string_indices) - else: - max_option_string_index = -1 - while start_index <= max_option_string_index: - - # consume any Positionals preceding the next option - next_option_string_index = min([ - index - for index in option_string_indices - if index >= start_index]) - if start_index != next_option_string_index: - positionals_end_index = consume_positionals(start_index) - - # only try to parse the next optional if we didn't consume - # the option string during the positionals parsing - if positionals_end_index > start_index: - start_index = positionals_end_index - continue - else: - start_index = positionals_end_index - - # if we consumed all the positionals we could and we're not - # at the index of an option string, there were extra arguments - if start_index not in option_string_indices: - strings = arg_strings[start_index:next_option_string_index] - extras.extend(strings) - start_index = next_option_string_index - - # consume the next optional and any arguments for it - start_index = consume_optional(start_index) - - # consume any positionals following the last Optional - stop_index = consume_positionals(start_index) - - # if we didn't consume all the argument strings, there were extras - extras.extend(arg_strings[stop_index:]) - - # if we didn't use all the Positional objects, there were too few - # arg strings supplied. - if positionals: - self.error(_('too few arguments')) - - # make sure all required actions were present - for action in self._actions: - if action.required: - if action not in seen_actions: - name = _get_action_name(action) - self.error(_('argument %s is required') % name) - - # make sure all required groups had one option present - for group in self._mutually_exclusive_groups: - if group.required: - for action in group._group_actions: - if action in seen_non_default_actions: - break - - # if no actions were used, report the error - else: - names = [_get_action_name(action) - for action in group._group_actions - if action.help is not SUPPRESS] - msg = _('one of the arguments %s is required') - self.error(msg % ' '.join(names)) - - # return the updated namespace and the extra arguments - return namespace, extras - - def _read_args_from_files(self, arg_strings): - # expand arguments referencing files - new_arg_strings = [] - for arg_string in arg_strings: - - # for regular arguments, just add them back into the list - if arg_string[0] not in self.fromfile_prefix_chars: - new_arg_strings.append(arg_string) - - # replace arguments referencing files with the file content - else: - try: - args_file = open(arg_string[1:]) - try: - arg_strings = [] - for arg_line in args_file.read().splitlines(): - for arg in self.convert_arg_line_to_args(arg_line): - arg_strings.append(arg) - arg_strings = self._read_args_from_files(arg_strings) - new_arg_strings.extend(arg_strings) - finally: - args_file.close() - except IOError: - err = _sys.exc_info()[1] - self.error(str(err)) - - # return the modified argument list - return new_arg_strings - - def convert_arg_line_to_args(self, arg_line): - return [arg_line] - - def _match_argument(self, action, arg_strings_pattern): - # match the pattern for this action to the arg strings - nargs_pattern = self._get_nargs_pattern(action) - match = _re.match(nargs_pattern, arg_strings_pattern) - - # raise an exception if we weren't able to find a match - if match is None: - nargs_errors = { - None: _('expected one argument'), - OPTIONAL: _('expected at most one argument'), - ONE_OR_MORE: _('expected at least one argument'), - } - default = _('expected %s argument(s)') % action.nargs - msg = nargs_errors.get(action.nargs, default) - raise ArgumentError(action, msg) - - # return the number of arguments matched - return len(match.group(1)) - - def _match_arguments_partial(self, actions, arg_strings_pattern): - # progressively shorten the actions list by slicing off the - # final actions until we find a match - result = [] - for i in range(len(actions), 0, -1): - actions_slice = actions[:i] - pattern = ''.join([self._get_nargs_pattern(action) - for action in actions_slice]) - match = _re.match(pattern, arg_strings_pattern) - if match is not None: - result.extend([len(string) for string in match.groups()]) - break - - # return the list of arg string counts - return result - - def _parse_optional(self, arg_string): - # if it's an empty string, it was meant to be a positional - if not arg_string: - return None - - # if it doesn't start with a prefix, it was meant to be positional - if not arg_string[0] in self.prefix_chars: - return None - - # if the option string is present in the parser, return the action - if arg_string in self._option_string_actions: - action = self._option_string_actions[arg_string] - return action, arg_string, None - - # if it's just a single character, it was meant to be positional - if len(arg_string) == 1: - return None - - # if the option string before the "=" is present, return the action - if '=' in arg_string: - option_string, explicit_arg = arg_string.split('=', 1) - if option_string in self._option_string_actions: - action = self._option_string_actions[option_string] - return action, option_string, explicit_arg - - # search through all possible prefixes of the option string - # and all actions in the parser for possible interpretations - option_tuples = self._get_option_tuples(arg_string) - - # if multiple actions match, the option string was ambiguous - if len(option_tuples) > 1: - options = ', '.join([option_string - for action, option_string, explicit_arg in option_tuples]) - tup = arg_string, options - self.error(_('ambiguous option: %s could match %s') % tup) - - # if exactly one action matched, this segmentation is good, - # so return the parsed action - elif len(option_tuples) == 1: - option_tuple, = option_tuples - return option_tuple - - # if it was not found as an option, but it looks like a negative - # number, it was meant to be positional - # unless there are negative-number-like options - if self._negative_number_matcher.match(arg_string): - if not self._has_negative_number_optionals: - return None - - # if it contains a space, it was meant to be a positional - if ' ' in arg_string: - return None - - # it was meant to be an optional but there is no such option - # in this parser (though it might be a valid option in a subparser) - return None, arg_string, None - - def _get_option_tuples(self, option_string): - result = [] - - # option strings starting with two prefix characters are only - # split at the '=' - chars = self.prefix_chars - if option_string[0] in chars and option_string[1] in chars: - if '=' in option_string: - option_prefix, explicit_arg = option_string.split('=', 1) - else: - option_prefix = option_string - explicit_arg = None - for option_string in self._option_string_actions: - if option_string.startswith(option_prefix): - action = self._option_string_actions[option_string] - tup = action, option_string, explicit_arg - result.append(tup) - - # single character options can be concatenated with their arguments - # but multiple character options always have to have their argument - # separate - elif option_string[0] in chars and option_string[1] not in chars: - option_prefix = option_string - explicit_arg = None - short_option_prefix = option_string[:2] - short_explicit_arg = option_string[2:] - - for option_string in self._option_string_actions: - if option_string == short_option_prefix: - action = self._option_string_actions[option_string] - tup = action, option_string, short_explicit_arg - result.append(tup) - elif option_string.startswith(option_prefix): - action = self._option_string_actions[option_string] - tup = action, option_string, explicit_arg - result.append(tup) - - # shouldn't ever get here - else: - self.error(_('unexpected option string: %s') % option_string) - - # return the collected option tuples - return result - - def _get_nargs_pattern(self, action): - # in all examples below, we have to allow for '--' args - # which are represented as '-' in the pattern - nargs = action.nargs - - # the default (None) is assumed to be a single argument - if nargs is None: - nargs_pattern = '(-*A-*)' - - # allow zero or one arguments - elif nargs == OPTIONAL: - nargs_pattern = '(-*A?-*)' - - # allow zero or more arguments - elif nargs == ZERO_OR_MORE: - nargs_pattern = '(-*[A-]*)' - - # allow one or more arguments - elif nargs == ONE_OR_MORE: - nargs_pattern = '(-*A[A-]*)' - - # allow any number of options or arguments - elif nargs == REMAINDER: - nargs_pattern = '([-AO]*)' - - # allow one argument followed by any number of options or arguments - elif nargs == PARSER: - nargs_pattern = '(-*A[-AO]*)' - - # all others should be integers - else: - nargs_pattern = '(-*%s-*)' % '-*'.join('A' * nargs) - - # if this is an optional action, -- is not allowed - if action.option_strings: - nargs_pattern = nargs_pattern.replace('-*', '') - nargs_pattern = nargs_pattern.replace('-', '') - - # return the pattern - return nargs_pattern - - # ======================== - # Value conversion methods - # ======================== - def _get_values(self, action, arg_strings): - # for everything but PARSER args, strip out '--' - if action.nargs not in [PARSER, REMAINDER]: - arg_strings = [s for s in arg_strings if s != '--'] - - # optional argument produces a default when not present - if not arg_strings and action.nargs == OPTIONAL: - if action.option_strings: - value = action.const - else: - value = action.default - if isinstance(value, basestring): - value = self._get_value(action, value) - self._check_value(action, value) - - # when nargs='*' on a positional, if there were no command-line - # args, use the default if it is anything other than None - elif (not arg_strings and action.nargs == ZERO_OR_MORE and - not action.option_strings): - if action.default is not None: - value = action.default - else: - value = arg_strings - self._check_value(action, value) - - # single argument or optional argument produces a single value - elif len(arg_strings) == 1 and action.nargs in [None, OPTIONAL]: - arg_string, = arg_strings - value = self._get_value(action, arg_string) - self._check_value(action, value) - - # REMAINDER arguments convert all values, checking none - elif action.nargs == REMAINDER: - value = [self._get_value(action, v) for v in arg_strings] - - # PARSER arguments convert all values, but check only the first - elif action.nargs == PARSER: - value = [self._get_value(action, v) for v in arg_strings] - self._check_value(action, value[0]) - - # all other types of nargs produce a list - else: - value = [self._get_value(action, v) for v in arg_strings] - for v in value: - self._check_value(action, v) - - # return the converted value - return value - - def _get_value(self, action, arg_string): - type_func = self._registry_get('type', action.type, action.type) - if not _callable(type_func): - msg = _('%r is not callable') - raise ArgumentError(action, msg % type_func) - - # convert the value to the appropriate type - try: - result = type_func(arg_string) - - # ArgumentTypeErrors indicate errors - except ArgumentTypeError: - name = getattr(action.type, '__name__', repr(action.type)) - msg = str(_sys.exc_info()[1]) - raise ArgumentError(action, msg) - - # TypeErrors or ValueErrors also indicate errors - except (TypeError, ValueError): - name = getattr(action.type, '__name__', repr(action.type)) - msg = _('invalid %s value: %r') - raise ArgumentError(action, msg % (name, arg_string)) - - # return the converted value - return result - - def _check_value(self, action, value): - # converted value must be one of the choices (if specified) - if action.choices is not None and value not in action.choices: - tup = value, ', '.join(map(repr, action.choices)) - msg = _('invalid choice: %r (choose from %s)') % tup - raise ArgumentError(action, msg) - - # ======================= - # Help-formatting methods - # ======================= - def format_usage(self): - formatter = self._get_formatter() - formatter.add_usage(self.usage, self._actions, - self._mutually_exclusive_groups) - return formatter.format_help() - - def format_help(self): - formatter = self._get_formatter() - - # usage - formatter.add_usage(self.usage, self._actions, - self._mutually_exclusive_groups) - - # description - formatter.add_text(self.description) - - # positionals, optionals and user-defined groups - for action_group in self._action_groups: - formatter.start_section(action_group.title) - formatter.add_text(action_group.description) - formatter.add_arguments(action_group._group_actions) - formatter.end_section() - - # epilog - formatter.add_text(self.epilog) - - # determine help from format above - return formatter.format_help() - - def format_version(self): - import warnings - warnings.warn( - 'The format_version method is deprecated -- the "version" ' - 'argument to ArgumentParser is no longer supported.', - DeprecationWarning) - formatter = self._get_formatter() - formatter.add_text(self.version) - return formatter.format_help() - - def _get_formatter(self): - return self.formatter_class(prog=self.prog) - - # ===================== - # Help-printing methods - # ===================== - def print_usage(self, file=None): - if file is None: - file = _sys.stdout - self._print_message(self.format_usage(), file) - - def print_help(self, file=None): - if file is None: - file = _sys.stdout - self._print_message(self.format_help(), file) - - def print_version(self, file=None): - import warnings - warnings.warn( - 'The print_version method is deprecated -- the "version" ' - 'argument to ArgumentParser is no longer supported.', - DeprecationWarning) - self._print_message(self.format_version(), file) - - def _print_message(self, message, file=None): - if message: - if file is None: - file = _sys.stderr - file.write(message) - - # =============== - # Exiting methods - # =============== - def exit(self, status=0, message=None): - if message: - self._print_message(message, _sys.stderr) - _sys.exit(status) - - def error(self, message): - """error(message: string) - - Prints a usage message incorporating the message to stderr and - exits. - - If you override this in a subclass, it should not return -- it - should either exit or raise an exception. - """ - self.print_usage(_sys.stderr) - self.exit(2, _('%s: error: %s\n') % (self.prog, message)) diff --git a/astropy/utils/compat/gzip.py b/astropy/utils/compat/gzip.py deleted file mode 100644 index 4387cb5af9dd..000000000000 --- a/astropy/utils/compat/gzip.py +++ /dev/null @@ -1,21 +0,0 @@ -# Licensed under a 3-clause BSD style license - see LICENSE.rst -""" -Handles backports of the standard library's `gzip.py`. - -Python 2.6 has a `gzip.py` without support for the `with` statement. -Here, the version that ships with Python 2.7 is used instead. - -Python 3.1 has a `gzip.py` that can not be wrapped by an -`io.TextIOWrapper`. Here, the version that ships with Python 3.2 is -used instead. -""" - -from __future__ import absolute_import - -import sys -if sys.version_info[:2] == (3, 1): - from ._gzip_32 import * -elif sys.version_info[:2] == (2, 6): - from ._gzip_27 import * -else: - from gzip import * diff --git a/astropy/utils/compat/misc.py b/astropy/utils/compat/misc.py deleted file mode 100644 index 8d662c189a9a..000000000000 --- a/astropy/utils/compat/misc.py +++ /dev/null @@ -1,92 +0,0 @@ -# Licensed under a 3-clause BSD style license - see LICENSE.rst -""" -Simple utility functions and bug fixes for compatibility with all supported -versions of Python. This module should generally not be used directly, as -everything in `__all__` will be imported into `astropy.utils.compat` and can -be accessed from there. - -Includes the following fixes: - -* The `inspect.getmodule` function does not always work in Python 3.1 and 3.2. - This package includes a function `inspect_getmodule` that will simply be an - alias to `inspect.getmodule` if the stdlib version is correct, but for - versions of python with the bug, it uses an internal patched version. - -""" - -from __future__ import absolute_import - -from sys import version_info - -__all__ = ['inspect_getmodule'] - - -def _patched_getmodule(object, _filename=None): - """Return the module an object was defined in, or None if not found. - - This replicates the functionality of the stdlib `inspect.getmodule` - function but includes a fix for a bug present in Python 3.1 and 3.2. - """ - #these imports mock up what would otherwise have been in inspect - import sys - import os - from inspect import modulesbyfile, _filesbymodname, getabsfile, ismodule - - if ismodule(object): - return object - if hasattr(object, '__module__'): - return sys.modules.get(object.__module__) - # Try the filename to modulename cache - if _filename is not None and _filename in modulesbyfile: - return sys.modules.get(modulesbyfile[_filename]) - # Try the cache again with the absolute file name - try: - file = getabsfile(object, _filename) - except TypeError: - return None - if file in modulesbyfile: - return sys.modules.get(modulesbyfile[file]) - # Update the filename to module name cache and check yet again - # Copy sys.modules in order to cope with changes while iterating - # This is where the fix is made - the adding of the "list" call: - for modname, module in list(sys.modules.items()): - if ismodule(module) and hasattr(module, '__file__'): - f = module.__file__ - if f == _filesbymodname.get(modname, None): - # Have already mapped this module, so skip it - continue - _filesbymodname[modname] = f - f = getabsfile(module) - # Always map to the name the module knows itself by - modulesbyfile[f] = modulesbyfile[ - os.path.realpath(f)] = module.__name__ - if file in modulesbyfile: - return sys.modules.get(modulesbyfile[file]) - # Check the main module - main = sys.modules['__main__'] - if not hasattr(object, '__name__'): - return None - if hasattr(main, object.__name__): - mainobject = getattr(main, object.__name__) - if mainobject is object: - return main - # Check builtins - builtin = sys.modules['builtins'] - if hasattr(builtin, object.__name__): - builtinobject = getattr(builtin, object.__name__) - if builtinobject is object: - return builtin - -inspect_getmodule = None -""" -An alias to `inspect.getmodule`, or a patched version that replicates the -functionality with a bugfix for Python 3.1 and 3.2. -""" - -#This assigns the stdlib inspect.getmodule to the variable name -#`inspect_getmodule` if it's not buggy, and uses the matched version if it is. -if version_info[0]<3 or version_info[1]>2: - #in 2.x everythig is fine, as well as >=3.3 - from inspect import getmodule as inspect_getmodule -else: - inspect_getmodule = _patched_getmodule diff --git a/astropy/utils/compat/numpycompat.py b/astropy/utils/compat/numpycompat.py new file mode 100644 index 000000000000..b4f40fb1caa9 --- /dev/null +++ b/astropy/utils/compat/numpycompat.py @@ -0,0 +1,89 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +""" +This is a collection of monkey patches and workarounds for bugs in +earlier versions of Numpy. +""" + +import warnings + +import numpy as np + +from astropy.utils import minversion +from astropy.utils.exceptions import ( + AstropyDeprecationWarning, + AstropyPendingDeprecationWarning, +) + +__all__ = [ + "NUMPY_LT_2_1", + "NUMPY_LT_2_2", + "NUMPY_LT_2_3", + "NUMPY_LT_2_4", + "NUMPY_LT_2_4_1", + "NUMPY_LT_2_5", + "chararray", + "get_chararray", +] + +# TODO: It might also be nice to have aliases to these named for specific +# features/bugs we're checking for (ex: +# astropy.table.table._BROKEN_UNICODE_TABLE_SORT) +NUMPY_LT_2_1 = not minversion(np, "2.1.0.dev") +NUMPY_LT_2_2 = not minversion(np, "2.2.0.dev0") +NUMPY_LT_2_3 = not minversion(np, "2.3.0.dev0") +NUMPY_LT_2_4 = not minversion(np, "2.4.0.dev0") +NUMPY_LT_2_4_1 = not minversion(np, "2.4.1.dev0") +NUMPY_LT_2_5 = not minversion(np, "2.5.0.dev0") + + +def __getattr__(attr): + # MHvK: Added in 8.0. Regular deprecation in 9.0, remove in 10.0? + if attr == "COPY_IF_NEEDED": + warnings.warn( + "COPY_IF_NEEDED is no longer needed now that astropy only " + "supports numpy >= 2. It can be safely replaced with 'None'. ", + AstropyPendingDeprecationWarning, + ) + return None + + raise AttributeError(f"module {__name__!r} has no attribute {attr!r}.") + + +with warnings.catch_warnings(): + warnings.filterwarnings("ignore", category=DeprecationWarning) + + from numpy.char import array as np_char_array + from numpy.char import chararray as np_chararray + + +_deprecated_chararray_attributes = { + "capitalize", "center", "count", "decode", "encode", "endswith", + "expandtabs", "find", "index", "isalnum", "isalpha", "isdecimal", + "isdigit", "islower", "isnumeric", "isspace", "istitle", "isupper", + "join", "ljust", "lower", "lstrip", "replace", "rfind", "rindex", + "rjust", "rpartition", "rsplit", "rstrip", "split", "splitlines", + "startswith", "strip", "swapcase", "title", "translate", "upper", + "zfill" +} # fmt: skip + + +class chararray(np_chararray): + """Version of np.char.chararray with deprecation warnings on special methods.""" + + def __getattribute__(self, name): + if name in _deprecated_chararray_attributes: + warnings.warn( + "chararray is deprecated, in future versions astropy will " + "return a normal array so the special chararray methods " + "(e.g., .rstrip()) will not be available. Use np.strings " + "functions instead.", + AstropyDeprecationWarning, + ) + return super().__getattribute__(name) + + +def get_chararray(obj, itemsize=None, copy=True, unicode=None, order=None): + """Get version of np.char.chararray that gives deprecation warnings on special methods.""" + return np_char_array( + obj, itemsize=itemsize, copy=copy, unicode=unicode, order=order + ).view(chararray) diff --git a/astropy/utils/compat/odict.py b/astropy/utils/compat/odict.py deleted file mode 100644 index 8d7961e19d77..000000000000 --- a/astropy/utils/compat/odict.py +++ /dev/null @@ -1,194 +0,0 @@ -# Licensed under a 3-clause BSD style license - see LICENSE.rst -""" -Backport of the Python 2.7 collections.OrderedDict class to Python 2.6. - -Ordered dictionaries are just like regular dictionaries but they remember the -order that items were inserted. When iterating over an ordered dictionary, -the items are returned in the order their keys were first added. - -See: http://docs.python.org/library/collections.html#collections.OrderedDict -""" - -from __future__ import absolute_import - -__all__ = ['OrderedDict'] - -try: - from collections import OrderedDict -except ImportError: - - from collections import MutableMapping - from operator import eq as _eq - from itertools import imap as _imap - - try: - from thread import get_ident - except ImportError: - from dummy_thread import get_ident - - - def _recursive_repr(user_function): - 'Decorator to make a repr function return "..." for a recursive call' - repr_running = set() - - def wrapper(self): - key = id(self), get_ident() - if key in repr_running: - return '...' - repr_running.add(key) - try: - result = user_function(self) - finally: - repr_running.discard(key) - return result - - # Can't use functools.wraps() here because of bootstrap issues - wrapper.__module__ = getattr(user_function, '__module__') - wrapper.__doc__ = getattr(user_function, '__doc__') - wrapper.__name__ = getattr(user_function, '__name__') - return wrapper - - class OrderedDict(dict, MutableMapping): - 'Dictionary that remembers insertion order' - # An inherited dict maps keys to values. - # The inherited dict provides __getitem__, __len__, __contains__, and get. - # The remaining methods are order-aware. - # Big-O running times for all methods are the same as for regular dictionaries. - - # The internal self.__map dictionary maps keys to links in a doubly linked list. - # The circular doubly linked list starts and ends with a sentinel element. - # The sentinel element never gets deleted (this simplifies the algorithm). - # Each link is stored as a list of length three: [PREV, NEXT, KEY]. - - def __init__(self, *args, **kwds): - '''Initialize an ordered dictionary. Signature is the same as for - regular dictionaries, but keyword arguments are not recommended - because their insertion order is arbitrary. - - ''' - if len(args) > 1: - raise TypeError('expected at most 1 arguments, got %d' % len(args)) - try: - self.__root - except AttributeError: - self.__root = root = [None, None, None] # sentinel node - PREV = 0 - NEXT = 1 - root[PREV] = root[NEXT] = root - self.__map = {} - self.update(*args, **kwds) - - def __setitem__(self, key, value, PREV=0, NEXT=1, dict_setitem=dict.__setitem__): - 'od.__setitem__(i, y) <==> od[i]=y' - # Setting a new item creates a new link which goes at the end of the linked - # list, and the inherited dictionary is updated with the new key/value pair. - if key not in self: - root = self.__root - last = root[PREV] - last[NEXT] = root[PREV] = self.__map[key] = [last, root, key] - dict_setitem(self, key, value) - - def __delitem__(self, key, PREV=0, NEXT=1, dict_delitem=dict.__delitem__): - 'od.__delitem__(y) <==> del od[y]' - # Deleting an existing item uses self.__map to find the link which is - # then removed by updating the links in the predecessor and successor nodes. - dict_delitem(self, key) - link = self.__map.pop(key) - link_prev = link[PREV] - link_next = link[NEXT] - link_prev[NEXT] = link_next - link_next[PREV] = link_prev - - def __iter__(self, NEXT=1, KEY=2): - 'od.__iter__() <==> iter(od)' - # Traverse the linked list in order. - root = self.__root - curr = root[NEXT] - while curr is not root: - yield curr[KEY] - curr = curr[NEXT] - - def __reversed__(self, PREV=0, KEY=2): - 'od.__reversed__() <==> reversed(od)' - # Traverse the linked list in reverse order. - root = self.__root - curr = root[PREV] - while curr is not root: - yield curr[KEY] - curr = curr[PREV] - - def __reduce__(self): - 'Return state information for pickling' - items = [[k, self[k]] for k in self] - tmp = self.__map, self.__root - del self.__map, self.__root - inst_dict = vars(self).copy() - self.__map, self.__root = tmp - if inst_dict: - return (self.__class__, (items,), inst_dict) - return self.__class__, (items,) - - def clear(self): - 'od.clear() -> None. Remove all items from od.' - try: - for node in self.__map.itervalues(): - del node[:] - self.__root[:] = [self.__root, self.__root, None] - self.__map.clear() - except AttributeError: - pass - dict.clear(self) - - setdefault = MutableMapping.setdefault - update = MutableMapping.update - pop = MutableMapping.pop - keys = MutableMapping.keys - values = MutableMapping.values - items = MutableMapping.items - iterkeys = MutableMapping.iterkeys - itervalues = MutableMapping.itervalues - iteritems = MutableMapping.iteritems - __ne__ = MutableMapping.__ne__ - - def popitem(self, last=True): - '''od.popitem() -> (k, v), return and remove a (key, value) pair. - Pairs are returned in LIFO order if last is true or FIFO order if false. - - ''' - if not self: - raise KeyError('dictionary is empty') - key = next(reversed(self) if last else iter(self)) - value = self.pop(key) - return key, value - - @_recursive_repr - def __repr__(self): - 'od.__repr__() <==> repr(od)' - if not self: - return '%s()' % (self.__class__.__name__,) - return '%s(%r)' % (self.__class__.__name__, self.items()) - - def copy(self): - 'od.copy() -> a shallow copy of od' - return self.__class__(self) - - @classmethod - def fromkeys(cls, iterable, value=None): - '''OD.fromkeys(S[, v]) -> New ordered dictionary with keys from S - and values equal to v (which defaults to None). - - ''' - d = cls() - for key in iterable: - d[key] = value - return d - - def __eq__(self, other): - '''od.__eq__(y) <==> od==y. Comparison to another OD is order-sensitive - while comparison to a regular mapping is order-insensitive. - - ''' - if isinstance(other, OrderedDict): - return len(self)==len(other) and \ - all(_imap(_eq, self.iteritems(), other.iteritems())) - return dict.__eq__(self, other) diff --git a/astropy/utils/compat/optional_deps.py b/astropy/utils/compat/optional_deps.py new file mode 100644 index 000000000000..505944640d94 --- /dev/null +++ b/astropy/utils/compat/optional_deps.py @@ -0,0 +1,62 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +"""Checks for optional dependencies using lazy import from +`PEP 562 `_. +""" + +from importlib.util import find_spec + +# First, the top-level packages: +# TODO: This list is a duplicate of the dependencies in pyproject.toml "all", but +# some of the package names are different from the pip-install name (e.g., +# beautifulsoup4 -> bs4). +# Some optional parts of the standard library also find a place here, +# but don't appear in pyproject.toml +_optional_deps = [ + "asdf_astropy", + "bleach", + "bottleneck", + "bs4", + "bz2", # stdlib + "certifi", + "dask", + "duckdb", + "fsspec", + "h5py", + "html5lib", + "ipykernel", + "IPython", + "ipywidgets", + "ipydatagrid", + "jplephem", + "lxml", + "matplotlib", + "mpmath", + "narwhals", + "numpy_quaddtype", + "pandas", + "PIL", + "polars", + "pytz", + "s3fs", + "scipy", + "skyfield", + "sortedcontainers", + "uncompresspy", + "lzma", # stdlib + "pyarrow", + "pytest_mpl", + "array_api_strict", +] +_deps = {k.upper(): k for k in _optional_deps} + +# Any subpackages that have different import behavior: +_deps["PLT"] = "matplotlib" + +__all__ = [f"HAS_{pkg}" for pkg in _deps] + + +def __getattr__(name): + if name in __all__: + return find_spec(_deps[name.removeprefix("HAS_")]) is not None + + raise AttributeError(f"Module {__name__!r} has no attribute {name!r}.") diff --git a/astropy/utils/console.py b/astropy/utils/console.py index 278b552f5e94..591f91839d0f 100644 --- a/astropy/utils/console.py +++ b/astropy/utils/console.py @@ -1,63 +1,203 @@ -# -*- coding: utf-8 -*- # Licensed under a 3-clause BSD style license - see LICENSE.rst """ Utilities for console input and output. """ -from __future__ import division, print_function -import re +import codecs +import locale import math +import multiprocessing +import os +import struct import sys +import threading import time +from shutil import get_terminal_size + +# concurrent.futures imports moved inside functions using them to avoid +# import failure when running in pyodide/Emscripten try: - from IPython.zmq.iostream import OutStream -except ImportError: - OutStream = None + import fcntl + import signal + import termios -from ..config import ConfigurationItem + _CAN_RESIZE_TERMINAL = True +except ImportError: + _CAN_RESIZE_TERMINAL = False +import numpy as np -__all__ = [ - 'isatty', 'color_print', 'human_time', 'ProgressBar', 'Spinner', - 'print_code_line', 'ProgressBarOrSpinner'] +from astropy import conf +from astropy.utils.compat.optional_deps import HAS_IPYKERNEL, HAS_IPYWIDGETS +from .decorators import deprecated -USE_COLOR = ConfigurationItem( - 'use_color', True, - 'When True, use ANSI color escape sequences when writing to the console.') -USE_UNICODE = ConfigurationItem( - 'use_unicode', True, - 'Use Unicode characters when drawing progress bars etc. at the console.') +__all__ = [ + "ProgressBar", + "ProgressBarOrSpinner", + "Spinner", + "color_print", + "human_file_size", + "human_time", + "isatty", + "print_code_line", + "terminal_size", +] def isatty(file): """ - Returns `True` if `file` is a tty. + Returns `True` if ``file`` is a tty. Most built-in Python file-like objects have an `isatty` member, but some user-defined types may not, so this assumes those are not ttys. """ - if (OutStream is not None and - isinstance(file, OutStream) and - file.name == 'stdout'): - return True - elif hasattr(file, 'isatty'): - return file.isatty() - return False + if ( + multiprocessing.current_process().name != "MainProcess" + or threading.current_thread().name != "MainThread" + ): + return False + + return hasattr(file, "isatty") and file.isatty() + + +@deprecated("6.1", alternative="shutil.get_terminal_size") +def terminal_size(file=None): + """ + Returns a tuple (height, width) containing the height and width of + the terminal. + + This function will look for the width in height in multiple areas + before falling back on the width and height in astropy's + configuration. + """ + if file is None: + file = sys.stdout + + try: + s = struct.pack("HHHH", 0, 0, 0, 0) + x = fcntl.ioctl(file, termios.TIOCGWINSZ, s) + (lines, width, xpixels, ypixels) = struct.unpack("HHHH", x) + if lines > 12: + lines -= 6 + if width > 10: + width -= 1 + if lines <= 0 or width <= 0: + raise Exception("unable to get terminal size") + return (lines, width) + except Exception: + try: + # see if POSIX standard variables will work + return (int(os.environ.get("LINES")), int(os.environ.get("COLUMNS"))) + except TypeError: + # fall back on configuration variables, or if not + # set, (25, 80) + lines = conf.max_lines + width = conf.max_width + if lines is None: + lines = 25 + if width is None: + width = 80 + return lines, width + + +def _color_text(text, color): + """Returns a string wrapped in ANSI color codes for coloring the text in a terminal. + + :: + + colored_text = color_text('Here is a message', 'blue') + + This won't actually effect the text until it is printed to the + terminal. + + Parameters + ---------- + text : str + The string to return, bounded by the color codes. + color : str + An ANSI terminal color name. Must be one of: + black, red, green, brown, blue, magenta, cyan, lightgrey, + default, darkgrey, lightred, lightgreen, yellow, lightblue, + lightmagenta, lightcyan, white, or '' (the empty string). + """ + color_mapping = { + "black": "0;30", + "red": "0;31", + "green": "0;32", + "brown": "0;33", + "blue": "0;34", + "magenta": "0;35", + "cyan": "0;36", + "lightgrey": "0;37", + "default": "0;39", + "darkgrey": "1;30", + "lightred": "1;31", + "lightgreen": "1;32", + "yellow": "1;33", + "lightblue": "1;34", + "lightmagenta": "1;35", + "lightcyan": "1;36", + "white": "1;37", + } + + if sys.platform == "win32" and not HAS_IPYKERNEL: + # On Windows do not colorize text unless in IPython + return text + + color_code = color_mapping.get(color, "0;39") + return f"\033[{color_code}m{text}\033[0m" + + +def _write_with_fallback(s, write, fileobj): + """Write the supplied string with the given write function like + ``write(s)``, but use a writer for the locale's preferred encoding in case + of a UnicodeEncodeError. Failing that attempt to write with 'utf-8' or + 'latin-1'. + """ + try: + write(s) + return write + except UnicodeEncodeError: + # Let's try the next approach... + pass + + enc = locale.getpreferredencoding() + try: + Writer = codecs.getwriter(enc) + except LookupError: + Writer = codecs.getwriter("utf-8") + + f = Writer(fileobj) + write = f.write + try: + write(s) + return write + except UnicodeEncodeError: + Writer = codecs.getwriter("latin-1") + f = Writer(fileobj) + write = f.write -def color_print(*args, **kwargs): + # If this doesn't work let the exception bubble up; I'm out of ideas + write(s) + return write + + +def color_print(*args, end="\n", **kwargs): """ Prints colors and styles to the terminal uses ANSI escape - sequences:: + sequences. + + :: color_print('This is the color ', 'default', 'GREEN', 'green') Parameters ---------- - positional args : strings + positional args : str The positional arguments come in pairs (*msg*, *color*), where *msg* is the string to display and *color* is the color to display it in. @@ -67,7 +207,7 @@ def color_print(*args, **kwargs): default, darkgrey, lightred, lightgreen, yellow, lightblue, lightmagenta, lightcyan, white, or '' (the empty string). - file : writeable file-like object, optional + file : :term:`file-like (writeable)`, optional Where to write to. Defaults to `sys.stdout`. If file is not a tty (as determined by calling its `isatty` member, if one exists), no coloring will be included. @@ -76,64 +216,34 @@ def color_print(*args, **kwargs): The ending of the message. Defaults to ``\\n``. The end will be printed after resetting any color or font state. """ - color_mapping = { - 'black': '0;30', - 'red': '0;31', - 'green': '0;32', - 'brown': '0;33', - 'blue': '0;34', - 'magenta': '0;35', - 'cyan': '0;36', - 'lightgrey': '0;37', - 'default': '0;39', - 'darkgrey': '1;30', - 'lightred': '1;31', - 'lightgreen': '1;32', - 'yellow': '1;33', - 'lightblue': '1;34', - 'lightmagenta': '1;35', - 'lightcyan': '1;36', - 'white': '1;37'} - - file = kwargs.get('file', sys.stdout) - end = kwargs.get('end', u'\n') + file = kwargs.get("file", sys.stdout) write = file.write - if isatty(file) and USE_COLOR(): - for i in xrange(0, len(args), 2): + if isatty(file) and conf.use_color: + for i in range(0, len(args), 2): msg = args[i] if i + 1 == len(args): - color = '' + color = "" else: color = args[i + 1] - if isinstance(msg, bytes): - msg = msg.decode('ascii') + if color: + msg = _color_text(msg, color) - if color == u'' or color is None: - write(msg) - else: - color_code = color_mapping.get(color, '0;39') - write(u'\033[{0}m{1}\033[0m'.format( - color_code, msg)) + # Some file objects support writing unicode sensibly on some Python + # versions; if this fails try creating a writer using the locale's + # preferred encoding. If that fails too give up. + + write = _write_with_fallback(msg, write, file) write(end) else: - for i in xrange(0, len(args), 2): + for i in range(0, len(args), 2): msg = args[i] - if isinstance(msg, bytes): - msg = msg.decode('ascii') write(msg) write(end) -def strip_ansi_codes(s): - """ - Remove ANSI color codes from the string. - """ - return re.sub('\033\[([0-9]+)(;[0-9]+)*m', '', s) - - def human_time(seconds): """ Returns a human-friendly time string that is always exactly 6 @@ -161,82 +271,165 @@ def human_time(seconds): that is always exactly 6 characters. """ units = [ - (u'y', 60 * 60 * 24 * 7 * 52), - (u'w', 60 * 60 * 24 * 7), - (u'd', 60 * 60 * 24), - (u'h', 60 * 60), - (u'm', 60), - (u's', 1), - ] + ("y", 60 * 60 * 24 * 7 * 52), + ("w", 60 * 60 * 24 * 7), + ("d", 60 * 60 * 24), + ("h", 60 * 60), + ("m", 60), + ("s", 1), + ] seconds = int(seconds) if seconds < 60: - return u' {0:02d}s'.format(seconds) - for i in xrange(len(units) - 1): + return f" {seconds:2d}s" + for i in range(len(units) - 1): unit1, limit1 = units[i] unit2, limit2 = units[i + 1] if seconds >= limit1: - return u'{0:02d}{1}{2:02d}{3}'.format( - seconds // limit1, unit1, - (seconds % limit1) // limit2, unit2) - return u' ~inf' + return ( + f"{seconds // limit1:2d}{unit1}{(seconds % limit1) // limit2:2d}{unit2}" + ) + return " ~inf" + + +def human_file_size(size): + """ + Returns a human-friendly string representing a file size + that is 2-4 characters long. + + For example, depending on the number of bytes given, can be one + of:: + + 256b + 64k + 1.1G + + Parameters + ---------- + size : int + The size of the file (in bytes) + + Returns + ------- + size : str + A human-friendly representation of the size of the file + """ + if hasattr(size, "unit"): + # Import units only if necessary because the import takes a + # significant time [#4649] + from astropy import units as u + + size = u.Quantity(size, u.byte).value + + suffixes = " kMGTPEZY" + if size == 0: + num_scale = 0 + else: + num_scale = math.floor(math.log(size) / math.log(1000)) + if num_scale > 7: + suffix = "?" + else: + suffix = suffixes[num_scale] + num_scale = int(math.pow(1000, num_scale)) + value = size / num_scale + str_value = str(value) + if suffix == " ": + str_value = str_value[: str_value.index(".")] + elif str_value[2] == ".": + str_value = str_value[:2] + else: + str_value = str_value[:3] + return f"{str_value:>3s}{suffix}" + + +class _mapfunc: + """ + A function wrapper to support ProgressBar.map(). + """ + + def __init__(self, func): + self._func = func + + def __call__(self, i_arg): + i, arg = i_arg + return i, self._func(arg) class ProgressBar: """ A class to display a progress bar in the terminal. - It is designed to be used with the `with` statement:: + It is designed to be used either with the ``with`` statement:: with ProgressBar(len(items)) as bar: for item in enumerate(items): bar.update() + + or as a generator:: + + for item in ProgressBar(items): + item.process() """ - def __init__(self, total, file=sys.stdout): + + def __init__(self, total_or_items, ipython_widget=False, file=None): """ Parameters ---------- - total : int - The number of increments in the process being tracked. + total_or_items : int or sequence + If an int, the number of increments in the process being + tracked. If a sequence, the items to iterate over. + + ipython_widget : bool, optional + If `True`, the progress bar will display as an IPython + notebook widget. - file : writable file-like object, optional + file : :term:`file-like (writeable)`, optional The file to write the progress bar to. Defaults to - `sys.stdout`. If `file` is not a tty (as determined by - calling its `isatty` member, if any), the scrollbar will - be completely silent. + `sys.stdout`. If ``file`` is not a tty (as determined by + calling its `isatty` member, if any, or special case hacks + to detect the IPython console), the progress bar will be + completely silent. """ - if not isatty(file): + if file is None: + file = sys.stdout + + if not ipython_widget and not isatty(file): self.update = self._silent_update self._silent = True else: self._silent = False - self._total = total + if np.iterable(total_or_items): + self._items = iter(total_or_items) + self._total = len(total_or_items) + else: + try: + self._total = int(total_or_items) + except TypeError: + raise TypeError("First argument must be int or sequence") + else: + self._items = iter(range(self._total)) + self._file = file self._start_time = time.time() - terminal_width = 78 - if sys.platform.startswith('linux'): - import subprocess - p = subprocess.Popen( - 'stty size', - shell=True, - stdout=subprocess.PIPE) - stdout, stderr = p.communicate() - parts = stdout.split() - if len(parts) == 2: - rows, cols = parts - terminal_width = int(cols) - self._bar_length = terminal_width - 36 - num_scale = int(math.floor(math.log(self._total) / math.log(1000))) - if num_scale > 7: - self._suffix = '?' - else: - suffixes = u' kMGTPEH' - self._suffix = suffixes[num_scale] - self._num_scale = int(math.pow(1000, num_scale)) + self._human_total = human_file_size(self._total) + self._ipython_widget = ipython_widget + + self._signal_set = False + if not ipython_widget: + self._should_handle_resize = _CAN_RESIZE_TERMINAL and self._file.isatty() + self._handle_resize() + if self._should_handle_resize: + signal.signal(signal.SIGWINCH, self._handle_resize) + self._signal_set = True + self.update(0) + def _handle_resize(self, signum=None, frame=None): + terminal_width = get_terminal_size().columns + self._bar_length = terminal_width - 37 + def __enter__(self): return self @@ -244,18 +437,44 @@ def __exit__(self, exc_type, exc_value, traceback): if not self._silent: if exc_type is None: self.update(self._total) - self._file.write('\n') + self._file.write("\n") self._file.flush() + if self._signal_set: + signal.signal(signal.SIGWINCH, signal.SIG_DFL) + + def __iter__(self): + return self + + def __next__(self): + try: + rv = next(self._items) + except StopIteration: + self.__exit__(None, None, None) + raise + else: + self.update() + return rv def update(self, value=None): """ - Update the progress bar to the given value (out of the total - given to the constructor. + Update progress bar via the console or notebook accordingly. """ + # Update self.value if value is None: - value = self._current_value = self._current_value + 1 + value = self._current_value + 1 + self._current_value = value + + # Choose the appropriate environment + if self._ipython_widget: + self._update_ipython_widget(value) else: - self._current_value = value + self._update_console(value) + + def _update_console(self, value=None): + """ + Update the progress bar to the given value (out of the total + given to the constructor). + """ if self._total == 0: frac = 1.0 else: @@ -264,41 +483,81 @@ def update(self, value=None): file = self._file write = file.write - bar_fill = int(float(self._bar_length) * frac) - write(u'\r|') - color_print(u'=' * bar_fill, 'blue', file=file, end=u'') + if frac > 1: + bar_fill = int(self._bar_length) + else: + bar_fill = int(float(self._bar_length) * frac) + write("\r|") + color_print("=" * bar_fill, "blue", file=file, end="") if bar_fill < self._bar_length: - color_print(u'>', 'green', file=file, end=u'') - write(u'-' * (self._bar_length - bar_fill - 1)) - write(u'|') + color_print(">", "green", file=file, end="") + write("-" * (self._bar_length - bar_fill - 1)) + write("|") if value >= self._total: t = time.time() - self._start_time - prefix = u' ' + prefix = " " elif value <= 0: t = None - prefix = u'' + prefix = "" else: t = ((time.time() - self._start_time) * (1.0 - frac)) / frac - prefix = u' ETA ' - write(u' {0:>3d}/{1:>3d}{2}'.format( - value // self._num_scale, - self._total // self._num_scale, - self._suffix)) - write(u' ({0:>6s}%)'.format(u'{0:.2f}'.format(frac * 100.0))) + prefix = " ETA " + write(f" {human_file_size(value):>4s}/{self._human_total:>4s}") + write(f" ({frac:>6.2%})") write(prefix) if t is not None: write(human_time(t)) self._file.flush() + def _update_ipython_widget(self, value=None): + """ + Update the progress bar to the given value (out of a total + given to the constructor). + + This method is for use in the IPython notebook 2+. + """ + # Create and display an empty progress bar widget, + # if none exists. + + if not hasattr(self, "_widget"): + # Import only if an IPython widget, i.e., widget in iPython NB + if not HAS_IPYWIDGETS: + raise ModuleNotFoundError("ipywidgets is not installed") + + from ipywidgets import widgets + + self._widget = widgets.FloatProgress() + from IPython.display import display + + display(self._widget) + self._widget.value = 0 + + # Calculate percent completion, and update progress bar + frac = value / self._total + self._widget.value = frac * 100 + self._widget.description = f" ({frac:>6.2%})" + def _silent_update(self, value=None): pass @classmethod - def map(cls, function, items, multiprocess=False, file=sys.stdout): - """ - Does a `map` operation while displaying a progress bar with - percentage complete:: + def map( + cls, + function, + items, + multiprocess=False, + file=None, + step=100, + ipython_widget=False, + multiprocessing_start_method=None, + ): + """Map function over items while displaying a progress bar with percentage complete. + + The map operation may run in arbitrary order on the items, but the results are + returned in sequential order. + + :: def work(i): print(i) @@ -314,77 +573,171 @@ def work(i): Sequence where each element is a tuple of arguments to pass to *function*. - multiprocess : bool, optional - If `True`, use the `multiprocessing` module to distribute each - task to a different processor core. + multiprocess : bool, int, optional + If `True`, use the `multiprocessing` module to distribute each task + to a different processor core. If a number greater than 1, then use + that number of cores. + + ipython_widget : bool, optional + If `True`, the progress bar will display as an IPython + notebook widget. - file : writeable file-like object, optional + file : :term:`file-like (writeable)`, optional The file to write the progress bar to. Defaults to - `sys.stdout`. If `file` is not a tty (as determined by + `sys.stdout`. If ``file`` is not a tty (as determined by calling its `isatty` member, if any), the scrollbar will be completely silent. + + step : int, optional + Update the progress bar at least every *step* steps (default: 100). + If ``multiprocess`` is `True`, this will affect the size + of the chunks of ``items`` that are submitted as separate tasks + to the process pool. A large step size may make the job + complete faster if ``items`` is very long. + + multiprocessing_start_method : str, optional + Useful primarily for testing; if in doubt leave it as the default. + When using multiprocessing, certain anomalies occur when starting + processes with the "spawn" method (the only option on Windows); + other anomalies occur with the "fork" method (the default on + Linux). """ - with cls(len(items), file=file) as bar: - step_size = max(200, bar._bar_length) - steps = max(int(float(len(items)) / step_size), 1) - if not multiprocess: - for i, item in enumerate(items): - function(item) - if (i % steps) == 0: - bar.update(i) - else: - import multiprocessing - p = multiprocessing.Pool() - for i, _ in enumerate( - p.imap_unordered(function, items, steps)): - bar.update(i) + if multiprocess: + function = _mapfunc(function) + items = list(enumerate(items)) + + results = cls.map_unordered( + function, + items, + multiprocess=multiprocess, + file=file, + step=step, + ipython_widget=ipython_widget, + multiprocessing_start_method=multiprocessing_start_method, + ) + + if multiprocess: + _, results = zip(*sorted(results)) + results = list(results) + + return results @classmethod - def iterate(cls, items, file=sys.stdout): - """ - Iterate over a sequence while indicating progress with a progress - bar in the terminal:: + def map_unordered( + cls, + function, + items, + multiprocess=False, + file=None, + step=100, + ipython_widget=False, + multiprocessing_start_method=None, + ): + """Map function over items, reporting the progress. - for item in ProgressBar.iterate(items): - pass + Does a `map` operation while displaying a progress bar with + percentage complete. The map operation may run on arbitrary order + on the items, and the results may be returned in arbitrary order. + + :: + + def work(i): + print(i) + + ProgressBar.map(work, range(50)) Parameters ---------- + function : function + Function to call for each step + items : sequence - A sequence of items to iterate over + Sequence where each element is a tuple of arguments to pass to + *function*. + + multiprocess : bool, int, optional + If `True`, use the `multiprocessing` module to distribute each task + to a different processor core. If a number greater than 1, then use + that number of cores. + + ipython_widget : bool, optional + If `True`, the progress bar will display as an IPython + notebook widget. - file : writeable file-like object, optional + file : :term:`file-like (writeable)`, optional The file to write the progress bar to. Defaults to - `sys.stdout`. If `file` is not a tty (as determined by + `sys.stdout`. If ``file`` is not a tty (as determined by calling its `isatty` member, if any), the scrollbar will be completely silent. - Returns - ------- - generator : - A generator over `items` + step : int, optional + Update the progress bar at least every *step* steps (default: 100). + If ``multiprocess`` is `True`, this will affect the size + of the chunks of ``items`` that are submitted as separate tasks + to the process pool. A large step size may make the job + complete faster if ``items`` is very long. + + multiprocessing_start_method : str, optional + Useful primarily for testing; if in doubt leave it as the default. + When using multiprocessing, certain anomalies occur when starting + processes with the "spawn" method (the only option on Windows); + other anomalies occur with the "fork" method (the default on + Linux). """ - with cls(len(items), file=file) as bar: - for item in items: - yield item - bar.update() + # concurrent.futures import here to avoid import failure when running + # in pyodide/Emscripten + from concurrent.futures import ProcessPoolExecutor, as_completed + results = [] -class Spinner(): + if file is None: + file = sys.stdout + + with cls(len(items), ipython_widget=ipython_widget, file=file) as bar: + if bar._ipython_widget: + chunksize = step + else: + default_step = max(int(float(len(items)) / bar._bar_length), 1) + chunksize = min(default_step, step) + if not multiprocess or multiprocess < 1: + for i, item in enumerate(items): + results.append(function(item)) + if (i % chunksize) == 0: + bar.update(i) + else: + ctx = multiprocessing.get_context(multiprocessing_start_method) + kwargs = dict(mp_context=ctx) + + with ProcessPoolExecutor( + max_workers=( + int(multiprocess) if multiprocess is not True else None + ), + **kwargs, + ) as p: + for i, f in enumerate( + as_completed(p.submit(function, item) for item in items) + ): + bar.update(i) + results.append(f.result()) + + return results + + +class Spinner: """ A class to display a spinner in the terminal. - It is designed to be used with the `with` statement:: + It is designed to be used with the ``with`` statement:: with Spinner("Reticulating splines", "green") as s: for item in enumerate(items): - s.next() + s.update() """ - _default_unicode_chars = u"◓◑◒◐" - _default_ascii_chars = u"-/|\\" - def __init__(self, msg, color='default', file=sys.stdout, step=1, - chars=None): + _default_unicode_chars = "◓◑◒◐" + _default_ascii_chars = "-/|\\" + + def __init__(self, msg, color="default", file=None, step=1, chars=None): """ Parameters ---------- @@ -397,11 +750,12 @@ def __init__(self, msg, color='default', file=sys.stdout, step=1, darkgrey, lightred, lightgreen, yellow, lightblue, lightmagenta, lightcyan, white. - file : writeable file-like object, optional + file : :term:`file-like (writeable)`, optional The file to write the spinner to. Defaults to - `sys.stdout`. If `file` is not a tty (as determined by - calling its `isatty` member, if any), the scrollbar will - be completely silent. + `sys.stdout`. If ``file`` is not a tty (as determined by + calling its `isatty` member, if any, or special case hacks + to detect the IPython console), the spinner will be + completely silent. step : int, optional Only update the spinner every *step* steps @@ -409,12 +763,15 @@ def __init__(self, msg, color='default', file=sys.stdout, step=1, chars : str, optional The character sequence to use for the spinner """ + if file is None: + file = sys.stdout + self._msg = msg self._color = color self._file = file self._step = step if chars is None: - if USE_UNICODE: + if conf.unicode_output: chars = self._default_unicode_chars else: chars = self._default_ascii_chars @@ -422,33 +779,49 @@ def __init__(self, msg, color='default', file=sys.stdout, step=1, self._silent = not isatty(file) + if self._silent: + self._iter = self._silent_iterator() + else: + self._iter = self._iterator() + def _iterator(self): chars = self._chars index = 0 file = self._file write = file.write flush = file.flush + try_fallback = True + terminal_width = get_terminal_size().columns + if len(self._msg) > terminal_width: + message = self._msg[: terminal_width - 8] + " ..." + else: + message = self._msg while True: - write(u'\r') - color_print(self._msg, self._color, file=file, end=u'') - write(u' ') - write(chars[index]) + write("\r") + color_print(message, self._color, file=file, end="") + write(" ") + try: + if try_fallback: + write = _write_with_fallback(chars[index], write, file) + else: + write(chars[index]) + except UnicodeError: + # If even _write_with_fallback failed for any reason just give + # up on trying to use the unicode characters + chars = self._default_ascii_chars + write(chars[index]) + try_fallback = False # No good will come of using this again flush() yield - for i in xrange(self._step): + for _ in range(self._step): yield - index += 1 - if index == len(chars): - index = 0 + index = (index + 1) % len(chars) def __enter__(self): - if self._silent: - return self._silent_iterator() - else: - return self._iterator() + return self def __exit__(self, exc_type, exc_value, traceback): file = self._file @@ -456,16 +829,33 @@ def __exit__(self, exc_type, exc_value, traceback): flush = file.flush if not self._silent: - write(u'\r') - color_print(self._msg, self._color, file=file, end=u'') + write("\r") + color_print(self._msg, self._color, file=file, end="") if exc_type is None: - color_print(u' [Done]', 'green', file=file) + color_print(" [Done]", "green", file=file) else: - color_print(u' [Failed]', 'red', file=file) + color_print(" [Failed]", "red", file=file) flush() + def __iter__(self): + return self + + def __next__(self): + next(self._iter) + + def update(self, value=None): + """Update the spin wheel in the terminal. + + Parameters + ---------- + value : int, optional + Ignored (present just for compatibility with `ProgressBar.update`). + + """ + next(self) + def _silent_iterator(self): - color_print(self._msg, self._color, file=self._file, end=u'') + color_print(self._msg, self._color, file=self._file, end="") self._file.flush() while True: @@ -478,7 +868,7 @@ class ProgressBarOrSpinner: depending on whether the total size of the operation is known or not. - It is designed to be used with the `with` statement:: + It is designed to be used with the ``with`` statement:: if file.has_length(): length = file.get_length() @@ -491,7 +881,7 @@ class ProgressBarOrSpinner: bar.update(bytes_read) """ - def __init__(self, total, msg, color='default', file=sys.stdout): + def __init__(self, total, msg, color="default", file=None): """ Parameters ---------- @@ -505,18 +895,21 @@ def __init__(self, total, msg, color='default', file=sys.stdout): alongside the `Spinner`. color : str, optional - The color of `msg`, if any. Must be an ANSI terminal + The color of ``msg``, if any. Must be an ANSI terminal color name. Must be one of: black, red, green, brown, blue, magenta, cyan, lightgrey, default, darkgrey, lightred, lightgreen, yellow, lightblue, lightmagenta, lightcyan, white. - file : writable file-like object, optional + file : :term:`file-like (writeable)`, optional The file to write the to. Defaults to `sys.stdout`. If - `file` is not a tty (as determined by calling its `isatty` - member, if any), only `msg` will be displayed: the + ``file`` is not a tty (as determined by calling its `isatty` + member, if any), only ``msg`` will be displayed: the `ProgressBar` or `Spinner` will be silent. """ + if file is None: + file = sys.stdout + if total is None or not isatty(file): self._is_spinner = True self._obj = Spinner(msg, color=color, file=file) @@ -526,7 +919,6 @@ def __init__(self, total, msg, color='default', file=sys.stdout): self._obj = ProgressBar(total, file=file) def __enter__(self): - self._iter = self._obj.__enter__() return self def __exit__(self, exc_type, exc_value, traceback): @@ -537,19 +929,16 @@ def update(self, value): Update the progress bar to the given value (out of the total given to the constructor. """ - if self._is_spinner: - self._iter.next() - else: - self._obj.update(value) + self._obj.update(value) -def print_code_line(line, col=None, file=sys.stdout, tabwidth=8, width=70): - u""" +def print_code_line(line, col=None, file=None, tabwidth=8, width=70): + """ Prints a line of source code, highlighting a particular character position in the line. Useful for displaying the context of error messages. - If the line is more than `width` characters, the line is truncated + If the line is more than ``width`` characters, the line is truncated accordingly and 'â€Ļ' characters are inserted at the front and/or end. @@ -564,10 +953,10 @@ def print_code_line(line, col=None, file=sys.stdout, tabwidth=8, width=70): The line of code to display col : int, optional - The character in the line to highlight. `col` must be less - than `len(line)`. + The character in the line to highlight. ``col`` must be less + than ``len(line)``. - file : writeable file-like object, optional + file : :term:`file-like (writeable)`, optional Where to write to. Defaults to `sys.stdout`. tabwidth : int, optional @@ -580,74 +969,81 @@ def print_code_line(line, col=None, file=sys.stdout, tabwidth=8, width=70): truncated. Defaults to 70 (this matches the default in the standard library's `textwrap` module). """ + if file is None: + file = sys.stdout + + if conf.unicode_output: + ellipsis = "â€Ļ" + else: + ellipsis = "..." + write = file.write if col is not None: - assert col < len(line) - ntabs = line[:col].count(u'\t') + if col >= len(line): + raise ValueError("col must be less than the line length.") + ntabs = line[:col].count("\t") col += ntabs * (tabwidth - 1) - line = line.rstrip(u'\n') - line = line.replace(u'\t', u' ' * tabwidth) + line = line.rstrip("\n") + line = line.replace("\t", " " * tabwidth) if col is not None and col > width: new_col = min(width // 2, len(line) - col) offset = col - new_col - line = line[offset + 1:] + line = line[offset + len(ellipsis) :] + width -= len(ellipsis) new_col = col col -= offset - width = width - 3 - color_print(u'â€Ļ', 'darkgrey', file=file, end=u'') + color_print(ellipsis, "darkgrey", file=file, end="") if len(line) > width: - write(line[:width - 1]) - color_print(u'â€Ļ', 'darkgrey', file=file) + write(line[: width - len(ellipsis)]) + color_print(ellipsis, "darkgrey", file=file) else: write(line) - write(u'\n') + write("\n") if col is not None: - write(u' ' * col) - color_print(u'^', 'red', file=file) + write(" " * col) + color_print("^", "red", file=file) -# The following four Getch* classes implement unbuffered character reading from -# stdin on Windows, linux, MacOSX. This is taken directly from ActiveState +# The following three Getch* classes implement unbuffered character reading from +# stdin on Windows, and Unix. This is taken directly from ActiveState # Code Recipes: # http://code.activestate.com/recipes/134892-getch-like-unbuffered-character-reading-from-stdin/ # -class Getch(object): + +class Getch: """Get a single character from standard input without screen echo. Returns ------- char : str (one character) """ + def __init__(self): try: self.impl = _GetchWindows() except ImportError: - try: - self.impl = _GetchMacCarbon() - except (ImportError, AttributeError): - self.impl = _GetchUnix() + self.impl = _GetchUnix() def __call__(self): return self.impl() -class _GetchUnix(object): +class _GetchUnix: def __init__(self): - import tty - import sys - import termios # import termios now or else you'll get the Unix - # version on the Mac + import sys # noqa: F401 + import tty # noqa: F401 def __call__(self): import sys - import tty import termios + import tty + fd = sys.stdin.fileno() old_settings = termios.tcgetattr(fd) try: @@ -658,39 +1054,11 @@ def __call__(self): return ch -class _GetchWindows(object): +class _GetchWindows: def __init__(self): - import msvcrt + import msvcrt # noqa: F401 def __call__(self): import msvcrt - return msvcrt.getch() - -class _GetchMacCarbon: - """ - A function which returns the current ASCII key that is down; - if no ASCII key is down, the null string is returned. The - page http://www.mactech.com/macintosh-c/chap02-1.html was - very helpful in figuring out how to do this. - """ - def __init__(self): - import Carbon - Carbon.Evt # see if it has this (in Unix, it doesn't) - - def __call__(self): - import Carbon - if Carbon.Evt.EventAvail(0x0008)[0] == 0: # 0x0008 is the keyDownMask - return '' - else: - # - # The event contains the following info: - # (what,msg,when,where,mod)=Carbon.Evt.GetNextEvent(0x0008)[1] - # - # The message (msg) contains the ASCII char which is - # extracted with the 0x000000FF charCodeMask; this - # number is converted to an ASCII character with chr() and - # returned - # - (what, msg, when, where, mod) = Carbon.Evt.GetNextEvent(0x0008)[1] - return chr(msg & 0x000000FF) + return msvcrt.getch() diff --git a/astropy/utils/data.py b/astropy/utils/data.py new file mode 100644 index 000000000000..ee3c2dac2d95 --- /dev/null +++ b/astropy/utils/data.py @@ -0,0 +1,2450 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +"""Functions for accessing, downloading, and caching data files.""" + +import atexit +import contextlib +import errno +import fnmatch +import ftplib +import functools +import hashlib +import io +import os +import re +import shutil +import sys +import urllib.error +import urllib.parse +import urllib.request +import warnings +import zipfile +from collections.abc import Container, Generator, Iterable +from contextlib import suppress +from importlib import import_module +from pathlib import Path +from tempfile import NamedTemporaryFile, TemporaryDirectory, gettempdir +from types import MappingProxyType +from typing import Literal, assert_never, overload +from warnings import warn + +import astropy_iers_data + +import astropy.config.paths +from astropy import config as _config +from astropy.units import Quantity, UnitBase +from astropy.utils.compat.optional_deps import ( + HAS_BZ2, + HAS_CERTIFI, + HAS_FSSPEC, + HAS_LZMA, + HAS_UNCOMPRESSPY, +) +from astropy.utils.exceptions import AstropyDeprecationWarning, AstropyWarning +from astropy.utils.introspection import find_current_module + +# Order here determines order in the autosummary +__all__ = [ + "CacheDamaged", + "CacheMissingWarning", + "Conf", + "cache_contents", + "cache_total_size", + "check_download_cache", + "check_free_space_in_dir", + "clear_download_cache", + "compute_hash", + "conf", + "download_file", + "download_files_in_parallel", + "export_download_cache", + "get_cached_urls", + "get_file_contents", + "get_free_space_in_dir", + "get_pkg_data_contents", + "get_pkg_data_filename", + "get_pkg_data_filenames", + "get_pkg_data_fileobj", + "get_pkg_data_fileobjs", + "get_pkg_data_path", + "get_readable_fileobj", + "import_download_cache", + "import_file_to_cache", + "is_url", + "is_url_in_cache", +] + +_dataurls_to_alias = {} + + +_IERS_DATA_REDIRECTS = { + "Leap_Second.dat": ( + "IERS_LEAP_SECOND_FILE", + astropy_iers_data.IERS_LEAP_SECOND_FILE, + ), + "ReadMe.finals2000A": ("IERS_A_README", astropy_iers_data.IERS_A_README), + "ReadMe.eopc04": ("IERS_B_README", astropy_iers_data.IERS_B_README), + "eopc04.1962-now": ("IERS_B_FILE", astropy_iers_data.IERS_B_FILE), +} + + +class _NonClosingBufferedReader(io.BufferedReader): + def __del__(self): + try: + # NOTE: self.raw will not be closed, but left in the state + # it was in at detachment + self.detach() + except Exception: + pass + + +class _NonClosingTextIOWrapper(io.TextIOWrapper): + def __del__(self): + try: + # NOTE: self.stream will not be closed, but left in the state + # it was in at detachment + self.detach() + except Exception: + pass + + +class Conf(_config.ConfigNamespace): + """ + Configuration parameters for `astropy.utils.data`. + """ + + dataurl = _config.ConfigItem( + "http://data.astropy.org/", "Primary URL for astropy remote data site." + ) + dataurl_mirror = _config.ConfigItem( + "http://www.astropy.org/astropy-data/", + "Mirror URL for astropy remote data site.", + ) + default_http_user_agent = _config.ConfigItem( + "astropy", + "Default User-Agent for HTTP request headers. This can be overwritten " + "for a particular call via http_headers option, where available. " + "This only provides the default value when not set by https_headers.", + ) + remote_timeout = _config.ConfigItem( + 10.0, + "Time to wait for remote data queries (in seconds).", + aliases=["astropy.coordinates.name_resolve.name_resolve_timeout"], + ) + allow_internet = _config.ConfigItem( + True, "If False, prevents any attempt to download from Internet." + ) + compute_hash_block_size = _config.ConfigItem( + 2**16, # 64K + "Block size for computing file hashes.", + ) + download_block_size = _config.ConfigItem( + 2**16, # 64K + "Number of bytes of remote data to download per step.", + ) + delete_temporary_downloads_at_exit = _config.ConfigItem( + True, + "If True, temporary download files created when the cache is " + "inaccessible will be deleted at the end of the python session.", + ) + + +conf = Conf() + + +class CacheMissingWarning(AstropyWarning): + """ + This warning indicates the standard cache directory is not accessible, with + the first argument providing the warning message. If args[1] is present, it + is a filename indicating the path to a temporary file that was created to + store a remote data download in the absence of the cache. + """ + + +def is_url(string): + """ + Test whether a string is a valid URL for :func:`download_file`. + + Parameters + ---------- + string : str + The string to test. + + Returns + ------- + status : bool + String is URL or not. + + """ + url = urllib.parse.urlparse(string) + # we can't just check that url.scheme is not an empty string, because + # file paths in windows would return a non-empty scheme (e.g. e:\\ + # returns 'e'). + return url.scheme.lower() in ["http", "https", "ftp", "sftp", "ssh", "file"] + + +# Backward compatibility because some downstream packages allegedly use it. +_is_url = is_url + + +def _requires_fsspec(url): + """Does the `url` require the optional ``fsspec`` dependency to open?""" + return isinstance(url, str) and url.startswith(("s3://", "gs://")) + + +def _is_inside(path, parent_path): + # We have to try realpath too to avoid issues with symlinks, but we leave + # abspath because some systems like debian have the absolute path (with no + # symlinks followed) match, but the real directories in different + # locations, so need to try both cases. + return os.path.abspath(path).startswith( + os.path.abspath(parent_path) + ) or os.path.realpath(path).startswith(os.path.realpath(parent_path)) + + +@contextlib.contextmanager +def get_readable_fileobj( + name_or_obj, + encoding=None, + cache=False, + show_progress=True, + remote_timeout=None, + sources=None, + http_headers=None, + *, + use_fsspec=None, + fsspec_kwargs=None, + fsspec_filesystem=None, + close_files=True, +): + """Yield a readable, seekable file-like object from a file or URL. + + This supports passing filenames, URLs, and readable file-like objects, + any of which can be compressed in gzip, bzip2, lzma (xz) or lzw (Z) if the + appropriate compression libraries are provided by the Python installation. + + Notes + ----- + This function is a context manager, and should be used for example + as:: + + with get_readable_fileobj('file.dat') as f: + contents = f.read() + + If a URL is provided and the cache is in use, the provided URL will be the + name used in the cache. The contents may already be stored in the cache + under this URL provided, they may be downloaded from this URL, or they may + be downloaded from one of the locations listed in ``sources``. See + `~download_file` for details. + + Parameters + ---------- + name_or_obj : str or file-like + The filename of the file to access (if given as a string), or + the file-like object to access. + + If a file-like object, it must be opened in binary mode. + + encoding : str, optional + When `None` (default), returns a file-like object with a + ``read`` method that returns `str` (``unicode``) objects, using + `locale.getpreferredencoding` as an encoding. This matches + the default behavior of the built-in `open` when no ``mode`` + argument is provided. + + When ``'binary'``, returns a file-like object where its ``read`` + method returns `bytes` objects. + + When another string, it is the name of an encoding, and the + file-like object's ``read`` method will return `str` (``unicode``) + objects, decoded from binary using the given encoding. + + cache : bool or "update", optional + Whether to cache the contents of remote URLs. If "update", + check the remote URL for a new version but store the result + in the cache. + + show_progress : bool, optional + Whether to display a progress bar if the file is downloaded + from a remote server. Default is `True`. + + remote_timeout : float + Timeout for remote requests in seconds (default is the configurable + `astropy.utils.data.Conf.remote_timeout`). + + sources : list of str, optional + If provided, a list of URLs to try to obtain the file from. The + result will be stored under the original URL. The original URL + will *not* be tried unless it is in this list; this is to prevent + long waits for a primary server that is known to be inaccessible + at the moment. + + http_headers : dict or None + HTTP request headers to pass into ``urlopen`` if needed. (These headers + are ignored if the protocol for the ``name_or_obj``/``sources`` entry + is not a remote HTTP URL.) In the default case (None), the headers are + ``User-Agent: some_value`` and ``Accept: */*``, where ``some_value`` + is set by ``astropy.utils.data.conf.default_http_user_agent``. + + use_fsspec : bool, optional + Use `fsspec.open` to open the file? Defaults to `False` unless + ``name_or_obj`` starts with the Amazon S3 storage prefix ``s3://`` + or the Google Cloud Storage prefix ``gs://``. Can also be used for paths + with other prefixes (e.g. ``http://``) but in this case you must + explicitly pass ``use_fsspec=True``. + Use of this feature requires the optional ``fsspec`` package. + A ``ModuleNotFoundError`` will be raised if the dependency is missing. + + .. versionadded:: 5.2 + + fsspec_kwargs : dict, optional + Keyword arguments passed on to `fsspec.open`. This can be used to + configure cloud storage credentials and caching behavior. + For example, pass ``fsspec_kwargs={"anon": True}`` to enable + anonymous access to Amazon S3 open data buckets. + See ``fsspec``'s documentation for available parameters. + Finer control over ``fsspec`` filesystem configuration is + available through the ``fsspec_filesystem`` keyword argument. + + .. versionadded:: 5.2 + + fsspec_filesystem : `fsspec.spec.AbstractFileSystem`, optional + A ``filesystem`` instance initialized by the user, for example, + via `fsspec.spec.AbstractFileSystem`. Files will be opened by calling + `fsspec.spec.AbstractFileSystem.open` on ``fsspec_filesystem``, + giving the user a way to set, for example, the filesystem's + ``protocol``, ``block_size``, and ``cache_type``. See + ``fsspec``'s documentation for available parameters. + + .. versionadded:: 8.0 + + close_files : bool, optional + Close the file object when exiting the context manager. + Default is `True`. + + .. versionadded:: 5.2 + + Returns + ------- + file : :term:`file-like (readable)` + """ + # close_fds is a list of file handles created by this function + # that need to be closed. We don't want to always just close the + # returned file handle, because it may simply be the file handle + # passed in. In that case it is not the responsibility of this + # function to close it: doing so could result in a "double close" + # and an "invalid file descriptor" exception. + + close_fds = [] + delete_fds = [] + + if remote_timeout is None: + # use configfile default + remote_timeout = conf.remote_timeout + + # Have `use_fsspec` default to ``True`` if the user passed an Amazon S3 + # or Google Cloud Storage URI. + if use_fsspec is None and _requires_fsspec(name_or_obj): + use_fsspec = True + + if use_fsspec: + if not isinstance(name_or_obj, str): + raise TypeError("`name_or_obj` must be a string when `use_fsspec=True`") + if fsspec_kwargs is None: + fsspec_kwargs = {} + + # name_or_obj could be an os.PathLike object + if isinstance(name_or_obj, os.PathLike): + name_or_obj = os.fspath(name_or_obj) + + # Get a file object to the content + if isinstance(name_or_obj, str): + # Use fsspec to open certain cloud-hosted files (e.g., AWS S3, Google Cloud Storage) + if use_fsspec: + if not HAS_FSSPEC: + raise ModuleNotFoundError("please install `fsspec` to open this file") + + import fsspec # local import because it is a niche dependency + + if fsspec_filesystem: + fileobj = fsspec_filesystem.open(name_or_obj, **fsspec_kwargs) + else: + openfileobj = fsspec.open(name_or_obj, **fsspec_kwargs) + close_fds.append(openfileobj) + fileobj = openfileobj.open() + + close_fds.append(fileobj) + + else: + is_url = _is_url(name_or_obj) + if is_url: + name_or_obj = download_file( + name_or_obj, + cache=cache, + show_progress=show_progress, + timeout=remote_timeout, + sources=sources, + http_headers=http_headers, + ) + fileobj = io.FileIO(name_or_obj, "r") + if is_url and not cache: + delete_fds.append(fileobj) + close_fds.append(fileobj) + else: + fileobj = name_or_obj + + # Check if the file object supports random access, and if not, + # then wrap it in a BytesIO buffer. It would be nicer to use a + # BufferedReader to avoid loading the whole file first, + # but that might not be compatible with all possible I/O classes. + if not hasattr(fileobj, "seek"): + try: + # py.path.LocalPath objects have .read() method but it uses + # text mode, which won't work. .read_binary() does, and + # surely other ducks would return binary contents when + # called like this. + # py.path.LocalPath is what comes from the legacy tmpdir fixture + # in pytest. + fileobj = io.BytesIO(fileobj.read_binary()) + except AttributeError: + fileobj = io.BytesIO(fileobj.read()) + + # Now read enough bytes to look at signature + signature = fileobj.read(6) + fileobj.seek(0) + + if signature[:3] == b"\x1f\x8b\x08": # gzip + import struct + + try: + import gzip + + fileobj_new = gzip.GzipFile(fileobj=fileobj, mode="rb") + fileobj_new.read(1) # need to check that the file is really gzip + except (OSError, EOFError, struct.error): # invalid gzip file + fileobj.seek(0) + fileobj_new.close() + else: + fileobj_new.seek(0) + fileobj = fileobj_new + elif signature[:3] == b"BZh": # bzip2 + if not HAS_BZ2: + for fd in close_fds: + fd.close() + raise ModuleNotFoundError( + "This Python installation does not provide the bz2 module." + ) + import bz2 + + try: + # bz2.BZ2File does not support file objects, only filenames, so we + # need to write the data to a temporary file + with NamedTemporaryFile("wb", delete=False) as tmp: + tmp.write(fileobj.read()) + tmp.close() + fileobj_new = bz2.BZ2File(tmp.name, mode="rb") + fileobj_new.read(1) # need to check that the file is really bzip2 + except OSError: # invalid bzip2 file + fileobj.seek(0) + fileobj_new.close() + # raise + else: + fileobj_new.seek(0) + close_fds.append(fileobj_new) + fileobj = fileobj_new + elif signature[:6] == b"\xfd7zXZ\x00": # xz + if not HAS_LZMA: + for fd in close_fds: + fd.close() + raise ModuleNotFoundError( + "This Python installation does not provide the lzma module." + ) + import lzma + + try: + fileobj_new = lzma.LZMAFile(fileobj, mode="rb") + fileobj_new.read(1) # need to check that the file is really xz + except lzma.LZMAError: # invalid xz file + fileobj.seek(0) + fileobj_new.close() + # should we propagate this to the caller to signal bad content? + # raise ValueError(e) + else: + fileobj_new.seek(0) + fileobj = fileobj_new + elif signature[:2] == b"\x1f\x9d": # LZW + if not HAS_UNCOMPRESSPY: + for fd in close_fds: + fd.close() + raise ModuleNotFoundError( + "The optional package uncompresspy is necessary for reading LZW" + " compressed files (.Z extension)." + ) + import uncompresspy + + try: + fileobj_new = uncompresspy.LZWFile(fileobj) + fileobj_new.read(1) + except ValueError: + fileobj.seek(0) + fileobj_new.close() + else: + fileobj_new.seek(0) + close_fds.append(fileobj) + fileobj = fileobj_new + + # By this point, we have a file, io.FileIO, gzip.GzipFile, bz2.BZ2File, + # lzma.LZMAFile or uncompresspy.LZWFile instance opened in binary mode (that + # is, read returns bytes). Now we need to, if requested, wrap it in a + # io.TextIOWrapper so read will return unicode based on the + # encoding parameter. + + needs_textio_wrapper = encoding != "binary" + + if needs_textio_wrapper: + # A bz2.BZ2File can not be wrapped by a TextIOWrapper, + # so we decompress it to a temporary file and then + # return a handle to that. + if HAS_BZ2: + import bz2 + + if isinstance(fileobj, bz2.BZ2File): + tmp = NamedTemporaryFile("wb", delete=False) + data = fileobj.read() + tmp.write(data) + tmp.close() + delete_fds.append(tmp) + + fileobj = io.FileIO(tmp.name, "r") + close_fds.append(fileobj) + + fileobj = _NonClosingBufferedReader(fileobj) + fileobj = _NonClosingTextIOWrapper(fileobj, encoding=encoding) + + # Ensure that file is at the start - io.FileIO will for + # example not always be at the start: + # >>> import io + # >>> f = open('test.fits', 'rb') + # >>> f.read(4) + # 'SIMP' + # >>> f.seek(0) + # >>> fileobj = io.FileIO(f.fileno()) + # >>> fileobj.tell() + # 4096L + + fileobj.seek(0) + + try: + yield fileobj + finally: + if close_files: + for fd in close_fds: + fd.close() + for fd in delete_fds: + os.remove(fd.name) + + +def get_file_contents(*args, **kwargs): + """ + Retrieves the contents of a filename or file-like object. + + See the `get_readable_fileobj` docstring for details on parameters. + + Returns + ------- + object + The content of the file (as requested by ``encoding``). + """ + with get_readable_fileobj(*args, **kwargs) as f: + return f.read() + + +@contextlib.contextmanager +def get_pkg_data_fileobj(data_name, package=None, encoding=None, cache=True): + """ + Retrieves a data file from the standard locations for the package and + provides the file as a file-like object that reads bytes. + + Parameters + ---------- + data_name : str + Name/location of the desired data file. One of the following: + + * The name of a data file included in the source + distribution. The path is relative to the module + calling this function. For example, if calling from + ``astropy.pkname``, use ``'data/file.dat'`` to get the + file in ``astropy/pkgname/data/file.dat``. Double-dots + can be used to go up a level. In the same example, use + ``'../data/file.dat'`` to get ``astropy/data/file.dat``. + * If a matching local file does not exist, the Astropy + data server will be queried for the file. + * A hash like that produced by `compute_hash` can be + requested, prefixed by 'hash/' + e.g. 'hash/34c33b3eb0d56eb9462003af249eff28'. The hash + will first be searched for locally, and if not found, + the Astropy data server will be queried. + + package : str, optional + If specified, look for a file relative to the given package, rather + than the default of looking relative to the calling module's package. + + encoding : str, optional + When `None` (default), returns a file-like object with a + ``read`` method returns `str` (``unicode``) objects, using + `locale.getpreferredencoding` as an encoding. This matches + the default behavior of the built-in `open` when no ``mode`` + argument is provided. + + When ``'binary'``, returns a file-like object where its ``read`` + method returns `bytes` objects. + + When another string, it is the name of an encoding, and the + file-like object's ``read`` method will return `str` (``unicode``) + objects, decoded from binary using the given encoding. + + cache : bool + If True, the file will be downloaded and saved locally or the + already-cached local copy will be accessed. If False, the + file-like object will directly access the resource (e.g. if a + remote URL is accessed, an object like that from + `urllib.request.urlopen` is returned). + + Returns + ------- + fileobj : file-like + An object with the contents of the data file available via + ``read`` function. Can be used as part of a ``with`` statement, + automatically closing itself after the ``with`` block. + + Raises + ------ + urllib.error.URLError + If a remote file cannot be found. + OSError + If problems occur writing or reading a local file. + + Examples + -------- + This will retrieve a data file and its contents for the `astropy.wcs` + tests:: + + >>> from astropy.utils.data import get_pkg_data_fileobj + >>> with get_pkg_data_fileobj('data/3d_cd.hdr', + ... package='astropy.wcs.tests') as fobj: + ... fcontents = fobj.read() + ... + + This next example would download a data file from the astropy data server + because the ``allsky/allsky_rosat.fits`` file is not present in the + source distribution. It will also save the file locally so the + next time it is accessed it won't need to be downloaded.:: + + >>> from astropy.utils.data import get_pkg_data_fileobj + >>> with get_pkg_data_fileobj('allsky/allsky_rosat.fits', + ... encoding='binary') as fobj: # doctest: +REMOTE_DATA +IGNORE_OUTPUT + ... fcontents = fobj.read() + ... + Downloading http://data.astropy.org/allsky/allsky_rosat.fits [Done] + + This does the same thing but does *not* cache it locally:: + + >>> with get_pkg_data_fileobj('allsky/allsky_rosat.fits', + ... encoding='binary', cache=False) as fobj: # doctest: +REMOTE_DATA +IGNORE_OUTPUT + ... fcontents = fobj.read() + ... + Downloading http://data.astropy.org/allsky/allsky_rosat.fits [Done] + + See Also + -------- + get_pkg_data_contents : returns the contents of a file or url as a bytes object + get_pkg_data_filename : returns a local name for a file containing the data + """ + datafn = get_pkg_data_path(data_name, package=package) + if os.path.isdir(datafn): + raise OSError( + "Tried to access a data file that's actually a package data directory" + ) + elif os.path.isfile(datafn): # local file + with get_readable_fileobj(datafn, encoding=encoding) as fileobj: + yield fileobj + else: # remote file + with get_readable_fileobj( + conf.dataurl + data_name, + encoding=encoding, + cache=cache, + sources=[conf.dataurl + data_name, conf.dataurl_mirror + data_name], + ) as fileobj: + # We read a byte to trigger any URLErrors + fileobj.read(1) + fileobj.seek(0) + yield fileobj + + +def get_pkg_data_filename( + data_name: str, + package: str | None = None, + show_progress: bool = True, + remote_timeout: float | None = None, +) -> str: + """ + Retrieves a data file from the standard locations for the package and + provides a local filename for the data. + + This function is similar to `get_pkg_data_fileobj` but returns the + file *name* instead of a readable file-like object. This means + that this function must always cache remote files locally, unlike + `get_pkg_data_fileobj`. + + Parameters + ---------- + data_name : str + Name/location of the desired data file. One of the following: + + * The name of a data file included in the source + distribution. The path is relative to the module + calling this function. For example, if calling from + ``astropy.pkname``, use ``'data/file.dat'`` to get the + file in ``astropy/pkgname/data/file.dat``. Double-dots + can be used to go up a level. In the same example, use + ``'../data/file.dat'`` to get ``astropy/data/file.dat``. + * If a matching local file does not exist, the Astropy + data server will be queried for the file. + * A hash like that produced by `compute_hash` can be + requested, prefixed by 'hash/' + e.g. 'hash/34c33b3eb0d56eb9462003af249eff28'. The hash + will first be searched for locally, and if not found, + the Astropy data server will be queried. + + package : str, optional + If specified, look for a file relative to the given package, rather + than the default of looking relative to the calling module's package. + + show_progress : bool, optional + Whether to display a progress bar if the file is downloaded + from a remote server. Default is `True`. + + remote_timeout : float + Timeout for the requests in seconds (default is the + configurable `astropy.utils.data.Conf.remote_timeout`). + + Raises + ------ + urllib.error.URLError + If a remote file cannot be found. + OSError + If problems occur writing or reading a local file. + + Returns + ------- + filename : str + A file path on the local file system corresponding to the data + requested in ``data_name``. + + Examples + -------- + This will retrieve the contents of the data file for the `astropy.wcs` + tests:: + + >>> from astropy.utils.data import get_pkg_data_filename + >>> fn = get_pkg_data_filename('data/3d_cd.hdr', + ... package='astropy.wcs.tests') + >>> with open(fn) as f: + ... fcontents = f.read() + ... + + This retrieves a data file by hash either locally or from the astropy data + server:: + + >>> from astropy.utils.data import get_pkg_data_filename + >>> fn = get_pkg_data_filename('hash/34c33b3eb0d56eb9462003af249eff28') # doctest: +SKIP + >>> with open(fn) as f: + ... fcontents = f.read() + ... + + See Also + -------- + get_pkg_data_contents : returns the contents of a file or url as a bytes object + get_pkg_data_fileobj : returns a file-like object with the data + """ + if remote_timeout is None: + # use configfile default + remote_timeout = conf.remote_timeout + + if data_name.startswith("hash/"): + # first try looking for a local version if a hash is specified + hashfn = _find_hash_fn(data_name[5:]) + + if hashfn is None: + return download_file( + conf.dataurl + data_name, + cache=True, + show_progress=show_progress, + timeout=remote_timeout, + sources=[conf.dataurl + data_name, conf.dataurl_mirror + data_name], + ) + else: + return hashfn + else: + fs_path = os.path.normpath(data_name) + datafn = get_pkg_data_path(fs_path, package=package) + if os.path.isdir(datafn): + raise OSError( + "Tried to access a data file that's actually a package data directory" + ) + elif os.path.isfile(datafn): # local file + return datafn + else: # remote file + return download_file( + conf.dataurl + data_name, + cache=True, + show_progress=show_progress, + timeout=remote_timeout, + sources=[conf.dataurl + data_name, conf.dataurl_mirror + data_name], + ) + + +def get_pkg_data_contents( + data_name: str, + package: str | None = None, + encoding: str | None = None, + cache: bool = True, +) -> bytes: + """ + Retrieves a data file from the standard locations and returns its + contents as a bytes object. + + Parameters + ---------- + data_name : str + Name/location of the desired data file. One of the following: + + * The name of a data file included in the source + distribution. The path is relative to the module + calling this function. For example, if calling from + ``astropy.pkname``, use ``'data/file.dat'`` to get the + file in ``astropy/pkgname/data/file.dat``. Double-dots + can be used to go up a level. In the same example, use + ``'../data/file.dat'`` to get ``astropy/data/file.dat``. + * If a matching local file does not exist, the Astropy + data server will be queried for the file. + * A hash like that produced by `compute_hash` can be + requested, prefixed by 'hash/' + e.g. 'hash/34c33b3eb0d56eb9462003af249eff28'. The hash + will first be searched for locally, and if not found, + the Astropy data server will be queried. + * A URL to some other file. + + package : str, optional + If specified, look for a file relative to the given package, rather + than the default of looking relative to the calling module's package. + + + encoding : str, optional + When `None` (default), returns a file-like object with a + ``read`` method that returns `str` (``unicode``) objects, using + `locale.getpreferredencoding` as an encoding. This matches + the default behavior of the built-in `open` when no ``mode`` + argument is provided. + + When ``'binary'``, returns a file-like object where its ``read`` + method returns `bytes` objects. + + When another string, it is the name of an encoding, and the + file-like object's ``read`` method will return `str` (``unicode``) + objects, decoded from binary using the given encoding. + + cache : bool + If True, the file will be downloaded and saved locally or the + already-cached local copy will be accessed. If False, the + file-like object will directly access the resource (e.g. if a + remote URL is accessed, an object like that from + `urllib.request.urlopen` is returned). + + Returns + ------- + contents : bytes + The complete contents of the file as a bytes object. + + Raises + ------ + urllib.error.URLError + If a remote file cannot be found. + OSError + If problems occur writing or reading a local file. + + See Also + -------- + get_pkg_data_fileobj : returns a file-like object with the data + get_pkg_data_filename : returns a local name for a file containing the data + """ + with get_pkg_data_fileobj( + data_name, package=package, encoding=encoding, cache=cache + ) as fd: + contents = fd.read() + return contents + + +def get_pkg_data_filenames( + datadir: str, + package: str | None = None, + pattern: str = "*", +) -> Generator[str, None, None]: + """ + Returns the path of all of the data files in a given directory + that match a given glob pattern. + + Parameters + ---------- + datadir : str + Name/location of the desired data files. One of the following: + + * The name of a directory included in the source + distribution. The path is relative to the module + calling this function. For example, if calling from + ``astropy.pkname``, use ``'data'`` to get the + files in ``astropy/pkgname/data``. + * Remote URLs are not currently supported. + + package : str, optional + If specified, look for a file relative to the given package, rather + than the default of looking relative to the calling module's package. + + pattern : str, optional + A UNIX-style filename glob pattern to match files. See the + `glob` module in the standard library for more information. + By default, matches all files. + + Returns + ------- + filenames : iterator of str + Paths on the local filesystem in *datadir* matching *pattern*. + + Examples + -------- + This will retrieve the contents of the data file for the `astropy.wcs` + tests:: + + >>> from astropy.utils.data import get_pkg_data_filenames + >>> for fn in get_pkg_data_filenames('data/maps', 'astropy.wcs.tests', + ... '*.hdr'): + ... with open(fn) as f: + ... fcontents = f.read() + ... + """ + path = get_pkg_data_path(datadir, package=package) + if os.path.isfile(path): + raise OSError( + "Tried to access a data directory that's actually a package data file" + ) + elif os.path.isdir(path): + for filename in os.listdir(path): + if fnmatch.fnmatch(filename, pattern): + yield os.path.join(path, filename) + else: + raise OSError("Path not found") + + +def get_pkg_data_fileobjs(datadir, package=None, pattern="*", encoding=None): + """ + Returns readable file objects for all of the data files in a given + directory that match a given glob pattern. + + Parameters + ---------- + datadir : str + Name/location of the desired data files. One of the following: + + * The name of a directory included in the source + distribution. The path is relative to the module + calling this function. For example, if calling from + ``astropy.pkname``, use ``'data'`` to get the + files in ``astropy/pkgname/data`` + * Remote URLs are not currently supported + + package : str, optional + If specified, look for a file relative to the given package, rather + than the default of looking relative to the calling module's package. + + pattern : str, optional + A UNIX-style filename glob pattern to match files. See the + `glob` module in the standard library for more information. + By default, matches all files. + + encoding : str, optional + When `None` (default), returns a file-like object with a + ``read`` method that returns `str` (``unicode``) objects, using + `locale.getpreferredencoding` as an encoding. This matches + the default behavior of the built-in `open` when no ``mode`` + argument is provided. + + When ``'binary'``, returns a file-like object where its ``read`` + method returns `bytes` objects. + + When another string, it is the name of an encoding, and the + file-like object's ``read`` method will return `str` (``unicode``) + objects, decoded from binary using the given encoding. + + Returns + ------- + fileobjs : iterator of file object + File objects for each of the files on the local filesystem in + *datadir* matching *pattern*. + + Examples + -------- + This will retrieve the contents of the data file for the `astropy.wcs` + tests:: + + >>> from astropy.utils.data import get_pkg_data_filenames + >>> for fd in get_pkg_data_fileobjs('data/maps', 'astropy.wcs.tests', + ... '*.hdr'): + ... fcontents = fd.read() + ... + """ + for fn in get_pkg_data_filenames(datadir, package=package, pattern=pattern): + with get_readable_fileobj(fn, encoding=encoding) as fd: + yield fd + + +def compute_hash(localfn: str) -> str: + """Computes the MD5 hash for a file. + + The hash for a data file is used for looking up data files in a unique + fashion. This is of particular use for tests; a test may require a + particular version of a particular file, in which case it can be accessed + via hash to get the appropriate version. + + Typically, if you wish to write a test that requires a particular data + file, you will want to submit that file to the astropy data servers, and + use + e.g. ``get_pkg_data_filename('hash/34c33b3eb0d56eb9462003af249eff28')``, + but with the hash for your file in place of the hash in the example. + + Parameters + ---------- + localfn : str + The path to the file for which the hash should be generated. + + Returns + ------- + hash : str + The hex digest of the cryptographic hash for the contents of the + ``localfn`` file. + """ + with open(localfn, "rb") as f: + h = hashlib.md5(usedforsecurity=False) + block = f.read(conf.compute_hash_block_size) + while block: + h.update(block) + block = f.read(conf.compute_hash_block_size) + + return h.hexdigest() + + +def get_pkg_data_path( + *path: str, + package: str | None = None, +) -> str: + """Get path from source-included data directories. + + Parameters + ---------- + *path : str + Name/location of the desired data file/directory. + May be a tuple of strings for ``os.path`` joining. + + package : str or None, optional, keyword-only + If specified, look for a file relative to the given package, rather + than the calling module's package. + + Returns + ------- + path : str + Name/location of the desired data file/directory. + + Raises + ------ + ImportError + Given package or module is not importable. + RuntimeError + If the local data file is outside of the package's tree. + + """ + if package is None: + module = find_current_module(1, finddiff=["astropy.utils.data", "contextlib"]) + if module is None: + # not called from inside an astropy package. So just pass name + # through + return os.path.join(*path) + + if not hasattr(module, "__package__") or not module.__package__: + # The __package__ attribute may be missing or set to None; see + # PEP-366, also astropy issue #1256 + if "." in module.__name__: + package = module.__name__.rpartition(".")[0] + else: + package = module.__name__ + else: + package = module.__package__ + else: + # Backward-compatibility for files that used to exist in astropy.utils.iers + if package == "astropy.utils.iers": + filename = os.path.basename(path[-1]) + if filename in _IERS_DATA_REDIRECTS: + warn( + f"Accessing {filename} in this way is deprecated in v6.0, " + f"use astropy.utils.iers.{_IERS_DATA_REDIRECTS[filename][0]} " + "instead.", + AstropyDeprecationWarning, + stacklevel=2, + ) + return _IERS_DATA_REDIRECTS[filename][1] + + # package errors if it isn't a str + # so there is no need for checks in the containing if/else + module = import_module(package) + + # module path within package + module_path = os.path.dirname(module.__file__) + full_path = os.path.join(module_path, *path) + + # Check that file is inside tree. + rootpkgname = package.partition(".")[0] + root_dir = os.path.dirname(import_module(rootpkgname).__file__) + if not _is_inside(full_path, root_dir): + raise RuntimeError( + f"attempted to get a local data file outside of the {rootpkgname} tree." + ) + + return full_path + + +def _find_hash_fn( + hexdigest: str, + pkgname: str = "astropy", +) -> str | None: + """ + Looks for a local file by hash - returns file name if found and a valid + file, otherwise returns None. + """ + for v in cache_contents(pkgname=pkgname).values(): + if compute_hash(v) == hexdigest: + return v + return None + + +@overload +def get_free_space_in_dir( + path: str | os.PathLike[str], unit: Literal[False] +) -> int: ... +@overload +def get_free_space_in_dir( + path: str | os.PathLike[str], unit: UnitBase | Literal[True] +) -> Quantity: ... + + +def get_free_space_in_dir(path: str | os.PathLike[str], unit: UnitBase | bool = False): + """ + Given a path to a directory, returns the amount of free space + on that filesystem. + + Parameters + ---------- + path : str, os.PathLike + The path to a directory. + + unit : bool or `~astropy.units.Unit` + Return the amount of free space as Quantity in the given unit, + if provided. Default is `False` for backward-compatibility. + + Returns + ------- + free_space : int or `~astropy.units.Quantity` + The amount of free space on the partition that the directory is on. + If ``unit=False``, it is returned as plain integer (in bytes). + + """ + if (path := Path(path)).is_file(): + raise OSError( + "Can only determine free space associated with directories, not files." + ) + # Actually you can on Linux but I want to avoid code that fails + # on Windows only. + free_space = shutil.disk_usage(path).free + if unit: + from astropy import units as u + + # TODO: Automatically determine best prefix to use. + if unit is True: + unit = u.byte + free_space = u.Quantity(free_space, u.byte).to(unit) + return free_space + + +def check_free_space_in_dir( + path: str | os.PathLike[str], + size: int | Quantity, +) -> None: + """ + Determines if a given directory has enough space to hold a file of + a given size. + + Parameters + ---------- + path : str, os.PathLike[str] + The path to a directory. + + size : int or `~astropy.units.Quantity` + A proposed filesize. If not a Quantity, assume it is in bytes. + + Raises + ------ + OSError + There is not enough room on the filesystem. + """ + space = get_free_space_in_dir(path, unit=getattr(size, "unit", False)) + if space < size: + from astropy.utils.console import human_file_size + + raise OSError( + f"Not enough free space in {path} " + f"to download a {human_file_size(size)} file, " + f"only {human_file_size(space)} left" + ) + + +class _ftptlswrapper(urllib.request.ftpwrapper): + def init(self): + self.busy = 0 + self.ftp = ftplib.FTP_TLS() + self.ftp.connect(self.host, self.port, self.timeout) + self.ftp.login(self.user, self.passwd) + self.ftp.prot_p() + _target = "/".join(self.dirs) + self.ftp.cwd(_target) + + +class _FTPTLSHandler(urllib.request.FTPHandler): + def connect_ftp(self, user, passwd, host, port, dirs, timeout): + return _ftptlswrapper(user, passwd, host, port, dirs, timeout, persistent=False) + + +@functools.lru_cache +def _build_urlopener( + ftp_tls: bool = False, ssl_context=None, allow_insecure: bool = False +) -> urllib.request.OpenerDirector: + """ + Helper for building a `urllib.request.build_opener` which handles TLS/SSL. + """ + # Import ssl here to avoid import failure when running in pyodide/Emscripten + import ssl + + ssl_context = dict(it for it in ssl_context) if ssl_context else {} + cert_chain = {} + if "certfile" in ssl_context: + cert_chain.update( + { + "certfile": ssl_context.pop("certfile"), + "keyfile": ssl_context.pop("keyfile", None), + "password": ssl_context.pop("password", None), + } + ) + elif "password" in ssl_context or "keyfile" in ssl_context: + raise ValueError( + "passing 'keyfile' or 'password' in the ssl_context argument " + "requires passing 'certfile' as well" + ) + + if "cafile" not in ssl_context and HAS_CERTIFI: + import certifi + + ssl_context["cafile"] = certifi.where() + + ssl_context = ssl.create_default_context(**ssl_context) + + if allow_insecure: + ssl_context.check_hostname = False + ssl_context.verify_mode = ssl.CERT_NONE + + if cert_chain: + ssl_context.load_cert_chain(**cert_chain) + + https_handler = urllib.request.HTTPSHandler(context=ssl_context) + + if ftp_tls: + urlopener = urllib.request.build_opener(_FTPTLSHandler(), https_handler) + else: + urlopener = urllib.request.build_opener(https_handler) + + return urlopener + + +def _try_url_open( + source_url: str, + timeout: float | None = None, + http_headers=None, + ftp_tls=False, + ssl_context=None, + allow_insecure: bool = False, +): + """Helper for opening a URL while handling TLS/SSL verification issues.""" + # Import ssl here to avoid import failure when running in pyodide/Emscripten + import ssl + + # Always try first with a secure connection + # _build_urlopener uses lru_cache, so the ssl_context argument must be + # converted to a hashable type (a set of 2-tuples) + ssl_context = frozenset(ssl_context.items() if ssl_context else []) + urlopener = _build_urlopener( + ftp_tls=ftp_tls, ssl_context=ssl_context, allow_insecure=False + ) + req = urllib.request.Request(source_url, headers=http_headers) + + try: + return urlopener.open(req, timeout=timeout) + except urllib.error.URLError as exc: + reason = exc.reason + if ( + isinstance(reason, ssl.SSLError) + and reason.reason == "CERTIFICATE_VERIFY_FAILED" + ): + msg = ( + f"Verification of TLS/SSL certificate at {source_url} " + "failed: this can mean either the server is " + "misconfigured or your local root CA certificates are " + "out-of-date; in the latter case this can usually be " + 'addressed by installing the Python package "certifi" ' + "(see the documentation for astropy.utils.data.download_file)" + ) + if not allow_insecure: + msg += ( + " or in both cases you can work around this by " + "passing allow_insecure=True, but only if you " + "understand the implications; the original error " + f"was: {reason}" + ) + raise urllib.error.URLError(msg) + else: + msg += ". Re-trying with allow_insecure=True." + warn(msg, AstropyWarning, stacklevel=2) + # Try again with a new urlopener allowing insecure connections + urlopener = _build_urlopener( + ftp_tls=ftp_tls, ssl_context=ssl_context, allow_insecure=True + ) + return urlopener.open(req, timeout=timeout) + + raise + + +def _download_file_from_source( + source_url: str, + show_progress: bool = True, + timeout: float | None = None, + remote_url: str | None = None, + cache: bool = False, + pkgname: str = "astropy", + http_headers=None, + ftp_tls=None, + ssl_context=None, + allow_insecure: bool = False, +) -> Path: + from astropy.utils.console import ProgressBarOrSpinner + + if not conf.allow_internet: + raise urllib.error.URLError( + f"URL {remote_url} was supposed to be downloaded but " + f"allow_internet is {conf.allow_internet}; " + "if this is unexpected check the astropy.cfg file for the option " + "allow_internet" + ) + + if remote_url is None: + remote_url = source_url + if http_headers is None: + http_headers = {} + + if ftp_tls is None and urllib.parse.urlparse(remote_url).scheme == "ftp": + try: + return _download_file_from_source( + source_url, + show_progress=show_progress, + timeout=timeout, + remote_url=remote_url, + cache=cache, + pkgname=pkgname, + http_headers=http_headers, + ftp_tls=False, + ) + except urllib.error.URLError as e: + # e.reason might not be a string, e.g. socket.gaierror + # URLError changed to report original exception in Python 3.11 (bpo-43564) + if ( + str(e.reason) + .removeprefix("ftp error: ") + .startswith(("error_perm", "5")) + ): + ftp_tls = True + else: + raise + + with _try_url_open( + source_url, + timeout=timeout, + http_headers=http_headers, + ftp_tls=ftp_tls, + ssl_context=ssl_context, + allow_insecure=allow_insecure, + ) as remote: + info = remote.info() + try: + size = int(info["Content-Length"]) + except (KeyError, ValueError, TypeError): + size = None + + if size is not None: + check_free_space_in_dir(gettempdir(), size) + if cache and (dldir := _get_download_cache_loc(pkgname)).exists(): + check_free_space_in_dir(dldir, size) + + # If a user has overridden sys.stdout it might not have the + # isatty method, in that case assume it's not a tty + is_tty = hasattr(sys.stdout, "isatty") and sys.stdout.isatty() + if show_progress and is_tty: + progress_stream = sys.stdout + else: + progress_stream = io.StringIO() + + if source_url == remote_url: + dlmsg = f"Downloading {remote_url}" + else: + dlmsg = f"Downloading {remote_url} from {source_url}" + with ProgressBarOrSpinner(size, dlmsg, file=progress_stream) as p: + with NamedTemporaryFile( + prefix=f"astropy-download-{os.getpid()}-", delete=False + ) as f: + fp = Path(f.name) + try: + bytes_read = 0 + block = remote.read(conf.download_block_size) + while block: + f.write(block) + bytes_read += len(block) + p.update(bytes_read) + block = remote.read(conf.download_block_size) + if size is not None and bytes_read > size: + raise urllib.error.URLError( + f"File was supposed to be {size} bytes but " + f"server provides more, at least {bytes_read} " + "bytes. Download failed." + ) + if size is not None and bytes_read < size: + raise urllib.error.ContentTooShortError( + f"File was supposed to be {size} bytes but we " + f"only got {bytes_read} bytes. Download failed.", + content=None, + ) + except BaseException: + with suppress(PermissionError): + fp.unlink() + raise + return fp + + +def download_file( + remote_url: str, + cache: bool | Literal["update"] = False, + show_progress: bool = True, + timeout: float | None = None, + sources: list[str] | None = None, + pkgname: str = "astropy", + http_headers=None, + ssl_context=None, + allow_insecure: bool = False, +) -> str: + """Downloads a URL and optionally caches the result. + + It returns the filename of a file containing the URL's contents. + If ``cache=True`` and the file is present in the cache, just + returns the filename; if the file had to be downloaded, add it + to the cache. If ``cache="update"`` always download and add it + to the cache. + + The cache is effectively a dictionary mapping URLs to files; by default the + file contains the contents of the URL that is its key, but in practice + these can be obtained from a mirror (using ``sources``) or imported from + the local filesystem (using `~import_file_to_cache` or + `~import_download_cache`). Regardless, each file is regarded as + representing the contents of a particular URL, and this URL should be used + to look them up or otherwise manipulate them. + + The files in the cache directory are named according to a cryptographic + hash of their URLs (currently MD5, so hackers can cause collisions). + The modification times on these files normally indicate when they were + last downloaded from the Internet. + + Parameters + ---------- + remote_url : str + The URL of the file to download + + cache : bool or "update", optional + Whether to cache the contents of remote URLs. If "update", + always download the remote URL in case there is a new version + and store the result in the cache. + + show_progress : bool, optional + Whether to display a progress bar during the download (default + is `True`). Regardless of this setting, the progress bar is only + displayed when outputting to a terminal. + + timeout : float, optional + Timeout for remote requests in seconds (default is the configurable + `astropy.utils.data.Conf.remote_timeout`). + + sources : list of str, optional + If provided, a list of URLs to try to obtain the file from. The + result will be stored under the original URL. The original URL + will *not* be tried unless it is in this list; this is to prevent + long waits for a primary server that is known to be inaccessible + at the moment. If an empty list is passed, then ``download_file`` + will not attempt to connect to the Internet, that is, if the file + is not in the cache a KeyError will be raised. + + pkgname : `str`, optional + The package name to use to locate the download cache. i.e. for + ``pkgname='astropy'`` the default cache location is + ``~/.cache/astropy``. + + http_headers : dict or None + HTTP request headers to pass into ``urlopen`` if needed. (These headers + are ignored if the protocol for the ``name_or_obj``/``sources`` entry + is not a remote HTTP URL.) In the default case (None), the headers are + ``User-Agent: some_value`` and ``Accept: */*``, where ``some_value`` + is set by ``astropy.utils.data.conf.default_http_user_agent``. + + ssl_context : dict, optional + Keyword arguments to pass to `ssl.create_default_context` when + downloading from HTTPS or TLS+FTP sources. This can be used provide + alternative paths to root CA certificates. Additionally, if the key + ``'certfile'`` and optionally ``'keyfile'`` and ``'password'`` are + included, they are passed to `ssl.SSLContext.load_cert_chain`. This + can be used for performing SSL/TLS client certificate authentication + for servers that require it. + + allow_insecure : bool, optional + Allow downloading files over a TLS/SSL connection even when the server + certificate verification failed. When set to `True` the potentially + insecure download is allowed to proceed, but an + `~astropy.utils.exceptions.AstropyWarning` is issued. If you are + frequently getting certificate verification warnings, consider + installing or upgrading `certifi`_ package, which provides frequently + updated certificates for common root CAs (i.e., a set similar to those + used by web browsers). If installed, Astropy will use it + automatically. + + .. _certifi: https://pypi.org/project/certifi/ + + Returns + ------- + local_path : str + Returns the local path that the file was download to. + + Raises + ------ + urllib.error.URLError + Whenever there's a problem getting the remote file. + KeyError + When a file was requested from the cache but is missing and no + sources were provided to obtain it from the Internet. + + Notes + ----- + Because this function returns a filename, another process could run + `clear_download_cache` before you actually open the file, leaving + you with a filename that no longer points to a usable file. + """ + if timeout is None: + timeout = conf.remote_timeout + if sources is None: + sources = [remote_url] + if http_headers is None: + http_headers = {"User-Agent": conf.default_http_user_agent, "Accept": "*/*"} + + missing_cache = "" + + url_key = remote_url + + if cache: + try: + dldir = _get_download_cache_loc(pkgname, ensure_exists=True) + except OSError as e: + cache = False + missing_cache = ( + f"Cache directory cannot be read or created ({e}), " + "providing data in temporary file instead." + ) + else: + if cache == "update": + pass + elif isinstance(cache, str): + raise ValueError( + f"Cache value '{cache}' was requested but " + "'update' is the only recognized string; " + "otherwise use a boolean" + ) + else: + filename = dldir / _url_to_dirname(url_key) / "contents" + if filename.exists(): + return str(filename.absolute()) + + errors = {} + for source_url in sources: + try: + f_name = _download_file_from_source( + source_url, + timeout=timeout, + show_progress=show_progress, + cache=cache, + remote_url=remote_url, + pkgname=pkgname, + http_headers=http_headers, + ssl_context=ssl_context, + allow_insecure=allow_insecure, + ) + # Success! + break + + except (urllib.error.URLError, TimeoutError) as e: + e.add_note(f"requested URL: {remote_url}") + errors[source_url] = e + + else: # No success + if not sources: + raise KeyError( + f"No sources listed and file {remote_url} not in cache! " + "Please include primary URL in sources if you want it to be " + "included as a valid source." + ) + elif len(sources) == 1: + raise errors[sources[0]] + else: + raise urllib.error.URLError( + f"Unable to open any source! Exceptions were {errors}" + ) from errors[sources[0]] + + if cache: + try: + return import_file_to_cache( + url_key, + str(f_name), + remove_original=True, + replace=(cache == "update"), + pkgname=pkgname, + ) + except PermissionError as e: + # Cache is readonly, we can't update it + missing_cache = ( + f"Cache directory appears to be read-only ({e}), unable to import " + f"downloaded file, providing data in temporary file {f_name} " + "instead." + ) + # FIXME: other kinds of cache problem can occur? + + if missing_cache: + warn(CacheMissingWarning(missing_cache, f_name), stacklevel=2) + if conf.delete_temporary_downloads_at_exit: + _tempfilestodel.append(f_name) + return os.path.abspath(f_name) + + +def is_url_in_cache( + url_key: str, + pkgname: str = "astropy", + *, + on_missing: Literal["ignore", "warn", "error"] = "warn", +) -> bool: + """Check if a download for ``url_key`` is in the cache. + + The provided ``url_key`` will be the name used in the cache. The contents + may have been downloaded from this URL or from a mirror or they may have + been provided by the user. See `~download_file` for details. + + Parameters + ---------- + url_key : str + The URL retrieved + pkgname : `str`, optional + The package name to use to locate the download cache. i.e. for + ``pkgname='astropy'`` the default cache location is + ``~/.cache/astropy``. + on_missing : 'ignore', 'warn', or 'error' + What to do if the cache directory doesn't exist. + Default: 'warn' + + .. versionadded:: 8.0.0 + + Returns + ------- + in_cache : bool + `True` if a download for ``url_key`` is in the cache, `False` if not + or if the cache does not exist at all. + + See Also + -------- + cache_contents : obtain a dictionary listing everything in the cache + """ + dldir = _get_download_cache_loc(pkgname, on_missing=on_missing) + if not dldir.exists(): + return False + filename = dldir / _url_to_dirname(url_key) / "contents" + return filename.exists() + + +def cache_total_size( + pkgname: str = "astropy", +) -> int: + """Return the total size in bytes of all files in the cache.""" + size = 0 + dldir = _get_download_cache_loc(pkgname=pkgname, on_missing="ignore") + for root, _, files in os.walk(dldir): + size += sum(os.path.getsize(os.path.join(root, name)) for name in files) + return size + + +def _do_download_files_in_parallel(kwargs) -> str: + if (temp_config_path := kwargs.pop("temp_config")) is not None: + temp_config_args = (temp_config_path,) + else: + temp_config_args = () + if (temp_cache_path := kwargs.pop("temp_cache")) is not None: + temp_cache_args = (temp_cache_path,) + else: + temp_cache_args = () + with astropy.config.paths.set_temp_config(*temp_config_args): + with astropy.config.paths.set_temp_cache(*temp_cache_args): + return download_file(**kwargs) + + +def download_files_in_parallel( + urls: list[str], + cache: bool | Literal["update"] = "update", + show_progress: bool = True, + timeout: float | None = None, + sources=None, + multiprocessing_start_method: str | None = None, + pkgname: str = "astropy", +) -> list[str]: + """Download multiple files in parallel from the given URLs. + + Blocks until all files have downloaded. The result is a list of + local file paths corresponding to the given urls. + + The results will be stored in the cache under the values in ``urls`` even + if they are obtained from some other location via ``sources``. See + `~download_file` for details. + + Parameters + ---------- + urls : list of str + The URLs to retrieve. + + cache : bool or "update", optional + Whether to use the cache (default is `True`). If "update", + always download the remote URLs to see if new data is available + and store the result in cache. + + .. versionchanged:: 4.0 + The default was changed to ``"update"`` and setting it to + ``False`` will print a Warning and set it to ``"update"`` again, + because the function will not work properly without cache. Using + ``True`` will work as expected. + + .. versionchanged:: 3.0 + The default was changed to ``True`` and setting it to ``False`` + will print a Warning and set it to ``True`` again, because the + function will not work properly without cache. + + show_progress : bool, optional + Whether to display a progress bar during the download (default + is `True`) + + timeout : float, optional + Timeout for each individual requests in seconds (default is the + configurable `astropy.utils.data.Conf.remote_timeout`). + + sources : dict, optional + If provided, for each URL a list of URLs to try to obtain the + file from. The result will be stored under the original URL. + For any URL in this dictionary, the original URL will *not* be + tried unless it is in this list; this is to prevent long waits + for a primary server that is known to be inaccessible at the + moment. + + multiprocessing_start_method : str, optional + Useful primarily for testing; if in doubt leave it as the default. + When using multiprocessing, certain anomalies occur when starting + processes with the "spawn" method (the only option on Windows); + other anomalies occur with the "fork" method (the default on + Linux). + + pkgname : `str`, optional + The package name to use to locate the download cache. i.e. for + ``pkgname='astropy'`` the default cache location is + ``~/.cache/astropy``. + + Returns + ------- + paths : list of str + The local file paths corresponding to the downloaded URLs. + + Notes + ----- + If a URL is unreachable, the downloading will grind to a halt and the + exception will propagate upward, but an unpredictable number of + files will have been successfully downloaded and will remain in + the cache. + """ + from astropy.config.paths import ( + set_temp_cache, + set_temp_config, + ) + + from .console import ProgressBar + + if timeout is None: + timeout = conf.remote_timeout + if sources is None: + sources = {} + + if not cache: + # See issue #6662, on windows won't work because the files are removed + # again before they can be used. On *NIX systems it will behave as if + # cache was set to True because multiprocessing cannot insert the items + # in the list of to-be-removed files. This could be fixed, but really, + # just use the cache, with update_cache if appropriate. + warn( + "Disabling the cache does not work because of multiprocessing, " + 'it will be set to ``"update"``. You may need to manually remove ' + "the cached files with clear_download_cache() afterwards.", + AstropyWarning, + stacklevel=2, + ) + cache = "update" + + if show_progress: + progress = sys.stdout + else: + progress = io.BytesIO() + + # Combine duplicate URLs + combined_urls = list(set(urls)) + combined_paths = ProgressBar.map( + _do_download_files_in_parallel, + [ + dict( + remote_url=u, + cache=cache, + show_progress=False, + timeout=timeout, + sources=sources.get(u), + pkgname=pkgname, + temp_cache=set_temp_cache._get_current_override(), + temp_config=set_temp_config._get_current_override(), + ) + for u in combined_urls + ], + file=progress, + multiprocess=True, + multiprocessing_start_method=multiprocessing_start_method, + ) + paths: list[str] = [] + for url in urls: + paths.append(combined_paths[combined_urls.index(url)]) + return paths + + +# This is used by download_file and _deltemps to determine the files to delete +# when the interpreter exits +_tempfilestodel: list[Path] = [] + + +@atexit.register +def _deltemps(): + if _tempfilestodel is not None: + while len(_tempfilestodel) > 0: + fn = _tempfilestodel.pop() + if fn.is_file(): + try: + fn.unlink() + except OSError: + # oh well we tried + # could be held open by some process, on Windows + pass + elif fn.is_dir(): + try: + shutil.rmtree(fn) + except OSError: + # couldn't get rid of it, sorry + # could be held open by some process, on Windows + pass + + +def clear_download_cache( + hashorurl: str | None = None, + pkgname: str = "astropy", + *, + on_missing: Literal["ignore", "warn", "error"] = "warn", +) -> None: + """Clears the data file cache by deleting the local file(s). + + If a URL is provided, it will be the name used in the cache. The contents + may have been downloaded from this URL or from a mirror or they may have + been provided by the user. See `~download_file` for details. + + For the purposes of this function, a file can also be identified by a hash + of its contents or by the filename under which the data is stored (as + returned by `~download_file`, for example). + + Parameters + ---------- + hashorurl : str or None + If None, the whole cache is cleared. Otherwise, specify + a hash for the cached file that is supposed to be deleted, + the full path to a file in the cache that should be deleted, + or a URL that should be removed from the cache if present. + + pkgname : `str`, optional + The package name to use to locate the download cache. i.e. for + ``pkgname='astropy'`` the default cache location is + ``~/.cache/astropy``. + + on_missing : 'ignore', 'warn', or 'error' + What to do if the path requested for deletion doesn't exist. + Default: 'warn' + + .. versionadded:: 8.0.0 + """ + try: + dldir = _get_download_cache_loc(pkgname, on_missing="ignore") + if not dldir.exists(): + match on_missing: + case "ignore": + pass + case "warn": + warn( + f"{dldir} does not exist", + CacheMissingWarning, + stacklevel=2, + ) + case "error": + raise FileNotFoundError(f"no such file or directory {dldir}") + case _ as unreachable: + assert_never(unreachable) + return + if hashorurl is None: + # Optional: delete old incompatible caches too + _rmtree(dldir) + elif _is_url(hashorurl): + filepath = dldir / _url_to_dirname(hashorurl) + _rmtree(filepath) + else: + # Not a URL, it should be either a filename or a hash + filepath = dldir / hashorurl + rp = os.path.relpath(filepath, dldir) + if rp.startswith(".."): + raise RuntimeError( + "attempted to use clear_download_cache on the path " + f"{filepath} outside the data cache directory {dldir}" + ) + d, f = os.path.split(rp) + if d and f in ["contents", "url"]: + # It's a filename not the hash of a URL + # so we want to zap the directory containing the + # files "url" and "contents" + filepath = dldir / d + if os.path.exists(filepath): + _rmtree(filepath) + elif len(hashorurl) == 2 * hashlib.md5( + usedforsecurity=False + ).digest_size and re.match(r"[0-9a-f]+", hashorurl): + # It's the hash of some file contents, we have to find the right file + filename = _find_hash_fn(hashorurl) + if filename is not None: + clear_download_cache(filename) + except OSError as e: + msg = "Not clearing data from cache - problem arose " + estr = "" if len(e.args) < 1 else (": " + str(e)) + warn(CacheMissingWarning(msg + e.__class__.__name__ + estr), stacklevel=2) + + +def _get_download_cache_loc( + pkgname: str = "astropy", + *, + ensure_exists: bool = False, + on_missing: Literal["ignore", "warn", "error"] = "warn", +) -> Path: + """Finds the path to the cache directory. + + Parameters + ---------- + pkgname : `str`, optional + The package name to use to locate the download cache. i.e. for + ``pkgname='astropy'`` the default cache location is + ``~/.cache/astropy``. + + ensure_exists : bool, optional, keyword-only + Default: False + + on_missing : 'ignore', 'warn', or 'error' + What to do if the download cache directory doesn't exist. + Default: 'warn' + + .. versionadded:: 8.0.0 + + Returns + ------- + datadir : pathlib.Path + The path to the data cache directory. + """ + dldir = astropy.config.paths.get_cache_dir_path( + pkgname, + ensure_exists=False, + ).joinpath("download", "url") + + if ensure_exists: + dldir.mkdir(parents=True, exist_ok=True) + + if dldir.is_dir(): + return dldir + + if dldir.exists(): + raise NotADirectoryError(f"Data cache directory {dldir} is not a directory") + + match on_missing: + case "ignore": + pass + case "warn": + warnings.warn( + f"{dldir} does not exist", + CacheMissingWarning, + stacklevel=2, + ) + case "error": + raise FileNotFoundError(f"No such file or directory {dldir}") + case _ as unreachable: + assert_never(unreachable) + + return dldir + + +def _url_to_dirname(url: str) -> str: + if not _is_url(url): + raise ValueError(f"Malformed URL: '{url}'") + # Make domain names case-insensitive + # Also makes the http:// case-insensitive + urlobj = list(urllib.parse.urlsplit(url)) + urlobj[1] = urlobj[1].lower() + if urlobj[0].lower() in ["http", "https"] and urlobj[1] and urlobj[2] == "": + urlobj[2] = "/" + url_c = urllib.parse.urlunsplit(urlobj) + return hashlib.md5(url_c.encode("utf-8"), usedforsecurity=False).hexdigest() + + +class CacheDamaged(ValueError): + """Record the URL or file that was a problem. + Using clear_download_cache on the .bad_file or .bad_url attribute, + whichever is not None, should resolve this particular problem. + """ + + def __init__( + self, + *args: object, + bad_urls: list[str] | None = None, + bad_files: list[Path] | None = None, + **kwargs, + ): + super().__init__(*args, **kwargs) + self.bad_urls = bad_urls if bad_urls is not None else [] + self.bad_files = bad_files if bad_files is not None else [] + + +def check_download_cache( + pkgname: str = "astropy", +) -> None: + """Do a consistency check on the cache. + + .. note:: + + Since v5.0, this function no longer returns anything. + + Because the cache is shared by all versions of ``astropy`` in all virtualenvs + run by your user, possibly concurrently, it could accumulate problems. + This could lead to hard-to-debug problems or wasted space. This function + detects a number of incorrect conditions, including nonexistent files that + are indexed, files that are indexed but in the wrong place, and, if you + request it, files whose content does not match the hash that is indexed. + + This function also returns a list of non-indexed files. A few will be + associated with the shelve object; their exact names depend on the backend + used but will probably be based on ``urlmap``. The presence of other files + probably indicates that something has gone wrong and inaccessible files + have accumulated in the cache. These can be removed with + :func:`clear_download_cache`, either passing the filename returned here, or + with no arguments to empty the entire cache and return it to a + reasonable, if empty, state. + + Parameters + ---------- + pkgname : str, optional + The package name to use to locate the download cache, i.e., for + ``pkgname='astropy'`` the default cache location is + ``~/.cache/astropy``. + + Raises + ------ + `~astropy.utils.data.CacheDamaged` + To indicate a problem with the cache contents; the exception contains + a ``.bad_files`` attribute containing a set of filenames to allow the + user to use :func:`clear_download_cache` to remove the offending items. + OSError, RuntimeError + To indicate some problem with the cache structure. This may need a full + :func:`clear_download_cache` to resolve, or may indicate some kind of + misconfiguration. + """ + bad_files: str[Path] = set() + messages: set[str] = set() + dldir = _get_download_cache_loc(pkgname=pkgname, on_missing="ignore") + if not dldir.exists(): + return + with os.scandir(dldir) as it: + for entry in it: + f = dldir.joinpath(entry.name).absolute() + if entry.name.startswith("rmtree-"): + if f not in _tempfilestodel: + bad_files.add(f) + messages.add(f"Cache entry {entry.name} not scheduled for deletion") + elif entry.is_dir(): + for sf in os.listdir(f): + if sf in ["url", "contents"]: + continue + sf = f / sf + bad_files.add(sf) + messages.add(f"Unexpected file f{sf}") + urlf = f / "url" + url = None + if not urlf.is_file(): + bad_files.add(urlf) + messages.add(f"Problem with URL file f{urlf}") + else: + url = get_file_contents(urlf, encoding="utf-8") + if not _is_url(url): + bad_files.add(f) + messages.add(f"Malformed URL: {url}") + else: + hashname = _url_to_dirname(url) + if entry.name != hashname: + bad_files.add(f) + messages.add( + f"URL hashes to {hashname} but is stored in" + f" {entry.name}" + ) + if not f.joinpath("contents").is_file(): + bad_files.add(f) + if url is None: + messages.add(f"Hash {entry.name} is missing contents") + else: + messages.add( + f"URL {url} with hash {entry.name} is missing contents" + ) + else: + bad_files.add(f) + messages.add(f"Left-over non-directory {f} in cache") + if bad_files: + raise CacheDamaged("\n".join(messages), bad_files=sorted(bad_files)) + + +def _rmtree( + path: str | os.PathLike[str], + replace: str | os.PathLike[str] | None = None, +) -> None: + """More-atomic rmtree. Ignores missing directory.""" + with TemporaryDirectory( + prefix="rmtree-", dir=os.path.dirname(os.path.abspath(path)) + ) as d: + try: + os.rename(path, os.path.join(d, "to-zap")) + except FileNotFoundError: + pass + except PermissionError: + warn( + CacheMissingWarning( + f"Unable to remove directory {path} because a file in it " + "is in use and you are on Windows", + path, + ), + stacklevel=2, + ) + raise + except OSError as e: + if e.errno == errno.EXDEV: + warn(e.strerror or "", AstropyWarning, stacklevel=2) + shutil.move(path, os.path.join(d, "to-zap")) + else: + raise + + if replace is not None: + try: + os.rename(replace, path) + except FileExistsError: + # already there, fine + pass + except OSError as e: + if e.errno == errno.ENOTEMPTY: + # already there, fine + pass + elif e.errno == errno.EXDEV: + warn(e.strerror or "", AstropyWarning, stacklevel=2) + shutil.move(replace, path) + else: + raise + + +def import_file_to_cache( + url_key: str, + filename: str, + remove_original: bool = False, + pkgname: str = "astropy", + *, + replace: bool = True, +) -> str: + """Import the on-disk file specified by filename to the cache. + + The provided ``url_key`` will be the name used in the cache. The file + should contain the contents of this URL, at least notionally (the URL may + be temporarily or permanently unavailable). It is using ``url_key`` that + users will request these contents from the cache. See :func:`download_file` for + details. + + If ``url_key`` already exists in the cache, it will be updated to point to + these imported contents, and its old contents will be deleted from the + cache. + + Parameters + ---------- + url_key : str + The key to index the file under. This should probably be + the URL where the file was located, though if you obtained + it from a mirror you should use the URL of the primary + location. + filename : str + The file whose contents you want to import. + remove_original : bool + Whether to remove the original file (``filename``) once import is + complete. + pkgname : `str`, optional + The package name to use to locate the download cache. i.e. for + ``pkgname='astropy'`` the default cache location is + ``~/.cache/astropy``. + replace : boolean, optional + Whether or not to replace an existing object in the cache, if one exists. + If replacement is not requested but the object exists, silently pass. + """ + cache_dir = _get_download_cache_loc(pkgname=pkgname, ensure_exists=True) + cache_dirname = _url_to_dirname(url_key) + local_dirname = cache_dir / cache_dirname + local_filename = local_dirname / "contents" + with TemporaryDirectory( + prefix="temp_dir", dir=cache_dir, ignore_cleanup_errors=True + ) as temp_dir: + temp_path = Path(temp_dir) + temp_filename = temp_path / "contents" + # Make sure we're on the same filesystem + # This will raise an exception if the url_key doesn't turn into a valid filename + shutil.copy(filename, temp_filename) + temp_path.joinpath("url").write_text(url_key, encoding="utf-8") + if replace: + _rmtree(local_dirname, replace=temp_dir) + else: + try: + os.rename(temp_dir, local_dirname) + except FileExistsError: + # already there, fine + pass + except OSError as e: + if e.errno == errno.ENOTEMPTY: + # already there, fine + pass + else: + raise + if remove_original: + os.remove(filename) + return os.path.abspath(local_filename) + + +def get_cached_urls( + pkgname: str = "astropy", + *, + on_missing: Literal["ignore", "warn", "error"] = "warn", +) -> list[str]: + """ + Get the list of URLs in the cache. Especially useful for looking up what + files are stored in your cache when you don't have internet access. + + The listed URLs are the keys programs should use to access the file + contents, but those contents may have actually been obtained from a mirror. + See `~download_file` for details. + + Parameters + ---------- + pkgname : `str`, optional + The package name to use to locate the download cache. i.e. for + ``pkgname='astropy'`` the default cache location is + ``~/.cache/astropy``. + + on_missing : 'ignore', 'warn', or 'error' + What to do if the cache directory doesn't exist. + Default: 'warn' + + .. versionadded:: 8.0.0 + + Returns + ------- + cached_urls : list + List of cached URLs. + + See Also + -------- + cache_contents : obtain a dictionary listing everything in the cache + """ + return sorted(cache_contents(pkgname=pkgname, on_missing=on_missing).keys()) + + +def cache_contents( + pkgname: str = "astropy", + *, + on_missing: Literal["ignore", "warn", "error"] = "warn", +) -> MappingProxyType[str, str]: + """Obtain a dict mapping cached URLs to filenames. + + This dictionary is a read-only snapshot of the state of the cache when this + function was called. If other processes are actively working with the + cache, it is possible for them to delete files that are listed in this + dictionary. Use with some caution if you are working on a system that is + busy with many running astropy processes, although the same issues apply to + most functions in this module. + """ + dldir = _get_download_cache_loc(pkgname=pkgname, on_missing=on_missing) + if not dldir.exists(): + return MappingProxyType({}) + + r: dict[str, str] = {} + with os.scandir(dldir) as it: + for entry in it: + if entry.is_dir(): + url = get_file_contents(dldir / entry.name / "url", encoding="utf-8") + r[str(url)] = str(dldir.joinpath(entry.name, "contents").absolute()) + return MappingProxyType(r) + + +def export_download_cache( + filename_or_obj, + urls: Iterable[str] | None = None, + overwrite: bool = False, + pkgname: str = "astropy", +) -> None: + """Exports the cache contents as a ZIP file. + + Parameters + ---------- + filename_or_obj : str or file-like + Where to put the created ZIP file. Must be something the zipfile + module can write to. + urls : iterable of str or None + The URLs to include in the exported cache. The default is all + URLs currently in the cache. If a URL is included in this list + but is not currently in the cache, a KeyError will be raised. + To ensure that all are in the cache use `~download_file` + or `~download_files_in_parallel`. + overwrite : bool, optional + If filename_or_obj is a filename that exists, it will only be + overwritten if this is True. + pkgname : `str`, optional + The package name to use to locate the download cache. i.e. for + ``pkgname='astropy'`` the default cache location is + ``~/.cache/astropy``. + + See Also + -------- + import_download_cache : import the contents of such a ZIP file + import_file_to_cache : import a single file directly + """ + if urls is None: + urls = get_cached_urls(pkgname) + with zipfile.ZipFile(filename_or_obj, "w" if overwrite else "x") as z: + for u in urls: + fn = download_file(u, cache=True, sources=[], pkgname=pkgname) + # Do not use os.path.join because ZIP files want + # "/" on all platforms + z_fn = urllib.parse.quote(u, safe="") + z.write(fn, z_fn) + + +def import_download_cache( + filename_or_obj, + urls: Container[str] | None = None, + update_cache: bool = False, + pkgname: str = "astropy", +) -> None: + """Imports the contents of a ZIP file into the cache. + + Each member of the ZIP file should be named by a quoted version of the + URL whose contents it stores. These names are decoded with + :func:`~urllib.parse.unquote`. + + Parameters + ---------- + filename_or_obj : str or file-like + Where the stored ZIP file is. Must be something the :mod:`~zipfile` + module can read from. + urls : set of str or list of str or None + The URLs to import from the ZIP file. The default is all + URLs in the file. + update_cache : bool, optional + If True, any entry in the ZIP file will overwrite the value in the + cache; if False, leave untouched any entry already in the cache. + pkgname : `str`, optional + The package name to use to locate the download cache. i.e. for + ``pkgname='astropy'`` the default cache location is + ``~/.cache/astropy``. + + See Also + -------- + export_download_cache : export the contents the cache to of such a ZIP file + import_file_to_cache : import a single file directly + """ + with zipfile.ZipFile(filename_or_obj, "r") as z, TemporaryDirectory() as d: + for i, zf in enumerate(z.infolist()): + url = urllib.parse.unquote(zf.filename) + # FIXME(aarchiba): do we want some kind of validation on this URL? + # urllib.parse might do something sensible...but what URLs might + # they have? + # is_url in this file is probably a good check, not just here + # but throughout this file. + if urls is not None and url not in urls: + continue + if ( + not update_cache + and _get_download_cache_loc(pkgname, on_missing="ignore").exists() + and is_url_in_cache(url, pkgname=pkgname) + ): + continue + f_temp_name = os.path.join(d, str(i)) + with z.open(zf) as f_zip, open(f_temp_name, "wb") as f_temp: + block = f_zip.read(conf.download_block_size) + while block: + f_temp.write(block) + block = f_zip.read(conf.download_block_size) + import_file_to_cache( + url, f_temp_name, remove_original=True, pkgname=pkgname + ) diff --git a/astropy/utils/data_info.py b/astropy/utils/data_info.py new file mode 100644 index 000000000000..426fc28f56a6 --- /dev/null +++ b/astropy/utils/data_info.py @@ -0,0 +1,807 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +"""This module contains functions and methods that relate to the DataInfo class +which provides a container for informational attributes as well as summary info +methods. + +A DataInfo object is attached to the Quantity, SkyCoord, and Time classes in +astropy. Here it allows those classes to be used in Tables and uniformly carry +table column attributes such as name, format, dtype, meta, and description. +""" + +# Note: these functions and classes are tested extensively in astropy table +# tests via their use in providing mixin column info, and in +# astropy/tests/test_info for providing table and column info summary data. + +import os +import re +import sys +import warnings +import weakref +from collections import OrderedDict +from contextlib import contextmanager +from copy import deepcopy +from functools import partial +from io import StringIO + +import numpy as np + +from . import metadata + +__all__ = [ + "BaseColumnInfo", + "DataInfo", + "MixinInfo", + "ParentDtypeInfo", + "data_info_factory", + "dtype_info_name", +] + +# Tuple of filterwarnings kwargs to ignore when calling info +IGNORE_WARNINGS = ( + dict( + category=RuntimeWarning, + message=( + "All-NaN|" + "Mean of empty slice|Degrees of freedom <= 0|" + "invalid value encountered in sqrt" + ), + ), +) + + +@contextmanager +def serialize_context_as(context): + """Set context for serialization. + + This will allow downstream code to understand the context in which a column + is being serialized. Objects like Time or SkyCoord will have different + default serialization representations depending on context. + + Parameters + ---------- + context : str + Context name, e.g. 'fits', 'hdf5', 'parquet', 'ecsv', 'yaml' + """ + old_context = BaseColumnInfo._serialize_context + BaseColumnInfo._serialize_context = context + try: + yield + finally: + BaseColumnInfo._serialize_context = old_context + + +def dtype_info_name(dtype): + """Return a human-oriented string name of the ``dtype`` arg. + This can be use by astropy methods that present type information about + a data object. + + The output is mostly equivalent to ``dtype.name`` which takes the form + [B] where is like ``int`` or ``bool`` and [B] is an + optional number of bits which gets included only for numeric types. + + The output is shown below for ``bytes`` and ``str`` types, with being + the number of characters. This representation corresponds to the Python + type that matches the dtype:: + + Numpy S U + Python bytes str + + Parameters + ---------- + dtype : str, `~numpy.dtype`, type + Input as an object that can be converted via :class:`numpy.dtype`. + + Returns + ------- + dtype_info_name : str + String name of ``dtype`` + """ + dtype = np.dtype(dtype) + if dtype.names is not None: + info_names = ", ".join(dtype_info_name(dt[0]) for dt in dtype.fields.values()) + return f"({info_names})" + if dtype.subdtype is not None: + dtype, shape = dtype.subdtype + else: + shape = () + + if dtype.kind in ("S", "U"): + type_name = "bytes" if dtype.kind == "S" else "str" + length = re.search(r"(\d+)", dtype.str).group(1) + out = type_name + length + else: + out = dtype.name + + if shape: + out += f"[{','.join(str(n) for n in shape)}]" + + return out + + +def data_info_factory(names, funcs): + """ + Factory to create a function that can be used as an ``option`` + for outputting data object summary information. + + Examples + -------- + >>> from astropy.utils.data_info import data_info_factory + >>> from astropy.table import Column + >>> c = Column([4., 3., 2., 1.]) + >>> mystats = data_info_factory(names=['min', 'median', 'max'], + ... funcs=[np.min, np.median, np.max]) + >>> c.info(option=mystats) + min = 1 + median = 2.5 + max = 4 + n_bad = 0 + length = 4 + + Parameters + ---------- + names : list + List of information attribute names + funcs : list + List of functions that compute the corresponding information attribute + + Returns + ------- + func : function + Function that can be used as a data info option + """ + + def func(dat): + outs = [] + for func in funcs: + try: + if isinstance(func, str): + out = getattr(dat, func)() + else: + out = func(dat) + except Exception: + outs.append("--") + else: + try: + outs.append(f"{out:g}") + except (TypeError, ValueError): + outs.append(str(out)) + + return OrderedDict(zip(names, outs)) + + return func + + +def _get_data_attribute(dat, attr=None): + """ + Get a data object attribute for the ``attributes`` info summary method. + """ + if attr == "class": + val = type(dat).__name__ + elif attr == "dtype": + val = dtype_info_name(dat.info.dtype) + elif attr == "shape": + datshape = dat.shape[1:] + val = datshape or "" + else: + val = getattr(dat.info, attr) + if val is None: + val = "" + return str(val) + + +class InfoAttribute: + def __init__(self, attr, default=None): + self.attr = attr + self.default = default + + def __get__(self, instance, owner_cls): + if instance is None: + return self + + return instance._attrs.get(self.attr, self.default) + + def __set__(self, instance, value): + if instance is None: + # This is an unbound descriptor on the class + raise ValueError("cannot set unbound descriptor") + + instance._attrs[self.attr] = value + + +class ParentAttribute: + def __init__(self, attr): + self.attr = attr + + def __get__(self, instance, owner_cls): + if instance is None: + return self + + return getattr(instance._parent, self.attr) + + def __set__(self, instance, value): + if instance is None: + # This is an unbound descriptor on the class + raise ValueError("cannot set unbound descriptor") + + setattr(instance._parent, self.attr, value) + + +class DataInfoMeta(type): + def __new__(cls, name, bases, dct): + # Ensure that we do not gain a __dict__, which would mean + # arbitrary attributes could be set. + dct.setdefault("__slots__", []) + return super().__new__(cls, name, bases, dct) + + def __init__(cls, name, bases, dct): + super().__init__(name, bases, dct) + + # Define default getters/setters for attributes, if needed. + for attr in cls.attr_names: + if attr not in dct: + # If not defined explicitly for this class, did any of + # its superclasses define it, and, if so, was this an + # automatically defined look-up-on-parent attribute? + cls_attr = getattr(cls, attr, None) + if attr in cls.attrs_from_parent: + # If the attribute is supposed to be stored on the parent, + # and that is stated by this class yet it was not the case + # on the superclass, override it. + if "attrs_from_parent" in dct and not isinstance( + cls_attr, ParentAttribute + ): + setattr(cls, attr, ParentAttribute(attr)) + elif not cls_attr or isinstance(cls_attr, ParentAttribute): + # If the attribute is not meant to be stored on the parent, + # and if it was not defined already or was previously defined + # as an attribute on the parent, define a regular + # look-up-on-info attribute + setattr( + cls, attr, InfoAttribute(attr, cls._attr_defaults.get(attr)) + ) + + +class DataInfo(metaclass=DataInfoMeta): + """ + Descriptor that data classes use to add an ``info`` attribute for storing + data attributes in a uniform and portable way. Note that it *must* be + called ``info`` so that the DataInfo() object can be stored in the + ``instance`` using the ``info`` key. Because owner_cls.x is a descriptor, + Python doesn't use __dict__['x'] normally, and the descriptor can safely + store stuff there. Thanks to + https://nbviewer.jupyter.org/urls/gist.github.com/ChrisBeaumont/5758381/raw/descriptor_writeup.ipynb + for this trick that works for non-hashable classes. + + Parameters + ---------- + bound : bool + If True this is a descriptor attribute in a class definition, else it + is a DataInfo() object that is bound to a data object instance. Default is False. + """ + + _stats = ["mean", "std", "min", "max"] + attrs_from_parent = set() + attr_names = {"name", "unit", "dtype", "format", "description", "meta"} + _attr_defaults = {"dtype": np.dtype("O")} + _attrs_no_copy = set() + _info_summary_attrs = ("dtype", "shape", "unit", "format", "description", "class") + __slots__ = ["_attrs", "_parent_cls", "_parent_ref"] + # This specifies the list of object attributes which must be stored in + # order to re-create the object after serialization. This is independent + # of normal `info` attributes like name or description. Subclasses will + # generally either define this statically (QuantityInfo) or dynamically + # (SkyCoordInfo). These attributes may be scalars or arrays. If arrays + # that match the object length they will be serialized as an independent + # column. + _represent_as_dict_attrs: tuple[str, ...] = () + + # This specifies attributes which are to be provided to the class + # initializer as ordered args instead of keyword args. This is needed + # for Quantity subclasses where the keyword for data varies (e.g. + # between Quantity and Angle). + _construct_from_dict_args: tuple[str, ...] = () + + # This specifies the name of an attribute which is the "primary" data. + # Then when representing as columns + # (table.serialize._represent_mixin_as_column) the output for this + # attribute will be written with the just name of the mixin instead of the + # usual ".". + _represent_as_dict_primary_data: str | None = None + + def __init__(self, bound=False): + # If bound to a data object instance then create the dict of attributes + # which stores the info attribute values. Default of None for "unset" + # except for dtype where the default is object. + if bound: + self._attrs = {} + + @property + def _parent(self): + try: + parent = self._parent_ref() + except AttributeError: + return None + + if parent is None: + raise AttributeError( + """\ +failed to access "info" attribute on a temporary object. + +It looks like you have done something like ``col[3:5].info`` or +``col.quantity.info``, i.e. you accessed ``info`` from a temporary slice +object that only exists momentarily. This has failed because the reference to +that temporary object is now lost. Instead force a permanent reference (e.g. +``c = col[3:5]`` followed by ``c.info``).""" + ) + + return parent + + def __get__(self, instance, owner_cls): + if instance is None: + # This is an unbound descriptor on the class + self._parent_cls = owner_cls + return self + + info = instance.__dict__.get("info") + if info is None: + info = instance.__dict__["info"] = self.__class__(bound=True) + # We set _parent_ref on every call, since if one makes copies of + # instances, 'info' will be copied as well, which will lose the + # reference. + info._parent_ref = weakref.ref(instance) + return info + + def __set__(self, instance, value): + if instance is None: + # This is an unbound descriptor on the class + raise ValueError("cannot set unbound descriptor") + + if isinstance(value, DataInfo): + info = instance.__dict__["info"] = self.__class__(bound=True) + attr_names = info.attr_names + if value.__class__ is self.__class__: + # For same class, attributes are guaranteed to be stored in + # _attrs, so speed matters up by not accessing defaults. + # Doing this before difference in for loop helps speed. + attr_names = attr_names & set(value._attrs) # NOT in-place! + else: + # For different classes, copy over the attributes in common. + attr_names = attr_names & (value.attr_names - value._attrs_no_copy) + + for attr in attr_names - info.attrs_from_parent - info._attrs_no_copy: + info._attrs[attr] = deepcopy(getattr(value, attr)) + + else: + raise TypeError("info must be set with a DataInfo instance") + + def __getstate__(self): + return self._attrs + + def __setstate__(self, state): + self._attrs = state + + def _represent_as_dict(self, attrs=None): + """Get the values for the parent ``attrs`` and return as a dict. This + ignores any attributes that are None. In the context of serializing + the supported core astropy classes this conversion will succeed and + results in more succinct and less python-specific YAML. + By default, uses '_represent_as_dict_attrs'. + """ + return { + key: val + for key in (self._represent_as_dict_attrs if attrs is None else attrs) + if (val := getattr(self._parent, key, None)) is not None + } + + def _construct_from_dict(self, map): + args = [map.pop(attr) for attr in self._construct_from_dict_args] + return self._parent_cls(*args, **map) + + info_summary_attributes = staticmethod( + data_info_factory( + names=_info_summary_attrs, + funcs=[ + partial(_get_data_attribute, attr=attr) for attr in _info_summary_attrs + ], + ) + ) + + # No nan* methods in numpy < 1.8 + info_summary_stats = staticmethod( + data_info_factory( + names=_stats, funcs=[getattr(np, "nan" + stat) for stat in _stats] + ) + ) + + def __call__(self, option="attributes", out=""): + """ + Write summary information about data object to the ``out`` filehandle. + By default this prints to standard output via sys.stdout. + + The ``option`` argument specifies what type of information + to include. This can be a string, a function, or a list of + strings or functions. Built-in options are: + + - ``attributes``: data object attributes like ``dtype`` and ``format`` + - ``stats``: basic statistics: min, mean, and max + + If a function is specified then that function will be called with the + data object as its single argument. The function must return an + OrderedDict containing the information attributes. + + If a list is provided then the information attributes will be + appended for each of the options, in order. + + Examples + -------- + >>> from astropy.table import Column + >>> c = Column([1, 2], unit='m', dtype='int32') + >>> c.info() + dtype = int32 + unit = m + class = Column + n_bad = 0 + length = 2 + + >>> c.info(['attributes', 'stats']) + dtype = int32 + unit = m + class = Column + mean = 1.5 + std = 0.5 + min = 1 + max = 2 + n_bad = 0 + length = 2 + + Parameters + ---------- + option : str, callable, list of (str or callable) + Info option, defaults to 'attributes'. + out : file-like, None + Output destination, defaults to sys.stdout. If None then the + OrderedDict with information attributes is returned + + Returns + ------- + info : `~collections.OrderedDict` or None + `~collections.OrderedDict` if out==None else None + """ + if out == "": + out = sys.stdout + + dat = self._parent + info = OrderedDict() + name = dat.info.name + if name is not None: + info["name"] = name + + options = option if isinstance(option, (list, tuple)) else [option] + for option_ in options: + if isinstance(option_, str): + if hasattr(self, "info_summary_" + option_): + option_ = getattr(self, "info_summary_" + option_) + else: + raise ValueError( + f"option={option_} is not an allowed information type" + ) + + with warnings.catch_warnings(): + for ignore_kwargs in IGNORE_WARNINGS: + warnings.filterwarnings("ignore", **ignore_kwargs) + info.update(option_(dat)) + + if hasattr(dat, "mask"): + n_bad = np.count_nonzero(dat.mask) + else: + try: + n_bad = np.count_nonzero(np.isinf(dat) | np.isnan(dat)) + except Exception: + n_bad = 0 + info["n_bad"] = n_bad + + try: + info["length"] = len(dat) + except (TypeError, IndexError): + pass + + if out is None: + return info + + for key, val in info.items(): + if val != "": + out.write(f"{key} = {val}" + os.linesep) + + def __repr__(self): + if self._parent is None: + return super().__repr__() + + out = StringIO() + self.__call__(out=out) + return out.getvalue() + + +class BaseColumnInfo(DataInfo): + """Base info class for anything that can be a column in an astropy Table. + + There are at least two classes that inherit from this: + + ColumnInfo: for native astropy Column / MaskedColumn objects + MixinInfo: for mixin column objects + + Note that this class is defined here so that mixins can use it + without importing the table package. + """ + + attr_names = DataInfo.attr_names | {"parent_table", "indices"} + _attrs_no_copy = {"parent_table", "indices"} + + # Context for serialization. This can be set temporarily via + # ``serialize_context_as(context)`` context manager to allow downstream + # code to understand the context in which a column is being serialized. + # Typical values are 'fits', 'hdf5', 'parquet', 'ecsv', 'yaml'. Objects + # like Time or SkyCoord will have different default serialization + # representations depending on context. + _serialize_context = None + __slots__ = ["_copy_indices", "_format_funcs"] + + @property + def parent_table(self): + value = self._attrs.get("parent_table") + if callable(value): + value = value() + return value + + @parent_table.setter + def parent_table(self, parent_table): + if parent_table is None: + self._attrs.pop("parent_table", None) + else: + parent_table = weakref.ref(parent_table) + self._attrs["parent_table"] = parent_table + + def __init__(self, bound=False): + super().__init__(bound=bound) + + # If bound to a data object instance then add a _format_funcs dict + # for caching functions for print formatting. + if bound: + self._format_funcs = {} + + def __set__(self, instance, value): + # For Table columns do not set `info` when the instance is a scalar. + try: + if not instance.shape: + return + except AttributeError: + pass + + super().__set__(instance, value) + + def iter_str_vals(self): + """ + This is a mixin-safe version of Column.iter_str_vals. + """ + col = self._parent + if self.parent_table is None: + from astropy.table.column import FORMATTER as formatter + else: + formatter = self.parent_table.formatter + + _pformat_col_iter = formatter._pformat_col_iter + yield from _pformat_col_iter(col, -1, False, False, {}) + + @property + def indices(self): + # Implementation note: the auto-generation as an InfoAttribute cannot + # be used here, since on access, one should not just return the + # default (empty list is this case), but set _attrs['indices'] so that + # if the list is appended to, it is registered here. + return self._attrs.setdefault("indices", []) + + @indices.setter + def indices(self, indices): + self._attrs["indices"] = indices + + def adjust_indices(self, index, value, col_len): + """ + Adjust info indices after column modification. + + Parameters + ---------- + index : slice, int, list, or ndarray + Element(s) of column to modify. This parameter can + be a single row number, a list of row numbers, an + ndarray of row numbers, a boolean ndarray (a mask), + or a column slice. + value : int, list, or ndarray + New value(s) to insert + col_len : int + Length of the column + """ + if not self.indices: + return + + if isinstance(index, slice): + # run through each key in slice + t = index.indices(col_len) + keys = list(range(*t)) + elif isinstance(index, np.ndarray) and index.dtype.kind == "b": + # boolean mask + keys = np.where(index)[0] + else: # single int + keys = [index] + + value = np.atleast_1d(value) # turn array(x) into array([x]) + if value.size == 1: + # repeat single value + value = list(value) * len(keys) + + for key, val in zip(keys, value): + for col_index in self.indices: + col_index.replace(key, self.name, val) + + def slice_indices(self, col_slice, item, col_len): + """ + Given a sliced object, modify its indices + to correctly represent the slice. + + Parameters + ---------- + col_slice : `~astropy.table.Column` or mixin + Sliced object. If not a column, it must be a valid mixin, see + https://docs.astropy.org/en/stable/table/mixin_columns.html + item : slice, list, or ndarray + Slice used to create col_slice + col_len : int + Length of original object + """ + from astropy.table.sorted_array import SortedArray + + if not getattr(self, "_copy_indices", True): + # Necessary because MaskedArray will perform a shallow copy + col_slice.info.indices = [] + return col_slice + elif isinstance(item, slice): + col_slice.info.indices = [x[item] for x in self.indices] + elif self.indices: + if isinstance(item, np.ndarray) and item.dtype.kind == "b": + # boolean mask + item = np.where(item)[0] + # Empirical testing suggests that recreating a BST/RBT index is + # more effective than relabelling when less than ~60% of + # the total number of rows are involved, and is in general + # more effective for SortedArray. + small = len(item) <= 0.6 * col_len + col_slice.info.indices = [] + for index in self.indices: + if small or isinstance(index, SortedArray): + new_index = index.get_slice(col_slice, item) + else: + new_index = deepcopy(index) + new_index.replace_rows(item) + col_slice.info.indices.append(new_index) + + return col_slice + + @staticmethod + def merge_cols_attributes(cols, metadata_conflicts, name, attrs): + """ + Utility method to merge and validate the attributes ``attrs`` for the + input table columns ``cols``. + + Note that ``dtype`` and ``shape`` attributes are handled specially. + These should not be passed in ``attrs`` but will always be in the + returned dict of merged attributes. + + Parameters + ---------- + cols : list + List of input Table column objects + metadata_conflicts : str ('warn'|'error'|'silent') + How to handle metadata conflicts + name : str or None + Output column name + attrs : list + List of attribute names to be merged + + Returns + ------- + attrs : dict + Of merged attributes. + + """ + from astropy.table.operations import TableMergeError + + def warn_str_func(key, left, right): + out = ( + f"In merged column '{name}' the '{key}' attribute does not match " + f"({left} != {right}). Using {right} for merged output" + ) + return out + + def getattrs(col): + return { + attr: getattr(col.info, attr) + for attr in attrs + if getattr(col.info, attr, None) is not None + } + + out = getattrs(cols[0]) + for col in cols[1:]: + out = metadata.merge( + out, + getattrs(col), + metadata_conflicts=metadata_conflicts, + warn_str_func=warn_str_func, + ) + + # Output dtype is the superset of all dtypes in in_cols + out["dtype"] = metadata.utils.result_type(cols) + + # Make sure all input shapes are the same + uniq_shapes = {col.shape[1:] for col in cols} + if len(uniq_shapes) != 1: + raise TableMergeError("columns have different shapes") + out["shape"] = uniq_shapes.pop() + + # "Merged" output name is the supplied name + if name is not None: + out["name"] = str(name) + + return out + + def get_sortable_arrays(self): + """ + Return a list of arrays which can be lexically sorted to represent + the order of the parent column. + + The base method raises NotImplementedError and must be overridden. + + Returns + ------- + arrays : list of ndarray + """ + raise NotImplementedError(f"column {self.name} is not sortable") + + +class MixinInfo(BaseColumnInfo): + @property + def name(self): + return self._attrs.get("name") + + @name.setter + def name(self, name: str | None): + if name is None: + new_name = None + elif isinstance(name, str): + new_name = str(name) + else: + raise TypeError( + f"Expected a str value, got {name} with type {type(name).__name__}" + ) + + # For mixin columns that live within a table, rename the column in the + # table when setting the name attribute. This mirrors the same + # functionality in the BaseColumn class. + if self.parent_table is not None: + self.parent_table.columns._rename_column(self.name, new_name) + + self._attrs["name"] = new_name + + @property + def groups(self): + # This implementation for mixin columns essentially matches the Column + # property definition. `groups` is a read-only property here and + # depends on the parent table of the column having `groups`. This will + # allow aggregating mixins as long as they support those operations. + from astropy.table import groups + + return self._attrs.setdefault("groups", groups.ColumnGroups(self._parent)) + + +class ParentDtypeInfo(MixinInfo): + """Mixin that gets info.dtype from parent.""" + + attrs_from_parent = {"dtype"} # dtype and unit taken from parent diff --git a/astropy/utils/decorators.py b/astropy/utils/decorators.py new file mode 100644 index 000000000000..061771782b02 --- /dev/null +++ b/astropy/utils/decorators.py @@ -0,0 +1,1273 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +"""Sundry function and class decorators.""" + +import functools +import inspect +import sys +import textwrap +import threading +import types +import warnings +from functools import wraps +from inspect import signature +from types import FunctionType + +from .exceptions import ( + AstropyDeprecationWarning, + AstropyPendingDeprecationWarning, + AstropyUserWarning, +) + +__all__ = [ + "classproperty", + "deprecated", + "deprecated_attribute", + "deprecated_renamed_argument", + "format_doc", + "lazyproperty", + "sharedmethod", +] + +_NotFound = object() + + +def deprecated( + since, + message="", + name="", + alternative="", + pending=False, + obj_type=None, + warning_type=AstropyDeprecationWarning, + *, + pending_warning_type=AstropyPendingDeprecationWarning, +): + """ + Used to mark a function or class as deprecated. + + To mark an attribute as deprecated, use `deprecated_attribute`. + + Parameters + ---------- + since : str + The release at which this API became deprecated. + This is required. + + message : str, optional + Override the default deprecation message. The format + specifier ``func`` may be used for the name of the function, + and ``alternative`` may be used in the deprecation message + to insert the name of an alternative to the deprecated + function. ``obj_type`` may be used to insert a friendly name + for the type of object being deprecated. + + name : str, optional + The name of the deprecated function or class; if not provided + the name is automatically determined from the passed in + function or class, though this is useful in the case of + renamed functions, where the new function is just assigned to + the name of the deprecated function. For example:: + + def new_function(): + ... + oldFunction = new_function + + alternative : str, optional + An alternative function or class name that the user may use in + place of the deprecated object. The deprecation warning will + tell the user about this alternative if provided. + + pending : bool, optional + If True, uses a ``pending_warning_type`` instead of a + ``warning_type``. + + obj_type : str, optional + The type of this object, if the automatically determined one + needs to be overridden. + + warning_type : Warning + Warning to be issued. + Default is `~astropy.utils.exceptions.AstropyDeprecationWarning`. + + pending_warning_type : Warning + Pending warning to be issued. + This only works if ``pending`` is set to True. + Default is `~astropy.utils.exceptions.AstropyPendingDeprecationWarning`. + """ + method_types = (classmethod, staticmethod, types.MethodType) + + def deprecate_doc(old_doc, message): + """ + Returns a given docstring with a deprecation message prepended + to it. + """ + if not old_doc: + old_doc = "" + old_doc = textwrap.dedent(old_doc).strip("\n") + new_doc = f"\n.. deprecated:: {since}\n {message.strip()}\n\n" + old_doc + if not old_doc: + # This is to prevent a spurious 'unexpected unindent' warning from + # docutils when the original docstring was blank. + new_doc += r"\ " + return new_doc + + def get_function(func): + """ + Given a function or classmethod (or other function wrapper type), get + the function object. + """ + if isinstance(func, method_types): + func = func.__func__ + return func + + def deprecate_function(func, message, warning_type=warning_type): + """ + Returns a wrapped function that displays ``warning_type`` + when it is called. + """ + if isinstance(func, method_types): + func_wrapper = type(func) + else: + func_wrapper = lambda f: f + + func = get_function(func) + + def deprecated_func(*args, **kwargs): + if pending: + category = pending_warning_type + else: + category = warning_type + + warnings.warn(message, category, stacklevel=2) + + return func(*args, **kwargs) + + # If this is an extension function, we can't call + # functools.wraps on it, but we normally don't care. + # This crazy way to get the type of a wrapper descriptor is + # straight out of the Python 3.3 inspect module docs. + if type(func) is not type(str.__dict__["__add__"]): + deprecated_func = functools.wraps(func)(deprecated_func) + + deprecated_func.__doc__ = deprecate_doc(deprecated_func.__doc__, message) + deprecated_func.__deprecated__ = message + + return func_wrapper(deprecated_func) + + def deprecate_class(cls, message, warning_type=warning_type): + """ + Update the docstring and wrap the ``__init__`` in-place (or ``__new__`` + if the class or any of the bases overrides ``__new__``) so it will give + a deprecation warning when an instance is created. + + This won't work for extension classes because these can't be modified + in-place and the alternatives don't work in the general case: + + - Using a new class that looks and behaves like the original doesn't + work because the __new__ method of extension types usually makes sure + that it's the same class or a subclass. + - Subclassing the class and return the subclass can lead to problems + with pickle and will look weird in the Sphinx docs. + """ + cls.__doc__ = deprecate_doc(cls.__doc__, message) + cls.__deprecated__ = message + if cls.__new__ is object.__new__: + cls.__init__ = deprecate_function( + get_function(cls.__init__), message, warning_type + ) + else: + cls.__new__ = deprecate_function( + get_function(cls.__new__), message, warning_type + ) + return cls + + def deprecate( + obj, + message=message, + name=name, + alternative=alternative, + pending=pending, + warning_type=warning_type, + ): + if obj_type is None: + if isinstance(obj, type): + obj_type_name = "class" + elif inspect.isfunction(obj): + obj_type_name = "function" + elif inspect.ismethod(obj) or isinstance(obj, method_types): + obj_type_name = "method" + else: + obj_type_name = "object" + else: + obj_type_name = obj_type + + if not name: + name = get_function(obj).__name__ + + altmessage = "" + if not message or type(message) is type(deprecate): + if pending: + message = ( + "The {func} {obj_type} will be deprecated in a future version." + ) + else: + message = ( + "The {func} {obj_type} is deprecated and may " + "be removed in a future version." + ) + if alternative: + altmessage = f"\n Use {alternative} instead." + + message = ( + message.format( + func=name, + name=name, + alternative=alternative, + obj_type=obj_type_name, + ) + ) + altmessage + + if isinstance(obj, type): + return deprecate_class(obj, message, warning_type) + else: + return deprecate_function(obj, message, warning_type) + + if type(message) is type(deprecate): + return deprecate(message) + + return deprecate + + +def deprecated_attribute( + name, + since, + message=None, + alternative=None, + pending=False, + warning_type=AstropyDeprecationWarning, + pending_warning_type=AstropyPendingDeprecationWarning, +): + """ + Used to mark a public attribute as deprecated. This creates a + property that will warn when the given attribute name is accessed. + To prevent the warning (i.e. for internal code), use the private + name for the attribute by prepending an underscore + (i.e. ``self._name``), or set an alternative explicitly. + + Parameters + ---------- + name : str + The name of the deprecated attribute. + + since : str + The release at which this API became deprecated. This is + required. + + message : str, optional + Override the default deprecation message. The format + specifier ``name`` may be used for the name of the attribute, + and ``alternative`` may be used in the deprecation message + to insert the name of an alternative to the deprecated + function. + + alternative : str, optional + An alternative attribute that the user may use in place of the + deprecated attribute. The deprecation warning will tell the + user about this alternative if provided. + + pending : bool, optional + If True, uses a AstropyPendingDeprecationWarning instead of + ``warning_type``. + + warning_type : Warning + Warning to be issued. + Default is `~astropy.utils.exceptions.AstropyDeprecationWarning`. + + pending_warning_type : Warning + Pending warning to be issued. + This only works if ``pending`` is set to True. + Default is `~astropy.utils.exceptions.AstropyPendingDeprecationWarning`. + + Examples + -------- + :: + + class MyClass: + # Mark the old_name as deprecated + old_name = deprecated_attribute("old_name", "0.1") + + def method(self): + self._old_name = 42 + + class MyClass2: + old_name = deprecated_attribute( + "old_name", "1.2", alternative="new_name" + ) + + def method(self): + self.new_name = 24 + """ + private_name = alternative or "_" + name + + specific_deprecated = deprecated( + since, + name=name, + obj_type="attribute", + message=message, + alternative=alternative, + pending=pending, + warning_type=warning_type, + pending_warning_type=pending_warning_type, + ) + + @specific_deprecated + def get(self): + return getattr(self, private_name) + + @specific_deprecated + def set(self, val): + setattr(self, private_name, val) + + @specific_deprecated + def delete(self): + delattr(self, private_name) + + return property(get, set, delete) + + +def deprecated_renamed_argument( + old_name, + new_name, + since, + arg_in_kwargs=False, + relax=False, + pending=False, + warning_type=AstropyDeprecationWarning, + alternative="", + message="", +): + """Deprecate a _renamed_ or _removed_ function argument. + + The decorator assumes that the argument with the ``old_name`` was removed + from the function signature and the ``new_name`` replaced it at the + **same position** in the signature. If the ``old_name`` argument is + given when calling the decorated function the decorator will catch it and + issue a deprecation warning and pass it on as ``new_name`` argument. + + Parameters + ---------- + old_name : str or sequence of str + The old name of the argument. + + new_name : str or sequence of str or None + The new name of the argument. Set this to `None` to remove the + argument ``old_name`` instead of renaming it. + + since : str or number or sequence of str or number + The release at which the old argument became deprecated. + + arg_in_kwargs : bool or sequence of bool, optional + If the argument is not a named argument (for example it + was meant to be consumed by ``**kwargs``) set this to + ``True``. Otherwise the decorator will throw an Exception + if the ``new_name`` cannot be found in the signature of + the decorated function. + Default is ``False``. + + relax : bool or sequence of bool, optional + If ``False`` a ``TypeError`` is raised if both ``new_name`` and + ``old_name`` are given. If ``True`` the value for ``new_name`` is used + and a Warning is issued. + Default is ``False``. + + pending : bool or sequence of bool, optional + If ``True`` this will hide the deprecation warning and ignore the + corresponding ``relax`` parameter value. + Default is ``False``. + + warning_type : Warning + Warning to be issued. + Default is `~astropy.utils.exceptions.AstropyDeprecationWarning`. + + alternative : str, optional + An alternative function or class name that the user may use in + place of the deprecated object if ``new_name`` is None. The deprecation + warning will tell the user about this alternative if provided. + + message : str, optional + A custom warning message. If provided then ``since`` and + ``alternative`` options will have no effect. + + Raises + ------ + TypeError + If the new argument name cannot be found in the function + signature and arg_in_kwargs was False or if it is used to + deprecate the name of the ``*args``-, ``**kwargs``-like arguments. + At runtime such an Error is raised if both the new_name + and old_name were specified when calling the function and + "relax=False". + + Notes + ----- + The decorator should be applied to a function where the **name** + of an argument was changed but it applies the same logic. + + .. warning:: + If ``old_name`` is a list or tuple the ``new_name`` and ``since`` must + also be a list or tuple with the same number of entries. ``relax`` and + ``arg_in_kwarg`` can be a single bool (applied to all) or also a + list/tuple with the same number of entries like ``new_name``, etc. + + Examples + -------- + The deprecation warnings are not shown in the following examples. + + To deprecate a positional or keyword argument:: + + >>> from astropy.utils.decorators import deprecated_renamed_argument + >>> @deprecated_renamed_argument('sig', 'sigma', '1.0') + ... def test(sigma): + ... return sigma + + >>> test(2) + 2 + >>> test(sigma=2) + 2 + >>> test(sig=2) # doctest: +SKIP + 2 + + To deprecate an argument caught inside the ``**kwargs`` the + ``arg_in_kwargs`` has to be set:: + + >>> @deprecated_renamed_argument('sig', 'sigma', '1.0', + ... arg_in_kwargs=True) + ... def test(**kwargs): + ... return kwargs['sigma'] + + >>> test(sigma=2) + 2 + >>> test(sig=2) # doctest: +SKIP + 2 + + By default providing the new and old keyword will lead to an Exception. If + a Warning is desired set the ``relax`` argument:: + + >>> @deprecated_renamed_argument('sig', 'sigma', '1.0', relax=True) + ... def test(sigma): + ... return sigma + + >>> test(sig=2) # doctest: +SKIP + 2 + + It is also possible to replace multiple arguments. The ``old_name``, + ``new_name`` and ``since`` have to be `tuple` or `list` and contain the + same number of entries:: + + >>> @deprecated_renamed_argument(['a', 'b'], ['alpha', 'beta'], + ... ['1.0', 1.2]) + ... def test(alpha, beta): + ... return alpha, beta + + >>> test(a=2, b=3) # doctest: +SKIP + (2, 3) + + In this case ``arg_in_kwargs`` and ``relax`` can be a single value (which + is applied to all renamed arguments) or must also be a `tuple` or `list` + with values for each of the arguments. + + """ + cls_iter = (list, tuple) + if isinstance(old_name, cls_iter): + n = len(old_name) + # Assume that new_name and since are correct (tuple/list with the + # appropriate length) in the spirit of the "consenting adults". But the + # optional parameters may not be set, so if these are not iterables + # wrap them. + if not isinstance(arg_in_kwargs, cls_iter): + arg_in_kwargs = [arg_in_kwargs] * n + if not isinstance(relax, cls_iter): + relax = [relax] * n + if not isinstance(pending, cls_iter): + pending = [pending] * n + if not isinstance(message, cls_iter): + message = [message] * n + else: + # To allow a uniform approach later on, wrap all arguments in lists. + n = 1 + old_name = [old_name] + new_name = [new_name] + since = [since] + arg_in_kwargs = [arg_in_kwargs] + relax = [relax] + pending = [pending] + message = [message] + + def decorator(function): + # The named arguments of the function. + arguments = signature(function).parameters + keys = list(arguments.keys()) + position = [None] * n + + for i in range(n): + # Determine the position of the argument. + if arg_in_kwargs[i]: + pass + else: + if new_name[i] is None: + param = arguments[old_name[i]] + elif new_name[i] in arguments: + param = arguments[new_name[i]] + # In case the argument is not found in the list of arguments + # the only remaining possibility is that it should be caught + # by some kind of **kwargs argument. + # This case has to be explicitly specified, otherwise throw + # an exception! + else: + raise TypeError( + f'"{new_name[i]}" was not specified in the function ' + "signature. If it was meant to be part of " + '"**kwargs" then set "arg_in_kwargs" to "True"' + ) + + # There are several possibilities now: + + # 1.) Positional or keyword argument: + if param.kind == param.POSITIONAL_OR_KEYWORD: + if new_name[i] is None: + position[i] = keys.index(old_name[i]) + else: + position[i] = keys.index(new_name[i]) + + # 2.) Keyword only argument: + elif param.kind == param.KEYWORD_ONLY: + # These cannot be specified by position. + position[i] = None + + # 3.) positional-only argument, varargs, varkwargs or some + # unknown type: + else: + raise TypeError( + f'cannot replace argument "{new_name[i]}" ' + f"of kind {repr(param.kind)}." + ) + + @functools.wraps(function) + def wrapper(*args, **kwargs): + for i in range(n): + msg = message[i] or ( + f'"{old_name[i]}" was deprecated in ' + f"version {since[i]} and will be removed " + "in a future version. " + ) + # The only way to have oldkeyword inside the function is + # that it is passed as kwarg because the oldkeyword + # parameter was renamed to newkeyword. + if old_name[i] in kwargs: + value = kwargs.pop(old_name[i]) + # Display the deprecation warning only when it's not + # pending. + if not pending[i]: + if not message[i]: + if new_name[i] is not None: + msg += f'Use argument "{new_name[i]}" instead.' + elif alternative: + msg += f"\n Use {alternative} instead." + warnings.warn(msg, warning_type, stacklevel=2) + + # Check if the newkeyword was given as well. + newarg_in_args = position[i] is not None and len(args) > position[i] + newarg_in_kwargs = new_name[i] in kwargs + + if newarg_in_args or newarg_in_kwargs: + if not pending[i]: + # If both are given print a Warning if relax is + # True or raise an Exception is relax is False. + if relax[i]: + warnings.warn( + f'"{old_name[i]}" and "{new_name[i]}" ' + "keywords were set. " + f'Using the value of "{new_name[i]}".', + AstropyUserWarning, + ) + else: + raise TypeError( + f'cannot specify both "{old_name[i]}" and ' + f'"{new_name[i]}".' + ) + else: + # Pass the value of the old argument with the + # name of the new argument to the function + if new_name[i] is not None: + kwargs[new_name[i]] = value + # If old argument has no replacement, cast it back. + # https://github.com/astropy/astropy/issues/9914 + else: + kwargs[old_name[i]] = value + + # Deprecated keyword without replacement is given as + # positional argument. + elif ( + not pending[i] + and not new_name[i] + and position[i] + and len(args) > position[i] + ): + if alternative and not message[i]: + msg += f"\n Use {alternative} instead." + warnings.warn(msg, warning_type, stacklevel=2) + + return function(*args, **kwargs) + + return wrapper + + return decorator + + +def future_keyword_only(names: list[str], *, since=list[str]) -> FunctionType: + """Decorator to mark one or more function parameter(s) as future keyword-only. + + Parameters + ---------- + names: list[str] + names of parameters to be marked as future keyword-only + since: list[str] + versions in which each parameter was marked + + Examples + -------- + >>> @future_keyword_only(['spam', 'eggs'], since=['7.1', '7.1']) + ... def bacon(spam, eggs): + ... ... + """ + + def decorate(raw_function): + # validate the conditions in which the decorator is used + # this adds a small overhead on startup time but not on function call + + future_kwo_names = names + if len(future_kwo_names) != len(since): + raise ValueError( + "Expected names and since values with " + "identical length. " + f"Got {len(names)=} and {len(since)=}" + ) + params = signature(raw_function).parameters + params_names: list[str] = list(params) + future_kwo_to_since = dict(zip(future_kwo_names, since)) + + # check that all names exist in the current signature + if any(n not in params for n in future_kwo_names): + unknown = sorted(set(future_kwo_names) - set(params_names)) + raise ValueError( + "The following arguments cannot be marked as future keyword-only " + "because they were not found in the decorated function's signature: " + f"{', '.join(unknown)}" + ) + + # check that every future kwo is currently allowed as positional-or-keyword + # (otherwise something's wrong with the decorator call itself) + pos_or_kw_names = { + n for (n, p) in params.items() if p.kind is p.POSITIONAL_OR_KEYWORD + } + if not pos_or_kw_names.issuperset(future_kwo_names): + diff = sorted(set(future_kwo_names) - pos_or_kw_names) + raise ValueError( + "The following arguments cannot be marked as future keyword-only " + "because they are not currently positional-or-keyword: " + f"{', '.join(diff)}" + ) + + # check that there are no other positionally allowed arguments beyond + # any future kwo (otherwise these arguments could still break) + future_kwo_positions = [params_names.index(n) for n in future_kwo_names] + idx_min = min(future_kwo_positions) + idx_max = max(future_kwo_positions) + 1 + while idx_max < len(params): + p = params[params_names[idx_max]] + if p.kind in (p.KEYWORD_ONLY, p.VAR_KEYWORD): + break + idx_max += 1 + if any(n not in future_kwo_names for n in list(params)[idx_min:idx_max]): + broken = params_names[max(future_kwo_positions) + 1 : idx_max] + raise ValueError( + "The following positionally-allowed arguments were not marked as " + "future keyword-only and would be broken under future keyword-only " + f"requirements: {', '.join(broken)}" + ) + + @wraps(raw_function) + def decorated_function(*args, **kwargs): + if len(args) > idx_min: + flagged_positions = range(idx_min, len(args)) + flagged_names = [params_names[pos] for pos in flagged_positions] + depr_versions = [future_kwo_to_since[n] for n in flagged_names] + if len(set(depr_versions)) == 1: + since_details = depr_versions[0] + else: + since_details = ", ".join(depr_versions) + ", respectively" + msg = ( + "The following arguments were received positionally, which " + f"will be disallowed in a future release: {', '.join(flagged_names)}\n" + "Pass them as keywords to suppress this warning. " + f"(deprecated since {since_details})" + ) + warnings.warn(msg, AstropyDeprecationWarning, stacklevel=2) + return raw_function(*args, **kwargs) + + return decorated_function + + return decorate + + +# TODO: This can still be made to work for setters by implementing an +# accompanying metaclass that supports it; we just don't need that right this +# second +class classproperty(property): + """ + Similar to `property`, but allows class-level properties. That is, + a property whose getter is like a `classmethod`. + + The wrapped method may explicitly use the `classmethod` decorator (which + must become before this decorator), or the `classmethod` may be omitted + (it is implicit through use of this decorator). + + .. note:: + + classproperty only works for *read-only* properties. It does not + currently allow writeable/deletable properties, due to subtleties of how + Python descriptors work. In order to implement such properties on a class + a metaclass for that class must be implemented. + + Parameters + ---------- + fget : callable + The function that computes the value of this property (in particular, + the function when this is used as a decorator) a la `property`. + + doc : str, optional + The docstring for the property--by default inherited from the getter + function. + + lazy : bool, optional + If True, caches the value returned by the first call to the getter + function, so that it is only called once (used for lazy evaluation + of an attribute). This is analogous to `lazyproperty`. The ``lazy`` + argument can also be used when `classproperty` is used as a decorator + (see the third example below). When used in the decorator syntax this + *must* be passed in as a keyword argument. + + Examples + -------- + :: + + >>> class Foo: + ... _bar_internal = 1 + ... @classproperty + ... def bar(cls): + ... return cls._bar_internal + 1 + ... + >>> Foo.bar + 2 + >>> foo_instance = Foo() + >>> foo_instance.bar + 2 + >>> foo_instance._bar_internal = 2 + >>> foo_instance.bar # Ignores instance attributes + 2 + + As previously noted, a `classproperty` is limited to implementing + read-only attributes:: + + >>> class Foo: + ... _bar_internal = 1 + ... @classproperty + ... def bar(cls): + ... return cls._bar_internal + ... @bar.setter + ... def bar(cls, value): + ... cls._bar_internal = value + ... + Traceback (most recent call last): + ... + NotImplementedError: classproperty can only be read-only; use a + metaclass to implement modifiable class-level properties + + When the ``lazy`` option is used, the getter is only called once:: + + >>> class Foo: + ... @classproperty(lazy=True) + ... def bar(cls): + ... print("Performing complicated calculation") + ... return 1 + ... + >>> Foo.bar + Performing complicated calculation + 1 + >>> Foo.bar + 1 + + If a subclass inherits a lazy `classproperty` the property is still + re-evaluated for the subclass:: + + >>> class FooSub(Foo): + ... pass + ... + >>> FooSub.bar + Performing complicated calculation + 1 + >>> FooSub.bar + 1 + """ + + def __new__(cls, fget=None, doc=None, lazy=False): + if fget is None: + # Being used as a decorator--return a wrapper that implements + # decorator syntax + def wrapper(func): + return cls(func, lazy=lazy) + + return wrapper + + return super().__new__(cls) + + def __init__(self, fget, doc=None, lazy=False): + self._lazy = lazy + if lazy: + self._lock = threading.RLock() # Protects _cache + self._cache = {} + fget = self._wrap_fget(fget) + + super().__init__(fget=fget, doc=doc) + + # There is a buglet in Python where self.__doc__ doesn't + # get set properly on instances of property subclasses if + # the doc argument was used rather than taking the docstring + # from fget + # Related Python issue: https://bugs.python.org/issue24766 + if doc is not None and sys.flags.optimize < 2: + self.__doc__ = doc + + def __get__(self, obj, objtype): + if self._lazy: + val = self._cache.get(objtype, _NotFound) + if val is _NotFound: + with self._lock: + # Check if another thread initialised before we locked. + val = self._cache.get(objtype, _NotFound) + if val is _NotFound: + val = self.fget.__wrapped__(objtype) + self._cache[objtype] = val + else: + # The base property.__get__ will just return self here; + # instead we pass objtype through to the original wrapped + # function (which takes the class as its sole argument) + val = self.fget.__wrapped__(objtype) + return val + + def getter(self, fget): + return super().getter(self._wrap_fget(fget)) + + def setter(self, fset): + raise NotImplementedError( + "classproperty can only be read-only; use a metaclass to " + "implement modifiable class-level properties" + ) + + def deleter(self, fdel): + raise NotImplementedError( + "classproperty can only be read-only; use a metaclass to " + "implement modifiable class-level properties" + ) + + @staticmethod + def _wrap_fget(orig_fget): + if isinstance(orig_fget, classmethod): + orig_fget = orig_fget.__func__ + + # Using stock functools.wraps instead of the fancier version + # found later in this module, which is overkill for this purpose + + @functools.wraps(orig_fget) + def fget(obj): + return orig_fget(obj.__class__) + + return fget + + +# Adapted from the recipe at +# http://code.activestate.com/recipes/363602-lazy-property-evaluation +class lazyproperty(property): + """ + Works similarly to property(), but computes the value only once. + + This essentially memorizes the value of the property by storing the result + of its computation in the ``__dict__`` of the object instance. This is + useful for computing the value of some property that should otherwise be + invariant. For example:: + + >>> class LazyTest: + ... @lazyproperty + ... def complicated_property(self): + ... print('Computing the value for complicated_property...') + ... return 42 + ... + >>> lt = LazyTest() + >>> lt.complicated_property + Computing the value for complicated_property... + 42 + >>> lt.complicated_property + 42 + + As the example shows, the second time ``complicated_property`` is accessed, + the ``print`` statement is not executed. Only the return value from the + first access off ``complicated_property`` is returned. + + By default, a setter and deleter are used which simply overwrite and + delete, respectively, the value stored in ``__dict__``. Any user-specified + setter or deleter is executed before executing these default actions. + The one exception is that the default setter is not run if the user setter + already sets the new value in ``__dict__`` and returns that value and the + returned value is not ``None``. + + """ + + def __init__(self, fget, fset=None, fdel=None, doc=None): + super().__init__(fget, fset, fdel, doc) + self._key = self.fget.__name__ + self._lock = threading.RLock() + + def __get__(self, obj, owner=None): + try: + obj_dict = obj.__dict__ + val = obj_dict.get(self._key, _NotFound) + if val is _NotFound: + with self._lock: + # Check if another thread beat us to it. + val = obj_dict.get(self._key, _NotFound) + if val is _NotFound: + val = self.fget(obj) + obj_dict[self._key] = val + return val + except AttributeError: + if obj is None: + return self + raise + + def __set__(self, obj, val): + obj_dict = obj.__dict__ + if self.fset: + ret = self.fset(obj, val) + if ret is not None and obj_dict.get(self._key) is ret: + # By returning the value set the setter signals that it + # took over setting the value in obj.__dict__; this + # mechanism allows it to override the input value + return + obj_dict[self._key] = val + + def __delete__(self, obj): + if self.fdel: + self.fdel(obj) + obj.__dict__.pop(self._key, None) # Delete if present + + +class sharedmethod(classmethod): + """ + This is a method decorator that allows both an instance method and a + `classmethod` to share the same name. + + When using `sharedmethod` on a method defined in a class's body, it + may be called on an instance, or on a class. In the former case it + behaves like a normal instance method (a reference to the instance is + automatically passed as the first ``self`` argument of the method):: + + >>> class Example: + ... @sharedmethod + ... def identify(self, *args): + ... print('self was', self) + ... print('additional args were', args) + ... + >>> ex = Example() + >>> ex.identify(1, 2) + self was + additional args were (1, 2) + + In the latter case, when the `sharedmethod` is called directly from a + class, it behaves like a `classmethod`:: + + >>> Example.identify(3, 4) + self was + additional args were (3, 4) + + This also supports a more advanced usage, where the `classmethod` + implementation can be written separately. If the class' *metaclass* + has a method of the same name as the `sharedmethod`, the version on + the metaclass is delegated to:: + + >>> class ExampleMeta(type): + ... def identify(self): + ... print('this implements the {0}.identify ' + ... 'classmethod'.format(self.__name__)) + ... + >>> class Example(metaclass=ExampleMeta): + ... @sharedmethod + ... def identify(self): + ... print('this implements the instancemethod') + ... + >>> Example().identify() + this implements the instancemethod + >>> Example.identify() + this implements the Example.identify classmethod + """ + + def __get__(self, obj, objtype=None): + if obj is None: + mcls = type(objtype) + clsmeth = getattr(mcls, self.__func__.__name__, None) + if callable(clsmeth): + func = clsmeth + else: + func = self.__func__ + + return self._make_method(func, objtype) + else: + return self._make_method(self.__func__, obj) + + @staticmethod + def _make_method(func, instance): + return types.MethodType(func, instance) + + +def format_doc(docstring, *args, **kwargs): + """ + Replaces the docstring of the decorated object and then formats it. + + The formatting works like :meth:`str.format` and if the decorated object + already has a docstring this docstring can be included in the new + documentation if you use the ``{__doc__}`` placeholder. + Its primary use is for reusing a *long* docstring in multiple functions + when it is the same or only slightly different between them. + + Parameters + ---------- + docstring : str or object or None + The docstring that will replace the docstring of the decorated + object. If it is an object like a function or class it will + take the docstring of this object. If it is a string it will use the + string itself. One special case is if the string is ``None`` then + it will use the decorated functions docstring and formats it. + + args : + passed to :meth:`str.format`. + + kwargs : + passed to :meth:`str.format`. If the function has a (not empty) + docstring the original docstring is added to the kwargs with the + keyword ``'__doc__'``. + + Raises + ------ + ValueError + If the ``docstring`` (or interpreted docstring if it was ``None`` + or not a string) is empty. + + IndexError, KeyError + If a placeholder in the (interpreted) ``docstring`` was not filled. see + :meth:`str.format` for more information. + + Notes + ----- + Using this decorator allows, for example Sphinx, to parse the + correct docstring. + + Examples + -------- + Replacing the current docstring is very easy:: + + >>> from astropy.utils.decorators import format_doc + >>> @format_doc('''Perform num1 + num2''') + ... def add(num1, num2): + ... return num1+num2 + ... + >>> help(add) # doctest: +SKIP + Help on function add in module __main__: + + add(num1, num2) + Perform num1 + num2 + + sometimes instead of replacing you only want to add to it:: + + >>> doc = ''' + ... {__doc__} + ... Parameters + ... ---------- + ... num1, num2 : Numbers + ... Returns + ... ------- + ... result: Number + ... ''' + >>> @format_doc(doc) + ... def add(num1, num2): + ... '''Perform addition.''' + ... return num1+num2 + ... + >>> help(add) # doctest: +SKIP + Help on function add in module __main__: + + add(num1, num2) + Perform addition. + Parameters + ---------- + num1, num2 : Numbers + Returns + ------- + result : Number + + in case one might want to format it further:: + + >>> doc = ''' + ... Perform {0}. + ... Parameters + ... ---------- + ... num1, num2 : Numbers + ... Returns + ... ------- + ... result: Number + ... result of num1 {op} num2 + ... {__doc__} + ... ''' + >>> @format_doc(doc, 'addition', op='+') + ... def add(num1, num2): + ... return num1+num2 + ... + >>> @format_doc(doc, 'subtraction', op='-') + ... def subtract(num1, num2): + ... '''Notes: This one has additional notes.''' + ... return num1-num2 + ... + >>> help(add) # doctest: +SKIP + Help on function add in module __main__: + + add(num1, num2) + Perform addition. + Parameters + ---------- + num1, num2 : Numbers + Returns + ------- + result : Number + result of num1 + num2 + >>> help(subtract) # doctest: +SKIP + Help on function subtract in module __main__: + + subtract(num1, num2) + Perform subtraction. + Parameters + ---------- + num1, num2 : Numbers + Returns + ------- + result : Number + result of num1 - num2 + Notes : This one has additional notes. + + These methods can be combined; even taking the docstring from another + object is possible as docstring attribute. You just have to specify the + object:: + + >>> @format_doc(add) + ... def another_add(num1, num2): + ... return num1 + num2 + ... + >>> help(another_add) # doctest: +SKIP + Help on function another_add in module __main__: + + another_add(num1, num2) + Perform addition. + Parameters + ---------- + num1, num2 : Numbers + Returns + ------- + result : Number + result of num1 + num2 + + But be aware that this decorator *only* formats the given docstring not + the strings passed as ``args`` or ``kwargs`` (not even the original + docstring):: + + >>> @format_doc(doc, 'addition', op='+') + ... def yet_another_add(num1, num2): + ... '''This one is good for {0}.''' + ... return num1 + num2 + ... + >>> help(yet_another_add) # doctest: +SKIP + Help on function yet_another_add in module __main__: + + yet_another_add(num1, num2) + Perform addition. + Parameters + ---------- + num1, num2 : Numbers + Returns + ------- + result : Number + result of num1 + num2 + This one is good for {0}. + + To work around it you could specify the docstring to be ``None``:: + + >>> @format_doc(None, 'addition') + ... def last_add_i_swear(num1, num2): + ... '''This one is good for {0}.''' + ... return num1 + num2 + ... + >>> help(last_add_i_swear) # doctest: +SKIP + Help on function last_add_i_swear in module __main__: + + last_add_i_swear(num1, num2) + This one is good for addition. + + Using it with ``None`` as docstring allows to use the decorator twice + on an object to first parse the new docstring and then to parse the + original docstring or the ``args`` and ``kwargs``. + """ + if sys.flags.optimize >= 2: + # docstrings are dropped at runtime, so let's return a noop decorator + return lambda func: func + + def set_docstring(obj): + if docstring is None: + # None means: use the objects __doc__ + doc = obj.__doc__ + # Delete documentation in this case so we don't end up with + # awkwardly self-inserted docs. + obj.__doc__ = None + elif isinstance(docstring, str): + # String: use the string that was given + doc = docstring + else: + # Something else: Use the __doc__ of this + doc = docstring.__doc__ + + if not doc: + # In case the docstring is empty it's probably not what was wanted. + raise ValueError( + "docstring must be a string or containing a " + "docstring that is not empty." + ) + + # Dedent both the original and the new docstring to ensure consistent + # leading whitespace, because from Python 3.13 the bytecode compiler + # strips leading whitespace from docstrings. If the text in ``doc`` + # has any leading whitespace, this can lead to reST/Sphinx errors. + if sys.version_info[:2] >= (3, 13): + doc = textwrap.dedent(doc).lstrip("\n") + + # If the original has a not-empty docstring append it to the format + # kwargs. + kwargs["__doc__"] = obj.__doc__ or "" + obj.__doc__ = doc.format(*args, **kwargs) + return obj + + return set_docstring diff --git a/astropy/utils/diff.py b/astropy/utils/diff.py new file mode 100644 index 000000000000..0fa404f3ecf5 --- /dev/null +++ b/astropy/utils/diff.py @@ -0,0 +1,213 @@ +import difflib +import functools +import math +import sys +from textwrap import indent + +import numpy as np + +__all__ = [ + "diff_values", + "report_diff_values", + "where_not_allclose", +] + + +def diff_values(a, b, rtol=0.0, atol=0.0): + """ + Diff two scalar values. If both values are floats, they are compared to + within the given absolute and relative tolerance. + + Parameters + ---------- + a, b : int, float, str + Scalar values to compare. + + rtol, atol : float + Relative and absolute tolerances as accepted by + :func:`numpy.allclose`. + + Returns + ------- + is_different : bool + `True` if they are different, else `False`. + + """ + if isinstance(a, float) and isinstance(b, float): + if math.isfinite(b): + return not abs(a - b) <= atol + rtol * abs(b) + if math.isnan(a) and math.isnan(b): + return False + return a != b + + +def _ignore_astropy_terminal_size(func): + @functools.wraps(func) + def inner(*args, **kwargs): + from astropy import conf + + with conf.set_temp("max_width", -1), conf.set_temp("max_lines", -1): + return func(*args, **kwargs) + + return inner + + +@_ignore_astropy_terminal_size +def report_diff_values(a, b, fileobj=sys.stdout, indent_width=0, rtol=0.0, atol=0.0): + """ + Write a diff report between two values to the specified file-like object. + + Parameters + ---------- + a, b + Values to compare. Anything that can be turned into strings + and compared using :py:mod:`difflib` should work. + + fileobj : object + File-like object to write to. + The default is ``sys.stdout``, which writes to terminal. + + indent_width : int + Character column(s) to indent. + + rtol, atol : float + Relative and absolute tolerances as accepted by + :func:`numpy.allclose`. + + Returns + ------- + identical : bool + `True` if no diff, else `False`. + + """ + indent_prefix = indent_width * " " + if isinstance(a, np.ndarray) and isinstance(b, np.ndarray): + if a.shape != b.shape: + fileobj.write(indent(" Different array shapes:\n", indent_prefix)) + report_diff_values( + str(a.shape), + str(b.shape), + fileobj=fileobj, + indent_width=indent_width + 1, + ) + return False + + if np.issubdtype(a.dtype, np.floating) and np.issubdtype(b.dtype, np.floating): + diff_indices = np.transpose(where_not_allclose(a, b, rtol=rtol, atol=atol)) + else: + diff_indices = np.transpose(np.where(a != b)) + + num_diffs = diff_indices.shape[0] + + for idx in diff_indices[:3]: + lidx = idx.tolist() + fileobj.write(indent(f" at {lidx!r}:\n", indent_prefix)) + report_diff_values( + a[tuple(idx)], + b[tuple(idx)], + fileobj=fileobj, + indent_width=indent_width + 1, + rtol=rtol, + atol=atol, + ) + + if num_diffs > 3: + fileobj.write( + indent(f" ...and at {num_diffs - 3:d} more indices.\n", indent_prefix) + ) + return False + + return num_diffs == 0 + + typea = type(a) + typeb = type(b) + + if typea == typeb: + lnpad = " " + sign_a = "a>" + sign_b = "b>" + a = str(a) + b = str(b) + else: + padding = max(len(typea.__name__), len(typeb.__name__)) + 3 + lnpad = (padding + 1) * " " + sign_a = ("(" + typea.__name__ + ") ").rjust(padding) + "a>" + sign_b = ("(" + typeb.__name__ + ") ").rjust(padding) + "b>" + + is_a_str = isinstance(a, str) + is_b_str = isinstance(b, str) + a = repr(a) if is_a_str and not is_b_str else str(a) + b = repr(b) if is_b_str and not is_a_str else str(b) + + identical = True + + for line in difflib.ndiff(a.splitlines(), b.splitlines()): + if line[0] == "-": + identical = False + line = sign_a + line[1:] + elif line[0] == "+": + identical = False + line = sign_b + line[1:] + else: + line = lnpad + line + fileobj.write(indent(" {}\n".format(line.rstrip("\n")), indent_prefix)) + + return identical + + +def where_not_allclose(a, b, rtol=1e-5, atol=1e-8, return_maxdiff=False): + """ + A version of :func:`numpy.allclose` that returns the indices + where the two arrays differ, instead of just a boolean value. + + Parameters + ---------- + a, b : array-like + Input arrays to compare. + rtol, atol : float + Relative and absolute tolerances as accepted by + :func:`numpy.allclose`. + return_maxdiff : bool + Return the maximum of absolute and relative differences. + + Returns + ------- + idx : tuple of array + Indices where the two arrays differ. + max_absolute : float + Maximum of absolute difference, returned if ``return_maxdiff=True``. + max_relative : float + Maximum of relative difference, returned if ``return_maxdiff=True``. + + """ + # Create fixed mask arrays to handle INF and NaN; currently INF and NaN + # are handled as equivalent + a = np.ma.masked_invalid(a) + b = np.ma.masked_invalid(b) + + absolute = np.ma.abs(b - a) + + if atol == 0.0 and rtol == 0.0: + # Use a faster comparison for the most simple (and common) case + thresh = 0 + else: + thresh = atol + rtol * np.abs(b) + + # values invalid in only one of the two arrays should be reported + invalid = a.mask ^ b.mask + indices = np.where(invalid | (absolute.filled(0) > thresh)) + + if return_maxdiff: + absolute[invalid] = np.ma.masked + finites = ~absolute.mask + absolute = absolute.compressed() + if len(indices[0]) == 0 or absolute.size == 0: + max_absolute = max_relative = 0 + else: + # remove all invalid values before computing max differences + relative = absolute / np.abs(b[finites]) + max_absolute = float(np.max(absolute)) + max_relative = np.max(relative) + return indices, max_absolute, max_relative + else: + return indices diff --git a/astropy/utils/exceptions.py b/astropy/utils/exceptions.py new file mode 100644 index 000000000000..4c7d81847853 --- /dev/null +++ b/astropy/utils/exceptions.py @@ -0,0 +1,94 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +""" +This module contains errors/exceptions and warnings of general use for +astropy. Exceptions that are specific to a given subpackage should *not* be +here, but rather in the particular subpackage. +""" + +__all__ = [ + "AstropyBackwardsIncompatibleChangeWarning", + "AstropyDeprecationWarning", + "AstropyPendingDeprecationWarning", + "AstropyUserWarning", + "AstropyWarning", + "DuplicateRepresentationWarning", + "NoValue", +] + + +class AstropyWarning(Warning): + """ + The base warning class from which all Astropy warnings should inherit. + + Any warning inheriting from this class is handled by the Astropy logger. + """ + + +class AstropyUserWarning(UserWarning, AstropyWarning): + """ + The primary warning class for Astropy. + + Use this if you do not need a specific sub-class. + """ + + +class AstropyDeprecationWarning(AstropyWarning): + """ + A warning class to indicate a deprecated feature. + """ + + +class AstropyPendingDeprecationWarning(PendingDeprecationWarning, AstropyWarning): + """ + A warning class to indicate a soon-to-be deprecated feature. + """ + + +class AstropyBackwardsIncompatibleChangeWarning(AstropyWarning): + """ + A warning class indicating a change in astropy that is incompatible + with previous versions. + + The suggested procedure is to issue this warning for the version in + which the change occurs, and remove it for all following versions. + """ + + +class DuplicateRepresentationWarning(AstropyWarning): + """ + A warning class indicating a representation name was already registered. + """ + + +class _NoValue: + """Special keyword value. + + This class may be used as the default value assigned to a + deprecated keyword in order to check if it has been given a user + defined value. + """ + + def __repr__(self): + return "astropy.utils.exceptions.NoValue" + + +NoValue = _NoValue() + + +def __getattr__(name: str): + if name in ("ErfaError", "ErfaWarning"): + import warnings + + warnings.warn( + f"Importing {name} from astropy.utils.exceptions was deprecated " + "in version 6.1 and will stop working in a future version. " + f"Instead, please use\nfrom erfa import {name}\n\n", + category=AstropyDeprecationWarning, + stacklevel=1, + ) + + import erfa + + return getattr(erfa, name) + + raise AttributeError(f"Module {__name__!r} has no attribute {name!r}.") diff --git a/astropy/utils/iers/__init__.py b/astropy/utils/iers/__init__.py new file mode 100644 index 000000000000..34761f56692a --- /dev/null +++ b/astropy/utils/iers/__init__.py @@ -0,0 +1 @@ +from .iers import * diff --git a/astropy/utils/iers/data/ReadMe.eopc04_IAU2000 b/astropy/utils/iers/data/ReadMe.eopc04_IAU2000 new file mode 100644 index 000000000000..d2a3d9ffa2f8 --- /dev/null +++ b/astropy/utils/iers/data/ReadMe.eopc04_IAU2000 @@ -0,0 +1,41 @@ +Table: eopc04_iau2000 +================================================================================ + +from http://hpiers.obspm.fr/iers/eop/eopc04/eopc04_IAU2000.62-now + + INTERNATIONAL EARTH ROTATION AND REFERENCE SYSTEMS SERVICE + EARTH ORIENTATION PARAMETERS + EOP (IERS) 08 C04 + +================================================================================ + +File Summary: +-------------------------------------------------------------------------------- + FileName Lrecl Records Explanations +-------------------------------------------------------------------------------- +ReadMe 80 . This file, made using header +eopc04_IAU2000.62-now 155 18279 all EOP values since 01 January 1962 +-------------------------------------------------------------------------------- + +================================================================================ +Byte-by-byte Description of file: * +-------------------------------------------------------------------------------- + Bytes Format Units Label Explanations +-------------------------------------------------------------------------------- + 1- 4 I4 --- year Calendar year + 5- 8 I4 --- month Month + 9- 12 I4 --- day day of month (0 hr UTC) + 13- 19 I7 d MJD Modified Julian Date (MJD, 0 hr UTC) + 20- 30 F11.6 arcsec PM_x polar motion x + 31- 41 F11.6 arcsec PM_y polar motion y + 42- 53 F12.7 s UT1_UTC Difference UT1-UTC + 54- 65 F12.7 s LOD length of day + 66- 76 F11.6 arcsec dX_2000A dX wrt IAU2000A Nutation + 77- 87 F11.6 arcsec dY_2000A dY wrt IAU2000A Nutation + 88- 98 F11.6 arcsec e_PM_x error in PM_x + 99-109 F11.6 arcsec e_PM_y error in PM_y +110-120 F11.7 s e_UT1_UTC error in UT1_UTC +121-131 F11.7 s e_LOD error in length of day +132-143 F12.6 arcsec e_dX_2000A error in dX_2000A +144-155 F12.6 arcsec e_dY_2000A error in dY_2000A +-------------------------------------------------------------------------------- diff --git a/astropy/utils/iers/iers.py b/astropy/utils/iers/iers.py new file mode 100644 index 000000000000..0fa3161e9466 --- /dev/null +++ b/astropy/utils/iers/iers.py @@ -0,0 +1,1366 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +""" +The astropy.utils.iers package provides access to the tables provided by +the International Earth Rotation and Reference Systems Service, in +particular allowing interpolation of published UT1-UTC values for given +times. These are used in `astropy.time` to provide UT1 values. The polar +motions are also used for determining earth orientation for +celestial-to-terrestrial coordinate transformations +(in `astropy.coordinates`). +""" + +import os +import re +from datetime import UTC, datetime +from typing import Self +from urllib.parse import urlparse +from warnings import warn + +import erfa +import numpy as np +from astropy_iers_data import ( + IERS_A_FILE, + IERS_A_README, + IERS_A_URL, + IERS_A_URL_MIRROR, + IERS_B_FILE, + IERS_B_README, + IERS_B_URL, + IERS_LEAP_SECOND_FILE, + IERS_LEAP_SECOND_URL, +) +from astropy_iers_data import IERS_LEAP_SECOND_URL_MIRROR as IETF_LEAP_SECOND_URL + +from astropy import config as _config +from astropy import units as u +from astropy import utils +from astropy.table import MaskedColumn, QTable +from astropy.time import Time, TimeDelta +from astropy.utils.data import ( + clear_download_cache, + get_readable_fileobj, + is_url_in_cache, +) +from astropy.utils.exceptions import AstropyDeprecationWarning, AstropyWarning +from astropy.utils.state import ScienceState + +__all__ = [ + "FROM_IERS_A", + "FROM_IERS_A_PREDICTION", + "FROM_IERS_B", + "IERS", + "IERS_A", + "IERS_A_FILE", + "IERS_A_README", + "IERS_A_URL", + "IERS_A_URL_MIRROR", + "IERS_B", + "IERS_B_FILE", + "IERS_B_README", + "IERS_B_URL", + "IERS_LEAP_SECOND_FILE", + "IERS_LEAP_SECOND_URL", + "IETF_LEAP_SECOND_URL", + "TIME_BEFORE_IERS_RANGE", + "TIME_BEYOND_IERS_RANGE", + "Conf", + "IERSDegradedAccuracyWarning", + "IERSRangeError", + "IERSStaleWarning", + "IERSWarning", + "IERS_Auto", + "LeapSeconds", + "conf", + "earth_orientation_table", +] + +# Status/source values returned by IERS.ut1_utc +FROM_IERS_B = 0 +FROM_IERS_A = 1 +FROM_IERS_A_PREDICTION = 2 +TIME_BEFORE_IERS_RANGE = -1 +TIME_BEYOND_IERS_RANGE = -2 + +MJD_ZERO = 2400000.5 + +INTERPOLATE_ERROR = """\ +interpolating from IERS_Auto using predictive values that are more +than {0} days old. + +Normally you should not see this error because this class +automatically downloads the latest IERS-A table. Perhaps you are +offline? If you understand what you are doing then this error can be +suppressed by setting the auto_max_age configuration variable to +``None``: + + from astropy.utils.iers import conf + conf.auto_max_age = None +""" + +MONTH_ABBR = [ + "Jan", + "Feb", + "Mar", + "Apr", + "May", + "Jun", + "Jul", + "Aug", + "Sep", + "Oct", + "Nov", + "Dec", +] + + +class IERSWarning(AstropyWarning): + """ + Generic warning class for IERS. + """ + + +class IERSDegradedAccuracyWarning(AstropyWarning): + """ + IERS time conversion has degraded accuracy normally due to setting + ``conf.auto_download = False`` and ``conf.iers_degraded_accuracy = 'warn'``. + """ + + +class IERSStaleWarning(IERSWarning): + """ + Downloaded IERS table may be stale. + """ + + +def download_file(*args, **kwargs): + """ + Overload astropy.utils.data.download_file within iers module to use a + custom (longer) wait time. This just passes through ``*args`` and + ``**kwargs`` after temporarily setting the download_file remote timeout to + the local ``iers.conf.remote_timeout`` value. + """ + kwargs.setdefault( + "http_headers", + { + "User-Agent": "astropy/iers", + "Accept": "*/*", + }, + ) + + with utils.data.conf.set_temp("remote_timeout", conf.remote_timeout): + return utils.data.download_file(*args, **kwargs) + + +def _none_to_float(value): + """ + Convert None to a valid floating point value. Especially + for auto_max_age = None. + """ + return value if value is not None else np.finfo(float).max + + +class Conf(_config.ConfigNamespace): + """ + Configuration parameters for `astropy.utils.iers`. + """ + + auto_download = _config.ConfigItem( + True, + "Enable auto-downloading of the latest IERS-A data. If set to False " + "then the bundled IERS-A file will be used by default (even if a " + "newer version of the IERS-A file was previously downloaded and cached). " + "This parameter also controls whether internet resources will be " + "queried to update the leap second table if the installed version is " + "out of date. Default is True.", + ) + auto_max_age = _config.ConfigItem( + 30.0, + "Maximum age (days) of predictive data before auto-downloading. " + 'See "Auto refresh behavior" in astropy.utils.iers documentation for details. ' + "Default is 30.", + ) + iers_auto_url = _config.ConfigItem( + IERS_A_URL, "URL for auto-downloading IERS file data." + ) + iers_auto_url_mirror = _config.ConfigItem( + IERS_A_URL_MIRROR, "Mirror URL for auto-downloading IERS file data." + ) + remote_timeout = _config.ConfigItem( + 10.0, "Remote timeout downloading IERS file data (seconds)." + ) + iers_degraded_accuracy = _config.ConfigItem( + ["error", "warn", "ignore"], + "IERS behavior if the range of available IERS data does not " + "cover the times when converting time scales, potentially leading " + "to degraded accuracy. Applies only when using IERS-B data on its own.", + ) + system_leap_second_file = _config.ConfigItem("", "System file with leap seconds.") + iers_leap_second_auto_url = _config.ConfigItem( + IERS_LEAP_SECOND_URL, "URL for auto-downloading leap seconds." + ) + ietf_leap_second_auto_url = _config.ConfigItem( + IETF_LEAP_SECOND_URL, "Alternate URL for auto-downloading leap seconds." + ) + + +conf = Conf() + + +class IERSRangeError(IndexError): + """ + Any error for when dates are outside of the valid range for IERS. + """ + + +class IERS(QTable): + """Generic IERS table class, defining interpolation functions. + + Sub-classed from `astropy.table.QTable`. The table should hold columns + 'MJD', 'UT1_UTC', 'dX_2000A'/'dY_2000A', and 'PM_x'/'PM_y'. + """ + + iers_table = None + """Cached table, returned if ``open`` is called without arguments.""" + + @classmethod + def open(cls, file=None, cache=False, **kwargs): + """Open an IERS table, reading it from a file if not loaded before. + + Parameters + ---------- + file : str or None + full local or network path to the ascii file holding IERS data, + for passing on to the ``read`` class methods (further optional + arguments that are available for some IERS subclasses can be added). + If None, use the default location from the ``read`` class method. + cache : bool + Whether to use cache. Defaults to False, since IERS files + are regularly updated. + + Returns + ------- + IERS + An IERS table class instance + + Notes + ----- + On the first call in a session, the table will be memoized (in the + ``iers_table`` class attribute), and further calls to ``open`` will + return this stored table if ``file=None`` (the default). + + If a table needs to be re-read from disk, pass on an explicit file + location or use the (sub-class) close method and re-open. + + If the location is a network location it is first downloaded via + download_file. + + For the IERS class itself, an IERS_B sub-class instance is opened. + + """ + if file is not None or cls.iers_table is None: + if file is not None: + if urlparse(file).netloc: + kwargs.update(file=download_file(file, cache=cache)) + else: + kwargs.update(file=file) + + # TODO: the below is really ugly and probably a bad idea. Instead, + # there should probably be an IERSBase class, which provides + # useful methods but cannot really be used on its own, and then + # *perhaps* an IERS class which provides best defaults. But for + # backwards compatibility, we use the IERS_B reader for IERS here. + if cls is IERS: + cls.iers_table = IERS_B.read(**kwargs) + else: + cls.iers_table = cls.read(**kwargs) + return cls.iers_table + + @classmethod + def close(cls): + """Remove the IERS table from the class. + + This allows the table to be re-read from disk during one's session + (e.g., if one finds it is out of date and has updated the file). + """ + cls.iers_table = None + + def mjd_utc(self, jd1, jd2=0.0): + """Turn a time to MJD, returning integer and fractional parts. + + Parameters + ---------- + jd1 : float, array, or `~astropy.time.Time` + first part of two-part JD, or Time object + jd2 : float or array, optional + second part of two-part JD. + Default is 0., ignored if jd1 is `~astropy.time.Time`. + + Returns + ------- + mjd : float or array + integer part of MJD + utc : float or array + fractional part of MJD + """ + try: # see if this is a Time object + jd1, jd2 = jd1.utc.jd1, jd1.utc.jd2 + except Exception: + pass + + mjd = np.floor(jd1 - MJD_ZERO + jd2) + utc = jd1 - (MJD_ZERO + mjd) + jd2 + return mjd, utc + + def ut1_utc(self, jd1, jd2=0.0, return_status=False): + """Interpolate UT1-UTC corrections in IERS Table for given dates. + + Parameters + ---------- + jd1 : float, array of float, or `~astropy.time.Time` object + first part of two-part JD, or Time object + jd2 : float or float array, optional + second part of two-part JD. + Default is 0., ignored if jd1 is `~astropy.time.Time`. + return_status : bool + Whether to return status values. If False (default), + raise ``IERSRangeError`` if any time is out of the range covered + by the IERS table. + + Returns + ------- + ut1_utc : float or float array + UT1-UTC, interpolated in IERS Table + status : int or int array + Status values (if ``return_status``=``True``):: + ``iers.FROM_IERS_B`` + ``iers.FROM_IERS_A`` + ``iers.FROM_IERS_A_PREDICTION`` + ``iers.TIME_BEFORE_IERS_RANGE`` + ``iers.TIME_BEYOND_IERS_RANGE`` + """ + return self._interpolate( + jd1, jd2, ["UT1_UTC"], self.ut1_utc_source if return_status else None + ) + + def dcip_xy(self, jd1, jd2=0.0, return_status=False): + """Interpolate CIP corrections in IERS Table for given dates. + + Parameters + ---------- + jd1 : float, array of float, or `~astropy.time.Time` object + first part of two-part JD, or Time object + jd2 : float or float array, optional + second part of two-part JD (default 0., ignored if jd1 is Time) + return_status : bool + Whether to return status values. If False (default), + raise ``IERSRangeError`` if any time is out of the range covered + by the IERS table. + + Returns + ------- + D_x : `~astropy.units.Quantity` ['angle'] + x component of CIP correction for the requested times. + D_y : `~astropy.units.Quantity` ['angle'] + y component of CIP correction for the requested times + status : int or int array + Status values (if ``return_status``=``True``):: + ``iers.FROM_IERS_B`` + ``iers.FROM_IERS_A`` + ``iers.FROM_IERS_A_PREDICTION`` + ``iers.TIME_BEFORE_IERS_RANGE`` + ``iers.TIME_BEYOND_IERS_RANGE`` + """ + return self._interpolate( + jd1, + jd2, + ["dX_2000A", "dY_2000A"], + self.dcip_source if return_status else None, + ) + + def pm_xy(self, jd1, jd2=0.0, return_status=False): + """Interpolate polar motions from IERS Table for given dates. + + Parameters + ---------- + jd1 : float, array of float, or `~astropy.time.Time` object + first part of two-part JD, or Time object + jd2 : float or float array, optional + second part of two-part JD. + Default is 0., ignored if jd1 is `~astropy.time.Time`. + return_status : bool + Whether to return status values. If False (default), + raise ``IERSRangeError`` if any time is out of the range covered + by the IERS table. + + Returns + ------- + PM_x : `~astropy.units.Quantity` ['angle'] + x component of polar motion for the requested times. + PM_y : `~astropy.units.Quantity` ['angle'] + y component of polar motion for the requested times. + status : int or int array + Status values (if ``return_status``=``True``):: + ``iers.FROM_IERS_B`` + ``iers.FROM_IERS_A`` + ``iers.FROM_IERS_A_PREDICTION`` + ``iers.TIME_BEFORE_IERS_RANGE`` + ``iers.TIME_BEYOND_IERS_RANGE`` + """ + return self._interpolate( + jd1, jd2, ["PM_x", "PM_y"], self.pm_source if return_status else None + ) + + def _check_interpolate_indices(self, indices_orig, indices_clipped, max_input_mjd): + """ + Check that the indices from interpolation match those after clipping + to the valid table range. This method gets overridden in the IERS_Auto + class because it has different requirements. + """ + if np.any(indices_orig != indices_clipped): + if conf.iers_degraded_accuracy == "error": + msg = ( + "(some) times are outside of range covered by IERS table. Cannot" + " convert with full accuracy. To allow conversion with degraded" + " accuracy set astropy.utils.iers.conf.iers_degraded_accuracy to" + ' "warn" or "silent". For more information about setting this' + " configuration parameter or controlling its value globally, see" + " the Astropy configuration system documentation" + " https://docs.astropy.org/en/stable/config/index.html." + ) + raise IERSRangeError(msg) + elif conf.iers_degraded_accuracy == "warn": + # No IERS data covering the time(s) and user requested a warning. + msg = ( + "(some) times are outside of range covered by IERS table, " + "accuracy is degraded." + ) + warn(msg, IERSDegradedAccuracyWarning) + # No IERS data covering the time(s) and user is OK with no warning. + + def _interpolate(self, jd1, jd2, columns, source=None): + mjd, utc = self.mjd_utc(jd1, jd2) + # enforce array + is_scalar = not hasattr(mjd, "__array__") or mjd.ndim == 0 + if is_scalar: + mjd = np.array([mjd]) + utc = np.array([utc]) + + self._refresh_table_as_needed(mjd) + + # For typical format, will always find a match (since MJD are integer) + # hence, important to define which side we will be; this ensures + # self['MJD'][i-1]<=mjd Self: + """Read IERS-A table from a finals2000a.* file provided by USNO. + + Parameters + ---------- + file : str or os.PathLike[str] + full path to ascii file holding IERS-A data. + Defaults to ``iers.IERS_A_FILE``. + readme : str or os.PathLike[str] + full path to ascii file holding CDS-style readme. + Defaults to package version, ``iers.IERS_A_README``. + + Returns + ------- + ``IERS_A`` class instance + """ + if file is None: + # In prior versions of astropy, read() would only work without a + # file argument if a finals2000A.all file was present in the + # current working directory. We can now use the versions from + # astropy-iers-data but for backward-compatibility we first check + # if there is a file in the current working directory and use that + # if so, emitting a deprecation warning + if os.path.exists("finals2000A.all"): + file = "finals2000A.all" + warn( + "The file= argument was not specified but " + "'finals2000A.all' is present in the current working " + "directory, so reading IERS data from that file. To " + "continue reading a local file from the current working " + "directory, specify file= explicitly otherwise a bundled " + "file will be used in future.", + AstropyDeprecationWarning, + ) + else: + file = IERS_A_FILE + else: + file = os.fspath(file) + if readme is None: + readme = IERS_A_README + else: + readme = os.fspath(readme) + + iers_a = super().read(file, format="cds", readme=readme) + + # Combine the A and B data for UT1-UTC and PM columns + table = cls._combine_a_b_columns(iers_a) + table.meta["data_path"] = file + table.meta["readme_path"] = readme + + return table + + def ut1_utc_source(self, i): + """Set UT1-UTC source flag for entries in IERS table.""" + ut1flag = self["UT1Flag"][i] + source = np.ones_like(i) * FROM_IERS_B + source[ut1flag == "I"] = FROM_IERS_A + source[ut1flag == "P"] = FROM_IERS_A_PREDICTION + return source + + def dcip_source(self, i): + """Set CIP correction source flag for entries in IERS table.""" + nutflag = self["NutFlag"][i] + source = np.ones_like(i) * FROM_IERS_B + source[nutflag == "I"] = FROM_IERS_A + source[nutflag == "P"] = FROM_IERS_A_PREDICTION + return source + + def pm_source(self, i): + """Set polar motion source flag for entries in IERS table.""" + pmflag = self["PolPMFlag"][i] + source = np.ones_like(i) * FROM_IERS_B + source[pmflag == "I"] = FROM_IERS_A + source[pmflag == "P"] = FROM_IERS_A_PREDICTION + return source + + +class IERS_B(IERS): + """IERS Table class targeted to IERS B, provided by IERS itself. + + These are final values; see https://www.iers.org/IERS/EN/Home/home_node.html + + Notes + ----- + If the package IERS B file (``iers.IERS_B_FILE``) is out of date, a new + version can be downloaded from ``iers.IERS_B_URL``. + + See `~astropy.utils.iers.IERS_B.read` for instructions on how to read + a pre-2023 style IERS B file (usually named ``eopc04_IAU2000.62-now``). + """ + + iers_table = None + + @classmethod + def read( + cls, + file: str | os.PathLike[str] | None = None, + readme: str | os.PathLike[str] | None = None, + data_start: int = 6, + ) -> Self: + """Read IERS-B table from a eopc04.* file provided by IERS. + + Parameters + ---------- + file : str or os.PathLike[str] + full path to ascii file holding IERS-B data. + Defaults to package version, ``iers.IERS_B_FILE``. + readme : str or os.PathLike[str] + full path to ascii file holding CDS-style readme. + Defaults to package version, ``iers.IERS_B_README``. + data_start : int + Starting row. Default is 6, appropriate for standard IERS files. + + Returns + ------- + ``IERS_B`` class instance + + Notes + ----- + To read a pre-2023 style IERS B file (usually named something like + ``eopc04_IAU2000.62-now``), do something like this example with an + excerpt that is used for testing:: + + >>> from astropy.utils.iers import IERS_B + >>> from astropy.utils.data import get_pkg_data_filename + >>> old_style_file = get_pkg_data_filename( + ... "tests/data/iers_b_old_style_excerpt", + ... package="astropy.utils.iers") + >>> iers_b = IERS_B.read( + ... old_style_file, + ... readme=get_pkg_data_filename("data/ReadMe.eopc04_IAU2000", + ... package="astropy.utils.iers"), + ... data_start=14) + + """ + if file is None: + file = IERS_B_FILE + else: + file = os.fspath(file) + if readme is None: + readme = IERS_B_README + else: + readme = os.fspath(readme) + table = super().read(file, format="cds", readme=readme, data_start=data_start) + + table.meta["data_path"] = file + table.meta["readme_path"] = readme + return table + + def ut1_utc_source(self, i): + """Set UT1-UTC source flag for entries in IERS table.""" + return np.ones_like(i) * FROM_IERS_B + + def dcip_source(self, i): + """Set CIP correction source flag for entries in IERS table.""" + return np.ones_like(i) * FROM_IERS_B + + def pm_source(self, i): + """Set PM source flag for entries in IERS table.""" + return np.ones_like(i) * FROM_IERS_B + + +class IERS_Auto(IERS_A): + """ + Provide most-recent IERS data and automatically handle downloading + of updated values as necessary. + + The returned table combines the IERS-A and IERS-B files, with the data + in the IERS-B file considered to be official values and thus superseding + values from the IERS-A file at the same times. + """ + + iers_table = None + + @classmethod + def open(cls): + """If the configuration setting ``astropy.utils.iers.conf.auto_download`` + is set to True (default), then open a recent version of the IERS-A + table with predictions for UT1-UTC and polar motion out to + approximately one year from now. If the available version of this file + is older than ``astropy.utils.iers.conf.auto_max_age`` days old + (or non-existent) then it will be downloaded over the network and cached. + + If the configuration setting ``astropy.utils.iers.conf.auto_download`` + is set to False then the bundled IERS-A table will be used rather than + any downloaded version of the IERS-A table. + + On the first call in a session, the table will be memoized (in the + ``iers_table`` class attribute), and further calls to ``open`` will + return this stored table. + + Returns + ------- + `~astropy.table.QTable` instance + With IERS (Earth rotation) data columns + + """ + if not conf.auto_download: + # If auto_download is changed to False mid-session, iers_table may have already been + # made from non-bundled files, so it should be remade from bundled files + if not hasattr(cls, "_iers_table_bundled"): + cls._iers_table_bundled = cls.read() + cls.iers_table = cls._iers_table_bundled + return cls.iers_table + + all_urls = (conf.iers_auto_url, conf.iers_auto_url_mirror) + + if cls.iers_table is not None: + # If the URL has changed, we need to redownload the file, so we + # should ignore the internally cached version. + + if cls.iers_table.meta.get("data_url") in all_urls: + return cls.iers_table + + for url in all_urls: + try: + filename = download_file(url, cache=True) + except Exception as err: + warn(f"failed to download {url}: {err}", IERSWarning) + continue + + try: + cls.iers_table = cls.read(file=filename) + except Exception as err: + warn(f"malformed IERS table from {url}: {err}", IERSWarning) + continue + cls.iers_table.meta["data_url"] = url + break + + else: + # Issue a warning here, perhaps user is offline. An exception + # will be raised downstream if actually trying to interpolate + # predictive values. + warn( + "unable to download valid IERS file, using bundled IERS-A", IERSWarning + ) + cls.iers_table = cls.read() + + return cls.iers_table + + def _check_interpolate_indices(self, indices_orig, indices_clipped, max_input_mjd): + """Check that the indices from interpolation match those after clipping to the + valid table range. The IERS_Auto class is exempted as long as it has + sufficiently recent available data so the clipped interpolation is + always within the confidence bounds of current Earth rotation + knowledge. + """ + predictive_mjd = self.meta["predictive_mjd"] + + # See explanation in _refresh_table_as_needed for these conditions + auto_max_age = _none_to_float(conf.auto_max_age) + if ( + max_input_mjd > predictive_mjd + and self.time_now.mjd - predictive_mjd > auto_max_age + ): + raise ValueError(INTERPOLATE_ERROR.format(auto_max_age)) + + def _refresh_table_as_needed(self, mjd): + """Potentially update the IERS table in place depending on the requested + time values in ``mjd`` and the time span of the table. + + For IERS_Auto the behavior is that the table is refreshed from the IERS + server if both the following apply: + + - Any of the requested IERS values are predictive. The IERS-A table + contains predictive data out for a year after the available + definitive values. + - The first predictive values are at least ``conf.auto_max_age days`` old. + In other words the IERS-A table was created by IERS long enough + ago that it can be considered stale for predictions. + """ + # If downloading is disabled, bail out silently. + # _check_interpolate_indices() will error later if appropriate. + if not conf.auto_download: + return + + # Pass in initial to np.max to allow things to work for empty mjd. + max_input_mjd = np.max(mjd, initial=50000) + now_mjd = self.time_now.mjd + + # IERS-A table contains predictive data out for a year after + # the available definitive values. + fpi = self.meta["predictive_index"] + predictive_mjd = self.meta["predictive_mjd"] + + # Update table in place if necessary + auto_max_age = _none_to_float(conf.auto_max_age) + + # If auto_max_age is smaller than IERS update time then repeated downloads may + # occur without getting updated values (giving a IERSStaleWarning). + if auto_max_age < 10: + raise ValueError( + "IERS auto_max_age configuration value must be larger than 10 days" + ) + + if max_input_mjd > predictive_mjd and (now_mjd - predictive_mjd) > auto_max_age: + all_urls = (conf.iers_auto_url, conf.iers_auto_url_mirror) + + # Get the latest version + try: + filename = download_file(all_urls[0], sources=all_urls, cache="update") + except Exception as err: + # Issue a warning here, perhaps user is offline. An exception + # will be raised downstream when actually trying to interpolate + # predictive values. + warn( + AstropyWarning( + f"failed to download {' and '.join(all_urls)}: {err}.\nA" + " coordinate or time-related calculation might be compromised" + " or fail because the dates are not covered by the available" + ' IERS file. See the "IERS data access" section of the' + " astropy documentation for additional information on working" + " offline." + ) + ) + return + + new_table = self.__class__.read(file=filename) + new_table.meta["data_url"] = str(all_urls[0]) + + # New table has new values? + if new_table["MJD"][-1] > self["MJD"][-1]: + # Replace *replace* current values from the first predictive index through + # the end of the current table. This replacement is much faster than just + # deleting all rows and then using add_row for the whole duration. + new_fpi = np.searchsorted( + new_table["MJD"].value, predictive_mjd, side="right" + ) + n_replace = len(self) - fpi + self[fpi:] = new_table[new_fpi : new_fpi + n_replace] + + # Sanity check for continuity + if new_table["MJD"][new_fpi + n_replace] - self["MJD"][-1] != 1.0 * u.d: + raise ValueError("unexpected gap in MJD when refreshing IERS table") + + # Now add new rows in place + for row in new_table[new_fpi + n_replace :]: + self.add_row(row) + + self.meta.update(new_table.meta) + else: + warn( + IERSStaleWarning( + "IERS_Auto predictive values are older than" + f" {conf.auto_max_age} days but downloading the latest table" + " did not find newer values" + ) + ) + + @classmethod + def _substitute_iers_b(cls, table): + """Substitute IERS B values with those from a real IERS B table. + + IERS-A has IERS-B values included, but for reasons unknown these + do not match the latest IERS-B values (see comments in #4436). + Here, we use the bundled astropy IERS-B table to overwrite the values + in the IERS-A table. + """ + iers_b = IERS_B.open() + # Substitute IERS-B values for existing B values in IERS-A table + mjd_b = table["MJD"][np.isfinite(table["UT1_UTC_B"])] + i0 = np.searchsorted(iers_b["MJD"], mjd_b[0], side="left") + i1 = np.searchsorted(iers_b["MJD"], mjd_b[-1], side="right") + iers_b = iers_b[i0:i1] + n_iers_b = len(iers_b) + # If there is overlap then replace IERS-A values from available IERS-B + if n_iers_b > 0: + # Sanity check that we are overwriting the correct values + if not u.allclose(table["MJD"][:n_iers_b], iers_b["MJD"]): + raise ValueError( + "unexpected mismatch when copying IERS-B values into IERS-A table." + ) + # Finally do the overwrite + table["UT1_UTC_B"][:n_iers_b] = iers_b["UT1_UTC"] + table["PM_X_B"][:n_iers_b] = iers_b["PM_x"] + table["PM_Y_B"][:n_iers_b] = iers_b["PM_y"] + table["dX_2000A_B"][:n_iers_b] = iers_b["dX_2000A"] + table["dY_2000A_B"][:n_iers_b] = iers_b["dY_2000A"] + + return table + + +class earth_orientation_table(ScienceState): + """Default IERS table for Earth rotation and reference systems service. + + These tables are used to calculate the offsets between ``UT1`` and ``UTC`` + and for conversion to Earth-based coordinate systems. + + The state itself is an IERS table, as an instance of one of the + `~astropy.utils.iers.IERS` classes. The default, the auto-updating + `~astropy.utils.iers.IERS_Auto` class, should suffice for most + purposes. + + Examples + -------- + To temporarily use the IERS-B file packaged with astropy:: + + >>> from astropy.utils import iers + >>> from astropy.time import Time + >>> iers_b = iers.IERS_B.open(iers.IERS_B_FILE) + >>> with iers.earth_orientation_table.set(iers_b): + ... print(Time('2000-01-01').ut1.isot) + 2000-01-01T00:00:00.355 + + To use the most recent IERS-A file for the whole session:: + + >>> iers_a = iers.IERS_A.open(iers.IERS_A_URL) # doctest: +SKIP + >>> iers.earth_orientation_table.set(iers_a) # doctest: +SKIP + ...> + + To go back to the default (of `~astropy.utils.iers.IERS_Auto`):: + + >>> iers.earth_orientation_table.set(None) # doctest: +SKIP + ...> + """ + + _value = None + + @classmethod + def validate(cls, value): + if value is None: + value = IERS_Auto.open() + if not isinstance(value, IERS): + raise ValueError("earth_orientation_table requires an IERS Table.") + return value + + +class LeapSeconds(QTable): + """Leap seconds class, holding TAI-UTC differences. + + The table should hold columns 'year', 'month', 'tai_utc'. + + Methods are provided to initialize the table from IERS ``Leap_Second.dat``, + IETF/ntp ``leap-seconds.list``, or built-in ERFA/SOFA, and to update the + list used by ERFA. + + Notes + ----- + Astropy has a built-in ``iers.IERS_LEAP_SECONDS_FILE``. Up to date versions + can be downloaded from ``iers.IERS_LEAP_SECONDS_URL`` or + ``iers.LEAP_SECONDS_LIST_URL``. Many systems also store a version + of ``leap-seconds.list`` for use with ``ntp`` (e.g., on Debian/Ubuntu + systems, ``/usr/share/zoneinfo/leap-seconds.list``). + + To prevent querying internet resources if the available local leap second + file(s) are out of date, set ``iers.conf.auto_download = False``. This + must be done prior to performing any ``Time`` scale transformations related + to UTC (e.g. converting from UTC to TAI). + """ + + # Note: Time instances in this class should use scale='tai' to avoid + # needing leap seconds in their creation or interpretation. + + _re_expires = re.compile(r"^#.*File expires on[:\s]+(\d+\s\w+\s\d+)\s*$") + _expires = None + _auto_open_files = [ + "erfa", + IERS_LEAP_SECOND_FILE, + "system_leap_second_file", + "iers_leap_second_auto_url", + "ietf_leap_second_auto_url", + ] + """Files or conf attributes to try in auto_open.""" + + @classmethod + def open(cls, file=None, cache=False): + """Open a leap-second list. + + Parameters + ---------- + file : path-like or None + Full local or network path to the file holding leap-second data, + for passing on to the various ``from_`` class methods. + If 'erfa', return the data used by the ERFA library. + If `None`, use default locations from file and configuration to + find a table that is not expired. + cache : bool + Whether to use cache. Defaults to False, since leap-second files + are regularly updated. + + Returns + ------- + leap_seconds : `~astropy.utils.iers.LeapSeconds` + Table with 'year', 'month', and 'tai_utc' columns, plus possibly + others. + + Notes + ----- + Bulletin C is released about 10 days after a possible leap second is + introduced, i.e., mid-January or mid-July. Expiration days are thus + generally at least 150 days after the present. For the auto-loading, + a list comprised of the table shipped with astropy, and files and + URLs in `~astropy.utils.iers.Conf` are tried, returning the first + that is sufficiently new, or the newest among them all. + """ + if file is None: + return cls.auto_open() + + if file.lower() == "erfa": + return cls.from_erfa() + + if urlparse(file).netloc: + file = download_file(file, cache=cache) + + # Just try both reading methods. + try: + return cls.from_iers_leap_seconds(file) + except Exception: + return cls.from_leap_seconds_list(file) + + @staticmethod + def _today(): + # Get current day in scale='tai' without going through a scale change + # (so we do not need leap seconds). + s = "{0.year:04d}-{0.month:02d}-{0.day:02d}".format(datetime.now(tz=UTC)) + return Time(s, scale="tai", format="iso", out_subfmt="date") + + @classmethod + def auto_open(cls, files=None): + """Attempt to get an up-to-date leap-second list. + + The routine will try the files in sequence until it finds one + whose expiration date is "good enough" (see below). If none + are good enough, it returns the one with the most recent expiration + date, warning if that file is expired. + + For remote files that are cached already, the cached file is tried + first before attempting to retrieve it again. + + Parameters + ---------- + files : list of path-like, optional + List of files/URLs to attempt to open. By default, uses + ``cls._auto_open_files``. + + Returns + ------- + leap_seconds : `~astropy.utils.iers.LeapSeconds` + Up to date leap-second table + + Notes + ----- + Bulletin C is released about 10 days after a possible leap second is + introduced, i.e., mid-January or mid-July. Expiration days are thus + generally at least 150 days after the present. We look for a file + that expires more than 180 - `~astropy.utils.iers.Conf.auto_max_age` + after the present. + """ + offset = 180 - (30 if conf.auto_max_age is None else conf.auto_max_age) + good_enough = cls._today() + TimeDelta(offset, format="jd") + + if files is None: + # Basic files to go over (entries in _auto_open_files can be + # configuration items, which we want to be sure are up to date). + files = [getattr(conf, f, f) for f in cls._auto_open_files] + + # Remove empty entries and normalize Path objects to string + files = [os.fspath(f) for f in files if f] + + # Our trials start with normal files and remote ones that are + # already in cache. The bools here indicate that the cache + # should be used. + trials = [ + (f, True) + for f in files + if not urlparse(f).netloc or is_url_in_cache(f, on_missing="ignore") + ] + # If we are allowed to download, we try downloading new versions + # if none of the above worked. + if conf.auto_download: + trials += [(f, False) for f in files if urlparse(f).netloc] + + self = None + err_list = [] + # Go through all entries, and return the first one that + # is not expired, or the most up to date one. + for f, allow_cache in trials: + if not allow_cache: + clear_download_cache(f) + + try: + trial = cls.open(f, cache=True) + except Exception as exc: + err_list.append(exc) + continue + + if self is None or trial.expires > self.expires: + self = trial + self.meta["data_url"] = str(f) + if self.expires > good_enough: + break + + if self is None: + raise ValueError( + "none of the files could be read. The " + f"following errors were raised:\n {err_list}" + ) + + if self.expires < self._today() and conf.auto_max_age is not None: + warn("leap-second file is expired.", IERSStaleWarning) + + return self + + @property + def expires(self): + """The limit of validity of the table.""" + return self._expires + + @classmethod + def _read_leap_seconds(cls, file, **kwargs): + """Read a file, identifying expiration by matching 'File expires'.""" + expires = None + # Find expiration date. + with get_readable_fileobj(file) as fh: + lines = fh.readlines() + for line in lines: + match = cls._re_expires.match(line) + if match: + day, month, year = match.groups()[0].split() + month_nb = MONTH_ABBR.index(month[:3]) + 1 + expires = Time( + f"{year}-{month_nb:02d}-{day}", scale="tai", out_subfmt="date" + ) + break + else: + raise ValueError(f"did not find expiration date in {file}") + + self = cls.read(lines, format="ascii.no_header", **kwargs) + self._expires = expires + return self + + @classmethod + def from_iers_leap_seconds(cls, file=IERS_LEAP_SECOND_FILE): + """Create a table from a file like the IERS ``Leap_Second.dat``. + + Parameters + ---------- + file : path-like, optional + Full local or network path to the file holding leap-second data + in a format consistent with that used by IERS. By default, uses + ``iers.IERS_LEAP_SECOND_FILE``. + + Notes + ----- + The file *must* contain the expiration date in a comment line, like + '# File expires on 28 June 2020' + """ + return cls._read_leap_seconds( + file, names=["mjd", "day", "month", "year", "tai_utc"] + ) + + @classmethod + def from_leap_seconds_list(cls, file): + """Create a table from a file like the IETF ``leap-seconds.list``. + + Parameters + ---------- + file : path-like, optional + Full local or network path to the file holding leap-second data + in a format consistent with that used by IETF. Up to date versions + can be retrieved from ``iers.IETF_LEAP_SECOND_URL``. + + Notes + ----- + The file *must* contain the expiration date in a comment line, like + '# File expires on: 28 June 2020' + """ + from astropy.io.ascii import convert_numpy # Here to avoid circular import + + names = ["ntp_seconds", "tai_utc", "comment", "day", "month", "year"] + # Note: ntp_seconds does not fit in 32 bit, so causes problems on + # 32-bit systems without the np.int64 converter. + self = cls._read_leap_seconds( + file, + names=names, + include_names=names[:2], + converters={"ntp_seconds": [convert_numpy(np.int64)]}, + ) + self["mjd"] = (self["ntp_seconds"] / 86400 + 15020).round() + # Note: cannot use Time.ymdhms, since that might require leap seconds. + isot = Time(self["mjd"], format="mjd", scale="tai").isot + ymd = np.array( + [[int(part) for part in t.partition("T")[0].split("-")] for t in isot] + ) + self["year"], self["month"], self["day"] = ymd.T + return self + + @classmethod + def from_erfa(cls, built_in=False): + """Create table from the leap-second list in ERFA. + + Parameters + ---------- + built_in : bool + If `False` (default), retrieve the list currently used by ERFA, + which may have been updated. If `True`, retrieve the list shipped + with erfa. + """ + current = cls(erfa.leap_seconds.get()) + expires = erfa.leap_seconds.expires + current._expires = Time( + f"{expires.year:04d}-{expires.month:02d}-{expires.day:02d}", + scale="tai", + ) + if not built_in: + return current + + try: + erfa.leap_seconds.set(None) # reset to defaults + return cls.from_erfa(built_in=False) + finally: + erfa.leap_seconds.set(current) + + def update_erfa_leap_seconds(self, initialize_erfa=False): + """Add any leap seconds not already present to the ERFA table. + + This method matches leap seconds with those present in the ERFA table, + and extends the latter as necessary. + + Parameters + ---------- + initialize_erfa : bool, or 'only', or 'empty' + Initialize the ERFA leap second table to its built-in value before + trying to expand it. This is generally not needed but can help + in case it somehow got corrupted. If equal to 'only', the ERFA + table is reinitialized and no attempt it made to update it. + If 'empty', the leap second table is emptied before updating, i.e., + it is overwritten altogether (note that this may break things in + surprising ways, as most leap second tables do not include pre-1970 + pseudo leap-seconds; you were warned). + + Returns + ------- + n_update : int + Number of items updated. + + Raises + ------ + ValueError + If the leap seconds in the table are not on 1st of January or July, + or if the matches are inconsistent. This would normally suggest + a corrupted leap second table, but might also indicate that the + ERFA table was corrupted. If needed, the ERFA table can be reset + by calling this method with an appropriate value for + ``initialize_erfa``. + """ + if initialize_erfa == "empty": + # Initialize to empty and update is the same as overwrite. + erfa.leap_seconds.set(self) + return len(self) + + if initialize_erfa: + erfa.leap_seconds.set() + if initialize_erfa == "only": + return 0 + + return erfa.leap_seconds.update(self) diff --git a/astropy/utils/iers/tests/__init__.py b/astropy/utils/iers/tests/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/astropy/utils/iers/tests/data/finals2000A-2016-02-30-test b/astropy/utils/iers/tests/data/finals2000A-2016-02-30-test new file mode 100644 index 000000000000..6f011a621f80 --- /dev/null +++ b/astropy/utils/iers/tests/data/finals2000A-2016-02-30-test @@ -0,0 +1,181 @@ +16 2 1 57359.00 I -0.003225 0.000031 0.299494 0.000025 I 0.0262864 0.0000042 1.3154 0.0035 I -0.094 0.119 -0.077 0.029 -0.003291 0.299534 0.0262719 -0.094 -0.169 +16 2 2 57360.00 I -0.004706 0.000030 0.301252 0.000024 I 0.0249911 0.0000057 1.2412 0.0034 I -0.108 0.119 -0.082 0.035 -0.004751 0.301342 0.0249958 -0.128 -0.151 +16 2 3 57361.00 I -0.005690 0.000031 0.302711 0.000025 I 0.0238039 0.0000054 1.1674 0.0040 I -0.112 0.045 -0.102 0.034 -0.005725 0.302649 0.0238016 -0.137 -0.154 +16 2 4 57362.00 I -0.006490 0.000030 0.304493 0.000023 I 0.0226216 0.0000057 1.1976 0.0040 I -0.105 0.045 -0.115 0.034 -0.006418 0.304497 0.0226308 -0.139 -0.163 +16 2 5 57363.00 I -0.007499 0.000030 0.306410 0.000020 I 0.0214229 0.0000059 1.1850 0.0048 I -0.094 0.045 -0.114 0.034 -0.007481 0.306466 0.0214306 -0.142 -0.171 +16 2 6 57364.00 I -0.008637 0.000024 0.308121 0.000018 I 0.0201799 0.0000077 1.3874 0.0042 I -0.094 0.070 -0.115 0.034 -0.008646 0.308134 0.0201501 -0.145 -0.180 +16 2 7 57365.00 I -0.009431 0.000032 0.309779 0.000020 I 0.0186616 0.0000061 1.5494 0.0050 I -0.103 0.053 -0.117 0.032 -0.009564 0.309799 0.0187256 -0.146 -0.188 +16 2 8 57366.00 I -0.009639 0.000035 0.311456 0.000019 I 0.0171013 0.0000064 1.6508 0.0052 I -0.118 0.052 -0.112 0.039 -0.009602 0.311455 0.0171286 -0.150 -0.197 +16 2 9 57367.00 I -0.009707 0.000034 0.313146 0.000017 I 0.0153086 0.0000083 1.9064 0.0048 I -0.137 0.119 -0.106 0.046 -0.009695 0.313158 0.0153216 -0.151 -0.205 +16 210 57368.00 I -0.009949 0.000035 0.315007 0.000024 I 0.0133131 0.0000072 2.0881 0.0053 I -0.163 0.119 -0.110 0.053 -0.009848 0.314971 0.0133091 -0.162 -0.134 +16 211 57369.00 I -0.010522 0.000036 0.317056 0.000024 I 0.0111967 0.0000067 2.0770 0.0049 I -0.189 0.119 -0.121 0.053 -0.010525 0.317115 0.0112339 -0.182 -0.133 +16 212 57370.00 I -0.011219 0.000037 0.319016 0.000025 I 0.0091589 0.0000066 2.0665 0.0055 I -0.206 0.119 -0.119 0.053 -0.011200 0.319001 0.0091407 -0.206 -0.158 +16 213 57371.00 I -0.011874 0.000031 0.321066 0.000024 I 0.0070911 0.0000088 1.9738 0.0047 I -0.211 0.119 -0.106 0.072 -0.011889 0.321068 0.0071356 -0.208 -0.158 +16 214 57372.00 I -0.012456 0.000029 0.323240 0.000028 I 0.0052399 0.0000068 1.7954 0.0055 I -0.210 0.119 -0.093 0.047 -0.012445 0.323271 0.0052511 -0.202 -0.149 +16 215 57373.00 I -0.013108 0.000027 0.325311 0.000027 I 0.0034804 0.0000065 1.6767 0.0047 I -0.209 0.119 -0.080 0.056 -0.013071 0.325381 0.0035069 -0.194 -0.140 +16 216 57374.00 I -0.013911 0.000023 0.327010 0.000022 I 0.0019144 0.0000065 1.4745 0.0042 I -0.203 0.045 -0.057 0.050 -0.013912 0.327076 0.0019126 -0.187 -0.130 +16 217 57375.00 I -0.014798 0.000021 0.328533 0.000023 I 0.0004966 0.0000052 1.3769 0.0045 I -0.186 0.045 -0.032 0.050 -0.014734 0.328472 0.0004608 -0.191 -0.037 +16 218 57376.00 I -0.015972 0.000018 0.330387 0.000023 I-0.0008425 0.0000063 1.2886 0.0043 I -0.166 0.045 -0.025 0.050 -0.015929 0.330417 -0.0008581 -0.186 -0.027 +16 219 57377.00 I -0.017359 0.000017 0.332361 0.000022 I-0.0021162 0.0000068 1.3072 0.0107 I -0.150 0.045 -0.032 0.050 -0.017369 0.332401 -0.0021130 -0.179 -0.049 +16 220 57378.00 I -0.018572 0.000019 0.334012 0.000020 I-0.0034607 0.0000205 1.3373 0.0040 I -0.135 0.082 -0.033 0.156 -0.018639 0.334094 -0.0034487 -0.171 -0.071 +16 221 57379.00 I -0.019315 0.000033 0.335553 0.000023 I-0.0048274 0.0000044 1.4772 0.0105 I -0.120 0.119 -0.029 0.011 -0.019329 0.335459 -0.0048827 -0.164 -0.092 +16 222 57380.00 I -0.019952 0.000034 0.337603 0.000023 I-0.0064226 0.0000046 1.6395 0.0033 I -0.108 0.119 -0.038 0.011 -0.019862 0.337623 -0.0064324 -0.156 -0.114 +16 223 57381.00 I -0.020869 0.000035 0.339814 0.000022 I-0.0080691 0.0000049 1.6897 0.0032 I -0.103 0.119 -0.063 0.027 -0.020905 0.339830 -0.0080787 -0.149 -0.136 +16 224 57382.00 I -0.021582 0.000037 0.342052 0.000026 I-0.0098083 0.0000045 1.7588 0.0034 I -0.096 0.119 -0.079 0.033 -0.021616 0.342062 -0.0097889 -0.143 -0.149 +16 225 57383.00 I -0.021898 0.000037 0.344332 0.000025 I-0.0115839 0.0000046 1.8195 0.0036 I -0.093 0.119 -0.073 0.033 -0.021911 0.344416 -0.0115637 -0.139 -0.160 +16 226 57384.00 I -0.022036 0.000036 0.346321 0.000022 I-0.0134316 0.0000055 1.8329 0.0048 I -0.102 0.119 -0.062 0.033 -0.022011 0.346307 -0.0134119 -0.133 -0.171 +16 227 57385.00 I -0.022387 0.000024 0.348429 0.000019 I-0.0152292 0.0000084 1.7846 0.0042 I -0.118 0.119 -0.064 0.059 -0.022273 0.348402 -0.0152189 -0.129 -0.181 +16 228 57386.00 I -0.023235 0.000025 0.350749 0.000020 I-0.0170194 0.0000064 1.7921 0.0052 I -0.122 0.119 -0.082 0.035 -0.023218 0.350815 -0.0169781 -0.124 -0.192 +16 229 57387.00 I -0.024219 0.000023 0.352784 0.000020 I-0.0187707 0.0000062 1.6716 0.0053 I -0.111 0.119 -0.105 0.027 -0.024254 0.352863 -0.0186952 -0.119 -0.203 +16 3 1 57388.00 I -0.024807 0.000023 0.354400 0.000014 I-0.0203703 0.0000084 1.5743 0.0046 I -0.088 0.119 -0.125 0.011 -0.024918 0.354451 -0.0203678 -0.114 -0.214 +16 3 2 57389.00 I -0.024651 0.000025 0.355942 0.000020 I-0.0219780 0.0000067 1.6532 0.0052 I -0.055 0.119 -0.125 0.049 +16 3 3 57390.00 I -0.024054 0.000025 0.357866 0.000019 I-0.0236772 0.0000060 1.7337 0.0044 I -0.016 0.119 -0.093 0.049 +16 3 4 57391.00 I -0.023735 0.000027 0.360180 0.000019 I-0.0254270 0.0000057 1.7591 0.0052 I 0.008 0.119 -0.056 0.049 +16 3 5 57392.00 I -0.023740 0.000026 0.362598 0.000018 I-0.0272149 0.0000084 1.8430 0.0041 I 0.011 0.119 -0.053 0.091 +16 3 6 57393.00 I -0.023745 0.000027 0.365029 0.000018 I-0.0291495 0.0000060 2.0365 0.0052 I 0.010 0.119 -0.081 0.052 +16 3 7 57394.00 I -0.024021 0.000024 0.367538 0.000018 I-0.0312844 0.0000060 2.2194 0.0045 I 0.021 0.119 -0.095 0.052 +16 3 8 57395.00 I -0.024457 0.000021 0.370024 0.000012 I-0.0335554 0.0000066 2.3035 0.0044 I 0.036 0.119 -0.085 0.026 +16 3 9 57396.00 I -0.024829 0.000022 0.372483 0.000011 I-0.0359012 0.0000065 2.4168 0.0045 I 0.037 0.119 -0.072 0.026 +16 310 57397.00 I -0.025066 0.000020 0.374873 0.000011 I-0.0383738 0.0000062 2.4843 0.0044 I 0.025 0.119 -0.063 0.026 +16 311 57398.00 I -0.025097 0.000017 0.377004 0.000011 I-0.0408388 0.0000060 2.4588 0.0080 I 0.011 0.119 -0.053 0.026 +16 312 57399.00 I -0.025181 0.000017 0.378932 0.000012 I-0.0432700 0.0000148 2.3690 0.0040 I 0.008 0.119 -0.040 0.150 +16 313 57400.00 I -0.025188 0.000024 0.380780 0.000016 I-0.0455624 0.0000054 2.2359 0.0079 I 0.013 0.119 -0.031 0.150 +16 314 57401.00 I -0.024758 0.000026 0.382629 0.000016 I-0.0477465 0.0000056 2.1168 0.0038 I 0.011 0.119 -0.012 0.150 +16 315 57402.00 I -0.023945 0.000025 0.384773 0.000017 I-0.0497744 0.0000053 1.9330 0.0037 I -0.002 0.119 0.022 0.150 +16 316 57403.00 I -0.022702 0.000026 0.387109 0.000023 I-0.0516492 0.0000048 1.8593 0.0035 I -0.012 0.042 0.057 0.020 +16 317 57404.00 I -0.020782 0.000026 0.389469 0.000023 I-0.0535037 0.0000047 1.8176 0.0034 I -0.009 0.042 0.067 0.020 +16 318 57405.00 I -0.018391 0.000025 0.391971 0.000022 I-0.0552954 0.0000047 1.8089 0.0041 I 0.001 0.042 0.060 0.020 +16 319 57406.00 I -0.016187 0.000017 0.394472 0.000020 I-0.0571458 0.0000067 1.8730 0.0036 I 0.003 0.047 0.046 0.029 +16 320 57407.00 I -0.014601 0.000025 0.396962 0.000029 I-0.0590734 0.0000055 2.0332 0.0044 I -0.007 0.052 0.033 0.049 +16 321 57408.00 I -0.013767 0.000026 0.399494 0.000030 I-0.0611669 0.0000057 2.0575 0.0043 I -0.028 0.052 0.021 0.056 +16 322 57409.00 I -0.013562 0.000024 0.402015 0.000024 I-0.0631898 0.0000065 2.0861 0.0043 I -0.054 0.059 0.010 0.076 +16 323 57410.00 I -0.013645 0.000024 0.404333 0.000024 I-0.0653380 0.0000064 2.1115 0.0046 I -0.074 0.059 0.009 0.076 +16 324 57411.00 I -0.013616 0.000024 0.406392 0.000024 I-0.0673565 0.0000066 1.9679 0.0046 I -0.088 0.059 0.018 0.076 +16 325 57412.00 I -0.013255 0.000024 0.408403 0.000024 I-0.0693406 0.0000065 2.0248 0.0063 I -0.109 0.059 0.024 0.076 +16 326 57413.00 I -0.012617 0.000013 0.410472 0.000013 I-0.0713968 0.0000108 2.0538 0.0066 I -0.134 0.119 0.017 0.225 +16 327 57414.00 I -0.011855 0.000011 0.412577 0.000013 I-0.0734180 0.0000116 1.9922 0.0069 I -0.144 0.119 -0.003 0.150 +16 328 57415.00 I -0.011092 0.000023 0.414506 0.000021 I-0.0753619 0.0000085 1.8727 0.0072 I -0.132 0.058 -0.029 0.069 +16 329 57416.00 I -0.010256 0.000023 0.416297 0.000022 I-0.0771761 0.0000085 1.7914 0.0055 I -0.106 0.058 -0.054 0.069 +16 330 57417.00 I -0.009394 0.000023 0.418007 0.000023 I-0.0789625 0.0000070 1.7637 0.0055 I -0.070 0.047 -0.058 0.063 +16 331 57418.00 I -0.008769 0.000022 0.419685 0.000025 I-0.0807036 0.0000069 1.7363 0.0049 I -0.034 0.047 -0.029 0.063 +16 4 1 57419.00 I -0.008414 0.000022 0.421202 0.000027 I-0.0824533 0.0000068 1.7634 0.0049 I -0.021 0.047 0.000 0.063 +16 4 2 57420.00 I -0.008088 0.000022 0.422663 0.000027 I-0.0842517 0.0000069 1.8544 0.0042 I -0.036 0.047 -0.021 0.063 +16 4 3 57421.00 I -0.007515 0.000013 0.424137 0.000024 I-0.0861632 0.0000050 1.9487 0.0047 I -0.052 0.119 -0.082 0.024 +16 4 4 57422.00 I -0.006419 0.000021 0.425858 0.000023 I-0.0881549 0.0000064 2.0606 0.0045 I -0.040 0.119 -0.113 0.024 +16 4 5 57423.00 I -0.004709 0.000022 0.427854 0.000022 I-0.0902894 0.0000074 2.1876 0.0049 I -0.016 0.119 -0.084 0.014 +16 4 6 57424.00 I -0.002606 0.000022 0.429902 0.000021 I-0.0925256 0.0000073 2.3011 0.0052 I -0.002 0.119 -0.031 0.014 +16 4 7 57425.00 I -0.000789 0.000022 0.432202 0.000018 I-0.0948791 0.0000073 2.3772 0.0052 I 0.000 0.119 0.010 0.014 +16 4 8 57426.00 I 0.000664 0.000022 0.434713 0.000016 I-0.0972531 0.0000073 2.3725 0.0122 I -0.002 0.119 0.040 0.014 +16 4 9 57427.00 I 0.002059 0.000023 0.437042 0.000013 I-0.0995861 0.0000232 2.2539 0.0054 I -0.006 0.119 0.063 0.150 +16 410 57428.00 I 0.003347 0.000021 0.439038 0.000016 I-0.1017251 0.0000079 2.0278 0.0120 I -0.008 0.119 0.072 0.061 +16 411 57429.00 I 0.004628 0.000019 0.440926 0.000015 I-0.1036627 0.0000060 1.8647 0.0049 I -0.005 0.119 0.078 0.061 +16 412 57430.00 I 0.006143 0.000019 0.442665 0.000016 I-0.1054713 0.0000059 1.7520 0.0042 I 0.004 0.119 0.096 0.061 +16 413 57431.00 I 0.007737 0.000020 0.444189 0.000016 I-0.1071693 0.0000060 1.6470 0.0044 I 0.019 0.119 0.114 0.061 +16 414 57432.00 I 0.009116 0.000020 0.445678 0.000016 I-0.1087724 0.0000066 1.5633 0.0046 I 0.046 0.119 0.118 0.061 +16 415 57433.00 I 0.010471 0.000019 0.447462 0.000017 I-0.1103218 0.0000069 1.5574 0.0087 I 0.076 0.119 0.108 0.061 +16 416 57434.00 I 0.011967 0.000020 0.449627 0.000018 I-0.1119079 0.0000162 1.6135 0.0060 I 0.095 0.119 0.095 0.150 +16 417 57435.00 I 0.013661 0.000025 0.451890 0.000023 I-0.1135287 0.0000097 1.6091 0.0095 I 0.099 0.119 0.086 0.013 +16 418 57436.00 I 0.015451 0.000028 0.454016 0.000028 I-0.1151529 0.0000098 1.6851 0.0068 I 0.090 0.119 0.090 0.013 +16 419 57437.00 I 0.017078 0.000032 0.455707 0.000031 I-0.1169025 0.0000096 1.7719 0.0070 I 0.070 0.119 0.113 0.013 +16 420 57438.00 I 0.018626 0.000041 0.456788 0.000041 I-0.1186505 0.0000101 1.7201 0.0062 P 0.011 0.239 0.092 0.600 +16 421 57439.00 I 0.020312 0.000044 0.457663 0.000043 I-0.1203383 0.0000079 1.6551 0.0058 P 0.002 0.239 0.097 0.600 +16 422 57440.00 I 0.022149 0.000091 0.458767 0.000093 I-0.1219712 0.0000056 1.6216 0.0057 P -0.002 0.239 0.074 0.600 +16 423 57441.00 I 0.024059 0.000091 0.460210 0.000092 I-0.1235866 0.0000083 1.6033 0.0048 P -0.009 0.239 0.041 0.600 +16 424 57442.00 I 0.025767 0.000091 0.461829 0.000092 I-0.1251404 0.0000079 1.4728 0.0092 P -0.007 0.239 0.018 0.600 +16 425 57443.00 I 0.027084 0.000091 0.463531 0.000093 I-0.1265278 0.0000165 1.3290 0.0090 P 0.013 0.239 0.007 0.600 +16 426 57444.00 I 0.028086 0.000091 0.465338 0.000093 I-0.1278231 0.0000161 1.2593 0.0118 P 0.040 0.239 0.003 0.600 +16 427 57445.00 I 0.028928 0.000091 0.467015 0.000092 I-0.1290372 0.0000169 1.1618 0.0123 P 0.065 0.239 0.008 0.600 +16 428 57446.00 I 0.029730 0.000092 0.468564 0.000091 I-0.1303716 0.0000185 P 0.083 0.239 0.035 0.600 +16 429 57447.00 P 0.030798 0.000622 0.469783 0.000448 P-0.1317429 0.0001080 P 0.085 0.239 0.066 0.600 +16 430 57448.00 P 0.031994 0.000923 0.470993 0.000737 P-0.1331878 0.0002041 P 0.065 0.239 0.057 0.600 +16 5 1 57449.00 P 0.033490 0.001163 0.472083 0.000987 P-0.1347405 0.0003028 P 0.040 0.239 0.007 0.600 +16 5 2 57450.00 P 0.035183 0.001370 0.473116 0.001215 P-0.1364495 0.0004021 P 0.034 0.239 -0.028 0.600 +16 5 3 57451.00 P 0.036987 0.001556 0.474102 0.001426 P-0.1383398 0.0005017 P 0.043 0.239 -0.006 0.600 +16 5 4 57452.00 P 0.038845 0.001727 0.475046 0.001626 P-0.1403703 0.0006014 P 0.046 0.239 0.044 0.600 +16 5 5 57453.00 P 0.040719 0.001885 0.475994 0.001817 P-0.1424457 0.0007012 P 0.037 0.239 0.082 0.600 +16 5 6 57454.00 P 0.042581 0.002035 0.476950 0.002001 P-0.1444593 0.0007500 P 0.028 0.239 0.101 0.600 +16 5 7 57455.00 P 0.044425 0.002176 0.477871 0.002178 P-0.1463307 0.0005500 P 0.014 0.239 0.107 0.600 +16 5 8 57456.00 P 0.046286 0.002310 0.478748 0.002349 P-0.1480080 0.0007548 P -0.006 0.239 0.096 0.600 +16 5 9 57457.00 P 0.048207 0.002439 0.479583 0.002516 P-0.1494885 0.0009344 P -0.019 0.239 0.073 0.600 +16 510 57458.00 P 0.050193 0.002564 0.480379 0.002679 P-0.1508450 0.0010994 P -0.012 0.239 0.056 0.600 +16 511 57459.00 P 0.052250 0.002683 0.481153 0.002838 P-0.1521313 0.0012543 P 0.016 0.239 0.051 0.600 +16 512 57460.00 P 0.054368 0.002799 0.481924 0.002993 P-0.1533788 0.0014016 P 0.052 0.239 0.053 0.600 +16 513 57461.00 P 0.056522 0.002911 0.482690 0.003146 P-0.1546475 0.0015429 P 0.077 0.239 0.062 0.600 +16 514 57462.00 P 0.058696 0.003020 0.483452 0.003295 P-0.1559391 0.0016792 P 0.082 0.239 0.068 0.600 +16 515 57463.00 P 0.060870 0.003127 0.484205 0.003442 P-0.1572557 0.0018113 P 0.077 0.239 0.061 0.600 +16 516 57464.00 P 0.063031 0.003230 0.484931 0.003587 P-0.1585736 0.0019399 P 0.067 0.239 0.062 0.600 +16 517 57465.00 P 0.065179 0.003331 0.485615 0.003729 P-0.1598755 0.0020652 P 0.046 0.239 0.087 0.600 +16 518 57466.00 P 0.067327 0.003430 0.486255 0.003870 P-0.1611463 0.0021877 P 0.017 0.239 0.124 0.600 +16 519 57467.00 P 0.069480 0.003527 0.486853 0.004008 P-0.1623693 0.0023077 P -0.001 0.239 0.132 0.600 +16 520 57468.00 P 0.071641 0.003621 0.487411 0.004145 P-0.1635235 0.0024255 P 0.001 0.239 0.103 0.600 +16 521 57469.00 P 0.073815 0.003714 0.487931 0.004279 P-0.1645892 0.0025411 P 0.009 0.239 0.066 0.600 +16 522 57470.00 P 0.076004 0.003806 0.488420 0.004413 P-0.1655561 0.0026548 P 0.018 0.239 0.050 0.600 +16 523 57471.00 P 0.078203 0.003895 0.488880 0.004544 P-0.1664451 0.0027668 P 0.037 0.239 0.053 0.600 +16 524 57472.00 P 0.080408 0.003983 0.489309 0.004674 P-0.1672908 0.0028772 P 0.059 0.239 0.062 0.600 +16 525 57473.00 P 0.082617 0.004070 0.489704 0.004803 P-0.1681336 0.0029860 P 0.067 0.239 0.075 0.600 +16 526 57474.00 P 0.084827 0.004155 0.490064 0.004930 P-0.1690194 0.0030934 P 0.059 0.239 0.093 0.600 +16 527 57475.00 P 0.087040 0.004239 0.490383 0.005057 P-0.1699970 0.0031995 P 0.047 0.239 0.110 0.600 +16 528 57476.00 P 0.089257 0.004322 0.490663 0.005182 P-0.1710955 0.0033043 P 0.037 0.239 0.111 0.600 +16 529 57477.00 P 0.091478 0.004403 0.490904 0.005305 P-0.1723451 0.0034080 P 0.027 0.239 0.088 0.600 +16 530 57478.00 P 0.093706 0.004484 0.491106 0.005428 P-0.1737501 0.0035105 P 0.023 0.239 0.065 0.600 +16 531 57479.00 P 0.095941 0.004563 0.491273 0.005550 P-0.1752782 0.0036120 P 0.025 0.239 0.063 0.600 +16 6 1 57480.00 P 0.098182 0.004641 0.491405 0.005670 P-0.1768742 0.0037124 P 0.023 0.239 0.083 0.600 +16 6 2 57481.00 P 0.100428 0.004719 0.491506 0.005790 P-0.1784618 0.0038119 P 0.012 0.239 0.109 0.600 +16 6 3 57482.00 P 0.102675 0.004795 0.491574 0.005908 P-0.1799593 0.0039105 P 0.000 0.239 0.131 0.600 +16 6 4 57483.00 P 0.104924 0.004871 0.491610 0.006026 P-0.1813071 0.0040082 P -0.005 0.239 0.137 0.600 +16 6 5 57484.00 P 0.107172 0.004945 0.491612 0.006143 P-0.1824904 0.0041051 P -0.005 0.239 0.124 0.600 +16 6 6 57485.00 P 0.109420 0.005019 0.491579 0.006259 P-0.1835411 0.0042012 P -0.003 0.239 0.101 0.600 +16 6 7 57486.00 P 0.111667 0.005092 0.491512 0.006374 P-0.1845091 0.0042965 P 0.007 0.239 0.085 0.600 +16 6 8 57487.00 P 0.113913 0.005164 0.491411 0.006488 P-0.1854466 0.0043911 P 0.030 0.239 0.084 0.600 +16 6 9 57488.00 P 0.116157 0.005235 0.491274 0.006602 P-0.1863873 0.0044849 P 0.055 0.239 0.098 0.600 +16 610 57489.00 P 0.118399 0.005306 0.491104 0.006715 P-0.1873523 0.0045781 P 0.065 0.239 0.127 0.600 +16 611 57490.00 P 0.120639 0.005376 0.490901 0.006827 P-0.1883409 0.0046706 P 0.060 0.239 0.154 0.600 +16 612 57491.00 P 0.122875 0.005445 0.490664 0.006938 P-0.1893388 0.0047625 P 0.052 0.239 0.159 0.600 +16 613 57492.00 P 0.125106 0.005514 0.490394 0.007049 P-0.1903249 0.0048537 P 0.046 0.239 0.145 0.600 +16 614 57493.00 P 0.127331 0.005582 0.490090 0.007159 P-0.1912717 0.0049444 P 0.032 0.239 0.141 0.600 +16 615 57494.00 P 0.129551 0.005650 0.489753 0.007268 P-0.1921561 0.0050344 P 0.005 0.239 0.154 0.600 +16 616 57495.00 P 0.131763 0.005716 0.489382 0.007377 P-0.1929601 0.0051240 P -0.012 0.239 0.159 0.600 +16 617 57496.00 P 0.133969 0.005782 0.488978 0.007485 P-0.1936729 0.0052129 P -0.009 0.239 0.143 0.600 +16 618 57497.00 P 0.136167 0.005848 0.488539 0.007592 P-0.1942946 0.0053014 P 0.004 0.239 0.122 0.600 +16 619 57498.00 P 0.138356 0.005913 0.488067 0.007699 P-0.1948369 0.0053893 P 0.015 0.239 0.115 0.600 +16 620 57499.00 P 0.140538 0.005978 0.487561 0.007806 P-0.1953178 0.0054768 P 0.029 0.239 0.117 0.600 +16 621 57500.00 P 0.142710 0.006042 0.487022 0.007911 P-0.1957658 0.0055637 P 0.048 0.239 0.121 0.600 +16 622 57501.00 P 0.144872 0.006105 0.486450 0.008017 P-0.1962213 0.0056502 P 0.056 0.239 0.132 0.600 +16 623 57502.00 P 0.147024 0.006168 0.485846 0.008121 P-0.1967329 0.0057362 P 0.040 0.239 0.147 0.600 +16 624 57503.00 P 0.149165 0.006231 0.485208 0.008226 P-0.1973441 0.0058218 P 0.019 0.239 0.155 0.600 +16 625 57504.00 P 0.151294 0.006293 0.484538 0.008329 P-0.1980871 0.0059070 P 0.009 0.239 0.148 0.600 +16 626 57505.00 P 0.153411 0.006355 0.483836 0.008432 P-0.1989695 0.0059917 P 0.009 0.239 0.141 0.600 +16 627 57506.00 P 0.155515 0.006416 0.483101 0.008535 P-0.1999726 0.0060760 P 0.010 0.239 0.141 0.600 +16 628 57507.00 P 0.157606 0.006477 0.482334 0.008637 P-0.2010510 0.0061599 P 0.007 0.239 0.138 0.600 +16 629 57508.00 P 0.159683 0.006537 0.481535 0.008739 P-0.2021438 0.0062434 P 0.002 0.239 0.130 0.600 +16 630 57509.00 P 0.161745 0.006597 0.480705 0.008840 P-0.2031853 0.0063266 P -0.007 0.239 0.133 0.600 +16 7 1 57510.00 P 0.163793 0.006656 0.479843 0.008941 P-0.2041214 0.0064093 P -0.016 0.239 0.151 0.600 +16 7 2 57511.00 P 0.165825 0.006715 0.478950 0.009041 P-0.2049288 0.0064917 P -0.018 0.239 0.166 0.600 +16 7 3 57512.00 P 0.167840 0.006774 0.478026 0.009141 P-0.2056188 0.0065737 P -0.007 0.239 0.157 0.600 +16 7 4 57513.00 P 0.169839 0.006832 0.477071 0.009241 P-0.2062331 0.0066554 P 0.011 0.239 0.140 0.600 +16 7 5 57514.00 P 0.171821 0.006890 0.476086 0.009340 P-0.2068275 0.0067368 P 0.024 0.239 0.138 0.600 +16 7 6 57515.00 P 0.173785 0.006948 0.475071 0.009439 P-0.2074512 0.0068177 P 0.031 0.239 0.152 0.600 +16 7 7 57516.00 P 0.175730 0.007005 0.474025 0.009537 P-0.2081302 0.0068984 P 0.033 0.239 0.174 0.600 +16 7 8 57517.00 P 0.177656 0.007062 0.472950 0.009635 P-0.2088663 0.0069788 P 0.031 0.239 0.202 0.600 +16 7 9 57518.00 P 0.179563 0.007118 0.471846 0.009732 P-0.2096451 0.0070588 P 0.027 0.239 0.235 0.600 +16 710 57519.00 P 0.181450 0.007175 0.470713 0.009829 P-0.2104419 0.0071385 P 0.025 0.239 0.250 0.600 +16 711 57520.00 P 0.183317 0.007230 0.469550 0.009926 P-0.2112284 0.0072179 P 0.024 0.239 0.237 0.600 +16 712 57521.00 P 0.185162 0.007286 0.468360 0.010023 P-0.2119781 0.0072970 P 0.013 0.239 0.216 0.600 +16 713 57522.00 P 0.186986 0.007341 0.467141 0.010119 P-0.2126684 0.0073758 P -0.003 0.239 0.205 0.600 +16 714 57523.00 P 0.188788 0.007396 0.465894 0.010214 P-0.2132848 0.0074544 P -0.011 0.239 0.203 0.600 +16 715 57524.00 P 0.190567 0.007451 0.464620 0.010310 P-0.2138206 0.0075326 P -0.007 0.239 0.193 0.600 +16 716 57525.00 P 0.192324 0.007505 0.463319 0.010405 P-0.2142803 0.0076106 P 0.000 0.239 0.179 0.600 +16 717 57526.00 P 0.194056 0.007559 0.461990 0.010499 P-0.2146833 0.0076883 P 0.004 0.239 0.170 0.600 +16 718 57527.00 P 0.195765 0.007613 0.460636 0.010594 P-0.2150602 0.0077657 P 0.013 0.239 0.165 0.600 +16 719 57528.00 P 0.197449 0.007666 0.459255 0.010688 P-0.2154534 0.0078428 +16 720 57529.00 P 0.199109 0.007719 0.457848 0.010781 P-0.2159127 0.0079197 +16 721 57530.00 P 0.200742 0.007772 0.456416 0.010875 P-0.2164842 0.0079964 +16 722 57531.00 P 0.202351 0.007825 0.454960 0.010968 P-0.2172005 0.0080728 +16 723 57532.00 P 0.203932 0.007877 0.453478 0.011060 P-0.2180702 0.0081489 +16 724 57533.00 P 0.205487 0.007929 0.451972 0.011153 P-0.2190692 0.0082248 +16 725 57534.00 P 0.207015 0.007981 0.450443 0.011245 P-0.2201448 0.0083005 +16 726 57535.00 P 0.208515 0.008033 0.448890 0.011337 P-0.2212286 0.0083759 +16 727 57536.00 P 0.209988 0.008084 0.447315 0.011428 P-0.2222556 0.0084511 +16 728 57537.00 P 0.211431 0.008135 0.445716 0.011520 P-0.2231750 0.0085261 +16 729 57538.00 P 0.212846 0.008186 0.444096 0.011611 P-0.2239618 0.0086008 +16 730 57539.00 P 0.214232 0.008236 0.442454 0.011701 P-0.2246227 0.0086754 diff --git a/astropy/utils/iers/tests/data/finals2000A-2016-04-30-test b/astropy/utils/iers/tests/data/finals2000A-2016-04-30-test new file mode 100644 index 000000000000..bec95018790d --- /dev/null +++ b/astropy/utils/iers/tests/data/finals2000A-2016-04-30-test @@ -0,0 +1,181 @@ +16 2 1 57419.00 I -0.003225 0.000031 0.299494 0.000025 I 0.0262864 0.0000042 1.3154 0.0035 I -0.094 0.119 -0.077 0.029 -0.003291 0.299534 0.0262719 -0.094 -0.169 +16 2 2 57420.00 I -0.004706 0.000030 0.301252 0.000024 I 0.0249911 0.0000057 1.2412 0.0034 I -0.108 0.119 -0.082 0.035 -0.004751 0.301342 0.0249958 -0.128 -0.151 +16 2 3 57421.00 I -0.005690 0.000031 0.302711 0.000025 I 0.0238039 0.0000054 1.1674 0.0040 I -0.112 0.045 -0.102 0.034 -0.005725 0.302649 0.0238016 -0.137 -0.154 +16 2 4 57422.00 I -0.006490 0.000030 0.304493 0.000023 I 0.0226216 0.0000057 1.1976 0.0040 I -0.105 0.045 -0.115 0.034 -0.006418 0.304497 0.0226308 -0.139 -0.163 +16 2 5 57423.00 I -0.007499 0.000030 0.306410 0.000020 I 0.0214229 0.0000059 1.1850 0.0048 I -0.094 0.045 -0.114 0.034 -0.007481 0.306466 0.0214306 -0.142 -0.171 +16 2 6 57424.00 I -0.008637 0.000024 0.308121 0.000018 I 0.0201799 0.0000077 1.3874 0.0042 I -0.094 0.070 -0.115 0.034 -0.008646 0.308134 0.0201501 -0.145 -0.180 +16 2 7 57425.00 I -0.009431 0.000032 0.309779 0.000020 I 0.0186616 0.0000061 1.5494 0.0050 I -0.103 0.053 -0.117 0.032 -0.009564 0.309799 0.0187256 -0.146 -0.188 +16 2 8 57426.00 I -0.009639 0.000035 0.311456 0.000019 I 0.0171013 0.0000064 1.6508 0.0052 I -0.118 0.052 -0.112 0.039 -0.009602 0.311455 0.0171286 -0.150 -0.197 +16 2 9 57427.00 I -0.009707 0.000034 0.313146 0.000017 I 0.0153086 0.0000083 1.9064 0.0048 I -0.137 0.119 -0.106 0.046 -0.009695 0.313158 0.0153216 -0.151 -0.205 +16 210 57428.00 I -0.009949 0.000035 0.315007 0.000024 I 0.0133131 0.0000072 2.0881 0.0053 I -0.163 0.119 -0.110 0.053 -0.009848 0.314971 0.0133091 -0.162 -0.134 +16 211 57429.00 I -0.010522 0.000036 0.317056 0.000024 I 0.0111967 0.0000067 2.0770 0.0049 I -0.189 0.119 -0.121 0.053 -0.010525 0.317115 0.0112339 -0.182 -0.133 +16 212 57430.00 I -0.011219 0.000037 0.319016 0.000025 I 0.0091589 0.0000066 2.0665 0.0055 I -0.206 0.119 -0.119 0.053 -0.011200 0.319001 0.0091407 -0.206 -0.158 +16 213 57431.00 I -0.011874 0.000031 0.321066 0.000024 I 0.0070911 0.0000088 1.9738 0.0047 I -0.211 0.119 -0.106 0.072 -0.011889 0.321068 0.0071356 -0.208 -0.158 +16 214 57432.00 I -0.012456 0.000029 0.323240 0.000028 I 0.0052399 0.0000068 1.7954 0.0055 I -0.210 0.119 -0.093 0.047 -0.012445 0.323271 0.0052511 -0.202 -0.149 +16 215 57433.00 I -0.013108 0.000027 0.325311 0.000027 I 0.0034804 0.0000065 1.6767 0.0047 I -0.209 0.119 -0.080 0.056 -0.013071 0.325381 0.0035069 -0.194 -0.140 +16 216 57434.00 I -0.013911 0.000023 0.327010 0.000022 I 0.0019144 0.0000065 1.4745 0.0042 I -0.203 0.045 -0.057 0.050 -0.013912 0.327076 0.0019126 -0.187 -0.130 +16 217 57435.00 I -0.014798 0.000021 0.328533 0.000023 I 0.0004966 0.0000052 1.3769 0.0045 I -0.186 0.045 -0.032 0.050 -0.014734 0.328472 0.0004608 -0.191 -0.037 +16 218 57436.00 I -0.015972 0.000018 0.330387 0.000023 I-0.0008425 0.0000063 1.2886 0.0043 I -0.166 0.045 -0.025 0.050 -0.015929 0.330417 -0.0008581 -0.186 -0.027 +16 219 57437.00 I -0.017359 0.000017 0.332361 0.000022 I-0.0021162 0.0000068 1.3072 0.0107 I -0.150 0.045 -0.032 0.050 -0.017369 0.332401 -0.0021130 -0.179 -0.049 +16 220 57438.00 I -0.018572 0.000019 0.334012 0.000020 I-0.0034607 0.0000205 1.3373 0.0040 I -0.135 0.082 -0.033 0.156 -0.018639 0.334094 -0.0034487 -0.171 -0.071 +16 221 57439.00 I -0.019315 0.000033 0.335553 0.000023 I-0.0048274 0.0000044 1.4772 0.0105 I -0.120 0.119 -0.029 0.011 -0.019329 0.335459 -0.0048827 -0.164 -0.092 +16 222 57440.00 I -0.019952 0.000034 0.337603 0.000023 I-0.0064226 0.0000046 1.6395 0.0033 I -0.108 0.119 -0.038 0.011 -0.019862 0.337623 -0.0064324 -0.156 -0.114 +16 223 57441.00 I -0.020869 0.000035 0.339814 0.000022 I-0.0080691 0.0000049 1.6897 0.0032 I -0.103 0.119 -0.063 0.027 -0.020905 0.339830 -0.0080787 -0.149 -0.136 +16 224 57442.00 I -0.021582 0.000037 0.342052 0.000026 I-0.0098083 0.0000045 1.7588 0.0034 I -0.096 0.119 -0.079 0.033 -0.021616 0.342062 -0.0097889 -0.143 -0.149 +16 225 57443.00 I -0.021898 0.000037 0.344332 0.000025 I-0.0115839 0.0000046 1.8195 0.0036 I -0.093 0.119 -0.073 0.033 -0.021911 0.344416 -0.0115637 -0.139 -0.160 +16 226 57444.00 I -0.022036 0.000036 0.346321 0.000022 I-0.0134316 0.0000055 1.8329 0.0048 I -0.102 0.119 -0.062 0.033 -0.022011 0.346307 -0.0134119 -0.133 -0.171 +16 227 57445.00 I -0.022387 0.000024 0.348429 0.000019 I-0.0152292 0.0000084 1.7846 0.0042 I -0.118 0.119 -0.064 0.059 -0.022273 0.348402 -0.0152189 -0.129 -0.181 +16 228 57446.00 I -0.023235 0.000025 0.350749 0.000020 I-0.0170194 0.0000064 1.7921 0.0052 I -0.122 0.119 -0.082 0.035 -0.023218 0.350815 -0.0169781 -0.124 -0.192 +16 229 57447.00 I -0.024219 0.000023 0.352784 0.000020 I-0.0187707 0.0000062 1.6716 0.0053 I -0.111 0.119 -0.105 0.027 -0.024254 0.352863 -0.0186952 -0.119 -0.203 +16 3 1 57448.00 I -0.024807 0.000023 0.354400 0.000014 I-0.0203703 0.0000084 1.5743 0.0046 I -0.088 0.119 -0.125 0.011 -0.024918 0.354451 -0.0203678 -0.114 -0.214 +16 3 2 57449.00 I -0.024651 0.000025 0.355942 0.000020 I-0.0219780 0.0000067 1.6532 0.0052 I -0.055 0.119 -0.125 0.049 +16 3 3 57450.00 I -0.024054 0.000025 0.357866 0.000019 I-0.0236772 0.0000060 1.7337 0.0044 I -0.016 0.119 -0.093 0.049 +16 3 4 57451.00 I -0.023735 0.000027 0.360180 0.000019 I-0.0254270 0.0000057 1.7591 0.0052 I 0.008 0.119 -0.056 0.049 +16 3 5 57452.00 I -0.023740 0.000026 0.362598 0.000018 I-0.0272149 0.0000084 1.8430 0.0041 I 0.011 0.119 -0.053 0.091 +16 3 6 57453.00 I -0.023745 0.000027 0.365029 0.000018 I-0.0291495 0.0000060 2.0365 0.0052 I 0.010 0.119 -0.081 0.052 +16 3 7 57454.00 I -0.024021 0.000024 0.367538 0.000018 I-0.0312844 0.0000060 2.2194 0.0045 I 0.021 0.119 -0.095 0.052 +16 3 8 57455.00 I -0.024457 0.000021 0.370024 0.000012 I-0.0335554 0.0000066 2.3035 0.0044 I 0.036 0.119 -0.085 0.026 +16 3 9 57456.00 I -0.024829 0.000022 0.372483 0.000011 I-0.0359012 0.0000065 2.4168 0.0045 I 0.037 0.119 -0.072 0.026 +16 310 57457.00 I -0.025066 0.000020 0.374873 0.000011 I-0.0383738 0.0000062 2.4843 0.0044 I 0.025 0.119 -0.063 0.026 +16 311 57458.00 I -0.025097 0.000017 0.377004 0.000011 I-0.0408388 0.0000060 2.4588 0.0080 I 0.011 0.119 -0.053 0.026 +16 312 57459.00 I -0.025181 0.000017 0.378932 0.000012 I-0.0432700 0.0000148 2.3690 0.0040 I 0.008 0.119 -0.040 0.150 +16 313 57460.00 I -0.025188 0.000024 0.380780 0.000016 I-0.0455624 0.0000054 2.2359 0.0079 I 0.013 0.119 -0.031 0.150 +16 314 57461.00 I -0.024758 0.000026 0.382629 0.000016 I-0.0477465 0.0000056 2.1168 0.0038 I 0.011 0.119 -0.012 0.150 +16 315 57462.00 I -0.023945 0.000025 0.384773 0.000017 I-0.0497744 0.0000053 1.9330 0.0037 I -0.002 0.119 0.022 0.150 +16 316 57463.00 I -0.022702 0.000026 0.387109 0.000023 I-0.0516492 0.0000048 1.8593 0.0035 I -0.012 0.042 0.057 0.020 +16 317 57464.00 I -0.020782 0.000026 0.389469 0.000023 I-0.0535037 0.0000047 1.8176 0.0034 I -0.009 0.042 0.067 0.020 +16 318 57465.00 I -0.018391 0.000025 0.391971 0.000022 I-0.0552954 0.0000047 1.8089 0.0041 I 0.001 0.042 0.060 0.020 +16 319 57466.00 I -0.016187 0.000017 0.394472 0.000020 I-0.0571458 0.0000067 1.8730 0.0036 I 0.003 0.047 0.046 0.029 +16 320 57467.00 I -0.014601 0.000025 0.396962 0.000029 I-0.0590734 0.0000055 2.0332 0.0044 I -0.007 0.052 0.033 0.049 +16 321 57468.00 I -0.013767 0.000026 0.399494 0.000030 I-0.0611669 0.0000057 2.0575 0.0043 I -0.028 0.052 0.021 0.056 +16 322 57469.00 I -0.013562 0.000024 0.402015 0.000024 I-0.0631898 0.0000065 2.0861 0.0043 I -0.054 0.059 0.010 0.076 +16 323 57470.00 I -0.013645 0.000024 0.404333 0.000024 I-0.0653380 0.0000064 2.1115 0.0046 I -0.074 0.059 0.009 0.076 +16 324 57471.00 I -0.013616 0.000024 0.406392 0.000024 I-0.0673565 0.0000066 1.9679 0.0046 I -0.088 0.059 0.018 0.076 +16 325 57472.00 I -0.013255 0.000024 0.408403 0.000024 I-0.0693406 0.0000065 2.0248 0.0063 I -0.109 0.059 0.024 0.076 +16 326 57473.00 I -0.012617 0.000013 0.410472 0.000013 I-0.0713968 0.0000108 2.0538 0.0066 I -0.134 0.119 0.017 0.225 +16 327 57474.00 I -0.011855 0.000011 0.412577 0.000013 I-0.0734180 0.0000116 1.9922 0.0069 I -0.144 0.119 -0.003 0.150 +16 328 57475.00 I -0.011092 0.000023 0.414506 0.000021 I-0.0753619 0.0000085 1.8727 0.0072 I -0.132 0.058 -0.029 0.069 +16 329 57476.00 I -0.010256 0.000023 0.416297 0.000022 I-0.0771761 0.0000085 1.7914 0.0055 I -0.106 0.058 -0.054 0.069 +16 330 57477.00 I -0.009394 0.000023 0.418007 0.000023 I-0.0789625 0.0000070 1.7637 0.0055 I -0.070 0.047 -0.058 0.063 +16 331 57478.00 I -0.008769 0.000022 0.419685 0.000025 I-0.0807036 0.0000069 1.7363 0.0049 I -0.034 0.047 -0.029 0.063 +16 4 1 57479.00 I -0.008414 0.000022 0.421202 0.000027 I-0.0824533 0.0000068 1.7634 0.0049 I -0.021 0.047 0.000 0.063 +16 4 2 57480.00 I -0.008088 0.000022 0.422663 0.000027 I-0.0842517 0.0000069 1.8544 0.0042 I -0.036 0.047 -0.021 0.063 +16 4 3 57481.00 I -0.007515 0.000013 0.424137 0.000024 I-0.0861632 0.0000050 1.9487 0.0047 I -0.052 0.119 -0.082 0.024 +16 4 4 57482.00 I -0.006419 0.000021 0.425858 0.000023 I-0.0881549 0.0000064 2.0606 0.0045 I -0.040 0.119 -0.113 0.024 +16 4 5 57483.00 I -0.004709 0.000022 0.427854 0.000022 I-0.0902894 0.0000074 2.1876 0.0049 I -0.016 0.119 -0.084 0.014 +16 4 6 57484.00 I -0.002606 0.000022 0.429902 0.000021 I-0.0925256 0.0000073 2.3011 0.0052 I -0.002 0.119 -0.031 0.014 +16 4 7 57485.00 I -0.000789 0.000022 0.432202 0.000018 I-0.0948791 0.0000073 2.3772 0.0052 I 0.000 0.119 0.010 0.014 +16 4 8 57486.00 I 0.000664 0.000022 0.434713 0.000016 I-0.0972531 0.0000073 2.3725 0.0122 I -0.002 0.119 0.040 0.014 +16 4 9 57487.00 I 0.002059 0.000023 0.437042 0.000013 I-0.0995861 0.0000232 2.2539 0.0054 I -0.006 0.119 0.063 0.150 +16 410 57488.00 I 0.003347 0.000021 0.439038 0.000016 I-0.1017251 0.0000079 2.0278 0.0120 I -0.008 0.119 0.072 0.061 +16 411 57489.00 I 0.004628 0.000019 0.440926 0.000015 I-0.1036627 0.0000060 1.8647 0.0049 I -0.005 0.119 0.078 0.061 +16 412 57490.00 I 0.006143 0.000019 0.442665 0.000016 I-0.1054713 0.0000059 1.7520 0.0042 I 0.004 0.119 0.096 0.061 +16 413 57491.00 I 0.007737 0.000020 0.444189 0.000016 I-0.1071693 0.0000060 1.6470 0.0044 I 0.019 0.119 0.114 0.061 +16 414 57492.00 I 0.009116 0.000020 0.445678 0.000016 I-0.1087724 0.0000066 1.5633 0.0046 I 0.046 0.119 0.118 0.061 +16 415 57493.00 I 0.010471 0.000019 0.447462 0.000017 I-0.1103218 0.0000069 1.5574 0.0087 I 0.076 0.119 0.108 0.061 +16 416 57494.00 I 0.011967 0.000020 0.449627 0.000018 I-0.1119079 0.0000162 1.6135 0.0060 I 0.095 0.119 0.095 0.150 +16 417 57495.00 I 0.013661 0.000025 0.451890 0.000023 I-0.1135287 0.0000097 1.6091 0.0095 I 0.099 0.119 0.086 0.013 +16 418 57496.00 I 0.015451 0.000028 0.454016 0.000028 I-0.1151529 0.0000098 1.6851 0.0068 I 0.090 0.119 0.090 0.013 +16 419 57497.00 I 0.017078 0.000032 0.455707 0.000031 I-0.1169025 0.0000096 1.7719 0.0070 I 0.070 0.119 0.113 0.013 +16 420 57498.00 I 0.018626 0.000041 0.456788 0.000041 I-0.1186505 0.0000101 1.7201 0.0062 P 0.011 0.239 0.092 0.600 +16 421 57499.00 I 0.020312 0.000044 0.457663 0.000043 I-0.1203383 0.0000079 1.6551 0.0058 P 0.002 0.239 0.097 0.600 +16 422 57500.00 I 0.022149 0.000091 0.458767 0.000093 I-0.1219712 0.0000056 1.6216 0.0057 P -0.002 0.239 0.074 0.600 +16 423 57501.00 I 0.024059 0.000091 0.460210 0.000092 I-0.1235866 0.0000083 1.6033 0.0048 P -0.009 0.239 0.041 0.600 +16 424 57502.00 I 0.025767 0.000091 0.461829 0.000092 I-0.1251404 0.0000079 1.4728 0.0092 P -0.007 0.239 0.018 0.600 +16 425 57503.00 I 0.027084 0.000091 0.463531 0.000093 I-0.1265278 0.0000165 1.3290 0.0090 P 0.013 0.239 0.007 0.600 +16 426 57504.00 I 0.028086 0.000091 0.465338 0.000093 I-0.1278231 0.0000161 1.2593 0.0118 P 0.040 0.239 0.003 0.600 +16 427 57505.00 I 0.028928 0.000091 0.467015 0.000092 I-0.1290372 0.0000169 1.1618 0.0123 P 0.065 0.239 0.008 0.600 +16 428 57506.00 I 0.029730 0.000092 0.468564 0.000091 I-0.1303716 0.0000185 P 0.083 0.239 0.035 0.600 +16 429 57507.00 P 0.030798 0.000622 0.469783 0.000448 P-0.1317429 0.0001080 P 0.085 0.239 0.066 0.600 +16 430 57508.00 P 0.031994 0.000923 0.470993 0.000737 P-0.1331878 0.0002041 P 0.065 0.239 0.057 0.600 +16 5 1 57509.00 P 0.033490 0.001163 0.472083 0.000987 P-0.1347405 0.0003028 P 0.040 0.239 0.007 0.600 +16 5 2 57510.00 P 0.035183 0.001370 0.473116 0.001215 P-0.1364495 0.0004021 P 0.034 0.239 -0.028 0.600 +16 5 3 57511.00 P 0.036987 0.001556 0.474102 0.001426 P-0.1383398 0.0005017 P 0.043 0.239 -0.006 0.600 +16 5 4 57512.00 P 0.038845 0.001727 0.475046 0.001626 P-0.1403703 0.0006014 P 0.046 0.239 0.044 0.600 +16 5 5 57513.00 P 0.040719 0.001885 0.475994 0.001817 P-0.1424457 0.0007012 P 0.037 0.239 0.082 0.600 +16 5 6 57514.00 P 0.042581 0.002035 0.476950 0.002001 P-0.1444593 0.0007500 P 0.028 0.239 0.101 0.600 +16 5 7 57515.00 P 0.044425 0.002176 0.477871 0.002178 P-0.1463307 0.0005500 P 0.014 0.239 0.107 0.600 +16 5 8 57516.00 P 0.046286 0.002310 0.478748 0.002349 P-0.1480080 0.0007548 P -0.006 0.239 0.096 0.600 +16 5 9 57517.00 P 0.048207 0.002439 0.479583 0.002516 P-0.1494885 0.0009344 P -0.019 0.239 0.073 0.600 +16 510 57518.00 P 0.050193 0.002564 0.480379 0.002679 P-0.1508450 0.0010994 P -0.012 0.239 0.056 0.600 +16 511 57519.00 P 0.052250 0.002683 0.481153 0.002838 P-0.1521313 0.0012543 P 0.016 0.239 0.051 0.600 +16 512 57520.00 P 0.054368 0.002799 0.481924 0.002993 P-0.1533788 0.0014016 P 0.052 0.239 0.053 0.600 +16 513 57521.00 P 0.056522 0.002911 0.482690 0.003146 P-0.1546475 0.0015429 P 0.077 0.239 0.062 0.600 +16 514 57522.00 P 0.058696 0.003020 0.483452 0.003295 P-0.1559391 0.0016792 P 0.082 0.239 0.068 0.600 +16 515 57523.00 P 0.060870 0.003127 0.484205 0.003442 P-0.1572557 0.0018113 P 0.077 0.239 0.061 0.600 +16 516 57524.00 P 0.063031 0.003230 0.484931 0.003587 P-0.1585736 0.0019399 P 0.067 0.239 0.062 0.600 +16 517 57525.00 P 0.065179 0.003331 0.485615 0.003729 P-0.1598755 0.0020652 P 0.046 0.239 0.087 0.600 +16 518 57526.00 P 0.067327 0.003430 0.486255 0.003870 P-0.1611463 0.0021877 P 0.017 0.239 0.124 0.600 +16 519 57527.00 P 0.069480 0.003527 0.486853 0.004008 P-0.1623693 0.0023077 P -0.001 0.239 0.132 0.600 +16 520 57528.00 P 0.071641 0.003621 0.487411 0.004145 P-0.1635235 0.0024255 P 0.001 0.239 0.103 0.600 +16 521 57529.00 P 0.073815 0.003714 0.487931 0.004279 P-0.1645892 0.0025411 P 0.009 0.239 0.066 0.600 +16 522 57530.00 P 0.076004 0.003806 0.488420 0.004413 P-0.1655561 0.0026548 P 0.018 0.239 0.050 0.600 +16 523 57531.00 P 0.078203 0.003895 0.488880 0.004544 P-0.1664451 0.0027668 P 0.037 0.239 0.053 0.600 +16 524 57532.00 P 0.080408 0.003983 0.489309 0.004674 P-0.1672908 0.0028772 P 0.059 0.239 0.062 0.600 +16 525 57533.00 P 0.082617 0.004070 0.489704 0.004803 P-0.1681336 0.0029860 P 0.067 0.239 0.075 0.600 +16 526 57534.00 P 0.084827 0.004155 0.490064 0.004930 P-0.1690194 0.0030934 P 0.059 0.239 0.093 0.600 +16 527 57535.00 P 0.087040 0.004239 0.490383 0.005057 P-0.1699970 0.0031995 P 0.047 0.239 0.110 0.600 +16 528 57536.00 P 0.089257 0.004322 0.490663 0.005182 P-0.1710955 0.0033043 P 0.037 0.239 0.111 0.600 +16 529 57537.00 P 0.091478 0.004403 0.490904 0.005305 P-0.1723451 0.0034080 P 0.027 0.239 0.088 0.600 +16 530 57538.00 P 0.093706 0.004484 0.491106 0.005428 P-0.1737501 0.0035105 P 0.023 0.239 0.065 0.600 +16 531 57539.00 P 0.095941 0.004563 0.491273 0.005550 P-0.1752782 0.0036120 P 0.025 0.239 0.063 0.600 +16 6 1 57540.00 P 0.098182 0.004641 0.491405 0.005670 P-0.1768742 0.0037124 P 0.023 0.239 0.083 0.600 +16 6 2 57541.00 P 0.100428 0.004719 0.491506 0.005790 P-0.1784618 0.0038119 P 0.012 0.239 0.109 0.600 +16 6 3 57542.00 P 0.102675 0.004795 0.491574 0.005908 P-0.1799593 0.0039105 P 0.000 0.239 0.131 0.600 +16 6 4 57543.00 P 0.104924 0.004871 0.491610 0.006026 P-0.1813071 0.0040082 P -0.005 0.239 0.137 0.600 +16 6 5 57544.00 P 0.107172 0.004945 0.491612 0.006143 P-0.1824904 0.0041051 P -0.005 0.239 0.124 0.600 +16 6 6 57545.00 P 0.109420 0.005019 0.491579 0.006259 P-0.1835411 0.0042012 P -0.003 0.239 0.101 0.600 +16 6 7 57546.00 P 0.111667 0.005092 0.491512 0.006374 P-0.1845091 0.0042965 P 0.007 0.239 0.085 0.600 +16 6 8 57547.00 P 0.113913 0.005164 0.491411 0.006488 P-0.1854466 0.0043911 P 0.030 0.239 0.084 0.600 +16 6 9 57548.00 P 0.116157 0.005235 0.491274 0.006602 P-0.1863873 0.0044849 P 0.055 0.239 0.098 0.600 +16 610 57549.00 P 0.118399 0.005306 0.491104 0.006715 P-0.1873523 0.0045781 P 0.065 0.239 0.127 0.600 +16 611 57550.00 P 0.120639 0.005376 0.490901 0.006827 P-0.1883409 0.0046706 P 0.060 0.239 0.154 0.600 +16 612 57551.00 P 0.122875 0.005445 0.490664 0.006938 P-0.1893388 0.0047625 P 0.052 0.239 0.159 0.600 +16 613 57552.00 P 0.125106 0.005514 0.490394 0.007049 P-0.1903249 0.0048537 P 0.046 0.239 0.145 0.600 +16 614 57553.00 P 0.127331 0.005582 0.490090 0.007159 P-0.1912717 0.0049444 P 0.032 0.239 0.141 0.600 +16 615 57554.00 P 0.129551 0.005650 0.489753 0.007268 P-0.1921561 0.0050344 P 0.005 0.239 0.154 0.600 +16 616 57555.00 P 0.131763 0.005716 0.489382 0.007377 P-0.1929601 0.0051240 P -0.012 0.239 0.159 0.600 +16 617 57556.00 P 0.133969 0.005782 0.488978 0.007485 P-0.1936729 0.0052129 P -0.009 0.239 0.143 0.600 +16 618 57557.00 P 0.136167 0.005848 0.488539 0.007592 P-0.1942946 0.0053014 P 0.004 0.239 0.122 0.600 +16 619 57558.00 P 0.138356 0.005913 0.488067 0.007699 P-0.1948369 0.0053893 P 0.015 0.239 0.115 0.600 +16 620 57559.00 P 0.140538 0.005978 0.487561 0.007806 P-0.1953178 0.0054768 P 0.029 0.239 0.117 0.600 +16 621 57560.00 P 0.142710 0.006042 0.487022 0.007911 P-0.1957658 0.0055637 P 0.048 0.239 0.121 0.600 +16 622 57561.00 P 0.144872 0.006105 0.486450 0.008017 P-0.1962213 0.0056502 P 0.056 0.239 0.132 0.600 +16 623 57562.00 P 0.147024 0.006168 0.485846 0.008121 P-0.1967329 0.0057362 P 0.040 0.239 0.147 0.600 +16 624 57563.00 P 0.149165 0.006231 0.485208 0.008226 P-0.1973441 0.0058218 P 0.019 0.239 0.155 0.600 +16 625 57564.00 P 0.151294 0.006293 0.484538 0.008329 P-0.1980871 0.0059070 P 0.009 0.239 0.148 0.600 +16 626 57565.00 P 0.153411 0.006355 0.483836 0.008432 P-0.1989695 0.0059917 P 0.009 0.239 0.141 0.600 +16 627 57566.00 P 0.155515 0.006416 0.483101 0.008535 P-0.1999726 0.0060760 P 0.010 0.239 0.141 0.600 +16 628 57567.00 P 0.157606 0.006477 0.482334 0.008637 P-0.2010510 0.0061599 P 0.007 0.239 0.138 0.600 +16 629 57568.00 P 0.159683 0.006537 0.481535 0.008739 P-0.2021438 0.0062434 P 0.002 0.239 0.130 0.600 +16 630 57569.00 P 0.161745 0.006597 0.480705 0.008840 P-0.2031853 0.0063266 P -0.007 0.239 0.133 0.600 +16 7 1 57570.00 P 0.163793 0.006656 0.479843 0.008941 P-0.2041214 0.0064093 P -0.016 0.239 0.151 0.600 +16 7 2 57571.00 P 0.165825 0.006715 0.478950 0.009041 P-0.2049288 0.0064917 P -0.018 0.239 0.166 0.600 +16 7 3 57572.00 P 0.167840 0.006774 0.478026 0.009141 P-0.2056188 0.0065737 P -0.007 0.239 0.157 0.600 +16 7 4 57573.00 P 0.169839 0.006832 0.477071 0.009241 P-0.2062331 0.0066554 P 0.011 0.239 0.140 0.600 +16 7 5 57574.00 P 0.171821 0.006890 0.476086 0.009340 P-0.2068275 0.0067368 P 0.024 0.239 0.138 0.600 +16 7 6 57575.00 P 0.173785 0.006948 0.475071 0.009439 P-0.2074512 0.0068177 P 0.031 0.239 0.152 0.600 +16 7 7 57576.00 P 0.175730 0.007005 0.474025 0.009537 P-0.2081302 0.0068984 P 0.033 0.239 0.174 0.600 +16 7 8 57577.00 P 0.177656 0.007062 0.472950 0.009635 P-0.2088663 0.0069788 P 0.031 0.239 0.202 0.600 +16 7 9 57578.00 P 0.179563 0.007118 0.471846 0.009732 P-0.2096451 0.0070588 P 0.027 0.239 0.235 0.600 +16 710 57579.00 P 0.181450 0.007175 0.470713 0.009829 P-0.2104419 0.0071385 P 0.025 0.239 0.250 0.600 +16 711 57580.00 P 0.183317 0.007230 0.469550 0.009926 P-0.2112284 0.0072179 P 0.024 0.239 0.237 0.600 +16 712 57581.00 P 0.185162 0.007286 0.468360 0.010023 P-0.2119781 0.0072970 P 0.013 0.239 0.216 0.600 +16 713 57582.00 P 0.186986 0.007341 0.467141 0.010119 P-0.2126684 0.0073758 P -0.003 0.239 0.205 0.600 +16 714 57583.00 P 0.188788 0.007396 0.465894 0.010214 P-0.2132848 0.0074544 P -0.011 0.239 0.203 0.600 +16 715 57584.00 P 0.190567 0.007451 0.464620 0.010310 P-0.2138206 0.0075326 P -0.007 0.239 0.193 0.600 +16 716 57585.00 P 0.192324 0.007505 0.463319 0.010405 P-0.2142803 0.0076106 P 0.000 0.239 0.179 0.600 +16 717 57586.00 P 0.194056 0.007559 0.461990 0.010499 P-0.2146833 0.0076883 P 0.004 0.239 0.170 0.600 +16 718 57587.00 P 0.195765 0.007613 0.460636 0.010594 P-0.2150602 0.0077657 P 0.013 0.239 0.165 0.600 +16 719 57588.00 P 0.197449 0.007666 0.459255 0.010688 P-0.2154534 0.0078428 +16 720 57589.00 P 0.199109 0.007719 0.457848 0.010781 P-0.2159127 0.0079197 +16 721 57590.00 P 0.200742 0.007772 0.456416 0.010875 P-0.2164842 0.0079964 +16 722 57591.00 P 0.202351 0.007825 0.454960 0.010968 P-0.2172005 0.0080728 +16 723 57592.00 P 0.203932 0.007877 0.453478 0.011060 P-0.2180702 0.0081489 +16 724 57593.00 P 0.205487 0.007929 0.451972 0.011153 P-0.2190692 0.0082248 +16 725 57594.00 P 0.207015 0.007981 0.450443 0.011245 P-0.2201448 0.0083005 +16 726 57595.00 P 0.208515 0.008033 0.448890 0.011337 P-0.2212286 0.0083759 +16 727 57596.00 P 0.209988 0.008084 0.447315 0.011428 P-0.2222556 0.0084511 +16 728 57597.00 P 0.211431 0.008135 0.445716 0.011520 P-0.2231750 0.0085261 +16 729 57598.00 P 0.212846 0.008186 0.444096 0.011611 P-0.2239618 0.0086008 +16 730 57599.00 P 0.214232 0.008236 0.442454 0.011701 P-0.3000000 0.0086754 diff --git a/astropy/utils/iers/tests/data/iers_a_excerpt b/astropy/utils/iers/tests/data/iers_a_excerpt new file mode 100644 index 000000000000..25ac33116ad1 --- /dev/null +++ b/astropy/utils/iers/tests/data/iers_a_excerpt @@ -0,0 +1,60 @@ +15 126 57048.00 I 0.002902 0.000024 0.302160 0.000040 I-0.4867876 0.0000073 1.2748 0.0071 I -0.155 0.119 -0.040 0.034 0.002890 0.302200 -0.4867861 -0.194 -0.055 +15 127 57049.00 I 0.002371 0.000039 0.303081 0.000035 I-0.4880090 0.0000121 1.1461 0.0051 I -0.148 0.049 -0.021 0.073 0.002389 0.303074 -0.4880195 -0.193 -0.060 +15 128 57050.00 I 0.002261 0.000048 0.304429 0.000038 I-0.4890652 0.0000071 0.9738 0.0068 I -0.144 0.119 0.011 0.055 0.002280 0.304472 -0.4890627 -0.166 -0.016 +15 129 57051.00 I 0.002244 0.000048 0.306179 0.000035 I-0.4899792 0.0000060 0.8694 0.0047 I -0.133 0.119 0.034 0.055 0.002250 0.306164 -0.4899632 -0.132 0.035 +15 130 57052.00 I 0.002854 0.000048 0.308403 0.000035 I-0.4908258 0.0000062 0.8305 0.0039 I -0.115 0.119 0.040 0.055 0.002907 0.308446 -0.4908208 -0.089 0.086 +15 131 57053.00 I 0.003759 0.000045 0.310804 0.000024 I-0.4916596 0.0000051 0.8499 0.0043 I -0.101 0.119 0.042 0.054 0.003734 0.310824 -0.4916557 -0.086 0.094 +15 2 1 57054.00 I 0.004544 0.000061 0.313148 0.000030 I-0.4925306 0.0000060 0.8845 0.0039 I -0.094 0.119 0.052 0.059 0.004581 0.313150 -0.4925323 -0.093 0.081 +15 2 2 57055.00 I 0.004623 0.000051 0.315517 0.000029 I-0.4934373 0.0000058 0.9452 0.0061 I -0.087 0.119 0.072 0.053 +15 2 3 57056.00 I 0.004190 0.000042 0.317761 0.000025 I-0.4944317 0.0000107 1.0378 0.0041 I -0.082 0.119 0.089 0.075 +15 2 4 57057.00 I 0.003858 0.000052 0.319727 0.000027 I-0.4955079 0.0000059 1.1152 0.0061 I -0.075 0.119 0.103 0.048 +15 2 5 57058.00 I 0.003203 0.000052 0.321256 0.000026 I-0.4966586 0.0000058 1.1820 0.0040 I -0.063 0.119 0.126 0.048 +15 2 6 57059.00 I 0.002604 0.000052 0.322746 0.000026 I-0.4978678 0.0000053 1.2367 0.0033 I -0.053 0.119 0.163 0.048 +15 2 7 57060.00 I 0.002144 0.000032 0.324351 0.000019 I-0.4991451 0.0000033 1.3309 0.0034 I -0.056 0.119 0.201 0.024 +15 2 8 57061.00 I 0.001898 0.000037 0.325746 0.000023 I-0.5005174 0.0000043 1.3884 0.0027 I -0.071 0.119 0.225 0.052 +15 2 9 57062.00 I 0.002025 0.000037 0.327045 0.000021 I-0.5018830 0.0000043 1.3290 0.0036 I -0.086 0.119 0.228 0.052 +15 210 57063.00 I 0.002138 0.000021 0.328333 0.000019 I-0.5031643 0.0000058 1.2363 0.0030 I -0.098 0.119 0.202 0.062 +15 211 57064.00 I 0.002223 0.000032 0.329713 0.000023 I-0.5043650 0.0000043 1.1720 0.0036 I -0.100 0.119 0.149 0.047 +15 212 57065.00 I 0.002256 0.000032 0.331138 0.000024 I-0.5055269 0.0000042 1.1636 0.0030 I -0.079 0.119 0.095 0.047 +15 213 57066.00 I 0.002424 0.000032 0.332371 0.000024 I-0.5067041 0.0000042 1.1909 0.0026 I -0.036 0.119 0.066 0.047 +15 214 57067.00 I 0.002704 0.000026 0.333462 0.000020 I-0.5078936 0.0000030 1.1728 0.0027 I 0.003 0.119 0.045 0.027 +15 215 57068.00 I 0.002772 0.000033 0.334534 0.000024 I-0.5090570 0.0000033 1.1764 0.0023 I 0.027 0.045 0.008 0.037 +15 216 57069.00 I 0.002854 0.000033 0.335722 0.000025 I-0.5102715 0.0000035 1.2552 0.0033 I 0.045 0.045 -0.015 0.037 +15 217 57070.00 I 0.002844 0.000023 0.337156 0.000022 I-0.5115860 0.0000058 1.3897 0.0024 I 0.055 0.066 0.003 0.050 +15 218 57071.00 I 0.002756 0.000033 0.338410 0.000025 I-0.5130736 0.0000033 1.5925 0.0034 I 0.043 0.040 0.046 0.036 +15 219 57072.00 I 0.002769 0.000032 0.339609 0.000025 I-0.5147512 0.0000034 1.7361 0.0025 I 0.012 0.040 0.080 0.036 +15 220 57073.00 I 0.002580 0.000032 0.341034 0.000026 I-0.5165088 0.0000037 1.7687 0.0027 I -0.024 0.040 0.101 0.036 +15 221 57074.00 I 0.002345 0.000025 0.342662 0.000021 I-0.5182664 0.0000042 1.7345 0.0024 I -0.052 0.119 0.114 0.023 +15 222 57075.00 I 0.002461 0.000030 0.344425 0.000024 I-0.5199568 0.0000031 1.6371 0.0026 I -0.059 0.119 0.108 0.036 +15 223 57076.00 I 0.003021 0.000030 0.346361 0.000024 I-0.5215198 0.0000029 1.4774 0.0026 I -0.043 0.119 0.092 0.036 +15 224 57077.00 I 0.003313 0.000020 0.348467 0.000020 I-0.5228871 0.0000042 1.2443 0.0026 I -0.020 0.119 0.100 0.044 +15 225 57078.00 I 0.003151 0.000020 0.350293 0.000019 I-0.5240195 0.0000044 1.0446 0.0031 I -0.004 0.119 0.151 0.045 +15 226 57079.00 I 0.003153 0.000020 0.351720 0.000019 I-0.5250116 0.0000045 0.9509 0.0033 I 0.012 0.119 0.220 0.045 +15 227 57080.00 I 0.003288 0.000021 0.353151 0.000021 I-0.5259330 0.0000050 0.8925 0.0063 I 0.041 0.119 0.284 0.045 +15 228 57081.00 I 0.003184 0.000011 0.354780 0.000015 I-0.5268057 0.0000118 0.8616 0.0064 P -0.006 0.239 0.108 0.600 +15 3 1 57082.00 I 0.003159 0.000012 0.356592 0.000017 I-0.5276758 0.0000118 0.8894 0.0080 P 0.007 0.239 0.093 0.600 +15 3 2 57083.00 I 0.003484 0.000091 0.358608 0.000092 I-0.5285954 0.0000109 0.9498 0.0107 P 0.017 0.239 0.086 0.600 +15 3 3 57084.00 I 0.003836 0.000092 0.360558 0.000092 I-0.5295768 0.0000179 1.0144 0.0094 P 0.025 0.239 0.094 0.600 +15 3 4 57085.00 I 0.003961 0.000092 0.362222 0.000093 I-0.5306268 0.0000154 1.0866 0.0108 P 0.027 0.239 0.117 0.600 +15 3 5 57086.00 I 0.004269 0.000093 0.363666 0.000093 I-0.5317409 0.0000122 1.1313 0.0089 P 0.027 0.239 0.139 0.600 +15 3 6 57087.00 I 0.004778 0.000092 0.364949 0.000092 I-0.5328903 0.0000088 1.1785 0.0079 P 0.027 0.239 0.149 0.600 +15 3 7 57088.00 I 0.004988 0.000092 0.366147 0.000092 I-0.5340985 0.0000100 1.2274 0.0066 P 0.015 0.239 0.146 0.600 +15 3 8 57089.00 I 0.004638 0.000091 0.367236 0.000093 I-0.5353452 0.0000099 1.2765 0.0070 P -0.009 0.239 0.142 0.600 +15 3 9 57090.00 I 0.004036 0.000092 0.368200 0.000093 I-0.5366619 0.0000099 1.3567 0.0070 P -0.033 0.239 0.135 0.600 +15 310 57091.00 I 0.003585 0.000091 0.369259 0.000092 I-0.5380421 0.0000098 1.3875 0.0070 P -0.045 0.239 0.117 0.600 +15 311 57092.00 I 0.003343 0.000092 0.370447 0.000093 I-0.5394314 0.0000100 1.4018 0.0068 P -0.040 0.239 0.091 0.600 +15 312 57093.00 I 0.003194 0.000092 0.371602 0.000093 I-0.5408469 0.0000094 1.4197 0.0068 P -0.015 0.239 0.085 0.600 +15 313 57094.00 I 0.002917 0.000091 0.372658 0.000092 I-0.5422589 0.0000091 1.4020 0.0065 P 0.019 0.239 0.117 0.600 +15 314 57095.00 I 0.002739 0.000092 0.373613 0.000092 I-0.5436785 0.0000090 1.4673 0.0056 P 0.029 0.239 0.148 0.600 +15 315 57096.00 I 0.002778 0.000091 0.374548 0.000091 I-0.5452040 0.0000066 1.5642 0.0053 P 0.008 0.239 0.132 0.600 +15 316 57097.00 I 0.002789 0.000091 0.375473 0.000093 I-0.5468035 0.0000056 1.6509 0.0042 P -0.012 0.239 0.085 0.600 +15 317 57098.00 I 0.002799 0.000091 0.376430 0.000094 I-0.5485481 0.0000051 1.8644 0.0326 P -0.009 0.239 0.068 0.600 +15 318 57099.00 I 0.002880 0.000091 0.377429 0.000094 I-0.5504555 0.0000650 1.8477 0.0277 P 0.007 0.239 0.099 0.600 +15 319 57100.00 I 0.003450 0.000091 0.378351 0.000091 I-0.5525515 0.0000552 P 0.014 0.239 0.137 0.600 +15 320 57101.00 P 0.004024 0.000697 0.379498 0.000414 P-0.5547450 0.0001080 P 0.011 0.239 0.163 0.600 +15 321 57102.00 P 0.004848 0.001034 0.380682 0.000681 P-0.5569577 0.0002041 P 0.005 0.239 0.177 0.600 +15 322 57103.00 P 0.005564 0.001303 0.381927 0.000912 P-0.5590933 0.0003028 P -0.001 0.239 0.180 0.600 +15 323 57104.00 P 0.006227 0.001536 0.383232 0.001122 P-0.5610707 0.0004021 P -0.006 0.239 0.167 0.600 +15 324 57105.00 P 0.006860 0.001744 0.384571 0.001318 P-0.5628741 0.0005017 P -0.012 0.239 0.152 0.600 +15 325 57106.00 P 0.007427 0.001935 0.385947 0.001503 P-0.5645205 0.0006014 P -0.015 0.239 0.145 0.600 +15 326 57107.00 P 0.007984 0.002113 0.387309 0.001679 P-0.5660324 0.0007012 P -0.010 0.239 0.143 0.600 diff --git a/astropy/utils/iers/tests/data/iers_b_old_style_excerpt b/astropy/utils/iers/tests/data/iers_b_old_style_excerpt new file mode 100644 index 000000000000..0fd71e0ab41f --- /dev/null +++ b/astropy/utils/iers/tests/data/iers_b_old_style_excerpt @@ -0,0 +1,36 @@ + EARTH ORIENTATION PARAMETER (EOP) PRODUCT CENTER CENTER (PARIS OBSERVATORY) + INTERNATIONAL EARTH ROTATION AND REFERENCE SYSTEMS SERVICE + EOP (IERS) 14 C04 TIME SERIES + Description: https://hpiers.obspm.fr/eoppc/eop/eopc04/C04.guide.pdf + contact: christian.bizouard@obspm.fr + + + FORMAT(3(I4),I7,2(F11.6),2(F12.7),2(F11.6),2(F11.6),2(F11.7),2(F12.6)) +################################################################################## + + Date MJD x y UT1-UTC LOD dX dY x Err y Err UT1-UTC Err LOD Err dX Err dY Err + " " s s " " " " s s " " + (0h UTC) + +1962 1 1 37665 -0.012700 0.213000 0.0326338 0.0017230 0.000000 0.000000 0.030000 0.030000 0.0020000 0.0014000 0.004774 0.002000 +1962 1 2 37666 -0.015900 0.214100 0.0320547 0.0016690 0.000000 0.000000 0.030000 0.030000 0.0020000 0.0014000 0.004774 0.002000 +1962 1 3 37667 -0.019000 0.215200 0.0315526 0.0015820 0.000000 0.000000 0.030000 0.030000 0.0020000 0.0014000 0.004774 0.002000 +1962 1 4 37668 -0.021999 0.216301 0.0311435 0.0014960 0.000000 0.000000 0.030000 0.030000 0.0020000 0.0014000 0.004774 0.002000 +1962 1 5 37669 -0.024799 0.217301 0.0308154 0.0014160 0.000000 0.000000 0.030000 0.030000 0.0020000 0.0014000 0.004774 0.002000 +1962 1 6 37670 -0.027599 0.218301 0.0305353 0.0013820 0.000000 0.000000 0.030000 0.030000 0.0020000 0.0014000 0.004774 0.002000 +1962 1 7 37671 -0.030199 0.219301 0.0302682 0.0014130 0.000000 0.000000 0.030000 0.030000 0.0020000 0.0014000 0.004774 0.002000 +1962 1 8 37672 -0.032798 0.220202 0.0299280 0.0015050 0.000000 0.000000 0.030000 0.030000 0.0020000 0.0014000 0.004774 0.002000 +1962 1 9 37673 -0.035198 0.221102 0.0294869 0.0016280 0.000000 0.000000 0.030000 0.030000 0.0020000 0.0014000 0.004774 0.002000 +1962 1 10 37674 -0.037498 0.222002 0.0289268 0.0017380 0.000000 0.000000 0.030000 0.030000 0.0020000 0.0014000 0.004774 0.002000 +1962 1 11 37675 -0.039697 0.222803 0.0282797 0.0017940 0.000000 0.000000 0.030000 0.030000 0.0020000 0.0014000 0.004774 0.002000 +1962 1 12 37676 -0.041797 0.223703 0.0276136 0.0017740 0.000000 0.000000 0.030000 0.030000 0.0020000 0.0014000 0.004774 0.002000 +1962 1 13 37677 -0.043797 0.224503 0.0270075 0.0016670 0.000000 0.000000 0.030000 0.030000 0.0020000 0.0014000 0.004774 0.002000 +1962 1 14 37678 -0.045697 0.225203 0.0265403 0.0015100 0.000000 0.000000 0.030000 0.030000 0.0020000 0.0014000 0.004774 0.002000 +1962 1 15 37679 -0.047496 0.226004 0.0262572 0.0013120 0.000000 0.000000 0.030000 0.030000 0.0020000 0.0014000 0.004774 0.002000 +1962 1 16 37680 -0.049196 0.226704 0.0261751 0.0011120 0.000000 0.000000 0.030000 0.030000 0.0020000 0.0014000 0.004774 0.002000 +1962 1 17 37681 -0.050796 0.227404 0.0262740 0.0009360 0.000000 0.000000 0.030000 0.030000 0.0020000 0.0014000 0.004774 0.002000 +1962 1 18 37682 -0.052295 0.228005 0.0265299 0.0008110 0.000000 0.000000 0.030000 0.030000 0.0020000 0.0014000 0.004774 0.002000 +1962 1 19 37683 -0.053595 0.228705 0.0268868 0.0007330 0.000000 0.000000 0.030000 0.030000 0.0020000 0.0014000 0.004774 0.002000 +1962 1 20 37684 -0.054895 0.229305 0.0273077 0.0006810 0.000000 0.000000 0.030000 0.030000 0.0020000 0.0014000 0.004774 0.002000 +1962 1 21 37685 -0.055995 0.229905 0.0277506 0.0006780 0.000000 0.000000 0.030000 0.030000 0.0020000 0.0014000 0.004774 0.002000 +1962 1 22 37686 -0.057094 0.230506 0.0281834 0.0007210 0.000000 0.000000 0.030000 0.030000 0.0020000 0.0014000 0.004774 0.002000 diff --git a/astropy/utils/iers/tests/data/leap-seconds.list b/astropy/utils/iers/tests/data/leap-seconds.list new file mode 100644 index 000000000000..b36845610e94 --- /dev/null +++ b/astropy/utils/iers/tests/data/leap-seconds.list @@ -0,0 +1,255 @@ +# +# In the following text, the symbol '#' introduces +# a comment, which continues from that symbol until +# the end of the line. A plain comment line has a +# whitespace character following the comment indicator. +# There are also special comment lines defined below. +# A special comment will always have a non-whitespace +# character in column 2. +# +# A blank line should be ignored. +# +# The following table shows the corrections that must +# be applied to compute International Atomic Time (TAI) +# from the Coordinated Universal Time (UTC) values that +# are transmitted by almost all time services. +# +# The first column shows an epoch as a number of seconds +# since 1 January 1900, 00:00:00 (1900.0 is also used to +# indicate the same epoch.) Both of these time stamp formats +# ignore the complexities of the time scales that were +# used before the current definition of UTC at the start +# of 1972. (See note 3 below.) +# The second column shows the number of seconds that +# must be added to UTC to compute TAI for any timestamp +# at or after that epoch. The value on each line is +# valid from the indicated initial instant until the +# epoch given on the next one or indefinitely into the +# future if there is no next line. +# (The comment on each line shows the representation of +# the corresponding initial epoch in the usual +# day-month-year format. The epoch always begins at +# 00:00:00 UTC on the indicated day. See Note 5 below.) +# +# Important notes: +# +# 1. Coordinated Universal Time (UTC) is often referred to +# as Greenwich Mean Time (GMT). The GMT time scale is no +# longer used, and the use of GMT to designate UTC is +# discouraged. +# +# 2. The UTC time scale is realized by many national +# laboratories and timing centers. Each laboratory +# identifies its realization with its name: Thus +# UTC(NIST), UTC(USNO), etc. The differences among +# these different realizations are typically on the +# order of a few nanoseconds (i.e., 0.000 000 00x s) +# and can be ignored for many purposes. These differences +# are tabulated in Circular T, which is published monthly +# by the International Bureau of Weights and Measures +# (BIPM). See www.bipm.org for more information. +# +# 3. The current definition of the relationship between UTC +# and TAI dates from 1 January 1972. A number of different +# time scales were in use before that epoch, and it can be +# quite difficult to compute precise timestamps and time +# intervals in those "prehistoric" days. For more information, +# consult: +# +# The Explanatory Supplement to the Astronomical +# Ephemeris. +# or +# Terry Quinn, "The BIPM and the Accurate Measurement +# of Time," Proc. of the IEEE, Vol. 79, pp. 894-905, +# July, 1991. +# reprinted in: +# Christine Hackman and Donald B Sullivan (eds.) +# Time and Frequency Measurement +# American Association of Physics Teachers (1996) +# , pp. 75-86 +# +# 4. The decision to insert a leap second into UTC is currently +# the responsibility of the International Earth Rotation and +# Reference Systems Service. (The name was changed from the +# International Earth Rotation Service, but the acronym IERS +# is still used.) +# +# Leap seconds are announced by the IERS in its Bulletin C. +# +# See www.iers.org for more details. +# +# Every national laboratory and timing center uses the +# data from the BIPM and the IERS to construct UTC(lab), +# their local realization of UTC. +# +# Although the definition also includes the possibility +# of dropping seconds ("negative" leap seconds), this has +# never been done and is unlikely to be necessary in the +# foreseeable future. +# +# 5. If your system keeps time as the number of seconds since +# some epoch (e.g., NTP timestamps), then the algorithm for +# assigning a UTC time stamp to an event that happens during a positive +# leap second is not well defined. The official name of that leap +# second is 23:59:60, but there is no way of representing that time +# in these systems. +# Many systems of this type effectively stop the system clock for +# one second during the leap second and use a time that is equivalent +# to 23:59:59 UTC twice. For these systems, the corresponding TAI +# timestamp would be obtained by advancing to the next entry in the +# following table when the time equivalent to 23:59:59 UTC +# is used for the second time. Thus the leap second which +# occurred on 30 June 1972 at 23:59:59 UTC would have TAI +# timestamps computed as follows: +# +# ... +# 30 June 1972 23:59:59 (2287785599, first time): TAI= UTC + 10 seconds +# 30 June 1972 23:59:60 (2287785599,second time): TAI= UTC + 11 seconds +# 1 July 1972 00:00:00 (2287785600) TAI= UTC + 11 seconds +# ... +# +# If your system realizes the leap second by repeating 00:00:00 UTC twice +# (this is possible but not usual), then the advance to the next entry +# in the table must occur the second time that a time equivalent to +# 00:00:00 UTC is used. Thus, using the same example as above: +# +# ... +# 30 June 1972 23:59:59 (2287785599): TAI= UTC + 10 seconds +# 30 June 1972 23:59:60 (2287785600, first time): TAI= UTC + 10 seconds +# 1 July 1972 00:00:00 (2287785600,second time): TAI= UTC + 11 seconds +# ... +# +# in both cases the use of timestamps based on TAI produces a smooth +# time scale with no discontinuity in the time interval. However, +# although the long-term behavior of the time scale is correct in both +# methods, the second method is technically not correct because it adds +# the extra second to the wrong day. +# +# This complexity would not be needed for negative leap seconds (if they +# are ever used). The UTC time would skip 23:59:59 and advance from +# 23:59:58 to 00:00:00 in that case. The TAI offset would decrease by +# 1 second at the same instant. This is a much easier situation to deal +# with, since the difficulty of unambiguously representing the epoch +# during the leap second does not arise. +# +# Some systems implement leap seconds by amortizing the leap second +# over the last few minutes of the day. The frequency of the local +# clock is decreased (or increased) to realize the positive (or +# negative) leap second. This method removes the time step described +# above. Although the long-term behavior of the time scale is correct +# in this case, this method introduces an error during the adjustment +# period both in time and in frequency with respect to the official +# definition of UTC. +# +# Questions or comments to: +# Judah Levine +# Time and Frequency Division +# NIST +# Boulder, Colorado +# Judah.Levine@nist.gov +# +# Last Update of leap second values: 8 July 2016 +# +# The following line shows this last update date in NTP timestamp +# format. This is the date on which the most recent change to +# the leap second data was added to the file. This line can +# be identified by the unique pair of characters in the first two +# columns as shown below. +# +#$ 3676924800 +# +# The NTP timestamps are in units of seconds since the NTP epoch, +# which is 1 January 1900, 00:00:00. The Modified Julian Day number +# corresponding to the NTP time stamp, X, can be computed as +# +# X/86400 + 15020 +# +# where the first term converts seconds to days and the second +# term adds the MJD corresponding to the time origin defined above. +# The integer portion of the result is the integer MJD for that +# day, and any remainder is the time of day, expressed as the +# fraction of the day since 0 hours UTC. The conversion from day +# fraction to seconds or to hours, minutes, and seconds may involve +# rounding or truncation, depending on the method used in the +# computation. +# +# The data in this file will be updated periodically as new leap +# seconds are announced. In addition to being entered on the line +# above, the update time (in NTP format) will be added to the basic +# file name leap-seconds to form the name leap-seconds.. +# In addition, the generic name leap-seconds.list will always point to +# the most recent version of the file. +# +# This update procedure will be performed only when a new leap second +# is announced. +# +# The following entry specifies the expiration date of the data +# in this file in units of seconds since the origin at the instant +# 1 January 1900, 00:00:00. This expiration date will be changed +# at least twice per year whether or not a new leap second is +# announced. These semi-annual changes will be made no later +# than 1 June and 1 December of each year to indicate what +# action (if any) is to be taken on 30 June and 31 December, +# respectively. (These are the customary effective dates for new +# leap seconds.) This expiration date will be identified by a +# unique pair of characters in columns 1 and 2 as shown below. +# In the unlikely event that a leap second is announced with an +# effective date other than 30 June or 31 December, then this +# file will be edited to include that leap second as soon as it is +# announced or at least one month before the effective date +# (whichever is later). +# If an announcement by the IERS specifies that no leap second is +# scheduled, then only the expiration date of the file will +# be advanced to show that the information in the file is still +# current -- the update time stamp, the data and the name of the file +# will not change. +# +# Updated through IERS Bulletin C58 +# File expires on: 28 June 2020 +# +#@ 3802291200 +# +2272060800 10 # 1 Jan 1972 +2287785600 11 # 1 Jul 1972 +2303683200 12 # 1 Jan 1973 +2335219200 13 # 1 Jan 1974 +2366755200 14 # 1 Jan 1975 +2398291200 15 # 1 Jan 1976 +2429913600 16 # 1 Jan 1977 +2461449600 17 # 1 Jan 1978 +2492985600 18 # 1 Jan 1979 +2524521600 19 # 1 Jan 1980 +2571782400 20 # 1 Jul 1981 +2603318400 21 # 1 Jul 1982 +2634854400 22 # 1 Jul 1983 +2698012800 23 # 1 Jul 1985 +2776982400 24 # 1 Jan 1988 +2840140800 25 # 1 Jan 1990 +2871676800 26 # 1 Jan 1991 +2918937600 27 # 1 Jul 1992 +2950473600 28 # 1 Jul 1993 +2982009600 29 # 1 Jul 1994 +3029443200 30 # 1 Jan 1996 +3076704000 31 # 1 Jul 1997 +3124137600 32 # 1 Jan 1999 +3345062400 33 # 1 Jan 2006 +3439756800 34 # 1 Jan 2009 +3550089600 35 # 1 Jul 2012 +3644697600 36 # 1 Jul 2015 +3692217600 37 # 1 Jan 2017 +# +# the following special comment contains the +# hash value of the data in this file computed +# use the secure hash algorithm as specified +# by FIPS 180-1. See the files in ~/pub/sha for +# the details of how this hash value is +# computed. Note that the hash computation +# ignores comments and whitespace characters +# in data lines. It includes the NTP values +# of both the last modification time and the +# expiration time of the file, but not the +# white space on those lines. +# the hash line is also ignored in the +# computation. +# +#h f28827d2 f263b6c3 ec0f19eb a3e0dbf0 97f3fa30 diff --git a/astropy/utils/iers/tests/test_iers.py b/astropy/utils/iers/tests/test_iers.py new file mode 100644 index 000000000000..29f0e802e401 --- /dev/null +++ b/astropy/utils/iers/tests/test_iers.py @@ -0,0 +1,536 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +import os +import re +import warnings +from pathlib import Path + +import numpy as np +import pytest +from astropy_iers_data import ( + IERS_A_README, + IERS_B_FILE, + IERS_B_README, + IERS_LEAP_SECOND_FILE, +) + +from astropy import units as u +from astropy.config import set_temp_cache +from astropy.table import QTable +from astropy.tests.helper import CI, assert_quantity_allclose +from astropy.time import Time, TimeDelta +from astropy.utils.data import get_pkg_data_filename +from astropy.utils.exceptions import AstropyDeprecationWarning +from astropy.utils.iers import iers + +FILE_NOT_FOUND_ERROR = getattr(__builtins__, "FileNotFoundError", OSError) + +IERS_A_EXCERPT = get_pkg_data_filename(os.path.join("data", "iers_a_excerpt")) + + +class TestBasic: + """Basic tests that IERS_B returns correct values""" + + @pytest.mark.parametrize("iers_cls", (iers.IERS_B, iers.IERS)) + def test_simple(self, iers_cls): + """Test the default behaviour for IERS_B and IERS.""" + # Arguably, IERS itself should not be used at all, but it used to + # provide IERS_B by default so we check that it continues to do so. + # Eventually, IERS should probably be deprecated. + iers_cls.close() + assert iers_cls.iers_table is None + iers_tab = iers_cls.open() + assert iers_cls.iers_table is not None + assert iers_cls.iers_table is iers_tab + assert isinstance(iers_tab, QTable) + assert isinstance(iers_tab, iers.IERS_B) + assert (iers_tab["UT1_UTC"].unit / u.second).is_unity() + assert (iers_tab["PM_x"].unit / u.arcsecond).is_unity() + assert (iers_tab["PM_y"].unit / u.arcsecond).is_unity() + jd1 = np.array([2456108.5, 2456108.5, 2456108.5, 2456109.5, 2456109.5]) + jd2 = np.array([0.49999421, 0.99997685, 0.99998843, 0.0, 0.5]) + ut1_utc = iers_tab.ut1_utc(jd1, jd2) + assert isinstance(ut1_utc, u.Quantity) + assert (ut1_utc.unit / u.second).is_unity() + # IERS files change at the 0.1 ms level; see gh-6981 + assert_quantity_allclose( + ut1_utc, + [-0.5868211, -0.5868184, -0.5868184, 0.4131816, 0.41328895] * u.s, + atol=0.1 * u.ms, + ) + # should be future-proof; surely we've moved to another planet by then + with pytest.raises(IndexError): + ut1_utc2, status2 = iers_tab.ut1_utc(1e11, 0.0) + # also check it returns the right status + ut1_utc2, status2 = iers_tab.ut1_utc(jd1, jd2, return_status=True) + assert np.all(status2 == iers.FROM_IERS_B) + ut1_utc4, status4 = iers_tab.ut1_utc(1e11, 0.0, return_status=True) + assert status4 == iers.TIME_BEYOND_IERS_RANGE + + # check it works via Time too + t = Time(jd1, jd2, format="jd", scale="utc") + ut1_utc3 = iers_tab.ut1_utc(t) + assert_quantity_allclose( + ut1_utc3, + [-0.5868211, -0.5868184, -0.5868184, 0.4131816, 0.41328895] * u.s, + atol=0.1 * u.ms, + ) + + # Table behaves properly as a table (e.g. can be sliced) + assert len(iers_tab[:2]) == 2 + + def test_open_filename(self): + iers.IERS_B.close() + iers.IERS_B.open(iers.IERS_B_FILE) + assert iers.IERS_B.iers_table is not None + assert isinstance(iers.IERS_B.iers_table, QTable) + iers.IERS_B.close() + with pytest.raises(FILE_NOT_FOUND_ERROR): + iers.IERS_B.open("surely this does not exist") + + def test_open_network_url(self): + iers.IERS_A.close() + iers.IERS_A.open(Path(IERS_A_EXCERPT).as_uri()) + assert iers.IERS_A.iers_table is not None + assert isinstance(iers.IERS_A.iers_table, QTable) + iers.IERS_A.close() + + +@pytest.mark.parametrize("path_transform", [os.fspath, Path]) +def test_IERS_B_old_style_excerpt(path_transform): + """Check that the instructions given in `IERS_B.read` actually work.""" + # If this test is changed, be sure to also adjust the instructions. + # + # TODO: this test and the note can probably be removed after + # enough time has passed that old-style IERS_B files are simply + # not around any more, say in 2025. If so, also remove the excerpt + # and the ReadMe.eopc04_IAU2000 file. + old_style_file = path_transform( + get_pkg_data_filename(os.path.join("data", "iers_b_old_style_excerpt")) + ) + excerpt = iers.IERS_B.read( + old_style_file, + readme=get_pkg_data_filename( + "data/ReadMe.eopc04_IAU2000", package="astropy.utils.iers" + ), + data_start=14, + ) + assert isinstance(excerpt, QTable) + assert "PM_x_dot" not in excerpt.colnames + + +class TestIERS_AExcerpt: + @classmethod + def teardown_class(cls): + iers.IERS_A.close() + + def test_simple(self): + # Test the IERS A reader. It is also a regression tests that ensures + # values do not get overridden by IERS B; see #4933. + iers_tab = iers.IERS_A.open(IERS_A_EXCERPT) + + assert (iers_tab["UT1_UTC"].unit / u.second).is_unity() + assert "P" in iers_tab["UT1Flag"] + assert "I" in iers_tab["UT1Flag"] + assert "B" in iers_tab["UT1Flag"] + assert np.all( + (iers_tab["UT1Flag"] == "I") + | (iers_tab["UT1Flag"] == "P") + | (iers_tab["UT1Flag"] == "B") + ) + + assert (iers_tab["dX_2000A"].unit / u.marcsec).is_unity() + assert (iers_tab["dY_2000A"].unit / u.marcsec).is_unity() + assert "P" in iers_tab["NutFlag"] + assert "I" in iers_tab["NutFlag"] + assert "B" in iers_tab["NutFlag"] + assert np.all( + (iers_tab["NutFlag"] == "P") + | (iers_tab["NutFlag"] == "I") + | (iers_tab["NutFlag"] == "B") + ) + + assert (iers_tab["PM_x"].unit / u.arcsecond).is_unity() + assert (iers_tab["PM_y"].unit / u.arcsecond).is_unity() + assert "P" in iers_tab["PolPMFlag"] + assert "I" in iers_tab["PolPMFlag"] + assert "B" in iers_tab["PolPMFlag"] + assert np.all( + (iers_tab["PolPMFlag"] == "P") + | (iers_tab["PolPMFlag"] == "I") + | (iers_tab["PolPMFlag"] == "B") + ) + + t = Time([57053.0, 57054.0, 57055.0], format="mjd") + ut1_utc, status = iers_tab.ut1_utc(t, return_status=True) + assert status[0] == iers.FROM_IERS_B + assert np.all(status[1:] == iers.FROM_IERS_A) + # These values are *exactly* as given in the table, so they should + # match to double precision accuracy. + assert_quantity_allclose( + ut1_utc, [-0.4916557, -0.4925323, -0.4934373] * u.s, atol=0.1 * u.ms + ) + + dcip_x, dcip_y, status = iers_tab.dcip_xy(t, return_status=True) + assert status[0] == iers.FROM_IERS_B + assert np.all(status[1:] == iers.FROM_IERS_A) + # These values are *exactly* as given in the table, so they should + # match to double precision accuracy. + print(dcip_x) + print(dcip_y) + assert_quantity_allclose( + dcip_x, [-0.086, -0.093, -0.087] * u.marcsec, atol=1.0 * u.narcsec + ) + assert_quantity_allclose( + dcip_y, [0.094, 0.081, 0.072] * u.marcsec, atol=1 * u.narcsec + ) + + pm_x, pm_y, status = iers_tab.pm_xy(t, return_status=True) + assert status[0] == iers.FROM_IERS_B + assert np.all(status[1:] == iers.FROM_IERS_A) + assert_quantity_allclose( + pm_x, [0.003734, 0.004581, 0.004623] * u.arcsec, atol=0.1 * u.marcsec + ) + assert_quantity_allclose( + pm_y, [0.310824, 0.313150, 0.315517] * u.arcsec, atol=0.1 * u.marcsec + ) + + # Table behaves properly as a table (e.g. can be sliced) + assert len(iers_tab[:2]) == 2 + + +class TestIERS_A: + @classmethod + def teardown_class(cls): + iers.IERS_A.close() + + def test_simple(self): + """Test that open() by default reads a 'finals2000A.all' file.""" + # Ensure we remove any cached table (gh-5131). + iers.IERS_A.close() + iers_tab = iers.IERS_A.open() + jd1 = np.array([2456108.5, 2456108.5, 2456108.5, 2456109.5, 2456109.5]) + jd2 = np.array([0.49999421, 0.99997685, 0.99998843, 0.0, 0.5]) + ut1_utc, status = iers_tab.ut1_utc(jd1, jd2, return_status=True) + assert np.all(status == iers.FROM_IERS_B) + assert_quantity_allclose( + ut1_utc, + [-0.5868211, -0.5868184, -0.5868184, 0.4131816, 0.41328895] * u.s, + atol=0.1 * u.ms, + ) + ut1_utc2, status2 = iers_tab.ut1_utc(1e11, 0.0, return_status=True) + assert status2 == iers.TIME_BEYOND_IERS_RANGE + + tnow = Time.now() + + ut1_utc3, status3 = iers_tab.ut1_utc(tnow, return_status=True) + assert status3 == iers.FROM_IERS_A_PREDICTION + assert ut1_utc3 != 0.0 + + +class TestIERS_Auto: + def setup_class(self): + """Set up useful data for the tests.""" + self.N = 40 + self.ame = 30.0 + self.iers_a_file_1 = get_pkg_data_filename( + os.path.join("data", "finals2000A-2016-02-30-test") + ) + self.iers_a_file_2 = get_pkg_data_filename( + os.path.join("data", "finals2000A-2016-04-30-test") + ) + self.iers_a_url_1 = Path(self.iers_a_file_1).as_uri() + self.iers_a_url_2 = Path(self.iers_a_file_2).as_uri() + self.t = Time.now() + TimeDelta(10, format="jd") * np.arange(self.N) + + # This group of tests requires auto downloading to be on + self._auto_download = iers.conf.auto_download + iers.conf.auto_download = True + + # auto_download = False is tested in test_IERS_B_parameters_loading_into_IERS_Auto() + + def teardown_class(self): + # Restore the auto downloading setting + iers.conf.auto_download = self._auto_download + + def teardown_method(self, method): + """Run this after every test.""" + iers.IERS_Auto.close() + + def test_interpolate_error_formatting(self): + """Regression test: make sure the error message in + IERS_Auto._check_interpolate_indices() is formatted correctly. + """ + with iers.conf.set_temp("iers_auto_url", self.iers_a_url_1): + with iers.conf.set_temp("iers_auto_url_mirror", self.iers_a_url_1): + with iers.conf.set_temp("auto_max_age", self.ame): + with pytest.raises( + ValueError, + match=re.escape(iers.INTERPOLATE_ERROR.format(self.ame)), + ): + iers_table = iers.IERS_Auto.open() + with warnings.catch_warnings(): + # Ignoring this if it comes up -- IERS_Auto predictive + # values are older than 30.0 days but downloading the + # latest table did not find newer values + warnings.simplefilter("ignore", iers.IERSStaleWarning) + iers_table.ut1_utc(self.t.jd1, self.t.jd2) + + def test_auto_max_age_none(self): + """Make sure that iers.INTERPOLATE_ERROR's advice about setting + auto_max_age = None actually works. + """ + with iers.conf.set_temp("iers_auto_url", self.iers_a_url_1): + with iers.conf.set_temp("auto_max_age", None): + iers_table = iers.IERS_Auto.open() + delta = iers_table.ut1_utc(self.t.jd1, self.t.jd2) + assert isinstance(delta, np.ndarray) + assert delta.shape == (self.N,) + assert_quantity_allclose(delta, np.array([-0.2246227] * self.N) * u.s) + + def test_auto_max_age_minimum(self): + """Check that the minimum auto_max_age is enforced.""" + with iers.conf.set_temp("iers_auto_url", self.iers_a_url_1): + with iers.conf.set_temp("auto_max_age", 5.0): + with pytest.raises( + ValueError, + match=( + r"IERS auto_max_age configuration value must be larger than 10" + r" days" + ), + ): + iers_table = iers.IERS_Auto.open() + _ = iers_table.ut1_utc(self.t.jd1, self.t.jd2) + + def test_simple(self): + with iers.conf.set_temp("iers_auto_url", self.iers_a_url_1): + dat = iers.IERS_Auto.open() + assert dat["MJD"][0] == 57359.0 * u.d + assert dat["MJD"][-1] == 57539.0 * u.d + + # Pretend we are accessing at a time 7 days after start of predictive data + predictive_mjd = dat.meta["predictive_mjd"] + dat._time_now = Time(predictive_mjd, format="mjd") + 7 * u.d + + # Look at times before and after the test file begins. 0.1292934 is + # the IERS-B value from MJD=57359. The value in + # finals2000A-2016-02-30-test has been replaced at this point. + assert np.allclose( + dat.ut1_utc(Time(50000, format="mjd").jd).value, 0.1292934 + ) + assert np.allclose( + dat.ut1_utc(Time(60000, format="mjd").jd).value, -0.2246227 + ) + + # Now pretend we are accessing at time 60 days after start of predictive data. + # There will be a warning when downloading the file doesn't give new data + # and an exception when extrapolating into the future with insufficient data. + dat._time_now = Time(predictive_mjd, format="mjd") + 60 * u.d + assert np.allclose( + dat.ut1_utc(Time(50000, format="mjd").jd).value, 0.1292934 + ) + with ( + pytest.warns( + iers.IERSStaleWarning, match="IERS_Auto predictive values are older" + ) as warns, + pytest.raises( + ValueError, + match="interpolating from IERS_Auto using predictive values", + ), + ): + dat.ut1_utc(Time(60000, format="mjd").jd) + assert len(warns) == 1 + + # Confirm that disabling the download means no warning because there is no + # refresh to even fail, but there will still be the interpolation error + with ( + iers.conf.set_temp("auto_download", False), + pytest.raises( + ValueError, + match="interpolating from IERS_Auto using predictive values that are more", + ), + ): + dat.ut1_utc(Time(60000, format="mjd").jd) + + # Warning only (i.e., no exception) if we are getting return status + with pytest.warns( + iers.IERSStaleWarning, match="IERS_Auto predictive values are older" + ): + dat.ut1_utc(Time(60000, format="mjd").jd, return_status=True) + + # Now set auto_max_age = None which says that we don't care how old the + # available IERS-A file is. There should be no warnings or exceptions. + with iers.conf.set_temp("auto_max_age", None): + dat.ut1_utc(Time(60000, format="mjd").jd) + + # Now point to a later file with same values but MJD increased by + # 60 days and see that things work. dat._time_now is still the same value + # as before, i.e. right around the start of predictive values for the new file. + # (In other words this is like downloading the latest file online right now). + with iers.conf.set_temp("iers_auto_url", self.iers_a_url_2): + # Look at times before and after the test file begins. This forces a new download. + assert np.allclose( + dat.ut1_utc(Time(50000, format="mjd").jd).value, 0.1292934 + ) + assert np.allclose(dat.ut1_utc(Time(60000, format="mjd").jd).value, -0.3) + + # Now the time range should be different. + assert dat["MJD"][0] == 57359.0 * u.d + assert dat["MJD"][-1] == (57539.0 + 60) * u.d + + +@pytest.mark.parametrize("query", ["ut1_utc", "pm_xy"]) +@pytest.mark.parametrize("jd", [np.array([]), Time([], format="mjd")]) +@pytest.mark.parametrize("return_status", [False, True]) +def test_empty_mjd(query, jd, return_status): + # Regression test for gh-17008 + iers_table = iers.IERS_Auto.open() + result = getattr(iers_table, query)(jd, return_status=return_status) + n_exp = (1 if query == "ut1_utc" else 2) + (1 if return_status else 0) + if n_exp == 1: + assert isinstance(result, np.ndarray) + assert result.size == 0 + else: + assert len(result) == n_exp + assert all(r.size == 0 for r in result) + + +def test_IERS_B_parameters_loading_into_IERS_Auto(): + # Make sure that auto downloading is off + with iers.conf.set_temp("auto_download", False): + A = iers.IERS_Auto.open() + B = iers.IERS_B.open() + + ok_A = A["MJD"] <= B["MJD"][-1] + assert not np.all(ok_A), "IERS B covers all of IERS A: should not happen" + + # We only overwrite IERS_B values in the IERS_A table that were already + # there in the first place. Better take that into account. + ok_A &= np.isfinite(A["UT1_UTC_B"]) + + i_B = np.searchsorted(B["MJD"], A["MJD"][ok_A]) + + assert np.all(np.diff(i_B) == 1), "Valid region not contiguous" + assert np.all(A["MJD"][ok_A] == B["MJD"][i_B]) + # Check that values are copied correctly. Since units are not + # necessarily the same, we use allclose with very strict tolerance. + for name in ("UT1_UTC", "PM_x", "PM_y", "dX_2000A", "dY_2000A"): + assert_quantity_allclose( + A[name][ok_A], + B[name][i_B], + rtol=1e-15, + err_msg=( + f"Bug #9206 IERS B parameter {name} not copied over " + "correctly to IERS Auto" + ), + ) + + +# Issue with FTP, rework test into previous one when it's fixed +@pytest.mark.skipif(CI, reason="Flaky on CI") +@pytest.mark.remote_data +def test_iers_a_dl(): + iersa_tab = iers.IERS_A.open(iers.IERS_A_URL, cache=False) + try: + # some basic checks to ensure the format makes sense + assert len(iersa_tab) > 0 + assert "UT1_UTC_A" in iersa_tab.colnames + finally: + iers.IERS_A.close() + + +@pytest.mark.remote_data +def test_iers_a_dl_mirror(): + iersa_tab = iers.IERS_A.open(iers.IERS_A_URL_MIRROR, cache=False) + try: + # some basic checks to ensure the format makes sense + assert len(iersa_tab) > 0 + assert "UT1_UTC_A" in iersa_tab.colnames + finally: + iers.IERS_A.close() + + +@pytest.mark.remote_data +def test_iers_b_dl(): + iersb_tab = iers.IERS_B.open(iers.IERS_B_URL, cache=False) + try: + # some basic checks to ensure the format makes sense + assert len(iersb_tab) > 0 + assert "UT1_UTC" in iersb_tab.colnames + finally: + iers.IERS_B.close() + + +def test_iers_b_out_of_range_handling(): + # The following error/warning applies only to IERS_B, not to the default IERS_Auto + with iers.earth_orientation_table.set(iers.IERS_B.open()): + now = Time.now() + + # Should be fine with bundled IERS-B + (now - 300 * u.day).ut1 + + # Default is to raise an error + match = r"\(some\) times are outside of range covered by IERS table" + with pytest.raises(iers.IERSRangeError, match=match): + (now + 100 * u.day).ut1 + + with iers.conf.set_temp("iers_degraded_accuracy", "warn"): + with pytest.warns(iers.IERSDegradedAccuracyWarning, match=match): + (now + 100 * u.day).ut1 + + with iers.conf.set_temp("iers_degraded_accuracy", "ignore"): + (now + 100 * u.day).ut1 + + +@pytest.mark.remote_data +def test_iers_download_error_handling(tmp_path): + # Make sure an IERS-A table isn't already loaded + with set_temp_cache(tmp_path), iers.conf.set_temp("auto_download", True): + iers.IERS_A.close() + iers.IERS_Auto.close() + iers.IERS.close() + now = Time.now() + + # bad site name + with iers.conf.set_temp("iers_auto_url", "FAIL FAIL"): + # site that exists but doesn't have IERS data + with iers.conf.set_temp("iers_auto_url_mirror", "https://google.com"): + with pytest.warns(iers.IERSWarning) as record: + with iers.conf.set_temp("iers_degraded_accuracy", "ignore"): + (now + 400 * u.day).ut1 + + assert len(record) == 3 + assert str(record[0].message).startswith( + "failed to download FAIL FAIL: Malformed URL" + ) + assert str(record[1].message).startswith( + "malformed IERS table from https://google.com" + ) + assert str(record[2].message).startswith( + "unable to download valid IERS file, using bundled IERS-A" + ) + + +OLD_DATA_FILES = { + "Leap_Second.dat": IERS_LEAP_SECOND_FILE, + "ReadMe.finals2000A": IERS_A_README, + "ReadMe.eopc04": IERS_B_README, + "eopc04.1962-now": IERS_B_FILE, +} + + +@pytest.mark.parametrize("data_file", sorted(OLD_DATA_FILES)) +def test_get_pkg_data_filename_backcompat(data_file): + # Check that get_pkg_data_filename continues to work without breakage + # if users use it to access IERS tables and READMEs that used to be in + # astropy/utils/iers/data. + + with pytest.warns( + AstropyDeprecationWarning, + match=f"Accessing {data_file} in this way is deprecated", + ): + filename = get_pkg_data_filename( + "data/" + data_file, package="astropy.utils.iers" + ) + + assert filename == OLD_DATA_FILES[data_file] diff --git a/astropy/utils/iers/tests/test_leap_second.py b/astropy/utils/iers/tests/test_leap_second.py new file mode 100644 index 000000000000..26565881dd28 --- /dev/null +++ b/astropy/utils/iers/tests/test_leap_second.py @@ -0,0 +1,507 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +import locale +import os +import pkgutil +import platform +import urllib.request + +import erfa +import numpy as np +import pytest +from numpy.testing import assert_array_equal + +import astropy +from astropy.time import Time, TimeDelta +from astropy.utils.data import get_pkg_data_filename +from astropy.utils.iers import iers + +# Import every astropy module as a test that the ERFA leap second +# table is not updated for normal imports. +for finder, name, _ in pkgutil.walk_packages(astropy.__path__, prefix="astropy."): + finder.find_spec(name) + +# Now test that the erfa leap_seconds table has not been updated. This must be +# done at the module level, which unfortunately will abort the entire test run +# if if fails. Running within a normal pytest test will not work because the +# other tests will end up updating this attribute by virtue of doing Time UTC +# transformations. +assert erfa.leap_seconds._expires is None + +# Tests in this module assume that the erfa.leap_seconds attribute has been +# updated from the `erfa` package built-in table to the astropy built-in +# leap-second table. That has the effect of ensuring that the +# `erfa.leap_seconds.expires` property is sufficiently in the future. +iers_table = iers.LeapSeconds.auto_open() +erfa.leap_seconds.update(iers_table) +assert erfa.leap_seconds._expires is not None + +SYSTEM_FILE = "/usr/share/zoneinfo/leap-seconds.list" + +# Test leap_seconds.list in test/data. +LEAP_SECOND_LIST = get_pkg_data_filename("data/leap-seconds.list") + + +def test_configuration(): + # This test just ensures things stay consistent. + # Adjust if changes are made. + assert iers.conf.iers_leap_second_auto_url == iers.IERS_LEAP_SECOND_URL + assert iers.conf.ietf_leap_second_auto_url == iers.IETF_LEAP_SECOND_URL + + +class TestReading: + """Basic tests that leap seconds can be read.""" + + def verify_day_month_year(self, ls): + assert np.all(ls["day"] == 1) + assert np.all((ls["month"] == 1) | (ls["month"] == 7) | (ls["year"] < 1970)) + assert np.all(ls["year"] >= 1960) + t = Time( + {"year": ls["year"], "month": ls["month"], "day": ls["day"]}, + format="ymdhms", + ) + assert np.all(t == Time(ls["mjd"], format="mjd")) + + def test_read_leap_second_dat(self): + ls = iers.LeapSeconds.from_iers_leap_seconds(iers.IERS_LEAP_SECOND_FILE) + # Below, >= to take into account we might ship and updated file. + assert ls.expires >= Time("2020-06-28", scale="tai") + assert ls["mjd"][0] == 41317 + assert ls["tai_utc"][0] == 10 + assert ls["mjd"][-1] >= 57754 + assert ls["tai_utc"][-1] >= 37 + self.verify_day_month_year(ls) + + def test_read_leap_second_dat_locale(self): + current = locale.setlocale(locale.LC_ALL) + try: + if platform.system() == "Darwin": + locale.setlocale(locale.LC_ALL, "fr_FR") + else: + locale.setlocale(locale.LC_ALL, "fr_FR.utf8") + + ls = iers.LeapSeconds.from_iers_leap_seconds(iers.IERS_LEAP_SECOND_FILE) + except locale.Error as e: + pytest.skip(f"Locale error: {e}") + finally: + locale.setlocale(locale.LC_ALL, current) + + # Below, >= to take into account we might ship and updated file. + assert ls.expires >= Time("2020-06-28", scale="tai") + + def test_open_leap_second_dat(self): + ls = iers.LeapSeconds.from_iers_leap_seconds(iers.IERS_LEAP_SECOND_FILE) + ls2 = iers.LeapSeconds.open(iers.IERS_LEAP_SECOND_FILE) + assert np.all(ls == ls2) + + @pytest.mark.parametrize( + "file", + (LEAP_SECOND_LIST, "file:" + urllib.request.pathname2url(LEAP_SECOND_LIST)), + ) + def test_read_leap_seconds_list(self, file): + ls = iers.LeapSeconds.from_leap_seconds_list(file) + assert ls.expires == Time("2020-06-28", scale="tai") + assert ls["mjd"][0] == 41317 + assert ls["tai_utc"][0] == 10 + assert ls["mjd"][-1] == 57754 + assert ls["tai_utc"][-1] == 37 + self.verify_day_month_year(ls) + + @pytest.mark.parametrize( + "file", + (LEAP_SECOND_LIST, "file:" + urllib.request.pathname2url(LEAP_SECOND_LIST)), + ) + def test_open_leap_seconds_list(self, file): + ls = iers.LeapSeconds.from_leap_seconds_list(file) + ls2 = iers.LeapSeconds.open(file) + assert np.all(ls == ls2) + + @pytest.mark.skipif( + not os.path.isfile(SYSTEM_FILE), reason=f"system does not have {SYSTEM_FILE}" + ) + def test_open_system_file(self): + ls = iers.LeapSeconds.open(SYSTEM_FILE) + expired = ls.expires < Time.now() + if expired: + pytest.skip("System leap second file is expired.") + assert not expired + + +def make_fake_file(expiration, tmp_path): + """copy the built-in IERS file but set a different expiration date.""" + ls = iers.LeapSeconds.from_iers_leap_seconds() + fake_file = str(tmp_path / "fake_leap_seconds.dat") + with open(fake_file, "w") as fh: + fh.write( + "\n".join([f"# File expires on {expiration}"] + str(ls).split("\n")[2:-1]) + ) + return fake_file + + +def test_fake_file(tmp_path): + fake_file = make_fake_file("28 June 2345", tmp_path) + fake = iers.LeapSeconds.from_iers_leap_seconds(fake_file) + assert fake.expires == Time("2345-06-28", scale="tai") + + +class TestAutoOpenExplicitLists: + # For this set of tests, leap-seconds are allowed to be expired + # except as explicitly tested. + @pytest.mark.filterwarnings(iers.IERSStaleWarning) + def test_auto_open_simple(self): + ls = iers.LeapSeconds.auto_open([iers.IERS_LEAP_SECOND_FILE]) + assert ls.meta["data_url"] == iers.IERS_LEAP_SECOND_FILE + + @pytest.mark.filterwarnings(iers.IERSStaleWarning) + def test_auto_open_erfa(self): + ls = iers.LeapSeconds.auto_open(["erfa", iers.IERS_LEAP_SECOND_FILE]) + assert ls.meta["data_url"] in ["erfa", iers.IERS_LEAP_SECOND_FILE] + + @pytest.mark.filterwarnings(iers.IERSStaleWarning) + def test_fake_future_file(self, tmp_path): + fake_file = make_fake_file("28 June 2345", tmp_path) + # Try as system file for auto_open, setting auto_max_age such + # that any ERFA or system files are guaranteed to be expired, + # while the fake file is guaranteed to be OK. + with iers.conf.set_temp("auto_max_age", -100000): + ls = iers.LeapSeconds.auto_open( + ["erfa", iers.IERS_LEAP_SECOND_FILE, fake_file] + ) + assert ls.expires == Time("2345-06-28", scale="tai") + assert ls.meta["data_url"] == str(fake_file) + # And as URL + fake_url = "file:" + urllib.request.pathname2url(fake_file) + ls2 = iers.LeapSeconds.auto_open( + ["erfa", iers.IERS_LEAP_SECOND_FILE, fake_url] + ) + assert ls2.expires == Time("2345-06-28", scale="tai") + assert ls2.meta["data_url"] == str(fake_url) + + def test_fake_expired_file(self, tmp_path): + fake_file1 = make_fake_file("28 June 2010", tmp_path) + fake_file2 = make_fake_file("27 June 2012", tmp_path) + # Between these and the built-in one, the built-in file is best. + ls = iers.LeapSeconds.auto_open( + [fake_file1, fake_file2, iers.IERS_LEAP_SECOND_FILE] + ) + assert ls.meta["data_url"] == iers.IERS_LEAP_SECOND_FILE + + # But if we remove the built-in one, the least expired one will be + # used and we get a warning that it is stale. + with pytest.warns(iers.IERSStaleWarning): + ls2 = iers.LeapSeconds.auto_open([fake_file1, fake_file2]) + assert ls2.meta["data_url"] == fake_file2 + assert ls2.expires == Time("2012-06-27", scale="tai") + + # Use the fake files to make sure auto_max_age is safe. + # Should have no warning in either example. + with iers.conf.set_temp("auto_max_age", None): + ls3 = iers.LeapSeconds.auto_open([fake_file1, iers.IERS_LEAP_SECOND_FILE]) + assert ls3.meta["data_url"] == iers.IERS_LEAP_SECOND_FILE + with iers.conf.set_temp("auto_max_age", None): + ls4 = iers.LeapSeconds.auto_open([fake_file1, fake_file2]) + assert ls4.meta["data_url"] == fake_file2 + + +@pytest.mark.remote_data +class TestRemoteURLs: + def setup_class(cls): + # Need auto_download so that IERS_B won't be loaded and cause tests to + # fail. + iers.conf.auto_download = True + + def teardown_class(cls): + # This setting is to be consistent with astropy/conftest.py + iers.conf.auto_download = False + + # In these tests, the results may be cached. + # This is fine - no need to download again. + def test_iers_url(self): + ls = iers.LeapSeconds.auto_open([iers.IERS_LEAP_SECOND_URL]) + assert ls.expires > Time.now() + + def test_ietf_url(self): + ls = iers.LeapSeconds.auto_open([iers.IETF_LEAP_SECOND_URL]) + assert ls.expires > Time.now() + + +class TestDefaultAutoOpen: + """Test auto_open with different _auto_open_files.""" + + def setup_method(self): + # Identical to what is used in LeapSeconds.auto_open(). + self.good_enough = iers.LeapSeconds._today() + TimeDelta( + 180 - iers._none_to_float(iers.conf.auto_max_age), format="jd" + ) + self._auto_open_files = iers.LeapSeconds._auto_open_files.copy() + + def teardown_method(self): + iers.LeapSeconds._auto_open_files = self._auto_open_files + + def remove_auto_open_files(self, *files): + """Remove some files from the auto-opener. + + The default set is restored in teardown. + """ + for f in files: + iers.LeapSeconds._auto_open_files.remove(f) + + def test_erfa_found(self): + # Set huge maximum age such that whatever ERFA has is OK. + # Since it is checked first, it should thus be found. + with iers.conf.set_temp("auto_max_age", 100000): + ls = iers.LeapSeconds.open() + assert ls.meta["data_url"] == "erfa" + + def test_builtin_found(self): + # Set huge maximum age such that built-in file is always OK. + # If we remove 'erfa', it should thus be found. + self.remove_auto_open_files("erfa") + with iers.conf.set_temp("auto_max_age", 100000): + ls = iers.LeapSeconds.open() + assert ls.meta["data_url"] == iers.IERS_LEAP_SECOND_FILE + + # The test below is marked remote_data only to ensure it runs + # as an allowed-fail job on CI: i.e., we will notice it (eventually) + # but will not be misled in thinking that a PR is bad. + @pytest.mark.remote_data + def test_builtin_not_expired(self): + # TODO: would be nice to have automatic PRs for this! + ls = iers.LeapSeconds.open(iers.IERS_LEAP_SECOND_FILE) + assert ls.expires > self.good_enough, ( + "The leap second file built in to astropy is expired. Fix with:\n" + "cd astropy/utils/iers/data/; . update_builtin_iers.sh\n" + "and commit as a PR (for details, see release procedure)." + ) + + def test_fake_future_file(self, tmp_path): + fake_file = make_fake_file("28 June 2345", tmp_path) + # Try as system file for auto_open, setting auto_max_age such + # that any ERFA or system files are guaranteed to be expired. + with ( + iers.conf.set_temp("auto_max_age", -100000), + iers.conf.set_temp("system_leap_second_file", fake_file), + ): + ls = iers.LeapSeconds.open() + assert ls.expires == Time("2345-06-28", scale="tai") + assert ls.meta["data_url"] == str(fake_file) + # And as URL + fake_url = "file:" + urllib.request.pathname2url(fake_file) + with ( + iers.conf.set_temp("auto_max_age", -100000), + iers.conf.set_temp("iers_leap_second_auto_url", fake_url), + ): + ls2 = iers.LeapSeconds.open() + assert ls2.expires == Time("2345-06-28", scale="tai") + assert ls2.meta["data_url"] == str(fake_url) + + def test_fake_expired_file(self, tmp_path): + self.remove_auto_open_files( + "erfa", "iers_leap_second_auto_url", "ietf_leap_second_auto_url" + ) + fake_file = make_fake_file("28 June 2010", tmp_path) + with iers.conf.set_temp("system_leap_second_file", fake_file): + # If we try this directly, the built-in file will be found. + ls = iers.LeapSeconds.open() + assert ls.meta["data_url"] == iers.IERS_LEAP_SECOND_FILE + + # But if we remove the built-in one, the expired one will be + # used and we get a warning that it is stale. + self.remove_auto_open_files(iers.IERS_LEAP_SECOND_FILE) + with pytest.warns(iers.IERSStaleWarning): + ls2 = iers.LeapSeconds.open() + assert ls2.meta["data_url"] == fake_file + assert ls2.expires == Time("2010-06-28", scale="tai") + + @pytest.mark.skipif( + not os.path.isfile(SYSTEM_FILE), reason=f"system does not have {SYSTEM_FILE}" + ) + def test_system_file_used_if_not_expired(self, tmp_path): + # We skip the test if the system file is on a CI and is expired - + # we should not depend on CI keeping it up to date, but if it is, + # we should check that it is used if possible. + if iers.LeapSeconds.open(SYSTEM_FILE).expires <= self.good_enough: + pytest.skip("System leap second file is expired.") + + self.remove_auto_open_files("erfa") + with iers.conf.set_temp("system_leap_second_file", SYSTEM_FILE): + ls = iers.LeapSeconds.open() + assert ls.expires > self.good_enough + assert ls.meta["data_url"] in (iers.IERS_LEAP_SECOND_FILE, SYSTEM_FILE) + + # Also check with a "built-in" file that is expired + fake_file = make_fake_file("28 June 2017", tmp_path) + iers.LeapSeconds._auto_open_files[0] = fake_file + ls2 = iers.LeapSeconds.open() + assert ls2.expires > Time.now() + assert ls2.meta["data_url"] == SYSTEM_FILE + + @pytest.mark.remote_data + def test_auto_open_urls_always_good_enough(self): + # Avoid using the erfa, built-in and system files, as they might + # be good enough already. + try: + # Need auto_download so that IERS_B won't be loaded and + # cause tests to fail. + iers.conf.auto_download = True + + self.remove_auto_open_files( + "erfa", iers.IERS_LEAP_SECOND_FILE, "system_leap_second_file" + ) + ls = iers.LeapSeconds.open() + assert ls.expires > self.good_enough + assert ls.meta["data_url"].startswith("http") + finally: + # This setting is to be consistent with astropy/conftest.py + iers.conf.auto_download = False + + +class ERFALeapSecondsSafe: + """Base class for tests that change the ERFA leap-second tables. + + It ensures the original state is restored. + """ + + def setup_method(self): + # Keep current leap-second table and expiration. + self.erfa_ls = self._erfa_ls = erfa.leap_seconds.get() + self.erfa_expires = self._expires = erfa.leap_seconds._expires + + def teardown_method(self): + # Restore leap-second table and expiration. + erfa.leap_seconds.set(self.erfa_ls) + erfa.leap_seconds._expires = self._expires + + +class TestFromERFA(ERFALeapSecondsSafe): + def test_get_erfa_ls(self): + ls = iers.LeapSeconds.from_erfa() + assert ls.colnames == ["year", "month", "tai_utc"] + assert isinstance(ls.expires, Time) + assert ls.expires == self.erfa_expires + ls_array = np.array(ls["year", "month", "tai_utc"]) + assert np.all(ls_array == self.erfa_ls) + + def test_get_built_in_erfa_ls(self): + ls = iers.LeapSeconds.from_erfa(built_in=True) + assert ls.colnames == ["year", "month", "tai_utc"] + assert isinstance(ls.expires, Time) + ls_array = np.array(ls["year", "month", "tai_utc"]) + assert np.all(ls_array == self.erfa_ls[: len(ls_array)]) + + def test_get_modified_erfa_ls(self): + erfa.leap_seconds.set(self.erfa_ls[:-10]) + ls = iers.LeapSeconds.from_erfa() + assert len(ls) == len(self.erfa_ls) - 10 + ls_array = np.array(ls["year", "month", "tai_utc"]) + assert np.all(ls_array == self.erfa_ls[:-10]) + ls2 = iers.LeapSeconds.from_erfa(built_in=True) + assert len(ls2) > len(ls) + erfa.leap_seconds.set(None) + erfa_built_in = erfa.leap_seconds.get() + assert len(ls2) == len(erfa_built_in) + ls2_array = np.array(ls2["year", "month", "tai_utc"]) + assert np.all(ls2_array == erfa_built_in) + + def test_open(self): + ls = iers.LeapSeconds.open("erfa") + assert isinstance(ls.expires, Time) + assert ls.expires == self.erfa_expires + ls_array = np.array(ls["year", "month", "tai_utc"]) + assert np.all(ls_array == self.erfa_ls) + + +class TestUpdateLeapSeconds(ERFALeapSecondsSafe): + def setup_method(self): + super().setup_method() + # Read default leap second table. + self.ls = iers.LeapSeconds.from_iers_leap_seconds() + # For tests, reset ERFA table to built-in default. + erfa.leap_seconds.set() + self.erfa_ls = erfa.leap_seconds.get() + + def test_built_in_up_to_date(self): + """Leap second should match between built-in and ERFA.""" + erfa_since_1970 = self.erfa_ls[self.erfa_ls["year"] > 1970] + assert len(self.ls) >= len(erfa_since_1970), "built-in leap seconds out of date" + assert len(self.ls) <= len(erfa_since_1970), "ERFA leap seconds out of date" + overlap = np.array(self.ls["year", "month", "tai_utc"]) + assert np.all(overlap == erfa_since_1970.astype(overlap.dtype)) + + def test_update_with_built_in(self): + """An update with built-in should not do anything.""" + n_update = self.ls.update_erfa_leap_seconds() + assert n_update == 0 + new_erfa_ls = erfa.leap_seconds.get() + assert np.all(new_erfa_ls == self.erfa_ls) + + @pytest.mark.parametrize("n_short", (1, 3)) + def test_update(self, n_short): + """Check whether we can recover removed leap seconds.""" + erfa.leap_seconds.set(self.erfa_ls[:-n_short]) + n_update = self.ls.update_erfa_leap_seconds() + assert n_update == n_short + new_erfa_ls = erfa.leap_seconds.get() + assert_array_equal(new_erfa_ls, self.erfa_ls) + # Check that a second update does not do anything. + n_update2 = self.ls.update_erfa_leap_seconds() + assert n_update2 == 0 + new_erfa_ls2 = erfa.leap_seconds.get() + assert_array_equal(new_erfa_ls2, self.erfa_ls) + + def test_update_initialize_erfa(self): + # With pre-initialization, update does nothing. + erfa.leap_seconds.set(self.erfa_ls[:-2]) + n_update = self.ls.update_erfa_leap_seconds(initialize_erfa=True) + assert n_update == 0 + new_erfa_ls = erfa.leap_seconds.get() + assert_array_equal(new_erfa_ls, self.erfa_ls) + + def test_update_overwrite(self): + n_update = self.ls.update_erfa_leap_seconds(initialize_erfa="empty") + assert n_update == len(self.ls) + new_erfa_ls = erfa.leap_seconds.get() + assert new_erfa_ls["year"].min() > 1970 + n_update2 = self.ls.update_erfa_leap_seconds() + assert n_update2 == 0 + new_erfa_ls2 = erfa.leap_seconds.get() + assert_array_equal(new_erfa_ls2, new_erfa_ls) + n_update3 = self.ls.update_erfa_leap_seconds(initialize_erfa=True) + assert n_update3 == 0 + new_erfa_ls3 = erfa.leap_seconds.get() + assert_array_equal(new_erfa_ls3, self.erfa_ls) + + def test_bad_jump(self): + erfa.leap_seconds.set(self.erfa_ls[:-2]) + bad = self.ls.copy() + bad["tai_utc"][-1] = 5 + with pytest.raises(ValueError, match="jump"): + bad.update_erfa_leap_seconds() + # With an error the ERFA table should not change. + assert_array_equal(erfa.leap_seconds.get(), self.erfa_ls[:-2]) + + # Unless we initialized it beforehand. + with pytest.raises(ValueError, match="jump"): + bad.update_erfa_leap_seconds(initialize_erfa=True) + assert_array_equal(erfa.leap_seconds.get(), self.erfa_ls) + + # Of course, we get no errors if we initialize only. + erfa.leap_seconds.set(self.erfa_ls[:-2]) + n_update = bad.update_erfa_leap_seconds(initialize_erfa="only") + assert n_update == 0 + new_erfa_ls = erfa.leap_seconds.get() + assert_array_equal(new_erfa_ls, self.erfa_ls) + + def test_bad_day(self): + erfa.leap_seconds.set(self.erfa_ls[:-2]) + bad = self.ls.copy() + bad["day"][-1] = 5 + with pytest.raises(ValueError, match="not on 1st"): + bad.update_erfa_leap_seconds() + + def test_bad_month(self): + erfa.leap_seconds.set(self.erfa_ls[:-2]) + bad = self.ls.copy() + bad["month"][-1] = 5 + with pytest.raises(ValueError, match="January"): + bad.update_erfa_leap_seconds() + assert_array_equal(erfa.leap_seconds.get(), self.erfa_ls[:-2]) diff --git a/astropy/utils/introspection.py b/astropy/utils/introspection.py new file mode 100644 index 000000000000..ad7bec8f2d2e --- /dev/null +++ b/astropy/utils/introspection.py @@ -0,0 +1,398 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +"""Functions related to Python runtime introspection.""" + +import inspect +import os +import sys +from importlib import import_module, metadata +from importlib.metadata import packages_distributions +from types import FrameType, ModuleType +from typing import Literal + +from packaging.version import Version + +from .decorators import deprecated + +__all__ = ["find_current_module", "isinstancemethod", "minversion", "resolve_name"] + +__doctest_skip__ = ["find_current_module"] + + +@deprecated( + since="7.0", alternative="importlib (e.g. importlib.import_module for modules)" +) +def resolve_name(name: str, *additional_parts: str) -> object: + """Resolve a name like ``module.object`` to an object and return it. + + This ends up working like ``from module import object`` but is easier + to deal with than the `__import__` builtin and supports digging into + submodules. + + Parameters + ---------- + name : `str` + A dotted path to a Python object--that is, the name of a function, + class, or other object in a module with the full path to that module, + including parent modules, separated by dots. Also known as the fully + qualified name of the object. + + additional_parts : iterable, optional + If more than one positional arguments are given, those arguments are + automatically dotted together with ``name``. + + Raises + ------ + `ImportError` + If the module or named object is not found. + """ + additional_parts = ".".join(additional_parts) + + if additional_parts: + name = name + "." + additional_parts + + parts = name.split(".") + + if len(parts) == 1: + # No dots in the name--just a straight up module import + cursor = 1 + fromlist = [] + else: + cursor = len(parts) - 1 + fromlist = [parts[-1]] + + module_name = parts[:cursor] + + while cursor > 0: + try: + ret = __import__(".".join(module_name), fromlist=fromlist) + break + except ImportError: + if cursor == 0: + raise + cursor -= 1 + module_name = parts[:cursor] + fromlist = [parts[cursor]] + ret = "" + + for part in parts[cursor:]: + try: + ret = getattr(ret, part) + except AttributeError: + raise ImportError(name) + + return ret + + +def minversion(module: ModuleType | str, version: str, inclusive: bool = True) -> bool: + """ + Returns `True` if the specified Python module satisfies a minimum version + requirement, and `False` if not. + + Parameters + ---------- + module : module or `str` + An imported module of which to check the version, or the name of + that module (in which case an import of that module is attempted-- + if this fails `False` is returned). + + version : `str` + The version as a string that this module must have at a minimum (e.g. + ``'0.12'``). + + inclusive : `bool` + The specified version meets the requirement inclusively (i.e. ``>=``) + as opposed to strictly greater than (default: `True`). + + Examples + -------- + >>> import numpy + >>> minversion(numpy, '1.21.0') + True + """ + if inspect.ismodule(module): + module_name = module.__name__ + module_version = getattr(module, "__version__", None) + elif isinstance(module, str): + module_name = module + module_version = None + try: + module = import_module(module_name) + except ImportError: + return False + else: + raise ValueError( + "module argument must be an actual imported " + "module, or the import name of the module; " + f"got {repr(module)}" + ) + + if module_version is None: + try: + module_version = metadata.version(module_name) + except metadata.PackageNotFoundError: + # Maybe the distribution name is different from package name. + # Calling packages_distributions is costly so we do it only + # if necessary, as only a few packages don't have the same + # distribution name. + dist_names = packages_distributions() + module_version = metadata.version(dist_names[module_name][0]) + + if inclusive: + return Version(module_version) >= Version(version) + else: + return Version(module_version) > Version(version) + + +def find_current_module( + depth: int = 1, finddiff: bool | list[Literal[True] | str | ModuleType] = False +) -> ModuleType | None: + """ + Determines the module/package from which this function is called. + + This function has two modes, determined by the ``finddiff`` option. it + will either simply go the requested number of frames up the call + stack (if ``finddiff`` is False), or it will go up the call stack until + it reaches a module that is *not* in a specified set. + + Parameters + ---------- + depth : int + Specifies how far back to go in the call stack (0-indexed, so that + passing in 0 gives back `astropy.utils.misc`). + finddiff : bool or list + If False, the returned ``mod`` will just be ``depth`` frames up from + the current frame. Otherwise, the function will start at a frame + ``depth`` up from current, and continue up the call stack to the + first module that is *different* from those in the provided list. + In this case, ``finddiff`` can be a list of modules or modules + names. Alternatively, it can be True, which will use the module + ``depth`` call stack frames up as the module the returned module + most be different from. + + Returns + ------- + mod : module or None + The module object or None if the package cannot be found. The name of + the module is available as the ``__name__`` attribute of the returned + object (if it isn't None). + + Raises + ------ + ValueError + If ``finddiff`` is a list with an invalid entry. + + Examples + -------- + The examples below assume that there are two modules in a package named + ``pkg``. ``mod1.py``:: + + def find1(): + from astropy.utils import find_current_module + print find_current_module(1).__name__ + def find2(): + from astropy.utils import find_current_module + cmod = find_current_module(2) + if cmod is None: + print 'None' + else: + print cmod.__name__ + def find_diff(): + from astropy.utils import find_current_module + print find_current_module(0,True).__name__ + + ``mod2.py``:: + + def find(): + from .mod1 import find2 + find2() + + With these modules in place, the following occurs:: + + >>> from pkg import mod1, mod2 + >>> from astropy.utils import find_current_module + >>> mod1.find1() + pkg.mod1 + >>> mod1.find2() + None + >>> mod2.find() + pkg.mod2 + >>> find_current_module(0) + + >>> mod1.find_diff() + pkg.mod1 + + """ + frm = inspect.currentframe() + for _ in range(depth): + frm = frm.f_back + if frm is None: + return None + + if finddiff: + currmod = _get_module_from_frame(frm) + if finddiff is True: + diffmods = [currmod] + else: + diffmods = [] + for fd in finddiff: + if inspect.ismodule(fd): + diffmods.append(fd) + elif isinstance(fd, str): + diffmods.append(import_module(fd)) + elif fd is True: + diffmods.append(currmod) + else: + raise ValueError("invalid entry in finddiff") + + while frm: + frmb = frm.f_back + modb = _get_module_from_frame(frmb) + if modb not in diffmods: + return modb + frm = frmb + else: + return _get_module_from_frame(frm) + + +def _get_module_from_frame(frm: FrameType) -> ModuleType | None: + """Uses inspect.getmodule() to get the module that the current frame's + code is running in. + + However, this does not work reliably for code imported from a zip file, + so this provides a fallback mechanism for that case which is less + reliable in general, but more reliable than inspect.getmodule() for this + particular case. + """ + mod = inspect.getmodule(frm) + if mod is not None: + return mod + + # Check to see if we're importing from a bundle file. First ensure that + # __file__ is available in globals; this is cheap to check to bail out + # immediately if this fails + + if "__file__" in frm.f_globals and "__name__" in frm.f_globals: + filename = frm.f_globals["__file__"] + + # Using __file__ from the frame's globals and getting it into the form + # of an absolute path name with .py at the end works pretty well for + # looking up the module using the same means as inspect.getmodule + + if filename[-4:].lower() in (".pyc", ".pyo"): + filename = filename[:-4] + ".py" + filename = os.path.realpath(os.path.abspath(filename)) + if filename in inspect.modulesbyfile: + return sys.modules.get(inspect.modulesbyfile[filename]) + + # On Windows, inspect.modulesbyfile appears to have filenames stored + # in lowercase, so we check for this case too. + if filename.lower() in inspect.modulesbyfile: + return sys.modules.get(inspect.modulesbyfile[filename.lower()]) + + # Otherwise there are still some even trickier things that might be possible + # to track down the module, but we'll leave those out unless we find a case + # where it's really necessary. So return None if the module is not found. + return None + + +@deprecated(since="6.1") +def find_mod_objs(modname, onlylocals=False): + """Returns all the public attributes of a module referenced by name. + + .. note:: + The returned list *not* include subpackages or modules of + ``modname``, nor does it include private attributes (those that + begin with '_' or are not in `__all__`). + + Parameters + ---------- + modname : str + The name of the module to search. + onlylocals : bool or list of str + If `True`, only attributes that are either members of ``modname`` OR + one of its modules or subpackages will be included. If it is a list + of strings, those specify the possible packages that will be + considered "local". + + Returns + ------- + localnames : list of str + A list of the names of the attributes as they are named in the + module ``modname`` . + fqnames : list of str + A list of the full qualified names of the attributes (e.g., + ``astropy.utils.introspection.find_mod_objs``). For attributes that are + simple variables, this is based on the local name, but for functions or + classes it can be different if they are actually defined elsewhere and + just referenced in ``modname``. + objs : list of objects + A list of the actual attributes themselves (in the same order as + the other arguments) + + """ + mod = import_module(modname) + + if hasattr(mod, "__all__"): + pkgitems = [(k, getattr(mod, k)) for k in mod.__all__] + else: + pkgitems = [(k, getattr(mod, k)) for k in dir(mod) if k[0] != "_"] + + # filter out modules and pull the names and objs out + ismodule = inspect.ismodule + localnames = [k for k, v in pkgitems if not ismodule(v)] + objs = [v for k, v in pkgitems if not ismodule(v)] + + # fully qualified names can be determined from the object's module + fqnames = [] + for obj, lnm in zip(objs, localnames): + if hasattr(obj, "__module__") and hasattr(obj, "__name__"): + fqnames.append(obj.__module__ + "." + obj.__name__) + else: + fqnames.append(modname + "." + lnm) + + if onlylocals: + if onlylocals is True: + onlylocals = [modname] + valids = [any(fqn.startswith(nm) for nm in onlylocals) for fqn in fqnames] + localnames = [e for i, e in enumerate(localnames) if valids[i]] + fqnames = [e for i, e in enumerate(fqnames) if valids[i]] + objs = [e for i, e in enumerate(objs) if valids[i]] + + return localnames, fqnames, objs + + +# Note: I would have preferred call this is_instancemethod, but this naming is +# for consistency with other functions in the `inspect` module +@deprecated(since="6.1") +def isinstancemethod(cls, obj): + """ + Returns `True` if the given object is an instance method of the class + it is defined on (as opposed to a `staticmethod` or a `classmethod`). + + This requires both the class the object is a member of as well as the + object itself in order to make this determination. + + Parameters + ---------- + cls : `type` + The class on which this method was defined. + obj : `object` + A member of the provided class (the membership is not checked directly, + but this function will always return `False` if the given object is not + a member of the given class). + """ + if not inspect.isfunction(obj): + return False + + # Unfortunately it seems the easiest way to get to the original + # staticmethod object is to look in the class's __dict__, though we + # also need to look up the MRO in case the method is not in the given + # class's dict + name = obj.__name__ + for basecls in cls.mro(): # This includes cls + if name in basecls.__dict__: + return not isinstance(basecls.__dict__[name], staticmethod) + + # This shouldn't happen, though this is the most sensible response if + # it does. + raise AttributeError(name) diff --git a/astropy/utils/masked/__init__.py b/astropy/utils/masked/__init__.py new file mode 100644 index 000000000000..1286e92e4d67 --- /dev/null +++ b/astropy/utils/masked/__init__.py @@ -0,0 +1,11 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +""" +Built-in mask mixin class. + +The design uses `Masked` as a factory class which automatically +generates new subclasses for any data class that is itself a +subclass of a predefined masked class, with `MaskedNDArray` +providing such a predefined class for `~numpy.ndarray`. +""" + +from .core import * diff --git a/astropy/utils/masked/core.py b/astropy/utils/masked/core.py new file mode 100644 index 000000000000..eb96ad5aa3f2 --- /dev/null +++ b/astropy/utils/masked/core.py @@ -0,0 +1,1462 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +""" +Built-in mask mixin class. + +The design uses `Masked` as a factory class which automatically +generates new subclasses for any data class that is itself a +subclass of a predefined masked class, with `MaskedNDArray` +providing such a predefined class for `~numpy.ndarray`. + +Generally, any new predefined class should override the +``from_unmasked(data, mask, copy=False)`` class method that +creates an instance from unmasked data and a mask, as well as +the ``unmasked`` property that returns just the data. +The `Masked` class itself provides a base ``mask`` property, +which can also be overridden if needed. + +""" + +import abc +import builtins +import importlib + +import numpy as np + +from astropy.utils.compat import NUMPY_LT_2_5 +from astropy.utils.data_info import ParentDtypeInfo +from astropy.utils.shapes import NDArrayShapeMethods, ShapedLikeNDArray + +from .function_helpers import ( + APPLY_TO_BOTH_FUNCTIONS, + DISPATCHED_FUNCTIONS, + MASKED_SAFE_FUNCTIONS, + UNSUPPORTED_FUNCTIONS, +) + +__all__ = [ + "MaskableShapedLikeNDArray", + "Masked", + "MaskedNDArray", + "combine_masks", + "get_data_and_mask", +] + + +get__doc__ = """Masked version of {0.__name__}. + +Except for the ability to pass in a ``mask``, parameters are +as for `{0.__module__}.{0.__name__}`. +""".format + + +def get_data_and_mask(array): + """Split possibly masked array into unmasked and mask. + + Parameters + ---------- + array : array-like + Possibly masked item, judged by whether it has a ``mask`` attribute + (but ignored when a possible ``masked`` attribute is `False`). + If so, checks for having an ``unmasked`` attribute (as expected for + instances of `~astropy.utils.masked.Masked`), or uses the ``_data`` + attribute if the inpuit is an instance of `~numpy.ma.MaskedArray`. + + Returns + ------- + unmasked, mask : array-like + If the input array had no mask, this will be ``array, None``. + + Raises + ------ + AttributeError + If ``array`` has a ``mask`` but not an ``unmasked`` attribute, and + is not an instance of `~numpy.ma.MaskedArray`. + ValueError + If ``array`` is ``np.ma.masked`` (since it has no data). + + """ + mask = getattr(array, "mask", None) + if mask is None or not getattr(array, "masked", True): + return array, None + + try: + return array.unmasked, mask + except AttributeError as exc: + if not isinstance(array, np.ma.MaskedArray): + raise AttributeError( + f"'{type(array).__name__}' object has a 'mask' attribute but not an " + "'unmasked' attribute (and is not an np.ma.MaskedArray instance)." + ) from None + + if array is np.ma.masked: + raise ValueError("cannot handle np.ma.masked.") + + # We use the private _data attribute here since MaskedColumn + # overrides the normal ".data". + return array._data, mask + + +def combine_masks(masks, *, out=None, where=True, copy=True): + """Combine masks, possibly storing it in some output. + + Parameters + ---------- + masks : tuple of array of bool or False or None + Input masks. Any that are `None` or `False` are ignored. + Should broadcast to each other. For structured dtype, + an element is considered masked if any of the fields is. + out : array, optional + Possible output array to hold the result. + where : array of bool, optional + Which elements of the output array to fill. + copy : bool optional + Whether to ensure a copy is made. Only relevant if just a + single input mask is not `None`, and ``out`` is not given. + + Returns + ------- + mask : array + Combined mask. + """ + # Simplify masks, by removing empty ones and combining possible fields. + masks = [ + m if m.dtype.names is None else (m != np.zeros((), dtype=m.dtype)) + for m in masks + if m is not None and m is not False + ] + if not masks: + if out is None: + return False + else: + # Use copyto to deal with broadcasting with `where`. + np.copyto(out, False, where=where) + return out + + if len(masks) == 1: + if out is None: + return masks[0].copy() if copy else masks[0] + else: + np.copyto(out, masks[0], where=where) + return out + + result = np.logical_or(masks[0], masks[1], out=out, where=where) + for mask in masks[2:]: + result = np.logical_or(result, mask, out=out, where=where) + return result + + +class Masked(NDArrayShapeMethods): + """A scalar value or array of values with associated mask. + + The resulting instance will take its exact type from whatever the + contents are, with the type generated on the fly as needed. + + Parameters + ---------- + data : array-like + The data for which a mask is to be added. The result will be a + a subclass of the type of ``data``. + mask : array-like of bool, optional + The initial mask to assign. If not given, taken from the data. + If the data already has a mask, the masks are combined. + copy : bool + Whether the data and mask should be copied. Default: `False`. + + """ + + _base_classes = {} + """Explicitly defined masked classes keyed by their unmasked counterparts. + + For subclasses of these unmasked classes, masked counterparts can be generated. + """ + + _masked_classes = {} + """Masked classes keyed by their unmasked data counterparts.""" + + def __new__(cls, *args, **kwargs): + if cls is Masked: + # Initializing with Masked itself means we're in "factory mode". + if not kwargs and len(args) == 1 and isinstance(args[0], type): + # Create a new masked class. + return cls._get_masked_cls(args[0]) + else: + return cls._get_masked_instance(*args, **kwargs) + else: + # Otherwise we're a subclass and should just pass information on. + return super().__new__(cls, *args, **kwargs) + + def __init_subclass__(cls, base_cls=None, data_cls=None, **kwargs): + """Register a Masked subclass. + + Parameters + ---------- + base_cls : type, optional + If given, it is taken to mean that ``cls`` can be used as + a base for masked versions of all subclasses of ``base_cls``, + so it is registered as such in ``_base_classes``. + data_cls : type, optional + If given, ``cls`` should will be registered as the masked version of + ``data_cls``. Will set the private ``cls._data_cls`` attribute, + and auto-generate a docstring if not present already. + **kwargs + Passed on for possible further initialization by superclasses. + + """ + if base_cls is not None: + Masked._base_classes[base_cls] = cls + + if data_cls is not None: + cls._data_cls = data_cls + cls._masked_classes[data_cls] = cls + if cls.__doc__ is None: + cls.__doc__ = get__doc__(data_cls) + + super().__init_subclass__(**kwargs) + + # This base implementation just uses the class initializer. + # Subclasses can override this in case the class does not work + # with this signature, or to provide a faster implementation. + @classmethod + def from_unmasked(cls, data, mask=None, copy=None): + """Create an instance from unmasked data and a mask.""" + return cls(data, mask=mask, copy=copy) + + @classmethod + def _get_masked_instance(cls, data, mask=None, copy=None): + data, data_mask = get_data_and_mask(data) + if mask is None: + mask = False if data_mask is None else data_mask + elif data_mask is not None: + mask = mask | data_mask + + masked_cls = cls._get_masked_cls(data.__class__) + return masked_cls.from_unmasked(data, mask, copy) + + @classmethod + def _get_masked_cls(cls, data_cls): + """Get the masked wrapper for a given data class. + + If the data class does not exist yet but is a subclass of any of the + registered base data classes, it is automatically generated + (except we skip `~numpy.ma.MaskedArray` subclasses, since then the + masking mechanisms would interfere). + """ + if issubclass(data_cls, (Masked, np.ma.MaskedArray)): + return data_cls + + masked_cls = cls._masked_classes.get(data_cls) + if masked_cls is None: + # Walk through MRO and find closest base data class. + # Note: right now, will basically always be ndarray, but + # one could imagine needing some special care for one subclass, + # which would then get its own entry. E.g., if MaskedAngle + # defined something special, then MaskedLongitude should depend + # on it. + for mro_item in data_cls.__mro__: + base_cls = cls._base_classes.get(mro_item) + if base_cls is not None: + break + else: + # Just hope that MaskedNDArray can handle it. + # TODO: this covers the case where a user puts in a list or so, + # but for those one could just explicitly do something like + # _masked_classes[list] = MaskedNDArray. + return MaskedNDArray + + # Create (and therefore register) new Masked subclass for the + # given data_cls. + masked_cls = type( + "Masked" + data_cls.__name__, + (data_cls, base_cls), + {}, + data_cls=data_cls, + ) + + return masked_cls + + def _get_mask(self): + """The mask. + + If set, replace the original mask, with whatever it is set with, + using a view if no broadcasting or type conversion is required. + """ + return self._mask + + def _set_mask(self, mask, copy=False): + self_dtype = getattr(self, "dtype", None) + mask_dtype = ( + np.ma.make_mask_descr(self_dtype) + if self_dtype and self_dtype.names + else np.dtype("?") + ) + ma = np.asanyarray(mask, dtype=mask_dtype) + if ma.shape != self.shape: + # This will fail (correctly) if not broadcastable. + self._mask = np.empty(self.shape, dtype=mask_dtype) + self._mask[...] = ma + elif ma is mask: + # Even if not copying use a view so that shape setting + # does not propagate. + self._mask = mask.copy() if copy else mask.view() + else: + self._mask = ma + + mask = property(_get_mask, _set_mask) + + # Note: subclass should generally override the unmasked property. + # This one assumes the unmasked data is stored in a private attribute. + @property + def unmasked(self): + """The unmasked values. + + See Also + -------- + astropy.utils.masked.Masked.filled + """ + return self._unmasked + + def filled(self, fill_value): + """Get a copy of the underlying data, with masked values filled in. + + Parameters + ---------- + fill_value : object + Value to replace masked values with. + + See Also + -------- + astropy.utils.masked.Masked.unmasked + """ + unmasked = self.unmasked.copy() + if self.mask.dtype.names: + np.ma.core._recursive_filled(unmasked, self.mask, fill_value) + else: + unmasked[self.mask] = fill_value + + return unmasked + + def _apply(self, method, *args, **kwargs): + # Required method for NDArrayShapeMethods, to help provide __getitem__ + # and shape-changing methods. + if callable(method): + data = method(self.unmasked, *args, **kwargs) + mask = method(self.mask, *args, **kwargs) + else: + data = getattr(self.unmasked, method)(*args, **kwargs) + mask = getattr(self.mask, method)(*args, **kwargs) + + result = self.from_unmasked(data, mask, copy=None) + if "info" in self.__dict__: + result.info = self.info + + return result + + def __setitem__(self, item, value): + if value is np.ma.masked or value is np.ma.nomask: + mask = value is np.ma.masked + else: + unmasked, mask = get_data_and_mask(value) + self.unmasked[item] = unmasked + self.mask[item] = mask + + +class MaskableShapedLikeNDArray(ShapedLikeNDArray): + """Like ShapedLikeNDArray, but for classes that can work with masked data. + + Defines default unmasked property as well as a filled method, and inherits + private class methods that help deal with masked inputs. + + Any class using this must provide a masked property, which tells whether + the underlying data are Masked, as well as a mask property, which + generally should provide a read-only copy of the underlying mask. + + """ + + @property + @abc.abstractmethod + def masked(self): + """Whether or not the instance uses masked values.""" + + @property + @abc.abstractmethod + def mask(self): + """The mask.""" + + @property + def unmasked(self): + """Get an instance without the mask. + + Note that while one gets a new instance, the underlying data will be shared. + + See Also + -------- + filled : get a copy of the underlying data, with masked values filled in. + """ + return self._apply(lambda x: getattr(x, "unmasked", x)) + + def filled(self, fill_value): + """Get a copy of the underlying data, with masked values filled in. + + Parameters + ---------- + fill_value : object + Value to replace masked values with. + + Returns + ------- + filled : instance + Copy of ``self`` with masked items replaced by ``fill_value``. + + See Also + -------- + unmasked : get an instance without the mask. + """ + unmasked = self.unmasked.copy() + unmasked[self.mask] = fill_value + return unmasked + + +class MaskedInfoBase: + mask_val = np.ma.masked + + def __init__(self, bound=False): + super().__init__(bound) + + # If bound to a data object instance then create the dict of attributes + # which stores the info attribute values. + if bound: + # Specify how to serialize this object depending on context. + self.serialize_method = { + "fits": "null_value", + "ecsv": "null_value", + "hdf5": "data_mask", + "parquet": "data_mask", + None: "null_value", + } + + +class MaskedNDArrayInfo(MaskedInfoBase, ParentDtypeInfo): + """ + Container for meta information like name, description, format. + """ + + # Add `serialize_method` attribute to the attrs that MaskedNDArrayInfo knows + # about. This allows customization of the way that MaskedColumn objects + # get written to file depending on format. The default is to use whatever + # the writer would normally do, which in the case of FITS or ECSV is to use + # a NULL value within the data itself. If serialize_method is 'data_mask' + # then the mask is explicitly written out as a separate column if there + # are any masked values. This is the same as for MaskedColumn. + attr_names = ParentDtypeInfo.attr_names | {"serialize_method"} + + # When `serialize_method` is 'data_mask', and data and mask are being written + # as separate columns, use column names and .mask (instead + # of default encoding as .data and .mask). + _represent_as_dict_primary_data = "data" + + def _represent_as_dict(self): + out = super()._represent_as_dict() + + masked_array = self._parent + + # If the serialize method for this context (e.g. 'fits' or 'ecsv') is + # 'data_mask', that means to serialize using an explicit mask column. + method = self.serialize_method[self._serialize_context] + + if method == "data_mask": + out["data"] = masked_array.unmasked + + if np.any(masked_array.mask): + # Only if there are actually masked elements do we add the ``mask`` column + out["mask"] = masked_array.mask + + elif method == "null_value": + out["data"] = np.ma.MaskedArray( + masked_array.unmasked, mask=masked_array.mask + ) + + else: + raise ValueError( + 'serialize method must be either "data_mask" or "null_value"' + ) + + return out + + def _construct_from_dict(self, map): + # Override usual handling, since MaskedNDArray takes shape and buffer + # as input, which is less useful here. + # The map can contain either a MaskedColumn or a Column and a mask. + # Extract the mask for the former case. + map.setdefault("mask", getattr(map["data"], "mask", False)) + return self._parent_cls.from_unmasked(**map) + + +class MaskedArraySubclassInfo(MaskedInfoBase): + """Mixin class to create a subclasses such as MaskedQuantityInfo.""" + + # This is used below in __init_subclass__, which also inserts a + # 'serialize_method' attribute in attr_names. + + def _represent_as_dict(self): + # Use the data_cls as the class name for serialization, + # so that we do not have to store all possible masked classes + # in astropy.table.serialize.__construct_mixin_classes. + out = super()._represent_as_dict() + data_cls = self._parent._data_cls + out.setdefault("__class__", data_cls.__module__ + "." + data_cls.__name__) + return out + + +def _comparison_method(op): + """ + Create a comparison operator for MaskedNDArray. + + Needed since for string dtypes the base operators bypass __array_ufunc__ + and hence return unmasked results. + """ + + def _compare(self, other): + other_data, other_mask = get_data_and_mask(other) + result = getattr(self.unmasked, op)(other_data) + if result is NotImplemented: + return NotImplemented + mask = self.mask | (other_mask if other_mask is not None else False) + return self._masked_result(result, mask, None) + + return _compare + + +class MaskedIterator: + """ + Flat iterator object to iterate over Masked Arrays. + + A `~astropy.utils.masked.MaskedIterator` iterator is returned by ``m.flat`` + for any masked array ``m``. It allows iterating over the array as if it + were a 1-D array, either in a for-loop or by calling its `next` method. + + Iteration is done in C-contiguous style, with the last index varying the + fastest. The iterator can also be indexed using basic slicing or + advanced indexing. + + Notes + ----- + The design of `~astropy.utils.masked.MaskedIterator` follows that of + `~numpy.ma.core.MaskedIterator`. It is not exported by the + `~astropy.utils.masked` module. Instead of instantiating directly, + use the ``flat`` method in the masked array instance. + """ + + def __init__(self, m): + self._masked = m + self._dataiter = m.unmasked.flat + self._maskiter = m.mask.flat + + def __iter__(self): + return self + + def __getitem__(self, index): + out = self._dataiter.__getitem__(index) + mask = self._maskiter.__getitem__(index) + # For single elements, ndarray.flat.__getitem__ returns scalars; these + # need a new view as a Masked array. + if not isinstance(out, np.ndarray): + out = out[...] + mask = mask[...] + + return self._masked.from_unmasked(out, mask, copy=False) + + def __setitem__(self, index, value): + if value is np.ma.masked or value is np.ma.nomask: + mask = value is np.ma.masked + else: + unmasked, mask = get_data_and_mask(value) + self._dataiter[index] = unmasked + self._maskiter[index] = mask + + def __next__(self): + """ + Return the next value, or raise StopIteration. + """ + out = next(self._dataiter)[...] + mask = next(self._maskiter)[...] + return self._masked.from_unmasked(out, mask, copy=False) + + next = __next__ + + +class MaskedNDArray(Masked, np.ndarray, base_cls=np.ndarray, data_cls=np.ndarray): + _mask = None + + info = MaskedNDArrayInfo() + + def __new__(cls, *args, mask=None, **kwargs): + """Get data class instance from arguments and then set mask.""" + self = super().__new__(cls, *args, **kwargs) + if mask is not None: + self.mask = mask + elif self._mask is None: + self.mask = False + return self + + def __init_subclass__(cls, **kwargs): + super().__init_subclass__(cls, **kwargs) + # For all subclasses we should set a default __new__ that passes on + # arguments other than mask to the data class, and then sets the mask. + if "__new__" not in cls.__dict__: + + def __new__(newcls, *args, mask=None, **kwargs): + """Get data class instance from arguments and then set mask.""" + # Need to explicitly mention classes outside of class definition. + self = super(cls, newcls).__new__(newcls, *args, **kwargs) + if mask is not None: + self.mask = mask + elif self._mask is None: + self.mask = False + return self + + cls.__new__ = __new__ + + if "info" not in cls.__dict__ and hasattr(cls._data_cls, "info"): + data_info = cls._data_cls.info + attr_names = data_info.attr_names | {"serialize_method"} + # Ensure the new info class uses the class's module. + # Without this, the DataInfoMeta metaclass incorrectly sets + # __module__ to its own. + new_info = type( + cls.__name__ + "Info", + (MaskedArraySubclassInfo, data_info.__class__), + dict(attr_names=attr_names, __module__=cls.__module__), + ) + cls.info = new_info() + + # The two pieces typically overridden. + @classmethod + def from_unmasked(cls, data, mask=None, copy=None): + # Note: have to override since __new__ would use ndarray.__new__ + # which expects the shape as its first argument, not an array. + data = np.array(data, subok=True, copy=copy) + self = data.view(cls) + self._set_mask(mask, copy=copy) + return self + + @property + def unmasked(self): + return super().view(self._data_cls) + + @classmethod + def _get_masked_cls(cls, data_cls): + # Short-cuts + if data_cls is np.ndarray: + return MaskedNDArray + elif data_cls is None: # for .view() + return cls + + return super()._get_masked_cls(data_cls) + + @property + def flat(self): + """A 1-D iterator over the Masked array. + + This returns a ``MaskedIterator`` instance, which behaves the same + as the `~numpy.flatiter` instance returned by `~numpy.ndarray.flat`, + and is similar to Python's built-in iterator, except that it also + allows assignment. + """ + return MaskedIterator(self) + + @property + def _baseclass(self): + """Work-around for MaskedArray initialization. + + Allows the base class to be inferred correctly when a masked instance + is used to initialize (or viewed as) a `~numpy.ma.MaskedArray`. + + """ + return self._data_cls + + def view(self, dtype=None, type=None): + """New view of the masked array. + + Like `numpy.ndarray.view`, but always returning a masked array subclass. + """ + if type is None and ( + isinstance(dtype, builtins.type) and issubclass(dtype, np.ndarray) + ): + return super().view(self._get_masked_cls(dtype)) + + if dtype is None: + return super().view(self._get_masked_cls(type)) + + dtype = np.dtype(dtype) + result = super().view(dtype, self._get_masked_cls(type)) + # Mask should be viewed in all but simplest case. + if ( + dtype.itemsize != self.dtype.itemsize + or dtype.names + or dtype.shape + or self.dtype.names + or self.dtype.shape + ): + try: + result.mask = self.mask.view(np.ma.make_mask_descr(dtype)) + except Exception as exc: + raise NotImplementedError( + f"{self.__class__} cannot be viewed with a dtype " + "with a different number of fields or size." + ) from None + + return result + + def __array_finalize__(self, obj): + # If we're a new object or viewing an ndarray, nothing has to be done. + if obj is None or obj.__class__ is np.ndarray: + return + + # Logically, this should come from ndarray and hence do nothing, but + # just in case someone creates a new mixin, we run it. + super().__array_finalize__(obj) + + if self._mask is None: + # Got here after, e.g., a view of another masked class. + # Get its mask, or initialize ours. + self._set_mask(getattr(obj, "_mask", False)) + + if "info" in obj.__dict__: + self.info = obj.info + + @property + def shape(self): + """The shape of the data and the mask. + + Usually used to get the current shape of an array, but may also be + used to reshape the array in-place by assigning a tuple of array + dimensions to it. As with `numpy.reshape`, one of the new shape + dimensions can be -1, in which case its value is inferred from the + size of the array and the remaining dimensions. + + Raises + ------ + AttributeError + If a copy is required, of either the data or the mask. + + """ + # Redefinition to allow defining a setter and add a docstring. + return super().shape + + @shape.setter + def shape(self, shape): + return self._set_shape(shape) + + def _set_shape(self, shape): + old_shape = self.shape + if NUMPY_LT_2_5: + self._mask.shape = shape + else: + self._mask._set_shape(shape) + # Reshape array proper in try/except just in case some broadcasting + # or so causes it to fail. + try: + if NUMPY_LT_2_5: + super(MaskedNDArray, type(self)).shape.__set__(self, shape) + else: + super()._set_shape(shape) + except Exception as exc: + if NUMPY_LT_2_5: + self._mask.shape = old_shape + else: + self._mask._set_shape(old_shape) + # Given that the mask reshaping succeeded, the only logical + # reason for an exception is something like a broadcast error in + # in __array_finalize__, or a different memory ordering between + # mask and data. For those, give a more useful error message; + # otherwise just raise the error. + if "could not broadcast" in exc.args[0]: + raise AttributeError( + "Incompatible shape for in-place modification. " + "Use `.reshape()` to make a copy with the desired " + "shape." + ) from None + else: # pragma: no cover + raise + + _eq_simple = _comparison_method("__eq__") + _ne_simple = _comparison_method("__ne__") + __lt__ = _comparison_method("__lt__") + __le__ = _comparison_method("__le__") + __gt__ = _comparison_method("__gt__") + __ge__ = _comparison_method("__ge__") + + def __eq__(self, other): + if not self.dtype.names: + return self._eq_simple(other) + + # For structured arrays, we treat this as a reduction over the fields, + # where masked fields are skipped and thus do not influence the result. + other = np.asanyarray(other, dtype=self.dtype) + result = np.stack( + [self[field] == other[field] for field in self.dtype.names], axis=-1 + ) + return result.all(axis=-1) + + def __ne__(self, other): + if not self.dtype.names: + return self._ne_simple(other) + + # For structured arrays, we treat this as a reduction over the fields, + # where masked fields are skipped and thus do not influence the result. + other = np.asanyarray(other, dtype=self.dtype) + result = np.stack( + [self[field] != other[field] for field in self.dtype.names], axis=-1 + ) + return result.any(axis=-1) + + @staticmethod + def _get_data_and_masks(arrays): + """Extracts the data and masks from the given arrays. + + Parameters + ---------- + arrays : iterable of array + An iterable of arrays, possibly masked. + + Returns + ------- + datas, masks: tuple of array + Extracted data and mask arrays. For any input array without + a mask, the corresponding entry in ``masks`` is `None`. + """ + data_masks = [get_data_and_mask(array) for array in arrays] + return tuple(zip(*data_masks)) + + def __array_ufunc__(self, ufunc, method, *inputs, **kwargs): + # Get inputs and there masks. + unmasked, masks = self._get_data_and_masks(inputs) + + # Deal with possible outputs and their masks. + out = kwargs.get("out") + out_mask = None + if out is None: + out_masks = [None] * ufunc.nout + else: + out_unmasked, out_masks = self._get_data_and_masks(out) + kwargs["out"] = out_unmasked + for d, m in zip(out_unmasked, out_masks): + if m is None: + # TODO: allow writing to unmasked output if nothing is masked? + if d is not None: + raise TypeError("cannot write to unmasked output") + elif out_mask is None: + out_mask = m + + # TODO: where is only needed for __call__ and reduce; + # this is very fast, but still worth separating out? + where = kwargs.get("where", True) + if where is True: + where_unmasked = True + where_mask = None + else: + where_unmasked, where_mask = get_data_and_mask(where) + kwargs["where"] = where_unmasked + + # First calculate the unmasked result. This will also verify kwargs. + # It will raise if the arguments do not know how to deal with each other. + result = getattr(ufunc, method)(*unmasked, **kwargs) + + if ufunc.signature: + # We're dealing with a gufunc. For now, only deal with + # np.matmul and gufuncs for which the mask of any output always + # depends on all core dimension values of all inputs. + # TODO: in principle, it should be possible to generate the mask + # purely based on the signature. + if ufunc is np.matmul: + # np.matmul is tricky and its signature cannot be parsed by + # _parse_gufunc_signature. But we can calculate the mask + # with matmul by using that nan will propagate correctly. + # We use float16 to minimize the memory requirements. + nan_masks = [] + for a, m in zip(unmasked, masks): + nan_mask = np.zeros(a.shape, dtype=np.float16) + if m is not None: + nan_mask[m] = np.nan + nan_masks.append(nan_mask) + m_kwargs = { + k: v for k, v in kwargs.items() if k not in ("out", "where") + } + t = ufunc(*nan_masks, **m_kwargs) + mask = np.isnan(t, out=out_mask) + + else: + # Parse signature with private numpy function. Note it + # cannot handle spaces in tuples, so remove those. + in_sig, out_sig = np.lib._function_base_impl._parse_gufunc_signature( + ufunc.signature.replace(" ", "") + ) + axes = kwargs.get("axes") + if axes is None: + # Maybe axis was given? (Note: ufunc will not take both.) + axes = [kwargs.get("axis")] * ufunc.nargs + elif len(axes) < ufunc.nargs: + # All outputs have no core dimensions, which means axes + # is not needed, but add None's for the zip below. + axes = axes + [None] * (ufunc.nargs - len(axes)) # not inplace! + keepdims = kwargs.get("keepdims", False) + in_masks = [] + for sig, mask, axis in zip(in_sig, masks, axes[: ufunc.nin]): + if mask is not None: + if sig: + if axis is None: + axis = tuple(range(-1, -1 - len(sig), -1)) + # Input has core dimensions. Assume that if any + # value in those is masked, the output will be + # masked too (TODO: for multiple core dimensions + # this may be too strong). + mask = np.logical_or.reduce( + mask, axis=axis, keepdims=keepdims + ) + in_masks.append(mask) + + if ufunc.nout == 1 and out_sig[0] == (): + # Special-case where possible in-place is easy. + mask = combine_masks(in_masks, out=out_mask, copy=False) + else: + # Here, some masks may need expansion, so we forego in-place. + mask = combine_masks(in_masks, copy=False) + result_masks = [] + for os, omask, axis in zip(out_sig, out_masks, axes[ufunc.nin :]): + if os: + # Output has core dimensions. Assume all those + # get the same mask. + if axis is None: + axis = tuple(range(-1, -1 - len(os), -1)) + result_mask = np.expand_dims(mask, axis) + else: + result_mask = mask + if omask is not None: + omask[...] = result_mask + result_masks.append(result_mask) + + mask = result_masks if ufunc.nout > 1 else result_masks[0] + + elif method == "__call__": + # Regular ufunc call. + # Combine the masks from the input, possibly selecting elements. + mask = combine_masks(masks, out=out_mask, where=where_unmasked) + # If relevant, also mask output elements for which where was masked. + if where_mask is not None: + mask |= where_mask + if out_mask is not None: + # Check for any additional explicitly given outputs. + for m in out_masks[1:]: + if m is not None and m is not out_mask: + m[...] = mask + + elif method == "outer": + # Must have two inputs and one output, so also only one output mask. + # Adjust masks as will be done for data. + m0, m1 = masks + if m0 is not None and m0.ndim > 0: + m0 = m0[(...,) + (np.newaxis,) * np.ndim(unmasked[1])] + mask = combine_masks((m0, m1), out=out_mask) + + elif method in {"reduce", "accumulate"}: + # Reductions like np.add.reduce (sum). + # Treat any masked where as if the input element was masked. + mask = combine_masks((masks[0], where_mask), copy=False) + if mask is False and out_mask is not None: + if where_unmasked is True: + out_mask[...] = False + else: + # This is too complicated, just fall through to below. + mask = np.broadcast_to(False, inputs[0].shape) + + if mask is not False: + # By default, we simply propagate masks, since for + # things like np.sum, it makes no sense to do otherwise. + # Individual methods need to override as needed. + if method == "reduce": + axis = kwargs.get("axis") + keepdims = kwargs.get("keepdims", False) + mask = np.logical_or.reduce( + mask, + where=where_unmasked, + axis=axis, + keepdims=keepdims, + out=out_mask, + ) + if where_unmasked is not True: + # Mask also whole rows in which no elements were selected; + # those will have been left as unmasked above. + mask |= ~np.logical_or.reduce( + where_unmasked, axis=axis, keepdims=keepdims + ) + + else: + # Accumulate + axis = kwargs.get("axis", 0) + mask = np.logical_or.accumulate(mask, axis=axis, out=out_mask) + + elif method in {"reduceat", "at"}: # pragma: no cover + raise NotImplementedError( + "masked instances cannot yet deal with 'reduceat' or 'at'." + ) + + if result is None: # pragma: no cover + # This happens for the "at" method. + return result + + if out is not None and ufunc.nout == 1: + out = out[0] + return self._masked_result(result, mask, out) + + def __array_function__(self, function, types, args, kwargs): + # TODO: go through functions systematically to see which ones + # work and/or can be supported. + if function in MASKED_SAFE_FUNCTIONS: + return super().__array_function__(function, types, args, kwargs) + + elif function in APPLY_TO_BOTH_FUNCTIONS: + helper = APPLY_TO_BOTH_FUNCTIONS[function] + try: + helper_result = helper(*args, **kwargs) + except NotImplementedError: + return self._not_implemented_or_raise(function, types) + + data_args, mask_args, kwargs, out = helper_result + if out is not None: + if not isinstance(out, Masked): + return self._not_implemented_or_raise(function, types) + function(*mask_args, out=out.mask, **kwargs) + function(*data_args, out=out.unmasked, **kwargs) + return out + + mask = function(*mask_args, **kwargs) + result = function(*data_args, **kwargs) + + elif function in DISPATCHED_FUNCTIONS: + dispatched_function = DISPATCHED_FUNCTIONS[function] + try: + dispatched_result = dispatched_function(*args, **kwargs) + except NotImplementedError: + return self._not_implemented_or_raise(function, types) + + if dispatched_result is None: + return None + + result, mask, out = dispatched_result + + elif function in UNSUPPORTED_FUNCTIONS: + return NotImplemented + + else: # pragma: no cover + # By default, just pass it through for now. + return super().__array_function__(function, types, args, kwargs) + + if mask is None: + return result + else: + return self._masked_result(result, mask, out) + + def _not_implemented_or_raise(self, function, types): + # Our function helper or dispatcher found that the function does not + # work with Masked. In principle, there may be another class that + # knows what to do with us, for which we should return NotImplemented. + # But if there is ndarray (or a non-Masked subclass of it) around, + # it quite likely coerces, so we should just break. + if any(issubclass(t, np.ndarray) and not issubclass(t, Masked) for t in types): + raise TypeError( + f"the MaskedNDArray implementation cannot handle {function} " + "with the given arguments." + ) from None + else: + return NotImplemented + + def _masked_result(self, result, mask, out): + if isinstance(result, tuple): + if out is None: + out = (None,) * len(result) + if not isinstance(mask, (list, tuple)): + mask = (mask,) * len(result) + return tuple( + self._masked_result(result_, mask_, out_) + for (result_, mask_, out_) in zip(result, mask, out) + ) + + if out is None: + # Note that we cannot count on result being the same class as + # 'self' (e.g., comparison of quantity results in an ndarray, most + # operations on Longitude and Latitude result in Angle or + # Quantity), so use Masked to determine the appropriate class. + return Masked(result, mask) + + # TODO: remove this sanity check once test cases are more complete. + assert isinstance(out, Masked) + # For inplace, the mask will have been set already. + return out + + def __array_wrap__(self, obj, context=None, return_scalar=False): + if context is None: + # Functions like np.ediff1d call __array_wrap__ to turn the array + # into self's subclass. + return self.from_unmasked(*get_data_and_mask(obj)) + + raise NotImplementedError( + "__array_wrap__ should not be used with a context any more since all use " + "should go through array_function. Please raise an issue on " + "https://github.com/astropy/astropy" + ) + + # Below are ndarray methods that need to be overridden as masked elements + # need to be skipped and/or an initial value needs to be set. + def _reduce_defaults(self, kwargs, initial_func=None): + """Get default where and initial for masked reductions. + + Generally, the default should be to skip all masked elements. For + reductions such as np.minimum.reduce, we also need an initial value, + which can be determined using ``initial_func``. + + """ + if "where" not in kwargs: + kwargs["where"] = ~self.mask + if initial_func is not None and "initial" not in kwargs: + kwargs["initial"] = initial_func(self.unmasked) + return kwargs + + def trace(self, offset=0, axis1=0, axis2=1, dtype=None, out=None): + # Unfortunately, cannot override the call to diagonal inside trace, so + # duplicate implementation in numpy/core/src/multiarray/calculation.c. + diagonal = self.diagonal(offset=offset, axis1=axis1, axis2=axis2) + return diagonal.sum(-1, dtype=dtype, out=out) + + def min(self, axis=None, out=None, **kwargs): + return super().min( + axis=axis, out=out, **self._reduce_defaults(kwargs, np.nanmax) + ) + + def max(self, axis=None, out=None, **kwargs): + return super().max( + axis=axis, out=out, **self._reduce_defaults(kwargs, np.nanmin) + ) + + def ptp(self, axis=None, out=None, **kwargs): + result = self.max(axis=axis, out=out, **kwargs) + result -= self.min(axis=axis, **kwargs) + return result + + def nonzero(self): + unmasked_nonzero = self.unmasked.nonzero() + if self.ndim >= 1: + not_masked = ~self.mask[unmasked_nonzero] + return tuple(u[not_masked] for u in unmasked_nonzero) + else: + return unmasked_nonzero if not self.mask else np.nonzero([0]) + + def compress(self, condition, axis=None, out=None): + if out is not None: + raise NotImplementedError("cannot yet give output") + return self._apply("compress", condition, axis=axis) + + def repeat(self, repeats, axis=None): + return self._apply("repeat", repeats, axis=axis) + + def choose(self, choices, out=None, mode="raise"): + # Let __array_function__ take care since choices can be masked too. + return np.choose(self, choices, out=out, mode=mode) + + def argmin(self, axis=None, out=None, *, keepdims=False): + # TODO: should this return a masked integer array, with masks + # if all elements were masked? + at_min = self == self.min(axis=axis, keepdims=True) + return at_min.filled(False).argmax(axis=axis, out=out, keepdims=keepdims) + + def argmax(self, axis=None, out=None, *, keepdims=False): + at_max = self == self.max(axis=axis, keepdims=True) + return at_max.filled(False).argmax(axis=axis, out=out, keepdims=keepdims) + + def argsort(self, axis=-1, kind=None, order=None, *, stable=None): + """Returns the indices that would sort an array. + + Perform an indirect sort along the given axis on both the array + and the mask, with masked items being sorted to the end. + + Parameters + ---------- + axis : int or None, optional + Axis along which to sort. The default is -1 (the last axis). + If None, the flattened array is used. + kind : str or None, ignored. + The kind of sort. Present only to allow subclasses to work. + order : str or list of str. + For an array with fields defined, the fields to compare first, + second, etc. A single field can be specified as a string, and not + all fields need be specified, but unspecified fields will still be + used, in dtype order, to break ties. + stable: bool, keyword-only, ignored + Sort stability. Present only to allow subclasses to work. + + Returns + ------- + index_array : ndarray, int + Array of indices that sorts along the specified ``axis``. Use + ``np.take_along_axis(self, index_array, axis=axis)`` to obtain + the sorted array. + + """ + if axis is None: + data = self.ravel() + axis = -1 + else: + data = self + + if self.dtype.names: + # As done inside the argsort implementation in multiarray/methods.c. + if order is None: + order = self.dtype.names + else: + order = np._core._internal._newnames(self.dtype, order) + + keys = tuple(data[name] for name in order[::-1]) + + elif order is not None: + raise ValueError("Cannot specify order when the array has no fields.") + + else: + keys = (data,) + + return np.lexsort(keys, axis=axis) + + def sort(self, axis=-1, kind=None, order=None, *, stable=False): + """Sort an array in-place. Refer to `numpy.sort` for full documentation. + + Notes + ----- + Masked items will be sorted to the end. The implementation + is via `numpy.lexsort` and thus ignores the ``kind`` and ``stable`` arguments; + they are present only so that subclasses can pass them on. + """ + # TODO: probably possible to do this faster than going through argsort! + argsort_kwargs = dict(kind=kind, order=order, stable=stable) + indices = self.argsort(axis, **argsort_kwargs) + self[:] = np.take_along_axis(self, indices, axis=axis) + + def argpartition(self, kth, axis=-1, kind="introselect", order=None): + # TODO: should be possible to do this faster than with a full argsort! + return self.argsort(axis=axis, order=order) + + def partition(self, kth, axis=-1, kind="introselect", order=None): + # TODO: should be possible to do this faster than with a full argsort! + return self.sort(axis=axis, order=None) + + def cumsum(self, axis=None, dtype=None, out=None): + if axis is None: + self = self.ravel() + axis = 0 + return np.add.accumulate(self, axis=axis, dtype=dtype, out=out) + + def cumprod(self, axis=None, dtype=None, out=None): + if axis is None: + self = self.ravel() + axis = 0 + return np.multiply.accumulate(self, axis=axis, dtype=dtype, out=out) + + def clip(self, min=None, max=None, out=None, **kwargs): + """Return an array whose values are limited to ``[min, max]``. + + Like `~numpy.clip`, but any masked values in ``min`` and ``max`` + are ignored for clipping. The mask of the input array is propagated. + """ + # TODO: implement this at the ufunc level. + dmin, mmin = get_data_and_mask(min) + dmax, mmax = get_data_and_mask(max) + if mmin is None and mmax is None: + # Fast path for unmasked max, min. + return super().clip(min, max, out=out, **kwargs) + + masked_out = np.positive(self, out=out) + out = masked_out.unmasked + if dmin is not None: + np.maximum(out, dmin, out=out, where=True if mmin is None else ~mmin) + if dmax is not None: + np.minimum(out, dmax, out=out, where=True if mmax is None else ~mmax) + return masked_out + + def mean(self, axis=None, dtype=None, out=None, keepdims=False, *, where=True): + # Implementation based on that in numpy/core/_methods.py + # Cast bool, unsigned int, and int to float64 by default, + # and do float16 at higher precision. + is_float16_result = False + if dtype is None: + if issubclass(self.dtype.type, (np.integer, np.bool_)): + dtype = np.dtype("f8") + elif issubclass(self.dtype.type, np.float16): + dtype = np.dtype("f4") + is_float16_result = out is None + + where = ~self.mask & where + + result = self.sum( + axis=axis, dtype=dtype, out=out, keepdims=keepdims, where=where + ) + n = np.add.reduce(where, axis=axis, keepdims=keepdims) + + # catch the case when an axis is fully masked to prevent div by zero: + neq0 = n == 0 + n += neq0 + result /= n + + # correct fully-masked slice results to what is expected for 0/0 division + result.unmasked[neq0] = np.nan + + if is_float16_result: + result = result.astype(self.dtype) + return result + + def var( + self, axis=None, dtype=None, out=None, ddof=0, keepdims=False, *, where=True + ): + where_final = ~self.mask & where + + # Simplified implementation based on that in numpy/core/_methods.py + n = np.add.reduce(where_final, axis=axis, keepdims=keepdims)[...] + + # Cast bool, unsigned int, and int to float64 by default. + if dtype is None and issubclass(self.dtype.type, (np.integer, np.bool_)): + dtype = np.dtype("f8") + mean = self.mean(axis=axis, dtype=dtype, keepdims=True, where=where) + + x = self - mean + x *= x.conjugate() # Conjugate just returns x if not complex. + + result = x.sum( + axis=axis, dtype=dtype, out=out, keepdims=keepdims, where=where_final + ) + n -= ddof + n = np.maximum(n, 0, out=n) + result /= n + result._mask |= n == 0 + return result + + def std( + self, axis=None, dtype=None, out=None, ddof=0, keepdims=False, *, where=True + ): + result = self.var( + axis=axis, dtype=dtype, out=out, ddof=ddof, keepdims=keepdims, where=where + ) + return np.sqrt(result, out=result) + + def __bool__(self): + # First get result from array itself; this will error if not a scalar. + result = super().__bool__() + return result and not self.mask + + def any(self, axis=None, out=None, keepdims=False, *, where=True): + return np.logical_or.reduce( + self, axis=axis, out=out, keepdims=keepdims, where=~self.mask & where + ) + + def all(self, axis=None, out=None, keepdims=False, *, where=True): + return np.logical_and.reduce( + self, axis=axis, out=out, keepdims=keepdims, where=~self.mask & where + ) + + # Following overrides needed since somehow the ndarray implementation + # does not actually call these. + def __str__(self): + return np.array_str(self) + + def __repr__(self): + return np.array_repr(self) + + def __format__(self, format_spec): + string = super().__format__(format_spec) + if self.shape == () and self.mask: + n = min(3, max(1, len(string))) + return " " * (len(string) - n) + "\u2014" * n + else: + return string + + def __hash__(self): + # Try to be somewhat like a numpy array scalar if possible. + if self.ndim == 0 and not self.mask: + return hash(self.unmasked[()]) + + # Will raise regular ndarray error. + return hash((self.unmasked, self.mask)) + + +class MaskedRecarrayInfo(MaskedNDArrayInfo): + # Ensure that we output a plain MaskedArray, not a masked_recarray. + def _represent_as_dict(self): + masked_ndarray = self._parent.view(np.ndarray) + return masked_ndarray.info._represent_as_dict() + + +class MaskedRecarray(np.recarray, MaskedNDArray, data_cls=np.recarray): + # Explicit definition since we need to override some methods. + + info = MaskedRecarrayInfo() + + def __array_finalize__(self, obj): + # recarray.__array_finalize__ does not do super, so we do it + # explicitly. + super().__array_finalize__(obj) + super(np.recarray, self).__array_finalize__(obj) + + # __getattribute__, __setattr__, and field use these somewhat + # obscrure ndarray methods. TODO: override in MaskedNDArray? + def getfield(self, dtype, offset=0): + for field, info in self.dtype.fields.items(): + if offset == info[1] and dtype == info[0]: + return self[field] + + raise NotImplementedError("can only get existing field from structured dtype.") + + def setfield(self, val, dtype, offset=0): + for field, info in self.dtype.fields.items(): + if offset == info[1] and dtype == info[0]: + self[field] = val + return + + raise NotImplementedError("can only set existing field from structured dtype.") + + def __repr__(self): + cls_name = type(self).__name__ + out = super().__repr__().splitlines() + prefix, _, rest = out[0].partition("(") + out0 = cls_name + "(" + rest + extra_space = (len(cls_name) - len(prefix)) * " " + return "\n".join([out0] + [extra_space + o for o in out[1:]]) + + +def __getattr__(key): + """Make commonly used Masked subclasses and their info classes importable for ASDF support. + + Registered types associated with ASDF converters must be importable by + their fully qualified name. Masked classes are dynamically created and have + apparent names like ``astropy.utils.masked.core.MaskedQuantity`` although + they aren't actually attributes of this module. Customize module attribute + lookup so that certain commonly used Masked classes are importable. The same + is done for their dynamically created info classes. + + See: + - https://asdf.readthedocs.io/en/latest/asdf/extending/converters.html#entry-point-performance-considerations + - https://github.com/astropy/asdf-astropy/pull/253 + """ + if key.startswith(Masked.__name__): + # TODO: avoid using a private attribute from table. + # Can we make this more beautiful? + from astropy.table.serialize import __construct_mixin_classes + + base_class_name = key[len(Masked.__name__) :] + for base_class_qualname in __construct_mixin_classes: + module, _, name = base_class_qualname.rpartition(".") + is_info = name + "Info" == base_class_name + if name == base_class_name or is_info: + base_class = getattr(importlib.import_module(module), name) + # Try creating the masked class + masked_class = Masked(base_class) + # But only return it if it is a standard one, not one + # where we just used the ndarray fallback. + if base_class in Masked._masked_classes: + return masked_class.info.__class__ if is_info else masked_class + + raise AttributeError(f"module '{__name__}' has no attribute '{key}'") diff --git a/astropy/utils/masked/function_helpers.py b/astropy/utils/masked/function_helpers.py new file mode 100644 index 000000000000..08e5ad856be7 --- /dev/null +++ b/astropy/utils/masked/function_helpers.py @@ -0,0 +1,1457 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +"""Helpers for letting numpy functions interact with Masked arrays. + +The module supplies helper routines for numpy functions that propagate +masks appropriately, for use in the ``__array_function__`` +implementation of `~astropy.utils.masked.MaskedNDArray`. They are not +very useful on their own, but the ones with docstrings are included in +the documentation so that there is a place to find out how the mask is +interpreted. + +""" + +import warnings + +import numpy as np +import numpy._core as np_core +from numpy.lib._function_base_impl import _quantile_is_valid, _ureduce + +from astropy.units.quantity_helper.function_helpers import FunctionAssigner +from astropy.utils.compat import NUMPY_LT_2_1, NUMPY_LT_2_2, NUMPY_LT_2_4, NUMPY_LT_2_5 + +# This module should not really be imported, but we define __all__ +# such that sphinx can typeset the functions with docstrings. +# The latter are added to __all__ at the end. +__all__ = [ + "APPLY_TO_BOTH_FUNCTIONS", + "DISPATCHED_FUNCTIONS", + "MASKED_SAFE_FUNCTIONS", + "UNSUPPORTED_FUNCTIONS", +] + + +MASKED_SAFE_FUNCTIONS = set() +"""Set of functions that work fine on Masked classes already. + +Most of these internally use `numpy.ufunc` or other functions that +are already covered. +""" + +APPLY_TO_BOTH_FUNCTIONS = {} +"""Dict of functions that should apply to both data and mask. + +The `dict` is keyed by the numpy function and the values are functions +that take the input arguments of the numpy function and organize these +for passing the data and mask to the numpy function. + +Returns +------- +data_args : tuple + Arguments to pass on to the numpy function for the unmasked data. +mask_args : tuple + Arguments to pass on to the numpy function for the masked data. +kwargs : dict + Keyword arguments to pass on for both unmasked data and mask. +out : `~astropy.utils.masked.Masked` instance or None + Optional instance in which to store the output. + +Raises +------ +NotImplementedError + When an arguments is masked when it should not be or vice versa. +""" + +DISPATCHED_FUNCTIONS = {} +"""Dict of functions that provide the numpy function's functionality. + +These are for more complicated versions where the numpy function itself +cannot easily be used. It should return either the result of the +function, or a tuple consisting of the unmasked result, the mask for the +result and a possible output instance. + +It should raise `NotImplementedError` if one of the arguments is masked +when it should not be or vice versa. +""" + +UNSUPPORTED_FUNCTIONS = set() +"""Set of numpy functions that are not supported for masked arrays. + +For most, masked input simply makes no sense, but for others it may have +been lack of time. Issues or PRs for support for functions are welcome. +""" + + +SUPPORTED_NEP35_FUNCTIONS = set() +"""Set of supported numpy functions with a 'like' keyword argument that dispatch +on it (NEP 35). +""" +if not NUMPY_LT_2_2: + # in numpy 2.2 these are auto detected by numpy itself + # xref https://github.com/numpy/numpy/issues/27451 + SUPPORTED_NEP35_FUNCTIONS |= set() + UNSUPPORTED_FUNCTIONS |= { + np.arange, + np.empty, np.ones, np.zeros, np.full, + np.array, np.asarray, np.asanyarray, np.ascontiguousarray, np.asfortranarray, + np.frombuffer, np.fromfile, np.fromfunction, np.fromiter, np.fromstring, + np.require, np.identity, np.eye, np.tri, np.genfromtxt, np.loadtxt, + } # fmt: skip + +# Almost all from np.core.fromnumeric defer to methods so are OK. +MASKED_SAFE_FUNCTIONS |= { + getattr(np, name) + for name in np_core.fromnumeric.__all__ + if name not in {"choose", "put", "resize", "searchsorted", "where", "alen", "ptp"} +} +MASKED_SAFE_FUNCTIONS |= { + # built-in from multiarray + np.may_share_memory, np.can_cast, np.min_scalar_type, np.result_type, + np.shares_memory, + # np.core.arrayprint + np.array_repr, + # np.core.function_base + np.linspace, np.logspace, np.geomspace, + # np.core.numeric + np.isclose, np.allclose, np.flatnonzero, np.argwhere, + # np.core.shape_base + np.atleast_1d, np.atleast_2d, np.atleast_3d, np.stack, np.hstack, np.vstack, + # np.lib._function_base_impl + np.average, np.diff, np.extract, np.meshgrid, np.gradient, + # np.lib.index_tricks + np.diag_indices_from, np.triu_indices_from, np.tril_indices_from, + np.fill_diagonal, + # np.lib.shape_base + np.column_stack, np.dstack, + np.array_split, np.split, np.hsplit, np.vsplit, np.dsplit, + np.expand_dims, np.apply_along_axis, np.kron, np.tile, + np.take_along_axis, np.put_along_axis, + # np.lib.type_check (all but nan_to_num) + np.iscomplexobj, np.isrealobj, np.imag, np.isreal, np.real, + np.real_if_close, np.common_type, + # np.lib.ufunclike + np.fix, np.isneginf, np.isposinf, + # np.lib._function_base_impl + np.angle, np.i0, + # np.lib._arraysetops_impl + np.intersect1d, np.setxor1d, np.union1d, np.unique, +} # fmt: skip + + +MASKED_SAFE_FUNCTIONS |= { + np.astype, np.trapezoid, + np.unique_all, np.unique_counts, np.unique_inverse, np.unique_values, +} # fmt: skip +if not NUMPY_LT_2_1: + MASKED_SAFE_FUNCTIONS |= { + np.unstack, + } # fmt: skip +IGNORED_FUNCTIONS = { + # I/O - useless for Masked, since no way to store the mask. + np.save, np.savez, np.savetxt, np.savez_compressed, + # Polynomials + np.poly, np.polyadd, np.polyder, np.polydiv, np.polyfit, np.polyint, + np.polymul, np.polysub, np.polyval, np.roots, np.vander, +} # fmt: skip +IGNORED_FUNCTIONS |= { + np.pad, np.searchsorted, np.digitize, + np.is_busday, np.busday_count, np.busday_offset, + # numpy.lib._function_base_impl + np.cov, np.corrcoef, np.trim_zeros, + # numpy.core.numeric + np.correlate, np.convolve, + # numpy.lib.histograms + np.histogram, np.histogram2d, np.histogramdd, np.histogram_bin_edges, + # TODO!! + np.dot, np.vdot, np.inner, np.tensordot, np.cross, + np.einsum, np.einsum_path, +} # fmt: skip + +# Explicitly unsupported functions +UNSUPPORTED_FUNCTIONS |= { + np.unravel_index, + np.ravel_multi_index, + np.ix_, +} + +# No support for the functions also not supported by Quantity +# (io, polynomial, etc.). +UNSUPPORTED_FUNCTIONS |= IGNORED_FUNCTIONS + + +apply_to_both = FunctionAssigner(APPLY_TO_BOTH_FUNCTIONS) +dispatched_function = FunctionAssigner(DISPATCHED_FUNCTIONS) + + +def _get_data_and_mask_array(array): + """Like get_data_and_mask, but always returns a mask array.""" + from .core import get_data_and_mask + + data, mask = get_data_and_mask(array) + if mask is None: + mask = np.zeros(np.shape(data), bool) + return data, mask + + +def _get_data_and_mask_arrays(arrays): + """Separate out arguments into tuples of data and masks. + + An all-False mask is created if an argument does not have a mask. + """ + return tuple(zip(*[_get_data_and_mask_array(array) for array in arrays])) + + +# Following are simple ufunc-like functions which should just copy the mask. +@dispatched_function +def datetime_as_string(arr, *args, **kwargs): + return (np.datetime_as_string(arr.unmasked, *args, **kwargs), arr.mask.copy(), None) + + +@dispatched_function +def sinc(x): + return np.sinc(x.unmasked), x.mask.copy(), None + + +@dispatched_function +def iscomplex(x): + return np.iscomplex(x.unmasked), x.mask.copy(), None + + +@dispatched_function +def unwrap(p, *args, **kwargs): + return np.unwrap(p.unmasked, *args, **kwargs), p.mask.copy(), None + + +@dispatched_function +def nan_to_num(x, copy=True, nan=0.0, posinf=None, neginf=None): + data = np.nan_to_num(x.unmasked, copy=copy, nan=nan, posinf=posinf, neginf=neginf) + if copy: + return (data, x.mask.copy(), None) + else: + return (x, None, None) + + +# Following are simple functions related to shapes, where the same function +# should be applied to the data and the mask. They cannot all share the +# same helper, because the first arguments have different names. +@apply_to_both(helps=({np.copy, np.resize, np.moveaxis, np.rollaxis, np.roll})) +def masked_a_helper(a, *args, **kwargs): + data, mask = _get_data_and_mask_array(a) + return (data,) + args, (mask,) + args, kwargs, None + + +@apply_to_both(helps={np.flip, np.flipud, np.fliplr, np.rot90, np.triu, np.tril}) +def masked_m_helper(m, *args, **kwargs): + data, mask = _get_data_and_mask_array(m) + return (data,) + args, (mask,) + args, kwargs, None + + +@apply_to_both(helps={np.diag, np.diagflat}) +def masked_v_helper(v, *args, **kwargs): + data, mask = _get_data_and_mask_array(v) + return (data,) + args, (mask,) + args, kwargs, None + + +@apply_to_both(helps={np.delete}) +def masked_arr_helper(array, *args, **kwargs): + data, mask = _get_data_and_mask_array(array) + return (data,) + args, (mask,) + args, kwargs, None + + +@apply_to_both +def broadcast_to(array, shape, subok=False): + """Broadcast array to the given shape. + + Like `numpy.broadcast_to`, and applied to both unmasked data and mask. + Note that ``subok`` is taken to mean whether or not subclasses of + the unmasked data and mask are allowed, i.e., for ``subok=False``, + a `~astropy.utils.masked.MaskedNDArray` will be returned. + """ + data, mask = _get_data_and_mask_array(array) + return (data,), (mask,), dict(shape=shape, subok=subok), None + + +@dispatched_function +def outer(a, b, out=None): + return np.multiply.outer(np.ravel(a), np.ravel(b), out=out), None, None + + +def empty_like_impl( + prototype, /, dtype=None, order="K", subok=True, shape=None, *, device=None +): + """Return a new array with the same shape and type as a given array. + + Like `numpy.empty_like`, but will add an empty mask. + """ + unmasked = np.empty_like( + prototype.unmasked, + dtype=dtype, + order=order, + subok=subok, + shape=shape, + device=device, + ) + if dtype is not None: + dtype = ( + np.ma.make_mask_descr(unmasked.dtype) + if unmasked.dtype.names + else np.dtype("?") + ) + mask = np.empty_like( + prototype.mask, + dtype=dtype, + order=order, + subok=subok, + shape=shape, + device=device, + ) + + return unmasked, mask, None + + +if not NUMPY_LT_2_4: + + @dispatched_function + def empty_like( + prototype, /, dtype=None, order="K", subok=True, shape=None, *, device=None + ): + return empty_like_impl( + prototype, + dtype=dtype, + order=order, + subok=subok, + shape=shape, + device=device, + ) +else: + + @dispatched_function + def empty_like( + prototype, dtype=None, order="K", subok=True, shape=None, *, device=None + ): + return empty_like_impl( + prototype, + dtype=dtype, + order=order, + subok=subok, + shape=shape, + device=device, + ) + + +@dispatched_function +def zeros_like(a, dtype=None, order="K", subok=True, shape=None, *, device=None): + """Return an array of zeros with the same shape and type as a given array. + + Like `numpy.zeros_like`, but will add an all-false mask. + """ + unmasked = np.zeros_like( + a.unmasked, + dtype=dtype, + order=order, + subok=subok, + shape=shape, + device=device, + ) + return unmasked, False, None + + +@dispatched_function +def ones_like(a, dtype=None, order="K", subok=True, shape=None, *, device=None): + """Return an array of ones with the same shape and type as a given array. + + Like `numpy.ones_like`, but will add an all-false mask. + """ + unmasked = np.ones_like( + a.unmasked, + dtype=dtype, + order=order, + subok=subok, + shape=shape, + device=device, + ) + return unmasked, False, None + + +@dispatched_function +def full_like( + a, fill_value, dtype=None, order="K", subok=True, shape=None, *, device=None +): + """Return a full array with the same shape and type as a given array. + + Like `numpy.full_like`, but with a mask that is also set. + If ``fill_value`` is `numpy.ma.masked`, the data will be left unset + (i.e., as created by `numpy.empty_like`). + """ + result = np.empty_like( + a, dtype=dtype, order=order, subok=subok, shape=shape, device=device + ) + result[...] = fill_value + return result, None, None + + +@dispatched_function +def put(a, ind, v, mode="raise"): + """Replaces specified elements of an array with given values. + + Like `numpy.put`, but for masked array ``a`` and possibly masked + value ``v``. Masked indices ``ind`` are not supported. + """ + from astropy.utils.masked import Masked, get_data_and_mask + + if isinstance(ind, Masked) or not isinstance(a, Masked): + raise NotImplementedError + + if v is np.ma.masked or v is np.ma.nomask: + v_mask = v is np.ma.masked + else: + v_data, v_mask = get_data_and_mask(v) + np.put(a.unmasked, ind, v_data, mode=mode) + # v_mask of None will be correctly interpreted as False. + np.put(a.mask, ind, v_mask, mode=mode) + + +def putmask_impl(a, /, mask, values): + """Changes elements of an array based on conditional and input values. + + Like `numpy.putmask`, but for masked array ``a`` and possibly masked + ``values``. Masked ``mask`` is not supported. + """ + from astropy.utils.masked import Masked, get_data_and_mask + + if isinstance(mask, Masked) or not isinstance(a, Masked): + raise NotImplementedError + + if values is np.ma.masked or values is np.ma.nomask: + values_mask = values is np.ma.masked + else: + values_data, values_mask = get_data_and_mask(values) + np.putmask(a.unmasked, mask, values_data) + np.putmask(a.mask, mask, values_mask) + + +if not NUMPY_LT_2_4: + + @dispatched_function + def putmask(a, /, mask, values): + return putmask_impl(a, mask=mask, values=values) +else: + + @dispatched_function + def putmask(a, mask, values): + return putmask_impl(a, mask=mask, values=values) + + +@dispatched_function +def place(arr, mask, vals): + """Change elements of an array based on conditional and input values. + + Like `numpy.place`, but for masked array ``a`` and possibly masked + ``values``. Masked ``mask`` is not supported. + """ + from astropy.utils.masked import Masked, get_data_and_mask + + if isinstance(mask, Masked) or not isinstance(arr, Masked): + raise NotImplementedError + + if vals is np.ma.masked or vals is np.ma.nomask: + vals_mask = vals is np.ma.masked + else: + vals_data, vals_mask = get_data_and_mask(vals) + np.place(arr.unmasked, mask, vals_data) + np.place(arr.mask, mask, vals_mask) + + +@dispatched_function +def copyto(dst, src, casting="same_kind", where=True): + """Copies values from one array to another, broadcasting as necessary. + + Like `numpy.copyto`, but for masked destination ``dst`` and possibly + masked source ``src``. + """ + from astropy.utils.masked import Masked, get_data_and_mask + + if not isinstance(dst, Masked) or isinstance(where, Masked): + raise NotImplementedError + + if src is np.ma.masked or src is np.ma.nomask: + src_mask = src is np.ma.masked + else: + src_data, src_mask = get_data_and_mask(src) + np.copyto(dst.unmasked, src_data, casting=casting, where=where) + np.copyto(dst.mask, src_mask, where=where) + + +@dispatched_function +def packbits(a, /, *args, **kwargs): + result = np.packbits(a.unmasked, *args, **kwargs) + mask = np.packbits(a.mask, *args, **kwargs).astype(bool) + return result, mask, None + + +@dispatched_function +def unpackbits(a, /, *args, **kwargs): + result = np.unpackbits(a.unmasked, *args, **kwargs) + mask = np.zeros(a.shape, dtype="u1") + mask[a.mask] = 255 + mask = np.unpackbits(mask, *args, **kwargs).astype(bool) + return result, mask, None + + +@dispatched_function +def bincount(x, /, weights=None, minlength=0): + """Count number of occurrences of each value in array of non-negative ints. + + Like `numpy.bincount`, but masked entries in ``x`` will be skipped. + Any masked entries in ``weights`` will lead the corresponding bin to + be masked. + """ + from astropy.utils.masked import Masked, get_data_and_mask + + if weights is not None: + weights = np.asanyarray(weights) + if isinstance(x, Masked) and x.ndim <= 1: + # let other dimensions lead to errors. + if weights is not None and weights.ndim == x.ndim: + weights = weights[~x.mask] + x = x.unmasked[~x.mask] + mask = None + if weights is not None: + weights, w_mask = get_data_and_mask(weights) + if w_mask is not None: + mask = np.bincount(x, w_mask.astype(int), minlength=minlength).astype(bool) + result = np.bincount(x, weights, minlength=0) + return result, mask, None + + +# Used to work via ptp method, but now need to override, otherwise +# plain reduction is used, which gives different mask. +@dispatched_function +def ptp(a, axis=None, out=None, keepdims=np._NoValue): + if keepdims is np._NoValue: + keepdims = False + result = a.max(axis=axis, out=out, keepdims=keepdims) + result -= a.min(axis=axis, keepdims=keepdims) + return result, None, None + + +@dispatched_function +def sort_complex(a): + # Just a copy of np.lib._function_base_impl.sort_complex, to avoid the asarray. + b = a.copy() + b.sort() + if not issubclass(b.dtype.type, np.complexfloating): # pragma: no cover + if b.dtype.char in "bhBH": + result = b.astype("F") + elif b.dtype.char == "g": + result = b.astype("G") + else: + result = b.astype("D") + else: + result = b + + return result, None, None + + +def concatenate_impl(arrays, /, axis=0, out=None, *, dtype=None, casting="same_kind"): + data, masks = _get_data_and_mask_arrays(arrays) + if out is None: + return ( + np.concatenate(data, axis=axis, dtype=dtype, casting=casting), + np.concatenate(masks, axis=axis), + None, + ) + else: + from astropy.utils.masked import Masked + + if not isinstance(out, Masked): + raise NotImplementedError + np.concatenate(masks, out=out.mask, axis=axis) + np.concatenate(data, out=out.unmasked, axis=axis, dtype=dtype, casting=casting) + return out, None, None + + +if NUMPY_LT_2_4: + + @dispatched_function + def concatenate(arrays, axis=0, out=None, dtype=None, casting="same_kind"): + return concatenate_impl( + arrays, axis=axis, out=out, dtype=dtype, casting=casting + ) +else: + + @dispatched_function + def concatenate(arrays, /, axis=0, out=None, *, dtype=None, casting="same_kind"): + return concatenate_impl( + arrays, axis=axis, out=out, dtype=dtype, casting=casting + ) + + +@apply_to_both +def append(arr, values, axis=None): + data, masks = _get_data_and_mask_arrays((arr, values)) + return data, masks, dict(axis=axis), None + + +@dispatched_function +def block(arrays): + # We need to override block since the numpy implementation can take two + # different paths, one for concatenation, one for creating a large empty + # result array in which parts are set. Each assumes array input and + # cannot be used directly. Since it would be very costly to inspect all + # arrays and then turn them back into a nested list, we just copy here the + # second implementation, np.core.shape_base._block_slicing, since it is + # shortest and easiest. + from astropy.utils.masked import Masked + + arrays, list_ndim, result_ndim, final_size = np_core.shape_base._block_setup(arrays) + shape, slices, arrays = np_core.shape_base._block_info_recursion( + arrays, list_ndim, result_ndim + ) + dtype = np.result_type(*[arr.dtype for arr in arrays]) + F_order = all(arr.flags["F_CONTIGUOUS"] for arr in arrays) + C_order = all(arr.flags["C_CONTIGUOUS"] for arr in arrays) + order = "F" if F_order and not C_order else "C" + result = Masked(np.empty(shape=shape, dtype=dtype, order=order)) + for the_slice, arr in zip(slices, arrays): + result[(Ellipsis,) + the_slice] = arr + return result, None, None + + +@dispatched_function +def broadcast_arrays(*args, subok=False): + """Broadcast arrays to a common shape. + + Like `numpy.broadcast_arrays`, applied to both unmasked data and masks. + Note that ``subok`` is taken to mean whether or not subclasses of + the unmasked data and masks are allowed, i.e., for ``subok=False``, + `~astropy.utils.masked.MaskedNDArray` instances will be returned. + """ + from .core import Masked + + are_masked = [isinstance(arg, Masked) for arg in args] + data = [ + (arg.unmasked if is_masked else arg) for arg, is_masked in zip(args, are_masked) + ] + results = np.broadcast_arrays(*data, subok=subok) + + shape = results[0].shape + masks = [ + (np.broadcast_to(arg.mask, shape, subok=subok) if is_masked else None) + for arg, is_masked in zip(args, are_masked) + ] + results = tuple( + (Masked(result, mask) if mask is not None else result) + for (result, mask) in zip(results, masks) + ) + return results, None, None + + +@apply_to_both +def insert(arr, obj, values, axis=None): + """Insert values along the given axis before the given indices. + + Like `numpy.insert` but for possibly masked ``arr`` and ``values``. + Masked ``obj`` is not supported. + """ + from astropy.utils.masked import Masked + + if isinstance(obj, Masked) or not isinstance(arr, Masked): + raise NotImplementedError + + arr_data, arr_mask = _get_data_and_mask_array(arr) + val_data, val_mask = _get_data_and_mask_array(values) + return (arr_data, obj, val_data, axis), (arr_mask, obj, val_mask, axis), {}, None + + +@dispatched_function +def count_nonzero(a, axis=None, *, keepdims=False): + """Counts the number of non-zero values in the array ``a``. + + Like `numpy.count_nonzero`, with masked values counted as 0 or `False`. + """ + filled = a.filled(np.zeros((), a.dtype)) + return np.count_nonzero(filled, axis, keepdims=keepdims), None, None + + +def _masked_median_1d(a, overwrite_input, keepdims): + # TODO: need an in-place mask-sorting option. + unmasked = a.unmasked[~a.mask] + if unmasked.size: + return a.from_unmasked( + np.median(unmasked, overwrite_input=overwrite_input, keepdims=keepdims) + ) + else: + return a.from_unmasked(np.zeros_like(a.unmasked, shape=(1,))[0], mask=True) + + +def _masked_median(a, axis=None, out=None, overwrite_input=False, keepdims=False): + # As for np.nanmedian, but without a fast option as yet. + if axis is None or a.ndim == 1: + part = a.ravel() + result = _masked_median_1d(part, overwrite_input, keepdims) + else: + result = np.apply_along_axis( + _masked_median_1d, axis, a, overwrite_input, keepdims + ) + if out is not None: + out[...] = result + return result + + +@dispatched_function +def median(a, axis=None, out=None, overwrite_input=False, keepdims=False): + from astropy.utils.masked import Masked + + if out is not None and not isinstance(out, Masked): + raise NotImplementedError + + result = _ureduce( + Masked(a), + func=_masked_median, + axis=axis, + out=out, + overwrite_input=overwrite_input, + keepdims=keepdims, + ) + return result, None, None + + +def _masked_quantile_1d(a, q, **kwargs): + """ + Private function for rank 1 arrays. Compute quantile ignoring NaNs. + See nanpercentile for parameter usage. + """ + unmasked = a.unmasked[~a.mask] + if unmasked.size: + result = np.lib._function_base_impl._quantile_unchecked(unmasked, q, **kwargs) + return a.from_unmasked(result) + else: + return a.from_unmasked(np.zeros_like(a.unmasked, shape=q.shape), True) + + +def _masked_quantile(a, q, axis=None, out=None, **kwargs): + # As for np.nanmedian, but without a fast option as yet. + if axis is None or a.ndim == 1: + part = a.ravel() + result = _masked_quantile_1d(part, q, **kwargs) + else: + result = np.apply_along_axis(_masked_quantile_1d, axis, a, q, **kwargs) + # apply_along_axis fills in collapsed axis with results. + # Move that axis to the beginning to match percentile's + # convention. + if q.ndim != 0: + result = np.moveaxis(result, axis, 0) + + if out is not None: + out[...] = result + return result + + +def _preprocess_quantile(a, q, axis=None, out=None, **kwargs): + from astropy.utils.masked import Masked + + if isinstance(q, Masked) or (out is not None and not isinstance(out, Masked)): + raise NotImplementedError + + a = Masked(a) + q = np.asanyarray(q) + if not _quantile_is_valid(q): + raise ValueError("Quantiles must be in the range [0, 1]") + + if (interpolation := kwargs.pop("interpolation", None)) is not None: + # we have to duplicate logic from np.quantile here to avoid + # passing down the 'interpolation' keyword argument, as it's not + # supported by np.lib._function_base_impl._quantile_unchecked + assert NUMPY_LT_2_4 # 'interpolation' kwarg was removed in numpy 2.4 + from numpy.lib._function_base_impl import _check_interpolation_as_method + + kwargs["method"] = _check_interpolation_as_method( + kwargs.get("method", "linear"), interpolation, "quantile" + ) + return a, q, axis, out, kwargs + + +if NUMPY_LT_2_4: + + @dispatched_function + def quantile( + a, + q, + axis=None, + out=None, + overwrite_input=False, + method="linear", + keepdims=False, + *, + weights=None, + interpolation=None, + ): + a, q, axis, out, kwargs = _preprocess_quantile( + a, + q, + axis, + out, + overwrite_input=overwrite_input, + method=method, + keepdims=keepdims, + weights=weights, + interpolation=interpolation, + ) + result = _ureduce(a, func=_masked_quantile, q=q, axis=axis, out=out, **kwargs) + return result, None, None + +else: + + @dispatched_function + def quantile( + a, + q, + axis=None, + out=None, + overwrite_input=False, + method="linear", + keepdims=False, + *, + weights=None, + ): + a, q, axis, out, kwargs = _preprocess_quantile( + a, + q, + axis, + out, + overwrite_input=overwrite_input, + method=method, + keepdims=keepdims, + weights=weights, + ) + result = _ureduce(a, func=_masked_quantile, q=q, axis=axis, out=out, **kwargs) + return result, None, None + + +@dispatched_function +def percentile(a, q, *args, **kwargs): + q = np.true_divide(q, 100) + return quantile(a, q, *args, **kwargs) + + +@dispatched_function +def array_equal(a1, a2, equal_nan=False): + a1d, a1m = _get_data_and_mask_array(a1) + a2d, a2m = _get_data_and_mask_array(a2) + if a1d.shape != a2d.shape: + return False, None, None + + equal = a1d == a2d + if equal_nan: + equal |= np.isnan(a1d) & np.isnan(a2d) + return bool((equal | a1m | a2m).all()), None, None + + +@dispatched_function +def array_equiv(a1, a2): + return bool((a1 == a2).all()), None, None + + +@dispatched_function +def where(condition, /, *args): + from astropy.utils.masked import Masked, get_data_and_mask + + if not args: + return condition.nonzero(), None, None + + condition, c_mask = get_data_and_mask(condition) + + data, masks = _get_data_and_mask_arrays(args) + unmasked = np.where(condition, *data) + mask = np.where(condition, *masks) + if c_mask is not None: + mask |= c_mask + return Masked(unmasked, mask=mask), None, None + + +@dispatched_function +def choose(a, choices, out=None, mode="raise"): + """Construct an array from an index array and a set of arrays to choose from. + + Like `numpy.choose`. Masked indices in ``a`` will lead to masked output + values and underlying data values are ignored if out of bounds (for + ``mode='raise'``). Any values masked in ``choices`` will be propagated + if chosen. + + """ + from astropy.utils.masked import Masked, get_data_and_mask + + a_data, a_mask = get_data_and_mask(a) + if a_mask is not None and mode == "raise": + # Avoid raising on masked indices. + a_data = a.filled(fill_value=0) + + kwargs = {"mode": mode} + if out is not None: + if not isinstance(out, Masked): + raise NotImplementedError + kwargs["out"] = out.unmasked + + data, masks = _get_data_and_mask_arrays(choices) + data_chosen = np.choose(a_data, data, **kwargs) + if out is not None: + kwargs["out"] = out.mask + + mask_chosen = np.choose(a_data, masks, **kwargs) + if a_mask is not None: + mask_chosen |= a_mask + + return Masked(data_chosen, mask_chosen) if out is None else out, None, None + + +@apply_to_both +def select(condlist, choicelist, default=0): + """Return an array drawn from elements in choicelist, depending on conditions. + + Like `numpy.select`, with masks in ``choicelist`` are propagated. + Any masks in ``condlist`` are ignored. + + """ + from astropy.utils.masked import Masked + + condlist = [c.unmasked if isinstance(c, Masked) else c for c in condlist] + + data_list, mask_list = _get_data_and_mask_arrays(choicelist) + default = Masked(default) if default is not np.ma.masked else Masked(0, mask=True) + return ( + (condlist, data_list, default.unmasked), + (condlist, mask_list, default.mask), + {}, + None, + ) + + +@dispatched_function +def piecewise(x, condlist, funclist, *args, **kw): + """Evaluate a piecewise-defined function. + + Like `numpy.piecewise` but for masked input array ``x``. + Any masks in ``condlist`` are ignored. + + """ + # Copied implementation from numpy.lib._function_base_impl.piecewise, + # just to ensure output is Masked. + n2 = len(funclist) + # undocumented: single condition is promoted to a list of one condition + if np.isscalar(condlist) or ( + not isinstance(condlist[0], (list, np.ndarray)) and x.ndim != 0 + ): # pragma: no cover + condlist = [condlist] + + condlist = np.array(condlist, dtype=bool) + n = len(condlist) + + if n == n2 - 1: # compute the "otherwise" condition. + condelse = ~np.any(condlist, axis=0, keepdims=True) + condlist = np.concatenate([condlist, condelse], axis=0) + n += 1 + elif n != n2: + raise ValueError( + f"with {n} condition(s), either {n} or {n + 1} functions are expected" + ) + + # The one real change... + y = np.zeros_like(x) + where = [] + what = [] + for k in range(n): + item = funclist[k] + if not callable(item): + where.append(condlist[k]) + what.append(item) + else: + vals = x[condlist[k]] + if vals.size > 0: + where.append(condlist[k]) + what.append(item(vals, *args, **kw)) + + for item, value in zip(where, what): + y[item] = value + + return y, None, None + + +@dispatched_function +def interp(x, xp, fp, *args, **kwargs): + """One-dimensional linear interpolation. + + Like `numpy.interp`, but any masked points in ``xp`` and ``fp`` + are ignored. Any masked values in ``x`` will still be evaluated, + but masked on output. + """ + from astropy.utils.masked import Masked, get_data_and_mask + + xd, xm = get_data_and_mask(x) + if isinstance(xp, Masked) or isinstance(fp, Masked): + xp, xpm = _get_data_and_mask_array(xp) + fp, fpm = _get_data_and_mask_array(fp) + if xp.ndim == fp.ndim == 1: + # Avoid making arrays 1-D; will just raise below. + m = xpm | fpm + xp = xp[~m] + fp = fp[~m] + + result = np.interp(xd, xp, fp, *args, **kwargs) + return (result if xm is None else Masked(result, xm.copy())), None, None + + +@dispatched_function +def lexsort(keys, axis=-1): + """Perform an indirect stable sort using a sequence of keys. + + Like `numpy.lexsort` but for possibly masked ``keys``. Masked + values are sorted towards the end for each key. + """ + # Sort masks to the end. + from .core import Masked + + new_keys = [] + for key in keys: + if isinstance(key, Masked): + # If there are other keys below, want to be sure that + # for masked values, those other keys set the order. + new_key = key.unmasked + if new_keys and key.mask.any(): + new_key = new_key.copy() + new_key[key.mask] = new_key.item(0) + new_keys.extend([new_key, key.mask]) + else: + new_keys.append(key) + + return np.lexsort(new_keys, axis=axis), None, None + + +@dispatched_function +def apply_over_axes(func, a, axes): + # Copied straight from numpy/lib/shape_base, just to omit its + # val = asarray(a); if only it had been asanyarray, or just not there + # since a is assumed to an an array in the next line... + # Which is what we do here - we can only get here if it is Masked. + val = a + N = a.ndim + if np.array(axes).ndim == 0: + axes = (axes,) + for axis in axes: + if axis < 0: + axis = N + axis + args = (val, axis) + res = func(*args) + if res.ndim == val.ndim: + val = res + else: + res = np.expand_dims(res, axis) + if res.ndim == val.ndim: + val = res + else: + raise ValueError( + "function is not returning an array of the correct shape" + ) + + return val, None, None + + +class MaskedFormat: + """Formatter for masked array scalars. + + For use in `numpy.array2string`, wrapping the regular formatters such + that if a value is masked, its formatted string is replaced. + + Typically initialized using the ``from_data`` class method. + """ + + def __init__(self, format_function): + self.format_function = format_function + # Special case for structured void and subarray: we need to make all the + # format functions for the items masked as well. + # TODO: maybe is a separate class is more logical? + ffs = getattr(format_function, "format_functions", None) + if ffs: + # StructuredVoidFormat: multiple format functions to be changed. + self.format_function.format_functions = [MaskedFormat(ff) for ff in ffs] + + ff = getattr(format_function, "format_function", None) + if ff: + # SubarrayFormat: change format function for the elements. + self.format_function.format_function = MaskedFormat(ff) + + def __call__(self, x): + if x.dtype.names: + # The replacement of x with a list is needed because the function + # inside StructuredVoidFormat iterates over x, which works for an + # np.void but not an array scalar. + return self.format_function([x[field] for field in x.dtype.names]) + + if x.shape: + # For a subarray pass on the data directly, since the + # items will be iterated on inside the function. + return self.format_function(x) + + # Single element: first just typeset it normally, replace with masked + # string if needed. + string = self.format_function(x.unmasked[()]) + if x.mask: + # Strikethrough would be neat, but terminal needs a different + # formatting than, say, jupyter notebook. + # return "\x1B[9m"+string+"\x1B[29m" + # return ''.join(s+'\u0336' for s in string) + n = min(3, max(1, len(string))) + return " " * (len(string) - n) + "\u2014" * n + else: + return string + + @classmethod + def from_data(cls, data, **options): + from numpy._core.arrayprint import _get_format_function + + return cls(_get_format_function(data, **options)) + + +def _array2string_impl(a, options, separator=" ", prefix=""): + # Mostly copied from numpy.core.arrayprint, except: + # - The format function is wrapped in a mask-aware class; + # - Arrays scalars are not cast as arrays. + from numpy._core.arrayprint import _formatArray, _leading_trailing + + data = np.asarray(a) + + if a.size > options["threshold"]: + summary_insert = "..." + data = _leading_trailing(data, options["edgeitems"]) + else: + summary_insert = "" + + # find the right formatting function for the array + format_function = MaskedFormat.from_data(data, **options) + + # skip over "[" + next_line_prefix = " " + # skip over array( + next_line_prefix += " " * len(prefix) + + lst = _formatArray( + a, + format_function, + options["linewidth"], + next_line_prefix, + separator, + options["edgeitems"], + summary_insert, + options["legacy"], + ) + return lst + + +def _array2string_main( + a, + *, # make most arguments keyword only to minimize the risk of a human mistake + max_line_width, + precision, + suppress_small, + separator, + prefix, + formatter, + threshold, + edgeitems, + sign, + floatmode, + suffix, + legacy, + style=np._NoValue, # deprecated, removed in numpy 2.4 +): + # Copied from numpy.core.arrayprint, but using _array2string_impl above. + if NUMPY_LT_2_1: + from numpy._core.arrayprint import _format_options + + options = _format_options.copy() + else: + from numpy._core.printoptions import format_options + + options = format_options.get().copy() + + from numpy._core.arrayprint import _make_options_dict + + overrides = _make_options_dict( + precision, + threshold, + edgeitems, + max_line_width, + suppress_small, + nanstr=None, + infstr=None, + sign=sign, + formatter=formatter, + floatmode=floatmode, + ) + options.update(overrides) + + options["linewidth"] -= len(suffix) + + if legacy is not None: + warnings.warn(f"{legacy=} is ignored.") + + # treat as a null array if any of shape elements == 0 + if a.size == 0: + result = "[]" + else: + result = _array2string_impl(a, options, separator, prefix) + + return result, None, None + + +if not NUMPY_LT_2_4: + + @dispatched_function + def array2string( + a, + max_line_width=None, + precision=None, + suppress_small=None, + separator=" ", + prefix="", + *, + formatter=None, + threshold=None, + edgeitems=None, + sign=None, + floatmode=None, + suffix="", + legacy=None, + ): + return _array2string_main( + a, + max_line_width=max_line_width, + precision=precision, + suppress_small=suppress_small, + separator=separator, + prefix=prefix, + formatter=formatter, + threshold=threshold, + edgeitems=edgeitems, + sign=sign, + floatmode=floatmode, + suffix=suffix, + legacy=legacy, + ) +else: + + @dispatched_function + def array2string( + a, + max_line_width=None, + precision=None, + suppress_small=None, + separator=" ", + prefix="", + style=np._NoValue, # removed in numpy 2.4 + formatter=None, + threshold=None, + edgeitems=None, + sign=None, + floatmode=None, + suffix="", + *, + legacy=None, + ): + return _array2string_main( + a, + max_line_width=max_line_width, + precision=precision, + suppress_small=suppress_small, + separator=separator, + prefix=prefix, + style=style, + formatter=formatter, + threshold=threshold, + edgeitems=edgeitems, + sign=sign, + floatmode=floatmode, + suffix=suffix, + legacy=legacy, + ) + + +def _array_str_scalar(x): + # This wraps np.array_str for use as a format function in + # MaskedFormat. We cannot use it directly as format functions + # expect numpy scalars, while np.array_str expects an array. + return np.array_str(np.array(x)) + + +@dispatched_function +def array_str(a, max_line_width=None, precision=None, suppress_small=None): + # Override to change special treatment of array scalars, since the numpy + # code turns the masked array scalar into a regular array scalar. + # By going through MaskedFormat, we can replace the string as needed. + if a.shape == (): + if a.dtype.names is None: + return MaskedFormat(_array_str_scalar)(a), None, None + else: + from numpy._core.arrayprint import StructuredVoidFormat + + # Following numpy._core.arrayprint._void_scalar_to_string + if NUMPY_LT_2_1: + from numpy._core.arrayprint import _format_options + + options = _format_options.copy() + else: + from numpy._core.printoptions import format_options + + options = format_options.get().copy() + + if options.get("formatter") is None: + options["formatter"] = {} + options["formatter"].setdefault("float_kind", str) + return ( + MaskedFormat(StructuredVoidFormat.from_data(a, **options))(a), + None, + None, + ) + + return array2string(a, max_line_width, precision, suppress_small, " ", "") + + +# For the nanfunctions, we just treat any nan as an additional mask. +_nanfunc_fill_values = {"nansum": 0, "nancumsum": 0, "nanprod": 1, "nancumprod": 1} + + +def masked_nanfunc(nanfuncname): + np_func = getattr(np, nanfuncname[3:]) + fill_value = _nanfunc_fill_values.get(nanfuncname) + + def nanfunc(a, *args, **kwargs): + from astropy.utils.masked import Masked, get_data_and_mask + + a, mask = get_data_and_mask(a) + if issubclass(a.dtype.type, np.inexact): + nans = np.isnan(a) + mask = nans if mask is None else (nans | mask) + + if mask is not None: + a = Masked(a, mask) + if fill_value is not None: + a = a.filled(fill_value) + + return np_func(a, *args, **kwargs), None, None + + doc = f"Like `numpy.{nanfuncname}`, skipping masked values as well.\n\n" + if fill_value is not None: + # sum, cumsum, prod, cumprod + doc += ( + f"Masked/NaN values are replaced with {fill_value}. " + "The output is not masked." + ) + elif "arg" in nanfuncname: + doc += ( + "No exceptions are raised for fully masked/NaN slices.\n" + "Instead, these give index 0." + ) + else: + doc += ( + "No warnings are given for fully masked/NaN slices.\n" + "Instead, they are masked in the output." + ) + + nanfunc.__doc__ = doc + nanfunc.__name__ = nanfuncname + + return nanfunc + + +for nanfuncname in np.lib._nanfunctions_impl.__all__: + globals()[nanfuncname] = dispatched_function( + masked_nanfunc(nanfuncname), helps=getattr(np, nanfuncname) + ) + + +@dispatched_function +def ediff1d(ary, to_end=None, to_begin=None): + from astropy.utils.masked import Masked + + # ediff1d works fine if ary is Masked, but not if it is not (and + # we got here because to_end and/or to_begin are Masked). + if not isinstance(ary, Masked): + ary = Masked(ary) + + return np.ediff1d.__wrapped__(ary, to_end, to_begin), None, None + + +def _in1d(ar1, ar2, assume_unique=False, invert=False, *, kind=None): + # Copy sorting implementation from _arraysetops_impl._in1d; + # the others cannot work in the presence of a mask. + if kind == "table": + raise ValueError( + "The 'table' method is not supported for Masked arrays." + "Please select 'sort' or None for kind." + ) + + # Straight copy from here on. + if not assume_unique: + ar1, rev_idx = np.unique(ar1, return_inverse=True) + ar2 = np.unique(ar2) + + ar = np.concatenate((ar1, ar2)) + # We need this to be a stable sort, so always use 'mergesort' + # here. The values from the first array should always come before + # the values from the second array. + order = ar.argsort(kind="mergesort") + sar = ar[order] + if invert: + bool_ar = sar[1:] != sar[:-1] + else: + bool_ar = sar[1:] == sar[:-1] + flag = np.concatenate((bool_ar, [invert])) + ret = np.empty(ar.shape, dtype=bool) + ret[order] = flag + return ret[: len(ar1)] if assume_unique else ret[rev_idx] + + +def _copy_of_mask(a): + mask = getattr(a, "mask", None) + return mask.copy() if mask is not None else False + + +if NUMPY_LT_2_4: + + @dispatched_function + def in1d(ar1, ar2, assume_unique=False, invert=False, *, kind=None): + mask = _copy_of_mask(ar1).ravel() + return _in1d(ar1, ar2, assume_unique, invert, kind=kind), mask, None + + +@dispatched_function +def isin(element, test_elements, assume_unique=False, invert=False, *, kind=None): + element = np.asanyarray(element) + result = _in1d(element, test_elements, assume_unique, invert, kind=kind) + if NUMPY_LT_2_5: + result.shape = element.shape + else: + result._set_shape(element.shape) + return result, _copy_of_mask(element), None + + +@dispatched_function +def setdiff1d(ar1, ar2, assume_unique=False): + # Again, mostly just to avoid an asarray call. + if assume_unique: + ar1 = np.asanyarray(ar1).ravel() + else: + ar1 = np.unique(ar1) + ar2 = np.unique(ar2) + return ar1[np.isin(ar1, ar2, assume_unique=True, invert=True)], None, None + + +# Add any dispatched or helper function that has a docstring to +# __all__, so they will be typeset by sphinx. The logic is that for +# those presumably the use of the mask is not entirely obvious. +__all__ += sorted( # noqa: PLE0605 + helper.__name__ + for helper in ( + set(APPLY_TO_BOTH_FUNCTIONS.values()) | set(DISPATCHED_FUNCTIONS.values()) + ) + if helper.__doc__ +) diff --git a/astropy/utils/masked/tests/__init__.py b/astropy/utils/masked/tests/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/astropy/utils/masked/tests/test_containers.py b/astropy/utils/masked/tests/test_containers.py new file mode 100644 index 000000000000..c76c931419c5 --- /dev/null +++ b/astropy/utils/masked/tests/test_containers.py @@ -0,0 +1,173 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +import numpy as np +import pytest +from numpy.testing import assert_array_equal + +from astropy import units as u +from astropy.coordinates import SkyCoord +from astropy.coordinates import representation as r +from astropy.time import Time +from astropy.utils.masked import Masked, get_data_and_mask + + +class TestRepresentations: + def setup_class(self): + self.x = np.array([3.0, 5.0, 0.0]) << u.m + self.y = np.array([4.0, 12.0, 1.0]) << u.m + self.z = np.array([0.0, 0.0, 1.0]) << u.m + self.c = r.CartesianRepresentation(self.x, self.y, self.z) + self.mask = np.array([False, False, True]) + self.mx = Masked(self.x, self.mask) + self.my = Masked(self.y, self.mask) + self.mz = Masked(self.z, self.mask) + self.mc = r.CartesianRepresentation(self.mx, self.my, self.mz) + + def test_initialization(self): + check = self.mc.z == self.mz + assert_array_equal(check.unmasked, np.ones(3, bool)) + assert_array_equal(check.mask, self.mask) + assert_array_equal(self.mc.x, self.mx) + assert_array_equal(self.mc.y, self.my) + assert_array_equal(self.mc.z, self.mz) + + def test_get_data_and_mask(self): + c, m = get_data_and_mask(self.c) + assert m is None + assert c is self.c + c, m = get_data_and_mask(self.mc) + assert_array_equal(m, self.mask) + assert not c.masked + + def test_norm(self): + # Need stacking and erfa override. + norm = self.mc.norm() + assert_array_equal(norm.unmasked, self.c.norm()) + assert_array_equal(norm.mask, self.mask) + + def test_transformation(self): + msr = self.mc.represent_as(r.SphericalRepresentation) + sr = self.c.represent_as(r.SphericalRepresentation) + for comp in msr.components: + mc = getattr(msr, comp) + c = getattr(sr, comp) + assert_array_equal(mc.unmasked, c) + assert_array_equal(mc.mask, self.mask) + + # Transformation back. This also tests erfa.ufunc.s2p, which + # is special in having a core dimension only in the output. + cr2 = sr.represent_as(r.CartesianRepresentation) + mcr2 = msr.represent_as(r.CartesianRepresentation) + for comp in mcr2.components: + mc = getattr(mcr2, comp) + c = getattr(cr2, comp) + assert_array_equal(mc.unmasked, c) + assert_array_equal(mc.mask, self.mask) + + +class TestSkyCoord: + def setup_class(self): + self.ra = np.array([3.0, 5.0, 0.0]) << u.hourangle + self.dec = np.array([4.0, 12.0, 1.0]) << u.deg + self.sc = SkyCoord(self.ra, self.dec) + self.mask = np.array([False, False, True]) + self.mra = Masked(self.ra, self.mask) + self.mdec = Masked(self.dec, self.mask) + self.msc = SkyCoord(self.mra, self.mdec) + + def test_initialization(self): + check = self.msc.dec == self.mdec + assert_array_equal(check.unmasked, np.ones(3, bool)) + assert_array_equal(check.mask, self.mask) + assert_array_equal(self.msc.data.lon, self.mra) + assert_array_equal(self.msc.data.lat, self.mdec) + + def test_get_data_and_mask(self): + sc, m = get_data_and_mask(self.sc) + assert m is None + assert sc is self.sc + sc, m = get_data_and_mask(self.msc) + assert_array_equal(m, self.mask) + assert not sc.masked + + def test_transformation(self): + gcrs = self.sc.gcrs + mgcrs = self.msc.gcrs + assert_array_equal(mgcrs.data.lon.mask, self.msc.data.lon.mask) + assert_array_equal(mgcrs.data.lon.unmasked, gcrs.data.lon) + assert_array_equal(mgcrs.data.lat.unmasked, gcrs.data.lat) + + @pytest.mark.filterwarnings("ignore:.*ERFA.*distance overridden.*") + def test_apply_space_motion(self): + # Regression test for gh-13041. It is important that the + # distance is missing here, so that a warning is raised. + # Before gh-16224, the ERFA warning machinery let to an error. + kwargs = dict( + ra=[1, 2] * u.deg, + dec=[3, 4] * u.deg, + pm_ra_cosdec=[5, 6] * u.mas / u.yr, + pm_dec=[1, 2] * u.mas / u.yr, + obstime=Time(["2000-10-22T12:23:45", "2000-10-22T12:23:45"]), + ) + sc = SkyCoord(**kwargs) + mask = np.array([False, True]) + kwargs["pm_ra_cosdec"] = Masked(kwargs["pm_ra_cosdec"], mask=mask) + kwargs["pm_dec"] = Masked(kwargs["pm_dec"], mask=mask) + msc = SkyCoord(**kwargs) + new_time = Time("2020-10-22T12:23:45") + sc2 = sc.apply_space_motion(new_obstime=new_time) + msc2 = msc.apply_space_motion(new_obstime=new_time) + assert_array_equal(msc2.data.lon.mask, [False, True]) + assert_array_equal(msc2.data.lon.unmasked, sc2.data.lon) + assert_array_equal(msc2.data.lat.unmasked, sc2.data.lat) + + +class TestTime: + def setup_class(self): + self.s = np.array( + [ + "2010-11-12T13:14:15.160", + "2010-11-12T13:14:15.161", + "2011-12-13T14:15:16.170", + ] + ) + self.t = Time(self.s) + # Time formats will currently strip any ndarray subtypes, so we cannot + # initialize a Time with a Masked version of self.s yet. Instead, we + # work around it, for now only testing that masked are preserved by + # transformations. + self.mask = np.array([False, False, True]) + self.mt = self.t._apply(Masked, self.mask) + + def test_initialization(self): + assert_array_equal(self.mt.jd1.mask, self.mask) + assert_array_equal(self.mt.jd2.mask, self.mask) + assert_array_equal(self.mt.jd1.unmasked, self.t.jd1) + assert_array_equal(self.mt.jd2.unmasked, self.t.jd2) + + def test_get_data_and_mask(self): + t, m = get_data_and_mask(self.t) + assert m is None + assert t is self.t + t, m = get_data_and_mask(self.mt) + assert_array_equal(m, self.mask) + assert not t.masked + + @pytest.mark.parametrize("format_", ["jd", "cxcsec", "jyear"]) + def test_different_formats(self, format_): + # Formats do not yet work with everything; e.g., isot is not supported + # since the Masked class does not yet support structured arrays. + tfmt = getattr(self.t, format_) + mtfmt = getattr(self.mt, format_) + check = mtfmt == tfmt + assert_array_equal(check.unmasked, np.ones(3, bool)) + assert_array_equal(check.mask, self.mask) + + @pytest.mark.parametrize("scale", ["tai", "tcb", "ut1"]) + def test_transformation(self, scale): + tscl = getattr(self.t, scale) + mtscl = getattr(self.mt, scale) + assert_array_equal(mtscl.jd1.mask, self.mask) + assert_array_equal(mtscl.jd2.mask, self.mask) + assert_array_equal(mtscl.jd1.unmasked, tscl.jd1) + assert_array_equal(mtscl.jd2.unmasked, tscl.jd2) diff --git a/astropy/utils/masked/tests/test_dynamic_subclasses.py b/astropy/utils/masked/tests/test_dynamic_subclasses.py new file mode 100644 index 000000000000..972b09ac9177 --- /dev/null +++ b/astropy/utils/masked/tests/test_dynamic_subclasses.py @@ -0,0 +1,21 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +"""Test importability of common Masked classes and their info classes.""" + +import pytest + +from astropy.coordinates.angles.core import Angle, Latitude, Longitude +from astropy.units.quantity import Quantity +from astropy.utils.masked import core + + +@pytest.mark.parametrize("base_class", [Quantity, Angle, Latitude, Longitude]) +def test_importable(base_class): + assert getattr(core, f"Masked{base_class.__name__}") is core.Masked(base_class) + + +@pytest.mark.parametrize("base_class", [Quantity, Angle, Latitude, Longitude]) +def test_importable_info(base_class): + assert ( + getattr(core, f"Masked{base_class.__name__}Info") + is core.Masked(base_class).info.__class__ + ) diff --git a/astropy/utils/masked/tests/test_function_helpers.py b/astropy/utils/masked/tests/test_function_helpers.py new file mode 100644 index 000000000000..84fbe40d61af --- /dev/null +++ b/astropy/utils/masked/tests/test_function_helpers.py @@ -0,0 +1,1742 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +"""Test all functions covered by __array_function__. + +Here, run through all functions, with simple tests just to check the helpers. +More complicated tests of functionality, including with subclasses, are done +in test_functions. + +TODO: finish full coverage (see also `~astropy.utils.masked.function_helpers`) +- np.linalg +- np.fft (is there any point?) + +""" + +import itertools + +import numpy as np +import pytest +from numpy.testing import assert_array_equal + +import astropy.units as u +from astropy.units.tests.test_quantity_non_ufuncs import ( + CheckSignatureCompatibilityBase, + get_covered_functions, + get_wrapped_functions, +) +from astropy.utils.compat import NUMPY_LT_2_1, NUMPY_LT_2_2, NUMPY_LT_2_4 +from astropy.utils.masked import Masked, MaskedNDArray +from astropy.utils.masked.function_helpers import ( + APPLY_TO_BOTH_FUNCTIONS, + DISPATCHED_FUNCTIONS, + IGNORED_FUNCTIONS, + MASKED_SAFE_FUNCTIONS, + SUPPORTED_NEP35_FUNCTIONS, + UNSUPPORTED_FUNCTIONS, +) + +from .test_masked import MaskedArraySetup, assert_masked_equal + + +class BasicTestSetup(MaskedArraySetup): + def check(self, func, *args, **kwargs): + out = func(self.ma, *args, **kwargs) + expected = Masked( + func(self.a, *args, **kwargs), mask=func(self.mask_a, *args, **kwargs) + ) + assert_masked_equal(out, expected) + + def check2(self, func, *args, **kwargs): + out = func(self.ma, self.mb, *args, **kwargs) + expected = Masked( + func(self.a, self.b, *args, **kwargs), + mask=func(self.mask_a, self.mask_b, *args, **kwargs), + ) + if isinstance(out, (tuple, list)): + for o, x in zip(out, expected): + assert_masked_equal(o, x) + else: + assert_masked_equal(out, expected) + + +class NoMaskTestSetup(MaskedArraySetup): + def check(self, func, *args, **kwargs): + o = func(self.ma, *args, **kwargs) + expected = func(self.a, *args, **kwargs) + assert_array_equal(o, expected) + + +class InvariantMaskTestSetup(MaskedArraySetup): + def check(self, func, *args, **kwargs): + o = func(self.ma, *args, **kwargs) + expected = func(self.a, *args, **kwargs) + assert_array_equal(o.unmasked, expected) + assert_array_equal(o.mask, self.mask_a) + + +class TestShapeInformation(BasicTestSetup): + def test_shape(self): + assert np.shape(self.ma) == (2, 3) + + def test_size(self): + assert np.size(self.ma) == 6 + + def test_ndim(self): + assert np.ndim(self.ma) == 2 + + +class TestShapeManipulation(BasicTestSetup): + # Note: do not parametrize the below, since test names are used + # to check coverage. + def test_reshape(self): + self.check(np.reshape, (6, 1)) + + def test_ravel(self): + self.check(np.ravel) + + def test_moveaxis(self): + self.check(np.moveaxis, 0, 1) + + def test_rollaxis(self): + self.check(np.rollaxis, 0, 2) + + def test_swapaxes(self): + self.check(np.swapaxes, 0, 1) + + def test_transpose(self): + self.check(np.transpose) + + def test_matrix_transpose(self): + self.check(np.matrix_transpose) + + def test_atleast_1d(self): + self.check(np.atleast_1d) + o, so = np.atleast_1d(self.mb[0], self.mc[0]) + assert o.shape == o.mask.shape == so.shape == so.mask.shape == (1,) + + def test_atleast_2d(self): + self.check(np.atleast_2d) + o, so = np.atleast_2d(self.mb[0], self.mc[0]) + assert o.shape == o.mask.shape == so.shape == so.mask.shape == (1, 1) + + def test_atleast_3d(self): + self.check(np.atleast_3d) + o, so = np.atleast_3d(self.mb[0], self.mc[0]) + assert o.shape == o.mask.shape == so.shape == so.mask.shape == (1, 1, 1) + + def test_expand_dims(self): + self.check(np.expand_dims, 1) + + def test_squeeze(self): + o = np.squeeze(self.mc) + assert o.shape == o.mask.shape == (2,) + assert_array_equal(o.unmasked, self.c.squeeze()) + assert_array_equal(o.mask, self.mask_c.squeeze()) + + def test_flip(self): + self.check(np.flip) + + def test_fliplr(self): + self.check(np.fliplr) + + def test_flipud(self): + self.check(np.flipud) + + def test_rot90(self): + self.check(np.rot90) + + def test_broadcast_to(self): + self.check(np.broadcast_to, (3, 2, 3)) + self.check(np.broadcast_to, (3, 2, 3), subok=False) + + def test_broadcast_arrays(self): + self.check2(np.broadcast_arrays) + self.check2(np.broadcast_arrays, subok=False) + # Regression test for bug for single array + ba = np.broadcast_arrays(self.ma, subok=True) + assert isinstance(ba, tuple) + assert len(ba) == 1 + assert_array_equal(ba[0].unmasked, self.a) + assert_array_equal(ba[0].mask, self.mask_a) + assert np.may_share_memory(ba[0], self.a) + + +class TestArgFunctions(MaskedArraySetup): + def check(self, function, *args, fill_value=np.nan, **kwargs): + o = function(self.ma, *args, **kwargs) + a_filled = self.ma.filled(fill_value=fill_value) + expected = function(a_filled, *args, **kwargs) + assert_array_equal(o, expected) + + def test_argmin(self): + self.check(np.argmin, fill_value=np.inf) + + def test_argmax(self): + self.check(np.argmax, fill_value=-np.inf) + + def test_argsort(self): + self.check(np.argsort, fill_value=np.nan) + + def test_lexsort(self): + self.check(np.lexsort, fill_value=np.nan) + + def test_nonzero(self): + self.check(np.nonzero, fill_value=0.0) + + @pytest.mark.skipif( + not NUMPY_LT_2_1, reason="support for 0d arrays was removed in numpy 2.1" + ) + @pytest.mark.filterwarnings("ignore:Calling nonzero on 0d arrays is deprecated") + def test_nonzero_0d_np_lt_2_1(self): + res1 = Masked(1, mask=False).nonzero() + assert len(res1) == 1 + assert_array_equal(res1[0], 0) + res2 = Masked(1, mask=True).nonzero() + assert len(res2) == 1 + assert_array_equal(res2[0], 0) + + @pytest.mark.skipif( + NUMPY_LT_2_1, reason="support for 0d arrays was removed in numpy 2.1" + ) + def test_nonzero_0d_np_ge_2_1(self): + with pytest.raises(ValueError): + Masked(1, mask=False).nonzero() + + def test_argwhere(self): + self.check(np.argwhere, fill_value=0.0) + + def test_argpartition(self): + self.check(np.argpartition, 2, fill_value=np.inf) + + def test_flatnonzero(self): + self.check(np.flatnonzero, fill_value=0.0) + + +class TestAlongAxis(MaskedArraySetup): + def test_take_along_axis(self): + indices = np.expand_dims(np.argmax(self.ma, axis=0), axis=0) + out = np.take_along_axis(self.ma, indices, axis=0) + expected = np.take_along_axis(self.a, indices, axis=0) + expected_mask = np.take_along_axis(self.mask_a, indices, axis=0) + assert_array_equal(out.unmasked, expected) + assert_array_equal(out.mask, expected_mask) + + def test_put_along_axis(self): + ma = self.ma.copy() + indices = np.expand_dims(np.argmax(self.ma, axis=0), axis=0) + np.put_along_axis(ma, indices, axis=0, values=-1) + expected = self.a.copy() + np.put_along_axis(expected, indices, axis=0, values=-1) + assert_array_equal(ma.unmasked, expected) + assert_array_equal(ma.mask, self.mask_a) + np.put_along_axis(ma, indices, axis=0, values=np.ma.masked) + assert_array_equal(ma.unmasked, expected) + expected_mask = self.mask_a.copy() + np.put_along_axis(expected_mask, indices, axis=0, values=True) + assert_array_equal(ma.mask, expected_mask) + + @pytest.mark.parametrize("axis", (0, 1)) + def test_apply_along_axis(self, axis): + out = np.apply_along_axis(np.square, axis, self.ma) + expected = np.apply_along_axis(np.square, axis, self.a) + assert_array_equal(out.unmasked, expected) + assert_array_equal(out.mask, self.mask_a) + + @pytest.mark.parametrize("axes", [(1,), 0, (0, -1)]) + def test_apply_over_axes(self, axes): + def function(x, axis): + return np.mean(np.square(x), axis) + + out = np.apply_over_axes(function, self.ma, axes) + expected = self.ma + for axis in axes if isinstance(axes, tuple) else (axes,): + expected = (expected**2).mean(axis, keepdims=True) + assert_array_equal(out.unmasked, expected.unmasked) + assert_array_equal(out.mask, expected.mask) + + def test_apply_over_axes_no_reduction(self): + out = np.apply_over_axes(np.cumsum, self.ma, 0) + expected = self.ma.cumsum(axis=0) + assert_masked_equal(out, expected) + + def test_apply_over_axes_wrong_size(self): + with pytest.raises(ValueError, match="not.*correct shape"): + np.apply_over_axes(lambda x, axis: x[..., np.newaxis], self.ma, 0) + + +class TestIndicesFrom(NoMaskTestSetup): + @classmethod + def setup_class(cls): + cls.a = np.arange(9).reshape(3, 3) + cls.mask_a = np.eye(3, dtype=bool) + cls.ma = Masked(cls.a, cls.mask_a) + + def test_diag_indices_from(self): + self.check(np.diag_indices_from) + + def test_triu_indices_from(self): + self.check(np.triu_indices_from) + + def test_tril_indices_from(self): + self.check(np.tril_indices_from) + + +class TestRealImag(InvariantMaskTestSetup): + @classmethod + def setup_class(cls): + cls.a = np.array([1 + 2j, 3 + 4j]) + cls.mask_a = np.array([True, False]) + cls.ma = Masked(cls.a, mask=cls.mask_a) + + def test_real(self): + self.check(np.real) + + def test_imag(self): + self.check(np.imag) + + +class TestCopyAndCreation(InvariantMaskTestSetup): + def test_copy(self): + self.check(np.copy) + # Also as kwarg + copy = np.copy(a=self.ma) + assert_array_equal(copy, self.ma) + + def test_astype(self): + int32ma = self.ma.astype("int32") + assert_array_equal(np.astype(int32ma, "int32"), int32ma) + + +class TestArrayCreation(MaskedArraySetup): + def test_empty_like(self): + o = np.empty_like(self.ma) + assert o.shape == (2, 3) + assert isinstance(o, Masked) + assert isinstance(o, np.ndarray) + o2 = np.empty_like(self.ma) + assert o2.shape == (2, 3) + assert isinstance(o2, Masked) + assert isinstance(o2, np.ndarray) + o3 = np.empty_like(self.ma, subok=False) + assert type(o3) is MaskedNDArray + + def test_zeros_like(self): + o = np.zeros_like(self.ma) + assert_array_equal(o.unmasked, np.zeros_like(self.a)) + assert_array_equal(o.mask, np.zeros_like(self.mask_a)) + o2 = np.zeros_like(a=self.ma) + assert_array_equal(o2.unmasked, np.zeros_like(self.a)) + assert_array_equal(o2.mask, np.zeros_like(self.mask_a)) + + def test_ones_like(self): + o = np.ones_like(self.ma) + assert_array_equal(o.unmasked, np.ones_like(self.a)) + assert_array_equal(o.mask, np.zeros_like(self.mask_a)) + o2 = np.ones_like(a=self.ma) + assert_array_equal(o2.unmasked, np.ones_like(self.a)) + assert_array_equal(o2.mask, np.zeros_like(self.mask_a)) + + @pytest.mark.parametrize("value", [0.5, Masked(0.5, mask=True), np.ma.masked]) + def test_full_like(self, value): + o = np.full_like(self.ma, value) + if value is np.ma.masked: + expected = Masked(o.unmasked, True) + else: + expected = Masked(np.empty_like(self.a)) + expected[...] = value + assert_array_equal(o.unmasked, expected.unmasked) + assert_array_equal(o.mask, expected.mask) + + +class TestAccessingParts(BasicTestSetup): + def test_diag(self): + self.check(np.diag) + + def test_diag_1d_input(self): + ma = self.ma.ravel() + o = np.diag(ma) + assert_array_equal(o.unmasked, np.diag(self.a.ravel())) + assert_array_equal(o.mask, np.diag(self.mask_a.ravel())) + + def test_diagonal(self): + self.check(np.diagonal) + + def test_diagflat(self): + self.check(np.diagflat) + + def test_compress(self): + o = np.compress([True, False], self.ma, axis=0) + expected = np.compress([True, False], self.a, axis=0) + expected_mask = np.compress([True, False], self.mask_a, axis=0) + assert_array_equal(o.unmasked, expected) + assert_array_equal(o.mask, expected_mask) + + def test_extract(self): + o = np.extract([True, False, True], self.ma) + expected = np.extract([True, False, True], self.a) + expected_mask = np.extract([True, False, True], self.mask_a) + assert_array_equal(o.unmasked, expected) + assert_array_equal(o.mask, expected_mask) + + def test_delete(self): + self.check(np.delete, slice(1, 2), 0) + self.check(np.delete, [0, 2], 1) + + def test_roll(self): + self.check(np.roll, 1) + self.check(np.roll, 1, axis=0) + + def test_take(self): + self.check(np.take, [0, 1], axis=1) + self.check(np.take, 1) + + +class TestSettingParts(MaskedArraySetup): + def test_put(self): + ma = self.ma.copy() + v = Masked([50, 150], [False, True]) + np.put(ma, [0, 2], v) + expected = self.a.copy() + np.put(expected, [0, 2], [50, 150]) + expected_mask = self.mask_a.copy() + np.put(expected_mask, [0, 2], [False, True]) + assert_array_equal(ma.unmasked, expected) + assert_array_equal(ma.mask, expected_mask) + np.put(ma, [1, 2], np.ma.masked) + np.put(expected_mask, [1, 2], True) + assert_array_equal(ma.unmasked, expected) + assert_array_equal(ma.mask, expected_mask) + np.put(ma, [0, 1], np.ma.nomask) + np.put(expected_mask, [0, 1], False) + assert_array_equal(ma.unmasked, expected) + assert_array_equal(ma.mask, expected_mask) + + with pytest.raises(TypeError): + # Indices cannot be masked. + np.put(ma, Masked([0, 2]), v) + + with pytest.raises(TypeError): + # Array to put masked values in must be masked. + np.put(self.a.copy(), [0, 2], v) + + def test_putmask(self): + ma = self.ma.flatten() + mask = np.array([True, False, False, False, True, False]) + values = Masked( + np.arange(100, 650, 100), mask=[False, True, True, True, False, False] + ) + np.putmask(ma, mask, values) + expected = self.a.flatten() + np.putmask(expected, mask, values.unmasked) + expected_mask = self.mask_a.flatten() + np.putmask(expected_mask, mask, values.mask) + assert_array_equal(ma.unmasked, expected) + assert_array_equal(ma.mask, expected_mask) + np.putmask(ma, ~mask, np.ma.masked) + np.putmask(expected_mask, ~mask, True) + assert_array_equal(ma.unmasked, expected) + assert_array_equal(ma.mask, expected_mask) + np.putmask(ma, mask, np.ma.nomask) + np.putmask(expected_mask, mask, False) + assert_array_equal(ma.unmasked, expected) + assert_array_equal(ma.mask, expected_mask) + + with pytest.raises(TypeError): + np.putmask(self.a.flatten(), mask, values) + + def test_place(self): + ma = self.ma.flatten() + mask = np.array([True, False, False, False, True, False]) + values = Masked([100, 200], mask=[False, True]) + np.place(ma, mask, values) + expected = self.a.flatten() + np.place(expected, mask, values.unmasked) + expected_mask = self.mask_a.flatten() + np.place(expected_mask, mask, values.mask) + assert_array_equal(ma.unmasked, expected) + assert_array_equal(ma.mask, expected_mask) + np.place(ma, ~mask, np.ma.masked) + np.place(expected_mask, ~mask, True) + assert_array_equal(ma.unmasked, expected) + assert_array_equal(ma.mask, expected_mask) + np.place(ma, mask, np.ma.nomask) + np.place(expected_mask, mask, False) + assert_array_equal(ma.unmasked, expected) + assert_array_equal(ma.mask, expected_mask) + + with pytest.raises(TypeError): + np.place(self.a.flatten(), mask, values) + + def test_copyto(self): + ma = self.ma.flatten() + mask = np.array([True, False, False, False, True, False]) + values = Masked( + np.arange(100, 650, 100), mask=[False, True, True, True, False, False] + ) + np.copyto(ma, values, where=mask) + expected = self.a.flatten() + np.copyto(expected, values.unmasked, where=mask) + expected_mask = self.mask_a.flatten() + np.copyto(expected_mask, values.mask, where=mask) + assert_array_equal(ma.unmasked, expected) + assert_array_equal(ma.mask, expected_mask) + np.copyto(ma, np.ma.masked, where=~mask) + np.copyto(expected_mask, True, where=~mask) + assert_array_equal(ma.unmasked, expected) + assert_array_equal(ma.mask, expected_mask) + np.copyto(ma, np.ma.nomask, where=mask) + np.copyto(expected_mask, False, where=mask) + assert_array_equal(ma.unmasked, expected) + assert_array_equal(ma.mask, expected_mask) + + with pytest.raises(TypeError): + np.copyto(self.a.flatten(), values, where=mask) + + @pytest.mark.parametrize("value", [0.25, np.ma.masked]) + def test_fill_diagonal(self, value): + ma = self.ma[:2, :2].copy() + np.fill_diagonal(ma, value) + expected = ma.copy() + expected[np.diag_indices_from(expected)] = value + assert_array_equal(ma.unmasked, expected.unmasked) + assert_array_equal(ma.mask, expected.mask) + + +class TestRepeat(BasicTestSetup): + def test_tile(self): + self.check(np.tile, 2) + + def test_repeat(self): + self.check(np.repeat, 2) + + def test_resize(self): + self.check(np.resize, (4, 4)) + + +class TestConcatenate(MaskedArraySetup): + # More tests at TestMaskedArrayConcatenation in test_functions. + def check(self, func, *args, **kwargs): + ma_list = kwargs.pop("ma_list", [self.ma, self.ma]) + a_list = [Masked(ma).unmasked for ma in ma_list] + m_list = [Masked(ma).mask for ma in ma_list] + o = func(ma_list, *args, **kwargs) + expected = func(a_list, *args, **kwargs) + expected_mask = func(m_list, *args, **kwargs) + assert_array_equal(o.unmasked, expected) + assert_array_equal(o.mask, expected_mask) + + def test_concatenate(self): + self.check(np.concatenate) + self.check(np.concatenate, axis=1) + self.check(np.concatenate, ma_list=[self.a, self.ma]) + self.check(np.concatenate, dtype="f4") + + out = Masked(np.empty((4, 3))) + result = np.concatenate([self.ma, self.ma], out=out) + assert out is result + expected = np.concatenate([self.a, self.a]) + expected_mask = np.concatenate([self.mask_a, self.mask_a]) + assert_array_equal(out.unmasked, expected) + assert_array_equal(out.mask, expected_mask) + + with pytest.raises(TypeError): + np.concatenate([self.ma, self.ma], out=np.empty((4, 3))) + + def test_stack(self): + self.check(np.stack) + + def test_column_stack(self): + self.check(np.column_stack) + + def test_hstack(self): + self.check(np.hstack) + + def test_vstack(self): + self.check(np.vstack) + + def test_dstack(self): + self.check(np.dstack) + + def test_block(self): + self.check(np.block) + # Check that this also works on MaskedQuantity, properly propagating + # the fact that we are based on MaskedNDArray. + self.check(np.block, ma_list=[self.ma << u.m, self.mc << u.km]) + # And check a mix of float and masked values, with different dtype. + out = np.block([[0.0, Masked(1.0, True)], [Masked(1, False), Masked(2, False)]]) + expected = np.array([[0, 1.0], [1, 2]]) + expected_mask = np.array([[False, True], [False, False]]) + assert_array_equal(out.unmasked, expected) + assert_array_equal(out.mask, expected_mask) + # And check single array. + in2 = Masked([1.0], [True]) + out2 = np.block(Masked([1.0], [True])) + assert not np.may_share_memory(out2, in2) + assert_array_equal(out2.unmasked, in2.unmasked) + assert_array_equal(out2.mask, in2.mask) + + def test_append(self): + out = np.append(self.ma, self.mc, axis=1) + expected = np.append(self.a, self.c, axis=1) + expected_mask = np.append(self.mask_a, self.mask_c, axis=1) + assert_array_equal(out.unmasked, expected) + assert_array_equal(out.mask, expected_mask) + + def test_insert(self): + obj = (1, 1) + values = Masked([50.0, 25.0], mask=[True, False]) + out = np.insert(self.ma.flatten(), obj, values) + expected = np.insert(self.a.flatten(), obj, [50.0, 25.0]) + expected_mask = np.insert(self.mask_a.flatten(), obj, [True, False]) + assert_array_equal(out.unmasked, expected) + assert_array_equal(out.mask, expected_mask) + + with pytest.raises(TypeError): + np.insert(self.a.flatten(), obj, values) + + with pytest.raises(TypeError): + np.insert(self.ma.flatten(), Masked(obj), values) + + +class TestSplit: + @classmethod + def setup_class(cls): + cls.a = np.arange(54.0).reshape(3, 3, 6) + cls.mask_a = np.zeros(cls.a.shape, dtype=bool) + cls.mask_a[1, 1, 1] = True + cls.mask_a[0, 1, 4] = True + cls.mask_a[1, 2, 5] = True + cls.ma = Masked(cls.a, mask=cls.mask_a) + + def check(self, func, *args, **kwargs): + out = func(self.ma, *args, **kwargs) + expected = func(self.a, *args, **kwargs) + expected_mask = func(self.mask_a, *args, **kwargs) + assert len(out) == len(expected) + for o, x, xm in zip(out, expected, expected_mask): + assert_array_equal(o.unmasked, x) + assert_array_equal(o.mask, xm) + + def test_split(self): + self.check(np.split, [1]) + + def test_array_split(self): + self.check(np.array_split, 2) + + def test_hsplit(self): + self.check(np.hsplit, [1, 4]) + + def test_vsplit(self): + self.check(np.vsplit, [1]) + + def test_dsplit(self): + self.check(np.dsplit, [1]) + + @pytest.mark.skipif(NUMPY_LT_2_1, reason="np.unstack is new in Numpy 2.1") + def test_unstack(self): + self.check(np.unstack) + + +class TestMethodLikes(MaskedArraySetup): + def check(self, function, *args, method=None, **kwargs): + if method is None: + method = function.__name__ + + o = function(self.ma, *args, **kwargs) + x = getattr(self.ma, method)(*args, **kwargs) + assert_masked_equal(o, x) + + def test_max(self): + self.check(np.max, method="max") + + def test_min(self): + self.check(np.min, method="min") + + def test_amax(self): + self.check(np.amax, method="max") + + def test_amin(self): + self.check(np.amin, method="min") + + def test_sum(self): + self.check(np.sum) + + def test_cumsum(self): + self.check(np.cumsum) + + def test_any(self): + self.check(np.any) + + def test_all(self): + self.check(np.all) + + def test_prod(self): + self.check(np.prod) + + def test_cumprod(self): + self.check(np.cumprod) + + def test_round(self): + self.check(np.round, method="round") + + def test_around(self): + self.check(np.around, method="round") + + def test_clip(self): + self.check(np.clip, 2.0, 4.0) + self.check(np.clip, self.mb, self.mc) + + def test_mean(self): + self.check(np.mean) + + def test_std(self): + self.check(np.std) + + def test_var(self): + self.check(np.var) + + +class TestUfuncLike(InvariantMaskTestSetup): + @pytest.mark.filterwarnings("ignore:numpy.fix is deprecated:DeprecationWarning") + def test_fix(self): + self.check(np.fix) + # Check np.fix with out argument for completeness + # (Note: could be done in self.check, but np.fix is the only + # invariant mask function that has `out`, so no point.) + out = np.zeros_like(self.ma) + result = np.fix(self.ma, out=out) + assert result is out + expected = np.fix(self.a) + assert_array_equal(out.unmasked, expected) + assert_array_equal(out.mask, self.mask_a) + + def test_angle(self): + a = np.array([1 + 0j, 0 + 1j, 1 + 1j, 0 + 0j]) + mask_a = np.array([True, False, True, False]) + ma = Masked(a, mask=mask_a) + out = np.angle(ma) + expected = np.angle(ma.unmasked) + assert_array_equal(out.unmasked, expected) + assert_array_equal(out.mask, mask_a) + + def test_i0(self): + self.check(np.i0) + + def test_sinc(self): + self.check(np.sinc) + + def test_where(self): + mask = [True, False, True] + out = np.where(mask, self.ma, 1000.0) + expected = np.where(mask, self.a, 1000.0) + expected_mask = np.where(mask, self.mask_a, False) + assert_array_equal(out.unmasked, expected) + assert_array_equal(out.mask, expected_mask) + + mask2 = Masked(mask, [True, False, False]) + out2 = np.where(mask2, self.ma, 1000.0) + expected2 = np.where(mask, self.a, 1000.0) + expected_mask2 = np.where(mask, self.mask_a, False) | mask2.mask + assert_array_equal(out2.unmasked, expected2) + assert_array_equal(out2.mask, expected_mask2) + + def test_where_single_arg(self): + m = Masked(np.arange(3), mask=[True, False, False]) + out = np.where(m) + expected = m.nonzero() + assert isinstance(out, tuple) and len(out) == 1 + assert_array_equal(out[0], expected[0]) + + def test_where_wrong_number_of_arg(self): + with pytest.raises(ValueError, match="either both or neither"): + np.where([True, False, False], self.a) + + def test_choose(self): + a = np.array([0, 1]).reshape((2, 1)) + result = np.choose(a, (self.ma, self.mb)) + expected = np.choose(a, (self.a, self.b)) + expected_mask = np.choose(a, (self.mask_a, self.mask_b)) + assert_array_equal(result.unmasked, expected) + assert_array_equal(result.mask, expected_mask) + + out = np.zeros_like(result) + result2 = np.choose(a, (self.ma, self.mb), out=out) + assert result2 is out + assert_array_equal(result2, result) + + with pytest.raises(TypeError): + np.choose(a, (self.ma, self.mb), out=np.zeros_like(expected)) + + def test_choose_masked(self): + ma = Masked(np.array([-1, 1]), mask=[True, False]).reshape((2, 1)) + out = ma.choose((self.ma, self.mb)) + expected = np.choose(ma.filled(0), (self.a, self.b)) + expected_mask = np.choose(ma.filled(0), (self.mask_a, self.mask_b)) | ma.mask + assert_array_equal(out.unmasked, expected) + assert_array_equal(out.mask, expected_mask) + + with pytest.raises(ValueError): + ma.unmasked.choose((self.ma, self.mb)) + + @pytest.mark.parametrize("default", [-1.0, np.ma.masked, Masked(-1, mask=True)]) + def test_select(self, default): + a, mask_a, ma = self.a, self.mask_a, self.ma + out = np.select([a < 1.5, a > 3.5], [ma, ma + 1], default=default) + expected = np.select( + [a < 1.5, a > 3.5], + [a, a + 1], + default=-1 if default is not np.ma.masked else 0, + ) + expected_mask = np.select( + [a < 1.5, a > 3.5], + [mask_a, mask_a], + default=getattr(default, "mask", False), + ) + assert_array_equal(out.unmasked, expected) + assert_array_equal(out.mask, expected_mask) + + def test_real_if_close(self): + a = np.array([1 + 0j, 0 + 1j, 1 + 1j, 0 + 0j]) + mask_a = np.array([True, False, True, False]) + ma = Masked(a, mask=mask_a) + out = np.real_if_close(ma) + expected = np.real_if_close(a) + assert_array_equal(out.unmasked, expected) + assert_array_equal(out.mask, mask_a) + + def test_tril(self): + self.check(np.tril) + + def test_triu(self): + self.check(np.triu) + + def test_unwrap(self): + self.check(np.unwrap) + + def test_nan_to_num(self): + self.check(np.nan_to_num) + ma = Masked([np.nan, 1.0], mask=[True, False]) + o = np.nan_to_num(ma, copy=False) + assert_masked_equal(o, Masked([0.0, 1.0], mask=[True, False])) + assert ma is o + + +class TestUfuncLikeTests: + @classmethod + def setup_class(cls): + cls.a = np.array([[-np.inf, +np.inf, np.nan, 3.0, 4.0]] * 2) + cls.mask_a = np.array([[False] * 5, [True] * 4 + [False]]) + cls.ma = Masked(cls.a, mask=cls.mask_a) + cls.b = np.array([[3.0001], [3.9999]]) + cls.mask_b = np.array([[True], [False]]) + cls.mb = Masked(cls.b, mask=cls.mask_b) + + def check(self, func): + out = func(self.ma) + expected = func(self.a) + assert type(out) is MaskedNDArray + assert out.dtype.kind == "b" + assert_array_equal(out.unmasked, expected) + assert_array_equal(out.mask, self.mask_a) + assert not np.may_share_memory(out.mask, self.mask_a) + + def test_isposinf(self): + self.check(np.isposinf) + + def test_isneginf(self): + self.check(np.isneginf) + + def test_isreal(self): + self.check(np.isreal) + o = np.isreal(Masked([1.0 + 1j], mask=False)) + assert not o.unmasked and not o.mask + o = np.isreal(Masked([1.0 + 1j], mask=True)) + assert not o.unmasked and o.mask + + def test_iscomplex(self): + self.check(np.iscomplex) + o = np.iscomplex(Masked([1.0 + 1j], mask=False)) + assert o.unmasked and not o.mask + o = np.iscomplex(Masked([1.0 + 1j], mask=True)) + assert o.unmasked and o.mask + + def test_isclose(self): + out = np.isclose(self.ma, self.mb, atol=0.01) + expected = np.isclose(self.ma, self.mb, atol=0.01) + expected_mask = self.mask_a | self.mask_b + assert_array_equal(out.unmasked, expected) + assert_array_equal(out.mask, expected_mask) + + def test_allclose(self): + out = np.allclose(self.ma, self.mb, atol=0.01) + expected = np.isclose(self.ma, self.mb, atol=0.01)[ + self.mask_a | self.mask_b + ].all() + assert_array_equal(out, expected) + + def test_array_equal(self): + assert not np.array_equal(self.ma, self.ma) + assert not np.array_equal(self.ma, self.a) + assert np.array_equal(self.ma, self.ma, equal_nan=True) + assert np.array_equal(self.ma, self.a, equal_nan=True) + assert not np.array_equal(self.ma, self.mb) + ma2 = self.ma.copy() + ma2.mask |= np.isnan(self.a) + assert np.array_equal(ma2, self.ma) + + def test_array_equiv(self): + assert np.array_equiv(self.mb, self.mb) + assert np.array_equiv(self.mb, self.b) + assert not np.array_equiv(self.ma, self.mb) + assert np.array_equiv(self.mb, np.stack([self.mb, self.mb])) + + +class TestArrayAPI: + @classmethod + def setup_class(cls): + cls.a = np.tile(np.arange(5.0), 2).reshape(2, 5) + cls.mask_a = np.array([[False] * 5, [True] * 4 + [False]]) + cls.ma = Masked(cls.a, mask=cls.mask_a) + + def check(self, func, *args, **kwargs): + out = func(self.ma, *args, **kwargs) + expected = func(self.a, *args, **kwargs) + assert type(out) is MaskedNDArray + assert out.dtype.kind == "f" + assert_array_equal(out.unmasked, expected) + assert_array_equal(out.mask, self.mask_a) + assert not np.may_share_memory(out.mask, self.mask_a) + + @pytest.mark.skipif(NUMPY_LT_2_1, reason="np.cumulative_prod is new in NumPy 2.1") + def test_cumulative_prod(self): + self.check(np.cumulative_prod, axis=0) + + @pytest.mark.skipif(NUMPY_LT_2_1, reason="np.cumulative_sum is new in NumPy 2.1") + def test_cumulative_sum(self): + self.check(np.cumulative_sum, axis=0) + + +class TestOuterLikeFunctions(MaskedArraySetup): + def test_outer(self): + result = np.outer(self.ma, self.mb) + expected_data = np.outer(self.a.ravel(), self.b.ravel()) + expected_mask = np.logical_or.outer(self.mask_a.ravel(), self.mask_b.ravel()) + assert_array_equal(result.unmasked, expected_data) + assert_array_equal(result.mask, expected_mask) + + out = np.zeros_like(result) + result2 = np.outer(self.ma, self.mb, out=out) + assert result2 is out + assert result2 is not result + assert_masked_equal(result2, result) + + out2 = np.zeros_like(result.unmasked) + with pytest.raises(TypeError): + np.outer(self.ma, self.mb, out=out2) + + def test_kron(self): + result = np.kron(self.ma, self.mb) + expected_data = np.kron(self.a, self.b) + expected_mask = np.logical_or.outer(self.mask_a, self.mask_b).reshape( + result.shape + ) + assert_array_equal(result.unmasked, expected_data) + assert_array_equal(result.mask, expected_mask) + + +class TestReductionLikeFunctions(MaskedArraySetup): + def test_average(self): + o = np.average(self.ma) + assert_masked_equal(o, self.ma.mean()) + + o = np.average(self.ma, weights=self.mb, axis=-1) + expected = np.average(self.a, weights=self.b, axis=-1) + expected_mask = (self.mask_a | self.mask_b).any(-1) + assert_array_equal(o.unmasked, expected) + assert_array_equal(o.mask, expected_mask) + + @pytest.mark.parametrize("kwargs", [{}, {"axis": 0}]) + def test_ptp(self, kwargs): + o = np.ptp(self.ma, **kwargs) + expected = self.ma.max(**kwargs) - self.ma.min(**kwargs) + assert_array_equal(o.unmasked, expected.unmasked) + assert_array_equal(o.mask, expected.mask) + out = np.zeros_like(expected) + o2 = np.ptp(self.ma, out=out, **kwargs) + assert o2 is out + assert_array_equal(o2.unmasked, expected.unmasked) + assert_array_equal(o2.mask, expected.mask) + + def test_trace(self): + o = np.trace(self.ma) + expected = np.trace(self.a) + expected_mask = np.trace(self.mask_a).astype(bool) + assert_array_equal(o.unmasked, expected) + assert_array_equal(o.mask, expected_mask) + + @pytest.mark.parametrize("axis", [0, 1, None]) + def test_count_nonzero(self, axis): + o = np.count_nonzero(self.ma, axis=axis) + expected = np.count_nonzero(self.ma.filled(0), axis=axis) + assert_array_equal(o, expected) + + +@pytest.mark.filterwarnings("ignore:all-nan") +class TestPartitionLikeFunctions: + @classmethod + def setup_class(cls): + cls.a = np.arange(36.0).reshape(6, 6) + cls.mask_a = np.zeros_like(cls.a, bool) + # On purpose fill diagonal, so we get all masked elements. + cls.mask_a[np.tril_indices_from(cls.a)] = True + cls.ma = Masked(cls.a, mask=cls.mask_a) + + def check(self, function, *args, **kwargs): + # Check function by comparing to nan-equivalent, with masked + # values set to NaN. + o = function(self.ma, *args, **kwargs) + nanfunc = getattr(np, "nan" + function.__name__) + nanfilled = self.ma.filled(np.nan) + expected = nanfunc(nanfilled, *args, **kwargs) + assert_array_equal(o.filled(np.nan), expected) + assert_array_equal(o.mask, np.isnan(expected)) + # Also check that we can give an output MaskedArray. + out = np.zeros_like(o) + o2 = function(self.ma, *args, out=out, **kwargs) + assert o2 is out + assert_masked_equal(o2, o) + # But that a regular array cannot be used since it has no mask. + with pytest.raises(TypeError): + function(self.ma, *args, out=np.zeros_like(expected), **kwargs) + + @pytest.mark.parametrize("keepdims", [False, True]) + @pytest.mark.parametrize("axis", [None, 0, 1]) + def test_median(self, axis, keepdims): + self.check(np.median, axis=axis, keepdims=keepdims) + + @pytest.mark.parametrize("keepdims", [False, True]) + @pytest.mark.parametrize("axis", [None, 0, 1]) + def test_quantile(self, axis, keepdims): + self.check(np.quantile, q=[0.25, 0.5], axis=axis, keepdims=keepdims) + + def test_quantile_out_of_range(self): + with pytest.raises(ValueError, match="must be in the range"): + np.quantile(self.ma, q=1.5) + + @pytest.mark.parametrize("axis", [None, 0, 1]) + def test_percentile(self, axis): + self.check(np.percentile, q=50, axis=axis) + + +class TestIntDiffFunctions(MaskedArraySetup): + def test_diff(self): + out = np.diff(self.ma) + expected = np.diff(self.a) + expected_mask = self.mask_a[:, 1:] | self.mask_a[:, :-1] + assert_array_equal(out.unmasked, expected) + assert_array_equal(out.mask, expected_mask) + + def test_diff_prepend_append(self): + out = np.diff(self.ma, prepend=Masked(-1, mask=True), append=1) + expected = np.diff(self.a, prepend=-1, append=1.0) + mask = np.concatenate( + [np.ones((2, 1), bool), self.mask_a, np.zeros((2, 1), bool)], axis=-1 + ) + expected_mask = mask[:, 1:] | mask[:, :-1] + assert_array_equal(out.unmasked, expected) + assert_array_equal(out.mask, expected_mask) + + def check_trapezoid(self, func): + ma = self.ma.copy() + ma.mask[1] = False + out = func(ma) + assert_array_equal(out.unmasked, func(self.a)) + assert_array_equal(out.mask, np.array([True, False])) + + def test_trapezoid(self): + self.check_trapezoid(np.trapezoid) + + def test_gradient(self): + out = np.gradient(self.ma) + expected = np.gradient(self.a) + expected_mask = [ + (self.mask_a[1:] | self.mask_a[:-1]).repeat(2, axis=0), + np.stack( + [ + self.mask_a[:, 0] | self.mask_a[:, 1], + self.mask_a[:, 0] | self.mask_a[:, 2], + self.mask_a[:, 1] | self.mask_a[:, 2], + ], + axis=-1, + ), + ] + + for o, x, m in zip(out, expected, expected_mask): + assert_array_equal(o.unmasked, x) + assert_array_equal(o.mask, m) + + +class TestSpaceFunctions: + @classmethod + def setup_class(cls): + cls.a = np.arange(1.0, 7.0).reshape(2, 3) + cls.mask_a = np.array( + [ + [True, False, False], + [False, True, False], + ] + ) + cls.ma = Masked(cls.a, mask=cls.mask_a) + cls.b = np.array([2.5, 10.0, 3.0]) + cls.mask_b = np.array([False, True, False]) + cls.mb = Masked(cls.b, mask=cls.mask_b) + + def check(self, function, *args, **kwargs): + out = function(self.ma, self.mb, 5) + expected = function(self.a, self.b, 5) + expected_mask = np.broadcast_to( + self.mask_a | self.mask_b, expected.shape + ).copy() + # TODO: make implementations that ensure both start and stop masks + # are determined just by their respective point? + if function is np.geomspace: + expected_mask[0] = self.mask_a + else: + expected_mask[-1] = self.mask_b + + assert_array_equal(out.unmasked, expected) + assert_array_equal(out.mask, expected_mask) + + def test_linspace(self): + self.check(np.linspace, 5) + + def test_logspace(self): + self.check(np.logspace, 10) + + def test_geomspace(self): + self.check(np.geomspace, 5) + + +class TestInterpolationFunctions(MaskedArraySetup): + def test_interp(self): + xp = np.arange(5.0) + fp = np.array([1.0, 5.0, 6.0, 19.0, 20.0]) + mask_fp = np.array([False, False, False, True, False]) + mfp = Masked(fp, mask=mask_fp) + x = np.array([1.5, 17.0]) + mask_x = np.array([False, True]) + mx = Masked(x, mask=mask_x) + out = np.interp(mx, xp, mfp) + expected = np.interp(x, xp[~mask_fp], fp[~mask_fp]) + assert_array_equal(out.unmasked, expected) + assert_array_equal(out.mask, mask_x) + + def test_piecewise(self): + condlist = [self.a < 1, self.a >= 1] + out = np.piecewise(self.ma, condlist, [Masked(-1, mask=True), 1.0]) + expected = np.piecewise(self.a, condlist, [-1, 1.0]) + expected_mask = np.piecewise(self.mask_a, condlist, [True, False]) + assert_array_equal(out.unmasked, expected) + assert_array_equal(out.mask, expected_mask) + condlist2 = [self.a < 1, self.a >= 3] + out2 = np.piecewise( + self.ma, + condlist2, + [Masked(-1, True), 1, lambda x: Masked(np.full_like(x, 2.0), mask=~x.mask)], + ) + expected = np.piecewise(self.a, condlist2, [-1, 1, 2]) + expected_mask = np.piecewise( + self.mask_a, condlist2, [True, False, lambda x: ~x] + ) + assert_array_equal(out2.unmasked, expected) + assert_array_equal(out2.mask, expected_mask) + + with pytest.raises(ValueError, match="with 2 condition"): + np.piecewise(self.ma, condlist2, []) + + def test_regression_12978(self): + """Regression tests for https://github.com/astropy/astropy/pull/12978""" + # This case produced incorrect results + mask = [False, True, False] + x = np.array([1, 2, 3]) + xp = Masked(np.array([1, 2, 3]), mask=mask) + fp = Masked(np.array([1, 2, 3]), mask=mask) + result = np.interp(x, xp, fp) + assert_array_equal(result, x) + + # This case raised a ValueError + xp = np.array([1, 3]) + fp = Masked(np.array([1, 3])) + result = np.interp(x, xp, fp) + assert_array_equal(result, x) + + +class TestBincount(MaskedArraySetup): + def test_bincount(self): + i = np.array([1, 1, 2, 3, 2, 4]) + mask_i = np.array([True, False, False, True, False, False]) + mi = Masked(i, mask=mask_i) + out = np.bincount(mi) + expected = np.bincount(i[~mask_i]) + assert_array_equal(out, expected) + w = np.arange(len(i)) + mask_w = np.array([True] + [False] * 5) + mw = Masked(w, mask=mask_w) + out2 = np.bincount(i, mw) + expected = np.bincount(i, w) + expected_mask = np.array([False, True, False, False, False]) + assert_array_equal(out2.unmasked, expected) + assert_array_equal(out2.mask, expected_mask) + + out3 = np.bincount(mi, mw) + expected = np.bincount(i[~mask_i], w[~mask_i]) + expected_mask = np.array([False, False, False, False, False]) + assert_array_equal(out3.unmasked, expected) + assert_array_equal(out3.mask, expected_mask) + + +class TestSortFunctions(MaskedArraySetup): + def test_sort(self): + o = np.sort(self.ma) + expected = self.ma.copy() + expected.sort() + assert_masked_equal(o, expected) + + def test_sort_complex(self): + ma = Masked( + np.array([1 + 2j, 0 + 4j, 3 + 0j, -1 - 1j]), + mask=[True, False, False, False], + ) + o = np.sort_complex(ma) + expected = ma[np.lexsort((ma.unmasked.imag, ma.unmasked.real, ma.mask))] + assert_masked_equal(o, expected) + + def test_partition(self): + o = np.partition(self.ma, 1) + expected = self.ma.copy() + expected.partition(1) + assert_masked_equal(o, expected) + + +class TestStringFunctions: + # More elaborate tests done in test_masked.py + @classmethod + def setup_class(cls): + cls.ma = Masked(np.arange(3), mask=[True, False, False]) + + def test_array2string(self): + out0 = np.array2string(self.ma) + assert out0 == "[— 1 2]" + # Arguments are interpreted as usual. + out1 = np.array2string(self.ma, separator=", ") + assert out1 == "[—, 1, 2]" + # If we do pass in a formatter, though, it should be used. + out2 = np.array2string(self.ma, separator=", ", formatter={"all": hex}) + assert out2 == "[———, 0x1, 0x2]" + # Also as positional argument (no, nobody will do this!) + if NUMPY_LT_2_4: + args = (self.ma, None, None, None, ", ", "", np._NoValue, {"int": hex}) + out3 = np.array2string(*args) + assert out3 == out2 + # But not if the formatter is not relevant for us. + out4 = np.array2string(self.ma, separator=", ", formatter={"float": hex}) + assert out4 == out1 + + def test_array_repr(self): + out = np.array_repr(self.ma) + assert out == "MaskedNDArray([—, 1, 2])" + ma2 = self.ma.astype("f4") + out2 = np.array_repr(ma2) + assert out2 == "MaskedNDArray([——, 1., 2.], dtype=float32)" + + def test_array_str(self): + out = np.array_str(self.ma) + assert out == "[— 1 2]" + + +class TestBitFunctions: + @classmethod + def setup_class(cls): + cls.a = np.array([15, 255, 0], dtype="u1") + cls.mask_a = np.array([False, True, False]) + cls.ma = Masked(cls.a, mask=cls.mask_a) + cls.b = np.unpackbits(cls.a).reshape(6, 4) + cls.mask_b = np.array([False] * 15 + [True, True] + [False] * 7).reshape(6, 4) + cls.mb = Masked(cls.b, mask=cls.mask_b) + + @pytest.mark.parametrize("axis", [None, 1, 0]) + def test_packbits(self, axis): + out = np.packbits(self.mb, axis=axis) + if axis is None: + expected = self.a + else: + expected = np.packbits(self.b, axis=axis) + expected_mask = np.packbits(self.mask_b, axis=axis) > 0 + assert_array_equal(out.unmasked, expected) + assert_array_equal(out.mask, expected_mask) + + def test_unpackbits(self): + out = np.unpackbits(self.ma) + mask = np.where(self.mask_a, np.uint8(255), np.uint8(0)) + expected_mask = np.unpackbits(mask) > 0 + assert_array_equal(out.unmasked, self.b.ravel()) + assert_array_equal(out.mask, expected_mask) + + +class TestIndexFunctions(MaskedArraySetup): + """Does not seem much sense to support these...""" + + def test_unravel_index(self): + with pytest.raises(TypeError): + np.unravel_index(self.ma, 3) + + def test_ravel_multi_index(self): + with pytest.raises(TypeError): + np.ravel_multi_index((self.ma,), 3) + + def test_ix_(self): + with pytest.raises(TypeError): + np.ix_(self.ma) + + +class TestDtypeFunctions(MaskedArraySetup): + def check(self, function, *args, **kwargs): + out = function(self.ma, *args, **kwargs) + expected = function(self.a, *args, **kwargs) + assert out == expected + + def test_common_type(self): + self.check(np.common_type) + + def test_result_type(self): + self.check(np.result_type) + + def test_can_cast(self): + self.check(np.can_cast, self.a.dtype) + self.check(np.can_cast, "f4") + + def test_min_scalar_type(self): + out = np.min_scalar_type(self.ma[0, 0]) + expected = np.min_scalar_type(self.a[0, 0]) + assert out == expected + + def test_iscomplexobj(self): + self.check(np.iscomplexobj) + + def test_isrealobj(self): + self.check(np.isrealobj) + + +class TestMeshGrid(MaskedArraySetup): + def test_meshgrid(self): + a = np.arange(1.0, 4.0) + mask_a = np.array([True, False, False]) + ma = Masked(a, mask=mask_a) + b = np.array([2.5, 10.0, 3.0, 4.0]) + mask_b = np.array([False, True, False, True]) + mb = Masked(b, mask=mask_b) + oa, ob = np.meshgrid(ma, mb) + xa, xb = np.broadcast_arrays(a, b[:, np.newaxis]) + ma, mb = np.broadcast_arrays(mask_a, mask_b[:, np.newaxis]) + for o, x, m in ((oa, xa, ma), (ob, xb, mb)): + assert_array_equal(o.unmasked, x) + assert_array_equal(o.mask, m) + + +class TestMemoryFunctions(MaskedArraySetup): + def test_shares_memory(self): + assert np.shares_memory(self.ma, self.ma.unmasked) + assert not np.shares_memory(self.ma, self.ma.mask) + + def test_may_share_memory(self): + assert np.may_share_memory(self.ma, self.ma.unmasked) + assert not np.may_share_memory(self.ma, self.ma.mask) + + +class TestDatetimeFunctions: + # Could in principle support np.is_busday, np.busday_count, np.busday_offset. + @classmethod + def setup_class(cls): + cls.a = np.array(["2020-12-31", "2021-01-01", "2021-01-02"], dtype="M") + cls.mask_a = np.array([False, True, False]) + cls.ma = Masked(cls.a, mask=cls.mask_a) + cls.b = np.array([["2021-01-07"], ["2021-01-31"]], dtype="M") + cls.mask_b = np.array([[False], [True]]) + cls.mb = Masked(cls.b, mask=cls.mask_b) + + def test_datetime_as_string(self): + out = np.datetime_as_string(self.ma) + expected = np.datetime_as_string(self.a) + assert_array_equal(out.unmasked, expected) + assert_array_equal(out.mask, self.mask_a) + + +@pytest.mark.filterwarnings("ignore:all-nan") +class TestNaNFunctions: + def setup_class(self): + self.a = np.array( + [ + [np.nan, np.nan, 3.0], + [4.0, 5.0, 6.0], + ] + ) + self.mask_a = np.array( + [ + [True, False, False], + [False, True, False], + ] + ) + self.b = np.arange(1, 7).reshape(2, 3) + self.mask_b = self.mask_a + self.ma = Masked(self.a, mask=self.mask_a) + self.mb = Masked(self.b, mask=self.mask_b) + + def check(self, function, exact_fill_value=None, masked_result=True, **kwargs): + result = function(self.ma, **kwargs) + expected_data = function(self.ma.filled(np.nan), **kwargs) + expected_mask = np.isnan(expected_data) + if masked_result: + assert isinstance(result, Masked) + assert_array_equal(result.mask, expected_mask) + assert np.all(result == expected_data) + else: + assert not isinstance(result, Masked) + assert_array_equal(result, expected_data) + assert not np.any(expected_mask) + out = np.zeros_like(result) + result2 = function(self.ma, out=out, **kwargs) + assert result2 is out + assert_array_equal(result2, result) + + def check_arg(self, function, **kwargs): + # arg functions do not have an 'out' argument, so just test directly. + result = function(self.ma, **kwargs) + assert not isinstance(result, Masked) + expected = function(self.ma.filled(np.nan), **kwargs) + assert_array_equal(result, expected) + + def test_nanmin(self): + self.check(np.nanmin) + self.check(np.nanmin, axis=0) + self.check(np.nanmin, axis=1) + resi = np.nanmin(self.mb, axis=1) + assert_array_equal(resi.unmasked, np.array([2, 4])) + assert_array_equal(resi.mask, np.array([False, False])) + + def test_nanmax(self): + self.check(np.nanmax) + + def test_nanargmin(self): + self.check_arg(np.nanargmin) + self.check_arg(np.nanargmin, axis=1) + + def test_nanargmax(self): + self.check_arg(np.nanargmax) + + def test_nansum(self): + self.check(np.nansum, masked_result=False) + resi = np.nansum(self.mb, axis=1) + assert not isinstance(resi, Masked) + assert_array_equal(resi, np.array([5, 10])) + + def test_nanprod(self): + self.check(np.nanprod, masked_result=False) + resi = np.nanprod(self.mb, axis=1) + assert not isinstance(resi, Masked) + assert_array_equal(resi, np.array([6, 24])) + + def test_nancumsum(self): + self.check(np.nancumsum, masked_result=False) + resi = np.nancumsum(self.mb, axis=1) + assert not isinstance(resi, Masked) + assert_array_equal(resi, np.array([[0, 2, 5], [4, 4, 10]])) + + def test_nancumprod(self): + self.check(np.nancumprod, masked_result=False) + resi = np.nancumprod(self.mb, axis=1) + assert not isinstance(resi, Masked) + assert_array_equal(resi, np.array([[1, 2, 6], [4, 4, 24]])) + + def test_nanmean(self): + self.check(np.nanmean) + resi = np.nanmean(self.mb, axis=1) + assert_array_equal(resi.unmasked, np.mean(self.mb, axis=1).unmasked) + assert_array_equal(resi.mask, np.array([False, False])) + + def test_nanvar(self): + self.check(np.nanvar) + self.check(np.nanvar, ddof=1) + + def test_nanstd(self): + self.check(np.nanstd) + + def test_nanmedian(self): + self.check(np.nanmedian) + + def test_nanquantile(self): + self.check(np.nanquantile, q=0.5) + + def test_nanpercentile(self): + self.check(np.nanpercentile, q=50) + + +class TestArraySetOps: + """Tests based on those from numpy.ma.tests.test_extras. + + Adjusted to take into account that comparing masked values should + result in masked equality. + + """ + + @classmethod + def setup_class(cls): + # Setup for unique (names as in unique_all NamedTuple) + # input data, unique values, indices in data to those, + # inverse indices in values to reconstruct data, counts. + cls.data = Masked([1, 1, 1, 2, 2, 3], mask=[0, 0, 1, 0, 1, 0]) + cls.values = Masked([1, 2, 3, 1, 2], mask=[0, 0, 0, 1, 1]) + cls.indices = np.array([0, 3, 5, 2, 4]) + cls.inverse_indices = np.array([0, 0, 3, 1, 4, 2]) + cls.counts = np.array([2, 1, 1, 1, 1]) + + @pytest.mark.parametrize("dtype", [int, float, object]) + def test_unique(self, dtype): + values, indices, inverse_indices = np.unique( + self.data.astype(dtype), return_index=True, return_inverse=True + ) + assert_masked_equal(values, self.values.astype(dtype)) + assert_array_equal(indices, self.indices) + assert_array_equal(inverse_indices, self.inverse_indices) + # All masked + data2 = Masked([2, 1, 3], mask=True) + values2, indices2, inverse_indices2 = np.unique( + data2.astype(dtype), return_index=True, return_inverse=True + ) + expected_values2 = Masked([1, 2, 3], mask=True) + assert_masked_equal(values2, expected_values2.astype(dtype)) + assert_array_equal(indices2, [1, 0, 2]) + assert_array_equal(inverse_indices2, [1, 0, 2]) + + def check_unique(self, test): + for name in test._fields: + assert_array_equal(getattr(test, name), getattr(self, name)) + + def test_unique_all(self): + test = np.unique_all(self.data) + assert len(test) == 4 + self.check_unique(test) + + def test_unique_counts(self): + test = np.unique_counts(self.data) + assert len(test) == 2 + self.check_unique(test) + + def test_unique_inverse(self): + test = np.unique_inverse(self.data) + assert len(test) == 2 + self.check_unique(test) + + def test_unique_values(self): + test = np.unique_values(self.data) + assert isinstance(test, Masked) + assert_array_equal(test, self.values) + + def test_ediff1d(self): + x = Masked(np.arange(5), mask=[1, 0, 0, 0, 1]) + control = Masked([1, 1, 1, 1], mask=[1, 0, 0, 1]) + test = np.ediff1d(x) + assert_masked_equal(test, control) + # Test ediff1d w/ to_begin + test2 = np.ediff1d(x, to_begin=Masked(10, mask=True)) + control2 = Masked([10, 1, 1, 1, 1], mask=[1, 1, 0, 0, 1]) + assert_masked_equal(test2, control2) + test3 = np.ediff1d(x, to_begin=[1, 2, 3]) + control3 = Masked([1, 2, 3, 1, 1, 1, 1], mask=[0, 0, 0, 1, 0, 0, 1]) + assert_masked_equal(test3, control3) + # Test ediff1d w/ to_end + test4 = np.ediff1d(x, to_end=Masked(10, mask=True)) + control4 = Masked([1, 1, 1, 1, 10], mask=[1, 0, 0, 1, 1]) + assert_masked_equal(test4, control4) + test5 = np.ediff1d(x, to_end=[1, 2, 3]) + control5 = Masked([1, 1, 1, 1, 1, 2, 3], mask=[1, 0, 0, 1, 0, 0, 0]) + assert_masked_equal(test5, control5) + # Test ediff1d w/ to_begin and to_end + test6 = np.ediff1d( + x, to_end=Masked(10, mask=True), to_begin=Masked(20, mask=True) + ) + control6 = Masked([20, 1, 1, 1, 1, 10], mask=[1, 1, 0, 0, 1, 1]) + assert_masked_equal(test6, control6) + test7 = np.ediff1d(x, to_end=[1, 2, 3], to_begin=Masked(10, mask=True)) + control7 = Masked([10, 1, 1, 1, 1, 1, 2, 3], mask=[1, 1, 0, 0, 1, 0, 0, 0]) + assert_masked_equal(test7, control7) + # Test ediff1d w/ a ndarray + test8 = np.ediff1d( + np.arange(5), to_end=Masked(10, mask=True), to_begin=Masked(20, mask=True) + ) + control8 = Masked([20, 1, 1, 1, 1, 10], mask=[1, 0, 0, 0, 0, 1]) + assert_masked_equal(test8, control8) + + def test_intersect1d(self): + x = Masked([1, 3, 3, 3, 4], mask=[0, 0, 0, 1, 1]) + y = Masked([3, 1, 1, 1, 4], mask=[0, 0, 0, 1, 1]) + test = np.intersect1d(x, y) + control = Masked([1, 3, 4], mask=[0, 0, 1]) + assert_masked_equal(test, control) + + def test_setxor1d(self): + a = Masked([1, 2, 5, 7, -1], mask=[0, 0, 0, 0, 1]) + b = Masked([1, 2, 3, 4, 5, -1], mask=[0, 0, 0, 0, 0, 1]) + test = np.setxor1d(a, b) + assert_masked_equal(test, Masked([3, 4, 7])) + a = Masked([1, 2, 5, 7, -1], mask=[0, 0, 0, 0, 1]) + b = [1, 2, 3, 4, 5] + test = np.setxor1d(a, b) + assert_masked_equal(test, Masked([3, 4, 7, -1], mask=[0, 0, 0, 1])) + a = Masked([1, 8, 2, 3], mask=[0, 1, 0, 0]) + b = Masked([6, 5, 4, 8], mask=[0, 0, 0, 1]) + test = np.setxor1d(a, b) + assert_masked_equal(test, Masked([1, 2, 3, 4, 5, 6])) + assert_masked_equal(np.setxor1d(Masked([]), []), Masked([])) + + @pytest.mark.parametrize("dtype", [int, float, object]) + def test_isin(self, dtype): + a = np.arange(24).reshape((2, 3, 4)) + mask = np.zeros(a.shape, bool) + mask[1, 2, 0] = 1 # 20 + mask[1, 2, 1] = 1 # 21 + a = Masked(a, mask=mask) + b = Masked([0, 10, 20, 30, 1, 3, 11, 21, 33], mask=[0, 1, 0, 1, 0, 1, 0, 1, 0]) + # unmasked 0, 20, 1, 11, 33, masked 10, 30, 3, 21 + ec = np.zeros((2, 3, 4), dtype=bool) + ec[0, 0, 0] = True # 0 + ec[0, 0, 1] = True # 1 + ec[0, 2, 3] = True # 11 + ec[1, 2, 1] = True # masked 21 + ec = Masked(ec, mask) + c = np.isin(a.astype(dtype), b.astype(dtype)) + assert_masked_equal(c, ec) + + @pytest.mark.skipif(not NUMPY_LT_2_4, reason="np.in1d was removed in numpy 2.4") + @pytest.mark.filterwarnings("ignore:in1d.*deprecated") + def test_in1d(self): + # Once we require numpy>=2.4, these tests should be joined with np.isin. + a = Masked([1, 2, 5, -2, -1], mask=[0, 0, 0, 1, 1]) + b = Masked([1, 2, 3, 4, 5, -2], mask=[0, 0, 0, 0, 0, 1]) + test = np.in1d(a, b) # noqa: NPY201 + assert_masked_equal(test, Masked([True, True, True, True, False], mask=a.mask)) + assert_array_equal(np.in1d(a, b, invert=True), ~test) # noqa: NPY201 + + a = Masked([5, 5, 2, -2, -1], mask=[0, 0, 0, 1, 1]) + b = Masked([1, 5, -1], mask=[0, 0, 1]) + test = np.in1d(a, b) # noqa: NPY201 + assert_masked_equal(test, Masked([True, True, False, False, True], mask=a.mask)) + + assert_masked_equal(np.in1d(Masked([]), []), Masked([])) # noqa: NPY201 + assert_masked_equal(np.in1d(Masked([]), [], invert=True), Masked([])) # noqa: NPY201 + + @pytest.mark.skipif(not NUMPY_LT_2_4, reason="np.in1d was removed in numpy 2.4") + def test_in1d_kind_table_error(self): + with pytest.raises(ValueError, match="'table' method is not supported"): + np.in1d(Masked([1, 2, 3]), [4, 5], kind="table") # noqa: NPY201 + + @pytest.mark.parametrize("dtype", [int, float, object]) + def test_union1d(self, dtype): + a = Masked([1, 2, 5, 7, 5, 5], mask=[0, 0, 0, 0, 0, 1]) + b = Masked([1, 2, 3, 4, 5, 6], mask=[0, 0, 0, 0, 0, 1]) + control = Masked([1, 2, 3, 4, 5, 7, 5, 6], mask=[0, 0, 0, 0, 0, 0, 1, 1]) + test = np.union1d(a.astype(dtype), b.astype(dtype)) + assert_masked_equal(test, control.astype(dtype)) + + assert_masked_equal(np.union1d(Masked([]), []), Masked([])) + + def test_setdiff1d(self): + a = Masked([6, 5, 4, 7, 7, 1, 2, 1], mask=[0, 0, 0, 0, 0, 0, 0, 1]) + b = np.array([2, 4, 3, 3, 2, 1, 5]) + test = np.setdiff1d(a, b) + assert_masked_equal(test, Masked([6, 7, 1], mask=[0, 0, 1])) + b2 = Masked(b, mask=[1, 1, 1, 1, 0, 0, 0]) + test2 = np.setdiff1d(a, b2) + assert_masked_equal(test2, Masked([4, 6, 7, 1], mask=[0, 0, 0, 1])) + + a = Masked(np.array([], dtype=np.uint32), mask=[]) + assert np.setdiff1d(a, []).dtype == np.uint32 + + a = Masked(["a", "b", "c"], mask=[0, 1, 1]) + b = Masked(["a", "b", "s"], mask=[0, 1, 1]) + test3 = np.setdiff1d(a, b, assume_unique=True) + assert_masked_equal(test3, Masked(["c"], True)) + + +# Get wrapped and covered functions. +all_wrapped_functions = get_wrapped_functions(np) +tested_functions = get_covered_functions(locals()) +# Create set of untested functions. +untested_functions = set() + +deprecated_functions = set() + +untested_functions |= deprecated_functions +io_functions = {np.save, np.savez, np.savetxt, np.savez_compressed} +untested_functions |= io_functions +poly_functions = { + np.poly, np.polyadd, np.polyder, np.polydiv, np.polyfit, np.polyint, + np.polymul, np.polysub, np.polyval, np.roots, np.vander, +} # fmt: skip +untested_functions |= poly_functions + + +def test_basic_testing_completeness(): + declared_functions = tested_functions | IGNORED_FUNCTIONS | UNSUPPORTED_FUNCTIONS + if NUMPY_LT_2_2: + declared_functions |= SUPPORTED_NEP35_FUNCTIONS + + assert declared_functions == all_wrapped_functions + + +@pytest.mark.xfail(reason="coverage not completely set up yet") +def test_testing_completeness(): + assert not tested_functions.intersection(untested_functions) + assert all_wrapped_functions == (tested_functions | untested_functions) + + +class TestFunctionHelpersCompleteness: + @pytest.mark.parametrize( + "one, two", + list( + itertools.combinations( + ( + MASKED_SAFE_FUNCTIONS, + UNSUPPORTED_FUNCTIONS, + set(APPLY_TO_BOTH_FUNCTIONS.keys()), + set(DISPATCHED_FUNCTIONS.keys()), + ), + 2, + ), + ), + ) + def test_no_duplicates(self, one, two): + assert not one.intersection(two) + + def test_all_included(self): + included_in_helpers = ( + MASKED_SAFE_FUNCTIONS + | UNSUPPORTED_FUNCTIONS + | set(APPLY_TO_BOTH_FUNCTIONS.keys()) + | set(DISPATCHED_FUNCTIONS.keys()) + ) + assert all_wrapped_functions == included_in_helpers + + @pytest.mark.xfail(reason="coverage not completely set up yet") + def test_ignored_are_untested(self): + assert IGNORED_FUNCTIONS == untested_functions + + +@pytest.mark.parametrize( + "target, helper", + sorted( + DISPATCHED_FUNCTIONS.items(), + key=lambda items: items[0].__name__, + ), + ids=lambda func: func.__name__, +) +class TestFunctionHelpersSignatureCompatibility(CheckSignatureCompatibilityBase): + pass diff --git a/astropy/utils/masked/tests/test_functions.py b/astropy/utils/masked/tests/test_functions.py new file mode 100644 index 000000000000..c867903f7a3b --- /dev/null +++ b/astropy/utils/masked/tests/test_functions.py @@ -0,0 +1,630 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +"""Test numpy functions and ufuncs on Masked arrays and quantities. + +The tests here are fairly detailed but do not aim for complete +coverage. Complete coverage of all numpy functions is done +with less detailed tests in test_function_helpers. +""" + +# We generally call the ufunc in the tests, since those can take +# all ufunc arguments (like axes), but also test whether we can +# mask the exceptions and warnings from the wrappers in erfa itself. +import erfa +import erfa.ufunc as erfa_ufunc +import numpy as np +import pytest +from numpy.testing import assert_allclose, assert_array_equal + +from astropy import units as u +from astropy.units import Quantity +from astropy.utils import minversion +from astropy.utils.masked.core import Masked + +from .test_masked import ( + LongitudeSetup, + MaskedArraySetup, + QuantitySetup, + assert_masked_equal, +) + + +class MaskedUfuncTests(MaskedArraySetup): + @pytest.mark.parametrize( + "ufunc", (np.add, np.subtract, np.divide, np.arctan2, np.minimum) + ) + @pytest.mark.parametrize("a, b", [("ma", "mb"), ("ma", "b"), ("a", "mb")]) + def test_2op_ufunc(self, ufunc, a, b): + a, b = getattr(self, a), getattr(self, b) + mask_a = getattr(a, "mask", np.zeros(a.shape, bool)) + mask_b = getattr(b, "mask", np.zeros(b.shape, bool)) + result = ufunc(a, b) + expected_data = ufunc(self.a, self.b) + expected_mask = mask_a | mask_b + # Note: assert_array_equal also checks type, i.e., that, e.g., + # Longitude decays into an Angle. + assert_array_equal(result.unmasked, expected_data) + assert_array_equal(result.mask, expected_mask) + + out = Masked(np.zeros_like(result.unmasked)) + result2 = ufunc(a, b, out=out) + assert result2 is out + assert_masked_equal(result2, result) + + @pytest.mark.parametrize("base_mask", [True, False]) + def test_ufunc_inplace_where(self, base_mask): + # Construct base filled with -9 and base_mask (copying to get unit/class). + base = self.ma.copy() + base.unmasked.view(np.ndarray)[...] = -9.0 + base._mask[...] = base_mask + out = base.copy() + where = np.array([[True, False, False], [False, True, False]]) + result = np.add(self.ma, self.mb, out=out, where=where) + # Direct checks. + assert np.all(result.unmasked[~where] == base.unmasked[0, 0]) + assert np.all(result.unmasked[where] == (self.a + self.b)[where]) + # Full comparison. + expected = base.unmasked.copy() + np.add(self.a, self.b, out=expected, where=where) + expected_mask = base.mask.copy() + np.logical_or(self.mask_a, self.mask_b, out=expected_mask, where=where) + assert_array_equal(result.unmasked, expected) + assert_array_equal(result.mask, expected_mask) + + @pytest.mark.parametrize("base_mask", [True, False]) + def test_ufunc_inplace_masked_where(self, base_mask): + base = self.ma.copy() + base.unmasked.view(np.ndarray)[...] = -9.0 + base._mask[...] = base_mask + out = base.copy() + where = Masked( + [[True, False, True], [False, False, True]], + mask=[[True, False, False], [True, False, True]], + ) + result = np.add(self.ma, self.mb, out=out, where=where) + # Direct checks. + assert np.all(result.unmasked[~where.unmasked] == base.unmasked[0, 0]) + assert np.all( + result.unmasked[where.unmasked] == (self.a + self.b)[where.unmasked] + ) + assert np.all(result.mask[where.mask]) + assert np.all(result.mask[~where.mask & ~where.unmasked] == base.mask[0, 0]) + assert np.all( + result.mask[~where.mask & where.unmasked] + == (self.mask_a | self.mask_b)[~where.mask & where.unmasked] + ) + # Full comparison. + expected = base.unmasked.copy() + np.add(self.a, self.b, out=expected, where=where.unmasked) + expected_mask = base.mask.copy() + np.logical_or(self.mask_a, self.mask_b, out=expected_mask, where=where.unmasked) + expected_mask |= where.mask + assert_array_equal(result.unmasked, expected) + assert_array_equal(result.mask, expected_mask) + + def test_ufunc_inplace_no_masked_input(self): + a_b = np.add(self.a, self.b) + out = Masked(np.zeros_like(a_b)) + result = np.add(self.a, self.b, out=out) + assert result is out + assert_array_equal(result.unmasked, a_b) + assert_array_equal(result.mask, np.zeros(a_b.shape, bool)) + + def test_ufunc_inplace_error(self): + # Output is not masked. + out = np.zeros(self.ma.shape) + with pytest.raises(TypeError): + np.add(self.ma, self.mb, out=out) + + def test_ufunc_inplace_error_masked_where(self): + # Input and output are not masked, but where is. + out = self.a.copy() + with pytest.raises(TypeError): + np.add(self.a, self.b, out=out, where=Masked(True, mask=True)) + + @pytest.mark.parametrize("ufunc", (np.add.outer, np.minimum.outer)) + @pytest.mark.parametrize("a, b", [("ma", "mb"), ("ma", "b"), ("a", "mb")]) + def test_2op_ufunc_outer(self, ufunc, a, b): + a, b = getattr(self, a), getattr(self, b) + mask_a = getattr(a, "mask", np.zeros(a.shape, bool)) + mask_b = getattr(b, "mask", np.zeros(b.shape, bool)) + result = ufunc(a, b) + expected_data = ufunc(self.a, self.b) + expected_mask = np.logical_or.outer(mask_a, mask_b) + # Note: assert_array_equal also checks type, i.e., that, e.g., + # Longitude decays into an Angle. + assert_array_equal(result.unmasked, expected_data) + assert_array_equal(result.mask, expected_mask) + + out = Masked(np.zeros_like(result.unmasked)) + result2 = ufunc(a, b, out=out) + assert result2 is out + assert_masked_equal(result2, result) + + @pytest.mark.parametrize("ufunc", (np.add.outer, np.minimum.outer)) + def test_2op_ufunc_outer_no_masked_input(self, ufunc): + expected_data = ufunc(self.a, self.b) + out = Masked(np.zeros_like(expected_data), True) + result = ufunc(self.a, self.b, out=out) + assert_array_equal(out.unmasked, expected_data) + assert_array_equal(out.mask, np.zeros(out.shape, dtype=bool)) + + def test_3op_ufunc(self): + ma_mb = np.clip(self.ma, self.b, self.c) + expected_data = np.clip(self.a, self.b, self.c) + expected_mask = self.mask_a + assert_array_equal(ma_mb.unmasked, expected_data) + assert_array_equal(ma_mb.mask, expected_mask) + + def test_multi_op_ufunc(self): + mask = [True, False, False] + iy = Masked([2000, 2001, 2002], mask=mask) + im = Masked([1, 2, 3], mask=mask) + idy = Masked([10, 20, 25], mask=mask) + ihr = Masked([11, 12, 13], mask=[False, False, True]) + imn = np.array([50, 51, 52]) + isc = np.array([12.5, 13.6, 14.7]) + result = erfa_ufunc.dtf2d("utc", iy, im, idy, ihr, imn, isc) + # Also test scalar + result0 = erfa_ufunc.dtf2d("utc", iy[0], im[0], idy[0], ihr[0], imn[0], isc[0]) + expected = erfa_ufunc.dtf2d( + "utc", iy.unmasked, im.unmasked, idy.unmasked, ihr.unmasked, imn, isc + ) + expected_mask = np.array([True, False, True]) + for res, res0, exp in zip(result, result0, expected): + assert_array_equal(res.unmasked, exp) + assert_array_equal(res.mask, expected_mask) + assert res0.unmasked == exp[0] + assert res0.mask == expected_mask[0] + + def test_erfa_pdp_with_out(self): + p = np.arange(9.0).reshape(3, 3) + mp = Masked(p) + mp.mask[1, 2] = True + out = Masked(np.empty(3), mask=True) + result = erfa_ufunc.pdp(mp, mp, out=out) + assert result is out + assert_array_equal(result.unmasked, erfa_ufunc.pdp(p, p)) + assert_array_equal(result.mask, [False, True, False]) + # With axes just for the inputs. + axes = [0, 1] + result2 = erfa_ufunc.pdp(mp, mp, out=out, axes=axes) + assert result2 is out + assert_array_equal(result2.unmasked, erfa_ufunc.pdp(p, p, axes=axes)) + assert_array_equal(result2.mask, [False, True, True]) + + @pytest.mark.parametrize("kwargs", [{}, dict(axis=0), dict(axes=[0])]) + def test_erfa_p2s_with_out(self, kwargs): + p = np.arange(9.0).reshape(3, 3) + mp = Masked(p) + mp.mask[1, 2] = True + # Outputs are theta, phi, r. + outs = tuple(Masked(np.empty(3), mask=True) for _ in range(3)) + masks = tuple(out.mask for out in outs) + results = erfa_ufunc.p2s(mp, out=outs, **kwargs) + assert len(results) == 3 + expected = erfa_ufunc.p2s(mp.unmasked, **kwargs) + expected_mask = mp.mask.any(0 if kwargs else -1) + for a, b, m, x in zip(results, outs, masks, expected): + assert a is b + assert a.mask is m + assert_array_equal(a.unmasked, x) + assert_array_equal(a.mask, expected_mask) + + def test_erfa_rxp(self): + # Regression tests for gh-16116 + m = Masked(np.eye(3)) + v = Masked(np.arange(6).reshape(2, 3)) + rxp1 = erfa_ufunc.rxp(m, v) + exp = erfa_ufunc.rxp(m.unmasked, v.unmasked) + assert_array_equal(rxp1.unmasked, exp) + assert_array_equal(rxp1.mask, False) + v.mask[0, 0] = True + rxp2 = erfa_ufunc.rxp(m, v) + assert_array_equal(rxp2.unmasked, exp) + assert_array_equal(rxp2.mask, [[True] * 3, [False] * 3]) + m.mask[1, 1] = True + v.mask[...] = False + rxp3 = erfa_ufunc.rxp(m, v) + assert_array_equal(rxp3.unmasked, exp) + assert_array_equal(rxp3.mask, True) + + def test_erfa_rxr_axes(self): + m1 = Masked(np.arange(27.0).reshape(3, 3, 3)) + m2 = Masked(np.arange(-27.0, 0.0).reshape(3, 3, 3)) + rxr1 = erfa_ufunc.rxr(m1, m2) + exp = erfa_ufunc.rxr(m1.unmasked, m2.unmasked) + assert_array_equal(rxr1.unmasked, exp) + assert_array_equal(rxr1.mask, False) + m1.mask[0, 1, 2] = True + rxr2 = erfa_ufunc.rxr(m1, m2) + assert_array_equal(rxr2.unmasked, exp) + assert np.all(rxr2.mask == [[[True]], [[False]], [[False]]]) + axes = [(0, 2), (-2, -1), (0, 1)] + rxr3 = erfa_ufunc.rxr(m1, m2, axes=axes) + exp3 = erfa_ufunc.rxr(m1.unmasked, m2.unmasked, axes=axes) + assert_array_equal(rxr3.unmasked, exp3) + assert np.all(rxr3.mask == [False, True, False]) + + @pytest.mark.parametrize("axis", (0, 1, None)) + def test_add_reduce(self, axis): + ma_reduce = np.add.reduce(self.ma, axis=axis) + expected_data = np.add.reduce(self.a, axis=axis) + expected_mask = np.logical_or.reduce(self.ma.mask, axis=axis) + assert_array_equal(ma_reduce.unmasked, expected_data) + assert_array_equal(ma_reduce.mask, expected_mask) + + out = Masked(np.zeros_like(ma_reduce.unmasked), np.ones_like(ma_reduce.mask)) + ma_reduce2 = np.add.reduce(self.ma, axis=axis, out=out) + assert ma_reduce2 is out + assert_masked_equal(ma_reduce2, ma_reduce) + + def test_add_reduce_no_masked_input(self): + a_reduce = np.add.reduce(self.a, axis=0) + out = Masked(np.zeros_like(a_reduce), np.ones(a_reduce.shape, bool)) + result = np.add.reduce(self.a, axis=0, out=out) + assert result is out + assert_array_equal(out.unmasked, a_reduce) + assert_array_equal(out.mask, False) + # Also try with where (which should have different path) + where = np.array([[True, False, False], [True, True, False]]) + a_reduce2 = np.add.reduce(self.a, axis=0, where=where) + result2 = np.add.reduce(self.a, axis=0, out=out, where=where) + assert result2 is out + assert_array_equal(out.unmasked, a_reduce2) + assert_array_equal(out.mask, [False, False, True]) + + @pytest.mark.parametrize("axis", (0, 1, None)) + def test_minimum_reduce(self, axis): + ma_reduce = np.minimum.reduce(self.ma, axis=axis) + expected_data = np.minimum.reduce(self.a, axis=axis) + expected_mask = np.logical_or.reduce(self.ma.mask, axis=axis) + assert_array_equal(ma_reduce.unmasked, expected_data) + assert_array_equal(ma_reduce.mask, expected_mask) + + @pytest.mark.parametrize("axis", (0, 1, None)) + def test_maximum_reduce(self, axis): + ma_reduce = np.maximum.reduce(self.ma, axis=axis) + expected_data = np.maximum.reduce(self.a, axis=axis) + expected_mask = np.logical_or.reduce(self.ma.mask, axis=axis) + assert_array_equal(ma_reduce.unmasked, expected_data) + assert_array_equal(ma_reduce.mask, expected_mask) + + +class TestMaskedArrayUfuncs(MaskedUfuncTests): + # multiply.reduce does not work with units, so test only for plain array. + # Similarly, modf only works for dimensionless, but we are using it just + # to check multiple outputs get the right mask. + @pytest.mark.parametrize("axis", (0, 1, None)) + def test_multiply_reduce(self, axis): + ma_reduce = np.multiply.reduce(self.ma, axis=axis) + expected_data = np.multiply.reduce(self.a, axis=axis) + expected_mask = np.logical_or.reduce(self.ma.mask, axis=axis) + assert_array_equal(ma_reduce.unmasked, expected_data) + assert_array_equal(ma_reduce.mask, expected_mask) + + def test_ufunc_two_out(self): + out0 = np.empty_like(self.ma) + out0_mask = out0.mask + out1 = np.empty_like(self.ma) + out1_mask = out1.mask + res0, res1 = np.modf(self.ma, out=(out0, out1)) + assert res0 is out0 + assert res1 is out1 + assert out0.mask is out0_mask + assert out1.mask is out1_mask + assert_array_equal(out0.mask, self.mask_a) + assert_array_equal(out1.mask, self.mask_a) + + def test_ufunc_not_implemented_for_other(self): + """ + If the unmasked operation returns NotImplemented, this + should lead to a TypeError also for the masked version. + """ + a = np.array([1, 2]) + b = 3 * u.m + with pytest.raises(TypeError): + a & b + + ma = Masked(a) + with pytest.raises(TypeError): + ma & b + + +class TestMaskedQuantityUfuncs(MaskedUfuncTests, QuantitySetup): + def test_ufunc_inplace_error2(self): + out = Masked(np.zeros(self.ma.shape)) + with pytest.raises(TypeError): + np.add(self.ma, self.mb, out=out) + + +class TestMaskedLongitudeUfuncs(MaskedUfuncTests, LongitudeSetup): + def test_ufunc_inplace_quantity_initial(self): + out = Masked(np.zeros(self.ma.shape) << u.m) + result = np.add(self.ma, self.mb, out=out) + assert result is out + expected = np.add(self.ma, self.mb).view(Quantity) + assert_masked_equal(result, expected) + + +class TestMaskedArrayConcatenation(MaskedArraySetup): + def test_concatenate(self): + mb = self.mb[np.newaxis] + concat_a_b = np.concatenate((self.ma, mb), axis=0) + expected_data = np.concatenate((self.a, self.b[np.newaxis]), axis=0) + expected_mask = np.concatenate((self.mask_a, self.mask_b[np.newaxis]), axis=0) + assert_array_equal(concat_a_b.unmasked, expected_data) + assert_array_equal(concat_a_b.mask, expected_mask) + + def test_concatenate_not_all_masked(self): + mb = self.mb[np.newaxis] + concat_a_b = np.concatenate((self.a, mb), axis=0) + expected_data = np.concatenate((self.a, self.b[np.newaxis]), axis=0) + expected_mask = np.concatenate( + (np.zeros(self.a.shape, bool), self.mask_b[np.newaxis]), axis=0 + ) + assert_array_equal(concat_a_b.unmasked, expected_data) + assert_array_equal(concat_a_b.mask, expected_mask) + + @pytest.mark.parametrize("obj", (1, slice(2, 3))) + def test_insert(self, obj): + mc_in_a = np.insert(self.ma, obj, self.mc, axis=-1) + expected = Masked( + np.insert(self.a, obj, self.c, axis=-1), + np.insert(self.mask_a, obj, self.mask_c, axis=-1), + ) + assert_masked_equal(mc_in_a, expected) + + def test_insert_masked_obj(self): + with pytest.raises(TypeError): + np.insert(self.ma, Masked(1, mask=False), self.mc, axis=-1) + + def test_append(self): + mc_to_a = np.append(self.ma, self.mc, axis=-1) + expected = Masked( + np.append(self.a, self.c, axis=-1), + np.append(self.mask_a, self.mask_c, axis=-1), + ) + assert_masked_equal(mc_to_a, expected) + + +class TestMaskedQuantityConcatenation(TestMaskedArrayConcatenation, QuantitySetup): + pass + + +class TestMaskedLongitudeConcatenation(TestMaskedArrayConcatenation, LongitudeSetup): + pass + + +class TestMaskedArrayBroadcast(MaskedArraySetup): + def test_broadcast_to(self): + shape = self.ma.shape + ba = np.broadcast_to(self.mb, shape, subok=True) + assert ba.shape == shape + assert ba.mask.shape == shape + expected = Masked( + np.broadcast_to(self.mb.unmasked, shape, subok=True), + np.broadcast_to(self.mb.mask, shape, subok=True), + ) + assert_masked_equal(ba, expected) + + def test_broadcast_to_using_apply(self): + # Partially just to ensure we cover the relevant part of _apply. + shape = self.ma.shape + ba = self.mb._apply(np.broadcast_to, shape=shape, subok=True) + assert ba.shape == shape + assert ba.mask.shape == shape + expected = Masked( + np.broadcast_to(self.mb.unmasked, shape, subok=True), + np.broadcast_to(self.mb.mask, shape, subok=True), + ) + assert_masked_equal(ba, expected) + + def test_broadcast_arrays(self): + mb = np.broadcast_arrays(self.ma, self.mb, self.mc, subok=True) + b = np.broadcast_arrays(self.a, self.b, self.c, subok=True) + bm = np.broadcast_arrays(self.mask_a, self.mask_b, self.mask_c) + for mb_, b_, bm_ in zip(mb, b, bm): + assert_array_equal(mb_.unmasked, b_) + assert_array_equal(mb_.mask, bm_) + + def test_broadcast_arrays_not_all_masked(self): + mb = np.broadcast_arrays(self.a, self.mb, self.c, subok=True) + assert_array_equal(mb[0], self.a) + expected1 = np.broadcast_to(self.mb, self.a.shape, subok=True) + assert_masked_equal(mb[1], expected1) + expected2 = np.broadcast_to(self.c, self.a.shape, subok=True) + assert_array_equal(mb[2], expected2) + + def test_broadcast_arrays_subok_false(self): + # subok affects ndarray subclasses but not masking itself. + mb = np.broadcast_arrays(self.ma, self.mb, self.mc, subok=False) + assert all(type(mb_.unmasked) is np.ndarray for mb_ in mb) + b = np.broadcast_arrays(self.a, self.b, self.c, subok=False) + mask_b = np.broadcast_arrays(self.mask_a, self.mask_b, self.mask_c, subok=False) + for mb_, b_, mask_ in zip(mb, b, mask_b): + assert_array_equal(mb_.unmasked, b_) + assert_array_equal(mb_.mask, mask_) + + +class TestMaskedQuantityBroadcast(TestMaskedArrayBroadcast, QuantitySetup): + pass + + +class TestMaskedLongitudeBroadcast(TestMaskedArrayBroadcast, LongitudeSetup): + pass + + +class TestMaskedArrayCalculation(MaskedArraySetup): + @pytest.mark.parametrize("n,axis", [(1, -1), (2, -1), (1, 0)]) + def test_diff(self, n, axis): + mda = np.diff(self.ma, n=n, axis=axis) + expected_data = np.diff(self.a, n, axis) + nan_mask = np.zeros_like(self.a) + nan_mask[self.ma.mask] = np.nan + expected_mask = np.isnan(np.diff(nan_mask, n=n, axis=axis)) + assert_array_equal(mda.unmasked, expected_data) + assert_array_equal(mda.mask, expected_mask) + + def test_diff_explicit(self): + ma = Masked( + np.arange(8.0), [True, False, False, False, False, True, False, False] + ) + mda = np.diff(ma) + assert np.all(mda.unmasked == 1.0) + assert np.all(mda.mask == [True, False, False, False, True, True, False]) + mda = np.diff(ma, n=2) + assert np.all(mda.unmasked == 0.0) + assert np.all(mda.mask == [True, False, False, True, True, True]) + + +class TestMaskedQuantityCalculation(TestMaskedArrayCalculation, QuantitySetup): + pass + + +class TestMaskedLongitudeCalculation(TestMaskedArrayCalculation, LongitudeSetup): + pass + + +class TestMaskedArraySorting(MaskedArraySetup): + @pytest.mark.parametrize("axis", [-1, 0]) + def test_lexsort1(self, axis): + ma_lexsort = np.lexsort((self.ma,), axis=axis) + filled = self.a.copy() + filled[self.mask_a] = 9e9 + expected_data = filled.argsort(axis) + assert_array_equal(ma_lexsort, expected_data) + + @pytest.mark.parametrize("axis", [-1, 0]) + def test_lexsort2(self, axis): + mb = np.broadcast_to(-self.mb, self.ma.shape).copy() + mamb_lexsort = np.lexsort((self.ma, mb), axis=axis) + filled_a = self.ma.filled(9e9) + filled_b = mb.filled(9e9) + expected_ab = np.lexsort((filled_a, filled_b), axis=axis) + assert_array_equal(mamb_lexsort, expected_ab) + mbma_lexsort = np.lexsort((mb, self.ma), axis=axis) + expected_ba = np.lexsort((filled_b, filled_a), axis=axis) + assert_array_equal(mbma_lexsort, expected_ba) + mbma_lexsort2 = np.lexsort(np.stack([mb, self.ma], axis=0), axis=axis) + assert_array_equal(mbma_lexsort2, expected_ba) + + @pytest.mark.parametrize("axis", [-1, 0]) + def test_lexsort_mix(self, axis): + mb = np.broadcast_to(-self.mb, self.ma.shape).copy() + mamb_lexsort = np.lexsort((self.a, mb), axis=axis) + filled_b = mb.filled(9e9) + expected_ab = np.lexsort((self.a, filled_b), axis=axis) + assert_array_equal(mamb_lexsort, expected_ab) + mbma_lexsort = np.lexsort((mb, self.a), axis=axis) + expected_ba = np.lexsort((filled_b, self.a), axis=axis) + assert_array_equal(mbma_lexsort, expected_ba) + mbma_lexsort2 = np.lexsort(np.stack([mb, self.a], axis=0), axis=axis) + assert_array_equal(mbma_lexsort2, expected_ba) + + +class TestStructuredUfuncs: + """Test with structure dtypes, using erfa ufuncs.""" + + def test_erfa_d2tf_tf2d(self): + mask = np.array([True, False, False]) + days = Masked([0.25, 0.875, 0.0625], mask=mask) + sign, ihmsf = erfa_ufunc.d2tf(3, days) + assert_array_equal(sign.mask["sign"], mask) + sign = sign.view("S1") # Like is done by the erfa wrapper. + assert_array_equal(sign.mask, mask) + for name in ihmsf.dtype.names: + assert_array_equal(ihmsf[name].mask, mask) + + # Check roundtrip. + check, stat = erfa_ufunc.tf2d( + sign, ihmsf["h"], ihmsf["m"], ihmsf["s"] + ihmsf["f"] + ) + assert_allclose(check.unmasked, days, atol=1e-3 / 24 / 3600) + assert_array_equal(check.mask, mask) + assert_array_equal(stat.unmasked, 0) + assert_array_equal(stat.mask, mask) + + def test_erfa_astrom(self): + mask = np.array([True, False, False]) + jd2 = Masked([0, 0.401182685, 0.5], mask=mask) + astrom, eo = erfa_ufunc.apci13(2456165.5, jd2) + assert_array_equal(eo.mask, mask) + for n in astrom.dtype.names: + # .T for multi-element fields. + assert np.all(astrom[n].mask.T == mask) + + along = np.array([0.125, 0.25, 0.35]) + # Not going to worry about different masks for different elements. + # In principle aper could propagate just what it needs. + astrom["along"] = Masked([0.125, 0.25, 0.35], mask) + astrom2 = erfa_ufunc.aper(Masked(np.ones(3), [False, True, False]), astrom) + assert_array_equal(astrom2["eral"].unmasked, along + 1.0) + mask2 = mask | [False, True, False] + for n in astrom2.dtype.names: + # .T for multi-element fields. + assert np.all(astrom2[n].mask.T == mask2) + + def test_erfa_atioq(self): + # Regression test for gh-16123, using test from erfa. + astrom, _ = erfa_ufunc.apio13( + 2456384.5, + 0.969254051, + 0.1550675, + -0.527800806, + -1.2345856, + 2738.0, + 2.47230737e-7, + 1.82640464e-6, + 731.0, + 12.8, + 0.59, + 0.55, + ) + astrom = Masked(astrom) + ri = 2.710121572969038991 + di = 0.1729371367218230438 + aob, zob, hob, dob, rob = erfa_ufunc.atioq(ri, di, astrom) + assert isinstance(aob, Masked) + # Really should not need to check the values, since done + # in units/tests/test_quantity_erfa_ufuncs, but why not... + assert_allclose(aob, 0.9233952224895122499e-1, atol=1e-12, rtol=0) + assert_allclose(zob, 1.407758704513549991, atol=1e-12, rtol=0) + assert_allclose(hob, -0.9247619879881698140e-1, atol=1e-12, rtol=0) + assert_allclose(dob, 0.1717653435756234676, atol=1e-12, rtol=0) + assert_allclose(rob, 2.710085107988480746, atol=1e-12, rtol=0) + + +def test_erfa_no_warnings_on_masked_entries(): + # Erfa warns for invalid inputs for some routines. + msg = 'ERFA function "tf2d" yielded {count} of "ihour outside range 0-23"' + ihour1 = [25, 26, 10] + with pytest.warns(erfa.ErfaWarning, match=msg.format(count=2)): + res1 = erfa.tf2d("+", ihour1, 0, 0.0) + # But will not if they are masked. + mask = [True, True, False] + ihour2 = Masked(ihour1, mask) + res2 = erfa.tf2d("+", ihour2, 0, 0.0) + assert_array_equal(res2.unmasked, res1) + assert_array_equal(res2.mask, mask) + # And will count correctly. + mask = [True, False, False] + ihour3 = Masked(ihour1, mask) + count = 1 if minversion(erfa, "2.0.1.1", inclusive=False) else "—" + with pytest.warns(erfa.ErfaWarning, match=msg.format(count=count)): + res3 = erfa.tf2d("+", ihour3, 0, 0.0) + assert_array_equal(res3.unmasked, res1) + assert_array_equal(res3.mask, mask) + + +def test_erfa_no_exceptions_on_masked_entries(): + # Erfa raises exceptions for invalid inputs in some routines. + iday1 = [25, 30] + with pytest.raises(erfa.ErfaError, match="bad day"): + erfa.dat(2000, 2, iday1, 0.0) + # But will not if they are masked. + mask = [False, True] + iday2 = Masked(iday1, mask) + res = erfa.dat(2000, 2, iday2, 0.0) + exp1 = erfa.dat(2000, 2, iday1[0], 0.0) + assert_array_equal(res.unmasked[0], exp1) + assert_array_equal(res.mask, mask) diff --git a/astropy/utils/masked/tests/test_masked.py b/astropy/utils/masked/tests/test_masked.py new file mode 100644 index 000000000000..c9dc2c5c5bc7 --- /dev/null +++ b/astropy/utils/masked/tests/test_masked.py @@ -0,0 +1,1626 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +"""Test masked class initialization, methods, and operators. + +Functions, including ufuncs, are tested in test_functions.py +""" + +import operator +import sys + +import numpy as np +import pytest +from numpy.testing import assert_array_equal + +from astropy import units as u +from astropy.coordinates import Longitude +from astropy.units import Quantity +from astropy.utils.compat import NUMPY_LT_2_1, NUMPY_LT_2_2, NUMPY_LT_2_3 +from astropy.utils.compat.optional_deps import HAS_PLT +from astropy.utils.masked import Masked, MaskedNDArray + + +def assert_masked_equal(a, b): + __tracebackhide__ = True + assert_array_equal(a.unmasked, b.unmasked) + assert_array_equal(a.mask, b.mask) + + +VARIOUS_ITEMS = [(1, 1), slice(None, 1), (), 1] + + +class ArraySetup: + _data_cls = np.ndarray + + @classmethod + def setup_class(cls): + cls.a = np.arange(6.0).reshape(2, 3) + cls.mask_a = np.array([[True, False, False], [False, True, False]]) + cls.b = np.array([-3.0, -2.0, -1.0]) + cls.mask_b = np.array([False, True, False]) + cls.c = np.array([[0.25], [0.5]]) + cls.mask_c = np.array([[False], [True]]) + cls.sdt = np.dtype([("a", "f8"), ("b", "f8")]) + cls.mask_sdt = np.dtype([("a", "?"), ("b", "?")]) + cls.sa = np.array( + [ + [(1.0, 2.0), (3.0, 4.0)], + [(11.0, 12.0), (13.0, 14.0)], + ], + dtype=cls.sdt, + ) + cls.mask_sa = np.array( + [ + [(True, True), (False, False)], + [(False, True), (True, False)], + ], + dtype=cls.mask_sdt, + ) + cls.sb = np.array([(1.0, 2.0), (-3.0, 4.0)], dtype=cls.sdt) + cls.mask_sb = np.array([(True, False), (False, False)], dtype=cls.mask_sdt) + cls.scdt = np.dtype([("sa", "2f8"), ("sb", "i8", (2, 2))]) + cls.sc = np.array( + [ + ([1.0, 2.0], [[1, 2], [3, 4]]), + ([-1.0, -2.0], [[-1, -2], [-3, -4]]), + ], + dtype=cls.scdt, + ) + cls.mask_scdt = np.dtype([("sa", "2?"), ("sb", "?", (2, 2))]) + cls.mask_sc = np.array( + [ + ([True, False], [[False, False], [True, True]]), + ([False, True], [[True, False], [False, True]]), + ], + dtype=cls.mask_scdt, + ) + + +class QuantitySetup(ArraySetup): + _data_cls = Quantity + + @classmethod + def setup_class(cls): + super().setup_class() + cls.a = Quantity(cls.a, u.m) + cls.b = Quantity(cls.b, u.cm) + cls.c = Quantity(cls.c, u.km) + cls.sa = Quantity(cls.sa, u.m, dtype=cls.sdt) + cls.sb = Quantity(cls.sb, u.cm, dtype=cls.sdt) + + +class LongitudeSetup(ArraySetup): + _data_cls = Longitude + + @classmethod + def setup_class(cls): + super().setup_class() + cls.a = Longitude(cls.a, u.deg) + cls.b = Longitude(cls.b, u.deg) + cls.c = Longitude(cls.c, u.deg) + # Note: Longitude does not work on structured arrays, so + # leaving it as regular array (which just reruns some tests). + + +class TestMaskedArrayInitialization(ArraySetup): + def test_simple(self): + ma = Masked(self.a, mask=self.mask_a) + assert isinstance(ma, np.ndarray) + assert isinstance(ma, type(self.a)) + assert isinstance(ma, Masked) + assert_array_equal(ma.unmasked, self.a) + assert_array_equal(ma.mask, self.mask_a) + assert ma.mask is not self.mask_a + assert np.may_share_memory(ma.mask, self.mask_a) + + def test_structured(self): + ma = Masked(self.sa, mask=self.mask_sa) + assert isinstance(ma, np.ndarray) + assert isinstance(ma, type(self.sa)) + assert isinstance(ma, Masked) + assert_array_equal(ma.unmasked, self.sa) + assert_array_equal(ma.mask, self.mask_sa) + assert ma.mask is not self.mask_sa + assert np.may_share_memory(ma.mask, self.mask_sa) + + def test_masked_input(self): + ma = Masked(self.a, mask=self.mask_a) + mab = Masked(ma, mask=self.mask_b) + assert isinstance(mab, np.ndarray) + assert isinstance(mab, type(self.sa)) + assert isinstance(mab, Masked) + assert_array_equal(mab.unmasked, self.a) + assert_array_equal(mab.mask, self.mask_a | self.mask_b) + + +def test_masked_ndarray_init(): + # Note: as a straight ndarray subclass, MaskedNDArray passes on + # the arguments relevant for np.ndarray, not np.array. + a_in = np.arange(3, dtype=int) + m_in = np.array([True, False, False]) + buff = a_in.tobytes() + # Check we're doing things correctly using regular ndarray. + a = np.ndarray(shape=(3,), dtype=int, buffer=buff) + assert_array_equal(a, a_in) + # Check with and without mask. + ma = MaskedNDArray((3,), dtype=int, mask=m_in, buffer=buff) + assert_array_equal(ma.unmasked, a_in) + assert_array_equal(ma.mask, m_in) + ma = MaskedNDArray((3,), dtype=int, buffer=buff) + assert_array_equal(ma.unmasked, a_in) + assert_array_equal(ma.mask, np.zeros(3, bool)) + + +def test_cannot_initialize_with_masked(): + with pytest.raises(ValueError, match="cannot handle np.ma.masked"): + Masked(np.ma.masked) + + +def test_cannot_just_use_anything_with_a_mask_attribute(): + class my_array(np.ndarray): + mask = True + + a = np.array([1.0, 2.0]).view(my_array) + with pytest.raises(AttributeError, match="unmasked"): + Masked(a) + + +class TestMaskedClassCreation: + """Try creating a MaskedList and subclasses. + + By no means meant to be realistic, just to check that the basic + machinery allows it. + """ + + @classmethod + def setup_class(cls): + cls._base_classes_orig = Masked._base_classes.copy() + cls._masked_classes_orig = Masked._masked_classes.copy() + + class MaskedList(Masked, list, base_cls=list, data_cls=list): + def __new__(cls, *args, mask=None, copy=False, **kwargs): + self = super().__new__(cls) + self._unmasked = self._data_cls(*args, **kwargs) + self.mask = mask + return self + + # Need to have shape for basics to work. + @property + def shape(self): + return (len(self._unmasked),) + + cls.MaskedList = MaskedList + + def teardown_class(self): + Masked._base_classes = self._base_classes_orig + Masked._masked_classes = self._masked_classes_orig + + def test_setup(self): + assert issubclass(self.MaskedList, Masked) + assert issubclass(self.MaskedList, list) + assert Masked(list) is self.MaskedList + + def test_masked_list(self): + ml = self.MaskedList(range(3), mask=[True, False, False]) + assert ml.unmasked == [0, 1, 2] + assert_array_equal(ml.mask, np.array([True, False, False])) + ml01 = ml[:2] + assert ml01.unmasked == [0, 1] + assert_array_equal(ml01.mask, np.array([True, False])) + + def test_from_list(self): + ml = Masked([1, 2, 3], mask=[True, False, False]) + assert ml.unmasked == [1, 2, 3] + assert_array_equal(ml.mask, np.array([True, False, False])) + + def test_masked_list_subclass(self): + class MyList(list): + pass + + ml = MyList(range(3)) + mml = Masked(ml, mask=[False, True, False]) + assert isinstance(mml, Masked) + assert isinstance(mml, MyList) + assert isinstance(mml.unmasked, MyList) + assert mml.unmasked == [0, 1, 2] + assert_array_equal(mml.mask, np.array([False, True, False])) + + assert Masked(MyList) is type(mml) + + +class TestMaskedNDArraySubclassCreation: + """Test that masked subclasses can be created directly and indirectly.""" + + @classmethod + def setup_class(cls): + class MyArray(np.ndarray): + def __new__(cls, *args, **kwargs): + return np.asanyarray(*args, **kwargs).view(cls) + + cls.MyArray = MyArray + cls.a = np.array([1.0, 2.0]).view(cls.MyArray) + cls.m = np.array([True, False], dtype=bool) + + def teardown_method(self, method): + Masked._masked_classes.pop(self.MyArray, None) + + def test_direct_creation(self): + assert self.MyArray not in Masked._masked_classes + mcls = Masked(self.MyArray) + assert issubclass(mcls, Masked) + assert issubclass(mcls, self.MyArray) + assert mcls.__name__ == "MaskedMyArray" + assert mcls.__doc__.startswith("Masked version of MyArray") + mms = mcls(self.a, mask=self.m) + assert isinstance(mms, mcls) + assert_array_equal(mms.unmasked, self.a) + assert_array_equal(mms.mask, self.m) + + def test_initialization_without_mask(self): + # Default for not giving a mask should be False. + mcls = Masked(self.MyArray) + mms = mcls(self.a) + assert isinstance(mms, mcls) + assert_array_equal(mms.unmasked, self.a) + assert_array_equal(mms.mask, np.zeros(mms.shape, bool)) + + @pytest.mark.parametrize("masked_array", [Masked, np.ma.MaskedArray]) + def test_initialization_with_masked_values(self, masked_array): + mcls = Masked(self.MyArray) + ma = masked_array(np.asarray(self.a), mask=self.m) + mms = mcls(ma) + assert isinstance(mms, Masked) + assert isinstance(mms, self.MyArray) + assert_array_equal(mms.unmasked, self.a) + assert_array_equal(mms.mask, self.m) + + def test_indirect_creation(self): + assert self.MyArray not in Masked._masked_classes + mms = Masked(self.a, mask=self.m) + assert isinstance(mms, Masked) + assert isinstance(mms, self.MyArray) + assert_array_equal(mms.unmasked, self.a) + assert_array_equal(mms.mask, self.m) + assert self.MyArray in Masked._masked_classes + assert Masked(self.MyArray) is type(mms) + + def test_can_initialize_with_masked_values(self): + mcls = Masked(self.MyArray) + mms = mcls(Masked(np.asarray(self.a), mask=self.m)) + assert isinstance(mms, Masked) + assert isinstance(mms, self.MyArray) + assert_array_equal(mms.unmasked, self.a) + assert_array_equal(mms.mask, self.m) + + def test_viewing(self): + mms = Masked(self.a, mask=self.m) + mms2 = mms.view() + assert type(mms2) is mms.__class__ + assert_masked_equal(mms2, mms) + + ma = mms.view(np.ndarray) + assert type(ma) is MaskedNDArray + assert_array_equal(ma.unmasked, self.a.view(np.ndarray)) + assert_array_equal(ma.mask, self.m) + + def test_viewing_independent_shape(self): + mms = Masked(self.a, mask=self.m) + if NUMPY_LT_2_1: + mms2 = mms.view() + mms2.shape = mms2.shape[::-1] + else: + mms2 = np.reshape(mms, mms.shape[::-1], copy=False) + assert mms2.shape == mms.shape[::-1] + assert mms2.mask.shape == mms.shape[::-1] + # This should not affect the original array! + assert mms.shape == self.a.shape + assert mms.mask.shape == self.a.shape + + +class TestMaskedQuantityInitialization(TestMaskedArrayInitialization, QuantitySetup): + @classmethod + def setup_class(cls): + super().setup_class() + # Ensure we have used MaskedQuantity before - just in case a single test gets + # called; see gh-15316. + cls.MQ = Masked(Quantity) + + def test_masked_quantity_getting(self): + # First check setup_class (or previous use) defined a cache entry. + mcls = Masked._masked_classes[type(self.a)] + # Next check this is what one gets now. + MQ = Masked(Quantity) + assert MQ is mcls + assert issubclass(MQ, Quantity) + assert issubclass(MQ, Masked) + # And also what one gets implicitly. + mq = Masked([1.0, 2.0] * u.m) + assert isinstance(mq, MQ) + + def test_masked_quantity_class_init(self): + # This is not a very careful test. + mq = self.MQ([1.0, 2.0], mask=[True, False], unit=u.s) + assert mq.unit == u.s + assert np.all(mq.value.unmasked == [1.0, 2.0]) + assert np.all(mq.value.mask == [True, False]) + assert np.all(mq.mask == [True, False]) + + def test_initialization_without_mask(self): + # Default for not giving a mask should be False. + mq = self.MQ([1.0, 2.0], u.s) + assert mq.unit == u.s + assert np.all(mq.value.unmasked == [1.0, 2.0]) + assert np.all(mq.mask == [False, False]) + + @pytest.mark.parametrize("masked_array", [Masked, np.ma.MaskedArray]) + def test_initialization_with_masked_values(self, masked_array): + a = np.array([1.0, 2.0]) + m = np.array([True, False]) + ma = masked_array(a, m) + mq = self.MQ(ma) + assert isinstance(mq, self.MQ) + assert_array_equal(mq.value.unmasked, a) + assert_array_equal(mq.mask, m) + + def test_initialization_with_list_of_masked_quantity_scalars(self): + mq = self.MQ([Masked(1 * u.m, True), 2 * u.km, Masked(3 * u.Mm, False)]) + assert mq.unit == u.m + assert_array_equal(mq.value.unmasked, [1.0, 2e3, 3e6]) + assert_array_equal(mq.mask, [True, False, False]) + + def test_initialization_with_list_of_masked_quantity_arrays(self): + ma = Masked(self.a, self.mask_a) + mq = self.MQ([ma, ma << u.km]) + assert isinstance(mq, self.MQ) + assert_array_equal(mq.unmasked, u.Quantity([self.a, self.a << u.km])) + assert_array_equal(mq.mask, np.array([self.mask_a, self.mask_a])) + + +class TestMaskSetting(ArraySetup): + def test_whole_mask_setting_simple(self): + ma = Masked(self.a) + assert ma.mask.shape == ma.shape + assert not ma.mask.any() + ma.mask = True + assert ma.mask.shape == ma.shape + assert ma.mask.all() + ma.mask = [[True], [False]] + assert ma.mask.shape == ma.shape + assert_array_equal(ma.mask, np.array([[True] * 3, [False] * 3])) + ma.mask = self.mask_a + assert ma.mask.shape == ma.shape + assert_array_equal(ma.mask, self.mask_a) + assert ma.mask is not self.mask_a + assert np.may_share_memory(ma.mask, self.mask_a) + + def test_whole_mask_setting_structured(self): + ma = Masked(self.sa) + assert ma.mask.shape == ma.shape + assert not ma.mask["a"].any() and not ma.mask["b"].any() + ma.mask = True + assert ma.mask.shape == ma.shape + assert ma.mask["a"].all() and ma.mask["b"].all() + ma.mask = [[True], [False]] + assert ma.mask.shape == ma.shape + assert_array_equal( + ma.mask, + np.array([[(True, True)] * 2, [(False, False)] * 2], dtype=self.mask_sdt), + ) + ma.mask = self.mask_sa + assert ma.mask.shape == ma.shape + assert_array_equal(ma.mask, self.mask_sa) + assert ma.mask is not self.mask_sa + assert np.may_share_memory(ma.mask, self.mask_sa) + + @pytest.mark.parametrize("item", VARIOUS_ITEMS) + def test_part_mask_setting(self, item): + ma = Masked(self.a) + ma.mask[item] = True + expected = np.zeros(ma.shape, bool) + expected[item] = True + assert_array_equal(ma.mask, expected) + ma.mask[item] = False + assert_array_equal(ma.mask, np.zeros(ma.shape, bool)) + # Mask propagation + mask = np.zeros(self.a.shape, bool) + ma = Masked(self.a, mask) + ma.mask[item] = True + assert np.may_share_memory(ma.mask, mask) + assert_array_equal(ma.mask, mask) + + @pytest.mark.parametrize("item", ["a"] + VARIOUS_ITEMS) + def test_part_mask_setting_structured(self, item): + ma = Masked(self.sa) + ma.mask[item] = True + expected = np.zeros(ma.shape, self.mask_sdt) + expected[item] = True + assert_array_equal(ma.mask, expected) + ma.mask[item] = False + assert_array_equal(ma.mask, np.zeros(ma.shape, self.mask_sdt)) + # Mask propagation + mask = np.zeros(self.sa.shape, self.mask_sdt) + ma = Masked(self.sa, mask) + ma.mask[item] = True + assert np.may_share_memory(ma.mask, mask) + assert_array_equal(ma.mask, mask) + + +# Following are tests where we trust the initializer works. + + +class MaskedArraySetup(ArraySetup): + @classmethod + def setup_class(cls): + super().setup_class() + cls.ma = Masked(cls.a, mask=cls.mask_a) + cls.mb = Masked(cls.b, mask=cls.mask_b) + cls.mc = Masked(cls.c, mask=cls.mask_c) + cls.msa = Masked(cls.sa, mask=cls.mask_sa) + cls.msb = Masked(cls.sb, mask=cls.mask_sb) + cls.msc = Masked(cls.sc, mask=cls.mask_sc) + + +class TestViewing(MaskedArraySetup): + def test_viewing_as_new_type(self): + ma2 = self.ma.view(type(self.ma)) + assert_masked_equal(ma2, self.ma) + + ma3 = self.ma.view() + assert_masked_equal(ma3, self.ma) + + def test_viewing_as_new_dtype(self): + # Not very meaningful, but possible... + ma2 = self.ma.view("c8") + assert_array_equal(ma2.unmasked, self.a.view("c8")) + assert_array_equal(ma2.mask, self.mask_a) + + def test_viewing_as_new_structured_dtype(self): + ma2 = self.ma.view("f8,f8,f8") + assert_array_equal(ma2.unmasked, self.a.view("f8,f8,f8")) + assert_array_equal(ma2.mask, self.mask_a.view("?,?,?")) + # Check round-trip + ma3 = ma2.view(self.ma.dtype) + assert_array_equal(ma3.unmasked, self.ma.unmasked) + assert_array_equal(ma3.mask, self.mask_a) + + @pytest.mark.parametrize("new_dtype", ["f4", "2f4"]) + def test_viewing_as_new_dtype_not_implemented(self, new_dtype): + # But cannot (yet) view in way that would need to create a new mask, + # even though that view is possible for a regular array. + check = self.a.view(new_dtype) + with pytest.raises(NotImplementedError, match="different.*size"): + self.ma.view(new_dtype) + + def test_viewing_as_something_impossible(self): + with pytest.raises(TypeError): + # Use intp to ensure have the same size as object, + # otherwise we get a different error message + Masked(np.array([1, 2], dtype=np.intp)).view(Masked) + + +class TestMaskedArrayCopyFilled(MaskedArraySetup): + def test_copy(self): + ma_copy = self.ma.copy() + assert type(ma_copy) is type(self.ma) + assert_array_equal(ma_copy.unmasked, self.ma.unmasked) + assert_array_equal(ma_copy.mask, self.ma.mask) + assert not np.may_share_memory(ma_copy.unmasked, self.ma.unmasked) + assert not np.may_share_memory(ma_copy.mask, self.ma.mask) + + @pytest.mark.parametrize("fill_value", (0, 1)) + def test_filled(self, fill_value): + fill_value = fill_value * getattr(self.a, "unit", 1) + expected = self.a.copy() + expected[self.ma.mask] = fill_value + result = self.ma.filled(fill_value) + assert_array_equal(expected, result) + + def test_filled_no_fill_value(self): + with pytest.raises(TypeError, match="missing 1 required"): + self.ma.filled() + + @pytest.mark.parametrize("fill_value", [(0, 1), (-1, -1)]) + def test_filled_structured(self, fill_value): + fill_value = np.array(fill_value, dtype=self.sdt) + if hasattr(self.sa, "unit"): + fill_value = fill_value << self.sa.unit + expected = self.sa.copy() + expected["a"][self.msa.mask["a"]] = fill_value["a"] + expected["b"][self.msa.mask["b"]] = fill_value["b"] + result = self.msa.filled(fill_value) + assert_array_equal(expected, result) + + def test_flat(self): + ma_copy = self.ma.copy() + ma_flat = ma_copy.flat + # Check that single item keeps class and mask + ma_flat1 = ma_flat[1] + assert ma_flat1.unmasked == self.a.flat[1] + assert ma_flat1.mask == self.mask_a.flat[1] + # As well as getting items via iteration. + assert all( + (ma.unmasked == a and ma.mask == m) + for (ma, a, m) in zip(self.ma.flat, self.a.flat, self.mask_a.flat) + ) + + # check that flat works like a view of the real array + ma_flat[1] = self.b[1] + assert ma_flat[1] == self.b[1] + assert ma_copy[0, 1] == self.b[1] + + +class TestMaskedQuantityCopyFilled(TestMaskedArrayCopyFilled, QuantitySetup): + pass + + +class TestMaskedLongitudeCopyFilled(TestMaskedArrayCopyFilled, LongitudeSetup): + pass + + +class TestMaskedArrayShaping(MaskedArraySetup): + def test_reshape(self): + ma_reshape = self.ma.reshape((6,)) + expected_data = self.a.reshape((6,)) + expected_mask = self.mask_a.reshape((6,)) + assert ma_reshape.shape == expected_data.shape + assert_array_equal(ma_reshape.unmasked, expected_data) + assert_array_equal(ma_reshape.mask, expected_mask) + + def test_shape_setting(self): + if NUMPY_LT_2_1: + ma_reshape = self.ma.copy() + ma_reshape.shape = (6,) + else: + ma_reshape = np.reshape(self.ma, (6,), copy=True) + + expected_data = self.a.reshape((6,)) + expected_mask = self.mask_a.reshape((6,)) + assert ma_reshape.shape == expected_data.shape + assert_array_equal(ma_reshape.unmasked, expected_data) + assert_array_equal(ma_reshape.mask, expected_mask) + + @pytest.mark.filterwarnings( + "default:Setting the shape on a NumPy array has been deprecated in NumPy 2.5:DeprecationWarning" + ) + def test_shape_setting_failure(self): + ma = self.ma.copy() + with pytest.raises(ValueError, match="cannot reshape"): + ma.shape = (5,) + + assert ma.shape == self.ma.shape + assert ma.mask.shape == self.ma.shape + + # Here, mask can be reshaped but array cannot. + ma2 = Masked(np.broadcast_to([[1.0], [2.0]], self.a.shape), mask=self.mask_a) + with pytest.raises(AttributeError, match="ncompatible shape"): + ma2.shape = (6,) + + assert ma2.shape == self.ma.shape + assert ma2.mask.shape == self.ma.shape + + # Here, array can be reshaped but mask cannot. + ma3 = Masked( + self.a.copy(), mask=np.broadcast_to([[True], [False]], self.mask_a.shape) + ) + with pytest.raises(AttributeError, match="ncompatible shape"): + ma3.shape = (6,) + + assert ma3.shape == self.ma.shape + assert ma3.mask.shape == self.ma.shape + + def test_ravel(self): + ma_ravel = self.ma.ravel() + expected_data = self.a.ravel() + expected_mask = self.mask_a.ravel() + assert ma_ravel.shape == expected_data.shape + assert_array_equal(ma_ravel.unmasked, expected_data) + assert_array_equal(ma_ravel.mask, expected_mask) + + def test_transpose(self): + ma_transpose = self.ma.transpose() + expected_data = self.a.transpose() + expected_mask = self.mask_a.transpose() + assert ma_transpose.shape == expected_data.shape + assert_array_equal(ma_transpose.unmasked, expected_data) + assert_array_equal(ma_transpose.mask, expected_mask) + + def test_iter(self): + for ma, d, m in zip(self.ma, self.a, self.mask_a): + assert_array_equal(ma.unmasked, d) + assert_array_equal(ma.mask, m) + + +class MaskedItemTests(MaskedArraySetup): + @pytest.mark.parametrize("item", VARIOUS_ITEMS) + def test_getitem(self, item): + ma_part = self.ma[item] + expected_data = self.a[item] + expected_mask = self.mask_a[item] + assert_array_equal(ma_part.unmasked, expected_data) + assert_array_equal(ma_part.mask, expected_mask) + + @pytest.mark.parametrize("item", ["a"] + VARIOUS_ITEMS) + def test_getitem_structured(self, item): + ma_part = self.msa[item] + expected_data = self.sa[item] + expected_mask = self.mask_sa[item] + assert_array_equal(ma_part.unmasked, expected_data) + assert_array_equal(ma_part.mask, expected_mask) + + @pytest.mark.parametrize( + "indices,axis", + [([0, 1], 1), ([0, 1], 0), ([0, 1], None), ([[0, 1], [2, 3]], None)], + ) + def test_take(self, indices, axis): + ma_take = self.ma.take(indices, axis=axis) + expected_data = self.a.take(indices, axis=axis) + expected_mask = self.mask_a.take(indices, axis=axis) + assert_array_equal(ma_take.unmasked, expected_data) + assert_array_equal(ma_take.mask, expected_mask) + ma_take2 = np.take(self.ma, indices, axis=axis) + assert_masked_equal(ma_take2, ma_take) + + @pytest.mark.parametrize("item", VARIOUS_ITEMS) + @pytest.mark.parametrize("mask", [None, True, False]) + def test_setitem(self, item, mask): + base = self.ma.copy() + expected_data = self.a.copy() + expected_mask = self.mask_a.copy() + value = self.a[0, 0] if mask is None else Masked(self.a[0, 0], mask) + base[item] = value + expected_data[item] = value if mask is None else value.unmasked + expected_mask[item] = False if mask is None else value.mask + assert_array_equal(base.unmasked, expected_data) + assert_array_equal(base.mask, expected_mask) + + @pytest.mark.parametrize("item", ["a"] + VARIOUS_ITEMS) + @pytest.mark.parametrize("mask", [None, True, False]) + def test_setitem_structured(self, item, mask): + base = self.msa.copy() + expected_data = self.sa.copy() + expected_mask = self.mask_sa.copy() + value = self.sa["b"] if item == "a" else self.sa[0, 0] + if mask is not None: + value = Masked(value, mask) + base[item] = value + expected_data[item] = value if mask is None else value.unmasked + expected_mask[item] = False if mask is None else value.mask + assert_array_equal(base.unmasked, expected_data) + assert_array_equal(base.mask, expected_mask) + + @pytest.mark.parametrize("item", VARIOUS_ITEMS) + def test_setitem_np_ma_masked(self, item): + base = self.ma.copy() + expected_mask = self.mask_a.copy() + base[item] = np.ma.masked + expected_mask[item] = True + assert_array_equal(base.unmasked, self.a) + assert_array_equal(base.mask, expected_mask) + + @pytest.mark.parametrize("item", VARIOUS_ITEMS) + def test_hash(self, item): + ma_part = self.ma[item] + if ma_part.ndim > 0 or ma_part.mask.any(): + with pytest.raises(TypeError, match="unhashable"): + hash(ma_part) + else: + assert hash(ma_part) == hash(ma_part.unmasked) + + +class TestMaskedArrayItems(MaskedItemTests): + @classmethod + def setup_class(cls): + super().setup_class() + cls.d = np.array(["aa", "bb"]) + cls.mask_d = np.array([True, False]) + cls.md = Masked(cls.d, cls.mask_d) + + # Quantity, Longitude cannot hold strings. + def test_getitem_strings(self): + md = self.md.copy() + md0 = md[0] + assert md0.unmasked == self.d[0] + assert md0.mask + md_all = md[:] + assert_masked_equal(md_all, md) + + def test_setitem_strings_np_ma_masked(self): + md = self.md.copy() + md[1] = np.ma.masked + assert_array_equal(md.unmasked, self.d) + assert_array_equal(md.mask, np.ones(2, bool)) + + +class TestMaskedQuantityItems(MaskedItemTests, QuantitySetup): + pass + + +class TestMaskedLongitudeItems(MaskedItemTests, LongitudeSetup): + pass + + +class MaskedOperatorTests(MaskedArraySetup): + @pytest.mark.parametrize("op", (operator.add, operator.sub)) + def test_add_subtract(self, op): + mapmb = op(self.ma, self.mb) + expected_data = op(self.a, self.b) + expected_mask = self.ma.mask | self.mb.mask + # Note: assert_array_equal also checks type, i.e., that, e.g., + # Longitude decays into an Angle. + assert_array_equal(mapmb.unmasked, expected_data) + assert_array_equal(mapmb.mask, expected_mask) + + @pytest.mark.parametrize("op", (operator.eq, operator.ne)) + def test_equality(self, op): + mapmb = op(self.ma, self.mb) + expected_data = op(self.a, self.b) + expected_mask = self.ma.mask | self.mb.mask + # Note: assert_array_equal also checks type, i.e., that boolean + # output is represented as plain Masked ndarray. + assert_array_equal(mapmb.unmasked, expected_data) + assert_array_equal(mapmb.mask, expected_mask) + + def test_not_implemented(self): + with pytest.raises(TypeError): + self.ma > "abc" # noqa: B015 + + @pytest.mark.parametrize("different_names", [False, True]) + @pytest.mark.parametrize("op", (operator.eq, operator.ne)) + def test_structured_equality(self, op, different_names): + msb = self.msb + if different_names: + msb = msb.astype( + [(f"different_{name}", dt) for name, dt in msb.dtype.fields.items()] + ) + mapmb = op(self.msa, self.msb) + # Expected is a bit tricky here: only unmasked fields count + expected_data = np.ones(mapmb.shape, bool) + expected_mask = np.ones(mapmb.shape, bool) + for field in self.sdt.names: + fa, mfa = self.sa[field], self.mask_sa[field] + fb, mfb = self.sb[field], self.mask_sb[field] + mfequal = mfa | mfb + fequal = (fa == fb) | mfequal + expected_data &= fequal + expected_mask &= mfequal + + if op is operator.ne: + expected_data = ~expected_data + + # Note: assert_array_equal also checks type, i.e., that boolean + # output is represented as plain Masked ndarray. + assert_array_equal(mapmb.unmasked, expected_data) + assert_array_equal(mapmb.mask, expected_mask) + + def test_matmul(self): + result = self.ma.T @ self.ma + assert_array_equal(result.unmasked, self.a.T @ self.a) + mask1 = np.any(self.mask_a, axis=0) + expected_mask = np.logical_or.outer(mask1, mask1) + assert_array_equal(result.mask, expected_mask) + result2 = self.ma.T @ self.a + assert_array_equal(result2.unmasked, self.a.T @ self.a) + expected_mask2 = np.logical_or.outer(mask1, np.zeros(3, bool)) + assert_array_equal(result2.mask, expected_mask2) + result3 = self.a.T @ self.ma + assert_array_equal(result3.unmasked, self.a.T @ self.a) + expected_mask3 = np.logical_or.outer(np.zeros(3, bool), mask1) + assert_array_equal(result3.mask, expected_mask3) + + def test_matmul_axes(self): + m1 = Masked(np.arange(27.0).reshape(3, 3, 3)) + m2 = Masked(np.arange(-27.0, 0.0).reshape(3, 3, 3)) + mxm1 = np.matmul(m1, m2) + exp = np.matmul(m1.unmasked, m2.unmasked) + assert_array_equal(mxm1.unmasked, exp) + assert_array_equal(mxm1.mask, False) + m1.mask[0, 1, 2] = True + m2.mask[0, 2, 0] = True + axes = [(0, 2), (-2, -1), (0, 1)] + mxm2 = np.matmul(m1, m2, axes=axes) + exp2 = np.matmul(m1.unmasked, m2.unmasked, axes=axes) + # Any unmasked result will have all elements contributing unity, + # while masked entries mean the total will be lower. + mask2 = ( + np.matmul( + (~m1.mask).astype(int), + (~m2.mask).astype(int), + axes=axes, + ) + != m1.shape[axes[0][1]] + ) + assert_array_equal(mxm2.unmasked, exp2) + assert_array_equal(mxm2.mask, mask2) + + def test_matmul_matvec(self): + result = self.ma @ self.mb + assert np.all(result.mask) + assert_array_equal(result.unmasked, self.a @ self.b) + # Just using the masked vector still has all elements masked. + result2 = self.a @ self.mb + assert np.all(result2.mask) + assert_array_equal(result2.unmasked, self.a @ self.b) + new_ma = self.ma.copy() + new_ma.mask[0, 0] = False + result3 = new_ma @ self.b + assert_array_equal(result3.unmasked, self.a @ self.b) + assert_array_equal(result3.mask, new_ma.mask.any(-1)) + + def test_matmul_vecmat(self): + result = self.mb @ self.ma.T + assert np.all(result.mask) + assert_array_equal(result.unmasked, self.b @ self.a.T) + result2 = self.b @ self.ma.T + assert np.all(result2.mask) + assert_array_equal(result2.unmasked, self.b @ self.a.T) + new_ma = self.ma.T.copy() + new_ma.mask[0, 0] = False + result3 = self.b @ new_ma + assert_array_equal(result3.unmasked, self.b @ self.a.T) + assert_array_equal(result3.mask, new_ma.mask.any(0)) + + def test_matmul_vecvec(self): + result = self.mb @ self.mb + assert result.shape == () + assert result.mask + assert result.unmasked == self.b @ self.b + mb_no_mask = Masked(self.b, False) + result2 = mb_no_mask @ mb_no_mask + assert not result2.mask + + @pytest.mark.skipif( + NUMPY_LT_2_3, + reason="np.matvec and np.vecmat are new in NumPy 2.3", + ) + def test_matvec_vecmat(self): + vec = Masked(np.arange(3, like=self.a), [True, False, False]) + mat_mask = np.zeros((3, 3), bool) + mat_mask[0, 0] = True + mat = Masked( + np.array([[1.0, -1.0, 2.0], [0.0, 3.0, -1.0], [-1.0, -1.0, 1.0]]), + mat_mask, + ) + + ref_matvec = (vec * mat).sum(-1) + res_matvec = np.matvec(mat, vec) + assert_masked_equal(res_matvec, ref_matvec) + + ref_vecmat = (vec * mat.T).sum(-1) + res_vecmat = np.vecmat(vec, mat) + assert_masked_equal(res_vecmat, ref_vecmat) + + +class TestMaskedArrayOperators(MaskedOperatorTests): + # Some further tests that use strings, which are not useful for Quantity. + @pytest.mark.parametrize("op", (operator.eq, operator.ne)) + def test_equality_strings(self, op): + m1 = Masked(np.array(["a", "b", "c"]), mask=[True, False, False]) + m2 = Masked(np.array(["a", "b", "d"]), mask=[False, False, False]) + result = op(m1, m2) + assert_array_equal(result.unmasked, op(m1.unmasked, m2.unmasked)) + assert_array_equal(result.mask, m1.mask | m2.mask) + + result2 = op(m1, m2.unmasked) + assert_masked_equal(result2, result) + + def test_not_implemented(self): + with pytest.raises(TypeError): + Masked(["a", "b"]) > object() # noqa: B015 + + +class TestMaskedQuantityOperators(MaskedOperatorTests, QuantitySetup): + pass + + +class TestMaskedLongitudeOperators(MaskedOperatorTests, LongitudeSetup): + pass + + +class TestMaskedArrayMethods(MaskedArraySetup): + def test_round(self): + # Goes via ufunc, hence easy. + mrc = self.mc.round() + expected = Masked(self.c.round(), self.mask_c) + assert_masked_equal(mrc, expected) + + @pytest.mark.parametrize("axis", (0, 1, None)) + def test_sum(self, axis): + ma_sum = self.ma.sum(axis) + expected_data = self.a.sum(axis) + expected_mask = self.ma.mask.any(axis) + assert_array_equal(ma_sum.unmasked, expected_data) + assert_array_equal(ma_sum.mask, expected_mask) + + @pytest.mark.parametrize("axis", (0, 1, None)) + def test_sum_where(self, axis): + where = np.array( + [ + [True, False, False], + [True, True, True], + ] + ) + where_final = ~self.ma.mask & where + ma_sum = self.ma.sum(axis, where=where_final) + expected_data = self.ma.unmasked.sum(axis, where=where_final) + expected_mask = np.logical_or.reduce( + self.ma.mask, axis=axis, where=where_final + ) | (~where_final).all(axis) + assert_array_equal(ma_sum.unmasked, expected_data) + assert_array_equal(ma_sum.mask, expected_mask) + + def test_sum_hash(self): + ma_sum = self.ma.sum() + assert ma_sum.mask + # Masked scalars cannot be hashed. + with pytest.raises(TypeError, match="unhashable"): + hash(ma_sum) + ma_sum2 = Masked(self.a).sum() + # But an unmasked scalar can. + assert hash(ma_sum2) == hash(self.a.sum()) + + @pytest.mark.parametrize("axis", (0, 1, None)) + def test_cumsum(self, axis): + ma_sum = self.ma.cumsum(axis) + expected_data = self.a.cumsum(axis) + mask = self.mask_a + if axis is None: + mask = mask.ravel() + expected_mask = np.logical_or.accumulate(mask, axis=axis) + assert_array_equal(ma_sum.unmasked, expected_data) + assert_array_equal(ma_sum.mask, expected_mask) + + @pytest.mark.parametrize("axis", (0, 1, None)) + def test_mean(self, axis): + ma_mean = self.ma.mean(axis) + filled = self.a.copy() + filled[self.mask_a] = 0.0 + count = 1 - self.ma.mask.astype(int) + expected_data = filled.sum(axis) / count.sum(axis) + expected_mask = self.ma.mask.all(axis) + assert_array_equal(ma_mean.unmasked, expected_data) + assert_array_equal(ma_mean.mask, expected_mask) + + @pytest.mark.parametrize("axis", (0, 1, None)) + def test_mean_all_masked(self, axis): + # test corner case when all values are masked + md = Masked(self.a, np.ones(self.a.shape, dtype=bool)) + md_mean = md.mean(axis) + assert np.all(np.isnan(md_mean.unmasked)) + assert np.all(md_mean.mask) + + def test_mean_int16(self): + ma = self.ma.astype("i2") + ma_mean = ma.mean() + assert ma_mean.dtype == "f8" + expected = ma.astype("f8").mean() + assert_masked_equal(ma_mean, expected) + + def test_mean_float16(self): + ma = self.ma.astype("f2") + ma_mean = ma.mean() + assert ma_mean.dtype == "f2" + expected = self.ma.mean().astype("f2") + assert_masked_equal(ma_mean, expected) + + def test_mean_inplace(self): + expected = self.ma.mean(1) + out = Masked(np.zeros_like(expected.unmasked)) + result = self.ma.mean(1, out=out) + assert result is out + assert_masked_equal(out, expected) + + @pytest.mark.filterwarnings("ignore:.*encountered in.*divide") + @pytest.mark.filterwarnings("ignore:Mean of empty slice") + @pytest.mark.parametrize("axis", (0, 1, None)) + def test_mean_where(self, axis): + where = np.array( + [ + [True, False, False], + [True, True, True], + ] + ) + where_final = ~self.ma.mask & where + ma_mean = self.ma.mean(axis, where=where) + expected_data = self.ma.unmasked.mean(axis, where=where_final) + expected_mask = np.logical_or.reduce( + self.ma.mask, axis=axis, where=where_final + ) | (~where_final).all(axis) + assert_array_equal(ma_mean.unmasked, expected_data) + assert_array_equal(ma_mean.mask, expected_mask) + + def test_mean_hash(self): + ma_mean = self.ma.mean() + assert hash(ma_mean) == hash(ma_mean.unmasked[()]) + + @pytest.mark.filterwarnings("ignore:.*encountered in.*divide") + @pytest.mark.parametrize("axis", (0, 1, None)) + def test_var(self, axis): + ma_var = self.ma.var(axis) + filled = (self.a - self.ma.mean(axis, keepdims=True)) ** 2 + filled[self.mask_a] = 0.0 + count = (1 - self.ma.mask.astype(int)).sum(axis) + expected_data = filled.sum(axis) / count + expected_mask = self.ma.mask.all(axis) + assert_array_equal(ma_var.unmasked, expected_data) + assert_array_equal(ma_var.mask, expected_mask) + ma_var1 = self.ma.var(axis, ddof=1) + expected_data1 = filled.sum(axis) / (count - 1) + expected_mask1 = self.ma.mask.all(axis) | (count <= 1) + assert_array_equal(ma_var1.unmasked, expected_data1) + assert_array_equal(ma_var1.mask, expected_mask1) + ma_var5 = self.ma.var(axis, ddof=5) + assert np.all(~np.isfinite(ma_var5.unmasked)) + assert ma_var5.mask.all() + + def test_var_int16(self): + ma = self.ma.astype("i2") + ma_var = ma.var() + assert ma_var.dtype == "f8" + expected = ma.astype("f8").var() + assert_masked_equal(ma_var, expected) + + @pytest.mark.filterwarnings("ignore:.*encountered in.*divide") + @pytest.mark.filterwarnings("ignore:Degrees of freedom <= 0 for slice") + @pytest.mark.parametrize("axis", (0, 1, None)) + def test_var_where(self, axis): + where = np.array( + [ + [True, False, False], + [True, True, True], + ] + ) + where_final = ~self.ma.mask & where + ma_var = self.ma.var(axis, where=where) + expected_data = self.ma.unmasked.var(axis, where=where_final) + expected_mask = np.logical_or.reduce( + self.ma.mask, axis=axis, where=where_final + ) | (~where_final).all(axis) + assert_array_equal(ma_var.unmasked, expected_data) + assert_array_equal(ma_var.mask, expected_mask) + + def test_std(self): + ma_std = self.ma.std(1, ddof=1) + ma_var1 = self.ma.var(1, ddof=1) + expected = np.sqrt(ma_var1) + assert_masked_equal(ma_std, expected) + + def test_std_inplace(self): + expected = self.ma.std(1, ddof=1) + out = Masked(np.zeros_like(expected.unmasked)) + result = self.ma.std(1, ddof=1, out=out) + assert result is out + assert_masked_equal(result, expected) + + @pytest.mark.filterwarnings("ignore:.*encountered in.*divide") + @pytest.mark.filterwarnings("ignore:Degrees of freedom <= 0 for slice") + @pytest.mark.parametrize("axis", (0, 1, None)) + def test_std_where(self, axis): + where = np.array( + [ + [True, False, False], + [True, True, True], + ] + ) + where_final = ~self.ma.mask & where + ma_std = self.ma.std(axis, where=where) + expected_data = self.ma.unmasked.std(axis, where=where_final) + expected_mask = np.logical_or.reduce( + self.ma.mask, axis=axis, where=where_final + ) | (~where_final).all(axis) + assert_array_equal(ma_std.unmasked, expected_data) + assert_array_equal(ma_std.mask, expected_mask) + + @pytest.mark.parametrize("axis", (0, 1, None)) + def test_min(self, axis): + ma_min = self.ma.min(axis) + filled = self.a.copy() + filled[self.mask_a] = self.a.max() + expected_data = filled.min(axis) + assert_array_equal(ma_min.unmasked, expected_data) + assert not np.any(ma_min.mask) + + def test_min_with_masked_nan(self): + ma = Masked([3.0, np.nan, 2.0], mask=[False, True, False]) + ma_min = ma.min() + assert_array_equal(ma_min.unmasked, np.array(2.0)) + assert not ma_min.mask + + @pytest.mark.parametrize("axis", (0, 1, None)) + def test_min_where(self, axis): + where = np.array( + [ + [True, False, False], + [True, True, True], + ] + ) + where_final = ~self.ma.mask & where + ma_min = self.ma.min(axis, where=where_final, initial=np.inf) + expected_data = self.ma.unmasked.min(axis, where=where_final, initial=np.inf) + expected_mask = np.logical_or.reduce( + self.ma.mask, axis=axis, where=where_final + ) | (~where_final).all(axis) + assert_array_equal(ma_min.unmasked, expected_data) + assert_array_equal(ma_min.mask, expected_mask) + + @pytest.mark.parametrize("axis", (0, 1, None)) + def test_max(self, axis): + ma_max = self.ma.max(axis) + filled = self.a.copy() + filled[self.mask_a] = self.a.min() + expected_data = filled.max(axis) + assert_array_equal(ma_max.unmasked, expected_data) + assert not np.any(ma_max.mask) + + @pytest.mark.parametrize("axis", (0, 1, None)) + def test_max_where(self, axis): + where = np.array( + [ + [True, False, False], + [True, True, True], + ] + ) + where_final = ~self.ma.mask & where + ma_max = self.ma.max(axis, where=where_final, initial=-np.inf) + expected_data = self.ma.unmasked.max(axis, where=where_final, initial=-np.inf) + expected_mask = np.logical_or.reduce( + self.ma.mask, axis=axis, where=where_final + ) | (~where_final).all(axis) + assert_array_equal(ma_max.unmasked, expected_data) + assert_array_equal(ma_max.mask, expected_mask) + + @pytest.mark.parametrize("axis", (0, 1, None)) + def test_argmin(self, axis): + ma_argmin = self.ma.argmin(axis) + filled = self.a.copy() + filled[self.mask_a] = self.a.max() + expected_data = filled.argmin(axis) + assert_array_equal(ma_argmin, expected_data) + + def test_argmin_only_one_unmasked_element(self): + # Regression test for example from @taldcroft at + # https://github.com/astropy/astropy/pull/11127#discussion_r600864559 + ma = Masked(data=[1, 2], mask=[True, False]) + assert ma.argmin() == 1 + + def test_argmin_keepdims(self): + ma = Masked(data=[[1, 2], [3, 4]], mask=[[True, False], [False, False]]) + assert_array_equal(ma.argmin(axis=0, keepdims=True), np.array([[1, 0]])) + + @pytest.mark.parametrize("axis", (0, 1, None)) + def test_argmax(self, axis): + ma_argmax = self.ma.argmax(axis) + filled = self.a.copy() + filled[self.mask_a] = self.a.min() + expected_data = filled.argmax(axis) + assert_array_equal(ma_argmax, expected_data) + + def test_argmax_keepdims(self): + ma = Masked(data=[[1, 2], [3, 4]], mask=[[True, False], [False, False]]) + assert_array_equal(ma.argmax(axis=1, keepdims=True), np.array([[1], [1]])) + + @pytest.mark.parametrize("axis", (0, 1, None)) + def test_argsort(self, axis): + ma_argsort = self.ma.argsort(axis) + filled = self.a.copy() + filled[self.mask_a] = self.a.max() * 1.1 + expected_data = filled.argsort(axis) + assert_array_equal(ma_argsort, expected_data) + + @pytest.mark.parametrize("order", [None, "a", ("a", "b"), ("b", "a")]) + @pytest.mark.parametrize("axis", [0, 1]) + def test_structured_argsort(self, axis, order): + ma_argsort = self.msa.argsort(axis, order=order) + filled = self.msa.filled(fill_value=np.array((np.inf, np.inf), dtype=self.sdt)) + expected_data = filled.argsort(axis, order=order) + assert_array_equal(ma_argsort, expected_data) + + def test_argsort_error(self): + with pytest.raises(ValueError, match="when the array has no fields"): + self.ma.argsort(axis=0, order="a") + + @pytest.mark.parametrize("axis", (0, 1)) + def test_sort(self, axis): + ma_sort = self.ma.copy() + ma_sort.sort(axis) + indices = self.ma.argsort(axis) + expected_data = np.take_along_axis(self.ma.unmasked, indices, axis) + expected_mask = np.take_along_axis(self.ma.mask, indices, axis) + assert_array_equal(ma_sort.unmasked, expected_data) + assert_array_equal(ma_sort.mask, expected_mask) + + @pytest.mark.parametrize("kth", [1, 3]) + def test_argpartition(self, kth): + ma = self.ma.ravel() + ma_argpartition = ma.argpartition(kth) + partitioned = ma[ma_argpartition] + assert (partitioned[:kth] < partitioned[kth]).all() + assert (partitioned[kth:] >= partitioned[kth]).all() + if partitioned[kth].mask: + assert all(partitioned.mask[kth:]) + else: + assert not any(partitioned.mask[:kth]) + + @pytest.mark.parametrize("kth", [1, 3]) + def test_partition(self, kth): + partitioned = self.ma.flatten() + partitioned.partition(kth) + assert (partitioned[:kth] < partitioned[kth]).all() + assert (partitioned[kth:] >= partitioned[kth]).all() + if partitioned[kth].mask: + assert all(partitioned.mask[kth:]) + else: + assert not any(partitioned.mask[:kth]) + + def test_all_explicit(self): + a1 = np.array( + [ + [1.0, 2.0], + [3.0, 4.0], + ] + ) + a2 = np.array( + [ + [1.0, 0.0], + [3.0, 4.0], + ] + ) + if self._data_cls is not np.ndarray: + a1 = self._data_cls(a1, self.a.unit) + a2 = self._data_cls(a2, self.a.unit) + ma1 = Masked( + a1, + mask=[ + [False, False], + [True, True], + ], + ) + ma2 = Masked( + a2, + mask=[ + [False, True], + [False, True], + ], + ) + ma1_eq_ma2 = ma1 == ma2 + assert_array_equal( + ma1_eq_ma2.unmasked, + np.array( + [ + [True, False], + [True, True], + ] + ), + ) + assert_array_equal( + ma1_eq_ma2.mask, + np.array( + [ + [False, True], + [True, True], + ] + ), + ) + assert ma1_eq_ma2.all() + assert not (ma1 != ma2).all() + ma_eq1 = ma1_eq_ma2.all(1) + assert_array_equal(ma_eq1.mask, np.array([False, True])) + assert bool(ma_eq1[0]) is True + assert bool(ma_eq1[1]) is False + ma_eq0 = ma1_eq_ma2.all(0) + assert_array_equal(ma_eq0.mask, np.array([False, True])) + assert bool(ma_eq1[0]) is True + assert bool(ma_eq1[1]) is False + + @pytest.mark.parametrize("method", ["any", "all"]) + @pytest.mark.parametrize( + "array,axis", + [("a", 0), ("a", 1), ("a", None), ("b", None), ("c", 0), ("c", 1), ("c", None)], + ) + def test_all_and_any(self, array, axis, method): + ma = getattr(self, "m" + array) + ma_eq = ma == ma + ma_all_or_any = getattr(ma_eq, method)(axis=axis) + filled = ma_eq.unmasked.copy() + filled[ma_eq.mask] = method == "all" + a_all_or_any = getattr(filled, method)(axis=axis) + all_masked = ma.mask.all(axis) + assert_array_equal(ma_all_or_any.mask, all_masked) + assert_array_equal(ma_all_or_any.unmasked, a_all_or_any) + # interpretation as bool + as_bool = [bool(a) for a in ma_all_or_any.ravel()] + expected = [bool(a) for a in (a_all_or_any & ~all_masked).ravel()] + assert as_bool == expected + + def test_any_inplace(self): + ma_eq = self.ma == self.ma + expected = ma_eq.any(1) + out = Masked(np.zeros_like(expected.unmasked)) + result = ma_eq.any(1, out=out) + assert result is out + assert_masked_equal(result, expected) + + @pytest.mark.parametrize("method", ("all", "any")) + @pytest.mark.parametrize("axis", (0, 1, None)) + def test_all_and_any_where(self, method, axis): + where = np.array( + [ + [True, False, False], + [True, True, True], + ] + ) + where_final = ~self.ma.mask & where + ma_eq = self.ma == self.ma + ma_any = getattr(ma_eq, method)(axis, where=where) + expected_data = getattr(ma_eq.unmasked, method)(axis, where=where_final) + expected_mask = np.logical_or.reduce( + self.ma.mask, axis=axis, where=where_final + ) | (~where_final).all(axis) + assert_array_equal(ma_any.unmasked, expected_data) + assert_array_equal(ma_any.mask, expected_mask) + + @pytest.mark.parametrize("offset", (0, 1)) + def test_diagonal(self, offset): + mda = self.ma.diagonal(offset=offset) + expected = Masked( + self.a.diagonal(offset=offset), self.mask_a.diagonal(offset=offset) + ) + assert_masked_equal(mda, expected) + + @pytest.mark.parametrize("offset", (0, 1)) + def test_trace(self, offset): + mta = self.ma.trace(offset=offset) + expected = Masked( + self.a.trace(offset=offset), self.mask_a.trace(offset=offset, dtype=bool) + ) + assert_masked_equal(mta, expected) + + def test_clip(self): + maclip = self.ma.clip(self.b, self.c) + expected = Masked(self.a.clip(self.b, self.c), self.mask_a) + assert_masked_equal(maclip, expected) + + def test_clip_masked_min_max(self): + maclip = self.ma.clip(self.mb, self.mc) + # Need to be careful with min, max because of Longitude, which wraps. + dmax = np.maximum(np.maximum(self.a, self.b), self.c).max() + dmin = np.minimum(np.minimum(self.a, self.b), self.c).min() + expected = Masked( + self.a.clip(self.mb.filled(dmin), self.mc.filled(dmax)), mask=self.mask_a + ) + assert_masked_equal(maclip, expected) + + +class TestMaskedQuantityMethods(TestMaskedArrayMethods, QuantitySetup): + pass + + +class TestMaskedLongitudeMethods(TestMaskedArrayMethods, LongitudeSetup): + pass + + +class TestMaskedArrayProductMethods(MaskedArraySetup): + # These cannot work on Quantity, so done separately + @pytest.mark.parametrize("axis", (0, 1, None)) + def test_prod(self, axis): + ma_sum = self.ma.prod(axis) + expected_data = self.a.prod(axis) + expected_mask = self.ma.mask.any(axis) + assert_array_equal(ma_sum.unmasked, expected_data) + assert_array_equal(ma_sum.mask, expected_mask) + + @pytest.mark.parametrize("axis", (0, 1, None)) + def test_cumprod(self, axis): + ma_sum = self.ma.cumprod(axis) + expected_data = self.a.cumprod(axis) + mask = self.mask_a + if axis is None: + mask = mask.ravel() + expected_mask = np.logical_or.accumulate(mask, axis=axis) + assert_array_equal(ma_sum.unmasked, expected_data) + assert_array_equal(ma_sum.mask, expected_mask) + + +def test_masked_str_repr_explicit_float(): + sa = np.array([np.pi, 2 * np.pi]) + msa = Masked(sa, [False, True]) + # Test masking the array works as expected, including truncating digits + assert str(msa) == "[3.14159265 ———]" + # Test the digits are kept for scalars. + assert str(msa[0]) == "3.141592653589793" == str(sa[0]) + # And that the masked string has the same length. + assert str(msa[1]) == " ———" + # Test temporary precision change (which does not affect scalars). + with np.printoptions(precision=3, floatmode="fixed"): + assert str(msa) == "[3.142 ———]" + assert str(msa[0]) == "3.141592653589793" == str(sa[0]) + assert repr(msa) == "MaskedNDArray([3.14159265, ———])" + + +def test_masked_str_explicit_string(): + sa = np.array(["2001-02-03", "2002-03-04"]) + msa = Masked(sa, [False, True]) + assert str(msa) == "['2001-02-03' ———]" + assert str(msa[0]) == "2001-02-03" == str(sa[0]) + assert str(msa[1]) == " ———" + byteorder = "<" if sys.byteorder == "little" else ">" + repr_ = f"MaskedNDArray(['2001-02-03', ———], dtype='{byteorder}U10')" + assert repr(msa) == repr_ + + +def test_masked_str_explicit_structured(): + sa = np.array([(1.0, 2.0), (3.0, 4.0)], dtype="f8,f8") + msa = Masked(sa, [(False, True), (False, False)]) + assert str(msa) == "[(1., ——) (3., 4.)]" + assert str(msa[0]) == "(1.0, ———)" + assert str(msa[1]) == str(sa[1]) == "(3.0, 4.0)" + with np.printoptions(precision=3, floatmode="fixed"): + assert str(msa) == "[(1.000, ———) (3.000, 4.000)]" + assert str(msa[0]) == "(1.0, ———)" + assert str(msa[1]) == str(sa[1]) == "(3.0, 4.0)" + + +def test_masked_repr_explicit_structured(): + # Use explicit endianness to ensure tests pass on all architectures + sa = np.array([(1.0, 2.0), (3.0, 4.0)], dtype=">f8,>f8") + msa = Masked(sa, [(False, True), (False, False)]) + assert ( + repr(msa) + == "MaskedNDArray([(1., ——), (3., 4.)], dtype=[('f0', '>f8'), ('f1', '>f8')])" + ) + assert ( + repr(msa[0]) == "MaskedNDArray((1., ——), dtype=[('f0', '>f8'), ('f1', '>f8')])" + ) + assert ( + repr(msa[1]) == "MaskedNDArray((3., 4.), dtype=[('f0', '>f8'), ('f1', '>f8')])" + ) + + +def test_masked_repr_summary(): + ma = Masked(np.arange(15.0), mask=[True] + [False] * 14) + if NUMPY_LT_2_2: + expected = "MaskedNDArray([———, 1., 2., ..., 12., 13., 14.])" + else: + expected = "MaskedNDArray([———, 1., 2., ..., 12., 13., 14.], shape=(15,))" + + with np.printoptions(threshold=2): + assert repr(ma) == expected + + +def test_masked_repr_nodata(): + assert repr(Masked([])) == "MaskedNDArray([], dtype=float64)" + + +class TestMaskedArrayRepr(MaskedArraySetup): + def test_array_str(self): + # very blunt check they work at all. + str(self.ma) + str(self.mb) + str(self.mc) + str(self.msa) + str(self.msb) + str(self.msc) + + def test_scalar_str(self): + assert self.mb[0].shape == () + str(self.mb[0]) + assert self.msb[0].shape == () + str(self.msb[0]) + assert self.msc[0].shape == () + str(self.msc[0]) + + def test_array_repr(self): + repr(self.ma) + repr(self.mb) + repr(self.mc) + repr(self.msa) + repr(self.msb) + repr(self.msc) + + def test_scalar_repr(self): + repr(self.mb[0]) + repr(self.msb[0]) + repr(self.msc[0]) + + +class TestMaskedQuantityRepr(TestMaskedArrayRepr, QuantitySetup): + pass + + +class TestMaskedRecarray(MaskedArraySetup): + @classmethod + def setup_class(cls): + super().setup_class() + cls.ra = cls.sa.view(np.recarray) + cls.mra = Masked(cls.ra, mask=cls.mask_sa) + + def test_recarray_setup(self): + assert isinstance(self.mra, Masked) + assert isinstance(self.mra, np.recarray) + assert np.all(self.mra.unmasked == self.ra) + assert np.all(self.mra.mask == self.mask_sa) + assert_array_equal(self.mra.view(np.ndarray), self.sa) + assert isinstance(self.mra.a, Masked) + assert_array_equal(self.mra.a.unmasked, self.sa["a"]) + assert_array_equal(self.mra.a.mask, self.mask_sa["a"]) + + def test_recarray_setting(self): + mra = self.mra.copy() + mra.a = self.msa["b"] + assert_array_equal(mra.a.unmasked, self.msa["b"].unmasked) + assert_array_equal(mra.a.mask, self.msa["b"].mask) + + @pytest.mark.parametrize("attr", [0, "a"]) + def test_recarray_field_getting(self, attr): + mra_a = self.mra.field(attr) + assert isinstance(mra_a, Masked) + assert_array_equal(mra_a.unmasked, self.sa["a"]) + assert_array_equal(mra_a.mask, self.mask_sa["a"]) + + @pytest.mark.parametrize("attr", [0, "a"]) + def test_recarray_field_setting(self, attr): + mra = self.mra.copy() + mra.field(attr, self.msa["b"]) + assert_array_equal(mra.a.unmasked, self.msa["b"].unmasked) + assert_array_equal(mra.a.mask, self.msa["b"].mask) + + def test_recarray_repr(self): + # Omit dtype part with endian-dependence. + assert repr(self.mra).startswith( + "MaskedRecarray([[(———, ———), ( 3., 4.)],\n" + " [(11., ———), (———, 14.)]],\n" + ) + + def test_recarray_represent_as_dict(self): + rasd = self.mra.info._represent_as_dict() + assert type(rasd["data"]) is np.ma.MaskedArray + assert type(rasd["data"].base) is np.ndarray + mra2 = type(self.mra).info._construct_from_dict(rasd) + assert type(mra2) is type(self.mra) + assert_array_equal(mra2.unmasked, self.ra) + assert_array_equal(mra2.mask, self.mra.mask) + + +class TestMaskedArrayInteractionWithNumpyMA(MaskedArraySetup): + def test_masked_array_from_masked(self): + """Check that we can initialize a MaskedArray properly.""" + np_ma = np.ma.MaskedArray(self.ma) + assert type(np_ma) is np.ma.MaskedArray + assert type(np_ma.data) is self._data_cls + assert type(np_ma.mask) is np.ndarray + assert_array_equal(np_ma.data, self.a) + assert_array_equal(np_ma.mask, self.mask_a) + + def test_view_as_masked_array(self): + """Test that we can be viewed as a MaskedArray.""" + np_ma = self.ma.view(np.ma.MaskedArray) + assert type(np_ma) is np.ma.MaskedArray + assert type(np_ma.data) is self._data_cls + assert type(np_ma.mask) is np.ndarray + assert_array_equal(np_ma.data, self.a) + assert_array_equal(np_ma.mask, self.mask_a) + + +class TestMaskedQuantityInteractionWithNumpyMA( + TestMaskedArrayInteractionWithNumpyMA, QuantitySetup +): + pass + + +@pytest.mark.skipif(not HAS_PLT, reason="requires matplotlib.pyplot") +def test_plt_scatter_masked(): + # check that plotting Masked data doesn't raise an exception + # see https://github.com/astropy/astropy/issues/12481 + import matplotlib.pyplot as plt + + _, ax = plt.subplots() + + # no mask + x = Masked([1, 2, 3]) + ax.scatter(x, x, c=x) + + # all masked + x = Masked([1, 2, 3], mask=True) + ax.scatter(x, x, c=x) + + # *some* masked + x = Masked([1, 2, 3], mask=[False, True, False]) + ax.scatter(x, x, c=x) diff --git a/astropy/utils/masked/tests/test_pickle.py b/astropy/utils/masked/tests/test_pickle.py new file mode 100644 index 000000000000..1429a91fbd43 --- /dev/null +++ b/astropy/utils/masked/tests/test_pickle.py @@ -0,0 +1,37 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +import pickle + +import numpy as np +import pytest + +import astropy.units as u +from astropy.coordinates import Angle, Latitude, Longitude +from astropy.units import Quantity +from astropy.utils.masked import Masked + + +@pytest.mark.parametrize( + "data", + [ + Quantity([1, 2, 3], u.m), + Angle([1, 2, 3], u.deg), + Latitude([1, 2, 3], u.deg), + Longitude([1, 2, 3], u.deg), + ], +) +def test_masked_pickle(data): + mask = [True, False, False] + m = Masked(data, mask=mask) + + # Force creation of the info object (see #19142) + assert m.info is not None + + m2 = pickle.loads(pickle.dumps(m)) + + np.testing.assert_array_equal(m.unmasked, m2.unmasked) + np.testing.assert_array_equal(m.mask, m2.mask) + + assert m.unit == m2.unit + assert type(m) is type(m2) + assert type(m.info) is type(m2.info) diff --git a/astropy/utils/masked/tests/test_table.py b/astropy/utils/masked/tests/test_table.py new file mode 100644 index 000000000000..aaa2984aee8f --- /dev/null +++ b/astropy/utils/masked/tests/test_table.py @@ -0,0 +1,171 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +import numpy as np +import pytest +from numpy.testing import assert_array_equal + +from astropy import units as u +from astropy.table import QTable, hstack, join, vstack +from astropy.utils.compat.optional_deps import HAS_H5PY +from astropy.utils.masked import Masked + +from .test_masked import assert_masked_equal + +FILE_FORMATS = ["ecsv", "fits"] +if HAS_H5PY: + FILE_FORMATS.append("h5") + + +class MaskedArrayTableSetup: + @classmethod + def setup_arrays(cls): + cls.a = np.array([3.0, 5.0, 0.0]) + cls.mask_a = np.array([True, False, False]) + + @classmethod + def setup_class(cls): + cls.setup_arrays() + cls.ma = Masked(cls.a, mask=cls.mask_a) + cls.ma.info.format = ".1f" + cls.t = QTable([cls.ma], names=["ma"]) + + +class MaskedQuantityTableSetup(MaskedArrayTableSetup): + @classmethod + def setup_arrays(cls): + cls.a = np.array([3.0, 5.0, 0.0]) << u.m + cls.mask_a = np.array([True, False, False]) + + +class TestMaskedArrayTable(MaskedArrayTableSetup): + def test_table_initialization(self): + assert_array_equal(self.t["ma"].unmasked, self.a) + assert_array_equal(self.t["ma"].mask, self.mask_a) + assert repr(self.t).splitlines()[-3:] == [ + " ———", + " 5.0", + " 0.0", + ] + + def test_info_basics(self): + assert self.t["ma"].info.name == "ma" + assert "serialize_method" in self.t["ma"].info.attr_names + t2 = self.t.copy() + t2["ma"].info.format = ".2f" + t2["ma"].info.serialize_method["fits"] = "nonsense" + assert repr(t2).splitlines()[-3:] == [ + " ———", + " 5.00", + " 0.00", + ] + # Check that if we slice, things get copied over correctly. + t3 = t2[:2] + assert t3["ma"].info.name == "ma" + assert t3["ma"].info.format == ".2f" + assert "serialize_method" in t3["ma"].info.attr_names + assert t3["ma"].info.serialize_method["fits"] == "nonsense" + + @pytest.mark.parametrize("file_format", FILE_FORMATS) + def test_table_write(self, file_format, tmp_path): + name = tmp_path / f"a.{file_format}" + kwargs = {} + if file_format == "h5": + kwargs["path"] = "trial" + kwargs["serialize_meta"] = True + + self.t.write(name, **kwargs) + t2 = QTable.read(name) + assert isinstance(t2["ma"], self.ma.__class__) + assert np.all(t2["ma"] == self.ma) + assert np.all(t2["ma"].mask == self.mask_a) + if file_format == "fits": + # Imperfect roundtrip through FITS native format description. + assert self.t["ma"].info.format in t2["ma"].info.format + else: + assert t2["ma"].info.format == self.t["ma"].info.format + + @pytest.mark.parametrize("serialize_method", ["data_mask", "null_value"]) + def test_table_write_serialization(self, serialize_method, tmp_path): + name = tmp_path / "test.ecsv" + self.t.write(name, serialize_method=serialize_method) + with open(name) as fh: + lines = fh.readlines() + + t2 = QTable.read(name) + assert isinstance(t2["ma"], self.ma.__class__) + + if serialize_method == "data_mask": + # Will data_mask, we have data and mask columns and should + # exactly round-trip. + assert len(lines[-1].split()) == 2 + assert_masked_equal(t2["ma"], self.ma) + else: + # With null_value we have just a data column with null values + # marked, so we lost information on the data below the mask. + assert len(lines[-1].split()) == 1 + assert np.all(t2["ma"] == self.ma) + assert np.all(t2["ma"].mask == self.mask_a) + + def test_non_existing_serialize_method(self, tmp_path): + name = tmp_path / "bad.ecsv" + with pytest.raises(ValueError, match="serialize method must be"): + self.t.write(name, serialize_method="bad_serialize_method") + + +class TestMaskedQuantityTable(TestMaskedArrayTable, MaskedQuantityTableSetup): + # Runs tests from TestMaskedArrayTable as well as some extra ones. + def test_table_operations_requiring_masking(self): + t1 = self.t + t2 = QTable({"ma2": Masked([1, 2] * u.m)}) + t12 = hstack([t1, t2], join_type="outer") + assert np.all(t12["ma"].mask == [True, False, False]) + # 'ma2' is shorter by one so we expect one True from hstack so length matches + assert np.all(t12["ma2"].mask == [False, False, True]) + + t12 = hstack([t1, t2], join_type="inner") + assert np.all(t12["ma"].mask == [True, False]) + assert np.all(t12["ma2"].mask == [False, False]) + + # Vstack tables with different column names. In this case we get masked + # values + t12 = vstack([t1, t2], join_type="outer") + # ma ma2 + # m m + # --- --- + # —— —— + # 5.0 —— + # 0.0 —— + # —— 1.0 + # —— 2.0 + assert np.all(t12["ma"].mask == [True, False, False, True, True]) + assert np.all(t12["ma2"].mask == [True, True, True, False, False]) + + def test_table_operations_requiring_masking_auto_promote(self): + MaskedQuantity = Masked(u.Quantity) + t1 = QTable({"ma1": [1, 2] * u.m}) + t2 = QTable({"ma2": [3, 4, 5] * u.m}) + t12 = hstack([t1, t2], join_type="outer") + assert isinstance(t12["ma1"], MaskedQuantity) + assert np.all(t12["ma1"].mask == [False, False, True]) + assert np.all(t12["ma1"] == [1, 2, 0] * u.m) + assert not isinstance(t12["ma2"], MaskedQuantity) + assert isinstance(t12["ma2"], u.Quantity) + assert np.all(t12["ma2"] == [3, 4, 5] * u.m) + + t12 = hstack([t1, t2], join_type="inner") + assert isinstance(t12["ma1"], u.Quantity) + assert not isinstance(t12["ma1"], MaskedQuantity) + assert isinstance(t12["ma2"], u.Quantity) + assert not isinstance(t12["ma2"], MaskedQuantity) + + # Vstack tables with different column names. In this case we get masked + # values + t12 = vstack([t1, t2], join_type="outer") + assert np.all(t12["ma1"].mask == [False, False, True, True, True]) + assert np.all(t12["ma2"].mask == [True, True, False, False, False]) + + t1["a"] = [1, 2] + t2["a"] = [1, 3, 4] + t12 = join(t1, t2, join_type="outer") + assert np.all(t12["ma1"].mask == [False, False, True, True]) + assert np.all(t12["ma2"].mask == [False, True, False, False]) diff --git a/astropy/utils/metadata/__init__.py b/astropy/utils/metadata/__init__.py new file mode 100644 index 000000000000..7dd80bc3eae5 --- /dev/null +++ b/astropy/utils/metadata/__init__.py @@ -0,0 +1,17 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +"""This module contains helper functions and classes for handling metadata.""" + +from . import core as _core +from . import exceptions as _exceptions +from . import merge as _merge +from . import utils as _utils +from .core import * +from .exceptions import * +from .merge import * +from .utils import * + +__all__ = [] +__all__ += _core.__all__ +__all__ += _merge.__all__ +__all__ += _exceptions.__all__ +__all__ += _utils.__all__ diff --git a/astropy/utils/metadata/core.py b/astropy/utils/metadata/core.py new file mode 100644 index 000000000000..ffffd578ba68 --- /dev/null +++ b/astropy/utils/metadata/core.py @@ -0,0 +1,214 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +"""Classes for handling metadata.""" + +__all__ = ["MetaAttribute", "MetaData"] + +import inspect +from collections import OrderedDict +from collections.abc import Mapping +from copy import deepcopy +from dataclasses import is_dataclass + + +class MetaData: + """ + A descriptor for classes that have a ``meta`` property. + + This can be set to any valid :class:`~collections.abc.Mapping`. + + Parameters + ---------- + doc : `str`, optional + Documentation for the attribute of the class. + Default is ``""``. + + .. versionadded:: 1.2 + + copy : `bool`, optional + If ``True`` the value is deepcopied before setting, otherwise it + is saved as reference. + Default is ``True``. + + .. versionadded:: 1.2 + + default_factory : Callable[[], Mapping], optional keyword-only + The factory to use to create the default value of the ``meta`` + attribute. This must be a callable that returns a `Mapping` object. + Default is `OrderedDict`, creating an empty `OrderedDict`. + + .. versionadded:: 6.0 + + Examples + -------- + ``MetaData`` can be used as a descriptor to define a ``meta`` attribute`. + + >>> class Foo: + ... meta = MetaData() + ... def __init__(self, meta=None): + ... self.meta = meta + + ``Foo`` can be instantiated with a ``meta`` argument. + + >>> foo = Foo(meta={'a': 1, 'b': 2}) + >>> foo.meta + {'a': 1, 'b': 2} + + The default value of ``meta`` is an empty :class:`~collections.OrderedDict`. + This can be set by passing ``None`` to the ``meta`` argument. + + >>> foo = Foo() + >>> foo.meta + OrderedDict() + + If an :class:`~collections.OrderedDict` is not a good default metadata type then + the ``default_factory`` keyword can be used to set the default to a different + `Mapping` type, when the class is defined.' + + >>> class Bar: + ... meta = MetaData(default_factory=dict) + ... def __init__(self, meta=None): + ... self.meta = meta + + >>> Bar().meta + {} + + When accessed from the class ``.meta`` returns `None` since metadata is + on the class' instances, not the class itself. + + >>> print(Foo.meta) + None + """ + + def __init__(self, doc="", copy=True, *, default_factory=OrderedDict): + self.__doc__ = doc + self.copy = copy + self._default_factory = default_factory + + @property + def default_factory(self): + return self._default_factory + + def __get__(self, instance, owner): + # class attribute access. Often, descriptors just return `self`, but if the + # owning class is a `dataclass`, the expectation is that the default is + # returned. In our case, this is None, triggering the creation of a dict-like in + # `__set__`. + if instance is None: + return None + # instance attribute access + if not hasattr(instance, "_meta"): + self.__set__(instance, None) + return instance._meta + + def __set__(self, instance, value): + # The 'default' value is `None`, but we want to set it to an empty `Mapping` + # if it is `None` so that we can always assume it is a `Mapping` and not have + # to check for `None` everywhere. + if value is None: + value = self.default_factory() + # We don't want to allow setting the meta attribute to a non-dict-like object. + # NOTE: with mypyc compilation this can be removed. + elif not isinstance(value, Mapping): + raise TypeError("meta attribute must be dict-like") + # This is called when the dataclass is instantiated with a `meta` argument. + else: + value = deepcopy(value) if self.copy else value + + if is_dataclass(instance) and instance.__dataclass_params__.frozen: + object.__setattr__(instance, "_meta", value) + else: + instance._meta = value + + +class MetaAttribute: + """ + Descriptor to define custom attribute which gets stored in the object + ``meta`` dict and can have a defined default. + + This descriptor is intended to provide a convenient way to add attributes + to a subclass of a complex class such as ``Table`` or ``NDData``. + + This requires that the object has an attribute ``meta`` which is a + dict-like object. The value of the MetaAttribute will be stored in a + new dict meta['__attributes__'] that is created when required. + + Classes that define MetaAttributes are encouraged to support initializing + the attributes via the class ``__init__``. For example:: + + for attr in list(kwargs): + descr = getattr(self.__class__, attr, None) + if isinstance(descr, MetaAttribute): + setattr(self, attr, kwargs.pop(attr)) + + The name of a ``MetaAttribute`` cannot be the same as any of the following: + + - Keyword argument in the owner class ``__init__`` + - Method or attribute of the "parent class", where the parent class is + taken to be ``owner.__mro__[1]``. + + Parameters + ---------- + default : Any, optional + Default value for the attribute, by default `None`. + """ + + def __init__(self, default=None): + self.default = default + + def __get__(self, instance, owner): + # When called without an instance, return self to allow access + # to descriptor attributes. + if instance is None: + return self + + # If default is None and value has not been set already then return None + # without doing touching meta['__attributes__'] at all. This helps e.g. + # with the Table._hidden_columns attribute so it doesn't auto-create + # meta['__attributes__'] always. + if self.default is None and self.name not in instance.meta.get( + "__attributes__", {} + ): + return None + + # Get the __attributes__ dict and create if not there already. + attributes = instance.meta.setdefault("__attributes__", {}) + try: + value = attributes[self.name] + except KeyError: + if self.default is not None: + attributes[self.name] = deepcopy(self.default) + # Return either specified default or None + value = attributes.get(self.name) + return value + + def __set__(self, instance, value): + # Get the __attributes__ dict and create if not there already. + attributes = instance.meta.setdefault("__attributes__", {}) + attributes[self.name] = value + + def __delete__(self, instance): + # Remove this attribute from meta['__attributes__'] if it exists. + if "__attributes__" in instance.meta: + attrs = instance.meta["__attributes__"] + if self.name in attrs: + del attrs[self.name] + # If this was the last attribute then remove the meta key as well + if not attrs: + del instance.meta["__attributes__"] + + def __set_name__(self, owner, name): + params = [ + param.name + for param in inspect.signature(owner).parameters.values() + if param.kind + not in (inspect.Parameter.VAR_KEYWORD, inspect.Parameter.VAR_POSITIONAL) + ] + + # Reject names from existing params or best guess at parent class + if name in params or hasattr(owner.__mro__[1], name): + raise ValueError(f"{name} not allowed as {self.__class__.__name__}") + + self.name = name + + def __repr__(self): + return f"<{self.__class__.__name__} name={self.name} default={self.default}>" diff --git a/astropy/utils/metadata/exceptions.py b/astropy/utils/metadata/exceptions.py new file mode 100644 index 000000000000..d8e05a9260b0 --- /dev/null +++ b/astropy/utils/metadata/exceptions.py @@ -0,0 +1,14 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +"""Metadata exceptions and warnings.""" + +from astropy.utils.exceptions import AstropyWarning + +__all__ = ["MergeConflictError", "MergeConflictWarning"] + + +class MergeConflictError(TypeError): + pass + + +class MergeConflictWarning(AstropyWarning): + pass diff --git a/astropy/utils/metadata/merge.py b/astropy/utils/metadata/merge.py new file mode 100644 index 000000000000..ec21cada0347 --- /dev/null +++ b/astropy/utils/metadata/merge.py @@ -0,0 +1,300 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +"""Metadata merging.""" + +import warnings +from contextlib import contextmanager +from copy import deepcopy + +import numpy as np + +from .exceptions import MergeConflictError, MergeConflictWarning +from .utils import result_type + +__all__ = [ + "MERGE_STRATEGIES", + "MergeNpConcatenate", + "MergePlus", + "MergeStrategy", + "enable_merge_strategies", + "merge", +] + +MERGE_STRATEGIES = [] + + +class MergeStrategy: + """ + Base class for defining a strategy for merging metadata from two + sources, left and right, into a single output. + + The primary functionality for the class is the ``merge(cls, left, right)`` + class method. This takes ``left`` and ``right`` side arguments and + returns a single merged output. + + The first class attribute is ``types``. This is defined as a list of + (left_types, right_types) tuples that indicate for which input types the + merge strategy applies. In determining whether to apply this merge + strategy to a pair of (left, right) objects, a test is done: + ``isinstance(left, left_types) and isinstance(right, right_types)``. For + example:: + + types = [(np.ndarray, np.ndarray), # Two ndarrays + (np.ndarray, (list, tuple)), # ndarray and (list or tuple) + ((list, tuple), np.ndarray)] # (list or tuple) and ndarray + + As a convenience, ``types`` can be defined as a single two-tuple instead of + a list of two-tuples, e.g. ``types = (np.ndarray, np.ndarray)``. + + The other class attribute is ``enabled``, which defaults to ``False`` in + the base class. By defining a subclass of ``MergeStrategy`` the new merge + strategy is automatically registered to be available for use in + merging. However, by default the new merge strategy is *not enabled*. This + prevents inadvertently changing the behavior of unrelated code that is + performing metadata merge operations. + + In most cases (particularly in library code that others might use) it is + recommended to leave custom strategies disabled and use the + `~astropy.utils.metadata.enable_merge_strategies` context manager to locally + enable the desired strategies. However, if one is confident that the + new strategy will not produce unexpected behavior, then one can globally + enable it by setting the ``enabled`` class attribute to ``True``. + + Examples + -------- + Here we define a custom merge strategy that takes an int or float on + the left and right sides and returns a list with the two values. + + >>> from astropy.utils.metadata import MergeStrategy + >>> class MergeNumbersAsList(MergeStrategy): + ... types = ((int, float), (int, float)) # (left_types, right_types) + ... + ... @classmethod + ... def merge(cls, left, right): + ... return [left, right] + + """ + + # Set ``enabled = True`` to globally enable applying this merge strategy. + # This is not generally recommended. + enabled = False + + # types = [(left_types, right_types), ...] + + def __init_subclass__(cls): + members = vars(cls) + # Register merging class (except for base MergeStrategy class) + if (types := members.get("types")) is not None: + if isinstance(types, tuple): + types = [types] + for left, right in reversed(types): + MERGE_STRATEGIES.insert(0, (left, right, cls)) + + @classmethod + def _merge(cls, left, right): + try: + return cls.merge(left, right) + except Exception as err: + raise MergeConflictError(err) + + +class MergePlus(MergeStrategy): + """ + Merge ``left`` and ``right`` objects using the plus operator. This + merge strategy is globally enabled by default. + """ + + types = [(list, list), (tuple, tuple)] + enabled = True + + @classmethod + def merge(cls, left, right): + return left + right + + +class MergeNpConcatenate(MergeStrategy): + """ + Merge ``left`` and ``right`` objects using np.concatenate. This + merge strategy is globally enabled by default. + + This will upcast a list or tuple to np.ndarray and the output is + always ndarray. + """ + + types = [ + (np.ndarray, np.ndarray), + (np.ndarray, (list, tuple)), + ((list, tuple), np.ndarray), + ] + enabled = True + + @classmethod + def merge(cls, left, right): + left, right = np.asanyarray(left), np.asanyarray(right) + result_type([left, right]) # Ensure left and right have compatible dtype + return np.concatenate([left, right]) + + +# ============================================================================ + + +@contextmanager +def enable_merge_strategies(*merge_strategies): + """ + Context manager to temporarily enable one or more custom metadata merge + strategies. + + Examples + -------- + Here we define a custom merge strategy that takes an int or float on + the left and right sides and returns a list with the two values. + + >>> from astropy.utils.metadata import MergeStrategy + >>> class MergeNumbersAsList(MergeStrategy): + ... types = ((int, float), # left side types + ... (int, float)) # right side types + ... @classmethod + ... def merge(cls, left, right): + ... return [left, right] + + By defining this class the merge strategy is automatically registered to be + available for use in merging. However, by default new merge strategies are + *not enabled*. This prevents inadvertently changing the behavior of + unrelated code that is performing metadata merge operations. + + In order to use the new merge strategy, use this context manager as in the + following example:: + + >>> from astropy.table import Table, vstack + >>> from astropy.utils.metadata import enable_merge_strategies + >>> t1 = Table([[1]], names=['a']) + >>> t2 = Table([[2]], names=['a']) + >>> t1.meta = {'m': 1} + >>> t2.meta = {'m': 2} + >>> with enable_merge_strategies(MergeNumbersAsList): + ... t12 = vstack([t1, t2]) + >>> t12.meta['m'] + [1, 2] + + One can supply further merge strategies as additional arguments to the + context manager. + + As a convenience, the enabling operation is actually done by checking + whether the registered strategies are subclasses of the context manager + arguments. This means one can define a related set of merge strategies and + then enable them all at once by enabling the base class. As a trivial + example, *all* registered merge strategies can be enabled with:: + + >>> with enable_merge_strategies(MergeStrategy): + ... t12 = vstack([t1, t2]) + + Parameters + ---------- + *merge_strategies : :class:`~astropy.utils.metadata.MergeStrategy` class + Merge strategies that will be enabled. + """ + orig_enabled = {} + for _, _, merge_strategy in MERGE_STRATEGIES: + if issubclass(merge_strategy, merge_strategies): + orig_enabled[merge_strategy] = merge_strategy.enabled + merge_strategy.enabled = True + + yield + + for merge_strategy, enabled in orig_enabled.items(): + merge_strategy.enabled = enabled + + +# ============================================================================= + + +def _both_isinstance(left, right, cls): + return isinstance(left, cls) and isinstance(right, cls) + + +def _not_equal(left, right): + try: + return bool(left != right) + except Exception: + return True + + +def _warn_str_func(key, left, right): + out = ( + f"Cannot merge meta key {key!r} types {type(left)!r}" + f" and {type(right)!r}, choosing {key}={right!r}" + ) + return out + + +def _error_str_func(key, left, right): + out = f"Cannot merge meta key {key!r} types {type(left)!r} and {type(right)!r}" + return out + + +def _raise_merge_conflict_error(left, right): + raise MergeConflictError + + +def merge( + left, + right, + merge_func=None, + metadata_conflicts="warn", + warn_str_func=_warn_str_func, + error_str_func=_error_str_func, +): + """ + Merge the ``left`` and ``right`` metadata objects. + + This is a simplistic and limited implementation at this point. + """ + if not _both_isinstance(left, right, dict): + raise MergeConflictError("Can only merge two dict-based objects") + + out = deepcopy(left) + + for key, right_val in right.items(): + # If no conflict then insert val into out dict and continue + if key not in out: + out[key] = deepcopy(right_val) + continue + + # There is a conflict that must be resolved + left_val = out[key] + if _both_isinstance(left_val, right_val, dict): + out[key] = merge( + left_val, right_val, merge_func, metadata_conflicts=metadata_conflicts + ) + continue + + local_merge_func = merge_func + if local_merge_func is None: + for left_type, right_type, merge_cls in MERGE_STRATEGIES: + if ( + merge_cls.enabled + and isinstance(left_val, left_type) + and isinstance(right_val, right_type) + ): + local_merge_func = merge_cls._merge + break + else: + local_merge_func = _raise_merge_conflict_error + try: + out[key] = local_merge_func(left_val, right_val) + except MergeConflictError: + # If right_val is None or equal to left_val then we are happy with what's + # already in out. Otherwise right_val takes priority (or there's an error). + if right_val is not None and _not_equal(left_val, right_val): + if metadata_conflicts == "warn": + warnings.warn( + warn_str_func(key, left_val, right_val), MergeConflictWarning + ) + elif metadata_conflicts == "error": + raise MergeConflictError(error_str_func(key, left_val, right_val)) + elif metadata_conflicts != "silent": + raise ValueError( + "metadata_conflicts argument must be one " + 'of "silent", "warn", or "error"' + ) + out[key] = right_val + return out diff --git a/astropy/utils/metadata/tests/__init__.py b/astropy/utils/metadata/tests/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/astropy/utils/metadata/tests/test_metadata.py b/astropy/utils/metadata/tests/test_metadata.py new file mode 100644 index 000000000000..436c2f602586 --- /dev/null +++ b/astropy/utils/metadata/tests/test_metadata.py @@ -0,0 +1,367 @@ +from collections import OrderedDict, defaultdict +from dataclasses import dataclass + +import numpy as np +import pytest + +from astropy.io import fits +from astropy.utils import metadata +from astropy.utils.metadata import ( + MergeConflictError, + MetaData, + common_dtype, + enable_merge_strategies, + merge, +) +from astropy.utils.metadata.utils import result_type + + +class OrderedDictSubclass(OrderedDict): + pass + + +class MetaBaseTest: + def test_none(self): + d = self.test_class(*self.args) + assert isinstance(d.meta, dict) + assert len(d.meta) == 0 + + @pytest.mark.parametrize( + "meta", + ([{"a": 1}, OrderedDict([("a", 1)]), OrderedDictSubclass([("a", 1)])]), + ) + def test_mapping_init(self, meta): + d = self.test_class(*self.args, meta=meta) + assert type(d.meta) == type(meta) + assert d.meta["a"] == 1 + + @pytest.mark.parametrize("meta", (["ceci n'est pas un meta", 1.2, [1, 2, 3]])) + def test_non_mapping_init(self, meta): + with pytest.raises(TypeError): + self.test_class(*self.args, meta=meta) + + @pytest.mark.parametrize( + "meta", + ([{"a": 1}, OrderedDict([("a", 1)]), OrderedDictSubclass([("a", 1)])]), + ) + def test_mapping_set(self, meta): + d = self.test_class(*self.args, meta=meta) + assert type(d.meta) == type(meta) + assert d.meta["a"] == 1 + + @pytest.mark.parametrize("meta", (["ceci n'est pas un meta", 1.2, [1, 2, 3]])) + def test_non_mapping_set(self, meta): + with pytest.raises(TypeError): + d = self.test_class(*self.args, meta=meta) + + def test_meta_fits_header(self): + header = fits.header.Header() + header.set("observer", "Edwin Hubble") + header.set("exptime", "3600") + + d = self.test_class(*self.args, meta=header) + + assert d.meta["OBSERVER"] == "Edwin Hubble" + + +class ExampleData: + meta = MetaData() + + def __init__(self, meta=None): + self.meta = meta + + +class TestMetaExampleData(MetaBaseTest): + test_class = ExampleData + args = () + + +@dataclass +class ExampleDataclass: + meta: MetaData = MetaData() # noqa: RUF009 + + +class TestMetaExampleDataclass(MetaBaseTest): + test_class = ExampleDataclass + args = () + + +@dataclass(frozen=True) +class ExampleFrozenDataclass: + meta: MetaData = MetaData() # noqa: RUF009 + + +class TestMetaExampleFrozenDataclass(MetaBaseTest): + test_class = ExampleFrozenDataclass + args = () + + +def test_metadata_default(): + data = ExampleDataclass() + data.meta["a"] = 1 + assert isinstance(data.meta, OrderedDict) + + +def test_metadata_default_factory(): + """Test the default_factory argument to MetaData.""" + + class ExampleData: + meta = MetaData(default_factory=defaultdict) + + def __init__(self, meta=None): + self.meta = meta + + data = ExampleData() + assert isinstance(data.meta, defaultdict) + assert len(data.meta) == 0 + + +def test_metadata_merging_conflict_exception(): + """Regression test for issue #3294. + + Ensure that an exception is raised when a metadata conflict exists + and ``metadata_conflicts='error'`` has been set. + """ + data1 = ExampleData() + data2 = ExampleData() + data1.meta["somekey"] = {"x": 1, "y": 1} + data2.meta["somekey"] = {"x": 1, "y": 999} + with pytest.raises(MergeConflictError): + merge(data1.meta, data2.meta, metadata_conflicts="error") + + +def test_metadata_merging(): + # Recursive merge + meta1 = { + "k1": { + "k1": [1, 2], + "k2": 2, + }, + "k2": 2, + "k4": (1, 2), + } + meta2 = { + "k1": {"k1": [3]}, + "k3": 3, + "k4": (3,), + } + out = merge(meta1, meta2, metadata_conflicts="error") + assert out == { + "k1": { + "k2": 2, + "k1": [1, 2, 3], + }, + "k2": 2, + "k3": 3, + "k4": (1, 2, 3), + } + + # Merge two ndarrays + meta1 = {"k1": np.array([1, 2])} + meta2 = {"k1": np.array([3])} + out = merge(meta1, meta2, metadata_conflicts="error") + assert np.all(out["k1"] == np.array([1, 2, 3])) + + # Merge list and np.ndarray + meta1 = {"k1": [1, 2]} + meta2 = {"k1": np.array([3])} + assert np.all(out["k1"] == np.array([1, 2, 3])) + + # Can't merge two scalar types + meta1 = {"k1": 1} + meta2 = {"k1": 2} + with pytest.raises(MergeConflictError): + merge(meta1, meta2, metadata_conflicts="error") + + # Conflicting shape + meta1 = {"k1": np.array([1, 2])} + meta2 = {"k1": np.array([[3]])} + with pytest.raises(MergeConflictError): + merge(meta1, meta2, metadata_conflicts="error") + + # Conflicting array type + meta1 = {"k1": np.array([1, 2])} + meta2 = {"k1": np.array(["3"])} + with pytest.raises(MergeConflictError): + merge(meta1, meta2, metadata_conflicts="error") + + # Conflicting array type with 'silent' merging + meta1 = {"k1": np.array([1, 2])} + meta2 = {"k1": np.array(["3"])} + out = merge(meta1, meta2, metadata_conflicts="silent") + assert np.all(out["k1"] == np.array(["3"])) + + +def test_metadata_merging_new_strategy(): + original_merge_strategies = list(metadata.MERGE_STRATEGIES) + + class MergeNumbersAsList(metadata.MergeStrategy): + """ + Scalar float or int values are joined in a list. + """ + + types = ((int, float), (int, float)) + + @classmethod + def merge(cls, left, right): + return [left, right] + + class MergeConcatStrings(metadata.MergePlus): + """ + Scalar string values are concatenated + """ + + types = (str, str) + enabled = False + + # Normally can't merge two scalar types + meta1 = {"k1": 1, "k2": "a"} + meta2 = {"k1": 2, "k2": "b"} + + # Enable new merge strategy + with enable_merge_strategies(MergeNumbersAsList, MergeConcatStrings): + assert MergeNumbersAsList.enabled + assert MergeConcatStrings.enabled + out = merge(meta1, meta2, metadata_conflicts="error") + assert out["k1"] == [1, 2] + assert out["k2"] == "ab" + assert not MergeNumbersAsList.enabled + assert not MergeConcatStrings.enabled + + # Confirm the default enabled=False behavior + with pytest.raises(MergeConflictError): + merge(meta1, meta2, metadata_conflicts="error") + + # Enable all MergeStrategy subclasses + with enable_merge_strategies(metadata.MergeStrategy): + assert MergeNumbersAsList.enabled + assert MergeConcatStrings.enabled + out = merge(meta1, meta2, metadata_conflicts="error") + assert out["k1"] == [1, 2] + assert out["k2"] == "ab" + assert not MergeNumbersAsList.enabled + assert not MergeConcatStrings.enabled + + metadata.MERGE_STRATEGIES = original_merge_strategies + + +def test_result_type_string(): + u3 = np.array(["123"]) + u4 = np.array(["1234"]) + b3 = np.array([b"123"]) + b5 = np.array([b"12345"]) + assert result_type([u3, u4]) == np.dtype("U4") + assert result_type([b5, u4]) == np.dtype("U5") + assert result_type([b3, b5]) == np.dtype("S5") + + +def test_result_type_basic(): + i8 = np.array(1, dtype=np.int64) + f8 = np.array(1, dtype=np.float64) + u3 = np.array("123") + + with pytest.raises(MergeConflictError): + result_type([i8, u3]) + + assert result_type([i8, i8]) == np.dtype("i8") + assert result_type([i8, f8]) == np.dtype("f8") + + +@pytest.mark.parametrize("function", [result_type, common_dtype]) +def test_finding_common_type_exhaustive(function): + """ + Test that allowed combinations are those expected. + """ + dtype = [ + ("int", int), + ("uint8", np.uint8), + ("float32", np.float32), + ("float64", np.float64), + ("str", "S2"), + ("uni", "U2"), + ("bool", bool), + ("object", np.object_), + ] + arr = np.empty(1, dtype=dtype) + fail = set() + succeed = set() + for name1, _ in dtype: + for name2, _ in dtype: + try: + function([arr[name1], arr[name2]]) + succeed.add(f"{name1} {name2}") + except MergeConflictError: + fail.add(f"{name1} {name2}") + + # known bad combinations + bad = { + "str int", + "str bool", + "uint8 bool", + "uint8 str", + "object float32", + "bool object", + "uni uint8", + "int str", + "bool str", + "bool float64", + "bool uni", + "str float32", + "uni float64", + "uni object", + "bool uint8", + "object float64", + "float32 bool", + "str uint8", + "uni bool", + "float64 bool", + "float64 object", + "int bool", + "uni int", + "uint8 object", + "int uni", + "uint8 uni", + "float32 uni", + "object uni", + "bool float32", + "uni float32", + "object str", + "int object", + "str float64", + "object int", + "float64 uni", + "bool int", + "object bool", + "object uint8", + "float32 object", + "str object", + "float64 str", + "float32 str", + } + assert fail == bad + + good = { + "float64 int", + "int int", + "uint8 float64", + "uint8 int", + "str uni", + "float32 float32", + "float64 float64", + "float64 uint8", + "float64 float32", + "int uint8", + "int float32", + "uni str", + "int float64", + "uint8 float32", + "float32 int", + "float32 uint8", + "bool bool", + "uint8 uint8", + "str str", + "float32 float64", + "object object", + "uni uni", + } + assert succeed == good diff --git a/astropy/utils/metadata/utils.py b/astropy/utils/metadata/utils.py new file mode 100644 index 000000000000..19573d6462a6 --- /dev/null +++ b/astropy/utils/metadata/utils.py @@ -0,0 +1,69 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +"""This module contains helper functions handling metadata.""" + +import numpy as np + +from .exceptions import MergeConflictError + +__all__ = ["common_dtype"] + + +def dtype(arr): + return getattr(arr, "dtype", np.dtype("O")) + + +def common_dtype(arrs): + """ + Use numpy to find the common dtype for a list of ndarrays. + + Only allow arrays within the following fundamental numpy data types: + ``np.bool_``, ``np.object_``, ``np.number``, ``np.character``, ``np.void`` + + Parameters + ---------- + arrs : list of ndarray + Arrays for which to find the common dtype + + Returns + ------- + dtype_str : str + String representation of dytpe (dtype ``str`` attribute) + """ + dt = result_type(arrs) + return dt.str if dt.names is None else dt.descr + + +def result_type(arrs): + """ + Use numpy to find the common type for a list of ndarray. + + The difference with `numpy.result_type` is that all arrays should + share the same fundamental numpy data type, one of: + ``np.bool_``, ``np.object_``, ``np.number``, ``np.character``, ``np.void`` + Hence, a mix like integer and string will raise instead of resulting + in a string type. + + Parameters + ---------- + arrs : list of ndarray + Array likes for which to find the common dtype. Anything not + an array (e.g, |Time|), will be considered an object array. + + Returns + ------- + dtype : ~numpy.dtype + The common type. + """ + dtypes = [dtype(arr) for arr in arrs] + np_types = (np.bool_, np.object_, np.number, np.character, np.void) + uniq_types = { + tuple(issubclass(dt.type, np_type) for np_type in np_types) for dt in dtypes + } + if len(uniq_types) > 1: + # Embed into the exception the actual list of incompatible types. + incompat_types = [dt.name for dt in dtypes] + tme = MergeConflictError(f"Arrays have incompatible types {incompat_types}") + tme._incompat_types = incompat_types + raise tme + + return np.result_type(*dtypes) diff --git a/astropy/utils/misc.py b/astropy/utils/misc.py index 8e8eee8dc313..9a029f58fb3e 100644 --- a/astropy/utils/misc.py +++ b/astropy/utils/misc.py @@ -1,522 +1,550 @@ # Licensed under a 3-clause BSD style license - see LICENSE.rst """ -A "grab bag" of smallish general-purpose utilities that don't have a -clear other module/pakcage to live in. +A "grab bag" of relatively small general-purpose utilities that don't have +a clear module/package to live in. """ -from __future__ import absolute_import - -import collections -import functools +import contextlib +import difflib +import inspect +import json +import locale +import re import sys -import textwrap -import warnings +import threading +import traceback +import unicodedata +from collections import defaultdict +from collections.abc import Callable, Generator, Iterable +from contextlib import contextmanager +from itertools import chain +from types import TracebackType +from typing import Final +from urllib.parse import urlencode + +import numpy as np + +from astropy.utils import deprecated +from astropy.version import version as __version__ + +__all__ = [ + "JsonCustomEncoder", + "NumpyRNGContext", + "dtype_bytes_or_chars", + "find_api_page", + "format_exception", + "indent", + "isiterable", + "online_help", + "silence", +] + +NOT_OVERWRITING_MSG: Final = ( + "File {} already exists. If you mean to replace it " + 'then use the argument "overwrite=True".' +) +# A useful regex for tests. +_NOT_OVERWRITING_MSG_MATCH: Final = ( + r"File .* already exists\. If you mean to " + r"replace it then use the argument " + r'"overwrite=True"\.' +) + + +@deprecated(since="7.2", alternative="numpy.iterable()") +def isiterable(obj): + """Returns `True` if the given object is iterable.""" + try: + iter(obj) + return True + except TypeError: + return False -__all__ = ['find_current_module', 'fnpickle', 'fnunpickle', 'isiterable', - 'deprecated', 'lazyproperty'] +@deprecated(since="6.1", alternative="textwrap.indent()") +def indent(s, shift=1, width=4): + """Indent a block of text. The indentation is applied to each line.""" + indented = "\n".join(" " * (width * shift) + l if l else "" for l in s.splitlines()) + if s[-1] == "\n": + indented += "\n" -def find_current_module(depth=1, finddiff=False): - """ Determines the module/package from which this function is called. + return indented - This function has two modes, determined by the `finddiff` option. it - will either simply go the requested number of frames up the call - stack (if `finddiff` is False), or it will go up the call stack until - it reaches a module that is *not* in a specified set. - Parameters - ---------- - depth : int - Specifies how far back to go in the call stack (0-indexed, so that - passing in 0 gives back `astropy.utils.misc`). - finddiff : bool or list - If False, the returned `mod` will just be `depth` frames up from - the current frame. Otherwise, the function will start at a frame - `depth` up from current, and continue up the call stack to the - first module that is *different* from those in the provided list. - In this case, `finddiff` can be a list of modules or modules - names. Alternatively, it can be True, which will use the module - `depth` call stack frames up as the module the returned module - most be different from. +class _DummyFile: + """A noop writeable object.""" - Returns - ------- - mod : module or None - The module object or None if the package cannot be found. The name of - the module is available as the ``__name__`` attribute of the returned - object (if it isn't None). + def write(self, s: str) -> None: + pass - Raises - ------ - ValueError - If `finddiff` is a list with an invalid entry. + def flush(self) -> None: + pass - Examples - -------- - The examples below assume that there are two modules in a package named - `pkg`. ``mod1.py``:: - - def find1(): - from astropy.utils import find_current_module - print find_current_module(1).__name__ - def find2(): - from astropy.utils import find_current_module - cmod = find_current_module(2) - if cmod is None: - print 'None' - else: - print cmod.__name__ - def find_diff(): - from astropy.utils import find_current_module - print find_current_module(0,True).__name__ - - ``mod2.py``:: - - def find(): - from .mod1 import find2 - find2() - - With these modules in place, the following occurs:: - - >>> from pkg import mod1, mod2 - >>> from astropy.utils import find_current_module - >>> mod1.find1() - pkg.mod1 - >>> mod1.find2() - None - >>> mod2.find() - pkg.mod2 - >>> find_current_module(0) - - >>> mod1.find_diff() - pkg.mod1 + def isatty(self) -> bool: + return False + + +@contextlib.contextmanager +def silence() -> Generator[None, None, None]: + """A context manager that silences sys.stdout and sys.stderr.""" + old_stdout = sys.stdout + old_stderr = sys.stderr + sys.stdout = _DummyFile() + sys.stderr = _DummyFile() + yield + sys.stdout = old_stdout + sys.stderr = old_stderr + + +@deprecated(since="7.0") +def format_exception(msg, *args, **kwargs): + """Fill in information about the exception that occurred. + + Given an exception message string, uses new-style formatting arguments + ``{filename}``, ``{lineno}``, ``{func}`` and/or ``{text}`` to fill in + information about the exception that occurred. For example: + + try: + 1/0 + except: + raise ZeroDivisionError( + format_except('A divide by zero occurred in {filename} at ' + 'line {lineno} of function {func}.')) + + Any additional positional or keyword arguments passed to this function are + also used to format the message. + .. note:: + This uses `sys.exc_info` to gather up the information needed to fill + in the formatting arguments. Since `sys.exc_info` is not carried + outside a handled exception, it's not wise to use this + outside of an ``except`` clause - if it is, this will substitute + '' for the 4 formatting arguments. """ - from inspect import currentframe, ismodule - - # using a patched version of getmodule because the py 3.1 and 3.2 stdlib - # is broken if the list of modules changes during import - from .compat import inspect_getmodule - - frm = currentframe() - for i in range(depth): - frm = frm.f_back - if frm is None: - return None - - if finddiff: - currmod = inspect_getmodule(frm) - if finddiff is True: - diffmods = [currmod] - else: - diffmods = [] - for fd in finddiff: - if ismodule(fd): - diffmods.append(fd) - elif isinstance(fd, basestring): - diffmods.append(__import__(fd)) - elif fd is True: - diffmods.append(currmod) - else: - raise ValueError('invalid entry in finddiff') - - while frm: - frmb = frm.f_back - modb = inspect_getmodule(frmb) - if modb not in diffmods: - return modb - frm = frmb + tb = traceback.extract_tb(sys.exc_info()[2], limit=1) + if len(tb) > 0: + filename, lineno, func, text = tb[0] else: - return inspect_getmodule(frm) + filename = lineno = func = text = "" + return msg.format( + *args, filename=filename, lineno=lineno, func=func, text=text, **kwargs + ) -def find_mod_objs(modname, onlylocals=False): - """ Returns all the public attributes of a module referenced by name. - .. note:: - The returned list *not* include subpackages or modules of - `modname`,nor does it include private attributes (those that - beginwith '_' or are not in `__all__`). +class NumpyRNGContext: + """ + A context manager (for use with the ``with`` statement) that will seed the + numpy random number generator (RNG) to a specific value, and then restore + the RNG state back to whatever it was before. + + This is primarily intended for use in the astropy testing suit, but it + may be useful in ensuring reproducibility of Monte Carlo simulations in a + science context. Parameters ---------- - modname : str - The name of the module to search. - onlylocals : bool - If True, only attributes that are either members of `modname` OR one of - its modules or subpackages will be included. + seed : int + The value to use to seed the numpy RNG - Returns - ------- - localnames : list of str - A list of the names of the attributes as they are named in the - module `modname` . - fqnames : list of str - A list of the full qualified names of the attributes (e.g., - ``astropy.utils.misc.find_mod_objs``). For attributes that are - simple variables, this is based on the local name, but for - functions or classes it can be different if they are actually - defined elsewhere and just referenced in `modname`. - objs : list of objects - A list of the actual attributes themselves (in the same order as - the other arguments) + Examples + -------- + A typical use case might be:: - """ - from inspect import ismodule + with NumpyRNGContext(): + from numpy import random - __import__(modname) - mod = sys.modules[modname] + randarr = random.randn(100) + ... run your test using `randarr` ... - if hasattr(mod, '__all__'): - pkgitems = [(k, getattr(mod, k)) for k in mod.__all__] - else: - pkgitems = [(k, getattr(mod, k)) for k in dir(mod) if k[0] != '_'] + #Any code using numpy.random at this indent level will act just as it + #would have if it had been before the with statement - e.g. whatever + #the default seed is. - #filter out modules and pull the names and objs out - localnames = [k for k, v in pkgitems if not ismodule(v)] - objs = [v for k, v in pkgitems if not ismodule(v)] - #fully qualified names can be determined from the object's module - fqnames = [] - for obj, lnm in zip(objs, localnames): - if hasattr(obj, '__module__') and hasattr(obj, '__name__'): - fqnames.append(obj.__module__ + '.' + obj.__name__) - else: - fqnames.append(modname + '.' + lnm) + """ - if onlylocals: - valids = [fqn.startswith(modname) for fqn in fqnames] - localnames = [e for i, e in enumerate(localnames) if valids[i]] - fqnames = [e for i, e in enumerate(fqnames) if valids[i]] - objs = [e for i, e in enumerate(objs) if valids[i]] + def __init__(self, seed: int) -> None: + self.seed = seed - return localnames, fqnames, objs + def __enter__(self) -> None: + self.startstate = np.random.get_state() + np.random.seed(self.seed) + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + traceback: TracebackType | None, + ) -> None: + np.random.set_state(self.startstate) -def fnunpickle(fileorname, number=0, usecPickle=True): - """ Unpickle pickled objects from a specified file and return the contents. + +def find_api_page( + obj: object, + version: str | None = None, + openinbrowser: bool = True, + timeout: float | None = None, +) -> str: + """ + Determines the URL of the API page for the specified object, and + optionally open that page in a web browser. + + .. note:: + You must be connected to the internet for this to function even if + ``openinbrowser`` is `False`, unless you provide a local version of + the documentation to ``version`` (e.g., ``file:///path/to/docs``). Parameters ---------- - fileorname : str or `file`-like - The file name or file from which to unpickle objects. If a file object, - it should have been opened in binary mode. - number : int - If 0, a single object will be returned (the first in the file). If >0, - this specifies the number of objects to be unpickled, and a list will - be returned with exactly that many objects. If <0, all objects in the - file will be unpickled and returned as a list. - usecPickle : bool - If True, the :mod:`cPickle` module is to be used in place of - :mod:`pickle` (cPickle is faster). This only applies for python 2.x. - - Raises - ------ - EOFError - If `number` is >0 and there are fewer than `number` objects in the - pickled file. + obj + The object to open the docs for or its fully-qualified name + (as a str). + version : str + The doc version - either a version number like '0.1', 'dev' for + the development/latest docs, or a URL to point to a specific + location that should be the *base* of the documentation. Defaults to + latest if you are on aren't on a release, otherwise, the version you + are on. + openinbrowser : bool + If `True`, the `webbrowser` package will be used to open the doc + page in a new web browser window. + timeout : number, optional + The number of seconds to wait before timing-out the query to + the astropy documentation. If not given, the default python + stdlib timeout will be used. Returns ------- - contents : obj or list - If `number` is 0, this is a individual object - the first one unpickled - from the file. Otherwise, it is a list of objects unpickled from the - file. + url : str + The loaded URL + + Raises + ------ + ValueError + If the documentation can't be found """ + import webbrowser + from zlib import decompress - if usecPickle and sys.version_info[0] < 3: # pragma: py2 - import cPickle as pickle - else: - import pickle + from astropy.utils.data import get_readable_fileobj - if isinstance(fileorname, basestring): - f = open(fileorname, 'rb') - close = True - else: - f = fileorname - close = False + if ( + not isinstance(obj, str) + and hasattr(obj, "__module__") + and hasattr(obj, "__name__") + ): + obj = obj.__module__ + "." + obj.__name__ + elif inspect.ismodule(obj): + obj = obj.__name__ - try: - if number > 0: # get that number - res = [] - for i in range(number): - res.append(pickle.load(f)) - elif number < 0: # get all objects - res = [] - eof = False - while not eof: - try: - res.append(pickle.load(f)) - except EOFError: - eof = True - else: # number==0 - res = pickle.load(f) - finally: - if close: - f.close() - - return res - - -def fnpickle(object, fileorname, usecPickle=True, protocol=None, append=False): - """Pickle an object to a specified file. + if version is None: + from astropy import version + + if version.release: + version = "v" + version.version + else: + version = "dev" + + if "://" in version: + if version.endswith("index.html"): + baseurl = version[:-10] + elif version.endswith("/"): + baseurl = version + else: + baseurl = version + "/" + elif version == "dev" or version == "latest": + baseurl = "http://devdocs.astropy.org/" + else: + baseurl = f"https://docs.astropy.org/en/{version}/" + + # Custom request headers; see + # https://github.com/astropy/astropy/issues/8990 + url = baseurl + "objects.inv" + headers = {"User-Agent": f"Astropy/{version}"} + with get_readable_fileobj( + url, encoding="binary", remote_timeout=timeout, http_headers=headers + ) as uf: + oiread = uf.read() + + # need to first read/remove the first four lines, which have info before + # the compressed section with the actual object inventory + idx = -1 + headerlines = [] + for _ in range(4): + oldidx = idx + idx = oiread.index(b"\n", oldidx + 1) + headerlines.append(oiread[(oldidx + 1) : idx].decode("utf-8")) + + # intersphinx version line, project name, and project version + ivers, proj, vers, compr = headerlines + if "The remainder of this file is compressed using zlib" not in compr: + raise ValueError( + f"The file downloaded from {baseurl}objects.inv does not seem to be" + "the usual Sphinx objects.inv format. Maybe it " + "has changed?" + ) + + compressed = oiread[(idx + 1) :] + + decompressed = decompress(compressed).decode("utf-8") + + resurl = None + + for l in decompressed.strip().splitlines(): + ls = l.split() + name = ls[0] + loc = ls[3] + if loc.endswith("$"): + loc = loc[:-1] + name + + if name == obj: + resurl = baseurl + loc + break + + if resurl is None: + raise ValueError(f"Could not find the docs for the object {obj}") + elif openinbrowser: + webbrowser.open(resurl) + + return resurl + + +# The location of the online documentation for astropy +# This location will normally point to the current released version of astropy +online_docs_root: Final = "https://docs.astropy.org/en/{}/".format( + "latest" if "dev" in __version__ else f"v{__version__}" +) + + +def online_help(query: str) -> None: + """ + Search the online Astropy documentation for the given query. + Opens the results in the default web browser. Requires an active + Internet connection. Parameters ---------- - object - The python object to pickle. - fileorname : str or `file`-like - The filename or file into which the `object` should be pickled. If a - file object, it should have been opened in binary mode. - usecPickle : bool - If True (default), the :mod:`cPickle` module is to be used in place of - :mod:`pickle` (cPickle is faster). This only applies for python 2.x. - protocol : int or None - Pickle protocol to use - see the :mod:`pickle` module for details on - these options. If None, the most recent protocol will be used. - append : bool - If True, the object is appended to the end of the file, otherwise the - file will be overwritten (if a file object is given instead of a - file name, this has no effect). - + query : str + The search query. """ + import webbrowser - if usecPickle and sys.version_info[0] < 3: # pragma: py2 - import cPickle as pickle - else: - import pickle + webbrowser.open(online_docs_root + f"search.html?{urlencode({'q': query})}") - if protocol is None: - protocol = pickle.HIGHEST_PROTOCOL - if isinstance(fileorname, basestring): - f = open(fileorname, 'ab' if append else 'wb') - close = True - else: - f = fileorname - close = False +class JsonCustomEncoder(json.JSONEncoder): + """Support for data types that JSON default encoder + does not do. - try: - pickle.dump(object, f, protocol=protocol) - finally: - if close: - f.close() + This includes: + * Numpy array or number + * Complex number + * Set + * Bytes + * astropy.UnitBase + * astropy.Quantity -def isiterable(obj): - """Returns `True` if the given object is iterable.""" - from numpy import ndarray + Examples + -------- + >>> import json + >>> import numpy as np + >>> from astropy.utils.misc import JsonCustomEncoder + >>> json.dumps(np.arange(3), cls=JsonCustomEncoder) + '[0, 1, 2]' - # Numpy arrays are in collections.Iterable no matter what, but if you - # attempt to iterate over a 0-d array, it throws a TypeError. - if isinstance(obj, ndarray) and len(obj.shape) == 0: - return False + """ - if isinstance(obj, collections.Iterable): - return True + def default(self, obj: object) -> object: + from astropy import units as u + + if isinstance(obj, u.Quantity): + return dict(value=obj.value, unit=obj.unit.to_string()) + if isinstance(obj, (np.number, np.ndarray)): + return obj.tolist() + elif isinstance(obj, complex): + return [obj.real, obj.imag] + elif isinstance(obj, set): + return list(obj) + elif isinstance(obj, bytes): # pragma: py3 + return obj.decode() + elif isinstance(obj, (u.UnitBase, u.FunctionUnitBase)): + if obj == u.dimensionless_unscaled: + obj = "dimensionless_unit" + else: + return obj.to_string() + + return json.JSONEncoder.default(self, obj) - try: - iter(obj) - return True - except TypeError: - return False +def strip_accents(s: str) -> str: + """ + Remove accents from a Unicode string. -class lazyproperty(object): + This helps with matching "ÃĨngstrÃļm" to "angstrom", for example. """ - Works similarly to property(), but computes the value only once. - - This essentially memoizes the value of the property by storing the result - of its computation in the ``__dict__`` of the object instance. This is - useful for computing the value of some property that should otherwise be - invariant. For example:: - - >>> class LazyTest(object): - ... @lazyproperty - ... def complicated_property(self): - ... print 'Computing the value for complicated_property..." - ... return 42 - ... - >>> lt = LazyTest() - >>> lt.complicated_property - Computing the value for complicated_property... - 42 - >>> lt.complicated_property - 42 - - If a setter for this property is defined, it will still be possible to - manually update the value of the property, if that capability is desired. - - Adapted from the recipe at - http://code.activestate.com/recipes/363602-lazy-property-evaluation + return "".join( + c for c in unicodedata.normalize("NFD", s) if unicodedata.category(c) != "Mn" + ) + + +def did_you_mean( + s: str, + candidates: Iterable[str], + n: int = 3, + cutoff: float = 0.8, + fix: Callable[[str], list[str]] | None = None, +) -> str: """ + When a string isn't found in a set of candidates, we can be nice + to provide a list of alternatives in the exception. This + convenience function helps to format that part of the exception. - def __init__(self, fget, fset=None, fdel=None, doc=None): - self._fget = fget - self._fset = fset - self._fdel = fdel - if doc is None: - self.__doc__ = fget.__doc__ - else: - self.__doc__ = doc - - def __get__(self, obj, owner=None): - if obj is None: - return self - key = self._fget.func_name - if key not in obj.__dict__: - val = self._fget(obj) - obj.__dict__[key] = val - return val - else: - return obj.__dict__[key] + Parameters + ---------- + s : str - def __set__(self, obj, val): - if self._fset: - self._fset(obj, val) - obj.__dict__[self._fget.func_name] = val + candidates : iterable of str + Note that str itself does not cause an error, but the output + might not be what was expected. - def __delete__(self, obj): - if self._fdel: - self._fdel(obj) - key = self._fget.func_name - if key in obj.__dict__: - del obj.__dict__[key] + n : int + The maximum number of results to include. See + `difflib.get_close_matches`. - def getter(self, fget): - return self.__ter(fget, 0) + cutoff : float + In the range [0, 1]. Possibilities that don't score at least + that similar to word are ignored. See + `difflib.get_close_matches`. - def setter(self, fset): - return self.__ter(fset, 1) + fix : callable + A callable to modify the results after matching. It should + take a single string and return a list of strings + containing the fixed matches. - def deleter(self, fdel): - return self.__ter(fdel, 2) + Returns + ------- + message : str + Returns the string "Did you mean X, Y, or Z?", or the empty + string if no alternatives were found. + """ + s_lower = strip_accents(s).lower() + + # Create a mapping from the lower case name to all capitalization + # variants of that name. + candidates_lower = defaultdict(list) + for candidate in candidates: + candidates_lower[candidate.lower()].append(candidate) + + # The heuristic here is to first try "singularizing" the word. If + # that doesn't match anything use difflib to find close matches in + # original, lower and upper case. + matches: Iterable[str] = ( + [s_lower[:-1]] + if s_lower.endswith("s") and s_lower[:-1] in candidates_lower + else difflib.get_close_matches(s_lower, candidates_lower, n=n, cutoff=cutoff) + ) + + if not matches: + return "" + matches = chain.from_iterable(candidates_lower[match] for match in matches) + if fix is not None: + matches = chain.from_iterable(fix(match) for match in matches) + *first_matches, suggestion = sorted(set(matches)) + if first_matches: + suggestion = ", ".join(first_matches) + " or " + suggestion + return f"Did you mean {suggestion}?" + + +LOCALE_LOCK: Final = threading.Lock() + + +@contextmanager +def _set_locale(name: str) -> Generator[None, None, None]: + """ + Context manager to temporarily set the locale to ``name``. + + An example is setting locale to "C" so that the C strtod() + function will use "." as the decimal point to enable consistent + numerical string parsing. + + Note that one cannot nest multiple _set_locale() context manager + statements as this causes a threading lock. - def __ter(self, f, arg): - args = [self._fget, self._fset, self._fdel, self.__doc__] - args[arg] = f - cls_ns = sys._getframe(1).f_locals - for k, v in cls_ns.iteritems(): - if v is self: - property_name = k - break + This code taken from https://stackoverflow.com/questions/18593661/how-do-i-strftime-a-date-object-in-a-different-locale. - cls_ns[property_name] = lazyproperty(*args) + Parameters + ---------- + name : str + Locale name, e.g. "C" or "fr_FR". + """ + name = str(name) - return cls_ns[property_name] + with LOCALE_LOCK: + saved = locale.setlocale(locale.LC_ALL) + if saved == name: + # Don't do anything if locale is already the requested locale + yield + else: + try: + locale.setlocale(locale.LC_ALL, name) + yield + finally: + locale.setlocale(locale.LC_ALL, saved) -# TODO: Provide a class deprecation marker as well. -def deprecated(since, message='', name='', alternative='', pending=False): +def dtype_bytes_or_chars(dtype: np.dtype) -> int | None: """ - Used to mark a function as deprecated. + Parse the number out of a dtype.str value like ' Generator[None, None, None]: + """Temporarily replace the module's get_caller_module_dict. + + This is a function inside ``ply.lex`` and ``ply.yacc`` (each has a copy) + that is used to retrieve the caller's local symbols. Here, we patch the + function to instead retrieve the grandparent's local symbols to account + for a wrapper layer. + + Additionally, a custom header is inserted into any files ``ply`` writes. + """ + original = module.get_caller_module_dict + + @functools.wraps(original) + def wrapper(levels): + # Add 2, not 1, because the wrapper itself adds another level + return original(levels + 2) + + file_exists = file.exists() or file.with_suffix(".pyc").exists() + module.get_caller_module_dict = wrapper + yield + module.get_caller_module_dict = original + if not file_exists: + file.write_text(_TAB_HEADER.format(package=package) + file.read_text()) + + +def lex(lextab: str, package: str, reflags: int = int(re.VERBOSE)) -> Lexer: + """Create a lexer from local variables. + + It automatically compiles the lexer in optimized mode, writing to + ``lextab`` in the same directory as the calling file. + + This function is thread-safe. The returned lexer is *not* thread-safe, but + if it is used exclusively with a single parser returned by :func:`yacc` + then it will be safe. + + It is only intended to work with lexers defined within the calling + function, rather than at class or module scope. + + Parameters + ---------- + lextab : str + Name for the file to write with the generated tables, if it does not + already exist (without ``.py`` suffix). + package : str + Name of a test package which should be run with pytest to regenerate + the output file. This is inserted into a comment in the generated + file. + reflags : int + Passed to ``ply.lex``. + """ + from astropy.extern.ply import lex + + caller_dir = Path(lex.get_caller_module_dict(2)["__file__"]).parent + with _LOCK, _patch_ply_module(lex, caller_dir / (lextab + ".py"), package): + return lex.lex( + optimize=True, lextab=lextab, outputdir=caller_dir, reflags=reflags + ) + + +class ThreadSafeParser: + """Wrap a parser produced by ``ply.yacc.yacc``. + + It provides a :meth:`parse` method that is thread-safe. + """ + + def __init__(self, parser: LRParser) -> None: + self.parser = parser + self._lock = threading.RLock() + + def parse(self, *args, **kwargs): + """Run the wrapped parser, with a lock to ensure serialization.""" + with self._lock: + return self.parser.parse(*args, **kwargs) + + +def yacc(tabmodule: str, package: str) -> ThreadSafeParser: + """Create a parser from local variables. + + It automatically compiles the parser in optimized mode, writing to + ``tabmodule`` in the same directory as the calling file. + + This function is thread-safe, and the returned parser is also thread-safe, + provided that it does not share a lexer with any other parser. + + It is only intended to work with parsers defined within the calling + function, rather than at class or module scope. + + Parameters + ---------- + tabmodule : str + Name for the file to write with the generated tables, if it does not + already exist (without ``.py`` suffix). + package : str + Name of a test package which should be run with pytest to regenerate + the output file. This is inserted into a comment in the generated + file. + """ + from astropy.extern.ply import yacc + + caller_dir = Path(yacc.get_caller_module_dict(2)["__file__"]).parent + with _LOCK, _patch_ply_module(yacc, caller_dir / (tabmodule + ".py"), package): + parser = yacc.yacc( + tabmodule=tabmodule, + outputdir=caller_dir, + debug=False, + optimize=True, + write_tables=True, + ) + return ThreadSafeParser(parser) diff --git a/astropy/utils/release.py b/astropy/utils/release.py deleted file mode 100644 index 2bdcdac44a96..000000000000 --- a/astropy/utils/release.py +++ /dev/null @@ -1,100 +0,0 @@ -# Licensed under a 3-clause BSD style license - see LICENSE.rst - -""" -This module contains hooks for zest.releaser for use in semi-automated releases -of Astropy. -""" - -def releaser_middle(data): - """ - releaser.middle hook to monkey-patch zest.releaser to support signed - tagging--currently this is the only way to do this. Also monkey-patches to - disable an annoyance where zest.releaser only creates .zip source - distributions. This is supposedly a workaround for a bug in Python 2.4, - but we don't care about Python 2.4. - """ - - if data['name'] != 'astropy': - return - - import os - import sys - - from zest.releaser.git import Git - from zest.releaser.release import Releaser - - # Copied verbatim from zest.releaser, but with the cmd string modified to - # use the -s option to create a signed tag and add the 'v' in front of the - # version number - def _my_create_tag(self, version): - version = 'v' + version - msg = "Tagging %s" % (version,) - cmd = 'git tag -s %s -m "%s"' % (version, msg) - if os.path.isdir('.git/svn'): - print "\nEXPERIMENTAL support for git-svn tagging!\n" - cur_branch = open('.git/HEAD').read().strip().split('/')[-1] - print "You are on branch %s." % (cur_branch,) - if cur_branch != 'master': - print "Only the master branch is supported for git-svn tagging." - print "Please tag yourself." - print "'git tag' needs to list tag named %s." % (version,) - sys.exit() - cmd = [cmd] - local_head = open('.git/refs/heads/master').read() - trunk = open('.git/refs/remotes/trunk').read() - if local_head != trunk: - print "Your local master diverges from trunk.\n" - # dcommit before local tagging - cmd.insert(0, 'git svn dcommit') - # create tag in svn - cmd.append('git svn tag -m "%s" %s' % (msg, version)) - return cmd - - # Similarly copied from zer.releaser to support use of 'v' in front - # of the version number - def _my_make_tag(self): - from zest.releaser import utils - from os import system - - if self.data['tag_already_exists']: - return - cmds = self.vcs.cmd_create_tag(self.data['version']) - if not isinstance(cmds, list): - cmds = [cmds] - if len(cmds) == 1: - print "Tag needed to proceed, you can use the following command:" - for cmd in cmds: - print cmd - if utils.ask("Run this command"): - print system(cmd) - else: - # all commands are needed in order to proceed normally - print "Please create a tag for %s yourself and rerun." % \ - (self.data['version'],) - sys.exit() - if not self.vcs.tag_exists('v' + self.data['version']): - print "\nFailed to create tag %s!" % (self.data['version'],) - sys.exit() - - # Normally all this does is to return '--formats=zip', which is currently - # hard-coded as an option to always add to the sdist command; they ought to - # make this actually optional - def _my_sdist_options(self): - return '' - - Git.cmd_create_tag = _my_create_tag - Releaser._make_tag = _my_make_tag - Releaser._sdist_options = _my_sdist_options - - -def postreleaser_before(data): - """ - postreleaser.before hook to set a different dev_version_template from the - default: By default zest.releaser uses .dev0. We want just - .dev without the mysterious 0. - """ - - if data['name'] != 'astropy': - return - - data['dev_version_template'] = '%(new_version)s.dev' diff --git a/astropy/utils/setup_package.py b/astropy/utils/setup_package.py deleted file mode 100644 index e92a972932df..000000000000 --- a/astropy/utils/setup_package.py +++ /dev/null @@ -1,11 +0,0 @@ -from distutils.core import Extension -from os.path import dirname, join, relpath - - -def get_extensions(): - ROOT = dirname(__file__) - - return [ - Extension('astropy.utils._compiler', - [relpath(join(ROOT, 'src', 'compiler.c'))]) - ] diff --git a/astropy/utils/shapes.py b/astropy/utils/shapes.py new file mode 100644 index 000000000000..85657974dbd8 --- /dev/null +++ b/astropy/utils/shapes.py @@ -0,0 +1,528 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +"""The ShapedLikeNDArray mixin class and shape-related functions.""" + +import abc +import numbers +from collections.abc import Sequence +from itertools import zip_longest +from math import prod +from types import EllipsisType +from typing import Self, TypeVar + +import numpy as np +import numpy._core as np_core +from numpy.lib.array_utils import normalize_axis_index +from numpy.typing import NDArray + +from astropy.utils.decorators import deprecated + +__all__ = [ + "IncompatibleShapeError", + "NDArrayShapeMethods", + "ShapedLikeNDArray", + "check_broadcast", + "simplify_basic_index", + "unbroadcast", +] + + +DT = TypeVar("DT", bound=np.generic) + + +class NDArrayShapeMethods: + """Mixin class to provide shape-changing methods. + + The class proper is assumed to have some underlying data, which are arrays + or array-like structures. It must define a ``shape`` property, which gives + the shape of those data, as well as an ``_apply`` method that creates a new + instance in which a `~numpy.ndarray` method has been applied to those. + + Furthermore, for consistency with `~numpy.ndarray`, it is recommended to + define a setter for the ``shape`` property, which, like the + `~numpy.ndarray.shape` property allows in-place reshaping the internal data + (and, unlike the ``reshape`` method raises an exception if this is not + possible). + + This class only provides the shape-changing methods and is meant in + particular for `~numpy.ndarray` subclasses that need to keep track of + other arrays. For other classes, `~astropy.utils.shapes.ShapedLikeNDArray` + is recommended. + + """ + + # Note to developers: if new methods are added here, be sure to check that + # they work properly with the classes that use this, such as Time and + # BaseRepresentation, i.e., look at their ``_apply`` methods and add + # relevant tests. This is particularly important for methods that imply + # copies rather than views of data (see the special-case treatment of + # 'flatten' in Time). + + def __getitem__(self, item): + return self._apply("__getitem__", item) + + def copy(self, *args, **kwargs): + """Return an instance containing copies of the internal data. + + Parameters are as for :meth:`~numpy.ndarray.copy`. + """ + return self._apply("copy", *args, **kwargs) + + def reshape(self, *args, **kwargs): + """Returns an instance containing the same data with a new shape. + + Parameters are as for :meth:`~numpy.ndarray.reshape`. Note that it is + not always possible to change the shape of an array without copying the + data (see :func:`~numpy.reshape` documentation). If you want an error + to be raise if the data is copied, you should assign the new shape to + the shape attribute (note: this may not be implemented for all classes + using ``NDArrayShapeMethods``). + """ + return self._apply("reshape", *args, **kwargs) + + def ravel(self, *args, **kwargs): + """Return an instance with the array collapsed into one dimension. + + Parameters are as for :meth:`~numpy.ndarray.ravel`. Note that it is + not always possible to unravel an array without copying the data. + If you want an error to be raise if the data is copied, you should + should assign shape ``(-1,)`` to the shape attribute. + """ + return self._apply("ravel", *args, **kwargs) + + def flatten(self, *args, **kwargs): + """Return a copy with the array collapsed into one dimension. + + Parameters are as for :meth:`~numpy.ndarray.flatten`. + """ + return self._apply("flatten", *args, **kwargs) + + def transpose(self, *args, **kwargs): + """Return an instance with the data transposed. + + Parameters are as for :meth:`~numpy.ndarray.transpose`. All internal + data are views of the data of the original. + """ + return self._apply("transpose", *args, **kwargs) + + @property + def T(self) -> Self: + """Return an instance with the data transposed. + + Parameters are as for :attr:`~numpy.ndarray.T`. All internal + data are views of the data of the original. + """ + if self.ndim < 2: + return self + else: + return self.transpose() + + def swapaxes(self, *args, **kwargs): + """Return an instance with the given axes interchanged. + + Parameters are as for :meth:`~numpy.ndarray.swapaxes`: + ``axis1, axis2``. All internal data are views of the data of the + original. + """ + return self._apply("swapaxes", *args, **kwargs) + + def diagonal(self, *args, **kwargs): + """Return an instance with the specified diagonals. + + Parameters are as for :meth:`~numpy.ndarray.diagonal`. All internal + data are views of the data of the original. + """ + return self._apply("diagonal", *args, **kwargs) + + def squeeze(self, *args, **kwargs): + """Return an instance with single-dimensional shape entries removed. + + Parameters are as for :meth:`~numpy.ndarray.squeeze`. All internal + data are views of the data of the original. + """ + return self._apply("squeeze", *args, **kwargs) + + def take(self, indices, axis=None, out=None, mode="raise"): + """Return a new instance formed from the elements at the given indices. + + Parameters are as for :meth:`~numpy.ndarray.take`, except that, + obviously, no output array can be given. + """ + if out is not None: + raise NotImplementedError("cannot pass 'out' argument to 'take.") + + return self._apply("take", indices, axis=axis, mode=mode) + + +def _combine_helper(func, arrays, axis, out, dtype): + """Get normalized axis and create empty output instance if needed.""" + # Get the final shape by applying the function on arrays of bool with the + # same shapes as the input instance. This avoids having to write tests + # that the shapes match, etc. + empties = [np.empty(shape=np.shape(array), dtype=bool) for array in arrays] + shape = func(empties, axis=axis).shape + # Normalize the axis to [0, shape> for use in the implementations. + axis = normalize_axis_index(axis, len(shape)) + # If needed, use the first instance as base to create a correctly shaped + # output array in which to store the result. + if out is None: + out = arrays[0]._apply(np.empty_like, shape=shape) + return axis, out + + +def concatenate(arrays, axis=0, out=None, dtype=None, casting="same_kind"): + axis, out = _combine_helper(np.concatenate, arrays, axis, out, dtype) + + offset = 0 + for array in arrays: + n_el = array.shape[axis] + out[(slice(None),) * axis + (slice(offset, offset + n_el),)] = array + offset += n_el + + return out + + +def stack(arrays, axis=0, out=None, *, dtype=None, casting="same_kind"): + axis, out = _combine_helper(np.stack, arrays, axis, out, dtype) + for i, array in enumerate(arrays): + out[(slice(None),) * axis + (i,)] = array + + return out + + +class ShapedLikeNDArray(NDArrayShapeMethods, metaclass=abc.ABCMeta): + """Mixin class to provide shape-changing methods. + + The class proper is assumed to have some underlying data, which are arrays + or array-like structures. It must define a ``shape`` property, which gives + the shape of those data, as well as an ``_apply`` method that creates a new + instance in which a `~numpy.ndarray` method has been applied to those. + + Furthermore, for consistency with `~numpy.ndarray`, it is recommended to + define a setter for the ``shape`` property, which, like the + `~numpy.ndarray.shape` property allows in-place reshaping the internal data + (and, unlike the ``reshape`` method raises an exception if this is not + possible). + + This class also defines default implementations for ``ndim`` and ``size`` + properties, calculating those from the ``shape``. These can be overridden + by subclasses if there are faster ways to obtain those numbers. + + """ + + # Note to developers: if new methods are added here, be sure to check that + # they work properly with the classes that use this, such as Time and + # BaseRepresentation, i.e., look at their ``_apply`` methods and add + # relevant tests. This is particularly important for methods that imply + # copies rather than views of data (see the special-case treatment of + # 'flatten' in Time). + + @property + @abc.abstractmethod + def shape(self) -> tuple[int, ...]: + """The shape of the underlying data.""" + + @abc.abstractmethod + def _apply(method, *args, **kwargs): + """Create a new instance, with ``method`` applied to underlying data. + + The method is any of the shape-changing methods for `~numpy.ndarray` + (``reshape``, ``swapaxes``, etc.), as well as those picking particular + elements (``__getitem__``, ``take``, etc.). It will be applied to the + underlying arrays (e.g., ``jd1`` and ``jd2`` in `~astropy.time.Time`), + with the results used to create a new instance. + + Parameters + ---------- + method : str + Method to be applied to the instance's internal data arrays. + args : tuple + Any positional arguments for ``method``. + kwargs : dict + Any keyword arguments for ``method``. + + """ + + @property + def ndim(self) -> int: + """The number of dimensions of the instance and underlying arrays.""" + return len(self.shape) + + @property + def size(self) -> int: + """The size of the object, as calculated from its shape.""" + return prod(self.shape) + + @property + def isscalar(self) -> bool: + return self.shape == () + + def __len__(self) -> int: + if self.isscalar: + raise TypeError(f"Scalar {self.__class__.__name__!r} object has no len()") + return self.shape[0] + + def __bool__(self) -> bool: + """Any instance should evaluate to True, except when it is empty.""" + return self.size > 0 + + def __getitem__(self, item): + try: + return self._apply("__getitem__", item) + except IndexError: + if self.isscalar: + raise TypeError( + f"scalar {self.__class.__name__!r} object is not subscriptable." + ) + else: + raise + + def __iter__(self): + if self.isscalar: + raise TypeError( + f"scalar {self.__class__.__name__!r} object is not iterable." + ) + + # We cannot just write a generator here, since then the above error + # would only be raised once we try to use the iterator, rather than + # upon its definition using iter(self). + def self_iter(): + for idx in range(len(self)): + yield self[idx] + + return self_iter() + + # Functions that change shape or essentially do indexing. + _APPLICABLE_FUNCTIONS = { + np.moveaxis, + np.rollaxis, + np.atleast_1d, + np.atleast_2d, + np.atleast_3d, + np.expand_dims, + np.broadcast_to, + np.flip, + np.fliplr, + np.flipud, + np.rot90, + np.roll, + np.delete, + } + # TODO: use astropy.units.quantity_helpers.function_helpers.FunctionAssigner? + # Maybe better after moving that to astropy.utils, since Masked uses it too. + _CUSTOM_FUNCTIONS = { + np.concatenate: concatenate, + np.stack: stack, + } + # Functions that themselves defer to a method. Those are all + # defined in np._core.fromnumeric, but exclude alen as well as + # sort and partition, which make copies before calling the method. + _METHOD_FUNCTIONS = { + getattr(np, name): { + "amax": "max", + "amin": "min", + "around": "round", + "round_": "round", + "alltrue": "all", + "sometrue": "any", + }.get(name, name) + for name in np_core.fromnumeric.__all__ + if name not in ["alen", "sort", "partition"] + } + # Add np.copy, which we may as well let defer to our method. + _METHOD_FUNCTIONS[np.copy] = "copy" + + # Could be made to work with a bit of effort: + # np.where, np.compress, np.extract, + # np.diag_indices_from, np.triu_indices_from, np.tril_indices_from + # np.tile, np.repeat (need .repeat method) + # TODO: create a proper implementation. + # Furthermore, some arithmetic functions such as np.mean, np.median, + # could work for Time, and many more for TimeDelta, so those should + # override __array_function__. + def __array_function__(self, function, types, args, kwargs): + """Wrap numpy functions that make sense.""" + if function in self._APPLICABLE_FUNCTIONS: + if function is np.broadcast_to: + # Ensure that any ndarray subclasses used are + # properly propagated. + kwargs.setdefault("subok", True) + elif ( + function in {np.atleast_1d, np.atleast_2d, np.atleast_3d} + and len(args) > 1 + ): + return tuple(function(arg, **kwargs) for arg in args) + + if self is not args[0]: + return NotImplemented + + return self._apply(function, *args[1:], **kwargs) + + elif function in self._CUSTOM_FUNCTIONS: + return self._CUSTOM_FUNCTIONS[function](*args, **kwargs) + + # For functions that defer to methods, use the corresponding + # method/attribute if we have it. Otherwise, fall through. + if self is args[0] and function in self._METHOD_FUNCTIONS: + method = getattr(self, self._METHOD_FUNCTIONS[function], None) + if method is not None: + if callable(method): + return method(*args[1:], **kwargs) + else: + # For np.shape, etc., just return the attribute. + return method + + # Fall-back, just pass the arguments on since perhaps the function + # works already (see above). + return function.__wrapped__(*args, **kwargs) + + +class IncompatibleShapeError(ValueError): + def __init__( + self, + shape_a: tuple[int, ...], + shape_a_idx: int, + shape_b: tuple[int, ...], + shape_b_idx: int, + ) -> None: + super().__init__(shape_a, shape_a_idx, shape_b, shape_b_idx) + + +@deprecated("7.0", alternative="np.broadcast_shapes") +def check_broadcast(*shapes: tuple[int, ...]) -> tuple[int, ...]: + """ + Determines whether two or more Numpy arrays can be broadcast with each + other based on their shape tuple alone. + + Parameters + ---------- + *shapes : tuple + All shapes to include in the comparison. If only one shape is given it + is passed through unmodified. If no shapes are given returns an empty + `tuple`. + + Returns + ------- + broadcast : `tuple` + If all shapes are mutually broadcastable, returns a tuple of the full + broadcast shape. + """ + if len(shapes) == 0: + return () + elif len(shapes) == 1: + return shapes[0] + + reversed_shapes = (reversed(shape) for shape in shapes) + + full_shape = [] + + for dims in zip_longest(*reversed_shapes, fillvalue=1): + max_dim = 1 + max_dim_idx = None + for idx, dim in enumerate(dims): + if dim == 1: + continue + + if max_dim == 1: + # The first dimension of size greater than 1 + max_dim = dim + max_dim_idx = idx + elif dim != max_dim: + raise IncompatibleShapeError( + shapes[max_dim_idx], max_dim_idx, shapes[idx], idx + ) + + full_shape.append(max_dim) + + return tuple(full_shape[::-1]) + + +def unbroadcast(array: NDArray[DT]) -> NDArray[DT]: + """ + Given an array, return a new array that is the smallest subset of the + original array that can be re-broadcasted back to the original array. + + See https://stackoverflow.com/questions/40845769/un-broadcasting-numpy-arrays + for more details. + """ + if array.ndim == 0: + return array + + array = array[ + tuple((slice(0, 1) if stride == 0 else slice(None)) for stride in array.strides) + ] + + # Remove leading ones, which are not needed in numpy broadcasting. + first_not_unity = next( + (i for (i, s) in enumerate(array.shape) if s > 1), array.ndim + ) + + return array.reshape(array.shape[first_not_unity:]) + + +def simplify_basic_index( + basic_index: int | slice | Sequence[int | slice | EllipsisType | None], + *, + shape: Sequence[int], +) -> tuple[int | slice, ...]: + """ + Given a Numpy basic index, return a tuple of integers and slice objects + with no default values (`None`) if possible. + + If one of the dimensions has a slice and the step is negative and the stop + value of the slice was originally `None`, the new stop value of the slice + may still be set to `None`. + + For more information on valid basic indices, see + https://numpy.org/doc/stable/user/basics.indexing.html#basic-indexing + + Parameters + ---------- + basic_index + A valid Numpy basic index + shape + The shape of the array being indexed + """ + ndim = len(shape) + + if not isinstance(basic_index, (tuple, list)): # We just have a single int + basic_index = (basic_index,) + + new_index = list(basic_index) + + if Ellipsis in new_index: + if new_index.count(Ellipsis) > 1: + raise IndexError("an index can only have a single ellipsis ('...')") + + # Replace the Ellipsis with the correct number of slice(None)s + e_ind = new_index.index(Ellipsis) + new_index.remove(Ellipsis) + n_e = ndim - len(new_index) + for i in range(n_e): + ind = e_ind + i + new_index.insert(ind, slice(0, shape[ind], 1)) + + if len(new_index) > ndim: + raise ValueError( + f"The dimensionality of the basic index {basic_index} can not be greater " + f"than the dimensionality ({ndim}) of the data." + ) + + for i in range(ndim): + if i < len(new_index): + slc = new_index[i] + if isinstance(slc, slice): + indices = list(slc.indices(shape[i])) + # The following case is the only one where slice(*indices) does + # not give the 'correct' answer because it will set stop to -1 + # which means the last element in the array. + if indices[1] == -1: + indices[1] = None + new_index[i] = slice(*indices) + elif isinstance(slc, numbers.Integral): + new_index[i] = normalize_axis_index(int(slc), shape[i]) + else: + raise ValueError(f"Unexpected index element in basic index: {slc}") + else: + new_index.append(slice(0, shape[i], 1)) + + return tuple(new_index) diff --git a/astropy/utils/src/.gitignore b/astropy/utils/src/.gitignore deleted file mode 100644 index 970d78fd3f93..000000000000 --- a/astropy/utils/src/.gitignore +++ /dev/null @@ -1 +0,0 @@ -!*.c diff --git a/astropy/utils/src/compiler.c b/astropy/utils/src/compiler.c deleted file mode 100644 index 75500caf58a7..000000000000 --- a/astropy/utils/src/compiler.c +++ /dev/null @@ -1,129 +0,0 @@ -#include - -/*************************************************************************** - * Macros for determining the compiler version. - * - * These are borrowed from boost, and majorly abridged to include only - * the compilers we care about. - ***************************************************************************/ - -#ifndef PY3K -#if PY_MAJOR_VERSION >= 3 -#define PY3K 1 -#else -#define PY3K 0 -#endif -#endif - - -#define STRINGIZE(X) DO_STRINGIZE(X) -#define DO_STRINGIZE(X) #X - -#if defined __clang__ -/* Clang C++ emulates GCC, so it has to appear early. */ -# define COMPILER "Clang version " __clang_version__ - -#elif defined(__INTEL_COMPILER) || defined(__ICL) || defined(__ICC) || defined(__ECC) -/* Intel */ -# if defined(__INTEL_COMPILER) -# define INTEL_VERSION __INTEL_COMPILER -# elif defined(__ICL) -# define INTEL_VERSION __ICL -# elif defined(__ICC) -# define INTEL_VERSION __ICC -# elif defined(__ECC) -# define INTEL_VERSION __ECC -# endif -# define COMPILER "Intel C compiler version " STRINGIZE(INTEL_VERSION) - -#elif defined(__GNUC__) -/* gcc */ -# define COMPILER "GCC version " __VERSION__ - -#elif defined(__SUNPRO_CC) -/* Sun Workshop Compiler */ -# define COMPILER "Sun compiler version " STRINGIZE(__SUNPRO_CC) - -#elif defined(_MSC_VER) -/* Microsoft Visual C/C++ - Must be last since other compilers define _MSC_VER for compatibility as well */ -# if _MSC_VER < 1200 -# define COMPILER_VERSION 5.0 -# elif _MSC_VER < 1300 -# define COMPILER_VERSION 6.0 -# elif _MSC_VER == 1300 -# define COMPILER_VERSION 7.0 -# elif _MSC_VER == 1310 -# define COMPILER_VERSION 7.1 -# elif _MSC_VER == 1400 -# define COMPILER_VERSION 8.0 -# elif _MSC_VER == 1500 -# define COMPILER_VERSION 9.0 -# elif _MSC_VER == 1600 -# define COMPILER_VERSION 10.0 -# else -# define COMPILER_VERSION _MSC_VER -# endif -# define COMPILER "Microsoft Visual C++ version " STRINGIZE(COMPILER_VERSION) - -#else -/* Fallback */ -# define COMPILER "Unknown compiler" - -#endif - - -/*************************************************************************** - * Module-level - ***************************************************************************/ - -struct module_state { -/* The Sun compiler can't handle empty structs */ -#if defined(__SUNPRO_C) || defined(_MSC_VER) - int _dummy; -#endif -}; - -#if PY3K - static struct PyModuleDef moduledef = { - PyModuleDef_HEAD_INIT, - "_compiler", - NULL, - sizeof(struct module_state), - NULL, - NULL, - NULL, - NULL, - NULL - }; - - #define INITERROR return NULL - - PyMODINIT_FUNC - PyInit__compiler(void) - -#else - #define INITERROR return - - PyMODINIT_FUNC - init_compiler(void) -#endif - -{ - PyObject* m; - -#if PY3K - m = PyModule_Create(&moduledef); -#else - m = Py_InitModule3("_compiler", NULL, NULL); -#endif - - if (m == NULL) - INITERROR; - - PyModule_AddStringConstant(m, "compiler", COMPILER); - -#if PY3K - return m; -#endif -} diff --git a/astropy/utils/state.py b/astropy/utils/state.py new file mode 100644 index 000000000000..4f74d263d945 --- /dev/null +++ b/astropy/utils/state.py @@ -0,0 +1,78 @@ +""" +A simple class to manage a piece of global science state. See +:ref:`astropy:config-developer` for more details. +""" + +__all__ = ["ScienceState"] + + +class _ScienceStateContext: + def __init__(self, parent, value): + self._value = value + self._parent = parent + + def __enter__(self): + pass + + def __exit__(self, type, value, tb): + self._parent._value = self._value + + def __repr__(self): + # Ensure we have a single-line repr, just in case our + # value is not something simple like a string. + value_repr, lb, _ = repr(self._parent._value).partition("\n") + if lb: + value_repr += "..." + return f"" + + +class ScienceState: + """ + Science state subclasses are used to manage global items that can + affect science results. Subclasses will generally override + `validate` to convert from any of the acceptable inputs (such as + strings) to the appropriate internal objects, and set an initial + value to the ``_value`` member so it has a default. + + Examples + -------- + :: + + class MyState(ScienceState): + @classmethod + def validate(cls, value): + if value not in ('A', 'B', 'C'): + raise ValueError("Must be one of A, B, C") + return value + """ + + def __init__(self): + raise RuntimeError("This class is a singleton. Do not instantiate.") + + @classmethod + def get(cls): + """ + Get the current science state value. + """ + return cls.validate(cls._value) + + @classmethod + def set(cls, value): + """Set the current science state value.""" + # Create context with current value + ctx = _ScienceStateContext(cls, cls._value) + + # Set new value + value = cls.validate(value) + cls._value = value + + # Return context manager + return ctx + + @classmethod + def validate(cls, value): + """ + Validate the value and convert it to its native type, if + necessary. + """ + return value diff --git a/astropy/utils/system_info.py b/astropy/utils/system_info.py new file mode 100644 index 000000000000..7ebad83b8e3a --- /dev/null +++ b/astropy/utils/system_info.py @@ -0,0 +1,86 @@ +r""" +A helper script to automate system info in bug reports. + +This can be run as:: + + python -c "import astropy; astropy.system_info()" + +(note that the interpreter might be called python3 instead of python, depending +on your system's details) + +This script should not require anything else than the standard library and is +subject to change without notice. +""" + +__all__ = ["system_info"] + +import platform +from importlib.metadata import version +from importlib.util import find_spec + + +def _header(name: str) -> list[str]: + return [name, "-" * len(name)] + + +def _report_platform() -> list[str]: + return [ + f"{platform.platform() = }", + f"{platform.version() = }", + f"{platform.python_version() = }", + ] + + +def _report_packages() -> list[str]: + pkgs = [ + "astropy", + "numpy", + "scipy", + "matplotlib", + "pandas", + ] + lines = [f"{p:<20} {'--' if find_spec(p) is None else version(p)}" for p in pkgs] + + # pyerfa may not export its package metadata (as of 2.0.1.4) + try: + import erfa + except ImportError: + v = "--" + else: + v = erfa.__version__ + p = "pyerfa" + lines.append(f"{p:<20} {v}") + return lines + + +def system_info() -> None: + """Print relevant system information for astropy bug reports. + + Examples + -------- + >>> import astropy + >>> astropy.system_info() # doctest: +ELLIPSIS + platform + -------- + platform.platform() = ... + platform.version() = ... + platform.python_version() = ... + + packages + -------- + astropy ... + numpy ... + scipy ... + matplotlib ... + pandas ... + pyerfa ... + + """ + report_lines = ( + *_header("platform"), + *_report_platform(), + "", + *_header("packages"), + *_report_packages(), + ) + print("\n".join(report_lines)) diff --git a/astropy/utils/tests/data/alias.cfg b/astropy/utils/tests/data/alias.cfg new file mode 100644 index 000000000000..291b7e728db1 --- /dev/null +++ b/astropy/utils/tests/data/alias.cfg @@ -0,0 +1,2 @@ +[cosmology.core] +default_cosmology = WMAP7 \ No newline at end of file diff --git a/astropy/utils/tests/data/dataurl/index.html b/astropy/utils/tests/data/dataurl/index.html new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/astropy/utils/tests/data/dataurl_mirror/index.html b/astropy/utils/tests/data/dataurl_mirror/index.html new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/astropy/config/tests/data/local.dat b/astropy/utils/tests/data/local.dat similarity index 100% rename from astropy/config/tests/data/local.dat rename to astropy/utils/tests/data/local.dat diff --git a/astropy/utils/tests/data/local.dat.Z b/astropy/utils/tests/data/local.dat.Z new file mode 100644 index 000000000000..b7b3fe5b21b7 --- /dev/null +++ b/astropy/utils/tests/data/local.dat.Z @@ -0,0 +1,2 @@ +TФ™ÂL6e@ QgN2 +Ũ€ ƒ&!2sč|aķfL6_ȄĄ拊‰é¤qsĻ`7cTžq3§‚!OœP)ĸĶ& \ No newline at end of file diff --git a/astropy/utils/tests/data/local.dat.bz2 b/astropy/utils/tests/data/local.dat.bz2 new file mode 100644 index 000000000000..7518fbafcbc5 Binary files /dev/null and b/astropy/utils/tests/data/local.dat.bz2 differ diff --git a/astropy/utils/tests/data/local.dat.gz b/astropy/utils/tests/data/local.dat.gz new file mode 100644 index 000000000000..8ac4dd26ac57 Binary files /dev/null and b/astropy/utils/tests/data/local.dat.gz differ diff --git a/astropy/utils/tests/data/local.dat.xz b/astropy/utils/tests/data/local.dat.xz new file mode 100644 index 000000000000..481dbd2cfdbd Binary files /dev/null and b/astropy/utils/tests/data/local.dat.xz differ diff --git a/astropy/utils/tests/data/test_package/__init__.py b/astropy/utils/tests/data/test_package/__init__.py new file mode 100644 index 000000000000..f3a0fc80d567 --- /dev/null +++ b/astropy/utils/tests/data/test_package/__init__.py @@ -0,0 +1,5 @@ +from astropy.utils.data import get_pkg_data_filename + + +def get_data_filename(): + return get_pkg_data_filename("data/foo.txt") diff --git a/astropy/utils/tests/data/test_package/data/foo.txt b/astropy/utils/tests/data/test_package/data/foo.txt new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/astropy/utils/tests/data/unicode.txt b/astropy/utils/tests/data/unicode.txt new file mode 100644 index 000000000000..9d8f18659e12 --- /dev/null +++ b/astropy/utils/tests/data/unicode.txt @@ -0,0 +1 @@ +האסטרונומי פיי×Ēון diff --git a/astropy/utils/tests/data/unicode.txt.Z b/astropy/utils/tests/data/unicode.txt.Z new file mode 100644 index 000000000000..0f888f19bec3 Binary files /dev/null and b/astropy/utils/tests/data/unicode.txt.Z differ diff --git a/astropy/utils/tests/data/unicode.txt.bz2 b/astropy/utils/tests/data/unicode.txt.bz2 new file mode 100644 index 000000000000..8444a5d1470d Binary files /dev/null and b/astropy/utils/tests/data/unicode.txt.bz2 differ diff --git a/astropy/utils/tests/data/unicode.txt.gz b/astropy/utils/tests/data/unicode.txt.gz new file mode 100644 index 000000000000..b74a93aaee03 Binary files /dev/null and b/astropy/utils/tests/data/unicode.txt.gz differ diff --git a/astropy/utils/tests/data/unicode.txt.xz b/astropy/utils/tests/data/unicode.txt.xz new file mode 100644 index 000000000000..1d0515b06c07 Binary files /dev/null and b/astropy/utils/tests/data/unicode.txt.xz differ diff --git a/astropy/utils/tests/odict_mapping.py b/astropy/utils/tests/odict_mapping.py deleted file mode 100644 index 8e105e71ade6..000000000000 --- a/astropy/utils/tests/odict_mapping.py +++ /dev/null @@ -1,315 +0,0 @@ -# Licensed under a 3-clause BSD style license - see LICENSE.rst -import unittest - -try: - import UserDict -except ImportError: - from collections import UserDict - -from . import odict_support as test_support - - -class BasicTestMappingProtocol(unittest.TestCase): - # This base class can be used to check that an object conforms to the - # mapping protocol - - # Functions that can be useful to override to adapt to dictionary - # semantics - type2test = None # which class is being tested (overwrite in subclasses) - - def _reference(self): - """Return a dictionary of values which are invariant by storage - in the object under test.""" - return {1:2, "key1":"value1", "key2":(1,2,3)} - def _empty_mapping(self): - """Return an empty mapping object""" - return self.type2test() - def _full_mapping(self, data): - """Return a mapping object with the value contained in data - dictionary""" - x = self._empty_mapping() - for key, value in data.items(): - x[key] = value - return x - - def __init__(self, *args, **kw): - unittest.TestCase.__init__(self, *args, **kw) - self.reference = self._reference().copy() - - # A (key, value) pair not in the mapping - key, value = self.reference.popitem() - self.other = {key:value} - - # A (key, value) pair in the mapping - key, value = self.reference.popitem() - self.inmapping = {key:value} - self.reference[key] = value - - def test_read(self): - # Test for read only operations on mapping - p = self._empty_mapping() - p1 = dict(p) #workaround for singleton objects - d = self._full_mapping(self.reference) - if d is p: - p = p1 - #Indexing - for key, value in self.reference.items(): - self.assertEqual(d[key], value) - knownkey = self.other.keys()[0] - self.assertRaises(KeyError, lambda:d[knownkey]) - #len - self.assertEqual(len(p), 0) - self.assertEqual(len(d), len(self.reference)) - #in - for k in self.reference: - self.assertIn(k, d) - for k in self.other: - self.assertNotIn(k, d) - #has_key - with test_support.check_py3k_warnings(quiet=True): - for k in self.reference: - self.assertTrue(d.has_key(k)) - for k in self.other: - self.assertFalse(d.has_key(k)) - #cmp - self.assertEqual(cmp(p,p), 0) - self.assertEqual(cmp(d,d), 0) - self.assertEqual(cmp(p,d), -1) - self.assertEqual(cmp(d,p), 1) - #__non__zero__ - if p: self.fail("Empty mapping must compare to False") - if not d: self.fail("Full mapping must compare to True") - # keys(), items(), iterkeys() ... - def check_iterandlist(iter, lst, ref): - self.assertTrue(hasattr(iter, 'next')) - self.assertTrue(hasattr(iter, '__iter__')) - x = list(iter) - self.assertTrue(set(x)==set(lst)==set(ref)) - check_iterandlist(d.iterkeys(), d.keys(), self.reference.keys()) - check_iterandlist(iter(d), d.keys(), self.reference.keys()) - check_iterandlist(d.itervalues(), d.values(), self.reference.values()) - check_iterandlist(d.iteritems(), d.items(), self.reference.items()) - #get - key, value = d.iteritems().next() - knownkey, knownvalue = self.other.iteritems().next() - self.assertEqual(d.get(key, knownvalue), value) - self.assertEqual(d.get(knownkey, knownvalue), knownvalue) - self.assertNotIn(knownkey, d) - - def test_write(self): - # Test for write operations on mapping - p = self._empty_mapping() - #Indexing - for key, value in self.reference.items(): - p[key] = value - self.assertEqual(p[key], value) - for key in self.reference.keys(): - del p[key] - self.assertRaises(KeyError, lambda:p[key]) - p = self._empty_mapping() - #update - p.update(self.reference) - self.assertEqual(dict(p), self.reference) - items = p.items() - p = self._empty_mapping() - p.update(items) - self.assertEqual(dict(p), self.reference) - d = self._full_mapping(self.reference) - #setdefault - key, value = d.iteritems().next() - knownkey, knownvalue = self.other.iteritems().next() - self.assertEqual(d.setdefault(key, knownvalue), value) - self.assertEqual(d[key], value) - self.assertEqual(d.setdefault(knownkey, knownvalue), knownvalue) - self.assertEqual(d[knownkey], knownvalue) - #pop - self.assertEqual(d.pop(knownkey), knownvalue) - self.assertNotIn(knownkey, d) - self.assertRaises(KeyError, d.pop, knownkey) - default = 909 - d[knownkey] = knownvalue - self.assertEqual(d.pop(knownkey, default), knownvalue) - self.assertNotIn(knownkey, d) - self.assertEqual(d.pop(knownkey, default), default) - #popitem - key, value = d.popitem() - self.assertNotIn(key, d) - self.assertEqual(value, self.reference[key]) - p=self._empty_mapping() - self.assertRaises(KeyError, p.popitem) - - def test_constructor(self): - self.assertEqual(self._empty_mapping(), self._empty_mapping()) - - def test_bool(self): - self.assertTrue(not self._empty_mapping()) - self.assertTrue(self.reference) - self.assertTrue(bool(self._empty_mapping()) is False) - self.assertTrue(bool(self.reference) is True) - - def test_keys(self): - d = self._empty_mapping() - self.assertEqual(d.keys(), []) - d = self.reference - self.assertIn(self.inmapping.keys()[0], d.keys()) - self.assertNotIn(self.other.keys()[0], d.keys()) - self.assertRaises(TypeError, d.keys, None) - - def test_values(self): - d = self._empty_mapping() - self.assertEqual(d.values(), []) - - self.assertRaises(TypeError, d.values, None) - - def test_items(self): - d = self._empty_mapping() - self.assertEqual(d.items(), []) - - self.assertRaises(TypeError, d.items, None) - - def test_len(self): - d = self._empty_mapping() - self.assertEqual(len(d), 0) - - def test_getitem(self): - d = self.reference - self.assertEqual(d[self.inmapping.keys()[0]], self.inmapping.values()[0]) - - self.assertRaises(TypeError, d.__getitem__) - - def test_update(self): - # mapping argument - d = self._empty_mapping() - d.update(self.other) - self.assertEqual(d.items(), self.other.items()) - - # No argument - d = self._empty_mapping() - d.update() - self.assertEqual(d, self._empty_mapping()) - - # item sequence - d = self._empty_mapping() - d.update(self.other.items()) - self.assertEqual(d.items(), self.other.items()) - - # Iterator - d = self._empty_mapping() - d.update(self.other.iteritems()) - self.assertEqual(d.items(), self.other.items()) - - # FIXME: Doesn't work with UserDict - # self.assertRaises((TypeError, AttributeError), d.update, None) - self.assertRaises((TypeError, AttributeError), d.update, 42) - - outerself = self - class SimpleUserDict: - def __init__(self): - self.d = outerself.reference - def keys(self): - return self.d.keys() - def __getitem__(self, i): - return self.d[i] - d.clear() - d.update(SimpleUserDict()) - i1 = d.items() - i2 = self.reference.items() - i1.sort() - i2.sort() - self.assertEqual(i1, i2) - - class Exc(Exception): pass - - d = self._empty_mapping() - class FailingUserDict: - def keys(self): - raise Exc - self.assertRaises(Exc, d.update, FailingUserDict()) - - d.clear() - - class FailingUserDict: - def keys(self): - class BogonIter: - def __init__(self): - self.i = 1 - def __iter__(self): - return self - def next(self): - if self.i: - self.i = 0 - return 'a' - raise Exc - return BogonIter() - def __getitem__(self, key): - return key - self.assertRaises(Exc, d.update, FailingUserDict()) - - class FailingUserDict: - def keys(self): - class BogonIter: - def __init__(self): - self.i = ord('a') - def __iter__(self): - return self - def next(self): - if self.i <= ord('z'): - rtn = chr(self.i) - self.i += 1 - return rtn - raise StopIteration - return BogonIter() - def __getitem__(self, key): - raise Exc - self.assertRaises(Exc, d.update, FailingUserDict()) - - d = self._empty_mapping() - class badseq(object): - def __iter__(self): - return self - def next(self): - raise Exc() - - self.assertRaises(Exc, d.update, badseq()) - - self.assertRaises(ValueError, d.update, [(1, 2, 3)]) - - # no test_fromkeys or test_copy as both os.environ and selves don't support it - - def test_get(self): - d = self._empty_mapping() - self.assertTrue(d.get(self.other.keys()[0]) is None) - self.assertEqual(d.get(self.other.keys()[0], 3), 3) - d = self.reference - self.assertTrue(d.get(self.other.keys()[0]) is None) - self.assertEqual(d.get(self.other.keys()[0], 3), 3) - self.assertEqual(d.get(self.inmapping.keys()[0]), self.inmapping.values()[0]) - self.assertEqual(d.get(self.inmapping.keys()[0], 3), self.inmapping.values()[0]) - self.assertRaises(TypeError, d.get) - self.assertRaises(TypeError, d.get, None, None, None) - - def test_setdefault(self): - d = self._empty_mapping() - self.assertRaises(TypeError, d.setdefault) - - def test_popitem(self): - d = self._empty_mapping() - self.assertRaises(KeyError, d.popitem) - self.assertRaises(TypeError, d.popitem, 42) - - def test_pop(self): - d = self._empty_mapping() - k, v = self.inmapping.items()[0] - d[k] = v - self.assertRaises(KeyError, d.pop, self.other.keys()[0]) - - self.assertEqual(d.pop(k), v) - self.assertEqual(len(d), 0) - - self.assertRaises(KeyError, d.pop, k) - - def assertIn(self, key, d): - self.assertTrue(key in d) - - def assertNotIn(self, key, d): - self.assertFalse(key in d) diff --git a/astropy/utils/tests/odict_support.py b/astropy/utils/tests/odict_support.py deleted file mode 100644 index 90418a3ad0a9..000000000000 --- a/astropy/utils/tests/odict_support.py +++ /dev/null @@ -1,98 +0,0 @@ -# Licensed under a 3-clause BSD style license - see LICENSE.rst -"""Supporting definitions for the Python regression tests.""" - -import contextlib -import sys -import warnings -import re -try: - import thread -except ImportError: - thread = None - -__all__ = ["check_py3k_warnings"] - - -class WarningsRecorder(object): - """Convenience wrapper for the warnings list returned on - entry to the warnings.catch_warnings() context manager. - """ - def __init__(self, warnings_list): - self._warnings = warnings_list - self._last = 0 - - def __getattr__(self, attr): - if len(self._warnings) > self._last: - return getattr(self._warnings[-1], attr) - elif attr in warnings.WarningMessage._WARNING_DETAILS: - return None - raise AttributeError("%r has no attribute %r" % (self, attr)) - - @property - def warnings(self): - return self._warnings[self._last:] - - def reset(self): - self._last = len(self._warnings) - - -def _filterwarnings(filters, quiet=False): - """Catch the warnings, then check if all the expected - warnings have been raised and re-raise unexpected warnings. - If 'quiet' is True, only re-raise the unexpected warnings. - """ - # Clear the warning registry of the calling module - # in order to re-raise the warnings. - frame = sys._getframe(2) - registry = frame.f_globals.get('__warningregistry__') - if registry: - registry.clear() - with warnings.catch_warnings(record=True) as w: - # Set filter "always" to record all warnings. Because - # test_warnings swap the module, we need to look up in - # the sys.modules dictionary. - sys.modules['warnings'].simplefilter("always") - yield WarningsRecorder(w) - # Filter the recorded warnings - reraise = [warning.message for warning in w] - missing = [] - for msg, cat in filters: - seen = False - for exc in reraise[:]: - message = str(exc) - # Filter out the matching messages - if (re.match(msg, message, re.I) and - issubclass(exc.__class__, cat)): - seen = True - reraise.remove(exc) - if not seen and not quiet: - # This filter caught nothing - missing.append((msg, cat.__name__)) - if reraise: - raise AssertionError("unhandled warning %r" % reraise[0]) - if missing: - raise AssertionError("filter (%r, %s) did not catch any warning" % - missing[0]) - - -@contextlib.contextmanager -def check_py3k_warnings(*filters, **kwargs): - """Context manager to silence py3k warnings. - - Accept 2-tuples as positional arguments: - ("message regexp", WarningCategory) - - Optional argument: - - if 'quiet' is True, it does not fail if a filter catches nothing - (default False) - - Without argument, it defaults to: - check_py3k_warnings(("", DeprecationWarning), quiet=False) - """ - if sys.py3kwarning: - if not filters: - filters = (("", DeprecationWarning),) - else: - # It should not raise any py3k warning - filters = () - return _filterwarnings(filters, kwargs.get('quiet')) diff --git a/astropy/utils/tests/test_codegen.py b/astropy/utils/tests/test_codegen.py new file mode 100644 index 000000000000..79e055159d19 --- /dev/null +++ b/astropy/utils/tests/test_codegen.py @@ -0,0 +1,40 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +import sys +import traceback + +import pytest + +from astropy.utils.codegen import make_function_with_signature + + +def test_make_function_with_signature_lineno(): + """ + Tests that a function made with ``make_function_with_signature`` is give + the correct line number into the module it was created from (i.e. the line + ``make_function_with_signature`` was called from). + """ + + def crashy_function(*args, **kwargs): + 1 / 0 + + # Make a wrapper around this function with the signature: + # crashy_function(a, b) + # Note: the signature is not really relevant to this test + wrapped = make_function_with_signature(crashy_function, ("a", "b")) + line = """ + wrapped = make_function_with_signature(crashy_function, ('a', 'b')) + """.strip() + + try: + wrapped(1, 2) + except Exception: + exc_cls, exc, tb = sys.exc_info() + assert exc_cls is ZeroDivisionError + # The *last* line in the traceback should be the 1 / 0 line in + # crashy_function; the next line up should be the line that the + # make_function_with_signature call was one + tb_lines = traceback.format_tb(tb) + assert "1 / 0" in tb_lines[-1] + else: + pytest.fail("This should have caused an exception") diff --git a/astropy/utils/tests/test_collections.py b/astropy/utils/tests/test_collections.py index 83a2dcc5ba9e..e6b04921b021 100644 --- a/astropy/utils/tests/test_collections.py +++ b/astropy/utils/tests/test_collections.py @@ -1,31 +1,75 @@ -from astropy.tests.helper import raises +# Licensed under a 3-clause BSD style license - see LICENSE.rst -from .. import collections +import pytest + +from astropy.utils import collections -@raises(TypeError) def test_homogeneous_list(): l = collections.HomogeneousList(int) - l.append(5.0) + with pytest.raises(TypeError): + l.append(5.0) -@raises(TypeError) def test_homogeneous_list2(): l = collections.HomogeneousList(int) - l.extend([5.0]) + with pytest.raises(TypeError): + l.extend([5.0]) def test_homogeneous_list3(): l = collections.HomogeneousList(int) l.append(5) + assert l == [5] def test_homogeneous_list4(): l = collections.HomogeneousList(int) l.extend([5]) + assert l == [5] -@raises(TypeError) def test_homogeneous_list5(): - l = collections.HomogeneousList(int, [1,2,3]) - l[1] = 5.0 + l = collections.HomogeneousList(int, [1, 2, 3]) + with pytest.raises(TypeError): + l[1] = 5.0 + + +def test_homogeneous_list_setitem_works(): + l = collections.HomogeneousList(int, [1, 2, 3]) + l[1] = 5 + assert l == [1, 5, 3] + + +def test_homogeneous_list_setitem_works_with_slice(): + l = collections.HomogeneousList(int, [1, 2, 3]) + l[0:1] = [10, 20, 30] + assert l == [10, 20, 30, 2, 3] + + l[:] = [5, 4, 3] + assert l == [5, 4, 3] + + l[::2] = [2, 1] + assert l == [2, 4, 1] + + +def test_homogeneous_list_init_got_invalid_type(): + with pytest.raises(TypeError): + collections.HomogeneousList(int, [1, 2.0, 3]) + + +def test_homogeneous_list_works_with_generators(): + hl = collections.HomogeneousList(int, (i for i in range(3))) + assert hl == [0, 1, 2] + + hl = collections.HomogeneousList(int) + hl.extend(i for i in range(3)) + assert hl == [0, 1, 2] + + hl = collections.HomogeneousList(int) + hl[0:1] = (i for i in range(3)) + assert hl == [0, 1, 2] + + hl = collections.HomogeneousList(int) + hl += (i for i in range(3)) + assert hl == [0, 1, 2] diff --git a/astropy/utils/tests/test_compat.py b/astropy/utils/tests/test_compat.py new file mode 100644 index 000000000000..8a6db6933420 --- /dev/null +++ b/astropy/utils/tests/test_compat.py @@ -0,0 +1,18 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +""" +Just deprecation tests, the rest is tested throughout astropy. +""" + +import pytest + +from astropy.utils.exceptions import AstropyPendingDeprecationWarning + + +def test_copy_if_needed_deprecation(): + with pytest.warns(AstropyPendingDeprecationWarning, match="COPY_IF_NEEDED"): + from astropy.utils.compat.numpycompat import COPY_IF_NEEDED + assert COPY_IF_NEEDED is None + + with pytest.warns(AstropyPendingDeprecationWarning, match="COPY_IF_NEEDED"): + from astropy.utils.compat import COPY_IF_NEEDED + assert COPY_IF_NEEDED is None diff --git a/astropy/utils/tests/test_console.py b/astropy/utils/tests/test_console.py index f0d53d2122ad..125f4121ae97 100644 --- a/astropy/utils/tests/test_console.py +++ b/astropy/utils/tests/test_console.py @@ -1,11 +1,68 @@ -# -*- coding: utf-8 -*- +# Licensed under a 3-clause BSD style license - see LICENSE.rst + import io import sys -from astropy.tests.helper import raises +import pytest + +from astropy import units as u +from astropy.utils import console + +from . import test_progress_bar_func + + +class FakeTTY(io.StringIO): + """IOStream that fakes a TTY; provide an encoding to emulate an output + stream with a specific encoding. + """ + + def __new__(cls, encoding=None): + # Return a new subclass of FakeTTY with the requested encoding + if encoding is None: + return super().__new__(cls) + + cls = type(encoding.title() + cls.__name__, (cls,), {"encoding": encoding}) + + return cls.__new__(cls) + + def __init__(self, encoding=None): + super().__init__() + + def write(self, s): + if isinstance(s, bytes): + # Just allow this case to work + s = s.decode("latin-1") + elif self.encoding is not None: + s.encode(self.encoding) + + return super().write(s) + + def isatty(self): + return True + -from .. import console +def test_fake_tty(): + # First test without a specified encoding; we should be able to write + # arbitrary unicode strings + f1 = FakeTTY() + assert f1.isatty() + f1.write("☃") + assert f1.getvalue() == "☃" + + # Now test an ASCII-only TTY--it should raise a UnicodeEncodeError when + # trying to write a string containing non-ASCII characters + f2 = FakeTTY("ascii") + assert f2.isatty() + assert f2.__class__.__name__ == "AsciiFakeTTY" + with pytest.raises(UnicodeEncodeError): + f2.write("☃") + assert f2.getvalue() == "" + + +@pytest.mark.skipif(sys.platform.startswith("win"), reason="Cannot test on Windows") +def test_color_text(): + assert console._color_text("foo", "green") == "\033[0;32mfoo\033[0m" def test_color_print(): @@ -20,45 +77,58 @@ def test_color_print2(): # not a tty stream = io.StringIO() console.color_print("foo", "green", file=stream) - assert stream.getvalue() == u'foo\n' + assert stream.getvalue() == "foo\n" stream = io.StringIO() console.color_print("foo", "green", "bar", "red", "baz", file=stream) - assert stream.getvalue() == u'foobarbaz\n' + assert stream.getvalue() == "foobarbaz\n" +@pytest.mark.skipif(sys.platform.startswith("win"), reason="Cannot test on Windows") def test_color_print3(): - # Test that this things the FakeTTY is a tty and applies colors. - class FakeTTY(io.StringIO): - def isatty(self): - return True + # Test that this thinks the FakeTTY is a tty and applies colors. stream = FakeTTY() console.color_print("foo", "green", file=stream) - assert stream.getvalue() == u'\x1b[0;32mfoo\x1b[0m\n' + assert stream.getvalue() == "\x1b[0;32mfoo\x1b[0m\n" stream = FakeTTY() console.color_print("foo", "green", "bar", "red", "baz", file=stream) - assert stream.getvalue() == u'\x1b[0;32mfoo\x1b[0m\x1b[0;31mbar\x1b[0mbaz\n' + assert stream.getvalue() == "\x1b[0;32mfoo\x1b[0m\x1b[0;31mbar\x1b[0mbaz\n" def test_color_print_unicode(): - console.color_print(u"ÃŧberbÃĻr", "red") + console.color_print("ÃŧberbÃĻr", "red") def test_color_print_invalid_color(): console.color_print("foo", "unknown") +def test_spinner_non_unicode_console(): + """Regression test for #1760 + + Ensures that the spinner can fall go into fallback mode when using the + unicode spinner on a terminal whose default encoding cannot encode the + unicode characters. + """ + + stream = FakeTTY("ascii") + chars = console.Spinner._default_unicode_chars + + with console.Spinner("Reticulating splines", file=stream, chars=chars) as s: + next(s) + + def test_progress_bar(): # This stuff is hard to test, at least smoke test it with console.ProgressBar(50) as bar: - for i in range(50): + for _ in range(50): bar.update() def test_progress_bar2(): - for x in console.ProgressBar.iterate(range(50)): + for _ in console.ProgressBar(range(50)): pass @@ -67,3 +137,72 @@ def do_nothing(*args, **kwargs): pass console.ProgressBar.map(do_nothing, range(50)) + + +def test_zero_progress_bar(): + with console.ProgressBar(0) as bar: + pass + + +def test_progress_bar_as_generator(): + sum = 0 + for x in console.ProgressBar(range(50)): + sum += x + assert sum == 1225 + + sum = 0 + for x in console.ProgressBar(50): + sum += x + assert sum == 1225 + + +def test_progress_bar_map(): + items = list(range(100)) + result = console.ProgressBar.map( + test_progress_bar_func.func, items, step=10, multiprocess=True + ) + assert items == result + + result1 = console.ProgressBar.map( + test_progress_bar_func.func, items, step=10, multiprocess=2 + ) + + assert items == result1 + + +@pytest.mark.parametrize( + ("seconds", "string"), + [ + (864088, " 1w 3d"), + (187213, " 2d 4h"), + (3905, " 1h 5m"), + (64, " 1m 4s"), + (15, " 15s"), + (2, " 2s"), + ], +) +def test_human_time(seconds, string): + human_time = console.human_time(seconds) + assert human_time == string + + +@pytest.mark.parametrize( + ("size", "string"), + [ + (8640882, "8.6M"), + (187213, "187k"), + (3905, "3.9k"), + (64, " 64 "), + (2, " 2 "), + (10 * u.GB, " 10G"), + ], +) +def test_human_file_size(size, string): + human_time = console.human_file_size(size) + assert human_time == string + + +@pytest.mark.parametrize("size", (50 * u.km, 100 * u.g)) +def test_bad_human_file_size(size): + with pytest.raises(u.UnitConversionError): + console.human_file_size(size) diff --git a/astropy/utils/tests/test_data.py b/astropy/utils/tests/test_data.py new file mode 100644 index 000000000000..d8a16562f33e --- /dev/null +++ b/astropy/utils/tests/test_data.py @@ -0,0 +1,2429 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +# NOTE: All the tests here might non-deterministically emit +# ResourceWarning about unclosed socket or SSLSocket, +# which we tell pytest to ignore. See GH Issue 9619. + +import base64 +import contextlib +import errno +import hashlib +import io +import itertools +import os +import pathlib +import platform +import random +import shutil +import stat +import sys +import tempfile +import urllib.error +import urllib.parse +import urllib.request +import warnings +from concurrent.futures import ThreadPoolExecutor +from itertools import islice +from pathlib import Path +from tempfile import NamedTemporaryFile, TemporaryDirectory +from uuid import uuid4 + +import pytest + +import astropy.utils.data +from astropy import units as _u # u is taken +from astropy.config import paths +from astropy.io import fits +from astropy.tests.helper import CI, IS_CRON +from astropy.utils.compat.optional_deps import HAS_BZ2, HAS_LZMA, HAS_UNCOMPRESSPY +from astropy.utils.data import ( + CacheDamaged, + CacheMissingWarning, + _deltemps, + _get_download_cache_loc, + _tempfilestodel, + cache_contents, + cache_total_size, + check_download_cache, + check_free_space_in_dir, + clear_download_cache, + compute_hash, + conf, + download_file, + download_files_in_parallel, + export_download_cache, + get_cached_urls, + get_file_contents, + get_free_space_in_dir, + get_pkg_data_contents, + get_pkg_data_filename, + get_pkg_data_fileobj, + get_pkg_data_path, + get_readable_fileobj, + import_download_cache, + import_file_to_cache, + is_url, + is_url_in_cache, +) +from astropy.utils.exceptions import AstropyWarning + +TESTURL = "http://www.astropy.org" +TESTURL2 = "http://www.astropy.org/about.html" +TESTURL_SSL = "https://www.astropy.org" +TESTLOCAL = get_pkg_data_filename(os.path.join("data", "local.dat")) + +# For when we need "some" test URLs +FEW = 5 + +# For stress testing the locking system using multiprocessing +N_PARALLEL_HAMMER = 5 # as high as 500 to replicate a bug + +# For stress testing the locking system using threads +# (cheaper, works with coverage) +N_THREAD_HAMMER = 10 # as high as 1000 to replicate a bug + + +def can_rename_directory_in_use(): + with TemporaryDirectory() as d: + d1 = os.path.join(d, "a") + d2 = os.path.join(d, "b") + f1 = os.path.join(d1, "file") + os.mkdir(d1) + with open(f1, "w") as f: + f.write("some contents\n") + try: + with open(f1): + os.rename(d1, d2) + except PermissionError: + return False + else: + return True + + +CAN_RENAME_DIRECTORY_IN_USE = can_rename_directory_in_use() + + +def url_to(path): + return pathlib.Path(path).resolve().as_uri() + + +@pytest.fixture +def valid_urls(tmp_path): + def _valid_urls(tmp_path): + for i in itertools.count(): + c = os.urandom(16).hex() + fn = tmp_path / f"valid_{i}" + with open(fn, "w") as f: + f.write(c) + u = url_to(fn) + yield u, c + + return _valid_urls(tmp_path) + + +@pytest.fixture +def invalid_urls(tmp_path): + def _invalid_urls(tmp_path): + for i in itertools.count(): + fn = tmp_path / f"invalid_{i}" + if not os.path.exists(fn): + yield url_to(fn) + + return _invalid_urls(tmp_path) + + +@pytest.fixture +def temp_cache(tmp_path): + with paths.set_temp_cache(tmp_path) as tmpdir: + os.makedirs(os.path.join(tmpdir, "download", "url")) + yield None + check_download_cache() + + +def change_tree_permission(d, writable=False): + if writable: + dirperm = stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR + fileperm = stat.S_IRUSR | stat.S_IWUSR + else: + dirperm = stat.S_IRUSR | stat.S_IXUSR + fileperm = stat.S_IRUSR + for dirpath, _, filenames in os.walk(d): + os.chmod(dirpath, dirperm) + for f in filenames: + os.chmod(os.path.join(dirpath, f), fileperm) + + +def is_dir_readonly(d): + try: + with NamedTemporaryFile(dir=d): + return False + except PermissionError: + return True + + +@contextlib.contextmanager +def readonly_dir(d): + try: + change_tree_permission(d, writable=False) + yield + finally: + change_tree_permission(d, writable=True) + + +@pytest.fixture +def readonly_cache(tmp_path, valid_urls): + with TemporaryDirectory(dir=tmp_path) as d: + # other fixtures use the same tmp_path so we need a subdirectory + # to make into the cache + d = pathlib.Path(d) + with paths.set_temp_cache(d): + us = {u for u, _ in islice(valid_urls, FEW)} + urls = {u: download_file(u, cache=True) for u in us} + files = set(d.iterdir()) + with readonly_dir(d): + if not is_dir_readonly(d): + pytest.skip("Unable to make directory readonly") + yield urls + assert set(d.iterdir()) == files + check_download_cache() + + +@pytest.fixture +def fake_readonly_cache(tmp_path, valid_urls, monkeypatch): + def no_mkdir(path, mode=None): + raise OSError(errno.EPERM, "os.mkdir monkeypatched out") + + def no_mkdtemp(*args, **kwargs): + """On Windows, mkdtemp uses mkdir in a loop and therefore hangs + with it monkeypatched out. + """ + raise OSError(errno.EPERM, "os.mkdtemp monkeypatched out") + + with TemporaryDirectory(dir=tmp_path) as d: + # other fixtures use the same tmp_path so we need a subdirectory + # to make into the cache + d = pathlib.Path(d) + with paths.set_temp_cache(d): + us = {u for u, _ in islice(valid_urls, FEW)} + urls = {u: download_file(u, cache=True) for u in us} + files = set(d.iterdir()) + monkeypatch.setattr(os, "mkdir", no_mkdir) + monkeypatch.setattr(tempfile, "mkdtemp", no_mkdtemp) + yield urls + assert set(d.iterdir()) == files + check_download_cache() + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_download_file_basic(valid_urls, temp_cache): + u, c = next(valid_urls) + assert get_file_contents(download_file(u, cache=False)) == c + assert not is_url_in_cache(u) + assert get_file_contents(download_file(u, cache=True)) == c # Cache miss + assert is_url_in_cache(u) + assert get_file_contents(download_file(u, cache=True)) == c # Cache hit + assert get_file_contents(download_file(u, cache=True, sources=[])) == c + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_download_file_absolute_path(valid_urls, temp_cache): + def is_abs(p): + return p == os.path.abspath(p) + + u, c = next(valid_urls) + assert is_abs(download_file(u, cache=False)) # no cache + assert is_abs(download_file(u, cache=True)) # not in cache + assert is_abs(download_file(u, cache=True)) # in cache + for v in cache_contents().values(): + assert is_abs(v) + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_unicode_url(valid_urls, temp_cache): + u, c = next(valid_urls) + unicode_url = "http://Ê—☃—è.com" + download_file(unicode_url, cache=False, sources=[u]) + download_file(unicode_url, cache=True, sources=[u]) + download_file(unicode_url, cache=True, sources=[]) + assert is_url_in_cache(unicode_url) + assert unicode_url in cache_contents() + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_too_long_url(valid_urls, temp_cache): + u, c = next(valid_urls) + long_url = "http://" + "a" * 256 + ".com" + download_file(long_url, cache=False, sources=[u]) + download_file(long_url, cache=True, sources=[u]) + download_file(long_url, cache=True, sources=[]) + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_case_collision(valid_urls, temp_cache): + u, c = next(valid_urls) + u2, c2 = next(valid_urls) + f1 = download_file("http://example.com/thing", cache=True, sources=[u]) + f2 = download_file("http://example.com/THING", cache=True, sources=[u2]) + assert f1 != f2 + assert get_file_contents(f1) != get_file_contents(f2) + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_domain_name_case(valid_urls, temp_cache): + u, c = next(valid_urls) + download_file("http://Example.com/thing", cache=True, sources=[u]) + assert is_url_in_cache("http://EXAMPLE.com/thing") + download_file("http://EXAMPLE.com/thing", cache=True, sources=[]) + assert is_url_in_cache("Http://example.com/thing") + download_file("Http://example.com/thing", cache=True, sources=[]) + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +@pytest.mark.remote_data(source="astropy") +def test_download_nocache_from_internet(): + fnout = download_file(TESTURL, cache=False) + assert os.path.isfile(fnout) + + +@pytest.fixture +def a_binary_file(tmp_path): + fn = tmp_path / "file" + b_contents = b"\xde\xad\xbe\xef" + with open(fn, "wb") as f: + f.write(b_contents) + return fn, b_contents + + +@pytest.fixture +def a_file(tmp_path): + fn = tmp_path / "file.txt" + contents = "contents\n" + with open(fn, "w") as f: + f.write(contents) + return fn, contents + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +@pytest.mark.parametrize("strategy", ["parallel", "sequential"]) +def test_download_with_sources_and_bogus_original( + valid_urls, invalid_urls, temp_cache, strategy +): + # This is a combined test because the parallel version triggered a nasty + # bug and I was trying to track it down by comparing with the non-parallel + # version. I think the bug was that the parallel downloader didn't respect + # temporary cache settings. + + # Make a big list of test URLs + u, c = next(valid_urls) + # as tuples (URL, right_content, wrong_content) + urls = [(u, c, None)] + # where to download the contents + sources = {} + # Set up some URLs to download where the "true" URL is not in the sources + # list; make the true URL valid with different contents so we can tell if + # it was loaded by mistake. + for i, (um, c_bad) in enumerate(islice(valid_urls, FEW)): + assert not is_url_in_cache(um) + # For many of them the sources list starts with invalid URLs + sources[um] = list(islice(invalid_urls, i)) + u, c = next(valid_urls) + sources[um].append(u) + urls.append((um, c, c_bad)) + + # Now fetch them all + if strategy == "parallel": + rs = download_files_in_parallel( + [u for (u, c, c_bad) in urls], cache=True, sources=sources + ) + elif strategy == "sequential": + rs = [ + download_file(u, cache=True, sources=sources.get(u)) + for (u, c, c_bad) in urls + ] + else: + raise AssertionError + assert len(rs) == len(urls) + for r, (u, c, c_bad) in zip(rs, urls): + assert get_file_contents(r) == c + assert get_file_contents(r) != c_bad + assert is_url_in_cache(u) + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +@pytest.mark.skipif( + (sys.platform.startswith("win") and CI), reason="flaky cache error on Windows CI" +) +def test_download_file_threaded_many(temp_cache, valid_urls): + """Hammer download_file with multiple threaded requests. + + The goal is to stress-test the locking system. Normal parallel downloading + also does this but coverage tools lose track of which paths are explored. + + """ + urls = list(islice(valid_urls, N_THREAD_HAMMER)) + with ThreadPoolExecutor(max_workers=len(urls)) as P: + r = list(P.map(lambda u: download_file(u, cache=True), [u for (u, c) in urls])) + check_download_cache() + assert len(r) == len(urls) + for r_, (_, c) in zip(r, urls): + assert get_file_contents(r_) == c + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +@pytest.mark.skipif( + (sys.platform.startswith("win") and CI), reason="flaky cache error on Windows CI" +) +def test_threaded_segfault(valid_urls): + """Demonstrate urllib's segfault.""" + + def slurp_url(u): + with urllib.request.urlopen(u) as remote: + block = True + while block: + block = remote.read(1024) + + urls = list(islice(valid_urls, N_THREAD_HAMMER)) + with ThreadPoolExecutor(max_workers=len(urls)) as P: + list(P.map(slurp_url, [u for (u, c) in urls])) + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +@pytest.mark.skipif( + (sys.platform.startswith("win") and CI), reason="flaky cache error on Windows CI" +) +def test_download_file_threaded_many_partial_success( + temp_cache, valid_urls, invalid_urls +): + """Hammer download_file with multiple threaded requests. + + Because some of these requests fail, the locking context manager is + exercised with exceptions as well as success returns. I do not expect many + surprises from the threaded version, but the process version gave trouble + here. + + """ + urls = [] + contents = {} + for (u, c), i in islice(zip(valid_urls, invalid_urls), N_THREAD_HAMMER): + urls.append(u) + contents[u] = c + urls.append(i) + + def get(u): + try: + return download_file(u, cache=True) + except OSError: + return None + + with ThreadPoolExecutor(max_workers=len(urls)) as P: + r = list(P.map(get, urls)) + check_download_cache() + assert len(r) == len(urls) + for r_, u in zip(r, urls): + if u in contents: + assert get_file_contents(r_) == contents[u] + else: + assert r_ is None + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_clear_download_cache(valid_urls): + u1, c1 = next(valid_urls) + download_file(u1, cache=True) + + u2, c2 = next(valid_urls) + download_file(u2, cache=True) + + assert is_url_in_cache(u2) + clear_download_cache(u2) + assert not is_url_in_cache(u2) + assert is_url_in_cache(u1) + + u3, c3 = next(valid_urls) + f3 = download_file(u3, cache=True) + + assert is_url_in_cache(u3) + clear_download_cache(f3) + assert not is_url_in_cache(u3) + assert is_url_in_cache(u1) + + u4, c4 = next(valid_urls) + f4 = download_file(u4, cache=True) + + assert is_url_in_cache(u4) + clear_download_cache(compute_hash(f4)) + assert not is_url_in_cache(u4) + assert is_url_in_cache(u1) + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_clear_download_multiple_references_doesnt_corrupt_storage( + temp_cache, tmp_path +): + """Check that files with the same hash don't confuse the storage.""" + content = "Test data; doesn't matter much.\n" + + def make_url(): + with NamedTemporaryFile("w", dir=tmp_path, delete=False) as f: + f.write(content) + url = url_to(f.name) + clear_download_cache(url, on_missing="ignore") + filename = download_file(url, cache=True) + return url, filename + + a_url, a_filename = make_url() + clear_download_cache(a_filename) + assert not is_url_in_cache(a_url) + + f_url, f_filename = make_url() + g_url, g_filename = make_url() + + assert f_url != g_url + assert is_url_in_cache(f_url) + assert is_url_in_cache(g_url) + + clear_download_cache(f_url) + assert not is_url_in_cache(f_url) + assert is_url_in_cache(g_url) + assert os.path.exists(g_filename), ( + "Contents should not be deleted while a reference exists" + ) + + clear_download_cache(g_url) + assert not os.path.exists(g_filename), ( + "No reference exists any more, file should be deleted" + ) + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +@pytest.mark.parametrize("use_cache", [False, True]) +def test_download_file_local_cache_survives(tmp_path, temp_cache, use_cache): + """Confirm that downloading a local file does not delete it. + + When implemented with urlretrieve (rather than urlopen) local files are + not copied to create temporaries, so importing them to the cache deleted + the original from wherever it was in the filesystem. I lost some built-in + astropy data. + + """ + fn = tmp_path / "file" + contents = "some text" + with open(fn, "w") as f: + f.write(contents) + u = url_to(fn) + f = download_file(u, cache=use_cache) + assert fn not in _tempfilestodel, "File should not be deleted!" + assert os.path.isfile(fn), "File should not be deleted!" + assert get_file_contents(f) == contents + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_sources_normal(temp_cache, valid_urls, invalid_urls): + primary, contents = next(valid_urls) + fallback1 = next(invalid_urls) + f = download_file(primary, cache=True, sources=[primary, fallback1]) + assert get_file_contents(f) == contents + assert is_url_in_cache(primary) + assert not is_url_in_cache(fallback1) + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_sources_fallback(temp_cache, valid_urls, invalid_urls): + primary = next(invalid_urls) + fallback1, contents = next(valid_urls) + f = download_file(primary, cache=True, sources=[primary, fallback1]) + assert get_file_contents(f) == contents + assert is_url_in_cache(primary) + assert not is_url_in_cache(fallback1) + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_sources_ignore_primary(temp_cache, valid_urls, invalid_urls): + primary, bogus = next(valid_urls) + fallback1, contents = next(valid_urls) + f = download_file(primary, cache=True, sources=[fallback1]) + assert get_file_contents(f) == contents + assert is_url_in_cache(primary) + assert not is_url_in_cache(fallback1) + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_sources_multiple(temp_cache, valid_urls, invalid_urls): + primary = next(invalid_urls) + fallback1 = next(invalid_urls) + fallback2, contents = next(valid_urls) + f = download_file(primary, cache=True, sources=[primary, fallback1, fallback2]) + assert get_file_contents(f) == contents + assert is_url_in_cache(primary) + assert not is_url_in_cache(fallback1) + assert not is_url_in_cache(fallback2) + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_sources_multiple_missing(temp_cache, valid_urls, invalid_urls): + primary = next(invalid_urls) + fallback1 = next(invalid_urls) + fallback2 = next(invalid_urls) + with pytest.raises(urllib.error.URLError): + download_file(primary, cache=True, sources=[primary, fallback1, fallback2]) + assert not is_url_in_cache(primary) + assert not is_url_in_cache(fallback1) + assert not is_url_in_cache(fallback2) + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_update_url(tmp_path, temp_cache): + with TemporaryDirectory(dir=tmp_path) as d: + f_name = os.path.join(d, "f") + with open(f_name, "w") as f: + f.write("old") + f_url = url_to(f.name) + assert get_file_contents(download_file(f_url, cache=True)) == "old" + with open(f_name, "w") as f: + f.write("new") + assert get_file_contents(download_file(f_url, cache=True)) == "old" + assert get_file_contents(download_file(f_url, cache="update")) == "new" + # Now the URL doesn't exist any more. + assert not os.path.exists(f_name) + with pytest.raises(urllib.error.URLError): + # Direct download should fail + download_file(f_url, cache=False) + assert get_file_contents(download_file(f_url, cache=True)) == "new", ( + "Cached version should still exist" + ) + with pytest.raises(urllib.error.URLError): + # cannot download new version to check for updates + download_file(f_url, cache="update") + assert get_file_contents(download_file(f_url, cache=True)) == "new", ( + "Failed update should not remove the current version" + ) + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +@pytest.mark.remote_data(source="astropy") +def test_download_noprogress(): + fnout = download_file(TESTURL, cache=False, show_progress=False) + assert os.path.isfile(fnout) + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +@pytest.mark.remote_data(source="astropy") +def test_download_cache(): + download_dir = _get_download_cache_loc() + + # Download the test URL and make sure it exists, then clear just that + # URL and make sure it got deleted. + fnout = download_file(TESTURL, cache=True) + assert os.path.isdir(download_dir) + assert os.path.isfile(fnout) + clear_download_cache(TESTURL) + assert not os.path.exists(fnout) + + # Clearing download cache succeeds even if the URL does not exist. + clear_download_cache("http://this_was_never_downloaded_before.com") + + # Make sure lockdir was released + lockdir = os.path.join(download_dir, "lock") + assert not os.path.isdir(lockdir), "Cache dir lock was not released!" + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +@pytest.mark.remote_data(source="astropy") +def test_download_certificate_verification_failed(): + """Tests for https://github.com/astropy/astropy/pull/10434""" + + # First test the expected exception when download fails due to a + # certificate verification error; we simulate this by passing a bogus + # CA directory to the ssl_context argument + ssl_context = {"cafile": None, "capath": "/does/not/exist"} + msg = f"Verification of TLS/SSL certificate at {TESTURL_SSL} failed" + with pytest.raises(urllib.error.URLError, match=msg): + download_file(TESTURL_SSL, cache=False, ssl_context=ssl_context) + + with pytest.warns(AstropyWarning, match=msg) as warning_lines: + fnout = download_file( + TESTURL_SSL, cache=False, ssl_context=ssl_context, allow_insecure=True + ) + + assert len(warning_lines) == 1 + assert os.path.isfile(fnout) + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_download_cache_after_clear(tmp_path, temp_cache, valid_urls): + testurl, contents = next(valid_urls) + # Test issues raised in #4427 with clear_download_cache() without a URL, + # followed by subsequent download. + download_dir = _get_download_cache_loc(on_missing="ignore") + + fnout = download_file(testurl, cache=True) + assert os.path.isfile(fnout) + + clear_download_cache() + assert not os.path.exists(fnout) + assert not os.path.exists(download_dir) + + fnout = download_file(testurl, cache=True) + assert os.path.isfile(fnout) + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +@pytest.mark.remote_data(source="astropy") +def test_download_parallel_from_internet_works(temp_cache): + main_url = conf.dataurl + mirror_url = conf.dataurl_mirror + fileloc = "intersphinx/README" + urls = [] + sources = {} + for s in ["", fileloc]: + urls.append(main_url + s) + sources[urls[-1]] = [urls[-1], mirror_url + s] + fnout = download_files_in_parallel(urls, sources=sources) + assert all(os.path.isfile(f) for f in fnout), fnout + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +@pytest.mark.parametrize("method", [None, "spawn"]) +def test_download_parallel_fills_cache(valid_urls, method): + urls = [] + with paths.temporary_cache_dir_path(namespace="astropy") as tmp_path: + tmp_path.joinpath("download", "url").mkdir(parents=True) + for um, c in islice(valid_urls, FEW): + assert not is_url_in_cache(um) + urls.append((um, c)) + rs = download_files_in_parallel( + [u for (u, c) in urls], multiprocessing_start_method=method + ) + assert len(rs) == len(urls) + url_set = {u for (u, c) in urls} + assert url_set <= set(get_cached_urls()) + for r, (_, c) in zip(rs, urls): + assert get_file_contents(r) == c + check_download_cache() + with warnings.catch_warnings(): + warnings.simplefilter("ignore", CacheMissingWarning) + new_urls = get_cached_urls() + assert not url_set.intersection(new_urls) + check_download_cache() + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_download_parallel_with_empty_sources(valid_urls, temp_cache): + urls = [] + sources = {} + for um, c in islice(valid_urls, FEW): + assert not is_url_in_cache(um) + urls.append((um, c)) + rs = download_files_in_parallel([u for (u, c) in urls], sources=sources) + assert len(rs) == len(urls) + # u = set(u for (u, c) in urls) + # assert u <= set(get_cached_urls()) + check_download_cache() + for r, (_, c) in zip(rs, urls): + assert get_file_contents(r) == c + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_download_parallel_with_sources_and_bogus_original( + valid_urls, invalid_urls, temp_cache +): + u, c = next(valid_urls) + urls = [(u, c, None)] + sources = {} + for i, (um, c_bad) in enumerate(islice(valid_urls, FEW)): + assert not is_url_in_cache(um) + sources[um] = list(islice(invalid_urls, i)) + u, c = next(valid_urls) + sources[um].append(u) + urls.append((um, c, c_bad)) + rs = download_files_in_parallel([u for (u, c, c_bad) in urls], sources=sources) + assert len(rs) == len(urls) + # u = set(u for (u, c, c_bad) in urls) + # assert u <= set(get_cached_urls()) + for r, (_, c, c_bad) in zip(rs, urls): + assert get_file_contents(r) == c + assert get_file_contents(r) != c_bad + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_download_parallel_many(temp_cache, valid_urls): + td = list(islice(valid_urls, N_PARALLEL_HAMMER)) + + r = download_files_in_parallel([u for (u, c) in td]) + assert len(r) == len(td) + for r_, (_, c) in zip(r, td): + assert get_file_contents(r_) == c + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_download_parallel_partial_success(temp_cache, valid_urls, invalid_urls): + """Check that a partially successful download works. + + Even in the presence of many requested URLs, presumably hitting all the + parallelism this system can manage, a download failure leads to a tidy + shutdown. + + """ + td = list(islice(valid_urls, N_PARALLEL_HAMMER)) + + u_bad = next(invalid_urls) + + with pytest.raises(urllib.request.URLError): + download_files_in_parallel([u_bad] + [u for (u, c) in td]) + # Actually some files may get downloaded, others not. + # Is this good? Should we stubbornly keep trying? + # assert not any([is_url_in_cache(u) for (u, c) in td]) + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +@pytest.mark.slow +def test_download_parallel_partial_success_lock_safe( + temp_cache, valid_urls, invalid_urls +): + """Check that a partially successful parallel download leaves the cache unlocked. + + This needs to be repeated many times because race conditions are what cause + this sort of thing, especially situations where a process might be forcibly + shut down while it holds the lock. + + """ + s = random.getstate() + try: + random.seed(0) + for _ in range(N_PARALLEL_HAMMER): + td = list(islice(valid_urls, FEW)) + + u_bad = next(invalid_urls) + urls = [u_bad] + [u for (u, c) in td] + random.shuffle(urls) + with pytest.raises(urllib.request.URLError): + download_files_in_parallel(urls) + finally: + random.setstate(s) + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_download_parallel_update(temp_cache, tmp_path): + td = [] + for i in range(N_PARALLEL_HAMMER): + c = f"{i:04d}" + fn = tmp_path / c + with open(fn, "w") as f: + f.write(c) + u = url_to(fn) + clear_download_cache(u, on_missing="ignore") + td.append((fn, u, c)) + + r1 = download_files_in_parallel([u for (fn, u, c) in td]) + assert len(r1) == len(td) + for r_1, (_, _, c) in zip(r1, td): + assert get_file_contents(r_1) == c + + td2 = [] + for fn, u, c in td: + c_plus = f"{c} updated" + fn = tmp_path / c + with open(fn, "w") as f: + f.write(c_plus) + td2.append((fn, u, c, c_plus)) + + r2 = download_files_in_parallel([u for (fn, u, c) in td], cache=True) + assert len(r2) == len(td) + for r_2, (_, _, c, c_plus) in zip(r2, td2): + assert get_file_contents(r_2) == c + assert c != c_plus + r3 = download_files_in_parallel([u for (fn, u, c) in td], cache="update") + + assert len(r3) == len(td) + for r_3, (_, _, c, c_plus) in zip(r3, td2): + assert get_file_contents(r_3) != c + assert get_file_contents(r_3) == c_plus + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +@pytest.mark.skipif( + (sys.platform.startswith("win") and CI), reason="flaky cache error on Windows CI" +) +def test_update_parallel(temp_cache, valid_urls): + u, c = next(valid_urls) + u2, c2 = next(valid_urls) + + f = download_file(u, cache=True) + assert get_file_contents(f) == c + + def update(i): + return download_file(u, cache="update", sources=[u2]) + + with ThreadPoolExecutor(max_workers=N_THREAD_HAMMER) as P: + r = set(P.map(update, range(N_THREAD_HAMMER))) + + check_download_cache() + for f in r: + assert get_file_contents(f) == c2 + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +@pytest.mark.skipif( + (sys.platform.startswith("win") and CI), reason="flaky cache error on Windows CI" +) +def test_update_parallel_multi(temp_cache, valid_urls): + u, c = next(valid_urls) + iucs = list(islice(valid_urls, N_THREAD_HAMMER)) + + f = download_file(u, cache=True) + assert get_file_contents(f) == c + + def update(uc): + u2, c2 = uc + return download_file(u, cache="update", sources=[u2]), c2 + + with ThreadPoolExecutor(max_workers=len(iucs)) as P: + r = list(P.map(update, iucs)) + + check_download_cache() + assert any(get_file_contents(f) == c for (f, c) in r) + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +@pytest.mark.remote_data(source="astropy") +def test_url_nocache(): + with get_readable_fileobj(TESTURL, cache=False, encoding="utf-8") as page: + assert page.read().find("Astropy") > -1 + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_find_by_hash(valid_urls, temp_cache): + testurl, contents = next(valid_urls) + p = download_file(testurl, cache=True) + hash = compute_hash(p) + + hashstr = "hash/" + hash + + fnout = get_pkg_data_filename(hashstr) + assert os.path.isfile(fnout) + clear_download_cache(fnout) + assert not os.path.isfile(fnout) + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +@pytest.mark.remote_data(source="astropy") +def test_find_invalid(): + # this is of course not a real data file and not on any remote server, but + # it should *try* to go to the remote server + with pytest.raises((urllib.error.URLError, TimeoutError)): + get_pkg_data_filename( + "kjfrhgjklahgiulrhgiuraehgiurhgiuhreglhurieghruelighiuerahiulruli" + ) + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +@pytest.mark.parametrize("package", [None, "astropy", "numpy"]) +def test_get_invalid(package): + """Test can create a file path to an invalid file.""" + path = get_pkg_data_path("kjfrhgjkla", "hgiulrhgiu", package=package) + assert not os.path.isfile(path) + assert not os.path.isdir(path) + + +# Package data functions +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +@pytest.mark.parametrize( + "filename", + ["local.dat", "local.dat.gz", "local.dat.bz2", "local.dat.xz", "local.dat.Z"], +) +def test_local_data_obj(filename): + if ( + (not HAS_BZ2 and "bz2" in filename) + or (not HAS_LZMA and "xz" in filename) + or (not HAS_UNCOMPRESSPY and "Z" in filename) + ): + with pytest.raises(ModuleNotFoundError): + with get_pkg_data_fileobj( + os.path.join("data", filename), encoding="binary" + ) as f: + f.readline() + # assert f.read().rstrip() == b'CONTENT' + else: + with get_pkg_data_fileobj( + os.path.join("data", filename), encoding="binary" + ) as f: + f.readline() + assert f.read().rstrip() == b"CONTENT" + + +@pytest.fixture( + params=["invalid.dat.bz2", "invalid.dat.xz", "invalid.dat.gz", "invalid.dat.Z"] +) +def bad_compressed(request, tmp_path): + # These contents have valid headers for their respective file formats, but + # are otherwise malformed and invalid. + bz_content = b"BZhinvalid" + gz_content = b"\x1f\x8b\x08invalid" + xz_content = b"\xfd7zXZ\x00invalid" + lzw_content = b"\x1f\x9d\x90invalid" + + datafile = tmp_path / request.param + filename = str(datafile) + + if filename.endswith(".bz2"): + contents = bz_content + elif filename.endswith(".gz"): + contents = gz_content + elif filename.endswith(".xz"): + contents = xz_content + elif filename.endswith(".Z"): + contents = lzw_content + else: + contents = "invalid" + + datafile.write_bytes(contents) + + return filename + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_local_data_obj_invalid(bad_compressed): + is_bz2 = bad_compressed.endswith(".bz2") + is_xz = bad_compressed.endswith(".xz") + is_lzw = bad_compressed.endswith(".Z") + + # Note, since these invalid files are created on the fly in order to avoid + # problems with detection by antivirus software + # (see https://github.com/astropy/astropy/issues/6520), it is no longer + # possible to use ``get_pkg_data_fileobj`` to read the files. Technically, + # they're not local anymore: they just live in a temporary directory + # created by pytest. However, we can still use get_readable_fileobj for the + # test. + if ( + (not HAS_BZ2 and is_bz2) + or (not HAS_LZMA and is_xz) + or (not HAS_UNCOMPRESSPY and is_lzw) + ): + with pytest.raises(ModuleNotFoundError): + with get_readable_fileobj(bad_compressed, encoding="binary") as f: + f.read() + else: + with get_readable_fileobj(bad_compressed, encoding="binary") as f: + assert f.read().rstrip().endswith(b"invalid") + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_local_data_name(): + assert os.path.isfile(TESTLOCAL) and TESTLOCAL.endswith("local.dat") + + # TODO: if in the future, the root data/ directory is added in, the below + # test should be uncommented and the README.rst should be replaced with + # whatever file is there + + # get something in the astropy root + # fnout2 = get_pkg_data_filename('../../data/README.rst') + # assert os.path.isfile(fnout2) and fnout2.endswith('README.rst') + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_data_name_third_party_package(): + """Regression test for issue #1256 + + Tests that `get_pkg_data_filename` works in a third-party package that + doesn't make any relative imports from the module it's used from. + + Uses a test package under ``data/test_package``. + """ + + # Get the actual data dir: + data_dir = os.path.join(os.path.dirname(__file__), "data") + + sys.path.insert(0, data_dir) + try: + import test_package + + filename = test_package.get_data_filename() + assert os.path.normcase(filename) == ( + os.path.normcase(os.path.join(data_dir, "test_package", "data", "foo.txt")) + ) + finally: + sys.path.pop(0) + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_local_data_nonlocalfail(): + # this would go *outside* the astropy tree + with pytest.raises(RuntimeError): + get_pkg_data_filename("../../../data/README.rst") + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_compute_hash(tmp_path): + rands = b"1234567890abcdefghijklmnopqrstuvwxyz" + + filename = tmp_path / "tmp.dat" + + with open(filename, "wb") as ntf: + ntf.write(rands) + ntf.flush() + + chhash = compute_hash(filename) + shash = hashlib.md5(rands, usedforsecurity=False).hexdigest() + + assert chhash == shash + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_get_pkg_data_contents(): + with get_pkg_data_fileobj("data/local.dat") as f: + contents1 = f.read() + + contents2 = get_pkg_data_contents("data/local.dat") + + assert contents1 == contents2 + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +@pytest.mark.remote_data(source="astropy") +@pytest.mark.usefixtures("ignore_config_paths_global_state") +def test_data_noastropy_fallback(monkeypatch): + """ + Tests to make sure the default behavior when the cache directory can't + be located is correct + """ + + # better yet, set the configuration to make sure the temp files are deleted + conf.delete_temporary_downloads_at_exit = True + + # make sure the _find_or_create_astropy_dir function fails as though the + # astropy dir could not be accessed + @classmethod + def osraiser(cls, linkto, pkgname=None): + raise OSError("mock os error") + + monkeypatch.setattr(paths._DirectoryFinder, "find_namespaced_node", osraiser) + + # make sure the config dir search fails + with pytest.raises(OSError, match="^mock os error$"): + paths.get_cache_dir(rootname="astropy") + + with pytest.raises(OSError, match="^mock os error$"): + paths.get_cache_dir_path(rootname="astropy") + + with pytest.warns(CacheMissingWarning) as warning_lines: + fnout = download_file(TESTURL, cache=True) + n_warns = len(warning_lines) + + allowed_warn_msgs = ["remote data cache could not be accessed", "temporary file"] + if n_warns == 4: + allowed_warn_msgs.extend(["socket", "socket"]) + + unexpected_warn_msgs: list[str] = [] + for wl in warning_lines: + msg = str(wl).lower() + for allowed in allowed_warn_msgs: + if allowed in msg: + break + else: + unexpected_warn_msgs.append(msg) + + assert not unexpected_warn_msgs, ( + f"Got some unexpected warnings: {unexpected_warn_msgs}" + ) + + assert os.path.isfile(fnout) + + # clearing the cache should be a no-up that doesn't affect fnout + with pytest.warns( + CacheMissingWarning, + match="^Not clearing data from cache - problem arose OSError: mock os error$", + ): + clear_download_cache(TESTURL) + assert os.path.isfile(fnout) + + # now remove it so tests don't clutter up the temp dir this should get + # called at exit, anyway, but we do it here just to make sure it's working + # correctly + _deltemps() + assert not os.path.isfile(fnout) + + # now try with no cache + fnnocache = download_file(TESTURL, cache=False) + with open(fnnocache, "rb") as page: + assert page.read().decode("utf-8").find("Astropy") > -1 + + # no warnings should be raise in fileobj because cache is unnecessary + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +@pytest.mark.parametrize( + "encoding, expected_type, expected_lines", + [ + pytest.param("utf-8", str, ["האסטרונומי פיי×Ēון"], id="utf-8"), + pytest.param( + "binary", + bytes, + [ + b"\xd7\x94\xd7\x90\xd7\xa1\xd7\x98\xd7\xa8\xd7\x95\xd7\xa0\xd7\x95" + b"\xd7\x9e\xd7\x99 \xd7\xa4\xd7\x99\xd7\x99\xd7\xaa\xd7\x95\xd7\x9f" + ], + id="binary", + ), + ], +) +@pytest.mark.parametrize( + "filename", + [ + "unicode.txt", + "unicode.txt.gz", + pytest.param( + "unicode.txt.bz2", + marks=pytest.mark.xfail(not HAS_BZ2, reason="no bz2 support"), + ), + pytest.param( + "unicode.txt.xz", + marks=pytest.mark.xfail(not HAS_LZMA, reason="no lzma support"), + ), + pytest.param( + "unicode.txt.Z", + marks=pytest.mark.xfail(not HAS_UNCOMPRESSPY, reason="no lzw support"), + ), + ], +) +def test_read_unicode(filename, encoding, expected_type, expected_lines): + contents = get_pkg_data_contents(os.path.join("data", filename), encoding=encoding) + assert type(contents) is expected_type + # Using splitlines() instead of split("\n") here for portability as + # newlines can be represented differently in different OSes. + assert contents.splitlines() == expected_lines + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_compressed_stream(): + gzipped_data = ( + b"H4sICIxwG1AAA2xvY2FsLmRhdAALycgsVkjLzElVANKlxakpCpl5CiUZqQ" + b"olqcUl8Tn5yYk58SmJJYnxWmCRzLx0hbTSvOSSzPy8Yi5nf78QV78QLgAlLytnRQAAAA==" + ) + gzipped_data = base64.b64decode(gzipped_data) + assert isinstance(gzipped_data, bytes) + + class FakeStream: + """ + A fake stream that has `read`, but no `seek`. + """ + + def __init__(self, data): + self.data = data + + def read(self, nbytes=None): + if nbytes is None: + result = self.data + self.data = b"" + else: + result = self.data[:nbytes] + self.data = self.data[nbytes:] + return result + + stream = FakeStream(gzipped_data) + with get_readable_fileobj(stream, encoding="binary") as f: + f.readline() + assert f.read().rstrip() == b"CONTENT" + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +@pytest.mark.remote_data(source="astropy") +def test_invalid_location_download_raises_urlerror(): + """ + checks that download_file gives a URLError and not an AttributeError, + as its code pathway involves some fiddling with the exception. + """ + + with pytest.raises(urllib.error.URLError): + download_file("http://www.astropy.org/nonexistentfile") + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_invalid_location_download_noconnect(): + """ + checks that download_file gives an OSError if the socket is blocked + """ + + # This should invoke socket's monkeypatched failure + with pytest.raises(OSError): + download_file("http://astropy.org/nonexistentfile") + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +@pytest.mark.remote_data(source="astropy") +def test_is_url_in_cache_remote(): + assert not is_url_in_cache("http://astropy.org/nonexistentfile") + + download_file(TESTURL, cache=True, show_progress=False) + assert is_url_in_cache(TESTURL) + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_is_url_in_cache_local(temp_cache, valid_urls, invalid_urls): + testurl, contents = next(valid_urls) + nonexistent = next(invalid_urls) + + assert not is_url_in_cache(testurl) + assert not is_url_in_cache(nonexistent) + + download_file(testurl, cache=True, show_progress=False) + assert is_url_in_cache(testurl) + assert not is_url_in_cache(nonexistent) + + +# If non-deterministic failure happens see +# https://github.com/astropy/astropy/issues/9765 +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_check_download_cache(tmp_path, temp_cache, valid_urls, invalid_urls): + testurl, testurl_contents = next(valid_urls) + testurl2, testurl2_contents = next(valid_urls) + + zip_file_name = tmp_path / "the.zip" + clear_download_cache(on_missing="ignore") + assert not check_download_cache() + + download_file(testurl, cache=True) + check_download_cache() + download_file(testurl2, cache=True) + check_download_cache() + + export_download_cache(zip_file_name, [testurl, testurl2]) + check_download_cache() + + clear_download_cache(testurl2) + check_download_cache() + + import_download_cache(zip_file_name, [testurl]) + check_download_cache() + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_export_import_roundtrip_one(tmp_path, temp_cache, valid_urls): + testurl, contents = next(valid_urls) + f = download_file(testurl, cache=True, show_progress=False) + assert get_file_contents(f) == contents + + initial_urls_in_cache = set(get_cached_urls()) + zip_file_name = tmp_path / "the.zip" + + export_download_cache(zip_file_name, [testurl]) + clear_download_cache(testurl) + import_download_cache(zip_file_name) + assert is_url_in_cache(testurl) + assert set(get_cached_urls()) == initial_urls_in_cache + assert ( + get_file_contents(download_file(testurl, cache=True, show_progress=False)) + == contents + ) + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_export_url_not_present(temp_cache, valid_urls): + testurl, contents = next(valid_urls) + with NamedTemporaryFile("wb") as zip_file: + assert not is_url_in_cache(testurl) + with pytest.raises(KeyError): + export_download_cache(zip_file, [testurl]) + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_import_one(tmp_path, temp_cache, valid_urls): + testurl, testurl_contents = next(valid_urls) + testurl2, testurl2_contents = next(valid_urls) + zip_file_name = tmp_path / "the.zip" + + download_file(testurl, cache=True) + download_file(testurl2, cache=True) + assert is_url_in_cache(testurl2) + export_download_cache(zip_file_name, [testurl, testurl2]) + clear_download_cache(testurl) + clear_download_cache(testurl2) + import_download_cache(zip_file_name, [testurl]) + assert is_url_in_cache(testurl) + assert not is_url_in_cache(testurl2) + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_export_import_roundtrip(tmp_path, temp_cache, valid_urls): + zip_file_name = tmp_path / "the.zip" + for u, _ in islice(valid_urls, FEW): + download_file(u, cache=True) + + initial_urls_in_cache = set(get_cached_urls()) + + export_download_cache(zip_file_name) + clear_download_cache() + import_download_cache(zip_file_name) + + assert set(get_cached_urls()) == initial_urls_in_cache + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_export_import_roundtrip_stream(temp_cache, valid_urls): + for u, _ in islice(valid_urls, FEW): + download_file(u, cache=True) + initial_urls_in_cache = set(get_cached_urls()) + + with io.BytesIO() as f: + export_download_cache(f) + b = f.getvalue() + clear_download_cache() + with io.BytesIO(b) as f: + import_download_cache(f) + + assert set(get_cached_urls()) == initial_urls_in_cache + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_export_overwrite_flag_works(temp_cache, valid_urls, tmp_path): + fn = tmp_path / "f.zip" + c = b"Some contents\nto check later" + with open(fn, "wb") as f: + f.write(c) + for u, _ in islice(valid_urls, FEW): + download_file(u, cache=True) + + with pytest.raises(FileExistsError): + export_download_cache(fn) + assert get_file_contents(fn, encoding="binary") == c + + export_download_cache(fn, overwrite=True) + assert get_file_contents(fn, encoding="binary") != c + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_export_import_roundtrip_different_location(tmp_path, valid_urls): + original_cache = tmp_path / "original" + original_cache.mkdir() + zip_file_name = tmp_path / "the.zip" + + urls = list(islice(valid_urls, FEW)) + initial_urls_in_cache = {u for (u, c) in urls} + with paths.set_temp_cache(original_cache): + for u, _ in urls: + download_file(u, cache=True) + assert set(get_cached_urls()) == initial_urls_in_cache + export_download_cache(zip_file_name) + + new_cache = tmp_path / "new" + new_cache.mkdir() + with paths.set_temp_cache(new_cache): + import_download_cache(zip_file_name) + check_download_cache() + assert set(get_cached_urls()) == initial_urls_in_cache + for u, c in urls: + assert get_file_contents(download_file(u, cache=True)) == c + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_cache_size_is_zero_when_empty(temp_cache): + assert not get_cached_urls() + assert cache_total_size() == 0 + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_cache_size_changes_correctly_when_files_are_added_and_removed( + temp_cache, valid_urls +): + u, c = next(valid_urls) + clear_download_cache(u, on_missing="ignore") + s_i = cache_total_size() + download_file(u, cache=True) + assert cache_total_size() == s_i + len(c) + len(u.encode("utf-8")) + clear_download_cache(u) + assert cache_total_size() == s_i + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_cache_contents_agrees_with_get_urls(temp_cache, valid_urls): + r = [] + for a, a_c in islice(valid_urls, FEW): + a_f = download_file(a, cache=True) + r.append((a, a_c, a_f)) + assert set(cache_contents().keys()) == set(get_cached_urls()) + for u, _, h in r: + assert cache_contents()[u] == h + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +@pytest.mark.parametrize("desired_size", [1_000_000_000_000_000_000, 1 * _u.Ebyte]) +def test_free_space_checker_huge(tmp_path, desired_size): + with pytest.raises(OSError): + check_free_space_in_dir(tmp_path, desired_size) + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_get_free_space_file_directory(tmp_path): + fn = tmp_path / "file" + with open(fn, "w"): + pass + with pytest.raises(OSError): + get_free_space_in_dir(fn) + + free_space = get_free_space_in_dir(tmp_path) + assert free_space > 0 and not hasattr(free_space, "unit") + + # TODO: If unit=True starts to auto-guess prefix, this needs updating. + free_space = get_free_space_in_dir(tmp_path, unit=True) + assert free_space > 0 and free_space.unit == _u.byte + + free_space = get_free_space_in_dir(tmp_path, unit=_u.Mbit) + assert free_space > 0 and free_space.unit == _u.Mbit + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_download_file_bogus_settings(invalid_urls, temp_cache): + u = next(invalid_urls) + with pytest.raises(KeyError): + download_file(u, sources=[]) + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_download_file_local_directory(tmp_path): + """Make sure we get a URLError rather than OSError even if it's a + local directory.""" + with pytest.raises(urllib.request.URLError): + download_file(url_to(tmp_path)) + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_download_file_schedules_deletion(valid_urls): + u, c = next(valid_urls) + f = download_file(u) + assert Path(f) in _tempfilestodel + # how to test deletion actually occurs? + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_clear_download_cache_refuses_to_delete_outside_the_cache(tmp_path, temp_cache): + fn = str(tmp_path / "file") + with open(fn, "w") as f: + f.write("content") + assert os.path.exists(fn) + + with pytest.raises( + RuntimeError, + match="attempted to use clear_download_cache on the path", + ): + clear_download_cache(fn) + assert os.path.exists(fn) + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_check_download_cache_finds_bogus_entries(temp_cache, valid_urls): + u, c = next(valid_urls) + download_file(u, cache=True) + dldir = _get_download_cache_loc() + bf = dldir.joinpath("bogus").absolute() + with open(bf, "w") as f: + f.write("bogus file that exists") + with pytest.raises(CacheDamaged) as e: + check_download_cache() + assert bf in e.value.bad_files + clear_download_cache() + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_check_download_cache_finds_bogus_subentries(temp_cache, valid_urls): + u, c = next(valid_urls) + f = Path(download_file(u, cache=True)) + bf = f.parent.joinpath("bogus").absolute() + bf.write_text("bogus file that exists") + with pytest.raises(CacheDamaged) as e: + check_download_cache() + assert bf in e.value.bad_files + clear_download_cache() + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_check_download_cache_cleanup(temp_cache, valid_urls): + u, c = next(valid_urls) + fn = download_file(u, cache=True) + dldir = _get_download_cache_loc() + + bf1 = dldir.joinpath("bogus1").absolute() + bf1.write_text("bogus file that exists") + + bf2 = Path(fn).parent.joinpath("bogus2").absolute() + bf2.write_text("other bogus file that exists") + + bf3 = dldir.joinpath("contents").absolute() + bf3.write_text("awkwardly-named bogus file that exists") + + f2 = Path(download_file(u, cache=True)) + f2.unlink() + bf4 = f2.parent + + with pytest.raises(CacheDamaged) as e: + check_download_cache() + assert set(e.value.bad_files) == {bf1, bf2, bf3, bf4} + for bf in e.value.bad_files: + clear_download_cache(str(bf)) + # download cache will be checked on exit + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_download_cache_update_doesnt_damage_cache(temp_cache, valid_urls): + u, _ = next(valid_urls) + download_file(u, cache=True) + download_file(u, cache="update") + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_cache_dir_is_actually_a_file(tmp_path: Path, valid_urls): + """Ensure that bogus cache settings are handled sensibly. + + Because the user can specify the cache location in a config file, and + because they might try to deduce the location by looking around at what's + in their directory tree, and because the cache directory is actual several + tree levels down from the directory set in the config file, it's important + to check what happens if any of the steps in the path is wrong somehow. + """ + + def check_quietly_ignores_bogus_cache(expect_file: bool = False) -> None: + """We want a broken cache to produce a warning but then astropy should + act like there isn't a cache. + """ + if expect_file: + ctx = pytest.raises(OSError) + ctx2 = pytest.warns(CacheMissingWarning) + ctx3 = contextlib.nullcontext() + ctx4 = ctx + else: + ctx = pytest.warns(CacheMissingWarning) + ctx2 = contextlib.nullcontext() + ctx3 = ctx + ctx4 = contextlib.nullcontext() + + with ctx: + assert not get_cached_urls() + with ctx: + assert not is_url_in_cache("http://www.example.com/") + with ctx: + assert not cache_contents() + with ctx3: + u, c = next(valid_urls) + with ctx2: + r = download_file(u, cache=True) + assert get_file_contents(r) == c + # check the filename r appears in a warning message? + # check r is added to the delete_at_exit list? + # in fact should there be testing of the delete_at_exit mechanism, + # as far as that is possible? + with ctx: + assert not is_url_in_cache(u) + with ctx4: + check_download_cache() + + dldir = _get_download_cache_loc(ensure_exists=True) + # set_temp_cache acts weird if it is pointed at a file (see below) + # but we want to see what happens when the cache is pointed + # at a file instead of a directory, so make a directory we can + # replace later. + fn = tmp_path / "file" + fn.mkdir() + ct = "contents\n" + with paths.set_temp_cache(fn): + shutil.rmtree(fn) + fn.write_text(ct) + with pytest.raises(OSError): + paths.get_cache_dir() + check_quietly_ignores_bogus_cache() + assert dldir == _get_download_cache_loc() + assert fn.read_text() == ct, "File should not be harmed." + + # See what happens when set_temp_cache is pointed at a file + with pytest.raises(Exception) as _: + with paths.set_temp_cache(fn): + pass + assert dldir == _get_download_cache_loc() + assert fn.read_text() == ct + + # Now the cache directory is normal but the subdirectory it wants + # to make is a file + cd = tmp_path / "astropy" + cd.write_text(ct) + with paths.set_temp_cache(tmp_path): + check_quietly_ignores_bogus_cache() + assert dldir == _get_download_cache_loc() + assert cd.read_text() == ct + cd.unlink() + + # Ditto one level deeper + cd.mkdir(parents=True) + cd /= "download" + cd.write_text(ct) + with paths.set_temp_cache(tmp_path): + check_quietly_ignores_bogus_cache() + assert dldir == _get_download_cache_loc() + assert cd.read_text() == ct + cd.unlink() + + # Ditto another level deeper + cd.mkdir(parents=True) + cd /= "url" + cd.write_text(ct) + with paths.set_temp_cache(tmp_path): + check_quietly_ignores_bogus_cache(expect_file=True) + assert dldir == _get_download_cache_loc() + assert cd.read_text() == ct + cd.unlink() + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_get_fileobj_str(a_file): + fn, c = a_file + with get_readable_fileobj(str(fn)) as rf: + assert rf.read() == c + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_get_fileobj_pathlib(a_file): + fn, c = a_file + with get_readable_fileobj(pathlib.Path(fn)) as rf: + assert rf.read() == c + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_get_fileobj_binary(a_binary_file): + fn, c = a_binary_file + with get_readable_fileobj(fn, encoding="binary") as rf: + assert rf.read() == c + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_get_fileobj_already_open_text(a_file): + fn, c = a_file + with open(fn) as f: + with get_readable_fileobj(f) as rf: + with pytest.raises(TypeError): + rf.read() + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_get_fileobj_already_open_binary(a_file): + fn, c = a_file + with open(fn, "rb") as f: + with get_readable_fileobj(f) as rf: + assert rf.read() == c + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_get_fileobj_binary_already_open_binary(a_binary_file): + fn, c = a_binary_file + with open(fn, "rb") as f: + with get_readable_fileobj(f, encoding="binary") as rf: + assert rf.read() == c + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_cache_contents_not_writable(temp_cache, valid_urls): + c = cache_contents() + with pytest.raises(TypeError): + c["foo"] = 7 + u, _ = next(valid_urls) + download_file(u, cache=True) + c = cache_contents() + assert u in c + with pytest.raises(TypeError): + c["foo"] = 7 + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_cache_relocatable(tmp_path, valid_urls): + u, c = next(valid_urls) + d1 = tmp_path / "1" + d2 = tmp_path / "2" + os.mkdir(d1) + with paths.set_temp_cache(d1): + p1 = download_file(u, cache=True) + assert is_url_in_cache(u) + assert get_file_contents(p1) == c + shutil.copytree(d1, d2) + clear_download_cache() + with paths.set_temp_cache(d2): + assert is_url_in_cache(u) + p2 = download_file(u, cache=True) + assert p1 != p2 + assert os.path.exists(p2) + clear_download_cache(p2) + check_download_cache() + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_get_readable_fileobj_cleans_up_temporary_files(tmp_path, monkeypatch): + """checks that get_readable_fileobj leaves no temporary files behind""" + # Create a 'file://' URL pointing to a path on the local filesystem + url = url_to(TESTLOCAL) + + # Save temporary files to a known location + monkeypatch.setattr(tempfile, "tempdir", str(tmp_path)) + + # Call get_readable_fileobj() as a context manager + with get_readable_fileobj(url) as f: + f.read() + + # Get listing of files in temporary directory + tempdir_listing = list(tmp_path.iterdir()) + + # Assert that the temporary file was empty after get_readable_fileobj() + # context manager finished running + assert len(tempdir_listing) == 0 + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_path_objects_get_readable_fileobj(): + fpath = pathlib.Path(TESTLOCAL) + with get_readable_fileobj(fpath) as f: + assert ( + f.read().rstrip() + == "This file is used in the test_local_data_* testing functions\nCONTENT" + ) + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_nested_get_readable_fileobj(): + """Ensure fileobj state is as expected when get_readable_fileobj() + is called inside another get_readable_fileobj(). + """ + with get_readable_fileobj(TESTLOCAL, encoding="binary") as fileobj: + with get_readable_fileobj(fileobj, encoding="UTF-8") as fileobj2: + fileobj2.seek(1) + fileobj.seek(1) + + # Theoretically, fileobj2 should be closed already here but it is not. + # See https://github.com/astropy/astropy/pull/8675. + # UNCOMMENT THIS WHEN PYTHON FINALLY LETS IT HAPPEN. + # assert fileobj2.closed + + assert fileobj.closed and fileobj2.closed + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_download_file_wrong_size(monkeypatch): + @contextlib.contextmanager + def mockurl(remote_url, timeout=None): + yield MockURL() + + def mockurl_builder(*args, tlscontext=None, **kwargs): + mock_opener = type("MockOpener", (object,), {})() + mock_opener.open = mockurl + return mock_opener + + class MockURL: + def __init__(self): + self.reader = io.BytesIO(b"a" * real_length) + + def info(self): + return {"Content-Length": str(report_length)} + + def read(self, length=None): + return self.reader.read(length) + + monkeypatch.setattr(astropy.utils.data, "_build_urlopener", mockurl_builder) + + with pytest.raises(urllib.error.ContentTooShortError): + report_length = 1024 + real_length = 1023 + download_file(TESTURL, cache=False) + + with pytest.raises(urllib.error.URLError): + report_length = 1023 + real_length = 1024 + download_file(TESTURL, cache=False) + + report_length = 1023 + real_length = 1023 + fn = download_file(TESTURL, cache=False) + with open(fn, "rb") as f: + assert f.read() == b"a" * real_length + + report_length = None + real_length = 1023 + fn = download_file(TESTURL, cache=False) + with open(fn, "rb") as f: + assert f.read() == b"a" * real_length + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_can_make_directories_readonly(tmp_path): + try: + with readonly_dir(tmp_path): + assert is_dir_readonly(tmp_path) + except AssertionError: + if hasattr(os, "geteuid") and os.geteuid() == 0: + pytest.skip( + "We are root, we can't make a directory un-writable with chmod." + ) + elif platform.system() == "Windows": + pytest.skip( + "It seems we can't make a directory un-writable under Windows " + "with chmod, in spite of the documentation." + ) + else: + raise + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_can_make_files_readonly(tmp_path): + fn = tmp_path / "test" + c = "contents\n" + with open(fn, "w") as f: + f.write(c) + with readonly_dir(tmp_path): + try: + with open(fn, "w+") as f: + f.write("more contents\n") + except PermissionError: + pass + else: + if hasattr(os, "geteuid") and os.geteuid() == 0: + pytest.skip("We are root, we can't make a file un-writable with chmod.") + assert get_file_contents(fn) == c + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_read_cache_readonly(readonly_cache): + assert cache_contents() == readonly_cache + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_download_file_cache_readonly(readonly_cache): + for u in readonly_cache: + f = download_file(u, cache=True) + assert f == readonly_cache[u] + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_import_file_cache_readonly(readonly_cache, tmp_path): + filename = tmp_path / "test-file" + content = "Some text or other" + url = "http://example.com/" + with open(filename, "w") as f: + f.write(content) + + with pytest.raises(OSError): + import_file_to_cache(url, filename, remove_original=True) + assert not is_url_in_cache(url) + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_import_file_cache_invalid_cross_device_link(tmp_path, monkeypatch): + def no_rename(path, mode=None): + if os.path.exists(path): + raise OSError(errno.EXDEV, "os.rename monkeypatched out") + else: + raise FileNotFoundError(f"File {path} does not exist.") + + monkeypatch.setattr(os, "rename", no_rename) + + filename = tmp_path / "test-file" + content = "Some text or other" + url = "http://example.com/" + with open(filename, "w") as f: + f.write(content) + + with pytest.warns(AstropyWarning, match="os.rename monkeypatched out"): + import_file_to_cache(url, filename, remove_original=True, replace=True) + assert is_url_in_cache(url) + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_download_file_cache_readonly_cache_miss(readonly_cache, valid_urls): + u, c = next(valid_urls) + with pytest.warns(CacheMissingWarning): + f = download_file(u, cache=True) + assert get_file_contents(f) == c + assert not is_url_in_cache(u) + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_download_file_cache_readonly_update(readonly_cache): + for u in readonly_cache: + with pytest.warns(CacheMissingWarning): + f = download_file(u, cache="update") + assert f != readonly_cache[u] + assert compute_hash(f) == compute_hash(readonly_cache[u]) + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_check_download_cache_works_if_readonly(readonly_cache): + check_download_cache() + + +# On Windows I can't make directories readonly. On CircleCI I can't make +# anything readonly because the test suite runs as root. So on those platforms +# none of the "real" tests above can be run. I can use monkeypatch to trigger +# the readonly code paths, see the "fake" versions of the tests below, but I +# don't totally trust those to completely explore what happens either, so we +# have both. I couldn't see an easy way to parameterize over fixtures and share +# tests. + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_read_cache_fake_readonly(fake_readonly_cache): + assert cache_contents() == fake_readonly_cache + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_download_file_cache_fake_readonly(fake_readonly_cache): + for u in fake_readonly_cache: + f = download_file(u, cache=True) + assert f == fake_readonly_cache[u] + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_mkdtemp_cache_fake_readonly(fake_readonly_cache): + with pytest.raises(OSError): + tempfile.mkdtemp() + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_TD_cache_fake_readonly(fake_readonly_cache): + with pytest.raises(OSError): + with TemporaryDirectory(): + pass + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_import_file_cache_fake_readonly(fake_readonly_cache, tmp_path): + filename = tmp_path / "test-file" + content = "Some text or other" + url = "http://example.com/" + with open(filename, "w") as f: + f.write(content) + + with pytest.raises(OSError): + import_file_to_cache(url, filename, remove_original=True) + assert not is_url_in_cache(url) + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_download_file_cache_fake_readonly_cache_miss(fake_readonly_cache, valid_urls): + u, c = next(valid_urls) + with pytest.warns(CacheMissingWarning): + f = download_file(u, cache=True) + assert not is_url_in_cache(u) + assert get_file_contents(f) == c + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_download_file_cache_fake_readonly_update(fake_readonly_cache): + for u in fake_readonly_cache: + with pytest.warns(CacheMissingWarning): + f = download_file(u, cache="update") + assert f != fake_readonly_cache[u] + assert compute_hash(f) == compute_hash(fake_readonly_cache[u]) + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_check_download_cache_works_if_fake_readonly(fake_readonly_cache): + check_download_cache() + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +@pytest.mark.usefixtures("ignore_config_paths_global_state") +def test_pkgname_isolation(temp_cache, valid_urls): + a = str(uuid4()) + + assert not get_cached_urls() + with pytest.warns(CacheMissingWarning): + a_urls = get_cached_urls(pkgname=a) + assert not a_urls + + for u, _ in islice(valid_urls, FEW): + download_file(u, cache=True, pkgname=a) + assert not get_cached_urls() + assert len(get_cached_urls(pkgname=a)) == FEW + assert cache_total_size() < cache_total_size(pkgname=a) + + for u, _ in islice(valid_urls, FEW + 1): + download_file(u, cache=True) + assert len(get_cached_urls()) == FEW + 1 + assert len(get_cached_urls(pkgname=a)) == FEW + assert cache_total_size() > cache_total_size(pkgname=a) + + assert set(get_cached_urls()) == set(cache_contents().keys()) + assert set(get_cached_urls(pkgname=a)) == set(cache_contents(pkgname=a).keys()) + for i in get_cached_urls(): + assert is_url_in_cache(i) + assert not is_url_in_cache(i, pkgname=a) + for i in get_cached_urls(pkgname=a): + assert not is_url_in_cache(i) + assert is_url_in_cache(i, pkgname=a) + + # FIXME: need to break a cache to test whether we check the right one + check_download_cache() + check_download_cache(pkgname=a) + + # FIXME: check that cache='update' works + + u = get_cached_urls()[0] + with pytest.raises(KeyError): + download_file(u, cache=True, sources=[], pkgname=a) + clear_download_cache(u, pkgname=a) + assert len(get_cached_urls()) == FEW + 1, "wrong pkgname should do nothing" + assert len(get_cached_urls(pkgname=a)) == FEW, "wrong pkgname should do nothing" + + f = download_file(u, sources=[], cache=True) + with pytest.raises(RuntimeError): + clear_download_cache(f, pkgname=a) + + ua = get_cached_urls(pkgname=a)[0] + with pytest.raises(KeyError): + download_file(ua, cache=True, sources=[]) + + fa = download_file(ua, sources=[], cache=True, pkgname=a) + with pytest.raises(RuntimeError): + clear_download_cache(fa) + + clear_download_cache(ua, pkgname=a) + assert len(get_cached_urls()) == FEW + 1 + assert len(get_cached_urls(pkgname=a)) == FEW - 1 + + clear_download_cache(u) + assert len(get_cached_urls()) == FEW + assert len(get_cached_urls(pkgname=a)) == FEW - 1 + + clear_download_cache(pkgname=a) + assert len(get_cached_urls()) == FEW + with pytest.warns(CacheMissingWarning): + a_urls = get_cached_urls(pkgname=a) + assert not a_urls + + clear_download_cache() + with pytest.warns(CacheMissingWarning): + urls = get_cached_urls() + assert not urls + with pytest.warns(CacheMissingWarning): + a_urls = get_cached_urls(pkgname=a) + assert not a_urls + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +@pytest.mark.usefixtures("ignore_config_paths_global_state") +def test_transport_cache_via_zip(temp_cache, valid_urls): + a = str(uuid4()) + + assert not get_cached_urls() + with pytest.warns(CacheMissingWarning): + a_urls = get_cached_urls(pkgname=a) + assert not a_urls + + for u, _ in islice(valid_urls, FEW): + download_file(u, cache=True) + + with io.BytesIO() as f: + export_download_cache(f) + b = f.getvalue() + with io.BytesIO(b) as f: + import_download_cache(f, pkgname=a) + + check_download_cache() + check_download_cache(pkgname=a) + + assert set(get_cached_urls()) == set(get_cached_urls(pkgname=a)) + cca = cache_contents(pkgname=a) + for k, v in cache_contents().items(): + assert v != cca[k] + assert get_file_contents(v) == get_file_contents(cca[k]) + clear_download_cache() + + with io.BytesIO() as f: + export_download_cache(f, pkgname=a) + b = f.getvalue() + with io.BytesIO(b) as f: + import_download_cache(f) + + assert set(get_cached_urls()) == set(get_cached_urls(pkgname=a)) + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_download_parallel_respects_pkgname(temp_cache, valid_urls): + a = str(uuid4()) + + assert not get_cached_urls() + with pytest.warns(CacheMissingWarning): + a_urls = get_cached_urls(pkgname=a) + assert not a_urls + + download_files_in_parallel([u for (u, c) in islice(valid_urls, FEW)], pkgname=a) + assert not get_cached_urls() + assert len(get_cached_urls(pkgname=a)) == FEW + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +@pytest.mark.skipif( + not CAN_RENAME_DIRECTORY_IN_USE, + reason="This platform is unable to rename directories that are in use.", +) +def test_removal_of_open_files(temp_cache, valid_urls): + u, c = next(valid_urls) + with open(download_file(u, cache=True)): + clear_download_cache(u) + assert not is_url_in_cache(u) + check_download_cache() + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +@pytest.mark.skipif( + not CAN_RENAME_DIRECTORY_IN_USE, + reason="This platform is unable to rename directories that are in use.", +) +def test_update_of_open_files(temp_cache, valid_urls): + u, c = next(valid_urls) + with open(download_file(u, cache=True)): + u2, c2 = next(valid_urls) + f = download_file(u, cache="update", sources=[u2]) + check_download_cache() + assert is_url_in_cache(u) + assert get_file_contents(f) == c2 + assert is_url_in_cache(u) + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_removal_of_open_files_windows(temp_cache, valid_urls, monkeypatch): + def no_rmtree(*args, **kwargs): + warnings.warn(CacheMissingWarning("in use")) + raise PermissionError + + if CAN_RENAME_DIRECTORY_IN_USE: + # This platform is able to remove files while in use. + monkeypatch.setattr(astropy.utils.data, "_rmtree", no_rmtree) + + u, c = next(valid_urls) + with open(download_file(u, cache=True)): + with ( + pytest.warns(CacheMissingWarning, match=".*in use.*"), + pytest.warns(CacheMissingWarning, match=".*PermissionError.*"), + ): + clear_download_cache(u) + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_update_of_open_files_windows(temp_cache, valid_urls, monkeypatch): + def no_rmtree(*args, **kwargs): + warnings.warn(CacheMissingWarning("in use")) + raise PermissionError + + if CAN_RENAME_DIRECTORY_IN_USE: + # This platform is able to remove files while in use. + monkeypatch.setattr(astropy.utils.data, "_rmtree", no_rmtree) + + u, c = next(valid_urls) + with open(download_file(u, cache=True)): + u2, c2 = next(valid_urls) + with ( + pytest.warns(CacheMissingWarning, match=".*in use.*"), + pytest.warns(CacheMissingWarning, match=".*read-only.*"), + ): + f = download_file(u, cache="update", sources=[u2]) + check_download_cache() + assert is_url_in_cache(u) + assert get_file_contents(f) == c2 + assert get_file_contents(download_file(u, cache=True, sources=[])) == c + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_no_allow_internet(temp_cache, valid_urls): + u, c = next(valid_urls) + with conf.set_temp("allow_internet", False): + with pytest.raises(urllib.error.URLError): + download_file(u) + assert not is_url_in_cache(u) + with pytest.raises(urllib.error.URLError): + # This will trigger the remote data error if it's allowed to touch the internet + download_file(TESTURL) + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_clear_download_cache_not_too_aggressive(temp_cache, valid_urls): + u, c = next(valid_urls) + download_file(u, cache=True) + dldir = _get_download_cache_loc() + + bad_filename = os.path.join(dldir, "contents") + assert is_url_in_cache(u) + clear_download_cache(bad_filename) + assert is_url_in_cache(u) + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_clear_download_cache_variants(temp_cache, valid_urls): + # deletion by contents filename + u, c = next(valid_urls) + f = download_file(u, cache=True) + clear_download_cache(f) + assert not is_url_in_cache(u) + + # deletion by url filename + u, c = next(valid_urls) + f = download_file(u, cache=True) + clear_download_cache(os.path.join(os.path.dirname(f), "url")) + assert not is_url_in_cache(u) + + # deletion by hash directory name + u, c = next(valid_urls) + f = download_file(u, cache=True) + clear_download_cache(os.path.dirname(f)) + assert not is_url_in_cache(u) + + # deletion by directory name with trailing slash + u, c = next(valid_urls) + f = download_file(u, cache=True) + clear_download_cache(os.path.dirname(f) + "/") + assert not is_url_in_cache(u) + + # deletion by hash of file contents + u, c = next(valid_urls) + f = download_file(u, cache=True) + h = compute_hash(f) + clear_download_cache(h) + assert not is_url_in_cache(u) + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_clear_download_cache_invalid_cross_device_link( + temp_cache, valid_urls, monkeypatch +): + def no_rename(path, mode=None): + raise OSError(errno.EXDEV, "os.rename monkeypatched out") + + u, c = next(valid_urls) + download_file(u, cache=True) + + monkeypatch.setattr(os, "rename", no_rename) + + assert is_url_in_cache(u) + with pytest.warns(AstropyWarning, match="os.rename monkeypatched out"): + clear_download_cache(u) + assert not is_url_in_cache(u) + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_clear_download_cache_raises_os_error(temp_cache, valid_urls, monkeypatch): + def no_rename(path, mode=None): + raise OSError(errno.EBUSY, "os.rename monkeypatched out") + + u, c = next(valid_urls) + download_file(u, cache=True) + + monkeypatch.setattr(os, "rename", no_rename) + + assert is_url_in_cache(u) + with pytest.warns(CacheMissingWarning, match="os.rename monkeypatched out"): + clear_download_cache(u) + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +@pytest.mark.skipif( + CI and not IS_CRON, + reason="Flaky/too much external traffic for regular CI", +) +@pytest.mark.remote_data +def test_ftp_tls_auto(temp_cache): + """Test that download automatically enables TLS/SSL when required""" + + url = "ftp://anonymous:mail%40astropy.org@gdc.cddis.eosdis.nasa.gov/pub/products/iers/finals2000A.daily" + download_file(url) + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +@pytest.mark.parametrize("base", ["http://example.com", "https://example.com"]) +def test_url_trailing_slash(temp_cache, valid_urls, base): + slash = base + "/" + no_slash = base + + u, c = next(valid_urls) + + download_file(slash, cache=True, sources=[u]) + + assert is_url_in_cache(no_slash) + download_file(no_slash, cache=True, sources=[]) + clear_download_cache(no_slash) + assert not is_url_in_cache(no_slash) + assert not is_url_in_cache(slash) + + download_file(no_slash, cache=True, sources=[u]) + # see if implicit check_download_cache squawks + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +def test_empty_url(temp_cache, valid_urls): + u, c = next(valid_urls) + download_file("file://", cache=True, sources=[u]) + assert not is_url_in_cache("file:///") + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +@pytest.mark.remote_data +def test_download_ftp_file_properly_handles_socket_error(): + faulty_url = "ftp://anonymous:mail%40astropy.org@nonexisting/pub/products/iers/finals2000A.all" + with pytest.raises(urllib.error.URLError) as excinfo: + download_file(faulty_url) + errmsg = excinfo.exconly() + found_msg = False + possible_msgs = [ + "Name or service not known", + "nodename nor servname provided, or not known", + "getaddrinfo failed", + "Temporary failure in name resolution", + "No address associated with hostname", + ] + for cur_msg in possible_msgs: + if cur_msg in errmsg: + found_msg = True + break + assert found_msg, f"Got {errmsg}, expected one of these: {','.join(possible_msgs)}" + + +@pytest.mark.filterwarnings("ignore:unclosed:ResourceWarning") +@pytest.mark.parametrize( + ("s", "ans"), + [ + ("http://googlecom", True), + ("https://google.com", True), + ("ftp://google.com", True), + ("sftp://google.com", True), + ("ssh://google.com", True), + ("file:///c:/path/to/the%20file.txt", True), + ("google.com", False), + ("C:\\\\path\\\\file.docx", False), + ("data://file", False), + ], +) +def test_string_is_url_check(s, ans): + assert is_url(s) is ans + + +@pytest.mark.remote_data +@pytest.mark.parametrize( + "filesystem_kwargs", + [ + None, + dict(protocol="s3", anon=True, block_size=1_000, cache_type="bytes"), + ], +) +def test_all_get_readable_fileobj_fsspec(filesystem_kwargs): + fsspec = pytest.importorskip("fsspec") + + s3_uri = "s3://stpubdata/hst/public/j8pu/j8pu0y010/j8pu0y010_drc.fits" + + fsspec_kwargs = {"anon": True} + + if filesystem_kwargs: + # pass in a user-initialized filesystem: + filesystem = fsspec.filesystem(**filesystem_kwargs) + else: + # let get_readable_fileobj construct the filesystem: + filesystem = None + + # this example calls `fits.open`, but it's testing a feature that + # gets passed through to `get_readable_fileobj``. + with fits.open( + s3_uri, fsspec_filesystem=filesystem, fsspec_kwargs=fsspec_kwargs + ) as hdulist: + # assert fileobj == hdulist + cutout = hdulist[1].section[:2, :2] + + # check that cutout is retrieved correctly: + assert cutout.shape == (2, 2) + assert cutout.dtype == "float32" diff --git a/astropy/utils/tests/test_data_info.py b/astropy/utils/tests/test_data_info.py new file mode 100644 index 000000000000..521133ed95d1 --- /dev/null +++ b/astropy/utils/tests/test_data_info.py @@ -0,0 +1,108 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +import numpy as np +import pytest + +import astropy.units as u +from astropy.coordinates import SkyCoord +from astropy.table import QTable +from astropy.table.index import SlicedIndex +from astropy.time import Time +from astropy.utils.data_info import dtype_info_name + +STRING_TYPE_NAMES = {(True, "S"): "bytes", (True, "U"): "str"} + +DTYPE_TESTS = ( + (np.array(b"abcd").dtype, STRING_TYPE_NAMES[(True, "S")] + "4"), + (np.array("abcd").dtype, STRING_TYPE_NAMES[(True, "U")] + "4"), + ("S4", STRING_TYPE_NAMES[(True, "S")] + "4"), + ("U4", STRING_TYPE_NAMES[(True, "U")] + "4"), + (np.void, "void"), + (np.int32, "int32"), + (bool, "bool"), + (float, "float64"), + ("" in out + assert "b>" in out + + +def test_diff_types(): + """ + Regression test for https://github.com/astropy/astropy/issues/4122 + """ + f = io.StringIO() + a = 1.0 + b = "1.0" + identical = report_diff_values(a, b, fileobj=f) + assert not identical + assert f.getvalue().splitlines() == [ + " (float) a> 1.0", + " (str) b> '1.0'", + " ? + +", + ] + + +def test_diff_numeric_scalar_types(): + """Test comparison of different numeric scalar types.""" + f = io.StringIO() + assert not report_diff_values(1.0, 1, fileobj=f) + out = f.getvalue() + assert out == " (float) a> 1.0\n (int) b> 1\n" + + +def test_array_comparison(): + """ + Test diff-ing two arrays. + """ + f = io.StringIO() + a = np.arange(9).reshape(3, 3) + b = a + 1 + identical = report_diff_values(a, b, fileobj=f) + assert not identical + out = f.getvalue() + assert ( + out == " at [0, 0]:\n" + " a> 0\n" + " b> 1\n" + " at [0, 1]:\n" + " a> 1\n" + " b> 2\n" + " at [0, 2]:\n" + " a> 2\n" + " b> 3\n" + " ...and at 6 more indices.\n" + ) + + +def test_diff_shaped_array_comparison(): + """ + Test diff-ing two differently shaped arrays. + """ + f = io.StringIO() + a = np.empty((1, 2, 3)) + identical = report_diff_values(a, a[0], fileobj=f) + assert not identical + out = f.getvalue() + assert ( + out + == " Different array shapes:\n a> (1, 2, 3)\n ? ---\n b> (2, 3)\n" + ) + + +def test_tablediff(): + """ + Test diff-ing two simple Table objects. + """ + a = Table.read( + """name obs_date mag_b mag_v +M31 2012-01-02 17.0 16.0 +M82 2012-10-29 16.2 15.2 +M101 2012-10-31 15.1 15.5""", + format="ascii", + ) + b = Table.read( + """name obs_date mag_b mag_v +M31 2012-01-02 17.0 16.5 +M82 2012-10-29 16.2 15.2 +M101 2012-10-30 15.1 15.5 +NEW 2018-05-08 nan 9.0""", + format="ascii", + ) + f = io.StringIO() + identical = report_diff_values(a, b, fileobj=f) + assert not identical + out = f.getvalue() + assert ( + out == " name obs_date mag_b mag_v\n" + " ---- ---------- ----- -----\n" + " a> M31 2012-01-02 17.0 16.0\n" + " ? ^\n" + " b> M31 2012-01-02 17.0 16.5\n" + " ? ^\n" + " M82 2012-10-29 16.2 15.2\n" + " a> M101 2012-10-31 15.1 15.5\n" + " ? ^\n" + " b> M101 2012-10-30 15.1 15.5\n" + " ? ^\n" + " b> NEW 2018-05-08 nan 9.0\n" + ) + + # Identical + assert report_diff_values(a, a, fileobj=f) + + +def test_large_table_diff(): + # see https://github.com/astropy/astropy/issues/14010 + colnames = [f"column{i}" for i in range(100)] + t1 = Table(names=colnames) + + colnames.insert(50, "test") + t2 = Table(names=colnames) + + assert not report_diff_values(t1, t2, fileobj=io.StringIO()) + + +@pytest.mark.parametrize("kwargs", [{}, {"atol": 0, "rtol": 0}]) +def test_where_not_allclose(kwargs): + a = np.array([1, np.nan, np.inf, 4.5]) + b = np.array([1, np.inf, np.nan, 4.6]) + + assert where_not_allclose(a, b, **kwargs) == ([3],) + assert len(where_not_allclose(a, a, **kwargs)[0]) == 0 + + diff, maxabs, maxrel = where_not_allclose(a, b, return_maxdiff=True, **kwargs) + assert np.isclose(maxabs, 0.1) + assert np.isclose(maxrel, 0.1 / 4.6) + + a = np.array([np.nan, np.inf, 0, 4.5]) + b = np.array([1, np.inf, np.nan, 4.6]) + + diff, maxabs, maxrel = where_not_allclose(a, b, return_maxdiff=True, **kwargs) + assert list(diff[0]) == [0, 2, 3] + assert np.isclose(maxabs, 0.1) + assert np.isclose(maxrel, 0.1 / 4.6) + + a = np.array([np.nan, np.inf, 0]) + b = np.array([1, np.inf, np.nan]) + + diff, maxabs, maxrel = where_not_allclose(a, b, return_maxdiff=True, **kwargs) + assert list(diff[0]) == [0, 2] + assert maxabs == 0 + assert maxrel == 0 diff --git a/astropy/utils/tests/test_gzip.py b/astropy/utils/tests/test_gzip.py deleted file mode 100644 index d9e321f16f56..000000000000 --- a/astropy/utils/tests/test_gzip.py +++ /dev/null @@ -1,18 +0,0 @@ -import io -import os - -from ...tests.helper import pytest -from ..compat import gzip - -pytestmark = pytest.mark.skipif("sys.version_info < (3,0)") - - -def test_gzip(tmpdir): - fd = gzip.GzipFile(str(tmpdir.join("test.gz")), 'wb') - fd = io.TextIOWrapper(fd, encoding='utf8') - - -def test_gzip2(tmpdir): - with gzip.GzipFile(str(tmpdir.join("test.gz")), 'wb') as fd: - pass - diff --git a/astropy/utils/tests/test_introspection.py b/astropy/utils/tests/test_introspection.py new file mode 100644 index 000000000000..97345c26ab67 --- /dev/null +++ b/astropy/utils/tests/test_introspection.py @@ -0,0 +1,154 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +# ruff: noqa: PYI024 + +# namedtuple is needed for find_mod_objs so it can have a non-local module +from collections import namedtuple +from unittest import mock + +import pytest +import yaml + +from astropy.utils import introspection +from astropy.utils.exceptions import AstropyDeprecationWarning +from astropy.utils.introspection import ( + find_current_module, + find_mod_objs, + isinstancemethod, + minversion, + resolve_name, +) + + +def test_pkg_finder(): + """ + Tests that the `find_current_module` function works. Note that + this also implicitly tests compat.misc._patched_getmodule + """ + mod1 = "astropy.utils.introspection" + mod2 = "astropy.utils.tests.test_introspection" + mod3 = "astropy.utils.tests.test_introspection" + assert find_current_module(0).__name__ == mod1 + assert find_current_module(1).__name__ == mod2 + assert find_current_module(0, True).__name__ == mod3 + + +def test_find_current_mod(): + from sys import getrecursionlimit + + thismodnm = __name__ + + assert find_current_module(0) is introspection + assert find_current_module(1).__name__ == thismodnm + assert find_current_module(getrecursionlimit() + 1) is None + + assert find_current_module(0, True).__name__ == thismodnm + assert find_current_module(0, [introspection]).__name__ == thismodnm + assert find_current_module(0, ["astropy.utils.introspection"]).__name__ == thismodnm + + with pytest.raises(ImportError): + find_current_module(0, ["faddfdsasewrweriopunjlfiurrhujnkflgwhu"]) + + +def test_find_mod_objs(): + deprecation_message = ( + "^The find_mod_objs function is deprecated and may be removed in " + r"a future version\.$" + ) + with pytest.warns(AstropyDeprecationWarning, match=deprecation_message): + lnms, fqns, objs = find_mod_objs("astropy") + + # this import is after the above call intentionally to make sure + # find_mod_objs properly imports astropy on its own + import astropy + + # just check for astropy.conf ... other things might be added, so we + # shouldn't check that it's the only thing + assert "conf" in lnms + assert astropy.conf in objs + + with pytest.warns(AstropyDeprecationWarning, match=deprecation_message): + lnms, fqns, objs = find_mod_objs(__name__, onlylocals=False) + assert "namedtuple" in lnms + assert "collections.namedtuple" in fqns + assert namedtuple in objs + + with pytest.warns(AstropyDeprecationWarning, match=deprecation_message): + lnms, fqns, objs = find_mod_objs(__name__, onlylocals=True) + assert "namedtuple" not in lnms + assert "collections.namedtuple" not in fqns + assert namedtuple not in objs + + +def test_minversion(): + import numpy as np + + good_versions = ["1.16", "1.16.1", "1.16.0.dev", "1.16dev"] + bad_versions = ["100000", "100000.2rc1"] + for version in good_versions: + assert minversion(np, version) + assert minversion("numpy", version) + for version in bad_versions: + assert not minversion(np, version) + assert not minversion("numpy", version) + + assert minversion(yaml, "3.1") + assert minversion("yaml", "3.1") + + +def test_find_current_module_bundle(): + """ + Tests that the `find_current_module` function would work if used inside + an application bundle. Since we can't test this directly, we test what + would happen if inspect.getmodule returned `None`, which is what happens + inside PyInstaller and py2app bundles. + """ + with mock.patch("inspect.getmodule", return_value=None): + mod1 = "astropy.utils.introspection" + mod2 = "astropy.utils.tests.test_introspection" + mod3 = "astropy.utils.tests.test_introspection" + assert find_current_module(0).__name__ == mod1 + assert find_current_module(1).__name__ == mod2 + assert find_current_module(0, True).__name__ == mod3 + + +def test_deprecated_isinstancemethod(): + class MetaClass(type): + def a_classmethod(cls): + pass + + class MyClass(metaclass=MetaClass): + def an_instancemethod(self): + pass + + @classmethod + def another_classmethod(cls): + pass + + @staticmethod + def a_staticmethod(): + pass + + deprecation_message = ( + "^The isinstancemethod function is deprecated and may be removed in " + r"a future version\.$" + ) + with pytest.warns(AstropyDeprecationWarning, match=deprecation_message): + assert isinstancemethod(MyClass, MyClass.a_classmethod) is False + with pytest.warns(AstropyDeprecationWarning, match=deprecation_message): + assert isinstancemethod(MyClass, MyClass.another_classmethod) is False + with pytest.warns(AstropyDeprecationWarning, match=deprecation_message): + assert isinstancemethod(MyClass, MyClass.a_staticmethod) is False + with pytest.warns(AstropyDeprecationWarning, match=deprecation_message): + assert isinstancemethod(MyClass, MyClass.an_instancemethod) is True + + +def test_resolve_name_deprecation(): + with pytest.warns( + AstropyDeprecationWarning, + match=( + "^The resolve_name function is deprecated and may be removed in a future " + r"version\.\n Use importlib \(e\.g\. importlib\.import_module for " + r"modules\) instead\.$" + ), + ): + assert resolve_name("astropy.utils.introspection") is introspection diff --git a/astropy/utils/tests/test_misc.py b/astropy/utils/tests/test_misc.py index c7599414fcca..ab067371e868 100644 --- a/astropy/utils/tests/test_misc.py +++ b/astropy/utils/tests/test_misc.py @@ -1,174 +1,163 @@ # Licensed under a 3-clause BSD style license - see LICENSE.rst -from .. import misc - -#namedtuple is needed for find_mod_objs so it can have a non-local module -from collections import namedtuple - - -def test_pkg_finder(): - """ - Tests that the `utils.misc.find_current_module` function works. Note that - this also implicitly tests compat.misc._patched_getmodule - """ - mod1 = 'astropy.utils.misc' - mod2 = 'astropy.utils.tests.test_misc' - mod3 = 'astropy.utils.tests.test_misc' - assert misc.find_current_module(0).__name__ == mod1 - assert misc.find_current_module(1).__name__ == mod2 - assert misc.find_current_module(0, True).__name__ == mod3 - - -def test_fnpickling_simple(tmpdir): - """ - Tests the `utils.misc.fnpickle` and `utils.misc.fnupickle` functions' basic - operation by pickling and unpickling a string, using both a filename and a - file. - """ - fn = str(tmpdir.join('test1.pickle')) - - obj1 = 'astring' - misc.fnpickle(obj1, fn) - res = misc.fnunpickle(fn) - assert obj1 == res - - #try without cPickle - misc.fnpickle(obj1, fn, usecPickle=False) - res = misc.fnunpickle(fn, usecPickle=False) - assert obj1 == res - - #now try with a file-like object instead of a string - with open(fn, 'wb') as f: - misc.fnpickle(obj1, f) - with open(fn, 'rb') as f: - res = misc.fnunpickle(f) - assert obj1 == res - - #same without cPickle - with open(fn, 'wb') as f: - misc.fnpickle(obj1, f, usecPickle=False) - with open(fn, 'rb') as f: - res = misc.fnunpickle(f, usecPickle=False) - assert obj1 == res - - -class ToBePickled(object): - def __init__(self, item): - self.item = item - - def __eq__(self, other): - if isinstance(other, ToBePickled): - return self.item == other.item - else: - return False - - -def test_fnpickling_class(tmpdir): - """ - Tests the `utils.misc.fnpickle` and `utils.misc.fnupickle` functions' - ability to pickle and unpickle custom classes. - """ - fn = str(tmpdir.join('test2.pickle')) - - obj1 = 'astring' - obj2 = ToBePickled(obj1) - misc.fnpickle(obj2, fn) - res = misc.fnunpickle(fn) - assert res == obj2 - - -def test_fnpickling_protocol(tmpdir): - """ - Tests the `utils.misc.fnpickle` and `utils.misc.fnupickle` functions' - ability to pickle and unpickle pickle files from all protcols. - """ - import pickle - - obj1 = 'astring' - obj2 = ToBePickled(obj1) - - for p in range(pickle.HIGHEST_PROTOCOL + 1): - fn = str(tmpdir.join('testp%i.pickle' % p)) - misc.fnpickle(obj2, fn, protocol=p) - res = misc.fnunpickle(fn) - assert res == obj2 - - -def test_fnpickling_many(tmpdir): - """ - Tests the `utils.misc.fnpickle` and `utils.misc.fnupickle` functions' - ability to pickle and unpickle multiple objects from a single file. - """ - from pytest import raises - - fn = str(tmpdir.join('test3.pickle')) - - #now try multiples - obj3 = 328.3432 - obj4 = 'blahblahfoo' - misc.fnpickle(obj3, fn) - misc.fnpickle(obj4, fn, append=True) - - res = misc.fnunpickle(fn, number=-1) - assert len(res) == 2 - assert res[0] == obj3 - assert res[1] == obj4 - - misc.fnpickle(obj4, fn, append=True) - res = misc.fnunpickle(fn, number=2) - assert len(res) == 2 - - with raises(EOFError): - misc.fnunpickle(fn, number=5) - -def test_find_mod_objs(): - lnms, fqns, objs = misc.find_mod_objs('astropy') - - # this import is after the above call intentionally to make sure - # find_mod_objs properly imports astropy on its own - import astropy - - # just check for astropy.test ... other things might be added, so we - # shouldn't check that it's the only thing - assert 'test' in lnms - assert astropy.test in objs - - lnms, fqns, objs = misc.find_mod_objs('astropy.utils.tests.test_misc', - onlylocals=False) - assert 'namedtuple' in lnms - assert 'collections.namedtuple' in fqns - assert namedtuple in objs - - lnms, fqns, objs = misc.find_mod_objs('astropy.utils.tests.test_misc', - onlylocals=True) - assert 'namedtuple' not in lnms - assert 'collections.namedtuple' not in fqns - assert namedtuple not in objs - - -def test_find_current_mod(): - from sys import getrecursionlimit - from pytest import raises - - thismodnm = __name__ - - assert misc.find_current_module(0) is misc - assert misc.find_current_module(1).__name__ == thismodnm - assert misc.find_current_module(getrecursionlimit() + 1) is None - - assert misc.find_current_module(0, True).__name__ == thismodnm - assert misc.find_current_module(0, [misc]).__name__ == thismodnm - assert misc.find_current_module(0, ['astropy.utils.misc']).__name__ == thismodnm - - with raises(ImportError): - misc.find_current_module(0, ['faddfdsasewrweriopunjlfiurrhujnkflgwhu']) - - -def test_isiterable(): - from numpy import array - - assert misc.isiterable(2) is False - assert misc.isiterable([2]) is True - assert misc.isiterable([1, 2, 3]) is True - assert misc.isiterable(array(2)) is False - assert misc.isiterable(array([1, 2, 3])) is True +import json +import locale +import urllib.error +from datetime import datetime + +import numpy as np +import pytest + +from astropy.io import fits +from astropy.tests.helper import CI +from astropy.utils import misc +from astropy.utils.exceptions import AstropyDeprecationWarning + + +@pytest.mark.parametrize( + "obj,expectation", + [ + (2, False), + ([2], True), + ([1, 2, 3], True), + (np.array(2), False), + (np.array([1, 2, 3]), True), + ], +) +def test_isiterable(obj, expectation): + with pytest.warns( + AstropyDeprecationWarning, match=r"Use numpy.iterable\(\) instead\.$" + ): + assert misc.isiterable(obj) is expectation + + +@pytest.mark.remote_data +def test_api_lookup(): + try: + strurl = misc.find_api_page("astropy.utils.misc", "dev", False, timeout=5) + objurl = misc.find_api_page(misc, "dev", False, timeout=5) + except (urllib.error.URLError, TimeoutError): + if CI: + pytest.xfail("Timed out in CI") + else: + raise + + assert strurl == objurl + assert ( + strurl + == "http://devdocs.astropy.org/utils/ref_api.html#module-astropy.utils.misc" + ) + + # Try a non-dev version + objurl = misc.find_api_page(misc, "stable", False, timeout=3) + assert ( + objurl + == "https://docs.astropy.org/en/stable/utils/ref_api.html#module-astropy.utils.misc" + ) + + +def test_JsonCustomEncoder(): + from astropy import units as u + + assert json.dumps(np.arange(3), cls=misc.JsonCustomEncoder) == "[0, 1, 2]" + assert json.dumps(1 + 2j, cls=misc.JsonCustomEncoder) == "[1.0, 2.0]" + assert json.dumps({1, 2}, cls=misc.JsonCustomEncoder) == "[1, 2]" + assert ( + json.dumps(b"hello world \xc3\x85", cls=misc.JsonCustomEncoder) + == '"hello world \\u00c5"' + ) + assert json.dumps({1: 2}, cls=misc.JsonCustomEncoder) == '{"1": 2}' # default + assert json.dumps({1: u.m}, cls=misc.JsonCustomEncoder) == '{"1": "m"}' + # Quantities + tmp = json.dumps({"a": 5 * u.cm}, cls=misc.JsonCustomEncoder) + newd = json.loads(tmp) + tmpd = {"a": {"unit": "cm", "value": 5.0}} + assert newd == tmpd + tmp2 = json.dumps({"a": np.arange(2) * u.cm}, cls=misc.JsonCustomEncoder) + newd = json.loads(tmp2) + tmpd = {"a": {"unit": "cm", "value": [0.0, 1.0]}} + assert newd == tmpd + tmp3 = json.dumps({"a": np.arange(2) * u.erg / u.s}, cls=misc.JsonCustomEncoder) + newd = json.loads(tmp3) + tmpd = {"a": {"unit": "erg / s", "value": [0.0, 1.0]}} + assert newd == tmpd + + +def test_JsonCustomEncoder_FITS_rec_from_files(): + with fits.open( + fits.util.get_testdata_filepath("variable_length_table.fits") + ) as hdul: + assert ( + json.dumps(hdul[1].data, cls=misc.JsonCustomEncoder) + == "[[[45, 56], [11, 3]], [[11, 12, 13], [12, 4]]]" + ) + + with fits.open(fits.util.get_testdata_filepath("btable.fits")) as hdul: + assert ( + json.dumps(hdul[1].data, cls=misc.JsonCustomEncoder) + == '[[1, "Sirius", -1.4500000476837158, "A1V"], ' + '[2, "Canopus", -0.7300000190734863, "F0Ib"], ' + '[3, "Rigil Kent", -0.10000000149011612, "G2V"]]' + ) + + with fits.open(fits.util.get_testdata_filepath("table.fits")) as hdul: + assert ( + json.dumps(hdul[1].data, cls=misc.JsonCustomEncoder) + == '[["NGC1001", 11.100000381469727], ' + '["NGC1002", 12.300000190734863], ' + '["NGC1003", 15.199999809265137]]' + ) + + +def test_set_locale(): + # First, test if the required locales are available + current = locale.setlocale(locale.LC_ALL) + try: + locale.setlocale(locale.LC_ALL, "en_US.utf8") + locale.setlocale(locale.LC_ALL, "fr_FR.utf8") + except locale.Error as e: + pytest.skip(f"Locale error: {e}") + finally: + locale.setlocale(locale.LC_ALL, current) + + date = datetime(2000, 10, 1, 0, 0, 0) + day_mon = date.strftime("%a, %b") + + with misc._set_locale("en_US.utf8"): + assert date.strftime("%a, %b") == "Sun, Oct" + + with misc._set_locale("fr_FR.utf8"): + assert date.strftime("%a, %b") == "dim., oct." + + # Back to original + assert date.strftime("%a, %b") == day_mon + + with misc._set_locale(current): + assert date.strftime("%a, %b") == day_mon + + +def test_dtype_bytes_or_chars(): + assert misc.dtype_bytes_or_chars(np.dtype(np.float64)) == 8 + assert misc.dtype_bytes_or_chars(np.dtype(object)) is None + assert misc.dtype_bytes_or_chars(np.dtype(np.int32)) == 4 + assert misc.dtype_bytes_or_chars(np.array(b"12345").dtype) == 5 + assert misc.dtype_bytes_or_chars(np.array("12345").dtype) == 5 + + +def test_indent_deprecation(): + with pytest.warns(AstropyDeprecationWarning, match=r"Use textwrap\.indent"): + misc.indent("Obsolete since Python 3.3") + + +def test_format_exception_deprecation(): + with pytest.warns(AstropyDeprecationWarning): + misc.format_exception("this is deprecated") + + +def test_silence_stdout_file_protocol(): + import sys + + with misc.silence(): + assert sys.stdout.isatty() is False + sys.stdout.flush() + sys.stdout.write("ignored") diff --git a/astropy/utils/tests/test_odict.py b/astropy/utils/tests/test_odict.py deleted file mode 100644 index 6743f7eaf78b..000000000000 --- a/astropy/utils/tests/test_odict.py +++ /dev/null @@ -1,277 +0,0 @@ -# Licensed under a 3-clause BSD style license - see LICENSE.rst -""" -Test the Python 2.6 backport of OrderedDict. - -Modified the Python 2.7 Lib/test/test_collections.py to test only -OrderedDict and slightly customized (and renamed) the modules -test_support.py and mapping_tests.py that provide support for those -tests. -""" - -import unittest -import inspect -import pickle -import copy -from ...tests.helper import pytest -from random import shuffle - -import astropy.utils.tests.odict_mapping as mapping_tests - -from astropy.utils.compat.odict import OrderedDict - -#Skips all of these tests if the builtin ordered dict is available -pytestmark = pytest.mark.skipif("sys.version_info >= (2,7)") - - -class TestOrderedDict(unittest.TestCase): - - def test_init(self): - - try: - OrderedDict([('a', 1), ('b', 2)], None) # too many args - assert False - except TypeError: - pass - else: - assert False - - pairs = [('a', 1), ('b', 2), ('c', 3), ('d', 4), ('e', 5)] - self.assertEqual(sorted(OrderedDict(dict(pairs)).items()), pairs) # dict input - self.assertEqual(sorted(OrderedDict(**dict(pairs)).items()), pairs) # kwds input - self.assertEqual(list(OrderedDict(pairs).items()), pairs) # pairs input - self.assertEqual(list(OrderedDict([('a', 1), ('b', 2), ('c', 9), ('d', 4)], - c=3, e=5).items()), pairs) # mixed input - - # make sure no positional args conflict with possible kwdargs - self.assertEqual(inspect.getargspec(OrderedDict.__dict__['__init__']).args, - ['self']) - - # Make sure that direct calls to __init__ do not clear previous contents - d = OrderedDict([('a', 1), ('b', 2), ('c', 3), ('d', 44), ('e', 55)]) - d.__init__([('e', 5), ('f', 6)], g=7, d=4) - self.assertEqual(list(d.items()), - [('a', 1), ('b', 2), ('c', 3), ('d', 4), ('e', 5), ('f', 6), ('g', 7)]) - - def test_update(self): - - try: - OrderedDict().update([('a', 1), ('b', 2)], None) # too many args - assert False - except TypeError: - pass - else: - assert False - - pairs = [('a', 1), ('b', 2), ('c', 3), ('d', 4), ('e', 5)] - od = OrderedDict() - od.update(dict(pairs)) - self.assertEqual(sorted(od.items()), pairs) # dict input - od = OrderedDict() - od.update(**dict(pairs)) - self.assertEqual(sorted(od.items()), pairs) # kwds input - od = OrderedDict() - od.update(pairs) - self.assertEqual(list(od.items()), pairs) # pairs input - od = OrderedDict() - od.update([('a', 1), ('b', 2), ('c', 9), ('d', 4)], c=3, e=5) - self.assertEqual(list(od.items()), pairs) # mixed input - - # Make sure that direct calls to update do not clear previous contents - # add that updates items are not moved to the end - d = OrderedDict([('a', 1), ('b', 2), ('c', 3), ('d', 44), ('e', 55)]) - d.update([('e', 5), ('f', 6)], g=7, d=4) - self.assertEqual(list(d.items()), - [('a', 1), ('b', 2), ('c', 3), ('d', 4), ('e', 5), ('f', 6), ('g', 7)]) - - def test_clear(self): - pairs = [('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)] - shuffle(pairs) - od = OrderedDict(pairs) - self.assertEqual(len(od), len(pairs)) - od.clear() - self.assertEqual(len(od), 0) - - def test_delitem(self): - pairs = [('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)] - od = OrderedDict(pairs) - del od['a'] - self.assertNotIn('a', od) - - try: - del od['a'] - assert False - except KeyError: - pass - else: - assert False - - self.assertEqual(list(od.items()), pairs[:2] + pairs[3:]) - - def test_setitem(self): - od = OrderedDict([('d', 1), ('b', 2), ('c', 3), ('a', 4), ('e', 5)]) - od['c'] = 10 # existing element - od['f'] = 20 # new element - self.assertEqual(list(od.items()), - [('d', 1), ('b', 2), ('c', 10), ('a', 4), ('e', 5), ('f', 20)]) - - def test_iterators(self): - pairs = [('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)] - shuffle(pairs) - od = OrderedDict(pairs) - self.assertEqual(list(od), [t[0] for t in pairs]) - self.assertEqual(od.keys()[:], [t[0] for t in pairs]) - self.assertEqual(od.values()[:], [t[1] for t in pairs]) - self.assertEqual(od.items()[:], pairs) - self.assertEqual(list(od.iterkeys()), [t[0] for t in pairs]) - self.assertEqual(list(od.itervalues()), [t[1] for t in pairs]) - self.assertEqual(list(od.iteritems()), pairs) - self.assertEqual(list(reversed(od)), - [t[0] for t in reversed(pairs)]) - - def test_popitem(self): - pairs = [('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)] - shuffle(pairs) - od = OrderedDict(pairs) - while pairs: - self.assertEqual(od.popitem(), pairs.pop()) - - try: - od.popitem() - assert False - except: - pass - else: - assert False - - self.assertEqual(len(od), 0) - - def test_pop(self): - pairs = [('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)] - shuffle(pairs) - od = OrderedDict(pairs) - shuffle(pairs) - while pairs: - k, v = pairs.pop() - self.assertEqual(od.pop(k), v) - - try: - od.pop('xyz') - assert False - except KeyError: - pass - else: - assert False - - self.assertEqual(len(od), 0) - self.assertEqual(od.pop(k, 12345), 12345) - - def test_equality(self): - pairs = [('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)] - shuffle(pairs) - od1 = OrderedDict(pairs) - od2 = OrderedDict(pairs) - self.assertEqual(od1, od2) # same order implies equality - pairs = pairs[2:] + pairs[:2] - od2 = OrderedDict(pairs) - self.assertNotEqual(od1, od2) # different order implies inequality - # comparison to regular dict is not order sensitive - self.assertEqual(od1, dict(od2)) - self.assertEqual(dict(od2), od1) - # different length implied inequality - self.assertNotEqual(od1, OrderedDict(pairs[:-1])) - - def test_copying(self): - # Check that ordered dicts are copyable, deepcopyable, picklable, - # and have a repr/eval round-trip - pairs = [('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)] - od = OrderedDict(pairs) - update_test = OrderedDict() - update_test.update(od) - for i, dup in enumerate([ - od.copy(), - copy.copy(od), - copy.deepcopy(od), - pickle.loads(pickle.dumps(od, 0)), - pickle.loads(pickle.dumps(od, 1)), - pickle.loads(pickle.dumps(od, 2)), - pickle.loads(pickle.dumps(od, -1)), - eval(repr(od)), - update_test, - OrderedDict(od), - ]): - self.assertTrue(dup is not od) - self.assertEquals(dup, od) - self.assertEquals(list(dup.items()), list(od.items())) - self.assertEquals(len(dup), len(od)) - self.assertEquals(type(dup), type(od)) - - def test_yaml_linkage(self): - # Verify that __reduce__ is setup in a way that supports PyYAML's dump() feature. - # In yaml, lists are native but tuples are not. - pairs = [('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)] - od = OrderedDict(pairs) - # yaml.dump(od) --> - # '!!python/object/apply:__main__.OrderedDict\n- - [a, 1]\n - [b, 2]\n' - self.assertTrue(all(type(pair)==list for pair in od.__reduce__()[1])) - - def test_reduce_not_too_fat(self): - # do not save instance dictionary if not needed - pairs = [('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)] - od = OrderedDict(pairs) - self.assertEqual(len(od.__reduce__()), 2) - od.x = 10 - self.assertEqual(len(od.__reduce__()), 3) - - def test_repr(self): - od = OrderedDict([('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)]) - self.assertEqual(repr(od), - "OrderedDict([('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)])") - self.assertEqual(eval(repr(od)), od) - self.assertEqual(repr(OrderedDict()), "OrderedDict()") - - def test_setdefault(self): - pairs = [('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)] - shuffle(pairs) - od = OrderedDict(pairs) - pair_order = list(od.items()) - self.assertEqual(od.setdefault('a', 10), 3) - # make sure order didn't change - self.assertEqual(list(od.items()), pair_order) - self.assertEqual(od.setdefault('x', 10), 10) - # make sure 'x' is added to the end - self.assertEqual(list(od.items())[-1], ('x', 10)) - - def test_reinsert(self): - # Given insert a, insert b, delete a, re-insert a, - # verify that a is now later than b. - od = OrderedDict() - od['a'] = 1 - od['b'] = 2 - del od['a'] - od['a'] = 1 - self.assertEqual(list(od.items()), [('b', 2), ('a', 1)]) - - def assertIn(self, key, d): - self.assertTrue(key in d) - - def assertNotIn(self, key, d): - self.assertFalse(key in d) - -class GeneralMappingTests(mapping_tests.BasicTestMappingProtocol): - type2test = OrderedDict - - def test_popitem(self): - d = self._empty_mapping() - self.assertRaises(KeyError, d.popitem) - - -class MyOrderedDict(OrderedDict): - pass - - -class SubclassMappingTests(mapping_tests.BasicTestMappingProtocol): - type2test = MyOrderedDict - - def test_popitem(self): - d = self._empty_mapping() - self.assertRaises(KeyError, d.popitem) diff --git a/astropy/utils/tests/test_parsing.py b/astropy/utils/tests/test_parsing.py new file mode 100644 index 000000000000..a6838d38f585 --- /dev/null +++ b/astropy/utils/tests/test_parsing.py @@ -0,0 +1,97 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +import importlib +import secrets +from py_compile import compile +from textwrap import dedent + +import pytest + +from astropy.utils.parsing import _TAB_HEADER + + +def _docstring_canary(): + """Docstring that's here just to check for -OO.""" + + +@pytest.mark.skipif(not _docstring_canary.__doc__, reason="Test cannot be run with -OO") +def test_generate_parser(tmp_path, monkeypatch): + # Write Python code into the temporary directory, so that the + # generated tables will also go into the temporary directory. + # We use a unique suffix so that the test can be run multiple times + # without weirdness due to module caching. + suffix = secrets.token_hex(16) + lexer_file = tmp_path / f"test_parsing_lexer_{suffix}.py" + lexer_file.write_text( + dedent( + rf""" + from astropy.utils.parsing import lex + + def make_lexer(): + tokens = ('NUMBER', 'PLUS') + t_PLUS = r'\+' + + def t_NUMBER(t): + r'\d+' + t.value = int(t.value) + return t + + def t_error(t): + raise ValueError('Invalid character') + + return lex('test_parsing_lextab_{suffix}', 'test_parsing_lexer_{suffix}') + """ + ) + ) + parser_file = tmp_path / f"test_parsing_parser_{suffix}.py" + parser_file.write_text( + dedent( + rf""" + from astropy.utils.parsing import yacc + + def make_parser(): + tokens = ('NUMBER', 'PLUS') + + def p_expression_number(p): + 'expression : NUMBER' + p[0] = p[1] + + def p_expression_plus(p): + 'expression : expression PLUS NUMBER' + p[0] = p[1] + p[3] + + return yacc('test_parsing_parsetab_{suffix}', 'test_parsing_parser_{suffix}') + """ + ) + ) + + monkeypatch.syspath_prepend(tmp_path) + + lexer_mod = importlib.import_module(f"test_parsing_lexer_{suffix}") + lexer = lexer_mod.make_lexer() + parser_mod = importlib.import_module(f"test_parsing_parser_{suffix}") + parser = parser_mod.make_parser() + result = parser.parse("1+2+3", lexer=lexer) + assert result == 6 + + lextab_path = tmp_path / f"test_parsing_lextab_{suffix}.py" + lextab = lextab_path.read_text() + assert lextab.startswith(_TAB_HEADER.format(package=f"test_parsing_lexer_{suffix}")) + parsetab_path = tmp_path / f"test_parsing_parsetab_{suffix}.py" + parsetab = parsetab_path.read_text() + assert parsetab.startswith( + _TAB_HEADER.format(package=f"test_parsing_parser_{suffix}") + ) + + # Now test bytecode-only distribution of the parsetab and lextab + + lexer_mod = importlib.reload(lexer_mod) + parser_mod = importlib.reload(parser_mod) + for path in lextab_path, parsetab_path: + compile(path, cfile=path.with_suffix(".pyc"), doraise=True) + path.unlink() + + lexer = lexer_mod.make_lexer() + parser = parser_mod.make_parser() + result = parser.parse("1+2+3", lexer=lexer) + assert result == 6 diff --git a/astropy/utils/tests/test_progress_bar_func.py b/astropy/utils/tests/test_progress_bar_func.py new file mode 100644 index 000000000000..2cb017013bee --- /dev/null +++ b/astropy/utils/tests/test_progress_bar_func.py @@ -0,0 +1,19 @@ +import time + +import numpy as np + +from astropy.utils.misc import NumpyRNGContext + + +def func(i): + """An identity function that jitters its execution time by a + pseudo-random amount. + + FIXME: This function should be defined in test_console.py, but Astropy's + test runner interacts strangely with Python's `multiprocessing` + module. I was getting a mysterious PicklingError until I moved this + function into a separate module. (It worked fine in a standalone pytest + script.)""" + with NumpyRNGContext(i): + time.sleep(np.random.uniform(0, 0.01)) + return i diff --git a/astropy/utils/tests/test_shapes.py b/astropy/utils/tests/test_shapes.py new file mode 100644 index 000000000000..8f14eabdba9d --- /dev/null +++ b/astropy/utils/tests/test_shapes.py @@ -0,0 +1,98 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +import numpy as np +import pytest +from hypothesis import given +from hypothesis.extra.numpy import basic_indices +from numpy.testing import assert_equal + +from astropy.utils.exceptions import AstropyDeprecationWarning +from astropy.utils.shapes import ( + ShapedLikeNDArray, + check_broadcast, + simplify_basic_index, + unbroadcast, +) + + +class _ShapedDummy(ShapedLikeNDArray): + def __init__(self, values): + self._values = np.asarray(values) + + @property + def shape(self): + return self._values.shape + + def _apply(self, method, *args, **kwargs): + if callable(method): + values = method(self._values, *args, **kwargs) + else: + values = getattr(self._values, method)(*args, **kwargs) + return self.__class__(values) + + +def test_check_broadcast_deprecation(): + with pytest.warns(AstropyDeprecationWarning): + check_broadcast((1,), (2,)) + + +@pytest.mark.filterwarnings("ignore") +def test_check_broadcast(): + assert check_broadcast((10, 1), (3,)) == (10, 3) + assert check_broadcast((10, 1), (3,), (4, 1, 1, 3)) == (4, 1, 10, 3) + with pytest.raises(ValueError): + check_broadcast((10, 2), (3,)) + + with pytest.raises(ValueError): + check_broadcast((10, 1), (3,), (4, 1, 2, 3)) + + +def test_unbroadcast(): + x = np.array([1, 2, 3]) + y = np.broadcast_to(x, (2, 4, 3)) + z = unbroadcast(y) + assert z.shape == (3,) + np.testing.assert_equal(z, x) + + x = np.ones((3, 5)) + y = np.broadcast_to(x, (5, 3, 5)) + z = unbroadcast(y) + assert z.shape == (3, 5) + + +def test_take_out_raises(): + shaped = _ShapedDummy([1, 2, 3, 4]) + with pytest.raises(NotImplementedError, match="cannot pass 'out' argument"): + shaped.take((0, 1), out=np.empty(2, dtype=int)) + + +TEST_SHAPE = (13, 16, 4, 90) + + +class TestSimplifyBasicIndex: + # We use a class here so that we can allocate the data once and for all to + # speed up the testing. + + def setup_class(self): + self.shape = TEST_SHAPE + self.data = np.random.random(TEST_SHAPE) + + @given(basic_indices(TEST_SHAPE)) + def test_indexing(self, index): + new_index = simplify_basic_index(index, shape=self.shape) + assert_equal(self.data[index], self.data[new_index]) + assert isinstance(new_index, tuple) + assert len(new_index) == len(self.shape) + for idim, idx in enumerate(new_index): + assert isinstance(idx, (slice, int)) + if isinstance(idx, int): + assert idx >= 0 + else: + assert isinstance(idx.start, int) + assert idx.start >= 0 + assert idx.start < TEST_SHAPE[idim] + if idx.stop is not None: + assert isinstance(idx.stop, int) + assert idx.stop >= 0 + assert idx.stop <= TEST_SHAPE[idim] + assert isinstance(idx.step, int) diff --git a/astropy/utils/tests/test_state.py b/astropy/utils/tests/test_state.py new file mode 100644 index 000000000000..7a34f280e7ad --- /dev/null +++ b/astropy/utils/tests/test_state.py @@ -0,0 +1,25 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +from astropy.utils.state import ScienceState + + +def test_ScienceState_and_Context(): + """ + Tests a ScienceState and spawned contexts. + """ + + class MyState(ScienceState): + _value = "A" + _state = dict(foo="bar") + + state = {"foo": "bar"} + + # test created ScienceState + assert MyState.get() == "A" + assert MyState.validate("B") == "B" + assert MyState._state == state + + # test setting + with MyState.set("B"): + assert MyState.get() == "B" + assert MyState.get() == "A" # test returning diff --git a/astropy/utils/tests/test_xml.py b/astropy/utils/tests/test_xml.py index 30912fae525a..c8ee55967fa4 100644 --- a/astropy/utils/tests/test_xml.py +++ b/astropy/utils/tests/test_xml.py @@ -1,6 +1,11 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + import io -from ..xml import check, writer +import pytest + +from astropy.utils.compat.optional_deps import HAS_BLEACH +from astropy.utils.xml import check, unescaper, writer def test_writer(): @@ -11,8 +16,8 @@ def test_writer(): w.data("This is the content") w.comment("comment") - value = ''.join(fh.getvalue().split()) - assert value == 'Thisisthecontent' + value = "".join(fh.getvalue().split()) + assert value == "Thisisthecontent" def test_check_id(): @@ -38,3 +43,77 @@ def test_check_mime_content_type(): def test_check_anyuri(): assert check.check_anyuri("https://github.com/astropy/astropy") + + +def test_unescape_all(): + # str + url_in = ( + "http://casu.ast.cam.ac.uk/ag/iphas-dsa%2FSubmitCone?" + "DSACAT=IDR&amp;DSATAB=Emitters&amp;" + ) + url_out = ( + "http://casu.ast.cam.ac.uk/ag/iphas-dsa/SubmitCone?DSACAT=IDR&DSATAB=Emitters&" + ) + assert unescaper.unescape_all(url_in) == url_out + + # bytes + url_in = ( + b"http://casu.ast.cam.ac.uk/ag/iphas-dsa%2FSubmitCone?" + b"DSACAT=IDR&amp;DSATAB=Emitters&amp;" + ) + url_out = ( + b"http://casu.ast.cam.ac.uk/ag/iphas-dsa/SubmitCone?DSACAT=IDR&DSATAB=Emitters&" + ) + assert unescaper.unescape_all(url_in) == url_out + + +def test_escape_xml(): + s = writer.xml_escape("This & That") + assert type(s) == str + assert s == "This & That" + + s = writer.xml_escape(1) + assert type(s) == str + assert s == "1" + + s = writer.xml_escape(b"This & That") + assert type(s) == bytes + assert s == b"This & That" + + +@pytest.mark.skipif(HAS_BLEACH, reason="bleach is installed") +def test_escape_xml_without_bleach(): + fh = io.StringIO() + w = writer.XMLWriter(fh) + + with pytest.raises( + ValueError, match=r"bleach package is required when HTML escaping is disabled" + ): + with w.xml_cleaning_method("bleach_clean"): + pass + + +@pytest.mark.skipif(not HAS_BLEACH, reason="requires bleach") +def test_escape_xml_with_bleach(): + fh = io.StringIO() + w = writer.XMLWriter(fh) + + # Turn off XML escaping, but still sanitize unsafe tags like OK") + w.end(indent=False) + assert fh.getvalue() == "<script>x</script> OK\n" + + fh = io.StringIO() + w = writer.XMLWriter(fh) + + # Default is True (all XML tags escaped) + with w.xml_cleaning_method(): + w.start("td") + w.data(" OK") + w.end(indent=False) + assert ( + fh.getvalue() + == "<script>x</script> <em>OK</em>\n" + ) diff --git a/astropy/utils/xml/check.py b/astropy/utils/xml/check.py index 706d5724e291..dd02887ecd2f 100644 --- a/astropy/utils/xml/check.py +++ b/astropy/utils/xml/check.py @@ -5,7 +5,7 @@ """ import re -import urlparse +import urllib.parse def check_id(ID): @@ -25,12 +25,14 @@ def fix_id(ID): return ID if len(ID): corrected = ID - if not len(corrected) or re.match('^[^A-Za-z_]$', corrected[0]): - corrected = '_' + corrected - corrected = (re.sub(r"[^A-Za-z_]", '_', corrected[0]) + - re.sub(r"[^A-Za-z0-9_\.\-]", "_", corrected[1:])) + if not len(corrected) or re.match("^[^A-Za-z_]$", corrected[0]): + corrected = "_" + corrected + corrected = re.sub(r"[^A-Za-z_]", "_", corrected[0]) + re.sub( + r"[^A-Za-z0-9_\.\-]", "_", corrected[1:] + ) return corrected - return '' + return "" + _token_regex = r"(?![\r\l\t ])[^\r\l\t]*(?![\r\l\t ])" @@ -40,10 +42,11 @@ def check_token(token): Returns `True` if *token* is a valid XML token, as defined by XML Schema Part 2. """ - return (token == '' or - re.match( - "[^\r\n\t ]?([^\r\n\t ]| [^\r\n\t ])*[^\r\n\t ]?$", token) - is not None) + return ( + token == "" + or re.match(r"[^\r\n\t ]?([^\r\n\t ]| [^\r\n\t ])*[^\r\n\t ]?$", token) + is not None + ) def check_mime_content_type(content_type): @@ -51,24 +54,31 @@ def check_mime_content_type(content_type): Returns `True` if *content_type* is a valid MIME content type (syntactically at least), as defined by RFC 2045. """ - ctrls = ''.join(chr(x) for x in xrange(0, 0x20)) - token_regex = '[^()<>@,;:\\\"/[\]?= %s\x7f]+' % ctrls - return re.match( - r'(?P%s)/(?P%s)$' % (token_regex, token_regex), - content_type) is not None + ctrls = "".join(chr(x) for x in range(0x20)) + token_regex = f'[^()<>@,;:\\"/[\\]?= {ctrls}\x7f]+' + return ( + re.match(rf"(?P{token_regex})/(?P{token_regex})$", content_type) + is not None + ) def check_anyuri(uri): """ Returns `True` if *uri* is a valid URI as defined in RFC 2396. """ - if (re.match( - (r"(([a-zA-Z][0-9a-zA-Z+\-\.]*:)?/{0,2}[0-9a-zA-Z;" + - r"/?:@&=+$\.\-_!~*'()%]+)?(#[0-9a-zA-Z;/?:@&=+$\.\-_!~*'()%]+)?"), - uri) is None): + if ( + re.match( + ( + r"(([a-zA-Z][0-9a-zA-Z+\-\.]*:)?/{0,2}[0-9a-zA-Z;" + r"/?:@&=+$\.\-_!~*'()%]+)?(#[0-9a-zA-Z;/?:@&=+$\.\-_!~*'()%]+)?" + ), + uri, + ) + is None + ): return False try: - urlparse.urlparse(uri) - except: + urllib.parse.urlparse(uri) + except Exception: return False return True diff --git a/astropy/utils/xml/iterparser.py b/astropy/utils/xml/iterparser.py index b67ad74c077f..0fc0af784ac1 100644 --- a/astropy/utils/xml/iterparser.py +++ b/astropy/utils/xml/iterparser.py @@ -4,34 +4,16 @@ """ # STDLIB -import collections import contextlib import io import sys +# ASTROPY +from astropy.utils import data -__all__ = ['get_xml_iterator', 'get_xml_encoding', 'xml_readlines'] +from ._iterparser import IterParser as _fast_iterparse - -############################################################ -# TODO: Refactor this into a py3k compatibility module -IS_PY3K = (sys.version_info[0] >= 3) - -if IS_PY3K: - def is_callable(o): - """ - Abstracts away the different ways to test for a callable object in - Python 2.x and 3.x. - """ - return isinstance(o, collections.Callable) -else: - def is_callable(o): - """ - Abstracts away the different ways to test for a callable object in - Python 2.x and 3.x. - """ - return callable(o) -############################################################ +__all__ = ["get_xml_encoding", "get_xml_iterator", "xml_readlines"] @contextlib.contextmanager @@ -47,20 +29,25 @@ def _convert_to_fd_or_read_function(fd): - If it's not a real file object, it's much handier to just have a Python function to call. + This is somewhat quirky behavior, of course, which is why it is + private. For a more useful version of similar behavior, see + `astropy.utils.misc.get_readable_fileobj`. + Parameters ---------- fd : object May be: - - a file object, in which case it is returned verbatim. + - a file object. If the file is uncompressed, this raw + file object is returned verbatim. Otherwise, the read + method is returned. - a function that reads from a stream, in which case it is returned verbatim. - - a file path, in which case it is opened. If it ends in - `.gz`, it is assumed to be a gzipped file, and the - :meth:`read` method on the file object is returned. - Otherwise, the raw file object is returned. + - a file path, in which case it is opened. Again, like a + file object, if it's uncompressed, a raw file object is + returned, otherwise its read method. - an object with a :meth:`read` method, in which case that method is returned. @@ -70,59 +57,24 @@ def _convert_to_fd_or_read_function(fd): fd : context-dependent See above. """ - if is_callable(fd): + if callable(fd): yield fd return - elif isinstance(fd, basestring): - if fd.endswith('.gz'): - from ...utils.compat import gzip - with gzip.GzipFile(fd, 'rb') as real_fd: - yield real_fd.read - return + + with data.get_readable_fileobj(fd, encoding="binary") as new_fd: + if sys.platform.startswith("win"): + yield new_fd.read else: - with open(fd, 'rb') as real_fd: - if sys.platform.startswith('win'): - # On Windows, we can't pass a real file descriptor - # to the C level, so we pass the read method - yield real_fd.read - return - yield real_fd - return - elif hasattr(fd, 'read'): - assert is_callable(fd.read) - magic = fd.read(2) - fd.seek(0) - if magic == b'\x1f\x8b': - from ...utils.compat import gzip - fd = gzip.GzipFile(fileobj=fd) - - if type(fd.read(0)) == type(u''): - def make_encoder(reader): - def read(n): - return reader(n).encode('utf-8') - yield make_encoder(fd.read) - return - - if not sys.platform.startswith('win'): - if IS_PY3K: - if isinstance(fd, io.FileIO): - yield fd - return + if isinstance(new_fd, io.FileIO): + yield new_fd else: - if isinstance(fd, file): - yield fd - return - - yield fd.read - return - else: - raise TypeError("Can not be coerced to read function") + yield new_fd.read -def _fast_iterparse(fd, buffersize=2 ** 10): +def _slow_iterparse(fd, buffersize=2**10): from xml.parsers import expat - if not is_callable(fd): + if not callable(fd): read = fd.read else: read = fd @@ -131,17 +83,22 @@ def _fast_iterparse(fd, buffersize=2 ** 10): text = [] def start(name, attr): - queue.append((True, name, attr, - (parser.CurrentLineNumber, parser.CurrentColumnNumber))) + queue.append( + (True, name, attr, (parser.CurrentLineNumber, parser.CurrentColumnNumber)) + ) del text[:] def end(name): - queue.append((False, name, u''.join(text).strip(), - (parser.CurrentLineNumber, parser.CurrentColumnNumber))) + queue.append( + ( + False, + name, + "".join(text).strip(), + (parser.CurrentLineNumber, parser.CurrentColumnNumber), + ) + ) parser = expat.ParserCreate() - if not IS_PY3K: - parser.returns_unicode = True parser.specified_attributes = True parser.StartElementHandler = start parser.EndElementHandler = end @@ -151,24 +108,12 @@ def end(name): data = read(buffersize) while data: Parse(data, False) - for elem in queue: - yield elem + yield from queue del queue[:] data = read(buffersize) - Parse('', True) - for elem in queue: - yield elem - - -# Try to import the C version of the iterparser, otherwise fall back -# to the Python implementation above. -_slow_iterparse = _fast_iterparse -try: - from . import _iterparser - _fast_iterparse = _iterparser.IterParser -except ImportError: - pass + Parse("", True) + yield from queue @contextlib.contextmanager @@ -177,11 +122,14 @@ def get_xml_iterator(source, _debug_python_based_parser=False): Returns an iterator over the elements of an XML file. The iterator doesn't ever build a tree, so it is much more memory - and time efficient than the alternative in `cElementTree`. + and time efficient than the alternative in ``cElementTree``. Parameters ---------- - fd : readable file-like object or read function + source : path-like, :term:`file-like (readable)`, or callable + Handle that contains the data or function that reads it. + If a function or callable object, it must directly read from a stream. + Non-callable objects must define a ``read`` method. Returns ------- @@ -219,18 +167,22 @@ def get_xml_encoding(source): Parameters ---------- - source : readable file-like object, read function or str path + source : path-like, :term:`file-like (readable)`, or callable + Handle that contains the data or function that reads it. + If a function or callable object, it must directly read from a stream. + Non-callable objects must define a ``read`` method. Returns ------- encoding : str """ with get_xml_iterator(source) as iterator: - start, tag, data, pos = iterator.next() - if not start or tag != u'xml': - raise IOError('Invalid XML file') + start, tag, data, pos = next(iterator) + if not start or tag != "xml": + raise OSError("Invalid XML file") - return data['encoding'] + # The XML spec says that no encoding === utf-8 + return data.get("encoding") or "utf-8" def xml_readlines(source): @@ -240,7 +192,10 @@ def xml_readlines(source): Parameters ---------- - source : readable file-like object, read function or str path + source : path-like, :term:`file-like (readable)`, or callable + Handle that contains the data or function that reads it. + If a function or callable object, it must directly read from a stream. + Non-callable objects must define a ``read`` method. Returns ------- @@ -248,7 +203,8 @@ def xml_readlines(source): """ encoding = get_xml_encoding(source) - with io.open(source, 'rt', encoding=encoding) as input: + with data.get_readable_fileobj(source, encoding=encoding) as input: + input.seek(0) xml_lines = input.readlines() return xml_lines diff --git a/astropy/utils/xml/setup_package.py b/astropy/utils/xml/setup_package.py index efb9de866762..23acac9ce362 100644 --- a/astropy/utils/xml/setup_package.py +++ b/astropy/utils/xml/setup_package.py @@ -1,54 +1,48 @@ -from distutils.core import Extension -from os.path import join -import sys +# Licensed under a 3-clause BSD style license - see LICENSE.rst -from astropy import setup_helpers +import os +import sys +from collections import defaultdict +from os.path import join +from setuptools import Extension -def get_external_libraries(): - return ['expat'] +from extension_helpers import pkg_config -def get_extensions(build_type='release'): - XML_DIR = 'astropy/utils/xml/src' +def get_extensions(build_type="release"): + XML_DIR = "astropy/utils/xml/src" - source_files = [join(XML_DIR, "iterparse.c")] - include_dirs = [] - extra_link_args = [] - defines = [] - libraries = [] - library_dirs = [] + cfg = defaultdict(list) + cfg["sources"] = [join(XML_DIR, "iterparse.c")] - if setup_helpers.use_system_library('expat'): - setup_helpers.pkg_config( - ['expat'], ['expat'], include_dirs, library_dirs, libraries) + if int(os.environ.get("ASTROPY_USE_SYSTEM_EXPAT", "0")) or int( + os.environ.get("ASTROPY_USE_SYSTEM_ALL", "0") + ): + for k, v in pkg_config(["expat"], ["expat"]).items(): + cfg[k].extend(v) else: - EXPAT_DIR = 'cextern/expat/lib' - source_files.extend([ - join(EXPAT_DIR, fn) for fn in - ["xmlparse.c", "xmlrole.c", "xmltok.c", "xmltok_impl.c"]]) - include_dirs.extend([XML_DIR, EXPAT_DIR]) - if sys.platform.startswith('linux'): + EXPAT_DIR = "cextern/expat/lib" + cfg["sources"].extend( + [ + join(EXPAT_DIR, fn) + for fn in ["xmlparse.c", "xmlrole.c", "xmltok.c", "xmltok_impl.c"] + ] + ) + cfg["include_dirs"].extend([XML_DIR, EXPAT_DIR]) + if sys.platform.startswith("linux"): # This is to ensure we only export the Python entry point # symbols and the linker won't try to use the system expat in # place of ours. - extra_link_args.append( - '-Wl,--version-script={0}'.format( - join(XML_DIR, 'iterparse.map')) - ) - defines = [("HAVE_EXPAT_CONFIG_H", 1)] - if sys.byteorder == 'big': - defines.append(('BYTEORDER', '4321')) + cfg["extra_link_args"].extend( + [f"-Wl,--version-script={join(XML_DIR, 'iterparse.map')}"] + ) + cfg["define_macros"].append(("HAVE_EXPAT_CONFIG_H", 1)) + if sys.byteorder == "big": + cfg["define_macros"].append(("BYTEORDER", "4321")) else: - defines.append(('BYTEORDER', '1234')) - if sys.platform != 'win32': - defines.append(('HAVE_UNISTD_H', None)) - - return [Extension( - "astropy.utils.xml._iterparser", - source_files, - define_macros=defines, - include_dirs=include_dirs, - library_dirs=library_dirs, - libraries=libraries, - extra_link_args=extra_link_args)] + cfg["define_macros"].append(("BYTEORDER", "1234")) + if sys.platform != "win32": + cfg["define_macros"].append(("HAVE_UNISTD_H", None)) + + return [Extension("astropy.utils.xml._iterparser", **cfg)] diff --git a/astropy/utils/xml/src/expat_config.h b/astropy/utils/xml/src/expat_config.h index 0ddece5611cb..82a04040ba2a 100644 --- a/astropy/utils/xml/src/expat_config.h +++ b/astropy/utils/xml/src/expat_config.h @@ -1,36 +1,49 @@ -/* expat_config.h. Generated by configure. */ -/* expat_config.h.in. Generated from configure.in by autoheader. */ +/* expat_config.h. Generated from expat_config.h.in by configure. */ +/* expat_config.h.in. Generated from configure.ac by autoheader. */ -/* 1234 = LIL_ENDIAN, 4321 = BIGENDIAN */ -/* #define BYTEORDER 1234 */ +#ifndef EXPAT_CONFIG_H +#define EXPAT_CONFIG_H 1 -/* Define to 1 if you have the `bcopy' function. */ -#define HAVE_BCOPY 1 +/* Define if building universal (internal helper macro) */ +/* #undef AC_APPLE_UNIVERSAL_BUILD */ -/* Define to 1 if you have the header file. */ -#define HAVE_DLFCN_H 1 +/* 1234 = LILENDIAN, 4321 = BIGENDIAN */ +/* This is redefined in astropy/utils/xml/setup_package.py based on arch */ +/* #undef BYTEORDER */ + +/* Define to 1 if you have the `arc4random' function. */ +/* #undef HAVE_ARC4RANDOM */ + +/* Define to 1 if you have the `arc4random_buf' function. */ +/* #undef HAVE_ARC4RANDOM_BUF */ + +/* define if the compiler supports basic C++11 syntax */ +#define HAVE_CXX11 1 /* Define to 1 if you have the header file. */ #define HAVE_FCNTL_H 1 -/* Define to 1 if you have the `getpagesize' function. */ +/* Define to 1 if you have the 'getpagesize' function. */ #define HAVE_GETPAGESIZE 1 +/* Define to 1 if you have the `getrandom' function. */ +/* #undef HAVE_GETRANDOM */ + /* Define to 1 if you have the header file. */ #define HAVE_INTTYPES_H 1 -/* Define to 1 if you have the `memmove' function. */ -#define HAVE_MEMMOVE 1 +/* Define to 1 if you have the 'bsd' library (-lbsd). */ +/* #undef HAVE_LIBBSD */ -/* Define to 1 if you have the header file. */ -#define HAVE_MEMORY_H 1 - -/* Define to 1 if you have a working `mmap' system call. */ +/* Define to 1 if you have a working 'mmap' system call. */ #define HAVE_MMAP 1 /* Define to 1 if you have the header file. */ #define HAVE_STDINT_H 1 +/* Define to 1 if you have the header file. */ +#define HAVE_STDIO_H 1 + /* Define to 1 if you have the header file. */ #define HAVE_STDLIB_H 1 @@ -40,54 +53,88 @@ /* Define to 1 if you have the header file. */ #define HAVE_STRING_H 1 +/* Unavailable on OSX */ +/* Define to 1 if you have `syscall' and `SYS_getrandom'. */ +/* #undef HAVE_SYSCALL_GETRANDOM */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_PARAM_H 1 + /* Define to 1 if you have the header file. */ #define HAVE_SYS_STAT_H 1 /* Define to 1 if you have the header file. */ #define HAVE_SYS_TYPES_H 1 +/* Unavailable on Windows */ /* Define to 1 if you have the header file. */ -/* #define HAVE_UNISTD_H 1 */ +/* undef HAVE_UNISTD_H */ + +/* Name of package */ +#define PACKAGE "expat" /* Define to the address where bug reports for this package should be sent. */ -#define PACKAGE_BUGREPORT "expat-bugs@libexpat.org" +#define PACKAGE_BUGREPORT "https://github.com/libexpat/libexpat/issues" /* Define to the full name of this package. */ #define PACKAGE_NAME "expat" /* Define to the full name and version of this package. */ -#define PACKAGE_STRING "expat 2.0.1" +#define PACKAGE_STRING "expat 2.7.3" /* Define to the one symbol short name of this package. */ #define PACKAGE_TARNAME "expat" +/* Define to the home page for this package. */ +#define PACKAGE_URL "" + /* Define to the version of this package. */ -#define PACKAGE_VERSION "2.0.1" +#define PACKAGE_VERSION "2.7.3" -/* Define to 1 if you have the ANSI C header files. */ +/* Define to 1 if all of the C89 standard headers exist (not just the ones + required in a freestanding environment). This macro is provided for + backward compatibility; new code need not use it. */ #define STDC_HEADERS 1 -/* whether byteorder is bigendian */ -/* #undef WORDS_BIGENDIAN */ +/* Version number of package */ +#define VERSION "2.7.3" + +/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most + significant byte first (like Motorola and SPARC, unlike Intel). */ +#if defined AC_APPLE_UNIVERSAL_BUILD +#if defined __BIG_ENDIAN__ +#define WORDS_BIGENDIAN 1 +#endif +#else +#ifndef WORDS_BIGENDIAN +/* # undef WORDS_BIGENDIAN */ +#endif +#endif + +/* Define to allow retrieving the byte offsets for attribute names and values. + */ +/* #undef XML_ATTR_INFO */ /* Define to specify how much context to retain around the current parse - point. */ + point, 0 to disable. */ #define XML_CONTEXT_BYTES 1024 +/* Define to include code reading entropy from `/dev/urandom'. */ +#define XML_DEV_URANDOM 1 + /* Define to make parameter entity parsing functionality available. */ -#define XML_DTD 0 +#define XML_DTD 1 -/* Define to make XML Namespaces functionality available. */ -#define XML_NS 0 +/* Define as 1/0 to enable/disable support for general entities. */ +#define XML_GE 1 -/* Define to __FUNCTION__ or "" if `__func__' does not conform to ANSI C. */ -/* #undef __func__ */ +/* Define to make XML Namespaces functionality available. */ +#define XML_NS 1 -/* Define to empty if `const' does not conform to ANSI C. */ +/* Define to empty if 'const' does not conform to ANSI C. */ /* #undef const */ -/* Define to `long' if does not define. */ +/* Define to 'long int' if does not define. */ /* #undef off_t */ -/* Define to `unsigned' if does not define. */ -/* #undef size_t */ +#endif // ndef EXPAT_CONFIG_H diff --git a/astropy/utils/xml/src/iterparse.c b/astropy/utils/xml/src/iterparse.c index 4a6f2124470a..e186756e6e74 100644 --- a/astropy/utils/xml/src/iterparse.c +++ b/astropy/utils/xml/src/iterparse.c @@ -1,7 +1,7 @@ /****************************************************************************** * C extension code for astropy.utils.xml.iterparse * - * Everything in this file has an alternate Python implementation and + * Everything in this file could be implemented in Python and * is included for performance reasons only. * * It has two main parts: @@ -11,10 +11,10 @@ * faster and more memory efficient than the alternatives in the * Python standard library because it does not build a tree of * objects, and also throws away most text nodes, since for - * astropy.io.vo (the primary user of this library) we only care - * about simple text nodes contained between a single pair of + * astropy.io.votable (the primary user of this library) we only + * care about simple text nodes contained between a single pair of * open/close element nodes. It also has an optimization for - * recognizing the most commonly occuring element in a VO file, + * recognizing the most commonly occurring element in a VO file, * "TD". * * - Two functions, escape_xml() and escape_xml_cdata() that escape @@ -23,6 +23,11 @@ ******************************************************************************/ #include +#include +#include // memcpy +#ifndef _MSC_VER +#include // read +#endif #include "structmember.h" #include "expat.h" @@ -34,19 +39,40 @@ #define inline #endif -#undef CLAMP -#define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x))) +#undef CLAMP +#define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x))) -static Py_ssize_t -next_power_of_2(Py_ssize_t n) +static Py_ssize_t next_power_of_2(Py_ssize_t n) { - /* Calculate the next-highest power of two */ + /* Calculate the next-higher power of two that is >= 'n' */ + + /* These instructions are intended for uint32_t and originally + from http://www-graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 + Py_ssize_t is the same as the C datatype ssize_t and is + 32/64-bits on 32-bit and 64-bit systems respectively. The implementation + here accounts for both. + + Limitations: Since a signed size_t (ssize_t) was required for the underlying CPython implementation, + on 32 bit systems, it will not be possible to allocate memory sizes between + SSIZE_MAX and SIZE_MAX (i.e, for allocations in the range [2^31, 2^32 - 1] bytes) + even though such memory might be available and accessible on the (32-bit) computer. That + said, since the underlying CPython implementation *also* uses Py_ssize_t (i.e., ssize_t), + it is safe to assume that such memory allocations would probably not be usable anyway. + + TLDR: Will work on 64-bit machines but be careful on 32 bit machines when reading in + ~ 2+ GB of memory -- @manodeep 2020-03-27 + */ + n--; n |= n >> 1; n |= n >> 2; n |= n >> 4; n |= n >> 8; n |= n >> 16; + if (sizeof(Py_ssize_t) > 4) { + n |= n >> 32; /* this works for 64-bit systems but will need to be updated if (Py)_ssize_t + ever increases beyond 64-bits */ + } n++; return n; @@ -55,60 +81,48 @@ next_power_of_2(Py_ssize_t n) /****************************************************************************** * Python version compatibility macros ******************************************************************************/ -#if PY_MAJOR_VERSION >= 3 -# define IS_PY3K -#endif - -# ifndef Py_TYPE -# define Py_TYPE(o) ((o)->ob_type) -# endif #if BYTEORDER == 1234 -# define TD_AS_INT 0x00004454 -# define TD_AS_INT_MASK 0x00ffffff +#define TD_AS_INT 0x00004454 +#define TD_AS_INT_MASK 0x00ffffff #else -# define TD_AS_INT 0x54440000 -# define TD_AS_INT_MASK 0xffffff00 +#define TD_AS_INT 0x54440000 +#define TD_AS_INT_MASK 0xffffff00 #endif -/* Clang doesn't like the hackish stuff PyTuple_SET_ITEM does... */ -#ifdef __clang__ -#undef PyTuple_SET_ITEM -#define PyTuple_SET_ITEM(a, b, c) PyTuple_SetItem((a), (b), (c)) -#endif /****************************************************************************** * IterParser type ******************************************************************************/ typedef struct { PyObject_HEAD - XML_Parser parser; /* The expat parser */ - int done; /* True when expat parser has read to EOF */ + XML_Parser parser; /* The expat parser */ + int done; /* True when expat parser has read to EOF */ /* File-like object reading */ - PyObject* fd; /* Python file object */ - int file; /* C file descriptor */ - PyObject* read; /* The read method on the file object */ - ssize_t buffersize; /* The size of the read buffer */ - XML_Char* buffer; /* The read buffer */ + PyObject *fd; /* Python file object */ + int file; /* C file descriptor */ + PyObject *read; /* The read method on the file object */ + Py_ssize_t buffersize; /* The size of the read buffer */ + XML_Char *buffer; /* The read buffer */ /* Text nodes */ - Py_ssize_t text_alloc; /* The allocated size of the text buffer */ - Py_ssize_t text_size; /* The size of the content in the text buffer */ - XML_Char* text; /* Text buffer (for returning text nodes) */ - int keep_text; /* Flag: keep appending text chunks to the current text node */ + Py_ssize_t text_alloc; /* The allocated size of the text buffer */ + Py_ssize_t text_size; /* The size of the content in the text buffer */ + XML_Char *text; /* Text buffer (for returning text nodes) */ + int keep_text; /* Flag: keep appending text chunks to the current text node */ /* XML event queue */ - PyObject** queue; + PyObject **queue; Py_ssize_t queue_size; Py_ssize_t queue_read_idx; Py_ssize_t queue_write_idx; /* Store the last Python exception so it can be returned when - dequeing events */ - PyObject* error_type; - PyObject* error_value; - PyObject* error_traceback; + dequeuing events */ + PyObject *error_type; + PyObject *error_value; + PyObject *error_traceback; /* Store the position for any XML exceptions that may be returned later */ @@ -116,11 +130,56 @@ typedef struct { unsigned long last_col; /* "Constants" for efficiency */ - PyObject* dict_singleton; /* Empty dict */ - PyObject* td_singleton; /* String "TD" */ - PyObject* read_args; /* (buffersize) */ + PyObject *dict_singleton; /* Empty dict */ + PyObject *td_singleton; /* String "TD" */ + PyObject *read_args; /* (buffersize) */ } IterParser; +/****************************************************************************** + * Tuple queue + ******************************************************************************/ + +/** + * Extend the tuple queue based on the new length of the textual XML input. + * This helps to cope with situations where the input is longer than + * requested (as occurs with transparent decompression of the input + * stream), and for the initial allocation to combine the logic in one place. + */ +static int queue_realloc(IterParser *self, Py_ssize_t req_size) +{ + PyObject **new_queue; + Py_ssize_t n = req_size / 2; + + if (n <= self->queue_size) { + return 0; + } + + new_queue = realloc(self->queue, sizeof(PyObject *) * (size_t)n); + + if (new_queue == NULL) { + PyErr_SetString(PyExc_MemoryError, "Out of memory for XML parsing queue."); + /* + * queue_realloc() is only called from IterParser_init() or + * IterParser_next() in situations where the queue is clear + * and empty. If this function were to be used in other + * situations it would be wise to iterate over the queue and + * clear/decrement the individual references, to save work for + * the garbage collector (in an out-of-memory situation). + */ + goto fail; + } + + self->queue = new_queue; + self->queue_size = n; + return 0; + +fail: + free(self->queue); + self->queue = NULL; + self->queue_size = 0; + return -1; +} + /****************************************************************************** * Text buffer ******************************************************************************/ @@ -129,11 +188,10 @@ typedef struct { * Reallocate text buffer to the next highest power of two that fits the * requested size. */ -static int -text_realloc(IterParser *self, Py_ssize_t req_size) +static int text_realloc(IterParser *self, Py_ssize_t req_size) { - Py_ssize_t n = req_size; - char *new_mem = NULL; + Py_ssize_t n = req_size; + char *new_mem = NULL; if (req_size < self->text_alloc) { return 0; @@ -162,10 +220,9 @@ text_realloc(IterParser *self, Py_ssize_t req_size) return 0; } -#define IS_WHITESPACE(c) ((c) == (XML_Char)0x20 || \ - (c) == (XML_Char)0x0d || \ - (c) == (XML_Char)0x0a || \ - (c) == (XML_Char)0x09) +#define IS_WHITESPACE(c) \ + ((c) == (XML_Char)0x20 || (c) == (XML_Char)0x0d || (c) == (XML_Char)0x0a || \ + (c) == (XML_Char)0x09) /* * Append text to the text buffer. @@ -174,8 +231,7 @@ text_realloc(IterParser *self, Py_ssize_t req_size) * first non-whitespace character are stripped. This saves time * stripping on the Python side later. */ -static int -text_append(IterParser *self, const XML_Char *data, Py_ssize_t len) +static int text_append(IterParser *self, const XML_Char *data, Py_ssize_t len) { Py_ssize_t new_size; @@ -197,9 +253,7 @@ text_append(IterParser *self, const XML_Char *data, Py_ssize_t len) return -1; } - memcpy(self->text + self->text_size, - data, - (size_t)len * sizeof(XML_Char)); + memcpy(self->text + self->text_size, data, (size_t)len * sizeof(XML_Char)); self->text_size = new_size; self->text[self->text_size] = (XML_Char)0x0; @@ -210,8 +264,7 @@ text_append(IterParser *self, const XML_Char *data, Py_ssize_t len) /* * Erase all content from the text buffer. */ -static void -text_clear(IterParser *self) +static void text_clear(IterParser *self) { self->text[0] = (XML_Char)0; self->text_size = 0; @@ -228,22 +281,9 @@ text_clear(IterParser *self) * * It is of the form (line, col), where line and col are both PyInts. */ -static inline PyObject* -make_pos(const IterParser *self) +static inline PyObject *make_pos(const IterParser *self) { - PyObject* tuple; - PyObject* line_obj; - PyObject* col_obj; - - tuple = PyTuple_New(2); - - line_obj = PyLong_FromSize_t((size_t)self->last_line); - col_obj = PyLong_FromSize_t((size_t)self->last_col); - - PyTuple_SetItem(tuple, 0, line_obj); - PyTuple_SetItem(tuple, 1, col_obj); - - return tuple; + return Py_BuildValue("(nn)", (size_t)self->last_line, (size_t)self->last_col); } /* @@ -254,13 +294,11 @@ make_pos(const IterParser *self) * The returned pointer is an internal pointer to the buffer passed * in. */ -static const XML_Char * -remove_namespace(const XML_Char *name) +static const XML_Char *remove_namespace(const XML_Char *name) { - const XML_Char* name_start = NULL; + const XML_Char *name_start = NULL; /* If there is a namespace specifier, just chop it off */ - name_start = name; for (name_start = name; *name_start != '\0'; ++name_start) { if (*name_start == ':') { break; @@ -269,7 +307,8 @@ remove_namespace(const XML_Char *name) if (*name_start == ':') { ++name_start; - } else { + } + else { name_start = name; } @@ -279,16 +318,16 @@ remove_namespace(const XML_Char *name) /* * Handle the expat startElement event. */ -static void -startElement(IterParser *self, const XML_Char *name, const XML_Char **atts) +static void startElement(IterParser *self, const XML_Char *name, const XML_Char **atts) { - PyObject* pyname = NULL; - PyObject* pyatts = NULL; - const XML_Char** att_ptr = atts; - const XML_Char* name_start = NULL; - PyObject* tuple = NULL; - PyObject* key = NULL; - PyObject* val = NULL; + PyObject *pyname = NULL; + PyObject *pyatts = NULL; + const XML_Char **att_ptr = atts; + const XML_Char *name_start = NULL; + PyObject *tuple = NULL; + PyObject *key = NULL; + PyObject *val = NULL; + PyObject *pos = NULL; /* If we've already had an error in a previous call, don't make things worse. */ @@ -301,12 +340,11 @@ startElement(IterParser *self, const XML_Char *name, const XML_Char **atts) if (self->queue_write_idx < self->queue_size) { tuple = PyTuple_New(4); if (tuple == NULL) { - XML_StopParser(self->parser, 0); - return; + goto fail; } Py_INCREF(Py_True); - PyTuple_SET_ITEM(tuple, 0, Py_True); + PyTuple_SetItem(tuple, 0, Py_True); /* This is an egregious but effective optimization. Since by far the most frequently occurring element name in a large @@ -314,92 +352,99 @@ startElement(IterParser *self, const XML_Char *name, const XML_Char **atts) integer comparison to avoid the lookup in the interned string table in PyString_InternFromString, and return a singleton string for "TD" */ - if ((*(int*)name & TD_AS_INT_MASK) == TD_AS_INT) { + if ((*(int *)name & TD_AS_INT_MASK) == TD_AS_INT) { Py_INCREF(self->td_singleton); PyTuple_SetItem(tuple, 1, self->td_singleton); - } else { + } + else { name_start = remove_namespace(name); pyname = PyUnicode_FromString(name_start); if (pyname == NULL) { - Py_DECREF(tuple); - XML_StopParser(self->parser, 0); - return; + goto fail; } PyTuple_SetItem(tuple, 1, pyname); + pyname = NULL; } if (*att_ptr) { pyatts = PyDict_New(); if (pyatts == NULL) { - Py_DECREF(tuple); - XML_StopParser(self->parser, 0); - return; + goto fail; } do { - if (*(*(att_ptr + 1)) != 0) { - key = PyUnicode_FromString(*att_ptr); - if (key == NULL) { - Py_DECREF(tuple); - XML_StopParser(self->parser, 0); - return; - } - val = PyUnicode_FromString(*(att_ptr + 1)); - if (val == NULL) { - Py_DECREF(key); - Py_DECREF(tuple); - XML_StopParser(self->parser, 0); - return; - } - if (PyDict_SetItem(pyatts, key, val)) { - Py_DECREF(pyatts); - Py_DECREF(tuple); - Py_DECREF(key); - Py_DECREF(val); - XML_StopParser(self->parser, 0); - return; - } + key = PyUnicode_FromString(*att_ptr); + if (key == NULL) { + goto fail; + } + val = PyUnicode_FromString(*(att_ptr + 1)); + if (val == NULL) { + Py_DECREF(key); + goto fail; + } + if (PyDict_SetItem(pyatts, key, val)) { Py_DECREF(key); Py_DECREF(val); + goto fail; } + Py_DECREF(key); + Py_DECREF(val); + key = val = NULL; + att_ptr += 2; } while (*att_ptr); - } else { + } + else { Py_INCREF(self->dict_singleton); pyatts = self->dict_singleton; } PyTuple_SetItem(tuple, 2, pyatts); + pyatts = NULL; + + self->last_line = (unsigned long)XML_GetCurrentLineNumber(self->parser); + self->last_col = (unsigned long)XML_GetCurrentColumnNumber(self->parser); - self->last_line = (unsigned long)XML_GetCurrentLineNumber( - self->parser); - self->last_col = (unsigned long)XML_GetCurrentColumnNumber( - self->parser); - PyTuple_SetItem(tuple, 3, make_pos(self)); + pos = make_pos(self); + if (pos == NULL) { + goto fail; + } + PyTuple_SetItem(tuple, 3, pos); + pos = NULL; text_clear(self); self->keep_text = 1; self->queue[self->queue_write_idx++] = tuple; - } else { + } + else { PyErr_SetString( PyExc_RuntimeError, - "XML queue overflow in startElement. This most likely indicates an internal bug."); + "XML queue overflow in startElement. This most likely indicates an internal bug." + ); + goto fail; } + + return; + +fail: + Py_XDECREF(tuple); + Py_XDECREF(pyatts); + XML_StopParser(self->parser, 0); } /* * Handle the expat endElement event. */ -static void -endElement(IterParser *self, const XML_Char *name) +static void endElement(IterParser *self, const XML_Char *name) { - PyObject* pyname = NULL; - PyObject* tuple = NULL; - PyObject* pytext = NULL; - const XML_Char* name_start = NULL; - XML_Char* end; + PyObject *pyname = NULL; + PyObject *tuple = NULL; + PyObject *pytext = NULL; + const XML_Char *name_start = NULL; + XML_Char *end; + PyObject *pos = NULL; /* If we've already had an error in a previous call, don't make things worse. */ @@ -412,12 +457,11 @@ endElement(IterParser *self, const XML_Char *name) if (self->queue_write_idx < self->queue_size) { tuple = PyTuple_New(4); if (tuple == NULL) { - XML_StopParser(self->parser, 0); - return; + goto fail; } Py_INCREF(Py_False); - PyTuple_SET_ITEM(tuple, 0, Py_False); + PyTuple_SetItem(tuple, 0, Py_False); /* This is an egregious but effective optimization. Since by far the most frequently occurring element name in a large @@ -425,19 +469,19 @@ endElement(IterParser *self, const XML_Char *name) integer comparison to avoid the lookup in the interned string table in PyString_InternFromString, and return a singleton string for "TD" */ - if ((*(int*)name & TD_AS_INT_MASK) == TD_AS_INT) { + if ((*(int *)name & TD_AS_INT_MASK) == TD_AS_INT) { Py_INCREF(self->td_singleton); PyTuple_SetItem(tuple, 1, self->td_singleton); - } else { + } + else { name_start = remove_namespace(name); pyname = PyUnicode_FromString(name_start); if (pyname == NULL) { - Py_DECREF(tuple); - XML_StopParser(self->parser, 0); - return; + goto fail; } PyTuple_SetItem(tuple, 1, pyname); + pyname = NULL; } /* Cut whitespace off the end of the string */ @@ -446,31 +490,44 @@ endElement(IterParser *self, const XML_Char *name) --end; --self->text_size; } + pytext = PyUnicode_FromStringAndSize(self->text, self->text_size); if (pytext == NULL) { - Py_DECREF(tuple); - XML_StopParser(self->parser, 0); - return; + goto fail; } PyTuple_SetItem(tuple, 2, pytext); + pytext = NULL; - PyTuple_SetItem(tuple, 3, make_pos(self)); + pos = make_pos(self); + if (pos == NULL) { + goto fail; + } + PyTuple_SetItem(tuple, 3, pos); + pos = NULL; self->keep_text = 0; self->queue[self->queue_write_idx++] = tuple; - } else { + } + else { PyErr_SetString( PyExc_RuntimeError, - "XML queue overflow in endElement. This most likely indicates an internal bug."); + "XML queue overflow in endElement. This most likely indicates an internal bug." + ); + goto fail; } + + return; + +fail: + Py_XDECREF(tuple); + XML_StopParser(self->parser, 0); } /* * Handle the expat characterData event. */ -static void -characterData(IterParser *self, const XML_Char *text, int len) +static void characterData(IterParser *self, const XML_Char *text, int len) { /* If we've already had an error in a previous call, don't make things worse. */ @@ -480,10 +537,8 @@ characterData(IterParser *self, const XML_Char *text, int len) } if (self->text_size == 0) { - self->last_line = (unsigned long)XML_GetCurrentLineNumber( - self->parser); - self->last_col = (unsigned long)XML_GetCurrentColumnNumber( - self->parser); + self->last_line = (unsigned long)XML_GetCurrentLineNumber(self->parser); + self->last_col = (unsigned long)XML_GetCurrentColumnNumber(self->parser); } if (self->keep_text) { @@ -494,72 +549,109 @@ characterData(IterParser *self, const XML_Char *text, int len) /* * Handle the XML declaration so that we can determine its encoding. */ -static void -xmlDecl(IterParser *self, const XML_Char *version, - const XML_Char *encoding, int standalone) +static void xmlDecl( + IterParser *self, const XML_Char *version, const XML_Char *encoding, int standalone +) { - PyObject* tuple = NULL; - PyObject* xml_str = NULL; - PyObject* attrs = NULL; - PyObject* encoding_str = NULL; - PyObject* version_str = NULL; + PyObject *tuple = NULL; + PyObject *xml_str = NULL; + PyObject *attrs = NULL; + PyObject *encoding_str = NULL; + PyObject *version_str = NULL; + PyObject *pos = NULL; if (self->queue_write_idx < self->queue_size) { tuple = PyTuple_New(4); if (tuple == NULL) { - XML_StopParser(self->parser, 0); - return; + goto fail; } Py_INCREF(Py_True); - PyTuple_SET_ITEM(tuple, 0, Py_True); + PyTuple_SetItem(tuple, 0, Py_True); xml_str = PyUnicode_FromString("xml"); - PyTuple_SET_ITEM(tuple, 1, xml_str); + if (xml_str == NULL) { + goto fail; + } + PyTuple_SetItem(tuple, 1, xml_str); + xml_str = NULL; attrs = PyDict_New(); + if (attrs == NULL) { + goto fail; + } if (encoding) { encoding_str = PyUnicode_FromString(encoding); - } else { + } + else { encoding_str = PyUnicode_FromString(""); } - PyDict_SetItemString(attrs, "encoding", encoding_str); + if (encoding_str == NULL) { + goto fail; + } + if (PyDict_SetItemString(attrs, "encoding", encoding_str)) { + Py_DECREF(encoding_str); + goto fail; + } Py_DECREF(encoding_str); + encoding_str = NULL; if (version) { version_str = PyUnicode_FromString(version); - } else { + } + else { version_str = PyUnicode_FromString(""); } - PyDict_SetItemString(attrs, "version", version_str); + if (version_str == NULL) { + goto fail; + } + if (PyDict_SetItemString(attrs, "version", version_str)) { + Py_DECREF(version_str); + goto fail; + } Py_DECREF(version_str); + version_str = NULL; + + PyTuple_SetItem(tuple, 2, attrs); + attrs = NULL; - PyTuple_SET_ITEM(tuple, 2, attrs); + self->last_line = (unsigned long)XML_GetCurrentLineNumber(self->parser); + self->last_col = (unsigned long)XML_GetCurrentColumnNumber(self->parser); - self->last_line = (unsigned long)XML_GetCurrentLineNumber( - self->parser); - self->last_col = (unsigned long)XML_GetCurrentColumnNumber( - self->parser); - PyTuple_SET_ITEM(tuple, 3, make_pos(self)); + pos = make_pos(self); + if (pos == NULL) { + goto fail; + } + PyTuple_SetItem(tuple, 3, pos); + pos = NULL; self->queue[self->queue_write_idx++] = tuple; - } else { + } + else { PyErr_SetString( PyExc_RuntimeError, - "XML queue overflow in xmlDecl. This most likely indicates an internal bug."); + "XML queue overflow in xmlDecl. This most likely indicates an internal bug." + ); + goto fail; } + + return; + +fail: + Py_XDECREF(tuple); + Py_XDECREF(attrs); + XML_StopParser(self->parser, 0); } /* * The object itself is an iterator, just return self for "iter(self)" * on the Python side. */ -static PyObject * -IterParser_iter(IterParser* self) +static PyObject *IterParser_iter(IterParser *self) { - Py_INCREF(self); - return (PyObject*) self; + Py_INCREF((PyObject *)self); + return (PyObject *)self; } /* @@ -573,11 +665,10 @@ IterParser_iter(IterParser* self) * later thrown once the queue is emptied, otherwise the exception is * raised "too early" in queue order. */ -static PyObject * -IterParser_next(IterParser* self) +static PyObject *IterParser_next(IterParser *self) { - PyObject* data = NULL; - XML_Char* buf; + PyObject *data = NULL; + XML_Char *buf; Py_ssize_t buflen; /* Is there anything in the queue to return? */ @@ -618,23 +709,31 @@ IterParser_next(IterParser* self) } if (buflen < self->buffersize) { + /* EOF detection method only works for local regular files */ self->done = 1; } - /* Handle a real C file descriptor or handle -- this is faster + /* Handle a real C file descriptor or handle -- this is faster if we've got one. */ - } else { - buflen = (Py_ssize_t)read( - self->file, self->buffer, (size_t)self->buffersize); + } + else { + buflen = (Py_ssize_t)read(self->file, self->buffer, (size_t)self->buffersize); if (buflen == -1) { - PyErr_SetFromErrno(PyExc_IOError); + PyErr_SetFromErrno(PyExc_OSError); goto fail; - } else if (buflen < self->buffersize) { + } + else if (buflen < self->buffersize) { + /* EOF detection method only works for local regular files */ self->done = 1; } buf = self->buffer; } + if (queue_realloc(self, buflen)) { + Py_XDECREF(data); + goto fail; + } + /* Feed the read buffer to expat, which will call the event handlers */ if (XML_Parse(self->parser, buf, (int)buflen, self->done) == XML_STATUS_ERROR) { /* One of the event handlers raised a Python error, make @@ -648,10 +747,12 @@ IterParser_next(IterParser* self) until the queue is emptied. */ Py_XDECREF(data); PyErr_Format( - PyExc_ValueError, "%lu:%lu: %s", + PyExc_ValueError, + "%lu:%lu: %s", XML_GetCurrentLineNumber(self->parser), XML_GetCurrentColumnNumber(self->parser), - XML_ErrorString(XML_GetErrorCode(self->parser))); + XML_ErrorString(XML_GetErrorCode(self->parser)) + ); goto fail; } Py_XDECREF(data); @@ -667,14 +768,14 @@ IterParser_next(IterParser* self) if (self->queue_write_idx >= self->queue_size) { PyErr_SetString( - PyExc_RuntimeError, - "XML queue overflow. This most likely indicates an internal bug."); + PyExc_RuntimeError, "XML queue overflow. This most likely indicates an internal bug." + ); return NULL; } return self->queue[self->queue_read_idx++]; - fail: +fail: /* We got an exception somewhere along the way. Store the exception in the IterParser object, but clear the exception in the Python interpreter, so we can empty the event queue and raise the exception later. */ @@ -698,64 +799,84 @@ IterParser_next(IterParser* self) /* To support cyclical garbage collection, all PyObject's must be visited. */ -static int -IterParser_traverse(IterParser *self, visitproc visit, void *arg) +static int IterParser_traverse(IterParser *self, visitproc visit, void *arg) { int vret; Py_ssize_t read_index; + // Heap types must visit their type + // see https://docs.python.org/3/c-api/typeobj.html#c.PyTypeObject.tp_traverse + Py_VISIT((PyObject *)Py_TYPE((PyObject *)self)); + read_index = self->queue_read_idx; while (read_index < self->queue_write_idx) { vret = visit(self->queue[read_index++], arg); - if (vret != 0) return vret; + if (vret != 0) { + return vret; + } } if (self->fd) { vret = visit(self->fd, arg); - if (vret != 0) return vret; + if (vret != 0) { + return vret; + } } if (self->read) { vret = visit(self->read, arg); - if (vret != 0) return vret; + if (vret != 0) { + return vret; + } } if (self->read_args) { vret = visit(self->read_args, arg); - if (vret != 0) return vret; + if (vret != 0) { + return vret; + } } if (self->dict_singleton) { vret = visit(self->dict_singleton, arg); - if (vret != 0) return vret; + if (vret != 0) { + return vret; + } } if (self->td_singleton) { vret = visit(self->td_singleton, arg); - if (vret != 0) return vret; + if (vret != 0) { + return vret; + } } if (self->error_type) { vret = visit(self->error_type, arg); - if (vret != 0) return vret; + if (vret != 0) { + return vret; + } } if (self->error_value) { vret = visit(self->error_value, arg); - if (vret != 0) return vret; + if (vret != 0) { + return vret; + } } if (self->error_traceback) { vret = visit(self->error_traceback, arg); - if (vret != 0) return vret; + if (vret != 0) { + return vret; + } } return 0; } /* To support cyclical garbage collection */ -static int -IterParser_clear(IterParser *self) +static int IterParser_clear(IterParser *self) { PyObject *tmp; @@ -805,52 +926,57 @@ IterParser_clear(IterParser *self) * Deallocate the IterParser object. For the internal PyObject*, just * punt to IterParser_clear. */ -static void -IterParser_dealloc(IterParser* self) +static void IterParser_dealloc(IterParser *self) { IterParser_clear(self); - free(self->buffer); self->buffer = NULL; - free(self->queue); self->queue = NULL; - free(self->text); self->text = NULL; + free(self->buffer); + self->buffer = NULL; + free(self->queue); + self->queue = NULL; + free(self->text); + self->text = NULL; if (self->parser != NULL) { XML_ParserFree(self->parser); self->parser = NULL; } - Py_TYPE(self)->tp_free((PyObject*)self); + PyTypeObject *type = Py_TYPE((PyObject *)self); + freefunc free_func = PyType_GetSlot(type, Py_tp_free); + free_func((PyObject *)self); } /* * Initialize the memory for an IterParser object */ -static PyObject * -IterParser_new(PyTypeObject *type, PyObject *args, PyObject *kwds) + +static PyObject *IterParser_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { IterParser *self = NULL; - self = (IterParser *)type->tp_alloc(type, 0); + allocfunc alloc_func = PyType_GetSlot(type, Py_tp_alloc); + self = (IterParser *)alloc_func(type, 0); if (self != NULL) { - self->parser = NULL; - self->fd = NULL; - self->file = -1; - self->read = NULL; - self->read_args = NULL; - self->dict_singleton = NULL; - self->td_singleton = NULL; - self->buffersize = 0; - self->buffer = NULL; - self->queue_read_idx = 0; + self->parser = NULL; + self->fd = NULL; + self->file = -1; + self->read = NULL; + self->read_args = NULL; + self->dict_singleton = NULL; + self->td_singleton = NULL; + self->buffersize = 0; + self->buffer = NULL; + self->queue_read_idx = 0; self->queue_write_idx = 0; - self->text_alloc = 0; - self->text_size = 0; - self->text = NULL; - self->keep_text = 0; - self->done = 0; - self->queue_size = 0; - self->queue = NULL; - self->error_type = NULL; - self->error_value = NULL; + self->text_alloc = 0; + self->text_size = 0; + self->text = NULL; + self->keep_text = 0; + self->done = 0; + self->queue_size = 0; + self->queue = NULL; + self->error_type = NULL; + self->error_value = NULL; self->error_traceback = NULL; } @@ -865,21 +991,21 @@ IterParser_new(PyTypeObject *type, PyObject *args, PyObject *kwds) * *fd*: A Python file object or a callable object * *buffersize*: The size of the read buffer */ -static int -IterParser_init(IterParser *self, PyObject *args, PyObject *kwds) +static int IterParser_init(IterParser *self, PyObject *args, PyObject *kwds) { - PyObject* fd = NULL; - PyObject* read = NULL; - ssize_t buffersize = 1 << 14; + PyObject *fd = NULL; + PyObject *read = NULL; + Py_ssize_t buffersize = 1 << 14; static char *kwlist[] = {"fd", "buffersize", NULL}; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|n:IterParser.__init__", kwlist, - &fd, &buffersize)) { + if (!PyArg_ParseTupleAndKeywords( + args, kwds, "O|n:IterParser.__init__", kwlist, &fd, &buffersize + )) { return -1; } /* Keep the buffersize within a reasonable range */ - self->buffersize = CLAMP(buffersize, (ssize_t)(1 << 10), (ssize_t)(1 << 24)); + self->buffersize = CLAMP(buffersize, (Py_ssize_t)(1 << 10), (Py_ssize_t)(1 << 24)); #ifdef __clang__ /* Clang can't handle the file descriptors Python gives us, so in that case, we just call the object's read method. */ @@ -898,26 +1024,31 @@ IterParser_init(IterParser *self, PyObject *args, PyObject *kwds) PyErr_SetString(PyExc_MemoryError, "Out of memory"); goto fail; } - self->fd = fd; Py_INCREF(self->fd); + self->fd = fd; + Py_INCREF(self->fd); lseek(self->file, 0, SEEK_SET); - } else + } + else #endif if (PyCallable_Check(fd)) { /* fd is a Python callable */ - self->fd = fd; Py_INCREF(self->fd); - self->read = fd; Py_INCREF(self->read); - } else { + self->fd = fd; + Py_INCREF(self->fd); + self->read = fd; + Py_INCREF(self->read); + } + else { PyErr_SetString( - PyExc_TypeError, - "Arg 1 to iterparser must be a file object or callable object"); + PyExc_TypeError, "Arg 1 to iterparser must be a file object or callable object" + ); goto fail; } PyErr_Clear(); - self->queue_read_idx = 0; + self->queue_read_idx = 0; self->queue_write_idx = 0; - self->done = 0; + self->done = 0; self->text = malloc((size_t)buffersize * sizeof(XML_Char)); self->text_alloc = buffersize; @@ -927,7 +1058,7 @@ IterParser_init(IterParser *self, PyObject *args, PyObject *kwds) } text_clear(self); - self->read_args = PyTuple_Pack(1, PyLong_FromSsize_t(buffersize)); + self->read_args = Py_BuildValue("(n)", buffersize); if (self->read_args == NULL) { goto fail; } @@ -942,10 +1073,7 @@ IterParser_init(IterParser *self, PyObject *args, PyObject *kwds) goto fail; } - self->queue_size = buffersize / 2; - self->queue = malloc(sizeof(PyObject*) * (size_t)self->queue_size); - if (self->queue == NULL) { - PyErr_SetString(PyExc_MemoryError, "Out of memory"); + if (queue_realloc(self, buffersize)) { goto fail; } @@ -957,21 +1085,16 @@ IterParser_init(IterParser *self, PyObject *args, PyObject *kwds) } XML_SetUserData(self->parser, self); XML_SetElementHandler( - self->parser, - (XML_StartElementHandler)startElement, - (XML_EndElementHandler)endElement); - XML_SetCharacterDataHandler( - self->parser, - (XML_CharacterDataHandler)characterData); - XML_SetXmlDeclHandler( - self->parser, - (XML_XmlDeclHandler)xmlDecl); + self->parser, (XML_StartElementHandler)startElement, (XML_EndElementHandler)endElement + ); + XML_SetCharacterDataHandler(self->parser, (XML_CharacterDataHandler)characterData); + XML_SetXmlDeclHandler(self->parser, (XML_XmlDeclHandler)xmlDecl); Py_XDECREF(read); return 0; - fail: +fail: Py_XDECREF(read); Py_XDECREF(self->fd); Py_XDECREF(self->read); @@ -984,85 +1107,117 @@ IterParser_init(IterParser *self, PyObject *args, PyObject *kwds) return -1; } -static PyMemberDef IterParser_members[] = -{ - {NULL} /* Sentinel */ +static PyMemberDef IterParser_members[] = { + {NULL} /* Sentinel */ }; -static PyMethodDef IterParser_methods[] = -{ - {NULL} /* Sentinel */ +static PyMethodDef IterParser_methods[] = { + {NULL} /* Sentinel */ }; -static PyTypeObject IterParserType = -{ - #ifdef IS_PY3K - PyVarObject_HEAD_INIT(NULL, 0) - #else - PyObject_HEAD_INIT(NULL) - 0, /*ob_size*/ - #endif - "astropy.utils.xml._iterparser.IterParser", /*tp_name*/ - sizeof(IterParser), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - (destructor)IterParser_dealloc, /*tp_dealloc*/ - 0, /*tp_print*/ - 0, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_compare*/ - 0, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - 0, /*tp_hash */ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /*tp_flags*/ - "IterParser objects", /* tp_doc */ - (traverseproc)IterParser_traverse, /* tp_traverse */ - (inquiry)IterParser_clear, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - (getiterfunc)IterParser_iter, /* tp_iter */ - (iternextfunc)IterParser_next, /* tp_iternext */ - IterParser_methods, /* tp_methods */ - IterParser_members, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - (initproc)IterParser_init, /* tp_init */ - 0, /* tp_alloc */ - IterParser_new, /* tp_new */ +static PyType_Spec IterParserType_spec = { + .name = "astropy.utils.xml._iterparser.IterParser", + .basicsize = sizeof(IterParser), + .itemsize = 0, + .flags = + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_IMMUTABLETYPE, + .slots = (PyType_Slot[]){ + {Py_tp_dealloc, (destructor)IterParser_dealloc}, + {Py_tp_doc, "IterParser objects"}, + {Py_tp_traverse, (traverseproc)IterParser_traverse}, + {Py_tp_clear, (inquiry)IterParser_clear}, + {Py_tp_iter, (getiterfunc)IterParser_iter}, + {Py_tp_iternext, (iternextfunc)IterParser_next}, + {Py_tp_methods, IterParser_methods}, + {Py_tp_members, IterParser_members}, + {Py_tp_init, (initproc)IterParser_init}, + {Py_tp_new, IterParser_new}, + {0, NULL}, + }, }; +PyObject *IterParserType = NULL; + /****************************************************************************** * XML escaping ******************************************************************************/ /* These are in reverse order by input character */ -static const char* escapes_cdata[] = { - ">", ">", - "<", "<", - "&", "&", - "\0", "\0", +static const char *escapes_cdata[] = { + ">", + ">", + "<", + "<", + "&", + "&", + "\0", + "\0", }; /* These are in reverse order by input character */ -static const char* escapes[] = { - ">", ">", - "<", "<", - "'", "'", - "&", "&", - "\"", """, - "\0", "\0" +static const char *escapes[] = { + ">", ">", "<", "<", "'", "'", "&", "&", "\"", """, "\0", "\0" }; +/* Implementation of escape_xml. + * + * Returns: + * * 0 : No need to escape + * * >0 : output is escaped + * * -1 : error + */ +static Py_ssize_t _escape_xml_impl( + const char *input, Py_ssize_t input_len, char **output, const char **escapes +) +{ + Py_ssize_t i; + int count = 0; + char *p = NULL; + const char **esc; + const char *ent; + + for (i = 0; i < input_len; ++i) { + for (esc = escapes;; esc += 2) { + if ((unsigned char)input[i] > **esc) { + break; + } + else if (input[i] == **esc) { + ++count; + break; + } + } + } + + if (!count) { + return 0; + } + + p = malloc((input_len + 1 + count * 5) * sizeof(char)); + if (p == NULL) { + PyErr_SetString(PyExc_MemoryError, "Out of memory"); + return -1; + } + *output = p; + + for (i = 0; i < input_len; ++i) { + for (esc = escapes;; esc += 2) { + if ((unsigned char)input[i] > **esc) { + *(p++) = input[i]; + break; + } + else if (input[i] == **esc) { + for (ent = *(esc + 1); *ent != '\0'; ++ent) { + *(p++) = *ent; + } + break; + } + } + } + + *p = 0; + return p - *output; +} + /* * Returns a copy of the given string (8-bit or Unicode) with the XML * control characters converted to XML character entities. @@ -1070,168 +1225,104 @@ static const char* escapes[] = { * If an 8-bit string is passed in, an 8-bit string is returned. If a * Unicode string is passed in, a Unicode string is returned. */ -static PyObject* -_escape_xml(PyObject* self, PyObject *args, PyObject *kwds, - const char** escapes) +static PyObject *_escape_xml(PyObject *self, PyObject *args, const char **escapes) { - PyObject* input_obj; - PyObject* output_obj; - int count = 0; - Py_UNICODE* uinput = NULL; - char* input = NULL; + PyObject *input_obj; + PyObject *input_coerce = NULL; + PyObject *output_obj; + char *input = NULL; Py_ssize_t input_len; - Py_UNICODE* uoutput = NULL; - char* output = NULL; - Py_UNICODE* up = NULL; - char* p = NULL; - Py_ssize_t i; - const char** esc; - const char* ent; + char *output = NULL; + Py_ssize_t output_len; if (!PyArg_ParseTuple(args, "O:escape_xml", &input_obj)) { return NULL; } - if (PyUnicode_Check(input_obj)) { - uinput = PyUnicode_AsUnicode(input_obj); - if (uinput == NULL) { + /* First, try as Unicode */ + if (!PyBytes_Check(input_obj)) { + input_coerce = PyObject_Str(input_obj); + } + if (input_coerce) { + input = (char *)PyUnicode_AsUTF8AndSize(input_coerce, &input_len); + if (input == NULL) { + Py_DECREF(input_coerce); return NULL; } - input_len = PyUnicode_GetSize(input_obj); - - for (i = 0; i < input_len; ++i) { - for (esc = escapes; ; esc += 2) { - if (uinput[i] > (Py_UNICODE)**esc) { - break; - } else if (uinput[i] == (Py_UNICODE)**esc) { - ++count; - break; - } - } + output_len = _escape_xml_impl(input, input_len, &output, escapes); + if (output_len < 0) { + Py_DECREF(input_coerce); + return NULL; } - - if (count) { - uoutput = malloc((input_len + 1 + count * 5) * sizeof(Py_UNICODE)); - if (uoutput == NULL) { - PyErr_SetString(PyExc_MemoryError, "Out of memory"); - return NULL; - } - - up = uoutput; - for (i = 0; i < input_len; ++i) { - for (esc = escapes; ; esc += 2) { - if (uinput[i] > (Py_UNICODE)**esc) { - *(up++) = uinput[i]; - break; - } else if (uinput[i] == (Py_UNICODE)**esc) { - for (ent = *(esc + 1); *ent != '\0'; ++ent) { - *(up++) = (Py_UNICODE)*ent; - } - break; - } - } - } - - *up = 0; - - output_obj = PyUnicode_FromUnicode(uoutput, up - uoutput); - free(uoutput); + if (output_len > 0) { + Py_DECREF(input_coerce); + output_obj = PyUnicode_FromStringAndSize(output, output_len); + free(output); return output_obj; } - } else if (PyBytes_Check(input_obj)) { - if (PyBytes_AsStringAndSize(input_obj, &input, &input_len) == -1) { + return input_coerce; + } + + /* Now try as bytes */ + input_coerce = PyObject_Bytes(input_obj); + if (input_coerce) { + if (PyBytes_AsStringAndSize(input_coerce, &input, &input_len) == -1) { + Py_DECREF(input_coerce); return NULL; } - for (i = 0; i < input_len; ++i) { - for (esc = escapes; ; esc += 2) { - if (input[i] > **esc) { - break; - } else if (input[i] == **esc) { - ++count; - break; - } - } + output_len = _escape_xml_impl(input, input_len, &output, escapes); + if (output_len < 0) { + Py_DECREF(input_coerce); + return NULL; } - - if (count) { - output = malloc((input_len + 1 + count * 5) * sizeof(char)); - if (output == NULL) { - PyErr_SetString(PyExc_MemoryError, "Out of memory"); - return NULL; - } - - p = output; - for (i = 0; i < input_len; ++i) { - for (esc = escapes; ; esc += 2) { - if (input[i] > **esc) { - *(p++) = input[i]; - break; - } else if (input[i] == **esc) { - for (ent = *(esc + 1); *ent != '\0'; ++ent) { - *(p++) = *ent; - } - break; - } - } - } - - *p = 0; - - output_obj = PyBytes_FromStringAndSize(output, p - output); + if (output_len > 0) { + Py_DECREF(input_coerce); + output_obj = PyBytes_FromStringAndSize(output, output_len); free(output); return output_obj; } - } else { - #ifdef IS_PY3K - PyErr_SetString(PyExc_TypeError, "must be str or bytes"); - #else - PyErr_SetString(PyExc_TypeError, "must be str or unicode"); - #endif - return NULL; + return input_coerce; } - Py_INCREF(input_obj); - return input_obj; + PyErr_SetString(PyExc_TypeError, "must be convertible to str or bytes"); + return NULL; } -static PyObject* -escape_xml(PyObject* self, PyObject *args, PyObject *kwds) +static PyObject *escape_xml(PyObject *self, PyObject *args) { - return _escape_xml(self, args, kwds, escapes); + return _escape_xml(self, args, escapes); } -static PyObject* -escape_xml_cdata(PyObject* self, PyObject *args, PyObject *kwds) +static PyObject *escape_xml_cdata(PyObject *self, PyObject *args) { - return _escape_xml(self, args, kwds, escapes_cdata); + return _escape_xml(self, args, escapes_cdata); } /****************************************************************************** * Module setup ******************************************************************************/ -static PyMethodDef module_methods[] = -{ - {"escape_xml", (PyCFunction)escape_xml, METH_VARARGS, - "Fast method to escape XML strings"}, - {"escape_xml_cdata", (PyCFunction)escape_xml_cdata, METH_VARARGS, +static PyMethodDef module_methods[] = { + {"escape_xml", (PyCFunction)escape_xml, METH_VARARGS, "Fast method to escape XML strings"}, + {"escape_xml_cdata", + (PyCFunction)escape_xml_cdata, + METH_VARARGS, "Fast method to escape XML strings"}, - {NULL} /* Sentinel */ + {NULL} /* Sentinel */ }; struct module_state { - void* none; + void *none; }; -#ifdef IS_PY3K -static int module_traverse(PyObject* m, visitproc visit, void* arg) +static int module_traverse(PyObject *m, visitproc visit, void *arg) { return 0; } -static int module_clear(PyObject* m) +static int module_clear(PyObject *m) { return 0; } @@ -1248,39 +1339,21 @@ static struct PyModuleDef moduledef = { NULL }; -# define INITERROR return NULL - -PyMODINIT_FUNC -PyInit__iterparser(void) -#else /* Not PY3K */ -# define INITERROR return - -# ifndef PyMODINIT_FUNC /* declarations for DLL import/export */ -# define PyMODINIT_FUNC void -# endif - -PyMODINIT_FUNC -init_iterparser(void) -#endif +PyMODINIT_FUNC PyInit__iterparser(void) { - PyObject* m; - -#ifdef IS_PY3K + PyObject *m; m = PyModule_Create(&moduledef); -#else - m = Py_InitModule3("_iterparser", module_methods, "Fast XML parser"); -#endif - if (m == NULL) - INITERROR; + if (m == NULL) { + return NULL; + } - if (PyType_Ready(&IterParserType) < 0) - INITERROR; + IterParserType = PyType_FromModuleAndSpec(m, &IterParserType_spec, NULL); + if (IterParserType == NULL) { + return NULL; + } - Py_INCREF(&IterParserType); - PyModule_AddObject(m, "IterParser", (PyObject *)&IterParserType); + PyModule_AddObject(m, "IterParser", IterParserType); -#ifdef IS_PY3K return m; -#endif } diff --git a/astropy/utils/xml/tests/__init__.py b/astropy/utils/xml/tests/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/astropy/utils/xml/tests/test_iterparse.py b/astropy/utils/xml/tests/test_iterparse.py new file mode 100644 index 000000000000..2156f8ceb04c --- /dev/null +++ b/astropy/utils/xml/tests/test_iterparse.py @@ -0,0 +1,132 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +# LOCAL +# SYSTEM +import io +import zlib + +from astropy.utils.xml.iterparser import _fast_iterparse + +# The C-based XML parser for VOTables previously used fixed-sized +# buffers (allocated at __init__() time). This test will +# only pass with the patch that allows a dynamic realloc() of +# the queue. This addresses the bugs: +# +# - "RuntimeError: XML queue overflow" +# https://github.com/astropy/astropy/issues/5824 +# (Kudos to Stefan Becker---ARI/ZAH Heidelberg) +# +# - "iterparse.c: add queue_realloc() + move 'buffersize / 2' logic there" +# https://github.com/astropy/astropy/issues/5869 +# +# This test code can emulate a combination of network buffering and +# gzip decompression---with different request sizes, it can be used to +# demonstrate both under-reading and over-reading. +# +# Using the 512-tag VOTABLE XML sample input, and various combinations +# of minimum/maximum fetch sizes, the following situations can be +# generated: +# +# maximum_fetch = 1 (ValueError, no element found) still within gzip headers +# maximum_fetch = 80 (ValueError, unclosed token) short read +# maximum_fetch =217 passes, because decompressed_length > requested +# && <512 tags in a single parse +# maximum_fetch =218 (RuntimeError, XML queue overflow) +# +# The test provided here covers the over-reading identified in #5824 +# (equivalent to the 217). + +# Firstly, assemble a minimal VOTABLE header, table contents and footer. +# This is done in textual form, as the aim is to only test the parser, not +# the outputter! +HEADER = """ + + + + + + +""" + +ROW = """ +""" + +FOOTER = """ + + +
0
+
+
+""" + +# minimum passable buffer size => 1024 +# 1024 / 2 => 512 tags for overflow +# 512 - 7 tags in header, - 5 tags in footer = 500 tags required for overflow +# 500 / 4 tags () per row == 125 rows required for overflow +VOTABLE_XML = HEADER + 125 * ROW + FOOTER + +# UngzipFileWrapper() wraps an existing file-like Object, +# decompressing the content and returning the plaintext. +# This therefore emulates the behavior of the Python 'requests' +# library when transparently decompressing Gzip HTTP responses. +# +# The critical behavior is that---because of the +# decompression---read() can return considerably more +# bytes than were requested! (But, read() can also return less). +# +# inspiration: +# http://stackoverflow.com/questions/4013843/how-to-wrap-file-object-read-and-write-operation-which-are-readonly + + +class UngzipFileWrapper: + def __init__(self, fd, **kwargs): + self._file = fd + self._z = zlib.decompressobj(16 + zlib.MAX_WBITS) + + def read(self, requested_length): + # emulate network buffering dynamics by clamping the read size + clamped_length = max(1, min(1 << 24, requested_length)) + compressed = self._file.read(clamped_length) + plaintext = self._z.decompress(compressed) + # Only for real local files---just for the testcase + if len(compressed) == 0: + self.close() + return plaintext + + def __getattr__(self, attr): + return getattr(self._file, attr) + + +# test_iterparser_over_read_simple() is a very cut down test, +# of the original more flexible test-case, but without external +# dependencies. The plaintext is compressed and then decompressed +# to provide a better emulation of the original situation where +# the bug was observed. +# +# If a dependency upon 'zlib' is not desired, it would be possible to +# simplify this testcase by replacing the compress/decompress with a +# read() method emulation that always returned more from a buffer +# that was requested. + + +def test_iterparser_over_read_simple(): + # Take the plaintext of 512 tags, and compression it with a + # Gzip-style header (+16), to most closely emulate the behavior + # of most HTTP servers. + zlib_GZIP_STYLE_HEADER = 16 + compo = zlib.compressobj( + zlib.Z_BEST_COMPRESSION, zlib.DEFLATED, zlib.MAX_WBITS + zlib_GZIP_STYLE_HEADER + ) + + # Bytes vs. String .encode()/.decode() for compatibility with Python 3.5. + s = compo.compress(VOTABLE_XML.encode()) + s = s + compo.flush() + fd = io.BytesIO(s) + fd.seek(0) + + # Finally setup the test of the C-based '_fast_iterparse()' iterator + # and a situation in which it can be called a-la the VOTable Parser. + MINIMUM_REQUESTABLE_BUFFER_SIZE = 1024 + uncompressed_fd = UngzipFileWrapper(fd) + iterable = _fast_iterparse(uncompressed_fd.read, MINIMUM_REQUESTABLE_BUFFER_SIZE) + list(iterable) diff --git a/astropy/utils/xml/unescaper.py b/astropy/utils/xml/unescaper.py new file mode 100644 index 000000000000..93b1d43e1284 --- /dev/null +++ b/astropy/utils/xml/unescaper.py @@ -0,0 +1,63 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +"""URL unescaper functions.""" + +# STDLIB +from xml.sax import saxutils + +__all__ = ["unescape_all"] + +# This is DIY +_bytes_entities = { + b"&": b"&", + b"<": b"<", + b">": b">", + b"&&": b"&", + b"&&": b"&", + b"%2F": b"/", +} +_bytes_keys = [b"&&", b"&&", b"&", b"<", b">", b"%2F"] + +# This is used by saxutils +_str_entities = {"&&": "&", "&&": "&", "%2F": "/"} +_str_keys = ["&&", "&&", "&", "<", ">", "%2F"] + + +def unescape_all(url): + """Recursively unescape a given URL. + + .. note:: '&&' becomes a single '&'. + + Parameters + ---------- + url : str or bytes + URL to unescape. + + Returns + ------- + clean_url : str or bytes + Unescaped URL. + + """ + if isinstance(url, bytes): + func2use = _unescape_bytes + keys2use = _bytes_keys + else: + func2use = _unescape_str + keys2use = _str_keys + clean_url = func2use(url) + not_done = [clean_url.count(key) > 0 for key in keys2use] + if True in not_done: + return unescape_all(clean_url) + else: + return clean_url + + +def _unescape_str(url): + return saxutils.unescape(url, _str_entities) + + +def _unescape_bytes(url): + clean_url = url + for key in _bytes_keys: + clean_url = clean_url.replace(key, _bytes_entities[key]) + return clean_url diff --git a/astropy/utils/xml/validate.py b/astropy/utils/xml/validate.py index cc429e58c847..4ba537f7d77e 100644 --- a/astropy/utils/xml/validate.py +++ b/astropy/utils/xml/validate.py @@ -1,4 +1,5 @@ # Licensed under a 3-clause BSD style license - see LICENSE.rst + """ Functions to do XML schema and DTD validation. At the moment, this makes a subprocess call to xmllint. This could use a Python-based @@ -7,6 +8,8 @@ """ import os +import subprocess +from signal import Signals def validate_schema(filename, schema_file): @@ -18,7 +21,7 @@ def validate_schema(filename, schema_file): filename : str The path to the XML file to validate - schema : str + schema_file : str The path to the XML schema or DTD Returns @@ -27,23 +30,26 @@ def validate_schema(filename, schema_file): Returns the returncode from xmllint and the stdout and stderr as strings """ - import subprocess - base, ext = os.path.splitext(schema_file) - if ext == '.xsd': - schema_part = '--schema ' + schema_file - elif ext == '.dtd': - schema_part = '--dtdvalid ' + schema_file + if ext == ".xsd": + schema_part = "--schema" + elif ext == ".dtd": + schema_part = "--dtdvalid" else: raise TypeError("schema_file must be a path to an XML Schema or DTD") p = subprocess.Popen( - "xmllint --noout --nonet %s %s" % (schema_part, filename), - shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + ["xmllint", "--noout", "--nonet", schema_part, schema_file, filename], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) stdout, stderr = p.communicate() if p.returncode == 127: + raise OSError("xmllint not found, so can not validate schema") + elif p.returncode < 0: raise OSError( - "xmllint not found, so can not validate schema") + f"xmllint was terminated by signal '{Signals(-p.returncode).name}'" + ) return p.returncode, stdout, stderr diff --git a/astropy/utils/xml/writer.py b/astropy/utils/xml/writer.py index 7676ba5a22d8..73f881ac086e 100644 --- a/astropy/utils/xml/writer.py +++ b/astropy/utils/xml/writer.py @@ -4,31 +4,13 @@ nicely-indented XML. """ -# STDLIB import contextlib import textwrap +from astropy.utils.compat.optional_deps import HAS_BLEACH -def xml_escape_cdata(s): - """ - Escapes &, < and > in an XML CDATA string. - """ - s = s.replace(u"&", u"&") - s = s.replace(u"<", u"<") - s = s.replace(u">", u">") - return s - - -def xml_escape(s): - """ - Escapes &, ', ", < and > in an XML attribute value. - """ - s = s.replace(u"&", u"&") - s = s.replace(u"'", u"'") - s = s.replace(u"\"", u""") - s = s.replace(u"<", u"<") - s = s.replace(u">", u">") - return s +from ._iterparser import escape_xml as xml_escape +from ._iterparser import escape_xml_cdata as xml_escape_cdata class XMLWriter: @@ -55,7 +37,7 @@ def __init__(self, file): """ Parameters ---------- - file : writable file-like object. + file : :term:`file-like (writeable)` """ self.write = file.write if hasattr(file, "flush"): @@ -63,15 +45,10 @@ def __init__(self, file): self._open = 0 # true if start tag is open self._tags = [] self._data = [] - self._indentation = u" " * 64 + self._indentation = " " * 64 - try: - from . import _iterparser - self.xml_escape_cdata = _iterparser.escape_xml_cdata - self.xml_escape = _iterparser.escape_xml - except ImportError: - self.xml_escape_cdata = xml_escape_cdata - self.xml_escape = xml_escape + self.xml_escape_cdata = xml_escape_cdata + self.xml_escape = xml_escape def _flush(self, indent=True, wrap=False): """ @@ -79,21 +56,20 @@ def _flush(self, indent=True, wrap=False): """ if self._open: if indent: - self.write(u">\n") + self.write(">\n") else: - self.write(u">") + self.write(">") self._open = 0 if self._data: - data = u''.join(self._data) + data = "".join(self._data) if wrap: indent = self.get_indentation_spaces(1) data = textwrap.fill( - data, - initial_indent=indent, - subsequent_indent=indent) - self.write(u'\n') + data, initial_indent=indent, subsequent_indent=indent + ) + self.write("\n") self.write(self.xml_escape_cdata(data)) - self.write(u'\n') + self.write("\n") self.write(self.get_indentation_spaces()) else: self.write(self.xml_escape_cdata(data)) @@ -127,30 +103,97 @@ def start(self, tag, attrib={}, **extra): self._data = [] self._tags.append(tag) self.write(self.get_indentation_spaces(-1)) - self.write(u"<%s" % tag) + self.write(f"<{tag}") if attrib or extra: attrib = attrib.copy() attrib.update(extra) - attrib = attrib.items() + attrib = list(attrib.items()) attrib.sort() for k, v in attrib: if v is not None: # This is just busy work -- we know our keys are clean # k = xml_escape_cdata(k) v = self.xml_escape(v) - self.write(u" %s=\"%s\"" % (k, v)) + self.write(f' {k}="{v}"') self._open = 1 return len(self._tags) + @contextlib.contextmanager + def xml_cleaning_method(self, method="escape_xml", **clean_kwargs): + """Context manager to control how XML data tags are cleaned (escaped) to + remove potentially unsafe characters or constructs. + + The default (``method='escape_xml'``) applies brute-force escaping of + certain key XML characters like ``<``, ``>``, and ``&`` to ensure that + the output is not valid XML. + + In order to explicitly allow certain XML tags (e.g. link reference or + emphasis tags), use ``method='bleach_clean'``. This sanitizes the data + string using the ``clean`` function of the + `bleach `_ package. + Any additional keyword arguments will be passed directly to the + ``clean`` function. + + Finally, use ``method='none'`` to disable any sanitization. This should + be used sparingly. + + Example:: + + w = writer.XMLWriter(ListWriter(lines)) + with w.xml_cleaning_method('bleach_clean'): + w.start('td') + w.data('google.com') + w.end() + + Parameters + ---------- + method : str + Cleaning method. Allowed values are "escape_xml", + "bleach_clean", and "none". + + **clean_kwargs : keyword args + Additional keyword args that are passed to the + bleach.clean() function. + """ + current_xml_escape_cdata = self.xml_escape_cdata + + if method == "bleach_clean": + # NOTE: bleach is imported locally to avoid importing it when + # it is not necessary + if not HAS_BLEACH: + raise ValueError( + "bleach package is required when HTML escaping is disabled.\n" + 'Use "pip install bleach".' + ) + import bleach + + if clean_kwargs is None: + clean_kwargs = {} + self.xml_escape_cdata = lambda x: bleach.clean(x, **clean_kwargs) + elif method == "none": + self.xml_escape_cdata = lambda x: x + elif method != "escape_xml": + raise ValueError( + 'allowed values of method are "escape_xml", "bleach_clean", and "none"' + ) + + yield + + self.xml_escape_cdata = current_xml_escape_cdata + @contextlib.contextmanager def tag(self, tag, attrib={}, **extra): """ - A convenience method for use with the `with` statement:: + A convenience method for creating wrapper elements using the + ``with`` statement. - with writer.tag('foo'): - writer.element('bar') - # is implicitly closed here + Examples + -------- + >>> with writer.tag('foo'): # doctest: +SKIP + ... writer.element('bar') + ... # is implicitly closed here + ... Parameters are the same as to `start`. """ @@ -169,7 +212,7 @@ def comment(self, comment): """ self._flush() self.write(self.get_indentation_spaces()) - self.write(u"\n" % self.xml_escape_cdata(comment)) + self.write(f"\n") def data(self, text): """ @@ -194,21 +237,23 @@ def end(self, tag=None, indent=True, wrap=False): If omitted, the current element is closed. """ if tag: - assert self._tags, "unbalanced end(%s)" % tag - assert tag == self._tags[-1],\ - "expected end(%s), got %s" % (self._tags[-1], tag) + if not self._tags: + raise ValueError(f"unbalanced end({tag})") + if tag != self._tags[-1]: + raise ValueError(f"expected end({self._tags[-1]}), got {tag}") else: - assert self._tags, "unbalanced end()" + if not self._tags: + raise ValueError("unbalanced end()") tag = self._tags.pop() if self._data: self._flush(indent, wrap) elif self._open: self._open = 0 - self.write(u"/>\n") + self.write("/>\n") return if indent: self.write(self.get_indentation_spaces()) - self.write(u"\n" % tag) + self.write(f"\n") def close(self, id): """ @@ -226,7 +271,7 @@ def close(self, id): def element(self, tag, text=None, wrap=False, attrib={}, **extra): """ Adds an entire element. This is the same as calling `start`, - `data`, and `end` in sequence. The `text` argument + `data`, and `end` in sequence. The ``text`` argument can be omitted. """ self.start(tag, attrib, **extra) @@ -234,6 +279,15 @@ def element(self, tag, text=None, wrap=False, attrib={}, **extra): self.data(text) self.end(indent=False, wrap=wrap) + def string_element(self, xml_string): + """ + Reformat the indentation in the XML to insert the MIVOT block. + """ + self._flush() + indent = self.get_indentation_spaces() + str_to_write = indent + xml_string.replace("\n", f"\n{indent}").strip() + "\n" + self.write(str_to_write) + def flush(self): pass # replaced by the constructor @@ -249,7 +303,7 @@ def get_indentation_spaces(self, offset=0): Returns a string of spaces that matches the current indentation level. """ - return self._indentation[:len(self._tags) + offset] + return self._indentation[: len(self._tags) + offset] @staticmethod def object_attrs(obj, attrs): @@ -269,11 +323,11 @@ def object_attrs(obj, attrs): ------- attrs : dict Maps attribute names to the values retrieved from - `obj.attr`. If any of the attributes is `None`, it will + ``obj.attr``. If any of the attributes is `None`, it will not appear in the output dictionary. """ d = {} for attr in attrs: if getattr(obj, attr) is not None: - d[attr.replace(u'_', u'-')] = unicode(getattr(obj, attr)) + d[attr.replace("_", "-")] = str(getattr(obj, attr)) return d diff --git a/astropy/version.py b/astropy/version.py new file mode 100644 index 000000000000..f9b0be7655fc --- /dev/null +++ b/astropy/version.py @@ -0,0 +1,41 @@ +from packaging.version import Version + +# NOTE: First try _dev.scm_version if it exists and setuptools_scm is installed +# This file is not included in astropy wheels/tarballs, so otherwise it will +# fall back on the generated _version module. +try: + try: + from ._dev.scm_version import version + except ImportError: + from ._version import version +except Exception: + import warnings + + warnings.warn( + f"could not determine {__name__.split('.')[0]} package version; " + "this indicates a broken installation" + ) + del warnings + + version = "0.0.0" + + +# We use Version to define major, minor, micro, but ignore any suffixes. +def split_version(version): + pieces = [0, 0, 0] + + try: + v = Version(version) + pieces = [v.major, v.minor, v.micro] + + except Exception: + pass + + return pieces + + +major, minor, bugfix = split_version(version) + +del split_version # clean up namespace. + +release = "dev" not in version diff --git a/astropy/version_helper.py b/astropy/version_helper.py deleted file mode 100644 index d6d54c4cf1cb..000000000000 --- a/astropy/version_helper.py +++ /dev/null @@ -1,224 +0,0 @@ -# Licensed under a 3-clause BSD style license - see LICENSE.rst -from __future__ import division - -""" -Utilities for generating the version string for Astropy (or an affiliated -package) and the version.py module, which contains version info for the -package. - -Within the generated astropy.version module, the `major`, `minor`, and `bugfix` -variables hold the respective parts of the version number (bugfix is '0' if -absent). The `release` variable is True if this is a release, and False if this -is a development version of astropy. For the actual version string, use:: - - from astropy.version import version - -or:: - - from astropy import __version__ - -""" - - -def _version_split(version): - """ - Split a version string into major, minor, and bugfix numbers (with bugfix - optional, defaulting to 0). - """ - - versplit = version.split('.dev')[0].split('.') - major = int(versplit[0]) - minor = int(versplit[1]) - bugfix = 0 if len(versplit) < 3 else int(versplit[2]) - return major, minor, bugfix - - -def _update_git_devstr(version, path=None): - """ - Updates the git revision string if and only if the path is being imported - directly from a git working copy. This ensures that the revision number in - the version string is accurate. - """ - - try: - # Quick way to determine if we're in git or not - returns '' if not - devstr = get_git_devstr(sha=True, show_warning=False, path=path) - except OSError: - return version - - if not devstr: - # Probably not in git so just pass silently - return version - - if 'dev' in version: # update to the current git revision - version_base = version.split('.dev', 1)[0] - devstr = get_git_devstr(sha=False, show_warning=False, path=path) - - return version_base + '.dev' + devstr - else: - #otherwise it's already the true/release version - return version - - -def get_git_devstr(sha=False, show_warning=True, path=None): - """ - Determines the number of revisions in this repository. - - Parameters - ---------- - sha : bool - If True, the full SHA1 hash will be returned. Otherwise, the total - count of commits in the repository will be used as a "revision - number". - - show_warning : bool - If True, issue a warning if git returns an error code, otherwise errors - pass silently. - - path : str or None - If a string, specifies the directory to look in to find the git - repository. If None, the location of the file this function is in - is used to infer the git repository location. If given a filename it - uses the directory containing that file. - - Returns - ------- - devversion : str - Either a string with the revsion number (if `sha` is False), the - SHA1 hash of the current commit (if `sha` is True), or an empty string - if git version info could not be identified. - - """ - - import os - from subprocess import Popen, PIPE - from warnings import warn - from .utils import find_current_module - - if path is None: - try: - mod = find_current_module(1, finddiff=True) - path = os.path.abspath(mod.__file__) - except (ValueError, AttributeError): - path = __file__ - if not os.path.isdir(path): - path = os.path.abspath(os.path.split(path)[0]) - - if sha: - cmd = 'rev-parse' # Faster for getting just the hash of HEAD - else: - cmd = 'rev-list' - - try: - p = Popen(['git', cmd, 'HEAD'], cwd=path, - stdout=PIPE, stderr=PIPE, stdin=PIPE) - stdout, stderr = p.communicate() - except OSError as e: - if show_warning: - warn('Error running git: ' + str(e)) - return '' - - if p.returncode == 128: - if show_warning: - warn('No git repository present! Using default dev version.') - return '' - elif p.returncode != 0: - if show_warning: - warn('Git failed while determining revision count: ' + stderr) - return '' - - if sha: - return stdout.decode('utf-8')[:40] - else: - nrev = stdout.decode('utf-8').count('\n') - return str(nrev) - - -# This is used by setup.py to create a new version.py - see that file for -# details -_frozen_version_py_template = """ -# Autogenerated by {packagename}'s setup.py on {timestamp} - -from astropy.version_helper import _update_git_devstr, get_git_devstr - -version = _update_git_devstr({verstr!r}) -githash = get_git_devstr(sha=True, show_warning=False) - -major = {major} -minor = {minor} -bugfix = {bugfix} - -release = {rel} -debug = {debug} - -try: - from astropy.utils._compiler import compiler -except ImportError: - compiler = "unknown" -"""[1:] - - -def _get_version_py_str(packagename, version, release, debug): - - import datetime - - timestamp = str(datetime.datetime.now()) - major, minor, bugfix = _version_split(version) - if packagename.lower() == 'astropy': - packagename = 'Astropy' - else: - packagename = 'Astropy-affiliated package ' + packagename - return _frozen_version_py_template.format(packagename=packagename, - timestamp=timestamp, - verstr=version, - major=major, - minor=minor, - bugfix=bugfix, - rel=release, debug=debug) - - -def generate_version_py(packagename, version, release, debug=None): - """Regenerate the version.py module if necessary.""" - - from .setup_helpers import is_distutils_display_option - from distutils import log - import imp - import os - import sys - - try: - version_module = __import__(packagename + '.version', - fromlist=['version', 'release', 'debug']) - current_version = version_module.version - current_release = version_module.release - current_debug = version_module.debug - except ImportError: - version_module = None - current_version = None - current_release = None - current_debug = None - - if debug is None: - # Keep whatever the current value is, if it exists - debug = bool(current_debug) - - version_py = os.path.join(packagename, 'version.py') - - if (current_version != version or current_release != release or - current_debug != debug): - if '-q' not in sys.argv and '--quiet' not in sys.argv: - log.set_threshold(log.INFO) - - if is_distutils_display_option(): - # Always silence unnecessary log messages when display options are - # being used - log.set_threshold(log.WARN) - - log.info('Freezing version number to {0}'.format(version_py)) - - with open(version_py, 'w') as f: - # This overwrites the actual version.py - f.write(_get_version_py_str(packagename, version, release, debug)) - - if version_module: - imp.reload(version_module) diff --git a/astropy/visualization/__init__.py b/astropy/visualization/__init__.py new file mode 100644 index 000000000000..29cc9ef832d2 --- /dev/null +++ b/astropy/visualization/__init__.py @@ -0,0 +1,12 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +from .basic_rgb import * +from .hist import * +from .interval import * +from .lupton_rgb import LuptonAsinhStretch, LuptonAsinhZscaleStretch, make_lupton_rgb +from .mpl_normalize import * +from .mpl_style import * +from .stretch import * +from .time import * +from .transform import * +from .units import * diff --git a/astropy/visualization/basic_rgb.py b/astropy/visualization/basic_rgb.py new file mode 100644 index 000000000000..b8a83d6aada0 --- /dev/null +++ b/astropy/visualization/basic_rgb.py @@ -0,0 +1,217 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +""" +Combine 3 images to produce properly-scaled RGB images with arbitrary scaling. + +The three images must be aligned and have the same pixel scale and size. +""" + +import numpy as np + +from astropy.visualization.interval import ManualInterval +from astropy.visualization.stretch import LinearStretch + +_OUTPUT_IMAGE_FORMATS = [float, np.float64, np.uint8] + +__all__ = ["make_rgb"] + + +class RGBImageMapping: + """ + Class to map red, blue, green images into either a normalized float or + an 8-bit image, by performing optional clipping and applying + a scaling function to each band independently. + + Parameters + ---------- + interval : `~astropy.visualization.BaseInterval` subclass instance or array-like, optional + The interval object to apply to the data (either a single instance or + an array for R, G, B). Default is + `~astropy.visualization.ManualInterval`. + stretch : `~astropy.visualization.BaseStretch` subclass instance, optional + The stretch object to apply to the data. Default is + `~astropy.visualization.LinearStretch`. + + """ + + def __init__( + self, interval=ManualInterval(vmin=0, vmax=None), stretch=LinearStretch() + ): + try: + len(interval) + except TypeError: + interval = 3 * [interval] + if len(interval) != 3: + raise ValueError("please provide 1 or 3 instances for interval.") + + self.intervals = interval + self.stretch = stretch + + def make_rgb_image(self, image_r, image_g, image_b, output_dtype=np.uint8): + """ + Convert 3 arrays, image_r, image_g, and image_b into a RGB image, + either as an 8-bit per-channel or normalized image. + + The input images can be int or float, and in any range or bit-depth, + but must have the same shape (NxM). + + Parameters + ---------- + image_r : ndarray + Image to map to red. + image_g : ndarray + Image to map to green. + image_b : ndarray + Image to map to blue. + output_dtype : numpy scalar type, optional + Image output format. Default is np.uint8. + + Returns + ------- + RGBimage : ndarray + RGB color image with the specified format as an NxMx3 numpy array. + + """ + if output_dtype not in _OUTPUT_IMAGE_FORMATS: + raise ValueError(f"'output_dtype' must be one of {_OUTPUT_IMAGE_FORMATS}!") + + image_r = np.asarray(image_r) + image_g = np.asarray(image_g) + image_b = np.asarray(image_b) + + if (image_r.shape != image_g.shape) or (image_g.shape != image_b.shape): + msg = "The image shapes must match. r: {}, g: {} b: {}" + raise ValueError(msg.format(image_r.shape, image_g.shape, image_b.shape)) + + image_rgb = self.apply_mappings(image_r, image_g, image_b) + if np.issubdtype(output_dtype, float): + conv_images = self._convert_images_to_float(image_rgb, output_dtype) + elif np.issubdtype(output_dtype, np.unsignedinteger): + conv_images = self._convert_images_to_uint(image_rgb, output_dtype) + + return np.dstack(conv_images) + + def apply_mappings(self, image_r, image_g, image_b): + """ + Apply mapping stretch and intervals to convert images image_r, image_g, + and image_b to a triplet of normalized images. + + Parameters + ---------- + image_r : ndarray + Intensity of image to be mapped to red + image_g : ndarray + Intensity of image to be mapped to green. + image_b : ndarray + Intensity of image to be mapped to blue. + + Returns + ------- + image_rgb : ndarray + Triplet of mapped images based on the specified (per-band) + intervals and the stretch function + + """ + image_rgb = [image_r, image_g, image_b] + for i, img in enumerate(image_rgb): + # Using syntax from mpl_normalize.ImageNormalize, + # but NOT using that class to avoid dependency on matplotlib. + + # Define vmin and vmax + vmin, vmax = self.intervals[i].get_limits(img) + + # copy because of in-place operations after + img = np.array(img, copy=True, dtype=float) + + # Normalize based on vmin and vmax + np.subtract(img, vmin, out=img) + np.true_divide(img, vmax - vmin, out=img) + + # Clip to the 0 to 1 range + img = np.clip(img, 0.0, 1.0, out=img) + + # Stretch values + img = self.stretch(img, out=img, clip=False) + + image_rgb[i] = img + + return np.asarray(image_rgb) + + def _convert_images_to_float(self, image_rgb, output_dtype): + """ + Convert a triplet of normalized images to float. + """ + return image_rgb.astype(output_dtype) + + def _convert_images_to_uint(self, image_rgb, output_dtype): + """ + Convert a triplet of normalized images to unsigned integer images + """ + pixmax = float(np.iinfo(output_dtype).max) + + image_rgb *= pixmax + + return image_rgb.astype(output_dtype) + + +def make_rgb( + image_r, + image_g, + image_b, + interval=ManualInterval(vmin=0, vmax=None), + stretch=LinearStretch(), + filename=None, + output_dtype=np.uint8, +): + """ + Base class to return a Red/Green/Blue color image from 3 images using + a specified stretch and interval, for each band *independently*. + + The input images can be int or float, and in any range or bit-depth, + but must have the same shape (NxM). + + For a more detailed look at the use of this method, see the document + :ref:`astropy:astropy-visualization-rgb`. + + Parameters + ---------- + image_r : ndarray + Image to map to red. + image_g : ndarray + Image to map to green. + image_b : ndarray + Image to map to blue. + interval : `~astropy.visualization.BaseInterval` subclass instance or array-like, optional + The interval object to apply to the data (either a single instance or + an array for R, G, B). Default is + `~astropy.visualization.ManualInterval` with vmin=0. + stretch : `~astropy.visualization.BaseStretch` subclass instance, optional + The stretch object to apply to the data. Default is + `~astropy.visualization.LinearStretch`. + filename : str, optional + Write the resulting RGB image to a file (file type determined + from extension). + output_dtype : numpy scalar type, optional + Image output data type. Default is np.uint8. + + Returns + ------- + rgb : ndarray + RGB (either float or integer with 8-bits per channel) color image + as an NxMx3 numpy array. + + Notes + ----- + This procedure of clipping and then scaling is similar to the DS9 + image algorithm (see the DS9 reference guide: + http://ds9.si.edu/doc/ref/how.html). + + """ + map_ = RGBImageMapping(interval=interval, stretch=stretch) + rgb = map_.make_rgb_image(image_r, image_g, image_b, output_dtype=output_dtype) + + if filename: + import matplotlib.image + + matplotlib.image.imsave(filename, rgb, origin="lower") + + return rgb diff --git a/astropy/visualization/hist.py b/astropy/visualization/hist.py new file mode 100644 index 000000000000..929209c89777 --- /dev/null +++ b/astropy/visualization/hist.py @@ -0,0 +1,75 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +from astropy.stats.histogram import calculate_bin_edges + +__all__ = ["hist"] + + +def hist(x, bins=10, ax=None, max_bins=1e5, **kwargs): + """Enhanced histogram function. + + This is a histogram function that enables the use of more sophisticated + algorithms for determining bins. Aside from the ``bins`` argument allowing + a string specified how bins are computed, the parameters are the same + as matplotlib.pyplot.hist(). + + This function was ported from astroML: https://www.astroml.org/ + + Parameters + ---------- + x : array-like + array of data to be histogrammed + + bins : int, list, or str, optional + If bins is a string, then it must be one of: + + - 'blocks' : use bayesian blocks for dynamic bin widths + + - 'knuth' : use Knuth's rule to determine bins + + - 'scott' : use Scott's rule to determine bins + + - 'freedman' : use the Freedman-Diaconis rule to determine bins + + ax : `~matplotlib.axes.Axes` instance, optional + Specify the Axes on which to draw the histogram. If not specified, + then the current active axes will be used. + + max_bins : int, optional + Maximum number of bins allowed. With more than a few thousand bins + the performance of matplotlib will not be great. If the number of + bins is large *and* the number of input data points is large then + the it will take a very long time to compute the histogram. + + **kwargs : + other keyword arguments are described in ``plt.hist()``. + + Notes + ----- + Return values are the same as for ``plt.hist()`` + + See Also + -------- + astropy.stats.histogram + """ + # Note that we only calculate the bin edges...matplotlib will calculate + # the actual histogram. + range = kwargs.get("range") + weights = kwargs.get("weights") + bins = calculate_bin_edges(x, bins, range=range, weights=weights) + + if len(bins) > max_bins: + raise ValueError( + "Histogram has too many bins: " + f"{len(bins)}. Use max_bins to increase the number " + "of allowed bins or range to restrict " + "the histogram range." + ) + + if ax is None: + # optional dependency; only import if strictly needed. + import matplotlib.pyplot as plt + + ax = plt.gca() + + return ax.hist(x, bins, **kwargs) diff --git a/astropy/visualization/interval.py b/astropy/visualization/interval.py new file mode 100644 index 000000000000..a0adeda495f8 --- /dev/null +++ b/astropy/visualization/interval.py @@ -0,0 +1,379 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +""" +Classes that deal with computing intervals from arrays of values based on +various criteria. +""" + +import abc + +import numpy as np + +from astropy.utils.masked import get_data_and_mask + +from .transform import BaseTransform + +__all__ = [ + "AsymmetricPercentileInterval", + "BaseInterval", + "ManualInterval", + "MinMaxInterval", + "PercentileInterval", + "SymmetricInterval", + "ZScaleInterval", +] + + +class BaseInterval(BaseTransform): + """ + Base class for the interval classes, which, when called with an + array of values, return an interval computed following different + algorithms. + """ + + @abc.abstractmethod + def get_limits(self, values): + """ + Return the minimum and maximum value in the interval based on + the values provided. + + Parameters + ---------- + values : ndarray + The image values. + + Returns + ------- + vmin, vmax : float + The mininium and maximum image value in the interval. + """ + raise NotImplementedError("Needs to be implemented in a subclass.") + + @staticmethod + def _process_values(values): + """ + Process the input values. + + This function filters out masked and/or invalid values (inf, + nan) and returns a flattened 1D array. + + Parameters + ---------- + values : array-like + The input values. + + Returns + ------- + result : 1D ndarray + The processed values. + """ + data, mask = get_data_and_mask(np.asanyarray(values)) + ok = np.isfinite(data) + if mask is not None: + ok &= ~mask + + return data[ok] + + def __call__(self, values, clip=True, out=None): + """ + Transform values using this interval. + + The ``vmin`` and ``vmax`` values are determined by the + `get_limits` method. + + The following transformation is then applied to the values: + + .. math:: + + {\\rm result} = \\frac{{\\rm values} - v_{\\rm min}} + {v_{\\rm max} - v_{\\rm min}} + + If ``clip`` is `True` (default), the result is then clipped to + the [0:1] range. + + Parameters + ---------- + values : array-like + The input values. + clip : bool, optional + If `True` (default), values outside the [0:1] range are + clipped to the [0:1] range. + out : ndarray, optional + If specified, the output values will be placed in this array + (typically used for in-place calculations). + + Returns + ------- + result : ndarray + The transformed values. + """ + vmin, vmax = self.get_limits(values) + + if out is None: + values = np.subtract(values, float(vmin)) + else: + if out.dtype.kind != "f": + raise TypeError( + "Can only do in-place scaling for floating-point arrays" + ) + values = np.subtract(values, float(vmin), out=out) + + if (vmax - vmin) != 0: + np.true_divide(values, vmax - vmin, out=values) + + if clip: + np.clip(values, 0.0, 1.0, out=values) + + return values + + +class ManualInterval(BaseInterval): + """ + Interval based on user-specified values. + + Parameters + ---------- + vmin : float, optional + The minimum value in the scaling. Defaults to the image + minimum (ignoring NaNs) + vmax : float, optional + The maximum value in the scaling. Defaults to the image + maximum (ignoring NaNs) + """ + + def __init__(self, vmin=None, vmax=None): + self.vmin = vmin + self.vmax = vmax + + def get_limits(self, values): + # Avoid overhead of preparing array if both limits have been specified + # manually, for performance. + if self.vmin is not None and self.vmax is not None: + return self.vmin, self.vmax + + values = self._process_values(values) + vmin = np.min(values) if self.vmin is None else self.vmin + vmax = np.max(values) if self.vmax is None else self.vmax + + return vmin, vmax + + +class MinMaxInterval(BaseInterval): + """ + Interval based on the minimum and maximum values in the data. + """ + + def get_limits(self, values): + values = self._process_values(values) + + return np.min(values), np.max(values) + + +class AsymmetricPercentileInterval(BaseInterval): + """ + Interval based on a keeping a specified fraction of pixels (can be + asymmetric). + + Parameters + ---------- + lower_percentile : float or None + The lower percentile below which to ignore pixels. If None, then + defaults to 0. + upper_percentile : float or None + The upper percentile above which to ignore pixels. If None, then + defaults to 100. + n_samples : int, optional + Maximum number of values to use. If this is specified, and there + are more values in the dataset as this, then values are randomly + sampled from the array (with replacement). + """ + + def __init__(self, lower_percentile=None, upper_percentile=None, n_samples=None): + self.lower_percentile = ( + lower_percentile if lower_percentile is not None else 0.0 + ) + self.upper_percentile = ( + upper_percentile if upper_percentile is not None else 100.0 + ) + self.n_samples = n_samples + + def get_limits(self, values): + values = self._process_values(values) + + # If needed, limit the number of samples. We sample with replacement + # since this is much faster. + if self.n_samples is not None and values.size > self.n_samples: + values = np.random.choice(values, self.n_samples) + + # Determine values at percentiles + vmin, vmax = np.percentile( + values, (self.lower_percentile, self.upper_percentile) + ) + + return vmin, vmax + + +class PercentileInterval(AsymmetricPercentileInterval): + """ + Interval based on a keeping a specified fraction of pixels. + + Parameters + ---------- + percentile : float + The fraction of pixels to keep. The same fraction of pixels is + eliminated from both ends. + n_samples : int, optional + Maximum number of values to use. If this is specified, and there + are more values in the dataset as this, then values are randomly + sampled from the array (with replacement). + """ + + def __init__(self, percentile, n_samples=None): + lower_percentile = (100 - percentile) * 0.5 + upper_percentile = 100 - lower_percentile + super().__init__(lower_percentile, upper_percentile, n_samples=n_samples) + + +class SymmetricInterval(BaseInterval): + """ + Interval based on a symmetric radius away from a midpoint. + + Parameters + ---------- + radius : float or None + The amount the interval extends to either side of the midpoint, so the total + interval is twice this value. If None, the radius is automatically + determined such that the resulting interval contains both the image minimum + and maximum (ignoring NaNs). + midpoint : float, optional + The midpoint of the symmetric interval. Defaults to zero. + """ + + def __init__(self, radius=None, *, midpoint=0): + self.radius = radius + self.midpoint = midpoint + + def get_limits(self, values): + radius = self.radius + if radius is None: + values = self._process_values(values) + radius = np.max( + [np.max(values) - self.midpoint, self.midpoint - np.min(values)] + ) + + return self.midpoint - radius, self.midpoint + radius + + +class ZScaleInterval(BaseInterval): + """ + Interval based on IRAF's zscale. + + Original implementation: + https://github.com/spacetelescope/stsci.numdisplay/blob/master/lib/stsci/numdisplay/zscale.py + + Licensed under a 3-clause BSD style license (see AURA_LICENSE.rst). + + Parameters + ---------- + n_samples : int, optional + The number of points in the array to sample for determining + scaling factors. Defaults to 1000. + + .. versionchanged:: 7.0 + ``nsamples`` parameter is removed. + + contrast : float, optional + The scaling factor (between 0 and 1) for determining the minimum + and maximum value. Larger values decrease the difference + between the minimum and maximum values used for display. + Defaults to 0.25. + max_reject : float, optional + If more than ``max_reject * npixels`` pixels are rejected, then + the returned values are the minimum and maximum of the data. + Defaults to 0.5. + min_npixels : int, optional + If there are less than ``min_npixels`` pixels remaining after + the pixel rejection, then the returned values are the minimum + and maximum of the data. Defaults to 5. + krej : float, optional + The number of sigma used for the rejection. Defaults to 2.5. + max_iterations : int, optional + The maximum number of iterations for the rejection. Defaults to + 5. + """ + + def __init__( + self, + n_samples=1000, + contrast=0.25, + max_reject=0.5, + min_npixels=5, + krej=2.5, + max_iterations=5, + ): + self.n_samples = n_samples + self.contrast = contrast + self.max_reject = max_reject + self.min_npixels = min_npixels + self.krej = krej + self.max_iterations = max_iterations + + def get_limits(self, values): + values = self._process_values(values) + + # Sample the image + stride = int(max(1.0, values.size / self.n_samples)) + samples = values[::stride][: self.n_samples] + samples.sort() + + npix = len(samples) + vmin = samples[0] + vmax = samples[-1] + + # Fit a line to the sorted array of samples + minpix = max(self.min_npixels, int(npix * self.max_reject)) + x = np.arange(npix) + ngoodpix = npix + last_ngoodpix = npix + 1 + + # Bad pixels mask used in k-sigma clipping + badpix = np.zeros(npix, dtype=bool) + + # Kernel used to dilate the bad pixels mask + ngrow = max(1, int(npix * 0.01)) + kernel = np.ones(ngrow, dtype=bool) + + for _ in range(self.max_iterations): + if ngoodpix >= last_ngoodpix or ngoodpix < minpix: + break + + fit = np.polyfit(x, samples, deg=1, w=(~badpix).astype(int)) + fitted = np.poly1d(fit)(x) + + # Subtract fitted line from the data array + flat = samples - fitted + + # Compute the k-sigma rejection threshold + threshold = self.krej * flat[~badpix].std() + + # Detect and reject pixels further than k*sigma from the + # fitted line + badpix[(flat < -threshold) | (flat > threshold)] = True + + # Convolve with a kernel of length ngrow + badpix = np.convolve(badpix, kernel, mode="same") + + last_ngoodpix = ngoodpix + ngoodpix = np.sum(~badpix) + + if ngoodpix >= minpix: + slope, _ = fit + + if self.contrast > 0: + slope = slope / self.contrast + center_pixel = (npix - 1) // 2 + median = np.median(samples) + vmin = max(vmin, median - (center_pixel - 1) * slope) + vmax = min(vmax, median + (npix - center_pixel) * slope) + + return vmin, vmax diff --git a/astropy/visualization/lupton_rgb.py b/astropy/visualization/lupton_rgb.py new file mode 100644 index 000000000000..356ec92963e2 --- /dev/null +++ b/astropy/visualization/lupton_rgb.py @@ -0,0 +1,758 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +""" +Combine 3 images to produce a properly-scaled RGB image following +Lupton et al. (2004). + +The three images must be aligned and have the same pixel scale and size. + +For details, see : https://ui.adsabs.harvard.edu/abs/2004PASP..116..133L +""" + +import numpy as np + +from astropy.visualization.interval import ManualInterval, ZScaleInterval +from astropy.visualization.stretch import BaseStretch +from astropy.visualization.stretch import _prepare as _stretch_prepare + +from .basic_rgb import RGBImageMapping + +__all__ = [ + "AsinhMapping", + "AsinhZScaleMapping", + "LinearMapping", + "LuptonAsinhStretch", + "LuptonAsinhZscaleStretch", + "Mapping", + "make_lupton_rgb", +] + + +def compute_intensity(image_r, image_g=None, image_b=None): + """ + Return a naive total intensity from the red, blue, and green intensities. + + Parameters + ---------- + image_r : ndarray + Intensity of image to be mapped to red; or total intensity if + ``image_g`` and ``image_b`` are None. + image_g : ndarray, optional + Intensity of image to be mapped to green. + image_b : ndarray, optional + Intensity of image to be mapped to blue. + + Returns + ------- + intensity : ndarray + Total intensity from the red, blue and green intensities, or + ``image_r`` if green and blue images are not provided. + + """ + if image_g is None or image_b is None: + if not (image_g is None and image_b is None): + raise ValueError( + "please specify either a single image or red, green, and blue images." + ) + return image_r + + intensity = (image_r + image_g + image_b) / 3.0 + + # Repack into whatever type was passed to us + return np.asarray(intensity, dtype=image_r.dtype) + + +class Mapping: + """ + Baseclass to map red, blue, green intensities into uint8 values. + + Parameters + ---------- + minimum : float or sequence(3) + Intensity that should be mapped to black (a scalar or array for R, G, B). + image : ndarray, optional + An image used to calculate some parameters of some mappings. + """ + + def __init__(self, minimum=None, image=None): + self._uint8Max = float(np.iinfo(np.uint8).max) + + try: + len(minimum) + except TypeError: + minimum = 3 * [minimum] + if len(minimum) != 3: + raise ValueError("please provide 1 or 3 values for minimum.") + + self.minimum = minimum + self._image = np.asarray(image) + + def make_rgb_image(self, image_r, image_g, image_b): + """ + Convert 3 arrays, image_r, image_g, and image_b into an 8-bit RGB image. + + Parameters + ---------- + image_r : ndarray + Image to map to red. + image_g : ndarray + Image to map to green. + image_b : ndarray + Image to map to blue. + + Returns + ------- + RGBimage : ndarray + RGB (integer, 8-bits per channel) color image as an NxNx3 numpy array. + """ + image_r = np.asarray(image_r) + image_g = np.asarray(image_g) + image_b = np.asarray(image_b) + + if (image_r.shape != image_g.shape) or (image_g.shape != image_b.shape): + msg = "The image shapes must match. r: {}, g: {} b: {}" + raise ValueError(msg.format(image_r.shape, image_g.shape, image_b.shape)) + + return np.dstack( + self._convert_images_to_uint8(image_r, image_g, image_b) + ).astype(np.uint8) + + def intensity(self, image_r, image_g, image_b): + """ + Return the total intensity from the red, blue, and green intensities. + This is a naive computation, and may be overridden by subclasses. + + Parameters + ---------- + image_r : ndarray + Intensity of image to be mapped to red; or total intensity if + ``image_g`` and ``image_b`` are None. + image_g : ndarray, optional + Intensity of image to be mapped to green. + image_b : ndarray, optional + Intensity of image to be mapped to blue. + + Returns + ------- + intensity : ndarray + Total intensity from the red, blue and green intensities, or + ``image_r`` if green and blue images are not provided. + """ + return compute_intensity(image_r, image_g, image_b) + + def map_intensity_to_uint8(self, I): + """ + Return an array which, when multiplied by an image, returns that image + mapped to the range of a uint8, [0, 255] (but not converted to uint8). + + The intensity is assumed to have had minimum subtracted (as that can be + done per-band). + + Parameters + ---------- + I : ndarray + Intensity to be mapped. + + Returns + ------- + mapped_I : ndarray + ``I`` mapped to uint8 + """ + with np.errstate(invalid="ignore", divide="ignore"): + return np.clip(I, 0, self._uint8Max) + + def _convert_images_to_uint8(self, image_r, image_g, image_b): + """ + Use the mapping to convert images image_r, image_g, and image_b to a triplet of uint8 images. + """ + image_r = image_r - self.minimum[0] # n.b. makes copy + image_g = image_g - self.minimum[1] + image_b = image_b - self.minimum[2] + + fac = self.map_intensity_to_uint8(self.intensity(image_r, image_g, image_b)) + + image_rgb = [image_r, image_g, image_b] + for c in image_rgb: + c *= fac + with np.errstate(invalid="ignore"): + c[c < 0] = 0 # individual bands can still be < 0, even if fac isn't + + pixmax = self._uint8Max + # copies -- could work row by row to minimise memory usage + r0, g0, b0 = image_rgb + + # n.b. np.where can't and doesn't short-circuit + with np.errstate(invalid="ignore", divide="ignore"): + for i, c in enumerate(image_rgb): + c = np.where( + r0 > g0, + np.where( + r0 > b0, + np.where(r0 >= pixmax, c * pixmax / r0, c), + np.where(b0 >= pixmax, c * pixmax / b0, c), + ), + np.where( + g0 > b0, + np.where(g0 >= pixmax, c * pixmax / g0, c), + np.where(b0 >= pixmax, c * pixmax / b0, c), + ), + ).astype(np.uint8) + c[c > pixmax] = pixmax + + image_rgb[i] = c + + return image_rgb + + +class LinearMapping(Mapping): + """ + A linear map map of red, blue, green intensities into uint8 values. + + A linear stretch from [minimum, maximum]. + If one or both are omitted use image min and/or max to set them. + + Parameters + ---------- + minimum : float + Intensity that should be mapped to black (a scalar or array for R, G, B). + maximum : float + Intensity that should be mapped to white (a scalar). + """ + + def __init__(self, minimum=None, maximum=None, image=None): + if minimum is None or maximum is None: + if image is None: + raise ValueError( + "you must provide an image if you don't " + "set both minimum and maximum" + ) + if minimum is None: + minimum = image.min() + if maximum is None: + maximum = image.max() + + Mapping.__init__(self, minimum=minimum, image=image) + self.maximum = maximum + + if maximum is None: + self._range = None + else: + if maximum == minimum: + raise ValueError("minimum and maximum values must not be equal") + self._range = float(maximum - minimum) + + def map_intensity_to_uint8(self, I): + # n.b. np.where can't and doesn't short-circuit + with np.errstate(invalid="ignore", divide="ignore"): + return np.where( + I <= 0, + 0, + np.where( + I >= self._range, self._uint8Max / I, self._uint8Max / self._range + ), + ) + + +class AsinhMapping(Mapping): + """ + A mapping for an asinh stretch (preserving colours independent of brightness). + + x = asinh(Q (I - minimum)/stretch)/Q + + This reduces to a linear stretch if Q == 0 + + See https://ui.adsabs.harvard.edu/abs/2004PASP..116..133L + + Parameters + ---------- + minimum : float + Intensity that should be mapped to black (a scalar or array for R, G, B). + stretch : float + The linear stretch of the image. + Q : float + The asinh softening parameter. + """ + + def __init__(self, minimum, stretch, Q=8): + Mapping.__init__(self, minimum) + + # 32bit floating point machine epsilon; sys.float_info.epsilon is 64bit + epsilon = 1.0 / 2**23 + if abs(Q) < epsilon: + Q = 0.1 + else: + Qmax = 1e10 + if Q > Qmax: + Q = Qmax + + frac = 0.1 # gradient estimated using frac*stretch is _slope + self._slope = frac * self._uint8Max / np.arcsinh(frac * Q) + + self._soften = Q / float(stretch) + + def map_intensity_to_uint8(self, I): + # n.b. np.where can't and doesn't short-circuit + with np.errstate(invalid="ignore", divide="ignore"): + return np.where(I <= 0, 0, np.arcsinh(I * self._soften) * self._slope / I) + + +class AsinhZScaleMapping(AsinhMapping): + """ + A mapping for an asinh stretch, estimating the linear stretch by zscale. + + x = asinh(Q (I - z1)/(z2 - z1))/Q + + Parameters + ---------- + image1 : ndarray or a list of arrays + The image to analyse, or a list of 3 images to be converted to + an intensity image. + image2 : ndarray, optional + the second image to analyse (must be specified with image3). + image3 : ndarray, optional + the third image to analyse (must be specified with image2). + Q : float, optional + The asinh softening parameter. Default is 8. + pedestal : float or sequence(3), optional + The value, or array of 3 values, to subtract from the images; or None. + + Notes + ----- + pedestal, if not None, is removed from the images when calculating the + zscale stretch, and added back into Mapping.minimum[] + """ + + def __init__(self, image1, image2=None, image3=None, Q=8, pedestal=None): + if image2 is None or image3 is None: + if not (image2 is None and image3 is None): + raise ValueError( + "please specify either a single image or three images." + ) + image = [image1] + else: + image = [image1, image2, image3] + + if pedestal is not None: + try: + len(pedestal) + except TypeError: + pedestal = 3 * [pedestal] + + if len(pedestal) != 3: + raise ValueError("please provide 1 or 3 pedestals.") + + image = list(image) # needs to be mutable + for i, im in enumerate(image): + if pedestal[i] != 0.0: + image[i] = im - pedestal[i] # n.b. a copy + else: + pedestal = len(image) * [0.0] + + image = compute_intensity(*image) + + zscale_limits = ZScaleInterval().get_limits(image) + zscale = LinearMapping(*zscale_limits, image=image) + # zscale.minimum is always a triple + stretch = zscale.maximum - zscale.minimum[0] + minimum = zscale.minimum + + for i, level in enumerate(pedestal): + minimum[i] += level + + AsinhMapping.__init__(self, minimum, stretch, Q) + self._image = image + + +class LuptonAsinhStretch(BaseStretch): + r""" + A modified asinh stretch, with some changes to the constants + relative to `~astropy.visualization.AsinhStretch`. + + The stretch is given by: + + .. math:: + & y = {\rm asinh}\left(\frac{Q * x}{stretch}\right) * + \frac{frac}{{\rm asinh}(frac * Q)} \\ + & frac = 0.1 + + Parameters + ---------- + stretch : float, optional + Linear stretch of the image. ``stretch`` must be greater than 0. + Default is 5. + + Q : float, optional + The asinh softening parameter. ``Q`` must be greater than 0. + Default is 8. + + Notes + ----- + Based on the asinh stretch presented in Lupton et al. 2004 + (https://ui.adsabs.harvard.edu/abs/2004PASP..116..133L). + + Examples + -------- + .. plot:: + :show-source-link: + + import numpy as np + from astropy.visualization import LuptonAsinhStretch + from matplotlib import pyplot as plt + + fig, ax = plt.subplots(nrows=2, ncols=2, figsize=(8, 8), + layout='constrained') + ax = ax.ravel() + + x = np.linspace(0, 1, 100) + stretches = (0.05, 0.1, 0.2, 0.5, 1, 5, 10) + + Qs = (2, 5, 8, 10) + for i, Q in enumerate(Qs): + for st in stretches: + stretch = LuptonAsinhStretch(stretch=st, Q=Q) + label = f'{st=}' + ax[i].plot(x, stretch(x, clip=True), label=label) + + ax[i].axis('equal') + ax[i].plot(x, x, ls='dotted', color='k', alpha=0.3) + ax[i].set_xlim(0, 1) + ax[i].set_ylim(0, 1) + ax[i].set_xlabel('Input Value') + ax[i].set_ylabel('Output Value') + ax[i].set_title(f'{stretch.__class__.__name__}, {Q=}') + ax[i].legend(loc='lower right', fontsize=8) + """ + + def __init__(self, stretch=5, Q=8): + super().__init__() + + if stretch < 0: + raise ValueError(f"Stretch must be non-negative! {stretch=}") + if Q < 0: + raise ValueError(f"Q must be non-negative! {Q=}") + + # 32bit floating point machine epsilon; sys.float_info.epsilon is 64bit + epsilon = 1.0 / 2**23 + if abs(Q) < epsilon: + Q = 0.1 + else: + Qmax = 1e10 + if Q > Qmax: + Q = Qmax + + self.stretch = stretch + self.Q = Q + + frac = 0.1 + self._slope = frac / np.arcsinh(frac * Q) + self._soften = Q / float(stretch) + + def __call__(self, values, clip=False, out=None): + values = _stretch_prepare(values, clip=clip, out=out) + np.multiply(values, self._soften, out=values) + np.arcsinh(values, out=values) + np.multiply(values, self._slope, out=values) + return values + + +class LuptonAsinhZscaleStretch(LuptonAsinhStretch): + r""" + A modified asinh stretch, where the linear stretch is calculated using + zscale. + + The stretch is given by: + + .. math:: + & y = {\rm asinh}\left(\frac{Q * x}{stretch}\right) * + \frac{frac}{{\rm asinh}(frac * Q)} \\ + & frac = 0.1 \\ + & stretch = z2 - z1 + + Parameters + ---------- + image1 : ndarray or array-like + The image to analyse, or a list of 3 images to be converted to + an intensity image. + + Q : float, optional + The asinh softening parameter. ``Q`` must be greater than 0. + Default is 8. + + pedestal : or array-like, optional + The value, or array of 3 values, to subtract from the images(s) + before determining the zscaling. Default is None (nothing subtracted). + + """ + + def __init__(self, image, Q=8, pedestal=None): + # copy because of in-place operations after + image = np.array(image, copy=True, dtype=float) + + _raiseerr = False + if len(image.shape) == 2: + image = [image] + elif len(image.shape) == 3: + if image.shape[0] != 3: + _raiseerr = True + else: + _raiseerr = True + + if _raiseerr: + raise ValueError( + "Input 'image' must be a single image " + "or a stack/3xMxN array of 3 images! " + f"{image.shape=}" + ) + + image = list(image) # needs to be mutable + + if pedestal is not None: + try: + len(pedestal) + except TypeError: + pedestal = 3 * [pedestal] + + if len(pedestal) != 3: + raise ValueError( + "pedestal must be 1 or 3 values, matching the image input." + ) + for i, im in enumerate(image): + if pedestal[i] != 0.0: + image[i] = im - pedestal[i] # n.b. a copy + + image = compute_intensity(*image) + zscale_limits = ZScaleInterval().get_limits(image) + + _stretch = zscale_limits[1] - zscale_limits[0] + + self._image = image + + super().__init__(stretch=_stretch, Q=Q) + + +class RGBImageMappingLupton(RGBImageMapping): + """ + Class to map red, blue, green images into either a normalized float or + an 8-bit image, by performing optional clipping and applying + a scaling function to each band in non-independent manner that depends + on the other bands, following the scaling scheme presented in + Lupton et al. 2004. + + Parameters + ---------- + interval : `~astropy.visualization.BaseInterval` subclass instance or array-like, optional + The interval object to apply to the data (either a single instance or + an array for R, G, B). Default is + `~astropy.visualization.ManualInterval`. + stretch : `~astropy.visualization.BaseStretch` subclass instance + The stretch object to apply to the data. The default is + `~astropy.visualization.AsinhLuptonStretch`. + + """ + + def __init__( + self, + interval=ManualInterval(vmin=0, vmax=None), + stretch=LuptonAsinhStretch(stretch=5, Q=8), + ): + super().__init__(interval=interval, stretch=stretch) + self._pixmax = 1.0 + + def intensity(self, image_r, image_g, image_b): + """ + Return the total intensity from the red, blue, and green intensities. + This is a naive computation, and may be overridden by subclasses. + + Parameters + ---------- + image_r : ndarray + Intensity of image to be mapped to red; or total intensity if + ``image_g`` and ``image_b`` are None. + image_g : ndarray, optional + Intensity of image to be mapped to green. + image_b : ndarray, optional + Intensity of image to be mapped to blue. + + Returns + ------- + intensity : ndarray + Total intensity from the red, blue and green intensities, or + ``image_r`` if green and blue images are not provided. + + """ + return compute_intensity(image_r, image_g, image_b) + + def apply_mappings(self, image_r, image_g, image_b): + """ + Apply mapping stretch and intervals to convert images image_r, image_g, + and image_b to a triplet of normalized images, following the scaling + scheme presented in Lupton et al. 2004. + + Compared to astropy's ImageNormalize which first normalizes images + by cropping and linearly mapping onto [0.,1.] and then applies + a specified stretch algorithm, the Lupton et al. algorithm applies + stretching to an multi-color intensity and then computes per-band + scaled images with bound cropping. + + This is modified here by allowing for different minimum values + for each of the input r, g, b images, and then computing + the intensity on the subtracted images. + + Parameters + ---------- + image_r : ndarray + Intensity of image to be mapped to red + image_g : ndarray + Intensity of image to be mapped to green. + image_b : ndarray + Intensity of image to be mapped to blue. + + Returns + ------- + image_rgb : ndarray + Triplet of mapped images based on the specified (per-band) + intervals and the stretch function + + Notes + ----- + The Lupton et al 2004 algorithm is computed with the following steps: + + 1. Shift each band with the minimum values + 2. Compute the intensity I and stretched intensity f(I) + 3. Compute the ratio of the stretched intensity to intensity f(I)/I, + and clip to a lower bound of 0 + 4. Compute the scaled band images by multiplying with the ratio f(I)/I + 5. Clip each band to a lower bound of 0 + 6. Scale down pixels where max(R,G,B)>1 by the value max(R,G,B) + + """ + image_r = np.array(image_r, copy=True) + image_g = np.array(image_g, copy=True) + image_b = np.array(image_b, copy=True) + + # Subtract per-band minima + image_rgb = [image_r, image_g, image_b] + for i, img in enumerate(image_rgb): + vmin, _ = self.intervals[i].get_limits(img) + image_rgb[i] = np.subtract(img, vmin) + + image_rgb = np.asarray(image_rgb) + + # Determine the intensity and streteched intensity + Int = self.intensity(*image_rgb) + fI = self.stretch(Int, clip=False) + + # Get normalized fI, and clip to lower bound of 0: + fInorm = np.where(Int <= 0, 0, np.true_divide(fI, Int)) + + # Compute X = x * f(I) / I for each filter x=(r,g,b) + np.multiply(image_rgb, fInorm, out=image_rgb) + + # Clip individual bands to minimum of 0, as + # individual bands can be < 0 even if fI/I isn't. + image_rgb = np.clip(image_rgb, 0.0, None) + + # Determine the max of all 3 bands at each position + maxRGB = np.max(image_rgb, axis=0) + + with np.errstate(invalid="ignore", divide="ignore"): + image_rgb = np.where( + maxRGB > self._pixmax, + np.true_divide(image_rgb * self._pixmax, maxRGB), + image_rgb, + ) + + return np.asarray(image_rgb) + + +def make_lupton_rgb( + image_r, + image_g, + image_b, + interval=None, + stretch_object=None, + minimum=None, + stretch=5, + Q=8, + filename=None, + output_dtype=np.uint8, +): + r""" + Return a Red/Green/Blue color image from 3 images using interconnected + band scaling, and an arbitrary stretch function (by default, an asinh stretch). + The input images can be int or float, and in any range or bit-depth. + + For a more detailed look at the use of this method, see the document + :ref:`astropy:astropy-visualization-rgb`. + + Parameters + ---------- + image_r : ndarray + Image to map to red. + image_g : ndarray + Image to map to green. + image_b : ndarray + Image to map to blue. + interval : `~astropy.visualization.BaseInterval` subclass instance or array-like, optional + The interval object to apply to the data (either a single instance or + an array for R, G, B). Default is + `~astropy.visualization.ManualInterval` with vmin=0. + stretch_object : `~astropy.visualization.BaseStretch` subclass instance, optional + The stretch object to apply to the data. If set, the input values of + ``minimum``, ``stretch``, and ``Q`` will be ignored. + For the Lupton scheme, this would be an instance of + `~astropy.visualization.LuptonAsinhStretch`, but alternatively + `~astropy.visualization.LuptonAsinhZscaleStretch` or some other + stretch can be used. + minimum : float or array-like, optional + Deprecated. Intensity that should be mapped to black (a scalar or + array of R, G, B). If `None`, each image's minimum value is used. + Default is None. + stretch : float, optional + The linear stretch of the image. Default is 5 + Q : float, optional + The asinh softening parameter. Default is 8. + filename : str, optional + Write the resulting RGB image to a file (file type determined + from extension). + output_dtype : numpy scalar type, optional + Image output data type. Default is np.uint8. + + Returns + ------- + rgb : ndarray + RGB color image as an NxNx3 numpy array, with the specified + data type format + + """ + if stretch_object is None: + stretch_object = LuptonAsinhStretch(stretch=stretch, Q=Q) + + if interval is None: + # Only use minimum if interval is not specified: + if minimum is not None: + # Backwards compatibility: + try: + len(minimum) + except TypeError: + minimum = 3 * [minimum] + if len(minimum) != 3: + raise ValueError("please provide 1 or 3 values for minimum.") + interval = [] + for i in range(3): + interval.append(ManualInterval(vmin=minimum[i], vmax=None)) + else: + # Default option: + interval = ManualInterval(vmin=0, vmax=None) + + lup_map = RGBImageMappingLupton( + interval=interval, + stretch=stretch_object, + ) + rgb = lup_map.make_rgb_image(image_r, image_g, image_b, output_dtype=output_dtype) + + if filename: + import matplotlib.image + + matplotlib.image.imsave(filename, rgb, origin="lower") + + return rgb diff --git a/astropy/visualization/mpl_normalize.py b/astropy/visualization/mpl_normalize.py new file mode 100644 index 000000000000..cc5911803f5d --- /dev/null +++ b/astropy/visualization/mpl_normalize.py @@ -0,0 +1,737 @@ +""" +Normalization class for Matplotlib that can be used to produce +colorbars. +""" + +import inspect + +import numpy as np +from numpy import ma + +from astropy.utils.compat.optional_deps import HAS_MATPLOTLIB +from astropy.utils.decorators import future_keyword_only + +from .interval import ( + AsymmetricPercentileInterval, + BaseInterval, + ManualInterval, + MinMaxInterval, + PercentileInterval, +) +from .stretch import ( + AsinhStretch, + BaseStretch, + LinearStretch, + LogStretch, + PowerStretch, + SinhStretch, + SqrtStretch, +) + +if HAS_MATPLOTLIB: + from matplotlib.colors import Normalize +else: + + class Normalize: + def __init__(self, *args, **kwargs): + raise ImportError("matplotlib is required in order to use this class.") + + +__all__ = [ + "ImageNormalize", + "SimpleNorm", + "imshow_norm", + "imshow_simple_norm", + "simple_norm", +] + +__doctest_requires__ = {"*": ["matplotlib"]} + + +class ImageNormalize(Normalize): + """ + Normalization class to be used with Matplotlib. + + Parameters + ---------- + data : ndarray, optional + The image array. This input is used only if ``interval`` is + also input. ``data`` and ``interval`` are used to compute the + vmin and/or vmax values only if ``vmin`` or ``vmax`` are not + input. + interval : `~astropy.visualization.BaseInterval` subclass instance, optional + The interval object to apply to the input ``data`` to determine + the ``vmin`` and ``vmax`` values. This input is used only if + ``data`` is also input. ``data`` and ``interval`` are used to + compute the vmin and/or vmax values only if ``vmin`` or ``vmax`` + are not input. + vmin, vmax : float, optional + The minimum and maximum levels to show for the data. The + ``vmin`` and ``vmax`` inputs override any calculated values from + the ``interval`` and ``data`` inputs. + stretch : `~astropy.visualization.BaseStretch` subclass instance + The stretch object to apply to the data. The default is + `~astropy.visualization.LinearStretch`. + clip : bool, optional + If `True`, data values outside the [0:1] range are clipped to + the [0:1] range. + invalid : None or float, optional + Value to assign NaN values generated by this class. NaNs in the + input ``data`` array are not changed. For matplotlib + normalization, the ``invalid`` value should map to the + matplotlib colormap "under" value (i.e., any finite value < 0). + If `None`, then NaN values are not replaced. This keyword has + no effect if ``clip=True``. + + Notes + ----- + If ``vmin == vmax``, the input data will be mapped to 0. + """ + + def __init__( + self, + data: np.ndarray | None = None, + interval: BaseInterval | None = None, + vmin: float | None = None, + vmax: float | None = None, + stretch: BaseStretch = LinearStretch(), + clip: bool = False, + invalid: float | None = -1.0, + ): + # this super call checks for matplotlib + super().__init__(vmin=vmin, vmax=vmax, clip=clip) + + self.vmin = vmin + self.vmax = vmax + + if stretch is None: + raise ValueError("stretch must be input") + if not isinstance(stretch, BaseStretch): + raise TypeError("stretch must be an instance of a BaseStretch subclass") + self.stretch = stretch + + if interval is not None and not isinstance(interval, BaseInterval): + raise TypeError("interval must be an instance of a BaseInterval subclass") + self.interval = interval + + self.inverse_stretch = stretch.inverse + self.clip = clip + self.invalid = invalid + + # Define vmin and vmax if not None and data was input + if data is not None: + self._set_limits(data) + + def _set_limits(self, data): + if self.vmin is not None and self.vmax is not None: + return + + # Define vmin and vmax from the interval class if not None + if self.interval is None: + if self.vmin is None: + self.vmin = np.min(data[np.isfinite(data)]) + if self.vmax is None: + self.vmax = np.max(data[np.isfinite(data)]) + else: + _vmin, _vmax = self.interval.get_limits(data) + if self.vmin is None: + self.vmin = _vmin + if self.vmax is None: + self.vmax = _vmax + + # Override the matplotlib method + def autoscale_None(self, A): + """ + If vmin or vmax are not set, set them according to the interval. + If no interval is set, set them to the min/max of the data array. + """ + self._set_limits(A) + + def __call__(self, values, clip=None, invalid=None): + """ + Transform values using this normalization. + + Parameters + ---------- + values : array-like + The input values. + clip : bool, optional + If `True`, values outside the [0:1] range are clipped to the + [0:1] range. If `None` then the ``clip`` value from the + `ImageNormalize` instance is used (the default of which is + `False`). + invalid : None or float, optional + Value to assign NaN values generated by this class. NaNs in + the input ``data`` array are not changed. For matplotlib + normalization, the ``invalid`` value should map to the + matplotlib colormap "under" value (i.e., any finite value < + 0). If `None`, then the `ImageNormalize` instance value is + used. This keyword has no effect if ``clip=True``. + """ + if clip is None: + clip = self.clip + + if invalid is None: + invalid = self.invalid + + if isinstance(values, ma.MaskedArray): + if clip: + mask = False + else: + mask = values.mask + values = values.filled(self.vmax) + else: + mask = False + + # Make sure scalars get broadcast to 1-d + if np.isscalar(values): + values = np.array([values], dtype=float) + else: + # copy because of in-place operations after + values = np.array(values, copy=True, dtype=float) + + # Define vmin and vmax if not None + self._set_limits(values) + + if self.vmin == self.vmax: + values *= 0.0 + elif self.vmin > self.vmax: + raise ValueError("vmin must be less than or equal to vmax") + else: + # Normalize based on vmin and vmax + np.subtract(values, self.vmin, out=values) + np.true_divide(values, self.vmax - self.vmin, out=values) + + # Clip to the 0 to 1 range + if clip: + values = np.clip(values, 0.0, 1.0, out=values) + + # Stretch values + if self.stretch._supports_invalid_kw: + values = self.stretch(values, out=values, clip=False, invalid=invalid) + else: + values = self.stretch(values, out=values, clip=False) + + # Convert to masked array for matplotlib + return ma.array(values, mask=mask) + + def inverse(self, values, invalid=None): + # Find unstretched values in range 0 to 1 + if self.inverse_stretch._supports_invalid_kw: + values_norm = self.inverse_stretch(values, clip=False, invalid=invalid) + else: + values_norm = self.inverse_stretch(values, clip=False) + + # Scale to original range + return values_norm * (self.vmax - self.vmin) + self.vmin + + +class SimpleNorm: + """ + Class to create a normalization object that can be used for + displaying images with Matplotlib. + + This convenience class provides the most common image stretching + functions. Additional stretch functions are available in + `~astropy.visualization.mpl_normalize.ImageNormalize`. + + Parameters + ---------- + stretch : {'linear', 'sqrt', 'power', log', 'asinh', 'sinh'}, optional + The stretch function to apply to the image. The default is + 'linear'. + + percent : float, optional + The percentage of the image values used to determine the pixel + values of the minimum and maximum cut levels. The lower cut + level will set at the ``(100 - percent) / 2`` percentile, while + the upper cut level will be set at the ``(100 + percent) / 2`` + percentile. The default is 100.0. ``percent`` is ignored if + either ``min_percent`` or ``max_percent`` is input. + + min_percent : float, optional + The percentile value used to determine the pixel value of + minimum cut level. The default is 0.0. ``min_percent`` overrides + ``percent``. + + max_percent : float, optional + The percentile value used to determine the pixel value of + maximum cut level. The default is 100.0. ``max_percent`` + overrides ``percent``. + + vmin : float, optional + The pixel value of the minimum cut level. Data values less + than ``vmin`` will set to ``vmin`` before stretching the + image. The default is the image minimum. ``vmin`` overrides + ``min_percent``. + + vmax : float, optional + The pixel value of the maximum cut level. Data values greater + than ``vmax`` will set to ``vmax`` before stretching the + image. The default is the image maximum. ``vmax`` overrides + ``max_percent``. + + power : float, optional + The power index for ``stretch='power'``. The default is 1.0. + + log_a : float, optional + The log index for ``stretch='log'``. The default is 1000. + + asinh_a : float, optional + For ``stretch='asinh'``, the value where the asinh curve + transitions from linear to logarithmic behavior, expressed as a + fraction of the normalized image. Must be in the range between 0 + and 1. The default is 0.1. + + sinh_a : float, optional + The scaling parameter for ``stretch='sinh'``. The default is + 0.3. + + clip : bool, optional + If `True`, data values outside the [0:1] range are clipped to + the [0:1] range. + + invalid : None or float, optional + Value to assign NaN values generated by the normalization. NaNs + in the input ``data`` array are not changed. For matplotlib + normalization, the ``invalid`` value should map to the + matplotlib colormap "under" value (i.e., any finite value < 0). + If `None`, then NaN values are not replaced. This keyword has no + effect if ``clip=True``. + + See Also + -------- + simple_norm + + Examples + -------- + .. plot:: + :include-source: + + import numpy as np + import matplotlib.pyplot as plt + from astropy.visualization import SimpleNorm + + image = np.arange(65536).reshape((256, 256)) + snorm = SimpleNorm('sqrt', percent=98) + norm = snorm(image) + fig, ax = plt.subplots() + axim = ax.imshow(image, norm=norm, origin='lower') + fig.colorbar(axim) + """ + + def __init__( + self, + stretch="linear", + percent=None, + *, + min_percent=None, + max_percent=None, + vmin=None, + vmax=None, + power=1.0, + log_a=1000, + asinh_a=0.1, + sinh_a=0.3, + clip=False, + invalid=-1.0, + ): + if percent is not None: + interval = PercentileInterval(percent) + elif min_percent is not None or max_percent is not None: + interval = AsymmetricPercentileInterval( + lower_percentile=min_percent, upper_percentile=max_percent + ) + elif vmin is not None or vmax is not None: + interval = ManualInterval(vmin, vmax) + else: + interval = MinMaxInterval() + self.interval = interval + + if stretch == "linear": + stretch = LinearStretch() + elif stretch == "sqrt": + stretch = SqrtStretch() + elif stretch == "power": + stretch = PowerStretch(power) + elif stretch == "log": + stretch = LogStretch(log_a) + elif stretch == "asinh": + stretch = AsinhStretch(asinh_a) + elif stretch == "sinh": + stretch = SinhStretch(sinh_a) + else: + raise ValueError(f"Unknown stretch: {stretch}.") + self.stretch = stretch + + self.clip = clip + self.invalid = invalid + + def __call__(self, data): + """ + Return an `ImageNormalize` instance that can be used for displaying + images with Matplotlib. + + Parameters + ---------- + data : ndarray + The image array. + + Returns + ------- + result : `ImageNormalize` instance + An `ImageNormalize` instance that can be used for + displaying images with Matplotlib. + """ + vmin, vmax = self.interval.get_limits(data) + return ImageNormalize( + vmin=vmin, + vmax=vmax, + stretch=self.stretch, + clip=self.clip, + invalid=self.invalid, + ) + + def imshow(self, data, ax=None, **kwargs): + """ + A convenience function to display an image using matplotlib's + `matplotlib.pyplot.imshow` function with the normalization + defined by this class. + + Parameters + ---------- + data : 2D or 3D array-like + The data to display. Can be whatever + `~matplotlib.pyplot.imshow` and `ImageNormalize` both + accept. + + ax : None or `~matplotlib.axes.Axes`, optional + The matplotlib axes on which to plot. If `None`, then the + current `~matplotlib.axes.Axes` instance is used. + + **kwargs : dict, optional + Keywords arguments passed to `~matplotlib.pyplot.imshow`. + Cannot include the ``norm`` or ``X`` keyword. + + Returns + ------- + result : `~matplotlib.image.AxesImage` + The `~matplotlib.image.AxesImage` generated by + `~matplotlib.pyplot.imshow`. + + Examples + -------- + .. plot:: + :include-source: + + import numpy as np + import matplotlib.pyplot as plt + from astropy.visualization import SimpleNorm + + image = np.arange(65536).reshape((256, 256)) + snorm = SimpleNorm('sqrt', percent=98) + fig, ax = plt.subplots() + axim = snorm.imshow(image, ax=ax, origin='lower') + fig.colorbar(axim) + """ + import matplotlib.pyplot as plt + + if ax is None: + ax = plt.gca() + + if "norm" in kwargs: + raise ValueError( + "This class already defines the norm. Use " + "matplotlib.pyplot.imshow directly to use your norm." + ) + + return ax.imshow(data, norm=self(data), **kwargs) + + +@future_keyword_only( + [ + "power", + "asinh_a", + "vmin", + "vmax", + "min_percent", + "max_percent", + "percent", + "clip", + "log_a", + "invalid", + "sinh_a", + ], + since=["7.1"] * 11, +) +def simple_norm( + data, + stretch="linear", + power=1.0, + asinh_a=0.1, + vmin=None, + vmax=None, + min_percent=None, + max_percent=None, + percent=None, + clip=False, + log_a=1000, + invalid=-1.0, + sinh_a=0.3, +): + """ + Return a Normalization class that can be used for displaying images + with Matplotlib. + + This function enables only a subset of image stretching functions + available in `~astropy.visualization.mpl_normalize.ImageNormalize`. + + This function is used by the + ``astropy.visualization.scripts.fits2bitmap`` script. + + Parameters + ---------- + data : ndarray + The image array. + + stretch : {'linear', 'sqrt', 'power', log', 'asinh', 'sinh'}, optional + The stretch function to apply to the image. The default is + 'linear'. + + power : float, optional + The power index for ``stretch='power'``. The default is 1.0. + + asinh_a : float, optional + For ``stretch='asinh'``, the value where the asinh curve + transitions from linear to logarithmic behavior, expressed as a + fraction of the normalized image. Must be in the range between 0 + and 1. The default is 0.1. + + vmin : float, optional + The pixel value of the minimum cut level. Data values less + than ``vmin`` will set to ``vmin`` before stretching the + image. The default is the image minimum. ``vmin`` overrides + ``min_percent``. + + vmax : float, optional + The pixel value of the maximum cut level. Data values greater + than ``vmax`` will set to ``vmax`` before stretching the image. + The default is the image maximum. ``vmax`` overrides + + min_percent : float, optional + The percentile value used to determine the pixel value of + minimum cut level. The default is 0.0. ``min_percent`` overrides + ``percent``. + + max_percent : float, optional + The percentile value used to determine the pixel value of + maximum cut level. The default is 100.0. ``max_percent`` + overrides ``percent``. + + percent : float, optional + The percentage of the image values used to determine the pixel + values of the minimum and maximum cut levels. The lower cut + level will set at the ``(100 - percent) / 2`` percentile, while + the upper cut level will be set at the ``(100 + percent) / 2`` + percentile. The default is 100.0. ``percent`` is ignored if + either ``min_percent`` or ``max_percent`` is input. + + clip : bool, optional + If `True`, data values outside the [0:1] range are clipped to + the [0:1] range. + + log_a : float, optional + The log index for ``stretch='log'``. The default is 1000. + + invalid : None or float, optional + Value to assign NaN values generated by the normalization. NaNs + in the input ``data`` array are not changed. For matplotlib + normalization, the ``invalid`` value should map to the + matplotlib colormap "under" value (i.e., any finite value < 0). + If `None`, then NaN values are not replaced. This keyword has no + effect if ``clip=True``. + + sinh_a : float, optional + The scaling parameter for ``stretch='sinh'``. The default is + 0.3. + + Returns + ------- + result : `ImageNormalize` instance + An `ImageNormalize` instance that can be used for displaying + images with Matplotlib. + + See Also + -------- + SimpleNorm + """ + simple_norm = SimpleNorm( + stretch=stretch, + percent=percent, + min_percent=min_percent, + max_percent=max_percent, + vmin=vmin, + vmax=vmax, + power=power, + log_a=log_a, + asinh_a=asinh_a, + sinh_a=sinh_a, + clip=clip, + invalid=invalid, + ) + return simple_norm(data) + + +# used in imshow_norm +_norm_sig = inspect.signature(ImageNormalize) +# used in imshow_simple_norm +_snorm_sig = inspect.signature(SimpleNorm) + + +def imshow_norm(data, ax=None, **kwargs): + """A convenience function to call matplotlib's `matplotlib.pyplot.imshow` + function, using an `ImageNormalize` object as the normalization. + + Parameters + ---------- + data : 2D or 3D array-like + The data to show. Can be whatever `~matplotlib.pyplot.imshow` and + `ImageNormalize` both accept. See `~matplotlib.pyplot.imshow`. + ax : None or `~matplotlib.axes.Axes`, optional + If None, use pyplot's imshow. Otherwise, calls ``imshow`` method of + the supplied axes. + **kwargs : dict, optional + All other keyword arguments are parsed first by the + `ImageNormalize` initializer, then to + `~matplotlib.pyplot.imshow`. + + Returns + ------- + result : tuple + A tuple containing the `~matplotlib.image.AxesImage` generated + by `~matplotlib.pyplot.imshow` as well as the `ImageNormalize` + instance. + + Notes + ----- + The ``norm`` matplotlib keyword is not supported. + + Examples + -------- + .. plot:: + :include-source: + + import numpy as np + import matplotlib.pyplot as plt + from astropy.visualization import (imshow_norm, MinMaxInterval, + SqrtStretch) + + # Generate and display a test image + image = np.arange(65536).reshape((256, 256)) + fig = plt.figure() + ax = fig.add_subplot(1, 1, 1) + im, norm = imshow_norm(image, ax, origin='lower', + interval=MinMaxInterval(), + stretch=SqrtStretch()) + fig.colorbar(im) + """ + if ax is None: + if not HAS_MATPLOTLIB: + raise ModuleNotFoundError("matplotlib is required for imshow norm") + + import matplotlib.pyplot as plt + + ax = plt.gca() + + if "norm" in kwargs: + raise ValueError( + "There is no point in using imshow_norm if you give " + "the ``norm`` keyword - use imshow directly if you " + "want that." + ) + + imshow_kwargs = dict(kwargs) + + norm_kwargs = {"data": data} + for pname in _norm_sig.parameters: + if pname in kwargs: + norm_kwargs[pname] = imshow_kwargs.pop(pname) + + imshow_kwargs["norm"] = ImageNormalize(**norm_kwargs) + imshow_result = ax.imshow(data, **imshow_kwargs) + + return imshow_result, imshow_kwargs["norm"] + + +def imshow_simple_norm(data, ax=None, **kwargs) -> tuple: + """A convenience function to call matplotlib's `matplotlib.pyplot.imshow` + function, using an `SimpleNorm` object as the normalization. + + Parameters + ---------- + data : 2D or 3D array-like + The data to show. Can be whatever `~matplotlib.pyplot.imshow` and + `SimpleNorm` both accept. See `~matplotlib.pyplot.imshow`. + ax : None or `~matplotlib.axes.Axes`, optional + If None, use pyplot's imshow. Otherwise, calls ``imshow`` method of + the supplied axes. + **kwargs : dict, optional + All other keyword arguments are parsed first by the + `SimpleNorm` initializer, then to + `~matplotlib.pyplot.imshow`. + + Returns + ------- + result : tuple + A tuple containing the `~matplotlib.image.AxesImage` generated + by `~matplotlib.pyplot.imshow` as well as the `ImageNormalize` + instance generated by `SimpleNorm`. + + Notes + ----- + The ``norm`` matplotlib keyword is not supported. + + Examples + -------- + .. plot:: + :include-source: + + import numpy as np + import matplotlib.pyplot as plt + from astropy.visualization import imshow_simple_norm + + # Generate and display a test image + image = np.arange(65536).reshape((256, 256)) + fig = plt.figure() + ax = fig.add_subplot(1, 1, 1) + im, norm = imshow_simple_norm(image, ax, origin='lower', + min_percent=1, max_percent=99.9, + stretch='sinh') + fig.colorbar(im) + """ + if ax is None: + if not HAS_MATPLOTLIB: + raise ModuleNotFoundError("matplotlib is required for imshow_simple_norm") + + import matplotlib.pyplot as plt + + ax = plt.gca() + + if "norm" in kwargs: + raise ValueError( + "There is no point in using imshow_simple_norm if you give " + "the norm keyword - use imshow directly if you " + "want that." + ) + + imshow_kwargs = dict(kwargs) + + snorm_kwargs = {} + for pname in _snorm_sig.parameters: + if pname in kwargs: + snorm_kwargs[pname] = imshow_kwargs.pop(pname) + + imshow_kwargs["norm"] = SimpleNorm(**snorm_kwargs)(data) + imshow_result = ax.imshow(data, **imshow_kwargs) + + return imshow_result, imshow_kwargs["norm"] diff --git a/astropy/visualization/mpl_style.py b/astropy/visualization/mpl_style.py new file mode 100644 index 000000000000..7adc0253a811 --- /dev/null +++ b/astropy/visualization/mpl_style.py @@ -0,0 +1,78 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +# This module contains dictionaries that can be used to set a matplotlib +# plotting style. It is no longer documented/recommended as of Astropy v3.0 +# but is kept here for backward-compatibility. + +__all__ = ["astropy_mpl_style", "astropy_mpl_style_1"] + +# Version 1 astropy plotting style for matplotlib +astropy_mpl_style_1 = { + # Lines + "lines.linewidth": 1.7, + "lines.antialiased": True, + # Patches + "patch.linewidth": 1.0, + "patch.facecolor": "#348ABD", + "patch.edgecolor": "#CCCCCC", + "patch.antialiased": True, + # Images + "image.cmap": "gist_heat", + "image.origin": "upper", + # Font + "font.size": 12.0, + # Axes + "axes.facecolor": "#FFFFFF", + "axes.edgecolor": "#AAAAAA", + "axes.linewidth": 1.0, + "axes.grid": True, + "axes.titlesize": "x-large", + "axes.labelsize": "large", + "axes.labelcolor": "k", + "axes.axisbelow": True, + # Ticks + "xtick.major.size": 0, + "xtick.minor.size": 0, + "xtick.major.pad": 6, + "xtick.minor.pad": 6, + "xtick.color": "#565656", + "xtick.direction": "in", + "ytick.major.size": 0, + "ytick.minor.size": 0, + "ytick.major.pad": 6, + "ytick.minor.pad": 6, + "ytick.color": "#565656", + "ytick.direction": "in", + # Legend + "legend.fancybox": True, + "legend.loc": "best", + # Figure + "figure.figsize": [8, 6], + "figure.facecolor": "1.0", + "figure.edgecolor": "0.50", + "figure.subplot.hspace": 0.5, + # Other + "savefig.dpi": 72, +} +color_cycle = [ + "#348ABD", # blue + "#7A68A6", # purple + "#A60628", # red + "#467821", # green + "#CF4457", # pink + "#188487", # turquoise + "#E24A33", +] # orange + +try: + # This is a dependency of matplotlib, so should be present if matplotlib + # is installed. + from cycler import cycler + + astropy_mpl_style_1["axes.prop_cycle"] = cycler("color", color_cycle) +except ImportError: + astropy_mpl_style_1["axes.color_cycle"] = color_cycle + + +astropy_mpl_style = astropy_mpl_style_1 +"""The most recent version of the astropy plotting style.""" diff --git a/astropy/visualization/scripts/__init__.py b/astropy/visualization/scripts/__init__.py new file mode 100644 index 000000000000..9dce85d06f83 --- /dev/null +++ b/astropy/visualization/scripts/__init__.py @@ -0,0 +1 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst diff --git a/astropy/visualization/scripts/fits2bitmap.py b/astropy/visualization/scripts/fits2bitmap.py new file mode 100644 index 000000000000..6fc5c06be20a --- /dev/null +++ b/astropy/visualization/scripts/fits2bitmap.py @@ -0,0 +1,248 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +from pathlib import Path + +from astropy import log +from astropy.io.fits import getdata +from astropy.visualization.mpl_normalize import simple_norm + +__all__ = ["fits2bitmap", "main"] + + +def fits2bitmap( + filename, + ext=0, + out_fn=None, + stretch="linear", + power=1.0, + asinh_a=0.1, + vmin=None, + vmax=None, + min_percent=None, + max_percent=None, + percent=None, + cmap="Greys_r", +): + """ + Create a bitmap file from a FITS image, applying a stretching + transform between minimum and maximum cut levels and a matplotlib + colormap. + + Parameters + ---------- + filename : str | PathLike + The filename of the FITS file. + ext : int + FITS extension name or number of the image to convert. The + default is 0. + out_fn : str | PathLike + The filename of the output bitmap image. The type of bitmap is + determined by the filename extension (e.g. '.jpg', '.png'). The + default is a PNG file with the same name as the FITS file. + stretch : {'linear', 'sqrt', 'power', log', 'asinh'} + The stretching function to apply to the image. The default is + 'linear'. + power : float, optional + The power index for ``stretch='power'``. The default is 1.0. + asinh_a : float, optional + For ``stretch='asinh'``, the value where the asinh curve + transitions from linear to logarithmic behavior, expressed as a + fraction of the normalized image. Must be in the range between 0 + and 1. The default is 0.1. + vmin : float, optional + The pixel value of the minimum cut level. Data values less + than ``vmin`` will set to ``vmin`` before stretching the + image. The default is the image minimum. ``vmin`` overrides + ``min_percent``. + vmax : float, optional + The pixel value of the maximum cut level. Data values greater + than ``vmax`` will set to ``vmax`` before stretching the + image. The default is the image maximum. ``vmax`` overrides + ``max_percent``. + min_percent : float, optional + The percentile value used to determine the pixel value of + minimum cut level. The default is 0.0. ``min_percent`` overrides + ``percent``. + max_percent : float, optional + The percentile value used to determine the pixel value of + maximum cut level. The default is 100.0. ``max_percent`` + overrides ``percent``. + percent : float, optional + The percentage of the image values used to determine the pixel + values of the minimum and maximum cut levels. The lower cut + level will set at the ``(100 - percent) / 2`` percentile, while + the upper cut level will be set at the ``(100 + percent) / 2`` + percentile. The default is 100.0. ``percent`` is ignored if + either ``min_percent`` or ``max_percent`` is input. + cmap : str + The matplotlib color map name. The default is 'Greys_r'. + """ + import matplotlib as mpl + import matplotlib.image as mimg + + # __main__ gives ext as a string + try: + ext = int(ext) + except ValueError: + pass + + try: + image = getdata(filename, ext) + except Exception as e: + log.critical(e) + return 1 + + if image.ndim != 2: + log.critical(f"data in FITS extension {ext} is not a 2D array") + + filename = Path(filename) + + if out_fn is None: + out_fn = filename.with_suffix("") + # If the filename ends with .fits.*, remove the * extension. + if out_fn.suffix == ".fits": + out_fn = out_fn.with_suffix("") + out_fn = out_fn.with_suffix(out_fn.suffix + ".png") + else: + out_fn = Path(out_fn) + out_format = out_fn.suffix[1:] + + if cmap not in mpl.colormaps: + log.critical(f"{cmap} is not a valid matplotlib colormap name.") + return 1 + + norm = simple_norm( + image, + stretch=stretch, + power=power, + asinh_a=asinh_a, + vmin=vmin, + vmax=vmax, + min_percent=min_percent, + max_percent=max_percent, + percent=percent, + ) + + mimg.imsave(out_fn, norm(image), cmap=cmap, origin="lower", format=out_format) + log.info(f"Saved file to {out_fn}.") + + +def main(args=None): + import argparse + + parser = argparse.ArgumentParser( + description="Create a bitmap file from a FITS image." + ) + # TODO: pass suggest_on_error as kwarg when PYTHON_LT_14 is dropped + parser.suggest_on_error = True + + parser.add_argument( + "-e", + "--ext", + metavar="hdu", + default=0, + help="Specify the HDU extension number or name (Default is 0).", + ) + parser.add_argument( + "-o", + metavar="filename", + type=str, + default=None, + help=( + "Filename for the output image (Default is a " + "PNG file with the same name as the FITS file)." + ), + ) + parser.add_argument( + "--stretch", + type=str, + default="linear", + help=( + 'Type of image stretching ("linear", "sqrt", ' + '"power", "log", or "asinh") (Default is "linear").' + ), + ) + parser.add_argument( + "--power", + type=float, + default=1.0, + help='Power index for "power" stretching (Default is 1.0).', + ) + parser.add_argument( + "--asinh_a", + type=float, + default=0.1, + help=( + "The value in normalized image where the asinh " + "curve transitions from linear to logarithmic " + 'behavior (used only for "asinh" stretch) ' + "(Default is 0.1)." + ), + ) + parser.add_argument( + "--vmin", + type=float, + default=None, + help="The pixel value of the minimum cut level (Default is the image minimum).", + ) + parser.add_argument( + "--vmax", + type=float, + default=None, + help="The pixel value of the maximum cut level (Default is the image maximum).", + ) + parser.add_argument( + "--min_percent", + type=float, + default=None, + help=( + "The percentile value used to determine the " + "minimum cut level (Default is 0)." + ), + ) + parser.add_argument( + "--max_percent", + type=float, + default=None, + help=( + "The percentile value used to determine the " + "maximum cut level (Default is 100)." + ), + ) + parser.add_argument( + "--percent", + type=float, + default=None, + help=( + "The percentage of the image values used to " + "determine the pixel values of the minimum and " + "maximum cut levels (Default is 100)." + ), + ) + parser.add_argument( + "--cmap", + metavar="colormap_name", + type=str, + default="Greys_r", + help='matplotlib color map name (Default is "Greys_r").', + ) + parser.add_argument( + "filename", nargs="+", help="Path to one or more FITS files to convert" + ) + args = parser.parse_args(args) + + for filename in args.filename: + fits2bitmap( + filename, + ext=args.ext, + out_fn=args.o, + stretch=args.stretch, + vmin=args.vmin, + vmax=args.vmax, + min_percent=args.min_percent, + max_percent=args.max_percent, + percent=args.percent, + power=args.power, + asinh_a=args.asinh_a, + cmap=args.cmap, + ) diff --git a/astropy/visualization/scripts/tests/__init__.py b/astropy/visualization/scripts/tests/__init__.py new file mode 100644 index 000000000000..9dce85d06f83 --- /dev/null +++ b/astropy/visualization/scripts/tests/__init__.py @@ -0,0 +1 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst diff --git a/astropy/visualization/scripts/tests/test_fits2bitmap.py b/astropy/visualization/scripts/tests/test_fits2bitmap.py new file mode 100644 index 000000000000..f841ee78c0f8 --- /dev/null +++ b/astropy/visualization/scripts/tests/test_fits2bitmap.py @@ -0,0 +1,70 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +import numpy as np +import pytest + +from astropy.io import fits +from astropy.utils.compat.optional_deps import HAS_MATPLOTLIB + +if HAS_MATPLOTLIB: + import matplotlib.image as mpimg + + from astropy.visualization.scripts.fits2bitmap import fits2bitmap, main + + +@pytest.mark.skipif(not HAS_MATPLOTLIB, reason="requires matplotlib") +class TestFits2Bitmap: + def setup_class(self): + self.filename = "test.fits" + self.array = np.arange(16384).reshape((128, 128)) + + def test_function(self, tmp_path): + filename = tmp_path / self.filename + fits.writeto(filename, self.array) + fits2bitmap(filename) + + def test_script(self, tmp_path): + filename = str(tmp_path / self.filename) + fits.writeto(filename, self.array) + main([filename, "-e", "0"]) + + def test_exten_num(self, tmp_path): + filename = str(tmp_path / self.filename) + hdu1 = fits.PrimaryHDU() + hdu2 = fits.ImageHDU(self.array) + hdulist = fits.HDUList([hdu1, hdu2]) + hdulist.writeto(filename) + main([filename, "-e", "1"]) + + def test_exten_name(self, tmp_path): + filename = str(tmp_path / self.filename) + hdu1 = fits.PrimaryHDU() + extname = "SCI" + hdu2 = fits.ImageHDU(self.array) + hdu2.header["EXTNAME"] = extname + hdulist = fits.HDUList([hdu1, hdu2]) + hdulist.writeto(filename) + main([filename, "-e", extname]) + + @pytest.mark.parametrize("file_exten", [".gz", ".bz2"]) + def test_compressed_fits(self, tmp_path, file_exten): + filename = str(tmp_path / f"test.fits{file_exten}") + fits.writeto(filename, self.array) + main([filename, "-e", "0"]) + + def test_orientation(self, tmp_path): + """ + Regression test to check the image vertical orientation/origin. + """ + + filename = str(tmp_path / self.filename) + out_filename = "fits2bitmap_test.png" + out_filename = str(tmp_path / out_filename) + data = np.zeros((32, 32)) + data[0:16, :] = 1.0 + fits.writeto(filename, data) + main([filename, "-e", "0", "-o", out_filename]) + + img = mpimg.imread(out_filename) + assert img[0, 0, 0] == 0 + assert img[31, 31, 0] == 1 diff --git a/astropy/visualization/stretch.py b/astropy/visualization/stretch.py new file mode 100644 index 000000000000..437b76aae495 --- /dev/null +++ b/astropy/visualization/stretch.py @@ -0,0 +1,991 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +""" +Classes that deal with stretching, i.e. mapping a range of [0:1] values onto +another set of [0:1] values with a transformation. +""" + +import numpy as np + +from .transform import BaseTransform, CompositeTransform + +__all__ = [ + "AsinhStretch", + "BaseStretch", + "CompositeStretch", + "ContrastBiasStretch", + "HistEqStretch", + "LinearStretch", + "LogStretch", + "PowerDistStretch", + "PowerStretch", + "SinhStretch", + "SqrtStretch", + "SquaredStretch", +] + + +def _logn(n, x, out=None): + """Calculate the log base n of x.""" + # We define this because numpy.emath.logn doesn't support the out + # keyword. + if out is None: + return np.log(x) / np.log(n) + else: + np.log(x, out=out) + np.true_divide(out, np.log(n), out=out) + return out + + +def _prepare(values, clip=True, out=None): + """ + Prepare the data by optionally clipping and copying, and return the + array that should be subsequently used for in-place calculations. + """ + if clip: + return np.clip(values, 0.0, 1.0, out=out) + else: + if out is None: + return np.array(values, copy=True) + else: + out[:] = np.asarray(values) + return out + + +class BaseStretch(BaseTransform): + """ + Base class for the stretch classes, which when called with an array + of values in the range [0:1], returns an transformed array of values + also in the range [0:1]. + """ + + @property + def _supports_invalid_kw(self): + return False + + def __add__(self, other): + return CompositeStretch(other, self) + + def __call__(self, values, clip=True, out=None): + """ + Transform values using this stretch. + + Parameters + ---------- + values : array-like + The input values, which should already be normalized to the + [0:1] range. + clip : bool, optional + If `True` (default), values outside the [0:1] range are + clipped to the [0:1] range. + out : ndarray, optional + If specified, the output values will be placed in this array + (typically used for in-place calculations). + + Returns + ------- + result : ndarray + The transformed values. + """ + + @property + def inverse(self): + """A stretch object that performs the inverse operation.""" + + +class LinearStretch(BaseStretch): + """ + A linear stretch with a slope and offset. + + The stretch is given by: + + .. math:: + y = slope * x + intercept + + Parameters + ---------- + slope : float, optional + The ``slope`` parameter used in the above formula. Default is 1. + intercept : float, optional + The ``intercept`` parameter used in the above formula. Default is 0. + + Examples + -------- + .. plot:: + :show-source-link: + + import numpy as np + from astropy.visualization import LinearStretch + from matplotlib import pyplot as plt + + fig, ax = plt.subplots(figsize=(5, 5)) + + x = np.linspace(0, 1, 100) + slopes = [1, 0.5, 1.3, 1.4, 2.0] + intercepts = [0, 0.0, -0.4, 0., 0.2] + for slope, intercept in zip(slopes, intercepts): + stretch = LinearStretch(slope, intercept) + label = f'{slope=}, {intercept=}' + ax.plot(x, stretch(x, clip=True), label=label) + + ax.axis('equal') + ax.plot(x, x, ls='dotted', color='k', alpha=0.3) + ax.set_xlim(0, 1) + ax.set_ylim(0, 1) + ax.set_xlabel('Input Value') + ax.set_ylabel('Output Value') + ax.set_title(stretch.__class__.__name__) + ax.legend(loc='lower right', fontsize=8) + """ + + def __init__(self, slope=1, intercept=0): + super().__init__() + self.slope = slope + self.intercept = intercept + + def __call__(self, values, clip=True, out=None): + values = _prepare(values, clip=clip, out=out) + if self.slope != 1: + np.multiply(values, self.slope, out=values) + if self.intercept != 0: + np.add(values, self.intercept, out=values) + + if clip: + np.clip(values, 0, 1, out=values) + + return values + + @property + def inverse(self): + """A stretch object that performs the inverse operation.""" + return LinearStretch(1.0 / self.slope, -self.intercept / self.slope) + + +class SqrtStretch(BaseStretch): + r""" + A square root stretch. + + The stretch is given by: + + .. math:: + y = \sqrt{x} + + Examples + -------- + .. plot:: + :show-source-link: + + import numpy as np + from astropy.visualization import SqrtStretch + from matplotlib import pyplot as plt + + fig, ax = plt.subplots(figsize=(5, 5)) + + x = np.linspace(0, 1, 100) + stretch = SqrtStretch() + ax.plot(x, stretch(x, clip=True)) + + ax.axis('equal') + ax.plot(x, x, ls='dotted', color='k', alpha=0.3) + ax.set_xlim(0, 1) + ax.set_ylim(0, 1) + ax.set_xlabel('Input Value') + ax.set_ylabel('Output Value') + ax.set_title(stretch.__class__.__name__) + """ + + @property + def _supports_invalid_kw(self): + return True + + def __call__(self, values, clip=True, out=None, invalid=None): + """ + Transform values using this stretch. + + Parameters + ---------- + values : array-like + The input values, which should already be normalized to the + [0:1] range. + clip : bool, optional + If `True` (default), values outside the [0:1] range are + clipped to the [0:1] range. + out : ndarray, optional + If specified, the output values will be placed in this array + (typically used for in-place calculations). + invalid : None or float, optional + Value to assign NaN values generated by this class. NaNs in + the input ``values`` array are not changed. This option is + generally used with matplotlib normalization classes, where + the ``invalid`` value should map to the matplotlib colormap + "under" value (i.e., any finite value < 0). If `None`, then + NaN values are not replaced. This keyword has no effect if + ``clip=True``. + + Returns + ------- + result : ndarray + The transformed values. + """ + values = _prepare(values, clip=clip, out=out) + replace_invalid = not clip and invalid is not None + with np.errstate(invalid="ignore"): + if replace_invalid: + idx = values < 0 + np.sqrt(values, out=values) + + if replace_invalid: + # Assign new NaN (i.e., NaN not in the original input + # values, but generated by this class) to the invalid value. + values[idx] = invalid + + return values + + @property + def inverse(self): + """A stretch object that performs the inverse operation.""" + return PowerStretch(2) + + +class PowerStretch(BaseStretch): + r""" + A power stretch. + + The stretch is given by: + + .. math:: + y = x^a + + Parameters + ---------- + a : float + The power index (see the above formula). ``a`` must be greater + than 0. + + Examples + -------- + .. plot:: + :show-source-link: + + import numpy as np + from astropy.visualization import PowerStretch + from matplotlib import pyplot as plt + + fig, ax = plt.subplots(figsize=(5, 5)) + + x = np.linspace(0, 1, 100) + a_vals = (0.3, 0.5, 0.7, 1, 1.5, 2, 3) + for a in a_vals: + stretch = PowerStretch(a) + label = f'{a=}' + ax.plot(x, stretch(x, clip=True), label=label) + + ax.axis('equal') + ax.plot(x, x, ls='dotted', color='k', alpha=0.3) + ax.set_xlim(0, 1) + ax.set_ylim(0, 1) + ax.set_xlabel('Input Value') + ax.set_ylabel('Output Value') + ax.set_title(stretch.__class__.__name__) + ax.legend(loc='lower right', fontsize=8) + """ + + @property + def _supports_invalid_kw(self): + return True + + def __init__(self, a): + super().__init__() + if a <= 0: + raise ValueError("a must be > 0") + self.a = a + + def __call__(self, values, clip=True, out=None, invalid=None): + """ + Transform values using this stretch. + + Parameters + ---------- + values : array-like + The input values, which should already be normalized to the + [0:1] range. + clip : bool, optional + If `True` (default), values outside the [0:1] range are + clipped to the [0:1] range. + out : ndarray, optional + If specified, the output values will be placed in this array + (typically used for in-place calculations). + invalid : None or float, optional + Value to assign NaN values generated by this class. NaNs in + the input ``values`` array are not changed. This option is + generally used with matplotlib normalization classes, where + the ``invalid`` value should map to the matplotlib colormap + "under" value (i.e., any finite value < 0). If `None`, then + NaN values are not replaced. This keyword has no effect if + ``clip=True``. + + Returns + ------- + result : ndarray + The transformed values. + """ + values = _prepare(values, clip=clip, out=out) + replace_invalid = ( + not clip and invalid is not None and ((-1 < self.a < 0) or (0 < self.a < 1)) + ) + with np.errstate(invalid="ignore"): + if replace_invalid: + idx = values < 0 + np.power(values, self.a, out=values) + + if replace_invalid: + # Assign new NaN (i.e., NaN not in the original input + # values, but generated by this class) to the invalid value. + values[idx] = invalid + + return values + + @property + def inverse(self): + """A stretch object that performs the inverse operation.""" + return PowerStretch(1.0 / self.a) + + +class PowerDistStretch(BaseStretch): + r""" + An alternative power stretch. + + The stretch is given by: + + .. math:: + y = \frac{a^x - 1}{a - 1} + + Parameters + ---------- + a : float, optional + The ``a`` parameter used in the above formula. The stretch + becomes more linear as ``a`` approaches 1, more exponential for + ``a`` values greater than 1, and more logarithmic for ``a`` + values less than 1. ``a`` must be greater than 0, but cannot be + set to 1. Default is 1000. + + Examples + -------- + .. plot:: + :show-source-link: + + import numpy as np + from astropy.visualization import PowerDistStretch + from matplotlib import pyplot as plt + + fig, ax = plt.subplots(figsize=(5, 5)) + + x = np.linspace(0, 1, 100) + a_vals = (0.001, 0.05, 0.3, 0.8, 1.2, 3, 10, 30, 100, 1000) + for a in a_vals: + if a == 1000: + lw = 3 + else: + lw = 1 + stretch = PowerDistStretch(a) + label = f'{a=}' + ax.plot(x, stretch(x, clip=True), label=label, lw=lw) + + ax.axis('equal') + ax.plot(x, x, ls='dotted', color='k', alpha=0.3) + ax.set_xlim(0, 1) + ax.set_ylim(0, 1) + ax.set_xlabel('Input Value') + ax.set_ylabel('Output Value') + ax.set_title(stretch.__class__.__name__) + ax.legend(loc='upper left', fontsize=8) + """ + + def __init__(self, a=1000.0): + if a <= 0 or a == 1: # singularity + raise ValueError("a must be > 0, but cannot be set to 1") + super().__init__() + self.a = a + + def __call__(self, values, clip=True, out=None): + values = _prepare(values, clip=clip, out=out) + np.power(self.a, values, out=values) + np.subtract(values, 1, out=values) + np.true_divide(values, self.a - 1.0, out=values) + return values + + @property + def inverse(self): + """A stretch object that performs the inverse operation.""" + return InvertedPowerDistStretch(a=self.a) + + +class InvertedPowerDistStretch(BaseStretch): + r""" + Inverse transformation for + `~astropy.visualization.PowerDistStretch`. + + The stretch is given by: + + .. math:: + y = \frac{\log(x (a - 1) + 1)}{\log a} + + Parameters + ---------- + a : float, optional + The ``a`` parameter used in the above formula. The stretch + becomes more linear as ``a`` approaches 1, more logarithmic for + ``a`` values greater than 1, and more exponential for ``a`` + values less than 1. ``a`` must be greater than 0, but cannot be + set to 1. Default is 1000. + """ + + def __init__(self, a=1000.0): + if a <= 0 or a == 1: # singularity + raise ValueError("a must be > 0, but cannot be set to 1") + super().__init__() + self.a = a + + def __call__(self, values, clip=True, out=None): + values = _prepare(values, clip=clip, out=out) + np.multiply(values, self.a - 1.0, out=values) + np.add(values, 1, out=values) + _logn(self.a, values, out=values) + return values + + @property + def inverse(self): + """A stretch object that performs the inverse operation.""" + return PowerDistStretch(a=self.a) + + +class SquaredStretch(PowerStretch): + r""" + A convenience class for a power stretch of 2. + + The stretch is given by: + + .. math:: + y = x^2 + + Examples + -------- + .. plot:: + :show-source-link: + + import numpy as np + from astropy.visualization import SquaredStretch + from matplotlib import pyplot as plt + + fig, ax = plt.subplots(figsize=(5, 5)) + + x = np.linspace(0, 1, 100) + stretch = SquaredStretch() + ax.plot(x, stretch(x, clip=True)) + + ax.axis('equal') + ax.plot(x, x, ls='dotted', color='k', alpha=0.3) + ax.set_xlim(0, 1) + ax.set_ylim(0, 1) + ax.set_xlabel('Input Value') + ax.set_ylabel('Output Value') + ax.set_title(stretch.__class__.__name__) + """ + + def __init__(self): + super().__init__(2) + + @property + def inverse(self): + """A stretch object that performs the inverse operation.""" + return SqrtStretch() + + +class LogStretch(BaseStretch): + r""" + A log stretch. + + The stretch is given by: + + .. math:: + y = \frac{\log{(a x + 1)}}{\log{(a + 1)}} + + Parameters + ---------- + a : float + The ``a`` parameter used in the above formula. The stretch + becomes more linear for small ``a`` values. ``a`` must be + greater than 0. Default is 1000. + + Examples + -------- + .. plot:: + :show-source-link: + + import numpy as np + from astropy.visualization import LogStretch + from matplotlib import pyplot as plt + + fig, ax = plt.subplots(figsize=(5, 5)) + + x = np.linspace(0, 1, 100) + a_vals = (0.1, 1, 3, 10, 30, 100, 1000, 10000) + for a in a_vals: + if a == 1000: + lw = 3 + else: + lw = 1 + stretch = LogStretch(a) + label = f'{a=}' + ax.plot(x, stretch(x, clip=True), label=label, lw=lw) + + ax.axis('equal') + ax.plot(x, x, ls='dotted', color='k', alpha=0.3) + ax.set_xlim(0, 1) + ax.set_ylim(0, 1) + ax.set_xlabel('Input Value') + ax.set_ylabel('Output Value') + ax.set_title(stretch.__class__.__name__) + ax.legend(loc='lower right', fontsize=8) + """ + + @property + def _supports_invalid_kw(self): + return True + + def __init__(self, a=1000.0): + super().__init__() + if a <= 0: # singularity + raise ValueError("a must be > 0") + self.a = a + + def __call__(self, values, clip=True, out=None, invalid=None): + """ + Transform values using this stretch. + + Parameters + ---------- + values : array-like + The input values, which should already be normalized to the + [0:1] range. + clip : bool, optional + If `True` (default), values outside the [0:1] range are + clipped to the [0:1] range. + out : ndarray, optional + If specified, the output values will be placed in this array + (typically used for in-place calculations). + invalid : None or float, optional + Value to assign NaN values generated by this class. NaNs in + the input ``values`` array are not changed. This option is + generally used with matplotlib normalization classes, where + the ``invalid`` value should map to the matplotlib colormap + "under" value (i.e., any finite value < 0). If `None`, then + NaN values are not replaced. This keyword has no effect if + ``clip=True``. + + Returns + ------- + result : ndarray + The transformed values. + """ + values = _prepare(values, clip=clip, out=out) + replace_invalid = not clip and invalid is not None + with np.errstate(invalid="ignore"): + if replace_invalid: + idx = values < 0 + np.multiply(values, self.a, out=values) + np.add(values, 1.0, out=values) + np.log(values, out=values) + np.true_divide(values, np.log(self.a + 1.0), out=values) + + if replace_invalid: + # Assign new NaN (i.e., NaN not in the original input + # values, but generated by this class) to the invalid value. + values[idx] = invalid + + return values + + @property + def inverse(self): + """A stretch object that performs the inverse operation.""" + return InvertedLogStretch(self.a) + + +class InvertedLogStretch(BaseStretch): + r""" + Inverse transformation for `~astropy.visualization.LogStretch`. + + The stretch is given by: + + .. math:: + y = \frac{e^{x \log{a + 1}} - 1}{a} = \frac{(a + 1)^x - 1}{a} + + Parameters + ---------- + a : float, optional + The ``a`` parameter used in the above formula. The stretch + becomes more linear for small ``a`` values and more exponential + for large ``a`` values. ``a`` must be greater than 0. Default is + 1000. + """ + + def __init__(self, a): + super().__init__() + if a <= 0: # singularity + raise ValueError("a must be > 0") + self.a = a + + def __call__(self, values, clip=True, out=None): + values = _prepare(values, clip=clip, out=out) + np.multiply(values, np.log(self.a + 1.0), out=values) + np.exp(values, out=values) + np.subtract(values, 1.0, out=values) + np.true_divide(values, self.a, out=values) + return values + + @property + def inverse(self): + """A stretch object that performs the inverse operation.""" + return LogStretch(self.a) + + +class AsinhStretch(BaseStretch): + r""" + An asinh stretch. + + The stretch is given by: + + .. math:: + y = \frac{{\rm asinh}(x / a)}{{\rm asinh}(1 / a)}. + + Parameters + ---------- + a : float, optional + The ``a`` parameter used in the above formula. The value of this + parameter is where the asinh curve transitions from linear to + logarithmic behavior, expressed as a fraction of the normalized + image. The stretch becomes more linear for larger ``a`` values + and more logarithmic for smaller ``a`` values. ``a`` must be + greater than 0. Default is 0.1. + + Examples + -------- + .. plot:: + :show-source-link: + + import numpy as np + from astropy.visualization import AsinhStretch + from matplotlib import pyplot as plt + + fig, ax = plt.subplots(figsize=(5, 5)) + + x = np.linspace(0, 1, 100) + a_vals = (0.01, 0.05, 0.1, 0.2, 0.3, 0.5, 0.9, 3.0) + for a in a_vals: + if a == 0.1: + lw = 3 + else: + lw = 1 + stretch = AsinhStretch(a) + label = f'{a=}' + ax.plot(x, stretch(x, clip=True), label=label, lw=lw) + + ax.axis('equal') + ax.plot(x, x, ls='dotted', color='k', alpha=0.3) + ax.set_xlim(0, 1) + ax.set_ylim(0, 1) + ax.set_xlabel('Input Value') + ax.set_ylabel('Output Value') + ax.set_title(stretch.__class__.__name__) + ax.legend(loc='lower right', fontsize=8) + """ + + def __init__(self, a=0.1): + super().__init__() + if a <= 0: + raise ValueError("a must be > 0") + self.a = a + + def __call__(self, values, clip=True, out=None): + values = _prepare(values, clip=clip, out=out) + np.true_divide(values, self.a, out=values) + np.arcsinh(values, out=values) + np.true_divide(values, np.arcsinh(1.0 / self.a), out=values) + return values + + @property + def inverse(self): + """A stretch object that performs the inverse operation.""" + return SinhStretch(a=1.0 / np.arcsinh(1.0 / self.a)) + + +class SinhStretch(BaseStretch): + r""" + A sinh stretch. + + The stretch is given by: + + .. math:: + y = \frac{{\rm sinh}(x / a)}{{\rm sinh}(1 / a)} + + Parameters + ---------- + a : float, optional + The ``a`` parameter used in the above formula. The stretch + becomes more linear for larger ``a`` values and more exponential + for smaller ``a`` values. ``a`` must be greater than 0. Default + is 1/3. + + Examples + -------- + .. plot:: + :show-source-link: + + import numpy as np + from astropy.visualization import SinhStretch + from matplotlib import pyplot as plt + + fig, ax = plt.subplots(figsize=(5, 5)) + + x = np.linspace(0, 1, 100) + a_vals = (0.1, 0.2, 0.3333, 0.5, 0.9, 3) + for a in a_vals: + if a == 0.3333: + lw = 3 + else: + lw = 1 + stretch = SinhStretch(a) + label = f'{a=}' + ax.plot(x, stretch(x, clip=True), label=label, lw=lw) + + ax.axis('equal') + ax.plot(x, x, ls='dotted', color='k', alpha=0.3) + ax.set_xlim(0, 1) + ax.set_ylim(0, 1) + ax.set_xlabel('Input Value') + ax.set_ylabel('Output Value') + ax.set_title(stretch.__class__.__name__) + ax.legend(loc='upper left', fontsize=8) + """ + + def __init__(self, a=1.0 / 3.0): + super().__init__() + if a <= 0: + raise ValueError("a must be > 0") + self.a = a + + def __call__(self, values, clip=True, out=None): + values = _prepare(values, clip=clip, out=out) + np.true_divide(values, self.a, out=values) + np.sinh(values, out=values) + np.true_divide(values, np.sinh(1.0 / self.a), out=values) + return values + + @property + def inverse(self): + """A stretch object that performs the inverse operation.""" + return AsinhStretch(a=1.0 / np.sinh(1.0 / self.a)) + + +class HistEqStretch(BaseStretch): + """ + A histogram equalization stretch. + + Parameters + ---------- + data : array-like + The data defining the equalization. + values : array-like, optional + The input image values, which should already be normalized to + the [0:1] range. + """ + + def __init__(self, data, values=None): + # Assume data is not necessarily normalized at this point + self.data = np.sort(data.ravel()) + self.data = self.data[np.isfinite(self.data)] + vmin = self.data.min() + vmax = self.data.max() + self.data = (self.data - vmin) / (vmax - vmin) + + # Compute relative position of each pixel + if values is None: + self.values = np.linspace(0.0, 1.0, len(self.data)) + else: + self.values = values + + def __call__(self, values, clip=True, out=None): + values = _prepare(values, clip=clip, out=out) + values[:] = np.interp(values, self.data, self.values) + return values + + @property + def inverse(self): + """A stretch object that performs the inverse operation.""" + return InvertedHistEqStretch(self.data, values=self.values) + + +class InvertedHistEqStretch(BaseStretch): + """ + Inverse transformation for `~astropy.visualization.HistEqStretch`. + + Parameters + ---------- + data : array-like + The data defining the equalization. + values : array-like, optional + The input image values, which should already be normalized to + the [0:1] range. + """ + + def __init__(self, data, values=None): + self.data = data[np.isfinite(data)] + if values is None: + self.values = np.linspace(0.0, 1.0, len(self.data)) + else: + self.values = values + + def __call__(self, values, clip=True, out=None): + values = _prepare(values, clip=clip, out=out) + values[:] = np.interp(values, self.values, self.data) + return values + + @property + def inverse(self): + """A stretch object that performs the inverse operation.""" + return HistEqStretch(self.data, values=self.values) + + +class ContrastBiasStretch(BaseStretch): + r""" + A stretch that takes into account contrast and bias. + + The stretch is given by: + + .. math:: + y = (x - {\rm bias}) * {\rm contrast} + 0.5 + + and the output values are clipped to the [0:1] range. + + Parameters + ---------- + contrast : float + The contrast parameter (see the above formula). + + bias : float + The bias parameter (see the above formula). + + Examples + -------- + .. plot:: + :show-source-link: + + import numpy as np + from astropy.visualization import ContrastBiasStretch + from matplotlib import pyplot as plt + + fig, ax = plt.subplots(figsize=(5, 5)) + + x = np.linspace(0, 1, 100) + contrasts = [1.0, 2.0, 0.7, 1.0, 1.0, 2.0] + biases = [0.5, 0.5, 0.5, 0.3, 0.7, 0.3] + for contrast, bias in zip(contrasts, biases): + stretch = ContrastBiasStretch(contrast, bias) + ax.plot(x, stretch(x, clip=True), label=f'{contrast=}, {bias=}') + + ax.axis('equal') + ax.plot(x, x, ls='dotted', color='k', alpha=0.3) + ax.set_xlim(0, 1) + ax.set_ylim(0, 1) + ax.set_xlabel('Input Value') + ax.set_ylabel('Output Value') + ax.set_title(stretch.__class__.__name__) + ax.legend(loc='lower right', fontsize=8) + """ + + def __init__(self, contrast, bias): + super().__init__() + self.contrast = contrast + self.bias = bias + + def __call__(self, values, clip=True, out=None): + # As a special case here, we only clip *after* the + # transformation since it does not map [0:1] to [0:1] + values = _prepare(values, clip=False, out=out) + + np.subtract(values, self.bias, out=values) + np.multiply(values, self.contrast, out=values) + np.add(values, 0.5, out=values) + + if clip: + np.clip(values, 0, 1, out=values) + + return values + + @property + def inverse(self): + """A stretch object that performs the inverse operation.""" + return InvertedContrastBiasStretch(self.contrast, self.bias) + + +class InvertedContrastBiasStretch(BaseStretch): + r""" + Inverse transformation for + `~astropy.visualization.ContrastBiasStretch`. + + The stretch is given by: + + .. math:: + y = \frac{x - 0.5}{{\rm contrast}} + {\rm bias} + + Parameters + ---------- + contrast : float + The contrast parameter (see + `~astropy.visualization.ContrastBiasStretch`). + + bias : float + The bias parameter (see + `~astropy.visualization.ContrastBiasStretch`). + """ + + def __init__(self, contrast, bias): + super().__init__() + self.contrast = contrast + self.bias = bias + + def __call__(self, values, clip=True, out=None): + # As a special case here, we only clip *after* the + # transformation since it does not map [0:1] to [0:1] + values = _prepare(values, clip=False, out=out) + np.subtract(values, 0.5, out=values) + np.true_divide(values, self.contrast, out=values) + np.add(values, self.bias, out=values) + + if clip: + np.clip(values, 0, 1, out=values) + + return values + + @property + def inverse(self): + """A stretch object that performs the inverse operation.""" + return ContrastBiasStretch(self.contrast, self.bias) + + +class CompositeStretch(CompositeTransform, BaseStretch): + """ + A combination of two stretches. + + Parameters + ---------- + stretch_1 : :class:`astropy.visualization.BaseStretch` + The first stretch to apply. + stretch_2 : :class:`astropy.visualization.BaseStretch` + The second stretch to apply. + """ + + def __call__(self, values, clip=True, out=None): + return self.transform_2( + self.transform_1(values, clip=clip, out=out), clip=clip, out=out + ) diff --git a/astropy/visualization/tests/__init__.py b/astropy/visualization/tests/__init__.py new file mode 100644 index 000000000000..9dce85d06f83 --- /dev/null +++ b/astropy/visualization/tests/__init__.py @@ -0,0 +1 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst diff --git a/astropy/visualization/tests/test_basic_rgb.py b/astropy/visualization/tests/test_basic_rgb.py new file mode 100644 index 000000000000..dccfbf198753 --- /dev/null +++ b/astropy/visualization/tests/test_basic_rgb.py @@ -0,0 +1,376 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +import os +import sys +import tempfile + +import numpy as np +import pytest +from numpy.testing import assert_allclose + +from astropy.utils.compat.optional_deps import HAS_MATPLOTLIB, HAS_PLT +from astropy.visualization import basic_rgb +from astropy.visualization.interval import ManualInterval +from astropy.visualization.stretch import LinearStretch, LogStretch + +# Set DISPLAY=True to get matplotlib imshow windows to help with debugging. +DISPLAY = False + +# Override any debugging if not HAS_PLT +if HAS_PLT & DISPLAY: + import matplotlib.pyplot as plt +elif not HAS_PLT: + DISPLAY = False + +MINSC = 0.0 +MAXSC = 5.0e4 +MIN = [0.0, 0.0, 0.0] +MAX = [5.0e4, 5.0e4, 4.0e4] +IX = 16 +IY = 16 + +SCALEA = 1500.0 +SHAPE = (85, 75) + +# Gaussian pixel centers, peak values, and colors +_points = [[15, 15], [50, 45], [30, 30], [45, 15]] +_values = [1000, 5500, 600, 20000] +_stddevs = [1.5, 2.0, 1.0, 2.5] +_g_r = [1.0, -1.0, 1.0, 1.0] +_r_i = [2.0, -0.5, 2.5, 1.0] + +_grid = np.indices(SHAPE) +IMAGER = np.zeros(SHAPE) +IMAGEG = np.zeros(SHAPE) +IMAGEB = np.zeros(SHAPE) +for p, v, std, gr, ri in zip(_points, _values, _stddevs, _g_r, _r_i): + _gaus = np.exp( + -( + (_grid[0] - p[0]) ** 2 / (2.0 * std**2) + + (_grid[1] - p[1]) ** 2 / (2.0 * std**2) + ) + ) + IMAGER += v * np.power(10, 0.4 * ri) * _gaus + IMAGEG += v * np.power(10, 0.4 * gr) * _gaus + IMAGEB += v * _gaus + +RSEED = 0 +rng = np.random.default_rng(RSEED) +IMAGER = IMAGER + rng.normal(0, 2, SHAPE) +IMAGEG = IMAGEG + rng.normal(0, 2, SHAPE) +IMAGEB = IMAGEB + rng.normal(0, 2, SHAPE) + +INCORRECT_OUTPUT_TYPES = [bool, str, np.int64, np.cdouble, "str"] + + +def _display_rgb(rgb, title=None): + """Display an rgb image using matplotlib (useful for debugging)""" + plt.imshow(rgb, interpolation="nearest", origin="lower") + if title: + plt.title(title) + plt.show() + return plt + + +def test_image_mapping(): + """Test creating an RGB image using a linear stretch, + using RGBImageMapping()""" + stretch = LinearStretch() + interval = [] + for i in range(3): + interval.append(ManualInterval(vmin=MIN[i], vmax=MAX[i])) + map_ = basic_rgb.RGBImageMapping(stretch=stretch, interval=interval) + rgb_image = map_.make_rgb_image(IMAGER, IMAGEG, IMAGEB, output_dtype=np.float64) + for i, (min_, max_, iref_) in enumerate( + zip( + [0.0, 0.0, 0.0], + [1.0, 1.0, 0.5000598388671327], + [0.08093024185629245, 0.032216094791227695, 0.016040737174622725], + ) + ): + assert_allclose(rgb_image[:, :, i].min(), min_) + assert_allclose(rgb_image[:, :, i].max(), max_) + assert_allclose(rgb_image[IX, IY, i], iref_) + if DISPLAY: + _display_rgb(rgb_image, title=sys._getframe().f_code.co_name) + + +def test_linear(): + """Test creating an RGB image using a linear stretch, + using individual routines""" + interval = [] + for i in range(3): + interval.append(ManualInterval(vmin=MIN[i], vmax=MAX[i])) + rgb_image = basic_rgb.make_rgb( + IMAGER, + IMAGEG, + IMAGEB, + interval=interval, + output_dtype=np.float64, + ) + for i, (min_, max_, iref_) in enumerate( + zip( + [0.0, 0.0, 0.0], + [1.0, 1.0, 0.5000598388671327], + [0.08093024185629245, 0.032216094791227695, 0.016040737174622725], + ) + ): + assert_allclose(rgb_image[:, :, i].min(), min_) + assert_allclose(rgb_image[:, :, i].max(), max_) + assert_allclose(rgb_image[IX, IY, i], iref_) + if DISPLAY: + _display_rgb(rgb_image, title=sys._getframe().f_code.co_name) + + +def test_log(): + """Test creating an RGB image using an log stretch""" + interval = [] + for i in range(3): + interval.append(ManualInterval(vmin=MIN[i], vmax=MAX[i])) + rgb_image = basic_rgb.make_rgb( + IMAGER, + IMAGEG, + IMAGEB, + interval=interval, + stretch=LogStretch(a=SCALEA), + output_dtype=np.float64, + ) + for i, (min_, max_, iref_) in enumerate( + zip( + [0.0, 0.0, 0.0], + [1.0, 1.0, 0.9053360156408082], + [0.6572779418489928, 0.5330153105260111, 0.4404384627801792], + ) + ): + assert_allclose(rgb_image[:, :, i].min(), min_) + assert_allclose(rgb_image[:, :, i].max(), max_) + assert_allclose(rgb_image[IX, IY, i], iref_) + if DISPLAY: + _display_rgb(rgb_image, title=sys._getframe().f_code.co_name) + + +def test_int8(): + """Test creating an RGB image with 8-bit output format""" + interval = [] + for i in range(3): + interval.append(ManualInterval(vmin=MIN[i], vmax=MAX[i])) + rgb_image = basic_rgb.make_rgb( + IMAGER, + IMAGEG, + IMAGEB, + interval=interval, + stretch=LogStretch(a=SCALEA), + output_dtype=np.uint8, + ) + assert np.issubdtype(rgb_image.dtype, np.uint8) + if DISPLAY: + _display_rgb(rgb_image, title=sys._getframe().f_code.co_name) + + +def test_float64(): + """Test creating an RGB image with normalized float output format""" + interval = [] + for i in range(3): + interval.append(ManualInterval(vmin=MIN[i], vmax=MAX[i])) + rgb_image = basic_rgb.make_rgb( + IMAGER, + IMAGEG, + IMAGEB, + interval=interval, + stretch=LogStretch(a=SCALEA), + output_dtype=np.float64, + ) + assert np.issubdtype(rgb_image.dtype, float) + if DISPLAY: + _display_rgb(rgb_image, title=sys._getframe().f_code.co_name) + + +def test_linear_min_max(): + """Test using a min/max linear stretch determined from one image""" + rgb_image = basic_rgb.make_rgb( + IMAGER, + IMAGEG, + IMAGEB, + interval=ManualInterval(vmin=None, vmax=None), + output_dtype=np.float64, + ) + for i, (min_, max_, iref_) in enumerate( + zip( + [0.0, 0.0, 0.0], + [1.0, 1.0, 1.0], + [0.08069534125307666, 0.032196043103128555, 0.032466842729915714], + ) + ): + assert_allclose(rgb_image[:, :, i].min(), min_) + assert_allclose(rgb_image[:, :, i].max(), max_) + assert_allclose(rgb_image[IX, IY, i], iref_) + if DISPLAY: + _display_rgb(rgb_image, title=sys._getframe().f_code.co_name) + + +def test_log_min_max(): + """Test using a min/max log stretch determined from one image""" + rgb_image = basic_rgb.make_rgb( + IMAGER, + IMAGEG, + IMAGEB, + interval=ManualInterval(vmin=None, vmax=None), + stretch=LogStretch(a=SCALEA), + output_dtype=np.float64, + ) + for i, (min_, max_, iref_) in enumerate( + zip( + [0.0, 0.0, 0.0], + [1.0, 1.0, 1.0], + [0.6568837677677257, 0.5329319103684619, 0.5340539629318083], + ) + ): + assert_allclose(rgb_image[:, :, i].min(), min_) + assert_allclose(rgb_image[:, :, i].max(), max_) + assert_allclose(rgb_image[IX, IY, i], iref_) + if DISPLAY: + _display_rgb(rgb_image, title=sys._getframe().f_code.co_name) + + +def test_log_scalar_interval(): + """Test creating a black+white image using a linear stretch""" + rgb_image = basic_rgb.make_rgb( + IMAGER, + IMAGEG, + IMAGEB, + interval=ManualInterval(vmin=MINSC, vmax=MAXSC), + stretch=LogStretch(a=SCALEA), + output_dtype=np.float64, + ) + for i, (min_, max_, iref_) in enumerate( + zip( + [0.0, 0.0, 0.0], + [1.0, 1.0, 0.8748719461075388], + [0.6572779418489928, 0.5330153105260111, 0.41128606174423155], + ) + ): + assert_allclose(rgb_image[:, :, i].min(), min_) + assert_allclose(rgb_image[:, :, i].max(), max_) + assert_allclose(rgb_image[IX, IY, i], iref_) + if DISPLAY: + _display_rgb(rgb_image, title=sys._getframe().f_code.co_name) + + +def test_linear_bw(): + """Test creating a black+white image using a linear stretch""" + rgb_image = basic_rgb.make_rgb( + IMAGER, + IMAGER, + IMAGER, + interval=ManualInterval(vmin=MINSC, vmax=MAXSC), + output_dtype=np.float64, + ) + for i, (min_, max_, iref_) in enumerate( + zip( + [0.0, 0.0, 0.0], + [1.0, 1.0, 1.0], + [0.08093024185629245, 0.08093024185629245, 0.08093024185629245], + ) + ): + assert_allclose(rgb_image[:, :, i].min(), min_) + assert_allclose(rgb_image[:, :, i].max(), max_) + assert_allclose(rgb_image[IX, IY, i], iref_) + if DISPLAY: + _display_rgb(rgb_image, title=sys._getframe().f_code.co_name) + + +def test_log_bw(): + """Test creating a black+white image using a log stretch""" + rgb_image = basic_rgb.make_rgb( + IMAGER, + IMAGER, + IMAGER, + interval=ManualInterval(vmin=MINSC, vmax=MAXSC), + stretch=LogStretch(a=SCALEA), + output_dtype=np.float64, + ) + for i, (min_, max_, iref_) in enumerate( + zip( + [0.0, 0.0, 0.0], + [1.0, 1.0, 1.0], + [0.6572779418489928, 0.6572779418489928, 0.6572779418489928], + ) + ): + assert_allclose(rgb_image[:, :, i].min(), min_) + assert_allclose(rgb_image[:, :, i].max(), max_) + assert_allclose(rgb_image[IX, IY, i], iref_) + if DISPLAY: + _display_rgb(rgb_image, title=sys._getframe().f_code.co_name) + + +@pytest.mark.skipif(not HAS_MATPLOTLIB, reason="requires matplotlib") +def test_make_log_rgb_file(): + """Test the function that does it all""" + interval = [] + for i in range(3): + interval.append(ManualInterval(vmin=MIN[i], vmax=MAX[i])) + with tempfile.NamedTemporaryFile(suffix=".png") as temp: + red = IMAGER + green = IMAGEG + blue = IMAGEB + basic_rgb.make_rgb( + red, + green, + blue, + interval=interval, + stretch=LogStretch(a=SCALEA), + filename=temp, + ) + assert os.path.exists(temp.name) + + +@pytest.mark.skipif(not HAS_MATPLOTLIB, reason="requires matplotlib") +def test_make_linear_rgb_file(): + """Test the function that does it all""" + interval = [] + for i in range(3): + interval.append(ManualInterval(vmin=MIN[i], vmax=MAX[i])) + with tempfile.NamedTemporaryFile(suffix=".png") as temp: + red = IMAGER + green = IMAGEG + blue = IMAGEB + basic_rgb.make_rgb(red, green, blue, interval=interval, filename=temp) + assert os.path.exists(temp.name) + + +def test_different_shapes_asserts(): + """Test the different shape assertion""" + with pytest.raises(ValueError, match=r"shapes must match"): + # just swap the dimensions to get a differently-shaped 'r' + image_r = IMAGER.reshape(SHAPE[1], SHAPE[0]) + basic_rgb.make_rgb(image_r, IMAGEG, IMAGEB, stretch=LogStretch(a=SCALEA)) + + +def test_incorrect_interval_length(): + """Test incorrect input interval array length""" + with pytest.raises(ValueError, match=r"3 instances for interval."): + interval = ManualInterval(vmin=MINSC, vmax=MAXSC) + basic_rgb.make_rgb( + IMAGER, + IMAGEG, + IMAGEB, + interval=[interval, interval], + stretch=LogStretch(a=SCALEA), + ) + + +@pytest.mark.parametrize(("out_format"), INCORRECT_OUTPUT_TYPES) +def test_invalid_output_dtype(out_format): + """Test incorrect output image format""" + interval = [] + for i in range(3): + interval.append(ManualInterval(vmin=MIN[i], vmax=MAX[i])) + with pytest.raises(ValueError, match=r"'output_dtype' must be one"): + basic_rgb.make_rgb( + IMAGER, + IMAGEG, + IMAGEB, + interval=interval, + stretch=LogStretch(a=SCALEA), + output_dtype=out_format, + ) diff --git a/astropy/visualization/tests/test_histogram.py b/astropy/visualization/tests/test_histogram.py new file mode 100644 index 000000000000..b4f5c7968840 --- /dev/null +++ b/astropy/visualization/tests/test_histogram.py @@ -0,0 +1,86 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + + +from numpy.testing import assert_allclose + +from astropy.utils.compat.optional_deps import HAS_PLT, HAS_SCIPY + +if HAS_PLT: + from matplotlib.figure import Figure + +import numpy as np +import pytest + +from astropy.stats import histogram +from astropy.visualization import hist + + +@pytest.mark.skipif(not HAS_PLT, reason="requires matplotlib") +def test_hist_basic(): + rng = np.random.default_rng(0) + x = rng.standard_normal(100) + + fig = Figure() + ax = fig.add_subplot() + for range in [None, (-2, 2)]: + n1, bins1, patches1 = ax.hist(x, 10, range=range) + n2, bins2, patches2 = hist(x, 10, range=range, ax=ax) + + assert_allclose(n1, n2) + assert_allclose(bins1, bins2) + + +@pytest.mark.skipif(not HAS_PLT, reason="requires matplotlib") +def test_hist_specify_ax(): + rng = np.random.default_rng(0) + x = rng.standard_normal(100) + + fig = Figure() + ax = fig.subplots(2) + + n1, bins1, patches1 = hist(x, 10, ax=ax[0]) + assert patches1[0].axes is ax[0] + + n2, bins2, patches2 = hist(x, 10, ax=ax[1]) + assert patches2[0].axes is ax[1] + + +@pytest.mark.skipif(not HAS_PLT, reason="requires matplotlib") +def test_hist_autobin(): + rng = np.random.default_rng(0) + x = rng.standard_normal(100) + + # 'knuth' bintype depends on scipy that is optional dependency + if HAS_SCIPY: + bintypes = [10, np.arange(-3, 3, 10), "knuth", "scott", "freedman", "blocks"] + else: + bintypes = [10, np.arange(-3, 3, 10), "scott", "freedman", "blocks"] + + for bintype in bintypes: + for range in [None, (-3, 3)]: + n1, bins1 = histogram(x, bintype, range=range) + n2, bins2, patches = hist(x, bintype, range=range) + assert_allclose(n1, n2) + assert_allclose(bins1, bins2) + + +def test_histogram_pathological_input(): + # Regression test for https://github.com/astropy/astropy/issues/7758 + + # The key feature of the data below is that one of the points is very, + # very different than the rest. That leads to a large number of bins. + data = [ + 9.99999914e05, + -8.31312483e-03, + 6.52755852e-02, + 1.43104653e-03, + -2.26311017e-02, + 2.82660007e-03, + 1.80307521e-02, + 9.26294279e-03, + 5.06606026e-02, + 2.05418011e-03, + ] + + with pytest.raises(ValueError): + hist(data, bins="freedman", max_bins=10000) diff --git a/astropy/visualization/tests/test_interval.py b/astropy/visualization/tests/test_interval.py new file mode 100644 index 000000000000..c5619f6b5e14 --- /dev/null +++ b/astropy/visualization/tests/test_interval.py @@ -0,0 +1,187 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +import numpy as np +import pytest +from numpy.testing import assert_allclose + +from astropy.utils import NumpyRNGContext +from astropy.utils.masked import Masked +from astropy.visualization.interval import ( + AsymmetricPercentileInterval, + ManualInterval, + MinMaxInterval, + PercentileInterval, + SymmetricInterval, + ZScaleInterval, +) + + +class TestInterval: + data = np.linspace(-20.0, 60.0, 100) + + def test_manual(self): + interval = ManualInterval(-10.0, +15.0) + vmin, vmax = interval.get_limits(self.data) + assert_allclose(vmin, -10.0) + assert_allclose(vmax, +15.0) + + def test_manual_defaults(self): + interval = ManualInterval(vmin=-10.0) + vmin, vmax = interval.get_limits(self.data) + assert_allclose(vmin, -10.0) + assert_allclose(vmax, np.max(self.data)) + + interval = ManualInterval(vmax=15.0) + vmin, vmax = interval.get_limits(self.data) + assert_allclose(vmin, np.min(self.data)) + assert_allclose(vmax, 15.0) + + def test_manual_zero_limit(self): + # Regression test for a bug that caused ManualInterval to compute the + # limit (min or max) if it was set to zero. + interval = ManualInterval(vmin=0, vmax=0) + vmin, vmax = interval.get_limits(self.data) + assert_allclose(vmin, 0) + assert_allclose(vmax, 0) + + def test_manual_defaults_with_nan(self): + interval = ManualInterval() + data = np.copy(self.data) + data[0] = np.nan + vmin, vmax = interval.get_limits(self.data) + assert_allclose(vmin, -20) + assert_allclose(vmax, +60) + + def test_minmax(self): + interval = MinMaxInterval() + vmin, vmax = interval.get_limits(self.data) + assert_allclose(vmin, -20.0) + assert_allclose(vmax, +60.0) + + def test_percentile(self): + interval = PercentileInterval(62.2) + vmin, vmax = interval.get_limits(self.data) + assert_allclose(vmin, -4.88) + assert_allclose(vmax, 44.88) + + def test_asymmetric_percentile(self): + interval = AsymmetricPercentileInterval(10.5, 70.5) + vmin, vmax = interval.get_limits(self.data) + assert_allclose(vmin, -11.6) + assert_allclose(vmax, 36.4) + + def test_asymmetric_percentile_nsamples(self): + with NumpyRNGContext(12345): + interval = AsymmetricPercentileInterval(10.5, 70.5, n_samples=20) + vmin, vmax = interval.get_limits(self.data) + assert_allclose(vmin, -14.367676767676768) + assert_allclose(vmax, 40.266666666666666) + + def test_symmetric_interval_manual(self): + interval = SymmetricInterval(radius=40) + vmin, vmax = interval.get_limits(self.data) + assert_allclose(vmin, -40.0) + assert_allclose(vmax, +40.0) + + interval = SymmetricInterval(radius=100, midpoint=10) + vmin, vmax = interval.get_limits(self.data) + assert_allclose(vmin, -90.0) + assert_allclose(vmax, +110.0) + + def test_symmetric_interval_auto(self): + interval = SymmetricInterval() + vmin, vmax = interval.get_limits(self.data) + assert_allclose(vmin, -60.0) + assert_allclose(vmax, +60.0) + + interval = SymmetricInterval(midpoint=50) + vmin, vmax = interval.get_limits(self.data) + assert_allclose(vmin, -20.0) + assert_allclose(vmax, +120.0) + + +class TestIntervalList(TestInterval): + # Make sure intervals work with lists + data = np.linspace(-20.0, 60.0, 100).tolist() + + +class TestInterval2D(TestInterval): + # Make sure intervals work with 2d arrays + data = np.linspace(-20.0, 60.0, 100).reshape(100, 1) + + +class TestIntervalMaskedArray(TestInterval): + # Make sure intervals work with MaskedArray + data = np.concatenate((np.linspace(-20.0, 60.0, 100), np.full(100, 1e6))) + data = np.ma.MaskedArray(data, data > 1000) + + +class TestIntervalMaskedNDArray(TestInterval): + # Make sure intervals work with MaskedArray + data = np.concatenate((np.linspace(-20.0, 60.0, 100), np.full(100, 1e6))) + data = Masked(data, data > 1000) + + +def test_zscale(): + np.random.seed(42) + data = np.random.randn(100, 100) * 5 + 10 + interval = ZScaleInterval() + vmin, vmax = interval.get_limits(data) + assert_allclose(vmin, -9.6, atol=0.1) + assert_allclose(vmax, 25.4, atol=0.1) + + data = list(range(1000)) + [np.nan] + interval = ZScaleInterval() + vmin, vmax = interval.get_limits(data) + assert_allclose(vmin, 0, atol=0.1) + assert_allclose(vmax, 999, atol=0.1) + + data = list(range(100)) + interval = ZScaleInterval() + vmin, vmax = interval.get_limits(data) + assert_allclose(vmin, 0, atol=0.1) + assert_allclose(vmax, 99, atol=0.1) + + +def test_zscale_npoints(): + """ + Regression test to ensure ZScaleInterval returns the minimum and + maximum of the data if the number of data points is less than + ``min_pixels``. + """ + + data = np.arange(4).reshape((2, 2)) + interval = ZScaleInterval(min_npixels=5) + vmin, vmax = interval.get_limits(data) + assert vmin == 0 + assert vmax == 3 + + +def test_integers(): + # Need to make sure integers get cast to float + interval = MinMaxInterval() + values = interval([1, 3, 4, 5, 6]) + assert_allclose(values, [0.0, 0.4, 0.6, 0.8, 1.0]) + + # Don't accept integer array in output + out = np.zeros(5, dtype=int) + with pytest.raises( + TypeError, match=r"Can only do in-place scaling for floating-point arrays" + ): + values = interval([1, 3, 4, 5, 6], out=out) + + # But integer input and floating point output is fine + out = np.zeros(5, dtype=float) + interval([1, 3, 4, 5, 6], out=out) + assert_allclose(out, [0.0, 0.4, 0.6, 0.8, 1.0]) + + +def test_constant_data(): + """Test intervals with constant data (avoiding divide-by-zero).""" + shape = (10, 10) + data = np.ones(shape) + interval = MinMaxInterval() + limits = interval.get_limits(data) + values = interval(data) + assert_allclose(limits, (1.0, 1.0)) + assert_allclose(values, np.zeros(shape)) diff --git a/astropy/visualization/tests/test_lupton_rgb.py b/astropy/visualization/tests/test_lupton_rgb.py new file mode 100644 index 000000000000..c1f2ff1f68c4 --- /dev/null +++ b/astropy/visualization/tests/test_lupton_rgb.py @@ -0,0 +1,351 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +""" +Tests for RGB Images +""" + +import sys + +import numpy as np +import pytest +from numpy.testing import assert_allclose, assert_equal + +from astropy.convolution import Gaussian2DKernel, convolve +from astropy.utils.compat.optional_deps import HAS_MATPLOTLIB +from astropy.visualization import lupton_rgb +from astropy.visualization.interval import ManualInterval +from astropy.visualization.stretch import LinearStretch + +# Set display=True to get matplotlib imshow windows to help with debugging. +display = False + + +def display_rgb(rgb, title=None): + """Display an rgb image using matplotlib (useful for debugging)""" + import matplotlib.pyplot as plt + + plt.imshow(rgb, interpolation="nearest", origin="lower") + if title: + plt.title(title) + plt.show() + return plt + + +def saturate(image, satValue): + """ + Return image with all points above satValue set to NaN. + + Simulates saturation on an image, so we can test 'replace_saturated_pixels' + """ + result = image.copy() + saturated = image > satValue + result[saturated] = np.nan + return result + + +def random_array(dtype, N=100): + return np.array(np.random.random(10) * 100, dtype=dtype) + + +def test_compute_intensity_1_float(): + image_r = random_array(np.float64) + intensity = lupton_rgb.compute_intensity(image_r) + assert image_r.dtype == intensity.dtype + assert_equal(image_r, intensity) + + +def test_compute_intensity_1_uint(): + image_r = random_array(np.uint8) + intensity = lupton_rgb.compute_intensity(image_r) + assert image_r.dtype == intensity.dtype + assert_equal(image_r, intensity) + + +def test_compute_intensity_3_float(): + image_r = random_array(np.float64) + image_g = random_array(np.float64) + image_b = random_array(np.float64) + intensity = lupton_rgb.compute_intensity(image_r, image_g, image_b) + assert image_r.dtype == intensity.dtype + assert_equal(intensity, (image_r + image_g + image_b) / 3.0) + + +def test_compute_intensity_3_uint(): + image_r = random_array(np.uint8) + image_g = random_array(np.uint8) + image_b = random_array(np.uint8) + intensity = lupton_rgb.compute_intensity(image_r, image_g, image_b) + assert image_r.dtype == intensity.dtype + assert_equal(intensity, (image_r + image_g + image_b) // 3) + + +class TestLuptonRgb: + """A test case for Rgb""" + + def setup_method(self, method): + np.random.seed(1000) # so we always get the same images. + + self.min_, self.stretch_, self.Q = 0, 5, 20 # asinh + + width, height = 85, 75 + self.width = width + self.height = height + + shape = (width, height) + image_r = np.zeros(shape) + image_g = np.zeros(shape) + image_b = np.zeros(shape) + + # pixel locations, values and colors + points = [[15, 15], [50, 45], [30, 30], [45, 15]] + values = [1000, 5500, 600, 20000] + g_r = [1.0, -1.0, 1.0, 1.0] + r_i = [2.0, -0.5, 2.5, 1.0] + + # Put pixels in the images. + for p, v, gr, ri in zip(points, values, g_r, r_i): + image_r[p[0], p[1]] = v * pow(10, 0.4 * ri) + image_g[p[0], p[1]] = v * pow(10, 0.4 * gr) + image_b[p[0], p[1]] = v + + # convolve the image with a reasonable PSF, + # and add Gaussian background noise + def convolve_with_noise(image, psf): + convolvedImage = convolve( + image, psf, boundary="extend", normalize_kernel=True + ) + randomImage = np.random.normal(0, 2, image.shape) + return randomImage + convolvedImage + + psf = Gaussian2DKernel(2.5) + self.image_r = convolve_with_noise(image_r, psf) + self.image_g = convolve_with_noise(image_g, psf) + self.image_b = convolve_with_noise(image_b, psf) + + def test_Asinh(self): + """Test creating an RGB image using an asinh stretch""" + + asinh_map = lupton_rgb.RGBImageMappingLupton( + interval=ManualInterval(vmin=self.min_, vmax=None), + stretch=lupton_rgb.LuptonAsinhStretch(self.stretch_, self.Q), + ) + rgb_image = asinh_map.make_rgb_image(self.image_r, self.image_g, self.image_b) + + if display: + display_rgb(rgb_image, title=sys._getframe().f_code.co_name) + + def test_Asinh_incorrect_stretch_asserts(self): + with pytest.raises(ValueError, match=r"Stretch must be non-negative"): + _ = lupton_rgb.LuptonAsinhStretch(-1.0, self.Q) + + def test_Asinh_incorrect_Q_asserts(self): + with pytest.raises(ValueError, match=r"Q must be non-negative"): + _ = lupton_rgb.LuptonAsinhStretch(self.stretch_, -1.0) + + def test_Asinh_Q_machine_floor(self): + asinh_map = lupton_rgb.LuptonAsinhStretch(self.stretch_, 1.0e-24) + assert_allclose(asinh_map.Q, 0.1) + + def test_Asinh_Q_ceil(self): + asinh_map = lupton_rgb.LuptonAsinhStretch(self.stretch_, 1e11) + assert_allclose(asinh_map.Q, 1e10) + + def test_AsinhZscale(self): + """ + Test creating an RGB image using an asinh stretch estimated + using zscale + """ + map_ = lupton_rgb.RGBImageMappingLupton( + interval=ManualInterval(vmin=self.min_, vmax=None), + stretch=lupton_rgb.LuptonAsinhZscaleStretch( + [self.image_r, self.image_g, self.image_b], self.Q + ), + ) + rgb_image = map_.make_rgb_image(self.image_r, self.image_g, self.image_b) + + if display: + display_rgb(rgb_image, title=sys._getframe().f_code.co_name) + + def test_AsinhZscaleIntensity(self): + """ + Test creating an RGB image using an asinh stretch estimated + using zscale on the intensity + """ + map_ = lupton_rgb.RGBImageMappingLupton( + interval=ManualInterval(vmin=self.min_, vmax=None), + stretch=lupton_rgb.LuptonAsinhZscaleStretch( + lupton_rgb.compute_intensity(self.image_r, self.image_g, self.image_b), + self.Q, + ), + ) + rgb_image = map_.make_rgb_image(self.image_r, self.image_g, self.image_b) + + if display: + display_rgb(rgb_image, title=sys._getframe().f_code.co_name) + + def test_AsinhZscaleIntensityBW(self): + """Test creating a black-and-white image using an asinh stretch + estimated using zscale on the intensity""" + map_ = lupton_rgb.RGBImageMappingLupton( + interval=ManualInterval(vmin=self.min_, vmax=None), + stretch=lupton_rgb.LuptonAsinhZscaleStretch( + self.image_r, + self.Q, + ), + ) + rgb_image = map_.make_rgb_image(self.image_r, self.image_r, self.image_r) + if display: + display_rgb(rgb_image, title=sys._getframe().f_code.co_name) + + def test_AsinhZscale_pedestal_array(self): + """ + Test creating an RGB image using an asinh stretch estimated + using zscale + """ + map_ = lupton_rgb.RGBImageMappingLupton( + interval=ManualInterval(vmin=self.min_, vmax=None), + stretch=lupton_rgb.LuptonAsinhZscaleStretch( + [self.image_r, self.image_g, self.image_b], + self.Q, + pedestal=[1.0, 1.0, 2.0], + ), + ) + rgb_image = map_.make_rgb_image(self.image_r, self.image_g, self.image_b) + + if display: + display_rgb(rgb_image, title=sys._getframe().f_code.co_name) + + def test_AsinhZscale_pedestal_float(self): + """ + Test creating an RGB image using an asinh stretch estimated + using zscale + """ + map_ = lupton_rgb.RGBImageMappingLupton( + interval=ManualInterval(vmin=self.min_, vmax=None), + stretch=lupton_rgb.LuptonAsinhZscaleStretch( + [self.image_r, self.image_g, self.image_b], + self.Q, + pedestal=1.0, + ), + ) + rgb_image = map_.make_rgb_image(self.image_r, self.image_g, self.image_b) + + if display: + display_rgb(rgb_image, title=sys._getframe().f_code.co_name) + + def test_AsinhZscale_pedestal_incorrect_assert(self): + """ + Test creating an RGB image using an asinh stretch estimated + using zscale + """ + with pytest.raises(ValueError, match=r"pedestal must be 1 or 3 values"): + _ = lupton_rgb.LuptonAsinhZscaleStretch( + [self.image_r, self.image_g, self.image_b], + self.Q, + pedestal=[1.0, 2.0], + ) + + def test_AsinhZscale_incorrect_input_asserts(self): + with pytest.raises(ValueError, match=r"Input 'image' must be a single"): + _ = lupton_rgb.LuptonAsinhZscaleStretch( + [self.image_r, self.image_g], self.Q + ) + + def test_AsinhZscale_incorrect_input_nonimage_asserts(self): + with pytest.raises(ValueError, match=r"Input 'image' must be a single"): + _ = lupton_rgb.LuptonAsinhZscaleStretch([1], self.Q) + + @pytest.mark.skipif(not HAS_MATPLOTLIB, reason="requires matplotlib") + def test_make_rgb(self, tmp_path): + """Test the function that does it all""" + temp = tmp_path.with_suffix(".png") + lupton_rgb.make_lupton_rgb( + self.image_r, + self.image_g, + self.image_b, + stretch=self.stretch_, + Q=self.Q, + minimum=self.min_, + filename=temp, + ) + assert temp.exists() + + def test_make_rgb_incorrect_min_input(self): + with pytest.raises(ValueError, match=r"3 values for minimum."): + lupton_rgb.make_lupton_rgb( + self.image_r, + self.image_g, + self.image_b, + stretch=self.stretch_, + Q=self.Q, + minimum=[self.min_, self.min_], + ) + + def test_make_rgb_saturated_fix(self, tmp_path): + pytest.skip("saturation correction is not implemented") + satValue = 1000.0 + # TODO: Cannot test with these options yet, as that part of the code + # is not implemented. + temp = tmp_path.with_suffix(".png") + red = saturate(self.image_r, satValue) + green = saturate(self.image_g, satValue) + blue = saturate(self.image_b, satValue) + lupton_rgb.make_lupton_rgb( + red, + green, + blue, + minimum=self.min_, + stretch=self.stretch_, + Q=self.Q, + saturated_border_width=1, + saturated_pixel_value=2000, + filename=temp, + ) + + def test_linear(self): + """Test using a specified linear stretch""" + + map_ = lupton_rgb.RGBImageMappingLupton( + interval=ManualInterval(vmin=self.min_, vmax=None), + stretch=LinearStretch(-8.45, 13.44), + ) + rgb_image = map_.make_rgb_image(self.image_r, self.image_g, self.image_b) + if display: + display_rgb(rgb_image, title=sys._getframe().f_code.co_name) + + def test_saturated(self): + """Test interpolationolating saturated pixels""" + pytest.skip("replaceSaturatedPixels is not implemented in astropy yet") + + satValue = 1000.0 + self.image_r = saturate(self.image_r, satValue) + self.image_g = saturate(self.image_g, satValue) + self.image_b = saturate(self.image_b, satValue) + + lupton_rgb.replaceSaturatedPixels( + self.image_r, self.image_g, self.image_b, 1, 2000 + ) + # Check that we replaced those NaNs with some reasonable value + assert np.isfinite(self.image_r.getImage().getArray()).all() + assert np.isfinite(self.image_g.getImage().getArray()).all() + assert np.isfinite(self.image_b.getImage().getArray()).all() + + # Prepare for generating an output file + self.imagesR = self.imagesR.getImage() + self.imagesR = self.imagesG.getImage() + self.imagesR = self.imagesB.getImage() + + asinhMap = lupton_rgb.AsinhMapping(self.min_, self.stretch_, self.Q) + rgb_image = asinhMap.make_rgb_image(self.image_r, self.image_g, self.image_b) + if display: + display_rgb(rgb_image, title=sys._getframe().f_code.co_name) + + def test_different_shapes_asserts(self): + with pytest.raises(ValueError, match=r"shapes must match"): + # just swap the dimensions to get a differently-shaped 'r' + image_r = self.image_r.reshape(self.height, self.width) + lupton_rgb.make_lupton_rgb(image_r, self.image_g, self.image_b) + + def test_incorrect_input_compute_intensity_asserts(self): + with pytest.raises(ValueError, match=r"specify either a single image"): + lupton_rgb.compute_intensity(self.image_r, self.image_g) diff --git a/astropy/visualization/tests/test_norm.py b/astropy/visualization/tests/test_norm.py new file mode 100644 index 000000000000..645228bff295 --- /dev/null +++ b/astropy/visualization/tests/test_norm.py @@ -0,0 +1,394 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +import numpy as np +import pytest +from numpy import ma +from numpy.testing import assert_allclose, assert_array_equal, assert_equal + +from astropy.utils.compat.optional_deps import HAS_PLT +from astropy.visualization.interval import ManualInterval, PercentileInterval +from astropy.visualization.mpl_normalize import ( + ImageNormalize, + SimpleNorm, + imshow_norm, + imshow_simple_norm, + simple_norm, +) +from astropy.visualization.stretch import LogStretch, PowerStretch, SqrtStretch + +DATA = np.linspace(0.0, 15.0, 6) +DATA2 = np.arange(3) +DATA2SCL = 0.5 * DATA2 +DATA3 = np.linspace(-3.0, 3.0, 7) +STRETCHES = (SqrtStretch(), PowerStretch(0.5), LogStretch()) +INVALID = (None, -np.inf, -1) + + +@pytest.mark.skipif(HAS_PLT, reason="matplotlib is installed") +def test_normalize_error_message(): + with pytest.raises( + ImportError, match=r"matplotlib is required in order to use this class." + ): + ImageNormalize() + + +@pytest.mark.skipif(not HAS_PLT, reason="requires matplotlib") +class TestNormalize: + def test_invalid_interval(self): + with pytest.raises(TypeError): + ImageNormalize(vmin=2.0, vmax=10.0, interval=ManualInterval, clip=True) + + def test_invalid_vmin_vmax(self): + with pytest.raises(ValueError): + norm = ImageNormalize(vmin=10.0, vmax=2.0) + norm(10) + + def test_invalid_stretch(self): + with pytest.raises(TypeError): + ImageNormalize(vmin=2.0, vmax=10.0, stretch=SqrtStretch, clip=True) + + def test_stretch_none(self): + with pytest.raises(ValueError): + ImageNormalize(vmin=2.0, vmax=10.0, stretch=None) + + def test_scalar(self): + norm = ImageNormalize(vmin=2.0, vmax=10.0, stretch=SqrtStretch(), clip=True) + norm2 = ImageNormalize( + data=6, interval=ManualInterval(2, 10), stretch=SqrtStretch(), clip=True + ) + assert_allclose(norm(6), 0.70710678) + assert_allclose(norm(6), norm2(6)) + + def test_vmin_vmax_equal(self): + norm = ImageNormalize(vmin=2.0, vmax=2.0) + data = np.arange(10) - 5.0 + assert_array_equal(norm(data), 0) + + def test_clip(self): + norm = ImageNormalize(vmin=2.0, vmax=10.0, stretch=SqrtStretch(), clip=True) + norm2 = ImageNormalize( + DATA, interval=ManualInterval(2, 10), stretch=SqrtStretch(), clip=True + ) + output = norm(DATA) + expected = [0.0, 0.35355339, 0.70710678, 0.93541435, 1.0, 1.0] + assert_allclose(output, expected) + assert_allclose(output.mask, [0, 0, 0, 0, 0, 0]) + assert_allclose(output, norm2(DATA)) + + def test_noclip(self): + norm = ImageNormalize( + vmin=2.0, vmax=10.0, stretch=SqrtStretch(), clip=False, invalid=None + ) + norm2 = ImageNormalize( + DATA, + interval=ManualInterval(2, 10), + stretch=SqrtStretch(), + clip=False, + invalid=None, + ) + output = norm(DATA) + expected = [np.nan, 0.35355339, 0.70710678, 0.93541435, 1.11803399, 1.27475488] + assert_allclose(output, expected) + assert_allclose(output.mask, [0, 0, 0, 0, 0, 0]) + assert_allclose(norm.inverse(norm(DATA))[1:], DATA[1:]) + assert_allclose(output, norm2(DATA)) + + def test_implicit_autoscale(self): + norm = ImageNormalize(vmin=None, vmax=10.0, stretch=SqrtStretch(), clip=False) + norm2 = ImageNormalize( + DATA, interval=ManualInterval(None, 10), stretch=SqrtStretch(), clip=False + ) + output = norm(DATA) + assert norm.vmin == np.min(DATA) + assert norm.vmax == 10.0 + assert_allclose(output, norm2(DATA)) + + norm = ImageNormalize(vmin=2.0, vmax=None, stretch=SqrtStretch(), clip=False) + norm2 = ImageNormalize( + DATA, interval=ManualInterval(2, None), stretch=SqrtStretch(), clip=False + ) + output = norm(DATA) + assert norm.vmin == 2.0 + assert norm.vmax == np.max(DATA) + assert_allclose(output, norm2(DATA)) + + def test_call_clip(self): + """Test that the clip keyword is used when calling the object.""" + data = np.arange(5) + norm = ImageNormalize(vmin=1.0, vmax=3.0, clip=False) + + output = norm(data, clip=True) + assert_equal(output.data, [0, 0, 0.5, 1.0, 1.0]) + assert np.all(~output.mask) + + output = norm(data, clip=False) + assert_equal(output.data, [-0.5, 0, 0.5, 1.0, 1.5]) + assert np.all(~output.mask) + + def test_masked_clip(self): + mdata = ma.array(DATA, mask=[0, 0, 1, 0, 0, 0]) + norm = ImageNormalize(vmin=2.0, vmax=10.0, stretch=SqrtStretch(), clip=True) + norm2 = ImageNormalize( + mdata, interval=ManualInterval(2, 10), stretch=SqrtStretch(), clip=True + ) + output = norm(mdata) + expected = [0.0, 0.35355339, 1.0, 0.93541435, 1.0, 1.0] + assert_allclose(output.filled(-10), expected) + assert_allclose(output.mask, [0, 0, 0, 0, 0, 0]) + assert_allclose(output, norm2(mdata)) + + def test_masked_noclip(self): + mdata = ma.array(DATA, mask=[0, 0, 1, 0, 0, 0]) + norm = ImageNormalize( + vmin=2.0, vmax=10.0, stretch=SqrtStretch(), clip=False, invalid=None + ) + norm2 = ImageNormalize( + mdata, + interval=ManualInterval(2, 10), + stretch=SqrtStretch(), + clip=False, + invalid=None, + ) + output = norm(mdata) + expected = [np.nan, 0.35355339, -10, 0.93541435, 1.11803399, 1.27475488] + assert_allclose(output.filled(-10), expected) + assert_allclose(output.mask, [0, 0, 1, 0, 0, 0]) + + assert_allclose(norm.inverse(norm(DATA))[1:], DATA[1:]) + assert_allclose(output, norm2(mdata)) + + def test_invalid_data(self): + data = np.arange(25.0).reshape((5, 5)) + data[2, 2] = np.nan + data[1, 2] = np.inf + percent = 85.0 + interval = PercentileInterval(percent) + + # initialized without data + norm = ImageNormalize(interval=interval) + norm(data) # sets vmin/vmax + assert_equal((norm.vmin, norm.vmax), (1.65, 22.35)) + + # initialized with data + norm2 = ImageNormalize(data, interval=interval) + assert_equal((norm2.vmin, norm2.vmax), (norm.vmin, norm.vmax)) + + norm3 = simple_norm(data, "linear", percent=percent) + assert_equal((norm3.vmin, norm3.vmax), (norm.vmin, norm.vmax)) + + assert_allclose(norm(data), norm2(data)) + assert_allclose(norm(data), norm3(data)) + + norm4 = ImageNormalize() + norm4(data) # sets vmin/vmax + assert_equal((norm4.vmin, norm4.vmax), (0, 24)) + + norm5 = ImageNormalize(data) + assert_equal((norm5.vmin, norm5.vmax), (norm4.vmin, norm4.vmax)) + + @pytest.mark.parametrize("stretch", STRETCHES) + def test_invalid_keyword(self, stretch): + norm1 = ImageNormalize( + stretch=stretch, vmin=-1, vmax=1, clip=False, invalid=None + ) + norm2 = ImageNormalize(stretch=stretch, vmin=-1, vmax=1, clip=False) + norm3 = ImageNormalize( + DATA3, stretch=stretch, vmin=-1, vmax=1, clip=False, invalid=-1.0 + ) + result1 = norm1(DATA3) + result2 = norm2(DATA3) + result3 = norm3(DATA3) + assert_equal(result1[0:2], (np.nan, np.nan)) + assert_equal(result2[0:2], (-1.0, -1.0)) + assert_equal(result1[2:], result2[2:]) + assert_equal(result2, result3) + + +@pytest.mark.skipif(not HAS_PLT, reason="requires matplotlib") +class TestImageScaling: + def test_linear(self): + """Test linear scaling.""" + norm = simple_norm(DATA2, stretch="linear") + assert_allclose(norm(DATA2), DATA2SCL, atol=0, rtol=1.0e-5) + + def test_sqrt(self): + """Test sqrt scaling.""" + norm1 = simple_norm(DATA2, stretch="sqrt") + assert_allclose(norm1(DATA2), np.sqrt(DATA2SCL), atol=0, rtol=1.0e-5) + + @pytest.mark.parametrize("invalid", INVALID) + def test_sqrt_invalid_kw(self, invalid): + stretch = SqrtStretch() + norm1 = simple_norm( + DATA3, stretch="sqrt", vmin=-1, vmax=1, clip=False, invalid=invalid + ) + norm2 = ImageNormalize( + stretch=stretch, vmin=-1, vmax=1, clip=False, invalid=invalid + ) + assert_equal(norm1(DATA3), norm2(DATA3)) + + def test_power(self): + """Test power scaling.""" + power = 3.0 + norm = simple_norm(DATA2, stretch="power", power=power) + assert_allclose(norm(DATA2), DATA2SCL**power, atol=0, rtol=1.0e-5) + + def test_log(self): + """Test log10 scaling.""" + norm = simple_norm(DATA2, stretch="log") + ref = np.log10(1000 * DATA2SCL + 1.0) / np.log10(1001.0) + assert_allclose(norm(DATA2), ref, atol=0, rtol=1.0e-5) + + def test_log_with_log_a(self): + """Test log10 scaling with a custom log_a.""" + log_a = 100 + norm = simple_norm(DATA2, stretch="log", log_a=log_a) + ref = np.log10(log_a * DATA2SCL + 1.0) / np.log10(log_a + 1) + assert_allclose(norm(DATA2), ref, atol=0, rtol=1.0e-5) + + def test_asinh(self): + """Test asinh scaling.""" + norm = simple_norm(DATA2, stretch="asinh") + ref = np.arcsinh(10 * DATA2SCL) / np.arcsinh(10) + assert_allclose(norm(DATA2), ref, atol=0, rtol=1.0e-5) + + def test_asinh_with_asinh_a(self): + """Test asinh scaling with a custom asinh_a.""" + asinh_a = 0.5 + norm = simple_norm(DATA2, stretch="asinh", asinh_a=asinh_a) + ref = np.arcsinh(DATA2SCL / asinh_a) / np.arcsinh(1.0 / asinh_a) + assert_allclose(norm(DATA2), ref, atol=0, rtol=1.0e-5) + + def test_sinh(self): + """Test sinh scaling.""" + sinh_a = 0.5 + norm = simple_norm(DATA2, stretch="sinh", sinh_a=sinh_a) + ref = np.sinh(DATA2SCL / sinh_a) / np.sinh(1 / sinh_a) + assert_allclose(norm(DATA2), ref, atol=0, rtol=1.0e-5) + + def test_min(self): + """Test linear scaling.""" + norm = simple_norm(DATA2, stretch="linear", vmin=1.0, clip=True) + assert_allclose(norm(DATA2), [0.0, 0.0, 1.0], atol=0, rtol=1.0e-5) + + def test_percent(self): + """Test percent keywords.""" + norm = simple_norm(DATA2, stretch="linear", percent=99.0, clip=True) + assert_allclose(norm(DATA2), DATA2SCL, atol=0, rtol=1.0e-5) + + norm2 = simple_norm( + DATA2, stretch="linear", min_percent=0.5, max_percent=99.5, clip=True + ) + assert_allclose(norm(DATA2), norm2(DATA2), atol=0, rtol=1.0e-5) + + def test_invalid_stretch(self): + """Test invalid stretch keyword.""" + with pytest.raises(ValueError): + simple_norm(DATA2, stretch="invalid") + + +@pytest.mark.skipif(not HAS_PLT, reason="requires matplotlib") +@pytest.mark.parametrize("stretch", ["linear", "sqrt", "power", "log", "asinh", "sinh"]) +def test_simplenorm(stretch): + data = np.arange(25).reshape((5, 5)) + snorm = SimpleNorm(stretch, percent=99) + norm = snorm(data) + assert isinstance(norm, ImageNormalize) + assert_allclose(norm(data), simple_norm(data, stretch, percent=99)(data)) + + +@pytest.mark.skipif(not HAS_PLT, reason="requires matplotlib") +def test_simplenorm_imshow(): + from matplotlib.figure import Figure + from matplotlib.image import AxesImage + + data = np.arange(25).reshape((5, 5)) + fig = Figure() + ax = fig.add_subplot() + snorm = SimpleNorm("sqrt", percent=99) + axim = snorm.imshow(data, ax=ax) + assert isinstance(axim, AxesImage) + keys = ("vmin", "vmax", "stretch", "clip", "invalid") + for key in keys: + assert getattr(axim.norm, key) == getattr(snorm(data), key) + + fig.clear() + axim = snorm.imshow(data, ax=None) + + with pytest.raises(ValueError): + snorm.imshow(data, ax=ax, norm=ImageNormalize()) + + +@pytest.mark.skipif(not HAS_PLT, reason="requires matplotlib") +def test_imshow_norm(): + from matplotlib.figure import Figure + + image = np.random.randn(10, 10) + + fig = Figure() + ax = fig.add_subplot(label="test_imshow_norm") + imshow_norm(image, ax=ax) + + with pytest.raises(ValueError): + # illegal to manually pass in normalization since that defeats the point + imshow_norm(image, ax=ax, norm=ImageNormalize()) + + fig.clear() + imshow_norm(image, ax=ax, vmin=0, vmax=1) + + # make sure the matplotlib version works + fig.clear() + imres, norm = imshow_norm(image, ax=None) + + assert isinstance(norm, ImageNormalize) + + +@pytest.mark.skipif(not HAS_PLT, reason="requires matplotlib") +def test_imshow_simple_norm(): + from matplotlib.figure import Figure + + image = np.arange(100).reshape(10, 10) + + fig = Figure() + ax = fig.add_subplot(label="test_imshow_simple_norm") + imshow_simple_norm(image, ax=ax) + + with pytest.raises(ValueError): + # illegal to manually pass in normalization since that defeats the point + imshow_simple_norm(image, ax=ax, norm=ImageNormalize()) + + fig.clear() + imshow_simple_norm(image, ax=ax, vmin=0, vmax=1) + + fig.clear() + imshow_simple_norm(image, ax=ax, min_percent=1, max_percent=99.9, stretch="sinh") + + # make sure the matplotlib version works + fig.clear() + imres, norm = imshow_simple_norm(image, ax=None) + + assert np.all(imres.get_array() == image) + + # ensure the normalization is *not* just minmax like default imshow + assert (image.min(), image.max()) == imres.get_clim() + + assert isinstance(norm, ImageNormalize) + + +@pytest.mark.skipif(not HAS_PLT, reason="requires matplotlib") +def test_norm_without_data(): + from matplotlib.figure import Figure + + image = np.arange(10).reshape((1, 10)) + interval = ManualInterval(2, 5) + norm_without_data = ImageNormalize(interval=interval) + + assert norm_without_data.vmin is None + assert norm_without_data.vmax is None + + fig = Figure() + ax = fig.add_subplot() + ax.imshow(image, norm=norm_without_data) # calls norm_without_data.autoscale_None() + + assert norm_without_data.vmin == interval.vmin + assert norm_without_data.vmax == interval.vmax diff --git a/astropy/visualization/tests/test_stretch.py b/astropy/visualization/tests/test_stretch.py new file mode 100644 index 000000000000..6def82936cc1 --- /dev/null +++ b/astropy/visualization/tests/test_stretch.py @@ -0,0 +1,169 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +import numpy as np +import pytest +from numpy.testing import assert_allclose, assert_equal + +from astropy.visualization.stretch import ( + AsinhStretch, + ContrastBiasStretch, + HistEqStretch, + InvertedHistEqStretch, + InvertedLogStretch, + InvertedPowerDistStretch, + LinearStretch, + LogStretch, + PowerDistStretch, + PowerStretch, + SinhStretch, + SqrtStretch, + SquaredStretch, +) + +DATA = np.array([0.00, 0.25, 0.50, 0.75, 1.00]) + +RESULTS = {} +RESULTS[LinearStretch()] = np.array([0.00, 0.25, 0.50, 0.75, 1.00]) +RESULTS[LinearStretch(intercept=0.5) + LinearStretch(slope=0.5)] = np.array( + [0.5, 0.625, 0.75, 0.875, 1.0] +) +RESULTS[SqrtStretch()] = np.array([0.0, 0.5, 0.70710678, 0.8660254, 1.0]) +RESULTS[SquaredStretch()] = np.array([0.0, 0.0625, 0.25, 0.5625, 1.0]) +RESULTS[PowerStretch(0.5)] = np.array([0.0, 0.5, 0.70710678, 0.8660254, 1.0]) +RESULTS[PowerDistStretch()] = np.array([0.0, 0.004628, 0.030653, 0.177005, 1.0]) +RESULTS[LogStretch()] = np.array([0.0, 0.799776, 0.899816, 0.958408, 1.0]) +RESULTS[AsinhStretch()] = np.array([0.0, 0.549402, 0.77127, 0.904691, 1.0]) +RESULTS[SinhStretch()] = np.array([0.0, 0.082085, 0.212548, 0.46828, 1.0]) +RESULTS[ContrastBiasStretch(contrast=2.0, bias=0.4)] = np.array( + [-0.3, 0.2, 0.7, 1.2, 1.7] +) +RESULTS[HistEqStretch(DATA)] = DATA +RESULTS[HistEqStretch(DATA[::-1])] = DATA +RESULTS[HistEqStretch(DATA**0.5)] = np.array([0.0, 0.125, 0.25, 0.5674767, 1.0]) + + +class TestStretch: + @pytest.mark.parametrize("stretch", RESULTS.keys()) + def test_no_clip(self, stretch): + np.testing.assert_allclose( + stretch(DATA, clip=False), RESULTS[stretch], atol=1.0e-6 + ) + + @pytest.mark.parametrize("ndim", [2, 3]) + @pytest.mark.parametrize("stretch", RESULTS.keys()) + def test_clip_ndimensional(self, stretch, ndim): + new_shape = DATA.shape + (1,) * ndim + + np.testing.assert_allclose( + stretch(DATA.reshape(new_shape), clip=True).ravel(), + np.clip(RESULTS[stretch], 0.0, 1), + atol=1.0e-6, + ) + + @pytest.mark.parametrize("stretch", RESULTS.keys()) + def test_clip(self, stretch): + np.testing.assert_allclose( + stretch(DATA, clip=True), np.clip(RESULTS[stretch], 0.0, 1), atol=1.0e-6 + ) + + @pytest.mark.parametrize("stretch", RESULTS.keys()) + def test_inplace(self, stretch): + data_in = DATA.copy() + result = np.zeros(DATA.shape) + stretch(data_in, out=result, clip=False) + np.testing.assert_allclose(result, RESULTS[stretch], atol=1.0e-6) + np.testing.assert_allclose(data_in, DATA) + + @pytest.mark.parametrize("stretch", RESULTS.keys()) + def test_round_trip(self, stretch): + np.testing.assert_allclose( + stretch.inverse(stretch(DATA, clip=False), clip=False), DATA + ) + + @pytest.mark.parametrize("stretch", RESULTS.keys()) + def test_inplace_roundtrip(self, stretch): + result = np.zeros(DATA.shape) + stretch(DATA, out=result, clip=False) + stretch.inverse(result, out=result, clip=False) + np.testing.assert_allclose(result, DATA) + + @pytest.mark.parametrize("stretch", RESULTS.keys()) + def test_double_inverse(self, stretch): + np.testing.assert_allclose( + stretch.inverse.inverse(DATA), stretch(DATA), atol=1.0e-6 + ) + + def test_inverted(self): + stretch_1 = SqrtStretch().inverse + stretch_2 = PowerStretch(2) + np.testing.assert_allclose(stretch_1(DATA), stretch_2(DATA)) + + def test_chaining(self): + stretch_1 = SqrtStretch() + SqrtStretch() + stretch_2 = PowerStretch(0.25) + stretch_3 = PowerStretch(4.0) + + np.testing.assert_allclose(stretch_1(DATA), stretch_2(DATA)) + + np.testing.assert_allclose(stretch_1.inverse(DATA), stretch_3(DATA)) + + +def test_clip_invalid(): + stretch = SqrtStretch() + + values = stretch([-1.0, 0.0, 0.5, 1.0, 1.5]) + np.testing.assert_allclose(values, [0.0, 0.0, 0.70710678, 1.0, 1.0]) + + values = stretch([-1.0, 0.0, 0.5, 1.0, 1.5], clip=False) + np.testing.assert_allclose(values, [np.nan, 0.0, 0.70710678, 1.0, 1.2247448]) + + +@pytest.mark.parametrize("a", [-2.0, -1, 0.0, 1.0]) +def test_invalid_powerdist_a(a): + match = "a must be > 0, but cannot be set to 1" + with pytest.raises(ValueError, match=match): + PowerDistStretch(a=a) + with pytest.raises(ValueError, match=match): + InvertedPowerDistStretch(a=a) + + +@pytest.mark.parametrize("a", [-2.0, -1, 0.0]) +def test_invalid_power_log_a(a): + match = "a must be > 0" + with pytest.raises(ValueError, match=match): + PowerStretch(a=a) + with pytest.raises(ValueError, match=match): + LogStretch(a=a) + with pytest.raises(ValueError, match=match): + InvertedLogStretch(a=a) + + +@pytest.mark.parametrize("a", [-2.0, -1, 0.0]) +def test_invalid_sinh_a(a): + match = "a must be > 0" + with pytest.raises(ValueError, match=match): + AsinhStretch(a=a) + with pytest.raises(ValueError, match=match): + SinhStretch(a=a) + + +def test_sinh_a(): + a = 0.9 + a_inv = 1.0 / np.arcsinh(1.0 / a) + z = AsinhStretch(a=a) + assert_allclose(z.inverse.a, a_inv) + + +def test_histeqstretch_invalid(): + data = np.array([-np.inf, 0.00, 0.25, 0.50, 0.75, 1.00, np.inf]) + result = np.array([0.0, 0.0, 0.25, 0.5, 0.75, 1.0, 1.0]) + assert_equal(HistEqStretch(data)(data), result) + assert_equal(InvertedHistEqStretch(data)(data), result) + + +def test_linearstretch_clip(): + data = np.linspace(0, 1, 100) + stretch = LinearStretch(slope=2) + result = stretch(data, clip=True) + assert np.min(result) >= 0 + assert np.max(result) <= 1 diff --git a/astropy/visualization/tests/test_time.py b/astropy/visualization/tests/test_time.py new file mode 100644 index 000000000000..b4825f720c3a --- /dev/null +++ b/astropy/visualization/tests/test_time.py @@ -0,0 +1,268 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +from contextlib import nullcontext + +import pytest +from erfa import ErfaWarning + +from astropy.time import Time +from astropy.visualization.time import time_support + +pytest.importorskip("matplotlib.pyplot") +from matplotlib.figure import Figure + +# Since some of the examples below use times/dates in the future, we use the +# TAI time scale to avoid ERFA warnings about dubious years. +DEFAULT_SCALE = "tai" + + +def get_ticklabels(axis): + axis.figure.canvas.draw() + return [x.get_text() for x in axis.get_ticklabels()] + + +# We first check that we get the expected labels for different time intervals +# for standard ISO formatting. This is a way to check both the locator and +# formatter code. + +RANGE_CASES = [ + # Interval of many years + ( + ("2014-03-22T12:30:30.9", "2077-03-22T12:30:32.1"), + ["2020-01-01", "2040-01-01", "2060-01-01"], + ), + # Interval of a few years + ( + ("2014-03-22T12:30:30.9", "2017-03-22T12:30:32.1"), + ["2015-01-01", "2016-01-01", "2017-01-01"], + ), + # Interval of just under a year + (("2014-03-22T12:30:30.9", "2015-01-22T12:30:32.1"), ["2014-05-01", "2014-10-01"]), + # Interval of a few months + ( + ("2014-11-22T12:30:30.9", "2015-02-22T12:30:32.1"), + ["2014-12-01", "2015-01-01", "2015-02-01"], + ), + # Interval of just over a month + (("2014-03-22T12:30:30.9", "2014-04-23T12:30:32.1"), ["2014-04-01"]), + # Interval of just under a month + ( + ("2014-03-22T12:30:30.9", "2014-04-21T12:30:32.1"), + ["2014-03-24", "2014-04-03", "2014-04-13"], + ), + # Interval of just over an hour + ( + ("2014-03-22T12:30:30.9", "2014-03-22T13:31:30.9"), + [ + "2014-03-22T12:40:00.000", + "2014-03-22T13:00:00.000", + "2014-03-22T13:20:00.000", + ], + ), + # Interval of just under an hour + ( + ("2014-03-22T12:30:30.9", "2014-03-22T13:28:30.9"), + [ + "2014-03-22T12:40:00.000", + "2014-03-22T13:00:00.000", + "2014-03-22T13:20:00.000", + ], + ), + # Interval of a few minutes + ( + ("2014-03-22T12:30:30.9", "2014-03-22T12:38:30.9"), + ["2014-03-22T12:33:00.000", "2014-03-22T12:36:00.000"], + ), + # Interval of a few seconds + ( + ("2014-03-22T12:30:30.9", "2014-03-22T12:30:40.9"), + [ + "2014-03-22T12:30:33.000", + "2014-03-22T12:30:36.000", + "2014-03-22T12:30:39.000", + ], + ), + # Interval of a couple of seconds + ( + ("2014-03-22T12:30:30.9", "2014-03-22T12:30:32.1"), + [ + "2014-03-22T12:30:31.000", + "2014-03-22T12:30:31.500", + "2014-03-22T12:30:32.000", + ], + ), + # Interval of under a second + ( + ("2014-03-22T12:30:30.89", "2014-03-22T12:30:31.19"), + [ + "2014-03-22T12:30:30.900", + "2014-03-22T12:30:31.000", + "2014-03-22T12:30:31.100", + ], + ), +] + + +@pytest.mark.parametrize(("interval", "expected"), RANGE_CASES) +def test_formatter_locator(interval, expected): + # Check that the ticks and labels returned for the above cases are correct. + + with time_support(): + fig = Figure() + ax = fig.add_subplot(1, 1, 1) + ax.set_xlim( + Time(interval[0], scale=DEFAULT_SCALE), + Time(interval[1], scale=DEFAULT_SCALE), + ) + assert get_ticklabels(ax.xaxis) == expected + + +FORMAT_CASES = [ + ("byear", ["2020", "2040", "2060"]), + ("byear_str", ["B2020.000", "B2040.000", "B2060.000"]), + ("cxcsec", ["1000000000", "1500000000", "2000000000", "2500000000"]), + ("decimalyear", ["2020", "2040", "2060"]), + ( + "fits", + [ + "2020-01-01T00:00:00.000", + "2040-01-01T00:00:00.000", + "2060-01-01T00:00:00.000", + ], + ), + ("gps", ["1500000000", "2000000000", "2500000000", "3000000000"]), + ( + "iso", + [ + "2020-01-01 00:00:00.000", + "2040-01-01 00:00:00.000", + "2060-01-01 00:00:00.000", + ], + ), + ( + "isot", + [ + "2020-01-01T00:00:00.000", + "2040-01-01T00:00:00.000", + "2060-01-01T00:00:00.000", + ], + ), + ("jd", ["2458000", "2464000", "2470000", "2476000"]), + ("jyear", ["2020", "2040", "2060"]), + ("jyear_str", ["J2020.000", "J2040.000", "J2060.000"]), + ("mjd", ["60000", "66000", "72000", "78000"]), + ("plot_date", (["18000", "24000", "30000", "36000"])), + ("unix", ["1500000000", "2000000000", "2500000000", "3000000000"]), + ( + "yday", + ["2020:001:00:00:00.000", "2040:001:00:00:00.000", "2060:001:00:00:00.000"], + ), +] + + +@pytest.mark.parametrize(("format", "expected"), FORMAT_CASES) +def test_formats(format, expected): + # Check that the locators/formatters work fine for all time formats + with time_support(format=format, simplify=False): + fig = Figure() + ax = fig.add_subplot(1, 1, 1) + # Getting unix time and plot_date requires going through a scale for + # which ERFA emits a warning about the date being dubious + with ( + pytest.warns(ErfaWarning) + if format in ["unix", "plot_date"] + else nullcontext() + ): + ax.set_xlim( + Time("2014-03-22T12:30:30.9", scale=DEFAULT_SCALE), + Time("2077-03-22T12:30:32.1", scale=DEFAULT_SCALE), + ) + assert get_ticklabels(ax.xaxis) == expected + ax.get_xlabel() == f"Time ({format})" + + +@pytest.mark.parametrize(("format", "expected"), FORMAT_CASES) +def test_auto_formats(format, expected): + # Check that the format/scale is taken from the first time used. + with time_support(simplify=False): + fig = Figure() + ax = fig.add_subplot(1, 1, 1) + # Getting unix time and plot_date requires going through a scale for + # which ERFA emits a warning about the date being dubious + with ( + pytest.warns(ErfaWarning) + if format in ["unix", "plot_date"] + else nullcontext() + ): + ax.set_xlim( + Time(Time("2014-03-22T12:30:30.9", scale=DEFAULT_SCALE), format=format), + Time("2077-03-22T12:30:32.1", scale=DEFAULT_SCALE), + ) + assert get_ticklabels(ax.xaxis) == expected + ax.get_xlabel() == f"Time ({format})" + + +FORMAT_CASES_SIMPLIFY = [ + ("fits", ["2020-01-01", "2040-01-01", "2060-01-01"]), + ("iso", ["2020-01-01", "2040-01-01", "2060-01-01"]), + ("isot", ["2020-01-01", "2040-01-01", "2060-01-01"]), + ("yday", ["2020", "2040", "2060"]), +] + + +@pytest.mark.parametrize(("format", "expected"), FORMAT_CASES_SIMPLIFY) +def test_formats_simplify(format, expected): + # Check the use of the simplify= option + with time_support(format=format, simplify=True): + fig = Figure() + ax = fig.add_subplot(1, 1, 1) + ax.set_xlim( + Time("2014-03-22T12:30:30.9", scale=DEFAULT_SCALE), + Time("2077-03-22T12:30:32.1", scale=DEFAULT_SCALE), + ) + assert get_ticklabels(ax.xaxis) == expected + + +def test_plot(): + # Make sure that plot() works properly + with time_support(): + fig = Figure() + ax = fig.add_subplot(1, 1, 1) + ax.set_xlim( + Time("2014-03-22T12:30:30.9", scale=DEFAULT_SCALE), + Time("2077-03-22T12:30:32.1", scale=DEFAULT_SCALE), + ) + ax.plot( + Time( + [ + "2015-03-22T12:30:30.9", + "2018-03-22T12:30:30.9", + "2021-03-22T12:30:30.9", + ], + scale=DEFAULT_SCALE, + ) + ) + + +def test_nested(): + with time_support(format="iso", simplify=False): + with time_support(format="yday", simplify=True): + fig = Figure() + ax = fig.add_subplot(1, 1, 1) + ax.set_xlim( + Time("2014-03-22T12:30:30.9", scale=DEFAULT_SCALE), + Time("2077-03-22T12:30:32.1", scale=DEFAULT_SCALE), + ) + assert get_ticklabels(ax.xaxis) == ["2020", "2040", "2060"] + + fig = Figure() + ax = fig.add_subplot(1, 1, 1) + ax.set_xlim( + Time("2014-03-22T12:30:30.9", scale=DEFAULT_SCALE), + Time("2077-03-22T12:30:32.1", scale=DEFAULT_SCALE), + ) + assert get_ticklabels(ax.xaxis) == [ + "2020-01-01 00:00:00.000", + "2040-01-01 00:00:00.000", + "2060-01-01 00:00:00.000", + ] diff --git a/astropy/visualization/tests/test_units.py b/astropy/visualization/tests/test_units.py new file mode 100644 index 000000000000..fc9e737e6f3e --- /dev/null +++ b/astropy/visualization/tests/test_units.py @@ -0,0 +1,232 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +import io + +import pytest + +from astropy.utils.compat.optional_deps import HAS_PLT + +if HAS_PLT: + from matplotlib.figure import Figure + from matplotlib.units import ConversionError + +import numpy as np + +from astropy import units as u +from astropy.coordinates import Angle +from astropy.visualization.units import MplQuantityConverter, quantity_support + + +@pytest.fixture +def converter_class() -> type[MplQuantityConverter]: + return MplQuantityConverter + + +@pytest.mark.skipif(not HAS_PLT, reason="requires matplotlib") +def test_units(): + fig = Figure() + ax = fig.add_subplot() + + with quantity_support(): + buff = io.BytesIO() + + ax.plot([1, 2, 3] * u.m, [3, 4, 5] * u.kg, label="label") + ax.plot([105, 210, 315] * u.cm, [3050, 3025, 3010] * u.g) + ax.legend() + # Also test fill_between, which requires actual conversion to ndarray. + ax.fill_between([1, 3] * u.m, [3, 5] * u.kg, [3050, 3010] * u.g) + fig.savefig(buff, format="svg") + + assert ax.xaxis.get_units() == u.m + assert ax.yaxis.get_units() == u.kg + + +@pytest.mark.skipif(not HAS_PLT, reason="requires matplotlib") +def test_units_decorator(): + @quantity_support() + def run_test(): + fig = Figure() + ax = fig.add_subplot() + + buff = io.BytesIO() + + ax.plot([1, 2, 3] * u.m, [3, 4, 5] * u.kg, label="label") + ax.plot([105, 210, 315] * u.cm, [3050, 3025, 3010] * u.g) + ax.legend() + # Also test fill_between, which requires actual conversion to ndarray. + ax.fill_between([1, 3] * u.m, [3, 5] * u.kg, [3050, 3010] * u.g) + fig.savefig(buff, format="svg") + + assert ax.xaxis.get_units() == u.m + assert ax.yaxis.get_units() == u.kg + + run_test() + + +@pytest.mark.skipif(not HAS_PLT, reason="requires matplotlib") +def test_units_errbarr(): + with quantity_support(): + x = [1, 2, 3] * u.s + y = [1, 2, 3] * u.m + yerr = [3, 2, 1] * u.cm + + fig = Figure() + ax = fig.add_subplot() + ax.errorbar(x, y, yerr=yerr) + + assert ax.xaxis.get_units() == u.s + assert ax.yaxis.get_units() == u.m + + +@pytest.mark.skipif(not HAS_PLT, reason="requires matplotlib") +def test_incompatible_units(): + fig = Figure() + ax = fig.add_subplot() + + with quantity_support(): + ax.plot([1, 2, 3] * u.m) + with pytest.raises(ConversionError): + ax.plot([105, 210, 315] * u.kg) + + +@pytest.mark.skipif(not HAS_PLT, reason="requires matplotlib") +def test_quantity_subclass(): + """Check that subclasses are recognized. + + Also see https://github.com/matplotlib/matplotlib/pull/13536 + """ + fig = Figure() + ax = fig.add_subplot() + + with quantity_support(): + ax.scatter(Angle([1, 2, 3], u.deg), [3, 4, 5] * u.kg) + ax.scatter([105, 210, 315] * u.arcsec, [3050, 3025, 3010] * u.g) + ax.plot(Angle([105, 210, 315], u.arcsec), [3050, 3025, 3010] * u.g) + + assert ax.xaxis.get_units() == u.deg + assert ax.yaxis.get_units() == u.kg + + +@pytest.mark.skipif(not HAS_PLT, reason="requires matplotlib") +def test_nested(): + with quantity_support(): + with quantity_support(): + fig = Figure() + ax = fig.add_subplot(1, 1, 1) + ax.scatter(Angle([1, 2, 3], u.deg), [3, 4, 5] * u.kg) + + assert ax.xaxis.get_units() == u.deg + assert ax.yaxis.get_units() == u.kg + + fig = Figure() + ax = fig.add_subplot(1, 1, 1) + ax.scatter(Angle([1, 2, 3], u.arcsec), [3, 4, 5] * u.pc) + + assert ax.xaxis.get_units() == u.arcsec + assert ax.yaxis.get_units() == u.pc + + +@pytest.mark.skipif(not HAS_PLT, reason="requires matplotlib") +def test_empty_hist(): + with quantity_support(): + fig = Figure() + ax = fig.add_subplot(1, 1, 1) + ax.hist([1, 2, 3, 4] * u.mmag, bins=100) + # The second call results in an empty list being passed to the + # unit converter in matplotlib >= 3.1 + ax.hist([] * u.mmag, bins=100) + + +@pytest.mark.skipif(not HAS_PLT, reason="requires matplotlib") +def test_radian_formatter(): + with quantity_support(): + fig = Figure() + ax = fig.add_subplot() + ax.plot([1, 2, 3], [1, 2, 3] * u.rad * np.pi) + fig.canvas.draw() + labels = [tl.get_text() for tl in ax.yaxis.get_ticklabels()] + assert labels == ["Ī€/2", "Ī€", "3Ī€/2", "2Ī€", "5Ī€/2", "3Ī€", "7Ī€/2"] + + +@pytest.mark.skipif(not HAS_PLT, reason="requires matplotlib") +def test_small_range(): + # see https://github.com/astropy/astropy/issues/13211 + y = [10.0, 10.25, 10.5, 10.75, 11.0, 11.25, 11.5, 11.75] * u.degree + + fig = Figure() + ax = fig.add_subplot() + + with quantity_support(): + ax.plot(y) + fig.canvas.draw() + labels = [t.get_text() for t in ax.yaxis.get_ticklabels()] + + # check uniqueness of labels + assert len(set(labels)) == len(labels) + + +@pytest.mark.skipif(not HAS_PLT, reason="requires matplotlib") +def test_override_axis_unit(): + fig = Figure() + ax = fig.add_subplot() + + with quantity_support(): + ax.plot([1, 2, 3] * u.m) + ax.yaxis.set_units(u.cm) + fig.canvas.draw() + assert ax.yaxis.get_units() == u.cm + + +@pytest.mark.parametrize( + ("format", "expected"), + [ + pytest.param(None, "$\\mathrm{erg\\,s^{-1}\\,cm^{-2}}$", id="None"), + pytest.param("console", "erg s^-1 cm^-2", id="console"), + ], +) +@pytest.mark.skipif(not HAS_PLT, reason="requires matplotlib") +def test_axisinfo_label(converter_class, format, expected): + result = converter_class.axisinfo(u.erg / (u.cm**2 * u.s), None, format).label + assert result == expected + + +@pytest.mark.parametrize( + ("val", "expected"), + [ + pytest.param(u.Quantity([1, 2, 3], u.m), np.array([1, 2, 3]), id="Quantity[m]"), + pytest.param( + u.Quantity([1, 2, 3], u.cm), np.array([0.01, 0.02, 0.03]), id="Quantity[cm]" + ), + pytest.param( + np.array([1 * u.m, 2 * u.m, 3 * u.m], dtype=object), + np.array([1, 2, 3]), + id="np_array", + ), + ], +) +@pytest.mark.skipif(not HAS_PLT, reason="requires matplotlib") +def test_convert(converter_class, val, expected): + result = converter_class.convert(val, u.m, None) + assert all(result == expected) + + +@pytest.mark.parametrize( + ("x", "expected"), + [ + pytest.param(u.Quantity([1, 2, 3], u.m), u.m, id="Quantity[m]"), + pytest.param( + u.Quantity([1, 2, 3]), + u.dimensionless_unscaled, + id="Quantity[dimensionless]", + ), + pytest.param([1, 2, 3], None, id="scalar list"), + pytest.param( + np.array([1 * u.m, 2 * u.m, 3 * u.m], dtype=object), u.m, id="np array" + ), + pytest.param(np.array([], dtype=object), None, id="np array empty"), + ], +) +@pytest.mark.skipif(not HAS_PLT, reason="requires matplotlib") +def test_default_units(converter_class, x, expected): + result = converter_class.default_units(x, None) + assert result == expected diff --git a/astropy/visualization/time.py b/astropy/visualization/time.py new file mode 100644 index 000000000000..245a54710083 --- /dev/null +++ b/astropy/visualization/time.py @@ -0,0 +1,247 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +from datetime import datetime + +import numpy as np + +from astropy import units as u +from astropy.time import Time + +__all__ = ["time_support"] + +__doctest_requires__ = {"time_support": ["matplotlib"]} + +UNSUPPORTED_FORMATS = ("datetime", "datetime64") +YMDHMS_FORMATS = ("fits", "iso", "isot", "yday") +STR_FORMATS = YMDHMS_FORMATS + ("byear_str", "jyear_str") + + +def time_support(*, scale=None, format=None, simplify=True): + """ + Enable support for plotting `astropy.time.Time` instances in + matplotlib. + + May be (optionally) used with a ``with`` statement. + + >>> import matplotlib.pyplot as plt + >>> from astropy import units as u + >>> from astropy import visualization + >>> with visualization.time_support(): # doctest: +IGNORE_OUTPUT + ... plt.figure() + ... plt.plot(Time(['2016-03-22T12:30:31', '2016-03-22T12:30:38', '2016-03-22T12:34:40'])) + ... plt.draw() + + Parameters + ---------- + scale : str, optional + The time scale to use for the times on the axis. If not specified, + the scale of the first Time object passed to Matplotlib is used. + format : str, optional + The time format to use for the times on the axis. If not specified, + the format of the first Time object passed to Matplotlib is used. + simplify : bool, optional + If possible, simplify labels, e.g. by removing 00:00:00.000 times from + ISO strings if all labels fall on that time. + """ + import matplotlib.units as units + from matplotlib.ticker import MaxNLocator, ScalarFormatter + + from astropy.visualization.wcsaxes.utils import select_step_hour, select_step_scalar + + class AstropyTimeLocator(MaxNLocator): + # Note: we default to AutoLocator since many time formats + # can just use this. + + def __init__(self, converter, *args, **kwargs): + kwargs["nbins"] = 4 + super().__init__(*args, **kwargs) + self._converter = converter + + def tick_values(self, vmin, vmax): + # Where we put the ticks depends on the format we are using + if self._converter.format in YMDHMS_FORMATS: + # If we are here, we need to check what the range of values + # is and decide how to find tick locations accordingly + + vrange = vmax - vmin + + if ( + self._converter.format != "yday" and vrange > 31 + ) or vrange > 366: # greater than a month + # We need to be careful here since not all years and months have + # the same length + + # Start off by converting the values from the range to + # datetime objects, so that we can easily extract the year and + # month. + + tmin = Time( + vmin, scale=self._converter.scale, format="mjd" + ).datetime + tmax = Time( + vmax, scale=self._converter.scale, format="mjd" + ).datetime + + # Find the range of years + ymin = tmin.year + ymax = tmax.year + + if ymax > ymin + 1: # greater than a year + # Find the step we want to use + ystep = int(select_step_scalar(max(1, (ymax - ymin) / 3))) + + ymin = ystep * (ymin // ystep) + + # Generate the years for these steps + times = [] + for year in range(ymin, ymax + 1, ystep): + times.append(datetime(year=year, month=1, day=1)) + + else: # greater than a month but less than a year + mmin = tmin.month + mmax = tmax.month + 12 * (ymax - ymin) + + mstep = int(select_step_scalar(max(1, (mmax - mmin) / 3))) + + mmin = mstep * max(1, mmin // mstep) + + # Generate the months for these steps + times = [] + for month in range(mmin, mmax + 1, mstep): + times.append( + datetime( + year=ymin + (month - 1) // 12, + month=(month - 1) % 12 + 1, + day=1, + ) + ) + + # Convert back to MJD + values = Time(times, scale=self._converter.scale).mjd + + elif vrange > 1: # greater than a day + self.set_params(steps=[1, 2, 5, 10]) + values = super().tick_values(vmin, vmax) + + else: + # Determine ideal step + dv = (vmax - vmin) / 3 * 24 << u.hourangle + + # And round to nearest sensible value + dv = select_step_hour(dv).to_value(u.hourangle) / 24 + + # Determine tick locations + imin = np.ceil(vmin / dv) + imax = np.floor(vmax / dv) + values = np.arange(imin, imax + 1, dtype=np.int64) * dv + + else: + values = super().tick_values(vmin, vmax) + + # Get rid of values outside of the input interval + return values[(values >= vmin) & (values <= vmax)] + + def __call__(self): + vmin, vmax = self.axis.get_view_interval() + return self.tick_values(vmin, vmax) + + class AstropyTimeFormatter(ScalarFormatter): + def __init__(self, converter, *args, **kwargs): + super().__init__(*args, **kwargs) + self._converter = converter + self.set_useOffset(False) + self.set_scientific(False) + + def format_ticks(self, values): + if len(values) == 0: + return [] + if self._converter.format in YMDHMS_FORMATS: + times = Time(values, format="mjd", scale=self._converter.scale) + formatted = getattr(times, self._converter.format) + if self._converter.simplify: + if self._converter.format in ("fits", "iso", "isot"): + if all(x.endswith("00:00:00.000") for x in formatted): + split = " " if self._converter.format == "iso" else "T" + formatted = [x.split(split)[0] for x in formatted] + elif self._converter.format == "yday": + if all(x.endswith(":001:00:00:00.000") for x in formatted): + formatted = [x.split(":", 1)[0] for x in formatted] + return formatted + elif self._converter.format == "byear_str": + return Time( + values, format="byear", scale=self._converter.scale + ).byear_str + elif self._converter.format == "jyear_str": + return Time( + values, format="jyear", scale=self._converter.scale + ).jyear_str + else: + return super().format_ticks(values) + + class MplTimeConverter(units.ConversionInterface): + def __init__(self, scale=None, format=None, simplify=None): + super().__init__() + + self.format = format + self.scale = scale + self.simplify = simplify + + # Keep track of original converter in case the context manager is + # used in a nested way. + self._original_converter = units.registry.get(Time) + + units.registry[Time] = self + + @property + def format(self): + return self._format + + @format.setter + def format(self, value): + if value in UNSUPPORTED_FORMATS: + raise ValueError(f"time_support does not support format={value}") + self._format = value + + def __enter__(self): + return self + + def __exit__(self, type, value, tb): + if self._original_converter is None: + del units.registry[Time] + else: + units.registry[Time] = self._original_converter + + def default_units(self, x, axis): + if isinstance(x, tuple): + x = x[0] + if self.format is None: + self.format = x.format + if self.scale is None: + self.scale = x.scale + return "astropy_time" + + def convert(self, value, unit, axis): + """ + Convert a Time value to a scalar or array. + """ + scaled = getattr(value, self.scale) + if self.format in YMDHMS_FORMATS: + return scaled.mjd + elif self.format == "byear_str": + return scaled.byear + elif self.format == "jyear_str": + return scaled.jyear + else: + return getattr(scaled, self.format) + + def axisinfo(self, unit, axis): + """ + Return major and minor tick locators and formatters. + """ + majloc = AstropyTimeLocator(self) + majfmt = AstropyTimeFormatter(self) + return units.AxisInfo( + majfmt=majfmt, majloc=majloc, label=f"Time ({self.scale})" + ) + + return MplTimeConverter(scale=scale, format=format, simplify=simplify) diff --git a/astropy/visualization/transform.py b/astropy/visualization/transform.py new file mode 100644 index 000000000000..64fbc712595f --- /dev/null +++ b/astropy/visualization/transform.py @@ -0,0 +1,41 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + + +__all__ = ["BaseTransform", "CompositeTransform"] + + +class BaseTransform: + """ + A transformation object. + + This is used to construct transformations such as scaling, stretching, and + so on. + """ + + def __add__(self, other): + return CompositeTransform(other, self) + + +class CompositeTransform(BaseTransform): + """ + A combination of two transforms. + + Parameters + ---------- + transform_1 : :class:`astropy.visualization.BaseTransform` + The first transform to apply. + transform_2 : :class:`astropy.visualization.BaseTransform` + The second transform to apply. + """ + + def __init__(self, transform_1, transform_2): + super().__init__() + self.transform_1 = transform_1 + self.transform_2 = transform_2 + + def __call__(self, values, clip=True): + return self.transform_2(self.transform_1(values, clip=clip), clip=clip) + + @property + def inverse(self): + return self.__class__(self.transform_2.inverse, self.transform_1.inverse) diff --git a/astropy/visualization/units.py b/astropy/visualization/units.py new file mode 100644 index 000000000000..97c74cabc552 --- /dev/null +++ b/astropy/visualization/units.py @@ -0,0 +1,160 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +from contextlib import ContextDecorator + +import numpy as np + +from astropy import units as u +from astropy.utils.compat.optional_deps import HAS_MATPLOTLIB + +__all__ = ["MplQuantityConverter", "quantity_support"] + +__doctest_skip__ = ["quantity_support"] + +_default_format = "latex_inline" + +if HAS_MATPLOTLIB: + from matplotlib import ticker, units + + class MplQuantityConverter(units.ConversionInterface, ContextDecorator): + """Matplotlib converter for ``astropy.units.Quantity``. + + Registers itself to matplotlib as the converter for + ``astropy.units.Quantity`` when initialized. If used as a context manager, + it will restore the original converter upon exit. Also see + :meth:`quantity_support` for a convenient way to use this converter + with an optional format for the ``axisinfo``. + """ + + def __init__(self): + # Keep track of original converter in case the context manager is + # used in a nested way. + self._original_converter = {u.Quantity: units.registry.get(u.Quantity)} + units.registry[u.Quantity] = self + + @staticmethod + def axisinfo(unit, axis, format=None): + """Return a :class:`matplotlib.units.AxisInfo` for *unit* and *axis*. + + Parameters + ---------- + unit : `~astropy.units.UnitBase` + The unit to format the axis for. + axis : `matplotlib.axis.Axis` + The matplotlib axis being formatted. + format : `astropy.units.format.Base` subclass or str or None, optional + The name of a format or a formatter class used to render the + axis label. If `None`, the module-level default + (``"latex_inline"``) is used. + """ + if format is None: + format = _default_format + if unit == u.radian: + + def rad_fn(x, pos=None): + n = int((x / np.pi) * 2.0 + 0.25) + if n < 3: + return ("0", "Ī€/2", "Ī€")[n] + elif n % 2 == 0: + return f"{n // 2}Ī€" + else: + return f"{n}Ī€/2" + + return units.AxisInfo( + majloc=ticker.MultipleLocator(base=np.pi / 2), + majfmt=ticker.FuncFormatter(rad_fn), + label=unit.to_string(), + ) + elif unit == u.degree: + return units.AxisInfo( + majloc=ticker.AutoLocator(), + majfmt=ticker.FormatStrFormatter("%g°"), + label=unit.to_string(), + ) + elif unit is not None: + return units.AxisInfo(label=unit.to_string(format)) + return None + + @staticmethod + def convert(val, unit, axis): + if isinstance(val, u.Quantity): + return val.to_value(unit) + elif ( + all( + hasattr(val, attr) + for attr in ["__getitem__", "__iter__", "__len__"] + ) + and len(val) > 0 + and isinstance(val[0], u.Quantity) + ): + return np.array([v.to_value(unit) for v in val]) + else: + return val + + @staticmethod + def default_units(x, axis): + if hasattr(x, "unit"): + return x.unit + elif ( + all(hasattr(x, attr) for attr in ["__getitem__", "__iter__", "__len__"]) + and len(x) > 0 + and hasattr(x[0], "unit") + ): + return x[0].unit + return None + + def __enter__(self): + return self + + def __exit__(self, type, value, tb): + if self._original_converter[u.Quantity] is None: + del units.registry[u.Quantity] + else: + units.registry[u.Quantity] = self._original_converter[u.Quantity] + +else: + # Create mock-up class to avoid import errors when matplotlib is not available. + class MplQuantityConverter: + def __init__(self, *args, **kwargs): + raise ImportError("matplotlib is required in order to use this class.") + + +def quantity_support(format=None): + """ + Enable support for plotting :class:`astropy.units.Quantity` instances in + matplotlib. + + May be (optionally) used with a ``with`` statement. + + >>> import matplotlib.pyplot as plt + >>> from astropy import units as u + >>> from astropy import visualization + >>> with visualization.quantity_support(): + ... fig, ax = plt.subplots() + ... ax.plot([1, 2, 3] * u.m) + ... # doctest: +ELLIPSIS + ... ax.plot([101, 125, 150] * u.cm) + ... # doctest: +ELLIPSIS + ... ax.yaxis.set_units(u.km) + ... plt.draw() + + The default axis unit is inferred from the first plot using a Quantity. + To override it, you can explicitly set the axis unit using + :meth:`matplotlib.axis.Axis.set_units`, for example, + ``ax.yaxis.set_units(u.km)``. + + Parameters + ---------- + format : :class:`astropy.units.format.Base` subclass or str or None, optional + The name of a format or a formatter class. If not + provided, defaults to ``latex_inline``. + + """ + + class MplQuantityConverterFormatted(MplQuantityConverter): + # Override to pass on `format` + @staticmethod + def axisinfo(unit, axis): + return MplQuantityConverter.axisinfo(unit, axis, format) + + return MplQuantityConverterFormatted() diff --git a/astropy/visualization/wcsaxes/__init__.py b/astropy/visualization/wcsaxes/__init__.py new file mode 100644 index 000000000000..18bf323780a3 --- /dev/null +++ b/astropy/visualization/wcsaxes/__init__.py @@ -0,0 +1,49 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +# The following few lines skip this module when running tests if matplotlib is +# not available (and will have no impact otherwise) + +try: + import pytest + + pytest.importorskip("matplotlib") + del pytest +except ImportError: + pass + +from astropy import config as _config + +from .coordinate_helpers import CoordinateHelper +from .coordinates_map import CoordinatesMap +from .core import * +from .helpers import * +from .patches import * +from .wcsapi import custom_ucd_coord_meta_mapping + + +class Conf(_config.ConfigNamespace): + """ + Configuration parameters for `astropy.visualization.wcsaxes`. + """ + + coordinate_range_samples = _config.ConfigItem( + 50, + "The number of samples along each image axis when determining " + "the range of coordinates in a plot.", + ) + + frame_boundary_samples = _config.ConfigItem( + 1000, + "How many points to sample along the axes when determining tick locations.", + ) + + grid_samples = _config.ConfigItem( + 1000, "How many points to sample along grid lines." + ) + + contour_grid_samples = _config.ConfigItem( + 200, "The grid size to use when drawing a grid using contours" + ) + + +conf = Conf() diff --git a/astropy/visualization/wcsaxes/_auto.py b/astropy/visualization/wcsaxes/_auto.py new file mode 100644 index 000000000000..d746673f157b --- /dev/null +++ b/astropy/visualization/wcsaxes/_auto.py @@ -0,0 +1,123 @@ +from itertools import permutations + +import numpy as np + +__all__ = ["auto_assign_coord_positions"] + + +def auto_assign_coord_positions(ax): + """ + Given a ``WCSAxes`` instance, automatically update any dynamic tick, tick + label and axis label positions. + + This function operates in-place on the axes and assumes that + ``_update_ticks`` has already been called on all the ``CoordinateHelper`` + instances. + """ + # Since ticks, tick labels and axis labels can all be auto or fixed, we need + # a few rules to decide in what order to process things: + # + # * We should deal with axis labels last - if they are set to auto they + # should be made to match the tick labels. + # * If ticks and tick labels are both set to automatic, then we first + # process tick labels and at the end we can make ticks match tick labels. + # * We should use existing fixed tick label assignments to exclude certain + # spines. + # * Fixed tick positions should not exclude other labels from being placed + # on that same spine since multiple different ticks might appear on the + # same axis. + # * If ticks are not auto and tick labels are auto then tick labels should be + # placed at a spine containing ticks. + + # Start off by simplifying some cases that don't require algorithmic + # positioning. First, if tick labels are shown at fixed positions, we + # should just adjust any auto ticks and axis labels to match. + for coords in ax._all_coords: + for coord in coords: + if "#" not in (pos := coord.get_ticklabel_position()): + if "#" in coord.get_ticks_position(): + coord.set_ticks_position(pos + ["#"]) + if "#" in coord.get_axislabel_position(): + coord.set_axislabel_position(pos + ["#"]) + + # At this point, all coordinates requiring automatic placement have + # tick labels requiring automatic placement (any coordinates with fixed + # tick label placement are dealt with). We construct a list of these + # coordinates and also keep track of spines on which tick labels are + # already fixed. + + auto_coords = [] + already_used = [] + for coords in ax._all_coords: + for coord in coords: + pos = coord.get_ticklabel_position() + if "#" in pos: + auto_coords.append(coord) + else: + already_used += list(pos) + + # If there are no more coordinates with auto settings, we are all done + if len(auto_coords) == 0: + return + + # Extract the spines for the frame + spines = coords.frame._spine_auto_position_order + + # Construct a new list of spines taking into account excluded ones + spines = "".join(s for s in spines if s not in already_used) + + # We create an iterable of different assignments of spines to + # coords, where empty string means the coordinate will not be shown + # on any axis. + if len(auto_coords) > len(spines): + spines = spines + "".join(" " * (len(auto_coords) - len(spines))) + options = permutations(spines, r=len(auto_coords)) + + # We now loop through and check different assignments based on the options + # and try and find the one that maximises the number of tick labels in the + # overall plot. + n_tick_max = -1 + best_option = None + for option in options: + # Check if option is consistent with any fixed tick positions - that + # is, if ticks were explicitly requested on axes say b and t then we + # shouldn't automatically put tick labels on say l or r. + consistent = True + for c, s in zip(auto_coords, option): + pos = c.get_ticks_position() + if "#" not in pos and s not in pos: + consistent = False + break + if not consistent: + continue + + # Determine the number of tick labels on each axis + n_on_each = {s: len(c._ticks.world[s]) for c, s in zip(auto_coords, option)} + + # Determine the total number of tick labels + n_tick = sum(n_on_each.values()) + + # Determine a sorted version of this list by spine axis order + n_on_each_sorted = [n_on_each.get(s, 0) for s in spines] + + # We should ideally not have empty spines, so if there are any non-zero + # values in n_on_each then we should add a penalty for every 0 value + # that occurs before the last non-zero value + idx = np.nonzero(n_on_each_sorted)[0] + if len(idx) > 0: + n_tick -= n_on_each_sorted[: idx[-1]].count(0) + + # Keep track of the best option so far + if n_tick > n_tick_max: + n_tick_max = n_tick + best_option = option + + # Finalize assignments + for coord, spine in zip(auto_coords, best_option): + position = [spine, "#"] if spine != " " else "#" + if "#" in coord.get_ticks_position(): + coord.set_ticks_position(position) + if "#" in coord.get_ticklabel_position(): + coord.set_ticklabel_position(position) + if "#" in coord.get_axislabel_position(): + coord.set_axislabel_position(position) diff --git a/astropy/visualization/wcsaxes/axislabels.py b/astropy/visualization/wcsaxes/axislabels.py new file mode 100644 index 000000000000..5b011d5a4711 --- /dev/null +++ b/astropy/visualization/wcsaxes/axislabels.py @@ -0,0 +1,151 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + + +import matplotlib.transforms as mtransforms +import numpy as np +from matplotlib import rcParams +from matplotlib.text import Text + +from .frame import RectangularFrame + + +class AxisLabels(Text): + def __init__(self, frame, minpad=1, *args, **kwargs): + # Use rcParams if the following parameters were not specified explicitly + if "weight" not in kwargs: + kwargs["weight"] = rcParams["axes.labelweight"] + if "size" not in kwargs: + kwargs["size"] = rcParams["axes.labelsize"] + if "color" not in kwargs: + kwargs["color"] = rcParams["axes.labelcolor"] + + self._frame = frame + super().__init__(*args, **kwargs) + self.set_clip_on(True) + self.set_visible_axes("all") + self.set_ha("center") + self.set_va("center") + self._minpad = minpad + self._visibility_rule = "labels" + + def get_minpad(self, axis): + try: + return self._minpad[axis] + except TypeError: + return self._minpad + + def set_visible_axes(self, visible_axes): + self._visible_axes = self._frame._validate_positions(visible_axes) + + def get_visible_axes(self): + if self._visible_axes == "all": + return list(self._frame.keys()) + else: + return [x for x in self._visible_axes if x in self._frame or x == "#"] + + def set_minpad(self, minpad): + self._minpad = minpad + + def set_visibility_rule(self, value): + allowed = ["always", "labels", "ticks"] + if value not in allowed: + raise ValueError( + f"Axis label visibility rule must be one of {' / '.join(allowed)}" + ) + + self._visibility_rule = value + + def get_visibility_rule(self): + return self._visibility_rule + + def draw( + self, + renderer, + bboxes, + ticklabels_bbox, + coord_ticklabels_bbox, + ticks_locs, + visible_ticks, + ): + if not self.get_visible(): + return + + text_size = renderer.points_to_pixels(self.get_size()) + # Flatten the bboxes for all coords and all axes + ticklabels_bbox_list = [] + for bbcoord in ticklabels_bbox.values(): + for bbaxis in bbcoord.values(): + ticklabels_bbox_list += bbaxis + + for axis in self.get_visible_axes(): + if axis == "#": + continue + + if self.get_visibility_rule() == "ticks": + if not ticks_locs[axis]: + continue + elif self.get_visibility_rule() == "labels": + if not coord_ticklabels_bbox: + continue + + padding = text_size * self.get_minpad(axis) + + # Find position of the axis label. For now we pick the mid-point + # along the path but in future we could allow this to be a + # parameter. + x, y, normal_angle = self._frame[axis]._halfway_x_y_angle() + + label_angle = (normal_angle - 90.0) % 360.0 + if 135 < label_angle < 225: + label_angle += 180 + self.set_rotation(label_angle) + + # Find label position by looking at the bounding box of ticks' + # labels and the image. It sets the default padding at 1 times the + # axis label font size which can also be changed by setting + # the minpad parameter. + + if isinstance(self._frame, RectangularFrame): + if ( + len(ticklabels_bbox_list) > 0 + and ticklabels_bbox_list[0] is not None + ): + coord_ticklabels_bbox[axis] = [ + mtransforms.Bbox.union(ticklabels_bbox_list) + ] + else: + coord_ticklabels_bbox[axis] = [None] + + visible = ( + axis in visible_ticks and coord_ticklabels_bbox[axis][0] is not None + ) + + if axis == "l": + if visible: + x = coord_ticklabels_bbox[axis][0].xmin + x = x - padding + + elif axis == "r": + if visible: + x = coord_ticklabels_bbox[axis][0].x1 + x = x + padding + + elif axis == "b": + if visible: + y = coord_ticklabels_bbox[axis][0].ymin + y = y - padding + + elif axis == "t": + if visible: + y = coord_ticklabels_bbox[axis][0].y1 + y = y + padding + + else: # arbitrary axis + x = x + np.cos(np.radians(normal_angle)) * (padding + text_size * 1.5) + y = y + np.sin(np.radians(normal_angle)) * (padding + text_size * 1.5) + + self.set_position((x, y)) + super().draw(renderer) + + bb = super().get_window_extent(renderer) + bboxes.append(bb) diff --git a/astropy/visualization/wcsaxes/coordinate_helpers.py b/astropy/visualization/wcsaxes/coordinate_helpers.py new file mode 100644 index 000000000000..3f490a8fc835 --- /dev/null +++ b/astropy/visualization/wcsaxes/coordinate_helpers.py @@ -0,0 +1,1614 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +""" +This file defines the classes used to represent a 'coordinate', which includes +axes, ticks, tick labels, and grid lines. +""" + +import warnings + +import numpy as np +from matplotlib import rcParams +from matplotlib.patches import PathPatch +from matplotlib.path import Path +from matplotlib.transforms import Affine2D, ScaledTranslation + +from astropy import units as u +from astropy.utils.decorators import deprecated_renamed_argument +from astropy.utils.exceptions import AstropyDeprecationWarning + +from .axislabels import AxisLabels +from .formatter_locator import AngleFormatterLocator, ScalarFormatterLocator +from .frame import EllipticalFrame, RectangularFrame1D +from .grid_paths import get_gridline_path, get_lon_lat_path +from .ticklabels import TickLabels +from .ticks import Ticks + +__all__ = ["CoordinateHelper"] + +# Matplotlib's gridlines use Line2D, but ours use PathPatch. +# Patches take a slightly different format of linestyle argument. +LINES_TO_PATCHES_LINESTYLE = { + "-": "solid", + "--": "dashed", + "-.": "dashdot", + ":": "dotted", + "none": "none", + "None": "none", + " ": "none", + "": "none", +} + + +def wrap_angle_at(values, coord_wrap): + # On ARM processors, np.mod emits warnings if there are NaN values in the + # array, although this doesn't seem to happen on other processors. + with np.errstate(invalid="ignore"): + return np.mod(values - coord_wrap, 360.0) - (360.0 - coord_wrap) + + +class CoordinateHelper: + """ + Helper class to control one of the coordinates in the + :class:`~astropy.visualization.wcsaxes.WCSAxes`. + + Parameters + ---------- + parent_axes : :class:`~astropy.visualization.wcsaxes.WCSAxes` + The axes the coordinate helper belongs to. + parent_map : :class:`~astropy.visualization.wcsaxes.CoordinatesMap` + The :class:`~astropy.visualization.wcsaxes.CoordinatesMap` object this + coordinate belongs to. + transform : `~matplotlib.transforms.Transform` + The transform corresponding to this coordinate system. + coord_index : int + The index of this coordinate in the + :class:`~astropy.visualization.wcsaxes.CoordinatesMap`. + coord_type : {'longitude', 'latitude', 'scalar'} + The type of this coordinate, which is used to determine the wrapping and + boundary behavior of coordinates. Longitudes wrap at ``coord_wrap``, + latitudes have to be in the range -90 to 90, and scalars are unbounded + and do not wrap. + coord_unit : `~astropy.units.Unit` + The unit that this coordinate is in given the output of transform. + format_unit : `~astropy.units.Unit`, optional + The unit to use to display the coordinates. + coord_wrap : `astropy.units.Quantity` + The angle at which the longitude wraps (defaults to 360 degrees). + frame : `~astropy.visualization.wcsaxes.frame.BaseFrame` + The frame of the :class:`~astropy.visualization.wcsaxes.WCSAxes`. + default_label : str, optional + The axis label to show by default if none is set later. + """ + + def __init__( + self, + parent_axes=None, + parent_map=None, + transform=None, + coord_index=None, + coord_type="scalar", + coord_unit=None, + coord_wrap=None, + frame=None, + format_unit=None, + default_label=None, + ): + # Keep a reference to the parent axes and the transform + self._parent_axes = parent_axes + self._parent_map = parent_map + self._transform = transform + self._coord_index = coord_index + self._coord_unit = coord_unit + self._format_unit = format_unit + self._frame = frame + self._default_label = default_label or "" + self._auto_axislabel = True + self._axislabel_set = False + self._custom_formatter = None + + # Disable auto label for elliptical frames as it puts labels in + # annoying places. + if issubclass(self.parent_axes.frame_class, EllipticalFrame): + self._auto_axislabel = False + + self.set_coord_type(coord_type, coord_wrap) + + # Initialize ticks + self.dpi_transform = Affine2D() + self.offset_transform = ScaledTranslation(0, 0, self.dpi_transform) + self._ticks = Ticks( + frame=self.frame, transform=parent_axes.transData + self.offset_transform + ) + + # Initialize tick labels + self._ticklabels = TickLabels( + self.frame, + transform=None, # display coordinates + figure=parent_axes.get_figure(), + ) + self._ticks.display_minor_ticks(rcParams["xtick.minor.visible"]) + self._minor_frequency = 5 + + # Initialize axis labels + self._axislabels = AxisLabels( + self.frame, + transform=None, # display coordinates + figure=parent_axes.get_figure(), + ) + + # Initialize container for the grid lines + self._grid_lines = [] + + # Initialize grid style. Take defaults from matplotlib.rcParams. + # Based on matplotlib.axis.YTick._get_gridline. + self._grid_lines_kwargs = { + "visible": False, + "facecolor": "none", + "edgecolor": rcParams["grid.color"], + "linestyle": LINES_TO_PATCHES_LINESTYLE[rcParams["grid.linestyle"]], + "linewidth": rcParams["grid.linewidth"], + "alpha": rcParams["grid.alpha"], + "transform": self.parent_axes.transData, + } + + @property + def parent_axes(self): + """ + The axes the coordinate helper belongs to. + """ + return self._parent_axes + + @parent_axes.setter + def parent_axes(self, value): + warnings.warn( + "Setting CoordinateHelper.parent_axes directly is deprecated", + AstropyDeprecationWarning, + ) + self._parent_axes = value + + @property + def parent_map(self): + """ + The :class:`~astropy.visualization.wcsaxes.CoordinatesMap` object this + coordinate belongs to. + """ + return self._parent_map + + @parent_map.setter + def parent_map(self, value): + warnings.warn( + "Setting CoordinateHelper.parent_map directly is deprecated", + AstropyDeprecationWarning, + ) + self._parent_map = value + + @property + def transform(self): + """ + The transform corresponding to this coordinate system. + """ + return self._transform + + @transform.setter + def transform(self, value): + warnings.warn( + "Setting CoordinateHelper.transform directly is deprecated", + AstropyDeprecationWarning, + ) + self._transform = value + + @property + def coord_index(self): + """ + The index of this coordinate in the + :class:`~astropy.visualization.wcsaxes.CoordinatesMap`. + """ + return self._coord_index + + @coord_index.setter + def coord_index(self, value): + warnings.warn( + "Setting CoordinateHelper.coord_index directly is deprecated", + AstropyDeprecationWarning, + ) + self._coord_index = value + + @property + def coord_type(self): + """ + The type of this coordinate (e.g., ``'longitude'``) + """ + return self._coord_type + + @coord_type.setter + def coord_type(self, value): + warnings.warn( + "Setting CoordinateHelper.coord_type directly is deprecated, use CoordinateHelper.set_coord_type instead", + AstropyDeprecationWarning, + ) + self._coord_type = value + + @property + def coord_unit(self): + """ + The unit that this coordinate is in given the output of transform. + """ + return self._coord_unit + + @coord_unit.setter + def coord_unit(self, value): + warnings.warn( + "Setting CoordinateHelper.coord_unit directly is deprecated", + AstropyDeprecationWarning, + ) + self._coord_unit = value + + @property + def coord_wrap(self): + """ + The angle at which the longitude wraps (defaults to 360 degrees). + """ + return self._coord_wrap + + @coord_wrap.setter + def coord_wrap(self, value): + warnings.warn( + "Setting CoordinateHelper.coord_wrap directly is deprecated, use CoordinateHelper.set_coord_type instead", + AstropyDeprecationWarning, + ) + self._coord_wrap = value + + @property + def frame(self): + """ + The frame of the :class:`~astropy.visualization.wcsaxes.WCSAxes`. + """ + return self._frame + + @frame.setter + def frame(self, value): + warnings.warn( + "Setting CoordinateHelper.frame directly is deprecated", + AstropyDeprecationWarning, + ) + self._frame = value + + @property + def default_label(self): + """ + The axis label to show by default if none is set later. + """ + return self._default_label + + @default_label.setter + def default_label(self, value): + warnings.warn( + "Setting CoordinateHelper.default_label directly is deprecated", + AstropyDeprecationWarning, + ) + self._default_label = value + + @property + def ticks(self): + warnings.warn( + "CoordinateHelper.ticks should not be accessed directly and is deprecated", + AstropyDeprecationWarning, + ) + return self._ticks + + @ticks.setter + def ticks(self, value): + warnings.warn( + "Setting CoordinateHelper.ticks directly is deprecated", + AstropyDeprecationWarning, + ) + self._ticks = value + + @property + def ticklabels(self): + warnings.warn( + "CoordinateHelper.ticklabels should not be accessed directly and is deprecated", + AstropyDeprecationWarning, + ) + return self._ticklabels + + @ticklabels.setter + def ticklabels(self, value): + warnings.warn( + "Setting CoordinateHelper.ticklabels directly is deprecated", + AstropyDeprecationWarning, + ) + self._ticklabels = value + + @property + def axislabels(self): + warnings.warn( + "CoordinateHelper.axislabels should not be accessed directly and is deprecated", + AstropyDeprecationWarning, + ) + return self._axislabels + + @axislabels.setter + def axislabels(self, value): + warnings.warn( + "Setting CoordinateHelper.axislabels directly is deprecated", + AstropyDeprecationWarning, + ) + self._axislabels = value + + def grid(self, draw_grid=True, grid_type=None, **kwargs): + """ + Plot grid lines for this coordinate. + + Standard matplotlib appearance options (color, alpha, etc.) can be + passed as keyword arguments. + + Parameters + ---------- + draw_grid : bool + Whether to show the gridlines + grid_type : {'lines', 'contours'} + Whether to plot the contours by determining the grid lines in + world coordinates and then plotting them in world coordinates + (``'lines'``) or by determining the world coordinates at many + positions in the image and then drawing contours + (``'contours'``). The first is recommended for 2-d images, while + for 3-d (or higher dimensional) cubes, the ``'contours'`` option + is recommended. By default, 'lines' is used if the transform has + an inverse, otherwise 'contours' is used. + """ + if grid_type == "lines" and not self.transform.has_inverse: + raise ValueError( + "The specified transform has no inverse, so the " + "grid cannot be drawn using grid_type='lines'" + ) + + if grid_type is None: + grid_type = "lines" if self.transform.has_inverse else "contours" + + if grid_type in ("lines", "contours"): + self._grid_type = grid_type + else: + raise ValueError("grid_type should be 'lines' or 'contours'") + + if "color" in kwargs: + kwargs["edgecolor"] = kwargs.pop("color") + + self._grid_lines_kwargs.update(kwargs) + + if draw_grid is None: + draw_grid = True + self._grid_lines_kwargs["visible"] = draw_grid + + def set_coord_type(self, coord_type, coord_wrap=None): + """ + Set the coordinate type for the axis. + + Parameters + ---------- + coord_type : str + One of 'longitude', 'latitude' or 'scalar' + coord_wrap : `~astropy.units.Quantity`, optional + The value to wrap at for angular coordinates. + """ + self._coord_type = coord_type + + if coord_wrap is not None and not isinstance(coord_wrap, u.Quantity): + warnings.warn( + "Passing 'coord_wrap' as a number is deprecated. Use a Quantity with units convertible to angular degrees instead.", + AstropyDeprecationWarning, + ) + coord_wrap = coord_wrap * u.deg + + if coord_type == "longitude" and coord_wrap is None: + self._coord_wrap = 360 * u.deg + elif coord_type != "longitude" and coord_wrap is not None: + raise NotImplementedError( + "coord_wrap is not yet supported for non-longitude coordinates" + ) + else: + self._coord_wrap = coord_wrap + + # Initialize tick formatter/locator + if coord_type == "scalar": + self._coord_scale_to_deg = None + self._formatter_locator = ScalarFormatterLocator(unit=self.coord_unit) + elif coord_type in ["longitude", "latitude"]: + if self.coord_unit is u.deg: + self._coord_scale_to_deg = None + else: + self._coord_scale_to_deg = self.coord_unit.to(u.deg) + self._formatter_locator = AngleFormatterLocator( + unit=self.coord_unit, format_unit=self._format_unit + ) + else: + raise ValueError( + "coord_type should be one of 'scalar', 'longitude', or 'latitude'" + ) + + def set_major_formatter(self, formatter, show_decimal_unit=True): + """ + Set the format string to use for the major tick labels. + + See :ref:`tick_label_format` for accepted format strings and examples. + + Parameters + ---------- + formatter : str or callable + The format string to use, or a callable (for advanced use cases). + If specified as a callable, this should take a + `~astropy.units.Quantity` (which could be scalar or array) of tick + world coordinates as well as an optional ``spacing`` keyword + argument, which gives (also as a `~astropy.units.Quantity`) the + spacing between ticks, and returns an iterable of strings + containing the labels. + show_decimal_unit : str + Whether to show the unit or not when using decimal formatting (e.g., + ``d.dd`` or ``x.xxx``). + """ + if callable(formatter): + self._custom_formatter = formatter + elif isinstance(formatter, str): + self._formatter_locator.format = formatter + self._custom_formatter = None + else: + raise TypeError("formatter should be a string") + + self._formatter_locator.show_decimal_unit = show_decimal_unit + + def format_coord(self, value, format="auto"): + """ + Given the value of a coordinate, will format it according to the + format of the formatter_locator. + + Parameters + ---------- + value : float + The value to format + format : {'auto', 'ascii', 'latex'}, optional + The format to use - by default the formatting will be adjusted + depending on whether Matplotlib is using LaTeX or MathTex. To + get plain ASCII strings, use format='ascii'. + """ + if not hasattr(self, "_fl_spacing"): + return "" # _update_ticks has not been called yet + + fl = self._formatter_locator + if isinstance(fl, AngleFormatterLocator): + # Convert to degrees if needed + if self._coord_scale_to_deg is not None: + value *= self._coord_scale_to_deg + + if self.coord_type == "longitude": + value = wrap_angle_at(value, self.coord_wrap.to_value(u.deg)) + value = value * u.degree + value = value.to_value(fl._unit) + + spacing = self._fl_spacing + + string = self.formatter( + values=[value] * fl._unit, spacing=spacing, format=format + ) + + return string[0] + + def set_separator(self, separator): + """ + Set the separator to use for the angle major tick labels. + + Parameters + ---------- + separator : str or tuple or None + The separator between numbers in sexagesimal representation. Can be + either a string or a tuple (or `None` for default). + """ + if not (self._formatter_locator.__class__ == AngleFormatterLocator): + raise TypeError("Separator can only be specified for angle coordinates") + if isinstance(separator, (str, tuple)) or separator is None: + self._formatter_locator.sep = separator + else: + raise TypeError("separator should be a string, a tuple, or None") + + def set_format_unit(self, unit, decimal=None, show_decimal_unit=True): + """ + Set the unit for the major tick labels. + + Parameters + ---------- + unit : class:`~astropy.units.Unit` + The unit to which the tick labels should be converted to. + decimal : bool, optional + Whether to use decimal formatting. By default this is `False` + for degrees or hours (which therefore use sexagesimal formatting) + and `True` for all other units. + show_decimal_unit : bool, optional + Whether to include units when in decimal mode. + """ + self._formatter_locator.format_unit = u.Unit(unit) + self._formatter_locator.decimal = decimal + self._formatter_locator.show_decimal_unit = show_decimal_unit + + def get_format_unit(self): + """ + Get the unit for the major tick labels. + """ + return self._formatter_locator.format_unit + + def set_ticks( + self, + values=None, + spacing=None, + number=None, + size=None, + width=None, + color=None, + alpha=None, + direction=None, + exclude_overlapping=None, + ): + """ + Set the location and properties of the ticks. + + At most one of the options from ``values``, ``spacing``, or + ``number`` can be specified. + + Parameters + ---------- + values : iterable, optional + The coordinate values at which to show the ticks. + spacing : float, optional + The spacing between ticks. + number : float, optional + The approximate number of ticks shown. + size : float, optional + The length of the ticks in points + color : str or tuple, optional + A valid Matplotlib color for the ticks + alpha : float, optional + The alpha value (transparency) for the ticks. + direction : {'in','out'}, optional + Whether the ticks should point inwards or outwards. + """ + if sum([values is None, spacing is None, number is None]) < 2: + raise ValueError( + "At most one of values, spacing, or number should be specified" + ) + + if values is not None: + self._formatter_locator.values = values + elif spacing is not None: + self._formatter_locator.spacing = spacing + elif number is not None: + self._formatter_locator.number = number + + if size is not None: + self._ticks.set_ticksize(size) + + if width is not None: + self._ticks.set_linewidth(width) + + if color is not None: + self._ticks.set_color(color) + + if alpha is not None: + self._ticks.set_alpha(alpha) + + if direction is not None: + if direction in ("in", "out"): + self._ticks.set_tick_out(direction == "out") + else: + raise ValueError("direction should be 'in' or 'out'") + + if exclude_overlapping is not None: + warnings.warn( + "exclude_overlapping= should be passed to " + "set_ticklabel instead of set_ticks", + AstropyDeprecationWarning, + ) + self._ticklabels.set_exclude_overlapping(exclude_overlapping) + + def set_ticks_position(self, position): + """ + Set where ticks should appear. + + Parameters + ---------- + position : str or list + The axes on which the ticks for this coordinate should appear. + Should be a sequence containing zero or more of ``'b'``, ``'t'``, + ``'l'``, ``'r'``. For example, ``'lb'`` will lead the ticks to be + shown on the left and bottom axis. In addition, if ``'#'`` is + included in the sequence, the position will be considered dynamic and + will be updated at draw-time in order to show the ticks on the same + axes as the tick labels are shown. + """ + self._ticks.set_visible_axes(position) + + def get_ticks_position(self): + """ + Get where tick labels will appear. + """ + return list(self._ticks.get_visible_axes()) + + def set_ticks_visible(self, visible): + """ + Set whether ticks are visible or not. + + Parameters + ---------- + visible : bool + The visibility of ticks. Setting as ``False`` will hide ticks + along this coordinate. + """ + self._ticks.set_visible(visible) + + def get_ticks_visible(self): + """ + Get whether the ticks are currently visible. + """ + return self._ticks.get_visible() + + def set_ticklabel( + self, + color=None, + size=None, + pad=None, + exclude_overlapping=None, + *, + simplify=True, + **kwargs, + ): + """ + Set the visual properties for the tick labels. + + Parameters + ---------- + size : float, optional + The size of the ticks labels in points + color : str or tuple, optional + A valid Matplotlib color for the tick labels + pad : float, optional + Distance in points between tick and label. + exclude_overlapping : bool, optional + Whether to exclude tick labels that overlap over each other. + simplify : bool, optional + Whether to remove repeated parts of tick labels. + **kwargs + Other keyword arguments are passed to :class:`matplotlib.text.Text`. + """ + if size is not None: + self._ticklabels.set_size(size) + if color is not None: + self._ticklabels.set_color(color) + if pad is not None: + self._ticklabels.set_pad(pad) + if exclude_overlapping is not None: + self._ticklabels.set_exclude_overlapping(exclude_overlapping) + self._ticklabels.set_simplify(simplify) + self._ticklabels.set(**kwargs) + + def get_ticklabel_visible(self): + """ + Get whether the tick labels are currently visible. + """ + return self._ticklabels.get_visible() + + def set_ticklabel_position(self, position): + """ + Set where tick labels should appear. + + Parameters + ---------- + position : str or list + The axes on which the tick labels for this coordinate should + appear. Should be a sequence containing zero or more of ``'b'``, + ``'t'``, ``'l'``, ``'r'``. For example, ``'lb'`` will lead the + tick labels to be shown on the left and bottom axis. In addition, + if ``'#'`` is included in the sequence, the position will be + considered dynamic and will be updated at draw-time in order to + attempt to optimize the layout of all the coordinates. + """ + self._ticklabels.set_visible_axes(position) + + def get_ticklabel_position(self): + """ + Get where tick labels will appear. + """ + return list(self._ticklabels.get_visible_axes()) + + def set_ticklabel_visible(self, visible): + """ + Set whether the tick labels are visible or not. + + Parameters + ---------- + visible : bool + The visibility of ticks. Setting as ``False`` will hide this + coordinate's tick labels. + """ + self._ticklabels.set_visible(visible) + + def set_axislabel(self, text, minpad=1, **kwargs): + """ + Set the text and optionally visual properties for the axis label. + + Parameters + ---------- + text : str + The axis label text. + minpad : float, optional + The padding for the label in terms of axis label font size. + **kwargs + Keywords are passed to :class:`matplotlib.text.Text`. These + can include keywords to set the ``color``, ``size``, ``weight``, and + other text properties. + """ + fontdict = kwargs.pop("fontdict", None) + + # NOTE: When using plt.xlabel/plt.ylabel, minpad can get set explicitly + # to None so we need to make sure that in that case we change to a + # default numerical value. + if minpad is None: + minpad = 1 + + self._axislabel_set = True + + self._axislabels.set_text(text) + self._axislabels.set_minpad(minpad) + self._axislabels.set(**kwargs) + + if fontdict is not None: + self._axislabels.update(fontdict) + + def get_axislabel(self): + """ + Get the text for the axis label. + + Returns + ------- + label : str + The axis label + """ + if self._auto_axislabel and not self._axislabel_set: + return self._get_default_axislabel() + else: + return self._axislabels.get_text() + + def get_axislabel_visible(self): + """ + Get whether the axis label is currently visible. + """ + return self._axislabels.get_visible() + + def set_auto_axislabel(self, auto_label): + """ + Render default axis labels if no explicit label is provided. + + Parameters + ---------- + auto_label : `bool` + `True` if default labels will be rendered. + """ + self._auto_axislabel = bool(auto_label) + + def get_auto_axislabel(self): + """ + Render default axis labels if no explicit label is provided. + + Returns + ------- + auto_axislabel : `bool` + `True` if default labels will be rendered. + """ + return self._auto_axislabel + + def _get_default_axislabel(self): + unit = self.get_format_unit() or self.coord_unit + + if not unit or unit is u.one or self.coord_type in ("longitude", "latitude"): + return f"{self.default_label}" + else: + return f"{self.default_label} [{unit:latex}]" + + def set_axislabel_position(self, position): + """ + Set where axis labels should appear. + + Parameters + ---------- + position : str or list + The axes on which the axis label for this coordinate should + appear. Should be a sequence containing zero or more of ``'b'``, + ``'t'``, ``'l'``, ``'r'``. For example, ``'lb'`` will lead the + axis label to be shown on the left and bottom axis. In addition, if + ``'#'`` is included in the sequence, the position will be considered + dynamic and will be updated at draw-time in order to show the axis + label on the same axes as the tick labels are shown. + """ + self._axislabels.set_visible_axes(position) + + def get_axislabel_position(self): + """ + Get where axis labels will appear. + """ + return list(self._axislabels.get_visible_axes()) + + def set_axislabel_visibility_rule(self, rule): + """ + Set the rule used to determine when the axis label is drawn. + + Parameters + ---------- + rule : str + If the rule is 'always' axis labels will always be drawn on the + axis. If the rule is 'ticks' the label will only be drawn if ticks + were drawn on that axis. If the rule is 'labels' the axis label + will only be drawn if tick labels were drawn on that axis. + """ + self._axislabels.set_visibility_rule(rule) + + def set_visible(self, visible): + """ + Set the visibility for ticks, tick labels, and axis labels. + + Parameters + ---------- + visible : bool + If 'True', show all elements. + If 'False', hide all elements. + """ + if isinstance(visible, bool): + self.set_ticks_visible(visible) + self.set_ticklabel_visible(visible) + self._axislabels.set_visible(visible) + else: + raise TypeError("visible must be a boolean") + + def set_position(self, position): + """ + Set the position for ticks, tick labels, and axis labels. + + Parameters + ---------- + position : str + Show all elements at the given position string (e.g. 't', 'lb', '#'). + """ + if isinstance(position, str): + self.set_ticks_position(position) + self.set_ticklabel_position(position) + self.set_axislabel_position(position) + + self.set_ticks_visible(True) + self.set_ticklabel_visible(True) + self._axislabels.set_visible(True) + else: + raise TypeError("position must be a string") + + @deprecated_renamed_argument("rule", None, "7.2.0") + def get_axislabel_visibility_rule(self, rule): + """ + Get the rule used to determine when the axis label is drawn. + """ + return self._axislabels.get_visibility_rule() + + @property + def locator(self): + return self._formatter_locator.locator + + @property + def formatter(self): + return self._custom_formatter or self._formatter_locator.formatter + + def _draw_grid(self, renderer): + renderer.open_group("grid lines") + + if self._grid_lines_kwargs["visible"]: + if isinstance(self.frame, RectangularFrame1D): + self._update_grid_lines_1d() + else: + if self._grid_type == "lines": + self._update_grid_lines() + else: + self._update_grid_contour() + + if self._grid_type == "lines": + frame_patch = self.frame.patch + for path in self._grid_lines: + p = PathPatch(path, **self._grid_lines_kwargs) + p.set_clip_path(frame_patch) + p.draw(renderer) + + elif self._grid is not None: + self._grid.set(**self._grid_lines_kwargs) + self._grid.draw(renderer) + + renderer.close_group("grid lines") + + def _draw_ticks(self, renderer, existing_bboxes): + """ + Draw all ticks and ticklabels. + + Parameters + ---------- + existing_bboxes : list[Bbox] + All bboxes for ticks that have already been drawn by other + coordinates. + """ + renderer.open_group("ticks") + self._ticks.draw(renderer) + + self._ticklabels._tick_out_size = self._ticks.out_size + + self._ticklabels._set_existing_bboxes(existing_bboxes) + self._ticklabels.draw(renderer) + + renderer.close_group("ticks") + + def _draw_axislabels(self, renderer, bboxes, ticklabels_bbox, visible_ticks): + # Render the default axis label if no axis label is set. + if self._auto_axislabel and not self._axislabel_set: + self.set_axislabel(self._get_default_axislabel()) + + renderer.open_group("axis labels") + + self._axislabels.draw( + renderer, + bboxes=bboxes, + ticklabels_bbox=ticklabels_bbox, + coord_ticklabels_bbox=ticklabels_bbox[self], + ticks_locs=self._ticks.ticks_locs, + visible_ticks=visible_ticks, + ) + + renderer.close_group("axis labels") + + def _update_ticks(self): + if self.coord_index is None: + return + + # TODO: this method should be optimized for speed + + # Here we determine the location and rotation of all the ticks. For + # each axis, we can check the intersections for the specific + # coordinate and once we have the tick positions, we can use the WCS + # to determine the rotations. + + coord_range = self.parent_map._coord_range + + # First find the ticks we want to show + tick_world_coordinates, self._fl_spacing = self.locator( + *coord_range[self.coord_index] + ) + + if self._ticks.get_display_minor_ticks(): + minor_ticks_w_coordinates = self._formatter_locator.minor_locator( + self._fl_spacing, + self.get_minor_frequency(), + *coord_range[self.coord_index], + ) + + # We want to allow non-standard rectangular frames, so we just rely on + # the parent axes to tell us what the bounding frame is. + from . import conf + + frame = self.frame.sample(conf.frame_boundary_samples) + + self._ticks.clear() + self._ticklabels.clear() + self._lblinfo = [] + self._lbl_world = [] + # Look up parent axes' transform from data to figure coordinates. + # + # See: + # https://matplotlib.org/stable/tutorials/advanced/transforms_tutorial.html#the-transformation-pipeline + transData = self.parent_axes.transData + invertedTransLimits = transData.inverted() + + for axis, spine in frame.items(): + if spine.data.size == 0: + continue + + if not isinstance(self.frame, RectangularFrame1D): + # Determine tick rotation in display coordinates and compare to + # the normal angle in display coordinates. + + pixel0 = spine.data + world0 = spine.world[:, self.coord_index] + if np.isnan(world0).all(): + continue + axes0 = transData.transform(pixel0) + + # Advance 2 pixels in figure coordinates + pixel1 = axes0.copy() + pixel1[:, 0] += 2.0 + pixel1 = invertedTransLimits.transform(pixel1) + with np.errstate(invalid="ignore"): + world1 = self.transform.transform(pixel1)[:, self.coord_index] + + # Advance 2 pixels in figure coordinates + pixel2 = axes0.copy() + pixel2[:, 1] += 2.0 if self.frame.origin == "lower" else -2.0 + pixel2 = invertedTransLimits.transform(pixel2) + with np.errstate(invalid="ignore"): + world2 = self.transform.transform(pixel2)[:, self.coord_index] + + dx = world1 - world0 + dy = world2 - world0 + + # Rotate by 90 degrees + dx, dy = -dy, dx + + if self.coord_type == "longitude": + if self._coord_scale_to_deg is not None: + dx *= self._coord_scale_to_deg + dy *= self._coord_scale_to_deg + + # Here we wrap at 180 not self.coord_wrap since we want to + # always ensure abs(dx) < 180 and abs(dy) < 180 + dx = wrap_angle_at(dx, 180.0) + dy = wrap_angle_at(dy, 180.0) + + tick_angle = np.degrees(np.arctan2(dy, dx)) + + normal_angle_full = np.hstack( + [spine.normal_angle, spine.normal_angle[-1]] + ) + with np.errstate(invalid="ignore"): + reset = ((normal_angle_full - tick_angle) % 360 > 90.0) & ( + (tick_angle - normal_angle_full) % 360 > 90.0 + ) + tick_angle[reset] -= 180.0 + + else: + rotation = 90 if axis == "b" else -90 + tick_angle = np.zeros((conf.frame_boundary_samples,)) + rotation + + # We find for each interval the starting and ending coordinate, + # ensuring that we take wrapping into account correctly for + # longitudes. + w1 = spine.world[:-1, self.coord_index] + w2 = spine.world[1:, self.coord_index] + + if self.coord_type == "longitude": + if self._coord_scale_to_deg is not None: + w1 = w1 * self._coord_scale_to_deg + w2 = w2 * self._coord_scale_to_deg + + w1 = wrap_angle_at(w1, self.coord_wrap.to_value(u.deg)) + w2 = wrap_angle_at(w2, self.coord_wrap.to_value(u.deg)) + with np.errstate(invalid="ignore"): + w1[w2 - w1 > 180.0] += 360 + w2[w1 - w2 > 180.0] += 360 + + if self._coord_scale_to_deg is not None: + w1 = w1 / self._coord_scale_to_deg + w2 = w2 / self._coord_scale_to_deg + + # For longitudes, we need to check ticks as well as ticks + 360, + # since the above can produce pairs such as 359 to 361 or 0.5 to + # 1.5, both of which would match a tick at 0.75. Otherwise we just + # check the ticks determined above. + self._compute_ticks(tick_world_coordinates, spine, axis, w1, w2, tick_angle) + + if self._ticks.get_display_minor_ticks(): + self._compute_ticks( + minor_ticks_w_coordinates, + spine, + axis, + w1, + w2, + tick_angle, + ticks="minor", + ) + + # format tick labels, add to scene + text = self.formatter(u.Quantity(self._lbl_world), spacing=self._fl_spacing) + + for kwargs, txt in zip(self._lblinfo, text): + self._ticklabels.add(text=txt, **kwargs) + + def _compute_ticks( + self, tick_world_coordinates, spine, axis, w1, w2, tick_angle, ticks="major" + ): + if self.coord_type == "longitude": + tick_world_coordinates_values = tick_world_coordinates.to_value(u.deg) + tick_world_coordinates_values = np.hstack( + [tick_world_coordinates_values, tick_world_coordinates_values + 360] + ) + tick_world_coordinates_values *= u.deg.to(self.coord_unit) + else: + tick_world_coordinates_values = tick_world_coordinates.to_value( + self.coord_unit + ) + + for t in tick_world_coordinates_values: + # Find steps where a tick is present. We have to check + # separately for the case where the tick falls exactly on the + # frame points, otherwise we'll get two matches, one for w1 and + # one for w2. + with np.errstate(invalid="ignore"): + intersections = np.hstack( + [ + np.nonzero((t - w1) == 0)[0], + np.nonzero(((t - w1) * (t - w2)) < 0)[0], + ] + ) + + # But we also need to check for intersection with the last w2 + if t - w2[-1] == 0: + intersections = np.append(intersections, len(w2) - 1) + + # Loop over ticks, and find exact pixel coordinates by linear + # interpolation + for imin in intersections: + imax = imin + 1 + + if np.allclose(w1[imin], w2[imin], rtol=1.0e-13, atol=1.0e-13): + continue # tick is exactly aligned with frame + else: + frac = (t - w1[imin]) / (w2[imin] - w1[imin]) + x_data_i = spine.data[imin, 0] + frac * ( + spine.data[imax, 0] - spine.data[imin, 0] + ) + y_data_i = spine.data[imin, 1] + frac * ( + spine.data[imax, 1] - spine.data[imin, 1] + ) + delta_angle = tick_angle[imax] - tick_angle[imin] + if delta_angle > 180.0: + delta_angle -= 360.0 + elif delta_angle < -180.0: + delta_angle += 360.0 + angle_i = tick_angle[imin] + frac * delta_angle + + if self.coord_type == "longitude": + if self._coord_scale_to_deg is not None: + t *= self._coord_scale_to_deg + + world = wrap_angle_at(t, self.coord_wrap.to_value(u.deg)) + + if self._coord_scale_to_deg is not None: + world /= self._coord_scale_to_deg + + else: + world = t + + if ticks == "major": + self._ticks.add( + axis=axis, + pixel=(x_data_i, y_data_i), + world=world, + angle=angle_i, + axis_displacement=imin + frac, + ) + + # store information to pass to ticklabels.add + # it's faster to format many ticklabels at once outside + # of the loop + self._lblinfo.append( + dict( + axis=axis, + data=(x_data_i, y_data_i), + world=world, + angle=spine.normal_angle[imin], + axis_displacement=imin + frac, + ) + ) + self._lbl_world.append( + (world * self.coord_unit).to(tick_world_coordinates.unit) + ) + + else: + self._ticks.add_minor( + minor_axis=axis, + minor_pixel=(x_data_i, y_data_i), + minor_world=world, + minor_angle=angle_i, + minor_axis_displacement=imin + frac, + ) + + def display_minor_ticks(self, display_minor_ticks): + """ + Display minor ticks for this coordinate. + + Parameters + ---------- + display_minor_ticks : bool + Whether or not to display minor ticks. + """ + self._ticks.display_minor_ticks(display_minor_ticks) + + def get_minor_frequency(self): + return self._minor_frequency + + def set_minor_frequency(self, frequency): + """ + Set the frequency of minor ticks per major ticks. + + Parameters + ---------- + frequency : int + The number of minor ticks per major ticks. + """ + self._minor_frequency = frequency + + def _update_grid_lines_1d(self): + if self.coord_index is None: + return + + x_ticks_pos = [a[0] for a in self._ticks.pixel["b"]] + + ymin, ymax = self.parent_axes.get_ylim() + + self._grid_lines = [] + for x_coord in x_ticks_pos: + pixel = [[x_coord, ymin], [x_coord, ymax]] + self._grid_lines.append(Path(pixel)) + + def _update_grid_lines(self): + # For 3-d WCS with a correlated third axis, the *proper* way of + # drawing a grid should be to find the world coordinates of all pixels + # and drawing contours. What we are doing here assumes that we can + # define the grid lines with just two of the coordinates (and + # therefore assumes that the other coordinates are fixed and set to + # the value in the slice). Here we basically assume that if the WCS + # had a third axis, it has been abstracted away in the transformation. + + if self.coord_index is None: + return + + coord_range = self.parent_map._coord_range + + tick_world_coordinates, spacing = self.locator(*coord_range[self.coord_index]) + tick_world_coordinates_values = tick_world_coordinates.to_value(self.coord_unit) + + n_coord = len(tick_world_coordinates_values) + if n_coord == 0: + return + + from . import conf + + n_samples = conf.grid_samples + + xy_world = np.zeros((n_samples * n_coord, 2)) + + self._grid_lines = [] + + for iw, w in enumerate(tick_world_coordinates_values): + subset = slice(iw * n_samples, (iw + 1) * n_samples) + if self.coord_index == 0: + xy_world[subset, 0] = np.repeat(w, n_samples) + xy_world[subset, 1] = np.linspace( + coord_range[1][0], coord_range[1][1], n_samples + ) + else: + xy_world[subset, 0] = np.linspace( + coord_range[0][0], coord_range[0][1], n_samples + ) + xy_world[subset, 1] = np.repeat(w, n_samples) + + # We now convert all the world coordinates to pixel coordinates in a + # single go rather than doing this in the gridline to path conversion + # to fully benefit from vectorized coordinate transformations. + + # Transform line to pixel coordinates + pixel = self.transform.inverted().transform(xy_world) + + # Create round-tripped values for checking + xy_world_round = self.transform.transform(pixel) + + for iw in range(n_coord): + subset = slice(iw * n_samples, (iw + 1) * n_samples) + self._grid_lines.append( + self._get_gridline( + xy_world[subset], pixel[subset], xy_world_round[subset] + ) + ) + + def add_tickable_gridline(self, name, constant): + """ + Define a gridline that can be used for ticks and labels. + + This gridline is not itself drawn, but instead can be specified in calls to + methods such as + :meth:`~astropy.visualization.wcsaxes.coordinate_helpers.CoordinateHelper.set_ticklabel_position` + for drawing ticks and labels. Since the gridline has a constant value in this + coordinate, and thus would not have any ticks or labels for the same coordinate, + the call to + :meth:`~astropy.visualization.wcsaxes.coordinate_helpers.CoordinateHelper.set_ticklabel_position` + would typically be made on the complementary coordinate. + + Parameters + ---------- + name : str + The name for the gridline, usually a single character, but can be longer + constant : `~astropy.units.Quantity` + The constant coordinate value of the gridline + + Notes + ----- + A limitation is that the tickable part of the gridline must be contiguous. If + the gridline consists of more than one disconnected segment within the plot + extent, only one of those segments will be made tickable. + """ + if self.coord_index is None: + return + + if name in self.frame: + raise ValueError(f"The frame already has a spine with the name '{name}'") + + coord_range = self.parent_map.get_coord_range() + constant = constant.to_value(self.coord_unit) + + from . import conf + + n_samples = conf.grid_samples + + # See comment in _update_grid_lines() about a WCS with more than 2 axes + + xy_world = np.zeros((n_samples, 2)) + xy_world[:, self.coord_index] = np.repeat(constant, n_samples) + + # If the complementary coordinate is longitude, we attempt to close the gridline + # If such closure is a discontinuity, it will be filtered out later + if self.parent_map[1 - self.coord_index].coord_type == "longitude": + xy_world[:-1, 1 - self.coord_index] = np.linspace( + coord_range[1 - self.coord_index][0], + coord_range[1 - self.coord_index][1], + n_samples - 1, + ) + xy_world[-1, 1 - self.coord_index] = coord_range[1 - self.coord_index][0] + else: + xy_world[:, 1 - self.coord_index] = np.linspace( + coord_range[1 - self.coord_index][0], + coord_range[1 - self.coord_index][1], + n_samples, + ) + + # Transform line to pixel coordinates + pixel = self.transform.inverted().transform(xy_world) + + # Create round-tripped values for checking + xy_world_round = self.transform.transform(pixel) + + # Get the path of the gridline, which masks hidden parts + gridline = self._get_gridline(xy_world, pixel, xy_world_round) + + def data_for_spine(spine): + vertices = gridline.vertices.copy() + codes = gridline.codes.copy() + + # Retain the parts of the gridline within the rectangular plot bounds. + # We ought to use the potentially non-rectangular plot frame, but + # calculating that patch requires updating all spines first, which is a + # catch-22. + xmin, xmax = spine.parent_axes.get_xlim() + ymin, ymax = spine.parent_axes.get_ylim() + keep = ( + (vertices[:, 0] >= xmin) + & (vertices[:, 0] <= xmax) + & (vertices[:, 1] >= ymin) + & (vertices[:, 1] <= ymax) + ) + codes[~keep] = Path.MOVETO + codes[1:][~keep[:-1]] = Path.MOVETO + + # We isolate the last segment (the last run of LINETOs), which must be preceded + # by at least one MOVETO and may be succeeded by MOVETOs. + # We have to account for longitude wrapping as well. + + # Bail out if there is no visible segment + lineto = np.flatnonzero(codes == Path.LINETO) + if np.size(lineto) == 0: + return np.zeros((0, 2)) + + # Find the start of the last segment (the last MOVETO before the LINETOs) + last_segment = np.flatnonzero(codes[: lineto[-1]] == Path.MOVETO)[-1] + + # Double the gridline if it is closed (i.e., spans all longitudes) + if vertices[0, 0] == vertices[-1, 0] and vertices[0, 1] == vertices[-1, 1]: + codes = np.concatenate([codes, codes[1:]]) + vertices = np.vstack([vertices, vertices[1:, :]]) + + # Stop the last segment before any trailing MOVETOs + moveto = np.flatnonzero(codes[last_segment + 1 :] == Path.MOVETO) + if np.size(moveto) > 0: + return vertices[last_segment : last_segment + moveto[0] + 1, :] + else: + return vertices[last_segment:n_samples, :] + + self.frame[name] = self.frame.spine_class( + self.frame.parent_axes, self.frame.transform, data_func=data_for_spine + ) + + def _get_gridline(self, xy_world, pixel, xy_world_round): + if self.coord_type == "scalar": + return get_gridline_path(xy_world, pixel) + else: + return get_lon_lat_path(xy_world, pixel, xy_world_round) + + def _clear_grid_contour(self): + if hasattr(self, "_grid") and self._grid: + self._grid.remove() + + def _update_grid_contour(self): + if self.coord_index is None: + return + + xmin, xmax = self.parent_axes.get_xlim() + ymin, ymax = self.parent_axes.get_ylim() + + from . import conf + + res = conf.contour_grid_samples + + x, y = np.meshgrid(np.linspace(xmin, xmax, res), np.linspace(ymin, ymax, res)) + pixel = np.array([x.ravel(), y.ravel()]).T + world = self.transform.transform(pixel) + field = world[:, self.coord_index].reshape(res, res).T + + coord_range = self.parent_map._coord_range + + tick_world_coordinates, spacing = self.locator(*coord_range[self.coord_index]) + + # tick_world_coordinates is a Quantities array and we only needs its values + tick_world_coordinates_values = tick_world_coordinates.value + + if self.coord_type == "longitude": + # Find biggest gap in tick_world_coordinates and wrap in middle + # For now just assume spacing is equal, so any mid-point will do + mid = 0.5 * ( + tick_world_coordinates_values[0] + tick_world_coordinates_values[1] + ) + field = wrap_angle_at(field, mid) + tick_world_coordinates_values = wrap_angle_at( + tick_world_coordinates_values, mid + ) + + # Replace wraps by NaN + with np.errstate(invalid="ignore"): + reset = (np.abs(np.diff(field[:, :-1], axis=0)) > 180) | ( + np.abs(np.diff(field[:-1, :], axis=1)) > 180 + ) + field[:-1, :-1][reset] = np.nan + field[1:, :-1][reset] = np.nan + field[:-1, 1:][reset] = np.nan + field[1:, 1:][reset] = np.nan + + if len(tick_world_coordinates_values) > 0: + with np.errstate(invalid="ignore"): + self._grid = self.parent_axes.contour( + x, + y, + field.transpose(), + levels=np.sort(tick_world_coordinates_values), + ) + else: + self._grid = None + + def tick_params(self, which="both", **kwargs): + """ + Method to set the tick and tick label parameters in the same way as the + :meth:`~matplotlib.axes.Axes.tick_params` method in Matplotlib. + + This is provided for convenience, but the recommended API is to use + :meth:`~astropy.visualization.wcsaxes.CoordinateHelper.set_ticks`, + :meth:`~astropy.visualization.wcsaxes.CoordinateHelper.set_ticklabel`, + :meth:`~astropy.visualization.wcsaxes.CoordinateHelper.set_ticks_position`, + :meth:`~astropy.visualization.wcsaxes.CoordinateHelper.set_ticklabel_position`, + and :meth:`~astropy.visualization.wcsaxes.CoordinateHelper.grid`. + + Parameters + ---------- + which : {'both', 'major', 'minor'}, optional + Which ticks to apply the settings to. By default, setting are + applied to both major and minor ticks. Note that if ``'minor'`` is + specified, only the length of the ticks can be set currently. + direction : {'in', 'out'}, optional + Puts ticks inside the axes, or outside the axes. + length : float, optional + Tick length in points. + width : float, optional + Tick width in points. + color : color, optional + Tick color (accepts any valid Matplotlib color) + pad : float, optional + Distance in points between tick and label. + labelsize : float or str, optional + Tick label font size in points or as a string (e.g., 'large'). + labelcolor : color, optional + Tick label color (accepts any valid Matplotlib color) + colors : color, optional + Changes the tick color and the label color to the same value + (accepts any valid Matplotlib color). + bottom, top, left, right : bool, optional + Where to draw the ticks. Note that this will not work correctly if + the frame is not rectangular. + labelbottom, labeltop, labelleft, labelright : bool, optional + Where to draw the tick labels. Note that this will not work + correctly if the frame is not rectangular. + grid_color : color, optional + The color of the grid lines (accepts any valid Matplotlib color). + grid_alpha : float, optional + Transparency of grid lines: 0 (transparent) to 1 (opaque). + grid_linewidth : float, optional + Width of grid lines in points. + grid_linestyle : str, optional + The style of the grid lines (accepts any valid Matplotlib line + style). + """ + # First do some sanity checking on the keyword arguments + + # colors= is a fallback default for color and labelcolor + if "colors" in kwargs: + if "color" not in kwargs: + kwargs["color"] = kwargs["colors"] + if "labelcolor" not in kwargs: + kwargs["labelcolor"] = kwargs["colors"] + + # The only property that can be set *specifically* for minor ticks is + # the length. In future we could consider having a separate Ticks instance + # for minor ticks so that e.g. the color can be set separately. + if which == "minor": + if len(set(kwargs) - {"length"}) > 0: + raise ValueError( + "When setting which='minor', the only " + "property that can be set at the moment is " + "'length' (the minor tick length)" + ) + else: + if "length" in kwargs: + self._ticks.set_minor_ticksize(kwargs["length"]) + return + + # At this point, we can now ignore the 'which' argument. + + # Set the tick arguments + self.set_ticks( + size=kwargs.get("length"), + width=kwargs.get("width"), + color=kwargs.get("color"), + direction=kwargs.get("direction"), + ) + + # Set the tick position + position = None + for arg in ("bottom", "left", "top", "right"): + if arg in kwargs and position is None: + position = "" + if kwargs.get(arg): + position += arg[0] + if position is not None: + self.set_ticks_position(position) + + # Set the tick label arguments. + self.set_ticklabel( + color=kwargs.get("labelcolor"), + size=kwargs.get("labelsize"), + pad=kwargs.get("pad"), + ) + + # Set the tick label position + position = None + for arg in ("bottom", "left", "top", "right"): + if "label" + arg in kwargs and position is None: + position = "" + if kwargs.get("label" + arg): + position += arg[0] + if position is not None: + self.set_ticklabel_position(position) + + # And the grid settings + if "grid_color" in kwargs: + self._grid_lines_kwargs["edgecolor"] = kwargs["grid_color"] + if "grid_alpha" in kwargs: + self._grid_lines_kwargs["alpha"] = kwargs["grid_alpha"] + if "grid_linewidth" in kwargs: + self._grid_lines_kwargs["linewidth"] = kwargs["grid_linewidth"] + if "grid_linestyle" in kwargs: + if kwargs["grid_linestyle"] in LINES_TO_PATCHES_LINESTYLE: + self._grid_lines_kwargs["linestyle"] = LINES_TO_PATCHES_LINESTYLE[ + kwargs["grid_linestyle"] + ] + else: + self._grid_lines_kwargs["linestyle"] = kwargs["grid_linestyle"] diff --git a/astropy/visualization/wcsaxes/coordinate_range.py b/astropy/visualization/wcsaxes/coordinate_range.py new file mode 100644 index 000000000000..f883b0c664ea --- /dev/null +++ b/astropy/visualization/wcsaxes/coordinate_range.py @@ -0,0 +1,147 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + + +import warnings + +import numpy as np + +from astropy import units as u +from astropy.utils.exceptions import AstropyDeprecationWarning + +# Algorithm inspired by PGSBOX from WCSLIB by M. Calabretta + +LONLAT = {"longitude", "latitude"} + + +def wrap_180(values): + values_new = values % 360.0 + with np.errstate(invalid="ignore"): + values_new[values_new > 180.0] -= 360 + return values_new + + +def find_coordinate_range(transform, extent, coord_types, coord_units, coord_wraps): + """ + Find the range of coordinates to use for ticks/grids. + + Parameters + ---------- + transform : func + Function to transform pixel to world coordinates. Should take two + values (the pixel coordinates) and return two values (the world + coordinates). + extent : iterable + The range of the image viewport in pixel coordinates, given as [xmin, + xmax, ymin, ymax]. + coord_types : list of str + Whether each coordinate is a ``'longitude'``, ``'latitude'``, or + ``'scalar'`` value. + coord_units : list of `astropy.units.Unit` + The units for each coordinate. + coord_wraps : list of `astropy.units.Quantity` + The wrap angles for longitudes. + """ + # Sample coordinates on a NX x NY grid. + from . import conf + + if len(extent) == 4: + nx = ny = conf.coordinate_range_samples + x = np.linspace(extent[0], extent[1], nx + 1) + y = np.linspace(extent[2], extent[3], ny + 1) + xp, yp = np.meshgrid(x, y) + with np.errstate(invalid="ignore"): + world = transform.transform(np.vstack([xp.ravel(), yp.ravel()]).transpose()) + else: + nx = conf.coordinate_range_samples + xp = np.linspace(extent[0], extent[1], nx + 1)[None] + with np.errstate(invalid="ignore"): + world = transform.transform(xp.T) + + ranges = [] + + for coord_index, coord_type in enumerate(coord_types): + xw = world[:, coord_index].reshape(xp.shape) + + if coord_type in LONLAT: + unit = coord_units[coord_index] + xw = xw * unit.to(u.deg) + + # Iron out coordinates along first row + wjump = xw[0, 1:] - xw[0, :-1] + with np.errstate(invalid="ignore"): + reset = np.abs(wjump) > 180.0 + if np.any(reset): + wjump = wjump + np.sign(wjump) * 180.0 + wjump = 360.0 * np.trunc(wjump / 360.0) + xw[0, 1:][reset] -= wjump[reset] + + # Now iron out coordinates along all columns, starting with first row. + wjump = xw[1:] - xw[:1] + with np.errstate(invalid="ignore"): + reset = np.abs(wjump) > 180.0 + if np.any(reset): + wjump = wjump + np.sign(wjump) * 180.0 + wjump = 360.0 * np.trunc(wjump / 360.0) + xw[1:][reset] -= wjump[reset] + + with warnings.catch_warnings(): + warnings.simplefilter("ignore", RuntimeWarning) + xw_min = np.nanmin(xw) + xw_max = np.nanmax(xw) + + # Check if range is smaller when normalizing to the range 0 to 360 + + if coord_type in LONLAT: + with warnings.catch_warnings(): + warnings.simplefilter("ignore", RuntimeWarning) + xw_min_check = np.nanmin(xw % 360.0) + xw_max_check = np.nanmax(xw % 360.0) + + if xw_max_check - xw_min_check <= xw_max - xw_min < 360.0: + xw_min = xw_min_check + xw_max = xw_max_check + + # Check if range is smaller when normalizing to the range -180 to 180 + + if coord_type in LONLAT: + with warnings.catch_warnings(): + warnings.simplefilter("ignore", RuntimeWarning) + xw_min_check = np.nanmin(wrap_180(xw)) + xw_max_check = np.nanmax(wrap_180(xw)) + + if ( + xw_max_check - xw_min_check < 360.0 + and xw_max - xw_min >= xw_max_check - xw_min_check + ): + xw_min = xw_min_check + xw_max = xw_max_check + + x_range = xw_max - xw_min + if coord_type == "longitude": + if x_range > 300.0: + coord_wrap = coord_wraps[coord_index] + if not isinstance(coord_wrap, u.Quantity): + warnings.warn( + "Passing 'coord_wraps' as numbers is deprecated. Use a Quantity with units convertible to angular degrees instead.", + AstropyDeprecationWarning, + ) + coord_wrap = coord_wrap * u.deg + xw_min = coord_wrap.to_value(u.deg) - 360 + xw_max = coord_wrap.to_value(u.deg) - np.spacing(360.0) + elif xw_min < 0.0: + xw_min = max(-180.0, xw_min - 0.1 * x_range) + xw_max = min(+180.0, xw_max + 0.1 * x_range) + else: + xw_min = max(0.0, xw_min - 0.1 * x_range) + xw_max = min(360.0, xw_max + 0.1 * x_range) + elif coord_type == "latitude": + xw_min = max(-90.0, xw_min - 0.1 * x_range) + xw_max = min(+90.0, xw_max + 0.1 * x_range) + + if coord_type in LONLAT: + xw_min *= u.deg.to(unit) + xw_max *= u.deg.to(unit) + + ranges.append((xw_min, xw_max)) + + return ranges diff --git a/astropy/visualization/wcsaxes/coordinates_map.py b/astropy/visualization/wcsaxes/coordinates_map.py new file mode 100644 index 000000000000..30b5a11a0e91 --- /dev/null +++ b/astropy/visualization/wcsaxes/coordinates_map.py @@ -0,0 +1,199 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +from collections import OrderedDict +from textwrap import indent + +from .coordinate_helpers import CoordinateHelper +from .coordinate_range import find_coordinate_range +from .frame import RectangularFrame, RectangularFrame1D + + +class CoordinatesMap: + """ + A container for coordinate helpers that represents a coordinate system. + + This object can be used to access coordinate helpers by index (like a list) + or by name (like a dictionary). + + Parameters + ---------- + axes : :class:`~astropy.visualization.wcsaxes.WCSAxes` + The axes the coordinate map belongs to. + transform : `~matplotlib.transforms.Transform`, optional + The transform for the data. + coord_meta : dict, optional + A dictionary providing additional metadata. This should include the keys + ``type``, ``wrap``, and ``unit``. Each of these should be a list with as + many items as the dimension of the coordinate system. The ``type`` + entries should be one of ``longitude``, ``latitude``, or ``scalar``, the + ``wrap`` entries should give, for the longitude, the angle at which the + coordinate wraps (and `None` otherwise), and the ``unit`` should give + the unit of the coordinates as :class:`~astropy.units.Unit` instances. + This can optionally also include a ``format_unit`` entry giving the + units to use for the tick labels (if not specified, this defaults to + ``unit``). + frame_class : type, optional + The class for the frame, which should be a subclass of + :class:`~astropy.visualization.wcsaxes.frame.BaseFrame`. The default is to use a + :class:`~astropy.visualization.wcsaxes.frame.RectangularFrame` + previous_frame_path : `~matplotlib.path.Path`, optional + When changing the WCS of the axes, the frame instance will change but + we might want to keep reusing the same underlying matplotlib + `~matplotlib.path.Path` - in that case, this can be passed to this + keyword argument. + """ + + def __init__( + self, + axes, + transform=None, + coord_meta=None, + frame_class=RectangularFrame, + previous_frame_path=None, + ): + self._axes = axes + self._transform = transform + + self.frame = frame_class(axes, self._transform, path=previous_frame_path) + + # Set up coordinates + self._coords = [] + self._aliases = {} + + visible_count = 0 + + for index in range(len(coord_meta["type"])): + # Extract coordinate metadata + coord_type = coord_meta["type"][index] + coord_wrap = coord_meta["wrap"][index] + coord_unit = coord_meta["unit"][index] + name = coord_meta["name"][index] + + visible = True + if "visible" in coord_meta: + visible = coord_meta["visible"][index] + + format_unit = None + if "format_unit" in coord_meta: + format_unit = coord_meta["format_unit"][index] + + default_label = name[0] if isinstance(name, (tuple, list)) else name + if "default_axis_label" in coord_meta: + default_label = coord_meta["default_axis_label"][index] + + coord_index = None + if visible: + visible_count += 1 + coord_index = visible_count - 1 + + self._coords.append( + CoordinateHelper( + parent_axes=axes, + parent_map=self, + transform=self._transform, + coord_index=coord_index, + coord_type=coord_type, + coord_wrap=coord_wrap, + coord_unit=coord_unit, + format_unit=format_unit, + frame=self.frame, + default_label=default_label, + ) + ) + + # Set up aliases for coordinates + if isinstance(name, tuple): + for nm in name: + nm = nm.lower() + # Do not replace an alias already in the map if we have + # more than one alias for this axis. + if nm not in self._aliases: + self._aliases[nm] = index + else: + self._aliases[name.lower()] = index + + def __getitem__(self, item): + if isinstance(item, str): + return self._coords[self._aliases[item.lower()]] + else: + return self._coords[item] + + def __contains__(self, item): + if isinstance(item, str): + return item.lower() in self._aliases + else: + return 0 <= item < len(self._coords) + + def set_visible(self, visibility): + raise NotImplementedError() + + def __iter__(self): + yield from self._coords + + def grid(self, draw_grid=True, grid_type=None, **kwargs): + """ + Plot gridlines for both coordinates. + + Standard matplotlib appearance options (color, alpha, etc.) can be + passed as keyword arguments. + + Parameters + ---------- + draw_grid : bool + Whether to show the gridlines + grid_type : { 'lines' | 'contours' } + Whether to plot the contours by determining the grid lines in + world coordinates and then plotting them in world coordinates + (``'lines'``) or by determining the world coordinates at many + positions in the image and then drawing contours + (``'contours'``). The first is recommended for 2-d images, while + for 3-d (or higher dimensional) cubes, the ``'contours'`` option + is recommended. By default, 'lines' is used if the transform has + an inverse, otherwise 'contours' is used. + """ + for coord in self: + coord.grid(draw_grid=draw_grid, grid_type=grid_type, **kwargs) + + def get_coord_range(self): + xmin, xmax = self._axes.get_xlim() + + if isinstance(self.frame, RectangularFrame1D): + extent = [xmin, xmax] + else: + ymin, ymax = self._axes.get_ylim() + extent = [xmin, xmax, ymin, ymax] + + return find_coordinate_range( + self._transform, + extent, + [coord.coord_type for coord in self if coord.coord_index is not None], + [coord.coord_unit for coord in self if coord.coord_index is not None], + [coord.coord_wrap for coord in self if coord.coord_index is not None], + ) + + def _as_table(self): + # Import Table here to avoid importing the astropy.table package + # every time astropy.visualization.wcsaxes is imported. + from astropy.table import Table + + rows = [] + for icoord, coord in enumerate(self._coords): + aliases = [key for key, value in self._aliases.items() if value == icoord] + row = OrderedDict( + [ + ("index", icoord), + ("aliases", " ".join(aliases)), + ("type", coord.coord_type), + ("unit", coord.coord_unit), + ("wrap", coord.coord_wrap), + ("format_unit", coord.get_format_unit()), + ("visible", "no" if coord.coord_index is None else "yes"), + ] + ) + rows.append(row) + return Table(rows=rows) + + def __repr__(self): + s = f"" diff --git a/astropy/visualization/wcsaxes/core.py b/astropy/visualization/wcsaxes/core.py new file mode 100644 index 000000000000..e6cf7098e66a --- /dev/null +++ b/astropy/visualization/wcsaxes/core.py @@ -0,0 +1,939 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +from collections import defaultdict +from functools import partial + +import numpy as np +from matplotlib import rcParams +from matplotlib.artist import Artist +from matplotlib.axes import Axes, subplot_class_factory +from matplotlib.transforms import Affine2D, Bbox, Transform + +from astropy.coordinates import BaseCoordinateFrame, SkyCoord +from astropy.utils.compat.optional_deps import HAS_PIL +from astropy.wcs import WCS +from astropy.wcs.wcsapi import BaseHighLevelWCS, BaseLowLevelWCS + +from ._auto import auto_assign_coord_positions +from .coordinates_map import CoordinatesMap +from .frame import RectangularFrame, RectangularFrame1D +from .transforms import CoordinateTransform +from .utils import get_coord_meta, transform_contour_set_inplace +from .wcsapi import IDENTITY, transform_coord_meta_from_wcs + +__all__ = ["WCSAxes", "WCSAxesSubplot"] + +VISUAL_PROPERTIES = ["facecolor", "edgecolor", "linewidth", "alpha", "linestyle"] + +if HAS_PIL: + from PIL.Image import Image, Transpose + + +class _WCSAxesArtist(Artist): + """This is a dummy artist to enforce the correct z-order of axis ticks, + tick labels, and gridlines. + + FIXME: This is a bit of a hack. ``Axes.draw`` sorts the artists by zorder + and then renders them in sequence. For normal Matplotlib axes, the ticks, + tick labels, and gridlines are included in this list of artists and hence + are automatically drawn in the correct order. However, ``WCSAxes`` disables + the native ticks, labels, and gridlines. Instead, ``WCSAxes.draw`` renders + ersatz ticks, labels, and gridlines by explicitly calling the functions + ``CoordinateHelper._draw_ticks``, ``CoordinateHelper._draw_grid``, etc. + This hack would not be necessary if ``WCSAxes`` drew ticks, tick labels, + and gridlines in the standary way. + """ + + def draw(self, renderer): + self.axes.draw_wcsaxes(renderer) + + +class WCSAxes(Axes): + """ + The main axes class that can be used to show world coordinates from a WCS. + + Parameters + ---------- + fig : `~matplotlib.figure.Figure` + The figure to add the axes to + *args + ``*args`` can be a single ``(left, bottom, width, height)`` + rectangle or a single `matplotlib.transforms.Bbox`. This specifies + the rectangle (in figure coordinates) where the Axes is positioned. + ``*args`` can also consist of three numbers or a single three-digit + number; in the latter case, the digits are considered as + independent numbers. The numbers are interpreted as ``(nrows, + ncols, index)``: ``(nrows, ncols)`` specifies the size of an array + of subplots, and ``index`` is the 1-based index of the subplot + being created. Finally, ``*args`` can also directly be a + `matplotlib.gridspec.SubplotSpec` instance. + wcs : :class:`~astropy.wcs.WCS`, optional + The WCS for the data. If this is specified, ``transform`` cannot be + specified. + transform : `~matplotlib.transforms.Transform`, optional + The transform for the data. If this is specified, ``wcs`` cannot be + specified. + coord_meta : dict, optional + A dictionary providing additional metadata when ``transform`` is + specified. This should include the keys ``type``, ``wrap``, and + ``unit``. Each of these should be a list with as many items as the + dimension of the WCS. The ``type`` entries should be one of + ``longitude``, ``latitude``, or ``scalar``, the ``wrap`` entries should + give, for the longitude, the angle at which the coordinate wraps (and + `None` otherwise), and the ``unit`` should give the unit of the + coordinates as :class:`~astropy.units.Unit` instances. This can + optionally also include a ``format_unit`` entry giving the units to use + for the tick labels (if not specified, this defaults to ``unit``). + transData : `~matplotlib.transforms.Transform`, optional + Can be used to override the default data -> pixel mapping. + slices : tuple, optional + For WCS transformations with more than two dimensions, we need to + choose which dimensions are being shown in the 2D image. The slice + should contain one ``x`` entry, one ``y`` entry, and the rest of the + values should be integers indicating the slice through the data. The + order of the items in the slice should be the same as the order of the + dimensions in the :class:`~astropy.wcs.WCS`, and the opposite of the + order of the dimensions in Numpy. For example, ``(50, 'x', 'y')`` means + that the first WCS dimension (last Numpy dimension) will be sliced at + an index of 50, the second WCS and Numpy dimension will be shown on the + x axis, and the final WCS dimension (first Numpy dimension) will be + shown on the y-axis (and therefore the data will be plotted using + ``data[:, :, 50].transpose()``) + frame_class : type, optional + The class for the frame, which should be a subclass of + :class:`~astropy.visualization.wcsaxes.frame.BaseFrame`. The default is to use a + :class:`~astropy.visualization.wcsaxes.frame.RectangularFrame` + + Attributes + ---------- + coords : :class:`~astropy.visualization.wcsaxes.CoordinatesMap` + Container for coordinate information. + """ + + def __init__( + self, + fig, + *args, + wcs=None, + transform=None, + coord_meta=None, + transData=None, + slices=None, + frame_class=None, + **kwargs, + ): + """ """ + + super().__init__(fig, *args, **kwargs) + self._bboxes = [] + + if frame_class is not None: + self.frame_class = frame_class + elif wcs is not None and ( + wcs.pixel_n_dim == 1 or (slices is not None and "y" not in slices) + ): + self.frame_class = RectangularFrame1D + else: + self.frame_class = RectangularFrame + + if transData is not None: + # User wants to override the transform for the final + # data->pixel mapping + self.transData = transData + + self.reset_wcs( + wcs=wcs, slices=slices, transform=transform, coord_meta=coord_meta + ) + self._hide_parent_artists() + self.format_coord = self._display_world_coords + self._display_coords_index = 0 + fig.canvas.mpl_connect("key_press_event", self._set_cursor_prefs) + self.patch = self.coords.frame.patch + self._wcsaxesartist = _WCSAxesArtist() + self.add_artist(self._wcsaxesartist) + self._drawn = False + + def _display_world_coords(self, x, y): + if not self._drawn: + return "" + + if self._display_coords_index == -1: + return f"{x} {y} (pixel)" + + pixel = np.array([x, y]) + + coords = self._all_coords[self._display_coords_index] + + world = coords._transform.transform(np.array([pixel]))[0] + + coord_strings = [] + for coord in coords: + if coord.coord_index is not None: + coord_strings.append( + coord.format_coord(world[coord.coord_index], format="ascii") + ) + + coord_string = " ".join(coord_strings) + + if self._display_coords_index == 0: + system = "world" + else: + system = f"world, overlay {self._display_coords_index}" + + return f"{coord_string} ({system})" + + def _set_cursor_prefs(self, event, **kwargs): + if event.key == "w": + self._display_coords_index += 1 + if self._display_coords_index + 1 > len(self._all_coords): + self._display_coords_index = -1 + + def _hide_parent_artists(self): + # Turn off spines and current axes + for s in self.spines.values(): + s.set_visible(False) + + self.xaxis.set_visible(False) + if self.frame_class is not RectangularFrame1D: + self.yaxis.set_visible(False) + + # We now overload ``imshow`` because we need to make sure that origin is + # set to ``lower`` for all images, which means that we need to flip RGB + # images. + def imshow(self, X, *args, **kwargs): + """ + Wrapper to Matplotlib's :meth:`~matplotlib.axes.Axes.imshow`. + + If an RGB image is passed as a PIL object, it will be flipped + vertically and ``origin`` will be set to ``lower``, since WCS + transformations - like FITS files - assume that the origin is the lower + left pixel of the image (whereas RGB images have the origin in the top + left). + + All arguments are passed to :meth:`~matplotlib.axes.Axes.imshow`. + """ + origin = kwargs.pop("origin", "lower") + + # plt.imshow passes origin as None, which we should default to lower. + if origin is None: + origin = "lower" + elif origin == "upper": + raise ValueError("Cannot use images with origin='upper' in WCSAxes.") + + if HAS_PIL and (isinstance(X, Image) or hasattr(X, "getpixel")): + X = X.transpose(Transpose.FLIP_TOP_BOTTOM) + + return super().imshow(X, *args, origin=origin, **kwargs) + + def contour(self, *args, **kwargs): + """ + Plot contours. + + This is a custom implementation of :meth:`~matplotlib.axes.Axes.contour` + which applies the transform (if specified) to all contours in one go for + performance rather than to each contour line individually. All + positional and keyword arguments are the same as for + :meth:`~matplotlib.axes.Axes.contour`. + """ + # In Matplotlib, when calling contour() with a transform, each + # individual path in the contour map is transformed separately. However, + # this is much too slow for us since each call to the transforms results + # in an Astropy coordinate transformation, which has a non-negligible + # overhead - therefore a better approach is to override contour(), call + # the Matplotlib one with no transform, then apply the transform in one + # go to all the segments that make up the contour map. + + transform = kwargs.pop("transform", None) + + cset = super().contour(*args, **kwargs) + + if transform is not None: + # The transform passed to self.contour will normally include + # a transData component at the end, but we can remove that since + # we are already working in data space. + transform = transform - self.transData + transform_contour_set_inplace(cset, transform) + + return cset + + def contourf(self, *args, **kwargs): + """ + Plot filled contours. + + This is a custom implementation of :meth:`~matplotlib.axes.Axes.contourf` + which applies the transform (if specified) to all contours in one go for + performance rather than to each contour line individually. All + positional and keyword arguments are the same as for + :meth:`~matplotlib.axes.Axes.contourf`. + """ + # See notes for contour above. + + transform = kwargs.pop("transform", None) + + cset = super().contourf(*args, **kwargs) + + if transform is not None: + # The transform passed to self.contour will normally include + # a transData component at the end, but we can remove that since + # we are already working in data space. + transform = transform - self.transData + transform_contour_set_inplace(cset, transform) + + return cset + + def _transform_plot_args(self, *args, **kwargs): + """ + Apply transformations to arguments to ``plot_coord`` and + ``scatter_coord``. + """ + if isinstance(args[0], (SkyCoord, BaseCoordinateFrame)): + # Extract the frame from the first argument. + frame0 = args[0] + if isinstance(frame0, SkyCoord): + frame0 = frame0.frame + + native_frame = self._transform_pixel2world.frame_out + # Transform to the native frame of the plot + frame0 = frame0.transform_to(native_frame) + + plot_data = [] + coord_types = {coord.coord_type for coord in self.coords} + if "longitude" in coord_types and "latitude" in coord_types: + for coord in self.coords: + if coord.coord_type == "longitude": + plot_data.append( + frame0.spherical.lon.to_value(coord.coord_unit) + ) + elif coord.coord_type == "latitude": + plot_data.append( + frame0.spherical.lat.to_value(coord.coord_unit) + ) + else: + raise NotImplementedError( + "Coordinates cannot be plotted with this " + "method because the WCS does not represent longitude/latitude." + ) + + if "transform" in kwargs.keys(): + raise TypeError( + "The 'transform' keyword argument is not allowed," + " as it is automatically determined by the input coordinate frame." + ) + + transform = self.get_transform(native_frame) + kwargs.update({"transform": transform}) + + args = tuple(plot_data) + args[1:] + + return args, kwargs + + def plot_coord(self, *args, **kwargs): + """ + Plot `~astropy.coordinates.SkyCoord` or + `~astropy.coordinates.BaseCoordinateFrame` objects onto the axes. + + The first argument to + :meth:`~astropy.visualization.wcsaxes.WCSAxes.plot_coord` should be a + coordinate, which will then be converted to the first two parameters to + `matplotlib.axes.Axes.plot`. All other arguments are the same as + `matplotlib.axes.Axes.plot`. If not specified a ``transform`` keyword + argument will be created based on the coordinate. + + Parameters + ---------- + coordinate : `~astropy.coordinates.SkyCoord` or `~astropy.coordinates.BaseCoordinateFrame` + The coordinate object to plot on the axes. This is converted to the + first two arguments to `matplotlib.axes.Axes.plot`. + + See Also + -------- + matplotlib.axes.Axes.plot : + This method is called from this function with all arguments passed to it. + + """ + args, kwargs = self._transform_plot_args(*args, **kwargs) + + return super().plot(*args, **kwargs) + + def text_coord(self, *args, **kwargs): + """ + Print a text string using `~astropy.coordinates.SkyCoord` or + `~astropy.coordinates.BaseCoordinateFrame` objects onto the axes. + + The first argument to + :meth:`~astropy.visualization.wcsaxes.WCSAxes.text_coord` should be a + coordinate, which will then be converted to the first two parameters to + `matplotlib.axes.Axes.text`. All other arguments are the same as + `matplotlib.axes.Axes.text`. If not specified a ``transform`` keyword + argument will be created based on the coordinate. + + Parameters + ---------- + coordinate : `~astropy.coordinates.SkyCoord` or `~astropy.coordinates.BaseCoordinateFrame` + The coordinate object to plot on the axes. This is converted to the + first two arguments to `matplotlib.axes.Axes.text`. + + See Also + -------- + matplotlib.axes.Axes.text : + This method is called from this function with all arguments passed to it. + + """ + args, kwargs = self._transform_plot_args(*args, **kwargs) + + return super().text(*args, **kwargs) + + def scatter_coord(self, *args, **kwargs): + """ + Scatter `~astropy.coordinates.SkyCoord` or + `~astropy.coordinates.BaseCoordinateFrame` objects onto the axes. + + The first argument to + :meth:`~astropy.visualization.wcsaxes.WCSAxes.scatter_coord` should be a + coordinate, which will then be converted to the first two parameters to + `matplotlib.axes.Axes.scatter`. All other arguments are the same as + `matplotlib.axes.Axes.scatter`. If not specified a ``transform`` + keyword argument will be created based on the coordinate. + + Parameters + ---------- + coordinate : `~astropy.coordinates.SkyCoord` or `~astropy.coordinates.BaseCoordinateFrame` + The coordinate object to scatter on the axes. This is converted to + the first two arguments to `matplotlib.axes.Axes.scatter`. + + See Also + -------- + matplotlib.axes.Axes.scatter : This method is called from this function with all arguments passed to it. + """ + args, kwargs = self._transform_plot_args(*args, **kwargs) + + return super().scatter(*args, **kwargs) + + def reset_wcs(self, wcs=None, slices=None, transform=None, coord_meta=None): + """ + Reset the current Axes, to use a new WCS object. + """ + # Here determine all the coordinate axes that should be shown. + if wcs is None and transform is None: + self.wcs = IDENTITY + + else: + # We now force call 'set', which ensures the WCS object is + # consistent, which will only be important if the WCS has been set + # by hand. For example if the user sets a celestial WCS by hand and + # forgets to set the units, WCS.wcs.set() will do this. + if wcs is not None: + # Check if the WCS object is an instance of `astropy.wcs.WCS` + # This check is necessary as only `astropy.wcs.WCS` supports + # wcs.set() method + if isinstance(wcs, WCS): + wcs.wcs.set() + + if isinstance(wcs, BaseHighLevelWCS): + wcs = wcs.low_level_wcs + + self.wcs = wcs + + # If we are making a new WCS, we need to preserve the path object since + # it may already be used by objects that have been plotted, and we need + # to continue updating it. CoordinatesMap will create a new frame + # instance, but we can tell that instance to keep using the old path. + if hasattr(self, "coords"): + previous_frame = { + "path": self.coords.frame._path, + "color": self.coords.frame.get_color(), + "linewidth": self.coords.frame.get_linewidth(), + } + else: + previous_frame = {"path": None} + + if self.wcs is not None: + transform, coord_meta = transform_coord_meta_from_wcs( + self.wcs, self.frame_class, slices=slices + ) + + self.coords = CoordinatesMap( + self, + transform=transform, + coord_meta=coord_meta, + frame_class=self.frame_class, + previous_frame_path=previous_frame["path"], + ) + + self._transform_pixel2world = transform + + if previous_frame["path"] is not None: + self.coords.frame.set_color(previous_frame["color"]) + self.coords.frame.set_linewidth(previous_frame["linewidth"]) + + self._all_coords = [self.coords] + + # Common default settings for Rectangular Frame + for ind, pos in enumerate( + coord_meta.get("default_axislabel_position", ["b", "l"]) + ): + self.coords[ind].set_axislabel_position(pos) + + for ind, pos in enumerate( + coord_meta.get("default_ticklabel_position", ["b", "l"]) + ): + self.coords[ind].set_ticklabel_position(pos) + + for ind, pos in enumerate( + coord_meta.get("default_ticks_position", ["bltr", "bltr"]) + ): + self.coords[ind].set_ticks_position(pos) + + if rcParams["axes.grid"]: + self.grid() + + def _update_tick_and_label_positions(self, keep_coord_range=False): + """ + This method will update the tick positions and will then optionally + decide on which axes to show ticks/tick labels/axis labels on if in + automatic mode. + + The ``keep_coord_range`` argument is used to indicate whether to keep + coords._coord_range at the end of the method or whether to clean it + up. + """ + # Start off by updating the frame, pre-computing the coordinate range + # in the figure, and updating the tick positions. + for coords in self._all_coords: + coords.frame.update() + coords._coord_range = coords.get_coord_range() + + for coord in coords: + coord._update_ticks() + + # At this point, if any of the tick/ticklabel/axislabel positions are + # set to be automatic, we need to determine the optimal positions. + auto_assign_coord_positions(self) + + if not keep_coord_range: + for coords in self._all_coords: + del coords._coord_range + + def draw_wcsaxes(self, renderer): + if not self.axison: + return + # Here need to find out range of all coordinates, and update range for + # each coordinate axis. For now, just assume it covers the whole sky. + + self._bboxes = [] + # This generates a structure like [coords][axis] = [...] + ticklabels_bbox = defaultdict(partial(defaultdict, list)) + + visible_ticks = [] + + self._update_tick_and_label_positions(keep_coord_range=True) + + for coords in self._all_coords: + for coord in coords: + coord._draw_grid(renderer) + + # Delete the computation to protect from accidental use of a stale range + del coords._coord_range + + for coords in self._all_coords: + # Draw tick labels + for coord in coords: + coord._draw_ticks(renderer, self._bboxes) + + visible_ticks.extend(coord._ticklabels.get_visible_axes()) + # Save ticklabel bboxes + ticklabels_bbox[coord] = coord._ticklabels._axis_bboxes + self._bboxes += coord._ticklabels._all_bboxes + + for coords in self._all_coords: + # Draw axis labels + for coord in coords: + coord._draw_axislabels( + renderer, + bboxes=self._bboxes, + ticklabels_bbox=ticklabels_bbox, + visible_ticks=visible_ticks, + ) + + self.coords.frame.draw(renderer) + + def draw(self, renderer): + """Draw the axes.""" + # Before we do any drawing, we need to remove any existing grid lines + # drawn with contours, otherwise if we try and remove the contours + # part way through drawing, we end up with the issue mentioned in + # https://github.com/astropy/astropy/issues/12446 + for coords in self._all_coords: + for coord in coords: + coord._clear_grid_contour() + + # In Axes.draw, the following code can result in the xlim and ylim + # values changing, so we need to force call this here to make sure that + # the limits are correct before we update the patch. + locator = self.get_axes_locator() + if locator: + pos = locator(self, renderer) + self.apply_aspect(pos) + else: + self.apply_aspect() + + if self._axisbelow is True: + self._wcsaxesartist.set_zorder(0.5) + elif self._axisbelow is False: + self._wcsaxesartist.set_zorder(2.5) + else: + # 'line': above patches, below lines + self._wcsaxesartist.set_zorder(1.5) + + # We need to make sure that that frame path is up to date + self.coords.frame._update_patch_path() + + super().draw(renderer) + + self._drawn = True + + # Matplotlib internally sometimes calls set_xlabel(label=...). + def set_xlabel(self, xlabel=None, labelpad=1, loc=None, **kwargs): + """Set x-label.""" + self._update_tick_and_label_positions() + if xlabel is None: + xlabel = kwargs.pop("label", None) + if xlabel is None: + raise TypeError( + "set_xlabel() missing 1 required positional argument: 'xlabel'" + ) + for coord in self.coords: + if ( + "b" in coord._axislabels.get_visible_axes() + or "h" in coord._axislabels.get_visible_axes() + ): + coord.set_axislabel(xlabel, minpad=labelpad, **kwargs) + break + + def set_ylabel(self, ylabel=None, labelpad=1, loc=None, **kwargs): + """Set y-label.""" + self._update_tick_and_label_positions() + if ylabel is None: + ylabel = kwargs.pop("label", None) + if ylabel is None: + raise TypeError( + "set_ylabel() missing 1 required positional argument: 'ylabel'" + ) + + if self.frame_class is RectangularFrame1D: + return super().set_ylabel(ylabel, labelpad=labelpad, **kwargs) + + for coord in self.coords: + if ( + "l" in coord._axislabels.get_visible_axes() + or "c" in coord._axislabels.get_visible_axes() + ): + coord.set_axislabel(ylabel, minpad=labelpad, **kwargs) + break + + def get_xlabel(self): + self._update_tick_and_label_positions() + for coord in self.coords: + if ( + "b" in coord._axislabels.get_visible_axes() + or "h" in coord._axislabels.get_visible_axes() + ): + return coord.get_axislabel() + + def get_ylabel(self): + self._update_tick_and_label_positions() + if self.frame_class is RectangularFrame1D: + return super().get_ylabel() + + for coord in self.coords: + if ( + "l" in coord._axislabels.get_visible_axes() + or "c" in coord._axislabels.get_visible_axes() + ): + return coord.get_axislabel() + + def get_coords_overlay(self, frame, coord_meta=None): + """Get coordinates overlay on given frame. + + Parameters + ---------- + frame : str, `~astropy.coordinates.BaseCoordinateFrame` + Frame to get overlay for. If a string must correspond to + one of the coordinate frames registered in the astropy + frame transform graph. + + coord_meta : dict + Metadata for the coordinates overlay. + + Returns + ------- + overlay : `~astropy.visualization.wcsaxes.CoordinatesMap` + Coordinates overlay. + """ + # Here we can't use get_transform because that deals with + # pixel-to-pixel transformations when passing a WCS object. + if isinstance(frame, WCS): + transform, coord_meta = transform_coord_meta_from_wcs( + frame, self.frame_class + ) + else: + transform = self._get_transform_no_transdata(frame) + + if coord_meta is None: + coord_meta = get_coord_meta(frame) + + coords = CoordinatesMap( + self, + transform=transform, + coord_meta=coord_meta, + frame_class=self.frame_class, + ) + + self._all_coords.append(coords) + + # Common settings for overlay + coords[0].set_axislabel_position("t") + coords[1].set_axislabel_position("r") + coords[0].set_ticklabel_position("t") + coords[1].set_ticklabel_position("r") + + self.overlay_coords = coords + + return coords + + def get_transform(self, frame): + """ + Return a transform from the specified frame to display coordinates. + + This includes the transData transformation + + Parameters + ---------- + frame : :class:`~astropy.wcs.WCS` or :class:`~matplotlib.transforms.Transform` or str + The ``frame`` parameter can have several possible types: + * :class:`~astropy.wcs.WCS` instance: assumed to be a + transformation from pixel to world coordinates, where the + world coordinates are the same as those in the WCS + transformation used for this ``WCSAxes`` instance. This is + used for example to show contours, since this involves + plotting an array in pixel coordinates that are not the + final data coordinate and have to be transformed to the + common world coordinate system first. + * :class:`~matplotlib.transforms.Transform` instance: it is + assumed to be a transform to the world coordinates that are + part of the WCS used to instantiate this ``WCSAxes`` + instance. + * ``'pixel'`` or ``'world'``: return a transformation that + allows users to plot in pixel/data coordinates (essentially + an identity transform) and ``world`` (the default + world-to-pixel transformation used to instantiate the + ``WCSAxes`` instance). + * ``'fk5'`` or ``'galactic'``: return a transformation from + the specified frame to the pixel/data coordinates. In this + case, the values are assumed to be in degrees. + * :class:`~astropy.coordinates.BaseCoordinateFrame` instance. + In this case, the values are assumed to be in degrees. + """ + return self._get_transform_no_transdata(frame).inverted() + self.transData + + def _get_transform_no_transdata(self, frame): + """ + Return a transform from data to the specified frame. + """ + if isinstance(frame, (BaseLowLevelWCS, BaseHighLevelWCS)): + if isinstance(frame, BaseHighLevelWCS): + frame = frame.low_level_wcs + + transform, coord_meta = transform_coord_meta_from_wcs( + frame, self.frame_class + ) + transform_world2pixel = transform.inverted() + + if ( + self._transform_pixel2world.frame_out == transform_world2pixel.frame_in + and self._transform_pixel2world.units_out + == transform_world2pixel.units_in + ): + return self._transform_pixel2world + transform_world2pixel + + else: + return ( + self._transform_pixel2world + + CoordinateTransform( + self._transform_pixel2world.frame_out, + self._transform_pixel2world.units_out, + transform_world2pixel.frame_in, + transform_world2pixel.units_in, + ) + + transform_world2pixel + ) + + elif isinstance(frame, str) and frame == "pixel": + return Affine2D() + + elif isinstance(frame, Transform): + return self._transform_pixel2world + frame + + else: + if isinstance(frame, str) and frame == "world": + return self._transform_pixel2world + + else: + coordinate_transform = CoordinateTransform( + self._transform_pixel2world.frame_out, + self._transform_pixel2world.units_out, + frame, + ["deg", "deg"], + ) + + if coordinate_transform.same_frames: + return self._transform_pixel2world + else: + return self._transform_pixel2world + coordinate_transform + + def get_tightbbox(self, renderer, *args, **kwargs): + # FIXME: we should determine what to do with the extra arguments here. + # Note that the expected signature of this method is different in + # Matplotlib 3.x compared to 2.x, but we only support 3.x now. + + if not self.get_visible(): + return + + # Do a draw to populate the self._bboxes list + self.draw_wcsaxes(renderer) + bb = [b for b in self._bboxes if b and (b.width != 0 or b.height != 0)] + bb.append(super().get_tightbbox(renderer, *args, **kwargs)) + + return Bbox.union(bb) + + def grid(self, b=None, axis="both", *, which="major", **kwargs): + """ + Plot gridlines for both coordinates. + + Standard matplotlib appearance options (color, alpha, etc.) can be + passed as keyword arguments. This behaves like `matplotlib.axes.Axes` + except that if no arguments are specified, the grid is shown rather + than toggled. + + Parameters + ---------- + b : bool + Whether to show the gridlines. + axis : 'both', 'x', 'y' + Which axis to turn the gridlines on/off for. + which : str + Currently only ``'major'`` is supported. + """ + if not hasattr(self, "coords"): + return + + if which != "major": + raise NotImplementedError( + "Plotting the grid for the minor ticks is not supported." + ) + + if axis == "both": + self.coords.grid(draw_grid=b, **kwargs) + elif axis == "x": + self.coords[0].grid(draw_grid=b, **kwargs) + elif axis == "y": + self.coords[1].grid(draw_grid=b, **kwargs) + else: + raise ValueError("axis should be one of x/y/both") + + def tick_params(self, axis="both", **kwargs): + """ + Method to set the tick and tick label parameters in the same way as the + :meth:`~matplotlib.axes.Axes.tick_params` method in Matplotlib. + + This is provided for convenience, but the recommended API is to use + :meth:`~astropy.visualization.wcsaxes.CoordinateHelper.set_ticks`, + :meth:`~astropy.visualization.wcsaxes.CoordinateHelper.set_ticklabel`, + :meth:`~astropy.visualization.wcsaxes.CoordinateHelper.set_ticks_position`, + :meth:`~astropy.visualization.wcsaxes.CoordinateHelper.set_ticklabel_position`, + and :meth:`~astropy.visualization.wcsaxes.CoordinateHelper.grid`. + + Parameters + ---------- + axis : int or str, optional + Which axis to apply the parameters to. This defaults to 'both' + but this can also be set to an `int` or `str` that refers to the + axis to apply it to, following the valid values that can index + ``ax.coords``. Note that ``'x'`` and ``'y``' are also accepted in + the case of rectangular axes. + which : {'both', 'major', 'minor'}, optional + Which ticks to apply the settings to. By default, setting are + applied to both major and minor ticks. Note that if ``'minor'`` is + specified, only the length of the ticks can be set currently. + direction : {'in', 'out'}, optional + Puts ticks inside the axes, or outside the axes. + length : float, optional + Tick length in points. + width : float, optional + Tick width in points. + color : color, optional + Tick color (accepts any valid Matplotlib color) + pad : float, optional + Distance in points between tick and label. + labelsize : float or str, optional + Tick label font size in points or as a string (e.g., 'large'). + labelcolor : color, optional + Tick label color (accepts any valid Matplotlib color) + colors : color, optional + Changes the tick color and the label color to the same value + (accepts any valid Matplotlib color). + bottom, top, left, right : bool, optional + Where to draw the ticks. Note that this can only be given if a + specific coordinate is specified via the ``axis`` argument, and it + will not work correctly if the frame is not rectangular. + labelbottom, labeltop, labelleft, labelright : bool, optional + Where to draw the tick labels. Note that this can only be given if a + specific coordinate is specified via the ``axis`` argument, and it + will not work correctly if the frame is not rectangular. + grid_color : color, optional + The color of the grid lines (accepts any valid Matplotlib color). + grid_alpha : float, optional + Transparency of grid lines: 0 (transparent) to 1 (opaque). + grid_linewidth : float, optional + Width of grid lines in points. + grid_linestyle : str, optional + The style of the grid lines (accepts any valid Matplotlib line + style). + """ + if not hasattr(self, "coords"): + # Axes haven't been fully initialized yet, so just ignore, as + # Axes.__init__ calls this method + return + + if axis == "both": + for pos in ("bottom", "left", "top", "right"): + if pos in kwargs: + raise ValueError(f"Cannot specify {pos}= when axis='both'") + if "label" + pos in kwargs: + raise ValueError(f"Cannot specify label{pos}= when axis='both'") + + for coord in self.coords: + coord.tick_params(**kwargs) + + elif axis in self.coords: + self.coords[axis].tick_params(**kwargs) + + elif axis in ("x", "y") and self.frame_class is RectangularFrame: + spine = "b" if axis == "x" else "l" + + self._update_tick_and_label_positions() + + for coord in self.coords: + if spine in coord._axislabels.get_visible_axes(): + coord.tick_params(**kwargs) + + +# In the following, we put the generated subplot class in a temporary class and +# we then inherit it - if we don't do this, the generated class appears to +# belong in matplotlib, not in WCSAxes, from the API's point of view. + + +class WCSAxesSubplot(subplot_class_factory(WCSAxes)): + """ + A subclass class for WCSAxes. + """ diff --git a/astropy/visualization/wcsaxes/formatter_locator.py b/astropy/visualization/wcsaxes/formatter_locator.py new file mode 100644 index 000000000000..53197ca4c8c2 --- /dev/null +++ b/astropy/visualization/wcsaxes/formatter_locator.py @@ -0,0 +1,660 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + + +# This file defines the AngleFormatterLocator class which is a class that +# provides both a method for a formatter and one for a locator, for a given +# label spacing. The advantage of keeping the two connected is that we need to +# make sure that the formatter can correctly represent the spacing requested and +# vice versa. For example, a format of dd:mm cannot work with a tick spacing +# that is not a multiple of one arcminute. + +import re +import warnings + +import numpy as np +from matplotlib import rcParams +from matplotlib.ticker import Formatter + +from astropy import units as u +from astropy.coordinates import Angle +from astropy.units import UnitsError + +DMS_RE = re.compile("^dd(:mm(:ss(.(s)+)?)?)?$") +HMS_RE = re.compile("^hh(:mm(:ss(.(s)+)?)?)?$") +DDEC_RE = re.compile("^d(.(d)+)?$") +DMIN_RE = re.compile("^m(.(m)+)?$") +DSEC_RE = re.compile("^s(.(s)+)?$") +SCAL_RE = re.compile("^x(.(x)+)?$") + + +# Units with custom representations - see the note where it is used inside +# AngleFormatterLocator.formatter for more details. + +CUSTOM_UNITS = { + u.degree: u.def_unit( + "custom_degree", + represents=u.degree, + format={"generic": "\xb0", "latex": r"^\circ", "unicode": "°"}, + ), + u.arcmin: u.def_unit( + "custom_arcmin", + represents=u.arcmin, + format={"generic": "'", "latex": r"^\prime", "unicode": "′"}, + ), + u.arcsec: u.def_unit( + "custom_arcsec", + represents=u.arcsec, + format={"generic": '"', "latex": r"^{\prime\prime}", "unicode": "â€ŗ"}, + ), + u.hourangle: u.def_unit( + "custom_hourangle", + represents=u.hourangle, + format={ + "generic": "h", + "latex": r"^{\mathrm{h}}", + "unicode": r"$\mathregular{^h}$", + }, + ), +} + + +def _fix_minus(labels: list[str], /) -> list[str]: + # correctly support axes.unicode_minus, but do it in a + # way that preserves arbitrary separators: only fix the leading character + # see https://github.com/astropy/astropy/issues/15898 + return [Formatter.fix_minus(s[0]) + s[1:] for s in labels] + + +class BaseFormatterLocator: + """ + A joint formatter/locator. + """ + + def __init__( + self, + values=None, + number=None, + spacing=None, + format=None, + unit=None, + format_unit=None, + ): + if len([x for x in (values, number, spacing) if x is None]) < 2: + raise ValueError("At most one of values/number/spacing can be specified") + + self._unit = unit + self._format_unit = format_unit or unit + + if values is not None: + self.values = values + elif number is not None: + self.number = number + elif spacing is not None: + self.spacing = spacing + else: + self.number = 5 + + self.format = format + + @property + def values(self): + return self._values + + @values.setter + def values(self, values): + if not isinstance(values, u.Quantity) or (not values.ndim == 1): + raise TypeError("values should be an astropy.units.Quantity array") + if not values.unit.is_equivalent(self._unit): + raise UnitsError( + "value should be in units compatible with " + f"coordinate units ({self._unit}) but found {values.unit}" + ) + self._number = None + self._spacing = None + self._values = values + + @property + def number(self): + return self._number + + @number.setter + def number(self, number): + self._number = number + self._spacing = None + self._values = None + + @property + def spacing(self): + return self._spacing + + @spacing.setter + def spacing(self, spacing): + self._number = None + self._spacing = spacing + self._values = None + + def minor_locator(self, spacing, frequency, value_min, value_max): + if self.values is not None: + return [] * self._unit + + minor_spacing = spacing.value / frequency + values = self._locate_values(value_min, value_max, minor_spacing) + index = np.where((values % frequency) == 0) + index = index[0][0] + values = np.delete(values, np.s_[index::frequency]) + return values * minor_spacing * self._unit + + @property + def format_unit(self): + return self._format_unit + + @format_unit.setter + def format_unit(self, unit): + self._format_unit = u.Unit(unit) + + @staticmethod + def _locate_values(value_min, value_max, spacing): + imin = np.ceil(value_min / spacing) + imax = np.floor(value_max / spacing) + return np.arange(imin, imax + 1, dtype=int) + + +class AngleFormatterLocator(BaseFormatterLocator): + """ + A joint formatter/locator. + + Parameters + ---------- + number : int, optional + Number of ticks. + """ + + def __init__( + self, + values=None, + number=None, + spacing=None, + format=None, + unit=None, + decimal=None, + format_unit=None, + show_decimal_unit=True, + ): + if unit is None: + unit = u.degree + + if format_unit is None: + format_unit = unit + + if format_unit not in (u.degree, u.hourangle, u.hour): + if decimal is False: + raise UnitsError( + "Units should be degrees or hours when using non-decimal" + " (sexagesimal) mode" + ) + + self._decimal = decimal + self._sep = None + self.show_decimal_unit = show_decimal_unit + self._alwayssign = False + + super().__init__( + values=values, + number=number, + spacing=spacing, + format=format, + unit=unit, + format_unit=format_unit, + ) + + @property + def decimal(self): + decimal = self._decimal + if self.format_unit not in (u.degree, u.hourangle, u.hour): + if self._decimal is None: + decimal = True + elif self._decimal is False: + raise UnitsError( + "Units should be degrees or hours when using non-decimal" + " (sexagesimal) mode" + ) + elif self._decimal is None: + decimal = False + return decimal + + @decimal.setter + def decimal(self, value): + self._decimal = value + + @property + def spacing(self): + return self._spacing + + @spacing.setter + def spacing(self, spacing): + if spacing is not None and ( + not isinstance(spacing, u.Quantity) or spacing.unit.physical_type != "angle" + ): + raise TypeError( + "spacing should be an astropy.units.Quantity " + "instance with units of angle" + ) + self._number = None + self._spacing = spacing + self._values = None + + @property + def sep(self): + return self._sep + + @sep.setter + def sep(self, separator): + self._sep = separator + + @property + def format(self): + return self._format + + @format.setter + def format(self, value): + self._format = value + + if value is None: + return + + self._alwayssign = value.startswith("+") + if self._alwayssign: + value = value[1:] + + if DMS_RE.match(value) is not None: + self._decimal = False + self._format_unit = u.degree + if "." in value: + self._precision = len(value) - value.index(".") - 1 + self._fields = 3 + else: + self._precision = 0 + self._fields = value.count(":") + 1 + elif HMS_RE.match(value) is not None: + self._decimal = False + self._format_unit = u.hourangle + if "." in value: + self._precision = len(value) - value.index(".") - 1 + self._fields = 3 + else: + self._precision = 0 + self._fields = value.count(":") + 1 + elif DDEC_RE.match(value) is not None: + self._decimal = True + self._format_unit = u.degree + self._fields = 1 + if "." in value: + self._precision = len(value) - value.index(".") - 1 + else: + self._precision = 0 + elif DMIN_RE.match(value) is not None: + self._decimal = True + self._format_unit = u.arcmin + self._fields = 1 + if "." in value: + self._precision = len(value) - value.index(".") - 1 + else: + self._precision = 0 + elif DSEC_RE.match(value) is not None: + self._decimal = True + self._format_unit = u.arcsec + self._fields = 1 + if "." in value: + self._precision = len(value) - value.index(".") - 1 + else: + self._precision = 0 + else: + raise ValueError(f"Invalid format: {value}") + + if self.spacing is not None and self.spacing < self.base_spacing: + warnings.warn("Spacing is too small - resetting spacing to match format") + self.spacing = self.base_spacing + + if self.spacing is not None: + ratio = (self.spacing / self.base_spacing).decompose().value + remainder = ratio - np.round(ratio) + + if abs(remainder) > 1.0e-10: + warnings.warn( + "Spacing is not a multiple of base spacing - resetting spacing to" + " match format" + ) + self.spacing = self.base_spacing * max(1, round(ratio)) + + @property + def base_spacing(self): + if self.decimal: + spacing = self._format_unit / (10.0**self._precision) + + else: + if self._fields == 1: + spacing = 1.0 * u.degree + elif self._fields == 2: + spacing = 1.0 * u.arcmin + elif self._fields == 3: + if self._precision == 0: + spacing = 1.0 * u.arcsec + else: + spacing = u.arcsec / (10.0**self._precision) + + if self._format_unit is u.hourangle: + spacing *= 15 + + return spacing + + def locator(self, value_min, value_max): + if self.values is not None: + # values were manually specified + return self.values, 1.1 * u.arcsec + + else: + # In the special case where value_min is the same as value_max, we + # don't locate any ticks. This can occur for example when taking a + # slice for a cube (along the dimension sliced). We return a + # non-zero spacing in case the caller needs to format a single + # coordinate, e.g. for mousover. + if value_min == value_max: + return [] * self._unit, 1 * u.arcsec + + if self.spacing is not None: + # spacing was manually specified + spacing_value = self.spacing.to_value(self._unit) + + elif self.number == 0: + return [] * self._unit, np.nan * self._unit + + elif self.number is not None: + # number of ticks was specified, work out optimal spacing + + # first compute the exact spacing + dv = abs(float(value_max - value_min)) / self.number * self._unit + + if self.format is not None and dv < self.base_spacing: + # if the spacing is less than the minimum spacing allowed by the format, simply + # use the format precision instead. + spacing_value = self.base_spacing.to_value(self._unit) + else: + # otherwise we clip to the nearest 'sensible' spacing + if self.decimal: + from .utils import select_step_scalar + + spacing_value = select_step_scalar( + dv.to_value(self._format_unit) + ) * self._format_unit.to(self._unit) + else: + if self._format_unit is u.degree: + from .utils import select_step_degree + + spacing_value = select_step_degree(dv).to_value(self._unit) + else: + from .utils import select_step_hour + + spacing_value = select_step_hour(dv).to_value(self._unit) + + # We now find the interval values as multiples of the spacing and + # generate the tick positions from this. + values = self._locate_values(value_min, value_max, spacing_value) + return values * spacing_value * self._unit, spacing_value * self._unit + + def formatter(self, values, spacing, format="auto"): + if not isinstance(values, u.Quantity) and values is not None: + raise TypeError("values should be a Quantities array") + + if len(values) > 0: + decimal = self.decimal + unit = self._format_unit + + if unit is u.hour: + unit = u.hourangle + + if self.format is None: + if decimal: + # Here we assume the spacing can be arbitrary, so for example + # 1.000223 degrees, in which case we don't want to have a + # format that rounds to degrees. So we find the number of + # decimal places we get from representing the spacing as a + # string in the desired units. The easiest way to find + # the smallest number of decimal places required is to + # format the number as a decimal float and strip any zeros + # from the end. We do this rather than just trusting e.g. + # str() because str(15.) == 15.0. We format using 10 decimal + # places by default before stripping the zeros since this + # corresponds to a resolution of less than a microarcsecond, + # which should be sufficient. + spacing = spacing.to_value(unit) + fields = 0 + precision = len( + f"{spacing:.10f}".replace("0", " ").strip().split(".", 1)[1] + ) + else: + spacing = spacing.to_value(unit / 3600) + if spacing >= 3600: + fields = 1 + precision = 0 + elif spacing >= 60: + fields = 2 + precision = 0 + elif spacing >= 1: + fields = 3 + precision = 0 + else: + fields = 3 + precision = -int(np.floor(np.log10(spacing))) + else: + fields = self._fields + precision = self._precision + + is_latex = format == "latex" or ( + format == "auto" and rcParams["text.usetex"] + ) + + if decimal: + if self.show_decimal_unit: + sep = "fromunit" + if is_latex: + fmt = "latex" + else: + if unit is u.hourangle: + fmt = "unicode" + else: + fmt = "generic" + unit = CUSTOM_UNITS.get(unit, unit) + else: + sep = "fromunit" + fmt = None + elif self.sep is not None: + sep = self.sep + fmt = None + else: + sep = "fromunit" + if unit == u.degree: + if is_latex: + fmt = "latex" + else: + sep = ("\xb0", "'", '"') + fmt = None + else: + if format == "ascii": + fmt = None + elif is_latex: + fmt = "latex" + else: + # Here we still use LaTeX but this is for Matplotlib's + # LaTeX engine - we can't use fmt='latex' as this + # doesn't produce LaTeX output that respects the fonts. + sep = ( + r"$\mathregular{^h}$", + r"$\mathregular{^m}$", + r"$\mathregular{^s}$", + ) + fmt = None + + angles = Angle(values) + string = angles.to_string( + unit=unit, + precision=precision, + decimal=decimal, + fields=fields, + sep=sep, + format=fmt, + alwayssign=self._alwayssign, + ).tolist() + + return _fix_minus(string) + else: + return [] + + +class ScalarFormatterLocator(BaseFormatterLocator): + """ + A joint formatter/locator. + """ + + def __init__( + self, + values=None, + number=None, + spacing=None, + format=None, + unit=None, + format_unit=None, + ): + if unit is None: + if spacing is not None: + unit = spacing.unit + elif values is not None: + unit = values.unit + format_unit = format_unit or unit + + super().__init__( + values=values, + number=number, + spacing=spacing, + format=format, + unit=unit, + format_unit=format_unit, + ) + + @property + def spacing(self): + return self._spacing + + @spacing.setter + def spacing(self, spacing): + if spacing is not None and not isinstance(spacing, u.Quantity): + raise TypeError("spacing should be an astropy.units.Quantity instance") + self._number = None + self._spacing = spacing + self._values = None + + @property + def format(self): + return self._format + + @format.setter + def format(self, value): + self._format = value + + if value is None: + return + + if SCAL_RE.match(value) is not None: + if "." in value: + self._precision = len(value) - value.index(".") - 1 + else: + self._precision = 0 + + if self.spacing is not None and self.spacing < self.base_spacing: + warnings.warn( + "Spacing is too small - resetting spacing to match format" + ) + self.spacing = self.base_spacing + + if self.spacing is not None: + ratio = (self.spacing / self.base_spacing).decompose().value + remainder = ratio - np.round(ratio) + + if abs(remainder) > 1.0e-10: + warnings.warn( + "Spacing is not a multiple of base spacing - resetting spacing" + " to match format" + ) + self.spacing = self.base_spacing * max(1, round(ratio)) + + elif not value.startswith("%"): + raise ValueError(f"Invalid format: {value}") + + @property + def base_spacing(self): + return self._format_unit / (10.0**self._precision) + + def locator(self, value_min, value_max): + if self.values is not None: + # values were manually specified + return self.values, 1.1 * self._unit + else: + # In the special case where value_min is the same as value_max, we + # don't locate any ticks. This can occur for example when taking a + # slice for a cube (along the dimension sliced). + if value_min == value_max: + return [] * self._unit, 0 * self._unit + + if self.spacing is not None: + # spacing was manually specified + spacing = self.spacing.to_value(self._unit) + + elif self.number is not None: + # number of ticks was specified, work out optimal spacing + + # first compute the exact spacing + dv = abs(float(value_max - value_min)) / self.number * self._unit + + if ( + self.format is not None + and (not self.format.startswith("%")) + and dv < self.base_spacing + ): + # if the spacing is less than the minimum spacing allowed by the format, simply + # use the format precision instead. + spacing = self.base_spacing.to_value(self._unit) + else: + from .utils import select_step_scalar + + spacing = select_step_scalar( + dv.to_value(self._format_unit) + ) * self._format_unit.to(self._unit) + + # We now find the interval values as multiples of the spacing and + # generate the tick positions from this + + values = self._locate_values(value_min, value_max, spacing) + return values * spacing * self._unit, spacing * self._unit + + def formatter(self, values, spacing, format="auto"): + if len(values) > 0: + if self.format is None: + if spacing.value < 1.0: + precision = -int(np.floor(np.log10(spacing.value))) + else: + precision = 0 + elif self.format.startswith("%"): + return [(self.format % x.value) for x in values] + else: + precision = self._precision + + return _fix_minus( + [ + ("{0:." + str(precision) + "f}").format( + x.to_value(self._format_unit) + ) + for x in values + ] + ) + + else: + return [] diff --git a/astropy/visualization/wcsaxes/frame.py b/astropy/visualization/wcsaxes/frame.py new file mode 100644 index 000000000000..3b875c960a07 --- /dev/null +++ b/astropy/visualization/wcsaxes/frame.py @@ -0,0 +1,444 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + + +import abc +import warnings +from collections import OrderedDict + +import numpy as np +from matplotlib import rcParams +from matplotlib.lines import Line2D, Path +from matplotlib.patches import PathPatch + +from astropy.utils.exceptions import AstropyDeprecationWarning + +__all__ = [ + "BaseFrame", + "EllipticalFrame", + "RectangularFrame", + "RectangularFrame1D", + "Spine", +] + + +class Spine: + """ + A single side of an axes. + + This does not need to be a straight line, but represents a 'side' when + determining which part of the frame to put labels and ticks on. + + Parameters + ---------- + parent_axes : `~astropy.visualization.wcsaxes.WCSAxes` + The parent axes + transform : `~matplotlib.transforms.Transform` + The transform from data to world + data_func : callable + If not ``None``, it should be a function that returns the appropriate spine + data when called with this object as the sole argument. If ``None``, the + spine data must be manually updated in ``update_spines()``. + """ + + def __init__(self, parent_axes, transform, *, data_func=None): + self.parent_axes = parent_axes + self.transform = transform + self.data_func = data_func + + self._data = None + self._world = None + + @property + def data(self): + if self._data is None and self.data_func: + self.data = self.data_func(self) + return self._data + + @data.setter + def data(self, value): + self._data = value + if value is None: + self._world = None + else: + with np.errstate(invalid="ignore"): + self._world = self.transform.transform(self._data) + self._update_normal() + + def _get_pixel(self): + return self.parent_axes.transData.transform(self._data) + + @property + def pixel(self): + warnings.warn( + "Pixel coordinates cannot be accurately calculated unless " + "Matplotlib is currently drawing a figure, so the .pixel " + "attribute is deprecated and will be removed in a future " + "astropy release.", + AstropyDeprecationWarning, + ) + return self._get_pixel() + + @pixel.setter + def pixel(self, value): + warnings.warn( + "Manually setting pixel values of a Spine can lead to incorrect results " + "as these can only be accurately calculated when Matplotlib is drawing " + "a figure. As such the .pixel setter now does nothing, is deprecated, " + "and will be removed in a future astropy release.", + AstropyDeprecationWarning, + ) + + @property + def world(self): + return self._world + + @world.setter + def world(self, value): + self._world = value + if value is None: + self._data = None + self._pixel = None + else: + self._data = self.transform.transform(value) + self._pixel = self.parent_axes.transData.transform(self._data) + self._update_normal() + + def _update_normal(self): + pixel = self._get_pixel() + # Find angle normal to border and inwards, in display coordinate + dx = pixel[1:, 0] - pixel[:-1, 0] + dy = pixel[1:, 1] - pixel[:-1, 1] + self.normal_angle = np.degrees(np.arctan2(dx, -dy)) + + def _halfway_x_y_angle(self): + """ + Return the x, y, normal_angle values halfway along the spine. + """ + pixel = self._get_pixel() + x_disp, y_disp = pixel[:, 0], pixel[:, 1] + # Get distance along the path + d = np.hstack( + [0.0, np.cumsum(np.sqrt(np.diff(x_disp) ** 2 + np.diff(y_disp) ** 2))] + ) + xcen = np.interp(d[-1] / 2.0, d, x_disp) + ycen = np.interp(d[-1] / 2.0, d, y_disp) + + # Find segment along which the mid-point lies + imin = np.searchsorted(d, d[-1] / 2.0) - 1 + + # Find normal of the axis label facing outwards on that segment + normal_angle = self.normal_angle[imin] + 180.0 + return xcen, ycen, normal_angle + + +class SpineXAligned(Spine): + """ + A single side of an axes, aligned with the X data axis. + + This does not need to be a straight line, but represents a 'side' when + determining which part of the frame to put labels and ticks on. + """ + + @property + def data(self): + return self._data + + @data.setter + def data(self, value): + self._data = value + if value is None: + self._world = None + else: + with np.errstate(invalid="ignore"): + self._world = self.transform.transform(self._data[:, 0:1]) + self._update_normal() + + +class BaseFrame(OrderedDict, metaclass=abc.ABCMeta): + """ + Base class for frames, which are collections of + :class:`~astropy.visualization.wcsaxes.frame.Spine` instances. + """ + + spine_class = Spine + + def __init__(self, parent_axes, transform, path=None): + super().__init__() + + self.parent_axes = parent_axes + self._transform = transform + self._linewidth = rcParams["axes.linewidth"] + self._color = rcParams["axes.edgecolor"] + self._path = path + + for axis in self.spine_names: + self[axis] = self.spine_class(parent_axes, transform) + + @property + def origin(self): + ymin, ymax = self.parent_axes.get_ylim() + return "lower" if ymin < ymax else "upper" + + @property + def transform(self): + return self._transform + + @transform.setter + def transform(self, value): + self._transform = value + for axis in self: + self[axis].transform = value + + def _update_patch_path(self): + self.update_spines() + x, y = [], [] + for axis in self.spine_names: + x.append(self[axis].data[:, 0]) + y.append(self[axis].data[:, 1]) + vertices = np.vstack([np.hstack(x), np.hstack(y)]).transpose() + + if self._path is None: + self._path = Path(vertices) + else: + self._path.vertices = vertices + + @property + def patch(self): + self._update_patch_path() + return PathPatch( + self._path, + transform=self.parent_axes.transData, + facecolor=rcParams["axes.facecolor"], + edgecolor="white", + ) + + def draw(self, renderer): + for axis in self: + pixel = self[axis]._get_pixel() + x, y = pixel[:, 0], pixel[:, 1] + line = Line2D( + x, y, linewidth=self._linewidth, color=self._color, zorder=1000 + ) + line.draw(renderer) + + def sample(self, n_samples): + self.update_spines() + + spines = OrderedDict() + + for axis in self: + data = self[axis].data + spines[axis] = self.spine_class(self.parent_axes, self.transform) + if data.size > 0: + p = np.linspace(0.0, 1.0, data.shape[0]) + p_new = np.linspace(0.0, 1.0, n_samples) + spines[axis].data = np.array( + [np.interp(p_new, p, d) for d in data.T] + ).transpose() + else: + spines[axis].data = data + + return spines + + def set_color(self, color): + """ + Sets the color of the frame. + + Parameters + ---------- + color : str + The color of the frame. + """ + self._color = color + + def get_color(self): + return self._color + + def set_linewidth(self, linewidth): + """ + Sets the linewidth of the frame. + + Parameters + ---------- + linewidth : float + The linewidth of the frame in points. + """ + self._linewidth = linewidth + + def get_linewidth(self): + return self._linewidth + + def update_spines(self): + for spine in self.values(): + if spine.data_func: + spine.data = spine.data_func(spine) + + def _validate_positions(self, positions): + """ + Given a string with single character positions or an iterable with + single or multi-character positions, emit a warning for any + unrecognized positions and return a list of valid positions. + """ + if positions == "all": + return positions + + valid_positions = [] + invalid_positions = [] + for position in positions: + if position in self or position == "#": + valid_positions.append(position) + else: + invalid_positions.append(position) + + if invalid_positions: + if isinstance(positions, str) and positions in self: + hint = ( + f"It looks like '{positions}' matches the name of a single " + f"axis. If you are trying to specify a multi-character axis " + f"name, use a list or a tuple, e.g. ('{positions}',)." + ) + else: + hint = "" + + warnings.warn( + f"Ignoring unrecognized position(s): {invalid_positions}, should " + f"be one of {'/'.join(self.keys())}. In future this will " + f"raise an error. {hint}", + AstropyDeprecationWarning, + ) + + return valid_positions + + +class RectangularFrame1D(BaseFrame): + """ + A classic rectangular frame. + """ + + spine_names = "bt" + _spine_auto_position_order = "bt" + spine_class = SpineXAligned + + def update_spines(self): + xmin, xmax = self.parent_axes.get_xlim() + ymin, ymax = self.parent_axes.get_ylim() + + self["b"].data = np.array(([xmin, ymin], [xmax, ymin])) + self["t"].data = np.array(([xmax, ymax], [xmin, ymax])) + + super().update_spines() + + def _update_patch_path(self): + self.update_spines() + + xmin, xmax = self.parent_axes.get_xlim() + ymin, ymax = self.parent_axes.get_ylim() + + x = [xmin, xmax, xmax, xmin, xmin] + y = [ymin, ymin, ymax, ymax, ymin] + + vertices = np.vstack([np.hstack(x), np.hstack(y)]).transpose() + + if self._path is None: + self._path = Path(vertices) + else: + self._path.vertices = vertices + + def draw(self, renderer): + xmin, xmax = self.parent_axes.get_xlim() + ymin, ymax = self.parent_axes.get_ylim() + + x = [xmin, xmax, xmax, xmin, xmin] + y = [ymin, ymin, ymax, ymax, ymin] + + line = Line2D( + x, + y, + linewidth=self._linewidth, + color=self._color, + zorder=1000, + transform=self.parent_axes.transData, + ) + line.draw(renderer) + + +class RectangularFrame(BaseFrame): + """ + A classic rectangular frame. + """ + + spine_names = "brtl" + _spine_auto_position_order = "bltr" + + def update_spines(self): + xmin, xmax = self.parent_axes.get_xlim() + ymin, ymax = self.parent_axes.get_ylim() + + self["b"].data = np.array(([xmin, ymin], [xmax, ymin])) + self["r"].data = np.array(([xmax, ymin], [xmax, ymax])) + self["t"].data = np.array(([xmax, ymax], [xmin, ymax])) + self["l"].data = np.array(([xmin, ymax], [xmin, ymin])) + + super().update_spines() + + +class EllipticalFrame(BaseFrame): + """ + An elliptical frame. + """ + + spine_names = "chv" + _spine_auto_position_order = "chv" + + def update_spines(self): + xmin, xmax = self.parent_axes.get_xlim() + ymin, ymax = self.parent_axes.get_ylim() + + xmid = 0.5 * (xmax + xmin) + ymid = 0.5 * (ymax + ymin) + + dx = xmid - xmin + dy = ymid - ymin + + theta = np.linspace(0.0, 2 * np.pi, 1000) + self["c"].data = np.array( + [xmid + dx * np.cos(theta), ymid + dy * np.sin(theta)] + ).transpose() + self["h"].data = np.array( + [np.linspace(xmin, xmax, 1000), np.repeat(ymid, 1000)] + ).transpose() + self["v"].data = np.array( + [np.repeat(xmid, 1000), np.linspace(ymin, ymax, 1000)] + ).transpose() + + super().update_spines() + + def _update_patch_path(self): + """Override path patch to include only the outer ellipse, + not the major and minor axes in the middle. + """ + self.update_spines() + vertices = self["c"].data + + if self._path is None: + self._path = Path(vertices) + else: + self._path.vertices = vertices + + def draw(self, renderer): + """Override to draw only the outer ellipse, + not the major and minor axes in the middle. + + FIXME: we may want to add a general method to give the user control + over which spines are drawn. + """ + axis = "c" + pixel = self[axis]._get_pixel() + line = Line2D( + pixel[:, 0], + pixel[:, 1], + linewidth=self._linewidth, + color=self._color, + zorder=1000, + ) + line.draw(renderer) diff --git a/astropy/visualization/wcsaxes/grid_paths.py b/astropy/visualization/wcsaxes/grid_paths.py new file mode 100644 index 000000000000..0e79e54f9f5e --- /dev/null +++ b/astropy/visualization/wcsaxes/grid_paths.py @@ -0,0 +1,120 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + + +import numpy as np +from matplotlib.lines import Path + +from astropy.coordinates import angular_separation + +# Tolerance for WCS round-tripping, relative to the scale size +ROUND_TRIP_RTOL = 1.0 + +# Tolerance for discontinuities relative to the median +DISCONT_FACTOR = 10.0 + + +def get_lon_lat_path(lon_lat, pixel, lon_lat_check): + """ + Draw a curve, taking into account discontinuities. + + Parameters + ---------- + lon_lat : ndarray + The longitude and latitude values along the curve, given as a (n,2) + array. + pixel : ndarray + The pixel coordinates corresponding to ``lon_lat`` + lon_lat_check : ndarray + The world coordinates derived from converting from ``pixel``, which is + used to ensure round-tripping. + """ + # In some spherical projections, some parts of the curve are 'behind' or + # 'in front of' the plane of the image, so we find those by reversing the + # transformation and finding points where the result is not consistent. + + sep = angular_separation( + np.radians(lon_lat[:, 0]), + np.radians(lon_lat[:, 1]), + np.radians(lon_lat_check[:, 0]), + np.radians(lon_lat_check[:, 1]), + ) + + # Define the relevant scale size using the separation between the first two points + scale_size = angular_separation( + *np.radians(lon_lat[0, :]), *np.radians(lon_lat[1, :]) + ) + + with np.errstate(invalid="ignore"): + sep[sep > np.pi] -= 2.0 * np.pi + + mask = np.abs(sep > ROUND_TRIP_RTOL * scale_size) + + # Mask values with invalid pixel positions + mask = mask | np.isnan(pixel[:, 0]) | np.isnan(pixel[:, 1]) + + # We can now start to set up the codes for the Path. + codes = np.zeros(lon_lat.shape[0], dtype=np.uint8) + codes[:] = Path.LINETO + codes[0] = Path.MOVETO + codes[mask] = Path.MOVETO + + # Also need to move to point *after* a hidden value + codes[1:][mask[:-1]] = Path.MOVETO + + # We now go through and search for discontinuities in the curve that would + # be due to the curve going outside the field of view, invalid WCS values, + # or due to discontinuities in the projection. + + # We start off by pre-computing the step in pixel coordinates from one + # point to the next. The idea is to look for large jumps that might indicate + # discontinuities. + step = np.sqrt( + (pixel[1:, 0] - pixel[:-1, 0]) ** 2 + (pixel[1:, 1] - pixel[:-1, 1]) ** 2 + ) + + # We search for discontinuities by looking for places where the step + # is larger by more than a given factor compared to the median + # discontinuous = step > DISCONT_FACTOR * np.median(step) + discontinuous = step[1:] > DISCONT_FACTOR * step[:-1] + + # Skip over discontinuities + codes[2:][discontinuous] = Path.MOVETO + + # The above missed the first step, so check that too + if step[0] > DISCONT_FACTOR * step[1]: + codes[1] = Path.MOVETO + + # Create the path + return Path(pixel, codes=codes) + + +def get_gridline_path(world, pixel): + """ + Draw a grid line. + + Parameters + ---------- + world : ndarray + The longitude and latitude values along the curve, given as a (n,2) + array. + pixel : ndarray + The pixel coordinates corresponding to ``lon_lat`` + """ + # Mask values with invalid pixel positions + mask = np.isnan(pixel[:, 0]) | np.isnan(pixel[:, 1]) + + # We can now start to set up the codes for the Path. + codes = np.zeros(world.shape[0], dtype=np.uint8) + codes[:] = Path.LINETO + codes[0] = Path.MOVETO + codes[mask] = Path.MOVETO + + # Also need to move to point *after* a hidden value + codes[1:][mask[:-1]] = Path.MOVETO + + # We now go through and search for discontinuities in the curve that would + # be due to the curve going outside the field of view, invalid WCS values, + # or due to discontinuities in the projection. + + # Create the path + return Path(pixel, codes=codes) diff --git a/astropy/visualization/wcsaxes/helpers.py b/astropy/visualization/wcsaxes/helpers.py new file mode 100644 index 000000000000..900b20bcdde3 --- /dev/null +++ b/astropy/visualization/wcsaxes/helpers.py @@ -0,0 +1,200 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +""" +Helpers functions for different kinds of WCSAxes instances. +""" + +import numpy as np +from matplotlib.offsetbox import AnchoredOffsetbox, AuxTransformBox +from matplotlib.patches import Ellipse +from mpl_toolkits.axes_grid1.anchored_artists import AnchoredSizeBar + +import astropy.units as u +from astropy.wcs.utils import proj_plane_pixel_scales + +__all__ = ["add_beam", "add_scalebar"] + +CORNERS = { + "top right": 1, + "top left": 2, + "bottom left": 3, + "bottom right": 4, + "right": 5, + "left": 6, + "bottom": 8, + "top": 9, +} + + +def add_beam( + ax, + header=None, + major=None, + minor=None, + angle=None, + corner="bottom left", + frame=False, + borderpad=0.4, + pad=0.5, + **kwargs, +): + """ + Display the beam shape and size. + + Parameters + ---------- + ax : :class:`~astropy.visualization.wcsaxes.WCSAxes` + WCSAxes instance in which the beam shape and size is displayed. The WCS + must be celestial. + header : :class:`~astropy.io.fits.Header`, optional + Header containing the beam parameters. If specified, the ``BMAJ``, + ``BMIN``, and ``BPA`` keywords will be searched in the FITS header + to set the major and minor axes and the position angle on the sky. + major : float or :class:`~astropy.units.Quantity`, optional + Major axis of the beam in degrees or an angular quantity. + minor : float, or :class:`~astropy.units.Quantity`, optional + Minor axis of the beam in degrees or an angular quantity. + angle : float or :class:`~astropy.units.Quantity`, optional + Position angle of the beam on the sky in degrees or an angular + quantity in the anticlockwise direction. + corner : str, optional + The beam location. Acceptable values are ``'left'``, ``'right'``, + ``'top'``, 'bottom', ``'top left'``, ``'top right'``, ``'bottom left'`` + (default), and ``'bottom right'``. + frame : bool, optional + Whether to display a frame behind the beam (default is ``False``). + borderpad : float, optional + Border padding, in fraction of the font size. Default is 0.4. + pad : float, optional + Padding around the beam, in fraction of the font size. Default is 0.5. + kwargs + Additional arguments are passed to :class:`matplotlib.patches.Ellipse`. + + Notes + ----- + This function may be inaccurate when: + + - The pixel scales at the reference pixel are different from the pixel scales + within the image extent (e.g., when the reference pixel is well outside of + the image extent and the projection is non-linear) + - The pixel scales in the two directions are very different from each other + (e.g., rectangular pixels) + + """ + if header and major: + raise ValueError( + "Either header or major/minor/angle must be specified, not both." + ) + + if header: + major = header["BMAJ"] + minor = header["BMIN"] + angle = header["BPA"] + + if isinstance(major, u.Quantity): + major = major.to(u.degree).value + + if isinstance(minor, u.Quantity): + minor = minor.to(u.degree).value + + if isinstance(angle, u.Quantity): + angle = angle.to(u.degree).value + + if ax.wcs.is_celestial: + pix_scale = proj_plane_pixel_scales(ax.wcs) + sx = pix_scale[0] + sy = pix_scale[1] + degrees_per_pixel = np.sqrt(sx * sy) + else: + raise ValueError("Cannot show beam when WCS is not celestial") + + minor /= degrees_per_pixel + major /= degrees_per_pixel + + aux_tr_box = AuxTransformBox(ax.transData) + ellipse = Ellipse((0, 0), width=minor, height=major, angle=angle, **kwargs) + aux_tr_box.add_artist(ellipse) + box = AnchoredOffsetbox( + child=aux_tr_box, + pad=pad, + borderpad=borderpad, + loc=CORNERS[corner], + frameon=frame, + ) + ax.add_artist(box) + + +def add_scalebar( + ax, + length, + label=None, + corner="bottom right", + frame=False, + borderpad=0.4, + pad=0.5, + **kwargs, +): + """Add a scale bar. + + Parameters + ---------- + ax : :class:`~astropy.visualization.wcsaxes.WCSAxes` + WCSAxes instance in which the scale bar is displayed. The WCS must be + celestial. + length : float or :class:`~astropy.units.Quantity` + The length of the scalebar in degrees or an angular quantity + label : str, optional + Label to place below the scale bar + corner : str, optional + Where to place the scale bar. Acceptable values are:, ``'left'``, + ``'right'``, ``'top'``, ``'bottom'``, ``'top left'``, ``'top right'``, + ``'bottom left'`` and ``'bottom right'`` (default) + frame : bool, optional + Whether to display a frame behind the scale bar (default is ``False``) + borderpad : float, optional + Border padding, in fraction of the font size. Default is 0.4. + pad : float, optional + Padding around the scale bar, in fraction of the font size. Default is 0.5. + kwargs + Additional arguments are passed to + :class:`mpl_toolkits.axes_grid1.anchored_artists.AnchoredSizeBar`. + + Notes + ----- + This function may be inaccurate when: + + - The pixel scales at the reference pixel are different from the pixel scales + within the image extent (e.g., when the reference pixel is well outside of + the image extent and the projection is non-linear) + - The pixel scales in the two directions are very different from each other + (e.g., rectangular pixels) + + """ + if isinstance(length, u.Quantity): + length = length.to(u.degree).value + + if ax.wcs.is_celestial: + pix_scale = proj_plane_pixel_scales(ax.wcs) + sx = pix_scale[0] + sy = pix_scale[1] + degrees_per_pixel = np.sqrt(sx * sy) + else: + raise ValueError("Cannot show scalebar when WCS is not celestial") + + length = length / degrees_per_pixel + + corner = CORNERS[corner] + + scalebar = AnchoredSizeBar( + ax.transData, + length, + label, + corner, + pad=pad, + borderpad=borderpad, + sep=5, + frameon=frame, + **kwargs, + ) + + ax.add_artist(scalebar) diff --git a/astropy/visualization/wcsaxes/patches.py b/astropy/visualization/wcsaxes/patches.py new file mode 100644 index 000000000000..cdf2f25904c4 --- /dev/null +++ b/astropy/visualization/wcsaxes/patches.py @@ -0,0 +1,197 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + + +import sys +import warnings + +import numpy as np +from matplotlib.patches import Polygon + +from astropy import units as u +from astropy.coordinates import SkyCoord, rotation_matrix +from astropy.coordinates.representation import ( + SphericalRepresentation, + UnitSphericalRepresentation, +) +from astropy.utils.exceptions import AstropyUserWarning + +__all__ = ["Quadrangle", "SphericalCircle"] + +# Monkey-patch the docs to fix CapStyle and JoinStyle subs. +# TODO! delete when upstream fix matplotlib/matplotlib#19839 +if sys.flags.optimize < 2: + Polygon.__init__.__doc__ = Polygon.__init__.__doc__.replace( + "`.CapStyle`", "``matplotlib._enums.CapStyle``" + ) + Polygon.__init__.__doc__ = Polygon.__init__.__doc__.replace( + "`.JoinStyle`", "``matplotlib._enums.JoinStyle``" + ) + Polygon.set_capstyle.__doc__ = Polygon.set_capstyle.__doc__.replace( + "`.CapStyle`", "``matplotlib._enums.CapStyle``" + ) + Polygon.set_joinstyle.__doc__ = Polygon.set_joinstyle.__doc__.replace( + "`.JoinStyle`", "``matplotlib._enums.JoinStyle``" + ) + + +def _rotate_polygon(lon, lat, lon0, lat0): + """ + Given a polygon with vertices defined by (lon, lat), rotate the polygon + such that the North pole of the spherical coordinates is now at (lon0, + lat0). Therefore, to end up with a polygon centered on (lon0, lat0), the + polygon should initially be drawn around the North pole. + """ + # Create a representation object + polygon = UnitSphericalRepresentation(lon=lon, lat=lat) + + # Determine rotation matrix to make it so that the circle is centered + # on the correct longitude/latitude. + transform_matrix = rotation_matrix(-lon0, axis="z") @ rotation_matrix( + -(0.5 * np.pi * u.radian - lat0), axis="y" + ) + + # Apply 3D rotation + polygon = polygon.to_cartesian() + polygon = polygon.transform(transform_matrix) + polygon = UnitSphericalRepresentation.from_cartesian(polygon) + + return polygon.lon, polygon.lat + + +class SphericalCircle(Polygon): + """ + Create a patch representing a spherical circle - that is, a circle that is + formed of all the points that are within a certain angle of the central + coordinates on a sphere. Here we assume that latitude goes from -90 to +90. + + This class is needed in cases where the user wants to add a circular patch + to a celestial image, since otherwise the circle will be distorted, because + a fixed interval in longitude corresponds to a different angle on the sky + depending on the latitude. + + Parameters + ---------- + center : tuple or `~astropy.units.Quantity` ['angle'] + This can be either a tuple of two `~astropy.units.Quantity` objects, or + a single `~astropy.units.Quantity` array with two elements + or a `~astropy.coordinates.SkyCoord` object. + radius : `~astropy.units.Quantity` ['angle'] + The radius of the circle + resolution : int, optional + The number of points that make up the circle - increase this to get a + smoother circle. + vertex_unit : `~astropy.units.Unit` + The units in which the resulting polygon should be defined - this + should match the unit that the transformation (e.g. the WCS + transformation) expects as input. + + Notes + ----- + Additional keyword arguments are passed to `~matplotlib.patches.Polygon` + """ + + def __init__(self, center, radius, resolution=100, vertex_unit=u.degree, **kwargs): + # Extract longitude/latitude, either from a SkyCoord object, or + # from a tuple of two quantities or a single 2-element Quantity. + # The SkyCoord is converted to SphericalRepresentation, if not already. + if isinstance(center, SkyCoord): + rep_type = center.representation_type + if not issubclass( + rep_type, (SphericalRepresentation, UnitSphericalRepresentation) + ): + warnings.warn( + f"Received `center` of representation type {rep_type} " + "will be converted to SphericalRepresentation ", + AstropyUserWarning, + ) + longitude, latitude = center.spherical.lon, center.spherical.lat + else: + longitude, latitude = center + + # Start off by generating the circle around the North pole + lon = np.linspace(0.0, 2 * np.pi, resolution + 1)[:-1] * u.radian + lat = np.repeat(0.5 * np.pi - radius.to_value(u.radian), resolution) * u.radian + + lon, lat = _rotate_polygon(lon, lat, longitude, latitude) + + # Extract new longitude/latitude in the requested units + lon = lon.to_value(vertex_unit) + lat = lat.to_value(vertex_unit) + + # Create polygon vertices + vertices = np.array([lon, lat]).transpose() + + super().__init__(vertices, **kwargs) + + +class Quadrangle(Polygon): + """ + Create a patch representing a latitude-longitude quadrangle. + + The edges of the quadrangle lie on two lines of constant longitude and two + lines of constant latitude (or the equivalent component names in the + coordinate frame of interest, such as right ascension and declination). + Note that lines of constant latitude are not great circles. + + Unlike `matplotlib.patches.Rectangle`, the edges of this patch will render + as curved lines if appropriate for the WCS transformation. + + Parameters + ---------- + anchor : tuple or `~astropy.units.Quantity` ['angle'] + This can be either a tuple of two `~astropy.units.Quantity` objects, or + a single `~astropy.units.Quantity` array with two elements. + width : `~astropy.units.Quantity` ['angle'] + The width of the quadrangle in longitude (or, e.g., right ascension) + height : `~astropy.units.Quantity` ['angle'] + The height of the quadrangle in latitude (or, e.g., declination) + resolution : int, optional + The number of points that make up each side of the quadrangle - + increase this to get a smoother quadrangle. + vertex_unit : `~astropy.units.Unit` ['angle'] + The units in which the resulting polygon should be defined - this + should match the unit that the transformation (e.g. the WCS + transformation) expects as input. + + Notes + ----- + Additional keyword arguments are passed to `~matplotlib.patches.Polygon` + """ + + def __init__( + self, anchor, width, height, resolution=100, vertex_unit=u.degree, **kwargs + ): + # Extract longitude/latitude, either from a tuple of two quantities, or + # a single 2-element Quantity. + longitude, latitude = u.Quantity(anchor).to_value(vertex_unit) + + # Convert the quadrangle dimensions to the appropriate units + width = width.to_value(vertex_unit) + height = height.to_value(vertex_unit) + + # Create progressions in longitude and latitude + lon_seq = longitude + np.linspace(0, width, resolution + 1) + lat_seq = latitude + np.linspace(0, height, resolution + 1) + + # Trace the path of the quadrangle + lon = np.concatenate( + [ + lon_seq[:-1], + np.repeat(lon_seq[-1], resolution), + np.flip(lon_seq[1:]), + np.repeat(lon_seq[0], resolution), + ] + ) + lat = np.concatenate( + [ + np.repeat(lat_seq[0], resolution), + lat_seq[:-1], + np.repeat(lat_seq[-1], resolution), + np.flip(lat_seq[1:]), + ] + ) + + # Create polygon vertices + vertices = np.array([lon, lat]).transpose() + + super().__init__(vertices, **kwargs) diff --git a/astropy/visualization/wcsaxes/tests/__init__.py b/astropy/visualization/wcsaxes/tests/__init__.py new file mode 100644 index 000000000000..a308d4a70c68 --- /dev/null +++ b/astropy/visualization/wcsaxes/tests/__init__.py @@ -0,0 +1,8 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +# This sub-package makes use of image testing with the pytest-mpl package: +# +# https://pypi.org/project/pytest-mpl +# +# For more information on writing image tests, see the 'Image tests with +# pytest-mpl' section of the developer docs. diff --git a/astropy/visualization/wcsaxes/tests/data/2MASS_k_header b/astropy/visualization/wcsaxes/tests/data/2MASS_k_header new file mode 100644 index 000000000000..a6a883a25fb9 --- /dev/null +++ b/astropy/visualization/wcsaxes/tests/data/2MASS_k_header @@ -0,0 +1,14 @@ +WCSAXES = 2 / Number of coordinate axes +CRPIX1 = 361.0 / Pixel coordinate of reference point +CRPIX2 = 360.5 / Pixel coordinate of reference point +CDELT1 = -0.001388889 / [deg] Coordinate increment at reference point +CDELT2 = 0.001388889 / [deg] Coordinate increment at reference point +CUNIT1 = 'deg' / Units of coordinate increment and value +CUNIT2 = 'deg' / Units of coordinate increment and value +CTYPE1 = 'RA---TAN' / Right ascension, gnomonic projection +CTYPE2 = 'DEC--TAN' / Declination, gnomonic projection +CRVAL1 = 266.4 / [deg] Coordinate value at reference point +CRVAL2 = -28.93333 / [deg] Coordinate value at reference point +LONPOLE = 180.0 / [deg] Native longitude of celestial pole +LATPOLE = -28.93333 / [deg] Native latitude of celestial pole +EQUINOX = 2000.0 / [yr] Equinox of equatorial coordinates \ No newline at end of file diff --git a/astropy/visualization/wcsaxes/tests/data/cube_header b/astropy/visualization/wcsaxes/tests/data/cube_header new file mode 100644 index 000000000000..996aec252d56 --- /dev/null +++ b/astropy/visualization/wcsaxes/tests/data/cube_header @@ -0,0 +1,20 @@ +WCSAXES = 3 / Number of coordinate axes +CRPIX1 = -799.0 / Pixel coordinate of reference point +CRPIX2 = -4741.913 / Pixel coordinate of reference point +CRPIX3 = -187.0 / Pixel coordinate of reference point +CDELT1 = -0.006388889 / [deg] Coordinate increment at reference point +CDELT2 = 0.006388889 / [deg] Coordinate increment at reference point +CDELT3 = 66.42361 / [m s-1] Coordinate increment at reference point +CUNIT1 = 'deg' / Units of coordinate increment and value +CUNIT2 = 'deg' / Units of coordinate increment and value +CUNIT3 = 'm s-1' / Units of coordinate increment and value +CTYPE1 = 'RA---SFL' / Right ascension, Sanson-Flamsteed projection +CTYPE2 = 'DEC--SFL' / Declination, Sanson-Flamsteed projection +CTYPE3 = 'VOPT' / Optical velocity (linear) +CRVAL1 = 57.6599999999 / [deg] Coordinate value at reference point +CRVAL2 = 0.0 / [deg] Coordinate value at reference point +CRVAL3 = -9959.44378305 / [m s-1] Coordinate value at reference point +LONPOLE = 0.0 / [deg] Native longitude of celestial pole +LATPOLE = 90.0 / [deg] Native latitude of celestial pole +EQUINOX = 0.0 / [yr] Equinox of equatorial coordinates +SPECSYS = 'LSRK' / Reference frame of spectral coordinates \ No newline at end of file diff --git a/astropy/visualization/wcsaxes/tests/data/msx_header b/astropy/visualization/wcsaxes/tests/data/msx_header new file mode 100644 index 000000000000..2c2142e66268 --- /dev/null +++ b/astropy/visualization/wcsaxes/tests/data/msx_header @@ -0,0 +1,13 @@ +WCSAXES = 2 / Number of coordinate axes +CRPIX1 = 75.907 / Pixel coordinate of reference point +CRPIX2 = 74.8485 / Pixel coordinate of reference point +CDELT1 = -0.006666666828 / [deg] Coordinate increment at reference point +CDELT2 = 0.006666666828 / [deg] Coordinate increment at reference point +CUNIT1 = 'deg' / Units of coordinate increment and value +CUNIT2 = 'deg' / Units of coordinate increment and value +CTYPE1 = 'GLON-CAR' / galactic longitude, plate caree projection +CTYPE2 = 'GLAT-CAR' / galactic latitude, plate caree projection +CRVAL1 = 0.0 / [deg] Coordinate value at reference point +CRVAL2 = 0.0 / [deg] Coordinate value at reference point +LONPOLE = 0.0 / [deg] Native longitude of celestial pole +LATPOLE = 90.0 / [deg] Native latitude of celestial pole \ No newline at end of file diff --git a/astropy/visualization/wcsaxes/tests/data/rosat_header b/astropy/visualization/wcsaxes/tests/data/rosat_header new file mode 100644 index 000000000000..b198df82b17a --- /dev/null +++ b/astropy/visualization/wcsaxes/tests/data/rosat_header @@ -0,0 +1,13 @@ +WCSAXES = 2 / Number of coordinate axes +CRPIX1 = 240.5 / Pixel coordinate of reference point +CRPIX2 = 120.5 / Pixel coordinate of reference point +CDELT1 = -0.675 / [deg] Coordinate increment at reference point +CDELT2 = 0.675 / [deg] Coordinate increment at reference point +CUNIT1 = 'deg' / Units of coordinate increment and value +CUNIT2 = 'deg' / Units of coordinate increment and value +CTYPE1 = 'GLON-AIT' / galactic longitude, Hammer-Aitoff projection +CTYPE2 = 'GLAT-AIT' / galactic latitude, Hammer-Aitoff projection +CRVAL1 = 0.0 / [deg] Coordinate value at reference point +CRVAL2 = 0.0 / [deg] Coordinate value at reference point +LONPOLE = 0.0 / [deg] Native longitude of celestial pole +LATPOLE = 90.0 / [deg] Native latitude of celestial pole \ No newline at end of file diff --git a/astropy/visualization/wcsaxes/tests/data/slice_header b/astropy/visualization/wcsaxes/tests/data/slice_header new file mode 100644 index 000000000000..b8b2ce888510 --- /dev/null +++ b/astropy/visualization/wcsaxes/tests/data/slice_header @@ -0,0 +1,16 @@ +WCSAXES = 2 / Number of coordinate axes +CRPIX1 = 1.0 / Pixel coordinate of reference point +CRPIX2 = 99.0 / Pixel coordinate of reference point +CDELT1 = 0.00416666666667 / [deg] Coordinate increment at reference point +CDELT2 = 1000.0 / [m s-1] Coordinate increment at reference point +CUNIT1 = 'deg' / Units of coordinate increment and value +CUNIT2 = 'm s-1' / Units of coordinate increment and value +CTYPE1 = 'OFFSET' / Coordinate type code +CTYPE2 = 'VRAD' / Radio velocity (linear) +CRVAL1 = 0.0 / [deg] Coordinate value at reference point +CRVAL2 = 50000.0 / [m s-1] Coordinate value at reference point +LONPOLE = 0.0 / [deg] Native longitude of celestial pole +LATPOLE = 90.0 / [deg] Native latitude of celestial pole +RESTFRQ = 4829659400.0 / [Hz] Line rest frequency +EQUINOX = 2000.0 / [yr] Equinox of equatorial coordinates +SPECSYS = 'LSRK' / Reference frame of spectral coordinates \ No newline at end of file diff --git a/astropy/visualization/wcsaxes/tests/test_coordinate_helpers.py b/astropy/visualization/wcsaxes/tests/test_coordinate_helpers.py new file mode 100644 index 000000000000..29ca0656e64d --- /dev/null +++ b/astropy/visualization/wcsaxes/tests/test_coordinate_helpers.py @@ -0,0 +1,398 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +from unittest.mock import MagicMock, patch + +import matplotlib.transforms as transforms +import pytest +from matplotlib.backends.backend_agg import FigureCanvasAgg +from matplotlib.figure import Figure + +from astropy import units as u +from astropy.io import fits +from astropy.utils.data import get_pkg_data_filename +from astropy.utils.exceptions import AstropyDeprecationWarning +from astropy.visualization.wcsaxes.coordinate_helpers import CoordinateHelper +from astropy.visualization.wcsaxes.core import WCSAxes +from astropy.wcs import WCS + +MSX_HEADER = fits.Header.fromtextfile(get_pkg_data_filename("data/msx_header")) + + +def test_getaxislabel(ignore_matplotlibrc): + fig = Figure() + ax = WCSAxes(fig, [0.1, 0.1, 0.8, 0.8], aspect="equal") + + ax.coords[0].set_axislabel("X") + ax.coords[1].set_axislabel("Y") + assert ax.coords[0].get_axislabel() == "X" + assert ax.coords[1].get_axislabel() == "Y" + + +@pytest.fixture +def ax(): + fig = Figure() + _canvas = FigureCanvasAgg(fig) + ax = WCSAxes(fig, [0.1, 0.1, 0.8, 0.8], aspect="equal") + fig.add_axes(ax) + + return ax + + +def assert_label_draw(ax, x_label, y_label): + ax.coords[0].set_axislabel("Label 1") + ax.coords[1].set_axislabel("Label 2") + + with patch.object(ax.coords[0]._axislabels, "set_position") as pos1: + with patch.object(ax.coords[1]._axislabels, "set_position") as pos2: + ax.figure.canvas.draw() + + assert pos1.call_count == x_label + assert pos2.call_count == y_label + + +def test_label_visibility_rules_default(ignore_matplotlibrc, ax): + assert_label_draw(ax, True, True) + + +def test_label_visibility_rules_label(ignore_matplotlibrc, ax): + ax.coords[0].set_ticklabel_visible(False) + ax.coords[1].set_ticks(values=[-9999] * u.one) + + assert_label_draw(ax, False, False) + + +def test_label_visibility_rules_ticks(ignore_matplotlibrc, ax): + ax.coords[0].set_axislabel_visibility_rule("ticks") + ax.coords[1].set_axislabel_visibility_rule("ticks") + + ax.coords[0].set_ticklabel_visible(False) + ax.coords[1].set_ticks(values=[-9999] * u.one) + + assert_label_draw(ax, True, False) + + +def test_label_visibility_rules_always(ignore_matplotlibrc, ax): + ax.coords[0].set_axislabel_visibility_rule("always") + ax.coords[1].set_axislabel_visibility_rule("always") + + ax.coords[0].set_ticklabel_visible(False) + ax.coords[1].set_ticks(values=[-9999] * u.one) + + assert_label_draw(ax, True, True) + + +def test_format_unit(): + fig = Figure() + canvas = FigureCanvasAgg(fig) + ax = WCSAxes(fig, [0.1, 0.1, 0.8, 0.8], wcs=WCS(MSX_HEADER)) + fig.add_axes(ax) + + # Force a draw which is required for format_coord to work + canvas.draw() + + ori_fu = ax.coords[1].get_format_unit() + assert ori_fu == "deg" + + ax.coords[1].set_format_unit("arcsec") + fu = ax.coords[1].get_format_unit() + assert fu == "arcsec" + + +def test_set_separator(): + fig = Figure() + canvas = FigureCanvasAgg(fig) + ax = WCSAxes(fig, [0.1, 0.1, 0.8, 0.8], wcs=WCS(MSX_HEADER)) + fig.add_axes(ax) + + # Force a draw which is required for format_coord to work + canvas.draw() + + ax.coords[1].set_format_unit("deg") + assert ax.coords[1].format_coord(4) == "4\xb000'00\"" + ax.coords[1].set_separator((":", ":", "")) + assert ax.coords[1].format_coord(4) == "4:00:00" + ax.coords[1].set_separator("abc") + assert ax.coords[1].format_coord(4) == "4a00b00c" + ax.coords[1].set_separator(None) + assert ax.coords[1].format_coord(4) == "4\xb000'00\"" + + +@pytest.mark.parametrize( + "draw_grid, expected_visibility", [(True, True), (False, False), (None, True)] +) +def test_grid_variations(ignore_matplotlibrc, draw_grid, expected_visibility): + fig = Figure() + ax = WCSAxes(fig, [0.1, 0.1, 0.8, 0.8], aspect="equal") + fig.add_axes(ax) + transform = transforms.Affine2D().scale(2.0) + coord_helper = CoordinateHelper( + parent_axes=ax, + transform=transform, + frame=MagicMock(), + ) + coord_helper.grid(draw_grid=draw_grid) + assert coord_helper._grid_lines_kwargs["visible"] == expected_visibility + + +def test_get_position(): + fig = Figure() + _canvas = FigureCanvasAgg(fig) + ax = WCSAxes(fig, [0.1, 0.1, 0.8, 0.8], aspect="equal") + fig.add_axes(ax) + + assert ax.coords[0].get_ticks_position() == ["b", "r", "t", "l"] + assert ax.coords[1].get_ticks_position() == ["b", "r", "t", "l"] + assert ax.coords[0].get_ticklabel_position() == ["#"] + assert ax.coords[1].get_ticklabel_position() == ["#"] + assert ax.coords[0].get_axislabel_position() == ["#"] + assert ax.coords[1].get_axislabel_position() == ["#"] + + fig.canvas.draw() + + assert ax.coords[0].get_ticks_position() == ["b", "r", "t", "l"] + assert ax.coords[1].get_ticks_position() == ["b", "r", "t", "l"] + assert ax.coords[0].get_ticklabel_position() == ["b", "#"] + assert ax.coords[1].get_ticklabel_position() == ["l", "#"] + assert ax.coords[0].get_axislabel_position() == ["b", "#"] + assert ax.coords[1].get_axislabel_position() == ["l", "#"] + + ax.coords[0].set_ticks_position("br") + ax.coords[1].set_ticks_position("tl") + ax.coords[0].set_ticklabel_position("bt") + ax.coords[1].set_ticklabel_position("rl") + ax.coords[0].set_axislabel_position("t") + ax.coords[1].set_axislabel_position("r") + + assert ax.coords[0].get_ticks_position() == ["b", "r"] + assert ax.coords[1].get_ticks_position() == ["t", "l"] + assert ax.coords[0].get_ticklabel_position() == ["b", "t"] + assert ax.coords[1].get_ticklabel_position() == ["r", "l"] + assert ax.coords[0].get_axislabel_position() == ["t"] + assert ax.coords[1].get_axislabel_position() == ["r"] + + +def test_deprecated_getters(): + fig = Figure() + _canvas = FigureCanvasAgg(fig) + ax = WCSAxes(fig, [0.1, 0.1, 0.8, 0.8], aspect="equal") + fig.add_axes(ax) + + helper = CoordinateHelper( + parent_axes=ax, + frame=MagicMock(), + ) + + with pytest.warns(AstropyDeprecationWarning): + ticks = helper.ticks + assert not ticks.get_display_minor_ticks() + with pytest.warns(AstropyDeprecationWarning): + ticklabels = helper.ticklabels + assert ticklabels.text == {} + with pytest.warns(AstropyDeprecationWarning): + axislabels = helper.axislabels + assert axislabels.get_visibility_rule() == "labels" + + +def test_set_major_formatter(): + fig = Figure() + canvas = FigureCanvasAgg(fig) + ax = WCSAxes(fig, [0.1, 0.1, 0.8, 0.8], wcs=WCS(MSX_HEADER)) + fig.add_axes(ax) + + # Force a draw which is required for format_coord to work + canvas.draw() + + ax.coords[1].set_major_formatter("d.ddd") + assert ax.coords[1].format_coord(4) == "4.000\xb0" + + ax.coords[1].set_major_formatter("d.dd", show_decimal_unit=False) + assert ax.coords[1].format_coord(4) == "4.00" + + # Show unit has no effect on sexagesimal coordinates + + ax.coords[1].set_major_formatter("dd:mm:ss.s", show_decimal_unit=False) + assert ax.coords[1].format_coord(4) == "4\xb000'00.0\"" + + +def test_get_ticks_visible(): + fig = Figure() + canvas = FigureCanvasAgg(fig) + ax = WCSAxes(fig, [0.1, 0.1, 0.8, 0.8], wcs=WCS(MSX_HEADER)) + fig.add_axes(ax) + + assert ax.coords[0].get_ticks_visible() is True + + ax.coords[0].set_ticks_visible(False) + assert ax.coords[0].get_ticks_visible() is False + + ax.coords[0].set_ticks_visible(True) + assert ax.coords[0].get_ticks_visible() is True + + +def test_get_ticklabel_visible(): + fig = Figure() + canvas = FigureCanvasAgg(fig) + ax = WCSAxes(fig, [0.1, 0.1, 0.8, 0.8], wcs=WCS(MSX_HEADER)) + fig.add_axes(ax) + + assert ax.coords[0].get_ticklabel_visible() is True + + ax.coords[0].set_ticklabel_visible(False) + assert ax.coords[0].get_ticklabel_visible() is False + + ax.coords[0].set_ticklabel_visible(True) + assert ax.coords[0].get_ticklabel_visible() is True + + +def test_get_axislabel_visible(): + fig = Figure() + canvas = FigureCanvasAgg(fig) + ax = WCSAxes(fig, [0.1, 0.1, 0.8, 0.8], wcs=WCS(MSX_HEADER)) + fig.add_axes(ax) + + assert ax.coords[0].get_axislabel_visible() is True + + ax.coords[0]._axislabels.set_visible(False) + assert ax.coords[0].get_axislabel_visible() is False + + ax.coords[0]._axislabels.set_visible(True) + assert ax.coords[0].get_axislabel_visible() is True + + +def test_set_visible(): + fig = Figure() + canvas = FigureCanvasAgg(fig) + ax = WCSAxes(fig, [0.1, 0.1, 0.8, 0.8], wcs=WCS(MSX_HEADER)) + fig.add_axes(ax) + + assert ax.coords[0].get_ticks_visible() is True + assert ax.coords[0].get_ticklabel_visible() is True + assert ax.coords[0].get_axislabel_visible() is True + + ax.coords[0].set_visible(False) + assert ax.coords[0].get_ticks_visible() is False + assert ax.coords[0].get_ticklabel_visible() is False + assert ax.coords[0].get_axislabel_visible() is False + + ax.coords[0].set_visible(True) + assert ax.coords[0].get_ticks_visible() is True + assert ax.coords[0].get_ticklabel_visible() is True + assert ax.coords[0].get_axislabel_visible() is True + + +@pytest.mark.parametrize("invalid_input", [123, 123.45, None, [], {}]) +def test_set_visible_invalid_type(invalid_input): + fig = Figure() + canvas = FigureCanvasAgg(fig) + ax = WCSAxes(fig, [0.1, 0.1, 0.8, 0.8], wcs=WCS(MSX_HEADER)) + fig.add_axes(ax) + + with pytest.raises(TypeError): + ax.coords[0].set_visible(invalid_input) + + +def test_set_position(): + fig = Figure() + canvas = FigureCanvasAgg(fig) + ax = WCSAxes(fig, [0.1, 0.1, 0.8, 0.8], wcs=WCS(MSX_HEADER)) + fig.add_axes(ax) + + ax.coords[0].set_position("t") + assert ax.coords[0].get_ticks_position() == ["t"] + assert ax.coords[0].get_ticklabel_position() == ["t"] + assert ax.coords[0].get_axislabel_position() == ["t"] + + assert ax.coords[0].get_ticks_visible() is True + assert ax.coords[0].get_ticklabel_visible() is True + assert ax.coords[0].get_axislabel_visible() is True + + ax.coords[0].set_position("bl") + assert ax.coords[0].get_ticks_position() == ["b", "l"] + assert ax.coords[0].get_ticklabel_position() == ["b", "l"] + assert ax.coords[0].get_axislabel_position() == ["b", "l"] + + assert ax.coords[0].get_ticks_visible() is True + assert ax.coords[0].get_ticklabel_visible() is True + assert ax.coords[0].get_axislabel_visible() is True + + +@pytest.mark.parametrize("invalid_input", [123, 123.45, None, [], {}]) +def test_set_position_invalid_type(invalid_input): + fig = Figure() + canvas = FigureCanvasAgg(fig) + ax = WCSAxes(fig, [0.1, 0.1, 0.8, 0.8], wcs=WCS(MSX_HEADER)) + fig.add_axes(ax) + + with pytest.raises(TypeError): + ax.coords[0].set_position(invalid_input) + + +def test_set_position_invalid(): + fig = Figure() + _canvas = FigureCanvasAgg(fig) + ax = WCSAxes(fig, [0.1, 0.1, 0.8, 0.8], aspect="equal") + fig.add_axes(ax) + + with pytest.warns( + AstropyDeprecationWarning, + match=r"Ignoring unrecognized position\(s\): \['x'\], should be one of b/r/t/l", + ): + ax.coords[0].set_ticks_position("xl") + with pytest.warns( + AstropyDeprecationWarning, + match=r"Ignoring unrecognized position\(s\): \['o'\], should be one of b/r/t/l", + ): + ax.coords[1].set_ticklabel_position("to") + with pytest.warns( + AstropyDeprecationWarning, + match=r"Ignoring unrecognized position\(s\): \['q', 'p'\], should be one of b/r/t/l", + ): + ax.coords[1].set_axislabel_position("qbp") + + assert ax.coords[0].get_ticks_position() == ["l"] + assert ax.coords[1].get_ticklabel_position() == ["t"] + assert ax.coords[1].get_axislabel_position() == ["b"] + + +def test_set_position_invalid_gridline(): + fig = Figure() + _canvas = FigureCanvasAgg(fig) + ax = WCSAxes(fig, [0.1, 0.1, 0.8, 0.8], aspect="equal") + fig.add_axes(ax) + + ax.coords[0].add_tickable_gridline("my-grid-line", -30 * u.one) + + with pytest.warns( + AstropyDeprecationWarning, + match=r"Ignoring unrecognized position\(s\): \['my-parrot-line'\]", + ): + ax.coords[1].set_ticks_position(["my-grid-line", "my-parrot-line"]) + + with pytest.warns( + AstropyDeprecationWarning, + match=r"It looks like 'my-grid-line' matches the name of a single axis. If " + r"you are trying to specify a multi-character axis name, use a list " + r"or a tuple, e.g. \('my-grid-line',\).", + ): + ax.coords[1].set_ticks_position("my-grid-line") + + +def test_set_ticks_values(): + fig = Figure() + _canvas = FigureCanvasAgg(fig) + ax = WCSAxes(fig, [0.1, 0.1, 0.8, 0.8], wcs=WCS(MSX_HEADER)) + fig.add_axes(ax) + + xticks = [1795, 1780, 1783] * u.arcsec + ax.coords[0].set_format_unit(u.arcsec) + ax.coords[0].set_ticks(xticks) + + # Force a draw to calculate the tick positions + _canvas.draw() + # This attribute only exists after a draw + lbl_world = ax.coords[0]._lbl_world + lbl_world1 = lbl_world[: len(lbl_world) // 2] + + lbl_locations = u.Quantity(lbl_world1, unit=u.deg) + assert u.allclose(lbl_locations, ax.coords[0]._formatter_locator.values) + assert u.Quantity(lbl_world).unit is xticks.unit diff --git a/astropy/visualization/wcsaxes/tests/test_display_world_coordinates.py b/astropy/visualization/wcsaxes/tests/test_display_world_coordinates.py new file mode 100644 index 000000000000..d23e7b92ce21 --- /dev/null +++ b/astropy/visualization/wcsaxes/tests/test_display_world_coordinates.py @@ -0,0 +1,164 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +import matplotlib as mpl +import numpy as np +from matplotlib.backend_bases import KeyEvent +from matplotlib.backends.backend_agg import FigureCanvasAgg +from matplotlib.figure import Figure + +import astropy.units as u +from astropy.coordinates import FK5, SkyCoord, galactocentric_frame_defaults +from astropy.time import Time +from astropy.visualization.wcsaxes.core import WCSAxes +from astropy.wcs import WCS + +from .test_images import BaseImageTests + + +class TestDisplayWorldCoordinate(BaseImageTests): + def test_overlay_coords(self, ignore_matplotlibrc, tmp_path): + minus_sign = "\N{MINUS SIGN}" if mpl.rcParams["axes.unicode_minus"] else "-" + wcs = WCS(self.msx_header) + + fig = Figure(figsize=(4, 4)) + canvas = FigureCanvasAgg(fig) + + ax = WCSAxes(fig, [0.1, 0.1, 0.8, 0.8], wcs=wcs) + fig.add_axes(ax) + + # On some systems, fig.canvas.draw is not enough to force a draw, so we + # save to a temporary file. + fig.savefig(tmp_path / "test1.png") + + # Testing default displayed world coordinates + string_world = ax._display_world_coords(0.523412, 0.518311) + assert string_world == f"0\xb029'45\" {minus_sign}0\xb029'20\" (world)" + + # Test pixel coordinates + event1 = KeyEvent("test_pixel_coords", canvas, "w") + fig.canvas.callbacks.process("key_press_event", event1) + string_pixel = ax._display_world_coords(0.523412, 0.523412) + assert string_pixel == "0.523412 0.523412 (pixel)" + + event3 = KeyEvent("test_pixel_coords", canvas, "w") + fig.canvas.callbacks.process("key_press_event", event3) + # Test that it still displays world coords when there are no overlay coords + string_world2 = ax._display_world_coords(0.523412, 0.518311) + assert string_world2 == f"0\xb029'45\" {minus_sign}0\xb029'20\" (world)" + + overlay = ax.get_coords_overlay("fk5") + + # Regression test for bug that caused format to always be taken from + # main world coordinates. + overlay[0].set_major_formatter("d.ddd") + + # On some systems, fig.canvas.draw is not enough to force a draw, so we + # save to a temporary file. + fig.savefig(tmp_path / "test2.png") + + event4 = KeyEvent("test_pixel_coords", canvas, "w") + fig.canvas.callbacks.process("key_press_event", event4) + # Test that it displays the overlay world coordinates + string_world3 = ax._display_world_coords(0.523412, 0.518311) + + assert ( + string_world3 == f"267.176\xb0 {minus_sign}28\xb045'56\" (world, overlay 1)" + ) + + overlay = ax.get_coords_overlay(FK5()) + + # Regression test for bug that caused format to always be taken from + # main world coordinates. + overlay[0].set_major_formatter("d.ddd") + + # On some systems, fig.canvas.draw is not enough to force a draw, so we + # save to a temporary file. + fig.savefig(tmp_path / "test3.png") + + event5 = KeyEvent("test_pixel_coords", canvas, "w") + fig.canvas.callbacks.process("key_press_event", event5) + # Test that it displays the overlay world coordinates + string_world4 = ax._display_world_coords(0.523412, 0.518311) + + assert ( + string_world4 == f"267.176\xb0 {minus_sign}28\xb045'56\" (world, overlay 2)" + ) + + overlay = ax.get_coords_overlay(FK5(equinox=Time("J2030"))) + + # Regression test for bug that caused format to always be taken from + # main world coordinates. + overlay[0].set_major_formatter("d.ddd") + + # On some systems, fig.canvas.draw is not enough to force a draw, so we + # save to a temporary file. + fig.savefig(tmp_path / "test4.png") + + event6 = KeyEvent("test_pixel_coords", canvas, "w") + fig.canvas.callbacks.process("key_press_event", event6) + # Test that it displays the overlay world coordinates + string_world5 = ax._display_world_coords(0.523412, 0.518311) + + assert ( + string_world5 == f"267.652\xb0 {minus_sign}28\xb046'23\" (world, overlay 3)" + ) + + def test_cube_coords(self, ignore_matplotlibrc, tmp_path): + wcs = WCS(self.cube_header) + + fig = Figure(figsize=(4, 4)) + canvas = FigureCanvasAgg(fig) + + ax = WCSAxes(fig, [0.1, 0.1, 0.8, 0.8], wcs=wcs, slices=("y", 50, "x")) + fig.add_axes(ax) + + # On some systems, fig.canvas.draw is not enough to force a draw, so we + # save to a temporary file. + fig.savefig(tmp_path / "test.png") + + # Testing default displayed world coordinates + string_world = ax._display_world_coords(0.523412, 0.518311) + assert string_world == "3h26m52.0s 30\xb037'17\" 2563 (world)" + + # Test pixel coordinates + event1 = KeyEvent("test_pixel_coords", canvas, "w") + fig.canvas.callbacks.process("key_press_event", event1) + string_pixel = ax._display_world_coords(0.523412, 0.523412) + assert string_pixel == "0.523412 0.523412 (pixel)" + + def test_cube_coords_uncorr_slicing(self, ignore_matplotlibrc, tmp_path): + # Regression test for a bug that occurred with coordinate formatting if + # some dimensions were uncorrelated and sliced out. + + wcs = WCS(self.cube_header) + + fig = Figure(figsize=(4, 4)) + canvas = FigureCanvasAgg(fig) + + ax = WCSAxes(fig, [0.1, 0.1, 0.8, 0.8], wcs=wcs, slices=("x", "y", 2)) + fig.add_axes(ax) + + # On some systems, fig.canvas.draw is not enough to force a draw, so we + # save to a temporary file. + fig.savefig(tmp_path / "test.png") + + # Testing default displayed world coordinates + string_world = ax._display_world_coords(0.523412, 0.518311) + assert string_world == "3h26m56.6s 30\xb018'19\" (world)" + + # Test pixel coordinates + event1 = KeyEvent("test_pixel_coords", canvas, "w") + fig.canvas.callbacks.process("key_press_event", event1) + string_pixel = ax._display_world_coords(0.523412, 0.523412) + assert string_pixel == "0.523412 0.523412 (pixel)" + + def test_plot_coord_3d_transform(self): + wcs = WCS(self.msx_header) + + with galactocentric_frame_defaults.set("latest"): + coord = SkyCoord(0 * u.kpc, 0 * u.kpc, 0 * u.kpc, frame="galactocentric") + + fig = Figure() + ax = fig.add_subplot(1, 1, 1, projection=wcs) + (point,) = ax.plot_coord(coord, "ro") + + np.testing.assert_allclose(point.get_xydata()[0], [0, 0], atol=1e-4) diff --git a/astropy/visualization/wcsaxes/tests/test_formatter_locator.py b/astropy/visualization/wcsaxes/tests/test_formatter_locator.py new file mode 100644 index 000000000000..f74db6eff621 --- /dev/null +++ b/astropy/visualization/wcsaxes/tests/test_formatter_locator.py @@ -0,0 +1,645 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +import numpy as np +import pytest +from matplotlib import rc_context +from numpy.testing import assert_almost_equal + +from astropy import units as u +from astropy.tests.helper import assert_quantity_allclose +from astropy.units import UnitsError +from astropy.visualization.wcsaxes.formatter_locator import ( + AngleFormatterLocator, + ScalarFormatterLocator, +) + + +class TestAngleFormatterLocator: + def test_no_options(self): + fl = AngleFormatterLocator() + assert fl.values is None + assert fl.number == 5 + assert fl.spacing is None + + def test_too_many_options(self): + MESSAGE = r"At most one of values/number/spacing can be specified" + + with pytest.raises(ValueError, match=MESSAGE): + AngleFormatterLocator(values=[1.0, 2.0], number=5) + + with pytest.raises(ValueError, match=MESSAGE): + AngleFormatterLocator(values=[1.0, 2.0], spacing=5.0 * u.deg) + + with pytest.raises(ValueError, match=MESSAGE): + AngleFormatterLocator(number=5, spacing=5.0 * u.deg) + + with pytest.raises(ValueError, match=MESSAGE): + AngleFormatterLocator(values=[1.0, 2.0], number=5, spacing=5.0 * u.deg) + + def test_values(self): + fl = AngleFormatterLocator(values=[0.1, 1.0, 14.0] * u.degree) + assert fl.values.to_value(u.degree).tolist() == [0.1, 1.0, 14.0] + assert fl.number is None + assert fl.spacing is None + + values, spacing = fl.locator(34.3, 55.4) + assert_almost_equal(values.to_value(u.degree), [0.1, 1.0, 14.0]) + + def test_number(self): + fl = AngleFormatterLocator(number=7) + assert fl.values is None + assert fl.number == 7 + assert fl.spacing is None + + values, spacing = fl.locator(34.3, 55.4) + assert_almost_equal(values.to_value(u.degree), [35.0, 40.0, 45.0, 50.0, 55.0]) + + values, spacing = fl.locator(34.3, 36.1) + assert_almost_equal( + values.to_value(u.degree), [34.5, 34.75, 35.0, 35.25, 35.5, 35.75, 36.0] + ) + + fl.format = "dd" + values, spacing = fl.locator(34.3, 36.1) + assert_almost_equal(values.to_value(u.degree), [35.0, 36.0]) + + def test_spacing(self): + with pytest.raises( + TypeError, + match=( + r"spacing should be an astropy.units.Quantity instance with units of" + r" angle" + ), + ): + AngleFormatterLocator(spacing=3.0) + + fl = AngleFormatterLocator(spacing=3.0 * u.degree) + assert fl.values is None + assert fl.number is None + assert fl.spacing == 3.0 * u.degree + + values, spacing = fl.locator(34.3, 55.4) + assert_almost_equal( + values.to_value(u.degree), [36.0, 39.0, 42.0, 45.0, 48.0, 51.0, 54.0] + ) + + fl.spacing = 30.0 * u.arcmin + values, spacing = fl.locator(34.3, 36.1) + assert_almost_equal(values.to_value(u.degree), [34.5, 35.0, 35.5, 36.0]) + + with pytest.warns(UserWarning, match=r"Spacing is too small"): + fl.format = "dd" + values, spacing = fl.locator(34.3, 36.1) + assert_almost_equal(values.to_value(u.degree), [35.0, 36.0]) + + def test_minor_locator(self): + fl = AngleFormatterLocator() + + values, spacing = fl.locator(34.3, 55.4) + + minor_values = fl.minor_locator(spacing, 5, 34.3, 55.4) + + assert_almost_equal( + minor_values.to_value(u.degree), + [ + 36.0, + 37.0, + 38.0, + 39.0, + 41.0, + 42.0, + 43.0, + 44.0, + 46.0, + 47.0, + 48.0, + 49.0, + 51.0, + 52.0, + 53.0, + 54.0, + ], + ) + + minor_values = fl.minor_locator(spacing, 2, 34.3, 55.4) + + assert_almost_equal(minor_values.to_value(u.degree), [37.5, 42.5, 47.5, 52.5]) + + fl.values = [0.1, 1.0, 14.0] * u.degree + + values, spacing = fl.locator(34.3, 36.1) + + minor_values = fl.minor_locator(spacing, 2, 34.3, 55.4) + + assert_almost_equal(minor_values.to_value(u.degree), []) + + @pytest.mark.parametrize( + ("format", "string"), + [ + ("dd", "15\xb0"), + ("dd:mm", "15\xb024'"), + ("dd:mm:ss", "15\xb023'32\""), + ("+dd:mm:ss", "+15\xb023'32\""), + ("dd:mm:ss.s", "15\xb023'32.0\""), + ("dd:mm:ss.ssss", "15\xb023'32.0316\""), + ("hh", "1h"), + ("hh:mm", "1h02m"), + ("hh:mm:ss", "1h01m34s"), + ("hh:mm:ss.s", "1h01m34.1s"), + ("hh:mm:ss.ssss", "1h01m34.1354s"), + ("d", "15\xb0"), + ("d.d", "15.4\xb0"), + ("d.dd", "15.39\xb0"), + ("d.ddd", "15.392\xb0"), + ("m", "924'"), + ("m.m", "923.5'"), + ("m.mm", "923.53'"), + ("s", '55412"'), + ("s.s", '55412.0"'), + ("s.ss", '55412.03"'), + ("+s.ss", '+55412.03"'), + ], + ) + def test_format(self, format, string): + fl = AngleFormatterLocator(number=5, format=format) + assert fl.formatter([15.392231] * u.degree, None, format="ascii")[0] == string + + @pytest.mark.parametrize( + ("separator", "format", "string"), + [ + (("deg", "'", '"'), "dd", "15deg"), + (("deg", "'", '"'), "dd:mm", "15deg24'"), + (("deg", "'", '"'), "dd:mm:ss", "15deg23'32\""), + ((":", "-", "s"), "dd:mm:ss.s", "15:23-32.0s"), + (":", "dd:mm:ss.s", "15:23:32.0"), + ((":", ":", "s"), "hh", "1:"), + (("-", "-", "s"), "hh:mm:ss.ssss", "1-01-34.1354s"), + (("d", ":", '"'), "d", "15\xb0"), + (("d", ":", '"'), "d.d", "15.4\xb0"), + ], + ) + def test_separator(self, separator, format, string): + fl = AngleFormatterLocator(number=5, format=format) + fl.sep = separator + assert fl.formatter([15.392231] * u.degree, None)[0] == string + + def test_latex_format(self): + fl = AngleFormatterLocator(number=5, format="dd:mm:ss") + assert fl.formatter([15.392231] * u.degree, None)[0] == "15\xb023'32\"" + with rc_context(rc={"text.usetex": True}): + assert ( + fl.formatter([15.392231] * u.degree, None)[0] + == "$15^\\circ23{}^\\prime32{}^{\\prime\\prime}$" + ) + + @pytest.mark.parametrize("format", ["x.xxx", "dd.ss", "dd:ss", "mdd:mm:ss"]) + def test_invalid_formats(self, format): + fl = AngleFormatterLocator(number=5) + with pytest.raises(ValueError, match=f"Invalid format: {format}"): + fl.format = format + + @pytest.mark.parametrize( + ("format", "base_spacing"), + [ + ("dd", 1.0 * u.deg), + ("dd:mm", 1.0 * u.arcmin), + ("dd:mm:ss", 1.0 * u.arcsec), + ("dd:mm:ss.ss", 0.01 * u.arcsec), + ("hh", 15.0 * u.deg), + ("hh:mm", 15.0 * u.arcmin), + ("hh:mm:ss", 15.0 * u.arcsec), + ("hh:mm:ss.ss", 0.15 * u.arcsec), + ("d", 1.0 * u.deg), + ("d.d", 0.1 * u.deg), + ("d.dd", 0.01 * u.deg), + ("d.ddd", 0.001 * u.deg), + ("m", 1.0 * u.arcmin), + ("m.m", 0.1 * u.arcmin), + ("m.mm", 0.01 * u.arcmin), + ("s", 1.0 * u.arcsec), + ("s.s", 0.1 * u.arcsec), + ("s.ss", 0.01 * u.arcsec), + ], + ) + def test_base_spacing(self, format, base_spacing): + fl = AngleFormatterLocator(number=5, format=format) + assert fl.base_spacing == base_spacing + + def test_incorrect_spacing(self): + fl = AngleFormatterLocator() + fl.spacing = 0.032 * u.deg + with pytest.warns( + UserWarning, match=r"Spacing is not a multiple of base spacing" + ): + fl.format = "dd:mm:ss" + assert_almost_equal(fl.spacing.to_value(u.arcsec), 115.0) + + def test_decimal_values(self): + # Regression test for a bug that meant that the spacing was not + # determined correctly for decimal coordinates + + fl = AngleFormatterLocator() + fl.format = "d.dddd" + assert_quantity_allclose( + fl.locator(266.9730, 266.9750)[0], + [266.9735, 266.9740, 266.9745, 266.9750] * u.deg, + ) + + fl = AngleFormatterLocator(decimal=True, format_unit=u.hourangle, number=4) + assert_quantity_allclose( + fl.locator(266.9730, 266.9750)[0], [17.79825, 17.79830] * u.hourangle + ) + + def test_values_unit(self): + # Make sure that the intrinsic unit and format unit are correctly + # taken into account when using the locator + + fl = AngleFormatterLocator(unit=u.arcsec, format_unit=u.arcsec, decimal=True) + assert_quantity_allclose( + fl.locator(850, 2150)[0], + [1000.0, 1200.0, 1400.0, 1600.0, 1800.0, 2000.0] * u.arcsec, + ) + + fl = AngleFormatterLocator(unit=u.arcsec, format_unit=u.degree, decimal=False) + assert_quantity_allclose( + fl.locator(850, 2150)[0], [15.0, 20.0, 25.0, 30.0, 35.0] * u.arcmin + ) + + fl = AngleFormatterLocator( + unit=u.arcsec, format_unit=u.hourangle, decimal=False + ) + assert_quantity_allclose( + fl.locator(850, 2150)[0], + [60.0, 75.0, 90.0, 105.0, 120.0, 135.0] * (15 * u.arcsec), + ) + + fl = AngleFormatterLocator(unit=u.arcsec) + fl.format = "dd:mm:ss" + assert_quantity_allclose(fl.locator(0.9, 1.1)[0], [1] * u.arcsec) + + fl = AngleFormatterLocator(unit=u.arcsec, spacing=0.2 * u.arcsec) + assert_quantity_allclose(fl.locator(0.3, 0.9)[0], [0.4, 0.6, 0.8] * u.arcsec) + + @pytest.mark.parametrize( + ("spacing", "string"), + [ + (2 * u.deg, "15\xb0"), + (2 * u.arcmin, "15\xb024'"), + (2 * u.arcsec, "15\xb023'32\""), + (0.1 * u.arcsec, "15\xb023'32.0\""), + ], + ) + def test_formatter_no_format(self, spacing, string): + fl = AngleFormatterLocator() + assert fl.formatter([15.392231] * u.degree, spacing)[0] == string + + @pytest.mark.parametrize( + ("format_unit", "decimal", "show_decimal_unit", "spacing", "ascii", "latex"), + [ + (u.degree, False, True, 2 * u.degree, "15\xb0", r"$15^\circ$"), + ( + u.degree, + False, + True, + 2 * u.arcmin, + "15\xb024'", + r"$15^\circ24{}^\prime$", + ), + ( + u.degree, + False, + True, + 2 * u.arcsec, + "15\xb023'32\"", + r"$15^\circ23{}^\prime32{}^{\prime\prime}$", + ), + ( + u.degree, + False, + True, + 0.1 * u.arcsec, + "15\xb023'32.0\"", + r"$15^\circ23{}^\prime32.0{}^{\prime\prime}$", + ), + (u.hourangle, False, True, 15 * u.degree, "1h", r"$1^{\mathrm{h}}$"), + ( + u.hourangle, + False, + True, + 15 * u.arcmin, + "1h02m", + r"$1^{\mathrm{h}}02^{\mathrm{m}}$", + ), + ( + u.hourangle, + False, + True, + 15 * u.arcsec, + "1h01m34s", + r"$1^{\mathrm{h}}01^{\mathrm{m}}34^{\mathrm{s}}$", + ), + ( + u.hourangle, + False, + True, + 1.5 * u.arcsec, + "1h01m34.1s", + r"$1^{\mathrm{h}}01^{\mathrm{m}}34.1^{\mathrm{s}}$", + ), + (u.degree, True, True, 15 * u.degree, "15\xb0", r"$15\mathrm{^\circ}$"), + ( + u.degree, + True, + True, + 0.12 * u.degree, + "15.39\xb0", + r"$15.39\mathrm{^\circ}$", + ), + ( + u.degree, + True, + True, + 0.0036 * u.arcsec, + "15.392231\xb0", + r"$15.392231\mathrm{^\circ}$", + ), + (u.arcmin, True, True, 15 * u.degree, "924'", r"$924\mathrm{^\prime}$"), + ( + u.arcmin, + True, + True, + 0.12 * u.degree, + "923.5'", + r"$923.5\mathrm{^\prime}$", + ), + ( + u.arcmin, + True, + True, + 0.1 * u.arcmin, + "923.5'", + r"$923.5\mathrm{^\prime}$", + ), + ( + u.arcmin, + True, + True, + 0.0002 * u.arcmin, + "923.5339'", + r"$923.5339\mathrm{^\prime}$", + ), + ( + u.arcsec, + True, + True, + 0.01 * u.arcsec, + '55412.03"', + r"$55412.03\mathrm{^{\prime\prime}}$", + ), + ( + u.arcsec, + True, + True, + 0.001 * u.arcsec, + '55412.032"', + r"$55412.032\mathrm{^{\prime\prime}}$", + ), + ( + u.mas, + True, + True, + 0.001 * u.arcsec, + "55412032 mas", + r"$55412032\;\mathrm{mas}$", + ), + (u.degree, True, False, 15 * u.degree, "15", "15"), + (u.degree, True, False, 0.12 * u.degree, "15.39", "15.39"), + (u.degree, True, False, 0.0036 * u.arcsec, "15.392231", "15.392231"), + (u.arcmin, True, False, 15 * u.degree, "924", "924"), + (u.arcmin, True, False, 0.12 * u.degree, "923.5", "923.5"), + (u.arcmin, True, False, 0.1 * u.arcmin, "923.5", "923.5"), + (u.arcmin, True, False, 0.0002 * u.arcmin, "923.5339", "923.5339"), + (u.arcsec, True, False, 0.01 * u.arcsec, "55412.03", "55412.03"), + (u.arcsec, True, False, 0.001 * u.arcsec, "55412.032", "55412.032"), + (u.mas, True, False, 0.001 * u.arcsec, "55412032", "55412032"), + # Make sure that specifying None defaults to + # decimal for non-degree or non-hour angles + ( + u.arcsec, + None, + True, + 0.01 * u.arcsec, + '55412.03"', + r"$55412.03\mathrm{^{\prime\prime}}$", + ), + ], + ) + def test_formatter_no_format_with_units( + self, format_unit, decimal, show_decimal_unit, spacing, ascii, latex + ): + # Check the formatter works when specifying the default units and + # decimal behavior to use. + fl = AngleFormatterLocator( + unit=u.degree, + format_unit=format_unit, + decimal=decimal, + show_decimal_unit=show_decimal_unit, + ) + assert fl.formatter([15.392231] * u.degree, spacing, format="ascii")[0] == ascii + assert fl.formatter([15.392231] * u.degree, spacing, format="latex")[0] == latex + + def test_incompatible_unit_decimal(self): + with pytest.raises( + UnitsError, + match=( + r"Units should be degrees or hours when using non-decimal .sexagesimal." + r" mode" + ), + ): + AngleFormatterLocator(unit=u.arcmin, decimal=False) + + @pytest.mark.parametrize( + "unicode_minus, expected_char", + [ + (True, "\N{MINUS SIGN}"), + (False, "-"), + ], + ) + @pytest.mark.parametrize("cls", [AngleFormatterLocator, ScalarFormatterLocator]) + def test_unicode_minus(self, cls, unicode_minus, expected_char): + # see https://github.com/astropy/astropy/issues/15898 + fl = cls() + with rc_context(rc={"axes.unicode_minus": unicode_minus}): + minus_one, _ = fl.formatter([-1.0, 1.0] * u.deg, spacing=2 * u.deg) + + assert minus_one.startswith(expected_char) + + +class TestScalarFormatterLocator: + def test_no_options(self): + fl = ScalarFormatterLocator(unit=u.m) + assert fl.values is None + assert fl.number == 5 + assert fl.spacing is None + + def test_too_many_options(self): + MESSAGE = r"At most one of values/number/spacing can be specified" + + with pytest.raises(ValueError, match=MESSAGE): + ScalarFormatterLocator(values=[1.0, 2.0] * u.m, number=5) + + with pytest.raises(ValueError, match=MESSAGE): + ScalarFormatterLocator(values=[1.0, 2.0] * u.m, spacing=5.0 * u.m) + + with pytest.raises(ValueError, match=MESSAGE): + ScalarFormatterLocator(number=5, spacing=5.0 * u.m) + + with pytest.raises(ValueError, match=MESSAGE): + ScalarFormatterLocator(values=[1.0, 2.0] * u.m, number=5, spacing=5.0 * u.m) + + def test_values(self): + fl = ScalarFormatterLocator(values=[0.1, 1.0, 14.0] * u.m, unit=u.m) + assert fl.values.value.tolist() == [0.1, 1.0, 14.0] + assert fl.number is None + assert fl.spacing is None + + values, spacing = fl.locator(34.3, 55.4) + assert_almost_equal(values.value, [0.1, 1.0, 14.0]) + + def test_number(self): + fl = ScalarFormatterLocator(number=7, unit=u.m) + assert fl.values is None + assert fl.number == 7 + assert fl.spacing is None + + values, spacing = fl.locator(34.3, 55.4) + assert_almost_equal(values.value, np.linspace(36.0, 54.0, 10)) + + values, spacing = fl.locator(34.3, 36.1) + assert_almost_equal(values.value, np.linspace(34.4, 36, 9)) + + fl.format = "x" + values, spacing = fl.locator(34.3, 36.1) + assert_almost_equal(values.value, [35.0, 36.0]) + + def test_spacing(self): + fl = ScalarFormatterLocator(spacing=3.0 * u.m) + assert fl.values is None + assert fl.number is None + assert fl.spacing == 3.0 * u.m + + values, spacing = fl.locator(34.3, 55.4) + assert_almost_equal(values.value, [36.0, 39.0, 42.0, 45.0, 48.0, 51.0, 54.0]) + + fl.spacing = 0.5 * u.m + values, spacing = fl.locator(34.3, 36.1) + assert_almost_equal(values.value, [34.5, 35.0, 35.5, 36.0]) + + with pytest.warns(UserWarning, match=r"Spacing is too small"): + fl.format = "x" + values, spacing = fl.locator(34.3, 36.1) + assert_almost_equal(values.value, [35.0, 36.0]) + + def test_minor_locator(self): + fl = ScalarFormatterLocator(unit=u.m) + + values, spacing = fl.locator(34.3, 55.4) + + minor_values = fl.minor_locator(spacing, 5, 34.3, 55.4) + + assert_almost_equal( + minor_values.value, + [ + 36.0, + 37.0, + 38.0, + 39.0, + 41.0, + 42.0, + 43.0, + 44.0, + 46.0, + 47.0, + 48.0, + 49.0, + 51.0, + 52.0, + 53.0, + 54.0, + ], + ) + + minor_values = fl.minor_locator(spacing, 2, 34.3, 55.4) + + assert_almost_equal(minor_values.value, [37.5, 42.5, 47.5, 52.5]) + + fl.values = [0.1, 1.0, 14.0] * u.m + + values, spacing = fl.locator(34.3, 36.1) + + minor_values = fl.minor_locator(spacing, 2, 34.3, 55.4) + + assert_almost_equal(minor_values.value, []) + + @pytest.mark.parametrize( + ("format", "string"), + [ + ("x", "15"), + ("x.x", "15.4"), + ("x.xx", "15.39"), + ("x.xxx", "15.392"), + ("%g", "15.3922"), + ("%f", "15.392231"), + ("%.2f", "15.39"), + ("%.3f", "15.392"), + ], + ) + def test_format(self, format, string): + fl = ScalarFormatterLocator(number=5, format=format, unit=u.m) + assert fl.formatter([15.392231] * u.m, None)[0] == string + + @pytest.mark.parametrize( + ("format", "string"), + [("x", "1539"), ("x.x", "1539.2"), ("x.xx", "1539.22"), ("x.xxx", "1539.223")], + ) + def test_format_unit(self, format, string): + fl = ScalarFormatterLocator(number=5, format=format, unit=u.m) + fl.format_unit = u.cm + assert fl.formatter([15.392231] * u.m, None)[0] == string + + @pytest.mark.parametrize("format", ["dd", "dd:mm", "xx:mm", "mx.xxx"]) + def test_invalid_formats(self, format): + fl = ScalarFormatterLocator(number=5, unit=u.m) + with pytest.raises(ValueError, match=f"Invalid format: {format}"): + fl.format = format + + @pytest.mark.parametrize( + ("format", "base_spacing"), + [("x", 1.0 * u.m), ("x.x", 0.1 * u.m), ("x.xxx", 0.001 * u.m)], + ) + def test_base_spacing(self, format, base_spacing): + fl = ScalarFormatterLocator(number=5, format=format, unit=u.m) + assert fl.base_spacing == base_spacing + + def test_incorrect_spacing(self): + fl = ScalarFormatterLocator(unit=u.m) + fl.spacing = 0.032 * u.m + with pytest.warns( + UserWarning, match=r"Spacing is not a multiple of base spacing" + ): + fl.format = "x.xx" + assert_almost_equal(fl.spacing.to_value(u.m), 0.03) + + def test_values_unit(self): + # Make sure that the intrinsic unit and format unit are correctly + # taken into account when using the locator + + fl = ScalarFormatterLocator(unit=u.cm, format_unit=u.m) + assert_quantity_allclose( + fl.locator(850, 2150)[0], + [1000.0, 1200.0, 1400.0, 1600.0, 1800.0, 2000.0] * u.cm, + ) + + fl = ScalarFormatterLocator(unit=u.cm, format_unit=u.m) + fl.format = "x.x" + assert_quantity_allclose(fl.locator(1, 19)[0], [10] * u.cm) diff --git a/astropy/visualization/wcsaxes/tests/test_frame.py b/astropy/visualization/wcsaxes/tests/test_frame.py new file mode 100644 index 000000000000..480e78f0525d --- /dev/null +++ b/astropy/visualization/wcsaxes/tests/test_frame.py @@ -0,0 +1,157 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +import numpy as np +from matplotlib.figure import Figure + +from astropy.tests.figures import figure_test +from astropy.visualization.wcsaxes import WCSAxes +from astropy.visualization.wcsaxes.frame import BaseFrame +from astropy.wcs import WCS + +from .test_images import BaseImageTests + + +class HexagonalFrame(BaseFrame): + spine_names = "abcdef" + + def update_spines(self): + xmin, xmax = self.parent_axes.get_xlim() + ymin, ymax = self.parent_axes.get_ylim() + + ymid = 0.5 * (ymin + ymax) + xmid1 = (xmin + xmax) / 4.0 + xmid2 = (xmin + xmax) * 3.0 / 4.0 + + self["a"].data = np.array(([xmid1, ymin], [xmid2, ymin])) + self["b"].data = np.array(([xmid2, ymin], [xmax, ymid])) + self["c"].data = np.array(([xmax, ymid], [xmid2, ymax])) + self["d"].data = np.array(([xmid2, ymax], [xmid1, ymax])) + self["e"].data = np.array(([xmid1, ymax], [xmin, ymid])) + self["f"].data = np.array(([xmin, ymid], [xmid1, ymin])) + + +class TestFrame(BaseImageTests): + @figure_test(tolerance=0.5) + def test_custom_frame(self): + wcs = WCS(self.msx_header) + + fig = Figure(figsize=(4, 4)) + ax = WCSAxes(fig, [0.15, 0.15, 0.7, 0.7], wcs=wcs, frame_class=HexagonalFrame) + fig.add_axes(ax) + + ax.coords.grid(color="white") + + im = ax.imshow( + np.ones((149, 149)), + vmin=0.0, + vmax=2.0, + origin="lower", + cmap="gist_heat", + ) + + minpad = {} + minpad["a"] = minpad["d"] = 1 + minpad["b"] = minpad["c"] = minpad["e"] = minpad["f"] = 2.75 + + ax.coords["glon"].set_axislabel("Longitude", minpad=minpad) + ax.coords["glon"].set_axislabel_position("ad") + + ax.coords["glat"].set_axislabel("Latitude", minpad=minpad) + ax.coords["glat"].set_axislabel_position("bcef") + + ax.coords["glon"].set_ticklabel_position("ad") + ax.coords["glat"].set_ticklabel_position("bcef") + + # Set limits so that no labels overlap + ax.set_xlim(5.5, 100.5) + ax.set_ylim(5.5, 110.5) + + # Clip the image to the frame + im.set_clip_path(ax.coords.frame.patch) + + return fig + + @figure_test + def test_update_clip_path_rectangular(self, tmp_path): + fig = Figure() + ax = WCSAxes(fig, [0.1, 0.1, 0.8, 0.8], aspect="equal") + + fig.add_axes(ax) + + ax.set_xlim(0.0, 2.0) + ax.set_ylim(0.0, 2.0) + + # Force drawing, which freezes the clip path returned by WCSAxes + fig.savefig(tmp_path / "nothing") + + ax.imshow(np.zeros((12, 4))) + + ax.set_xlim(-0.5, 3.5) + ax.set_ylim(-0.5, 11.5) + + ax.coords[0].set_auto_axislabel(False) + ax.coords[1].set_auto_axislabel(False) + + return fig + + @figure_test + def test_update_clip_path_nonrectangular(self, tmp_path): + fig = Figure() + ax = WCSAxes( + fig, [0.1, 0.1, 0.8, 0.8], aspect="equal", frame_class=HexagonalFrame + ) + + fig.add_axes(ax) + + ax.set_xlim(0.0, 2.0) + ax.set_ylim(0.0, 2.0) + + # Force drawing, which freezes the clip path returned by WCSAxes + fig.savefig(tmp_path / "nothing") + + ax.imshow(np.zeros((12, 4))) + + ax.set_xlim(-0.5, 3.5) + ax.set_ylim(-0.5, 11.5) + + return fig + + @figure_test + def test_update_clip_path_change_wcs(self, tmp_path): + # When WCS is changed, a new frame is created, so we need to make sure + # that the path is carried over to the new frame. + + fig = Figure() + ax = WCSAxes(fig, [0.1, 0.1, 0.8, 0.8], aspect="equal") + + fig.add_axes(ax) + + ax.set_xlim(0.0, 2.0) + ax.set_ylim(0.0, 2.0) + + # Force drawing, which freezes the clip path returned by WCSAxes + fig.savefig(tmp_path / "nothing") + + ax.reset_wcs() + + ax.imshow(np.zeros((12, 4))) + + ax.set_xlim(-0.5, 3.5) + ax.set_ylim(-0.5, 11.5) + + ax.coords[0].set_auto_axislabel(False) + ax.coords[1].set_auto_axislabel(False) + + return fig + + def test_copy_frame_properties_change_wcs(self): + # When WCS is changed, a new frame is created, so we need to make sure + # that the color and linewidth are transferred over + + fig = Figure() + ax = WCSAxes(fig, [0.1, 0.1, 0.8, 0.8]) + fig.add_axes(ax) + ax.coords.frame.set_linewidth(5) + ax.coords.frame.set_color("purple") + ax.reset_wcs() + assert ax.coords.frame.get_linewidth() == 5 + assert ax.coords.frame.get_color() == "purple" diff --git a/astropy/visualization/wcsaxes/tests/test_grid_paths.py b/astropy/visualization/wcsaxes/tests/test_grid_paths.py new file mode 100644 index 000000000000..8d7e8f961c4e --- /dev/null +++ b/astropy/visualization/wcsaxes/tests/test_grid_paths.py @@ -0,0 +1,28 @@ +import numpy as np +import pytest +from matplotlib.lines import Path + +from astropy.visualization.wcsaxes.grid_paths import get_lon_lat_path + + +@pytest.mark.parametrize("step_in_degrees", [10, 1, 0.01]) +def test_round_trip_visibility(step_in_degrees): + zero = np.zeros(100) + + # The pixel values are irrelevant for this test + pixel = np.stack([zero, zero]).T + + # Create a grid line of constant latitude with a point every step + line = np.stack([np.arange(100), zero]).T * step_in_degrees + + # Create a modified grid line where the point spacing is larger by 5% + # Starting with point 20, the discrepancy between `line` and `line_round` is greater than `step` + line_round = line * 1.05 + + # Perform the round-trip check + path = get_lon_lat_path(line, pixel, line_round) + + # The grid line should be visible for only the initial part line (19 points) + codes_check = np.full(100, Path.MOVETO) + codes_check[line_round[:, 0] - line[:, 0] < step_in_degrees] = Path.LINETO + assert np.all(path.codes[1:] == codes_check[1:]) diff --git a/astropy/visualization/wcsaxes/tests/test_images.py b/astropy/visualization/wcsaxes/tests/test_images.py new file mode 100644 index 000000000000..85284162daa2 --- /dev/null +++ b/astropy/visualization/wcsaxes/tests/test_images.py @@ -0,0 +1,1574 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +import matplotlib.lines +import matplotlib.text +import numpy as np +import pytest +from matplotlib import rc_context +from matplotlib.backends.backend_agg import FigureCanvasAgg +from matplotlib.figure import Figure +from matplotlib.patches import Circle, Rectangle + +from astropy import units as u +from astropy.coordinates import ( + ICRS, + RepresentationMapping, + SkyCoord, + SphericalRepresentation, + StaticMatrixTransform, + frame_transform_graph, +) +from astropy.io import fits +from astropy.tests.figures import figure_test +from astropy.utils.data import get_pkg_data_filename +from astropy.utils.exceptions import AstropyUserWarning +from astropy.visualization.wcsaxes import WCSAxes, add_beam, add_scalebar +from astropy.visualization.wcsaxes.frame import EllipticalFrame +from astropy.visualization.wcsaxes.patches import Quadrangle, SphericalCircle +from astropy.wcs import WCS +from astropy.wcs.wcsapi import BaseLowLevelWCS + + +class BaseImageTests: + @classmethod + def setup_class(cls): + msx_header = get_pkg_data_filename("data/msx_header") + cls.msx_header = fits.Header.fromtextfile(msx_header) + + rosat_header = get_pkg_data_filename("data/rosat_header") + cls.rosat_header = fits.Header.fromtextfile(rosat_header) + + twoMASS_k_header = get_pkg_data_filename("data/2MASS_k_header") + cls.twoMASS_k_header = fits.Header.fromtextfile(twoMASS_k_header) + + cube_header = get_pkg_data_filename("data/cube_header") + cls.cube_header = fits.Header.fromtextfile(cube_header) + + slice_header = get_pkg_data_filename("data/slice_header") + cls.slice_header = fits.Header.fromtextfile(slice_header) + + +class TestBasic(BaseImageTests): + @figure_test + def test_tight_layout(self): + # Check that tight_layout works on a WCSAxes. + fig = Figure(figsize=(8, 6)) + canvas = FigureCanvasAgg(fig) + for i in (1, 2): + fig.add_subplot(2, 1, i, projection=WCS(self.msx_header)) + fig.tight_layout() + canvas.draw() + return fig + + @figure_test + def test_image_plot(self): + # Test for plotting image and also setting values of ticks + fig = Figure(figsize=(6, 6)) + canvas = FigureCanvasAgg(fig) + ax = fig.add_axes( + [0.1, 0.1, 0.8, 0.8], projection=WCS(self.msx_header), aspect="equal" + ) + ax.set_xlim(-0.5, 148.5) + ax.set_ylim(-0.5, 148.5) + ax.coords[0].set_ticks([-0.30, 0.0, 0.20] * u.degree, size=5, width=1) + canvas.draw() + return fig + + @figure_test + def test_axes_off(self): + # Test for turning the axes off + fig = Figure(figsize=(3, 3)) + canvas = FigureCanvasAgg(fig) + ax = fig.add_axes([0.1, 0.1, 0.8, 0.8], projection=WCS(self.msx_header)) + ax.imshow(np.arange(12).reshape((3, 4))) + ax.set_axis_off() + canvas.draw() + return fig + + @figure_test + @pytest.mark.parametrize("axisbelow", [True, False, "line"]) + def test_axisbelow(self, axisbelow): + # Test that tick marks, labels, and gridlines are drawn with the + # correct zorder controlled by the axisbelow property. + fig = Figure(figsize=(6, 6)) + canvas = FigureCanvasAgg(fig) + ax = fig.add_axes( + [0.1, 0.1, 0.8, 0.8], projection=WCS(self.msx_header), aspect="equal" + ) + ax.set_axisbelow(axisbelow) + ax.set_xlim(-0.5, 148.5) + ax.set_ylim(-0.5, 148.5) + ax.coords[0].set_ticks([-0.30, 0.0, 0.20] * u.degree, size=5, width=1) + ax.grid() + ax.coords[0].set_auto_axislabel(False) + ax.coords[1].set_auto_axislabel(False) + + # Add an image (default zorder=0). + ax.imshow(np.zeros((64, 64))) + + # Add a patch (default zorder=1). + r = Rectangle((30.0, 50.0), 60.0, 50.0, facecolor="green", edgecolor="red") + ax.add_patch(r) + + # Add a line (default zorder=2). + ax.plot([32, 128], [32, 128], linewidth=10) + + canvas.draw() + return fig + + @figure_test + def test_contour_overlay(self): + # Test for overlaying contours on images + path = get_pkg_data_filename("galactic_center/gc_msx_e.fits") + with fits.open(path) as pf: + data = pf[0].data + + wcs_msx = WCS(self.msx_header) + + fig = Figure(figsize=(6, 6)) + canvas = FigureCanvasAgg(fig) + ax = fig.add_axes( + [0.15, 0.15, 0.8, 0.8], + projection=WCS(self.twoMASS_k_header), + aspect="equal", + ) + ax.set_xlim(-0.5, 720.5) + ax.set_ylim(-0.5, 720.5) + + # Overplot contour + ax.contour( + data, + transform=ax.get_transform(wcs_msx), + colors="orange", + levels=[2.5e-5, 5e-5, 1.0e-4], + ) + ax.coords[0].set_ticks(size=5, width=1) + ax.coords[1].set_ticks(size=5, width=1) + ax.set_xlim(0.0, 720.0) + ax.set_ylim(0.0, 720.0) + + # In previous versions, all angle axes defaulted to being displayed in + # degrees. We now automatically show RA axes in hour angle units, but + # for backward-compatibility with previous reference images we + # explicitly use degrees here. + ax.coords[0].set_format_unit(u.degree) + + canvas.draw() + return fig + + @figure_test + def test_contourf_overlay(self): + # Test for overlaying contours on images + path = get_pkg_data_filename("galactic_center/gc_msx_e.fits") + with fits.open(path) as pf: + data = pf[0].data + + wcs_msx = WCS(self.msx_header) + + fig = Figure(figsize=(6, 6)) + canvas = FigureCanvasAgg(fig) + ax = fig.add_axes( + [0.15, 0.15, 0.8, 0.8], + projection=WCS(self.twoMASS_k_header), + aspect="equal", + ) + ax.set_xlim(-0.5, 720.5) + ax.set_ylim(-0.5, 720.5) + + # Overplot contour + ax.contourf( + data, transform=ax.get_transform(wcs_msx), levels=[2.5e-5, 5e-5, 1.0e-4] + ) + ax.coords[0].set_ticks(size=5, width=1) + ax.coords[1].set_ticks(size=5, width=1) + ax.set_xlim(0.0, 720.0) + ax.set_ylim(0.0, 720.0) + + # In previous versions, all angle axes defaulted to being displayed in + # degrees. We now automatically show RA axes in hour angle units, but + # for backward-compatibility with previous reference images we + # explicitly use degrees here. + ax.coords[0].set_format_unit(u.degree) + + canvas.draw() + return fig + + @figure_test + def test_overlay_features_image(self): + # Test for overlaying grid, changing format of ticks, setting spacing + # and number of ticks + + fig = Figure(figsize=(6, 6)) + canvas = FigureCanvasAgg(fig) + ax = fig.add_axes( + [0.25, 0.25, 0.65, 0.65], projection=WCS(self.msx_header), aspect="equal" + ) + + # Change the format of the ticks + ax.coords[0].set_major_formatter("dd:mm:ss") + ax.coords[1].set_major_formatter("dd:mm:ss.ssss") + + # Overlay grid on image + ax.grid(color="red", alpha=1.0, lw=1, linestyle="dashed") + + # Set the spacing of ticks on the 'glon' axis to 4 arcsec + ax.coords["glon"].set_ticks(spacing=4 * u.arcsec, size=5, width=1) + + # Set the number of ticks on the 'glat' axis to 9 + ax.coords["glat"].set_ticks(number=9, size=5, width=1) + + # Set labels on axes + ax.coords["glon"].set_axislabel("Galactic Longitude", minpad=1.6) + ax.coords["glat"].set_axislabel("Galactic Latitude", minpad=-0.75) + + # Change the frame linewidth and color + ax.coords.frame.set_color("red") + ax.coords.frame.set_linewidth(2) + + assert ax.coords.frame.get_color() == "red" + assert ax.coords.frame.get_linewidth() == 2 + + canvas.draw() + return fig + + @figure_test + def test_curvilinear_grid_patches_image(self): + # Overlay curvilinear grid and patches on image + + fig = Figure(figsize=(8, 8)) + canvas = FigureCanvasAgg(fig) + ax = fig.add_axes( + [0.1, 0.1, 0.8, 0.8], projection=WCS(self.rosat_header), aspect="equal" + ) + + ax.set_xlim(-0.5, 479.5) + ax.set_ylim(-0.5, 239.5) + + ax.grid(color="black", alpha=1.0, lw=1, linestyle="dashed") + + p = Circle((300, 100), radius=40, ec="yellow", fc="none") + ax.add_patch(p) + + p = Circle( + (30.0, 20.0), + radius=20.0, + ec="orange", + fc="none", + transform=ax.get_transform("world"), + ) + ax.add_patch(p) + + p = Circle( + (60.0, 50.0), + radius=20.0, + ec="red", + fc="none", + transform=ax.get_transform("fk5"), + ) + ax.add_patch(p) + + p = Circle( + (40.0, 60.0), + radius=20.0, + ec="green", + fc="none", + transform=ax.get_transform("galactic"), + ) + ax.add_patch(p) + + canvas.draw() + return fig + + @figure_test + def test_cube_slice_image(self): + # Test for cube slicing + + fig = Figure() + canvas = FigureCanvasAgg(fig) + ax = fig.add_axes( + [0.1, 0.1, 0.8, 0.8], + projection=WCS(self.cube_header), + slices=(50, "y", "x"), + aspect="equal", + ) + + ax.set_xlim(-0.5, 52.5) + ax.set_ylim(-0.5, 106.5) + + ax.coords[2].set_axislabel("Velocity m/s") + + ax.coords[1].set_ticks(spacing=0.2 * u.deg, width=1) + ax.coords[2].set_ticks(spacing=400 * u.m / u.s, width=1) + + ax.coords[1].set_ticklabel(exclude_overlapping=True) + ax.coords[2].set_ticklabel(exclude_overlapping=True) + + ax.coords[0].grid(grid_type="contours", color="purple", linestyle="solid") + ax.coords[1].grid(grid_type="contours", color="orange", linestyle="solid") + ax.coords[2].grid(grid_type="contours", color="red", linestyle="solid") + + canvas.draw() + return fig + + @figure_test + def test_cube_slice_image_lonlat(self): + # Test for cube slicing. Here we test with longitude and latitude since + # there is some longitude-specific code in _update_grid_contour. + + fig = Figure() + canvas = FigureCanvasAgg(fig) + ax = fig.add_axes( + [0.1, 0.1, 0.8, 0.8], + projection=WCS(self.cube_header), + slices=("x", "y", 50), + aspect="equal", + ) + + ax.set_xlim(-0.5, 106.5) + ax.set_ylim(-0.5, 106.5) + + ax.coords[0].grid(grid_type="contours", color="blue", linestyle="solid") + ax.coords[1].grid(grid_type="contours", color="red", linestyle="solid") + + # In previous versions, all angle axes defaulted to being displayed in + # degrees. We now automatically show RA axes in hour angle units, but + # for backward-compatibility with previous reference images we + # explicitly use degrees here. + ax.coords[0].set_format_unit(u.degree) + + canvas.draw() + return fig + + @figure_test + def test_plot_coord(self): + fig = Figure(figsize=(6, 6)) + canvas = FigureCanvasAgg(fig) + ax = fig.add_axes( + [0.15, 0.15, 0.8, 0.8], + projection=WCS(self.twoMASS_k_header), + aspect="equal", + ) + ax.set_xlim(-0.5, 720.5) + ax.set_ylim(-0.5, 720.5) + + c = SkyCoord(266 * u.deg, -29 * u.deg) + lines = ax.plot_coord(c, "o") + + # Test that plot_coord returns the results from ax.plot + assert isinstance(lines, list) + assert isinstance(lines[0], matplotlib.lines.Line2D) + + # In previous versions, all angle axes defaulted to being displayed in + # degrees. We now automatically show RA axes in hour angle units, but + # for backward-compatibility with previous reference images we + # explicitly use degrees here. + ax.coords[0].set_format_unit(u.degree) + + canvas.draw() + return fig + + @figure_test + def test_scatter_coord(self): + from matplotlib.collections import PathCollection + + fig = Figure(figsize=(6, 6)) + canvas = FigureCanvasAgg(fig) + ax = fig.add_axes( + [0.15, 0.15, 0.8, 0.8], + projection=WCS(self.twoMASS_k_header), + aspect="equal", + ) + ax.set_xlim(-0.5, 720.5) + ax.set_ylim(-0.5, 720.5) + + c = SkyCoord(266 * u.deg, -29 * u.deg) + sc = ax.scatter_coord(c, marker="o") + + # Test that plot_coord returns the results from ax.plot + assert isinstance(sc, PathCollection) + + # In previous versions, all angle axes defaulted to being displayed in + # degrees. We now automatically show RA axes in hour angle units, but + # for backward-compatibility with previous reference images we + # explicitly use degrees here. + ax.coords[0].set_format_unit(u.degree) + + canvas.draw() + return fig + + @figure_test + def test_text_coord(self): + fig = Figure(figsize=(6, 6)) + canvas = FigureCanvasAgg(fig) + ax = fig.add_axes( + [0.15, 0.15, 0.8, 0.8], + projection=WCS(self.twoMASS_k_header), + aspect="equal", + ) + ax.set_xlim(-0.5, 720.5) + ax.set_ylim(-0.5, 720.5) + + c = SkyCoord(266 * u.deg, -29 * u.deg) + text = ax.text_coord(c, "Sample Label", color="blue", ha="right", va="top") + + # Test that plot_coord returns the results from ax.text + assert isinstance(text, matplotlib.text.Text) + + # In previous versions, all angle axes defaulted to being displayed in + # degrees. We now automatically show RA axes in hour angle units, but + # for backward-compatibility with previous reference images we + # explicitly use degrees here. + ax.coords[0].set_format_unit(u.degree) + + canvas.draw() + return fig + + @figure_test + def test_plot_line(self): + fig = Figure(figsize=(6, 6)) + canvas = FigureCanvasAgg(fig) + ax = fig.add_axes( + [0.15, 0.15, 0.8, 0.8], + projection=WCS(self.twoMASS_k_header), + aspect="equal", + ) + ax.set_xlim(-0.5, 720.5) + ax.set_ylim(-0.5, 720.5) + + c = SkyCoord([266, 266.8] * u.deg, [-29, -28.9] * u.deg) + ax.plot_coord(c) + + # In previous versions, all angle axes defaulted to being displayed in + # degrees. We now automatically show RA axes in hour angle units, but + # for backward-compatibility with previous reference images we + # explicitly use degrees here. + ax.coords[0].set_format_unit(u.degree) + + canvas.draw() + return fig + + @figure_test + def test_changed_axis_units(self): + # Test to see if changing the units of axis works + fig = Figure() + canvas = FigureCanvasAgg(fig) + ax = fig.add_axes( + [0.1, 0.1, 0.8, 0.8], + projection=WCS(self.cube_header), + slices=(50, "y", "x"), + aspect="equal", + ) + ax.set_xlim(-0.5, 52.5) + ax.set_ylim(-0.5, 106.5) + ax.coords[0].set_ticks_position("") + ax.coords[0].set_ticklabel_position("") + ax.coords[0].set_axislabel_position("") + ax.coords[1].set_ticks_position("lr") + ax.coords[1].set_ticklabel_position("l") + ax.coords[1].set_axislabel_position("l") + ax.coords[2].set_ticks_position("bt") + ax.coords[2].set_ticklabel_position("b") + ax.coords[2].set_axislabel_position("b") + ax.coords[2].set_major_formatter("x.xx") + ax.coords[2].set_format_unit(u.km / u.s) + ax.coords[2].set_axislabel("Velocity km/s") + ax.coords[1].set_ticks(width=1) + ax.coords[2].set_ticks(width=1) + ax.coords[1].set_ticklabel(exclude_overlapping=True) + ax.coords[2].set_ticklabel(exclude_overlapping=True) + + canvas.draw() + return fig + + @figure_test + def test_minor_ticks(self): + # Test for drawing minor ticks + fig = Figure() + canvas = FigureCanvasAgg(fig) + ax = fig.add_axes( + [0.1, 0.1, 0.8, 0.8], + projection=WCS(self.cube_header), + slices=(50, "y", "x"), + aspect="equal", + ) + ax.set_xlim(-0.5, 52.5) + ax.set_ylim(-0.5, 106.5) + ax.coords[0].set_ticks_position("") + ax.coords[0].set_ticklabel_position("") + ax.coords[0].set_axislabel_position("") + ax.coords[1].set_ticks_position("lr") + ax.coords[1].set_ticklabel_position("l") + ax.coords[1].set_axislabel_position("l") + ax.coords[2].set_ticks_position("bt") + ax.coords[2].set_ticklabel_position("b") + ax.coords[2].set_axislabel_position("b") + ax.coords[2].set_ticklabel(exclude_overlapping=True) + ax.coords[1].set_ticklabel(exclude_overlapping=True) + ax.coords[2].display_minor_ticks(True) + ax.coords[1].display_minor_ticks(True) + ax.coords[2].set_minor_frequency(3) + ax.coords[1].set_minor_frequency(10) + + canvas.draw() + return fig + + @figure_test + def test_ticks_labels(self): + fig = Figure(figsize=(6, 6)) + canvas = FigureCanvasAgg(fig) + ax = WCSAxes(fig, [0.1, 0.1, 0.7, 0.7], wcs=None) + fig.add_axes(ax) + ax.set_xlim(-0.5, 2) + ax.set_ylim(-0.5, 2) + ax.coords[0].set_ticks(size=10, color="blue", alpha=0.2, width=1) + ax.coords[1].set_ticks(size=20, color="red", alpha=0.9, width=1) + ax.coords[0].set_ticks_position("all") + ax.coords[1].set_ticks_position("all") + ax.coords[0].set_axislabel("X-axis", size=20) + ax.coords[1].set_axislabel( + "Y-axis", + color="green", + size=25, + weight="regular", + style="normal", + family="cmtt10", + ) + ax.coords[0].set_axislabel_position("t") + ax.coords[1].set_axislabel_position("r") + ax.coords[0].set_ticklabel( + color="purple", + size=15, + alpha=1, + weight="light", + style="normal", + family="cmss10", + ) + ax.coords[1].set_ticklabel( + color="black", size=18, alpha=0.9, weight="bold", family="cmr10" + ) + ax.coords[0].set_ticklabel_position("all") + ax.coords[1].set_ticklabel_position("r") + + canvas.draw() + return fig + + @figure_test + def test_no_ticks(self): + # Check that setting no ticks works + fig = Figure(figsize=(6, 6)) + canvas = FigureCanvasAgg(fig) + ax = fig.add_axes( + [0.1, 0.1, 0.8, 0.8], projection=WCS(self.msx_header), aspect="equal" + ) + ax.set_xlim(-0.5, 148.5) + ax.set_ylim(-0.5, 148.5) + ax.coords[0].set_ticks(number=0) + ax.coords[0].grid(True) + canvas.draw() + return fig + + @figure_test + def test_rcparams(self): + # Test custom rcParams + + with rc_context( + { + "axes.labelcolor": "purple", + "axes.labelsize": 14, + "axes.labelweight": "bold", + "axes.linewidth": 3, + "axes.facecolor": "0.5", + "axes.edgecolor": "green", + "xtick.color": "red", + "xtick.labelsize": 8, + "xtick.direction": "in", + "xtick.minor.visible": True, + "xtick.minor.size": 5, + "xtick.major.size": 20, + "xtick.major.width": 3, + "xtick.major.pad": 10, + "grid.color": "blue", + "grid.linestyle": ":", + "grid.linewidth": 1, + "grid.alpha": 0.5, + } + ): + fig = Figure(figsize=(6, 6)) + canvas = FigureCanvasAgg(fig) + ax = WCSAxes(fig, [0.15, 0.1, 0.7, 0.7], wcs=None) + fig.add_axes(ax) + ax.set_xlim(-0.5, 2) + ax.set_ylim(-0.5, 2) + ax.grid() + ax.set_xlabel("X label") + ax.set_ylabel("Y label") + ax.coords[0].set_ticklabel(exclude_overlapping=True) + ax.coords[1].set_ticklabel(exclude_overlapping=True) + canvas.draw() + return fig + + @figure_test + def test_tick_angles(self): + # Test that tick marks point in the correct direction, even when the + # axes limits extend only over a few FITS pixels. Addresses #45, #46. + w = WCS() + w.wcs.ctype = ["RA---TAN", "DEC--TAN"] + w.wcs.crval = [90, 70] + w.wcs.cdelt = [16, 16] + w.wcs.crpix = [1, 1] + w.wcs.radesys = "ICRS" + w.wcs.equinox = 2000.0 + fig = Figure(figsize=(3, 3)) + canvas = FigureCanvasAgg(fig) + ax = fig.add_axes([0.1, 0.1, 0.8, 0.8], projection=w) + ax.set_xlim(1, -1) + ax.set_ylim(-1, 1) + ax.grid(color="gray", alpha=0.5, linestyle="solid") + ax.coords["ra"].set_ticks(color="red", size=20) + ax.coords["dec"].set_ticks(color="red", size=20) + # In previous versions, all angle axes defaulted to being displayed in + # degrees. We now automatically show RA axes in hour angle units, but + # for backward-compatibility with previous reference images we + # explicitly use degrees here. + ax.coords[0].set_format_unit(u.degree) + canvas.draw() + return fig + + @figure_test + def test_tick_angles_non_square_axes(self): + # Test that tick marks point in the correct direction, even when the + # axes limits extend only over a few FITS pixels, and the axes are + # non-square. + w = WCS() + w.wcs.ctype = ["RA---TAN", "DEC--TAN"] + w.wcs.crval = [90, 70] + w.wcs.cdelt = [16, 16] + w.wcs.crpix = [1, 1] + w.wcs.radesys = "ICRS" + w.wcs.equinox = 2000.0 + fig = Figure(figsize=(6, 3)) + canvas = FigureCanvasAgg(fig) + ax = fig.add_axes([0.1, 0.1, 0.8, 0.8], projection=w) + ax.set_xlim(1, -1) + ax.set_ylim(-1, 1) + ax.grid(color="gray", alpha=0.5, linestyle="solid") + ax.coords["ra"].set_ticks(color="red", size=20) + ax.coords["dec"].set_ticks(color="red", size=20) + # In previous versions, all angle axes defaulted to being displayed in + # degrees. We now automatically show RA axes in hour angle units, but + # for backward-compatibility with previous reference images we + # explicitly use degrees here. + ax.coords[0].set_format_unit(u.degree) + canvas.draw() + return fig + + @figure_test + def test_set_coord_type(self): + # Test for setting coord_type + fig = Figure(figsize=(3, 3)) + canvas = FigureCanvasAgg(fig) + ax = fig.add_axes( + [0.2, 0.2, 0.6, 0.6], projection=WCS(self.msx_header), aspect="equal" + ) + ax.set_xlim(-0.5, 148.5) + ax.set_ylim(-0.5, 148.5) + ax.coords[0].set_coord_type("scalar") + ax.coords[1].set_coord_type("scalar") + ax.coords[0].set_major_formatter("x.xxx") + ax.coords[1].set_major_formatter("x.xxx") + ax.coords[0].set_ticklabel(exclude_overlapping=True) + ax.coords[1].set_ticklabel(exclude_overlapping=True) + canvas.draw() + return fig + + @figure_test + def test_ticks_regression(self): + # Regression test for a bug that caused ticks aligned exactly with a + # sampled frame point to not appear. This also checks that tick labels + # don't get added more than once, and that no error occurs when e.g. + # the top part of the frame is all at the same coordinate as one of the + # potential ticks (which causes the tick angle calculation to return + # NaN). + wcs = WCS(self.slice_header) + fig = Figure(figsize=(3, 3)) + canvas = FigureCanvasAgg(fig) + ax = fig.add_axes([0.25, 0.25, 0.5, 0.5], projection=wcs, aspect="auto") + limits = wcs.wcs_world2pix([0, 0], [35e3, 80e3], 0)[1] + ax.set_ylim(*limits) + ax.coords[0].set_ticks(spacing=0.002 * u.deg) + ax.coords[1].set_ticks(spacing=5 * u.km / u.s) + ax.coords[0].set_ticklabel(alpha=0.5) # to see multiple labels + ax.coords[1].set_ticklabel(alpha=0.5) + ax.coords[0].set_ticklabel_position("all") + ax.coords[1].set_ticklabel_position("all") + ax.coords[0].set_axislabel_position("b") + ax.coords[1].set_axislabel_position("l") + canvas.draw() + return fig + + @figure_test + def test_axislabels_regression(self): + # Regression test for a bug that meant that if tick labels were made + # invisible with ``set_visible(False)``, they were still added to the + # list of bounding boxes for tick labels, but with default values of 0 + # to 1, which caused issues. + wcs = WCS(self.msx_header) + fig = Figure(figsize=(3, 3)) + canvas = FigureCanvasAgg(fig) + ax = fig.add_axes([0.25, 0.25, 0.5, 0.5], projection=wcs, aspect="auto") + ax.coords[0].set_axislabel("Label 1") + ax.coords[1].set_axislabel("Label 2") + ax.coords[1].set_axislabel_visibility_rule("always") + ax.coords[1].set_ticklabel_visible(False) + canvas.draw() + return fig + + @figure_test(savefig_kwargs={"bbox_inches": "tight"}) + def test_noncelestial_angular(self): + # Regression test for a bug that meant that when passing a WCS that had + # angular axes and using set_coord_type to set the coordinates to + # longitude/latitude, but where the WCS wasn't recognized as celestial, + # the WCS units are not converted to deg, so we can't assume that + # transform will always return degrees. + + wcs = WCS(naxis=2) + + wcs.wcs.ctype = ["solar-x", "solar-y"] + wcs.wcs.cunit = ["arcsec", "arcsec"] + + fig = Figure(figsize=(3, 3)) + canvas = FigureCanvasAgg(fig) + ax = fig.add_subplot(1, 1, 1, projection=wcs) + + ax.imshow(np.zeros([1024, 1024]), origin="lower") + + ax.coords[0].set_coord_type("longitude", coord_wrap=180 * u.deg) + ax.coords[1].set_coord_type("latitude") + + ax.coords[0].set_major_formatter("s.s") + ax.coords[1].set_major_formatter("s.s") + + ax.coords[0].set_format_unit(u.arcsec, show_decimal_unit=False) + ax.coords[1].set_format_unit(u.arcsec, show_decimal_unit=False) + + ax.grid(color="white", ls="solid") + + # Force drawing (needed for format_coord) + fig.canvas.draw() + + assert ax.format_coord(512, 512) == "513.0 513.0 (world)" + + canvas.draw() + return fig + + @figure_test + def test_patches_distortion(self, tmp_path): + # Check how patches get distorted (and make sure that scatter markers + # and SphericalCircle don't) + + wcs = WCS(self.msx_header) + fig = Figure(figsize=(3, 3)) + canvas = FigureCanvasAgg(fig) + ax = fig.add_axes([0.25, 0.25, 0.5, 0.5], projection=wcs, aspect="equal") + + # Pixel coordinates + r = Rectangle((30.0, 50.0), 60.0, 50.0, edgecolor="green", facecolor="none") + ax.add_patch(r) + + # FK5 coordinates + r = Rectangle( + (266.4, -28.9), + 0.3, + 0.3, + edgecolor="cyan", + facecolor="none", + transform=ax.get_transform("fk5"), + ) + ax.add_patch(r) + + # FK5 coordinates + c = Circle( + (266.4, -29.1), + 0.15, + edgecolor="magenta", + facecolor="none", + transform=ax.get_transform("fk5"), + ) + ax.add_patch(c) + + # Pixel coordinates + ax.scatter( + [40, 100, 130], + [30, 130, 60], + s=100, + edgecolor="red", + facecolor=(1, 0, 0, 0.5), + ) + + # World coordinates (should not be distorted) + ax.scatter( + 266.78238, + -28.769255, + transform=ax.get_transform("fk5"), + s=300, + edgecolor="red", + facecolor="none", + ) + + # World coordinates (should not be distorted) + r1 = SphericalCircle( + (266.4 * u.deg, -29.1 * u.deg), + 0.15 * u.degree, + edgecolor="purple", + facecolor="none", + transform=ax.get_transform("fk5"), + ) + ax.add_patch(r1) + + r2 = SphericalCircle( + SkyCoord(266.4 * u.deg, -29.1 * u.deg), + 0.15 * u.degree, + edgecolor="purple", + facecolor="none", + transform=ax.get_transform("fk5"), + ) + + with pytest.warns( + AstropyUserWarning, + match="Received `center` of representation type " + " " + "will be converted to SphericalRepresentation", + ): + r3 = SphericalCircle( + SkyCoord( + x=-0.05486461, + y=-0.87204803, + z=-0.48633538, + representation_type="cartesian", + ), + 0.15 * u.degree, + edgecolor="purple", + facecolor="none", + transform=ax.get_transform("fk5"), + ) + + ax.coords[0].set_ticklabel_visible(False) + ax.coords[1].set_ticklabel_visible(False) + + # Test to verify that SphericalCircle works irrespective of whether + # the input(center) is a tuple or a SkyCoord object. + assert (r1.get_xy() == r2.get_xy()).all() + assert np.allclose(r1.get_xy(), r3.get_xy()) + assert np.allclose(r2.get_xy()[0], [266.4, -29.25]) + + canvas.draw() + return fig + + @figure_test + def test_quadrangle(self, tmp_path): + # Test that Quadrangle can have curved edges while Rectangle does not + wcs = WCS(self.msx_header) + fig = Figure(figsize=(3, 3)) + canvas = FigureCanvasAgg(fig) + ax = fig.add_axes([0.25, 0.25, 0.5, 0.5], projection=wcs, aspect="equal") + ax.set_xlim(0, 10000) + ax.set_ylim(-10000, 0) + + # Add a quadrangle patch (100 degrees by 20 degrees) + q = Quadrangle( + (255, -70) * u.deg, + 100 * u.deg, + 20 * u.deg, + label="Quadrangle", + edgecolor="blue", + facecolor="none", + transform=ax.get_transform("icrs"), + ) + ax.add_patch(q) + + # Add a rectangle patch (100 degrees by 20 degrees) + r = Rectangle( + (255, -70), + 100, + 20, + label="Rectangle", + edgecolor="red", + facecolor="none", + linestyle="--", + transform=ax.get_transform("icrs"), + ) + ax.add_patch(r) + + ax.coords[0].set_ticklabel_visible(False) + ax.coords[1].set_ticklabel_visible(False) + + canvas.draw() + return fig + + @figure_test + def test_beam_shape_from_args(self, tmp_path): + # Test for adding the beam shape with the beam parameters as arguments + wcs = WCS(self.msx_header) + fig = Figure(figsize=(4, 3)) + canvas = FigureCanvasAgg(fig) + ax = fig.add_axes([0.2, 0.2, 0.6, 0.6], projection=wcs, aspect="equal") + ax.set_xlim(-10, 10) + ax.set_ylim(-10, 10) + + add_beam( + ax, + major=2 * u.arcmin, + minor=1 * u.arcmin, + angle=-30 * u.degree, + corner="bottom right", + frame=True, + borderpad=0.0, + pad=1.0, + color="black", + ) + + canvas.draw() + return fig + + @figure_test + def test_beam_shape_from_header(self, tmp_path): + # Test for adding the beam shape with the beam parameters from a header + hdr = self.msx_header + hdr["BMAJ"] = (2 * u.arcmin).to(u.degree).value + hdr["BMIN"] = (1 * u.arcmin).to(u.degree).value + hdr["BPA"] = 30.0 + + wcs = WCS(hdr) + fig = Figure(figsize=(4, 3)) + canvas = FigureCanvasAgg(fig) + ax = fig.add_axes([0.2, 0.2, 0.6, 0.6], projection=wcs, aspect="equal") + ax.set_xlim(-10, 10) + ax.set_ylim(-10, 10) + + add_beam(ax, header=hdr) + + canvas.draw() + return fig + + @figure_test + def test_scalebar(self, tmp_path): + # Test for adding a scale bar + wcs = WCS(self.msx_header) + fig = Figure(figsize=(4, 3)) + canvas = FigureCanvasAgg(fig) + ax = fig.add_axes([0.2, 0.2, 0.6, 0.6], projection=wcs, aspect="equal") + ax.set_xlim(-10, 10) + ax.set_ylim(-10, 10) + + add_scalebar( + ax, + 2 * u.arcmin, + label="2'", + corner="top right", + borderpad=1.0, + label_top=True, + ) + + canvas.draw() + return fig + + @figure_test + def test_elliptical_frame(self): + # Regression test for a bug (astropy/astropy#6063) that caused labels to + # be incorrectly simplified. + + wcs = WCS(self.msx_header) + fig = Figure(figsize=(5, 3)) + canvas = FigureCanvasAgg(fig) + fig.add_axes([0.2, 0.2, 0.6, 0.6], projection=wcs, frame_class=EllipticalFrame) + canvas.draw() + return fig + + @figure_test + def test_hms_labels(self): + # This tests the appearance of the hms superscripts in tick labels + fig = Figure(figsize=(3, 3)) + canvas = FigureCanvasAgg(fig) + ax = fig.add_axes( + [0.3, 0.2, 0.65, 0.6], projection=WCS(self.twoMASS_k_header), aspect="equal" + ) + ax.set_xlim(-0.5, 0.5) + ax.set_ylim(-0.5, 0.5) + ax.coords[0].set_ticks(spacing=0.2 * 15 * u.arcsec) + canvas.draw() + return fig + + @figure_test(style={"text.usetex": True}) + def test_latex_labels(self): + fig = Figure(figsize=(3, 3)) + canvas = FigureCanvasAgg(fig) + ax = fig.add_axes( + [0.3, 0.2, 0.65, 0.6], projection=WCS(self.twoMASS_k_header), aspect="equal" + ) + ax.set_xlim(-0.5, 0.5) + ax.set_ylim(-0.5, 0.5) + ax.coords[0].set_ticks(spacing=0.2 * 15 * u.arcsec) + canvas.draw() + return fig + + @figure_test + def test_tick_params(self): + # This is a test to make sure that tick_params works correctly. We try + # and test as much as possible with a single reference image. + + wcs = WCS() + wcs.wcs.ctype = ["lon", "lat"] + + fig = Figure(figsize=(6, 6)) + canvas = FigureCanvasAgg(fig) + + # The first subplot tests: + # - that plt.tick_params works + # - that by default both axes are changed + # - changing the tick direction and appearance, the label appearance and padding + ax = fig.add_subplot(2, 2, 1, projection=wcs) + ax.tick_params( + direction="in", + length=20, + width=5, + pad=6, + labelsize=6, + color="red", + labelcolor="blue", + ) + + ax.coords[0].set_auto_axislabel(False) + ax.coords[1].set_auto_axislabel(False) + + # The second subplot tests: + # - that specifying grid parameters doesn't actually cause the grid to + # be shown (as expected) + # - that axis= can be given integer coordinates or their string name + # - that the tick positioning works (bottom/left/top/right) + # Make sure that we can pass things that can index coords + ax = fig.add_subplot(2, 2, 2, projection=wcs) + ax.tick_params( + axis=0, + direction="in", + length=20, + width=5, + pad=4, + labelsize=6, + color="red", + labelcolor="blue", + bottom=True, + grid_color="purple", + ) + ax.tick_params( + axis="lat", + direction="out", + labelsize=8, + color="blue", + labelcolor="purple", + left=True, + right=True, + grid_color="red", + ) + + ax.coords[0].set_auto_axislabel(False) + ax.coords[1].set_auto_axislabel(False) + + # The third subplot tests: + # - that ax.tick_params works + # - that the grid has the correct settings once shown explicitly + # - that we can use axis='x' and axis='y' + ax = fig.add_subplot(2, 2, 3, projection=wcs) + ax.tick_params( + axis="x", + direction="in", + length=20, + width=5, + pad=20, + labelsize=6, + color="red", + labelcolor="blue", + bottom=True, + grid_color="purple", + ) + ax.tick_params( + axis="y", + direction="out", + labelsize=8, + color="blue", + labelcolor="purple", + left=True, + right=True, + grid_color="red", + ) + ax.grid() + + ax.coords[0].set_auto_axislabel(False) + ax.coords[1].set_auto_axislabel(False) + + # The final subplot tests: + # - that we can use tick_params on a specific coordinate + # - that the label positioning can be customized + # - that the colors argument works + # - that which='minor' works + ax = fig.add_subplot(2, 2, 4, projection=wcs) + ax.coords[0].tick_params( + length=4, + pad=2, + colors="orange", + labelbottom=True, + labeltop=True, + labelsize=10, + ) + ax.coords[1].display_minor_ticks(True) + ax.coords[1].tick_params(which="minor", length=6) + + ax.coords[0].set_auto_axislabel(False) + ax.coords[1].set_auto_axislabel(False) + + canvas.draw() + return fig + + +@pytest.fixture +def wave_wcs_1d(): + wcs = WCS(naxis=1) + wcs.wcs.ctype = ["WAVE"] + wcs.wcs.cunit = ["m"] + wcs.wcs.crpix = [1] + wcs.wcs.cdelt = [5] + wcs.wcs.crval = [45] + wcs.wcs.set() + return wcs + + +@figure_test +def test_1d_plot_1d_wcs(wave_wcs_1d): + fig = Figure() + canvas = FigureCanvasAgg(fig) + ax = fig.add_subplot(1, 1, 1, projection=wave_wcs_1d) + (lines,) = ax.plot([10, 12, 14, 12, 10]) + + ax.set_xlabel("this is the x-axis") + ax.set_ylabel("this is the y-axis") + + canvas.draw() + return fig + + +@figure_test +def test_1d_plot_1d_wcs_format_unit(wave_wcs_1d): + """ + This test ensures that the format unit is updated and displayed for both + the axis ticks and default axis labels. + """ + fig = Figure() + canvas = FigureCanvasAgg(fig) + ax = fig.add_subplot(1, 1, 1, projection=wave_wcs_1d) + (lines,) = ax.plot([10, 12, 14, 12, 10]) + + ax.coords[0].set_format_unit("nm") + + canvas.draw() + return fig + + +@figure_test +def test_1d_plot_1d_wcs_get_transform(wave_wcs_1d): + fig = Figure() + canvas = FigureCanvasAgg(fig) + ax = fig.add_subplot(1, 1, 1, projection=wave_wcs_1d) + ax.plot([100, 200, 300], [2, 3, 2], transform=ax.get_transform("world")) + + return fig + + +@pytest.fixture +def spatial_wcs_2d(): + wcs = WCS(naxis=2) + wcs.wcs.ctype = ["GLON-TAN", "GLAT-TAN"] + wcs.wcs.crpix = [3.0] * 2 + wcs.wcs.cdelt = [15] * 2 + wcs.wcs.crval = [50.0] * 2 + wcs.wcs.set() + return wcs + + +@figure_test +def test_1d_plot_2d_wcs_correlated(spatial_wcs_2d): + fig = Figure() + canvas = FigureCanvasAgg(fig) + ax = fig.add_subplot(1, 1, 1, projection=spatial_wcs_2d, slices=("x", 0)) + (lines,) = ax.plot([10, 12, 14, 12, 10], "-o", color="orange") + + ax.coords["glon"].set_ticks(color="red") + ax.coords["glon"].set_ticklabel(color="red") + ax.coords["glon"].grid(color="red") + + ax.coords["glat"].set_ticks(color="blue") + ax.coords["glat"].set_ticklabel(color="blue") + ax.coords["glat"].grid(color="blue") + + canvas.draw() + return fig + + +@pytest.fixture +def spatial_wcs_2d_small_angle(): + """ + This WCS has an almost linear correlation between the pixel and world axes + close to the reference pixel. + """ + wcs = WCS(naxis=2) + wcs.wcs.ctype = ["HPLN-TAN", "HPLT-TAN"] + wcs.wcs.crpix = [3.0] * 2 + wcs.wcs.cdelt = [10 / 3600, 5 / 3600] + wcs.wcs.crval = [0] * 2 + wcs.wcs.set() + return wcs + + +@pytest.mark.parametrize( + "slices, bottom_axis", + [ + # Remember SLLWCS takes slices in array order + (np.s_[0, :], "custom:pos.helioprojective.lon"), + (np.s_[:, 0], "custom:pos.helioprojective.lat"), + ], +) +@figure_test +def test_1d_plot_1d_sliced_low_level_wcs( + spatial_wcs_2d_small_angle, slices, bottom_axis +): + """ + Test that a SLLWCS through a coupled 2D WCS plots as line OK. + """ + fig = Figure() + canvas = FigureCanvasAgg(fig) + ax = fig.add_subplot(1, 1, 1, projection=spatial_wcs_2d_small_angle[slices]) + (lines,) = ax.plot([10, 12, 14, 12, 10], "-o", color="orange") + + # Draw to trigger rendering the ticks. + canvas.draw() + + assert ax.coords[bottom_axis].get_ticks_position() == ["b", "#"] + return fig + + +@pytest.mark.parametrize( + "slices, bottom_axis", [(("x", 0), "hpln"), ((0, "x"), "hplt")] +) +@figure_test +def test_1d_plot_put_varying_axis_on_bottom_lon( + spatial_wcs_2d_small_angle, slices, bottom_axis +): + """ + When we plot a 1D slice through spatial axes, we want to put the axis which + actually changes on the bottom. + + For example an aligned wcs, pixel grid where you plot a lon slice through a + lat axis, you would end up with no ticks on the bottom as the lon doesn't + change, and a set of lat ticks on the top because it does but it's the + correlated axis not the actual one you are plotting against. + """ + fig = Figure() + canvas = FigureCanvasAgg(fig) + ax = fig.add_subplot(1, 1, 1, projection=spatial_wcs_2d_small_angle, slices=slices) + ax.plot([10, 12, 14, 12, 10], "-o", color="orange") + + # Draw to trigger rendering the ticks. + canvas.draw() + + assert ax.coords[bottom_axis].get_ticks_position() == ["b", "#"] + return fig + + +@figure_test +def test_allsky_labels_wrap(): + # Regression test for a bug that caused some tick labels to not be shown + # when looking at all-sky maps in the case where coord_wrap < 360 + + fig = Figure(figsize=(4, 4)) + canvas = FigureCanvasAgg(fig) + + icen = 0 + + for ctype in [("GLON-CAR", "GLAT-CAR"), ("HGLN-CAR", "HGLT-CAR")]: + for cen in [0, 90, 180, 270]: + icen += 1 + + wcs = WCS(naxis=2) + wcs.wcs.ctype = ctype + wcs.wcs.crval = cen, 0 + wcs.wcs.crpix = 360.5, 180.5 + wcs.wcs.cdelt = -0.5, 0.5 + + ax = fig.add_subplot(8, 1, icen, projection=wcs) + ax.set_xlim(-0.5, 719.5) + ax.coords[0].set_ticks(spacing=50 * u.deg) + ax.coords[0].set_ticks_position("b") + ax.coords[0].set_auto_axislabel(False) + ax.coords[1].set_auto_axislabel(False) + ax.coords[1].set_ticklabel_visible(False) + ax.coords[1].set_ticks_visible(False) + + fig.subplots_adjust(hspace=2, left=0.05, right=0.95, bottom=0.1, top=0.95) + + canvas.draw() + return fig + + +@figure_test +def test_tickable_gridlines(): + wcs = WCS( + { + "naxis": 2, + "naxis1": 360, + "naxis2": 180, + "crpix1": 180.5, + "crpix2": 90.5, + "cdelt1": -1, + "cdelt2": 1, + "ctype1": "RA---CAR", + "ctype2": "DEC--CAR", + } + ) + + fig = Figure() + canvas = FigureCanvasAgg(fig) + ax = fig.add_subplot(projection=wcs) + ax.set_xlim(-0.5, 360 - 0.5) + ax.set_ylim(-0.5, 150 - 0.5) + + lon, lat = ax.coords + lon.grid() + lat.grid() + + overlay = ax.get_coords_overlay("galactic") + overlay[0].set_ticks(spacing=30 * u.deg) + overlay[1].set_ticks(spacing=30 * u.deg) + + # Test both single-character and multi-character names + overlay[1].add_tickable_gridline("g", -30 * u.deg) + overlay[0].add_tickable_gridline("const-glon", 30 * u.deg) + + overlay[0].grid(color="magenta") + overlay[0].set_ticklabel_position("gt") + overlay[0].set_ticklabel(color="magenta") + overlay[0].set_axislabel("Galactic longitude", color="magenta") + + overlay[1].grid(color="blue") + overlay[1].set_ticklabel_position(("const-glon", "r")) + overlay[1].set_ticklabel(color="blue") + overlay[1].set_axislabel("Galactic latitude", color="blue") + + canvas.draw() + return fig + + +@pytest.fixture +def nondegree_frame(): + # Provide a frame where the default units are not degrees for either longitude or latitude + class FakeICRS(ICRS): + frame_specific_representation_info = { + SphericalRepresentation: [ + RepresentationMapping("lon", "ra", u.hourangle), + RepresentationMapping("lat", "dec", u.arcmin), + ] + } + + # We need valid transformations to/from a real frame, and they are just identity transformations + trans1 = StaticMatrixTransform( + np.identity(3), ICRS, FakeICRS, register_graph=frame_transform_graph + ) + trans2 = StaticMatrixTransform( + np.identity(3), FakeICRS, ICRS, register_graph=frame_transform_graph + ) + + yield FakeICRS + + # Clean up the transformation graph so that other tests are not affected + trans1.unregister(frame_transform_graph) + trans2.unregister(frame_transform_graph) + + +@figure_test +def test_overlay_nondegree_unit(nondegree_frame): + wcs = WCS( + { + "CTYPE1": "RA---TAN", + "CTYPE2": "DEC--TAN", + "CDELT1": -1, + "CDELT2": 1, + "CRPIX1": 20 + 0.5, + "CRPIX2": 0 + 0.5, + "CRVAL1": 0, + "CRVAL2": 0, + } + ) + + fig = Figure() + canvas = FigureCanvasAgg(fig) + ax = fig.add_subplot(projection=wcs) + + ax.set_xlim(-0.5, 20 - 0.5) + ax.set_ylim(-0.5, 20 - 0.5) + ax.set_aspect("equal") + + ax.coords[0].set_ticks_position("b") + ax.coords[0].grid() + ax.coords[1].set_ticks_position("l") + ax.coords[1].set_ticks(color="b") + ax.coords[1].set_ticklabel(color="b") + ax.coords[1].grid(color="b") + + overlay = ax.get_coords_overlay(nondegree_frame()) + overlay[0].set_ticks_position("t") + overlay[1].set_ticks_position("r") + overlay[1].set_ticks(color="r") + overlay[1].set_ticklabel(color="r") + overlay[1].grid(color="r", linestyle="dashed") + + canvas.draw() + return fig + + +@figure_test +def test_nosimplify(): + wcs = WCS( + { + "ctype1": "RA---CAR", + "ctype2": "DEC--CAR", + "crval1": 0, + "crval2": 0, + "cdelt1": -1, + "cdelt2": 1, + "crpix1": 1, + "crpix2": 1, + } + ) + + fig = Figure(figsize=(7, 8)) + canvas = FigureCanvasAgg(fig) + ax = fig.add_subplot(projection=wcs) + + ax.coords[0].set_ticks(spacing=0.25 * u.hourangle) + ax.coords[0].set_ticklabel(rotation=90, pad=25, simplify=False) + ax.coords[0].set_ticklabel_position("bt") + + ax.coords[1].set_ticks(spacing=0.25 * u.deg) + ax.coords[1].set_ticklabel(simplify=False) + ax.coords[1].set_ticklabel_position("lr") + + ax.set_xlim(-30, 30) + ax.set_ylim(-2, 2) + ax.set_aspect(15) + ax.grid() + + canvas.draw() + return fig + + +@figure_test +def test_custom_formatter(spatial_wcs_2d_small_angle): + def double_format(value, **kwargs): + if np.iterable(value): + return [f"{(v * 2):.4f}" for v in value] + else: + return f"{(value * 2):.2f}" + + def fruit_format(value, **kwargs): + fruits = ["apple", "pear", "banana", "orange", "kiwi", "grape"] + if np.iterable(value): + return (fruits * 10)[: len(value)] + else: + return "apple" + + fig = Figure() + canvas = FigureCanvasAgg(fig) + ax = fig.add_subplot(1, 1, 1, projection=spatial_wcs_2d_small_angle) + ax.coords[0].set_major_formatter(double_format) + ax.coords[1].set_major_formatter(fruit_format) + canvas.draw() + return fig + + +class EquatorialArcsecWCS(BaseLowLevelWCS): + @property + def pixel_n_dim(self): + return 2 + + @property + def world_n_dim(self): + return 2 + + @property + def world_axis_physical_types(self): + return [ + "pos.eq.ra", + "pos.eq.dec", + ] + + @property + def world_axis_units(self): + return ["arcsec", "arcsec"] + + @property + def world_axis_names(self): + return ["RA", "DEC"] + + def pixel_to_world_values(self, *pixel_arrays): + return pixel_arrays + + def world_to_pixel_values(self, *world_arrays): + return world_arrays + + @property + def world_axis_object_components(self): + return [ + ("celestial", 0, "spherical.lon.degree"), + ("celestial", 1, "spherical.lat.degree"), + ] + + @property + def world_axis_object_classes(self): + return { + "celestial": (SkyCoord, (), {"unit": "deg"}), + } + + +@figure_test +def test_equatorial_arcsec(): + wcs = EquatorialArcsecWCS() + + fig = Figure() + canvas = FigureCanvasAgg(fig) + ax = fig.add_subplot(projection=wcs) + + ax.set_xlim(-0.5, 20 - 0.5) + ax.set_ylim(-0.5, 30 - 0.5) + return fig + + +@figure_test +def test_wcs_preserve_units(): + # Test to make sure WCS with preserve_units=True works fine + + header = fits.Header() + header["CTYPE1"] = "RA---TAN" + header["CTYPE2"] = "DEC--TAN" + header["CUNIT1"] = "arcsec" + header["CUNIT2"] = "arcsec" + header["CRVAL1"] = 20 + header["CRVAL2"] = 20 + header["CRPIX1"] = 1 + header["CRPIX2"] = 1 + header["CDELT1"] = -1 + header["CDELT2"] = 1 + + wcs = WCS(header, preserve_units=True) + + fig = Figure() + canvas = FigureCanvasAgg(fig) + ax = fig.add_subplot(1, 1, 1, projection=wcs) + ax.set_xlim(-0.5, 30) + ax.set_ylim(-0.5, 20) + return fig diff --git a/astropy/visualization/wcsaxes/tests/test_misc.py b/astropy/visualization/wcsaxes/tests/test_misc.py new file mode 100644 index 000000000000..e2395e4fe876 --- /dev/null +++ b/astropy/visualization/wcsaxes/tests/test_misc.py @@ -0,0 +1,826 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +import warnings +from unittest.mock import MagicMock + +import matplotlib as mpl +import numpy as np +import pytest +from matplotlib.backends.backend_agg import FigureCanvasAgg +from matplotlib.contour import QuadContourSet +from matplotlib.figure import Figure +from numpy.testing import assert_allclose +from packaging.version import Version + +from astropy import units as u +from astropy.coordinates import SkyCoord +from astropy.io import fits +from astropy.utils.data import get_pkg_data_filename +from astropy.visualization.wcsaxes.core import WCSAxes +from astropy.visualization.wcsaxes.frame import ( + EllipticalFrame, + RectangularFrame, + RectangularFrame1D, +) +from astropy.visualization.wcsaxes.ticklabels import TickLabels +from astropy.visualization.wcsaxes.transforms import CurvedTransform +from astropy.visualization.wcsaxes.utils import get_coord_meta +from astropy.wcs import WCS +from astropy.wcs.wcsapi import HighLevelWCSWrapper, SlicedLowLevelWCS + +ft_version = Version(mpl.ft2font.__freetype_version__) +FREETYPE_261 = ft_version == Version("2.6.1") + +# We cannot use matplotlib.checkdep_usetex() anymore, see +# https://github.com/matplotlib/matplotlib/issues/23244 +TEX_UNAVAILABLE = True + + +def test_grid_regression(ignore_matplotlibrc): + # Regression test for a bug that meant that if the rc parameter + # axes.grid was set to True, WCSAxes would crash upon initialization. + mpl.rc("axes", grid=True) + fig = Figure(figsize=(3, 3)) + WCSAxes(fig, [0.1, 0.1, 0.8, 0.8]) + + +def test_format_coord_regression(ignore_matplotlibrc, tmp_path): + # Regression test for a bug that meant that if format_coord was called by + # Matplotlib before the axes were drawn, an error occurred. + fig = Figure(figsize=(3, 3)) + ax = WCSAxes(fig, [0.1, 0.1, 0.8, 0.8]) + fig.add_axes(ax) + assert ax.format_coord(10, 10) == "" + assert ax.coords[0].format_coord(10) == "" + assert ax.coords[1].format_coord(10) == "" + fig.savefig(tmp_path / "nothing") + assert ax.format_coord(10, 10) == "10.0 10.0 (world)" + assert ax.coords[0].format_coord(10) == "10.0" + assert ax.coords[1].format_coord(10) == "10.0" + + +TARGET_HEADER = fits.Header.fromstring( + """ +NAXIS = 2 +NAXIS1 = 200 +NAXIS2 = 100 +CTYPE1 = 'RA---MOL' +CRPIX1 = 500 +CRVAL1 = 180.0 +CDELT1 = -0.4 +CUNIT1 = 'deg ' +CTYPE2 = 'DEC--MOL' +CRPIX2 = 400 +CRVAL2 = 0.0 +CDELT2 = 0.4 +CUNIT2 = 'deg ' +COORDSYS= 'icrs ' +""", + sep="\n", +) + + +@pytest.mark.parametrize("grid_type", ["lines", "contours"]) +def test_no_numpy_warnings(ignore_matplotlibrc, tmp_path, grid_type): + fig = Figure() + ax = fig.add_subplot(1, 1, 1, projection=WCS(TARGET_HEADER)) + ax.imshow(np.zeros((100, 200))) + ax.coords.grid(color="white", grid_type=grid_type) + + # There should be no warnings raised if some pixels are outside WCS + # (since this is normal). + # BUT our own catch_warning was ignoring some warnings before, so now we + # have to catch it. Otherwise, the pytest filterwarnings=error + # setting in pyproject.toml will fail this test. + # There are actually multiple warnings but they are all similar. + with warnings.catch_warnings(): + warnings.filterwarnings( + "ignore", message=r".*converting a masked element to nan.*" + ) + warnings.filterwarnings( + "ignore", message=r".*No contour levels were found within the data range.*" + ) + warnings.filterwarnings( + "ignore", message=r".*np\.asscalar\(a\) is deprecated since NumPy v1\.16.*" + ) + warnings.filterwarnings( + "ignore", message=r".*PY_SSIZE_T_CLEAN will be required.*" + ) + fig.savefig(tmp_path / "test.png") + + +def test_invalid_frame_overlay(ignore_matplotlibrc): + # Make sure a nice error is returned if a frame doesn't exist + fig = Figure() + ax = fig.add_subplot(1, 1, 1, projection=WCS(TARGET_HEADER)) + with pytest.raises(ValueError, match=r"Frame banana not found"): + ax.get_coords_overlay("banana") + + with pytest.raises(ValueError, match=r"Unknown frame: banana"): + get_coord_meta("banana") + + +def test_plot_coord_transform(ignore_matplotlibrc): + twoMASS_k_header = get_pkg_data_filename("data/2MASS_k_header") + twoMASS_k_header = fits.Header.fromtextfile(twoMASS_k_header) + fig = Figure(figsize=(6, 6)) + ax = fig.add_axes( + [0.15, 0.15, 0.8, 0.8], projection=WCS(twoMASS_k_header), aspect="equal" + ) + ax.set_xlim(-0.5, 720.5) + ax.set_ylim(-0.5, 720.5) + + c = SkyCoord(359.76045223 * u.deg, 0.26876217 * u.deg) + with pytest.raises(TypeError): + ax.plot_coord(c, "o", transform=ax.get_transform("galactic")) + + +def test_scatter_coord_transform(ignore_matplotlibrc): + twoMASS_k_header = get_pkg_data_filename("data/2MASS_k_header") + twoMASS_k_header = fits.Header.fromtextfile(twoMASS_k_header) + fig = Figure(figsize=(6, 6)) + ax = fig.add_axes( + [0.15, 0.15, 0.8, 0.8], projection=WCS(twoMASS_k_header), aspect="equal" + ) + ax.set_xlim(-0.5, 720.5) + ax.set_ylim(-0.5, 720.5) + + c = SkyCoord(359.76045223 * u.deg, 0.26876217 * u.deg) + with pytest.raises(TypeError): + ax.scatter_coord(c, marker="o", transform=ax.get_transform("galactic")) + + +def test_set_label_properties(ignore_matplotlibrc): + # Regression test to make sure that arguments passed to + # set_xlabel/set_ylabel are passed to the underlying coordinate helpers + + fig = Figure() + ax = fig.add_subplot(1, 1, 1, projection=WCS(TARGET_HEADER)) + + ax.set_xlabel("Test x label", labelpad=2, color="red") + ax.set_ylabel("Test y label", labelpad=3, color="green") + + assert ax.coords[0]._axislabels.get_text() == "Test x label" + assert ax.coords[0]._axislabels.get_minpad("b") == 2 + assert ax.coords[0]._axislabels.get_color() == "red" + + assert ax.coords[1]._axislabels.get_text() == "Test y label" + assert ax.coords[1]._axislabels.get_minpad("l") == 3 + assert ax.coords[1]._axislabels.get_color() == "green" + + assert ax.get_xlabel() == "Test x label" + assert ax.get_ylabel() == "Test y label" + + +GAL_HEADER = fits.Header.fromstring( + """ +SIMPLE = T / conforms to FITS standard +BITPIX = -32 / array data type +NAXIS = 3 / number of array dimensions +NAXIS1 = 31 +NAXIS2 = 2881 +NAXIS3 = 480 +EXTEND = T +CTYPE1 = 'DISTMOD ' +CRVAL1 = 3.5 +CDELT1 = 0.5 +CRPIX1 = 1.0 +CTYPE2 = 'GLON-CAR' +CRVAL2 = 180.0 +CDELT2 = -0.125 +CRPIX2 = 1.0 +CTYPE3 = 'GLAT-CAR' +CRVAL3 = 0.0 +CDELT3 = 0.125 +CRPIX3 = 241.0 +""", + sep="\n", +) + + +def test_slicing_warnings(ignore_matplotlibrc, tmp_path): + # Regression test to make sure that no warnings are emitted by the tick + # locator for the sliced axis when slicing a cube. + + # Scalar case + + wcs3d = WCS(naxis=3) + wcs3d.wcs.ctype = ["x", "y", "z"] + wcs3d.wcs.cunit = ["deg", "deg", "km/s"] + wcs3d.wcs.crpix = [614.5, 856.5, 333] + wcs3d.wcs.cdelt = [6.25, 6.25, 23] + wcs3d.wcs.crval = [0.0, 0.0, 1.0] + + fig = Figure() + with warnings.catch_warnings(): + # https://github.com/astropy/astropy/issues/9690 + warnings.filterwarnings("ignore", message=r".*PY_SSIZE_T_CLEAN.*") + fig.add_subplot(1, 1, 1, projection=wcs3d, slices=("x", "y", 1)) + fig.savefig(tmp_path / "test.png") + + # Angle case + + wcs3d = WCS(GAL_HEADER) + + with warnings.catch_warnings(): + # https://github.com/astropy/astropy/issues/9690 + warnings.filterwarnings("ignore", message=r".*PY_SSIZE_T_CLEAN.*") + fig.clear() + fig.add_subplot(1, 1, 1, projection=wcs3d, slices=("x", "y", 2)) + fig.savefig(tmp_path / "test.png") + + +@pytest.fixture +def tmp_plt_figure(): + import matplotlib.pyplot as plt + + figure = plt.figure() + yield figure + plt.close(figure) + + +@pytest.mark.usefixtures("tmp_plt_figure") +def test_plt_xlabel_ylabel(tmp_path): + # Regression test for a bug that happened when using plt.xlabel + # and plt.ylabel with Matplotlib 3.0 + import matplotlib.pyplot as plt + + plt.subplot(projection=WCS()) + plt.xlabel("Galactic Longitude") + plt.ylabel("Galactic Latitude") + plt.savefig(tmp_path / "test.png") + + +def test_grid_type_contours_transform(tmp_path): + # Regression test for a bug that caused grid_type='contours' to not work + # with custom transforms + + class CustomTransform(CurvedTransform): + # We deliberately don't define the inverse, and has_inverse should + # default to False. + + def transform(self, values): + return values * 1.3 + + transform = CustomTransform() + coord_meta = { + "type": ("scalar", "scalar"), + "unit": (u.m, u.s), + "wrap": (None, None), + "name": ("x", "y"), + } + + fig = Figure() + ax = WCSAxes(fig, [0.1, 0.1, 0.8, 0.8], transform=transform, coord_meta=coord_meta) + fig.add_axes(ax) + ax.grid(grid_type="contours") + fig.savefig(tmp_path / "test.png") + + +@pytest.mark.usefixtures("tmp_plt_figure") +def test_plt_imshow_origin(): + # Regression test for a bug that caused origin to be set to upper when + # plt.imshow was called. + import matplotlib.pyplot as plt + + ax = plt.subplot(projection=WCS()) + plt.imshow(np.ones((2, 2))) + assert ax.get_xlim() == (-0.5, 1.5) + assert ax.get_ylim() == (-0.5, 1.5) + + +def test_ax_imshow_origin(): + # Regression test for a bug that caused origin to be set to upper when + # ax.imshow was called with no origin + fig = Figure() + ax = fig.add_subplot(projection=WCS()) + ax.imshow(np.ones((2, 2))) + assert ax.get_xlim() == (-0.5, 1.5) + assert ax.get_ylim() == (-0.5, 1.5) + + +def test_grid_contour_large_spacing(tmp_path): + # Regression test for a bug that caused a crash when grid was called and + # didn't produce grid lines (due e.g. to too large spacing) and was then + # called again. + + filename = tmp_path / "test.png" + + fig = Figure() + ax = fig.add_subplot(projection=WCS()) + ax.set_xlim(-0.5, 1.5) + ax.set_ylim(-0.5, 1.5) + ax.coords[0].set_ticks(values=[] * u.one) + + ax.coords[0].grid(grid_type="contours") + fig.savefig(filename) + + ax.coords[0].grid(grid_type="contours") + fig.savefig(filename) + + +def test_contour_return(): + # Regression test for a bug that caused contour and contourf to return None + # instead of the contour object. + + fig = Figure() + ax = WCSAxes(fig, [0.1, 0.1, 0.8, 0.8]) + fig.add_axes(ax) + + cset = ax.contour(np.arange(16).reshape(4, 4), transform=ax.get_transform("world")) + assert isinstance(cset, QuadContourSet) + + cset = ax.contourf(np.arange(16).reshape(4, 4), transform=ax.get_transform("world")) + assert isinstance(cset, QuadContourSet) + + +def test_contour_empty(): + # Regression test for a bug that caused contour to crash if no contours + # were present. + + fig = Figure() + ax = WCSAxes(fig, [0.1, 0.1, 0.8, 0.8]) + fig.add_axes(ax) + ax.contour(np.zeros((4, 4)), transform=ax.get_transform("world")) + + +def test_iterate_coords(ignore_matplotlibrc): + # Regression test for a bug that caused ax.coords to return too few axes + + wcs3d = WCS(naxis=3) + wcs3d.wcs.ctype = ["x", "y", "z"] + wcs3d.wcs.cunit = ["deg", "deg", "km/s"] + wcs3d.wcs.crpix = [614.5, 856.5, 333] + wcs3d.wcs.cdelt = [6.25, 6.25, 23] + wcs3d.wcs.crval = [0.0, 0.0, 1.0] + + fig = Figure() + ax = fig.add_subplot(1, 1, 1, projection=wcs3d, slices=("x", "y", 1)) + + x, y, z = ax.coords + + +def test_invalid_slices_errors(ignore_matplotlibrc): + # Make sure that users get a clear message when specifying a WCS with + # >2 dimensions without giving the 'slices' argument, or if the 'slices' + # argument has too many/few elements. + fig = Figure() + wcs3d = WCS(naxis=3) + wcs3d.wcs.ctype = ["x", "y", "z"] + + fig.add_subplot(1, 1, 1, projection=wcs3d, slices=("x", "y", 1)) + + with pytest.raises( + ValueError, + match=r"WCS has more than 2 pixel dimensions, so 'slices' should be set", + ): + fig.add_subplot(1, 1, 1, projection=wcs3d) + + with pytest.raises( + ValueError, + match=( + r"'slices' should have as many elements as WCS has pixel dimensions .should" + r" be 3." + ), + ): + fig.add_subplot(1, 1, 1, projection=wcs3d, slices=("x", "y", 1, 2)) + + wcs2d = WCS(naxis=2) + wcs2d.wcs.ctype = ["x", "y"] + + fig.clear() + ax = fig.add_subplot(1, 1, 1, projection=wcs2d) + assert ax.frame_class is RectangularFrame + fig.clear() + ax = fig.add_subplot(1, 1, 1, projection=wcs2d, slices=("x", "y")) + assert ax.frame_class is RectangularFrame + fig.clear() + ax = fig.add_subplot(1, 1, 1, projection=wcs2d, slices=("y", "x")) + assert ax.frame_class is RectangularFrame + fig.clear() + ax = fig.add_subplot(1, 1, 1, projection=wcs2d, slices=["x", "y"]) + assert ax.frame_class is RectangularFrame + fig.clear() + ax = fig.add_subplot(1, 1, 1, projection=wcs2d, slices=(1, "x")) + assert ax.frame_class is RectangularFrame1D + + wcs1d = WCS(naxis=1) + wcs1d.wcs.ctype = ["x"] + + fig.clear() + ax = fig.add_subplot(1, 1, 1, projection=wcs1d) + assert ax.frame_class is RectangularFrame1D + + with pytest.raises(ValueError): + fig.add_subplot(1, 1, 1, projection=wcs2d, slices=(1, "y")) + + +EXPECTED_REPR_1 = """ + + """.strip() + +EXPECTED_REPR_2 = """ + + """.strip() + + +def test_repr(ignore_matplotlibrc): + # Unit test to make sure __repr__ looks as expected + fig = Figure() + wcs3d = WCS(GAL_HEADER) + + # Cube header has world coordinates as distance, lon, lat, so start off + # by slicing in a way that we select just lon,lat: + + ax = fig.add_subplot(1, 1, 1, projection=wcs3d, slices=(1, "x", "y")) + assert repr(ax.coords) == EXPECTED_REPR_1 + + # Now slice in a way that all world coordinates are still present: + + fig.clear() + ax = fig.add_subplot(1, 1, 1, projection=wcs3d, slices=("x", "y", 1)) + assert repr(ax.coords) == EXPECTED_REPR_2 + + +@pytest.fixture +def time_spectral_wcs_2d(): + wcs = WCS(naxis=2) + wcs.wcs.ctype = ["FREQ", "TIME"] + wcs.wcs.set() + return wcs + + +def test_time_wcs(time_spectral_wcs_2d): + # Regression test for a bug that caused WCSAxes to error when using a WCS + # with a time axis. + fig = Figure() + fig.add_subplot(projection=time_spectral_wcs_2d) + + +@pytest.mark.skipif(TEX_UNAVAILABLE, reason="TeX is unavailable") +def test_simplify_labels_usetex(ignore_matplotlibrc, tmp_path): + """Regression test for https://github.com/astropy/astropy/issues/8004.""" + mpl.rc("text", usetex=True) + + header = { + "NAXIS": 2, + "NAXIS1": 360, + "NAXIS2": 180, + "CRPIX1": 180.5, + "CRPIX2": 90.5, + "CRVAL1": 180.0, + "CRVAL2": 0.0, + "CDELT1": -2 * np.sqrt(2) / np.pi, + "CDELT2": 2 * np.sqrt(2) / np.pi, + "CTYPE1": "RA---MOL", + "CTYPE2": "DEC--MOL", + "RADESYS": "ICRS", + } + + wcs = WCS(header) + fig = Figure() + ax = fig.add_subplot(frame_class=EllipticalFrame, projection=wcs) + ax.set_xlim(-0.5, header["NAXIS1"] - 0.5) + ax.set_ylim(-0.5, header["NAXIS2"] - 0.5) + ax.coords[0].set_ticklabel(exclude_overlapping=True) + ax.coords[1].set_ticklabel(exclude_overlapping=True) + ax.coords[0].set_ticks(spacing=45 * u.deg) + ax.coords[1].set_ticks(spacing=30 * u.deg) + ax.grid() + + fig.savefig(tmp_path / "plot.png") + + +@pytest.mark.parametrize( + "usetex, unicode_minus, label_str", + [ + (True, True, "$-{}$"), + (True, False, "$-{}$"), + (False, True, "\N{MINUS SIGN}{}"), + (False, False, "-{}"), + ], +) +def test_simplify_labels_minus_sign( + ignore_matplotlibrc, usetex, unicode_minus, label_str +): + # Ensure minus signs aren't removed from the front of labels across a grid of configuration possibilities + if usetex and TEX_UNAVAILABLE: + pytest.skip("TeX is unavailable") + + ticklabels = TickLabels(frame=MagicMock()) + expected_labels = [] + for i in range(1, 6): + label = label_str.format(i) + ticklabels.add( + axis="axis", + world=0, + angle=0, + text=label, + axis_displacement=0, + data=(i, i), + ) + expected_labels.append(label) + + with mpl.rc_context( + rc={"text.usetex": usetex, "axes.unicode_minus": unicode_minus} + ): + ticklabels.simplify_labels() + assert ticklabels.text["axis"] == expected_labels + + +@pytest.mark.parametrize("frame_class", [RectangularFrame, EllipticalFrame]) +def test_set_labels_with_coords(ignore_matplotlibrc, frame_class): + """Test if ``axis.set_xlabel()`` calls the correct ``coords[i]_set_axislabel()`` in a + WCS plot. Regression test for https://github.com/astropy/astropy/issues/10435. + """ + labels = ["RA", "Declination"] + header = { + "NAXIS": 2, + "NAXIS1": 360, + "NAXIS2": 180, + "CRPIX1": 180.5, + "CRPIX2": 90.5, + "CRVAL1": 180.0, + "CRVAL2": 0.0, + "CDELT1": -2 * np.sqrt(2) / np.pi, + "CDELT2": 2 * np.sqrt(2) / np.pi, + "CTYPE1": "RA---AIT", + "CTYPE2": "DEC--AIT", + } + + wcs = WCS(header) + fig = Figure() + ax = fig.add_subplot(frame_class=frame_class, projection=wcs) + ax.set_xlabel(labels[0]) + ax.set_ylabel(labels[1]) + + assert ax.get_xlabel() == labels[0] + assert ax.get_ylabel() == labels[1] + for i in range(2): + assert ax.coords[i].get_axislabel() == labels[i] + + +@pytest.mark.parametrize("atol", [0.2, 1.0e-8]) +def test_bbox_size(atol): + # Test for the size of a WCSAxes bbox (only have Matplotlib >= 3.0 now) + extents = [11.38888888888889, 3.5, 576.0, 432.0] + + fig = Figure() + canvas = FigureCanvasAgg(fig) + ax = WCSAxes(fig, [0.1, 0.1, 0.8, 0.8]) + canvas.figure.add_axes(ax) + canvas.draw() + renderer = fig.canvas.renderer + ax_bbox = ax.get_tightbbox(renderer) + + # Enforce strict test only with reference Freetype version + if atol < 0.1 and not FREETYPE_261: + pytest.xfail( + "Exact BoundingBox dimensions are only ensured with FreeType 2.6.1" + ) + assert np.allclose(ax_bbox.extents, extents, atol=atol) + + +def test_wcs_type_transform_regression(): + fig = Figure() + wcs = WCS(TARGET_HEADER) + sliced_wcs = SlicedLowLevelWCS(wcs, np.s_[1:-1, 1:-1]) + ax = fig.add_subplot(1, 1, 1, projection=wcs) + ax.get_transform(sliced_wcs) + + high_wcs = HighLevelWCSWrapper(sliced_wcs) + ax.get_transform(sliced_wcs) + + +def test_multiple_draws_grid_contours(tmp_path): + fig = Figure() + ax = fig.add_subplot(1, 1, 1, projection=WCS()) + ax.grid(color="black", grid_type="contours") + fig.savefig(tmp_path / "plot.png") + fig.savefig(tmp_path / "plot.png") + + +def test_get_coord_range_nan_regression(): + # Test to make sure there is no internal casting of NaN to integers + # NumPy 1.24 raises a RuntimeWarning if a NaN is cast to an integer + + fig = Figure() + wcs = WCS(TARGET_HEADER) + wcs.wcs.crval[0] = 0 # Re-position the longitude wrap to the middle + ax = fig.add_subplot(1, 1, 1, projection=wcs) + + # Set the Y limits within valid latitudes/declinations + ax.set_ylim(300, 500) + + # Set the X limits within valid longitudes/RAs, so the world coordinates have no NaNs + ax.set_xlim(300, 700) + assert np.allclose( + ax.coords.get_coord_range(), + np.array( + [ + (-123.5219272110385, 122.49684897692201), + (-44.02289164685554, 44.80732766607591), + ] + ), + ) + + # Extend the X limits to include invalid longitudes/RAs, so the world coordinates have NaNs + ax.set_xlim(0, 700) + assert np.allclose( + ax.coords.get_coord_range(), + np.array( + [(-131.3193386797236, 180.0), (-44.02289164685554, 44.80732766607591)] + ), + ) + + +def test_imshow_error(): + fig = Figure() + ax = fig.add_subplot(1, 1, 1, projection=WCS()) + with pytest.raises(ValueError, match="Cannot use images with origin='upper"): + ax.imshow(np.ones(100).reshape(10, 10), origin="upper") + + +def test_label_setting(): + fig = Figure() + ax = fig.add_subplot(1, 1, 1, projection=WCS()) + # Check both xlabel and label kwargs work + ax.set_xlabel(xlabel="label") + ax.set_xlabel(label="label") + # Check no label errors: + with pytest.raises( + TypeError, match=r"set_xlabel\(\) missing 1 required positional argument" + ): + ax.set_xlabel() + + # Check both xlabel and label kwargs work + ax.set_ylabel(ylabel="label") + ax.set_ylabel(label="label") + # Check no label errors: + with pytest.raises( + TypeError, match=r"set_ylabel\(\) missing 1 required positional argument" + ): + ax.set_ylabel() + + +def test_invisible_bbox(): + fig = Figure() + _canvas = FigureCanvasAgg(fig) + ax = fig.add_subplot(1, 1, 1, projection=WCS()) + + assert ax.get_tightbbox(fig.canvas.get_renderer()) is not None + ax.set_visible(False) + assert ax.get_tightbbox(fig.canvas.get_renderer()) is None + + +def test_get_axislabel_default(): + wcs = WCS(naxis=2) + wcs.wcs.ctype = "RA---TAN", "DEC--TAN" + + fig = Figure() + _canvas = FigureCanvasAgg(fig) + ax = fig.add_subplot(1, 1, 1, projection=wcs) + assert ax.coords[0].get_axislabel() == "pos.eq.ra" + assert ax.coords[1].get_axislabel() == "pos.eq.dec" + + # Make sure that setting axis labels explicitly works, including to an + # empty string + ax.coords[0].set_axislabel("Right Ascension") + ax.coords[1].set_axislabel("") + assert ax.coords[0].get_axislabel() == "Right Ascension" + assert ax.coords[1].get_axislabel() == "" + + +def test_plot_coord_slicing(ignore_matplotlibrc): + # Regression test to ensure plot_coord does not raise a NotImplementedError + # after slicing coordinates (issue #10254). + + cube_header = get_pkg_data_filename("data/cube_header") + cube_header = fits.Header.fromtextfile(cube_header) + + fig = Figure(figsize=(6, 6)) + ax = fig.add_subplot(projection=WCS(cube_header), slices=("x", "y", 0)) + + c = SkyCoord(52 * u.deg, 30.5 * u.deg) + ax.plot_coord(c, "o") + + +SIMPLIFY_CASES = [ + (["13d14m15s", "13d14m15s"], ["13d14m15s", "15s"]), + (["13d14m5s", "13d14m15s"], ["13d14m5s", "15s"]), + ( + ["−29°25'55\"", "−29°25'54\"", "−29°25'53\"", "−29°25'52\"", "−29°25'51\""], + ["−29°25'55\"", '54"', '53"', '52"', '51"'], + ), + ( + [ + r"0$\mathregular{^h}$", + r"10$\mathregular{^h}$", + r"10$\mathregular{^h}$", + r"5$\mathregular{^h}$", + ], + [ + r"0$\mathregular{^h}$", + r"10$\mathregular{^h}$", + r"10$\mathregular{^h}$", + r"5$\mathregular{^h}$", + ], + ), + ( + [ + r"0$\mathregular{^h}$10$\mathregular{^m}$5$\mathregular{^m}$", + r"0$\mathregular{^h}$10$\mathregular{^m}$10$\mathregular{^m}$", + r"0$\mathregular{^h}$10$\mathregular{^m}$15$\mathregular{^m}$", + r"0$\mathregular{^h}$10$\mathregular{^m}$15$\mathregular{^m}$", + ], + [ + r"0$\mathregular{^h}$10$\mathregular{^m}$5$\mathregular{^m}$", + r"10$\mathregular{^m}$", + r"15$\mathregular{^m}$", + r"15$\mathregular{^m}$", + ], + ), + ( + [ + "$17^{\\mathrm{h}}47^{\\mathrm{m}}53.8^{\\mathrm{s}}$", + "$17^{\\mathrm{h}}47^{\\mathrm{m}}53.6^{\\mathrm{s}}$", + ], + [ + "$17^{\\mathrm{h}}47^{\\mathrm{m}}53.8^{\\mathrm{s}}$", + "$53.6^{\\mathrm{s}}$", + ], + ), +] + + +@pytest.mark.parametrize(("before", "after"), SIMPLIFY_CASES) +def test_simplify_cases(before, after): + # Regression test for a bug that caused the simplification to not work + # correctly in the presence of dollar signs in LaTeX strings. + + ticklabels = TickLabels(frame=MagicMock()) + expected_labels = [] + + for i, label in enumerate(before): + ticklabels.add( + axis="axis", + world=0, + angle=0, + text=label, + axis_displacement=i, + data=(i, i), + ) + + ticklabels.simplify_labels() + assert ticklabels.text["axis"] == after + + +def test_get_transform_unit_mismatch(): + """ + Regression test for a bug that caused get_transform to ignore differences + in WCS units. + + https://github.com/astropy/astropy/issues/18246 + """ + + wcs_deg = WCS(naxis=2, preserve_units=True) + wcs_deg.wcs.ctype = "RA---TAN", "DEC--TAN" + wcs_deg.wcs.crval = 20, 30 + wcs_deg.wcs.cunit = "deg", "deg" + wcs_deg.wcs.crpix = 1, 1 + wcs_deg.wcs.cdelt = 1 / 60, 1 / 60 + + wcs_arcmin = WCS(naxis=2, preserve_units=True) + wcs_arcmin.wcs.ctype = "RA---TAN", "DEC--TAN" + wcs_arcmin.wcs.crval = 1200, 1800 + wcs_arcmin.wcs.cunit = "arcmin", "arcmin" + wcs_arcmin.wcs.crpix = 1, 1 + wcs_arcmin.wcs.cdelt = 1, 1 + + fig = Figure(figsize=(6, 6)) + ax = fig.add_subplot(projection=wcs_arcmin) + transform1 = ax.get_transform(wcs_arcmin) + transform2 = ax.get_transform(wcs_deg) + + # Since the two WCS are equivalent, the returned transforms should also + # be equivalent + + rng = np.random.default_rng(12345) + pixels = rng.uniform(0, 100, (2, 100)) + + assert_allclose(transform1.transform(pixels), transform2.transform(pixels)) diff --git a/astropy/visualization/wcsaxes/tests/test_transform_coord_meta.py b/astropy/visualization/wcsaxes/tests/test_transform_coord_meta.py new file mode 100644 index 000000000000..6c0353b28bd7 --- /dev/null +++ b/astropy/visualization/wcsaxes/tests/test_transform_coord_meta.py @@ -0,0 +1,155 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + + +import numpy as np +from matplotlib.figure import Figure + +from astropy import units as u +from astropy.tests.figures import figure_test +from astropy.visualization.wcsaxes import WCSAxes +from astropy.visualization.wcsaxes.transforms import CurvedTransform +from astropy.wcs import WCS + +from .test_images import BaseImageTests + +# Create fake transforms that roughly mimic a polar projection + + +class DistanceToLonLat(CurvedTransform): + has_inverse = True + + def __init__(self, R=6e3): + super().__init__() + self.R = R + + def transform(self, xy): + x, y = xy[:, 0], xy[:, 1] + lam = np.degrees(np.arctan2(y, x)) + phi = 90.0 - np.degrees(np.hypot(x, y) / self.R) + return np.array((lam, phi)).transpose() + + transform_non_affine = transform + + def inverted(self): + return LonLatToDistance(R=self.R) + + +class LonLatToDistance(CurvedTransform): + def __init__(self, R=6e3): + super().__init__() + self.R = R + + def transform(self, lamphi): + lam, phi = lamphi[:, 0], lamphi[:, 1] + r = np.radians(90 - phi) * self.R + x = r * np.cos(np.radians(lam)) + y = r * np.sin(np.radians(lam)) + return np.array((x, y)).transpose() + + transform_non_affine = transform + + def inverted(self): + return DistanceToLonLat(R=self.R) + + +class TestTransformCoordMeta(BaseImageTests): + @figure_test + def test_coords_overlay(self): + # Set up a simple WCS that maps pixels to non-projected distances + wcs = WCS(naxis=2) + wcs.wcs.ctype = ["x", "y"] + wcs.wcs.cunit = ["km", "km"] + wcs.wcs.crpix = [614.5, 856.5] + wcs.wcs.cdelt = [6.25, 6.25] + wcs.wcs.crval = [0.0, 0.0] + + fig = Figure(figsize=(4, 4)) + + ax = WCSAxes(fig, [0.15, 0.15, 0.7, 0.7], wcs=wcs) + fig.add_axes(ax) + + s = DistanceToLonLat(R=6378.273) + + ax.coords["x"].set_ticklabel_position("") + ax.coords["y"].set_ticklabel_position("") + + coord_meta = {} + coord_meta["type"] = ("longitude", "latitude") + coord_meta["wrap"] = (360.0 * u.deg, None) + coord_meta["unit"] = (u.deg, u.deg) + coord_meta["name"] = "lon", "lat" + + overlay = ax.get_coords_overlay(s, coord_meta=coord_meta) + + overlay.grid(color="red") + overlay["lon"].grid(color="red", linestyle="solid", alpha=0.3) + overlay["lat"].grid(color="blue", linestyle="solid", alpha=0.3) + + overlay["lon"].set_ticklabel(size=7, exclude_overlapping=True) + overlay["lat"].set_ticklabel(size=7, exclude_overlapping=True) + + overlay["lon"].set_ticklabel_position("brtl") + overlay["lat"].set_ticklabel_position("brtl") + + overlay["lon"].set_ticks(spacing=10.0 * u.deg) + overlay["lat"].set_ticks(spacing=10.0 * u.deg) + + ax.set_xlim(-0.5, 1215.5) + ax.set_ylim(-0.5, 1791.5) + + return fig + + @figure_test + def test_coords_overlay_auto_coord_meta(self): + fig = Figure(figsize=(4, 4)) + + ax = WCSAxes(fig, [0.15, 0.15, 0.7, 0.7], wcs=WCS(self.msx_header)) + fig.add_axes(ax) + + ax.grid(color="red", alpha=0.5, linestyle="solid") + + overlay = ax.get_coords_overlay("fk5") # automatically sets coord_meta + + overlay.grid(color="black", alpha=0.5, linestyle="solid") + + overlay["ra"].set_ticks(color="black") + overlay["dec"].set_ticks(color="black") + + ax.set_xlim(-0.5, 148.5) + ax.set_ylim(-0.5, 148.5) + + return fig + + @figure_test + def test_direct_init(self): + s = DistanceToLonLat(R=6378.273) + + coord_meta = {} + coord_meta["type"] = ("longitude", "latitude") + coord_meta["wrap"] = (360.0 * u.deg, None) + coord_meta["unit"] = (u.deg, u.deg) + coord_meta["name"] = "lon", "lat" + fig = Figure(figsize=(4, 4)) + + ax = WCSAxes(fig, [0.15, 0.15, 0.7, 0.7], transform=s, coord_meta=coord_meta) + fig.add_axes(ax) + + ax.coords["lon"].grid(color="red", linestyle="solid", alpha=0.3) + ax.coords["lat"].grid(color="blue", linestyle="solid", alpha=0.3) + + ax.coords["lon"].set_auto_axislabel(False) + ax.coords["lat"].set_auto_axislabel(False) + + ax.coords["lon"].set_ticklabel(size=7, exclude_overlapping=True) + ax.coords["lat"].set_ticklabel(size=7, exclude_overlapping=True) + + ax.coords["lon"].set_ticklabel_position("brtl") + ax.coords["lat"].set_ticklabel_position("brtl") + + ax.coords["lon"].set_ticks(spacing=10.0 * u.deg) + ax.coords["lat"].set_ticks(spacing=10.0 * u.deg) + + ax.set_xlim(-400.0, 500.0) + ax.set_ylim(-300.0, 400.0) + + return fig diff --git a/astropy/visualization/wcsaxes/tests/test_transforms.py b/astropy/visualization/wcsaxes/tests/test_transforms.py new file mode 100644 index 000000000000..9dce85d06f83 --- /dev/null +++ b/astropy/visualization/wcsaxes/tests/test_transforms.py @@ -0,0 +1 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst diff --git a/astropy/visualization/wcsaxes/tests/test_utils.py b/astropy/visualization/wcsaxes/tests/test_utils.py new file mode 100644 index 000000000000..c740b7abfe16 --- /dev/null +++ b/astropy/visualization/wcsaxes/tests/test_utils.py @@ -0,0 +1,88 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + + +from numpy.testing import assert_almost_equal + +from astropy import units as u +from astropy.coordinates import Angle, Galactic, HADec +from astropy.tests.helper import ( + assert_quantity_allclose as assert_almost_equal_quantity, +) +from astropy.visualization.wcsaxes.utils import ( + get_coord_meta, + select_step_degree, + select_step_hour, + select_step_scalar, +) + + +def test_select_step_degree(): + assert_almost_equal_quantity(select_step_degree(127 * u.deg), 180.0 * u.deg) + assert_almost_equal_quantity(select_step_degree(44 * u.deg), 45.0 * u.deg) + assert_almost_equal_quantity(select_step_degree(18 * u.arcmin), 15 * u.arcmin) + assert_almost_equal_quantity(select_step_degree(3.4 * u.arcmin), 3 * u.arcmin) + assert_almost_equal_quantity(select_step_degree(2 * u.arcmin), 2 * u.arcmin) + assert_almost_equal_quantity(select_step_degree(59 * u.arcsec), 1 * u.arcmin) + assert_almost_equal_quantity(select_step_degree(33 * u.arcsec), 30 * u.arcsec) + assert_almost_equal_quantity(select_step_degree(2.2 * u.arcsec), 2 * u.arcsec) + assert_almost_equal_quantity(select_step_degree(0.8 * u.arcsec), 1 * u.arcsec) + assert_almost_equal_quantity(select_step_degree(0.2 * u.arcsec), 0.2 * u.arcsec) + assert_almost_equal_quantity(select_step_degree(0.11 * u.arcsec), 0.1 * u.arcsec) + assert_almost_equal_quantity(select_step_degree(0.022 * u.arcsec), 0.02 * u.arcsec) + assert_almost_equal_quantity( + select_step_degree(0.0043 * u.arcsec), 0.005 * u.arcsec + ) + assert_almost_equal_quantity( + select_step_degree(0.00083 * u.arcsec), 0.001 * u.arcsec + ) + assert_almost_equal_quantity( + select_step_degree(0.000027 * u.arcsec), 0.00002 * u.arcsec + ) + + +def test_select_step_hour(): + assert_almost_equal_quantity(select_step_hour(127 * u.deg), 8.0 * u.hourangle) + assert_almost_equal_quantity(select_step_hour(44 * u.deg), 3.0 * u.hourangle) + assert_almost_equal_quantity(select_step_hour(18 * u.arcmin), 15 * u.arcmin) + assert_almost_equal_quantity(select_step_hour(3.4 * u.arcmin), 3 * u.arcmin) + assert_almost_equal_quantity(select_step_hour(2 * u.arcmin), 1.5 * u.arcmin) + assert_almost_equal_quantity(select_step_hour(59 * u.arcsec), 1 * u.arcmin) + assert_almost_equal_quantity(select_step_hour(33 * u.arcsec), 30 * u.arcsec) + assert_almost_equal_quantity(select_step_hour(2.2 * u.arcsec), 3.0 * u.arcsec) + assert_almost_equal_quantity(select_step_hour(0.8 * u.arcsec), 0.75 * u.arcsec) + assert_almost_equal_quantity(select_step_hour(0.2 * u.arcsec), 0.15 * u.arcsec) + assert_almost_equal_quantity(select_step_hour(0.11 * u.arcsec), 0.15 * u.arcsec) + assert_almost_equal_quantity(select_step_hour(0.022 * u.arcsec), 0.03 * u.arcsec) + assert_almost_equal_quantity(select_step_hour(0.0043 * u.arcsec), 0.003 * u.arcsec) + assert_almost_equal_quantity( + select_step_hour(0.00083 * u.arcsec), 0.00075 * u.arcsec + ) + assert_almost_equal_quantity( + select_step_hour(0.000027 * u.arcsec), 0.00003 * u.arcsec + ) + + +def test_select_step_scalar(): + assert_almost_equal(select_step_scalar(33122.0), 50000.0) + assert_almost_equal(select_step_scalar(433.0), 500.0) + assert_almost_equal(select_step_scalar(12.3), 10) + assert_almost_equal(select_step_scalar(3.3), 5.0) + assert_almost_equal(select_step_scalar(0.66), 0.5) + assert_almost_equal(select_step_scalar(0.0877), 0.1) + assert_almost_equal(select_step_scalar(0.00577), 0.005) + assert_almost_equal(select_step_scalar(0.00022), 0.0002) + assert_almost_equal(select_step_scalar(0.000012), 0.00001) + assert_almost_equal(select_step_scalar(0.000000443), 0.0000005) + + +def test_get_coord_meta(): + galactic_meta = get_coord_meta(Galactic) + assert galactic_meta["name"] == ["l", "b"] + assert galactic_meta["wrap"] == (Angle(360 * u.deg), None) + assert galactic_meta["unit"] == (u.deg, u.deg) + + hadec_meta = get_coord_meta(HADec) + assert hadec_meta["name"] == ["ha", "dec"] + assert hadec_meta["wrap"] == (Angle(180 * u.deg), None) + assert hadec_meta["unit"] == (u.deg, u.deg) + assert hadec_meta["format_unit"] == (u.hourangle, u.deg) diff --git a/astropy/visualization/wcsaxes/tests/test_wcsapi.py b/astropy/visualization/wcsaxes/tests/test_wcsapi.py new file mode 100644 index 000000000000..7c2106e7ab81 --- /dev/null +++ b/astropy/visualization/wcsaxes/tests/test_wcsapi.py @@ -0,0 +1,804 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +import warnings +from copy import deepcopy +from textwrap import dedent + +import numpy as np +import pytest +from matplotlib.backends.backend_agg import FigureCanvasAgg +from matplotlib.figure import Figure +from matplotlib.transforms import Affine2D, IdentityTransform + +from astropy import units as u +from astropy.coordinates import SkyCoord +from astropy.io import fits +from astropy.tests.figures import figure_test +from astropy.tests.helper import assert_quantity_allclose +from astropy.time import Time +from astropy.units import Quantity +from astropy.utils.data import get_pkg_data_filename +from astropy.visualization.wcsaxes.frame import RectangularFrame, RectangularFrame1D +from astropy.visualization.wcsaxes.wcsapi import ( + WCSPixel2WorldTransform, + WCSWorld2PixelTransform, + apply_slices, + custom_ucd_coord_meta_mapping, + transform_coord_meta_from_wcs, +) +from astropy.wcs import WCS +from astropy.wcs.wcsapi import BaseLowLevelWCS, SlicedLowLevelWCS +from astropy.wcs.wcsapi.fitswcs import custom_ctype_to_ucd_mapping + +WCS2D = WCS(naxis=2) +WCS2D.wcs.ctype = ["x", "y"] +WCS2D.wcs.cunit = ["km", "km"] +WCS2D.wcs.crpix = [614.5, 856.5] +WCS2D.wcs.cdelt = [6.25, 6.25] +WCS2D.wcs.crval = [0.0, 0.0] + +WCS3D = WCS(naxis=3) +WCS3D.wcs.ctype = ["x", "y", "z"] +WCS3D.wcs.cunit = ["km", "km", "km"] +WCS3D.wcs.crpix = [614.5, 856.5, 333] +WCS3D.wcs.cdelt = [6.25, 6.25, 23] +WCS3D.wcs.crval = [0.0, 0.0, 1.0] + + +@pytest.fixture +def wcs_4d(): + header = dedent( + """\ + WCSAXES = 4 / Number of coordinate axes + CRPIX1 = 0.0 / Pixel coordinate of reference point + CRPIX2 = 0.0 / Pixel coordinate of reference point + CRPIX3 = 0.0 / Pixel coordinate of reference point + CRPIX4 = 5.0 / Pixel coordinate of reference point + CDELT1 = 0.4 / [min] Coordinate increment at reference point + CDELT2 = 2E-11 / [m] Coordinate increment at reference point + CDELT3 = 0.0027777777777778 / [deg] Coordinate increment at reference point + CDELT4 = 0.0013888888888889 / [deg] Coordinate increment at reference point + CUNIT1 = 'min' / Units of coordinate increment and value + CUNIT2 = 'm' / Units of coordinate increment and value + CUNIT3 = 'deg' / Units of coordinate increment and value + CUNIT4 = 'deg' / Units of coordinate increment and value + CTYPE1 = 'TIME' / Coordinate type code + CTYPE2 = 'WAVE' / Vacuum wavelength (linear) + CTYPE3 = 'HPLT-TAN' / Coordinate type codegnomonic projection + CTYPE4 = 'HPLN-TAN' / Coordinate type codegnomonic projection + CRVAL1 = 0.0 / [min] Coordinate value at reference point + CRVAL2 = 0.0 / [m] Coordinate value at reference point + CRVAL3 = 0.0 / [deg] Coordinate value at reference point + CRVAL4 = 0.0 / [deg] Coordinate value at reference point + LONPOLE = 180.0 / [deg] Native longitude of celestial pole + LATPOLE = 0.0 / [deg] Native latitude of celestial pole + """ + ) + return WCS(header=fits.Header.fromstring(header, sep="\n")) + + +@pytest.fixture +def cube_wcs(): + cube_header = get_pkg_data_filename("data/cube_header") + header = fits.Header.fromtextfile(cube_header) + return WCS(header=header) + + +def assert_item_equal(i1: object, i2: object) -> None: + __tracebackhide__ = True + assert type(i1) is type(i2) + if type(i1) is Quantity and type(i2) is Quantity: + assert_quantity_allclose(i1, i2) + else: + assert i1 == i2 + + +def assert_dict_equal(d1: dict, d2: dict) -> None: + __tracebackhide__ = True + # note that key insertion order matters in this comparison + assert list(d2.keys()) == list(d1.keys()) + for v1, v2 in zip(d1.values(), d2.values(), strict=True): + assert type(v1) is type(v2) + if isinstance(v1, dict): + assert_dict_equal(v1, v2) + elif isinstance(v1, list): + for i1, i2 in zip(v1, v2, strict=True): + assert_item_equal(i1, i2) + else: + assert_item_equal(v1, v2) + + +def test_shorthand_inversion(): + """ + Test that the Matplotlib subtraction shorthand for composing and inverting + transformations works. + """ + w1 = WCS(naxis=2) + w1.wcs.ctype = ["RA---TAN", "DEC--TAN"] + w1.wcs.crpix = [256.0, 256.0] + w1.wcs.cdelt = [-0.05, 0.05] + w1.wcs.crval = [120.0, -19.0] + + w2 = WCS(naxis=2) + w2.wcs.ctype = ["RA---SIN", "DEC--SIN"] + w2.wcs.crpix = [256.0, 256.0] + w2.wcs.cdelt = [-0.05, 0.05] + w2.wcs.crval = [235.0, +23.7] + + t1 = WCSWorld2PixelTransform(w1) + t2 = WCSWorld2PixelTransform(w2) + + assert t1 - t2 == t1 + t2.inverted() + assert t1 - t2 != t2.inverted() + t1 + assert t1 - t1 == IdentityTransform() + + +@pytest.mark.parametrize( + "wcs_factory", [WCSPixel2WorldTransform, WCSWorld2PixelTransform] +) +def test_hashing(wcs_factory): + w1 = wcs_factory(WCS2D) + w2 = w1.inverted() + assert hash(w1) != hash(w2) + assert hash(w1) == hash(w2.inverted()) + + +# We add Affine2D to catch the fact that in Matplotlib, having a Composite +# transform can end up in more strict requirements for the dimensionality. + + +def test_2d(): + world = np.ones((10, 2)) + + w1 = WCSWorld2PixelTransform(WCS2D) + Affine2D() + pixel = w1.transform(world) + world_2 = w1.inverted().transform(pixel) + + np.testing.assert_allclose(world, world_2) + + +def test_3d(): + world = np.ones((10, 2)) + + w1 = WCSWorld2PixelTransform(WCS3D[:, 0, :]) + Affine2D() + pixel = w1.transform(world) + world_2 = w1.inverted().transform(pixel) + + np.testing.assert_allclose(world[:, 0], world_2[:, 0]) + np.testing.assert_allclose(world[:, 1], world_2[:, 1]) + + +def test_coord_type_from_ctype(cube_wcs): + _, coord_meta = transform_coord_meta_from_wcs( + cube_wcs, RectangularFrame, slices=(50, "y", "x") + ) + + axislabel_position = coord_meta["default_axislabel_position"] + ticklabel_position = coord_meta["default_ticklabel_position"] + ticks_position = coord_meta["default_ticks_position"] + + # These axes are swapped due to the pixel derivatives + assert axislabel_position == ["#", "#", "#"] + assert ticklabel_position == ["#", "#", "#"] + assert ticks_position == ["#", "#", "#"] + + wcs = WCS(naxis=2) + wcs.wcs.ctype = ["GLON-TAN", "GLAT-TAN"] + wcs.wcs.crpix = [256.0] * 2 + wcs.wcs.cdelt = [-0.05] * 2 + wcs.wcs.crval = [50.0] * 2 + wcs.wcs.cname = ["Longitude", ""] + wcs.wcs.set() + + _, coord_meta = transform_coord_meta_from_wcs(wcs, RectangularFrame) + + assert coord_meta["type"] == ["longitude", "latitude"] + assert coord_meta["format_unit"] == [u.deg, u.deg] + assert coord_meta["wrap"] == [None, None] + assert coord_meta["default_axis_label"] == ["Longitude", "pos.galactic.lat"] + assert coord_meta["name"] == [ + ("pos.galactic.lon", "glon-tan", "glon", "Longitude"), + ("pos.galactic.lat", "glat-tan", "glat"), + ] + + wcs = WCS(naxis=2) + wcs.wcs.ctype = ["HPLN-TAN", "HPLT-TAN"] + wcs.wcs.crpix = [256.0] * 2 + wcs.wcs.cdelt = [-0.05] * 2 + wcs.wcs.crval = [50.0] * 2 + wcs.wcs.set() + + _, coord_meta = transform_coord_meta_from_wcs(wcs, RectangularFrame) + + assert coord_meta["type"] == ["longitude", "latitude"] + assert coord_meta["format_unit"] == [u.arcsec, u.arcsec] + assert coord_meta["wrap"] == [180.0 * u.deg, None] + + _, coord_meta = transform_coord_meta_from_wcs( + wcs, RectangularFrame, slices=("y", "x") + ) + + axislabel_position = coord_meta["default_axislabel_position"] + ticklabel_position = coord_meta["default_ticklabel_position"] + ticks_position = coord_meta["default_ticks_position"] + + # These axes should be swapped because of slices + assert axislabel_position == ["#", "#"] + assert ticklabel_position == ["#", "#"] + assert ticks_position == ["brtl", "brtl"] + + wcs = WCS(naxis=2) + wcs.wcs.ctype = ["HGLN-TAN", "HGLT-TAN"] + wcs.wcs.crpix = [256.0] * 2 + wcs.wcs.cdelt = [-0.05] * 2 + wcs.wcs.crval = [50.0] * 2 + wcs.wcs.set() + + _, coord_meta = transform_coord_meta_from_wcs(wcs, RectangularFrame) + + assert coord_meta["type"] == ["longitude", "latitude"] + assert coord_meta["format_unit"] == [u.deg, u.deg] + assert coord_meta["wrap"] == [180.0 * u.deg, None] + + wcs = WCS(naxis=2) + wcs.wcs.ctype = ["CRLN-TAN", "CRLT-TAN"] + wcs.wcs.crpix = [256.0] * 2 + wcs.wcs.cdelt = [-0.05] * 2 + wcs.wcs.crval = [50.0] * 2 + wcs.wcs.set() + + _, coord_meta = transform_coord_meta_from_wcs(wcs, RectangularFrame) + + assert coord_meta["type"] == ["longitude", "latitude"] + assert coord_meta["format_unit"] == [u.deg, u.deg] + assert coord_meta["wrap"] == [360.0 * u.deg, None] + + wcs = WCS(naxis=2) + wcs.wcs.ctype = ["RA---TAN", "DEC--TAN"] + wcs.wcs.crpix = [256.0] * 2 + wcs.wcs.cdelt = [-0.05] * 2 + wcs.wcs.crval = [50.0] * 2 + wcs.wcs.set() + + _, coord_meta = transform_coord_meta_from_wcs(wcs, RectangularFrame) + + assert coord_meta["type"] == ["longitude", "latitude"] + assert coord_meta["format_unit"] == [u.hourangle, u.deg] + assert coord_meta["wrap"] == [None, None] + + wcs = WCS(naxis=2) + wcs.wcs.ctype = ["HHLN-TAN", "HHLT-TAN"] + wcs.wcs.crpix = [256.0] * 2 + wcs.wcs.cdelt = [-0.05] * 2 + wcs.wcs.crval = [50.0] * 2 + wcs.wcs.set() + + custom_mapping = { + "HHLN": "custom:pos.custom.lon", + "HHLT": "custom:pos.custom.lat", + } + with custom_ctype_to_ucd_mapping(custom_mapping): + _, coord_meta = transform_coord_meta_from_wcs(wcs, RectangularFrame) + + # Ensure these custom types get mapped to longitude and latitude + assert coord_meta["type"] == ["longitude", "latitude"] + assert coord_meta["format_unit"] == [u.deg, u.deg] + assert coord_meta["wrap"] == [None, None] + + wcs = WCS(naxis=2) + wcs.wcs.ctype = ["spam", "spam"] + wcs.wcs.crpix = [256.0] * 2 + wcs.wcs.cdelt = [-0.05] * 2 + wcs.wcs.crval = [50.0] * 2 + wcs.wcs.set() + + _, coord_meta = transform_coord_meta_from_wcs(wcs, RectangularFrame) + + assert coord_meta["type"] == ["scalar", "scalar"] + assert coord_meta["format_unit"] == [u.one, u.one] + assert coord_meta["wrap"] == [None, None] + + myframe_mapping = { + "custom:pos.myframe.lon": { + "coord_wrap": 180.0 * u.deg, + "format_unit": u.arcsec, + "coord_type": "longitude", + }, + "custom:pos.myframe.lat": {"format_unit": u.arcsec, "coord_type": "latitude"}, + } + + +def test_custom_coord_type_from_ctype(): + wcs = WCS(naxis=1) + wcs.wcs.ctype = ["eggs"] + wcs.wcs.cunit = ["deg"] + + custom_mapping = { + "eggs": "custom:pos.eggs", + } + with custom_ctype_to_ucd_mapping(custom_mapping): + fig = Figure() + ax = fig.add_subplot(111, projection=wcs) + assert ax.coords["eggs"].coord_type == "scalar" + assert ax.coords["eggs"].coord_wrap == None + assert ax.coords["eggs"].get_format_unit() == u.deg + + custom_meta = { + "pos.eggs": { + "coord_wrap": 360.0 * u.deg, + "format_unit": u.arcsec, + "coord_type": "longitude", + } + } + with custom_ucd_coord_meta_mapping(custom_meta): + ax = fig.add_subplot(111, projection=wcs) + ax.coords + assert ax.coords["eggs"].coord_type == "longitude" + assert ax.coords["eggs"].coord_wrap == 360 * u.deg + assert ax.coords["eggs"].get_format_unit() == u.arcsec + + fig = Figure() + ax = fig.add_subplot(111, projection=wcs) + assert ax.coords["eggs"].coord_type == "scalar" + assert ax.coords["eggs"].coord_wrap == None + assert ax.coords["eggs"].get_format_unit() == u.deg + + +def test_custom_coord_type_from_ctype_nested(): + from astropy.visualization.wcsaxes.wcsapi import ( + CUSTOM_UCD_COORD_META_MAPPING as global_state, # noqa: N811 + ) + + init_state = deepcopy(global_state) + + wcs = WCS(naxis=2) + wcs.wcs.ctype = ["eggs", "spam"] + wcs.wcs.cunit = ["deg", "deg"] + + custom_mapping = { + "eggs": "custom:pos.eggs", + "spam": "custom:pos.spam", + } + + with custom_ctype_to_ucd_mapping(custom_mapping): + fig = Figure() + custom_meta_1 = { + "pos.eggs": { + "coord_wrap": 360.0 * u.deg, + "format_unit": u.arcsec, + "coord_type": "longitude", + } + } + with custom_ucd_coord_meta_mapping(custom_meta_1): + custom_meta_2 = { + "pos.spam": { + "format_unit": u.deg, + "coord_type": "latitude", + } + } + with custom_ucd_coord_meta_mapping(custom_meta_2): + ax = fig.add_subplot(111, projection=wcs) + ax.coords + assert ax.coords["eggs"].coord_type == "longitude" + assert ax.coords["eggs"].coord_wrap == 360 * u.deg + assert ax.coords["eggs"].get_format_unit() == u.arcsec + assert ax.coords["spam"].coord_type == "latitude" + assert ax.coords["spam"].get_format_unit() == u.deg + + # Now test the mappings have been removed + fig2 = Figure() + ax = fig2.add_subplot(111, projection=wcs) + assert ax.coords["eggs"].coord_type == "scalar" + assert ax.coords["eggs"].coord_wrap == None + assert ax.coords["eggs"].get_format_unit() == u.deg + assert ax.coords["spam"].coord_type == "scalar" + assert ax.coords["spam"].coord_wrap == None + + assert_dict_equal(global_state, init_state) + + +def test_custom_coord_type_1d_2d_wcs_overwrite(): + from astropy.visualization.wcsaxes.wcsapi import ( + CUSTOM_UCD_COORD_META_MAPPING as global_state, # noqa: N811 + ) + + init_state = deepcopy(global_state) + + wcs = WCS(naxis=2) + wcs.wcs.ctype = ["HGLN-TAN", "HGLT-TAN"] + wcs.wcs.crpix = [256.0] * 2 + wcs.wcs.cdelt = [-0.05] * 2 + wcs.wcs.crval = [50.0] * 2 + wcs.wcs.set() + + custom_meta = { + "custom:pos.heliographic.stonyhurst.lon": { + "format_unit": u.arcsec, + # This also tests that we don't overwrite the custom meta with the + # stock meta that will set to longitude when the UCD ends in lon + "coord_type": "latitude", + } + } + + with pytest.raises( + ValueError, match="pos.heliographic.stonyhurst.lon already exists" + ): + with custom_ucd_coord_meta_mapping(custom_meta): + _, coord_meta = transform_coord_meta_from_wcs(wcs, RectangularFrame) + + assert_dict_equal(global_state, init_state) + + with custom_ucd_coord_meta_mapping(custom_meta, overwrite=True): + _, coord_meta = transform_coord_meta_from_wcs(wcs, RectangularFrame) + + assert_dict_equal(global_state, init_state) + assert coord_meta["type"] == ["latitude", "latitude"] + assert coord_meta["format_unit"] == [u.arcsec, u.deg] + assert coord_meta["wrap"] == [None, None] + + +def test_coord_type_1d_1d_wcs(): + wcs = WCS(naxis=1) + wcs.wcs.ctype = ["WAVE"] + wcs.wcs.crpix = [256.0] + wcs.wcs.cdelt = [-0.05] + wcs.wcs.crval = [50.0] + wcs.wcs.set() + + _, coord_meta = transform_coord_meta_from_wcs(wcs, RectangularFrame1D) + + assert coord_meta["type"] == ["scalar"] + assert coord_meta["format_unit"] == [u.m] + assert coord_meta["wrap"] == [None] + + +def test_coord_type_1d_2d_wcs_correlated(): + wcs = WCS(naxis=2) + wcs.wcs.ctype = ["GLON-TAN", "GLAT-TAN"] + wcs.wcs.crpix = [256.0] * 2 + wcs.wcs.cdelt = [-0.05] * 2 + wcs.wcs.crval = [50.0] * 2 + wcs.wcs.set() + + _, coord_meta = transform_coord_meta_from_wcs( + wcs, RectangularFrame1D, slices=("x", 0) + ) + + assert coord_meta["type"] == ["longitude", "latitude"] + assert coord_meta["format_unit"] == [u.deg, u.deg] + assert coord_meta["wrap"] == [None, None] + assert coord_meta["visible"] == [True, True] + + +def test_coord_type_1d_2d_wcs_uncorrelated(): + wcs = WCS(naxis=2) + wcs.wcs.ctype = ["WAVE", "UTC"] + wcs.wcs.crpix = [256.0] * 2 + wcs.wcs.cdelt = [-0.05] * 2 + wcs.wcs.crval = [50.0] * 2 + wcs.wcs.cunit = ["nm", "s"] + wcs.wcs.set() + + _, coord_meta = transform_coord_meta_from_wcs( + wcs, RectangularFrame1D, slices=("x", 0) + ) + + assert coord_meta["type"] == ["scalar", "scalar"] + assert coord_meta["format_unit"] == [u.m, u.s] + assert coord_meta["wrap"] == [None, None] + assert coord_meta["visible"] == [True, False] + + +def test_coord_meta_4d(wcs_4d): + _, coord_meta = transform_coord_meta_from_wcs( + wcs_4d, RectangularFrame, slices=(0, 0, "x", "y") + ) + + axislabel_position = coord_meta["default_axislabel_position"] + ticklabel_position = coord_meta["default_ticklabel_position"] + ticks_position = coord_meta["default_ticks_position"] + + assert axislabel_position == ["", "", "#", "#"] + assert ticklabel_position == ["", "", "#", "#"] + assert ticks_position == ["", "", "brtl", "brtl"] + + +def test_coord_meta_4d_line_plot(wcs_4d): + _, coord_meta = transform_coord_meta_from_wcs( + wcs_4d, RectangularFrame1D, slices=(0, 0, 0, "x") + ) + + axislabel_position = coord_meta["default_axislabel_position"] + ticklabel_position = coord_meta["default_ticklabel_position"] + ticks_position = coord_meta["default_ticks_position"] + + # These axes are swapped due to the pixel derivatives + assert axislabel_position == ["", "", "#", "#"] + assert ticklabel_position == ["", "", "#", "#"] + assert ticks_position == ["", "", "#", "#"] + + +@pytest.fixture +def sub_wcs(wcs_4d, wcs_slice): + return SlicedLowLevelWCS(wcs_4d, wcs_slice) + + +@pytest.mark.parametrize( + ("wcs_slice", "wcsaxes_slices", "world_map", "ndim"), + [ + (np.s_[...], [0, 0, "x", "y"], (2, 3), 2), + (np.s_[...], [0, "x", 0, "y"], (1, 2, 3), 3), + (np.s_[...], ["x", 0, 0, "y"], (0, 2, 3), 3), + (np.s_[...], ["x", "y", 0, 0], (0, 1), 2), + (np.s_[:, :, 0, :], [0, "x", "y"], (1, 2), 2), + (np.s_[:, :, 0, :], ["x", 0, "y"], (0, 1, 2), 3), + (np.s_[:, :, 0, :], ["x", "y", 0], (0, 1, 2), 3), + (np.s_[:, 0, :, :], ["x", "y", 0], (0, 1), 2), + ], +) +def test_apply_slices(sub_wcs, wcs_slice, wcsaxes_slices, world_map, ndim): + transform_wcs, _, out_world_map = apply_slices(sub_wcs, wcsaxes_slices) + assert transform_wcs.world_n_dim == ndim + + assert out_world_map == world_map + + +# parametrize here to pass to the fixture +@pytest.mark.parametrize("wcs_slice", [np.s_[:, :, 0, :]]) +def test_sliced_ND_input(wcs_4d, sub_wcs, wcs_slice): + slices_wcsaxes = [0, "x", "y"] + fig = Figure() + + for sub_wcs_ in (sub_wcs, SlicedLowLevelWCS(wcs_4d, wcs_slice)): + with warnings.catch_warnings(): + warnings.filterwarnings("ignore", category=FutureWarning) + _, coord_meta = transform_coord_meta_from_wcs( + sub_wcs_, RectangularFrame, slices=slices_wcsaxes + ) + + assert all(len(x) == 3 for x in coord_meta.values()) + + assert coord_meta["name"] == [ + "time", + ("custom:pos.helioprojective.lat", "hplt-tan", "hplt"), + ("custom:pos.helioprojective.lon", "hpln-tan", "hpln"), + ] + assert coord_meta["type"] == ["scalar", "latitude", "longitude"] + assert coord_meta["wrap"] == [None, None, 180.0 * u.deg] + assert coord_meta["unit"] == [u.Unit("min"), u.Unit("deg"), u.Unit("deg")] + assert coord_meta["visible"] == [False, True, True] + assert coord_meta["format_unit"] == [ + u.Unit("min"), + u.Unit("arcsec"), + u.Unit("arcsec"), + ] + assert coord_meta["default_axislabel_position"] == ["", "#", "#"] + assert coord_meta["default_ticklabel_position"] == ["", "#", "#"] + assert coord_meta["default_ticks_position"] == ["", "brtl", "brtl"] + + # Validate the axes initialize correctly + fig.clear() + fig.add_subplot(projection=sub_wcs_, slices=slices_wcsaxes) + + +class LowLevelWCS5D(BaseLowLevelWCS): + pixel_dim = 2 + + @property + def pixel_n_dim(self): + return self.pixel_dim + + @property + def world_n_dim(self): + return 5 + + @property + def world_axis_physical_types(self): + return [ + "em.freq", + "time", + "pos.eq.ra", + "pos.eq.dec", + "phys.polarization.stokes", + ] + + @property + def world_axis_units(self): + return ["Hz", "day", "deg", "deg", ""] + + @property + def world_axis_names(self): + return ["Frequency", "", "RA", "DEC", ""] + + def pixel_to_world_values(self, *pixel_arrays): + pixel_arrays = (list(pixel_arrays) * 3)[:-1] # make list have 5 elements + return [ + np.asarray(pix) * scale + for pix, scale in zip(pixel_arrays, [10, 0.2, 0.4, 0.39, 2]) + ] + + def world_to_pixel_values(self, *world_arrays): + world_arrays = world_arrays[:2] # make list have 2 elements + return [ + np.asarray(world) / scale for world, scale in zip(world_arrays, [10, 0.2]) + ] + + @property + def world_axis_object_components(self): + return [ + ("freq", 0, "value"), + ("time", 0, "mjd"), + ("celestial", 0, "spherical.lon.degree"), + ("celestial", 1, "spherical.lat.degree"), + ("stokes", 0, "value"), + ] + + @property + def world_axis_object_classes(self): + return { + "celestial": (SkyCoord, (), {"unit": "deg"}), + "time": (Time, (), {"format": "mjd"}), + "freq": (Quantity, (), {"unit": "Hz"}), + "stokes": (Quantity, (), {"unit": "one"}), + } + + +def test_edge_axes(): + # Check that axes on the edge of a spherical projection are shown properly + # (see https://github.com/astropy/astropy/issues/10441) + shape = [180, 360] + data = np.random.rand(*shape) + header = { + "wcsaxes": 2, + "crpix1": 180.5, + "crpix2": 90.5, + "cdelt1": 1.0, + "cdelt2": 1.0, + "cunit1": "deg", + "cunit2": "deg", + "ctype1": "CRLN-CAR", + "ctype2": "CRLT-CAR", + "crval1": 0.0, + "crval2": 0.0, + "lonpole": 0.0, + "latpole": 90.0, + } + wcs = WCS(header) + fig = Figure() + canvas = FigureCanvasAgg(fig) + ax = fig.add_axes([0.1, 0.1, 0.8, 0.8], projection=wcs) + ax.imshow(data, origin="lower") + # By default the x- and y- axes should be drawn + lon = ax.coords[0] + lat = ax.coords[1] + canvas.draw() + np.testing.assert_equal( + lon._ticks.world["b"], np.array([90.0, 180.0, 180.0, 270.0, 0.0]) + ) + np.testing.assert_equal( + lat._ticks.world["l"], np.array([-90.0, -60.0, -30.0, 0.0, 30.0, 60.0, 90.0]) + ) + + +def test_coord_meta_wcsapi(): + wcs = LowLevelWCS5D() + wcs.pixel_dim = 5 + _, coord_meta = transform_coord_meta_from_wcs( + wcs, RectangularFrame, slices=[0, 0, "x", "y", 0] + ) + + assert coord_meta["name"] == [ + ("em.freq", "Frequency"), + "time", + ("pos.eq.ra", "RA"), + ("pos.eq.dec", "DEC"), + "phys.polarization.stokes", + ] + assert coord_meta["type"] == ["scalar", "scalar", "longitude", "latitude", "scalar"] + assert coord_meta["wrap"] == [None, None, None, None, None] + assert coord_meta["unit"] == [ + u.Unit("Hz"), + u.Unit("d"), + u.Unit("deg"), + u.Unit("deg"), + u.one, + ] + assert coord_meta["visible"] == [True, True, True, True, True] + assert coord_meta["format_unit"] == [ + u.Unit("Hz"), + u.Unit("d"), + u.Unit("hourangle"), + u.Unit("deg"), + u.one, + ] + assert coord_meta["default_axislabel_position"] == ["#", "#", "#", "#", "#"] + assert coord_meta["default_ticklabel_position"] == ["#", "#", "#", "#", "#"] + assert coord_meta["default_ticks_position"] == ["#", "#", "#", "#", "#"] + assert coord_meta["default_axis_label"] == [ + "Frequency", + "time", + "RA", + "DEC", + "phys.polarization.stokes", + ] + + +@figure_test +def test_wcsapi_5d_with_names(): + # Test for plotting image and also setting values of ticks + fig = Figure(figsize=(6, 6)) + ax = fig.add_axes([0.1, 0.1, 0.8, 0.8], projection=LowLevelWCS5D()) + ax.set_xlim(-0.5, 148.5) + ax.set_ylim(-0.5, 148.5) + return fig + + +class LowLevelWCSCelestial2D(BaseLowLevelWCS): + # APE 14 WCS that has celestial coordinates that are deliberately not in degrees + + @property + def pixel_n_dim(self): + return 2 + + @property + def world_n_dim(self): + return 2 + + @property + def world_axis_physical_types(self): + return [ + "pos.eq.ra", + "pos.eq.dec", + ] + + @property + def world_axis_units(self): + return ["arcsec", "arcsec"] + + @property + def world_axis_names(self): + return ["RA", "DEC"] + + # Since the units are in arcsec, we can just go for an identity transform + # where 1 pixel = 1" since this is not completely unrealistic + + def pixel_to_world_values(self, *pixel_arrays): + return pixel_arrays + + def world_to_pixel_values(self, *world_arrays): + return world_arrays + + @property + def world_axis_object_components(self): + return [ + ("celestial", 0, "spherical.lon.arcsec"), + ("celestial", 1, "spherical.lat.arcsec"), + ] + + @property + def world_axis_object_classes(self): + return { + "celestial": (SkyCoord, (), {"unit": "arcsec"}), + } + + +@figure_test +def test_wcsapi_2d_celestial_arcsec(): + # Regression test for plot_coord/scatter_coord/text_coord with celestial WCS that is not in degrees + fig = Figure(figsize=(6, 6)) + ax = fig.add_axes([0.15, 0.1, 0.8, 0.8], projection=LowLevelWCSCelestial2D()) + ax.set_xlim(-0.5, 200.5) + ax.set_ylim(-0.5, 200.5) + ax.coords[0].set_format_unit("arcsec") + ax.plot_coord(SkyCoord([50, 150], [100, 100], unit="arcsec"), "ro") + ax.scatter_coord( + SkyCoord([100, 100], [50, 150], unit="arcsec"), color="green", s=50 + ) + ax.text_coord( + SkyCoord(50, 50, unit="arcsec"), + "Plot Label", + color="blue", + ha="right", + va="top", + ) + return fig diff --git a/astropy/visualization/wcsaxes/ticklabels.py b/astropy/visualization/wcsaxes/ticklabels.py new file mode 100644 index 000000000000..fdc55a47b63d --- /dev/null +++ b/astropy/visualization/wcsaxes/ticklabels.py @@ -0,0 +1,374 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +import warnings +from collections import defaultdict + +import numpy as np +from matplotlib import rcParams +from matplotlib.artist import allow_rasterization +from matplotlib.text import Text + +from astropy.utils.decorators import deprecated_renamed_argument +from astropy.utils.exceptions import AstropyDeprecationWarning + +from .frame import RectangularFrame + + +def sort_using(X, Y): + return [x for (y, x) in sorted(zip(Y, X))] + + +def _find_start_of_last_number(label): + """ + Given a label, find the index of the start of the last numerical value + in the label. + """ + numerical_chars = "0123456789.+" + if rcParams["axes.unicode_minus"] and not rcParams["text.usetex"]: + numerical_chars += "\N{MINUS SIGN}" + else: + numerical_chars += "-" + + in_number = False + for j in range(len(label) - 1, -1, -1): + if in_number: + if label[j] not in numerical_chars: + return j + 1 + elif label[j] in numerical_chars: + in_number = True + + +class TickLabels(Text): + def __init__(self, frame, *args, **kwargs): + self.clear() + self._frame = frame + super().__init__(*args, **kwargs) + self.set_clip_on(True) + self.set_visible_axes("all") + self.set_pad(rcParams["xtick.major.pad"]) + self._exclude_overlapping = False + self._simplify = True + + # Mapping from axis > list[bounding boxes] + self._axis_bboxes = defaultdict(list) + + # Stale if either xy positions haven't been calculated, or if + # something changes that requires recomputing the positions + self._stale = True + + # Check rcParams + if "color" not in kwargs: + self.set_color(rcParams["xtick.color"]) + + if "size" not in kwargs: + self.set_size(rcParams["xtick.labelsize"]) + + def clear(self): + self.world = defaultdict(list) + self.data = defaultdict(list) + self.angle = defaultdict(list) + self.text = defaultdict(list) + self.disp = defaultdict(list) + + def add( + self, + axis=None, + world=None, + pixel=None, + angle=None, + text=None, + axis_displacement=None, + data=None, + ): + """ + Add a label. + + Parameters + ---------- + axis : str + Axis to add label to. + world : Quantity + Coordinate value along this axis. + pixel : [float, float] + Pixel coordinates of the label. Deprecated and no longer used. + angle : float + Angle of the label. + text : str + Label text. + axis_displacement : float + Displacement from axis. + data : [float, float] + Data coordinates of the label. + """ + required_args = ["axis", "world", "angle", "text", "axis_displacement", "data"] + if pixel is not None: + warnings.warn( + "Setting the pixel coordinates of a label does nothing and is" + " deprecated, as these can only be accurately calculated when" + " Matplotlib is drawing a figure. To prevent this warning pass the" + f" following arguments as keyword arguments: {required_args}", + AstropyDeprecationWarning, + ) + if ( + axis is None + or world is None + or angle is None + or text is None + or axis_displacement is None + or data is None + ): + raise TypeError( + f"All of the following arguments must be provided: {required_args}" + ) + + self.world[axis].append(world) + self.data[axis].append(data) + self.angle[axis].append(angle) + self.text[axis].append(text) + self.disp[axis].append(axis_displacement) + + self._stale = True + + def sort(self): + """ + Sort by axis displacement, which allows us to figure out which parts + of labels to not repeat. + """ + for axis in self.world: + self.world[axis] = sort_using(self.world[axis], self.disp[axis]) + self.data[axis] = sort_using(self.data[axis], self.disp[axis]) + self.angle[axis] = sort_using(self.angle[axis], self.disp[axis]) + self.text[axis] = sort_using(self.text[axis], self.disp[axis]) + self.disp[axis] = sort_using(self.disp[axis], self.disp[axis]) + self._stale = True + + def simplify_labels(self): + """ + Figure out which parts of labels can be dropped to avoid repetition. + """ + self.sort() + + for axis in self.world: + t1 = self.text[axis][0] + for i in range(1, len(self.world[axis])): + t2 = self.text[axis][i] + + if t1 == t2: + # In this case, we still need to preserve the last segment + # of the label. We search backwards from the end, and + # search for a number, and we then search for the first + # non-number (and non-decimal place) character we can find. + start = _find_start_of_last_number(t2) + else: + for j in range(len(t1)): + if t1[j] != t2[j]: + start = _find_start_of_last_number(t2[: j + 1]) + break + + if start != 0: + starts_dollar = t2.startswith("$") + self.text[axis][i] = t2[start:] + if starts_dollar: + self.text[axis][i] = "$" + self.text[axis][i] + + # Remove any empty LaTeX inline math mode string + if self.text[axis][i] == "$$": + self.text[axis][i] = "" + + t1 = t2 + + self._stale = True + + def set_pad(self, value): + self._pad = value + self._stale = True + + def get_pad(self): + return self._pad + + def set_visible_axes(self, visible_axes): + self._visible_axes = self._frame._validate_positions(visible_axes) + self._stale = True + + def get_visible_axes(self): + if self._visible_axes == "all": + return list(self._frame.keys()) + else: + return [x for x in self._visible_axes if x in self._frame or x == "#"] + + def set_exclude_overlapping(self, exclude_overlapping): + self._exclude_overlapping = exclude_overlapping + + def set_simplify(self, simplify): + self._simplify = simplify + + def _set_xy_alignments(self, renderer): + """ + Compute and set the x, y positions and the horizontal/vertical alignment of + each label. + """ + if not self._stale: + return + + if self._simplify: + self.simplify_labels() + text_size = renderer.points_to_pixels(self.get_size()) + + visible_axes = self.get_visible_axes() + self.xy = {axis: {} for axis in visible_axes} + self.ha = {axis: {} for axis in visible_axes} + self.va = {axis: {} for axis in visible_axes} + + for axis in visible_axes: + for i in range(len(self.world[axis])): + # In the event that the label is empty (which is not expected + # but could happen in unforeseen corner cases), we should just + # skip to the next label. + if self.text[axis][i] == "": + continue + + x, y = self._frame.parent_axes.transData.transform(self.data[axis][i]) + pad = renderer.points_to_pixels(self.get_pad() + self._tick_out_size) + + if isinstance(self._frame, RectangularFrame): + # This is just to preserve the current results, but can be + # removed next time the reference images are re-generated. + if np.abs(self.angle[axis][i]) < 45.0: + ha = "right" + va = "bottom" + dx = -pad + dy = -text_size * 0.5 + elif np.abs(self.angle[axis][i] - 90.0) < 45: + ha = "center" + va = "bottom" + dx = 0 + dy = -text_size - pad + elif np.abs(self.angle[axis][i] - 180.0) < 45: + ha = "left" + va = "bottom" + dx = pad + dy = -text_size * 0.5 + else: + ha = "center" + va = "bottom" + dx = 0 + dy = pad + + x = x + dx + y = y + dy + + else: + # This is the more general code for arbitrarily oriented + # axes + + # Set initial position and find bounding box + self.set_text(self.text[axis][i]) + self.set_position((x, y)) + bb = super().get_window_extent(renderer) + + # Find width and height, as well as angle at which we + # transition which side of the label we use to anchor the + # label. + width = bb.width + height = bb.height + + # Project axis angle onto bounding box + ax = np.cos(np.radians(self.angle[axis][i])) + ay = np.sin(np.radians(self.angle[axis][i])) + + # Set anchor point for label + if np.abs(self.angle[axis][i]) < 45.0: + dx = width + dy = ay * height + elif np.abs(self.angle[axis][i] - 90.0) < 45: + dx = ax * width + dy = height + elif np.abs(self.angle[axis][i] - 180.0) < 45: + dx = -width + dy = ay * height + else: + dx = ax * width + dy = -height + + dx *= 0.5 + dy *= 0.5 + + # Find normalized vector along axis normal, so as to be + # able to nudge the label away by a constant padding factor + + dist = np.hypot(dx, dy) + + ddx = dx / dist + ddy = dy / dist + + dx += ddx * pad + dy += ddy * pad + + x = x - dx + y = y - dy + + ha = "center" + va = "center" + + self.xy[axis][i] = (x, y) + self.ha[axis][i] = ha + self.va[axis][i] = va + + self._stale = False + + def _get_bb(self, axis, i, renderer): + """ + Get the bounding box of an individual label. n.b. _set_xy_alignment() + must be called before this method. + """ + if self.text[axis][i] == "": + return + + self.set_text(self.text[axis][i]) + self.set_position(self.xy[axis][i]) + self.set_ha(self.ha[axis][i]) + self.set_va(self.va[axis][i]) + return super().get_window_extent(renderer) + + @property + def _all_bboxes(self): + # List of all tick label bounding boxes + ret = [] + for axis in self._axis_bboxes: + ret += self._axis_bboxes[axis] + return ret + + def _set_existing_bboxes(self, bboxes): + self._existing_bboxes = bboxes + + @allow_rasterization + @deprecated_renamed_argument(old_name="bboxes", new_name=None, since="6.0") + @deprecated_renamed_argument(old_name="ticklabels_bbox", new_name=None, since="6.0") + @deprecated_renamed_argument(old_name="tick_out_size", new_name=None, since="6.0") + def draw(self, renderer, bboxes=None, ticklabels_bbox=None, tick_out_size=None): + # Reset bounding boxes + self._axis_bboxes = defaultdict(list) + + if not self.get_visible(): + return + + self._set_xy_alignments(renderer) + + for axis in self.get_visible_axes(): + if axis == "#": + continue + + for i in range(len(self.world[axis])): + # This implicitly sets the label text, position, alignment + bb = self._get_bb(axis, i, renderer) + if bb is None: + continue + + # TODO: the problem here is that we might get rid of a label + # that has a key starting bit such as -0:30 where the -0 + # might be dropped from all other labels. + if ( + not self._exclude_overlapping + or bb.count_overlaps(self._all_bboxes + self._existing_bboxes) == 0 + ): + super().draw(renderer) + self._axis_bboxes[axis].append(bb) diff --git a/astropy/visualization/wcsaxes/ticks.py b/astropy/visualization/wcsaxes/ticks.py new file mode 100644 index 000000000000..385276d1fb2c --- /dev/null +++ b/astropy/visualization/wcsaxes/ticks.py @@ -0,0 +1,206 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +from collections import defaultdict + +import numpy as np +from matplotlib import rcParams +from matplotlib.lines import Line2D, Path +from matplotlib.transforms import Affine2D + + +class Ticks(Line2D): + """ + Ticks are derived from Line2D, and note that ticks themselves + are markers. Thus, you should use set_mec, set_mew, etc. + + To change the tick size (length), you need to use + set_ticksize. To change the direction of the ticks (ticks are + in opposite direction of ticklabels by default), use + set_tick_out(False). + + Note that Matplotlib's defaults dictionary :data:`~matplotlib.rcParams` + contains default settings (color, size, width) of the form `xtick.*` and + `ytick.*`. In a WCS projection, there may not be a clear relationship + between axes of the projection and 'x' or 'y' axes. For this reason, + we read defaults from `xtick.*`. The following settings affect the + default appearance of ticks: + + * `xtick.direction` + * `xtick.major.size` + * `xtick.major.width` + * `xtick.minor.size` + * `xtick.color` + + Attributes + ---------- + ticks_locs : dict + This is set when the ticks are drawn, and is a mapping from axis to + the locations of the ticks for that axis. + """ + + def __init__(self, frame=None, ticksize=None, **kwargs): + self._frame = frame + if ticksize is None: + ticksize = rcParams["xtick.major.size"] + self.set_ticksize(ticksize) + self.set_minor_ticksize(rcParams["xtick.minor.size"]) + self.set_tick_out(rcParams["xtick.direction"] == "out") + self.clear() + line2d_kwargs = { + "color": rcParams["xtick.color"], + "linewidth": rcParams["xtick.major.width"], + } + line2d_kwargs.update(kwargs) + Line2D.__init__(self, [0.0], [0.0], **line2d_kwargs) + self.set_visible_axes("all") + self._display_minor_ticks = False + + def display_minor_ticks(self, display_minor_ticks): + self._display_minor_ticks = display_minor_ticks + + def get_display_minor_ticks(self): + return self._display_minor_ticks + + def set_tick_out(self, tick_out): + """ + set True if tick need to be rotated by 180 degree. + """ + self._tick_out = tick_out + + def get_tick_out(self): + """ + Return True if the tick will be rotated by 180 degree. + """ + return self._tick_out + + def set_ticksize(self, ticksize): + """ + set length of the ticks in points. + """ + self._ticksize = ticksize + + def get_ticksize(self): + """ + Return length of the ticks in points. + """ + return self._ticksize + + def set_minor_ticksize(self, ticksize): + """ + set length of the minor ticks in points. + """ + self._minor_ticksize = ticksize + + def get_minor_ticksize(self): + """ + Return length of the minor ticks in points. + """ + return self._minor_ticksize + + @property + def out_size(self): + if self._tick_out: + return self._ticksize + else: + return 0.0 + + def set_visible_axes(self, visible_axes): + self._visible_axes = self._frame._validate_positions(visible_axes) + + def get_visible_axes(self): + if self._visible_axes == "all": + return list(self._frame.keys()) + else: + return [x for x in self._visible_axes if x in self._frame or x == "#"] + + def clear(self): + self.world = defaultdict(list) + self.pixel = defaultdict(list) + self.angle = defaultdict(list) + self.disp = defaultdict(list) + self.minor_world = defaultdict(list) + self.minor_pixel = defaultdict(list) + self.minor_angle = defaultdict(list) + self.minor_disp = defaultdict(list) + + def add(self, axis, world, pixel, angle, axis_displacement): + self.world[axis].append(world) + self.pixel[axis].append(pixel) + self.angle[axis].append(angle) + self.disp[axis].append(axis_displacement) + + def get_minor_world(self): + return self.minor_world + + def add_minor( + self, minor_axis, minor_world, minor_pixel, minor_angle, minor_axis_displacement + ): + self.minor_world[minor_axis].append(minor_world) + self.minor_pixel[minor_axis].append(minor_pixel) + self.minor_angle[minor_axis].append(minor_angle) + self.minor_disp[minor_axis].append(minor_axis_displacement) + + def __len__(self): + return len(self.world) + + _tickvert_path = Path([[0.0, 0.0], [1.0, 0.0]]) + + def draw(self, renderer): + """ + Draw the ticks. + """ + self.ticks_locs = defaultdict(list) + + if not self.get_visible(): + return + + offset = renderer.points_to_pixels(self.get_ticksize()) + self._draw_ticks(renderer, self.pixel, self.angle, offset) + if self._display_minor_ticks: + offset = renderer.points_to_pixels(self.get_minor_ticksize()) + self._draw_ticks(renderer, self.minor_pixel, self.minor_angle, offset) + + def _draw_ticks(self, renderer, pixel_array, angle_array, offset): + """ + Draw the minor ticks. + """ + path_trans = self.get_transform() + + gc = renderer.new_gc() + gc.set_foreground(self.get_color()) + gc.set_alpha(self.get_alpha()) + gc.set_linewidth(self.get_linewidth()) + + marker_scale = Affine2D().scale(offset, offset) + marker_rotation = Affine2D() + marker_transform = marker_scale + marker_rotation + + initial_angle = 180.0 if self.get_tick_out() else 0.0 + + for axis in self.get_visible_axes(): + if axis == "#": + continue + + if axis not in pixel_array: + continue + + for loc, angle in zip(pixel_array[axis], angle_array[axis]): + # Set the rotation for this tick + marker_rotation.rotate_deg(initial_angle + angle) + + # Draw the markers + locs = path_trans.transform_non_affine(np.array([loc, loc])) + renderer.draw_markers( + gc, + self._tickvert_path, + marker_transform, + Path(locs), + path_trans.get_affine(), + ) + + # Reset the tick rotation before moving to the next tick + marker_rotation.clear() + + self.ticks_locs[axis].append(locs) + + gc.restore() diff --git a/astropy/visualization/wcsaxes/transforms.py b/astropy/visualization/wcsaxes/transforms.py new file mode 100644 index 000000000000..f05f8dbc8697 --- /dev/null +++ b/astropy/visualization/wcsaxes/transforms.py @@ -0,0 +1,222 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + + +# Note: This file includes code derived from pywcsgrid2 +# +# This file contains Matplotlib transformation objects (e.g. from pixel to world +# coordinates, but also world-to-world). + +import abc + +import numpy as np +from matplotlib.path import Path +from matplotlib.transforms import Transform + +from astropy import units as u +from astropy.coordinates import ( + BaseCoordinateFrame, + SkyCoord, + UnitSphericalRepresentation, + frame_transform_graph, +) + +__all__ = [ + "CoordinateTransform", + "CurvedTransform", + "Pixel2WorldTransform", + "World2PixelTransform", +] + + +class CurvedTransform(Transform, metaclass=abc.ABCMeta): + """ + Abstract base class for non-affine curved transforms. + """ + + input_dims = 2 + output_dims = 2 + is_separable = False + + def transform_path(self, path): + """ + Transform a Matplotlib Path. + + Parameters + ---------- + path : :class:`~matplotlib.path.Path` + The path to transform + + Returns + ------- + path : :class:`~matplotlib.path.Path` + The resulting path + """ + return Path(self.transform(path.vertices), path.codes) + + transform_path_non_affine = transform_path + + def transform(self, input): + raise NotImplementedError("") + + def inverted(self): + raise NotImplementedError("") + + +class CoordinateTransform(CurvedTransform): + has_inverse = True + + def __init__(self, input_system, input_units, output_system, output_units): + super().__init__() + self._input_system_name = input_system + self._output_system_name = output_system + self._input_units = [u.Unit(unit) for unit in input_units] + self._output_units = [u.Unit(unit) for unit in output_units] + self.same_units = input_units == output_units + + if isinstance(self._input_system_name, str): + frame_cls = frame_transform_graph.lookup_name(self._input_system_name) + if frame_cls is None: + raise ValueError(f"Frame {self._input_system_name} not found") + else: + self.input_system = frame_cls() + elif isinstance(self._input_system_name, BaseCoordinateFrame): + self.input_system = self._input_system_name + else: + raise TypeError( + "input_system should be a WCS instance, string, or a coordinate frame" + " instance" + ) + + if isinstance(self._output_system_name, str): + frame_cls = frame_transform_graph.lookup_name(self._output_system_name) + if frame_cls is None: + raise ValueError(f"Frame {self._output_system_name} not found") + else: + self.output_system = frame_cls() + + elif isinstance(self._output_system_name, BaseCoordinateFrame): + self.output_system = self._output_system_name + else: + raise TypeError( + "output_system should be a WCS instance, string, or a coordinate frame" + " instance" + ) + + if self.output_system == self.input_system: + self.same_frames = True + else: + self.same_frames = False + + @property + def same_frames(self): + return self._same_frames + + @same_frames.setter + def same_frames(self, same_frames): + self._same_frames = same_frames + + def transform(self, input_coords): + """ + Transform one set of coordinates to another. + """ + if self.same_frames and self.same_units: + return input_coords + + x_in = input_coords[:, 0] * u.Unit(self._input_units[0]) + y_in = input_coords[:, 1] * u.Unit(self._input_units[1]) + + if self.same_frames: + lon = x_in + lat = y_in + else: + c_in = SkyCoord( + UnitSphericalRepresentation(x_in, y_in), frame=self.input_system + ) + + # We often need to transform arrays that contain NaN values, and filtering + # out the NaN values would have a performance hit, so instead we just pass + # on all values and just ignore Numpy warnings + with np.errstate(all="ignore"): + c_out = c_in.transform_to(self.output_system) + + lon = c_out.spherical.lon + lat = c_out.spherical.lat + + lon = lon.to_value(self._output_units[0]) + lat = lat.to_value(self._output_units[1]) + + return np.concatenate((lon[:, np.newaxis], lat[:, np.newaxis]), axis=1) + + transform_non_affine = transform + + def inverted(self): + """ + Return the inverse of the transform. + """ + return CoordinateTransform( + self._output_system_name, + self._output_units, + self._input_system_name, + self._input_units, + ) + + +class World2PixelTransform(CurvedTransform, metaclass=abc.ABCMeta): + """ + Base transformation from world to pixel coordinates. + """ + + has_inverse = True + frame_in = None + + @property + @abc.abstractmethod + def input_dims(self): + """ + The number of input world dimensions. + """ + + @abc.abstractmethod + def transform(self, world): + """ + Transform world to pixel coordinates. You should pass in a NxM array + where N is the number of points to transform, and M is the number of + dimensions. This then returns the (x, y) pixel coordinates + as a Nx2 array. + """ + + @abc.abstractmethod + def inverted(self): + """ + Return the inverse of the transform. + """ + + +class Pixel2WorldTransform(CurvedTransform, metaclass=abc.ABCMeta): + """ + Base transformation from pixel to world coordinates. + """ + + has_inverse = True + frame_out = None + + @property + @abc.abstractmethod + def output_dims(self): + """ + The number of output world dimensions. + """ + + @abc.abstractmethod + def transform(self, pixel): + """ + Transform pixel to world coordinates. You should pass in a Nx2 array + of (x, y) pixel coordinates to transform to world coordinates. This + will then return an NxM array where M is the number of dimensions. + """ + + @abc.abstractmethod + def inverted(self): + """ + Return the inverse of the transform. + """ diff --git a/astropy/visualization/wcsaxes/utils.py b/astropy/visualization/wcsaxes/utils.py new file mode 100644 index 000000000000..57a61bec843c --- /dev/null +++ b/astropy/visualization/wcsaxes/utils.py @@ -0,0 +1,167 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +import numpy as np + +from astropy import units as u +from astropy.coordinates import BaseCoordinateFrame, UnitSphericalRepresentation + +__all__ = [ + "select_step_degree", + "select_step_hour", + "select_step_scalar", + "transform_contour_set_inplace", +] + + +def select_step_degree(dv): + # Modified from axis_artist, supports astropy.units + + if dv > 1.0 * u.arcsec: + degree_limits_ = [1.5, 3, 7, 13, 20, 40, 70, 120, 270, 520] + degree_steps_ = [1, 2, 5, 10, 15, 30, 45, 90, 180, 360] + degree_units = [u.degree] * len(degree_steps_) + + minsec_limits_ = [1.5, 2.5, 3.5, 8, 11, 18, 25, 45] + minsec_steps_ = [1, 2, 3, 5, 10, 15, 20, 30] + + minute_limits_ = np.array(minsec_limits_) / 60.0 + minute_units = [u.arcmin] * len(minute_limits_) + + second_limits_ = np.array(minsec_limits_) / 3600.0 + second_units = [u.arcsec] * len(second_limits_) + + degree_limits = np.concatenate([second_limits_, minute_limits_, degree_limits_]) + + degree_steps = minsec_steps_ + minsec_steps_ + degree_steps_ + degree_units = second_units + minute_units + degree_units + + n = degree_limits.searchsorted(dv.to(u.degree)) + step = degree_steps[n] + unit = degree_units[n] + + return step * unit + + else: + return select_step_scalar(dv.to_value(u.arcsec)) * u.arcsec + + +def select_step_hour(dv): + if dv > 15.0 * u.arcsec: + hour_limits_ = [1.5, 2.5, 3.5, 5, 7, 10, 15, 21, 36] + hour_steps_ = [1, 2, 3, 4, 6, 8, 12, 18, 24] + hour_units = [u.hourangle] * len(hour_steps_) + + minsec_limits_ = [1.5, 2.5, 3.5, 4.5, 5.5, 8, 11, 14, 18, 25, 45] + minsec_steps_ = [1, 2, 3, 4, 5, 6, 10, 12, 15, 20, 30] + + minute_limits_ = np.array(minsec_limits_) / 60.0 + minute_units = [15.0 * u.arcmin] * len(minute_limits_) + + second_limits_ = np.array(minsec_limits_) / 3600.0 + second_units = [15.0 * u.arcsec] * len(second_limits_) + + hour_limits = np.concatenate([second_limits_, minute_limits_, hour_limits_]) + + hour_steps = minsec_steps_ + minsec_steps_ + hour_steps_ + hour_units = second_units + minute_units + hour_units + + n = hour_limits.searchsorted(dv.to(u.hourangle)) + step = hour_steps[n] + unit = hour_units[n] + + return step * unit + + else: + return select_step_scalar(dv.to_value(15.0 * u.arcsec)) * (15.0 * u.arcsec) + + +def select_step_scalar(dv): + log10_dv = np.log10(dv) + + base = np.floor(log10_dv) + frac = log10_dv - base + + steps = np.log10([1, 2, 5, 10]) + + imin = np.argmin(np.abs(frac - steps)) + + return 10.0 ** (base + steps[imin]) + + +def get_coord_meta(frame): + coord_meta = {} + coord_meta["type"] = ("longitude", "latitude") + + from astropy.coordinates import frame_transform_graph + + if isinstance(frame, str): + initial_frame = frame + frame = frame_transform_graph.lookup_name(frame) + if frame is None: + raise ValueError(f"Unknown frame: {initial_frame}") + + if not isinstance(frame, BaseCoordinateFrame): + frame = frame() + + names = list(frame.representation_component_names.keys()) + coord_meta["name"] = names[:2] + + # Add dummy data to the frame to determine the longitude wrap angle and the units + frame = frame.realize_frame(UnitSphericalRepresentation(0 * u.deg, 0 * u.deg)) + coord_meta["wrap"] = (frame.spherical.lon.wrap_angle, None) + coord_meta["unit"] = (u.deg, u.deg) + coord_meta["format_unit"] = (frame.spherical.lon.unit, frame.spherical.lat.unit) + + return coord_meta + + +def transform_contour_set_inplace(cset, transform): + """ + Transform a contour set in-place using a specified + :class:`matplotlib.transform.Transform`. + + Using transforms with the native Matplotlib contour/contourf can be slow if + the transforms have a non-negligible overhead (which is the case for + WCS/SkyCoord transforms) since the transform is called for each individual + contour line. It is more efficient to stack all the contour lines together + temporarily and transform them in one go. + """ + # The contours are represented as paths grouped into levels. Each can have + # one or more paths. The approach we take here is to stack the vertices of + # all paths and transform them in one go. The pos_level list helps us keep + # track of where the set of segments for each overall contour level ends. + # The pos_segments list helps us keep track of where each segmnt ends for + # each contour level. + all_paths = [] + pos_level = [] + pos_segments = [] + paths = cset.get_paths() + if len(paths) > 0: + all_paths.append(paths) + # The last item in pos isn't needed for np.split and in fact causes + # issues if we keep it because it will cause an extra empty array to be + # returned. + pos = np.cumsum([len(x) for x in paths]) + pos_segments.append(pos[:-1]) + pos_level.append(pos[-1]) + + # As above the last item isn't needed + pos_level = np.cumsum(pos_level)[:-1] + + # Stack all the segments into a single (n, 2) array + vertices = [path.vertices for paths in all_paths for path in paths] + if len(vertices) > 0: + vertices = np.concatenate(vertices) + else: + return + + # Transform all coordinates in one go + vertices = transform.transform(vertices) + + # Split up into levels again + vertices = np.split(vertices, pos_level) + + # Now re-populate the segments in the line collections + for ilevel, vert in enumerate(vertices): + vert = np.split(vert, pos_segments[ilevel]) + for iseg, ivert in enumerate(vert): + all_paths[ilevel][iseg].vertices = ivert diff --git a/astropy/visualization/wcsaxes/wcsapi.py b/astropy/visualization/wcsaxes/wcsapi.py new file mode 100644 index 000000000000..c50f1a4bf05e --- /dev/null +++ b/astropy/visualization/wcsaxes/wcsapi.py @@ -0,0 +1,459 @@ +# Functions/classes for WCSAxes related to APE14 WCSes +from __future__ import annotations + +from contextlib import contextmanager + +import numpy as np + +from astropy import units as u +from astropy.coordinates import ICRS, BaseCoordinateFrame, SkyCoord +from astropy.wcs import WCS +from astropy.wcs.wcsapi import SlicedLowLevelWCS + +from .frame import EllipticalFrame, RectangularFrame, RectangularFrame1D +from .transforms import CurvedTransform + +__all__ = [ + "WCSPixel2WorldTransform", + "WCSWorld2PixelTransform", + "custom_ucd_coord_meta_mapping", + "transform_coord_meta_from_wcs", +] + +IDENTITY = WCS(naxis=2) +IDENTITY.wcs.ctype = ["X", "Y"] +IDENTITY.wcs.crval = [0.0, 0.0] +IDENTITY.wcs.crpix = [1.0, 1.0] +IDENTITY.wcs.cdelt = [1.0, 1.0] + +UCD_COORD_META_MAPPING = { + "lon": {"coord_type": "longitude"}, + "lat": {"coord_type": "latitude"}, + "ra": {"coord_type": "longitude"}, + "dec": {"coord_type": "latitude"}, + "alt": {"coord_type": "longitude"}, + "az": {"coord_type": "latitude"}, + "long": {"coord_type": "longitude"}, +} + +CUSTOM_UCD_COORD_META_MAPPING = { + "pos.helioprojective.lon": { + "coord_wrap": 180.0 * u.deg, + "format_unit": u.arcsec, + "coord_type": "longitude", + }, + "pos.helioprojective.lat": {"format_unit": u.arcsec, "coord_type": "latitude"}, + "pos.heliographic.stonyhurst.lon": { + "coord_wrap": 180.0 * u.deg, + "format_unit": u.deg, + "coord_type": "longitude", + }, + "pos.heliographic.stonyhurst.lat": {"format_unit": u.deg, "coord_type": "latitude"}, + "pos.heliographic.carrington.lon": { + "coord_wrap": 360.0 * u.deg, + "format_unit": u.deg, + "coord_type": "longitude", + }, + "pos.heliographic.carrington.lat": {"format_unit": u.deg, "coord_type": "latitude"}, +} + + +@contextmanager +def custom_ucd_coord_meta_mapping(mapping, *, overwrite=False): + """ + A context manager that makes it possible to temporarily add new UCD+ to WCS coordinate + plot metadata mappings. + + Parameters + ---------- + mapping : dict + A dictionary mapping a UCD to coordinate plot metadata. + Note that custom UCD names have their "custom:" prefix stripped. + overwrite : bool + If `True` overwrite existing entries with ``mapping``. + + Examples + -------- + >>> from matplotlib import pyplot as plt + >>> from astropy.visualization.wcsaxes.wcsapi import custom_ucd_coord_meta_mapping + >>> from astropy.wcs.wcsapi.fitswcs import custom_ctype_to_ucd_mapping + >>> wcs = WCS(naxis=1) + >>> wcs.wcs.ctype = ["eggs"] + >>> wcs.wcs.cunit = ["deg"] + >>> custom_mapping = {"eggs": "custom:pos.eggs"} + >>> with custom_ctype_to_ucd_mapping(custom_mapping): + ... custom_meta = { + ... "pos.eggs": { + ... "coord_wrap": 360.0 * u.deg, + ... "format_unit": u.arcsec, + ... "coord_type": "longitude", + ... } + ... } + ... with custom_ucd_coord_meta_mapping(custom_meta): + ... fig = plt.figure() + ... ax = fig.add_subplot(111, projection=wcs) + ... ax.coords + + index aliases type unit wrap format_unit visible + deg + ----- -------------------- --------- ---- ----- ----------- ------- + 0 custom:pos.eggs eggs longitude deg 360.0 arcsec yes + + > + """ + added_keys = [] + overwritten = {} + for k, v in mapping.items(): + k = k.removeprefix("custom:") + if k in CUSTOM_UCD_COORD_META_MAPPING: + if not overwrite: + raise ValueError(f"UCD metadata mapping {k} already exists.") + overwritten[k] = CUSTOM_UCD_COORD_META_MAPPING[k] + else: + added_keys.append(k) + CUSTOM_UCD_COORD_META_MAPPING[k] = v + + try: + yield + finally: + for k in added_keys: + del CUSTOM_UCD_COORD_META_MAPPING[k] + CUSTOM_UCD_COORD_META_MAPPING.update(overwritten) + + +def transform_coord_meta_from_wcs(wcs, frame_class, slices=None): + if slices is not None: + slices = tuple(slices) + + if wcs.pixel_n_dim > 2: + if slices is None: + raise ValueError( + "WCS has more than 2 pixel dimensions, so 'slices' should be set" + ) + elif len(slices) != wcs.pixel_n_dim: + raise ValueError( + "'slices' should have as many elements as WCS " + f"has pixel dimensions (should be {wcs.pixel_n_dim})" + ) + + is_fits_wcs = isinstance(wcs, WCS) or ( + isinstance(wcs, SlicedLowLevelWCS) and isinstance(wcs._wcs, WCS) + ) + + coord_meta = {} + coord_meta["name"] = [] + coord_meta["type"] = [] + coord_meta["wrap"] = [] + coord_meta["unit"] = [] + coord_meta["visible"] = [] + coord_meta["format_unit"] = [] + + for idx in range(wcs.world_n_dim): + axis_type = wcs.world_axis_physical_types[idx] + axis_unit = u.Unit(wcs.world_axis_units[idx]) + coord_wrap = None + format_unit = axis_unit + coord_type = "scalar" + + dim_meta = { + "coord_type": coord_type, + "coord_wrap": coord_wrap, + "format_unit": format_unit, + "axis_unit": axis_unit, + } + + if axis_type is not None: + axis_type_split = axis_type.split(".") + + if len(axis_type_split): + axis_type_split[0] = axis_type_split[0].replace("custom:", "") + + for ucd, meta in CUSTOM_UCD_COORD_META_MAPPING.items(): + if ucd in axis_type: + dim_meta.update(meta) + break + else: + for ucd, meta in UCD_COORD_META_MAPPING.items(): + if ucd == axis_type_split[-1]: + dim_meta.update(meta) + # We only do the following if the original unit was + # degrees. If the unit was e.g. arcsec, it seems + # reasonable to stick to the WCS unit. + if ucd == "ra" and axis_unit is u.deg: + dim_meta["format_unit"] = u.hourangle + break + + coord_meta["type"].append(dim_meta["coord_type"]) + coord_meta["wrap"].append(dim_meta["coord_wrap"]) + coord_meta["format_unit"].append(dim_meta["format_unit"]) + coord_meta["unit"].append(dim_meta["axis_unit"]) + + # For FITS-WCS, for backward-compatibility, we need to make sure that we + # provide aliases based on CTYPE for the name. + if is_fits_wcs: + name = [] + if isinstance(wcs, WCS): + name.append(wcs.wcs.ctype[idx].lower()) + name.append(wcs.wcs.ctype[idx][:4].replace("-", "").lower()) + elif isinstance(wcs, SlicedLowLevelWCS): + name.append(wcs._wcs.wcs.ctype[wcs._world_keep[idx]].lower()) + name.append( + wcs._wcs.wcs.ctype[wcs._world_keep[idx]][:4] + .replace("-", "") + .lower() + ) + if name[0] == name[1]: + name = name[0:1] + if axis_type: + if axis_type not in name: + name.insert(0, axis_type) + if wcs.world_axis_names and wcs.world_axis_names[idx]: + if wcs.world_axis_names[idx] not in name: + name.append(wcs.world_axis_names[idx]) + name = tuple(name) if len(name) > 1 else name[0] + else: + name = axis_type or "" + if wcs.world_axis_names: + name = ( + (name, wcs.world_axis_names[idx]) + if wcs.world_axis_names[idx] + else name + ) + + coord_meta["name"].append(name) + + coord_meta["default_axislabel_position"] = [""] * wcs.world_n_dim + coord_meta["default_ticklabel_position"] = [""] * wcs.world_n_dim + coord_meta["default_ticks_position"] = [""] * wcs.world_n_dim + # If the world axis has a name use it, else display the world axis physical type. + fallback_labels = [ + name[0] if isinstance(name, (list, tuple)) else name + for name in coord_meta["name"] + ] + coord_meta["default_axis_label"] = [ + wcs.world_axis_names[i] or fallback_label + for i, fallback_label in enumerate(fallback_labels) + ] + + transform_wcs, invert_xy, world_map = apply_slices(wcs, slices) + + transform = WCSPixel2WorldTransform(transform_wcs, invert_xy=invert_xy) + + for i in range(len(coord_meta["type"])): + coord_meta["visible"].append(i in world_map) + + inv_all_corr = [False] * wcs.world_n_dim + m = transform_wcs.axis_correlation_matrix.copy() + if invert_xy: + inv_all_corr = np.all(m, axis=1) + m = m[:, ::-1] + + if frame_class in (RectangularFrame, RectangularFrame1D): + for index in world_map: + coord_meta["default_axislabel_position"][index] = "#" + coord_meta["default_ticklabel_position"][index] = "#" + coord_meta["default_ticks_position"][index] = "#" + + # In the special and common case where the frame is rectangular and we + # are dealing with a 2-d WCS (after slicing) for RectangularFrame or a + # 1-d WCS for RectangularFrame1D, we show all ticks on all axes. + if (frame_class is RectangularFrame and len(world_map) == 2) or ( + frame_class is RectangularFrame1D and len(world_map) == 1 + ): + for index in world_map: + coord_meta["default_ticks_position"][index] = frame_class.spine_names + + elif frame_class is EllipticalFrame: + if "longitude" in coord_meta["type"]: + lon_idx = coord_meta["type"].index("longitude") + coord_meta["default_axislabel_position"][lon_idx] = "h" + coord_meta["default_ticklabel_position"][lon_idx] = "h" + coord_meta["default_ticks_position"][lon_idx] = "h" + + if "latitude" in coord_meta["type"]: + lat_idx = coord_meta["type"].index("latitude") + coord_meta["default_axislabel_position"][lat_idx] = "c" + coord_meta["default_ticklabel_position"][lat_idx] = "c" + coord_meta["default_ticks_position"][lat_idx] = "c" + + else: + for index in range(len(coord_meta["type"])): + if index in world_map: + coord_meta["default_axislabel_position"][index] = ( + frame_class.spine_names + ) + coord_meta["default_ticklabel_position"][index] = ( + frame_class.spine_names + ) + coord_meta["default_ticks_position"][index] = frame_class.spine_names + + return transform, coord_meta + + +def apply_slices(wcs, slices): + """ + Take the input WCS and slices and return a sliced WCS for the transform and + a mapping of world axes in the sliced WCS to the input WCS. + """ + if isinstance(wcs, SlicedLowLevelWCS): + world_keep = list(wcs._world_keep) + else: + world_keep = list(range(wcs.world_n_dim)) + + # world_map is the index of the world axis in the input WCS for a given + # axis in the transform_wcs + world_map = list(range(wcs.world_n_dim)) + transform_wcs = wcs + invert_xy = False + if slices is not None: + wcs_slice = list(slices) + wcs_slice[wcs_slice.index("x")] = slice(None) + if "y" in slices: + wcs_slice[wcs_slice.index("y")] = slice(None) + invert_xy = slices.index("x") > slices.index("y") + + transform_wcs = SlicedLowLevelWCS(wcs, wcs_slice[::-1]) + world_map = tuple(world_keep.index(i) for i in transform_wcs._world_keep) + + return transform_wcs, invert_xy, world_map + + +def wcsapi_to_celestial_frame(wcs): + for cls, _, kwargs, *_ in wcs.world_axis_object_classes.values(): + if issubclass(cls, SkyCoord): + return kwargs.get("frame", ICRS()) + elif issubclass(cls, BaseCoordinateFrame): + return cls(**kwargs) + + +class WCSWorld2PixelTransform(CurvedTransform): + """ + WCS transformation from world to pixel coordinates. + """ + + has_inverse = True + frame_in = None + units_in = None + + def __init__(self, wcs, invert_xy=False): + super().__init__() + + if wcs.pixel_n_dim > 2: + raise ValueError("Only pixel_n_dim =< 2 is supported") + + self.wcs = wcs + self.invert_xy = invert_xy + + self.frame_in = wcsapi_to_celestial_frame(wcs) + self.units_in = wcs.world_axis_units + + def __hash__(self): + return hash((type(self), self.wcs, self.invert_xy)) + + def __eq__(self, other): + return ( + isinstance(other, type(self)) + and self.wcs is other.wcs + and self.invert_xy == other.invert_xy + ) + + @property + def input_dims(self): + return self.wcs.world_n_dim + + def transform(self, world): + # Convert to a list of arrays + world = list(world.T) + + if len(world) != 2: + raise ValueError(f"Expected 2 world coordinates, got {len(world)}") + + if self.wcs.world_n_dim == 1: + world_non_wcs = world[1] + world = world[0:1] + + if len(world[0]) == 0: + pixel = np.zeros((0, 2)) + else: + pixel = self.wcs.world_to_pixel_values(*world) + + if self.invert_xy: + pixel = pixel[::-1] + + if self.wcs.world_n_dim == 1: + pixel = [pixel, world_non_wcs] + + return np.array(pixel).T + + transform_non_affine = transform + + def inverted(self): + """ + Return the inverse of the transform. + """ + return WCSPixel2WorldTransform(self.wcs, invert_xy=self.invert_xy) + + +class WCSPixel2WorldTransform(CurvedTransform): + """ + WCS transformation from pixel to world coordinates. + """ + + has_inverse = True + + def __init__(self, wcs, invert_xy=False): + super().__init__() + + if wcs.pixel_n_dim > 2: + raise ValueError("Only pixel_n_dim =< 2 is supported") + + self.wcs = wcs + self.invert_xy = invert_xy + + self.frame_out = wcsapi_to_celestial_frame(wcs) + self.units_out = wcs.world_axis_units + + def __hash__(self): + return hash((type(self), self.wcs, self.invert_xy)) + + def __eq__(self, other): + return ( + isinstance(other, type(self)) + and self.wcs is other.wcs + and self.invert_xy == other.invert_xy + ) + + @property + def output_dims(self): + return self.wcs.world_n_dim + + def transform(self, pixel): + # Convert to a list of arrays + pixel = list(pixel.T) + + if len(pixel) != self.wcs.pixel_n_dim: + raise ValueError( + f"Expected {self.wcs.pixel_n_dim} world coordinates, got {len(pixel)} " + ) + + if self.invert_xy: + pixel = pixel[::-1] + + if len(pixel[0]) == 0: + world = np.zeros((0, self.wcs.world_n_dim)) + else: + world = self.wcs.pixel_to_world_values(*pixel) + + if self.wcs.world_n_dim == 1: + world = [world] + + return np.array(world).T + + transform_non_affine = transform + + def inverted(self): + """ + Return the inverse of the transform. + """ + return WCSWorld2PixelTransform(self.wcs, invert_xy=self.invert_xy) diff --git a/astropy/wcs/.clang-format b/astropy/wcs/.clang-format new file mode 100644 index 000000000000..47a38a93f2db --- /dev/null +++ b/astropy/wcs/.clang-format @@ -0,0 +1,2 @@ +DisableFormat: true +SortIncludes: Never diff --git a/astropy/wcs/__init__.py b/astropy/wcs/__init__.py index c605ec0e9f38..1a09f1e55271 100644 --- a/astropy/wcs/__init__.py +++ b/astropy/wcs/__init__.py @@ -1,30 +1,38 @@ -""" -.. _wcslib: http://www.atnf.csiro.au/~mcalabre/WCS/ -.. _Paper IV: http://www.atnf.csiro.au/people/mcalabre/WCS/index.html -.. _SIP: http://irsa.ipac.caltech.edu/data/SPITZER/docs/files/spitzer/shupeADASS.pdf -.. _ds9: http://hea-www.harvard.edu/RD/ds9/ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +"""World Coordinate System (WCS) transformations in FITS files. -Introduction ------------- +.. _wcslib: https://www.atnf.csiro.au/people/mcalabre/WCS/wcslib/index.html +.. _distortion paper: https://www.atnf.csiro.au/people/mcalabre/WCS/dcs_20040422.pdf +.. _SIP: https://irsa.ipac.caltech.edu/data/SPITZER/docs/files/spitzer/shupeADASS.pdf +.. _FITS WCS standard: https://fits.gsfc.nasa.gov/fits_wcs.html `astropy.wcs` contains utilities for managing World Coordinate System (WCS) transformations in FITS files. These transformations map the pixel locations in an image to their real-world units, such as their position on the sky sphere. -It is at its base a wrapper around Mark Calabretta's `wcslib`_, but -also adds support for the Simple Imaging Polynomial (`SIP`_) -convention and table lookup distortions as defined in WCS `Paper IV`_. +It performs three separate classes of WCS transformations: + +- Core WCS, as defined in the `FITS WCS standard`_, based on Mark + Calabretta's `wcslib`_. See `~astropy.wcs.Wcsprm`. +- Simple Imaging Polynomial (`SIP`_) convention. See + `~astropy.wcs.Sip`. +- table lookup distortions as defined in WCS `distortion paper`_. See + `~astropy.wcs.DistortionLookupTable`. + Each of these transformations can be used independently or together in a standard pipeline. """ -from __future__ import division # confidence high +from . import utils +from .wcs import * +from .wcs import InvalidTabularParametersError # just for docs -if not _ASTROPY_SETUP_: - from .wcs import * - class Wcsprm(Wcsprm): - pass +def get_include(): + """ + Get the path to astropy.wcs's C header files. + """ + import os - pass + return os.path.join(os.path.dirname(__file__), "include") diff --git a/astropy/wcs/_docutil.py b/astropy/wcs/_docutil.py deleted file mode 100644 index 0f32ed8eddec..000000000000 --- a/astropy/wcs/_docutil.py +++ /dev/null @@ -1,58 +0,0 @@ -""" -astropy.wcs-specific utilities for generating boilerplate in docstrings. -""" - -from __future__ import division # confidence high - - -__all__ = ['TWO_OR_THREE_ARGS', 'RETURNS', 'ORIGIN', 'RA_DEC_ORDER'] - - -def _fix(content, indent=0): - lines = content.split('\n') - indent = '\n' + ' ' * indent - return indent.join(lines) - - -def TWO_OR_THREE_ARGS(naxis, indent=0): - return _fix( -"""args : flexible - Either two or three arguments may be provided. - - - 2 arguments: An *N* x *{0}* array of *x*- and - *y*-coordinates, and an *origin*. - - - 3 arguments: 2 one-dimensional arrays of *x* and *y* - coordinates, and an *origin*. - - Here, *origin* is the coordinate in the upper left corner of the - image. In FITS and Fortran standards, this is 1. In Numpy and C - standards this is 0. -""".format(naxis), indent) - - -def RETURNS(out_type, indent=0): - return _fix("""result : array - Returns the {0}. If the input was a single array and - origin, a single array is returned, otherwise a tuple of arrays is - returned.""".format(out_type), indent) - - -def ORIGIN(indent=0): - return _fix( -""" -origin : int - Specifies the origin of pixel values. The Fortran and FITS - standards use an origin of 1. Numpy and C use array indexing with - origin at 0. -""", indent) - - -def RA_DEC_ORDER(indent=0): - return _fix( -""" -ra_dec_order : bool, optional - When `True` will ensure that world coordinates are always given - and returned in as (*ra*, *dec*) pairs, regardless of the order of - the axes specified by the in the ``CTYPE`` keywords. -""", indent) diff --git a/astropy/wcs/docstrings.py b/astropy/wcs/docstrings.py index 3a12cd79c0bc..5d7b7af2161c 100644 --- a/astropy/wcs/docstrings.py +++ b/astropy/wcs/docstrings.py @@ -1,7 +1,4 @@ -from __future__ import division # confidence high -del division -# We don't want the "division" symbol in the namespace, since it -# should have only docstrings +# Licensed under a 3-clause BSD style license - see LICENSE.rst # It gets to be really tedious to type long docstrings in ANSI C # syntax (since multi-line string literals are not valid). @@ -9,7 +6,69 @@ # which are then converted by setup.py into docstrings.h, which is # included by pywcs.c -from . import _docutil as __ +__all__ = ["ORIGIN", "RA_DEC_ORDER", "RETURNS", "TWO_OR_MORE_ARGS"] + + +def _fix(content, indent=0): + lines = content.split("\n") + indent = "\n" + " " * indent + return indent.join(lines) + + +def TWO_OR_MORE_ARGS(naxis, indent=0): + return _fix( + f"""*args + There are two accepted forms for the positional arguments: + + - 2 arguments: An *N* x *{naxis}* array of coordinates, and an + *origin*. + + - more than 2 arguments: An array for each axis, followed by + an *origin*. These arrays must be broadcastable to one + another. + + Here, *origin* is the coordinate in the upper left corner of the + image. In FITS and Fortran standards, this is 1. In Numpy and C + standards this is 0. +""", + indent, + ) + + +def RETURNS(out_type, indent=0): + return _fix( + f"""result : array + Returns the {out_type}. If the input was a single array and + origin, a single array is returned, otherwise a tuple of arrays is + returned.""", + indent, + ) + + +def ORIGIN(indent=0): + return _fix( + """ +origin : int + Specifies the origin of pixel values. The Fortran and FITS + standards use an origin of 1. Numpy and C use array indexing with + origin at 0. +""", + indent, + ) + + +def RA_DEC_ORDER(indent=0): + return _fix( + """ +ra_dec_order : bool, optional + When `True` will ensure that world coordinates are always given + and returned in as (*ra*, *dec*) pairs, regardless of the order of + the axes specified by the in the ``CTYPE`` keywords. Default is + `False`. +""", + indent, + ) + a = """ ``double array[a_order+1][a_order+1]`` Focal plane transformation @@ -26,34 +85,39 @@ ``int`` (read-only) Order of the polynomial (``A_ORDER``). """ -all_pix2world = """ +a_radius = """ +``double`` semimajor axis of the ellipsoid that defines the approximate shape +of a target body used in projection (m). If undefined, this is set to `None`. +""" + +all_pix2world = f""" all_pix2world(pixcrd, origin) -> ``double array[ncoord][nelem]`` Transforms pixel coordinates to world coordinates. Does the following: - - Detector to image plane correction (optionally) + - Detector to image plane correction (if present) - - SIP distortion correction (optionally) + - SIP distortion correction (if present) - - Paper IV distortion correction (optionally) + - FITS WCS distortion correction (if present) - - wcslib WCS transformation + - wcslib "core" WCS transformation The first three (the distortion corrections) are done in parallel. Parameters ---------- -pixcrd : double array[ncoord][nelem] - Array of pixel coordinates. +pixcrd : ndarray + Array of pixel coordinates as ``double array[ncoord][nelem]``. -{0} +{ORIGIN()} Returns ------- -world : double array[ncoord][nelem] - Returns an array of world coordinates. +world : ndarray + Returns an array of world coordinates as ``double array[ncoord][nelem]``. Raises ------ @@ -80,7 +144,7 @@ InvalidTransformError Ill-conditioned coordinate transformation parameters. -""".format(__.ORIGIN()) +""" alt = """ ``str`` Character code for alternate coordinate descriptions. @@ -103,6 +167,64 @@ ``int`` (read-only) Order of the polynomial (``AP_ORDER``). """ +b_radius = """ +``double`` intermediate axis of the ellipsoid that defines the approximate shape +of a target body used in projection (m). If undefined, this is set to `None`. +""" + +bdis_obs = """ +``double`` Distance between the centre of the celestial body and the observer (m). +If undefined, this is set to `None`. +""" + +blon_obs = """ +``double`` Bodycentric longitude of the observer in the body-fixed reference frame +of the target body, spanning from 0 to 360 deg (deg). +If undefined, this is set to `None`. +""" + +blat_obs = """ +``double`` Bodycentric latitude of the observer in the body-fixed reference frame +of the target body (deg). If undefined, this is set to `None`. +""" + +c_radius = """ +``double`` semiminor axis of the ellipsoid that defines the approximate shape +of a target body used in projection (m). If undefined, this is set to `None`. +""" + +cel = """ +`~astropy.wcs.Celprm` Information required to transform celestial coordinates. +""" + +Celprm = """ +Class that contains information required to transform celestial coordinates. +It consists of certain members that must be set by the user (given) and others +that are set by the WCSLIB routines (returned). +Some of the latter are supplied for informational purposes and others are for +internal use only. +""" + +Prjprm = """ +Class that contains information needed to project or deproject native spherical coordinates. +It consists of certain members that must be set by the user (given) and others +that are set by the WCSLIB routines (returned). +Some of the latter are supplied for informational purposes and others are for +internal use only. +""" + +aux = """ +`~astropy.wcs.Auxprm` Auxiliary coordinate system information of a specialist nature. +""" + +Auxprm = """ +Class that contains auxiliary coordinate system information of a specialist +nature. + +This class can not be constructed directly from Python, but instead is +returned from `~astropy.wcs.Wcsprm.aux`. +""" + axis_types = """ ``int array[naxis]`` An array of four-digit type codes for each axis. @@ -163,6 +285,27 @@ ``int`` (read-only) Order of the polynomial (``B_ORDER``). """ +bounds_check = """ +bounds_check(pix2world, world2pix) + +Enable/disable bounds checking. + +Parameters +---------- +pix2world : bool, optional + When `True`, enable bounds checking for the pixel-to-world (p2x) + transformations. Default is `True`. + +world2pix : bool, optional + When `True`, enable bounds checking for the world-to-pixel (s2x) + transformations. Default is `True`. + +Notes +----- +Note that by default (without calling `bounds_check`) strict bounds +checking is enabled. +""" + bp = """ ``double array[bp_order+1][bp_order+1]`` Focal plane to pixel transformation matrix. @@ -181,11 +324,11 @@ matrix. For historical compatibility, three alternate specifications of the -linear transforations are available in wcslib. The canonical -``PCi_ja`` with ``CDELTia``, and the deprecated ``CDi_ja`` and -``CROTAia`` keywords. Although the deprecated versions may not -formally co-exist with ``PCi_ja``, the approach here is simply to -ignore them if given in conjunction with ``PCi_ja``. +linear transformations are available in wcslib. The canonical +``PCi_ja`` with ``CDELTia``, ``CDi_ja``, and the deprecated +``CROTAia`` keywords. Although the latter may not formally co-exist +with ``PCi_ja``, the approach here is simply to ignore them if given +in conjunction with ``PCi_ja``. `~astropy.wcs.Wcsprm.has_pc`, `~astropy.wcs.Wcsprm.has_cd` and `~astropy.wcs.Wcsprm.has_crota` can be used to determine which of @@ -237,6 +380,317 @@ (0, 0)`` at the fiducial point, (phi_0, theta_0). Default is `False`. """ +celprm_phi0 = r""" +`float`, `None`. The native longitude, :math:`\phi_0`, in degrees of the +fiducial point, i.e., the point whose celestial coordinates are given in +''Celprm.ref[0:1]''. If `None` or ``nan``, the initialization routine, +``celset()``, will set this to a projection-specific default. +""" + +celprm_theta0 = r""" +`float`, `None`. The native latitude, :math:`\theta_0`, in degrees of the +fiducial point, i.e. the point whose celestial coordinates are given in +``Celprm:ref[0:1]``. If `None` or ``nan``, the initialization routine, +``celset()``, will set this to a projection-specific default. +""" + +celprm_ref = """ +``numpy.ndarray`` with 4 elements. +(Given) The first pair of values should be set to the celestial longitude and +latitude of the fiducial point in degrees - typically right ascension and +declination. These are given by the ``CRVALia`` keywords in ``FITS``. + +(Given and returned) The second pair of values are the native longitude, +``phi_p`` (in degrees), and latitude, ``theta_p`` (in degrees), of the +celestial pole (the latter is the same as the celestial latitude of the +native pole, ``delta_p``) and these are given by the ``FITS`` keywords +``LONPOLEa`` and ``LATPOLEa`` (or by ``PVi_2a`` and ``PVi_3a`` attached +to the longitude axis which take precedence if defined). + +``LONPOLEa`` defaults to ``phi0`` if the celestial latitude of the fiducial +point of the projection is greater than or equal to the native latitude, +otherwise ``phi0 + 180`` (degrees). (This is the condition for the celestial +latitude to increase in the same direction as the native latitude at the +fiducial point.) ``ref[2]`` may be set to `None` or ``numpy.nan`` +or 999.0 to indicate that the correct default should be substituted. + +``theta_p``, the native latitude of the celestial pole (or equally the +celestial latitude of the native pole, ``delta_p``) is often determined +uniquely by ``CRVALia`` and ``LONPOLEa`` in which case ``LATPOLEa`` is ignored. +However, in some circumstances there are two valid solutions for ``theta_p`` +and ``LATPOLEa`` is used to choose between them. ``LATPOLEa`` is set in +``ref[3]`` and the solution closest to this value is used to reset ``ref[3]``. +It is therefore legitimate, for example, to set ``ref[3]`` to ``+90.0`` +to choose the more northerly solution - the default if the ``LATPOLEa`` keyword +is omitted from the ``FITS`` header. For the special case where the fiducial +point of the projection is at native latitude zero, its celestial latitude +is zero, and ``LONPOLEa`` = ``+/- 90.0`` then the celestial latitude of the +native pole is not determined by the first three reference values and +``LATPOLEa`` specifies it completely. + +The returned value, celprm.latpreq, specifies how ``LATPOLEa`` +was actually used.""" + +celprm_euler = """ +*Read-only* ``numpy.ndarray`` with 5 elements. Euler angles and associated +intermediaries derived from the coordinate reference values. The first three +values are the ``Z-``, ``X-``, and ``Z``-Euler angles in degrees, and the +remaining two are the cosine and sine of the ``X``-Euler angle. +""" + +celprm_latpreq = """ +``int``, *read-only*. For informational purposes, this indicates how the +``LATPOLEa`` keyword was used: + +- 0: Not required, ``theta_p == delta_p`` was determined uniquely by the + ``CRVALia`` and ``LONPOLEa`` keywords. +- 1: Required to select between two valid solutions of ``theta_p``. +- 2: ``theta_p`` was specified solely by ``LATPOLEa``. +""" + +celprm_isolat = """ +``bool``, *read-only*. True if the spherical rotation preserves the magnitude +of the latitude, which occurs if the axes of the native and celestial +coordinates are coincident. It signals an opportunity to cache intermediate +calculations common to all elements in a vector computation. +""" + +celprm_prj = """ +*Read-only* Celestial transformation parameters. Some members of `Prjprm` +are read-write, i.e., can be set by the user. For more details, see +documentation for `Prjprm`. +""" + +prjprm_r0 = r""" +The radius of the generating sphere for the projection, a linear scaling +parameter. If this is zero, it will be reset to its default value of +:math:`180^\circ/\pi` (the value for FITS WCS). +""" + +prjprm_code = """ +Three-letter projection code defined by the FITS standard. +""" + +prjprm_pv = """ +Projection parameters. These correspond to the ``PVi_ma`` keywords in FITS, +so ``pv[0]`` is ``PVi_0a``, ``pv[1]`` is ``PVi_1a``, etc., where ``i`` denotes +the latitude-like axis. Many projections use ``pv[1]`` (``PVi_1a``), +some also use ``pv[2]`` (``PVi_2a``) and ``SZP`` uses ``pv[3]`` (``PVi_3a``). +``ZPN`` is currently the only projection that uses any of the others. + +When setting ``pv`` values using lists or ``numpy.ndarray``, +elements set to `None` will be left unchanged while those set to ``numpy.nan`` +will be set to ``WCSLIB``'s ``UNDEFINED`` special value. For efficiency +purposes, if supplied list or ``numpy.ndarray`` is shorter than the length of +the ``pv`` member, then remaining values in ``pv`` will be left unchanged. + +.. note:: + When retrieving ``pv``, a copy of the ``prjprm.pv`` array is returned. + Modifying this array values will not modify underlying ``WCSLIB``'s + ``prjprm.pv`` data. +""" + +prjprm_pvi = """ +Set/Get projection parameters for specific index. These correspond to the +``PVi_ma`` keywords in FITS, so ``pv[0]`` is ``PVi_0a``, ``pv[1]`` is +``PVi_1a``, etc., where ``i`` denotes the latitude-like axis. +Many projections use ``pv[1]`` (``PVi_1a``), +some also use ``pv[2]`` (``PVi_2a``) and ``SZP`` uses ``pv[3]`` (``PVi_3a``). +``ZPN`` is currently the only projection that uses any of the others. + +Setting a ``pvi`` value to `None` will reset the corresponding ``WCSLIB``'s +``prjprm.pv`` element to the default value as set by ``WCSLIB``'s ``prjini()``. + +Setting a ``pvi`` value to ``numpy.nan`` will set the corresponding +``WCSLIB``'s ``prjprm.pv`` element to ``WCSLIB``'s ``UNDEFINED`` special value. +""" + +prjprm_phi0 = r""" +The native longitude, :math:`\phi_0` (in degrees) of the reference point, +i.e. the point ``(x,y) = (0,0)``. If undefined the initialization routine +will set this to a projection-specific default. +""" + +prjprm_theta0 = r""" +the native latitude, :math:`\theta_0` (in degrees) of the reference point, +i.e. the point ``(x,y) = (0,0)``. If undefined the initialization routine +will set this to a projection-specific default. +""" + +prjprm_bounds = """ +Controls bounds checking. If ``bounds&1`` then enable strict bounds checking +for the spherical-to-Cartesian (``s2x``) transformation for the +``AZP``, ``SZP``, ``TAN``, ``SIN``, ``ZPN``, and ``COP`` projections. +If ``bounds&2`` then enable strict bounds checking for the +Cartesian-to-spherical transformation (``x2s``) for the ``HPX`` and ``XPH`` +projections. If ``bounds&4`` then the Cartesian- to-spherical transformations +(``x2s``) will invoke WCSLIB's ``prjbchk()`` to perform bounds checking on the +computed native coordinates, with a tolerance set to suit each projection. +bounds is set to 7 during initialization by default which enables all checks. +Zero it to disable all checking. + +It is not necessary to reset the ``Prjprm`` struct (via ``Prjprm.set()``) when +``bounds`` is changed. +""" + +prjprm_name = """ +*Read-only.* Long name of the projection. +""" + +prjprm_category = """ +*Read-only.* Projection category matching the value of the relevant ``wcs`` +module constants: + +PRJ_ZENITHAL, +PRJ_CYLINDRICAL, +PRJ_PSEUDOCYLINDRICAL, +PRJ_CONVENTIONAL, +PRJ_CONIC, +PRJ_POLYCONIC, +PRJ_QUADCUBE, and +PRJ_HEALPIX. +""" + +prjprm_w = """ +*Read-only.* Intermediate floating-point values derived from the projection +parameters, cached here to save recomputation. + +.. note:: + When retrieving ``w``, a copy of the ``prjprm.w`` array is returned. + Modifying this array values will not modify underlying ``WCSLIB``'s + ``prjprm.w`` data. + +""" + +prjprm_pvrange = """ +*Read-only.* Range of projection parameter indices: 100 times the first allowed +index plus the number of parameters, e.g. ``TAN`` is 0 (no parameters), +``SZP`` is 103 (1 to 3), and ``ZPN`` is 30 (0 to 29). +""" + +prjprm_simplezen = """ +*Read-only.* True if the projection is a radially-symmetric zenithal projection. +""" + +prjprm_equiareal = """ +*Read-only.* True if the projection is equal area. +""" + +prjprm_conformal = """ +*Read-only.* True if the projection is conformal. +""" + +prjprm_global_projection = """ +*Read-only.* True if the projection can represent the whole sphere in a finite, +non-overlapped mapping. +""" + +prjprm_divergent = """ +*Read-only.* True if the projection diverges in latitude. +""" + +prjprm_x0 = r""" +*Read-only.* The offset in ``x`` used to force :math:`(x,y) = (0,0)` at +:math:`(\phi_0, \theta_0)`. +""" + +prjprm_y0 = r""" +*Read-only.* The offset in ``y`` used to force :math:`(x,y) = (0,0)` at +:math:`(\phi_0, \theta_0)`. +""" + +prjprm_m = """ +*Read-only.* Intermediate integer value (used only for the ``ZPN`` and ``HPX`` projections). +""" + +prjprm_n = """ +*Read-only.* Intermediate integer value (used only for the ``ZPN`` and ``HPX`` projections). +""" + +prjprm_set = """ +This method sets up a ``Prjprm`` object according to information supplied +within it. + +Note that this routine need not be called directly; it will be invoked by +`prjx2s` and `prjs2x` if ``Prjprm.flag`` is anything other than a predefined +magic value. + +The one important property of ``set()`` is that the projection code must be +defined in the ``Prjprm`` in order for ``set()`` to identify the required +projection. + +Raises +------ +MemoryError + Null ``prjprm`` pointer passed to WCSLIB routines. + +InvalidPrjParametersError + Invalid projection parameters. + +InvalidCoordinateError + One or more of the ``(x,y)`` or ``(lon,lat)`` coordinates were invalid. +""" + +prjprm_prjx2s = r""" +Deproject Cartesian ``(x,y)`` coordinates in the plane of projection to native +spherical coordinates :math:`(\phi,\theta)`. + +The projection is that specified by ``Prjprm.code``. + +Parameters +---------- +x, y : numpy.ndarray + Arrays corresponding to the first (``x``) and second (``y``) projected + coordinates. + +Returns +------- +phi, theta : tuple of numpy.ndarray + Longitude and latitude :math:`(\phi,\theta)` of the projected point in + native spherical coordinates (in degrees). Values corresponding to + invalid ``(x,y)`` coordinates are set to ``numpy.nan``. + +Raises +------ +MemoryError + Null ``prjprm`` pointer passed to WCSLIB routines. + +InvalidPrjParametersError + Invalid projection parameters. + +""" + +prjprm_prjs2x = r""" +Project native spherical coordinates :math:`(\phi,\theta)` to Cartesian +``(x,y)`` coordinates in the plane of projection. + +The projection is that specified by ``Prjprm.code``. + +Parameters +---------- +phi : numpy.ndarray + Array corresponding to the longitude :math:`\phi` of the projected point + in native spherical coordinates (in degrees). +theta : numpy.ndarray + Array corresponding to the longitude :math:`\theta` of the projected point + in native spherical coordinates (in degrees). Values corresponding to + invalid :math:`(\phi, \theta)` coordinates are set to ``numpy.nan``. + +Returns +------- +x, y : tuple of numpy.ndarray + Projected coordinates. + +Raises +------ +MemoryError + Null ``prjprm`` pointer passed to WCSLIB routines. + +InvalidPrjParametersError + Invalid projection parameters. + +""" + celfix = """ Translates AIPS-convention celestial projection types, ``-NCP`` and ``-GLS``. @@ -267,6 +721,50 @@ It should be set to zero for an image header or pixel list. """ +compare = """ +compare(other, cmp=0, tolerance=0.0) + +Compare two Wcsprm objects for equality. + +Parameters +---------- + +other : Wcsprm + The other Wcsprm object to compare to. + +cmp : int, optional + A bit field controlling the strictness of the comparison. When 0, + (the default), all fields must be identical. + + The following constants, defined in the `astropy.wcs` module, + may be or'ed together to loosen the comparison. + + - ``WCSCOMPARE_ANCILLARY``: Ignores ancillary keywords that don't + change the WCS transformation, such as ``XPOSURE`` or + ``EQUINOX``. Note that this also ignores ``DATE-OBS``, which does + change the WCS transformation in some cases. + + - ``WCSCOMPARE_TILING``: Ignore integral differences in + ``CRPIXja``. This is the 'tiling' condition, where two WCSes + cover different regions of the same map projection and align on + the same map grid. + + - ``WCSCOMPARE_CRPIX``: Ignore any differences at all in + ``CRPIXja``. The two WCSes cover different regions of the same + map projection but may not align on the same grid map. + Overrides ``WCSCOMPARE_TILING``. + +tolerance : float, optional + The amount of tolerance required. For example, for a value of + 1e-6, all floating-point values in the objects must be equal to + the first 6 decimal places. The default value of 0.0 implies + exact equality. + +Returns +------- +equal : bool +""" + convert = """ convert(array) @@ -281,7 +779,7 @@ (K_M, ... K_2, K_1, M) -(see `~astropy.wcs._astropy.wcs.Tabprm.K`) i.e. with the `M` dimension +(see `~astropy.wcs.Tabprm.K`) i.e. with the `M` dimension varying fastest so that the `M` elements of a coordinate vector are stored contiguously in memory. """ @@ -309,16 +807,21 @@ An undefined value is represented by NaN. """ +crln_obs = """ +``double`` Carrington heliographic longitude of the observer (deg). If +undefined, this is set to `None`. +""" + crota = """ ``double array[naxis]`` ``CROTAia`` keyvalues for each coordinate axis. For historical compatibility, three alternate specifications of the -linear transforations are available in wcslib. The canonical -``PCi_ja`` with ``CDELTia``, and the deprecated ``CDi_ja`` and -``CROTAia`` keywords. Although the deprecated versions may not -formally co-exist with ``PCi_ja``, the approach here is simply to -ignore them if given in conjunction with ``PCi_ja``. +linear transformations are available in wcslib. The canonical +``PCi_ja`` with ``CDELTia``, ``CDi_ja``, and the deprecated +``CROTAia`` keywords. Although the latter may not formally co-exist +with ``PCi_ja``, the approach here is simply to ignore them if given +in conjunction with ``PCi_ja``. `~astropy.wcs.Wcsprm.has_pc`, `~astropy.wcs.Wcsprm.has_cd` and `~astropy.wcs.Wcsprm.has_crota` can be used to determine which of @@ -400,7 +903,8 @@ """ cunit = """ -``list of strings[naxis]`` List of ``CUNITia`` keyvalues. +``list of astropy.UnitBase[naxis]`` List of ``CUNITia`` keyvalues as +`astropy.units.UnitBase` instances. These define the units of measurement of the ``CRVALia``, ``CDELTia`` and ``CDi_ja`` keywords. @@ -413,13 +917,13 @@ as a separate step before invoking `~astropy.wcs.Wcsprm.set`. For celestial axes, if `~astropy.wcs.Wcsprm.cunit` is not blank, -`~astropy.wcs.Wcsprm.set` uses `wcsunits` to parse it and scale +`~astropy.wcs.Wcsprm.set` uses ``wcsunits`` to parse it and scale `~astropy.wcs.Wcsprm.cdelt`, `~astropy.wcs.Wcsprm.crval`, and `~astropy.wcs.Wcsprm.cd` to decimal degrees. It then resets `~astropy.wcs.Wcsprm.cunit` to ``"deg"``. For spectral axes, if `~astropy.wcs.Wcsprm.cunit` is not blank, -`~astropy.wcs.Wcsprm.set` uses `wcsunits` to parse it and scale +`~astropy.wcs.Wcsprm.set` uses ``wcsunits`` to parse it and scale `~astropy.wcs.Wcsprm.cdelt`, `~astropy.wcs.Wcsprm.crval`, and `~astropy.wcs.Wcsprm.cd` to SI units. It then resets `~astropy.wcs.Wcsprm.cunit` accordingly. @@ -495,7 +999,7 @@ Array of interpolated indices into the coordinate array such that Upsilon_m, as defined in Paper III, is equal to -(`~astropy.wcs._astropy.wcs.Tabprm.p0` [m] + 1) + delta[m]. +(`~astropy.wcs.Tabprm.p0` [m] + 1) + delta[m]. """ det2im = """ @@ -516,13 +1020,13 @@ ``int array[ndim]`` (read-only) The dimensions of the tabular array -`~astropy.wcs._astropy.wcs.Wtbarr.data`. +`~astropy.wcs.Wtbarr.data`. """ DistortionLookupTable = """ -DistortionLookupTable(*table*, *crpix*, *crval*, *cdelt*) +DistortionLookupTable(table, crpix, crval, cdelt) -Represents a single lookup table for a `Paper IV`_ distortion +Represents a single lookup table for a `distortion paper`_ transformation. Parameters @@ -531,15 +1035,22 @@ The distortion lookup table. crpix : 2-tuple - The distortion array reference pixel + The distortion array reference pixel, in FITS Header format: 1-based + indexing, (x,y) order. crval : 2-tuple - The image array pixel coordinate + The image array pixel coordinate, in FITS Header format: 1-based indexing, + (x,y) order. cdelt : 2-tuple The grid step size """ +dsun_obs = """ +``double`` Distance between the centre of the Sun and the observer (m). If +undefined, this is set to `None`. +""" + equinox = """ ``double`` The equinox associated with dynamical equatorial or ecliptic coordinate systems. @@ -551,15 +1062,11 @@ """ extlev = """ -``int`` (read-only) - -``EXTLEV`` identifying the binary table extension. +``int`` (read-only) ``EXTLEV`` identifying the binary table extension. """ extnam = """ -``str`` (read-only) - -``EXTNAME`` identifying the binary table extension. +``str`` (read-only) ``EXTNAME`` identifying the binary table extension. """ extrema = """ @@ -571,16 +1078,14 @@ (K_M, ... K_2, 2, M) -(see `~astropy.wcs._astropy.wcs.Tabprm.K`). The minimum is recorded +(see `~astropy.wcs.Tabprm.K`). The minimum is recorded in the first element of the compressed K_1 dimension, then the maximum. This array is used by the inverse table lookup function to speed up table searches. """ extver = """ -``int`` (read-only) - -``EXTVER`` identifying the binary table extension. +``int`` (read-only) ``EXTVER`` identifying the binary table extension. """ find_all_wcs = """ @@ -604,7 +1109,7 @@ standard. - `int`: a bit field selecting specific extensions to accept. See - :ref:`relaxread` for details. + :ref:`astropy:relaxread` for details. keysel : sequence of flags Used to restrict the keyword types considered: @@ -620,7 +1125,7 @@ Returns ------- -wcs_list : list of `~astropy.wcs._astropy.wcs._Wcsprm` objects +wcs_list : list of `~astropy.wcs.Wcsprm` """ fix = """ @@ -634,8 +1139,9 @@ Parameters ---------- -translate_units : str - Do potentially unsafe translations of non-standard unit strings. +translate_units : str, optional + Specify which potentially unsafe translations of non-standard unit + strings to perform. By default, performs all. Although ``"S"`` is commonly used to represent seconds, its translation to ``"s"`` is potentially unsafe since the standard @@ -655,7 +1161,7 @@ Thus ``''`` doesn't do any unsafe translations, whereas ``'shd'`` does all of them. -naxis : int array[naxis] +naxis : int array, optional Image axis lengths. If this array is set to zero or ``None``, then `~astropy.wcs.Wcsprm.cylfix` will not be invoked. @@ -687,42 +1193,42 @@ Returns ------- -coordinate : coordinate pair +coordinate : (2,) tuple The offset from the distortion table for pixel point (*x*, *y*). """ get_cdelt = """ -get_cdelt() -> double array[naxis] +get_cdelt() -> numpy.ndarray -Coordinate increments (``CDELTia``) for each coord axis. +Coordinate increments (``CDELTia``) for each coord axis as ``double array[naxis]``. Returns the ``CDELT`` offsets in read-only form. Unlike the `~astropy.wcs.Wcsprm.cdelt` property, this works even when the header -specifies the linear transformation matrix in one of the deprecated +specifies the linear transformation matrix in one of the alternative ``CDi_ja`` or ``CROTAia`` forms. This is useful when you want access to the linear transformation matrix, but don't care how it was specified in the header. """ get_pc = """ -get_pc() -> double array[naxis][naxis] +get_pc() -> numpy.ndarray -Returns the ``PC`` matrix in read-only form. Unlike the +Returns the ``PC`` matrix in read-only form as ``double array[naxis][naxis]``. Unlike the `~astropy.wcs.Wcsprm.pc` property, this works even when the header -specifies the linear transformation matrix in one of the deprecated +specifies the linear transformation matrix in one of the alternative ``CDi_ja`` or ``CROTAia`` forms. This is useful when you want access to the linear transformation matrix, but don't care how it was specified in the header. """ get_ps = """ -get_ps() -> list of tuples +get_ps() -> list -Returns ``PSi_ma`` keywords for each *i* and *m*. +Returns ``PSi_ma`` keywords for each *i* and *m* as list of tuples. Returns ------- -ps : list of tuples +ps : list Returned as a list of tuples of the form (*i*, *m*, *value*): @@ -738,13 +1244,13 @@ """ get_pv = """ -get_pv() -> list of tuples +get_pv() -> list -Returns ``PVi_ma`` keywords for each *i* and *m*. +Returns ``PVi_ma`` keywords for each *i* and *m* as list of tuples. Returns ------- - +sequence of tuple Returned as a list of tuples of the form (*i*, *m*, *value*): - *i*: int. Axis number, as in ``PVi_ma``, (i.e. 1-relative) @@ -846,17 +1352,18 @@ compatibility. """ -have = """ -``string`` The name of the unit being converted from. +hgln_obs = """ +``double`` Stonyhurst heliographic longitude of the observer. If +undefined, this is set to `None`. +""" -This value always uses standard unit names, even if the -`UnitConverter` was initialized with a non-standard unit name. +hglt_obs = """ +``double`` Heliographic latitude (Carrington or Stonyhurst) of the observer +(deg). If undefined, this is set to `None`. """ i = """ -``int`` (read-only) - -Image axis number. +``int`` (read-only) Image axis number. """ imgpix_matrix = """ @@ -883,9 +1390,9 @@ """ kind = """ -``str`` (read-only) +``str`` (read-only) ``wcstab`` array type. -Character identifying the wcstab array type: +Character identifying the ``wcstab`` array type: - ``'c'``: coordinate array, - ``'i'``: index vector. @@ -932,15 +1439,13 @@ """ m = """ -``int`` (read-only) - -Array axis number for index vectors. +``int`` (read-only) ``wcstab`` axis number for index vectors. """ map = """ ``int array[M]`` Association between axes. -A vector of length `~astropy.wcs._astropy.wcs.Tabprm.M` that defines +A vector of length `~astropy.wcs.Tabprm.M` that defines the association between axis *m* in the *M*-dimensional coordinate array (1 <= *m* <= *M*) and the indices of the intermediate world coordinate and world coordinate arrays. @@ -960,7 +1465,7 @@ 1. In this case ``map[0] = 0`` regardless of the value of *i*. """ -mix = """ +mix = f""" mix(mixpix, mixcel, vspan, vstep, viter, world, pixcrd, origin) Given either the celestial longitude or latitude plus an element of @@ -980,7 +1485,7 @@ celestial latitude is given in ``world[self.lat]``, longitude returned in ``world[self.lng]``. -vspan : pair of floats +vspan : (float, float) Solution interval for the celestial coordinate, in degrees. The ordering of the two limits is irrelevant. Longitude ranges may be specified with any convenient normalization, for example @@ -997,18 +1502,18 @@ the search recommenced. *viter* controls how many times the step size is halved. The allowed range is 5 - 10. -world : double array[naxis] - World coordinate elements. ``world[self.lng]`` and +world : ndarray + World coordinate elements as ``double array[naxis]``. ``world[self.lng]`` and ``world[self.lat]`` are the celestial longitude and latitude, in degrees. Which is given and which returned depends on the value of *mixcel*. All other elements are given. The results will be written to this array in-place. -pixcrd : double array[naxis]. - Pixel coordinates. The element indicated by *mixpix* is given and +pixcrd : ndarray + Pixel coordinates as ``double array[naxis]``. The element indicated by *mixpix* is given and the remaining elements will be written in-place. -{0} +{ORIGIN()} Returns ------- @@ -1016,20 +1521,20 @@ Returns a dictionary with the following keys: - - *phi* (double array[naxis]) + - *phi* (``double array[naxis]``) - - *theta* (double array[naxis]) + - *theta* (``double array[naxis]``) - Longitude and latitude in the native coordinate system of the projection, in degrees. - - *imgcrd* (double array[naxis]) + - *imgcrd* (``double array[naxis]``) - Image coordinate elements. ``imgcrd[self.lng]`` and ``imgcrd[self.lat]`` are the projected *x*- and *y*-coordinates, in decimal degrees. - - *world* (double array[naxis]) + - *world* (``double array[naxis]``) - Another reference to the *world* argument passed in. @@ -1094,7 +1599,7 @@ compute-intensive. For compute-limited applications, more efficient special-case solvers could be written for simple projections, for example non-oblique cylindrical projections. -""".format(__.ORIGIN()) +""" mjdavg = """ ``double`` Modified Julian Date corresponding to ``DATE-AVG``. @@ -1159,9 +1664,7 @@ """ ndim = """ -``int`` (read-only) - -Expected dimensionality of the wcstab array. +``int`` (read-only) Expected dimensionality of the ``wcstab`` array. """ obsgeo = """ @@ -1173,19 +1676,15 @@ An undefined value is represented by NaN. """ -offset = """ -``double`` The offset of the unit conversion. -""" - p0 = """ ``int array[M]`` Interpolated indices into the coordinate array. -Vector of length `~astropy.wcs._astropy.wcs.Tabprm.M` of interpolated +Vector of length `~astropy.wcs.Tabprm.M` of interpolated indices into the coordinate array such that Upsilon_m, as defined in Paper III, is equal to ``(p0[m] + 1) + delta[m]``. """ -p2s = """ +p2s = f""" p2s(pixcrd, origin) Converts pixel to world coordinates. @@ -1193,42 +1692,44 @@ Parameters ---------- -pixcrd : double array[ncoord][nelem] - Array of pixel coordinates. +pixcrd : ndarray + Array of pixel coordinates as ``double array[ncoord][nelem]``. -{0} +{ORIGIN()} Returns ------- result : dict Returns a dictionary with the following keys: - - *imgcrd*: double array[ncoord][nelem] + - *imgcrd*: ndarray - - Array of intermediate world coordinates. For celestial axes, + - Array of intermediate world coordinates as ``double array[ncoord][nelem]``. For celestial axes, ``imgcrd[][self.lng]`` and ``imgcrd[][self.lat]`` are the projected *x*-, and *y*-coordinates, in pseudo degrees. For spectral axes, ``imgcrd[][self.spec]`` is the intermediate spectral coordinate, in SI units. - - *phi*: double array[ncoord] + - *phi*: ndarray + + - Array as ``double array[ncoord]``. - - *theta*: double array[ncoord] + - *theta*: ndarray - Longitude and latitude in the native coordinate system of the - projection, in degrees. + projection, in degrees, as ``double array[ncoord]``. - - *world*: double array[ncoord][nelem] + - *world*: ndarray - - Array of world coordinates. For celestial axes, + - Array of world coordinates as ``double array[ncoord][nelem]``. For celestial axes, ``world[][self.lng]`` and ``world[][self.lat]`` are the celestial longitude and latitude, in degrees. For spectral axes, ``world[][self.spec]`` is the intermediate spectral coordinate, in SI units. - - *stat*: int array[ncoord] + - *stat*: ndarray - - Status return value for each coordinate. ``0`` for success, + - Status return value for each coordinate as ``int array[ncoord]``. ``0`` for success, ``1+`` for invalid pixel coordinate. Raises @@ -1258,26 +1759,26 @@ See also -------- astropy.wcs.Wcsprm.lat, astropy.wcs.Wcsprm.lng - Definition of the latitude andlongitude axes -""".format(__.ORIGIN()) + Definition of the latitude and longitude axes +""" -p4_pix2foc = """ -p4_pix2foc(*pixcrd, origin*) -> double array[ncoord][nelem] +p4_pix2foc = f""" +p4_pix2foc(*pixcrd, origin*) -> ``double array[ncoord][nelem]`` -Convert pixel coordinates to focal plane coordinates using `Paper IV`_ -lookup-table distortion correction. +Convert pixel coordinates to focal plane coordinates using `distortion +paper`_ lookup-table correction. Parameters ---------- -pixcrd : double array[ncoord][nelem]. - Array of pixel coordinates. +pixcrd : ndarray + Array of pixel coordinates as ``double array[ncoord][nelem]``. -{0} +{ORIGIN()} Returns ------- -foccrd : double array[ncoord][nelem] - Returns an array of focal plane coordinates. +foccrd : ndarray + Returns an array of focal plane coordinates as ``double array[ncoord][nelem]``. Raises ------ @@ -1286,7 +1787,7 @@ ValueError Invalid coordinate transformation parameters. -""".format(__.ORIGIN()) +""" pc = """ ``double array[naxis][naxis]`` The ``PCi_ja`` (pixel coordinate) @@ -1298,11 +1799,11 @@ [PC2_1, PC2_2]] For historical compatibility, three alternate specifications of the -linear transforations are available in wcslib. The canonical -``PCi_ja`` with ``CDELTia``, and the deprecated ``CDi_ja`` and -``CROTAia`` keywords. Although the deprecated versions may not -formally co-exist with ``PCi_ja``, the approach here is simply to -ignore them if given in conjunction with ``PCi_ja``. +linear transformations are available in wcslib. The canonical +``PCi_ja`` with ``CDELTia``, ``CDi_ja``, and the deprecated +``CROTAia`` keywords. Although the latter may not formally co-exist +with ``PCi_ja``, the approach here is simply to ignore them if given +in conjunction with ``PCi_ja``. `~astropy.wcs.Wcsprm.has_pc`, `~astropy.wcs.Wcsprm.has_cd` and `~astropy.wcs.Wcsprm.has_crota` can be used to determine which of @@ -1329,23 +1830,23 @@ astropy.wcs.Wcsprm.theta0 """ -pix2foc = """ -pix2foc(*pixcrd, origin*) -> double array[ncoord][nelem] +pix2foc = f""" +pix2foc(*pixcrd, origin*) -> ``double array[ncoord][nelem]`` -Perform both `SIP`_ polynomial and `Paper IV`_ lookup-table distortion +Perform both `SIP`_ polynomial and `distortion paper`_ lookup-table correction in parallel. Parameters ---------- -pixcrd : double array[ncoord][nelem] - Array of pixel coordinates. +pixcrd : ndarray + Array of pixel coordinates as ``double array[ncoord][nelem]``. -{0} +{ORIGIN()} Returns ------- -foccrd : double array[ncoord][nelem] - Returns an array of focal plane coordinates. +foccrd : ndarray + Returns an array of focal plane coordinates as ``double array[ncoord][nelem]``. Raises ------ @@ -1354,17 +1855,13 @@ ValueError Invalid coordinate transformation parameters. -""".format(__.ORIGIN()) +""" piximg_matrix = """ ``double array[2][2]`` (read-only) Matrix containing the product of the ``CDELTia`` diagonal matrix and the ``PCi_ja`` matrix. """ -power = """ -``double`` The exponent of the unit conversion. -""" - print_contents = """ print_contents() @@ -1378,13 +1875,23 @@ print_contents_tabprm = """ print_contents() -Print the contents of the `~astropy.wcs._astropy.wcs.Tabprm` object to +Print the contents of the `~astropy.wcs.Tabprm` object to stdout. Probably only useful for debugging purposes, and may be removed in the future. To get a string of the contents, use `repr`. """ +print_contents_wtbarr = """ +print_contents() + +Print the contents of the `~astropy.wcs.Wtbarr` object to +stdout. Probably only useful for debugging purposes, and may be +removed in the future. + +To get a string of the contents, use `repr`. +""" + radesys = """ ``string`` The equatorial or ecliptic coordinate system type, ``RADESYSa``. @@ -1403,36 +1910,39 @@ """ row = """ -``int`` (read-only) +``int`` (read-only) Table row number. +""" -Table row number. +rsun_ref = """ +``double`` Reference radius of the Sun used in coordinate calculations (m). +If undefined, this is set to `None`. """ -s2p = """ +s2p = f""" s2p(world, origin) Transforms world coordinates to pixel coordinates. Parameters ---------- -world : double array[ncoord][nelem] - Array of world coordinates, in decimal degrees. +world : ndarray + Array of world coordinates, in decimal degrees, as ``double array[ncoord][nelem]``. -{0} +{ORIGIN()} Returns ------- result : dict Returns a dictionary with the following keys: - - *phi*: double array[ncoord] + - *phi*: ``double array[ncoord]`` - - *theta*: double array[ncoord] + - *theta*: ``double array[ncoord]`` - Longitude and latitude in the native coordinate system of the projection, in degrees. - - *imgcrd*: double array[ncoord][nelem] + - *imgcrd*: ``double array[ncoord][nelem]`` - Array of intermediate world coordinates. For celestial axes, ``imgcrd[][self.lng]`` and ``imgcrd[][self.lat]`` are the @@ -1442,12 +1952,12 @@ spectral axes, ``imgcrd[][self.spec]`` is the intermediate spectral coordinate, in SI units. - - *pixcrd*: double array[ncoord][nelem] + - *pixcrd*: ``double array[ncoord][nelem]`` - Array of pixel coordinates. Pixel coordinates are zero-based. - - *stat*: int array[ncoord] + - *stat*: ``int array[ncoord]`` - Status return value for each coordinate. ``0`` for success, ``1+`` for invalid pixel coordinate. @@ -1476,16 +1986,12 @@ -------- astropy.wcs.Wcsprm.lat, astropy.wcs.Wcsprm.lng Definition of the latitude and longitude axes -""".format(__.ORIGIN()) - -scale = """ -``double`` The scaling factor for the unit conversion. """ sense = """ ``int array[M]`` +1 if monotonically increasing, -1 if decreasing. -A vector of length `~astropy.wcs._astropy.wcs.Tabprm.M` whose elements +A vector of length `~astropy.wcs.Tabprm.M` whose elements indicate whether the corresponding indexing vector is monotonically increasing (+1), or decreasing (-1). """ @@ -1549,18 +2055,35 @@ MemoryError Memory allocation failed. -InvalidTabularParameters +InvalidTabularParametersError Invalid tabular parameters. """ +set_celprm = """ +set() + +Sets up a ``celprm`` struct according to information supplied within it. + +Note that this routine need not be called directly; it will be invoked +by functions that need it. + +Raises +------ +MemoryError + Memory allocation failed. + +InvalidPrjParametersError + Invalid celestial parameters. +""" + set_ps = """ -set_ps(list) +set_ps(ps) -Sets `PSi_ma` keywords for each *i* and *m*. +Sets ``PSi_ma`` keywords for each *i* and *m*. Parameters ---------- -ps : sequence of tuples +ps : sequence of tuple The input must be a sequence of tuples of the form (*i*, *m*, *value*): @@ -1577,13 +2100,13 @@ """ set_pv = """ -set_pv(list) +set_pv(pv) -Sets `PVi_ma` keywords for each *i* and *m*. +Sets ``PVi_ma`` keywords for each *i* and *m*. Parameters ---------- -pv : list of tuples +pv : list of tuple The input must be a sequence of tuples of the form (*i*, *m*, *value*): @@ -1612,24 +2135,24 @@ Parameters ---------- -a : double array[m+1][m+1] - The ``A_i_j`` polynomial for pixel to focal plane transformation. +a : ndarray + The ``A_i_j`` polynomial for pixel to focal plane transformation as ``double array[m+1][m+1]``. Its size must be (*m* + 1, *m* + 1) where *m* = ``A_ORDER``. -b : double array[m+1][m+1] - The ``B_i_j`` polynomial for pixel to focal plane transformation. +b : ndarray + The ``B_i_j`` polynomial for pixel to focal plane transformation as ``double array[m+1][m+1]``. Its size must be (*m* + 1, *m* + 1) where *m* = ``B_ORDER``. -ap : double array[m+1][m+1] - The ``AP_i_j`` polynomial for pixel to focal plane transformation. +ap : ndarray + The ``AP_i_j`` polynomial for pixel to focal plane transformation as ``double array[m+1][m+1]``. Its size must be (*m* + 1, *m* + 1) where *m* = ``AP_ORDER``. -bp : double array[m+1][m+1] - The ``BP_i_j`` polynomial for pixel to focal plane transformation. +bp : ndarray + The ``BP_i_j`` polynomial for pixel to focal plane transformation as ``double array[m+1][m+1]``. Its size must be (*m* + 1, *m* + 1) where *m* = ``BP_ORDER``. -crpix : double array[2] - The reference pixel. +crpix : ndarray + The reference pixel as ``double array[2]``. Notes ----- @@ -1638,23 +2161,23 @@ Headers." ADASS XIV. """ -sip_foc2pix = """ -sip_foc2pix(*foccrd, origin*) -> double array[ncoord][nelem] +sip_foc2pix = f""" +sip_foc2pix(*foccrd, origin*) -> ``double array[ncoord][nelem]`` Convert focal plane coordinates to pixel coordinates using the `SIP`_ polynomial distortion convention. Parameters ---------- -foccrd : double array[ncoord][nelem] - Array of focal plane coordinates. +foccrd : ndarray + Array of focal plane coordinates as ``double array[ncoord][nelem]``. -{0} +{ORIGIN()} Returns ------- -pixcrd : double array[ncoord][nelem] - Returns an array of pixel coordinates. +pixcrd : ndarray + Returns an array of pixel coordinates as ``double array[ncoord][nelem]``. Raises ------ @@ -1663,25 +2186,25 @@ ValueError Invalid coordinate transformation parameters. -""".format(__.ORIGIN()) +""" -sip_pix2foc = """ -sip_pix2foc(*pixcrd, origin*) -> double array[ncoord][nelem] +sip_pix2foc = f""" +sip_pix2foc(*pixcrd, origin*) -> ``double array[ncoord][nelem]`` Convert pixel coordinates to focal plane coordinates using the `SIP`_ polynomial distortion convention. Parameters ---------- -pixcrd : double array[ncoord][nelem] - Array of pixel coordinates. +pixcrd : ndarray + Array of pixel coordinates as ``double array[ncoord][nelem]``. -{0} +{ORIGIN()} Returns ------- -foccrd : double array[ncoord][nelem] - Returns an array of focal plane coordinates. +foccrd : ndarray + Returns an array of focal plane coordinates as ``double array[ncoord][nelem]``. Raises ------ @@ -1690,7 +2213,7 @@ ValueError Invalid coordinate transformation parameters. -""".format(__.ORIGIN()) +""" spcfix = """ spcfix() -> int @@ -1727,7 +2250,7 @@ Parameters ---------- -ctype : string +ctype : str Required spectral ``CTYPEia``, maximum of 8 characters. The first four characters are required to be given and are never modified. The remaining four, the algorithm code, are completely determined @@ -1831,6 +2354,8 @@ - ``'stokes'`` / ``WCSSUB_STOKES``: Stokes axis + - ``'temporal'`` / ``WCSSUB_TIME``: Time axis (requires ``WCSLIB`` version 7.8 or greater) + - ``'celestial'`` / ``WCSSUB_CELESTIAL``: An alias for the combination of ``'longitude'``, ``'latitude'`` and ``'cubeface'``. @@ -1846,7 +2371,7 @@ InvalidSubimageSpecificationError Invalid subimage specification (no spectral axis). -NonseparableSubimageCoordinateSystem +NonseparableSubimageCoordinateSystemError Non-separable subimage coordinate system. Notes @@ -1875,13 +2400,13 @@ -(WCSSUB_SPECTRAL | WCSSUB_STOKES)]) The last of these specifies all axis types other than spectral or -Stokes. Extraction is done in the order specified by `axes`, i.e. a +Stokes. Extraction is done in the order specified by ``axes``, i.e. a longitude axis (if present) would be extracted first (via ``axes[0]``) and not subsequently (via ``axes[3]``). Likewise for the latitude and cubeface axes in this example. The number of dimensions in the returned object may be less than or -greater than the length of `axes`. However, it will never exceed the +greater than the length of ``axes``. However, it will never exceed the number of axes in the input image. """ @@ -1936,7 +2461,7 @@ 2. Deprecated (e.g. ``CROTAn``) or non-standard usage will be translated to standard (this is partially dependent on whether - `fix` was applied). + ``fix`` was applied). 3. Quantities will be converted to the units used internally, basically SI with the addition of degrees. @@ -1959,7 +2484,7 @@ Keywords can be translated between the image array, binary table, and pixel lists forms by manipulating the `~astropy.wcs.Wcsprm.colnum` or -`~astropy.wcs.Wcsprm.colax` members of the `~astropy.wcs.Wcsprm.WCS` +`~astropy.wcs.Wcsprm.colax` members of the `~astropy.wcs.WCS` object. Parameters @@ -1975,7 +2500,7 @@ standard. - `int`: a bit field selecting specific extensions to write. - See :ref:`relaxwrite` for details. + See :ref:`astropy:relaxwrite` for details. Returns ------- @@ -1984,100 +2509,10 @@ """ ttype = """ -``str`` (read-only) - -``TTYPEn`` identifying the column of the binary table that contains +``str`` (read-only) ``TTYPEn`` identifying the column of the binary table that contains the wcstab array. """ -UnitConverter = """ -UnitConverter(have, want, translate_units='') - -An object for converting from one system of units to another. - -Use the returned object's `~astropy.wcs.UnitConverter.convert` method -to convert values from *have* to *want*. - -This function is permissive in accepting whitespace in all contexts in -a units specification where it does not create ambiguity (e.g. not -between a metric prefix and a basic unit string), including in strings -like ``"log (m ** 2)"`` which is formally disallowed. - -Parameters ----------- - -have : string - :ref:`fits-unit` to convert from, with or without surrounding - square brackets (for inline specifications); text following the - closing bracket is ignored. - -want : string - :ref:`fits-unit` to convert to, with or without surrounding square - brackets (for inline specifications); text following the closing - bracket is ignored. - -ctrl : string, optional - Do potentially unsafe translations of non-standard unit strings. - - Although ``\"S\"`` is commonly used to represent seconds, its - recognizes ``\"S\"`` formally as Siemens, however rarely that may - be translation to ``\"s\"`` is potentially unsafe since the - standard used. The same applies to ``\"H\"`` for hours (Henry), - and ``\"D\"`` for days (Debye). - - This string controls what to do in such cases, and is - case-insensitive. - - - If the string contains ``"s"``, translate ``"S"`` to ``"s"``. - - - If the string contains ``"h"``, translate ``"H"`` to ``"h"``. - - - If the string contains ``"d"``, translate ``"D"`` to ``"d"``. - - Thus ``''`` doesn't do any unsafe translations, whereas ``'shd'`` - does all of them. - - See :ref:`fits-unit` for more information. - -Raises ------- -ValueError - Invalid numeric multiplier. - -SyntaxError - Dangling binary operator. - -SyntaxError - Invalid symbol in INITIAL context. - -SyntaxError - Function in invalid context. - -SyntaxError - Invalid symbol in EXPON context. - -SyntaxError - Unbalanced bracket. - -SyntaxError - Unbalanced parenthesis. - -SyntaxError - Consecutive binary operators. - -SyntaxError - Internal parser error. - -SyntaxError - Non-conformant unit specifications. - -SyntaxError - Non-conformant functions. - -ValueError - Potentially unsafe translation. -""" - unitfix = """ unitfix(translate_units='') @@ -2088,7 +2523,7 @@ Parameters ---------- -translate_units : string, optional +translate_units : str, optional Do potentially unsafe translations of non-standard unit strings. Although ``\"S\"`` is commonly used to represent seconds, its @@ -2109,8 +2544,6 @@ Thus ``''`` doesn't do any unsafe translations, whereas ``'shd'`` does all of them. - See :ref:`fits-unit` for more information. - Returns ------- success : int @@ -2140,11 +2573,10 @@ astropy.wcs.Wcsprm.specsys, astropy.wcs.Wcsprm.ssysobs """ -want = """ -``string`` The name of the unit being converted to. +velref = """ +``int`` AIPS velocity code. -This value always uses standard unit names, even if the -`UnitConverter` was initialized with a non-standard unit name. +From ``VELREF`` keyword. """ wcs = """ @@ -2156,29 +2588,34 @@ Wcs(*sip, cpdis, wcsprm, det2im*) Wcs objects amalgamate basic WCS (as provided by `wcslib`_), with -`SIP`_ and `Paper IV`_ distortion operations. +`SIP`_ and `distortion paper`_ operations. -To perform all distortion corrections and WCS tranformation, use -`all_pix2world`. +To perform all distortion corrections and WCS transformation, use +``all_pix2world``. Parameters ---------- -sip : `~astropy.wcs.Sip` object or `None` +sip : `~astropy.wcs.Sip` object or None -cpdis : A pair of `~astropy.wcs.DistortionLookupTable` objects, or - ``(None, None)``. +cpdis : (2,) tuple of `~astropy.wcs.DistortionLookupTable` or None -wcsprm : `~astropy.wcs.Wcsprm` object +wcsprm : `~astropy.wcs.Wcsprm` -det2im : A pair of `~astropy.wcs.DistortionLookupTable` objects, or - ``(None, None)``. +det2im : (2,) tuple of `~astropy.wcs.DistortionLookupTable` or None """ Wcsprm = """ Wcsprm(header=None, key=' ', relax=False, naxis=2, keysel=0, colsel=None) -`~astropy.wcs.Wcsprm` is a direct wrapper around `wcslib`_, and -provides access to the core WCS transformations that it supports. +`~astropy.wcs.Wcsprm` performs the core WCS transformations. + +.. note:: + The members of this object correspond roughly to the key/value + pairs in the FITS header. However, they are adjusted and + normalized in a number of ways that make performing the WCS + transformation easier. Therefore, they can not be relied upon to + get the original values in the header. For that, use + `astropy.io.fits.Header` directly. The FITS header parsing enforces correct FITS "keyword = value" syntax with regard to the equals sign occurring in columns 9 and 10. @@ -2186,12 +2623,25 @@ Sect. 5.2.1), integer (Sect. 5.2.3), and floating-point values (Sect. 5.2.4) for all keywords. + +.. warning:: + + Many of the attributes of this class require additional processing when + modifying underlying C structure. When needed, this additional processing + is implemented in attribute setters. Therefore, for mutable attributes, one + should always set the attribute rather than a slice of its current value (or + its individual elements) since the latter may lead the class instance to be + in an invalid state. For example, attribute ``crpix`` of a 2D WCS' + ``Wcsprm`` object ``wcs`` should be set as ``wcs.crpix = [crpix1, crpix2]`` + instead of ``wcs.crpix[0] = crpix1; wcs.crpix[1] = crpix2]``. + + Parameters ---------- -header : An astropy.io.fits header, string, or `None`. +header : `~astropy.io.fits.Header`, str, or None. If ``None``, the object will be initialized to default values. -key : string, optional +key : str, optional The key referring to a particular WCS transform in the header. This may be either ``' '`` or ``'A'``-``'Z'`` and corresponds to the ``\"a\"`` part of ``\"CTYPEia\"``. (*key* may only be @@ -2208,7 +2658,7 @@ standard. - `int`: a bit field selecting specific extensions to accept. See - :ref:`relaxread` for details. + :ref:`astropy:relaxread` for details. naxis : int, optional The number of world coordinates axes for the object. (*naxis* may @@ -2243,6 +2693,11 @@ Key not found in FITS header. """ +wtb = """ +``list of Wtbarr`` objects to construct coordinate lookup tables from BINTABLE. + +""" + Wtbarr = """ Classes to construct coordinate lookup tables from a binary table extension (BINTABLE). @@ -2256,3 +2711,291 @@ An undefined value is represented by NaN. """ + +WcsError = """ +Base class of all invalid WCS errors. +""" + +SingularMatrix = """ +SingularMatrixError() + +The linear transformation matrix is singular. +""" + +InconsistentAxisTypes = """ +InconsistentAxisTypesError() + +The WCS header inconsistent or unrecognized coordinate axis type(s). +""" + +InvalidTransform = """ +InvalidTransformError() + +The WCS transformation is invalid, or the transformation parameters +are invalid. +""" + +InvalidCoordinate = """ +InvalidCoordinateError() + +One or more of the world coordinates is invalid. +""" + +NoSolution = """ +NoSolutionError() + +No solution can be found in the given interval. +""" + +InvalidSubimageSpecification = """ +InvalidSubimageSpecificationError() + +The subimage specification is invalid. +""" + +NonseparableSubimageCoordinateSystem = """ +NonseparableSubimageCoordinateSystemError() + +Non-separable subimage coordinate system. +""" + +NoWcsKeywordsFound = """ +NoWcsKeywordsFoundError() + +No WCS keywords were found in the given header. +""" + +InvalidTabularParameters = """ +InvalidTabularParametersError() + +The given tabular parameters are invalid. +""" + +InvalidPrjParameters = """ +InvalidPrjParametersError() + +The given projection parameters are invalid. +""" + +mjdbeg = """ +``double`` Modified Julian Date corresponding to ``DATE-BEG``. + +``(MJD = JD - 2400000.5)``. + +An undefined value is represented by NaN. + +See also +-------- +astropy.wcs.Wcsprm.mjdbeg +""" + +mjdend = """ +``double`` Modified Julian Date corresponding to ``DATE-END``. + +``(MJD = JD - 2400000.5)``. + +An undefined value is represented by NaN. + +See also +-------- +astropy.wcs.Wcsprm.mjdend +""" + +mjdref = """ +``double`` Modified Julian Date corresponding to ``DATE-REF``. + +``(MJD = JD - 2400000.5)``. + +An undefined value is represented by NaN. + +See also +-------- +astropy.wcs.Wcsprm.dateref +""" + +bepoch = """ +``double`` Equivalent to ``DATE-OBS``. + +Expressed as a Besselian epoch. + +See also +-------- +astropy.wcs.Wcsprm.dateobs +""" + +jepoch = """ +``double`` Equivalent to ``DATE-OBS``. + +Expressed as a Julian epoch. + +See also +-------- +astropy.wcs.Wcsprm.dateobs +""" + +datebeg = """ +``string`` Date at the start of the observation. + +In ISO format, ``yyyy-mm-ddThh:mm:ss``. + +See also +-------- +astropy.wcs.Wcsprm.datebeg +""" + +dateend = """ +``string`` Date at the end of the observation. + +In ISO format, ``yyyy-mm-ddThh:mm:ss``. + +See also +-------- +astropy.wcs.Wcsprm.dateend +""" + +dateref = """ +``string`` Date of a reference epoch relative to which +other time measurements refer. + +See also +-------- +astropy.wcs.Wcsprm.dateref +""" + +timesys = """ +``string`` Time scale (UTC, TAI, etc.) in which all other time-related +auxiliary header values are recorded. Also defines the time scale for +an image axis with CTYPEia set to 'TIME'. + +See also +-------- +astropy.wcs.Wcsprm.timesys +""" + +trefpos = """ +``string`` Location in space where the recorded time is valid. + +See also +-------- +astropy.wcs.Wcsprm.trefpos +""" + +trefdir = """ +``string`` Reference direction used in calculating a pathlength delay. + +See also +-------- +astropy.wcs.Wcsprm.trefdir +""" + +timeunit = """ +``string`` Time units in which the following header values are expressed: +``TSTART``, ``TSTOP``, ``TIMEOFFS``, ``TIMSYER``, ``TIMRDER``, ``TIMEDEL``. + +It also provides the default value for ``CUNITia`` for time axes. + +See also +-------- +astropy.wcs.Wcsprm.trefdir +""" + +plephem = """ +``string`` The Solar System ephemeris used for calculating a pathlength delay. + +See also +-------- +astropy.wcs.Wcsprm.plephem +""" + +tstart = """ +``double`` equivalent to DATE-BEG expressed as a time in units of TIMEUNIT relative to DATEREF+TIMEOFFS. + +See also +-------- +astropy.wcs.Wcsprm.tstop +""" + +tstop = """ +``double`` equivalent to DATE-END expressed as a time in units of TIMEUNIT relative to DATEREF+TIMEOFFS. + +See also +-------- +astropy.wcs.Wcsprm.tstart +""" + +telapse = """ +``double`` equivalent to the elapsed time between DATE-BEG and DATE-END, in units of TIMEUNIT. + +See also +-------- +astropy.wcs.Wcsprm.tstart +""" + +timeoffs = """ +``double`` Time offset, which may be used, for example, to provide a uniform clock correction + for times referenced to DATEREF. + +See also +-------- +astropy.wcs.Wcsprm.timeoffs +""" + +timsyer = """ +``double`` the absolute error of the time values, in units of TIMEUNIT. + +See also +-------- +astropy.wcs.Wcsprm.timrder +""" + +timrder = """ +``double`` the accuracy of time stamps relative to each other, in units of TIMEUNIT. + +See also +-------- +astropy.wcs.Wcsprm.timsyer +""" + +timedel = """ +``double`` the resolution of the time stamps. + +See also +-------- +astropy.wcs.Wcsprm.timedel +""" + +timepixr = """ +``double`` relative position of the time stamps in binned time intervals, a value between 0.0 and 1.0. + +See also +-------- +astropy.wcs.Wcsprm.timepixr +""" + +obsorbit = """ +``string`` URI, URL, or name of an orbit ephemeris file giving spacecraft coordinates relating to TREFPOS. + +See also +-------- +astropy.wcs.Wcsprm.trefpos + +""" +xposure = """ +``double`` effective exposure time in units of TIMEUNIT. + +See also +-------- +astropy.wcs.Wcsprm.timeunit +""" + +czphs = """ +``double array[naxis]`` The time at the zero point of a phase axis, ``CSPHSia``. + +An undefined value is represented by NaN. +""" + +cperi = """ +``double array[naxis]`` period of a phase axis, CPERIia. + +An undefined value is represented by NaN. +""" diff --git a/astropy/wcs/include/astropy_wcs.h b/astropy/wcs/include/astropy_wcs/astropy_wcs.h similarity index 100% rename from astropy/wcs/include/astropy_wcs.h rename to astropy/wcs/include/astropy_wcs/astropy_wcs.h diff --git a/astropy/wcs/include/astropy_wcs/astropy_wcs_api.h b/astropy/wcs/include/astropy_wcs/astropy_wcs_api.h new file mode 100644 index 000000000000..5f0d73aa7644 --- /dev/null +++ b/astropy/wcs/include/astropy_wcs/astropy_wcs_api.h @@ -0,0 +1,114 @@ +#ifndef ASTROPY_WCS_API_H +#define ASTROPY_WCS_API_H + +#include "wcsconfig.h" +#include "pyutil.h" +#include "distortion.h" +#include "pipeline.h" +#include "sip.h" +#include "wcs.h" +#include "wcsprintf.h" + +/* +HOW TO UPDATE THE PUBLIC API + +This code uses a table of function pointers to dynamically expose the +public API to other code that wants to use astropy.wcs from C. + +Each function should be: + + 1) Declared, as usual for C, in a .h file + + 2) Defined in a .c file that is compiled as part of the _wcs.so file + + 3) Have a macro that maps the function name to a position in the + function table. That macro should go in this file + (astropy_wcs_api.h) + + 4) An entry in the function table, which lives in astropy_wcs_api.c + +Every time the function signatures change, or functions are added or +removed from the table, the value of REVISION should be incremented. +This allows for a rudimentary version check upon dynamic linking to +the astropy._wcs module. + */ + +#define REVISION 4 + +#ifdef ASTROPY_WCS_BUILD + +int _setup_api(PyObject* m); + +#else + +#if defined(NO_IMPORT_ASTROPY_WCS_API) +extern void** AstropyWcs_API; +#else +void** AstropyWcs_API; +#endif /* defined(NO_IMPORT_ASTROPY_PYWCS_API) */ + +/* Function macros that delegate to a function pointer in the AstropyWcs_API table */ +#define AstropyWcs_GetCVersion (*(int (*)(void)) AstropyWcs_API[0]) +#define wcsprm_python2c (*(void (*)(struct wcsprm*)) AstropyWcs_API[1]) +#define wcsprm_c2python (*(void (*)(struct wcsprm*)) AstropyWcs_API[2]) +#define distortion_lookup_t_init (*(int (*)(distortion_lookup_t* lookup)) AstropyWcs_API[3]) +#define distortion_lookup_t_free (*(void (*)(distortion_lookup_t* lookup)) AstropyWcs_API[4]) +#define get_distortion_offset (*(double (*)(const distortion_lookup_t*, const double* const)) AstropyWcs_API[5]) +#define p4_pix2foc (*(int (*)(const unsigned int, const distortion_lookup_t**, const unsigned int, const double *, double *)) AstropyWcs_API[6]) +#define p4_pix2deltas (*(int (*)(const unsigned int, const distortion_lookup_t**, const unsigned int, const double *, double *)) AstropyWcs_API[7]) +#define sip_clear (*(void (*)(sip_t*) AstropyWcs_API[8])) +#define sip_init (*(int (*)(sip_t*, unsigned int, double*, unsigned int, double*, unsigned int, double*, unsigned int, double*, double*)) AstropyWcs_API[9]) +#define sip_free (*(void (*)(sip_t*) AstropyWcs_API[10])) +#define sip_pix2foc (*(int (*)(sip_t*, unsigned int, unsigned int, double*, double*)) AstropyWcs_API[11]) +#define sip_pix2deltas (*(int (*)(sip_t*, unsigned int, unsigned int, double*, double*)) AstropyWcs_API[12]) +#define sip_foc2pix (*(int (*)(sip_t*, unsigned int, unsigned int, double*, double*)) AstropyWcs_API[13]) +#define sip_foc2deltas (*(int (*)(sip_t*, unsigned int, unsigned int, double*, double*)) AstropyWcs_API[14]) +#define pipeline_clear (*(void (*)(pipeline_t*)) AstropyWcs_API[15]) +#define pipeline_init (*(void (*)(pipeline_t*, sip_t*, distortion_lookup_t**, struct wcsprm*)) AstropyWcs_API[16]) +#define pipeline_free (*(void (*)(pipeline_t*)) AstropyWcs_API[17]) +#define pipeline_all_pixel2world (*(int (*)(pipeline_t*, unsigned int, unsigned int, double*, double*)) AstropyWcs_API[18]) +#define pipeline_pix2foc (*(int (*)(pipeline_t*, unsigned int, unsigned int, double*, double*)) AstropyWcs_API[19]) +#define wcsp2s (*(int (*)(struct wcsprm *, int, int, const double[], double[], double[], double[], double[], int[])) AstropyWcs_API[20]) +#define wcss2p (*(int (*)(struct wcsprm *, int, int, const double[], double[], double[], double[], double[], int[])) AstropyWcs_API[21]) +#define wcsprt (*(int (*)(struct wcsprm *)) AstropyWcs_API[22]) +#define wcslib_get_error_message (*(const char* (*)(int)) AstropyWcs_API[23]) +#define wcsprintf_buf (*(const char * (*)()) AstropyWcs_API[24]) + +#ifndef NO_IMPORT_ASTROPY_WCS_API +int +import_astropy_wcs(void) { + PyObject *wcs_module = NULL; + PyObject *c_api = NULL; + int status = -1; + + wcs_module = PyImport_ImportModule("astropy.wcs._wcs"); + if (wcs_module == NULL) goto exit; + + c_api = PyObject_GetAttrString(wcs_module, "_ASTROPY_WCS_API"); + if (c_api == NULL) goto exit; + + AstropyWcs_API = (void **)PyCapsule_GetPointer(c_api, "_wcs._ASTROPY_WCS_API"); + if (AstropyWcs_API == NULL) + goto exit; + + /* Perform runtime check of C API version */ + if (REVISION != AstropyWcs_GetCVersion()) { + PyErr_Format( + PyExc_ImportError, "module compiled against " \ + "ABI version '%x' but this version of astropy.wcs is '%x'", \ + (int)REVISION, (int)AstropyWcs_GetCVersion()); + return -1; + } + + exit: + Py_XDECREF(wcs_module); + Py_XDECREF(c_api); + + return status; +} + +#endif /* !defined(NO_IMPORT_ASTROPY_WCS_API) */ + +#endif /* ASTROPY_WCS_BUILD */ + +#endif /* ASTROPY_WCS_API_H */ diff --git a/astropy/wcs/include/distortion.h b/astropy/wcs/include/astropy_wcs/distortion.h similarity index 92% rename from astropy/wcs/include/distortion.h rename to astropy/wcs/include/astropy_wcs/distortion.h index 7ec5f3cfa014..edd4979232e1 100644 --- a/astropy/wcs/include/distortion.h +++ b/astropy/wcs/include/astropy_wcs/distortion.h @@ -59,7 +59,7 @@ get_distortion_offset( const double * const img /* [NAXES] */); /** -Perform just the distortion table part of Paper IV. +Perform just the distortion table part of the FITS WCS distortion paper. @param naxes @@ -82,8 +82,8 @@ p4_pix2foc( double *foc /* [NAXES][nelem] */); /** -Perform just the distortion table part of Paper IV, by adding -distortion to the values already in place in foc. +Perform just the distortion table part of the FITS WCS distortion paper, by +adding distortion to the values already in place in foc. @param naxes diff --git a/astropy/wcs/include/distortion_wrap.h b/astropy/wcs/include/astropy_wcs/distortion_wrap.h similarity index 90% rename from astropy/wcs/include/distortion_wrap.h rename to astropy/wcs/include/astropy_wcs/distortion_wrap.h index 2fce3b52bcbf..f1779e185f7e 100644 --- a/astropy/wcs/include/distortion_wrap.h +++ b/astropy/wcs/include/astropy_wcs/distortion_wrap.h @@ -9,7 +9,7 @@ #include "pyutil.h" #include "distortion.h" -extern PyTypeObject PyDistLookupType; +extern PyObject* PyDistLookupType; typedef struct { PyObject_HEAD diff --git a/astropy/wcs/include/isnan.h b/astropy/wcs/include/astropy_wcs/isnan.h similarity index 100% rename from astropy/wcs/include/isnan.h rename to astropy/wcs/include/astropy_wcs/isnan.h diff --git a/astropy/wcs/include/pipeline.h b/astropy/wcs/include/astropy_wcs/pipeline.h similarity index 95% rename from astropy/wcs/include/pipeline.h rename to astropy/wcs/include/astropy_wcs/pipeline.h index 3293af53f70d..8defb98c2ae7 100644 --- a/astropy/wcs/include/pipeline.h +++ b/astropy/wcs/include/astropy_wcs/pipeline.h @@ -52,7 +52,7 @@ coordinates, in the following order: - SIP distortion correction (optionally) - - Paper IV distortion correction (optionally) + - FITS WCS distortion paper correction (optionally) - wcslib WCS transformation @@ -82,7 +82,7 @@ coordinates to focal plane coordinates. - SIP distortion correction (optionally) - - Paper IV distortion correction (optionally) + - FITS WCS distortion paper correction (optionally) @param ncoord: diff --git a/astropy/wcs/include/pyutil.h b/astropy/wcs/include/astropy_wcs/pyutil.h similarity index 82% rename from astropy/wcs/include/pyutil.h rename to astropy/wcs/include/astropy_wcs/pyutil.h index 24e2a6ac3cf1..9122a3fbcfd5 100644 --- a/astropy/wcs/include/pyutil.h +++ b/astropy/wcs/include/astropy_wcs/pyutil.h @@ -6,24 +6,15 @@ #ifndef __PYUTIL_H__ #define __PYUTIL_H__ +#include #include "util.h" #define PY_ARRAY_UNIQUE_SYMBOL astropy_wcs_numpy_api -#include - +#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION #include #include -#if PY_MAJOR_VERSION >= 3 -#define PY3K 1 -#else -#define PY3K 0 -#ifndef Py_TYPE - #define Py_TYPE(ob) (((PyObject*)(ob))->ob_type) -#endif -#endif - PyObject* PyArrayProxy_New( PyObject* self, @@ -40,15 +31,12 @@ PyArrayReadOnlyProxy_New( int typenum, const void* data); -typedef int (*str_verify_fn)(char *); - /*@null@*/ PyObject * PyStrListProxy_New( PyObject* owner, Py_ssize_t size, Py_ssize_t maxsize, - char (*array)[72], - str_verify_fn verify + char (*array)[72] ); int @@ -144,6 +132,7 @@ extern PyObject* WcsExc_InvalidSubimageSpecification; extern PyObject* WcsExc_NonseparableSubimageCoordinateSystem; extern PyObject* WcsExc_NoWcsKeywordsFound; extern PyObject* WcsExc_InvalidTabularParameters; +extern PyObject* WcsExc_InvalidPrjParameters; /* This is an array mapping the wcs status codes to Python exception * types. The exception string is stored as part of wcslib itself in @@ -166,10 +155,10 @@ void wcs_to_python_exc(const struct wcsprm *wcs); void -wcserr_fix_to_python_exc(const struct wcserr *err); +wcshdr_err_to_python_exc(int status, const struct wcsprm *wcs); void -wcserr_units_to_python_exc(const struct wcserr *err); +wcserr_fix_to_python_exc(const struct wcserr *err); /*************************************************************************** Property helpers @@ -191,12 +180,7 @@ static INLINE PyObject* get_string( /*@unused@*/ const char* propname, const char* value) { - - #if PY3K - return PyBytes_FromString(value); - #else - return PyString_FromString(value); - #endif + return PyUnicode_FromString(value); } int @@ -225,11 +209,7 @@ get_int( /*@unused@*/ const char* propname, long value) { - #if PY3K return PyLong_FromLong(value); - #else - return PyInt_FromLong(value); - #endif } int @@ -260,7 +240,7 @@ get_double_array( const npy_intp* dims, /*@shared@*/ PyObject* owner) { - return PyArrayProxy_New(owner, ndims, dims, PyArray_DOUBLE, value); + return PyArrayProxy_New(owner, ndims, dims, NPY_DOUBLE, value); } /*@null@*/ static INLINE PyObject* @@ -271,7 +251,7 @@ get_double_array_readonly( const npy_intp* dims, /*@shared@*/ PyObject* owner) { - return PyArrayReadOnlyProxy_New(owner, ndims, dims, PyArray_DOUBLE, value); + return PyArrayReadOnlyProxy_New(owner, ndims, dims, NPY_DOUBLE, value); } int @@ -290,7 +270,7 @@ get_int_array( const npy_intp* dims, /*@shared@*/ PyObject* owner) { - return PyArrayProxy_New(owner, ndims, dims, PyArray_INT, value); + return PyArrayProxy_New(owner, ndims, dims, NPY_INT, value); } int @@ -301,18 +281,6 @@ set_int_array( const npy_intp* dims, int* dest); -static INLINE PyObject* -get_str_list_verified( - /*@unused@*/ const char* propname, - char (*array)[72], - Py_ssize_t len, - Py_ssize_t maxlen, - PyObject* owner, - str_verify_fn verify) { - - return PyStrListProxy_New(owner, len, maxlen, array, verify); -} - static INLINE PyObject* get_str_list( /*@unused@*/ const char* propname, @@ -321,28 +289,16 @@ get_str_list( Py_ssize_t maxlen, PyObject* owner) { - return get_str_list_verified(propname, array, len, maxlen, owner, NULL); + return PyStrListProxy_New(owner, len, maxlen, array); } int -set_str_list_verified( - const char* propname, - PyObject* value, - Py_ssize_t len, - Py_ssize_t maxlen, - char (*dest)[72], - str_verify_fn verify); - -static INLINE int set_str_list( const char* propname, PyObject* value, Py_ssize_t len, Py_ssize_t maxlen, - char (*dest)[72]) { - - return set_str_list_verified(propname, value, len, maxlen, dest, NULL); -} + char (*dest)[72]); PyObject* get_pscards( diff --git a/astropy/wcs/include/sip.h b/astropy/wcs/include/astropy_wcs/sip.h similarity index 100% rename from astropy/wcs/include/sip.h rename to astropy/wcs/include/astropy_wcs/sip.h diff --git a/astropy/wcs/include/sip_wrap.h b/astropy/wcs/include/astropy_wcs/sip_wrap.h similarity index 88% rename from astropy/wcs/include/sip_wrap.h rename to astropy/wcs/include/astropy_wcs/sip_wrap.h index 4eee5349b0ab..89211b869cf6 100644 --- a/astropy/wcs/include/sip_wrap.h +++ b/astropy/wcs/include/astropy_wcs/sip_wrap.h @@ -9,7 +9,7 @@ #include "pyutil.h" #include "sip.h" -extern PyTypeObject PySipType; +extern PyObject* PySipType; typedef struct { PyObject_HEAD diff --git a/astropy/wcs/include/str_list_proxy.h b/astropy/wcs/include/astropy_wcs/str_list_proxy.h similarity index 86% rename from astropy/wcs/include/str_list_proxy.h rename to astropy/wcs/include/astropy_wcs/str_list_proxy.h index 6e1def1500df..b6462656a764 100644 --- a/astropy/wcs/include/str_list_proxy.h +++ b/astropy/wcs/include/astropy_wcs/str_list_proxy.h @@ -22,10 +22,15 @@ PyStrListProxy_New( PyObject* owner, Py_ssize_t size, Py_ssize_t maxsize, - char (*array)[72], - str_verify_fn verify + char (*array)[72] ); +/*@null@*/ PyObject* +str_list_proxy_repr( + char (*array)[72], + Py_ssize_t size, + Py_ssize_t maxsize); + int _setup_str_list_proxy_type( PyObject* m); diff --git a/astropy/wcs/include/astropy_wcs/unit_list_proxy.h b/astropy/wcs/include/astropy_wcs/unit_list_proxy.h new file mode 100644 index 000000000000..c3efd53073d1 --- /dev/null +++ b/astropy/wcs/include/astropy_wcs/unit_list_proxy.h @@ -0,0 +1,49 @@ +/* + Author: Michael Droettboom + mdroe@stsci.edu +*/ + +#ifndef __UNIT_LIST_PROXY_H__ +#define __UNIT_LIST_PROXY_H__ + +#include "pyutil.h" + +/*************************************************************************** + * List-of-units proxy object + * + * A Python object that looks like a list of units, but is back by a C + * char * list[]; + ***************************************************************************/ + +/*@null@*/ PyObject * +PyUnitListProxy_New( + PyObject* owner, + Py_ssize_t size, + char (*array)[72], + int readonly + ); + +int +_setup_unit_list_proxy_type( + PyObject* m); + +static INLINE PyObject* +get_unit_list( + /*@unused@*/ const char* propname, + char (*array)[72], + Py_ssize_t len, + PyObject* owner, + int readonly) { + + return PyUnitListProxy_New(owner, len, array, readonly); +} + +int +set_unit_list( + PyObject *owner, + const char* propname, + PyObject* value, + Py_ssize_t len, + char (*dest)[72]); + +#endif /* __UNIT_LIST_PROXY_H__ */ diff --git a/astropy/wcs/include/util.h b/astropy/wcs/include/astropy_wcs/util.h similarity index 100% rename from astropy/wcs/include/util.h rename to astropy/wcs/include/astropy_wcs/util.h diff --git a/astropy/wcs/include/astropy_wcs/wcslib_auxprm_wrap.h b/astropy/wcs/include/astropy_wcs/wcslib_auxprm_wrap.h new file mode 100644 index 000000000000..ddff30452779 --- /dev/null +++ b/astropy/wcs/include/astropy_wcs/wcslib_auxprm_wrap.h @@ -0,0 +1,20 @@ +#ifndef __WCSLIB_AUXPRM_WRAP_H__ +#define __WCSLIB_AUXPRM_WRAP_H__ + +#include "pyutil.h" +#include "wcs.h" + +extern PyObject* PyAuxprmType; + +typedef struct { + PyObject_HEAD + struct auxprm* x; + PyObject* owner; +} PyAuxprm; + +PyAuxprm* +PyAuxprm_cnew(PyObject* wcsprm, struct auxprm* x); + +int _setup_auxprm_type(PyObject* m); + +#endif diff --git a/astropy/wcs/include/astropy_wcs/wcslib_celprm_wrap.h b/astropy/wcs/include/astropy_wcs/wcslib_celprm_wrap.h new file mode 100644 index 000000000000..61a9777afc3c --- /dev/null +++ b/astropy/wcs/include/astropy_wcs/wcslib_celprm_wrap.h @@ -0,0 +1,20 @@ +#ifndef __WCSLIB_CELPRM_WRAP_H__ +#define __WCSLIB_CELPRM_WRAP_H__ + +#include "pyutil.h" +#include "wcs.h" + +extern PyObject* PyCelprmType; + +typedef struct { + PyObject_HEAD + struct celprm* x; + int* prefcount; + PyObject* owner; +} PyCelprm; + +PyCelprm* PyCelprm_cnew(PyObject* wcsprm_obj, struct celprm* x, int* prefcount); + +int _setup_celprm_type(PyObject* m); + +#endif diff --git a/astropy/wcs/include/astropy_wcs/wcslib_prjprm_wrap.h b/astropy/wcs/include/astropy_wcs/wcslib_prjprm_wrap.h new file mode 100644 index 000000000000..4d74148c58c7 --- /dev/null +++ b/astropy/wcs/include/astropy_wcs/wcslib_prjprm_wrap.h @@ -0,0 +1,20 @@ +#ifndef __WCSLIB_PRJPRM_WRAP_H__ +#define __WCSLIB_PRJPRM_WRAP_H__ + +#include "pyutil.h" +#include "wcs.h" + +extern PyObject* PyPrjprmType; + +typedef struct { + PyObject_HEAD + struct prjprm* x; + int* prefcount; + PyObject* owner; +} PyPrjprm; + +PyPrjprm* PyPrjprm_cnew(PyObject* celprm, struct prjprm* x, int* prefcount); + +int _setup_prjprm_type(PyObject* m); + +#endif diff --git a/astropy/wcs/include/wcslib_tabprm_wrap.h b/astropy/wcs/include/astropy_wcs/wcslib_tabprm_wrap.h similarity index 91% rename from astropy/wcs/include/wcslib_tabprm_wrap.h rename to astropy/wcs/include/astropy_wcs/wcslib_tabprm_wrap.h index 4a9fd36531ad..37b0f9a500b2 100644 --- a/astropy/wcs/include/wcslib_tabprm_wrap.h +++ b/astropy/wcs/include/astropy_wcs/wcslib_tabprm_wrap.h @@ -9,7 +9,7 @@ #include "pyutil.h" #include "wcs.h" -extern PyTypeObject PyTabprmType; +extern PyObject* PyTabprmType; typedef struct { PyObject_HEAD diff --git a/astropy/wcs/include/wcslib_units_wrap.h b/astropy/wcs/include/astropy_wcs/wcslib_units_wrap.h similarity index 100% rename from astropy/wcs/include/wcslib_units_wrap.h rename to astropy/wcs/include/astropy_wcs/wcslib_units_wrap.h diff --git a/astropy/wcs/include/astropy_wcs/wcslib_wrap.h b/astropy/wcs/include/astropy_wcs/wcslib_wrap.h new file mode 100644 index 000000000000..b28be09680e4 --- /dev/null +++ b/astropy/wcs/include/astropy_wcs/wcslib_wrap.h @@ -0,0 +1,47 @@ +/* + Author: Michael Droettboom +*/ + +#ifndef __WCSLIB_WRAP_H__ +#define __WCSLIB_WRAP_H__ + +#include "pyutil.h" + +extern PyObject* PyWcsprmType; + +typedef struct { + + PyObject_HEAD + struct wcsprm x; + + // WCSLIB converts units to SI (for example nanometers for a specrtal axis to + // meters, or arcseconds for a celestial frame to degrees). There is no easy + // way to turn off this behavior in WCSLIB itself, but since we expose WCSLIB + // through a wrapper layer, we can optionally do conversions on-the-fly to + // make it look to the user as if the units are the original ones. This is + // controlled via the preserve_units option which can be 0 (default WCSLIB + // behavior) or 1 (preserve original units). The original_cunit array is used + // to store the original CUNIT values, while the unit_scaling array contains + // the multiplicative scaling required to convert the units from the original + // units (e.g. arcsec) to the internal WCSLIB units (e.g. deg) + int preserve_units; + char (*original_cunit)[72]; + double *unit_scaling; + +} PyWcsprm; + +int _setup_wcsprm_type(PyObject* m); + +PyObject* +PyWcsprm_find_all_wcs( + PyObject* self, + PyObject* args, + PyObject* kwds); + +int _update_wtbarr_from_hdulist(PyObject *hdulist, struct wtbarr *wtb); + +void _set_wtbarr_callback(PyObject* callback); + +int PyWcsprm_cset(PyWcsprm* self, const int convert); + +#endif diff --git a/astropy/wcs/include/wcslib_wtbarr_wrap.h b/astropy/wcs/include/astropy_wcs/wcslib_wtbarr_wrap.h similarity index 91% rename from astropy/wcs/include/wcslib_wtbarr_wrap.h rename to astropy/wcs/include/astropy_wcs/wcslib_wtbarr_wrap.h index 0eac2d204ba5..b3521c3a7ef3 100644 --- a/astropy/wcs/include/wcslib_wtbarr_wrap.h +++ b/astropy/wcs/include/astropy_wcs/wcslib_wtbarr_wrap.h @@ -9,7 +9,7 @@ #include "pyutil.h" #include "wcs.h" -extern PyTypeObject PyWtbarrType; +extern PyObject* PyWtbarrType; typedef struct { PyObject_HEAD diff --git a/astropy/wcs/include/astropy_wcs_api.h b/astropy/wcs/include/astropy_wcs_api.h index 8e9d30eb198b..670831f7ef84 100644 --- a/astropy/wcs/include/astropy_wcs_api.h +++ b/astropy/wcs/include/astropy_wcs_api.h @@ -1,121 +1 @@ -#ifndef ASTROPY_WCS_API_H -#define ASTROPY_WCS_API_H - -#include "wcsconfig.h" -#include "pyutil.h" -#include "distortion.h" -#include "pipeline.h" -#include "sip.h" -#include "wcs.h" -#include "wcsprintf.h" - -/* -HOW TO UPDATE THE PUBLIC API - -This code uses a table of function pointers to dynamically expose the -public API to other code that wants to use astropy.wcs from C. - -Each function should be: - - 1) Declared, as usual for C, in a .h file - - 2) Defined in a .c file that is compiled as part of the _wcs.so file - - 3) Have a macro that maps the function name to a position in the - function table. That macro should go in this file - (astropy_wcs_api.h) - - 4) An entry in the function table, which lives in astropy_wcs_api.c - -Every time the function signatures change, or functions are added or -removed from the table, the value of REVISION should be incremented. -This allows for a rudimentary version check upon dynamic linking to -the astropy._wcs module. - */ - -#define REVISION 4 - -#ifdef ASTROPY_WCS_BUILD - -int _setup_api(PyObject* m); - -#else - -#if defined(NO_IMPORT_ASTROPY_WCS_API) -extern void** AstropyWcs_API; -#else -void** AstropyWcs_API; -#endif /* defined(NO_IMPORT_ASTROPY_PYWCS_API) */ - -/* Function macros that delegate to a function pointer in the AstropyWcs_API table */ -#define AstropyWcs_GetCVersion (*(int (*)(void)) AstropyWcs_API[0]) -#define wcsprm_python2c (*(void (*)(struct wcsprm*)) AstropyWcs_API[1]) -#define wcsprm_c2python (*(void (*)(struct wcsprm*)) AstropyWcs_API[2]) -#define distortion_lookup_t_init (*(int (*)(distortion_lookup_t* lookup)) AstropyWcs_API[3]) -#define distortion_lookup_t_free (*(void (*)(distortion_lookup_t* lookup)) AstropyWcs_API[4]) -#define get_distortion_offset (*(double (*)(const distortion_lookup_t*, const double* const)) AstropyWcs_API[5]) -#define p4_pix2foc (*(int (*)(const unsigned int, const distortion_lookup_t**, const unsigned int, const double *, double *)) AstropyWcs_API[6]) -#define p4_pix2deltas (*(int (*)(const unsigned int, const distortion_lookup_t**, const unsigned int, const double *, double *)) AstropyWcs_API[7]) -#define sip_clear (*(void (*)(sip_t*) AstropyWcs_API[8])) -#define sip_init (*(int (*)(sip_t*, unsigned int, double*, unsigned int, double*, unsigned int, double*, unsigned int, double*, double*)) AstropyWcs_API[9]) -#define sip_free (*(void (*)(sip_t*) AstropyWcs_API[10])) -#define sip_pix2foc (*(int (*)(sip_t*, unsigned int, unsigned int, double*, double*)) AstropyWcs_API[11]) -#define sip_pix2deltas (*(int (*)(sip_t*, unsigned int, unsigned int, double*, double*)) AstropyWcs_API[12]) -#define sip_foc2pix (*(int (*)(sip_t*, unsigned int, unsigned int, double*, double*)) AstropyWcs_API[13]) -#define sip_foc2deltas (*(int (*)(sip_t*, unsigned int, unsigned int, double*, double*)) AstropyWcs_API[14]) -#define pipeline_clear (*(void (*)(pipeline_t*)) AstropyWcs_API[15]) -#define pipeline_init (*(void (*)(pipeline_t*, sip_t*, distortion_lookup_t**, struct wcsprm*)) AstropyWcs_API[16]) -#define pipeline_free (*(void (*)(pipeline_t*)) AstropyWcs_API[17]) -#define pipeline_all_pixel2world (*(int (*)(pipeline_t*, unsigned int, unsigned int, double*, double*)) AstropyWcs_API[18]) -#define pipeline_pix2foc (*(int (*)(pipeline_t*, unsigned int, unsigned int, double*, double*)) AstropyWcs_API[19]) -#define wcsp2s (*(int (*)(struct wcsprm *, int, int, const double[], double[], double[], double[], double[], int[])) AstropyWcs_API[20]) -#define wcss2p (*(int (*)(struct wcsprm *, int, int, const double[], double[], double[], double[], double[], int[])) AstropyWcs_API[21]) -#define wcsprt (*(int (*)(struct wcsprm *)) AstropyWcs_API[22]) -#define wcslib_get_error_message (*(const char* (*)(int)) AstropyWcs_API[23]) -#define wcsprintf_buf (*(const char * (*)()) AstropyWcs_API[24]) - -#ifndef NO_IMPORT_ASTROPY_WCS_API -int -import_astropy_wcs(void) { - PyObject *wcs_module = NULL; - PyObject *c_api = NULL; - int status = -1; - - #if PY_VERSION_HEX >= 0x03020000 - AstropyWcs_API = (void **)PyCapsule_Import("astropy.wcs._wcs._ASTROPY_WCS_API", 0); - if (AstropyWcs_API == NULL) goto exit; - #else - pywcs_module = PyImport_ImportModule("astropy.wcs._wcs"); - if (pywcs_module == NULL) goto exit; - - c_api = PyObject_GetAttrString(pywcs_module, "_ASTROPY_WCS_API"); - if (c_api == NULL) goto exit; - - if (PyCObject_Check(c_api)) { - AstropyWcs_API = (void **)PyCObject_AsVoidPtr(c_api); - } else { - goto exit; - } - #endif - - /* Perform runtime check of C API version */ - if (REVISION != AstropyWcs_GetCVersion()) { - PyErr_Format( - PyExc_ImportError, "module compiled against " \ - "ABI version '%x' but this version of astropy.wcs is '%x'", \ - (int)REVISION, (int)PyWcs_GetCVersion()); - return -1; - } - - exit: - Py_XDECREF(wcs_module); - Py_XDECREF(c_api); - - return status; -} - -#endif /* !defined(NO_IMPORT_ASTROPY_WCS_API) */ - -#endif /* ASTROPY_WCS_BUILD */ - -#endif /* ASTROPY_WCS_API_H */ +#error "Since version 0.3, astropy.wcs public API should be imported as \"astropy_wcs/astropy_wcs_api.h" diff --git a/astropy/wcs/include/wcslib/.empty b/astropy/wcs/include/wcslib/.empty new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/astropy/wcs/include/wcslib/.gitignore b/astropy/wcs/include/wcslib/.gitignore new file mode 100644 index 000000000000..576ff4c7b621 --- /dev/null +++ b/astropy/wcs/include/wcslib/.gitignore @@ -0,0 +1,4 @@ +# We copy header files here from `cextern/wcslib/C`, but they should +# be ignored by git. + +*.h diff --git a/astropy/wcs/include/wcslib_wrap.h b/astropy/wcs/include/wcslib_wrap.h deleted file mode 100644 index 4746bc041b03..000000000000 --- a/astropy/wcs/include/wcslib_wrap.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - Author: Michael Droettboom - mdroe@stsci.edu -*/ - -#ifndef __WCSLIB_WRAP_H__ -#define __WCSLIB_WRAP_H__ - -#include "pyutil.h" - -extern PyTypeObject PyWcsprmType; - -typedef struct { - PyObject_HEAD - struct wcsprm x; -} PyWcsprm; - -int _setup_wcsprm_type(PyObject* m); - -PyObject* -PyWcsprm_find_all_wcs( - PyObject* self, - PyObject* args, - PyObject* kwds); - -#endif diff --git a/astropy/wcs/setup_package.py b/astropy/wcs/setup_package.py index c2e84bde1481..5efe5279867a 100644 --- a/astropy/wcs/setup_package.py +++ b/astropy/wcs/setup_package.py @@ -1,46 +1,39 @@ -from __future__ import division # confidence high +# Licensed under a 3-clause BSD style license - see LICENSE.rst -CONTACT = "Michael Droettboom" -EMAIL = "mdroe@stsci.edu" - -from distutils.core import Extension -from os.path import join +import io +import os import os.path +import shutil import sys +from collections import defaultdict +from os.path import join +from pathlib import Path -from astropy import setup_helpers +from numpy import get_include as get_numpy_include +from setuptools import Extension + +from extension_helpers import get_compiler, import_file, pkg_config, write_if_different WCSROOT = os.path.relpath(os.path.dirname(__file__)) -WCSVERSION = "4.10" +WCSVERSION = "8.6" def b(s): - return s.encode('ascii') - -if sys.version_info[0] >= 3: + return s.encode("ascii") - def string_escape(s): - s = s.decode('ascii').encode('ascii', 'backslashreplace') - s = s.replace(b('\n'), b('\\n')) - s = s.replace(b('\0'), b('\\0')) - return s.decode('ascii') - from io import StringIO - string_types = (str, bytes) -else: - - def string_escape(s): - return s.encode('string_escape') - - from cStringIO import StringIO - string_types = (str, unicode) +def string_escape(s): + s = s.decode("ascii").encode("ascii", "backslashreplace") + s = s.replace(b"\n", b"\\n") + s = s.replace(b"\0", b"\\0") + return s.decode("ascii") def determine_64_bit_int(): """ The only configuration parameter needed at compile-time is how to specify a 64-bit signed integer. Python's ctypes module can get us - that information, but it is only available in Python 2.5 or later. + that information. If we can't be absolutely certain, we default to "long long int", which is correct on most platforms (x86, x86_64). If we find platforms where this heuristic doesn't work, we may need to @@ -65,17 +58,21 @@ def determine_64_bit_int(): return "long long int" -def write_wcsconfig_h(): +def write_wcsconfig_h(paths): """ Writes out the wcsconfig.h header with local configuration. """ - h_file = StringIO() - h_file.write(""" + h_file = io.StringIO() + h_file.write( + f""" + /* The bundled version has WCSLIB_VERSION */ + #define HAVE_WCSLIB_VERSION 1 + /* WCSLIB library version number. */ - #define WCSLIB_VERSION {0} + #define WCSLIB_VERSION {WCSVERSION} /* 64-bit integer data type. */ - #define WCSLIB_INT64 {1} + #define WCSLIB_INT64 {determine_64_bit_int()} /* Windows needs some other defines to prevent inclusion of wcsset() which conflicts with wcslib's wcsset(). These need to be set @@ -104,28 +101,32 @@ def write_wcsconfig_h(): #endif #endif - """.format(WCSVERSION, determine_64_bit_int())) - setup_helpers.write_if_different( - join(WCSROOT, 'include', 'wcsconfig.h'), - h_file.getvalue().encode('ascii')) + """ + ) + content = h_file.getvalue().encode("ascii") + for path in paths: + write_if_different(path, content) + ###################################################################### # GENERATE DOCSTRINGS IN C def generate_c_docstrings(): - from astropy.wcs import docstrings + docstrings = import_file(os.path.join(WCSROOT, "docstrings.py")) docstrings = docstrings.__dict__ keys = [ - key for key in docstrings.keys() - if not key.startswith('__') and type(key) in string_types] + key for key, val in docstrings.items() + if not key.startswith("__") and isinstance(val, str) + ] # fmt: skip keys.sort() docs = {} for key in keys: - docs[key] = docstrings[key].encode('utf8').lstrip() + b'\0' + docs[key] = docstrings[key].encode("utf8").lstrip() + b"\0" - h_file = StringIO() - h_file.write("""/* + h_file = io.StringIO() + h_file.write( + """/* DO NOT EDIT! This file is autogenerated by astropy/wcs/setup_package.py. To edit @@ -135,22 +136,21 @@ def generate_c_docstrings(): #ifndef __DOCSTRINGS_H__ #define __DOCSTRINGS_H__ -#if defined(_MSC_VER) -void fill_docstrings(void); -#endif - -""") +""" + ) for key in keys: val = docs[key] - h_file.write('extern char doc_{0}[{1}];\n'.format(key, len(val))) + h_file.write(f"extern char doc_{key}[{len(val)}];\n") h_file.write("\n#endif\n\n") - setup_helpers.write_if_different( - join(WCSROOT, 'include', 'docstrings.h'), - h_file.getvalue().encode('utf-8')) + write_if_different( + join(WCSROOT, "include", "astropy_wcs", "docstrings.h"), + h_file.getvalue().encode("utf-8"), + ) - c_file = StringIO() - c_file.write("""/* + c_file = io.StringIO() + c_file.write( + """/* DO NOT EDIT! This file is autogenerated by astropy/wcs/setup_package.py. To edit @@ -161,163 +161,190 @@ def generate_c_docstrings(): */ #include -#include "docstrings.h" +#include "astropy_wcs/docstrings.h" -#if defined(_MSC_VER) -""") +""" + ) for key in keys: val = docs[key] - c_file.write('char doc_{0}[{1}];\n'.format(key, len(val))) - - c_file.write("\nvoid fill_docstrings(void)\n{\n") - for key in keys: - val = docs[key] - # For portability across various compilers, we need to fill the - # docstrings in 256-character chunks - for i in range(0, len(val), 256): - chunk = string_escape(val[i:i + 256]).replace('"', '\\"') - c_file.write(' strncpy(doc_{0} + {1}, "{2}", {3});\n'.format( - key, i, chunk, min(len(val) - i, 256))) - c_file.write("\n") - c_file.write("\n}\n\n") - - c_file.write("#else /* UNIX */\n") + c_file.write(f"char doc_{key}[{len(val)}] = {{\n") + for i in range(0, len(val), 12): + section = val[i : i + 12] + c_file.write(" ") + c_file.write("".join(f"0x{x:02x}, " for x in section)) + c_file.write("\n") + + c_file.write(" };\n\n") + + write_if_different( + join(WCSROOT, "src", "docstrings.c"), c_file.getvalue().encode("utf-8") + ) + + +def get_wcslib_cfg(cfg, wcslib_files, include_paths): + debug = "--debug" in sys.argv + + cfg["include_dirs"].append(get_numpy_include()) + cfg["define_macros"].extend( + [ + ("ECHO", None), + ("WCSTRIG_MACRO", None), + ("ASTROPY_WCS_BUILD", None), + ("_GNU_SOURCE", None), + ] + ) + + if ( + int(os.environ.get("ASTROPY_USE_SYSTEM_WCSLIB", "0")) + or int(os.environ.get("ASTROPY_USE_SYSTEM_ALL", "0")) + ) and not sys.platform == "win32": + wcsconfig_h_path = join(WCSROOT, "include", "wcsconfig.h") + if os.path.exists(wcsconfig_h_path): + os.unlink(wcsconfig_h_path) + for k, v in pkg_config(["wcslib"], ["wcs"]).items(): + cfg[k].extend(v) + else: + write_wcsconfig_h(include_paths) - for key in keys: - val = docs[key] - c_file.write('char doc_{0}[{1}] = "{2}";\n\n'.format( - key, len(val), string_escape(val).replace('"', '\\"'))) + wcslib_path = join("cextern", "wcslib") # Path to wcslib + wcslib_cpath = join(wcslib_path, "C") # Path to wcslib source files + cfg["sources"].extend(join(wcslib_cpath, x) for x in wcslib_files) + cfg["include_dirs"].append(wcslib_cpath) - c_file.write("#endif\n") + if debug: + cfg["define_macros"].append(("DEBUG", None)) + cfg["undef_macros"].append("NDEBUG") + if not sys.platform.startswith("sun") and not sys.platform == "win32": + cfg["extra_compile_args"].extend(["-fno-inline", "-O0", "-g"]) + else: + # Define ECHO as nothing to prevent spurious newlines from + # printing within the libwcs parser + cfg["define_macros"].append(("NDEBUG", None)) + cfg["undef_macros"].append("DEBUG") - setup_helpers.write_if_different( - join(WCSROOT, 'src', 'docstrings.c'), - c_file.getvalue().encode('utf-8')) + if sys.platform == "win32": + # These are written into wcsconfig.h, but that file is not + # used by all parts of wcslib. + cfg["define_macros"].extend( + [ + ("YY_NO_UNISTD_H", None), + ("_CRT_SECURE_NO_WARNINGS", None), + ("_NO_OLDNAMES", None), # for mingw32 + ("NO_OLDNAMES", None), # for mingw64 + ("__STDC__", None), # for MSVC + ] + ) + + if sys.platform.startswith("linux"): + cfg["define_macros"].append(("HAVE_SINCOS", None)) + + # For 4.7+ enable C99 syntax in older compilers (need 'gnu99' std for gcc) + if get_compiler() == "unix": + cfg["extra_compile_args"].extend(["-std=gnu99"]) + + # Squelch a few compilation warnings in WCSLIB + if get_compiler() in ("unix", "mingw32"): + if not debug: + cfg["extra_compile_args"].extend( + [ + "-Wno-strict-prototypes", + "-Wno-unused-function", + "-Wno-unused-value", + "-Wno-uninitialized", + ] + ) def get_extensions(): - from astropy.version import debug - generate_c_docstrings() ###################################################################### # DISTUTILS SETUP - source_files = [] - libraries = [] - include_dirs = [ - setup_helpers.get_numpy_include_path(), - join(WCSROOT, "include")] - - library_dirs = [] - define_macros = [ - ('ECHO', None), - ('WCSTRIG_MACRO', None), - ('ASTROPY_WCS_BUILD', None), - ('_GNU_SOURCE', None), - ('WCSVERSION', WCSVERSION)] - undef_macros = [] - extra_compile_args = [] - extra_link_args = [] - - if (not setup_helpers.use_system_library('wcslib') or - sys.platform == 'win32'): - write_wcsconfig_h() - - wcslib_path = join("cextern", "wcslib") # Path to wcslib - wcslib_cpath = join(wcslib_path, "C") # Path to wcslib source files - wcslib_files = [ # List of wcslib files to compile - 'flexed/wcsbth.c', - 'flexed/wcspih.c', - 'flexed/wcsulex.c', - 'flexed/wcsutrn.c', - 'cel.c', - 'lin.c', - 'log.c', - 'prj.c', - 'spc.c', - 'sph.c', - 'spx.c', - 'tab.c', - 'wcs.c', - 'wcserr.c', - 'wcsfix.c', - 'wcshdr.c', - 'wcsprintf.c', - 'wcsunits.c', - 'wcsutil.c'] - source_files.extend(join(wcslib_cpath, x) for x in wcslib_files) - include_dirs.append(wcslib_cpath) - else: - setup_helpers.pkg_config( - ['wcs'], ['wcs'], include_dirs, library_dirs, libraries) + cfg = defaultdict(list) + + wcslib_files = [ # List of wcslib files to compile + "flexed/wcsbth.c", + "flexed/wcspih.c", + "flexed/wcsulex.c", + "flexed/wcsutrn.c", + "cel.c", + "dis.c", + "lin.c", + "log.c", + "prj.c", + "spc.c", + "sph.c", + "spx.c", + "tab.c", + "wcs.c", + "wcserr.c", + "wcsfix.c", + "wcshdr.c", + "wcsprintf.c", + "wcsunits.c", + "wcsutil.c", + ] + + wcslib_config_paths = [ + join(WCSROOT, "include", "astropy_wcs", "wcsconfig.h"), + join(WCSROOT, "include", "wcsconfig.h"), + ] + + get_wcslib_cfg(cfg, wcslib_files, wcslib_config_paths) + + cfg["include_dirs"].append(join(WCSROOT, "include")) astropy_wcs_files = [ # List of astropy.wcs files to compile - 'distortion.c', - 'distortion_wrap.c', - 'docstrings.c', - 'pipeline.c', - 'pyutil.c', - 'astropy_wcs.c', - 'astropy_wcs_api.c', - 'sip.c', - 'sip_wrap.c', - 'str_list_proxy.c', - 'util.c', - 'wcslib_wrap.c', - 'wcslib_tabprm_wrap.c', - 'wcslib_units_wrap.c', - 'wcslib_wtbarr_wrap.c'] - source_files.extend(join(WCSROOT, 'src', x) for x in astropy_wcs_files) - - if debug: - define_macros.append(('DEBUG', None)) - undef_macros.append('NDEBUG') - if not sys.platform.startswith('sun') and \ - not sys.platform == 'win32': - extra_compile_args.extend(["-fno-inline", "-O0", "-g"]) - else: - # Define ECHO as nothing to prevent spurious newlines from - # printing within the libwcs parser - define_macros.append(('NDEBUG', None)) - undef_macros.append('DEBUG') - - if sys.platform == 'win32': - # These are written into wcsconfig.h, but that file is not - # used by all parts of wcslib. - define_macros.extend([ - ('YY_NO_UNISTD_H', None), - ('_CRT_SECURE_NO_WARNINGS', None), - ('_NO_OLDNAMES', None), # for mingw32 - ('NO_OLDNAMES', None), # for mingw64 - ('__STDC__', None) # for MSVC - ]) - - if sys.platform.startswith('linux'): - define_macros.append(('HAVE_SINCOS', None)) - - return [ - Extension('astropy.wcs._wcs', - source_files, - include_dirs=include_dirs, - define_macros=define_macros, - undef_macros=undef_macros, - extra_compile_args=extra_compile_args, - extra_link_args=extra_link_args, - libraries=libraries, - library_dirs=library_dirs)] - - -def get_package_data(): - # Installs the testing data files - return { - 'astropy.wcs.tests': ['data/*.hdr', 'data/*.fits', - 'maps/*.hdr', 'spectra/*.hdr'], - 'astropy.wcs': ['include/*.h']} - - -def get_legacy_alias(): - return setup_helpers.add_legacy_alias('pywcs', 'astropy.wcs', '1.11') - - -def get_external_libraries(): - return ['wcslib'] + "distortion.c", + "distortion_wrap.c", + "docstrings.c", + "pipeline.c", + "pyutil.c", + "astropy_wcs.c", + "astropy_wcs_api.c", + "sip.c", + "sip_wrap.c", + "str_list_proxy.c", + "unit_list_proxy.c", + "util.c", + "wcslib_wrap.c", + "wcslib_auxprm_wrap.c", + "wcslib_prjprm_wrap.c", + "wcslib_celprm_wrap.c", + "wcslib_tabprm_wrap.c", + "wcslib_wtbarr_wrap.c", + ] + cfg["sources"].extend(join(WCSROOT, "src", x) for x in astropy_wcs_files) + + cfg["sources"] = [str(x) for x in cfg["sources"]] + cfg = {str(key): val for key, val in cfg.items()} + + # Copy over header files from WCSLIB into the installed version of Astropy + # so that other Python packages can write extensions that link to it. We + # do the copying here then include the data in [tools.setuptools.package_data] + # in the pyproject.toml file + + wcslib_headers = [ + "cel.h", + "lin.h", + "prj.h", + "spc.h", + "spx.h", + "tab.h", + "wcs.h", + "wcserr.h", + "wcsmath.h", + "wcsprintf.h", + ] + + if not ( + int(os.environ.get("ASTROPY_USE_SYSTEM_WCSLIB", "0")) + or int(os.environ.get("ASTROPY_USE_SYSTEM_ALL", "0")) + ): + for header in wcslib_headers: + source = Path("cextern", "wcslib", "C", header) + dest = Path("astropy", "wcs", "include", "wcslib", header) + if not dest.is_file() or source.stat().st_mtime > dest.stat().st_mtime: + shutil.copy(source, dest) + + return [Extension("astropy.wcs._wcs", **cfg)] diff --git a/astropy/wcs/src/astropy_wcs.c b/astropy/wcs/src/astropy_wcs.c index b50344183af8..e98e9b7c4172 100644 --- a/astropy/wcs/src/astropy_wcs.c +++ b/astropy/wcs/src/astropy_wcs.c @@ -3,30 +3,51 @@ mdroe@stsci.edu */ -#include "astropy_wcs.h" -#include "wcslib_wrap.h" -#include "wcslib_tabprm_wrap.h" -#include "wcslib_units_wrap.h" -#include "wcslib_wtbarr_wrap.h" -#include "distortion_wrap.h" -#include "sip_wrap.h" -#include "docstrings.h" -#include "astropy_wcs_api.h" +#include "astropy_wcs/astropy_wcs.h" +#include "astropy_wcs/wcslib_wrap.h" +#include "astropy_wcs/wcslib_tabprm_wrap.h" +#include "astropy_wcs/wcslib_auxprm_wrap.h" +#include "astropy_wcs/wcslib_prjprm_wrap.h" +#include "astropy_wcs/wcslib_celprm_wrap.h" +#include "astropy_wcs/wcslib_units_wrap.h" +#include "astropy_wcs/wcslib_wtbarr_wrap.h" +#include "astropy_wcs/distortion_wrap.h" +#include "astropy_wcs/sip_wrap.h" +#include "astropy_wcs/docstrings.h" +#include "astropy_wcs/astropy_wcs_api.h" +#include "astropy_wcs/unit_list_proxy.h" #include /* from Python */ #include #include +#include +#include + /*************************************************************************** * Wcs type ***************************************************************************/ -static PyTypeObject WcsType; - static int _setup_wcs_type(PyObject* m); +PyObject* PyWcsprm_set_wtbarr_fitsio_callback(PyObject *dummy, PyObject *args) { + PyObject *callback; + + if (PyArg_ParseTuple(args, "O:set_wtbarr_fitsio_callback", &callback)) { + if (!PyCallable_Check(callback)) { + PyErr_SetString(PyExc_TypeError, "parameter must be callable"); + return NULL; + } + _set_wtbarr_callback(callback); + + Py_RETURN_NONE; + } + return NULL; +} + + /*************************************************************************** * PyWcs methods */ @@ -43,6 +64,7 @@ Wcs_traverse( Py_VISIT(self->py_distortion_lookup[0]); Py_VISIT(self->py_distortion_lookup[1]); Py_VISIT(self->py_wcsprm); + Py_VISIT((PyObject*)Py_TYPE((PyObject*)self)); return 0; } @@ -51,31 +73,12 @@ static int Wcs_clear( Wcs* self) { - PyObject* tmp; - - tmp = self->py_det2im[0]; - self->py_det2im[0] = NULL; - Py_XDECREF(tmp); - - tmp = self->py_det2im[1]; - self->py_det2im[1] = NULL; - Py_XDECREF(tmp); - - tmp = self->py_sip; - self->py_sip = NULL; - Py_XDECREF(tmp); - - tmp = self->py_distortion_lookup[0]; - self->py_distortion_lookup[0] = NULL; - Py_XDECREF(tmp); - - tmp = self->py_distortion_lookup[1]; - self->py_distortion_lookup[1] = NULL; - Py_XDECREF(tmp); - - tmp = self->py_wcsprm; - self->py_wcsprm = NULL; - Py_XDECREF(tmp); + Py_CLEAR(self->py_det2im[0]); + Py_CLEAR(self->py_det2im[1]); + Py_CLEAR(self->py_sip); + Py_CLEAR(self->py_distortion_lookup[0]); + Py_CLEAR(self->py_distortion_lookup[1]); + Py_CLEAR(self->py_wcsprm); return 0; } @@ -84,9 +87,13 @@ static void Wcs_dealloc( Wcs* self) { + PyObject_GC_UnTrack(self); Wcs_clear(self); pipeline_free(&self->x); - Py_TYPE(self)->tp_free((PyObject*)self); + PyTypeObject *tp = Py_TYPE((PyObject*)self); + freefunc free_func = PyType_GetSlot(tp, Py_tp_free); + free_func((PyObject*)self); + Py_DECREF(tp); } /*@null@*/ static PyObject * @@ -96,7 +103,8 @@ Wcs_new( /*@unused@*/ PyObject* kwds) { Wcs* self; - self = (Wcs*)type->tp_alloc(type, 0); + allocfunc alloc_func = PyType_GetSlot(type, Py_tp_alloc); + self = (Wcs*)alloc_func(type, 0); if (self != NULL) { pipeline_clear(&self->x); self->py_det2im[0] = NULL; @@ -135,62 +143,63 @@ Wcs_init( /* Check and set Distortion lookup tables */ for (i = 0; i < 2; ++i) { if (py_det2im[i] != NULL && py_det2im[i] != Py_None) { - if (!PyObject_TypeCheck(py_det2im[i], &PyDistLookupType)) { + if (!PyObject_TypeCheck(py_det2im[i], (PyTypeObject*)PyDistLookupType)) { PyErr_SetString(PyExc_TypeError, "Arg 4 must be a pair of DistortionLookupTable or None objects"); return -1; } + Py_CLEAR(self->py_det2im[i]); self->py_det2im[i] = py_det2im[i]; + Py_INCREF(py_det2im[i]); self->x.det2im[i] = &(((PyDistLookup*)py_det2im[i])->x); } } /* Check and set SIP */ if (py_sip != NULL && py_sip != Py_None) { - if (!PyObject_TypeCheck(py_sip, &PySipType)) { + if (!PyObject_TypeCheck(py_sip, (PyTypeObject*)PySipType)) { PyErr_SetString(PyExc_TypeError, "Arg 1 must be Sip object"); return -1; } + Py_CLEAR(self->py_sip); self->py_sip = py_sip; + Py_INCREF(py_sip); self->x.sip = &(((PySip*)py_sip)->x); } /* Check and set Distortion lookup tables */ for (i = 0; i < 2; ++i) { if (py_distortion_lookup[i] != NULL && py_distortion_lookup[i] != Py_None) { - if (!PyObject_TypeCheck(py_distortion_lookup[i], &PyDistLookupType)) { + if (!PyObject_TypeCheck(py_distortion_lookup[i], (PyTypeObject*)PyDistLookupType)) { PyErr_SetString(PyExc_TypeError, "Arg 2 must be a pair of DistortionLookupTable or None objects"); return -1; } + Py_CLEAR(self->py_distortion_lookup[i]); self->py_distortion_lookup[i] = py_distortion_lookup[i]; + Py_INCREF(py_distortion_lookup[i]); self->x.cpdis[i] = &(((PyDistLookup*)py_distortion_lookup[i])->x); } } /* Set and lookup Wcsprm object */ if (py_wcsprm != NULL && py_wcsprm != Py_None) { - if (!PyObject_TypeCheck(py_wcsprm, &PyWcsprmType)) { + if (!PyObject_TypeCheck(py_wcsprm, (PyTypeObject*)PyWcsprmType)) { PyErr_SetString(PyExc_TypeError, "Arg 3 must be Wcsprm object"); return -1; } + Py_CLEAR(self->py_wcsprm); self->py_wcsprm = py_wcsprm; + Py_INCREF(py_wcsprm); self->x.wcs = &(((PyWcsprm*)py_wcsprm)->x); } - Py_XINCREF(self->py_sip); - Py_XINCREF(self->py_distortion_lookup[0]); - Py_XINCREF(self->py_distortion_lookup[1]); - Py_XINCREF(self->py_wcsprm); - Py_XINCREF(self->py_det2im[0]); - Py_XINCREF(self->py_det2im[1]); - return 0; } @@ -201,6 +210,8 @@ Wcs_all_pix2world( PyObject* kwds) { int naxis = 2; + npy_intp ncoord; + npy_intp nelem; PyObject* pixcrd_obj = NULL; int origin = 1; PyArrayObject* pixcrd = NULL; @@ -217,12 +228,15 @@ Wcs_all_pix2world( naxis = self->x.wcs->naxis; - pixcrd = (PyArrayObject*)PyArray_ContiguousFromAny(pixcrd_obj, PyArray_DOUBLE, 2, 2); + pixcrd = (PyArrayObject*)PyArray_ContiguousFromAny(pixcrd_obj, NPY_DOUBLE, 2, 2); if (pixcrd == NULL) { return NULL; } - if (PyArray_DIM(pixcrd, 1) < naxis) { + ncoord = PyArray_DIM(pixcrd, 0); + nelem = PyArray_DIM(pixcrd, 1); + + if (nelem < naxis) { PyErr_Format( PyExc_RuntimeError, "Input array must be 2-dimensional, where the second dimension >= %d", @@ -230,18 +244,26 @@ Wcs_all_pix2world( goto exit; } - world = (PyArrayObject*)PyArray_SimpleNew(2, PyArray_DIMS(pixcrd), PyArray_DOUBLE); + world = (PyArrayObject*)PyArray_SimpleNew(2, PyArray_DIMS(pixcrd), NPY_DOUBLE); if (world == NULL) { goto exit; } + // Here we force a call to wcsset. Normally, WCSLIB will call wcsset automatically when + // calling wcsp2s, but we need to call it ourselves using PyWcsprm_cset so that we can + // catch cases where the units might change if e.g. they are not in SI to start with. + /* Force a call to wcsset here*/ + if (((PyWcsprm*)(self->py_wcsprm))->preserve_units && PyWcsprm_cset(((PyWcsprm*)(self->py_wcsprm)), 1)) { + return NULL; + } + /* Make the call */ Py_BEGIN_ALLOW_THREADS preoffset_array(pixcrd, origin); wcsprm_python2c(self->x.wcs); status = pipeline_all_pixel2world(&self->x, - (unsigned int)PyArray_DIM(pixcrd, 0), - (unsigned int)PyArray_DIM(pixcrd, 1), + (unsigned int)ncoord, + (unsigned int)nelem, (double*)PyArray_DATA(pixcrd), (double*)PyArray_DATA(world)); wcsprm_c2python(self->x.wcs); @@ -250,23 +272,36 @@ Wcs_all_pix2world( /* unoffset_array(world, origin); */ exit: - Py_XDECREF(pixcrd); + Py_XDECREF((PyObject*)pixcrd); if (status == 0 || status == 8) { + // Since the conversion succeeded, if user has requested to preserve units, + // we convert the world coordinates to the original units + if (((PyWcsprm*)(self->py_wcsprm))->unit_scaling != NULL) { + double *world_data = (double *)PyArray_DATA(world); + double *unit_scaling = ((PyWcsprm*)(self->py_wcsprm))->unit_scaling; + for (npy_intp i = 0; i < nelem; ++i) { + for (npy_intp j = 0; j < ncoord; ++j) { + world_data[j * nelem + i] /= unit_scaling[i]; + } + } + } return (PyObject*)world; - } else if (status == -1) { - PyErr_SetString( - PyExc_ValueError, - "Wrong number of dimensions in input array. Expected 2."); - return NULL; } else { - Py_DECREF(world); + Py_XDECREF((PyObject*)world); if (status == -1) { - /* exception already set */ + PyErr_SetString( + PyExc_ValueError, + "Wrong number of dimensions in input array. Expected 2."); return NULL; } else { - wcserr_to_python_exc(self->x.err); - return NULL; + if (status == -1) { + /* exception already set */ + return NULL; + } else { + wcserr_to_python_exc(self->x.err); + return NULL; + } } } } @@ -295,7 +330,7 @@ Wcs_p4_pix2foc( return pixcrd_obj; } - pixcrd = (PyArrayObject*)PyArray_ContiguousFromAny(pixcrd_obj, PyArray_DOUBLE, 2, 2); + pixcrd = (PyArrayObject*)PyArray_ContiguousFromAny(pixcrd_obj, NPY_DOUBLE, 2, 2); if (pixcrd == NULL) { return NULL; } @@ -305,7 +340,7 @@ Wcs_p4_pix2foc( goto exit; } - foccrd = (PyArrayObject*)PyArray_SimpleNew(2, PyArray_DIMS(pixcrd), PyArray_DOUBLE); + foccrd = (PyArrayObject*)PyArray_SimpleNew(2, PyArray_DIMS(pixcrd), NPY_DOUBLE); if (foccrd == NULL) { status = 2; goto exit; @@ -323,12 +358,12 @@ Wcs_p4_pix2foc( exit: - Py_XDECREF(pixcrd); + Py_XDECREF((PyObject*)pixcrd); if (status == 0) { return (PyObject*)foccrd; } else { - Py_XDECREF(foccrd); + Py_XDECREF((PyObject*)foccrd); if (status == -1) { /* Exception already set */ return NULL; @@ -363,7 +398,7 @@ Wcs_det2im( return detcrd_obj; } - detcrd = (PyArrayObject*)PyArray_ContiguousFromAny(detcrd_obj, PyArray_DOUBLE, 2, 2); + detcrd = (PyArrayObject*)PyArray_ContiguousFromAny(detcrd_obj, NPY_DOUBLE, 2, 2); if (detcrd == NULL) { return NULL; } @@ -373,7 +408,7 @@ Wcs_det2im( goto exit; } - imcrd = (PyArrayObject*)PyArray_SimpleNew(2, PyArray_DIMS(detcrd), PyArray_DOUBLE); + imcrd = (PyArrayObject*)PyArray_SimpleNew(2, PyArray_DIMS(detcrd), NPY_DOUBLE); if (imcrd == NULL) { status = 2; goto exit; @@ -391,12 +426,12 @@ Wcs_det2im( exit: - Py_XDECREF(detcrd); + Py_XDECREF((PyObject*)detcrd); if (status == 0) { return (PyObject*)imcrd; } else { - Py_XDECREF(imcrd); + Py_XDECREF((PyObject*)imcrd); if (status == -1) { /* Exception already set */ return NULL; @@ -426,7 +461,7 @@ Wcs_pix2foc( return NULL; } - pixcrd = (PyArrayObject*)PyArray_ContiguousFromAny(pixcrd_obj, PyArray_DOUBLE, 2, 2); + pixcrd = (PyArrayObject*)PyArray_ContiguousFromAny(pixcrd_obj, NPY_DOUBLE, 2, 2); if (pixcrd == NULL) { return NULL; } @@ -436,7 +471,7 @@ Wcs_pix2foc( goto _exit; } - foccrd = (PyArrayObject*)PyArray_SimpleNew(2, PyArray_DIMS(pixcrd), PyArray_DOUBLE); + foccrd = (PyArrayObject*)PyArray_SimpleNew(2, PyArray_DIMS(pixcrd), NPY_DOUBLE); if (foccrd == NULL) { goto _exit; } @@ -454,12 +489,12 @@ Wcs_pix2foc( _exit: - Py_XDECREF(pixcrd); + Py_XDECREF((PyObject*)pixcrd); if (status == 0) { return (PyObject*)foccrd; } else { - Py_XDECREF(foccrd); + Py_XDECREF((PyObject*)foccrd); if (status == -1) { /* Exception already set */ return NULL; @@ -490,12 +525,11 @@ Wcs_set_wcs( /*@shared@*/ PyObject* value, /*@unused@*/ void* closure) { - Py_XDECREF(self->py_wcsprm); - self->py_wcsprm = NULL; + Py_CLEAR(self->py_wcsprm); self->x.wcs = NULL; if (value != NULL && value != Py_None) { - if (!PyObject_TypeCheck(value, &PyWcsprmType)) { + if (!PyObject_TypeCheck(value, (PyTypeObject*)PyWcsprmType)) { PyErr_SetString(PyExc_TypeError, "wcs must be Wcsprm object"); return -1; @@ -529,12 +563,11 @@ Wcs_set_cpdis1( /*@shared@*/ PyObject* value, /*@unused@*/ void* closure) { - Py_XDECREF(self->py_distortion_lookup[0]); - self->py_distortion_lookup[0] = NULL; + Py_CLEAR(self->py_distortion_lookup[0]); self->x.cpdis[0] = NULL; if (value != NULL && value != Py_None) { - if (!PyObject_TypeCheck(value, &PyDistLookupType)) { + if (!PyObject_TypeCheck(value, (PyTypeObject*)PyDistLookupType)) { PyErr_SetString(PyExc_TypeError, "cpdis1 must be DistortionLookupTable object"); return -1; @@ -568,12 +601,11 @@ Wcs_set_cpdis2( /*@shared@*/ PyObject* value, /*@unused@*/ void* closure) { - Py_XDECREF(self->py_distortion_lookup[1]); - self->py_distortion_lookup[1] = NULL; + Py_CLEAR(self->py_distortion_lookup[1]); self->x.cpdis[1] = NULL; if (value != NULL && value != Py_None) { - if (!PyObject_TypeCheck(value, &PyDistLookupType)) { + if (!PyObject_TypeCheck(value, (PyTypeObject*)PyDistLookupType)) { PyErr_SetString(PyExc_TypeError, "cpdis2 must be DistortionLookupTable object"); return -1; @@ -607,12 +639,11 @@ Wcs_set_det2im1( /*@shared@*/ PyObject* value, /*@unused@*/ void* closure) { - Py_XDECREF(self->py_det2im[0]); - self->py_det2im[0] = NULL; + Py_CLEAR(self->py_det2im[0]); self->x.det2im[0] = NULL; if (value != NULL && value != Py_None) { - if (!PyObject_TypeCheck(value, &PyDistLookupType)) { + if (!PyObject_TypeCheck(value, (PyTypeObject*)PyDistLookupType)) { PyErr_SetString(PyExc_TypeError, "det2im1 must be DistortionLookupTable object"); return -1; @@ -646,12 +677,11 @@ Wcs_set_det2im2( /*@shared@*/ PyObject* value, /*@unused@*/ void* closure) { - Py_XDECREF(self->py_det2im[1]); - self->py_det2im[1] = NULL; + Py_CLEAR(self->py_det2im[1]); self->x.det2im[1] = NULL; if (value != NULL && value != Py_None) { - if (!PyObject_TypeCheck(value, &PyDistLookupType)) { + if (!PyObject_TypeCheck(value, (PyTypeObject*)PyDistLookupType)) { PyErr_SetString(PyExc_TypeError, "det2im2 must be DistortionLookupTable object"); return -1; @@ -685,12 +715,11 @@ Wcs_set_sip( /*@shared@*/ PyObject* value, /*@unused@*/ void* closure) { - Py_XDECREF(self->py_sip); - self->py_sip = NULL; + Py_CLEAR(self->py_sip); self->x.sip = NULL; if (value != NULL && value != Py_None) { - if (!PyObject_TypeCheck(value, &PySipType)) { + if (!PyObject_TypeCheck(value, (PyTypeObject*)PySipType)) { PyErr_SetString(PyExc_TypeError, "sip must be Sip object"); return -1; @@ -704,97 +733,6 @@ Wcs_set_sip( return 0; } -static PyObject* -Wcs___copy__( - Wcs* self, - /*@unused@*/ PyObject* args, - /*@unused@*/ PyObject* kwds) { - - PyObject* copy = NULL; - - copy = Wcs_new(&WcsType, NULL, NULL); - if (copy == NULL) { - return NULL; - } - - if (self->py_det2im[0]) { - Wcs_set_det2im1((Wcs*)copy, self->py_det2im[0], NULL); - } - - if (self->py_det2im[1]) { - Wcs_set_det2im2((Wcs*)copy, self->py_det2im[1], NULL); - } - - if (self->py_sip) { - Wcs_set_sip((Wcs*)copy, self->py_sip, NULL); - } - - if (self->py_distortion_lookup[0]) { - Wcs_set_cpdis1((Wcs*)copy, self->py_distortion_lookup[0], NULL); - } - - if (self->py_distortion_lookup[1]) { - Wcs_set_cpdis2((Wcs*)copy, self->py_distortion_lookup[1], NULL); - } - - if (self->py_wcsprm) { - Wcs_set_wcs((Wcs*)copy, self->py_wcsprm, NULL); - } - - return copy; -} - -static int -_deepcopy_helper( - Wcs* copy, - PyObject* item, - int (*function)(Wcs*, PyObject*, void*), - PyObject* memo) { - PyObject* obj_copy; - - if (item) { - obj_copy = get_deepcopy(item, memo); - if (obj_copy == NULL) { - return 1; - } - - if (function(copy, obj_copy, NULL)) { - Py_DECREF(obj_copy); - return 1; - } - - Py_DECREF(obj_copy); - } - - return 0; -} - -static PyObject* -Wcs___deepcopy__( - Wcs* self, - PyObject* memo, - /*@unused@*/ PyObject* kwds) { - - Wcs* copy; - - copy = (Wcs*)Wcs_new(&WcsType, NULL, NULL); - if (copy == NULL) { - return NULL; - } - - if (_deepcopy_helper(copy, self->py_det2im[0], Wcs_set_det2im1, memo) || - _deepcopy_helper(copy, self->py_det2im[1], Wcs_set_det2im2, memo) || - _deepcopy_helper(copy, self->py_sip, Wcs_set_sip, memo) || - _deepcopy_helper(copy, self->py_distortion_lookup[0], Wcs_set_cpdis1, memo) || - _deepcopy_helper(copy, self->py_distortion_lookup[1], Wcs_set_det2im1, memo) || - _deepcopy_helper(copy, self->py_wcsprm, Wcs_set_wcs, memo)) { - Py_DECREF(copy); - return NULL; - } - - return (PyObject*)copy; -} - static PyObject* _sanity_check( PyObject* self, @@ -826,8 +764,6 @@ static PyGetSetDef Wcs_getset[] = { static PyMethodDef Wcs_methods[] = { {"_all_pix2world", (PyCFunction)Wcs_all_pix2world, METH_VARARGS|METH_KEYWORDS, doc_all_pix2world}, - {"__copy__", (PyCFunction)Wcs___copy__, METH_NOARGS, NULL}, - {"__deepcopy__", (PyCFunction)Wcs___deepcopy__, METH_O, NULL}, {"_det2im", (PyCFunction)Wcs_det2im, METH_VARARGS|METH_KEYWORDS, doc_det2im}, {"_p4_pix2foc", (PyCFunction)Wcs_p4_pix2foc, METH_VARARGS|METH_KEYWORDS, doc_p4_pix2foc}, {"_pix2foc", (PyCFunction)Wcs_pix2foc, METH_VARARGS|METH_KEYWORDS, doc_pix2foc}, @@ -837,55 +773,29 @@ static PyMethodDef Wcs_methods[] = { static PyMethodDef module_methods[] = { {"_sanity_check", (PyCFunction)_sanity_check, METH_NOARGS, ""}, {"find_all_wcs", (PyCFunction)PyWcsprm_find_all_wcs, METH_VARARGS|METH_KEYWORDS, doc_find_all_wcs}, + {"set_wtbarr_fitsio_callback", (PyCFunction)PyWcsprm_set_wtbarr_fitsio_callback, METH_VARARGS, NULL}, {NULL} /* Sentinel */ }; -static PyTypeObject WcsType = { - #if PY3K - PyVarObject_HEAD_INIT(NULL, 0) - #else - PyObject_HEAD_INIT(NULL) - 0, /*ob_size*/ - #endif - "astropy.wcs.WCSBase", /*tp_name*/ - sizeof(Wcs), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - (destructor)Wcs_dealloc, /*tp_dealloc*/ - 0, /*tp_print*/ - 0, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_compare*/ - 0, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - 0, /*tp_hash */ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /*tp_flags*/ - doc_Wcs, /* tp_doc */ - (traverseproc)Wcs_traverse, /* tp_traverse */ - (inquiry)Wcs_clear, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - Wcs_methods, /* tp_methods */ - 0, /* tp_members */ - Wcs_getset, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - (initproc)Wcs_init, /* tp_init */ - 0, /* tp_alloc */ - Wcs_new, /* tp_new */ +static PyType_Spec WcsType_spec = { + .name = "astropy.wcs.WCSBase", + .basicsize = sizeof(Wcs), + .itemsize = 0, + .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_IMMUTABLETYPE, + .slots = (PyType_Slot[]){ + {Py_tp_dealloc, (destructor)Wcs_dealloc}, + {Py_tp_doc, doc_Wcs}, + {Py_tp_traverse, (traverseproc)Wcs_traverse}, + {Py_tp_clear, (inquiry)Wcs_clear}, + {Py_tp_methods, Wcs_methods}, + {Py_tp_getset, Wcs_getset}, + {Py_tp_init, (initproc)Wcs_init}, + {Py_tp_new, Wcs_new}, + {0, NULL}, + }, }; +static PyObject* WcsType = NULL; /*************************************************************************** * Module-level @@ -894,11 +804,11 @@ static PyTypeObject WcsType = { int _setup_wcs_type( PyObject* m) { - if (PyType_Ready(&WcsType) < 0) + WcsType = PyType_FromSpec(&WcsType_spec); + if (WcsType == NULL) return -1; - Py_INCREF(&WcsType); - return PyModule_AddObject(m, "_Wcs", (PyObject *)&WcsType); + return PyModule_AddObject(m, "_Wcs", WcsType); } struct module_state { @@ -908,30 +818,20 @@ struct module_state { #endif }; -#if PY3K - static struct PyModuleDef moduledef = { - PyModuleDef_HEAD_INIT, - "_wcs", - NULL, - sizeof(struct module_state), - module_methods, - NULL, - NULL, - NULL, - NULL - }; - - #define INITERROR return NULL - - PyMODINIT_FUNC - PyInit__wcs(void) - -#else - #define INITERROR return +static struct PyModuleDef moduledef = { + PyModuleDef_HEAD_INIT, + "_wcs", + NULL, + sizeof(struct module_state), + module_methods, + NULL, + NULL, + NULL, + NULL +}; - PyMODINIT_FUNC - init_wcs(void) -#endif +PyMODINIT_FUNC +PyInit__wcs(void) { PyObject* m; @@ -953,35 +853,39 @@ struct module_state { wcs_errexc[12] = &WcsExc_InvalidSubimageSpecification; /* Invalid subimage specification (no spectral axis) */ wcs_errexc[13] = &WcsExc_NonseparableSubimageCoordinateSystem; /* Non-separable subimage coordinate system */ -#if PY3K m = PyModule_Create(&moduledef); -#else - m = Py_InitModule3("_wcs", module_methods, NULL); -#endif if (m == NULL) - INITERROR; + return NULL; import_array(); -#if defined(_MSC_VER) - fill_docstrings(); -#endif if (_setup_api(m) || _setup_str_list_proxy_type(m) || + _setup_unit_list_proxy_type(m)|| _setup_wcsprm_type(m) || + _setup_auxprm_type(m) || + _setup_prjprm_type(m) || + _setup_celprm_type(m) || _setup_tabprm_type(m) || - _setup_units_type(m) || - /* _setup_wtbarr_type(m) || */ + _setup_wtbarr_type(m) || _setup_distortion_type(m) || _setup_sip_type(m) || _setup_wcs_type(m) || _define_exceptions(m)) { Py_DECREF(m); - INITERROR; + return NULL; } -#if PY3K - return m; +#ifdef HAVE_WCSLIB_VERSION + if (PyModule_AddStringConstant(m, "WCSLIB_VERSION", wcslib_version(NULL))) { + return NULL; + } +#else + if (PyModule_AddStringConstant(m, "WCSLIB_VERSION", "4.x")) { + return NULL; + } #endif + + return m; } diff --git a/astropy/wcs/src/astropy_wcs_api.c b/astropy/wcs/src/astropy_wcs_api.c index f5b83de60220..d3f1cb90f11d 100644 --- a/astropy/wcs/src/astropy_wcs_api.c +++ b/astropy/wcs/src/astropy_wcs_api.c @@ -1,6 +1,6 @@ #define NO_IMPORT_ARRAY -#include "astropy_wcs_api.h" +#include "astropy_wcs/astropy_wcs_api.h" int AstropyWcs_GetCVersion(void) { @@ -45,11 +45,7 @@ void* AstropyWcs_API[] = { int _setup_api(PyObject *m) { PyObject* c_api; - #if PY_VERSION_HEX >= 0x03020000 - c_api = PyCapsule_New((void *)AstropyWcs_API, "_wcs._ASTROPY_WCS_API", NULL); - #else - c_api = PyCObject_FromVoidPtr((void *)AstropyWcs_API, NULL); - #endif + c_api = PyCapsule_New((void *)AstropyWcs_API, "_wcs._ASTROPY_WCS_API", NULL); PyModule_AddObject(m, "_ASTROPY_WCS_API", c_api); return 0; diff --git a/astropy/wcs/src/distortion.c b/astropy/wcs/src/distortion.c index 7a8d630953f5..91af1f17496a 100644 --- a/astropy/wcs/src/distortion.c +++ b/astropy/wcs/src/distortion.c @@ -3,7 +3,7 @@ mdroe@stsci.edu */ -#include "distortion.h" +#include "astropy_wcs/distortion.h" #include #include @@ -79,11 +79,11 @@ image_coord_to_distortion_coord( assert(lookup != NULL); assert(axis < NAXES); - /* The "- 1./stepsize" is here because the input coordinates are 1-based, + /* The "- 1" is here because the input coordinates are 1-based, but this is a C-array underneath */ result = ( ((img - lookup->crval[axis]) / lookup->cdelt[axis]) + - lookup->crpix[axis]) - 1.0/lookup->cdelt[axis]; + lookup->crpix[axis]) - 1.0; return CLAMP(result, 0.0, (double)(lookup->naxis[axis] - 1)); } @@ -172,12 +172,16 @@ p4_pix2deltas( const double* pix0; const double* pixend; +#ifndef NDEBUG + unsigned int k; +#endif + assert(naxes == NAXES); assert(lookup != NULL); assert(pix != NULL); assert(foc != NULL); + #ifndef NDEBUG - unsigned int k; for (k = 0; k < naxes; ++k) { if (lookup[k] != NULL) { assert(lookup[k]->data != NULL); @@ -220,4 +224,3 @@ p4_pix2foc( return p4_pix2deltas(naxes, lookup, nelem, pix, foc); } - diff --git a/astropy/wcs/src/distortion_wrap.c b/astropy/wcs/src/distortion_wrap.c index e8568c8b8700..d242a5d5026b 100644 --- a/astropy/wcs/src/distortion_wrap.c +++ b/astropy/wcs/src/distortion_wrap.c @@ -5,8 +5,8 @@ #define NO_IMPORT_ARRAY -#include "distortion_wrap.h" -#include "docstrings.h" +#include "astropy_wcs/distortion_wrap.h" +#include "astropy_wcs/docstrings.h" #include /* From Python */ @@ -17,6 +17,7 @@ PyDistLookup_traverse( void* arg) { Py_VISIT(self->py_data); + Py_VISIT((PyObject*)Py_TYPE((PyObject*)self)); return 0; } @@ -25,11 +26,7 @@ static int PyDistLookup_clear( PyDistLookup* self) { - PyObject* tmp; - - tmp = (PyObject*)self->py_data; - self->py_data = NULL; - Py_XDECREF(tmp); + Py_CLEAR(self->py_data); return 0; } @@ -38,9 +35,13 @@ static void PyDistLookup_dealloc( PyDistLookup* self) { + PyObject_GC_UnTrack(self); distortion_lookup_t_free(&self->x); - Py_XDECREF(self->py_data); - Py_TYPE(self)->tp_free((PyObject*)self); + Py_XDECREF((PyObject*)self->py_data); + PyTypeObject *tp = Py_TYPE((PyObject*)self); + freefunc free_func = PyType_GetSlot(tp, Py_tp_free); + free_func((PyObject*)self); + Py_DECREF(tp); } /*@null@*/ static PyObject * @@ -51,7 +52,8 @@ PyDistLookup_new( PyDistLookup* self; - self = (PyDistLookup*)type->tp_alloc(type, 0); + allocfunc alloc_func = PyType_GetSlot(type, Py_tp_alloc); + self = (PyDistLookup*)alloc_func(type, 0); if (self != NULL) { if (distortion_lookup_t_init(&self->x)) { return NULL; @@ -78,7 +80,7 @@ PyDistLookup_init( return -1; } - array_obj = (PyArrayObject*)PyArray_ContiguousFromAny(py_array_obj, PyArray_FLOAT32, 2, 2); + array_obj = (PyArrayObject*)PyArray_ContiguousFromAny(py_array_obj, NPY_FLOAT32, 2, 2); if (array_obj == NULL) { return -1; } @@ -163,7 +165,7 @@ PyDistLookup_get_data( Py_INCREF(Py_None); return Py_None; } else { - Py_INCREF(self->py_data); + Py_INCREF((PyObject*)self->py_data); return (PyObject*)self->py_data; } } @@ -177,19 +179,18 @@ PyDistLookup_set_data( PyArrayObject* value_array = NULL; if (value == NULL) { - Py_XDECREF(self->py_data); - self->py_data = NULL; + Py_CLEAR(self->py_data); self->x.data = NULL; return 0; } - value_array = (PyArrayObject*)PyArray_ContiguousFromAny(value, PyArray_FLOAT32, 2, 2); + value_array = (PyArrayObject*)PyArray_ContiguousFromAny(value, NPY_FLOAT32, 2, 2); if (value_array == NULL) { return -1; } - Py_XDECREF(self->py_data); + Py_XDECREF((PyObject*)self->py_data); self->py_data = value_array; self->x.naxis[0] = (unsigned int)PyArray_DIM(value_array, 1); @@ -231,7 +232,7 @@ PyDistLookup___copy__( PyDistLookup* copy = NULL; int i = 0; - copy = (PyDistLookup*)PyDistLookup_new(&PyDistLookupType, NULL, NULL); + copy = (PyDistLookup*)PyDistLookup_new((PyTypeObject*)PyDistLookupType, NULL, NULL); if (copy == NULL) { return NULL; } @@ -260,7 +261,7 @@ PyDistLookup___deepcopy__( PyObject* obj_copy; int i = 0; - copy = (PyDistLookup*)PyDistLookup_new(&PyDistLookupType, NULL, NULL); + copy = (PyDistLookup*)PyDistLookup_new((PyTypeObject*)PyDistLookupType, NULL, NULL); if (copy == NULL) { return NULL; } @@ -301,59 +302,33 @@ static PyMethodDef PyDistLookup_methods[] = { {NULL} }; -PyTypeObject PyDistLookupType = { -#if PY3K - PyVarObject_HEAD_INIT(NULL, 0) -#else - PyObject_HEAD_INIT(NULL) - 0, /*ob_size*/ -#endif - "astropy.wcs.DistortionLookupTable", /*tp_name*/ - sizeof(PyDistLookup), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - (destructor)PyDistLookup_dealloc, /*tp_dealloc*/ - 0, /*tp_print*/ - 0, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_compare*/ - 0, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - 0, /*tp_hash */ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /*tp_flags*/ - doc_DistortionLookupTable, /* tp_doc */ - (traverseproc)PyDistLookup_traverse, /* tp_traverse */ - (inquiry)PyDistLookup_clear, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - PyDistLookup_methods, /* tp_methods */ - 0, /* tp_members */ - PyDistLookup_getset, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - (initproc)PyDistLookup_init, /* tp_init */ - 0, /* tp_alloc */ - PyDistLookup_new, /* tp_new */ +static PyType_Spec PyDistLookupType_spec = { + .name = "astropy.wcs.DistortionLookupTable", + .basicsize = sizeof(PyDistLookup), + .itemsize = 0, + .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_IMMUTABLETYPE, + .slots = (PyType_Slot[]){ + {Py_tp_dealloc, (destructor)PyDistLookup_dealloc}, + {Py_tp_doc, doc_DistortionLookupTable}, + {Py_tp_traverse, (traverseproc)PyDistLookup_traverse}, + {Py_tp_clear, (inquiry)PyDistLookup_clear}, + {Py_tp_methods, PyDistLookup_methods}, + {Py_tp_getset, PyDistLookup_getset}, + {Py_tp_init, (initproc)PyDistLookup_init}, + {Py_tp_new, PyDistLookup_new}, + {0, NULL}, + }, }; +PyObject* PyDistLookupType = NULL; + int _setup_distortion_type( PyObject* m) { - if (PyType_Ready(&PyDistLookupType) < 0) { + PyDistLookupType = PyType_FromSpec(&PyDistLookupType_spec); + if (PyDistLookupType == NULL) { return -1; } - Py_INCREF(&PyDistLookupType); - return PyModule_AddObject(m, "DistortionLookupTable", (PyObject *)&PyDistLookupType); + return PyModule_AddObject(m, "DistortionLookupTable", PyDistLookupType); } diff --git a/astropy/wcs/src/pipeline.c b/astropy/wcs/src/pipeline.c index bfa1a2b79b6f..fd6501c457c2 100644 --- a/astropy/wcs/src/pipeline.c +++ b/astropy/wcs/src/pipeline.c @@ -3,8 +3,9 @@ mdroe@stsci.edu */ -#include "pipeline.h" -#include "util.h" +#include "astropy_wcs/pipeline.h" +#include "astropy_wcs/util.h" +#include "wcserr.h" #include #include #include @@ -98,6 +99,13 @@ pipeline_all_pixel2world( } if (has_wcs) { + if (ncoord < 1) { + status = wcserr_set( + PIP_ERRMSG(WCSERR_BAD_PIX), + "The number of coordinates must be > 0"); + goto exit; + } + buffer = mem = malloc( ncoord * nelem * sizeof(double) + /* imgcrd */ ncoord * sizeof(double) + /* phi */ @@ -142,6 +150,9 @@ pipeline_all_pixel2world( if ((status = wcsp2s(pipeline->wcs, (int)ncoord, (int)nelem, wcs_input, imgcrd, phi, theta, wcs_output, stat))) { + if (pipeline->err == NULL) { + pipeline->err = calloc(1, sizeof(struct wcserr)); + } wcserr_copy(pipeline->wcs->err, pipeline->err); } @@ -186,6 +197,13 @@ int pipeline_pix2foc( err = &(pipeline->err); + if (ncoord < 1) { + status = wcserr_set( + PIP_ERRMSG(WCSERR_BAD_PIX), + "The number of coordinates must be > 0"); + goto exit; + } + has_det2im = pipeline->det2im[0] != NULL || pipeline->det2im[1] != NULL; has_sip = pipeline->sip != NULL; has_p4 = pipeline->cpdis[0] != NULL || pipeline->cpdis[1] != NULL; @@ -228,6 +246,9 @@ int pipeline_pix2foc( if (has_sip) { status = sip_pix2deltas(pipeline->sip, 2, ncoord, input, foc); if (status) { + if (pipeline->err == NULL) { + pipeline->err = calloc(1, sizeof(struct wcserr)); + } wcserr_copy(pipeline->sip->err, pipeline->err); goto exit; } @@ -248,4 +269,3 @@ int pipeline_pix2foc( return status; } - diff --git a/astropy/wcs/src/pyutil.c b/astropy/wcs/src/pyutil.c index 2f96b8cfe865..504d4f8f0625 100644 --- a/astropy/wcs/src/pyutil.c +++ b/astropy/wcs/src/pyutil.c @@ -6,9 +6,14 @@ #define NO_IMPORT_ARRAY /* util.h must be imported first */ -#include "pyutil.h" +#include "astropy_wcs/pyutil.h" +#include "astropy_wcs/docstrings.h" + +#include // malloc, free +#include // memcpy #include "wcsfix.h" +#include "wcshdr.h" #include "wcsprintf.h" #include "wcsunits.h" @@ -35,14 +40,14 @@ _PyArrayProxy_New( nd, (npy_intp*)dims, NULL, (void*)data, - NPY_C_CONTIGUOUS | flags, + NPY_ARRAY_C_CONTIGUOUS | flags, NULL); if (result == NULL) { return NULL; } Py_INCREF(self); - PyArray_BASE(result) = (PyObject*)self; + PyArray_SetBaseObject((PyArrayObject *)result, self); return result; } @@ -54,7 +59,7 @@ PyArrayProxy_New( int typenum, const void* data) { - return _PyArrayProxy_New(self, nd, dims, typenum, data, NPY_WRITEABLE); + return _PyArrayProxy_New(self, nd, dims, typenum, data, NPY_ARRAY_WRITEABLE); } /*@null@*/ PyObject* @@ -159,7 +164,7 @@ wcsprm_fix_values( unsigned int naxis = (unsigned int)x->naxis; - value_fixer(x->cd, 4); + value_fixer(x->cd, naxis * naxis); value_fixer(x->cdelt, naxis); value_fixer(x->crder, naxis); value_fixer(x->crota, naxis); @@ -171,7 +176,7 @@ wcsprm_fix_values( value_fixer(&x->lonpole, 1); value_fixer(&x->mjdavg, 1); value_fixer(&x->mjdobs, 1); - value_fixer(x->obsgeo, 3); + value_fixer(x->obsgeo, 6); value_fixer(&x->cel.phi0, 1); value_fixer(&x->restfrq, 1); value_fixer(&x->restwav, 1); @@ -179,6 +184,22 @@ wcsprm_fix_values( value_fixer(&x->velangl, 1); value_fixer(&x->velosys, 1); value_fixer(&x->zsource, 1); + value_fixer(x->czphs, naxis); + value_fixer(x->cperi, naxis); + value_fixer(x->mjdref, 2); + value_fixer(&x->mjdbeg, 1); + value_fixer(&x->mjdend, 1); + value_fixer(&x->jepoch, 1); + value_fixer(&x->bepoch, 1); + value_fixer(&x->tstart, 1); + value_fixer(&x->tstop, 1); + value_fixer(&x->xposure, 1); + value_fixer(&x->timsyer, 1); + value_fixer(&x->timrder, 1); + value_fixer(&x->timedel, 1); + value_fixer(&x->timepixr, 1); + value_fixer(&x->timeoffs, 1); + value_fixer(&x->telapse, 1); } void @@ -203,6 +224,7 @@ wcsprm_python2c( * Exceptions * ***************************************************************************/ +PyObject* WcsExc_Wcs; PyObject* WcsExc_SingularMatrix; PyObject* WcsExc_InconsistentAxisTypes; PyObject* WcsExc_InvalidTransform; @@ -212,6 +234,7 @@ PyObject* WcsExc_InvalidSubimageSpecification; PyObject* WcsExc_NonseparableSubimageCoordinateSystem; PyObject* WcsExc_NoWcsKeywordsFound; PyObject* WcsExc_InvalidTabularParameters; +PyObject* WcsExc_InvalidPrjParameters; /* This is an array mapping the wcs status codes to Python exception * types. The exception string is stored as part of wcslib itself in @@ -219,8 +242,17 @@ PyObject* WcsExc_InvalidTabularParameters; */ PyObject** wcs_errexc[14]; +static PyObject* +_new_exception_with_doc(char *name, char *doc, PyObject *base) +{ + return PyErr_NewExceptionWithDoc(name, doc, base, NULL); +} + #define DEFINE_EXCEPTION(exc) \ - WcsExc_##exc = PyErr_NewException("astropy.wcs._wcs." #exc "Error", PyExc_ValueError, NULL); \ + WcsExc_##exc = _new_exception_with_doc( \ + "astropy.wcs._wcs." #exc "Error", \ + doc_##exc, \ + WcsExc_Wcs); \ if (WcsExc_##exc == NULL) \ return 1; \ PyModule_AddObject(m, #exc "Error", WcsExc_##exc); \ @@ -229,6 +261,15 @@ int _define_exceptions( PyObject* m) { + WcsExc_Wcs = _new_exception_with_doc( + "astropy.wcs._wcs.WcsError", + doc_WcsError, + PyExc_ValueError); + if (WcsExc_Wcs == NULL) { + return 1; + } + PyModule_AddObject(m, "WcsError", WcsExc_Wcs); + DEFINE_EXCEPTION(SingularMatrix); DEFINE_EXCEPTION(InconsistentAxisTypes); DEFINE_EXCEPTION(InvalidTransform); @@ -238,6 +279,7 @@ _define_exceptions( DEFINE_EXCEPTION(NonseparableSubimageCoordinateSystem); DEFINE_EXCEPTION(NoWcsKeywordsFound); DEFINE_EXCEPTION(InvalidTabularParameters); + DEFINE_EXCEPTION(InvalidPrjParameters); return 0; } @@ -302,28 +344,30 @@ wcserr_fix_to_python_exc(const struct wcserr *err) { } void -wcserr_units_to_python_exc(const struct wcserr *err) { - PyObject *exc; - if (err == NULL) { - PyErr_SetString(PyExc_RuntimeError, "NULL error object in wcslib"); +wcshdr_err_to_python_exc(int status, const struct wcsprm *wcs) { + /* Add error to wcslib error buffer */ + wcsperr(wcs, NULL); + if (status > 0 && status != WCSHDRERR_PARSER) { + PyErr_Format( + PyExc_MemoryError, + "Memory allocation error:\n%s", + wcsprintf_buf() + ); } else { - if (err->status > 0 && err->status <= UNITSERR_UNSAFE_TRANS) { - exc = PyExc_ValueError; - } else { - exc = PyExc_RuntimeError; - } - /* This is technically not thread-safe -- make sure we have the GIL */ - wcsprintf_set(NULL); - wcserr_prt(err, ""); - PyErr_SetString(exc, wcsprintf_buf()); + PyErr_Format( + PyExc_ValueError, + "Internal error in wcslib header parser:\n %s", + wcsprintf_buf() + ); } } + /*************************************************************************** Property helpers ***************************************************************************/ -#define SHAPE_STR_LEN 128 +#define SHAPE_STR_LEN 2048 /* Helper function to display the desired shape of an array as a string, eg. 2x2 */ @@ -363,33 +407,45 @@ set_string( char* buffer; Py_ssize_t len; + PyObject* ascii_obj = NULL; + int result = -1; if (check_delete(propname, value)) { return -1; } - #if PY3K - if (PyBytes_AsStringAndSize(value, &buffer, &len) == -1) { - return -1; - } - #else - if (PyString_AsStringAndSize(value, &buffer, &len) == -1) { - return -1; + if (PyUnicode_Check(value)) { + ascii_obj = PyUnicode_AsASCIIString(value); + if (ascii_obj == NULL) { + goto end; + } + if (PyBytes_AsStringAndSize(ascii_obj, &buffer, &len) == -1) { + goto end; + } + } else if (PyBytes_Check(value)) { + if (PyBytes_AsStringAndSize(value, &buffer, &len) == -1) { + goto end; + } + } else { + PyErr_SetString(PyExc_TypeError, "'value' must be bytes or unicode."); + goto end; } - #endif - if (len > maxlen) { + if (len >= maxlen) { PyErr_Format( PyExc_ValueError, - "'%s' must be less than %u characters", + "'%s' length must be less than %u characters.", propname, - (unsigned int)maxlen); - return -1; + (unsigned int) maxlen); + goto end; } - strncpy(dest, buffer, (size_t)maxlen); + strncpy(dest, buffer, (size_t)len + 1); + result = 0; - return 0; + end: + Py_XDECREF(ascii_obj); + return result; } /* get_bool is inlined */ @@ -422,16 +478,13 @@ set_int( return -1; } - #if PY3K value_int = PyLong_AsLong(value); - #else - value_int = PyInt_AsLong(value); - #endif if (value_int == -1 && PyErr_Occurred()) { return -1; } if ((unsigned long)value_int > 0x7fffffff) { + PyErr_SetString(PyExc_OverflowError, "integer value too large"); return -1; } @@ -479,7 +532,7 @@ set_double_array( return -1; } - value_array = (PyArrayObject*)PyArray_ContiguousFromAny(value, PyArray_DOUBLE, + value_array = (PyArrayObject*)PyArray_ContiguousFromAny(value, NPY_DOUBLE, ndims, ndims); if (value_array == NULL) { return -1; @@ -521,7 +574,7 @@ set_int_array( return -1; } - value_array = (PyArrayObject*)PyArray_ContiguousFromAny(value, PyArray_INT, + value_array = (PyArrayObject*)PyArray_ContiguousFromAny(value, NPY_INT, ndims, ndims); if (value_array == NULL) { return -1; @@ -550,20 +603,16 @@ set_int_array( /* get_str_list is inlined */ -/* set_str_list is inlined */ - int -set_str_list_verified( +set_str_list( const char* propname, PyObject* value, Py_ssize_t len, Py_ssize_t maxlen, - char (*dest)[72], - str_verify_fn verify) { + char (*dest)[72]) { PyObject* str = NULL; - char* str_char = NULL; - Py_ssize_t str_len = 0; + Py_ssize_t input_len; Py_ssize_t i = 0; if (check_delete(propname, value)) { @@ -601,50 +650,26 @@ set_str_list_verified( return -1; } - #if PY3K - if (!PyBytes_CheckExact(str)) { - #else - if (!PyString_CheckExact(str)) { - #endif + if (!(PyBytes_CheckExact(str) || PyUnicode_CheckExact(str))) { PyErr_Format( PyExc_TypeError, - #if PY3K - "'%s' must be a sequence of bytes", - #else - "'%s' must be a sequence of strings", - #endif + "'%s' must be a sequence of bytes or strings", propname); Py_DECREF(str); return -1; } - #if PY3K - if (PyBytes_Size(str) > maxlen) { - #else - if (PyString_Size(str) > maxlen) { - #endif + input_len = PySequence_Size(str); + if (input_len > maxlen) { PyErr_Format( - PyExc_TypeError, - #if PY3K - "Each bytes in '%s' must be less than %u characters", - #else - "Each string in '%s' must be less than %u characters", - #endif + PyExc_ValueError, + "Each entry in '%s' must be less than %u characters", propname, (unsigned int)maxlen); Py_DECREF(str); return -1; - } - - if (verify) { - #if PY3K - str_char = PyBytes_AsString(str); - #else - str_char = PyString_AsString(str); - #endif - if (!verify(str_char)) { - Py_DECREF(str); - return -1; - } + } else if (input_len == -1) { + Py_DECREF(str); + return -1; } Py_DECREF(str); @@ -655,20 +680,15 @@ set_str_list_verified( if (str == NULL) { /* Theoretically, something has gone really wrong here, since we've already verified the list. */ + PyErr_Clear(); PyErr_Format( PyExc_RuntimeError, "Input values have changed underneath us. Something is seriously wrong."); return -1; } - /* We already know its a string of the correct length */ - #if PY3K - if (PyBytes_AsStringAndSize(str, &str_char, &str_len)) { - #else - if (PyString_AsStringAndSize(str, &str_char, &str_len)) { - #endif - /* Theoretically, something has gone really wrong here, since - we've already verified the list. */ + if (set_string(propname, str, dest[i], maxlen)) { + PyErr_Clear(); PyErr_Format( PyExc_RuntimeError, "Input values have changed underneath us. Something is seriously wrong."); @@ -676,14 +696,13 @@ set_str_list_verified( return -1; } - strncpy(dest[i], str_char, (size_t)maxlen); - Py_DECREF(str); } return 0; } + /*@null@*/ PyObject* get_pscards( /*@unused@*/ const char* propname, @@ -695,7 +714,7 @@ get_pscards( Py_ssize_t i = 0; if (nps < 0) { - return NULL; + nps = 0; } result = PyList_New((Py_ssize_t)nps); @@ -703,6 +722,11 @@ get_pscards( return NULL; } + if (nps && ps == NULL) { + PyErr_SetString(PyExc_MemoryError, "NULL pointer"); + return NULL; + } + for (i = 0; i < (Py_ssize_t)nps; ++i) { subresult = Py_BuildValue("iis", ps[i].i, ps[i].m, ps[i].value); if (subresult == NULL) { @@ -711,7 +735,6 @@ get_pscards( } if (PyList_SetItem(result, i, subresult)) { - Py_DECREF(subresult); Py_DECREF(result); return NULL; } @@ -800,11 +823,20 @@ get_pvcards( PyObject* subresult = NULL; Py_ssize_t i = 0; + if (npv < 0) { + npv = 0; + } + result = PyList_New((Py_ssize_t)npv); if (result == NULL) { return NULL; } + if (npv && pv == NULL) { + PyErr_SetString(PyExc_MemoryError, "NULL pointer"); + return NULL; + } + for (i = 0; i < (Py_ssize_t)npv; ++i) { subresult = Py_BuildValue("iid", pv[i].i, pv[i].m, pv[i].value); if (subresult == NULL) { @@ -813,7 +845,6 @@ get_pvcards( } if (PyList_SetItem(result, i, subresult)) { - Py_DECREF(subresult); Py_DECREF(result); return NULL; } @@ -830,65 +861,52 @@ set_pvcards( int *npv, int *npvmax) { - PyObject* subvalue = NULL; - int i = 0; - Py_ssize_t size = 0; - int ival = 0; - int mval = 0; - double dblvalue = 0.0; - void* newmem = NULL; + PyObject* fastseq = NULL; + struct pvcard* newmem = NULL; + Py_ssize_t size; + int ret = -1; + int i; + + fastseq = PySequence_Fast(value, "Expected sequence type"); + if (!fastseq) + goto done; - if (!PySequence_Check(value)) { - return -1; - } size = PySequence_Size(value); - if (size > 0x7fffffff) { - return -1; - } + newmem = malloc(sizeof(struct pvcard) * size); - if (size > (Py_ssize_t)*npvmax) { - newmem = malloc(sizeof(struct pvcard) * size); - if (newmem == NULL) { - PyErr_SetString(PyExc_MemoryError, "Could not allocate memory."); - return -1; - } - free(*pv); - *pv = newmem; - *npvmax = (int)size; + /* Raise exception if size is nonzero but newmem + * could not be allocated. */ + if (size && !newmem) { + PyErr_SetString(PyExc_MemoryError, "Could not allocate memory."); + return -1; } - /* Verify the entire list for correct types first, so we don't have - to undo anything copied into the canonical array. */ - for (i = 0; i < size; ++i) { - subvalue = PySequence_GetItem(value, i); - if (subvalue == NULL) { - return -1; - } - if (!PyArg_ParseTuple(subvalue, "iid", &ival, &mval, &dblvalue)) { - Py_DECREF(subvalue); - return -1; + PyObject* item = NULL; + for (i = 0; i < size; ++i) + { + if (!PyArg_ParseTuple((item = PySequence_GetItem(value, i)), "iid", + &newmem[i].i, &newmem[i].m, &newmem[i].value)) + { + Py_DECREF(item); + goto done; } - Py_DECREF(subvalue); } - for (i = 0; i < size; ++i) { - subvalue = PySequence_GetItem(value, i); - if (subvalue == NULL) { - return -1; - } - if (!PyArg_ParseTuple(subvalue, "iid", &ival, &mval, &dblvalue)) { - Py_DECREF(subvalue); - return -1; - } - Py_DECREF(subvalue); - - (*pv)[i].i = ival; - (*pv)[i].m = mval; - (*pv)[i].value = dblvalue; - (*npv) = i + 1; + if (size <= (Py_ssize_t)*npvmax) { + memcpy(*pv, newmem, sizeof(struct pvcard) * size); + } else { /* (size > (Py_ssize_t)*npvmax) */ + free(*pv); + *npv = (int)size; + *pv = newmem; + newmem = NULL; } + *npv = (int)size; - return 0; + ret = 0; +done: + Py_XDECREF(fastseq); + free(newmem); + return ret; } PyObject* @@ -915,7 +933,6 @@ parse_unsafe_unit_conversion_spec( *ctrl = 0; - p = arg; for (p = arg; *p != '\0'; ++p) { switch (*p) { case 's': @@ -940,5 +957,3 @@ parse_unsafe_unit_conversion_spec( return 0; } - - diff --git a/astropy/wcs/src/sip.c b/astropy/wcs/src/sip.c index 69587095b39a..62043fc700e5 100644 --- a/astropy/wcs/src/sip.c +++ b/astropy/wcs/src/sip.c @@ -3,11 +3,14 @@ mdroe@stsci.edu */ -#include "sip.h" +#include "astropy_wcs/sip.h" #include #include #include +#include + +#include #define SIP_ERRMSG(status) WCSERR_SET(status) @@ -40,11 +43,11 @@ sip_init( const unsigned int bp_order, const double* bp, const double* crpix /* [2] */) { - unsigned int a_size = 0; - unsigned int b_size = 0; - unsigned int ap_size = 0; - unsigned int bp_size = 0; - unsigned int scratch_size = 0; + size_t a_size = 0u; + size_t b_size = 0u; + size_t ap_size = 0u; + size_t bp_size = 0u; + size_t scratch_size = 0u; int status = 0; struct wcserr** err = NULL; static const char *function = "sip_init"; @@ -69,7 +72,7 @@ sip_init( if (a != NULL) { sip->a_order = a_order; - a_size = (a_order + 1) * (a_order + 1) * sizeof(double); + a_size = (size_t)(a_order + 1u) * (a_order + 1u) * sizeof(double); sip->a = malloc(a_size); if (sip->a == NULL) { sip_free(sip); @@ -83,7 +86,7 @@ sip_init( } sip->b_order = b_order; - b_size = (b_order + 1) * (b_order + 1) * sizeof(double); + b_size = (size_t)(b_order + 1u) * (b_order + 1u) * sizeof(double); sip->b = malloc(b_size); if (sip->b == NULL) { sip_free(sip); @@ -99,7 +102,7 @@ sip_init( if (ap != NULL) { sip->ap_order = ap_order; - ap_size = (ap_order + 1) * (ap_order + 1) * sizeof(double); + ap_size = (size_t)(ap_order + 1u) * (ap_order + 1u) * sizeof(double); sip->ap = malloc(ap_size); if (sip->ap == NULL) { sip_free(sip); @@ -113,7 +116,7 @@ sip_init( } sip->bp_order = bp_order; - bp_size = (bp_order + 1) * (bp_order + 1) * sizeof(double); + bp_size = (size_t)(bp_order + 1u) * (bp_order + 1u) * sizeof(double); sip->bp = malloc(bp_size); if (sip->bp == NULL) { sip_free(sip); @@ -127,15 +130,13 @@ sip_init( } } - if (scratch_size > 0) { - scratch_size = (scratch_size + 1) * sizeof(double); - sip->scratch = malloc(scratch_size); - if (sip->scratch == NULL) { - sip_free(sip); - status = wcserr_set( - SIP_ERRMSG(WCSERR_MEMORY), "Memory allocation failed"); - goto exit; - } + scratch_size = (scratch_size + 1) * sizeof(double); + sip->scratch = malloc(scratch_size); + if (sip->scratch == NULL) { + sip_free(sip); + status = wcserr_set( + SIP_ERRMSG(WCSERR_MEMORY), "Memory allocation failed"); + goto exit; } sip->crpix[0] = crpix[0]; @@ -330,4 +331,3 @@ sip_foc2pix( return sip_foc2deltas(sip, naxes, nelem, foc, pix); } - diff --git a/astropy/wcs/src/sip_wrap.c b/astropy/wcs/src/sip_wrap.c index 4153846354a3..cff85f780bec 100644 --- a/astropy/wcs/src/sip_wrap.c +++ b/astropy/wcs/src/sip_wrap.c @@ -5,8 +5,8 @@ #define NO_IMPORT_ARRAY -#include "sip_wrap.h" -#include "docstrings.h" +#include "astropy_wcs/sip_wrap.h" +#include "astropy_wcs/docstrings.h" #include "wcs.h" static void @@ -14,7 +14,10 @@ PySip_dealloc( PySip* self) { sip_free(&self->x); - Py_TYPE(self)->tp_free((PyObject*)self); + PyTypeObject *tp = Py_TYPE((PyObject*)self); + freefunc free_func = PyType_GetSlot(tp, Py_tp_free); + free_func((PyObject*)self); + Py_DECREF(tp); } /*@null@*/ static PyObject * @@ -24,8 +27,8 @@ PySip_new( /*@unused@*/ PyObject* kwds) { PySip* self; - - self = (PySip*)type->tp_alloc(type, 0); + allocfunc alloc_func = PyType_GetSlot(type, Py_tp_alloc); + self = (PySip*)alloc_func(type, 0); if (self != NULL) { sip_clear(&self->x); } @@ -47,7 +50,7 @@ convert_matrix( } *array = (PyArrayObject*)PyArray_ContiguousFromAny( - pyobj, PyArray_DOUBLE, 2, 2); + pyobj, NPY_DOUBLE, 2, 2); if (*array == NULL) { return -1; } @@ -102,7 +105,7 @@ PySip_init( goto exit; } - crpix = (PyArrayObject*)PyArray_ContiguousFromAny(py_crpix, PyArray_DOUBLE, + crpix = (PyArrayObject*)PyArray_ContiguousFromAny(py_crpix, NPY_DOUBLE, 1, 1); if (crpix == NULL) { goto exit; @@ -121,11 +124,11 @@ PySip_init( PyArray_DATA(crpix)); exit: - Py_XDECREF(a); - Py_XDECREF(b); - Py_XDECREF(ap); - Py_XDECREF(bp); - Py_XDECREF(crpix); + Py_XDECREF((PyObject*)a); + Py_XDECREF((PyObject*)b); + Py_XDECREF((PyObject*)ap); + Py_XDECREF((PyObject*)bp); + Py_XDECREF((PyObject*)crpix); if (status == 0) { return 0; @@ -148,6 +151,9 @@ PySip_pix2foc( int origin = 1; PyArrayObject* pixcrd = NULL; PyArrayObject* foccrd = NULL; + double* foccrd_data = NULL; + unsigned int nelem = 0; + unsigned int i, j; int status = -1; const char* keywords[] = { "pixcrd", "origin", NULL }; @@ -164,7 +170,7 @@ PySip_pix2foc( return NULL; } - pixcrd = (PyArrayObject*)PyArray_ContiguousFromAny(pixcrd_obj, PyArray_DOUBLE, 2, 2); + pixcrd = (PyArrayObject*)PyArray_ContiguousFromAny(pixcrd_obj, NPY_DOUBLE, 2, 2); if (pixcrd == NULL) { goto exit; } @@ -175,7 +181,7 @@ PySip_pix2foc( } foccrd = (PyArrayObject*)PyArray_SimpleNew(2, PyArray_DIMS(pixcrd), - PyArray_DOUBLE); + NPY_DOUBLE); if (foccrd == NULL) { goto exit; } @@ -188,17 +194,26 @@ PySip_pix2foc( (const double*)PyArray_DATA(pixcrd), (double*)PyArray_DATA(foccrd)); unoffset_array(pixcrd, origin); + + /* Adjust for crpix */ + foccrd_data = (double *)PyArray_DATA(foccrd); + nelem = (unsigned int)PyArray_DIM(foccrd, 0); + for (i = 0; i < nelem; ++i) { + for (j = 0; j < 2; ++j) { + foccrd_data[i*2 + j] -= self->x.crpix[j]; + } + } unoffset_array(foccrd, origin); Py_END_ALLOW_THREADS exit: - Py_XDECREF(pixcrd); + Py_XDECREF((PyObject*)pixcrd); if (status == 0) { return (PyObject*)foccrd; } else { - Py_XDECREF(foccrd); + Py_XDECREF((PyObject*)foccrd); if (status == -1) { /* Exception already set */ return NULL; @@ -220,6 +235,9 @@ PySip_foc2pix( PyArrayObject* foccrd = NULL; PyArrayObject* pixcrd = NULL; int status = -1; + double* foccrd_data = NULL; + unsigned int nelem = 0; + unsigned int i, j; const char* keywords[] = { "foccrd", "origin", NULL }; @@ -235,7 +253,7 @@ PySip_foc2pix( return NULL; } - foccrd = (PyArrayObject*)PyArray_ContiguousFromAny(foccrd_obj, PyArray_DOUBLE, 2, 2); + foccrd = (PyArrayObject*)PyArray_ContiguousFromAny(foccrd_obj, NPY_DOUBLE, 2, 2); if (foccrd == NULL) { goto exit; } @@ -246,7 +264,7 @@ PySip_foc2pix( } pixcrd = (PyArrayObject*)PyArray_SimpleNew(2, PyArray_DIMS(foccrd), - PyArray_DOUBLE); + NPY_DOUBLE); if (pixcrd == NULL) { status = 2; goto exit; @@ -254,22 +272,38 @@ PySip_foc2pix( Py_BEGIN_ALLOW_THREADS preoffset_array(foccrd, origin); + /* Adjust for crpix */ + foccrd_data = (double *)PyArray_DATA(foccrd); + nelem = (unsigned int)PyArray_DIM(foccrd, 0); + for (i = 0; i < nelem; ++i) { + for (j = 0; j < 2; ++j) { + foccrd_data[i*2 + j] += self->x.crpix[j]; + } + } + status = sip_foc2pix(&self->x, (unsigned int)PyArray_DIM(pixcrd, 1), (unsigned int)PyArray_DIM(pixcrd, 0), (double*)PyArray_DATA(foccrd), (double*)PyArray_DATA(pixcrd)); + + /* Adjust for crpix */ + for (i = 0; i < nelem; ++i) { + for (j = 0; j < 2; ++j) { + foccrd_data[i*2 + j] -= self->x.crpix[j]; + } + } unoffset_array(foccrd, origin); unoffset_array(pixcrd, origin); Py_END_ALLOW_THREADS exit: - Py_XDECREF(foccrd); + Py_XDECREF((PyObject*)foccrd); if (status == 0) { return (PyObject*)pixcrd; } else { - Py_XDECREF(pixcrd); + Py_XDECREF((PyObject*)pixcrd); if (status == -1) { /* Exception already set */ return NULL; @@ -402,7 +436,7 @@ PySip___copy__( PySip* copy = NULL; - copy = (PySip*)PySip_new(&PySipType, NULL, NULL); + copy = (PySip*)PySip_new((PyTypeObject*)PySipType, NULL, NULL); if (copy == NULL) { return NULL; } @@ -442,59 +476,32 @@ static PyMethodDef PySip_methods[] = { {NULL} }; -PyTypeObject PySipType = { - #if PY3K - PyVarObject_HEAD_INIT(NULL, 0) - #else - PyObject_HEAD_INIT(NULL) - 0, /*ob_size*/ - #endif - "astropy.wcs.Sip", /*tp_name*/ - sizeof(PySip), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - (destructor)PySip_dealloc, /*tp_dealloc*/ - 0, /*tp_print*/ - 0, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_compare*/ - 0, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - 0, /*tp_hash */ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ - doc_Sip, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - PySip_methods, /* tp_methods */ - 0, /* tp_members */ - PySip_getset, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - (initproc)PySip_init, /* tp_init */ - 0, /* tp_alloc */ - PySip_new, /* tp_new */ +static PyType_Spec PySipType_spec = { + .name = "astropy.wcs.Sip", + .basicsize = sizeof(PySip), + .itemsize = 0, + .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_IMMUTABLETYPE, + .slots = (PyType_Slot[]){ + {Py_tp_dealloc, (destructor)PySip_dealloc}, + {Py_tp_doc, doc_Sip}, + {Py_tp_methods, PySip_methods}, + {Py_tp_getset, PySip_getset}, + {Py_tp_init, (initproc)PySip_init}, + {Py_tp_new, PySip_new}, + {0, NULL}, + }, }; +PyObject* PySipType = NULL; + int _setup_sip_type( PyObject* m) { - if (PyType_Ready(&PySipType) < 0) + PySipType = PyType_FromSpec(&PySipType_spec); + + if (PySipType == NULL) return -1; - Py_INCREF(&PySipType); - return PyModule_AddObject(m, "Sip", (PyObject *)&PySipType); + return PyModule_AddObject(m, "Sip", PySipType); } diff --git a/astropy/wcs/src/str_list_proxy.c b/astropy/wcs/src/str_list_proxy.c index 59a0552f8a83..c0d283fa1c76 100644 --- a/astropy/wcs/src/str_list_proxy.c +++ b/astropy/wcs/src/str_list_proxy.c @@ -5,13 +5,14 @@ #define NO_IMPORT_ARRAY -#include "pyutil.h" +#include "astropy_wcs/pyutil.h" +#include // malloc, free /*************************************************************************** * List-of-strings proxy object ***************************************************************************/ -static PyTypeObject PyStrListProxyType; +static PyObject* PyStrListProxyType; typedef struct { PyObject_HEAD @@ -19,15 +20,18 @@ typedef struct { Py_ssize_t size; Py_ssize_t maxsize; char (*array)[72]; - str_verify_fn verify; } PyStrListProxy; static void PyStrListProxy_dealloc( PyStrListProxy* self) { + PyObject_GC_UnTrack(self); Py_XDECREF(self->pyobject); - Py_TYPE(self)->tp_free((PyObject*)self); + PyTypeObject *tp = Py_TYPE((PyObject*)self); + freefunc free_func = PyType_GetSlot(tp, Py_tp_free); + free_func((PyObject*)self); + Py_DECREF(tp); } /*@null@*/ static PyObject * @@ -38,7 +42,8 @@ PyStrListProxy_new( PyStrListProxy* self = NULL; - self = (PyStrListProxy*)type->tp_alloc(type, 0); + allocfunc alloc_func = PyType_GetSlot(type, Py_tp_alloc); + self = (PyStrListProxy*)alloc_func(type, 0); if (self != NULL) { self->pyobject = NULL; } @@ -51,15 +56,8 @@ PyStrListProxy_traverse( visitproc visit, void *arg) { - int vret; - - if (self->pyobject) { - vret = visit(self->pyobject, arg); - if (vret != 0) { - return vret; - } - } - + Py_VISIT(self->pyobject); + Py_VISIT((PyObject*)Py_TYPE((PyObject*)self)); return 0; } @@ -67,11 +65,7 @@ static int PyStrListProxy_clear( PyStrListProxy *self) { - PyObject *tmp; - - tmp = self->pyobject; - self->pyobject = NULL; - Py_XDECREF(tmp); + Py_CLEAR(self->pyobject); return 0; } @@ -81,8 +75,7 @@ PyStrListProxy_New( /*@shared@*/ PyObject* owner, Py_ssize_t size, Py_ssize_t maxsize, - char (*array)[72], - str_verify_fn verify) { + char (*array)[72]) { PyStrListProxy* self = NULL; @@ -90,7 +83,9 @@ PyStrListProxy_New( maxsize = 68; } - self = (PyStrListProxy*)PyStrListProxyType.tp_alloc(&PyStrListProxyType, 0); + PyTypeObject* tp = (PyTypeObject*)PyStrListProxyType; + allocfunc alloc_func = PyType_GetSlot(tp, Py_tp_alloc); + self = (PyStrListProxy*)alloc_func(tp, 0); if (self == NULL) { return NULL; } @@ -100,7 +95,6 @@ PyStrListProxy_New( self->size = size; self->maxsize = maxsize; self->array = array; - self->verify = verify; return (PyObject*)self; } @@ -116,16 +110,12 @@ PyStrListProxy_getitem( PyStrListProxy* self, Py_ssize_t index) { - if (index >= self->size) { + if (index >= self->size || index < 0) { PyErr_SetString(PyExc_IndexError, "index out of range"); return NULL; } - #if PY3K - return PyBytes_FromString(self->array[index]); - #else - return PyString_FromString(self->array[index]); - #endif + return get_string("string", self->array[index]); } static int @@ -134,40 +124,19 @@ PyStrListProxy_setitem( Py_ssize_t index, PyObject* arg) { - char* value; - Py_ssize_t value_length; - - if (index > self->size) { + if (index >= self->size || index < 0) { PyErr_SetString(PyExc_IndexError, "index out of range"); return -1; } - #if PY3K - if (PyBytes_AsStringAndSize(arg, &value, &value_length)) { - #else - if (PyString_AsStringAndSize(arg, &value, &value_length)) { - #endif - return -1; - } - - if (value_length >= self->maxsize) { - PyErr_Format(PyExc_ValueError, - "string must be less than %zd characters", self->maxsize); - return -1; - } - - if (self->verify && !self->verify(value)) { - return -1; - } - - strncpy(self->array[index], value, self->maxsize); - - return 0; + return set_string("string", arg, self->array[index], self->maxsize); } -/*@null@*/ static PyObject* -PyStrListProxy_repr( - PyStrListProxy* self) { +/*@null@*/ PyObject* +str_list_proxy_repr( + char (*array)[72], + Py_ssize_t size, + Py_ssize_t maxsize) { char* buffer = NULL; char* wp = NULL; @@ -182,7 +151,7 @@ PyStrListProxy_repr( char next_char = '\0'; /* Overallocating to allow for escaped characters */ - buffer = malloc((size_t)self->size*self->maxsize*2 + 2); + buffer = malloc((size_t)size*maxsize*2 + 2); if (buffer == NULL) { PyErr_SetString(PyExc_MemoryError, "Could not allocate memory."); return NULL; @@ -191,10 +160,10 @@ PyStrListProxy_repr( wp = buffer; *wp++ = '['; - for (i = 0; i < self->size; ++i) { + for (i = 0; i < size; ++i) { *wp++ = '\''; - rp = self->array[i]; - for (j = 0; j < self->maxsize && *rp != '\0'; ++j) { + rp = array[i]; + for (j = 0; j < maxsize && *rp != '\0'; ++j) { /* Check if this character should be escaped */ e = escapes; next_char = *rp++; @@ -215,7 +184,7 @@ PyStrListProxy_repr( *wp++ = '\''; /* Add a comma for all but the last one */ - if (i != self->size - 1) { + if (i != size - 1) { *wp++ = ','; *wp++ = ' '; } @@ -224,79 +193,45 @@ PyStrListProxy_repr( *wp++ = ']'; *wp++ = '\0'; - #if PY3K result = PyUnicode_FromString(buffer); - #else - result = PyString_FromString(buffer); - #endif free(buffer); return result; } -static PySequenceMethods PyStrListProxy_sequence_methods = { - (lenfunc)PyStrListProxy_len, - NULL, - NULL, - (ssizeargfunc)PyStrListProxy_getitem, - NULL, - (ssizeobjargproc)PyStrListProxy_setitem, - NULL, - NULL, - NULL, - NULL -}; +/*@null@*/ static PyObject* +PyStrListProxy_repr( + PyStrListProxy* self) { -static PyTypeObject PyStrListProxyType = { - #if PY3K - PyVarObject_HEAD_INIT(NULL, 0) - #else - PyObject_HEAD_INIT(NULL) - 0, /*ob_size*/ - #endif - "astropy.wcs.StrListProxy", /*tp_name*/ - sizeof(PyStrListProxy), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - (destructor)PyStrListProxy_dealloc, /*tp_dealloc*/ - 0, /*tp_print*/ - 0, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_compare*/ - (reprfunc)PyStrListProxy_repr, /*tp_repr*/ - 0, /*tp_as_number*/ - &PyStrListProxy_sequence_methods, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - 0, /*tp_hash */ - 0, /*tp_call*/ - (reprfunc)PyStrListProxy_repr, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /*tp_flags*/ - 0, /* tp_doc */ - (traverseproc)PyStrListProxy_traverse, /* tp_traverse */ - (inquiry)PyStrListProxy_clear, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - 0, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - PyStrListProxy_new, /* tp_new */ + return str_list_proxy_repr(self->array, self->size, self->maxsize); +} + +static PyType_Spec PyStrListProxyType_spec = { + .name = "astropy.wcs.StrListProxy", + .basicsize = sizeof(PyStrListProxy), + .itemsize = 0, + .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, + .slots = (PyType_Slot[]){ + {Py_tp_dealloc, (destructor)PyStrListProxy_dealloc}, + {Py_tp_repr, (reprfunc)PyStrListProxy_repr}, + {Py_sq_length, (lenfunc)PyStrListProxy_len}, + {Py_sq_item, (ssizeargfunc)PyStrListProxy_getitem}, + {Py_sq_ass_item, (ssizeobjargproc)PyStrListProxy_setitem}, + {Py_tp_str, (reprfunc)PyStrListProxy_repr}, + {Py_tp_traverse, (traverseproc)PyStrListProxy_traverse}, + {Py_tp_clear, (inquiry)PyStrListProxy_clear}, + {Py_tp_new, (newfunc)PyStrListProxy_new}, + {0, NULL}, + }, }; +static PyObject* PyStrListProxyType = NULL; + int _setup_str_list_proxy_type( /*@unused@*/ PyObject* m) { - if (PyType_Ready(&PyStrListProxyType) < 0) { + PyStrListProxyType = PyType_FromSpec(&PyStrListProxyType_spec); + if (PyStrListProxyType == NULL) { return 1; } diff --git a/astropy/wcs/src/unit_list_proxy.c b/astropy/wcs/src/unit_list_proxy.c new file mode 100644 index 000000000000..169815ed6398 --- /dev/null +++ b/astropy/wcs/src/unit_list_proxy.c @@ -0,0 +1,370 @@ +/* + Author: Michael Droettboom + mdroe@stsci.edu +*/ + +#define NO_IMPORT_ARRAY + +#include "astropy_wcs/pyutil.h" +#include "astropy_wcs/str_list_proxy.h" +#include // strncmp + +/*************************************************************************** + * List-of-units proxy object + ***************************************************************************/ + +#define MAXSIZE 68 +#define ARRAYSIZE 72 + +static PyObject* PyUnitListProxyType; + +typedef struct { + PyObject_HEAD + /*@null@*/ /*@shared@*/ PyObject* pyobject; + Py_ssize_t size; + char (*array)[ARRAYSIZE]; + PyObject* unit_class; + int readonly; +} PyUnitListProxy; + +static void +PyUnitListProxy_dealloc( + PyUnitListProxy* self) { + + PyObject_GC_UnTrack(self); + Py_XDECREF(self->pyobject); + PyTypeObject *tp = Py_TYPE((PyObject*)self); + freefunc free_func = PyType_GetSlot(tp, Py_tp_free); + free_func((PyObject*)self); + Py_DECREF(tp); +} + +/*@null@*/ static PyObject * +PyUnitListProxy_new( + PyTypeObject* type, + /*@unused@*/ PyObject* args, + /*@unused@*/ PyObject* kwds) { + + PyUnitListProxy* self = NULL; + + allocfunc alloc_func = PyType_GetSlot(type, Py_tp_alloc); + self = (PyUnitListProxy*)alloc_func(type, 0); + if (self != NULL) { + self->pyobject = NULL; + self->unit_class = NULL; + } + return (PyObject*)self; +} + +static int +PyUnitListProxy_traverse( + PyUnitListProxy* self, + visitproc visit, + void *arg) { + + Py_VISIT(self->pyobject); + Py_VISIT(self->unit_class); + Py_VISIT((PyObject*)Py_TYPE((PyObject*)self)); + return 0; +} + +static int +PyUnitListProxy_clear( + PyUnitListProxy *self) { + + Py_CLEAR(self->pyobject); + Py_CLEAR(self->unit_class); + + return 0; +} + +/*@null@*/ PyObject * +PyUnitListProxy_New( + /*@shared@*/ PyObject* owner, + Py_ssize_t size, + char (*array)[ARRAYSIZE], + int readonly) { + + PyUnitListProxy* self = NULL; + PyObject *units_module; + PyObject *units_dict; + PyObject *unit_class; + + units_module = PyImport_ImportModule("astropy.units"); + if (units_module == NULL) { + return NULL; + } + + units_dict = PyModule_GetDict(units_module); + if (units_dict == NULL) { + return NULL; + } + + unit_class = PyDict_GetItemString(units_dict, "Unit"); + if (unit_class == NULL) { + PyErr_SetString(PyExc_RuntimeError, "Could not import Unit class"); + return NULL; + } + + Py_INCREF(unit_class); + + PyTypeObject* type = (PyTypeObject*)PyUnitListProxyType; + allocfunc alloc_func = PyType_GetSlot(type, Py_tp_alloc); + self = (PyUnitListProxy*)alloc_func(type, 0); + if (self == NULL) { + return NULL; + } + + Py_XINCREF(owner); + self->pyobject = owner; + self->size = size; + self->array = array; + self->unit_class = unit_class; + self->readonly = readonly; + return (PyObject*)self; +} + +static Py_ssize_t +PyUnitListProxy_len( + PyUnitListProxy* self) { + + return self->size; +} + +static PyObject* +_get_unit( + PyObject *unit_class, + PyObject *unit) { + + PyObject *args; + PyObject *kw; + PyObject *result; + + kw = Py_BuildValue("{s:s,s:s}", "format", "fits", "parse_strict", "warn"); + if (kw == NULL) { + return NULL; + } + + args = PyTuple_New(1); + if (args == NULL) { + Py_DECREF(kw); + return NULL; + } + PyTuple_SetItem(args, 0, unit); + Py_INCREF(unit); + + result = PyObject_Call(unit_class, args, kw); + + Py_DECREF(args); + Py_DECREF(kw); + return result; +} + +/*@null@*/ static PyObject* +PyUnitListProxy_getitem( + PyUnitListProxy* self, + Py_ssize_t index) { + + PyObject *value; + PyObject *result; + + if (index >= self->size || index < 0) { + PyErr_SetString(PyExc_IndexError, "index out of range"); + return NULL; + } + + value = PyUnicode_FromString(self->array[index]); + + result = _get_unit(self->unit_class, value); + + Py_DECREF(value); + return result; +} + +static PyObject* +PyUnitListProxy_richcmp( + PyObject *a, + PyObject *b, + int op){ + PyUnitListProxy *lhs, *rhs; + Py_ssize_t idx; + int equal = 1; + assert(a != NULL && b != NULL); + if (!PyObject_TypeCheck(a, (PyTypeObject*)PyUnitListProxyType) || + !PyObject_TypeCheck(b, (PyTypeObject*)PyUnitListProxyType)) { + Py_RETURN_NOTIMPLEMENTED; + } + if (op != Py_EQ && op != Py_NE) { + Py_RETURN_NOTIMPLEMENTED; + } + + /* The actual comparison of the two objects. unit_class is ignored because + * it's not an essential property of the instances. + */ + lhs = (PyUnitListProxy *)a; + rhs = (PyUnitListProxy *)b; + if (lhs->size != rhs->size) { + equal = 0; + } + for (idx = 0; idx < lhs->size && equal == 1; idx++) { + if (strncmp(lhs->array[idx], rhs->array[idx], ARRAYSIZE) != 0) { + equal = 0; + } + } + if ((op == Py_EQ && equal == 1) || + (op == Py_NE && equal == 0)) { + Py_RETURN_TRUE; + } else { + Py_RETURN_FALSE; + } +} + +static int +PyUnitListProxy_setitem( + PyUnitListProxy* self, + Py_ssize_t index, + PyObject* arg) { + + if (self->readonly) { + PyErr_SetString(PyExc_RuntimeError, "Cannot set individual units in-place once set() has been called when using preserve_units=True"); + return -1; + } + + PyObject* value; + PyObject* unicode_value; + PyObject* bytes_value; + + if (index >= self->size || index < 0) { + PyErr_SetString(PyExc_IndexError, "index out of range"); + return -1; + } + + value = _get_unit(self->unit_class, arg); + if (value == NULL) { + return -1; + } + + unicode_value = PyObject_CallMethod(value, "to_string", "s", "fits"); + if (unicode_value == NULL) { + Py_DECREF(value); + return -1; + } + Py_DECREF(value); + + if (PyUnicode_Check(unicode_value)) { + bytes_value = PyUnicode_AsASCIIString(unicode_value); + if (bytes_value == NULL) { + Py_DECREF(unicode_value); + return -1; + } + Py_DECREF(unicode_value); + } else { + bytes_value = unicode_value; + } + + strncpy(self->array[index], PyBytes_AsString(bytes_value), MAXSIZE); + Py_DECREF(bytes_value); + + return 0; +} + +/*@null@*/ static PyObject* +PyUnitListProxy_repr( + PyUnitListProxy* self) { + + return str_list_proxy_repr(self->array, self->size, MAXSIZE); +} + +static PyType_Spec PyUnitListProxyType_spec = { + .name = "astropy.wcs.UnitListProxy", + .basicsize = sizeof(PyUnitListProxy), + .itemsize = 0, + .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_IMMUTABLETYPE, + .slots = (PyType_Slot[]){ + {Py_tp_dealloc, (destructor)PyUnitListProxy_dealloc}, + {Py_tp_repr, (reprfunc)PyUnitListProxy_repr}, + {Py_tp_str, (reprfunc)PyUnitListProxy_repr}, + {Py_tp_traverse, (traverseproc)PyUnitListProxy_traverse}, + {Py_tp_clear, (inquiry)PyUnitListProxy_clear}, + {Py_tp_richcompare, (richcmpfunc)PyUnitListProxy_richcmp}, + {Py_tp_new, (newfunc)PyUnitListProxy_new}, + {Py_sq_length, (lenfunc)PyUnitListProxy_len}, + {Py_sq_item, (ssizeargfunc)PyUnitListProxy_getitem}, + {Py_sq_ass_item, (ssizeobjargproc)PyUnitListProxy_setitem}, + {0, NULL}, + }, +}; + +static PyObject* PyUnitListProxyType = NULL; + +int +set_unit_list( + PyObject* owner, + const char* propname, + PyObject* value, + Py_ssize_t len, + char (*dest)[ARRAYSIZE]) { + + PyObject* unit = NULL; + PyObject* proxy = NULL; + Py_ssize_t i = 0; + + if (check_delete(propname, value)) { + return -1; + } + + if (!PySequence_Check(value)) { + PyErr_Format( + PyExc_TypeError, + "'%s' must be a sequence of strings", + propname); + return -1; + } + + if (PySequence_Size(value) != len) { + PyErr_Format( + PyExc_ValueError, + "len(%s) must be %u", + propname, + (unsigned int)len); + return -1; + } + + proxy = PyUnitListProxy_New(owner, len, dest, 0); + if (proxy == NULL) { + return -1; + } + + for (i = 0; i < len; ++i) { + unit = PySequence_GetItem(value, i); + if (unit == NULL) { + Py_DECREF(proxy); + return -1; + } + + if (PySequence_SetItem(proxy, i, unit) == -1) { + Py_DECREF(proxy); + Py_DECREF(unit); + return -1; + } + + Py_DECREF(unit); + } + + Py_DECREF(proxy); + + return 0; +} + + +int +_setup_unit_list_proxy_type( + /*@unused@*/ PyObject* m) { + + PyUnitListProxyType = PyType_FromSpec(&PyUnitListProxyType_spec); + if (PyUnitListProxyType == NULL) { + return 1; + } + + return 0; +} diff --git a/astropy/wcs/src/util.c b/astropy/wcs/src/util.c index 2c1532b78916..53ded280b2d8 100644 --- a/astropy/wcs/src/util.c +++ b/astropy/wcs/src/util.c @@ -5,7 +5,7 @@ #define NO_IMPORT_ARRAY -#include "util.h" +#include "astropy_wcs/util.h" #include #include @@ -26,12 +26,23 @@ void set_invalid_to_nan( #define NAN (INF-INF) #endif + // Note that stat is a bit mask, so we need to mask only some of + // the coordinates depending on the bit mask values. + n = NAN; for ( ; s != s_end; ++s) { if (*s) { + int bit = 1; for (i = 0; i < nelem; ++i) { - *d++ = n; + if (*s & bit) { + *d = n; + } + d++; + // We don't need to worry about overflow here because the WCS + // class cannot be used for naxis > 15 so nelem will always + // be <=15. + bit <<= 1; } } else { d += nelem; diff --git a/astropy/wcs/src/wcslib_auxprm_wrap.c b/astropy/wcs/src/wcslib_auxprm_wrap.c new file mode 100644 index 000000000000..16d249a2de3f --- /dev/null +++ b/astropy/wcs/src/wcslib_auxprm_wrap.c @@ -0,0 +1,372 @@ +#define NO_IMPORT_ARRAY + +#include "astropy_wcs/wcslib_auxprm_wrap.h" + +#include +#include +#include + +/* + It gets to be really tedious to type long docstrings in ANSI C syntax + (since multi-line strings literals are not valid). Therefore, the + docstrings are written in doc/docstrings.py, which are then converted + by setup.py into docstrings.h, which we include here. +*/ +#include "astropy_wcs/docstrings.h" + + +/*************************************************************************** + * PyAuxprm methods * + ***************************************************************************/ + +static PyObject* +PyAuxprm_new(PyTypeObject* type, PyObject* args, PyObject* kwds) { + PyAuxprm* self; + + allocfunc alloc_func = PyType_GetSlot(type, Py_tp_alloc); + self = (PyAuxprm*)alloc_func(type, 0); + return (PyObject*)self; +} + + +static int +PyAuxprm_traverse(PyAuxprm* self, visitproc visit, void *arg) { + Py_VISIT(self->owner); + Py_VISIT((PyObject*)Py_TYPE((PyObject*)self)); + return 0; +} + + +static int +PyAuxprm_clear(PyAuxprm* self) { + Py_CLEAR(self->owner); + return 0; +} + + +static void PyAuxprm_dealloc(PyAuxprm* self) { + PyAuxprm_clear(self); + PyTypeObject *tp = Py_TYPE((PyObject*)self); + freefunc free_func = PyType_GetSlot(tp, Py_tp_free); + free_func((PyObject*)self); + Py_DECREF(tp); +} + + +PyAuxprm* PyAuxprm_cnew(PyObject* wcsprm, struct auxprm* x) { + PyAuxprm* self; + PyTypeObject* type = (PyTypeObject*)PyAuxprmType; + allocfunc alloc_func = PyType_GetSlot(type, Py_tp_alloc); + self = (PyAuxprm*)alloc_func(type, 0); + if (self == NULL) return NULL; + self->x = x; + Py_INCREF(wcsprm); + self->owner = wcsprm; + return self; +} + + +static void auxprmprt(const struct auxprm *aux) { + + if (aux == 0x0) return; + + wcsprintf("rsun_ref:"); + if (aux->rsun_ref != UNDEFINED) wcsprintf(" %f", aux->rsun_ref); + wcsprintf("\ndsun_obs:"); + if (aux->dsun_obs != UNDEFINED) wcsprintf(" %f", aux->dsun_obs); + wcsprintf("\ncrln_obs:"); + if (aux->crln_obs != UNDEFINED) wcsprintf(" %f", aux->crln_obs); + wcsprintf("\nhgln_obs:"); + if (aux->hgln_obs != UNDEFINED) wcsprintf(" %f", aux->hgln_obs); + wcsprintf("\nhglt_obs:"); + if (aux->hglt_obs != UNDEFINED) wcsprintf(" %f", aux->hglt_obs); + wcsprintf("\na_radius:"); + if (aux->a_radius != UNDEFINED) wcsprintf(" %f", aux->a_radius); + wcsprintf("\nb_radius:"); + if (aux->b_radius != UNDEFINED) wcsprintf(" %f", aux->b_radius); + wcsprintf("\nc_radius:"); + if (aux->c_radius != UNDEFINED) wcsprintf(" %f", aux->c_radius); + wcsprintf("\nbdis_obs:"); + if (aux->bdis_obs != UNDEFINED) wcsprintf(" %f", aux->bdis_obs); + wcsprintf("\nblon_obs:"); + if (aux->blon_obs != UNDEFINED) wcsprintf(" %f", aux->blon_obs); + wcsprintf("\nblat_obs:"); + if (aux->blat_obs != UNDEFINED) wcsprintf(" %f", aux->blat_obs); + return; +} + + +static PyObject* PyAuxprm___str__(PyAuxprm* self) { + /* This is not thread-safe, but since we're holding onto the GIL, + we can assume we won't have thread conflicts */ + wcsprintf_set(NULL); + auxprmprt(self->x); + return PyUnicode_FromString(wcsprintf_buf()); +} + + +/*************************************************************************** + * Member getters/setters (properties) + */ + +static PyObject* PyAuxprm_get_rsun_ref(PyAuxprm* self, void* closure) { + if(self->x == NULL || self->x->rsun_ref == UNDEFINED) { + Py_RETURN_NONE; + } else { + return get_double("rsun_ref", self->x->rsun_ref); + } +} + +static int PyAuxprm_set_rsun_ref(PyAuxprm* self, PyObject* value, void* closure) { + if(self->x == NULL) { + return -1; + } else if (value == Py_None) { + self->x->rsun_ref = UNDEFINED; + return 0; + } else { + return set_double("rsun_ref", value, &self->x->rsun_ref); + } +} + +static PyObject* PyAuxprm_get_dsun_obs(PyAuxprm* self, void* closure) { + if(self->x == NULL || self->x->dsun_obs == UNDEFINED) { + Py_RETURN_NONE; + } else { + return get_double("dsun_obs", self->x->dsun_obs); + } +} + +static int PyAuxprm_set_dsun_obs(PyAuxprm* self, PyObject* value, void* closure) { + if(self->x == NULL) { + return -1; + } else if (value == Py_None) { + self->x->dsun_obs = UNDEFINED; + return 0; + } else { + return set_double("dsun_obs", value, &self->x->dsun_obs); + } +} + +static PyObject* PyAuxprm_get_crln_obs(PyAuxprm* self, void* closure) { + if(self->x == NULL || self->x->crln_obs == UNDEFINED) { + Py_RETURN_NONE; + } else { + return get_double("crln_obs", self->x->crln_obs); + } +} + +static int PyAuxprm_set_crln_obs(PyAuxprm* self, PyObject* value, void* closure) { + if(self->x == NULL) { + return -1; + } else if (value == Py_None) { + self->x->crln_obs = UNDEFINED; + return 0; + } else { + return set_double("crln_obs", value, &self->x->crln_obs); + } +} + +static PyObject* PyAuxprm_get_hgln_obs(PyAuxprm* self, void* closure) { + if(self->x == NULL || self->x->hgln_obs == UNDEFINED) { + Py_RETURN_NONE; + } else { + return get_double("hgln_obs", self->x->hgln_obs); + } +} + +static int PyAuxprm_set_hgln_obs(PyAuxprm* self, PyObject* value, void* closure) { + if(self->x == NULL) { + return -1; + } else if (value == Py_None) { + self->x->hgln_obs = UNDEFINED; + return 0; + } else { + return set_double("hgln_obs", value, &self->x->hgln_obs); + } +} + +static PyObject* PyAuxprm_get_hglt_obs(PyAuxprm* self, void* closure) { + if(self->x == NULL || self->x->hglt_obs == UNDEFINED) { + Py_RETURN_NONE; + } else { + return get_double("hglt_obs", self->x->hglt_obs); + } +} + +static int PyAuxprm_set_hglt_obs(PyAuxprm* self, PyObject* value, void* closure) { + if(self->x == NULL) { + return -1; + } else if (value == Py_None) { + self->x->hglt_obs = UNDEFINED; + return 0; + } else { + return set_double("hglt_obs", value, &self->x->hglt_obs); + } +} + +static PyObject* PyAuxprm_get_a_radius(PyAuxprm* self, void* closure) { + if(self->x == NULL || self->x->a_radius == UNDEFINED) { + Py_RETURN_NONE; + } else { + return get_double("a_radius", self->x->a_radius); + } +} + +static int PyAuxprm_set_a_radius(PyAuxprm* self, PyObject* value, void* closure) { + if(self->x == NULL) { + return -1; + } else if (value == Py_None) { + self->x->a_radius = UNDEFINED; + return 0; + } else { + return set_double("a_radius", value, &self->x->a_radius); + } +} + +static PyObject* PyAuxprm_get_b_radius(PyAuxprm* self, void* closure) { + if(self->x == NULL || self->x->b_radius == UNDEFINED) { + Py_RETURN_NONE; + } else { + return get_double("b_radius", self->x->b_radius); + } +} + +static int PyAuxprm_set_b_radius(PyAuxprm* self, PyObject* value, void* closure) { + if(self->x == NULL) { + return -1; + } else if (value == Py_None) { + self->x->b_radius = UNDEFINED; + return 0; + } else { + return set_double("b_radius", value, &self->x->b_radius); + } +} + +static PyObject* PyAuxprm_get_c_radius(PyAuxprm* self, void* closure) { + if(self->x == NULL || self->x->c_radius == UNDEFINED) { + Py_RETURN_NONE; + } else { + return get_double("c_radius", self->x->c_radius); + } +} + +static int PyAuxprm_set_c_radius(PyAuxprm* self, PyObject* value, void* closure) { + if(self->x == NULL) { + return -1; + } else if (value == Py_None) { + self->x->c_radius = UNDEFINED; + return 0; + } else { + return set_double("c_radius", value, &self->x->c_radius); + } +} + +static PyObject* PyAuxprm_get_bdis_obs(PyAuxprm* self, void* closure) { + if(self->x == NULL || self->x->bdis_obs == UNDEFINED) { + Py_RETURN_NONE; + } else { + return get_double("bdis_obs", self->x->bdis_obs); + } +} + +static int PyAuxprm_set_bdis_obs(PyAuxprm* self, PyObject* value, void* closure) { + if(self->x == NULL) { + return -1; + } else if (value == Py_None) { + self->x->bdis_obs = UNDEFINED; + return 0; + } else { + return set_double("bdis_obs", value, &self->x->bdis_obs); + } +} + +static PyObject* PyAuxprm_get_blon_obs(PyAuxprm* self, void* closure) { + if(self->x == NULL || self->x->blon_obs == UNDEFINED) { + Py_RETURN_NONE; + } else { + return get_double("blon_obs", self->x->blon_obs); + } +} + +static int PyAuxprm_set_blon_obs(PyAuxprm* self, PyObject* value, void* closure) { + if(self->x == NULL) { + return -1; + } else if (value == Py_None) { + self->x->blon_obs = UNDEFINED; + return 0; + } else { + return set_double("blon_obs", value, &self->x->blon_obs); + } +} + +static PyObject* PyAuxprm_get_blat_obs(PyAuxprm* self, void* closure) { + if(self->x == NULL || self->x->blat_obs == UNDEFINED) { + Py_RETURN_NONE; + } else { + return get_double("blat_obs", self->x->blat_obs); + } +} + +static int PyAuxprm_set_blat_obs(PyAuxprm* self, PyObject* value, void* closure) { + if(self->x == NULL) { + return -1; + } else if (value == Py_None) { + self->x->blat_obs = UNDEFINED; + return 0; + } else { + return set_double("blat_obs", value, &self->x->blat_obs); + } +} + + +/*************************************************************************** + * PyAuxprm definition structures + */ + +static PyGetSetDef PyAuxprm_getset[] = { + {"rsun_ref", (getter)PyAuxprm_get_rsun_ref, (setter)PyAuxprm_set_rsun_ref, (char *)doc_rsun_ref}, + {"dsun_obs", (getter)PyAuxprm_get_dsun_obs, (setter)PyAuxprm_set_dsun_obs, (char *)doc_dsun_obs}, + {"crln_obs", (getter)PyAuxprm_get_crln_obs, (setter)PyAuxprm_set_crln_obs, (char *)doc_crln_obs}, + {"hgln_obs", (getter)PyAuxprm_get_hgln_obs, (setter)PyAuxprm_set_hgln_obs, (char *)doc_hgln_obs}, + {"hglt_obs", (getter)PyAuxprm_get_hglt_obs, (setter)PyAuxprm_set_hglt_obs, (char *)doc_hglt_obs}, + {"a_radius", (getter)PyAuxprm_get_a_radius, (setter)PyAuxprm_set_a_radius, (char *)doc_a_radius}, + {"b_radius", (getter)PyAuxprm_get_b_radius, (setter)PyAuxprm_set_b_radius, (char *)doc_b_radius}, + {"c_radius", (getter)PyAuxprm_get_c_radius, (setter)PyAuxprm_set_c_radius, (char *)doc_c_radius}, + {"bdis_obs", (getter)PyAuxprm_get_bdis_obs, (setter)PyAuxprm_set_bdis_obs, (char *)doc_bdis_obs}, + {"blon_obs", (getter)PyAuxprm_get_blon_obs, (setter)PyAuxprm_set_blon_obs, (char *)doc_blon_obs}, + {"blat_obs", (getter)PyAuxprm_get_blat_obs, (setter)PyAuxprm_set_blat_obs, (char *)doc_blat_obs}, + {NULL} +}; + +PyType_Spec PyAuxprmType_spec = { + .name = "astropy.wcs.Auxprm", + .basicsize = sizeof(PyAuxprm), + .itemsize = 0, + .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_IMMUTABLETYPE, + .slots = (PyType_Slot[]) { + {Py_tp_dealloc, (destructor)PyAuxprm_dealloc}, + {Py_tp_str, (reprfunc)PyAuxprm___str__}, + {Py_tp_doc, doc_Auxprm}, + {Py_tp_traverse, (traverseproc)PyAuxprm_traverse}, + {Py_tp_clear, (inquiry)PyAuxprm_clear}, + {Py_tp_getset, PyAuxprm_getset}, + // FIXME: this seems logical but this slot wasn't previously set + // maybe a mistake from https://github.com/astropy/astropy/pull/10333 ? + // {Py_tp_new, (void*)PyAuxprm_new}, + {0, NULL} + }, +}; + +PyObject* PyAuxprmType = NULL; + +int +_setup_auxprm_type(PyObject* m) { + PyAuxprmType = PyType_FromSpec(&PyAuxprmType_spec); + if (PyAuxprmType == NULL) { + return -1; + } + + PyModule_AddObject(m, "Auxprm", PyAuxprmType); + + return 0; +} diff --git a/astropy/wcs/src/wcslib_celprm_wrap.c b/astropy/wcs/src/wcslib_celprm_wrap.c new file mode 100644 index 000000000000..e00c1809f7a8 --- /dev/null +++ b/astropy/wcs/src/wcslib_celprm_wrap.c @@ -0,0 +1,470 @@ +#define NO_IMPORT_ARRAY + +#include "astropy_wcs/wcslib_celprm_wrap.h" +#include "astropy_wcs/wcslib_prjprm_wrap.h" + +#include // calloc, malloc, free +#include // memcpy + +#include +#include +#include +#include +#include +#include + +#include + +/* + It gets to be really tedious to type long docstrings in ANSI C syntax + (since multi-line strings literals are not valid). Therefore, the + docstrings are written in doc/docstrings.py, which are then converted + by setup.py into docstrings.h, which we include here. +*/ +#include "astropy_wcs/docstrings.h" +#include "astropy_wcs/wcslib_wrap.h" + + +PyObject** cel_errexc[7]; + + +static int wcslib_cel_to_python_exc(int status) +{ + if (status > 0 && status < 7) { + PyErr_SetString(*cel_errexc[status], cel_errmsg[status]); + } else if (status > 6) { + PyErr_SetString( + PyExc_RuntimeError, + "Unknown WCSLIB celprm-related error occurred."); + } + return status; +} + + +static int is_readonly(PyCelprm* self) +{ + if (self != NULL && self->owner != NULL) { + PyErr_SetString( + PyExc_AttributeError, + "Attribute 'cel' of 'astropy.wcs.Wcsprm' objects is read-only."); + return 1; + } else { + return 0; + } +} + + +static int is_cel_null(PyCelprm* self) +{ + if (self->x == NULL) { + PyErr_SetString( + PyExc_MemoryError, + "Underlying 'celprm' object is NULL."); + return 1; + } else { + return 0; + } +} + + +/*************************************************************************** + * PyCelprm methods * + ***************************************************************************/ + +static PyObject* PyCelprm_new(PyTypeObject* type, PyObject* args, PyObject* kwds) +{ + PyCelprm* self; + allocfunc alloc_func = PyType_GetSlot(type, Py_tp_alloc); + self = (PyCelprm*)alloc_func(type, 0); + if (self == NULL) return NULL; + self->owner = NULL; + self->prefcount = NULL; + + if ((self->x = calloc(1, sizeof(struct celprm))) == 0x0) { + PyErr_SetString(PyExc_MemoryError, + "Could not allocate memory for celprm structure."); + return NULL; + } + if ((self->prefcount = (int*) malloc(sizeof(int))) == 0x0) { + PyErr_SetString(PyExc_MemoryError, "Could not allocate memory."); + free(self->x); + return NULL; + } + + if (wcslib_cel_to_python_exc(celini(self->x))) { + free(self->x); + free(self->prefcount); + return NULL; + } + *(self->prefcount) = 1; + return (PyObject*)self; +} + + +static int PyCelprm_traverse(PyCelprm* self, visitproc visit, void *arg) +{ + Py_VISIT(self->owner); + Py_VISIT((PyObject*)Py_TYPE((PyObject*)self)); + return 0; +} + + +static int PyCelprm_clear(PyCelprm* self) +{ + Py_CLEAR(self->owner); + return 0; +} + + +static void PyCelprm_dealloc(PyCelprm* self) +{ + PyCelprm_clear(self); + wcslib_cel_to_python_exc(celfree(self->x)); // free memory used for err msg + if (self->prefcount && (--(*self->prefcount)) == 0) { + free(self->x); + free(self->prefcount); + } + PyTypeObject *tp = Py_TYPE((PyObject*)self); + freefunc free_func = PyType_GetSlot(tp, Py_tp_free); + free_func((PyObject*)self); + Py_DECREF(tp); +} + + +static int PyCelprm_cset(PyCelprm* self) +{ + if (wcslib_cel_to_python_exc(celset(self->x))) { + return -1; + } + return 0; +} + + +static PyObject* PyCelprm_set(PyCelprm* self) +{ + if (is_readonly(self) || PyCelprm_cset(self)) return NULL; + Py_RETURN_NONE; +} + + +PyCelprm* PyCelprm_cnew(PyObject* wcsprm_obj, struct celprm* x, int* prefcount) +{ + PyCelprm* self; + PyTypeObject* type = (PyTypeObject*)PyCelprmType; + allocfunc alloc_func = PyType_GetSlot(type, Py_tp_alloc); + self = (PyCelprm*)alloc_func(type, 0); + if (self == NULL) return NULL; + self->x = x; + Py_XINCREF(wcsprm_obj); + self->owner = wcsprm_obj; + self->prefcount = prefcount; + if (prefcount) (*prefcount)++; + return self; +} + + +static PyObject* PyCelprm_copy(PyCelprm* self) +{ + PyCelprm* copy = NULL; + copy = PyCelprm_cnew(self->owner, self->x, self->prefcount); + if (copy == NULL) return NULL; + return (PyObject*)copy; +} + + +static PyObject* PyCelprm_deepcopy(PyCelprm* self) +{ + PyCelprm* copy = (PyCelprm*) PyCelprm_new((PyTypeObject*)PyCelprmType, NULL, NULL); + if (copy == NULL) return NULL; + + memcpy(copy->x, self->x, sizeof(struct celprm)); + copy->x->err = NULL; + return (PyObject*)copy; +} + + +static PyObject* PyCelprm___str__(PyCelprm* self) { + /* if (PyCelprm_cset(self)) return NULL; */ + /* This is not thread-safe, but since we're holding onto the GIL, + we can assume we won't have thread conflicts */ + wcsprintf_set(NULL); + if (wcslib_cel_to_python_exc(celprt(self->x))) { + return NULL; + } + return PyUnicode_FromString(wcsprintf_buf()); +} + + +/*************************************************************************** + * Member getters/setters (properties) + */ + + +static PyObject* PyCelprm_get_flag(PyCelprm* self, void* closure) +{ + if (is_cel_null(self)) { + return NULL; + } else { + return get_int("flag", self->x->flag); + } +} + +static PyObject* PyCelprm_get_offset(PyCelprm* self, void* closure) +{ + if (is_cel_null(self)) { + return NULL; + } else { + return get_bool("offset", self->x->offset); + } +} + + +static int PyCelprm_set_offset(PyCelprm* self, PyObject* value, void* closure) +{ + if (is_cel_null(self) || is_readonly(self)) { + return -1; + } else if (value == Py_None) { + self->x->offset = 0; + return 0; + } else { + return set_bool("offset", value, &self->x->offset); + } +} + + +static PyObject* PyCelprm_get_phi0(PyCelprm* self, void* closure) +{ + if (is_cel_null(self)) { + return NULL; + } else if (self->x->phi0 != UNDEFINED) { + return get_double("phi0", self->x->phi0); + } + Py_RETURN_NONE; +} + + +static int PyCelprm_set_phi0(PyCelprm* self, PyObject* value, void* closure) +{ + int result; + double phi0; + + if (is_cel_null(self) || is_readonly(self)) { + return -1; + } else if (value == Py_None) { + if (self->x->phi0 != UNDEFINED) { + self->x->phi0 = UNDEFINED; + self->x->flag = 0; + } + } else { + result = set_double("phi0", value, &phi0); + if (result) return result; + if (phi0 != self->x->phi0) { + self->x->phi0 = phi0; + self->x->flag = 0; + } + } + return 0; +} + + +static PyObject* PyCelprm_get_theta0(PyCelprm* self, void* closure) +{ + if (is_cel_null(self)) { + return NULL; + } else if (self->x->theta0 != UNDEFINED) { + return get_double("theta0", self->x->theta0); + } + Py_RETURN_NONE; +} + + +static int PyCelprm_set_theta0(PyCelprm* self, PyObject* value, void* closure) +{ + int result; + double theta0; + if(is_cel_null(self) || is_readonly(self)) { + return -1; + } else if (value == Py_None) { + if (self->x->theta0 != UNDEFINED) { + self->x->theta0 = UNDEFINED; + self->x->flag = 0; + } + } else { + result = set_double("theta0", value, &theta0); + if (result) return result; + if (theta0 != self->x->theta0) { + self->x->theta0 = theta0; + self->x->flag = 0; + } + } + return 0; +} + + +static PyObject* PyCelprm_get_ref(PyCelprm* self, void* closure) +{ + Py_ssize_t size = 4; + if (is_cel_null(self)) { + return NULL; + } else { + return get_double_array("ref", self->x->ref, 1, &size, (PyObject*) self); + } +} + + +static int PyCelprm_set_ref(PyCelprm* self, PyObject* value, void* closure) +{ + int i; + int skip[4] = {0, 0, 0, 0}; + double ref[4] = {0.0, 0.0, UNDEFINED, +90.0}; + npy_intp size; + double *data; + + if (is_cel_null(self) || is_readonly(self)) return -1; + + if (value == Py_None) { + /* If ref is set to None - reset ref to celini values: */ + for (i = 0; i < 4; i++) { + self->x->ref[i] = ref[i]; + } + self->x->flag = 0; + return 0; + } + + PyArrayObject* value_array = (PyArrayObject*) PyArray_ContiguousFromAny(value, NPY_DOUBLE, 1, 1); + if (!value_array) return -1; + + size = PyArray_SIZE(value_array); + + if (size < 1) { + Py_DECREF(value_array); + PyErr_SetString(PyExc_ValueError, + "'ref' must be a non-empty 1-dimentional list of values or None."); + return -1; + } + + if (size > 4) { + Py_DECREF(value_array); + PyErr_SetString(PyExc_RuntimeError, "Number of 'ref' values cannot exceed 4."); + return -1; + } + + if (PyList_Check(value)) { + for (i = 0; i < size; i++) { + skip[i] = (PyList_GetItem(value, i) == Py_None); + } + } + + data = (double*) PyArray_DATA(value_array); + + for (i = 0; i < size; i++) { + if (skip[i]) continue; + if (npy_isnan(self->x->ref[i])) { + self->x->ref[i] = UNDEFINED; + } else { + self->x->ref[i] = data[i]; + } + } + for (i = size; i < 4; i++) { + self->x->ref[i] = ref[i]; + } + + self->x->flag = 0; + Py_DECREF(value_array); + return 0; +} + + +static PyObject* PyCelprm_get_prj(PyCelprm* self, void* closure) +{ + if (is_cel_null(self)) return NULL; + return (PyObject*)PyPrjprm_cnew((PyObject *)self, &(self->x->prj), NULL); +} + + +static PyObject* PyCelprm_get_euler(PyCelprm* self, void* closure) +{ + Py_ssize_t size = 5; + if (is_cel_null(self)) return NULL; + return get_double_array("euler", self->x->euler, 1, &size, (PyObject*) self); +} + + +static PyObject* PyCelprm_get_latpreq(PyCelprm* self, void* closure) +{ + if (is_cel_null(self)) return NULL; + return get_int("lapreq", self->x->latpreq); +} + + +static PyObject* PyCelprm_get_isolat(PyCelprm* self, void* closure) +{ + if (is_cel_null(self)) { + return NULL; + } else { + return get_bool("isolat", self->x->isolat); + } +} + + +/*************************************************************************** + * PyCelprm definition structures + */ + +static PyGetSetDef PyCelprm_getset[] = { + {"offset", (getter)PyCelprm_get_offset, (setter)PyCelprm_set_offset, (char *)doc_cel_offset}, + {"phi0", (getter)PyCelprm_get_phi0, (setter)PyCelprm_set_phi0, (char *)doc_celprm_phi0}, + {"theta0", (getter)PyCelprm_get_theta0, (setter)PyCelprm_set_theta0, (char *)doc_celprm_theta0}, + {"ref", (getter)PyCelprm_get_ref, (setter)PyCelprm_set_ref, (char *)doc_celprm_ref}, + {"euler", (getter)PyCelprm_get_euler, NULL, (char *)doc_celprm_euler}, + {"latpreq", (getter)PyCelprm_get_latpreq, NULL, (char *)doc_celprm_latpreq}, + {"isolat", (getter)PyCelprm_get_isolat, NULL, (char *)doc_celprm_isolat}, + {"_flag", (getter)PyCelprm_get_flag, NULL, ""}, + {"prj", (getter)PyCelprm_get_prj, NULL, (char *)doc_celprm_prj}, + {NULL} +}; + + +static PyMethodDef PyCelprm_methods[] = { + {"set", (PyCFunction)PyCelprm_set, METH_NOARGS, doc_set_celprm}, + {"__copy__", (PyCFunction)PyCelprm_copy, METH_NOARGS, ""}, + {"__deepcopy__", (PyCFunction)PyCelprm_deepcopy, METH_O, ""}, + {NULL} +}; + +static PyType_Spec PyCelprmType_spec = { + .name = "astropy.wcs.Celprm", + .basicsize = sizeof(PyCelprm), + .itemsize = 0, + .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_IMMUTABLETYPE, + .slots = (PyType_Slot[]){ + {Py_tp_dealloc, (destructor)PyCelprm_dealloc}, + {Py_tp_str, (reprfunc)PyCelprm___str__}, + {Py_tp_doc, doc_Celprm}, + {Py_tp_traverse, (traverseproc)PyCelprm_traverse}, + {Py_tp_clear, (inquiry)PyCelprm_clear}, + {Py_tp_methods, PyCelprm_methods}, + {Py_tp_getset, PyCelprm_getset}, + {Py_tp_new, PyCelprm_new}, + {0, NULL}, + }, +}; + +PyObject* PyCelprmType = NULL; + +int _setup_celprm_type(PyObject* m) +{ + PyCelprmType = PyType_FromSpec(&PyCelprmType_spec); + if (PyCelprmType == NULL) return -1; + PyModule_AddObject(m, "Celprm", PyCelprmType); + + cel_errexc[0] = NULL; /* Success */ + cel_errexc[1] = &PyExc_MemoryError; /* Null celprm pointer passed */ + cel_errexc[2] = &WcsExc_InvalidPrjParameters; /* Invalid projection parameters */ + cel_errexc[3] = &WcsExc_InvalidTransform; /* Invalid coordinate transformation parameters */ + cel_errexc[4] = &WcsExc_InvalidTransform; /* Ill-conditioned coordinate transformation parameters */ + cel_errexc[5] = &WcsExc_InvalidCoordinate; /* One or more of the (x,y) coordinates were invalid */ + cel_errexc[6] = &WcsExc_InvalidCoordinate; /* One or more of the (lng,lat) coordinates were invalid */ + + return 0; +} diff --git a/astropy/wcs/src/wcslib_prjprm_wrap.c b/astropy/wcs/src/wcslib_prjprm_wrap.c new file mode 100644 index 000000000000..7fcef537a087 --- /dev/null +++ b/astropy/wcs/src/wcslib_prjprm_wrap.c @@ -0,0 +1,998 @@ +#define NO_IMPORT_ARRAY +#include "astropy_wcs/wcslib_celprm_wrap.h" +#include "astropy_wcs/wcslib_prjprm_wrap.h" + +#include +#include +#include // calloc, malloc, free +#include // memcpy, strlen, strncpy + +#include +#include +#include +#include +#include "astropy_wcs/docstrings.h" + + +PyObject** prj_errexc[5]; + + +static int is_dbl_equal(double x1, double x2) +{ + double ax1 = fabs(x1); + double ax2 = fabs(x2); + double minx = (ax1 < ax2) ? ax1 : ax2; + double diff = fabs(x1 - x2); + return (diff <= (2.0 * DBL_EPSILON * minx) || diff < DBL_MIN); +} + + +static int wcslib_prj_to_python_exc(int status) +{ + if (status > 0 && status < 5) { + PyErr_SetString(*prj_errexc[status], prj_errmsg[status]); + } else if (status > 5) { + PyErr_SetString( + PyExc_RuntimeError, + "Unknown WCSLIB prjprm-related error occurred."); + } + return status; +} + + +static int is_readonly(PyPrjprm* self) +{ + if (self != NULL && self->owner != NULL && + ((PyCelprm*)self->owner)->owner != NULL) { + PyErr_SetString( + PyExc_AttributeError, + "Attribute 'prj' of 'astropy.wcs.Wcsprm.cel' objects is read-only."); + return 1; + } else { + return 0; + } +} + + +static int is_prj_null(PyPrjprm* self) +{ + if (self->x == NULL) { + PyErr_SetString(PyExc_MemoryError, "Underlying 'prjprm' object is NULL."); + return 1; + } else { + return 0; + } +} + + +/*************************************************************************** + * PyPrjprm methods * + ***************************************************************************/ + +static PyObject* PyPrjprm_new(PyTypeObject* type, PyObject* args, PyObject* kwds) +{ + PyPrjprm* self; + allocfunc alloc_func = PyType_GetSlot(type, Py_tp_alloc); + self = (PyPrjprm*)alloc_func(type, 0); + if (self == NULL) return NULL; + self->owner = NULL; + self->x = NULL; + self->prefcount = NULL; + if ((self->x = calloc(1, sizeof(struct prjprm))) == 0x0) { + PyErr_SetString(PyExc_MemoryError, "Could not allocate memory."); + return NULL; + } + if ((self->prefcount = (int*) malloc(sizeof(int))) == 0x0) { + PyErr_SetString(PyExc_MemoryError, "Could not allocate memory."); + free(self->x); + return NULL; + } + if (wcslib_prj_to_python_exc(prjini(self->x))) + { + free(self->x); + free(self->prefcount); + return NULL; + } + *(self->prefcount) = 1; + return (PyObject*)self; +} + + +static int PyPrjprm_traverse(PyPrjprm* self, visitproc visit, void *arg) +{ + Py_VISIT(self->owner); + Py_VISIT((PyObject*)Py_TYPE((PyObject*)self)); + return 0; +} + + +static int PyPrjprm_clear(PyPrjprm* self) +{ + Py_CLEAR(self->owner); + return 0; +} + + +static void PyPrjprm_dealloc(PyPrjprm* self) +{ + PyPrjprm_clear(self); + if (self->prefcount && (--(*self->prefcount)) == 0) { + wcslib_prj_to_python_exc(prjfree(self->x)); + free(self->x); + free(self->prefcount); + } + PyTypeObject *tp = Py_TYPE((PyObject*)self); + freefunc free_func = PyType_GetSlot(tp, Py_tp_free); + free_func((PyObject*)self); + Py_DECREF(tp); +} + + +PyPrjprm* PyPrjprm_cnew(PyObject* celprm_obj, struct prjprm* x, int* prefcount) +{ + PyPrjprm* self; + PyTypeObject* type = (PyTypeObject*)PyPrjprmType; + allocfunc alloc_func = PyType_GetSlot(type, Py_tp_alloc); + self = (PyPrjprm*)alloc_func(type, 0); + if (self == NULL) return NULL; + self->x = x; + Py_XINCREF(celprm_obj); + self->owner = celprm_obj; + self->prefcount = prefcount; + if (prefcount) (*prefcount)++; + return self; +} + + +static PyObject* PyPrjprm_copy(PyPrjprm* self) +{ + PyPrjprm* copy = NULL; + copy = PyPrjprm_cnew(self->owner, self->x, self->prefcount); + if (copy == NULL) return NULL; + return (PyObject*)copy; +} + + +static PyObject* PyPrjprm_deepcopy(PyPrjprm* self) +{ + PyPrjprm* copy = (PyPrjprm*) PyPrjprm_new((PyTypeObject*)PyPrjprmType, NULL, NULL); + if (copy == NULL) return NULL; + + memcpy(copy->x, self->x, sizeof(struct prjprm)); + copy->x->err = NULL; + return (PyObject*)copy; +} + + +static PyObject* PyPrjprm___str__(PyPrjprm* self) +{ + wcsprintf_set(NULL); + if (wcslib_prj_to_python_exc(prjprt(self->x))) { + return NULL; + } + return PyUnicode_FromString(wcsprintf_buf()); +} + + +static int PyPrjprm_cset(PyPrjprm* self) +{ + if (wcslib_prj_to_python_exc(prjset(self->x))) { + return -1; + } + return 0; +} + + +static PyObject* PyPrjprm_set(PyPrjprm* self) +{ + if (is_readonly(self) || PyPrjprm_cset(self)) return NULL; + Py_RETURN_NONE; +} + + +static PyObject* _prj_eval(PyPrjprm* self, int (*prjfn)(PRJX2S_ARGS), + PyObject* x1_in, PyObject* x2_in) +{ + Py_ssize_t i, ndim; + npy_intp *x1_dims, *x2_dims; + Py_ssize_t nelem = 1; + PyArrayObject* x1 = NULL; + PyArrayObject* x2 = NULL; + PyArrayObject* prj_x1 = NULL; + PyArrayObject* prj_x2 = NULL; + PyArrayObject* stat = NULL; + PyObject* result = NULL; + int status = -1; + + // TODO: This assumes the same shape for the input arrays. + // Instead, we should broadcast. + + x1 = (PyArrayObject *) PyArray_ContiguousFromObject(x1_in, NPY_DOUBLE, 1, NPY_MAXDIMS); + if (x1 == NULL) { + goto exit; + } + x2 = (PyArrayObject *) PyArray_ContiguousFromObject(x2_in, NPY_DOUBLE, 1, NPY_MAXDIMS); + if (x2 == NULL) { + goto exit; + } + + ndim = PyArray_NDIM(x1); + + if (ndim != PyArray_NDIM(x2)) { + PyErr_SetString(PyExc_ValueError, "Input array dimensions do not match."); + goto exit; + } + + x1_dims = PyArray_DIMS(x1); + x2_dims = PyArray_DIMS(x2); + for (i = 0; i < ndim; i++) { + if (x1_dims[i] != x2_dims[i]) { + PyErr_SetString(PyExc_ValueError, "Input array dimensions do not match."); + goto exit; + } + nelem *= x1_dims[i]; + } + + prj_x1 = (PyArrayObject*)PyArray_SimpleNew(ndim, x1_dims, NPY_DOUBLE); + if (prj_x1 == NULL) { + goto exit; + } + + prj_x2 = (PyArrayObject*)PyArray_SimpleNew(ndim, x1_dims, NPY_DOUBLE); + if (prj_x2 == NULL) { + goto exit; + } + + stat = (PyArrayObject*)PyArray_SimpleNew(ndim, x1_dims, NPY_INT); + if (stat == NULL) { + goto exit; + } + + Py_BEGIN_ALLOW_THREADS + status = prjfn( + self->x, + nelem, 0, 1, 1, + (double*)PyArray_DATA(x1), + (double*)PyArray_DATA(x2), + (double*)PyArray_DATA(prj_x1), + (double*)PyArray_DATA(prj_x2), + (int*)PyArray_DATA(stat)); + Py_END_ALLOW_THREADS + + switch (status) { + case 3: + case 4: + for (i = 0; i < nelem; ++i) { + if (((int *)PyArray_DATA(stat))[i]) { + ((double *)PyArray_DATA(prj_x1))[i] = NPY_NAN; + ((double *)PyArray_DATA(prj_x2))[i] = NPY_NAN; + } + } + case 0: + result = Py_BuildValue("(OO)", prj_x1, prj_x2); + break; + default: + wcslib_prj_to_python_exc(status); + break; + } + + exit: + Py_XDECREF((PyObject*)x1); + Py_XDECREF((PyObject*)x2); + Py_XDECREF((PyObject*)prj_x1); + Py_XDECREF((PyObject*)prj_x2); + Py_XDECREF((PyObject*)stat); + + return result; +} + + +static PyObject* PyPrjprm_prjx2s(PyPrjprm* self, PyObject* args, PyObject* kwds) +{ + PyObject* x = NULL; + PyObject* y = NULL; + const char* keywords[] = { "x", "y", NULL }; + + if (is_prj_null(self)) return NULL; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO:prjx2s", + (char **)keywords, &x, &y)) { + return NULL; + } + + if (self->x->prjx2s == NULL || self->x->flag == 0) { + if (is_readonly(self)) { + PyErr_SetString( + PyExc_AttributeError, + "Attribute 'prj' of 'astropy.wcs.Wcsprm.cel' objects is " + "read-only and cannot be automatically set."); + return NULL; + } else if (PyPrjprm_cset(self)) { + return NULL; + } + } + + return _prj_eval(self, self->x->prjx2s, x, y); +} + + +static PyObject* PyPrjprm_prjs2x(PyPrjprm* self, PyObject* args, PyObject* kwds) +{ + PyObject* phi = NULL; + PyObject* theta = NULL; + const char* keywords[] = { "phi", "theta", NULL }; + + if (is_prj_null(self)) return NULL; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO:prjs2x", + (char **)keywords, &phi, &theta)) { + return NULL; + } + + if (self->x->prjs2x == NULL || self->x->flag == 0) { + if (is_readonly(self)) { + PyErr_SetString( + PyExc_AttributeError, + "Attribute 'prj' of 'astropy.wcs.Wcsprm.cel' objects is " + "read-only and cannot be automatically set."); + return NULL; + } else if (PyPrjprm_cset(self)) { + return NULL; + } + } + + return _prj_eval(self, self->x->prjs2x, phi, theta); +} + + +/*************************************************************************** + * Member getters/setters (properties) + */ + +static PyObject* PyPrjprm_get_flag(PyPrjprm* self, void* closure) +{ + if (is_prj_null(self)) { + return NULL; + } else { + return get_int("flag", self->x->flag); + } +} + + +static PyObject* PyPrjprm_get_code(PyPrjprm* self, void* closure) +{ + if (is_prj_null(self)) { + return NULL; + } else { + return get_string("code", self->x->code); + } +} + + +static int PyPrjprm_set_code(PyPrjprm* self, PyObject* value, void* closure) +{ + char code[4]; + int code_len; + + if (is_prj_null(self) || is_readonly(self)) { + return -1; + } else if (value == Py_None) { + if (strcmp(" ", self->x->code)) { + strcpy(self->x->code, " "); + self->x->flag = 0; + if (self->owner) ((PyCelprm*)self->owner)->x->flag = 0; + } + } else { + if (set_string("code", value, code, 4)) return -1; + code_len = strlen(code); + if (code_len != 3) { + PyErr_Format(PyExc_ValueError, + "'code' must be exactly a three character string. " + "Provided 'code' ('%s') is %d characters long.", + code, code_len); + return -1; + } + if (strcmp(code, self->x->code)) { + strncpy(self->x->code, code, 4); + self->x->code[3] = '\0'; /* just to be safe */ + self->x->flag = 0; + if (self->owner) ((PyCelprm*)self->owner)->x->flag = 0; + } + } + return 0; +} + + +static PyObject* PyPrjprm_get_r0(PyPrjprm* self, void* closure) +{ + if (is_prj_null(self)) { + return NULL; + } else if (self->x->r0 == UNDEFINED) { + Py_RETURN_NONE; + } else { + return get_double("r0", self->x->r0); + } +} + + +static int PyPrjprm_set_r0(PyPrjprm* self, PyObject* value, void* closure) +{ + int result; + double r0; + if (is_prj_null(self) || is_readonly(self)) { + return -1; + } else if (value == Py_None) { + if (self->x->r0 != UNDEFINED) { + self->x->r0 = UNDEFINED; + self->x->flag = 0; + if (self->owner) ((PyCelprm*)self->owner)->x->flag = 0; + } + } else { + result = set_double("r0", value, &r0); + if (result) return result; + if (r0 != self->x->r0) { + self->x->r0 = r0; + self->x->flag = 0; + if (self->owner) ((PyCelprm*)self->owner)->x->flag = 0; + } + } + return 0; +} + + +static PyObject* PyPrjprm_get_phi0(PyPrjprm* self, void* closure) +{ + if (is_prj_null(self)) { + return NULL; + } else if (self->x->phi0 == UNDEFINED) { + Py_RETURN_NONE; + } else { + return get_double("phi0", self->x->phi0); + } +} + + +static int PyPrjprm_set_phi0(PyPrjprm* self, PyObject* value, void* closure) +{ + int result; + double phi0; + if (is_prj_null(self) || is_readonly(self)) { + return -1; + } else if (value == Py_None) { + if (self->x->phi0 != UNDEFINED) { + self->x->phi0 = UNDEFINED; + self->x->flag = 0; + if (self->owner) ((PyCelprm*)self->owner)->x->flag = 0; + } + } else { + result = set_double("phi0", value, &phi0); + if (result) return result; + if (phi0 != self->x->phi0) { + self->x->phi0 = phi0; + self->x->flag = 0; + if (self->owner) ((PyCelprm*)self->owner)->x->flag = 0; + } + } + return 0; +} + + +static PyObject* PyPrjprm_get_theta0(PyPrjprm* self, void* closure) +{ + if (is_prj_null(self)) { + return NULL; + } else if (self->x->theta0 == UNDEFINED) { + Py_RETURN_NONE; + } else { + return get_double("theta0", self->x->theta0); + } +} + + +static int PyPrjprm_set_theta0(PyPrjprm* self, PyObject* value, void* closure) +{ + int result; + double theta0; + if (is_prj_null(self) || is_readonly(self)) { + return -1; + } else if (value == Py_None) { + if (self->x->theta0 != UNDEFINED) { + self->x->theta0 = UNDEFINED; + self->x->flag = 0; + if (self->owner) ((PyCelprm*)self->owner)->x->flag = 0; + } + } else { + result = set_double("theta0", value, &theta0); + if (result) return result; + if (theta0 != self->x->theta0) { + self->x->theta0 = theta0; + self->x->flag = 0; + if (self->owner) ((PyCelprm*)self->owner)->x->flag = 0; + } + } + return 0; +} + + +static PyObject* PyPrjprm_get_pv(PyPrjprm* self, void* closure) +{ + int k; + Py_ssize_t size = PVN; + double *pv; + PyObject* pv_pyobj; + PyArrayObject* pv_array; + if (is_prj_null(self)) return NULL; + + pv_pyobj = PyArray_SimpleNew(1, &size, NPY_DOUBLE); + pv_array = (PyArrayObject*) pv_pyobj; + if (pv_array == NULL) return NULL; + pv = (double*) PyArray_DATA(pv_array); + + for (k = 0; k < PVN; k++) { + if (self->x->pv[k] == UNDEFINED) { + pv[k] = (double) NPY_NAN; + } else { + pv[k] = self->x->pv[k]; + } + } + + return pv_pyobj; +} + + +static int PyPrjprm_set_pv(PyPrjprm* self, PyObject* value, void* closure) +{ + int k, modified; + npy_intp size; + double *data; + PyArrayObject* value_array = NULL; + int skip[PVN]; + + if (is_prj_null(self) || is_readonly(self)) return -1; + + if (value == Py_None) { + /* If pv is set to None - reset pv to prjini values: */ + self->x->pv[0] = 0.0; + for (k = 1; k < 4; self->x->pv[k++] = UNDEFINED); + for (k = 4; k < PVN; self->x->pv[k++] = 0.0); + self->x->flag = 0; + if (self->owner) ((PyCelprm*)self->owner)->x->flag = 0; + return 0; + } + + value_array = (PyArrayObject*) PyArray_ContiguousFromAny(value, NPY_DOUBLE, 1, 1); + if (!value_array) return -1; + + size = PyArray_SIZE(value_array); + + if (size < 1) { + Py_DECREF(value_array); + PyErr_SetString(PyExc_ValueError, + "PV must be a non-empty 1-dimentional list of values or None."); + return -1; + } + + if (size > PVN) { + Py_DECREF(value_array); + PyErr_Format(PyExc_RuntimeError, "Number of PV values cannot exceed %d.", PVN); + return -1; + } + + if (PyList_Check(value)) { + for (k = 0; k < size; k++) { + skip[k] = (PyList_GetItem(value, k) == Py_None); + } + } else if (PyTuple_Check(value)) { + for (k = 0; k < size; k++) { + skip[k] = (PyTuple_GetItem(value, k) == Py_None); + } + } else { + for (k = 0; k < size; k++) skip[k] = 0; + } + + data = (double*) PyArray_DATA(value_array); + + modified = 0; + for (k = 0; k < size; k++) { + if (skip[k]) continue; + if (is_dbl_equal(self->x->pv[k], data[k])) { + /* update PV but do not flag it as modified since values are + essentially the same. + */ + self->x->pv[k] = data[k]; + } else if (npy_isnan(data[k])) { + self->x->pv[k] = UNDEFINED; + modified = 1; + } else { + self->x->pv[k] = data[k]; + modified = 1; + } + } + Py_DECREF(value_array); + + if (modified) { + self->x->flag = 0; + if (self->owner) ((PyCelprm*)self->owner)->x->flag = 0; + } + return 0; +} + + +static PyObject* PyPrjprm_get_pvi(PyPrjprm* self, PyObject* args, PyObject* kwds) +{ + int idx; + PyObject* index = NULL; + const char* keywords[] = { "index", NULL }; + + if (is_prj_null(self)) return NULL; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O:get_pvi", + (char **)keywords, &index)) { + return NULL; + } + + if (!PyLong_Check(index)) { + PyErr_SetString(PyExc_TypeError, + "PV index must be an integer number."); + } + + idx = PyLong_AsLong(index); + if (idx == -1 && PyErr_Occurred()) { + return NULL; + } + + if (idx < 0 || idx >= PVN) { + PyErr_Format(PyExc_ValueError, + "PV index must be an integer number between 0 and %d.", PVN - 1); + return NULL; + } + + if (self->x->pv[idx] == UNDEFINED) { + return PyFloat_FromDouble((double) NPY_NAN); + } else { + return PyFloat_FromDouble(self->x->pv[idx]); + } +} + + +static PyObject* PyPrjprm_set_pvi(PyPrjprm* self, PyObject* args, PyObject* kwds) +{ + int idx, size; + double data; + PyObject* scalar= NULL; + PyObject* index = NULL; + PyObject* value = NULL; + PyObject* flt_value = NULL; + PyObject* value_array_pyobj = NULL; + PyArrayObject* value_array = NULL; + const char* keywords[] = { "index", "value", NULL }; + PyArray_Descr* dbl_descr = PyArray_DescrNewFromType(NPY_DOUBLE); + + if (is_prj_null(self) || is_readonly(self)) return NULL; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO:set_pvi", + (char **)keywords, &index, &value)) { + return NULL; + } + + if (!PyLong_Check(index)) { + PyErr_SetString(PyExc_TypeError, + "PV index must be an integer number."); + } + + idx = PyLong_AsLong(index); + if (idx == -1 && PyErr_Occurred()) { + return NULL; + } + + if (idx < 0 || idx >= PVN) { + PyErr_Format(PyExc_ValueError, + "PV index must be an integer number between 0 and %d.", PVN - 1); + return NULL; + } + + if (value == Py_None) { + /* If pv is set to None - reset pv to prjini values: */ + self->x->pv[idx] = (idx > 0 && idx < 4) ? UNDEFINED : 0.0; + self->x->flag = 0; + if (self->owner) ((PyCelprm*)self->owner)->x->flag = 0; + Py_RETURN_NONE; + } + + if (PyFloat_Check(value) || PyLong_Check(value)) { + data = PyFloat_AsDouble(value); + if (data == -1.0 && PyErr_Occurred()) { + return NULL; + } + + } else if (PyUnicode_Check(value)) { + flt_value = PyFloat_FromString(value); + if (!flt_value) return NULL; + data = PyFloat_AsDouble(flt_value); + Py_DECREF(flt_value); + if (data == -1.0 && PyErr_Occurred()) { + return NULL; + } + + } else { + if (PyArray_Converter(value, &value_array_pyobj) == NPY_FAIL) { + return NULL; + } + value_array = (PyArrayObject*) value_array_pyobj; + + size = PyArray_SIZE(value_array); + if (size != 1) { + Py_DECREF(value_array); + PyErr_SetString(PyExc_ValueError, + "PV value must be a scalar-like object or None."); + return NULL; + } + + scalar = PyArray_ToScalar(PyArray_DATA(value_array), value_array); + Py_DECREF(value_array); + if (!scalar) { + Py_DECREF(scalar); + PyErr_SetString(PyExc_TypeError, "Unable to convert value to scalar."); + } + + PyArray_CastScalarToCtype(scalar, &data, dbl_descr); + Py_DECREF(scalar); + if (PyErr_Occurred()) { + return NULL; + } + } + + data = (isnan(data)) ? UNDEFINED : data; + + if (!is_dbl_equal(self->x->pv[idx], data)) { + self->x->flag = 0; + if (self->owner) ((PyCelprm*)self->owner)->x->flag = 0; + } + self->x->pv[idx] = data; + + Py_RETURN_NONE; +} + + +static PyObject* PyPrjprm_get_bounds(PyPrjprm* self, void* closure) +{ + if (is_prj_null(self)) { + return NULL; + } else { + return get_int("bounds", self->x->bounds); + } +} + + +static int PyPrjprm_set_bounds(PyPrjprm* self, PyObject* value, void* closure) +{ + if (is_prj_null(self) || is_readonly(self)) { + return -1; + } else if (value == Py_None) { + self->x->bounds = 0; + return 0; + } else { + return set_int("bounds", value, &self->x->bounds); + } +} + + +static PyObject* PyPrjprm_get_w(PyPrjprm* self, void* closure) +{ + Py_ssize_t size = 10; + int k; + double *w; + PyArrayObject* w_array; + + if (is_prj_null(self)) return NULL; + + w_array = (PyArrayObject*) PyArray_SimpleNew(1, &size, NPY_DOUBLE); + if (w_array == NULL) return NULL; + w = (double*) PyArray_DATA(w_array); + + for (k = 0; k < size; k++) { + if (self->x->w[k] == UNDEFINED) { + w[k] = (double) NPY_NAN; + } else { + w[k] = self->x->w[k]; + } + } + + return (PyObject*) w_array; +} + + +static PyObject* PyPrjprm_get_name(PyPrjprm* self, void* closure) +{ + if (is_prj_null(self)) { + return NULL; + } else { + return get_string("name", self->x->name); + } +} + + +static PyObject* PyPrjprm_get_category(PyPrjprm* self, void* closure) +{ + if (is_prj_null(self)) { + return NULL; + } else { + return get_int("category", self->x->category); + } +} + + +static PyObject* PyPrjprm_get_pvrange(PyPrjprm* self, void* closure) +{ + if (is_prj_null(self)) { + return NULL; + } else { + return get_int("pvrange", self->x->pvrange); + } +} + + +static PyObject* PyPrjprm_get_simplezen(PyPrjprm* self, void* closure) +{ + if (is_prj_null(self)) { + return NULL; + } else { + return PyBool_FromLong(self->x->simplezen); + } +} + + +static PyObject* PyPrjprm_get_equiareal(PyPrjprm* self, void* closure) +{ + if (is_prj_null(self)) { + return NULL; + } else { + return PyBool_FromLong(self->x->equiareal); + } +} + + +static PyObject* PyPrjprm_get_conformal(PyPrjprm* self, void* closure) +{ + if (is_prj_null(self)) { + return NULL; + } else { + return PyBool_FromLong(self->x->conformal); + } +} + + +static PyObject* PyPrjprm_get_global_projection(PyPrjprm* self, void* closure) +{ + if (is_prj_null(self)) { + return NULL; + } else { + return PyBool_FromLong(self->x->global); + } +} + + +static PyObject* PyPrjprm_get_divergent(PyPrjprm* self, void* closure) +{ + if (is_prj_null(self)) { + return NULL; + } else { + return PyBool_FromLong(self->x->divergent); + } +} + + +static PyObject* PyPrjprm_get_x0(PyPrjprm* self, void* closure) +{ + if (is_prj_null(self)) { + return NULL; + } else { + return get_double("x0", self->x->x0); + } +} + + +static PyObject* PyPrjprm_get_y0(PyPrjprm* self, void* closure) +{ + if (is_prj_null(self)) { + return NULL; + } else { + return get_double("y0", self->x->y0); + } +} + + +static PyObject* PyPrjprm_get_m(PyPrjprm* self, void* closure) +{ + if (is_prj_null(self)) { + return NULL; + } else { + return get_int("m", self->x->m); + } +} + + +static PyObject* PyPrjprm_get_n(PyPrjprm* self, void* closure) +{ + if (is_prj_null(self)) { + return NULL; + } else { + return get_int("n", self->x->n); + } +} + + +/*************************************************************************** + * PyPrjprm definition structures + */ + +static PyGetSetDef PyPrjprm_getset[] = { + {"r0", (getter)PyPrjprm_get_r0, (setter)PyPrjprm_set_r0, (char *)doc_prjprm_r0}, + {"phi0", (getter)PyPrjprm_get_phi0, (setter)PyPrjprm_set_phi0, (char *)doc_prjprm_phi0}, + {"theta0", (getter)PyPrjprm_get_theta0, (setter)PyPrjprm_set_theta0, (char *)doc_prjprm_theta0}, + {"pv", (getter)PyPrjprm_get_pv, (setter)PyPrjprm_set_pv, (char *)doc_prjprm_pv}, + {"w", (getter)PyPrjprm_get_w, NULL, (char *)doc_prjprm_w}, + {"name", (getter)PyPrjprm_get_name, NULL, (char *)doc_prjprm_name}, + {"code", (getter)PyPrjprm_get_code, (setter)PyPrjprm_set_code, (char *)doc_prjprm_code}, + {"bounds", (getter)PyPrjprm_get_bounds, (setter)PyPrjprm_set_bounds, (char *)doc_prjprm_bounds}, + {"category", (getter)PyPrjprm_get_category, NULL, (char *)doc_prjprm_category}, + {"pvrange", (getter)PyPrjprm_get_pvrange, NULL, (char *)doc_prjprm_pvrange}, + {"simplezen", (getter)PyPrjprm_get_simplezen, NULL, (char *)doc_prjprm_simplezen}, + {"equiareal", (getter)PyPrjprm_get_equiareal, NULL, (char *)doc_prjprm_equiareal}, + {"conformal", (getter)PyPrjprm_get_conformal, NULL, (char *)doc_prjprm_conformal}, + {"global_projection", (getter)PyPrjprm_get_global_projection, NULL, (char *)doc_prjprm_global_projection}, + {"divergent", (getter)PyPrjprm_get_divergent, NULL, (char *)doc_prjprm_divergent}, + {"x0", (getter)PyPrjprm_get_x0, NULL, (char *)doc_prjprm_x0}, + {"y0", (getter)PyPrjprm_get_y0, NULL, (char *)doc_prjprm_y0}, + {"m", (getter)PyPrjprm_get_m, NULL, (char *)doc_prjprm_m}, + {"n", (getter)PyPrjprm_get_n, NULL, (char *)doc_prjprm_n}, + {"_flag", (getter)PyPrjprm_get_flag, NULL, ""}, + {NULL} +}; + + +static PyMethodDef PyPrjprm_methods[] = { + {"set", (PyCFunction)PyPrjprm_set, METH_NOARGS, (char*)doc_prjprm_set}, + {"prjx2s", (PyCFunction)PyPrjprm_prjx2s, METH_VARARGS|METH_KEYWORDS, (char*)doc_prjprm_prjx2s}, + {"prjs2x", (PyCFunction)PyPrjprm_prjs2x, METH_VARARGS|METH_KEYWORDS, (char*)doc_prjprm_prjs2x}, + {"set_pvi", (PyCFunction)PyPrjprm_set_pvi, METH_VARARGS|METH_KEYWORDS, (char*)doc_prjprm_pvi}, + {"get_pvi", (PyCFunction)PyPrjprm_get_pvi, METH_VARARGS|METH_KEYWORDS, (char*)doc_prjprm_pvi}, + {"__copy__", (PyCFunction)PyPrjprm_copy, METH_NOARGS, ""}, + {"__deepcopy__", (PyCFunction)PyPrjprm_deepcopy, METH_O, ""}, + {NULL} +}; + +static PyType_Spec PyPrjprm_spec = { + .name = "astropy.wcs.Prjprm", + .basicsize = sizeof(PyPrjprm), + .itemsize = 0, + .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_IMMUTABLETYPE, + .slots = (PyType_Slot[]) { + {Py_tp_dealloc, (destructor)PyPrjprm_dealloc}, + {Py_tp_str, (reprfunc)PyPrjprm___str__}, + {Py_tp_doc, doc_Prjprm}, + {Py_tp_traverse, (traverseproc)PyPrjprm_traverse}, + {Py_tp_clear, (inquiry)PyPrjprm_clear}, + {Py_tp_methods, PyPrjprm_methods}, + {Py_tp_getset, PyPrjprm_getset}, + {Py_tp_new, PyPrjprm_new}, + {0, NULL}, + }, +}; + +PyObject* PyPrjprmType = NULL; + +int _setup_prjprm_type(PyObject* m) +{ + PyPrjprmType = PyType_FromSpec(&PyPrjprm_spec); + if (PyPrjprmType == NULL) return -1; + PyModule_AddObject(m, "Prjprm", PyPrjprmType); + + prj_errexc[0] = NULL; /* Success */ + prj_errexc[1] = &PyExc_MemoryError; /* Null prjprm pointer passed */ + prj_errexc[2] = &WcsExc_InvalidPrjParameters; /* Invalid projection parameters */ + prj_errexc[3] = &WcsExc_InvalidCoordinate; /* One or more of the (x,y) coordinates were invalid */ + prj_errexc[4] = &WcsExc_InvalidCoordinate; /* One or more of the (lng,lat) coordinates were invalid */ + + return 0; +} diff --git a/astropy/wcs/src/wcslib_tabprm_wrap.c b/astropy/wcs/src/wcslib_tabprm_wrap.c old mode 100644 new mode 100755 index ab3903235161..4df49cda7414 --- a/astropy/wcs/src/wcslib_tabprm_wrap.c +++ b/astropy/wcs/src/wcslib_tabprm_wrap.c @@ -5,7 +5,7 @@ #define NO_IMPORT_ARRAY -#include "wcslib_tabprm_wrap.h" +#include "astropy_wcs/wcslib_tabprm_wrap.h" #include #include @@ -17,7 +17,7 @@ docstrings are written in doc/docstrings.py, which are then converted by setup.py into docstrings.h, which we include here. */ -#include "docstrings.h" +#include "astropy_wcs/docstrings.h" /*************************************************************************** * Helper functions * @@ -69,24 +69,16 @@ wcslib_tab_to_python_exc(int status) { static int PyTabprm_traverse( PyTabprm* self, visitproc visit, void *arg) { - int vret; - - vret = visit(self->owner, arg); - if (vret != 0) { - return vret; - } - + Py_VISIT(self->owner); + Py_VISIT(Py_TYPE((PyObject*)self)); return 0; } static int PyTabprm_clear( PyTabprm* self) { - PyObject* tmp; - tmp = self->owner; - self->owner = NULL; - Py_XDECREF(tmp); + Py_CLEAR(self->owner); return 0; } @@ -96,13 +88,19 @@ PyTabprm_dealloc( PyTabprm* self) { PyTabprm_clear(self); - Py_TYPE(self)->tp_free((PyObject*)self); + PyTypeObject *tp = Py_TYPE((PyObject*)self); + freefunc free_func = PyType_GetSlot(tp, Py_tp_free); + free_func((PyObject*)self); + Py_DECREF(tp); } PyTabprm* PyTabprm_cnew(PyObject* wcsprm, struct tabprm* x) { PyTabprm* self; - self = (PyTabprm*)(&PyTabprmType)->tp_alloc(&PyTabprmType, 0); + PyTypeObject* type = (PyTypeObject*)PyTabprmType; + allocfunc alloc_func = PyType_GetSlot(type, Py_tp_alloc); + self = (PyTabprm*)alloc_func(type, 0); + if (self == NULL) return NULL; self->x = x; Py_INCREF(wcsprm); self->owner = wcsprm; @@ -133,8 +131,7 @@ PyTabprm_set( return NULL; } - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } /*@null@*/ static PyObject* @@ -148,13 +145,10 @@ PyTabprm_print_contents( /* This is not thread-safe, but since we're holding onto the GIL, we can assume we won't have thread conflicts */ wcsprintf_set(NULL); - tabprt(self->x); - printf("%s", wcsprintf_buf()); - - Py_INCREF(Py_None); - return Py_None; + fflush(stdout); + Py_RETURN_NONE; } /*@null@*/ static PyObject* @@ -171,11 +165,7 @@ PyTabprm___str__( tabprt(self->x); - #if PY3K return PyUnicode_FromString(wcsprintf_buf()); - #else - return PyString_FromString(wcsprintf_buf()); - #endif } /*************************************************************************** @@ -416,63 +406,35 @@ static PyMethodDef PyTabprm_methods[] = { {NULL} }; -PyTypeObject PyTabprmType = { - #if PY3K - PyVarObject_HEAD_INIT(NULL, 0) - #else - PyObject_HEAD_INIT(NULL) - 0, /*ob_size*/ - #endif - "astropy.wcs.Tabprm", /*tp_name*/ - sizeof(PyTabprm), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - (destructor)PyTabprm_dealloc, /*tp_dealloc*/ - 0, /*tp_print*/ - 0, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_compare*/ - (reprfunc)PyTabprm___str__, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - 0, /*tp_hash */ - 0, /*tp_call*/ - (reprfunc)PyTabprm___str__, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ - doc_Tabprm, /* tp_doc */ - (traverseproc)PyTabprm_traverse, /* tp_traverse */ - (inquiry)PyTabprm_clear, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - PyTabprm_methods, /* tp_methods */ - 0, /* tp_members */ - PyTabprm_getset, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - 0, /* tp_new */ +static PyType_Spec PyTabprmType_spec = { + .name = "astropy.wcs.Tabprm", + .basicsize = sizeof(PyTabprm), + .itemsize = 0, + .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_IMMUTABLETYPE, + .slots = (PyType_Slot[]) { + {Py_tp_dealloc, (destructor)PyTabprm_dealloc}, + {Py_tp_str, (reprfunc)PyTabprm___str__}, + {Py_tp_doc, doc_Tabprm}, + {Py_tp_traverse, (traverseproc)PyTabprm_traverse}, + {Py_tp_clear, (inquiry)PyTabprm_clear}, + {Py_tp_getset, PyTabprm_getset}, + {Py_tp_methods, PyTabprm_methods}, + {0, NULL}, + }, }; +PyObject* PyTabprmType = NULL; + int _setup_tabprm_type( PyObject* m) { - if (PyType_Ready(&PyTabprmType) < 0) { + PyTabprmType = PyType_FromSpec(&PyTabprmType_spec); + if (PyTabprmType == NULL) { return -1; } - Py_INCREF(&PyTabprmType); - - PyModule_AddObject(m, "Tabprm", (PyObject *)&PyTabprmType); + PyModule_AddObject(m, "Tabprm", PyTabprmType); tab_errexc[0] = NULL; /* Success */ tab_errexc[1] = &PyExc_MemoryError; /* Null wcsprm pointer passed */ diff --git a/astropy/wcs/src/wcslib_units_wrap.c b/astropy/wcs/src/wcslib_units_wrap.c deleted file mode 100644 index 85ea6a5eb2ab..000000000000 --- a/astropy/wcs/src/wcslib_units_wrap.c +++ /dev/null @@ -1,389 +0,0 @@ -/* - Author: Michael Droettboom - mdroe@stsci.edu -*/ - -#define NO_IMPORT_ARRAY - -#include "wcslib_units_wrap.h" - -#include - -/* - It gets to be really tedious to type long docstrings in ANSI C syntax - (since multi-line strings literals are not valid). Therefore, the - docstrings are written in doc/docstrings.py, which are then converted - by setup.py into docstrings.h, which we include here. -*/ -#include "docstrings.h" - -/*************************************************************************** - * PyTabprm methods - */ - -static int -PyUnits_traverse( - PyUnits* self, visitproc visit, void *arg) { - - return 0; -} - -static int -PyUnits_clear( - PyUnits* self) { - - return 0; -} - -static void -PyUnits_dealloc( - PyUnits* self) { - - PyUnits_clear(self); - Py_TYPE(self)->tp_free((PyObject*)self); -} - -PyUnits* -PyUnits_cnew( - const char* const have, - const char* const want, - const double scale, - const double offset, - const double power) { - - PyUnits* self; - self = (PyUnits*)(&PyUnitsType)->tp_alloc(&PyUnitsType, 0); - if (have == NULL) { - self->have[0] = 0; - } else { - strncpy(self->have, have, 80); - } - if (want == NULL) { - self->want[0] = 0; - } else { - strncpy(self->want, want, 80); - } - self->scale = scale; - self->offset = offset; - self->power = power; - return self; -} - -static PyObject * -PyUnits_new( - PyTypeObject* type, - /*@unused@*/ PyObject* args, - /*@unused@*/ PyObject* kwds) { - - PyUnits* self; - self = (PyUnits*)type->tp_alloc(type, 0); - self->have[0] = 0; - self->want[0] = 0; - self->scale = 1.0; - self->offset = 0.0; - self->power = 1.0; - return (PyObject*)self; -} - -static int -PyUnits_init( - PyUnits* self, - PyObject* args, - PyObject* kwds) { - - int status = -1; - const char* have; - const char* want; - const char* ctrl_str = NULL; - int ctrl = 0; - const char* keywords[] = {"have", "want", "translate_units", NULL}; - struct wcserr* err = NULL; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "ss|s:UnitConverter.__init__", - (char **)keywords, &have, &want, - &ctrl_str)) { - goto exit; - } - - if (ctrl_str != NULL) { - if (parse_unsafe_unit_conversion_spec(ctrl_str, &ctrl)) { - goto exit; - } - } - - /* Copy the strings since we can't have wcslib monkeying with the - data in a Python string */ - strncpy(self->have, have, 80); - strncpy(self->want, want, 80); - - status = wcsutrne(ctrl, self->have, &err); - if (status != -1 && status != 0) { - goto exit; - } - - status = wcsutrne(ctrl, self->want, &err); - if (status != -1 && status != 0) { - goto exit; - } - - status = wcsunitse(self->have, self->want, - &self->scale, &self->offset, &self->power, &err); - - exit: - - if (PyErr_Occurred()) { - return -1; - } else if (status == 0) { - return 0; - } else { - wcserr_units_to_python_exc(err); - free(err); - return -1; - } -} - -/*@null@*/ static PyObject* -PyUnits___str__( - PyUnits* self) { - - #define BUF_SIZE (1 << 8) - char buffer[BUF_SIZE]; - char scale[BUF_SIZE]; - char offset[BUF_SIZE]; - char power[BUF_SIZE]; - - if (self->scale != 1.0) { - snprintf(scale, BUF_SIZE, "*%.12g", self->scale); - } else { - scale[0] = 0; - } - - if (self->offset != 0.0) { - snprintf(offset, BUF_SIZE, " + %.12g", self->offset); - } else { - offset[0] = 0; - } - - if (self->power != 1.0) { - snprintf(power, BUF_SIZE, " ** %.12g", self->power); - } else { - power[0] = 0; - } - - snprintf(buffer, 1 << 8, "", - self->have, self->want, scale, offset, power); - - #if PY3K - return PyUnicode_FromString(buffer); - #else - return PyString_FromString(buffer); - #endif -} - -static PyObject* -PyUnits_convert( - PyUnits* self, - PyObject* args, - PyObject* kwds) { - - int status = 1; - PyObject* input = NULL; - PyArrayObject* input_arr = NULL; - PyArrayObject* output_arr = NULL; - PyObject* input_iter = NULL; - PyObject* output_iter = NULL; - double input_val; - double output_val; - - if (!PyArg_ParseTuple(args, "O:UnitConverter.convert", &input)) { - goto exit; - } - - input_arr = (PyArrayObject*)PyArray_FromObject( - input, NPY_DOUBLE, 0, NPY_MAXDIMS); - if (input_arr == NULL) { - goto exit; - } - - output_arr = (PyArrayObject*)PyArray_SimpleNew( - PyArray_NDIM(input_arr), PyArray_DIMS(input_arr), PyArray_DOUBLE); - if (output_arr == NULL) { - goto exit; - } - - input_iter = PyArray_IterNew((PyObject*)input_arr); - if (input_iter == NULL) { - goto exit; - } - - output_iter = PyArray_IterNew((PyObject*)output_arr); - if (output_iter == NULL) { - goto exit; - } - - if (self->power != 1.0) { - while (PyArray_ITER_NOTDONE(input_iter)) { - input_val = *(double *)PyArray_ITER_DATA(input_iter); - output_val = pow(self->scale*input_val + self->offset, self->power); - if (errno) { - PyErr_SetFromErrno(PyExc_ValueError); - goto exit; - } - *(double *)PyArray_ITER_DATA(output_iter) = output_val; - PyArray_ITER_NEXT(input_iter); - PyArray_ITER_NEXT(output_iter); - } - } else { - while (PyArray_ITER_NOTDONE(input_iter)) { - input_val = *(double *)PyArray_ITER_DATA(input_iter); - output_val = self->scale*input_val + self->offset; - *(double *)PyArray_ITER_DATA(output_iter) = output_val; - PyArray_ITER_NEXT(input_iter); - PyArray_ITER_NEXT(output_iter); - } - } - - status = 0; - - exit: - - Py_XDECREF((PyObject*)input_arr); - Py_XDECREF(input_iter); - Py_XDECREF(output_iter); - if (status) { - Py_XDECREF((PyObject*)output_arr); - return NULL; - } - return (PyObject*)output_arr; -} - -/*************************************************************************** - * Member getters/setters (properties) - */ - -/*@null@*/ static PyObject* -PyUnits_get_have( - PyUnits* self, - /*@unused@*/ void* closure) { - - #if PY3K - return PyUnicode_FromString(self->have); - #else - return PyString_FromString(self->have); - #endif -} - -/*@null@*/ static PyObject* -PyUnits_get_want( - PyUnits* self, - /*@unused@*/ void* closure) { - - #if PY3K - return PyUnicode_FromString(self->want); - #else - return PyString_FromString(self->want); - #endif -} - -/*@null@*/ static PyObject* -PyUnits_get_scale( - PyUnits* self, - /*@unused@*/ void* closure) { - - return get_double("scale", self->scale); -} - -/*@null@*/ static PyObject* -PyUnits_get_offset( - PyUnits* self, - /*@unused@*/ void* closure) { - - return get_double("offset", self->offset); -} - -/*@null@*/ static PyObject* -PyUnits_get_power( - PyUnits* self, - /*@unused@*/ void* closure) { - - return get_double("power", self->power); -} - -/*************************************************************************** - * PyUnits definition structures - */ - -static PyGetSetDef PyUnits_getset[] = { - {"have", (getter)PyUnits_get_have, NULL, (char *)doc_have}, - {"want", (getter)PyUnits_get_want, NULL, (char *)doc_want}, - {"scale", (getter)PyUnits_get_scale, NULL, (char *)doc_scale}, - {"offset", (getter)PyUnits_get_offset, NULL, (char *)doc_offset}, - {"power", (getter)PyUnits_get_power, NULL, (char *)doc_power}, - {NULL} -}; - -static PyMethodDef PyUnits_methods[] = { - {"convert", (PyCFunction)PyUnits_convert, METH_VARARGS, doc_convert}, - {NULL} -}; - -PyTypeObject PyUnitsType = { - #if PY3K - PyVarObject_HEAD_INIT(NULL, 0) - #else - PyObject_HEAD_INIT(NULL) - 0, /*ob_size*/ - #endif - "astropy.wcs.UnitConverter", /*tp_name*/ - sizeof(PyUnits), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - (destructor)PyUnits_dealloc, /*tp_dealloc*/ - 0, /*tp_print*/ - 0, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_compare*/ - (reprfunc)PyUnits___str__, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - 0, /*tp_hash */ - 0, /*tp_call*/ - (reprfunc)PyUnits___str__, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ - doc_UnitConverter, /* tp_doc */ - (traverseproc)PyUnits_traverse, /* tp_traverse */ - (inquiry)PyUnits_clear, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - PyUnits_methods, /* tp_methods */ - 0, /* tp_members */ - PyUnits_getset, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - (initproc)PyUnits_init, /* tp_init */ - 0, /* tp_alloc */ - PyUnits_new, /* tp_new */ -}; - -int -_setup_units_type( - PyObject* m) { - - if (PyType_Ready(&PyUnitsType) < 0) { - return -1; - } - - Py_INCREF(&PyUnitsType); - - PyModule_AddObject(m, "UnitConverter", (PyObject *)&PyUnitsType); - - return 0; -} diff --git a/astropy/wcs/src/wcslib_wrap.c b/astropy/wcs/src/wcslib_wrap.c old mode 100644 new mode 100755 index 8ab99f06bcd1..ce50954f2f32 --- a/astropy/wcs/src/wcslib_wrap.c +++ b/astropy/wcs/src/wcslib_wrap.c @@ -5,21 +5,33 @@ #define NO_IMPORT_ARRAY -#include "wcslib_wrap.h" -#include "wcslib_tabprm_wrap.h" -#include "wcslib_wtbarr_wrap.h" -#include "wcslib_units_wrap.h" +#include "astropy_wcs/wcslib_wrap.h" +#include "astropy_wcs/wcslib_auxprm_wrap.h" +#include "astropy_wcs/wcslib_prjprm_wrap.h" +#include "astropy_wcs/wcslib_celprm_wrap.h" +#include "astropy_wcs/wcslib_tabprm_wrap.h" +#include "astropy_wcs/wcslib_wtbarr_wrap.h" +#include "astropy_wcs/wcslib_units_wrap.h" +#include "astropy_wcs/unit_list_proxy.h" #include /* from Python */ +#include // calloc, malloc, free +#include // memset, strlen, strncpy + #include #include #include #include #include #include +#include +#include +#include +#include +#include -#include "isnan.h" -#include "distortion.h" +#include "astropy_wcs/isnan.h" +#include "astropy_wcs/distortion.h" /* It gets to be really tedious to type long docstrings in ANSI C syntax @@ -27,7 +39,7 @@ docstrings are written in doc/docstrings.py, which are then converted by setup.py into docstrings.h, which we include here. */ -#include "docstrings.h" +#include "astropy_wcs/docstrings.h" /*************************************************************************** * Helper functions * @@ -53,11 +65,296 @@ is_valid_alt_key( return 1; } +static int +convert_rejections_to_warnings() { + char buf[1024]; + const char *src; + char *dst; + int last_was_space; + PyObject *wcs_module = NULL; + PyObject *FITSFixedWarning = NULL; + int status = -1; + char delimiter; + +#ifdef HAVE_WCSLIB_VERSION + delimiter = ','; +#else + delimiter = ':'; +#endif + + if (wcsprintf_buf()[0] == 0) { + return 0; + } + + wcs_module = PyImport_ImportModule("astropy.wcs"); + if (wcs_module == NULL) { + goto exit; + } + + FITSFixedWarning = PyObject_GetAttrString( + wcs_module, "FITSFixedWarning"); + if (FITSFixedWarning == NULL) { + goto exit; + } + + src = wcsprintf_buf(); + while (*src != 0) { + dst = buf; + + /* Read the first line, removing any repeated spaces */ + last_was_space = 0; + for (; *src != 0; ++src) { + if (*src == ' ') { + if (!last_was_space) { + *(dst++) = *src; + last_was_space = 1; + } + } else if (*src == '\n') { + ++src; + break; + } else { + *(dst++) = *src; + last_was_space = 0; + } + } + + *(dst++) = '\n'; + + /* For the second line, remove everything up to and including the + first colon */ + for (; *src != 0; ++src) { + if (*src == delimiter) { + ++src; + break; + } + } + + /* Read to the end of the second line, removing any repeated + spaces */ + last_was_space = 1; + for (; *src != 0; ++src) { + if (*src == ' ') { + if (!last_was_space) { + *(dst++) = *src; + last_was_space = 1; + } + } else if (*src == '\n') { + ++src; + break; + } else { + *(dst++) = *src; + last_was_space = 0; + } + } + + /* NULL terminate the string */ + *dst = 0; + + /* Raise the warning. Depending on the user's configuration, this + may raise an exception, and PyErr_WarnEx returns -1. */ + if (PyErr_WarnEx(FITSFixedWarning, buf, 1)) { + goto exit; + } + } + + status = 0; + + exit: + + Py_XDECREF(wcs_module); + Py_XDECREF(FITSFixedWarning); + + return status; +} + +PyObject* get_scaled_double_array_readonly( + /*@unused@*/ const char* propname, + double* value, + double *scale, + const npy_intp naxis, + const npy_intp* dims, + /*@shared@*/ PyObject* owner) { + + /* + This function is equivalent to get_double_array_readonly except that values + can be scaled before constructing the final array. The input parameters are as + in get_double_array_readonly except for the addition of scale which should be + an array of scalefactors with length dims[0] - the number of coordinate axes. + For now this supports only 1D or 2D arrays, as we do not need support for + higher dimensions. + */ + + PyArrayObject* array; + + array = (PyArrayObject*)PyArray_SimpleNew(naxis, dims, NPY_DOUBLE); + if (array == NULL) { + return NULL; + } + + double *array_data = (double *)PyArray_DATA(array); + + if (naxis == 1) { + for (npy_intp i = 0; i < dims[0]; ++i) { + array_data[i] = value[i] / scale[i]; + } + } else if (naxis == 2) { + for (npy_intp i = 0; i < dims[0]; ++i) { + for (npy_intp j = 0; j < dims[1]; ++j) { + array_data[i * dims[1] + j] = value[i * dims[1] + j] / scale[i]; + } + } + } else { + return NULL; + } + + // Make the array read-only + PyArray_CLEARFLAGS(array, NPY_ARRAY_WRITEABLE); + + return (PyObject*)array; + +} + +int cunit_empty(const char cunit[][72], int naxis) { + /* This function returns 0 if all the cunit values are empty, and 1 otherwise */ + for (int i = 0; i < naxis; ++i) { + if (strcmp(cunit[i], "") != 0) { + return 0; + } + } + return 1; +} + +/*************************************************************************** + * wtbarr-related global variables and functions * + ***************************************************************************/ + +static PyObject *get_wtbarr_data = NULL; + + +void _set_wtbarr_callback(PyObject* callback) { + Py_XINCREF(callback); /* Add a reference to new callback */ + Py_XDECREF(get_wtbarr_data); /* Dispose of previous callback */ + get_wtbarr_data = callback; /* Remember new callback */ +} + + +int _update_wtbarr_from_hdulist(PyObject *hdulist, struct wtbarr *wtb) { + PyArrayObject *arrayp=NULL; + PyObject *result=NULL; + int i, naxis, nelem, naxes[NPY_MAXDIMS]; + npy_intp *npy_naxes; + npy_double *appayp_data; + + if (hdulist == NULL || hdulist == Py_None) { + PyErr_SetString(PyExc_ValueError, + "HDUList is required to retrieve -TAB coordinates " + "and/or indices."); + return 0; + } + + if (wtb->ndim < 1) { + PyErr_SetString(PyExc_ValueError, "Number of dimensions should be positive."); + return 0; + } + + result = PyObject_CallFunction(get_wtbarr_data, "(OsiiCsli)", hdulist, + wtb->extnam, wtb->extver, wtb->extlev, wtb->kind, wtb->ttype, wtb->row, + wtb->ndim); + + if (result == NULL) return 0; + + arrayp = (PyArrayObject *)PyArray_FromAny(result, + PyArray_DescrFromType(NPY_DOUBLE), 0, 0, NPY_ARRAY_CARRAY, NULL); + + Py_DECREF(result); + + if (arrayp == NULL) { + PyErr_SetString(PyExc_TypeError, "Unable to convert wtbarr callback " + "result to a numpy.ndarray."); + return 0; + } + + if (!PyArray_Check((PyObject*)arrayp)) { + PyErr_SetString(PyExc_TypeError, + "wtbarr callback must return a numpy.ndarray type " + "coordinate or index array."); + Py_DECREF(arrayp); + return 0; + } + + naxis = PyArray_NDIM(arrayp); + + if (naxis == 0) { + PyErr_SetString(PyExc_ValueError, "-TAB coordinate or index arrays " + "cannot be 0-dimensional."); + Py_DECREF(arrayp); + return 0; + } + + npy_naxes = PyArray_DIMS(arrayp); + for (i = 0; i < naxis; i++) { + naxes[i] = (int) npy_naxes[i]; + } + + if (naxis != wtb->ndim) { + if (wtb->kind == 'c' && wtb->ndim == 2 && naxis == 1) { + /* Allow TDIMn to be omitted for degenerate coordinate arrays. */ + naxis = 2; + naxes[1] = 1; + } else { + PyErr_Format(PyExc_ValueError, + "An array with an unexpected number of axes was " + "received from the callback. Expected %d but got %d.", + wtb->ndim, (int) naxis); + Py_DECREF(arrayp); + return 0; + } + } + + if (wtb->kind == 'c') { + /* Coordinate array; calculate the array size. */ + nelem = naxes[naxis-1]; + for (i = 0; i < naxis-1; i++) { + *(wtb->dimlen + i) = naxes[naxis-2-i]; + nelem *= naxes[i]; + } + } else { + /* Index vector; check length. */ + if ((nelem = naxes[naxis-1]) != *(wtb->dimlen)) { + /* N.B. coordinate array precedes the index vectors. */ + PyErr_Format(PyExc_ValueError, + "An index array with an unexpected number of dimensions was " + "received from the callback. Expected %d but got %d.", + *(wtb->dimlen), (int) nelem); + Py_DECREF(arrayp); + return 0; + } + } + + /* Allocate memory for the array. */ + if (!((*wtb->arrayp) = calloc((size_t)nelem, sizeof(double)))) { + PyErr_SetString(PyExc_MemoryError, "Out of memory: can't allocate " + "coordinate or index array."); + Py_DECREF(arrayp); + return 0; + } + + /* Read the array from the table. */ + appayp_data = (npy_double*)PyArray_DATA(arrayp); + for (i = 0; i < nelem; i++) { + (*wtb->arrayp)[i] = (double)appayp_data[i]; + } + + Py_DECREF(arrayp); + return 1; +} + + /*************************************************************************** * PyWcsprm methods */ -static int +int PyWcsprm_cset(PyWcsprm* self, const int convert); static INLINE void @@ -70,13 +367,18 @@ PyWcsprm_dealloc( PyWcsprm* self) { wcsfree(&self->x); - Py_TYPE(self)->tp_free((PyObject*)self); + PyTypeObject *tp = Py_TYPE((PyObject*)self); + freefunc free_func = PyType_GetSlot(tp, Py_tp_free); + free_func((PyObject*)self); + Py_DECREF(tp); } static PyWcsprm* PyWcsprm_cnew(void) { PyWcsprm* self; - self = (PyWcsprm*)(&PyWcsprmType)->tp_alloc(&PyWcsprmType, 0); + PyTypeObject* type = (PyTypeObject*)PyWcsprmType; + allocfunc alloc_func = PyType_GetSlot(type, Py_tp_alloc); + self = (PyWcsprm*)alloc_func(type, 0); return self; } @@ -87,7 +389,8 @@ PyWcsprm_new( /*@unused@*/ PyObject* kwds) { PyWcsprm* self; - self = (PyWcsprm*)type->tp_alloc(type, 0); + allocfunc alloc_func = PyType_GetSlot(type, Py_tp_alloc); + self = (PyWcsprm*)alloc_func(type, 0); return (PyObject*)self; } @@ -99,6 +402,7 @@ PyWcsprm_init( int status; PyObject* header_obj = NULL; + PyObject* hdulist = NULL; char * header = NULL; Py_ssize_t header_length = 0; Py_ssize_t nkeyrec = 0; @@ -109,30 +413,29 @@ PyWcsprm_init( int keysel = -1; PyObject* colsel = Py_None; PyArrayObject* colsel_array = NULL; + int* colsel_data = NULL; int* colsel_ints = NULL; - int ctrl = 0; + int warnings = 1; int nreject = 0; int nwcs = 0; struct wcsprm* wcs = NULL; - int i = 0; + int i, j; + int preserve_units = 0; const char* keywords[] = {"header", "key", "relax", "naxis", "keysel", - "colsel", NULL}; + "colsel", "warnings", "hdulist", "preserve_units", NULL}; if (!PyArg_ParseTupleAndKeywords( - args, kwds, "|OsOiiO:WCSBase.__init__", + args, kwds, "|OsOiiOiOp:WCSBase.__init__", (char **)keywords, &header_obj, &key, &relax_obj, &naxis, &keysel, - &colsel)) { + &colsel, &warnings, &hdulist, &preserve_units)) { return -1; } - if (header_obj == NULL || header_obj == Py_None) { - if (relax_obj != NULL && relax_obj != Py_False) { - PyErr_SetString( - PyExc_ValueError, - "If no header is provided, relax may not be provided either."); - return -1; - } + // The user can optionally choose to preserve the original units rather + // than use the units WCSLIB converts to (e.g. arcsec -> deg) + self->preserve_units = preserve_units; + if (header_obj == NULL || header_obj == Py_None) { if (keysel > 0) { PyErr_SetString( PyExc_ValueError, @@ -159,7 +462,6 @@ PyWcsprm_init( return -1; } - note_change(self); self->x.flag = -1; status = wcsini(1, naxis, &self->x); @@ -179,11 +481,7 @@ PyWcsprm_init( return 0; } else { /* header != NULL */ - #if PY3K if (PyBytes_AsStringAndSize(header_obj, &header, &header_length)) { - #else - if (PyString_AsStringAndSize(header_obj, &header, &header_length)) { - #endif return -1; } @@ -192,11 +490,7 @@ PyWcsprm_init( } else if (relax_obj == NULL || relax_obj == Py_False) { relax = WCSHDR_none; } else { - #if PY3K relax = (int)PyLong_AsLong(relax_obj); - #else - relax = (int)PyInt_AsLong(relax_obj); - #endif if (relax == -1) { PyErr_SetString( PyExc_ValueError, @@ -226,7 +520,7 @@ PyWcsprm_init( if (colsel != Py_None) { colsel_array = (PyArrayObject*) PyArray_ContiguousFromAny( - colsel, 1, 1, PyArray_INT); + colsel, NPY_INT, 1, 1); if (colsel_array == NULL) { return -1; } @@ -241,19 +535,62 @@ PyWcsprm_init( } colsel_ints[0] = (int)PyArray_DIM(colsel_array, 0); + colsel_data = (int *)PyArray_DATA(colsel_array); for (i = 0; i < colsel_ints[0]; ++i) { - colsel_ints[i+1] = colsel_array->data[i]; + colsel_ints[i+1] = colsel_data[i]; } Py_DECREF(colsel_array); } + wcsprintf_set(NULL); + + /* Call the header parser twice, the first time to get warnings + out about "rejected" keywords (which we can then send to Python + as warnings), and the second time to get a corrected wcsprm + object. */ + + if (keysel < 0) { + status = wcspih( + header, + (int)nkeyrec, + WCSHDR_reject, + 2, + &nreject, + &nwcs, + &wcs); + } else { + status = wcsbth( + header, + (int)nkeyrec, + WCSHDR_reject, + 2, + keysel, + colsel_ints, + &nreject, + &nwcs, + &wcs); + } + + if (status != 0) { + free(colsel_ints); + wcshdr_err_to_python_exc(status, wcs); + return -1; + } + + wcsvfree(&nwcs, &wcs); + + if (warnings && convert_rejections_to_warnings()) { + free(colsel_ints); + return -1; + } + if (keysel < 0) { status = wcspih( header, (int)nkeyrec, relax, - ctrl, + 0, &nreject, &nwcs, &wcs); @@ -262,7 +599,7 @@ PyWcsprm_init( header, (int)nkeyrec, relax, - ctrl, + 0, keysel, colsel_ints, &nreject, @@ -273,13 +610,12 @@ PyWcsprm_init( free(colsel_ints); if (status != 0) { - PyErr_SetString( - PyExc_MemoryError, - "Memory allocation error."); + wcshdr_err_to_python_exc(status, wcs); return -1; } if (nwcs == 0) { + wcsvfree(&nwcs, &wcs); PyErr_SetString( WcsExc_NoWcsKeywordsFound, "No WCS keywords found in the given header"); @@ -310,6 +646,16 @@ PyWcsprm_init( return -1; } + if (self->x.ntab) { + wcstab(&self->x); + for (j = 0; j < self->x.nwtb; j++) { + if (!_update_wtbarr_from_hdulist(hdulist, &(self->x.wtb[j]))) { + wcsfree(&self->x); + return -1; + } + } + } + note_change(self); wcsprm_c2python(&self->x); wcsvfree(&nwcs, &wcs); @@ -317,36 +663,131 @@ PyWcsprm_init( } } +/*@null@*/ static PyObject* +PyWcsprm_bounds_check( + PyWcsprm* self, + PyObject* args, + PyObject* kwds) { + + unsigned char pix2sky = 1; + unsigned char sky2pix = 1; + int bounds = 0; + const char* keywords[] = {"pix2world", "world2pix", NULL}; + + if (!PyArg_ParseTupleAndKeywords( + args, kwds, "|bb:bounds_check", (char **)keywords, + &pix2sky, &sky2pix)) { + return NULL; + } + + if (pix2sky) { + bounds |= 2|4; + } + + if (sky2pix) { + bounds |= 1; + } + + wcsprm_python2c(&self->x); + wcsbchk(&self->x, bounds); + + Py_RETURN_NONE; +} + + /*@null@*/ static PyObject* PyWcsprm_copy( PyWcsprm* self) { - PyWcsprm* copy = NULL; - int status; + PyWcsprm* copy = NULL; + int status; copy = PyWcsprm_cnew(); if (copy == NULL) { return NULL; } + wcsini(0, self->x.naxis, ©->x); + wcsprm_python2c(&self->x); status = wcscopy(1, &self->x, ©->x); wcsprm_c2python(&self->x); + // Also copy over any information related to preserving units + + copy->preserve_units = self->preserve_units; + + if (self->original_cunit != NULL) { + copy->original_cunit = malloc(copy->x.naxis * sizeof(*copy->original_cunit)); + if (self->original_cunit == NULL) { + PyErr_NoMemory(); + return NULL; + } + for (int i = 0; i < copy->x.naxis; ++i) { + strncpy(copy->original_cunit[i], self->original_cunit[i], 72); + } + } + + if (self->unit_scaling != NULL) { + copy->unit_scaling = malloc(copy->x.naxis * sizeof(double)); + if (copy->unit_scaling == NULL) { + PyErr_NoMemory(); + return NULL; + } + for (int i = 0; i < copy->x.naxis; ++i) { + copy->unit_scaling[i] = self->unit_scaling[i]; + } + } + + if (status == 0) { if (PyWcsprm_cset(copy, 0)) { - Py_XDECREF(copy); + Py_XDECREF((PyObject*)copy); return NULL; } + wcsprm_c2python(©->x); return (PyObject*)copy; } else { - Py_XDECREF(copy); + Py_XDECREF((PyObject*)copy); wcs_to_python_exc(&(self->x)); return NULL; } } +static PyWcsprm* PyWcsprm_copy_with_patched_units(PyWcsprm* source) { + + // This function returns a copy of a PyWcsprm with units patched + // to match the original units (before WCSLIB changed them). The + // returned object should not be used to do any kind of transformations + // and is only for use in e.g. converting to a header, or printing + // contents. + + int original_flag; + PyWcsprm* copy = (PyWcsprm*)PyWcsprm_copy(source); + + // We make sure wcsset is happy + wcsset(©->x); + + // We preserve the value of the flag which indicates that wcsset + // has been called and there have been no further modifications + original_flag = copy->x.flag; + + // We now override the unit, cdelt and crval values + for (int i = 0; i < copy->x.naxis; ++i) { + strncpy(copy->x.cunit[i], source->original_cunit[i], 72); + copy->x.cdelt[i] /= source->unit_scaling[i]; + copy->x.crval[i] /= source->unit_scaling[i]; + } + + // We restore the flag to trick WCSLIB into thinking it no longer + // needs to change the units. + copy->x.flag = original_flag; + + return copy; + +} + PyObject* PyWcsprm_find_all_wcs( PyObject* __, @@ -360,27 +801,23 @@ PyWcsprm_find_all_wcs( PyObject* relax_obj = NULL; int relax = 0; int keysel = 0; - int ctrl = 0; + int warnings = 1; int nreject = 0; int nwcs = 0; struct wcsprm* wcs = NULL; PyObject* result = NULL; PyWcsprm* subresult = NULL; int i = 0; - const char* keywords[] = {"header", "relax", "keysel", NULL}; + const char* keywords[] = {"header", "relax", "keysel", "warnings", NULL}; int status = -1; if (!PyArg_ParseTupleAndKeywords( - args, kwds, "O|Oi:find_all_wcs", - (char **)keywords, &header_obj, &relax_obj, &keysel)) { + args, kwds, "O|Oii:find_all_wcs", + (char **)keywords, &header_obj, &relax_obj, &keysel, &warnings)) { return NULL; } - #if PY3K if (PyBytes_AsStringAndSize(header_obj, &header, &header_length)) { - #else - if (PyString_AsStringAndSize(header_obj, &header, &header_length)) { - #endif return NULL; } @@ -397,11 +834,7 @@ PyWcsprm_find_all_wcs( } else if (relax_obj == NULL || relax_obj == Py_False) { relax = WCSHDR_none; } else { - #if PY3K relax = (int)PyLong_AsLong(relax_obj); - #else - relax = (int)PyInt_AsLong(relax_obj); - #endif if (relax == -1) { PyErr_SetString( PyExc_ValueError, @@ -410,13 +843,53 @@ PyWcsprm_find_all_wcs( } } + /* Call the header parser twice, the first time to get warnings + out about "rejected" keywords (which we can then send to Python + as warnings), and the second time to get a corrected wcsprm + object. */ + + Py_BEGIN_ALLOW_THREADS + if (keysel < 0) { + status = wcspih( + header, + (int)nkeyrec, + WCSHDR_reject, + 2, + &nreject, + &nwcs, + &wcs); + } else { + status = wcsbth( + header, + (int)nkeyrec, + WCSHDR_reject, + 2, + keysel, + NULL, + &nreject, + &nwcs, + &wcs); + } + Py_END_ALLOW_THREADS + + if (status != 0) { + wcshdr_err_to_python_exc(status, wcs); + return NULL; + } + + wcsvfree(&nwcs, &wcs); + + if (warnings && convert_rejections_to_warnings()) { + return NULL; + } + Py_BEGIN_ALLOW_THREADS if (keysel < 0) { status = wcspih( header, (int)nkeyrec, relax, - ctrl, + 0, &nreject, &nwcs, &wcs); @@ -425,7 +898,7 @@ PyWcsprm_find_all_wcs( header, (int)nkeyrec, relax, - ctrl, + 0, keysel, NULL, &nreject, @@ -435,9 +908,7 @@ PyWcsprm_find_all_wcs( Py_END_ALLOW_THREADS if (status != 0) { - PyErr_SetString( - PyExc_MemoryError, - "Memory allocation error."); + wcshdr_err_to_python_exc(status, wcs); return NULL; } @@ -459,19 +930,12 @@ PyWcsprm_find_all_wcs( } if (PyList_SetItem(result, i, (PyObject *)subresult) == -1) { - Py_DECREF(subresult); Py_DECREF(result); wcsvfree(&nwcs, &wcs); return NULL; } subresult->x.flag = 0; - if (PyWcsprm_cset(subresult, 0)) { - Py_DECREF(subresult); - Py_DECREF(result); - wcsvfree(&nwcs, &wcs); - return NULL; - } wcsprm_c2python(&subresult->x); } @@ -490,11 +954,7 @@ PyWcsprm_cdfix( wcsprm_c2python(&self->x); if (status == -1 || status == 0) { - #if PY3K return PyLong_FromLong((long)status); - #else - return PyInt_FromLong((long)status); - #endif } else { wcserr_fix_to_python_exc(self->x.err); return NULL; @@ -512,47 +972,82 @@ PyWcsprm_celfix( wcsprm_c2python(&self->x); if (status == -1 || status == 0) { - #if PY3K return PyLong_FromLong((long)status); - #else - return PyInt_FromLong((long)status); - #endif } else { wcserr_fix_to_python_exc(self->x.err); return NULL; } } -/*@null@*/ static PyObject* -PyWcsprm_cylfix( +static PyObject * +PyWcsprm_compare( PyWcsprm* self, PyObject* args, PyObject* kwds) { - PyObject* naxis_obj = NULL; - PyArrayObject* naxis_array = NULL; - int* naxis = NULL; - int status = 0; - const char* keywords[] = {"naxis", NULL}; + int cmp = 0; + PyWcsprm *other; + double tolerance = 0.0; + int equal; + int status; + + const char* keywords[] = {"other", "cmp", "tolerance", NULL}; if (!PyArg_ParseTupleAndKeywords( - args, kwds, "|O:cylfix", (char **)keywords, - &naxis_obj)) { + args, kwds, "O!|id:compare", (char **)keywords, + (PyTypeObject*)PyWcsprmType, &other, &cmp, &tolerance)) { return NULL; } - if (naxis_obj != NULL) { - naxis_array = (PyArrayObject*)PyArray_ContiguousFromAny( - naxis_obj, 1, 1, PyArray_INT); - if (naxis_array == NULL) { - return NULL; - } - if (PyArray_DIM(naxis_array, 0) != self->x.naxis) { - PyErr_Format( - PyExc_ValueError, - "naxis must be same length as the number of axes of " - "the Wcsprm object (%d).", - self->x.naxis); + + wcsprm_python2c(&self->x); + wcsprm_python2c(&other->x); + status = wcscompare(cmp, tolerance, &self->x, &other->x, &equal); + wcsprm_c2python(&self->x); + wcsprm_c2python(&other->x); + + if (status) { + wcserr_fix_to_python_exc(self->x.err); + return NULL; + } else { + if (equal) { + Py_RETURN_TRUE; + } else { + Py_RETURN_FALSE; + } + } +} + +/*@null@*/ static PyObject* +PyWcsprm_cylfix( + PyWcsprm* self, + PyObject* args, + PyObject* kwds) { + + PyObject* naxis_obj = NULL; + PyArrayObject* naxis_array = NULL; + int* naxis = NULL; + int status = 0; + const char* keywords[] = {"naxis", NULL}; + + if (!PyArg_ParseTupleAndKeywords( + args, kwds, "|O:cylfix", (char **)keywords, + &naxis_obj)) { + return NULL; + } + + if (naxis_obj != NULL && naxis_obj != Py_None) { + naxis_array = (PyArrayObject*)PyArray_ContiguousFromAny( + naxis_obj, NPY_INT, 1, 1); + if (naxis_array == NULL) { + return NULL; + } + if (PyArray_DIM(naxis_array, 0) != self->x.naxis) { + PyErr_Format( + PyExc_ValueError, + "naxis must be same length as the number of axes of " + "the Wcsprm object (%d).", + self->x.naxis); Py_DECREF(naxis_array); return NULL; } @@ -563,14 +1058,10 @@ PyWcsprm_cylfix( status = cylfix(naxis, &self->x); wcsprm_c2python(&self->x); - Py_XDECREF(naxis_array); + Py_XDECREF((PyObject*)naxis_array); if (status == -1 || status == 0) { - #if PY3K return PyLong_FromLong((long)status); - #else - return PyInt_FromLong((long)status); - #endif } else { wcserr_fix_to_python_exc(self->x.err); return NULL; @@ -588,17 +1079,96 @@ PyWcsprm_datfix( wcsprm_c2python(&self->x); if (status == -1 || status == 0) { - #if PY3K return PyLong_FromLong((long)status); - #else - return PyInt_FromLong((long)status); - #endif } else { wcserr_fix_to_python_exc(self->x.err); return NULL; } } +int initialize_preserve_units(PyWcsprm* self) { + + // If the user has requested to preserve units, we keep track of what CUNIT + // was before and after fixing so that we can store the scaling factor if + // needed. However, we don't do this yet if the units have not been set. + if (self->preserve_units == 1 && !cunit_empty(self->x.cunit, self->x.naxis)) { + + if (self->original_cunit == NULL) { + self->original_cunit = malloc(self->x.naxis * sizeof(*self->original_cunit)); + if (self->original_cunit == NULL) { + PyErr_NoMemory(); + return -1; + } + for (int i = 0; i < self->x.naxis; ++i) { + strncpy(self->original_cunit[i], self->x.cunit[i], 72); + } + } + + } + + return 0; + +} + +int check_unit_changes(PyWcsprm* self) { + + double scale, offset, power; + int status; + + // Check which units have changed, and store scaling if changed. + if (self->original_cunit != NULL) { + + if (self->unit_scaling == NULL) { + self->unit_scaling = malloc(self->x.naxis * sizeof(double)); + if (self->unit_scaling == NULL) { + PyErr_NoMemory(); + return -1; + } + } + + for (int i = 0; i < self->x.naxis; ++i) { + if (strcmp(self->original_cunit[i], self->x.cunit[i]) != 0) { + status = wcsunits(self->original_cunit[i], self->x.cunit[i], &scale, &offset, &power); + if (status == 0) { + // We don't support offset and power currently because it should not be needed, + // and would introduce additional complexity. However, if needed this can be + // supported in future. + if (offset != 0 || power != 1) + PyErr_Format( + PyExc_ValueError, + "Preserving original units with non-trivial offset and power is not supported " + ); + self->unit_scaling[i] = scale; + } + } else { + self->unit_scaling[i] = 1.0; + } + + } + + // We now check if all the scales are 1. If so, then we actually deallocate the array + // again to not waste time doing unit conversions subsequently. It might seem wasteful + // to allocate the array and then deallocate it, but the other option would be to + // allocate a temporary array for the scales in this function and then only if all the + // values are not 1 allocate self->unit_scaling, but then we'd be doing two allocations. + + int scaling_needed = 0; + for (int i = 0; i < self->x.naxis; ++i) { + if (self->unit_scaling[i] != 1.0) { + scaling_needed = 1; + break; + } + } + if (!scaling_needed) { + free(self->unit_scaling); + self->unit_scaling = NULL; + } + + } + return 0; +} + + /*@null@*/ static PyObject* PyWcsprm_fix( PyWcsprm* self, @@ -625,6 +1195,9 @@ PyWcsprm_fix( const struct message_map_entry message_map[NWCSFIX] = { {"cdfix", CDFIX}, {"datfix", DATFIX}, +#if (NWCSFIX > 6) + {"obsfix", OBSFIX}, +#endif {"unitfix", UNITFIX}, {"celfix", CELFIX}, {"spcfix", SPCFIX}, @@ -644,9 +1217,9 @@ PyWcsprm_fix( } } - if (naxis_obj != NULL) { + if (naxis_obj != NULL && naxis_obj != Py_None) { naxis_array = (PyArrayObject*)PyArray_ContiguousFromAny( - naxis_obj, 1, 1, PyArray_INT); + naxis_obj, NPY_INT, 1, 1); if (naxis_array == NULL) { return NULL; } @@ -664,13 +1237,17 @@ PyWcsprm_fix( memset(err, 0, sizeof(struct wcserr) * NWCSFIX); + initialize_preserve_units(self); + wcsprm_python2c(&self->x); wcsfixi(ctrl, naxis, &self->x, stat, err); wcsprm_c2python(&self->x); + check_unit_changes(self); + /* We're done with this already, so deref now so we don't have to remember later */ - Py_XDECREF(naxis_array); + Py_XDECREF((PyObject*)naxis_array); result = PyDict_New(); if (result == NULL) { @@ -687,11 +1264,7 @@ PyWcsprm_fix( message = "No change"; } } - #if PY3K subresult = PyUnicode_FromString(message); - #else - subresult = PyString_FromString(message); - #endif if (subresult == NULL || PyDict_SetItemString(result, message_map[i].name, subresult)) { Py_XDECREF(subresult); @@ -711,6 +1284,7 @@ PyWcsprm_get_cdelt_func( /*@unused@*/ PyObject* kwds) { Py_ssize_t naxis = 0; + PyObject *result; if (is_null(self->x.cdelt)) { return NULL; @@ -722,7 +1296,13 @@ PyWcsprm_get_cdelt_func( naxis = self->x.naxis; - return get_double_array_readonly("cdelt", self->x.cdelt, 1, &naxis, (PyObject*)self); + if (self->unit_scaling != NULL) { + result = get_scaled_double_array_readonly("cdelt", self->x.cdelt, self->unit_scaling, 1, &naxis, (PyObject*)self); + } else { + result = get_double_array_readonly("cdelt", self->x.cdelt, 1, &naxis, (PyObject*)self); + } + + return result; } /*@null@*/ static PyObject* @@ -753,13 +1333,6 @@ PyWcsprm_get_ps( /*@unused@*/ PyObject* args, /*@unused@*/ PyObject* kwds) { - if (self->x.ps == NULL) { - PyErr_SetString( - PyExc_AssertionError, - "No PSi_ma records present."); - return NULL; - } - return get_pscards("ps", self->x.ps, self->x.nps); } @@ -769,13 +1342,6 @@ PyWcsprm_get_pv( /*@unused@*/ PyObject* args, /*@unused@*/ PyObject* kwds) { - if (self->x.pv == NULL) { - PyErr_SetString( - PyExc_AssertionError, - "No PVi_ma records present."); - return NULL; - } - return get_pvcards("pv", self->x.pv, self->x.npv); } @@ -863,7 +1429,7 @@ PyWcsprm_mix( } world = (PyArrayObject*)PyArray_ContiguousFromAny - (world_obj, PyArray_DOUBLE, 1, 1); + (world_obj, NPY_DOUBLE, 1, 1); if (world == NULL) { PyErr_SetString( PyExc_TypeError, @@ -880,7 +1446,7 @@ PyWcsprm_mix( } pixcrd = (PyArrayObject*)PyArray_ContiguousFromAny - (pixcrd_obj, PyArray_DOUBLE, 1, 1); + (pixcrd_obj, NPY_DOUBLE, 1, 1); if (pixcrd == NULL) { PyErr_SetString( PyExc_TypeError, @@ -917,19 +1483,19 @@ PyWcsprm_mix( */ naxis = (Py_ssize_t)self->x.naxis; phi = (PyArrayObject*)PyArray_SimpleNew - (1, &naxis, PyArray_DOUBLE); + (1, &naxis, NPY_DOUBLE); if (phi == NULL) { goto exit; } theta = (PyArrayObject*)PyArray_SimpleNew - (1, &naxis, PyArray_DOUBLE); + (1, &naxis, NPY_DOUBLE); if (theta == NULL) { goto exit; } imgcrd = (PyArrayObject*)PyArray_SimpleNew - (1, &naxis, PyArray_DOUBLE); + (1, &naxis, NPY_DOUBLE); if (imgcrd == NULL) { goto exit; } @@ -967,11 +1533,11 @@ PyWcsprm_mix( } exit: - Py_XDECREF(world); - Py_XDECREF(phi); - Py_XDECREF(theta); - Py_XDECREF(imgcrd); - Py_XDECREF(pixcrd); + Py_XDECREF((PyObject*)world); + Py_XDECREF((PyObject*)phi); + Py_XDECREF((PyObject*)theta); + Py_XDECREF((PyObject*)imgcrd); + Py_XDECREF((PyObject*)pixcrd); if (status == 0) { return result; @@ -1018,12 +1584,15 @@ PyWcsprm_p2s( naxis = self->x.naxis; pixcrd = (PyArrayObject*)PyArray_ContiguousFromAny - (pixcrd_obj, PyArray_DOUBLE, 2, 2); + (pixcrd_obj, NPY_DOUBLE, 2, 2); if (pixcrd == NULL) { return NULL; } - if (PyArray_DIM(pixcrd, 1) < naxis) { + ncoord = PyArray_DIM(pixcrd, 0); + nelem = PyArray_DIM(pixcrd, 1); + + if (nelem < naxis) { PyErr_Format( PyExc_RuntimeError, "Input array must be 2-dimensional, where the second dimension >= %d", @@ -1034,39 +1603,45 @@ PyWcsprm_p2s( /* Now we allocate a bunch of numpy arrays to store the results in. */ imgcrd = (PyArrayObject*)PyArray_SimpleNew( - 2, PyArray_DIMS(pixcrd), PyArray_DOUBLE); + 2, PyArray_DIMS(pixcrd), NPY_DOUBLE); if (imgcrd == NULL) { goto exit; } phi = (PyArrayObject*)PyArray_SimpleNew( - 1, PyArray_DIMS(pixcrd), PyArray_DOUBLE); + 1, PyArray_DIMS(pixcrd), NPY_DOUBLE); if (phi == NULL) { goto exit; } theta = (PyArrayObject*)PyArray_SimpleNew( - 1, PyArray_DIMS(pixcrd), PyArray_DOUBLE); + 1, PyArray_DIMS(pixcrd), NPY_DOUBLE); if (theta == NULL) { goto exit; } world = (PyArrayObject*)PyArray_SimpleNew( - 2, PyArray_DIMS(pixcrd), PyArray_DOUBLE); + 2, PyArray_DIMS(pixcrd), NPY_DOUBLE); if (world == NULL) { goto exit; } stat = (PyArrayObject*)PyArray_SimpleNew( - 1, PyArray_DIMS(pixcrd), PyArray_INT); + 1, PyArray_DIMS(pixcrd), NPY_INT); if (stat == NULL) { goto exit; } + // Here we force a call to wcsset. Normally, WCSLIB will call wcsset automatically when + // calling wcsp2s, but we need to call it ourselves using PyWcsprm_cset so that we can + // catch cases where the units might change if e.g. they are not in SI to start with. + /* Force a call to wcsset here*/ + if (self->preserve_units && PyWcsprm_cset(self, 1)) { + return NULL; + } + /* Make the call */ Py_BEGIN_ALLOW_THREADS - ncoord = PyArray_DIM(pixcrd, 0); - nelem = PyArray_DIM(pixcrd, 1); preoffset_array(pixcrd, origin); wcsprm_python2c(&self->x); status = wcsp2s( @@ -1096,6 +1671,16 @@ PyWcsprm_p2s( Py_END_ALLOW_THREADS if (status == 0 || status == 8) { + // Since the conversion succeeded, if user has requested to preserve units, + // we convert the world coordinates to the original units + if (self->unit_scaling != NULL) { + double *world_data = (double *)PyArray_DATA(world); + for (npy_intp i = 0; i < nelem; ++i) { + for (npy_intp j = 0; j < ncoord; ++j) { + world_data[j * nelem + i] /= self->unit_scaling[i]; + } + } + } result = PyDict_New(); if (result == NULL || PyDict_SetItemString(result, "imgcrd", (PyObject*)imgcrd) || @@ -1108,12 +1693,12 @@ PyWcsprm_p2s( } exit: - Py_XDECREF(pixcrd); - Py_XDECREF(imgcrd); - Py_XDECREF(phi); - Py_XDECREF(theta); - Py_XDECREF(world); - Py_XDECREF(stat); + Py_XDECREF((PyObject*)pixcrd); + Py_XDECREF((PyObject*)imgcrd); + Py_XDECREF((PyObject*)phi); + Py_XDECREF((PyObject*)theta); + Py_XDECREF((PyObject*)world); + Py_XDECREF((PyObject*)stat); if (status == 0 || status == 8) { return result; @@ -1148,6 +1733,7 @@ PyWcsprm_s2p( PyArrayObject* stat = NULL; PyObject* result = NULL; int status = -1; + double* world_copy = NULL; const char* keywords[] = { "world", "origin", NULL }; @@ -1160,12 +1746,15 @@ PyWcsprm_s2p( naxis = self->x.naxis; world = (PyArrayObject*)PyArray_ContiguousFromAny( - world_obj, PyArray_DOUBLE, 2, 2); + world_obj, NPY_DOUBLE, 2, 2); if (world == NULL) { return NULL; } - if (PyArray_DIM(world, 1) < naxis) { + ncoord = (int)PyArray_DIM(world, 0); + nelem = (int)PyArray_DIM(world, 1); + + if (nelem < naxis) { PyErr_Format( PyExc_RuntimeError, "Input array must be 2-dimensional, where the second dimension >= %d", @@ -1173,50 +1762,82 @@ PyWcsprm_s2p( goto exit; } + // Here we force a call to wcsset. Normally, WCSLIB will call wcsset automatically when + // calling wcsp2s, but we need to call it ourselves using PyWcsprm_cset so that we can + // catch cases where the units might change if e.g. they are not in SI to start with. + /* Force a call to wcsset here*/ + + if (self->preserve_units && PyWcsprm_cset(self, 1)) { + return NULL; + } + + // Convert world units to native WCSLIB units if needed. Here we use a check that + // original_cunit is allocated, because the above line does not guarantee that + // original_cunit will be allocated (for instance if the units are not set) + + double *world_data = (double *)PyArray_DATA(world); + + if (self->unit_scaling != NULL) { + + world_copy = malloc(sizeof(double) * nelem * ncoord); + if (!world_copy) { + return PyErr_NoMemory(); + return NULL; + } + + for (npy_intp i = 0; i < nelem; ++i) { + for (npy_intp j = 0; j < ncoord; ++j) { + world_copy[j * nelem + i] = world_data[j * nelem + i] * self->unit_scaling[i]; + } + } + + world_data = world_copy; + + } + + /* Now we allocate a bunch of numpy arrays to store the * results in. */ phi = (PyArrayObject*)PyArray_SimpleNew( - 1, PyArray_DIMS(world), PyArray_DOUBLE); + 1, PyArray_DIMS(world), NPY_DOUBLE); if (phi == NULL) { goto exit; } theta = (PyArrayObject*)PyArray_SimpleNew( - 1, PyArray_DIMS(world), PyArray_DOUBLE); + 1, PyArray_DIMS(world), NPY_DOUBLE); if (phi == NULL) { goto exit; } imgcrd = (PyArrayObject*)PyArray_SimpleNew( - 2, PyArray_DIMS(world), PyArray_DOUBLE); + 2, PyArray_DIMS(world), NPY_DOUBLE); if (theta == NULL) { goto exit; } pixcrd = (PyArrayObject*)PyArray_SimpleNew( - 2, PyArray_DIMS(world), PyArray_DOUBLE); + 2, PyArray_DIMS(world), NPY_DOUBLE); if (pixcrd == NULL) { goto exit; } stat = (PyArrayObject*)PyArray_SimpleNew( - 1, PyArray_DIMS(world), PyArray_INT); + 1, PyArray_DIMS(world), NPY_INT); if (stat == NULL) { goto exit; } /* Make the call */ Py_BEGIN_ALLOW_THREADS - ncoord = (int)PyArray_DIM(world, 0); - nelem = (int)PyArray_DIM(world, 1); /* preoffset_array(world, origin); */ wcsprm_python2c(&self->x); status = wcss2p( &self->x, ncoord, nelem, - (double*)PyArray_DATA(world), + world_data, (double*)PyArray_DATA(phi), (double*)PyArray_DATA(theta), (double*)PyArray_DATA(imgcrd), @@ -1226,7 +1847,7 @@ PyWcsprm_s2p( /* unoffset_array(world, origin); */ unoffset_array(pixcrd, origin); unoffset_array(imgcrd, origin); - if (status == 8) { + if (status == 9) { set_invalid_to_nan( ncoord, 1, (double*)PyArray_DATA(phi), (int*)PyArray_DATA(stat)); set_invalid_to_nan( @@ -1251,12 +1872,17 @@ PyWcsprm_s2p( } exit: - Py_XDECREF(pixcrd); - Py_XDECREF(imgcrd); - Py_XDECREF(phi); - Py_XDECREF(theta); - Py_XDECREF(world); - Py_XDECREF(stat); + + if (world_copy!=NULL) { + free(world_copy); + } + + Py_XDECREF((PyObject*)pixcrd); + Py_XDECREF((PyObject*)imgcrd); + Py_XDECREF((PyObject*)phi); + Py_XDECREF((PyObject*)theta); + Py_XDECREF((PyObject*)world); + Py_XDECREF((PyObject*)stat); if (status == 0 || status == 9) { return result; @@ -1272,17 +1898,27 @@ PyWcsprm_s2p( } } -static int +int PyWcsprm_cset( PyWcsprm* self, const int convert) { int status = 0; + // We want to avoid calling wcsset whenever possible as it is not thread-safe. We use wcsenq + // to see if the checksum of the wcsprm elements has changed since wcsset was last called. + if (wcsenq(&self->x, WCSENQ_CHK)) { + return 0; + } + + initialize_preserve_units(self); + if (convert) wcsprm_python2c(&self->x); status = wcsset(&self->x); if (convert) wcsprm_c2python(&self->x); + check_unit_changes(self); + if (status == 0) { return 0; } else { @@ -1333,18 +1969,14 @@ PyWcsprm_set_pv( if (is_null(self->x.pv)) { return NULL; - } - - if (set_pvcards("pv", arg, &self->x.pv, &self->x.npv, &self->x.npvmax)) { - self->x.m_pv = self->x.pv; + } else if (set_pvcards("pv", arg, &self->x.pv, &self->x.npv, &self->x.npvmax)) { return NULL; + } else { + self->x.m_pv = self->x.pv; + note_change(self); + Py_INCREF(Py_None); + return Py_None; } - self->x.m_pv = self->x.pv; - - note_change(self); - - Py_INCREF(Py_None); - return Py_None; } /* TODO: This is convenient for debugging for now -- but it's not very @@ -1363,10 +1995,18 @@ PyWcsprm_print_contents( wcsprm_c2python(&self->x); return NULL; } - wcsprt(&self->x); - wcsprm_c2python(&self->x); + +if (self->unit_scaling != NULL) { + PyWcsprm* copy = PyWcsprm_copy_with_patched_units(self); + wcsprt(©->x); + PyWcsprm_dealloc(copy); + } else { + wcsprt(&self->x); + wcsprm_c2python(&self->x); + } printf("%s", wcsprintf_buf()); + fflush(stdout); Py_INCREF(Py_None); return Py_None; @@ -1383,11 +2023,7 @@ PyWcsprm_spcfix( wcsprm_c2python(&self->x); if (status == -1 || status == 0) { - #if PY3K return PyLong_FromLong((long)status); - #else - return PyInt_FromLong((long)status); - #endif } else { wcserr_fix_to_python_exc(self->x.err); return NULL; @@ -1446,14 +2082,56 @@ PyWcsprm___str__( wcsprm_c2python(&self->x); return NULL; } - wcsprt(&self->x); - wcsprm_c2python(&self->x); - #if PY3K + if (self->unit_scaling != NULL) { + PyWcsprm* copy = PyWcsprm_copy_with_patched_units(self); + wcsprt(©->x); + PyWcsprm_dealloc(copy); + } else { + wcsprt(&self->x); + wcsprm_c2python(&self->x); + } + return PyUnicode_FromString(wcsprintf_buf()); - #else - return PyString_FromString(wcsprintf_buf()); - #endif +} + +PyObject *PyWcsprm_richcompare(PyObject *a, PyObject *b, int op) { + int equal; + int status; + + struct wcsprm *ax; + struct wcsprm *bx; + + if ((op == Py_EQ || op == Py_NE) && + PyObject_TypeCheck(b, (PyTypeObject*)PyWcsprmType)) { + ax = &((PyWcsprm *)a)->x; + bx = &((PyWcsprm *)b)->x; + + wcsprm_python2c(ax); + wcsprm_python2c(bx); + status = wcscompare( + WCSCOMPARE_ANCILLARY, 0.0, + ax, bx, &equal); + wcsprm_c2python(ax); + wcsprm_c2python(bx); + + if (status == 0) { + if (op == Py_NE) { + equal = !equal; + } + if (equal) { + Py_RETURN_TRUE; + } else { + Py_RETURN_FALSE; + } + } else { + wcs_to_python_exc(&(((PyWcsprm *)a)->x)); + return NULL; + } + } + + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; } /*@null@*/ static PyObject* @@ -1462,21 +2140,19 @@ PyWcsprm_sub( PyObject* args, PyObject* kwds) { - int i = -1; - Py_ssize_t tmp = 0; - PyObject* py_axes = NULL; - PyWcsprm* py_dest_wcs = NULL; - PyObject* element = NULL; - #if PY3K - PyObject* element_utf8 = NULL; - #endif - char* element_str = NULL; - int element_val = 0; - int nsub = 0; - int alloc_size = 0; - int* axes = NULL; - int status = -1; - const char* keywords[] = {"axes", NULL}; + int i = -1; + Py_ssize_t tmp = 0; + PyObject* py_axes = NULL; + PyWcsprm* py_dest_wcs = NULL; + PyObject* element = NULL; + PyObject* element_utf8 = NULL; + char* element_str = NULL; + int element_val = 0; + int nsub = 0; + int* axes = NULL; + int status = -1; + int wcslib_ver[] = {0, 0, 0}; + const char* keywords[] = {"axes", NULL}; if (!PyArg_ParseTupleAndKeywords( args, kwds, "|O:sub", (char **)keywords, @@ -1484,16 +2160,18 @@ PyWcsprm_sub( goto exit; } + wcslib_version(wcslib_ver); + if (py_axes == NULL || py_axes == Py_None) { /* leave all variables as is */ - } else if (PySequence_Check(py_axes)) { + } else if (PyList_Check(py_axes) || PyTuple_Check(py_axes)) { tmp = PySequence_Size(py_axes); if (tmp == -1) { goto exit; } nsub = (int)tmp; - axes = malloc(nsub * sizeof(int)); + axes = malloc(nsub * sizeof(int) * 2); if (axes == NULL) { PyErr_SetString(PyExc_MemoryError, "Out of memory"); goto exit; @@ -1505,19 +2183,18 @@ PyWcsprm_sub( goto exit; } - #if PY3K - if (PyUnicode_Check(element)) { - element_utf8 = PyUnicode_AsUTF8String(element); - if (element_utf8 == NULL) { - goto exit; + if (PyUnicode_Check(element) || PyBytes_Check(element)) { + if (PyUnicode_Check(element)) { + element_utf8 = PyUnicode_AsUTF8String(element); + if (element_utf8 == NULL) { + goto exit; + } + + element_str = PyBytes_AsString(element_utf8); + } else if (PyBytes_Check(element)) { + element_str = PyBytes_AsString(element); } - element_str = PyBytes_AsString(element_utf8); - Py_DECREF(element_utf8); element_utf8 = NULL; - #else - if (PyString_Check(element)) { - /* Doesn't return NULL, because we already known it's a string */ - element_str = PyString_AsString(element); - #endif + if (strncmp(element_str, "longitude", 10) == 0) { element_val = WCSSUB_LONGITUDE; } else if (strncmp(element_str, "latitude", 9) == 0) { @@ -1528,21 +2205,35 @@ PyWcsprm_sub( element_val = WCSSUB_SPECTRAL; } else if (strncmp(element_str, "stokes", 7) == 0) { element_val = WCSSUB_STOKES; +#if defined(WCSSUB_TIME) + } else if ((wcslib_ver[0] > 7 || (wcslib_ver[0] == 7 && wcslib_ver[1] >= 8)) && + strncmp(element_str, "temporal", 9) == 0) { + element_val = WCSSUB_TIME; +#else + } else if (strncmp(element_str, "temporal", 9) == 0) { + PyErr_Format( + PyExc_NotImplementedError, + "Support for 'temporal' axis requires WCSLIB version 7.8 or greater "\ + "but linked WCSLIB version is %s", wcslib_version(NULL) + ); + goto exit; +#endif } else if (strncmp(element_str, "celestial", 10) == 0) { element_val = WCSSUB_CELESTIAL; } else { PyErr_SetString( PyExc_ValueError, - "string values for axis sequence must be one of 'latitude', 'longitude', 'cubeface', 'spectral', 'stokes', or 'celestial'"); +#if defined(WCSSUB_TIME) + "string values for axis sequence must be one of 'latitude', 'longitude', 'cubeface', 'spectral', 'stokes', 'temporal', or 'celestial'" +#else + "string values for axis sequence must be one of 'latitude', 'longitude', 'cubeface', 'spectral', 'stokes', or 'celestial'" +#endif + ); goto exit; } - #if PY3K + Py_CLEAR(element_utf8); } else if (PyLong_Check(element)) { tmp = (Py_ssize_t)PyLong_AsSsize_t(element); - #else - } else if (PyInt_Check(element)) { - tmp = (Py_ssize_t)PyInt_AsLong(element); - #endif if (tmp == -1 && PyErr_Occurred()) { goto exit; } @@ -1556,16 +2247,10 @@ PyWcsprm_sub( axes[i] = element_val; - Py_DECREF(element); - element = NULL; + Py_CLEAR(element); } - #if PY3K } else if (PyLong_Check(py_axes)) { tmp = (Py_ssize_t)PyLong_AsSsize_t(py_axes); - #else - } else if (PyInt_Check(py_axes)) { - tmp = (Py_ssize_t)PyInt_AsLong(py_axes); - #endif if (tmp == -1 && PyErr_Occurred()) { goto exit; } @@ -1585,23 +2270,51 @@ PyWcsprm_sub( goto exit; } - if (nsub == 0) { - alloc_size = self->x.naxis; - } else { - alloc_size = nsub; - } - py_dest_wcs = (PyWcsprm*)PyWcsprm_cnew(); py_dest_wcs->x.flag = -1; - status = wcsini(1, alloc_size, &py_dest_wcs->x); + status = wcsini(0, nsub, &py_dest_wcs->x); if (status != 0) { goto exit; } wcsprm_python2c(&self->x); - status = wcssub(0, &self->x, &nsub, axes, &py_dest_wcs->x); + status = wcssub(1, &self->x, &nsub, axes, &py_dest_wcs->x); wcsprm_c2python(&self->x); + + // At this point, we need to copy over any relevant preserve_units + // information, but we need to be careful since axes may have been removed or + // re-ordered + + py_dest_wcs->preserve_units = self->preserve_units; + + // We check whether original_cunit is allocated to know whether we need to copy + // anything over - if it is not allocated then nothing needs to be done even + // if preserve_units is set. + + if (self->original_cunit != NULL) { + + initialize_preserve_units(py_dest_wcs); + + // Now override original_unit with the actual original units from the original WCS + + for (int i = 0; i < py_dest_wcs->x.naxis; ++i) { + + // The axis variable has now been modified by wcssub to give the mapping + // from new axis to original axis, with the axis numbers being 1-based. + // If axis is zero, this means that the axis is a new one and we should + // not do anything. + + if (axes[i] > 0) { + strncpy(py_dest_wcs->original_cunit[i], self->original_cunit[axes[i] - 1], 72); + } + + } + + check_unit_changes(py_dest_wcs); + +} if (PyWcsprm_cset(py_dest_wcs, 0)) { + status = -1; goto exit; } wcsprm_c2python(&py_dest_wcs->x); @@ -1613,19 +2326,17 @@ PyWcsprm_sub( exit: free(axes); Py_XDECREF(element); - #if PY3K Py_XDECREF(element_utf8); - #endif if (status == 0) { return (PyObject*)py_dest_wcs; } else if (status == -1) { - Py_XDECREF(py_dest_wcs); + Py_XDECREF((PyObject*)py_dest_wcs); /* Exception already set */ return NULL; } else { wcs_to_python_exc(&(py_dest_wcs->x)); - Py_XDECREF(py_dest_wcs); + Py_XDECREF((PyObject*)py_dest_wcs); return NULL; } } @@ -1653,13 +2364,9 @@ PyWcsprm_to_header( if (relax_obj == Py_True) { relax = WCSHDO_all; } else if (relax_obj == NULL || relax_obj == Py_False) { - relax = WCSHDO_none; + relax = WCSHDO_safe; } else { - #if PY3K relax = (int)PyLong_AsLong(relax_obj); - #else - relax = (int)PyInt_AsLong(relax_obj); - #endif if (relax == -1) { PyErr_SetString( PyExc_ValueError, @@ -1668,24 +2375,42 @@ PyWcsprm_to_header( } } - wcsprm_python2c(&self->x); - status = wcshdo(relax, &self->x, &nkeyrec, &header); - wcsprm_c2python(&self->x); + // If the user has requested to preserve the original units, then we + // could try and edit the header returned by wcshdo - however, this + // might be tricky and not robust to future WCSLIB changes. Instead, + // we make a copy of the Wcsprm object, change the units on that and + // prevent WCSLIB fixing the units, then convert to a header. + if (self->unit_scaling != NULL) { + + PyWcsprm* copy = PyWcsprm_copy_with_patched_units(self); + + wcsprm_python2c(©->x); + status = wcshdo(relax, ©->x, &nkeyrec, &header); + + if (status != 0) { + wcs_to_python_exc(&(copy->x)); + wcsfree(©->x); + goto exit; + } + + wcsfree(©->x); + + } else { + + wcsprm_python2c(&self->x); + status = wcshdo(relax, &self->x, &nkeyrec, &header); + wcsprm_c2python(&self->x); + + if (status != 0) { + wcs_to_python_exc(&(self->x)); + goto exit; + } - if (status != 0) { - PyErr_SetString( - PyExc_RuntimeError, - "Unknown error occurred. Something is seriously wrong."); - goto exit; } - /* Just return the raw header string. PyFITS on the Python side will help - to parse and use this information. */ - #if PY3K + /* Just return the raw header string. astropy.io.fits on the Python side will + help to parse and use this information. */ result = PyUnicode_FromStringAndSize(header, (Py_ssize_t)nkeyrec * 80); - #else - result = PyString_FromStringAndSize(header, (Py_ssize_t)nkeyrec * 80); - #endif exit: free(header); @@ -1718,11 +2443,7 @@ PyWcsprm_unitfix( status = unitfix(ctrl, &self->x); if (status == -1 || status == 0) { - #if PY3K return PyLong_FromLong((long)status); - #else - return PyInt_FromLong((long)status); - #endif } else { wcserr_fix_to_python_exc(self->x.err); return NULL; @@ -1776,8 +2497,6 @@ PyWcsprm_set_alt( strncpy(self->x.alt, value_string, 2); - note_change(self); - return 0; } @@ -1801,12 +2520,36 @@ PyWcsprm_get_axis_types( return get_int_array("axis_types", self->x.types, 1, &naxis, (PyObject*)self); } +static PyObject* +PyWcsprm_get_bepoch( + PyWcsprm* self, + /*@unused@*/ void* closure) { + + return get_double("bepoch", self->x.bepoch); +} + +static int +PyWcsprm_set_bepoch( + PyWcsprm* self, + PyObject* value, + /*@unused@*/ void* closure) { + + if (value == NULL) { + self->x.bepoch = (double)NPY_NAN; + return 0; + } + + return set_double("bepoch", value, &self->x.bepoch); +} + + /*@null@*/ static PyObject* PyWcsprm_get_cd( PyWcsprm* self, /*@unused@*/ void* closure) { npy_intp dims[2]; + PyObject* result; if (is_null(self->x.cd)) { return NULL; @@ -1820,7 +2563,13 @@ PyWcsprm_get_cd( dims[0] = self->x.naxis; dims[1] = self->x.naxis; - return get_double_array("cd", self->x.cd, 2, dims, (PyObject*)self); + if (self->unit_scaling != NULL) { + result = get_scaled_double_array_readonly("cd", self->x.cd, self->unit_scaling, 2, dims, (PyObject*)self); + } else { + result = get_double_array("cd", self->x.cd, 2, dims, (PyObject*)self); + } + + return result; } static int @@ -1849,6 +2598,14 @@ PyWcsprm_set_cd( return -1; } + if (self->unit_scaling != NULL) { + for (npy_intp i = 0; i < dims[0]; ++i) { + for (npy_intp j = 0; j < dims[1]; ++j) { + self->x.cd[i * dims[1] + j] *= self->unit_scaling[i]; + } + } + } + self->x.altlin |= has_cd; note_change(self); @@ -1862,6 +2619,7 @@ PyWcsprm_get_cdelt( /*@unused@*/ void* closure) { Py_ssize_t naxis = 0; + PyObject* result; if (is_null(self->x.cdelt)) { return NULL; @@ -1873,7 +2631,15 @@ PyWcsprm_get_cdelt( PyErr_WarnEx(NULL, "cdelt will be ignored since cd is present", 1); } - return get_double_array("cdelt", self->x.cdelt, 1, &naxis, (PyObject*)self); + if (self->unit_scaling != NULL) { + result = get_scaled_double_array_readonly("cdelt", self->x.cdelt, self->unit_scaling, 1, &naxis, (PyObject*)self); + } else { + result = get_double_array("cdelt", self->x.cdelt, 1, &naxis, (PyObject*)self); + } + + return result; + + } /*@null@*/ static int @@ -1883,6 +2649,7 @@ PyWcsprm_set_cdelt( /*@unused@*/ void* closure) { npy_intp dims; + int status; if (is_null(self->x.cdelt)) { return -1; @@ -1896,7 +2663,15 @@ PyWcsprm_set_cdelt( note_change(self); - return set_double_array("cdelt", value, 1, &dims, self->x.cdelt); + status = set_double_array("cdelt", value, 1, &dims, self->x.cdelt); + + if (status == 0 && self->original_cunit != NULL) { + for (npy_intp i = 0; i < dims; ++i) { + self->x.cdelt[i] *= self->unit_scaling[i]; + } + } + + return status; } static PyObject* @@ -1940,8 +2715,6 @@ PyWcsprm_set_cname( return -1; } - note_change(self); - return set_str_list("cname", value, (Py_ssize_t)self->x.naxis, 0, self->x.cname); } @@ -1975,8 +2748,6 @@ PyWcsprm_set_colax( naxis = (Py_ssize_t)self->x.naxis; - note_change(self); - return set_int_array("colax", value, 1, &naxis, self->x.colax); } @@ -1994,8 +2765,6 @@ PyWcsprm_set_colnum( PyObject* value, /*@unused@*/ void* closure) { - note_change(self); - return set_int("colnum", value, &self->x.colnum); } @@ -2029,8 +2798,6 @@ PyWcsprm_set_crder( naxis = (Py_ssize_t)self->x.naxis; - note_change(self); - return set_double_array("crder", value, 1, &naxis, self->x.crder); } @@ -2127,6 +2894,7 @@ PyWcsprm_get_crval( /*@unused@*/ void* closure) { Py_ssize_t naxis = 0; + PyObject* result; if (is_null(self->x.crval)) { return NULL; @@ -2134,7 +2902,14 @@ PyWcsprm_get_crval( naxis = (Py_ssize_t)self->x.naxis; - return get_double_array("crval", self->x.crval, 1, &naxis, (PyObject*)self); + if (self->unit_scaling != NULL) { + result = get_scaled_double_array_readonly("cdelt", self->x.crval, self->unit_scaling, 1, &naxis, (PyObject*)self); + } else { + result = get_double_array("crval", self->x.crval, 1, &naxis, (PyObject*)self); + } + + return result; + } static int @@ -2144,6 +2919,7 @@ PyWcsprm_set_crval( /*@unused@*/ void* closure) { npy_intp naxis; + int status; if (is_null(self->x.crval)) { return -1; @@ -2153,7 +2929,16 @@ PyWcsprm_set_crval( note_change(self); - return set_double_array("crval", value, 1, &naxis, self->x.crval); + status = set_double_array("crval", value, 1, &naxis, self->x.crval); + + if (status == 0 && self->original_cunit != NULL) { + for (npy_intp i = 0; i < naxis; ++i) { + self->x.crval[i] *= self->unit_scaling[i]; + } + } + + return status; + } /*@null@*/ static PyObject* @@ -2186,8 +2971,6 @@ PyWcsprm_set_csyer( naxis = (Py_ssize_t)self->x.naxis; - note_change(self); - return set_double_array("csyer", value, 1, &naxis, self->x.csyer); } @@ -2226,317 +3009,924 @@ PyWcsprm_get_cubeface( return get_int("cubeface", self->x.cubeface); } -static int -PyWcsprm_set_cubeface( +static int +PyWcsprm_set_cubeface( + PyWcsprm* self, + PyObject* value, + /*@unused@*/ void* closure) { + + note_change(self); + + return set_int("cubeface", value, &self->x.cubeface); +} + +/*@null@*/ static PyObject* +PyWcsprm_get_cunit( + PyWcsprm* self, + /*@unused@*/ void* closure) { + + if (is_null(self->x.cunit)) { + return NULL; + } + + if (self->original_cunit != NULL) { + return get_unit_list( + "cunit", self->original_cunit, (Py_ssize_t)self->x.naxis, (PyObject*)self, 1); + } else { + return get_unit_list( + "cunit", self->x.cunit, (Py_ssize_t)self->x.naxis, (PyObject*)self, 0); + } +} + +static int +PyWcsprm_set_cunit( + PyWcsprm* self, + PyObject* value, + /*@unused@*/ void* closure) { + + double scale; + + if (is_null(self->x.cunit)) { + return -1; + } + + note_change(self); + + // Note that if the user explicitly changes the units we should now preserve + // those new units. The way the WCS class works normally when units are not + // preserved is that changing units on an existing WCS is not just a unit + // conversion but truly overriding units. The simplest and safest way to achieve + // this here is to undo any changes that were made by the WCS class related to + // units (scaling cdelt, crval, and optionally cd), set the new units on the + // WCS class, and let it figure out if the new units warrant re-instating the + // scaling. + + if (self->unit_scaling != NULL) { + + for (int i = 0; i < self->x.naxis; ++i) { + + scale = self->unit_scaling[i]; + + if (scale != 1.) { + + self->x.cdelt[i] /= scale; + self->x.crval[i] /= scale; + + if (self->x.cd) { + for (int j = 0; j < self->x.naxis; j++) { + *(self->x.cd + i*self->x.naxis + j) /= scale; + } + } + + } + + } + + free(self->unit_scaling); + self->unit_scaling = NULL; + + } + + if (self->original_cunit != NULL) { + free(self->original_cunit); + self->original_cunit = NULL; + } + + return set_unit_list( + (PyObject *)self, "cunit", value, (Py_ssize_t)self->x.naxis, self->x.cunit); + +} + +/*@null@*/ static PyObject* +PyWcsprm_get_czphs( + PyWcsprm* self, + /*@unused@*/ void* closure) { + + Py_ssize_t naxis; + + if (is_null(self->x.czphs)) { + return NULL; + } + + naxis = (Py_ssize_t)self->x.naxis; + + return get_double_array("czphs", self->x.czphs, 1, &naxis, (PyObject*)self); +} + +static int +PyWcsprm_set_czphs( + PyWcsprm* self, + PyObject* value, + /*@unused@*/ void* closure) { + + npy_intp naxis; + + if (is_null(self->x.czphs)) { + return -1; + } + + naxis = (Py_ssize_t)self->x.naxis; + + return set_double_array("czphs", value, 1, &naxis, self->x.czphs); +} + +/*@null@*/ static PyObject* +PyWcsprm_get_cperi( + PyWcsprm* self, + /*@unused@*/ void* closure) { + + Py_ssize_t naxis; + + if (is_null(self->x.cperi)) { + return NULL; + } + + naxis = (Py_ssize_t)self->x.naxis; + + return get_double_array("cperi", self->x.cperi, 1, &naxis, (PyObject*)self); +} + +static int +PyWcsprm_set_cperi( + PyWcsprm* self, + PyObject* value, + /*@unused@*/ void* closure) { + + npy_intp naxis; + + if (is_null(self->x.cperi)) { + return -1; + } + + naxis = (Py_ssize_t)self->x.naxis; + + return set_double_array("cperi", value, 1, &naxis, self->x.cperi); +} + +/*@null@*/ static PyObject* +PyWcsprm_get_dateavg( + PyWcsprm* self, + /*@unused@*/ void* closure) { + + if (is_null(self->x.dateavg)) { + return NULL; + } + + return get_string("dateavg", self->x.dateavg); +} + +static int +PyWcsprm_set_dateavg( + PyWcsprm* self, + PyObject* value, + /*@unused@*/ void* closure) { + + if (is_null(self->x.dateavg)) { + return -1; + } + + /* TODO: Verify that this looks like a date string */ + + return set_string("dateavg", value, self->x.dateavg, 72); +} + +/*@null@*/ static PyObject* +PyWcsprm_get_datebeg( + PyWcsprm* self, + /*@unused@*/ void* closure) { + + if (is_null(self->x.datebeg)) { + return NULL; + } + + return get_string("datebeg", self->x.datebeg); +} + +static int +PyWcsprm_set_datebeg( + PyWcsprm* self, + PyObject* value, + /*@unused@*/ void* closure) { + + if (is_null(self->x.datebeg)) { + return -1; + } + + return set_string("datebeg", value, self->x.datebeg, 72); +} + +/*@null@*/ static PyObject* +PyWcsprm_get_dateend( + PyWcsprm* self, + /*@unused@*/ void* closure) { + + if (is_null(self->x.dateend)) { + return NULL; + } + + return get_string("dateend", self->x.dateend); +} + +static int +PyWcsprm_set_dateend( + PyWcsprm* self, + PyObject* value, + /*@unused@*/ void* closure) { + + if (is_null(self->x.dateend)) { + return -1; + } + + return set_string("dateend", value, self->x.dateend, 72); +} + + +/*@null@*/ static PyObject* +PyWcsprm_get_dateobs( + PyWcsprm* self, + /*@unused@*/ void* closure) { + + if (is_null(self->x.dateobs)) { + return NULL; + } + + return get_string("dateobs", self->x.dateobs); +} + +static int +PyWcsprm_set_dateobs( + PyWcsprm* self, + PyObject* value, + /*@unused@*/ void* closure) { + + if (is_null(self->x.dateobs)) { + return -1; + } + + return set_string("dateobs", value, self->x.dateobs, 72); +} + +/*@null@*/ static PyObject* +PyWcsprm_get_dateref( + PyWcsprm* self, + /*@unused@*/ void* closure) { + + if (is_null(self->x.dateref)) { + return NULL; + } + + return get_string("dateref", self->x.dateref); +} + +static int +PyWcsprm_set_dateref( + PyWcsprm* self, + PyObject* value, + /*@unused@*/ void* closure) { + + if (is_null(self->x.dateref)) { + return -1; + } + + return set_string("dateref", value, self->x.dateref, 72); +} + +static PyObject* +PyWcsprm_get_equinox( + PyWcsprm* self, + /*@unused@*/ void* closure) { + + return get_double("equinox", self->x.equinox); +} + +static int +PyWcsprm_set_equinox( + PyWcsprm* self, + PyObject* value, + /*@unused@*/ void* closure) { + + if (value == NULL) { /* deletion */ + self->x.equinox = (double)NPY_NAN; + return 0; + } + + return set_double("equinox", value, &self->x.equinox); +} + +/*@null@*/ static PyObject* +PyWcsprm_get_imgpix_matrix( + PyWcsprm* self, + /*@unused@*/ void* closure) { + + npy_intp dims[2]; + + if (is_null(self->x.lin.imgpix)) { + return NULL; + } + + if (PyWcsprm_cset(self, 1)) { + return NULL; + } + + dims[0] = self->x.naxis; + dims[1] = self->x.naxis; + + return get_double_array("imgpix_matrix", self->x.lin.imgpix, 2, dims, + (PyObject*)self); +} + +static PyObject* +PyWcsprm_get_jepoch( + PyWcsprm* self, + /*@unused@*/ void* closure) { + + return get_double("jepoch", self->x.jepoch); +} + +static int +PyWcsprm_set_jepoch( + PyWcsprm* self, + PyObject* value, + /*@unused@*/ void* closure) { + + note_change(self); + + if (value == NULL) { + self->x.jepoch = (double)NPY_NAN; + return 0; + } + + return set_double("jepoch", value, &self->x.jepoch); +} + + +static PyObject* +PyWcsprm_get_lat( + PyWcsprm* self, + /*@unused@*/ void* closure) { + + if (PyWcsprm_cset(self, 1)) { + return NULL; + } + + return get_int("lat", self->x.lat); +} + +static PyObject* +PyWcsprm_get_latpole( + PyWcsprm* self, + /*@unused@*/ void* closure) { + + return get_double("latpole", self->x.latpole); +} + +static int +PyWcsprm_set_latpole( + PyWcsprm* self, + PyObject* value, + /*@unused@*/ void* closure) { + + note_change(self); + + if (value == NULL) { + self->x.latpole = 90.0; + return 0; + } + + return set_double("latpole", value, &self->x.latpole); +} + +/*@null@*/ static PyObject* +PyWcsprm_get_lattyp( + PyWcsprm* self, + /*@unused@*/ void* closure) { + + if (is_null(self->x.lattyp)) { + return NULL; + } + + if (PyWcsprm_cset(self, 1)) { + return NULL; + } + + return get_string("lattyp", self->x.lattyp); +} + +static PyObject* +PyWcsprm_get_lng( + PyWcsprm* self, + /*@unused@*/ void* closure) { + + if (PyWcsprm_cset(self, 1)) { + return NULL; + } + + return get_int("lng", self->x.lng); +} + +/*@null@*/ static PyObject* +PyWcsprm_get_lngtyp( + PyWcsprm* self, + /*@unused@*/ void* closure) { + + if (is_null(self->x.lngtyp)) { + return NULL; + } + + if (PyWcsprm_cset(self, 1)) { + return NULL; + } + + return get_string("lngtyp", self->x.lngtyp); +} + +static PyObject* +PyWcsprm_get_lonpole( + PyWcsprm* self, + /*@unused@*/ void* closure) { + + return get_double("lonpole", self->x.lonpole); +} + +static int +PyWcsprm_set_lonpole( + PyWcsprm* self, + PyObject* value, + /*@unused@*/ void* closure) { + + note_change(self); + + if (value == NULL) { + self->x.lonpole = (double)NPY_NAN; + return 0; + } + + return set_double("lonpole", value, &self->x.lonpole); +} + +static PyObject* +PyWcsprm_get_mjdavg( + PyWcsprm* self, + /*@unused@*/ void* closure) { + + return get_double("mjdavg", self->x.mjdavg); +} + +static int +PyWcsprm_set_mjdavg( + PyWcsprm* self, + PyObject* value, + /*@unused@*/ void* closure) { + + if (value == NULL) { + self->x.mjdavg = (double)NPY_NAN; + return 0; + } + + return set_double("mjdavg", value, &self->x.mjdavg); +} + +static PyObject* +PyWcsprm_get_mjdbeg( + PyWcsprm* self, + /*@unused@*/ void* closure) { + + return get_double("mjdbeg", self->x.mjdbeg); +} + +static int +PyWcsprm_set_mjdbeg( + PyWcsprm* self, + PyObject* value, + /*@unused@*/ void* closure) { + + if (value == NULL) { + self->x.mjdbeg = (double)NPY_NAN; + return 0; + } + + return set_double("mjdbeg", value, &self->x.mjdbeg); +} + +static PyObject* +PyWcsprm_get_mjdend( + PyWcsprm* self, + /*@unused@*/ void* closure) { + + return get_double("mjdend", self->x.mjdend); +} + +static int +PyWcsprm_set_mjdend( + PyWcsprm* self, + PyObject* value, + /*@unused@*/ void* closure) { + + if (value == NULL) { + self->x.mjdend = (double)NPY_NAN; + return 0; + } + + return set_double("mjdend", value, &self->x.mjdend); +} + +static PyObject* +PyWcsprm_get_mjdobs( + PyWcsprm* self, + /*@unused@*/ void* closure) { + + return get_double("mjdobs", self->x.mjdobs); +} + +static int +PyWcsprm_set_mjdobs( + PyWcsprm* self, + PyObject* value, + /*@unused@*/ void* closure) { + + note_change(self); + + if (value == NULL) { + self->x.mjdobs = (double)NPY_NAN; + return 0; + } + + return set_double("mjdobs", value, &self->x.mjdobs); +} + +static PyObject* +PyWcsprm_get_mjdref( + PyWcsprm* self, + /*@unused@*/ void* closure) { + + npy_intp size = 2; + + return get_double_array("mjdref", self->x.mjdref, 1, &size, (PyObject*)self); +} + +static int +PyWcsprm_set_mjdref( + PyWcsprm* self, + PyObject* value, + /*@unused@*/ void* closure) { + + npy_intp size = 2; + + if (value == NULL) { + self->x.mjdref[0] = NPY_NAN; + self->x.mjdref[1] = NPY_NAN; + return 0; + } + return set_double_array("mjdref", value, 1, &size, self->x.mjdref); +} + + +/*@null@*/ static PyObject* +PyWcsprm_get_timesys( + PyWcsprm* self, + /*@unused@*/ void* closure) { + + if (is_null(self->x.timesys)) { + return NULL; + } + + return get_string("timesys", self->x.timesys); +} + +static int +PyWcsprm_set_timesys( + PyWcsprm* self, + PyObject* value, + /*@unused@*/ void* closure) { + + if (is_null(self->x.timesys)) { + return -1; + } + + return set_string("timesys", value, self->x.timesys, 72); +} + +/*@null@*/ static PyObject* +PyWcsprm_get_trefpos( PyWcsprm* self, - PyObject* value, /*@unused@*/ void* closure) { - note_change(self); + if (is_null(self->x.trefpos)) { + return NULL; + } - return set_int("cubeface", value, &self->x.cubeface); + return get_string("trefpos", self->x.trefpos); } static int -unit_verify(char* val) { - - int status, func; - double scale, units[WCSUNITS_NTYPE]; - struct wcserr *err = NULL; +PyWcsprm_set_trefpos( + PyWcsprm* self, + PyObject* value, + /*@unused@*/ void* closure) { - status = wcsulexe(val, &func, &scale, units, &err); - if (status == 0) { - return 1; - } else { - wcserr_units_to_python_exc(err); - free(err); - return 0; + if (is_null(self->x.trefpos)) { + return -1; } + + return set_string("trefpos", value, self->x.trefpos, 72); } /*@null@*/ static PyObject* -PyWcsprm_get_cunit( +PyWcsprm_get_trefdir( PyWcsprm* self, /*@unused@*/ void* closure) { - if (is_null(self->x.cunit)) { + if (is_null(self->x.trefdir)) { return NULL; } - return get_str_list_verified( - "cunit", self->x.cunit, (Py_ssize_t)self->x.naxis, 68, (PyObject*)self, - unit_verify); + return get_string("trefdir", self->x.trefdir); } static int -PyWcsprm_set_cunit( +PyWcsprm_set_trefdir( PyWcsprm* self, PyObject* value, /*@unused@*/ void* closure) { - if (is_null(self->x.cunit)) { + if (is_null(self->x.trefdir)) { return -1; } - note_change(self); - - return set_str_list_verified( - "cunit", value, (Py_ssize_t)self->x.naxis, 0, self->x.cunit, - unit_verify); + return set_string("trefdir", value, self->x.trefdir, 72); } /*@null@*/ static PyObject* -PyWcsprm_get_dateavg( +PyWcsprm_get_timeunit( PyWcsprm* self, /*@unused@*/ void* closure) { - if (is_null(self->x.dateavg)) { + if (is_null(self->x.timeunit)) { return NULL; } - return get_string("dateavg", self->x.dateavg); + return get_string("timeunit", self->x.timeunit); } static int -PyWcsprm_set_dateavg( +PyWcsprm_set_timeunit( PyWcsprm* self, PyObject* value, /*@unused@*/ void* closure) { - if (is_null(self->x.dateavg)) { + if (is_null(self->x.timeunit)) { return -1; } - note_change(self); - - /* TODO: Verify that this looks like a date string */ - - return set_string("dateavg", value, self->x.dateavg, 72); + return set_string("timeunit", value, self->x.timeunit, 72); } /*@null@*/ static PyObject* -PyWcsprm_get_dateobs( +PyWcsprm_get_plephem( PyWcsprm* self, /*@unused@*/ void* closure) { - if (is_null(self->x.dateobs)) { + if (is_null(self->x.plephem)) { return NULL; } - return get_string("dateobs", self->x.dateobs); + return get_string("plephem", self->x.plephem); } static int -PyWcsprm_set_dateobs( +PyWcsprm_set_plephem( PyWcsprm* self, PyObject* value, /*@unused@*/ void* closure) { - if (is_null(self->x.dateobs)) { + if (is_null(self->x.plephem)) { return -1; } - note_change(self); - - return set_string("dateobs", value, self->x.dateobs, 72); + return set_string("plephem", value, self->x.plephem, 72); } static PyObject* -PyWcsprm_get_equinox( +PyWcsprm_get_tstart( PyWcsprm* self, /*@unused@*/ void* closure) { - return get_double("equinox", self->x.equinox); + return get_double("tstart", self->x.tstart); } static int -PyWcsprm_set_equinox( +PyWcsprm_set_tstart( PyWcsprm* self, PyObject* value, /*@unused@*/ void* closure) { - if (value == NULL) { /* deletion */ - self->x.equinox = (double)NPY_NAN; + if (value == NULL) { + self->x.tstart = (double)NPY_NAN; return 0; } - note_change(self); - - return set_double("equinox", value, &self->x.equinox); + return set_double("tstart", value, &self->x.tstart); } -/*@null@*/ static PyObject* -PyWcsprm_get_imgpix_matrix( +static PyObject* +PyWcsprm_get_tstop( PyWcsprm* self, /*@unused@*/ void* closure) { - npy_intp dims[2]; + return get_double("tstop", self->x.tstop); +} - if (is_null(self->x.lin.imgpix)) { - return NULL; - } +static int +PyWcsprm_set_tstop( + PyWcsprm* self, + PyObject* value, + /*@unused@*/ void* closure) { - if (PyWcsprm_cset(self, 1)) { - return NULL; + if (value == NULL) { + self->x.tstop = (double)NPY_NAN; + return 0; } - dims[0] = self->x.naxis; - dims[1] = self->x.naxis; - - return get_double_array("imgpix_matrix", self->x.lin.imgpix, 2, dims, - (PyObject*)self); + return set_double("tstop", value, &self->x.tstop); } static PyObject* -PyWcsprm_get_lat( +PyWcsprm_get_telapse( PyWcsprm* self, /*@unused@*/ void* closure) { - if (PyWcsprm_cset(self, 1)) { - return NULL; + return get_double("telapse", self->x.telapse); +} + +static int +PyWcsprm_set_telapse( + PyWcsprm* self, + PyObject* value, + /*@unused@*/ void* closure) { + + if (value == NULL) { + self->x.telapse = (double)NPY_NAN; + return 0; } - return get_int("lat", self->x.lat); + return set_double("telapse", value, &self->x.telapse); } static PyObject* -PyWcsprm_get_latpole( +PyWcsprm_get_timeoffs( PyWcsprm* self, /*@unused@*/ void* closure) { - return get_double("latpole", self->x.latpole); + return get_double("timeoffs", self->x.timeoffs); } static int -PyWcsprm_set_latpole( +PyWcsprm_set_timeoffs( PyWcsprm* self, PyObject* value, /*@unused@*/ void* closure) { - note_change(self); - if (value == NULL) { - self->x.latpole = 90.0; + self->x.timeoffs = (double)NPY_NAN; return 0; } - return set_double("latpole", value, &self->x.latpole); + return set_double("timeoffs", value, &self->x.timeoffs); } -/*@null@*/ static PyObject* -PyWcsprm_get_lattyp( +static PyObject* +PyWcsprm_get_timsyer( PyWcsprm* self, /*@unused@*/ void* closure) { - if (is_null(self->x.lattyp)) { - return NULL; - } + return get_double("timsyer", self->x.timsyer); +} - if (PyWcsprm_cset(self, 1)) { - return NULL; +static int +PyWcsprm_set_timsyer( + PyWcsprm* self, + PyObject* value, + /*@unused@*/ void* closure) { + + if (value == NULL) { + self->x.timsyer = (double)NPY_NAN; + return 0; } - return get_string("lattyp", self->x.lattyp); + return set_double("timsyer", value, &self->x.timsyer); } static PyObject* -PyWcsprm_get_lng( +PyWcsprm_get_timrder( PyWcsprm* self, /*@unused@*/ void* closure) { - if (PyWcsprm_cset(self, 1)) { - return NULL; - } - - return get_int("lng", self->x.lng); + return get_double("timrder", self->x.timrder); } -/*@null@*/ static PyObject* -PyWcsprm_get_lngtyp( +static int +PyWcsprm_set_timrder( PyWcsprm* self, + PyObject* value, /*@unused@*/ void* closure) { - if (is_null(self->x.lngtyp)) { - return NULL; - } - - if (PyWcsprm_cset(self, 1)) { - return NULL; + if (value == NULL) { + self->x.timrder = (double)NPY_NAN; + return 0; } - return get_string("lngtyp", self->x.lngtyp); + return set_double("timrder", value, &self->x.timrder); } static PyObject* -PyWcsprm_get_lonpole( +PyWcsprm_get_timedel( PyWcsprm* self, /*@unused@*/ void* closure) { - return get_double("lonpole", self->x.lonpole); + return get_double("timedel", self->x.timedel); } static int -PyWcsprm_set_lonpole( +PyWcsprm_set_timedel( PyWcsprm* self, PyObject* value, /*@unused@*/ void* closure) { - note_change(self); - if (value == NULL) { - self->x.lonpole = (double)NPY_NAN; + self->x.timedel = (double)NPY_NAN; return 0; } - return set_double("lonpole", value, &self->x.lonpole); + return set_double("timedel", value, &self->x.timedel); } static PyObject* -PyWcsprm_get_mjdavg( +PyWcsprm_get_timepixr( PyWcsprm* self, /*@unused@*/ void* closure) { - return get_double("mjdavg", self->x.mjdavg); + return get_double("timepixr", self->x.timepixr); } static int -PyWcsprm_set_mjdavg( +PyWcsprm_set_timepixr( PyWcsprm* self, PyObject* value, /*@unused@*/ void* closure) { - note_change(self); - if (value == NULL) { - self->x.mjdavg = (double)NPY_NAN; + self->x.timepixr = (double)NPY_NAN; return 0; } - return set_double("mjdavg", value, &self->x.mjdavg); + return set_double("timepixr", value, &self->x.timepixr); } -static PyObject* -PyWcsprm_get_mjdobs( +/*@null@*/ static PyObject* +PyWcsprm_get_obsorbit( PyWcsprm* self, /*@unused@*/ void* closure) { - return get_double("mjdobs", self->x.mjdobs); + if (is_null(self->x.obsorbit)) { + return NULL; + } + + return get_string("obsorbit", self->x.obsorbit); } static int -PyWcsprm_set_mjdobs( +PyWcsprm_set_obsorbit( PyWcsprm* self, PyObject* value, /*@unused@*/ void* closure) { - note_change(self); + if (is_null(self->x.obsorbit)) { + return -1; + } + + return set_string("obsorbit", value, self->x.obsorbit, 72); +} + +static PyObject* +PyWcsprm_get_xposure( + PyWcsprm* self, + /*@unused@*/ void* closure) { + + return get_double("xposure", self->x.xposure); +} + +static int +PyWcsprm_set_xposure( + PyWcsprm* self, + PyObject* value, + /*@unused@*/ void* closure) { if (value == NULL) { - self->x.mjdobs = (double)NPY_NAN; + self->x.xposure = (double)NPY_NAN; return 0; } - return set_double("mjdobs", value, &self->x.mjdobs); + return set_double("xposure", value, &self->x.xposure); } /*@null@*/ static PyObject* @@ -2561,8 +3951,6 @@ PyWcsprm_set_name( return -1; } - note_change(self); - return set_string("name", value, self->x.wcsname, 72); } @@ -2579,7 +3967,7 @@ PyWcsprm_get_obsgeo( PyWcsprm* self, /*@unused@*/ void* closure) { - Py_ssize_t size = 3; + Py_ssize_t size = 6; if (is_null(self->x.obsgeo)) { return NULL; @@ -2594,18 +3982,19 @@ PyWcsprm_set_obsgeo( PyObject* value, /*@unused@*/ void* closure) { - npy_intp size = 3; + npy_intp size = 6; if (is_null(self->x.obsgeo)) { return -1; } - note_change(self); - if (value == NULL) { self->x.obsgeo[0] = NPY_NAN; self->x.obsgeo[1] = NPY_NAN; self->x.obsgeo[2] = NPY_NAN; + self->x.obsgeo[3] = NPY_NAN; + self->x.obsgeo[4] = NPY_NAN; + self->x.obsgeo[5] = NPY_NAN; return 0; } @@ -2755,8 +4144,6 @@ PyWcsprm_set_radesys( return -1; } - note_change(self); - return set_string("radesys", value, self->x.radesys, 72); } @@ -2838,8 +4225,6 @@ PyWcsprm_set_specsys( return -1; } - note_change(self); - return set_string("specsys", value, self->x.specsys, 72); } @@ -2892,8 +4277,6 @@ PyWcsprm_set_ssyssrc( return -1; } - note_change(self); - return set_string("ssyssrc", value, self->x.ssyssrc, 72); } @@ -2921,7 +4304,6 @@ PyWcsprm_get_tab( } if (PyList_SetItem(result, i, subresult) == -1) { - Py_DECREF(subresult); Py_DECREF(result); return NULL; } @@ -2973,8 +4355,6 @@ PyWcsprm_set_velangl( return 0; } - note_change(self); - return set_double("velangl", value, &self->x.velangl); } @@ -2997,43 +4377,55 @@ PyWcsprm_set_velosys( return 0; } - note_change(self); - return set_double("velosys", value, &self->x.velosys); } -/* static PyObject* */ -/* PyWcsprm_get_wtb( */ -/* PyWcsprm* self, */ -/* /\*@unused@*\/ void* closure) { */ +static PyObject* +PyWcsprm_get_velref( + PyWcsprm* self, + /*@unused@*/ void* closure) { + + return get_int("velref", self->x.velref); +} + +static int +PyWcsprm_set_velref( + PyWcsprm* self, + PyObject* value, + /*@unused@*/ void* closure) { + + if (value == NULL) { /* deletion */ + self->x.velref = 0; + return 0; + } + + return set_int("velref", value, &self->x.velref); +} + + +static PyObject* PyWcsprm_get_wtb(PyWcsprm* self, void* closure) { + PyObject* list; + PyObject* elem; + int i, nwtb; -/* PyObject* result; */ -/* PyObject* subresult; */ -/* int i, nwtb; */ + nwtb = self->x.nwtb; -/* nwtb = self->x.nwtb; */ + list = PyList_New(nwtb); + if (list == NULL) return NULL; -/* result = PyList_New(nwtb); */ -/* if (result == NULL) { */ -/* return NULL; */ -/* } */ + for (i = 0; i < nwtb; ++i) { + elem = (PyObject *)PyWtbarr_cnew((PyObject *)self, &(self->x.wtb[i])); + if (elem == NULL) { + Py_DECREF(list); + return NULL; + } -/* for (i = 0; i < nwtb; ++i) { */ -/* subresult = (PyObject *)PyWtbarr_cnew((PyObject *)self, &(self->x.wtb[i])); */ -/* if (subresult == NULL) { */ -/* Py_DECREF(result); */ -/* return NULL; */ -/* } */ + PyList_SetItem(list, i, elem); + } -/* if (PyList_SetItem(result, i, subresult) == -1) { */ -/* Py_DECREF(subresult); */ -/* Py_DECREF(result); */ -/* return NULL; */ -/* } */ -/* } */ + return list; +} -/* return result; */ -/* } */ static PyObject* PyWcsprm_get_zsource( @@ -3054,18 +4446,66 @@ PyWcsprm_set_zsource( return 0; } - note_change(self); - return set_double("zsource", value, &self->x.zsource); } + + /*@null@*/ static PyObject* +PyWcsprm_get_unit_scaling( + PyWcsprm* self, + /*@unused@*/ void* closure) { + + Py_ssize_t naxis = 0; + PyObject* result; + + if (self->unit_scaling == NULL) { + return Py_None; + } + + naxis = self->x.naxis; + + return get_double_array("unit_scaling", self->unit_scaling, 1, &naxis, (PyObject*)self); + +} + +static PyObject* +PyWcsprm_get_aux( + PyWcsprm* self, + /*@unused@*/ void* closure) { + + PyObject* result; + + // If wcsprm.aux is not initialized, we should do so here so that users can + // set auxiliary parameters on an empty WCS. + + if (self->x.aux == 0x0) { + wcsauxi(1, &self->x); + } + + result = (PyObject *)PyAuxprm_cnew((PyObject *)self, self->x.aux); + + return result; +} + + +static PyObject* +PyWcsprm_get_cel( + PyWcsprm* self, + /*@unused@*/ void* closure) { + + return (PyObject *)PyCelprm_cnew((PyObject *)self, &(self->x.cel), NULL); +} + /*************************************************************************** * PyWcsprm definition structures */ static PyGetSetDef PyWcsprm_getset[] = { {"alt", (getter)PyWcsprm_get_alt, (setter)PyWcsprm_set_alt, (char *)doc_alt}, + {"aux", (getter)PyWcsprm_get_aux, NULL, (char *)doc_aux}, + {"cel", (getter)PyWcsprm_get_cel, NULL, (char *)doc_cel}, {"axis_types", (getter)PyWcsprm_get_axis_types, NULL, (char *)doc_axis_types}, + {"bepoch", (getter)PyWcsprm_get_bepoch, (setter)PyWcsprm_set_bepoch, (char *)doc_bepoch}, {"cd", (getter)PyWcsprm_get_cd, (setter)PyWcsprm_set_cd, (char *)doc_cd}, {"cdelt", (getter)PyWcsprm_get_cdelt, (setter)PyWcsprm_set_cdelt, (char *)doc_cdelt}, {"cel_offset", (getter)PyWcsprm_get_cel_offset, (setter)PyWcsprm_set_cel_offset, (char *)doc_cel_offset}, @@ -3080,10 +4520,16 @@ static PyGetSetDef PyWcsprm_getset[] = { {"ctype", (getter)PyWcsprm_get_ctype, (setter)PyWcsprm_set_ctype, (char *)doc_ctype}, {"cubeface", (getter)PyWcsprm_get_cubeface, (setter)PyWcsprm_set_cubeface, (char *)doc_cubeface}, {"cunit", (getter)PyWcsprm_get_cunit, (setter)PyWcsprm_set_cunit, (char *)doc_cunit}, + {"czphs", (getter)PyWcsprm_get_czphs, (setter)PyWcsprm_set_czphs, (char *)doc_czphs}, + {"cperi", (getter)PyWcsprm_get_cperi, (setter)PyWcsprm_set_cperi, (char *)doc_cperi}, {"dateavg", (getter)PyWcsprm_get_dateavg, (setter)PyWcsprm_set_dateavg, (char *)doc_dateavg}, + {"datebeg", (getter)PyWcsprm_get_datebeg, (setter)PyWcsprm_set_datebeg, (char *)doc_datebeg}, + {"dateend", (getter)PyWcsprm_get_dateend, (setter)PyWcsprm_set_dateend, (char *)doc_dateend}, {"dateobs", (getter)PyWcsprm_get_dateobs, (setter)PyWcsprm_set_dateobs, (char *)doc_dateobs}, + {"dateref", (getter)PyWcsprm_get_dateref, (setter)PyWcsprm_set_dateref, (char *)doc_dateref}, {"equinox", (getter)PyWcsprm_get_equinox, (setter)PyWcsprm_set_equinox, (char *)doc_equinox}, {"imgpix_matrix", (getter)PyWcsprm_get_imgpix_matrix, NULL, (char *)doc_imgpix_matrix}, + {"jepoch", (getter)PyWcsprm_get_jepoch, (setter)PyWcsprm_set_jepoch, (char *)doc_jepoch}, {"lat", (getter)PyWcsprm_get_lat, NULL, (char *)doc_lat}, {"latpole", (getter)PyWcsprm_get_latpole, (setter)PyWcsprm_set_latpole, (char *)doc_latpole}, {"lattyp", (getter)PyWcsprm_get_lattyp, NULL, (char *)doc_lattyp}, @@ -3091,13 +4537,18 @@ static PyGetSetDef PyWcsprm_getset[] = { {"lngtyp", (getter)PyWcsprm_get_lngtyp, NULL, (char *)doc_lngtyp}, {"lonpole", (getter)PyWcsprm_get_lonpole, (setter)PyWcsprm_set_lonpole, (char *)doc_lonpole}, {"mjdavg", (getter)PyWcsprm_get_mjdavg, (setter)PyWcsprm_set_mjdavg, (char *)doc_mjdavg}, + {"mjdbeg", (getter)PyWcsprm_get_mjdbeg, (setter)PyWcsprm_set_mjdbeg, (char *)doc_mjdbeg}, + {"mjdend", (getter)PyWcsprm_get_mjdend, (setter)PyWcsprm_set_mjdend, (char *)doc_mjdend}, {"mjdobs", (getter)PyWcsprm_get_mjdobs, (setter)PyWcsprm_set_mjdobs, (char *)doc_mjdobs}, + {"mjdref", (getter)PyWcsprm_get_mjdref, (setter)PyWcsprm_set_mjdref, (char *)doc_mjdref}, {"name", (getter)PyWcsprm_get_name, (setter)PyWcsprm_set_name, (char *)doc_name}, {"naxis", (getter)PyWcsprm_get_naxis, NULL, (char *)doc_naxis}, {"obsgeo", (getter)PyWcsprm_get_obsgeo, (setter)PyWcsprm_set_obsgeo, (char *)doc_obsgeo}, + {"obsorbit", (getter)PyWcsprm_get_obsorbit, (setter)PyWcsprm_set_obsorbit, (char *)doc_obsorbit}, {"pc", (getter)PyWcsprm_get_pc, (setter)PyWcsprm_set_pc, (char *)doc_pc}, {"phi0", (getter)PyWcsprm_get_phi0, (setter)PyWcsprm_set_phi0, (char *)doc_phi0}, {"piximg_matrix", (getter)PyWcsprm_get_piximg_matrix, NULL, (char *)doc_piximg_matrix}, + {"plephem", (getter)PyWcsprm_get_plephem, (setter)PyWcsprm_set_plephem, (char *) doc_plephem}, {"radesys", (getter)PyWcsprm_get_radesys, (setter)PyWcsprm_set_radesys, (char *)doc_radesys}, {"restfrq", (getter)PyWcsprm_get_restfrq, (setter)PyWcsprm_set_restfrq, (char *)doc_restfrq}, {"restwav", (getter)PyWcsprm_get_restwav, (setter)PyWcsprm_set_restwav, (char *)doc_restwav}, @@ -3107,16 +4558,33 @@ static PyGetSetDef PyWcsprm_getset[] = { {"ssyssrc", (getter)PyWcsprm_get_ssyssrc, (setter)PyWcsprm_set_ssyssrc, (char *)doc_ssyssrc}, {"tab", (getter)PyWcsprm_get_tab, NULL, (char *)doc_tab}, {"theta0", (getter)PyWcsprm_get_theta0, (setter)PyWcsprm_set_theta0, (char *)doc_theta0}, + {"timesys", (getter)PyWcsprm_get_timesys, (setter)PyWcsprm_set_timesys, (char *) doc_timesys}, + {"trefpos", (getter)PyWcsprm_get_trefpos, (setter)PyWcsprm_set_trefpos, (char *) doc_trefpos}, + {"trefdir", (getter)PyWcsprm_get_trefdir, (setter)PyWcsprm_set_trefdir, (char *) doc_trefdir}, + {"tstart", (getter)PyWcsprm_get_tstart, (setter)PyWcsprm_set_tstart, (char *) doc_tstart}, + {"tstop", (getter)PyWcsprm_get_tstop, (setter)PyWcsprm_set_tstop, (char *) doc_tstop}, + {"telapse", (getter)PyWcsprm_get_telapse, (setter)PyWcsprm_set_telapse, (char *) doc_telapse}, + {"timeoffs", (getter)PyWcsprm_get_timeoffs, (setter)PyWcsprm_set_timeoffs, (char *) doc_timeoffs}, + {"timsyer", (getter)PyWcsprm_get_timsyer, (setter)PyWcsprm_set_timsyer, (char *) doc_timsyer}, + {"timrder", (getter)PyWcsprm_get_timrder, (setter)PyWcsprm_set_timrder, (char *) doc_timrder}, + {"timedel", (getter)PyWcsprm_get_timedel, (setter)PyWcsprm_set_timedel, (char *) doc_timedel}, + {"timepixr", (getter)PyWcsprm_get_timepixr, (setter)PyWcsprm_set_timepixr, (char *) doc_timepixr}, + {"timeunit", (getter)PyWcsprm_get_timeunit, (setter)PyWcsprm_set_timeunit, (char *) doc_timeunit}, {"velangl", (getter)PyWcsprm_get_velangl, (setter)PyWcsprm_set_velangl, (char *)doc_velangl}, {"velosys", (getter)PyWcsprm_get_velosys, (setter)PyWcsprm_set_velosys, (char *)doc_velosys}, - /* {"wtb", (getter)PyWcsprm_get_wtb, NULL, (char *)doc_tab}, */ + {"velref", (getter)PyWcsprm_get_velref, (setter)PyWcsprm_set_velref, (char *)doc_velref}, + {"xposure", (getter)PyWcsprm_get_xposure, (setter)PyWcsprm_set_xposure, (char *)doc_xposure}, + {"wtb", (getter)PyWcsprm_get_wtb, NULL, (char *) doc_wtb}, {"zsource", (getter)PyWcsprm_get_zsource, (setter)PyWcsprm_set_zsource, (char *)doc_zsource}, + {"_unit_scaling", (getter)PyWcsprm_get_unit_scaling, NULL, NULL}, // For debugging {NULL} }; static PyMethodDef PyWcsprm_methods[] = { + {"bounds_check", (PyCFunction)PyWcsprm_bounds_check, METH_VARARGS|METH_KEYWORDS, doc_bounds_check}, {"cdfix", (PyCFunction)PyWcsprm_cdfix, METH_NOARGS, doc_cdfix}, {"celfix", (PyCFunction)PyWcsprm_celfix, METH_NOARGS, doc_celfix}, + {"compare", (PyCFunction)PyWcsprm_compare, METH_VARARGS|METH_KEYWORDS, doc_compare}, {"__copy__", (PyCFunction)PyWcsprm_copy, METH_NOARGS, doc_copy}, {"cylfix", (PyCFunction)PyWcsprm_cylfix, METH_VARARGS|METH_KEYWORDS, doc_cylfix}, {"datfix", (PyCFunction)PyWcsprm_datfix, METH_NOARGS, doc_datfix}, @@ -3148,86 +4616,107 @@ static PyMethodDef PyWcsprm_methods[] = { {NULL} }; -PyTypeObject PyWcsprmType = { - #if PY3K - PyVarObject_HEAD_INIT(NULL, 0) - #else - PyObject_HEAD_INIT(NULL) - 0, /*ob_size*/ - #endif - "astropy.wcs._Wcsprm", /*tp_name*/ - sizeof(PyWcsprm), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - (destructor)PyWcsprm_dealloc, /*tp_dealloc*/ - 0, /*tp_print*/ - 0, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_compare*/ - (reprfunc)PyWcsprm___str__, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - 0, /*tp_hash */ - 0, /*tp_call*/ - (reprfunc)PyWcsprm___str__, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ - doc_Wcsprm, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - PyWcsprm_methods, /* tp_methods */ - 0, /* tp_members */ - PyWcsprm_getset, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - (initproc)PyWcsprm_init, /* tp_init */ - 0, /* tp_alloc */ - PyWcsprm_new, /* tp_new */ +static PyType_Spec PyWcsprm_spec = { + .name = "astropy.wcs.Wcsprm", + .basicsize = sizeof(PyWcsprm), + .itemsize = 0, + .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_IMMUTABLETYPE, + .slots = (PyType_Slot[]) { + {Py_tp_dealloc, (destructor)PyWcsprm_dealloc}, + {Py_tp_repr, (reprfunc)PyWcsprm___str__}, + {Py_tp_str, (reprfunc)PyWcsprm___str__}, + {Py_tp_doc, doc_Wcsprm}, + {Py_tp_richcompare, PyWcsprm_richcompare}, + {Py_tp_methods, PyWcsprm_methods}, + {Py_tp_getset, PyWcsprm_getset}, + {Py_tp_init, (initproc)PyWcsprm_init}, + {Py_tp_new, PyWcsprm_new}, + {0, NULL}, + }, }; +PyObject* PyWcsprmType = NULL; + #define CONSTANT(a) PyModule_AddIntConstant(m, #a, a) +#define CONSTANT2(n, v) PyModule_AddIntConstant(m, n, v) + +#define XSTRINGIFY(s) STRINGIFY(s) +#define STRINGIFY(s) #s + +int add_prj_codes(PyObject* module) +{ + int k; + PyObject* code; + PyObject* list = PyList_New(prj_ncode); + if (list == NULL) { + return -1; + } + + for (k = 0; k < prj_ncode; k++) { + code = PyUnicode_FromString(prj_codes[k]); + if (PyList_SetItem(list, k, code)) { + Py_DECREF(list); + return -1; + } + } + + if (PyModule_AddObject(module, "PRJ_CODES", list)) { + Py_DECREF(list); + return -1; + } + return 0; +} int _setup_wcsprm_type( PyObject* m) { + PyWcsprmType = PyType_FromSpec(&PyWcsprm_spec); - if (PyType_Ready(&PyWcsprmType) < 0) { + if (PyWcsprmType == NULL) { return -1; } - Py_INCREF(&PyWcsprmType); - wcsprintf_set(NULL); wcserr_enable(1); return ( - PyModule_AddObject(m, "_Wcsprm", (PyObject *)&PyWcsprmType) || + PyModule_AddObject(m, "Wcsprm", PyWcsprmType) || CONSTANT(WCSSUB_LONGITUDE) || CONSTANT(WCSSUB_LATITUDE) || CONSTANT(WCSSUB_CUBEFACE) || CONSTANT(WCSSUB_SPECTRAL) || CONSTANT(WCSSUB_STOKES) || +#if defined(WCSSUB_TIME) + CONSTANT(WCSSUB_TIME) || +#endif CONSTANT(WCSSUB_CELESTIAL) || CONSTANT(WCSHDR_IMGHEAD) || CONSTANT(WCSHDR_BIMGARR) || CONSTANT(WCSHDR_PIXLIST) || CONSTANT(WCSHDR_none) || CONSTANT(WCSHDR_all) || + CONSTANT(WCSHDR_reject) || +#ifdef WCSHDR_strict + CONSTANT(WCSHDR_strict) || +#endif CONSTANT(WCSHDR_CROTAia) || CONSTANT(WCSHDR_EPOCHa) || CONSTANT(WCSHDR_VELREFa) || CONSTANT(WCSHDR_CD00i00j) || CONSTANT(WCSHDR_PC00i00j) || CONSTANT(WCSHDR_PROJPn) || +#ifdef WCSHDR_CD0i_0ja + CONSTANT(WCSHDR_CD0i_0ja) || +#endif +#ifdef WCSHDR_PC0i_0ja + CONSTANT(WCSHDR_PC0i_0ja) || +#endif +#ifdef WCSHDR_PV0i_0ma + CONSTANT(WCSHDR_PV0i_0ma) || +#endif +#ifdef WCSHDR_PS0i_0ma + CONSTANT(WCSHDR_PS0i_0ma) || +#endif CONSTANT(WCSHDR_RADECSYS) || CONSTANT(WCSHDR_VSOURCE) || CONSTANT(WCSHDR_DOBSn) || @@ -3243,5 +4732,25 @@ _setup_wcsprm_type( CONSTANT(WCSHDO_PVn_ma) || CONSTANT(WCSHDO_CRPXna) || CONSTANT(WCSHDO_CNAMna) || - CONSTANT(WCSHDO_WCSNna)); + CONSTANT(WCSHDO_WCSNna) || + CONSTANT(WCSHDO_P12) || + CONSTANT(WCSHDO_P13) || + CONSTANT(WCSHDO_P14) || + CONSTANT(WCSHDO_P15) || + CONSTANT(WCSHDO_P16) || + CONSTANT(WCSHDO_P17) || + CONSTANT(WCSHDO_EFMT) || + CONSTANT(WCSCOMPARE_ANCILLARY) || + CONSTANT(WCSCOMPARE_TILING) || + CONSTANT(WCSCOMPARE_CRPIX) || + CONSTANT2("PRJ_PVN", PVN) || + add_prj_codes(m) || + CONSTANT2("PRJ_ZENITHAL", ZENITHAL) || + CONSTANT2("PRJ_CYLINDRICAL", CYLINDRICAL) || + CONSTANT2("PRJ_PSEUDOCYLINDRICAL", PSEUDOCYLINDRICAL) || + CONSTANT2("PRJ_CONVENTIONAL", CONVENTIONAL) || + CONSTANT2("PRJ_CONIC", CONIC) || + CONSTANT2("PRJ_POLYCONIC", POLYCONIC) || + CONSTANT2("PRJ_QUADCUBE", QUADCUBE) || + CONSTANT2("PRJ_HEALPIX", HEALPIX)); } diff --git a/astropy/wcs/src/wcslib_wtbarr_wrap.c b/astropy/wcs/src/wcslib_wtbarr_wrap.c index 8a2afdf816fb..23c570df3f49 100644 --- a/astropy/wcs/src/wcslib_wtbarr_wrap.c +++ b/astropy/wcs/src/wcslib_wtbarr_wrap.c @@ -1,13 +1,11 @@ -/* - Author: Michael Droettboom - mdroe@stsci.edu -*/ - #define NO_IMPORT_ARRAY -#include "wcslib_wtbarr_wrap.h" +#include "astropy_wcs/wcslib_wtbarr_wrap.h" #include +#include +#include +#include /* It gets to be really tedious to type long docstrings in ANSI C syntax @@ -15,280 +13,213 @@ docstrings are written in doc/docstrings.py, which are then converted by setup.py into docstrings.h, which we include here. */ -#include "docstrings.h" +#include "astropy_wcs/docstrings.h" -/*************************************************************************** - * Helper functions * - ***************************************************************************/ /*************************************************************************** - * PyWtbarr methods - */ + * PyWtbarr methods * + ***************************************************************************/ -static int -PyWtbarr_traverse( - PyWtbarr* self, visitproc visit, void *arg) { - int vret; +static PyObject* +PyWtbarr_new(PyTypeObject* type, PyObject* args, PyObject* kwds) { + PyWtbarr* self; + allocfunc alloc_func = PyType_GetSlot(type, Py_tp_alloc); + self = (PyWtbarr*)alloc_func(type, 0); + return (PyObject*)self; +} - vret = visit(self->owner, arg); - if (vret != 0) { - return vret; - } +static int +PyWtbarr_traverse(PyWtbarr* self, visitproc visit, void *arg) { + Py_VISIT(self->owner); + Py_VISIT(Py_TYPE((PyObject*)self)); return 0; } -static int -PyWtbarr_clear( - PyWtbarr* self) { - PyObject* tmp; - - tmp = self->owner; - self->owner = NULL; - Py_XDECREF(tmp); +static int +PyWtbarr_clear(PyWtbarr* self) { + Py_CLEAR(self->owner); return 0; } -static void -PyWtbarr_dealloc( - PyWtbarr* self) { +static void PyWtbarr_dealloc(PyWtbarr* self) { PyWtbarr_clear(self); - Py_TYPE(self)->tp_free((PyObject*)self); + PyTypeObject *tp = Py_TYPE((PyObject*)self); + freefunc free_func = PyType_GetSlot(tp, Py_tp_free); + free_func((PyObject*)self); + Py_DECREF(tp); } -PyWtbarr* -PyWtbarr_cnew(PyObject* wcsprm, struct wtbarr* x) { + +PyWtbarr* PyWtbarr_cnew(PyObject* wcsprm, struct wtbarr* x) { PyWtbarr* self; - self = (PyWtbarr*)(&PyWtbarrType)->tp_alloc(&PyWtbarrType, 0); + PyTypeObject* type = (PyTypeObject*)PyWtbarrType; + allocfunc alloc_func = PyType_GetSlot(type, Py_tp_alloc); + self = (PyWtbarr*)alloc_func(type, 0); + if (self == NULL) return NULL; self->x = x; Py_INCREF(wcsprm); self->owner = wcsprm; return self; } -/*************************************************************************** - * Member getters/setters (properties) - */ -/*@null@*/ static PyObject* -PyWtbarr_get_data( - PyWtbarr* self, - /*@unused@*/ void* closure) { +static void wtbarrprt(const struct wtbarr *wtb) { + int i, nd, ndim; - int ndims; - npy_intp dims[NPY_MAXDIMS]; - int i; + if (wtb == 0x0) return; - if (is_null(self->x->arrayp)) { - return NULL; - } + wcsprintf(" i: %d\n", wtb->i); + wcsprintf(" m: %d\n", wtb->m); + wcsprintf(" kind: %c\n", wtb->kind); + wcsprintf("extnam: %s\n", wtb->extnam); + wcsprintf("extver: %d\n", wtb->extver); + wcsprintf("extlev: %d\n", wtb->extlev); + wcsprintf(" ttype: %s\n", wtb->ttype); + wcsprintf(" row: %ld\n", wtb->row); + wcsprintf(" ndim: %d\n", wtb->ndim); + wcsprintf("dimlen: %p\n", (void *)wtb->dimlen); - ndims = self->x->ndim; - for (i = 0; i < ndims; ++i) { - dims[i] = self->x->dimlen[i]; + ndim = wtb->ndim - (int)(wtb->kind == 'c'); + nd = 1 + (int) log10(ndim ? ndim : 1); + for (i = 0; i < ndim; i++) { + wcsprintf(" %*d: %d\n", nd, i, wtb->dimlen[i]); } + wcsprintf("arrayp: %p\n", (void *)wtb->arrayp); - return get_double_array("data", *self->x->arrayp, ndims, dims, (PyObject*)self); + return; } -/*@null@*/ static int -PyWtbarr_set_data( - PyWtbarr* self, - PyObject* value, - /*@unused@*/ void* closure) { - - int ndims; - npy_intp dims[NPY_MAXDIMS]; - int i; - if (is_null(self->x->arrayp)) { - return -1; - } - - ndims = self->x->ndim; - for (i = 0; i < ndims; ++i) { - dims[i] = self->x->dimlen[i]; - } - - return set_double_array("data", value, ndims, dims, *self->x->arrayp); +static PyObject* PyWtbarr_print_contents(PyWtbarr* self) { + /* This is not thread-safe, but since we're holding onto the GIL, + we can assume we won't have thread conflicts */ + wcsprintf_set(NULL); + wtbarrprt(self->x); + printf("%s", wcsprintf_buf()); + fflush(stdout); + Py_RETURN_NONE; } -/*@null@*/ static PyObject* -PyWtbarr_get_dims( - PyWtbarr* self, - /*@unused@*/ void* closure) { - - Py_ssize_t ndims = 0; - if (is_null(self->x->dimlen)) { - return NULL; - } +static PyObject* PyWtbarr___str__(PyWtbarr* self) { + /* This is not thread-safe, but since we're holding onto the GIL, + we can assume we won't have thread conflicts */ + wcsprintf_set(NULL); + wtbarrprt(self->x); + return PyUnicode_FromString(wcsprintf_buf()); +} - ndims = (Py_ssize_t)self->x->ndim; - return get_int_array("dims", self->x->dimlen, 1, &ndims, (PyObject*)self); -} +/*************************************************************************** + * Member getters/setters (properties) + */ -/*@null@*/ static PyObject* -PyWtbarr_get_extlev( - PyWtbarr* self, - /*@unused@*/ void* closure) { - return get_int("extlev", self->x->extlev); +static PyObject* PyWtbarr_get_i(PyWtbarr* self, void* closure) { + return get_int("i", self->x->i); } -/*@null@*/ static PyObject* -PyWtbarr_get_extnam( - PyWtbarr* self, - /*@unused@*/ void* closure) { - return get_string("extnam", self->x->extnam); +static PyObject* PyWtbarr_get_m(PyWtbarr* self, void* closure) { + return get_int("m", self->x->m); } -/*@null@*/ static PyObject* -PyWtbarr_get_extver( - PyWtbarr* self, - /*@unused@*/ void* closure) { +static PyObject* PyWtbarr_get_extver(PyWtbarr* self, void* closure) { return get_int("extver", self->x->extver); } -/*@null@*/ static PyObject* -PyWtbarr_get_i( - PyWtbarr* self, - /*@unused@*/ void* closure) { - return get_int("i", self->x->i); +static PyObject* PyWtbarr_get_extlev(PyWtbarr* self, void* closure) { + return get_int("extlev", self->x->extlev); } -/*@null@*/ static PyObject* -PyWtbarr_get_kind( - PyWtbarr* self, - /*@unused@*/ void* closure) { - - char kind = (char)self->x->kind; - #if PY3K - return PyUnicode_FromStringAndSize(&kind, 1); - #else - return PyString_FromStringAndSize(&kind, 1); - #endif +static PyObject* PyWtbarr_get_ndim(PyWtbarr* self, void* closure) { + return get_int("ndim", self->x->ndim); } -/*@null@*/ static PyObject* -PyWtbarr_get_m( - PyWtbarr* self, - /*@unused@*/ void* closure) { - return get_int("m", self->x->m); +static PyObject* PyWtbarr_get_row(PyWtbarr* self, void* closure) { + return get_int("row", self->x->row); } -/*@null@*/ static PyObject* -PyWtbarr_get_ndim( - PyWtbarr* self, - /*@unused@*/ void* closure) { - return get_int("ndim", self->x->ndim); +static PyObject* PyWtbarr_get_extnam(PyWtbarr* self, void* closure) { + if (is_null(self->x->extnam)) return NULL; + return get_string("extnam", self->x->extnam); } -/*@null@*/ static PyObject* -PyWtbarr_get_row( - PyWtbarr* self, - /*@unused@*/ void* closure) { - return get_int("row", self->x->row); +static PyObject* PyWtbarr_get_ttype(PyWtbarr* self, void* closure) { + if (is_null(self->x->ttype)) return NULL; + return get_string("ttype", self->x->ttype); } -/*@null@*/ static PyObject* -PyWtbarr_get_ttype( - PyWtbarr* self, - /*@unused@*/ void* closure) { - return get_string("ttype", self->x->ttype); +static PyObject* PyWtbarr_get_kind(PyWtbarr* self, void* closure) { + return PyUnicode_FromFormat("%c", self->x->kind); } + /*************************************************************************** * PyWtbarr definition structures */ static PyGetSetDef PyWtbarr_getset[] = { - {"data", (getter)PyWtbarr_get_data, (setter)PyWtbarr_set_data, (char *)doc_data}, - {"dims", (getter)PyWtbarr_get_dims, NULL, (char *)doc_dims}, - {"extlev", (getter)PyWtbarr_get_extlev, NULL, (char *)doc_extlev}, - {"extnam", (getter)PyWtbarr_get_extnam, NULL, (char *)doc_extnam}, - {"extver", (getter)PyWtbarr_get_extver, NULL, (char *)doc_extver}, - {"i", (getter)PyWtbarr_get_i, NULL, (char *)doc_i}, - {"kind", (getter)PyWtbarr_get_kind, NULL, (char *)doc_kind}, - {"m", (getter)PyWtbarr_get_m, NULL, (char *)doc_m}, - {"ndim", (getter)PyWtbarr_get_ndim, NULL, (char *)doc_ndim}, - {"row", (getter)PyWtbarr_get_row, NULL, (char *)doc_row}, - {"ttype", (getter)PyWtbarr_get_ttype, NULL, (char *)doc_ttype}, + {"i", (getter)PyWtbarr_get_i, NULL, (char *) doc_i}, + {"m", (getter)PyWtbarr_get_m, NULL, (char *) doc_m}, + {"kind", (getter)PyWtbarr_get_kind, NULL, (char *) doc_kind}, + {"extnam", (getter)PyWtbarr_get_extnam, NULL, (char *) doc_extnam}, + {"extver", (getter)PyWtbarr_get_extver, NULL, (char *) doc_extver}, + {"extlev", (getter)PyWtbarr_get_extlev, NULL, (char *) doc_extlev}, + {"ttype", (getter)PyWtbarr_get_ttype, NULL, (char *) doc_ttype}, + {"row", (getter)PyWtbarr_get_row, NULL, (char *) doc_row}, + {"ndim", (getter)PyWtbarr_get_ndim, NULL, (char *) doc_ndim}, +/* {"dimlen", (getter)PyWtbarr_get_dimlen, NULL, (char *) NULL}, */ +/* {"arrayp", (getter)PyWtbarr_get_arrayp, NULL, (char *) NULL}, */ {NULL} }; + static PyMethodDef PyWtbarr_methods[] = { + {"print_contents", (PyCFunction)PyWtbarr_print_contents, METH_NOARGS, doc_print_contents_wtbarr}, {NULL} }; -PyTypeObject PyWtbarrType = { - #if PY3K - PyVarObject_HEAD_INIT(NULL, 0) - #else - PyObject_HEAD_INIT(NULL) - 0, /*ob_size*/ - #endif - "astropy.wcs.Wtbarr", /*tp_name*/ - sizeof(PyWtbarr), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - (destructor)PyWtbarr_dealloc, /*tp_dealloc*/ - 0, /*tp_print*/ - 0, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_compare*/ - 0, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - 0, /*tp_hash */ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ - doc_Wtbarr, /* tp_doc */ - (traverseproc)PyWtbarr_traverse, /* tp_traverse */ - (inquiry)PyWtbarr_clear, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - PyWtbarr_methods, /* tp_methods */ - 0, /* tp_members */ - PyWtbarr_getset, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - 0, /* tp_new */ +static PyType_Spec PyWtbarrType_spec = { + .name = "astropy.wcs.Wtbarr", + .basicsize = sizeof(PyWtbarr), + .itemsize = 0, + .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_IMMUTABLETYPE, + .slots = (PyType_Slot[]){ + {Py_tp_dealloc, (destructor)PyWtbarr_dealloc}, + {Py_tp_str, (reprfunc)PyWtbarr___str__}, + {Py_tp_doc, doc_Wtbarr}, + {Py_tp_traverse, (traverseproc)PyWtbarr_traverse}, + {Py_tp_clear, (inquiry)PyWtbarr_clear}, + {Py_tp_getset, PyWtbarr_getset}, + {Py_tp_methods, PyWtbarr_methods}, + // FIXME: this seems logical but this slot was not previously defined + // maybe an error from https://github.com/astropy/astropy/pull/9641 ? + // {Py_tp_new, (newfunc)PyWtbarr_new}, + {0, NULL}, + }, }; -int -_setup_wtbarr_type( - PyObject* m) { +PyObject* PyWtbarrType = NULL; - if (PyType_Ready(&PyWtbarrType) < 0) { +int +_setup_wtbarr_type(PyObject* m) { + PyWtbarrType = PyType_FromSpec(&PyWtbarrType_spec); + if (PyWtbarrType == NULL) { return -1; } - Py_INCREF(&PyWtbarrType); - - PyModule_AddObject(m, "Wtbarr", (PyObject *)&PyWtbarrType); + PyModule_AddObject(m, "Wtbarr", PyWtbarrType); return 0; } diff --git a/astropy/wcs/tests/__init__.py b/astropy/wcs/tests/__init__.py index 8b137891791f..9dce85d06f83 100644 --- a/astropy/wcs/tests/__init__.py +++ b/astropy/wcs/tests/__init__.py @@ -1 +1 @@ - +# Licensed under a 3-clause BSD style license - see LICENSE.rst diff --git a/astropy/wcs/tests/conftest.py b/astropy/wcs/tests/conftest.py new file mode 100644 index 000000000000..f19140246efc --- /dev/null +++ b/astropy/wcs/tests/conftest.py @@ -0,0 +1,54 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +import pytest + +from astropy import wcs + +from .helper import SimModelTAB + + +@pytest.fixture(scope="module") +def tab_wcs_2di(): + model = SimModelTAB(nx=150, ny=200) + + # generate FITS HDU list: + hdulist = model.hdulist + + # create WCS object: + w = wcs.WCS(hdulist[0].header, hdulist) + + return w + + +@pytest.fixture(scope="module") +def tab_wcsh_2di(): + model = SimModelTAB(nx=150, ny=200) + + # generate FITS HDU list: + hdulist = model.hdulist + + # create WCS object: + w = wcs.WCS(hdulist[0].header, hdulist) + + return w, hdulist + + +@pytest.fixture(scope="function") +def tab_wcs_2di_f(): + model = SimModelTAB(nx=150, ny=200) + + # generate FITS HDU list: + hdulist = model.hdulist + + # create WCS object: + w = wcs.WCS(hdulist[0].header, hdulist) + + return w + + +@pytest.fixture(scope="function") +def prj_TAB(): + prj = wcs.Prjprm() + prj.code = "TAN" + prj.set() + return prj diff --git a/astropy/wcs/tests/data/2wcses.hdr b/astropy/wcs/tests/data/2wcses.hdr new file mode 100644 index 000000000000..8e4a11b97ba5 --- /dev/null +++ b/astropy/wcs/tests/data/2wcses.hdr @@ -0,0 +1 @@ +SIMPLE = T / conforms to FITS standard BITPIX = -32 / array data type NAXIS = 2 / number of array dimensions NAXIS1 = 2048 NAXIS2 = 4096 EXTEND = T RUN = 418552 / Run number OBSERVAT= 'LAPALMA ' / Name of observatory (IRAF style) OBSERVER= 'Drew ' / Name of principal investigator OBJECT = 'intphas_4970 Ha' / Title of observation LATITUDE= 28.761929 / Telescope latitude (degrees), +28:45:42.9 LONGITUD= -17.877577 / Telescope longitude (degrees), -17:52:39.3 HEIGHT = 2348 / [m] Height above sea level. SLATEL = 'LPO2.5 ' / Telescope name known to SLALIB TELESCOP= 'INT ' / 2.5m Isaac Newton Telescope MJD-OBS = 53240.9816151 / Modified Julian Date of midtime of observation JD = 2453241.4816151 / Julian Date of midtime of observation PLATESCA= 6.856013 / [d/m] Platescale ( 24.68arcsec/mm) TELFOCUS= 0.043969 / Telescope focus (metres) AIRMASS = 1.048846 / Effective mean airmass DATE-OBS= '2004-08-23T23:32:32.4' / UTC date start of observation UTSTART = '23:32:32.4' / UTC of start of observation TEMPTUBE= 10.15365 / Truss Temperature (degrees Celsius) INSTRUME= 'WFC ' / INT wide-field camera is in use. WFFPOS = 1 / Position-number of deployed filter WFFBAND = 'Halpha ' / Waveband of filter WFFID = '197 ' / Unique identifier of filter SECPPIX = 0.333 / Arcseconds per pixel DETECTOR= 'WFC ' / Formal name of camera CCDSPEED= 'FAST ' / Readout speed CCDXBIN = 1 / Binning factor in x axis CCDYBIN = 1 / Binning factor in y axis CCDSUM = '1 1 ' / Binning factors (IRAF style) CCDTEMP = 156.114 / [K] Cryostat temperature NWINDOWS= 0 / Number of readout windows CCDNAME = 'A5506-4 ' / Name of detector chip. CCDXPIXE= 1.35E-05 / [m] Size of pixels in x. CCDYPIXE= 1.35E-05 / [m] Size of pixels in y. AMPNAME = 'LH ' / Name of output amplifier. GAIN = 2.8 / Nominal Photo-electrons per ADU. READNOIS= 6.4 / Nominal Readout noise in electrons. NUMBRMS = 257 / Number of standards used STDCRMS = 0.067 / Astrometric fit error (arcsec) PERCORR = 0.0 / Sky calibration correction (mags) EXTINCT = 0.09 / Extinction coefficient (mags) RADESYSA= 'ICRS ' EQUINOX = 2000.0 CTYPE1 = 'RA---ZPN' / Algorithm type for axis 1 CTYPE2 = 'DEC--ZPN' / Algorithm type for axis 2 CRPIX1 = -337.20001 / [pixel] Reference pixel along axis 1 CRPIX2 = 3040.5 / [pixel] Reference pixel along axis 2 CRVAL1 = 292.20508 / [deg] Right ascension at the reference pixel CRVAL2 = 18.582556 / [deg] Declination at the reference pixel CRUNIT1 = 'deg ' / Unit of right ascension coordinates CRUNIT2 = 'deg ' / Unit of declination coordinates CD1_1 = -1.3007094E-06 / Transformation matrix element CD1_2 = -9.2396054E-05 / Transformation matrix element CD2_1 = -9.2389091E-05 / Transformation matrix element CD2_2 = 1.3203634E-06 / Transformation matrix element PV2_1 = 1.0 / Coefficient for r term PV2_2 = 0.0 / Coefficient for r**2 term PV2_3 = 220.0 / Coefficient for r**3 term ORIGZPT = 21.53 / Original nightly ZP; uncorrected for extinctionMAGZPT = 21.40896253966641 / Re-calibrated DR2 zeropoint EXPTIME = 120.02 / [sec] Exposure time assumed by the pipeline CHECKSUM= '7RREBPRB9PRBAPRB' / HDU checksum updated 2014-02-06T12:02:07 DATASUM = '1660673036' / data unit checksum updated 2014-02-06T12:02:07 HISTORY 20041004 14:45:42 HISTORY $Id: cir_create_file.c,v 1.10 2004/09/03 10:48:45 jim Exp $ HISTORY 20041004 14:45:43 HISTORY $Id: cir_ccdproc.c,v 1.9 2004/09/07 14:18:51 jim Exp $ HISTORY 20041004 22:52:54 HISTORY $Id: cir_imcore.c,v 1.11 2004/09/07 14:18:52 jim Exp $ HISTORY 20041004 22:52:56 HISTORY $Id: cir_platesol.c,v 1.9 2004/09/07 14:18:54 jim Exp $ HISTORY 20041005 16:05:06 HISTORY $Id: cir_imcore.c,v 1.11 2004/09/07 14:18:52 jim Exp $ HISTORY 20041006 07:31:07 HISTORY $Id: cir_platesol.c,v 1.9 2004/09/07 14:18:54 jim Exp $ HISTORY 20131220 22:36:15 HISTORY Headers updated by Geert Barentsen as part of DR2. HISTORY This included changes to MAGZPT, EXPTIME and the WCS. COMMENT Calibration info COMMENT ================ COMMENT The MAGZPT keyword in this header has been corrected for atmospheric COMMENT extinction and gain (PERCORR) and has been re-calibrated as part of DR2.COMMENT COMMENT Hence to obtain calibrated magnitudes relative to Vega, use: COMMENT mag(Vega) = MAGZPT - 2.5*log(pixel value / EXPTIME) END \ No newline at end of file diff --git a/astropy/wcs/tests/data/3d_cd.hdr b/astropy/wcs/tests/data/3d_cd.hdr index be4ce2f1f88e..8791e9d35e81 100644 --- a/astropy/wcs/tests/data/3d_cd.hdr +++ b/astropy/wcs/tests/data/3d_cd.hdr @@ -1,16 +1 @@ -CD1_2 = -3.72E-05 -CD1_3 = 0 -CD1_1 = -4.12E-05 -CUNIT3 = 'nm ' -CUNIT2 = 'deg ' -CTYPE1 = 'RA---TAN' -NAXIS = 3 -CTYPE3 = 'AWAV ' -CD2_1 = -3.72E-05 -CTYPE2 = 'DEC--TAN' -CD2_3 = 0 -CD2_2 = 4.12E-05 -CUNIT1 = 'deg ' -CD3_1 = 0 -CD3_2 = 0 -CD3_3 = 0.2 \ No newline at end of file +CD1_2 = -3.72E-05 CD1_3 = 0 CD1_1 = -4.12E-05 CUNIT3 = 'nm ' CUNIT2 = 'deg ' CTYPE1 = 'RA---TAN' NAXIS = 3 CTYPE3 = 'AWAV ' CD2_1 = -3.72E-05 CTYPE2 = 'DEC--TAN' CD2_3 = 0 CD2_2 = 4.12E-05 CUNIT1 = 'deg ' CD3_1 = 0 CD3_2 = 0 CD3_3 = 0.2 \ No newline at end of file diff --git a/astropy/wcs/tests/data/chandra-pixlist-wcs.hdr b/astropy/wcs/tests/data/chandra-pixlist-wcs.hdr new file mode 100644 index 000000000000..0b54b087ff34 --- /dev/null +++ b/astropy/wcs/tests/data/chandra-pixlist-wcs.hdr @@ -0,0 +1,81 @@ +DATE = '2020-04-25T20:17:34' / Date and time of file creation +DATE-OBS= '2000-07-07T09:25:15' / Observation start date +DATE-END= '2000-07-07T15:27:29' / Observation end date +TTYPE5 = 'chipx ' / Chip coords +TFORM5 = '1I ' / format of field +TUNIT5 = 'pixel ' +TTYPE15 = 'pha_ro ' / total read-out pulse height of event +TFORM15 = '1J ' / format of field +TUNIT15 = 'adu ' +TNULL15 = 0 +MTYPE5 = 'CPC ' / DM Keyword: Descriptor name. +MFORM5 = 'CPCX,CPCY' / [mm] +TCTYP5 = 'CPCX ' +TCRVL5 = 0.0000000000000E+00 +TCRPX5 = 5.0000000000000E-01 +TCDLT5 = 2.3987000000000E-02 +TCUNI5 = 'mm ' +TTYPE6 = 'chipy ' / Chip coords +TFORM6 = '1I ' / format of field +TUNIT6 = 'pixel ' +TTYPE16 = 'energy ' / nominal energy of event (eV) +TFORM16 = '1E ' / format of field +TUNIT16 = 'eV ' +TCTYP6 = 'CPCY ' +TCRVL6 = 0.0000000000000E+00 +TCRPX6 = 5.0000000000000E-01 +TCDLT6 = 2.3987000000000E-02 +TCUNI6 = 'mm ' +MTYPE6 = 'MSC ' / DM Keyword: Descriptor name. +MFORM6 = 'PHI,THETA' / [deg] +TTYPE9 = 'detx ' / ACIS detector coordinates +TFORM9 = '1E ' / format of field +TUNIT9 = 'pixel ' +TTYPE19 = 'grade ' / binned event grade +TFORM19 = '1I ' / format of field +TCTYP9 = 'LONG-TAN' +TCRVL9 = 0.0000000000000E+00 +TCRPX9 = 4.0965000000000E+03 +TCDLT9 = 1.3666666666667E-04 +TCNA9 = 'PHI ' +TCUNI9 = 'deg ' +LONP9 = 2.7000000000000E+02 +LATP9 = 9.0000000000000E+01 +TTYPE10 = 'dety ' / ACIS detector coordinates +TFORM10 = '1E ' / format of field +TUNIT10 = 'pixel ' +TCTYP10 = 'NPOL-TAN' +TCRVL10 = 0.0000000000000E+00 +TCRPX10 = 4.0965000000000E+03 +TCDLT10 = 1.3666666666667E-04 +TCNA10 = 'THETA ' +TCUNI10 = 'deg ' +TTYPE11 = 'x ' / sky coordinates +TFORM11 = '1E ' / format of field +TUNIT11 = 'pixel ' +TCTYP11 = 'RA---TAN' +TCRVL11 = 2.2938051931869E+02 +TCRPX11 = 4.0965000000000E+03 +TCDLT11 = -1.3666666666667E-04 +TCUNI11 = 'deg ' +TTYPE12 = 'y ' / sky coordinates +TFORM12 = '1E ' / format of field +TUNIT12 = 'pixel ' +TCTYP12 = 'DEC--TAN' +TCRVL12 = -5.8811080688850E+01 +TCRPX12 = 4.0965000000000E+03 +TCDLT12 = 1.3666666666667E-04 +TCUNI12 = 'deg ' +TIMESYS = 'TT ' / Time system +TIMEZERO= 0.0000000000000E+00 / [s] Clock correction +TIMEUNIT= 's ' / Time unit +TIMEREF = 'LOCAL ' / Time reference (barycenter/local) +TIMEPIXR= 5.0000000000000E-01 / default +TIMEDEL = 9.4104000000000E-01 / [s] timedel Lev1 +TIMEDELA= 9.4104000000000E-01 / Inferred duration of primary exposure (s) +TIMEDELB= 0.0000000000000E+00 / Inferred duration of secondary exp. (s) +TIME_ADJ= 'NONE ' / time adjustment algorithm +TSTART = 7.9349115922606E+07 / [s] Observation start time (MET) +TSTOP = 7.9370849510907E+07 / [s] Observation end time (MET) +MJD-OBS = 5.1732392545401E+04 / Modified Julian date of observation +RADESYS = 'ICRS ' / Equatorial coordinate system \ No newline at end of file diff --git a/astropy/wcs/tests/data/defunct_keywords.hdr b/astropy/wcs/tests/data/defunct_keywords.hdr new file mode 100644 index 000000000000..61ba12ba4870 --- /dev/null +++ b/astropy/wcs/tests/data/defunct_keywords.hdr @@ -0,0 +1 @@ +SIMPLE = T / Uncompressed file's conforms to FITS BITPIX = 16 / data type of original image NAXIS = 2 / dimension of original image NAXIS1 = 720 / length of original image axis NAXIS2 = 720 / length of original image axis PSLIB_V = '34286 ' MODULE_V= '34287:34288' PHOT_V = '34286:34322' STATS_V = '34286 ' STACK_V = '34286 ' HISTORY ppStack source: 60eb6cdc-a59c-4636-a4e0-dba66a9721fd NINPUTS = 18 / Number of input images STK_TYPE= 'DEEP_STACK' / type of stack STK_ID = '1237984 ' / type of stack SKYCELL = 'skycell.0680.071' / type of stack TESS_ID = 'RINGS.V3' / type of stack AIRM_SLP= 0. / airmass slope PSCAMERA= 'GPC1 ' / Camera name PSFORMAT= 'SKYCELL ' / Camera format IMAGEID = 1237984 / Image identifier SOURCEID= 35 / Source identifier CTYPE1 = 'RA---TAN' CTYPE2 = 'DEC--TAN' CRVAL1 = 205.063293456991 CRVAL2 = -29.9999999999985 CRPIX1 = 17900.5 CRPIX2 = -13877.5 CDELT1 = 6.94444461259981E-05 CDELT2 = 6.94444461259981E-05 PC001001= -1. PC001002= 0. PC002001= 0. PC002002= 1. RA_DEG = 206.45559692 / Right Ascension of stamp center DEC_DEG = -29.00419807 / Declination of stamp center BSCALE = 1.073792648315E+01 / Scaling: TRUE = BZERO + BSCALE * DISK BZERO = 3.501794623489E+05 / Scaling: TRUE = BZERO + BSCALE * DISK BLANK = 32767 / Value for undefined pixels ZBLANK = 32767 / Value for undefined pixels END \ No newline at end of file diff --git a/astropy/wcs/tests/data/dist.fits b/astropy/wcs/tests/data/dist.fits index 5f68377c72d5..457c63fc6bf7 100644 Binary files a/astropy/wcs/tests/data/dist.fits and b/astropy/wcs/tests/data/dist.fits differ diff --git a/astropy/wcs/tests/data/dist_lookup.fits.gz b/astropy/wcs/tests/data/dist_lookup.fits.gz new file mode 100644 index 000000000000..f6ec2150c048 Binary files /dev/null and b/astropy/wcs/tests/data/dist_lookup.fits.gz differ diff --git a/astropy/wcs/tests/data/dss.14.29.56-62.41.05.fits.gz b/astropy/wcs/tests/data/dss.14.29.56-62.41.05.fits.gz new file mode 100644 index 000000000000..2ba9638f815b Binary files /dev/null and b/astropy/wcs/tests/data/dss.14.29.56-62.41.05.fits.gz differ diff --git a/astropy/wcs/tests/data/header_with_time.fits b/astropy/wcs/tests/data/header_with_time.fits new file mode 100644 index 000000000000..17cc4f3f86aa --- /dev/null +++ b/astropy/wcs/tests/data/header_with_time.fits @@ -0,0 +1 @@ +SIMPLE = F / Conforms to FITS standard? NO! BITPIX = -32 / IEEE single precision floating point NAXIS = 0 / No image data CRPIX1A = 513.0 / Pixel coordinate of reference point CRPIX2A = 513.0 / Pixel coordinate of reference point CRPIX3A = 1025.0 / Pixel coordinate of reference point CRPIX4A = 1.0 / Pixel coordinate of reference point PC1_1A = 0.866025404 / Linear transformation matrix element PC1_2A = 0.500000000 / Linear transformation matrix element PC2_1A = -0.500000000 / Linear transformation matrix element PC2_2A = 0.866025404 / Linear transformation matrix element CDELT1A = -0.10 / [deg] x-scale CUNIT1A = 'deg' / Degree units are required CTYPE1A = 'RA---SZP' / Right ascension in slant zenithal projection CRVAL1A = 150.0 / [deg] Right ascension at the reference point CNAME1A = 'Right ascension (J2000)' / Axis name for labelling purposes CDELT2A = 0.10 / [deg] y-scale CUNIT2A = 'deg' / Degree units are required CTYPE2A = 'DEC--SZP' / Declination in a slant zenithal projection CRVAL2A = -30.0 / [deg] Declination at the reference point CNAME2A = 'Declination (J2000)' / Axis name for labelling purposes PV1_1A = 0.0 / [deg] Native longitude of the reference point PV1_2A = 90.0 / [deg] Native latitude of the reference point PV1_3A = 195.0 / [deg] LONPOLEa by another name (precedence) PV1_4A = 999.0 / [deg] LATPOLEa by another name (precedence) PV2_1A = 0.0 / SZP distance, in spherical radii PV2_2A = 180.0 / [deg] SZP P-longitude PV2_3A = 45.0 / [deg] SZP P-latitude LONPOLEA= 195.0 / [deg] Native longitude of the NCP LATPOLEA= 999.0 / [deg] Native latitude of the NCP RADESYSA= 'FK5' / Mean equatorial coordinates, IAU 1984 system EQUINOXA= 2000.0 / [yr] Equinox of equatorial coordinates CDELT3A = -9.635265432E-6 / [m] Wavelength scale CUNIT3A = 'm' / Wavelength units CTYPE3A = 'WAVE-F2W' / Frequency axis expressed as wavelength CRVAL3A = 0.214982042 / [m] Reference wavelength CNAME3A = 'Wavelength' / Axis name for labelling purposes CRDER3A = 1.0E-11 / [m] Wavelength calibration, random error CSYER3A = 1.0E-12 / [m] Wavelength calibration, systematic error RESTFRQA= 1.42040575E9 / [Hz] HI rest frequency RESTWAVA= 0.211061141 / [m] HI rest wavelength SPECSYSA= 'BARYCENT' / Reference frame of spectral coordinates SSYSOBSA= 'TOPOCENT' / Reference frame of observation VELOSYSA= 1500.0 / [m/s] Bary-topo velocity towards the source SSYSSRCA= 'LSRK' / Reference frame of source redshift ZSOURCEA= 0.0025 / Redshift of the source CDELT4A = 1.0 / [s] Time scale CUNIT4A = 's' / Time units CTYPE4A = 'TIME ' / String value and comment containing quotes (') CRVAL4A = -2E3 / [s] Time at the reference point CNAME4A = 'Time offset' / Axis name for labelling purposes PS4_0A = 'UTC' / Time measurement system UNDEF = / Undefined keyvalue TRUE = T / Logical FALSE = F / Logical INT32 = 00000012345 / Not a 64-bit integer INT32 = -000000123456789 / Not a 64-bit integer INT32 = -2147483648 / Not a 64-bit integer (INT_MIN) INT32 = 2147483647 / Not a 64-bit integer (INT_MAX) INT32 = 0000000000000000000000000000000000012345 / Not a very long integer INT32 = -000000000000000000000000000123456789 / Not a very long integer INT64 = -2147483649 / 64-bit integer (INT_MIN - 1) INT64 = +2147483648 / 64-bit integer (INT_MAX + 1) INT64 = +100000000000000000 / 64-bit integer INT64 = -876543210987654321 / 64-bit integer INT64 = -9223372036854775808 / Not a very long integer (LONG_MIN) INT64 = +9223372036854775807 / Not a very long integer (LONG_MAX) INT64 = -000000000000000000000000000000876543210987654321 / 64-bit integer INTVL = -9223372036854775809 / Very long integer (LONG_MIN - 1) INTVL = +9223372036854775808 / Very long integer (LONG_MAX + 1) INTVL = -100000000000000000000000000000876543210987654321 / Very-long integer INTVL = +123456789012345678901234567890123456789012345678901234567890123456789INTVL = 1234567890123456789012345678901234567890123456789012345678901234567890FLOAT = 3.14159265358 / Floating point FLOAT = 1.602176565E-19 / Floating point, lower-case exp allowed FLOAT = 2.99792458E8 / Floating point FLOAT = 6.62606957D-34 / Floating point, lower-case exp allowed FLOAT = 6.02214129D23 / Floating point COMPLEX = (137, -1) / An integer complex keyvalue COMPLEX = (10E5, -0.1) / A floating point complex keyvalue GOODSTR = '"G''DAY" ' / A valid string keyvalue BLANKS = ' ' / An all-blank string equals a single blank LONGSTR = 'The loooooongest possible non-continued string value, 68 characters.'END \ No newline at end of file diff --git a/astropy/wcs/tests/data/header_with_time_wcslib71.fits b/astropy/wcs/tests/data/header_with_time_wcslib71.fits new file mode 100644 index 000000000000..0dde336b11f6 --- /dev/null +++ b/astropy/wcs/tests/data/header_with_time_wcslib71.fits @@ -0,0 +1 @@ +SIMPLE = F / Conforms to FITS standard? NO! BITPIX = -32 / IEEE single precision floating point NAXIS = 0 / No image data CRPIX1A = 513.0 / Pixel coordinate of reference point CRPIX2A = 513.0 / Pixel coordinate of reference point CRPIX3A = 1025.0 / Pixel coordinate of reference point CRPIX4A = 1.0 / Pixel coordinate of reference point PC1_1A = 0.866025404 / Linear transformation matrix element PC1_2A = 0.500000000 / Linear transformation matrix element PC2_1A = -0.500000000 / Linear transformation matrix element PC2_2A = 0.866025404 / Linear transformation matrix element CDELT1A = -0.10 / [deg] x-scale CUNIT1A = 'deg' / Degree units are required CTYPE1A = 'RA---SZP' / Right ascension in slant zenithal projection CRVAL1A = 150.0 / [deg] Right ascension at the reference point CNAME1A = 'Right ascension (J2000)' / Axis name for labelling purposes CDELT2A = 0.10 / [deg] y-scale CUNIT2A = 'deg' / Degree units are required CTYPE2A = 'DEC--SZP' / Declination in a slant zenithal projection CRVAL2A = -30.0 / [deg] Declination at the reference point CNAME2A = 'Declination (J2000)' / Axis name for labelling purposes PV1_1A = 0.0 / [deg] Native longitude of the reference point PV1_2A = 90.0 / [deg] Native latitude of the reference point PV1_3A = 195.0 / [deg] LONPOLEa by another name (precedence) PV1_4A = 999.0 / [deg] LATPOLEa by another name (precedence) PV2_1A = 0.0 / SZP distance, in spherical radii PV2_2A = 180.0 / [deg] SZP P-longitude PV2_3A = 45.0 / [deg] SZP P-latitude LONPOLEA= 195.0 / [deg] Native longitude of the NCP LATPOLEA= 999.0 / [deg] Native latitude of the NCP RADESYSA= 'FK5' / Mean equatorial coordinates, IAU 1984 system EQUINOXA= 2000.0 / [yr] Equinox of equatorial coordinates CDELT3A = -9.635265432E-6 / [m] Wavelength scale CUNIT3A = 'm' / Wavelength units CTYPE3A = 'WAVE-F2W' / Frequency axis expressed as wavelength CRVAL3A = 0.214982042 / [m] Reference wavelength CNAME3A = 'Wavelength' / Axis name for labelling purposes CRDER3A = 1.0E-11 / [m] Wavelength calibration, random error CSYER3A = 1.0E-12 / [m] Wavelength calibration, systematic error RESTFRQA= 1.42040575E9 / [Hz] HI rest frequency RESTWAVA= 0.211061141 / [m] HI rest wavelength SPECSYSA= 'BARYCENT' / Reference frame of spectral coordinates SSYSOBSA= 'TOPOCENT' / Reference frame of observation VELOSYSA= 1500.0 / [m/s] Bary-topo velocity towards the source SSYSSRCA= 'LSRK' / Reference frame of source redshift ZSOURCEA= 0.0025 / Redshift of the source CDELT4A = 1.0 / [s] Time scale CUNIT4A = 's' / Time units CTYPE4A = 'TIME ' / String value and comment containing quotes (') CRVAL4A = -2E3 / [s] Time at the reference point CNAME4A = 'Time offset' / Axis name for labelling purposes PS4_0A = 'UTC' / Time measurement system DATEREFA= '1858-11-17' / ISO-8601 fiducial time MJDREFIA= 0.0 / [d] MJD of fiducial time, integer part MJDREFFA= 0.0 / [d] MJD of fiducial time, fractional part UNDEF = / Undefined keyvalue TRUE = T / Logical FALSE = F / Logical INT32 = 00000012345 / Not a 64-bit integer INT32 = -000000123456789 / Not a 64-bit integer INT32 = -2147483648 / Not a 64-bit integer (INT_MIN) INT32 = 2147483647 / Not a 64-bit integer (INT_MAX) INT32 = 0000000000000000000000000000000000012345 / Not a very long integer INT32 = -000000000000000000000000000123456789 / Not a very long integer INT64 = -2147483649 / 64-bit integer (INT_MIN - 1) INT64 = +2147483648 / 64-bit integer (INT_MAX + 1) INT64 = +100000000000000000 / 64-bit integer INT64 = -876543210987654321 / 64-bit integer INT64 = -9223372036854775808 / Not a very long integer (LONG_MIN) INT64 = +9223372036854775807 / Not a very long integer (LONG_MAX) INT64 = -000000000000000000000000000000876543210987654321 / 64-bit integer INTVL = -9223372036854775809 / Very long integer (LONG_MIN - 1) INTVL = +9223372036854775808 / Very long integer (LONG_MAX + 1) INTVL = -100000000000000000000000000000876543210987654321 / Very-long integer INTVL = +123456789012345678901234567890123456789012345678901234567890123456789INTVL = 1234567890123456789012345678901234567890123456789012345678901234567890FLOAT = 3.14159265358 / Floating point FLOAT = 1.602176565E-19 / Floating point, lower-case exp allowed FLOAT = 2.99792458E8 / Floating point FLOAT = 6.62606957D-34 / Floating point, lower-case exp allowed FLOAT = 6.02214129D23 / Floating point COMPLEX = (137, -1) / An integer complex keyvalue COMPLEX = (10E5, -0.1) / A floating point complex keyvalue GOODSTR = '"G''DAY" ' / A valid string keyvalue BLANKS = ' ' / An all-blank string equals a single blank LONGSTR = 'The loooooongest possible non-continued string value, 68 characters.'END \ No newline at end of file diff --git a/astropy/wcs/tests/data/ie6d07ujq_wcs.fits b/astropy/wcs/tests/data/ie6d07ujq_wcs.fits new file mode 100644 index 000000000000..ce94109e4023 --- /dev/null +++ b/astropy/wcs/tests/data/ie6d07ujq_wcs.fits @@ -0,0 +1,511 @@ +SIMPLE = T / conforms to FITS standard BITPIX = 8 / array data type NAXIS = 0 / number of array dimensions EXTEND = T WCSAXES = 2 / Number of coordinate axes CRPIX1 = 2048.0 / Pixel coordinate of reference point CRPIX2 = 1026.0 / Pixel coordinate of reference point PC1_1 = 3.920290602516E-06 / Coordinate transformation matrix element PC1_2 = -1.0082434475667E-05 / Coordinate transformation matrix element PC2_1 = -1.0351930440257E-05 / Coordinate transformation matrix element PC2_2 = -4.5644540153863E-06 / Coordinate transformation matrix element CDELT1 = 1.0 / [deg] Coordinate increment at reference point CDELT2 = 1.0 / [deg] Coordinate increment at reference point CUNIT1 = 'deg' / Units of coordinate increment and value CUNIT2 = 'deg' / Units of coordinate increment and value CTYPE1 = 'RA---TAN' / TAN (gnomonic) projection + SIP distortions CTYPE2 = 'DEC--TAN' / TAN (gnomonic) projection + SIP distortions CRVAL1 = 83.19300506082 / [deg] Coordinate value at reference point CRVAL2 = -67.73222548109 / [deg] Coordinate value at reference point LONPOLE = 180.0 / [deg] Native longitude of celestial pole LATPOLE = -67.73222548109 / [deg] Native latitude of celestial pole CRDER1 = 5.1597710370541 / [deg] Random error in coordinate CRDER2 = 4.728058065173 / [deg] Random error in coordinate WCSNAME = 'IDC_2731450pi' / Coordinate system title MJDREF = 0.0 / [d] MJD of fiducial time RADESYS = 'ICRS' / Equatorial coordinate system D2IMDIS1= 'LOOKUP ' / Detector to image correction type D2IM1 = 'EXTVER: 1' / Version number of WCSDVARR extension D2IM1 = 'NAXES: 2' / Number of independent variables in D2IM function D2IM1 = 'AXIS.1: 1' / Axis number of the 1st variable in a D2IM function D2IM1 = 'AXIS.2: 2' / Axis number of the 2nd variable in a D2IM function D2IMDIS2= 'LOOKUP ' / Detector to image correction type D2IM2 = 'EXTVER: 2' / Version number of WCSDVARR extension D2IM2 = 'NAXES: 2' / Number of independent variables in D2IM function D2IM2 = 'AXIS.1: 1' / Axis number of the 1st variable in a D2IM function D2IM2 = 'AXIS.2: 2' / Axis number of the 2nd variable in a D2IM function END XTENSION= 'IMAGE ' / Image extension BITPIX = -32 / array data type NAXIS = 2 / number of array dimensions NAXIS1 = 64 NAXIS2 = 32 PCOUNT = 0 / number of parameters GCOUNT = 1 / number of groups EXTNAME = 'D2IMARR ' / extension name CRPIX1 = 0.0 / Coordinate system reference pixel CRPIX2 = 0.0 / Coordinate system reference pixel CRVAL1 = 0.0 / Coordinate system value at reference pixel CRVAL2 = 0.0 / Coordinate system value at reference pixel CDELT1 = 64.0 / Coordinate increment along axis CDELT2 = 64.0 / Coordinate increment along axis EXTVER = 1 / extension value END :ƒo:ƒo:ƒo;o;ěĻ<o COMMENT EQUINOX = 2000.0000 / Mean equinox RADECSYS= 'ICRS ' / Astrometric system CTYPE1 = 'RA---TAN' / WCS projection type for this axis CTYPE2 = 'DEC--TAN' / WCS projection type for this axis CUNIT1 = 'deg ' / Axis unit CUNIT2 = 'deg ' / Axis unit CRVAL1 = 6.188763218E+01 / World coordinate on this axis CRVAL2 = 4.380823580E+00 / World coordinate on this axis CRPIX1 = 7.894960327E+02 / Reference pixel on this axis CRPIX2 = 3.975587511E+02 / Reference pixel on this axis CD1_1 = 2.804743351E-04 / Linear projection matrix CD1_2 = 1.807931352E-06 / Linear projection matrix CD2_1 = 2.367504576E-06 / Linear projection matrix CD2_2 = -2.808315811E-04 / Linear projection matrix PV1_0 = 1.456397190E-04 / Projection distortion parameter PV1_1 = 9.998189362E-01 / Projection distortion parameter PV1_2 = 3.901789582E-04 / Projection distortion parameter PV1_4 = 2.684396013E-03 / Projection distortion parameter PV1_5 = -7.179465569E-04 / Projection distortion parameter PV1_6 = 5.132011264E-04 / Projection distortion parameter PV1_7 = -1.672242131E-03 / Projection distortion parameter PV1_8 = 5.565322076E-04 / Projection distortion parameter PV1_9 = -8.240132461E-04 / Projection distortion parameter PV1_10 = -3.698351238E-05 / Projection distortion parameter PV1_12 = 3.777486931E-03 / Projection distortion parameter PV1_13 = -1.658254611E-03 / Projection distortion parameter PV1_14 = 2.989635088E-04 / Projection distortion parameter PV1_15 = -4.907197064E-04 / Projection distortion parameter PV1_16 = 2.889486855E-05 / Projection distortion parameter PV2_0 = 1.104553949E-04 / Projection distortion parameter PV2_1 = 1.000001456E+00 / Projection distortion parameter PV2_2 = 5.771870225E-04 / Projection distortion parameter PV2_4 = 2.836854693E-04 / Projection distortion parameter PV2_5 = -1.340251514E-04 / Projection distortion parameter PV2_6 = -7.102989931E-04 / Projection distortion parameter PV2_7 = 1.022790097E-03 / Projection distortion parameter PV2_8 = 2.436846754E-04 / Projection distortion parameter PV2_9 = 9.322698310E-04 / Projection distortion parameter PV2_10 = -7.413484043E-03 / Projection distortion parameter PV2_12 = 5.280834355E-04 / Projection distortion parameter PV2_13 = 4.532076725E-04 / Projection distortion parameter PV2_14 = -1.305400687E-04 / Projection distortion parameter PV2_15 = 6.693583421E-03 / Projection distortion parameter PV2_16 = 2.823700675E-02 / Projection distortion parameter FGROUPNO= 1 / SCAMP field group label ASTIRMS1= 0.000000000E+00 / Astrom. dispersion RMS (intern., high S/N) ASTIRMS2= 0.000000000E+00 / Astrom. dispersion RMS (intern., high S/N) ASTRRMS1= 8.822499895E-06 / Astrom. dispersion RMS (ref., high S/N) ASTRRMS2= 2.438161774E-05 / Astrom. dispersion RMS (ref., high S/N) ASTINST = 1 / SCAMP astrometric instrument label FLXSCALE= 1.666666667E-01 / SCAMP relative flux scale MAGZEROP= 30.0000 / SCAMP zero-point PHOTIRMS= 0.0000 / mag dispersion RMS (internal, high S/N) PHOTINST= 1 / SCAMP photometric instrument label PHOTLINK= ' F' / True if linked to a photometric field END diff --git a/astropy/wcs/tests/data/irac_sip.hdr b/astropy/wcs/tests/data/irac_sip.hdr new file mode 100644 index 000000000000..08d370b747c7 --- /dev/null +++ b/astropy/wcs/tests/data/irac_sip.hdr @@ -0,0 +1 @@ +SIMPLE = T BITPIX = -32 / FOUR-BYTE SINGLE PRECISION FLOATING POINT NAXIS = 2 / STANDARD FITS FORMAT NAXIS1 = 256 / STANDARD FITS FORMAT NAXIS2 = 256 / STANDARD FITS FORMAT EXTEND = T / TAPE MAY HAVE STANDARD FITS EXTENSIONS ORIGIN = 'SIRTF Science Center' / Organization generating this FITS file CREATOR = 'S8.9.0' / SW version used to create this FITS file TELESCOP= 'SIRTF ' / SIRTF spacecraft INSTRUME= 'IRAC ' / SIRTF instrument ID COMMENT Controlled data files (CDFs) used: COMMENT w_bqd_files_to_copy_to_sandbox.nl, fileID = 2 COMMENT w_bqd_pointrefine.nl, fileID = 120903 CHNLNUM = 1 / 1 digit instrument channel number EXPTYPE = 'sci ' / Exposure Type REQTYPE = 'AOR ' / Request type (AOR, IER, or SER) AOT_TYPE= 'IracMap ' / Observation template type AORLABEL= 'NSMLT-0013 HP' / AOR Label FOVID = 74 / Field of View ID FOVNAME = 'IRAC_Center_of_4.5&8.0umArray' / Field of View Name / PROPOSAL INFORMATION OBSRVR = 'Giovanni Fazio' / Observer Name (Last, First) OBSRVRID= 2 / Observer ID of Principal Investigator PROCYCL = 1 / Proposal Cycle PROGID = 35 / Program ID PROTITLE= 'MULTIPLICTY AND INFRARED COLORS OF NEARBY MLT DWARFS' / Program TitlePROGCAT = 29 / Program Category / TIME AND EXPOSURE INFORMATION DATE_OBS= '2003-12-06T10:46:35.021' / Date & time at DCE start MJD_OBS = 52979.449 / [days] MJD at DCE start (,JD-2400000.05) UTCS_OBS= 123979595.021 / [sec] J2000 ephem. time at DCE start SCLK_OBS= 755174834.035 / [sec] SCLK time (since 1/1/1980) at DCE start SAMPTIME= 0.2 / [sec] Sample integration time FRAMTIME= 30. / [sec] Time spent integrating (whole array) COMMENT Photons in Well = Flux[photons/sec/pixel] * FRAMTIME EXPTIME = 26.8 / [sec] Effective integration time per pixel COMMENT DN per pixel = Flux[photons/sec/pixel] / GAIN * EXPTIME AINTBEG = 43146779. / [Secs since IRAC turn-on] Time of integ. start ATIMEEND= 431497.75 / [Secs since IRAC turn-on] Time of integ. end AFOWLNUM= 16 / Fowler number AWAITPER= 118 / [0.2 sec] Wait period ANUMREPS= 1 / Number of repeat integrations AREADMOD= 0 / Full (0) or subarray (1) ABARREL = 4 / Barrel shift APEDSIG = 0 / 0=Normal, 1=Pedestal, 2=Signal / TARGET AND POINTING INFORMATION OBJECT = 'BRI0021-02' / Target Name OBJTYPE = 'TargetFixedSingle' / Object Type CRVAL1 = 6.15501347619052 / [deg] RA at CRPIX1,CRPIX2 averaged over DCE CRVAL2 = -2.07230798888938 / [deg] DEC at CRPIX1,CRPIX2 averaged over DCE RA_HMS = '00h24m37.2s' / [hh:mm:ss.s] CRVAL1 as sexagesimal DEC_DMS = '-02d04m20s' / [dd:mm:ss] CRVAL2 as sexagesimal RADESYS = 'ICRS ' / International Celestial Reference System EQUINOX = 2000. / Equinox for ICRF celestial coord. system CD1_1 = -0.000147943581033529 CD1_2 = 0.000305150643914974 CD2_1 = 0.000305100010374518 CD2_2 = 0.000147710276207053 CTYPE1 = 'RA---TAN-SIP' / RA---TAN with distortion in pixel space CTYPE2 = 'DEC--TAN-SIP' / DEC--TAN with distortion in pixel space CRPIX1 = 128. / Reference pixel along axis 1 CRPIX2 = 128. / Reference pixel along axis 2 CRDER1 = 0.000630078723280563 / [deg] Uncertainty in CRVAL1 CRDER2 = 0.000630066308654874 / [deg] Uncertainty in CRVAL2 UNCRTPA = 0.00186634833181778 / [deg] Uncertainty in position angle CSDRADEC= 5.27382080384386E-06 / [deg] Costandard deviation in RA and Dec SIGRA = 0.141175326515381 / [arcsec] RMS dispersion of RA over DCE SIGDEC = 0.0260011516228373 / [arcsec] RMS dispersion of DEC over DCE SIGPA = 0.786707814443969 / [arcsec] RMS dispersion of PA over DCE PA = 64.170376337596 / [deg] Position angle of axis 2 (E of N) (was ORRA_RQST = 6.15510111508666 / [deg] Requested RA at CRPIX1, CRPIX2 DEC_RQST= -2.07249338178042 / [deg] Requested Dec at CRPIX1, CRPIX2 PM_RA = -1.4108 / [arcsec/yr] Proper Motion in RA (J2000) PM_DEC = 1.50775 / [arcsec/yr] Proper Motion in Dec (J200) RMS_JIT = 0.00840644136311876 / [arcsec] RMS jitter during DCE RMS_JITY= 0.00544908399993541 / [arcsec] RMS jitter during DCE along Y RMS_JITZ= 0.00640122956573203 / [arcsec] RMS jitter during DCE along Z SIG_JTYZ= 0.00350446005496643 / [arcsec] Costadard deviation of jitter in YZ PTGDIFF = 0.738140521808859 / [arcsec] Offset btwn actual and rqsted pntng RA_REF = 6.10241222222221 / [deg] Commanded RA (J2000) of ref. position DEC_REF = -1.97235500000001 / [deg] Commanded Dec (J2000) of ref. position USEDBPHF= T / T if Boresight Pointing History File was used / DISTORTION KEYWORDS A_ORDER = 2 / polynomial order, axis 1, detector to sky A_0_2 = 6.666E-06 / distortion coefficient A_1_1 = 1.801E-05 / distortion coefficient A_2_0 = -2.353E-05 / distortion coefficient A_DMAX = 0.58 / [pixel] maximum correction B_ORDER = 2 / polynomial order, axis 2, detector to sky B_0_2 = 2.601E-05 / distortion coefficient B_1_1 = -2.944E-05 / distortion coefficient B_2_0 = -1.226E-06 / distortion coefficient B_DMAX = 0.902 / [pixel] maximum correction AP_ORDER= 2 / polynomial order, axis 1, sky to detector AP_0_1 = -5.463E-06 / distortion coefficient AP_0_2 = -6.666E-06 / distortion coefficient AP_1_0 = 1.14E-05 / distortion coefficient AP_1_1 = -1.801E-05 / distortion coefficient AP_2_0 = 2.353E-05 / distortion coefficient BP_ORDER= 2 / polynomial order, axis 2, sky to detector BP_0_1 = 1.975E-05 / distortion coefficient BP_0_2 = -2.601E-05 / distortion coefficient BP_1_0 = -1.495E-05 / distortion coefficient BP_1_1 = 2.944E-05 / distortion coefficient BP_2_0 = 1.225E-06 / distortion coefficient / PHOTOMETRY BUNIT = 'MJy/sr ' / Units of image data FLUXCONV= 0.111 / Flux Conv. factor (MJy/Str per DN/sec) GAIN = 3.3 / e/DN conversion / GENERAL MAPPING KEYWORDS CYCLENUM= 6 / Current cycle number DITHPOS = 1 / Current dither position / IRAC MAPPING KEYWORDS READMODE= 'FULL ' / Readout mode DITHSCAL= 'small ' / Dither scale (small, medium, large) / INSTRUMENT TELEMETRY DATA ASHTCON = 2 / Shutter condition (1:closed, 2: open) AWEASIDE= 0 / WEA side in use (0:B, 1:A) ACTXSTAT= 0 / Cmded transcal status ATXSTAT = 0 / transcal status ACFLSTAT= 0 / Cmded floodcal status AFLSTAT = 0 / floodcal status AVRSTUCC= -3.5 / [Volts] Cmded VRSTUC Bias AVRSTBEG= -3.51078391 / [Volts] VRSTUC Bias at start integration AVDETC = -2.75 / [Volts] Cmded VDET Bias AVDETBEG= -2.75721574 / [Volts] VDET Bias at start of integration AVGG1C = -3.6500001 / [Volts] Cmded VGG1 Bias AVGG1BEG= -3.2065742 / [Volts] VGG1 Bias at start of integration AVDDUCC = -3 / [Volts] Cmded VDDUC Bias AVDDUBEG= -3 / [Volts] VDDUC Bias at start integration AVGGCLC = 1 / [Volts] Cmnded VGGCL clock rail voltage AVGGCBEG= 1 / [Volts] VGGCL clock rail voltage AHTRIBEG= 204.70100403 / [uAmps] Heater current at start of integ AHTRVBEG= 2.39006352 / [Volts] Heater Voltage at start integ. AFPAT2B = 15.02370644 / [Deg_K] FPA Temp sensor #2 at start integ. AFPAT2BT= 431446.8125 / [Sec] FPA Temp sensor #2 time tag AFPAT2E = 15.02312088 / [Deg_K] FPA temp sensor #2, end integ. AFPAT2ET= 431476.9375 / [Sec] FPA temp sensor #2 time tag ACTENDT = 20.46821594 / [Deg_C] C&T board thermistor AFPECTE = 18.34936523 / [Deg_C] FPE control board thermistor AFPEATE = 21.90242577 / [Deg_C] FPE analog board thermistor ASHTEMPE= 21.59600639 / [Deg_C] Shutter board thermistor ATCTEMPE= 22.81523895 / [Deg_C] Temp. controller board thermistor ACETEMPE= 20.49869537 / [Deg_C] Calib. electronics board thermistor APDTEMPE= 21.47408295 / [Deg_C] PDU board thermistor ACATMP1E= 1.31549275 / [Deg_K] CA Temp, end integration for temp1 ACATMP2E= 1.29850066 / [Deg_K] CA Temp, end integration for temp2 ACATMP3E= 1.33064687 / [Deg_K] CA Temp, end integration for temp3 ACATMP4E= 1.3274169 / [Deg_K] CA Temp, end integration for temp4 ACATMP5E= 1.3255291 / [Deg_K] CA Temp, end integration for temp5 ACATMP6E= 1.32403958 / [Deg_K] CA Temp, end integration for temp6 ACATMP7E= 1.32282794 / [Deg_K] CA Temp, end integration for temp7 ACATMP8E= 1.31592035 / [Deg_K] CA Temp, end integration for temp8 / DATA FLOW KEYWORDS ORIGIN0 = 'JPL_FOS ' / Site where RAW FITS file was written CREATOR0= 'J5.1.0 ' / SW system that created RAW FITS DATE = '2003-12-17T00:52:57' / [YYYY-MM-DDThh:mm:ss UTC] file creation date AORKEY = 3937792 / AOR or EIR key. Astrnmy Obs Req/Instr Eng Req EXPID = 11 / Exposure ID (0-9999) DCENUM = 0 / DCE number (0-9999) TLMGRPS = 1 / expected number of groups FILE_VER= 1 / Version of the raw file made by SIS RAWFILE = 'IRAC.1.0003937792.0011.0000.01.mipl.fits' / Raw data file name CPT_VER = '3.0.94 ' / Channel Param Table FOS versioN CTD_VER = '3.0.94S ' / Cmded telemetry data version EXPDFLAG= F / (T/F) expedited DCE MISS_LCT= 0 / Total Missed Line Cnt in this FITS MANCPKT = F / T if this FITS is Missing Ancillary Data MISSDATA= F / T if this FITS is Missing Image Data PAONUM = 206 / PAO Number CAMPAIGN= 'IRAC003500' / Campaign DCEID = 6086781 / Data-Collection-Event ID DCEINSID= 626089 / DCE Instance ID DPID = 2631728 / Data Product Instance ID PIPENUM = 107 / Pipeline Script Number SOS_VER = 2 / Data-Product Version PLVID = 4 / Pipeline Version ID CALID = 6 / CalTrans Version ID SDRKEPID= 28809 / Sky Dark ensemble product ID PMSKFBID= 341 / Pixel mask ID LINCFBID= 357 / Fall-back Linearity correction ID FLATFBID= 718 / Fall-back flat ID FLXCFBID= 349 / Flux conversion ID MBLTFBID= 696 / Muxbleed Lookup Table ID MBCFFBID= 704 / Muxbleed Coefficients ID / PROCESSING HISTORY HISTORY job.c ver: 1.000000 HISTORY TRANHEAD v. 11.9, ran Tue Dec 16 16:52:35 2003 HISTORY CALTRANS v. 2.7, ran Tue Dec 16 16:52:44 2003 HISTORY cvti2r4 v. 1.25 A30501, generated 12/16/03 at 16:52:44 HISTORY FFCORR v. 1.000, ran Tue Dec 16 16:52:46 2003 HISTORY MUXBLEEDCORR v. 1.600, ran Tue Dec 16 16:52:50 2003 HISTORY FOWLINEARIZE v. 4.800000, ran Tue Dec 16 16:52:50 2003 HISTORY DARKSUBNG v. 1.000, ran Tue Dec 16 16:52:51 2003 HISTORY DARKDRIFT v. 3.5, ran Tue Dec 16 16:52:52 2003 HISTORY FLATAP v. 1.300 Tue Dec 16 16:52:53 2003 HISTORY DNTOFLUX v. 3.7, ran Tue Dec 16 16:52:57 2003 HISTORY PREDICTSAT v. 3.500000, ran Tue Dec 16 16:57:59 2003 HISTORY CALTRANS v. 2.7, ran Tue Dec 16 17:07:31 2003 HISTORY PTNTRAN v. 1.2, ran Tue Dec 16 17:07:32 2003 HISTORY FPGen v. 1.22, ran Tue Dec 16 17:07:33 2003 HISTORY CALTRANS v. 2.7, ran Wed Dec 17 06:14:18 2003 SOFTWARE= 'pointingrefine' / Pointing refinement using pnt-src correlation PTGVERSN= 5.3 / Version number of pointingrefine program RARFND = 6.15526023786181 / [deg] Refined RA DECRFND = -2.07244250543341 / [deg] Refined DEC CT2RFND = -64.5569826743286 / [deg] Refined CROTA2 PA_RFND = 64.5569826743286 / [deg] Refined PA (= -CROTA2_refined) ERARFND = 0.000535377007940228 / [deg] Error in refined RA EDECRFND= 0.00123072014833503 / [deg] Error in refined DEC EPA_RFND= 2.28015678741471 / [deg] Error in refined PA or CROTA2 NASTROM = 6 / # Astrometric sources for absolute refinement RARESID = -0.887761029918005 / [arcsec] Residual: Observed-Refined RA DECRESID= 0.484259558515454 / [arcsec] Residual: Observed-Refined DEC PA_RESID= -1391.7828122373 / [arcsec] Residual: Observed-Refined PA CD11RFND= -0.000145881550132727 / [deg/pix] Refined CD matrix element 1_1 CD12RFND= 0.000306140372692502 / [deg/pix] Refined CD matrix element 1_2 CD21RFND= 0.00030609131452955 / [deg/pix] Refined CD matrix element 2_1 CD22RFND= 0.000145647908967425 / [deg/pix] Refined CD matrix element 2_2 END \ No newline at end of file diff --git a/astropy/wcs/tests/data/j94f05bgq_flt.fits b/astropy/wcs/tests/data/j94f05bgq_flt.fits new file mode 100644 index 000000000000..502a5b3643df --- /dev/null +++ b/astropy/wcs/tests/data/j94f05bgq_flt.fits @@ -0,0 +1 @@ +SIMPLE = T / Fits standard BITPIX = 16 / Bits per pixel NAXIS = 0 / Number of axes EXTEND = T / File may contain extensions ORIGIN = 'NOAO-IRAF FITS Image Kernel July 2003' / FITS file originator IRAF-TLM= '10:43:32 (18/12/2008)' / Time of last modification NEXTEND = 6 / Number of standard extensions DATE = '2007-02-08T21:38:46' / date this file was written (yyyy-mm-dd) FILENAME= 'j94f05bgq_flt.fits' / name of file FILETYPE= 'SCI ' / type of data found in data file TELESCOP= 'HST' / telescope used to acquire data INSTRUME= 'ACS ' / identifier for instrument used to acquire data EQUINOX = 2000.0 / equinox of celestial coord. system / DATA DESCRIPTION KEYWORDS ROOTNAME= 'j94f05bgq ' / rootname of the observation setIMAGETYP= 'EXT ' / type of exposure identifier PRIMESI = 'ACS ' / instrument designated as prime / TARGET INFORMATION TARGNAME= 'NGC104 ' / proposer's target name RA_TARG = 5.655000000000E+00 / right ascension of the target (deg) (J2000) DEC_TARG= -7.207055555556E+01 / declination of the target (deg) (J2000) / PROPOSAL INFORMATION PROPOSID= 10368 / PEP proposal identifier LINENUM = '05.004 ' / proposal logsheet line number PR_INV_L= 'Riess ' / last name of principal investigatorPR_INV_F= 'Adam ' / first name of principal investigator PR_INV_M= ' ' / middle name / initial of principal investigat / EXPOSURE INFORMATION SUNANGLE= 67.819656 / angle between sun and V1 axis MOONANGL= 57.400970 / angle between moon and V1 axis SUN_ALT = -5.746915 / altitude of the sun above Earth's limb FGSLOCK = 'FINE ' / commanded FGS lock (FINE,COARSE,GYROS,UNKNOWN) GYROMODE= '3' / observation scheduled with only two gyros (Y/N)REFFRAME= 'GSC1 ' / guide star catalog version DATE-OBS= '2005-03-07' / UT date of start of observation (yyyy-mm-dd) TIME-OBS= '06:51:26' / UT time of start of observation (hh:mm:ss) EXPSTART= 5.343628571938E+04 / exposure start time (Modified Julian Date) EXPEND = 5.343629036114E+04 / exposure end time (Modified Julian Date) EXPTIME = 400.000000 / exposure duration (seconds)--calculated EXPFLAG = 'NORMAL ' / Exposure interruption indicator QUALCOM1= ' 'QUALCOM2= ' 'QUALCOM3= ' 'QUALITY = ' ' / POINTING INFORMATION PA_V3 = 337.125305 / position angle of V3-axis of HST (deg) / TARGET OFFSETS (POSTARGS) POSTARG1= 0.000000 / POSTARG in axis 1 direction POSTARG2= 0.000000 / POSTARG in axis 2 direction / DIAGNOSTIC KEYWORDS OPUS_VER= 'OPUS 2006_6 ' / OPUS software system version number CAL_VER = '4.6.1 (13-Mar-2006)' / CALACS code version PROCTIME= 5.413989813657E+04 / Pipeline processing time (MJD) / SCIENCE INSTRUMENT CONFIGURATION OBSTYPE = 'IMAGING ' / observation type - imaging or spectroscopic OBSMODE = 'ACCUM ' / operating mode CTEIMAGE= 'NONE' / type of Charge Transfer Image, if applicable SCLAMP = 'NONE ' / lamp status, NONE or name of lamp which is on NRPTEXP = 1 / number of repeat exposures in set: default 1 SUBARRAY= F / data from a subarray (T) or full frame (F) DETECTOR= 'WFC' / detector in use: WFC, HRC, or SBC FILTER1 = 'F606W ' / element selected from filter wheel 1 FILTER2 = 'CLEAR2L ' / element selected from filter wheel 2 FWOFFSET= 0 / computed filter wheel offset FWERROR = F / filter wheel position error flag LRFWAVE = 0.000000 / proposed linear ramp filter wavelength APERTURE= 'WFC ' / aperture name PROPAPER= 'WFC ' / proposed aperture name DIRIMAGE= 'NONE ' / direct image for grism or prism exposure CTEDIR = 'NONE ' / CTE measurement direction: serial or parallel CRSPLIT = 1 / number of cosmic ray split exposures / CALIBRATION SWITCHES: PERFORM, OMIT, COMPLETE STATFLAG= F / Calculate statistics? WRTERR = T / write out error array extension DQICORR = 'COMPLETE' / data quality initialization ATODCORR= 'OMIT ' / correct for A to D conversion errors BLEVCORR= 'COMPLETE' / subtract bias level computed from overscan img BIASCORR= 'COMPLETE' / Subtract bias image FLSHCORR= 'OMIT ' / post flash correction CRCORR = 'OMIT ' / combine observations to reject cosmic rays EXPSCORR= 'COMPLETE' / process individual observations after cr-rejectSHADCORR= 'OMIT ' / apply shutter shading correction DARKCORR= 'COMPLETE' / Subtract dark image FLATCORR= 'COMPLETE' / flat field data PHOTCORR= 'COMPLETE' / populate photometric header keywords RPTCORR = 'OMIT ' / add individual repeat observations DRIZCORR= 'COMPLETE' / drizzle processing / CALIBRATION REFERENCE FILES BPIXTAB = 'jref$q860440tj_bpx.fits' / bad pixel table CCDTAB = 'jref$o151506fj_ccd.fits' / CCD calibration parameters ATODTAB = 'jref$kcb1734hj_a2d.fits' / analog to digital correction file OSCNTAB = 'jref$lch1459bj_osc.fits' / CCD overscan table BIASFILE= 'jref$p3v2228mj_bia.fits' / bias image file name FLSHFILE= 'jref$nad14594j_fls.fits' / post flash correction file name CRREJTAB= 'jref$n4e12511j_crr.fits' / cosmic ray rejection parameters SHADFILE= 'jref$kcb17349j_shd.fits' / shutter shading correction file DARKFILE= 'jref$p3v2228qj_drk.fits' / dark image file name PFLTFILE= 'jref$nar1136nj_pfl.fits' / pixel to pixel flat field file name DFLTFILE= 'N/A ' / delta flat field file name LFLTFILE= 'N/A ' / low order flat PHOTTAB = 'N/A ' / Photometric throughput table GRAPHTAB= 'mtab$r1m18595m_tmg.fits' / the HST graph table COMPTAB = 'mtab$r1j2146sm_tmc.fits' / the HST components table IDCTAB = 'jref$qbu1641sj_idc.fits' / image distortion correction table DGEOFILE= 'jref$qbu16424j_dxy.fits' / Distortion correction image MDRIZTAB= 'jref$p3p16511j_mdz.fits' / MultiDrizzle parameter table CFLTFILE= 'N/A ' / Coronagraphic spot image SPOTTAB = 'N/A ' / Coronagraphic spot offset table / COSMIC RAY REJECTION ALGORITHM PARAMETERS MEANEXP = 0.000000 / reference exposure time for parameters SCALENSE= 0.000000 / multiplicative scale factor applied to noise INITGUES= ' ' / initial guess method (MIN or MED) SKYSUB = ' ' / sky value subtracted (MODE or NONE) SKYSUM = 0.0 / sky level from the sum of all constituent imageCRSIGMAS= ' ' / statistical rejection criteria CRRADIUS= 0.000000 / rejection propagation radius (pixels) CRTHRESH= 0.000000 / rejection propagation threshold BADINPDQ= 0 / data quality flag bits to reject REJ_RATE= 0.0 / rate at which pixels are affected by cosmic rayCRMASK = F / flag CR-rejected pixels in input files (T/F) MDRIZSKY= 115.9195664624303 / Sky value computed by MultiDrizzle / OTFR KEYWORDS T_SGSTAR= ' ' / OMS calculated guide star control / PATTERN KEYWORDS PATTERN1= 'NONE ' / primary pattern type P1_SHAPE= ' ' / primary pattern shape P1_PURPS= ' ' / primary pattern purpose P1_NPTS = 0 / number of points in primary pattern P1_PSPAC= 0.000000 / point spacing for primary pattern (arc-sec) P1_LSPAC= 0.000000 / line spacing for primary pattern (arc-sec) P1_ANGLE= 0.000000 / angle between sides of parallelogram patt (deg)P1_FRAME= ' ' / coordinate frame of primary pattern P1_ORINT= 0.000000 / orientation of pattern to coordinate frame (degP1_CENTR= ' ' / center pattern relative to pointing (yes/no) PATTSTEP= 0 / position number of this point in the pattern / POST FLASH PARAMETERS FLASHDUR= 1.0 / Exposure time in seconds: 0.1 to 409.5 FLASHCUR= 'MED ' / Post flash current: OFF, LOW, MED, HIGH FLASHSTA= 'SUCCESSFUL ' / Status: SUCCESSFUL, ABORTED, NOT PERFORMED SHUTRPOS= 'B ' / Shutter position: A or B / ENGINEERING PARAMETERS CCDAMP = 'ABCD' / CCD Amplifier Readout Configuration CCDGAIN = 1 / commanded gain of CCD CCDOFSTA= 3 / commanded CCD bias offset for amplifier A CCDOFSTB= 3 / commanded CCD bias offset for amplifier B CCDOFSTC= 3 / commanded CCD bias offset for amplifier C CCDOFSTD= 3 / commanded CCD bias offset for amplifier D / CALIBRATED ENGINEERING PARAMETERS ATODGNA = 9.9989998E-01 / calibrated gain for amplifier A ATODGNB = 9.7210002E-01 / calibrated gain for amplifier B ATODGNC = 1.0107000E+00 / calibrated gain for amplifier C ATODGND = 1.0180000E+00 / calibrated gain for amplifier D READNSEA= 4.9699998E+00 / calibrated read noise for amplifier A READNSEB= 4.8499999E+00 / calibrated read noise for amplifier B READNSEC= 5.2399998E+00 / calibrated read noise for amplifier C READNSED= 4.8499999E+00 / calibrated read noise for amplifier D BIASLEVA= 2.4281760E+03 / bias level for amplifier A BIASLEVB= 2.5189324E+03 / bias level for amplifier B BIASLEVC= 2.4417756E+03 / bias level for amplifier C BIASLEVD= 2.4699448E+03 / bias level for amplifier D / ASSOCIATION KEYWORDS ASN_ID = 'NONE ' / unique identifier assigned to association ASN_TAB = 'NONE ' / name of the association table ASN_MTYP= ' ' / Role of the Member in the Association UPWCSVER= '1.1.3.dev30781' / Version of STWCS used to updated the WCS PYWCSVER= '1.12.1.dev3982' / Version of PYWCS used to updated the WCS HISTORY CCD parameters table: HISTORY reference table jref$o151506fj_ccd.fits HISTORY inflight HISTORY June 2002 HISTORY Uncertainty array initialized. HISTORY DQICORR complete ... HISTORY values checked for saturation HISTORY DQ array initialized ... HISTORY reference table jref$q860440tj_bpx.fits HISTORY BLEVCORR complete; bias level from overscan was subtracted. HISTORY BLEVCORR does not include correction for drift along lines. HISTORY Overscan region table: HISTORY reference table jref$lch1459bj_osc.fits HISTORY BIASCORR complete ... HISTORY reference image jref$p3v2228mj_bia.fits HISTORY INFLIGHT 05/03/2005 23/03/2005 HISTORY Superbias by Ray Lucas from proposal 10367 or 10370 HISTORY CCD parameters table: HISTORY reference table jref$o151506fj_ccd.fits HISTORY inflight HISTORY June 2002 HISTORY DARKCORR complete ... HISTORY reference image jref$p3v2228qj_drk.fits HISTORY INFLIGHT 05/03/2005 23/03/2005 HISTORY Superdark by Ray Lucas from proposal 10367 or 10370 HISTORY FLATCORR complete ... HISTORY reference image jref$nar1136nj_pfl.fits HISTORY InFlight 18/04/2002 - 09/05/2002 HISTORY F606W step +1 flat w/ mote shifted to -1 step HISTORY PHOTCORR complete ... HISTORY reference table mtab$r1m18595m_tmg.fits HISTORY reference table mtab$r1j2146sm_tmc.fits HISTORY EXPSCORR complete ... TDDCORR = 'PERFORM ' WFCTDD = 'T ' DISTNAME= 'j94f05bgq_qbu1641sj-NOMODEL-NOMODEL' SIPNAME = 'j94f05bgq_qbu1641sj' END XTENSION= 'IMAGE ' / Image extension BITPIX = -32 / Bits per pixel NAXIS = 2 / Number of axes NAXIS1 = 1 / Axis length NAXIS2 = 1 / Axis length PCOUNT = 0 / No 'random' parameters GCOUNT = 1 / Only one group ORIGIN = 'NOAO-IRAF FITS Image Kernel July 2003' / FITS file originator EXTNAME = 'SCI ' / Extension name EXTVER = 1 / Extension version DATE = '2007-02-08T21:38:47' / Date FITS file was generated IRAF-TLM= '13:38:23 (20/08/2008)' / Time of last modification INHERIT = T / inherit the primary header EXPNAME = 'j94f05bgq ' / exposure identifier BUNIT = 'ELECTRONS' / brightness units / WFC CCD CHIP IDENTIFICATION CCDCHIP = 2 / CCD chip (1 or 2) / World Coordinate System and Related Parameters WCSAXES = 2 / number of World Coordinate System axes CRPIX1 = 2048.0 / x-coordinate of reference pixel CRPIX2 = 1024.0 / y-coordinate of reference pixel CRVAL1 = 5.63056810618 / first axis value at reference pixel CRVAL2 = -72.05457184278998 / second axis value at reference pixel CTYPE1 = 'RA---TAN-SIP' / the coordinate type for the first axis CTYPE2 = 'DEC--TAN-SIP' / the coordinate type for the second axis CD1_1 = 1.29056256197165E-05 / partial of first axis coordinate w.r.t. x CD1_2 = 5.95309123310338E-06 / partial of first axis coordinate w.r.t. y CD2_1 = 5.0220581265601E-06 / partial of second axis coordinate w.r.t. x CD2_2 = -1.2644774105568E-05 / partial of second axis coordinate w.r.t. y LTV1 = 0.0000000E+00 / offset in X to subsection start LTV2 = 0.0000000E+00 / offset in Y to subsection start LTM1_1 = 1.0 / reciprocal of sampling rate in X LTM2_2 = 1.0 / reciprocal of sampling rate in Y ORIENTAT= 154.7891975615892 / position angle of image y axis (deg. e of n) RA_APER = 5.655000000000E+00 / RA of aperture reference position DEC_APER= -7.207055555556E+01 / Declination of aperture reference position PA_APER = 154.533 / Position Angle of reference aperture center (deVAFACTOR= 1.000018683511E+00 / velocity aberration plate scale factor / READOUT DEFINITION PARAMETERS CENTERA1= 2073 / subarray axis1 center pt in unbinned dect. pix CENTERA2= 1035 / subarray axis2 center pt in unbinned dect. pix SIZAXIS1= 4096 / subarray axis1 size in unbinned detector pixelsSIZAXIS2= 2048 / subarray axis2 size in unbinned detector pixelsBINAXIS1= 1 / axis1 data bin size in unbinned detector pixelsBINAXIS2= 1 / axis2 data bin size in unbinned detector pixels / PHOTOMETRY KEYWORDS PHOTMODE= 'ACS WFC1 F606W' / observation con PHOTFLAM= 7.9064521E-20 / inverse sensitivity, ergs/cm2/Ang/electron PHOTZPT = -2.1100000E+01 / ST magnitude zero point PHOTPLAM= 5.9176797E+03 / Pivot wavelength (Angstroms) PHOTBW = 6.7231146E+02 / RMS bandwidth of filter plus detector / REPEATED EXPOSURES INFO NCOMBINE= 1 / number of image sets combined during CR rejecti / DATA PACKET INFORMATION FILLCNT = 0 / number of segments containing fill ERRCNT = 0 / number of segments containing errors PODPSFF = F / podps fill present (T/F) STDCFFF = F / ST DDF fill present (T/F) STDCFFP = 'x5569 ' / ST DDF fill pattern (hex) / ON-BOARD COMPRESSION INFORMATION WFCMPRSD= F / was WFC data compressed? (T/F) CBLKSIZ = 0 / size of compression block in 2-byte words LOSTPIX = 0 / #pixels lost due to buffer overflow COMPTYP = 'None ' / compression type performed (Partial/Full/None) / IMAGE STATISTICS AND DATA QUALITY FLAGS NGOODPIX= 7822781 / number of good pixels SDQFLAGS= 31743 / serious data quality flags GOODMIN = -2.5959351E+02 / minimum value of good pixels GOODMAX = 6.5220551E+04 / maximum value of good pixels GOODMEAN= 2.0491536E+02 / mean value of good pixels SOFTERRS= 0 / number of soft error pixels (DQF=1) SNRMIN = -8.0327058E-01 / minimum signal to noise of good pixels SNRMAX = 2.1379723E+02 / maximum signal to noise of good pixels SNRMEAN = 1.0889255E+01 / mean value of signal to noise of good pixels MEANDARK= 1.5474443E+00 / average of the dark values subtracted MEANBLEV= 2.4558604E+03 / average of all bias levels subtracted MEANFLSH= 0.000000 / Mean number of counts in post flash exposure OCRVAL1 = 5.63056810618 / first axis value at reference pixel OCRVAL2 = -72.05457184279 / second axis value at reference pixel OCRPIX2 = 1024.0 / y-coordinate of reference pixel OCRPIX1 = 2048.0 / x-coordinate of reference pixel ONAXIS2 = 2048 / Axis length ONAXIS1 = 4096 / Axis length OCD2_2 = -1.26445E-05 / partial of second axis coordinate w.r.t. y OCD2_1 = 5.02243E-06 / partial of second axis coordinate w.r.t. x OORIENTA= 154.7886863186197 / position angle of image y axis (deg. e of n) OCTYPE1 = 'RA---TAN' / the coordinate type for the first axis OCD1_1 = 1.29046E-05 / partial of first axis coordinate w.r.t. x OCD1_2 = 5.9531E-06 / partial of first axis coordinate w.r.t. y OCTYPE2 = 'DEC--TAN' / the coordinate type for the second axis WCSCDATE= '21:39:44 (08/02/2007)' / Time WCS keywords were copied. A_0_2 = 2.16615952976212E-06 B_0_2 = -7.2168814507744E-06 A_1_1 = -5.1974576466834E-06 B_1_1 = 6.18443235774478E-06 A_2_0 = 8.551277582556502E-06 B_2_0 = -1.7464918770586E-06 A_0_3 = 1.08193519820265E-11 B_0_3 = -4.1754720492749E-10 A_1_2 = -5.234870743692412E-10 B_1_2 = -6.169265268681388E-11 A_2_1 = -3.9771547747287E-11 B_2_1 = -5.085716167386211E-10 A_3_0 = -4.7304448292227E-10 B_3_0 = 8.56763542781631E-11 A_0_4 = 1.49356171166049E-14 B_0_4 = -9.9570490655478E-15 A_1_3 = -2.456997553774615E-14 B_1_3 = 1.21743011568848E-14 A_2_2 = 3.46791267104378E-14 B_2_2 = -3.6614325928657E-14 A_3_1 = 1.971022971660309E-15 B_3_1 = -3.7795068054874E-15 A_4_0 = 2.37430106240231E-14 B_4_0 = -1.7687653826004E-14 A_ORDER = 4 B_ORDER = 4 HISTORY The following throughput tables were used: crotacomp$hst_ota_007_syn.fitHISTORY s, cracscomp$acs_wfc_im123_004_syn.fits, cracscomp$acs_f606w_005_syn.fitHISTORY s, cracscomp$acs_wfc_ebe_win12f_005_syn.fits, cracscomp$acs_wfc_ccd1_017HISTORY _syn.fits TDDALPHA= 0.1195051334702275 TDDBETA = -0.03716837782340918 IDCSCALE= 0.05 IDCV2REF= 256.6222229003906 IDCV3REF= 302.2264099121094 IDCTHETA= 0.0 OCX10 = 0.001961771095643072 OCX11 = 0.0498307741748614 OCY10 = 0.05027452911198226 OCY11 = 0.001490550115269471 SORIENTA= 154.7925383197021 / position angle of image y axis (deg. e of n) SCRVAL1 = 5.63056810618 / first axis value at reference pixel SNAXIS2 = 2048 / Axis length SNAXIS1 = 4096 / Axis length SCRVAL2 = -72.05457184279 / second axis value at reference pixel SCTYPE1 = 'RA---TAN-SIP' / the coordinate type for the first axis SCTYPE2 = 'DEC--TAN-SIP' / the coordinate type for the second axis SCD2_2 = -1.264489181627715E-05 / partial of second axis coordinate w.r.t. y SCD2_1 = 5.022886862247075E-06 / partial of second axis coordinate w.r.t. x SCD1_2 = 5.952245949610081E-06 / partial of first axis coordinate w.r.t. y SCRPIX2 = 1024.0 / y-coordinate of reference pixel SCRPIX1 = 2048.0 / x-coordinate of reference pixel SCD1_1 = 1.290545120875315E-05 / partial of first axis coordinate w.r.t. x IDCXREF = 2048.0 IDCYREF = 1024.0 WCSNAMEO= 'OPUS ' WCSAXESO= 2 CRPIX1O = 2048 CRPIX2O = 1024 CDELT1O = 1 CDELT2O = 1 CUNIT1O = 'deg ' CUNIT2O = 'deg ' CTYPE1O = 'RA---TAN-SIP' CTYPE2O = 'DEC--TAN-SIP' CRVAL1O = 5.63056810618 CRVAL2O = -72.0545718428 LONPOLEO= 180 LATPOLEO= -72.0545718428 RESTFRQO= 0 RESTWAVO= 0 CD1_1O = 1.29056256334E-05 CD1_2O = 5.9530912342E-06 CD2_1O = 5.02205812656E-06 CD2_2O = -1.26447741482E-05 IDCTAB = 'jref$qbu1641sj_idc.fits' WCSNAME = 'IDC_qbu1641sj' END XTENSION= 'IMAGE ' / Image extension BITPIX = -32 / Bits per pixel NAXIS = 0 / Number of axes PCOUNT = 0 / No 'random' parameters GCOUNT = 1 / Only one group ORIGIN = 'NOAO-IRAF FITS Image Kernel July 2003' / FITS file originator EXTNAME = 'ERR ' / Extension name EXTVER = 1 / Extension version DATE = '2007-02-08T21:38:48' / Date FITS file was generated IRAF-TLM= '13:38:31 (20/08/2008)' / Time of last modification INHERIT = T / inherit the primary header EXPNAME = 'j94f05bgq ' / exposure identifier BUNIT = 'ELECTRONS' / brightness units / World Coordinate System and Related Parameters WCSAXES = 2 / number of World Coordinate System axes CRPIX1 = 2048 / x-coordinate of reference pixel CRPIX2 = 1024 / y-coordinate of reference pixel CRVAL1 = 5.63056810618 / first axis value at reference pixel CRVAL2 = -72.0545718428 / second axis value at reference pixel CTYPE1 = 'RA---TAN-SIP' / the coordinate type for the first axis CTYPE2 = 'DEC--TAN-SIP' / the coordinate type for the second axis CD1_1 = 1.29056256197E-05 / partial of first axis coordinate w.r.t. x CD1_2 = 5.9530912331E-06 / partial of first axis coordinate w.r.t. y CD2_1 = 5.02205812656E-06 / partial of second axis coordinate w.r.t. x CD2_2 = -1.26447741056E-05 / partial of second axis coordinate w.r.t. y LTV1 = 0.0000000E+00 / offset in X to subsection start LTV2 = 0.0000000E+00 / offset in Y to subsection start LTM1_1 = 1.0 / reciprocal of sampling rate in X LTM2_2 = 1.0 / reciprocal of sampling rate in Y ORIENTAT= 154.789 / position angle of image y axis (deg. e of n) RA_APER = 5.655000000000E+00 / RA of aperture reference position DEC_APER= -7.207055555556E+01 / Declination of aperture reference position PA_APER = 154.533 / Position Angle of reference aperture center (deVAFACTOR= 1.000018683511E+00 / velocity aberration plate scale factor / IMAGE STATISTICS AND DATA QUALITY FLAGS NGOODPIX= 7822781 / number of good pixels SDQFLAGS= 31743 / serious data quality flags GOODMIN = 5.0154119E+00 / minimum value of good pixels GOODMAX = 4.6192120E+02 / maximum value of good pixels GOODMEAN= 1.3078087E+01 / mean value of good pixels TDDBETA = 0. TDDALPHA= 0. CDELT1 = 1 CDELT2 = 1 CUNIT1 = 'deg ' CUNIT2 = 'deg ' LONPOLE = 180 LATPOLE = -72.0545718428 RESTFRQ = 0 RESTWAV = 0 WCSNAME = 'IDC_qbu1641sj' END XTENSION= 'IMAGE ' / Image extension BITPIX = 16 / Bits per pixel NAXIS = 0 / Number of axes PCOUNT = 0 / No 'random' parameters GCOUNT = 1 / Only one group ORIGIN = 'NOAO-IRAF FITS Image Kernel July 2003' / FITS file originator EXTNAME = 'DQ ' / Extension name EXTVER = 1 / Extension version DATE = '2007-02-08T21:38:48' / Date FITS file was generated IRAF-TLM= '21:38:48 (08/02/2007)' / Time of last modification INHERIT = T / inherit the primary header EXPNAME = 'j94f05bgq ' / exposure identifier BUNIT = 'UNITLESS ' / brightness units / World Coordinate System and Related Parameters WCSAXES = 2 / number of World Coordinate System axes CRPIX1 = 2048 / x-coordinate of reference pixel CRPIX2 = 1024 / y-coordinate of reference pixel CRVAL1 = 5.63056810618 / first axis value at reference pixel CRVAL2 = -72.0545718428 / second axis value at reference pixel CTYPE1 = 'RA---TAN-SIP' / the coordinate type for the first axis CTYPE2 = 'DEC--TAN-SIP' / the coordinate type for the second axis CD1_1 = 1.29056256197E-05 / partial of first axis coordinate w.r.t. x CD1_2 = 5.9530912331E-06 / partial of first axis coordinate w.r.t. y CD2_1 = 5.02205812656E-06 / partial of second axis coordinate w.r.t. x CD2_2 = -1.26447741056E-05 / partial of second axis coordinate w.r.t. y LTV1 = 0.0000000E+00 / offset in X to subsection start LTV2 = 0.0000000E+00 / offset in Y to subsection start LTM1_1 = 1.0 / reciprocal of sampling rate in X LTM2_2 = 1.0 / reciprocal of sampling rate in Y ORIENTAT= 154.789 / position angle of image y axis (deg. e of n) RA_APER = 5.655000000000E+00 / RA of aperture reference position DEC_APER= -7.207055555556E+01 / Declination of aperture reference position PA_APER = 154.533 / Position Angle of reference aperture center (deVAFACTOR= 1.000018683511E+00 / velocity aberration plate scale factor CDELT1 = 1 CDELT2 = 1 CUNIT1 = 'deg ' CUNIT2 = 'deg ' LONPOLE = 180 LATPOLE = -72.0545718428 RESTFRQ = 0 RESTWAV = 0 WCSNAME = 'IDC_qbu1641sj' END XTENSION= 'IMAGE ' / Image extension BITPIX = -32 / Bits per pixel NAXIS = 2 / Number of axes NAXIS1 = 1 / Axis length NAXIS2 = 1 / Axis length PCOUNT = 0 / No 'random' parameters GCOUNT = 1 / Only one group ORIGIN = 'NOAO-IRAF FITS Image Kernel July 2003' / FITS file originator EXTNAME = 'SCI ' / Extension name EXTVER = 2 / Extension version DATE = '2007-02-08T21:39:08' / Date FITS file was generated IRAF-TLM= '21:39:07 (08/02/2007)' / Time of last modification INHERIT = T / inherit the primary header EXPNAME = 'j94f05bgq ' / exposure identifier BUNIT = 'ELECTRONS' / brightness units / WFC CCD CHIP IDENTIFICATION CCDCHIP = 1 / CCD chip (1 or 2) / World Coordinate System and Related Parameters WCSAXES = 2 / number of World Coordinate System axes CRPIX1 = 2048.0 / x-coordinate of reference pixel CRPIX2 = 1024.0 / y-coordinate of reference pixel CRVAL1 = 5.670733269328501 / first axis value at reference pixel CRVAL2 = -72.08067552067514 / second axis value at reference pixel CTYPE1 = 'RA---TAN-SIP' / the coordinate type for the first axis CTYPE2 = 'DEC--TAN-SIP' / the coordinate type for the second axis CD1_1 = 1.28168672384053E-05 / partial of first axis coordinate w.r.t. x CD1_2 = 5.85585924019860E-06 / partial of first axis coordinate w.r.t. y CD2_1 = 4.80078206009106E-06 / partial of second axis coordinate w.r.t. x CD2_2 = -1.2175120783707E-05 / partial of second axis coordinate w.r.t. y LTV1 = 0.0000000E+00 / offset in X to subsection start LTV2 = 0.0000000E+00 / offset in Y to subsection start LTM1_1 = 1.0 / reciprocal of sampling rate in X LTM2_2 = 1.0 / reciprocal of sampling rate in Y ORIENTAT= 154.3138744206447 / position angle of image y axis (deg. e of n) RA_APER = 5.655000000000E+00 / RA of aperture reference position DEC_APER= -7.207055555556E+01 / Declination of aperture reference position PA_APER = 154.533 / Position Angle of reference aperture center (deVAFACTOR= 1.000018683511E+00 / velocity aberration plate scale factor / READOUT DEFINITION PARAMETERS CENTERA1= 2073 / subarray axis1 center pt in unbinned dect. pix CENTERA2= 1035 / subarray axis2 center pt in unbinned dect. pix SIZAXIS1= 4096 / subarray axis1 size in unbinned detector pixelsSIZAXIS2= 2048 / subarray axis2 size in unbinned detector pixelsBINAXIS1= 1 / axis1 data bin size in unbinned detector pixelsBINAXIS2= 1 / axis2 data bin size in unbinned detector pixels / PHOTOMETRY KEYWORDS PHOTMODE= 'ACS WFC1 F606W' / observation con PHOTFLAM= 7.9064521E-20 / inverse sensitivity, ergs/cm2/Ang/electron PHOTZPT = -2.1100000E+01 / ST magnitude zero point PHOTPLAM= 5.9176797E+03 / Pivot wavelength (Angstroms) PHOTBW = 6.7231146E+02 / RMS bandwidth of filter plus detector / REPEATED EXPOSURES INFO NCOMBINE= 1 / number of image sets combined during CR rejecti / DATA PACKET INFORMATION FILLCNT = 0 / number of segments containing fill ERRCNT = 0 / number of segments containing errors PODPSFF = F / podps fill present (T/F) STDCFFF = F / ST DDF fill present (T/F) STDCFFP = '0x5569' / ST DDF fill pattern (hex) / ON-BOARD COMPRESSION INFORMATION WFCMPRSD= F / was WFC data compressed? (T/F) CBLKSIZ = 0 / size of compression block in 2-byte words LOSTPIX = 0 / #pixels lost due to buffer overflow COMPTYP = 'None ' / compression type performed (Partial/Full/None) / IMAGE STATISTICS AND DATA QUALITY FLAGS NGOODPIX= 7842694 / number of good pixels SDQFLAGS= 31743 / serious data quality flags GOODMIN = -7.0795517E+01 / minimum value of good pixels GOODMAX = 6.9170820E+04 / maximum value of good pixels GOODMEAN= 2.3160507E+02 / mean value of good pixels SOFTERRS= 0 / number of soft error pixels (DQF=1) SNRMIN = -2.9959621E+00 / minimum signal to noise of good pixels SNRMAX = 2.1083392E+02 / maximum signal to noise of good pixels SNRMEAN = 1.1493363E+01 / mean value of signal to noise of good pixels MEANDARK= 1.7685415E+00 / average of the dark values subtracted MEANBLEV= 2.4735542E+03 / average of all bias levels subtracted MEANFLSH= 0.000000 / Mean number of counts in post flash exposure OCRVAL1 = 5.670732627827 / first axis value at reference pixel OCRVAL2 = -72.08067512165999 / second axis value at reference pixel OCRPIX2 = 1024.0 / y-coordinate of reference pixel OCRPIX1 = 2048.0 / x-coordinate of reference pixel ONAXIS2 = 2048 / Axis length ONAXIS1 = 4096 / Axis length OCD2_2 = -1.21751E-05 / partial of second axis coordinate w.r.t. y OCD2_1 = 4.80165E-06 / partial of second axis coordinate w.r.t. x OORIENTA= 154.3148269477964 / position angle of image y axis (deg. e of n) OCTYPE1 = 'RA---TAN' / the coordinate type for the first axis OCD1_1 = 1.28162E-05 / partial of first axis coordinate w.r.t. x OCD1_2 = 5.8556E-06 / partial of first axis coordinate w.r.t. y OCTYPE2 = 'DEC--TAN' / the coordinate type for the second axis WCSCDATE= '21:40:01 (08/02/2007)' / Time WCS keywords were copied. A_0_2 = 2.261941311576225E-06 B_0_2 = -9.798580190744584E-06 A_1_1 = -7.5302899956868E-06 B_1_1 = 6.42569978230108E-06 A_2_0 = 8.518868718265025E-06 B_2_0 = -2.965892204849563E-06 A_0_3 = 6.510508887319651E-11 B_0_3 = -4.14215027648948E-10 A_1_2 = -5.253920848284526E-10 B_1_2 = -3.0354268445463E-11 A_2_1 = -1.0714005144979E-10 B_2_1 = -4.4034920544682E-10 A_3_0 = -4.693635321207041E-10 B_3_0 = 9.00334226345491E-11 A_0_4 = 1.35191450637820E-13 B_0_4 = -1.5248976030166E-13 A_1_3 = -1.4269338934146E-14 B_1_3 = 2.75911359120267E-14 A_2_2 = 9.70199525902786E-14 B_2_2 = -1.0403608182763E-13 A_3_1 = 3.800597894287968E-14 B_3_1 = -3.8363936250847E-14 A_4_0 = 1.836278740012908E-14 B_4_0 = -1.6913942401142E-14 A_ORDER = 4 B_ORDER = 4 IDCSCALE= 0.05 IDCV2REF= 260.8870544433594 IDCV3REF= 198.3322296142578 IDCTHETA= 0.0 OCX10 = 0.002267080585457191 OCX11 = 0.0492242502262243 OCY10 = 0.04858284026784934 OCY11 = 0.002132039303452174 TDDALPHA= 0.1195051334702275 TDDBETA = -0.03716837782340918 SORIENTA= 154.3172042452752 / position angle of image y axis (deg. e of n) SCRVAL1 = 5.670727606678185 / first axis value at reference pixel SNAXIS2 = 2048 / Axis length SNAXIS1 = 4096 / Axis length SCRVAL2 = -72.08067576427173 / second axis value at reference pixel SCTYPE1 = 'RA---TAN-SIP' / the coordinate type for the first axis SCTYPE2 = 'DEC--TAN-SIP' / the coordinate type for the second axis SCD2_2 = -1.217522934245482E-05 / partial of second axis coordinate w.r.t. y SCD2_1 = 4.801597437325027E-06 / partial of second axis coordinate w.r.t. x SCD1_2 = 5.855040197035933E-06 / partial of first axis coordinate w.r.t. y SCRPIX2 = 1024.0 / y-coordinate of reference pixel SCRPIX1 = 2048.0 / x-coordinate of reference pixel SCD1_1 = 1.281668382601152E-05 / partial of first axis coordinate w.r.t. x WCSNAMEO= 'OPUS ' WCSAXESO= 2 CRPIX1O = 2048 CRPIX2O = 1024 CDELT1O = 1 CDELT2O = 1 CUNIT1O = 'deg ' CUNIT2O = 'deg ' CTYPE1O = 'RA---TAN-SIP' CTYPE2O = 'DEC--TAN-SIP' CRVAL1O = 5.67073326894 CRVAL2O = -72.0806755207 LONPOLEO= 180 LATPOLEO= -72.0806755207 RESTFRQO= 0 RESTWAVO= 0 CD1_1O = 1.28168672365E-05 CD1_2O = 5.8558592186E-06 CD2_1O = 4.80078206009E-06 CD2_2O = -1.21751207695E-05 IDCXREF = 2048.0 IDCYREF = 1024.0 IDCTAB = 'jref$qbu1641sj_idc.fits' WCSNAME = 'IDC_qbu1641sj' HISTORY The following throughput tables were used: crotacomp$hst_ota_007_syn.fitHISTORY s, cracscomp$acs_wfc_im123_004_syn.fits, cracscomp$acs_f606w_005_syn.fitHISTORY s, cracscomp$acs_wfc_ebe_win12f_005_syn.fits, cracscomp$acs_wfc_ccd1_017HISTORY _syn.fits END XTENSION= 'IMAGE ' / Image extension BITPIX = -32 / Bits per pixel NAXIS = 0 / Number of axes PCOUNT = 0 / No 'random' parameters GCOUNT = 1 / Only one group ORIGIN = 'NOAO-IRAF FITS Image Kernel July 2003' / FITS file originator EXTNAME = 'ERR ' / Extension name EXTVER = 2 / Extension version DATE = '2007-02-08T21:39:09' / Date FITS file was generated IRAF-TLM= '21:39:08 (08/02/2007)' / Time of last modification INHERIT = T / inherit the primary header EXPNAME = 'j94f05bgq ' / exposure identifier BUNIT = 'ELECTRONS' / brightness units / World Coordinate System and Related Parameters WCSAXES = 2 / number of World Coordinate System axes CRPIX1 = 2048 / x-coordinate of reference pixel CRPIX2 = 1024 / y-coordinate of reference pixel CRVAL1 = 5.67073326933 / first axis value at reference pixel CRVAL2 = -72.0806755207 / second axis value at reference pixel CTYPE1 = 'RA---TAN-SIP' / the coordinate type for the first axis CTYPE2 = 'DEC--TAN-SIP' / the coordinate type for the second axis CD1_1 = 1.28168672384E-05 / partial of first axis coordinate w.r.t. x CD1_2 = 5.8558592402E-06 / partial of first axis coordinate w.r.t. y CD2_1 = 4.80078206009E-06 / partial of second axis coordinate w.r.t. x CD2_2 = -1.21751207837E-05 / partial of second axis coordinate w.r.t. y LTV1 = 0.0000000E+00 / offset in X to subsection start LTV2 = 0.0000000E+00 / offset in Y to subsection start LTM1_1 = 1.0 / reciprocal of sampling rate in X LTM2_2 = 1.0 / reciprocal of sampling rate in Y ORIENTAT= 154.315 / position angle of image y axis (deg. e of n) RA_APER = 5.655000000000E+00 / RA of aperture reference position DEC_APER= -7.207055555556E+01 / Declination of aperture reference position PA_APER = 154.533 / Position Angle of reference aperture center (deVAFACTOR= 1.000018683511E+00 / velocity aberration plate scale factor / IMAGE STATISTICS AND DATA QUALITY FLAGS NGOODPIX= 7842694 / number of good pixels SDQFLAGS= 31743 / serious data quality flags GOODMIN = 4.8147907E+00 / minimum value of good pixels GOODMAX = 6.5271405E+02 / maximum value of good pixels GOODMEAN= 1.3935461E+01 / mean value of good pixels CDELT1 = 1 CDELT2 = 1 CUNIT1 = 'deg ' CUNIT2 = 'deg ' LONPOLE = 180 LATPOLE = -72.0806755207 RESTFRQ = 0 RESTWAV = 0 WCSNAME = 'IDC_qbu1641sj' END XTENSION= 'IMAGE ' / Image extension BITPIX = 16 / Bits per pixel NAXIS = 0 / Number of axes PCOUNT = 0 / No 'random' parameters GCOUNT = 1 / Only one group ORIGIN = 'NOAO-IRAF FITS Image Kernel July 2003' / FITS file originator EXTNAME = 'DQ ' / Extension name EXTVER = 2 / Extension version DATE = '2007-02-08T21:39:09' / Date FITS file was generated IRAF-TLM= '21:39:09 (08/02/2007)' / Time of last modification INHERIT = T / inherit the primary header EXPNAME = 'j94f05bgq ' / exposure identifier BUNIT = 'UNITLESS ' / brightness units / World Coordinate System and Related Parameters WCSAXES = 2 / number of World Coordinate System axes CRPIX1 = 2048 / x-coordinate of reference pixel CRPIX2 = 1024 / y-coordinate of reference pixel CRVAL1 = 5.67073326933 / first axis value at reference pixel CRVAL2 = -72.0806755207 / second axis value at reference pixel CTYPE1 = 'RA---TAN-SIP' / the coordinate type for the first axis CTYPE2 = 'DEC--TAN-SIP' / the coordinate type for the second axis CD1_1 = 1.28168672384E-05 / partial of first axis coordinate w.r.t. x CD1_2 = 5.8558592402E-06 / partial of first axis coordinate w.r.t. y CD2_1 = 4.80078206009E-06 / partial of second axis coordinate w.r.t. x CD2_2 = -1.21751207837E-05 / partial of second axis coordinate w.r.t. y LTV1 = 0.0000000E+00 / offset in X to subsection start LTV2 = 0.0000000E+00 / offset in Y to subsection start LTM1_1 = 1.0 / reciprocal of sampling rate in X LTM2_2 = 1.0 / reciprocal of sampling rate in Y ORIENTAT= 154.315 / position angle of image y axis (deg. e of n) RA_APER = 5.655000000000E+00 / RA of aperture reference position DEC_APER= -7.207055555556E+01 / Declination of aperture reference position PA_APER = 154.533 / Position Angle of reference aperture center (deVAFACTOR= 1.000018683511E+00 / velocity aberration plate scale factor CDELT1 = 1 CDELT2 = 1 CUNIT1 = 'deg ' CUNIT2 = 'deg ' LONPOLE = 180 LATPOLE = -72.0806755207 RESTFRQ = 0 RESTWAV = 0 WCSNAME = 'IDC_qbu1641sj' END \ No newline at end of file diff --git a/astropy/wcs/tests/maps/1904-66_AIR.hdr b/astropy/wcs/tests/data/maps/1904-66_AIR.hdr similarity index 100% rename from astropy/wcs/tests/maps/1904-66_AIR.hdr rename to astropy/wcs/tests/data/maps/1904-66_AIR.hdr diff --git a/astropy/wcs/tests/maps/1904-66_AIT.hdr b/astropy/wcs/tests/data/maps/1904-66_AIT.hdr similarity index 100% rename from astropy/wcs/tests/maps/1904-66_AIT.hdr rename to astropy/wcs/tests/data/maps/1904-66_AIT.hdr diff --git a/astropy/wcs/tests/maps/1904-66_ARC.hdr b/astropy/wcs/tests/data/maps/1904-66_ARC.hdr similarity index 100% rename from astropy/wcs/tests/maps/1904-66_ARC.hdr rename to astropy/wcs/tests/data/maps/1904-66_ARC.hdr diff --git a/astropy/wcs/tests/maps/1904-66_AZP.hdr b/astropy/wcs/tests/data/maps/1904-66_AZP.hdr similarity index 100% rename from astropy/wcs/tests/maps/1904-66_AZP.hdr rename to astropy/wcs/tests/data/maps/1904-66_AZP.hdr diff --git a/astropy/wcs/tests/maps/1904-66_BON.hdr b/astropy/wcs/tests/data/maps/1904-66_BON.hdr similarity index 100% rename from astropy/wcs/tests/maps/1904-66_BON.hdr rename to astropy/wcs/tests/data/maps/1904-66_BON.hdr diff --git a/astropy/wcs/tests/maps/1904-66_CAR.hdr b/astropy/wcs/tests/data/maps/1904-66_CAR.hdr similarity index 100% rename from astropy/wcs/tests/maps/1904-66_CAR.hdr rename to astropy/wcs/tests/data/maps/1904-66_CAR.hdr diff --git a/astropy/wcs/tests/maps/1904-66_CEA.hdr b/astropy/wcs/tests/data/maps/1904-66_CEA.hdr similarity index 100% rename from astropy/wcs/tests/maps/1904-66_CEA.hdr rename to astropy/wcs/tests/data/maps/1904-66_CEA.hdr diff --git a/astropy/wcs/tests/maps/1904-66_COD.hdr b/astropy/wcs/tests/data/maps/1904-66_COD.hdr similarity index 100% rename from astropy/wcs/tests/maps/1904-66_COD.hdr rename to astropy/wcs/tests/data/maps/1904-66_COD.hdr diff --git a/astropy/wcs/tests/maps/1904-66_COE.hdr b/astropy/wcs/tests/data/maps/1904-66_COE.hdr similarity index 100% rename from astropy/wcs/tests/maps/1904-66_COE.hdr rename to astropy/wcs/tests/data/maps/1904-66_COE.hdr diff --git a/astropy/wcs/tests/maps/1904-66_COO.hdr b/astropy/wcs/tests/data/maps/1904-66_COO.hdr similarity index 100% rename from astropy/wcs/tests/maps/1904-66_COO.hdr rename to astropy/wcs/tests/data/maps/1904-66_COO.hdr diff --git a/astropy/wcs/tests/maps/1904-66_COP.hdr b/astropy/wcs/tests/data/maps/1904-66_COP.hdr similarity index 100% rename from astropy/wcs/tests/maps/1904-66_COP.hdr rename to astropy/wcs/tests/data/maps/1904-66_COP.hdr diff --git a/astropy/wcs/tests/maps/1904-66_CSC.hdr b/astropy/wcs/tests/data/maps/1904-66_CSC.hdr similarity index 100% rename from astropy/wcs/tests/maps/1904-66_CSC.hdr rename to astropy/wcs/tests/data/maps/1904-66_CSC.hdr diff --git a/astropy/wcs/tests/maps/1904-66_CYP.hdr b/astropy/wcs/tests/data/maps/1904-66_CYP.hdr similarity index 100% rename from astropy/wcs/tests/maps/1904-66_CYP.hdr rename to astropy/wcs/tests/data/maps/1904-66_CYP.hdr diff --git a/astropy/wcs/tests/maps/1904-66_HPX.hdr b/astropy/wcs/tests/data/maps/1904-66_HPX.hdr similarity index 100% rename from astropy/wcs/tests/maps/1904-66_HPX.hdr rename to astropy/wcs/tests/data/maps/1904-66_HPX.hdr diff --git a/astropy/wcs/tests/maps/1904-66_MER.hdr b/astropy/wcs/tests/data/maps/1904-66_MER.hdr similarity index 100% rename from astropy/wcs/tests/maps/1904-66_MER.hdr rename to astropy/wcs/tests/data/maps/1904-66_MER.hdr diff --git a/astropy/wcs/tests/maps/1904-66_MOL.hdr b/astropy/wcs/tests/data/maps/1904-66_MOL.hdr similarity index 100% rename from astropy/wcs/tests/maps/1904-66_MOL.hdr rename to astropy/wcs/tests/data/maps/1904-66_MOL.hdr diff --git a/astropy/wcs/tests/maps/1904-66_NCP.hdr b/astropy/wcs/tests/data/maps/1904-66_NCP.hdr similarity index 100% rename from astropy/wcs/tests/maps/1904-66_NCP.hdr rename to astropy/wcs/tests/data/maps/1904-66_NCP.hdr diff --git a/astropy/wcs/tests/maps/1904-66_PAR.hdr b/astropy/wcs/tests/data/maps/1904-66_PAR.hdr similarity index 100% rename from astropy/wcs/tests/maps/1904-66_PAR.hdr rename to astropy/wcs/tests/data/maps/1904-66_PAR.hdr diff --git a/astropy/wcs/tests/maps/1904-66_PCO.hdr b/astropy/wcs/tests/data/maps/1904-66_PCO.hdr similarity index 100% rename from astropy/wcs/tests/maps/1904-66_PCO.hdr rename to astropy/wcs/tests/data/maps/1904-66_PCO.hdr diff --git a/astropy/wcs/tests/maps/1904-66_QSC.hdr b/astropy/wcs/tests/data/maps/1904-66_QSC.hdr similarity index 100% rename from astropy/wcs/tests/maps/1904-66_QSC.hdr rename to astropy/wcs/tests/data/maps/1904-66_QSC.hdr diff --git a/astropy/wcs/tests/maps/1904-66_SFL.hdr b/astropy/wcs/tests/data/maps/1904-66_SFL.hdr similarity index 100% rename from astropy/wcs/tests/maps/1904-66_SFL.hdr rename to astropy/wcs/tests/data/maps/1904-66_SFL.hdr diff --git a/astropy/wcs/tests/maps/1904-66_SIN.hdr b/astropy/wcs/tests/data/maps/1904-66_SIN.hdr similarity index 100% rename from astropy/wcs/tests/maps/1904-66_SIN.hdr rename to astropy/wcs/tests/data/maps/1904-66_SIN.hdr diff --git a/astropy/wcs/tests/maps/1904-66_STG.hdr b/astropy/wcs/tests/data/maps/1904-66_STG.hdr similarity index 100% rename from astropy/wcs/tests/maps/1904-66_STG.hdr rename to astropy/wcs/tests/data/maps/1904-66_STG.hdr diff --git a/astropy/wcs/tests/maps/1904-66_SZP.hdr b/astropy/wcs/tests/data/maps/1904-66_SZP.hdr similarity index 100% rename from astropy/wcs/tests/maps/1904-66_SZP.hdr rename to astropy/wcs/tests/data/maps/1904-66_SZP.hdr diff --git a/astropy/wcs/tests/maps/1904-66_TAN.hdr b/astropy/wcs/tests/data/maps/1904-66_TAN.hdr similarity index 100% rename from astropy/wcs/tests/maps/1904-66_TAN.hdr rename to astropy/wcs/tests/data/maps/1904-66_TAN.hdr diff --git a/astropy/wcs/tests/maps/1904-66_TSC.hdr b/astropy/wcs/tests/data/maps/1904-66_TSC.hdr similarity index 100% rename from astropy/wcs/tests/maps/1904-66_TSC.hdr rename to astropy/wcs/tests/data/maps/1904-66_TSC.hdr diff --git a/astropy/wcs/tests/maps/1904-66_ZEA.hdr b/astropy/wcs/tests/data/maps/1904-66_ZEA.hdr similarity index 100% rename from astropy/wcs/tests/maps/1904-66_ZEA.hdr rename to astropy/wcs/tests/data/maps/1904-66_ZEA.hdr diff --git a/astropy/wcs/tests/maps/1904-66_ZPN.hdr b/astropy/wcs/tests/data/maps/1904-66_ZPN.hdr similarity index 100% rename from astropy/wcs/tests/maps/1904-66_ZPN.hdr rename to astropy/wcs/tests/data/maps/1904-66_ZPN.hdr diff --git a/astropy/wcs/tests/data/nonstandard_units.hdr b/astropy/wcs/tests/data/nonstandard_units.hdr index 680fc88f7cb3..255dbfc69716 100644 --- a/astropy/wcs/tests/data/nonstandard_units.hdr +++ b/astropy/wcs/tests/data/nonstandard_units.hdr @@ -1,16 +1 @@ -CD1_2 = -3.72E-05 -CD1_3 = 0 -CD1_1 = -4.12E-05 -CUNIT3 = 'HZ ' -CUNIT2 = 'deg ' -CTYPE1 = 'RA---TAN' -NAXIS = 3 -CTYPE3 = 'AWAV ' -CD2_1 = -3.72E-05 -CTYPE2 = 'DEC--TAN' -CD2_3 = 0 -CD2_2 = 4.12E-05 -CUNIT1 = 'deg ' -CD3_1 = 0 -CD3_2 = 0 -CD3_3 = 0.2 \ No newline at end of file +CD1_2 = -3.72E-05 CD1_3 = 0 CD1_1 = -4.12E-05 CUNIT3 = 'HZ ' CUNIT2 = 'M/S ' CTYPE1 = 'RA---TAN' NAXIS = 3 CTYPE3 = 'AWAV ' CD2_1 = -3.72E-05 CTYPE2 = 'DEC--TAN' CD2_3 = 0 CD2_2 = 4.12E-05 CUNIT1 = 'deg ' CD3_1 = 0 CD3_2 = 0 CD3_3 = 0.2 END \ No newline at end of file diff --git a/astropy/wcs/tests/data/sip-broken.hdr b/astropy/wcs/tests/data/sip-broken.hdr new file mode 100644 index 000000000000..61e7a74f4130 --- /dev/null +++ b/astropy/wcs/tests/data/sip-broken.hdr @@ -0,0 +1 @@ +XTENSION= 'IMAGE ' / Image extension BITPIX = -32 / array data type NAXIS = 2 / number of array dimensions NAXIS1 = 10 NAXIS2 = 10 PCOUNT = 0 / number of parameters GCOUNT = 1 / number of groups ORIGIN = 'NOAO-IRAF FITS Image Kernel July 2003' / FITS file originator EXTNAME = 'SCI ' / Extension name EXTVER = 1 / Extension version IRAF-TLM= '2010-01-28T21:42:25' / Time of last modification DATE = '2010-01-15T03:23:55' / Date FITS file was generated INHERIT = T / inherit the primary header EXPNAME = 'ibc301qrq ' / exposure identifier BUNIT = 'electrons' / brightness units / CCD CHIP IDENTIFICATION CCDCHIP = 2 / CCD chip (1 or 2) / World Coordinate System and Related Parameters WCSAXES = 2 / number of World Coordinate System axes CRPIX1 = 2048.0 / x-coordinate of reference pixel CRPIX2 = 1026.0 / y-coordinate of reference pixel CRVAL1 = 201.682062444 / first axis value at reference pixel CRVAL2 = -47.46654604529999 / second axis value at reference pixel CTYPE1 = 'RA---TAN-SIP' / the coordinate type for the first axis CTYPE2 = 'DEC--TAN-SIP' / the coordinate type for the second axis CD1_1 = 9.89532021391661E-06 / partial of first axis coordinate w.r.t. x CD1_2 = 5.57454535620407E-06 / partial of first axis coordinate w.r.t. y CD2_1 = 4.96143358219569E-06 / partial of second axis coordinate w.r.t. x CD2_2 = -9.5612017076973E-06 / partial of second axis coordinate w.r.t. y LTV1 = 0.0000000E+00 / offset in X to subsection start LTV2 = 0.0000000E+00 / offset in Y to subsection start LTM1_1 = 1.0 / reciprocal of sampling rate in X LTM2_2 = 1.0 / reciprocal of sampling rate in Y PA_APER = 149.806 / Position Angle of reference aperture center (deVAFACTOR= 1.0 / velocity aberration plate scale factor ORIENTAT= 149.7956191662691 / position angle of image y axis (deg. e of n) RA_APER = 2.016928333333E+02 / RA of aperture reference position DEC_APER= -4.747905555556E+01 / Declination of aperture reference position / REPEATED EXPOSURES INFORMATION NCOMBINE= 1 / number of image sets combined during CR rejecti / PHOTOMETRY KEYWORDS PHOTMODE= 'WFC3 UVIS2 F606W CAL' / observation con PHOTFLAM= 1.1598989E-19 / inverse sensitivity, ergs/cm2/Ang/electron PHOTFNU = 1.3410633E-07 / inverse sensitivity, Jy*sec/electron PHOTZPT = -2.1100000E+01 / ST magnitude zero point PHOTPLAM= 5.8874194E+03 / Pivot wavelength (Angstroms) PHOTBW = 6.5663947E+02 / RMS bandwidth of filter plus detector / READOUT DEFINITION PARAMETERS CENTERA1= 2104 / subarray axis1 center pt in unbinned dect. pix CENTERA2= 1036 / subarray axis2 center pt in unbinned dect. pix SIZAXIS1= 4096 / subarray axis1 size in unbinned detector pixelsSIZAXIS2= 2051 / subarray axis2 size in unbinned detector pixelsBINAXIS1= 1 / axis1 data bin size in unbinned detector pixelsBINAXIS2= 1 / axis2 data bin size in unbinned detector pixels / DATA PACKET INFORMATION FILLCNT = 0 / number of segments containing fill ERRCNT = 0 / number of segments containing errors PODPSFF = F / podps fill present (T/F) STDCFFF = F / science telemetry fill data present (T=1/F=0) STDCFFP = 'x5569 ' / science telemetry fill pattern (hex) / IMAGE STATISTICS AND DATA QUALITY FLAGS NGOODPIX= 8361737 / number of good pixels SDQFLAGS= 31743 / serious data quality flags GOODMIN = -1.5353586E+10 / minimum value of good pixels GOODMAX = 4.4784176E+08 / maximum value of good pixels GOODMEAN= -1.7965331E+03 / mean value of good pixels SNRMIN = -4.0580401E+00 / minimum signal to noise of good pixels SNRMAX = 2.0669971E+02 / maximum signal to noise of good pixels SNRMEAN = 9.1055450E+00 / mean value of signal to noise of good pixels SOFTERRS= 0 / number of soft error pixels (DQF=1) MEANDARK= -9.8821044E-04 / average of the dark values subtracted MEANBLEV= 2.5543987E+03 / average of all bias levels subtracted MEANFLSH= 0.000000 / Mean number of counts in post flash exposure OCX10 = 0.000175023073097690 OCX11 = 0.03978145867586136 OCY10 = 0.03984303399920464 OCY11 = 0.002337893005460501 IDCSCALE= 0.03962 WCSNAMEO= 'OPUS ' WCSAXESO= 2 CRPIX1O = 2048.0 CRPIX2O = 1026.0 CDELT1O = 1 CDELT2O = 1 CUNIT1O = 'deg ' CUNIT2O = 'deg ' CTYPE1O = 'RA---TAN' CTYPE2O = 'DEC--TAN' CRVAL1O = 201.682062444 CRVAL2O = -47.4665460453 LONPOLEO= 180 LATPOLEO= -47.4665460453 RESTFRQO= 0 RESTWAVO= 0 CD1_1O = 9.90756999999999E-06 CD1_2O = 5.55896E-06 CD2_1O = 4.9244E-06 CD2_2O = -9.54957E-06 IDCTAB = 'iref$v5r1512gi_idc.fits' A_3_1 = -5.1021945200133E-16 A_3_0 = 2.01645819721643E-11 B_3_0 = 1.69320438397225E-12 B_3_1 = 1.63251006567400E-15 B_1_2 = -1.4255424755601E-11 B_1_3 = -2.7323406250153E-15 B_1_1 = 2.81503635244692E-06 B_2_1 = 1.84371537198681E-11 B_2_0 = -4.1200147469028E-08 B_2_2 = 1.39530876683604E-14 A_4_0 = 1.93073916740935E-15 A_ORDER = 4 B_0_4 = 7.44136342655120E-15 B_0_3 = 1.35699782198128E-11 B_0_2 = -3.0813836048843E-06 B_ORDER = 4 B_4_0 = 6.75894948156410E-16 A_1_1 = -2.9689459039407E-06 A_1_3 = -2.5561816554973E-15 A_1_2 = 1.61518535366509E-11 A_0_4 = -1.7441112473874E-14 A_0_2 = 9.41762068657988E-08 A_0_3 = 2.11281723091275E-11 A_2_2 = -1.7127070982784E-14 A_2_0 = 2.87050290904523E-06 A_2_1 = -1.4037704327792E-11 IDCTHETA= 45.0 IDCXREF = 2048.0 IDCYREF = 1026.0 IDCV2REF= -27.56800079345703 IDCV3REF= -33.30899810791016 WCSNAMEA= 'IDC_v5r1512gi' WCSAXESA= 2 CRPIX1A = 2048 CRPIX2A = 1026 CDELT1A = 1 CDELT2A = 1 CUNIT1A = 'deg ' CUNIT2A = 'deg ' CTYPE1A = 'RA---TAN-SIP' CTYPE2A = 'DEC--TAN-SIP' CRVAL1A = 201.682062444 CRVAL2A = -47.4665460453 LONPOLEA= 180 LATPOLEA= -47.4665460453 RESTFRQA= 0 RESTWAVA= 0 CD1_1A = 9.89532021392E-06 CD1_2A = 5.5745453562E-06 CD2_1A = 4.9614335822E-06 CD2_2A = -9.5612017077E-06 A_3_1O = -5.1021945200133E-16 A_3_0O = 2.01645819721643E-11 B_3_0O = 1.69320438397225E-12 B_3_1O = 1.632510065674E-15 B_1_2O = -1.4255424755601E-11 B_1_3O = -2.7323406250153E-15 B_1_1O = 2.81503635244692E-06 B_2_1O = 1.84371537198681E-11 B_2_0O = -4.1200147469028E-08 B_2_2O = 1.39530876683604E-14 B_ORDERO= 4 A_ORDERO= 4 B_0_4O = 7.4413634265512E-15 B_0_3O = 1.35699782198128E-11 B_0_2O = -3.0813836048843E-06 A_4_0O = 1.93073916740935E-15 B_4_0O = 6.7589494815641E-16 A_1_1O = -2.9689459039407E-06 A_1_3O = -2.5561816554973E-15 A_1_2O = 1.61518535366509E-11 A_0_4O = -1.7441112473874E-14 A_0_2O = 9.41762068657988E-08 A_0_3O = 2.11281723091275E-11 A_2_2O = -1.7127070982784E-14 A_2_0O = 2.87050290904523E-06 A_2_1O = -1.4037704327792E-11 WCSNAME = 'IDC_v5r1512gi' WCSNAMEB= 'IDC_v5r1512gi' WCSAXESB= 2 CRPIX1B = 2048.0 CRPIX2B = 1026.0 CDELT1B = 1 CDELT2B = 1 CUNIT1B = 'deg ' CUNIT2B = 'deg ' CTYPE1B = 'RA---TAN-SIP' CTYPE2B = 'DEC--TAN-SIP' CRVAL1B = 201.682062444 CRVAL2B = -47.4665460453 LONPOLEB= 180 LATPOLEB= -47.4665460453 RESTFRQB= 0 RESTWAVB= 0 A_3_1B = -5.1021945200133E-16 A_3_0B = 2.01645819721643E-11 B_3_0B = 1.69320438397225E-12 B_3_1B = 1.632510065674E-15 B_1_2B = -1.4255424755601E-11 B_1_3B = -2.7323406250153E-15 B_1_1B = 2.81503635244692E-06 B_2_1B = 1.84371537198681E-11 B_2_0B = -4.1200147469028E-08 B_2_2B = 1.39530876683604E-14 B_ORDERB= 4 A_ORDERB= 4 B_0_4B = 7.4413634265512E-15 B_0_3B = 1.35699782198128E-11 B_0_2B = -3.0813836048843E-06 A_4_0B = 1.93073916740935E-15 B_4_0B = 6.7589494815641E-16 A_1_1B = -2.9689459039407E-06 A_1_3B = -2.5561816554973E-15 A_1_2B = 1.61518535366509E-11 A_0_4B = -1.7441112473874E-14 A_0_2B = 9.41762068657988E-08 A_0_3B = 2.11281723091275E-11 A_2_2B = -1.7127070982784E-14 A_2_0B = 2.87050290904523E-06 A_2_1B = -1.4037704327792E-11 CD1_1B = 9.89532021392E-06 CD1_2B = 5.5745453562E-06 CD2_1B = 4.9614335822E-06 CD2_2B = -9.5612017077E-06 WCSNAMEC= 'TWEAK_A ' WCSAXESC= 2 CRPIX1C = 2048.0 CRPIX2C = 1026.0 CDELT1C = 1 CDELT2C = 1 CUNIT1C = 'deg ' CUNIT2C = 'deg ' CTYPE1C = 'RA---TAN-SIP' CTYPE2C = 'DEC--TAN-SIP' CRVAL1C = 201.682062444 CRVAL2C = -47.4665460453 LONPOLEC= 180 LATPOLEC= -47.4665460453 RESTFRQC= 0 RESTWAVC= 0 A_3_1C = -5.1021945200133E-16 A_3_0C = 2.01645819721643E-11 B_3_0C = 1.69320438397225E-12 B_3_1C = 1.632510065674E-15 B_1_2C = -1.4255424755601E-11 B_1_3C = -2.7323406250153E-15 B_1_1C = 2.81503635244692E-06 B_2_1C = 1.84371537198681E-11 B_2_0C = -4.1200147469028E-08 B_2_2C = 1.39530876683604E-14 B_ORDERC= 4 A_ORDERC= 4 B_0_4C = 7.4413634265512E-15 B_0_3C = 1.35699782198128E-11 B_0_2C = -3.0813836048843E-06 A_4_0C = 1.93073916740935E-15 B_4_0C = 6.7589494815641E-16 A_1_1C = -2.9689459039407E-06 A_1_3C = -2.5561816554973E-15 A_1_2C = 1.61518535366509E-11 A_0_4C = -1.7441112473874E-14 A_0_2C = 9.41762068657988E-08 A_0_3C = 2.11281723091275E-11 A_2_2C = -1.7127070982784E-14 A_2_0C = 2.87050290904523E-06 A_2_1C = -1.4037704327792E-11 CD1_1C = 9.89532021392E-06 CD1_2C = 5.5745453562E-06 CD2_1C = 4.9614335822E-06 CD2_2C = -9.5612017077E-06 FITNAMEC= 'TWEAK_A ' NMATCHC = 0 RMS_RAC = 0.0 RMS_DECC= 0.0 HISTORY The following throughput tables were used: crotacomp$hst_ota_007_syn.fitHISTORY s, crwfc3comp$wfc3_pom_001_syn.fits, crwfc3comp$wfc3_uvis_mir1_002_syn.fHISTORY its, crwfc3comp$wfc3_uvis_mir2_002_syn.fits, crwfc3comp$wfc3_uvis_f606w_HISTORY 002_syn.fits, crwfc3comp$wfc3_uvis_owin_002_syn.fits, crwfc3comp$wfc3_uvHISTORY is_iwin_002_syn.fits, crwfc3comp$wfc3_uvis_ccd2_003_syn.fits, crwfc3compHISTORY $wfc3_uvis_f606wf2_001_syn.fits, crwfc3comp$wfc3_uvis_cor_003_syn.fits END \ No newline at end of file diff --git a/astropy/wcs/tests/data/siponly.hdr b/astropy/wcs/tests/data/siponly.hdr new file mode 100644 index 000000000000..8b33f9363714 --- /dev/null +++ b/astropy/wcs/tests/data/siponly.hdr @@ -0,0 +1 @@ +SIMPLE = T / Fits standard BITPIX = -32 / FOUR-BYTE SINGLE PRECISION FLOATING POINT NAXIS = 2 / STANDARD FITS FORMAT NAXIS1 = 2048 / STANDARD FITS FORMAT NAXIS2 = 4096 / STANDARD FITS FORMAT ORIGIN = 'Palomar Transient Factory' / Origin of these image data CREATOR = 'Infrared Processing and Analysis Center' / Creator of this FITS file TELESCOP= 'P48 ' / Name of telescope INSTRUME= 'PTF/MOSAIC' / Instrument name OBSERVER= 'KulkarniPTF' / Observer name and project CCDID = '5 ' / CCD number (0..11) DATE-OBS= '2014-07-31T04:49:58.673' / UTC shutter time YYYY-MM-DDTHH:MM:SS.SSS DATE = '2014-07-31T19:20:32' / File creation date (YYYY-MM-DDThh:mm:ss UT) REFERENC= 'http://www.astro.caltech.edu/ptf' / URL of PTF website / PROPOSAL INFORMATION PTFPRPI = 'Kulkarni' / PTF Project PI PTFPID = '52002 ' / Project type: 00000-49999 OBJECT = 'Galactic_Plane' / Fields object PTFFIELD= '1549 ' / PTF unique field ID PTFFLAG = '1 ' / 1 = PTF; 0 = non-PTF category / TIME AND EXPOSURE INFORMATION FILTER = 'R ' / Filter name FILTERID= '2 ' / Filter ID FILTERSL= '1 ' / Filter changer slot position EXPTIME = 60. / [s] Requested exposure time AEXPTIME= 60. / actual exposure time (sec) UTC-OBS = '2014-07-31T04:49:58.673' / UTC time shutter open YYYY-MM-DDTHH:MM:SS.OBSJD = 2456869.70137 / [day] Julian day corresponds to UTC-OBS OBSMJD = 56869.20137 / MJD corresponds to UTC-OBS (day) OBSLST = '17:37:29.36' / Mean LST corresponds to UTC-OBS 'HH:MM:SS.S' HOURANG = '-0:42:20.82' / Mean HA (sHH:MM:SS.S) based on LMST at UTC-OBS HJD = 2456869.70618 / [day] Heliocentric Julian Day OBSTYPE = 'object ' / Image type (dark,science,bias,focus) IMGTYP = 'object ' / Image type (dark,science,bias,focus) / MOON AND SUN MOONRA = 173.116974 / [deg] Moon J2000.0 R.A. MOONDEC = -0.404999 / [deg] Moon J2000.0 Dec. MOONILLF= 0.155837 / [frac] Moon illuminated fraction MOONPHAS= 133.4978 / [deg] Moon phase angle MOONESB = -0. / Moon excess in sky brightness V-band MOONALT = -1.271649 / [deg] Moon altitude SUNAZ = 312.4731 / [deg] Sun azimuth SUNALT = -22.25475 / [deg] Sun altitude / PHOTOMETRY BUNIT = 'DN ' / Data number (analog-to-digital units or ADU) PHTCALEX= 1 / Was phot.-cal. module executed? PHTCALFL= 0 / Flag for image is photometric (0=N, 1=Y) PCALRMSE= 0.171135 / RMSE from (zeropoint, extinction) data fit IMAGEZPT= 21.22791 / Image magnitude zeropoint IZPORIG = 'CALTRANS' / Photometric-calibration origin ZPRULE = 'COMPUTE ' / Photometric-calibration method MAGZPT = 23.55079 / Magnitude zeropoint at airmass=1 EXTINCT = 1.163014 / Extinction APSFILT = 'r ' / SDSS filter used in abs phot cal APSCOL = 'r-i ' / SDSS color used in abs phot cal APRMS = 0.06677995 / RMS in mag of final abs phot cal APBSRMS = 0.05033556 / RMS in mag of final abs phot cal for bright staAPNSTDI1= 308233 / Number of standard stars in first iteration APNSTDIF= 274569 / Number of standard stars in final iteration APCHI2 = 1861590.34570444 / Chi2 of final abs phot cal APDOF = 274569. / Dof of chi2 of final abs phot cal APMEDJD = 2456869.84882722 / Median JD used in abs phot cal APPN01 = 'ZeroPoint' / Name of parameter abs phot cal 01 APPAR01 = 23.67499643 / Value of parameter abs phot cal 01 APPARE01= 0.00324545 / Error of parameter abs phot cal 01 APPN02 = 'ColorTerm' / Name of parameter abs phot cal 02 APPAR02 = 0.44908632 / Value of parameter abs phot cal 02 APPARE02= 0.00423336 / Error of parameter abs phot cal 02 APPN03 = 'AirMassTerm' / Name of parameter abs phot cal 03 APPAR03 = -0.18342823 / Value of parameter abs phot cal 03 APPARE03= 0.00288243 / Error of parameter abs phot cal 03 APPN04 = 'AirMassColorTerm' / Name of parameter abs phot cal 04 APPAR04 = -0.14534473 / Value of parameter abs phot cal 04 APPARE04= 0.00381178 / Error of parameter abs phot cal 04 APPN05 = 'TimeTerm' / Name of parameter abs phot cal 05 APPAR05 = 0.42239539 / Value of parameter abs phot cal 05 APPARE05= 0.0019644 / Error of parameter abs phot cal 05 APPN06 = 'Time2Term' / Name of parameter abs phot cal 06 APPAR06 = 0.16770061 / Value of parameter abs phot cal 06 APPARE06= 0.01549427 / Error of parameter abs phot cal 06 APPN07 = 'XTerm ' / Name of parameter abs phot cal 07 APPAR07 = 0.02152189 / Value of parameter abs phot cal 07 APPARE07= 0.00047932 / Error of parameter abs phot cal 07 APPN08 = 'YTerm ' / Name of parameter abs phot cal 08 APPAR08 = 0.02739724 / Value of parameter abs phot cal 08 APPARE08= 0.00117248 / Error of parameter abs phot cal 08 APPN09 = 'Y2Term ' / Name of parameter abs phot cal 09 APPAR09 = 0.01522565 / Value of parameter abs phot cal 09 APPARE09= 0.0018581 / Error of parameter abs phot cal 09 APPN10 = 'Y3Term ' / Name of parameter abs phot cal 10 APPAR10 = -0.23390906 / Value of parameter abs phot cal 10 APPARE10= 0.00723349 / Error of parameter abs phot cal 10 APPN11 = 'XYTerm ' / Name of parameter abs phot cal 11 APPAR11 = -0.00677493 / Value of parameter abs phot cal 11 APPARE11= 0.00169149 / Error of parameter abs phot cal 11 / ASTROMETRY CRVAL1 = 274.806945708898 / [deg] RA of reference point CRVAL2 = -25.9746476963393 / [deg] DEC of reference point CRPIX1 = -3925.16 / [pix] Image reference point CRPIX2 = 4360.23 / [pix] Image reference point CTYPE1 = 'RA---TAN-SIP' / TAN (gnomic) projection + SIP distortions CTYPE2 = 'DEC--TAN-SIP' / TAN (gnomic) projection + SIP distortions CUNIT1 = 'deg ' / Image axis-1 celestial-coordinate units CUNIT2 = 'deg ' / Image axis-2 celestial-coordinate units CRTYPE1 = 'deg ' / Data units of CRVAL1 CRTYPE2 = 'deg ' / Data units of CRVAL2 CD1_1 = 0.000286102658601581 / Transformation matrix CD1_2 = -6.28816628331811E-07 CD2_1 = -5.77207018114522E-06 CD2_2 = -0.000281525256171892 OBJRA = '18:18:56.842' / Requested field J2000.0 Ra. OBJDEC = '-25:52:30.00' / Requested field J2000.0 Dec. OBJRAD = 274.73684 / [deg] Requested field RA (J2000.0) OBJDECD = -25.875 / [deg] Requested field Dec (J2000.0) PIXSCALE= 1.01 / [arcsec/pix] Pixel scale EQUINOX = 2000. / [yr] Equatorial coordinates definition / IMAGE QUALITY SEEING = 2.95 / [pix] Seeing FWHM PEAKDIST= 0.481336680505667 / [pix] Mean dist brightest pixel-centroid pixel ELLIP = 0.313 / Mean image ellipticity A/B ELLIPPA = 48.58 / [deg] Mean image ellipticity PA FBIAS = 785.8855 / [DN] Floating bias of the image SATURVAL= 50000. / [DN] Saturation value of the CCD array FWHMSEX = 2.45 / [arcsec] SExtractor SEEING estimate MSMAPCZP= 19.20814 / [mag/s-arcsec^2] Median sky abs. phot. cal. LMGAPCZP= 20.68008 / [mag/s-arcsec^2] Limiting mag. abs. phot. cal. MEDFWHM = 3.417924 / [arcsecond] Median FWHM MEDELONG= 1.406608 / [dimensionless] Median elongation STDELONG= 0.592749 / [dimensionless] Std. dev. of elongation MEDTHETA= -31.22347 / [deg] Atan(median sin(theta)/median cos(theta))STDTHETA= 65.37225 / [deg] Atan(stddev sin(theta)/stddev cos(theta))MEDDLMAG= 2.444709 / [mag/s-arcsec^2] Median (MU_MAX-MAG_AUTO) STDDLMAG= 0.4154117 / [mag/s-arcsec^2] Stddev of (MU_MAX-MAG_AUTO) / OBSERVATORY AND TCS OCS_TIME= '2014-07-31T04:49:58.613' / UTC Date for OCS calc time-dep params OPERMODE= 'OCS ' / Mode of operation: OCS | Manual | N/A SOFTVER = '1.1.1.1 ' / Softwere version (TCS.Camera.OCS.Sched) OCS_VER = '1 ' / OCS software version and date TCS_VER = '1 ' / TCS software version and date SCH_VER = '1 ' / OCS-Scheduler software version and date MAT_VER = '7.7.0.471' / Matlab version HDR_VER = '1 ' / Header version TRIGGER = 'N/A ' / trigger ID for TOO, e.g. VOEVENT-Nr TCSMODE = 'Star ' / TCS fundamental mode TCSSMODE= 'Active ' / TCS fundamental submode TCSFMODE= 'Pos ' / TCS focus mode TCSFSMOD= 'On-Target' / TCS focus submode TCSDMODE= 'Stop ' / TCS dome mode TCSDSMOD= 'N/A ' / TCS dome submode TCSWMODE= 'Slave ' / TCS windscreen mode TCSWSMOD= 'N/A ' / TCS windscreen submode OBSLAT = 33.3574 / [deg] Telescope geodetic latitude in WGS84 OBSLON = -116.8599 / [deg] Telescope geodetic longitude in WGS84 OBSALT = 1703.2 / [m] Telescope geodetic altitude in WGS84 DEFOCUS = 0. / [mm] Focus position - nominal focus FOCUSPOS= 1.3655 / [mm] Exposures focusPos DOMESTAT= 'open ' / Dome status at begining of exposure TRACKRA = 20.4 / [arcsec/hr] Track speed RA rel to sidereal TRACKDEC= -3.9 / [arcsec/hr] Track speed Dec rel to sidereal AZIMUTH = 169.2328 / [deg] Telescope Azimuth ALTITUDE= 29.95342 / [deg] Telescope altitude AIRMASS = 1.997293 / Telescope airmass TELRA = 274.9591 / [deg] Telescope ap equinox of date RA TELDEC = -25.8684 / [deg] Telescope ap equinox of date Dec TELHA = 349.4138 / [deg] Telescope ap equinox of date HA DOMEAZ = 169.4477 / [deg] Dome azimuth WINDSCAL= 12.8995 / [deg] Wind screen altitude WINDDIR = 1.3 / [deg] Azimuth of wind direction WINDSPED= 14.472 / Wind speed (km/hour) OUTTEMP = 22.16667 / [C] Outside temperature OUTRELHU= 0.513 / [frac] Outside relative humidity OUTDEWPT= 11.61111 / [C] Outside dew point / INSTRUMENT TELEMETRY PANID = '_p48m ' / PAN identification DHSID = '_p48m ' / DHS identification CCDSEC = '[1:2048,1:4096]' / CCD section CCDSIZE = '[1:2048,1:4096]' / CCD size DATASEC = '[1:2048,1:4096]' / Data section DETSEC = '[1:2048,1:4096]' / Detector section ROISEC = '[1:2048,1:4096]' / ROI section FPA = 'P48MOSAIC' / Focal plan array CCDNAME = 'W53C2 ' / Detector mfg serial number CHECKSUM= 'fGoXhEmXfEmXfEmX' / Image header unit checksum DATASUM = '2019013917' / Image data unit checksum DHEINF = 'SDSU, Gen-III' / Controller info DHEFIRM = '/usr/src/dsp/20090618/tim_m.lod' / DSP software CAM_VER = '20090615.1.3.100000' / Camera server date.rev.cfitsio LV_VER = '8.5 ' / LabVIEW software version PCI_VER = '2.0c ' / Astropci software version DETID = 'PTF/MOSAIC' / Detector ID AUTHOR = 'PTF/OCS/TCS/Camera' / Source for header information DATAMIN = 0. / Minimum value for array ROISTATE= 'ROI ' / ROI State (FULL | ROI) LEDBLUE = 'OFF ' / 470nm LED state (ON | OFF) LEDRED = 'OFF ' / 660nm LED state (ON | OFF) LEDNIR = 'OFF ' / 880nm LED state (ON | OFF) CCD9TEMP= 174.988 / [K] 0x0 servo temp sensor on CCD09 HSTEMP = 152.111 / [K] 0x1 heat spreader temp DHE0TEMP= 301.098 / [K] 0x2 detector head electronics temp, master DHE1TEMP= 303.178 / [K] 0x3 detector head electronics temp, slave DEWWTEMP= 287.05 / [K] 0x4 dewar wall temp HEADTEMP= 142.103 / [K] 0x5 cryo cooler cold head temp CCD5TEMP= 175.963 / [K] 0x6 temp sensor on CCD05 CCD11TEM= 177.375 / [K] 0x7 temp sensor on CCD11 CCD0TEMP= 170.213 / [K] 0x8 temp sensor on CCD00 RSTEMP = 238.936 / [K] 0x9 temp sensor on radiation shield DEWPRESS= 40. / [milli-torr] Dewar pressure DETHEAT = 1.6 / [%] Detector focal plane heater power NAMPSXY = '6 2 ' / Number of amplifiers in x y CCDSUM = '1 1 ' / [pix] Binning in x and y MODELFOC= 'N/A ' / MODELFOC EXPCKSUM= 'fGoXhEmXfEmXfEmX' / Primary header unit checksum EXPDTSUM= '2019013917' / Primary data unit checksum GAIN = 1.7 / [e-/D.N.] Gain of detector. READNOI = 3.4 / [e-] Read noise of detector. DARKCUR = 0.1 / [e-/s] Dark current of detector / SCAMP DISTORTION KEYWORDS RADECSYS= 'ICRS ' / Astrometric system FGROUPNO= 1 / SCAMP field group label ASTIRMS1= 0. / Astrom. dispersion RMS (intern., high S/N) ASTIRMS2= 0. / Astrom. dispersion RMS (intern., high S/N) ASTRRMS1= 2.362887E-05 / Astrom. dispersion RMS (ref., high S/N) ASTRRMS2= 2.36868E-05 / Astrom. dispersion RMS (ref., high S/N) ASTINST = 1 / SCAMP astrometric instrument label FLXSCALE= 0. / SCAMP relative flux scale MAGZEROP= 0. / SCAMP zero-point PHOTIRMS= 0. / mag dispersion RMS (internal, high S/N) RA_RMS = 0.1040474 / [arcsec] RMS of SCAMP fit from 2MASS matching DEC_RMS = 0.1017731 / [arcsec] RMS of SCAMP fit from 2MASS matching ASTROMN = 2636 / Number of stars in SCAMP astrometric solution SCAMPPTH= 'NotAvailable' / SCAMP catalog path SCAMPFIL= 'NotAvailable' / SCAMP catalog file / SIP DISTORTION KEYWORDS A_ORDER = 4 / Distortion order for A A_0_2 = -6.88320772436348E-08 / Projection distortion parameter A_0_3 = -3.9165520771852E-11 / Projection distortion parameter A_0_4 = -1.37347903340862E-15 / Projection distortion parameter A_1_1 = 1.47451309698268E-06 / Projection distortion parameter A_1_2 = -5.47895978084324E-11 / Projection distortion parameter A_1_3 = 4.32571760220798E-15 / Projection distortion parameter A_2_0 = -4.61014380203131E-06 / Projection distortion parameter A_2_1 = -3.25701227339755E-10 / Projection distortion parameter A_2_2 = 5.87253012315133E-15 / Projection distortion parameter A_3_0 = 5.10801798928538E-10 / Projection distortion parameter A_3_1 = 2.40245891539354E-14 / Projection distortion parameter A_4_0 = -2.24100384816689E-14 / Projection distortion parameter A_DMAX = 96.5018681533569 / Projection distortion parameter B_ORDER = 4 / Distortion order for B B_0_2 = 2.10619568626298E-07 / Projection distortion parameter B_0_3 = -5.06225421390773E-12 / Projection distortion parameter B_0_4 = 5.17539616845577E-16 / Projection distortion parameter B_1_1 = 5.91465601878924E-08 / Projection distortion parameter B_1_2 = -5.12374109506712E-11 / Projection distortion parameter B_1_3 = -1.85594858389364E-15 / Projection distortion parameter B_2_0 = -6.29264904991201E-06 / Projection distortion parameter B_2_1 = -6.77151075883653E-11 / Projection distortion parameter B_2_2 = 3.33079437463431E-15 / Projection distortion parameter B_3_0 = 8.62409953895856E-10 / Projection distortion parameter B_3_1 = 4.00773353822356E-15 / Projection distortion parameter B_4_0 = -4.38536973214709E-14 / Projection distortion parameter B_DMAX = 95.5403565807527 / Projection distortion parameter AP_ORDER= 4 / Distortion order for AP AP_0_1 = -8.35636338195056E-06 / Projection distortion parameter AP_0_2 = 6.41919370738511E-08 / Projection distortion parameter AP_0_3 = 3.82575929801279E-11 / Projection distortion parameter AP_0_4 = 1.34695941598154E-15 / Projection distortion parameter AP_1_0 = 5.96496231342059E-06 / Projection distortion parameter AP_1_1 = -1.47551597085589E-06 / Projection distortion parameter AP_1_2 = 5.9682183111397E-11 / Projection distortion parameter AP_1_3 = -4.07431356348696E-15 / Projection distortion parameter AP_2_0 = 4.63912798241099E-06 / Projection distortion parameter AP_2_1 = 3.2299095062767E-10 / Projection distortion parameter AP_2_2 = -6.30497770263709E-15 / Projection distortion parameter AP_3_0 = -5.03018200403734E-10 / Projection distortion parameter AP_3_1 = -2.35315281608906E-14 / Projection distortion parameter AP_4_0 = 2.13944449467657E-14 / Projection distortion parameter BP_ORDER= 4 / Distortion order for BP BP_0_1 = -5.35341799909328E-06 / Projection distortion parameter BP_0_2 = -2.17548011382368E-07 / Projection distortion parameter BP_0_3 = 3.46141884836402E-12 / Projection distortion parameter BP_0_4 = -5.50225060304376E-16 / Projection distortion parameter BP_1_0 = 2.24406272193445E-05 / Projection distortion parameter BP_1_1 = -5.78663264876419E-08 / Projection distortion parameter BP_1_2 = 5.4445150144274E-11 / Projection distortion parameter BP_1_3 = 2.29942375548271E-15 / Projection distortion parameter BP_2_0 = 6.33813482510013E-06 / Projection distortion parameter BP_2_1 = 5.99623548359284E-11 / Projection distortion parameter BP_2_2 = -3.3053766170687E-15 / Projection distortion parameter BP_3_0 = -8.55094688760831E-10 / Projection distortion parameter BP_3_1 = -2.73327308556661E-15 / Projection distortion parameter BP_4_0 = 4.26704732113368E-14 / Projection distortion parameter / DATA FLOW ORIGNAME= '/data/PTF_default_37806.fits' / Filename as written by the camera FILENAME= 'PTF201407312014_2_o_37806.fits' / Filename of delivered camera image PROCORIG= 'IPAC-PTF pipelines' / Processing origin PROCDATE= 'Fri Sep 26 14:52:55 2014' / Processing date/time (Pacific time) PTFVERSN= 5. / Version of PTFSCIENCEPIPELINE program PMASKPTH= '/ptf/pos/archive/fallbackcal/pmasks/' / Pathname of pixel mask PMASKFIL= '70sOn35s_pixmask_chip5.trimmed.v4.fits' / Filename of pixel mask SFLATPTH= '/ptf/pos/sbx2/2014/07/31/f2/c5/cal/p4/cId112103/' / Pathname of superSFLATFIL= 'PTF_201407310000_i_s_flat_t120000_u000112103_f02_p000000_c05.fits' SBIASPTH= '/ptf/pos/sbx2/2014/07/31/f2/c5/cal/p1/cId112095/' / Pathname of superSBIASFIL= 'PTF_201407310000_i_s_bias_t120000_u000112095_f00_p000000_c05.fits' DBNID = 1938 / Database night ID DBEXPID = 446050 / Database exposure ID DBRID = 6985566 / Database raw-image ID DBPID = 21528832 / Database processed-image ID DBFID = 2 / Database filter ID DBPIID = 1 / Database P.I. ID DBPRID = 31 / Database project ID DBFIELD = 446050 / Database field ID DBSVID = 54 / Database software-version ID DBCVID = 60 / Database config-data-file ID INFOBITS= 0 / Database infobits (2^2 and 2^3 excluded) END \ No newline at end of file diff --git a/astropy/wcs/tests/spectra/orion-freq-1.hdr b/astropy/wcs/tests/data/spectra/orion-freq-1.hdr similarity index 100% rename from astropy/wcs/tests/spectra/orion-freq-1.hdr rename to astropy/wcs/tests/data/spectra/orion-freq-1.hdr diff --git a/astropy/wcs/tests/spectra/orion-freq-4.hdr b/astropy/wcs/tests/data/spectra/orion-freq-4.hdr similarity index 100% rename from astropy/wcs/tests/spectra/orion-freq-4.hdr rename to astropy/wcs/tests/data/spectra/orion-freq-4.hdr diff --git a/astropy/wcs/tests/spectra/orion-velo-1.hdr b/astropy/wcs/tests/data/spectra/orion-velo-1.hdr similarity index 100% rename from astropy/wcs/tests/spectra/orion-velo-1.hdr rename to astropy/wcs/tests/data/spectra/orion-velo-1.hdr diff --git a/astropy/wcs/tests/spectra/orion-velo-4.hdr b/astropy/wcs/tests/data/spectra/orion-velo-4.hdr similarity index 100% rename from astropy/wcs/tests/spectra/orion-velo-4.hdr rename to astropy/wcs/tests/data/spectra/orion-velo-4.hdr diff --git a/astropy/wcs/tests/spectra/orion-wave-1.hdr b/astropy/wcs/tests/data/spectra/orion-wave-1.hdr similarity index 100% rename from astropy/wcs/tests/spectra/orion-wave-1.hdr rename to astropy/wcs/tests/data/spectra/orion-wave-1.hdr diff --git a/astropy/wcs/tests/spectra/orion-wave-4.hdr b/astropy/wcs/tests/data/spectra/orion-wave-4.hdr similarity index 100% rename from astropy/wcs/tests/spectra/orion-wave-4.hdr rename to astropy/wcs/tests/data/spectra/orion-wave-4.hdr diff --git a/astropy/wcs/tests/data/sub-segfault.hdr b/astropy/wcs/tests/data/sub-segfault.hdr new file mode 100644 index 000000000000..92a00401d822 --- /dev/null +++ b/astropy/wcs/tests/data/sub-segfault.hdr @@ -0,0 +1,28 @@ +WCSAXES = 4 / Number of coordinate axes +CRPIX1 = 8193 / Pixel coordinate of reference point +CRPIX2 = 8193 / Pixel coordinate of reference point +CRPIX3 = 1 / Pixel coordinate of reference point +CRPIX4 = 1 / Pixel coordinate of reference point +CDELT1 = -0.000555555555556 / [deg] Coordinate increment at reference point +CDELT2 = 0.000555555555556 / [deg] Coordinate increment at reference point +CDELT3 = 1 / Coordinate increment at reference point +CDELT4 = 33333332 / [Hz] Coordinate increment at reference point +CUNIT1 = 'deg' / Units of coordinate increment and value +CUNIT2 = 'deg' / Units of coordinate increment and value +CUNIT4 = 'Hz' / Units of coordinate increment and value +CTYPE1 = 'RA---SIN' / Right ascension, orthographic/synthesis project +CTYPE2 = 'DEC--SIN' / Declination, orthographic/synthesis projection +CTYPE3 = 'STOKES' / Coordinate type code +CTYPE4 = 'FREQ' / Frequency (linear) +CRVAL1 = 35.3324166667 / [deg] Coordinate value at reference point +CRVAL2 = -4.51685555556 / [deg] Coordinate value at reference point +CRVAL3 = 1 / Coordinate value at reference point +CRVAL4 = 322601561.836 / [Hz] Coordinate value at reference point +LONPOLE = 180 / [deg] Native longitude of celestial pole +LATPOLE = -4.51685555556 / [deg] Native latitude of celestial pole +RESTFRQ = 306000000 / [Hz] Line rest frequency +RESTWAV = 0 / [Hz] Line rest wavelength +EQUINOX = 2000 / [yr] Equinox of equatorial coordinates +SPECSYS = 'TOPOCENT' / Reference frame of spectral coordinates +MJD-OBS = 55794 / [d] MJD of observation matching DATE-OBS +DATE-OBS= '2011-08-21T00:00:00.000000' / ISO-8601 observation date matching MJD- \ No newline at end of file diff --git a/astropy/wcs/tests/data/tab-time-last-axis.fits b/astropy/wcs/tests/data/tab-time-last-axis.fits new file mode 100644 index 000000000000..b70fb692775a Binary files /dev/null and b/astropy/wcs/tests/data/tab-time-last-axis.fits differ diff --git a/astropy/wcs/tests/data/too_many_pv.hdr b/astropy/wcs/tests/data/too_many_pv.hdr new file mode 100644 index 000000000000..eb1a495fe98f --- /dev/null +++ b/astropy/wcs/tests/data/too_many_pv.hdr @@ -0,0 +1 @@ +SIMPLE = T / Fits standard BITPIX = 16 / FOUR-BYTE SINGLE PRECISION FLOATING POINT NAXIS = 2 / STANDARD FITS FORMAT NAXIS1 = 2048 / STANDARD FITS FORMAT NAXIS2 = 4096 / STANDARD FITS FORMAT ORIGIN = 'Palomar Transient Factory' / Origin of these image data CREATOR = 'Infrared Processing and Analysis Center' / Creator of this FITS file DATE = '2011-08-01T15:14:04' / File creation date (YYYY-MM-DDThh:mm:ss UT) / PTF DMASK BIT DEFINITIONS BIT00 = 0 / AIRCRAFT/SATELLITE TRACK BIT01 = 1 / OBJECT (detected by SExtractor) BIT02 = 2 / HIGH DARK-CURRENT BIT03 = 3 / RESERVED FOR FUTURE USE BIT04 = 4 / NOISY BIT05 = 5 / GHOST BIT06 = 6 / CCD BLEED BIT07 = 7 / RAD HIT BIT08 = 8 / SATURATED BIT09 = 9 / DEAD/BAD BIT10 = 10 / NAN (not a number) BIT11 = 11 / DIRTY (10-sigma below coarse local median) BIT12 = 12 / HALO BIT13 = 13 / RESERVED FOR FUTURE USE BIT14 = 14 / RESERVED FOR FUTURE USE BIT15 = 15 / RESERVED FOR FUTURE USE / DATA FLOW PMASKPTH= '/ptf/pos/archive/fallbackcal/pmasks/' / Pixel-mask pathname PMASKFIL= 'bpm_2009060s_c07.v2.fits' / Pixel-mask filename UDMPROC = 'updatemask' / Update bit in dmask UDMVERSN= 1. / Version of updatemask program UDMOP = 0 / Operation type UDMMBT = 4 / Mask bit template UDMIFIL = 'bpm_2009060s_c07.v1.fits' / Program updatemask input file UDMOFIL = 'bpm_2009060s_c07.v2.fits' / Program updatemask output file MCVERSN = 1. / Version of ptfMaskCombine program PTFPPROC= 'ptfPostProc' / Flags proc NaNs, CCD-bleeds and rad hits PPRVERSN= 3. / Version of ptfPostProc program / COPY OF IMAGE HEADER BELOW ORIGIN = 'Palomar Transient Factory' / Origin of these image data CREATOR = 'Infrared Processing and Analysis Center' / Creator of this FITS file TELESCOP= 'P48 ' / Name of telescope INSTRUME= 'PTF/MOSAIC' / Instrument name OBSERVER= 'KulkarniPTF' / Observer name and project CCDID = '7 ' / CCD number (0..11) DATE-OBS= '2009-06-25T08:41:23.970' / UTC shutter time YYYY-MM-DDTHH:MM:SS.SSS DATE = '2011-07-30T15:04:59' / File creation date (YYYY-MM-DDThh:mm:ss UT) REFERENC= 'http://www.astro.caltech.edu/ptf' / URL of PTF website / PROPOSAL INFORMATION PTFPRPI = 'Kulkarni' / PTF Project PI PTFPID = '20000 ' / Project type: 00000-49999 OBJECT = 'PTF_survey' / Fields object PTFFIELD= '2899 ' / PTF unique field ID PTFFLAG = '1 ' / 1 = PTF; 0 = non-PTF category / TIME AND EXPOSURE INFORMATION FILTER = 'R ' / Filter name FILTERID= '2 ' / Filter ID FILTERSL= '2 ' / Filter changer slot position EXPTIME = 60. / [s] Requested exposure time AEXPTIME= 60. / actual exposure time (sec) UTC-OBS = '2009-06-25T08:41:23.970' / UTC time shutter open YYYY-MM-DDTHH:MM:SS.OBSJD = 2455007.86207 / [day] Julian day corresponds to UTC-OBS OBSMJD = 55007.36207 / MJD corresponds to UTC-OBS (day) OBSLST = '19:08:26.70' / Mean LST corresponds to UTC-OBS 'HH:MM:SS.S' HOURANG = '-3:09:09.78' / Mean HA (sHH:MM:SS.S) based on LMST at UTC-OBS HJD = 2455007.86457 / [day] Heliocentric Julian Day OBSTYPE = 'object ' / Image type (dark,science,bias,focus) IMGTYP = 'object ' / Image type (dark,science,bias,focus) / MOON AND SUN MOONRA = 131.676395 / [deg] Moon J2000.0 R.A. MOONDEC = 16.207046 / [deg] Moon J2000.0 Dec. MOONILLF= 0.095389 / [frac] Moon illuminated fraction MOONPHAS= 144.0201 / [deg] Moon phase angle MOONESB = -0. / Moon excess in sky brightness V-band MOONALT = -35.16959 / [deg] Moon altitude SUNAZ = 13.90467 / [deg] Sun azimuth SUNALT = -31.96011 / [deg] Sun altitude / PHOTOMETRY BUNIT = 'DN ' / Data number (analog-to-digital units or ADU) PHTCALEX= 1 / Was phot.-cal. module executed? PHTCALFL= 0 / Flag for image is photometric (0=N, 1=Y) PCALRMSE= 0.030059 / RMSE from (zeropoint, extinction) data fit IMAGEZPT= 22.43948 / Image magnitude zeropoint IZPORIG = 'CALTRANS' / Photometric-calibration origin ZPRULE = 'COMPUTE ' / Photometric-calibration method MAGZPT = 22.73683 / Magnitude zeropoint at airmass=1 EXTINCT = 0.17995 / Extinction APSFILT = 'r ' / SDSS filter used in abs phot cal APSCOL = 'r-i ' / SDSS color used in abs phot cal APRMS = 0.0554857 / RMS in mag of final abs phot cal APBSRMS = 0.03911367 / RMS in mag of final abs phot cal for bright staAPNSTDI1= 69501 / Number of standard stars in first iteration APNSTDIF= 64858 / Number of standard stars in final iteration APCHI2 = 325618.88012443 / Chi2 of final abs phot cal APDOF = 64858. / Dof of chi2 of final abs phot cal APMEDJD = 2455007.84500722 / Median JD used in abs phot cal APPN01 = 'ZeroPoint' / Name of parameter abs phot cal 01 APPAR01 = 22.81878918 / Value of parameter abs phot cal 01 APPARE01= 0.00198923 / Error of parameter abs phot cal 01 APPN02 = 'ColorTerm' / Name of parameter abs phot cal 02 APPAR02 = 0.20481246 / Value of parameter abs phot cal 02 APPARE02= 0.00278076 / Error of parameter abs phot cal 02 APPN03 = 'AirMassTerm' / Name of parameter abs phot cal 03 APPAR03 = -0.12104427 / Value of parameter abs phot cal 03 APPARE03= 0.0011621 / Error of parameter abs phot cal 03 APPN04 = 'AirMassColorTerm' / Name of parameter abs phot cal 04 APPAR04 = 0.00904321 / Value of parameter abs phot cal 04 APPARE04= 0.00176968 / Error of parameter abs phot cal 04 APPN05 = 'TimeTerm' / Name of parameter abs phot cal 05 APPAR05 = 0.03012546 / Value of parameter abs phot cal 05 APPARE05= 0.00459951 / Error of parameter abs phot cal 05 APPN06 = 'Time2Term' / Name of parameter abs phot cal 06 APPAR06 = 1.27460327 / Value of parameter abs phot cal 06 APPARE06= 0.07646497 / Error of parameter abs phot cal 06 APPN07 = 'XTerm ' / Name of parameter abs phot cal 07 APPAR07 = 0.00768843 / Value of parameter abs phot cal 07 APPARE07= 0.00083226 / Error of parameter abs phot cal 07 APPN08 = 'YTerm ' / Name of parameter abs phot cal 08 APPAR08 = 0.06680527 / Value of parameter abs phot cal 08 APPARE08= 0.00203667 / Error of parameter abs phot cal 08 APPN09 = 'Y2Term ' / Name of parameter abs phot cal 09 APPAR09 = 0.31486016 / Value of parameter abs phot cal 09 APPARE09= 0.00318862 / Error of parameter abs phot cal 09 APPN10 = 'Y3Term ' / Name of parameter abs phot cal 10 APPAR10 = 0.69934253 / Value of parameter abs phot cal 10 APPARE10= 0.01257477 / Error of parameter abs phot cal 10 APPN11 = 'XYTerm ' / Name of parameter abs phot cal 11 APPAR11 = -0.04590337 / Value of parameter abs phot cal 11 APPARE11= 0.00286729 / Error of parameter abs phot cal 11 / ASTROMETRY CRVAL1 = 333.443801401309 / [deg] RA of reference point CRVAL2 = 3.08905544069643 / [deg] DEC of reference point CRPIX1 = 1175.019 / [pix] Image reference point CRPIX2 = 945.8826 / [pix] Image reference point CTYPE1 = 'RA---TAN-SIP' / TAN (gnomic) projection + SIP distortions CTYPE2 = 'DEC--TAN-SIP' / TAN (gnomic) projection + SIP distortions CUNIT1 = 'deg ' / Image axis-1 celestial-coordinate units CUNIT2 = 'deg ' / Image axis-2 celestial-coordinate units CRTYPE1 = 'deg ' / Data units of CRVAL1 CRTYPE2 = 'deg ' / Data units of CRVAL2 CD1_1 = 0.000281094342514378 / Transformation matrix CD1_2 = -5.00875320999652E-09 CD2_1 = -2.08930602680508E-07 CD2_2 = -0.000281284158795544 OBJRA = '22:17:08.571' / Requested field J2000.0 Ra. OBJDEC = '+03:22:30.00' / Requested field J2000.0 Dec. OBJRAD = 334.285714 / [deg] Requested field RA (J2000.0) OBJDECD = 3.375 / [deg] Requested field Dec (J2000.0) PIXSCALE= 1.01 / [arcsec/pix] Pixel scale WCSAXES = 2 EQUINOX = 2000. / [yr] Equatorial coordinates definition LONPOLE = 180. LATPOLE = 0. / IMAGE QUALITY SEEING = 2.04 / [pix] Seeing FWHM PEAKDIST= 0.396793397122491 / [pix] Mean dist brightest pixel-centroid pixel ELLIP = 0.063 / Mean image ellipticity A/B ELLIPPA = 48.65 / [deg] Mean image ellipticity PA FBIAS = 1060.884 / [DN] Floating bias of the image SATURVAL= 17000. / [DN] Saturation value of the CCD array FWHMSEX = 2.45 / [arcsec] SExtractor SEEING estimate MDSKYMAG= 20.53072 / [mag/s-arcsec^2] Median sky obsolete MSMAPCZP= 20.70926 / [mag/s-arcsec^2] Median sky abs. phot. cal. LIMITMAG= 20.92587 / [mag/s-arcsec^2] Limiting magnitude obsolete LMGAPCZP= 21.10442 / [mag/s-arcsec^2] Limiting mag. abs. phot. cal. MEDFWHM = 2.931446 / [arcsecond] Median FWHM MEDELONG= 1.132416 / [dimensionless] Median elongation STDELONG= 0.3298569 / [dimensionless] Std. dev. of elongation MEDTHETA= -42.28234 / [deg] Atan(median sin(theta)/median cos(theta))STDTHETA= 66.21399 / [deg] Atan(stddev sin(theta)/stddev cos(theta))MEDDLMAG= 33.85928 / [mag/s-arcsec^2] Median (MU_MAX-MAG_AUTO) STDDLMAG= 0.4367887 / [mag/s-arcsec^2] Stddev of (MU_MAX-MAG_AUTO) / OBSERVATORY AND TCS OCS_TIME= '2009-06-25T08:41:23.978' / UTC Date for OCS calc time-dep params OPERMODE= 'OCS ' / Mode of operation: OCS | Manual | N/A SOFTVER = '1.1.1.1 ' / Softwere version (TCS.Camera.OCS.Sched) OCS_VER = '1 ' / OCS software version and date TCS_VER = '1 ' / TCS software version and date SCH_VER = '1 ' / OCS-Scheduler software version and date MAT_VER = '7.7.0.471' / Matlab version HDR_VER = '1 ' / Header version TRIGGER = 'N/A ' / trigger ID for TOO, e.g. VOEVENT-Nr TCSMODE = 'Star ' / TCS fundamental mode TCSSMODE= 'Active ' / TCS fundamental submode TCSFMODE= 'Pos ' / TCS focus mode TCSFSMOD= 'On-Target' / TCS focus submode TCSDMODE= 'Stop ' / TCS dome mode TCSDSMOD= 'N/A ' / TCS dome submode TCSWMODE= 'Slave ' / TCS windscreen mode TCSWSMOD= 'N/A ' / TCS windscreen submode OBSLAT = 33.3574 / [deg] Telescope geodetic latitude in WGS84 OBSLON = 116.8599 / [deg] Telescope geodetic longitude in WGS84 OBSALT = 1703.2 / [m] Telescope geodetic altitude in WGS84 DEFOCUS = 0. / [mm] Focus position - nominal focus FOCUSPOS= 1.3851 / [mm] Exposures focusPos DOMESTAT= 'open ' / Dome status at begining of exposure TRACKRA = 23.7 / [arcsec/hr] Track speed RA rel to sidereal TRACKDEC= -11.2 / [arcsec/hr] Track speed Dec rel to sidereal AZIMUTH = 113.8682 / [deg] Telescope Azimuth ALTITUDE= 36.81047 / [deg] Telescope altitude AIRMASS = 1.666232 / Telescope airmass TELRA = 334.402 / [deg] Telescope ap equinox of date RA TELDEC = 3.4225 / [deg] Telescope ap equinox of date Dec TELHA = 312.7096 / [deg] Telescope ap equinox of date HA DOMEAZ = 112.8563 / [deg] Dome azimuth WINDSCAL= 10.466 / [deg] Wind screen altitude WINDDIR = 2.2 / [deg] Azimuth of wind direction WINDSPED= 14.6328 / Wind speed (km/hour) OUTTEMP = 20.94444 / [C] Outside temperature OUTRELHU= 0.09 / [frac] Outside relative humidity OUTDEWPT= -12.94444 / [C] Outside dew point / INSTRUMENT TELEMETRY PANID = '_p48s ' / PAN identification DHSID = '_p48s ' / DHS identification ROISTATE= 'ROI ' / ROI State (FULL | ROI) CCDSEC = '[1:2048,1:4096]' / CCD section CCDSIZE = '[1:2048,1:4096]' / CCD size DATASEC = '[1:2048,1:4096]' / Data section DETSEC = '[1:2048,1:4096]' / Detector section ROISEC = '[1:2048,1:4096]' / ROI section FPA = 'P48MOSAIC' / Focal plan array CCDNAME = 'W94C2 ' / Detector mfg serial number CHECKSUM= 'O3aBP1ZBO1aBO1YB' / HDU checksum updated 2010-03-15T13:06:37 DATASUM = '395763289' / Data unit checksum updated 2010-03-15T13:06:37 DHEINF = 'SDSU, Gen-III' / Controller info DHEFIRM = '/usr/src/dsp/tim_m.lod' / DSP software CAM_VER = '20090615.1.3.100000' / Camera server date.rev.cfitsio LV_VER = '8.5 ' / LabVIEW software version PCI_VER = '2.0c ' / Astropci software version DETID = 'PTF/MOSAIC' / Detector ID AUTHOR = 'PTF/OCS/TCS/Camera' / Source for header information DATAMIN = 0. / Minimum value for array ROISTATE= 'ROI ' / ROI State (FULL | ROI) LEDBLUE = 'OFF ' / 470nm LED state (ON | OFF) LEDRED = 'OFF ' / 660nm LED state (ON | OFF) LEDNIR = 'OFF ' / 880nm LED state (ON | OFF) CCD9TEMP= 175.003 / [K] 0x0 servo temp sensor on CCD09 HSTEMP = 148.207 / [K] 0x1 heat spreader temp DHE0TEMP= 295.951 / [K] 0x2 detector head electronics temp, master DHE1TEMP= 298.162 / [K] 0x3 detector head electronics temp, slave DEWWTEMP= 284.71 / [K] 0x4 dewar wall temp HEADTEMP= 138.414 / [K] 0x5 cryo cooler cold head temp CCD5TEMP= 174.91 / [K] 0x6 temp sensor on CCD05 CCD11TEM= 176.061 / [K] 0x7 temp sensor on CCD11 CCD0TEMP= 169.515 / [K] 0x8 temp sensor on CCD00 RSTEMP = 232.61 / [K] 0x9 temp sensor on radiation shield DEWPRESS= 0.82 / [milli-torr] Dewar pressure DETHEAT = 38. / [%] Detector focal plane heater power NAMPSXY = '6 2 ' / Number of amplifiers in x y CCDSUM = '1 1 ' / [pix] Binning in x and y MODELFOC= 'N/A ' / MODELFOC CHECKSUM= '6aLZ8ZKZ6aKZ6YKZ' / HDU checksum updated 2010-03-15T13:06:37 DATASUM = ' 0' / Data unit checksum (2010-03-15T13:06:37) GAIN = 1.7 / [e-/D.N.] Gain of detector. READNOI = 5.1 / [e-] Read noise of detector. DARKCUR = 0.1 / [e-/s] Dark current of detector / SCAMP DISTORTION KEYWORDS RADECSYS= 'ICRS ' / Astrometric system PV1_0 = 0. / Projection distortion parameter PV1_1 = 1. / Projection distortion parameter PV1_2 = 0. / Projection distortion parameter PV1_4 = 0.000811808026654439 / Projection distortion parameter PV1_5 = 0.000610424561546246 / Projection distortion parameter PV1_6 = 0.000247550637436069 / Projection distortion parameter PV1_7 = 0.000103962986153903 / Projection distortion parameter PV1_8 = -0.000463678684598807 / Projection distortion parameter PV1_9 = -0.000431244263972048 / Projection distortion parameter PV1_10 = -0.000152691163850316 / Projection distortion parameter PV1_12 = -0.00204628855915067 / Projection distortion parameter PV1_13 = -0.00173071932398225 / Projection distortion parameter PV1_14 = 0.000212015319199711 / Projection distortion parameter PV1_15 = -0.000489268678679085 / Projection distortion parameter PV1_16 = -0.000182891514774611 / Projection distortion parameter PV2_0 = 0. / Projection distortion parameter PV2_1 = 1. / Projection distortion parameter PV2_2 = 0. / Projection distortion parameter PV2_4 = 0.000273521447624334 / Projection distortion parameter PV2_5 = 0.000876139200581004 / Projection distortion parameter PV2_6 = -0.000122736852992318 / Projection distortion parameter PV2_7 = -0.00115870481394187 / Projection distortion parameter PV2_8 = 0.000744209714565589 / Projection distortion parameter PV2_9 = -0.00031431316953523 / Projection distortion parameter PV2_10 = -0.00025720525696749 / Projection distortion parameter PV2_12 = -0.00074859772103692 / Projection distortion parameter PV2_13 = 0.000838107200656415 / Projection distortion parameter PV2_14 = -0.00012633881376049 / Projection distortion parameter PV2_15 = -0.0020312867769692 / Projection distortion parameter PV2_16 = 0.00524608854745148 / Projection distortion parameter FGROUPNO= 1 / SCAMP field group label ASTIRMS1= 0. / Astrom. dispersion RMS (intern., high S/N) ASTIRMS2= 0. / Astrom. dispersion RMS (intern., high S/N) ASTRRMS1= 3.620458E-05 / Astrom. dispersion RMS (ref., high S/N) ASTRRMS2= 3.332156E-05 / Astrom. dispersion RMS (ref., high S/N) ASTINST = 1 / SCAMP astrometric instrument label FLXSCALE= 0. / SCAMP relative flux scale MAGZEROP= 0. / SCAMP zero-point PHOTIRMS= 0. / mag dispersion RMS (internal, high S/N) RA_RMS = 0.1655724 / [arcsec] RMS of SCAMP fit from 2MASS matching DEC_RMS = 0.1891921 / [arcsec] RMS of SCAMP fit from 2MASS matching ASTROMN = 384 / Number of stars in SCAMP astrometric solution SCAMPPTH= '/ptf/pos/archive/fallbackcal/scamp/7/' / SCAMP catalog path SCAMPFIL= 'PTF_201006174759_c_e_uca3_t112521_u001916251_f02_p002899_c07.fits' / SIP DISTORTION KEYWORDS A_ORDER = 4 / Distortion order for A A_0_2 = 6.96807813586153E-08 / Projection distortion parameter A_0_3 = 1.20881759870351E-11 / Projection distortion parameter A_0_4 = -4.07297345125509E-15 / Projection distortion parameter A_1_1 = -1.71602989085006E-07 / Projection distortion parameter A_1_2 = -3.40958003336147E-11 / Projection distortion parameter A_1_3 = 1.08769435952671E-14 / Projection distortion parameter A_2_0 = 2.28067760155696E-07 / Projection distortion parameter A_2_1 = 3.6610309234789E-11 / Projection distortion parameter A_2_2 = 4.73755078335384E-15 / Projection distortion parameter A_3_0 = 8.24210855193549E-12 / Projection distortion parameter A_3_1 = 3.84753767306115E-14 / Projection distortion parameter A_4_0 = -4.54223812412034E-14 / Projection distortion parameter A_DMAX = 1.53122472683886 / Projection distortion parameter B_ORDER = 4 / Distortion order for B B_0_2 = -7.69933957449607E-08 / Projection distortion parameter B_0_3 = -9.16855566272424E-11 / Projection distortion parameter B_0_4 = 1.66630509620112E-14 / Projection distortion parameter B_1_1 = 2.46289708854316E-07 / Projection distortion parameter B_1_2 = -5.90207917198792E-11 / Projection distortion parameter B_1_3 = 1.86811615261732E-14 / Projection distortion parameter B_2_0 = 3.44908367419592E-08 / Projection distortion parameter B_2_1 = -2.49509936365959E-11 / Projection distortion parameter B_2_2 = 2.84841315780067E-15 / Projection distortion parameter B_3_0 = 2.02845080441181E-11 / Projection distortion parameter B_3_1 = -4.51317603382652E-14 / Projection distortion parameter B_4_0 = -1.16438849571175E-13 / Projection distortion parameter B_DMAX = 2.89468553502114 / Projection distortion parameter AP_ORDER= 4 / Distortion order for AP AP_0_1 = -2.3927681685928E-08 / Projection distortion parameter AP_0_2 = -6.97379868441328E-08 / Projection distortion parameter AP_0_3 = -1.21069584606865E-11 / Projection distortion parameter AP_0_4 = 4.07524721573973E-15 / Projection distortion parameter AP_1_0 = 5.65239128994064E-08 / Projection distortion parameter AP_1_1 = 1.71734217296344E-07 / Projection distortion parameter AP_1_2 = 3.41724875038451E-11 / Projection distortion parameter AP_1_3 = -1.08775499102067E-14 / Projection distortion parameter AP_2_0 = -2.28068482487158E-07 / Projection distortion parameter AP_2_1 = -3.66548961802381E-11 / Projection distortion parameter AP_2_2 = -4.75858241735224E-15 / Projection distortion parameter AP_3_0 = -8.24781966878619E-12 / Projection distortion parameter AP_3_1 = -3.85281201904104E-14 / Projection distortion parameter AP_4_0 = 4.54275049666924E-14 / Projection distortion parameter BP_ORDER= 4 / Distortion order for BP BP_0_1 = -1.50638746640517E-07 / Projection distortion parameter BP_0_2 = 7.70565767927487E-08 / Projection distortion parameter BP_0_3 = 9.18374546897802E-11 / Projection distortion parameter BP_0_4 = -1.66839467627906E-14 / Projection distortion parameter BP_1_0 = -4.87195269294628E-08 / Projection distortion parameter BP_1_1 = -2.46371690411844E-07 / Projection distortion parameter BP_1_2 = 5.9111535979953E-11 / Projection distortion parameter BP_1_3 = -1.87729776729012E-14 / Projection distortion parameter BP_2_0 = -3.46046151217313E-08 / Projection distortion parameter BP_2_1 = 2.51320825919019E-11 / Projection distortion parameter BP_2_2 = -2.85758325791527E-15 / Projection distortion parameter BP_3_0 = -2.04221364218494E-11 / Projection distortion parameter BP_3_1 = 4.51336286236569E-14 / Projection distortion parameter BP_4_0 = 1.16567578965612E-13 / Projection distortion parameter / DATA FLOW ORIGNAME= '/data/PTF_default_38068.fits' / Filename as written by the camera FILENAME= 'PTF200906253621_2_o_38068.fits' / Filename of delivered camera image PROCORIG= 'IPAC-PTF pipelines' / Processing origin PROCDATE= 'Tue Feb 21 03:34:46 2012' / Processing date/time (Pacific time) PTFVERSN= 5. / Version of PTFSCIENCEPIPELINE program PMASKPTH= '/ptf/pos/archive/fallbackcal/pmasks/' / Pathname of pixel mask PMASKFIL= 'bpm_2009060s_c07.v2.fits' / Filename of pixel mask SFLATPTH= '/ptf/pos/sbx1/2009/06/25/f2/c7/cal/p4/cId45986/' / Pathname of super SFLATFIL= 'PTF_200906250000_i_s_flat_t120000_u000045986_f02_p000000_c07.fits' SBIASPTH= '/ptf/pos/sbx1/2009/06/25/f2/c7/cal/p1/cId45922/' / Pathname of super SBIASFIL= 'PTF_200906250000_i_s_bias_t120000_u000045922_f00_p000000_c07.fits' DBNID = 121 / Database night ID DBEXPID = 22920 / Database exposure ID DBRID = 3663141 / Database raw-image ID DBPID = 12052003 / Database processed-image ID DBFID = 2 / Database filter ID DBPIID = 1 / Database P.I. ID DBPRID = 3 / Database project ID DBFIELD = 22920 / Database field ID DBSVID = 50 / Database software-version ID DBCVID = 56 / Database config-data-file ID END \ No newline at end of file diff --git a/astropy/wcs/tests/data/tpvonly.hdr b/astropy/wcs/tests/data/tpvonly.hdr new file mode 100644 index 000000000000..79f236c8757c --- /dev/null +++ b/astropy/wcs/tests/data/tpvonly.hdr @@ -0,0 +1 @@ +SIMPLE = T / Fits standard BITPIX = -32 / FOUR-BYTE SINGLE PRECISION FLOATING POINT NAXIS = 2 / STANDARD FITS FORMAT NAXIS1 = 2048 / STANDARD FITS FORMAT NAXIS2 = 4096 / STANDARD FITS FORMAT ORIGIN = 'Palomar Transient Factory' / Origin of these image data CREATOR = 'Infrared Processing and Analysis Center' / Creator of this FITS file TELESCOP= 'P48 ' / Name of telescope INSTRUME= 'PTF/MOSAIC' / Instrument name OBSERVER= 'KulkarniPTF' / Observer name and project CCDID = '5 ' / CCD number (0..11) DATE-OBS= '2014-07-31T04:49:58.673' / UTC shutter time YYYY-MM-DDTHH:MM:SS.SSS DATE = '2014-07-31T19:20:32' / File creation date (YYYY-MM-DDThh:mm:ss UT) REFERENC= 'http://www.astro.caltech.edu/ptf' / URL of PTF website / PROPOSAL INFORMATION PTFPRPI = 'Kulkarni' / PTF Project PI PTFPID = '52002 ' / Project type: 00000-49999 OBJECT = 'Galactic_Plane' / Fields object PTFFIELD= '1549 ' / PTF unique field ID PTFFLAG = '1 ' / 1 = PTF; 0 = non-PTF category / TIME AND EXPOSURE INFORMATION FILTER = 'R ' / Filter name FILTERID= '2 ' / Filter ID FILTERSL= '1 ' / Filter changer slot position EXPTIME = 60. / [s] Requested exposure time AEXPTIME= 60. / actual exposure time (sec) UTC-OBS = '2014-07-31T04:49:58.673' / UTC time shutter open YYYY-MM-DDTHH:MM:SS.OBSJD = 2456869.70137 / [day] Julian day corresponds to UTC-OBS OBSMJD = 56869.20137 / MJD corresponds to UTC-OBS (day) OBSLST = '17:37:29.36' / Mean LST corresponds to UTC-OBS 'HH:MM:SS.S' HOURANG = '-0:42:20.82' / Mean HA (sHH:MM:SS.S) based on LMST at UTC-OBS HJD = 2456869.70618 / [day] Heliocentric Julian Day OBSTYPE = 'object ' / Image type (dark,science,bias,focus) IMGTYP = 'object ' / Image type (dark,science,bias,focus) / MOON AND SUN MOONRA = 173.116974 / [deg] Moon J2000.0 R.A. MOONDEC = -0.404999 / [deg] Moon J2000.0 Dec. MOONILLF= 0.155837 / [frac] Moon illuminated fraction MOONPHAS= 133.4978 / [deg] Moon phase angle MOONESB = -0. / Moon excess in sky brightness V-band MOONALT = -1.271649 / [deg] Moon altitude SUNAZ = 312.4731 / [deg] Sun azimuth SUNALT = -22.25475 / [deg] Sun altitude / PHOTOMETRY BUNIT = 'DN ' / Data number (analog-to-digital units or ADU) PHTCALEX= 1 / Was phot.-cal. module executed? PHTCALFL= 0 / Flag for image is photometric (0=N, 1=Y) PCALRMSE= 0.171135 / RMSE from (zeropoint, extinction) data fit IMAGEZPT= 21.22791 / Image magnitude zeropoint IZPORIG = 'CALTRANS' / Photometric-calibration origin ZPRULE = 'COMPUTE ' / Photometric-calibration method MAGZPT = 23.55079 / Magnitude zeropoint at airmass=1 EXTINCT = 1.163014 / Extinction APSFILT = 'r ' / SDSS filter used in abs phot cal APSCOL = 'r-i ' / SDSS color used in abs phot cal APRMS = 0.06677995 / RMS in mag of final abs phot cal APBSRMS = 0.05033556 / RMS in mag of final abs phot cal for bright staAPNSTDI1= 308233 / Number of standard stars in first iteration APNSTDIF= 274569 / Number of standard stars in final iteration APCHI2 = 1861590.34570444 / Chi2 of final abs phot cal APDOF = 274569. / Dof of chi2 of final abs phot cal APMEDJD = 2456869.84882722 / Median JD used in abs phot cal APPN01 = 'ZeroPoint' / Name of parameter abs phot cal 01 APPAR01 = 23.67499643 / Value of parameter abs phot cal 01 APPARE01= 0.00324545 / Error of parameter abs phot cal 01 APPN02 = 'ColorTerm' / Name of parameter abs phot cal 02 APPAR02 = 0.44908632 / Value of parameter abs phot cal 02 APPARE02= 0.00423336 / Error of parameter abs phot cal 02 APPN03 = 'AirMassTerm' / Name of parameter abs phot cal 03 APPAR03 = -0.18342823 / Value of parameter abs phot cal 03 APPARE03= 0.00288243 / Error of parameter abs phot cal 03 APPN04 = 'AirMassColorTerm' / Name of parameter abs phot cal 04 APPAR04 = -0.14534473 / Value of parameter abs phot cal 04 APPARE04= 0.00381178 / Error of parameter abs phot cal 04 APPN05 = 'TimeTerm' / Name of parameter abs phot cal 05 APPAR05 = 0.42239539 / Value of parameter abs phot cal 05 APPARE05= 0.0019644 / Error of parameter abs phot cal 05 APPN06 = 'Time2Term' / Name of parameter abs phot cal 06 APPAR06 = 0.16770061 / Value of parameter abs phot cal 06 APPARE06= 0.01549427 / Error of parameter abs phot cal 06 APPN07 = 'XTerm ' / Name of parameter abs phot cal 07 APPAR07 = 0.02152189 / Value of parameter abs phot cal 07 APPARE07= 0.00047932 / Error of parameter abs phot cal 07 APPN08 = 'YTerm ' / Name of parameter abs phot cal 08 APPAR08 = 0.02739724 / Value of parameter abs phot cal 08 APPARE08= 0.00117248 / Error of parameter abs phot cal 08 APPN09 = 'Y2Term ' / Name of parameter abs phot cal 09 APPAR09 = 0.01522565 / Value of parameter abs phot cal 09 APPARE09= 0.0018581 / Error of parameter abs phot cal 09 APPN10 = 'Y3Term ' / Name of parameter abs phot cal 10 APPAR10 = -0.23390906 / Value of parameter abs phot cal 10 APPARE10= 0.00723349 / Error of parameter abs phot cal 10 APPN11 = 'XYTerm ' / Name of parameter abs phot cal 11 APPAR11 = -0.00677493 / Value of parameter abs phot cal 11 APPARE11= 0.00169149 / Error of parameter abs phot cal 11 / ASTROMETRY CRVAL1 = 274.806945708898 / [deg] RA of reference point CRVAL2 = -25.9746476963393 / [deg] DEC of reference point CRPIX1 = -3925.16 / [pix] Image reference point CRPIX2 = 4360.23 / [pix] Image reference point CTYPE1 = 'RA---TPV' / TAN (gnomic) projection + SIP distortions CTYPE2 = 'DEC--TPV' / TAN (gnomic) projection + SIP distortions CUNIT1 = 'deg ' / Image axis-1 celestial-coordinate units CUNIT2 = 'deg ' / Image axis-2 celestial-coordinate units CRTYPE1 = 'deg ' / Data units of CRVAL1 CRTYPE2 = 'deg ' / Data units of CRVAL2 CD1_1 = 0.000286102658601581 / Transformation matrix CD1_2 = -6.28816628331811E-07 CD2_1 = -5.77207018114522E-06 CD2_2 = -0.000281525256171892 OBJRA = '18:18:56.842' / Requested field J2000.0 Ra. OBJDEC = '-25:52:30.00' / Requested field J2000.0 Dec. OBJRAD = 274.73684 / [deg] Requested field RA (J2000.0) OBJDECD = -25.875 / [deg] Requested field Dec (J2000.0) PIXSCALE= 1.01 / [arcsec/pix] Pixel scale EQUINOX = 2000. / [yr] Equatorial coordinates definition / IMAGE QUALITY SEEING = 2.95 / [pix] Seeing FWHM PEAKDIST= 0.481336680505667 / [pix] Mean dist brightest pixel-centroid pixel ELLIP = 0.313 / Mean image ellipticity A/B ELLIPPA = 48.58 / [deg] Mean image ellipticity PA FBIAS = 785.8855 / [DN] Floating bias of the image SATURVAL= 50000. / [DN] Saturation value of the CCD array FWHMSEX = 2.45 / [arcsec] SExtractor SEEING estimate MSMAPCZP= 19.20814 / [mag/s-arcsec^2] Median sky abs. phot. cal. LMGAPCZP= 20.68008 / [mag/s-arcsec^2] Limiting mag. abs. phot. cal. MEDFWHM = 3.417924 / [arcsecond] Median FWHM MEDELONG= 1.406608 / [dimensionless] Median elongation STDELONG= 0.592749 / [dimensionless] Std. dev. of elongation MEDTHETA= -31.22347 / [deg] Atan(median sin(theta)/median cos(theta))STDTHETA= 65.37225 / [deg] Atan(stddev sin(theta)/stddev cos(theta))MEDDLMAG= 2.444709 / [mag/s-arcsec^2] Median (MU_MAX-MAG_AUTO) STDDLMAG= 0.4154117 / [mag/s-arcsec^2] Stddev of (MU_MAX-MAG_AUTO) / OBSERVATORY AND TCS OCS_TIME= '2014-07-31T04:49:58.613' / UTC Date for OCS calc time-dep params OPERMODE= 'OCS ' / Mode of operation: OCS | Manual | N/A SOFTVER = '1.1.1.1 ' / Softwere version (TCS.Camera.OCS.Sched) OCS_VER = '1 ' / OCS software version and date TCS_VER = '1 ' / TCS software version and date SCH_VER = '1 ' / OCS-Scheduler software version and date MAT_VER = '7.7.0.471' / Matlab version HDR_VER = '1 ' / Header version TRIGGER = 'N/A ' / trigger ID for TOO, e.g. VOEVENT-Nr TCSMODE = 'Star ' / TCS fundamental mode TCSSMODE= 'Active ' / TCS fundamental submode TCSFMODE= 'Pos ' / TCS focus mode TCSFSMOD= 'On-Target' / TCS focus submode TCSDMODE= 'Stop ' / TCS dome mode TCSDSMOD= 'N/A ' / TCS dome submode TCSWMODE= 'Slave ' / TCS windscreen mode TCSWSMOD= 'N/A ' / TCS windscreen submode OBSLAT = 33.3574 / [deg] Telescope geodetic latitude in WGS84 OBSLON = -116.8599 / [deg] Telescope geodetic longitude in WGS84 OBSALT = 1703.2 / [m] Telescope geodetic altitude in WGS84 DEFOCUS = 0. / [mm] Focus position - nominal focus FOCUSPOS= 1.3655 / [mm] Exposures focusPos DOMESTAT= 'open ' / Dome status at begining of exposure TRACKRA = 20.4 / [arcsec/hr] Track speed RA rel to sidereal TRACKDEC= -3.9 / [arcsec/hr] Track speed Dec rel to sidereal AZIMUTH = 169.2328 / [deg] Telescope Azimuth ALTITUDE= 29.95342 / [deg] Telescope altitude AIRMASS = 1.997293 / Telescope airmass TELRA = 274.9591 / [deg] Telescope ap equinox of date RA TELDEC = -25.8684 / [deg] Telescope ap equinox of date Dec TELHA = 349.4138 / [deg] Telescope ap equinox of date HA DOMEAZ = 169.4477 / [deg] Dome azimuth WINDSCAL= 12.8995 / [deg] Wind screen altitude WINDDIR = 1.3 / [deg] Azimuth of wind direction WINDSPED= 14.472 / Wind speed (km/hour) OUTTEMP = 22.16667 / [C] Outside temperature OUTRELHU= 0.513 / [frac] Outside relative humidity OUTDEWPT= 11.61111 / [C] Outside dew point / INSTRUMENT TELEMETRY PANID = '_p48m ' / PAN identification DHSID = '_p48m ' / DHS identification CCDSEC = '[1:2048,1:4096]' / CCD section CCDSIZE = '[1:2048,1:4096]' / CCD size DATASEC = '[1:2048,1:4096]' / Data section DETSEC = '[1:2048,1:4096]' / Detector section ROISEC = '[1:2048,1:4096]' / ROI section FPA = 'P48MOSAIC' / Focal plan array CCDNAME = 'W53C2 ' / Detector mfg serial number CHECKSUM= 'fGoXhEmXfEmXfEmX' / Image header unit checksum DATASUM = '2019013917' / Image data unit checksum DHEINF = 'SDSU, Gen-III' / Controller info DHEFIRM = '/usr/src/dsp/20090618/tim_m.lod' / DSP software CAM_VER = '20090615.1.3.100000' / Camera server date.rev.cfitsio LV_VER = '8.5 ' / LabVIEW software version PCI_VER = '2.0c ' / Astropci software version DETID = 'PTF/MOSAIC' / Detector ID AUTHOR = 'PTF/OCS/TCS/Camera' / Source for header information DATAMIN = 0. / Minimum value for array ROISTATE= 'ROI ' / ROI State (FULL | ROI) LEDBLUE = 'OFF ' / 470nm LED state (ON | OFF) LEDRED = 'OFF ' / 660nm LED state (ON | OFF) LEDNIR = 'OFF ' / 880nm LED state (ON | OFF) CCD9TEMP= 174.988 / [K] 0x0 servo temp sensor on CCD09 HSTEMP = 152.111 / [K] 0x1 heat spreader temp DHE0TEMP= 301.098 / [K] 0x2 detector head electronics temp, master DHE1TEMP= 303.178 / [K] 0x3 detector head electronics temp, slave DEWWTEMP= 287.05 / [K] 0x4 dewar wall temp HEADTEMP= 142.103 / [K] 0x5 cryo cooler cold head temp CCD5TEMP= 175.963 / [K] 0x6 temp sensor on CCD05 CCD11TEM= 177.375 / [K] 0x7 temp sensor on CCD11 CCD0TEMP= 170.213 / [K] 0x8 temp sensor on CCD00 RSTEMP = 238.936 / [K] 0x9 temp sensor on radiation shield DEWPRESS= 40. / [milli-torr] Dewar pressure DETHEAT = 1.6 / [%] Detector focal plane heater power NAMPSXY = '6 2 ' / Number of amplifiers in x y CCDSUM = '1 1 ' / [pix] Binning in x and y MODELFOC= 'N/A ' / MODELFOC EXPCKSUM= 'fGoXhEmXfEmXfEmX' / Primary header unit checksum EXPDTSUM= '2019013917' / Primary data unit checksum GAIN = 1.7 / [e-/D.N.] Gain of detector. READNOI = 3.4 / [e-] Read noise of detector. DARKCUR = 0.1 / [e-/s] Dark current of detector / SCAMP DISTORTION KEYWORDS RADECSYS= 'ICRS ' / Astrometric system PV1_0 = 0. / Projection distortion parameter PV1_1 = 1. / Projection distortion parameter PV1_2 = 0. / Projection distortion parameter PV1_4 = -0.016169561788921 / Projection distortion parameter PV1_5 = -0.0051747493874632 / Projection distortion parameter PV1_6 = -0.000238504358056776 / Projection distortion parameter PV1_7 = 0.00629760478963159 / Projection distortion parameter PV1_8 = 0.00397207946734115 / Projection distortion parameter PV1_9 = -0.000677296206451849 / Projection distortion parameter PV1_10 = 0.000503546797066621 / Projection distortion parameter PV1_12 = -0.000973553429744082 / Projection distortion parameter PV1_13 = -0.00102312736844768 / Projection distortion parameter PV1_14 = 0.000253623568347818 / Projection distortion parameter PV1_15 = -0.000200211924758127 / Projection distortion parameter PV1_16 = -6.21626607050974E-05 / Projection distortion parameter PV2_0 = 0. / Projection distortion parameter PV2_1 = 1. / Projection distortion parameter PV2_2 = 0. / Projection distortion parameter PV2_4 = -0.000743645656922906 / Projection distortion parameter PV2_5 = 0.000184250025396486 / Projection distortion parameter PV2_6 = 0.0219715919766664 / Projection distortion parameter PV2_7 = -7.54497752637404E-05 / Projection distortion parameter PV2_8 = 0.000649357185110191 / Projection distortion parameter PV2_9 = -0.00081219646536117 / Projection distortion parameter PV2_10 = -0.0105098433615178 / Projection distortion parameter PV2_12 = -2.1755521894303E-05 / Projection distortion parameter PV2_13 = -7.90103717680049E-05 / Projection distortion parameter PV2_14 = -0.000155711703067327 / Projection distortion parameter PV2_15 = 0.000169335617180111 / Projection distortion parameter PV2_16 = 0.00186540574051853 / Projection distortion parameter FGROUPNO= 1 / SCAMP field group label ASTIRMS1= 0. / Astrom. dispersion RMS (intern., high S/N) ASTIRMS2= 0. / Astrom. dispersion RMS (intern., high S/N) ASTRRMS1= 2.362887E-05 / Astrom. dispersion RMS (ref., high S/N) ASTRRMS2= 2.36868E-05 / Astrom. dispersion RMS (ref., high S/N) ASTINST = 1 / SCAMP astrometric instrument label FLXSCALE= 0. / SCAMP relative flux scale MAGZEROP= 0. / SCAMP zero-point PHOTIRMS= 0. / mag dispersion RMS (internal, high S/N) RA_RMS = 0.1040474 / [arcsec] RMS of SCAMP fit from 2MASS matching DEC_RMS = 0.1017731 / [arcsec] RMS of SCAMP fit from 2MASS matching ASTROMN = 2636 / Number of stars in SCAMP astrometric solution SCAMPPTH= 'NotAvailable' / SCAMP catalog path SCAMPFIL= 'NotAvailable' / SCAMP catalog file / SIP DISTORTION KEYWORDS / DATA FLOW ORIGNAME= '/data/PTF_default_37806.fits' / Filename as written by the camera FILENAME= 'PTF201407312014_2_o_37806.fits' / Filename of delivered camera image PROCORIG= 'IPAC-PTF pipelines' / Processing origin PROCDATE= 'Fri Sep 26 14:52:55 2014' / Processing date/time (Pacific time) PTFVERSN= 5. / Version of PTFSCIENCEPIPELINE program PMASKPTH= '/ptf/pos/archive/fallbackcal/pmasks/' / Pathname of pixel mask PMASKFIL= '70sOn35s_pixmask_chip5.trimmed.v4.fits' / Filename of pixel mask SFLATPTH= '/ptf/pos/sbx2/2014/07/31/f2/c5/cal/p4/cId112103/' / Pathname of superSFLATFIL= 'PTF_201407310000_i_s_flat_t120000_u000112103_f02_p000000_c05.fits' SBIASPTH= '/ptf/pos/sbx2/2014/07/31/f2/c5/cal/p1/cId112095/' / Pathname of superSBIASFIL= 'PTF_201407310000_i_s_bias_t120000_u000112095_f00_p000000_c05.fits' DBNID = 1938 / Database night ID DBEXPID = 446050 / Database exposure ID DBRID = 6985566 / Database raw-image ID DBPID = 21528832 / Database processed-image ID DBFID = 2 / Database filter ID DBPIID = 1 / Database P.I. ID DBPRID = 31 / Database project ID DBFIELD = 446050 / Database field ID DBSVID = 54 / Database software-version ID DBCVID = 60 / Database config-data-file ID INFOBITS= 0 / Database infobits (2^2 and 2^3 excluded) END \ No newline at end of file diff --git a/astropy/wcs/tests/data/unit.hdr b/astropy/wcs/tests/data/unit.hdr new file mode 100644 index 000000000000..92301774f6af --- /dev/null +++ b/astropy/wcs/tests/data/unit.hdr @@ -0,0 +1 @@ +SIMPLE = T / conforms to FITS standard BITPIX = -64 / array data type NAXIS = 3 / number of array dimensions NAXIS1 = 400 NAXIS2 = 300 NAXIS3 = 251 EQUINOX = 2000.0 CTYPE1 = 'GLON-CAR' CTYPE2 = 'GLAT-CAR' CTYPE3 = 'VRAD' SPECSYS = 'LSRK' CUNIT3 = 'km/s ' BUNIT = 'K ' CRVAL1 = 49.209553 CRVAL2 = 0.0 CRVAL3 = 50.0 CRPIX1 = 200.0 CRPIX2 = 288.0 CRPIX3 = 125.0 CDELT1 = -0.00333333333333333 CDELT2 = 0.003333333333333333 CDELT3 = 0.5 RESTFREQ= 1000. TELESCOP= 'Arecibo' END \ No newline at end of file diff --git a/astropy/wcs/tests/data/validate.5.0.txt b/astropy/wcs/tests/data/validate.5.0.txt new file mode 100644 index 000000000000..37545342aec4 --- /dev/null +++ b/astropy/wcs/tests/data/validate.5.0.txt @@ -0,0 +1,16 @@ +HDU 1: + WCS key ' ': + - RADECSYS= 'ICRS ' / Astrometric system + the RADECSYS keyword is deprecated, use RADESYSa. + - The WCS transformation has more axes (2) than the image it is + associated with (0) + - Removed redundant SCAMP distortion parameters because SIP + parameters are also present + +HDU 2: + WCS key ' ': + - The WCS transformation has more axes (3) than the image it is + associated with (0) + - 'celfix' made the change 'In CUNIT3 : Mismatched units type + 'length': have 'Hz', want 'm''. + - 'unitfix' made the change 'Changed units: 'HZ ' -> 'Hz''. diff --git a/astropy/wcs/tests/data/validate.5.13.txt b/astropy/wcs/tests/data/validate.5.13.txt new file mode 100644 index 000000000000..9b5e4c5323b8 --- /dev/null +++ b/astropy/wcs/tests/data/validate.5.13.txt @@ -0,0 +1,16 @@ +HDU 1: + WCS key ' ': + - RADECSYS= 'ICRS ' / Astrometric system + the RADECSYS keyword is deprecated, use RADESYSa. + - The WCS transformation has more axes (2) than the image it is + associated with (0) + - Removed redundant SCAMP distortion parameters because SIP + parameters are also present + +HDU 2: + WCS key ' ': + - The WCS transformation has more axes (3) than the image it is + associated with (0) + - 'celfix' made the change 'In CUNIT3 : Mismatched units type + 'length': have 'Hz', want 'm''. + - 'unitfix' made the change 'Changed units: 'HZ' -> 'Hz''. diff --git a/astropy/wcs/tests/data/validate.6.txt b/astropy/wcs/tests/data/validate.6.txt new file mode 100644 index 000000000000..bc0dba23e37d --- /dev/null +++ b/astropy/wcs/tests/data/validate.6.txt @@ -0,0 +1,17 @@ +HDU 1: + WCS key ' ': + - RADECSYS= 'ICRS ' / Astrometric system + the RADECSYS keyword is deprecated, use RADESYSa. + - The WCS transformation has more axes (2) than the image it is + associated with (0) + - Removed redundant SCAMP distortion parameters because SIP + parameters are also present + +HDU 2: + WCS key ' ': + - The WCS transformation has more axes (3) than the image it is + associated with (0) + - 'unitfix' made the change 'Changed units: + 'HZ' -> 'Hz'. + - 'celfix' made the change 'In CUNIT3 : Mismatched units type + 'length': have 'Hz', want 'm''. diff --git a/astropy/wcs/tests/data/validate.7.4.txt b/astropy/wcs/tests/data/validate.7.4.txt new file mode 100644 index 000000000000..424a13c43cab --- /dev/null +++ b/astropy/wcs/tests/data/validate.7.4.txt @@ -0,0 +1,20 @@ +HDU 1: + WCS key ' ': + - RADECSYS= 'ICRS ' / Astrometric system + the RADECSYS keyword is deprecated, use RADESYSa. + - The WCS transformation has more axes (2) than the image it is + associated with (0) + - Removed redundant SCAMP distortion parameters because SIP + parameters are also present + - 'datfix' made the change 'Set MJD-OBS to 55007.362083 from DATE- + OBS'. + +HDU 2: + WCS key ' ': + - The WCS transformation has more axes (3) than the image it is + associated with (0) + - 'datfix' made the change 'Success'. + - 'unitfix' made the change 'Changed units: + 'HZ' -> 'Hz'. + - 'celfix' made the change 'In CUNIT3 : Mismatched units type + 'length': have 'Hz', want 'm''. diff --git a/astropy/wcs/tests/data/validate.7.6.txt b/astropy/wcs/tests/data/validate.7.6.txt new file mode 100644 index 000000000000..9c258c8a3963 --- /dev/null +++ b/astropy/wcs/tests/data/validate.7.6.txt @@ -0,0 +1,19 @@ +HDU 1: + WCS key ' ': + - RADECSYS= 'ICRS ' / Astrometric system + the RADECSYS keyword is deprecated, use RADESYSa. + - The WCS transformation has more axes (2) than the image it is + associated with (0) + - Removed redundant SCAMP distortion parameters because SIP + parameters are also present + - 'datfix' made the change 'Set MJD-OBS to 55007.362083 from DATE- + OBS'. + +HDU 2: + WCS key ' ': + - The WCS transformation has more axes (3) than the image it is + associated with (0) + - 'unitfix' made the change 'Changed units: + 'HZ' -> 'Hz'. + - 'celfix' made the change 'In CUNIT3 : Mismatched units type + 'length': have 'Hz', want 'm''. \ No newline at end of file diff --git a/astropy/wcs/tests/data/validate.fits b/astropy/wcs/tests/data/validate.fits new file mode 100644 index 000000000000..9c2ae0af3331 --- /dev/null +++ b/astropy/wcs/tests/data/validate.fits @@ -0,0 +1 @@ +SIMPLE = T / conforms to FITS standard BITPIX = 8 / array data type NAXIS = 0 / number of array dimensions EXTEND = T END XTENSION= 'IMAGE ' / Image extension BITPIX = 8 / array data type NAXIS = 0 / number of array dimensions PCOUNT = 0 / number of parameters GCOUNT = 1 / number of groups ORIGIN = 'Palomar Transient Factory' / Origin of these image data CREATOR = 'Infrared Processing and Analysis Center' / Creator of this FITS file DATE = '2011-08-01T15:14:04' / File creation date (YYYY-MM-DDThh:mm:ss UT) / PTF DMASK BIT DEFINITIONS BIT00 = 0 / AIRCRAFT/SATELLITE TRACK BIT01 = 1 / OBJECT (detected by SExtractor) BIT02 = 2 / HIGH DARK-CURRENT BIT03 = 3 / RESERVED FOR FUTURE USE BIT04 = 4 / NOISY BIT05 = 5 / GHOST BIT06 = 6 / CCD BLEED BIT07 = 7 / RAD HIT BIT08 = 8 / SATURATED BIT09 = 9 / DEAD/BAD BIT10 = 10 / NAN (not a number) BIT11 = 11 / DIRTY (10-sigma below coarse local median) BIT12 = 12 / HALO BIT13 = 13 / RESERVED FOR FUTURE USE BIT14 = 14 / RESERVED FOR FUTURE USE BIT15 = 15 / RESERVED FOR FUTURE USE / DATA FLOW PMASKPTH= '/ptf/pos/archive/fallbackcal/pmasks/' / Pixel-mask pathname PMASKFIL= 'bpm_2009060s_c07.v2.fits' / Pixel-mask filename UDMPROC = 'updatemask' / Update bit in dmask UDMVERSN= 1. / Version of updatemask program UDMOP = 0 / Operation type UDMMBT = 4 / Mask bit template UDMIFIL = 'bpm_2009060s_c07.v1.fits' / Program updatemask input file UDMOFIL = 'bpm_2009060s_c07.v2.fits' / Program updatemask output file MCVERSN = 1. / Version of ptfMaskCombine program PTFPPROC= 'ptfPostProc' / Flags proc NaNs, CCD-bleeds and rad hits PPRVERSN= 3. / Version of ptfPostProc program / COPY OF IMAGE HEADER BELOW ORIGIN = 'Palomar Transient Factory' / Origin of these image data CREATOR = 'Infrared Processing and Analysis Center' / Creator of this FITS file TELESCOP= 'P48 ' / Name of telescope INSTRUME= 'PTF/MOSAIC' / Instrument name OBSERVER= 'KulkarniPTF' / Observer name and project CCDID = '7 ' / CCD number (0..11) DATE-OBS= '2009-06-25T08:41:23.970' / UTC shutter time YYYY-MM-DDTHH:MM:SS.SSS DATE = '2011-07-30T15:04:59' / File creation date (YYYY-MM-DDThh:mm:ss UT) REFERENC= 'http://www.astro.caltech.edu/ptf' / URL of PTF website / PROPOSAL INFORMATION PTFPRPI = 'Kulkarni' / PTF Project PI PTFPID = '20000 ' / Project type: 00000-49999 OBJECT = 'PTF_survey' / Fields object PTFFIELD= '2899 ' / PTF unique field ID PTFFLAG = '1 ' / 1 = PTF; 0 = non-PTF category / TIME AND EXPOSURE INFORMATION FILTER = 'R ' / Filter name FILTERID= '2 ' / Filter ID FILTERSL= '2 ' / Filter changer slot position EXPTIME = 60. / [s] Requested exposure time AEXPTIME= 60. / actual exposure time (sec) UTC-OBS = '2009-06-25T08:41:23.970' / UTC time shutter open YYYY-MM-DDTHH:MM:SS.OBSJD = 2455007.86207 / [day] Julian day corresponds to UTC-OBS OBSMJD = 55007.36207 / MJD corresponds to UTC-OBS (day) OBSLST = '19:08:26.70' / Mean LST corresponds to UTC-OBS 'HH:MM:SS.S' HOURANG = '-3:09:09.78' / Mean HA (sHH:MM:SS.S) based on LMST at UTC-OBS HJD = 2455007.86457 / [day] Heliocentric Julian Day OBSTYPE = 'object ' / Image type (dark,science,bias,focus) IMGTYP = 'object ' / Image type (dark,science,bias,focus) / MOON AND SUN MOONRA = 131.676395 / [deg] Moon J2000.0 R.A. MOONDEC = 16.207046 / [deg] Moon J2000.0 Dec. MOONILLF= 0.095389 / [frac] Moon illuminated fraction MOONPHAS= 144.0201 / [deg] Moon phase angle MOONESB = -0. / Moon excess in sky brightness V-band MOONALT = -35.16959 / [deg] Moon altitude SUNAZ = 13.90467 / [deg] Sun azimuth SUNALT = -31.96011 / [deg] Sun altitude / PHOTOMETRY BUNIT = 'DN ' / Data number (analog-to-digital units or ADU) PHTCALEX= 1 / Was phot.-cal. module executed? PHTCALFL= 0 / Flag for image is photometric (0=N, 1=Y) PCALRMSE= 0.030059 / RMSE from (zeropoint, extinction) data fit IMAGEZPT= 22.43948 / Image magnitude zeropoint IZPORIG = 'CALTRANS' / Photometric-calibration origin ZPRULE = 'COMPUTE ' / Photometric-calibration method MAGZPT = 22.73683 / Magnitude zeropoint at airmass=1 EXTINCT = 0.17995 / Extinction APSFILT = 'r ' / SDSS filter used in abs phot cal APSCOL = 'r-i ' / SDSS color used in abs phot cal APRMS = 0.0554857 / RMS in mag of final abs phot cal APBSRMS = 0.03911367 / RMS in mag of final abs phot cal for bright staAPNSTDI1= 69501 / Number of standard stars in first iteration APNSTDIF= 64858 / Number of standard stars in final iteration APCHI2 = 325618.88012443 / Chi2 of final abs phot cal APDOF = 64858. / Dof of chi2 of final abs phot cal APMEDJD = 2455007.84500722 / Median JD used in abs phot cal APPN01 = 'ZeroPoint' / Name of parameter abs phot cal 01 APPAR01 = 22.81878918 / Value of parameter abs phot cal 01 APPARE01= 0.00198923 / Error of parameter abs phot cal 01 APPN02 = 'ColorTerm' / Name of parameter abs phot cal 02 APPAR02 = 0.20481246 / Value of parameter abs phot cal 02 APPARE02= 0.00278076 / Error of parameter abs phot cal 02 APPN03 = 'AirMassTerm' / Name of parameter abs phot cal 03 APPAR03 = -0.12104427 / Value of parameter abs phot cal 03 APPARE03= 0.0011621 / Error of parameter abs phot cal 03 APPN04 = 'AirMassColorTerm' / Name of parameter abs phot cal 04 APPAR04 = 0.00904321 / Value of parameter abs phot cal 04 APPARE04= 0.00176968 / Error of parameter abs phot cal 04 APPN05 = 'TimeTerm' / Name of parameter abs phot cal 05 APPAR05 = 0.03012546 / Value of parameter abs phot cal 05 APPARE05= 0.00459951 / Error of parameter abs phot cal 05 APPN06 = 'Time2Term' / Name of parameter abs phot cal 06 APPAR06 = 1.27460327 / Value of parameter abs phot cal 06 APPARE06= 0.07646497 / Error of parameter abs phot cal 06 APPN07 = 'XTerm ' / Name of parameter abs phot cal 07 APPAR07 = 0.00768843 / Value of parameter abs phot cal 07 APPARE07= 0.00083226 / Error of parameter abs phot cal 07 APPN08 = 'YTerm ' / Name of parameter abs phot cal 08 APPAR08 = 0.06680527 / Value of parameter abs phot cal 08 APPARE08= 0.00203667 / Error of parameter abs phot cal 08 APPN09 = 'Y2Term ' / Name of parameter abs phot cal 09 APPAR09 = 0.31486016 / Value of parameter abs phot cal 09 APPARE09= 0.00318862 / Error of parameter abs phot cal 09 APPN10 = 'Y3Term ' / Name of parameter abs phot cal 10 APPAR10 = 0.69934253 / Value of parameter abs phot cal 10 APPARE10= 0.01257477 / Error of parameter abs phot cal 10 APPN11 = 'XYTerm ' / Name of parameter abs phot cal 11 APPAR11 = -0.04590337 / Value of parameter abs phot cal 11 APPARE11= 0.00286729 / Error of parameter abs phot cal 11 / ASTROMETRY CRVAL1 = 333.443801401309 / [deg] RA of reference point CRVAL2 = 3.08905544069643 / [deg] DEC of reference point CRPIX1 = 1175.019 / [pix] Image reference point CRPIX2 = 945.8826 / [pix] Image reference point CTYPE1 = 'RA---TAN-SIP' / TAN (gnomic) projection + SIP distortions CTYPE2 = 'DEC--TAN-SIP' / TAN (gnomic) projection + SIP distortions CUNIT1 = 'deg ' / Image axis-1 celestial-coordinate units CUNIT2 = 'deg ' / Image axis-2 celestial-coordinate units CRTYPE1 = 'deg ' / Data units of CRVAL1 CRTYPE2 = 'deg ' / Data units of CRVAL2 CD1_1 = 0.000281094342514378 / Transformation matrix CD1_2 = -5.00875320999652E-09 CD2_1 = -2.08930602680508E-07 CD2_2 = -0.000281284158795544 OBJRA = '22:17:08.571' / Requested field J2000.0 Ra. OBJDEC = '+03:22:30.00' / Requested field J2000.0 Dec. OBJRAD = 334.285714 / [deg] Requested field RA (J2000.0) OBJDECD = 3.375 / [deg] Requested field Dec (J2000.0) PIXSCALE= 1.01 / [arcsec/pix] Pixel scale WCSAXES = 2 EQUINOX = 2000. / [yr] Equatorial coordinates definition LONPOLE = 180. LATPOLE = 0. / IMAGE QUALITY SEEING = 2.04 / [pix] Seeing FWHM PEAKDIST= 0.396793397122491 / [pix] Mean dist brightest pixel-centroid pixel ELLIP = 0.063 / Mean image ellipticity A/B ELLIPPA = 48.65 / [deg] Mean image ellipticity PA FBIAS = 1060.884 / [DN] Floating bias of the image SATURVAL= 17000. / [DN] Saturation value of the CCD array FWHMSEX = 2.45 / [arcsec] SExtractor SEEING estimate MDSKYMAG= 20.53072 / [mag/s-arcsec^2] Median sky obsolete MSMAPCZP= 20.70926 / [mag/s-arcsec^2] Median sky abs. phot. cal. LIMITMAG= 20.92587 / [mag/s-arcsec^2] Limiting magnitude obsolete LMGAPCZP= 21.10442 / [mag/s-arcsec^2] Limiting mag. abs. phot. cal. MEDFWHM = 2.931446 / [arcsecond] Median FWHM MEDELONG= 1.132416 / [dimensionless] Median elongation STDELONG= 0.3298569 / [dimensionless] Std. dev. of elongation MEDTHETA= -42.28234 / [deg] Atan(median sin(theta)/median cos(theta))STDTHETA= 66.21399 / [deg] Atan(stddev sin(theta)/stddev cos(theta))MEDDLMAG= 33.85928 / [mag/s-arcsec^2] Median (MU_MAX-MAG_AUTO) STDDLMAG= 0.4367887 / [mag/s-arcsec^2] Stddev of (MU_MAX-MAG_AUTO) / OBSERVATORY AND TCS OCS_TIME= '2009-06-25T08:41:23.978' / UTC Date for OCS calc time-dep params OPERMODE= 'OCS ' / Mode of operation: OCS | Manual | N/A SOFTVER = '1.1.1.1 ' / Softwere version (TCS.Camera.OCS.Sched) OCS_VER = '1 ' / OCS software version and date TCS_VER = '1 ' / TCS software version and date SCH_VER = '1 ' / OCS-Scheduler software version and date MAT_VER = '7.7.0.471' / Matlab version HDR_VER = '1 ' / Header version TRIGGER = 'N/A ' / trigger ID for TOO, e.g. VOEVENT-Nr TCSMODE = 'Star ' / TCS fundamental mode TCSSMODE= 'Active ' / TCS fundamental submode TCSFMODE= 'Pos ' / TCS focus mode TCSFSMOD= 'On-Target' / TCS focus submode TCSDMODE= 'Stop ' / TCS dome mode TCSDSMOD= 'N/A ' / TCS dome submode TCSWMODE= 'Slave ' / TCS windscreen mode TCSWSMOD= 'N/A ' / TCS windscreen submode OBSLAT = 33.3574 / [deg] Telescope geodetic latitude in WGS84 OBSLON = 116.8599 / [deg] Telescope geodetic longitude in WGS84 OBSALT = 1703.2 / [m] Telescope geodetic altitude in WGS84 DEFOCUS = 0. / [mm] Focus position - nominal focus FOCUSPOS= 1.3851 / [mm] Exposures focusPos DOMESTAT= 'open ' / Dome status at begining of exposure TRACKRA = 23.7 / [arcsec/hr] Track speed RA rel to sidereal TRACKDEC= -11.2 / [arcsec/hr] Track speed Dec rel to sidereal AZIMUTH = 113.8682 / [deg] Telescope Azimuth ALTITUDE= 36.81047 / [deg] Telescope altitude AIRMASS = 1.666232 / Telescope airmass TELRA = 334.402 / [deg] Telescope ap equinox of date RA TELDEC = 3.4225 / [deg] Telescope ap equinox of date Dec TELHA = 312.7096 / [deg] Telescope ap equinox of date HA DOMEAZ = 112.8563 / [deg] Dome azimuth WINDSCAL= 10.466 / [deg] Wind screen altitude WINDDIR = 2.2 / [deg] Azimuth of wind direction WINDSPED= 14.6328 / Wind speed (km/hour) OUTTEMP = 20.94444 / [C] Outside temperature OUTRELHU= 0.09 / [frac] Outside relative humidity OUTDEWPT= -12.94444 / [C] Outside dew point / INSTRUMENT TELEMETRY PANID = '_p48s ' / PAN identification DHSID = '_p48s ' / DHS identification ROISTATE= 'ROI ' / ROI State (FULL | ROI) CCDSEC = '[1:2048,1:4096]' / CCD section CCDSIZE = '[1:2048,1:4096]' / CCD size DATASEC = '[1:2048,1:4096]' / Data section DETSEC = '[1:2048,1:4096]' / Detector section ROISEC = '[1:2048,1:4096]' / ROI section FPA = 'P48MOSAIC' / Focal plan array CCDNAME = 'W94C2 ' / Detector mfg serial number CHECKSUM= 'O3aBP1ZBO1aBO1YB' / HDU checksum updated 2010-03-15T13:06:37 DATASUM = '395763289' / Data unit checksum updated 2010-03-15T13:06:37 DHEINF = 'SDSU, Gen-III' / Controller info DHEFIRM = '/usr/src/dsp/tim_m.lod' / DSP software CAM_VER = '20090615.1.3.100000' / Camera server date.rev.cfitsio LV_VER = '8.5 ' / LabVIEW software version PCI_VER = '2.0c ' / Astropci software version DETID = 'PTF/MOSAIC' / Detector ID AUTHOR = 'PTF/OCS/TCS/Camera' / Source for header information DATAMIN = 0. / Minimum value for array ROISTATE= 'ROI ' / ROI State (FULL | ROI) LEDBLUE = 'OFF ' / 470nm LED state (ON | OFF) LEDRED = 'OFF ' / 660nm LED state (ON | OFF) LEDNIR = 'OFF ' / 880nm LED state (ON | OFF) CCD9TEMP= 175.003 / [K] 0x0 servo temp sensor on CCD09 HSTEMP = 148.207 / [K] 0x1 heat spreader temp DHE0TEMP= 295.951 / [K] 0x2 detector head electronics temp, master DHE1TEMP= 298.162 / [K] 0x3 detector head electronics temp, slave DEWWTEMP= 284.71 / [K] 0x4 dewar wall temp HEADTEMP= 138.414 / [K] 0x5 cryo cooler cold head temp CCD5TEMP= 174.91 / [K] 0x6 temp sensor on CCD05 CCD11TEM= 176.061 / [K] 0x7 temp sensor on CCD11 CCD0TEMP= 169.515 / [K] 0x8 temp sensor on CCD00 RSTEMP = 232.61 / [K] 0x9 temp sensor on radiation shield DEWPRESS= 0.82 / [milli-torr] Dewar pressure DETHEAT = 38. / [%] Detector focal plane heater power NAMPSXY = '6 2 ' / Number of amplifiers in x y CCDSUM = '1 1 ' / [pix] Binning in x and y MODELFOC= 'N/A ' / MODELFOC CHECKSUM= '6aLZ8ZKZ6aKZ6YKZ' / HDU checksum updated 2010-03-15T13:06:37 DATASUM = ' 0' / Data unit checksum (2010-03-15T13:06:37) GAIN = 1.7 / [e-/D.N.] Gain of detector. READNOI = 5.1 / [e-] Read noise of detector. DARKCUR = 0.1 / [e-/s] Dark current of detector / SCAMP DISTORTION KEYWORDS RADECSYS= 'ICRS ' / Astrometric system PV1_0 = 0. / Projection distortion parameter PV1_1 = 1. / Projection distortion parameter PV1_2 = 0. / Projection distortion parameter PV1_4 = 0.000811808026654439 / Projection distortion parameter PV1_5 = 0.000610424561546246 / Projection distortion parameter PV1_6 = 0.000247550637436069 / Projection distortion parameter PV1_7 = 0.000103962986153903 / Projection distortion parameter PV1_8 = -0.000463678684598807 / Projection distortion parameter PV1_9 = -0.000431244263972048 / Projection distortion parameter PV1_10 = -0.000152691163850316 / Projection distortion parameter PV1_12 = -0.00204628855915067 / Projection distortion parameter PV1_13 = -0.00173071932398225 / Projection distortion parameter PV1_14 = 0.000212015319199711 / Projection distortion parameter PV1_15 = -0.000489268678679085 / Projection distortion parameter PV1_16 = -0.000182891514774611 / Projection distortion parameter PV2_0 = 0. / Projection distortion parameter PV2_1 = 1. / Projection distortion parameter PV2_2 = 0. / Projection distortion parameter PV2_4 = 0.000273521447624334 / Projection distortion parameter PV2_5 = 0.000876139200581004 / Projection distortion parameter PV2_6 = -0.000122736852992318 / Projection distortion parameter PV2_7 = -0.00115870481394187 / Projection distortion parameter PV2_8 = 0.000744209714565589 / Projection distortion parameter PV2_9 = -0.00031431316953523 / Projection distortion parameter PV2_10 = -0.00025720525696749 / Projection distortion parameter PV2_12 = -0.00074859772103692 / Projection distortion parameter PV2_13 = 0.000838107200656415 / Projection distortion parameter PV2_14 = -0.00012633881376049 / Projection distortion parameter PV2_15 = -0.0020312867769692 / Projection distortion parameter PV2_16 = 0.00524608854745148 / Projection distortion parameter FGROUPNO= 1 / SCAMP field group label ASTIRMS1= 0. / Astrom. dispersion RMS (intern., high S/N) ASTIRMS2= 0. / Astrom. dispersion RMS (intern., high S/N) ASTRRMS1= 3.620458E-05 / Astrom. dispersion RMS (ref., high S/N) ASTRRMS2= 3.332156E-05 / Astrom. dispersion RMS (ref., high S/N) ASTINST = 1 / SCAMP astrometric instrument label FLXSCALE= 0. / SCAMP relative flux scale MAGZEROP= 0. / SCAMP zero-point PHOTIRMS= 0. / mag dispersion RMS (internal, high S/N) RA_RMS = 0.1655724 / [arcsec] RMS of SCAMP fit from 2MASS matching DEC_RMS = 0.1891921 / [arcsec] RMS of SCAMP fit from 2MASS matching ASTROMN = 384 / Number of stars in SCAMP astrometric solution SCAMPPTH= '/ptf/pos/archive/fallbackcal/scamp/7/' / SCAMP catalog path SCAMPFIL= 'PTF_201006174759_c_e_uca3_t112521_u001916251_f02_p002899_c07.fits' / SIP DISTORTION KEYWORDS A_ORDER = 4 / Distortion order for A A_0_2 = 6.96807813586153E-08 / Projection distortion parameter A_0_3 = 1.20881759870351E-11 / Projection distortion parameter A_0_4 = -4.07297345125509E-15 / Projection distortion parameter A_1_1 = -1.71602989085006E-07 / Projection distortion parameter A_1_2 = -3.40958003336147E-11 / Projection distortion parameter A_1_3 = 1.08769435952671E-14 / Projection distortion parameter A_2_0 = 2.28067760155696E-07 / Projection distortion parameter A_2_1 = 3.6610309234789E-11 / Projection distortion parameter A_2_2 = 4.73755078335384E-15 / Projection distortion parameter A_3_0 = 8.24210855193549E-12 / Projection distortion parameter A_3_1 = 3.84753767306115E-14 / Projection distortion parameter A_4_0 = -4.54223812412034E-14 / Projection distortion parameter A_DMAX = 1.53122472683886 / Projection distortion parameter B_ORDER = 4 / Distortion order for B B_0_2 = -7.69933957449607E-08 / Projection distortion parameter B_0_3 = -9.16855566272424E-11 / Projection distortion parameter B_0_4 = 1.66630509620112E-14 / Projection distortion parameter B_1_1 = 2.46289708854316E-07 / Projection distortion parameter B_1_2 = -5.90207917198792E-11 / Projection distortion parameter B_1_3 = 1.86811615261732E-14 / Projection distortion parameter B_2_0 = 3.44908367419592E-08 / Projection distortion parameter B_2_1 = -2.49509936365959E-11 / Projection distortion parameter B_2_2 = 2.84841315780067E-15 / Projection distortion parameter B_3_0 = 2.02845080441181E-11 / Projection distortion parameter B_3_1 = -4.51317603382652E-14 / Projection distortion parameter B_4_0 = -1.16438849571175E-13 / Projection distortion parameter B_DMAX = 2.89468553502114 / Projection distortion parameter AP_ORDER= 4 / Distortion order for AP AP_0_1 = -2.3927681685928E-08 / Projection distortion parameter AP_0_2 = -6.97379868441328E-08 / Projection distortion parameter AP_0_3 = -1.21069584606865E-11 / Projection distortion parameter AP_0_4 = 4.07524721573973E-15 / Projection distortion parameter AP_1_0 = 5.65239128994064E-08 / Projection distortion parameter AP_1_1 = 1.71734217296344E-07 / Projection distortion parameter AP_1_2 = 3.41724875038451E-11 / Projection distortion parameter AP_1_3 = -1.08775499102067E-14 / Projection distortion parameter AP_2_0 = -2.28068482487158E-07 / Projection distortion parameter AP_2_1 = -3.66548961802381E-11 / Projection distortion parameter AP_2_2 = -4.75858241735224E-15 / Projection distortion parameter AP_3_0 = -8.24781966878619E-12 / Projection distortion parameter AP_3_1 = -3.85281201904104E-14 / Projection distortion parameter AP_4_0 = 4.54275049666924E-14 / Projection distortion parameter BP_ORDER= 4 / Distortion order for BP BP_0_1 = -1.50638746640517E-07 / Projection distortion parameter BP_0_2 = 7.70565767927487E-08 / Projection distortion parameter BP_0_3 = 9.18374546897802E-11 / Projection distortion parameter BP_0_4 = -1.66839467627906E-14 / Projection distortion parameter BP_1_0 = -4.87195269294628E-08 / Projection distortion parameter BP_1_1 = -2.46371690411844E-07 / Projection distortion parameter BP_1_2 = 5.9111535979953E-11 / Projection distortion parameter BP_1_3 = -1.87729776729012E-14 / Projection distortion parameter BP_2_0 = -3.46046151217313E-08 / Projection distortion parameter BP_2_1 = 2.51320825919019E-11 / Projection distortion parameter BP_2_2 = -2.85758325791527E-15 / Projection distortion parameter BP_3_0 = -2.04221364218494E-11 / Projection distortion parameter BP_3_1 = 4.51336286236569E-14 / Projection distortion parameter BP_4_0 = 1.16567578965612E-13 / Projection distortion parameter / DATA FLOW ORIGNAME= '/data/PTF_default_38068.fits' / Filename as written by the camera FILENAME= 'PTF200906253621_2_o_38068.fits' / Filename of delivered camera image PROCORIG= 'IPAC-PTF pipelines' / Processing origin PROCDATE= 'Tue Feb 21 03:34:46 2012' / Processing date/time (Pacific time) PTFVERSN= 5. / Version of PTFSCIENCEPIPELINE program PMASKPTH= '/ptf/pos/archive/fallbackcal/pmasks/' / Pathname of pixel mask PMASKFIL= 'bpm_2009060s_c07.v2.fits' / Filename of pixel mask SFLATPTH= '/ptf/pos/sbx1/2009/06/25/f2/c7/cal/p4/cId45986/' / Pathname of super SFLATFIL= 'PTF_200906250000_i_s_flat_t120000_u000045986_f02_p000000_c07.fits' SBIASPTH= '/ptf/pos/sbx1/2009/06/25/f2/c7/cal/p1/cId45922/' / Pathname of super SBIASFIL= 'PTF_200906250000_i_s_bias_t120000_u000045922_f00_p000000_c07.fits' DBNID = 121 / Database night ID DBEXPID = 22920 / Database exposure ID DBRID = 3663141 / Database raw-image ID DBPID = 12052003 / Database processed-image ID DBFID = 2 / Database filter ID DBPIID = 1 / Database P.I. ID DBPRID = 3 / Database project ID DBFIELD = 22920 / Database field ID DBSVID = 50 / Database software-version ID DBCVID = 56 / Database config-data-file ID END XTENSION= 'IMAGE ' / Image extension BITPIX = 8 / array data type NAXIS = 0 / number of array dimensions PCOUNT = 0 / number of parameters GCOUNT = 1 / number of groups CD1_2 = -3.72E-05 CD1_3 = 0 CD1_1 = -4.12E-05 CUNIT3 = 'HZ ' CUNIT2 = 'deg ' CTYPE1 = 'RA---TAN' CTYPE3 = 'AWAV ' CD2_1 = -3.72E-05 CTYPE2 = 'DEC--TAN' CD2_3 = 0 CD2_2 = 4.12E-05 CUNIT1 = 'deg ' CD3_1 = 0 CD3_2 = 0 CD3_3 = 0.2 END \ No newline at end of file diff --git a/astropy/wcs/tests/data/validate.txt b/astropy/wcs/tests/data/validate.txt new file mode 100644 index 000000000000..34ade07f3cbd --- /dev/null +++ b/astropy/wcs/tests/data/validate.txt @@ -0,0 +1,16 @@ +HDU 1: + WCS key ' ': + - RADECSYS= 'ICRS ' / Astrometric system + RADECSYS is non-standard, use RADESYSa. + - The WCS transformation has more axes (2) than the image it is + associated with (0) + - Removed redundant SCAMP distortion parameters because SIP + parameters are also present + +HDU 2: + WCS key ' ': + - The WCS transformation has more axes (3) than the image it is + associated with (0) + - 'celfix' made the change 'In CUNIT3 : Mismatched units type + 'length': have 'Hz', want 'm''. + - 'unitfix' made the change 'Changed units: 'HZ ' -> 'Hz''. diff --git a/astropy/wcs/tests/data/zpn-hole.hdr b/astropy/wcs/tests/data/zpn-hole.hdr new file mode 100644 index 000000000000..433659863b8e --- /dev/null +++ b/astropy/wcs/tests/data/zpn-hole.hdr @@ -0,0 +1 @@ +NAXIS = 2 NAXIS1 = 200 NAXIS2 = 200 CTYPE1 = 'RA---ZPN' CRPIX1 = 100 CDELT1 = -6.666666666667E-02 CRVAL1 = 0.000000000000E+00 CTYPE2 = 'DEC--ZPN' CRPIX2 = 100 CDELT2 = 6.666666666667E-02 CRVAL2 = -9.000000000000E+01 LONPOLE = 1.800000000000E+02 / Native longitude of celestial pole LATPOLE = -9.000000000000E+01 / Native latitude of celestial pole PV2_0 = 10.000000000000E-02 / Projection parameter 0 PV2_1 = 9.750000000000E-01 / Projection parameter 1 END \ No newline at end of file diff --git a/astropy/wcs/tests/helper.py b/astropy/wcs/tests/helper.py new file mode 100644 index 000000000000..2b2cb846f14b --- /dev/null +++ b/astropy/wcs/tests/helper.py @@ -0,0 +1,116 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +import numpy as np + +from astropy.io import fits + + +class SimModelTAB: + def __init__( + self, + nx=150, + ny=200, + crpix=[1, 1], + crval=[1, 1], + cdelt=[1, 1], + pc={"PC1_1": 1, "PC2_2": 1}, + ): + # set essential parameters of the model (coord transformations): + assert nx > 2 and ny > 1 # a limitation of this particular simulation + self.nx = nx + self.ny = ny + self.crpix = crpix + self.crval = crval + self.cdelt = cdelt + self.pc = pc + + def fwd_eval(self, xy): + xb = 1 + self.nx // 3 + px = np.array([1, xb, xb, self.nx + 1]) + py = np.array([1, self.ny + 1]) + + xi = self.crval[0] + self.cdelt[0] * (px - self.crpix[0]) + yi = self.crval[1] + self.cdelt[1] * (py - self.crpix[1]) + + cx = np.array([0.0, 0.26, 0.8, 1.0]) + cy = np.array([-0.5, 0.5]) + + xy = np.atleast_2d(xy) + x = xy[:, 0] + y = xy[:, 1] + + mbad = (x < px[0]) | (y < py[0]) | (x > px[-1]) | (y > py[-1]) + mgood = np.logical_not(mbad) + + i = 2 * (x > xb).astype(int) + + psix = self.crval[0] + self.cdelt[0] * (x - self.crpix[0]) + psiy = self.crval[1] + self.cdelt[1] * (y - self.crpix[1]) + + cfx = (psix - xi[i]) / (xi[i + 1] - xi[i]) + cfy = (psiy - yi[0]) / (yi[1] - yi[0]) + + ra = cx[i] + cfx * (cx[i + 1] - cx[i]) + dec = cy[0] + cfy * (cy[1] - cy[0]) + + return np.dstack([ra, dec])[0] + + @property + def hdulist(self): + """Simulates 2D data with a _spatial_ WCS that uses the ``-TAB`` + algorithm with indexing. + """ + # coordinate array (some "arbitrary" numbers with a "jump" along x axis): + x = np.array([[0.0, 0.26, 0.8, 1.0], [0.0, 0.26, 0.8, 1.0]]) + y = np.array([[-0.5, -0.5, -0.5, -0.5], [0.5, 0.5, 0.5, 0.5]]) + c = np.dstack([x, y]) + + # index arrays (skip PC matrix for simplicity - assume it is an + # identity matrix): + xb = 1 + self.nx // 3 + px = np.array([1, xb, xb, self.nx + 1]) + py = np.array([1, self.ny + 1]) + xi = self.crval[0] + self.cdelt[0] * (px - self.crpix[0]) + yi = self.crval[1] + self.cdelt[1] * (py - self.crpix[1]) + + # structured array (data) for binary table HDU: + arr = np.array( + [(c, xi, yi)], + dtype=[ + ("wavelength", np.float64, c.shape), + ("xi", np.double, (xi.size,)), + ("yi", np.double, (yi.size,)), + ], + ) + + # create binary table HDU: + bt = fits.BinTableHDU(arr) + bt.header["EXTNAME"] = "WCS-TABLE" + + # create primary header: + image_data = np.ones((self.ny, self.nx), dtype=np.float32) + pu = fits.PrimaryHDU(image_data) + pu.header["ctype1"] = "RA---TAB" + pu.header["ctype2"] = "DEC--TAB" + pu.header["naxis1"] = self.nx + pu.header["naxis2"] = self.ny + pu.header["PS1_0"] = "WCS-TABLE" + pu.header["PS2_0"] = "WCS-TABLE" + pu.header["PS1_1"] = "wavelength" + pu.header["PS2_1"] = "wavelength" + pu.header["PV1_3"] = 1 + pu.header["PV2_3"] = 2 + pu.header["CUNIT1"] = "deg" + pu.header["CUNIT2"] = "deg" + pu.header["CDELT1"] = self.cdelt[0] + pu.header["CDELT2"] = self.cdelt[1] + pu.header["CRPIX1"] = self.crpix[0] + pu.header["CRPIX2"] = self.crpix[1] + pu.header["CRVAL1"] = self.crval[0] + pu.header["CRVAL2"] = self.crval[1] + pu.header["PS1_2"] = "xi" + pu.header["PS2_2"] = "yi" + for k, v in self.pc.items(): + pu.header[k] = v + + hdulist = fits.HDUList([pu, bt]) + return hdulist diff --git a/astropy/wcs/tests/test_auxprm.py b/astropy/wcs/tests/test_auxprm.py new file mode 100644 index 000000000000..165cfb7da605 --- /dev/null +++ b/astropy/wcs/tests/test_auxprm.py @@ -0,0 +1,198 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +# Tests for the auxiliary parameters contained in wcsaux + +from numpy.testing import assert_allclose + +from astropy.io import fits +from astropy.wcs import WCS + +STR_PLANETARY_EXPECTED_EMPTY = """ +a_radius: +b_radius: +c_radius: +bdis_obs: +blon_obs: +blat_obs:""".lstrip() + + +STR_SOLAR_EXPECTED_EMPTY = """ +rsun_ref: +dsun_obs: +crln_obs: +hgln_obs: +hglt_obs:""".lstrip() + + +STR_EXPECTED_EMPTY = STR_SOLAR_EXPECTED_EMPTY + "\n" + STR_PLANETARY_EXPECTED_EMPTY + + +def test_empty(): + w = WCS(naxis=1) + assert w.wcs.aux.rsun_ref is None + assert w.wcs.aux.dsun_obs is None + assert w.wcs.aux.crln_obs is None + assert w.wcs.aux.hgln_obs is None + assert w.wcs.aux.hglt_obs is None + assert w.wcs.aux.a_radius is None + assert w.wcs.aux.b_radius is None + assert w.wcs.aux.c_radius is None + assert w.wcs.aux.bdis_obs is None + assert w.wcs.aux.blon_obs is None + assert w.wcs.aux.blat_obs is None + assert str(w.wcs.aux) == STR_EXPECTED_EMPTY + + +HEADER_SOLAR = fits.Header.fromstring( + """ +WCSAXES = 2 / Number of coordinate axes +CRPIX1 = 64.5 / Pixel coordinate of reference point +CRPIX2 = 64.5 / Pixel coordinate of reference point +PC1_1 = 0.99999994260024 / Coordinate transformation matrix element +PC1_2 = -0.00033882076120692 / Coordinate transformation matrix element +PC2_1 = 0.00033882076120692 / Coordinate transformation matrix element +PC2_2 = 0.99999994260024 / Coordinate transformation matrix element +CDELT1 = 0.0053287911111111 / [deg] Coordinate increment at reference point +CDELT2 = 0.0053287911111111 / [deg] Coordinate increment at reference point +CUNIT1 = 'deg' / Units of coordinate increment and value +CUNIT2 = 'deg' / Units of coordinate increment and value +CTYPE1 = 'HPLN-TAN' / Coordinate type codegnomonic projection +CTYPE2 = 'HPLT-TAN' / Coordinate type codegnomonic projection +CRVAL1 = -0.0012589367249586 / [deg] Coordinate value at reference point +CRVAL2 = 0.00079599300143911 / [deg] Coordinate value at reference point +LONPOLE = 180.0 / [deg] Native longitude of celestial pole +LATPOLE = 0.00079599300143911 / [deg] Native latitude of celestial pole +DATE-OBS= '2011-02-15T00:00:00.34' / ISO-8601 time of observation +MJD-OBS = 55607.000003935 / [d] MJD at start of observation +RSUN_REF= 696000000.0 / [m] Solar radius +DSUN_OBS= 147724815128.0 / [m] Distance from centre of Sun to observer +CRLN_OBS= 22.814522 / [deg] Carrington heliographic lng of observer +CRLT_OBS= -6.820544 / [deg] Heliographic latitude of observer +HGLN_OBS= 8.431123 / [deg] Stonyhurst heliographic lng of observer +HGLT_OBS= -6.820544 / [deg] Heliographic latitude of observer +""".lstrip(), + sep="\n", +) + + +STR_EXPECTED_GET = ( + """ +rsun_ref: 696000000.000000 +dsun_obs: 147724815128.000000 +crln_obs: 22.814522 +hgln_obs: 8.431123 +hglt_obs: -6.820544""".lstrip() + + "\n" + + STR_PLANETARY_EXPECTED_EMPTY +) + + +def test_solar_aux_get(): + w = WCS(HEADER_SOLAR) + assert_allclose(w.wcs.aux.rsun_ref, 696000000) + assert_allclose(w.wcs.aux.dsun_obs, 147724815128) + assert_allclose(w.wcs.aux.crln_obs, 22.814522) + assert_allclose(w.wcs.aux.hgln_obs, 8.431123) + assert_allclose(w.wcs.aux.hglt_obs, -6.820544) + assert str(w.wcs.aux) == STR_EXPECTED_GET + + +STR_EXPECTED_SET = ( + """ +rsun_ref: 698000000.000000 +dsun_obs: 140000000000.000000 +crln_obs: 10.000000 +hgln_obs: 30.000000 +hglt_obs: 40.000000""".lstrip() + + "\n" + + STR_PLANETARY_EXPECTED_EMPTY +) + + +def test_solar_aux_set(): + w = WCS(HEADER_SOLAR) + + w.wcs.aux.rsun_ref = 698000000 + assert_allclose(w.wcs.aux.rsun_ref, 698000000) + + w.wcs.aux.dsun_obs = 140000000000 + assert_allclose(w.wcs.aux.dsun_obs, 140000000000) + + w.wcs.aux.crln_obs = 10.0 + assert_allclose(w.wcs.aux.crln_obs, 10.0) + + w.wcs.aux.hgln_obs = 30.0 + assert_allclose(w.wcs.aux.hgln_obs, 30.0) + + w.wcs.aux.hglt_obs = 40.0 + assert_allclose(w.wcs.aux.hglt_obs, 40.0) + + assert str(w.wcs.aux) == STR_EXPECTED_SET + + header = w.to_header() + assert_allclose(header["RSUN_REF"], 698000000) + assert_allclose(header["DSUN_OBS"], 140000000000) + assert_allclose(header["CRLN_OBS"], 10.0) + assert_allclose(header["HGLN_OBS"], 30.0) + assert_allclose(header["HGLT_OBS"], 40.0) + + +def test_set_aux_on_empty(): + w = WCS(naxis=2) + + w.wcs.aux.rsun_ref = 698000000 + assert_allclose(w.wcs.aux.rsun_ref, 698000000) + + w.wcs.aux.dsun_obs = 140000000000 + assert_allclose(w.wcs.aux.dsun_obs, 140000000000) + + w.wcs.aux.crln_obs = 10.0 + assert_allclose(w.wcs.aux.crln_obs, 10.0) + + w.wcs.aux.hgln_obs = 30.0 + assert_allclose(w.wcs.aux.hgln_obs, 30.0) + + w.wcs.aux.hglt_obs = 40.0 + assert_allclose(w.wcs.aux.hglt_obs, 40.0) + + assert str(w.wcs.aux) == STR_EXPECTED_SET + + header = w.to_header() + assert_allclose(header["RSUN_REF"], 698000000) + assert_allclose(header["DSUN_OBS"], 140000000000) + assert_allclose(header["CRLN_OBS"], 10.0) + assert_allclose(header["HGLN_OBS"], 30.0) + assert_allclose(header["HGLT_OBS"], 40.0) + + +def test_unset_aux(): + w = WCS(HEADER_SOLAR) + + assert w.wcs.aux.rsun_ref is not None + w.wcs.aux.rsun_ref = None + assert w.wcs.aux.rsun_ref is None + + assert w.wcs.aux.dsun_obs is not None + w.wcs.aux.dsun_obs = None + assert w.wcs.aux.dsun_obs is None + + assert w.wcs.aux.crln_obs is not None + w.wcs.aux.crln_obs = None + assert w.wcs.aux.crln_obs is None + + assert w.wcs.aux.hgln_obs is not None + w.wcs.aux.hgln_obs = None + assert w.wcs.aux.hgln_obs is None + + assert w.wcs.aux.hglt_obs is not None + w.wcs.aux.hglt_obs = None + assert w.wcs.aux.hglt_obs is None + + assert str(w.wcs.aux) == STR_EXPECTED_EMPTY + + header = w.to_header() + assert "RSUN_REF" not in header + assert "DSUN_OBS" not in header + assert "CRLN_OBS" not in header + assert "HGLN_OBS" not in header + assert "HGLT_OBS" not in header diff --git a/astropy/wcs/tests/test_celprm.py b/astropy/wcs/tests/test_celprm.py new file mode 100644 index 000000000000..5d5f07cdc862 --- /dev/null +++ b/astropy/wcs/tests/test_celprm.py @@ -0,0 +1,143 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +from copy import copy, deepcopy + +import numpy as np +import pytest + +from astropy import wcs + +_WCS_UNDEFINED = 987654321.0e99 + + +def test_celprm_init(): + # test PyCelprm_cnew + assert wcs.WCS().wcs.cel + + # test PyCelprm_new + assert wcs.Celprm() + + with pytest.raises(wcs.InvalidPrjParametersError): + cel = wcs.Celprm() + cel.set() + + # test deletion does not crash + cel = wcs.Celprm() + del cel + + +def test_celprm_copy(): + # shallow copy + cel = wcs.Celprm() + cel2 = copy(cel) + cel3 = copy(cel2) + cel.ref = [6, 8, 18, 3] + assert np.allclose(cel.ref, cel2.ref, atol=1e-12, rtol=0) and np.allclose( + cel.ref, cel3.ref, atol=1e-12, rtol=0 + ) + del cel, cel2, cel3 + + # deep copy + cel = wcs.Celprm() + cel2 = deepcopy(cel) + cel.ref = [6, 8, 18, 3] + assert not np.allclose(cel.ref, cel2.ref, atol=1e-12, rtol=0) + del cel, cel2 + + +def test_celprm_offset(): + cel = wcs.Celprm() + assert not cel.offset + cel.offset = True + assert cel.offset + + +def test_celprm_prj(): + cel = wcs.Celprm() + assert cel.prj is not None + cel.prj.code = "TAN" + cel.set() + assert cel._flag + + +def test_celprm_phi0(): + cel = wcs.Celprm() + cel.prj.code = "TAN" + + assert cel.phi0 == None + assert cel._flag == 0 + cel.set() + assert cel.phi0 == 0.0 + + cel.phi0 = 0.0 + assert cel._flag + + cel.phi0 = 2.0 + assert cel._flag == 0 + + cel.phi0 = None + assert cel.phi0 == None + assert cel._flag == 0 + + +def test_celprm_theta0(): + cel = wcs.Celprm() + cel.prj.code = "TAN" + + assert cel.theta0 == None + assert cel._flag == 0 + + cel.theta0 = 4.0 + cel.set() + assert cel.theta0 == 4.0 + + cel.theta0 = 4.0 + assert cel._flag + + cel.theta0 = 8.0 + assert cel._flag == 0 + + cel.theta0 = None + assert cel.theta0 == None + assert cel._flag == 0 + + +def test_celprm_ref(): + cel = wcs.Celprm() + cel.prj.code = "TAN" + cel.set() + + assert np.allclose(cel.ref, [0.0, 0.0, 180.0, 0.0], atol=1e-12, rtol=0) + + cel.phi0 = 2.0 + cel.theta0 = 4.0 + cel.ref = [123, 12] + cel.set() + + assert np.allclose(cel.ref, [123.0, 12.0, 2, 82], atol=1e-12, rtol=0) + + cel.ref = [None, 13, None, None] + assert np.allclose(cel.ref, [123.0, 13.0, 2, 82], atol=1e-12, rtol=0) + + +def test_celprm_isolat(): + cel = wcs.Celprm() + cel.prj.code = "TAN" + cel.set() + + assert cel.isolat == 0 + + +def test_celprm_latpreq(): + cel = wcs.Celprm() + cel.prj.code = "TAN" + cel.set() + + assert cel.latpreq == 0 + + +def test_celprm_euler(): + cel = wcs.Celprm() + cel.prj.code = "TAN" + cel.set() + + assert np.allclose(cel.euler, [0.0, 90.0, 180.0, 0.0, 1.0], atol=1e-12, rtol=0) diff --git a/astropy/wcs/tests/test_pickle.py b/astropy/wcs/tests/test_pickle.py index ec67c5c3ee7b..07c26c710fc9 100644 --- a/astropy/wcs/tests/test_pickle.py +++ b/astropy/wcs/tests/test_pickle.py @@ -1,90 +1,204 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + import os -import cPickle +import pickle import numpy as np +import pytest from numpy.testing import assert_array_almost_equal -from ...config import get_data_contents, get_data_fileobj -from ...io import fits -from ...tests.helper import pytest -from ... import wcs +from astropy import wcs +from astropy.io import fits +from astropy.utils.data import ( + get_pkg_data_contents, + get_pkg_data_filename, + get_pkg_data_fileobj, +) +from astropy.utils.exceptions import AstropyDeprecationWarning +from astropy.utils.misc import NumpyRNGContext +from astropy.wcs.wcs import FITSFixedWarning def test_basic(): wcs1 = wcs.WCS() - s = cPickle.dumps(wcs1) - wcs2 = cPickle.loads(s) + s = pickle.dumps(wcs1) + pickle.loads(s) def test_dist(): - hdulist = fits.open(get_data_fileobj(os.path.join("data", "dist.fits"))) - wcs1 = wcs.WCS(hdulist[0].header, hdulist) - assert wcs1.det2im2 is not None - s = cPickle.dumps(wcs1) - wcs2 = cPickle.loads(s) - - x = np.random.rand(2 ** 16, wcs1.wcs.naxis) - world1 = wcs1.all_pix2world(x, 1) - world2 = wcs2.all_pix2world(x, 1) - - assert_array_almost_equal(world1, world2) + with get_pkg_data_fileobj( + os.path.join("data", "dist.fits"), encoding="binary" + ) as test_file: + hdulist = fits.open(test_file) + # The use of ``AXISCORR`` for D2IM correction has been deprecated + with ( + pytest.warns(AstropyDeprecationWarning), + pytest.warns( + wcs.FITSFixedWarning, + match="The WCS transformation has more axes", + ), + ): + wcs1 = wcs.WCS(hdulist[0].header, hdulist) + assert wcs1.det2im2 is not None + + s = pickle.dumps(wcs1) + wcs2 = pickle.loads(s) + + with NumpyRNGContext(123456789): + x = np.random.rand(2**16, wcs1.wcs.naxis) + world1 = wcs1.all_pix2world(x, 1) + world2 = wcs2.all_pix2world(x, 1) + + assert_array_almost_equal(world1, world2) def test_sip(): - hdulist = fits.open(get_data_fileobj(os.path.join("data", "sip.fits")), - ignore_missing_end=True) - wcs1 = wcs.WCS(hdulist[0].header) - assert wcs1.sip is not None - s = cPickle.dumps(wcs1) - wcs2 = cPickle.loads(s) + with get_pkg_data_fileobj( + os.path.join("data", "sip.fits"), encoding="binary" + ) as test_file: + hdulist = fits.open(test_file, ignore_missing_end=True) + with pytest.warns(FITSFixedWarning): + wcs1 = wcs.WCS(hdulist[0].header) + assert wcs1.sip is not None + s = pickle.dumps(wcs1) + wcs2 = pickle.loads(s) - x = np.random.rand(2 ** 16, wcs1.wcs.naxis) - world1 = wcs1.all_pix2world(x, 1) - world2 = wcs2.all_pix2world(x, 1) + with NumpyRNGContext(123456789): + x = np.random.rand(2**16, wcs1.wcs.naxis) + world1 = wcs1.all_pix2world(x, 1) + world2 = wcs2.all_pix2world(x, 1) - assert_array_almost_equal(world1, world2) + assert_array_almost_equal(world1, world2) def test_sip2(): - hdulist = fits.open(get_data_fileobj(os.path.join("data", "sip2.fits")), - ignore_missing_end=True) - wcs1 = wcs.WCS(hdulist[0].header) - assert wcs1.sip is not None - s = cPickle.dumps(wcs1) - wcs2 = cPickle.loads(s) - - x = np.random.rand(2 ** 16, wcs1.wcs.naxis) - world1 = wcs1.all_pix2world(x, 1) - world2 = wcs2.all_pix2world(x, 1) - - assert_array_almost_equal(world1, world2) - - + with get_pkg_data_fileobj( + os.path.join("data", "sip2.fits"), encoding="binary" + ) as test_file: + hdulist = fits.open(test_file, ignore_missing_end=True) + with pytest.warns(FITSFixedWarning): + wcs1 = wcs.WCS(hdulist[0].header) + assert wcs1.sip is not None + s = pickle.dumps(wcs1) + wcs2 = pickle.loads(s) + + with NumpyRNGContext(123456789): + x = np.random.rand(2**16, wcs1.wcs.naxis) + world1 = wcs1.all_pix2world(x, 1) + world2 = wcs2.all_pix2world(x, 1) + + assert_array_almost_equal(world1, world2) + + +# Ignore "PV2_2 = 0.209028857410973 invalid keyvalue" warning seen on Windows. +@pytest.mark.filterwarnings(r"ignore:PV2_2") def test_wcs(): - header = get_data_contents(os.path.join("data", "outside_sky.hdr")) + header = get_pkg_data_contents( + os.path.join("data", "outside_sky.hdr"), encoding="binary" + ) wcs1 = wcs.WCS(header) - s = cPickle.dumps(wcs1) - wcs2 = cPickle.loads(s) + s = pickle.dumps(wcs1) + wcs2 = pickle.loads(s) - x = np.random.rand(2 ** 16, wcs1.wcs.naxis) - world1 = wcs1.all_pix2world(x, 1) - world2 = wcs2.all_pix2world(x, 1) + with NumpyRNGContext(123456789): + x = np.random.rand(2**16, wcs1.wcs.naxis) + world1 = wcs1.all_pix2world(x, 1) + world2 = wcs2.all_pix2world(x, 1) assert_array_almost_equal(world1, world2) class Sub(wcs.WCS): def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) self.foo = 42 def test_subclass(): - wcs = Sub() - s = cPickle.dumps(wcs) - wcs2 = cPickle.loads(s) + wcs1 = Sub() + wcs1.foo = 45 + s = pickle.dumps(wcs1) + wcs2 = pickle.loads(s) assert isinstance(wcs2, Sub) - assert wcs.foo == 42 - assert wcs2.foo == 42 + assert wcs1.foo == 45 + assert wcs2.foo == 45 assert wcs2.wcs is not None + + +def test_axes_info(): + w = wcs.WCS(naxis=3) + w.pixel_shape = [100, 200, 300] + w.pixel_bounds = ((11, 22), (33, 45), (55, 67)) + w.extra = 111 + + w2 = pickle.loads(pickle.dumps(w)) + + # explicitly test naxis-related info + assert w.naxis == w2.naxis + assert w.pixel_shape == w2.pixel_shape + assert w.pixel_bounds == w2.pixel_bounds + + # test all attributes + for k, v in w.__dict__.items(): + assert getattr(w2, k) == v + + +def test_pixlist_wcs_colsel(): + """ + Test selection of a specific pixel list WCS using ``colsel``. See #11412. + """ + hdr_file = get_pkg_data_filename("data/chandra-pixlist-wcs.hdr") + hdr = fits.Header.fromtextfile(hdr_file) + with pytest.warns(wcs.FITSFixedWarning): + w0 = wcs.WCS(hdr, keysel=["image", "pixel"], colsel=[11, 12]) + + with pytest.warns(wcs.FITSFixedWarning): + w = pickle.loads(pickle.dumps(w0)) + + assert w.naxis == 2 + assert list(w.wcs.ctype) == ["RA---TAN", "DEC--TAN"] + assert np.allclose(w.wcs.crval, [229.38051931869, -58.81108068885]) + assert np.allclose(w.wcs.pc, [[1, 0], [0, 1]]) + assert np.allclose(w.wcs.cdelt, [-0.00013666666666666, 0.00013666666666666]) + assert np.allclose(w.wcs.lonpole, 180.0) + + +def test_alt_wcskey(): + w = wcs.WCS(key="A") + w2 = pickle.loads(pickle.dumps(w)) + + assert w2.wcs.alt == "A" + + +@pytest.mark.parametrize("preserve_units", [False, True]) +def test_preserve_units(preserve_units): + # Use non-SI units so that the world coordinates returned by + # ``wcs_pix2world`` differ between ``preserve_units=True`` (arcsec) and + # ``preserve_units=False`` (converted to deg). This ensures the pickle + # round-trip is exercising the actual transformation behavior, not just + # the stored flag. + header = fits.Header.fromstring( + "WCSAXES = 2\n" + "CTYPE1 = 'RA---TAN'\n" + "CTYPE2 = 'DEC--TAN'\n" + "CUNIT1 = 'arcsec'\n" + "CUNIT2 = 'arcsec'\n" + "CRVAL1 = 4\n" + "CRVAL2 = 6\n" + "CRPIX1 = 1\n" + "CRPIX2 = 1\n" + "CDELT1 = 4\n" + "CDELT2 = 2\n", + sep="\n", + ) + w = wcs.WCS(header, preserve_units=preserve_units) + assert w._preserve_units is preserve_units + + coords = np.array([[1.0, 2.0], [3.0, 4.0]]) + expected = w.wcs_pix2world(coords, 0) + + w2 = pickle.loads(pickle.dumps(w)) + assert w2._preserve_units is preserve_units + assert_array_almost_equal(w2.wcs_pix2world(coords, 0), expected) diff --git a/astropy/wcs/tests/test_prjprm.py b/astropy/wcs/tests/test_prjprm.py new file mode 100644 index 000000000000..e1e18f5e8ba1 --- /dev/null +++ b/astropy/wcs/tests/test_prjprm.py @@ -0,0 +1,247 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +from copy import copy, deepcopy + +import numpy as np +import pytest + +from astropy import wcs + + +def test_prjprm_init(): + # test PyPrjprm_cnew + assert wcs.WCS().wcs.cel.prj + + # test PyPrjprm_new + assert wcs.Prjprm() + + with pytest.raises(wcs.InvalidPrjParametersError): + prj = wcs.Prjprm() + prj.set() + + # test deletion does not crash + prj = wcs.Prjprm() + del prj + + +def test_prjprm_copy(): + # shallow copy + prj = wcs.Prjprm() + prj2 = copy(prj) + prj3 = copy(prj2) + prj.pv = [0, 6, 8, 18, 3] + assert np.allclose(prj.pv, prj2.pv, atol=1e-12, rtol=0) and np.allclose( + prj.pv, prj3.pv, atol=1e-12, rtol=0 + ) + del prj, prj2, prj3 + + # deep copy + prj = wcs.Prjprm() + prj2 = deepcopy(prj) + prj.pv = [0, 6, 8, 18, 3] + assert not np.allclose(prj.pv, prj2.pv, atol=1e-12, rtol=0) + del prj, prj2 + + +def test_prjprm_flag(): + prj = wcs.Prjprm() + assert prj._flag == 0 + + +def test_prjprm_code(): + prj = wcs.Prjprm() + + assert prj.code == " " + assert prj._flag == 0 + + prj.code = "TAN" + prj.set() + assert prj.code == "TAN" + assert prj._flag + + prj.code = "TAN" + assert prj._flag + + prj.code = None + assert prj.code == " " + assert prj._flag == 0 + + +def test_prjprm_phi0(): + prj = wcs.Prjprm() + + assert prj.phi0 == None + assert prj._flag == 0 + + prj.code = "TAN" + prj.phi0 = 2.0 + prj.set() + assert prj.phi0 == 0 + + prj.phi0 = 0.0 + assert prj._flag + + prj.phi0 = 2.0 + assert prj._flag == 0 + + prj.phi0 = None + assert prj.phi0 == None + assert prj._flag == 0 + + +def test_prjprm_theta0(): + prj = wcs.Prjprm() + + assert prj.theta0 == None + assert prj._flag == 0 + + prj.code = "TAN" + prj.phi0 = 2.0 + prj.theta0 = 4.0 + prj.set() + assert prj.theta0 == 4.0 + + prj.theta0 = 4.0 + assert prj._flag + + prj.theta0 = 8.0 + assert prj._flag == 0 + + prj.theta0 = None + assert prj.theta0 == None + assert prj._flag == 0 + + +def test_prjprm_pv(): + prj = wcs.Prjprm() + + assert prj.pv.size == wcs.PRJ_PVN + + assert prj.theta0 == None + assert prj._flag == 0 + + prj.code = "ZPN" + prj.phi0 = 2.0 + prj.theta0 = 4.0 + pv = [float(i) if (i % 2) else i for i in range(wcs.PRJ_PVN)] + prj.pv = pv + prj.set() + assert prj.phi0 == 2.0 + assert prj.theta0 == 4.0 + assert np.allclose(prj.pv, pv, atol=1e-6, rtol=0) + + # test the same with numpy.ndarray (flag not cleared for same values): + prj.pv = prj.pv + assert prj._flag != 0 + prj.set() + assert np.allclose(prj.pv, pv, atol=1e-6, rtol=0) + + # test the same but modify PV values to check that flag is cleared: + prj.pv = np.array(pv) + 2e-7 + assert prj._flag == 0 + prj.set() + assert np.allclose(prj.pv, pv, atol=1e-6, rtol=0) + + # check that None in pv list leave value unchanged and values not set + # by the list are left unchanged: + prj.code = "SZP" + prj.pv = [0.0, 99.0, None] + assert np.allclose(prj.pv[:4], [0.0, 99.0, 2.0, 3.0], atol=1e-6, rtol=0) + + # check that None resets PV to uninitialized (before prjset()) values: + prj.pv = None + assert prj.pv[0] == 0.0 + assert np.all(np.isnan(prj.pv[1:4])) + assert np.allclose(prj.pv[5:], 0, atol=0, rtol=0) + + # check we can set all PVs to nan: + nan_pvs = wcs.PRJ_PVN * [np.nan] + prj.code = "TAN" + prj.pv = nan_pvs # set using a list + prj.set() + assert np.all(np.isnan(prj.pv)) + prj.pv = np.array(nan_pvs) # set using a numpy.ndarray + prj.set() + assert np.all(np.isnan(prj.pv)) + + +def test_prjprm_pvrange(): + prj = wcs.Prjprm() + prj.code = "ZPN" + prj.phi0 = 2.0 + prj.theta0 = 4.0 + prj.pv = [0.0, 1.0, 2.0, 3.0] + prj.set() + assert prj.pvrange == wcs.PRJ_PVN + + prj.code = "SZP" + prj.set() + assert prj.pvrange == 103 + + +def test_prjprm_bounds(prj_TAB): + assert prj_TAB.bounds == 7 + prj_TAB.bounds = 0 + assert prj_TAB.bounds == 0 + + +def test_prjprm_category(prj_TAB): + assert prj_TAB.category == wcs.PRJ_ZENITHAL + + +def test_prjprm_name(prj_TAB): + assert prj_TAB.name + + +def test_prjprm_w(prj_TAB): + assert np.all(np.isfinite(prj_TAB.w)) + + +def test_prjprm_simplezen(prj_TAB): + assert prj_TAB.simplezen == 1 + + +def test_prjprm_equiareal(prj_TAB): + assert prj_TAB.equiareal == 0 + + +def test_prjprm_conformal(prj_TAB): + assert prj_TAB.conformal == 0 + + +def test_prjprm_global_projection(prj_TAB): + assert prj_TAB.global_projection == 0 + + +def test_prjprm_divergent(prj_TAB): + assert prj_TAB.divergent == 1 + + +def test_prjprm_r0(prj_TAB): + assert prj_TAB.r0 > 0.0 + + +def test_prjprm_x0_y0(prj_TAB): + assert prj_TAB.x0 == 0.0 + assert prj_TAB.y0 == 0.0 + + +def test_prjprm_n_m(prj_TAB): + assert prj_TAB.n == 0 + assert prj_TAB.m == 0 + + +def test_prjprm_prj_roundtrips(prj_TAB): + # some random values: + x = [-0.002, 0.014, -0.003, 0.015, -0.047, -0.029, -0.042, 0.027, 0.021] + y = [0.022, -0.017, -0.048, -0.049, -0.043, 0.015, 0.046, 0.031, 0.011] + + xr, yr = prj_TAB.prjs2x(*prj_TAB.prjx2s(x, y)) + assert np.allclose(x, xr, atol=1e-12, rtol=0) + assert np.allclose(y, yr, atol=1e-12, rtol=0) + + # same test for a Prjprm that was not previously explicitly "set": + prj = wcs.Prjprm() + prj.code = "TAN" + xr, yr = prj.prjs2x(*prj.prjx2s(x, y)) + assert np.allclose(x, xr, atol=1e-12, rtol=0) + assert np.allclose(y, yr, atol=1e-12, rtol=0) diff --git a/astropy/wcs/tests/test_profiling.py b/astropy/wcs/tests/test_profiling.py index 8bc517befc5e..ebf2ffe53b41 100644 --- a/astropy/wcs/tests/test_profiling.py +++ b/astropy/wcs/tests/test_profiling.py @@ -1,88 +1,79 @@ -import glob +# Licensed under a 3-clause BSD style license - see LICENSE.rst + import os -import sys import numpy as np -from numpy.testing import assert_array_almost_equal - -from ...config import get_data_filenames, get_data_contents - -from ... import wcs - - -def test_maps(): - def test_map(filename): - header = get_data_contents(os.path.join("maps", filename)) - wcsobj = wcs.WCS(header) - - x = np.random.rand(2 ** 12, wcsobj.wcs.naxis) - world = wcsobj.wcs_pix2world(x, 1) - pix = wcsobj.wcs_world2pix(x, 1) +import pytest - hdr_file_list = list(get_data_filenames("maps", "*.hdr")) +from astropy import wcs +from astropy.utils.data import get_pkg_data_contents, get_pkg_data_filenames +from astropy.utils.misc import NumpyRNGContext +from astropy.wcs.wcs import FITSFixedWarning - # actually perform a test for each one - for filename in hdr_file_list: +# use the base name of the file, because everything we yield +# will show up in the test name in the pandokia report +hdr_map_file_list = [ + os.path.basename(fname) + for fname in get_pkg_data_filenames("data/maps", pattern="*.hdr") +] - # use the base name of the file, because everything we yield - # will show up in the test name in the pandokia report - filename = os.path.basename(filename) +# Checking the number of files before reading them in. +# OLD COMMENTS: +# AFTER we tested with every file that we found, check to see that we +# actually have the list we expect. If N=0, we will not have performed +# any tests at all. If N < n_data_files, we are missing some files, +# so we will have skipped some tests. Without this check, both cases +# happen silently! - # yield a function name and parameters to make a generated test - yield test_map, filename - # AFTER we tested with every file that we found, check to see that we - # actually have the list we expect. If N=0, we will not have performed - # any tests at all. If N < n_data_files, we are missing some files, - # so we will have skipped some tests. Without this check, both cases - # happen silently! +def test_read_map_files(): + # how many map files we expect to see + n_map_files = 28 - # how many do we expect to see? - n_data_files = 28 + assert len(hdr_map_file_list) == n_map_files, ( + "test_read_map_files has wrong number data files: found" + f" {len(hdr_map_file_list)}, expected {n_map_files}" + ) - if len(hdr_file_list) != n_data_files: - assert False, ( - "test_maps has wrong number data files: found %d, expected " - " %d" % ( - len(hdr_file_list), n_data_files)) - # b.t.w. If this assert happens, py.test reports one more test - # than it would have otherwise. +@pytest.mark.parametrize("filename", hdr_map_file_list) +def test_map(filename): + header = get_pkg_data_contents(os.path.join("data/maps", filename)) + wcsobj = wcs.WCS(header) -def test_spectra(): - def test_spectrum(filename): - header = get_data_contents(os.path.join("spectra", filename)) - wcsobj = wcs.WCS(header) + with NumpyRNGContext(123456789): + x = np.random.rand(2**12, wcsobj.wcs.naxis) + wcsobj.wcs_pix2world(x, 1) + wcsobj.wcs_world2pix(x, 1) - x = np.random.rand(2 ** 16, wcsobj.wcs.naxis) - world = wcsobj.wcs_pix2world(x, 1) - pix = wcsobj.wcs_world2pix(x, 1) - hdr_file_list = list(get_data_filenames("spectra", "*.hdr")) +hdr_spec_file_list = [ + os.path.basename(fname) + for fname in get_pkg_data_filenames("data/spectra", pattern="*.hdr") +] - # actually perform a test for each one - for filename in hdr_file_list: - # use the base name of the file, because everything we yield - # will show up in the test name in the pandokia report - filename = os.path.basename(filename) +def test_read_spec_files(): + # how many spec files expected + n_spec_files = 6 - # yield a function name and parameters to make a generated test - yield test_spectrum, filename + assert len(hdr_spec_file_list) == n_spec_files, ( + f"test_spectra has wrong number data files: found {len(hdr_spec_file_list)}," + f" expected {n_spec_files}" + ) + # b.t.w. If this assert happens, pytest reports one more test + # than it would have otherwise. - # AFTER we tested with every file that we found, check to see that we - # actually have the list we expect. If N=0, we will not have performed - # any tests at all. If N < n_data_files, we are missing some files, - # so we will have skipped some tests. Without this check, both cases - # happen silently! - # how many do we expect to see? - n_data_files = 6 - - if len(hdr_file_list) != n_data_files: - assert False, ( - "test_spectra has wrong number data files: found %d, expected " - " %d" % ( - len(hdr_file_list), n_data_files)) - # b.t.w. If this assert happens, py.test reports one more test - # than it would have otherwise. +@pytest.mark.parametrize("filename", hdr_spec_file_list) +def test_spectrum(filename): + header = get_pkg_data_contents(os.path.join("data", "spectra", filename)) + # Warning only pops up for one of the inputs. + with pytest.warns() as warning_lines: + wcsobj = wcs.WCS(header) + for w in warning_lines: + assert issubclass(w.category, FITSFixedWarning) + with NumpyRNGContext(123456789): + x = np.random.rand(2**16, wcsobj.wcs.naxis) + wcsobj.wcs_pix2world(x, 1) + wcsobj.wcs_world2pix(x, 1) diff --git a/astropy/wcs/tests/test_tab.py b/astropy/wcs/tests/test_tab.py new file mode 100644 index 000000000000..88d14381bbff --- /dev/null +++ b/astropy/wcs/tests/test_tab.py @@ -0,0 +1,106 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +from copy import deepcopy + +import numpy as np +import pytest +from packaging.version import Version + +from astropy import wcs +from astropy.io import fits +from astropy.utils.data import get_pkg_data_filename +from astropy.wcs.wcs import WCSLIB_VERSION + +from .helper import SimModelTAB + + +def test_2d_spatial_tab_roundtrip(tab_wcs_2di): + nx, ny = tab_wcs_2di.pixel_shape + # generate "random" test coordinates: + np.random.seed(1) + xy = 0.51 + [nx + 0.99, ny + 0.99] * np.random.random((100, 2)) + rd = tab_wcs_2di.wcs_pix2world(xy, 1) + xy_roundtripped = tab_wcs_2di.wcs_world2pix(rd, 1) + m = np.logical_and(*(np.isfinite(xy_roundtripped).T)) + assert np.allclose(xy[m], xy_roundtripped[m], rtol=0, atol=1e-7) + + +def test_2d_spatial_tab_vs_model(): + nx = 150 + ny = 200 + model = SimModelTAB(nx=nx, ny=ny) + + # generate FITS HDU list: + hdulist = model.hdulist + + # create WCS object: + w = wcs.WCS(hdulist[0].header, hdulist) + + # generate "random" test coordinates: + np.random.seed(1) + xy = 0.51 + [nx + 0.99, ny + 0.99] * np.random.random((100, 2)) + rd = w.wcs_pix2world(xy, 1) + rd_model = model.fwd_eval(xy) + assert np.allclose(rd, rd_model, rtol=0, atol=1e-7) + + +@pytest.mark.skipif( + Version(WCSLIB_VERSION) < Version("7.6"), + reason="Only in WCSLIB 7.6 a 1D -TAB axis roundtrips unless first axis", +) +def test_mixed_celest_and_1d_tab_roundtrip(): + # Tests WCS roundtripping for the case when there is one -TAB axis and + # this axis is not the first axis. This tests a bug fixed in WCSLIB 7.6. + filename = get_pkg_data_filename("data/tab-time-last-axis.fits") + with fits.open(filename) as hdul: + w = wcs.WCS(hdul[0].header, hdul) + + pts = np.random.random((10, 3)) * [[2047, 2047, 127]] + assert np.allclose(pts, w.wcs_world2pix(w.wcs_pix2world(pts, 0), 0)) + + +@pytest.mark.skipif( + Version(WCSLIB_VERSION) < Version("7.8"), + reason="Requires WCSLIB >= 7.8 for swapping -TAB axes to work.", +) +def test_wcstab_swapaxes(): + # Crash on deepcopy of swapped -TAB axes reported in #13036. + # Fixed in #13063. + filename = get_pkg_data_filename("data/tab-time-last-axis.fits") + with fits.open(filename) as hdul: + w = wcs.WCS(hdul[0].header, hdul) + w.wcs.ctype[-1] = "FREQ-TAB" + w.wcs.set() + wswp = w.swapaxes(2, 0) + deepcopy(wswp) + + +@pytest.mark.skipif( + Version(WCSLIB_VERSION) < Version("7.8"), + reason="Requires WCSLIB >= 7.8 for swapping -TAB axes to work.", +) +@pytest.mark.xfail( + Version("7.8") <= Version(WCSLIB_VERSION) < Version("7.10"), + reason="Requires WCSLIB >= 7.10 for swapped -TAB axes to produce same results.", +) +def test_wcstab_swapaxes_same_val_roundtrip(): + filename = get_pkg_data_filename("data/tab-time-last-axis.fits") + + axes_order = [3, 2, 1] + axes_order0 = [i - 1 for i in axes_order] + + with fits.open(filename) as hdul: + w = wcs.WCS(hdul[0].header, hdul) + w.wcs.ctype[-1] = "FREQ-TAB" + w.wcs.set() + ws = w.sub(axes_order) + + imcoord = np.array([3, 5, 7]) + imcoords = imcoord[axes_order0] + val_ref = w.wcs_pix2world([imcoord], 0)[0] + val_swapped = ws.wcs_pix2world([imcoords], 0)[0] + + # check original axis and swapped give same results + assert np.allclose(val_ref[axes_order0], val_swapped, rtol=0, atol=1e-8) + + # check round-tripping: + assert np.allclose(w.wcs_world2pix([val_ref], 0)[0], imcoord, rtol=0, atol=1e-8) diff --git a/astropy/wcs/tests/test_tabprm.py b/astropy/wcs/tests/test_tabprm.py new file mode 100644 index 000000000000..089a3e7e58db --- /dev/null +++ b/astropy/wcs/tests/test_tabprm.py @@ -0,0 +1,134 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +from copy import deepcopy + +import numpy as np + + +def test_wcsprm_tab_basic(tab_wcs_2di): + assert len(tab_wcs_2di.wcs.tab) == 1 + t = tab_wcs_2di.wcs.tab[0] + assert tab_wcs_2di.wcs.tab[0] is not t + + +def test_tabprm_coord(tab_wcs_2di_f): + t = tab_wcs_2di_f.wcs.tab[0] + + c0 = t.coord + c1 = np.ones_like(c0) + t.coord = c1 + assert np.allclose(tab_wcs_2di_f.wcs.tab[0].coord, c1) + + +def test_tabprm_crval_and_deepcopy(tab_wcs_2di_f): + w = deepcopy(tab_wcs_2di_f) + + t = tab_wcs_2di_f.wcs.tab[0] + + pix = np.array([[2, 3]], dtype=np.float32) + + rd1 = tab_wcs_2di_f.wcs_pix2world(pix, 1) + + c = t.crval.copy() + d = 0.5 * np.ones_like(c) + t.crval += d + assert np.allclose(tab_wcs_2di_f.wcs.tab[0].crval, c + d) + + rd2 = tab_wcs_2di_f.wcs_pix2world(pix - d, 1) + assert np.allclose(rd1, rd2) + + rd3 = w.wcs_pix2world(pix, 1) + assert np.allclose(rd1, rd3) + + +def test_tabprm_delta(tab_wcs_2di): + t = tab_wcs_2di.wcs.tab[0] + assert np.allclose([0.0, 0.0], t.delta) + + +def test_tabprm_K(tab_wcs_2di): + t = tab_wcs_2di.wcs.tab[0] + assert np.all(t.K == [4, 2]) + + +def test_tabprm_M(tab_wcs_2di): + t = tab_wcs_2di.wcs.tab[0] + assert t.M == 2 + + +def test_tabprm_nc(tab_wcs_2di): + t = tab_wcs_2di.wcs.tab[0] + assert t.nc == 8 + + +def test_tabprm_extrema(tab_wcs_2di): + t = tab_wcs_2di.wcs.tab[0] + extrema = np.array( + [ + [[-0.0026, -0.5], [1.001, -0.5]], + [[-0.0026, 0.5], [1.001, 0.5]], + ] + ) + assert np.allclose(t.extrema, extrema) + + +def test_tabprm_map(tab_wcs_2di_f): + t = tab_wcs_2di_f.wcs.tab[0] + assert np.allclose(t.map, [0, 1]) + + t.map[1] = 5 + assert np.all(tab_wcs_2di_f.wcs.tab[0].map == [0, 5]) + + t.map = [1, 4] + assert np.all(tab_wcs_2di_f.wcs.tab[0].map == [1, 4]) + + +def test_tabprm_sense(tab_wcs_2di): + t = tab_wcs_2di.wcs.tab[0] + assert np.all(t.sense == [1, 1]) + + +def test_tabprm_p0(tab_wcs_2di): + t = tab_wcs_2di.wcs.tab[0] + assert np.all(t.p0 == [0, 0]) + + +def test_tabprm_print(tab_wcs_2di_f, capfd): + tab_wcs_2di_f.wcs.tab[0].print_contents() + captured = capfd.readouterr() + s = str(tab_wcs_2di_f.wcs.tab[0]) + out = str(captured.out) + lout = out.split("\n") + assert out == s + assert lout[0] == " flag: 137" + assert lout[1] == " M: 2" + + +def test_wcstab_copy(tab_wcs_2di_f): + t = tab_wcs_2di_f.wcs.tab[0] + + c0 = t.coord + c1 = np.ones_like(c0) + t.coord = c1 + assert np.allclose(tab_wcs_2di_f.wcs.tab[0].coord, c1) + + +def test_tabprm_crval(tab_wcs_2di_f): + w = deepcopy(tab_wcs_2di_f) + + t = tab_wcs_2di_f.wcs.tab[0] + + pix = np.array([[2, 3]], dtype=np.float32) + + rd1 = tab_wcs_2di_f.wcs_pix2world(pix, 1) + + c = t.crval.copy() + d = 0.5 * np.ones_like(c) + t.crval += d + assert np.allclose(tab_wcs_2di_f.wcs.tab[0].crval, c + d) + + rd2 = tab_wcs_2di_f.wcs_pix2world(pix - d, 1) + assert np.allclose(rd1, rd2) + + rd3 = w.wcs_pix2world(pix, 1) + assert np.allclose(rd1, rd3) diff --git a/astropy/wcs/tests/test_utils.py b/astropy/wcs/tests/test_utils.py new file mode 100644 index 000000000000..feef4aa8bf4e --- /dev/null +++ b/astropy/wcs/tests/test_utils.py @@ -0,0 +1,1780 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +from contextlib import nullcontext + +import numpy as np +import pytest +from numpy.testing import assert_allclose, assert_almost_equal, assert_equal +from packaging.version import Version + +from astropy import units as u +from astropy.coordinates import ITRS, BaseCoordinateFrame, EarthLocation, SkyCoord +from astropy.coordinates.representation import SphericalRepresentation +from astropy.coordinates.representation.geodetic import ( + BaseBodycentricRepresentation, + BaseGeodeticRepresentation, +) + +# Preserve the original REPRESENTATION_CLASSES dict so that importing +# the test file doesn't add a persistent test subclass +from astropy.coordinates.tests.test_representation import ( # noqa: F401 + setup_function, + teardown_function, +) +from astropy.io import fits +from astropy.time import Time +from astropy.units import Quantity +from astropy.utils import unbroadcast +from astropy.utils.compat.optional_deps import HAS_SCIPY +from astropy.utils.data import get_pkg_data_contents, get_pkg_data_filename +from astropy.utils.exceptions import AstropyUserWarning +from astropy.wcs.utils import ( + FRAME_WCS_MAPPINGS, + WCS_FRAME_MAPPINGS, + _pixel_to_pixel_correlation_matrix, + _pixel_to_world_correlation_matrix, + _split_matrix, + add_stokes_axis_to_wcs, + celestial_frame_to_wcs, + custom_frame_to_wcs_mappings, + custom_wcs_to_frame_mappings, + fit_wcs_from_points, + is_proj_plane_distorted, + local_partial_pixel_derivatives, + non_celestial_pixel_scales, + obsgeo_to_frame, + pixel_to_pixel, + pixel_to_skycoord, + proj_plane_pixel_scales, + skycoord_to_pixel, + wcs_to_celestial_frame, +) +from astropy.wcs.wcs import ( + WCS, + WCSLIB_VERSION, + WCSSUB_LATITUDE, + WCSSUB_LONGITUDE, + DistortionLookupTable, + FITSFixedWarning, + Sip, +) +from astropy.wcs.wcsapi.fitswcs import SlicedFITSWCS + + +def test_wcs_dropping(): + wcs = WCS(naxis=4) + wcs.wcs.pc = np.zeros([4, 4]) + np.fill_diagonal(wcs.wcs.pc, np.arange(1, 5)) + pc = wcs.wcs.pc # for later use below + + dropped = wcs.dropaxis(0) + assert np.all(dropped.wcs.get_pc().diagonal() == np.array([2, 3, 4])) + dropped = wcs.dropaxis(1) + assert np.all(dropped.wcs.get_pc().diagonal() == np.array([1, 3, 4])) + dropped = wcs.dropaxis(2) + assert np.all(dropped.wcs.get_pc().diagonal() == np.array([1, 2, 4])) + dropped = wcs.dropaxis(3) + assert np.all(dropped.wcs.get_pc().diagonal() == np.array([1, 2, 3])) + + wcs = WCS(naxis=4) + wcs.wcs.cd = pc + + dropped = wcs.dropaxis(0) + assert np.all(dropped.wcs.get_pc().diagonal() == np.array([2, 3, 4])) + dropped = wcs.dropaxis(1) + assert np.all(dropped.wcs.get_pc().diagonal() == np.array([1, 3, 4])) + dropped = wcs.dropaxis(2) + assert np.all(dropped.wcs.get_pc().diagonal() == np.array([1, 2, 4])) + dropped = wcs.dropaxis(3) + assert np.all(dropped.wcs.get_pc().diagonal() == np.array([1, 2, 3])) + + +def test_wcs_swapping(): + wcs = WCS(naxis=4) + wcs.wcs.pc = np.zeros([4, 4]) + np.fill_diagonal(wcs.wcs.pc, np.arange(1, 5)) + pc = wcs.wcs.pc # for later use below + + swapped = wcs.swapaxes(0, 1) + assert np.all(swapped.wcs.get_pc().diagonal() == np.array([2, 1, 3, 4])) + swapped = wcs.swapaxes(0, 3) + assert np.all(swapped.wcs.get_pc().diagonal() == np.array([4, 2, 3, 1])) + swapped = wcs.swapaxes(2, 3) + assert np.all(swapped.wcs.get_pc().diagonal() == np.array([1, 2, 4, 3])) + + wcs = WCS(naxis=4) + wcs.wcs.cd = pc + + swapped = wcs.swapaxes(0, 1) + assert np.all(swapped.wcs.get_pc().diagonal() == np.array([2, 1, 3, 4])) + swapped = wcs.swapaxes(0, 3) + assert np.all(swapped.wcs.get_pc().diagonal() == np.array([4, 2, 3, 1])) + swapped = wcs.swapaxes(2, 3) + assert np.all(swapped.wcs.get_pc().diagonal() == np.array([1, 2, 4, 3])) + + +@pytest.mark.parametrize("ndim", (2, 3)) +def test_add_stokes(ndim): + wcs = WCS(naxis=ndim) + + for ii in range(ndim + 1): + outwcs = add_stokes_axis_to_wcs(wcs, ii) + assert outwcs.wcs.naxis == ndim + 1 + assert outwcs.wcs.ctype[ii] == "STOKES" + assert outwcs.wcs.cname[ii] == "STOKES" + + +def test_slice(): + mywcs = WCS(naxis=2) + mywcs.wcs.crval = [1, 1] + mywcs.wcs.cdelt = [0.1, 0.1] + mywcs.wcs.crpix = [1, 1] + mywcs._naxis = [1000, 500] + pscale = 0.1 # from cdelt + + slice_wcs = mywcs.slice([slice(1, None), slice(0, None)]) + assert np.all(slice_wcs.wcs.crpix == np.array([1, 0])) + assert slice_wcs._naxis == [1000, 499] + + # test that CRPIX maps to CRVAL: + assert_allclose( + slice_wcs.wcs_pix2world(*slice_wcs.wcs.crpix, 1), + slice_wcs.wcs.crval, + rtol=0.0, + atol=1e-6 * pscale, + ) + + slice_wcs = mywcs.slice([slice(1, None, 2), slice(0, None, 4)]) + assert np.all(slice_wcs.wcs.crpix == np.array([0.625, 0.25])) + assert np.all(slice_wcs.wcs.cdelt == np.array([0.4, 0.2])) + assert slice_wcs._naxis == [250, 250] + + slice_wcs = mywcs.slice([slice(None, None, 2), slice(0, None, 2)]) + assert np.all(slice_wcs.wcs.cdelt == np.array([0.2, 0.2])) + assert slice_wcs._naxis == [500, 250] + + # Non-integral values do not alter the naxis attribute + with pytest.warns(AstropyUserWarning): + slice_wcs = mywcs.slice([slice(50.0), slice(20.0)]) + assert slice_wcs._naxis == [1000, 500] + with pytest.warns(AstropyUserWarning): + slice_wcs = mywcs.slice([slice(50.0), slice(20)]) + assert slice_wcs._naxis == [20, 500] + with pytest.warns(AstropyUserWarning): + slice_wcs = mywcs.slice([slice(50), slice(20.5)]) + assert slice_wcs._naxis == [1000, 50] + + +def test_slice_with_sip(): + mywcs = WCS(naxis=2) + mywcs.wcs.crval = [1, 1] + mywcs.wcs.cdelt = [0.1, 0.1] + mywcs.wcs.crpix = [1, 1] + mywcs._naxis = [1000, 500] + mywcs.wcs.ctype = ["RA---TAN-SIP", "DEC--TAN-SIP"] + a = np.array( + [ + [0, 0, 5.33092692e-08, 3.73753773e-11, -2.02111473e-13], + [0, 2.44084308e-05, 2.81394789e-11, 5.17856895e-13, 0.0], + [-2.41334657e-07, 1.29289255e-10, 2.35753629e-14, 0.0, 0.0], + [-2.37162007e-10, 5.43714947e-13, 0.0, 0.0, 0.0], + [-2.81029767e-13, 0.0, 0.0, 0.0, 0.0], + ] + ) + b = np.array( + [ + [0, 0, 2.99270374e-05, -2.38136074e-10, 7.23205168e-13], + [0, -1.71073858e-07, 6.31243431e-11, -5.16744347e-14, 0.0], + [6.95458963e-06, -3.08278961e-10, -1.75800917e-13, 0.0, 0.0], + [3.51974159e-11, 5.60993016e-14, 0.0, 0.0, 0.0], + [-5.92438525e-13, 0.0, 0.0, 0.0, 0.0], + ] + ) + mywcs.sip = Sip(a, b, None, None, mywcs.wcs.crpix) + mywcs.wcs.set() + pscale = 0.1 # from cdelt + + slice_wcs = mywcs.slice([slice(1, None), slice(0, None)]) + # test that CRPIX maps to CRVAL: + assert_allclose( + slice_wcs.all_pix2world(*slice_wcs.wcs.crpix, 1), + slice_wcs.wcs.crval, + rtol=0.0, + atol=1e-6 * pscale, + ) + + slice_wcs = mywcs.slice([slice(1, None, 2), slice(0, None, 4)]) + # test that CRPIX maps to CRVAL: + assert_allclose( + slice_wcs.all_pix2world(*slice_wcs.wcs.crpix, 1), + slice_wcs.wcs.crval, + rtol=0.0, + atol=1e-6 * pscale, + ) + + +def test_slice_with_cpdis_tables(): + # A basic WCS + mywcs = WCS(naxis=2) + mywcs.wcs.crval = [1, 1] + mywcs.wcs.cdelt = [0.1, 0.1] + mywcs.wcs.crpix = [1, 1] + mywcs.wcs.ctype = ["RA---TAN", "DEC--TAN"] + + # Arbitrary distortion maps for X and Y + distortion_array = np.arange(25 * 25, dtype=np.float32).reshape((25, 25)) + mywcs.cpdis1 = DistortionLookupTable(distortion_array, (1, 1), (1, 1), (10, 10)) + mywcs.cpdis2 = DistortionLookupTable(distortion_array, (1, 1), (1, 1), (10, 10)) + + # Test that equivalent pixels produce the same coordinates, whether or not + # they've been sliced out. + coord_from_slice = mywcs[40:, 50:].pixel_to_world(30, 60) + coord_from_full = mywcs.pixel_to_world(50 + 30, 40 + 60) + + assert coord_from_full == coord_from_slice + + # Test the same with a step size. (Note, per discussion in gh-10897, + # slicing a WCS means "binning", rather than "resampling", so there's a + # quarter-pixel offset to get the "equivalent" spot. The centers of the + # post-slice pixels are at the dividing line between the two "input" pixels + # that form this binned, post-slice pixel.) + coord_from_slice = mywcs[50::2, 50::2].pixel_to_world(24.75, 24.75) + coord_from_full = mywcs.pixel_to_world(100, 100) + + assert coord_from_full == coord_from_slice + + +def test_slice_getitem(): + mywcs = WCS(naxis=2) + mywcs.wcs.crval = [1, 1] + mywcs.wcs.cdelt = [0.1, 0.1] + mywcs.wcs.crpix = [1, 1] + + slice_wcs = mywcs[1::2, 0::4] + assert np.all(slice_wcs.wcs.crpix == np.array([0.625, 0.25])) + assert np.all(slice_wcs.wcs.cdelt == np.array([0.4, 0.2])) + + mywcs.wcs.crpix = [2, 2] + slice_wcs = mywcs[1::2, 0::4] + assert np.all(slice_wcs.wcs.crpix == np.array([0.875, 0.75])) + assert np.all(slice_wcs.wcs.cdelt == np.array([0.4, 0.2])) + + # Default: numpy order + slice_wcs = mywcs[1::2] + assert np.all(slice_wcs.wcs.crpix == np.array([2, 0.75])) + assert np.all(slice_wcs.wcs.cdelt == np.array([0.1, 0.2])) + + +def test_slice_fitsorder(): + mywcs = WCS(naxis=2) + mywcs.wcs.crval = [1, 1] + mywcs.wcs.cdelt = [0.1, 0.1] + mywcs.wcs.crpix = [1, 1] + + slice_wcs = mywcs.slice([slice(1, None), slice(0, None)], numpy_order=False) + assert np.all(slice_wcs.wcs.crpix == np.array([0, 1])) + + slice_wcs = mywcs.slice([slice(1, None, 2), slice(0, None, 4)], numpy_order=False) + assert np.all(slice_wcs.wcs.crpix == np.array([0.25, 0.625])) + assert np.all(slice_wcs.wcs.cdelt == np.array([0.2, 0.4])) + + slice_wcs = mywcs.slice([slice(1, None, 2)], numpy_order=False) + assert np.all(slice_wcs.wcs.crpix == np.array([0.25, 1])) + assert np.all(slice_wcs.wcs.cdelt == np.array([0.2, 0.1])) + + +def test_slice_wcs(): + mywcs = WCS(naxis=2) + + sub = mywcs[0] + assert isinstance(sub, SlicedFITSWCS) + + with pytest.raises(IndexError, match="Slicing WCS with a step is not supported."): + mywcs[0, ::2] + + +def test_slice_nd(): + # Regression test for a bug that caused slicing to not work correctly for + # WCS with more than 2 dimensions. + + sub = WCS(naxis=3)[:, :, :] + assert isinstance(sub, WCS) + + +def test_slice_ellipsis(): + # Regression test for a bug that caused slicing with an ellipsis to not + # return a WCS object but a SlicedFITSWCS instead + + sub = WCS(naxis=3)[...] + assert isinstance(sub, WCS) + + +def test_slice_drop_dimensions_order(): + # Regression test for a bug that caused WCS.slice to ignore + # ``numpy_order=False`` if dimensions were dropped. + + wcs = WCS(naxis=3) + wcs.wcs.ctype = "RA---TAN", "DEC--TAN", "FREQ" + + wcs_sliced_1 = wcs.slice([0, slice(None), slice(None)], numpy_order=True) + assert wcs_sliced_1.world_axis_physical_types == ["pos.eq.ra", "pos.eq.dec"] + + wcs_sliced_2 = wcs.slice([slice(None), slice(None), 0], numpy_order=False) + assert wcs_sliced_2.world_axis_physical_types == ["pos.eq.ra", "pos.eq.dec"] + + +def test_axis_names(): + mywcs = WCS(naxis=4) + mywcs.wcs.ctype = ["RA---TAN", "DEC--TAN", "VOPT-LSR", "STOKES"] + + assert mywcs.axis_type_names == ["RA", "DEC", "VOPT", "STOKES"] + + mywcs.wcs.cname = ["RA", "DEC", "VOPT", "STOKES"] + + assert mywcs.axis_type_names == ["RA", "DEC", "VOPT", "STOKES"] + + +def test_celestial(): + mywcs = WCS(naxis=4) + mywcs.wcs.ctype = ["RA---TAN", "DEC--TAN", "VOPT", "STOKES"] + cel = mywcs.celestial + assert tuple(cel.wcs.ctype) == ("RA---TAN", "DEC--TAN") + assert cel.axis_type_names == ["RA", "DEC"] + + +def test_wcs_to_celestial_frame(): + # Import astropy.coordinates here to avoid circular imports + from astropy.coordinates.builtin_frames import FK4, FK5, ICRS, ITRS, Galactic + + mywcs = WCS(naxis=2) + mywcs.wcs.set() + with pytest.raises( + ValueError, + match=( + "Could not determine celestial frame " + "corresponding to the specified WCS object" + ), + ): + assert wcs_to_celestial_frame(mywcs) is None + + mywcs = WCS(naxis=2) + mywcs.wcs.ctype = ["XOFFSET", "YOFFSET"] + mywcs.wcs.set() + with pytest.raises(ValueError): + assert wcs_to_celestial_frame(mywcs) is None + + mywcs = WCS(naxis=2) + mywcs.wcs.ctype = ["RA---TAN", "DEC--TAN"] + mywcs.wcs.set() + frame = wcs_to_celestial_frame(mywcs) + assert isinstance(frame, ICRS) + + mywcs = WCS(naxis=2) + mywcs.wcs.ctype = ["RA---TAN", "DEC--TAN"] + mywcs.wcs.equinox = 1987.0 + mywcs.wcs.set() + frame = wcs_to_celestial_frame(mywcs) + assert isinstance(frame, FK5) + assert frame.equinox == Time(1987.0, format="jyear") + + mywcs = WCS(naxis=2) + mywcs.wcs.ctype = ["RA---TAN", "DEC--TAN"] + mywcs.wcs.equinox = 1982 + mywcs.wcs.set() + frame = wcs_to_celestial_frame(mywcs) + assert isinstance(frame, FK4) + assert frame.equinox == Time(1982.0, format="byear") + + mywcs = WCS(naxis=2) + mywcs.wcs.ctype = ["GLON-SIN", "GLAT-SIN"] + mywcs.wcs.set() + frame = wcs_to_celestial_frame(mywcs) + assert isinstance(frame, Galactic) + + mywcs = WCS(naxis=2) + mywcs.wcs.ctype = ["TLON-CAR", "TLAT-CAR"] + mywcs.wcs.dateobs = "2017-08-17T12:41:04.430" + mywcs.wcs.set() + frame = wcs_to_celestial_frame(mywcs) + assert isinstance(frame, ITRS) + assert frame.obstime == Time("2017-08-17T12:41:04.430") + + for equinox in [np.nan, 1987, 1982]: + mywcs = WCS(naxis=2) + mywcs.wcs.ctype = ["RA---TAN", "DEC--TAN"] + mywcs.wcs.radesys = "ICRS" + mywcs.wcs.equinox = equinox + mywcs.wcs.set() + frame = wcs_to_celestial_frame(mywcs) + assert isinstance(frame, ICRS) + + # Flipped order + mywcs = WCS(naxis=2) + mywcs.wcs.ctype = ["DEC--TAN", "RA---TAN"] + mywcs.wcs.set() + frame = wcs_to_celestial_frame(mywcs) + assert isinstance(frame, ICRS) + + # More than two dimensions + mywcs = WCS(naxis=3) + mywcs.wcs.ctype = ["DEC--TAN", "VELOCITY", "RA---TAN"] + mywcs.wcs.set() + frame = wcs_to_celestial_frame(mywcs) + assert isinstance(frame, ICRS) + + mywcs = WCS(naxis=3) + mywcs.wcs.ctype = ["GLAT-CAR", "VELOCITY", "GLON-CAR"] + mywcs.wcs.set() + frame = wcs_to_celestial_frame(mywcs) + assert isinstance(frame, Galactic) + + +def test_wcs_to_body_frame(): + mywcs = WCS(naxis=2) + mywcs.wcs.ctype = ["VELN-TAN", "VELT-TAN"] + mywcs.wcs.dateobs = "2017-08-17T12:41:04.430" + mywcs.wcs.name = "Venus Geodetic Body-Fixed" + + mywcs.wcs.aux.a_radius = 6051800.0 + mywcs.wcs.aux.b_radius = 6051800.0 + mywcs.wcs.aux.c_radius = 6051800.0 + framev = wcs_to_celestial_frame(mywcs) + assert issubclass(framev, BaseCoordinateFrame) + assert issubclass(framev.representation_type, BaseGeodeticRepresentation) + assert framev.name == "Venus" + assert framev.representation_type._equatorial_radius == 6051800.0 * u.m + assert framev.representation_type._flattening == 0.0 + + # Check that frames are cached appropriately + framev2 = wcs_to_celestial_frame(mywcs) + assert framev2.representation_type is framev.representation_type + assert framev2 is framev + + mywcs = WCS(naxis=2) + mywcs.wcs.ctype = ["MALN-TAN", "MALT-TAN"] + mywcs.wcs.dateobs = "2017-08-17T12:41:04.430" + mywcs.wcs.name = "Mars Bodycentric Body-Fixed" + + mywcs.wcs.aux.a_radius = 3396190.0 + mywcs.wcs.aux.b_radius = 3396190.0 + mywcs.wcs.aux.c_radius = 3376190.0 + framem = wcs_to_celestial_frame(mywcs) + assert issubclass(framem, BaseCoordinateFrame) + assert issubclass(framem.representation_type, BaseBodycentricRepresentation) + assert framem.name == "Mars" + assert framem.representation_type._equatorial_radius == 3396190.0 * u.m + assert_almost_equal(framem.representation_type._flattening, 0.005888952031541227) + assert framem.representation_type is not framev.representation_type + assert framem is not framev + + mywcs = WCS(naxis=2) + mywcs.wcs.ctype = ["EALN-TAN", "EALT-TAN"] + mywcs.wcs.name = "Earth Geodetic Body-Fixed" + mywcs.wcs.aux.a_radius = 6378137.0 + mywcs.wcs.aux.b_radius = 6378137.0 + mywcs.wcs.aux.c_radius = 6356752.3 + mywcs.wcs.set() + frame = wcs_to_celestial_frame(mywcs) + assert issubclass(frame, BaseCoordinateFrame) + assert issubclass(frame.representation_type, BaseGeodeticRepresentation) + assert frame.representation_type._equatorial_radius == 6378137.0 * u.m + assert_almost_equal(frame.representation_type._flattening, 0.0033528128981864433) + + unknown_wcs = WCS(naxis=2) + unknown_wcs.wcs.ctype = ["UTLN-TAN", "UTLT-TAN"] + with pytest.raises( + ValueError, + match="Could not determine celestial frame corresponding to the specified WCS object", + ): + frame = wcs_to_celestial_frame(unknown_wcs) + + triaxial_wcs = WCS(naxis=2) + triaxial_wcs.wcs.ctype = ["MELN-TAN", "MELT-TAN"] + triaxial_wcs.wcs.aux.a_radius = 2439700.0 + triaxial_wcs.wcs.aux.b_radius = 2439900.0 + triaxial_wcs.wcs.aux.c_radius = 2438800.0 + with pytest.raises( + NotImplementedError, match="triaxial systems are not supported at this time" + ): + frame = wcs_to_celestial_frame(triaxial_wcs) + + +def test_wcs_to_celestial_frame_correlated(): + # Regression test for a bug that caused wcs_to_celestial_frame to fail when + # the celestial axes were correlated with other axes. + + # Import astropy.coordinates here to avoid circular imports + from astropy.coordinates.builtin_frames import ICRS + + mywcs = WCS(naxis=3) + mywcs.wcs.ctype = "RA---TAN", "DEC--TAN", "FREQ" + mywcs.wcs.cd = np.ones((3, 3)) + mywcs.wcs.set() + frame = wcs_to_celestial_frame(mywcs) + assert isinstance(frame, ICRS) + + +def test_wcs_to_celestial_frame_extend(): + mywcs = WCS(naxis=2) + mywcs.wcs.ctype = ["XOFFSET", "YOFFSET"] + mywcs.wcs.set() + with pytest.raises(ValueError): + wcs_to_celestial_frame(mywcs) + + class OffsetFrame: + pass + + def identify_offset(wcs): + if wcs.wcs.ctype[0].endswith("OFFSET") and wcs.wcs.ctype[1].endswith("OFFSET"): + return OffsetFrame() + + with custom_wcs_to_frame_mappings(identify_offset): + frame = wcs_to_celestial_frame(mywcs) + assert isinstance(frame, OffsetFrame) + + # Check that things are back to normal after the context manager + with pytest.raises(ValueError): + wcs_to_celestial_frame(mywcs) + + +def test_celestial_frame_to_wcs(): + # Import astropy.coordinates here to avoid circular imports + from astropy.coordinates import ( + FK4, + FK5, + ICRS, + ITRS, + BaseCoordinateFrame, + FK4NoETerms, + Galactic, + ) + + class FakeFrame(BaseCoordinateFrame): + pass + + frame = FakeFrame() + with pytest.raises( + ValueError, + match=( + r"Could not determine WCS corresponding to the specified coordinate frame." + ), + ): + celestial_frame_to_wcs(frame) + + frame = ICRS() + mywcs = celestial_frame_to_wcs(frame) + mywcs.wcs.set() + assert tuple(mywcs.wcs.ctype) == ("RA---TAN", "DEC--TAN") + assert mywcs.wcs.radesys == "ICRS" + assert np.isnan(mywcs.wcs.equinox) + assert mywcs.wcs.lonpole == 180 + assert mywcs.wcs.latpole == 0 + + frame = FK5(equinox="J1987") + mywcs = celestial_frame_to_wcs(frame) + assert tuple(mywcs.wcs.ctype) == ("RA---TAN", "DEC--TAN") + assert mywcs.wcs.radesys == "FK5" + assert mywcs.wcs.equinox == 1987.0 + + frame = FK4(equinox="B1982") + mywcs = celestial_frame_to_wcs(frame) + assert tuple(mywcs.wcs.ctype) == ("RA---TAN", "DEC--TAN") + assert mywcs.wcs.radesys == "FK4" + assert mywcs.wcs.equinox == 1982.0 + + frame = FK4NoETerms(equinox="B1982") + mywcs = celestial_frame_to_wcs(frame) + assert tuple(mywcs.wcs.ctype) == ("RA---TAN", "DEC--TAN") + assert mywcs.wcs.radesys == "FK4-NO-E" + assert mywcs.wcs.equinox == 1982.0 + + frame = Galactic() + mywcs = celestial_frame_to_wcs(frame) + assert tuple(mywcs.wcs.ctype) == ("GLON-TAN", "GLAT-TAN") + assert mywcs.wcs.radesys == "" + assert np.isnan(mywcs.wcs.equinox) + + frame = Galactic() + mywcs = celestial_frame_to_wcs(frame, projection="CAR") + assert tuple(mywcs.wcs.ctype) == ("GLON-CAR", "GLAT-CAR") + assert mywcs.wcs.radesys == "" + assert np.isnan(mywcs.wcs.equinox) + + frame = Galactic() + mywcs = celestial_frame_to_wcs(frame, projection="CAR") + mywcs.wcs.crval = [100, -30] + mywcs.wcs.set() + assert_allclose((mywcs.wcs.lonpole, mywcs.wcs.latpole), (180, 60)) + + frame = ITRS(obstime=Time("2017-08-17T12:41:04.43")) + mywcs = celestial_frame_to_wcs(frame, projection="CAR") + assert tuple(mywcs.wcs.ctype) == ("TLON-CAR", "TLAT-CAR") + assert mywcs.wcs.radesys == "ITRS" + assert mywcs.wcs.dateobs == "2017-08-17T12:41:04.430" + + frame = ITRS() + mywcs = celestial_frame_to_wcs(frame, projection="CAR") + assert tuple(mywcs.wcs.ctype) == ("TLON-CAR", "TLAT-CAR") + assert mywcs.wcs.radesys == "ITRS" + assert mywcs.wcs.dateobs == Time("J2000").utc.fits + + +def test_body_to_wcs_frame(): + class IAUMARS2000GeodeticRepresentation(BaseGeodeticRepresentation): + _equatorial_radius = 3396190.0 * u.m + _flattening = 0.5886007555512007 * u.percent + + class IAUMARS2000BodycentricRepresentation(BaseBodycentricRepresentation): + _equatorial_radius = 3396190.0 * u.m + _flattening = 0.5886007555512007 * u.percent + + class IAUMARS2000BodyFrame(BaseCoordinateFrame): + name = "Mars" + + frame = IAUMARS2000BodyFrame() + + frame.representation_type = IAUMARS2000GeodeticRepresentation + + mywcs = celestial_frame_to_wcs(frame, projection="CAR") + assert mywcs.wcs.ctype[0] == "MALN-CAR" + assert mywcs.wcs.ctype[1] == "MALT-CAR" + assert mywcs.wcs.name == "Planetographic Body-Fixed" + assert mywcs.wcs.aux.a_radius == 3396190.0 + assert mywcs.wcs.aux.b_radius == 3396190.0 + assert_almost_equal(mywcs.wcs.aux.c_radius, 3376200.0) + + frame.representation_type = IAUMARS2000BodycentricRepresentation + mywcs = celestial_frame_to_wcs(frame, projection="CAR") + assert mywcs.wcs.ctype[0] == "MALN-CAR" + assert mywcs.wcs.ctype[1] == "MALT-CAR" + assert mywcs.wcs.name == "Bodycentric Body-Fixed" + + assert mywcs.wcs.aux.a_radius == 3396190.0 + assert mywcs.wcs.aux.b_radius == 3396190.0 + assert_almost_equal(mywcs.wcs.aux.c_radius, 3376200.0) + + class IAUMARSSphereFrame(BaseCoordinateFrame): + name = "Mars" + representation_type = SphericalRepresentation + + frame = IAUMARSSphereFrame() + + with pytest.raises( + ValueError, + match="Planetary coordinates in WCS require a geodetic or bodycentric", + ): + celestial_frame_to_wcs(frame, projection="CAR") + + +def test_celestial_frame_to_wcs_extend(): + class OffsetFrame: + pass + + frame = OffsetFrame() + + with pytest.raises(ValueError): + celestial_frame_to_wcs(frame) + + def identify_offset(frame, projection=None): + if isinstance(frame, OffsetFrame): + wcs = WCS(naxis=2) + wcs.wcs.ctype = ["XOFFSET", "YOFFSET"] + return wcs + + with custom_frame_to_wcs_mappings(identify_offset): + mywcs = celestial_frame_to_wcs(frame) + assert tuple(mywcs.wcs.ctype) == ("XOFFSET", "YOFFSET") + + # Check that things are back to normal after the context manager + with pytest.raises(ValueError): + celestial_frame_to_wcs(frame) + + +def test_pixscale_nodrop(): + mywcs = WCS(naxis=2) + mywcs.wcs.cdelt = [0.1, 0.2] + mywcs.wcs.ctype = ["RA---TAN", "DEC--TAN"] + assert_almost_equal(proj_plane_pixel_scales(mywcs), (0.1, 0.2)) + + mywcs.wcs.cdelt = [-0.1, 0.2] + assert_almost_equal(proj_plane_pixel_scales(mywcs), (0.1, 0.2)) + + +def test_pixscale_withdrop(): + mywcs = WCS(naxis=3) + mywcs.wcs.cdelt = [0.1, 0.2, 1] + mywcs.wcs.ctype = ["RA---TAN", "DEC--TAN", "VOPT"] + assert_almost_equal(proj_plane_pixel_scales(mywcs.celestial), (0.1, 0.2)) + + mywcs.wcs.cdelt = [-0.1, 0.2, 1] + assert_almost_equal(proj_plane_pixel_scales(mywcs.celestial), (0.1, 0.2)) + + +def test_pixscale_cd(): + mywcs = WCS(naxis=2) + mywcs.wcs.cd = [[-0.1, 0], [0, 0.2]] + mywcs.wcs.ctype = ["RA---TAN", "DEC--TAN"] + assert_almost_equal(proj_plane_pixel_scales(mywcs), (0.1, 0.2)) + + +@pytest.mark.parametrize("angle", (30, 45, 60, 75)) +def test_pixscale_cd_rotated(angle): + mywcs = WCS(naxis=2) + rho = np.radians(angle) + scale = 0.1 + mywcs.wcs.cd = [ + [scale * np.cos(rho), -scale * np.sin(rho)], + [scale * np.sin(rho), scale * np.cos(rho)], + ] + mywcs.wcs.ctype = ["RA---TAN", "DEC--TAN"] + assert_almost_equal(proj_plane_pixel_scales(mywcs), (0.1, 0.1)) + + +@pytest.mark.parametrize("angle", (30, 45, 60, 75)) +def test_pixscale_pc_rotated(angle): + mywcs = WCS(naxis=2) + rho = np.radians(angle) + scale = 0.1 + mywcs.wcs.cdelt = [-scale, scale] + mywcs.wcs.pc = [[np.cos(rho), -np.sin(rho)], [np.sin(rho), np.cos(rho)]] + mywcs.wcs.ctype = ["RA---TAN", "DEC--TAN"] + assert_almost_equal(proj_plane_pixel_scales(mywcs), (0.1, 0.1)) + + +@pytest.mark.parametrize( + ("cdelt", "pc", "pccd"), + ( + ([0.1, 0.2], np.eye(2), np.diag([0.1, 0.2])), + ([0.1, 0.2, 0.3], np.eye(3), np.diag([0.1, 0.2, 0.3])), + ([1, 1, 1], np.diag([0.1, 0.2, 0.3]), np.diag([0.1, 0.2, 0.3])), + ), +) +def test_pixel_scale_matrix(cdelt, pc, pccd): + mywcs = WCS(naxis=(len(cdelt))) + mywcs.wcs.cdelt = cdelt + mywcs.wcs.pc = pc + + assert_almost_equal(mywcs.pixel_scale_matrix, pccd) + + +@pytest.mark.parametrize( + ("ctype", "cel"), + ( + (["RA---TAN", "DEC--TAN"], True), + (["RA---TAN", "DEC--TAN", "FREQ"], False), + (["RA---TAN", "FREQ"], False), + ), +) +def test_is_celestial(ctype, cel): + mywcs = WCS(naxis=len(ctype)) + mywcs.wcs.ctype = ctype + + assert mywcs.is_celestial == cel + + +@pytest.mark.parametrize( + ("ctype", "cel"), + ( + (["RA---TAN", "DEC--TAN"], True), + (["RA---TAN", "DEC--TAN", "FREQ"], True), + (["RA---TAN", "FREQ"], False), + ), +) +def test_has_celestial(ctype, cel): + mywcs = WCS(naxis=len(ctype)) + mywcs.wcs.ctype = ctype + + assert mywcs.has_celestial == cel + + +def test_has_celestial_correlated(): + # Regression test for astropy/astropy#8416 - has_celestial failed when + # celestial axes were correlated with other axes. + mywcs = WCS(naxis=3) + mywcs.wcs.ctype = "RA---TAN", "DEC--TAN", "FREQ" + mywcs.wcs.cd = np.ones((3, 3)) + mywcs.wcs.set() + assert mywcs.has_celestial + + +@pytest.mark.parametrize( + ("cdelt", "pc", "cd", "check_warning"), + ( + (np.array([0.1, 0.2]), np.eye(2), np.eye(2), True), + (np.array([1, 1]), np.diag([0.1, 0.2]), np.eye(2), True), + (np.array([0.1, 0.2]), np.eye(2), None, False), + (np.array([0.1, 0.2]), None, np.eye(2), True), + ), +) +def test_noncelestial_scale(cdelt, pc, cd, check_warning): + mywcs = WCS(naxis=2) + if cd is not None: + mywcs.wcs.cd = cd + if pc is not None: + mywcs.wcs.pc = pc + + # TODO: Some inputs emit RuntimeWarning from here onwards. + # Fix the test data. See @nden's comment in PR 9010. + if check_warning: + ctx = pytest.warns() + else: + ctx = nullcontext() + with ctx as warning_lines: + mywcs.wcs.cdelt = cdelt + if check_warning: + for w in warning_lines: + assert issubclass(w.category, RuntimeWarning) + assert "cdelt will be ignored since cd is present" in str(w.message) + + mywcs.wcs.ctype = ["RA---TAN", "FREQ"] + + ps = non_celestial_pixel_scales(mywcs) + + assert_almost_equal(ps.to_value(u.deg), np.array([0.1, 0.2])) + + +@pytest.mark.parametrize("mode", ["all", "wcs"]) +def test_skycoord_to_pixel(mode): + # Import astropy.coordinates here to avoid circular imports + from astropy.coordinates import SkyCoord + + header = get_pkg_data_contents("data/maps/1904-66_TAN.hdr", encoding="binary") + wcs = WCS(header) + + ref = SkyCoord(0.1 * u.deg, -89.0 * u.deg, frame="icrs") + + xp, yp = skycoord_to_pixel(ref, wcs, mode=mode) + + # WCS is in FK5 so we need to transform back to ICRS + new = pixel_to_skycoord(xp, yp, wcs, mode=mode).transform_to("icrs") + + assert_allclose(new.ra.degree, ref.ra.degree) + assert_allclose(new.dec.degree, ref.dec.degree) + + # Make sure you can specify a different class using ``cls`` keyword + class SkyCoord2(SkyCoord): + pass + + new2 = pixel_to_skycoord(xp, yp, wcs, mode=mode, cls=SkyCoord2).transform_to("icrs") + + assert new2.__class__ is SkyCoord2 + assert_allclose(new2.ra.degree, ref.ra.degree) + assert_allclose(new2.dec.degree, ref.dec.degree) + + +def test_skycoord_to_pixel_swapped(): + # Regression test for a bug that caused skycoord_to_pixel and + # pixel_to_skycoord to not work correctly if the axes were swapped in the + # WCS. + + # Import astropy.coordinates here to avoid circular imports + from astropy.coordinates import SkyCoord + + header = get_pkg_data_contents("data/maps/1904-66_TAN.hdr", encoding="binary") + wcs = WCS(header) + + wcs_swapped = wcs.sub([WCSSUB_LATITUDE, WCSSUB_LONGITUDE]) + + ref = SkyCoord(0.1 * u.deg, -89.0 * u.deg, frame="icrs") + + xp1, yp1 = skycoord_to_pixel(ref, wcs) + xp2, yp2 = skycoord_to_pixel(ref, wcs_swapped) + + assert_allclose(xp1, xp2) + assert_allclose(yp1, yp2) + + # WCS is in FK5 so we need to transform back to ICRS + new1 = pixel_to_skycoord(xp1, yp1, wcs).transform_to("icrs") + new2 = pixel_to_skycoord(xp1, yp1, wcs_swapped).transform_to("icrs") + + assert_allclose(new1.ra.degree, new2.ra.degree) + assert_allclose(new1.dec.degree, new2.dec.degree) + + +def test_is_proj_plane_distorted(): + # non-orthogonal CD: + wcs = WCS(naxis=2) + wcs.wcs.cd = [[-0.1, 0], [0, 0.2]] + wcs.wcs.ctype = ["RA---TAN", "DEC--TAN"] + assert is_proj_plane_distorted(wcs) + + # almost orthogonal CD: + wcs.wcs.cd = [[0.1 + 2.0e-7, 1.7e-7], [1.2e-7, 0.1 - 1.3e-7]] + assert not is_proj_plane_distorted(wcs) + + # real case: + header = get_pkg_data_filename("data/sip.fits") + with pytest.warns(FITSFixedWarning): + wcs = WCS(header) + assert is_proj_plane_distorted(wcs) + + +@pytest.mark.parametrize("mode", ["all", "wcs"]) +def test_skycoord_to_pixel_distortions(mode): + # Import astropy.coordinates here to avoid circular imports + from astropy.coordinates import SkyCoord + + header = get_pkg_data_filename("data/sip.fits") + with pytest.warns(FITSFixedWarning): + wcs = WCS(header) + + ref = SkyCoord(202.50 * u.deg, 47.19 * u.deg, frame="icrs") + + xp, yp = skycoord_to_pixel(ref, wcs, mode=mode) + + # WCS is in FK5 so we need to transform back to ICRS + new = pixel_to_skycoord(xp, yp, wcs, mode=mode).transform_to("icrs") + + assert_allclose(new.ra.degree, ref.ra.degree) + assert_allclose(new.dec.degree, ref.dec.degree) + + +@pytest.fixture +def spatial_wcs_2d_small_angle(): + """ + This WCS has an almost linear correlation between the pixel and world axes + close to the reference pixel. + """ + wcs = WCS(naxis=2) + wcs.wcs.ctype = ["HPLN-TAN", "HPLT-TAN"] + wcs.wcs.crpix = [3.0] * 2 + wcs.wcs.cdelt = [0.002] * 2 + wcs.wcs.crval = [0] * 2 + wcs.wcs.set() + return wcs + + +def test_local_pixel_derivatives(spatial_wcs_2d_small_angle): + not_diag = np.logical_not(np.diag([1, 1])) + # At (or close to) the reference pixel this should equal the cdelt + derivs = local_partial_pixel_derivatives(spatial_wcs_2d_small_angle, 3, 3) + np.testing.assert_allclose(np.diag(derivs), spatial_wcs_2d_small_angle.wcs.cdelt) + np.testing.assert_allclose(derivs[not_diag].flat, [0, 0], atol=1e-10) + + # Far away from the reference pixel this should not equal the cdelt + derivs = local_partial_pixel_derivatives(spatial_wcs_2d_small_angle, 3e4, 3e4) + assert not np.allclose(np.diag(derivs), spatial_wcs_2d_small_angle.wcs.cdelt) + + # At (or close to) the reference pixel this should equal the cdelt + derivs = local_partial_pixel_derivatives( + spatial_wcs_2d_small_angle, 3, 3, normalize_by_world=True + ) + np.testing.assert_allclose(np.diag(derivs), [1, 1]) + np.testing.assert_allclose(derivs[not_diag].flat, [0, 0], atol=1e-8) + + +def test_local_pixel_derivatives_cube(): + cube_wcs = WCS(naxis=3) + cube_wcs.wcs.ctype = "RA---TAN", "DEC--TAN", "FREQ" + cube_wcs.wcs.crval = 10, 20, 30 + cube_wcs.wcs.cdelt = 0.0001, 0.0001, 0.01 + cube_wcs.wcs.cunit = "deg", "deg", "GHz" + cube_wcs.wcs.set() + + derivs = local_partial_pixel_derivatives(cube_wcs, 0, 0, 0) + np.testing.assert_allclose( + derivs, [[0.0001, 0, 0], [0, 0.0001, 0], [0, 0, 1e7]], rtol=0.1, atol=1e-5 + ) + + derivs = local_partial_pixel_derivatives(cube_wcs, 0, 0, 0, normalize_by_world=True) + np.testing.assert_allclose( + derivs, [[1, 0, 0], [0, 1, 0], [0, 0, 1]], rtol=0.1, atol=1e-5 + ) + + # Slice WCS so that there are two pixel and three world coordinates + sliced_wcs = cube_wcs[:, 0, :] + + derivs = local_partial_pixel_derivatives(sliced_wcs, 0, 0, 0) + np.testing.assert_allclose( + derivs, [[0.0001, 0], [0, 0], [0, 1e7]], rtol=0.1, atol=1e-5 + ) + + derivs = local_partial_pixel_derivatives( + sliced_wcs, 0, 0, 0, normalize_by_world=True + ) + np.testing.assert_allclose(derivs, [[1, 0], [1, 0], [0, 1]], rtol=0.1, atol=1e-5) + + +def test_pixel_to_world_correlation_matrix_celestial(): + wcs = WCS(naxis=2) + wcs.wcs.ctype = "RA---TAN", "DEC--TAN" + wcs.wcs.set() + + assert_equal(wcs.axis_correlation_matrix, [[1, 1], [1, 1]]) + matrix, classes = _pixel_to_world_correlation_matrix(wcs) + assert_equal(matrix, [[1, 1]]) + assert classes == [SkyCoord] + + +def test_pixel_to_world_correlation_matrix_spectral_cube_uncorrelated(): + wcs = WCS(naxis=3) + wcs.wcs.ctype = "RA---TAN", "FREQ", "DEC--TAN" + wcs.wcs.set() + + assert_equal(wcs.axis_correlation_matrix, [[1, 0, 1], [0, 1, 0], [1, 0, 1]]) + matrix, classes = _pixel_to_world_correlation_matrix(wcs) + assert_equal(matrix, [[1, 0, 1], [0, 1, 0]]) + assert classes == [SkyCoord, Quantity] + + +def test_pixel_to_world_correlation_matrix_spectral_cube_correlated(): + wcs = WCS(naxis=3) + wcs.wcs.ctype = "RA---TAN", "FREQ", "DEC--TAN" + wcs.wcs.cd = np.ones((3, 3)) + wcs.wcs.set() + + assert_equal(wcs.axis_correlation_matrix, [[1, 1, 1], [1, 1, 1], [1, 1, 1]]) + matrix, classes = _pixel_to_world_correlation_matrix(wcs) + assert_equal(matrix, [[1, 1, 1], [1, 1, 1]]) + assert classes == [SkyCoord, Quantity] + + +def test_pixel_to_pixel_correlation_matrix_celestial(): + wcs_in = WCS(naxis=2) + wcs_in.wcs.ctype = "RA---TAN", "DEC--TAN" + wcs_in.wcs.set() + + wcs_out = WCS(naxis=2) + wcs_out.wcs.ctype = "DEC--TAN", "RA---TAN" + wcs_out.wcs.set() + + matrix = _pixel_to_pixel_correlation_matrix(wcs_in, wcs_out) + assert_equal(matrix, [[1, 1], [1, 1]]) + + +def test_pixel_to_pixel_correlation_matrix_spectral_cube_uncorrelated(): + wcs_in = WCS(naxis=3) + wcs_in.wcs.ctype = "RA---TAN", "DEC--TAN", "FREQ" + wcs_in.wcs.set() + + wcs_out = WCS(naxis=3) + wcs_out.wcs.ctype = "DEC--TAN", "FREQ", "RA---TAN" + wcs_out.wcs.set() + + matrix = _pixel_to_pixel_correlation_matrix(wcs_in, wcs_out) + assert_equal(matrix, [[1, 1, 0], [0, 0, 1], [1, 1, 0]]) + + +def test_pixel_to_pixel_correlation_matrix_spectral_cube_correlated(): + # NOTE: only make one of the WCSes have correlated axes to really test this + + wcs_in = WCS(naxis=3) + wcs_in.wcs.ctype = "RA---TAN", "DEC--TAN", "FREQ" + wcs_in.wcs.set() + + wcs_out = WCS(naxis=3) + wcs_out.wcs.ctype = "DEC--TAN", "FREQ", "RA---TAN" + wcs_out.wcs.cd = np.ones((3, 3)) + wcs_out.wcs.set() + + matrix = _pixel_to_pixel_correlation_matrix(wcs_in, wcs_out) + assert_equal(matrix, [[1, 1, 1], [1, 1, 1], [1, 1, 1]]) + + +def test_pixel_to_pixel_correlation_matrix_mismatch(): + wcs_in = WCS(naxis=2) + wcs_in.wcs.ctype = "RA---TAN", "DEC--TAN" + wcs_in.wcs.set() + + wcs_out = WCS(naxis=3) + wcs_out.wcs.ctype = "DEC--TAN", "FREQ", "RA---TAN" + wcs_out.wcs.set() + + with pytest.raises( + ValueError, match=r"The two WCS return a different number of world coordinates" + ): + _pixel_to_pixel_correlation_matrix(wcs_in, wcs_out) + + wcs3 = WCS(naxis=2) + wcs3.wcs.ctype = "FREQ", "PIXEL" + wcs3.wcs.set() + + with pytest.raises( + ValueError, match=r"The world coordinate types of the two WCS do not match" + ): + _pixel_to_pixel_correlation_matrix(wcs_out, wcs3) + + wcs4 = WCS(naxis=4) + wcs4.wcs.ctype = "RA---TAN", "DEC--TAN", "Q1", "Q2" + wcs4.wcs.cunit = ["deg", "deg", "m/s", "m/s"] + wcs4.wcs.set() + + wcs5 = WCS(naxis=4) + wcs5.wcs.ctype = "Q1", "RA---TAN", "DEC--TAN", "Q2" + wcs5.wcs.cunit = ["m/s", "deg", "deg", "m/s"] + wcs5.wcs.set() + + with pytest.raises( + ValueError, + match=( + "World coordinate order doesn't match and automatic matching is ambiguous" + ), + ): + _pixel_to_pixel_correlation_matrix(wcs4, wcs5) + + +def test_pixel_to_pixel_correlation_matrix_nonsquare(): + # Here we set up an input WCS that maps 3 pixel coordinates to 4 world + # coordinates - the idea is to make sure that things work fine in cases + # where the number of input and output pixel coordinates do not match. + + class FakeWCS: + pass + + wcs_in = FakeWCS() + wcs_in.low_level_wcs = wcs_in + wcs_in.pixel_n_dim = 3 + wcs_in.world_n_dim = 4 + wcs_in.axis_correlation_matrix = [ + [True, True, False], + [True, True, False], + [True, True, False], + [False, False, True], + ] + wcs_in.world_axis_object_components = [ + ("spat", "ra", "ra.degree"), + ("spat", "dec", "dec.degree"), + ("spec", 0, "value"), + ("time", 0, "utc.value"), + ] + wcs_in.world_axis_object_classes = { + "spat": ("astropy.coordinates.SkyCoord", (), {"frame": "icrs"}), + "spec": ("astropy.units.Wavelength", (None,), {}), + "time": ("astropy.time.Time", (None,), {"format": "mjd", "scale": "utc"}), + } + + wcs_out = FakeWCS() + wcs_out.low_level_wcs = wcs_out + wcs_out.pixel_n_dim = 4 + wcs_out.world_n_dim = 4 + wcs_out.axis_correlation_matrix = [ + [True, False, False, False], + [False, True, True, False], + [False, True, True, False], + [False, False, False, True], + ] + wcs_out.world_axis_object_components = [ + ("spec", 0, "value"), + ("spat", "ra", "ra.degree"), + ("spat", "dec", "dec.degree"), + ("time", 0, "utc.value"), + ] + wcs_out.world_axis_object_classes = wcs_in.world_axis_object_classes + + matrix = _pixel_to_pixel_correlation_matrix(wcs_in, wcs_out) + + matrix = matrix.astype(int) + + # The shape should be (n_pixel_out, n_pixel_in) + assert matrix.shape == (4, 3) + + expected = np.array([[1, 1, 0], [1, 1, 0], [1, 1, 0], [0, 0, 1]]) + assert_equal(matrix, expected) + + +def test_split_matrix(): + assert _split_matrix(np.array([[1]])) == [([0], [0])] + + assert _split_matrix( + np.array( + [ + [1, 1], + [1, 1], + ] + ) + ) == [([0, 1], [0, 1])] + + assert _split_matrix( + np.array( + [ + [1, 1, 0], + [1, 1, 0], + [0, 0, 1], + ] + ) + ) == [([0, 1], [0, 1]), ([2], [2])] + + assert _split_matrix( + np.array( + [ + [0, 1, 0], + [1, 0, 0], + [0, 0, 1], + ] + ) + ) == [([0], [1]), ([1], [0]), ([2], [2])] + + assert _split_matrix( + np.array( + [ + [0, 1, 1], + [1, 0, 0], + [1, 0, 1], + ] + ) + ) == [([0, 1, 2], [0, 1, 2])] + + +def test_pixel_to_pixel(): + wcs_in = WCS(naxis=3) + wcs_in.wcs.ctype = "DEC--TAN", "FREQ", "RA---TAN" + wcs_in.wcs.set() + + wcs_out = WCS(naxis=3) + wcs_out.wcs.ctype = "GLON-CAR", "GLAT-CAR", "FREQ" + wcs_out.wcs.set() + + # First try with scalars + x, y, z = pixel_to_pixel(wcs_in, wcs_out, 1, 2, 3) + assert x.shape == () + assert y.shape == () + assert z.shape == () + + # Now try with broadcasted arrays + x = np.linspace(10, 20, 10) + y = np.linspace(10, 20, 20) + z = np.linspace(10, 20, 30) + Z1, Y1, X1 = np.meshgrid(z, y, x, indexing="ij", copy=False) + X2, Y2, Z2 = pixel_to_pixel(wcs_in, wcs_out, X1, Y1, Z1) + + # The final arrays should have the correct shape + assert X2.shape == (30, 20, 10) + assert Y2.shape == (30, 20, 10) + assert Z2.shape == (30, 20, 10) + + # But behind the scenes should also be broadcasted + assert unbroadcast(X2).shape == (30, 1, 10) + assert unbroadcast(Y2).shape == (30, 1, 10) + assert unbroadcast(Z2).shape == (20, 1) + + # We can put the values back through the function to ensure round-tripping + X3, Y3, Z3 = pixel_to_pixel(wcs_out, wcs_in, X2, Y2, Z2) + + # The final arrays should have the correct shape + assert X2.shape == (30, 20, 10) + assert Y2.shape == (30, 20, 10) + assert Z2.shape == (30, 20, 10) + + # But behind the scenes should also be broadcasted + assert unbroadcast(X3).shape == (30, 1, 10) + assert unbroadcast(Y3).shape == (20, 1) + assert unbroadcast(Z3).shape == (30, 1, 10) + + # And these arrays should match the input + assert_allclose(X1, X3) + assert_allclose(Y1, Y3) + assert_allclose(Z1, Z3) + + +def test_pixel_to_pixel_correlated(): + wcs_in = WCS(naxis=2) + wcs_in.wcs.ctype = "DEC--TAN", "RA---TAN" + wcs_in.wcs.set() + + wcs_out = WCS(naxis=2) + wcs_out.wcs.ctype = "GLON-CAR", "GLAT-CAR" + wcs_out.wcs.set() + + # First try with scalars + x, y = pixel_to_pixel(wcs_in, wcs_out, 1, 2) + assert x.shape == () + assert y.shape == () + + # Now try with broadcasted arrays + x = np.linspace(10, 20, 10) + y = np.linspace(10, 20, 20) + Y1, X1 = np.meshgrid(y, x, indexing="ij", copy=False) + Y2, X2 = pixel_to_pixel(wcs_in, wcs_out, X1, Y1) + + # The final arrays should have the correct shape + assert X2.shape == (20, 10) + assert Y2.shape == (20, 10) + + # and there are no efficiency gains here since the celestial axes are correlated + assert unbroadcast(X2).shape == (20, 10) + + +def test_pixel_to_pixel_1d(): + # Simple test to make sure that when WCS only returns one world coordinate + # this still works correctly (since this requires special treatment behind + # the scenes). + + wcs_in = WCS(naxis=1) + wcs_in.wcs.ctype = ("COORD1",) + wcs_in.wcs.cunit = ("nm",) + wcs_in.wcs.set() + + wcs_out = WCS(naxis=1) + wcs_out.wcs.ctype = ("COORD2",) + wcs_out.wcs.cunit = ("cm",) + wcs_out.wcs.set() + + # First try with a scalar + x = pixel_to_pixel(wcs_in, wcs_out, 1) + assert x.shape == () + + # Next with a regular array + x = np.linspace(10, 20, 10) + x = pixel_to_pixel(wcs_in, wcs_out, x) + assert x.shape == (10,) + + # And now try with a broadcasted array + x = np.broadcast_to(np.linspace(10, 20, 10), (4, 10)) + x = pixel_to_pixel(wcs_in, wcs_out, x) + assert x.shape == (4, 10) + + # The broadcasting of the input should be retained + assert unbroadcast(x).shape == (10,) + + +header_str_linear = """ +XTENSION= 'IMAGE ' / Image extension +BITPIX = -32 / array data type +NAXIS = 2 / number of array dimensions +NAXIS1 = 50 +NAXIS2 = 50 +PCOUNT = 0 / number of parameters +GCOUNT = 1 / number of groups +RADESYS = 'ICRS ' +EQUINOX = 2000.0 +WCSAXES = 2 +CTYPE1 = 'RA---TAN' +CTYPE2 = 'DEC--TAN' +CRVAL1 = 250.3497414839765 +CRVAL2 = 2.280925599609063 +CRPIX1 = 1045.0 +CRPIX2 = 1001.0 +CD1_1 = -0.005564478186178 +CD1_2 = -0.001042099258152 +CD2_1 = 0.00118144146585 +CD2_2 = -0.005590816683583 +""" +header_str_sip = """ +XTENSION= 'IMAGE ' / Image extension +BITPIX = -32 / array data type +NAXIS = 2 / number of array dimensions +NAXIS1 = 50 +NAXIS2 = 50 +PCOUNT = 0 / number of parameters +GCOUNT = 1 / number of groups +RADESYS = 'ICRS ' +EQUINOX = 2000.0 +WCSAXES = 2 +CTYPE1 = 'RA---TAN-SIP' +CTYPE2 = 'DEC--TAN-SIP' +CRVAL1 = 250.3497414839765 +CRVAL2 = 2.280925599609063 +CRPIX1 = 1045.0 +CRPIX2 = 1001.0 +CD1_1 = -0.005564478186178 +CD1_2 = -0.001042099258152 +CD2_1 = 0.00118144146585 +CD2_2 = -0.005590816683583 +A_ORDER = 2 +B_ORDER = 2 +A_2_0 = 2.02451189234E-05 +A_0_2 = 3.317603337918E-06 +A_1_1 = 1.73456334971071E-05 +B_2_0 = 3.331330003472E-06 +B_0_2 = 2.04247482482589E-05 +B_1_1 = 1.71476710804143E-05 +AP_ORDER= 2 +BP_ORDER= 2 +AP_1_0 = 0.000904700296389636 +AP_0_1 = 0.000627660715584716 +AP_2_0 = -2.023482905861E-05 +AP_0_2 = -3.332285841011E-06 +AP_1_1 = -1.731636633824E-05 +BP_1_0 = 0.000627960882053211 +BP_0_1 = 0.000911222886084808 +BP_2_0 = -3.343918167224E-06 +BP_0_2 = -2.041598249021E-05 +BP_1_1 = -1.711876336719E-05 +A_DMAX = 44.72893589844534 +B_DMAX = 44.62692873032506 +""" +header_str_prob = """ +NAXIS = 2 / number of array dimensions +WCSAXES = 2 / Number of coordinate axes +CRPIX1 = 1024.5 / Pixel coordinate of reference point +CRPIX2 = 1024.5 / Pixel coordinate of reference point +CD1_1 = -1.7445934400771E-05 / Coordinate transformation matrix element +CD1_2 = -4.9826985362578E-08 / Coordinate transformation matrix element +CD2_1 = -5.0068838822312E-08 / Coordinate transformation matrix element +CD2_2 = 1.7530614610951E-05 / Coordinate transformation matrix element +CTYPE1 = 'RA---TAN' / Right ascension, gnomonic projection +CTYPE2 = 'DEC--TAN' / Declination, gnomonic projection +CRVAL1 = 5.8689341666667 / [deg] Coordinate value at reference point +CRVAL2 = -71.995508583333 / [deg] Coordinate value at reference point +""" + + +@pytest.mark.skipif(not HAS_SCIPY, reason="requires scipy") +@pytest.mark.parametrize( + "header_str,crval,sip_degree,user_proj_point,exp_max_dist,exp_std_dist", + [ + # simple testset no distortions + ( + header_str_linear, + 250.3497414839765, + None, + False, + 7e-5 * u.deg, + 2.5e-5 * u.deg, + ), + # simple testset with distortions + (header_str_sip, 250.3497414839765, 2, False, 7e-6 * u.deg, 2.5e-6 * u.deg), + # testset with problematic WCS header that failed before + (header_str_prob, 5.8689341666667, None, False, 7e-6 * u.deg, 2.5e-6 * u.deg), + # simple testset no distortions, user defined center + ( + header_str_linear, + 250.3497414839765, + None, + True, + 7e-5 * u.deg, + 2.5e-5 * u.deg, + ), + # 360->0 degree crossover, simple testset no distortions + ( + header_str_linear, + 352.3497414839765, + None, + False, + 7e-5 * u.deg, + 2.5e-5 * u.deg, + ), + # 360->0 degree crossover, simple testset with distortions + (header_str_sip, 352.3497414839765, 2, False, 7e-6 * u.deg, 2.5e-6 * u.deg), + # 360->0 degree crossover, testset with problematic WCS header that failed before + (header_str_prob, 352.3497414839765, None, False, 7e-6 * u.deg, 2.5e-6 * u.deg), + # 360->0 degree crossover, simple testset no distortions, user defined center + ( + header_str_linear, + 352.3497414839765, + None, + True, + 7e-5 * u.deg, + 2.5e-5 * u.deg, + ), + ], +) +def test_fit_wcs_from_points( + header_str, crval, sip_degree, user_proj_point, exp_max_dist, exp_std_dist +): + header = fits.Header.fromstring(header_str, sep="\n") + header["CRVAL1"] = crval + + true_wcs = WCS(header, relax=True) + + # Getting the pixel coordinates + x, y = np.meshgrid(list(range(10)), list(range(10))) + x = x.flatten() + y = y.flatten() + + # Calculating the true sky positions + world_pix = true_wcs.pixel_to_world(x, y) + + # which projection point to use + if user_proj_point: + proj_point = world_pix[0] + projlon = proj_point.data.lon.deg + projlat = proj_point.data.lat.deg + else: + proj_point = "center" + + # Fitting the wcs + fit_wcs = fit_wcs_from_points( + (x, y), world_pix, proj_point=proj_point, sip_degree=sip_degree + ) + + # Validate that the true sky coordinates + # match sky coordinates calculated from the wcs fit + world_pix_new = fit_wcs.pixel_to_world(x, y) + + dists = world_pix.separation(world_pix_new) + + assert dists.max() < exp_max_dist + assert np.std(dists) < exp_std_dist + + if user_proj_point: + assert (fit_wcs.wcs.crval == [projlon, projlat]).all() + + +@pytest.mark.skipif(not HAS_SCIPY, reason="requires scipy") +def test_fit_wcs_from_points_CRPIX_bounds(): + # Test CRPIX bounds requirement + wcs_str = """ +WCSAXES = 2 / Number of coordinate axes +CRPIX1 = 1045.0 / Pixel coordinate of reference point +CRPIX2 = 1001.0 / Pixel coordinate of reference point +PC1_1 = 0.00056205870415378 / Coordinate transformation matrix element +PC1_2 = -0.00569181083243 / Coordinate transformation matrix element +PC2_1 = 0.0056776810932466 / Coordinate transformation matrix element +PC2_2 = 0.0004208048403273 / Coordinate transformation matrix element +CDELT1 = 1.0 / [deg] Coordinate increment at reference point +CDELT2 = 1.0 / [deg] Coordinate increment at reference point +CUNIT1 = 'deg' / Units of coordinate increment and value +CUNIT2 = 'deg' / Units of coordinate increment and value +CTYPE1 = 'RA---TAN' / Right ascension, gnomonic projection +CTYPE2 = 'DEC--TAN' / Declination, gnomonic projection +CRVAL1 = 104.57797893504 / [deg] Coordinate value at reference point +CRVAL2 = -74.195502593322 / [deg] Coordinate value at reference point +LONPOLE = 180.0 / [deg] Native longitude of celestial pole +LATPOLE = -74.195502593322 / [deg] Native latitude of celestial pole +TIMESYS = 'TDB' / Time scale +TIMEUNIT= 'd' / Time units +DATEREF = '1858-11-17' / ISO-8601 fiducial time +MJDREFI = 0.0 / [d] MJD of fiducial time, integer part +MJDREFF = 0.0 / [d] MJD of fiducial time, fractional part +DATE-OBS= '2019-03-27T03:30:13.832Z' / ISO-8601 time of observation +MJD-OBS = 58569.145993426 / [d] MJD of observation +MJD-OBS = 58569.145993426 / [d] MJD at start of observation +TSTART = 1569.6467941661 / [d] Time elapsed since fiducial time at start +DATE-END= '2019-03-27T04:00:13.831Z' / ISO-8601 time at end of observation +MJD-END = 58569.166826748 / [d] MJD at end of observation +TSTOP = 1569.6676274905 / [d] Time elapsed since fiducial time at end +TELAPSE = 0.02083332443 / [d] Elapsed time (start to stop) +TIMEDEL = 0.020833333333333 / [d] Time resolution +TIMEPIXR= 0.5 / Reference position of timestamp in binned data +RADESYS = 'ICRS' / Equatorial coordinate system +""" + wcs_header = fits.Header.fromstring(wcs_str, sep="\n") + ffi_wcs = WCS(wcs_header) + + yi, xi = (1000, 1000) + y, x = (10, 200) + + center_coord = SkyCoord( + ffi_wcs.all_pix2world([[xi + x // 2, yi + y // 2]], 0), unit="deg" + )[0] + ypix, xpix = (arr.flatten() for arr in np.mgrid[xi : xi + x, yi : yi + y]) + world_pix = SkyCoord(*ffi_wcs.all_pix2world(xpix, ypix, 0), unit="deg") + + fit_wcs = fit_wcs_from_points((ypix, xpix), world_pix, proj_point="center") + + assert (fit_wcs.wcs.crpix.astype(int) == [1100, 1005]).all() + assert fit_wcs.pixel_shape == (1199, 1009) + + +@pytest.mark.skipif(not HAS_SCIPY, reason="requires scipy") +def test_issue10991(): + # test issue #10991 (it just needs to run and set the user defined crval) + xy = np.array( + [ + [1766.88276168, 662.96432257, 171.50212526, 120.70924648], + [1706.69832901, 1788.85480559, 1216.98949653, 1307.41843381], + ] + ) + world_coords = SkyCoord( + [ + (66.3542367, 22.20000162), + (67.15416174, 19.18042906), + (65.73375432, 17.54251555), + (66.02400512, 17.44413253), + ], + frame="icrs", + unit="deg", + ) + proj_point = SkyCoord(64.67514918, 19.63389538, frame="icrs", unit="deg") + + fit_wcs = fit_wcs_from_points( + xy=xy, world_coords=world_coords, proj_point=proj_point, projection="TAN" + ) + projlon = proj_point.data.lon.deg + projlat = proj_point.data.lat.deg + assert (fit_wcs.wcs.crval == [projlon, projlat]).all() + + +@pytest.mark.skipif(not HAS_SCIPY, reason="requires scipy") +def test_fit_wcs_from_points_returned_object_attributes(): + xy = ( + np.array( + [ + 2810.156, + 650.236, + 1820.927, + 3425.779, + 2750.369, + ] + ), + np.array( + [ + 1670.347, + 360.325, + 165.663, + 900.922, + 700.148, + ] + ), + ) + ra, dec = ( + np.array( + [ + 246.75001315, + 246.72033646, + 246.72303144, + 246.74164072, + 246.73540614, + ] + ), + np.array( + [ + 43.48690547, + 43.46792989, + 43.48075238, + 43.49560501, + 43.48903538, + ] + ), + ) + radec = SkyCoord(ra, dec, unit=(u.deg, u.deg)) + + placeholder_wcs = celestial_frame_to_wcs(frame=radec.frame, projection="TAN") + estimated_wcs = fit_wcs_from_points(xy, radec, projection=placeholder_wcs) + + estimated_wcs_attributes = sorted(dir(estimated_wcs)) + placeholder_wcs_attributes = sorted(dir(placeholder_wcs)) + assert estimated_wcs_attributes == placeholder_wcs_attributes + + +@pytest.mark.remote_data +@pytest.mark.parametrize("x_in,y_in", [[0, 0], [np.arange(5), np.arange(5)]]) +def test_pixel_to_world_itrs(x_in, y_in): + """Regression test for https://github.com/astropy/astropy/pull/9609""" + if Version(WCSLIB_VERSION) >= Version("7.4"): + ctx = pytest.warns( + FITSFixedWarning, + match=( + r"'datfix' made the change 'Set MJD-OBS to 57982\.528524 from" + r" DATE-OBS'\." + ), + ) + else: + ctx = nullcontext() + + with ctx: + wcs = WCS( + { + "NAXIS": 2, + "CTYPE1": "TLON-CAR", + "CTYPE2": "TLAT-CAR", + "RADESYS": "ITRS ", + "DATE-OBS": "2017-08-17T12:41:04.444", + } + ) + + # This shouldn't raise an exception. + coord = wcs.pixel_to_world(x_in, y_in) + + # Check round trip transformation. + x, y = wcs.world_to_pixel(coord) + + np.testing.assert_almost_equal(x, x_in) + np.testing.assert_almost_equal(y, y_in) + + +@pytest.fixture +def dkist_location(): + return EarthLocation( + *(-5466045.25695494, -2404388.73741278, 2242133.88769004) * u.m + ) + + +def test_obsgeo_cartesian(dkist_location): + obstime = Time("2021-05-21T03:00:00") + wcs = WCS(naxis=2) + wcs.wcs.obsgeo = list(dkist_location.to_value(u.m).tolist()) + [0, 0, 0] + wcs.wcs.dateobs = obstime.isot + + frame = obsgeo_to_frame(wcs.wcs.obsgeo, obstime) + + assert isinstance(frame, ITRS) + assert frame.x == dkist_location.x + assert frame.y == dkist_location.y + assert frame.z == dkist_location.z + + +def test_obsgeo_spherical(dkist_location): + obstime = Time("2021-05-21T03:00:00") + dkist_location = dkist_location.get_itrs(obstime) + loc_sph = dkist_location.spherical + + wcs = WCS(naxis=2) + wcs.wcs.obsgeo = [0, 0, 0] + [ + loc_sph.lon.value, + loc_sph.lat.value, + loc_sph.distance.value, + ] + wcs.wcs.dateobs = obstime.isot + + frame = obsgeo_to_frame(wcs.wcs.obsgeo, obstime) + + assert isinstance(frame, ITRS) + assert u.allclose(frame.x, dkist_location.x) + assert u.allclose(frame.y, dkist_location.y) + assert u.allclose(frame.z, dkist_location.z) + + +def test_obsgeo_infinite(dkist_location): + obstime = Time("2021-05-21T03:00:00") + dkist_location = dkist_location.get_itrs(obstime) + loc_sph = dkist_location.spherical + + wcs = WCS(naxis=2) + wcs.wcs.obsgeo = [1, 1, np.nan] + [ + loc_sph.lon.value, + loc_sph.lat.value, + loc_sph.distance.value, + ] + wcs.wcs.dateobs = obstime.isot + wcs.wcs.set() + + frame = obsgeo_to_frame(wcs.wcs.obsgeo, obstime) + + assert isinstance(frame, ITRS) + assert u.allclose(frame.x, dkist_location.x) + assert u.allclose(frame.y, dkist_location.y) + assert u.allclose(frame.z, dkist_location.z) + + +@pytest.mark.parametrize("obsgeo", ([np.nan] * 6, None, [0] * 6, [54] * 5)) +def test_obsgeo_invalid(obsgeo): + with pytest.raises(ValueError): + obsgeo_to_frame(obsgeo, None) + + +def test_custom_wcs_to_from_frame(): + # See https://github.com/astropy/astropy/issues/15625 + # test from Sam van Kooten + + class CustomFrame(BaseCoordinateFrame): + obstime = Time("2017-08-17T12:41:04.43") + + def custom_wcs_frame_mapping(wcs): + ctypes = {c[:4] for c in wcs.wcs.ctype} + if not ({"CSLN", "CSLT"} <= ctypes): + return None + + dateobs = wcs.wcs.dateavg or wcs.wcs.dateobs or None + custom_frame = CustomFrame() + return custom_frame + + def custom_frame_wcs_mapping(frame, projection="TAN"): + if not isinstance(frame, CustomFrame): + return None + wcs = WCS(naxis=2) + wcs.wcs.ctype = [f"CSLN-{projection}", f"CSLT-{projection}"] + return wcs + + WCS_FRAME_MAPPINGS.append([custom_wcs_frame_mapping]) + FRAME_WCS_MAPPINGS.append([custom_frame_wcs_mapping]) + + mywcs = WCS(naxis=2) + mywcs.wcs.ctype = ["CSLN-TAN", "CSLT-TAN"] + custom_frame = custom_wcs_frame_mapping(mywcs) + assert isinstance(custom_frame, CustomFrame) + + custom_wcs = custom_frame_wcs_mapping(custom_frame) + print(custom_wcs.wcs.ctype) + assert custom_wcs.wcs.ctype[0] == "CSLN-TAN" + assert custom_wcs.wcs.ctype[1] == "CSLT-TAN" diff --git a/astropy/wcs/tests/test_wcs.py b/astropy/wcs/tests/test_wcs.py index 05f099e3c972..240c3ccc5955 100644 --- a/astropy/wcs/tests/test_wcs.py +++ b/astropy/wcs/tests/test_wcs.py @@ -1,245 +1,2612 @@ -import glob +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +import io import os -import sys -import warnings +import re +from contextlib import nullcontext +from datetime import datetime +from multiprocessing.pool import ThreadPool import numpy as np -from numpy.testing import assert_array_almost_equal +import pytest +from numpy.random import default_rng +from numpy.testing import ( + assert_allclose, + assert_array_almost_equal, + assert_array_almost_equal_nulp, + assert_array_equal, +) +from packaging.version import Version -from ... import wcs -from ...config import get_data_filenames, get_data_contents, get_data_filename -from ...tests.helper import pytest +from astropy import units as u +from astropy import wcs +from astropy.coordinates import SkyCoord +from astropy.io import fits +from astropy.io.fits.verify import VerifyWarning +from astropy.nddata import Cutout2D +from astropy.tests.helper import assert_quantity_allclose +from astropy.utils.data import ( + get_pkg_data_contents, + get_pkg_data_filename, + get_pkg_data_filenames, +) +from astropy.utils.exceptions import ( + AstropyDeprecationWarning, + AstropyUserWarning, + AstropyWarning, +) +from astropy.utils.misc import NumpyRNGContext +from astropy.wcs.wcs import WCSLIB_VERSION -# test_maps() is a generator -def test_maps(): +# NOTE: User can choose to use system wcslib instead of bundled. +def ctx_for_v71_dateref_warnings(): + if Version("7.1") <= Version(WCSLIB_VERSION) < Version("7.3"): + ctx = pytest.warns( + wcs.FITSFixedWarning, + match=( + r"'datfix' made the change 'Set DATE-REF to '1858-11-17' from" + r" MJD-REF'\." + ), + ) + else: + ctx = nullcontext() + return ctx - # test_map() is the function that is called to perform the generated test - def test_map(filename): - # the test parameter is the base name of the file to use; find - # the file in the installed wcs test directory - header = get_data_contents(os.path.join("maps", filename)) - wcsobj = wcs.WCS(header) +def strip_memory_addresses(text: str) -> str: + """ + Replace hex memory addresses (e.g., 0x600000acf000) in the text with a placeholder. + """ + return re.sub(r"0x[0-9a-fA-F]+", "0xADDR", text) - world = wcsobj.wcs_pix2world([[97, 97]], 1) - assert_array_almost_equal(world, [[285.0, -66.25]], decimal=1) +class TestMaps: + def setup_method(self): + # get the list of the hdr files that we want to test + self._file_list = list(get_pkg_data_filenames("data/maps", pattern="*.hdr")) - pix = wcsobj.wcs_world2pix([[285.0, -66.25]], 1) + def test_consistency(self): + # Check to see that we actually have the list we expect, so that we + # do not get in a situation where the list is empty or incomplete and + # the tests still seem to pass correctly. - assert_array_almost_equal(pix, [[97, 97]], decimal=0) + # how many do we expect to see? + n_data_files = 28 - # get the list of the hdr files that we want to test - hdr_file_list = list(get_data_filenames("maps", "*.hdr")) + assert len(self._file_list) == n_data_files, ( + f"test_spectra has wrong number data files: found {len(self._file_list)}," + f" expected {n_data_files}" + ) - # actually perform a test for each one - for filename in hdr_file_list: + def test_maps(self): + for filename in self._file_list: + # use the base name of the file, so we get more useful messages + # for failing tests. + filename = os.path.basename(filename) + # Now find the associated file in the installed wcs test directory. + header = get_pkg_data_contents( + os.path.join("data", "maps", filename), encoding="binary" + ) + # finally run the test. + wcsobj = wcs.WCS(header) + world = wcsobj.wcs_pix2world([[97, 97]], 1) + assert_array_almost_equal(world, [[285.0, -66.25]], decimal=1) + pix = wcsobj.wcs_world2pix([[285.0, -66.25]], 1) + assert_array_almost_equal(pix, [[97, 97]], decimal=0) - # use the base name of the file, because everything we yield - # will show up in the test name in the pandokia report - filename = os.path.basename(filename) - # yield a function name and parameters to make a generated test - yield test_map, filename +class TestSpectra: + def setup_method(self): + self._file_list = list(get_pkg_data_filenames("data/spectra", pattern="*.hdr")) - # AFTER we tested with every file that we found, check to see that we - # actually have the list we expect. If N=0, we will not have performed - # any tests at all. If N < n_data_files, we are missing some files, - # so we will have skipped some tests. Without this check, both cases - # happen silently! + def test_consistency(self): + # Check to see that we actually have the list we expect, so that we + # do not get in a situation where the list is empty or incomplete and + # the tests still seem to pass correctly. - # how many do we expect to see? - n_data_files = 28 + # how many do we expect to see? + n_data_files = 6 - if len(hdr_file_list) != n_data_files: - assert False, ( - "test_maps has wrong number data files: found %d, expected " - " %d" % (len(hdr_file_list), n_data_files)) - # b.t.w. If this assert happens, py.test reports one more test - # than it would have otherwise. + assert len(self._file_list) == n_data_files, ( + f"test_spectra has wrong number data files: found {len(self._file_list)}," + f" expected {n_data_files}" + ) + def test_spectra(self): + for filename in self._file_list: + # use the base name of the file, so we get more useful messages + # for failing tests. + filename = os.path.basename(filename) + # Now find the associated file in the installed wcs test directory. + header = get_pkg_data_contents( + os.path.join("data", "spectra", filename), encoding="binary" + ) + # finally run the test. + if Version(WCSLIB_VERSION) >= Version("7.4"): + ctx = pytest.warns( + wcs.FITSFixedWarning, + match=( + r"'datfix' made the change 'Set MJD-OBS to 53925\.853472 from" + r" DATE-OBS'\." + ), + ) + else: + ctx = nullcontext() + with ctx: + all_wcs = wcs.find_all_wcs(header) -# test_spectra() is a generator -def test_spectra(): + assert len(all_wcs) == 9 - # test_spectrum() is the function that is called to perform the - # generated test - def test_spectrum(filename): - # the test parameter is the base name of the file to use; find - # the file in the installed wcs test directory - header = get_data_contents(os.path.join("spectra", filename)) +def test_fixes(): + """ + From github issue #36 + """ + header = get_pkg_data_contents("data/nonstandard_units.hdr", encoding="binary") - wcsobj = wcs.WCS(header) + with ( + pytest.raises(wcs.InvalidTransformError), + pytest.warns(wcs.FITSFixedWarning) as w, + ): + wcs.WCS(header, translate_units="dhs") - all = wcs.find_all_wcs(header) - assert len(all) == 9 + if Version("7.4") <= Version(WCSLIB_VERSION) < Version("7.6"): + assert len(w) == 3 + assert "'datfix' made the change 'Success'." in str(w.pop().message) + else: + assert len(w) == 2 - # get the list of the hdr files that we want to test - hdr_file_list = list(get_data_filenames("spectra", "*.hdr")) + first_wmsg = str(w[0].message) + assert "unitfix" in first_wmsg and "Hz" in first_wmsg and "M/S" in first_wmsg + assert "plane angle" in str(w[1].message) and "m/s" in str(w[1].message) - # actually perform a test for each one - for filename in hdr_file_list: - # use the base name of the file, because everything we yield - # will show up in the test name in the pandokia report - filename = os.path.basename(filename) +# Ignore "PV2_2 = 0.209028857410973 invalid keyvalue" warning seen on Windows. +@pytest.mark.filterwarnings(r"ignore:PV2_2") +def test_outside_sky(): + """ + From github issue #107 + """ + header = get_pkg_data_contents("data/outside_sky.hdr", encoding="binary") + w = wcs.WCS(header) - # yield a function name and parameters to make a generated test - yield test_spectrum, filename + assert np.all(np.isnan(w.wcs_pix2world([[100.0, 500.0]], 0))) # outside sky + assert np.all(np.isnan(w.wcs_pix2world([[200.0, 200.0]], 0))) # outside sky + assert not np.any(np.isnan(w.wcs_pix2world([[1000.0, 1000.0]], 0))) - # AFTER we tested with every file that we found, check to see that we - # actually have the list we expect. If N=0, we will not have performed - # any tests at all. If N < n_data_files, we are missing some files, - # so we will have skipped some tests. Without this check, both cases - # happen silently! - # how many do we expect to see? - n_data_files = 6 +def test_pix2world(): + """ + From github issue #1463 + """ + # TODO: write this to test the expected output behavior of pix2world, + # currently this just makes sure it doesn't error out in unexpected ways + # (and compares `wcs.pc` and `result` values?) + filename = get_pkg_data_filename("data/sip2.fits") + with pytest.warns(wcs.FITSFixedWarning) as caught_warnings: + # this raises a warning unimportant for this testing the pix2world + # FITSFixedWarning(u'The WCS transformation has more axes (2) than + # the image it is associated with (0)') + ww = wcs.WCS(filename) - if len(hdr_file_list) != n_data_files: - assert False, ( - "test_spectra has wrong number data files: found %d, expected " - " %d" % (len(hdr_file_list), n_data_files)) - # b.t.w. If this assert happens, py.test reports one more test - # than it would have otherwise. + # might as well monitor for changing behavior + if Version("7.4") <= Version(WCSLIB_VERSION) < Version("7.6"): + assert len(caught_warnings) == 2 + else: + assert len(caught_warnings) == 1 + n = 3 + pixels = (np.arange(n) * np.ones((2, n))).T + result = ww.wcs_pix2world(pixels, 0, ra_dec_order=True) -def test_units(): - u = wcs.UnitConverter("log(MHz)", "ln(Hz)") - print(u.convert([1, 2, 3, 4])) + # Catch #2791 + ww.wcs_pix2world(pixels[..., 0], pixels[..., 1], 0, ra_dec_order=True) -basic_units = "m s g rad sr K A mol cd".split() -derived_units = "Hz J W V N Pa C Ohm ohm S F Wb T H lm lx".split() -add_all_units = "eV Jy R G barn".split() -add_sup_units = "a yr pc bit byte Byte".split() -add_sub_units = "mag".split() -general_units = \ - "deg arcmin arcsec mas d h min erg Ry u D DEGREE DEGREES".split() -astro_units = "Angstrom angstrom AU lyr beam solRad solMass solLum Sun".split() -device_units = "adu bin chan count ct photon ph pixel pix voxel".split() + # assuming that the data of sip2.fits doesn't change + answer = np.array([[0.00024976, 0.00023018], [0.00023043, -0.00024997]]) -sub_prefixes = "y z a f p n u m c d".split() -sup_prefixes = "da h k M G T P E Z Y".split() + assert np.allclose(ww.wcs.pc, answer, atol=1.0e-8) + answer = np.array( + [ + [202.39265216, 47.17756518], + [202.39335826, 47.17754619], + [202.39406436, 47.1775272], + ] + ) -def test_all_units(): - def test_self(x): - # x appears in the test name. If we would have had an ambiguous - # test name, we had -xxx added to the unit name. Remove it if - # necessary. - if '-' in x: - x = x.split('-')[0] - - # here is the test: - try: - u = wcs.UnitConverter(x, x) - except ValueError: - e = sys.exc_info()[1] - if str(e).startswith("ERROR 12 in wcsutrne") and \ - x in ("S", "H", "D"): - return - else: - raise - assert u.scale == 1.0 - assert u.offset == 0.0 - assert u.power == 1.0 - - # list of all the units to test - all = sorted(basic_units + derived_units + add_all_units + add_sup_units - + add_sub_units + general_units + astro_units + device_units) - - # Pandokia has non-case-sensitve test names; since the unit name is - # showing up in the test name, we want to disambiguate any name collisions. - # Here is a list of all the lower-cased unit name names. - all_lower = [x.lower() for x in all] - - # here are serial numbers to use to disambiguate - unique_tags = {} - - for unit in all: - # disambiguate the test name, if necessary - l_unit = unit.lower() - if unit != l_unit and l_unit in all_lower: - n = unique_tags.get(l_unit, 1) - unique_tags[n] = n + 1 - - # the test will tear off the part after the '-' - unit = '%s-%d' % (unit, n) - - # perform the test - yield test_self, unit - - -def test_unit_prefixes(): - def test_self(x, p): - unit = p + x - try: - u = wcs.UnitConverter(unit, unit) - except ValueError: - e = sys.exc_info()[1] - if str(e) == "Potentially unsafe translation" and \ - x in ("S", "H", "D"): - return - else: - raise - assert u.scale == 1.0 - assert u.offset == 0.0 - assert u.power == 1.0 + assert np.allclose(result, answer, atol=1.0e-8, rtol=1.0e-10) + + +def test_load_fits_path(): + fits_name = get_pkg_data_filename("data/sip.fits") + with pytest.warns(wcs.FITSFixedWarning): + wcs.WCS(fits_name) - for unit in (basic_units + derived_units + add_all_units): - for prefix in (sub_prefixes + sup_prefixes): - yield test_self, unit, prefix - for unit in add_sup_units: - for prefix in sup_prefixes: - yield test_self, unit, prefix +def test_dict_init(): + """ + Test that WCS can be initialized with a dict-like object + """ - for unit in add_sub_units: - for prefix in sub_prefixes: - yield test_self, unit, prefix + # Dictionary with no actual WCS, returns identity transform + with ctx_for_v71_dateref_warnings(): + w = wcs.WCS({}) + xp, yp = w.wcs_world2pix(41.0, 2.0, 1) -def test_fixes(): + assert_array_almost_equal_nulp(xp, 41.0, 10) + assert_array_almost_equal_nulp(yp, 2.0, 10) + + # Valid WCS + hdr = { + "CTYPE1": "GLON-CAR", + "CTYPE2": "GLAT-CAR", + "CUNIT1": "deg", + "CUNIT2": "deg", + "CRPIX1": 1, + "CRPIX2": 1, + "CRVAL1": 40.0, + "CRVAL2": 0.0, + "CDELT1": -0.1, + "CDELT2": 0.1, + } + if Version(WCSLIB_VERSION) >= Version("7.1"): + hdr["DATEREF"] = "1858-11-17" + + if Version(WCSLIB_VERSION) >= Version("7.4"): + ctx = pytest.warns( + wcs.wcs.FITSFixedWarning, + match=r"'datfix' made the change 'Set MJDREF to 0\.000000 from DATEREF'\.", + ) + else: + ctx = nullcontext() + + with ctx: + w = wcs.WCS(hdr) + + xp, yp = w.wcs_world2pix(41.0, 2.0, 0) + + assert_array_almost_equal_nulp(xp, -10.0, 10) + assert_array_almost_equal_nulp(yp, 20.0, 10) + + +def test_extra_kwarg(): """ - From github issue #36 + Issue #444 + """ + w = wcs.WCS() + with NumpyRNGContext(123456789): + data = np.random.rand(100, 2) + with pytest.raises(TypeError): + w.wcs_pix2world(data, origin=1) + + +def test_3d_shapes(): """ - def run(): - header = get_data_contents('data/nonstandard_units.hdr') + Issue #444 + """ + w = wcs.WCS(naxis=3) + with NumpyRNGContext(123456789): + data = np.random.rand(100, 3) + result = w.wcs_pix2world(data, 1) + assert result.shape == (100, 3) + result = w.wcs_pix2world(data[..., 0], data[..., 1], data[..., 2], 1) + assert len(result) == 3 + + +def test_preserve_shape(): + w = wcs.WCS(naxis=2) + + x = np.random.random((2, 3, 4)) + y = np.random.random((2, 3, 4)) + + xw, yw = w.wcs_pix2world(x, y, 1) + + assert xw.shape == (2, 3, 4) + assert yw.shape == (2, 3, 4) + + xp, yp = w.wcs_world2pix(x, y, 1) + + assert xp.shape == (2, 3, 4) + assert yp.shape == (2, 3, 4) + + +def test_broadcasting(): + w = wcs.WCS(naxis=2) + + x = np.random.random((2, 3, 4)) + y = 1 + + xp, yp = w.wcs_world2pix(x, y, 1) + + assert xp.shape == (2, 3, 4) + assert yp.shape == (2, 3, 4) + + +def test_shape_mismatch(): + w = wcs.WCS(naxis=2) + + x = np.random.random((2, 3, 4)) + y = np.random.random((3, 2, 4)) + + MESSAGE = r"Coordinate arrays are not broadcastable to each other" + with pytest.raises(ValueError, match=MESSAGE): + xw, yw = w.wcs_pix2world(x, y, 1) + + with pytest.raises(ValueError, match=MESSAGE): + xp, yp = w.wcs_world2pix(x, y, 1) + + # There are some ambiguities that need to be worked around when + # naxis == 1 + w = wcs.WCS(naxis=1) + + x = np.random.random((42, 1)) + xw = w.wcs_pix2world(x, 1) + assert xw.shape == (42, 1) + + x = np.random.random((42,)) + (xw,) = w.wcs_pix2world(x, 1) + assert xw.shape == (42,) + + +def test_invalid_shape(): + """Issue #1395""" + MESSAGE = r"When providing two arguments, the array must be of shape [(]N, 2[)]" + + w = wcs.WCS(naxis=2) + + xy = np.random.random((2, 3)) + with pytest.raises(ValueError, match=MESSAGE): + w.wcs_pix2world(xy, 1) + + xy = np.random.random((2, 1)) + with pytest.raises(ValueError, match=MESSAGE): + w.wcs_pix2world(xy, 1) + + +def test_warning_about_defunct_keywords(): + header = get_pkg_data_contents("data/defunct_keywords.hdr", encoding="binary") + if Version("7.4") <= Version(WCSLIB_VERSION) < Version("7.6"): + n_warn = 5 + else: + n_warn = 4 + + # Make sure the warnings come out every time... + for _ in range(2): + with pytest.warns(wcs.FITSFixedWarning) as w: + wcs.WCS(header) + + assert len(w) == n_warn + # 7.4 adds a fifth warning "'datfix' made the change 'Success'." + for item in w[:4]: + assert "PCi_ja" in str(item.message) + + +def test_warning_about_defunct_keywords_exception(): + header = get_pkg_data_contents("data/defunct_keywords.hdr", encoding="binary") + with pytest.warns(wcs.FITSFixedWarning): + wcs.WCS(header) + + +def test_to_header_string(): + # fmt: off + hdrstr = ( + "WCSAXES = 2 / Number of coordinate axes ", + "CRPIX1 = 0.0 / Pixel coordinate of reference point ", + "CRPIX2 = 0.0 / Pixel coordinate of reference point ", + "CDELT1 = 1.0 / Coordinate increment at reference point ", + "CDELT2 = 1.0 / Coordinate increment at reference point ", + "CRVAL1 = 0.0 / Coordinate value at reference point ", + "CRVAL2 = 0.0 / Coordinate value at reference point ", + "LATPOLE = 90.0 / [deg] Native latitude of celestial pole ", + ) + # fmt: on + + if Version(WCSLIB_VERSION) >= Version("7.3"): + # fmt: off + hdrstr += ( + "MJDREF = 0.0 / [d] MJD of fiducial time ", + ) + # fmt: on + + elif Version(WCSLIB_VERSION) >= Version("7.1"): + # fmt: off + hdrstr += ( + "DATEREF = '1858-11-17' / ISO-8601 fiducial time ", + "MJDREFI = 0.0 / [d] MJD of fiducial time, integer part ", + "MJDREFF = 0.0 / [d] MJD of fiducial time, fractional part " + ) + # fmt: on + + hdrstr += ("END",) + + header_string = "".join(hdrstr) + + w = wcs.WCS() + h0 = fits.Header.fromstring(w.to_header_string().strip()) + if "COMMENT" in h0: + del h0["COMMENT"] + if "" in h0: + del h0[""] + h1 = fits.Header.fromstring(header_string.strip()) + assert dict(h0) == dict(h1) + + +def test_to_fits(): + nrec = 11 if Version(WCSLIB_VERSION) >= Version("7.1") else 8 + if Version(WCSLIB_VERSION) < Version("7.1"): + nrec = 8 + elif Version(WCSLIB_VERSION) < Version("7.3"): + nrec = 11 + else: + nrec = 9 + + w = wcs.WCS() + header_string = w.to_header() + wfits = w.to_fits() + assert isinstance(wfits, fits.HDUList) + assert isinstance(wfits[0], fits.PrimaryHDU) + assert header_string == wfits[0].header[-nrec:] + + +def test_to_header_warning(): + fits_name = get_pkg_data_filename("data/sip.fits") + with pytest.warns(wcs.FITSFixedWarning): + x = wcs.WCS(fits_name) + with pytest.warns(AstropyWarning, match="A_ORDER") as w: + x.to_header() + assert len(w) == 1 + + +def test_no_comments_in_header(): + w = wcs.WCS() + header = w.to_header() + assert w.wcs.alt not in header + assert "COMMENT" + w.wcs.alt.strip() not in header + assert "COMMENT" not in header + wkey = "P" + header = w.to_header(key=wkey) + assert wkey not in header + assert "COMMENT" not in header + assert "COMMENT" + w.wcs.alt.strip() not in header + + +def test_find_all_wcs_crash(): + """ + Causes a double free without a recent fix in wcslib_wrap.C + """ + with open(get_pkg_data_filename("data/too_many_pv.hdr")) as fd: + header = fd.read() + # We have to set fix=False here, because one of the fixing tasks is to + # remove redundant SCAMP distortion parameters when SIP distortion + # parameters are also present. + with pytest.raises(wcs.InvalidTransformError), pytest.warns(wcs.FITSFixedWarning): + wcs.find_all_wcs(header, fix=False) + + +# NOTE: Warning bubbles up from C layer during wcs.validate() and +# is hard to catch, so we just ignore it. +@pytest.mark.filterwarnings("ignore") +def test_validate(): + results = wcs.validate(get_pkg_data_filename("data/validate.fits")) + results_txt = sorted({x.strip() for x in repr(results).splitlines()}) + if Version(WCSLIB_VERSION) >= Version("7.6"): + filename = "data/validate.7.6.txt" + elif Version(WCSLIB_VERSION) >= Version("7.4"): + filename = "data/validate.7.4.txt" + elif Version(WCSLIB_VERSION) >= Version("6.0"): + filename = "data/validate.6.txt" + elif Version(WCSLIB_VERSION) >= Version("5.13"): + filename = "data/validate.5.13.txt" + elif Version(WCSLIB_VERSION) >= Version("5.0"): + filename = "data/validate.5.0.txt" + else: + filename = "data/validate.txt" + with open(get_pkg_data_filename(filename)) as fd: + lines = fd.readlines() + assert sorted({x.strip() for x in lines}) == results_txt + + +@pytest.mark.filterwarnings("ignore") +def test_validate_wcs_tab(): + results = wcs.validate(get_pkg_data_filename("data/tab-time-last-axis.fits")) + results_txt = sorted({x.strip() for x in repr(results).splitlines()}) + assert results_txt == [ + "", + "HDU 0 (PRIMARY):", + "HDU 1 (WCS-TABLE):", + "No issues.", + "WCS key ' ':", + ] + + +def test_validate_with_2_wcses(): + # From Issue #2053 + with pytest.warns(AstropyUserWarning): + results = wcs.validate(get_pkg_data_filename("data/2wcses.hdr")) + + assert "WCS key 'A':" in str(results) + + +def test_crpix_maps_to_crval(): + twcs = wcs.WCS(naxis=2) + twcs.wcs.crval = [251.29, 57.58] + twcs.wcs.cdelt = [1, 1] + twcs.wcs.crpix = [507, 507] + twcs.wcs.pc = np.array([[7.7e-6, 3.3e-5], [3.7e-5, -6.8e-6]]) + twcs._naxis = [1014, 1014] + twcs.wcs.ctype = ["RA---TAN-SIP", "DEC--TAN-SIP"] + a = np.array( + [ + [0, 0, 5.33092692e-08, 3.73753773e-11, -2.02111473e-13], + [0, 2.44084308e-05, 2.81394789e-11, 5.17856895e-13, 0.0], + [-2.41334657e-07, 1.29289255e-10, 2.35753629e-14, 0.0, 0.0], + [-2.37162007e-10, 5.43714947e-13, 0.0, 0.0, 0.0], + [-2.81029767e-13, 0.0, 0.0, 0.0, 0.0], + ] + ) + b = np.array( + [ + [0, 0, 2.99270374e-05, -2.38136074e-10, 7.23205168e-13], + [0, -1.71073858e-07, 6.31243431e-11, -5.16744347e-14, 0.0], + [6.95458963e-06, -3.08278961e-10, -1.75800917e-13, 0.0, 0.0], + [3.51974159e-11, 5.60993016e-14, 0.0, 0.0, 0.0], + [-5.92438525e-13, 0.0, 0.0, 0.0, 0.0], + ] + ) + twcs.sip = wcs.Sip(a, b, None, None, twcs.wcs.crpix) + twcs.wcs.set() + pscale = np.sqrt(wcs.utils.proj_plane_pixel_area(twcs)) + + # test that CRPIX maps to CRVAL: + assert_allclose( + twcs.wcs_pix2world(*twcs.wcs.crpix, 1), + twcs.wcs.crval, + rtol=0.0, + atol=1e-6 * pscale, + ) + + # test that CRPIX maps to CRVAL: + assert_allclose( + twcs.all_pix2world(*twcs.wcs.crpix, 1), + twcs.wcs.crval, + rtol=0.0, + atol=1e-6 * pscale, + ) + + +def test_all_world2pix(): + """Test all_world2pix, iterative inverse of all_pix2world""" + + tolerance = 1.0e-4 + origin = 0 + + # Open test FITS file: + fname = get_pkg_data_filename("data/j94f05bgq_flt.fits") + ext = ("SCI", 1) + if not os.path.isfile(fname): + raise OSError(f"Input file '{fname:s}' to 'test_all_world2pix' not found.") + h = fits.open(fname) + w = wcs.WCS(h[ext].header, h) + h.close() + del h + + crpix = w.wcs.crpix + ncoord = crpix.shape[0] + + # Assume that CRPIX is at the center of the image and that the image has + # a power-of-2 number of pixels along each axis. Only use the central + # 1/64 for this testing purpose: + naxesi_l = list((7.0 / 16 * crpix).astype(int)) + naxesi_u = list((9.0 / 16 * crpix).astype(int)) + + # Generate integer indices of pixels (image grid): + img_pix = np.dstack( + [i.flatten() for i in np.meshgrid(*map(range, naxesi_l, naxesi_u))] + )[0] + + # Generate random data (in image coordinates): + with NumpyRNGContext(123456789): + rnd_pix = np.random.rand(25_000, ncoord) + + # Scale random data to cover the central part of the image + mwidth = 2 * (crpix * 1.0 / 8) + rnd_pix = crpix - 0.5 * mwidth + (mwidth - 1) * rnd_pix + + # Reference pixel coordinates in image coordinate system (CS): + test_pix = np.append(img_pix, rnd_pix, axis=0) + # Reference pixel coordinates in sky CS using forward transformation: + all_world = w.all_pix2world(test_pix, origin) + + try: + runtime_begin = datetime.now() + # Apply the inverse iterative process to pixels in world coordinates + # to recover the pixel coordinates in image space. + all_pix = w.all_world2pix( + all_world, + origin, + tolerance=tolerance, + adaptive=False, + maxiter=20, + detect_divergence=True, + ) + runtime_end = datetime.now() + except wcs.wcs.NoConvergence as e: + runtime_end = datetime.now() + ndiv = 0 + if e.divergent is not None: + ndiv = e.divergent.shape[0] + print(f"There are {ndiv} diverging solutions.") + print(f"Indices of diverging solutions:\n{e.divergent}") + print(f"Diverging solutions:\n{e.best_solution[e.divergent]}\n") + print( + "Mean radius of the diverging solutions:" + f" {np.mean(np.linalg.norm(e.best_solution[e.divergent], axis=1))}" + ) + print( + "Mean accuracy of the diverging solutions:" + f" {np.mean(np.linalg.norm(e.accuracy[e.divergent], axis=1))}\n" + ) + else: + print("There are no diverging solutions.") + + nslow = 0 + if e.slow_conv is not None: + nslow = e.slow_conv.shape[0] + print(f"There are {nslow} slowly converging solutions.") + print(f"Indices of slowly converging solutions:\n{e.slow_conv}") + print(f"Slowly converging solutions:\n{e.best_solution[e.slow_conv]}\n") + else: + print("There are no slowly converging solutions.\n") + + print( + f"There are {e.best_solution.shape[0] - ndiv - nslow} converged solutions." + ) + print(f"Best solutions (all points):\n{e.best_solution}") + print(f"Accuracy:\n{e.accuracy}\n") + print( + "\nFinished running 'test_all_world2pix' with errors.\n" + f"ERROR: {e.args[0]}\nRun time: {runtime_end - runtime_begin}\n" + ) + raise e + + # Compute differences between reference pixel coordinates and + # pixel coordinates (in image space) recovered from reference + # pixels in world coordinates: + errors = np.sqrt(np.sum(np.power(all_pix - test_pix, 2), axis=1)) + meanerr = np.mean(errors) + maxerr = np.amax(errors) + print( + "\nFinished running 'test_all_world2pix'.\n" + f"Mean error = {meanerr:e} (Max error = {maxerr:e})\n" + f"Run time: {runtime_end - runtime_begin}\n" + ) + + assert maxerr < 2.0 * tolerance + + +def test_scamp_sip_distortion_parameters(): + """ + Test parsing of WCS parameters with redundant SIP and SCAMP distortion + parameters. + """ + header = get_pkg_data_contents("data/validate.fits", encoding="binary") + with pytest.warns(wcs.FITSFixedWarning): w = wcs.WCS(header) + # Just check that this doesn't raise an exception. + w.all_pix2world(0, 0, 0) - with warnings.catch_warnings(record=True) as w: - warnings.simplefilter("always") - run() - assert len(w) == 3 - for item in w: - assert issubclass(item.category, wcs.FITSFixedWarning) - if 'unitfix' in str(item.message): - assert 'Hz' in str(item.message) +def test_fixes2(): + """ + From github issue #1854 + """ + header = get_pkg_data_contents("data/nonstandard_units.hdr", encoding="binary") + with pytest.raises(wcs.InvalidTransformError): + wcs.WCS(header, fix=False) -def test_outside_sky(): + +def test_unit_normalization(): """ - From github issue #107 + From github issue #1918 + """ + header = get_pkg_data_contents("data/unit.hdr", encoding="binary") + w = wcs.WCS(header) + assert w.wcs.cunit[2] == "m/s" + + +def test_footprint_to_file(tmp_path): + """ + From github issue #1912 + """ + # Arbitrary keywords from real data + hdr = { + "CTYPE1": "RA---ZPN", + "CRUNIT1": "deg", + "CRPIX1": -3.3495999e02, + "CRVAL1": 3.185790700000e02, + "CTYPE2": "DEC--ZPN", + "CRUNIT2": "deg", + "CRPIX2": 3.0453999e03, + "CRVAL2": 4.388538000000e01, + "PV2_1": 1.0, + "PV2_3": 220.0, + "NAXIS1": 2048, + "NAXIS2": 1024, + } + w = wcs.WCS(hdr) + + testfile = tmp_path / "test.txt" + w.footprint_to_file(testfile) + + with open(testfile) as f: + lines = f.readlines() + + assert len(lines) == 4 + assert lines[2] == "ICRS\n" + assert "color=green" in lines[3] + + w.footprint_to_file(testfile, coordsys="FK5", color="red") + + with open(testfile) as f: + lines = f.readlines() + + assert len(lines) == 4 + assert lines[2] == "FK5\n" + assert "color=red" in lines[3] + + with pytest.raises(ValueError): + w.footprint_to_file(testfile, coordsys="FOO") + + del hdr["NAXIS1"] + del hdr["NAXIS2"] + w = wcs.WCS(hdr) + with pytest.warns(AstropyUserWarning): + w.footprint_to_file(testfile) + + +# Ignore FITSFixedWarning about keyrecords following the END keyrecord were +# ignored, which comes from src/astropy_wcs.c . Only a blind catch like this +# seems to work when pytest warnings are turned into exceptions. +@pytest.mark.filterwarnings("ignore") +def test_validate_faulty_wcs(): """ - header = get_data_contents('data/outside_sky.hdr') + From github issue #2053 + """ + h = fits.Header() + # Illegal WCS: + h["RADESYSA"] = "ICRS" + h["PV2_1"] = 1.0 + hdu = fits.PrimaryHDU([[0]], header=h) + hdulist = fits.HDUList([hdu]) + # Check that this doesn't raise a NameError exception + wcs.validate(hdulist) + + +def test_error_message(): + header = get_pkg_data_contents("data/invalid_header.hdr", encoding="binary") + + # make WCS transformation invalid + hdr = fits.Header.fromstring(header) + del hdr["PV?_*"] + hdr["PV1_1"] = 110 + hdr["PV1_2"] = 110 + hdr["PV2_1"] = -110 + hdr["PV2_2"] = -110 + with pytest.raises(wcs.InvalidTransformError): + with pytest.warns(wcs.FITSFixedWarning): + w = wcs.WCS(hdr, _do_set=False) + w.all_pix2world([[536.0, 894.0]], 0) + + +def test_out_of_bounds(): + # See #2107 + header = get_pkg_data_contents("data/zpn-hole.hdr", encoding="binary") w = wcs.WCS(header) - assert np.all(np.isnan(w.wcs_pix2world([[100.,500.]], 0))) # outside sky - assert np.all(np.isnan(w.wcs_pix2world([[200.,200.]], 0))) # outside sky - assert not np.any(np.isnan(w.wcs_pix2world([[1000.,1000.]], 0))) + ra, dec = w.wcs_pix2world(110, 110, 0) + assert np.isnan(ra) + assert np.isnan(dec) -def test_load_fits_path(): - fits = get_data_filename('data/sip.fits') - w = wcs.WCS(fits) + ra, dec = w.wcs_pix2world(0, 0, 0) + + assert not np.isnan(ra) + assert not np.isnan(dec) + + +def test_calc_footprint_1(): + fits = get_pkg_data_filename("data/sip.fits") + with pytest.warns(wcs.FITSFixedWarning): + w = wcs.WCS(fits) + + axes = (1000, 1051) + ref = np.array( + [ + [202.39314493, 47.17753352], + [202.71885939, 46.94630488], + [202.94631893, 47.15855022], + [202.72053428, 47.37893142], + ] + ) + footprint = w.calc_footprint(axes=axes) + assert_allclose(footprint, ref) + + +def test_calc_footprint_2(): + """Test calc_footprint without distortion.""" + fits = get_pkg_data_filename("data/sip.fits") + with pytest.warns(wcs.FITSFixedWarning): + w = wcs.WCS(fits) + + axes = (1000, 1051) + ref = np.array( + [ + [202.39265216, 47.17756518], + [202.7469062, 46.91483312], + [203.11487481, 47.14359319], + [202.76092671, 47.40745948], + ] + ) + footprint = w.calc_footprint(axes=axes, undistort=False) + assert_allclose(footprint, ref) + + +def test_calc_footprint_3(): + """Test calc_footprint with corner of the pixel.""" + w = wcs.WCS() + w.wcs.ctype = ["GLON-CAR", "GLAT-CAR"] + w.wcs.crpix = [1.5, 5.5] + w.wcs.cdelt = [-0.1, 0.1] + axes = (2, 10) + ref = np.array([[0.1, -0.5], [0.1, 0.5], [359.9, 0.5], [359.9, -0.5]]) + + footprint = w.calc_footprint(axes=axes, undistort=False, center=False) + assert_allclose(footprint, ref) + + +def test_sip(): + # See #2107 + header = get_pkg_data_contents("data/irac_sip.hdr", encoding="binary") + w = wcs.WCS(header) + + x0, y0 = w.sip_pix2foc(200, 200, 0) + + assert_allclose(72, x0, 1e-3) + assert_allclose(72, y0, 1e-3) + + x1, y1 = w.sip_foc2pix(x0, y0, 0) + + assert_allclose(200, x1, 1e-3) + assert_allclose(200, y1, 1e-3) + + +def test_sub_3d_with_sip(): + # See #10527 + header = get_pkg_data_contents("data/irac_sip.hdr", encoding="binary") + header = fits.Header.fromstring(header) + header["NAXIS"] = 3 + header.set("NAXIS3", 64, after=header.index("NAXIS2")) + w = wcs.WCS(header, naxis=2) + assert w.naxis == 2 + + +def test_printwcs(capsys): + """ + Just make sure that it runs + """ + h = get_pkg_data_contents("data/spectra/orion-freq-1.hdr", encoding="binary") + with pytest.warns(wcs.FITSFixedWarning): + w = wcs.WCS(h) + w.printwcs() + captured = capsys.readouterr() + assert "WCS Keywords" in captured.out + h = get_pkg_data_contents("data/3d_cd.hdr", encoding="binary") + w = wcs.WCS(h) + w.printwcs() + captured = capsys.readouterr() + assert "WCS Keywords" in captured.out + + +def test_invalid_spherical(): + header = """ +SIMPLE = T / conforms to FITS standard +BITPIX = 8 / array data type +WCSAXES = 2 / no comment +CTYPE1 = 'RA---TAN' / TAN (gnomic) projection +CTYPE2 = 'DEC--TAN' / TAN (gnomic) projection +EQUINOX = 2000.0 / Equatorial coordinates definition (yr) +LONPOLE = 180.0 / no comment +LATPOLE = 0.0 / no comment +CRVAL1 = 16.0531567459 / RA of reference point +CRVAL2 = 23.1148929108 / DEC of reference point +CRPIX1 = 2129 / X reference pixel +CRPIX2 = 1417 / Y reference pixel +CUNIT1 = 'deg ' / X pixel scale units +CUNIT2 = 'deg ' / Y pixel scale units +CD1_1 = -0.00912247310646 / Transformation matrix +CD1_2 = -0.00250608809647 / no comment +CD2_1 = 0.00250608809647 / no comment +CD2_2 = -0.00912247310646 / no comment +IMAGEW = 4256 / Image width, in pixels. +IMAGEH = 2832 / Image height, in pixels. + """ + + f = io.StringIO(header) + header = fits.Header.fromtextfile(f) + + w = wcs.WCS(header) + x, y = w.wcs_world2pix(211, -26, 0) + assert np.isnan(x) and np.isnan(y) + + +def test_no_iteration(): + """Regression test for #3066""" + MESSAGE = "'{}' object is not iterable" + + w = wcs.WCS(naxis=2) + + with pytest.raises(TypeError, match=MESSAGE.format("WCS")): + iter(w) + + class NewWCS(wcs.WCS): + pass + + w = NewWCS(naxis=2) + + with pytest.raises(TypeError, match=MESSAGE.format("NewWCS")): + iter(w) + + +@pytest.mark.skipif( + WCSLIB_VERSION[0] < "5", reason="TPV only works with wcslib 5.x or later" +) +def test_sip_tpv_agreement(): + sip_header = get_pkg_data_contents( + os.path.join("data", "siponly.hdr"), encoding="binary" + ) + tpv_header = get_pkg_data_contents( + os.path.join("data", "tpvonly.hdr"), encoding="binary" + ) + + with ( + pytest.warns(wcs.FITSFixedWarning), + pytest.warns( + AstropyWarning, match="Some non-standard WCS keywords were excluded" + ), + ): + w_sip = wcs.WCS(sip_header) + w_tpv = wcs.WCS(tpv_header) + + assert_array_almost_equal( + w_sip.all_pix2world([w_sip.wcs.crpix], 1), + w_tpv.all_pix2world([w_tpv.wcs.crpix], 1), + ) + + w_sip2 = wcs.WCS(w_sip.to_header()) + w_tpv2 = wcs.WCS(w_tpv.to_header()) + + assert_array_almost_equal( + w_sip.all_pix2world([w_sip.wcs.crpix], 1), + w_sip2.all_pix2world([w_sip.wcs.crpix], 1), + ) + assert_array_almost_equal( + w_tpv.all_pix2world([w_sip.wcs.crpix], 1), + w_tpv2.all_pix2world([w_sip.wcs.crpix], 1), + ) + assert_array_almost_equal( + w_sip2.all_pix2world([w_sip.wcs.crpix], 1), + w_tpv2.all_pix2world([w_tpv.wcs.crpix], 1), + ) + + +def test_tpv_ctype_sip(): + sip_header = fits.Header.fromstring( + get_pkg_data_contents(os.path.join("data", "siponly.hdr"), encoding="binary") + ) + tpv_header = fits.Header.fromstring( + get_pkg_data_contents(os.path.join("data", "tpvonly.hdr"), encoding="binary") + ) + sip_header.update(tpv_header) + sip_header["CTYPE1"] = "RA---TAN-SIP" + sip_header["CTYPE2"] = "DEC--TAN-SIP" + + with ( + pytest.warns( + wcs.FITSFixedWarning, + match="Removed redundant SCAMP distortion parameters " + "because SIP parameters are also present", + ), + pytest.warns( + wcs.FITSFixedWarning, + match=".*RADECSYS keyword is deprecated, use RADESYSa", + ), + pytest.warns(wcs.FITSFixedWarning, match=".*Set MJD-OBS to .* from DATE-OBS"), + ): + w_sip = wcs.WCS(sip_header) + + assert w_sip.sip is not None + + +def test_tpv_ctype_tpv(): + sip_header = fits.Header.fromstring( + get_pkg_data_contents(os.path.join("data", "siponly.hdr"), encoding="binary") + ) + tpv_header = fits.Header.fromstring( + get_pkg_data_contents(os.path.join("data", "tpvonly.hdr"), encoding="binary") + ) + sip_header.update(tpv_header) + sip_header["CTYPE1"] = "RA---TPV" + sip_header["CTYPE2"] = "DEC--TPV" + + with ( + pytest.warns( + wcs.FITSFixedWarning, + match="Removed redundant SIP distortion parameters " + "because CTYPE explicitly specifies TPV distortions", + ), + pytest.warns( + wcs.FITSFixedWarning, + match=".*RADECSYS keyword is deprecated, use RADESYSa", + ), + pytest.warns(wcs.FITSFixedWarning, match=".*Set MJD-OBS to .* from DATE-OBS"), + ): + w_sip = wcs.WCS(sip_header) + + assert w_sip.sip is None + + +def test_tpv_ctype_tan(): + sip_header = fits.Header.fromstring( + get_pkg_data_contents(os.path.join("data", "siponly.hdr"), encoding="binary") + ) + tpv_header = fits.Header.fromstring( + get_pkg_data_contents(os.path.join("data", "tpvonly.hdr"), encoding="binary") + ) + sip_header.update(tpv_header) + sip_header["CTYPE1"] = "RA---TAN" + sip_header["CTYPE2"] = "DEC--TAN" + + with ( + pytest.warns( + wcs.FITSFixedWarning, + match="Removed redundant SIP distortion parameters " + "because SCAMP' PV distortions are also present", + ), + pytest.warns( + wcs.FITSFixedWarning, + match=".*RADECSYS keyword is deprecated, use RADESYSa", + ), + pytest.warns(wcs.FITSFixedWarning, match=".*Set MJD-OBS to .* from DATE-OBS"), + ): + w_sip = wcs.WCS(sip_header) + + assert w_sip.sip is None + + +def test_car_sip_with_pv(): + # https://github.com/astropy/astropy/issues/14255 + header_dict = { + "SIMPLE": True, + "BITPIX": -32, + "NAXIS": 2, + "NAXIS1": 1024, + "NAXIS2": 1024, + "CRPIX1": 512.0, + "CRPIX2": 512.0, + "CDELT1": 0.01, + "CDELT2": 0.01, + "CRVAL1": 120.0, + "CRVAL2": 29.0, + "CTYPE1": "RA---CAR-SIP", + "CTYPE2": "DEC--CAR-SIP", + "PV1_1": 120.0, + "PV1_2": 29.0, + "PV1_0": 1.0, + "A_ORDER": 2, + "A_2_0": 5.0e-4, + "B_ORDER": 2, + "B_2_0": 5.0e-4, + } + + w = wcs.WCS(header_dict) + + assert w.sip is not None + + assert w.wcs.get_pv() == [(1, 1, 120.0), (1, 2, 29.0), (1, 0, 1.0)] + + assert np.allclose( + w.all_pix2world(header_dict["CRPIX1"], header_dict["CRPIX2"], 1), + [header_dict["CRVAL1"], header_dict["CRVAL2"]], + ) + + +@pytest.mark.skipif( + WCSLIB_VERSION[0] < "5", reason="TPV only works with wcslib 5.x or later" +) +def test_tpv_copy(): + # See #3904 + + tpv_header = get_pkg_data_contents( + os.path.join("data", "tpvonly.hdr"), encoding="binary" + ) + + with pytest.warns(wcs.FITSFixedWarning): + w_tpv = wcs.WCS(tpv_header) + + ra, dec = w_tpv.wcs_pix2world([0, 100, 200], [0, -100, 200], 0) + assert ra[0] != ra[1] and ra[1] != ra[2] + assert dec[0] != dec[1] and dec[1] != dec[2] + + +def test_hst_wcs(): + path = get_pkg_data_filename("data/dist_lookup.fits.gz") + + with fits.open(path) as hdulist: + # wcslib will complain about the distortion parameters if they + # weren't correctly deleted from the header + w = wcs.WCS(hdulist[1].header, hdulist) + + # Check pixel scale and area + assert_quantity_allclose( + w.proj_plane_pixel_scales(), [1.38484378e-05, 1.39758488e-05] * u.deg + ) + assert_quantity_allclose( + w.proj_plane_pixel_area(), 1.93085492e-10 * (u.deg * u.deg) + ) + + # Exercise the main transformation functions, mainly just for + # coverage + w.p4_pix2foc([0, 100, 200], [0, -100, 200], 0) + w.det2im([0, 100, 200], [0, -100, 200], 0) + + w.cpdis1 = w.cpdis1 + w.cpdis2 = w.cpdis2 + + w.det2im1 = w.det2im1 + w.det2im2 = w.det2im2 + + w.sip = w.sip + + w.cpdis1.cdelt = w.cpdis1.cdelt + w.cpdis1.crpix = w.cpdis1.crpix + w.cpdis1.crval = w.cpdis1.crval + w.cpdis1.data = w.cpdis1.data + + assert w.sip.a_order == 4 + assert w.sip.b_order == 4 + assert w.sip.ap_order == 0 + assert w.sip.bp_order == 0 + assert_array_equal(w.sip.crpix, [2048.0, 1024.0]) + wcs.WCS(hdulist[1].header, hdulist) + + +def test_cpdis_comments(): + path = get_pkg_data_filename("data/dist_lookup.fits.gz") + + f = fits.open(path) + w = wcs.WCS(f[1].header, f) + hdr = w.to_fits()[0].header + f.close() + + wcscards = list(hdr["CPDIS*"].cards) + list(hdr["DP*"].cards) + wcsdict = {k: (v, c) for k, v, c in wcscards} + + refcards = [ + ("CPDIS1", "LOOKUP", "Prior distortion function type"), + ("DP1.EXTVER", 1.0, "Version number of WCSDVARR extension"), + ("DP1.NAXES", 2.0, "Number of independent variables in CPDIS function"), + ("DP1.AXIS.1", 1.0, "Axis number of the 1st variable in a CPDIS function"), + ("DP1.AXIS.2", 2.0, "Axis number of the 2nd variable in a CPDIS function"), + ("CPDIS2", "LOOKUP", "Prior distortion function type"), + ("DP2.EXTVER", 2.0, "Version number of WCSDVARR extension"), + ("DP2.NAXES", 2.0, "Number of independent variables in CPDIS function"), + ("DP2.AXIS.1", 1.0, "Axis number of the 1st variable in a CPDIS function"), + ("DP2.AXIS.2", 2.0, "Axis number of the 2nd variable in a CPDIS function"), + ] + + assert len(wcsdict) == len(refcards) + + for k, v, c in refcards: + assert wcsdict[k] == (v, c) + + +def test_d2im_comments(): + path = get_pkg_data_filename("data/ie6d07ujq_wcs.fits") + + f = fits.open(path) + with pytest.warns(wcs.FITSFixedWarning): + w = wcs.WCS(f[0].header, f) + f.close() + wcscards = list(w.to_fits()[0].header["D2IM*"].cards) + wcsdict = {k: (v, c) for k, v, c in wcscards} + + refcards = [ + ("D2IMDIS1", "LOOKUP", "Detector to image correction type"), + ("D2IM1.EXTVER", 1.0, "Version number of WCSDVARR extension"), + ("D2IM1.NAXES", 2.0, "Number of independent variables in D2IM function"), + ("D2IM1.AXIS.1", 1.0, "Axis number of the 1st variable in a D2IM function"), + ("D2IM1.AXIS.2", 2.0, "Axis number of the 2nd variable in a D2IM function"), + ("D2IMDIS2", "LOOKUP", "Detector to image correction type"), + ("D2IM2.EXTVER", 2.0, "Version number of WCSDVARR extension"), + ("D2IM2.NAXES", 2.0, "Number of independent variables in D2IM function"), + ("D2IM2.AXIS.1", 1.0, "Axis number of the 1st variable in a D2IM function"), + ("D2IM2.AXIS.2", 2.0, "Axis number of the 2nd variable in a D2IM function"), + # ('D2IMERR1', 0.049, 'Maximum error of D2IM correction for axis 1'), + # ('D2IMERR2', 0.035, 'Maximum error of D2IM correction for axis 2'), + # ('D2IMEXT', 'iref$y7b1516hi_d2i.fits', ''), + ] + + assert len(wcsdict) == len(refcards) + + for k, v, c in refcards: + assert wcsdict[k] == (v, c) + + +def test_sip_broken(): + # This header caused wcslib to segfault because it has a SIP + # specification in a non-default keyword + hdr = get_pkg_data_contents("data/sip-broken.hdr") + + wcs.WCS(hdr) + + +def test_no_truncate_crval(): + """ + Regression test for https://github.com/astropy/astropy/issues/4612 + """ + w = wcs.WCS(naxis=3) + w.wcs.crval = [50, 50, 2.12345678e11] + w.wcs.cdelt = [1e-3, 1e-3, 1e8] + w.wcs.ctype = ["RA---TAN", "DEC--TAN", "FREQ"] + w.wcs.set() + + header = w.to_header() + for ii in range(3): + assert header[f"CRVAL{ii + 1}"] == w.wcs.crval[ii] + assert header[f"CDELT{ii + 1}"] == w.wcs.cdelt[ii] + + +def test_no_truncate_crval_try2(): + """ + Regression test for https://github.com/astropy/astropy/issues/4612 + """ + w = wcs.WCS(naxis=3) + w.wcs.crval = [50, 50, 2.12345678e11] + w.wcs.cdelt = [1e-5, 1e-5, 1e5] + w.wcs.ctype = ["RA---SIN", "DEC--SIN", "FREQ"] + w.wcs.cunit = ["deg", "deg", "Hz"] + w.wcs.crpix = [1, 1, 1] + w.wcs.restfrq = 2.34e11 + w.wcs.set() + + header = w.to_header() + for ii in range(3): + assert header[f"CRVAL{ii + 1}"] == w.wcs.crval[ii] + assert header[f"CDELT{ii + 1}"] == w.wcs.cdelt[ii] + + +def test_no_truncate_crval_p17(): + """ + Regression test for https://github.com/astropy/astropy/issues/5162 + """ + w = wcs.WCS(naxis=2) + w.wcs.crval = [50.1234567890123456, 50.1234567890123456] + w.wcs.cdelt = [1e-3, 1e-3] + w.wcs.ctype = ["RA---TAN", "DEC--TAN"] + w.wcs.set() + + header = w.to_header() + assert header["CRVAL1"] != w.wcs.crval[0] + assert header["CRVAL2"] != w.wcs.crval[1] + header = w.to_header(relax=wcs.WCSHDO_P17) + assert header["CRVAL1"] == w.wcs.crval[0] + assert header["CRVAL2"] == w.wcs.crval[1] + + +def test_no_truncate_using_compare(): + """ + Regression test for https://github.com/astropy/astropy/issues/4612 + + This one uses WCS.wcs.compare and some slightly different values + """ + w = wcs.WCS(naxis=3) + w.wcs.crval = [2.409303333333e02, 50, 2.12345678e11] + w.wcs.cdelt = [1e-3, 1e-3, 1e8] + w.wcs.ctype = ["RA---TAN", "DEC--TAN", "FREQ"] + w.wcs.set() + w2 = wcs.WCS(w.to_header()) + w.wcs.compare(w2.wcs) + + +def test_passing_ImageHDU(): + """ + Passing ImageHDU or PrimaryHDU and comparing it with + wcs initialized from header. For #4493. + """ + path = get_pkg_data_filename("data/validate.fits") + with fits.open(path) as hdulist: + with pytest.warns(wcs.FITSFixedWarning): + wcs_hdu = wcs.WCS(hdulist[0]) + wcs_header = wcs.WCS(hdulist[0].header) + assert wcs_hdu.wcs.compare(wcs_header.wcs) + wcs_hdu = wcs.WCS(hdulist[1]) + wcs_header = wcs.WCS(hdulist[1].header) + assert wcs_hdu.wcs.compare(wcs_header.wcs) + + +def test_inconsistent_sip(): + """ + Test for #4814 + """ + hdr = get_pkg_data_contents("data/sip-broken.hdr") + ctx = ctx_for_v71_dateref_warnings() + with ctx: + w = wcs.WCS(hdr) + with pytest.warns(AstropyWarning): + newhdr = w.to_header(relax=None) + # CTYPE should not include "-SIP" if relax is None + with ctx: + wnew = wcs.WCS(newhdr) + assert all(not ctyp.endswith("-SIP") for ctyp in wnew.wcs.ctype) + newhdr = w.to_header(relax=False) + assert "A_0_2" not in newhdr + # CTYPE should not include "-SIP" if relax is False + with ctx: + wnew = wcs.WCS(newhdr) + assert all(not ctyp.endswith("-SIP") for ctyp in wnew.wcs.ctype) + with pytest.warns(AstropyWarning): + newhdr = w.to_header(key="C") + assert "A_0_2" not in newhdr + # Test writing header with a different key + with ctx: + wnew = wcs.WCS(newhdr, key="C") + assert all(not ctyp.endswith("-SIP") for ctyp in wnew.wcs.ctype) + with pytest.warns(AstropyWarning): + newhdr = w.to_header(key=" ") + # Test writing a primary WCS to header + with ctx: + wnew = wcs.WCS(newhdr) + assert all(not ctyp.endswith("-SIP") for ctyp in wnew.wcs.ctype) + # Test that "-SIP" is kept into CTYPE if relax=True and + # "-SIP" was in the original header + newhdr = w.to_header(relax=True) + with ctx: + wnew = wcs.WCS(newhdr) + assert all(ctyp.endswith("-SIP") for ctyp in wnew.wcs.ctype) + assert "A_0_2" in newhdr + # Test that SIP coefficients are also written out. + assert wnew.sip is not None + # ######### broken header ########### + # Test that "-SIP" is added to CTYPE if relax=True and + # "-SIP" was not in the original header but SIP coefficients + # are present. + with ctx: + w = wcs.WCS(hdr) + w.wcs.ctype = ["RA---TAN", "DEC--TAN"] + newhdr = w.to_header(relax=True) + with ctx: + wnew = wcs.WCS(newhdr) + assert all(ctyp.endswith("-SIP") for ctyp in wnew.wcs.ctype) + + +def test_bounds_check(): + """Test for #4957""" + w = wcs.WCS(naxis=2) + w.wcs.ctype = ["RA---CAR", "DEC--CAR"] + w.wcs.cdelt = [10, 10] + w.wcs.crval = [-90, 90] + w.wcs.crpix = [1, 1] + w.wcs.bounds_check(False, False) + ra, dec = w.wcs_pix2world(300, 0, 0) + assert_allclose(ra, -180) + assert_allclose(dec, -30) + + +def test_naxis(): + w = wcs.WCS(naxis=2) + w.wcs.crval = [1, 1] + w.wcs.cdelt = [0.1, 0.1] + w.wcs.crpix = [1, 1] + w._naxis = [1000, 500] + assert w.pixel_shape == (1000, 500) + assert w.array_shape == (500, 1000) + + w.pixel_shape = (99, 59) + assert w._naxis == [99, 59] + + w.array_shape = (45, 23) + assert w._naxis == [23, 45] + assert w.pixel_shape == (23, 45) + + w.pixel_shape = None + assert w.pixel_bounds is None + + +def test_naxis_1d(): + w = wcs.WCS( + { + "naxis1": 100, + "crval1": 1, + "cdelt1": 0.1, + "crpix1": 1, + } + ) + assert w.pixel_shape == (100,) + assert w.array_shape == (100,) + + w.pixel_shape = (99,) + assert w._naxis == [99] + + w.pixel_shape = None + assert w.pixel_bounds is None + + +def test_sip_with_altkey(): + """ + Test that when creating a WCS object using a key, CTYPE with + that key is looked at and not the primary CTYPE. + fix for #5443. + """ + with fits.open(get_pkg_data_filename("data/sip.fits")) as f: + with pytest.warns(wcs.FITSFixedWarning): + w = wcs.WCS(f[0].header) + # create a header with two WCSs. + h1 = w.to_header(relax=True, key="A") + h2 = w.to_header(relax=False) + h1["CTYPE1A"] = "RA---SIN-SIP" + h1["CTYPE2A"] = "DEC--SIN-SIP" + h1.update(h2) + with ctx_for_v71_dateref_warnings(): + w = wcs.WCS(h1, key="A") + assert (w.wcs.ctype == np.array(["RA---SIN-SIP", "DEC--SIN-SIP"])).all() + + +def test_to_fits_1(): + """ + Test to_fits() with LookupTable distortion. + """ + fits_name = get_pkg_data_filename("data/dist.fits") + + with ( + pytest.warns(AstropyDeprecationWarning), + pytest.warns( + wcs.FITSFixedWarning, + match="The WCS transformation has more axes", + ), + ): + w = wcs.WCS(fits_name) + wfits = w.to_fits() + assert isinstance(wfits, fits.HDUList) + assert isinstance(wfits[0], fits.PrimaryHDU) + assert isinstance(wfits[1], fits.ImageHDU) + + +def test_keyedsip(): + """ + Test sip reading with extra key. + """ + hdr_name = get_pkg_data_filename("data/sip-broken.hdr") + header = fits.Header.fromfile(hdr_name) + del header["CRPIX1"] + del header["CRPIX2"] + + w = wcs.WCS(header=header, key="A") + assert isinstance(w.sip, wcs.Sip) + assert w.sip.crpix[0] == 2048 + assert w.sip.crpix[1] == 1026 + + +def test_zero_size_input(): + with fits.open(get_pkg_data_filename("data/sip.fits")) as f: + with pytest.warns(wcs.FITSFixedWarning): + w = wcs.WCS(f[0].header) + + inp = np.zeros((0, 2)) + assert_array_equal(inp, w.all_pix2world(inp, 0)) + assert_array_equal(inp, w.all_world2pix(inp, 0)) + + inp = [], [1] + result = w.all_pix2world([], [1], 0) + assert_array_equal(inp[0], result[0]) + assert_array_equal(inp[1], result[1]) + + result = w.all_world2pix([], [1], 0) + assert_array_equal(inp[0], result[0]) + assert_array_equal(inp[1], result[1]) + + +def test_scalar_inputs(): + """ + Issue #7845 + """ + wcsobj = wcs.WCS(naxis=1) + result = wcsobj.all_pix2world(2, 1) + assert_array_equal(result, [np.array(2.0)]) + assert result[0].shape == () + + result = wcsobj.all_pix2world([2], 1) + assert_array_equal(result, [np.array([2.0])]) + assert result[0].shape == (1,) + + +# Ignore RuntimeWarning raised on s390. +@pytest.mark.filterwarnings("ignore:.*invalid value encountered in.*") +def test_footprint_contains(): + """ + Test WCS.footprint_contains(skycoord) + """ + + header = """ +WCSAXES = 2 / Number of coordinate axes +CRPIX1 = 1045.0 / Pixel coordinate of reference point +CRPIX2 = 1001.0 / Pixel coordinate of reference point +PC1_1 = -0.00556448550786 / Coordinate transformation matrix element +PC1_2 = -0.001042120133257 / Coordinate transformation matrix element +PC2_1 = 0.001181477028705 / Coordinate transformation matrix element +PC2_2 = -0.005590809742987 / Coordinate transformation matrix element +CDELT1 = 1.0 / [deg] Coordinate increment at reference point +CDELT2 = 1.0 / [deg] Coordinate increment at reference point +CUNIT1 = 'deg' / Units of coordinate increment and value +CUNIT2 = 'deg' / Units of coordinate increment and value +CTYPE1 = 'RA---TAN' / TAN (gnomonic) projection + SIP distortions +CTYPE2 = 'DEC--TAN' / TAN (gnomonic) projection + SIP distortions +CRVAL1 = 250.34971683647 / [deg] Coordinate value at reference point +CRVAL2 = 2.2808772582495 / [deg] Coordinate value at reference point +LONPOLE = 180.0 / [deg] Native longitude of celestial pole +LATPOLE = 2.2808772582495 / [deg] Native latitude of celestial pole +RADESYS = 'ICRS' / Equatorial coordinate system +MJD-OBS = 58612.339199259 / [d] MJD of observation matching DATE-OBS +DATE-OBS= '2019-05-09T08:08:26.816Z' / ISO-8601 observation date matching MJD-OB +NAXIS = 2 / NAXIS +NAXIS1 = 2136 / length of first array dimension +NAXIS2 = 2078 / length of second array dimension + """ + + header = fits.Header.fromstring(header.strip(), "\n") + test_wcs = wcs.WCS(header) + + hasCoord = test_wcs.footprint_contains(SkyCoord(254, 2, unit="deg")) + assert hasCoord + + hasCoord = test_wcs.footprint_contains(SkyCoord(240, 2, unit="deg")) + assert not hasCoord + + hasCoord = test_wcs.footprint_contains(SkyCoord(24, 2, unit="deg")) + assert not hasCoord + + +def test_cunit(): + # Initializing WCS + w1 = wcs.WCS(naxis=2) + w2 = wcs.WCS(naxis=2) + w3 = wcs.WCS(naxis=2) + w4 = wcs.WCS(naxis=2) + # Initializing the values of cunit + w1.wcs.cunit = ["deg", "m/s"] + w2.wcs.cunit = ["km/h", "km/h"] + w3.wcs.cunit = ["deg", "m/s"] + w4.wcs.cunit = ["deg", "deg"] + + # Equality checking a cunit with itself + assert w1.wcs.cunit == w1.wcs.cunit + assert not w1.wcs.cunit != w1.wcs.cunit + # Equality checking of two different cunit object having same values + assert w1.wcs.cunit == w3.wcs.cunit + assert not w1.wcs.cunit != w3.wcs.cunit + # Equality checking of two different cunit object having the same first unit + # but different second unit (see #9154) + assert not w1.wcs.cunit == w4.wcs.cunit + assert w1.wcs.cunit != w4.wcs.cunit + # Inequality checking of two different cunit object having different values + assert not w1.wcs.cunit == w2.wcs.cunit + assert w1.wcs.cunit != w2.wcs.cunit + # Inequality checking of cunit with a list of literals + assert not w1.wcs.cunit == [1, 2, 3] + assert w1.wcs.cunit != [1, 2, 3] + # Inequality checking with some characters + assert not w1.wcs.cunit == ["a", "b", "c"] + assert w1.wcs.cunit != ["a", "b", "c"] + # Comparison is not implemented TypeError will raise + with pytest.raises(TypeError): + w1.wcs.cunit < w2.wcs.cunit # noqa: B015 + + +class TestWcsWithTime: + def setup_method(self): + if Version(WCSLIB_VERSION) >= Version("7.1"): + fname = get_pkg_data_filename("data/header_with_time_wcslib71.fits") + else: + fname = get_pkg_data_filename("data/header_with_time.fits") + self.header = fits.Header.fromfile(fname) + with pytest.warns(wcs.FITSFixedWarning): + self.w = wcs.WCS(self.header, key="A") + + def test_keywods2wcsprm(self): + """Make sure Wcsprm is populated correctly from the header.""" + + ctype = [self.header[val] for val in self.header["CTYPE*"]] + crval = [self.header[val] for val in self.header["CRVAL*"]] + crpix = [self.header[val] for val in self.header["CRPIX*"]] + cdelt = [self.header[val] for val in self.header["CDELT*"]] + cunit = [self.header[val] for val in self.header["CUNIT*"]] + assert list(self.w.wcs.ctype) == ctype + time_axis_code = 4000 if Version(WCSLIB_VERSION) >= Version("7.9") else 0 + assert list(self.w.wcs.axis_types) == [2200, 2201, 3300, time_axis_code] + assert_allclose(self.w.wcs.crval, crval) + assert_allclose(self.w.wcs.crpix, crpix) + assert_allclose(self.w.wcs.cdelt, cdelt) + assert list(self.w.wcs.cunit) == cunit + + naxis = self.w.naxis + assert naxis == 4 + pc = np.zeros((naxis, naxis), dtype=np.float64) + for i in range(1, 5): + for j in range(1, 5): + if i == j: + pc[i - 1, j - 1] = self.header.get(f"PC{i}_{j}A", 1) + else: + pc[i - 1, j - 1] = self.header.get(f"PC{i}_{j}A", 0) + assert_allclose(self.w.wcs.pc, pc) + + char_keys = [ + "timesys", + "trefpos", + "trefdir", + "plephem", + "timeunit", + "dateref", + "dateobs", + "datebeg", + "dateavg", + "dateend", + ] + for key in char_keys: + assert getattr(self.w.wcs, key) == self.header.get(key, "") + + num_keys = [ + "mjdref", + "mjdobs", + "mjdbeg", + "mjdend", + "jepoch", + "bepoch", + "tstart", + "tstop", + "xposure", + "timsyer", + "timrder", + "timedel", + "timepixr", + "timeoffs", + "telapse", + "czphs", + "cperi", + ] + + for key in num_keys: + if key.upper() == "MJDREF": + hdrv = [ + self.header.get("MJDREFIA", np.nan), + self.header.get("MJDREFFA", np.nan), + ] + else: + hdrv = self.header.get(key, np.nan) + assert_allclose(getattr(self.w.wcs, key), hdrv) + + def test_transforms(self): + assert_allclose(self.w.all_pix2world(*self.w.wcs.crpix, 1), self.w.wcs.crval) + + +def test_invalid_coordinate_masking(): + # Regression test for an issue which caused all coordinates to be set to NaN + # after a transformation rather than just the invalid ones as reported by + # WCSLIB. A specific example of this is that when considering an all-sky + # spectral cube with a spectral axis that is not correlated with the sky + # axes, if transforming pixel coordinates that did not fall 'in' the sky, + # the spectral world value was also masked even though that coordinate + # was valid. + + w = wcs.WCS(naxis=3) + w.wcs.ctype = "VELO_LSR", "GLON-CAR", "GLAT-CAR" + w.wcs.crval = -20, 0, 0 + w.wcs.crpix = 1, 1441, 241 + w.wcs.cdelt = 1.3, -0.125, 0.125 + + px = [-10, -10, 20] + py = [-10, 10, 20] + pz = [-10, 10, 20] + + wx, wy, wz = w.wcs_pix2world(px, py, pz, 0) + + # Before fixing this, wx used to return np.nan for the first element + + assert_allclose(wx, [-33, -33, 6]) + assert_allclose(wy, [np.nan, 178.75, 177.5]) + assert_allclose(wz, [np.nan, -28.75, -27.5]) + + +def test_no_pixel_area(): + w = wcs.WCS(naxis=3) + + # Pixel area cannot be computed + with pytest.raises(ValueError, match="Pixel area is defined only for 2D pixels"): + w.proj_plane_pixel_area() + + # Pixel scales still possible + assert_quantity_allclose(w.proj_plane_pixel_scales(), 1) + + +def test_distortion_header(tmp_path): + """ + Test that plate distortion model is correctly described by `wcs.to_header()` + and preserved when creating a Cutout2D from the image, writing it to FITS, + and reading it back from the file. + """ + path = get_pkg_data_filename("data/dss.14.29.56-62.41.05.fits.gz") + cen = np.array((50, 50)) + size = np.array((20, 20)) + + with fits.open(path) as hdulist: + with pytest.warns(VerifyWarning), pytest.warns(wcs.FITSFixedWarning): + w = wcs.WCS(hdulist[0].header) + cut = Cutout2D(hdulist[0].data, position=cen, size=size, wcs=w) + + # This converts the DSS plate solution model with AMD[XY]n coefficients into a + # Template Polynomial Distortion model (TPD.FWD.n coefficients); + # not testing explicitly for the header keywords here. + + if Version(WCSLIB_VERSION) < Version("7.4"): + with pytest.warns( + AstropyWarning, match="WCS contains a TPD distortion model in CQDIS" + ): + w0 = wcs.WCS(w.to_header_string()) + with pytest.warns( + AstropyWarning, match="WCS contains a TPD distortion model in CQDIS" + ): + w1 = wcs.WCS(cut.wcs.to_header_string()) + if Version(WCSLIB_VERSION) >= Version("7.1"): + pytest.xfail("TPD coefficients incomplete with WCSLIB >= 7.1 < 7.4") + else: + w0 = wcs.WCS(w.to_header_string()) + w1 = wcs.WCS(cut.wcs.to_header_string()) + + assert w.pixel_to_world(0, 0).separation(w0.pixel_to_world(0, 0)) < 1.0e-3 * u.mas + assert w.pixel_to_world(*cen).separation(w0.pixel_to_world(*cen)) < 1.0e-3 * u.mas + + assert ( + w.pixel_to_world(*cen).separation(w1.pixel_to_world(*(size / 2))) + < 1.0e-3 * u.mas + ) + + cutfile = tmp_path / "cutout.fits" + fits.writeto(cutfile, cut.data, cut.wcs.to_header()) + + with fits.open(cutfile) as hdulist: + w2 = wcs.WCS(hdulist[0].header) + + assert ( + w.pixel_to_world(*cen).separation(w2.pixel_to_world(*(size / 2))) + < 1.0e-3 * u.mas + ) + + +def test_pixlist_wcs_colsel(): + """ + Test selection of a specific pixel list WCS using ``colsel``. See #11412. + """ + hdr_file = get_pkg_data_filename("data/chandra-pixlist-wcs.hdr") + hdr = fits.Header.fromtextfile(hdr_file) + with pytest.warns(wcs.FITSFixedWarning): + w = wcs.WCS(hdr, keysel=["image", "pixel"], colsel=[11, 12]) + assert w.naxis == 2 + assert list(w.wcs.ctype) == ["RA---TAN", "DEC--TAN"] + assert np.allclose(w.wcs.crval, [229.38051931869, -58.81108068885]) + assert np.allclose(w.wcs.pc, [[1, 0], [0, 1]]) + assert np.allclose(w.wcs.cdelt, [-0.00013666666666666, 0.00013666666666666]) + assert np.allclose(w.wcs.lonpole, 180.0) + + +@pytest.mark.skipif( + Version(WCSLIB_VERSION) < Version("7.8"), + reason="TIME axis extraction only works with wcslib 7.8 or later", +) +def test_time_axis_selection(): + w = wcs.WCS(naxis=3) + w.wcs.ctype = ["RA---TAN", "DEC--TAN", "TIME"] + w.wcs.set() + assert list(w.sub([wcs.WCSSUB_TIME]).wcs.ctype) == ["TIME"] + assert ( + w.wcs_pix2world([[1, 2, 3]], 0)[0, 2] + == w.sub([wcs.WCSSUB_TIME]).wcs_pix2world([[3]], 0)[0, 0] + ) + + +@pytest.mark.skipif( + Version(WCSLIB_VERSION) < Version("7.8"), + reason="TIME axis extraction only works with wcslib 7.8 or later", +) +def test_temporal(): + w = wcs.WCS(naxis=3) + w.wcs.ctype = ["RA---TAN", "DEC--TAN", "TIME"] + w.wcs.set() + assert w.has_temporal + assert w.sub([wcs.WCSSUB_TIME]).is_temporal + assert ( + w.wcs_pix2world([[1, 2, 3]], 0)[0, 2] + == w.temporal.wcs_pix2world([[3]], 0)[0, 0] + ) + + +def test_swapaxes_same_val_roundtrip(): + w = wcs.WCS(naxis=3) + w.wcs.ctype = ["RA---TAN", "DEC--TAN", "FREQ"] + w.wcs.crpix = [32.5, 16.5, 1.0] + w.wcs.crval = [5.63, -72.05, 1.0] + w.wcs.pc = [[5.9e-06, 1.3e-05, 0.0], [-1.2e-05, 5.0e-06, 0.0], [0.0, 0.0, 1.0]] + w.wcs.cdelt = [1.0, 1.0, 1.0] + w.wcs.set() + axes_order = [3, 2, 1] + axes_order0 = [i - 1 for i in axes_order] + ws = w.sub(axes_order) + imcoord = np.array([3, 5, 7]) + imcoords = imcoord[axes_order0] + val_ref = w.wcs_pix2world([imcoord], 0)[0] + val_swapped = ws.wcs_pix2world([imcoords], 0)[0] + + # check original axis and swapped give same results + assert np.allclose(val_ref[axes_order0], val_swapped, rtol=0, atol=1e-8) + + # check round-tripping: + assert np.allclose(w.wcs_world2pix([val_ref], 0)[0], imcoord, rtol=0, atol=1e-8) + + +def test_DistortionLookupTable(): + img_world_wcs = wcs.WCS(naxis=2) + # A simple "pixel coordinates are world coordinates" WCS, to which we'll + # add distortion lookup tables + img_world_wcs.wcs.crpix = 1, 1 + img_world_wcs.wcs.crval = 0, 0 + img_world_wcs.wcs.cdelt = 1, 1 + + # Create maps with zero distortion except at one particular pixel + x_dist_array = np.zeros((25, 25)) + x_dist_array[10, 20] = 0.5 + map_x = wcs.DistortionLookupTable( + x_dist_array.astype(np.float32), (5, 10), (10, 20), (2, 2) + ) + + y_dist_array = np.zeros((25, 25)) + y_dist_array[10, 5] = 0.7 + map_y = wcs.DistortionLookupTable( + y_dist_array.astype(np.float32), (5, 10), (10, 20), (3, 3) + ) + + img_world_wcs.cpdis1 = map_x + img_world_wcs.cpdis2 = map_y + + # The x distortion of 0.5 pixels should appear at a specific spot in the + # image, and we need to work out what that is so we can check it. The + # distortion is at array index (10, 20), which is a 1-based pixel + # coordinate of (21, 11) and therefore at an offset of (16, 1) from the + # lookup table's CRPIX of (5, 10). CDELT is 2 image pixels / distortion + # pixel, so the distortion applies to the image pixel which is at an offset + # of (32, 2) from the CRVAL of (10, 20), meaning we should see the + # distortion at the 1-based image pixel coordinate of (42, 22). + assert_allclose(map_x.get_offset(42, 22), 0.5) + # And we should see it applied at the 0-based coordinate (41, 21) with our + # simple img<->world wcs. + assert_allclose(img_world_wcs.pixel_to_world_values(41, 21), [41.5, 21]) + # Similarly for the y distortion, the distortion is at array index (10, 5), + # which is a 1-based pixel coordinate of (6, 11) and therefore at an offset + # of (1, 1) from the lookup table's CRPIX of (5, 10). CDELT is 3 image + # pixels / distortion pixel, so the distortion applies to the image pixel + # which is at an offset of (3, 3) from the CRVAL of (10, 20), meaning we + # should see the distortion at the 1-based image pixel coordinate of (13, + # 23). + assert_allclose(map_y.get_offset(13, 23), 0.7) + # And we should see it applied at the 0-based coordinate (12, 22) with our + # simple img<->world wcs. + assert_allclose(img_world_wcs.pixel_to_world_values(12, 22), [12, 22.7]) + + # Now check that when we move the image location by the equivalent of 1/2 + # distortion-array pixel, we see only half the distortion. + for dx, dy in [(0.5, 0), (-0.5, 0), (0, 0.5), (0, -0.5)]: + # Scale dx, dy by 2 for the CDELT in the x distortion table (since + # we're looking for the x distortion). + assert_allclose( + img_world_wcs.pixel_to_world_values(41 + dx * 2, 21 + dy * 2), + [41 + dx * 2 + 0.25, 21 + dy * 2], + ) + # Scale dx, dy by 3 for the CDELT in the y distortion table (since + # we're looking for the y distortion). + assert_allclose( + img_world_wcs.pixel_to_world_values(12 + dx * 3, 22 + dy * 3), + [12 + dx * 3, 22 + dy * 3 + 0.35], + ) + + # Now check that when we move the image location by the equivalent of 1 + # distortion-array pixel, we see no distortion. + for dx, dy in [(2, 0), (-2, 0), (0, 2), (0, -2)]: + # Scale dx, dy by 2 for the CDELT in the x distortion table (since + # we're looking for the x distortion). + assert_allclose( + img_world_wcs.pixel_to_world_values(41 + dx * 2, 21 + dy * 2), + [41 + dx * 2, 21 + dy * 2], + ) + # Scale dx, dy by 3 for the CDELT in the y distortion table (since + # we're looking for the y distortion). + assert_allclose( + img_world_wcs.pixel_to_world_values(12 + dx * 3, 22 + dy * 3), + [12 + dx * 3, 22 + dy * 3], + ) + + +HEADER_WITH_NON_SI_UNITS = """ +WCSAXES = 5 +CTYPE1 = 'RA---TAN' +CTYPE2 = 'FREQ' +CTYPE3 = 'DEC--TAN' +CTYPE4 = 'WAVE' +CTYPE5 = 'OFFSET' +CUNIT1 = 'arcsec' +CUNIT2 = 'GHz' +CUNIT3 = 'arcsec' +CUNIT4 = 'nm' +CUNIT5 = 'arcmin' +CRVAL1 = 4 +CRVAL2 = 5 +CRVAL3 = 6 +CRVAL4 = 7 +CRVAL5 = 8 +CRPIX1 = 1 +CRPIX2 = 2 +CRPIX3 = 3 +CRPIX4 = 4 +CRPIX5 = 5 +CDELT1 = 4 +CDELT2 = 3 +CDELT3 = 2 +CDELT4 = 1 +CDELT5 = 6 +""".strip() + + +class TestPreserveUnits: + def setup_method(self, method): + self.header = fits.Header.fromstring(HEADER_WITH_NON_SI_UNITS, sep="\n") + self.wcs_default = wcs.WCS(self.header) + self.wcs_preserve = wcs.WCS(self.header, preserve_units=True) + rsn = default_rng(1234567890) + self.coords = rsn.uniform(-10, 10, (100, 5)) + self.scale = np.array([1 / 3600, 1e9, 1 / 3600, 1e-9, 1]) + + def test_preserve_units_property(self): + assert not self.wcs_default.preserve_units + assert self.wcs_preserve.preserve_units + + def test_get_cunit(self): + assert list(self.wcs_default.wcs.cunit) == ["deg", "Hz", "deg", "m", "arcmin"] + assert list(self.wcs_preserve.wcs.cunit) == [ + "arcsec", + "GHz", + "arcsec", + "nm", + "arcmin", + ] + + def test_set_cunit(self): + self.wcs_preserve.wcs.cunit = "arcmin", "MHz", "deg", "m", "arcmin" + assert list(self.wcs_preserve.wcs.cunit) == [ + "arcmin", + "MHz", + "deg", + "m", + "arcmin", + ] + + def test_get_cdelt(self): + assert_allclose(self.wcs_default.wcs.cdelt, [4 / 3600, 3e9, 2 / 3600, 1e-9, 6]) + assert_allclose(self.wcs_preserve.wcs.cdelt, [4, 3, 2, 1, 6]) + + def test_get_cdelt_indiv(self): + assert_allclose(self.wcs_default.wcs.cdelt[0], [4 / 3600]) + assert_allclose(self.wcs_preserve.wcs.cdelt[0], [4]) + + def test_set_cdelt(self): + self.wcs_default.wcs.cdelt = [1 / 3600, 2e9, 3 / 3600, 4e-9, 5] + self.wcs_preserve.wcs.cdelt = [1, 2, 3, 4, 5] + assert_allclose(self.wcs_default.wcs.cdelt, [1 / 3600, 2e9, 3 / 3600, 4e-9, 5]) + assert_allclose(self.wcs_preserve.wcs.cdelt, [1, 2, 3, 4, 5]) + + def test_set_cdelt_indiv(self): + # Changing individual items is complicated in preserve_units mode because + # there is no easy way to know that a value in memory has been changed + # in the array. If we really want to support it then before each + # transformation we could check if the WCS hash has changed, but it is + # easier and less confusing to simply require cdelt to be overwritten + # as a whole rather than individual values set. + + self.wcs_default.wcs.cdelt[0] = 1 / 3600 + assert_allclose(self.wcs_default.wcs.cdelt, [1 / 3600, 3e9, 2 / 3600, 1e-9, 6]) + + with pytest.raises(ValueError, match="assignment destination is read-only"): + self.wcs_preserve.wcs.cdelt[0] = 1 + + def test_get_cdelt_meth(self): + assert_allclose( + self.wcs_default.wcs.get_cdelt(), [4 / 3600, 3e9, 2 / 3600, 1e-9, 6] + ) + assert_allclose(self.wcs_preserve.wcs.get_cdelt(), [4, 3, 2, 1, 6]) + + def test_get_crval(self): + assert_allclose(self.wcs_default.wcs.crval, [4 / 3600, 5e9, 6 / 3600, 7e-9, 8]) + assert_allclose(self.wcs_preserve.wcs.crval, [4, 5, 6, 7, 8]) + + def test_get_crval_indiv(self): + assert_allclose(self.wcs_default.wcs.crval[0], [4 / 3600]) + assert_allclose(self.wcs_preserve.wcs.crval[0], [4]) + + def test_set_crval(self): + self.wcs_default.wcs.crval = [1 / 3600, 2e9, 3 / 3600, 4e-9, 5] + self.wcs_preserve.wcs.crval = [1, 2, 3, 4, 5] + assert_allclose(self.wcs_default.wcs.crval, [1 / 3600, 2e9, 3 / 3600, 4e-9, 5]) + assert_allclose(self.wcs_preserve.wcs.crval, [1, 2, 3, 4, 5]) + + def test_set_crval_indiv(self): + # See comment in test_set_cdelt_indiv about why this raises an error + + self.wcs_default.wcs.crval[0] = 1 / 3600 + assert_allclose(self.wcs_default.wcs.crval, [1 / 3600, 5e9, 6 / 3600, 7e-9, 8]) + + with pytest.raises(ValueError, match="assignment destination is read-only"): + self.wcs_preserve.wcs.crval[0] = 1 + + def test_p2s(self): + result_default = self.wcs_default.wcs.p2s(self.coords, 0) + result_preserve = self.wcs_preserve.wcs.p2s(self.coords, 0) + for key in result_default: + if key == "world": + assert_allclose(result_default[key], result_preserve[key] * self.scale) + else: + assert_allclose(result_default[key], result_preserve[key]) + + def test_s2p(self): + result_default = self.wcs_default.wcs.s2p(self.coords * self.scale, 0) + result_preserve = self.wcs_preserve.wcs.s2p(self.coords, 0) + for key in result_default: + assert_allclose(result_default[key], result_preserve[key]) + + def test_wcs_pix2world(self): + result_default = self.wcs_default.wcs_pix2world(self.coords, 0) + result_preserve = self.wcs_preserve.wcs_pix2world(self.coords, 0) + assert_allclose(result_default, result_preserve * self.scale) + # Make sure we aren't modifying the input array in-place + result_preserve = self.wcs_preserve.wcs_pix2world(self.coords, 0) + assert_allclose(result_default, result_preserve * self.scale) + + def test_wcs_world2pix(self): + result_default = self.wcs_default.wcs_world2pix(self.coords * self.scale, 0) + result_preserve = self.wcs_preserve.wcs_world2pix(self.coords, 0) + assert_allclose(result_default, result_preserve) + # Make sure we aren't modifying the input array in-place + result_preserve = self.wcs_preserve.wcs_world2pix(self.coords, 0) + assert_allclose(result_default, result_preserve) + + def test_all_pix2world(self): + result_default = self.wcs_default.all_pix2world(self.coords, 0) + result_preserve = self.wcs_preserve.all_pix2world(self.coords, 0) + assert_allclose(result_default, result_preserve * self.scale) + # Make sure we aren't modifying the input array in-place + result_preserve = self.wcs_preserve.all_pix2world(self.coords, 0) + assert_allclose(result_default, result_preserve * self.scale) + + def test_all_world2pix(self): + result_default = self.wcs_default.all_world2pix(self.coords * self.scale, 0) + result_preserve = self.wcs_preserve.all_world2pix(self.coords, 0) + assert_allclose(result_default, result_preserve) + # Make sure we aren't modifying the input array in-place + result_preserve = self.wcs_preserve.all_world2pix(self.coords, 0) + assert_allclose(result_default, result_preserve) + + def test_get_cd(self): + header = self.header.copy() + + for i in range(5): + header.pop(f"CDELT{i + 1}") + + CD = np.arange(25).reshape((5, 5)) + + for i in range(5): + for j in range(5): + header[f"CD{i + 1}_{j + 1}"] = CD[i, j] + + wcs_default = wcs.WCS(header) + wcs_preserve = wcs.WCS(header, preserve_units=True) + + assert_allclose(wcs_default.wcs.cd, CD * self.scale[:, np.newaxis]) + assert_allclose(wcs_preserve.wcs.cd, CD) + + assert_allclose(wcs_default.wcs.cd[0, 1], CD[0, 1] * self.scale[0]) + assert_allclose(wcs_preserve.wcs.cd[0, 1], CD[0, 1]) + + def test_set_cd(self): + CD = np.arange(25).reshape((5, 5)) + + self.wcs_default.wcs.cd = CD * self.scale[:, np.newaxis] + self.wcs_preserve.wcs.cd = CD + + assert_allclose(self.wcs_default.wcs.cd, CD * self.scale[:, np.newaxis]) + assert_allclose(self.wcs_preserve.wcs.cd, CD) + + # See comment in test_set_cdelt_indiv about why this raises an error + + self.wcs_default.wcs.cd[0, 1] = 1 / 3600 + assert_allclose(self.wcs_default.wcs.cd[0, 1], 1 / 3600) + + with pytest.raises(ValueError, match="assignment destination is read-only"): + self.wcs_preserve.wcs.cd[0, 1] = 1 + + def test_header(self): + header = self.wcs_preserve.to_header() + + expected_header = """ +WCSAXES = 5 / Number of coordinate axes +CRPIX1 = 1.0 / Pixel coordinate of reference point +CRPIX2 = 2.0 / Pixel coordinate of reference point +CRPIX3 = 3.0 / Pixel coordinate of reference point +CRPIX4 = 4.0 / Pixel coordinate of reference point +CRPIX5 = 5.0 / Pixel coordinate of reference point +CDELT1 = 4.0 / [arcsec] Coordinate increment at reference poin +CDELT2 = 3.0 / [GHz] Coordinate increment at reference point +CDELT3 = 2.0 / [arcsec] Coordinate increment at reference poin +CDELT4 = 1.0 / [nm] Coordinate increment at reference point +CDELT5 = 6.0 / [arcmin] Coordinate increment at reference poin +CUNIT1 = 'arcsec' / Units of coordinate increment and value +CUNIT2 = 'GHz' / Units of coordinate increment and value +CUNIT3 = 'arcsec' / Units of coordinate increment and value +CUNIT4 = 'nm' / Units of coordinate increment and value +CUNIT5 = 'arcmin' / Units of coordinate increment and value +CTYPE1 = 'RA---TAN' / Right ascension, gnomonic projection +CTYPE2 = 'FREQ' / Frequency (linear) +CTYPE3 = 'DEC--TAN' / Declination, gnomonic projection +CTYPE4 = 'WAVE' / Coordinate type code +CTYPE5 = 'OFFSET' / Coordinate type code +CRVAL1 = 4.0 / [arcsec] Coordinate value at reference point +CRVAL2 = 5.0 / [GHz] Coordinate value at reference point +CRVAL3 = 6.0 / [arcsec] Coordinate value at reference point +CRVAL4 = 7.0 / [nm] Coordinate value at reference point +CRVAL5 = 8.0 / [arcmin] Coordinate value at reference point +LONPOLE = 180.0 / [deg] Native longitude of celestial pole +LATPOLE = 0.0016666666666667 / [deg] Native latitude of celestial pole +MJDREF = 0.0 / [d] MJD of fiducial time +RADESYS = 'ICRS' / Equatorial coordinate system +""".strip() + + assert header.tostring(sep="\n") == fits.Header.fromstring( + expected_header, sep="\n" + ).tostring(sep="\n") + + @pytest.mark.parametrize("explicit_set", (False, True)) + @pytest.mark.parametrize( + "function", + ( + "p2s", + "s2p", + "wcs_pix2world", + "wcs_world2pix", + "all_pix2world", + "all_world2pix", + ), + ) + def test_programmatic(self, explicit_set, function): + # Make sure that things work fine if we make the WCS programmatically + # and not from a header + + wcs_prog = wcs.WCS(naxis=2, preserve_units=True) + wcs_prog.wcs.ctype = "RA---TAN", "DEC--TAN" + wcs_prog.wcs.cunit = "arcsec", "arcsec" + wcs_prog.wcs.crval = 10, 20 + wcs_prog.wcs.cdelt = 1, 2 + wcs_prog.wcs.crpix = 1, 1 + + if explicit_set: + wcs_prog.wcs.set() + + # FIXME: the following fails if explicit_set is False because wcsset + # gets called implicitly during the coordinate conversion but we don't + # catch this and store the before/after units. + + if function == "p2s": + assert_allclose( + wcs_prog.wcs.p2s(np.array([[1, 1]]), 1)["world"], [[10, 20]] + ) + elif function == "s2p": + assert_allclose( + wcs_prog.wcs.s2p(np.array([[10, 20]]), 1)["pixcrd"], [[1, 1]] + ) + elif function == "wcs_pix2world": + assert_allclose(wcs_prog.wcs_pix2world(1, 1, 1), [10, 20]) + elif function == "wcs_world2pix": + assert_allclose(wcs_prog.wcs_world2pix(10, 20, 1), [1, 1]) + elif function == "all_pix2world": + assert_allclose(wcs_prog.all_pix2world(1, 1, 1), [10, 20]) + elif function == "all_world2pix": + assert_allclose(wcs_prog.all_world2pix(10, 20, 1), [1, 1]) + + def test_change_cunit(self): + # Make sure that things work fine if we make the WCS programmatically + # and not from a header, and check that we can change the units after + # set() is called + + wcs_prog = wcs.WCS(naxis=2, preserve_units=True) + wcs_prog.wcs.ctype = "RA---TAN", "DEC--TAN" + wcs_prog.wcs.cunit = "arcsec", "arcsec" + wcs_prog.wcs.crval = 10, 20 + wcs_prog.wcs.cdelt = 1, 2 + wcs_prog.wcs.crpix = 1, 1 + + assert list(wcs_prog.wcs.cunit) == ["arcsec", "arcsec"] + assert_allclose(wcs_prog.wcs.crval, [10, 20]) + assert_allclose(wcs_prog.wcs.cdelt, [1, 2]) + + wcs_prog.wcs.set() + + assert list(wcs_prog.wcs.cunit) == ["arcsec", "arcsec"] + assert_allclose(wcs_prog.wcs.crval, [10, 20]) + assert_allclose(wcs_prog.wcs.cdelt, [1, 2]) + + wcs_prog.wcs.cunit = "arcmin", "arcmin" + + wcs_prog.wcs.set() + + assert list(wcs_prog.wcs.cunit) == ["arcmin", "arcmin"] + assert_allclose(wcs_prog.wcs.crval, [10, 20]) + assert_allclose(wcs_prog.wcs.cdelt, [1, 2]) + + wcs_prog.wcs.cunit = "deg", "deg" + + wcs_prog.wcs.set() + + assert list(wcs_prog.wcs.cunit) == ["deg", "deg"] + assert_allclose(wcs_prog.wcs.crval, [10, 20]) + assert_allclose(wcs_prog.wcs.cdelt, [1, 2]) + + wcs_equiv = wcs.WCS(naxis=2) + wcs_equiv.wcs.ctype = "RA---TAN", "DEC--TAN" + wcs_equiv.wcs.cunit = "deg", "deg" + wcs_equiv.wcs.crval = 10, 20 + wcs_equiv.wcs.cdelt = 1, 2 + wcs_equiv.wcs.crpix = 1, 1 + + assert_allclose( + wcs_prog.all_pix2world(2, 3, 0), wcs_equiv.all_pix2world(2, 3, 0) + ) + + def test_change_cunit_original_deg(self): + # Similar to test_change_cunit but here we start off with a WCS in + # degrees so that there is no scaling going on, and then we start + # changing the values. + + wcs_prog = wcs.WCS(naxis=2, preserve_units=True) + wcs_prog.wcs.ctype = "RA---TAN", "DEC--TAN" + wcs_prog.wcs.cunit = "deg", "deg" + wcs_prog.wcs.crval = 10, 20 + wcs_prog.wcs.cdelt = 1, 2 + wcs_prog.wcs.crpix = 1, 1 + + wcs_prog.wcs.set() + + assert list(wcs_prog.wcs.cunit) == ["deg", "deg"] + assert_allclose(wcs_prog.wcs.crval, [10, 20]) + assert_allclose(wcs_prog.wcs.cdelt, [1, 2]) + + assert wcs_prog.wcs._unit_scaling is None + + wcs_prog.wcs.cunit = "arcsec", "arcsec" + + wcs_prog.wcs.set() + + assert_allclose(wcs_prog.wcs._unit_scaling, [1 / 3600, 1 / 3600]) + + assert list(wcs_prog.wcs.cunit) == ["arcsec", "arcsec"] + assert_allclose(wcs_prog.wcs.crval, [10, 20]) + assert_allclose(wcs_prog.wcs.cdelt, [1, 2]) + + wcs_equiv = wcs.WCS(naxis=2) + wcs_equiv.wcs.ctype = "RA---TAN", "DEC--TAN" + wcs_equiv.wcs.cunit = "arcsec", "arcsec" + wcs_equiv.wcs.crval = 10, 20 + wcs_equiv.wcs.cdelt = 1, 2 + wcs_equiv.wcs.crpix = 1, 1 + wcs_equiv.wcs.set() + + assert_allclose( + wcs_prog.all_pix2world(2, 3, 0)[0], + wcs_equiv.all_pix2world(2, 3, 0)[0] * 3600, + ) + assert_allclose( + wcs_prog.all_pix2world(2, 3, 0)[1], + wcs_equiv.all_pix2world(2, 3, 0)[1] * 3600, + ) + + def test_change_cunit_inplace(self): + wcs_simple = wcs.WCS(naxis=1, preserve_units=True) + wcs_simple.wcs.ctype = ("FREQ",) + wcs_simple.wcs.cunit = ("GHz",) + + # Setting units in-place works before set() is called + + wcs_simple.wcs.cunit[0] = "MHz" + + # However, it should not once set() has been called + + wcs_simple.wcs.set() + + with pytest.raises( + RuntimeError, + match=re.escape( + "Cannot set individual units in-place " + "once set() has been called when using preserve_units=True" + ), + ): + wcs_simple.wcs.cunit[0] = "kHz" + + def test_change_cunit_incompatible(self): + # Make sure we still see errors related to incompatible units + + wcs_simple = wcs.WCS(naxis=1, preserve_units=True) + wcs_simple.wcs.ctype = ("FREQ",) + wcs_simple.wcs.cunit = ("GHz",) + wcs_simple.wcs.set() + + wcs_simple.wcs.cunit = ("mm",) + with pytest.raises(wcs.InvalidTransformError, match="Mismatched units type"): + wcs_simple.wcs.set() + + def test_str_and_repr(self): + expected = ( + "WCS Keywords\n" + "\n" + "Number of WCS axes: 5\n" + "CTYPE : 'RA---TAN' 'FREQ' 'DEC--TAN' 'WAVE' 'OFFSET'\n" + "CUNIT : 'arcsec' 'GHz' 'arcsec' 'nm' 'arcmin'\n" + "CRVAL : 4.0 5.0 6.0 7.0 8.0\n" + "CRPIX : 1.0 2.0 3.0 4.0 5.0\n" + "PC1_1 PC1_2 PC1_3 PC1_4 PC1_5 : 1.0 0.0 0.0 0.0 0.0\n" + "PC2_1 PC2_2 PC2_3 PC2_4 PC2_5 : 0.0 1.0 0.0 0.0 0.0\n" + "PC3_1 PC3_2 PC3_3 PC3_4 PC3_5 : 0.0 0.0 1.0 0.0 0.0\n" + "PC4_1 PC4_2 PC4_3 PC4_4 PC4_5 : 0.0 0.0 0.0 1.0 0.0\n" + "PC5_1 PC5_2 PC5_3 PC5_4 PC5_5 : 0.0 0.0 0.0 0.0 1.0\n" + "CDELT : 4.0 3.0 2.0 1.0 6.0\n" + "NAXIS : 0 0 0 0 0" + ) + assert str(self.wcs_preserve) == expected + assert repr(self.wcs_preserve) == expected + + def test_print_contents(self, capfd): + self.wcs_preserve.wcs.print_contents() + captured = capfd.readouterr() + + expected = """ + cdelt: 0xADDR + 4.0000 3.0000 2.0000 1.0000 6.0000 + crval: 0xADDR + 4.0000 5.0000 6.0000 7.0000 8.0000 + cunit: 0xADDR + "arcsec" + "GHz" + "arcsec" + "nm" + "arcmin" + """.strip() + + assert expected in "\n".join( + [x.rstrip() for x in strip_memory_addresses(captured.out).splitlines()] + ) + + def test_unit_scaling(self): + # This is testing a private property that exists to be able to check + # what the unit scaling is between the original and modified units + + assert self.wcs_default.wcs._unit_scaling is None + assert_allclose(self.wcs_preserve.wcs._unit_scaling, self.scale) + + def test_no_scaling_if_no_unit_changes(self): + # This checks that if a WCS doesn't actually have units changing in + # WCSLIB, that we aren't using the unit scaling machinery with scales + # all set to 1 since this would have an unnecessary impact on performance. + + simple_wcs = wcs.WCS(naxis=3, preserve_units=True) + simple_wcs.wcs.ctype = "RA---TAN", "DEC--TAN", "FREQ" + simple_wcs.wcs.cunit = "deg", "deg", "Hz" + simple_wcs.wcs.set() + + assert simple_wcs.wcs._unit_scaling is None + + def test_no_scaling_if_no_scale_changes(self): + # A variant of the above - sometimes units are changed, for example + # 'deg ' -> 'deg', which is strictly different but has a scale of 1. + # In this case, we shouldn't do any unit scaling. + + simple_wcs = wcs.WCS(naxis=3, preserve_units=True) + simple_wcs.wcs.ctype = "RA---TAN", "DEC--TAN", "FREQ" + simple_wcs.wcs.cunit = "deg ", "deg ", "Hz" + simple_wcs.wcs.set() + + assert simple_wcs.wcs._unit_scaling is None + + @pytest.mark.parametrize("mixed_with_spatial", (False, True)) + def test_wcstab_cunit_conversion(self, mixed_with_spatial): + # WCSLIB does not convert the units for coordinates that use lookup tables + # with -TAB, but we should make sure that things still work properly + # with preserve_units=True + + if mixed_with_spatial: + flux = np.random.rand(100, 4, 5) + else: + flux = np.random.rand(100) + + wav = np.linspace(4000, 5000, 100) + + primary_hdu = fits.PrimaryHDU(flux) + + arr = np.array([(wav,)], dtype=[("WAVE", np.float64, wav.size)]) + tab_hdu = fits.BinTableHDU(arr) + tab_hdu.name = "WCS-TAB" + + header = primary_hdu.header + header["WCSAXES"] = 3 if mixed_with_spatial else 1 + header["CTYPE1"] = "WAVE-TAB" + header["CRPIX1"] = 1 + header["CDELT1"] = 1 + header["CRVAL1"] = 0 + header["CUNIT1"] = "mm" + header["PS1_0"] = "WCS-TAB" + header["PS1_1"] = "WAVE" + header["PV1_1"] = 1 + + if mixed_with_spatial: + header["CTYPE2"] = "GLON-CAR" + header["CTYPE3"] = "GLAT-CAR" + header["CUNIT2"] = "arcsec" + header["CUNIT3"] = "deg" + header["CDELT2"] = -1 + header["CDELT3"] = 2 / 3600.0 + + hdul = fits.HDUList([primary_hdu, tab_hdu]) + + wcs_default = wcs.WCS(hdul[0].header, hdul) + wcs_preserve = wcs.WCS(hdul[0].header, hdul, preserve_units=True) + + rsn = default_rng(1234567890) + pixel = rsn.uniform(1, 99, 3) + world = rsn.uniform(4000, 5000, 3) + + pixel = [pixel] * (3 if mixed_with_spatial else 1) + + if mixed_with_spatial: + world_spatial = rsn.uniform(-1, 1, 3) + world = [world, world_spatial, world_spatial] + else: + world = [world] + + # Check that both the default and unit-preserving WCS have the same units + # of mm - this is used to know in future if WCSLIB changes the default + # behavior so that the default WCS would be in units of meters for example + assert wcs_default.wcs.cunit[0] == "mm" + assert wcs_preserve.wcs.cunit[0] == "mm" + + assert not np.any(np.isnan(np.array(wcs_default.all_pix2world(*pixel, 0)))) + + assert_allclose( + wcs_default.all_pix2world(*pixel, 0)[0], + wcs_preserve.all_pix2world(*pixel, 0)[0], + ) + + assert not np.any(np.isnan(np.array(wcs_default.all_world2pix(*world, 0)))) + + assert_allclose( + wcs_default.all_world2pix(*world, 0)[0], + wcs_preserve.all_world2pix(*world, 0)[0], + ) + + assert wcs_default.wcs._unit_scaling is None + if mixed_with_spatial: + assert_allclose(wcs_preserve.wcs._unit_scaling, [1, 1 / 3600, 1]) + else: + assert wcs_preserve.wcs._unit_scaling is None + + # If we change the units, the results should continue to be consistent + wcs_default.wcs.cunit = ["cm"] + (["deg", "deg"] if mixed_with_spatial else []) + wcs_preserve.wcs.cunit = ["cm"] + ( + ["arcsec", "deg"] if mixed_with_spatial else [] + ) + + assert wcs_default.wcs.cunit[0] == "cm" + assert wcs_preserve.wcs.cunit[0] == "cm" + + assert not np.any(np.isnan(np.array(wcs_default.all_pix2world(*pixel, 0)))) + + assert_allclose( + wcs_default.all_pix2world(*pixel, 0)[0], + wcs_preserve.all_pix2world(*pixel, 0)[0], + ) + + assert not np.any(np.isnan(np.array(wcs_default.all_world2pix(*world, 0)))) + + assert_allclose( + wcs_default.all_world2pix(*world, 0)[0], + wcs_preserve.all_world2pix(*world, 0)[0], + ) + + # Make sure that in both cases we aren't using the unit scaling machinery + assert wcs_default.wcs._unit_scaling is None + if mixed_with_spatial: + assert_allclose(wcs_preserve.wcs._unit_scaling, [1, 1 / 3600, 1]) + else: + assert wcs_preserve.wcs._unit_scaling is None + + def test_copy(self): + # Check that copying will keep the preserve_units setting and the + # correct unit scalefactors + copy = self.wcs_preserve.copy() + assert_allclose(copy.wcs._unit_scaling, self.scale) + + def test_deepcopy(self): + # Check that deep-copying will keep the preserve_units setting and the + # correct unit scalefactors + copy = self.wcs_preserve.deepcopy() + assert_allclose(copy.wcs._unit_scaling, self.scale) + + def test_sub(self): + # Check that taking e.g. .celestial, .spectral, and more generally .sub + # will pass the preserve_units to the new WCS object. + + assert_allclose( + self.wcs_preserve.celestial.wcs._unit_scaling, + np.array([1 / 3600, 1 / 3600]), + ) + + assert_allclose( + self.wcs_preserve.spectral.wcs._unit_scaling, np.array([1e9, 1e-9]) + ) + + assert_allclose( + self.wcs_preserve.sub([1, 2, 0, 3, 4, 5]).wcs._unit_scaling, + np.array([1 / 3600, 1e9, 1.0, 1 / 3600, 1e-9, 1]), + ) + + +def test_thread_safe_conversions(): + # This is a regression test for a bug which caused wcsset to be called + # unnecessarily multiple times, including every time some attribute were + # accessed on the WCS. This meant that if one was doing a series of + # coordinate transforms in a multi-threaded environment, wcsset could + # get called in the process and modify the WCS object, which was not + # thread-safe. Now wcsset is not actually called after the initial time. + # This was discussed in more detail in https://github.com/astropy/astropy/issues/16245 + + w = wcs.WCS(naxis=2) + w.wcs.crpix = [-234.75, 8.3393] + w.wcs.cdelt = np.array([-0.066667, 0.066667]) + w.wcs.crval = [0, -90] + w.wcs.ctype = ["RA---AIR", "DEC--AIR"] + w.wcs.set() + + N = 1_000_000 + pixel = np.random.randint(-1000, 1000, N * 2).reshape((N, 2)).astype(float) -def test_backward_compatible(): - fits = get_data_filename('data/sip.fits') - w = wcs.WCS(fits) + def round_trip_transform(pixel): + world = w.wcs.p2s(pixel.copy(), 1)["world"] + w.wcs.lng # this access causes issues, without it all works + pixel = w.wcs.s2p(world, 1)["pixcrd"] + return pixel - data = np.random.rand(100, 2) - assert np.all(w.wcs_pix2world(data, 0) == w.wcs_pix2sky(data, 0)) - assert np.all(w.wcs_world2pix(data, 0) == w.wcs_sky2pix(data, 0)) + with ThreadPool(8) as pool: + results = pool.map(round_trip_transform, (pixel,) * 8) + for pixel2 in results: + assert_allclose(pixel, pixel2, atol=1e-7) diff --git a/astropy/wcs/tests/test_wcsprm.py b/astropy/wcs/tests/test_wcsprm.py index b52127106c82..fb730b76b68b 100644 --- a/astropy/wcs/tests/test_wcsprm.py +++ b/astropy/wcs/tests/test_wcsprm.py @@ -1,100 +1,110 @@ -from __future__ import print_function +# Licensed under a 3-clause BSD style license - see LICENSE.rst +import gc import locale import re -from astropy.tests.helper import raises -from numpy.testing import assert_array_equal import numpy as np import pytest - -from .. import wcs -from .. import _wcs -from ...config import get_data_contents, get_data_fileobj - - -def b(s): - return s.encode('ascii') +from numpy.testing import assert_array_almost_equal, assert_array_equal +from packaging.version import Version + +from astropy import units as u +from astropy.io import fits +from astropy.units import UnitsWarning +from astropy.utils.data import ( + get_pkg_data_contents, + get_pkg_data_filename, + get_pkg_data_fileobj, +) +from astropy.utils.misc import _set_locale +from astropy.wcs import _wcs, wcs +from astropy.wcs.wcs import FITSFixedWarning ###################################################################### def test_alt(): - w = _wcs._Wcsprm() - assert w.alt == b(" ") - w.alt = b("X") - assert w.alt == b("X") + w = _wcs.Wcsprm() + assert w.alt == " " + w.alt = "X" + assert w.alt == "X" del w.alt - assert w.alt == b(" ") + assert w.alt == " " -@raises(ValueError) def test_alt_invalid1(): - w = _wcs._Wcsprm() - w.alt = b("$") + w = _wcs.Wcsprm() + with pytest.raises(ValueError): + w.alt = "$" -@raises(ValueError) def test_alt_invalid2(): - w = _wcs._Wcsprm() - w.alt = b(" ") + w = _wcs.Wcsprm() + with pytest.raises(ValueError): + w.alt = " " def test_axis_types(): - w = _wcs._Wcsprm() + w = _wcs.Wcsprm() assert_array_equal(w.axis_types, [0, 0]) def test_cd(): - w = _wcs._Wcsprm() + w = _wcs.Wcsprm() w.cd = [[1, 0], [0, 1]] - assert w.cd.dtype == np.float - assert w.has_cd() == True + assert w.cd.dtype == float + assert w.has_cd() is True assert_array_equal(w.cd, [[1, 0], [0, 1]]) del w.cd - assert w.has_cd() == False + assert w.has_cd() is False -@raises(AttributeError) def test_cd_missing(): - w = _wcs._Wcsprm() - assert w.has_cd() == False - w.cd + w = _wcs.Wcsprm() + assert w.has_cd() is False + with pytest.raises(AttributeError): + w.cd -@raises(AttributeError) def test_cd_missing2(): - w = _wcs._Wcsprm() + w = _wcs.Wcsprm() w.cd = [[1, 0], [0, 1]] - assert w.has_cd() == True + assert w.has_cd() is True del w.cd - assert w.has_cd() == False - w.cd + assert w.has_cd() is False + with pytest.raises(AttributeError): + w.cd -@raises(ValueError) def test_cd_invalid(): - w = _wcs._Wcsprm() - w.cd = [1, 0, 0, 1] + w = _wcs.Wcsprm() + with pytest.raises(ValueError): + w.cd = [1, 0, 0, 1] + + +def test_cdfix(): + w = _wcs.Wcsprm() + w.cdfix() def test_cdelt(): - w = _wcs._Wcsprm() + w = _wcs.Wcsprm() assert_array_equal(w.cdelt, [1, 1]) w.cdelt = [42, 54] assert_array_equal(w.cdelt, [42, 54]) -@raises(TypeError) def test_cdelt_delete(): - w = _wcs._Wcsprm() - del w.cdelt + w = _wcs.Wcsprm() + with pytest.raises(TypeError): + del w.cdelt def test_cel_offset(): - w = _wcs._Wcsprm() + w = _wcs.Wcsprm() assert w.cel_offset is False - w.cel_offset = 'foo' + w.cel_offset = "foo" assert w.cel_offset is True w.cel_offset = 0 assert w.cel_offset is False @@ -103,28 +113,28 @@ def test_cel_offset(): def test_celfix(): # TODO: We need some data with -NCP or -GLS projections to test # with. For now, this is just a smoke test - w = _wcs._Wcsprm() + w = _wcs.Wcsprm() assert w.celfix() == -1 def test_cname(): - w = _wcs._Wcsprm() + w = _wcs.Wcsprm() # Test that this works as an iterator for x in w.cname: - assert x == b('') - assert list(w.cname) == [b(''), b('')] - w.cname = [b('foo'), b('bar')] - assert list(w.cname) == [b('foo'), b('bar')] + assert x == "" + assert list(w.cname) == ["", ""] + w.cname = [b"foo", "bar"] + assert list(w.cname) == ["foo", "bar"] -@raises(TypeError) def test_cname_invalid(): - w = _wcs._Wcsprm() - w.cname = [42, 54] + w = _wcs.Wcsprm() + with pytest.raises(TypeError): + w.cname = [42, 54] def test_colax(): - w = _wcs._Wcsprm() + w = _wcs.Wcsprm() assert w.colax.dtype == np.intc assert_array_equal(w.colax, [0, 0]) w.colax = [42, 54] @@ -132,69 +142,85 @@ def test_colax(): w.colax[0] = 0 assert_array_equal(w.colax, [0, 54]) + with pytest.raises(ValueError): + w.colax = [1, 2, 3] + def test_colnum(): - w = _wcs._Wcsprm() + w = _wcs.Wcsprm() assert w.colnum == 0 w.colnum = 42 assert w.colnum == 42 + with pytest.raises(OverflowError): + w.colnum = 0xFFFFFFFFFFFFFFFFFFFF + + with pytest.raises(OverflowError): + w.colnum = 0xFFFFFFFF + + with pytest.raises(TypeError): + del w.colnum + -@raises(TypeError) def test_colnum_invalid(): - w = _wcs._Wcsprm() - w.colnum = 'foo' + w = _wcs.Wcsprm() + with pytest.raises(TypeError): + w.colnum = "foo" def test_crder(): - w = _wcs._Wcsprm() - assert w.crder.dtype == np.float + w = _wcs.Wcsprm() + assert w.crder.dtype == float assert np.all(np.isnan(w.crder)) w.crder[0] = 0 assert np.isnan(w.crder[1]) assert w.crder[0] == 0 + w.crder = w.crder def test_crota(): - w = _wcs._Wcsprm() + w = _wcs.Wcsprm() w.crota = [1, 0] - assert w.crota.dtype == np.float - assert w.has_crota() == True + assert w.crota.dtype == float + assert w.has_crota() is True assert_array_equal(w.crota, [1, 0]) del w.crota - assert w.has_crota() == False + assert w.has_crota() is False -@raises(AttributeError) def test_crota_missing(): - w = _wcs._Wcsprm() - assert w.has_crota() == False - w.crota + w = _wcs.Wcsprm() + assert w.has_crota() is False + with pytest.raises(AttributeError): + w.crota -@raises(AttributeError) def test_crota_missing2(): - w = _wcs._Wcsprm() + w = _wcs.Wcsprm() w.crota = [1, 0] - assert w.has_crota() == True + assert w.has_crota() is True del w.crota - assert w.has_crota() == False - w.crota + assert w.has_crota() is False + with pytest.raises(AttributeError): + w.crota def test_crpix(): - w = _wcs._Wcsprm() - assert w.crpix.dtype == np.float + w = _wcs.Wcsprm() + assert w.crpix.dtype == float assert_array_equal(w.crpix, [0, 0]) w.crpix = [42, 54] assert_array_equal(w.crpix, [42, 54]) w.crpix[0] = 0 assert_array_equal(w.crpix, [0, 54]) + with pytest.raises(ValueError): + w.crpix = [1, 2, 3] + def test_crval(): - w = _wcs._Wcsprm() - assert w.crval.dtype == np.float + w = _wcs.Wcsprm() + assert w.crval.dtype == float assert_array_equal(w.crval, [0, 0]) w.crval = [42, 54] assert_array_equal(w.crval, [42, 54]) @@ -203,173 +229,321 @@ def test_crval(): def test_csyer(): - w = _wcs._Wcsprm() - assert w.crder.dtype == np.float - assert np.all(np.isnan(w.crder)) - w.crder[0] = 0 - assert np.isnan(w.crder[1]) - assert w.crder[0] == 0 + w = _wcs.Wcsprm() + assert w.csyer.dtype == float + assert np.all(np.isnan(w.csyer)) + w.csyer[0] = 0 + assert np.isnan(w.csyer[1]) + assert w.csyer[0] == 0 + w.csyer = w.csyer def test_ctype(): - w = _wcs._Wcsprm() - assert list(w.ctype) == [b(''), b('')] - w.ctype = [b('RA---TAN'), b('DEC--TAN')] + w = _wcs.Wcsprm() + assert list(w.ctype) == ["", ""] + w.ctype = [b"RA---TAN", "DEC--TAN"] assert_array_equal(w.axis_types, [2200, 2201]) assert w.lat == 1 assert w.lng == 0 - assert w.lattyp == b('DEC') - assert w.lngtyp == b('RA') - assert list(w.ctype) == [b('RA---TAN'), b('DEC--TAN')] - w.ctype = [b('foo'), b('bar')] + assert w.lattyp == "DEC" + assert w.lngtyp == "RA" + assert list(w.ctype) == ["RA---TAN", "DEC--TAN"] + w.ctype = ["foo", "bar"] assert_array_equal(w.axis_types, [0, 0]) - assert list(w.ctype) == [b('foo'), b('bar')] + assert list(w.ctype) == ["foo", "bar"] assert w.lat == -1 assert w.lng == -1 - assert w.lattyp == b('DEC') - assert w.lngtyp == b('RA') + assert w.lattyp == "DEC" + assert w.lngtyp == "RA" + + +def test_ctype_repr(): + w = _wcs.Wcsprm() + assert list(w.ctype) == ["", ""] + w.ctype = [b"RA-\t--TAN", "DEC-\n-TAN"] + assert repr(w.ctype == '["RA-\t--TAN", "DEC-\n-TAN"]') + + +def test_ctype_index_error(): + w = _wcs.Wcsprm() + assert list(w.ctype) == ["", ""] + for idx in (2, -3): + with pytest.raises(IndexError): + w.ctype[idx] + with pytest.raises(IndexError): + w.ctype[idx] = "FOO" + + +def test_ctype_invalid_error(): + w = _wcs.Wcsprm() + assert list(w.ctype) == ["", ""] + with pytest.raises(ValueError): + w.ctype[0] = "X" * 100 + with pytest.raises(TypeError): + w.ctype[0] = True + with pytest.raises(TypeError): + w.ctype = ["a", 0] + with pytest.raises(TypeError): + w.ctype = None + with pytest.raises(ValueError): + w.ctype = ["a", "b", "c"] + with pytest.raises(ValueError): + w.ctype = ["FOO", "A" * 100] def test_cubeface(): - w = _wcs._Wcsprm() + w = _wcs.Wcsprm() assert w.cubeface == -1 + w.cubeface = 0 + with pytest.raises(OverflowError): + w.cubeface = -1 def test_cunit(): - w = _wcs._Wcsprm() - assert list(w.cunit) == [b(''), b('')] - w.cunit = [b('m'), b('km')] + w = _wcs.Wcsprm() + assert list(w.cunit) == [u.Unit(""), u.Unit("")] + w.cunit = [u.m, "km"] + assert w.cunit[0] == u.m + assert w.cunit[1] == u.km -@raises(ValueError) def test_cunit_invalid(): - w = _wcs._Wcsprm() - w.cunit[0] = b('foo') + w = _wcs.Wcsprm() + with pytest.warns(u.UnitsWarning, match="foo") as warns: + w.cunit[0] = "foo" + assert len(warns) == 1 -@raises(ValueError) def test_cunit_invalid2(): - w = _wcs._Wcsprm() - w.cunit = [b('foo'), b('bar')] + w = _wcs.Wcsprm() + with pytest.warns(u.UnitsWarning) as warns: + w.cunit = ["foo", "bar"] + assert len(warns) == 2 + assert "foo" in str(warns[0].message) + assert "bar" in str(warns[1].message) + + +def test_unit(): + w = wcs.WCS() + w.wcs.cunit[0] = u.erg + assert w.wcs.cunit[0] == u.erg + + assert repr(w.wcs.cunit) == "['erg', '']" + + +def test_unit2(): + w = wcs.WCS() + with pytest.warns(UnitsWarning): + myunit = u.Unit("FOOBAR", parse_strict="warn") + w.wcs.cunit[0] = myunit + + +def test_unit3(): + w = wcs.WCS() + for idx in (2, -3): + with pytest.raises(IndexError): + w.wcs.cunit[idx] + with pytest.raises(IndexError): + w.wcs.cunit[idx] = u.m + with pytest.raises(ValueError): + w.wcs.cunit = [u.m, u.m, u.m] + + +def test_unitfix(): + w = _wcs.Wcsprm() + w.unitfix() def test_cylfix(): # TODO: We need some data with broken cylindrical projections to # test with. For now, this is just a smoke test. - w = _wcs._Wcsprm() + w = _wcs.Wcsprm() assert w.cylfix() == -1 + assert w.cylfix([0, 1]) == -1 + + with pytest.raises(ValueError): + w.cylfix([0, 1, 2]) + def test_dateavg(): - w = _wcs._Wcsprm() - assert w.dateavg == b('') + w = _wcs.Wcsprm() + assert w.dateavg == "" # TODO: When dateavg is verified, check that it works def test_dateobs(): - w = _wcs._Wcsprm() - assert w.dateobs == b('') + w = _wcs.Wcsprm() + assert w.dateobs == "" # TODO: When dateavg is verified, check that it works def test_datfix(): - w = _wcs._Wcsprm() - w.dateobs = b('31/12/99') + w = _wcs.Wcsprm() + w.dateobs = "31/12/99" assert w.datfix() == 0 - assert w.dateobs == b('1999-12-31') + assert w.dateobs == "1999-12-31" assert w.mjdobs == 51543.0 def test_equinox(): - w = _wcs._Wcsprm() + w = _wcs.Wcsprm() assert np.isnan(w.equinox) w.equinox = 0 assert w.equinox == 0 del w.equinox assert np.isnan(w.equinox) + with pytest.raises(TypeError): + w.equinox = None + def test_fix(): - w = _wcs._Wcsprm() - assert w.fix() == { - 'cdfix': 'No change', - 'cylfix': 'No change', - 'datfix': 'No change', - 'spcfix': 'No change', - 'unitfix': 'No change', - 'celfix': 'No change'} + w = _wcs.Wcsprm() + fix_ref = { + "cdfix": "No change", + "cylfix": "No change", + "obsfix": "No change", + "datfix": "No change", + "spcfix": "No change", + "unitfix": "No change", + "celfix": "No change", + } + + version = _wcs.WCSLIB_VERSION + if Version(version) <= Version("5"): + del fix_ref["obsfix"] + + if Version(version) >= Version("7.1"): + w.dateref = "1858-11-17" + + if Version("7.4") <= Version(version) < Version("7.6"): + fix_ref["datfix"] = "Success" + + assert w.fix() == fix_ref def test_fix2(): - w = _wcs._Wcsprm() - w.dateobs = b('31/12/99') - assert w.fix() == { - 'cdfix': 'No change', - 'cylfix': 'No change', - 'datfix': "Changed '31/12/99' to '1999-12-31'", - 'spcfix': 'No change', - 'unitfix': 'No change', - 'celfix': 'No change'} - assert w.dateobs == b('1999-12-31') + w = _wcs.Wcsprm() + w.dateobs = "31/12/99" + fix_ref = { + "cdfix": "No change", + "cylfix": "No change", + "obsfix": "No change", + "datfix": ( + "Set MJD-OBS to 51543.000000 from DATE-OBS.\n" + "Changed DATE-OBS from '31/12/99' to '1999-12-31'" + ), + "spcfix": "No change", + "unitfix": "No change", + "celfix": "No change", + } + version = _wcs.WCSLIB_VERSION + if Version(version) <= Version("5"): + del fix_ref["obsfix"] + fix_ref["datfix"] = "Changed '31/12/99' to '1999-12-31'" + + if Version(version) >= Version("7.3"): + fix_ref["datfix"] = ( + "Set DATEREF to '1858-11-17' from MJDREF.\n" + fix_ref["datfix"] + ) + + elif Version(version) >= Version("7.1"): + fix_ref["datfix"] = ( + "Set DATE-REF to '1858-11-17' from MJD-REF.\n" + fix_ref["datfix"] + ) + + assert w.fix() == fix_ref + assert w.dateobs == "1999-12-31" assert w.mjdobs == 51543.0 def test_fix3(): - w = _wcs._Wcsprm() - w.dateobs = b('31/12/F9') - assert w.fix() == { - 'cdfix': 'No change', - 'cylfix': 'No change', - 'datfix': "Invalid parameter value: invalid date '31/12/F9'", - 'spcfix': 'No change', - 'unitfix': 'No change', - 'celfix': 'No change'} - assert w.dateobs == b('31/12/F9') + w = _wcs.Wcsprm() + w.dateobs = "31/12/F9" + fix_ref = { + "cdfix": "No change", + "cylfix": "No change", + "obsfix": "No change", + "datfix": "Invalid DATE-OBS format '31/12/F9'", + "spcfix": "No change", + "unitfix": "No change", + "celfix": "No change", + } + + version = _wcs.WCSLIB_VERSION + if Version(version) <= Version("5"): + del fix_ref["obsfix"] + fix_ref["datfix"] = "Invalid parameter value: invalid date '31/12/F9'" + + if Version(version) >= Version("7.3"): + fix_ref["datfix"] = ( + "Set DATEREF to '1858-11-17' from MJDREF.\n" + fix_ref["datfix"] + ) + elif Version(version) >= Version("7.1"): + fix_ref["datfix"] = ( + "Set DATE-REF to '1858-11-17' from MJD-REF.\n" + fix_ref["datfix"] + ) + + assert w.fix() == fix_ref + assert w.dateobs == "31/12/F9" assert np.isnan(w.mjdobs) +def test_fix4(): + w = _wcs.Wcsprm() + with pytest.raises(ValueError): + w.fix("X") + + +def test_fix5(): + w = _wcs.Wcsprm() + with pytest.raises(ValueError): + w.fix(naxis=[0, 1, 2]) + + def test_get_ps(): # TODO: We need some data with PSi_ma keywords - w = _wcs._Wcsprm() + w = _wcs.Wcsprm() assert len(w.get_ps()) == 0 def test_get_pv(): # TODO: We need some data with PVi_ma keywords - w = _wcs._Wcsprm() + w = _wcs.Wcsprm() assert len(w.get_pv()) == 0 -@raises(AssertionError) def test_imgpix_matrix(): - w = _wcs._Wcsprm() - w.imgpix_matrix + w = _wcs.Wcsprm() + with pytest.raises(AssertionError): + w.imgpix_matrix -@raises(AttributeError) -def test_imgpix_matrix(): - w = _wcs._Wcsprm() - w.imgpix_matrix = None +def test_imgpix_matrix2(): + w = _wcs.Wcsprm() + with pytest.raises(AttributeError): + w.imgpix_matrix = None def test_isunity(): - w = _wcs._Wcsprm() - assert(w.is_unity()) + w = _wcs.Wcsprm() + assert w.is_unity() def test_lat(): - w = _wcs._Wcsprm() + w = _wcs.Wcsprm() assert w.lat == -1 -@raises(AttributeError) def test_lat_set(): - w = _wcs._Wcsprm() - w.lat = 0 + w = _wcs.Wcsprm() + with pytest.raises(AttributeError): + w.lat = 0 def test_latpole(): - w = _wcs._Wcsprm() + w = _wcs.Wcsprm() assert w.latpole == 90.0 w.latpole = 45.0 assert w.latpole == 45.0 @@ -378,41 +552,40 @@ def test_latpole(): def test_lattyp(): - w = _wcs._Wcsprm() - print(repr(w.lattyp)) - assert w.lattyp == b(" ") + w = _wcs.Wcsprm() + assert w.lattyp == " " -@raises(AttributeError) def test_lattyp_set(): - w = _wcs._Wcsprm() - w.lattyp = 0 + w = _wcs.Wcsprm() + with pytest.raises(AttributeError): + w.lattyp = 0 def test_lng(): - w = _wcs._Wcsprm() + w = _wcs.Wcsprm() assert w.lng == -1 -@raises(AttributeError) def test_lng_set(): - w = _wcs._Wcsprm() - w.lng = 0 + w = _wcs.Wcsprm() + with pytest.raises(AttributeError): + w.lng = 0 def test_lngtyp(): - w = _wcs._Wcsprm() - assert w.lngtyp == b(" ") + w = _wcs.Wcsprm() + assert w.lngtyp == " " -@raises(AttributeError) def test_lngtyp_set(): - w = _wcs._Wcsprm() - w.lngtyp = 0 + w = _wcs.Wcsprm() + with pytest.raises(AttributeError): + w.lngtyp = 0 def test_lonpole(): - w = _wcs._Wcsprm() + w = _wcs.Wcsprm() assert np.isnan(w.lonpole) w.lonpole = 45.0 assert w.lonpole == 45.0 @@ -420,8 +593,15 @@ def test_lonpole(): assert np.isnan(w.lonpole) +def test_mix(): + w = _wcs.Wcsprm() + w.ctype = [b"RA---TAN", "DEC--TAN"] + with pytest.raises(_wcs.InvalidCoordinateError): + w.mix(1, 1, [240, 480], 1, 5, [0, 2], [54, 32], 1) + + def test_mjdavg(): - w = _wcs._Wcsprm() + w = _wcs.Wcsprm() assert np.isnan(w.mjdavg) w.mjdavg = 45.0 assert w.mjdavg == 45.0 @@ -430,7 +610,7 @@ def test_mjdavg(): def test_mjdobs(): - w = _wcs._Wcsprm() + w = _wcs.Wcsprm() assert np.isnan(w.mjdobs) w.mjdobs = 45.0 assert w.mjdobs == 45.0 @@ -439,34 +619,34 @@ def test_mjdobs(): def test_name(): - w = _wcs._Wcsprm() - assert w.name == b('') - w.name = b('foo') - assert w.name == b('foo') + w = _wcs.Wcsprm() + assert w.name == "" + w.name = "foo" + assert w.name == "foo" def test_naxis(): - w = _wcs._Wcsprm() + w = _wcs.Wcsprm() assert w.naxis == 2 -@raises(AttributeError) def test_naxis_set(): - w = _wcs._Wcsprm() - w.naxis = 4 + w = _wcs.Wcsprm() + with pytest.raises(AttributeError): + w.naxis = 4 def test_obsgeo(): - w = _wcs._Wcsprm() + w = _wcs.Wcsprm() assert np.all(np.isnan(w.obsgeo)) - w.obsgeo = [1, 2, 3] - assert_array_equal(w.obsgeo, [1, 2, 3]) + w.obsgeo = [1, 2, 3, 4, 5, 6] + assert_array_equal(w.obsgeo, [1, 2, 3, 4, 5, 6]) del w.obsgeo assert np.all(np.isnan(w.obsgeo)) def test_pc(): - w = _wcs._Wcsprm() + w = _wcs.Wcsprm() assert w.has_pc() assert_array_equal(w.pc, [[1, 0], [0, 1]]) w.cd = [[1, 0], [0, 1]] @@ -474,18 +654,19 @@ def test_pc(): del w.cd assert w.has_pc() assert_array_equal(w.pc, [[1, 0], [0, 1]]) + w.pc = w.pc -@raises(AttributeError) def test_pc_missing(): - w = _wcs._Wcsprm() + w = _wcs.Wcsprm() w.cd = [[1, 0], [0, 1]] assert not w.has_pc() - w.pc + with pytest.raises(AttributeError): + w.pc def test_phi0(): - w = _wcs._Wcsprm() + w = _wcs.Wcsprm() assert np.isnan(w.phi0) w.phi0 = 42.0 assert w.phi0 == 42.0 @@ -493,123 +674,151 @@ def test_phi0(): assert np.isnan(w.phi0) -@raises(AssertionError) def test_piximg_matrix(): - w = _wcs._Wcsprm() - w.piximg_matrix + w = _wcs.Wcsprm() + with pytest.raises(AssertionError): + w.piximg_matrix -@raises(AttributeError) -def test_piximg_matrix(): - w = _wcs._Wcsprm() - w.piximg_matrix = None +def test_piximg_matrix2(): + w = _wcs.Wcsprm() + with pytest.raises(AttributeError): + w.piximg_matrix = None -def test_print_contents(): +def test_str(): # In general, this is human-consumable, so we don't care if the # content changes, just check the type - w = _wcs._Wcsprm() + w = _wcs.Wcsprm() assert isinstance(str(w), str) +def test_print_contents(capfd): + # This is both a check that print_contents() runs and outputs something, + # and also a regression test for a bug in print_contents() which caused the + # stdout buffer to not be fully flushed, which could lead to the output of + # print_contents() being truncated and pollution of subsequent + # print_contents() calls in both wcsprm and other print_contents() methods in + # astropy.wcs (e.g. Wcs.wcs.wtb[0].print_contents()) + + for _ in range(5): + w = _wcs.Wcsprm() + w.print_contents() + + captured = capfd.readouterr() + + # The details of the output don't matter too much, but check that it + # outputs the correct number of lines and contains some strings we + # expect. Before the ``print_contents()`` bug was fixed, the number of + # lines from call to call was different and truncated. + + stdout = captured.out + + assert len(stdout.splitlines()) == 210 + + assert "crval:" in stdout and "restfrq" in stdout + + def test_radesys(): - w = _wcs._Wcsprm() - assert w.radesys == b('') - w.radesys = b('foo') - assert w.radesys == b('foo') + w = _wcs.Wcsprm() + assert w.radesys == "" + w.radesys = "foo" + assert w.radesys == "foo" def test_restfrq(): - w = _wcs._Wcsprm() + w = _wcs.Wcsprm() assert w.restfrq == 0.0 w.restfrq = np.nan assert np.isnan(w.restfrq) + del w.restfrq def test_restwav(): - w = _wcs._Wcsprm() + w = _wcs.Wcsprm() assert w.restwav == 0.0 w.restwav = np.nan assert np.isnan(w.restwav) + del w.restwav def test_set_ps(): - w = _wcs._Wcsprm() + w = _wcs.Wcsprm() data = [(0, 0, "param1"), (1, 1, "param2")] w.set_ps(data) assert w.get_ps() == data def test_set_ps_realloc(): - w = _wcs._Wcsprm() + w = _wcs.Wcsprm() w.set_ps([(0, 0, "param1")] * 16) def test_set_pv(): - w = _wcs._Wcsprm() - data = [(0, 0, 42.), (1, 1, 54.)] + w = _wcs.Wcsprm() + data = [(0, 0, 42.0), (1, 1, 54.0)] w.set_pv(data) assert w.get_pv() == data def test_set_pv_realloc(): - w = _wcs._Wcsprm() - w.set_pv([(0, 0, 42.)] * 16) + w = _wcs.Wcsprm() + w.set_pv([(0, 0, 42.0)] * 16) def test_spcfix(): # TODO: We need some data with broken spectral headers here to # really test - header = get_data_contents('spectra/orion-velo-1.hdr') - w = _wcs._Wcsprm(header) + header = get_pkg_data_contents("data/spectra/orion-velo-1.hdr", encoding="binary") + w = _wcs.Wcsprm(header) assert w.spcfix() == -1 def test_spec(): - w = _wcs._Wcsprm() + w = _wcs.Wcsprm() assert w.spec == -1 -@raises(AttributeError) def test_spec_set(): - w = _wcs._Wcsprm() - w.spec = 0 + w = _wcs.Wcsprm() + with pytest.raises(AttributeError): + w.spec = 0 def test_specsys(): - w = _wcs._Wcsprm() - assert w.specsys == b('') - w.specsys = b('foo') - assert w.specsys == b('foo') + w = _wcs.Wcsprm() + assert w.specsys == "" + w.specsys = "foo" + assert w.specsys == "foo" def test_sptr(): - #TODO: Write me + # TODO: Write me pass def test_ssysobs(): - w = _wcs._Wcsprm() - assert w.ssysobs == b('') - w.ssysobs = b('foo') - assert w.ssysobs == b('foo') + w = _wcs.Wcsprm() + assert w.ssysobs == "" + w.ssysobs = "foo" + assert w.ssysobs == "foo" def test_ssyssrc(): - w = _wcs._Wcsprm() - assert w.ssyssrc == b('') - w.ssyssrc = b('foo') - assert w.ssyssrc == b('foo') + w = _wcs.Wcsprm() + assert w.ssyssrc == "" + w.ssyssrc = "foo" + assert w.ssyssrc == "foo" def test_tab(): - w = _wcs._Wcsprm() + w = _wcs.Wcsprm() assert len(w.tab) == 0 # TODO: Inject some headers that have tables and test def test_theta0(): - w = _wcs._Wcsprm() + w = _wcs.Wcsprm() assert np.isnan(w.theta0) w.theta0 = 42.0 assert w.theta0 == 42.0 @@ -618,13 +827,13 @@ def test_theta0(): def test_toheader(): - w = _wcs._Wcsprm() + w = _wcs.Wcsprm() assert isinstance(w.to_header(), str) def test_velangl(): - w = _wcs._Wcsprm() - assert w.velangl == 0.0 + w = _wcs.Wcsprm() + assert np.isnan(w.velangl) w.velangl = 42.0 assert w.velangl == 42.0 del w.velangl @@ -632,7 +841,7 @@ def test_velangl(): def test_velosys(): - w = _wcs._Wcsprm() + w = _wcs.Wcsprm() assert np.isnan(w.velosys) w.velosys = 42.0 assert w.velosys == 42.0 @@ -640,8 +849,17 @@ def test_velosys(): assert np.isnan(w.velosys) +def test_velref(): + w = _wcs.Wcsprm() + assert w.velref == 0.0 + w.velref = 42 + assert w.velref == 42.0 + del w.velref + assert w.velref == 0.0 + + def test_zsource(): - w = _wcs._Wcsprm() + w = _wcs.Wcsprm() assert np.isnan(w.zsource) w.zsource = 42.0 assert w.zsource == 42.0 @@ -650,16 +868,16 @@ def test_zsource(): def test_cd_3d(): - header = get_data_contents('data/3d_cd.hdr') - w = _wcs._Wcsprm(header) + header = get_pkg_data_contents("data/3d_cd.hdr", encoding="binary") + w = _wcs.Wcsprm(header) assert w.cd.shape == (3, 3) assert w.get_pc().shape == (3, 3) assert w.get_cdelt().shape == (3,) def test_get_pc(): - header = get_data_contents('data/3d_cd.hdr') - w = _wcs._Wcsprm(header) + header = get_pkg_data_contents("data/3d_cd.hdr", encoding="binary") + w = _wcs.Wcsprm(header) pc = w.get_pc() try: pc[0, 0] = 42 @@ -668,32 +886,364 @@ def test_get_pc(): else: raise AssertionError() -@raises(_wcs.SingularMatrixError) + def test_detailed_err(): - w = _wcs._Wcsprm() + w = _wcs.Wcsprm() w.pc = [[0, 0], [0, 0]] - w.set() + with pytest.raises(_wcs.SingularMatrixError): + w.set() def test_header_parse(): from astropy.io import fits - hdulist = fits.open(get_data_fileobj('data/header_newlines.fits')) - w = wcs.WCS(hdulist[0].header) - assert w.wcs.ctype[0] == b'RA---TAN-SIP' + with get_pkg_data_fileobj( + "data/header_newlines.fits", encoding="binary" + ) as test_file: + hdulist = fits.open(test_file) + with pytest.warns(FITSFixedWarning): + w = wcs.WCS(hdulist[0].header) + assert w.wcs.ctype[0] == "RA---TAN-SIP" -def test_locale(): - orig_locale = locale.getlocale(locale.LC_NUMERIC)[0] +def test_locale(): try: - locale.setlocale(locale.LC_NUMERIC, 'fr_FR') - except: + with _set_locale("fr_FR"): + header = get_pkg_data_contents("data/locale.hdr", encoding="binary") + with pytest.warns(FITSFixedWarning): + w = _wcs.Wcsprm(header) + assert re.search("[0-9]+,[0-9]*", w.to_header()) is None + except locale.Error: pytest.xfail( "Can't set to 'fr_FR' locale, perhaps because it is not installed " - "on this system") - try: - header = get_data_contents('data/locale.hdr') - w = _wcs._Wcsprm(header) - assert re.search("[0-9]+,[0-9]*", w.to_header()) is None - finally: - locale.setlocale(locale.LC_NUMERIC, orig_locale) + "on this system" + ) + + +def test_unicode(): + w = _wcs.Wcsprm() + with pytest.raises(UnicodeEncodeError): + w.alt = "‰" + + +def test_sub_segfault(): + """Issue #1960""" + header = fits.Header.fromtextfile(get_pkg_data_filename("data/sub-segfault.hdr")) + w = wcs.WCS(header) + w.sub([wcs.WCSSUB_CELESTIAL]) + gc.collect() + + +def test_bounds_check(): + w = _wcs.Wcsprm() + w.bounds_check(False) + + +def test_wcs_sub_error_message(): + """Issue #1587""" + w = _wcs.Wcsprm() + with pytest.raises(TypeError, match="axes must None, a sequence or an integer$"): + w.sub("latitude") + + +def test_wcs_sub(): + """Issue #3356""" + w = _wcs.Wcsprm() + w.sub(["latitude"]) + + w = _wcs.Wcsprm() + w.sub([b"latitude"]) + + +def test_compare(): + header = get_pkg_data_contents("data/3d_cd.hdr", encoding="binary") + w = _wcs.Wcsprm(header) + w2 = _wcs.Wcsprm(header) + + assert w == w2 + + w.equinox = 42 + assert w == w2 + + assert not w.compare(w2) + assert w.compare(w2, _wcs.WCSCOMPARE_ANCILLARY) + + w = _wcs.Wcsprm(header) + w2 = _wcs.Wcsprm(header) + + with pytest.warns(RuntimeWarning): + w.cdelt[0] = np.float32(0.00416666666666666666666666) + w2.cdelt[0] = np.float64(0.00416666666666666666666666) + + assert not w.compare(w2) + assert w.compare(w2, tolerance=1e-6) + + +def test_radesys_defaults(): + w = _wcs.Wcsprm() + w.ctype = ["RA---TAN", "DEC--TAN"] + w.set() + assert w.radesys == "ICRS" + + +def test_radesys_defaults_full(): + # As described in Section 3.1 of the FITS standard "Equatorial and ecliptic + # coordinates", for those systems the RADESYS keyword can be used to + # indicate the equatorial/ecliptic frame to use. From the standard: + + # "For RADESYSa values of FK4 and FK4-NO-E, any stated equinox is Besselian + # and, if neither EQUINOXa nor EPOCH are given, a default of 1950.0 is to + # be taken. For FK5, any stated equinox is Julian and, if neither keyword + # is given, it defaults to 2000.0. + + # "If the EQUINOXa keyword is given it should always be accompanied by + # RADESYS a. However, if it should happen to ap- pear by itself then + # RADESYSa defaults to FK4 if EQUINOXa < 1984.0, or to FK5 if EQUINOXa + # 1984.0. Note that these defaults, while probably true of older files + # using the EPOCH keyword, are not required of them. + + # By default RADESYS is empty + w = _wcs.Wcsprm(naxis=2) + assert w.radesys == "" + assert np.isnan(w.equinox) + + # For non-ecliptic or equatorial systems it is still empty + w = _wcs.Wcsprm(naxis=2) + for ctype in [("GLON-CAR", "GLAT-CAR"), ("SLON-SIN", "SLAT-SIN")]: + w.ctype = ctype + w.set() + assert w.radesys == "" + assert np.isnan(w.equinox) + + for ctype in [ + ("RA---TAN", "DEC--TAN"), + ("ELON-TAN", "ELAT-TAN"), + ("DEC--TAN", "RA---TAN"), + ("ELAT-TAN", "ELON-TAN"), + ]: + # Check defaults for RADESYS + w = _wcs.Wcsprm(naxis=2) + w.ctype = ctype + w.set() + assert w.radesys == "ICRS" + + w = _wcs.Wcsprm(naxis=2) + w.ctype = ctype + w.equinox = 1980 + w.set() + assert w.radesys == "FK4" + + w = _wcs.Wcsprm(naxis=2) + w.ctype = ctype + w.equinox = 1984 + w.set() + assert w.radesys == "FK5" + + w = _wcs.Wcsprm(naxis=2) + w.ctype = ctype + w.radesys = "foo" + w.set() + assert w.radesys == "foo" + + # Check defaults for EQUINOX + w = _wcs.Wcsprm(naxis=2) + w.ctype = ctype + w.set() + assert np.isnan(w.equinox) # frame is ICRS, no equinox + + w = _wcs.Wcsprm(naxis=2) + w.ctype = ctype + w.radesys = "ICRS" + w.set() + assert np.isnan(w.equinox) + + w = _wcs.Wcsprm(naxis=2) + w.ctype = ctype + w.radesys = "FK5" + w.set() + assert w.equinox == 2000.0 + + w = _wcs.Wcsprm(naxis=2) + w.ctype = ctype + w.radesys = "FK4" + w.set() + assert w.equinox == 1950 + + w = _wcs.Wcsprm(naxis=2) + w.ctype = ctype + w.radesys = "FK4-NO-E" + w.set() + assert w.equinox == 1950 + + +def test_iteration(): + world = np.array( + [ + [-0.58995335, -0.5], + [0.00664326, -0.5], + [-0.58995335, -0.25], + [0.00664326, -0.25], + [-0.58995335, 0.0], + [0.00664326, 0.0], + [-0.58995335, 0.25], + [0.00664326, 0.25], + [-0.58995335, 0.5], + [0.00664326, 0.5], + ], + float, + ) + + w = wcs.WCS() + w.wcs.ctype = ["GLON-CAR", "GLAT-CAR"] + w.wcs.cdelt = [-0.006666666828, 0.006666666828] + w.wcs.crpix = [75.907, 74.8485] + x = w.wcs_world2pix(world, 1) + + expected = np.array( + [ + [1.64400000e02, -1.51498185e-01], + [7.49105110e01, -1.51498185e-01], + [1.64400000e02, 3.73485009e01], + [7.49105110e01, 3.73485009e01], + [1.64400000e02, 7.48485000e01], + [7.49105110e01, 7.48485000e01], + [1.64400000e02, 1.12348499e02], + [7.49105110e01, 1.12348499e02], + [1.64400000e02, 1.49848498e02], + [7.49105110e01, 1.49848498e02], + ], + float, + ) + + assert_array_almost_equal(x, expected) + + w2 = w.wcs_pix2world(x, 1) + + world[:, 0] %= 360.0 + + assert_array_almost_equal(w2, world) + + +def test_invalid_args(): + with pytest.raises(TypeError): + _wcs.Wcsprm(keysel="A") + + with pytest.raises(ValueError): + _wcs.Wcsprm(keysel=2) + + with pytest.raises(ValueError): + _wcs.Wcsprm(colsel=2) + + with pytest.raises(ValueError): + _wcs.Wcsprm(naxis=64) + + header = get_pkg_data_contents("data/spectra/orion-velo-1.hdr", encoding="binary") + with pytest.raises(ValueError): + _wcs.Wcsprm(header, relax="FOO") + + with pytest.raises(ValueError): + _wcs.Wcsprm(header, naxis=3) + + with pytest.raises(KeyError): + _wcs.Wcsprm(header, key="A") + + +# Test keywords in the Time standard + + +def test_datebeg(): + w = _wcs.Wcsprm() + assert w.datebeg == "" + w.datebeg = "2001-02-11" + assert w.datebeg == "2001-02-11" + w.datebeg = "31/12/99" + fix_ref = { + "cdfix": "No change", + "cylfix": "No change", + "obsfix": "No change", + "datfix": "Invalid DATE-BEG format '31/12/99'", + "spcfix": "No change", + "unitfix": "No change", + "celfix": "No change", + } + + if Version(_wcs.WCSLIB_VERSION) >= Version("7.3"): + fix_ref["datfix"] = ( + "Set DATEREF to '1858-11-17' from MJDREF.\n" + fix_ref["datfix"] + ) + elif Version(_wcs.WCSLIB_VERSION) >= Version("7.1"): + fix_ref["datfix"] = ( + "Set DATE-REF to '1858-11-17' from MJD-REF.\n" + fix_ref["datfix"] + ) + + assert w.fix() == fix_ref + + +char_keys = [ + "timesys", + "trefpos", + "trefdir", + "plephem", + "timeunit", + "dateref", + "dateavg", + "dateend", +] + + +@pytest.mark.parametrize("key", char_keys) +def test_char_keys(key): + w = _wcs.Wcsprm() + assert getattr(w, key) == "" + setattr(w, key, "foo") + assert getattr(w, key) == "foo" + with pytest.raises(TypeError): + setattr(w, key, 42) + + +num_keys = [ + "mjdobs", + "mjdbeg", + "mjdend", + "jepoch", + "bepoch", + "tstart", + "tstop", + "xposure", + "timsyer", + "timrder", + "timedel", + "timepixr", + "timeoffs", + "telapse", + "xposure", +] + + +@pytest.mark.parametrize("key", num_keys) +def test_num_keys(key): + w = _wcs.Wcsprm() + assert np.isnan(getattr(w, key)) + setattr(w, key, 42.0) + assert getattr(w, key) == 42.0 + delattr(w, key) + assert np.isnan(getattr(w, key)) + with pytest.raises(TypeError): + setattr(w, key, "foo") + + +@pytest.mark.parametrize("key", ["czphs", "cperi", "mjdref"]) +def test_array_keys(key): + w = _wcs.Wcsprm() + attr = getattr(w, key) + if key == "mjdref" and Version(_wcs.WCSLIB_VERSION) >= Version("7.1"): + assert np.allclose(attr, [0, 0]) + else: + assert np.all(np.isnan(attr)) + assert attr.dtype == float + setattr(w, key, [1.0, 2.0]) + assert_array_equal(getattr(w, key), [1.0, 2.0]) + with pytest.raises(ValueError): + setattr(w, key, ["foo", "bar"]) + with pytest.raises(ValueError): + setattr(w, key, "foo") diff --git a/astropy/wcs/tests/test_wtbarr.py b/astropy/wcs/tests/test_wtbarr.py new file mode 100644 index 000000000000..cb97a21cdc98 --- /dev/null +++ b/astropy/wcs/tests/test_wtbarr.py @@ -0,0 +1,58 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + + +def test_wtbarr_i(tab_wcs_2di): + assert tab_wcs_2di.wcs.wtb[0].i == 1 + + +def test_wtbarr_m(tab_wcs_2di): + assert tab_wcs_2di.wcs.wtb[0].m == 1 + + +def test_wtbarr_kind(tab_wcs_2di): + assert tab_wcs_2di.wcs.wtb[0].kind == "c" + + +def test_wtbarr_extnam(tab_wcs_2di): + assert tab_wcs_2di.wcs.wtb[0].extnam == "WCS-TABLE" + + +def test_wtbarr_extver(tab_wcs_2di): + assert tab_wcs_2di.wcs.wtb[0].extver == 1 + + +def test_wtbarr_extlev(tab_wcs_2di): + assert tab_wcs_2di.wcs.wtb[0].extlev == 1 + + +def test_wtbarr_ttype(tab_wcs_2di): + assert tab_wcs_2di.wcs.wtb[0].ttype == "wavelength" + + +def test_wtbarr_row(tab_wcs_2di): + assert tab_wcs_2di.wcs.wtb[0].row == 1 + + +def test_wtbarr_ndim(tab_wcs_2di): + assert tab_wcs_2di.wcs.wtb[0].ndim == 3 + + +def test_wtbarr_print(tab_wcs_2di, capfd): + tab_wcs_2di.wcs.wtb[0].print_contents() + captured = capfd.readouterr() + s = str(tab_wcs_2di.wcs.wtb[0]) + lines = s.split("\n") + assert captured.out == s + assert lines[0] == " i: 1" + assert lines[1] == " m: 1" + assert lines[2] == " kind: c" + assert lines[3] == "extnam: WCS-TABLE" + assert lines[4] == "extver: 1" + assert lines[5] == "extlev: 1" + assert lines[6] == " ttype: wavelength" + assert lines[7] == " row: 1" + assert lines[8] == " ndim: 3" + assert lines[9].startswith("dimlen: ") + assert lines[10] == " 0: 4" + assert lines[11] == " 1: 2" + assert lines[12].startswith("arrayp: ") diff --git a/astropy/wcs/utils.py b/astropy/wcs/utils.py new file mode 100644 index 000000000000..b13071dcb60d --- /dev/null +++ b/astropy/wcs/utils.py @@ -0,0 +1,1362 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +import copy +from functools import lru_cache + +import numpy as np + +import astropy.units as u +from astropy.coordinates import ( + ITRS, + BaseBodycentricRepresentation, + BaseCoordinateFrame, + BaseGeodeticRepresentation, + CartesianRepresentation, + SphericalRepresentation, +) +from astropy.utils import unbroadcast + +from .wcs import WCS, WCSSUB_LATITUDE, WCSSUB_LONGITUDE + +__doctest_skip__ = ["wcs_to_celestial_frame", "celestial_frame_to_wcs"] + +__all__ = [ + "add_stokes_axis_to_wcs", + "celestial_frame_to_wcs", + "custom_frame_to_wcs_mappings", + "custom_wcs_to_frame_mappings", + "fit_wcs_from_points", + "is_proj_plane_distorted", + "local_partial_pixel_derivatives", + "non_celestial_pixel_scales", + "obsgeo_to_frame", + "pixel_to_pixel", + "pixel_to_skycoord", + "proj_plane_pixel_area", + "proj_plane_pixel_scales", + "skycoord_to_pixel", + "wcs_to_celestial_frame", +] + +SOLAR_SYSTEM_OBJ_DICT = { + "EA": "Earth", + "SE": "Moon", + "ME": "Mercury", + "VE": "Venus", + "MA": "Mars", + "JU": "Jupiter", + "SA": "Saturn", + "UR": "Uranus", + "NE": "Neptune", +} + + +@lru_cache(maxsize=100) +def solar_system_body_frame(object_name, representation_type): + return type( + f"{object_name}Frame", + (BaseCoordinateFrame,), + dict(name=object_name, representation_type=representation_type), + ) + + +@lru_cache(maxsize=100) +def solar_system_body_representation_type( + object_name, baserepresentation, equatorial_radius, flattening +): + return type( + f"{object_name}{baserepresentation.__name__[4:]}", + (baserepresentation,), + dict(_equatorial_radius=equatorial_radius, _flattening=flattening), + ) + + +def add_stokes_axis_to_wcs(wcs, add_before_ind): + """ + Add a new Stokes axis that is uncorrelated with any other axes. + + Parameters + ---------- + wcs : `~astropy.wcs.WCS` + The WCS to add to + add_before_ind : int + Index of the WCS to insert the new Stokes axis in front of. + To add at the end, do add_before_ind = wcs.wcs.naxis + The beginning is at position 0. + + Returns + ------- + `~astropy.wcs.WCS` + A new `~astropy.wcs.WCS` instance with an additional axis + """ + inds = [i + 1 for i in range(wcs.wcs.naxis)] + inds.insert(add_before_ind, 0) + newwcs = wcs.sub(inds) + newwcs.wcs.ctype[add_before_ind] = "STOKES" + newwcs.wcs.cname[add_before_ind] = "STOKES" + return newwcs + + +def _wcs_to_celestial_frame_builtin(wcs): + # Import astropy.coordinates here to avoid circular imports + from astropy.coordinates import ( + FK4, + FK5, + ICRS, + ITRS, + FK4NoETerms, + Galactic, + SphericalRepresentation, + ) + + # Import astropy.time here otherwise setup.py fails before extensions are compiled + from astropy.time import Time + + if wcs.wcs.lng == -1 or wcs.wcs.lat == -1: + return None + + radesys = wcs.wcs.radesys + + if np.isnan(wcs.wcs.equinox): + equinox = None + else: + equinox = wcs.wcs.equinox + + xcoord = wcs.wcs.ctype[wcs.wcs.lng][:4] + ycoord = wcs.wcs.ctype[wcs.wcs.lat][:4] + + # Apply logic from FITS standard to determine the default radesys + if radesys == "" and xcoord == "RA--" and ycoord == "DEC-": + if equinox is None: + radesys = "ICRS" + elif equinox < 1984.0: + radesys = "FK4" + else: + radesys = "FK5" + + if radesys == "FK4": + if equinox is not None: + equinox = Time(equinox, format="byear") + frame = FK4(equinox=equinox) + elif radesys == "FK4-NO-E": + if equinox is not None: + equinox = Time(equinox, format="byear") + frame = FK4NoETerms(equinox=equinox) + elif radesys == "FK5": + if equinox is not None: + equinox = Time(equinox, format="jyear") + frame = FK5(equinox=equinox) + elif radesys == "ICRS": + frame = ICRS() + else: + if xcoord == "GLON" and ycoord == "GLAT": + frame = Galactic() + elif xcoord == "TLON" and ycoord == "TLAT": + # The default representation for ITRS is cartesian, but for WCS + # purposes, we need the spherical representation. + frame = ITRS( + representation_type=SphericalRepresentation, + obstime=wcs.wcs.dateobs or None, + ) + elif xcoord[2:4] in ("LN", "LT") and xcoord[:2] in SOLAR_SYSTEM_OBJ_DICT.keys(): + # Coordinates on a planetary body, as defined in + # https://agupubs.onlinelibrary.wiley.com/doi/10.1029/2018EA000388 + + object_name = SOLAR_SYSTEM_OBJ_DICT.get(xcoord[:2]) + + a_radius = wcs.wcs.aux.a_radius + b_radius = wcs.wcs.aux.b_radius + c_radius = wcs.wcs.aux.c_radius + if "bodycentric" in wcs.wcs.name.lower(): + baserepresentation = BaseBodycentricRepresentation + representation_type_name = "BodycentricRepresentation" + else: + baserepresentation = BaseGeodeticRepresentation + representation_type_name = "GeodeticRepresentation" + + if a_radius == b_radius: + equatorial_radius = a_radius * u.m + flattening = (a_radius - c_radius) / a_radius + else: + raise NotImplementedError( + "triaxial systems are not supported at this time." + ) + + # create a new representation class + representation_type = solar_system_body_representation_type( + SOLAR_SYSTEM_OBJ_DICT.get(xcoord[:2]), + baserepresentation, + equatorial_radius, + flattening, + ) + + # create a new frame class + frame = solar_system_body_frame( + SOLAR_SYSTEM_OBJ_DICT.get(xcoord[:2]), representation_type + ) + else: + frame = None + + return frame + + +def _celestial_frame_to_wcs_builtin(frame, projection="TAN"): + # Import astropy.coordinates here to avoid circular imports + from astropy.coordinates import ( + FK4, + FK5, + ICRS, + ITRS, + BaseRADecFrame, + FK4NoETerms, + Galactic, + ) + + # Create a 2-dimensional WCS + wcs = WCS(naxis=2) + + if isinstance(frame, BaseRADecFrame): + xcoord = "RA--" + ycoord = "DEC-" + if isinstance(frame, ICRS): + wcs.wcs.radesys = "ICRS" + elif isinstance(frame, FK4NoETerms): + wcs.wcs.radesys = "FK4-NO-E" + wcs.wcs.equinox = frame.equinox.byear + elif isinstance(frame, FK4): + wcs.wcs.radesys = "FK4" + wcs.wcs.equinox = frame.equinox.byear + elif isinstance(frame, FK5): + wcs.wcs.radesys = "FK5" + wcs.wcs.equinox = frame.equinox.jyear + else: + return None + elif isinstance(frame, Galactic): + xcoord = "GLON" + ycoord = "GLAT" + elif isinstance(frame, ITRS): + xcoord = "TLON" + ycoord = "TLAT" + wcs.wcs.radesys = "ITRS" + wcs.wcs.dateobs = frame.obstime.utc.isot + # TODO: once we have a BaseBodyFrame, replace this with an isinstance check + elif hasattr(frame, "name") and frame.name in SOLAR_SYSTEM_OBJ_DICT.values(): + xcoord = frame.name[:2].upper().replace("MO", "SE") + "LN" + ycoord = frame.name[:2].upper().replace("MO", "SE") + "LT" + if issubclass(frame.representation_type, BaseGeodeticRepresentation): + wcs.wcs.name = "Planetographic Body-Fixed" + elif issubclass(frame.representation_type, BaseBodycentricRepresentation): + wcs.wcs.name = "Bodycentric Body-Fixed" + else: + raise ValueError( + "Planetary coordinates in WCS require a geodetic or bodycentric " + "representation, not {frame.representation_type}." + ) + wcs.wcs.aux.a_radius = frame.representation_type._equatorial_radius.value + wcs.wcs.aux.b_radius = frame.representation_type._equatorial_radius.value + wcs.wcs.aux.c_radius = wcs.wcs.aux.a_radius * ( + 1.0 + - frame.representation_type._flattening.to(u.dimensionless_unscaled).value + ) + else: + return None + + wcs.wcs.ctype = [xcoord + "-" + projection, ycoord + "-" + projection] + + return wcs + + +WCS_FRAME_MAPPINGS = [[_wcs_to_celestial_frame_builtin]] +FRAME_WCS_MAPPINGS = [[_celestial_frame_to_wcs_builtin]] + + +class custom_wcs_to_frame_mappings: + def __init__(self, mappings=[]): + if callable(mappings): + mappings = [mappings] + WCS_FRAME_MAPPINGS.append(mappings) + + def __enter__(self): + pass + + def __exit__(self, type, value, tb): + WCS_FRAME_MAPPINGS.pop() + + +# Backward-compatibility +custom_frame_mappings = custom_wcs_to_frame_mappings + + +class custom_frame_to_wcs_mappings: + def __init__(self, mappings=[]): + if callable(mappings): + mappings = [mappings] + FRAME_WCS_MAPPINGS.append(mappings) + + def __enter__(self): + pass + + def __exit__(self, type, value, tb): + FRAME_WCS_MAPPINGS.pop() + + +def wcs_to_celestial_frame(wcs): + """ + For a given WCS, return the coordinate frame that matches the celestial + component of the WCS. + + Parameters + ---------- + wcs : :class:`~astropy.wcs.WCS` instance + The WCS to find the frame for + + Returns + ------- + frame : :class:`~astropy.coordinates.BaseCoordinateFrame` subclass instance + An instance of a :class:`~astropy.coordinates.BaseCoordinateFrame` + subclass instance that best matches the specified WCS. + + Notes + ----- + To extend this function to frames not defined in astropy.coordinates, you + can write your own function which should take a :class:`~astropy.wcs.WCS` + instance and should return either an instance of a frame, or `None` if no + matching frame was found. You can register this function temporarily with:: + + >>> from astropy.wcs.utils import wcs_to_celestial_frame, custom_wcs_to_frame_mappings + >>> with custom_wcs_to_frame_mappings(my_function): + ... wcs_to_celestial_frame(...) + + """ + for mapping_set in WCS_FRAME_MAPPINGS: + for func in mapping_set: + frame = func(wcs) + if frame is not None: + return frame + raise ValueError( + "Could not determine celestial frame corresponding to the specified WCS object" + ) + + +def celestial_frame_to_wcs(frame, projection="TAN"): + """ + For a given coordinate frame, return the corresponding WCS object. + + Note that the returned WCS object has only the elements corresponding to + coordinate frames set (e.g. ctype, equinox, radesys). + + Parameters + ---------- + frame : :class:`~astropy.coordinates.BaseCoordinateFrame` subclass instance + An instance of a :class:`~astropy.coordinates.BaseCoordinateFrame` + subclass instance for which to find the WCS + projection : str + Projection code to use in ctype, if applicable + + Returns + ------- + wcs : :class:`~astropy.wcs.WCS` instance + The corresponding WCS object + + Examples + -------- + :: + + >>> from astropy.wcs.utils import celestial_frame_to_wcs + >>> from astropy.coordinates import FK5 + >>> frame = FK5(equinox='J2010') + >>> wcs = celestial_frame_to_wcs(frame) + >>> wcs.to_header() + WCSAXES = 2 / Number of coordinate axes + CRPIX1 = 0.0 / Pixel coordinate of reference point + CRPIX2 = 0.0 / Pixel coordinate of reference point + CDELT1 = 1.0 / [deg] Coordinate increment at reference point + CDELT2 = 1.0 / [deg] Coordinate increment at reference point + CUNIT1 = 'deg' / Units of coordinate increment and value + CUNIT2 = 'deg' / Units of coordinate increment and value + CTYPE1 = 'RA---TAN' / Right ascension, gnomonic projection + CTYPE2 = 'DEC--TAN' / Declination, gnomonic projection + CRVAL1 = 0.0 / [deg] Coordinate value at reference point + CRVAL2 = 0.0 / [deg] Coordinate value at reference point + LONPOLE = 180.0 / [deg] Native longitude of celestial pole + LATPOLE = 0.0 / [deg] Native latitude of celestial pole + RADESYS = 'FK5' / Equatorial coordinate system + EQUINOX = 2010.0 / [yr] Equinox of equatorial coordinates + + + Notes + ----- + To extend this function to frames not defined in astropy.coordinates, you + can write your own function which should take a + :class:`~astropy.coordinates.BaseCoordinateFrame` subclass + instance and a projection (given as a string) and should return either a WCS + instance, or `None` if the WCS could not be determined. You can register + this function temporarily with:: + + >>> from astropy.wcs.utils import celestial_frame_to_wcs, custom_frame_to_wcs_mappings + >>> with custom_frame_to_wcs_mappings(my_function): + ... celestial_frame_to_wcs(...) + + """ + for mapping_set in FRAME_WCS_MAPPINGS: + for func in mapping_set: + wcs = func(frame, projection=projection) + if wcs is not None: + return wcs + raise ValueError( + "Could not determine WCS corresponding to the specified coordinate frame." + ) + + +def proj_plane_pixel_scales(wcs): + """ + For a WCS returns pixel scales along each axis of the image pixel at + the ``CRPIX`` location once it is projected onto the + "plane of intermediate world coordinates" as defined in + `Greisen & Calabretta 2002, A&A, 395, 1061 `_. + + .. note:: + This function is concerned **only** about the transformation + "image plane"->"projection plane" and **not** about the + transformation "celestial sphere"->"projection plane"->"image plane". + Therefore, this function ignores distortions arising due to + non-linear nature of most projections. + + .. note:: + In order to compute the scales corresponding to celestial axes only, + make sure that the input `~astropy.wcs.WCS` object contains + celestial axes only, e.g., by passing in the + `~astropy.wcs.WCS.celestial` WCS object. + + Parameters + ---------- + wcs : `~astropy.wcs.WCS` + A world coordinate system object. + + Returns + ------- + scale : ndarray + A vector (`~numpy.ndarray`) of projection plane increments + corresponding to each pixel side (axis). The units of the returned + results are the same as the units of `~astropy.wcs.Wcsprm.cdelt`, + `~astropy.wcs.Wcsprm.crval`, and `~astropy.wcs.Wcsprm.cd` for + the celestial WCS and can be obtained by inquiring the value + of `~astropy.wcs.Wcsprm.cunit` property of the input + `~astropy.wcs.WCS` WCS object. + + See Also + -------- + astropy.wcs.utils.proj_plane_pixel_area + + """ + return np.sqrt((wcs.pixel_scale_matrix**2).sum(axis=0, dtype=float)) + + +def proj_plane_pixel_area(wcs): + """ + For a **celestial** WCS (see `astropy.wcs.WCS.celestial`) returns pixel + area of the image pixel at the ``CRPIX`` location once it is projected + onto the "plane of intermediate world coordinates" as defined in + `Greisen & Calabretta 2002, A&A, 395, 1061 `_. + + .. note:: + This function is concerned **only** about the transformation + "image plane"->"projection plane" and **not** about the + transformation "celestial sphere"->"projection plane"->"image plane". + Therefore, this function ignores distortions arising due to + non-linear nature of most projections. + + .. note:: + In order to compute the area of pixels corresponding to celestial + axes only, this function uses the `~astropy.wcs.WCS.celestial` WCS + object of the input ``wcs``. This is different from the + `~astropy.wcs.utils.proj_plane_pixel_scales` function + that computes the scales for the axes of the input WCS itself. + + Parameters + ---------- + wcs : `~astropy.wcs.WCS` + A world coordinate system object. + + Returns + ------- + area : float + Area (in the projection plane) of the pixel at ``CRPIX`` location. + The units of the returned result are the same as the units of + the `~astropy.wcs.Wcsprm.cdelt`, `~astropy.wcs.Wcsprm.crval`, + and `~astropy.wcs.Wcsprm.cd` for the celestial WCS and can be + obtained by inquiring the value of `~astropy.wcs.Wcsprm.cunit` + property of the `~astropy.wcs.WCS.celestial` WCS object. + + Raises + ------ + ValueError + Pixel area is defined only for 2D pixels. Most likely the + `~astropy.wcs.Wcsprm.cd` matrix of the `~astropy.wcs.WCS.celestial` + WCS is not a square matrix of second order. + + Notes + ----- + Depending on the application, square root of the pixel area can be used to + represent a single pixel scale of an equivalent square pixel + whose area is equal to the area of a generally non-square pixel. + + See Also + -------- + astropy.wcs.utils.proj_plane_pixel_scales + + """ + psm = wcs.celestial.pixel_scale_matrix + if psm.shape != (2, 2): + raise ValueError("Pixel area is defined only for 2D pixels.") + return np.abs(np.linalg.det(psm)) + + +def is_proj_plane_distorted(wcs, maxerr=1.0e-5): + r""" + For a WCS returns `False` if square image (detector) pixels stay square + when projected onto the "plane of intermediate world coordinates" + as defined in + `Greisen & Calabretta 2002, A&A, 395, 1061 `_. + It will return `True` if transformation from image (detector) coordinates + to the focal plane coordinates is non-orthogonal or if WCS contains + non-linear (e.g., SIP) distortions. + + .. note:: + Since this function is concerned **only** about the transformation + "image plane"->"focal plane" and **not** about the transformation + "celestial sphere"->"focal plane"->"image plane", + this function ignores distortions arising due to non-linear nature + of most projections. + + Let's denote by *C* either the original or the reconstructed + (from ``PC`` and ``CDELT``) CD matrix. `is_proj_plane_distorted` + verifies that the transformation from image (detector) coordinates + to the focal plane coordinates is orthogonal using the following + check: + + .. math:: + \left \| \frac{C \cdot C^{\mathrm{T}}} + {| det(C)|} - I \right \|_{\mathrm{max}} < \epsilon . + + Parameters + ---------- + wcs : `~astropy.wcs.WCS` + World coordinate system object + + maxerr : float, optional + Accuracy to which the CD matrix, **normalized** such + that :math:`|det(CD)|=1`, should be close to being an + orthogonal matrix as described in the above equation + (see :math:`\epsilon`). + + Returns + ------- + distorted : bool + Returns `True` if focal (projection) plane is distorted and `False` + otherwise. + + """ + cwcs = wcs.celestial + return not _is_cd_orthogonal(cwcs.pixel_scale_matrix, maxerr) or _has_distortion(cwcs) # fmt: skip + + +def _is_cd_orthogonal(cd, maxerr): + shape = cd.shape + if not (len(shape) == 2 and shape[0] == shape[1]): + raise ValueError("CD (or PC) matrix must be a 2D square matrix.") + + pixarea = np.abs(np.linalg.det(cd)) + if pixarea == 0.0: + raise ValueError("CD (or PC) matrix is singular.") + + # NOTE: Technically, below we should use np.dot(cd, np.conjugate(cd.T)) + # However, I am not aware of complex CD/PC matrices... + I = np.dot(cd, cd.T) / pixarea + cd_unitary_err = np.amax(np.abs(I - np.eye(shape[0]))) + + return cd_unitary_err < maxerr + + +def non_celestial_pixel_scales(inwcs): + """ + Calculate the pixel scale along each axis of a non-celestial WCS, + for example one with mixed spectral and spatial axes. + + Parameters + ---------- + inwcs : `~astropy.wcs.WCS` + The world coordinate system object. + + Returns + ------- + scale : `numpy.ndarray` + The pixel scale along each axis. + """ + if inwcs.is_celestial: + raise ValueError("WCS is celestial, use celestial_pixel_scales instead") + + pccd = inwcs.pixel_scale_matrix + + if np.allclose(np.extract(1 - np.eye(*pccd.shape), pccd), 0): + return np.abs(np.diagonal(pccd)) * u.deg + else: + raise ValueError("WCS is rotated, cannot determine consistent pixel scales") + + +def _has_distortion(wcs): + """ + `True` if contains any SIP or image distortion components. + """ + return any( + getattr(wcs, dist_attr) is not None + for dist_attr in ["cpdis1", "cpdis2", "det2im1", "det2im2", "sip"] + ) + + +# TODO: in future, we should think about how the following two functions can be +# integrated better into the WCS class. + + +def skycoord_to_pixel(coords, wcs, origin=0, mode="all"): + """ + Convert a set of SkyCoord coordinates into pixels. + + Parameters + ---------- + coords : `~astropy.coordinates.SkyCoord` + The coordinates to convert. + wcs : `~astropy.wcs.WCS` + The WCS transformation to use. + origin : int + Whether to return 0 or 1-based pixel coordinates. + mode : 'all' or 'wcs' + Whether to do the transformation including distortions (``'all'``) or + only including only the core WCS transformation (``'wcs'``). + + Returns + ------- + xp, yp : `numpy.ndarray` + The pixel coordinates + + See Also + -------- + astropy.coordinates.SkyCoord.from_pixel + """ + if _has_distortion(wcs) and wcs.naxis != 2: + raise ValueError("Can only handle WCS with distortions for 2-dimensional WCS") + + # Keep only the celestial part of the axes, also re-orders lon/lat + wcs = wcs.sub([WCSSUB_LONGITUDE, WCSSUB_LATITUDE]) + + if wcs.naxis != 2: + raise ValueError("WCS should contain celestial component") + + # Check which frame the WCS uses + frame = wcs_to_celestial_frame(wcs) + + # Check what unit the WCS needs + xw_unit = u.Unit(wcs.wcs.cunit[0]) + yw_unit = u.Unit(wcs.wcs.cunit[1]) + + # Convert positions to frame + coords = coords.transform_to(frame) + + # Extract longitude and latitude. We first try and use lon/lat directly, + # but if the representation is not spherical or unit spherical this will + # fail. We should then force the use of the unit spherical + # representation. We don't do that directly to make sure that we preserve + # custom lon/lat representations if available. + try: + lon = coords.data.lon.to(xw_unit) + lat = coords.data.lat.to(yw_unit) + except AttributeError: + lon = coords.spherical.lon.to(xw_unit) + lat = coords.spherical.lat.to(yw_unit) + + # Convert to pixel coordinates + if mode == "all": + xp, yp = wcs.all_world2pix(lon.value, lat.value, origin) + elif mode == "wcs": + xp, yp = wcs.wcs_world2pix(lon.value, lat.value, origin) + else: + raise ValueError("mode should be either 'all' or 'wcs'") + + return xp, yp + + +def pixel_to_skycoord(xp, yp, wcs, origin=0, mode="all", cls=None): + """ + Convert a set of pixel coordinates into a `~astropy.coordinates.SkyCoord` + coordinate. + + Parameters + ---------- + xp, yp : float or ndarray + The coordinates to convert. + wcs : `~astropy.wcs.WCS` + The WCS transformation to use. + origin : int + Whether to return 0 or 1-based pixel coordinates. + mode : 'all' or 'wcs' + Whether to do the transformation including distortions (``'all'``) or + only including only the core WCS transformation (``'wcs'``). + cls : class or None + The class of object to create. Should be a + `~astropy.coordinates.SkyCoord` subclass. If None, defaults to + `~astropy.coordinates.SkyCoord`. + + Returns + ------- + coords : `~astropy.coordinates.SkyCoord` subclass + The celestial coordinates. Whatever ``cls`` type is. + + See Also + -------- + astropy.coordinates.SkyCoord.from_pixel + """ + # Import astropy.coordinates here to avoid circular imports + from astropy.coordinates import SkyCoord, UnitSphericalRepresentation + + # we have to do this instead of actually setting the default to SkyCoord + # because importing SkyCoord at the module-level leads to circular + # dependencies. + if cls is None: + cls = SkyCoord + + if _has_distortion(wcs) and wcs.naxis != 2: + raise ValueError("Can only handle WCS with distortions for 2-dimensional WCS") + + # Keep only the celestial part of the axes, also re-orders lon/lat + wcs = wcs.sub([WCSSUB_LONGITUDE, WCSSUB_LATITUDE]) + + if wcs.naxis != 2: + raise ValueError("WCS should contain celestial component") + + # Check which frame the WCS uses + frame = wcs_to_celestial_frame(wcs) + + # Check what unit the WCS gives + lon_unit = u.Unit(wcs.wcs.cunit[0]) + lat_unit = u.Unit(wcs.wcs.cunit[1]) + + # Convert pixel coordinates to celestial coordinates + if mode == "all": + lon, lat = wcs.all_pix2world(xp, yp, origin) + elif mode == "wcs": + lon, lat = wcs.wcs_pix2world(xp, yp, origin) + else: + raise ValueError("mode should be either 'all' or 'wcs'") + + # Add units to longitude/latitude + lon = lon * lon_unit + lat = lat * lat_unit + + # Create a SkyCoord-like object + data = UnitSphericalRepresentation(lon=lon, lat=lat) + coords = cls(frame.realize_frame(data)) + + return coords + + +def _unique_with_order_preserved(items): + """ + Return a list of unique items in the list provided, preserving the order + in which they are found. + """ + new_items = [] + for item in items: + if item not in new_items: + new_items.append(item) + return new_items + + +def _pixel_to_world_correlation_matrix(wcs): + """ + Return a correlation matrix between the pixel coordinates and the + high level world coordinates, along with the list of high level world + coordinate classes. + + The shape of the matrix is ``(n_world, n_pix)``, where ``n_world`` is the + number of high level world coordinates. + """ + # We basically want to collapse the world dimensions together that are + # combined into the same high-level objects. + + # Get the following in advance as getting these properties can be expensive + all_components = wcs.low_level_wcs.world_axis_object_components + all_classes = wcs.low_level_wcs.world_axis_object_classes + axis_correlation_matrix = wcs.low_level_wcs.axis_correlation_matrix + + components = _unique_with_order_preserved([c[0] for c in all_components]) + + matrix = np.zeros((len(components), wcs.pixel_n_dim), dtype=bool) + + for iworld in range(wcs.world_n_dim): + iworld_unique = components.index(all_components[iworld][0]) + matrix[iworld_unique] |= axis_correlation_matrix[iworld] + + classes = [all_classes[component][0] for component in components] + + return matrix, classes + + +def _pixel_to_pixel_correlation_matrix(wcs_in, wcs_out): + """ + Correlation matrix between the input and output pixel coordinates for a + pixel -> world -> pixel transformation specified by two WCS instances. + + The first WCS specified is the one used for the pixel -> world + transformation and the second WCS specified is the one used for the world -> + pixel transformation. The shape of the matrix is + ``(n_pixel_out, n_pixel_in)``. + """ + matrix1, classes1 = _pixel_to_world_correlation_matrix(wcs_in) + matrix2, classes2 = _pixel_to_world_correlation_matrix(wcs_out) + + if len(classes1) != len(classes2): + raise ValueError("The two WCS return a different number of world coordinates") + + # Check if classes match uniquely + unique_match = True + mapping = [] + for class1 in classes1: + matches = classes2.count(class1) + if matches == 0: + raise ValueError("The world coordinate types of the two WCS do not match") + elif matches > 1: + unique_match = False + break + else: + mapping.append(classes2.index(class1)) + + if unique_match: + # Classes are unique, so we need to re-order matrix2 along the world + # axis using the mapping we found above. + matrix2 = matrix2[mapping] + + elif classes1 != classes2: + raise ValueError( + "World coordinate order doesn't match and automatic matching is ambiguous" + ) + + matrix = np.matmul(matrix2.T, matrix1) + + return matrix + + +def _split_matrix(matrix): + """ + Given an axis correlation matrix from a WCS object, return information about + the individual WCS that can be split out. + + The output is a list of tuples, where each tuple contains a list of + pixel dimensions and a list of world dimensions that can be extracted to + form a new WCS. For example, in the case of a spectral cube with the first + two world coordinates being the celestial coordinates and the third + coordinate being an uncorrelated spectral axis, the matrix would look like:: + + array([[ True, True, False], + [ True, True, False], + [False, False, True]]) + + and this function will return ``[([0, 1], [0, 1]), ([2], [2])]``. + """ + pixel_used = [] + + split_info = [] + + for ipix in range(matrix.shape[1]): + if ipix in pixel_used: + continue + pixel_include = np.zeros(matrix.shape[1], dtype=bool) + pixel_include[ipix] = True + n_pix_prev, n_pix = 0, 1 + while n_pix > n_pix_prev: + world_include = matrix[:, pixel_include].any(axis=1) + pixel_include = matrix[world_include, :].any(axis=0) + n_pix_prev, n_pix = n_pix, np.sum(pixel_include) + pixel_indices = list(np.nonzero(pixel_include)[0]) + world_indices = list(np.nonzero(world_include)[0]) + pixel_used.extend(pixel_indices) + split_info.append((pixel_indices, world_indices)) + + return split_info + + +def pixel_to_pixel(wcs_in, wcs_out, *inputs): + """ + Transform pixel coordinates in a dataset with a WCS to pixel coordinates + in another dataset with a different WCS. + + This function is designed to efficiently deal with input pixel arrays that + are broadcasted views of smaller arrays, and is compatible with any + APE14-compliant WCS. + + Parameters + ---------- + wcs_in : `~astropy.wcs.wcsapi.BaseHighLevelWCS` + A WCS object for the original dataset which complies with the + high-level shared APE 14 WCS API. + wcs_out : `~astropy.wcs.wcsapi.BaseHighLevelWCS` + A WCS object for the target dataset which complies with the + high-level shared APE 14 WCS API. + *inputs : + Scalars or arrays giving the pixel coordinates to transform. + """ + # Shortcut for scalars + if np.isscalar(inputs[0]): + world_outputs = wcs_in.pixel_to_world(*inputs) + if not isinstance(world_outputs, (tuple, list)): + world_outputs = (world_outputs,) + return wcs_out.world_to_pixel(*world_outputs) + + # Remember original shape + original_shape = inputs[0].shape + + matrix = _pixel_to_pixel_correlation_matrix(wcs_in, wcs_out) + split_info = _split_matrix(matrix) + + outputs = [None] * wcs_out.pixel_n_dim + + for pixel_in_indices, pixel_out_indices in split_info: + pixel_inputs = [] + for ipix in range(wcs_in.pixel_n_dim): + if ipix in pixel_in_indices: + pixel_inputs.append(unbroadcast(inputs[ipix])) + else: + pixel_inputs.append(inputs[ipix].flat[0]) + + pixel_inputs = np.broadcast_arrays(*pixel_inputs) + + world_outputs = wcs_in.pixel_to_world(*pixel_inputs) + + if not isinstance(world_outputs, (tuple, list)): + world_outputs = (world_outputs,) + + pixel_outputs = wcs_out.world_to_pixel(*world_outputs) + + if wcs_out.pixel_n_dim == 1: + pixel_outputs = (pixel_outputs,) + + for ipix in range(wcs_out.pixel_n_dim): + if ipix in pixel_out_indices: + outputs[ipix] = np.broadcast_to(pixel_outputs[ipix], original_shape) + + return outputs[0] if wcs_out.pixel_n_dim == 1 else outputs + + +def local_partial_pixel_derivatives(wcs, *pixel, normalize_by_world=False): + """ + Return a matrix of shape ``(world_n_dim, pixel_n_dim)`` where each entry + ``[i, j]`` is the partial derivative d(world_i)/d(pixel_j) at the requested + pixel position. + + Parameters + ---------- + wcs : `~astropy.wcs.WCS` + The WCS transformation to evaluate the derivatives for. + *pixel : float + The scalar pixel coordinates at which to evaluate the derivatives. + normalize_by_world : bool + If `True`, the matrix is normalized so that for each world entry + the derivatives add up to 1. + """ + # Find the world coordinates at the requested pixel + pixel_ref = np.array(pixel) + world_ref = np.array(wcs.pixel_to_world_values(*pixel_ref)) + + # Set up the derivative matrix + derivatives = np.zeros((wcs.world_n_dim, wcs.pixel_n_dim)) + + for i in range(wcs.pixel_n_dim): + pixel_off = pixel_ref.copy() + pixel_off[i] += 1 + world_off = np.array(wcs.pixel_to_world_values(*pixel_off)) + derivatives[:, i] = world_off - world_ref + + if normalize_by_world: + derivatives /= derivatives.sum(axis=1)[:, np.newaxis] + + return derivatives + + +def _linear_wcs_fit(params, lon, lat, x, y, w_obj): + """ + Objective function for fitting linear terms. + + Parameters + ---------- + params : array + 6 element array. First 4 elements are PC matrix, last 2 are CRPIX. + lon, lat: array + Sky coordinates. + x, y: array + Pixel coordinates + w_obj: `~astropy.wcs.WCS` + WCS object + """ + cd = params[0:4] + crpix = params[4:6] + + w_obj.wcs.cd = ((cd[0], cd[1]), (cd[2], cd[3])) + w_obj.wcs.crpix = crpix + lon2, lat2 = w_obj.wcs_pix2world(x, y, 0) + + lat_resids = lat - lat2 + lon_resids = lon - lon2 + # In case the longitude has wrapped around + lon_resids = np.mod(lon_resids - 180.0, 360.0) - 180.0 + + resids = np.concatenate((lon_resids * np.cos(np.radians(lat)), lat_resids)) + + return resids + + +def _sip_fit(params, lon, lat, u, v, w_obj, order, coeff_names): + """Objective function for fitting SIP. + + Parameters + ---------- + params : array + Fittable parameters. First 4 elements are PC matrix, last 2 are CRPIX. + lon, lat: array + Sky coordinates. + u, v: array + Pixel coordinates + w_obj: `~astropy.wcs.WCS` + WCS object + """ + from astropy.modeling.models import SIP # here to avoid circular import + + # unpack params + crpix = params[0:2] + cdx = params[2:6].reshape((2, 2)) + a_params = params[6 : 6 + len(coeff_names)] + b_params = params[6 + len(coeff_names) :] + + # assign to wcs, used for transformations in this function + w_obj.wcs.cd = cdx + w_obj.wcs.crpix = crpix + + a_coeff, b_coeff = {}, {} + for i in range(len(coeff_names)): + a_coeff["A_" + coeff_names[i]] = a_params[i] + b_coeff["B_" + coeff_names[i]] = b_params[i] + + sip = SIP( + crpix=crpix, a_order=order, b_order=order, a_coeff=a_coeff, b_coeff=b_coeff + ) + fuv, guv = sip(u, v) + + xo, yo = np.dot(cdx, np.array([u + fuv - crpix[0], v + guv - crpix[1]])) + + # use all pix2world in case `projection` contains distortion table + x, y = w_obj.all_world2pix(lon, lat, 0) + x, y = np.dot(w_obj.wcs.cd, (x - w_obj.wcs.crpix[0], y - w_obj.wcs.crpix[1])) + + resids = np.concatenate((x - xo, y - yo)) + + return resids + + +def fit_wcs_from_points( + xy, world_coords, proj_point="center", projection="TAN", sip_degree=None +): + """ + Given two matching sets of coordinates on detector and sky, + compute the WCS. + + Fits a WCS object to matched set of input detector and sky coordinates. + Optionally, a SIP can be fit to account for geometric + distortion. Returns an `~astropy.wcs.WCS` object with the best fit + parameters for mapping between input pixel and sky coordinates. + + The projection type (default 'TAN') can passed in as a string, one of + the valid three-letter projection codes - or as a WCS object with + projection keywords already set. Note that if an input WCS has any + non-polynomial distortion, this will be applied and reflected in the + fit terms and coefficients. Passing in a WCS object in this way essentially + allows it to be refit based on the matched input coordinates and projection + point, but take care when using this option as non-projection related + keywords in the input might cause unexpected behavior. + + Notes + ----- + - The fiducial point for the spherical projection can be set to 'center' + to use the mean position of input sky coordinates, or as an + `~astropy.coordinates.SkyCoord` object. + - Units in all output WCS objects will always be in degrees. + - If the coordinate frame differs between `~astropy.coordinates.SkyCoord` + objects passed in for ``world_coords`` and ``proj_point``, the frame for + ``world_coords`` will override as the frame for the output WCS. + - If a WCS object is passed in to ``projection`` the CD/PC matrix will + be used as an initial guess for the fit. If this is known to be + significantly off and may throw off the fit, set to the identity matrix + (for example, by doing wcs.wcs.pc = [(1., 0.,), (0., 1.)]) + + Parameters + ---------- + xy : (`numpy.ndarray`, `numpy.ndarray`) tuple + x & y pixel coordinates. These should be in FITS convention, starting + from (1,1) as the center of the bottom-left pixel. + world_coords : `~astropy.coordinates.SkyCoord` + Skycoord object with world coordinates. + proj_point : 'center' or ~astropy.coordinates.SkyCoord` + Defaults to 'center', in which the geometric center of input world + coordinates will be used as the projection point. To specify an exact + point for the projection, a Skycoord object with a coordinate pair can + be passed in. For consistency, the units and frame of these coordinates + will be transformed to match ``world_coords`` if they don't. + projection : str or `~astropy.wcs.WCS` + Three letter projection code, of any of standard projections defined + in the FITS WCS standard. Optionally, a WCS object with projection + keywords set may be passed in. + sip_degree : None or int + If set to a non-zero integer value, will fit SIP of degree + ``sip_degree`` to model geometric distortion. Defaults to None, meaning + no distortion corrections will be fit. + + Returns + ------- + wcs : `~astropy.wcs.WCS` + The best-fit WCS to the points given. + """ + from scipy.optimize import least_squares + + import astropy.units as u + from astropy.coordinates import SkyCoord # here to avoid circular import + + from .wcs import Sip + + xp, yp = xy + try: + lon, lat = world_coords.data.lon.deg, world_coords.data.lat.deg + except AttributeError: + unit_sph = world_coords.unit_spherical + lon, lat = unit_sph.lon.deg, unit_sph.lat.deg + + # verify input + if (type(proj_point) != type(world_coords)) and (proj_point != "center"): + raise ValueError( + "proj_point must be set to 'center', or an" + "`~astropy.coordinates.SkyCoord` object with " + "a pair of points." + ) + + use_center_as_proj_point = str(proj_point) == "center" + + if not use_center_as_proj_point: + assert proj_point.size == 1 + + proj_codes = [ + "AZP", + "SZP", + "TAN", + "STG", + "SIN", + "ARC", + "ZEA", + "AIR", + "CYP", + "CEA", + "CAR", + "MER", + "SFL", + "PAR", + "MOL", + "AIT", + "COP", + "COE", + "COD", + "COO", + "BON", + "PCO", + "TSC", + "CSC", + "QSC", + "HPX", + "XPH", + ] + if type(projection) == str: + if projection not in proj_codes: + raise ValueError( + "Must specify valid projection code from list of supported types: ", + ", ".join(proj_codes), + ) + # empty wcs to fill in with fit values + wcs = celestial_frame_to_wcs(frame=world_coords.frame, projection=projection) + else: # if projection is not string, should be wcs object. use as template. + wcs = copy.deepcopy(projection) + wcs.wcs.cdelt = (1.0, 1.0) # make sure cdelt is 1 + wcs.sip = None + + # Change PC to CD, since cdelt will be set to 1 + if wcs.wcs.has_pc(): + wcs.wcs.cd = wcs.wcs.pc + wcs.wcs.__delattr__("pc") + + if (sip_degree is not None) and (type(sip_degree) != int): + raise ValueError("sip_degree must be None, or integer.") + + # compute bounding box for sources in image coordinates: + xpmin, xpmax, ypmin, ypmax = xp.min(), xp.max(), yp.min(), yp.max() + + # set pixel_shape to span of input points + wcs.pixel_shape = ( + 1 if xpmax <= 0.0 else int(np.ceil(xpmax)), + 1 if ypmax <= 0.0 else int(np.ceil(ypmax)), + ) + + # determine CRVAL from input + close = lambda l, p: p[np.argmin(np.abs(l))] + if use_center_as_proj_point: # use center of input points + sc1 = SkyCoord(lon.min() * u.deg, lat.max() * u.deg) + sc2 = SkyCoord(lon.max() * u.deg, lat.min() * u.deg) + pa = sc1.position_angle(sc2) + sep = sc1.separation(sc2) + midpoint_sc = sc1.directional_offset_by(pa, sep / 2) + wcs.wcs.crval = (midpoint_sc.data.lon.deg, midpoint_sc.data.lat.deg) + wcs.wcs.crpix = ((xpmax + xpmin) / 2.0, (ypmax + ypmin) / 2.0) + else: # convert units, initial guess for crpix + proj_point.transform_to(world_coords) + wcs.wcs.crval = (proj_point.data.lon.deg, proj_point.data.lat.deg) + wcs.wcs.crpix = ( + close(lon - wcs.wcs.crval[0], xp + 1), + close(lon - wcs.wcs.crval[1], yp + 1), + ) + + # fit linear terms, assign to wcs + # use (1, 0, 0, 1) as initial guess, in case input wcs was passed in + # and cd terms are way off. + # Use bounds to require that the fit center pixel is on the input image + if xpmin == xpmax: + xpmin, xpmax = xpmin - 0.5, xpmax + 0.5 + if ypmin == ypmax: + ypmin, ypmax = ypmin - 0.5, ypmax + 0.5 + + p0 = np.concatenate([wcs.wcs.cd.flatten(), wcs.wcs.crpix.flatten()]) + fit = least_squares( + _linear_wcs_fit, + p0, + args=(lon, lat, xp, yp, wcs), + bounds=[ + [-np.inf, -np.inf, -np.inf, -np.inf, xpmin + 1, ypmin + 1], + [np.inf, np.inf, np.inf, np.inf, xpmax + 1, ypmax + 1], + ], + ) + wcs.wcs.crpix = np.array(fit.x[4:6]) + wcs.wcs.cd = np.array(fit.x[0:4].reshape((2, 2))) + + # fit SIP, if specified. Only fit forward coefficients + if sip_degree: + degree = sip_degree + if "-SIP" not in wcs.wcs.ctype[0]: + wcs.wcs.ctype = [x + "-SIP" for x in wcs.wcs.ctype] + + coef_names = [ + f"{i}_{j}" + for i in range(degree + 1) + for j in range(degree + 1) + if (i + j) < (degree + 1) and (i + j) > 1 + ] + p0 = np.concatenate( + ( + np.array(wcs.wcs.crpix), + wcs.wcs.cd.flatten(), + np.zeros(2 * len(coef_names)), + ) + ) + + fit = least_squares( + _sip_fit, + p0, + args=(lon, lat, xp, yp, wcs, degree, coef_names), + bounds=[ + [xpmin + 1, ypmin + 1] + [-np.inf] * (4 + 2 * len(coef_names)), + [xpmax + 1, ypmax + 1] + [np.inf] * (4 + 2 * len(coef_names)), + ], + ) + coef_fit = ( + list(fit.x[6 : 6 + len(coef_names)]), + list(fit.x[6 + len(coef_names) :]), + ) + + # put fit values in wcs + wcs.wcs.cd = fit.x[2:6].reshape((2, 2)) + wcs.wcs.crpix = fit.x[0:2] + + a_vals = np.zeros((degree + 1, degree + 1)) + b_vals = np.zeros((degree + 1, degree + 1)) + + for coef_name in coef_names: + a_vals[int(coef_name[0])][int(coef_name[2])] = coef_fit[0].pop(0) + b_vals[int(coef_name[0])][int(coef_name[2])] = coef_fit[1].pop(0) + + wcs.sip = Sip( + a_vals, + b_vals, + np.zeros((degree + 1, degree + 1)), + np.zeros((degree + 1, degree + 1)), + wcs.wcs.crpix, + ) + + return wcs + + +def obsgeo_to_frame(obsgeo, obstime): + """ + Convert a WCS obsgeo property into an ITRS coordinate frame. + + Parameters + ---------- + obsgeo : array-like + A shape ``(6, )`` array representing ``OBSGEO-[XYZ], OBSGEO-[BLH]`` as + returned by ``WCS.wcs.obsgeo``. + + obstime : time-like + The time associated with the coordinate, will be passed to + `~astropy.coordinates.ITRS` as the obstime keyword. + + Returns + ------- + ~astropy.coordinates.ITRS + An `~astropy.coordinates.ITRS` coordinate frame + representing the coordinates. + + Notes + ----- + The obsgeo array as accessed on a `.WCS` object is a length 6 numpy array + where the first three elements are the coordinate in a cartesian + representation and the second 3 are the coordinate in a spherical + representation. + + This function priorities reading the cartesian coordinates, and will only + read the spherical coordinates if the cartesian coordinates are either all + zero or any of the cartesian coordinates are non-finite. + + In the case where both the spherical and cartesian coordinates have some + non-finite values the spherical coordinates will be returned with the + non-finite values included. + + """ + if ( + obsgeo is None + or len(obsgeo) != 6 + or np.all(np.array(obsgeo) == 0) + or np.all(~np.isfinite(obsgeo)) + ): + raise ValueError( + f"Can not parse the 'obsgeo' location ({obsgeo}). " + "obsgeo should be a length 6 non-zero, finite numpy array" + ) + + # If the cartesian coords are zero or have NaNs in them use the spherical ones + if np.all(obsgeo[:3] == 0) or np.any(~np.isfinite(obsgeo[:3])): + data = SphericalRepresentation(*(obsgeo[3:] * (u.deg, u.deg, u.m))) + + # Otherwise we assume the cartesian ones are valid + else: + data = CartesianRepresentation(*obsgeo[:3] * u.m) + + return ITRS(data, obstime=obstime) diff --git a/astropy/wcs/wcs.py b/astropy/wcs/wcs.py index 6494546d38fd..ae925afe0406 100644 --- a/astropy/wcs/wcs.py +++ b/astropy/wcs/wcs.py @@ -1,136 +1,379 @@ -""" -Under the hood, there are 3 separate classes that perform different -parts of the transformation: - - - `~astropy.wcs.Wcsprm`: Is a direct wrapper of the core WCS - functionality in `wcslib`_. - - - `~astropy.wcs.Sip`: Handles polynomial distortion as defined in the - `SIP`_ convention. - - - `~astropy.wcs.DistortionLookupTable`: Handles `Paper IV`_ distortion - lookup tables. - -Additionally, the class `WCS` aggregates all of these transformations -together in a pipeline: - - - Detector to image plane correction (by a pair of - `~astropy.wcs.DistortionLookupTable` objects). - - - `SIP`_ distortion correction (by an underlying `~astropy.wcs.Sip` - object) - - - `Paper IV`_ table-lookup distortion correction (by a pair of - `~astropy.wcs.DistortionLookupTable` objects). - - - `wcslib`_ WCS transformation (by a `~astropy.wcs.Wcsprm` object) -""" - -from __future__ import division # confidence high +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +# Under the hood, there are 3 separate classes that perform different +# parts of the transformation: +# +# - `~astropy.wcs.Wcsprm`: Is a direct wrapper of the core WCS +# functionality in `wcslib`_. (This includes TPV and TPD +# polynomial distortion, but not SIP distortion). +# +# - `~astropy.wcs.Sip`: Handles polynomial distortion as defined in the +# `SIP`_ convention. +# +# - `~astropy.wcs.DistortionLookupTable`: Handles `distortion paper`_ +# lookup tables. +# +# Additionally, the class `WCS` aggregates all of these transformations +# together in a pipeline: +# +# - Detector to image plane correction (by a pair of +# `~astropy.wcs.DistortionLookupTable` objects). +# +# - `SIP`_ distortion correction (by an underlying `~astropy.wcs.Sip` +# object) +# +# - `distortion paper`_ table-lookup correction (by a pair of +# `~astropy.wcs.DistortionLookupTable` objects). +# +# - `wcslib`_ WCS transformation (by a `~astropy.wcs.Wcsprm` object) # STDLIB +import builtins import copy import io +import itertools import os -import sys +import re +import textwrap +import uuid import warnings # THIRD-PARTY import numpy as np +from packaging.version import Version # LOCAL -from ..io import fits -from . import _docutil as __ -try: - from . import _wcs -except ImportError: - _wcs = None -from ..utils import deprecated - -if _wcs is not None: - assert _wcs._sanity_check(), \ - "astropy.wcs did not pass its sanity check for your build " \ - "on your platform." - -if sys.version_info[0] >= 3: # pragma: py3 - string_types = (bytes, str) -else: # pragma: py2 - string_types = (str, unicode) - - -__all__ = ['FITSFixedWarning', 'WCS', 'find_all_wcs', - 'DistortionLookupTable', 'Sip', 'Tabprm', 'UnitConverter', - 'Wcsprm'] - - -if _wcs is not None: - WCSBase = _wcs._Wcs - DistortionLookupTable = _wcs.DistortionLookupTable - Sip = _wcs.Sip - UnitConverter = _wcs.UnitConverter - Wcsprm = _wcs._Wcsprm - Tabprm = _wcs.Tabprm - # Copy all the constants from the C extension into this module's namespace - for key, val in _wcs.__dict__.items(): - if (key.startswith('WCSSUB') or - key.startswith('WCSHDR') or - key.startswith('WCSHDO')): - locals()[key] = val - __all__.append(key) -else: - WCSBase = object - Wcsprm = object - DistortionLookupTable = object - Sip = object - UnitConverter = object - Tabprm = object +from astropy import log +from astropy import units as u +from astropy.io import fits +from astropy.utils.exceptions import ( + AstropyDeprecationWarning, + AstropyUserWarning, + AstropyWarning, +) + +from . import docstrings +from ._wcs import ( + PRJ_CODES, + PRJ_CONIC, + PRJ_CONVENTIONAL, + PRJ_CYLINDRICAL, + PRJ_HEALPIX, + PRJ_POLYCONIC, + PRJ_PSEUDOCYLINDRICAL, + PRJ_PVN, + PRJ_QUADCUBE, + PRJ_ZENITHAL, + WCSCOMPARE_ANCILLARY, + WCSCOMPARE_CRPIX, + WCSCOMPARE_TILING, + WCSHDO_EFMT, + WCSHDO_P12, + WCSHDO_P13, + WCSHDO_P14, + WCSHDO_P15, + WCSHDO_P16, + WCSHDO_P17, + WCSHDR_ALLIMG, + WCSHDR_AUXIMG, + WCSHDR_BIMGARR, + WCSHDR_IMGHEAD, + WCSHDR_LONGKEY, + WCSHDR_PIXLIST, + WCSHDR_RADECSYS, + WCSHDR_VSOURCE, + WCSLIB_VERSION, + WCSSUB_CELESTIAL, + WCSSUB_CUBEFACE, + WCSSUB_LATITUDE, + WCSSUB_LONGITUDE, + WCSSUB_SPECTRAL, + WCSSUB_STOKES, + WCSSUB_TIME, + Auxprm, + Celprm, + DistortionLookupTable, + InconsistentAxisTypesError, + InvalidCoordinateError, + InvalidPrjParametersError, + InvalidSubimageSpecificationError, + InvalidTabularParametersError, + InvalidTransformError, + NonseparableSubimageCoordinateSystemError, + NoSolutionError, + NoWcsKeywordsFoundError, + Prjprm, + SingularMatrixError, + Sip, + Tabprm, + WcsError, + WCSHDO_all, + WCSHDO_CNAMna, + WCSHDO_CRPXna, + WCSHDO_DOBSn, + WCSHDO_none, + WCSHDO_PVn_ma, + WCSHDO_safe, + WCSHDO_TPCn_ka, + WCSHDO_WCSNna, + WCSHDR_all, + WCSHDR_CD0i_0ja, + WCSHDR_CD00i00j, + WCSHDR_CNAMn, + WCSHDR_CROTAia, + WCSHDR_DOBSn, + WCSHDR_EPOCHa, + WCSHDR_none, + WCSHDR_PC0i_0ja, + WCSHDR_PC00i00j, + WCSHDR_PROJPn, + WCSHDR_PS0i_0ma, + WCSHDR_PV0i_0ma, + WCSHDR_reject, + WCSHDR_strict, + WCSHDR_VELREFa, + Wcsprm, + Wtbarr, + _sanity_check, + set_wtbarr_fitsio_callback, +) +from ._wcs import _Wcs as WCSBase +from ._wcs import find_all_wcs as find_all_wcs_c + +# Mix-in class that provides the APE 14 API +from .wcsapi.fitswcs import FITSWCSAPIMixin, SlicedFITSWCS + +__all__ = [ + "PRJ_CODES", + "PRJ_CONIC", + "PRJ_CONVENTIONAL", + "PRJ_CYLINDRICAL", + "PRJ_HEALPIX", + "PRJ_POLYCONIC", + "PRJ_PSEUDOCYLINDRICAL", + "PRJ_PVN", + "PRJ_QUADCUBE", + "PRJ_ZENITHAL", + "WCS", + "WCSCOMPARE_ANCILLARY", + "WCSCOMPARE_CRPIX", + "WCSCOMPARE_TILING", + "WCSHDO_EFMT", + "WCSHDO_P12", + "WCSHDO_P13", + "WCSHDO_P14", + "WCSHDO_P15", + "WCSHDO_P16", + "WCSHDO_P17", + "WCSHDR_ALLIMG", + "WCSHDR_AUXIMG", + "WCSHDR_BIMGARR", + "WCSHDR_IMGHEAD", + "WCSHDR_LONGKEY", + "WCSHDR_PIXLIST", + "WCSHDR_RADECSYS", + "WCSHDR_VSOURCE", + "WCSSUB_CELESTIAL", + "WCSSUB_CUBEFACE", + "WCSSUB_LATITUDE", + "WCSSUB_LONGITUDE", + "WCSSUB_SPECTRAL", + "WCSSUB_STOKES", + "WCSSUB_TIME", + "Auxprm", + "Celprm", + "DistortionLookupTable", + "FITSFixedWarning", + "InconsistentAxisTypesError", + "InvalidCoordinateError", + "InvalidPrjParametersError", + "InvalidSubimageSpecificationError", + "InvalidTabularParametersError", + "InvalidTransformError", + "NoConvergence", + "NoSolutionError", + "NoWcsKeywordsFoundError", + "NonseparableSubimageCoordinateSystemError", + "Prjprm", + "SingularMatrixError", + "Sip", + "Tabprm", + "WCSBase", + "WCSHDO_CNAMna", + "WCSHDO_CRPXna", + "WCSHDO_DOBSn", + "WCSHDO_PVn_ma", + "WCSHDO_TPCn_ka", + "WCSHDO_WCSNna", + "WCSHDO_all", + "WCSHDO_none", + "WCSHDO_safe", + "WCSHDR_CD0i_0ja", + "WCSHDR_CD00i00j", + "WCSHDR_CNAMn", + "WCSHDR_CROTAia", + "WCSHDR_DOBSn", + "WCSHDR_EPOCHa", + "WCSHDR_PC0i_0ja", + "WCSHDR_PC00i00j", + "WCSHDR_PROJPn", + "WCSHDR_PS0i_0ma", + "WCSHDR_PV0i_0ma", + "WCSHDR_VELREFa", + "WCSHDR_all", + "WCSHDR_none", + "WCSHDR_reject", + "WCSHDR_strict", + "WcsError", + "Wcsprm", + "Wtbarr", + "find_all_wcs", + "validate", +] + +__doctest_skip__ = ["WCS.all_world2pix"] + + +if Version(WCSLIB_VERSION) < Version("5.8"): + raise ImportError( + "astropy.wcs is built with wcslib {0}, but only versions 5.8 and " + "later on the 5.x series are known to work. The version of wcslib " + "that ships with astropy may be used." + ) + +if not _sanity_check(): + raise RuntimeError( + "astropy.wcs did not pass its sanity check for your build on your platform." + ) + +_WCSSUB_TIME_SUPPORT = Version(WCSLIB_VERSION) >= Version("7.8") +_WCS_TPD_WARN_LT71 = Version(WCSLIB_VERSION) < Version("7.1") +_WCS_TPD_WARN_LT74 = Version(WCSLIB_VERSION) < Version("7.4") + + +# Set coordinate extraction callback for WCS -TAB: +def _load_tab_bintable(hdulist, extnam, extver, extlev, kind, ttype, row, ndim): + arr = hdulist[(extnam, extver)].data[ttype][row - 1] + + if arr.ndim != ndim: + if kind == "c" and ndim == 2: + arr = arr.reshape((arr.size, 1)) + else: + raise ValueError("Bad TDIM") + + return np.ascontiguousarray(arr, dtype=np.double) + + +set_wtbarr_fitsio_callback(_load_tab_bintable) + + +# Additional relax bit flags +WCSHDO_SIP = 0x80000 + +# Regular expression defining SIP keyword It matches keyword that starts with A +# or B, optionally followed by P, followed by an underscore then a number in +# range of 0-19, followed by an underscore and another number in range of 0-19. +# Keyword optionally ends with a capital letter. +SIP_KW = re.compile("""^[AB]P?_1?[0-9]_1?[0-9][A-Z]?$""") def _parse_keysel(keysel): keysel_flags = 0 if keysel is not None: for element in keysel: - if element.lower() == 'image': - keysel_flags |= _wcs.WCSHDR_IMGHEAD - elif element.lower() == 'binary': - keysel_flags |= _wcs.WCSHDR_BIMGARR - elif element.lower() == 'pixel': - keysel_flags |= _wcs.WCSHDR_PIXLIST + if element.lower() == "image": + keysel_flags |= WCSHDR_IMGHEAD + elif element.lower() == "binary": + keysel_flags |= WCSHDR_BIMGARR + elif element.lower() == "pixel": + keysel_flags |= WCSHDR_PIXLIST else: raise ValueError( - "keysel must be a list of 'image', 'binary' " + - "and/or 'pixel'") + "keysel must be a list of 'image', 'binary' and/or 'pixel'" + ) else: keysel_flags = -1 return keysel_flags -class FITSFixedWarning(Warning): +class NoConvergence(Exception): + """ + An error class used to report non-convergence and/or divergence + of numerical methods. It is used to report errors in the + iterative solution used by + the :py:meth:`~astropy.wcs.WCS.all_world2pix`. + + Attributes + ---------- + best_solution : `numpy.ndarray` + Best solution achieved by the numerical method. + + accuracy : `numpy.ndarray` + Accuracy of the ``best_solution``. + + niter : `int` + Number of iterations performed by the numerical method + to compute ``best_solution``. + + divergent : None, `numpy.ndarray` + Indices of the points in ``best_solution`` array + for which the solution appears to be divergent. If the + solution does not diverge, ``divergent`` will be set to `None`. + + slow_conv : None, `numpy.ndarray` + Indices of the solutions in ``best_solution`` array + for which the solution failed to converge within the + specified maximum number of iterations. If there are no + non-converging solutions (i.e., if the required accuracy + has been achieved for all input data points) + then ``slow_conv`` will be set to `None`. + + """ + + def __init__( + self, + *args, + best_solution=None, + accuracy=None, + niter=None, + divergent=None, + slow_conv=None, + ): + super().__init__(*args) + + self.best_solution = best_solution + self.accuracy = accuracy + self.niter = niter + self.divergent = divergent + self.slow_conv = slow_conv + + +class FITSFixedWarning(AstropyWarning): """ The warning raised when the contents of the FITS header have been modified to be standards compliant. """ - pass -class WCS(WCSBase): - """ - WCS objects perform standard WCS transformations, and correct for - `SIP`_ and `Paper IV`_ table-lookup distortions, based on the WCS - keywords and supplementary data read from a FITS file. +class WCS(FITSWCSAPIMixin, WCSBase): + """WCS objects perform standard WCS transformations, and correct for + `SIP`_ and `distortion paper`_ table-lookup transformations, based + on the WCS keywords and supplementary data read from a FITS file. + + See also: https://docs.astropy.org/en/stable/wcs/ Parameters ---------- - header : astropy.io.fits header object, string or None, optional + header : `~astropy.io.fits.Header`, `~astropy.io.fits.hdu.image.PrimaryHDU`, `~astropy.io.fits.hdu.image.ImageHDU`, str, dict-like, or None, optional If *header* is not provided or None, the object will be initialized to default values. - fobj : An astropy.io.fits file (hdulist) object, optional - It is needed when header keywords point to a `Paper IV`_ - Lookup table distortion stored in a different extension. + fobj : `~astropy.io.fits.HDUList`, optional + It is needed when header keywords point to a `distortion + paper`_ lookup table stored in a different extension. - key : string, optional + key : str, optional The name of a particular WCS transform to use. This may be either ``' '`` or ``'A'``-``'Z'`` and corresponds to the ``\"a\"`` part of the ``CTYPEia`` cards. *key* may only be @@ -144,14 +387,14 @@ class WCS(WCSBase): relax : bool or int, optional Degree of permissiveness: + - `True` (default): Admit all recognized informal extensions + of the WCS standard. + - `False`: Recognize only FITS keywords defined by the published WCS standard. - - `True`: Admit all recognized informal extensions of the WCS - standard. - - `int`: a bit field selecting specific extensions to accept. - See :ref:`relaxread` for details. + See :ref:`astropy:relaxread` for details. naxis : int or sequence, optional Extracts specific coordinate axes using @@ -161,7 +404,7 @@ class WCS(WCSBase): axes from the header. See :meth:`~astropy.wcs.Wcsprm.sub` for more details about this parameter. - keysel : sequence of flags, optional + keysel : sequence of str, optional A sequence of flags used to select the keyword types considered by wcslib. When ``None``, only the standard image header keywords are considered (and the underlying wcspih() C @@ -188,11 +431,23 @@ class WCS(WCSBase): specified columns. If `None`, there is no restriction. fix : bool, optional - When `True` (default), call `~astropy.wcs._wcs.Wcsprm.fix` on + When `True` (default), call `~astropy.wcs.Wcsprm.fix` on the resulting object to fix any non-standard uses in the header. `FITSFixedWarning` Warnings will be emitted if any changes were made. + translate_units : str, optional + Specify which potentially unsafe translations of non-standard + unit strings to perform. By default, performs none. See + `WCS.fix` for more information about this parameter. Only + effective when ``fix`` is `True`. + + preserve_units : bool, optional + By default, some units are converted to SI, for example spectral + axes in units of nm might be converted to m, and celestial axes + in units of arcsec might be converted to deg. If ``preserve_units`` + is set to `True`, the original units will be preserved. + Raises ------ MemoryError @@ -204,30 +459,89 @@ class WCS(WCSBase): KeyError Key not found in FITS header. - AssertionError + ValueError Lookup table distortion present in the header but *fobj* was not provided. Notes ----- - astropy.wcs supports arbitrary *n* dimensions for the core WCS - (the transformations handled by WCSLIB). However, the Paper IV - lookup table and SIP distortions must be two dimensional. - Therefore, if you try to create a WCS object where the core WCS - has a different number of dimensions than 2 and that object also - contains a Paper IV lookup table or SIP distortion, a `ValueError` - exception will be raised. To avoid this, consider using the - *naxis* kwarg to select two dimensions from the core WCS. + 1. astropy.wcs supports arbitrary *n* dimensions for the core WCS + (the transformations handled by WCSLIB). However, the + `distortion paper`_ lookup table and `SIP`_ distortions must be + two dimensional. Therefore, if you try to create a WCS object + where the core WCS has a different number of dimensions than 2 + and that object also contains a `distortion paper`_ lookup + table or `SIP`_ distortion, a `ValueError` + exception will be raised. To avoid this, consider using the + *naxis* kwarg to select two dimensions from the core WCS. + + 2. The number of coordinate axes in the transformation is not + determined directly from the ``NAXIS`` keyword but instead from + the highest of: + + - ``NAXIS`` keyword + + - ``WCSAXESa`` keyword + + - The highest axis number in any parameterized WCS keyword. + The keyvalue, as well as the keyword, must be + syntactically valid otherwise it will not be considered. + + If none of these keyword types is present, i.e. if the header + only contains auxiliary WCS keywords for a particular + coordinate representation, then no coordinate description is + constructed for it. + + The number of axes, which is set as the ``naxis`` member, may + differ for different coordinate representations of the same + image. + + 3. When the header includes duplicate keywords, in most cases the + last encountered is used. + + 4. `~astropy.wcs.Wcsprm.set` is called immediately after + construction, so any invalid keywords or transformations will + be raised by the constructor, not when subsequently calling a + transformation method. + """ - def __init__(self, header=None, fobj=None, key=' ', minerr=0.0, - relax=False, naxis=None, keysel=None, colsel=None, - fix=True): + def __init__( + self, + header=None, + fobj=None, + key=" ", + minerr=0.0, + relax=True, + naxis=None, + keysel=None, + colsel=None, + fix=True, + translate_units="", + _do_set=True, + preserve_units=False, + ): + close_fds = [] + + self._preserve_units = preserve_units + + # these parameters are stored to be used when unpickling a WCS object: + self._init_kwargs = { + "keysel": copy.copy(keysel), + "colsel": copy.copy(colsel), + "preserve_units": preserve_units, + } + if header is None: if naxis is None: naxis = 2 - wcsprm = _wcs._Wcsprm(header=None, key=key, - relax=relax, naxis=naxis) + wcsprm = Wcsprm( + header=None, + key=key, + relax=relax, + naxis=naxis, + preserve_units=preserve_units, + ) self.naxis = wcsprm.naxis # Set some reasonable defaults. det2im = (None, None) @@ -236,35 +550,125 @@ def __init__(self, header=None, fobj=None, key=' ', minerr=0.0, else: keysel_flags = _parse_keysel(keysel) - if isinstance(header, string_types): - if os.path.exists(header): + if isinstance(header, (str, bytes)): + try: + is_path = os.path.exists(header) + except (OSError, ValueError): + is_path = False + + if is_path: if fobj is not None: raise ValueError( "Can not provide both a FITS filename to " - "argument 1 and a FITS file object to argument 2") + "argument 1 and a FITS file object to argument 2" + ) fobj = fits.open(header) + close_fds.append(fobj) header = fobj[0].header - header_string = repr(header.ascard).encode('latin1') - else: - header_string = header - elif isinstance(header, fits.Header): - header_string = repr(header.ascard).encode('latin1') + elif isinstance(header, fits.hdu.image._ImageBaseHDU): + header = header.header + elif not isinstance(header, fits.Header): + try: + # Accept any dict-like object + orig_header = header + header = fits.Header() + for dict_key in orig_header.keys(): + header[dict_key] = orig_header[dict_key] + except TypeError: + raise TypeError( + "header must be a string, an astropy.io.fits.Header " + "object, or a dict-like object" + ) + + if isinstance(header, fits.Header): + header_string = header.tostring().rstrip() else: - raise TypeError( - "header must be a string or an astropy.io.fits.Header " - "object") + header_string = header + + # Importantly, header is a *copy* of the passed-in header + # because we will be modifying it + if isinstance(header_string, str): + header_bytes = header_string.encode("ascii") + else: + header_bytes = header_string + header_string = header_string.decode("ascii") + + if not (fobj is None or isinstance(fobj, fits.HDUList)): + raise AssertionError( + "'fobj' must be either None or an astropy.io.fits.HDUList object." + ) + + est_naxis = 2 try: - wcsprm = _wcs._Wcsprm(header=header_string, key=key, - relax=relax, keysel=keysel_flags, - colsel=colsel) - except _wcs.NoWcsKeywordsFoundError: + tmp_header = fits.Header.fromstring(header_string) + self._remove_sip_kw(tmp_header) + tmp_header_bytes = tmp_header.tostring().rstrip() + if isinstance(tmp_header_bytes, str): + tmp_header_bytes = tmp_header_bytes.encode("ascii") + tmp_wcsprm = Wcsprm( + header=tmp_header_bytes, + key=key, + relax=relax, + keysel=keysel_flags, + colsel=colsel, + warnings=False, + hdulist=fobj, + preserve_units=preserve_units, + ) + if naxis is not None: + try: + tmp_wcsprm = tmp_wcsprm.sub(naxis) + except ValueError: + pass + est_naxis = tmp_wcsprm.naxis or 2 + + except NoWcsKeywordsFoundError: + pass + + self.naxis = est_naxis + + header = fits.Header.fromstring(header_string) + + det2im = self._read_det2im_kw(header, fobj, err=minerr) + cpdis = self._read_distortion_kw(header, fobj, dist="CPDIS", err=minerr) + self._fix_pre2012_scamp_tpv(header) + + sip = self._read_sip_kw(header, wcskey=key) + self._remove_sip_kw(header) + + header_string = header.tostring() + header_string = header_string.replace("END" + " " * 77, "") + + if isinstance(header_string, str): + header_bytes = header_string.encode("ascii") + else: + header_bytes = header_string + header_string = header_string.decode("ascii") + + try: + wcsprm = Wcsprm( + header=header_bytes, + key=key, + relax=relax, + keysel=keysel_flags, + colsel=colsel, + hdulist=fobj, + preserve_units=preserve_units, + ) + except NoWcsKeywordsFoundError: # The header may have SIP or distortions, but no core # WCS. That isn't an error -- we want a "default" # (identity) core Wcs transformation in that case. if colsel is None: - wcsprm = _wcs._Wcsprm(header=None, key=key, - relax=relax, keysel=keysel_flags, - colsel=colsel) + wcsprm = Wcsprm( + header=None, + key=key, + relax=relax, + keysel=keysel_flags, + colsel=colsel, + hdulist=fobj, + preserve_units=preserve_units, + ) else: raise @@ -272,55 +676,73 @@ def __init__(self, header=None, fobj=None, key=' ', minerr=0.0, wcsprm = wcsprm.sub(naxis) self.naxis = wcsprm.naxis - det2im = self._read_det2im_kw(header, fobj) - cpdis = self._read_distortion_kw( - header, fobj, dist='CPDIS', err=minerr) - sip = self._read_sip_kw(header) - if (wcsprm.naxis != 2 and - (det2im[0] or det2im[1] or cpdis[0] or cpdis[1] or sip)): + if wcsprm.naxis != 2 and ( + det2im[0] or det2im[1] or cpdis[0] or cpdis[1] or sip + ): raise ValueError( - """ -Paper IV lookup tables and SIP distortions only work in 2 dimensions. -However, WCSLIB has detected {0} dimensions in the core WCS keywords. -To use core WCS in conjunction with Paper IV lookup tables or SIP -distortion, you must select or reduce these to 2 dimensions using the -naxis kwarg. -""".format(wcsprm.naxis)) + f""" +FITS WCS distortion paper lookup tables and SIP distortions only work +in 2 dimensions. However, WCSLIB has detected {wcsprm.naxis} dimensions in the +core WCS keywords. To use core WCS in conjunction with FITS WCS +distortion paper lookup tables or SIP distortion, you must select or +reduce these to 2 dimensions using the naxis kwarg. +""" + ) - if fix: - fixes = wcsprm.fix() - for key, val in fixes.iteritems(): - if val != "No change": - warnings.warn( - ("'{0}' made the change '{1}'. " - "This FITS header contains non-standard content."). - format(key, val), - FITSFixedWarning) + header_naxis = header.get("NAXIS", None) + if header_naxis is not None and header_naxis < wcsprm.naxis: + warnings.warn( + f"The WCS transformation has more axes ({wcsprm.naxis:d}) than the " + f"image it is associated with ({header_naxis:d})", + FITSFixedWarning, + ) - self.get_naxis(header) WCSBase.__init__(self, sip, cpdis, wcsprm, det2im) + if fix: + if header is None: + with warnings.catch_warnings(): + warnings.simplefilter("ignore", FITSFixedWarning) + self.fix(translate_units=translate_units) + else: + self.fix(translate_units=translate_units) + + if _do_set: + self.wcs.set() + + for fd in close_fds: + fd.close() + + self._get_naxis(header) + + self._pixel_bounds = None + def __copy__(self): new_copy = self.__class__() - WCSBase.__init__(new_copy, self.sip, - (self.cpdis1, self.cpdis2), - self.wcs, - (self.det2im1, self.det2im2)) + WCSBase.__init__( + new_copy, + self.sip, + (self.cpdis1, self.cpdis2), + self.wcs, + (self.det2im1, self.det2im2), + ) new_copy.__dict__.update(self.__dict__) return new_copy def __deepcopy__(self, memo): + from copy import deepcopy + new_copy = self.__class__() - new_copy.naxis = copy.deepcopy(self.naxis, memo) - WCSBase.__init__(new_copy, copy.deepcopy(self.sip, memo), - (copy.deepcopy(self.cpdis1, memo), - copy.deepcopy(self.cpdis2, memo)), - copy.deepcopy(self.wcs, memo), - (copy.deepcopy(self.det2im1, memo), - copy.deepcopy(self.det2im2, memo))) - for key in self.__dict__: - val = self.__dict__[key] - new_copy.__dict__[key] = copy.deepcopy(val, memo) + new_copy.naxis = deepcopy(self.naxis, memo) + WCSBase.__init__( + new_copy, + deepcopy(self.sip, memo), + (deepcopy(self.cpdis1, memo), deepcopy(self.cpdis2, memo)), + deepcopy(self.wcs, memo), + (deepcopy(self.det2im1, memo), deepcopy(self.det2im2, memo)), + ) + for key, val in self.__dict__.items(): + new_copy.__dict__[key] = deepcopy(val, memo) return new_copy def copy(self): @@ -329,6 +751,10 @@ def copy(self): Convenience method so user doesn't have to import the :mod:`copy` stdlib module. + + .. warning:: + Use `deepcopy` instead of `copy` unless you know why you need a + shallow copy. """ return copy.copy(self) @@ -343,13 +769,159 @@ def deepcopy(self): def sub(self, axes=None): copy = self.deepcopy() - copy.wcs = self.wcs.sub(axes) + + # We need to know which axes have been dropped, but there is no easy + # way to do this with the .sub function, so instead we assign UUIDs to + # the CNAME parameters in copy.wcs. We can later access the original + # CNAME properties from self.wcs. + cname_uuid = [str(uuid.uuid4()) for i in range(copy.wcs.naxis)] + copy.wcs.cname = cname_uuid + + # Subset the WCS + copy.wcs = copy.wcs.sub(axes) copy.naxis = copy.wcs.naxis + + # Construct a list of dimensions from the original WCS in the order + # in which they appear in the final WCS. + keep = [ + cname_uuid.index(cname) if cname in cname_uuid else None + for cname in copy.wcs.cname + ] + + # Restore the original CNAMEs + copy.wcs.cname = ["" if i is None else self.wcs.cname[i] for i in keep] + + # Subset pixel_shape and pixel_bounds + if self.pixel_shape: + copy.pixel_shape = tuple( + None if i is None else self.pixel_shape[i] for i in keep + ) + if self.pixel_bounds: + copy.pixel_bounds = [ + None if i is None else self.pixel_bounds[i] for i in keep + ] + return copy - if _wcs is not None: - sub.__doc__ = _wcs._Wcsprm.sub.__doc__ - def calcFootprint(self, header=None, undistort=True): + sub.__doc__ = Wcsprm.sub.__doc__ + + def _fix_scamp(self): + """ + Remove SCAMP's PVi_m distortion parameters if SIP distortion parameters + are also present. Some projects (e.g., Palomar Transient Factory) + convert SCAMP's distortion parameters (which abuse the PVi_m cards) to + SIP. However, wcslib gets confused by the presence of both SCAMP and + SIP distortion parameters. + + See https://github.com/astropy/astropy/issues/299. + + SCAMP uses TAN projection exclusively. The case of CTYPE ending + in -TAN should have been handled by ``_fix_pre2012_scamp_tpv()`` before + calling this function. + """ + if self.wcs is None: + return + + # Delete SIP if CTYPE explicitly has '-TPV' code: + ctype = [ct.strip().upper() for ct in self.wcs.ctype] + if sum(ct.endswith("-TPV") for ct in ctype) == 2: + if self.sip is not None: + self.sip = None + warnings.warn( + "Removed redundant SIP distortion parameters " + "because CTYPE explicitly specifies TPV distortions", + FITSFixedWarning, + ) + return + + # Nothing to be done if no PV parameters attached since SCAMP + # encodes distortion coefficients using PV keywords + pv = self.wcs.get_pv() + if not pv: + return + + # Nothing to be done if axes don't use SIP distortion parameters + if self.sip is None: + return + + # Loop over distinct values of `i' index + has_scamp = False + for i in {v[0] for v in pv}: + # Get all values of `j' index for this value of `i' index + js = tuple(v[1] for v in pv if v[0] == i) + if "-TAN" in self.wcs.ctype[i - 1].upper() and js and max(js) >= 5: + # TAN projection *may* use PVi_j with j up to 4 - see + # Sections 2.5, 2.6, and Table 13 + # in https://doi.org/10.1051/0004-6361:20021327 + has_scamp = True + break + + if has_scamp and all(ct.endswith("-SIP") for ct in ctype): + # Prefer SIP - see recommendations in Section 7 in + # http://web.ipac.caltech.edu/staff/shupe/reprints/SIP_to_PV_SPIE2012.pdf + self.wcs.set_pv([]) + warnings.warn( + "Removed redundant SCAMP distortion parameters " + "because SIP parameters are also present", + FITSFixedWarning, + ) + return + + def fix(self, translate_units="", naxis=None): + """ + Perform the fix operations from wcslib, and warn about any + changes it has made. + + Parameters + ---------- + translate_units : str, optional + Specify which potentially unsafe translations of + non-standard unit strings to perform. By default, + performs none. + + Although ``"S"`` is commonly used to represent seconds, + its translation to ``"s"`` is potentially unsafe since the + standard recognizes ``"S"`` formally as Siemens, however + rarely that may be used. The same applies to ``"H"`` for + hours (Henry), and ``"D"`` for days (Debye). + + This string controls what to do in such cases, and is + case-insensitive. + + - If the string contains ``"s"``, translate ``"S"`` to + ``"s"``. + + - If the string contains ``"h"``, translate ``"H"`` to + ``"h"``. + + - If the string contains ``"d"``, translate ``"D"`` to + ``"d"``. + + Thus ``''`` doesn't do any unsafe translations, whereas + ``'shd'`` does all of them. + + naxis : int array, optional + Image axis lengths. If this array is set to zero or + ``None``, then `~astropy.wcs.Wcsprm.cylfix` will not be + invoked. + """ + if self.wcs is not None: + self._fix_scamp() + fixes = self.wcs.fix(translate_units, naxis) + for key, val in fixes.items(): + if val != "No change": + if ( + key == "datfix" + and "1858-11-17" in val + and not np.count_nonzero(self.wcs.mjdref) + ): + continue + warnings.warn( + f"'{key}' made the change '{val}'.", + FITSFixedWarning, + ) + + def calc_footprint(self, header=None, undistort=True, axes=None, center=True): """ Calculates the footprint of the image on the sky. @@ -359,56 +931,75 @@ def calcFootprint(self, header=None, undistort=True): Parameters ---------- - header : astropy.io.fits header object, optional + header : `~astropy.io.fits.Header` object, optional + Used to get ``NAXIS1`` and ``NAXIS2`` + header and axes are mutually exclusive, alternative ways + to provide the same information. undistort : bool, optional If `True`, take SIP and distortion lookup table into account + axes : (int, int), optional + If provided, use the given sequence as the shape of the + image. Otherwise, use the ``NAXIS1`` and ``NAXIS2`` + keywords from the header that was used to create this + `WCS` object. + + center : bool, optional + If `True` use the center of the pixel, otherwise use the corner. + Returns ------- coord : (4, 2) array of (*x*, *y*) coordinates. + The order is clockwise starting with the bottom left corner. """ - if header is None: - try: - # classes that inherit from WCS and define naxis1/2 - # do not require a header parameter - naxis1 = self.naxis1 - naxis2 = self.naxis2 - except AttributeError: - print("Need a valid header in order to calculate footprint\n") - return None + if axes is not None: + naxis1, naxis2 = axes else: - naxis1 = header.get('NAXIS1', None) - naxis2 = header.get('NAXIS2', None) + if header is None: + try: + # classes that inherit from WCS and define naxis1/2 + # do not require a header parameter + naxis1, naxis2 = self.pixel_shape + except (AttributeError, TypeError): + warnings.warn( + "Need a valid header in order to calculate footprint\n", + AstropyUserWarning, + ) + return None + else: + naxis1 = header.get("NAXIS1", None) + naxis2 = header.get("NAXIS2", None) - corners = np.zeros(shape=(4, 2), dtype=np.float64) if naxis1 is None or naxis2 is None: - return None + raise ValueError("Image size could not be determined.") + + if center: + corners = np.array( + [[1, 1], [1, naxis2], [naxis1, naxis2], [naxis1, 1]], dtype=np.float64 + ) + else: + corners = np.array( + [ + [0.5, 0.5], + [0.5, naxis2 + 0.5], + [naxis1 + 0.5, naxis2 + 0.5], + [naxis1 + 0.5, 0.5], + ], + dtype=np.float64, + ) - corners[0, 0] = 1. - corners[0, 1] = 1. - corners[1, 0] = 1. - corners[1, 1] = naxis2 - corners[2, 0] = naxis1 - corners[2, 1] = naxis2 - corners[3, 0] = naxis1 - corners[3, 1] = 1. if undistort: return self.all_pix2world(corners, 1) else: return self.wcs_pix2world(corners, 1) - def _read_det2im_kw(self, header, fobj): + def _read_det2im_kw(self, header, fobj, err=0.0): """ - Create a `Paper IV`_ type lookup table for detector to image - plane correction. + Create a `distortion paper`_ type lookup table for detector to + image plane correction. """ - cpdis = [None, None] - crpix = [0., 0.] - crval = [0., 0.] - cdelt = [1., 1.] - if fobj is None: return (None, None) @@ -416,110 +1007,224 @@ def _read_det2im_kw(self, header, fobj): return (None, None) try: - d2im_data = fobj[('D2IMARR', 1)].data + axiscorr = header["AXISCORR"] + d2imdis = self._read_d2im_old_format(header, fobj, axiscorr) + return d2imdis + except KeyError: + pass + + dist = "D2IMDIS" + d_kw = "D2IM" + err_kw = "D2IMERR" + tables = {} + for i in range(1, self.naxis + 1): + d_error = header.get(err_kw + str(i), 0.0) + if d_error < err: + tables[i] = None + continue + distortion = dist + str(i) + if distortion in header: + dis = header[distortion].lower() + if dis == "lookup": + del header[distortion] + assert isinstance(fobj, fits.HDUList), ( + "An astropy.io.fits.HDUList" + "is required for Lookup table distortion." + ) + dp = (d_kw + str(i)).strip() + dp_extver_key = dp + ".EXTVER" + if dp_extver_key in header: + d_extver = header[dp_extver_key] + del header[dp_extver_key] + else: + d_extver = 1 + dp_axis_key = dp + f".AXIS.{i:d}" + if i == header[dp_axis_key]: + d_data = fobj["D2IMARR", d_extver].data + else: + d_data = (fobj["D2IMARR", d_extver].data).transpose() + del header[dp_axis_key] + d_header = fobj["D2IMARR", d_extver].header + d_crpix = (d_header.get("CRPIX1", 0.0), d_header.get("CRPIX2", 0.0)) + d_crval = (d_header.get("CRVAL1", 0.0), d_header.get("CRVAL2", 0.0)) + d_cdelt = (d_header.get("CDELT1", 1.0), d_header.get("CDELT2", 1.0)) + d_lookup = DistortionLookupTable(d_data, d_crpix, d_crval, d_cdelt) + tables[i] = d_lookup + else: + warnings.warn( + "Polynomial distortion is not implemented.\n", + AstropyUserWarning, + ) + for key in set(header): + if key.startswith(dp + "."): + del header[key] + else: + tables[i] = None + if not tables: + return (None, None) + else: + return (tables.get(1), tables.get(2)) + + def _read_d2im_old_format(self, header, fobj, axiscorr): + warnings.warn( + "The use of ``AXISCORR`` for D2IM correction has been" + " deprecated.`~astropy.wcs` will read in files with ``AXISCORR`` but" + " ``to_fits()`` will write out files without it.", + AstropyDeprecationWarning, + ) + cpdis = [None, None] + crpix = [0.0, 0.0] + crval = [0.0, 0.0] + cdelt = [1.0, 1.0] + try: + d2im_data = fobj[("D2IMARR", 1)].data except KeyError: return (None, None) except AttributeError: return (None, None) + d2im_data = np.array([d2im_data]) - d2im_hdr = fobj[('D2IMARR', 1)].header - naxis = d2im_hdr['NAXIS'] + d2im_hdr = fobj[("D2IMARR", 1)].header + naxis = d2im_hdr["NAXIS"] for i in range(1, naxis + 1): - crpix[i - 1] = d2im_hdr.get('CRPIX' + str(i), 0.0) - crval[i - 1] = d2im_hdr.get('CRVAL' + str(i), 0.0) - cdelt[i - 1] = d2im_hdr.get('CDELT' + str(i), 1.0) + crpix[i - 1] = d2im_hdr.get("CRPIX" + str(i), 0.0) + crval[i - 1] = d2im_hdr.get("CRVAL" + str(i), 0.0) + cdelt[i - 1] = d2im_hdr.get("CDELT" + str(i), 1.0) cpdis = DistortionLookupTable(d2im_data, crpix, crval, cdelt) - axiscorr = header.get('AXISCORR', None) - if axiscorr == 1: return (cpdis, None) - else: + elif axiscorr == 2: return (None, cpdis) + else: + warnings.warn("Expected AXISCORR to be 1 or 2", AstropyUserWarning) + return (None, None) def _write_det2im(self, hdulist): """ - Writes a Paper IV type lookup table to the given - `astropy.io.fits.HDUList`. + Writes a `distortion paper`_ type lookup table to the given + `~astropy.io.fits.HDUList`. """ - - det2im1 = self.det2im1 - det2im2 = self.det2im2 - if det2im1 is not None and det2im2 is None: - hdulist[0].header.update('AXISCORR', 1) - det2im = det2im1 - elif det2im1 is None and det2im2 is not None: - hdulist[0].header.update('AXISCORR', 0) - det2im = det2im2 - elif det2im1 is None and det2im2 is None: + if self.det2im1 is None and self.det2im2 is None: return - else: - raise ValueError("Saving both distortion images is not supported") + dist = "D2IMDIS" + d_kw = "D2IM" - image = fits.ImageHDU(det2im.data[0], name='D2IMARR') - header = image.header + def write_d2i(num, det2im): + if det2im is None: + return + + hdulist[0].header[f"{dist}{num:d}"] = ( + "LOOKUP", + "Detector to image correction type", + ) + hdulist[0].header[f"{d_kw}{num:d}.EXTVER"] = ( + num, + "Version number of WCSDVARR extension", + ) + hdulist[0].header[f"{d_kw}{num:d}.NAXES"] = ( + len(det2im.data.shape), + "Number of independent variables in D2IM function", + ) + + for i in range(det2im.data.ndim): + jth = {1: "1st", 2: "2nd", 3: "3rd"}.get(i + 1, f"{i + 1}th") + hdulist[0].header[f"{d_kw}{num:d}.AXIS.{i + 1:d}"] = ( + i + 1, + f"Axis number of the {jth} variable in a D2IM function", + ) + + image = fits.ImageHDU(det2im.data, name="D2IMARR") + header = image.header - header.update('CRPIX1', det2im.crpix[0]) - header.update('CRPIX2', det2im.crpix[1]) - header.update('CRVAL1', det2im.crval[0]) - header.update('CRVAL2', det2im.crval[1]) - header.update('CDELT1', det2im.cdelt[0]) - header.update('CDELT2', det2im.cdelt[1]) + header["CRPIX1"] = (det2im.crpix[0], "Coordinate system reference pixel") + header["CRPIX2"] = (det2im.crpix[1], "Coordinate system reference pixel") + header["CRVAL1"] = ( + det2im.crval[0], + "Coordinate system value at reference pixel", + ) + header["CRVAL2"] = ( + det2im.crval[1], + "Coordinate system value at reference pixel", + ) + header["CDELT1"] = (det2im.cdelt[0], "Coordinate increment along axis") + header["CDELT2"] = (det2im.cdelt[1], "Coordinate increment along axis") + image.ver = int(hdulist[0].header[f"{d_kw}{num:d}.EXTVER"]) + hdulist.append(image) - hdulist.append(image) + write_d2i(1, self.det2im1) + write_d2i(2, self.det2im2) - def _read_distortion_kw(self, header, fobj, dist='CPDIS', err=0.0): + def _read_distortion_kw(self, header, fobj, dist="CPDIS", err=0.0): """ - Reads `Paper IV`_ table-lookup distortion keywords and data, - and returns a 2-tuple of `~astropy.wcs.DistortionLookupTable` + Reads `distortion paper`_ table-lookup keywords and data, and + returns a 2-tuple of `~astropy.wcs.DistortionLookupTable` objects. - If no `Paper IV`_ distortion keywords are found, ``(None, - None)`` is returned. + If no `distortion paper`_ keywords are found, ``(None, None)`` + is returned. """ - if isinstance(header, string_types): + if isinstance(header, (str, bytes)): return (None, None) - if dist == 'CPDIS': - d_kw = 'DP' - err_kw = 'CPERR' + if dist == "CPDIS": + d_kw = "DP" + err_kw = "CPERR" else: - d_kw = 'DQ' - err_kw = 'CQERR' + d_kw = "DQ" + err_kw = "CQERR" tables = {} for i in range(1, self.naxis + 1): - d_error = header.get(err_kw + str(i), 0.0) + d_error_key = err_kw + str(i) + if d_error_key in header: + d_error = header[d_error_key] + del header[d_error_key] + else: + d_error = 0.0 if d_error < err: tables[i] = None continue distortion = dist + str(i) if distortion in header: dis = header[distortion].lower() - if dis == 'lookup': - assert isinstance(fobj, fits.HDUList), \ - 'An astropy.io.fits.HDUList is required for ' + \ - 'Lookup table distortion.' + del header[distortion] + if dis == "lookup": + if not isinstance(fobj, fits.HDUList): + raise ValueError( + "an astropy.io.fits.HDUList is " + "required for Lookup table distortion." + ) dp = (d_kw + str(i)).strip() - d_extver = header.get(dp + '.EXTVER', 1) - if i == header[dp + '.AXIS.' + str(i)]: - d_data = fobj['WCSDVARR', d_extver].data + dp_extver_key = dp + ".EXTVER" + if dp_extver_key in header: + d_extver = header[dp_extver_key] + del header[dp_extver_key] else: - d_data = (fobj['WCSDVARR', d_extver].data).transpose() - d_header = fobj['WCSDVARR', d_extver].header - d_crpix = (d_header.get('CRPIX1', 0.0), - d_header.get('CRPIX2', 0.0)) - d_crval = (d_header.get('CRVAL1', 0.0), - d_header.get('CRVAL2', 0.0)) - d_cdelt = (d_header.get('CDELT1', 1.0), - d_header.get('CDELT2', 1.0)) - d_lookup = DistortionLookupTable(d_data, d_crpix, - d_crval, d_cdelt) + d_extver = 1 + dp_axis_key = dp + f".AXIS.{i:d}" + if i == header[dp_axis_key]: + d_data = fobj["WCSDVARR", d_extver].data + else: + d_data = (fobj["WCSDVARR", d_extver].data).transpose() + del header[dp_axis_key] + d_header = fobj["WCSDVARR", d_extver].header + d_crpix = (d_header.get("CRPIX1", 0.0), d_header.get("CRPIX2", 0.0)) + d_crval = (d_header.get("CRVAL1", 0.0), d_header.get("CRVAL2", 0.0)) + d_cdelt = (d_header.get("CDELT1", 1.0), d_header.get("CDELT2", 1.0)) + d_lookup = DistortionLookupTable(d_data, d_crpix, d_crval, d_cdelt) tables[i] = d_lookup + + for key in set(header): + if key.startswith(dp + "."): + del header[key] else: - print('Polynomial distortion is not implemented.\n') + warnings.warn( + "Polynomial distortion is not implemented.\n", + AstropyUserWarning, + ) else: tables[i] = None @@ -528,103 +1233,244 @@ def _read_distortion_kw(self, header, fobj, dist='CPDIS', err=0.0): else: return (tables.get(1), tables.get(2)) - def _write_distortion_kw(self, hdulist, dist='CPDIS'): + def _write_distortion_kw(self, hdulist, dist="CPDIS"): """ - Write out Paper IV distortion keywords to the given - `fits.HDUList`. + Write out `distortion paper`_ keywords to the given + `~astropy.io.fits.HDUList`. """ if self.cpdis1 is None and self.cpdis2 is None: return - if dist == 'CPDIS': - d_kw = 'DP' - err_kw = 'CPERR' + if dist == "CPDIS": + d_kw = "DP" else: - d_kw = 'DQ' - err_kw = 'CQERR' + d_kw = "DQ" def write_dist(num, cpdis): if cpdis is None: return - hdulist[0].header.update('{0}{1:d}'.format(dist, num), 'LOOKUP') - hdulist[0].header.update('{0}{1:d}.EXTVER'.format(d_kw, num), - len(hdulist)) - hdulist[0].header.update('{0}{1:d}.AXIS.{1:d}'.format(d_kw, num), - num) - - image = fits.ImageHDU(cpdis.data, name='WCSDVARR') + hdulist[0].header[f"{dist}{num:d}"] = ( + "LOOKUP", + "Prior distortion function type", + ) + hdulist[0].header[f"{d_kw}{num:d}.EXTVER"] = ( + num, + "Version number of WCSDVARR extension", + ) + hdulist[0].header[f"{d_kw}{num:d}.NAXES"] = ( + len(cpdis.data.shape), + f"Number of independent variables in {dist} function", + ) + + for i in range(cpdis.data.ndim): + jth = {1: "1st", 2: "2nd", 3: "3rd"}.get(i + 1, f"{i + 1}th") + hdulist[0].header[f"{d_kw}{num:d}.AXIS.{i + 1:d}"] = ( + i + 1, + f"Axis number of the {jth} variable in a {dist} function", + ) + + image = fits.ImageHDU(cpdis.data, name="WCSDVARR") header = image.header - header.update('CRPIX1', cpdis.crpix[0]) - header.update('CRPIX2', cpdis.crpix[1]) - header.update('CRVAL1', cpdis.crval[0]) - header.update('CRVAL2', cpdis.crval[1]) - header.update('CDELT1', cpdis.cdelt[0]) - header.update('CDELT2', cpdis.cdelt[1]) - + header["CRPIX1"] = (cpdis.crpix[0], "Coordinate system reference pixel") + header["CRPIX2"] = (cpdis.crpix[1], "Coordinate system reference pixel") + header["CRVAL1"] = ( + cpdis.crval[0], + "Coordinate system value at reference pixel", + ) + header["CRVAL2"] = ( + cpdis.crval[1], + "Coordinate system value at reference pixel", + ) + header["CDELT1"] = (cpdis.cdelt[0], "Coordinate increment along axis") + header["CDELT2"] = (cpdis.cdelt[1], "Coordinate increment along axis") + image.ver = int(hdulist[0].header[f"{d_kw}{num:d}.EXTVER"]) hdulist.append(image) write_dist(1, self.cpdis1) write_dist(2, self.cpdis2) - def _read_sip_kw(self, header): + def _fix_pre2012_scamp_tpv(self, header, wcskey=""): + """ + Replace -TAN with TPV (for pre-2012 SCAMP headers that use -TAN + in CTYPE). Ignore SIP if present. This follows recommendations in + Section 7 in + http://web.ipac.caltech.edu/staff/shupe/reprints/SIP_to_PV_SPIE2012.pdf. + + This is to deal with pre-2012 headers that may contain TPV with a + CTYPE that ends in '-TAN' (post-2012 they should end in '-TPV' when + SCAMP has adopted the new TPV convention). + """ + if isinstance(header, (str, bytes)): + return + + wcskey = wcskey.strip().upper() + cntype = [ + (nax, header.get(f"CTYPE{nax}{wcskey}", "").strip()) + for nax in range(1, self.naxis + 1) + ] + + tan_axes = [ct[0] for ct in cntype if ct[1].endswith("-TAN")] + + if len(tan_axes) == 2: + # check if PVi_j with j >= 5 is present and if so, do not load SIP + tan_to_tpv = False + for nax in tan_axes: + js = [] + for p in header[f"PV{nax}_*{wcskey}"].keys(): + prefix = f"PV{nax}_" + if p.startswith(prefix): + p = p[len(prefix) :] + p = p.rstrip(wcskey) + try: + p = int(p) + except ValueError: + continue + js.append(p) + + if js and max(js) >= 5: + tan_to_tpv = True + break + + if tan_to_tpv: + warnings.warn( + "Removed redundant SIP distortion parameters " + "because SCAMP' PV distortions are also present", + FITSFixedWarning, + ) + self._remove_sip_kw(header, del_order=True) + for i in tan_axes: + kwd = f"CTYPE{i:d}{wcskey}" + if kwd in header: + header[kwd] = ( + header[kwd].strip().upper().replace("-TAN", "-TPV") + ) + + @staticmethod + def _remove_sip_kw(header, del_order=False): + """ + Remove SIP information from a header. + """ + # Never pass SIP coefficients to wcslib + # CTYPE must be passed with -SIP to wcslib + for key in { + m.group() for m in map(SIP_KW.match, list(header)) if m is not None + }: + del header[key] + + if del_order: + for kwd in ["A_ORDER", "B_ORDER", "AP_ORDER", "BP_ORDER"]: + if kwd in header: + del header[kwd] + + def _read_sip_kw(self, header, wcskey=""): """ Reads `SIP`_ header keywords and returns a `~astropy.wcs.Sip` object. If no `SIP`_ header keywords are found, ``None`` is returned. """ - if isinstance(header, string_types): + if isinstance(header, (str, bytes)): # TODO: Parse SIP from a string without pyfits around return None - if "A_ORDER" in header: + if "A_ORDER" in header and header["A_ORDER"] > 1: if "B_ORDER" not in header: raise ValueError( "A_ORDER provided without corresponding B_ORDER " - "keyword for SIP distortion") + "keyword for SIP distortion" + ) m = int(header["A_ORDER"]) a = np.zeros((m + 1, m + 1), np.double) for i in range(m + 1): for j in range(m - i + 1): - a[i, j] = header.get(("A_{0}_{1}".format(i, j)), 0.0) + key = f"A_{i}_{j}" + if key in header: + a[i, j] = header[key] + del header[key] m = int(header["B_ORDER"]) - b = np.zeros((m + 1, m + 1), np.double) - for i in range(m + 1): - for j in range(m - i + 1): - b[i, j] = header.get(("B_{0}_{1}".format(i, j)), 0.0) - elif "B_ORDER" in header: + if m > 1: + b = np.zeros((m + 1, m + 1), np.double) + for i in range(m + 1): + for j in range(m - i + 1): + key = f"B_{i}_{j}" + if key in header: + b[i, j] = header[key] + del header[key] + else: + a = None + b = None + + del header["A_ORDER"] + del header["B_ORDER"] + + ctype = [header[f"CTYPE{nax}{wcskey}"] for nax in range(1, self.naxis + 1)] + if any(not ctyp.endswith("-SIP") for ctyp in ctype): + message = """ + Inconsistent SIP distortion information is present in the FITS header and the WCS object: + SIP coefficients were detected, but CTYPE is missing a "-SIP" suffix. + astropy.wcs is using the SIP distortion coefficients, + therefore the coordinates calculated here might be incorrect. + + If you do not want to apply the SIP distortion coefficients, + please remove the SIP coefficients from the FITS header or the + WCS object. As an example, if the image is already distortion-corrected + (e.g., drizzled) then distortion components should not apply and the SIP + coefficients should be removed. + + While the SIP distortion coefficients are being applied here, if that was indeed the intent, + for consistency please append "-SIP" to the CTYPE in the FITS header or the WCS object. + + """ + log.info(message) + elif "B_ORDER" in header and header["B_ORDER"] > 1: raise ValueError( - "B_ORDER provided without corresponding A_ORDER " + - "keyword for SIP distortion") + "B_ORDER provided without corresponding A_ORDER " + "keyword for SIP distortion" + ) else: a = None b = None - if "AP_ORDER" in header: + if "AP_ORDER" in header and header["AP_ORDER"] > 1: if "BP_ORDER" not in header: raise ValueError( "AP_ORDER provided without corresponding BP_ORDER " - "keyword for SIP distortion") + "keyword for SIP distortion" + ) m = int(header["AP_ORDER"]) ap = np.zeros((m + 1, m + 1), np.double) for i in range(m + 1): for j in range(m - i + 1): - ap[i, j] = header.get("AP_{0}_{1}".format(i, j), 0.0) + key = f"AP_{i}_{j}" + if key in header: + ap[i, j] = header[key] + del header[key] m = int(header["BP_ORDER"]) - bp = np.zeros((m + 1, m + 1), np.double) - for i in range(m + 1): - for j in range(m - i + 1): - bp[i, j] = header.get("BP_{0}_{1}".format(i, j), 0.0) - elif "BP_ORDER" in header: + if m > 1: + bp = np.zeros((m + 1, m + 1), np.double) + for i in range(m + 1): + for j in range(m - i + 1): + key = f"BP_{i}_{j}" + if key in header: + bp[i, j] = header[key] + del header[key] + else: + ap = None + bp = None + + del header["AP_ORDER"] + del header["BP_ORDER"] + elif "BP_ORDER" in header and header["BP_ORDER"] > 1: raise ValueError( "BP_ORDER provided without corresponding AP_ORDER " - "keyword for SIP distortion") + "keyword for SIP distortion" + ) else: ap = None bp = None @@ -632,12 +1478,11 @@ def _read_sip_kw(self, header): if a is None and b is None and ap is None and bp is None: return None - if "CRPIX1" not in header or "CRPIX2" not in header: - raise ValueError( - "Header has SIP keywords without CRPIX keywords") + if f"CRPIX1{wcskey}" not in header or f"CRPIX2{wcskey}" not in header: + raise ValueError("Header has SIP keywords without CRPIX keywords") - crpix1 = header.get("CRPIX1") - crpix2 = header.get("CRPIX2") + crpix1 = header.get(f"CRPIX1{wcskey}") + crpix2 = header.get(f"CRPIX2{wcskey}") return Sip(a, b, ap, bp, (crpix1, crpix2)) @@ -655,31 +1500,36 @@ def write_array(name, a): if a is None: return size = a.shape[0] - keywords['{0}_ORDER'.format(name)] = size - 1 + trdir = "sky to detector" if name[-1] == "P" else "detector to sky" + comment = ( + f"SIP polynomial order, axis {ord(name[0]) - ord('A'):d}, {trdir:s}" + ) + keywords[f"{name}_ORDER"] = size - 1, comment + + comment = "SIP distortion coefficient" for i in range(size): for j in range(size - i): if a[i, j] != 0.0: - keywords['{0}_{1:d}_{2:d}'.format(name, i, j)] = a[i, j] - - write_array('A', self.sip.a) - write_array('B', self.sip.b) - write_array('AP', self.sip.ap) - write_array('BP', self.sip.bp) + keywords[f"{name}_{i:d}_{j:d}"] = a[i, j], comment - keywords['CRPIX1'] = self.sip.crpix[0] - keywords['CRPIX2'] = self.sip.crpix[1] + write_array("A", self.sip.a) + write_array("B", self.sip.b) + write_array("AP", self.sip.ap) + write_array("BP", self.sip.bp) return keywords def _denormalize_sky(self, sky): - if self.wcs.lngtyp != 'RA': + if self.wcs.lngtyp != "RA": raise ValueError( - "WCS does not have longitude type of 'RA', therefore " + - "(ra, dec) data can not be used as input") - if self.wcs.lattype != 'DEC': + "WCS does not have longitude type of 'RA', therefore " + "(ra, dec) data can not be used as input" + ) + if self.wcs.lattyp != "DEC": raise ValueError( - "WCS does not have longitude type of 'DEC', therefore " + - "(ra, dec) data can not be used as input") + "WCS does not have longitude type of 'DEC', therefore " + "(ra, dec) data can not be used as input" + ) if self.wcs.naxis == 2: if self.wcs.lng == 0 and self.wcs.lat == 1: return sky @@ -688,28 +1538,32 @@ def _denormalize_sky(self, sky): return sky[:, ::-1] else: raise ValueError( - "WCS does not have longitude and latitude celestial " + - "axes, therefore (ra, dec) data can not be used as input") + "WCS does not have longitude and latitude celestial " + "axes, therefore (ra, dec) data can not be used as input" + ) else: if self.wcs.lng < 0 or self.wcs.lat < 0: raise ValueError( "WCS does not have both longitude and latitude " - "celestial axes, therefore (ra, dec) data can not be " + - "used as input") + "celestial axes, therefore (ra, dec) data can not be " + "used as input" + ) out = np.zeros((sky.shape[0], self.wcs.naxis)) out[:, self.wcs.lng] = sky[:, 0] out[:, self.wcs.lat] = sky[:, 1] return out def _normalize_sky(self, sky): - if self.wcs.lngtyp != 'RA': + if self.wcs.lngtyp != "RA": raise ValueError( - "WCS does not have longitude type of 'RA', therefore " + - "(ra, dec) data can not be returned") - if self.wcs.lattype != 'DEC': + "WCS does not have longitude type of 'RA', therefore " + "(ra, dec) data can not be returned" + ) + if self.wcs.lattyp != "DEC": raise ValueError( - "WCS does not have longitude type of 'DEC', therefore " + - "(ra, dec) data can not be returned") + "WCS does not have longitude type of 'DEC', therefore " + "(ra, dec) data can not be returned" + ) if self.wcs.naxis == 2: if self.wcs.lng == 0 and self.wcs.lat == 1: return sky @@ -719,96 +1573,136 @@ def _normalize_sky(self, sky): else: raise ValueError( "WCS does not have longitude and latitude celestial " - "axes, therefore (ra, dec) data can not be returned") + "axes, therefore (ra, dec) data can not be returned" + ) else: if self.wcs.lng < 0 or self.wcs.lat < 0: raise ValueError( "WCS does not have both longitude and latitude celestial " - "axes, therefore (ra, dec) data can not be returned") + "axes, therefore (ra, dec) data can not be returned" + ) out = np.empty((sky.shape[0], 2)) out[:, 0] = sky[:, self.wcs.lng] out[:, 1] = sky[:, self.wcs.lat] return out - def _array_converter(self, func, sky, *args, **kwargs): + def _array_converter(self, func, sky, *args, ra_dec_order=False): """ A helper function to support reading either a pair of arrays or a single Nx2 array. """ - ra_dec_order = kwargs.get('ra_dec_order') - if len(args) == 2: - xy, origin = args + + def _return_list_of_arrays(axes, origin): + if any(x.size == 0 for x in axes): + return axes + try: - xy = np.asarray(xy) - origin = int(origin) - except: - raise TypeError( - "When providing two arguments, they must be (xy, origin)") - if ra_dec_order and sky == 'input': + axes = np.broadcast_arrays(*axes) + except ValueError: + raise ValueError( + "Coordinate arrays are not broadcastable to each other" + ) + + xy = np.hstack([x.reshape((x.size, 1)) for x in axes]) + + if ra_dec_order and sky == "input": + xy = self._denormalize_sky(xy) + output = func(xy, origin) + if ra_dec_order and sky == "output": + output = self._normalize_sky(output) + return ( + output[:, 0].reshape(axes[0].shape), + output[:, 1].reshape(axes[0].shape), + ) + return [output[:, i].reshape(axes[0].shape) for i in range(output.shape[1])] + + def _return_single_array(xy, origin): + if xy.shape[-1] != self.naxis: + raise ValueError( + "When providing two arguments, the array must be " + f"of shape (N, {self.naxis})" + ) + if 0 in xy.shape: + return xy + if ra_dec_order and sky == "input": xy = self._denormalize_sky(xy) result = func(xy, origin) - if ra_dec_order and sky == 'output': + if ra_dec_order and sky == "output": result = self._normalize_sky(result) return result - elif len(args) == 3: - x, y, origin = args + + if len(args) == 2: try: - x = np.asarray(x) - y = np.asarray(y) + xy, origin = args + xy = np.asarray(xy) origin = int(origin) - except: + except Exception: raise TypeError( - "When providing three arguments, they must be " + - "(x, y, origin)") - if x.size != y.size: - raise ValueError("x and y arrays are not the same size") - length = x.size - xy = np.hstack((x.reshape((length, 1)), - y.reshape((length, 1)))) - if ra_dec_order and sky == 'input': - xy = self._denormalize_sky(xy) - sky = func(xy, origin) - if ra_dec_order and sky == 'output': - sky = self._normalize_sky_output(sky) - return sky[:, 0], sky[:, 1] - return [sky[:, i] for i in range(sky.shape[1])] + "When providing two arguments, they must be " + f"(coords[N][{self.naxis}], origin)" + ) + if xy.shape == () or len(xy.shape) == 1: + return _return_list_of_arrays([xy], origin) + return _return_single_array(xy, origin) + + elif len(args) == self.naxis + 1: + axes = args[:-1] + origin = args[-1] + try: + axes = [np.asarray(x) for x in axes] + origin = int(origin) + except Exception: + raise TypeError( + "When providing more than two arguments, they must be " + "a 1-D array for each axis, followed by an origin." + ) + + return _return_list_of_arrays(axes, origin) + raise TypeError( - "Expected 2 or 3 arguments, {0} given".format(len(args))) + f"WCS projection has {self.naxis} dimensions, so expected 2 (an Nx{self.naxis} array " + f"and the origin argument) or {self.naxis + 1} arguments (the position in each " + f"dimension, and the origin argument). Instead, {len(args)} arguments were " + "given." + ) def all_pix2world(self, *args, **kwargs): - return self._array_converter( - self._all_pix2world, 'output', *args, **kwargs) - all_pix2world.__doc__ = """ + return self._array_converter(self._all_pix2world, "output", *args, **kwargs) + + all_pix2world.__doc__ = f""" Transforms pixel coordinates to world coordinates. - Performs all of the following in order: + Performs all of the following in series: - - Detector to image plane correction (optionally) + - Detector to image plane correction (if present in the + FITS file) - - `SIP`_ distortion correction (optionally) + - `SIP`_ distortion correction (if present in the FITS + file) - - `Paper IV`_ table-lookup distortion correction (optionally) + - `distortion paper`_ table-lookup correction (if present + in the FITS file) - - `wcslib`_ WCS transformation + - `wcslib`_ "core" WCS transformation Parameters ---------- - {0} + {docstrings.TWO_OR_MORE_ARGS("naxis", 8)} For a transformation that is not two-dimensional, the two-argument form must be used. - {1} + {docstrings.RA_DEC_ORDER(8)} Returns ------- - {2} + {docstrings.RETURNS("sky coordinates, in degrees", 8)} Notes ----- The order of the axes for the result is determined by the - `CTYPEia` keywords in the FITS header, therefore it may not + ``CTYPEia`` keywords in the FITS header, therefore it may not always be of the form (*ra*, *dec*). The `~astropy.wcs.Wcsprm.lat`, `~astropy.wcs.Wcsprm.lng`, `~astropy.wcs.Wcsprm.lattyp` and `~astropy.wcs.Wcsprm.lngtyp` @@ -839,25 +1733,20 @@ def all_pix2world(self, *args, **kwargs): InvalidTransformError Ill-conditioned coordinate transformation parameters. - """.format(__.TWO_OR_THREE_ARGS('naxis', 8), - __.RA_DEC_ORDER(8), - __.RETURNS('sky coordinates, in degrees', 8)) - - @deprecated("0.0", name="all_pix2sky", alternative="all_pix2world") - def all_pix2sky(self, *args, **kwargs): - return self.all_pix2world(*args, **kwargs) + """ def wcs_pix2world(self, *args, **kwargs): if self.wcs is None: raise ValueError("No basic WCS settings were created.") return self._array_converter( - lambda xy, o: self.wcs.p2s(xy, o)['world'], - 'output', *args, **kwargs) - wcs_pix2world.__doc__ = """ + lambda xy, o: self.wcs.p2s(xy, o)["world"], "output", *args, **kwargs + ) + + wcs_pix2world.__doc__ = f""" Transforms pixel coordinates to world coordinates by doing only the basic `wcslib`_ transformation. - No `SIP`_ or `Paper IV`_ table lookup distortion correction is + No `SIP`_ or `distortion paper`_ table lookup correction is applied. To perform distortion correction, see `~astropy.wcs.WCS.all_pix2world`, `~astropy.wcs.WCS.sip_pix2foc`, `~astropy.wcs.WCS.p4_pix2foc`, @@ -865,17 +1754,17 @@ def wcs_pix2world(self, *args, **kwargs): Parameters ---------- - {0} + {docstrings.TWO_OR_MORE_ARGS("naxis", 8)} For a transformation that is not two-dimensional, the two-argument form must be used. - {1} + {docstrings.RA_DEC_ORDER(8)} Returns ------- - {2} + {docstrings.RETURNS("world coordinates, in degrees", 8)} Raises ------ @@ -906,49 +1795,800 @@ def wcs_pix2world(self, *args, **kwargs): Notes ----- The order of the axes for the result is determined by the - `CTYPEia` keywords in the FITS header, therefore it may not + ``CTYPEia`` keywords in the FITS header, therefore it may not always be of the form (*ra*, *dec*). The `~astropy.wcs.Wcsprm.lat`, `~astropy.wcs.Wcsprm.lng`, `~astropy.wcs.Wcsprm.lattyp` and `~astropy.wcs.Wcsprm.lngtyp` members can be used to determine the order of the axes. - """.format(__.TWO_OR_THREE_ARGS('naxis', 8), - __.RA_DEC_ORDER(8), - __.RETURNS('world coordinates, in degrees', 8)) + """ + + def _all_world2pix( + self, world, origin, tolerance, maxiter, adaptive, detect_divergence, quiet + ): + # ############################################################ + # # DESCRIPTION OF THE NUMERICAL METHOD ## + # ############################################################ + # In this section I will outline the method of solving + # the inverse problem of converting world coordinates to + # pixel coordinates (*inverse* of the direct transformation + # `all_pix2world`) and I will summarize some of the aspects + # of the method proposed here and some of the issues of the + # original `all_world2pix` (in relation to this method) + # discussed in https://github.com/astropy/astropy/issues/1977 + # A more detailed discussion can be found here: + # https://github.com/astropy/astropy/pull/2373 + # + # + # ### Background ### + # + # + # I will refer here to the [SIP Paper] + # (http://fits.gsfc.nasa.gov/registry/sip/SIP_distortion_v1_0.pdf). + # According to this paper, the effect of distortions as + # described in *their* equation (1) is: + # + # (1) x = CD*(u+f(u)), + # + # where `x` is a *vector* of "intermediate spherical + # coordinates" (equivalent to (x,y) in the paper) and `u` + # is a *vector* of "pixel coordinates", and `f` is a vector + # function describing geometrical distortions + # (see equations 2 and 3 in SIP Paper. + # However, I prefer to use `w` for "intermediate world + # coordinates", `x` for pixel coordinates, and assume that + # transformation `W` performs the **linear** + # (CD matrix + projection onto celestial sphere) part of the + # conversion from pixel coordinates to world coordinates. + # Then we can re-write (1) as: + # + # (2) w = W*(x+f(x)) = T(x) + # + # In `astropy.wcs.WCS` transformation `W` is represented by + # the `wcs_pix2world` member, while the combined ("total") + # transformation (linear part + distortions) is performed by + # `all_pix2world`. Below I summarize the notations and their + # equivalents in `astropy.wcs.WCS`: + # + # | Equation term | astropy.WCS/meaning | + # | ------------- | ---------------------------- | + # | `x` | pixel coordinates | + # | `w` | world coordinates | + # | `W` | `wcs_pix2world()` | + # | `W^{-1}` | `wcs_world2pix()` | + # | `T` | `all_pix2world()` | + # | `x+f(x)` | `pix2foc()` | + # + # + # ### Direct Solving of Equation (2) ### + # + # + # In order to find the pixel coordinates that correspond to + # given world coordinates `w`, it is necessary to invert + # equation (2): `x=T^{-1}(w)`, or solve equation `w==T(x)` + # for `x`. However, this approach has the following + # disadvantages: + # 1. It requires unnecessary transformations (see next + # section). + # 2. It is prone to "RA wrapping" issues as described in + # https://github.com/astropy/astropy/issues/1977 + # (essentially because `all_pix2world` may return points with + # a different phase than user's input `w`). + # + # + # ### Description of the Method Used here ### + # + # + # By applying inverse linear WCS transformation (`W^{-1}`) + # to both sides of equation (2) and introducing notation `x'` + # (prime) for the pixels coordinates obtained from the world + # coordinates by applying inverse *linear* WCS transformation + # ("focal plane coordinates"): + # + # (3) x' = W^{-1}(w) + # + # we obtain the following equation: + # + # (4) x' = x+f(x), + # + # or, + # + # (5) x = x'-f(x) + # + # This equation is well suited for solving using the method + # of fixed-point iterations + # (http://en.wikipedia.org/wiki/Fixed-point_iteration): + # + # (6) x_{i+1} = x'-f(x_i) + # + # As an initial value of the pixel coordinate `x_0` we take + # "focal plane coordinate" `x'=W^{-1}(w)=wcs_world2pix(w)`. + # We stop iterations when `|x_{i+1}-x_i||x_i-x_{i-1}|` + # **when** `|x_{i+1}-x_i|>=tolerance` (when current + # approximation is close to the true solution, + # `|x_{i+1}-x_i|>|x_i-x_{i-1}|` may be due to rounding errors + # and we ignore such "divergences" when + # `|x_{i+1}-x_i|world transformations). + # + # As an added benefit, the process converges to the correct + # solution in just one iteration when distortions are not + # present (compare to + # https://github.com/astropy/astropy/issues/1977 and + # https://github.com/astropy/astropy/pull/2294): in this case + # `pix2foc` is the identical transformation + # `x_i=pix2foc(x_i)` and from equation (7) we get: + # + # x' = x_0 = wcs_world2pix(w) + # x_1 = x' - pix2foc(x_0) + x_0 = x' - pix2foc(x') + x' = x' + # = wcs_world2pix(w) = x_0 + # => + # |x_1-x_0| = 0 < tolerance (with tolerance > 0) + # + # However, for performance reasons, it is still better to + # avoid iterations altogether and return the exact linear + # solution (`wcs_world2pix`) right-away when non-linear + # distortions are not present by checking that attributes + # `sip`, `cpdis1`, `cpdis2`, `det2im1`, and `det2im2` are + # *all* `None`. + # + # + # ### Outline of the Algorithm ### + # + # + # While the proposed code is relatively long (considering + # the simplicity of the algorithm), this is due to: 1) + # checking if iterative solution is necessary at all; 2) + # checking for divergence; 3) re-implementation of the + # completely vectorized algorithm as an "adaptive" vectorized + # algorithm (for cases when some points diverge for which we + # want to stop iterations). In my tests, the adaptive version + # of the algorithm is about 50% slower than non-adaptive + # version for all HST images. + # + # The essential part of the vectorized non-adaptive algorithm + # (without divergence and other checks) can be described + # as follows: + # + # pix0 = self.wcs_world2pix(world, origin) + # pix = pix0.copy() # 0-order solution + # + # for k in range(maxiter): + # # find correction to the previous solution: + # dpix = self.pix2foc(pix, origin) - pix0 + # + # # compute norm (L2) of the correction: + # dn = np.linalg.norm(dpix, axis=1) + # + # # apply correction: + # pix -= dpix + # + # # check convergence: + # if np.max(dn) < tolerance: + # break + # + # return pix + # + # Here, the input parameter `world` can be a `MxN` array + # where `M` is the number of coordinate axes in WCS and `N` + # is the number of points to be converted simultaneously to + # image coordinates. + # + # + # ### IMPORTANT NOTE: ### + # + # If, in the future releases of the `~astropy.wcs`, + # `pix2foc` will not apply all the required distortion + # corrections then in the code below, calls to `pix2foc` will + # have to be replaced with + # wcs_world2pix(all_pix2world(pix_list, origin), origin) + # + + # ############################################################ + # # INITIALIZE ITERATIVE PROCESS: ## + # ############################################################ + + # initial approximation (linear WCS based only) + pix0 = self.wcs_world2pix(world, origin) + + # Check that an iterative solution is required at all + # (when any of the non-CD-matrix-based corrections are + # present). If not required return the initial + # approximation (pix0). + if not self.has_distortion: + # No non-WCS corrections detected so + # simply return initial approximation: + return pix0 + + pix = pix0.copy() # 0-order solution + + # initial correction: + dpix = self.pix2foc(pix, origin) - pix0 + + # Update initial solution: + pix -= dpix + + # Norm (L2) squared of the correction: + dn = np.sum(dpix * dpix, axis=1) + dnprev = dn.copy() # if adaptive else dn + tol2 = tolerance**2 + + # Prepare for iterative process + k = 1 + ind = None + inddiv = None + + # Turn off numpy runtime warnings for 'invalid' and 'over': + old_invalid = np.geterr()["invalid"] + old_over = np.geterr()["over"] + np.seterr(invalid="ignore", over="ignore") + + # ############################################################ + # # NON-ADAPTIVE ITERATIONS: ## + # ############################################################ + if not adaptive: + # Fixed-point iterations: + while np.nanmax(dn) >= tol2 and k < maxiter: + # Find correction to the previous solution: + dpix = self.pix2foc(pix, origin) - pix0 + + # Compute norm (L2) squared of the correction: + dn = np.sum(dpix * dpix, axis=1) + + # Check for divergence (we do this in two stages + # to optimize performance for the most common + # scenario when successive approximations converge): + if detect_divergence: + divergent = dn >= dnprev + if np.any(divergent): + # Find solutions that have not yet converged: + slowconv = dn >= tol2 + (inddiv,) = np.where(divergent & slowconv) + + if inddiv.shape[0] > 0: + # Update indices of elements that + # still need correction: + conv = dn < dnprev + iconv = np.where(conv) + + # Apply correction: + dpixgood = dpix[iconv] + pix[iconv] -= dpixgood + dpix[iconv] = dpixgood + + # For the next iteration choose + # non-divergent points that have not yet + # converged to the requested accuracy: + (ind,) = np.where(slowconv & conv) + pix0 = pix0[ind] + dnprev[ind] = dn[ind] + k += 1 + + # Switch to adaptive iterations: + adaptive = True + break + # Save current correction magnitudes for later: + dnprev = dn + + # Apply correction: + pix -= dpix + k += 1 + + # ############################################################ + # # ADAPTIVE ITERATIONS: ## + # ############################################################ + if adaptive: + if ind is None: + (ind,) = np.where(np.isfinite(pix).all(axis=1)) + pix0 = pix0[ind] + + # "Adaptive" fixed-point iterations: + while ind.shape[0] > 0 and k < maxiter: + # Find correction to the previous solution: + dpixnew = self.pix2foc(pix[ind], origin) - pix0 + + # Compute norm (L2) of the correction: + dnnew = np.sum(np.square(dpixnew), axis=1) + + # Bookkeeping of corrections: + dnprev[ind] = dn[ind].copy() + dn[ind] = dnnew + + if detect_divergence: + # Find indices of pixels that are converging: + conv = dnnew < dnprev[ind] + iconv = np.where(conv) + iiconv = ind[iconv] + + # Apply correction: + dpixgood = dpixnew[iconv] + pix[iiconv] -= dpixgood + dpix[iiconv] = dpixgood + + # Find indices of solutions that have not yet + # converged to the requested accuracy + # AND that do not diverge: + (subind,) = np.where((dnnew >= tol2) & conv) - @deprecated("0.0", name="wcs_pix2sky", alternative="wcs_pix2world") - def wcs_pix2sky(self, *args, **kwargs): - return self.wcs_pix2world(*args, **kwargs) + else: + # Apply correction: + pix[ind] -= dpixnew + dpix[ind] = dpixnew + + # Find indices of solutions that have not yet + # converged to the requested accuracy: + (subind,) = np.where(dnnew >= tol2) + + # Choose solutions that need more iterations: + ind = ind[subind] + pix0 = pix0[subind] + + k += 1 + + # ############################################################ + # # FINAL DETECTION OF INVALID, DIVERGING, ## + # # AND FAILED-TO-CONVERGE POINTS ## + # ############################################################ + # Identify diverging and/or invalid points: + invalid = (~np.all(np.isfinite(pix), axis=1)) & ( + np.all(np.isfinite(world), axis=1) + ) + + # When detect_divergence==False, dnprev is outdated + # (it is the norm of the very first correction). + # Still better than nothing... + (inddiv,) = np.where(((dn >= tol2) & (dn >= dnprev)) | invalid) + if inddiv.shape[0] == 0: + inddiv = None + + # Identify points that did not converge within 'maxiter' + # iterations: + if k >= maxiter: + (ind,) = np.where((dn >= tol2) & (dn < dnprev) & (~invalid)) + if ind.shape[0] == 0: + ind = None + else: + ind = None + + # Restore previous numpy error settings: + np.seterr(invalid=old_invalid, over=old_over) + + # ############################################################ + # # RAISE EXCEPTION IF DIVERGING OR TOO SLOWLY CONVERGING ## + # # DATA POINTS HAVE BEEN DETECTED: ## + # ############################################################ + if (ind is not None or inddiv is not None) and not quiet: + if inddiv is None: + raise NoConvergence( + "'WCS.all_world2pix' failed to " + f"converge to the requested accuracy after {k:d} " + "iterations.", + best_solution=pix, + accuracy=np.abs(dpix), + niter=k, + slow_conv=ind, + divergent=None, + ) + else: + raise NoConvergence( + "'WCS.all_world2pix' failed to " + "converge to the requested accuracy.\n" + f"After {k:d} iterations, the solution is diverging " + "at least for one input point.", + best_solution=pix, + accuracy=np.abs(dpix), + niter=k, + slow_conv=ind, + divergent=inddiv, + ) + + return pix + + def all_world2pix( + self, + *args, + tolerance=1e-4, + maxiter=20, + adaptive=False, + detect_divergence=True, + quiet=False, + **kwargs, + ): + if self.wcs is None: + raise ValueError("No basic WCS settings were created.") + + return self._array_converter( + lambda *args, **kwargs: self._all_world2pix( + *args, + tolerance=tolerance, + maxiter=maxiter, + adaptive=adaptive, + detect_divergence=detect_divergence, + quiet=quiet, + ), + "input", + *args, + **kwargs, + ) + + all_world2pix.__doc__ = f""" + all_world2pix(*arg, tolerance=1.0e-4, maxiter=20, + adaptive=False, detect_divergence=True, quiet=False) + + Transforms world coordinates to pixel coordinates, using + numerical iteration to invert the full forward transformation + `~astropy.wcs.WCS.all_pix2world` with complete + distortion model. + + + Parameters + ---------- + {docstrings.TWO_OR_MORE_ARGS("naxis", 8)} + + For a transformation that is not two-dimensional, the + two-argument form must be used. + + {docstrings.RA_DEC_ORDER(8)} + + tolerance : float, optional (default = 1.0e-4) + Tolerance of solution. Iteration terminates when the + iterative solver estimates that the "true solution" is + within this many pixels current estimate, more + specifically, when the correction to the solution found + during the previous iteration is smaller + (in the sense of the L2 norm) than ``tolerance``. + + maxiter : int, optional (default = 20) + Maximum number of iterations allowed to reach a solution. + + quiet : bool, optional (default = False) + Do not throw :py:class:`NoConvergence` exceptions when + the method does not converge to a solution with the + required accuracy within a specified number of maximum + iterations set by ``maxiter`` parameter. Instead, + simply return the found solution. + + Other Parameters + ---------------- + adaptive : bool, optional (default = False) + Specifies whether to adaptively select only points that + did not converge to a solution within the required + accuracy for the next iteration. Default is recommended + for HST as well as most other instruments. + + .. note:: + The :py:meth:`all_world2pix` uses a vectorized + implementation of the method of consecutive + approximations (see ``Notes`` section below) in which it + iterates over *all* input points *regardless* until + the required accuracy has been reached for *all* input + points. In some cases it may be possible that + *almost all* points have reached the required accuracy + but there are only a few of input data points for + which additional iterations may be needed (this + depends mostly on the characteristics of the geometric + distortions for a given instrument). In this situation + it may be advantageous to set ``adaptive`` = `True` in + which case :py:meth:`all_world2pix` will continue + iterating *only* over the points that have not yet + converged to the required accuracy. However, for the + HST's ACS/WFC detector, which has the strongest + distortions of all HST instruments, testing has + shown that enabling this option would lead to a about + 50-100% penalty in computational time (depending on + specifics of the image, geometric distortions, and + number of input points to be converted). Therefore, + for HST and possibly instruments, it is recommended + to set ``adaptive`` = `False`. The only danger in + getting this setting wrong will be a performance + penalty. + + .. note:: + When ``detect_divergence`` is `True`, + :py:meth:`all_world2pix` will automatically switch + to the adaptive algorithm once divergence has been + detected. + + detect_divergence : bool, optional (default = True) + Specifies whether to perform a more detailed analysis + of the convergence to a solution. Normally + :py:meth:`all_world2pix` may not achieve the required + accuracy if either the ``tolerance`` or ``maxiter`` arguments + are too low. However, it may happen that for some + geometric distortions the conditions of convergence for + the method of consecutive approximations used by + :py:meth:`all_world2pix` may not be satisfied, in which + case consecutive approximations to the solution will + diverge regardless of the ``tolerance`` or ``maxiter`` + settings. + + When ``detect_divergence`` is `False`, these divergent + points will be detected as not having achieved the + required accuracy (without further details). In addition, + if ``adaptive`` is `False` then the algorithm will not + know that the solution (for specific points) is diverging + and will continue iterating and trying to "improve" + diverging solutions. This may result in ``NaN`` or + ``Inf`` values in the return results (in addition to a + performance penalties). Even when ``detect_divergence`` + is `False`, :py:meth:`all_world2pix`, at the end of the + iterative process, will identify invalid results + (``NaN`` or ``Inf``) as "diverging" solutions and will + raise :py:class:`NoConvergence` unless the ``quiet`` + parameter is set to `True`. + + When ``detect_divergence`` is `True`, + :py:meth:`all_world2pix` will detect points for which + current correction to the coordinates is larger than + the correction applied during the previous iteration + **if** the requested accuracy **has not yet been + achieved**. In this case, if ``adaptive`` is `True`, + these points will be excluded from further iterations and + if ``adaptive`` is `False`, :py:meth:`all_world2pix` will + automatically switch to the adaptive algorithm. Thus, the + reported divergent solution will be the latest converging + solution computed immediately *before* divergence + has been detected. + + .. note:: + When accuracy has been achieved, small increases in + current corrections may be possible due to rounding + errors (when ``adaptive`` is `False`) and such + increases will be ignored. + + .. note:: + Based on our testing using HST ACS/WFC images, setting + ``detect_divergence`` to `True` will incur about 5-20% + performance penalty with the larger penalty + corresponding to ``adaptive`` set to `True`. + Because the benefits of enabling this + feature outweigh the small performance penalty, + especially when ``adaptive`` = `False`, it is + recommended to set ``detect_divergence`` to `True`, + unless extensive testing of the distortion models for + images from specific instruments show a good stability + of the numerical method for a wide range of + coordinates (even outside the image itself). + + .. note:: + Indices of the diverging inverse solutions will be + reported in the ``divergent`` attribute of the + raised :py:class:`NoConvergence` exception object. + + Returns + ------- + + {docstrings.RETURNS("pixel coordinates", 8)} + + Notes + ----- + The order of the axes for the input world array is determined by + the ``CTYPEia`` keywords in the FITS header, therefore it may + not always be of the form (*ra*, *dec*). The + `~astropy.wcs.Wcsprm.lat`, `~astropy.wcs.Wcsprm.lng`, + `~astropy.wcs.Wcsprm.lattyp`, and + `~astropy.wcs.Wcsprm.lngtyp` + members can be used to determine the order of the axes. + + Using the method of fixed-point iterations approximations we + iterate starting with the initial approximation, which is + computed using the non-distortion-aware + :py:meth:`wcs_world2pix` (or equivalent). + + The :py:meth:`all_world2pix` function uses a vectorized + implementation of the method of consecutive approximations and + therefore it is highly efficient (>30x) when *all* data points + that need to be converted from sky coordinates to image + coordinates are passed at *once*. Therefore, it is advisable, + whenever possible, to pass as input a long array of all points + that need to be converted to :py:meth:`all_world2pix` instead + of calling :py:meth:`all_world2pix` for each data point. Also + see the note to the ``adaptive`` parameter. + + Raises + ------ + NoConvergence + The method did not converge to a + solution to the required accuracy within a specified + number of maximum iterations set by the ``maxiter`` + parameter. To turn off this exception, set ``quiet`` to + `True`. Indices of the points for which the requested + accuracy was not achieved (if any) will be listed in the + ``slow_conv`` attribute of the + raised :py:class:`NoConvergence` exception object. + + See :py:class:`NoConvergence` documentation for + more details. + + MemoryError + Memory allocation failed. + + SingularMatrixError + Linear transformation matrix is singular. + + InconsistentAxisTypesError + Inconsistent or unrecognized coordinate axis types. + + ValueError + Invalid parameter value. + + ValueError + Invalid coordinate transformation parameters. + + ValueError + x- and y-coordinate arrays are not the same size. + + InvalidTransformError + Invalid coordinate transformation parameters. + + InvalidTransformError + Ill-conditioned coordinate transformation parameters. + + Examples + -------- + >>> import astropy.io.fits as fits + >>> import astropy.wcs as wcs + >>> import numpy as np + >>> import os + + >>> filename = os.path.join(wcs.__path__[0], 'tests/data/j94f05bgq_flt.fits') + >>> hdulist = fits.open(filename) + >>> w = wcs.WCS(hdulist[('sci',1)].header, hdulist) + >>> hdulist.close() + + >>> ra, dec = w.all_pix2world([1,2,3], [1,1,1], 1) + >>> print(ra) # doctest: +FLOAT_CMP + [ 5.52645627 5.52649663 5.52653698] + >>> print(dec) # doctest: +FLOAT_CMP + [-72.05171757 -72.05171276 -72.05170795] + >>> radec = w.all_pix2world([[1,1], [2,1], [3,1]], 1) + >>> print(radec) # doctest: +FLOAT_CMP + [[ 5.52645627 -72.05171757] + [ 5.52649663 -72.05171276] + [ 5.52653698 -72.05170795]] + >>> x, y = w.all_world2pix(ra, dec, 1) + >>> print(x) # doctest: +FLOAT_CMP + [ 1.00000238 2.00000237 3.00000236] + >>> print(y) # doctest: +FLOAT_CMP + [ 0.99999996 0.99999997 0.99999997] + >>> xy = w.all_world2pix(radec, 1) + >>> print(xy) # doctest: +FLOAT_CMP + [[ 1.00000238 0.99999996] + [ 2.00000237 0.99999997] + [ 3.00000236 0.99999997]] + >>> xy = w.all_world2pix(radec, 1, maxiter=3, + ... tolerance=1.0e-10, quiet=False) + Traceback (most recent call last): + ... + NoConvergence: 'WCS.all_world2pix' failed to converge to the + requested accuracy. After 3 iterations, the solution is + diverging at least for one input point. + + >>> # Now try to use some diverging data: + >>> divradec = w.all_pix2world([[1.0, 1.0], + ... [10000.0, 50000.0], + ... [3.0, 1.0]], 1) + >>> print(divradec) # doctest: +FLOAT_CMP + [[ 5.52645627 -72.05171757] + [ 7.15976932 -70.8140779 ] + [ 5.52653698 -72.05170795]] + + >>> # First, turn detect_divergence on: + >>> try: # doctest: +FLOAT_CMP + ... xy = w.all_world2pix(divradec, 1, maxiter=20, + ... tolerance=1.0e-4, adaptive=False, + ... detect_divergence=True, + ... quiet=False) + ... except wcs.wcs.NoConvergence as e: + ... print("Indices of diverging points: {{0}}" + ... .format(e.divergent)) + ... print("Indices of poorly converging points: {{0}}" + ... .format(e.slow_conv)) + ... print("Best solution:\\n{{0}}".format(e.best_solution)) + ... print("Achieved accuracy:\\n{{0}}".format(e.accuracy)) + Indices of diverging points: [1] + Indices of poorly converging points: None + Best solution: + [[ 1.00000238e+00 9.99999965e-01] + [ -1.99441636e+06 1.44309097e+06] + [ 3.00000236e+00 9.99999966e-01]] + Achieved accuracy: + [[ 6.13968380e-05 8.59638593e-07] + [ 8.59526812e+11 6.61713548e+11] + [ 6.09398446e-05 8.38759724e-07]] + >>> raise e + Traceback (most recent call last): + ... + NoConvergence: 'WCS.all_world2pix' failed to converge to the + requested accuracy. After 5 iterations, the solution is + diverging at least for one input point. + + >>> # This time turn detect_divergence off: + >>> try: # doctest: +FLOAT_CMP + ... xy = w.all_world2pix(divradec, 1, maxiter=20, + ... tolerance=1.0e-4, adaptive=False, + ... detect_divergence=False, + ... quiet=False) + ... except wcs.wcs.NoConvergence as e: + ... print("Indices of diverging points: {{0}}" + ... .format(e.divergent)) + ... print("Indices of poorly converging points: {{0}}" + ... .format(e.slow_conv)) + ... print("Best solution:\\n{{0}}".format(e.best_solution)) + ... print("Achieved accuracy:\\n{{0}}".format(e.accuracy)) + Indices of diverging points: [1] + Indices of poorly converging points: None + Best solution: + [[ 1.00000009 1. ] + [ nan nan] + [ 3.00000009 1. ]] + Achieved accuracy: + [[ 2.29417358e-06 3.21222995e-08] + [ nan nan] + [ 2.27407877e-06 3.13005639e-08]] + >>> raise e + Traceback (most recent call last): + ... + NoConvergence: 'WCS.all_world2pix' failed to converge to the + requested accuracy. After 6 iterations, the solution is + diverging at least for one input point. + + """ def wcs_world2pix(self, *args, **kwargs): if self.wcs is None: raise ValueError("No basic WCS settings were created.") return self._array_converter( - lambda xy, o: self.wcs.s2p(xy, o)['pixcrd'], - 'input', *args, **kwargs) - wcs_world2pix.__doc__ = """ + lambda xy, o: self.wcs.s2p(xy, o)["pixcrd"], "input", *args, **kwargs + ) + + wcs_world2pix.__doc__ = f""" Transforms world coordinates to pixel coordinates, using only - the basic `wcslib`_ WCS transformation. No `SIP`_ or `Paper - IV`_ table lookup distortion is applied. + the basic `wcslib`_ WCS transformation. No `SIP`_ or + `distortion paper`_ table lookup transformation is applied. Parameters ---------- - {0} + {docstrings.TWO_OR_MORE_ARGS("naxis", 8)} For a transformation that is not two-dimensional, the two-argument form must be used. - {1} + {docstrings.RA_DEC_ORDER(8)} Returns ------- - {2} + {docstrings.RETURNS("pixel coordinates", 8)} Notes ----- The order of the axes for the input world array is determined by - the `CTYPEia` keywords in the FITS header, therefore it may + the ``CTYPEia`` keywords in the FITS header, therefore it may not always be of the form (*ra*, *dec*). The `~astropy.wcs.Wcsprm.lat`, `~astropy.wcs.Wcsprm.lng`, `~astropy.wcs.Wcsprm.lattyp` and `~astropy.wcs.Wcsprm.lngtyp` @@ -979,30 +2619,28 @@ def wcs_world2pix(self, *args, **kwargs): InvalidTransformError Ill-conditioned coordinate transformation parameters. - """.format(__.TWO_OR_THREE_ARGS('naxis', 8), - __.RA_DEC_ORDER(8), - __.RETURNS('pixel coordinates', 8)) + """ - @deprecated("0.0", name="wcs_sky2pix", alternative="wcs_world2pix") - def wcs_sky2pix(self, *args, **kwargs): - return self.wcs_world2pix(*args, **kwargs) + def pix2foc(self, *args): + return self._array_converter(self._pix2foc, None, *args) - def pix2foc(self, *args, **kwargs): - return self._array_converter(self._pix2foc, None, *args, **kwargs) - pix2foc.__doc__ = """ + pix2foc.__doc__ = f""" Convert pixel coordinates to focal plane coordinates using the - `SIP`_ polynomial distortion convention and `Paper IV`_ - table-lookup distortion correction. + `SIP`_ polynomial distortion convention and `distortion + paper`_ table-lookup correction. + + The output is in absolute pixel coordinates, not relative to + ``CRPIX``. Parameters ---------- - {0} + {docstrings.TWO_OR_MORE_ARGS("2", 8)} Returns ------- - {1} + {docstrings.RETURNS("focal coordinates", 8)} Raises ------ @@ -1011,24 +2649,27 @@ def pix2foc(self, *args, **kwargs): ValueError Invalid coordinate transformation parameters. - """.format(__.TWO_OR_THREE_ARGS('2', 8), - __.RETURNS('focal coordinates', 8)) + """ - def p4_pix2foc(self, *args, **kwargs): - return self._array_converter(self._p4_pix2foc, None, *args, **kwargs) - p4_pix2foc.__doc__ = """ + def p4_pix2foc(self, *args): + return self._array_converter(self._p4_pix2foc, None, *args) + + p4_pix2foc.__doc__ = f""" Convert pixel coordinates to focal plane coordinates using - `Paper IV`_ table-lookup distortion correction. + `distortion paper`_ table-lookup correction. + + The output is in absolute pixel coordinates, not relative to + ``CRPIX``. Parameters ---------- - {0} + {docstrings.TWO_OR_MORE_ARGS("2", 8)} Returns ------- - {1} + {docstrings.RETURNS("focal coordinates", 8)} Raises ------ @@ -1037,24 +2678,27 @@ def p4_pix2foc(self, *args, **kwargs): ValueError Invalid coordinate transformation parameters. - """.format(__.TWO_OR_THREE_ARGS('2', 8), - __.RETURNS('focal coordinates', 8)) + """ + + def det2im(self, *args): + return self._array_converter(self._det2im, None, *args) - def det2im(self, *args, **kwargs): - return self._array_converter(self._det2im, None, *args, **kwargs) - det2im.__doc__ = """ + det2im.__doc__ = f""" Convert detector coordinates to image plane coordinates using - `Paper IV`_ table-lookup distortion correction. + `distortion paper`_ table-lookup correction. + + The output is in absolute pixel coordinates, not relative to + ``CRPIX``. Parameters ---------- - {0} + {docstrings.TWO_OR_MORE_ARGS("2", 8)} Returns ------- - {1} + {docstrings.RETURNS("pixel coordinates", 8)} Raises ------ @@ -1063,10 +2707,9 @@ def det2im(self, *args, **kwargs): ValueError Invalid coordinate transformation parameters. - """.format(__.TWO_OR_THREE_ARGS('2', 8), - __.RETURNS('pixel coordinates', 8)) + """ - def sip_pix2foc(self, *args, **kwargs): + def sip_pix2foc(self, *args): if self.sip is None: if len(args) == 2: return args[0] @@ -1074,26 +2717,29 @@ def sip_pix2foc(self, *args, **kwargs): return args[:2] else: raise TypeError("Wrong number of arguments") - return self._array_converter(self.sip.pix2foc, None, *args, **kwargs) - sip_pix2foc.__doc__ = """ + return self._array_converter(self.sip.pix2foc, None, *args) + + sip_pix2foc.__doc__ = f""" Convert pixel coordinates to focal plane coordinates using the `SIP`_ polynomial distortion convention. - `Paper IV`_ table lookup distortion correction is not applied, - even if that information existed in the FITS file that - initialized this :class:`~astropy.wcs.WCS` object. To correct - for that, use `~astropy.wcs.WCS.pix2foc` or + The output is in pixel coordinates, relative to ``CRPIX``. + + FITS WCS `distortion paper`_ table lookup correction is not + applied, even if that information existed in the FITS file + that initialized this :class:`~astropy.wcs.WCS` object. To + correct for that, use `~astropy.wcs.WCS.pix2foc` or `~astropy.wcs.WCS.p4_pix2foc`. Parameters ---------- - {0} + {docstrings.TWO_OR_MORE_ARGS("2", 8)} Returns ------- - {1} + {docstrings.RETURNS("focal coordinates", 8)} Raises ------ @@ -1102,10 +2748,9 @@ def sip_pix2foc(self, *args, **kwargs): ValueError Invalid coordinate transformation parameters. - """.format(__.TWO_OR_THREE_ARGS('2', 8), - __.RETURNS('focal coordinates', 8)) + """ - def sip_foc2pix(self, *args, **kwargs): + def sip_foc2pix(self, *args): if self.sip is None: if len(args) == 2: return args[0] @@ -1113,24 +2758,25 @@ def sip_foc2pix(self, *args, **kwargs): return args[:2] else: raise TypeError("Wrong number of arguments") - return self._array_converter(self.sip.foc2pix, None, *args, **kwargs) - sip_foc2pix.__doc__ = """ + return self._array_converter(self.sip.foc2pix, None, *args) + + sip_foc2pix.__doc__ = f""" Convert focal plane coordinates to pixel coordinates using the `SIP`_ polynomial distortion convention. - `Paper IV`_ table lookup distortion correction is not applied, - even if that information existed in the FITS file that - initialized this `~astropy.wcs.WCS` object. + FITS WCS `distortion paper`_ table lookup distortion + correction is not applied, even if that information existed in + the FITS file that initialized this `~astropy.wcs.WCS` object. Parameters ---------- - {0} + {docstrings.TWO_OR_MORE_ARGS("2", 8)} Returns ------- - {1} + {docstrings.RETURNS("pixel coordinates", 8)} Raises ------ @@ -1139,12 +2785,95 @@ def sip_foc2pix(self, *args, **kwargs): ValueError Invalid coordinate transformation parameters. - """.format(__.TWO_OR_THREE_ARGS('2', 8), - __.RETURNS('pixel coordinates', 8)) + """ + + def proj_plane_pixel_scales(self): + """ + Calculate pixel scales along each axis of the image pixel at + the ``CRPIX`` location once it is projected onto the + "plane of intermediate world coordinates" as defined in + `Greisen & Calabretta 2002, A&A, 395, 1061 `_. + + .. note:: + This method is concerned **only** about the transformation + "image plane"->"projection plane" and **not** about the + transformation "celestial sphere"->"projection plane"->"image plane". + Therefore, this function ignores distortions arising due to + non-linear nature of most projections. + + .. note:: + This method only returns sensible answers if the WCS contains + celestial axes, i.e., the `~astropy.wcs.WCS.celestial` WCS object. + + Returns + ------- + scale : list of `~astropy.units.Quantity` + A vector of projection plane increments corresponding to each + pixel side (axis). + + See Also + -------- + astropy.wcs.utils.proj_plane_pixel_scales - def to_fits(self, relax=False): """ - Generate an `astropy.io.fits.HDUList` object with all of the + from astropy.wcs.utils import proj_plane_pixel_scales # Avoid circular import + + values = proj_plane_pixel_scales(self) + units = [u.Unit(x) for x in self.wcs.cunit] + return [ + value * unit for (value, unit) in zip(values, units) + ] # Can have different units + + def proj_plane_pixel_area(self): + """ + For a **celestial** WCS (see `astropy.wcs.WCS.celestial`), returns pixel + area of the image pixel at the ``CRPIX`` location once it is projected + onto the "plane of intermediate world coordinates" as defined in + `Greisen & Calabretta 2002, A&A, 395, 1061 `_. + + .. note:: + This function is concerned **only** about the transformation + "image plane"->"projection plane" and **not** about the + transformation "celestial sphere"->"projection plane"->"image plane". + Therefore, this function ignores distortions arising due to + non-linear nature of most projections. + + .. note:: + This method only returns sensible answers if the WCS contains + celestial axes, i.e., the `~astropy.wcs.WCS.celestial` WCS object. + + Returns + ------- + area : `~astropy.units.Quantity` + Area (in the projection plane) of the pixel at ``CRPIX`` location. + + Raises + ------ + ValueError + Pixel area is defined only for 2D pixels. Most likely the + `~astropy.wcs.Wcsprm.cd` matrix of the `~astropy.wcs.WCS.celestial` + WCS is not a square matrix of second order. + + Notes + ----- + Depending on the application, square root of the pixel area can be used to + represent a single pixel scale of an equivalent square pixel + whose area is equal to the area of a generally non-square pixel. + + See Also + -------- + astropy.wcs.utils.proj_plane_pixel_area + + """ + from astropy.wcs.utils import proj_plane_pixel_area # Avoid circular import + + value = proj_plane_pixel_area(self) + unit = u.Unit(self.wcs.cunit[0]) * u.Unit(self.wcs.cunit[1]) # 2D only + return value * unit + + def to_fits(self, relax=False, key=None): + """ + Generate an `~astropy.io.fits.HDUList` object with all of the information stored in this object. This should be logically identical to the input FITS file, but it will be normalized in a number of ways. @@ -1152,25 +2881,28 @@ def to_fits(self, relax=False): Parameters ---------- - relax : bool or int, optional Degree of permissiveness: - - `False`: Recognize only FITS keywords defined by the - published WCS standard. + - `False` (default): Write all extensions that are + considered to be safe and recommended. - - `True`: Admit all recognized informal extensions of the + - `True`: Write all recognized informal extensions of the WCS standard. - `int`: a bit field selecting specific extensions to - write. See :ref:`relaxwrite` for details. + write. See :ref:`astropy:relaxwrite` for details. + + key : str + The name of a particular WCS transform to use. This may be + either ``' '`` or ``'A'``-``'Z'`` and corresponds to the ``"a"`` + part of the ``CTYPEia`` cards. Returns ------- - hdulist : `astropy.io.fits.HDUList` + hdulist : `~astropy.io.fits.HDUList` """ - - header = self.to_header(relax=relax) + header = self.to_header(relax=relax, key=key) hdu = fits.PrimaryHDU(header=header) hdulist = fits.HDUList(hdu) @@ -1180,33 +2912,42 @@ def to_fits(self, relax=False): return hdulist - def to_header(self, relax=False): - """ - Generate an `astropy.io.fits.Header` object with the basic WCS and SIP - information stored in this object. This should be logically - identical to the input FITS file, but it will be normalized in - a number of ways. + def to_header(self, relax=None, key=None): + """Generate an `astropy.io.fits.Header` object with the basic WCS + and SIP information stored in this object. This should be + logically identical to the input FITS file, but it will be + normalized in a number of ways. .. warning:: - This function does not write out Paper IV distortion - information, since that requires multiple FITS header data - units. To get a full representation of everything in this - object, use `to_fits`. + This function does not write out FITS WCS `distortion + paper`_ information, since that requires multiple FITS + header data units. To get a full representation of + everything in this object, use `to_fits`. Parameters ---------- relax : bool or int, optional Degree of permissiveness: - - `False`: Recognize only FITS keywords defined by the - published WCS standard. + - `False` (default): Write all extensions that are + considered to be safe and recommended. - - `True`: Admit all recognized informal extensions of the + - `True`: Write all recognized informal extensions of the WCS standard. - `int`: a bit field selecting specific extensions to - write. See :ref:`relaxwrite` for details. + write. See :ref:`astropy:relaxwrite` for details. + + If the ``relax`` keyword argument is not given and any + keywords were omitted from the output, an + `~astropy.utils.exceptions.AstropyWarning` is displayed. + To override this, explicitly pass a value to ``relax``. + + key : str + The name of a particular WCS transform to use. This may be + either ``' '`` or ``'A'``-``'Z'`` and corresponds to the ``"a"`` + part of the ``CTYPEia`` cards. Returns ------- @@ -1224,7 +2965,7 @@ def to_header(self, relax=False): 2. Deprecated (e.g. ``CROTAn``) or non-standard usage will be translated to standard (this is partially dependent on - whether `fix` was applied). + whether ``fix`` was applied). 3. Quantities will be converted to the units used internally, basically SI with the addition of degrees. @@ -1243,98 +2984,280 @@ def to_header(self, relax=False): `to_header` tries hard to write meaningful comments. 8. Keyword order may be changed. + """ + # default precision for numerical WCS keywords + precision = WCSHDO_P14 # Defined by C-ext + display_warning = False + if relax is None: + display_warning = True + relax = False + + if relax not in (True, False): + do_sip = relax & WCSHDO_SIP + relax &= ~WCSHDO_SIP + else: + do_sip = relax + relax = WCSHDO_all if relax is True else WCSHDO_safe # Defined by C-ext + + relax = precision | relax if self.wcs is not None: + if key is not None: + orig_key = self.wcs.alt + self.wcs.alt = key header_string = self.wcs.to_header(relax) header = fits.Header.fromstring(header_string) + keys_to_remove = ["", " ", "COMMENT"] + for kw in keys_to_remove: + if kw in header: + del header[kw] + # Check if we can handle TPD distortion correctly + if _WCS_TPD_WARN_LT71: + for kw, val in header.items(): + if kw[:5] in ("CPDIS", "CQDIS") and val == "TPD": + warnings.warn( + f"WCS contains a TPD distortion model in {kw}. WCSLIB" + f" {WCSLIB_VERSION} is writing this in a format" + " incompatible with current versions - please update to" + " 7.4 or use the bundled WCSLIB.", + AstropyWarning, + ) + elif _WCS_TPD_WARN_LT74: + for kw, val in header.items(): + if kw[:5] in ("CPDIS", "CQDIS") and val == "TPD": + warnings.warn( + f"WCS contains a TPD distortion model in {kw}, which" + " requires WCSLIB 7.4 or later to store in a FITS header" + f" (having {WCSLIB_VERSION}).", + AstropyWarning, + ) else: header = fits.Header() - if self.sip is not None: - for key, val in self._write_sip_kw().items(): - header.update(key, val) + if do_sip and self.sip is not None: + if self.wcs is not None and any( + not ctyp.endswith("-SIP") for ctyp in self.wcs.ctype + ): + self._fix_ctype(header, add_sip=True) + + for kw, val in self._write_sip_kw().items(): + header[kw] = val + + if ( + not do_sip + and self.wcs is not None + and any(self.wcs.ctype) + and self.sip is not None + ): + # This is called when relax is not False or WCSHDO_SIP + # The default case of ``relax=None`` is handled further in the code. + header = self._fix_ctype(header, add_sip=False) + + if display_warning: + full_header = self.to_header(relax=True, key=key) + missing_keys = [k for k in full_header if k not in header] + if missing_keys: + warnings.warn( + "Some non-standard WCS keywords were excluded:" + f" {', '.join(missing_keys)} Use the ``relax`` kwarg to control" + " this.", + AstropyWarning, + ) + # called when ``relax=None`` + # This is different from the case of ``relax=False``. + if any(self.wcs.ctype) and self.sip is not None: + header = self._fix_ctype(header, add_sip=False, log_message=False) + # Finally reset the key. This must be called after ``_fix_ctype``. + if key is not None: + self.wcs.alt = orig_key + return header + + def _fix_ctype(self, header, add_sip=True, log_message=True): + """ + Parameters + ---------- + header : `~astropy.io.fits.Header` + FITS header. + add_sip : bool + Flag indicating whether "-SIP" should be added or removed from CTYPE keywords. + + Remove "-SIP" from CTYPE when writing out a header with relax=False. + This needs to be done outside ``to_header`` because ``to_header`` runs + twice when ``relax=False`` and the second time ``relax`` is set to ``True`` + to display the missing keywords. + + If the user requested SIP distortion to be written out add "-SIP" to + CTYPE if it is missing. + """ + _add_sip_to_ctype = """ + Inconsistent SIP distortion information is present in the current WCS: + SIP coefficients were detected, but CTYPE is missing "-SIP" suffix, + therefore the current WCS is internally inconsistent. + + Because relax has been set to True, the resulting output WCS will have + "-SIP" appended to CTYPE in order to make the header internally consistent. + + However, this may produce incorrect astrometry in the output WCS, if + in fact the current WCS is already distortion-corrected. + + Therefore, if current WCS is already distortion-corrected (eg, drizzled) + then SIP distortion components should not apply. In that case, for a WCS + that is already distortion-corrected, please remove the SIP coefficients + from the header. + """ + if log_message: + if add_sip: + log.info(_add_sip_to_ctype) + for i in range(1, self.naxis + 1): + # strip() must be called here to cover the case of alt key= " " + kw = f"CTYPE{i}{self.wcs.alt}".strip() + if kw in header: + if add_sip: + val = header[kw].strip("-SIP") + "-SIP" + else: + val = header[kw].strip("-SIP") + header[kw] = val + else: + continue return header - def to_header_string(self, relax=False): + def to_header_string(self, relax=None): """ Identical to `to_header`, but returns a string containing the header cards. """ - return str(self.to_header(self, relax)) + return str(self.to_header(relax)) - def footprint_to_file(self, filename=None, color='green', width=2): + def footprint_to_file( + self, filename="footprint.reg", color="green", width=2, coordsys=None + ): """ Writes out a `ds9`_ style regions file. It can be loaded directly by `ds9`_. Parameters ---------- - filename : string, optional + filename : str, optional Output file name - default is ``'footprint.reg'`` - color : string, optional + color : str, optional Color to use when plotting the line. width : int, optional Width of the region line. + + coordsys : str, optional + Coordinate system. If not specified (default), the ``radesys`` + value is used. For all possible values, see + http://ds9.si.edu/doc/ref/region.html#RegionFileFormat + """ - if not filename: - filename = 'footprint.reg' - comments = '# Region file format: DS9 version 4.0 \n' - comments += ('# global color=green font="helvetica 12 bold ' + - 'select=1 highlite=1 edit=1 move=1 delete=1 ' + - 'include=1 fixed=0 source\n') - - f = open(filename, 'a') - f.write(comments) - f.write('linear\n') - f.write('polygon(') - self.footprint.tofile(f, sep=',') - f.write(') # color={0}, width={1:d} \n'.format(color, width)) - f.close() - - def get_naxis(self, header=None): - self.naxis1 = 0.0 - self.naxis2 = 0.0 - if header != None and not isinstance(header, string_types): - self.naxis1 = header.get('NAXIS1', 0.0) - self.naxis2 = header.get('NAXIS2', 0.0) - - def rotateCD(self, theta): - _theta = np.deg2rad(theta) - _mrot = np.zeros(shape=(2, 2), dtype=np.double) - _mrot[0] = (np.cos(_theta), np.sin(_theta)) - _mrot[1] = (-np.sin(_theta), np.cos(_theta)) - new_cd = np.dot(self.wcs.cd, _mrot) - self.wcs.cd = new_cd + comments = ( + "# Region file format: DS9 version 4.0 \n" + '# global color=green font="helvetica 12 bold ' + "select=1 highlite=1 edit=1 move=1 delete=1 " + "include=1 fixed=0 source\n" + ) + + coordsys = coordsys or self.wcs.radesys + + if coordsys not in ( + "PHYSICAL", + "IMAGE", + "FK4", + "B1950", + "FK5", + "J2000", + "GALACTIC", + "ECLIPTIC", + "ICRS", + "LINEAR", + "AMPLIFIER", + "DETECTOR", + ): + raise ValueError( + f"Coordinate system '{coordsys}' is not supported. A valid" + " one can be given with the 'coordsys' argument." + ) + + with open(filename, mode="w") as f: + f.write(comments) + f.write(f"{coordsys}\n") + f.write("polygon(") + ftpr = self.calc_footprint() + if ftpr is not None: + ftpr.tofile(f, sep=",") + f.write(f") # color={color}, width={width:d} \n") + + def _get_naxis(self, header=None): + _naxis = [] + if header is not None and not isinstance(header, (str, bytes)): + for naxis in itertools.count(1): + try: + _naxis.append(header[f"NAXIS{naxis}"]) + except KeyError: + break + + if len(_naxis) == 0: + _naxis = self.naxis * [0] + + self._naxis = _naxis def printwcs(self): + print(repr(self)) + + def __repr__(self): + """ + Return a short description. Simply porting the behavior from + the `printwcs()` method. """ - Temporary function for internal use. - """ - print('WCS Keywords\n') - if hasattr(self.wcs, 'cd'): - print('CD_11 CD_12: {!r} {!r}'.format( - self.wcs.cd[0, 0], self.wcs.cd[0, 1])) - print('CD_21 CD_22: {!r} {!r}'.format( - self.wcs.cd[1, 0], self.wcs.cd[1, 1])) - print('CRVAL : {!r} {!r}'.format( - self.wcs.crval[0], self.wcs.crval[1])) - print('CRPIX : {!r} {!r}'.format( - self.wcs.crpix[0], self.wcs.crpix[1])) - print('NAXIS : {!r} {!r}'.format( - self.naxis1, self.naxis2)) + description = ["WCS Keywords", "", f"Number of WCS axes: {self.naxis!r}"] + sfmt = " : " + "".join([f"{{{i}}} " for i in range(self.naxis)]) + + keywords = ["CTYPE", "CUNIT", "CRVAL", "CRPIX"] + values = [ + [repr(v) for v in self.wcs.ctype], + [repr(str(v)) for v in self.wcs.cunit], + self.wcs.crval, + self.wcs.crpix, + ] + for keyword, value in zip(keywords, values): + description.append(keyword + sfmt.format(*value)) + + if hasattr(self.wcs, "pc"): + for i in range(self.naxis): + s = "" + for j in range(self.naxis): + s += "".join(["PC", str(i + 1), "_", str(j + 1), " "]) + s += sfmt + description.append(s.format(*self.wcs.pc[i])) + s = "CDELT" + sfmt + description.append(s.format(*self.wcs.cdelt)) + elif hasattr(self.wcs, "cd"): + for i in range(self.naxis): + s = "" + for j in range(self.naxis): + s += "".join(["CD", str(i + 1), "_", str(j + 1), " "]) + s += sfmt + description.append(s.format(*self.wcs.cd[i])) + + description.append(f"NAXIS : {' '.join(map(str, self._naxis))}") + + # Strip trailing space in lines + description = [line.rstrip() for line in description] + + return "\n".join(description) def get_axis_types(self): """ - Similar to `self.wcsprm.axis_types <_wcs._Wcsprm.axis_types>` + Similar to `self.wcsprm.axis_types ` but provides the information in a more Python-friendly format. Returns ------- - result : list of dicts + result : list of dict Returns a list of dictionaries, one for each axis, each containing attributes about the type of that axis. @@ -1387,38 +3310,34 @@ def get_axis_types(self): generate an error. """ if self.wcs is None: - raise AttributeError( - "This WCS object does not have a wcsprm object.") + raise AttributeError("This WCS object does not have a wcsprm object.") - coordinate_type_map = { - 0: None, - 1: 'stokes', - 2: 'celestial', - 3: 'spectral'} + coordinate_type_map = {0: None, 1: "stokes", 2: "celestial", 3: "spectral"} scale_map = { - 0: 'linear', - 1: 'quantized', - 2: 'non-linear celestial', - 3: 'non-linear spectral', - 4: 'logarithmic', - 5: 'tabular'} + 0: "linear", + 1: "quantized", + 2: "non-linear celestial", + 3: "non-linear spectral", + 4: "logarithmic", + 5: "tabular", + } result = [] for axis_type in self.wcs.axis_types: subresult = {} coordinate_type = (axis_type // 1000) % 10 - subresult['coordinate_type'] = coordinate_type_map[coordinate_type] + subresult["coordinate_type"] = coordinate_type_map[coordinate_type] scale = (axis_type // 100) % 10 - subresult['scale'] = scale_map[scale] + subresult["scale"] = scale_map[scale] group = (axis_type // 10) % 10 - subresult['group'] = group + subresult["group"] = group number = axis_type % 10 - subresult['number'] = number + subresult["number"] = number result.append(subresult) @@ -1429,53 +3348,425 @@ def __reduce__(self): Support pickling of WCS objects. This is done by serializing to an in-memory FITS file and dumping that as a string. """ - hdulist = self.to_fits(relax=True) buffer = io.BytesIO() hdulist.writeto(buffer) - return (__WCS_unpickle__, - (self.__class__, self.__dict__, buffer.getvalue(),)) + # Exclude lazily-populated caches: they are pure functions of the + # WCS state, so unpickling can regenerate them on demand. Keeping + # them out of the pickle avoids bloating the payload and prevents + # non-picklable cache contents (e.g. closures) from breaking pickle. + dct = {k: v for k, v in self.__dict__.items() if not k.endswith("_cache")} + dct["_alt_wcskey"] = self.wcs.alt + + return ( + __WCS_unpickle__, + ( + self.__class__, + dct, + buffer.getvalue(), + ), + ) + + def dropaxis(self, dropax): + """ + Remove an axis from the WCS. + + Parameters + ---------- + wcs : `~astropy.wcs.WCS` + The WCS with naxis to be chopped to naxis-1 + dropax : int + The index of the WCS to drop, counting from 0 (i.e., python convention, + not FITS convention) + + Returns + ------- + `~astropy.wcs.WCS` + A new `~astropy.wcs.WCS` instance with one axis fewer + """ + inds = list(range(self.wcs.naxis)) + inds.pop(dropax) + + # axis 0 has special meaning to sub + # if wcs.wcs.ctype == ['RA','DEC','VLSR'], you want + # wcs.sub([1,2]) to get 'RA','DEC' back + return self.sub([i + 1 for i in inds]) + + def swapaxes(self, ax0, ax1): + """ + Swap axes in a WCS. + + Parameters + ---------- + wcs : `~astropy.wcs.WCS` + The WCS to have its axes swapped + ax0 : int + ax1 : int + The indices of the WCS to be swapped, counting from 0 (i.e., python + convention, not FITS convention) + + Returns + ------- + `~astropy.wcs.WCS` + A new `~astropy.wcs.WCS` instance with the same number of axes, + but two swapped + """ + inds = list(range(self.wcs.naxis)) + inds[ax0], inds[ax1] = inds[ax1], inds[ax0] + + return self.sub([i + 1 for i in inds]) + + def reorient_celestial_first(self): + """ + Reorient the WCS such that the celestial axes are first, followed by + the spectral axis, followed by any others. + Assumes at least celestial axes are present. + """ + return self.sub( + [WCSSUB_CELESTIAL, WCSSUB_SPECTRAL, WCSSUB_STOKES, WCSSUB_TIME] + ) # Defined by C-ext + + def slice(self, view, numpy_order=True): + """ + Slice a WCS instance using a Numpy slice. The order of the slice should + be reversed (as for the data) compared to the natural WCS order. + + Parameters + ---------- + view : tuple + A tuple containing the same number of slices as the WCS system. + The ``step`` method, the third argument to a slice, is not + presently supported. + numpy_order : bool, default: True + Use numpy order, i.e. slice the WCS so that an identical slice + applied to a numpy array will slice the array and WCS in the same + way. If set to `False`, the WCS will be sliced in FITS order, + meaning the first slice will be applied to the *last* numpy index + but the *first* WCS axis. + + Returns + ------- + wcs_new : `~astropy.wcs.WCS` + A new resampled WCS axis + """ + if view is Ellipsis: + return self.deepcopy() + + if hasattr(view, "__len__") and len(view) > self.wcs.naxis: + raise ValueError("Must have # of slices <= # of WCS axes") + elif not hasattr(view, "__len__"): # view MUST be an iterable + view = [view] + + if len(view) < self.wcs.naxis: + view = list(view) + [slice(None) for i in range(self.wcs.naxis - len(view))] + + if not numpy_order: + view = view[::-1] + + if not all(isinstance(x, slice) for x in view): + # We need to drop some dimensions, but this may not always be + # possible with .sub due to correlated axes, so instead we use the + # generalized slicing infrastructure from astropy.wcs.wcsapi. + return SlicedFITSWCS(self, view) + + # NOTE: we could in principle use SlicedFITSWCS as above for all slicing, + # but in the simple case where there are no axes dropped, we can just + # create a full WCS object with updated WCS parameters which is faster + # for this specific case and also backward-compatible. + + wcs_new = self.deepcopy() + if wcs_new.sip is not None: + sip_crpix = wcs_new.sip.crpix.tolist() + + # Group the distortion tables by which axis (x or y) they correspond to + x_tables = [t for t in (wcs_new.cpdis1, wcs_new.det2im1) if t is not None] + y_tables = [t for t in (wcs_new.cpdis2, wcs_new.det2im2) if t is not None] + distortion_tables = [*x_tables, *y_tables] + + for i, iview in enumerate(view): + if iview.step is not None and iview.step < 0: + raise NotImplementedError("Reversing an axis is not implemented.") + + wcs_index = self.wcs.naxis - 1 - i + + if wcs_index < 2: + itables = [x_tables, y_tables][wcs_index] + else: + itables = [] + + if iview.step is not None and iview.start is None: + # Slice from "None" is equivalent to slice from 0 (but one + # might want to downsample, so allow slices with + # None,None,step or None,stop,step) + iview = slice(0, iview.stop, iview.step) + + if iview.start is not None: + if iview.step not in (None, 1): + crpix = self.wcs.crpix[wcs_index] + cdelt = self.wcs.cdelt[wcs_index] + # equivalently (keep this comment so you can compare eqns): + # wcs_new.wcs.crpix[wcs_index] = + # (crpix - iview.start)*iview.step + 0.5 - iview.step/2. + scale_pixel = lambda px: ( + (px - iview.start - 1.0) / iview.step + + 0.5 + + 1.0 / iview.step / 2.0 + ) + crp = scale_pixel(crpix) + wcs_new.wcs.crpix[wcs_index] = crp + if wcs_new.sip is not None: + sip_crpix[wcs_index] = crp + for table in distortion_tables: + # The table's crval (which is an image pixel location) + # should be adjusted to the corresponding location in + # the sliced array + table.crval[wcs_index] = scale_pixel(table.crval[wcs_index]) + # And its cdelt (with units image pixels / distortion + # table pixel) should reflect the stride + table.cdelt[wcs_index] /= iview.step + for table in itables: + # If we stride an x axis, for example, x distortions + # should be adjusted in magnitude + table.data /= iview.step + wcs_new.wcs.cdelt[wcs_index] = cdelt * iview.step + else: + wcs_new.wcs.crpix[wcs_index] -= iview.start + if wcs_new.sip is not None: + sip_crpix[wcs_index] -= iview.start + for table in distortion_tables: + table.crval[wcs_index] -= iview.start + + try: + # range requires integers but the other attributes can also + # handle arbitrary values, so this needs to be in a try/except. + nitems = len(builtins.range(self._naxis[wcs_index])[iview]) + except TypeError as exc: + if "indices must be integers" not in str(exc): + raise + warnings.warn( + f"NAXIS{wcs_index} attribute is not updated because at " + f"least one index ('{iview}') is no integer.", + AstropyUserWarning, + ) + else: + wcs_new._naxis[wcs_index] = nitems + + if wcs_new.sip is not None: + wcs_new.sip = Sip( + self.sip.a, self.sip.b, self.sip.ap, self.sip.bp, sip_crpix + ) + + return wcs_new + + def __getitem__(self, item): + # "getitem" is a shortcut for self.slice; it is very limited + # there is no obvious and unambiguous interpretation of wcs[1,2,3] + # We COULD allow wcs[1] to link to wcs.sub([2]) + # (wcs[i] -> wcs.sub([i+1]) + return self.slice(item) + + def __iter__(self): + # Having __getitem__ makes Python think WCS is iterable. However, + # Python first checks whether __iter__ is present, so we can raise an + # exception here. + raise TypeError(f"'{self.__class__.__name__}' object is not iterable") + + @property + def axis_type_names(self): + """ + World names for each coordinate axis. + + Returns + ------- + list of str + A list of names along each axis. + """ + names = list(self.wcs.cname) + types = self.wcs.ctype + for i in range(len(names)): + if len(names[i]) > 0: + continue + names[i] = types[i].split("-")[0] + return names + + @property + def celestial(self): + """ + A copy of the current WCS with only the celestial axes included. + """ + return self.sub([WCSSUB_CELESTIAL]) # Defined by C-ext + + @property + def is_celestial(self): + return self.has_celestial and self.naxis == 2 + + @property + def has_celestial(self): + try: + return self.wcs.lng >= 0 and self.wcs.lat >= 0 + except InconsistentAxisTypesError: + return False + + @property + def spectral(self): + """ + A copy of the current WCS with only the spectral axes included. + """ + return self.sub([WCSSUB_SPECTRAL]) # Defined by C-ext + + @property + def is_spectral(self): + return self.has_spectral and self.naxis == 1 + + @property + def has_spectral(self): + try: + return self.wcs.spec >= 0 + except InconsistentAxisTypesError: + return False + + @property + def temporal(self): + """ + A copy of the current WCS with only the time axes included. + """ + if not _WCSSUB_TIME_SUPPORT: + raise NotImplementedError( + "Support for 'temporal' axis requires WCSLIB version 7.8 or " + f"greater but linked WCSLIB version is {WCSLIB_VERSION}" + ) + + return self.sub([WCSSUB_TIME]) # Defined by C-ext + + @property + def is_temporal(self): + return self.has_temporal and self.naxis == 1 + + @property + def has_temporal(self): + return any(t // 1000 == 4 for t in self.wcs.axis_types) + + @property + def has_distortion(self): + """ + Returns `True` if any distortion terms are present. + """ + return ( + self.sip is not None + or self.cpdis1 is not None + or self.cpdis2 is not None + or (self.det2im1 is not None and self.det2im2 is not None) + ) + + @property + def pixel_scale_matrix(self): + try: + cdelt = np.diag(self.wcs.get_cdelt()) + pc = self.wcs.get_pc() + except InconsistentAxisTypesError: + try: + # for non-celestial axes, get_cdelt doesn't work + with warnings.catch_warnings(): + warnings.filterwarnings( + "ignore", + "cdelt will be ignored since cd is present", + RuntimeWarning, + ) + cdelt = np.dot(self.wcs.cd, np.diag(self.wcs.cdelt)) + except AttributeError: + cdelt = np.diag(self.wcs.cdelt) + + try: + pc = self.wcs.pc + except AttributeError: + pc = 1 + + pccd = np.dot(cdelt, pc) + + return pccd + + @property + def preserve_units(self): + """ + Indicates whether the ``WCS`` class is preserving the original units. + + If `True`, units are always kept as specified, whereas is `False`, + units will in some cases be converted to SI/degrees - for example units + for celestial axes are converted to degrees, spectral frequencies to + Hz, and wavelengths to meters. + """ + return self._preserve_units + + def footprint_contains(self, coord, **kwargs): + """ + Determines if a given SkyCoord is contained in the wcs footprint. + + Parameters + ---------- + coord : `~astropy.coordinates.SkyCoord` + The coordinate to check if it is within the wcs coordinate. + **kwargs : + Additional arguments to pass to `~astropy.coordinates.SkyCoord.to_pixel` + + Returns + ------- + response : bool + True means the WCS footprint contains the coordinate, False means it does not. + """ + return coord.contained_by(self, **kwargs) def __WCS_unpickle__(cls, dct, fits_data): """ Unpickles a WCS object from a serialized FITS string. """ - self = cls.__new__(cls) - self.__dict__.update(dct) buffer = io.BytesIO(fits_data) hdulist = fits.open(buffer) - WCS.__init__(self, hdulist[0].header, hdulist) + naxis = dct.pop("naxis", None) + if naxis: + hdulist[0].header["naxis"] = naxis + naxes = dct.pop("_naxis", []) + for k, na in enumerate(naxes): + hdulist[0].header[f"naxis{k + 1:d}"] = na + + kwargs = dct.pop("_init_kwargs", {}) + self.__dict__.update(dct) + + wcskey = dct.pop("_alt_wcskey", " ") + WCS.__init__(self, hdulist[0].header, hdulist, key=wcskey, **kwargs) + self.pixel_bounds = dct.get("_pixel_bounds", None) return self -def find_all_wcs(header, relax=False, keysel=None): +def find_all_wcs( + header, relax=True, keysel=None, fix=True, translate_units="", _do_set=True +): """ Find all the WCS transformations in the given header. Parameters ---------- - header : string or astropy.io.fits header object. + header : str or `~astropy.io.fits.Header` object. relax : bool or int, optional Degree of permissiveness: + - `True` (default): Admit all recognized informal extensions of the + WCS standard. + - `False`: Recognize only FITS keywords defined by the published WCS standard. - - `True`: Admit all recognized informal extensions of the - WCS standard. - - `int`: a bit field selecting specific extensions to accept. - See :ref:`relaxread` for details. + See :ref:`astropy:relaxread` for details. - keysel : sequence of flags, optional + keysel : sequence of str, optional A list of flags used to select the keyword types considered by wcslib. When ``None``, only the standard image header keywords are considered (and the underlying wcspih() C @@ -1495,27 +3786,172 @@ def find_all_wcs(header, relax=False, keysel=None): ``WCSNna`` and ``TWCSna``) are selected by both 'binary' and 'pixel'. + fix : bool, optional + When `True` (default), call `~astropy.wcs.Wcsprm.fix` on + the resulting objects to fix any non-standard uses in the + header. `FITSFixedWarning` warnings will be emitted if any + changes were made. + + translate_units : str, optional + Specify which potentially unsafe translations of non-standard + unit strings to perform. By default, performs none. See + `WCS.fix` for more information about this parameter. Only + effective when ``fix`` is `True`. + Returns ------- - wcses : list of `WCS` objects + wcses : list of `WCS` """ - - if isinstance(header, string_types): + if isinstance(header, (str, bytes)): header_string = header elif isinstance(header, fits.Header): - header_string = repr(header) + header_string = header.tostring() else: - raise TypeError( - "header must be a string or astropy.io.fits.Header object") + raise TypeError("header must be a string or astropy.io.fits.Header object") keysel_flags = _parse_keysel(keysel) - wcsprms = _wcs.find_all_wcs(header_string, relax, keysel_flags) + if isinstance(header_string, str): + header_bytes = header_string.encode("ascii") + else: + header_bytes = header_string + + wcsprms = find_all_wcs_c(header_bytes, relax, keysel_flags) result = [] for wcsprm in wcsprms: - subresult = WCS() + subresult = WCS(fix=False, _do_set=False) subresult.wcs = wcsprm result.append(subresult) + if fix: + subresult.fix(translate_units) + + if _do_set: + subresult.wcs.set() + return result + + +def validate(source): + """ + Prints a WCS validation report for the given FITS file. + + Parameters + ---------- + source : str or file-like or `~astropy.io.fits.HDUList` + The FITS file to validate. + + Returns + ------- + results : list subclass instance + The result is returned as nested lists. The first level + corresponds to the HDUs in the given file. The next level has + an entry for each WCS found in that header. The special + subclass of list will pretty-print the results as a table when + printed. + + """ + + class _WcsValidateWcsResult(list): + def __init__(self, key): + self._key = key + + def __repr__(self): + result = [f" WCS key '{self._key or ' '}':"] + if len(self): + for entry in self: + for i, line in enumerate(entry.splitlines()): + if i == 0: + initial_indent = " - " + else: + initial_indent = " " + result.extend( + textwrap.wrap( + line, + initial_indent=initial_indent, + subsequent_indent=" ", + ) + ) + else: + result.append(" No issues.") + return "\n".join(result) + + class _WcsValidateHduResult(list): + def __init__(self, hdu_index, hdu_name): + self._hdu_index = hdu_index + self._hdu_name = hdu_name + list.__init__(self) + + def __repr__(self): + if len(self): + if self._hdu_name: + hdu_name = f" ({self._hdu_name})" + else: + hdu_name = "" + result = [f"HDU {self._hdu_index}{hdu_name}:"] + for wcs in self: + result.append(repr(wcs)) + return "\n".join(result) + return "" + + class _WcsValidateResults(list): + def __repr__(self): + result = [] + for hdu in self: + content = repr(hdu) + if content: + result.append(content) + return "\n\n".join(result) + + global __warningregistry__ + + if isinstance(source, fits.HDUList): + hdulist = source + close_file = False + else: + hdulist = fits.open(source) + close_file = True + + results = _WcsValidateResults() + + for i, hdu in enumerate(hdulist): + hdu_results = _WcsValidateHduResult(i, hdu.name) + results.append(hdu_results) + + with warnings.catch_warnings(record=True) as warning_lines: + wcses = find_all_wcs( + hdu.header, relax=WCSHDR_reject, fix=False, _do_set=False + ) + + for wcs in wcses: + wcs_results = _WcsValidateWcsResult(wcs.wcs.alt) + hdu_results.append(wcs_results) + + try: + del __warningregistry__ + except NameError: + pass + + with warnings.catch_warnings(record=True) as warning_lines: + warnings.resetwarnings() + warnings.simplefilter("always", FITSFixedWarning, append=True) + + try: + WCS( + hdu.header, + hdulist, + key=wcs.wcs.alt or " ", + relax=WCSHDR_reject, + fix=True, + _do_set=False, + ) + except WcsError as e: + wcs_results.append(str(e)) + + wcs_results.extend([str(x.message) for x in warning_lines]) + + if close_file: + hdulist.close() + + return results diff --git a/astropy/wcs/wcsapi/__init__.py b/astropy/wcs/wcsapi/__init__.py new file mode 100644 index 000000000000..83dec762dcc0 --- /dev/null +++ b/astropy/wcs/wcsapi/__init__.py @@ -0,0 +1,5 @@ +from .high_level_api import * +from .high_level_wcs_wrapper import * +from .low_level_api import * +from .utils import * +from .wrappers import * diff --git a/astropy/wcs/wcsapi/conftest.py b/astropy/wcs/wcsapi/conftest.py new file mode 100644 index 000000000000..45dffa471ae0 --- /dev/null +++ b/astropy/wcs/wcsapi/conftest.py @@ -0,0 +1,185 @@ +import numpy as np +import pytest + +from astropy.coordinates import SkyCoord +from astropy.units import Quantity +from astropy.wcs import WCS +from astropy.wcs.wcsapi import BaseLowLevelWCS + + +@pytest.fixture +def spectral_1d_fitswcs(): + wcs = WCS(naxis=1) + wcs.wcs.ctype = ("FREQ",) + wcs.wcs.cunit = ("Hz",) + wcs.wcs.cdelt = (3.0e9,) + wcs.wcs.crval = (4.0e9,) + wcs.wcs.crpix = (11.0,) + wcs.wcs.cname = ("Frequency",) + return wcs + + +@pytest.fixture +def time_1d_fitswcs(): + wcs = WCS(naxis=1) + wcs.wcs.ctype = ("TIME",) + wcs.wcs.mjdref = (30042, 0) + wcs.wcs.crval = (3.0,) + wcs.wcs.crpix = (11.0,) + wcs.wcs.cname = ("Time",) + wcs.wcs.cunit = "s" + return wcs + + +@pytest.fixture +def celestial_2d_fitswcs(): + wcs = WCS(naxis=2) + wcs.wcs.ctype = "RA---CAR", "DEC--CAR" + wcs.wcs.cunit = "deg", "deg" + wcs.wcs.cdelt = -2.0, 2.0 + wcs.wcs.crval = 4.0, 0.0 + wcs.wcs.crpix = 6.0, 7.0 + wcs.wcs.cname = "Right Ascension", "Declination" + wcs.pixel_shape = (6, 7) + wcs.pixel_bounds = [(-1, 5), (1, 7)] + return wcs + + +@pytest.fixture +def spectral_cube_3d_fitswcs(): + wcs = WCS(naxis=3) + wcs.wcs.ctype = "RA---CAR", "DEC--CAR", "FREQ" + wcs.wcs.cunit = "deg", "deg", "Hz" + wcs.wcs.cdelt = -2.0, 2.0, 3.0e9 + wcs.wcs.crval = 4.0, 0.0, 4.0e9 + wcs.wcs.crpix = 6.0, 7.0, 11.0 + wcs.wcs.cname = "Right Ascension", "Declination", "Frequency" + wcs.pixel_shape = (6, 7, 3) + wcs.pixel_bounds = [(-1, 5), (1, 7), (1, 2.5)] + return wcs + + +@pytest.fixture +def cube_4d_fitswcs(): + wcs = WCS(naxis=4) + wcs.wcs.ctype = "RA---CAR", "DEC--CAR", "FREQ", "TIME" + wcs.wcs.cunit = "deg", "deg", "Hz", "s" + wcs.wcs.cdelt = -2.0, 2.0, 3.0e9, 1 + wcs.wcs.crval = 4.0, 0.0, 4.0e9, 3 + wcs.wcs.crpix = 6.0, 7.0, 11.0, 11.0 + wcs.wcs.cname = "Right Ascension", "Declination", "Frequency", "Time" + wcs.wcs.mjdref = (30042, 0) + return wcs + + +class Spectral1DLowLevelWCS(BaseLowLevelWCS): + @property + def pixel_n_dim(self): + return 1 + + @property + def world_n_dim(self): + return 1 + + @property + def world_axis_physical_types(self): + return ("em.freq",) + + @property + def world_axis_units(self): + return ("Hz",) + + @property + def world_axis_names(self): + return ("Frequency",) + + _pixel_shape = None + + @property + def pixel_shape(self): + return self._pixel_shape + + @pixel_shape.setter + def pixel_shape(self, value): + self._pixel_shape = value + + _pixel_bounds = None + + @property + def pixel_bounds(self): + return self._pixel_bounds + + @pixel_bounds.setter + def pixel_bounds(self, value): + self._pixel_bounds = value + + def pixel_to_world_values(self, pixel_array): + return np.asarray(pixel_array - 10) * 3e9 + 4e9 + + def world_to_pixel_values(self, world_array): + return np.asarray(world_array - 4e9) / 3e9 + 10 + + @property + def world_axis_object_components(self): + return (("test", 0, "value"),) + + @property + def world_axis_object_classes(self): + return {"test": (Quantity, (), {"unit": "Hz"})} + + +@pytest.fixture +def spectral_1d_ape14_wcs(): + return Spectral1DLowLevelWCS() + + +class Celestial2DLowLevelWCS(BaseLowLevelWCS): + @property + def pixel_n_dim(self): + return 2 + + @property + def world_n_dim(self): + return 2 + + @property + def world_axis_physical_types(self): + return "pos.eq.ra", "pos.eq.dec" + + @property + def world_axis_units(self): + return "deg", "deg" + + @property + def world_axis_names(self): + return "Right Ascension", "Declination" + + @property + def pixel_shape(self): + return (6, 7) + + @property + def pixel_bounds(self): + return (-1, 5), (1, 7) + + def pixel_to_world_values(self, px, py): + return (-(np.asarray(px) - 5.0) * 2 + 4.0, (np.asarray(py) - 6.0) * 2) + + def world_to_pixel_values(self, wx, wy): + return (-(np.asarray(wx) - 4.0) / 2 + 5.0, np.asarray(wy) / 2 + 6.0) + + @property + def world_axis_object_components(self): + return [ + ("test", 0, "spherical.lon.degree"), + ("test", 1, "spherical.lat.degree"), + ] + + @property + def world_axis_object_classes(self): + return {"test": (SkyCoord, (), {"unit": "deg"})} + + +@pytest.fixture +def celestial_2d_ape14_wcs(): + return Celestial2DLowLevelWCS() diff --git a/astropy/wcs/wcsapi/data/ucds.txt b/astropy/wcs/wcsapi/data/ucds.txt new file mode 100644 index 000000000000..5e522cd78dc9 --- /dev/null +++ b/astropy/wcs/wcsapi/data/ucds.txt @@ -0,0 +1,474 @@ +# Copied from UCD1+ v1.23 +arith +arith.diff +arith.factor +arith.grad +arith.rate +arith.ratio +arith.zp +em +em.radio +em.radio.20-100MHz +em.radio.100-200MHz +em.radio.200-400MHz +em.radio.400-750MHz +em.radio.750-1500MHz +em.radio.1500-3000MHz +em.radio.3-6GHz +em.radio.6-12GHz +em.radio.12-30GHz +em.mm +em.mm.30-50GHz +em.mm.50-100GHz +em.mm.100-200GHz +em.mm.200-400GHz +em.mm.400-750GHz +em.mm.750-1500GHz +em.mm.1500-3000GHz +em.IR +em.IR.J +em.IR.H +em.IR.K +em.IR.3-4um +em.IR.4-8um +em.IR.8-15um +em.IR.15-30um +em.IR.30-60um +em.IR.60-100um +em.IR.NIR +em.IR.MIR +em.IR.FIR +em.opt +em.opt.U +em.opt.B +em.opt.V +em.opt.R +em.opt.I +em.UV +em.UV.10-50nm +em.UV.50-100nm +em.UV.100-200nm +em.UV.200-300nm +em.UV.FUV +em.X-ray +em.X-ray.soft +em.X-ray.medium +em.X-ray.hard +em.gamma +em.gamma.soft +em.gamma.hard +em.line +em.line.Brgamma +em.line.HI +em.line.Halpha +em.line.Hbeta +em.line.Hgamma +em.line.Hdelta +em.line.Lyalpha +em.line.OIII +em.line.CO +em.bin +em.energy +em.freq +em.wavenumber +em.wl +em.wl.central +em.wl.effective +instr +instr.background +instr.bandpass +instr.bandwidth +instr.baseline +instr.beam +instr.calib +instr.det +instr.det.noise +instr.det.psf +instr.det.qe +instr.dispersion +instr.filter +instr.fov +instr.obsty +instr.obsty.seeing +instr.offset +instr.order +instr.param +instr.pixel +instr.plate +instr.plate.emulsion +instr.precision +instr.saturation +instr.scale +instr.sensitivity +instr.setup +instr.skyLevel +instr.skyTemp +instr.tel +instr.tel.focalLength +meta +meta.abstract +meta.bib +meta.bib.author +meta.bib.bibcode +meta.bib.fig +meta.bib.journal +meta.bib.page +meta.bib.volume +meta.code +meta.code.class +meta.code.error +meta.code.member +meta.code.mime +meta.code.multip +meta.code.qual +meta.code.status +meta.cryptic +meta.curation +meta.dataset +meta.email +meta.file +meta.fits +meta.id +meta.id.assoc +meta.id.CoI +meta.id.cross +meta.id.parent +meta.id.part +meta.id.PI +meta.main +meta.modelled +meta.note +meta.number +meta.record +meta.ref +meta.ref.ivorn +meta.ref.uri +meta.ref.url +meta.software +meta.table +meta.title +meta.ucd +meta.unit +meta.version +obs +obs.airMass +obs.atmos +obs.atmos.extinction +obs.atmos.refractAngle +obs.calib +obs.calib.flat +obs.exposure +obs.field +obs.image +obs.observer +obs.param +obs.proposal +obs.proposal.cycle +obs.sequence +phot +phot.antennaTemp +phot.calib +phot.color +phot.color.excess +phot.color.reddFree +phot.count +phot.fluence +phot.flux +phot.flux.bol +phot.flux.density +phot.flux.density.sb +phot.flux.sb +phot.limbDark +phot.mag +phot.mag.bc +phot.mag.bol +phot.mag.distMod +phot.mag.reddFree +phot.mag.sb +phys +phys.SFR +phys.absorption +phys.absorption.coeff +phys.absorption.gal +phys.absorption.opticalDepth +phys.abund +phys.abund.Fe +phys.abund.X +phys.abund.Y +phys.abund.Z +phys.acceleration +phys.albedo +phys.angArea +phys.angMomentum +phys.angSize +phys.angSize.smajAxis +phys.angSize.sminAxis +phys.area +phys.atmol +phys.atmol.branchingRatio +phys.atmol.collStrength +phys.atmol.collisional +phys.atmol.configuration +phys.atmol.crossSection +phys.atmol.element +phys.atmol.excitation +phys.atmol.final +phys.atmol.initial +phys.atmol.ionStage +phys.atmol.ionization +phys.atmol.lande +phys.atmol.level +phys.atmol.lifetime +phys.atmol.lineShift +phys.atmol.number +phys.atmol.oscStrength +phys.atmol.parity +phys.atmol.qn +phys.atmol.radiationType +phys.atmol.symmetry +phys.atmol.sWeight +phys.atmol.sWeight.nuclear +phys.atmol.term +phys.atmol.transProb +phys.atmol.transition +phys.atmol.wOscStrength +phys.atmol.weight +phys.columnDensity +phys.composition +phys.composition.massLightRatio +phys.composition.yield +phys.cosmology +phys.damping +phys.density +phys.dielectric +phys.dispMeasure +phys.electField +phys.electron +phys.electron.degen +phys.emissMeasure +phys.emissivity +phys.energy +phys.energy.density +phys.entropy +phys.eos +phys.excitParam +phys.gauntFactor +phys.gravity +phys.ionizParam +phys.ionizParam.coll +phys.ionizParam.rad +phys.luminosity +phys.luminosity.fun +phys.magAbs +phys.magAbs.bol +phys.magField +phys.mass +phys.mass.loss +phys.mol +phys.mol.dipole +phys.mol.dipole.electric +phys.mol.dipole.magnetic +phys.mol.dissociation +phys.mol.formationHeat +phys.mol.quadrupole +phys.mol.quadrupole.electric +phys.mol.rotation +phys.mol.vibration +phys.particle.neutrino +phys.polarization +phys.polarization.circular +phys.polarization.linear +phys.polarization.rotMeasure +phys.polarization.stokes +phys.pressure +phys.recombination.coeff +phys.refractIndex +phys.size +phys.size.axisRatio +phys.size.diameter +phys.size.radius +phys.size.smajAxis +phys.size.sminAxis +phys.temperature +phys.temperature.effective +phys.temperature.electron +phys.transmission +phys.veloc +phys.veloc.ang +phys.veloc.dispersion +phys.veloc.escape +phys.veloc.expansion +phys.veloc.microTurb +phys.veloc.orbital +phys.veloc.pulsat +phys.veloc.rotat +phys.veloc.transverse +phys.virial +pos +pos.angDistance +pos.angResolution +pos.az +pos.az.alt +pos.az.azi +pos.az.zd +pos.barycenter +pos.bodyrc +pos.bodyrc.alt +pos.bodyrc.lat +pos.bodyrc.lon +pos.cartesian +pos.cartesian.x +pos.cartesian.y +pos.cartesian.z +pos.cmb +pos.dirCos +pos.distance +pos.earth +pos.earth.altitude +pos.earth.lat +pos.earth.lon +pos.ecliptic +pos.ecliptic.lat +pos.ecliptic.lon +pos.eop +pos.eop.nutation +pos.ephem +pos.eq +pos.eq.dec +pos.eq.ha +pos.eq.ra +pos.eq.spd +pos.errorEllipse +pos.frame +pos.galactic +pos.galactic.lat +pos.galactic.lon +pos.galactocentric +pos.geocentric +pos.healpix +pos.heliocentric +pos.HTM +pos.lambert +pos.lg +pos.lsr +pos.lunar +pos.lunar.occult +pos.parallax +pos.parallax.dyn +pos.parallax.phot +pos.parallax.spect +pos.parallax.trig +pos.phaseAng +pos.pm +pos.posAng +pos.precess +pos.supergalactic +pos.supergalactic.lat +pos.supergalactic.lon +pos.wcs +pos.wcs.cdmatrix +pos.wcs.crpix +pos.wcs.crval +pos.wcs.ctype +pos.wcs.naxes +pos.wcs.naxis +pos.wcs.scale +spect +spect.binSize +spect.continuum +spect.dopplerParam +spect.dopplerVeloc +spect.dopplerVeloc.opt +spect.dopplerVeloc.radio +spect.index +spect.line +spect.line.asymmetry +spect.line.broad +spect.line.broad.Stark +spect.line.broad.Zeeman +spect.line.eqWidth +spect.line.intensity +spect.line.profile +spect.line.strength +spect.line.width +spect.resolution +src +src.calib +src.calib.guideStar +src.class +src.class.color +src.class.distance +src.class.luminosity +src.class.richness +src.class.starGalaxy +src.class.struct +src.density +src.ellipticity +src.impactParam +src.morph +src.morph.param +src.morph.scLength +src.morph.type +src.net +src.orbital +src.orbital.eccentricity +src.orbital.inclination +src.orbital.meanAnomaly +src.orbital.meanMotion +src.orbital.node +src.orbital.periastron +src.redshift +src.redshift.phot +src.sample +src.spType +src.var +src.var.amplitude +src.var.index +src.var.pulse +stat +stat.Fourier +stat.Fourier.amplitude +stat.correlation +stat.covariance +stat.error +stat.error.sys +stat.filling +stat.fit +stat.fit.chi2 +stat.fit.dof +stat.fit.goodness +stat.fit.omc +stat.fit.param +stat.fit.residual +stat.likelihood +stat.max +stat.mean +stat.median +stat.min +stat.param +stat.probability +stat.snr +stat.stdev +stat.uncalib +stat.value +stat.variance +stat.weight +time +time.age +time.creation +time.crossing +time.duration +time.end +time.epoch +time.equinox +time.interval +time.lifetime +time.period +time.phase +time.processing +time.publiYear +time.relax +time.release +time.resolution +time.scale +time.start diff --git a/astropy/wcs/wcsapi/fitswcs.py b/astropy/wcs/wcsapi/fitswcs.py new file mode 100644 index 000000000000..da51b2bcb8da --- /dev/null +++ b/astropy/wcs/wcsapi/fitswcs.py @@ -0,0 +1,830 @@ +# This file includes the definition of a mix-in class that provides the low- +# and high-level WCS API to the astropy.wcs.WCS object. We keep this code +# isolated in this mix-in class to avoid making the main wcs.py file too +# long. + +import warnings + +import numpy as np + +import astropy.constants +from astropy import units as u +from astropy.coordinates import ICRS, Galactic, SpectralCoord +from astropy.coordinates.spectral_coordinate import ( + attach_zero_velocities, + update_differentials_to_match, +) +from astropy.units import allclose as quantity_allclose +from astropy.utils.exceptions import AstropyDeprecationWarning, AstropyUserWarning + +from .high_level_api import HighLevelWCSMixin +from .low_level_api import BaseLowLevelWCS +from .wrappers import SlicedLowLevelWCS + +__all__ = ["FITSWCSAPIMixin", "SlicedFITSWCS", "custom_ctype_to_ucd_mapping"] + +C_SI = astropy.constants.c.si.value + +VELOCITY_FRAMES = { + "GEOCENT": "gcrs", + "BARYCENT": "icrs", + "HELIOCENT": "hcrs", + "LSRK": "lsrk", + "LSRD": "lsrd", +} + +# The spectra velocity frames below are needed for FITS spectral WCS +# (see Greisen 06 table 12) but aren't yet defined as real +# astropy.coordinates frames, so we instead define them here as instances +# of existing coordinate frames with offset velocities. In future we should +# make these real frames so that users can more easily recognize these +# velocity frames when used in SpectralCoord. + +# This frame is defined as a velocity of 220 km/s in the +# direction of l=90, b=0. The rotation velocity is defined +# in: +# +# Kerr and Lynden-Bell 1986, Review of galactic constants. +# +# NOTE: this may differ from the assumptions of galcen_v_sun +# in the Galactocentric frame - the value used here is +# the one adopted by the WCS standard for spectral +# transformations. + +VELOCITY_FRAMES["GALACTOC"] = Galactic( + u=0 * u.km, + v=0 * u.km, + w=0 * u.km, + U=0 * u.km / u.s, + V=-220 * u.km / u.s, + W=0 * u.km / u.s, + representation_type="cartesian", + differential_type="cartesian", +) + +# This frame is defined as a velocity of 300 km/s in the +# direction of l=90, b=0. This is defined in: +# +# Transactions of the IAU Vol. XVI B Proceedings of the +# 16th General Assembly, Reports of Meetings of Commissions: +# Comptes Rendus Des SÊances Des Commissions, Commission 28, +# p201. +# +# Note that these values differ from those used by CASA +# (308 km/s towards l=105, b=-7) but we use the above values +# since these are the ones defined in Greisen et al (2006). + +VELOCITY_FRAMES["LOCALGRP"] = Galactic( + u=0 * u.km, + v=0 * u.km, + w=0 * u.km, + U=0 * u.km / u.s, + V=-300 * u.km / u.s, + W=0 * u.km / u.s, + representation_type="cartesian", + differential_type="cartesian", +) + +# This frame is defined as a velocity of 368 km/s in the +# direction of l=263.85, b=48.25. This is defined in: +# +# Bennett et al. (2003), First-Year Wilkinson Microwave +# Anisotropy Probe (WMAP) Observations: Preliminary Maps +# and Basic Results +# +# Note that in that paper, the dipole is expressed as a +# temperature (T=3.346 +/- 0.017mK) + +VELOCITY_FRAMES["CMBDIPOL"] = Galactic( + l=263.85 * u.deg, + b=48.25 * u.deg, + distance=0 * u.km, + radial_velocity=-(3.346e-3 / 2.725 * astropy.constants.c).to(u.km / u.s), +) + + +# Mapping from CTYPE axis name to UCD1 + +CTYPE_TO_UCD1 = { + # Celestial coordinates + "RA": "pos.eq.ra", + "DEC": "pos.eq.dec", + "GLON": "pos.galactic.lon", + "GLAT": "pos.galactic.lat", + "ELON": "pos.ecliptic.lon", + "ELAT": "pos.ecliptic.lat", + "TLON": "pos.bodyrc.lon", + "TLAT": "pos.bodyrc.lat", + "HPLT": "custom:pos.helioprojective.lat", + "HPLN": "custom:pos.helioprojective.lon", + "HPRZ": "custom:pos.helioprojective.z", + "HGLN": "custom:pos.heliographic.stonyhurst.lon", + "HGLT": "custom:pos.heliographic.stonyhurst.lat", + "CRLN": "custom:pos.heliographic.carrington.lon", + "CRLT": "custom:pos.heliographic.carrington.lat", + "SOLX": "custom:pos.heliocentric.x", + "SOLY": "custom:pos.heliocentric.y", + "SOLZ": "custom:pos.heliocentric.z", + # Spectral coordinates (WCS paper 3) + "FREQ": "em.freq", # Frequency + "ENER": "em.energy", # Energy + "WAVN": "em.wavenumber", # Wavenumber + "WAVE": "em.wl", # Vacuum wavelength + "VRAD": "spect.dopplerVeloc.radio", # Radio velocity + "VOPT": "spect.dopplerVeloc.opt", # Optical velocity + "ZOPT": "src.redshift", # Redshift + "AWAV": "em.wl;obs.atmos", # Air wavelength + "VELO": "spect.dopplerVeloc", # Apparent radial velocity + "BETA": "custom:spect.doplerVeloc.beta", # Beta factor (v/c) + "STOKES": "phys.polarization.stokes", # STOKES parameters + # Time coordinates (https://www.aanda.org/articles/aa/pdf/2015/02/aa24653-14.pdf) + "TIME": "time", + "TAI": "time", + "TT": "time", + "TDT": "time", + "ET": "time", + "IAT": "time", + "UT1": "time", + "UTC": "time", + "GMT": "time", + "GPS": "time", + "TCG": "time", + "TCB": "time", + "TDB": "time", + "LOCAL": "time", + # Distance coordinates + "DIST": "pos.distance", + "DSUN": "custom:pos.distance.sunToObserver", + # UT() and TT() are handled separately in world_axis_physical_types +} + +# Keep a list of additional custom mappings that have been registered. This +# is kept as a list in case nested context managers are used +CTYPE_TO_UCD1_CUSTOM = [] + + +class custom_ctype_to_ucd_mapping: + """ + A context manager that makes it possible to temporarily add new CTYPE to + UCD1+ mapping used by :attr:`FITSWCSAPIMixin.world_axis_physical_types`. + + Parameters + ---------- + mapping : dict + A dictionary mapping a CTYPE value to a UCD1+ value + + Examples + -------- + Consider a WCS with the following CTYPE:: + + >>> from astropy.wcs import WCS + >>> wcs = WCS(naxis=1) + >>> wcs.wcs.ctype = ['SPAM'] + + By default, :attr:`FITSWCSAPIMixin.world_axis_physical_types` returns `None`, + but this can be overridden:: + + >>> wcs.world_axis_physical_types + [None] + >>> with custom_ctype_to_ucd_mapping({'SPAM': 'food.spam'}): + ... wcs.world_axis_physical_types + ['food.spam'] + """ + + def __init__(self, mapping): + CTYPE_TO_UCD1_CUSTOM.insert(0, mapping) + self.mapping = mapping + + def __enter__(self): + pass + + def __exit__(self, type, value, tb): + CTYPE_TO_UCD1_CUSTOM.remove(self.mapping) + + +class SlicedFITSWCS(SlicedLowLevelWCS, HighLevelWCSMixin): + pass + + +class FITSWCSAPIMixin(BaseLowLevelWCS, HighLevelWCSMixin): + """ + A mix-in class that is intended to be inherited by the + :class:`~astropy.wcs.WCS` class and provides the low- and high-level WCS API. + """ + + @property + def pixel_n_dim(self): + return self.naxis + + @property + def world_n_dim(self): + return len(self.wcs.ctype) + + @property + def array_shape(self): + if self.pixel_shape is None: + return None + else: + return self.pixel_shape[::-1] + + @array_shape.setter + def array_shape(self, value): + if value is None: + self.pixel_shape = None + else: + self.pixel_shape = value[::-1] + + @property + def pixel_shape(self): + if all(i == 0 for i in self._naxis): + return None + else: + return tuple(self._naxis) + + @pixel_shape.setter + def pixel_shape(self, value): + if value is None: + self._naxis = self.naxis * [0] + else: + if len(value) != self.naxis: + raise ValueError( + f"The number of data axes, {self.naxis}, does not equal the shape" + f" {len(value)}." + ) + self._naxis = list(value) + + @property + def pixel_bounds(self): + return self._pixel_bounds + + @pixel_bounds.setter + def pixel_bounds(self, value): + if value is None: + self._pixel_bounds = value + else: + if len(value) != self.naxis: + raise ValueError( + "The number of data axes, " + f"{self.naxis}, does not equal the number of " + f"pixel bounds {len(value)}." + ) + self._pixel_bounds = list(value) + + @property + def world_axis_physical_types(self): + types = [] + # TODO: need to support e.g. TT(TAI) + for ctype in self.wcs.ctype: + if ctype.upper().startswith(("UT(", "TT(")): + types.append("time") + else: + ctype_name = ctype.split("-")[0] + for custom_mapping in CTYPE_TO_UCD1_CUSTOM: + if ctype_name in custom_mapping: + types.append(custom_mapping[ctype_name]) + break + else: + types.append(CTYPE_TO_UCD1.get(ctype_name.upper())) + return types + + @property + def world_axis_units(self): + units = [] + for unit in self.wcs.cunit: + if unit is None: + unit = "" + elif isinstance(unit, u.Unit): + unit = unit.to_string(format="vounit") + else: + try: + unit = u.Unit(unit).to_string(format="vounit") + except u.UnitsError: + unit = "" + units.append(unit) + return units + + @property + def world_axis_names(self): + return list(self.wcs.cname) + + @property + def axis_correlation_matrix(self): + # If there are any distortions present, we assume that there may be + # correlations between all axes. Maybe if some distortions only apply + # to the image plane we can improve this? + if self.has_distortion: + return np.ones((self.world_n_dim, self.pixel_n_dim), dtype=bool) + + # Assuming linear world coordinates along each axis, the correlation + # matrix would be given by whether or not the PC matrix is zero + matrix = self.wcs.get_pc() != 0 + + # We now need to check specifically for celestial coordinates since + # these can assume correlations because of spherical distortions. For + # each celestial coordinate we copy over the pixel dependencies from + # the other celestial coordinates. + celestial = (self.wcs.axis_types // 1000) % 10 == 2 + celestial_indices = np.nonzero(celestial)[0] + for world1 in celestial_indices: + for world2 in celestial_indices: + if world1 != world2: + matrix[world1] |= matrix[world2] + matrix[world2] |= matrix[world1] + + return matrix + + def _out_of_bounds_to_nan(self, pixel_arrays): + if self.pixel_bounds is not None: + pixel_arrays = list(pixel_arrays) + for idim in range(self.pixel_n_dim): + if self.pixel_bounds[idim] is None: + continue + out_of_bounds = (pixel_arrays[idim] < self.pixel_bounds[idim][0]) | ( + pixel_arrays[idim] > self.pixel_bounds[idim][1] + ) + if np.any(out_of_bounds): + pix = pixel_arrays[idim] + if np.isscalar(pix): + pix = np.nan + else: + pix = pix.astype(float, copy=True) + pix[out_of_bounds] = np.nan + pixel_arrays[idim] = pix + return pixel_arrays + + def pixel_to_world_values(self, *pixel_arrays): + pixel_arrays = self._out_of_bounds_to_nan(pixel_arrays) + world = self.all_pix2world(*pixel_arrays, 0) + return world[0] if self.world_n_dim == 1 else tuple(world) + + def world_to_pixel_values(self, *world_arrays): + # avoid circular import + from astropy.wcs.wcs import NoConvergence + + try: + pixel = self.all_world2pix(*world_arrays, 0) + except NoConvergence as e: + warnings.warn(str(e)) + # use best_solution contained in the exception and format the same + # way as all_world2pix does (using _array_converter) + pixel = self._array_converter( + lambda *args: e.best_solution, "input", *world_arrays, 0 + ) + + pixel = self._out_of_bounds_to_nan(pixel) + + return pixel[0] if self.pixel_n_dim == 1 else tuple(pixel) + + @property + def world_axis_object_components(self): + return self._get_components_and_classes()[0] + + @property + def world_axis_object_classes(self): + return self._get_components_and_classes()[1] + + @property + def serialized_classes(self): + return False + + def _get_components_and_classes(self): + # The aim of this function is to return whatever is needed for + # world_axis_object_components and world_axis_object_classes. It's easier + # to figure it out in one go and then return the values and let the + # properties return part of it. + + # Since this method might get called quite a few times, we need to cache + # it. We start off by defining a hash based on the attributes of the + # WCS that matter here (we can't just use the WCS object as a hash since + # it is mutable) + wcs_hash = ( + self.naxis, + list(self.wcs.ctype), + list(self.wcs.cunit), + self.wcs.radesys, + self.wcs.specsys, + self.wcs.equinox, + self.wcs.dateobs, + self.wcs.lng, + self.wcs.lat, + ) + + # If the cache is present, we need to check that the 'hash' matches. + if (cache := getattr(self, "_components_and_classes_cache", None)) is not None: + if cache[0] == wcs_hash: + return cache[1] + else: + self._components_and_classes_cache = None + + # Avoid circular imports by importing here + from astropy.coordinates import EarthLocation, SkyCoord, StokesCoord + from astropy.time import Time, TimeDelta + from astropy.time.formats import FITS_DEPRECATED_SCALES + from astropy.wcs.utils import wcs_to_celestial_frame + + components = [None] * self.naxis + classes = {} + + # Let's start off by checking whether the WCS has a pair of celestial + # components + + if self.has_celestial: + try: + celestial_frame = wcs_to_celestial_frame(self) + except ValueError: + # Some WCSes, e.g. solar, can be recognized by WCSLIB as being + # celestial but we don't necessarily have frames for them. + celestial_frame = None + else: + kwargs = {} + kwargs["frame"] = celestial_frame + # Very occasionally (i.e. with TAB) wcs does not convert the units + lon_unit = u.Unit(self.wcs.cunit[self.wcs.lng]) + lat_unit = u.Unit(self.wcs.cunit[self.wcs.lat]) + kwargs["unit"] = ( + lon_unit, + lat_unit, + ) + + classes["celestial"] = (SkyCoord, (), kwargs) + components[self.wcs.lng] = ( + "celestial", + 0, + lambda c: c.spherical.lon.to_value(lon_unit), + ) + components[self.wcs.lat] = ( + "celestial", + 1, + lambda c: c.spherical.lat.to_value(lat_unit), + ) + + # Next, we check for spectral components + + if self.has_spectral: + # Find index of spectral coordinate + ispec = self.wcs.spec + ctype = self.wcs.ctype[ispec][:4] + ctype = ctype.upper() + + kwargs = {} + + # Determine observer location and velocity + + # TODO: determine how WCS standard would deal with observer on a + # spacecraft far from earth. For now assume the obsgeo parameters, + # if present, give the geocentric observer location. + + if np.isnan(self.wcs.obsgeo[0]): + observer = None + else: + earth_location = EarthLocation(*self.wcs.obsgeo[:3], unit=u.m) + + # Get the time scale from TIMESYS or fall back to 'utc' + tscale = self.wcs.timesys.lower() or "utc" + + if np.isnan(self.wcs.mjdavg): + obstime = Time( + self.wcs.mjdobs, + format="mjd", + scale=tscale, + location=earth_location, + ) + else: + obstime = Time( + self.wcs.mjdavg, + format="mjd", + scale=tscale, + location=earth_location, + ) + observer_location = SkyCoord(earth_location.get_itrs(obstime=obstime)) + + if self.wcs.specsys in VELOCITY_FRAMES: + frame = VELOCITY_FRAMES[self.wcs.specsys] + observer = observer_location.transform_to(frame) + if isinstance(frame, str): + observer = attach_zero_velocities(observer) + else: + observer = update_differentials_to_match( + observer_location, + VELOCITY_FRAMES[self.wcs.specsys], + preserve_observer_frame=True, + ) + elif self.wcs.specsys == "TOPOCENT": + observer = attach_zero_velocities(observer_location) + else: + raise NotImplementedError( + f"SPECSYS={self.wcs.specsys} not yet supported" + ) + + # Determine target + + # This is tricker. In principle the target for each pixel is the + # celestial coordinates of the pixel, but we then need to be very + # careful about SSYSOBS which is tricky. For now, we set the + # target using the reference celestial coordinate in the WCS (if + # any). + + if self.has_celestial and celestial_frame is not None: + # NOTE: celestial_frame was defined higher up + + # NOTE: we set the distance explicitly to avoid warnings in SpectralCoord + + target = SkyCoord( + self.wcs.crval[self.wcs.lng] * self.wcs.cunit[self.wcs.lng], + self.wcs.crval[self.wcs.lat] * self.wcs.cunit[self.wcs.lat], + frame=celestial_frame, + distance=1000 * u.kpc, + ) + + target = attach_zero_velocities(target) + + else: + target = None + + # SpectralCoord does not work properly if either observer or target + # are not convertible to ICRS, so if this is the case, we (for now) + # drop the observer and target from the SpectralCoord and warn the + # user. + + if observer is not None: + try: + observer.transform_to(ICRS()) + except Exception: + warnings.warn( + "observer cannot be converted to ICRS, so will " + "not be set on SpectralCoord", + AstropyUserWarning, + ) + observer = None + + if target is not None: + try: + target.transform_to(ICRS()) + except Exception: + warnings.warn( + "target cannot be converted to ICRS, so will " + "not be set on SpectralCoord", + AstropyUserWarning, + ) + target = None + + # NOTE: below we include Quantity in classes['spectral'] instead + # of SpectralCoord - this is because we want to also be able to + # accept plain quantities. + + def apply_velocity_frame_change(spectralcoord): + if observer is None and spectralcoord.observer is None: + # When both observers are missing we silently skip the frame + # change since this is a common case and not worth warning + # about. + return spectralcoord + + if observer is None: + msg = "No observer defined on WCS" + elif spectralcoord.observer is None: + msg = "No observer defined on SpectralCoord" + elif spectralcoord.target is None: + msg = "No target defined on SpectralCoord" + else: + return spectralcoord.with_observer_stationary_relative_to(observer) + + warnings.warn( + f"{msg}, SpectralCoord " + "will be converted without any velocity " + "frame change", + AstropyUserWarning, + ) + + return spectralcoord + + if ctype == "ZOPT": + + def spectralcoord_from_redshift(redshift): + if isinstance(redshift, SpectralCoord): + return redshift + return SpectralCoord( + (redshift + 1) * self.wcs.restwav, + unit=u.m, + observer=observer, + target=target, + ) + + def redshift_from_spectralcoord(spectralcoord): + # TODO: check target is consistent between WCS and SpectralCoord, + # if they are not the transformation doesn't make conceptual sense. + return ( + apply_velocity_frame_change(spectralcoord).to_value(u.m) + / self.wcs.restwav + - 1.0 + ) + + classes["spectral"] = (u.Quantity, (), {}, spectralcoord_from_redshift) + components[self.wcs.spec] = ("spectral", 0, redshift_from_spectralcoord) + + elif ctype == "BETA": + + def spectralcoord_from_beta(beta): + if isinstance(beta, SpectralCoord): + return beta + return SpectralCoord( + beta * C_SI, + unit=u.m / u.s, + doppler_convention="relativistic", + doppler_rest=self.wcs.restwav * u.m, + observer=observer, + target=target, + ) + + def beta_from_spectralcoord(spectralcoord): + # TODO: check target is consistent between WCS and SpectralCoord, + # if they are not the transformation doesn't make conceptual sense. + doppler_equiv = u.doppler_relativistic(self.wcs.restwav * u.m) + return ( + apply_velocity_frame_change(spectralcoord).to_value( + u.m / u.s, doppler_equiv + ) + / C_SI + ) + + classes["spectral"] = (u.Quantity, (), {}, spectralcoord_from_beta) + components[self.wcs.spec] = ("spectral", 0, beta_from_spectralcoord) + + else: + kwargs["unit"] = self.wcs.cunit[ispec] + + # Make sure that if restfrq is defined and restwav is not or + # vice-versa, we define the other one. Typically if e.g. + # RESTFRQ is defined in the original FITS header, wcs.restwav + # is 0. + + if ctype in ("VELO", "VRAD", "VOPT"): + restfrq = self.wcs.restfrq + restwav = self.wcs.restwav + + if restfrq > 0 or restwav > 0: + if restwav == 0: + restfrq = u.Quantity(restfrq, u.Hz) + restwav = restfrq.to(u.m, u.spectral()) + elif restfrq == 0: + restwav = u.Quantity(restwav, u.m) + restfrq = restwav.to(u.Hz, u.spectral()) + else: + restfrq = u.Quantity(restfrq, u.Hz) + restwav = u.Quantity(restwav, u.m) + restfrq_derived = restwav.to(u.Hz, u.spectral()) + if not quantity_allclose( + restfrq, restfrq_derived, rtol=1e-4 + ): + used = "restwav" if ctype == "VOPT" else "restfrq" + warnings.warn( + f"restfrq={restfrq} and restwav={restwav}={restfrq_derived} " + f"are not consistent to rtol=1e-4, choosing {used}. In future, " + f"this will raise an exception.", + AstropyDeprecationWarning, + ) + + if ctype == "VELO": + kwargs["doppler_convention"] = "relativistic" + kwargs["doppler_rest"] = restfrq + elif ctype == "VRAD": + kwargs["doppler_convention"] = "radio" + kwargs["doppler_rest"] = restfrq + elif ctype == "VOPT": + kwargs["doppler_convention"] = "optical" + kwargs["doppler_rest"] = restwav + + def spectralcoord_from_value(value): + if isinstance(value, SpectralCoord): + return value + return SpectralCoord( + value, observer=observer, target=target, **kwargs + ) + + def value_from_spectralcoord(spectralcoord): + # TODO: check target is consistent between WCS and SpectralCoord, + # if they are not the transformation doesn't make conceptual sense. + return apply_velocity_frame_change(spectralcoord).to_value(**kwargs) + + classes["spectral"] = (u.Quantity, (), {}, spectralcoord_from_value) + components[self.wcs.spec] = ("spectral", 0, value_from_spectralcoord) + + # We can then make sure we correctly return Time objects where appropriate + # (https://www.aanda.org/articles/aa/pdf/2015/02/aa24653-14.pdf) + + if "time" in self.world_axis_physical_types: + multiple_time = self.world_axis_physical_types.count("time") > 1 + + for i in range(self.naxis): + if self.world_axis_physical_types[i] == "time": + if multiple_time: + name = f"time.{i}" + else: + name = "time" + + # Initialize delta + reference_time_delta = None + + # Extract time scale, and remove any algorithm code + scale = self.wcs.ctype[i].split("-")[0].lower() + + if scale == "time": + if self.wcs.timesys: + scale = self.wcs.timesys.lower() + else: + scale = "utc" + + # Drop sub-scales + if "(" in scale: + pos = scale.index("(") + scale, subscale = scale[:pos], scale[pos + 1 : -1] + warnings.warn( + "Dropping unsupported sub-scale " + f"{subscale.upper()} from scale {scale.upper()}", + UserWarning, + ) + + # TODO: consider having GPS as a scale in Time + # For now GPS is not a scale, we approximate this by TAI - 19s + if scale == "gps": + reference_time_delta = TimeDelta(19, format="sec") + scale = "tai" + + elif scale.upper() in FITS_DEPRECATED_SCALES: + scale = FITS_DEPRECATED_SCALES[scale.upper()] + + elif scale not in Time.SCALES: + raise ValueError(f"Unrecognized time CTYPE={self.wcs.ctype[i]}") + + # Determine location + trefpos = self.wcs.trefpos.lower() + + if trefpos.startswith("topocent"): + # Note that some headers use TOPOCENT instead of TOPOCENTER + if np.any(np.isnan(self.wcs.obsgeo[:3])): + warnings.warn( + "Missing or incomplete observer location " + "information, setting location in Time to None", + UserWarning, + ) + location = None + else: + location = EarthLocation(*self.wcs.obsgeo[:3], unit=u.m) + elif trefpos == "geocenter": + location = EarthLocation(0, 0, 0, unit=u.m) + elif trefpos == "": + location = None + else: + # TODO: implement support for more locations when Time supports it + warnings.warn( + f"Observation location '{trefpos}' is not " + "supported, setting location in Time to None", + UserWarning, + ) + location = None + + reference_time = Time( + np.nan_to_num(self.wcs.mjdref[0]), + np.nan_to_num(self.wcs.mjdref[1]), + format="mjd", + scale=scale, + location=location, + ) + + if reference_time_delta is not None: + reference_time = reference_time + reference_time_delta + + def time_from_reference_and_offset(offset): + if isinstance(offset, Time): + return offset + return reference_time + TimeDelta(offset, format="sec") + + def offset_from_time_and_reference(time): + return (time - reference_time).sec + + classes[name] = (Time, (), {}, time_from_reference_and_offset) + components[i] = (name, 0, offset_from_time_and_reference) + + if "phys.polarization.stokes" in self.world_axis_physical_types: + for i in range(self.naxis): + if self.world_axis_physical_types[i] == "phys.polarization.stokes": + name = "stokes" + classes[name] = (StokesCoord, (), {}) + components[i] = (name, 0, "value") + + # Fallback: for any remaining components that haven't been identified, just + # return Quantity as the class to use + + for i in range(self.naxis): + if components[i] is None: + name = self.wcs.ctype[i].split("-")[0].lower() + if name == "": + name = "world" + while name in classes: + name += "_" + classes[name] = (u.Quantity, (), {"unit": self.wcs.cunit[i]}) + components[i] = (name, 0, "value") + + # Keep a cached version of result + self._components_and_classes_cache = wcs_hash, (components, classes) + + return components, classes diff --git a/astropy/wcs/wcsapi/high_level_api.py b/astropy/wcs/wcsapi/high_level_api.py new file mode 100644 index 000000000000..ef10bc40c0ca --- /dev/null +++ b/astropy/wcs/wcsapi/high_level_api.py @@ -0,0 +1,436 @@ +import abc +import numbers +from collections import OrderedDict, defaultdict +from collections.abc import Callable +from typing import Any, Protocol + +import numpy as np +from numpy.typing import ArrayLike + +from astropy.utils.masked import Masked, MaskedNDArray, combine_masks + +from .utils import deserialize_class + +__all__ = [ + "BaseHighLevelWCS", + "HighLevelWCSMixin", + "high_level_objects_to_values", + "values_to_high_level_objects", +] + + +_WorldAxisComponent = tuple[str, str | int, str | Callable[[Any], Any]] +_WorldAxisClass = ( + tuple[type[Any] | str, tuple[Any, ...], dict[str, Any]] + | tuple[type[Any] | str, tuple[Any, ...], dict[str, Any], Callable[..., Any]] +) + + +class _WorldAxisMetadata(Protocol): + """ + Structural-subtyping interface for the world axis metadata used by + `high_level_objects_to_values` and `values_to_high_level_objects`. + + Any object exposing the two attributes below is accepted as the + ``low_level_wcs`` argument of those functions; this includes any + `BaseLowLevelWCS` instance. The optional ``serialized_classes`` attribute + is recognised when present and otherwise treated as ``False``. + """ + + world_axis_object_classes: dict[str, _WorldAxisClass] + world_axis_object_components: list[_WorldAxisComponent] + + +def rec_getattr(obj, att): + for a in att.split("."): + obj = getattr(obj, a) + return obj + + +def default_order(components): + order = [] + for key, _, _ in components: + if key not in order: + order.append(key) + return order + + +def _toindex(value): + """Convert value to an int or an int array. + + Input coordinates converted to integers + corresponding to the center of the pixel. + The convention is that the center of the pixel is + (0, 0), while the lower left corner is (-0.5, -0.5). + The outputs are used to index the mask. + + Examples + -------- + >>> _toindex(np.array([-0.5, 0.49999])) + array([0, 0]) + >>> _toindex(np.array([0.5, 1.49999])) + array([1, 1]) + >>> _toindex(np.array([1.5, 2.49999])) + array([2, 2]) + """ + arr = np.floor(np.asarray(value) + 0.5) + + fill_value = np.iinfo(int).min + if np.isscalar(arr): + if np.isnan(arr): + arr = fill_value + else: + arr[np.isnan(arr)] = fill_value + return np.asarray(arr, dtype=int) + + +class BaseHighLevelWCS(metaclass=abc.ABCMeta): + """ + Abstract base class for the high-level WCS interface. + + This is described in `APE 14: A shared Python interface for World Coordinate + Systems `_. + """ + + @property + @abc.abstractmethod + def low_level_wcs(self): + """ + Returns a reference to the underlying low-level WCS object. + """ + + @abc.abstractmethod + def pixel_to_world(self, *pixel_arrays): + """ + Convert pixel coordinates to world coordinates (represented by + high-level objects). + + If a single high-level object is used to represent the world coordinates + (i.e., if ``len(wcs.world_axis_object_classes) == 1``), it is returned + as-is (not in a tuple/list), otherwise a tuple of high-level objects is + returned. See + `~astropy.wcs.wcsapi.BaseLowLevelWCS.pixel_to_world_values` for pixel + indexing and ordering conventions. + """ + + def array_index_to_world(self, *index_arrays): + """ + Convert array indices to world coordinates (represented by Astropy + objects). + + If a single high-level object is used to represent the world coordinates + (i.e., if ``len(wcs.world_axis_object_classes) == 1``), it is returned + as-is (not in a tuple/list), otherwise a tuple of high-level objects is + returned. See + `~astropy.wcs.wcsapi.BaseLowLevelWCS.array_index_to_world_values` for + pixel indexing and ordering conventions. + """ + return self.pixel_to_world(*index_arrays[::-1]) + + @abc.abstractmethod + def world_to_pixel(self, *world_objects): + """ + Convert world coordinates (represented by Astropy objects) to pixel + coordinates. + + If `~astropy.wcs.wcsapi.BaseLowLevelWCS.pixel_n_dim` is ``1``, this + method returns a single scalar or array, otherwise a tuple of scalars or + arrays is returned. See + `~astropy.wcs.wcsapi.BaseLowLevelWCS.world_to_pixel_values` for pixel + indexing and ordering conventions. + """ + + def world_to_array_index(self, *world_objects): + """ + Convert world coordinates (represented by Astropy objects) to array + indices. + + If `~astropy.wcs.wcsapi.BaseLowLevelWCS.pixel_n_dim` is ``1``, this + method returns a single scalar or array, otherwise a tuple of scalars or + arrays is returned. See + `~astropy.wcs.wcsapi.BaseLowLevelWCS.world_to_array_index_values` for + pixel indexing and ordering conventions. The indices should be returned + as rounded integers. + """ + if self.low_level_wcs.pixel_n_dim == 1: + return _toindex(self.world_to_pixel(*world_objects)) + else: + return tuple( + np.asarray(x) + for x in _toindex(self.world_to_pixel(*world_objects)[::-1]) + ) + + +def high_level_objects_to_values( + *world_objects: Any, low_level_wcs: _WorldAxisMetadata +) -> list[float | int | np.ndarray]: + """ + Convert the input high level object to low level values. + + This function uses the information in ``low_level_wcs.world_axis_object_classes`` + and ``low_level_wcs.world_axis_object_components`` to convert the high level + objects (such as `~.SkyCoord`) to low level "values" which should be scalars or + Numpy arrays. + + This is used in `.HighLevelWCSMixin.world_to_pixel`, but provided as a + separate function for use in other places where needed. + + Parameters + ---------- + *world_objects : `~astropy.coordinates.SkyCoord`, `~astropy.units.Quantity`, etc. + High level coordinate objects. + + low_level_wcs : `.BaseLowLevelWCS` or object + Source of the world axis metadata to use for the conversion. A full + `.BaseLowLevelWCS` instance is accepted, but any object exposing + ``world_axis_object_classes`` and ``world_axis_object_components`` + attributes also works (for example a `types.SimpleNamespace` or a + namedtuple). The ``serialized_classes`` attribute is read if present + and otherwise treated as ``False``. This is useful when the metadata + for the intended conversion direction does not match what a WCS + exposes by default. + """ + # Cache the classes and components since this may be expensive + serialized_classes = low_level_wcs.world_axis_object_classes + components = low_level_wcs.world_axis_object_components + + # Deserialize world_axis_object_classes using the default order + classes = OrderedDict() + for key in default_order(components): + if getattr(low_level_wcs, "serialized_classes", False): + classes[key] = deserialize_class(serialized_classes[key], construct=False) + else: + classes[key] = serialized_classes[key] + + # Check that the number of classes matches the number of inputs + if len(world_objects) != len(classes): + raise ValueError( + f"Number of world inputs ({len(world_objects)}) does not match expected" + f" ({len(classes)})" + ) + + # Determine whether the classes are uniquely matched, that is we check + # whether there is only one of each class. + world_by_key = {} + unique_match = True + for w in world_objects: + matches = [] + for key, (klass, *_) in classes.items(): + if isinstance(w, klass): + matches.append(key) + if len(matches) == 1: + world_by_key[matches[0]] = w + else: + unique_match = False + break + + # If the match is not unique, the order of the classes needs to match, + # whereas if all classes are unique, we can still intelligently match + # them even if the order is wrong. + + objects = {} + + if unique_match: + for key, (klass, args, kwargs, *rest) in classes.items(): + if len(rest) == 0: + klass_gen = klass + elif len(rest) == 1: + klass_gen = rest[0] + else: + raise ValueError( + "Tuples in world_axis_object_classes should have length 3 or 4" + ) + + # FIXME: For now SkyCoord won't auto-convert upon initialization + # https://github.com/astropy/astropy/issues/7689 + from astropy.coordinates import SkyCoord + + if isinstance(world_by_key[key], SkyCoord): + if "frame" in kwargs: + objects[key] = world_by_key[key].transform_to(kwargs["frame"]) + else: + objects[key] = world_by_key[key] + else: + objects[key] = klass_gen(world_by_key[key], *args, **kwargs) + + else: + for ikey, key in enumerate(classes): + klass, args, kwargs, *rest = classes[key] + + if len(rest) == 0: + klass_gen = klass + elif len(rest) == 1: + klass_gen = rest[0] + else: + raise ValueError( + "Tuples in world_axis_object_classes should have length 3 or 4" + ) + + w = world_objects[ikey] + if not isinstance(w, klass): + raise ValueError( + "Expected the following order of world arguments:" + f" {', '.join([k.__name__ for (k, *_) in classes.values()])}" + ) + + # FIXME: For now SkyCoord won't auto-convert upon initialization + # https://github.com/astropy/astropy/issues/7689 + from astropy.coordinates import SkyCoord + + if isinstance(w, SkyCoord): + if "frame" in kwargs: + objects[key] = w.transform_to(kwargs["frame"]) + else: + objects[key] = w + else: + objects[key] = klass_gen(w, *args, **kwargs) + + # We now extract the attributes needed for the world values + world = [] + for key, _, attr in components: + if callable(attr): + world.append(attr(objects[key])) + else: + world.append(rec_getattr(objects[key], attr)) + + # Check the type of the return values - should be scalars or plain Numpy + # arrays, not e.g. Quantity. Note that we deliberately use type(w) because + # we don't want to match Numpy subclasses. + for w in world: + if ( + not isinstance(w, numbers.Number) + and not type(w) == np.ndarray + and not type(w) == MaskedNDArray + ): + raise TypeError( + f"WCS world_axis_object_components results in " + f"values which are not scalars or plain Numpy " + f"arrays (got {type(w)})" + ) + + return world + + +def values_to_high_level_objects( + *world_values: ArrayLike, low_level_wcs: _WorldAxisMetadata +) -> list[Any]: + """ + Convert low level values into high level objects. + + This function uses the information in ``low_level_wcs.world_axis_object_classes`` + and ``low_level_wcs.world_axis_object_components`` to convert low level "values" + `~.Quantity` objects, to high level objects (such as `~.SkyCoord`). + + This is used in `.HighLevelWCSMixin.pixel_to_world`, but provided as a + separate function for use in other places where needed. + + Parameters + ---------- + *world_values : `~numpy.typing.ArrayLike` + Low level, "values" representations of the world coordinates. + + low_level_wcs : `.BaseLowLevelWCS` or object + Source of the world axis metadata to use for the conversion. A full + `.BaseLowLevelWCS` instance is accepted, but any object exposing + ``world_axis_object_classes`` and ``world_axis_object_components`` + attributes also works (for example a `types.SimpleNamespace` or a + namedtuple). The ``serialized_classes`` attribute is read if present + and otherwise treated as ``False``. This is useful when the metadata + for the intended conversion direction does not match what a WCS + exposes by default. + """ + # Check the type of the input values - should be scalars or plain Numpy + # arrays, not e.g. Quantity. Note that we deliberately use type(w) because + # we don't want to match Numpy subclasses. + for w in world_values: + if ( + not isinstance(w, numbers.Number) + and not type(w) == np.ndarray + and not type(w) == MaskedNDArray + ): + raise TypeError( + f"Expected world coordinates as scalars or plain Numpy " + f"arrays (got {type(w)})" + ) + + # Cache the classes and components since this may be expensive + components = low_level_wcs.world_axis_object_components + classes = low_level_wcs.world_axis_object_classes + + # Deserialize classes + if getattr(low_level_wcs, "serialized_classes", False): + classes_new = {} + for key, value in classes.items(): + classes_new[key] = deserialize_class(value, construct=False) + classes = classes_new + + args = defaultdict(list) + kwargs = defaultdict(dict) + + for i, (key, attr, _) in enumerate(components): + if isinstance(attr, str): + kwargs[key][attr] = world_values[i] + else: + while attr > len(args[key]) - 1: + args[key].append(None) + args[key][attr] = world_values[i] + + result = [] + + for key in default_order(components): + klass, ar, kw, *rest = classes[key] + if len(rest) == 0: + klass_gen = klass + elif len(rest) == 1: + klass_gen = rest[0] + else: + raise ValueError( + "Tuples in world_axis_object_classes should have length 3 or 4" + ) + result.append(klass_gen(*args[key], *ar, **kwargs[key], **kw)) + + return result + + +class HighLevelWCSMixin(BaseHighLevelWCS): + """ + Mix-in class that automatically provides the high-level WCS API for the + low-level WCS object given by the `~HighLevelWCSMixin.low_level_wcs` + property. + """ + + @property + def low_level_wcs(self): + return self + + def world_to_pixel(self, *world_objects): + values, masks = MaskedNDArray._get_data_and_masks(world_objects) + world_values = high_level_objects_to_values( + *values, low_level_wcs=self.low_level_wcs + ) + + # Finally we convert to pixel coordinates + pixel_values = self.low_level_wcs.world_to_pixel_values(*world_values) + if (mask := combine_masks(masks)) is not False: + pixel_values = tuple(Masked(value, mask) for value in pixel_values) + return pixel_values + + def pixel_to_world(self, *pixel_arrays): + values, masks = MaskedNDArray._get_data_and_masks(pixel_arrays) + # Compute the world coordinate values + world_values = self.low_level_wcs.pixel_to_world_values(*values) + + if self.low_level_wcs.world_n_dim == 1: + world_values = (world_values,) + + if (mask := combine_masks(masks)) is not False: + world_values = tuple(Masked(value, mask) for value in world_values) + + pixel_values = values_to_high_level_objects( + *world_values, low_level_wcs=self.low_level_wcs + ) + + if len(pixel_values) == 1: + return pixel_values[0] + else: + return pixel_values diff --git a/astropy/wcs/wcsapi/high_level_wcs_wrapper.py b/astropy/wcs/wcsapi/high_level_wcs_wrapper.py new file mode 100644 index 000000000000..c4e31507d528 --- /dev/null +++ b/astropy/wcs/wcsapi/high_level_wcs_wrapper.py @@ -0,0 +1,85 @@ +from .high_level_api import HighLevelWCSMixin +from .low_level_api import BaseLowLevelWCS +from .utils import wcs_info_str + +__all__ = ["HighLevelWCSWrapper"] + + +class HighLevelWCSWrapper(HighLevelWCSMixin): + """ + Wrapper class that can take any :class:`~astropy.wcs.wcsapi.BaseLowLevelWCS` + object and expose the high-level WCS API. + """ + + def __init__(self, low_level_wcs): + if not isinstance(low_level_wcs, BaseLowLevelWCS): + raise TypeError( + "Input to a HighLevelWCSWrapper must be a low level WCS object" + ) + + self._low_level_wcs = low_level_wcs + + @property + def low_level_wcs(self): + return self._low_level_wcs + + @property + def pixel_n_dim(self): + """ + See `~astropy.wcs.wcsapi.BaseLowLevelWCS.world_n_dim`. + """ + return self.low_level_wcs.pixel_n_dim + + @property + def world_n_dim(self): + """ + See `~astropy.wcs.wcsapi.BaseLowLevelWCS.world_n_dim`. + """ + return self.low_level_wcs.world_n_dim + + @property + def world_axis_physical_types(self): + """ + See `~astropy.wcs.wcsapi.BaseLowLevelWCS.world_axis_physical_types`. + """ + return self.low_level_wcs.world_axis_physical_types + + @property + def world_axis_units(self): + """ + See `~astropy.wcs.wcsapi.BaseLowLevelWCS.world_axis_units`. + """ + return self.low_level_wcs.world_axis_units + + @property + def array_shape(self): + """ + See `~astropy.wcs.wcsapi.BaseLowLevelWCS.array_shape`. + """ + return self.low_level_wcs.array_shape + + @property + def pixel_bounds(self): + """ + See `~astropy.wcs.wcsapi.BaseLowLevelWCS.pixel_bounds`. + """ + return self.low_level_wcs.pixel_bounds + + @property + def axis_correlation_matrix(self): + """ + See `~astropy.wcs.wcsapi.BaseLowLevelWCS.axis_correlation_matrix`. + """ + return self.low_level_wcs.axis_correlation_matrix + + def _as_mpl_axes(self): + """ + See `~astropy.wcs.wcsapi.BaseLowLevelWCS._as_mpl_axes`. + """ + return self.low_level_wcs._as_mpl_axes() + + def __str__(self): + return wcs_info_str(self.low_level_wcs) + + def __repr__(self): + return f"{object.__repr__(self)}\n{str(self)}" diff --git a/astropy/wcs/wcsapi/low_level_api.py b/astropy/wcs/wcsapi/low_level_api.py new file mode 100644 index 000000000000..47ea5e59bd31 --- /dev/null +++ b/astropy/wcs/wcsapi/low_level_api.py @@ -0,0 +1,370 @@ +import abc +import os + +import numpy as np + +__all__ = ["BaseLowLevelWCS", "validate_physical_types"] + + +class BaseLowLevelWCS(metaclass=abc.ABCMeta): + """ + Abstract base class for the low-level WCS interface. + + This is described in `APE 14: A shared Python interface for World Coordinate + Systems `_. + """ + + @property + @abc.abstractmethod + def pixel_n_dim(self): + """ + The number of axes in the pixel coordinate system. + """ + + @property + @abc.abstractmethod + def world_n_dim(self): + """ + The number of axes in the world coordinate system. + """ + + @property + @abc.abstractmethod + def world_axis_physical_types(self): + """ + An iterable of strings describing the physical type for each world axis. + + These should be names from the VO UCD1+ controlled Vocabulary + (http://www.ivoa.net/documents/latest/UCDlist.html). If no matching UCD + type exists, this can instead be ``"custom:xxx"``, where ``xxx`` is an + arbitrary string. Alternatively, if the physical type is + unknown/undefined, an element can be `None`. + """ + + @property + @abc.abstractmethod + def world_axis_units(self): + """ + An iterable of strings given the units of the world coordinates for each + axis. + + The strings should follow the `IVOA VOUnit standard + `_ (though as noted in the VOUnit + specification document, units that do not follow this standard are still + allowed, but just not recommended). + """ + + @abc.abstractmethod + def pixel_to_world_values(self, *pixel_arrays): + """ + Convert pixel coordinates to world coordinates. + + This method takes `~astropy.wcs.wcsapi.BaseLowLevelWCS.pixel_n_dim` scalars or arrays as + input, and pixel coordinates should be zero-based. Returns + `~astropy.wcs.wcsapi.BaseLowLevelWCS.world_n_dim` scalars or arrays in units given by + `~astropy.wcs.wcsapi.BaseLowLevelWCS.world_axis_units`. Note that pixel coordinates are + assumed to be 0 at the center of the first pixel in each dimension. If a + pixel is in a region where the WCS is not defined, NaN should be returned. + The coordinates should be specified in the ``(x, y)`` order, where for + an image, ``x`` is the horizontal coordinate and ``y`` is the vertical + coordinate. + + If `~astropy.wcs.wcsapi.BaseLowLevelWCS.world_n_dim` is ``1``, this + method returns a single scalar or array, otherwise a tuple of scalars or + arrays is returned. + """ + + def array_index_to_world_values(self, *index_arrays): + """ + Convert array indices to world coordinates. + + This is the same as `~astropy.wcs.wcsapi.BaseLowLevelWCS.pixel_to_world_values` except that + the indices should be given in ``(i, j)`` order, where for an image + ``i`` is the row and ``j`` is the column (i.e. the opposite order to + `~astropy.wcs.wcsapi.BaseLowLevelWCS.pixel_to_world_values`). + + If `~astropy.wcs.wcsapi.BaseLowLevelWCS.world_n_dim` is ``1``, this + method returns a single scalar or array, otherwise a tuple of scalars or + arrays is returned. + """ + return self.pixel_to_world_values(*index_arrays[::-1]) + + @abc.abstractmethod + def world_to_pixel_values(self, *world_arrays): + """ + Convert world coordinates to pixel coordinates. + + This method takes `~astropy.wcs.wcsapi.BaseLowLevelWCS.world_n_dim` scalars or arrays as + input in units given by `~astropy.wcs.wcsapi.BaseLowLevelWCS.world_axis_units`. Returns + `~astropy.wcs.wcsapi.BaseLowLevelWCS.pixel_n_dim` scalars or arrays. Note that pixel + coordinates are assumed to be 0 at the center of the first pixel in each + dimension. If a world coordinate does not have a matching pixel + coordinate, NaN should be returned. The coordinates should be returned in + the ``(x, y)`` order, where for an image, ``x`` is the horizontal + coordinate and ``y`` is the vertical coordinate. + + If `~astropy.wcs.wcsapi.BaseLowLevelWCS.pixel_n_dim` is ``1``, this + method returns a single scalar or array, otherwise a tuple of scalars or + arrays is returned. + """ + + def world_to_array_index_values(self, *world_arrays): + """ + Convert world coordinates to array indices. + + This is the same as `~astropy.wcs.wcsapi.BaseLowLevelWCS.world_to_pixel_values` except that + the indices should be returned in ``(i, j)`` order, where for an image + ``i`` is the row and ``j`` is the column (i.e. the opposite order to + `~astropy.wcs.wcsapi.BaseLowLevelWCS.pixel_to_world_values`). The indices should be + returned as rounded integers. + + If `~astropy.wcs.wcsapi.BaseLowLevelWCS.pixel_n_dim` is ``1``, this + method returns a single scalar or array, otherwise a tuple of scalars or + arrays is returned. + """ + pixel_arrays = self.world_to_pixel_values(*world_arrays) + if self.pixel_n_dim == 1: + pixel_arrays = (pixel_arrays,) + else: + pixel_arrays = pixel_arrays[::-1] + array_indices = tuple( + np.asarray(np.floor(pixel + 0.5), dtype=int) for pixel in pixel_arrays + ) + return array_indices[0] if self.pixel_n_dim == 1 else array_indices + + @property + @abc.abstractmethod + def world_axis_object_components(self): + """ + A list with `~astropy.wcs.wcsapi.BaseLowLevelWCS.world_n_dim` elements giving information + on constructing high-level objects for the world coordinates. + + Each element of the list is a tuple with three items: + + * The first is a name for the world object this world array + corresponds to, which *must* match the string names used in + `~astropy.wcs.wcsapi.BaseLowLevelWCS.world_axis_object_classes`. Note that names might + appear twice because two world arrays might correspond to a single + world object (e.g. a celestial coordinate might have both “ra” and + “dec” arrays, which correspond to a single sky coordinate object). + + * The second element is either a string keyword argument name or a + positional index for the corresponding class from + `~astropy.wcs.wcsapi.BaseLowLevelWCS.world_axis_object_classes`. + + * The third argument is a string giving the name of the property + to access on the corresponding class from + `~astropy.wcs.wcsapi.BaseLowLevelWCS.world_axis_object_classes` in + order to get numerical values. Alternatively, this argument can be a + callable Python object that takes a high-level coordinate object and + returns the numerical values suitable for passing to the low-level + WCS transformation methods. + + See the document + `APE 14: A shared Python interface for World Coordinate Systems + `_ for examples. + """ + + @property + @abc.abstractmethod + def world_axis_object_classes(self): + """ + A dictionary giving information on constructing high-level objects for + the world coordinates. + + Each key of the dictionary is a string key from + `~astropy.wcs.wcsapi.BaseLowLevelWCS.world_axis_object_components`, and each value is a + tuple with three elements or four elements: + + * The first element of the tuple must be a class or a string specifying + the fully-qualified name of a class, which will specify the actual + Python object to be created. + + * The second element, should be a tuple specifying the positional + arguments required to initialize the class. If + `~astropy.wcs.wcsapi.BaseLowLevelWCS.world_axis_object_components` specifies that the + world coordinates should be passed as a positional argument, this this + tuple should include `None` placeholders for the world coordinates. + + * The third tuple element must be a dictionary with the keyword + arguments required to initialize the class. + + * Optionally, for advanced use cases, the fourth element (if present) + should be a callable Python object that gets called instead of the + class and gets passed the positional and keyword arguments. It should + return an object of the type of the first element in the tuple. + + Note that we don't require the classes to be Astropy classes since there + is no guarantee that Astropy will have all the classes to represent all + kinds of world coordinates. Furthermore, we recommend that the output be + kept as human-readable as possible. + + The classes used here should have the ability to do conversions by + passing an instance as the first argument to the same class with + different arguments (e.g. ``Time(Time(...), scale='tai')``). This is + a requirement for the implementation of the high-level interface. + + The second and third tuple elements for each value of this dictionary + can in turn contain either instances of classes, or if necessary can + contain serialized versions that should take the same form as the main + classes described above (a tuple with three elements with the fully + qualified name of the class, then the positional arguments and the + keyword arguments). For low-level API objects implemented in Python, we + recommend simply returning the actual objects (not the serialized form) + for optimal performance. Implementations should either always or never + use serialized classes to represent Python objects, and should indicate + which of these they follow using the + `~astropy.wcs.wcsapi.BaseLowLevelWCS.serialized_classes` attribute. + + See the document + `APE 14: A shared Python interface for World Coordinate Systems + `_ for examples. + """ + + # The following three properties have default fallback implementations, so + # they are not abstract. + + @property + def array_shape(self): + """ + The shape of the data that the WCS applies to as a tuple of length + `~astropy.wcs.wcsapi.BaseLowLevelWCS.pixel_n_dim` in ``(row, column)`` + order (the convention for arrays in Python). + + If the WCS is valid in the context of a dataset with a particular + shape, then this property can be used to store the shape of the + data. This can be used for example if implementing slicing of WCS + objects. This is an optional property, and it should return `None` + if a shape is not known or relevant. + """ + if self.pixel_shape is None: + return None + else: + return self.pixel_shape[::-1] + + @property + def pixel_shape(self): + """ + The shape of the data that the WCS applies to as a tuple of length + `~astropy.wcs.wcsapi.BaseLowLevelWCS.pixel_n_dim` in ``(x, y)`` + order (where for an image, ``x`` is the horizontal coordinate and ``y`` + is the vertical coordinate). + + If the WCS is valid in the context of a dataset with a particular + shape, then this property can be used to store the shape of the + data. This can be used for example if implementing slicing of WCS + objects. This is an optional property, and it should return `None` + if a shape is not known or relevant. + + If you are interested in getting a shape that is comparable to that of + a Numpy array, you should use + `~astropy.wcs.wcsapi.BaseLowLevelWCS.array_shape` instead. + """ + return None + + @property + def pixel_bounds(self): + """ + The bounds (in pixel coordinates) inside which the WCS is defined, + as a list with `~astropy.wcs.wcsapi.BaseLowLevelWCS.pixel_n_dim` + ``(min, max)`` tuples. + + The bounds should be given in ``[(xmin, xmax), (ymin, ymax)]`` + order. WCS solutions are sometimes only guaranteed to be accurate + within a certain range of pixel values, for example when defining a + WCS that includes fitted distortions. This is an optional property, + and it should return `None` if a shape is not known or relevant. + + The bounds can be a mix of values along dimensions where bounds exist, + and None for other dimensions, e.g. ``[(xmin, xmax), None]``. + """ + return None + + @property + def pixel_axis_names(self): + """ + An iterable of strings describing the name for each pixel axis. + + If an axis does not have a name, an empty string should be returned + (this is the default behavior for all axes if a subclass does not + override this property). Note that these names are just for display + purposes and are not standardized. + """ + return [""] * self.pixel_n_dim + + @property + def world_axis_names(self): + """ + An iterable of strings describing the name for each world axis. + + If an axis does not have a name, an empty string should be returned + (this is the default behavior for all axes if a subclass does not + override this property). Note that these names are just for display + purposes and are not standardized. For standardized axis types, see + `~astropy.wcs.wcsapi.BaseLowLevelWCS.world_axis_physical_types`. + """ + return [""] * self.world_n_dim + + @property + def axis_correlation_matrix(self): + """ + Returns an (`~astropy.wcs.wcsapi.BaseLowLevelWCS.world_n_dim`, + `~astropy.wcs.wcsapi.BaseLowLevelWCS.pixel_n_dim`) matrix that + indicates using booleans whether a given world coordinate depends on a + given pixel coordinate. + + This defaults to a matrix where all elements are `True` in the absence + of any further information. For completely independent axes, the + diagonal would be `True` and all other entries `False`. + """ + return np.ones((self.world_n_dim, self.pixel_n_dim), dtype=bool) + + @property + def serialized_classes(self): + """ + Indicates whether Python objects are given in serialized form or as + actual Python objects. + """ + return False + + def _as_mpl_axes(self): + """Compatibility hook for Matplotlib and WCSAxes. + + With this method, one can do:: + + from astropy.wcs import WCS + import matplotlib.pyplot as plt + wcs = WCS('filename.fits') + fig = plt.figure() + ax = fig.add_axes([0.15, 0.1, 0.8, 0.8], projection=wcs) + ... + + and this will generate a plot with the correct WCS coordinates on the + axes. + """ + from astropy.visualization.wcsaxes import WCSAxes + + return WCSAxes, {"wcs": self} + + +UCDS_FILE = os.path.join(os.path.dirname(__file__), "data", "ucds.txt") +with open(UCDS_FILE) as f: + VALID_UCDS = {x.strip() for x in f.read().splitlines()[1:]} + + +def validate_physical_types(physical_types): + """ + Validate a list of physical types against the UCD1+ standard. + """ + for physical_type in physical_types: + if ( + physical_type is not None + and physical_type not in VALID_UCDS + and not physical_type.startswith("custom:") + ): + raise ValueError( + f"'{physical_type}' is not a valid IOVA UCD1+ physical type. It must be" + " a string specified in the list" + " (http://www.ivoa.net/documents/latest/UCDlist.html) or if no" + " matching type exists it can be any string prepended with 'custom:'." + ) diff --git a/astropy/wcs/wcsapi/tests/__init__.py b/astropy/wcs/wcsapi/tests/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/astropy/wcs/wcsapi/tests/data/example_4d_tab.fits b/astropy/wcs/wcsapi/tests/data/example_4d_tab.fits new file mode 100644 index 000000000000..4776304227d2 --- /dev/null +++ b/astropy/wcs/wcsapi/tests/data/example_4d_tab.fits @@ -0,0 +1 @@ +SIMPLE = T / conforms to FITS standard BITPIX = 8 / array data type NAXIS = 0 / number of array dimensions EXTEND = T WCSAXES = 4 WCSNAME = 'CompositeFrame' CTYPE1 = 'GLON-TAB' CUNIT1 = 'arcsec ' PS1_0 = 'WCS-TABLE' PV1_1 = 1 PS1_1 = 'coordinates' PV1_3 = 1 CRVAL1 = 1 CRPIX1 = 1.0 PC1_1 = 1.0 CDELT1 = 1.0 CTYPE2 = 'GLAT-TAB' CUNIT2 = 'arcsec ' PS2_0 = 'WCS-TABLE' PV2_1 = 1 PS2_1 = 'coordinates' PV2_3 = 2 CRVAL2 = 1 CRPIX2 = 1.0 PC2_2 = 1.0 CDELT2 = 1.0 CTYPE3 = 'FREQ-TAB' CUNIT3 = 'Hz ' PS3_0 = 'WCS-TABLE' PV3_1 = 1 PS3_1 = 'coordinates' PV3_3 = 3 CRVAL3 = 1 CRPIX3 = 1.0 PC3_3 = 1.0 CDELT3 = 1.0 CTYPE4 = 'TIME-TAB' CUNIT4 = '' PS4_0 = 'WCS-TABLE' PV4_1 = 1 PS4_1 = 'coordinates' PV4_3 = 4 CRVAL4 = 1 CRPIX4 = 1.0 PC4_4 = 1.0 CDELT4 = 1.0 TIMESYS = 'UTC ' END XTENSION= 'BINTABLE' / binary table extension BITPIX = 8 / array data type NAXIS = 2 / number of array dimensions NAXIS1 = 3840 / length of dimension 1 NAXIS2 = 1 / length of dimension 2 PCOUNT = 0 / number of group parameters GCOUNT = 1 / number of groups TFIELDS = 1 / number of table fields TTYPE1 = 'coordinates' TFORM1 = '480D ' TDIM1 = '(4,5,4,3,2)' EXTNAME = 'WCS-TABLE' / extension name EXTVER = 1 / extension value END @m#ČŽĶ ,ĀUāū¸]@8õ\(ö@<€@jĩ؉¸=ąĀV  \šŽ@8õ\(ö@<€@f˙˙˙˙ûĀV @8õ\(ö@<€@bJ'vGÂHĀV  \šŽ@8õ\(ö@<€@_¸nâYåĄĀUāū¸]@8õ\(ö@<€@nmąæÔ]ėĀUđå°"@8õ\(ö@<€@lÜ@ûÍ+ĀV%~úŪ†@8õ\(ö@<€@f˙˙˙˙ųĀV@@8õ\(ö@<€@`ā#ŋ2ÎĀV%~úŪ…@8õ\(ö@<€@]$œ2WD!ĀUđå°"@8õ\(ö@<€@oūŊÛÔžĀUüėÔŊ@8õ\(ö@<€@nmÜÍqmhĀV8rQ/ô@8õ\(ö@<€@f˙˙˙˙ōĀV`@8õ\(ö@<€@]$Fe%$ĀV8rQ/ķ@8õ\(ö@<€@Z„HWĀÁĀUüėÔŧ@8õ\(ö@<€@pāĀV@8õ\(ö@<€@pāĀV@@8õ\(ö@<€@F€ĀV€@8õ\(ö@<€@V˙˙˙˙ōĀV@@8õ\(ö@<€@V˙˙˙˙ųĀV@8õ\(ö@<€@m#ČŽĶ ,ĀUāū¸]@8øQë…¸@<€@jĩ؉¸=ąĀV  \šŽ@8øQë…¸@<€@f˙˙˙˙ûĀV @8øQë…¸@<€@bJ'vGÂHĀV  \šŽ@8øQë…¸@<€@_¸nâYåĄĀUāū¸]@8øQë…¸@<€@nmąæÔ]ėĀUđå°"@8øQë…¸@<€@lÜ@ûÍ+ĀV%~úŪ†@8øQë…¸@<€@f˙˙˙˙ųĀV@@8øQë…¸@<€@`ā#ŋ2ÎĀV%~úŪ…@8øQë…¸@<€@]$œ2WD!ĀUđå°"@8øQë…¸@<€@oūŊÛÔžĀUüėÔŊ@8øQë…¸@<€@nmÜÍqmhĀV8rQ/ô@8øQë…¸@<€@f˙˙˙˙ōĀV`@8øQë…¸@<€@]$Fe%$ĀV8rQ/ķ@8øQë…¸@<€@Z„HWĀÁĀUüėÔŧ@8øQë…¸@<€@pāĀV@8øQë…¸@<€@pāĀV@@8øQë…¸@<€@F€ĀV€@8øQë…¸@<€@V˙˙˙˙ōĀV@@8øQë…¸@<€@V˙˙˙˙ųĀV@8øQë…¸@<€@m#ČŽĶ ,ĀUāū¸]@8úáGŽ{@<€@jĩ؉¸=ąĀV  \šŽ@8úáGŽ{@<€@f˙˙˙˙ûĀV @8úáGŽ{@<€@bJ'vGÂHĀV  \šŽ@8úáGŽ{@<€@_¸nâYåĄĀUāū¸]@8úáGŽ{@<€@nmąæÔ]ėĀUđå°"@8úáGŽ{@<€@lÜ@ûÍ+ĀV%~úŪ†@8úáGŽ{@<€@f˙˙˙˙ųĀV@@8úáGŽ{@<€@`ā#ŋ2ÎĀV%~úŪ…@8úáGŽ{@<€@]$œ2WD!ĀUđå°"@8úáGŽ{@<€@oūŊÛÔžĀUüėÔŊ@8úáGŽ{@<€@nmÜÍqmhĀV8rQ/ô@8úáGŽ{@<€@f˙˙˙˙ōĀV`@8úáGŽ{@<€@]$Fe%$ĀV8rQ/ķ@8úáGŽ{@<€@Z„HWĀÁĀUüėÔŧ@8úáGŽ{@<€@pāĀV@8úáGŽ{@<€@pāĀV@@8úáGŽ{@<€@F€ĀV€@8úáGŽ{@<€@V˙˙˙˙ōĀV@@8úáGŽ{@<€@V˙˙˙˙ųĀV@8úáGŽ{@<€@m#ČŽĶ ,ĀUāū¸]@8õ\(ö@<ĖĖĖĖĖÍ@jĩ؉¸=ąĀV  \šŽ@8õ\(ö@<ĖĖĖĖĖÍ@f˙˙˙˙ûĀV @8õ\(ö@<ĖĖĖĖĖÍ@bJ'vGÂHĀV  \šŽ@8õ\(ö@<ĖĖĖĖĖÍ@_¸nâYåĄĀUāū¸]@8õ\(ö@<ĖĖĖĖĖÍ@nmąæÔ]ėĀUđå°"@8õ\(ö@<ĖĖĖĖĖÍ@lÜ@ûÍ+ĀV%~úŪ†@8õ\(ö@<ĖĖĖĖĖÍ@f˙˙˙˙ųĀV@@8õ\(ö@<ĖĖĖĖĖÍ@`ā#ŋ2ÎĀV%~úŪ…@8õ\(ö@<ĖĖĖĖĖÍ@]$œ2WD!ĀUđå°"@8õ\(ö@<ĖĖĖĖĖÍ@oūŊÛÔžĀUüėÔŊ@8õ\(ö@<ĖĖĖĖĖÍ@nmÜÍqmhĀV8rQ/ô@8õ\(ö@<ĖĖĖĖĖÍ@f˙˙˙˙ōĀV`@8õ\(ö@<ĖĖĖĖĖÍ@]$Fe%$ĀV8rQ/ķ@8õ\(ö@<ĖĖĖĖĖÍ@Z„HWĀÁĀUüėÔŧ@8õ\(ö@<ĖĖĖĖĖÍ@pāĀV@8õ\(ö@<ĖĖĖĖĖÍ@pāĀV@@8õ\(ö@<ĖĖĖĖĖÍ@F€ĀV€@8õ\(ö@<ĖĖĖĖĖÍ@V˙˙˙˙ōĀV@@8õ\(ö@<ĖĖĖĖĖÍ@V˙˙˙˙ųĀV@8õ\(ö@<ĖĖĖĖĖÍ@m#ČŽĶ ,ĀUāū¸]@8øQë…¸@<ĖĖĖĖĖÍ@jĩ؉¸=ąĀV  \šŽ@8øQë…¸@<ĖĖĖĖĖÍ@f˙˙˙˙ûĀV @8øQë…¸@<ĖĖĖĖĖÍ@bJ'vGÂHĀV  \šŽ@8øQë…¸@<ĖĖĖĖĖÍ@_¸nâYåĄĀUāū¸]@8øQë…¸@<ĖĖĖĖĖÍ@nmąæÔ]ėĀUđå°"@8øQë…¸@<ĖĖĖĖĖÍ@lÜ@ûÍ+ĀV%~úŪ†@8øQë…¸@<ĖĖĖĖĖÍ@f˙˙˙˙ųĀV@@8øQë…¸@<ĖĖĖĖĖÍ@`ā#ŋ2ÎĀV%~úŪ…@8øQë…¸@<ĖĖĖĖĖÍ@]$œ2WD!ĀUđå°"@8øQë…¸@<ĖĖĖĖĖÍ@oūŊÛÔžĀUüėÔŊ@8øQë…¸@<ĖĖĖĖĖÍ@nmÜÍqmhĀV8rQ/ô@8øQë…¸@<ĖĖĖĖĖÍ@f˙˙˙˙ōĀV`@8øQë…¸@<ĖĖĖĖĖÍ@]$Fe%$ĀV8rQ/ķ@8øQë…¸@<ĖĖĖĖĖÍ@Z„HWĀÁĀUüėÔŧ@8øQë…¸@<ĖĖĖĖĖÍ@pāĀV@8øQë…¸@<ĖĖĖĖĖÍ@pāĀV@@8øQë…¸@<ĖĖĖĖĖÍ@F€ĀV€@8øQë…¸@<ĖĖĖĖĖÍ@V˙˙˙˙ōĀV@@8øQë…¸@<ĖĖĖĖĖÍ@V˙˙˙˙ųĀV@8øQë…¸@<ĖĖĖĖĖÍ@m#ČŽĶ ,ĀUāū¸]@8úáGŽ{@<ĖĖĖĖĖÍ@jĩ؉¸=ąĀV  \šŽ@8úáGŽ{@<ĖĖĖĖĖÍ@f˙˙˙˙ûĀV @8úáGŽ{@<ĖĖĖĖĖÍ@bJ'vGÂHĀV  \šŽ@8úáGŽ{@<ĖĖĖĖĖÍ@_¸nâYåĄĀUāū¸]@8úáGŽ{@<ĖĖĖĖĖÍ@nmąæÔ]ėĀUđå°"@8úáGŽ{@<ĖĖĖĖĖÍ@lÜ@ûÍ+ĀV%~úŪ†@8úáGŽ{@<ĖĖĖĖĖÍ@f˙˙˙˙ųĀV@@8úáGŽ{@<ĖĖĖĖĖÍ@`ā#ŋ2ÎĀV%~úŪ…@8úáGŽ{@<ĖĖĖĖĖÍ@]$œ2WD!ĀUđå°"@8úáGŽ{@<ĖĖĖĖĖÍ@oūŊÛÔžĀUüėÔŊ@8úáGŽ{@<ĖĖĖĖĖÍ@nmÜÍqmhĀV8rQ/ô@8úáGŽ{@<ĖĖĖĖĖÍ@f˙˙˙˙ōĀV`@8úáGŽ{@<ĖĖĖĖĖÍ@]$Fe%$ĀV8rQ/ķ@8úáGŽ{@<ĖĖĖĖĖÍ@Z„HWĀÁĀUüėÔŧ@8úáGŽ{@<ĖĖĖĖĖÍ@pāĀV@8úáGŽ{@<ĖĖĖĖĖÍ@pāĀV@@8úáGŽ{@<ĖĖĖĖĖÍ@F€ĀV€@8úáGŽ{@<ĖĖĖĖĖÍ@V˙˙˙˙ōĀV@@8úáGŽ{@<ĖĖĖĖĖÍ@V˙˙˙˙ųĀV@8úáGŽ{@<ĖĖĖĖĖÍ \ No newline at end of file diff --git a/astropy/wcs/wcsapi/tests/helpers.py b/astropy/wcs/wcsapi/tests/helpers.py new file mode 100644 index 000000000000..6a8acdb143b8 --- /dev/null +++ b/astropy/wcs/wcsapi/tests/helpers.py @@ -0,0 +1,3 @@ +def assert_celestial_component(component, index): + assert component[:2] == ("celestial", index) + assert callable(component[2]) diff --git a/astropy/wcs/wcsapi/tests/test_fitswcs.py b/astropy/wcs/wcsapi/tests/test_fitswcs.py new file mode 100644 index 000000000000..4ab31012ca33 --- /dev/null +++ b/astropy/wcs/wcsapi/tests/test_fitswcs.py @@ -0,0 +1,1693 @@ +# Note that we test the main astropy.wcs.WCS class directly rather than testing +# the mix-in class on its own (since it's not functional without being used as +# a mix-in) + +import re +import warnings + +import numpy as np +import pytest +from numpy.testing import assert_allclose, assert_array_equal, assert_equal +from packaging.version import Version + +from astropy import units as u +from astropy.coordinates import ( + FK5, + ICRS, + ITRS, + EarthLocation, + Galactic, + SkyCoord, + SpectralCoord, + StokesCoord, +) +from astropy.io import fits +from astropy.io.fits import Header +from astropy.io.fits.verify import VerifyWarning +from astropy.tests.helper import assert_quantity_allclose +from astropy.time import Time +from astropy.units import Quantity, UnitsWarning +from astropy.utils import iers +from astropy.utils.data import get_pkg_data_filename +from astropy.utils.exceptions import AstropyDeprecationWarning, AstropyUserWarning +from astropy.utils.masked import Masked +from astropy.wcs.wcs import WCS, WCSLIB_VERSION, FITSFixedWarning, NoConvergence, Sip +from astropy.wcs.wcsapi.fitswcs import VELOCITY_FRAMES, custom_ctype_to_ucd_mapping +from astropy.wcs.wcsapi.tests.helpers import assert_celestial_component + +############################################################################### +# The following example is the simplest WCS with default values +############################################################################### + + +WCS_EMPTY = WCS(naxis=1) +WCS_EMPTY.wcs.crpix = [1] + + +def test_empty(): + wcs = WCS_EMPTY + + # Low-level API + + assert wcs.pixel_n_dim == 1 + assert wcs.world_n_dim == 1 + assert wcs.array_shape is None + assert wcs.pixel_shape is None + assert wcs.world_axis_physical_types == [None] + assert wcs.world_axis_units == [""] + assert wcs.pixel_axis_names == [""] + assert wcs.world_axis_names == [""] + + assert_equal(wcs.axis_correlation_matrix, True) + + assert wcs.world_axis_object_components == [("world", 0, "value")] + + assert wcs.world_axis_object_classes["world"][0] is Quantity + assert wcs.world_axis_object_classes["world"][1] == () + assert wcs.world_axis_object_classes["world"][2]["unit"] is u.one + + assert_allclose(wcs.pixel_to_world_values(29), 29) + assert_allclose(wcs.array_index_to_world_values(29), 29) + + assert np.ndim(wcs.pixel_to_world_values(29)) == 0 + assert np.ndim(wcs.array_index_to_world_values(29)) == 0 + + assert_allclose(wcs.world_to_pixel_values(29), 29) + assert_equal(wcs.world_to_array_index_values(29), (29,)) + + assert np.ndim(wcs.world_to_pixel_values(29)) == 0 + assert np.ndim(wcs.world_to_array_index_values(29)) == 0 + + # High-level API + + coord = wcs.pixel_to_world(29) + assert_quantity_allclose(coord, 29 * u.one) + assert np.ndim(coord) == 0 + + coord = wcs.array_index_to_world(29) + assert_quantity_allclose(coord, 29 * u.one) + assert np.ndim(coord) == 0 + + coord = 15 * u.one + + x = wcs.world_to_pixel(coord) + assert_allclose(x, 15.0) + assert np.ndim(x) == 0 + + i = wcs.world_to_array_index(coord) + assert_equal(i, 15) + assert np.ndim(i) == 0 + + +############################################################################### +# The following example is a simple 2D image with celestial coordinates +############################################################################### + +HEADER_SIMPLE_CELESTIAL = """ +WCSAXES = 2 +CTYPE1 = RA---TAN +CTYPE2 = DEC--TAN +CRVAL1 = 10 +CRVAL2 = 20 +CRPIX1 = 30 +CRPIX2 = 40 +CDELT1 = -0.1 +CDELT2 = 0.1 +CROTA2 = 0. +CUNIT1 = deg +CUNIT2 = deg +""" + +with warnings.catch_warnings(): + warnings.simplefilter("ignore", VerifyWarning) + WCS_SIMPLE_CELESTIAL = WCS(Header.fromstring(HEADER_SIMPLE_CELESTIAL, sep="\n")) + + +def test_simple_celestial(): + wcs = WCS_SIMPLE_CELESTIAL + + # Low-level API + + assert wcs.pixel_n_dim == 2 + assert wcs.world_n_dim == 2 + assert wcs.array_shape is None + assert wcs.pixel_shape is None + assert wcs.world_axis_physical_types == ["pos.eq.ra", "pos.eq.dec"] + assert wcs.world_axis_units == ["deg", "deg"] + assert wcs.pixel_axis_names == ["", ""] + assert wcs.world_axis_names == ["", ""] + + assert_equal(wcs.axis_correlation_matrix, True) + + components = wcs.world_axis_object_components + assert len(components) == 2 + assert_celestial_component(components[0], 0) + assert_celestial_component(components[1], 1) + + assert wcs.world_axis_object_classes["celestial"][0] is SkyCoord + assert wcs.world_axis_object_classes["celestial"][1] == () + assert isinstance(wcs.world_axis_object_classes["celestial"][2]["frame"], ICRS) + assert wcs.world_axis_object_classes["celestial"][2]["unit"] == (u.deg, u.deg) + + assert_allclose(wcs.pixel_to_world_values(29, 39), (10, 20)) + assert_allclose(wcs.array_index_to_world_values(39, 29), (10, 20)) + + assert_allclose(wcs.world_to_pixel_values(10, 20), (29.0, 39.0)) + assert_equal(wcs.world_to_array_index_values(10, 20), (39, 29)) + + # High-level API + + coord = wcs.pixel_to_world(29, 39) + assert isinstance(coord, SkyCoord) + assert isinstance(coord.frame, ICRS) + assert_allclose(coord.ra.deg, 10) + assert_allclose(coord.dec.deg, 20) + + coord = wcs.array_index_to_world(39, 29) + assert isinstance(coord, SkyCoord) + assert isinstance(coord.frame, ICRS) + assert_allclose(coord.ra.deg, 10) + assert_allclose(coord.dec.deg, 20) + + coord = SkyCoord(10, 20, unit="deg", frame="icrs") + + x, y = wcs.world_to_pixel(coord) + assert_allclose(x, 29.0) + assert_allclose(y, 39.0) + + i, j = wcs.world_to_array_index(coord) + assert_equal(i, 39) + assert_equal(j, 29) + + # Check that if the coordinates are passed in a different frame things still + # work properly + + coord_galactic = coord.galactic + + x, y = wcs.world_to_pixel(coord_galactic) + assert_allclose(x, 29.0) + assert_allclose(y, 39.0) + + i, j = wcs.world_to_array_index(coord_galactic) + assert_equal(i, 39) + assert_equal(j, 29) + + # Check that we can actually index the array + + data = np.arange(3600).reshape((60, 60)) + + coord = SkyCoord(10, 20, unit="deg", frame="icrs") + index = wcs.world_to_array_index(coord) + assert_equal(data[index], 2369) + + coord = SkyCoord([10, 12], [20, 22], unit="deg", frame="icrs") + index = wcs.world_to_array_index(coord) + assert_equal(data[index], [2369, 3550]) + + +############################################################################### +# The following example is a spectral cube with axes in an unusual order +############################################################################### + +HEADER_SPECTRAL_CUBE = """ +WCSAXES = 3 +CTYPE1 = GLAT-CAR +CTYPE2 = FREQ +CTYPE3 = GLON-CAR +CNAME1 = Latitude +CNAME2 = Frequency +CNAME3 = Longitude +CRVAL1 = 10 +CRVAL2 = 20 +CRVAL3 = 25 +CRPIX1 = 30 +CRPIX2 = 40 +CRPIX3 = 45 +CDELT1 = -0.1 +CDELT2 = 0.5 +CDELT3 = 0.1 +CUNIT1 = deg +CUNIT2 = Hz +CUNIT3 = deg +""" + +with warnings.catch_warnings(): + warnings.simplefilter("ignore", VerifyWarning) + WCS_SPECTRAL_CUBE = WCS(Header.fromstring(HEADER_SPECTRAL_CUBE, sep="\n")) + + +def test_spectral_cube(): + # Spectral cube with a weird axis ordering + + wcs = WCS_SPECTRAL_CUBE + + # Low-level API + + assert wcs.pixel_n_dim == 3 + assert wcs.world_n_dim == 3 + assert wcs.array_shape is None + assert wcs.pixel_shape is None + assert wcs.world_axis_physical_types == [ + "pos.galactic.lat", + "em.freq", + "pos.galactic.lon", + ] + assert wcs.world_axis_units == ["deg", "Hz", "deg"] + assert wcs.pixel_axis_names == ["", "", ""] + assert wcs.world_axis_names == ["Latitude", "Frequency", "Longitude"] + + assert_equal( + wcs.axis_correlation_matrix, + [[True, False, True], [False, True, False], [True, False, True]], + ) + + components = wcs.world_axis_object_components + assert len(components) == 3 + assert_celestial_component(components[0], 1) + assert components[1][:2] == ("spectral", 0) + assert_celestial_component(components[2], 0) + + assert wcs.world_axis_object_classes["celestial"][0] is SkyCoord + assert wcs.world_axis_object_classes["celestial"][1] == () + assert isinstance(wcs.world_axis_object_classes["celestial"][2]["frame"], Galactic) + assert wcs.world_axis_object_classes["celestial"][2]["unit"] == (u.deg, u.deg) + + assert wcs.world_axis_object_classes["spectral"][0] is Quantity + assert wcs.world_axis_object_classes["spectral"][1] == () + assert wcs.world_axis_object_classes["spectral"][2] == {} + + assert_allclose(wcs.pixel_to_world_values(29, 39, 44), (10, 20, 25)) + assert_allclose(wcs.array_index_to_world_values(44, 39, 29), (10, 20, 25)) + + assert_allclose(wcs.world_to_pixel_values(10, 20, 25), (29.0, 39.0, 44.0)) + assert_equal(wcs.world_to_array_index_values(10, 20, 25), (44, 39, 29)) + + # High-level API + + coord, spec = wcs.pixel_to_world(29, 39, 44) + assert isinstance(coord, SkyCoord) + assert isinstance(coord.frame, Galactic) + assert_allclose(coord.l.deg, 25) + assert_allclose(coord.b.deg, 10) + assert isinstance(spec, SpectralCoord) + assert_allclose(spec.to_value(u.Hz), 20) + + coord, spec = wcs.array_index_to_world(44, 39, 29) + assert isinstance(coord, SkyCoord) + assert isinstance(coord.frame, Galactic) + assert_allclose(coord.l.deg, 25) + assert_allclose(coord.b.deg, 10) + assert isinstance(spec, SpectralCoord) + assert_allclose(spec.to_value(u.Hz), 20) + + coord = SkyCoord(25, 10, unit="deg", frame="galactic") + spec = 20 * u.Hz + + x, y, z = wcs.world_to_pixel(coord, spec) + assert_allclose(x, 29.0) + assert_allclose(y, 39.0) + assert_allclose(z, 44.0) + + # Order of world coordinates shouldn't matter + x, y, z = wcs.world_to_pixel(spec, coord) + assert_allclose(x, 29.0) + assert_allclose(y, 39.0) + assert_allclose(z, 44.0) + + i, j, k = wcs.world_to_array_index(coord, spec) + assert_equal(i, 44) + assert_equal(j, 39) + assert_equal(k, 29) + + # Order of world coordinates shouldn't matter + i, j, k = wcs.world_to_array_index(spec, coord) + assert_equal(i, 44) + assert_equal(j, 39) + assert_equal(k, 29) + + +HEADER_SPECTRAL_CUBE_NONALIGNED = ( + HEADER_SPECTRAL_CUBE.strip() + + "\n" + + """ +PC2_3 = -0.5 +PC3_2 = +0.5 +""" +) + +with warnings.catch_warnings(): + warnings.simplefilter("ignore", VerifyWarning) + WCS_SPECTRAL_CUBE_NONALIGNED = WCS( + Header.fromstring(HEADER_SPECTRAL_CUBE_NONALIGNED, sep="\n") + ) + + +def test_spectral_cube_nonaligned(): + # Make sure that correlation matrix gets adjusted if there are non-identity + # CD matrix terms. + + wcs = WCS_SPECTRAL_CUBE_NONALIGNED + + assert wcs.world_axis_physical_types == [ + "pos.galactic.lat", + "em.freq", + "pos.galactic.lon", + ] + assert wcs.world_axis_units == ["deg", "Hz", "deg"] + assert wcs.pixel_axis_names == ["", "", ""] + assert wcs.world_axis_names == ["Latitude", "Frequency", "Longitude"] + + assert_equal( + wcs.axis_correlation_matrix, + [ + [True, True, True], + [False, True, True], + [True, True, True], + ], + ) + + # NOTE: we check world_axis_object_components and world_axis_object_classes + # again here because in the past this failed when non-aligned axes were + # present, so this serves as a regression test. + + components = wcs.world_axis_object_components + assert len(components) == 3 + assert_celestial_component(components[0], 1) + assert components[1][:2] == ("spectral", 0) + assert_celestial_component(components[2], 0) + + assert wcs.world_axis_object_classes["celestial"][0] is SkyCoord + assert wcs.world_axis_object_classes["celestial"][1] == () + assert isinstance(wcs.world_axis_object_classes["celestial"][2]["frame"], Galactic) + assert wcs.world_axis_object_classes["celestial"][2]["unit"] == (u.deg, u.deg) + + assert wcs.world_axis_object_classes["spectral"][0] is Quantity + assert wcs.world_axis_object_classes["spectral"][1] == () + assert wcs.world_axis_object_classes["spectral"][2] == {} + + +############################################################################### +# The following example is from Rots et al (2015), Table 5. It represents a +# cube with two spatial dimensions and one time dimension +############################################################################### + + +HEADER_TIME_CUBE = """ +SIMPLE = T / Fits standard +BITPIX = -32 / Bits per pixel +NAXIS = 3 / Number of axes +NAXIS1 = 2048 / Axis length +NAXIS2 = 2048 / Axis length +NAXIS3 = 11 / Axis length +DATE = '2008-10-28T14:39:06' / Date FITS file was generated +OBJECT = '2008 TC3' / Name of the object observed +EXPTIME = 1.0011 / Integration time +MJD-OBS = 54746.02749237 / Obs start +DATE-OBS= '2008-10-07T00:39:35.3342' / Observing date +TELESCOP= 'VISTA' / ESO Telescope Name +INSTRUME= 'VIRCAM' / Instrument used. +TIMESYS = 'UTC' / From Observatory Time System +TREFPOS = 'TOPOCENT' / Topocentric +MJDREF = 54746.0 / Time reference point in MJD +RADESYS = 'ICRS' / Not equinoctal +CTYPE2 = 'RA---ZPN' / Zenithal Polynomial Projection +CRVAL2 = 2.01824372640628 / RA at ref pixel +CUNIT2 = 'deg' / Angles are degrees always +CRPIX2 = 2956.6 / Pixel coordinate at ref point +CTYPE1 = 'DEC--ZPN' / Zenithal Polynomial Projection +CRVAL1 = 14.8289418840003 / Dec at ref pixel +CUNIT1 = 'deg' / Angles are degrees always +CRPIX1 = -448.2 / Pixel coordinate at ref point +CTYPE3 = 'UTC' / linear time (UTC) +CRVAL3 = 2375.341 / Relative time of first frame +CUNIT3 = 's' / Time unit +CRPIX3 = 1.0 / Pixel coordinate at ref point +CTYPE3A = 'TT' / alternative linear time (TT) +CRVAL3A = 2440.525 / Relative time of first frame +CUNIT3A = 's' / Time unit +CRPIX3A = 1.0 / Pixel coordinate at ref point +OBSGEO-B= -24.6157 / [deg] Tel geodetic latitude (=North)+ +OBSGEO-L= -70.3976 / [deg] Tel geodetic longitude (=East)+ +OBSGEO-H= 2530.0000 / [m] Tel height above reference ellipsoid +CRDER3 = 0.0819 / random error in timings from fit +CSYER3 = 0.0100 / absolute time error +PC1_1 = 0.999999971570892 / WCS transform matrix element +PC1_2 = 0.000238449608932 / WCS transform matrix element +PC2_1 = -0.000621542859395 / WCS transform matrix element +PC2_2 = 0.999999806842218 / WCS transform matrix element +CDELT1 = -9.48575432499806E-5 / Axis scale at reference point +CDELT2 = 9.48683176211164E-5 / Axis scale at reference point +CDELT3 = 13.3629 / Axis scale at reference point +PV1_1 = 1. / ZPN linear term +PV1_3 = 42. / ZPN cubic term +""" + +with warnings.catch_warnings(): + warnings.simplefilter("ignore", (VerifyWarning, FITSFixedWarning)) + WCS_TIME_CUBE = WCS(Header.fromstring(HEADER_TIME_CUBE, sep="\n")) + + +def test_time_cube(): + # Spectral cube with a weird axis ordering + + wcs = WCS_TIME_CUBE + + assert wcs.pixel_n_dim == 3 + assert wcs.world_n_dim == 3 + assert wcs.array_shape == (11, 2048, 2048) + assert wcs.pixel_shape == (2048, 2048, 11) + assert wcs.world_axis_physical_types == ["pos.eq.dec", "pos.eq.ra", "time"] + assert wcs.world_axis_units == ["deg", "deg", "s"] + assert wcs.pixel_axis_names == ["", "", ""] + assert wcs.world_axis_names == ["", "", ""] + + assert_equal( + wcs.axis_correlation_matrix, + [[True, True, False], [True, True, False], [False, False, True]], + ) + + components = wcs.world_axis_object_components + assert_celestial_component(components[0], 1) + assert_celestial_component(components[1], 0) + assert components[2][:2] == ("time", 0) + assert callable(components[2][2]) + + assert wcs.world_axis_object_classes["celestial"][0] is SkyCoord + assert wcs.world_axis_object_classes["celestial"][1] == () + assert isinstance(wcs.world_axis_object_classes["celestial"][2]["frame"], ICRS) + assert wcs.world_axis_object_classes["celestial"][2]["unit"] == (u.deg, u.deg) + + assert wcs.world_axis_object_classes["time"][0] is Time + assert wcs.world_axis_object_classes["time"][1] == () + assert wcs.world_axis_object_classes["time"][2] == {} + assert callable(wcs.world_axis_object_classes["time"][3]) + + assert_allclose( + wcs.pixel_to_world_values(-449.2, 2955.6, 0), + (14.8289418840003, 2.01824372640628, 2375.341), + ) + + assert_allclose( + wcs.array_index_to_world_values(0, 2955.6, -449.2), + (14.8289418840003, 2.01824372640628, 2375.341), + ) + + assert_allclose( + wcs.world_to_pixel_values(14.8289418840003, 2.01824372640628, 2375.341), + (-449.2, 2955.6, 0), + ) + assert_equal( + wcs.world_to_array_index_values(14.8289418840003, 2.01824372640628, 2375.341), + (0, 2956, -449), + ) + + # High-level API + + coord, time = wcs.pixel_to_world(29, 39, 44) + assert isinstance(coord, SkyCoord) + assert isinstance(coord.frame, ICRS) + assert_allclose(coord.ra.deg, 1.7323356692202325) + assert_allclose(coord.dec.deg, 14.783516054817797) + assert isinstance(time, Time) + assert_allclose(time.mjd, 54746.03429755324) + + coord, time = wcs.array_index_to_world(44, 39, 29) + assert isinstance(coord, SkyCoord) + assert isinstance(coord.frame, ICRS) + assert_allclose(coord.ra.deg, 1.7323356692202325) + assert_allclose(coord.dec.deg, 14.783516054817797) + assert isinstance(time, Time) + assert_allclose(time.mjd, 54746.03429755324) + + x, y, z = wcs.world_to_pixel(coord, time) + assert_allclose(x, 29.0) + assert_allclose(y, 39.0) + assert_allclose(z, 44.0) + + # Order of world coordinates shouldn't matter + x, y, z = wcs.world_to_pixel(time, coord) + assert_allclose(x, 29.0) + assert_allclose(y, 39.0) + assert_allclose(z, 44.0) + + i, j, k = wcs.world_to_array_index(coord, time) + assert_equal(i, 44) + assert_equal(j, 39) + assert_equal(k, 29) + + # Order of world coordinates shouldn't matter + i, j, k = wcs.world_to_array_index(time, coord) + assert_equal(i, 44) + assert_equal(j, 39) + assert_equal(k, 29) + + +############################################################################### +# The following tests are to make sure that Time objects are constructed +# correctly for a variety of combinations of WCS keywords +############################################################################### + + +HEADER_TIME_1D = """ +SIMPLE = T +BITPIX = -32 +NAXIS = 1 +NAXIS1 = 2048 +TIMESYS = 'UTC' +TREFPOS = 'TOPOCENT' +MJDREF = 50002.6 +CTYPE1 = 'UTC' +CRVAL1 = 5 +CUNIT1 = 's' +CRPIX1 = 1.0 +CDELT1 = 2 +OBSGEO-L= -20 +OBSGEO-B= -70 +OBSGEO-H= 2530 +""" + +if Version(WCSLIB_VERSION) >= Version("7.1"): + HEADER_TIME_1D += "DATEREF = '1995-10-12T14:24:00'\n" + + +@pytest.fixture +def header_time_1d(): + return Header.fromstring(HEADER_TIME_1D, sep="\n") + + +def assert_time_at(header, position, jd1, jd2, scale, format): + with warnings.catch_warnings(): + warnings.simplefilter("ignore", FITSFixedWarning) + wcs = WCS(header) + time = wcs.pixel_to_world(position) + assert_allclose(time.jd1, jd1, rtol=1e-10) + assert_allclose(time.jd2, jd2, rtol=1e-10) + assert time.format == format + assert time.scale == scale + + +@pytest.mark.parametrize( + "scale", ("tai", "tcb", "tcg", "tdb", "tt", "ut1", "utc", "local") +) +def test_time_1d_values(header_time_1d, scale): + # Check that Time objects are instantiated with the correct values, + # scales, and formats. + + header_time_1d["CTYPE1"] = scale.upper() + assert_time_at(header_time_1d, 1, 2450003, 0.1 + 7 / 3600 / 24, scale, "mjd") + + +def test_time_1d_values_gps(header_time_1d): + # Special treatment for GPS scale + header_time_1d["CTYPE1"] = "GPS" + assert_time_at(header_time_1d, 1, 2450003, 0.1 + (7 + 19) / 3600 / 24, "tai", "mjd") + + +def test_time_1d_values_deprecated(header_time_1d): + # Deprecated (in FITS) scales + header_time_1d["CTYPE1"] = "TDT" + assert_time_at(header_time_1d, 1, 2450003, 0.1 + 7 / 3600 / 24, "tt", "mjd") + header_time_1d["CTYPE1"] = "IAT" + assert_time_at(header_time_1d, 1, 2450003, 0.1 + 7 / 3600 / 24, "tai", "mjd") + header_time_1d["CTYPE1"] = "GMT" + assert_time_at(header_time_1d, 1, 2450003, 0.1 + 7 / 3600 / 24, "utc", "mjd") + header_time_1d["CTYPE1"] = "ET" + assert_time_at(header_time_1d, 1, 2450003, 0.1 + 7 / 3600 / 24, "tt", "mjd") + + +def test_time_1d_values_time(header_time_1d): + header_time_1d["CTYPE1"] = "TIME" + assert_time_at(header_time_1d, 1, 2450003, 0.1 + 7 / 3600 / 24, "utc", "mjd") + header_time_1d["TIMESYS"] = "TAI" + assert_time_at(header_time_1d, 1, 2450003, 0.1 + 7 / 3600 / 24, "tai", "mjd") + + +@pytest.mark.remote_data +@pytest.mark.parametrize("scale", ("tai", "tcb", "tcg", "tdb", "tt", "ut1", "utc")) +def test_time_1d_roundtrip(header_time_1d, scale): + # Check that coordinates round-trip + + pixel_in = np.arange(3, 10) + + header_time_1d["CTYPE1"] = scale.upper() + + with warnings.catch_warnings(): + warnings.simplefilter("ignore", FITSFixedWarning) + wcs = WCS(header_time_1d) + + # Simple test + time = wcs.pixel_to_world(pixel_in) + pixel_out = wcs.world_to_pixel(time) + assert_allclose(pixel_in, pixel_out) + + # Test with an intermediate change to a different scale/format + time = wcs.pixel_to_world(pixel_in).tdb + time.format = "isot" + pixel_out = wcs.world_to_pixel(time) + assert_allclose(pixel_in, pixel_out) + + +def test_time_1d_high_precision(header_time_1d): + # Case where the MJDREF is split into two for high precision + del header_time_1d["MJDREF"] + header_time_1d["MJDREFI"] = 52000.0 + header_time_1d["MJDREFF"] = 1e-11 + + with warnings.catch_warnings(): + warnings.simplefilter("ignore", FITSFixedWarning) + wcs = WCS(header_time_1d) + + time = wcs.pixel_to_world(10) + + # Here we have to use a very small rtol to really test that MJDREFF is + # taken into account + assert_allclose(time.jd1, 2452001.0, rtol=1e-12) + assert_allclose(time.jd2, -0.5 + 25 / 3600 / 24 + 1e-11, rtol=1e-13) + + +def test_time_1d_location_geodetic(header_time_1d): + # Make sure that the location is correctly returned (geodetic case) + + with warnings.catch_warnings(): + warnings.simplefilter("ignore", FITSFixedWarning) + wcs = WCS(header_time_1d) + + time = wcs.pixel_to_world(10) + + lon, lat, alt = time.location.to_geodetic() + + # FIXME: alt won't work for now because ERFA doesn't implement the IAU 1976 + # ellipsoid (https://github.com/astropy/astropy/issues/9420) + assert_allclose(lon.degree, -20) + assert_allclose(lat.degree, -70) + # assert_allclose(alt.to_value(u.m), 2530.) + + +@pytest.fixture +def header_time_1d_no_obs(): + header = Header.fromstring(HEADER_TIME_1D, sep="\n") + del header["OBSGEO-L"] + del header["OBSGEO-B"] + del header["OBSGEO-H"] + return header + + +def test_time_1d_location_geocentric(header_time_1d_no_obs): + # Make sure that the location is correctly returned (geocentric case) + + header = header_time_1d_no_obs + + header["OBSGEO-X"] = 10 + header["OBSGEO-Y"] = -20 + header["OBSGEO-Z"] = 30 + + with warnings.catch_warnings(): + warnings.simplefilter("ignore", FITSFixedWarning) + wcs = WCS(header) + + time = wcs.pixel_to_world(10) + + x, y, z = time.location.to_geocentric() + + assert_allclose(x.to_value(u.m), 10) + assert_allclose(y.to_value(u.m), -20) + assert_allclose(z.to_value(u.m), 30) + + +def test_time_1d_location_geocenter(header_time_1d_no_obs): + header_time_1d_no_obs["TREFPOS"] = "GEOCENTER" + + wcs = WCS(header_time_1d_no_obs) + time = wcs.pixel_to_world(10) + + x, y, z = time.location.to_geocentric() + + assert_allclose(x.to_value(u.m), 0) + assert_allclose(y.to_value(u.m), 0) + assert_allclose(z.to_value(u.m), 0) + + +def test_time_1d_location_missing(header_time_1d_no_obs): + # Check what happens when no location is present + + wcs = WCS(header_time_1d_no_obs) + with pytest.warns( + UserWarning, + match=( + "Missing or incomplete observer location " + "information, setting location in Time to None" + ), + ): + time = wcs.pixel_to_world(10) + + assert time.location is None + + +def test_time_1d_location_incomplete(header_time_1d_no_obs): + # Check what happens when location information is incomplete + + header_time_1d_no_obs["OBSGEO-L"] = 10.0 + + with warnings.catch_warnings(): + warnings.simplefilter("ignore", FITSFixedWarning) + wcs = WCS(header_time_1d_no_obs) + + with pytest.warns( + UserWarning, + match=( + "Missing or incomplete observer location " + "information, setting location in Time to None" + ), + ): + time = wcs.pixel_to_world(10) + + assert time.location is None + + +def test_time_1d_location_unsupported(header_time_1d_no_obs): + # Check what happens when TREFPOS is unsupported + + header_time_1d_no_obs["TREFPOS"] = "BARYCENTER" + + wcs = WCS(header_time_1d_no_obs) + with pytest.warns( + UserWarning, + match=( + "Observation location 'barycenter' is not " + "supported, setting location in Time to None" + ), + ): + time = wcs.pixel_to_world(10) + + assert time.location is None + + +def test_time_1d_unsupported_ctype(header_time_1d_no_obs): + # For cases that we don't support yet, e.g. UT(...), use Time and drop sub-scale + + # Case where the MJDREF is split into two for high precision + header_time_1d_no_obs["CTYPE1"] = "UT(WWV)" + + wcs = WCS(header_time_1d_no_obs) + with ( + pytest.warns( + UserWarning, + match="Dropping unsupported sub-scale WWV from scale UT", + ), + pytest.warns( + UserWarning, + match="Missing or incomplete observer location information", + ), + ): + time = wcs.pixel_to_world(10) + + assert isinstance(time, Time) + + +############################################################################### +# Extra corner cases +############################################################################### + + +def test_unrecognized_unit(): + # TODO: Determine whether the following behavior is desirable + wcs = WCS(naxis=1) + with pytest.warns(UnitsWarning): + wcs.wcs.cunit = ["bananas // sekonds"] + assert wcs.world_axis_units == ["bananas // sekonds"] + + +def test_distortion_correlations(): + filename = get_pkg_data_filename("../../tests/data/sip.fits") + with pytest.warns(FITSFixedWarning): + w = WCS(filename) + assert_equal(w.axis_correlation_matrix, True) + + # Changing PC to an identity matrix doesn't change anything since + # distortions are still present. + w.wcs.pc = [[1, 0], [0, 1]] + assert_equal(w.axis_correlation_matrix, True) + + # Nor does changing the name of the axes to make them non-celestial + w.wcs.ctype = ["X", "Y"] + assert_equal(w.axis_correlation_matrix, True) + + # However once we turn off the distortions the matrix changes + w.sip = None + assert_equal(w.axis_correlation_matrix, [[True, False], [False, True]]) + + # If we go back to celestial coordinates then the matrix is all True again + w.wcs.ctype = ["RA---TAN", "DEC--TAN"] + assert_equal(w.axis_correlation_matrix, True) + + # Or if we change to X/Y but have a non-identity PC + w.wcs.pc = [[0.9, -0.1], [0.1, 0.9]] + w.wcs.ctype = ["X", "Y"] + assert_equal(w.axis_correlation_matrix, True) + + +def test_custom_ctype_to_ucd_mappings(): + wcs = WCS(naxis=1) + wcs.wcs.ctype = ["SPAM"] + + assert wcs.world_axis_physical_types == [None] + + # Check simple behavior + + with custom_ctype_to_ucd_mapping({"APPLE": "food.fruit"}): + assert wcs.world_axis_physical_types == [None] + + with custom_ctype_to_ucd_mapping({"APPLE": "food.fruit", "SPAM": "food.spam"}): + assert wcs.world_axis_physical_types == ["food.spam"] + + # Check nesting + + with custom_ctype_to_ucd_mapping({"SPAM": "food.spam"}): + with custom_ctype_to_ucd_mapping({"APPLE": "food.fruit"}): + assert wcs.world_axis_physical_types == ["food.spam"] + + with custom_ctype_to_ucd_mapping({"APPLE": "food.fruit"}): + with custom_ctype_to_ucd_mapping({"SPAM": "food.spam"}): + assert wcs.world_axis_physical_types == ["food.spam"] + + # Check priority in nesting + + with custom_ctype_to_ucd_mapping({"SPAM": "notfood"}): + with custom_ctype_to_ucd_mapping({"SPAM": "food.spam"}): + assert wcs.world_axis_physical_types == ["food.spam"] + + with custom_ctype_to_ucd_mapping({"SPAM": "food.spam"}): + with custom_ctype_to_ucd_mapping({"SPAM": "notfood"}): + assert wcs.world_axis_physical_types == ["notfood"] + + +def test_caching_components_and_classes(): + # Make sure that when we change the WCS object, the classes and components + # are updated (we use a cache internally, so we need to make sure the cache + # is invalidated if needed) + + wcs = WCS_SIMPLE_CELESTIAL.deepcopy() + + components = wcs.world_axis_object_components + assert len(components) == 2 + assert_celestial_component(components[0], 0) + assert_celestial_component(components[1], 1) + + assert wcs.world_axis_object_classes["celestial"][0] is SkyCoord + assert wcs.world_axis_object_classes["celestial"][1] == () + assert isinstance(wcs.world_axis_object_classes["celestial"][2]["frame"], ICRS) + assert wcs.world_axis_object_classes["celestial"][2]["unit"] == (u.deg, u.deg) + + wcs.wcs.radesys = "FK5" + + frame = wcs.world_axis_object_classes["celestial"][2]["frame"] + assert isinstance(frame, FK5) + assert frame.equinox.jyear == 2000.0 + + wcs.wcs.equinox = 2010 + + frame = wcs.world_axis_object_classes["celestial"][2]["frame"] + assert isinstance(frame, FK5) + assert frame.equinox.jyear == 2010.0 + + +@pytest.mark.parametrize("unit", ["arcsec", "mas"]) +def test_world_to_pixel_preserves_non_degree_celestial_units(unit): + header = Header() + header["NAXIS"] = 2 + header["NAXIS1"] = 100 + header["NAXIS2"] = 100 + header["CTYPE1"] = "RA---TAN" + header["CTYPE2"] = "DEC--TAN" + header["CUNIT1"] = unit + header["CUNIT2"] = unit + header["CRPIX1"] = 1 + header["CRPIX2"] = 1 + header["CRVAL1"] = 0 + header["CRVAL2"] = 0 + header["CDELT1"] = 1 + header["CDELT2"] = 1 + + wcs = WCS(header, preserve_units=True) + sky = wcs.pixel_to_world(0, 56) + + assert wcs.world_axis_units == [unit, unit] + assert_allclose(wcs.world_to_pixel(sky), (0, 56), atol=1e-14) + + +def test_sub_wcsapi_attributes(): + # Regression test for a bug that caused some of the WCS attributes to be + # incorrect when using WCS.sub or WCS.celestial (which is an alias for sub + # with lon/lat types). + + wcs = WCS_SPECTRAL_CUBE.deepcopy() + wcs.pixel_shape = (30, 40, 50) + wcs.pixel_bounds = [(-1, 11), (-2, 18), (5, 15)] + + # Use celestial shortcut + + wcs_sub1 = wcs.celestial + + assert wcs_sub1.pixel_n_dim == 2 + assert wcs_sub1.world_n_dim == 2 + assert wcs_sub1.array_shape == (50, 30) + assert wcs_sub1.pixel_shape == (30, 50) + assert wcs_sub1.pixel_bounds == [(-1, 11), (5, 15)] + assert wcs_sub1.world_axis_physical_types == [ + "pos.galactic.lat", + "pos.galactic.lon", + ] + assert wcs_sub1.world_axis_units == ["deg", "deg"] + assert wcs_sub1.world_axis_names == ["Latitude", "Longitude"] + + # Try adding axes + + wcs_sub2 = wcs.sub([0, 2, 0]) + + assert wcs_sub2.pixel_n_dim == 3 + assert wcs_sub2.world_n_dim == 3 + assert wcs_sub2.array_shape == (None, 40, None) + assert wcs_sub2.pixel_shape == (None, 40, None) + assert wcs_sub2.pixel_bounds == [None, (-2, 18), None] + assert wcs_sub2.world_axis_physical_types == [None, "em.freq", None] + assert wcs_sub2.world_axis_units == ["", "Hz", ""] + assert wcs_sub2.world_axis_names == ["", "Frequency", ""] + + # Use strings + + wcs_sub3 = wcs.sub(["longitude", "latitude"]) + + assert wcs_sub3.pixel_n_dim == 2 + assert wcs_sub3.world_n_dim == 2 + assert wcs_sub3.array_shape == (30, 50) + assert wcs_sub3.pixel_shape == (50, 30) + assert wcs_sub3.pixel_bounds == [(5, 15), (-1, 11)] + assert wcs_sub3.world_axis_physical_types == [ + "pos.galactic.lon", + "pos.galactic.lat", + ] + assert wcs_sub3.world_axis_units == ["deg", "deg"] + assert wcs_sub3.world_axis_names == ["Longitude", "Latitude"] + + # Now try without CNAME set + + wcs.wcs.cname = [""] * wcs.wcs.naxis + wcs_sub4 = wcs.sub(["longitude", "latitude"]) + + assert wcs_sub4.pixel_n_dim == 2 + assert wcs_sub4.world_n_dim == 2 + assert wcs_sub4.array_shape == (30, 50) + assert wcs_sub4.pixel_shape == (50, 30) + assert wcs_sub4.pixel_bounds == [(5, 15), (-1, 11)] + assert wcs_sub4.world_axis_physical_types == [ + "pos.galactic.lon", + "pos.galactic.lat", + ] + assert wcs_sub4.world_axis_units == ["deg", "deg"] + assert wcs_sub4.world_axis_names == ["", ""] + + +############################################################################### +# Spectral transformations +############################################################################### + +HEADER_SPECTRAL_FRAMES = """ +BUNIT = 'Jy/beam' +EQUINOX = 2.000000000E+03 +CTYPE1 = 'RA---SIN' +CRVAL1 = 2.60108333333E+02 +CDELT1 = -2.777777845E-04 +CRPIX1 = 1.0 +CUNIT1 = 'deg' +CTYPE2 = 'DEC--SIN' +CRVAL2 = -9.75000000000E-01 +CDELT2 = 2.777777845E-04 +CRPIX2 = 1.0 +CUNIT2 = 'deg' +CTYPE3 = 'FREQ' +CRVAL3 = 1.37835117405E+09 +CDELT3 = 9.765625000E+04 +CRPIX3 = 32.0 +CUNIT3 = 'Hz' +SPECSYS = 'TOPOCENT' +RESTFRQ = 1.420405752E+09 / [Hz] +RADESYS = 'FK5' +""" + + +@pytest.fixture +def header_spectral_frames(): + return Header.fromstring(HEADER_SPECTRAL_FRAMES, sep="\n") + + +def test_spectralcoord_frame(header_spectral_frames): + # This is a test to check the numerical results of transformations between + # different velocity frames. We simply make sure that the returned + # SpectralCoords are in the right frame but don't check the transformations + # since this is already done in test_spectralcoord_accuracy + # in astropy.coordinates. + + with iers.conf.set_temp("auto_download", False): + obstime = Time("2009-05-04T04:44:23", scale="utc") + + header = header_spectral_frames.copy() + header["MJD-OBS"] = obstime.mjd + header["CRVAL1"] = 16.33211 + header["CRVAL2"] = -34.2221 + header["OBSGEO-L"] = 144.2 + header["OBSGEO-B"] = -20.2 + header["OBSGEO-H"] = 0.0 + + # We start off with a WCS defined in topocentric frequency + with pytest.warns(FITSFixedWarning): + wcs_topo = WCS(header) + + # We convert a single pixel coordinate to world coordinates and keep only + # the second high level object - a SpectralCoord: + sc_topo = wcs_topo.pixel_to_world(0, 0, 31)[1] + + # We check that this is in topocentric frame with zero velocities + assert isinstance(sc_topo, SpectralCoord) + assert isinstance(sc_topo.observer, ITRS) + assert sc_topo.observer.obstime.isot == obstime.isot + assert_equal(sc_topo.observer.data.differentials["s"].d_xyz.value, 0) + + observatory = ( + EarthLocation.from_geodetic(144.2, -20.2) + .get_itrs(obstime=obstime) + .transform_to(ICRS()) + ) + assert ( + observatory.separation_3d(sc_topo.observer.transform_to(ICRS())) < 1 * u.km + ) + + for specsys, expected_frame in VELOCITY_FRAMES.items(): + header["SPECSYS"] = specsys + with pytest.warns(FITSFixedWarning): + wcs = WCS(header) + sc = wcs.pixel_to_world(0, 0, 31)[1] + + # Now transform to the expected velocity frame, which should leave + # the spectral coordinate unchanged + sc_check = sc.with_observer_stationary_relative_to(expected_frame) + assert_quantity_allclose(sc.quantity, sc_check.quantity) + + +@pytest.mark.parametrize("ctype3", ["ZOPT", "BETA", "VELO", "VRAD", "VOPT"]) +@pytest.mark.parametrize("observer", [False, True]) +def test_different_ctypes(header_spectral_frames, ctype3, observer): + header = header_spectral_frames.copy() + header["CTYPE3"] = ctype3 + header["CRVAL3"] = 0.1 + header["CDELT3"] = 0.001 + + if ctype3[0] == "V": + header["CUNIT3"] = "m s-1" + else: + header["CUNIT3"] = "" + + header["RESTWAV"] = 0.21106114 + header["MJD-OBS"] = 55197 + + if observer: + header["OBSGEO-L"] = 144.2 + header["OBSGEO-B"] = -20.2 + header["OBSGEO-H"] = 0.0 + header["SPECSYS"] = "BARYCENT" + + with warnings.catch_warnings(): + warnings.simplefilter("ignore", FITSFixedWarning) + wcs = WCS(header) + + skycoord, spectralcoord = wcs.pixel_to_world(0, 0, 31) + + assert isinstance(spectralcoord, SpectralCoord) + + pix = wcs.world_to_pixel(skycoord, spectralcoord) + assert_allclose(pix, [0, 0, 31], rtol=1e-6, atol=1e-9) + + +def test_non_convergence_warning(): + """Test case for issue #11446 + Since we can't define a target accuracy when plotting a WCS `all_world2pix` + should not error but only warn when the default accuracy can't be reached. + """ + # define a minimal WCS where convergence fails for certain image positions + wcs = WCS(naxis=2) + crpix = [0, 0] + a = b = ap = bp = np.zeros((4, 4)) + a[3, 0] = -1.20116753e-07 + + test_pos_x = [1000, 1] + test_pos_y = [0, 2] + + wcs.sip = Sip(a, b, ap, bp, crpix) + # first make sure the WCS works when using a low accuracy + expected = wcs.all_world2pix(test_pos_x, test_pos_y, 0, tolerance=1e-3) + + # then check that it fails when using the default accuracy + with pytest.raises(NoConvergence): + wcs.all_world2pix(test_pos_x, test_pos_y, 0) + + # at last check that world_to_pixel_values raises a warning but returns + # the same 'low accuray' result + with pytest.warns( + UserWarning, + match=( + r"^'WCS.all_world2pix' failed to converge to the requested accuracy\.\n" + r"After 20 iterations, the solution is diverging at least for one input point\.$" + ), + ): + assert_allclose(wcs.world_to_pixel_values(test_pos_x, test_pos_y), expected) + + +HEADER_SPECTRAL_1D = """ +CTYPE1 = 'FREQ' +CRVAL1 = 1.37835117405E+09 +CDELT1 = 9.765625000E+04 +CRPIX1 = 32.0 +CUNIT1 = 'Hz' +SPECSYS = 'TOPOCENT' +RESTFRQ = 1.420405752E+09 / [Hz] +RADESYS = 'FK5' +""" + + +@pytest.fixture +def header_spectral_1d(): + return Header.fromstring(HEADER_SPECTRAL_1D, sep="\n") + + +@pytest.mark.parametrize("ctype1", ["ZOPT", "BETA", "VELO", "VRAD", "VOPT"]) +@pytest.mark.parametrize("observer", [False, True]) +def test_spectral_1d(header_spectral_1d, ctype1, observer): + # This is a regression test for issues that happened with 1-d WCS + # where the target is not defined but observer is. + + header = header_spectral_1d.copy() + header["CTYPE1"] = ctype1 + header["CRVAL1"] = 0.1 + header["CDELT1"] = 0.001 + + if ctype1[0] == "V": + header["CUNIT1"] = "m s-1" + else: + header["CUNIT1"] = "" + + header["RESTWAV"] = 0.21106114 + header["MJD-OBS"] = 55197 + + if observer: + header["OBSGEO-L"] = 144.2 + header["OBSGEO-B"] = -20.2 + header["OBSGEO-H"] = 0.0 + header["SPECSYS"] = "BARYCENT" + + with warnings.catch_warnings(): + warnings.simplefilter("ignore", FITSFixedWarning) + wcs = WCS(header) + + # First ensure that transformations round-trip + + spectralcoord = wcs.pixel_to_world(31) + + assert isinstance(spectralcoord, SpectralCoord) + assert spectralcoord.target is None + assert (spectralcoord.observer is not None) is observer + + if observer: + # WCS has observer, SpectralCoord has observer but no target, + # so we still warn that the target is missing. + with pytest.warns( + AstropyUserWarning, match="No target defined on SpectralCoord" + ): + pix = wcs.world_to_pixel(spectralcoord) + else: + # Neither WCS nor SpectralCoord has an observer, so no warning. + pix = wcs.world_to_pixel(spectralcoord) + + assert_allclose(pix, [31], rtol=1e-6) + + # Also make sure that we can convert a SpectralCoord on which the observer + # is not defined but the target is. + + with pytest.warns(AstropyUserWarning, match="No velocity defined on frame"): + spectralcoord_no_obs = SpectralCoord( + spectralcoord.quantity, + doppler_rest=spectralcoord.doppler_rest, + doppler_convention=spectralcoord.doppler_convention, + target=ICRS(10 * u.deg, 20 * u.deg, distance=1 * u.kpc), + ) + + if observer: + with pytest.warns( + AstropyUserWarning, match="No observer defined on SpectralCoord" + ): + pix2 = wcs.world_to_pixel(spectralcoord_no_obs) + else: + # Neither WCS nor SpectralCoord has an observer, so no warning. + pix2 = wcs.world_to_pixel(spectralcoord_no_obs) + assert_allclose(pix2, [31], rtol=1e-6) + + # And finally check case when both observer and target are defined on the + # SpectralCoord + + with pytest.warns(AstropyUserWarning, match="No velocity defined on frame"): + spectralcoord_no_obs = SpectralCoord( + spectralcoord.quantity, + doppler_rest=spectralcoord.doppler_rest, + doppler_convention=spectralcoord.doppler_convention, + observer=ICRS(10 * u.deg, 20 * u.deg, distance=0 * u.kpc), + target=ICRS(10 * u.deg, 20 * u.deg, distance=1 * u.kpc), + ) + + if observer: + pix3 = wcs.world_to_pixel(spectralcoord_no_obs) + else: + with pytest.warns(AstropyUserWarning, match="No observer defined on WCS"): + pix3 = wcs.world_to_pixel(spectralcoord_no_obs) + + assert_allclose(pix3, [31], rtol=1e-6) + + +HEADER_SPECTRAL_WITH_TIME = """ +WCSAXES = 3 +CTYPE1 = 'RA---TAN' +CTYPE2 = 'DEC--TAN' +CTYPE3 = 'WAVE' +CRVAL1 = 98.83153 +CRVAL2 = -66.818 +CRVAL3 = 6.4205 +CRPIX1 = 21. +CRPIX2 = 22. +CRPIX3 = 1. +CDELT1 = 3.6111E-05 +CDELT2 = 3.6111E-05 +CDELT3 = 0.001 +CUNIT1 = 'deg' +CUNIT2 = 'deg' +CUNIT3 = 'um' +MJD-AVG = 59045.41466 +RADESYS = 'ICRS' +SPECSYS = 'BARYCENT' +TIMESYS = 'UTC' +""" + + +@pytest.fixture +def header_spectral_with_time(): + return Header.fromstring(HEADER_SPECTRAL_WITH_TIME, sep="\n") + + +def test_spectral_with_time_kw(header_spectral_with_time): + def check_wcs(header): + assert_allclose(w.all_pix2world(*w.wcs.crpix, 1), w.wcs.crval) + sky, spec = w.pixel_to_world(*w.wcs.crpix) + assert_allclose( + (sky.spherical.lon.degree, sky.spherical.lat.degree, spec.value), + w.wcs.crval, + rtol=1e-3, + ) + + # Check with MJD-AVG and TIMESYS + hdr = header_spectral_with_time.copy() + with warnings.catch_warnings(): + warnings.simplefilter("ignore", (VerifyWarning, FITSFixedWarning)) + w = WCS(hdr) + # Make sure the correct keyword is used in a test + assert ~np.isnan(w.wcs.mjdavg) + assert np.isnan(w.wcs.mjdobs) + + check_wcs(w) + + # Check fall back to MJD-OBS + hdr["MJD-OBS"] = hdr["MJD-AVG"] + del hdr["MJD-AVG"] + with warnings.catch_warnings(): + warnings.simplefilter("ignore", (VerifyWarning, FITSFixedWarning)) + w = WCS(hdr) + # Make sure the correct keyword is used in a test + assert ~np.isnan(w.wcs.mjdobs) + assert np.isnan(w.wcs.mjdavg) + check_wcs(w) + + # Check fall back to DATE--OBS + hdr["DATE-OBS"] = "2020-07-15" + del hdr["MJD-OBS"] + with warnings.catch_warnings(): + warnings.simplefilter("ignore", (VerifyWarning, FITSFixedWarning)) + w = WCS(hdr) + w.wcs.mjdobs = np.nan + # Make sure the correct keyword is used in a test + assert np.isnan(w.wcs.mjdobs) + assert np.isnan(w.wcs.mjdavg) + assert w.wcs.dateobs != "" + check_wcs(hdr) + + # Check fall back to scale='utc' + del hdr["TIMESYS"] + check_wcs(hdr) + + +def test_fits_tab_time_and_units(): + """ + This test is a regression test for https://github.com/astropy/astropy/issues/12095 + + It checks the following: + - If a spatial WCS isn't converted to units of deg by wcslib it still works. + - If TIMESYS is upper case we parse it correctly + - If a TIME CTYPE key has an algorithm code (in this case -TAB) it still works. + + The file used here was generated by gWCS and then edited to add the TIMESYS key. + """ + with ( + fits.open(get_pkg_data_filename("data/example_4d_tab.fits")) as hdul, + pytest.warns(FITSFixedWarning), + ): + w = WCS(header=hdul[0].header, fobj=hdul) + + with warnings.catch_warnings(): + warnings.filterwarnings("ignore", message=r".*dubious year \(Note \d\)") + world = w.pixel_to_world(0, 0, 0, 0) + + assert isinstance(world[0], SkyCoord) + assert world[0].data.lat.unit is u.arcsec + assert world[0].data.lon.unit is u.arcsec + assert u.allclose(world[0].l, 0.06475506 * u.deg) + assert u.allclose(world[0].b, -0.02430561 * u.deg) + assert isinstance(world[1], SpectralCoord) + assert u.allclose(world[1], 24.96 * u.Hz) + assert isinstance(world[2], Time) + assert world[2].scale == "utc" + assert u.allclose(world[2].mjd, 0.00032986111111110716) + + +################################################################################ +# Tests with Stokes +################################################################################ + + +HEADER_POLARIZED = """ +CTYPE1 = 'HPLT-TAN' +CTYPE2 = 'HPLN-TAN' +CTYPE3 = 'STOKES' +""" + + +@pytest.fixture +def header_polarized(): + return Header.fromstring(HEADER_POLARIZED, sep="\n") + + +@pytest.fixture +def wcs_polarized(header_polarized): + return WCS(header_polarized) + + +def test_phys_type_polarization(wcs_polarized): + w = wcs_polarized + assert w.world_axis_physical_types[2] == "phys.polarization.stokes" + + +def test_pixel_to_world_stokes(wcs_polarized): + w = wcs_polarized + world = w.pixel_to_world(0, 0, 0) + assert world[2] == 1 + assert isinstance(world[2], StokesCoord) + assert_equal(world[2].symbol, "I") + + world = w.pixel_to_world(0, 0, [0, 1, 2, 3]) + assert isinstance(world[2], StokesCoord) + assert_array_equal(world[2], [1, 2, 3, 4]) + assert_array_equal(world[2].symbol, ["I", "Q", "U", "V"]) + + +@pytest.mark.parametrize("direction", ("world_to_pixel", "pixel_to_world")) +def test_out_of_bounds(direction): + # Make sure that we correctly deal with any out-of-bound values in the + # low-level API. + + wcs = WCS(naxis=2) + wcs.wcs.crpix = (1, 1) + wcs.wcs.set() + + func = ( + wcs.world_to_pixel_values + if direction == "world_to_pixel" + else wcs.pixel_to_world_values + ) + + xp = np.arange(5) + 1 + yp = np.arange(5) + 1 + + # Before setting bounds + + # Python Scalars + xw, yw = func(1, 1) + assert_array_equal(xw, 1) + assert_array_equal(yw, 1) + + # Numpy Scalars + xw, yw = func(xp[0], yp[0]) + assert_array_equal(xw, 1) + assert_array_equal(yw, 1) + + # Arrays + xw, yw = func(xp, yp) + assert_array_equal(xw, [1, 2, 3, 4, 5]) + assert_array_equal(yw, [1, 2, 3, 4, 5]) + + # Mixed + xw, yw = func(xp[0], yp) + assert_array_equal(xw, 1) + assert_array_equal(yw, [1, 2, 3, 4, 5]) + + # Setting bounds on one dimension + + wcs.pixel_bounds = [(-0.5, 3.5), None] + + # Python Scalars + + xw, yw = func(1, 1) + assert_array_equal(xw, 1) + assert_array_equal(yw, 1) + + xw, yw = func(5, 5) + assert_array_equal(xw, np.nan) + assert_array_equal(yw, 5) + + # Numpy Scalars + + xw, yw = func(xp[0], yp[0]) + assert_array_equal(xw, 1) + assert_array_equal(yw, 1) + + xw, yw = func(xp[-1], yp[-1]) + assert_array_equal(xw, np.nan) + assert_array_equal(yw, 5) + + # Arrays + xw, yw = func(xp, yp) + assert_array_equal(xw, [1, 2, 3, np.nan, np.nan]) + assert_array_equal(yw, [1, 2, 3, 4, 5]) + + # Mixed + xw, yw = func(xp[-1], yp) + assert_array_equal(xw, np.nan) + assert_array_equal(yw, [1, 2, 3, 4, 5]) + + # Setting bounds on both dimensions + + wcs.pixel_bounds = [(-0.5, 3.5), (2.5, 5.5)] + + # Python Scalars + + xw, yw = func(1, 1) + assert_array_equal(xw, 1) + assert_array_equal(yw, np.nan) + + xw, yw = func(5, 5) + assert_array_equal(xw, np.nan) + assert_array_equal(yw, 5) + + # Numpy Scalars + + xw, yw = func(xp[0], yp[0]) + assert_array_equal(xw, 1) + assert_array_equal(yw, np.nan) + + xw, yw = func(xp[-1], yp[-1]) + assert_array_equal(xw, np.nan) + assert_array_equal(yw, 5) + + # Arrays + xw, yw = func(xp, yp) + assert_array_equal(xw, [1, 2, 3, np.nan, np.nan]) + assert_array_equal(yw, [np.nan, np.nan, 3, 4, 5]) + + # Mixed + xw, yw = func(xp[-1], yp) + assert_array_equal(xw, np.nan) + assert_array_equal(yw, [np.nan, np.nan, 3, 4, 5]) + + +def test_restfrq_restwav(): + # Regression test for a bug that caused an incorrect rest + # frequency/wavelength to be used. This happened for example when using + # VOPT but with only restfrq defined. + + wcs = WCS( + header={ + "CRVAL1": 100, + "CTYPE1": "VOPT", + "CDELT1": 1.0, + "CUNIT1": "m/s", + "CRPIX1": 1, + "RESTFRQ": 1e9, + } + ) + + scoord1 = wcs.pixel_to_world(5) + + assert scoord1.doppler_convention == "optical" + assert_quantity_allclose(scoord1.doppler_rest, (1 * u.GHz).to(u.m, u.spectral())) + + wcs = WCS( + header={ + "CRVAL1": 100, + "CTYPE1": "VRAD", + "CDELT1": 1.0, + "CUNIT1": "m/s", + "CRPIX1": 1, + "RESTWAV": 1e-6, + } + ) + + scoord2 = wcs.pixel_to_world(5) + + assert scoord2.doppler_convention == "radio" + assert_quantity_allclose(scoord2.doppler_rest, (1 * u.um).to(u.Hz, u.spectral())) + + wcs = WCS( + header={ + "CRVAL1": 100, + "CTYPE1": "VRAD", + "CDELT1": 1.0, + "CUNIT1": "m/s", + "CRPIX1": 1, + "RESTWAV": 1, + "RESTFRQ": 295000000.0, + } + ) + + # Once we switch from a deprecation warning to an exception, convert the + # following to pytest.raises + with pytest.warns( + AstropyDeprecationWarning, + match=re.escape( + "restfrq=295000000.0 Hz and restwav=1.0 m=299792458.0 Hz are not consistent to rtol=1e-4" + ), + ): + scoord3 = wcs.pixel_to_world(5) + + +def test_array_index_conversions_arrays_1d(): + # Regression test for a bug that caused world_to_array_index to return lists + # instead of Numpy arrays - 1D version + # Also ensures that pixels are plain arrays if the array is not Masked. + + wcs = WCS(naxis=1) + wcs.wcs.ctype = ("FREQ",) + + coord = SpectralCoord([10, 12], unit="Hz") + + i = wcs.world_to_array_index(coord) + assert isinstance(i, np.ndarray) + + x = wcs.world_to_pixel(coord) + assert isinstance(x, np.ndarray) and not isinstance(x, Masked) + + +def test_array_index_conversions_arrays_2d(): + # Regression test for a bug that caused world_to_array_index to return lists + # instead of Numpy arrays - 2D versions + + wcs = WCS(naxis=2) + wcs.wcs.ctype = "RA---TAN", "DEC--TAN" + + coord = SkyCoord([10, 12], [20, 22], unit="deg", frame="icrs") + + i, j = wcs.world_to_array_index(coord) + assert isinstance(i, np.ndarray) + assert isinstance(j, np.ndarray) + + x, y = wcs.world_to_pixel(coord) + assert isinstance(x, np.ndarray) + assert isinstance(y, np.ndarray) + + # Also check that pixels are plain arrays if the array is not Masked. + assert not isinstance(x, Masked) and not isinstance(y, Masked) + # But will be masked once mask has been set on any item. + coord[0] = np.ma.masked + x, y = wcs.world_to_pixel(coord) + assert isinstance(x, Masked) and isinstance(y, Masked) + assert_array_equal(x.mask, coord.mask) + assert_array_equal(y.mask, coord.mask) + # Even if the mask is later unset. + coord[:] = np.ma.nomask + x, y = wcs.world_to_pixel(coord) + assert isinstance(x, Masked) and isinstance(y, Masked) + assert not x.mask.any() and not y.mask.any() + + +def test_array_index_conversions_scalars_1d(): + # And check that things works properly for scalars (1D) + + wcs = WCS(naxis=1) + wcs.wcs.ctype = ("FREQ",) + + coord = SpectralCoord(10, unit="Hz") + + i = wcs.world_to_array_index(coord) + assert isinstance(i, np.ndarray) and i.ndim == 0 + + x = wcs.world_to_pixel(coord) + assert isinstance(x, np.ndarray) and x.ndim == 0 + + +def test_array_index_conversions_scalars_2d(): + # And check that things works properly for scalars (2D) + + wcs = WCS(naxis=2) + wcs.wcs.ctype = "RA---TAN", "DEC--TAN" + + scoord = SkyCoord(10, 20, unit="deg", frame="icrs") + + i, j = wcs.world_to_array_index(scoord) + assert isinstance(i, np.ndarray) and i.ndim == 0 + assert isinstance(j, np.ndarray) and j.ndim == 0 + + x, y = wcs.world_to_pixel(scoord) + assert isinstance(x, np.ndarray) and x.ndim == 0 + assert isinstance(y, np.ndarray) and y.ndim == 0 + + +class TestMaskedData: + wcs = WCS_SIMPLE_CELESTIAL + + def test_pixel_to_world(self): + mask = [False, True] + x = Masked([4.907, -75.09299637], mask=mask) + y = Masked([73.8485, 223.84849637], mask=mask) + world = self.wcs.pixel_to_world(x, y) + assert_array_equal(world.mask, mask) + + def test_world_to_pixel(self): + coord = SkyCoord( + l=Masked([0, 1] * u.deg, mask=[False, True]), + b=Masked([0, 1] * u.deg, mask=[False, True]), + frame="galactic", + ) + x, y = self.wcs.world_to_pixel(coord) + assert_array_equal(x.mask, coord.mask) + assert_array_equal(y.mask, coord.mask) diff --git a/astropy/wcs/wcsapi/tests/test_high_level_api.py b/astropy/wcs/wcsapi/tests/test_high_level_api.py new file mode 100644 index 000000000000..0be1bee1a2d8 --- /dev/null +++ b/astropy/wcs/wcsapi/tests/test_high_level_api.py @@ -0,0 +1,357 @@ +import re + +import numpy as np +import pytest +from numpy.testing import assert_allclose + +from astropy import units as u +from astropy.coordinates import SkyCoord +from astropy.units import Quantity +from astropy.wcs import WCS +from astropy.wcs.wcsapi.high_level_api import ( + HighLevelWCSMixin, + high_level_objects_to_values, + values_to_high_level_objects, +) +from astropy.wcs.wcsapi.low_level_api import BaseLowLevelWCS + + +class DoubleLowLevelWCS(BaseLowLevelWCS): + """ + Basic dummy transformation that doubles values. + """ + + def pixel_to_world_values(self, *pixel_arrays): + return [np.asarray(pix) * 2 for pix in pixel_arrays] + + def world_to_pixel_values(self, *world_arrays): + return [np.asarray(world) / 2 for world in world_arrays] + + +class SimpleDuplicateWCS(DoubleLowLevelWCS, HighLevelWCSMixin): + """ + This example WCS has two of the world coordinates that use the same class, + which triggers a different path in the high level WCS code. + """ + + @property + def pixel_n_dim(self): + return 2 + + @property + def world_n_dim(self): + return 2 + + @property + def world_axis_physical_types(self): + return ["pos.eq.ra", "pos.eq.dec"] + + @property + def world_axis_units(self): + return ["deg", "deg"] + + @property + def world_axis_object_components(self): + return [("test1", 0, "value"), ("test2", 0, "value")] + + @property + def world_axis_object_classes(self): + return { + "test1": (Quantity, (), {"unit": "deg"}), + "test2": (Quantity, (), {"unit": "deg"}), + } + + +def test_simple_duplicate(): + # Make sure that things work properly when the low-level WCS uses the same + # class for two of the coordinates. + + wcs = SimpleDuplicateWCS() + q1, q2 = wcs.pixel_to_world(1, 2) + + assert isinstance(q1, Quantity) + assert isinstance(q2, Quantity) + + x, y = wcs.world_to_pixel(q1, q2) + + assert_allclose(x, 1) + assert_allclose(y, 2) + + +class SkyCoordDuplicateWCS(DoubleLowLevelWCS, HighLevelWCSMixin): + """ + This example WCS returns two SkyCoord objects which, which triggers a + different path in the high level WCS code. + """ + + @property + def pixel_n_dim(self): + return 4 + + @property + def world_n_dim(self): + return 4 + + @property + def world_axis_physical_types(self): + return ["pos.eq.ra", "pos.eq.dec", "pos.galactic.lon", "pos.galactic.lat"] + + @property + def world_axis_units(self): + return ["deg", "deg", "deg", "deg"] + + @property + def world_axis_object_components(self): + # Deliberately use 'ra'/'dec' here to make sure that string argument + # names work properly. + return [ + ("test1", "ra", "spherical.lon.degree"), + ("test1", "dec", "spherical.lat.degree"), + ("test2", 0, "spherical.lon.degree"), + ("test2", 1, "spherical.lat.degree"), + ] + + @property + def world_axis_object_classes(self): + return { + "test1": (SkyCoord, (), {"unit": "deg"}), + "test2": (SkyCoord, (), {"unit": "deg", "frame": "galactic"}), + } + + +def test_skycoord_duplicate(): + # Make sure that things work properly when the low-level WCS uses the same + # class, and specifically a SkyCoord for two of the coordinates. + + wcs = SkyCoordDuplicateWCS() + c1, c2 = wcs.pixel_to_world(1, 2, 3, 4) + + assert isinstance(c1, SkyCoord) + assert isinstance(c2, SkyCoord) + + x, y, z, a = wcs.world_to_pixel(c1, c2) + + assert_allclose(x, 1) + assert_allclose(y, 2) + assert_allclose(z, 3) + assert_allclose(a, 4) + + +class SerializedWCS(DoubleLowLevelWCS, HighLevelWCSMixin): + """ + WCS with serialized classes + """ + + @property + def serialized_classes(self): + return True + + @property + def pixel_n_dim(self): + return 2 + + @property + def world_n_dim(self): + return 2 + + @property + def world_axis_physical_types(self): + return ["pos.eq.ra", "pos.eq.dec"] + + @property + def world_axis_units(self): + return ["deg", "deg"] + + @property + def world_axis_object_components(self): + return [("test", 0, "value")] + + @property + def world_axis_object_classes(self): + return { + "test": ( + "astropy.units.Quantity", + (), + {"unit": ("astropy.units.Unit", ("deg",), {})}, + ) + } + + +def test_serialized_classes(): + wcs = SerializedWCS() + q = wcs.pixel_to_world(1) + + assert isinstance(q, Quantity) + + x = wcs.world_to_pixel(q) + + assert_allclose(x, 1) + + +def test_objects_to_values(): + wcs = SkyCoordDuplicateWCS() + c1, c2 = wcs.pixel_to_world(1, 2, 3, 4) + + values = high_level_objects_to_values(c1, c2, low_level_wcs=wcs) + + assert np.allclose(values, [2, 4, 6, 8]) + + +def test_values_to_objects(): + wcs = SkyCoordDuplicateWCS() + c1, c2 = wcs.pixel_to_world(1, 2, 3, 4) + + c1_out, c2_out = values_to_high_level_objects(*[2, 4, 6, 8], low_level_wcs=wcs) + assert c1.ra == c1_out.ra + assert c2.l == c2_out.l + + assert c1.dec == c1_out.dec + assert c2.b == c2_out.b + + +def test_low_level_wcs_duck_typed(): + # Anything exposing world_axis_object_classes and world_axis_object_components + # should be accepted as low_level_wcs; serialized_classes is optional. + from types import SimpleNamespace + from typing import NamedTuple + + wcs = SkyCoordDuplicateWCS() + c1, c2 = wcs.pixel_to_world(1, 2, 3, 4) + + ns = SimpleNamespace( + world_axis_object_classes=wcs.world_axis_object_classes, + world_axis_object_components=wcs.world_axis_object_components, + ) + assert np.allclose( + high_level_objects_to_values(c1, c2, low_level_wcs=ns), [2, 4, 6, 8] + ) + c1_out, c2_out = values_to_high_level_objects(*[2, 4, 6, 8], low_level_wcs=ns) + assert c1.ra == c1_out.ra and c1.dec == c1_out.dec + assert c2.l == c2_out.l and c2.b == c2_out.b + + class Frame(NamedTuple): + world_axis_object_classes: dict + world_axis_object_components: list + + nt = Frame(wcs.world_axis_object_classes, wcs.world_axis_object_components) + assert np.allclose( + high_level_objects_to_values(c1, c2, low_level_wcs=nt), [2, 4, 6, 8] + ) + + +def test_low_level_wcs_duck_typed_serialized(): + # serialized_classes=True on a duck-typed object still triggers deserialization. + from types import SimpleNamespace + + wcs = SerializedWCS() + q = wcs.pixel_to_world(1) + + ns = SimpleNamespace( + world_axis_object_classes=wcs.world_axis_object_classes, + world_axis_object_components=wcs.world_axis_object_components, + serialized_classes=True, + ) + (value,) = high_level_objects_to_values(q, low_level_wcs=ns) + assert_allclose(value, 2) + (q_out,) = values_to_high_level_objects(2.0, low_level_wcs=ns) + assert isinstance(q_out, Quantity) + assert_allclose(q_out.to_value(u.deg), 2.0) + + +class InvalidWCSQuantity(SkyCoordDuplicateWCS): + """ + WCS which defines ``world_axis_object_components`` which returns Quantity + instead of bare Numpy arrays, which can cause issues. This is for a + regression test to make sure that we don't return Quantities from + ``world_axis_object_components``. + """ + + @property + def world_axis_object_components(self): + return [ + ("test1", "ra", "spherical.lon"), + ("test1", "dec", "spherical.lat"), + ("test2", 0, "spherical.lon"), + ("test2", 1, "spherical.lat"), + ] + + +def test_objects_to_values_invalid_type(): + wcs = InvalidWCSQuantity() + c1, c2 = wcs.pixel_to_world(1, 2, 3, 4) + with pytest.raises( + TypeError, + match=( + re.escape( + "WCS world_axis_object_components results in values which are not " + "scalars or plain Numpy arrays (got )" + ) + ), + ): + high_level_objects_to_values(c1, c2, low_level_wcs=wcs) + + +def test_values_to_objects_invalid_type(): + wcs = SkyCoordDuplicateWCS() + c1, c2 = wcs.pixel_to_world(1, 2, 3, 4) + with pytest.raises( + TypeError, + match=( + re.escape( + "Expected world coordinates as scalars or plain Numpy arrays (got " + ")" + ) + ), + ): + values_to_high_level_objects(2 * u.m, 4, 6, 8, low_level_wcs=wcs) + + +class MinimalHighLevelWCS(HighLevelWCSMixin): + def __init__(self, low_level_wcs): + self._low_level_wcs = low_level_wcs + + @property + def low_level_wcs(self): + return self._low_level_wcs + + +def test_minimal_mixin_subclass(): + # Regression test for a bug that caused coordinate conversions to fail + # unless the WCS dimensions were defined on the high level WCS (which they + # are not required to be) + + fits_wcs = WCS(naxis=2) + high_level_wcs = MinimalHighLevelWCS(fits_wcs) + + coord = high_level_wcs.pixel_to_world(1, 2) + pixel = high_level_wcs.world_to_pixel(*coord) + + coord = high_level_wcs.array_index_to_world(1, 2) + pixel = high_level_wcs.world_to_array_index(*coord) + + assert_allclose(pixel, (1, 2)) + + +def test_world_to_array_index_nan(): + # see https://github.com/astropy/astropy/issues/17227 + wcs1 = WCS(naxis=1) + wcs1.wcs.crpix = (1,) + wcs1.wcs.set() + wcs1.pixel_bounds = [None] + + res1 = wcs1.world_to_array_index(*wcs1.pixel_to_world((5,))) + assert not np.any(np.isnan(res1)) + assert res1.ndim == 0 + assert res1.item() == 5 + + wcs2 = WCS(naxis=2) + wcs2.wcs.crpix = (1, 1) + wcs2.wcs.set() + wcs2.pixel_bounds = [None, (-0.5, 3.5)] + + res2 = wcs2.world_to_array_index(*wcs2.pixel_to_world(5, 5)) + assert not np.any(np.isnan(res2)) + assert isinstance(res2, tuple) + assert len(res2) == 2 + assert res2 == (np.iinfo(int).min, 5) diff --git a/astropy/wcs/wcsapi/tests/test_high_level_wcs_wrapper.py b/astropy/wcs/wcsapi/tests/test_high_level_wcs_wrapper.py new file mode 100644 index 000000000000..ed349a42317d --- /dev/null +++ b/astropy/wcs/wcsapi/tests/test_high_level_wcs_wrapper.py @@ -0,0 +1,81 @@ +import numpy as np +import pytest +from numpy.testing import assert_allclose + +from astropy.coordinates import SkyCoord +from astropy.wcs.wcsapi.high_level_wcs_wrapper import HighLevelWCSWrapper +from astropy.wcs.wcsapi.low_level_api import BaseLowLevelWCS + + +class CustomLowLevelWCS(BaseLowLevelWCS): + @property + def pixel_n_dim(self): + return 2 + + @property + def world_n_dim(self): + return 2 + + @property + def world_axis_physical_types(self): + return ["pos.eq.ra", "pos.eq.dec"] + + @property + def world_axis_units(self): + return ["deg", "deg"] + + def pixel_to_world_values(self, *pixel_arrays): + return [np.asarray(pix) * 2 for pix in pixel_arrays] + + def world_to_pixel_values(self, *world_arrays): + return [np.asarray(world) / 2 for world in world_arrays] + + @property + def world_axis_object_components(self): + return [ + ("test", 0, "spherical.lon.degree"), + ("test", 1, "spherical.lat.degree"), + ] + + @property + def world_axis_object_classes(self): + return {"test": (SkyCoord, (), {"unit": "deg"})} + + +def test_wrapper(): + wcs = CustomLowLevelWCS() + + wrapper = HighLevelWCSWrapper(wcs) + + coord = wrapper.pixel_to_world(1, 2) + + assert isinstance(coord, SkyCoord) + assert coord.isscalar + + x, y = wrapper.world_to_pixel(coord) + + assert_allclose(x, 1) + assert_allclose(y, 2) + + assert wrapper.low_level_wcs is wcs + assert wrapper.pixel_n_dim == 2 + assert wrapper.world_n_dim == 2 + assert wrapper.world_axis_physical_types == ["pos.eq.ra", "pos.eq.dec"] + assert wrapper.world_axis_units == ["deg", "deg"] + assert wrapper.array_shape is None + assert wrapper.pixel_bounds is None + assert np.all(wrapper.axis_correlation_matrix) + + +def test_wrapper_invalid(): + class InvalidCustomLowLevelWCS(CustomLowLevelWCS): + @property + def world_axis_object_classes(self): + return {} + + wcs = InvalidCustomLowLevelWCS() + + wrapper = HighLevelWCSWrapper(wcs) + + with pytest.raises(KeyError): + wrapper.pixel_to_world(1, 2) diff --git a/astropy/wcs/wcsapi/tests/test_low_level_api.py b/astropy/wcs/wcsapi/tests/test_low_level_api.py new file mode 100644 index 000000000000..c2faac0086a3 --- /dev/null +++ b/astropy/wcs/wcsapi/tests/test_low_level_api.py @@ -0,0 +1,22 @@ +import pytest + +from astropy.wcs.wcsapi.low_level_api import validate_physical_types + + +def test_validate_physical_types(): + # Check valid cases + validate_physical_types(["pos.eq.ra", "pos.eq.ra"]) + validate_physical_types(["spect.dopplerVeloc.radio", "custom:spam"]) + validate_physical_types(["time", None]) + + # Make sure validation is case sensitive + with pytest.raises( + ValueError, match=r"'Pos\.eq\.dec' is not a valid IOVA UCD1\+ physical type" + ): + validate_physical_types(["pos.eq.ra", "Pos.eq.dec"]) + + # Make sure nonsense types are picked up + with pytest.raises( + ValueError, match=r"'spam' is not a valid IOVA UCD1\+ physical type" + ): + validate_physical_types(["spam"]) diff --git a/astropy/wcs/wcsapi/tests/test_utils.py b/astropy/wcs/wcsapi/tests/test_utils.py new file mode 100644 index 000000000000..bcdc857c1070 --- /dev/null +++ b/astropy/wcs/wcsapi/tests/test_utils.py @@ -0,0 +1,55 @@ +import pytest + +from astropy import units as u +from astropy.tests.helper import assert_quantity_allclose +from astropy.wcs import WCS +from astropy.wcs.wcsapi.utils import deserialize_class, wcs_info_str + + +def test_construct(): + result = deserialize_class(("astropy.units.Quantity", (10,), {"unit": "deg"})) + assert_quantity_allclose(result, 10 * u.deg) + + +def test_noconstruct(): + result = deserialize_class( + ("astropy.units.Quantity", (), {"unit": "deg"}), construct=False + ) + assert result == (u.Quantity, (), {"unit": "deg"}) + + +def test_invalid(): + with pytest.raises(ValueError) as exc: + deserialize_class(("astropy.units.Quantity", (), {"unit": "deg"}, ())) + assert exc.value.args[0] == "Expected a tuple of three values" + + +DEFAULT_1D_STR = """ +WCS Transformation + +This transformation has 1 pixel and 1 world dimensions + +Array shape (Numpy order): None + +Pixel Dim Axis Name Data size Bounds + 0 None None None + +World Dim Axis Name Physical Type Units + 0 None None unknown + +Correlation between pixel and world axes: + + Pixel Dim +World Dim 0 + 0 yes +""" + + +def test_wcs_info_str(): + # The tests in test_sliced_low_level_wcs.py exercise wcs_info_str + # extensively. This test is to ensure that the function exists and the + # API of the function works as expected. + + wcs_empty = WCS(naxis=1) + + assert wcs_info_str(wcs_empty).strip() == DEFAULT_1D_STR.strip() diff --git a/astropy/wcs/wcsapi/utils.py b/astropy/wcs/wcsapi/utils.py new file mode 100644 index 000000000000..13a3dbb935ef --- /dev/null +++ b/astropy/wcs/wcsapi/utils.py @@ -0,0 +1,152 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +import importlib + +import numpy as np + +__all__ = ["deserialize_class", "wcs_info_str"] + + +def deserialize_class(tpl, construct=True): + """ + Deserialize classes recursively. + """ + if not isinstance(tpl, tuple) or len(tpl) != 3: + raise ValueError("Expected a tuple of three values") + + module, klass = tpl[0].rsplit(".", 1) + module = importlib.import_module(module) + klass = getattr(module, klass) + + args = tuple( + deserialize_class(arg) if isinstance(arg, tuple) else arg for arg in tpl[1] + ) + + kwargs = dict( + (key, deserialize_class(val)) if isinstance(val, tuple) else (key, val) + for (key, val) in tpl[2].items() + ) + + if construct: + return klass(*args, **kwargs) + else: + return klass, args, kwargs + + +def wcs_info_str(wcs): + # Overall header + + if wcs.array_shape is None: + array_shape = None + else: + array_shape = tuple(int(n) for n in wcs.array_shape) + + s = ( + f"{type(wcs).__name__} Transformation\n\n" + f"This transformation has {wcs.pixel_n_dim} pixel and {wcs.world_n_dim} " + "world dimensions\n\n" + f"Array shape (Numpy order): {array_shape}\n\n" + ) + + # Pixel dimensions table + + array_shape = array_shape or (0,) + pixel_shape = wcs.pixel_shape or (None,) * wcs.pixel_n_dim + + # Find largest between header size and value length + pixel_dim_width = max(9, len(str(wcs.pixel_n_dim))) + pixel_nam_width = max(9, *map(len, wcs.pixel_axis_names)) + pixel_siz_width = max(9, len(str(max(array_shape)))) + + # fmt: off + s += (('{0:' + str(pixel_dim_width) + 's}').format('Pixel Dim') + ' ' + + ('{0:' + str(pixel_nam_width) + 's}').format('Axis Name') + ' ' + + ('{0:' + str(pixel_siz_width) + 's}').format('Data size') + ' ' + + 'Bounds\n') + # fmt: on + + if wcs.pixel_bounds is None: + pixel_bounds = [None for _ in range(wcs.pixel_n_dim)] + else: + # converting to scalar arrays and back to Python with np.array(val).item() + # guarantees that we end up with Python scalars (int or float) with + # simple reprs, while not making any unnecessary type promotion + # (e.g. int to float) + pixel_bounds = [ + tuple(np.array(b).item() for b in bounds) for bounds in wcs.pixel_bounds + ] + + for ipix in range(wcs.pixel_n_dim): + # fmt: off + s += (('{0:' + str(pixel_dim_width) + 'g}').format(ipix) + ' ' + + ('{0:' + str(pixel_nam_width) + 's}').format(wcs.pixel_axis_names[ipix] or 'None') + ' ' + + (" " * 5 + str(None) if pixel_shape[ipix] is None else + ('{0:' + str(pixel_siz_width) + 'g}').format(pixel_shape[ipix])) + ' ' + + f"{pixel_bounds[ipix]}\n" + ) + # fmt: on + + s += "\n" + + # World dimensions table + + # Find largest between header size and value length + world_dim_width = max(9, len(str(wcs.world_n_dim))) + world_nam_width = max(9, *(len(x) for x in wcs.world_axis_names if x is not None)) + world_typ_width = max( + [13] + [len(x) for x in wcs.world_axis_physical_types if x is not None] + ) + + # fmt: off + s += (('{0:' + str(world_dim_width) + 's}').format('World Dim') + ' ' + + ('{0:' + str(world_nam_width) + 's}').format('Axis Name') + ' ' + + ('{0:' + str(world_typ_width) + 's}').format('Physical Type') + ' ' + + 'Units\n') + # fmt: on + + for iwrl in range(wcs.world_n_dim): + name = wcs.world_axis_names[iwrl] or "None" + typ = wcs.world_axis_physical_types[iwrl] or "None" + unit = wcs.world_axis_units[iwrl] or "unknown" + + # fmt: off + s += (('{0:' + str(world_dim_width) + 'd}').format(iwrl) + ' ' + + ('{0:' + str(world_nam_width) + 's}').format(name) + ' ' + + ('{0:' + str(world_typ_width) + 's}').format(typ) + ' ' + + '{:s}'.format(unit + '\n')) + # fmt: on + + s += "\n" + + # Axis correlation matrix + + pixel_dim_width = max(3, len(str(wcs.world_n_dim))) + + s += "Correlation between pixel and world axes:\n\n" + + # fmt: off + s += (' ' * world_dim_width + ' ' + + ('{0:^' + str(wcs.pixel_n_dim * 5 - 2) + 's}').format('Pixel Dim') + + '\n') + + s += (('{0:' + str(world_dim_width) + 's}').format('World Dim') + + ''.join([' ' + ('{0:' + str(pixel_dim_width) + 'd}').format(ipix) + for ipix in range(wcs.pixel_n_dim)]) + + '\n') + # fmt: on + + matrix = wcs.axis_correlation_matrix + matrix_str = np.empty(matrix.shape, dtype="U3") + matrix_str[matrix] = "yes" + matrix_str[~matrix] = "no" + + for iwrl in range(wcs.world_n_dim): + # fmt: off + s += (('{0:' + str(world_dim_width) + 'd}').format(iwrl) + + ''.join([' ' + ('{0:>' + str(pixel_dim_width) + 's}').format(matrix_str[iwrl, ipix]) + for ipix in range(wcs.pixel_n_dim)]) + + '\n') + # fmt: on + + # Make sure we get rid of the extra whitespace at the end of some lines + return "\n".join([l.rstrip() for l in s.splitlines()]) diff --git a/astropy/wcs/wcsapi/wrappers/__init__.py b/astropy/wcs/wcsapi/wrappers/__init__.py new file mode 100644 index 000000000000..812b51cf71e2 --- /dev/null +++ b/astropy/wcs/wcsapi/wrappers/__init__.py @@ -0,0 +1,2 @@ +from .base import BaseWCSWrapper +from .sliced_wcs import * diff --git a/astropy/wcs/wcsapi/wrappers/base.py b/astropy/wcs/wcsapi/wrappers/base.py new file mode 100644 index 000000000000..5951538e2138 --- /dev/null +++ b/astropy/wcs/wcsapi/wrappers/base.py @@ -0,0 +1,83 @@ +import abc + +from astropy.wcs.wcsapi import BaseLowLevelWCS, wcs_info_str + + +class BaseWCSWrapper(BaseLowLevelWCS, metaclass=abc.ABCMeta): + """ + A base wrapper class for things that modify Low Level WCSes. + + This wrapper implements a transparent wrapper to many of the properties, + with the idea that not all of them would need to be overridden in your + wrapper, but some probably will. + + Parameters + ---------- + wcs : `astropy.wcs.wcsapi.BaseLowLevelWCS` + The WCS object to wrap + """ + + def __init__(self, wcs, *args, **kwargs): + self._wcs = wcs + + @property + def pixel_n_dim(self): + return self._wcs.pixel_n_dim + + @property + def world_n_dim(self): + return self._wcs.world_n_dim + + @property + def world_axis_physical_types(self): + return self._wcs.world_axis_physical_types + + @property + def world_axis_units(self): + return self._wcs.world_axis_units + + @property + def world_axis_object_components(self): + return self._wcs.world_axis_object_components + + @property + def world_axis_object_classes(self): + return self._wcs.world_axis_object_classes + + @property + def pixel_shape(self): + return self._wcs.pixel_shape + + @property + def pixel_bounds(self): + return self._wcs.pixel_bounds + + @property + def pixel_axis_names(self): + return self._wcs.pixel_axis_names + + @property + def world_axis_names(self): + return self._wcs.world_axis_names + + @property + def axis_correlation_matrix(self): + return self._wcs.axis_correlation_matrix + + @property + def serialized_classes(self): + return self._wcs.serialized_classes + + @abc.abstractmethod + def pixel_to_world_values(self, *pixel_arrays): + pass + + @abc.abstractmethod + def world_to_pixel_values(self, *world_arrays): + pass + + def __repr__(self): + return f"{object.__repr__(self)}\n{str(self)}" + + def __str__(self): + return wcs_info_str(self) diff --git a/astropy/wcs/wcsapi/wrappers/sliced_wcs.py b/astropy/wcs/wcsapi/wrappers/sliced_wcs.py new file mode 100644 index 000000000000..64623a044fd6 --- /dev/null +++ b/astropy/wcs/wcsapi/wrappers/sliced_wcs.py @@ -0,0 +1,336 @@ +import numbers +from collections import defaultdict + +import numpy as np + +from astropy.utils.decorators import lazyproperty + +from .base import BaseWCSWrapper + +__all__ = ["SlicedLowLevelWCS", "sanitize_slices"] + + +def sanitize_slices(slices, ndim): + """ + Given a slice as input sanitise it to an easier to parse format.format. + + This function returns a list ``ndim`` long containing slice objects (or ints). + """ + if not isinstance(slices, (tuple, list)): # We just have a single int + slices = (slices,) + + if len(slices) > ndim: + raise ValueError( + f"The dimensionality of the specified slice {slices} can not be greater " + f"than the dimensionality ({ndim}) of the wcs." + ) + + if any(np.iterable(s) for s in slices): + raise IndexError( + "This slice is invalid, only integer or range slices are supported." + ) + + slices = list(slices) + + if Ellipsis in slices: + if slices.count(Ellipsis) > 1: + raise IndexError("an index can only have a single ellipsis ('...')") + + # Replace the Ellipsis with the correct number of slice(None)s + e_ind = slices.index(Ellipsis) + slices.remove(Ellipsis) + n_e = ndim - len(slices) + for i in range(n_e): + ind = e_ind + i + slices.insert(ind, slice(None)) + + for i in range(ndim): + if i < len(slices): + slc = slices[i] + if isinstance(slc, slice): + if slc.step and slc.step != 1: + raise IndexError("Slicing WCS with a step is not supported.") + elif not isinstance(slc, numbers.Integral): + raise IndexError("Only integer or range slices are accepted.") + else: + slices.append(slice(None)) + + return slices + + +def combine_slices(slice1, slice2): + """ + Given two slices that can be applied to a 1-d array, find the resulting + slice that corresponds to the combination of both slices. We assume that + slice2 can be an integer, but slice1 cannot. + """ + if isinstance(slice1, slice) and slice1.step is not None: + raise ValueError("Only slices with steps of 1 are supported") + + if isinstance(slice2, slice) and slice2.step is not None: + raise ValueError("Only slices with steps of 1 are supported") + + if isinstance(slice2, numbers.Integral): + if slice1.start is None: + return slice2 + else: + return slice2 + slice1.start + + if slice1.start is None: + if slice1.stop is None: + return slice2 + else: + if slice2.stop is None: + return slice(slice2.start, slice1.stop) + else: + return slice(slice2.start, min(slice1.stop, slice2.stop)) + else: + if slice2.start is None: + start = slice1.start + else: + start = slice1.start + slice2.start + if slice2.stop is None: + stop = slice1.stop + else: + if slice1.start is None: + stop = slice2.stop + else: + stop = slice2.stop + slice1.start + if slice1.stop is not None: + stop = min(slice1.stop, stop) + return slice(start, stop) + + +class SlicedLowLevelWCS(BaseWCSWrapper): + """ + A Low Level WCS wrapper which applies an array slice to a WCS. + + This class does not modify the underlying WCS object and can therefore drop + coupled dimensions as it stores which pixel and world dimensions have been + sliced out (or modified) in the underlying WCS and returns the modified + results on all the Low Level WCS methods. + + Parameters + ---------- + wcs : `~astropy.wcs.wcsapi.BaseLowLevelWCS` + The WCS to slice. + slices : `slice` or `tuple` or `int` + A valid array slice to apply to the WCS. + + """ + + def __init__(self, wcs, slices): + slices = sanitize_slices(slices, wcs.pixel_n_dim) + + if isinstance(wcs, SlicedLowLevelWCS): + # Here we combine the current slices with the previous slices + # to avoid ending up with many nested WCSes + self._wcs = wcs._wcs + slices_original = wcs._slices_array.copy() + for ipixel in range(wcs.pixel_n_dim): + ipixel_orig = wcs._wcs.pixel_n_dim - 1 - wcs._pixel_keep[ipixel] + ipixel_new = wcs.pixel_n_dim - 1 - ipixel + slices_original[ipixel_orig] = combine_slices( + slices_original[ipixel_orig], slices[ipixel_new] + ) + self._slices_array = slices_original + else: + self._wcs = wcs + self._slices_array = slices + + self._slices_pixel = self._slices_array[::-1] + + # figure out which pixel dimensions have been kept, then use axis correlation + # matrix to figure out which world dims are kept + self._pixel_keep = np.nonzero( + [ + not isinstance(self._slices_pixel[ip], numbers.Integral) + for ip in range(self._wcs.pixel_n_dim) + ] + )[0] + + # axis_correlation_matrix[world, pixel] + self._world_keep = np.nonzero( + self._wcs.axis_correlation_matrix[:, self._pixel_keep].any(axis=1) + )[0] + + if len(self._pixel_keep) == 0 or len(self._world_keep) == 0: + raise ValueError( + "Cannot slice WCS: the resulting WCS should have " + "at least one pixel and one world dimension." + ) + + @lazyproperty + def dropped_world_dimensions(self): + """ + Information describing the dropped world dimensions. + """ + world_coords = self._pixel_to_world_values_all(*[0] * len(self._pixel_keep)) + dropped_info = defaultdict(list) + + for i in range(self._wcs.world_n_dim): + if i in self._world_keep: + continue + + if "world_axis_object_classes" not in dropped_info: + dropped_info["world_axis_object_classes"] = {} + + wao_classes = self._wcs.world_axis_object_classes + wao_components = self._wcs.world_axis_object_components + + dropped_info["value"].append(world_coords[i]) + dropped_info["world_axis_names"].append(self._wcs.world_axis_names[i]) + dropped_info["world_axis_physical_types"].append( + self._wcs.world_axis_physical_types[i] + ) + dropped_info["world_axis_units"].append(self._wcs.world_axis_units[i]) + dropped_info["world_axis_object_components"].append(wao_components[i]) + dropped_info["world_axis_object_classes"].update( + dict( + filter(lambda x: x[0] == wao_components[i][0], wao_classes.items()) + ) + ) + dropped_info["serialized_classes"] = self.serialized_classes + return dict(dropped_info) + + @property + def pixel_n_dim(self): + return len(self._pixel_keep) + + @property + def world_n_dim(self): + return len(self._world_keep) + + @property + def world_axis_physical_types(self): + return [self._wcs.world_axis_physical_types[i] for i in self._world_keep] + + @property + def world_axis_units(self): + return [self._wcs.world_axis_units[i] for i in self._world_keep] + + @property + def pixel_axis_names(self): + return [self._wcs.pixel_axis_names[i] for i in self._pixel_keep] + + @property + def world_axis_names(self): + return [self._wcs.world_axis_names[i] for i in self._world_keep] + + def _pixel_to_world_values_all(self, *pixel_arrays): + pixel_arrays = tuple(map(np.asanyarray, pixel_arrays)) + pixel_arrays_new = [] + ipix_curr = -1 + for ipix in range(self._wcs.pixel_n_dim): + if isinstance(self._slices_pixel[ipix], numbers.Integral): + pixel_arrays_new.append(self._slices_pixel[ipix]) + else: + ipix_curr += 1 + if self._slices_pixel[ipix].start is not None: + pixel_arrays_new.append( + pixel_arrays[ipix_curr] + self._slices_pixel[ipix].start + ) + else: + pixel_arrays_new.append(pixel_arrays[ipix_curr]) + + pixel_arrays_new = np.broadcast_arrays(*pixel_arrays_new) + return self._wcs.pixel_to_world_values(*pixel_arrays_new) + + def pixel_to_world_values(self, *pixel_arrays): + world_arrays = self._pixel_to_world_values_all(*pixel_arrays) + + # Detect the case of a length 0 array + if isinstance(world_arrays, np.ndarray) and not world_arrays.shape: + return world_arrays + + if self._wcs.world_n_dim > 1: + # Select the dimensions of the original WCS we are keeping. + world_arrays = [world_arrays[iw] for iw in self._world_keep] + # If there is only one world dimension (after slicing) we shouldn't return a tuple. + if self.world_n_dim == 1: + world_arrays = world_arrays[0] + + return world_arrays + + def world_to_pixel_values(self, *world_arrays): + sliced_out_world_coords = self._pixel_to_world_values_all( + *[0] * len(self._pixel_keep) + ) + + world_arrays = tuple(map(np.asanyarray, world_arrays)) + world_arrays_new = [] + iworld_curr = -1 + for iworld in range(self._wcs.world_n_dim): + if iworld in self._world_keep: + iworld_curr += 1 + world_arrays_new.append(world_arrays[iworld_curr]) + else: + world_arrays_new.append(sliced_out_world_coords[iworld]) + + world_arrays_new = np.broadcast_arrays(*world_arrays_new) + pixel_arrays = self._wcs.world_to_pixel_values(*world_arrays_new) + pixel_arrays = ( + list(pixel_arrays) if self._wcs.pixel_n_dim > 1 else [pixel_arrays] + ) + + for ipixel in range(self._wcs.pixel_n_dim): + if ( + isinstance(self._slices_pixel[ipixel], slice) + and self._slices_pixel[ipixel].start is not None + ): + pixel_arrays[ipixel] -= self._slices_pixel[ipixel].start + + # Detect the case of a length 0 array + if isinstance(pixel_arrays, np.ndarray) and not pixel_arrays.shape: + return pixel_arrays + pixel = tuple(pixel_arrays[ip] for ip in self._pixel_keep) + if self.pixel_n_dim == 1: + pixel = pixel[0] + return pixel + + @property + def world_axis_object_components(self): + return [self._wcs.world_axis_object_components[idx] for idx in self._world_keep] + + @property + def world_axis_object_classes(self): + keys_keep = [item[0] for item in self.world_axis_object_components] + return dict( + [ + item + for item in self._wcs.world_axis_object_classes.items() + if item[0] in keys_keep + ] + ) + + @property + def array_shape(self): + if self._wcs.array_shape: + return np.broadcast_to(0, self._wcs.array_shape)[ + tuple(self._slices_array) + ].shape + + @property + def pixel_shape(self): + if self.array_shape: + return tuple(self.array_shape[::-1]) + + @property + def pixel_bounds(self): + if self._wcs.pixel_bounds is None: + return + + bounds = [] + for idx in self._pixel_keep: + if self._slices_pixel[idx].start is None: + bounds.append(self._wcs.pixel_bounds[idx]) + else: + imin, imax = self._wcs.pixel_bounds[idx] + start = self._slices_pixel[idx].start + bounds.append((imin - start, imax - start)) + + return tuple(bounds) + + @property + def axis_correlation_matrix(self): + return self._wcs.axis_correlation_matrix[self._world_keep][:, self._pixel_keep] diff --git a/astropy/wcs/wcsapi/wrappers/tests/__init__.py b/astropy/wcs/wcsapi/wrappers/tests/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/astropy/wcs/wcsapi/wrappers/tests/test_sliced_wcs.py b/astropy/wcs/wcsapi/wrappers/tests/test_sliced_wcs.py new file mode 100644 index 000000000000..5af013d46bf8 --- /dev/null +++ b/astropy/wcs/wcsapi/wrappers/tests/test_sliced_wcs.py @@ -0,0 +1,1049 @@ +import warnings + +import numpy as np +import pytest +from numpy.testing import assert_allclose, assert_equal + +import astropy.units as u +from astropy.coordinates import ICRS, Galactic, SkyCoord +from astropy.io.fits import Header +from astropy.io.fits.verify import VerifyWarning +from astropy.time import Time +from astropy.units import Quantity +from astropy.wcs.wcs import WCS, FITSFixedWarning +from astropy.wcs.wcsapi.tests.helpers import assert_celestial_component +from astropy.wcs.wcsapi.wrappers.sliced_wcs import ( + SlicedLowLevelWCS, + combine_slices, + sanitize_slices, +) + +# To test the slicing we start off from standard FITS WCS +# objects since those implement the low-level API. We create +# a WCS for a spectral cube with axes in non-standard order +# and with correlated celestial axes and an uncorrelated +# spectral axis. + +HEADER_SPECTRAL_CUBE = """ +NAXIS = 3 +NAXIS1 = 10 +NAXIS2 = 20 +NAXIS3 = 30 +CTYPE1 = GLAT-CAR +CTYPE2 = FREQ +CTYPE3 = GLON-CAR +CNAME1 = Latitude +CNAME2 = Frequency +CNAME3 = Longitude +CRVAL1 = 10 +CRVAL2 = 20 +CRVAL3 = 25 +CRPIX1 = 30 +CRPIX2 = 40 +CRPIX3 = 45 +CDELT1 = -0.1 +CDELT2 = 0.5 +CDELT3 = 0.1 +CUNIT1 = deg +CUNIT2 = Hz +CUNIT3 = deg +""" + +with warnings.catch_warnings(): + warnings.simplefilter("ignore", VerifyWarning) + WCS_SPECTRAL_CUBE = WCS(Header.fromstring(HEADER_SPECTRAL_CUBE, sep="\n")) +WCS_SPECTRAL_CUBE.pixel_bounds = [(-1, 50), (-2, 60), (-5, 70)] + + +def test_invalid_slices(): + with pytest.raises(IndexError): + SlicedLowLevelWCS(WCS_SPECTRAL_CUBE, [None, None, [False, False, False]]) + + with pytest.raises(IndexError): + SlicedLowLevelWCS(WCS_SPECTRAL_CUBE, [None, None, slice(None, None, 2)]) + + with pytest.raises(IndexError): + SlicedLowLevelWCS(WCS_SPECTRAL_CUBE, [None, None, 1000.100]) + + +@pytest.mark.parametrize( + "item, ndim, expected", + ( + ([Ellipsis, 10], 4, [slice(None)] * 3 + [10]), + ([10, slice(20, 30)], 5, [10, slice(20, 30)] + [slice(None)] * 3), + ([10, Ellipsis, 8], 10, [10] + [slice(None)] * 8 + [8]), + ), +) +def test_sanitize_slice(item, ndim, expected): + new_item = sanitize_slices(item, ndim) + # FIXME: do we still need the first two since the third assert + # should cover it all? + assert len(new_item) == ndim + assert all(isinstance(i, (slice, int)) for i in new_item) + assert new_item == expected + + +EXPECTED_ELLIPSIS_REPR = """ +SlicedLowLevelWCS Transformation + +This transformation has 3 pixel and 3 world dimensions + +Array shape (Numpy order): (30, 20, 10) + +Pixel Dim Axis Name Data size Bounds + 0 None 10 (-1, 50) + 1 None 20 (-2, 60) + 2 None 30 (-5, 70) + +World Dim Axis Name Physical Type Units + 0 Latitude pos.galactic.lat deg + 1 Frequency em.freq Hz + 2 Longitude pos.galactic.lon deg + +Correlation between pixel and world axes: + + Pixel Dim +World Dim 0 1 2 + 0 yes no yes + 1 no yes no + 2 yes no yes +""" + + +def test_ellipsis(): + wcs = SlicedLowLevelWCS(WCS_SPECTRAL_CUBE, Ellipsis) + + assert wcs.pixel_n_dim == 3 + assert wcs.world_n_dim == 3 + assert wcs.array_shape == (30, 20, 10) + assert wcs.pixel_shape == (10, 20, 30) + assert wcs.world_axis_physical_types == [ + "pos.galactic.lat", + "em.freq", + "pos.galactic.lon", + ] + assert wcs.world_axis_units == ["deg", "Hz", "deg"] + assert wcs.pixel_axis_names == ["", "", ""] + assert wcs.world_axis_names == ["Latitude", "Frequency", "Longitude"] + + assert_equal( + wcs.axis_correlation_matrix, + [[True, False, True], [False, True, False], [True, False, True]], + ) + + components = wcs.world_axis_object_components + assert len(components) == 3 + assert_celestial_component(components[0], 1) + assert components[1][:2] == ("spectral", 0) + assert_celestial_component(components[2], 0) + + assert wcs.world_axis_object_classes["celestial"][0] is SkyCoord + assert wcs.world_axis_object_classes["celestial"][1] == () + assert isinstance(wcs.world_axis_object_classes["celestial"][2]["frame"], Galactic) + assert wcs.world_axis_object_classes["celestial"][2]["unit"] == (u.deg, u.deg) + + assert wcs.world_axis_object_classes["spectral"][0] is Quantity + assert wcs.world_axis_object_classes["spectral"][1] == () + assert wcs.world_axis_object_classes["spectral"][2] == {} + + assert_allclose(wcs.pixel_to_world_values(29, 39, 44), (10, 20, 25)) + assert_allclose(wcs.array_index_to_world_values(44, 39, 29), (10, 20, 25)) + + assert_allclose(wcs.world_to_pixel_values(10, 20, 25), (29.0, 39.0, 44.0)) + assert_equal(wcs.world_to_array_index_values(10, 20, 25), (44, 39, 29)) + + assert_equal(wcs.pixel_bounds, [(-1, 50), (-2, 60), (-5, 70)]) + + assert str(wcs) == EXPECTED_ELLIPSIS_REPR.strip() + assert EXPECTED_ELLIPSIS_REPR.strip() in repr(wcs) + + +def test_pixel_to_world_broadcasting(): + wcs = SlicedLowLevelWCS(WCS_SPECTRAL_CUBE, Ellipsis) + + assert_allclose( + wcs.pixel_to_world_values((29, 29), 39, 44), ((10, 10), (20, 20), (25, 25)) + ) + + +def test_world_to_pixel_broadcasting(): + wcs = SlicedLowLevelWCS(WCS_SPECTRAL_CUBE, Ellipsis) + + assert_allclose( + wcs.world_to_pixel_values((10, 10), 20, 25), + ((29.0, 29.0), (39.0, 39.0), (44.0, 44.0)), + ) + + +EXPECTED_SPECTRAL_SLICE_REPR = """ +SlicedLowLevelWCS Transformation + +This transformation has 2 pixel and 2 world dimensions + +Array shape (Numpy order): (30, 10) + +Pixel Dim Axis Name Data size Bounds + 0 None 10 (-1, 50) + 1 None 30 (-5, 70) + +World Dim Axis Name Physical Type Units + 0 Latitude pos.galactic.lat deg + 1 Longitude pos.galactic.lon deg + +Correlation between pixel and world axes: + + Pixel Dim +World Dim 0 1 + 0 yes yes + 1 yes yes +""" + + +def test_spectral_slice(): + wcs = SlicedLowLevelWCS(WCS_SPECTRAL_CUBE, [slice(None), 10]) + + assert wcs.pixel_n_dim == 2 + assert wcs.world_n_dim == 2 + assert wcs.array_shape == (30, 10) + assert wcs.pixel_shape == (10, 30) + assert wcs.world_axis_physical_types == ["pos.galactic.lat", "pos.galactic.lon"] + assert wcs.world_axis_units == ["deg", "deg"] + assert wcs.pixel_axis_names == ["", ""] + assert wcs.world_axis_names == ["Latitude", "Longitude"] + + assert_equal(wcs.axis_correlation_matrix, [[True, True], [True, True]]) + + components = wcs.world_axis_object_components + assert len(components) == 2 + assert_celestial_component(components[0], 1) + assert_celestial_component(components[1], 0) + + assert wcs.world_axis_object_classes["celestial"][0] is SkyCoord + assert wcs.world_axis_object_classes["celestial"][1] == () + assert isinstance(wcs.world_axis_object_classes["celestial"][2]["frame"], Galactic) + assert wcs.world_axis_object_classes["celestial"][2]["unit"] == (u.deg, u.deg) + + assert_allclose(wcs.pixel_to_world_values(29, 44), (10, 25)) + assert_allclose(wcs.array_index_to_world_values(44, 29), (10, 25)) + + assert_allclose(wcs.world_to_pixel_values(10, 25), (29.0, 44.0)) + assert_equal(wcs.world_to_array_index_values(10, 25), (44, 29)) + + assert_equal(wcs.pixel_bounds, [(-1, 50), (-5, 70)]) + + assert str(wcs) == EXPECTED_SPECTRAL_SLICE_REPR.strip() + assert EXPECTED_SPECTRAL_SLICE_REPR.strip() in repr(wcs) + + +EXPECTED_SPECTRAL_RANGE_REPR = """ +SlicedLowLevelWCS Transformation + +This transformation has 3 pixel and 3 world dimensions + +Array shape (Numpy order): (30, 6, 10) + +Pixel Dim Axis Name Data size Bounds + 0 None 10 (-1, 50) + 1 None 6 (-6, 56) + 2 None 30 (-5, 70) + +World Dim Axis Name Physical Type Units + 0 Latitude pos.galactic.lat deg + 1 Frequency em.freq Hz + 2 Longitude pos.galactic.lon deg + +Correlation between pixel and world axes: + + Pixel Dim +World Dim 0 1 2 + 0 yes no yes + 1 no yes no + 2 yes no yes +""" + + +def test_spectral_range(): + wcs = SlicedLowLevelWCS(WCS_SPECTRAL_CUBE, [slice(None), slice(4, 10)]) + + assert wcs.pixel_n_dim == 3 + assert wcs.world_n_dim == 3 + assert wcs.array_shape == (30, 6, 10) + assert wcs.pixel_shape == (10, 6, 30) + assert wcs.world_axis_physical_types == [ + "pos.galactic.lat", + "em.freq", + "pos.galactic.lon", + ] + assert wcs.world_axis_units == ["deg", "Hz", "deg"] + assert wcs.pixel_axis_names == ["", "", ""] + assert wcs.world_axis_names == ["Latitude", "Frequency", "Longitude"] + + assert_equal( + wcs.axis_correlation_matrix, + [[True, False, True], [False, True, False], [True, False, True]], + ) + + components = wcs.world_axis_object_components + assert len(components) == 3 + assert_celestial_component(components[0], 1) + assert components[1][:2] == ("spectral", 0) + assert_celestial_component(components[2], 0) + + assert wcs.world_axis_object_classes["celestial"][0] is SkyCoord + assert wcs.world_axis_object_classes["celestial"][1] == () + assert isinstance(wcs.world_axis_object_classes["celestial"][2]["frame"], Galactic) + assert wcs.world_axis_object_classes["celestial"][2]["unit"] == (u.deg, u.deg) + + assert wcs.world_axis_object_classes["spectral"][0] is Quantity + assert wcs.world_axis_object_classes["spectral"][1] == () + assert wcs.world_axis_object_classes["spectral"][2] == {} + + assert_allclose(wcs.pixel_to_world_values(29, 35, 44), (10, 20, 25)) + assert_allclose(wcs.array_index_to_world_values(44, 35, 29), (10, 20, 25)) + + assert_allclose(wcs.world_to_pixel_values(10, 20, 25), (29.0, 35.0, 44.0)) + assert_equal(wcs.world_to_array_index_values(10, 20, 25), (44, 35, 29)) + + assert_equal(wcs.pixel_bounds, [(-1, 50), (-6, 56), (-5, 70)]) + + assert str(wcs) == EXPECTED_SPECTRAL_RANGE_REPR.strip() + assert EXPECTED_SPECTRAL_RANGE_REPR.strip() in repr(wcs) + + +EXPECTED_CELESTIAL_SLICE_REPR = """ +SlicedLowLevelWCS Transformation + +This transformation has 2 pixel and 3 world dimensions + +Array shape (Numpy order): (30, 20) + +Pixel Dim Axis Name Data size Bounds + 0 None 20 (-2, 60) + 1 None 30 (-5, 70) + +World Dim Axis Name Physical Type Units + 0 Latitude pos.galactic.lat deg + 1 Frequency em.freq Hz + 2 Longitude pos.galactic.lon deg + +Correlation between pixel and world axes: + + Pixel Dim +World Dim 0 1 + 0 no yes + 1 yes no + 2 no yes +""" + + +def test_celestial_slice(): + wcs = SlicedLowLevelWCS(WCS_SPECTRAL_CUBE, [Ellipsis, 5]) + + assert wcs.pixel_n_dim == 2 + assert wcs.world_n_dim == 3 + assert wcs.array_shape == (30, 20) + assert wcs.pixel_shape == (20, 30) + assert wcs.world_axis_physical_types == [ + "pos.galactic.lat", + "em.freq", + "pos.galactic.lon", + ] + assert wcs.world_axis_units == ["deg", "Hz", "deg"] + assert wcs.pixel_axis_names == ["", ""] + assert wcs.world_axis_names == ["Latitude", "Frequency", "Longitude"] + + assert_equal( + wcs.axis_correlation_matrix, [[False, True], [True, False], [False, True]] + ) + + components = wcs.world_axis_object_components + assert len(components) == 3 + assert_celestial_component(components[0], 1) + assert components[1][:2] == ("spectral", 0) + assert_celestial_component(components[2], 0) + + assert wcs.world_axis_object_classes["celestial"][0] is SkyCoord + assert wcs.world_axis_object_classes["celestial"][1] == () + assert isinstance(wcs.world_axis_object_classes["celestial"][2]["frame"], Galactic) + assert wcs.world_axis_object_classes["celestial"][2]["unit"] == (u.deg, u.deg) + + assert wcs.world_axis_object_classes["spectral"][0] is Quantity + assert wcs.world_axis_object_classes["spectral"][1] == () + assert wcs.world_axis_object_classes["spectral"][2] == {} + + assert_allclose(wcs.pixel_to_world_values(39, 44), (12.4, 20, 25)) + assert_allclose(wcs.array_index_to_world_values(44, 39), (12.4, 20, 25)) + + assert_allclose(wcs.world_to_pixel_values(12.4, 20, 25), (39.0, 44.0)) + assert_equal(wcs.world_to_array_index_values(12.4, 20, 25), (44, 39)) + + assert_equal(wcs.pixel_bounds, [(-2, 60), (-5, 70)]) + + assert str(wcs) == EXPECTED_CELESTIAL_SLICE_REPR.strip() + assert EXPECTED_CELESTIAL_SLICE_REPR.strip() in repr(wcs) + + +EXPECTED_CELESTIAL_RANGE_REPR = """ +SlicedLowLevelWCS Transformation + +This transformation has 3 pixel and 3 world dimensions + +Array shape (Numpy order): (30, 20, 5) + +Pixel Dim Axis Name Data size Bounds + 0 None 5 (-6, 45) + 1 None 20 (-2, 60) + 2 None 30 (-5, 70) + +World Dim Axis Name Physical Type Units + 0 Latitude pos.galactic.lat deg + 1 Frequency em.freq Hz + 2 Longitude pos.galactic.lon deg + +Correlation between pixel and world axes: + + Pixel Dim +World Dim 0 1 2 + 0 yes no yes + 1 no yes no + 2 yes no yes +""" + + +def test_celestial_range(): + wcs = SlicedLowLevelWCS(WCS_SPECTRAL_CUBE, [Ellipsis, slice(5, 10)]) + + assert wcs.pixel_n_dim == 3 + assert wcs.world_n_dim == 3 + assert wcs.array_shape == (30, 20, 5) + assert wcs.pixel_shape == (5, 20, 30) + assert wcs.world_axis_physical_types == [ + "pos.galactic.lat", + "em.freq", + "pos.galactic.lon", + ] + assert wcs.world_axis_units == ["deg", "Hz", "deg"] + assert wcs.pixel_axis_names == ["", "", ""] + assert wcs.world_axis_names == ["Latitude", "Frequency", "Longitude"] + + assert_equal( + wcs.axis_correlation_matrix, + [[True, False, True], [False, True, False], [True, False, True]], + ) + + components = wcs.world_axis_object_components + assert len(components) == 3 + assert_celestial_component(components[0], 1) + assert components[1][:2] == ("spectral", 0) + assert_celestial_component(components[2], 0) + + assert wcs.world_axis_object_classes["celestial"][0] is SkyCoord + assert wcs.world_axis_object_classes["celestial"][1] == () + assert isinstance(wcs.world_axis_object_classes["celestial"][2]["frame"], Galactic) + assert wcs.world_axis_object_classes["celestial"][2]["unit"] == (u.deg, u.deg) + + assert wcs.world_axis_object_classes["spectral"][0] is Quantity + assert wcs.world_axis_object_classes["spectral"][1] == () + assert wcs.world_axis_object_classes["spectral"][2] == {} + + assert_allclose(wcs.pixel_to_world_values(24, 39, 44), (10, 20, 25)) + assert_allclose(wcs.array_index_to_world_values(44, 39, 24), (10, 20, 25)) + + assert_allclose(wcs.world_to_pixel_values(10, 20, 25), (24.0, 39.0, 44.0)) + assert_equal(wcs.world_to_array_index_values(10, 20, 25), (44, 39, 24)) + + assert_equal(wcs.pixel_bounds, [(-6, 45), (-2, 60), (-5, 70)]) + + assert str(wcs) == EXPECTED_CELESTIAL_RANGE_REPR.strip() + assert EXPECTED_CELESTIAL_RANGE_REPR.strip() in repr(wcs) + + +# Now try with a 90 degree rotation + +with warnings.catch_warnings(): + warnings.simplefilter("ignore", VerifyWarning) + WCS_SPECTRAL_CUBE_ROT = WCS(Header.fromstring(HEADER_SPECTRAL_CUBE, sep="\n")) +WCS_SPECTRAL_CUBE_ROT.wcs.pc = [[0, 0, 1], [0, 1, 0], [1, 0, 0]] +WCS_SPECTRAL_CUBE_ROT.wcs.crval[0] = 0 +WCS_SPECTRAL_CUBE_ROT.pixel_bounds = [(-1, 50), (-2, 60), (-5, 70)] + +EXPECTED_CELESTIAL_RANGE_ROT_REPR = """ +SlicedLowLevelWCS Transformation + +This transformation has 3 pixel and 3 world dimensions + +Array shape (Numpy order): (30, 20, 5) + +Pixel Dim Axis Name Data size Bounds + 0 None 5 (-6, 45) + 1 None 20 (-2, 60) + 2 None 30 (-5, 70) + +World Dim Axis Name Physical Type Units + 0 Latitude pos.galactic.lat deg + 1 Frequency em.freq Hz + 2 Longitude pos.galactic.lon deg + +Correlation between pixel and world axes: + + Pixel Dim +World Dim 0 1 2 + 0 yes no yes + 1 no yes no + 2 yes no yes +""" + + +def test_celestial_range_rot(): + wcs = SlicedLowLevelWCS(WCS_SPECTRAL_CUBE_ROT, [Ellipsis, slice(5, 10)]) + + assert wcs.pixel_n_dim == 3 + assert wcs.world_n_dim == 3 + assert wcs.array_shape == (30, 20, 5) + assert wcs.pixel_shape == (5, 20, 30) + assert wcs.world_axis_physical_types == [ + "pos.galactic.lat", + "em.freq", + "pos.galactic.lon", + ] + assert wcs.world_axis_units == ["deg", "Hz", "deg"] + assert wcs.pixel_axis_names == ["", "", ""] + assert wcs.world_axis_names == ["Latitude", "Frequency", "Longitude"] + + assert_equal( + wcs.axis_correlation_matrix, + [[True, False, True], [False, True, False], [True, False, True]], + ) + + components = wcs.world_axis_object_components + assert len(components) == 3 + assert_celestial_component(components[0], 1) + assert components[1][:2] == ("spectral", 0) + assert_celestial_component(components[2], 0) + + assert wcs.world_axis_object_classes["celestial"][0] is SkyCoord + assert wcs.world_axis_object_classes["celestial"][1] == () + assert isinstance(wcs.world_axis_object_classes["celestial"][2]["frame"], Galactic) + assert wcs.world_axis_object_classes["celestial"][2]["unit"] == (u.deg, u.deg) + + assert wcs.world_axis_object_classes["spectral"][0] is Quantity + assert wcs.world_axis_object_classes["spectral"][1] == () + assert wcs.world_axis_object_classes["spectral"][2] == {} + + assert_allclose(wcs.pixel_to_world_values(14, 29, 34), (1, 15, 24)) + assert_allclose(wcs.array_index_to_world_values(34, 29, 14), (1, 15, 24)) + + assert_allclose(wcs.world_to_pixel_values(1, 15, 24), (14.0, 29.0, 34.0)) + assert_equal(wcs.world_to_array_index_values(1, 15, 24), (34, 29, 14)) + + assert_equal(wcs.pixel_bounds, [(-6, 45), (-2, 60), (-5, 70)]) + + assert str(wcs) == EXPECTED_CELESTIAL_RANGE_ROT_REPR.strip() + assert EXPECTED_CELESTIAL_RANGE_ROT_REPR.strip() in repr(wcs) + + +HEADER_NO_SHAPE_CUBE = """ +NAXIS = 3 +CTYPE1 = GLAT-CAR +CTYPE2 = FREQ +CTYPE3 = GLON-CAR +CRVAL1 = 10 +CRVAL2 = 20 +CRVAL3 = 25 +CRPIX1 = 30 +CRPIX2 = 40 +CRPIX3 = 45 +CDELT1 = -0.1 +CDELT2 = 0.5 +CDELT3 = 0.1 +CUNIT1 = deg +CUNIT2 = Hz +CUNIT3 = deg +""" + +with warnings.catch_warnings(): + warnings.simplefilter("ignore", VerifyWarning) + WCS_NO_SHAPE_CUBE = WCS(Header.fromstring(HEADER_NO_SHAPE_CUBE, sep="\n")) + +EXPECTED_NO_SHAPE_REPR = """ +SlicedLowLevelWCS Transformation + +This transformation has 3 pixel and 3 world dimensions + +Array shape (Numpy order): None + +Pixel Dim Axis Name Data size Bounds + 0 None None None + 1 None None None + 2 None None None + +World Dim Axis Name Physical Type Units + 0 None pos.galactic.lat deg + 1 None em.freq Hz + 2 None pos.galactic.lon deg + +Correlation between pixel and world axes: + + Pixel Dim +World Dim 0 1 2 + 0 yes no yes + 1 no yes no + 2 yes no yes +""" + + +def test_no_array_shape(): + wcs = SlicedLowLevelWCS(WCS_NO_SHAPE_CUBE, Ellipsis) + + assert wcs.pixel_n_dim == 3 + assert wcs.world_n_dim == 3 + assert wcs.array_shape is None + assert wcs.pixel_shape is None + assert wcs.world_axis_physical_types == [ + "pos.galactic.lat", + "em.freq", + "pos.galactic.lon", + ] + assert wcs.world_axis_units == ["deg", "Hz", "deg"] + + assert_equal( + wcs.axis_correlation_matrix, + [[True, False, True], [False, True, False], [True, False, True]], + ) + + components = wcs.world_axis_object_components + assert len(components) == 3 + assert_celestial_component(components[0], 1) + assert components[1][:2] == ("spectral", 0) + assert_celestial_component(components[2], 0) + + assert wcs.world_axis_object_classes["celestial"][0] is SkyCoord + assert wcs.world_axis_object_classes["celestial"][1] == () + assert isinstance(wcs.world_axis_object_classes["celestial"][2]["frame"], Galactic) + assert wcs.world_axis_object_classes["celestial"][2]["unit"] == (u.deg, u.deg) + + assert wcs.world_axis_object_classes["spectral"][0] is Quantity + assert wcs.world_axis_object_classes["spectral"][1] == () + assert wcs.world_axis_object_classes["spectral"][2] == {} + + assert_allclose(wcs.pixel_to_world_values(29, 39, 44), (10, 20, 25)) + assert_allclose(wcs.array_index_to_world_values(44, 39, 29), (10, 20, 25)) + + assert_allclose(wcs.world_to_pixel_values(10, 20, 25), (29.0, 39.0, 44.0)) + assert_equal(wcs.world_to_array_index_values(10, 20, 25), (44, 39, 29)) + + assert str(wcs) == EXPECTED_NO_SHAPE_REPR.strip() + assert EXPECTED_NO_SHAPE_REPR.strip() in repr(wcs) + + +# Testing the WCS object having some physical types as None/Unknown +HEADER_SPECTRAL_CUBE_NONE_TYPES = { + "CTYPE1": "GLAT-CAR", + "CUNIT1": "deg", + "CDELT1": -0.1, + "CRPIX1": 30, + "CRVAL1": 10, + "NAXIS1": 10, + "CTYPE2": "", + "CUNIT2": "Hz", + "CDELT2": 0.5, + "CRPIX2": 40, + "CRVAL2": 20, + "NAXIS2": 20, + "CTYPE3": "GLON-CAR", + "CUNIT3": "deg", + "CDELT3": 0.1, + "CRPIX3": 45, + "CRVAL3": 25, + "NAXIS3": 30, +} + +WCS_SPECTRAL_CUBE_NONE_TYPES = WCS(header=HEADER_SPECTRAL_CUBE_NONE_TYPES) +WCS_SPECTRAL_CUBE_NONE_TYPES.pixel_bounds = [(-1, 50), (-2, 60), (-5, 70)] + +WCS_SPECTRAL_CUBE_NONE_TYPES_NP = WCS(header=HEADER_SPECTRAL_CUBE_NONE_TYPES) +WCS_SPECTRAL_CUBE_NONE_TYPES_NP.pixel_bounds = [ + tuple(np.int64(b) for b in t) for t in WCS_SPECTRAL_CUBE_NONE_TYPES.pixel_bounds +] + + +EXPECTED_ELLIPSIS_REPR_NONE_TYPES = """ +SlicedLowLevelWCS Transformation + +This transformation has 3 pixel and 3 world dimensions + +Array shape (Numpy order): (30, 20, 10) + +Pixel Dim Axis Name Data size Bounds + 0 None 10 (-1, 50) + 1 None 20 (-2, 60) + 2 None 30 (-5, 70) + +World Dim Axis Name Physical Type Units + 0 None pos.galactic.lat deg + 1 None None Hz + 2 None pos.galactic.lon deg + +Correlation between pixel and world axes: + + Pixel Dim +World Dim 0 1 2 + 0 yes no yes + 1 no yes no + 2 yes no yes +""" + + +def test_ellipsis_none_types(): + wcs = SlicedLowLevelWCS(WCS_SPECTRAL_CUBE_NONE_TYPES, Ellipsis) + + assert wcs.pixel_n_dim == 3 + assert wcs.world_n_dim == 3 + assert wcs.array_shape == (30, 20, 10) + assert wcs.pixel_shape == (10, 20, 30) + assert wcs.world_axis_physical_types == [ + "pos.galactic.lat", + None, + "pos.galactic.lon", + ] + assert wcs.world_axis_units == ["deg", "Hz", "deg"] + + assert_equal( + wcs.axis_correlation_matrix, + [[True, False, True], [False, True, False], [True, False, True]], + ) + + components = wcs.world_axis_object_components + assert len(components) == 3 + assert_celestial_component(components[0], 1) + assert components[1] == ("world", 0, "value") + assert_celestial_component(components[2], 0) + + assert wcs.world_axis_object_classes["celestial"][0] is SkyCoord + assert wcs.world_axis_object_classes["celestial"][1] == () + assert isinstance(wcs.world_axis_object_classes["celestial"][2]["frame"], Galactic) + assert wcs.world_axis_object_classes["celestial"][2]["unit"] == (u.deg, u.deg) + + assert_allclose(wcs.pixel_to_world_values(29, 39, 44), (10, 20, 25)) + assert_allclose(wcs.array_index_to_world_values(44, 39, 29), (10, 20, 25)) + + assert_allclose(wcs.world_to_pixel_values(10, 20, 25), (29.0, 39.0, 44.0)) + assert_equal(wcs.world_to_array_index_values(10, 20, 25), (44, 39, 29)) + + assert_equal(wcs.pixel_bounds, [(-1, 50), (-2, 60), (-5, 70)]) + + assert str(wcs) == EXPECTED_ELLIPSIS_REPR_NONE_TYPES.strip() + assert EXPECTED_ELLIPSIS_REPR_NONE_TYPES.strip() in repr(wcs) + + wcs_np = SlicedLowLevelWCS(WCS_SPECTRAL_CUBE_NONE_TYPES_NP, Ellipsis) + assert str(wcs_np) == EXPECTED_ELLIPSIS_REPR_NONE_TYPES.strip() + assert EXPECTED_ELLIPSIS_REPR_NONE_TYPES.strip() in repr(wcs) + + +CASES = [ + (slice(None), slice(None), slice(None)), + (slice(None), slice(3, None), slice(3, None)), + (slice(None), slice(None, 16), slice(None, 16)), + (slice(None), slice(3, 16), slice(3, 16)), + (slice(2, None), slice(None), slice(2, None)), + (slice(2, None), slice(3, None), slice(5, None)), + (slice(2, None), slice(None, 16), slice(2, 18)), + (slice(2, None), slice(3, 16), slice(5, 18)), + (slice(None, 10), slice(None), slice(None, 10)), + (slice(None, 10), slice(3, None), slice(3, 10)), + (slice(None, 10), slice(None, 16), slice(None, 10)), + (slice(None, 10), slice(3, 16), slice(3, 10)), + (slice(2, 10), slice(None), slice(2, 10)), + (slice(2, 10), slice(3, None), slice(5, 10)), + (slice(2, 10), slice(None, 16), slice(2, 10)), + (slice(2, 10), slice(3, 16), slice(5, 10)), + (slice(None), 3, 3), + (slice(2, None), 3, 5), + (slice(None, 10), 3, 3), + (slice(2, 10), 3, 5), +] + + +@pytest.mark.parametrize(("slice1", "slice2", "expected"), CASES) +def test_combine_slices(slice1, slice2, expected): + assert combine_slices(slice1, slice2) == expected + + +def test_nested_slicing(): + # Make sure that if we call slicing several times, the result is the same + # as calling the slicing once with the final slice settings. + wcs = WCS_SPECTRAL_CUBE + sub1 = SlicedLowLevelWCS( + SlicedLowLevelWCS( + SlicedLowLevelWCS(wcs, [slice(None), slice(1, 10), slice(None)]), + [3, slice(2, None)], + ), + [slice(None), slice(2, 8)], + ) + sub2 = wcs[3, 3:10, 2:8] + assert_allclose(sub1.pixel_to_world_values(3, 5), sub2.pixel_to_world_values(3, 5)) + assert not isinstance(sub1._wcs, SlicedLowLevelWCS) + + +def test_too_much_slicing(): + wcs = WCS_SPECTRAL_CUBE + with pytest.raises( + ValueError, + match=( + "Cannot slice WCS: the resulting WCS " + "should have at least one pixel and " + "one world dimension" + ), + ): + wcs[0, 1, 2] + + +HEADER_TIME_1D = """ +SIMPLE = T +BITPIX = -32 +NAXIS = 1 +NAXIS1 = 2048 +TIMESYS = 'UTC' +TREFPOS = 'TOPOCENT' +MJDREF = 50002.6 +CTYPE1 = 'UTC' +CRVAL1 = 5 +CUNIT1 = 's' +CRPIX1 = 1.0 +CDELT1 = 2 +OBSGEO-L= -20 +OBSGEO-B= -70 +OBSGEO-H= 2530 +""" + + +@pytest.fixture +def header_time_1d(): + return Header.fromstring(HEADER_TIME_1D, sep="\n") + + +@pytest.fixture +def time_1d_wcs(header_time_1d): + with warnings.catch_warnings(): + warnings.simplefilter("ignore", FITSFixedWarning) + return WCS(header_time_1d) + + +def test_1d_sliced_low_level(time_1d_wcs): + sll = SlicedLowLevelWCS(time_1d_wcs, np.s_[10:20]) + world = sll.pixel_to_world_values([1, 2]) + assert isinstance(world, np.ndarray) + assert np.allclose(world, [27, 29]) + + pixel = sll.world_to_pixel_values(world) + assert isinstance(pixel, np.ndarray) + assert np.allclose(pixel, [1, 2]) + + +def validate_info_dict(result, expected): + result_value = result.pop("value") + expected_value = expected.pop("value") + + np.testing.assert_allclose(result_value, expected_value) + assert result == expected + + +def test_dropped_dimensions(): + wcs = WCS_SPECTRAL_CUBE + + print(wcs.pixel_bounds) + + sub = SlicedLowLevelWCS(wcs, np.s_[:, :, :]) + + assert sub.dropped_world_dimensions == {} + + sub = SlicedLowLevelWCS(wcs, np.s_[:, 2:5, :]) + + assert sub.dropped_world_dimensions == {} + + sub = SlicedLowLevelWCS(wcs, np.s_[:, 0]) + + waocomp = sub.dropped_world_dimensions.pop("world_axis_object_components") + assert len(waocomp) == 1 and waocomp[0][0] == "spectral" and waocomp[0][1] == 0 + waocls = sub.dropped_world_dimensions.pop("world_axis_object_classes") + assert ( + len(waocls) == 1 + and "spectral" in waocls + and waocls["spectral"][0] == u.Quantity + ) + validate_info_dict( + sub.dropped_world_dimensions, + { + "value": [0.5], + "world_axis_physical_types": ["em.freq"], + "world_axis_names": ["Frequency"], + "world_axis_units": ["Hz"], + "serialized_classes": False, + }, + ) + + sub = SlicedLowLevelWCS(wcs, np.s_[:, 0, 0]) + + waocomp = sub.dropped_world_dimensions.pop("world_axis_object_components") + assert len(waocomp) == 1 and waocomp[0][0] == "spectral" and waocomp[0][1] == 0 + waocls = sub.dropped_world_dimensions.pop("world_axis_object_classes") + assert ( + len(waocls) == 1 + and "spectral" in waocls + and waocls["spectral"][0] == u.Quantity + ) + validate_info_dict( + sub.dropped_world_dimensions, + { + "value": [0.5], + "world_axis_physical_types": ["em.freq"], + "world_axis_names": ["Frequency"], + "world_axis_units": ["Hz"], + "serialized_classes": False, + }, + ) + + sub = SlicedLowLevelWCS(wcs, np.s_[0, :, 0]) + + dwd = sub.dropped_world_dimensions + wao_classes = dwd.pop("world_axis_object_classes") + wao_components = dwd.pop("world_axis_object_components") + validate_info_dict( + dwd, + { + "value": [12.86995801, 20.49217541], + "world_axis_physical_types": ["pos.galactic.lat", "pos.galactic.lon"], + "world_axis_names": ["Latitude", "Longitude"], + "world_axis_units": ["deg", "deg"], + "serialized_classes": False, + }, + ) + assert len(wao_components) == 2 + assert_celestial_component(wao_components[0], 1) + assert_celestial_component(wao_components[1], 0) + + assert wao_classes["celestial"][0] is SkyCoord + assert wao_classes["celestial"][1] == () + assert isinstance(wao_classes["celestial"][2]["frame"], Galactic) + assert wao_classes["celestial"][2]["unit"] == (u.deg, u.deg) + + sub = SlicedLowLevelWCS(wcs, np.s_[5, :5, 12]) + + dwd = sub.dropped_world_dimensions + wao_classes = dwd.pop("world_axis_object_classes") + wao_components = dwd.pop("world_axis_object_components") + validate_info_dict( + dwd, + { + "value": [11.67648267, 21.01921192], + "world_axis_physical_types": ["pos.galactic.lat", "pos.galactic.lon"], + "world_axis_names": ["Latitude", "Longitude"], + "world_axis_units": ["deg", "deg"], + "serialized_classes": False, + }, + ) + assert len(wao_components) == 2 + assert_celestial_component(wao_components[0], 1) + assert_celestial_component(wao_components[1], 0) + + assert wao_classes["celestial"][0] is SkyCoord + assert wao_classes["celestial"][1] == () + assert isinstance(wao_classes["celestial"][2]["frame"], Galactic) + assert wao_classes["celestial"][2]["unit"] == (u.deg, u.deg) + + +def test_dropped_dimensions_4d(cube_4d_fitswcs): + sub = SlicedLowLevelWCS(cube_4d_fitswcs, np.s_[:, 12, 5, 5]) + + dwd = sub.dropped_world_dimensions + wao_classes = dwd.pop("world_axis_object_classes") + wao_components = dwd.pop("world_axis_object_components") + + validate_info_dict( + dwd, + { + "value": [4.0e00, -2.0e00, 1.0e10], + "world_axis_physical_types": ["pos.eq.ra", "pos.eq.dec", "em.freq"], + "world_axis_names": ["Right Ascension", "Declination", "Frequency"], + "world_axis_units": ["deg", "deg", "Hz"], + "serialized_classes": False, + }, + ) + + assert wao_classes["celestial"][0] is SkyCoord + assert wao_classes["celestial"][1] == () + assert isinstance(wao_classes["celestial"][2]["frame"], ICRS) + assert wao_classes["celestial"][2]["unit"] == (u.deg, u.deg) + assert wao_classes["spectral"][0:3] == (u.Quantity, (), {}) + + assert_celestial_component(wao_components[0], 0) + assert_celestial_component(wao_components[1], 1) + assert wao_components[2][0:2] == ("spectral", 0) + + sub = SlicedLowLevelWCS(cube_4d_fitswcs, np.s_[12, 12]) + + dwd = sub.dropped_world_dimensions + wao_classes = dwd.pop("world_axis_object_classes") + wao_components = dwd.pop("world_axis_object_components") + validate_info_dict( + dwd, + { + "value": [1.0e10, 5.0e00], + "world_axis_physical_types": ["em.freq", "time"], + "world_axis_names": ["Frequency", "Time"], + "world_axis_units": ["Hz", "s"], + "serialized_classes": False, + }, + ) + assert wao_components[0][0:2] == ("spectral", 0) + assert wao_components[1][0] == "time" + assert wao_components[1][1] == 0 + + assert wao_classes["spectral"][0:3] == (u.Quantity, (), {}) + assert wao_classes["time"][0:3] == (Time, (), {}) + + +def test_pixel_to_world_values_different_int_types(): + int_sliced = SlicedLowLevelWCS(WCS_SPECTRAL_CUBE, np.s_[:, 0, :]) + np64_sliced = SlicedLowLevelWCS(WCS_SPECTRAL_CUBE, np.s_[:, np.int64(0), :]) + pixel_arrays = ([0, 1], [0, 1]) + for int_coord, np64_coord in zip( + int_sliced.pixel_to_world_values(*pixel_arrays), + np64_sliced.pixel_to_world_values(*pixel_arrays), + ): + assert all(int_coord == np64_coord) + + +COUPLED_WCS_HEADER = { + "WCSAXES": 3, + "CRPIX1": (100 + 1) / 2, + "CRPIX2": (25 + 1) / 2, + "CRPIX3": 1.0, + "PC1_1": 0.0, + "PC1_2": -1.0, + "PC1_3": 0.0, + "PC2_1": 1.0, + "PC2_2": 0.0, + "PC2_3": -1.0, + "CDELT1": 5, + "CDELT2": 5, + "CDELT3": 0.055, + "CUNIT1": "arcsec", + "CUNIT2": "arcsec", + "CUNIT3": "Angstrom", + "CTYPE1": "HPLN-TAN", + "CTYPE2": "HPLT-TAN", + "CTYPE3": "WAVE", + "CRVAL1": 0.0, + "CRVAL2": 0.0, + "CRVAL3": 1.05, +} + + +def test_coupled_world_slicing(): + fits_wcs = WCS(header=COUPLED_WCS_HEADER) + sl = SlicedLowLevelWCS(fits_wcs, 0) + world = fits_wcs.pixel_to_world_values(0, 0, 0) + out_pix = sl.world_to_pixel_values(world[0], world[1]) + + assert np.allclose(out_pix[0], 0) diff --git a/astropy/wcs/wcslint.py b/astropy/wcs/wcslint.py new file mode 100644 index 000000000000..528bffe32f3e --- /dev/null +++ b/astropy/wcs/wcslint.py @@ -0,0 +1,23 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +""" +Script support for validating the WCS keywords in a FITS file. +""" + + +def main(args=None): + import argparse + + from . import wcs + + parser = argparse.ArgumentParser( + description=( + "Check the WCS keywords in a FITS file for compliance against the standards" + ) + ) + # TODO: pass suggest_on_error as kwarg when PYTHON_LT_14 is dropped + parser.suggest_on_error = True + + parser.add_argument("filename", nargs=1, help="Path to FITS file to check") + args = parser.parse_args(args) + + print(wcs.validate(args.filename[0])) diff --git a/asv.ci.conf.json b/asv.ci.conf.json new file mode 100644 index 000000000000..c81701f64f33 --- /dev/null +++ b/asv.ci.conf.json @@ -0,0 +1,14 @@ + { + "version": 1, + "project": "astropy", + "project_url": "https://www.astropy.org/", + "repo": ".", + "install_command": [ + "pip install . matplotlib scipy" + ], + "branches": ["main"], + "show_commit_url": "https://github.com/astropy/astropy/commit/", + "pythons": ["3.11"], + "environment_type": "virtualenv", + "benchmark_dir": "astropy-benchmarks/benchmarks" + } diff --git a/cextern/.clang-format b/cextern/.clang-format new file mode 100644 index 000000000000..47a38a93f2db --- /dev/null +++ b/cextern/.clang-format @@ -0,0 +1,2 @@ +DisableFormat: true +SortIncludes: Never diff --git a/cextern/README.rst b/cextern/README.rst index 9e8caca9f3d2..ac531a2b9d4c 100644 --- a/cextern/README.rst +++ b/cextern/README.rst @@ -6,3 +6,8 @@ libraries without python-specific code should be included in this directory. Cython or C code intended for use with Astropy or wrapper code should be in the Astropy source tree. +CFITSIO +------- +The cfitsio directory only contains the small subset of files from CFITSIO which are +required for the astropy.io.fits.hdu.compressed._tiled_compression package. All files are +copied verbatim from CFITSIO and can easily be updated if needed. diff --git a/cextern/cfitsio/ChangeLog b/cextern/cfitsio/ChangeLog new file mode 100644 index 000000000000..ce0db459ef36 --- /dev/null +++ b/cextern/cfitsio/ChangeLog @@ -0,0 +1,4918 @@ + Log of Changes Made to CFITSIO + +Version 4.6.4 - Apr 2026 + + - This release includes patches to security vulnerabilities. We + strongly encourage this upgrade, particularly for those running + CFITSIO in web accessible applications. + + Our thanks to A. Denkiewicz, J.B. Ferreira, S.A. Vives, + Otis0408 and K.B. de Pouqueville for their help with this. + + - Input files iter_image.fits and vari.fits added for use in + iter_image and iter_var example programs. (R. Mathar) + New configure/build options --enable-iterprogs for 'configure', + and -DITERPROGS=ON for CMAKE. + + - Absolute paths for installation directories now supported in + CMAKE builds. + + - Bug fixes for edge cases involving lossless compression of int-type + images, and for float-types that cannot undergo lossy compression. + (M. Becker) + + - Bug fix for case of switching between HDUs of lossless and quantized + compression. (J. Bosch) + + - Bug fix for applying histogram binning to image extensions. + +Version 4.6.3 - Sep 2025 + + - For greater C23 compatibility, updated cfortran.h file and + removed old-style function declarations. + + - Cleanup of multiple compiler warnings. Our thanks to petesmc for + help with this. + + - Updated 'speed' utility to use higher precision total time. + + - Fixes for FreeBSD and OpenBSD build issues. Our thanks to Diab + Jerius. + + - Added RPM support file cfitsio.spec, and packaging helper file + ax_cfitsio.m4. Our thanks to Richard Mathar for these. + + - Added files portfile.cmake and vcpkg.json. Our thanks to + D. Partridge for these. + + - Fixed possible memory leaks in certain error exit conditions. + Our thanks to petesmc. + + - Fix to minor truncation error in ffgthd. + +Version 4.6.2 - Mar 2025 + + - Updated autotools configure script to use AM_MAINTAINER_MODE; + disables automatic regeneration of the build system unless + configure is invoked with '--enable-maintainer-mode' + +Version 4.6.1 - Mar 2025 + + - Updated CMake configuration to use GNUInstallDirs module and to + use CMAKE_INSTALL_LIBDIR and CMAKE_INSTALL_INCLUDEDIR; fixes bug + in 4.6.0 that led to an empty 'includedir' in cfitsio.pc + + - Updated autotools config files + +Version 4.6.0 - Mar 2025 + + - Improved the empty primary data test performed in fits_open_file. + + - Correction to eval_defs.h typedefs to allow for compilation on old + gcc compilers. + + - The fitsverify utility tool now distinguishes between alternate + coordinate systems when testing positions of WCSAXES keywords. + + - Updates made for greater C23 compatibility as required for gcc-15. + + - Possible memory vulnerabilities patched. Our thanks + to Doyensec for reporting this. + + - Fix to a possible heap buffer overflow in a utility tool. Our + thanks to TeamH4C for reporting this. + + - Negative size parameter vulnerability patched in utility + tool. Our thanks to TeamH4C for reporting this. + +Version 4.5.0 - Aug 2024 + + - Conversion of CFITSIO configure/build files to better conform with + automake and libtool. + + - Bug fix for case of bit column output in string format on clang + compilers with high optimization. + + - Added compiler macro support to improve builds on loongarch64 and on + Gnu/Hurd kernels. + + - Bug fix to fitsverify utility. + +Version 4.4.1 - Jun 2024 + + - Removed NOSA license and restored previous license file + +Version 4.4.0 - Feb 2024 + + - Reorganization of helper utility code; added fitsverify + + - CMakeLists.txt: Changed install location of cfitsio-targets.cmake + to conform with the one listed in cfitsio-config.cmake.in (i.e. + including the extra "cfitsio" subdir of lib/cmake). + + - calculator functions that read GTIs do more correct validity + checking of GTI input files + + - fits_insert_rows now works if input table starts with both no rows + and no columns + + - Can now write internal memory files of size > 2^32 directly to a + gzip-compressed output file. + + - Added support for unsigned long long types to fits_update_key. + + - Added ability for Windows builds to handle UTF-8 needed for reading + filenames with non-ASCII characters. + + - Added 2-byte int test to speed.c utility. + + - Made fix to http file handler to expand the allowed URL length. + +Version 4.3.1 - Nov 2023 (patch release) + + - Patch fix needed for modify and delete keyword functions to fully + conform with v4.3.0 long string keyword read/write enhancements. + +Version 4.3.0 - Jul 2023 + + - Bug fix to fits_make_hist[d] that was introduced in 4.2.0. + + - Added overflow checking for case of reading images with 8-byte + float values into 4-byte float arrays. + + - fits_write_key_longstr now handles case of writing a long keyword + in combination with a long keyword value string. + + - Add conversion of French locale comma-to-period in corner cases + appearing in ffr2e and ffd2e functions. + + - Increased the precision when writing version number to User-Agent + strings for http connections. This is needed to fully conform to + 3-field version string format. + + - Bug fix to GTIOVERLAP() calculator function, which was being + treated as a boolean value in expressions, and is now correctly + treated as a floating point result. + + - Bug fix to ARRAY() calculator function, which caused a memory + overflow error + + - Enhancement to the ARRAY function, such that ARRAY(V,d) can + apply new dimensions to V, as long as the total number + of array/vector elements does not change. + + - Enhancement of long string keyword read/write functions to fully + conform with FITS standard specifications for multi-line value + and comment strings. Two new functions have been added to + implement this: fits_get_key_com_strlen and fits_read_string_key_com. + +Version 4.2.0 - Nov 2022 + + - This release includes patches to security vulnerabilities. We + strongly encourage this upgrade, particularly for those running + CFITSIO in web accessible applications. + + - Fix to fits_read_key function, which was failing to properly read + keywords declared type TUINT on compilers where sizeof(int) = + sizeof(long). + + - Added new functions fits_read_cols and fits_write_cols to efficiently + read or write multiple columns in a single function call + + - Added new function fits_copy_selrows to copy only selected rows, such + as the selected rows returned by fits_find_rows + + - Added new calculator functions ERF(X), ERFC(X) and GAMMA(X), which are + mathematical special functions of the same name + + - Added new calculator function GTIFIND() which reports which GTI row + brackets a given time sample + + - Added new calculator functions which operate upon vector table + values NAXIS(V), NAXES(V,n), ELEMENTNUM(V) and AXISELEM(V,n), + and ARRAY(X,d) which promotes scalar X to a vector or array + with given dimensions. + + - The CFITSIO histogramming code now handles binning by any + arbitrary calculator expression rather than just a column name. + Both the binned columns as well as the optional weights may be + calculator expressions, enclosed in parentheses. + + - Binning of vector columns or expressions is now supported, as long + as all binned inputs (as well as the optional weighting) have the + same vector dimensions. Binning of variable-length columns remains + unsupported. + + - A bug that caused histogram weights from columns that are null values + to be tallied along with non-null values has been fixed. + + - The CFITSIO calculator and histogramming functionality is now + fully reentrant and does not require multithreading interlocks. + + - A long-standing segmentation fault bug in the histogramming code + related to binning any value using the "reciprocal" /XXXX notation + has been fixed. + + - Added mutex locks for thread safety in ftgiou and ftfiou. + + - Added several Fortran wrappers to support image read/write when + 'fpixel' and 'nelements' are 8-byte integers. + + - Fixed bug which was adding spaces to some cases of long string key + value output. + +Version 4.1.0 - Feb 2022 + + - Calls to the zlib inflate() function in zcompress.c now handle + the Z_BUF_ERROR return value rather than exiting. + + - The SUBTRACTIVE_DITHER_2 option has been removed when using the + HCOMPRESS algorithm. + + - Updated fits_get_version function to return a float calculated + from 3 version fields. + + - Added handling of SBYTE_IMG and ULONGLONG_IMG types to the + fits_resize_img function. + + - Updates made to C/Fortran interfacing in cfortran.h and f77_wrap.h + specifically driven by new Mac/ARM architecture. + + - Fix to the fits_insert_col functions to handle columns with + TULONGLONG data. + +Version 4.0.0 - May 2021 + + - Removed separate directory for zlib/gzip code, and updated + configuration to check for zlib on the user's system (required). + When use of cURL is enabled, it may also pull in zlib such + that user applications may not need to link with it separately. + + - Changed version numbering to 3-field format. + + - Added new calculator functions SETNULL(x,y) to allow substitution of + NULL values into tables, and GTIOVERLAP() for calculating the amount + of GTI overlap exposure for a time bin. + + - Fix added for proper handling of string columns with zero repeat + count. + + - Fix to column filtering expressions which write #NULL values to + columns of type (J) format. + + - Fix to memory clearing when using polygon shapes in region files. + + - Fix to fits_str2time function so that it now flags a particular case + of bad syntax which was previously getting through. + + - In ffgclb and ffpclb (read/write byte columns), the "undocumented" + feature of being able to transfer columns 'A' string columnss as + byte arrays is now handled correctly, with improved error checking + via updates to ffgcprll. More documentation on string handling is + in cfitsio.tex. + + - Fix bug in 'colfilter' functionality. When performing a + column deletion of the form -COLNAM*, and multiple matches + existed, then none of the matches got deleted. Now the + first is deleted as expected. + + - Improved handling of corner case in ffpkn functions. + + - In ffgky, modified TULONG case to allow it to read unsigned + values greater than the 8-byte signed limit. + + - Fix to parsing of corner case of extended file syntax. + + - Major updates to CMake configuration. + +Version 3.49 - Aug 2020 + + - Fix to imcompress.c. It now turns off quantization if ZSCALE + and ZZERO columns are missing. Treatment will be the same as + if ZQUANTIZ were set to 'NONE', even if ZQUANTIZ is present + and set to something else. + + - Added mutex to fits_execute_template() function so that the + creation of files using ASCII templates will be thread safe. + + - In fpack when using -table flag, replaced warning message with a + more detailed description mentioning FITS format update. + + - Added flag to CMake builds to disable curl dependency. Also + only add CURL_LIBRARIES to CMake link target if curl is found. + + - Minor adjustment to download progress output. + +Version 3.48 - Mar 2020 + + - Now can handle parentheses in path names rather than automatically + interpreting them as output file specifiers. + + - Fixed bug in imcompress.c that wasn't properly handling conversion + between float and double types when reading from a gzip compressed + float or double image. + + - Fixed bug that was preventing use of bracket and parentheses symbols + in pathnames when opening multiple READWRITE files, even when + requesting no-extended-syntax usage. *This fix necessitates a + library interface version number change. + + - Fixed bug in ffmnhd / fits_movnam_hdu to properly handle wildcard + syntax. + + - Fixed bug in fits_open_extlist to handle filename[EXT] syntax + properly. The hdutype parameter may now be null. More documentaion + for this function is in cfitsio.tex. + + - Added new function fits_copy_hdutab to create a new table with the same + structure as an existing table. + + - fits_copy_col / ffcpcl handles long long integer data types more + natively to prevent precision loss. + + - histo.c routines now recognize integer columns that have been scaled by + TSCALn keywords and may be closer to floating point type. + + - Added backward compatibility for very old Rice compressed files which + were not using the ZVAL2 keyword in the way that later became standard. + + - Change made to cfitsio.pc.in to prevent forcing downstream libraries + to link against cfitsio's dependencies when using pkgconfig. + +Version 3.47 - May 2019 + + - Added set of drivers for performing ftps file transfers. + + - Tile sizes for compression may now be specified for any pair of + axes, where previously 2D tiles where limited to just X and y. + + - Fix to ffgsky and ffgkls functions for case of keyword with long + string values where the final CONTINUE statement ended with '&'. + If the final CONTINUE also contained a comment, it was being + repeated twice when passed back through the 'comm' argument. + + - Fix made to ffedit_columns() for case of multiple col filters + containing wildcards. Only the first filter was being searched. + + - fits_copy_rows (ffcprw) can now handle 'P'-type variable-length + columns. + + - Fix made to an obscure case in fits_modify_vector_len, where a + wrongly issued EOF error may occur. + + - Added internal fffvcl() function. + +Version 3.46 - Oct 2018 (Ftools release) + + - Improved the algorithm for ensuring no tile dimensions are smaller + than 4 pixels for HCOMPRESS compression. + + - Added new functions intended to assist in diagnosing (primarily + https) download issues: fits_show_download_progress, + fits_get_timeout, fits_set_timeout. + + - Added the '-O ' option to fpack, which previously existed only + for funpack. Also added fpack/funpack auto-removal of .bz2 suffix + equivalent to what existed for .gz. + + - For the fpack '-table' cases, warning message is now sent to stderr + instead of stdout. This is to allow users to pipe the results from + stdout in valid FITS format. (The warning message is otherwise placed + at the start of the FITS file and therefore corrupts it.) + + - Fix made to the '-P' file prefix option in funpack. + + - Added wildcard deletion syntax for columns, i.e. -COLNAM* will delete + the first matching column as always; -COLNAM*+ will delete all matching + columns (or none); exact symmetry with the keyword deletion syntax. + +Version 3.45 - May 2018 + + - New support for reading and writing unsigned long long datatypes. + This includes 'implicit datatype conversion' between the unsigned long + long datatype and all the other datatypes. + + - Increased the hardcoded NMAXFILES setting for maximum number of + open files from 1000 to 10000. + + - Bug fix to fits_calc_binning wrapper function, which wasn't filling + in the returned float variables. + + - Fixed a parsing bug for image subsection and column binning range + specifiers that was introduced in v3.44. + +Version 3.44 - April 2018 + + - This release primarily patches security vulnerabilities. We + strongly encourage this upgrade, particularly for those running + CFITSIO in web accessible applications. + + In addition, the following enhancements and fixes were made: + + - Enhancement to 'template' and 'colfilter' functionality. It is now + possible to delete multiple keywords using wildcard syntax. See + "Column and Keyword Filtering Specification" section of manual for + details. + + - histo.c uses double precision internally for all floating point + binning; new double-precision subroutines fits_calc_binningd(), + fits_rebin_wcsd(), and fits_make_histd(); existing + single-precision histogram functions still work but convert values + to double-precision internally. + + - new subroutine fits_copy_cols() / ffccls() to copy multiple columns + + - Fix in imcompress.c for HCOMPRESS and PLIO compression of unsigned + short integers. + + - Fix to fits_insert_card(ffikey). It had wrongly been capitalizing + letters that appeared before an '=' sign on a CONTINUE line. + +Version 3.43 - March 2018 + +The NASA security team requires the following warning to all users of +CFITSIO: + + ===== + The CFITSIO open source software project contains vulnerabilities + that could allow a remote, unauthenticated attacker to take control + of a server running the CFITSIO software. These vulnerabilities + affect all servers and products running the CFITSIO software. + + The CFITSIO team has released software updates to address these + vulnerabilities. There are no workarounds to address these + vulnerabilities. In all cases, the CFITSIO team is recommending an + immediate update to resolve the issues. + ===== + + - Fixed security vulnerabilities. + + - Calls to https driver functions in cfileio.c need to be macro- + protected by the HAVE_NET_SERVICES variable (as are the http and + ftp driver function calls). Otherwise CMake builds on native + Windows will fail since drvrnet.o is left empty. + + - Bug fix to ffmvec function. Should be resetting a local colptr + variable after making a call to ffiblk (which can reallocate Ftpr-> + tableptr). Originally reported by Willem van Straten. + + - Ignore any attempted request to not quantize an image before + compressing it if the image has integer datatype pixels. + + - Improved error message construction throughout CFITSIO. + +Version 3.42 - August 2017 (Stand-alone release) + + - added https support to the collection of drivers handled in cfileio.c + and drvrnet.c. This also handles the case where http transfers are + rerouted to https. Note that this enhancement introduces a dependency + on the libcurl development package. If this package is absent, CFITSIO + will still build but will not have https capability. + + - made fix to imcomp_init_table function in imcompress.c. It now writes + ZSIMPLE keyword only to a compressed image that will be placed in the + primary header. + + - fix made to fits_get_col_display_width for case of a vector column + of strings. + +Version 3.42 - March 2017 (Ftools release only) + + - in ftp_open_network and in ftp_file_exist, added code to repeatedly + attempt to make a ftp connection if the ftp server does not respond + to the first request. (some ftp servers don't appear to be 100% reliable). + + - in drvrnet.c added many calls to 'fclose' to close unneeded files, + to avoid exceeding the maximum allowed number of files that can be + open at once. + + - made substantial changes to the ftp_checkfile and http_checkfile routines + to streamline the process of checking for the existence of a .gz or .Z + compressed version of the file before opening the uncompressed file + (when using http or ftp to open the file). + + - modified the code in ftp_open_network to send "\r\n" as end-of-line + characters instead of just "\n". Some ftp servers (in particular, + at heasarc.gsfc.nasa.gov) now require both characters, otherwise the + network connection simply hangs. + + - modified the http_open_network routine to handle HTTP 301 or 302 redirects + to a FTP url. This is needed to support the new configuration on + the heasarc HTTP server which sometimes redirects http URLS to a ftp URL. + +Version 3.41 - November 2016 + + - The change made in version 3.40 to include strings.h caused problems on + Windows (and other) platforms, so this change was backed out. The reason + for including it was to define the strcasecmp and strcasencmp functions, so + as an alternative, new equivalent functions called fits_strcasecmp and + fits_strncasecmp have been added to CFITSIO.as a substitute. All the + previous calls to the str[n]casecmp functions have been changed to + now call fits_str[n]casecmp. In addition, the previously defined + ngp_strcasecmp function (in grparser.c) has been removed and the calls to + it have been changed to fits_strcasecmp. + + - The speed.c utility program was changed to correctly call + the gettimeofday function with a NULL second arguement. + +Version 3.40 - October 2016 + + - fixed a bug when writing long string keywords with the CONTINUE convention + which caused the CONTINUE'd strings to only be 16 characters long, instead + of using up all the available space in the 80-character header record. + + - fixed a missing 'defined' keyword in fitsio.h. + + - replaced all calls to strtok (which is not threadsafe) with a new ffstrtok + function which internally calls the threadsafe strtok_r function. One + byproduct of this change is that must also be included + in several of the C source code files. + + - modified the ffphbn function in putkey.c to support TFORM specifiers that + use lowercase 'p' (instead of uppercase) when referring to a variable-length + array column. + + - modified the lexical parser in eval.y and eval_y.c to support bit array + columns (with TFORMn = 'X') with greater than 256 elements. Fix to bitcmp + function: The internal 'stream' array is now + allocated dynamically rather than statically fixed at size 256. + This was failing when users attempted a row filtering of a bitcol + that was wider than 256X. In bitlgte, bitand, and bitor functions, replaced + static stream[256] array allocation with dynamic allocation. + + - modified the ffiter function in putcol.c to fix a problem which could + cause the iterator function to incorrectly deal with null values. This + only affected TLONG type columns in cases where sizeof(long) = 8, as well + as for TLONGLONG type columns. + + - Fix made to uncompress2mem function in zcomprss.c for case where output + uncompressed file expands to over the 2^32 (4Gb) limit. It now + checks for this case at the start, and implements a 4Gb paging + system through the output buffer. The problem was specifically + caused by the d_stream.avail_out member being of 4-byte type uInt, + and thus unable to handle any memory position values above 4Gb. + + - fixed a bug in fpackutil.c when using the -i2f (integer to float) option + in fpack to compress an integer image that is scaled with non-default values + for BSCALE and BZERO. This required an additional call to ffrhdu to reset + the internal structures that describe the input FITS file. + + - modified fits_uncompress_table in imcompress.c to silently ignore the + ZTILELEN keyword value if it larger than the number of rows in the table + + - Tweak strcasecmp/strncasecmp ifdefs to exclude 64-bit MINGW + environment, as it does not lack those functions. (eval_l.c, + fitsio2.h) + + - CMakeLists.txt: Set M_LIB to "" for MINGW build environment (in + addition to MSVC). + + - Makefile.in: Add *.dSYM (non-XCode gcc leftovers on Macs) to + clean list. Install libs by name rather than using a wildcard. + + - configure: Fix rpath token usage for XCode vs. non-XCode gcc on Macs. + + +Version 3.39 - April 2016 + + - added 2 new routines suggested by Eric Mandel: + ffhisto3 is similar to ffhisto2, except that it does not close the + original file. + fits_open_extlist is similar to fits_open_data except that it opens + the FITS file and then moves to the first extension in the user-input + list of 'interesting' extensions. + + - in ffpsvc and ffprec, it is necessary to treat CONTINUE, COMMENT, HISTORY, + and blank name keywords as a special case which must be treated differently + from other keywords because they have no value field and, by definition, + have keyword names that are strictly limited in length. + + - added the Fortran wrapper routines for the 2 new string keyword reading + routines (FTGSKY and FTGKSL), and documented all the routines in the + FITSIO and CFITSIO users guides. + + - in ffinttyp, added explicit initialization of the input 'negative' + argument to 0. + + - added new routine to return the length of the keyword value string: + fits_get_key_strlen / ffgksl. + This is primarily intended for use with string keywords + that use the CONTINUE convention to continue the + value over multiple header records, but this routine can be used + to get the length of the value string for any type keyword. + + - added new routine to read string-valued keywords: + fits_read_string_key / ffgsky + This routine supports normal string keywords as well as long string + keywords that use the CONTINUE convention. In many cases this routine + may be more convenient to use then the older fits_read_key_longstr + routine. + + - changed the prototype of fits_register_driver in fitsio2.h so that the + pointer definition argument does not have the same name as the pointer + itself (to work around a bug in the pgcc compiler). + + - added the missing FTDTDM fortran wrapper definition to f77_wrap3.c. + + - modified Makefile.in and configure.in to add LDFLAGS_BIN for task linker + flages, which will be the same as LDFLAGS except on newer Mac OS X where + an rpath flag is added. + + - modified Makefile.in to add a new "make utils" command which will build + fpack, funpack, cookbook, fitscopy, imcopy, smem, speed, and testprog. + These programs will be installed into $prfix/bin. + + - fixed a bug when attempting to modify the values in a variable-length + bit ("X") column in a binary table. + + - reinstated the ability to write HIERARCH keywords that contain characters + that would not be allowed in a normal 8-character keyword name, which had + been disabled in the previous release. + +Version 3.38 - February 2016 + + - CRITICAL BUG FIX: + The Intel 15 and 16 compilers (and potentially other compilers) may silently + produce incorrect assembly code when compiling CFITSIO with the -O2 (or + higher) optimization flag. In particular, this problem could cause CFITSIO + to incorrectly read the values of arrays of 32-bit integers in a FITS file + (i.e., images with BITPIX = 32 or table columns with TFORM = 'J') when the + array is being read into a 'long' integer array in cases where the long + array elements are 8 bytes long. + + One way to test if a particular system is affected by this problem is to + compile CFITSIO V3.37 (or earlier) with optimization enabled, and then + compare the output of the testprog.c program with the testprog.out file + that is distributed with CFITSIO. If there are any differences in the + files, then this system might be affected by this bug. Further tests + should be performed to determine the exact cause. + + The root cause of this problem was traced to the fact that CFITSIO was + aliasing an array of 32-bit integers and an array of 64-bit integers to the + same memory location in order to obtain better data I/O efficiency when + reading FITS files. When CFITSIO modified the values in these arrays, it + was essential that the processing be done in strict sequential order from + one end of the array to the other end, as was implicit in the C code + algorithm. In this case, however, the compiler adopted certain loop + optimization techniques that produced assembly code that violated this + assumption. Technically, the CFITSIO code violates the "strict aliasing" + assumption in ANSI C99, therefore the affected CFITSIO routines have been + modified so that the aliasing of different data types to the same memory + location no longer occurs. + + - fixed problem in configure and configure.in which caused the programs that + are distributed with CFITSIO (most notably, fack and funpack) to be build + without using any compiler optimization options, which could make them + run more slowly than expected. + + - in imcompress.c, fixed bug where the rowspertile variable (declared as 'long') + was mistakenly declared as a TLONGLONG variable in a call to fits_write_key. + This could have caused the ZTILELEN keyword to be written incorrectly in + the header of tile-compressed FITS tables on systems where sizeof(long) = 4. + + - in imcompress.c, implemented a new set of routines that safely convert + shorter integer arrays into a longer integer arrays (e.g. short to int) + where both arrays are aliased to the same memory location. These + special routines were needed to guard against certain compiler optimization + techniques that could produce incorrect code. + + - modified the 4 FnNoise5_(type) routines in quantize.c to correctly + count the number of non-null pixels in the input array. Previously the + count could be inaccurate if the image mainly consisted of null pixels. + This could have caused certain floating point image tiles to be + quantized during the image compression process, when in fact the tile + did not satisfy all the criteria to be safely quantized. + + - in imcomp_copy_comp2img, added THEAP to the list of binary table + keywords that may be present in the header of a compressed image + and should not be copied to the uncompressed image header. + + - modified fits_copy_col to check that when copying a vector column, the + vector length in the output column is the same as in the input column. + Also modified the code to support the case where a column is being copied + to an earlier position in the same table (which shifts the input column + over 1 space). + + - added configure option (--with-bzip2) to support reading bzip2 compressed + FITS files. This also required modifications to drvrmem.c and drvrfile.c + This depends on having the bzlib library installed on the + local machine. This patch was submitted by Dustin Lang. + + - replaced calls to 'memcpy' by 'memmove' in getcolb.c, getcold.c, + getcole.c, and getcoli.c to support cases where the 2 memory areas + overlap. (submitted by Aurelien Jarno) + + - modified the FITS keyword reading and writing routines to potentially + support keywords with names longer than 8-characters. This was implemented + in anticipation of a new experimental FITS convention which allows longer + keyword names. + + - in fits_quantize_double in quantize.c, test if iseed == N_RANDOM, + to avoid the (unlikely) possibility of overflowing the random number + array bounds. (The corresponding fits_quantize_float routine already + performed this test). + + - in the FnNoise5_short routine in quantize.c, change the first 'if' + statement from "if (nx < 5)" to "if )nx < 9)", in order to support the + (very rare) case where the tile is from 5 to 8 pixels wide. Also make + the same change in the 3 other similar FnNoise5_* routines. + + - in the qtree_bitins64 routine in fits_hdecompress.c, must declare the + plane_val variable as 'LONGLONG' instead of int. This bug could have + caused integer overflow errors when uncompressing integer*4 images that + had been compressed with the Hcompress algorithm, but only in cases + where the image contains large regions of pixels whose values are close + to the maximum integer*4 value of 2**31. + + - in fits_hcompress.c, call the calloc function instead of malloc when + allocating the signbits array, to eliminate the need to individually + set each byte to zero. + + - in the ffinit routine, and in a couple other routines that call ffinit, + initialize the *fptr input parameter to NULL, even if the input + status parameter value is greater than zero. This helps prevent + errors later on if that fptr value is passed to ffclos. + + - modified ftcopy, in edithdu.c, to only abort if status > 0 rather + than if status != 0. This had caused a problem in funpack in rare + circumstances. + + - in imcompress.c changed all the calls to ffgdes to ffgdesll, to support + compressed files greater than 2.1 GB in size. + + - fixed bug in ffeqtyll when it is called with 4th and 5th arguments + set to NULL. + + - in fitsio.h, added the standard C++ guard around the declaration of the + function fits_read_wcstab. (reported by Tammo Jan Dijkema, Astron.) + + - in fitsio.h, changed the prototype variable name "zero" to "zeroval" to + avoid conflict in code that uses a literal definition of 'zero' to mean 0. + + - tweaked Makefile.in and configure.in to use LDFLAGS instead of CFLAGS + for linking, use Macros for library name, and let fpack and funpack + link with shared library. + + - modified an 'ifdef' statement in cfileio.c to test for '__GLIBC__' + instead of 'linux' when initializing support for multi-threading. + + - modified ffeqtyll to return an effective column data type of TDOUBLE + in the case of a 'K' (64-bit integer) column that has non-integer + TSCALn or TZEROn keywords. + + - modified ffgcls (which returns the value in a column as a formatted string) + so that when reading a 'K' (TLONGLONG) column it returns a long long integer + value if the column is not scaled, but returns a double floating point + value if the column has non-integer TSCALn or TZEROn values. + + - modified fitsio.h to correctly define "OFF_T long long" when using + the Borland compiler + + - converted the 'end of line' characters in simplerng.c file to the unix + style, instead of PC DOS. + + - updated CMakeLists.txt CMake build file which is primarily used to + build CFITSIO on Windows machines. + + - modified fits_get_keyclass to recognize ZQUANTIZ and ZDITHER0 as + TYP_CMPRS_KEY type keywords, i.e., keywords used in tile compressed + image files. + + - added test to see if HAVE_UNISTD_H is defined, as a condition for + including unistd.h in drvrfile.c drvrnet.c, drvrsmem.c, and group.c. + + - modified the CMakelist.txt file to fix several issues (primarily for + building CFITSIO on Windows machines).. + + - fixed bug when reading tile-compressed images that were compressed with + the IRAF PLIO algorithm. This bug did not affect fpack or funpack, but + other software that reads the compressed image could be affected. The + bug would cause the data values to be offset by 32768 from the actual + pixel values. + +Version 3.37 - 3 June 2014 + + - replaced the random Gaussian and Poissonian distribution functions with + new code written by Craig Markwardt derived from public domain C++ functions + written by John D Cook. + + - patched fitsio2.h to support CFITSIO on AArch64 (64-bit ARM) + architecture (both big and little endian). Supplied by + Marcin Juszkiewicz and Sergio Pascual Ramirez, with further update + by Michel Normand. + + - fixed bug in fpackutil.c that caused fpack to exit prematurely if + the FZALGOR directive keyword was present in the HDU header. + +Version 3.36 - 6 December 2013 + + - added 9 Dec: small change to the fileseek function in drvrfile.c to + support large files > 2 GB when building CFITSIO with MinGW on Windows + + - reorganized the CFITSIO code directory structure; added a 'docs' + subdirectory for all the documentation, and a 'zlib' directory + for the zlib/gzip file compression code. + + - made major changes to the compression code for FITS binary table + to support all types of columns, including variable-length arrays. + This code is mainly used via the fpack and funpack programs. + + - increased the number of FITS files that can be opened as one + time to 1000, as defined by NMAXFILES in fitsio2.h. + + - made small configuration changes to configure.in, configure, + fitsio.h, and drvrfile.c to support large files (64-bit file + offsets} when using the mingw-w64 compiler (provided by + Benjamin Gilbert). + + - made small change to fits_delete_file to more completely ignore + any non-zero input status value. + + - fixed a logic error in a 'if' test when parsing a keyword name + in the ngp_keyword_is_write function in grparser.c (provided + by David Binderman). + + - when specifying the image compression parameters as part of the + compressed image file name (using the "[compress]" qualifier + after the name of the file), the quantization level value, if + specified, was not being recognized by the CFITSIO compression + routines. The image would always be compressed with the default + quantization level of 4.0, regardless of what was specified. This + affected the imcopy program, and potentially other user-generated + application programs that used this method to specify the + compression parameters. This bug did not affect fpack or + funpack. This was fixed in the imcomp_get_compressed_image_par + routine in the imcompress.c file. (reported by Sean Peters) + + - defined a new CFITS_API macro in fitsio.h which is used to export the + public symbols when building CFITSIO on Windows systems with CMake. This + works in conjunction with the new Windows CMake build procedure that + is described in the README.win32 file. This complete revamping of the + way CFITSIO is built under Windows now supports building 64-bit + versions of the library. Thanks to Daniel Kaneider (Luminance HDR + Team) for providing these new CMake build procedures. + + - modified the way that the low-level file_create routine works when + running in the Hera environment to ensure that the FITS file that is + created is within the allow user data disk area. + + - modified fits_get_compression_type so that it does not return an error + if the HDU is a normal FITS IMAGE extension, and is not a tile-compressed + image. + + - modified the low-level ffgcl* and ffpcl* routines to ensure that they + never try ro read or write more than 2**31 bytes from disk at one time, + as might happen with very large images, to avoid integer overflow errors. + Fix kindly provided by Fred Gutsche at NanoFocus AG (www.nanofocus.de). + + - modified Makefile.in so that doing 'make distclean' does not delete + new config.sub and config.guess files that were recently added. + + - adopted a patch from Debian in zcompress.c to "define" the values of + GZBUFSIZE and BUFFINCR, instead of exporting the symbols as 'int's. + +Version 3.35 - 26 June 2013 (1st beta release was on 24 May) + + - fixed problem with the default tile size when compressing images with + fpack using the Hcompress algorithm. + + - fixed returned value ("status" instead of "*status") + + - in imcompress.c, declared some arrays that are used to store the dimensions + of the image from 'int' to 'long', to support very large images (at least + on systems where sizeof(long) = 8), + + - modified the routines that convert a string value to a float or double + to prevent them from returning a NaN or Inf value if the + string is "NaN" or "Inf" (as can happen with gcc implementation of the + strtod function). + + - removed/replaced the use of the assert() functions when locking or + unlocking threads because they did not work correctly if NDEBUG is + defined. + + - made modifications to the way the command-line file filters are parsed to + 1) remove the 1024-character limit when specifying a column filter, + 2) fixed a potential character buffer-overflow risk in fits_get_token, and + 3) improved the parsing logic to remove any possible of confusing + 2 slash characters ("//") in the string as the beginning of a + comment string. + + - modified configure and Makefile.in so that when building CFITSIO + as a shared library on linux or Mac platforms, it will use the SONAME + convention to indicate whether each new release of the CFITSIO + library is binary-compatible with the previous version. Application + programs that link with the shared library will not need to be + recompiled as long as the versions are compatible. In practice, + this means that the shared library binary file that is created (on + Linux systems) will have a name like 'libcfitsio.so.I.J.K', where I is the + SONAME version number, J is the major CFITSIO version number (e.g. 3), + and K is the minor CFITSIO version number (e.g., 34). Two link + files will also be created such that + libcfitsio.so -> libcfitsio.so.I, and + libcfitsio.so.I -> libcfitsio.I.J.K + Application programs will still run correctly with the new version of + CFITSIO as long as the 'I' version number remains the same, but the + applications will fail to run if the 'I' number changes, thus alerting + the user that the application must be rebuilt. + + - fixed bug in fits_insert_col when computing the new table row width + when inserting a '1Q' variable length array column. + + - modified the image compression routines so that the output compressed + image (stored in a FITS binary table) uses the '1Q' variable length + array format (instead of '1P') when the input file is larger than 4 GB. + + - added support for "compression directive" keywords which indicate how + that HDU should be compressed (e.g., which compression algorithm to use, + what tiling pattern to use, etc.). The values of these keywords will + override the compression parameters that were specified on the command + line when running the fpack FITS file compression program. + + - globally changed the variable and/or subroutine name "dither_offset" + to "dither_seed" and "quantize_dither" to "quantize_method" so + that the names more accurately reflects their purpose. + + - added support for a new SUBTRACTIVE_DITHER_2 method when compressing + floating point images. The only difference with the previous method + is that pixels with a value exactly equal to 0.0 will not be dithered, + and instead will be exactly preserved when the image is compressed. + + - added support for an alias of "RICE_ONE" for "RICE_1" as the value + of the ZCMPTYPE keyword, which gives the name of the image compression + algorithm. This alias is used if the new SUBTRACTIVE_DITHER_2 option + is used, to prevent old versions of funpack from creating a corrupted + uncompressed image file. Only newer versions of funpack will recognize + this alias and be able to uncompress the image. + + - made performance improvement to fits_read_compressed_img so that + when reading a section of an compressed image that includes only + every nth pixel in some dimension, it will only uncompressed a tile + if there are actually any pixels of interest in that tile. + + - fixed several issues with the beta FITS binary table compression code + that is used by fpack: added support for zero-length vector columns, + made improvements to the output report when using the -T option in fpack, + changed the default table compression method to 'Rice' instead of + 'Best', and now writes the 'ZTILELEN' keyword to document the number + of table rows in each tile. + + - fixed error in ffbinit in calculating the total length of the binary + table extension if the THEAP keyword was used to override the + default starting location of the heap. + +Version 3.34 - 20 March 2013 + + - modified configure and configure.in to support cross-compiled cfitsio + as a static library for Windows on a Linux platform using MXE + (http://mxe.cc) - a build environment for mingw32. (contributed by + Niels Kristian Bech Jensen) + + - added conditional compilation statementsfor the mingw32 environment in + drvrfile.c because mingw32 does not include the ftello and fseeko functions. + (contributed by Niels Kristian Bech Jensen) + + - fixed a potential bug in ffcpcl (routine to copy a column from one table + to another table) when dealing with the rare case of a '0X' column (zero + length bit column). + + - fixed an issue in the routines that update or modify string-valued + keyword values, as a result of the change to ffc2s in the previous + release. These routines would exit with a 204 error status if the + current value of the keyword to be updated or modified is null. + + - fixed typo in the previous modification that was intended to ignore + numerical overflows in Hcompress when decompressing an image. + + - moved the 'startcol' static variable out of the ffgcnn routine and + instead added it as a member of the 'FITSfile' structure that is defined + in fitsio.h. This removes a possible race condition in ffgcnn in + multi-threaded environments. + +Version 3.33 - 14 Feb 2013 + + - modified the imcomp_decompress_tile routine to ignore any numerical + overflows that might occur when using Hcompress to decompress the + image. If Hcompress is used in its 'lossy' mode, the uncompressed + image pixel values may slightly exceed the range of an integer*2 + variable. This is generally of no consequence, so we can safely ignore + any overflows in this case and just clip the values to the legal range. + + - the default tiling pattern when writing a tile-compressed image + has been changed. The old behavior was to compress the whole image + as one single large tile. This is often not optimal when dealing + with large images, so the new default behavior is to treat each + row of the image as one tile. This is the same default behavior + as in the standalone fpack program. The default tile size can + be overridden by calling fits_set_tile_dim. + + - fixed bug that resulted in a corrupted output FITS image when + attempting to write a float or double array of values to a + tile-compressed integer data type image. CFITSIO does not support + implicit data type conversion in this case and now correctly + returns an appropriate error status. + + - modified ricecomp.c to define the nonzero_count lookup table as an + external variable, rather then dynamically allocating it within the + 3 routines that use it. This simplifies the code and eliminates the + need for special thread locking and unlocking statements. (Thanks to + Lars Kr. Lundin for this suggestion). + + - modified how the uncompressed size of a gzipped file is computed in the + mem_compress_open routine in drvrmem.c. Since gzip only uses 4 bytes + in the compressed file header to store the original file size, one may + need to apply a modulo 2^32 byte correction in some cases. The logic + here was modified to allow for corner cases (e.g., very small files, and + when running on 32-bit platforms that do not support files larger than + 2^31 bytes in size). + + - added new public routine to construct a 80 keyword record from the 3 input + component strings, i.e, the keyword name string, the value string, and + the comment string: fits_make_key/ffmkky. (This was already an undocumented + internal routine in previous versions of CFITSIO). + + - modified ffc2s so that if the input keyword value string is a null string, + then it will return a VALUE_UNDEFINED (204) status value. This makes it + consistent with the behavior when attempting to read a null keyword + (which has no value) as a logical or as a number (which also returns + the 204 error). This should only affect cases where the header keyword + does not have an equal sign followed by a space character in columns 9 + and 10 of the header record. + + - Changed the "char *" parameter declarations to "const char *" in many + of the routines (mainly the routines that modify or update keywords) to + avoid compiler warnings or errors from C++ programs that tend to be more + rigorous about using "const char *" when appropriate. + + - added support for caching uncompressed image tiles, so that the tile does + not need to be uncompressed again if the application program wants + to read more data from the same tile. This required changes to the + main FITS file structure that is defined in fitsio.h, as well as + changes to imcompress.c. + + - enhanced the previous modification to drvrfile.c to handle additional user + cases when running in the HEASARC's Hera environment. + +Version 3.32 - Oct 2012 + + - fixed flaw in the way logical columns (TFORM = 'L') in binary tables + were read which caused an illegal value of 1 in the column to be interpreted + as a 'T' (TRUE) value. + + - extended the column filtering syntax in the CFITSIO file name parser to + enable users and scripts to append new COMMENT or HISTORY keyword into the + header of the filtered file (provided by Craig Markwardt). For example, + fcopy "infile.fits[col #HISTORY='Processed on 2012-10-05']" outfile.fits + will append this header keyword: "HISTORY Processed on 2012-10-05" + + - small change to the code that opens and reads an ASCII region file to + return an error if the file is empty. + + - fixed obscure sign propagation error when attempting to read the + uncompressed size of a gzipped FITS file. This resulted in a memory + allocation error if the gzipped file had an uncompressed file + size between 2^31 and 2^32 bytes. Fix supplied by Gudlaugur Johannesson + (Stanford). + +Version 3.31 - 18 July 2012 + + - enhanced the CFITSIO column filtering syntax to allow the comma, in addition + to the semi-colon, to be used to separate clauses, for example: + [col X,Y;Z = max(X,Y)]. This was done because users are not allowed to + enter the semi-colon character in the on-line Hera data processing + system due to computer security concerns. + + - enhanced the CFITSIO extended filename syntax to allow specifying image + compression parameters (e.g. '[compress Rice]') when opening an existing + FITS file with write access. The specified compression parameters will + be used by default if more images are appended to the existing file. + + - modified drvrfile.c to do additional file security checks when CFITSIO + is running within the HEASARC's Hera software system. In this case + CFITSIO will not allow FITS files to be created outside of the user's + individual Hera data directory area. + + - fixed an issue in fpack and funpack on Windows machines, caused by + the fact that the 'rename' function behaves differently on Windows + in that it does not clobber an existing file, as it does on Unix + platforms. + + - fixed bug in the way byte-swapping was being performed when writing + integer*8 null values to an image or binary table column. + + - added the missing macro definition for fffree to fitsio.h. + + - modified the low level table read and write functions in getcol*.c and + putcol*.c to remove the 32-bit limitation on the number of elements. + These routines now support reading and writing more than 2**31 elements + at one time. Thanks to Keh-Cheng Chu (Stanford U.) for the patch. + + - modified Makefile.in so that the shared libcfitsio.so is linked against + pthreads and libm. + +Version 3.30 - 11 April 2012 + + Enhancements + + - Added new routine called fits_is_reentrant which returns 1 or 0 depending on + whether or not CFITSIO was compiled with the -D_REENTRANT directive. This can + be used to determine if it is safe to use CFITSIO in multi-threaded programs. + + - Implemented much faster byte-swapping algorithms in swapproc.c based on code + provided by Julian Taylor at ESO, Garching. These routines significantly + improve the FITS image read and write speed (by more than a factor of 2 in + some cases) on little-endian machines (e.g., Linux and Microsoft Windows and + Macs running on x86 CPUs) where byte-swapping is required when reading and + writing data in FITS files. This has no effect on big-endian machines + (e.g. Motorola CPUs and some IBM systems). Even faster byte-swapping + performance can be achieved in some cases by invoking the new "--enable-sse2" + or "--enable-ssse3" configure options when building CFITSIO on machines that + have CPUs and compilers that support the SSE2 and SSSE3 machine instructions. + + - added additional support for implicit data type conversion in cases where + the floating point image has been losslessly compressed with gzip. The + pixels in these compressed images can now be read back as arrays of short, + int, and long integers as well as single and double precision floating-point. + + - modified fitsio2.h and f77_wrap.h to recognize IBM System z mainframes by + testing if __s390x__ or __s390__ is defined. + + - small change to ffgcrd in getkey.c so that it supports reading a blank + keyword (e.g., a keyword whose name simply contains 8 space characters). + + Bug Fixes + + - fixed a bug in imcomp_decompress_tile that caused the tile-compressed image + to be uncompressed incorrectly (even though the tile-compressed image itself + was written correctly) under the following specific conditions: + - the original FITS image has a "float" datatype (R*4) + - one or more of the image tiles cannot be compressed using the standard + quantization method and instead are losslessly compressed with gzip + - the pixels in these tiles are not all equal to zero (this bug does + affect tiles where all the pixels are equal to zero) + - the program that is reading the compressed image uses CFITSIO's + "implicit datatype conversion" feature to read the "float" image + back into an array of "double" pixel values. + If all these conditions are met, then the returned pixel values in the + affected image tiles will be garbage, with values often ranging + up to 10**34. Note that this bug does not affect the fpack/funpack + programs, because funpack does not use CFITSIO's implicit datatype + conversion feature when uncompressing the image. + +Version 3.29 - 2 December 2011 + + Enhancements + + - modified Makefile.in to allow configure to override the lib and include + destination directories. + + - added (or restored actually) support for tile compression of 1-byte integer + images in imcomp_compress_tile. Support for that data type was overlooked + during recent updates to this routine. + + - modified the fits_get_token command-line parsing routine to perform more + rigorous checks to determine if the token can be interpreted as a number + or not. + + - made small modification to fpack.c to not allow the -i2f option (convert + image from integer to floating point) with the "-g -q 0" option (do lossless + gzip compression). It is more efficient to simply use the -g option alone. + + - made modifications to fitsio.h and drvrfile.c to support reading and + writing large FITS files (> 2.1 GB) when building CFITSIO using + Microsoft Visual C++ on Windows platforms. + + - added new WCS routine (ffgicsa) which returns the WCS keyword values + for a particular WCS version ('A' - 'Z'). + + Bug Fixes + + - fixed a problem with multi-threaded apps that open/close FITS files + simultaneously by putting mutex locks around the call to + fits_already_open and in fits_clear_Fptr. + + - fixed a bug when using the 'regfilter' function to select a subset of the + rows in a FITS table that have coordinates that lie within a specified + spatial region on the sky. This bug only affects the rarely used panda + (and epanda and bpanda) region shapes in which the region is defined by + the intersection of an annulus and a pie-shaped wedge. The previous code + (starting with version 3.181 of CFITSIO where support for the panda region + was first introduced) only worked correctly if the 2 angles that define + the wedge have values between -180 and +180. If not, then fewer rows than + expected may have been selected from the table. + + - fixed the extended filename parser so that when creating a histogram by + binning 2 table columns, if a keyword or column name is given as the + weighting factor, then the output histogram image will have a floating + point datatype, not the default integer datatype as is the case when no + weight is specified (e.g. with a filename like + "myfile.fits[bin x,y; weight_column]" + + - added fix to the code in imcompress.c to work around a problem with + dereferencing the value of a pointer, in cases where the address of + that pointer has not been defined (e.g., the nulval variable). + + - modified the byte shuffling algorithm in fits_shuffle_8bytes to work + around a strange bug in the proprietary SunStudioExpress C compiler + under OpenSolaris. + + - removed spurious messages on the CFITSIO error stack when opening a + FITS file with FTP (in drvrnet.c); + +Version 3.28 - 12 May 2011 + + - added an enhancement to the tiled-image compression method when compressing + floating-point image using the standard (lossy) quantization method. In + cases where an image tile cannot be quantized, The floating-point pixel values + will be losslessly compressed with gzip before writing them to the tile- + compressed file. Previously, the uncompressed pixel values would have + been written to the file, which obviously requires more disk space. + + - made significant internal changes to the structure of the tile compression + and uncompression routines in imcompress.c to make them more modular and + easier to maintain. + + - modified configure.in and configure to force it to build a Universal + binary on Mac OS X. + + - modified the ffiter function in putcol.c to properly clean up allocated + memory if an error occurs. + + - in quantize.c, when searching for the min and max values in a float array, + initialize the max value to -FLT_MAX instead of FLT_MIN (and similarly + for double array). + +Version 3.27 - 3 March 2011 + + Enhancements + + - added new routines fits_read_str and fits_delete_str which read or + delete, respectively, a header keyword record that contains a specified + character string. + + - added a new routine called fits_free_memory which frees the memory + that fits_read_key_longstr allocated for the long string keyword value. + + - enhanced the ffmkky routine in fitscore.c to not put a space before the + equals sign when writing long string-valued keywords using the ESO + HIERARCH keyword convention, if that extra character is needed to + fit the length of the keyword name + value string within the 80-character + FITS keyword record. + + - made small change to fits_translate_keyword to support translation of + blank keywords (where the name = 8 blank characters) + + - modified fpack so that it uses the minimum of the 2nd, 3rd, and 5th order + MAD noise values when quantizing and compressing a floating point image. + This is more conservative than just using the 3rd order MAD value alone. + + - added new routine imcomp_copy_prime2img to imcompress.c that is used by + funpack to copy any keywords that may have been added to the primary + array of the compressed image file (a null image) back into the header of + the uncompressed image. + + - enhanced the fits_quantize_float and fits_quantize_double routines in + quantize.c to also compress the tile if it is completely filled with + null values. Previously, this type of tile would have been written + to the output compressed image without any compression. + + - enhanced imcomp_decompress_tile to support implicit datatype conversion + when reading a losslessly compressed (with gzip) real*4 image into an + array of real*8 values. + + - in imcompress.c, removed possible attempt to free memory that had not + been allocated. + + +Version 3.26 - 30 December 2010 + + Enhancements + + - defined 2 new macros in fitsio.h: + #define CFITSIO_MAJOR 3 + #define CFITSIO_MINOR 26 + These may be used within other macros to detect the CFITSIO + version number at compile time. + + - modified group.c to initialize the output URL to a null string in + fits_url2relurl. Also added more robust tests to see if 2 file + pointers point to the same file. + + - enhanced the template keyword parsing code in grparser.c to support + the 'D' exponent character in the ASCII representation of floating + point keyword values (as in TVAL = 1.23D03). Previously, the parser + would have written this keyword with a string value (TVAL = '1.23D03'). + + - modified the low-level routines that write a keyword record to a FITS + header so that they silently replace any illegal characters (ASCII + values less than 32 or greater than 126) with an ASCII space character. + Previously, these routines would have returned with an error when + encountering these illegal characters in the keyword record (most commonly + tab, carriage return, and line feed characters). + + - made substantial internal changes to imcompress.c in preparation for + possible future support for compression methods for FITS tables analogous + to the tiled image compression method. + + - replaced all the source code in CFITSIO that was distributed under the + GNU General Public License with freely available code. In particular, + the gzip file compression and uncompression code was replaced by the + zlib compression library. Thus, beginning with this version 3.26 of CFITSIO, + other software applications may freely use CFITSIO without necessarily + incurring any GNU licensing requirement. See the License.txt file for + the CFITSIO licensing requirements. + + - added support for using cfitsio in different 'locales' which use a + comma, not a period, as the decimal point character in ASCII + representation of a floating point number (e.g., France). This + affects how floating point keyword values and floating point numbers + in ASCII tables are read and written with the 'printf' and 'strtod' + functions. + + - added a new utility routine called fits_copy_rows/ffcprw that copies + a specified range of rows from one table to another. + + - enhanced the test for illegal ASCII characters in a header (fftrec) to + print out the name of the offending character (e.g TAB or Line Feed) as + well as the Hex value of the character. + + - modified ffgtbc (in fitscore.c) to support nonstandard vector variable + length array columns in binary tables (e.g. with TFORMn = 2000PE(500)'). + + - modified the configure file to add "-lm" when linking CFITSIO on + Solaris machines. + + - added new routine, fits_get_inttype, to parse an integer keyword value + string and return the minimum integer datatype (TBYTE, TSHORT, TLONG, + TLONGLONG) required to store the integer value. + + - added new routine, fits_convert_hdr2str, which is similar to fits_hdr2str + except that if the input HDU is a tile compressed image (stored + in a binary table) then it will first convert that header back to + that of a normal uncompressed FITS image before concatenating the header + keyword records. + + - modified the file template reading routine (ngp_line_from_file in + grparser.c) so that it ignores any carriage return characters (\r) + in the line, that might be present, e.g. if the file was created on a + Windows machine that uses \r\n as end of line characters. + + - modified the ffoptplt routine in cfileio.c to check if the PCOUNT + keyword in the template file has a non-zero value, and if so, resets + it to zero in the newly created file. + + Bug Fixes + + - fixed a bug when uncompressing floating-point images that contain Nan + values on some 64-bit platforms. + + - fixed a bug when updating the value of the CRPIXn world coordinate + system keywords when extracting a subimage from larger FITS image, using the + extended CFITSIO syntax (e.g. myimage[1:500:2, 1:500:2]). This bug only + affects cases where the pixel increment value is not equal to 1, and caused + the coordinate grid to be shifted by between 0.25 pixels (in the case of + a pixel increment of 2) and 0.5 pixels (for large pixel increment values). + + - fixed a potential string buffer overflow error in the ffmkls routine + that modifies the value and comment strings in a keyword that uses + the HEASARC long string keyword convention. + + - fixed a bug in imcompress.c that could cause programs to abort on 64-bit + machines when using gzip to tile-compress images. Changed the declaration + of clen in imcomp_compress_tile from int to size_t. + +Version 3.25 - 9 June 2010 + + - fixed bug that was introduced in version 3.13 that broke the ability + to reverse an image section along the y-axis with an image section + specifier like this: myimage.fits[*,-*]. This bug caused the output + image to be filled with zeros. + + - fixed typo in the definition of the ftgprh Fortran wrapper routine + in f77_wrap3.c. + + - modified the cfitsio.pc.in configuration file to make the lib path + a variable instead of hard coding the path. The provides more + flexibility for projects such as suse and fedora when building CFITSIO. + + - fixed bug in imcomp_compress_tile in imcompress.c which caused + null pixel values to be written incorrectly in the rare case where + the floating-point tile of pixels could not be quantized into integers. + + - modified imcompress.c to add a new specialized routine to uncompress + an input image and then write it to a output image on a tile by tile basis. + This appears to be faster than the old method of uncompressing the + whole image into memory before writing it out. It also supports + large images with more than 2**31 pixels. + + - made trivial changes to 2 statements in drvrfile.c to suppress + nuisance compiler warnings. + + - some compilers define CLOCKS_PER_SEC as a double instead of an integer, + so added an explicit integer type conversion to 2 statements in + imcompress.c that used this macro. + + - removed debugging printf statements in drvrnet.c (15 July) + +Version 3.24 - 26 January 2010 + + - modified fits_translate_keywords so that it silently ignores any + illegal ASCII characters in the value or comment fields of the input + FITS file. Otherwise, fpack would abort without compressing input + files that contained this minor violation of the FITS rules. + + - added support for Super H cpu in fitsio2.h + + - updated funpack to correctly handle the -S option, and to use a + more robust algorithm for creating temporary output files. + + - modified the imcomp_compress_tile routine to support the NOCOMPRESS + debugging option for real*4 images. + +Version 3.23 - 7 January 2010 + + - reduced the default value for the floating point image quantization + parameter (q) from 16 to 4. This parameter is used when tile compressing + floating point images. This change will increase the average compression + ratio for floating point images from about 4.6 to about 6.5 without losing + any significant information in the image. + + - enhanced the template keyword parsing routine to reject a header + template string that only contains a sequence of dashes. + + - enhanced the ASCII region file reading routine to allow tabs as well + as spaces between fields in the file. + + - got rid of bogus error message when calling fits_update_key_longstr + + - Made the error message more explicit when CFITSIO tries to write + to a GZIP compressed file. Instead of just stating "cannot write + to a READONLY file", it will say "cannot write to a GZIP compressed + file". + +Version 3.22 - 28 October 2009 + + - added an option (in imcompress.c) to losslessly compress floating + point images, rather than using the default integer scaling method. + This option is almost never useful in practice for astronomical + images (because the amount of compression is so poor), but it has + been added for test comparison purposes. + + - enhanced the dithering option when quantizing and compressing + floating point images so that a random dithering starting point + is used, so that the same dithering pattern does not get used for + every image. + + - modified the architecture setup section of fitsio2.h to support the + 64-core 8x8-architecture Tile64 platform (thanks to Ken Mighell, NOAO) + + Fixes + + - fixed a problem that was introduced in version 3.13 of CFITSIO + in cases where a program writes it own END keyword to the header + instead of letting CFITSIO do it, as is strongly recommended. In + one case this caused CFITSIO to rewrite the END keyword and any + blank fill keywords in the header many times, causing a + noticeable slow-down in the FITS file writing speed. + +Version 3.21 - 24 September 2009 + + - fixed bug in cfileio.c that caused CFITSIO to crash with a bus error + on Mac OS X if CFITSIO was compiled with multi-threaded support (with + the --enable-reentrant configure option). The Mac requires an + additional thread initialization step that is not required on Linux + machines. Even with this fix, occasional bus errors have been seen on + some Mac platforms, The bus errors are seen when running the + thread_test.c program. The bus errors are very intermittent, and occur + less than about 1% of the time, on the affected platforms. + These bus errors have not been seen on Linux platforms. + + - fixed invalid C comment delimiter ("//*" should have been "/*") + in imcompress.c. + + - Increased the CFITSIO version number string length + in fpackutil.c, to fix problem on some platforms when running + fpack -V or funpack -V. Also modified the output format of the + fpack -L command. + +Version 3.20 - 31 August 2009 + + - modified configure.in and configure so that it will build the Fortran + interface routines by default, even if no Fortran compiler is found + in the user's path. Building the interface routines may be disabled + by specifying FC="none". This was done at the request of users who + obtained CFITSIO from some other standard linux distributions, where + CFITSIO was apparently built in an environment that had no Fortran + compiler and hence did not build the Fortran wrappers. + + - modified ffchdu (close HDU) so that it calls the routine to update + the maximum length of variable length table columns in the TFORM + values in all cases where the values may have changed. Previously + it would not update the values if a value was already specified in + the TFORM value. + + - added 2 new string manipulation functions to the CFITSIO parser + (contributed by Craig Markwardt): strmid extracts a substring + from a string, and strstr searches for a substring within a string. + + - removed the code in quantize.c that treated "floating-point integer" + images as a special case (it would just do a datatype conversion from + float to int, and not otherwise quantize the pixel values). This + caused complications with the new subtractive dithering feature. + + - enhanced the code for converting floating point images to quantized + scaled integer prior to tile-compressing them, to apply a random + subtractive dithering, which improves the photometric accuracy + of the compressed images. + + - added new internal routine, iraf_delete_file, for use by fpack to + delete a pair of IRAF format header and pixel files. + + - small change in cfileio.c in the way it recognizes an IRAF format + .imh file. Instead of just requiring that the filename contain the + ".imh" string, that string must occur at the end of the file name. + + - fixed bug in the code that is used when tile-compressing real*4 FITS + images, which quantizes the floating point pixel values into + integer levels. The bug would only appear in the fairly rare + circumstance of tile compressing a floating point image that contains + null pixels (NaNs) and only when using the lossy Hcompress algorithm + (with the s parameter not equal to 1). This could cause underflow of + low valued pixels, causing them to appear as very large pixel values + (e.g., > 10**30) in the compressed image + + - changed the "if defined" blocks in fitsio.h, fitsio2.h and f77_wrap.h + to correctly set the length of long variables on sparc64 machines. + Patch contributed by Matthew Truch (U. Penn). + + - modified the HTTP file access code in drvrnet.c to support basic + HTTP authentication, where the user supplies a user name and + password. The CFITSIO filename format in this case is: + "http://username:password@hostname/..." + Thanks to Jochen Liske (ESO) for the suggestion and the code. + +Version 3.181 (BETA) - 12 May 2009 + + - modified region.c and region.h to add support for additional + types of region shapes that are supported by ds9: panda, epanda, + and bpanda. + + - fixed compiler error when using the new _REENTRANT flag, having to + do with the an attempted static definition of Fitsio_Lock in + several source files, after declaring it to be non-static in fitsio2.h. + +Version 3.18 (BETA) - 10 April 2009 + + - Made extensive changes to make CFITSIO thread safe. Previously, + all opened FITS files shared a common pool of memory to store + the most recently read or written FITS records in the files. + In a multi-threaded environment different threads could + simultaneously read or write to this common area causing + unpredictable results. This was changed so that every opened + FITS file has its own private memory area for buffering the + file. Most of the changes were in buffers.c, fitsio.h, and + fitsio2.h. Additional changes were made to cfileio.c, mainly + to put locks around small sections of code when setting up the + low-level drivers to read or write the FITS file. Also, locks + were needed around the GZIP compression and uncompression code + in compress.c., the error message stack access routine in + fitscore.c, the encode and decode routines in fits_hcompress.c + and fits_hdecompress.c, in ricecomp.c, and the table row + selection and table calculator functions. Also, removed the + 'static' declaration of the local variables in pliocomp.c + which did not appeared to be required and prevented the + routines from being thread safe. + + As a consequence of having a separate memory buffer for every + FITS file (by default, about 115 kB per file), CFITSIO may now + allocate more memory than previously when an application + program opens multiple FITS files at once. The read and write + speed may also be slightly faster, since the buffers are not + shared between files. + + - Added new families of Fortran wrapper routines to read and + write values to large tables that have more than 2**31 rows. + The arguments that define the first row and first element to + read or write must be I*8 integers, not ordinary I*4 + integers. The names of these new routines have 'LL' appended + to them, so for example, ftgcvb becomes ftgcvbll. + + Fixes + + - Corrected an obscure bug in imcompress.c that would have incorrectly + written the null values only in the rare case of writing a signed + byte array that is then tile compressed using the Hcompress or PLIO + algorithm. + +Version 3.14 - 18 March 2009 + + Enhancements + + - modified the tiled-image compression and uncompression code to + support compressing unsigned 16-bit integer images with PLIO. + FITS unsigned integer arrays are offset by -32768, but the PLIO + algorithm does not work with negative integer values. In this + case, an offset of 32768 is added to the array before compression, + and then subtracted again when reading the compressed array. + IMPORTANT NOTE: This change is not backward compatible, so + these PLIO compressed unsigned 16-bit integer images will not be + read correctly by previous versions of CFITSIO; the pixel values + will have an offset of +32768. + + - minor changes to the fpack utility to print out more complete + version information with the -V option, and format the report + produced by the -T option more compactly. + + Fixes + + - Modified imcomp_compress_image (which is called by fpack) so that + it will preserve any null values (NaNs) if the input image has + a floating point datatype (BITPIX = -32 or -64). Null values in + integer datatype images are handled correctly. + + - Modified imcomp_copy_comp2img so that it does not copy the + ZBLANK keyword, if present, from the compressed image header + when uncompressing the image. + + - Fixed typo in the Fortran wrapper macro for the ftexist function. + +Version 3.13 - 5 January 2009 + + Enhancements + + - updated the typedef of LONGLONG in fitsio.h and cfortran.h to + support the Borland compiler which uses the __int64 data type. + + - added new feature to the extended filename syntax so that when + performing a filtering operation on specified HDU, if you add + a '#' character after the name or number of the HDU, then ONLY + that HDU (and the primary array if the HDU is a table) will be + copied into the filtered version of the file in memory. Otherwise, + by default CFITSIO copies all the HDUs from the input file into + memory. + + - when specifying a section, if the specified number of dimensions + is less than the number of dimensions in the image, then CFITSIO + will use the entire dimension, as if a '*' had been specified. + Thus [1:100] is equivalent to [1:100,*] when specifying a section + of 2 dimensional image. + + - modified fits_copy_image_section to read/write the section 1 row + at a time, instead of the whole section, to reduce memory usage. + + - added new stream:// drivers for reading/writing to stdin/stdout. + This driver is somewhat fragile, but for simple FITS read and + write operations this driver streams the FITS file on stdin + or stdout without first copying the entire file in memory, as is + done when specifying the file name as "-". + + - slight modification to ffcopy to make sure that the END keyword + is correctly written before copying the data. This is required + by the new stream driver. + + - modified ffgcprll, so that when writing data to an HDU, it first + checks that the END keyword has been written to the correct place. + This is required by the new stream driver. + + Fixes + + - fixed bug in ffgcls2 when reading an ASCII string column in binary + tables in cases where the width of the column is greater than 2880 + characters and when reading more than 1 row at a time. Similar + change was made to ffpcls to fix same problem with writing to + columns wider than 2880 characters. + + - updated the source files listed in makepc.bat so that it can be + used to build CFITSIO with the Borland C++ compiler. + + - fixed overflow error in ffiblk that could cause writing to Large Files + (> 2.1 GB) to fail with an error status. + + - fixed a bug in the spatial region code (region.c) with the annulus + region. This bug only affected specialized applications which + directly use the internal region structure; it does not affect + any CFITSIO functions directly. + + - fixed memory corruption bug in region.c that was triggered if the + region file contained a large number of excluded regions. + + - got rid of a harmless error message that would appear if filtering + a FITS table with a GTI file that has zero rows. (eval_f.c) + + - modified fits_read_rgnfile so that it removes the error messages + from the error stack if it is unable to open the region file as + a FITS file. (region.c) + +Version 3.12 - 8 October 2008 + + - modified the histogramming code so that the first pixel in the binned + array is chosen as the reference pixel by default, if no other + value is previously defined. + + - modified ffitab and ffibin to allow a null pointer to the + EXTNAME string, when inserting a table with no name. + +Version 3.11 - 19 September 2008 + + - optimized the code when tile compressing real*4 images (which get + scaled to integers). This produced a modest speed increase. For + best performance, one must specify the absolute q quantization + parameter, rather than relative to the noise in the tile (which + is expensive to compute). + + - modified the FITS region file reading code to check for NaN values, + which signify the end of the array of points in a polygon region. + + - removed the test for LONGSIZE == 64 from fitsio.h, since it may + not be defined. + + - modified imcompress.c to support unconventional floating point FITS + images that also have BSCALE and BZERO keywords. The compressed + floating point images are linearly scaled twice in this case. + +Version 3.10 - 20 August 2008 + + - fixed a number of cases, mainly dealing with long input file names + (> 1024 char), where unsafe usage of strcat and strcpy could have caused + buffer overflows. These buffer overflows could cause the application + to crash, and at least theoretically, could be exploited by a + malicious user to execute arbitrary code. There are no known instances + of this type of malicious attack on CFITSIO applications, and the + likelihood of such an attack seems remote. None the less, it would + be prudent for CFITSIO users to upgrade to this new version to guard + against this possibility. + + - modified some of the routines to define input character string + parameters as "const char *" rather than just "char *" to eliminate + some compiler warnings when the calling routine passes a constant + string to the CFITSIO routine. Most of the changes were to the + keyword name argument in the many routines that read or write keywords. + + - fixed bug when tile-compressing a FITS image which caused all the + completely blank keywords in the input header to be deleted from + the output compressed image. Also added a feature to preserve any + empty FITS blocks in the header (reserved space for future keywords) + when compressing or uncompressing an image. + + - fixed small bug in the way the default tile size is set in imcompress.c. + (Fix sent in by Paul Price). + + - added support for reading FITS format region files (in addition + to the ASCII format that was previously supported). Thanks to + Keith Arnaud for modifying region.c to do this. + +Version 3.09 - 12 June 2008 + + - fixed bug in the calculator function, parse_data, that evaluates + expressions then selecting rows or modifying values in table columns. + This bug only appeared in unusual circumstances + where the calculated value has a null value (= TNULLn). The bug + could cause elements to not be flagged as having a null value, or + in rare cases could cause valid elements to be flagged as null. This + only appears to have affected 64-bit platforms (where size(long) = 8). + + - fixed typo in imcomp_decompress_tile: call to fffi2r8 should have + been to fffi4r8. + + - in the imcopy_copy_comp2img routine, moved the call to + fits_translate_keywords outside of the 'if' statement. This could + affect reading compressed images that did not have a EXTNAME keyword + in the header. + + - fixed imcomp_compress_tile in imcompress.c to properly support + writing unsigned integers, in place, to tile compressed images. + + - modified fits_read_compressed_img so that if the calling routine + specifies nullval = 0, then it will not check for null-valued + pixels in the compressed FITS image. This mimics the same + behavior when reading normal uncompressed FITS images. + +Version 3.08 - 15 April 2008 + + - fixed backwards compatibility issue when uncompressing a Rice + compressed image that was created with previous versions of + CFITSIO (this late fix was added on May 18). + + - small change to cfortran.h to add "extern" to the common block + definition. This was done for compatibility with the version + of cfortran.h that is distributed by the Debian project. + + - relaxed the requirement that a string valued keyword must have a + closing quote character. If the quote is missing, CFITSIO will silently + append a quote at the end of the keyword record. This change was made + because otherwise it is very difficult to correct the keyword + because CFITSIO would exit with an error before making the fix. + + - added a new BYTEPIX compression parameter when tile-compressing + images with the Rice algorithm. + + - cached the NAXIS and NAXISn keyword values in the fitsio structure + for efficiency, to eliminate duplicates reads of these keywords. + + - added variants of the Rice compression and uncompression routines to + support short int images (in addition to the routines that support int). + + - moved the definition of LONGLONG_MIN and LONGLONG_MAX from fitsio2.h + to fitsio.h, to make it accessible to application programs. + + - make efficiency improvements to fitscore.c, to avoid needless searches + through the entire header when reading the required keywords that must + be near the beginning of the header. + + - made several improvements to getcol.c to optimize reading of compressed + and uncompressed images. + + - changed the compression level in the gzip code from 6 to 1. In most + cases this will provide nearly the same amount of compression, but is + significantly faster in some cases. + + - added new "helper routines' to imcompress.c to allow applications to + specified the "quantize level" and Hcompress scaling and smoothing + parameters + + - modified the extended filename syntax to support the "quantize level" + and Hcompress scaling and smoothing parameters. The parser in + cfileio.c was extensively modified. + + - extensive changes to quantize.c: + - replace the "nbits" parameter with "quantize level" + - the quantize level is now relative to the RMS noise in the image + - the HCOMPRESS scale factor is now relative to the RMS noise + - added routines to calculate RMS noise in image + (these changes require a change to the main file structure in fitsio.h) + + - initialize errno = 0 before the call to strtol in ffext, in case errno + has previously been set by an unrelated error condition. + + - added the corresponding long name for the ffgkyjj routine to longnam.h. + + - changed imcomp_copy_comp2img (in imcompress.c) to not require the + presence of the EXTNAME keyword in the input compressed image header. + + - modified imcompress.c to only write the UNCOMPRESSED_DATA column + in tile-compressed images if it is actually needed. This eliminates + the need to subsequently delete the column if it is not used + (which is almost always the case). + + - found that it is necessary to seek to the EOF of a file after + truncating the size of the file, to reestablish a definite + current location in the file. The required small changes to 3 + routines: file_truncate (to seek to EOF) and fftrun (to set io_pos) + and the truncation routine in drvrmem.c. + + - improved the efficiency when compressing integer images with + gzip. Previously, the image was always represented using integer*4 + pixels, which were then compressed. Now, if the range of pixel + values can be represented with integer*2 pixels or integer*1 pixels, + then that is used. This change is backward compatible with any + compressed images that used the previous method. + + - changed the default tiling pattern when using Hcompress from + large squares (200 to 600 pixels wide) to 16 rows of the image. + This generally requires less memory, compresses faster, and is more + consistent with the default row by row tiling when using the other + compression methods. + + - modified imcomp_init_table in imcompress.c to enforce a restriction + when using the Hcompress algorithm that the 1st 2 dimensions of sll + image tiles must be at least 4 pixels long. Hcompress becomes very + inefficient for smaller dimensions, and does not work at all with + 1D images. + + - fixed bug in the Hcompress compression algorithm that could affect + compression of I*4 images, using non-square compression tiles + (in the encode64 routine). + +Version 3.07 - 6 December 2007 (internal release) + + - fixed bug with the PLIO image compression routine which silently + produced a corrupted compressed image if the uncompressed image pixels + were not all in the range 0 to 2**24. (fixed in November) + + - fixed several 'for' loops in imcompress.c which were exceeding the + bounds of an array by 1. (fixed in November) + + - fixed a possible, but unlikely, memory overflow issue in iraffits.c. + + - added a clarification to the cfortran.doc file that cfortran.h + may be used and distributed under the terms of the GNU Library + General Public License. + + - fixed bug in the fits_modify_vector_len routine when modifying + the vector length of a 'X' bit column. + +Version 3.06 - 27 August 2007 + + - modified the imcopy.c utility program (to tile-compress images) + so that it writes the default EXTNAME = 'COMPRESSED_IMAGE' + keyword in the compressed images, to preserve the behavior of + earlier versions of imcopy. + + - modified the angsep function in the FITS calculator (in eval.y) + to use haversines, instead of the 'law of cosines', to provide + more precision at small angles (< 0.1 arcsec). + +Version 3.05 - July 2007 (internal release only) + + - extensive changes to imcompress.c to fully support implicit data + type conversion when reading and writing arrays of data to FITS + images, where the data type of the array is not the same as the + data type of the FITS image. This includes support for null pixels, + and data scaling via the BSCALE and BZERO keywords. + + - rewrote the fits_read_tbl_coord routine in wcssub.c, that gets the + standard set of WCS keywords appropriate to a pair of columns in a + table, to better support the full set of officially approved WCS keywords. + + - made significant changes to histo.c, which creates an image by binning + columns of a table, to better translate the WCS keywords in the table + header into the WCS keywords that are appropriate for an image HDU. + + - modified imcompress.c so that when pixels are written to a + tile-compressed image, the appropriate BSCALE and BZERO values of + that image are applied. This fixes a bug in which writing to + an unsigned integer datatype image (with BZERO = 32768) was not + done correctly. + +Version 3.04 - 3 April 2007 + + - The various table calculator routines (fits_select_rows, etc.) implicitly + assumed that the input table has not been modified immediately prior to + the call. To cover cases where the table has been modified a call to + ffrdef has been added to ffprs. IN UNUSUAL CASES THIS CHANGE COULD + CAUSE CFITSIO TO BEHAVE DIFFERENTLY THAN IN PREVIOUS VERSIONS. For + example, opening a FITS table with this column-editing virtual file + expression: + myfile.fits[3][col A==X; B = sqrt(X)] + no longer works, because the X column does not exist when the + sqrt expression is evaluated. The correct expression in this case is + myfile.fits[3][col A==X; B = sqrt(A)] + + - modified putkey.c to support USHORT_IMG when calling fits_create_img + to create a signed byte datatype image. + + - enhanced the column histogramming function to propagate any TCn_k and + TPn_k keywords in the table header to the corresponding CDi_j and PCi_j + keywords in the image header. + + - enhanced the random, randomn, and randomp functions in the lexical + parser to take a vector column name argument to specify the length + of the vector of random numbers that should be generated (provided by + Craig Markwardt, GSFC) + + - enhanced the ffmcrd routine (to modify an existing header card) to + support long string keywords so that any CONTINUE keywords associated + with the previous keyword will be deleted. + + - modified the ffgtbp routine to recognize the TDIMn keyword for + ASCII string columns in a binary table. The first dimension is + taken to be the size of a unit string. (The TFORMn = 'rAw' + syntax may also be used to specify the unit string size). + + - in fits_img_decompress, the fits_get_img_param function was called + with an invalid dimension size, which caused a fatal error on at + least 1 platform. + + - in ffopentest, set the status value before returning in case of error. + + - in the drvrnet.c file, the string terminators needed to be changed + from "\n" to "\r\n" to support the strict interpretation of the + http and ftp standard that is enforced by some newer web servers. + +Version 3.03 - 11 December 2006 + + New Routine + + - fits_write_hdu writes the current HDU to a FILE stream (e.g. stdout). + + Changes + + - modified the region parsing code to support region files where the + keyword "physical" is on a separate line preceding the region shape + token. (However, "physical" coordinates are not fully supported, and + are treated identically to "image" coordinates). + + - enhanced the iterator routines to support calculations on 64-bit + integer columns and images. Currently, the values are cast to + double precision when doing the calculations, which can cause a + loss of precision for integer values greater than about 2**52. + + - added support for accessing FITS files on the computational grid. + Giuliano Taffoni and Andrea Barisani, at INAF, University of Trieste, + Italy, implemented the necessary I/O driver routines in drvrgsiftp.c. + + - modified the tiled image compression/uncompression routines to + preserve/restore the original CHECKSUM and DATASUM keywords if they + exist. (saved as ZHECKSUM and ZDATASUM in the compressed image) + + - split fits_select_image_section into 2 routines: a higher level routine + that creates the output file and copies other HDUs from the input file + to the output file, and a lower level routine that extracts the image + section from the input image into an output image HDU. + + - Improved the error messages that get generated if one tries to + use the lexical parser to perform calculations on variable-length + array columns. + + - added "#define MACHINE NATIVE" in fitsio2.h for all machines where + BYTESWAPPED == FALSE. This may improve the file writing performance + by eliminating the need to allocate a temporary buffer in some cases. + + - modified the configure.in and configure script to fix problems with + testing if network services are available, which affects the definition + of the HAVE_NET_SERVICES flag. + + - added explicit type casting to all malloc statements, and deleted + declarations of unreferenced variables in the image compression code + to suppress compiler warnings. + + - fixed incorrect logic in fitsio2.h in the way it determined if numerical + values are byteswapped or not on MIPS and ARM architectures. + + - added __BORLANDC__ to the list of environments in fitsio.h that don't + use %lld in printf for longlong integers + + - added "#if defined(unix)" around "#include " statements in + several C source files, to make them compatible with Windows. + + +Version 3.02 - 18 Sept 2006 + + - applied the security patch to the gzip code, available at + http://security.FreeBSD.org/patches/SA-06:21/gzip.patch + The insufficient bounds checks in buffer use can cause gzip to crash, + and may permit the execution of arbitrary code. The NULL pointer + deference can cause gzip to crash. The infinite loop can cause a + Denial-of-Service situation where gzip uses all available CPU time. + + - added HCOMPRESS as one of the compression algorithm options in the + tiled image compression code. (code provided by Richard White (STScI)) + Made other improvements to preserve the exact header structure in the + compressed image file so that the compressed-and-then-uncompressed FITS + image will be as identical as possible to the original FITS image file. + + New Routines + + - the following new routines were added to support reading and writing + non-standard extension types: + fits_write_exthdr - write required keywords for a conforming extension + fits_write_ext - write data to the extension + fits_read_ext - read data from the extension + + - added new routines to compute the RMS noise in the background pixels + of an image: fits_rms_float and fits_rms_short (take an input + array of floats or shorts, respectively). + + Fixes + + - added the missing 64-bit integer case to set of "if (datatype)" + statements in the routine that returns information about a + particular column (ffgbclll). + + - fixed a parsing error in ffexts in cases where an extension number + is followed by a semi-colon and then the column and row number of an + array in a binary table. Also removed an extraneous HISTORY keyword + that was being written when specifying an input image in a table cel. + + - modified the routine that reads a table column returning a string + value (ffgcls) so that if the displayed numerical value is too + wide to fit in the specified length string, then it will return + a string of "*" characters instead of the number string. + + - small change to fitsio.h to support a particular Fortran and C + compiler combination on a SGI Altix system + + - added a test in the gunzip code to prevent seg. fault when trying + to uncompress a corrupted file (at least in some cases). + + - fixed a rarely-occurring bug in the routine that copies a table + cell into an image; had to call the ffflsh call a few lines earlier. + +Version 3.01 - (in FTOOLS 6.1 release) + + - modified fits_copy_image2cell to correctly copy all the appropriate + header keywords when copying an image into a table cell + + - in eval.y, explicitly included the code for the lgamma function + instead of assuming it is available in a system library (e.g., the + lgamma function is currently not included in MS Visual++ libraries) + + - modified the logic in fits_pixel_filter so that the default data + type of the output image will be promoted to at least BITPIX = -32 + (a single precision floating point) if the expression that is being + evaluated resolves to a floating point result. If the expression + resolves to an integer result, the output image will have the same + BITPIX as the input image. + + - in fits_copy_cell2image, added 5 more WCS keywords to the list of + keywords related to other columns that should be deleted in the + output image header. + + - disabled code in cfileio.c that would write HISTORY keywords to the + output file in fits_copy_image2cell and cell2image, because some tasks + would not want these extraneous HISTORY keywords. + + - added 2 new random number functions to the CFITSIO parser + RANDOMN() - produces a normal deviate (mean=0, stddev=1) + RANDOMP(X) - produces a Poisson deviate for an expected # of counts X + + - in f77_wrap.h, removed the restriction that "g77Fortran" must be + defined on 64-bit Itanium machines before assuming that + sizeof(long) = 8. It appears that "long"s are always + 8 bytes long on this machine, regardless of what compilers are used. + + - added test in fitsio.h so that LONGLONG cannot be multiply defined + + - modified longnam.h so that both "fits_write_nulrows" and + "fits_write_nullrows" get replace by the string "ffprwu". This + fixes a documentation error regarding the long name of this + routine. + + Bug fixes + + - fixed a potential null character string dereferencing error in the + the ffphtb and ffphbn routines that write the FITS table keywords. + This concerned the optional TUNITn keywords. + + - fixed a few issues in fits_copy_cell2image and fits_copy_image2cell + related to converting some WCS keyword between the image extension + form and the table cell form of the keyword. (cfileio.c) + + - fixed bug in fits_translate_keyword (fitscore.c) that, e.g., caused + 'EQUINOX' to be translated to EQUINOXA' if the pattern is 'EQUINOXa' + + - fixed 2 bugs that could affect 'tile compressed' floating point + images that contain NaN pixels (null pixels). First, the + ZBLANK keyword was not being written, and second, an integer + overflow could occur when computing the BZERO offset in the + compressed array. (quantize.c and imcompress.c) + +Version 3.006 - 20 February 2006 -(first full release of v3) + + - enhanced the 'col' extended filename syntax to support keyword name + expressions like + [col error=sqrt(rate); #TUNIT# = 'counts/s'], + in which the trailing '#' will be replaced by the column number + of the most recently referenced column. + + - fixed bug in the parse_data iterator work function that caused it + to fail to return a value of -1 in cases where only a selected + set of rows were to be processed. (affected Fv) + + - added code to fitsio.h and cfortran.h to typedef LONGLONG to + the appropriate 8-byte integer data type. Most compilers now + support the 'long long' data type, but older MS Visual C++ + compilers used '__int64' instead. + + - made several small changes based on testing by Martin Reinecke: + o in eval.y, change 'int undef' to 'long undef' + o in getcold.c and getcole.c, fixed a couple format conversion + specifiers when displaying the value of long long variables. + o in fitsio.h, modified the definition of USE_LL_SUFFIX in the + case of Athon64 machines. + o in fitsio2.h, defined BYTESWAPPED in the case of SGI machines. + o in group.c, added 'include unistd.h' to get rid of compiler warning. + +Version 3.005 - 20 December 2005 (beta) + + - cfortran.h has been enhanced to support 64-bit integer parameters + when calling C routines from Fortran. This modification was kindly + provided by Martin Reinecke (MPE, Garching). + + - Many new Fortran wrapper routines have been added to support reading + and writing 64-bit integer values in FITS files. These new routines + are documented in the updated version of the 'FITSIO User's Guide' + for Fortran programmers. + + - fixed a problem in the fits_get_keyclass routine that caused it + to not recognize the special COMMENT keywords at the beginning + of most FITS files that defines the FITS format. + + - added a new check to the ffifile routine that parses the + input extended file name, to distinguish between a FITS extension + name that begins with 'pix', and a pixel filtering operator that + begins with the 'pix' keyword. + + - small change to the WCSLIB interface routine, fits_read_wcstab, to + be more permissive in allowing the TDIMn keyword to be omitted for + degenerate coordinate array. + +Version 3.004 - 16 September 2005 (3rd public beta release) + + - a major enhancement to the CFITSIO virtual file parser was provided + by Robert Wiegand (GSFC). One can now specify filtering operations + that will be applied on the fly to the pixel values in a FITS image. + For example [pix sqrt(X)] will create a virtual FITS image where the + pixel values are the square root of the input image pixels. + + - modified region.c so that it interprets the position angles of regions + in a SAO style region file in the same way as DS9. In particular, if + the region parameters are given in WCS units, then the position angle + should be relative to the WCS coordinates of the image (increasing CCW + from West) instead of relative to the X/Y pixel coordinate system. + This only affects rotated images (e.g. with non-zero CROTA2 keyword) + with elliptical or rectangular regions. + + - cleaned up fitsio.h and fitsio2.h to make the definition of LONGLONG + and BYTESWAPPED and MACHINE more logical. + + - removed HAVE_LONGLONG everywhere since it is no longer needed (the + compiler now must have an 8-byte integer datatype to build CFITSIO). + + - added support for the 64-bit IBM AIX platform + + - modified eval.y so that the circle, ellipse, box, and near functions + can operate on vectors as well as scalars. This allows region filtering + on images that are stored in a vector cell in a binary table. + (provided by Craig Markwardt, GSFC) + + New Routines + + - added new fits_read_wcstab routine that serves as an interface to + Mark Calabretta's wcslib library for reading WCS information when + the -TAB table lookup convention is used in the FITS file. + + - added new fits_write_nullrows routine, which writes null values into + every column of a specified range of rows in a FITS table. + + - added the fits_translate_keyword and fits_translate_keywords utility + routines for converting the names of keywords when moving columns and + images around. + + - added fits_copy_cell2image and fits_copy_image2cell routines for + copying an image extension (or primary array) to or from a cell + in a binary table vector column. + + Bug fixes + + - fixed a memory leak in eval.y; was fixed by changing a call to malloc + to cmalloc instead. + + - changed the definition of several global variables at the beginning + of buffers.c to make them 'static' and thus invisible to applications + programs. + + - in fits_copy_image_cell, added a call to flush the internal buffers + before reading from the file, in case any records had been modified. + +Version 3.003 - 28 July 2005 - 2nd public beta release (used in HEASOFT) + + Enhancements + + - enhanced the string column reading routing fits_get_col_str to + support cases where the user enters a null pointer (rather than + a null string) as the nulval parameter. + + - modified the low level ffread and ffwrite routines that physically + read and write data from the FITS file so that they write the name + of the file to the CFITSIO error stack if an error occurs. + + - changed the definition of fits_open_file into a macro that will test + that the version of the fitsio.h include file that was used to + build the CFITSIO library is the same version as included when + compiling the application program. + + - made a simple modification to region.c to support regions files + of type "linear", for compatibility with ds9 and fv. + + - modified the internal ffgpr routine (and renamed it ffgprll) so + that it returns the TNULL value as a LONGLONG parameter instead + of 'long'. + + - in fits_get_col_display_width, added support for TFORM = 'k' + + - modified fitsio.h, fitsio2.h, and f77_wrap.h to add test for (_SX) + to identify NEC SX supercomputers. + + - modified eval_f.c to treat table columns of TULONG (unsigned long) + as a double. Also added support for TLONGLONG (8-byte integers) as + a double, which is only a temporary fix, since doubles only have about + 52 bits of precision. + + - changed the 'blank' parameter in the internal ffgphd function to + to type LONGLONG to support integer*8 FITS images. + + - when reading the TNULL keyword value, now use ffc2jj instead of + ffc2ii, to support integer*8 values. + + Bug fixes + + - fixed a significant bug when writing character strings to a variable + length array column of a binary table. This bug would result in some + unused space in the variable length heap, making the heap somewhat + larger than necessary. This in itself is usually a minor issue, since + the FITS files are perfectly valid, and other software should have + no problems reading back the characters strings. In some cases, however, + this problem could cause the program that is writing the table + to exit with a status = 108 disk read error. + + - modified the standalone imcopy.c utility program to fix a memory allocation + bug when running on 64-bit platforms where sizeof(long) = 8 bytes. + + - added an immediate 'return' statement to ffgtcl if the input status >0, + to prevent a segfault on some platforms. + +Version 3.002 - 15 April 2005 - first public beta release + + - in drvrfile.c, if it fails to open the file for some reason, then + it should reset file_outfile to a null string, to avoid errors on + a subsequent call to open a file. + + - updated fits_get_keyclass to recognize most of the WCS keywords + defined in the WCS Papers I and II. + +Version 3.001 - 15 March 2005 - released with HEASOFT 6.0 + + - numerous minor changes to the code to get rid of compiler warning + messages, mainly dealing with numerical data type casting and the + subsequent possible loss of precision in the result. + +Version 3.000 - 1 March 2005 (internal beta release) + + Enhancements: + + - Made major changes to many of the CFITSIO routines to more generally + support Large Files (> 2.1 GB). These changes are intended to + be 100% backward compatible with software that used the previous + versions of CFITSIO. The datatype of many of the integer parameters + in the CFITSIO functions has been changed from 'long' to 'LONGLONG', + which is typedef'ed to be equivalent to an 8-byte integer datatype on + each platform. With these changes, CFITSIO supports the following: + - integer FITS keywords with absolute values > 2**31 + - FITS files with total sizes > 2**31 bytes + - FITS tables in which the number of rows, the row width, or + the size of the heap is > 2**31 bytes + - FITS images with dimensions > 2**31 bytes (support is still + somewhat limited, with full support to be added later). + + - added another lexical parser function (thanks to Craig Markwardt, + GSFC): angsep computes the angular separation between 2 positions + on the celestial sphere. + + - modified the image subset extraction code (e.g., when specifying + an image subregion when opening the file, such as + 'myimage.fits[21:40, 81:90]') so that in addition to + updating the values of the primary WCS keywords CRPIXk, CDELTi, and + CDj_i in the extracted/binned image, it also looks for and updates + any secondary WCS keywords (e.g., 'CRPIX1P'). + + - made cosmetic change to group.c, so that when a group table is + copied, any extra columns will be appended after the last existing + column, instead of being inserted before the last column. + + - modified the routines that read tile compressed images to support + NULL as the input value for the 'anynul' parameter (meaning the + calling program does not want the value of 'anynul' returned to it). + + - when constructing or parsing a year/month/day character string, + (e.g, when writing the DATE keyword) the routines now rigorously + verify that the input day value is valid for the given month + (including leap years). + + - added some checks in cfileio.c to detect if some vital parameters + that are stored in memory have been corrupted. This can occur if + a user's program writes to areas of memory that it did not allocate. + + - added the wcsutil_alternate.c source code file which contains + non-working stubs for the 2 Classic AIPS world coordinate + conversion routines that are distributed under the GNU General + Public License. Users who are unwilling or unable to distribute + their software under the General Public License may use this + alternate source file which has no GPL restrictions, instead + of wcsutil.c. This will have no effect on programs that use + CFITSIO as long as they do not call the fits_pix_to_world/ffwldp + or fits_world_to_pix/ffxypx routines. + + Bug Fixes + + - in ffdtdm (which parses the TDIMn keyword value), the check for + consistency between the length of the array defined by TDIMn and + the size of the TFORMn repeat value, is now not performed for variable + length array columns (which always have repeat = 1). + + - fixed byteswapping problem when writing null values to non-standard + long integer FITS images with BITPIX = 64 and FITS table columns with + TFORMn = 'K'. + + - fixed buffer overflow problem in fits_parse_template/ffgthd that + occurred only if the input template keyword value string was much + longer than can fit in an 80-char header record. + +Version 2.510 - 2 December 2004 + + New Routines: + + - added fits_open_diskfile and fits_create_diskfile routines that simply + open or create a FITS file with a specified name. CFITSIO does not + try to parse the name using the extended filename syntax. + + - 2 new C functions, CFITS2Unit and CUnit2FITS, were added to convert + between the C fitsfile pointer value and the Fortran unit number. + These functions may be useful in mixed language C and Fortran programs. + + Enhancements: + + - added the ability to recognize and open a compressed FITS file + (compressed with gzip or unix compress) on the stdin standard input + stream. + + - Craig Markwardt (GSFC) provided 2 more lexical parser functions: + accum(x) and seqdiff(x) that compute the cumulative sum and the + sequential difference of the values of x. + + - modified putcole.c and putcold.c so that when writing arrays of + pixels to the FITS image or column that contain null values, and + there are also numerical overflows when converting some of the + non-null values to the FITS values, CFITSIO will now ignore the + overflow error until after all the data have been written. Previously, + in some circumstances CFITSIO would have simply stopped writing any + data after the first overflow error. + + - modified fitsio2.h to try to eliminate compiler warning messages + on some platforms about the use of 'long long' constants when + defining the value of LONGLONG_MAX (whether to use L or LL + suffix). + + - modified region.c to support 'physical' regions in addition to + 'image', 'fk4', etc. + + - modified ffiurl (input filename parsing routine) to increase the + maximum allowed extension number that can be specified from 9999 + to 99999 (e.g. 'myfile.fits+99999') + + Bug Fixes: + + - added check to fits_create_template to force it to start with + the primary array in the template file, in case an extension + number was specified as part of the template FITS file name. + +Version 2.500 - 28 & 30 July 2004 + + New Routine: + + - fits_file_exists tests whether the specified input file, or a + compressed version of the file, exists on disk. + + Enhancements: + + - modified the way CFITSIO reads and writes data in COMPLEX ('C') and + DBLCOMPLEX 'M' columns. Now, in all cases, when referring to the + number of elements in the vector, or the value of the offset to a + particular element within the vector, CFITSIO considers each pair of + numbers (the imaginary and real parts) as a single element instead of + treating each single number as an element. In particular, this changes + the behavior of fits_write_col_null when writing to complex columns. + It also changes the length of the 'nullarray' vector in the + fits_read_colnull routine; it is now only 1/2 as long as before. + Each element of the nullarray is set = 1 if either the real or + imaginary parts of the corresponding complex value have a null + value.(this change was added to version 2.500 on 30 July). + + - Craig Markwardt, at GSFC, provided a number of significant enhancements + to the CFITSIO lexical parser that is used to evaluate expressions: + + - the parser now can operate on bit columns ('X') in a similar + way as for other numeric columns (e.g., 'B' or 'I' columns) + + - range checking has been implemented, so that the following + conditions return a Null value, rather than returning an error: + divide by zero, sqrt(negative), arccos(>1), arcsin(>1), + log(negative), log10(negative) + + - new vector functions: MEDIAN, AVERAGE, STDDEV, and + NVALID (returns the number of non-null values in the vector) + + - all the new functions (and SUM, MIN and MAX) ignore null values + + - modified the iterator to support variable-length array columns + + - modified configure to support AIX systems that have flock in a non- + standard location. + + - modified configure to remove the -D_FILE_OFFSET_BITS flag when running + on Mac Darwin systems. This caused conflicts with the Fortran + wrappers, and should only be needed in any case when using CFITSIO + to read/write FITS files greater than 2.1 GB in size. + + - modified fitsio2.h to support compilers that define LONG_LONG_MAX. + + - modified ffrsim (resize an existing image) so that it supports changing + the datatype to an unsigned integer image using the USHORT_IMG and + ULONG_IMG definitions. + + - modified the disk file driver (drvrfile.c) so that if an output + file is specified when opening an ordinary file (e.g. with the syntax + 'myfile.fits(outputfile.fits)' then it will make a copy of the file, + close the original file and open the copy. Previously, the + specified output file would be ignored unless the file was compressed. + + - modified f77_wrap.h and f77_wrap3.c to support the Fortran wrappers + on 64-bit AMD Opteron machines + + Bug fixes: + + - made small change to ffsrow in eval_f.c to avoid potential array + bounds overflow. + + - made small change to group.c to fix problem where an 'int' was + incorrectly being cast to a 'long'. + + - corrected a memory allocation error in the new fits_hdr2str routine + that was added in version 2.48 + + - The on-the-fly row-selection filtering would fail with a segfault + if the length of a table row (NAXIS1 value) was greater than + 500000 bytes. A small change to eval_f.c was required to fix this. + +Version 2.490 - 11 February 2004 + + Bug fixes: + + - fixed a bug that was introduced in the previous release, which caused + the CFITSIO parser to no longer move to a named extension when opening + a FITS file, e.g., when opening myfile.fit[events] CFITSIO would just + open the primary array instead of moving to the EVENTS extension. + + - new group.c file from the INTEGRAL Science Data Center. It fixes + a problem when you attach a child to a parent and they are both + is the same file, but, that parent contains groups in other files. + In certain cases the attach would not happen because it seemed that + the new child was already in the parent group. + + - fixed bug in fits_calculator_rng when performing a calculation + on a range of rows in a table, so that it does not reset the + value in all the other rows that are not in the range = 0. + + - modified fits_write_chksum so that it updates the TFORMn + keywords for any variable length vector table columns BEFORE + calculating the CHECKSUM values. Otherwise the CHECKSUM + value is invalidated when the HDU is subsequently closed. + +Version 2.480 - 28 January 2004 + + New Routines: + + - fits_get_img_equivtype - just like fits_get_img_type, except in + the case of scaled integer images, it returns the 'equivalent' + data type that is necessary to store the scaled data values. + + - fits_hdr2str copies all the header keywords in the current HDU + into a single long character string. This is a convenient method + of passing the header information to other subroutines. + The user may exclude any specified keywords from the list. + + Enhancements: + + - modified the filename parser so that it accepts extension + names that begin with digits, as in 'myfile.fits[123TEST]'. + In this case CFITSIO will try to open the extension with + EXTNAME = '123TEST' instead of trying to move to the 123rd + extension in the file. + + - the template keyword parser now preserves the comments on the + the mandatory FITS keywords if present, otherwise a standard + default comment is provided. + + - modified the ftp driver file (drvrnet.c) to overcome a timeout + or hangup problem caused by some firewall software at the user's + end (Thanks to Bruce O'Neel for this fix). + + - modified iraffits.c to incorporate Doug Mink's latest changes to + his wcstools library routines. The biggest change is that now + the actual image dimensions, rather than the physically stored + dimensions, are used when converting an IRAF file to FITS. + + Bug fixes: + + - when writing to ASCII FITS tables, the 'elemnum' parameter was + supposed to be ignored if it did not have the default value of 1. + In some cases however setting elemnum to a value other than 1 + could cause the wrong number of rows to be produced in the output + table. + + - If a cfitsio calculator expression was imported from a text file + (e.g. using the extended filename syntax 'file.fits[col @file.calc]') + and if any individual lines in that text file were greater than + 255 characters long, then a space character would be inserted + after the 255th character. This could corrupt the line if the space + was inserted within a column name or keyword name token. + +Version 2.480beta (used in the FTOOLS 5.3 release, 1 Nov 2003) + + New Routines: + + - fits_get_eqcoltype - just like fits_get_coltype, except in the + case of scaled integer columns, it returns the 'equivalent' + data type that is necessary to store the scaled data values. + + - fits_split_names - splits an input string containing a comma or + space delimited list of names (typically file names or column + names) into individual name tokens. + + Enhancements: + + - changed fhist in histo.c so that it can make histograms of ASCII + table columns as well as binary table columns (as long as they + contain numeric data). + + Bug fixes: + + - removed an erroneous reference to listhead.c in makefile.vcc, that is + used to build the cfitsio dll under Windows. This caused a 'main' + routine to be added to the library, which causes problems when linking + fortran programs to cfitsio under windows. + + - if an error occurs when opening for a 2nd time (with ffopen) a file that + is already open (e.g., the specified extension doesn't exist), and + if the file had been modified before attempting to reopen it, then + the modified buffers may not get written to disk and the internal + state of the file may become corrupted. ffclos was modified to + always set status=0 before calling ffflsh if the file has been + concurrently opened more than once. + +Version 2.470 - 18 August 2003 + + Enhancements: + + - defined 'TSBYTE' to represent the 'signed char' datatype (similar to + 'TBYTE' that represents the 'unsigned char' datatype) and added + support for this datatype to all the routines that read or write + data to a FITS image or table. This was implemented by adding 2 + new C source code files to the package: getcolsb.c and putcolsb.c. + + - Defined a new '1S' shorthand data code for a signed byte column in + a binary table. CFITSIO will write TFORMn = '1B' and + TZEROn = -128 in this case, which is the convention used to + store signed byte values in a 'B' type column. + + - in fitsio2.h, added test of whether `__x86_64__` is defined, to + support the new AMD Opteron 64-bit processor + + - modified configure to not use the -fast compiler flag on Solaris + platforms when using the proprietary Solaris cc compiler. This + flag causes compilation problems in eval_y.c (compiler just + hangs forever). + + Bug fixes: + + - In the special case of writing 0 elements to a vector table column + that contains 0 rows, ffgcpr no longer adds a blank row to the table. + + - added error checking code for cases where a ASCII string column + in a binary table is greater than 28800 characters wide, to avoid + going into an infinite loop. + + - the fits_get_col_display_width routine was incorrectly returning + width = 0 for a 'A' binary table column that did not have an + explicit vector length character. + +Version 2.460 - 20 May 2003 + + Enhancements: + + - modified the HTTP driver in drvrnet.c so that CFITSIO can read + FITS files via a proxy HTTP server. (This code was contributed by + Philippe Prugniel, Obs. de Lyon). To use this feature, the + 'http_proxy' environment variable must be defined with the + address (URL) and port number of the proxy server, i.e., + > setenv http_proxy http://heasarc.gsfc.nasa.gov:3128 + will use port 3128 on heasarc.gsfc.nasa.gov + + - suppressed some compiler warnings by casting a variable of + type 'size_t' to type 'int' in fftkey (in fitscore.c) and + iraftofits and irafrdimge (in iraffits.c). + +Version 2.450 - 30 April 2003 + + Enhancements: + + - modified the WCS keyword reading routine (ffgics) to support cases + where some of the CDi_j keywords are omitted (with an assumed + value = 0). + + - Made a change to http_open_network in drvrnet.c to add a 'Host: ' + string to the open request. This is required by newer HTTP 1.1 + servers (so-called virtual servers). + + - modified ffgcll (read logical table column) to return the illegal + character value itself if the FITS file contains a logical value that is + not equal to T, F or zero. Previously it treated this case the + same as if the FITS file value was = 0. + + - modified fits_movnam_hdu (ffmnhd) so that it will move to a tile- + compressed image (that is stored in a binary table) if the input + desired HDU type is BINARY_TBL as well as if the HDU type = IMAGE_HDU. + + Bug fixes: + + - in the routine that checks the data fill bytes (ffcdfl), the call + to ffmbyt should not ignore an EOF error when trying to read the bytes. + This is a little-used routine that is not called by any other CFITSIO + routine. + + - fits_copy_file was not reporting an error if it hit the End Of File + while copying the last extension in the input file to the output file. + + - fixed inconsistencies in the virtual file column filter parser + (ffedit_columns) to properly support expressions which create or + modify a keyword, instead of a column. Previously it was only possible + to modify keywords in a table extension (not an image), and the + keyword filtering could cause some of the table columns to not + get propagated into the virtual file. Also, spaces are now + allowed within the specified keyword comment field. + + - ffdtyp was incorrectly returning the data type of FITS keyword + values of the form '1E-09' (i.e., an exponential value without + a decimal point) as integer rather than floating point. + + - The enhancement in the previous 2.440 release to allow more files to be + opened at one time introduced a bug: if ffclos is called with + a non-zero status value, then any subsequent call to ffopen will likely + cause a segmentation fault. The fits_clear_Fptr routine was modified + to fix this. + + - rearranged the order of some computations in fits_resize_img so as + to not exceed the range of a 32-bit integer when dealing with + large images. + + - the template parser routine, ngp_read_xtension, was testing for + "ASCIITABLE" instead of "TABLE" as the XTENSION value of an ASCII + table, and it did not allow for optional trailing spaces in the IMAGE" + or "TABLE" string value. + +Version 2.440 - 8 January 2003 + + Enhancements: + + - modified the iterator function, ffiter, to operate on random + groups files. + + - decoupled the NIOBUF (= 40) parameter from the limit on the number + FITS files that can be opened, so that more files may be opened + without the overhead of having to increase the number of NIOBUF + buffers. A new NMAXFILES parameter is defined in fitsio2.h which sets + the maximum number of opened FITS files. It is set = 300 by default. + Note however, that the underlying compiler or operating system may + not allow this many files to be opened at one time. + + - updated the version of cfortran.h that is distributed with CFITSIO from + version 3.9 to version 4.4. This required changes to f77_wrap.h + and f77_wrap3.c. The original cfortran.h v4.4 file was modified + slightly to support CFITSIO and ftools (see comments in the header + of cfortran.h). + + - modified ffhist so that it copies all the non-structural keywords from + the original binary table header to the binned image header. + + - modified fits_get_keyclass so that it recognizes EXTNAME = + COMPRESSED_IMAGE as a special tile compression keyword. + + - modified Makefile.in to support the standard --prefix convention + for specifying the install target directory. + + Bug fixes: + + - in fits_decompress_img, needed to add a call to ffpscl to turn + off the BZERO and BSCALE scaling when reading the compressed image. + +Version 2.430 - 4 November 2002 + + Enhancements: + + - modified fits_create_hdu/ffcrhd so that it returns without doing + anything and does not generate an error if the current HDU is + already an empty HDU. There is no need in this case to append + a new empty HDU to the file. + + - new version of group.c (supplied by B. O'Neel at the ISDC) fixes 2 + limitations: 1 - Groups now have 256 characters rather than 160 + for the path lengths in the group tables. - ISDC SPR 1720. 2 - + Groups now can have backpointers longer than 68 chars using the long + string convention. - ISDC SPR 1738. + + - small change to f77_wrap.h and f77_wrap3.c to support the fortran + wrappers on SUN solaris 64-bit sparc systems (see also change to v2.033) + + - small change to find_column in eval_f.c to support unsigned long + columns in binary tables (with TZEROn = 2147483648.0) + + - small modification to cfortran.h to support Mac OS-X, (Darwin) + + Bug fixes: + + - When reading tile-compress images, the BSCALE and BZERO scaling + keywords were not being applied, if present. + + - Previous changes to the error message stack code caused the + tile compressed image routines to not clean up spurious error + messages properly. + + - fits_open_image was not skipping over null primary arrays. + +Version 2.420 - 19 July 2002 + + Enhancements: + + - modified the virtual filename parser to support exponential notation + when specifying the min, max or binsize in a binning specifier, as in: + myfile.fits[binr X=1:10:1.0E-01, Y=1:10:1.0E-01] + + - removed the limitation on the maximum number of HDUs in a FITS file + (limit used to be 1000 HDUs per file). Now any number of HDUs + can be written/read in a FITS file. (BUT files that have huge numbers + of HDUs can be difficult to manage and are not recommended); + + - modified grparser.c to support HIERARCH keywords, based on + code supplied by Richard Mathar (Max-Planck) + + - moved the ffflsh (fits_flush_buffer) from the private to the + public interface, since this routine may be useful for some + applications. It is much faster than ffflus. + + - small change to the definition of OFF_T in fitsio.h to support + large files on IBM AIX operating systems. + + Bug fixes: + + - fixed potential problem reading beyond array bounds in ffpkls. This + would not have affected the content of any previously generated FITS + files. + + - in the net driver code in drvrnet.c, the requested protocol string + was changed from "http/1.0" to "HTTP/1.0" to support apache 1.3.26. + + - When using the virtual file syntax to open a vector cell in a binary + table as if it were a primary array image, there was a bug + in fits_copy_image_cell which garbled the data if the vector + was more than 30000 bytes long. + + - fixed problem that caused fits_report_error to crash under Visual + C++ on Windows systems. The fix is to use the '/MD' switch + on the cl command line, or, in Visual Studio, under project + settings / C++ select use runtime library multithreaded DLL + + - modified ffpscl so it does not attempt to reset the scaling values + in the internal structure if the image is tile-compressed. + + - fixed multiple bugs in mem_rawfile_open which affected the case + where a raw binary file is read and converted on the fly into + a FITS file. + + - several small changes to group.c to suppress compiler warnings. + +Version 2.410 - 22 April 2002 (used in the FTOOLS 5.2 release) + + New Routines: + + - fits_open_data behaves similarly to fits_open_file except that it + also will move to the first HDU containing significant data if + and an explicit HDU name or number to open was not specified. + This is useful for automatically skipping over a null primary + array when opening the file. + + - fits_open_table and fits_open_image behaves similarly to + fits_open_data, except they move to the first table or image + HDU in the file, respectively. + + - fits_write_errmark and fits_clear_errmark routines can be use + to write an invisible marker to the CFITSIO error stack, and + then clear any more recent messages on the stack, back to + that mark. This preserves any older messages on the stack. + + - fits_parse_range utility routine parses a row list string + and returns integer arrays giving the min and max row in each + range. + + - fits_delete_rowrange deletes a specified list of rows or row + ranges. + + - fits_copy_file copies all or part of the HDUs in the input file + to the output file. + + - added fits_insert_card/ffikey to the publicly defined set + of routines (previously, it was a private routine). + + Enhancements: + + - changed the default numeric display format in ffgkys from 'E' format + to 'G' format, and changed the format for 'X' columns to a + string of 8 1s or 0s representing each bit value. + + - modified ffflsh so the system 'fflush' call is not made in cases + where the file was opened with 'READONLY' access. + + - modified the output filename parser so the "-.gz", and "stdout.gz" + now cause the output file to be initially created in memory, + and then compressed and written out to the stdout stream when + the file is closed. + + - modified the routines that delete rows from a table to also + update the variable length array heap, to remove any orphaned + data from the heap. + + - modified ffedit_columns so that wild card characters may be + used when specifying column names in the 'col' file filter + specifier (e.g., file.fits[col TIME; *RAW] will create a + virtual table contain only the TIME column and any other columns + whose name ends with 'RAW'). + + - modified the keyword classifier utility, fits_get_keyclass, to + support cases where the input string is just the keyword name, + not the entire 80-character card. + + - modified configure.in and configure to see if a proprietary + C compiler is available (e.g. 'cc'), and only use 'gcc' if not. + + - modified ffcpcl (copy columns from one table to another) so that + it also copies any WCS keywords related to that column. + + - included an alternate source file that can be used to replace + compress.c, which is distributed under the GNU General Public + License. The alternate file contains non-functional stubs for + the compression routines, which can be used to make a version of + CFITSIO that does not have the GPL restrictions (and is also less + functional since it cannot read or write compressed FITS files). + + - modifications to the iterator routine (ffiter) to support writing + tile compressed output images. + + - modified ffourl to support the [compress] qualifier when specifying + the optional output file name. E.g., file.fit(out.file[compress])[3] + + - modified imcomp_compress_tile to fully support implicit data type + conversion when writing to tile-compressed images. Previously, + one could not write a floating point array to an integer compressed + image. + + - increased the number of internal 2880-byte I/O buffers allocated + by CFITSIO from 25 to 40, in recognition of the larger amount + of memory available on typical machines today compared with + a few years ago. The number of buffers can be set by the user + with the NIOBUF parameter in fitsio2.h. (Setting this too large + can actually hurt performance). + + - modified the #if statements in fitsio2.h, f77_wrap.h and f77_wrap1.c + to support the new Itanium 64-bit Intel PC. + + - a couple minor modifications to fitsio.h needed to support the off_t + datatype on Debian linux systems. + + - increased internal buffer sizes in ffshft and ffsrow to improve + the I/O performance. + + Bug fixes: + + - fits_get_keyclass could sometimes try to append to an unterminated + string, causing an overflow of a string array. + + - fits_create_template no longer worked because of improvements made + to other routines. Had to modify ffghdt to not try to rescan + the header keywords if the file is still empty and contains no + keywords yet. + + - ffrtnm, which returns the root filename, sometimes did not work + properly when testing if the 'filename+n' convention was used for + specifying an extension number. + + - fixed minor problem in the keyword template parsing routine, ffgthd + which in rare cases could cause an improperly terminated string to + be returned. + + - the routine to compare 2 strings, ffcmps, failed to find a match + in comparing strings like "*R" and "ERROR" where the match occurs + on the last character, but where the same matching character occurs + previously in the 2nd string. + + - the region file reading routine (ffrrgn) did not work correctly if + the region file (created by POW and perhaps other programs) had an + 'exclude' region (beginning with a '-' sign) as the first region + in the file. In this case all points outside the excluded region + should be accepted, but in fact no points were being accepted + in this case. + +Version 2.401 - 28 Jan 2002 + + - added the imcopy example program to the release (and Makefile) + + Bug fixes: + + - fixed typo in the imcompress code which affected compression + of 3D datacubes. + + - made small change to fficls (insert column) to allow colums with + TFORMn = '1PU' and '1PV' to be inserted in a binary table. The + 'U' and 'V' are codes only used within CFITSIO to represent unsigned + 16-bit and 32-bit integers; They get replaced by '1PI' and '1PJ' + respectively in the FITS table header, along with the appropriate + TZEROn keyword. + +Version 2.400 - 18 Jan 2002 + + (N.B.: Application programs must be recompiled, not just relinked + with the new CFITSIO library because of changes made to fitsio.h) + + New Routines: + + - fits_write_subset/ffpss writes a rectangular subset (or the whole + image) to a FITS image. + + - added a whole new family of routines to read and write arrays of + 'long long' integers (64-bit) to FITS images or table columns. The + new routine names all end in 'jj': ffpprjj, ffppnjj, ffp2djj, + ffp3djj, ffppssjj, ffpgpjj, ffpcljj, ffpcnjj. ffgpvjj, ffgpfjj, + ffg2djj, ffg3djj, ffgsvjj, ffgsfjj, ffggpjj, ffgcvjj, and ffgcfjj. + + - added a set of helper routines that are used in conjunction with + the new support for tiled image compression. 3 routines set the + parameters that should be used when CFITSIO compresses an image: + fits_set_compression_type + fits_set_tile_dim + fits_set_noise_bits + + 3 corresponding routines report back the current settings: + fits_get_compression_type + fits_get_tile_dim + fits_get_noise_bits + + Enhancements: + + - major enhancement was made to support writing to tile-compressed + images. In this format, the image is divided up into a rectangular + grid of tiles, and each tile of pixels is compressed individually + and stored in a row of a variable-length array column in a binary + table. CFITSIO has been able to transparently read this compressed + image format ever since version 2.1. Now all the CFITSIO image + writing routines also transparently support this format. There are + 2 ways to force CFITSIO to write compressed images: 1) call the + fits_set_compression_type routine before writing the image header + keywords, or 2), specify that the image should be compressed when + entering the name of the output FITS file, using a new extended + filename syntax. (examples: "myfile.fits[compress]" will use the + default compression parameters, and "myfile.fits[compress GZIP + 100,100] will use the GZIP compression algorithm with 100 x 100 + pixel tiles. + + - added new driver to support creating output .gz compressed fits + files. If the name of the output FITS file to be created ends with + '.gz' then CFITSIO will initially write the FITS file in memory and + then, when the FITS file is closed, CFITSIO will gzip the entire + file before writing it out to disk. + + - when over-writing vectors in a variable length array in a binary + table, if the new vector to be written is less than or equal to + the length of the previously written vector, then CFITSIO will now + reuse the existing space in the heap, rather than always appending + the new array to the end of the heap. + + - modified configure.in to support building cfitsio as a dynamic + library on Mac OS X. Use 'make shared' like on other UNIX platforms, + but a .dylib file will be created instead of .so. If installed in a + nonstandard location, add its location to the DYLD_LIBRARY_PATH + environment variable so that the library can be found at run time. + + - made various modifications to better support the 8-byte long integer + datatype on more platforms. The 'LONGLONG' datatype is typedef'ed + to equal 'long long' on most Unix platforms and MacOS, and equal + to '__int64' on Windows machines. + + - modified configure.in and makefile.in to better support cases + where the system has no Fortran compiler and thus the f77 wrapper + routines should not be compiled. + + - made small modification to eval.y and eval_y.f to get rid of warning + on some platforms about redefinition of the 'alloca'. + + Bug fixes: + + - other recent bug fixes in ffdblk (delete blocks) caused ffdhdu (delete + HDU) to fail when trying to replace the primary array with a null + primary array. + + - fixed bug that prevented inserting a new variable length column + into a table that already contained variable length data. + + - modified fits_delete_file so that it will delete the file even if + the input status value is not equal to zero. + + - in fits_resize_image, it was sometimes necessary to call ffrdef to + force the image structure to be defined. + + - modified the filename parser to support input files with names like: + "myfile.fits.gz(mem://tmp)" in which the url type is specified for + the output file but not for the input file itself. This required + modifications to ffiurl and ffrtnm. + +Version 2.301 - 7 Dec 2001 + + Enhancements: + + - modified the http file driver so that if the filename to be opened + contains a '?' character (most likely a cgi related string) then it + will not attempt to append a .gz or .Z as it would normally do. + + - added support for the '!' clobber character when specifying + the output disk file name in CFITSIO's extended filename syntax, e.g., + 'http://a.b.c.d/myfile.fits.gz(!outfile.fits)' + + - added new device driver which is used when opening a compressed FITS + file on disk by uncompressing it into memory with READWRITE + access. This happens when specifying an output filename + 'mem://'. + + - added 2 other device drivers to open http and ftp files in memory + with write access. + + - improved the error trapping and reporting in cases where program + attempts to write to a READONLY file (especially in cases where the + 'file' resides in memory, as is the case when opening an ftp or http + file. + + - modified the extended filename parser so that it is does not confuse + the bracket character '[' which is sometimes used in the root name + of files of type 'http://', as the start of an extname or row filter + expression. If the file is of type 'http://', the parser now + checks to see if the last character in the extended file name is + a ')' or ']'. If not, it does not try to parse the file name + any further. + + - improved the efficiency when writing FITS files in memory, by + initially allocating enough memory for the entire HDU when it is + created, rather than incrementally reallocing memory 2880 bytes + at a time (modified ffrhdu and mem_truncate). This change also + means that the program will fail much sooner if it cannot allocate + enough memory to hold the entire FITS HDU. + + Bug fixes: + + - There was an error in the definition of the Fortran ftphtb wrapper + routine (writes required ASCII table header keywords) that caused + it to fail on DEC OSF and other platforms where sizeof(long) = 8. + +Version 2.300 - 23 Oct 2001 + + New Routines: + + - fits_comp_img and fits_decomp_img are now fully supported and + documented. These routine compress and decompress, respective, + a FITS image using a new algorithm in which the image is first + divided into a grid of rectangular tiles, then the compressed byte + stream from each tile is stored in a row of a binary table. + CFITSIO can transparently read FITS images stored in this + compressed format. Compression ratios of 3 - 6 are typically + achieved. Large compression ratios are achieved for floating + point images by throwing away non-significant noise bits in the + pixel values. + + - fits_test_heap tests the integrity of the binary table heap and + returns statistics on the amount of unused space in the heap and + the amount of space that is pointed to by more than 1 descriptor. + + - fits_compress_heap which will reorder the arrays in the binary + table heap, recovering any unused space. + + Enhancements: + + - made substantial internal changes to the code to support FITS + files containing 64-bit integer data values. These files have + BITPIX = 64 or TFORMn = 'K'. This new feature in CFITSIO is + currently only enabled if SUPPORT_64BIT_INTEGERS is defined = 1 in + the beginning of the fitsio2.h file. By default support for + 64-bit integers is not enabled. + + - improved the ability to read and return a table column value as a + formatted string by supporting quasi-legal TDISPn values which + have a lowercase format code letter, and by completely ignoring + other unrecognizable TDISPn values. Previously, unrecognized + TDISPn values could cause zero length strings to be returned. + + - made fits_write_key_longstr more efficient when writing keywords + using the long string CONTINUE convention. It previously did not + use all the available space on each card when the string to be + written contained many single quote characters. + + - added a new "CFITSIO Quick Start Guide" which provides all the + basic information needed to write C programs using CFITSIO. + + - updated the standard COMMENT keywords that are written at the + beginning of every primary array to refer to the newly published + FITS Standard document in Astronomy and Astrophysics. + Note: because of this change, any FITS file created with this + version of CFITSIO will not be identical to the same file written + with a previous version of CFITSIO. + + - replaced the 2 routines in pliocomp.c with new versions provided by + D Tody and N Zarate. These routines compress/uncompress image pixels + using the IRAF pixel list compression algorithm. + + - modified fits_copy_hdu so that when copying a Primary Array + to an Image extension, the COMMENT cards which give the reference + to the A&A journal article about FITS are not copied. In the + inverse case the COMMENT keywords are inserted in the header. + + - modified configure and Makefile.in to add capability to build a + shared version of the CFITSIO library. Type 'make shared' or + 'make libcfitsio.so' to invoke this option. + + - disabled some uninformative error messages on the error stack: + 1) when calling ffclos (and then ffchdu) with input status > 0 + 2) when ffmahd tries to move beyond the end of file. + The returned status value remains the same as before, but the + annoying error messages no longer get written to the error stack. + + - The syntax for column filtering has been modified so that + if one only specifies a list of column names, then only those + columns will be copied into the output file. This provides a simple + way to make a copy of a table containing only a specified list of + columns. If the column specifier explicitly deletes a column, however, + than all the other columns will be copied to the filtered input + file, regardless of whether the columns were listed or not. + Similarly, if the expression specifies only a column to be modified + or created, then all the other columns in the table will be + copied. + + mytable.fit[1][col Time;Rate] - only the Time and Rate + columns will be copied to the filtered input file. + + mytable.fit[1][col -Time ] - all but the Time column are copied + to the filtered input file. + + mytable.fit[1][col Rate;-Time] - same as above. + + - changed a '#if defined' statement in f77_wrap.h and f77_wrap1.c + to support the fortran wrappers on 64-bit IBM/RS6000 systems + + - modified group.c so that when attaching one group (the child) to + another (the parent), check in each file for the existence of a + pointer to the other before adding the link. This is to prevent + multiple links from forming under all circumstances. + + - modified the filename parser to accept 'STDIN', 'stdin', + 'STDOUT' and 'stdout' in addition to '-' to mean read the + file from standard input or write to standard output. + + - Added support for reversing an axis when reading a subsection + of a compressed image using the extended filename syntax, as in + myfile.fits+1[-*, *] or myfile.fits+1[600:501,501:600] + + - When copying a compressed image to a uncompressed image, the + EXTNAME keyword is no longer copied if the value is equal to + 'COMPRESSED_IMAGE'. + + - slight change to the comment field of the DATE keyword to reflect + the fact that the Unix system date and time is not true UTC time. + + Bug fixes: + + - fits_write_key_longstr was not writing the keyword if a null + input string value was given. + + - writing data to a variable length column, if that binary table is not + the last HDU in the FITS file, might overwrite the following HDU. + Fixed this by changing the order of a couple operations in ffgcpr. + + - deleting a column from a table containing variable length columns + could cause the last few FITS blocks of the file to be reset = 0. + This bug occurred as a result of modifications to ffdblk in v2.202. + This mainly affects users of the 'compress_fits' utility + program. + + - fixed obscure problem when writing bits to a variable length 'B' + column. + + - when reading a subsection of an image, the BSCALE and BZERO pixel + scaling may not have been applied when reading image pixel values + (even though the scaling keywords were properly written in the + header). + + - fits_get_keyclass was not returning 'TYP_STRUCT_KEY' for the + END keyword. + +Version 2.204 - 26 July 2001 + + Bug fixes: + + - Re-write of fits_clean_url in group.c to solve various problems + with invalid bounds checking. + +Version 2.203 - 19 July 2001 (version in FTOOLS v5.1) + + Enhancements: + + - When a row selection or calculator expression is written in + an external file (and read by CFITSIO with the '@filename' syntax) + the file can now contain comment lines. The comment line must + begin with 2 slash characters as the first 2 characters on the + line. CFITSIO will ignore the entire line when reading the + expression. + + Bug fixes: + + - With previous versions of CFITSIO, the pixel values in a FITS + image could be read incorrectly in the following case: when + opening a subset of a FITS image (using the + 'filename.fits[Xmin:Xmax,Ymin:Ymax]' notation) on a PC linux, PC + Windows, or DEC OSF machine (but not on a SUN or Mac). This + problem only occurs when reading more than 8640 bytes of data + (2160 4-byte integers) at a time, and usually only occurs if the + reading program reads the pixel data immediately after opening the + file, without first reading any header keywords. This error would + cause strips of zero valued pixels to appear at semi-random + positions in the image, where each strip usually would be 2880 + bytes long. This problem does not affect cases where the input + subsetted image is simply copied to a new output FITS file. + + +Version 2.202 - 22 May 2001 + + Enhancements: + + - revised the logic in the routine that tests if a point is + within a region: if the first region is an excluded region, + then it implicitly assumes a prior include region covering + the entire detector. It also now supports cases where a + smaller include region is within a prior exclude region. + + - made enhancement to ffgclb (read bytes) so that it can + also read values from a logical column, returning an array + of 1s and 0s. + + - defined 2 new grouping error status values (349, 350) in + cfitsio.h and made minor changes to group.c to use these new + status values. + + - modified fits_open_file so that if it encounters an error while + trying to move to a user-specified extension (or select a subset + of the rows in an input table, or make a histogram of the + column values) it will close the input file instead of leaving + it open. + + - when using the extended filename syntax to filter the rows in + an input table, or create a histogram image from the values in + a table column, CFITSIO now writes HISTORY keywords in the + output file to document the filtering expression that was used. + + Bug fixes: + + - ffdblk (called by ffdrow) could overwrite the last FITS block(s) in + the file in some cases where one writes data to a variable length + column and then calls ffdrow to delete rows in the table. This + bug was similar to the ffiblk bug that was fixed in v2.033. + + - modified fits_write_col_null to fix a problem which under unusual + circumstances would cause a End-of-File error when trying to + read back the value in an ASCII string column, after initializing + if by writing a null value to it. + + - fixed obscure bug in the calculator function that caused an + error when trying to modify the value of a keyword in a HDU + that does not have a NAXIS2 keyword (e.g., a null primary array). + + - the iterator function (in putcol.c) had a bug when calculating + the optimum number rows to process in the case where the table + has very wide rows (>33120 bytes) and the calculator expression + involves columns from more than one FITS table. This could + cause an infinite loop in calls to the ffcalc calculator function. + + - fixed bug in ffmvec, which modifies the length of an + existing vector column in a binary table. If the vector + was reduced in length, the FITS file could sometimes be left + in a corrupted state, and in all cases the values in the remaining + vector elements of that column would be altered. + + - in drvrfile.c, replaced calls to fsetpos and fgetpos with + fseek and ftell (or fseeko and ftello) because the fpos_t + filetype used in fsetpos is incompatible with the off_t + filetype used in fseek, at least on some platforms (Linux 7.0). + (This fix was inserted into the V2.201 release on April 4). + + - added "#define fits_write_pixnull ffppxn" to longnam.h + +Version 2.201 - 15 March 2001 + + Enhancements + + - enhanced the keyword reading routines so that they will do + implicit datatype conversion from a string keyword value + to a numeric keyword value, if the string consist of a + valid number enclosed in quotes. For example, the keyword + mykey = '37.5' can be read by ffgkye. + + - modified ffiimg so that it is possible to insert a new + primary array at the beginning of the file. The original + primary array is then converted into an IMAGE extension. + + - modified ffcpdt (copy data unit) to support the case where + the data unit is being copied between 2 HDUs in the same file. + + - enhanced the fits_read_pix and fits_read_pixnull routines so + that they support the tiled image compression format that the + other image reading routines also support. + + - modified the Extended File Name syntax to also accept a + minus sign (-) as well as an exclamation point (!) as + the leading character when specifying a column or or keyword + to be deleted, as in [col -time] will delete the TIME column. + + - now completely support reading subimages, including pixel + increments in each dimension, for tile-compressed images + (where the compressed image tiles are stored in a binary + table). + + Bug fixes: + + - fixed confusion in the use of the fpos_t and off_t datatypes + in the fgetpos and fsetpos routines in drvrfile.c which caused + problems with the Windows VC++ compiler. (fpos_t is not + necessarily identical to off_t) + + - fixed a typo in the fits_get_url function in group.c which + caused problems when determining the relative URL to a compressed + FITS file. + + - included fitsio.h in the shared memory utility program, + smem.c, in order to define OFF_T. + + - fixed typo in the datatype of 'nullvalue' in ffgsvi, which caused + attempts to read subsections of a short integer tiled compressed + image to fail with a bus error. + + - fixed bug in ffdkey which sometimes worked incorrectly if one + tried to delete a nonexistent keyword beyond the end of the header. + + - fixed problem in fits_select_image_section when it writes a dummy + value to the last pixel of the section. If the image contains + scaled integer pixels, then in some cases the pixel value could end + up out of range. + + - fixed obscure bug in the ffpcn_ family of routines which gave + a floating exception when trying to write zero number of pixels to + a zero length array (why would anyone do this?) + +Version 2.200 - 26 Jan 2001 + + Enhancements + + - updated the region filtering code to support the latest region + file formats that are generated by the POW, SAOtng and ds9 + programs. Region positions may now be given in HH:MM:SS.s, + DD:MM:SS.s format, and region sizes may be given arcsec or arcmin + instead of only in pixel units. Also changed the logic so that if + multiple 'include' regions are specified in the region file, they + are ORed together, instead of ANDed, so that the filtering keeps + points that are located within any of the 'include' regions, not + just the intersection of the regions. + + - added support for reading raw binary data arrays by converting + them on the fly into virtual FITS files. + + - modified ffpmsg, which writes error messages to CFITSIO's internal + error stack, so that messages > 80 characters long will be wrapped + around into multiple 80 character messages, instead of just + being truncated at 80 characters. + + - modified the CFITSIO parser so that expression which involve + scaled integer columns get cast to double rather than int. + + - Modified the keyword template parsing routine, ffgthd, to + support the HIERARCH keyword. + + - modified ffainit and ffbinit so that they don't unnecessarily + allocate 0 bytes of memory if there are no columns (TFIELDS = 0) + in the table that is being opened. + + - modified fitsio2.h to support NetBSD on Alpha OSF platforms + (NetBSD does not define the '__unix__' symbol). + + - changed the way OFF_T is defined in fitsio.h for greater + portability. + + - changed drvrsmem.c so it is compiled only when HAVE_SHMEM_SERVICES + is defined in order to removed the conditional logic from the Makefile + + - reorganized the CFITSIO User's guide to make it + clearer and easier for new users to learn the basic routines. + + - fixed ffhdef (which reserves space for more header keywords) so + that is also updates the start position of the next HDU. This + affected the offset values returned by ffghof. + +Version 2.100 - 18 Oct 2000 + + Enhancements + + - made substantial modification to the code to support Large files, + i.e., files larger than 2**31 bytes = 2.1GB. FITS files up to + 6 terabytes in size may now be read and written on platforms + that support Large files (currently only Solaris). + + - modified ffpcom and ffphis, which write COMMENT and HISTORY + keywords, respectively, so that they now use columns 9 - 80, + instead of only columns 11 - 80. Previously, these routines + avoided using columns 9 and 10, but this is was unnecessarily + restrictive. + + - modified ffdhdu so that instead of refusing to delete the + primary array, it will replace the current primary array + with a null primary array containing the bare minimum of + required keywords and no data. + + New Routines + + - fits_read_pix, fits_read_pixnull, fits_read_subset, and fits_write_pix + routines were added to enable reading and writing of Large images, + with more than 2.1e9 pixels. These new routines are now recommended + as the basic routines for reading and writing all images. + + - fits_get_hduoff returns the byte offset in the file to + the start and end of the current HDU. This routine replaces the + now obsolete fits_get_hduaddr routine; it uses 'off_t' instead of + 'long' as the datatype of the arguments and can support offsets + in files greater than 2.1GB in size. + + Bug fixes: + + - fixed bug in fits_select_image_section that caused an integer + overflow when reading very large image sections (bigger than + 8192 x 8192 4-byte pixels). + + - improved ffptbb, the low-level table writing routine, so that + it will insert additional rows in the table if the table is + not already big enough. Previously it would have just over- + written any HDUs following the table in the FITS file. + + - fixed a bug in the fits_write_col_bit/ffpclx routine which + could not write to a bit 'X' column if that was the first column + in the table to be written to. This bug would not appear if + any other datatype column was written to first. + + - non-sensible (but still formally legal) binary table TFORM values + such as '8A15', or '1A8' or 'A8' would confuse CFITSIO and cause it + to return a 308 error. When parsing the TFORMn = 'rAw' value, + the ffbnfm routine has been modified to ignore the 'w' value in cases + where w > r. + + - fixed bug in the blsearch routine in iraffits.c which sometimes + caused an out-of-bounds string pointer to be returned when searching + for blank space in the header just before the 'END' keyword. + + - fixed minor problem in ffgtcr in group.c, which sometimes failed + while trying to move to the end of file before appending a + grouping table. + + - on Solaris, with Sun CC 5.0, one must check for '__unix' rather + than '__unix__' or 'unix' as it's symbol. Needed to modify this + in drvrfile.c in 3 places. + + - in ffextn, the FITS file would be left open if the named + extension doesn't exist, thus preventing the file from being + opened again later with write access. + + - fixed bug in ffiimg that would cause attempts to insert a new + image extension following a table extension, and in front of any + other type of extension, to fail. + +Version 2.037 - 6 July 2000 + + Enhancements + + - added support in the extended filename syntax for flipping + an image along any axis either by specifying a starting + section pixel number greater than the ending pixel number, + or by using '-*' to flip the whole axis. Examples: + "myfile.fits[1:100, 50:10]" or "myfile.fits[-*,*]". + + - when reading a section of an image with the extended filename + syntax (e.g. image.fits[1:100:2, 1:100:2), any CDi_j WCS keywords + will be updated if necessary to transfer the world coordinate + system from the input image to the output image section. + + - on UNIX platforms, added support for filenames that begin + with "~/" or "~user/". The "~" symbol will get expanded + into a string that gives the user's home directory. + + - changed the filename parser to support disk file names that + begin with a minus sign. Previously, the leading minus sign would + cause CFITSIO to try to read/write the file from/to stdin/stdout. + + - modified the general fits_update_key routine, which writes + or updates a keyword value, to use the 'G' display format + instead of the 'E' format for floating point keyword values. + This will eliminate trailing zeros from appearing in the value. + + - added support for the "-CAR" celestial coordinate projection + in the ffwldp and ffxypx routines. The "-CAR" projection is + the default simplest possible linear projection. + + - added new fits_create_memfile/ffimem routine to create a new + fits file at a designated memory location. + + - ported f77_wrap.h and f77_wrap1.c so that the Fortran interface + wrappers work correctly on 64-bit SGI operating systems. In this + environment, C 'long's are 8-bytes long, but Fortran 'integers' + are still only 4-bytes long, so the words have to be converted + by the wrappers. + + - minor modification to cfortran.h to automatically detect when it + is running on a linux platform, and then define f2cFortran in that + case. This eliminates the need to define -Df2cFortran on the + command line. + + - modified group.c to support multiple "/" characters in + the path name of the file to be opened/created. + + - minor modifications to the parser (eval.y, eval_f.c, eval_y.c) + to a) add the unary '+' operator, and b) support copying the + TDIMn keyword from the input to the output image under certain + circumstances. + + - modified the lexical parser in eval_l.y and eval_l.c to + support #NULL and #SNULL constants which act to set the + value to Null. Support was also added for the C-conditional + expression: 'Boolean ? trueVal : falseVal'. + + - small modification to eval_f.c to write an error message to + the error stack if numerical overflow occurs when evaluating + an expression. + + - configure and configure.in now support the egcs g77 compiler + on Linux platforms. + + Bug fixes: + + - fixed a significant bug when using the extended filename binning + syntax to generate a 2-dimensional image from a histogram of the + values in 2 table columns. This bug would cause table events that + should have been located in the row just below the bottom row of + the image (and thus should have been excluded from the histogram) + to be instead added into the first row of the image. Similarly, + the first plane of a 3-D or 4-D data cube would include the events + that should have been excluded as falling in the previous plane of + the cube. + + - fixed minor bug when parsing an extended filename that contains + nested pairs of square brackets (e.g., '[col newcol=oldcol[9]]'). + + - fixed bug when reading unsigned integer values from a table or + image with fits_read_col_uint/ffgcvuk. This bug only occurred on + systems like Digital Unix (now Tru64 Unix) in which 'long' + integers are 8 bytes long, and only when reading more than 7200 + elements at a time. This bug would generally cause the program to + crash with a segmentation fault. + + - modified ffgcpr to update 'heapstart' as well as 'numrows' when + writing more rows beyond the end of the table. heapstart + is needed to calculate if more space needs to be inserted in the + table when inserting columns into the table. + + - modified fficls (insert column), ffmvec, ffdrow and ffdcol to + not use the value of the NAXIS2 keyword as the number of rows + in the table, and instead use the value that is stored in + an internal structure, because the keyword value may not + be up to date. + + - Fixed bug in the iterator function that affected the handling + of null values in string columns in ASCII and binary tables. + + - Reading a subsample of pixels in very large images, (e.g., + file = myfile.fits[1:10000:10,1:10000:10], could cause a + long integer overflow (value > 2**31) in the computation of the + starting byte offset in the file, and cause a return error status + = 304 (negative byte address). This was fixed by changing the + order of the arithmetic operations in calculating the value of + 'readptr' in the ffgcli, ffgclj, ffgcle, ffgcld, etc. routines. + + - In version 2.031, a fix to prevent compressed files from being + opened with write privilege was implemented incorrectly. The fix + was intended to not allow a compressed FITS file to be opened + except when a local uncompressed copy of the file is being + produced (then the copy is opened with write access), but in fact + the opposite behavior occurred: Compressed files could be opened + with write access, EXCEPT when a local copy is produced. This + has been fixed in the mem_compress_open and file_compress_open + routines. + + - in iraffits.c, a global variable called 'val' caused multiply + defined symbols warning when linking cfitsio and IRAF libraries. + This was fixed by making 'val' a local variable within the + routine. + +Version 2.036 - 1 Feb 2000 + + - added 2 new generic routines, ffgpf and ffgcf which are analogous + to ffgpv and ffgcv but return an array of null flag values instead + of setting null pixels to a reserved value. + + - minor change to eval_y.c and eval.y to "define alloca malloc" + on all platforms, not just VMS. + + - added support for the unsigned int datatype (TUINT) in the + generic ffuky routine and changed ffpky so that unsigned ints + are cast to double instead of long before being written to + the header. + + - modified ffs2c so that if a null string is given as input then + a null FITS string (2 successive single quotes) will be returned. + Previously this routine would just return a string with a single + quote, which could cause an illegal keyword record to be written. + + - The file flush operation on Windows platforms apparently + changes the internal file position pointer (!) in violation of the + C standard. Put a patch into the file_flush routine to explicitly + seek back to the original file position. + + - changed the name of imcomp_get_compressed_image_parms to + imcomp_get_compressed_image_par to not exceed the 31 character + limit on some compilers. + + - modified the filename parser (which is used when moving to a + named HDU) to support EXTNAME values which contain embedded blanks. + + - modified drvrnet.c to deal with ftp compressed files better so + that even fits files returned from cgi queries which have the wrong + mime types and/or wrong types of file names should still decompress. + + - modified ffgics to reduce the tolerance for acceptable skewness + between the axes, and added a new warning return status = + APPROX_WCS_KEY in cases where there is significant skewness + between the axes. + + - fixed bug in ffgics that affected cases where the first coordinate + axis was DEC, not RA, and the image was a mirror image of the sky. + + - fixed bug in ffhist when trying to read the default binning + factor keyword, TDBIN. + + - modified ffhist so that is correctly computes the rotation angle + in a 2-D image if the first histogram column has a CROTA type + keyword but the 2nd column does not. + + - modified ffcpcl so that it preserves the comment fields on the + TTYPE and TFORM keywords when the column is copied to a new file. + + - make small change to configure.in to support FreeBSD Linux + by setting CFLAGS = -Df2cFortran instead of -Dg77Fortran. Then + regenerated configure with autoconf 2.13 instead of 2.12. + +Version 2.035 - 7 Dec 1999 (internal release only, FTOOLS 5.0.2) + + - added new routine called fits_get_keyclass/ffgkcl that returns + the general class of the keyword, e.g., required structural + keyword, WCS keyword, Comment keyword, etc. 15 classes of + keywords have been defined in fitsio.h + + - added new routine called fits_get_img_parm/ffgipr that is similar + to ffgphd but it only return the bitpix, naxis, and naxisn values. + + - added 3 new routines that support the long string keyword + convention: fits_insert_key_longstr, fits_modify_key_longstr + fits_update_key_longstr. + + - modified ffgphd which reads image header keywords to support + the new experimental compressed image format. + + - when opening a .Z compressed file, CFITSIO tries to allocate + memory equal to 3 times the file size, which may be excessive + in some cases. This was changed so that if the allocation fails, + then CFITSIO will try again to allocate only enough memory + equal to 1 times the file size. More memory will be allocated + later if this turns out to be too small. + + - improved the error checking in the fits_insert_key routine + to check for illegal characters in the keyword. + +Version 2.034 - 23 Nov 1999 + + - enhanced support for the new 'CD' matrix world coordinate system + keywords in the ffigics routine. This routine has been enhanced + to look for the new 'CD' keywords, if present, and convert them + back to the old CDELTn and CROTAn values, which are then returned. + The routine will also swap the WCS parameters for the 2 axes if + the declination-like axis is the first WCS axis. + + - modified ffphbn in putkey.c to support the 'U' and 'V" TFORM characters + (which represent unsigned short and unsigned int columns) in variable + length array columns. (previously only supported these types in + fixed length columns). + + - added checks when reading gzipped files to detect unexpected EOF. + Previously, the 'inflate_codes' routine would just sit in an infinite + loop if the file ended unexpectedly. + + - modified fits_verify_chksum/ffvcks so that checksum keywords with + a blank value string are treated as undefined, the same as + if the keyword did not exist at all. + + - fixed ffghtb and ffghbn so that they return the extname value + in cases where there are no columns in the table. + + - fixed bug in the ffgtwcs routine (this is a little utility + routine to aid in interfacing to Doug Mink's WCS routines); + it was not correctly padding the length of string-valued keywords + in the returned string. + + - fixed bug in 'iraffits.c' that prevented Type-2 IRAF images from + being correctly byte-swapped on PCs and DEC-OSF machines. + + - fixed tiny memory leak in irafncmp in iraffits.c. Only relevant when + reading IRAF .imh files. + + - fixed a bug (introduced in version 2.027) that caused the keyword + reading routines to sometimes not find a matching keyword if the + input name template used the '*' wildcard as the last character. + (e.g., if input name = 'COMMENT*' then it would not find the + 'COMMENT' keywords. (It would have found longer keywords like + 'COMMENTX' correctly). The fix required a minor change to ffgcrd + in getkey.c + + - modified the routine (ffswap8) that does byteswapping of + double precision numbers. Some linux systems have reported floating + point exceptions because they were trying to interpret the bytes + as a double before the bytes had been swapped. + + - fixed bug in the calculation of the position of the last byte + in the string of bits to be read in ffgcxuk and ffgcxui. This + bug generally caused no harm, but could cause the routine to + exit with an invalid error message about trying to read + beyond the size of the field. + + - If a unix machine did not have '__unix__', 'unix', or '__unix' + C preprocessor symbols defined, then CFITSIO would correctly open + one FITS file, but would not correctly open subsequent files. Instead + it would think that the same file was being opened multiple times. + This problem has only been seen on an IBM/AIX machine. The fits_path2url + and fits_url2path routines in group.c were modified to fix the problem. + + - fixed bug in group.c, which affected WINDOWS platforms only, that + caused programs to go into infinite loop when trying to open + certain files. + + - the ftrsim Fortran wrapper routine to ffrsim was not defined + correctly, which caused the naxis(2) value to be passed incorrectly + on Dec OSF machines, where sizeof(long) != sizeof(int). + +Version 2.033 - 17 Sept 1999 + + - New Feature: enhanced the row selection parser so that comparisons + between values in different rows of the table are allowed, and the + string comparisons with <, >, <=, and >= are supported. + + - added new routine the returns the name of the keyword in the + input keyword record string. The name is usually the first + 8 characters of the record, except if the HIERARCH convention + is being used in which case the name may be up to 67 characters + long. + + - added new routine called fits_null_check/ffnchk that checks to + see if the current header contains any null (ASCII 0) characters. + These characters are illegal in FITS headers, but they go undetected + by the other CFITSIO routines that read the header keywords. + + - the group.c file has been replaced with a new version as supplied + by the ISDC. The changes are mainly to support partial URLs and + absolute URLs more robustly. Host dependent directory paths are + now converted to true URLs before being read from/written to + grouping tables. + + - modified ffnmhd slightly so that it will move to the first extension + in which either the EXTNAME or the HDUNAME keyword is equal to the + user-specified name. Previously, it only checked for HDUNAME if + the EXTNAME keyword did not exist. + + - made small change to drvrnet.c so that it uncompress files + which end in .Z and .gz just as for ftp files. + + - rewrote ffcphd (copy header) to handle the case where the + input and output HDU are in the same physical FITS file. + + - fixed bug in how long string keyword values (using the CONTINUE + convention) were read. If the string keyword value ended in an + '&' character, then fits_read_key_longstr, fits_modify_key_str, + and fits_delete_key would interpret the following keyword as + a continuation, regardless of whether that keyword name was + 'CONTINUE' as required by this convention. There was also a bug + in that if the string keyword value was all blanks, then + fits_modify_key_str could in certain unusual cases think + that the keyword ended in an '&' and go into an infinite loop. + + - modified ffgpv so that it calls the higher level ffgpv_ routine + rather than directly calling the lower level ffgcl_ routine. This + change is needed to eventually support reading compressed images. + + - added 3 new routines to get the image datatype, image dimensions, + and image axes length. These support the case where the image is + compressed and stored in a binary table. + + - fixed bug in ffiblk that could sometimes cause it to insert a + new block in a file somewhere in the middle of the data, instead + of at the end of the HDU. This fortunately is a rare problem, + mainly only occurring in certain cases when inserting rows in a binary + table that contains variable length array data (i.e., has a heap). + + - modified fits_write_tdim so that it double checks the TFORMn + value directly if the column repeat count stored in the internal + structure is not equal to the product of all the dimensions. + + - fixed bug that prevented ffitab or ffibin from inserting a new + table after a null primary array (can't read NAXIS2 keyword). + Required a small change to ffrdef. + + - modified testprog.c so that it will continue to run even if + it cannot open or process the template file testprog.tpt. + + - modified the logic in lines 1182-1185 of grparser.c so that + it returns the correct status value in case of an error. + + - added test in fitsio2.h to see if __sparcv9 is defined; this + identifies a machine running Solaris 7 in 64-bit mode where + long integers are 64 bits long. + +Version 2.032 - 25 May 1999 + + - the distribution .tar file was changed so that all the files + will be untarred into a subdirectory by default instead of + into the current directory. + + - modified ffclos so that it always frees the space allocated by + the fptr pointer, even when another fptr points to the same file. + + - plugged a potential (but rare in practice) memory leak in ffpinit + + - fixed bug in all the ffp3d_ and ffg3d_ routines in cases where + the data cube that has been allocated in memory has more planes + than the data cube in the FITS file. + + - modified drvrsmem.c so that it allocates a small shared + memory segment only if CFITSIO tries to read or write a + FITS file in shared memory. Previously it always allocated + the segment whether it was needed or not. Also, this small + segment is removed if 0 shared memory segments remain in + the system. + + - put "static" in front of 7 DECLARE macros in compress.c + because these global variables were causing conflicts with other + applications programs that had variables with the same names. + + - modified ffasfm to return datatype = TDOUBLE instead of TFLOAT + if the ASCII table column has TFORMn = 'Ew.d' with d > 6. + + - modified the column reading routines to a) print out the offending + entry if an error occurs when trying to read a numeric ASCII table + column, and b) print out the column number that had the error + (the messages are written to CFITSIOs error stack) + + - major updates to the Fortran FITSIO User's Guide to include many + new functions that have been added to CFITSIO in the past year. + + - modified fitsio2.h so that the test for __D_FLOAT etc. is only + made on Alpha VMS machines, to avoid syntax errors on some other + platforms. + + - modified ffgthd so that it recognizes a floating point value + that uses the 'd' or 'D' exponent character. + + - removed the range check in fftm2s that returned an error if + 'decimals' was less than zero. A negative value is OK and is + used to return only the date and not the time in the string. + +Version 2.031 - 31 Mar 1999 + + - moved the code that updates the NAXIS2 and PCOUNT keywords from + ffchdu into the lower lever ffrdef routine. This ensures that + other routines which call ffrdef will correctly update these 2 + keywords if required. Otherwise, for instance, calling + fits_write_checksum before closing the HDU could cause the NAXIS2 + keyword (number of rows in the table) to not be updated. + + - fixed bug (introduced in version 2.030) when writing null values + to a primary array or image extension. If trying to set more + than 1 pixel to null at a time, then typically only 1 null would + be written. Also fixed related bug when writing null values to + rows in a table that are beyond the currently defined size of the + table (the size of the table was not being expanded properly). + + - enhanced the extended filename parser to support '*' in image + section specifiers, to mean use the whole range of the axis. + myfile.fits[*,1:100] means use the whole range of the first + axis and pixels 1 to 100 in the second axis. Also supports + an increment, as in myfile.fits[*:2, *:2] to use just the + odd numbered rows and columns. + + - modified fitscore.c to set the initial max size of the header, when + first reading it, to the current size of the file, rather than to + 2 x 10**9 to avoid rare cases where CFITSIO ends up writing a huge + file to disk. + + - modified file_compress_open so that it will not allow a compressed + FITS file to be opened with write access. Otherwise, a program + could write to the temporary copy of the uncompressed file, but + the modification would be lost when the program exits. + +Version 2.030 - 24 Feb 1999 + + - fixed bug in ffpclu when trying to write a null value to a row + beyond the current size of the table (wouldn't append new rows + like it should). + + - major new feature: enhanced the routines that read ASCII string + columns in tables so that they can read any table column, including + logical and numeric valued columns. The column values are returned + as a formatted string. The format is determined by the TDISPn + keyword if present, otherwise a default format based on the + datatype of the column is used. + + - new routine: fits_get_col_display_width/ffgcdw returns the length + of the formatted strings that will be returned by the routines that + read table columns as strings. + + - major new feature: added support for specifying an 'image section' + when opening an image: e.g, myfile.fits[1:512:2,2:512:2] to + open a 256x256 pixel image consisting of the odd columns and the + even numbered rows of the input image. + + - added supporting project files and instructions for building + CFITSIO under Windows NT with the Microsoft Visual C++ compiler. + + - changed the variable 'template' to 'templt' in testprog.c since + it conflicted with a reserved word on some compilers. + + - modified group.c to conditionally include sys/stat.h only on + unix platforms + + - fixed bug in the ffiter iterator function that caused it to always + pass 'firstn' = 1 to the work function when reading from the + primary array or IMAGE extension. It worked correctly for tables. + + - fixed bug in the template header keyword parser (ffgthd) in cases + where the input template line contains a logical valued keyword + (T or F) without any following comment string. It was previously + interpreting this as a string-valued keyword. + + - modified ffrhdu that reads and opens a new HDU, so that it + ignores any leading blank characters in the XTENSION name, e.g., + XTENSION= ' BINTABLE' will not cause any errors, even though + this technically violates the FITS Standard. + + - modified ffgtbp that reads the required table keywords to make + it more lenient and not exit with an error if the THEAP keyword + in binary tables cannot be read as an integer. Now it will + simply ignore this keyword if it cannot be read. + + - added test for 'WIN32' as well as '__WIN32__' in fitsio2.h, + eval.l and eval_l.c in a preprocessor statement. + + - changed definition of strcasecmp and strncasecmp in fitsio2.h, + eval.l and eval_l.c to conform to the function prototypes under + the Alpha VMS v7.1 compiler. + + - corrected the long function names in longnam.h for the new WCS + utility functions in wcssubs.c + +Version 2.029 - 11 Feb 1999 + + - fixed bug in the way NANs and underflows were being detected on + VAX and Alpha VMS machines. + + - enhanced the filename parser to distinguish between a VMS-style + directory name (e.g. disk:[directory]myfile.fits) and a CFITSIO + filter specifier at the end of the name. + + - modified ffgthd to support the HIERARCH convention for keyword + names that are longer than 8 characters or contain characters + that would be illegal in standard FITS keyword names. + + - modified the include statements in grparser.c so that malloc.h + and memory.h are only included on the few platforms that really + need them. + + - modified the file_read routine in drvrfile.c to ignore the last + record in the FITS file it it only contains a single character that + is equal to 0, 10 or 32. Text editors sometimes append a character + like this to the end of the file, so CFITSIO will ignore it and + treat it as if it had reached the end of file. + + - minor modifications to fitsio.h to help support the ROOT environment. + + - installed new version of group.c and group.h; the main change + is to support relative paths (e.g. "../filename") in the URLs + + - modified the histogramming routines so that it looks for the + default preferred column axes in a keyword of the form + CPREF = 'Xcol, Ycol' + instead of separate keywords of the form + CPREF1 = 'Xcol' + CPREF2 = 'Ycol' + + - fixed bug so that if the binning spec is just a single integer, + as in [bin 4] then this will be interpreted as meaning to make + a 2D histogram using the preferred or default axes, with the + integer taken as the binning factor in both axes. + +Version 2.028 - 27 Jan 1999 + + - if the TNULLn keyword value was outside the range of a 'I' or 'B' + column, an overflow would occur when setting the short or char + to the TNULLn value, leading to incorrect values being flagged as + being undefined. This has been fixed so that CFITSIO will ignore + TNULLn values that are beyond the range of the column data type. + + - changed a few instances of the string {"\0"} to {'\0'} in the + file groups.c + + - installed new version of the grparser.c file from the ISDC + + - added new WCS support routines (in wcssub.c) which make it easier + to call Doug Mink's WCSlib routines for converting between plate + and sky coordinates. The CFITSIO routines themselves never + call a WCSlib routine, so CFITSIO is not dependent on WCSlib. + + - modified ffopen so that if you use the extended filename + syntax to both select rows in a table and then bin columns into + a histogram, then CFITSIO will simply construct an array listing + the good row numbers to be used when making the histogram, + instead of making a whole new temporary FITS file containing + the selected rows. + + - modified ffgphd which parses the primary array header keywords + when opening a file, to not choke on minor format errors in + optional keywords. Otherwise, this prevents CFITSIO from + even opening the file. + + - changed a few more variable declarations in compress.c from global + to static. + +Version 2.027 - 12 Jan 1999 + + - modified the usage of the output filename specifier so that it, + a) gives the name of the binned image, if specified, else, + b) gives the name of column filtered and/or row filtered table, if + specified, else + c) is the name for a local copy of the ftp or http file, else, + d) is the name for the local uncompressed version of the compressed + FITS file, else, + e) the output filename is ignored. + + - fixed minor bug in ffcmps, when comparing 2 strings while using + a '*' wild card character. + + - fixed bug in ftgthd that affected cases where the template string + started with a minus sign and contained 2 tokens (to rename a + keyword). + + - added support for the HIERARCH keyword convention for reading + and writing keywords longer than 8 characters or that contain + ASCII characters not allowed in normal FITS keywords. + + - modified the extended filename syntax to support opening images + that are contained in a single cell of a binary table with syntax: + filename.fits[extname; col_name(row_expression)] + +Version 2.026 - 23 Dec 1998 + + - modified the group parser to: + a) support CFITSIO_INCLUDE_FILES environment variable, which can + point to the location of template files, and, + b) the FITS file parameter passed to the parser no longer has to point + to an empty file. If there are already HDUs in the file, then the + parser appends new HDUs to the end of the file. + + - make a small change to the drvrnet.c file to accommodate creating + a static version of the CFITSIO library. + + - added 2 new routines to read consecutive bits as an unsigned integer + from a Bit 'X' or Byte 'B' column (ffgcxui and ffgcxuk). + + - modified the logic for determining histogram boundaries in ffhisto + to add one more bin by default, to catch values that are right on + the upper boundary of the histogram, or are in the last partial bin. + + - modified cfitsio2.h to support the new Solaris 7 64-bit mode operating + system. + + - Add utility routine, CFits2Unit, to the Fortran wrappers which searches + the gFitsFiles array for a fptr, returning its element (Fortran unit + number), or allocating a new element if one doesn't already + exists... for C calling Fortran calling CFITSIO. + + - modified configure so that it does not use the compiler optimizer + when using gcc 2.8.x on Linux + + - (re)added the fitsio.* documentation files that describe the + Fortran-callable FITSIO interface to the C routines. + + - modified the lexical parser in eval_f.c to fix bug in null detections + and bug in ffsrow when nrows = 0. + + - modified ffcalc so that it creates a TNULLn keyword if appropriate + when a new column is created. Also fixed detection of OVERFLOWs + so that it ignores null values. + + - added hyperbolic trig and rounding functions to + the lexical parser in the eval* files. + + - improved error message that gets written when the group number is + out of range when reading a 'random groups' array. + + - added description of shared memory, grouping, and template parsing + error messages to ffgerr and to the User's Guide. Moved the error + code definitions from drvsmem.h to fitsio.h. + + - modified grparser.c to compile correctly on Alpha/OSF machines + + - modified drvrnet.c to eliminate compiler warnings + + - Modified Makefile.in to include targets for building all the sample + programs that are included with CFITSIO. + +Version 2.025 - 1 Dec 1998 + + - modified ffgphd and ffgtbp so that they ignores BLANK and TNULLn keywords + that do not have a valid integer value. Also, any error while reading + the BSCALE, BZERO, TSCALn, or TZEROn keywords will be ignored. + Previously, CFITSIO would have simply refused to read an HDU that had + such an invalid keyword. + + - modified the parser in eval_f.c to accept out of order times in GTIs + + - updated cfitsio_mac.sit.hqx to fix bad target parameters for Mac's + speed test program + + - modified template parser in grparser.c to: 1) not write GRPNAME keyword + twice, and 2) assign correct value for EXTVERS keyword. + + - fixed minor bugs in group.c; mainly would only affect users of the + INTEGRAL Data Access Layer. + + - temporarily removed the prototype for ffiwcs from fitsio.h until + full WCS support is added to CFITSIO in the near future. + + - modified the HTTP driver to send a User-Agent string: + HEASARC/CFITSIO/ + + - declared local variables in compress.c as 'static' to avoid + conflicts with other libraries. + +Version 2.024 - 9 Nov 1998 + + - added new function fits_url_type which returns the driver prefix string + associated with a particular FITS file pointer. + +Version 2.023 - 1 Nov 1998 - first full release of CFITSIO 2.0 + + - slightly modified the way real keyword values are formatted, to ensure + that it includes a decimal point. E.g., '1.0E-09' instead of '1E-09' + + - added new function to support template files when creating new FITS files. + + - support the TCROTn WCS keyword in tables, when reading the WCS keywords. + + - modified the iterator to support null values in logical columns in + binary tables. + + - fixed bug in iterator to support null values in integer columns in + ASCII tables. + + - changed the values for FLOATNULLVALUE and DOUBLENULLVALUE to make them + less likely to duplicate actual values in the data. + + - fixed major bug when freeing memory in the iterator function. It caused + mysterious crashes on a few platforms, but had no effect on most others. + + - added support for reading IRAF format image (.imh files) + + - added more error checking to return an error if the size of the FITS + file exceeds the largest value of a long integer (2.1 GB on 32-bit + platforms). + + - CFITSIO now will automatically insert space for additional table rows + or add space to the data heap, if one writes beyond the current end + of the table or heap. This prevents any HDUs which might follow + the current HDU from being overwritten. It is thus no longer necessary + to explicitly call fits_insert_rows before writing new rows of data + to the FITS file. + + - CFITSIO now automatically keeps track of the number of rows that have + been written to a FITS table, and updates the NAXIS2 keyword accordingly + when the table is closed. It is no longer necessary for the application + program to updated NAXIS2. + + - When reading from a FITS table, CFITSIO will now return an error if the + application tries to read beyond the end of the table. + + - added 2 routines to get the number of rows or columns in a table. + + - improved the undocumented feature that allows a '20A' column to be + read as though it were a '20B' column by fits_read_col_byt. + + - added overflow error checking when reading keywords. Previously, the + returned value could be silently truncated to the maximum allowed value + for that data type. Now an error status is returned whenever an + overflow occurs. + + - added new set of routines dealing with hierarchical groups of files. + These were provided by Don Jennings of the INTEGRAL Science Data Center. + + - added new URL parsing routines. + + - changed the calling sequence to ffghad (get HDU address) from + ffghad(fitsfile *fptr, > long *headstart, long *dataend) to + ffghad(fitsfile *fptr, > long *headstart, long datastart, + long *dataend, int *status) + + - major modification to support opening the same FITS file more + than once. Now one can open the same file multiple times and + read and write simultaneously to different HDUs within the file. + fits_open_file automatically detects if the file is already opened. + + - added the ability to clobber/overwrite an existing file + with the same name when creating a new output file. Just + precede the output file name with '!' (an exclamation mark) + + - changed the ffpdat routine which writes the DATE keyword + to use the new 'YYYY-MM-DDThh:mm:ss' format. + + - added several new routines to create or parse the new date/time + format string. + + - changed ifdef for DECFortran in f77_wrap.h and f77_wrap1.c: + expanded to recognize Linux/Alpha + + - added new lexical parsing routines (from Peter Wilson): + eval_l.c, eval_y.c, eval_f.c, eval_defs.h, and eval_tab.h. + These are used when doing on-the-fly table row selections. + + - added new family of routines to support reading and writing + 'unsigned int' data type values in keywords, images or tables. + + - restructured all the putcol and getcol routines to provide + simpler and more robust support for machines which have + sizeof(long) = 8. Defined a new datatype INT32BIT which is + always 32 bits long (platform independent) and is used internally + in CFITSIO when reading or writing BITPIX = 32 images or 'J' + columns. This eliminated the need for specialize routines like + ffswaplong, ffunswaplong, and ffpacklong. + + - overhauled cfileio.c (and other files) to use loadable drivers for + doing data I/O to different devices. Now CFITSIO support network + access to ftp:// and http:// files, and to shared memory files. + + - removed the ffsmem routine and replaced it with ffomem. This will + only affect software that reads an existing file in core memory. + (written there by some other process). + + - modified all the ffgkn[] routines (get an array of keywords) so + that the 'nfound' parameter is = the number of keywords returned, + not the highest index value on the returned keywords. This makes + no difference if the starting index value to look for = 1. + This change is not backward compatible with previous versions + of CFITSIO, but is the way that FITSIO behaved. + + - added new error code = 1 for any application error external + to CFITSIO. Also reports "unknown error status" if the + value doesn't match a known CFITSIO error. + +Version 1.42 - 30 April 1998 (included in FTOOLS 4.1 release) + + - modified the routines which read a FITS float values into + a float array, or read FITS double values into a double array, + so that the array value is also explicitly set in addition + to setting the array of flag values, if the FITS value is a NaN. + This ensures that no NaN values get passed back to the calling + program, which can cause serious problems on some platforms (OSF). + + - added calls to ffrdef at the beginning of the insert + or delete rows or columns routines in editcol.c to make sure + that CFITSIO has correctly initialized the HDU information. + + - added new routine ffdrws to delete a list of rows in a table + + - added ffcphd to copy the header keywords from one hdu to another + + - made the anynul parameter in the ffgcl* routines optional + by first checking to see if the pointer is not null before + initializing it. + + - modified ffbinit and ffainit to ignore minor format + errors in header keywords so that cfitsio can at least + move to an extension that contains illegal keywords. + + - modified all the ffgcl* routines to simply return without + error if nelem = 0. + + - added check to ffclose to check the validity of the fitsfile + pointer before closing it. This should prevent program crashes + when someone tries to close the same file more than once. + + - replaced calls to strcmp and strncmp with macros FSTRCMP and + FSTRNCMP in a few places to improve performance when reading + header keywords (suggested by Mike Noble) + + Bug Fixes: + + - fixed typo in macro definition of error 504 in the file fitsio.h. + + - in ffopen, reserved space for 4 more characters in the input + file name in case a '.zip' suffix needs to be added. + + - small changes to ffpclx to fix problems when writing bit (X) data + columns beyond the current end of file. + + - fixed small bug in ffcrhd where a dummy pointer was not initialized + + - initialized the dummy variable in ffgcfe and ffgcfd which + was causing crashes under OSF in some cases. + + - increased the length of the allocated string ffgkls by 2 + to support the case of reading a numeric keyword as a string + which doesn't have the enclosing quote characters. + +Version 1.4 - 6 Feb 1998 + + - major restructuring of the CFITSIO User's Guide + + - added the new 'iterator' function. The fortran wrapper is + in f77_iter.c for now. + + - enhanced ffcrtb so that it writes a dummy primary array + if none currently exists before appending the table. + + - removed the ffgcl routine and replaced it with ffgcvl + + - modified ffpcnl to just take a single input null value instead + of an entire array of null value flags. + + - modified ffcmps and ffgnxk so that, for example, the string 'rate' + is not considered a match to the string 'rate2', and 'rate*' + is a match to the string 'rate'. + + - modified ffgrsz to also work with images, in which case + it returns the optimum number of pixels to process at + one time. + + - modified ffgthd to support null valued keywords + + - added a new source file 'f77_wrap.c' that includes all the + Fortran77 wrapper routines for calling CFITSIO. This will + eventually replace the Fortran FITSIO library. + + - added new routines: + ffppn - generic write primary array with null values + ffpprn - write null values to primary array + + ffuky - 'update' a keyword value, with any specified datatype. + + ffrprt - write out report of error status and error messages + ffiter - apply a user function iteratively to all the rows of a table + ffpkyc - write complex-valued keyword + ffpkym - write double complex-valued keyword + ffpkfc - write complex-valued keyword in fixed format + ffpkfm - write double complex-valued keyword in fixed format + + ffgkyc - read complex-valued keyword + ffgkym - read double complex-valued keyword + + ffmkyc - modify complex-valued keyword + ffmkym - modify double complex-valued keyword + ffmkfc - modify complex-valued keyword in fixed format + ffmkfm - modify double complex-valued keyword in fixed format + + ffukyc - update complex-valued keyword + ffukym - update double complex-valued keyword + ffukfc - update complex-valued keyword in fixed format + ffukfm - update double complex-valued keyword in fixed format + + ffikyc - insert complex-valued keyword + ffikym - insert double complex-valued keyword + ffikfc - insert complex-valued keyword in fixed format + ffikfm - insert double complex-valued keyword in fixed format + + ffpktp - write or modify keywords using ASCII template file + ffcpcl - copy a column from one table to another + ffcpky - copy an indexed keyword from one HDU to another + ffpcnl - write logical values, including nulls, to binary table + ffpcns - write string values, including nulls, to table + ffmnhd - move to HDU with given exttype, EXTNAME and EXTVERS values + ffthdu - return the total number of HDUs in the file + ffghdt - return the type of the CHDU + ffflnm - return the name of the open FITS file + ffflmd - return the mode of the file (READONLY or READWRITE) + + - modified ffmahd and ffmrhd (to move to a new extension) so that + a null pointer may be given for the returned HDUTYPE argument. + + - worked around a bug in the Mac CWpro2 compiler by changing all + the statements like "#if BYTESWAPPED == TRUE" to "if BYTESWAPPED". + + - modified ffitab (insert new ASCII table) to allow tables with + zero number of columns + + - modified Makefile.in and configure to define the -Dg77Fortran + CFLAGS variable on Linux platforms. This is needed to + compile the new f77_wrap.c file (which includes cfortran.h) + + Bug Fixes: + + - fixed small bug in ffgrz (get optimum row size) which sometimes + caused it to return slightly less than the maximum optimum size. + This bug would have done no harm to application programs. + + - fixed bug in ffpclk and ffgclk to add an 'else' case + if size of int is not equal to size of short or size of long. + + - added test to ffgkls to check if the input string is not null before + allocating memory for it. + +Version 1.32 - 21 November 1997 (internal release only) + + - fixed bug in the memory deallocation (free) statements + in the ffopen routine in the cfileio.c file. + + - modified ffgphd to tolerate minor violations of the FITS + standard in the format of the XTENSION = 'IMAGE ' + keyword when reading FITS files. Extra trailing spaces + are now allowed in the keyword value. (FITS standard + will be changed so that this is not a violation). + +Version 1.31 - 4 November 1997 (internal release only) + + Enhancements: + + - added support for directly reading compressed FITS files + by copying the algorithms from the gzip program. This + supports the Unix compress, gzip and pkzip algorithms. + + - modified ffiimg, ffitab, and ffibin (insert HDUs into + a FITS file) so that if the inserted HDU is at the end of + the FITS file, then it simply appends a new empty HDU + and writes the required keywords. This allows space + to be reserved for additional keywords in the header + if desired. + + - added the ffchfl and ffcdfl routines to check the header and + data fill values, for compatibility with the Fortran FITSIO + library. + + - added the ffgsdt routine to return the system date + for compatibility with the Fortran FITSIO library. + + - added a diagnostic error message (written to the error stack) + if the routines that read data from image or column fail. + + - modified ffgclb so that it simply copies the bytes from + an ASCII 'nA' or 'An' format column into the user's byte + array. Previously, CFITSIO would return an error when + trying to read an 'A' column with ffgclb. + + - modified ffpclb so that it simply copies the input array + of bytes to an ASCII 'nA' or 'An' format column. + Previously, CFITSIO would return an error when + trying to write to an 'A' column with ffpclb. + + Bug Fixes: + + - ffgkls was allocating one too few bytes when reading continued + string keyword values. + + - in testprog.c added code to properly free the memory that + had been allocated for string arrays. + + - corrected typographical errors in the User's Guide. + +Version 1.30 - 11 September 1997 + + - major overhaul to support reading and writing FITS files + in memory. The new routines fits_set_mem_buff and + fits_write_mem_buff have been added to initialize and + copy out the memory buffer, respectively. + + - added support for reading FITS files piped in on 'stdin' + and piped out on 'stdout'. Just specify the file name as '-' + when opening or creating the FITS file. + + - added support for 64-bit SGI IRIX machines. This required + adding routines to pack and unpack 32-bit integers into + 64-bit integers. + + - cleaned up the code that supports G_FLOAT and IEEE_FLOAT + on Alpha VMS systems. Now, the type of float is determined + at compile time, not run time. + + Bug Fixes: + + - replaced the malloc calls in the error message stack routines + with a static fixed size array. The malloc's cause more + problems than they solved, and were prone to cause memory + leaks if users don't clear the error message stack when + closing the FITS file. + + - when writing float or double keywords, test that the value + is not a special IEEE value such as a NaN. Some + compilers would write the string 'NaN' in this case into + the output value string. + + - fixed bug in ffiblk, to ignore EOF status return if it is + inserting blocks at the end of the file. + + - removed the 'l' from printf format string that is constructed + in the ffcfmt routine. This 'l' is non-standard and causes problems + with the Metrowerks compiler on a Mac. + + - the default null value in images was mistakenly being set + equal to NO_NULL = 314, rather than NULL_UNDEFINED = 1234554321 + in the ffgphd routine. + + - check status value in ffgkls to make sure the keyword exists + before allocating memory for the value string. + + - fixed the support for writing and reading unsigned long integer + keyword values in ffpky and ffgky by internally treating + the values as doubles. This required changes to ffc2r and + ffc2d as well. + + - added explicit cast to 'double' in one place in putcolb.c and + 6 places in pubcolui.c, to get rid of warning messages issued + by one compiler. + + - in ffbinit and ffainit, it is necessary to test that tfield > 0 + before trying to allocate memory with calloc. Otherwise, some + compilers return a null pointer which CFITSIO interprets to + mean the memory allocation failed. + + - had to explicitly cast the null buffer pointer to a char + pointer (cptr = (char *)buffer;) in 4 places in the buffers.c + file to satisfy a picky C++ compiler. + + - changed the test for an ALPHA VMS system to see if + '__VMS' is defined, rather than 'VMS'. The latter + is not defined by at least one C++ compiler. + + - modified ffpcls so that it can write a null string to + a variable length string column, without going into + an infinite loop. + + - fixed bug in ffgcfl that caused the 'next' variable to be + incremented twice. + + - fixed bug in ffgcpr that caused it write 2x the number of + complex elements into the descriptor when writing to + a complex or double complex variable length array column. + + - added call to ffrdef at the end of ffrsim to ensure that + the internal structures are updated to correspond to the + modified header keywords + +Version 1.25 - 7 July 1997 + + - improved the efficiency of the ffiblk routine, when inserting + more than one block into the file. + + - fixed bug in ffwend that in rare instances caused the beginning + of the following extension to be overwritten by blank fill. + + - added new routine to modify the size of an existing primary + array or image extension: fits_resize_img/ffrsim. + + - added support for null-valued keywords, e.g., keywords that + have no defined value. These keywords have an equal sign and + space in columns 9-10, but have not value string. Example: + KEYNAME = / null-valued keyword + Support for this feature required the following changes: + - modified ffpsvc to return a null value string without error + - modified ffc2[ilrd] to return error VALUE_UNDEFINED in this case + - modified ffgkn[sljed] to continue reading additional keywords + even if one or more keywords have undefined values. + - added 4 new routines: ffpkyu, ffikyu, ffmkyu, ffukyu to + write, insert, modify, or update an undefined keyword + + - a new makefile.os2 file was added, for building CFITSIO + on OS/2 systems. + + - modified ffgtkn so that if it finds an unexpected keyword + name, the returned error status = BAD_ORDER instead of + NOT_POS_INT. + + - added 2 new routines, fits_write_key_unit/ffpunt and + fits_read_key_unit/ffgunt to write/read the physical + units of a keyword value. These routines use a local + FITS convention for storing the units in square brackets + following the '/' comment field separator, as in: + VELOCITY= 12 / [km/s] orbit speed + The testprog.c program was modified to test these + new routines. + + - in the test of Alpha OSF/1 machines in fitsio2.h, + change 'defined(unix)' to 'defined(__unix__)' which + appears to be a more robust test. + + - remove test for linux environment variable from fitsio2.h + +Version 1.24 - 2 May 1997 + + - fixed bug in ffpbyt that incorrectly computed the current + location in the FITS file when writing > 10000 bytes. + + - changed the datatype of the 'nbytes' parameter in ffpbyt + from 'int' to 'long'. Made corresponding datatype change + to some internal variables in ffshft. + + - changed '(unsigned short *)' to '(short *)' in getcolui.c, and + changed '(unsigned long *)' to '(long *)' in getcoluj.c, to + work around problem with the VAX/VMS cc compiler. + +Version 1.23 - 24 April 1997 + + - modified ffcins and ffdins (in editcol.c) to simply return + without error if there are no (zero) rows in the table. + +Version 1.22 - 18 April 1997 + + - fixed bug in ffgcpr that caused it to think that all values were + undefined in ASCII tables columns that have TNULLn = ' ' + (i.e., the TNULLn keyword value is a string of blanks. + + - fixed bug in the ffgcl[bdeijk,ui,uj] family of routines + when parsing a numeric value in an ASCII table. The + returned values would have the decimal place shifted to + the left if the table field contained an explicit decimal + point followed by blanks. Example: in an F5.2 column, + the value '16. ' would be returned as 0.16. If the + trailing zeros were present, then cfitsio returned the + correct value (e.g., '16.00' returns 16.). + + - fixed another bug in the ffgcl[bdeijk,ui,uj] family of routines + that caused them to misread values in an ASCII table in rows + following an undefined value when all the values were read + at once in a single call to the routine. + +Version 1.21 - 26 March 1997 + + - added general support for reading and writing unsigned integer + keywords, images, and binary table column values. + + - fixed bug in the way the column number was used in ffgsve and + similar routines. This bug caused cfitsio to read (colnum - 1) + rather than the desired column. + + - fixed a bug in ftgkls that prevented it from reading more than one + continuation line of a long string keyword value. + + - fixed the definition of fits_write_longwarn in longnam.h + +Version 1.20 - 29 Jan 1997 + + - when creating a binary table with variable length vector columns, if the + calling routine does not specify a value for the maximum length of + the vector (e.g., TFORMn = '1PE(400)') then cfitsio will automatically + calculate the maximum value and append it to the TFORM value + when the binary table is first closed. + + - added the set of routines to do coordinate system transformations + + - added support for wildcards ('*', '?', and '#') in the input + keyword name when reading, modifying, or deleting keywords. + + - added new general keyword reading routine, ffgnxk, to return + the next keyword whose name matches a list of template names, + but does not match any names on a second template list. + + - modified ftgrec so that it simply moves to the beginning + of the header if the input keyword number = 0 + + - added check in ffdelt to make sure the input fits file pointer is + not already null + + - added check in ffcopy to make sure the output HDU does not + already contain any keywords (it must be empty). + + - modified ffgcls so that it does not test if each string column + value equals the null string value if the null string value + is longer than the width of the column. + + - fixed bug in ftgtdm that caused it to fail if the TDIMn + keyword did not exist in the FITS file + + - modified testprog.c to include tests of keyword wildcards + and the WCS coordinate transformation routines. + + - added a test for 'EMX' in fitsio2.h so that cfitsio builds + correctly on a PC running OS/2. + +Version 1.11 - 04 Dec 1996 + + - modified the testprog.c program that is included with the + distribution, so that the output FITS file is identical to + that produced by the Fortran FITSIO test program. + + - changed all instances of the 'extname' variable to 'extnm' + to avoid a conflict with the -Dextname switch in cfortran.h + on HP machines. + + - in all the routines like ffi4fi1, which convert an array + of values to integers just prior to writing them to the FITS + file, the integer value is now rounded to the nearest integer + rather than truncated. (ffi4fi1, ffi4fi2, ffi4fi4, etc) + + - changed ffgcfl (and hence ffgcl) so that the input value + of the logical array element is not changed if the corresponding + FITS value is undefined. + + - in ffgacl, the returned value of TBCOL was off by 1 (too small) + + - fixed the comment of EXTNAME keyword to read 'binary table' + instead of 'ASCII table' in the header of binary tables. + +Version 1.101 - 17 Nov 1996 + + - Made major I/O efficiency improvements by adding internal buffers + rather than directly reading or writing to disk. Access to + columns in binary tables is now 50 - 150 times faster. Access to + FITS image is also slightly faster. + + - made significant speed improvements when reading numerical data + in FITS ASCII tables by writing my own number parsing routines + rather than using the sscanf C library routine. This change + requires that the -lm argument now be included when linking + a program that calls cfitsio (under UNIX). + + - regrouped the source files into logically related sets of routines. + The Makefile now runs much faster since every single routine is + not split into a separate file. + + - now use the memcpy function, rather than a 'for' loop in several + places for added efficiency + + - redesigned the low-level binary table read and write routines + (ffpbytoff and ffgbytoff) for greater efficiency. + + - added a new error status: 103 = too many open FITS files. + + - added a 'extern "C"' statement around the function prototypes + in fitsio.h, to support use of cfitsio by C++ compilers. + + - fixed routines for writing or reading fixed-length substrings + within a binary table ASCII column, with TFORM values of + of the form 'rAw' where 'r' is the total width of the ASCII + column and 'w' is the width of a substring within the column. + + - no longer automatically rewrite the END card and following fill + values if they are already correct. + + - all the 'get keyword value and comment' routines have been changed + so that the comment is not returned if the input pointer is NULL. + + - added new routine to return the optimum number of tables rows + that should be read or written at one time for optimum efficiency. + + - modified the way numerical values in ASCII tables are parsed so + that embedded spaces in the value are ignored, and implicit + decimal points are now supported. (e.g, the string '123E 12' + in a 'E10.2' format column will be interpreted as 1.23 * 10**12). + + - modified ffpcl and ffgcl to support binary table columns of + all datatype (added logical, bit, complex, and double complex) + + - when writing numerical data to ASCII table columns, the ffpcl_ + routines now return an overflow error if a value is too large + to be expressed in the column format. + + - closed small memory leak in ffpcls. + + - initialized the 'incre' variable in ffgcpr to eliminate compiler warning. + +Version 1.04 - 17 Sept 1996 + + - added README.MacOS and cfitsio_mac.sit.hqx to the distribution + to support the Mac platforms. + + - fixed bug in ffpdfl that caused an EOF error (107) when a program + creates a new extension that is an exact multiple of 2880 bytes long, + AND the program does not write a value to the last element + in the table or image. + + - fixed bug in all the ffgsf* and ffgcv* routines which caused + core dumps when reading null values in a table. + +Version 1.03 - 20 August 1996 + + - added full support for reading and writing the C 'int' + data type. This was a problem on Alpha/OSF where short, + int, and long datatypes are 2, 4, and 8 bytes long, respectively. + + - cleaned up the code in the byte-swapping routines. + + - renamed the file 'longname.h' to 'longnam.h' to avoid conflict + with a file with the same name in another unrelated package. + +Version 1.02 - 15 August 1996 + + - ffgtbp was not correctly reading the THEAP keyword, hence would + not correctly read variable length data in binary tables if + the heap was not at the default starting location (i.e., + starting immediately after the fixed length table). + + - now force the cbuff variable in ffpcl_ and ffgcl_ to be + aligned on a double word boundary. Non-alignment can + cause program to crash on some systems. + +Version 1.01 - 12 August 1996 + + - initial public release diff --git a/cextern/cfitsio/README.rst b/cextern/cfitsio/README.rst new file mode 100644 index 000000000000..5807e387e954 --- /dev/null +++ b/cextern/cfitsio/README.rst @@ -0,0 +1,3 @@ +This directory only contains the small subset of files from CFITSIO which are +required for the astropy.io.fits.hdu.compressed._tiled_compression package. All files are +copied verbatim from CFITSIO and can easily be updated if needed. diff --git a/astropy/io/fits/src/fits_hcompress.c b/cextern/cfitsio/lib/fits_hcompress.c similarity index 80% rename from astropy/io/fits/src/fits_hcompress.c rename to cextern/cfitsio/lib/fits_hcompress.c index 4b01b2702ffb..bf107877e4c1 100644 --- a/astropy/io/fits/src/fits_hcompress.c +++ b/cextern/cfitsio/lib/fits_hcompress.c @@ -1,75 +1,3 @@ -/* $Id$ -*/ - -/*****************************************************************************/ -/* */ -/* This file, fits_hcompress.c, contains the code required to compress */ -/* data using the HCOMPRESS_1 compression format. */ -/* */ -/* Copyright (C) 2004 Association of Universities for Research in Astronomy */ -/* (AURA) */ -/* */ -/* Redistribution and use in source and binary forms, with or without */ -/* modification, are permitted provided that the following conditions are */ -/* met: */ -/* */ -/* 1. Redistributions of source code must retain the above copyright */ -/* notice, this list of conditions and the following disclaimer. */ -/* */ -/* 2. Redistributions in binary form must reproduce the above */ -/* copyright notice, this list of conditions and the following */ -/* disclaimer in the documentation and/or other materials provided */ -/* with the distribution. */ -/* */ -/* 3. The name of AURA and its representatives may not be used to */ -/* endorse or promote products derived from this software without */ -/* specific prior written permission. */ -/* */ -/* THIS SOFTWARE IS PROVIDED BY AURA ``AS IS'' AND ANY EXPRESS OR IMPLIED */ -/* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF */ -/* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE */ -/* DISCLAIMED. IN NO EVENT SHALL AURA BE LIABLE FOR ANY DIRECT, INDIRECT, */ -/* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, */ -/* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS */ -/* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND */ -/* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR */ -/* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE */ -/* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH */ -/* DAMAGE. */ -/* */ -/* This file was copied intact from the FITSIO software that was written by */ -/* William Pence at the High Energy Astrophysic Science Archive Research */ -/* Center (HEASARC) at the NASA Goddard Space Flight Center. That software */ -/* contained the following copyright and warranty notices: */ -/* */ -/* Copyright (Unpublished--all rights reserved under the copyright laws of */ -/* the United States), U.S. Government as represented by the Administrator */ -/* of the National Aeronautics and Space Administration. No copyright is */ -/* claimed in the United States under Title 17, U.S. Code. */ -/* */ -/* Permission to freely use, copy, modify, and distribute this software */ -/* and its documentation without fee is hereby granted, provided that this */ -/* copyright notice and disclaimer of warranty appears in all copies. */ -/* */ -/* DISCLAIMER: */ -/* */ -/* THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, */ -/* EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, */ -/* ANY WARRANTY THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY */ -/* IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR */ -/* PURPOSE, AND FREEDOM FROM INFRINGEMENT, AND ANY WARRANTY THAT THE */ -/* DOCUMENTATION WILL CONFORM TO THE SOFTWARE, OR ANY WARRANTY THAT THE */ -/* SOFTWARE WILL BE ERROR FREE. IN NO EVENT SHALL NASA BE LIABLE FOR ANY */ -/* DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT, INDIRECT, SPECIAL OR */ -/* CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM, OR IN ANY WAY */ -/* CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY, */ -/* CONTRACT, TORT , OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY */ -/* PERSONS OR PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED */ -/* FROM, OR AROSE OUT OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR */ -/* SERVICES PROVIDED HEREUNDER." */ -/* */ -/*****************************************************************************/ - /* ######################################################################### These routines to apply the H-compress compression algorithm to a 2-D Fits image were written by R. White at the STScI and were obtained from the STScI at @@ -106,7 +34,7 @@ The following modifications have been made to the original code: #include #include #include -#include "fitsio.h" +#include "fitsio2.h" static long noutchar; static long noutmax; @@ -140,15 +68,15 @@ static int bufcopy(unsigned char a[], int n, unsigned char buffer[], int *b, in static void write_bdirect(char *outfile, int a[], int n,int nqx, int nqy, unsigned char scratch[], int bit); static void write_bdirect64(char *outfile, LONGLONG a[], int n,int nqx, int nqy, unsigned char scratch[], int bit); -/* #define output_nybble(outfile,c) output_nbits(outfile,c,4) */ +/* #define output_nybble(outfile,c) output_nbits(outfile,c,4) */ static void output_nybble(char *outfile, int bits); static void output_nnybble(char *outfile, int n, unsigned char array[]); #define output_huffman(outfile,c) output_nbits(outfile,code[c],ncode[c]) /* ---------------------------------------------------------------------- */ -int _astropy_fits_hcompress(int *a, int ny, int nx, int scale, char *output, - long *nbytes, int *status) +int fits_hcompress(int *a, int ny, int nx, int scale, char *output, + long *nbytes, int *status) { /* compress the input image using the H-compress algorithm @@ -184,17 +112,19 @@ int _astropy_fits_hcompress(int *a, int ny, int nx, int scale, char *output, /* encode and write to output array */ + FFLOCK; noutmax = *nbytes; /* input value is the allocated size of the array */ *nbytes = 0; /* reset */ stat = encode(output, nbytes, a, nx, ny, scale); - + FFUNLOCK; + *status = stat; return(*status); } /* ---------------------------------------------------------------------- */ -int _astropy_fits_hcompress64(LONGLONG *a, int ny, int nx, int scale, - char *output, long *nbytes, int *status) +int fits_hcompress64(LONGLONG *a, int ny, int nx, int scale, char *output, + long *nbytes, int *status) { /* compress the input image using the H-compress algorithm @@ -228,10 +158,13 @@ int _astropy_fits_hcompress64(LONGLONG *a, int ny, int nx, int scale, digitize64(a, nx, ny, scale); /* encode and write to output array */ + + FFLOCK; noutmax = *nbytes; /* input value is the allocated size of the array */ *nbytes = 0; /* reset */ stat = encode64(output, nbytes, a, nx, ny, scale); + FFUNLOCK; *status = stat; return(*status); @@ -269,7 +202,7 @@ int *tmp; */ tmp = (int *) malloc(((nmax+1)/2)*sizeof(int)); if(tmp == (int *) NULL) { - _astropy_ffpmsg("htrans: insufficient memory"); + ffpmsg("htrans: insufficient memory"); return(DATA_COMPRESSION_ERR); } /* @@ -405,7 +338,7 @@ LONGLONG *tmp; */ tmp = (LONGLONG *) malloc(((nmax+1)/2)*sizeof(LONGLONG)); if(tmp == (LONGLONG *) NULL) { - _astropy_ffpmsg("htrans64: insufficient memory"); + ffpmsg("htrans64: insufficient memory"); return(DATA_COMPRESSION_ERR); } /* @@ -696,15 +629,16 @@ int stat; a[0] = 0; /* * allocate array for sign bits and save values, 8 per byte + (initialize to all zeros) */ - signbits = (unsigned char *) malloc((nel+7)/8); + signbits = (unsigned char *) calloc(1, (nel+7)/8); if (signbits == (unsigned char *) NULL) { - _astropy_ffpmsg("encode: insufficient memory"); + ffpmsg("encode: insufficient memory"); return(DATA_COMPRESSION_ERR); } nsign = 0; bits_to_go = 8; - signbits[0] = 0; +/* signbits[0] = 0; */ for (i=0; i 0) { /* @@ -730,7 +664,7 @@ int stat; */ bits_to_go = 8; nsign += 1; - signbits[nsign] = 0; +/* signbits[nsign] = 0; */ } } if (bits_to_go != 8) { @@ -769,28 +703,29 @@ int stat; */ /* this is a more efficient way to do this, */ - - + + for (q = 0; q < 3; q++) { - for (nbitplanes[q] = 0; vmax[q]>0; vmax[q] = vmax[q]>>1, nbitplanes[q]++) ; + for (nbitplanes[q] = 0; vmax[q]>0; vmax[q] = vmax[q]>>1, nbitplanes[q]++) ; } /* - for (q = 0; q < 3; q++) { - nbitplanes[q] = (int) (log((float) (vmax[q]+1))/log(2.0)+0.5); - if ( (vmax[q]+1) > (1< (1<= noutmax) { - _astropy_ffpmsg("encode: output buffer too small"); + ffpmsg("encode: output buffer too small"); return(DATA_COMPRESSION_ERR); } @@ -858,14 +793,14 @@ int stat; /* * allocate array for sign bits and save values, 8 per byte */ - signbits = (unsigned char *) malloc((nel+7)/8); + signbits = (unsigned char *) calloc(1, (nel+7)/8); if (signbits == (unsigned char *) NULL) { - _astropy_ffpmsg("encode64: insufficient memory"); + ffpmsg("encode64: insufficient memory"); return(DATA_COMPRESSION_ERR); } nsign = 0; bits_to_go = 8; - signbits[0] = 0; +/* signbits[0] = 0; */ for (i=0; i 0) { /* @@ -891,7 +826,7 @@ int stat; */ bits_to_go = 8; nsign += 1; - signbits[nsign] = 0; +/* signbits[nsign] = 0; */ } } if (bits_to_go != 8) { @@ -915,16 +850,16 @@ int stat; */ nx2 = (nx+1)/2; ny2 = (ny+1)/2; - j=0; /* column counter */ - k=0; /* row counter */ - for (i=0; i=ny2) + (k>=nx2); - if (vmax[q] < a[i]) vmax[q] = a[i]; - if (++j >= ny) { - j = 0; - k += 1; - } - } + j=0; /* column counter */ + k=0; /* row counter */ + for (i=0; i=ny2) + (k>=nx2); + if (vmax[q] < a[i]) vmax[q] = a[i]; + if (++j >= ny) { + j = 0; + k += 1; + } + } /* * now calculate number of bits for each quadrant */ @@ -951,8 +886,9 @@ int stat; */ if (0 == qwrite(outfile, (char *) nbitplanes, sizeof(nbitplanes))) { - *nlength = noutchar; - _astropy_ffpmsg("encode: output buffer too small"); + free(signbits); + *nlength = noutchar; + ffpmsg("encode: output buffer too small"); return(DATA_COMPRESSION_ERR); } @@ -969,7 +905,7 @@ int stat; if ( 0 == qwrite(outfile, (char *) signbits, nsign)) { free(signbits); *nlength = noutchar; - _astropy_ffpmsg("encode: output buffer too small"); + ffpmsg("encode: output buffer too small"); return(DATA_COMPRESSION_ERR); } } @@ -978,7 +914,7 @@ int stat; *nlength = noutchar; if (noutchar >= noutmax) { - _astropy_ffpmsg("encode64: output buffer too small"); + ffpmsg("encode64: output buffer too small"); return(DATA_COMPRESSION_ERR); } @@ -1034,8 +970,7 @@ unsigned char b[8]; } /* ######################################################################### */ static int -qwrite(char *file, char buffer[], int n) -{ +qwrite(char *file, char buffer[], int n){ /* * write n bytes from buffer into file * returns number of bytes read (=n) if successful, <=0 if not @@ -1179,8 +1114,8 @@ output_nbits(char *outfile, int bits, int n) * insert bits at end of buffer */ buffer2 <<= n; -/* buffer2 |= ( bits & ((1<>(-bits_to_go2)) & 0xff); + /* + * insert 4 bits at end of buffer + */ + buffer2 = (buffer2<<4) | ( bits & 15 ); + bits_to_go2 -= 4; + if (bits_to_go2 <= 0) { + /* + * buffer2 full, put out top 8 bits + */ - if (noutchar < noutmax) noutchar++; + outfile[noutchar] = ((buffer2>>(-bits_to_go2)) & 0xff); - bits_to_go2 += 8; - } - bitcount += 4; + if (noutchar < noutmax) noutchar++; + + bits_to_go2 += 8; + } + bitcount += 4; } /* ############################################################################ */ /* OUTPUT array of 4 BITS */ static void output_nnybble(char *outfile, int n, unsigned char array[]) { - /* pack the 4 lower bits in each element of the array into the outfile array */ + /* pack the 4 lower bits in each element of the array into the outfile array */ int ii, jj, kk = 0, shift; - if (n == 1) { - output_nybble(outfile, (int) array[0]); - return; - } + if (n == 1) { + output_nybble(outfile, (int) array[0]); + return; + } /* forcing byte alignment doesn;t help, and even makes it go slightly slower if (bits_to_go2 != 8) output_nbits(outfile, kk, bits_to_go2); */ - if (bits_to_go2 <= 4) - { - /* just room for 1 nybble; write it out separately */ - output_nybble(outfile, array[0]); - kk++; /* index to next array element */ - - if (n == 2) /* only 1 more nybble to write out */ - { - output_nybble(outfile, (int) array[1]); - return; - } - } + if (bits_to_go2 <= 4) + { + /* just room for 1 nybble; write it out separately */ + output_nybble(outfile, array[0]); + kk++; /* index to next array element */ + + if (n == 2) /* only 1 more nybble to write out */ + { + output_nybble(outfile, (int) array[1]); + return; + } + } /* bits_to_go2 is now in the range 5 - 8 */ - shift = 8 - bits_to_go2; - - /* now write out pairs of nybbles; this does not affect value of bits_to_go2 */ - jj = (n - kk) / 2; - - if (bits_to_go2 == 8) { - /* special case if nybbles are aligned on byte boundary */ - /* this actually seems to make very little differnece in speed */ - buffer2 = 0; - for (ii = 0; ii < jj; ii++) - { - outfile[noutchar] = ((array[kk] & 15)<<4) | (array[kk+1] & 15); - kk += 2; - noutchar++; - } - } else { - for (ii = 0; ii < jj; ii++) - { - buffer2 = (buffer2<<8) | ((array[kk] & 15)<<4) | (array[kk+1] & 15); - kk += 2; - - /* - buffer2 full, put out top 8 bits - */ - - outfile[noutchar] = ((buffer2>>shift) & 0xff); - noutchar++; - } - } + shift = 8 - bits_to_go2; + + /* now write out pairs of nybbles; this does not affect value of bits_to_go2 */ + jj = (n - kk) / 2; + + if (bits_to_go2 == 8) { + /* special case if nybbles are aligned on byte boundary */ + /* this actually seems to make very little differnece in speed */ + buffer2 = 0; + for (ii = 0; ii < jj; ii++) + { + outfile[noutchar] = ((array[kk] & 15)<<4) | (array[kk+1] & 15); + kk += 2; + noutchar++; + } + } else { + for (ii = 0; ii < jj; ii++) + { + buffer2 = (buffer2<<8) | ((array[kk] & 15)<<4) | (array[kk+1] & 15); + kk += 2; + + /* + buffer2 full, put out top 8 bits + */ - bitcount += (8 * (ii - 1)); + outfile[noutchar] = ((buffer2>>shift) & 0xff); + noutchar++; + } + } - /* write out last odd nybble, if present */ - if (kk != n) output_nybble(outfile, (int) array[n - 1]); + bitcount += (8 * (ii - 1)); - return; + /* write out last odd nybble, if present */ + if (kk != n) output_nybble(outfile, (int) array[n - 1]); + + return; } @@ -1378,10 +1313,15 @@ unsigned char *scratch, *buffer; * Buffer is used to store string of codes for output. */ scratch = (unsigned char *) malloc(2*bmax); + if (scratch == (unsigned char *) NULL) { + ffpmsg("qtree_encode: insufficient memory"); + return(DATA_COMPRESSION_ERR); + } + buffer = (unsigned char *) malloc(bmax); - if ((scratch == (unsigned char *) NULL) || - (buffer == (unsigned char *) NULL)) { - _astropy_ffpmsg("qtree_encode: insufficient memory"); + if (buffer == (unsigned char *) NULL) { + free(scratch); + ffpmsg("qtree_encode: insufficient memory"); return(DATA_COMPRESSION_ERR); } /* @@ -1497,12 +1437,18 @@ unsigned char *scratch, *buffer; * Buffer is used to store string of codes for output. */ scratch = (unsigned char *) malloc(2*bmax); + if (scratch == (unsigned char *) NULL) { + ffpmsg("qtree_encode64: insufficient memory"); + return(DATA_COMPRESSION_ERR); + } + buffer = (unsigned char *) malloc(bmax); - if ((scratch == (unsigned char *) NULL) || - (buffer == (unsigned char *) NULL)) { - _astropy_ffpmsg("qtree_encode64: insufficient memory"); + if (buffer == (unsigned char *) NULL) { + free(scratch); + ffpmsg("qtree_encode64: insufficient memory"); return(DATA_COMPRESSION_ERR); } + /* * now encode each bit plane, starting with the top */ @@ -1634,8 +1580,83 @@ int s10, s00; k = 0; /* k is index of b[i/2,j/2] */ for (i = 0; i +#include +#include +#include +#include +#include "fitsio2.h" + +/* WDP added test to see if min and max are already defined */ +#ifndef min +#define min(a,b) (((a)<(b))?(a):(b)) +#endif +#ifndef max +#define max(a,b) (((a)>(b))?(a):(b)) +#endif + +static long nextchar; + +static int decode(unsigned char *infile, int *a, int na, int *nx, int *ny, int *scale); +static int decode64(unsigned char *infile, LONGLONG *a, int na, int *nx, int *ny, int *scale); +static int hinv(int a[], int nx, int ny, int smooth ,int scale); +static int hinv64(LONGLONG a[], int nx, int ny, int smooth ,int scale); +static void undigitize(int a[], int nx, int ny, int scale); +static void undigitize64(LONGLONG a[], int nx, int ny, int scale); +static void unshuffle(int a[], int n, int n2, int tmp[]); +static void unshuffle64(LONGLONG a[], int n, int n2, LONGLONG tmp[]); +static void hsmooth(int a[], int nxtop, int nytop, int ny, int scale); +static void hsmooth64(LONGLONG a[], int nxtop, int nytop, int ny, int scale); +static void qread(unsigned char *infile,char *a, int n); +static int readint(unsigned char *infile); +static LONGLONG readlonglong(unsigned char *infile); +static int dodecode(unsigned char *infile, int a[], int nx, int ny, unsigned char nbitplanes[3]); +static int dodecode64(unsigned char *infile, LONGLONG a[], int nx, int ny, unsigned char nbitplanes[3]); +static int qtree_decode(unsigned char *infile, int a[], int n, int nqx, int nqy, int nbitplanes); +static int qtree_decode64(unsigned char *infile, LONGLONG a[], int n, int nqx, int nqy, int nbitplanes); +static void start_inputing_bits(void); +static int input_bit(unsigned char *infile); +static int input_nbits(unsigned char *infile, int n); +/* make input_nybble a separate routine, for added effiency */ +/* #define input_nybble(infile) input_nbits(infile,4) */ +static int input_nybble(unsigned char *infile); +static int input_nnybble(unsigned char *infile, int n, unsigned char *array); + +static void qtree_expand(unsigned char *infile, unsigned char a[], int nx, int ny, unsigned char b[]); +static void qtree_bitins(unsigned char a[], int nx, int ny, int b[], int n, int bit); +static void qtree_bitins64(unsigned char a[], int nx, int ny, LONGLONG b[], int n, int bit); +static void qtree_copy(unsigned char a[], int nx, int ny, unsigned char b[], int n); +static void read_bdirect(unsigned char *infile, int a[], int n, int nqx, int nqy, unsigned char scratch[], int bit); +static void read_bdirect64(unsigned char *infile, LONGLONG a[], int n, int nqx, int nqy, unsigned char scratch[], int bit); +static int input_huffman(unsigned char *infile); + +/* ---------------------------------------------------------------------- */ +int fits_hdecompress(unsigned char *input, int smooth, int *a, int na, int *ny, int *nx, + int *scale, int *status) +{ + /* + decompress the input byte stream using the H-compress algorithm + + input - input array of compressed bytes + a - pre-allocated array to hold the output uncompressed image + nx - returned X axis size + ny - returned Y axis size + + NOTE: the nx and ny dimensions as defined within this code are reversed from + the usual FITS notation. ny is the fastest varying dimension, which is + usually considered the X axis in the FITS image display + + */ +int stat; + + if (*status > 0) return(*status); + + /* decode the input array */ + + FFLOCK; /* decode uses the nextchar global variable */ + stat = decode(input, a, na, nx, ny, scale); + FFUNLOCK; + + *status = stat; + if (stat) return(*status); + + /* + * Un-Digitize + */ + undigitize(a, *nx, *ny, *scale); + + /* + * Inverse H-transform + */ + stat = hinv(a, *nx, *ny, smooth, *scale); + *status = stat; + + return(*status); +} +/* ---------------------------------------------------------------------- */ +int fits_hdecompress64(unsigned char *input, int smooth, LONGLONG *a, int na, int *ny, int *nx, + int *scale, int *status) +{ + /* + decompress the input byte stream using the H-compress algorithm + + input - input array of compressed bytes + a - pre-allocated array to hold the output uncompressed image + nx - returned X axis size + ny - returned Y axis size + + NOTE: the nx and ny dimensions as defined within this code are reversed from + the usual FITS notation. ny is the fastest varying dimension, which is + usually considered the X axis in the FITS image display + + */ + int stat, *iarray, ii, nval; + + if (*status > 0) return(*status); + + /* decode the input array */ + + FFLOCK; /* decode uses the nextchar global variable */ + stat = decode64(input, a, na, nx, ny, scale); + FFUNLOCK; + + *status = stat; + if (stat) return(*status); + + /* + * Un-Digitize + */ + undigitize64(a, *nx, *ny, *scale); + + /* + * Inverse H-transform + */ + stat = hinv64(a, *nx, *ny, smooth, *scale); + + *status = stat; + + /* pack the I*8 values back into an I*4 array */ + iarray = (int *) a; + nval = (*nx) * (*ny); + + for (ii = 0; ii < nval; ii++) + iarray[ii] = (int) a[ii]; + + return(*status); +} + +/* ############################################################################ */ +/* ############################################################################ */ + +/* Copyright (c) 1993 Association of Universities for Research + * in Astronomy. All rights reserved. Produced under National + * Aeronautics and Space Administration Contract No. NAS5-26555. + */ +/* hinv.c Inverse H-transform of NX x NY integer image + * + * Programmer: R. White Date: 23 July 1993 + */ + +/* ############################################################################ */ +static int +hinv(int a[], int nx, int ny, int smooth ,int scale) +/* +int smooth; 0 for no smoothing, else smooth during inversion +int scale; used if smoothing is specified +*/ +{ +int nmax, log2n, i, j, k; +int nxtop,nytop,nxf,nyf,c; +int oddx,oddy; +int shift, bit0, bit1, bit2, mask0, mask1, mask2, + prnd0, prnd1, prnd2, nrnd0, nrnd1, nrnd2, lowbit0, lowbit1; +int h0, hx, hy, hc; +int s10, s00; +int *tmp; + + /* + * log2n is log2 of max(nx,ny) rounded up to next power of 2 + */ + nmax = (nx>ny) ? nx : ny; + log2n = (int) (log((float) nmax)/log(2.0)+0.5); + if ( nmax > (1<> 1; + prnd1 = bit1 >> 1; + prnd2 = bit2 >> 1; + nrnd0 = prnd0 - 1; + nrnd1 = prnd1 - 1; + nrnd2 = prnd2 - 1; + /* + * round h0 to multiple of bit2 + */ + a[0] = (a[0] + ((a[0] >= 0) ? prnd2 : nrnd2)) & mask2; + /* + * do log2n expansions + * + * We're indexing a as a 2-D array with dimensions (nx,ny). + */ + nxtop = 1; + nytop = 1; + nxf = nx; + nyf = ny; + c = 1<=0; k--) { + /* + * this somewhat cryptic code generates the sequence + * ntop[k-1] = (ntop[k]+1)/2, where ntop[log2n] = n + */ + c = c>>1; + nxtop = nxtop<<1; + nytop = nytop<<1; + if (nxf <= c) { nxtop -= 1; } else { nxf -= c; } + if (nyf <= c) { nytop -= 1; } else { nyf -= c; } + /* + * double shift and fix nrnd0 (because prnd0=0) on last pass + */ + if (k == 0) { + nrnd0 = 0; + shift = 2; + } + /* + * unshuffle in each dimension to interleave coefficients + */ + for (i = 0; i= 0) ? prnd1 : nrnd1)) & mask1; + hy = (hy + ((hy >= 0) ? prnd1 : nrnd1)) & mask1; + hc = (hc + ((hc >= 0) ? prnd0 : nrnd0)) & mask0; + /* + * propagate bit0 of hc to hx,hy + */ + lowbit0 = hc & bit0; + hx = (hx >= 0) ? (hx - lowbit0) : (hx + lowbit0); + hy = (hy >= 0) ? (hy - lowbit0) : (hy + lowbit0); + /* + * Propagate bits 0 and 1 of hc,hx,hy to h0. + * This could be simplified if we assume h0>0, but then + * the inversion would not be lossless for images with + * negative pixels. + */ + lowbit1 = (hc ^ hx ^ hy) & bit1; + h0 = (h0 >= 0) + ? (h0 + lowbit0 - lowbit1) + : (h0 + ((lowbit0 == 0) ? lowbit1 : (lowbit0-lowbit1))); + /* + * Divide sums by 2 (4 last time) + */ + a[s10+1] = (h0 + hx + hy + hc) >> shift; + a[s10 ] = (h0 + hx - hy - hc) >> shift; + a[s00+1] = (h0 - hx + hy - hc) >> shift; + a[s00 ] = (h0 - hx - hy + hc) >> shift; + s00 += 2; + s10 += 2; + } + if (oddy) { + /* + * do last element in row if row length is odd + * s00+1, s10+1 are off edge + */ + h0 = a[s00 ]; + hx = a[s10 ]; + hx = ((hx >= 0) ? (hx+prnd1) : (hx+nrnd1)) & mask1; + lowbit1 = hx & bit1; + h0 = (h0 >= 0) ? (h0 - lowbit1) : (h0 + lowbit1); + a[s10 ] = (h0 + hx) >> shift; + a[s00 ] = (h0 - hx) >> shift; + } + } + if (oddx) { + /* + * do last row if column length is odd + * s10, s10+1 are off edge + */ + s00 = ny*i; + for (j = 0; j= 0) ? (hy+prnd1) : (hy+nrnd1)) & mask1; + lowbit1 = hy & bit1; + h0 = (h0 >= 0) ? (h0 - lowbit1) : (h0 + lowbit1); + a[s00+1] = (h0 + hy) >> shift; + a[s00 ] = (h0 - hy) >> shift; + s00 += 2; + } + if (oddy) { + /* + * do corner element if both row and column lengths are odd + * s00+1, s10, s10+1 are off edge + */ + h0 = a[s00 ]; + a[s00 ] = h0 >> shift; + } + } + /* + * divide all the masks and rounding values by 2 + */ + bit2 = bit1; + bit1 = bit0; + bit0 = bit0 >> 1; + mask1 = mask0; + mask0 = mask0 >> 1; + prnd1 = prnd0; + prnd0 = prnd0 >> 1; + nrnd1 = nrnd0; + nrnd0 = prnd0 - 1; + } + free(tmp); + return(0); +} +/* ############################################################################ */ +static int +hinv64(LONGLONG a[], int nx, int ny, int smooth ,int scale) +/* +int smooth; 0 for no smoothing, else smooth during inversion +int scale; used if smoothing is specified +*/ +{ +int nmax, log2n, i, j, k; +int nxtop,nytop,nxf,nyf,c; +int oddx,oddy; +int shift; +LONGLONG mask0, mask1, mask2, prnd0, prnd1, prnd2, bit0, bit1, bit2; +LONGLONG nrnd0, nrnd1, nrnd2, lowbit0, lowbit1; +LONGLONG h0, hx, hy, hc; +int s10, s00; +LONGLONG *tmp; + + /* + * log2n is log2 of max(nx,ny) rounded up to next power of 2 + */ + nmax = (nx>ny) ? nx : ny; + log2n = (int) (log((float) nmax)/log(2.0)+0.5); + if ( nmax > (1<> 1; + prnd1 = bit1 >> 1; + prnd2 = bit2 >> 1; + nrnd0 = prnd0 - 1; + nrnd1 = prnd1 - 1; + nrnd2 = prnd2 - 1; + /* + * round h0 to multiple of bit2 + */ + a[0] = (a[0] + ((a[0] >= 0) ? prnd2 : nrnd2)) & mask2; + /* + * do log2n expansions + * + * We're indexing a as a 2-D array with dimensions (nx,ny). + */ + nxtop = 1; + nytop = 1; + nxf = nx; + nyf = ny; + c = 1<=0; k--) { + /* + * this somewhat cryptic code generates the sequence + * ntop[k-1] = (ntop[k]+1)/2, where ntop[log2n] = n + */ + c = c>>1; + nxtop = nxtop<<1; + nytop = nytop<<1; + if (nxf <= c) { nxtop -= 1; } else { nxf -= c; } + if (nyf <= c) { nytop -= 1; } else { nyf -= c; } + /* + * double shift and fix nrnd0 (because prnd0=0) on last pass + */ + if (k == 0) { + nrnd0 = 0; + shift = 2; + } + /* + * unshuffle in each dimension to interleave coefficients + */ + for (i = 0; i= 0) ? prnd1 : nrnd1)) & mask1; + hy = (hy + ((hy >= 0) ? prnd1 : nrnd1)) & mask1; + hc = (hc + ((hc >= 0) ? prnd0 : nrnd0)) & mask0; + /* + * propagate bit0 of hc to hx,hy + */ + lowbit0 = hc & bit0; + hx = (hx >= 0) ? (hx - lowbit0) : (hx + lowbit0); + hy = (hy >= 0) ? (hy - lowbit0) : (hy + lowbit0); + /* + * Propagate bits 0 and 1 of hc,hx,hy to h0. + * This could be simplified if we assume h0>0, but then + * the inversion would not be lossless for images with + * negative pixels. + */ + lowbit1 = (hc ^ hx ^ hy) & bit1; + h0 = (h0 >= 0) + ? (h0 + lowbit0 - lowbit1) + : (h0 + ((lowbit0 == 0) ? lowbit1 : (lowbit0-lowbit1))); + /* + * Divide sums by 2 (4 last time) + */ + a[s10+1] = (h0 + hx + hy + hc) >> shift; + a[s10 ] = (h0 + hx - hy - hc) >> shift; + a[s00+1] = (h0 - hx + hy - hc) >> shift; + a[s00 ] = (h0 - hx - hy + hc) >> shift; + s00 += 2; + s10 += 2; + } + if (oddy) { + /* + * do last element in row if row length is odd + * s00+1, s10+1 are off edge + */ + h0 = a[s00 ]; + hx = a[s10 ]; + hx = ((hx >= 0) ? (hx+prnd1) : (hx+nrnd1)) & mask1; + lowbit1 = hx & bit1; + h0 = (h0 >= 0) ? (h0 - lowbit1) : (h0 + lowbit1); + a[s10 ] = (h0 + hx) >> shift; + a[s00 ] = (h0 - hx) >> shift; + } + } + if (oddx) { + /* + * do last row if column length is odd + * s10, s10+1 are off edge + */ + s00 = ny*i; + for (j = 0; j= 0) ? (hy+prnd1) : (hy+nrnd1)) & mask1; + lowbit1 = hy & bit1; + h0 = (h0 >= 0) ? (h0 - lowbit1) : (h0 + lowbit1); + a[s00+1] = (h0 + hy) >> shift; + a[s00 ] = (h0 - hy) >> shift; + s00 += 2; + } + if (oddy) { + /* + * do corner element if both row and column lengths are odd + * s00+1, s10, s10+1 are off edge + */ + h0 = a[s00 ]; + a[s00 ] = h0 >> shift; + } + } + /* + * divide all the masks and rounding values by 2 + */ + bit2 = bit1; + bit1 = bit0; + bit0 = bit0 >> 1; + mask1 = mask0; + mask0 = mask0 >> 1; + prnd1 = prnd0; + prnd0 = prnd0 >> 1; + nrnd1 = nrnd0; + nrnd0 = prnd0 - 1; + } + free(tmp); + return(0); +} + +/* ############################################################################ */ +static void +unshuffle(int a[], int n, int n2, int tmp[]) +/* +int a[]; array to shuffle +int n; number of elements to shuffle +int n2; second dimension +int tmp[]; scratch storage +*/ +{ +int i; +int nhalf; +int *p1, *p2, *pt; + + /* + * copy 2nd half of array to tmp + */ + nhalf = (n+1)>>1; + pt = tmp; + p1 = &a[n2*nhalf]; /* pointer to a[i] */ + for (i=nhalf; i= 0; i--) { + *p1 = *p2; + p2 -= n2; + p1 -= (n2+n2); + } + /* + * now distribute 2nd half of array (in tmp) to odd elements + */ + pt = tmp; + p1 = &a[n2]; /* pointer to a[i] */ + for (i=1; i>1; + pt = tmp; + p1 = &a[n2*nhalf]; /* pointer to a[i] */ + for (i=nhalf; i= 0; i--) { + *p1 = *p2; + p2 -= n2; + p1 -= (n2+n2); + } + /* + * now distribute 2nd half of array (in tmp) to odd elements + */ + pt = tmp; + p1 = &a[n2]; /* pointer to a[i] */ + for (i=1; i> 1); + if (smax <= 0) return; + ny2 = ny << 1; + /* + * We're indexing a as a 2-D array with dimensions (nxtop,ny) of which + * only (nxtop,nytop) are used. The coefficients on the edge of the + * array are not adjusted (which is why the loops below start at 2 + * instead of 0 and end at nxtop-2 instead of nxtop.) + */ + /* + * Adjust x difference hx + */ + for (i = 2; i=0, dmin<=0. + */ + if (dmin < dmax) { + diff = max( min(diff, dmax), dmin); + /* + * Compute change in slope limited to range +/- smax. + * Careful with rounding negative numbers when using + * shift for divide by 8. + */ + s = diff-(a[s10]<<3); + s = (s>=0) ? (s>>3) : ((s+7)>>3) ; + s = max( min(s, smax), -smax); + a[s10] = a[s10]+s; + } + s00 += 2; + s10 += 2; + } + } + /* + * Adjust y difference hy + */ + for (i = 0; i=0) ? (s>>3) : ((s+7)>>3) ; + s = max( min(s, smax), -smax); + a[s00+1] = a[s00+1]+s; + } + s00 += 2; + s10 += 2; + } + } + /* + * Adjust curvature difference hc + */ + for (i = 2; i=0, dmin<=0. + */ + if (dmin < dmax) { + diff = max( min(diff, dmax), dmin); + /* + * Compute change in slope limited to range +/- smax. + * Careful with rounding negative numbers when using + * shift for divide by 64. + */ + s = diff-(a[s10+1]<<6); + s = (s>=0) ? (s>>6) : ((s+63)>>6) ; + s = max( min(s, smax), -smax); + a[s10+1] = a[s10+1]+s; + } + s00 += 2; + s10 += 2; + } + } +} +/* ############################################################################ */ +static void +hsmooth64(LONGLONG a[], int nxtop, int nytop, int ny, int scale) +/* +LONGLONG a[]; array of H-transform coefficients +int nxtop,nytop; size of coefficient block to use +int ny; actual 1st dimension of array +int scale; truncation scale factor that was used +*/ +{ +int i, j; +int ny2, s10, s00; +LONGLONG hm, h0, hp, hmm, hpm, hmp, hpp, hx2, hy2, diff, dmax, dmin, s, smax, m1, m2; + + /* + * Maximum change in coefficients is determined by scale factor. + * Since we rounded during division (see digitize.c), the biggest + * permitted change is scale/2. + */ + smax = (scale >> 1); + if (smax <= 0) return; + ny2 = ny << 1; + /* + * We're indexing a as a 2-D array with dimensions (nxtop,ny) of which + * only (nxtop,nytop) are used. The coefficients on the edge of the + * array are not adjusted (which is why the loops below start at 2 + * instead of 0 and end at nxtop-2 instead of nxtop.) + */ + /* + * Adjust x difference hx + */ + for (i = 2; i=0, dmin<=0. + */ + if (dmin < dmax) { + diff = max( min(diff, dmax), dmin); + /* + * Compute change in slope limited to range +/- smax. + * Careful with rounding negative numbers when using + * shift for divide by 8. + */ + s = diff-(a[s10]<<3); + s = (s>=0) ? (s>>3) : ((s+7)>>3) ; + s = max( min(s, smax), -smax); + a[s10] = a[s10]+s; + } + s00 += 2; + s10 += 2; + } + } + /* + * Adjust y difference hy + */ + for (i = 0; i=0) ? (s>>3) : ((s+7)>>3) ; + s = max( min(s, smax), -smax); + a[s00+1] = a[s00+1]+s; + } + s00 += 2; + s10 += 2; + } + } + /* + * Adjust curvature difference hc + */ + for (i = 2; i=0, dmin<=0. + */ + if (dmin < dmax) { + diff = max( min(diff, dmax), dmin); + /* + * Compute change in slope limited to range +/- smax. + * Careful with rounding negative numbers when using + * shift for divide by 64. + */ + s = diff-(a[s10+1]<<6); + s = (s>=0) ? (s>>6) : ((s+63)>>6) ; + s = max( min(s, smax), -smax); + a[s10+1] = a[s10+1]+s; + } + s00 += 2; + s10 += 2; + } + } +} + + +/* ############################################################################ */ +/* ############################################################################ */ +/* Copyright (c) 1993 Association of Universities for Research + * in Astronomy. All rights reserved. Produced under National + * Aeronautics and Space Administration Contract No. NAS5-26555. + */ +/* undigitize.c undigitize H-transform + * + * Programmer: R. White Date: 9 May 1991 + */ + +/* ############################################################################ */ +static void +undigitize(int a[], int nx, int ny, int scale) +{ +int *p; + + /* + * multiply by scale + */ + if (scale <= 1) return; + for (p=a; p <= &a[nx*ny-1]; p++) *p = (*p)*scale; +} +/* ############################################################################ */ +static void +undigitize64(LONGLONG a[], int nx, int ny, int scale) +{ +LONGLONG *p, scale64; + + /* + * multiply by scale + */ + if (scale <= 1) return; + scale64 = (LONGLONG) scale; /* use a 64-bit int for efficiency in the big loop */ + + for (p=a; p <= &a[nx*ny-1]; p++) *p = (*p)*scale64; +} + +/* ############################################################################ */ +/* ############################################################################ */ +/* Copyright (c) 1993 Association of Universities for Research + * in Astronomy. All rights reserved. Produced under National + * Aeronautics and Space Administration Contract No. NAS5-26555. + */ +/* decode.c read codes from infile and construct array + * + * Programmer: R. White Date: 2 February 1994 + */ + + +static char code_magic[2] = { (char)0xDD, (char)0x99 }; + +/* ############################################################################ */ +static int decode(unsigned char *infile, int *a, int na, int *nx, int *ny, int *scale) +/* +char *infile; input file +int *a; address of output array [nx][ny] +int na; size allocated for output array +int *nx,*ny; dimensions of image +int *scale; scale factor for digitization +*/ +{ +LONGLONG sumall; +int stat; +unsigned char nbitplanes[3]; +char tmagic[2]; + + /* initialize the byte read position to the beginning of the array */; + nextchar = 0; + + /* + * File starts either with special 2-byte magic code or with + * FITS keyword "SIMPLE =" + */ + qread(infile, tmagic, sizeof(tmagic)); + /* + * check for correct magic code value + */ + if (memcmp(tmagic,code_magic,sizeof(code_magic)) != 0) { + ffpmsg("bad file format"); + return(DATA_DECOMPRESSION_ERR); + } + *nx =readint(infile); /* x size of image */ + *ny =readint(infile); /* y size of image */ + *scale=readint(infile); /* scale factor for digitization */ + + if ((*nx) > INT_MAX/(*ny)) { + ffpmsg("numerical overflow during decompression"); + return(DATA_DECOMPRESSION_ERR); + } + if ((*nx)*(*ny) > na) { + ffpmsg("wrong allocation size during decompression"); + return(DATA_DECOMPRESSION_ERR); + } + + /* sum of all pixels */ + sumall=readlonglong(infile); + /* # bits in quadrants */ + + qread(infile, (char *) nbitplanes, sizeof(nbitplanes)); + + stat = dodecode(infile, a, *nx, *ny, nbitplanes); + /* + * put sum of all pixels back into pixel 0 + */ + a[0] = (int) sumall; + return(stat); +} +/* ############################################################################ */ +static int decode64(unsigned char *infile, LONGLONG *a, int na, int *nx, int *ny, int *scale) +/* +char *infile; input file +LONGLONG *a; address of output array [nx][ny] +int na; size allocated for output array +int *nx,*ny; dimensions of image +int *scale; scale factor for digitization +*/ +{ +int stat; +LONGLONG sumall; +unsigned char nbitplanes[3]; +char tmagic[2]; + + /* initialize the byte read position to the beginning of the array */; + nextchar = 0; + + /* + * File starts either with special 2-byte magic code or with + * FITS keyword "SIMPLE =" + */ + qread(infile, tmagic, sizeof(tmagic)); + /* + * check for correct magic code value + */ + if (memcmp(tmagic,code_magic,sizeof(code_magic)) != 0) { + ffpmsg("bad file format"); + return(DATA_DECOMPRESSION_ERR); + } + *nx =readint(infile); /* x size of image */ + *ny =readint(infile); /* y size of image */ + *scale=readint(infile); /* scale factor for digitization */ + + if ((*nx) > INT_MAX/(*ny)) { + ffpmsg("numerical overflow during decompression"); + return(DATA_DECOMPRESSION_ERR); + } + if ((*nx)*(*ny) > na) { + ffpmsg("wrong allocation size during decompression"); + return(DATA_DECOMPRESSION_ERR); + } + + /* sum of all pixels */ + sumall=readlonglong(infile); + /* # bits in quadrants */ + + qread(infile, (char *) nbitplanes, sizeof(nbitplanes)); + + stat = dodecode64(infile, a, *nx, *ny, nbitplanes); + /* + * put sum of all pixels back into pixel 0 + */ + a[0] = sumall; + + return(stat); +} + + +/* ############################################################################ */ +/* ############################################################################ */ +/* Copyright (c) 1993 Association of Universities for Research + * in Astronomy. All rights reserved. Produced under National + * Aeronautics and Space Administration Contract No. NAS5-26555. + */ +/* dodecode.c Decode stream of characters on infile and return array + * + * This version encodes the different quadrants separately + * + * Programmer: R. White Date: 9 May 1991 + */ + +/* ############################################################################ */ +static int +dodecode(unsigned char *infile, int a[], int nx, int ny, unsigned char nbitplanes[3]) + +/* int a[]; + int nx,ny; Array dimensions are [nx][ny] + unsigned char nbitplanes[3]; Number of bit planes in quadrants +*/ +{ +int i, nel, nx2, ny2, stat; + + nel = nx*ny; + nx2 = (nx+1)/2; + ny2 = (ny+1)/2; + + /* + * initialize a to zero + */ + for (i=0; inqy) ? nqx : nqy; + log2n = (int) (log((float) nqmax)/log(2.0)+0.5); + if (nqmax > (1<= 0; bit--) { + /* + * Was bitplane was quadtree-coded or written directly? + */ + b = input_nybble(infile); + + if(b == 0) { + /* + * bit map was written directly + */ + read_bdirect(infile,a,n,nqx,nqy,scratch,bit); + } else if (b != 0xf) { + free(scratch); + ffpmsg("qtree_decode: bad format code"); + return(DATA_DECOMPRESSION_ERR); + } else { + /* + * bitmap was quadtree-coded, do log2n expansions + * + * read first code + */ + scratch[0] = input_huffman(infile); + /* + * now do log2n expansions, reading codes from file as necessary + */ + nx = 1; + ny = 1; + nfx = nqx; + nfy = nqy; + c = 1<>1; + nx = nx<<1; + ny = ny<<1; + if (nfx <= c) { nx -= 1; } else { nfx -= c; } + if (nfy <= c) { ny -= 1; } else { nfy -= c; } + qtree_expand(infile,scratch,nx,ny,scratch); + } + /* + * now copy last set of 4-bit codes to bitplane bit of array a + */ + qtree_bitins(scratch,nqx,nqy,a,n,bit); + } + } + free(scratch); + return(0); +} +/* ############################################################################ */ +static int +qtree_decode64(unsigned char *infile, LONGLONG a[], int n, int nqx, int nqy, int nbitplanes) + +/* +char *infile; +LONGLONG a[]; a is 2-D array with dimensions (n,n) +int n; length of full row in a +int nqx; partial length of row to decode +int nqy; partial length of column (<=n) +int nbitplanes; number of bitplanes to decode +*/ +{ +int log2n, k, bit, b, nqmax; +int nx,ny,nfx,nfy,c; +int nqx2, nqy2; +unsigned char *scratch; + + /* + * log2n is log2 of max(nqx,nqy) rounded up to next power of 2 + */ + nqmax = (nqx>nqy) ? nqx : nqy; + log2n = (int) (log((float) nqmax)/log(2.0)+0.5); + if (nqmax > (1<= 0; bit--) { + /* + * Was bitplane was quadtree-coded or written directly? + */ + b = input_nybble(infile); + + if(b == 0) { + /* + * bit map was written directly + */ + read_bdirect64(infile,a,n,nqx,nqy,scratch,bit); + } else if (b != 0xf) { + free(scratch); + ffpmsg("qtree_decode64: bad format code"); + return(DATA_DECOMPRESSION_ERR); + } else { + /* + * bitmap was quadtree-coded, do log2n expansions + * + * read first code + */ + scratch[0] = input_huffman(infile); + /* + * now do log2n expansions, reading codes from file as necessary + */ + nx = 1; + ny = 1; + nfx = nqx; + nfy = nqy; + c = 1<>1; + nx = nx<<1; + ny = ny<<1; + if (nfx <= c) { nx -= 1; } else { nfx -= c; } + if (nfy <= c) { ny -= 1; } else { nfy -= c; } + qtree_expand(infile,scratch,nx,ny,scratch); + } + /* + * now copy last set of 4-bit codes to bitplane bit of array a + */ + qtree_bitins64(scratch,nqx,nqy,a,n,bit); + } + } + free(scratch); + return(0); +} + + +/* ############################################################################ */ +/* + * do one quadtree expansion step on array a[(nqx+1)/2,(nqy+1)/2] + * results put into b[nqx,nqy] (which may be the same as a) + */ +static void +qtree_expand(unsigned char *infile, unsigned char a[], int nx, int ny, unsigned char b[]) +{ +int i; + + /* + * first copy a to b, expanding each 4-bit value + */ + qtree_copy(a,nx,ny,b,ny); + /* + * now read new 4-bit values into b for each non-zero element + */ + for (i = nx*ny-1; i >= 0; i--) { + if (b[i]) b[i] = input_huffman(infile); + } +} + +/* ############################################################################ */ +/* + * copy 4-bit values from a[(nx+1)/2,(ny+1)/2] to b[nx,ny], expanding + * each value to 2x2 pixels + * a,b may be same array + */ +static void +qtree_copy(unsigned char a[], int nx, int ny, unsigned char b[], int n) +/* int n; declared y dimension of b */ +{ +int i, j, k, nx2, ny2; +int s00, s10; + + /* + * first copy 4-bit values to b + * start at end in case a,b are same array + */ + nx2 = (nx+1)/2; + ny2 = (ny+1)/2; + k = ny2*(nx2-1)+ny2-1; /* k is index of a[i,j] */ + for (i = nx2-1; i >= 0; i--) { + s00 = 2*(n*i+ny2-1); /* s00 is index of b[2*i,2*j] */ + for (j = ny2-1; j >= 0; j--) { + b[s00] = a[k]; + k -= 1; + s00 -= 2; + } + } + /* + * now expand each 2x2 block + */ + for (i = 0; i>1) & 1; + b[s00+1] = (b[s00]>>2) & 1; + b[s00 ] = (b[s00]>>3) & 1; +*/ + + s00 += 2; + s10 += 2; + } + + if (j < ny) { + /* + * row size is odd, do last element in row + * s00+1, s10+1 are off edge + */ + /* not worth converting this to use 16 case statements */ + b[s10 ] = (b[s00]>>1) & 1; + b[s00 ] = (b[s00]>>3) & 1; + } + } + if (i < nx) { + /* + * column size is odd, do last row + * s10, s10+1 are off edge + */ + s00 = n*i; + for (j = 0; j>2) & 1; + b[s00 ] = (b[s00]>>3) & 1; + s00 += 2; + } + if (j < ny) { + /* + * both row and column size are odd, do corner element + * s00+1, s10, s10+1 are off edge + */ + /* not worth converting this to use 16 case statements */ + b[s00 ] = (b[s00]>>3) & 1; + } + } +} + +/* ############################################################################ */ +/* + * Copy 4-bit values from a[(nx+1)/2,(ny+1)/2] to b[nx,ny], expanding + * each value to 2x2 pixels and inserting into bitplane BIT of B. + * A,B may NOT be same array (it wouldn't make sense to be inserting + * bits into the same array anyway.) + */ +static void +qtree_bitins(unsigned char a[], int nx, int ny, int b[], int n, int bit) +/* + int n; declared y dimension of b +*/ +{ +int i, j, k; +int s00; +int plane_val; + + plane_val = 1 << bit; + + /* + * expand each 2x2 block + */ + k = 0; /* k is index of a[i/2,j/2] */ + for (i = 0; i>1) & 1) << bit; + b[s00+1] |= ((a[k]>>2) & 1) << bit; + b[s00 ] |= ((a[k]>>3) & 1) << bit; +*/ + s00 += 2; +/* s10 += 2; */ + k += 1; + } + if (j < ny) { + /* + * row size is odd, do last element in row + * s00+1, s10+1 are off edge + */ + + switch (a[k]) { + case(0): + break; + case(1): + break; + case(2): + b[s00+n ] |= plane_val; + break; + case(3): + b[s00+n ] |= plane_val; + break; + case(4): + break; + case(5): + break; + case(6): + b[s00+n ] |= plane_val; + break; + case(7): + b[s00+n ] |= plane_val; + break; + case(8): + b[s00 ] |= plane_val; + break; + case(9): + b[s00 ] |= plane_val; + break; + case(10): + b[s00+n ] |= plane_val; + b[s00 ] |= plane_val; + break; + case(11): + b[s00+n ] |= plane_val; + b[s00 ] |= plane_val; + break; + case(12): + b[s00 ] |= plane_val; + break; + case(13): + b[s00 ] |= plane_val; + break; + case(14): + b[s00+n ] |= plane_val; + b[s00 ] |= plane_val; + break; + case(15): + b[s00+n ] |= plane_val; + b[s00 ] |= plane_val; + break; + } + +/* + b[s10 ] |= ((a[k]>>1) & 1) << bit; + b[s00 ] |= ((a[k]>>3) & 1) << bit; +*/ + k += 1; + } + } + if (i < nx) { + /* + * column size is odd, do last row + * s10, s10+1 are off edge + */ + s00 = n*i; + for (j = 0; j>2) & 1) << bit; + b[s00 ] |= ((a[k]>>3) & 1) << bit; +*/ + + s00 += 2; + k += 1; + } + if (j < ny) { + /* + * both row and column size are odd, do corner element + * s00+1, s10, s10+1 are off edge + */ + + switch (a[k]) { + case(0): + break; + case(1): + break; + case(2): + break; + case(3): + break; + case(4): + break; + case(5): + break; + case(6): + break; + case(7): + break; + case(8): + b[s00 ] |= plane_val; + break; + case(9): + b[s00 ] |= plane_val; + break; + case(10): + b[s00 ] |= plane_val; + break; + case(11): + b[s00 ] |= plane_val; + break; + case(12): + b[s00 ] |= plane_val; + break; + case(13): + b[s00 ] |= plane_val; + break; + case(14): + b[s00 ] |= plane_val; + break; + case(15): + b[s00 ] |= plane_val; + break; + } + +/* + b[s00 ] |= ((a[k]>>3) & 1) << bit; +*/ + k += 1; + } + } +} +/* ############################################################################ */ +/* + * Copy 4-bit values from a[(nx+1)/2,(ny+1)/2] to b[nx,ny], expanding + * each value to 2x2 pixels and inserting into bitplane BIT of B. + * A,B may NOT be same array (it wouldn't make sense to be inserting + * bits into the same array anyway.) + */ +static void +qtree_bitins64(unsigned char a[], int nx, int ny, LONGLONG b[], int n, int bit) +/* + int n; declared y dimension of b +*/ +{ +int i, j, k; +int s00; +LONGLONG plane_val; + + plane_val = ((LONGLONG) 1) << bit; + + /* + * expand each 2x2 block + */ + k = 0; /* k is index of a[i/2,j/2] */ + for (i = 0; i>1) & 1) << bit; + b[s00+1] |= ((((LONGLONG)a[k])>>2) & 1) << bit; + b[s00 ] |= ((((LONGLONG)a[k])>>3) & 1) << bit; +*/ + s00 += 2; +/* s10 += 2; */ + k += 1; + } + if (j < ny) { + /* + * row size is odd, do last element in row + * s00+1, s10+1 are off edge + */ + + switch (a[k]) { + case(0): + break; + case(1): + break; + case(2): + b[s00+n ] |= plane_val; + break; + case(3): + b[s00+n ] |= plane_val; + break; + case(4): + break; + case(5): + break; + case(6): + b[s00+n ] |= plane_val; + break; + case(7): + b[s00+n ] |= plane_val; + break; + case(8): + b[s00 ] |= plane_val; + break; + case(9): + b[s00 ] |= plane_val; + break; + case(10): + b[s00+n ] |= plane_val; + b[s00 ] |= plane_val; + break; + case(11): + b[s00+n ] |= plane_val; + b[s00 ] |= plane_val; + break; + case(12): + b[s00 ] |= plane_val; + break; + case(13): + b[s00 ] |= plane_val; + break; + case(14): + b[s00+n ] |= plane_val; + b[s00 ] |= plane_val; + break; + case(15): + b[s00+n ] |= plane_val; + b[s00 ] |= plane_val; + break; + } +/* + b[s10 ] |= ((((LONGLONG)a[k])>>1) & 1) << bit; + b[s00 ] |= ((((LONGLONG)a[k])>>3) & 1) << bit; +*/ + k += 1; + } + } + if (i < nx) { + /* + * column size is odd, do last row + * s10, s10+1 are off edge + */ + s00 = n*i; + for (j = 0; j>2) & 1) << bit; + b[s00 ] |= ((((LONGLONG)a[k])>>3) & 1) << bit; +*/ + s00 += 2; + k += 1; + } + if (j < ny) { + /* + * both row and column size are odd, do corner element + * s00+1, s10, s10+1 are off edge + */ + + switch (a[k]) { + case(0): + break; + case(1): + break; + case(2): + break; + case(3): + break; + case(4): + break; + case(5): + break; + case(6): + break; + case(7): + break; + case(8): + b[s00 ] |= plane_val; + break; + case(9): + b[s00 ] |= plane_val; + break; + case(10): + b[s00 ] |= plane_val; + break; + case(11): + b[s00 ] |= plane_val; + break; + case(12): + b[s00 ] |= plane_val; + break; + case(13): + b[s00 ] |= plane_val; + break; + case(14): + b[s00 ] |= plane_val; + break; + case(15): + b[s00 ] |= plane_val; + break; + } +/* + b[s00 ] |= ((((LONGLONG)a[k])>>3) & 1) << bit; +*/ + k += 1; + } + } +} + +/* ############################################################################ */ +static void +read_bdirect(unsigned char *infile, int a[], int n, int nqx, int nqy, unsigned char scratch[], int bit) +{ + /* + * read bit image packed 4 pixels/nybble + */ +/* +int i; + for (i = 0; i < ((nqx+1)/2) * ((nqy+1)/2); i++) { + scratch[i] = input_nybble(infile); + } +*/ + input_nnybble(infile, ((nqx+1)/2) * ((nqy+1)/2), scratch); + + /* + * insert in bitplane BIT of image A + */ + qtree_bitins(scratch,nqx,nqy,a,n,bit); +} +/* ############################################################################ */ +static void +read_bdirect64(unsigned char *infile, LONGLONG a[], int n, int nqx, int nqy, unsigned char scratch[], int bit) +{ + /* + * read bit image packed 4 pixels/nybble + */ +/* +int i; + for (i = 0; i < ((nqx+1)/2) * ((nqy+1)/2); i++) { + scratch[i] = input_nybble(infile); + } +*/ + input_nnybble(infile, ((nqx+1)/2) * ((nqy+1)/2), scratch); + + /* + * insert in bitplane BIT of image A + */ + qtree_bitins64(scratch,nqx,nqy,a,n,bit); +} + +/* ############################################################################ */ +/* + * Huffman decoding for fixed codes + * + * Coded values range from 0-15 + * + * Huffman code values (hex): + * + * 3e, 00, 01, 08, 02, 09, 1a, 1b, + * 03, 1c, 0a, 1d, 0b, 1e, 3f, 0c + * + * and number of bits in each code: + * + * 6, 3, 3, 4, 3, 4, 5, 5, + * 3, 5, 4, 5, 4, 5, 6, 4 + */ +static int input_huffman(unsigned char *infile) +{ +int c; + + /* + * get first 3 bits to start + */ + c = input_nbits(infile,3); + if (c < 4) { + /* + * this is all we need + * return 1,2,4,8 for c=0,1,2,3 + */ + return(1<>bits_to_go) & 1); +} + +/* ############################################################################ */ +/* INPUT N BITS (N must be <= 8) */ + +static int input_nbits(unsigned char *infile, int n) +{ + /* AND mask for retreiving the right-most n bits */ + static int mask[9] = {0, 1, 3, 7, 15, 31, 63, 127, 255}; + + if (bits_to_go < n) { + /* + * need another byte's worth of bits + */ + + buffer2 = (buffer2<<8) | (int) infile[nextchar]; + nextchar++; + bits_to_go += 8; + } + /* + * now pick off the first n bits + */ + bits_to_go -= n; + + /* there was a slight gain in speed by replacing the following line */ +/* return( (buffer2>>bits_to_go) & ((1<>bits_to_go) & (*(mask+n)) ); +} +/* ############################################################################ */ +/* INPUT 4 BITS */ + +static int input_nybble(unsigned char *infile) +{ + if (bits_to_go < 4) { + /* + * need another byte's worth of bits + */ + + buffer2 = (buffer2<<8) | (int) infile[nextchar]; + nextchar++; + bits_to_go += 8; + } + /* + * now pick off the first 4 bits + */ + bits_to_go -= 4; + + return( (buffer2>>bits_to_go) & 15 ); +} +/* ############################################################################ */ +/* INPUT array of 4 BITS */ + +static int input_nnybble(unsigned char *infile, int n, unsigned char array[]) +{ + /* copy n 4-bit nybbles from infile to the lower 4 bits of array */ + +int ii, kk, shift1, shift2; + +/* forcing byte alignment doesn;t help, and even makes it go slightly slower +if (bits_to_go != 8) input_nbits(infile, bits_to_go); +*/ + if (n == 1) { + array[0] = input_nybble(infile); + return(0); + } + + if (bits_to_go == 8) { + /* + already have 2 full nybbles in buffer2, so + backspace the infile array to reuse last char + */ + nextchar--; + bits_to_go = 0; + } + + /* bits_to_go now has a value in the range 0 - 7. After adding */ + /* another byte, bits_to_go effectively will be in range 8 - 15 */ + + shift1 = bits_to_go + 4; /* shift1 will be in range 4 - 11 */ + shift2 = bits_to_go; /* shift2 will be in range 0 - 7 */ + kk = 0; + + /* special case */ + if (bits_to_go == 0) + { + for (ii = 0; ii < n/2; ii++) { + /* + * refill the buffer with next byte + */ + buffer2 = (buffer2<<8) | (int) infile[nextchar]; + nextchar++; + array[kk] = (int) ((buffer2>>4) & 15); + array[kk + 1] = (int) ((buffer2) & 15); /* no shift required */ + kk += 2; + } + } + else + { + for (ii = 0; ii < n/2; ii++) { + /* + * refill the buffer with next byte + */ + buffer2 = (buffer2<<8) | (int) infile[nextchar]; + nextchar++; + array[kk] = (int) ((buffer2>>shift1) & 15); + array[kk + 1] = (int) ((buffer2>>shift2) & 15); + kk += 2; + } + } + + + if (ii * 2 != n) { /* have to read last odd byte */ + array[n-1] = input_nybble(infile); + } + + return( (buffer2>>bits_to_go) & 15 ); +} diff --git a/cextern/cfitsio/lib/fitsio2.h b/cextern/cfitsio/lib/fitsio2.h new file mode 100644 index 000000000000..d1daea685b3a --- /dev/null +++ b/cextern/cfitsio/lib/fitsio2.h @@ -0,0 +1,23 @@ +#ifndef LONGLONG_TYPE +typedef long long LONGLONG; +typedef unsigned long long ULONGLONG; +#define LONGLONG_TYPE +#endif + +# define DATA_COMPRESSION_ERR 413 +# define DATA_DECOMPRESSION_ERR 414 + +void ffpmsg(const char *err_message); + +#define FFLOCK +#define FFUNLOCK + +#define N_RANDOM 10000 + +#define MEMORY_ALLOCATION 113 + +#define NO_DITHER -1 +#define SUBTRACTIVE_DITHER_1 1 +#define SUBTRACTIVE_DITHER_2 2 + +int fits_init_randoms(void); diff --git a/cextern/cfitsio/lib/pliocomp.c b/cextern/cfitsio/lib/pliocomp.c new file mode 100644 index 000000000000..47a636918cd0 --- /dev/null +++ b/cextern/cfitsio/lib/pliocomp.c @@ -0,0 +1,331 @@ +/* stdlib is needed for the abs function */ +#include +/* + The following prototype code was provided by Doug Tody, NRAO, for + performing conversion between pixel arrays and line lists. The + compression technique is used in IRAF. +*/ +int pl_p2li (int *pxsrc, int xs, short *lldst, int npix); +int pl_l2pi (short *ll_src, int xs, int *px_dst, int npix); + + +/* + * PL_P2L -- Convert a pixel array to a line list. The length of the list is + * returned as the function value. + * + * Translated from the SPP version using xc -f, f2c. 8Sep99 DCT. + */ + +#ifndef min +#define min(a,b) (((a)<(b))?(a):(b)) +#endif +#ifndef max +#define max(a,b) (((a)>(b))?(a):(b)) +#endif + +int pl_p2li (int *pxsrc, int xs, short *lldst, int npix) +/* int *pxsrc; input pixel array */ +/* int xs; starting index in pxsrc (?) */ +/* short *lldst; encoded line list */ +/* int npix; number of pixels to convert */ +{ + /* System generated locals */ + int ret_val, i__1, i__2, i__3; + + /* Local variables */ + int zero, v, x1, hi, ip, dv, xe, np, op, iz, nv = 0, pv, nz; + + /* Parameter adjustments */ + --lldst; + --pxsrc; + + /* Function Body */ + if (! (npix <= 0)) { + goto L110; + } + ret_val = 0; + goto L100; +L110: + lldst[3] = -100; + lldst[2] = 7; + lldst[1] = 0; + lldst[6] = 0; + lldst[7] = 0; + xe = xs + npix - 1; + op = 8; + zero = 0; +/* Computing MAX */ + i__1 = zero, i__2 = pxsrc[xs]; + pv = max(i__1,i__2); + x1 = xs; + iz = xs; + hi = 1; + i__1 = xe; + for (ip = xs; ip <= i__1; ++ip) { + if (! (ip < xe)) { + goto L130; + } +/* Computing MAX */ + i__2 = zero, i__3 = pxsrc[ip + 1]; + nv = max(i__2,i__3); + if (! (nv == pv)) { + goto L140; + } + goto L120; +L140: + if (! (pv == 0)) { + goto L150; + } + pv = nv; + x1 = ip + 1; + goto L120; +L150: + goto L131; +L130: + if (! (pv == 0)) { + goto L160; + } + x1 = xe + 1; +L160: +L131: + np = ip - x1 + 1; + nz = x1 - iz; + if (! (pv > 0)) { + goto L170; + } + dv = pv - hi; + if (! (dv != 0)) { + goto L180; + } + hi = pv; + if (! (abs(dv) > 4095)) { + goto L190; + } + lldst[op] = (short) ((pv & 4095) + 4096); + ++op; + lldst[op] = (short) (pv / 4096); + ++op; + goto L191; +L190: + if (! (dv < 0)) { + goto L200; + } + lldst[op] = (short) (-dv + 12288); + goto L201; +L200: + lldst[op] = (short) (dv + 8192); +L201: + ++op; + if (! (np == 1 && nz == 0)) { + goto L210; + } + v = lldst[op - 1]; + lldst[op - 1] = (short) (v | 16384); + goto L91; +L210: +L191: +L180: +L170: + if (! (nz > 0)) { + goto L220; + } +L230: + if (! (nz > 0)) { + goto L232; + } + lldst[op] = (short) min(4095,nz); + ++op; +/* L231: */ + nz += -4095; + goto L230; +L232: + if (! (np == 1 && pv > 0)) { + goto L240; + } + lldst[op - 1] = (short) (lldst[op - 1] + 20481); + goto L91; +L240: +L220: +L250: + if (! (np > 0)) { + goto L252; + } + lldst[op] = (short) (min(4095,np) + 16384); + ++op; +/* L251: */ + np += -4095; + goto L250; +L252: +L91: + x1 = ip + 1; + iz = x1; + pv = nv; +L120: + ; + } +/* L121: */ + lldst[4] = (short) ((op - 1) % 32768); + lldst[5] = (short) ((op - 1) / 32768); + ret_val = op - 1; + goto L100; +L100: + return ret_val; +} /* plp2li_ */ + +/* + * PL_L2PI -- Translate a PLIO line list into an integer pixel array. + * The number of pixels output (always npix) is returned as the function + * value. + * + * Translated from the SPP version using xc -f, f2c. 8Sep99 DCT. + */ + +int pl_l2pi (short *ll_src, int xs, int *px_dst, int npix) +/* short *ll_src; encoded line list */ +/* int xs; starting index in ll_src */ +/* int *px_dst; output pixel array */ +/* int npix; number of pixels to convert */ +{ + /* System generated locals */ + int ret_val, i__1, i__2; + + /* Local variables */ + int data, sw0001, otop, i__, lllen, i1, i2, x1, x2, ip, xe, np, + op, pv, opcode, llfirt; + int skipwd; + + /* Parameter adjustments */ + --px_dst; + --ll_src; + + /* Function Body */ + if (! (ll_src[3] > 0)) { + goto L110; + } + lllen = ll_src[3]; + llfirt = 4; + goto L111; +L110: + lllen = (ll_src[5] << 15) + ll_src[4]; + llfirt = ll_src[2] + 1; +L111: + if (! (npix <= 0 || lllen <= 0)) { + goto L120; + } + ret_val = 0; + goto L100; +L120: + xe = xs + npix - 1; + skipwd = 0; + op = 1; + x1 = 1; + pv = 1; + i__1 = lllen; + for (ip = llfirt; ip <= i__1; ++ip) { + if (! skipwd) { + goto L140; + } + skipwd = 0; + goto L130; +L140: + opcode = ll_src[ip] / 4096; + data = ll_src[ip] & 4095; + sw0001 = opcode; + goto L150; +L160: + x2 = x1 + data - 1; + i1 = max(x1,xs); + i2 = min(x2,xe); + np = i2 - i1 + 1; + if (! (np > 0)) { + goto L170; + } + otop = op + np - 1; + if (! (opcode == 4)) { + goto L180; + } + i__2 = otop; + for (i__ = op; i__ <= i__2; ++i__) { + px_dst[i__] = pv; +/* L190: */ + } +/* L191: */ + goto L181; +L180: + i__2 = otop; + for (i__ = op; i__ <= i__2; ++i__) { + px_dst[i__] = 0; +/* L200: */ + } +/* L201: */ + if (! (opcode == 5 && i2 == x2)) { + goto L210; + } + px_dst[otop] = pv; +L210: +L181: + op = otop + 1; +L170: + x1 = x2 + 1; + goto L151; +L220: + pv = (ll_src[ip + 1] << 12) + data; + skipwd = 1; + goto L151; +L230: + pv += data; + goto L151; +L240: + pv -= data; + goto L151; +L250: + pv += data; + goto L91; +L260: + pv -= data; +L91: + if (! (x1 >= xs && x1 <= xe)) { + goto L270; + } + px_dst[op] = pv; + ++op; +L270: + ++x1; + goto L151; +L150: + ++sw0001; + if (sw0001 < 1 || sw0001 > 8) { + goto L151; + } + switch ((int)sw0001) { + case 1: goto L160; + case 2: goto L220; + case 3: goto L230; + case 4: goto L240; + case 5: goto L160; + case 6: goto L160; + case 7: goto L250; + case 8: goto L260; + } +L151: + if (! (x1 > xe)) { + goto L280; + } + goto L131; +L280: +L130: + ; + } +L131: + i__1 = npix; + for (i__ = op; i__ <= i__1; ++i__) { + px_dst[i__] = 0; +/* L290: */ + } +/* L291: */ + ret_val = npix; + goto L100; +L100: + return ret_val; +} /* pll2pi_ */ + diff --git a/cextern/cfitsio/lib/quantize.c b/cextern/cfitsio/lib/quantize.c new file mode 100644 index 000000000000..fac4b8fb85b9 --- /dev/null +++ b/cextern/cfitsio/lib/quantize.c @@ -0,0 +1,3945 @@ +/* + The following code is based on algorithms written by Richard White at STScI and made + available for use in CFITSIO in July 1999 and updated in January 2008. +*/ + +# include +# include +# include +# include +# include + +#include "fitsio2.h" + +/* nearest integer function */ +# define NINT(x) ((x >= 0.) ? (int) (x + 0.5) : (int) (x - 0.5)) + +#define NULL_VALUE -2147483647 /* value used to represent undefined pixels */ +#define ZERO_VALUE -2147483646 /* value used to represent zero-valued pixels */ +#define N_RESERVED_VALUES 10 /* number of reserved values, starting with */ + /* and including NULL_VALUE. These values */ + /* may not be used to represent the quantized */ + /* and scaled floating point pixel values */ + /* If lossy Hcompression is used, and the */ + /* array contains null values, then it is also */ + /* possible for the compressed values to slightly */ + /* exceed the range of the actual (lossless) values */ + /* so we must reserve a little more space */ + +/* more than this many standard deviations from the mean is an outlier */ +# define SIGMA_CLIP 5. +# define NITER 3 /* number of sigma-clipping iterations */ + +static int FnMeanSigma_short(short *array, long npix, int nullcheck, + short nullvalue, long *ngoodpix, double *mean, double *sigma, int *status); +static int FnMeanSigma_int(int *array, long npix, int nullcheck, + int nullvalue, long *ngoodpix, double *mean, double *sigma, int *status); +static int FnMeanSigma_float(float *array, long npix, int nullcheck, + float nullvalue, long *ngoodpix, double *mean, double *sigma, int *status); +static int FnMeanSigma_double(double *array, long npix, int nullcheck, + double nullvalue, long *ngoodpix, double *mean, double *sigma, int *status); + +static int FnNoise5_short(short *array, long nx, long ny, int nullcheck, + short nullvalue, long *ngood, short *minval, short *maxval, + double *n2, double *n3, double *n5, int *status); +static int FnNoise5_int(int *array, long nx, long ny, int nullcheck, + int nullvalue, long *ngood, int *minval, int *maxval, + double *n2, double *n3, double *n5, int *status); +static int FnNoise5_float(float *array, long nx, long ny, int nullcheck, + float nullvalue, long *ngood, float *minval, float *maxval, + double *n2, double *n3, double *n5, int *status); +static int FnNoise5_double(double *array, long nx, long ny, int nullcheck, + double nullvalue, long *ngood, double *minval, double *maxval, + double *n2, double *n3, double *n5, int *status); + +static int FnNoise3_short(short *array, long nx, long ny, int nullcheck, + short nullvalue, long *ngood, short *minval, short *maxval, double *noise, int *status); +static int FnNoise3_int(int *array, long nx, long ny, int nullcheck, + int nullvalue, long *ngood, int *minval, int *maxval, double *noise, int *status); +static int FnNoise3_float(float *array, long nx, long ny, int nullcheck, + float nullvalue, long *ngood, float *minval, float *maxval, double *noise, int *status); +static int FnNoise3_double(double *array, long nx, long ny, int nullcheck, + double nullvalue, long *ngood, double *minval, double *maxval, double *noise, int *status); + +static int FnNoise1_short(short *array, long nx, long ny, + int nullcheck, short nullvalue, double *noise, int *status); +static int FnNoise1_int(int *array, long nx, long ny, + int nullcheck, int nullvalue, double *noise, int *status); +static int FnNoise1_float(float *array, long nx, long ny, + int nullcheck, float nullvalue, double *noise, int *status); +static int FnNoise1_double(double *array, long nx, long ny, + int nullcheck, double nullvalue, double *noise, int *status); + +static int FnCompare_short (const void *, const void *); +static int FnCompare_int (const void *, const void *); +static int FnCompare_float (const void *, const void *); +static int FnCompare_double (const void *, const void *); +static float quick_select_float(float arr[], int n); +static short quick_select_short(short arr[], int n); +static int quick_select_int(int arr[], int n); +static LONGLONG quick_select_longlong(LONGLONG arr[], int n); +static double quick_select_double(double arr[], int n); + +/*---------------------------------------------------------------------------*/ +int fits_quantize_float (long row, float fdata[], long nxpix, long nypix, int nullcheck, + float in_null_value, float qlevel, int dither_method, int idata[], double *bscale, + double *bzero, int *iminval, int *imaxval) { + +/* arguments: +long row i: if positive, used to calculate random dithering seed value + (this is only used when dithering the quantized values) +float fdata[] i: array of image pixels to be compressed +long nxpix i: number of pixels in each row of fdata +long nypix i: number of rows in fdata +nullcheck i: check for nullvalues in fdata? +float in_null_value i: value used to represent undefined pixels in fdata +float qlevel i: quantization level +int dither_method i; which dithering method to use +int idata[] o: values of fdata after applying bzero and bscale +double bscale o: scale factor +double bzero o: zero offset +int iminval o: minimum quantized value that is returned +int imaxval o: maximum quantized value that is returned + +The function value will be one if the input fdata were copied to idata; +in this case the parameters bscale and bzero can be used to convert back to +nearly the original floating point values: fdata ~= idata * bscale + bzero. +If the function value is zero, the data were not copied to idata. +*/ + + int status, iseed = 0; + long i, nx, ngood = 0; + double stdev, noise2, noise3, noise5; /* MAD 2nd, 3rd, and 5th order noise values */ + float minval = 0., maxval = 0.; /* min & max of fdata */ + double delta; /* bscale, 1 in idata = delta in fdata */ + double zeropt; /* bzero */ + double temp; + int nextrand = 0; + extern float *fits_rand_value; /* this is defined in imcompress.c */ + LONGLONG iqfactor; + + nx = nxpix * nypix; + if (nx <= 1) { + *bscale = 1.; + *bzero = 0.; + return (0); + } + + if (qlevel >= 0.) { + + /* estimate background noise using MAD pixel differences */ + FnNoise5_float(fdata, nxpix, nypix, nullcheck, in_null_value, &ngood, + &minval, &maxval, &noise2, &noise3, &noise5, &status); + + if (nullcheck && ngood == 0) { /* special case of an image filled with Nulls */ + /* set parameters to dummy values, which are not used */ + minval = 0.; + maxval = 1.; + stdev = 1; + } else { + + /* use the minimum of noise2, noise3, and noise5 as the best noise value */ + stdev = noise3; + if (noise2 != 0. && noise2 < stdev) stdev = noise2; + if (noise5 != 0. && noise5 < stdev) stdev = noise5; + } + + if (qlevel == 0.) + delta = stdev / 4.; /* default quantization */ + else + delta = stdev / qlevel; + + if (delta == 0.) + return (0); /* don't quantize */ + + } else { + /* negative value represents the absolute quantization level */ + delta = -qlevel; + + /* only nned to calculate the min and max values */ + FnNoise3_float(fdata, nxpix, nypix, nullcheck, in_null_value, &ngood, + &minval, &maxval, 0, &status); + } + + /* check that the range of quantized levels is not > range of int */ + if ((maxval - minval) / delta > 2. * 2147483647. - N_RESERVED_VALUES ) + return (0); /* don't quantize */ + + if (row > 0) { /* we need to dither the quantized values */ + if (!fits_rand_value) + if (fits_init_randoms()) return(MEMORY_ALLOCATION); + + /* initialize the index to the next random number in the list */ + iseed = (int) ((row - 1) % N_RANDOM); + nextrand = (int) (fits_rand_value[iseed] * 500.); + } + + if (ngood == nx) { /* don't have to check for nulls */ + /* return all positive values, if possible since some */ + /* compression algorithms either only work for positive integers, */ + /* or are more efficient. */ + + if (dither_method == SUBTRACTIVE_DITHER_2) + { + /* shift the range to be close to the value used to represent zeros */ + zeropt = minval - delta * (NULL_VALUE + N_RESERVED_VALUES); + } + else if ((maxval - minval) / delta < 2147483647. - N_RESERVED_VALUES ) + { + zeropt = minval; + /* fudge the zero point so it is an integer multiple of delta */ + /* This helps to ensure the same scaling will be performed if the */ + /* file undergoes multiple fpack/funpack cycles */ + iqfactor = (LONGLONG) (zeropt/delta + 0.5); + zeropt = iqfactor * delta; + } + else + { + /* center the quantized levels around zero */ + zeropt = (minval + maxval) / 2.; + } + + if (row > 0) { /* dither the values when quantizing */ + for (i = 0; i < nx; i++) { + + if (dither_method == SUBTRACTIVE_DITHER_2 && fdata[i] == 0.0) { + idata[i] = ZERO_VALUE; + } else { + idata[i] = NINT((((double) fdata[i] - zeropt) / delta) + fits_rand_value[nextrand] - 0.5); + } + + nextrand++; + if (nextrand == N_RANDOM) { + iseed++; + if (iseed == N_RANDOM) iseed = 0; + nextrand = (int) (fits_rand_value[iseed] * 500); + } + } + } else { /* do not dither the values */ + + for (i = 0; i < nx; i++) { + idata[i] = NINT ((fdata[i] - zeropt) / delta); + } + } + } + else { + /* data contains null values; shift the range to be */ + /* close to the value used to represent null values */ + zeropt = minval - delta * (NULL_VALUE + N_RESERVED_VALUES); + + if (row > 0) { /* dither the values */ + for (i = 0; i < nx; i++) { + if (fdata[i] != in_null_value) { + if (dither_method == SUBTRACTIVE_DITHER_2 && fdata[i] == 0.0) { + idata[i] = ZERO_VALUE; + } else { + idata[i] = NINT((((double) fdata[i] - zeropt) / delta) + fits_rand_value[nextrand] - 0.5); + } + } else { + idata[i] = NULL_VALUE; + } + + /* increment the random number index, regardless */ + nextrand++; + if (nextrand == N_RANDOM) { + iseed++; + if (iseed == N_RANDOM) iseed = 0; + nextrand = (int) (fits_rand_value[iseed] * 500); + } + } + } else { /* do not dither the values */ + for (i = 0; i < nx; i++) { + + if (fdata[i] != in_null_value) { + idata[i] = NINT((fdata[i] - zeropt) / delta); + } else { + idata[i] = NULL_VALUE; + } + } + } + } + + /* calc min and max values */ + temp = (minval - zeropt) / delta; + *iminval = NINT (temp); + temp = (maxval - zeropt) / delta; + *imaxval = NINT (temp); + + *bscale = delta; + *bzero = zeropt; + return (1); /* yes, data have been quantized */ +} +/*---------------------------------------------------------------------------*/ +int fits_quantize_double (long row, double fdata[], long nxpix, long nypix, int nullcheck, + double in_null_value, float qlevel, int dither_method, int idata[], double *bscale, + double *bzero, int *iminval, int *imaxval) { + +/* arguments: +long row i: tile number = row number in the binary table + (this is only used when dithering the quantized values) +double fdata[] i: array of image pixels to be compressed +long nxpix i: number of pixels in each row of fdata +long nypix i: number of rows in fdata +nullcheck i: check for nullvalues in fdata? +double in_null_value i: value used to represent undefined pixels in fdata +float qlevel i: quantization level +int dither_method i; which dithering method to use +int idata[] o: values of fdata after applying bzero and bscale +double bscale o: scale factor +double bzero o: zero offset +int iminval o: minimum quantized value that is returned +int imaxval o: maximum quantized value that is returned + +The function value will be one if the input fdata were copied to idata; +in this case the parameters bscale and bzero can be used to convert back to +nearly the original floating point values: fdata ~= idata * bscale + bzero. +If the function value is zero, the data were not copied to idata. +*/ + + int status, iseed = 0; + long i, nx, ngood = 0; + double stdev, noise2 = 0., noise3 = 0., noise5 = 0.; /* MAD 2nd, 3rd, and 5th order noise values */ + double minval = 0., maxval = 0.; /* min & max of fdata */ + double delta; /* bscale, 1 in idata = delta in fdata */ + double zeropt; /* bzero */ + double temp; + int nextrand = 0; + extern float *fits_rand_value; + LONGLONG iqfactor; + + nx = nxpix * nypix; + if (nx <= 1) { + *bscale = 1.; + *bzero = 0.; + return (0); + } + + if (qlevel >= 0.) { + + /* estimate background noise using MAD pixel differences */ + FnNoise5_double(fdata, nxpix, nypix, nullcheck, in_null_value, &ngood, + &minval, &maxval, &noise2, &noise3, &noise5, &status); + + if (nullcheck && ngood == 0) { /* special case of an image filled with Nulls */ + /* set parameters to dummy values, which are not used */ + minval = 0.; + maxval = 1.; + stdev = 1; + } else { + + /* use the minimum of noise2, noise3, and noise5 as the best noise value */ + stdev = noise3; + if (noise2 != 0. && noise2 < stdev) stdev = noise2; + if (noise5 != 0. && noise5 < stdev) stdev = noise5; + } + + if (qlevel == 0.) + delta = stdev / 4.; /* default quantization */ + else + delta = stdev / qlevel; + + if (delta == 0.) + return (0); /* don't quantize */ + + } else { + /* negative value represents the absolute quantization level */ + delta = -qlevel; + + /* only nned to calculate the min and max values */ + FnNoise3_double(fdata, nxpix, nypix, nullcheck, in_null_value, &ngood, + &minval, &maxval, 0, &status); + } + + /* check that the range of quantized levels is not > range of int */ + if ((maxval - minval) / delta > 2. * 2147483647. - N_RESERVED_VALUES ) + return (0); /* don't quantize */ + + if (row > 0) { /* we need to dither the quantized values */ + if (!fits_rand_value) + if (fits_init_randoms()) return(MEMORY_ALLOCATION); + + /* initialize the index to the next random number in the list */ + iseed = (int) ((row - 1) % N_RANDOM); + nextrand = (int) (fits_rand_value[iseed] * 500); + } + + if (ngood == nx) { /* don't have to check for nulls */ + /* return all positive values, if possible since some */ + /* compression algorithms either only work for positive integers, */ + /* or are more efficient. */ + + if (dither_method == SUBTRACTIVE_DITHER_2) + { + /* shift the range to be close to the value used to represent zeros */ + zeropt = minval - delta * (NULL_VALUE + N_RESERVED_VALUES); + } + else if ((maxval - minval) / delta < 2147483647. - N_RESERVED_VALUES ) + { + zeropt = minval; + /* fudge the zero point so it is an integer multiple of delta */ + /* This helps to ensure the same scaling will be performed if the */ + /* file undergoes multiple fpack/funpack cycles */ + iqfactor = (LONGLONG) (zeropt/delta + 0.5); + zeropt = iqfactor * delta; + } + else + { + /* center the quantized levels around zero */ + zeropt = (minval + maxval) / 2.; + } + + if (row > 0) { /* dither the values when quantizing */ + for (i = 0; i < nx; i++) { + + if (dither_method == SUBTRACTIVE_DITHER_2 && fdata[i] == 0.0) { + idata[i] = ZERO_VALUE; + } else { + idata[i] = NINT((((double) fdata[i] - zeropt) / delta) + fits_rand_value[nextrand] - 0.5); + } + + nextrand++; + if (nextrand == N_RANDOM) { + iseed++; + if (iseed == N_RANDOM) iseed = 0; + nextrand = (int) (fits_rand_value[iseed] * 500); + } + } + } else { /* do not dither the values */ + + for (i = 0; i < nx; i++) { + idata[i] = NINT ((fdata[i] - zeropt) / delta); + } + } + } + else { + /* data contains null values; shift the range to be */ + /* close to the value used to represent null values */ + zeropt = minval - delta * (NULL_VALUE + N_RESERVED_VALUES); + + if (row > 0) { /* dither the values */ + for (i = 0; i < nx; i++) { + if (fdata[i] != in_null_value) { + if (dither_method == SUBTRACTIVE_DITHER_2 && fdata[i] == 0.0) { + idata[i] = ZERO_VALUE; + } else { + idata[i] = NINT((((double) fdata[i] - zeropt) / delta) + fits_rand_value[nextrand] - 0.5); + } + } else { + idata[i] = NULL_VALUE; + } + + /* increment the random number index, regardless */ + nextrand++; + if (nextrand == N_RANDOM) { + iseed++; + if (iseed == N_RANDOM) iseed = 0; + nextrand = (int) (fits_rand_value[iseed] * 500); + } + } + } else { /* do not dither the values */ + for (i = 0; i < nx; i++) { + if (fdata[i] != in_null_value) + idata[i] = NINT((fdata[i] - zeropt) / delta); + else + idata[i] = NULL_VALUE; + } + } + } + + /* calc min and max values */ + temp = (minval - zeropt) / delta; + *iminval = NINT (temp); + temp = (maxval - zeropt) / delta; + *imaxval = NINT (temp); + + *bscale = delta; + *bzero = zeropt; + + return (1); /* yes, data have been quantized */ +} +/*--------------------------------------------------------------------------*/ +int fits_img_stats_short(short *array, /* 2 dimensional array of image pixels */ + long nx, /* number of pixels in each row of the image */ + long ny, /* number of rows in the image */ + /* (if this is a 3D image, then ny should be the */ + /* product of the no. of rows times the no. of planes) */ + int nullcheck, /* check for null values, if true */ + short nullvalue, /* value of null pixels, if nullcheck is true */ + + /* returned parameters (if the pointer is not null) */ + long *ngoodpix, /* number of non-null pixels in the image */ + short *minvalue, /* returned minimum non-null value in the array */ + short *maxvalue, /* returned maximum non-null value in the array */ + double *mean, /* returned mean value of all non-null pixels */ + double *sigma, /* returned R.M.S. value of all non-null pixels */ + double *noise1, /* 1st order estimate of noise in image background level */ + double *noise2, /* 2nd order estimate of noise in image background level */ + double *noise3, /* 3rd order estimate of noise in image background level */ + double *noise5, /* 5th order estimate of noise in image background level */ + int *status) /* error status */ + +/* + Compute statistics of the input short integer image. +*/ +{ + long ngood; + short minval = 0, maxval = 0; + double xmean = 0., xsigma = 0., xnoise = 0., xnoise2 = 0., xnoise3 = 0., xnoise5 = 0.; + + /* need to calculate mean and/or sigma and/or limits? */ + if (mean || sigma ) { + FnMeanSigma_short(array, nx * ny, nullcheck, nullvalue, + &ngood, &xmean, &xsigma, status); + + if (ngoodpix) *ngoodpix = ngood; + if (mean) *mean = xmean; + if (sigma) *sigma = xsigma; + } + + if (noise1) { + FnNoise1_short(array, nx, ny, nullcheck, nullvalue, + &xnoise, status); + + *noise1 = xnoise; + } + + if (minvalue || maxvalue || noise3) { + FnNoise5_short(array, nx, ny, nullcheck, nullvalue, + &ngood, &minval, &maxval, &xnoise2, &xnoise3, &xnoise5, status); + + if (ngoodpix) *ngoodpix = ngood; + if (minvalue) *minvalue= minval; + if (maxvalue) *maxvalue = maxval; + if (noise2) *noise2 = xnoise2; + if (noise3) *noise3 = xnoise3; + if (noise5) *noise5 = xnoise5; + } + return(*status); +} +/*--------------------------------------------------------------------------*/ +int fits_img_stats_int(int *array, /* 2 dimensional array of image pixels */ + long nx, /* number of pixels in each row of the image */ + long ny, /* number of rows in the image */ + /* (if this is a 3D image, then ny should be the */ + /* product of the no. of rows times the no. of planes) */ + int nullcheck, /* check for null values, if true */ + int nullvalue, /* value of null pixels, if nullcheck is true */ + + /* returned parameters (if the pointer is not null) */ + long *ngoodpix, /* number of non-null pixels in the image */ + int *minvalue, /* returned minimum non-null value in the array */ + int *maxvalue, /* returned maximum non-null value in the array */ + double *mean, /* returned mean value of all non-null pixels */ + double *sigma, /* returned R.M.S. value of all non-null pixels */ + double *noise1, /* 1st order estimate of noise in image background level */ + double *noise2, /* 2nd order estimate of noise in image background level */ + double *noise3, /* 3rd order estimate of noise in image background level */ + double *noise5, /* 5th order estimate of noise in image background level */ + int *status) /* error status */ + +/* + Compute statistics of the input integer image. +*/ +{ + long ngood; + int minval = 0, maxval = 0; + double xmean = 0., xsigma = 0., xnoise = 0., xnoise2 = 0., xnoise3 = 0., xnoise5 = 0.; + + /* need to calculate mean and/or sigma and/or limits? */ + if (mean || sigma ) { + FnMeanSigma_int(array, nx * ny, nullcheck, nullvalue, + &ngood, &xmean, &xsigma, status); + + if (ngoodpix) *ngoodpix = ngood; + if (mean) *mean = xmean; + if (sigma) *sigma = xsigma; + } + + if (noise1) { + FnNoise1_int(array, nx, ny, nullcheck, nullvalue, + &xnoise, status); + + *noise1 = xnoise; + } + + if (minvalue || maxvalue || noise3) { + FnNoise5_int(array, nx, ny, nullcheck, nullvalue, + &ngood, &minval, &maxval, &xnoise2, &xnoise3, &xnoise5, status); + + if (ngoodpix) *ngoodpix = ngood; + if (minvalue) *minvalue= minval; + if (maxvalue) *maxvalue = maxval; + if (noise2) *noise2 = xnoise2; + if (noise3) *noise3 = xnoise3; + if (noise5) *noise5 = xnoise5; + } + return(*status); +} +/*--------------------------------------------------------------------------*/ +int fits_img_stats_float(float *array, /* 2 dimensional array of image pixels */ + long nx, /* number of pixels in each row of the image */ + long ny, /* number of rows in the image */ + /* (if this is a 3D image, then ny should be the */ + /* product of the no. of rows times the no. of planes) */ + int nullcheck, /* check for null values, if true */ + float nullvalue, /* value of null pixels, if nullcheck is true */ + + /* returned parameters (if the pointer is not null) */ + long *ngoodpix, /* number of non-null pixels in the image */ + float *minvalue, /* returned minimum non-null value in the array */ + float *maxvalue, /* returned maximum non-null value in the array */ + double *mean, /* returned mean value of all non-null pixels */ + double *sigma, /* returned R.M.S. value of all non-null pixels */ + double *noise1, /* 1st order estimate of noise in image background level */ + double *noise2, /* 2nd order estimate of noise in image background level */ + double *noise3, /* 3rd order estimate of noise in image background level */ + double *noise5, /* 5th order estimate of noise in image background level */ + int *status) /* error status */ + +/* + Compute statistics of the input float image. +*/ +{ + long ngood; + float minval, maxval; + double xmean = 0., xsigma = 0., xnoise = 0., xnoise2 = 0., xnoise3 = 0., xnoise5 = 0.; + + /* need to calculate mean and/or sigma and/or limits? */ + if (mean || sigma ) { + FnMeanSigma_float(array, nx * ny, nullcheck, nullvalue, + &ngood, &xmean, &xsigma, status); + + if (ngoodpix) *ngoodpix = ngood; + if (mean) *mean = xmean; + if (sigma) *sigma = xsigma; + } + + if (noise1) { + FnNoise1_float(array, nx, ny, nullcheck, nullvalue, + &xnoise, status); + + *noise1 = xnoise; + } + + if (minvalue || maxvalue || noise3) { + FnNoise5_float(array, nx, ny, nullcheck, nullvalue, + &ngood, &minval, &maxval, &xnoise2, &xnoise3, &xnoise5, status); + + if (ngoodpix) *ngoodpix = ngood; + if (minvalue) *minvalue= minval; + if (maxvalue) *maxvalue = maxval; + if (noise2) *noise2 = xnoise2; + if (noise3) *noise3 = xnoise3; + if (noise5) *noise5 = xnoise5; + } + return(*status); +} +/*--------------------------------------------------------------------------*/ +static int FnMeanSigma_short + (short *array, /* 2 dimensional array of image pixels */ + long npix, /* number of pixels in the image */ + int nullcheck, /* check for null values, if true */ + short nullvalue, /* value of null pixels, if nullcheck is true */ + + /* returned parameters */ + + long *ngoodpix, /* number of non-null pixels in the image */ + double *mean, /* returned mean value of all non-null pixels */ + double *sigma, /* returned R.M.S. value of all non-null pixels */ + int *status) /* error status */ + +/* +Compute mean and RMS sigma of the non-null pixels in the input array. +*/ +{ + long ii, ngood = 0; + short *value; + double sum = 0., sum2 = 0., xtemp; + + value = array; + + if (nullcheck) { + for (ii = 0; ii < npix; ii++, value++) { + if (*value != nullvalue) { + ngood++; + xtemp = (double) *value; + sum += xtemp; + sum2 += (xtemp * xtemp); + } + } + } else { + ngood = npix; + for (ii = 0; ii < npix; ii++, value++) { + xtemp = (double) *value; + sum += xtemp; + sum2 += (xtemp * xtemp); + } + } + + if (ngood > 1) { + if (ngoodpix) *ngoodpix = ngood; + xtemp = sum / ngood; + if (mean) *mean = xtemp; + if (sigma) *sigma = sqrt((sum2 / ngood) - (xtemp * xtemp)); + } else if (ngood == 1){ + if (ngoodpix) *ngoodpix = 1; + if (mean) *mean = sum; + if (sigma) *sigma = 0.0; + } else { + if (ngoodpix) *ngoodpix = 0; + if (mean) *mean = 0.; + if (sigma) *sigma = 0.; + } + return(*status); +} +/*--------------------------------------------------------------------------*/ +static int FnMeanSigma_int + (int *array, /* 2 dimensional array of image pixels */ + long npix, /* number of pixels in the image */ + int nullcheck, /* check for null values, if true */ + int nullvalue, /* value of null pixels, if nullcheck is true */ + + /* returned parameters */ + + long *ngoodpix, /* number of non-null pixels in the image */ + double *mean, /* returned mean value of all non-null pixels */ + double *sigma, /* returned R.M.S. value of all non-null pixels */ + int *status) /* error status */ + +/* +Compute mean and RMS sigma of the non-null pixels in the input array. +*/ +{ + long ii, ngood = 0; + int *value; + double sum = 0., sum2 = 0., xtemp; + + value = array; + + if (nullcheck) { + for (ii = 0; ii < npix; ii++, value++) { + if (*value != nullvalue) { + ngood++; + xtemp = (double) *value; + sum += xtemp; + sum2 += (xtemp * xtemp); + } + } + } else { + ngood = npix; + for (ii = 0; ii < npix; ii++, value++) { + xtemp = (double) *value; + sum += xtemp; + sum2 += (xtemp * xtemp); + } + } + + if (ngood > 1) { + if (ngoodpix) *ngoodpix = ngood; + xtemp = sum / ngood; + if (mean) *mean = xtemp; + if (sigma) *sigma = sqrt((sum2 / ngood) - (xtemp * xtemp)); + } else if (ngood == 1){ + if (ngoodpix) *ngoodpix = 1; + if (mean) *mean = sum; + if (sigma) *sigma = 0.0; + } else { + if (ngoodpix) *ngoodpix = 0; + if (mean) *mean = 0.; + if (sigma) *sigma = 0.; + } + return(*status); +} +/*--------------------------------------------------------------------------*/ +static int FnMeanSigma_float + (float *array, /* 2 dimensional array of image pixels */ + long npix, /* number of pixels in the image */ + int nullcheck, /* check for null values, if true */ + float nullvalue, /* value of null pixels, if nullcheck is true */ + + /* returned parameters */ + + long *ngoodpix, /* number of non-null pixels in the image */ + double *mean, /* returned mean value of all non-null pixels */ + double *sigma, /* returned R.M.S. value of all non-null pixels */ + int *status) /* error status */ + +/* +Compute mean and RMS sigma of the non-null pixels in the input array. +*/ +{ + long ii, ngood = 0; + float *value; + double sum = 0., sum2 = 0., xtemp; + + value = array; + + if (nullcheck) { + for (ii = 0; ii < npix; ii++, value++) { + if (*value != nullvalue) { + ngood++; + xtemp = (double) *value; + sum += xtemp; + sum2 += (xtemp * xtemp); + } + } + } else { + ngood = npix; + for (ii = 0; ii < npix; ii++, value++) { + xtemp = (double) *value; + sum += xtemp; + sum2 += (xtemp * xtemp); + } + } + + if (ngood > 1) { + if (ngoodpix) *ngoodpix = ngood; + xtemp = sum / ngood; + if (mean) *mean = xtemp; + if (sigma) *sigma = sqrt((sum2 / ngood) - (xtemp * xtemp)); + } else if (ngood == 1){ + if (ngoodpix) *ngoodpix = 1; + if (mean) *mean = sum; + if (sigma) *sigma = 0.0; + } else { + if (ngoodpix) *ngoodpix = 0; + if (mean) *mean = 0.; + if (sigma) *sigma = 0.; + } + return(*status); +} +/*--------------------------------------------------------------------------*/ +static int FnMeanSigma_double + (double *array, /* 2 dimensional array of image pixels */ + long npix, /* number of pixels in the image */ + int nullcheck, /* check for null values, if true */ + double nullvalue, /* value of null pixels, if nullcheck is true */ + + /* returned parameters */ + + long *ngoodpix, /* number of non-null pixels in the image */ + double *mean, /* returned mean value of all non-null pixels */ + double *sigma, /* returned R.M.S. value of all non-null pixels */ + int *status) /* error status */ + +/* +Compute mean and RMS sigma of the non-null pixels in the input array. +*/ +{ + long ii, ngood = 0; + double *value; + double sum = 0., sum2 = 0., xtemp; + + value = array; + + if (nullcheck) { + for (ii = 0; ii < npix; ii++, value++) { + if (*value != nullvalue) { + ngood++; + xtemp = *value; + sum += xtemp; + sum2 += (xtemp * xtemp); + } + } + } else { + ngood = npix; + for (ii = 0; ii < npix; ii++, value++) { + xtemp = *value; + sum += xtemp; + sum2 += (xtemp * xtemp); + } + } + + if (ngood > 1) { + if (ngoodpix) *ngoodpix = ngood; + xtemp = sum / ngood; + if (mean) *mean = xtemp; + if (sigma) *sigma = sqrt((sum2 / ngood) - (xtemp * xtemp)); + } else if (ngood == 1){ + if (ngoodpix) *ngoodpix = 1; + if (mean) *mean = sum; + if (sigma) *sigma = 0.0; + } else { + if (ngoodpix) *ngoodpix = 0; + if (mean) *mean = 0.; + if (sigma) *sigma = 0.; + } + return(*status); +} +/*--------------------------------------------------------------------------*/ +static int FnNoise5_short + (short *array, /* 2 dimensional array of image pixels */ + long nx, /* number of pixels in each row of the image */ + long ny, /* number of rows in the image */ + int nullcheck, /* check for null values, if true */ + short nullvalue, /* value of null pixels, if nullcheck is true */ + /* returned parameters */ + long *ngood, /* number of good, non-null pixels? */ + short *minval, /* minimum non-null value */ + short *maxval, /* maximum non-null value */ + double *noise2, /* returned 2nd order MAD of all non-null pixels */ + double *noise3, /* returned 3rd order MAD of all non-null pixels */ + double *noise5, /* returned 5th order MAD of all non-null pixels */ + int *status) /* error status */ + +/* +Estimate the median and background noise in the input image using 2nd, 3rd and 5th +order Median Absolute Differences. + +The noise in the background of the image is calculated using the MAD algorithms +developed for deriving the signal to noise ratio in spectra +(see issue #42 of the ST-ECF newsletter, http://www.stecf.org/documents/newsletter/) + +3rd order: noise = 1.482602 / sqrt(6) * median (abs(2*flux(i) - flux(i-2) - flux(i+2))) + +The returned estimates are the median of the values that are computed for each +row of the image. +*/ +{ + long ii, jj, nrows = 0, nrows2 = 0, nvals, nvals2, ngoodpix = 0; + int *differences2, *differences3, *differences5; + short *rowpix, v1, v2, v3, v4, v5, v6, v7, v8, v9; + short xminval = SHRT_MAX, xmaxval = SHRT_MIN; + int do_range = 0; + double *diffs2, *diffs3, *diffs5; + double xnoise2 = 0, xnoise3 = 0, xnoise5 = 0; + + if (nx < 9) { + /* treat entire array as an image with a single row */ + nx = nx * ny; + ny = 1; + } + + /* rows must have at least 9 pixels */ + if (nx < 9) { + + for (ii = 0; ii < nx; ii++) { + if (nullcheck && array[ii] == nullvalue) + continue; + else { + if (array[ii] < xminval) xminval = array[ii]; + if (array[ii] > xmaxval) xmaxval = array[ii]; + ngoodpix++; + } + } + if (minval) *minval = xminval; + if (maxval) *maxval = xmaxval; + if (ngood) *ngood = ngoodpix; + if (noise2) *noise2 = 0.; + if (noise3) *noise3 = 0.; + if (noise5) *noise5 = 0.; + return(*status); + } + + /* do we need to compute the min and max value? */ + if (minval || maxval) do_range = 1; + + /* allocate arrays used to compute the median and noise estimates */ + differences2 = calloc(nx, sizeof(int)); + if (!differences2) { + *status = MEMORY_ALLOCATION; + return(*status); + } + differences3 = calloc(nx, sizeof(int)); + if (!differences3) { + free(differences2); + *status = MEMORY_ALLOCATION; + return(*status); + } + differences5 = calloc(nx, sizeof(int)); + if (!differences5) { + free(differences2); + free(differences3); + *status = MEMORY_ALLOCATION; + return(*status); + } + + diffs2 = calloc(ny, sizeof(double)); + if (!diffs2) { + free(differences2); + free(differences3); + free(differences5); + *status = MEMORY_ALLOCATION; + return(*status); + } + + diffs3 = calloc(ny, sizeof(double)); + if (!diffs3) { + free(differences2); + free(differences3); + free(differences5); + free(diffs2); + *status = MEMORY_ALLOCATION; + return(*status); + } + + diffs5 = calloc(ny, sizeof(double)); + if (!diffs5) { + free(differences2); + free(differences3); + free(differences5); + free(diffs2); + free(diffs3); + *status = MEMORY_ALLOCATION; + return(*status); + } + + /* loop over each row of the image */ + for (jj=0; jj < ny; jj++) { + + rowpix = array + (jj * nx); /* point to first pixel in the row */ + + /***** find the first valid pixel in row */ + ii = 0; + if (nullcheck) + while (ii < nx && rowpix[ii] == nullvalue) ii++; + + if (ii == nx) continue; /* hit end of row */ + v1 = rowpix[ii]; /* store the good pixel value */ + ngoodpix++; + + if (do_range) { + if (v1 < xminval) xminval = v1; + if (v1 > xmaxval) xmaxval = v1; + } + + /***** find the 2nd valid pixel in row (which we will skip over) */ + ii++; + if (nullcheck) + while (ii < nx && rowpix[ii] == nullvalue) ii++; + + if (ii == nx) continue; /* hit end of row */ + v2 = rowpix[ii]; /* store the good pixel value */ + ngoodpix++; + + if (do_range) { + if (v2 < xminval) xminval = v2; + if (v2 > xmaxval) xmaxval = v2; + } + + /***** find the 3rd valid pixel in row */ + ii++; + if (nullcheck) + while (ii < nx && rowpix[ii] == nullvalue) ii++; + + if (ii == nx) continue; /* hit end of row */ + v3 = rowpix[ii]; /* store the good pixel value */ + ngoodpix++; + + if (do_range) { + if (v3 < xminval) xminval = v3; + if (v3 > xmaxval) xmaxval = v3; + } + + /* find the 4nd valid pixel in row (to be skipped) */ + ii++; + if (nullcheck) + while (ii < nx && rowpix[ii] == nullvalue) ii++; + + if (ii == nx) continue; /* hit end of row */ + v4 = rowpix[ii]; /* store the good pixel value */ + ngoodpix++; + + if (do_range) { + if (v4 < xminval) xminval = v4; + if (v4 > xmaxval) xmaxval = v4; + } + + /* find the 5th valid pixel in row (to be skipped) */ + ii++; + if (nullcheck) + while (ii < nx && rowpix[ii] == nullvalue) ii++; + + if (ii == nx) continue; /* hit end of row */ + v5 = rowpix[ii]; /* store the good pixel value */ + ngoodpix++; + + if (do_range) { + if (v5 < xminval) xminval = v5; + if (v5 > xmaxval) xmaxval = v5; + } + + /* find the 6th valid pixel in row (to be skipped) */ + ii++; + if (nullcheck) + while (ii < nx && rowpix[ii] == nullvalue) ii++; + + if (ii == nx) continue; /* hit end of row */ + v6 = rowpix[ii]; /* store the good pixel value */ + ngoodpix++; + + if (do_range) { + if (v6 < xminval) xminval = v6; + if (v6 > xmaxval) xmaxval = v6; + } + + /* find the 7th valid pixel in row (to be skipped) */ + ii++; + if (nullcheck) + while (ii < nx && rowpix[ii] == nullvalue) ii++; + + if (ii == nx) continue; /* hit end of row */ + v7 = rowpix[ii]; /* store the good pixel value */ + ngoodpix++; + + if (do_range) { + if (v7 < xminval) xminval = v7; + if (v7 > xmaxval) xmaxval = v7; + } + + /* find the 8th valid pixel in row (to be skipped) */ + ii++; + if (nullcheck) + while (ii < nx && rowpix[ii] == nullvalue) ii++; + + if (ii == nx) continue; /* hit end of row */ + v8 = rowpix[ii]; /* store the good pixel value */ + ngoodpix++; + + if (do_range) { + if (v8 < xminval) xminval = v8; + if (v8 > xmaxval) xmaxval = v8; + } + /* now populate the differences arrays */ + /* for the remaining pixels in the row */ + nvals = 0; + nvals2 = 0; + for (ii++; ii < nx; ii++) { + + /* find the next valid pixel in row */ + if (nullcheck) + while (ii < nx && rowpix[ii] == nullvalue) ii++; + + if (ii == nx) break; /* hit end of row */ + v9 = rowpix[ii]; /* store the good pixel value */ + + if (do_range) { + if (v9 < xminval) xminval = v9; + if (v9 > xmaxval) xmaxval = v9; + } + + /* construct array of absolute differences */ + + if (!(v5 == v6 && v6 == v7) ) { + differences2[nvals2] = abs((int) v5 - (int) v7); + nvals2++; + } + + if (!(v3 == v4 && v4 == v5 && v5 == v6 && v6 == v7) ) { + differences3[nvals] = abs((2 * (int) v5) - (int) v3 - (int) v7); + differences5[nvals] = abs((6 * (int) v5) - (4 * (int) v3) - (4 * (int) v7) + (int) v1 + (int) v9); + nvals++; + } else { + /* ignore constant background regions */ + ngoodpix++; + } + + /* shift over 1 pixel */ + v1 = v2; + v2 = v3; + v3 = v4; + v4 = v5; + v5 = v6; + v6 = v7; + v7 = v8; + v8 = v9; + } /* end of loop over pixels in the row */ + + /* compute the median diffs */ + /* Note that there are 8 more pixel values than there are diffs values. */ + ngoodpix += nvals; + + if (nvals == 0) { + continue; /* cannot compute medians on this row */ + } else if (nvals == 1) { + if (nvals2 == 1) { + diffs2[nrows2] = differences2[0]; + nrows2++; + } + + diffs3[nrows] = differences3[0]; + diffs5[nrows] = differences5[0]; + } else { + /* quick_select returns the median MUCH faster than using qsort */ + if (nvals2 > 1) { + diffs2[nrows2] = quick_select_int(differences2, nvals); + nrows2++; + } + + diffs3[nrows] = quick_select_int(differences3, nvals); + diffs5[nrows] = quick_select_int(differences5, nvals); + } + + nrows++; + } /* end of loop over rows */ + + /* compute median of the values for each row */ + if (nrows == 0) { + xnoise3 = 0; + xnoise5 = 0; + } else if (nrows == 1) { + xnoise3 = diffs3[0]; + xnoise5 = diffs5[0]; + } else { + qsort(diffs3, nrows, sizeof(double), FnCompare_double); + qsort(diffs5, nrows, sizeof(double), FnCompare_double); + xnoise3 = (diffs3[(nrows - 1)/2] + diffs3[nrows/2]) / 2.; + xnoise5 = (diffs5[(nrows - 1)/2] + diffs5[nrows/2]) / 2.; + } + + if (nrows2 == 0) { + xnoise2 = 0; + } else if (nrows2 == 1) { + xnoise2 = diffs2[0]; + } else { + qsort(diffs2, nrows2, sizeof(double), FnCompare_double); + xnoise2 = (diffs2[(nrows2 - 1)/2] + diffs2[nrows2/2]) / 2.; + } + + if (ngood) *ngood = ngoodpix; + if (minval) *minval = xminval; + if (maxval) *maxval = xmaxval; + if (noise2) *noise2 = 1.0483579 * xnoise2; + if (noise3) *noise3 = 0.6052697 * xnoise3; + if (noise5) *noise5 = 0.1772048 * xnoise5; + + free(diffs5); + free(diffs3); + free(diffs2); + free(differences5); + free(differences3); + free(differences2); + + return(*status); +} +/*--------------------------------------------------------------------------*/ +static int FnNoise5_int + (int *array, /* 2 dimensional array of image pixels */ + long nx, /* number of pixels in each row of the image */ + long ny, /* number of rows in the image */ + int nullcheck, /* check for null values, if true */ + int nullvalue, /* value of null pixels, if nullcheck is true */ + /* returned parameters */ + long *ngood, /* number of good, non-null pixels? */ + int *minval, /* minimum non-null value */ + int *maxval, /* maximum non-null value */ + double *noise2, /* returned 2nd order MAD of all non-null pixels */ + double *noise3, /* returned 3rd order MAD of all non-null pixels */ + double *noise5, /* returned 5th order MAD of all non-null pixels */ + int *status) /* error status */ + +/* +Estimate the median and background noise in the input image using 2nd, 3rd and 5th +order Median Absolute Differences. + +The noise in the background of the image is calculated using the MAD algorithms +developed for deriving the signal to noise ratio in spectra +(see issue #42 of the ST-ECF newsletter, http://www.stecf.org/documents/newsletter/) + +3rd order: noise = 1.482602 / sqrt(6) * median (abs(2*flux(i) - flux(i-2) - flux(i+2))) + +The returned estimates are the median of the values that are computed for each +row of the image. +*/ +{ + long ii, jj, nrows = 0, nrows2 = 0, nvals, nvals2, ngoodpix = 0; + LONGLONG *differences2, *differences3, *differences5, tdiff; + int *rowpix, v1, v2, v3, v4, v5, v6, v7, v8, v9; + int xminval = INT_MAX, xmaxval = INT_MIN; + int do_range = 0; + double *diffs2, *diffs3, *diffs5; + double xnoise2 = 0, xnoise3 = 0, xnoise5 = 0; + + if (nx < 9) { + /* treat entire array as an image with a single row */ + nx = nx * ny; + ny = 1; + } + + /* rows must have at least 9 pixels */ + if (nx < 9) { + + for (ii = 0; ii < nx; ii++) { + if (nullcheck && array[ii] == nullvalue) + continue; + else { + if (array[ii] < xminval) xminval = array[ii]; + if (array[ii] > xmaxval) xmaxval = array[ii]; + ngoodpix++; + } + } + if (minval) *minval = xminval; + if (maxval) *maxval = xmaxval; + if (ngood) *ngood = ngoodpix; + if (noise2) *noise2 = 0.; + if (noise3) *noise3 = 0.; + if (noise5) *noise5 = 0.; + return(*status); + } + + /* do we need to compute the min and max value? */ + if (minval || maxval) do_range = 1; + + /* allocate arrays used to compute the median and noise estimates */ + differences2 = calloc(nx, sizeof(LONGLONG)); + if (!differences2) { + *status = MEMORY_ALLOCATION; + return(*status); + } + differences3 = calloc(nx, sizeof(LONGLONG)); + if (!differences3) { + free(differences2); + *status = MEMORY_ALLOCATION; + return(*status); + } + differences5 = calloc(nx, sizeof(LONGLONG)); + if (!differences5) { + free(differences2); + free(differences3); + *status = MEMORY_ALLOCATION; + return(*status); + } + + diffs2 = calloc(ny, sizeof(double)); + if (!diffs2) { + free(differences2); + free(differences3); + free(differences5); + *status = MEMORY_ALLOCATION; + return(*status); + } + + diffs3 = calloc(ny, sizeof(double)); + if (!diffs3) { + free(differences2); + free(differences3); + free(differences5); + free(diffs2); + *status = MEMORY_ALLOCATION; + return(*status); + } + + diffs5 = calloc(ny, sizeof(double)); + if (!diffs5) { + free(differences2); + free(differences3); + free(differences5); + free(diffs2); + free(diffs3); + *status = MEMORY_ALLOCATION; + return(*status); + } + + /* loop over each row of the image */ + for (jj=0; jj < ny; jj++) { + + rowpix = array + (jj * nx); /* point to first pixel in the row */ + + /***** find the first valid pixel in row */ + ii = 0; + if (nullcheck) + while (ii < nx && rowpix[ii] == nullvalue) ii++; + + if (ii == nx) continue; /* hit end of row */ + v1 = rowpix[ii]; /* store the good pixel value */ + ngoodpix++; + + if (do_range) { + if (v1 < xminval) xminval = v1; + if (v1 > xmaxval) xmaxval = v1; + } + + /***** find the 2nd valid pixel in row (which we will skip over) */ + ii++; + if (nullcheck) + while (ii < nx && rowpix[ii] == nullvalue) ii++; + + if (ii == nx) continue; /* hit end of row */ + v2 = rowpix[ii]; /* store the good pixel value */ + ngoodpix++; + + if (do_range) { + if (v2 < xminval) xminval = v2; + if (v2 > xmaxval) xmaxval = v2; + } + + /***** find the 3rd valid pixel in row */ + ii++; + if (nullcheck) + while (ii < nx && rowpix[ii] == nullvalue) ii++; + + if (ii == nx) continue; /* hit end of row */ + v3 = rowpix[ii]; /* store the good pixel value */ + ngoodpix++; + + if (do_range) { + if (v3 < xminval) xminval = v3; + if (v3 > xmaxval) xmaxval = v3; + } + + /* find the 4nd valid pixel in row (to be skipped) */ + ii++; + if (nullcheck) + while (ii < nx && rowpix[ii] == nullvalue) ii++; + + if (ii == nx) continue; /* hit end of row */ + v4 = rowpix[ii]; /* store the good pixel value */ + ngoodpix++; + + if (do_range) { + if (v4 < xminval) xminval = v4; + if (v4 > xmaxval) xmaxval = v4; + } + + /* find the 5th valid pixel in row (to be skipped) */ + ii++; + if (nullcheck) + while (ii < nx && rowpix[ii] == nullvalue) ii++; + + if (ii == nx) continue; /* hit end of row */ + v5 = rowpix[ii]; /* store the good pixel value */ + ngoodpix++; + + if (do_range) { + if (v5 < xminval) xminval = v5; + if (v5 > xmaxval) xmaxval = v5; + } + + /* find the 6th valid pixel in row (to be skipped) */ + ii++; + if (nullcheck) + while (ii < nx && rowpix[ii] == nullvalue) ii++; + + if (ii == nx) continue; /* hit end of row */ + v6 = rowpix[ii]; /* store the good pixel value */ + ngoodpix++; + + if (do_range) { + if (v6 < xminval) xminval = v6; + if (v6 > xmaxval) xmaxval = v6; + } + + /* find the 7th valid pixel in row (to be skipped) */ + ii++; + if (nullcheck) + while (ii < nx && rowpix[ii] == nullvalue) ii++; + + if (ii == nx) continue; /* hit end of row */ + v7 = rowpix[ii]; /* store the good pixel value */ + ngoodpix++; + + if (do_range) { + if (v7 < xminval) xminval = v7; + if (v7 > xmaxval) xmaxval = v7; + } + + /* find the 8th valid pixel in row (to be skipped) */ + ii++; + if (nullcheck) + while (ii < nx && rowpix[ii] == nullvalue) ii++; + + if (ii == nx) continue; /* hit end of row */ + v8 = rowpix[ii]; /* store the good pixel value */ + ngoodpix++; + + if (do_range) { + if (v8 < xminval) xminval = v8; + if (v8 > xmaxval) xmaxval = v8; + } + /* now populate the differences arrays */ + /* for the remaining pixels in the row */ + nvals = 0; + nvals2 = 0; + for (ii++; ii < nx; ii++) { + + /* find the next valid pixel in row */ + if (nullcheck) + while (ii < nx && rowpix[ii] == nullvalue) ii++; + + if (ii == nx) break; /* hit end of row */ + v9 = rowpix[ii]; /* store the good pixel value */ + + if (do_range) { + if (v9 < xminval) xminval = v9; + if (v9 > xmaxval) xmaxval = v9; + } + + /* construct array of absolute differences */ + + if (!(v5 == v6 && v6 == v7) ) { + tdiff = (LONGLONG) v5 - (LONGLONG) v7; + if (tdiff < 0) + differences2[nvals2] = -1 * tdiff; + else + differences2[nvals2] = tdiff; + + nvals2++; + } + + if (!(v3 == v4 && v4 == v5 && v5 == v6 && v6 == v7) ) { + tdiff = (2 * (LONGLONG) v5) - (LONGLONG) v3 - (LONGLONG) v7; + if (tdiff < 0) + differences3[nvals] = -1 * tdiff; + else + differences3[nvals] = tdiff; + + tdiff = (6 * (LONGLONG) v5) - (4 * (LONGLONG) v3) - (4 * (LONGLONG) v7) + (LONGLONG) v1 + (LONGLONG) v9; + if (tdiff < 0) + differences5[nvals] = -1 * tdiff; + else + differences5[nvals] = tdiff; + + nvals++; + } else { + /* ignore constant background regions */ + ngoodpix++; + } + + /* shift over 1 pixel */ + v1 = v2; + v2 = v3; + v3 = v4; + v4 = v5; + v5 = v6; + v6 = v7; + v7 = v8; + v8 = v9; + } /* end of loop over pixels in the row */ + + /* compute the median diffs */ + /* Note that there are 8 more pixel values than there are diffs values. */ + ngoodpix += nvals; + + if (nvals == 0) { + continue; /* cannot compute medians on this row */ + } else if (nvals == 1) { + if (nvals2 == 1) { + diffs2[nrows2] = (double) differences2[0]; + nrows2++; + } + + diffs3[nrows] = (double) differences3[0]; + diffs5[nrows] = (double) differences5[0]; + } else { + /* quick_select returns the median MUCH faster than using qsort */ + if (nvals2 > 1) { + diffs2[nrows2] = (double) quick_select_longlong(differences2, nvals); + nrows2++; + } + + diffs3[nrows] = (double) quick_select_longlong(differences3, nvals); + diffs5[nrows] = (double) quick_select_longlong(differences5, nvals); + } + + nrows++; + } /* end of loop over rows */ + + /* compute median of the values for each row */ + if (nrows == 0) { + xnoise3 = 0; + xnoise5 = 0; + } else if (nrows == 1) { + xnoise3 = diffs3[0]; + xnoise5 = diffs5[0]; + } else { + qsort(diffs3, nrows, sizeof(double), FnCompare_double); + qsort(diffs5, nrows, sizeof(double), FnCompare_double); + xnoise3 = (diffs3[(nrows - 1)/2] + diffs3[nrows/2]) / 2.; + xnoise5 = (diffs5[(nrows - 1)/2] + diffs5[nrows/2]) / 2.; + } + + if (nrows2 == 0) { + xnoise2 = 0; + } else if (nrows2 == 1) { + xnoise2 = diffs2[0]; + } else { + qsort(diffs2, nrows2, sizeof(double), FnCompare_double); + xnoise2 = (diffs2[(nrows2 - 1)/2] + diffs2[nrows2/2]) / 2.; + } + + if (ngood) *ngood = ngoodpix; + if (minval) *minval = xminval; + if (maxval) *maxval = xmaxval; + if (noise2) *noise2 = 1.0483579 * xnoise2; + if (noise3) *noise3 = 0.6052697 * xnoise3; + if (noise5) *noise5 = 0.1772048 * xnoise5; + + free(diffs5); + free(diffs3); + free(diffs2); + free(differences5); + free(differences3); + free(differences2); + + return(*status); +} +/*--------------------------------------------------------------------------*/ +static int FnNoise5_float + (float *array, /* 2 dimensional array of image pixels */ + long nx, /* number of pixels in each row of the image */ + long ny, /* number of rows in the image */ + int nullcheck, /* check for null values, if true */ + float nullvalue, /* value of null pixels, if nullcheck is true */ + /* returned parameters */ + long *ngood, /* number of good, non-null pixels? */ + float *minval, /* minimum non-null value */ + float *maxval, /* maximum non-null value */ + double *noise2, /* returned 2nd order MAD of all non-null pixels */ + double *noise3, /* returned 3rd order MAD of all non-null pixels */ + double *noise5, /* returned 5th order MAD of all non-null pixels */ + int *status) /* error status */ + +/* +Estimate the median and background noise in the input image using 2nd, 3rd and 5th +order Median Absolute Differences. + +The noise in the background of the image is calculated using the MAD algorithms +developed for deriving the signal to noise ratio in spectra +(see issue #42 of the ST-ECF newsletter, http://www.stecf.org/documents/newsletter/) + +3rd order: noise = 1.482602 / sqrt(6) * median (abs(2*flux(i) - flux(i-2) - flux(i+2))) + +The returned estimates are the median of the values that are computed for each +row of the image. +*/ +{ + long ii, jj, nrows = 0, nrows2 = 0, nvals, nvals2, ngoodpix = 0; + float *differences2, *differences3, *differences5; + float *rowpix, v1, v2, v3, v4, v5, v6, v7, v8, v9; + float xminval = FLT_MAX, xmaxval = -FLT_MAX; + int do_range = 0; + double *diffs2, *diffs3, *diffs5; + double xnoise2 = 0, xnoise3 = 0, xnoise5 = 0; + + if (nx < 9) { + /* treat entire array as an image with a single row */ + nx = nx * ny; + ny = 1; + } + + /* rows must have at least 9 pixels */ + if (nx < 9) { + + for (ii = 0; ii < nx; ii++) { + if (nullcheck && array[ii] == nullvalue) + continue; + else { + if (array[ii] < xminval) xminval = array[ii]; + if (array[ii] > xmaxval) xmaxval = array[ii]; + ngoodpix++; + } + } + if (minval) *minval = xminval; + if (maxval) *maxval = xmaxval; + if (ngood) *ngood = ngoodpix; + if (noise2) *noise2 = 0.; + if (noise3) *noise3 = 0.; + if (noise5) *noise5 = 0.; + return(*status); + } + + /* do we need to compute the min and max value? */ + if (minval || maxval) do_range = 1; + + /* allocate arrays used to compute the median and noise estimates */ + differences2 = calloc(nx, sizeof(float)); + if (!differences2) { + *status = MEMORY_ALLOCATION; + return(*status); + } + differences3 = calloc(nx, sizeof(float)); + if (!differences3) { + free(differences2); + *status = MEMORY_ALLOCATION; + return(*status); + } + differences5 = calloc(nx, sizeof(float)); + if (!differences5) { + free(differences2); + free(differences3); + *status = MEMORY_ALLOCATION; + return(*status); + } + + diffs2 = calloc(ny, sizeof(double)); + if (!diffs2) { + free(differences2); + free(differences3); + free(differences5); + *status = MEMORY_ALLOCATION; + return(*status); + } + + diffs3 = calloc(ny, sizeof(double)); + if (!diffs3) { + free(differences2); + free(differences3); + free(differences5); + free(diffs2); + *status = MEMORY_ALLOCATION; + return(*status); + } + + diffs5 = calloc(ny, sizeof(double)); + if (!diffs5) { + free(differences2); + free(differences3); + free(differences5); + free(diffs2); + free(diffs3); + *status = MEMORY_ALLOCATION; + return(*status); + } + + /* loop over each row of the image */ + for (jj=0; jj < ny; jj++) { + + rowpix = array + (jj * nx); /* point to first pixel in the row */ + + /***** find the first valid pixel in row */ + ii = 0; + if (nullcheck) + while (ii < nx && rowpix[ii] == nullvalue) ii++; + + if (ii == nx) continue; /* hit end of row */ + v1 = rowpix[ii]; /* store the good pixel value */ + ngoodpix++; + + if (do_range) { + if (v1 < xminval) xminval = v1; + if (v1 > xmaxval) xmaxval = v1; + } + + /***** find the 2nd valid pixel in row (which we will skip over) */ + ii++; + if (nullcheck) + while (ii < nx && rowpix[ii] == nullvalue) ii++; + + if (ii == nx) continue; /* hit end of row */ + v2 = rowpix[ii]; /* store the good pixel value */ + ngoodpix++; + + if (do_range) { + if (v2 < xminval) xminval = v2; + if (v2 > xmaxval) xmaxval = v2; + } + + /***** find the 3rd valid pixel in row */ + ii++; + if (nullcheck) + while (ii < nx && rowpix[ii] == nullvalue) ii++; + + if (ii == nx) continue; /* hit end of row */ + v3 = rowpix[ii]; /* store the good pixel value */ + ngoodpix++; + + if (do_range) { + if (v3 < xminval) xminval = v3; + if (v3 > xmaxval) xmaxval = v3; + } + + /* find the 4nd valid pixel in row (to be skipped) */ + ii++; + if (nullcheck) + while (ii < nx && rowpix[ii] == nullvalue) ii++; + + if (ii == nx) continue; /* hit end of row */ + v4 = rowpix[ii]; /* store the good pixel value */ + ngoodpix++; + + if (do_range) { + if (v4 < xminval) xminval = v4; + if (v4 > xmaxval) xmaxval = v4; + } + + /* find the 5th valid pixel in row (to be skipped) */ + ii++; + if (nullcheck) + while (ii < nx && rowpix[ii] == nullvalue) ii++; + + if (ii == nx) continue; /* hit end of row */ + v5 = rowpix[ii]; /* store the good pixel value */ + ngoodpix++; + + if (do_range) { + if (v5 < xminval) xminval = v5; + if (v5 > xmaxval) xmaxval = v5; + } + + /* find the 6th valid pixel in row (to be skipped) */ + ii++; + if (nullcheck) + while (ii < nx && rowpix[ii] == nullvalue) ii++; + + if (ii == nx) continue; /* hit end of row */ + v6 = rowpix[ii]; /* store the good pixel value */ + ngoodpix++; + + if (do_range) { + if (v6 < xminval) xminval = v6; + if (v6 > xmaxval) xmaxval = v6; + } + + /* find the 7th valid pixel in row (to be skipped) */ + ii++; + if (nullcheck) + while (ii < nx && rowpix[ii] == nullvalue) ii++; + + if (ii == nx) continue; /* hit end of row */ + v7 = rowpix[ii]; /* store the good pixel value */ + ngoodpix++; + + if (do_range) { + if (v7 < xminval) xminval = v7; + if (v7 > xmaxval) xmaxval = v7; + } + + /* find the 8th valid pixel in row (to be skipped) */ + ii++; + if (nullcheck) + while (ii < nx && rowpix[ii] == nullvalue) ii++; + + if (ii == nx) continue; /* hit end of row */ + v8 = rowpix[ii]; /* store the good pixel value */ + ngoodpix++; + + if (do_range) { + if (v8 < xminval) xminval = v8; + if (v8 > xmaxval) xmaxval = v8; + } + /* now populate the differences arrays */ + /* for the remaining pixels in the row */ + nvals = 0; + nvals2 = 0; + for (ii++; ii < nx; ii++) { + + /* find the next valid pixel in row */ + if (nullcheck) + while (ii < nx && rowpix[ii] == nullvalue) ii++; + + if (ii == nx) break; /* hit end of row */ + v9 = rowpix[ii]; /* store the good pixel value */ + + if (do_range) { + if (v9 < xminval) xminval = v9; + if (v9 > xmaxval) xmaxval = v9; + } + + /* construct array of absolute differences */ + + if (!(v5 == v6 && v6 == v7) ) { + differences2[nvals2] = (float) fabs(v5 - v7); + nvals2++; + } + + if (!(v3 == v4 && v4 == v5 && v5 == v6 && v6 == v7) ) { + differences3[nvals] = (float) fabs((2 * v5) - v3 - v7); + differences5[nvals] = (float) fabs((6 * v5) - (4 * v3) - (4 * v7) + v1 + v9); + nvals++; + } else { + /* ignore constant background regions */ + ngoodpix++; + } + + /* shift over 1 pixel */ + v1 = v2; + v2 = v3; + v3 = v4; + v4 = v5; + v5 = v6; + v6 = v7; + v7 = v8; + v8 = v9; + } /* end of loop over pixels in the row */ + + /* compute the median diffs */ + /* Note that there are 8 more pixel values than there are diffs values. */ + ngoodpix += nvals; + + if (nvals == 0) { + continue; /* cannot compute medians on this row */ + } else if (nvals == 1) { + if (nvals2 == 1) { + diffs2[nrows2] = differences2[0]; + nrows2++; + } + + diffs3[nrows] = differences3[0]; + diffs5[nrows] = differences5[0]; + } else { + /* quick_select returns the median MUCH faster than using qsort */ + if (nvals2 > 1) { + diffs2[nrows2] = quick_select_float(differences2, nvals); + nrows2++; + } + + diffs3[nrows] = quick_select_float(differences3, nvals); + diffs5[nrows] = quick_select_float(differences5, nvals); + } + + nrows++; + } /* end of loop over rows */ + + /* compute median of the values for each row */ + if (nrows == 0) { + xnoise3 = 0; + xnoise5 = 0; + } else if (nrows == 1) { + xnoise3 = diffs3[0]; + xnoise5 = diffs5[0]; + } else { + qsort(diffs3, nrows, sizeof(double), FnCompare_double); + qsort(diffs5, nrows, sizeof(double), FnCompare_double); + xnoise3 = (diffs3[(nrows - 1)/2] + diffs3[nrows/2]) / 2.; + xnoise5 = (diffs5[(nrows - 1)/2] + diffs5[nrows/2]) / 2.; + } + + if (nrows2 == 0) { + xnoise2 = 0; + } else if (nrows2 == 1) { + xnoise2 = diffs2[0]; + } else { + qsort(diffs2, nrows2, sizeof(double), FnCompare_double); + xnoise2 = (diffs2[(nrows2 - 1)/2] + diffs2[nrows2/2]) / 2.; + } + + if (ngood) *ngood = ngoodpix; + if (minval) *minval = xminval; + if (maxval) *maxval = xmaxval; + if (noise2) *noise2 = 1.0483579 * xnoise2; + if (noise3) *noise3 = 0.6052697 * xnoise3; + if (noise5) *noise5 = 0.1772048 * xnoise5; + + free(diffs5); + free(diffs3); + free(diffs2); + free(differences5); + free(differences3); + free(differences2); + + return(*status); +} +/*--------------------------------------------------------------------------*/ +static int FnNoise5_double + (double *array, /* 2 dimensional array of image pixels */ + long nx, /* number of pixels in each row of the image */ + long ny, /* number of rows in the image */ + int nullcheck, /* check for null values, if true */ + double nullvalue, /* value of null pixels, if nullcheck is true */ + /* returned parameters */ + long *ngood, /* number of good, non-null pixels? */ + double *minval, /* minimum non-null value */ + double *maxval, /* maximum non-null value */ + double *noise2, /* returned 2nd order MAD of all non-null pixels */ + double *noise3, /* returned 3rd order MAD of all non-null pixels */ + double *noise5, /* returned 5th order MAD of all non-null pixels */ + int *status) /* error status */ + +/* +Estimate the median and background noise in the input image using 2nd, 3rd and 5th +order Median Absolute Differences. + +The noise in the background of the image is calculated using the MAD algorithms +developed for deriving the signal to noise ratio in spectra +(see issue #42 of the ST-ECF newsletter, http://www.stecf.org/documents/newsletter/) + +3rd order: noise = 1.482602 / sqrt(6) * median (abs(2*flux(i) - flux(i-2) - flux(i+2))) + +The returned estimates are the median of the values that are computed for each +row of the image. +*/ +{ + long ii, jj, nrows = 0, nrows2 = 0, nvals, nvals2, ngoodpix = 0; + double *differences2, *differences3, *differences5; + double *rowpix, v1, v2, v3, v4, v5, v6, v7, v8, v9; + double xminval = DBL_MAX, xmaxval = -DBL_MAX; + int do_range = 0; + double *diffs2, *diffs3, *diffs5; + double xnoise2 = 0, xnoise3 = 0, xnoise5 = 0; + + if (nx < 9) { + /* treat entire array as an image with a single row */ + nx = nx * ny; + ny = 1; + } + + /* rows must have at least 9 pixels */ + if (nx < 9) { + + for (ii = 0; ii < nx; ii++) { + if (nullcheck && array[ii] == nullvalue) + continue; + else { + if (array[ii] < xminval) xminval = array[ii]; + if (array[ii] > xmaxval) xmaxval = array[ii]; + ngoodpix++; + } + } + if (minval) *minval = xminval; + if (maxval) *maxval = xmaxval; + if (ngood) *ngood = ngoodpix; + if (noise2) *noise2 = 0.; + if (noise3) *noise3 = 0.; + if (noise5) *noise5 = 0.; + return(*status); + } + + /* do we need to compute the min and max value? */ + if (minval || maxval) do_range = 1; + + /* allocate arrays used to compute the median and noise estimates */ + differences2 = calloc(nx, sizeof(double)); + if (!differences2) { + *status = MEMORY_ALLOCATION; + return(*status); + } + differences3 = calloc(nx, sizeof(double)); + if (!differences3) { + free(differences2); + *status = MEMORY_ALLOCATION; + return(*status); + } + differences5 = calloc(nx, sizeof(double)); + if (!differences5) { + free(differences2); + free(differences3); + *status = MEMORY_ALLOCATION; + return(*status); + } + + diffs2 = calloc(ny, sizeof(double)); + if (!diffs2) { + free(differences2); + free(differences3); + free(differences5); + *status = MEMORY_ALLOCATION; + return(*status); + } + + diffs3 = calloc(ny, sizeof(double)); + if (!diffs3) { + free(differences2); + free(differences3); + free(differences5); + free(diffs2); + *status = MEMORY_ALLOCATION; + return(*status); + } + + diffs5 = calloc(ny, sizeof(double)); + if (!diffs5) { + free(differences2); + free(differences3); + free(differences5); + free(diffs2); + free(diffs3); + *status = MEMORY_ALLOCATION; + return(*status); + } + + /* loop over each row of the image */ + for (jj=0; jj < ny; jj++) { + + rowpix = array + (jj * nx); /* point to first pixel in the row */ + + /***** find the first valid pixel in row */ + ii = 0; + if (nullcheck) + while (ii < nx && rowpix[ii] == nullvalue) ii++; + + if (ii == nx) continue; /* hit end of row */ + v1 = rowpix[ii]; /* store the good pixel value */ + ngoodpix++; + + if (do_range) { + if (v1 < xminval) xminval = v1; + if (v1 > xmaxval) xmaxval = v1; + } + + /***** find the 2nd valid pixel in row (which we will skip over) */ + ii++; + if (nullcheck) + while (ii < nx && rowpix[ii] == nullvalue) ii++; + + if (ii == nx) continue; /* hit end of row */ + v2 = rowpix[ii]; /* store the good pixel value */ + ngoodpix++; + + if (do_range) { + if (v2 < xminval) xminval = v2; + if (v2 > xmaxval) xmaxval = v2; + } + + /***** find the 3rd valid pixel in row */ + ii++; + if (nullcheck) + while (ii < nx && rowpix[ii] == nullvalue) ii++; + + if (ii == nx) continue; /* hit end of row */ + v3 = rowpix[ii]; /* store the good pixel value */ + ngoodpix++; + + if (do_range) { + if (v3 < xminval) xminval = v3; + if (v3 > xmaxval) xmaxval = v3; + } + + /* find the 4nd valid pixel in row (to be skipped) */ + ii++; + if (nullcheck) + while (ii < nx && rowpix[ii] == nullvalue) ii++; + + if (ii == nx) continue; /* hit end of row */ + v4 = rowpix[ii]; /* store the good pixel value */ + ngoodpix++; + + if (do_range) { + if (v4 < xminval) xminval = v4; + if (v4 > xmaxval) xmaxval = v4; + } + + /* find the 5th valid pixel in row (to be skipped) */ + ii++; + if (nullcheck) + while (ii < nx && rowpix[ii] == nullvalue) ii++; + + if (ii == nx) continue; /* hit end of row */ + v5 = rowpix[ii]; /* store the good pixel value */ + ngoodpix++; + + if (do_range) { + if (v5 < xminval) xminval = v5; + if (v5 > xmaxval) xmaxval = v5; + } + + /* find the 6th valid pixel in row (to be skipped) */ + ii++; + if (nullcheck) + while (ii < nx && rowpix[ii] == nullvalue) ii++; + + if (ii == nx) continue; /* hit end of row */ + v6 = rowpix[ii]; /* store the good pixel value */ + ngoodpix++; + + if (do_range) { + if (v6 < xminval) xminval = v6; + if (v6 > xmaxval) xmaxval = v6; + } + + /* find the 7th valid pixel in row (to be skipped) */ + ii++; + if (nullcheck) + while (ii < nx && rowpix[ii] == nullvalue) ii++; + + if (ii == nx) continue; /* hit end of row */ + v7 = rowpix[ii]; /* store the good pixel value */ + ngoodpix++; + + if (do_range) { + if (v7 < xminval) xminval = v7; + if (v7 > xmaxval) xmaxval = v7; + } + + /* find the 8th valid pixel in row (to be skipped) */ + ii++; + if (nullcheck) + while (ii < nx && rowpix[ii] == nullvalue) ii++; + + if (ii == nx) continue; /* hit end of row */ + v8 = rowpix[ii]; /* store the good pixel value */ + ngoodpix++; + + if (do_range) { + if (v8 < xminval) xminval = v8; + if (v8 > xmaxval) xmaxval = v8; + } + /* now populate the differences arrays */ + /* for the remaining pixels in the row */ + nvals = 0; + nvals2 = 0; + for (ii++; ii < nx; ii++) { + + /* find the next valid pixel in row */ + if (nullcheck) + while (ii < nx && rowpix[ii] == nullvalue) ii++; + + if (ii == nx) break; /* hit end of row */ + v9 = rowpix[ii]; /* store the good pixel value */ + + if (do_range) { + if (v9 < xminval) xminval = v9; + if (v9 > xmaxval) xmaxval = v9; + } + + /* construct array of absolute differences */ + + if (!(v5 == v6 && v6 == v7) ) { + differences2[nvals2] = fabs(v5 - v7); + nvals2++; + } + + if (!(v3 == v4 && v4 == v5 && v5 == v6 && v6 == v7) ) { + differences3[nvals] = fabs((2 * v5) - v3 - v7); + differences5[nvals] = fabs((6 * v5) - (4 * v3) - (4 * v7) + v1 + v9); + nvals++; + } else { + /* ignore constant background regions */ + ngoodpix++; + } + + /* shift over 1 pixel */ + v1 = v2; + v2 = v3; + v3 = v4; + v4 = v5; + v5 = v6; + v6 = v7; + v7 = v8; + v8 = v9; + } /* end of loop over pixels in the row */ + + /* compute the median diffs */ + /* Note that there are 8 more pixel values than there are diffs values. */ + ngoodpix += nvals; + + if (nvals == 0) { + continue; /* cannot compute medians on this row */ + } else if (nvals == 1) { + if (nvals2 == 1) { + diffs2[nrows2] = differences2[0]; + nrows2++; + } + + diffs3[nrows] = differences3[0]; + diffs5[nrows] = differences5[0]; + } else { + /* quick_select returns the median MUCH faster than using qsort */ + if (nvals2 > 1) { + diffs2[nrows2] = quick_select_double(differences2, nvals); + nrows2++; + } + + diffs3[nrows] = quick_select_double(differences3, nvals); + diffs5[nrows] = quick_select_double(differences5, nvals); + } + + nrows++; + } /* end of loop over rows */ + + /* compute median of the values for each row */ + if (nrows == 0) { + xnoise3 = 0; + xnoise5 = 0; + } else if (nrows == 1) { + xnoise3 = diffs3[0]; + xnoise5 = diffs5[0]; + } else { + qsort(diffs3, nrows, sizeof(double), FnCompare_double); + qsort(diffs5, nrows, sizeof(double), FnCompare_double); + xnoise3 = (diffs3[(nrows - 1)/2] + diffs3[nrows/2]) / 2.; + xnoise5 = (diffs5[(nrows - 1)/2] + diffs5[nrows/2]) / 2.; + } + + if (nrows2 == 0) { + xnoise2 = 0; + } else if (nrows2 == 1) { + xnoise2 = diffs2[0]; + } else { + qsort(diffs2, nrows2, sizeof(double), FnCompare_double); + xnoise2 = (diffs2[(nrows2 - 1)/2] + diffs2[nrows2/2]) / 2.; + } + + if (ngood) *ngood = ngoodpix; + if (minval) *minval = xminval; + if (maxval) *maxval = xmaxval; + if (noise2) *noise2 = 1.0483579 * xnoise2; + if (noise3) *noise3 = 0.6052697 * xnoise3; + if (noise5) *noise5 = 0.1772048 * xnoise5; + + free(diffs5); + free(diffs3); + free(diffs2); + free(differences5); + free(differences3); + free(differences2); + + return(*status); +} +/*--------------------------------------------------------------------------*/ +static int FnNoise3_short + (short *array, /* 2 dimensional array of image pixels */ + long nx, /* number of pixels in each row of the image */ + long ny, /* number of rows in the image */ + int nullcheck, /* check for null values, if true */ + short nullvalue, /* value of null pixels, if nullcheck is true */ + /* returned parameters */ + long *ngood, /* number of good, non-null pixels? */ + short *minval, /* minimum non-null value */ + short *maxval, /* maximum non-null value */ + double *noise, /* returned R.M.S. value of all non-null pixels */ + int *status) /* error status */ + +/* +Estimate the median and background noise in the input image using 3rd order differences. + +The noise in the background of the image is calculated using the 3rd order algorithm +developed for deriving the signal to noise ratio in spectra +(see issue #42 of the ST-ECF newsletter, http://www.stecf.org/documents/newsletter/) + + noise = 1.482602 / sqrt(6) * median (abs(2*flux(i) - flux(i-2) - flux(i+2))) + +The returned estimates are the median of the values that are computed for each +row of the image. +*/ +{ + long ii, jj, nrows = 0, nvals, ngoodpix = 0; + short *differences, *rowpix, v1, v2, v3, v4, v5; + short xminval = SHRT_MAX, xmaxval = SHRT_MIN, do_range = 0; + double *diffs, xnoise = 0, sigma; + + if (nx < 5) { + /* treat entire array as an image with a single row */ + nx = nx * ny; + ny = 1; + } + + /* rows must have at least 5 pixels */ + if (nx < 5) { + + for (ii = 0; ii < nx; ii++) { + if (nullcheck && array[ii] == nullvalue) + continue; + else { + if (array[ii] < xminval) xminval = array[ii]; + if (array[ii] > xmaxval) xmaxval = array[ii]; + ngoodpix++; + } + } + if (minval) *minval = xminval; + if (maxval) *maxval = xmaxval; + if (ngood) *ngood = ngoodpix; + if (noise) *noise = 0.; + return(*status); + } + + /* do we need to compute the min and max value? */ + if (minval || maxval) do_range = 1; + + /* allocate arrays used to compute the median and noise estimates */ + differences = calloc(nx, sizeof(short)); + if (!differences) { + *status = MEMORY_ALLOCATION; + return(*status); + } + + diffs = calloc(ny, sizeof(double)); + if (!diffs) { + free(differences); + *status = MEMORY_ALLOCATION; + return(*status); + } + + /* loop over each row of the image */ + for (jj=0; jj < ny; jj++) { + + rowpix = array + (jj * nx); /* point to first pixel in the row */ + + /***** find the first valid pixel in row */ + ii = 0; + if (nullcheck) + while (ii < nx && rowpix[ii] == nullvalue) ii++; + + if (ii == nx) continue; /* hit end of row */ + v1 = rowpix[ii]; /* store the good pixel value */ + + if (do_range) { + if (v1 < xminval) xminval = v1; + if (v1 > xmaxval) xmaxval = v1; + } + + /***** find the 2nd valid pixel in row (which we will skip over) */ + ii++; + if (nullcheck) + while (ii < nx && rowpix[ii] == nullvalue) ii++; + + if (ii == nx) continue; /* hit end of row */ + v2 = rowpix[ii]; /* store the good pixel value */ + + if (do_range) { + if (v2 < xminval) xminval = v2; + if (v2 > xmaxval) xmaxval = v2; + } + + /***** find the 3rd valid pixel in row */ + ii++; + if (nullcheck) + while (ii < nx && rowpix[ii] == nullvalue) ii++; + + if (ii == nx) continue; /* hit end of row */ + v3 = rowpix[ii]; /* store the good pixel value */ + + if (do_range) { + if (v3 < xminval) xminval = v3; + if (v3 > xmaxval) xmaxval = v3; + } + + /* find the 4nd valid pixel in row (to be skipped) */ + ii++; + if (nullcheck) + while (ii < nx && rowpix[ii] == nullvalue) ii++; + + if (ii == nx) continue; /* hit end of row */ + v4 = rowpix[ii]; /* store the good pixel value */ + + if (do_range) { + if (v4 < xminval) xminval = v4; + if (v4 > xmaxval) xmaxval = v4; + } + + /* now populate the differences arrays */ + /* for the remaining pixels in the row */ + nvals = 0; + for (ii++; ii < nx; ii++) { + + /* find the next valid pixel in row */ + if (nullcheck) + while (ii < nx && rowpix[ii] == nullvalue) ii++; + + if (ii == nx) break; /* hit end of row */ + v5 = rowpix[ii]; /* store the good pixel value */ + + if (do_range) { + if (v5 < xminval) xminval = v5; + if (v5 > xmaxval) xmaxval = v5; + } + + /* construct array of 3rd order absolute differences */ + if (!(v1 == v2 && v2 == v3 && v3 == v4 && v4 == v5)) { + differences[nvals] = abs((2 * v3) - v1 - v5); + nvals++; + } else { + /* ignore constant background regions */ + ngoodpix++; + } + + + /* shift over 1 pixel */ + v1 = v2; + v2 = v3; + v3 = v4; + v4 = v5; + } /* end of loop over pixels in the row */ + + /* compute the 3rd order diffs */ + /* Note that there are 4 more pixel values than there are diffs values. */ + ngoodpix += (nvals + 4); + + if (nvals == 0) { + continue; /* cannot compute medians on this row */ + } else if (nvals == 1) { + diffs[nrows] = differences[0]; + } else { + /* quick_select returns the median MUCH faster than using qsort */ + diffs[nrows] = quick_select_short(differences, nvals); + } + + nrows++; + } /* end of loop over rows */ + + /* compute median of the values for each row */ + if (nrows == 0) { + xnoise = 0; + } else if (nrows == 1) { + xnoise = diffs[0]; + } else { + + + qsort(diffs, nrows, sizeof(double), FnCompare_double); + xnoise = (diffs[(nrows - 1)/2] + diffs[nrows/2]) / 2.; + + FnMeanSigma_double(diffs, nrows, 0, 0.0, 0, &xnoise, &sigma, status); + + /* do a 4.5 sigma rejection of outliers */ + jj = 0; + sigma = 4.5 * sigma; + for (ii = 0; ii < nrows; ii++) { + if ( fabs(diffs[ii] - xnoise) <= sigma) { + if (jj != ii) + diffs[jj] = diffs[ii]; + jj++; + } + } + if (ii != jj) + FnMeanSigma_double(diffs, jj, 0, 0.0, 0, &xnoise, &sigma, status); + } + + if (ngood) *ngood = ngoodpix; + if (minval) *minval = xminval; + if (maxval) *maxval = xmaxval; + if (noise) *noise = 0.6052697 * xnoise; + + free(diffs); + free(differences); + + return(*status); +} +/*--------------------------------------------------------------------------*/ +static int FnNoise3_int + (int *array, /* 2 dimensional array of image pixels */ + long nx, /* number of pixels in each row of the image */ + long ny, /* number of rows in the image */ + int nullcheck, /* check for null values, if true */ + int nullvalue, /* value of null pixels, if nullcheck is true */ + /* returned parameters */ + long *ngood, /* number of good, non-null pixels? */ + int *minval, /* minimum non-null value */ + int *maxval, /* maximum non-null value */ + double *noise, /* returned R.M.S. value of all non-null pixels */ + int *status) /* error status */ + +/* +Estimate the background noise in the input image using 3rd order differences. + +The noise in the background of the image is calculated using the 3rd order algorithm +developed for deriving the signal to noise ratio in spectra +(see issue #42 of the ST-ECF newsletter, http://www.stecf.org/documents/newsletter/) + + noise = 1.482602 / sqrt(6) * median (abs(2*flux(i) - flux(i-2) - flux(i+2))) + +The returned estimates are the median of the values that are computed for each +row of the image. +*/ +{ + long ii, jj, nrows = 0, nvals, ngoodpix = 0; + int *differences, *rowpix, v1, v2, v3, v4, v5; + int xminval = INT_MAX, xmaxval = INT_MIN, do_range = 0; + double *diffs, xnoise = 0, sigma; + + if (nx < 5) { + /* treat entire array as an image with a single row */ + nx = nx * ny; + ny = 1; + } + + /* rows must have at least 5 pixels */ + if (nx < 5) { + + for (ii = 0; ii < nx; ii++) { + if (nullcheck && array[ii] == nullvalue) + continue; + else { + if (array[ii] < xminval) xminval = array[ii]; + if (array[ii] > xmaxval) xmaxval = array[ii]; + ngoodpix++; + } + } + if (minval) *minval = xminval; + if (maxval) *maxval = xmaxval; + if (ngood) *ngood = ngoodpix; + if (noise) *noise = 0.; + return(*status); + } + + /* do we need to compute the min and max value? */ + if (minval || maxval) do_range = 1; + + /* allocate arrays used to compute the median and noise estimates */ + differences = calloc(nx, sizeof(int)); + if (!differences) { + *status = MEMORY_ALLOCATION; + return(*status); + } + + diffs = calloc(ny, sizeof(double)); + if (!diffs) { + free(differences); + *status = MEMORY_ALLOCATION; + return(*status); + } + + /* loop over each row of the image */ + for (jj=0; jj < ny; jj++) { + + rowpix = array + (jj * nx); /* point to first pixel in the row */ + + /***** find the first valid pixel in row */ + ii = 0; + if (nullcheck) + while (ii < nx && rowpix[ii] == nullvalue) ii++; + + if (ii == nx) continue; /* hit end of row */ + v1 = rowpix[ii]; /* store the good pixel value */ + + if (do_range) { + if (v1 < xminval) xminval = v1; + if (v1 > xmaxval) xmaxval = v1; + } + + /***** find the 2nd valid pixel in row (which we will skip over) */ + ii++; + if (nullcheck) + while (ii < nx && rowpix[ii] == nullvalue) ii++; + + if (ii == nx) continue; /* hit end of row */ + v2 = rowpix[ii]; /* store the good pixel value */ + + if (do_range) { + if (v2 < xminval) xminval = v2; + if (v2 > xmaxval) xmaxval = v2; + } + + /***** find the 3rd valid pixel in row */ + ii++; + if (nullcheck) + while (ii < nx && rowpix[ii] == nullvalue) ii++; + + if (ii == nx) continue; /* hit end of row */ + v3 = rowpix[ii]; /* store the good pixel value */ + + if (do_range) { + if (v3 < xminval) xminval = v3; + if (v3 > xmaxval) xmaxval = v3; + } + + /* find the 4nd valid pixel in row (to be skipped) */ + ii++; + if (nullcheck) + while (ii < nx && rowpix[ii] == nullvalue) ii++; + + if (ii == nx) continue; /* hit end of row */ + v4 = rowpix[ii]; /* store the good pixel value */ + + if (do_range) { + if (v4 < xminval) xminval = v4; + if (v4 > xmaxval) xmaxval = v4; + } + + /* now populate the differences arrays */ + /* for the remaining pixels in the row */ + nvals = 0; + for (ii++; ii < nx; ii++) { + + /* find the next valid pixel in row */ + if (nullcheck) + while (ii < nx && rowpix[ii] == nullvalue) ii++; + + if (ii == nx) break; /* hit end of row */ + v5 = rowpix[ii]; /* store the good pixel value */ + + if (do_range) { + if (v5 < xminval) xminval = v5; + if (v5 > xmaxval) xmaxval = v5; + } + + /* construct array of 3rd order absolute differences */ + if (!(v1 == v2 && v2 == v3 && v3 == v4 && v4 == v5)) { + differences[nvals] = abs((2 * v3) - v1 - v5); + nvals++; + } else { + /* ignore constant background regions */ + ngoodpix++; + } + + /* shift over 1 pixel */ + v1 = v2; + v2 = v3; + v3 = v4; + v4 = v5; + } /* end of loop over pixels in the row */ + + /* compute the 3rd order diffs */ + /* Note that there are 4 more pixel values than there are diffs values. */ + ngoodpix += (nvals + 4); + + if (nvals == 0) { + continue; /* cannot compute medians on this row */ + } else if (nvals == 1) { + diffs[nrows] = differences[0]; + } else { + /* quick_select returns the median MUCH faster than using qsort */ + diffs[nrows] = quick_select_int(differences, nvals); + } + + nrows++; + } /* end of loop over rows */ + + /* compute median of the values for each row */ + if (nrows == 0) { + xnoise = 0; + } else if (nrows == 1) { + xnoise = diffs[0]; + } else { + + qsort(diffs, nrows, sizeof(double), FnCompare_double); + xnoise = (diffs[(nrows - 1)/2] + diffs[nrows/2]) / 2.; + + FnMeanSigma_double(diffs, nrows, 0, 0.0, 0, &xnoise, &sigma, status); + + /* do a 4.5 sigma rejection of outliers */ + jj = 0; + sigma = 4.5 * sigma; + for (ii = 0; ii < nrows; ii++) { + if ( fabs(diffs[ii] - xnoise) <= sigma) { + if (jj != ii) + diffs[jj] = diffs[ii]; + jj++; + } + } + if (ii != jj) + FnMeanSigma_double(diffs, jj, 0, 0.0, 0, &xnoise, &sigma, status); + } + + if (ngood) *ngood = ngoodpix; + if (minval) *minval = xminval; + if (maxval) *maxval = xmaxval; + if (noise) *noise = 0.6052697 * xnoise; + + free(diffs); + free(differences); + + return(*status); +} +/*--------------------------------------------------------------------------*/ +static int FnNoise3_float + (float *array, /* 2 dimensional array of image pixels */ + long nx, /* number of pixels in each row of the image */ + long ny, /* number of rows in the image */ + int nullcheck, /* check for null values, if true */ + float nullvalue, /* value of null pixels, if nullcheck is true */ + /* returned parameters */ + long *ngood, /* number of good, non-null pixels? */ + float *minval, /* minimum non-null value */ + float *maxval, /* maximum non-null value */ + double *noise, /* returned R.M.S. value of all non-null pixels */ + int *status) /* error status */ + +/* +Estimate the median and background noise in the input image using 3rd order differences. + +The noise in the background of the image is calculated using the 3rd order algorithm +developed for deriving the signal to noise ratio in spectra +(see issue #42 of the ST-ECF newsletter, http://www.stecf.org/documents/newsletter/) + + noise = 1.482602 / sqrt(6) * median (abs(2*flux(i) - flux(i-2) - flux(i+2))) + +The returned estimates are the median of the values that are computed for each +row of the image. +*/ +{ + long ii, jj, nrows = 0, nvals, ngoodpix = 0; + float *differences, *rowpix, v1, v2, v3, v4, v5; + float xminval = FLT_MAX, xmaxval = -FLT_MAX; + int do_range = 0; + double *diffs, xnoise = 0; + + if (nx < 5) { + /* treat entire array as an image with a single row */ + nx = nx * ny; + ny = 1; + } + + /* rows must have at least 5 pixels to calc noise, so just calc min, max, ngood */ + if (nx < 5) { + + for (ii = 0; ii < nx; ii++) { + if (nullcheck && array[ii] == nullvalue) + continue; + else { + if (array[ii] < xminval) xminval = array[ii]; + if (array[ii] > xmaxval) xmaxval = array[ii]; + ngoodpix++; + } + } + if (minval) *minval = xminval; + if (maxval) *maxval = xmaxval; + if (ngood) *ngood = ngoodpix; + if (noise) *noise = 0.; + return(*status); + } + + /* do we need to compute the min and max value? */ + if (minval || maxval) do_range = 1; + + /* allocate arrays used to compute the median and noise estimates */ + if (noise) { + differences = calloc(nx, sizeof(float)); + if (!differences) { + *status = MEMORY_ALLOCATION; + return(*status); + } + + diffs = calloc(ny, sizeof(double)); + if (!diffs) { + free(differences); + *status = MEMORY_ALLOCATION; + return(*status); + } + } + + /* loop over each row of the image */ + for (jj=0; jj < ny; jj++) { + + rowpix = array + (jj * nx); /* point to first pixel in the row */ + + /***** find the first valid pixel in row */ + ii = 0; + if (nullcheck) + while (ii < nx && rowpix[ii] == nullvalue) ii++; + + if (ii == nx) continue; /* hit end of row */ + v1 = rowpix[ii]; /* store the good pixel value */ + + if (do_range) { + if (v1 < xminval) xminval = v1; + if (v1 > xmaxval) xmaxval = v1; + } + + /***** find the 2nd valid pixel in row (which we will skip over) */ + ii++; + if (nullcheck) + while (ii < nx && rowpix[ii] == nullvalue) ii++; + + if (ii == nx) continue; /* hit end of row */ + v2 = rowpix[ii]; /* store the good pixel value */ + + if (do_range) { + if (v2 < xminval) xminval = v2; + if (v2 > xmaxval) xmaxval = v2; + } + + /***** find the 3rd valid pixel in row */ + ii++; + if (nullcheck) + while (ii < nx && rowpix[ii] == nullvalue) ii++; + + if (ii == nx) continue; /* hit end of row */ + v3 = rowpix[ii]; /* store the good pixel value */ + + if (do_range) { + if (v3 < xminval) xminval = v3; + if (v3 > xmaxval) xmaxval = v3; + } + + /* find the 4nd valid pixel in row (to be skipped) */ + ii++; + if (nullcheck) + while (ii < nx && rowpix[ii] == nullvalue) ii++; + + if (ii == nx) continue; /* hit end of row */ + v4 = rowpix[ii]; /* store the good pixel value */ + + if (do_range) { + if (v4 < xminval) xminval = v4; + if (v4 > xmaxval) xmaxval = v4; + } + + /* now populate the differences arrays */ + /* for the remaining pixels in the row */ + nvals = 0; + for (ii++; ii < nx; ii++) { + + /* find the next valid pixel in row */ + if (nullcheck) + while (ii < nx && rowpix[ii] == nullvalue) { + ii++; + } + + if (ii == nx) break; /* hit end of row */ + v5 = rowpix[ii]; /* store the good pixel value */ + + if (do_range) { + if (v5 < xminval) xminval = v5; + if (v5 > xmaxval) xmaxval = v5; + } + + /* construct array of 3rd order absolute differences */ + if (noise) { + if (!(v1 == v2 && v2 == v3 && v3 == v4 && v4 == v5)) { + + differences[nvals] = (float) fabs((2. * v3) - v1 - v5); + nvals++; + } else { + /* ignore constant background regions */ + ngoodpix++; + } + } else { + /* just increment the number of non-null pixels */ + ngoodpix++; + } + + /* shift over 1 pixel */ + v1 = v2; + v2 = v3; + v3 = v4; + v4 = v5; + } /* end of loop over pixels in the row */ + + /* compute the 3rd order diffs */ + /* Note that there are 4 more pixel values than there are diffs values. */ + ngoodpix += (nvals + 4); + + if (noise) { + if (nvals == 0) { + continue; /* cannot compute medians on this row */ + } else if (nvals == 1) { + diffs[nrows] = differences[0]; + } else { + /* quick_select returns the median MUCH faster than using qsort */ + diffs[nrows] = quick_select_float(differences, nvals); + } + } + nrows++; + } /* end of loop over rows */ + + /* compute median of the values for each row */ + if (noise) { + if (nrows == 0) { + xnoise = 0; + } else if (nrows == 1) { + xnoise = diffs[0]; + } else { + qsort(diffs, nrows, sizeof(double), FnCompare_double); + xnoise = (diffs[(nrows - 1)/2] + diffs[nrows/2]) / 2.; + } + } + + if (ngood) *ngood = ngoodpix; + if (minval) *minval = xminval; + if (maxval) *maxval = xmaxval; + if (noise) { + *noise = 0.6052697 * xnoise; + free(diffs); + free(differences); + } + + return(*status); +} +/*--------------------------------------------------------------------------*/ +static int FnNoise3_double + (double *array, /* 2 dimensional array of image pixels */ + long nx, /* number of pixels in each row of the image */ + long ny, /* number of rows in the image */ + int nullcheck, /* check for null values, if true */ + double nullvalue, /* value of null pixels, if nullcheck is true */ + /* returned parameters */ + long *ngood, /* number of good, non-null pixels? */ + double *minval, /* minimum non-null value */ + double *maxval, /* maximum non-null value */ + double *noise, /* returned R.M.S. value of all non-null pixels */ + int *status) /* error status */ + +/* +Estimate the median and background noise in the input image using 3rd order differences. + +The noise in the background of the image is calculated using the 3rd order algorithm +developed for deriving the signal to noise ratio in spectra +(see issue #42 of the ST-ECF newsletter, http://www.stecf.org/documents/newsletter/) + + noise = 1.482602 / sqrt(6) * median (abs(2*flux(i) - flux(i-2) - flux(i+2))) + +The returned estimates are the median of the values that are computed for each +row of the image. +*/ +{ + long ii, jj, nrows = 0, nvals, ngoodpix = 0; + double *differences, *rowpix, v1, v2, v3, v4, v5; + double xminval = DBL_MAX, xmaxval = -DBL_MAX; + int do_range = 0; + double *diffs, xnoise = 0; + + if (nx < 5) { + /* treat entire array as an image with a single row */ + nx = nx * ny; + ny = 1; + } + + /* rows must have at least 5 pixels */ + if (nx < 5) { + + for (ii = 0; ii < nx; ii++) { + if (nullcheck && array[ii] == nullvalue) + continue; + else { + if (array[ii] < xminval) xminval = array[ii]; + if (array[ii] > xmaxval) xmaxval = array[ii]; + ngoodpix++; + } + } + if (minval) *minval = xminval; + if (maxval) *maxval = xmaxval; + if (ngood) *ngood = ngoodpix; + if (noise) *noise = 0.; + return(*status); + } + + /* do we need to compute the min and max value? */ + if (minval || maxval) do_range = 1; + + /* allocate arrays used to compute the median and noise estimates */ + if (noise) { + differences = calloc(nx, sizeof(double)); + if (!differences) { + *status = MEMORY_ALLOCATION; + return(*status); + } + + diffs = calloc(ny, sizeof(double)); + if (!diffs) { + free(differences); + *status = MEMORY_ALLOCATION; + return(*status); + } + } + + /* loop over each row of the image */ + for (jj=0; jj < ny; jj++) { + + rowpix = array + (jj * nx); /* point to first pixel in the row */ + + /***** find the first valid pixel in row */ + ii = 0; + if (nullcheck) + while (ii < nx && rowpix[ii] == nullvalue) ii++; + + if (ii == nx) continue; /* hit end of row */ + v1 = rowpix[ii]; /* store the good pixel value */ + + if (do_range) { + if (v1 < xminval) xminval = v1; + if (v1 > xmaxval) xmaxval = v1; + } + + /***** find the 2nd valid pixel in row (which we will skip over) */ + ii++; + if (nullcheck) + while (ii < nx && rowpix[ii] == nullvalue) ii++; + + if (ii == nx) continue; /* hit end of row */ + v2 = rowpix[ii]; /* store the good pixel value */ + + if (do_range) { + if (v2 < xminval) xminval = v2; + if (v2 > xmaxval) xmaxval = v2; + } + + /***** find the 3rd valid pixel in row */ + ii++; + if (nullcheck) + while (ii < nx && rowpix[ii] == nullvalue) ii++; + + if (ii == nx) continue; /* hit end of row */ + v3 = rowpix[ii]; /* store the good pixel value */ + + if (do_range) { + if (v3 < xminval) xminval = v3; + if (v3 > xmaxval) xmaxval = v3; + } + + /* find the 4nd valid pixel in row (to be skipped) */ + ii++; + if (nullcheck) + while (ii < nx && rowpix[ii] == nullvalue) ii++; + + if (ii == nx) continue; /* hit end of row */ + v4 = rowpix[ii]; /* store the good pixel value */ + + if (do_range) { + if (v4 < xminval) xminval = v4; + if (v4 > xmaxval) xmaxval = v4; + } + + /* now populate the differences arrays */ + /* for the remaining pixels in the row */ + nvals = 0; + for (ii++; ii < nx; ii++) { + + /* find the next valid pixel in row */ + if (nullcheck) + while (ii < nx && rowpix[ii] == nullvalue) ii++; + + if (ii == nx) break; /* hit end of row */ + v5 = rowpix[ii]; /* store the good pixel value */ + + if (do_range) { + if (v5 < xminval) xminval = v5; + if (v5 > xmaxval) xmaxval = v5; + } + + /* construct array of 3rd order absolute differences */ + if (noise) { + if (!(v1 == v2 && v2 == v3 && v3 == v4 && v4 == v5)) { + + differences[nvals] = fabs((2. * v3) - v1 - v5); + nvals++; + } else { + /* ignore constant background regions */ + ngoodpix++; + } + } else { + /* just increment the number of non-null pixels */ + ngoodpix++; + } + + /* shift over 1 pixel */ + v1 = v2; + v2 = v3; + v3 = v4; + v4 = v5; + } /* end of loop over pixels in the row */ + + /* compute the 3rd order diffs */ + /* Note that there are 4 more pixel values than there are diffs values. */ + ngoodpix += (nvals + 4); + + if (noise) { + if (nvals == 0) { + continue; /* cannot compute medians on this row */ + } else if (nvals == 1) { + diffs[nrows] = differences[0]; + } else { + /* quick_select returns the median MUCH faster than using qsort */ + diffs[nrows] = quick_select_double(differences, nvals); + } + } + nrows++; + } /* end of loop over rows */ + + /* compute median of the values for each row */ + if (noise) { + if (nrows == 0) { + xnoise = 0; + } else if (nrows == 1) { + xnoise = diffs[0]; + } else { + qsort(diffs, nrows, sizeof(double), FnCompare_double); + xnoise = (diffs[(nrows - 1)/2] + diffs[nrows/2]) / 2.; + } + } + + if (ngood) *ngood = ngoodpix; + if (minval) *minval = xminval; + if (maxval) *maxval = xmaxval; + if (noise) { + *noise = 0.6052697 * xnoise; + free(diffs); + free(differences); + } + + return(*status); +} +/*--------------------------------------------------------------------------*/ +static int FnNoise1_short + (short *array, /* 2 dimensional array of image pixels */ + long nx, /* number of pixels in each row of the image */ + long ny, /* number of rows in the image */ + int nullcheck, /* check for null values, if true */ + short nullvalue, /* value of null pixels, if nullcheck is true */ + /* returned parameters */ + double *noise, /* returned R.M.S. value of all non-null pixels */ + int *status) /* error status */ +/* +Estimate the background noise in the input image using sigma of 1st order differences. + + noise = 1.0 / sqrt(2) * rms of (flux[i] - flux[i-1]) + +The returned estimate is the median of the values that are computed for each +row of the image. +*/ +{ + int iter; + long ii, jj, kk, nrows = 0, nvals; + short *differences, *rowpix, v1; + double *diffs, xnoise, mean, stdev; + + /* rows must have at least 3 pixels to estimate noise */ + if (nx < 3) { + *noise = 0; + return(*status); + } + + /* allocate arrays used to compute the median and noise estimates */ + differences = calloc(nx, sizeof(short)); + if (!differences) { + *status = MEMORY_ALLOCATION; + return(*status); + } + + diffs = calloc(ny, sizeof(double)); + if (!diffs) { + free(differences); + *status = MEMORY_ALLOCATION; + return(*status); + } + + /* loop over each row of the image */ + for (jj=0; jj < ny; jj++) { + + rowpix = array + (jj * nx); /* point to first pixel in the row */ + + /***** find the first valid pixel in row */ + ii = 0; + if (nullcheck) + while (ii < nx && rowpix[ii] == nullvalue) ii++; + + if (ii == nx) continue; /* hit end of row */ + v1 = rowpix[ii]; /* store the good pixel value */ + + /* now continue populating the differences arrays */ + /* for the remaining pixels in the row */ + nvals = 0; + for (ii++; ii < nx; ii++) { + + /* find the next valid pixel in row */ + if (nullcheck) + while (ii < nx && rowpix[ii] == nullvalue) ii++; + + if (ii == nx) break; /* hit end of row */ + + /* construct array of 1st order differences */ + differences[nvals] = v1 - rowpix[ii]; + + nvals++; + /* shift over 1 pixel */ + v1 = rowpix[ii]; + } /* end of loop over pixels in the row */ + + if (nvals < 2) + continue; + else { + + FnMeanSigma_short(differences, nvals, 0, 0, 0, &mean, &stdev, status); + + if (stdev > 0.) { + for (iter = 0; iter < NITER; iter++) { + kk = 0; + for (ii = 0; ii < nvals; ii++) { + if (fabs (differences[ii] - mean) < SIGMA_CLIP * stdev) { + if (kk < ii) + differences[kk] = differences[ii]; + kk++; + } + } + if (kk == nvals) break; + + nvals = kk; + FnMeanSigma_short(differences, nvals, 0, 0, 0, &mean, &stdev, status); + } + } + + diffs[nrows] = stdev; + nrows++; + } + } /* end of loop over rows */ + + /* compute median of the values for each row */ + if (nrows == 0) { + xnoise = 0; + } else if (nrows == 1) { + xnoise = diffs[0]; + } else { + qsort(diffs, nrows, sizeof(double), FnCompare_double); + xnoise = (diffs[(nrows - 1)/2] + diffs[nrows/2]) / 2.; + } + + *noise = .70710678 * xnoise; + + free(diffs); + free(differences); + + return(*status); +} +/*--------------------------------------------------------------------------*/ +static int FnNoise1_int + (int *array, /* 2 dimensional array of image pixels */ + long nx, /* number of pixels in each row of the image */ + long ny, /* number of rows in the image */ + int nullcheck, /* check for null values, if true */ + int nullvalue, /* value of null pixels, if nullcheck is true */ + /* returned parameters */ + double *noise, /* returned R.M.S. value of all non-null pixels */ + int *status) /* error status */ +/* +Estimate the background noise in the input image using sigma of 1st order differences. + + noise = 1.0 / sqrt(2) * rms of (flux[i] - flux[i-1]) + +The returned estimate is the median of the values that are computed for each +row of the image. +*/ +{ + int iter; + long ii, jj, kk, nrows = 0, nvals; + int *differences, *rowpix, v1; + double *diffs, xnoise, mean, stdev; + + /* rows must have at least 3 pixels to estimate noise */ + if (nx < 3) { + *noise = 0; + return(*status); + } + + /* allocate arrays used to compute the median and noise estimates */ + differences = calloc(nx, sizeof(int)); + if (!differences) { + *status = MEMORY_ALLOCATION; + return(*status); + } + + diffs = calloc(ny, sizeof(double)); + if (!diffs) { + free(differences); + *status = MEMORY_ALLOCATION; + return(*status); + } + + /* loop over each row of the image */ + for (jj=0; jj < ny; jj++) { + + rowpix = array + (jj * nx); /* point to first pixel in the row */ + + /***** find the first valid pixel in row */ + ii = 0; + if (nullcheck) + while (ii < nx && rowpix[ii] == nullvalue) ii++; + + if (ii == nx) continue; /* hit end of row */ + v1 = rowpix[ii]; /* store the good pixel value */ + + /* now continue populating the differences arrays */ + /* for the remaining pixels in the row */ + nvals = 0; + for (ii++; ii < nx; ii++) { + + /* find the next valid pixel in row */ + if (nullcheck) + while (ii < nx && rowpix[ii] == nullvalue) ii++; + + if (ii == nx) break; /* hit end of row */ + + /* construct array of 1st order differences */ + differences[nvals] = v1 - rowpix[ii]; + + nvals++; + /* shift over 1 pixel */ + v1 = rowpix[ii]; + } /* end of loop over pixels in the row */ + + if (nvals < 2) + continue; + else { + + FnMeanSigma_int(differences, nvals, 0, 0, 0, &mean, &stdev, status); + + if (stdev > 0.) { + for (iter = 0; iter < NITER; iter++) { + kk = 0; + for (ii = 0; ii < nvals; ii++) { + if (fabs (differences[ii] - mean) < SIGMA_CLIP * stdev) { + if (kk < ii) + differences[kk] = differences[ii]; + kk++; + } + } + if (kk == nvals) break; + + nvals = kk; + FnMeanSigma_int(differences, nvals, 0, 0, 0, &mean, &stdev, status); + } + } + + diffs[nrows] = stdev; + nrows++; + } + } /* end of loop over rows */ + + /* compute median of the values for each row */ + if (nrows == 0) { + xnoise = 0; + } else if (nrows == 1) { + xnoise = diffs[0]; + } else { + qsort(diffs, nrows, sizeof(double), FnCompare_double); + xnoise = (diffs[(nrows - 1)/2] + diffs[nrows/2]) / 2.; + } + + *noise = .70710678 * xnoise; + + free(diffs); + free(differences); + + return(*status); +} +/*--------------------------------------------------------------------------*/ +static int FnNoise1_float + (float *array, /* 2 dimensional array of image pixels */ + long nx, /* number of pixels in each row of the image */ + long ny, /* number of rows in the image */ + int nullcheck, /* check for null values, if true */ + float nullvalue, /* value of null pixels, if nullcheck is true */ + /* returned parameters */ + double *noise, /* returned R.M.S. value of all non-null pixels */ + int *status) /* error status */ +/* +Estimate the background noise in the input image using sigma of 1st order differences. + + noise = 1.0 / sqrt(2) * rms of (flux[i] - flux[i-1]) + +The returned estimate is the median of the values that are computed for each +row of the image. +*/ +{ + int iter; + long ii, jj, kk, nrows = 0, nvals; + float *differences, *rowpix, v1; + double *diffs, xnoise, mean, stdev; + + /* rows must have at least 3 pixels to estimate noise */ + if (nx < 3) { + *noise = 0; + return(*status); + } + + /* allocate arrays used to compute the median and noise estimates */ + differences = calloc(nx, sizeof(float)); + if (!differences) { + *status = MEMORY_ALLOCATION; + return(*status); + } + + diffs = calloc(ny, sizeof(double)); + if (!diffs) { + free(differences); + *status = MEMORY_ALLOCATION; + return(*status); + } + + /* loop over each row of the image */ + for (jj=0; jj < ny; jj++) { + + rowpix = array + (jj * nx); /* point to first pixel in the row */ + + /***** find the first valid pixel in row */ + ii = 0; + if (nullcheck) + while (ii < nx && rowpix[ii] == nullvalue) ii++; + + if (ii == nx) continue; /* hit end of row */ + v1 = rowpix[ii]; /* store the good pixel value */ + + /* now continue populating the differences arrays */ + /* for the remaining pixels in the row */ + nvals = 0; + for (ii++; ii < nx; ii++) { + + /* find the next valid pixel in row */ + if (nullcheck) + while (ii < nx && rowpix[ii] == nullvalue) ii++; + + if (ii == nx) break; /* hit end of row */ + + /* construct array of 1st order differences */ + differences[nvals] = v1 - rowpix[ii]; + + nvals++; + /* shift over 1 pixel */ + v1 = rowpix[ii]; + } /* end of loop over pixels in the row */ + + if (nvals < 2) + continue; + else { + + FnMeanSigma_float(differences, nvals, 0, 0, 0, &mean, &stdev, status); + + if (stdev > 0.) { + for (iter = 0; iter < NITER; iter++) { + kk = 0; + for (ii = 0; ii < nvals; ii++) { + if (fabs (differences[ii] - mean) < SIGMA_CLIP * stdev) { + if (kk < ii) + differences[kk] = differences[ii]; + kk++; + } + } + if (kk == nvals) break; + + nvals = kk; + FnMeanSigma_float(differences, nvals, 0, 0, 0, &mean, &stdev, status); + } + } + + diffs[nrows] = stdev; + nrows++; + } + } /* end of loop over rows */ + + /* compute median of the values for each row */ + if (nrows == 0) { + xnoise = 0; + } else if (nrows == 1) { + xnoise = diffs[0]; + } else { + qsort(diffs, nrows, sizeof(double), FnCompare_double); + xnoise = (diffs[(nrows - 1)/2] + diffs[nrows/2]) / 2.; + } + + *noise = .70710678 * xnoise; + + free(diffs); + free(differences); + + return(*status); +} +/*--------------------------------------------------------------------------*/ +static int FnNoise1_double + (double *array, /* 2 dimensional array of image pixels */ + long nx, /* number of pixels in each row of the image */ + long ny, /* number of rows in the image */ + int nullcheck, /* check for null values, if true */ + double nullvalue, /* value of null pixels, if nullcheck is true */ + /* returned parameters */ + double *noise, /* returned R.M.S. value of all non-null pixels */ + int *status) /* error status */ +/* +Estimate the background noise in the input image using sigma of 1st order differences. + + noise = 1.0 / sqrt(2) * rms of (flux[i] - flux[i-1]) + +The returned estimate is the median of the values that are computed for each +row of the image. +*/ +{ + int iter; + long ii, jj, kk, nrows = 0, nvals; + double *differences, *rowpix, v1; + double *diffs, xnoise, mean, stdev; + + /* rows must have at least 3 pixels to estimate noise */ + if (nx < 3) { + *noise = 0; + return(*status); + } + + /* allocate arrays used to compute the median and noise estimates */ + differences = calloc(nx, sizeof(double)); + if (!differences) { + *status = MEMORY_ALLOCATION; + return(*status); + } + + diffs = calloc(ny, sizeof(double)); + if (!diffs) { + free(differences); + *status = MEMORY_ALLOCATION; + return(*status); + } + + /* loop over each row of the image */ + for (jj=0; jj < ny; jj++) { + + rowpix = array + (jj * nx); /* point to first pixel in the row */ + + /***** find the first valid pixel in row */ + ii = 0; + if (nullcheck) + while (ii < nx && rowpix[ii] == nullvalue) ii++; + + if (ii == nx) continue; /* hit end of row */ + v1 = rowpix[ii]; /* store the good pixel value */ + + /* now continue populating the differences arrays */ + /* for the remaining pixels in the row */ + nvals = 0; + for (ii++; ii < nx; ii++) { + + /* find the next valid pixel in row */ + if (nullcheck) + while (ii < nx && rowpix[ii] == nullvalue) ii++; + + if (ii == nx) break; /* hit end of row */ + + /* construct array of 1st order differences */ + differences[nvals] = v1 - rowpix[ii]; + + nvals++; + /* shift over 1 pixel */ + v1 = rowpix[ii]; + } /* end of loop over pixels in the row */ + + if (nvals < 2) + continue; + else { + + FnMeanSigma_double(differences, nvals, 0, 0, 0, &mean, &stdev, status); + + if (stdev > 0.) { + for (iter = 0; iter < NITER; iter++) { + kk = 0; + for (ii = 0; ii < nvals; ii++) { + if (fabs (differences[ii] - mean) < SIGMA_CLIP * stdev) { + if (kk < ii) + differences[kk] = differences[ii]; + kk++; + } + } + if (kk == nvals) break; + + nvals = kk; + FnMeanSigma_double(differences, nvals, 0, 0, 0, &mean, &stdev, status); + } + } + + diffs[nrows] = stdev; + nrows++; + } + } /* end of loop over rows */ + + /* compute median of the values for each row */ + if (nrows == 0) { + xnoise = 0; + } else if (nrows == 1) { + xnoise = diffs[0]; + } else { + qsort(diffs, nrows, sizeof(double), FnCompare_double); + xnoise = (diffs[(nrows - 1)/2] + diffs[nrows/2]) / 2.; + } + + *noise = .70710678 * xnoise; + + free(diffs); + free(differences); + + return(*status); +} +/*--------------------------------------------------------------------------*/ +static int FnCompare_short(const void *v1, const void *v2) +{ + const short *i1 = v1; + const short *i2 = v2; + + if (*i1 < *i2) + return(-1); + else if (*i1 > *i2) + return(1); + else + return(0); +} +/*--------------------------------------------------------------------------*/ +static int FnCompare_int(const void *v1, const void *v2) +{ + const int *i1 = v1; + const int *i2 = v2; + + if (*i1 < *i2) + return(-1); + else if (*i1 > *i2) + return(1); + else + return(0); +} +/*--------------------------------------------------------------------------*/ +static int FnCompare_float(const void *v1, const void *v2) +{ + const float *i1 = v1; + const float *i2 = v2; + + if (*i1 < *i2) + return(-1); + else if (*i1 > *i2) + return(1); + else + return(0); +} +/*--------------------------------------------------------------------------*/ +static int FnCompare_double(const void *v1, const void *v2) +{ + const double *i1 = v1; + const double *i2 = v2; + + if (*i1 < *i2) + return(-1); + else if (*i1 > *i2) + return(1); + else + return(0); +} +/*--------------------------------------------------------------------------*/ + +/* + * These Quickselect routines are based on the algorithm described in + * "Numerical recipes in C", Second Edition, + * Cambridge University Press, 1992, Section 8.5, ISBN 0-521-43108-5 + * This code by Nicolas Devillard - 1998. Public domain. + */ + +/*--------------------------------------------------------------------------*/ + +#define ELEM_SWAP(a,b) { register float t=(a);(a)=(b);(b)=t; } + +static float quick_select_float(float arr[], int n) +{ + int low, high ; + int median; + int middle, ll, hh; + + low = 0 ; high = n-1 ; median = (low + high) / 2; + for (;;) { + if (high <= low) /* One element only */ + return arr[median] ; + + if (high == low + 1) { /* Two elements only */ + if (arr[low] > arr[high]) + ELEM_SWAP(arr[low], arr[high]) ; + return arr[median] ; + } + + /* Find median of low, middle and high items; swap into position low */ + middle = (low + high) / 2; + if (arr[middle] > arr[high]) ELEM_SWAP(arr[middle], arr[high]) ; + if (arr[low] > arr[high]) ELEM_SWAP(arr[low], arr[high]) ; + if (arr[middle] > arr[low]) ELEM_SWAP(arr[middle], arr[low]) ; + + /* Swap low item (now in position middle) into position (low+1) */ + ELEM_SWAP(arr[middle], arr[low+1]) ; + + /* Nibble from each end towards middle, swapping items when stuck */ + ll = low + 1; + hh = high; + for (;;) { + do ll++; while (arr[low] > arr[ll]) ; + do hh--; while (arr[hh] > arr[low]) ; + + if (hh < ll) + break; + + ELEM_SWAP(arr[ll], arr[hh]) ; + } + + /* Swap middle item (in position low) back into correct position */ + ELEM_SWAP(arr[low], arr[hh]) ; + + /* Re-set active partition */ + if (hh <= median) low = ll; + if (hh >= median) high = hh - 1; + } +} + +#undef ELEM_SWAP + +/*--------------------------------------------------------------------------*/ + +#define ELEM_SWAP(a,b) { register short t=(a);(a)=(b);(b)=t; } + +static short quick_select_short(short arr[], int n) +{ + int low, high ; + int median; + int middle, ll, hh; + + low = 0 ; high = n-1 ; median = (low + high) / 2; + for (;;) { + if (high <= low) /* One element only */ + return arr[median] ; + + if (high == low + 1) { /* Two elements only */ + if (arr[low] > arr[high]) + ELEM_SWAP(arr[low], arr[high]) ; + return arr[median] ; + } + + /* Find median of low, middle and high items; swap into position low */ + middle = (low + high) / 2; + if (arr[middle] > arr[high]) ELEM_SWAP(arr[middle], arr[high]) ; + if (arr[low] > arr[high]) ELEM_SWAP(arr[low], arr[high]) ; + if (arr[middle] > arr[low]) ELEM_SWAP(arr[middle], arr[low]) ; + + /* Swap low item (now in position middle) into position (low+1) */ + ELEM_SWAP(arr[middle], arr[low+1]) ; + + /* Nibble from each end towards middle, swapping items when stuck */ + ll = low + 1; + hh = high; + for (;;) { + do ll++; while (arr[low] > arr[ll]) ; + do hh--; while (arr[hh] > arr[low]) ; + + if (hh < ll) + break; + + ELEM_SWAP(arr[ll], arr[hh]) ; + } + + /* Swap middle item (in position low) back into correct position */ + ELEM_SWAP(arr[low], arr[hh]) ; + + /* Re-set active partition */ + if (hh <= median) low = ll; + if (hh >= median) high = hh - 1; + } +} + +#undef ELEM_SWAP + +/*--------------------------------------------------------------------------*/ + +#define ELEM_SWAP(a,b) { register int t=(a);(a)=(b);(b)=t; } + +static int quick_select_int(int arr[], int n) +{ + int low, high ; + int median; + int middle, ll, hh; + + low = 0 ; high = n-1 ; median = (low + high) / 2; + for (;;) { + if (high <= low) /* One element only */ + return arr[median] ; + + if (high == low + 1) { /* Two elements only */ + if (arr[low] > arr[high]) + ELEM_SWAP(arr[low], arr[high]) ; + return arr[median] ; + } + + /* Find median of low, middle and high items; swap into position low */ + middle = (low + high) / 2; + if (arr[middle] > arr[high]) ELEM_SWAP(arr[middle], arr[high]) ; + if (arr[low] > arr[high]) ELEM_SWAP(arr[low], arr[high]) ; + if (arr[middle] > arr[low]) ELEM_SWAP(arr[middle], arr[low]) ; + + /* Swap low item (now in position middle) into position (low+1) */ + ELEM_SWAP(arr[middle], arr[low+1]) ; + + /* Nibble from each end towards middle, swapping items when stuck */ + ll = low + 1; + hh = high; + for (;;) { + do ll++; while (arr[low] > arr[ll]) ; + do hh--; while (arr[hh] > arr[low]) ; + + if (hh < ll) + break; + + ELEM_SWAP(arr[ll], arr[hh]) ; + } + + /* Swap middle item (in position low) back into correct position */ + ELEM_SWAP(arr[low], arr[hh]) ; + + /* Re-set active partition */ + if (hh <= median) low = ll; + if (hh >= median) high = hh - 1; + } +} + +#undef ELEM_SWAP + +/*--------------------------------------------------------------------------*/ + +#define ELEM_SWAP(a,b) { register LONGLONG t=(a);(a)=(b);(b)=t; } + +static LONGLONG quick_select_longlong(LONGLONG arr[], int n) +{ + int low, high ; + int median; + int middle, ll, hh; + + low = 0 ; high = n-1 ; median = (low + high) / 2; + for (;;) { + if (high <= low) /* One element only */ + return arr[median] ; + + if (high == low + 1) { /* Two elements only */ + if (arr[low] > arr[high]) + ELEM_SWAP(arr[low], arr[high]) ; + return arr[median] ; + } + + /* Find median of low, middle and high items; swap into position low */ + middle = (low + high) / 2; + if (arr[middle] > arr[high]) ELEM_SWAP(arr[middle], arr[high]) ; + if (arr[low] > arr[high]) ELEM_SWAP(arr[low], arr[high]) ; + if (arr[middle] > arr[low]) ELEM_SWAP(arr[middle], arr[low]) ; + + /* Swap low item (now in position middle) into position (low+1) */ + ELEM_SWAP(arr[middle], arr[low+1]) ; + + /* Nibble from each end towards middle, swapping items when stuck */ + ll = low + 1; + hh = high; + for (;;) { + do ll++; while (arr[low] > arr[ll]) ; + do hh--; while (arr[hh] > arr[low]) ; + + if (hh < ll) + break; + + ELEM_SWAP(arr[ll], arr[hh]) ; + } + + /* Swap middle item (in position low) back into correct position */ + ELEM_SWAP(arr[low], arr[hh]) ; + + /* Re-set active partition */ + if (hh <= median) low = ll; + if (hh >= median) high = hh - 1; + } +} + +#undef ELEM_SWAP + +/*--------------------------------------------------------------------------*/ + +#define ELEM_SWAP(a,b) { register double t=(a);(a)=(b);(b)=t; } + +static double quick_select_double(double arr[], int n) +{ + int low, high ; + int median; + int middle, ll, hh; + + low = 0 ; high = n-1 ; median = (low + high) / 2; + for (;;) { + if (high <= low) /* One element only */ + return arr[median] ; + + if (high == low + 1) { /* Two elements only */ + if (arr[low] > arr[high]) + ELEM_SWAP(arr[low], arr[high]) ; + return arr[median] ; + } + + /* Find median of low, middle and high items; swap into position low */ + middle = (low + high) / 2; + if (arr[middle] > arr[high]) ELEM_SWAP(arr[middle], arr[high]) ; + if (arr[low] > arr[high]) ELEM_SWAP(arr[low], arr[high]) ; + if (arr[middle] > arr[low]) ELEM_SWAP(arr[middle], arr[low]) ; + + /* Swap low item (now in position middle) into position (low+1) */ + ELEM_SWAP(arr[middle], arr[low+1]) ; + + /* Nibble from each end towards middle, swapping items when stuck */ + ll = low + 1; + hh = high; + for (;;) { + do ll++; while (arr[low] > arr[ll]) ; + do hh--; while (arr[hh] > arr[low]) ; + + if (hh < ll) + break; + + ELEM_SWAP(arr[ll], arr[hh]) ; + } + + /* Swap middle item (in position low) back into correct position */ + ELEM_SWAP(arr[low], arr[hh]) ; + + /* Re-set active partition */ + if (hh <= median) low = ll; + if (hh >= median) high = hh - 1; + } +} + +#undef ELEM_SWAP + + diff --git a/astropy/io/fits/src/ricecomp.c b/cextern/cfitsio/lib/ricecomp.c similarity index 76% rename from astropy/io/fits/src/ricecomp.c rename to cextern/cfitsio/lib/ricecomp.c index 34d72f73ad03..e2bc2c65358d 100644 --- a/astropy/io/fits/src/ricecomp.c +++ b/cextern/cfitsio/lib/ricecomp.c @@ -1,80 +1,16 @@ -/* $Id$ -*/ - -/*****************************************************************************/ -/* */ -/* This file, ricecomp.c, contains the code required to compress and */ -/* uncompress data using the RICE_1 compression format. */ -/* */ -/* Copyright (C) 2004 Association of Universities for Research in Astronomy */ -/* (AURA) */ -/* */ -/* Redistribution and use in source and binary forms, with or without */ -/* modification, are permitted provided that the following conditions are */ -/* met: */ -/* */ -/* 1. Redistributions of source code must retain the above copyright */ -/* notice, this list of conditions and the following disclaimer. */ -/* */ -/* 2. Redistributions in binary form must reproduce the above */ -/* copyright notice, this list of conditions and the following */ -/* disclaimer in the documentation and/or other materials provided */ -/* with the distribution. */ -/* */ -/* 3. The name of AURA and its representatives may not be used to */ -/* endorse or promote products derived from this software without */ -/* specific prior written permission. */ -/* */ -/* THIS SOFTWARE IS PROVIDED BY AURA ``AS IS'' AND ANY EXPRESS OR IMPLIED */ -/* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF */ -/* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE */ -/* DISCLAIMED. IN NO EVENT SHALL AURA BE LIABLE FOR ANY DIRECT, INDIRECT, */ -/* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, */ -/* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS */ -/* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND */ -/* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR */ -/* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE */ -/* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH */ -/* DAMAGE. */ -/* */ -/* This file was copied intact from the FITSIO software that was written by */ -/* William Pence at the High Energy Astrophysic Science Archive Research */ -/* Center (HEASARC) at the NASA Goddard Space Flight Center. That software */ -/* contained the following copyright and warranty notices: */ -/* */ -/* Copyright (Unpublished--all rights reserved under the copyright laws of */ -/* the United States), U.S. Government as represented by the Administrator */ -/* of the National Aeronautics and Space Administration. No copyright is */ -/* claimed in the United States under Title 17, U.S. Code. */ -/* */ -/* Permission to freely use, copy, modify, and distribute this software */ -/* and its documentation without fee is hereby granted, provided that this */ -/* copyright notice and disclaimer of warranty appears in all copies. */ -/* */ -/* DISCLAIMER: */ -/* */ -/* THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, */ -/* EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, */ -/* ANY WARRANTY THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY */ -/* IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR */ -/* PURPOSE, AND FREEDOM FROM INFRINGEMENT, AND ANY WARRANTY THAT THE */ -/* DOCUMENTATION WILL CONFORM TO THE SOFTWARE, OR ANY WARRANTY THAT THE */ -/* SOFTWARE WILL BE ERROR FREE. IN NO EVENT SHALL NASA BE LIABLE FOR ANY */ -/* DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT, INDIRECT, SPECIAL OR */ -/* CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM, OR IN ANY WAY */ -/* CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY, */ -/* CONTRACT, TORT , OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY */ -/* PERSONS OR PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED */ -/* FROM, OR AROSE OUT OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR */ -/* SERVICES PROVIDED HEREUNDER." */ -/* */ -/*****************************************************************************/ - /* The following code was written by Richard White at STScI and made available for use in CFITSIO in July 1999. These routines were originally contained in 2 source files: rcomp.c and rdecomp.c, and the 'include' file now called ricecomp.h was originally called buffer.h. + + Note that beginning with CFITSIO v3.08, EOB checking was removed to improve + speed, and so now the input compressed bytes buffers must have been + allocated big enough so that they will never be overflowed. A simple + rule of thumb that guarantees the buffer will be large enough is to make + it 1% larger than the size of the input array of pixels that are being + compressed. + */ /*----------------------------------------------------------*/ @@ -95,29 +31,65 @@ #include #include +/* + * nonzero_count is lookup table giving number of bits in 8-bit values not including + * leading zeros used in fits_rdecomp, fits_rdecomp_short and fits_rdecomp_byte + */ +static const int nonzero_count[256] = { +0, +1, +2, 2, +3, 3, 3, 3, +4, 4, 4, 4, 4, 4, 4, 4, +5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, +6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, +6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, +7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, +7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, +7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, +7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, +8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, +8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, +8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, +8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, +8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, +8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, +8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, +8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}; + typedef unsigned char Buffer_t; typedef struct { - int bitbuffer; /* bit buffer */ - int bits_to_go; /* bits to go in buffer */ - Buffer_t *start; /* start of buffer */ + int bitbuffer; /* bit buffer */ + int bits_to_go; /* bits to go in buffer */ + Buffer_t *start; /* start of buffer */ Buffer_t *current; /* current position in buffer */ - Buffer_t *end; /* end of buffer */ + Buffer_t *end; /* end of buffer */ } Buffer; #define putcbuf(c,mf) ((*(mf->current)++ = c), 0) -#include "fitsio.h" +#include "fitsio2.h" static void start_outputing_bits(Buffer *buffer); static int done_outputing_bits(Buffer *buffer); static int output_nbits(Buffer *buffer, int bits, int n); +/* only used for diagnoistics +static int case1, case2, case3; +int fits_get_case(int *c1, int*c2, int*c3) { + + *c1 = case1; + *c2 = case2; + *c3 = case3; + return(0); +} +*/ + /* this routine used to be called 'rcomp' (WDP) */ /*---------------------------------------------------------------------------*/ -int _astropy_fits_rcomp( - int a[], /* input array */ +int fits_rcomp(int a[], /* input array */ int nx, /* number of input pixels */ unsigned char *c, /* output buffer */ int clen, /* max length of output */ @@ -172,6 +144,7 @@ unsigned int *diff; /* move out of switch block, to tweak performance */ fsbits = 5; fsmax = 25; + bbits = 1<current > buffer->end) { - _astropy_ffpmsg("rice_encode: end of buffer"); + ffpmsg("rice_encode: end of buffer"); free(diff); return(-1); } @@ -335,7 +308,7 @@ unsigned int *diff; } /*---------------------------------------------------------------------------*/ -int _astropy_fits_rcomp_short( +int fits_rcomp_short( short a[], /* input array */ int nx, /* number of input pixels */ unsigned char *c, /* output buffer */ @@ -414,7 +387,7 @@ unsigned int *diff; */ diff = (unsigned int *) malloc(nblock*sizeof(unsigned int)); if (diff == (unsigned int *) NULL) { - _astropy_ffpmsg("_astropy_fits_rcomp: insufficient memory"); + ffpmsg("fits_rcomp: insufficient memory"); return(-1); } /* @@ -424,7 +397,7 @@ unsigned int *diff; /* write out first short value to the first 2 bytes of the buffer */ if (output_nbits(buffer, a[0], 16) == EOF) { - _astropy_ffpmsg("rice_encode: end of buffer"); + ffpmsg("rice_encode: end of buffer"); free(diff); return(-1); } @@ -468,36 +441,39 @@ unsigned int *diff; * fsbits ID bits used to indicate split level */ if (fs >= fsmax) { +/* case3++; */ /* Special high entropy case when FS >= fsmax * Just write pixel difference values directly, no Rice coding at all. */ if (output_nbits(buffer, fsmax+1, fsbits) == EOF) { - _astropy_ffpmsg("rice_encode: end of buffer"); + ffpmsg("rice_encode: end of buffer"); free(diff); return(-1); } for (j=0; jcurrent > buffer->end) { - _astropy_ffpmsg("rice_encode: end of buffer"); + ffpmsg("rice_encode: end of buffer"); free(diff); return(-1); } @@ -561,7 +537,7 @@ unsigned int *diff; } /*---------------------------------------------------------------------------*/ -int _astropy_fits_rcomp_byte( +int fits_rcomp_byte( signed char a[], /* input array */ int nx, /* number of input pixels */ unsigned char *c, /* output buffer */ @@ -639,7 +615,7 @@ unsigned int *diff; */ diff = (unsigned int *) malloc(nblock*sizeof(unsigned int)); if (diff == (unsigned int *) NULL) { - _astropy_ffpmsg("_astropy_fits_rcomp: insufficient memory"); + ffpmsg("fits_rcomp: insufficient memory"); return(-1); } /* @@ -649,7 +625,7 @@ unsigned int *diff; /* write out first byte value to the first byte of the buffer */ if (output_nbits(buffer, a[0], 8) == EOF) { - _astropy_ffpmsg("rice_encode: end of buffer"); + ffpmsg("rice_encode: end of buffer"); free(diff); return(-1); } @@ -697,13 +673,13 @@ unsigned int *diff; * Just write pixel difference values directly, no Rice coding at all. */ if (output_nbits(buffer, fsmax+1, fsbits) == EOF) { - _astropy_ffpmsg("rice_encode: end of buffer"); + ffpmsg("rice_encode: end of buffer"); free(diff); return(-1); } for (j=0; jcurrent > buffer->end) { - _astropy_ffpmsg("rice_encode: end of buffer"); + ffpmsg("rice_encode: end of buffer"); free(diff); return(-1); } @@ -889,8 +865,7 @@ static int done_outputing_bits(Buffer *buffer) /*---------------------------------------------------------------------------*/ /* this routine used to be called 'rdecomp' (WDP) */ -int _astropy_fits_rdecomp ( - unsigned char *c, /* input buffer */ +int fits_rdecomp (unsigned char *c, /* input buffer */ int clen, /* length of input */ unsigned int array[], /* output array */ int nx, /* number of output pixels */ @@ -902,7 +877,7 @@ int nbits, nzero, fs; unsigned char *cend, bytevalue; unsigned int b, diff, lastpix; int fsmax, fsbits, bbits; -static int *nonzero_count = (int *)NULL; +extern const int nonzero_count[]; /* * Original size of each pixel (bsize, bytes) and coding block @@ -946,26 +921,6 @@ static int *nonzero_count = (int *)NULL; bbits = 1<=0; ) { - for ( ; i>=k; i--) nonzero_count[i] = nzero; - k = k/2; - nzero--; - } - } /* * Decode in blocks of nblock pixels */ @@ -973,6 +928,11 @@ static int *nonzero_count = (int *)NULL; /* first 4 bytes of input buffer contain the value of the first */ /* 4 byte integer value, without any encoding */ + if (clen < 4) + { + ffpmsg("decompression error: input buffer not properly allocated"); + return 1; + } lastpix = 0; bytevalue = c[0]; lastpix = lastpix | (bytevalue<<24); @@ -1066,20 +1026,19 @@ static int *nonzero_count = (int *)NULL; } } if (c > cend) { - _astropy_ffpmsg("decompression error: hit end of compressed byte stream"); + ffpmsg("decompression error: hit end of compressed byte stream"); return 1; } } if (c < cend) { - _astropy_ffpmsg("decompression warning: unused bytes at end of compressed buffer"); + ffpmsg("decompression warning: unused bytes at end of compressed buffer"); } return 0; } /*---------------------------------------------------------------------------*/ /* this routine used to be called 'rdecomp' (WDP) */ -int _astropy_fits_rdecomp_short ( - unsigned char *c, /* input buffer */ +int fits_rdecomp_short (unsigned char *c, /* input buffer */ int clen, /* length of input */ unsigned short array[], /* output array */ int nx, /* number of output pixels */ @@ -1092,7 +1051,7 @@ int nbits, nzero, fs; unsigned char *cend, bytevalue; unsigned int b, diff, lastpix; int fsmax, fsbits, bbits; -static int *nonzero_count = (int *)NULL; +extern const int nonzero_count[]; /* * Original size of each pixel (bsize, bytes) and coding block @@ -1137,26 +1096,6 @@ static int *nonzero_count = (int *)NULL; bbits = 1<=0; ) { - for ( ; i>=k; i--) nonzero_count[i] = nzero; - k = k/2; - nzero--; - } - } /* * Decode in blocks of nblock pixels */ @@ -1254,20 +1193,19 @@ static int *nonzero_count = (int *)NULL; } } if (c > cend) { - _astropy_ffpmsg("decompression error: hit end of compressed byte stream"); + ffpmsg("decompression error: hit end of compressed byte stream"); return 1; } } if (c < cend) { - _astropy_ffpmsg("decompression warning: unused bytes at end of compressed buffer"); + ffpmsg("decompression warning: unused bytes at end of compressed buffer"); } return 0; } /*---------------------------------------------------------------------------*/ /* this routine used to be called 'rdecomp' (WDP) */ -int _astropy_fits_rdecomp_byte ( - unsigned char *c, /* input buffer */ +int fits_rdecomp_byte (unsigned char *c, /* input buffer */ int clen, /* length of input */ unsigned char array[], /* output array */ int nx, /* number of output pixels */ @@ -1280,7 +1218,7 @@ int nbits, nzero, fs; unsigned char *cend; unsigned int b, diff, lastpix; int fsmax, fsbits, bbits; -static int *nonzero_count = (int *)NULL; +extern const int nonzero_count[]; /* * Original size of each pixel (bsize, bytes) and coding block @@ -1325,26 +1263,6 @@ static int *nonzero_count = (int *)NULL; bbits = 1<=0; ) { - for ( ; i>=k; i--) nonzero_count[i] = nzero; - k = k/2; - nzero--; - } - } /* * Decode in blocks of nblock pixels */ @@ -1437,12 +1355,12 @@ static int *nonzero_count = (int *)NULL; } } if (c > cend) { - _astropy_ffpmsg("decompression error: hit end of compressed byte stream"); + ffpmsg("decompression error: hit end of compressed byte stream"); return 1; } } if (c < cend) { - _astropy_ffpmsg("decompression warning: unused bytes at end of compressed buffer"); + ffpmsg("decompression warning: unused bytes at end of compressed buffer"); } return 0; } diff --git a/cextern/cfitsio/licenses/License.txt b/cextern/cfitsio/licenses/License.txt new file mode 100644 index 000000000000..2f5f48d3d2eb --- /dev/null +++ b/cextern/cfitsio/licenses/License.txt @@ -0,0 +1,25 @@ +Copyright (Unpublished--all rights reserved under the copyright laws of +the United States), U.S. Government as represented by the Administrator +of the National Aeronautics and Space Administration. No copyright is +claimed in the United States under Title 17, U.S. Code. + +Permission to freely use, copy, modify, and distribute this software +and its documentation without fee is hereby granted, provided that this +copyright notice and disclaimer of warranty appears in all copies. + +DISCLAIMER: + +THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, +EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, +ANY WARRANTY THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY +IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE, AND FREEDOM FROM INFRINGEMENT, AND ANY WARRANTY THAT THE +DOCUMENTATION WILL CONFORM TO THE SOFTWARE, OR ANY WARRANTY THAT THE +SOFTWARE WILL BE ERROR FREE. IN NO EVENT SHALL NASA BE LIABLE FOR ANY +DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT, INDIRECT, SPECIAL OR +CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM, OR IN ANY WAY +CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY, +CONTRACT, TORT , OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY +PERSONS OR PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED +FROM, OR AROSE OUT OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR +SERVICES PROVIDED HEREUNDER. diff --git a/cextern/expat/AUTHORS b/cextern/expat/AUTHORS new file mode 100644 index 000000000000..99475bb1b2ef --- /dev/null +++ b/cextern/expat/AUTHORS @@ -0,0 +1,10 @@ +Expat is brought to you by: + +Clark Cooper +Fred L. Drake, Jr. +Greg Stein +James Clark +Karl Waclawek +Rhodri James +Sebastian Pipping +Steven Solie diff --git a/cextern/expat/COPYING b/cextern/expat/COPYING old mode 100755 new mode 100644 index dcb450642968..c6d184a8aae8 --- a/cextern/expat/COPYING +++ b/cextern/expat/COPYING @@ -1,6 +1,5 @@ -Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd - and Clark Cooper -Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006 Expat maintainers. +Copyright (c) 1998-2000 Thai Open Source Software Center Ltd and Clark Cooper +Copyright (c) 2001-2025 Expat maintainers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/cextern/expat/Changes b/cextern/expat/Changes old mode 100755 new mode 100644 index 1e885ab75c97..01e54b676416 --- a/cextern/expat/Changes +++ b/cextern/expat/Changes @@ -1,5 +1,1639 @@ + __ __ _ + ___\ \/ /_ __ __ _| |_ + / _ \\ /| '_ \ / _` | __| + | __// \| |_) | (_| | |_ + \___/_/\_\ .__/ \__,_|\__| + |_| XML parser + +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +!! Expat is UNDERSTAFFED and WITHOUT FUNDING. !! +!! ~~~~~~~~~~~~ !! +!! The following topics need *additional skilled C developers* to progress !! +!! in a timely manner or at all (loosely ordered by descending priority): !! +!! !! +!! - teaming up on researching and fixing future security reports and !! +!! ClusterFuzz findings with few-days-max response times in communication !! +!! in order to (1) have a sound fix ready before the end of a 90 days !! +!! grace period and (2) in a sustainable manner, !! +!! - helping CPython Expat bindings with supporting Expat's amplification !! +!! attack protection API (https://github.com/python/cpython/issues/90949): !! +!! - XML_SetAllocTrackerActivationThreshold !! +!! - XML_SetAllocTrackerMaximumAmplification !! +!! - XML_SetBillionLaughsAttackProtectionActivationThreshold !! +!! - XML_SetBillionLaughsAttackProtectionMaximumAmplification !! +!! - helping Perl's XML::Parser Expat bindings with supporting Expat's !! +!! security API (https://github.com/cpan-authors/XML-Parser/issues/102): !! +!! - XML_SetAllocTrackerActivationThreshold !! +!! - XML_SetAllocTrackerMaximumAmplification !! +!! - XML_SetBillionLaughsAttackProtectionActivationThreshold !! +!! - XML_SetBillionLaughsAttackProtectionMaximumAmplification !! +!! - XML_SetReparseDeferralEnabled !! +!! - implementing and auto-testing XML 1.0r5 support !! +!! (needs discussion before pull requests), !! +!! - smart ideas on fixing the Autotools CMake files generation issue !! +!! without breaking CI (needs discussion before pull requests), !! +!! - pushing migration from `int` to `size_t` further !! +!! including edge-cases test coverage (needs discussion before anything). !! +!! !! +!! For details, please reach out via e-mail to sebastian@pipping.org so we !! +!! can schedule a voice call on the topic, in English or German. !! +!! !! +!! THANK YOU! Sebastian Pipping -- Berlin, 2024-03-09 !! +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +Release 2.7.3 Wed September 24 2025 + Security fixes: + #1046 #1048 Fix alignment of internal allocations for some non-amd64 + architectures (e.g. sparc32); fixes up on the fix to + CVE-2025-59375 from #1034 (of Expat 2.7.2 and related + backports) + #1059 Fix a class of false positives where input should have been + rejected with error XML_ERROR_ASYNC_ENTITY; regression from + CVE-2024-8176 fix pull request #973 (of Expat 2.7.0 and + related backports). Please check the added unit tests for + example documents. + + Other changes: + #1043 Prove and regression-proof absence of integer overflow + from function expat_realloc + #1062 Remove "harmless" cast that truncated a size_t to unsigned + #1049 Autotools: Remove "ln -s" discovery + #1054 docs: Be consistent with use of floating point around + XML_SetAllocTrackerMaximumAmplification + #1056 docs: Make it explicit that XML_GetCurrentColumnNumber + starts at 0 + #1057 docs: Better integrate the effect of the activation + thresholds + #1058 docs: Fix an in-comment typo in expat.h + #1045 docs: Fix a typo in README.md + #1041 docs: Improve change log of release 2.7.2 + #1053 xmlwf: Resolve use of functions XML_GetErrorLineNumber + and XML_GetErrorColumnNumber + #1032 Windows: Normalize .bat files to CRLF line endings + #1060 #1061 Version info bumped from 12:0:11 (libexpat*.so.1.11.0) + to 12:1:11 (libexpat*.so.1.11.1); see https://verbump.de/ + for what these numbers do + + Infrastructure: + #1047 #1050 CI: Cleanup UndefinedBehaviorSanitizer fatality + #1044 CI|Linux: Stop aborting at first job failure + #1052 CI|FreeBSD: Upgrade to FreeBSD 15.0 + #1039 CI|FreeBSD: Do not install CMake meta-package + + Special thanks to: + BÊnÊdikt Tran + Berkay Eren ÜrÃŧn + Daniel Engberg + Hanno BÃļck + Matthew Fernandez + Rolf Eike Beer + Sam James + Tim Bray + and + Clang/GCC UndefinedBehaviorSanitizer + OSS-Fuzz / ClusterFuzz + Z3 Theorem Prover + +Release 2.7.2 Tue September 16 2025 + Security fixes: + #1018 #1034 CVE-2025-59375 -- Disallow use of disproportional amounts of + dynamic memory from within an Expat parser (e.g. previously + a ~250 KiB sized document was able to cause allocation of + ~800 MiB from the heap, i.e. an "amplification" of factor + ~3,300); once a threshold (that defaults to 64 MiB) is + reached, a maximum amplification factor (that defaults to + 100.0) is enforced, and violating documents are rejected + with an out-of-memory error. + There are two new API functions to fine-tune this new + behavior: + - XML_SetAllocTrackerActivationThreshold + - XML_SetAllocTrackerMaximumAmplification . + If you ever need to increase these defaults for non-attack + XML payload, please file a bug report with libexpat. + There is also a new environment variable + EXPAT_MALLOC_DEBUG=(0|1|2) to control the verbosity + of allocations debugging at runtime, disabled by default. + Known impact is (reliable and easy) denial of service: + CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H/E:H/RL:O/RC:C + (Base Score: 7.5, Temporal Score: 7.2) + Please note that a layer of compression around XML can + significantly reduce the minimum attack payload size. + Distributors intending to backport (or cherry-pick) the + fix need to copy 99% of the related pull request, not just + the "lib: Implement tracking of dynamic memory allocations" + commit, to not end up with a state that literally does both + too much and too little at the same time. Appending ".diff" + to the pull request URL could be of help. + + Other changes: + #1008 #1017 Autotools|macOS: Sync CMake templates with CMake 3.31 + #1007 CMake: Drop support for CMake <3.15 + #1004 CMake: Fix off_t detection for -Werror + #1007 CMake|Windows: Fix -DEXPAT_MSVC_STATIC_CRT=ON + #1013 Windows: Drop support for Visual Studio <=16.0/2019 + #1026 xmlwf: Mention supported environment variables in + --help output + #1024 xmlwf: Fix (internal) help generator + #1034 docs: Promote the contract to call function + XML_FreeContentModel when registering a custom + element declaration handler (via a call to function + XML_SetElementDeclHandler) + #1027 docs: Add missing

..

wrap + #994 docs: Drop AppVeyor badge + #1000 tests: Fix portable_strndup + #1036 Drop casts around malloc/free/realloc that C99 does not need + #1010 Replace empty for loops with while loops + #1011 Add const with internal XmlInitUnknownEncodingNS + #14 #1037 Drop an OpenVMS support leftover + #999 #1001 Address more clang-tidy warnings + #1030 #1038 Version info bumped from 11:2:10 (libexpat*.so.1.10.2) + to 12:0:11 (libexpat*.so.1.11.0); see https://verbump.de/ + for what these numbers do + + Infrastructure: + #1003 CI: Cover compilation on FreeBSD + #1009 #1035 CI: Upgrade Clang from 19 to 21 + #1031 CI: Make calling Cppcheck without --suppress=objectIndex + and --suppress=unknownMacro possible + #1013 CI|Windows: Get off of deprecated image "windows-2019" + #1008 #1017 .. + #1023 #1025 CI: Adapt to breaking changes in GitHub Actions + + Special thanks to: + Alexander Bluhm + Neil Pang + Theo Buehler + and + GNU Time + OSS-Fuzz / ClusterFuzz + Perl XML::Parser + +Release 2.7.1 Thu March 27 2025 + Bug fixes: + #980 #989 Restore event pointer behavior from Expat 2.6.4 + (that the fix to CVE-2024-8176 changed in 2.7.0); + affected API functions are: + - XML_GetCurrentByteCount + - XML_GetCurrentByteIndex + - XML_GetCurrentColumnNumber + - XML_GetCurrentLineNumber + - XML_GetInputContext + + Other changes: + #976 #977 Autotools: Integrate files "fuzz/xml_lpm_fuzzer.{cpp,proto}" + with Automake that were missing from 2.7.0 release tarballs + #983 #984 Fix printf format specifiers for 32bit Emscripten + #992 docs: Promote OpenSSF Best Practices self-certification + #978 tests/benchmark: Resolve mistaken double close + #986 Address Frama-C warnings + #990 #993 Version info bumped from 11:1:10 (libexpat*.so.1.10.1) + to 11:2:10 (libexpat*.so.1.10.2); see https://verbump.de/ + for what these numbers do + + Infrastructure: + #982 CI: Start running Perl XML::Parser integration tests + #987 CI: Enforce Clang Static Analyzer clean code + #991 CI: Re-enable warning clang-analyzer-valist.Uninitialized + for clang-tidy + #981 CI: Cover compilation with musl + #983 #984 CI: Cover compilation with 32bit Emscripten + #976 #977 CI: Protect against fuzzer files missing from future + release archives + + Special thanks to: + Berkay Eren ÜrÃŧn + Matthew Fernandez + and + Perl XML::Parser + +Release 2.7.0 Thu March 13 2025 + Security fixes: + #893 #973 CVE-2024-8176 -- Fix crash from chaining a large number + of entities caused by stack overflow by resolving use of + recursion, for all three uses of entities: + - general entities in character data ("&g1;") + - general entities in attribute values ("") + - parameter entities ("%p1;") + Known impact is (reliable and easy) denial of service: + CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H/E:H/RL:O/RC:C + (Base Score: 7.5, Temporal Score: 7.2) + Please note that a layer of compression around XML can + significantly reduce the minimum attack payload size. + + Other changes: + #935 #937 Autotools: Make generated CMake files look for + libexpat.@SO_MAJOR@.dylib on macOS + #925 Autotools: Sync CMake templates with CMake 3.29 + #945 #962 #966 CMake: Drop support for CMake <3.13 + #942 CMake: Small fuzzing related improvements + #921 docs: Add missing documentation of error code + XML_ERROR_NOT_STARTED that was introduced with 2.6.4 + #941 docs: Document need for C++11 compiler for use from C++ + #959 tests/benchmark: Fix a (harmless) TOCTTOU + #944 Windows: Fix installer target location of file xmlwf.xml + for CMake + #953 Windows: Address warning -Wunknown-warning-option + about -Wno-pedantic-ms-format from LLVM MinGW + #971 Address Cppcheck warnings + #969 #970 Mass-migrate links from http:// to https:// + #947 #958 .. + #974 #975 Document changes since the previous release + #974 #975 Version info bumped from 11:0:10 (libexpat*.so.1.10.0) + to 11:1:10 (libexpat*.so.1.10.1); see https://verbump.de/ + for what these numbers do + + Infrastructure: + #926 tests: Increase robustness + #927 #932 .. + #930 #933 tests: Increase test coverage + #617 #950 .. + #951 #952 .. + #954 #955 .. Fuzzing: Add new fuzzer "xml_lpm_fuzzer" based on + #961 Google's libprotobuf-mutator ("LPM") + #957 Fuzzing|CI: Start producing fuzzing code coverage reports + #936 CI: Pass -q -q for LCOV >=2.1 in coverage.sh + #942 CI: Small fuzzing related improvements + #139 #203 .. + #791 #946 CI: Make GitHub Actions build using MSVC on Windows and + produce 32bit and 64bit Windows binaries + #956 CI: Get off of about-to-be-removed Ubuntu 20.04 + #960 #964 CI: Start uploading to Coverity Scan for static analysis + #972 CI: Stop loading DTD from the internet to address flaky CI + #971 CI: Adapt to breaking changes in Cppcheck + + Special thanks to: + Alexander Gieringer + Berkay Eren ÜrÃŧn + Hanno BÃļck + Jann Horn + Mark Brand + Sebastian Andrzej Siewior + Snild Dolkow + Thomas PrÃļll + Tomas Korbar + valord577 + and + Google Project Zero + Linutronix + Red Hat + Siemens + +Release 2.6.4 Wed November 6 2024 + Security fixes: + #915 CVE-2024-50602 -- Fix crash within function XML_ResumeParser + from a NULL pointer dereference by disallowing function + XML_StopParser to (stop or) suspend an unstarted parser. + A new error code XML_ERROR_NOT_STARTED was introduced to + properly communicate this situation. // CWE-476 CWE-754 + + Other changes: + #903 CMake: Add alias target "expat::expat" + #905 docs: Document use via CMake >=3.18 with FetchContent + and SOURCE_SUBDIR and its consequences + #902 tests: Reduce use of global parser instance + #904 tests: Resolve duplicate handler + #317 #918 tests: Improve tests on doctype closing (ex CVE-2019-15903) + #914 Fix signedness of format strings + #915 For use from C++, expat.h started requiring C++11 due to + use of C99 features + #919 #920 Version info bumped from 10:3:9 (libexpat*.so.1.9.3) + to 11:0:10 (libexpat*.so.1.10.0); see https://verbump.de/ + for what these numbers do + + Infrastructure: + #907 CI: Upgrade Clang from 18 to 19 + #913 CI: Drop macos-12 and add macos-15 + #910 CI: Adapt to breaking changes in GitHub Actions + #898 Add missing entries to .gitignore + + Special thanks to: + Hanno BÃļck + JosÊ Eduardo GutiÊrrez Conejo + JosÊ Ricardo Cardona Quesada + +Release 2.6.3 Wed September 4 2024 + Security fixes: + #887 #890 CVE-2024-45490 -- Calling function XML_ParseBuffer with + len < 0 without noticing and then calling XML_GetBuffer + will have XML_ParseBuffer fail to recognize the problem + and XML_GetBuffer corrupt memory. + With the fix, XML_ParseBuffer now complains with error + XML_ERROR_INVALID_ARGUMENT just like sibling XML_Parse + has been doing since Expat 2.2.1, and now documented. + Impact is denial of service to potentially artitrary code + execution. + #888 #891 CVE-2024-45491 -- Internal function dtdCopy can have an + integer overflow for nDefaultAtts on 32-bit platforms + (where UINT_MAX equals SIZE_MAX). + Impact is denial of service to potentially artitrary code + execution. + #889 #892 CVE-2024-45492 -- Internal function nextScaffoldPart can + have an integer overflow for m_groupSize on 32-bit + platforms (where UINT_MAX equals SIZE_MAX). + Impact is denial of service to potentially artitrary code + execution. + + Other changes: + #851 #879 Autotools: Sync CMake templates with CMake 3.28 + #853 Autotools: Always provide path to find(1) for portability + #861 Autotools: Ensure that the m4 directory always exists. + #870 Autotools: Simplify handling of SIZEOF_VOID_P + #869 Autotools: Support non-GNU sed + #856 Autotools|CMake: Fix main() to main(void) + #865 Autotools|CMake: Fix compile tests for HAVE_SYSCALL_GETRANDOM + #863 Autotools|CMake: Stop requiring dos2unix + #854 #855 CMake: Fix check for symbols size_t and off_t + #864 docs|tests: Convert README to Markdown and update + #741 Windows: Drop support for Visual Studio <=15.0/2017 + #886 Drop needless XML_DTD guards around is_param access + #885 Fix typo in a code comment + #894 #896 Version info bumped from 10:2:9 (libexpat*.so.1.9.2) + to 10:3:9 (libexpat*.so.1.9.3); see https://verbump.de/ + for what these numbers do + + Infrastructure: + #880 Readme: Promote the call for help + #868 CI: Fix various issues + #849 CI: Allow triggering GitHub Actions workflows manually + #851 #872 .. + #873 #879 CI: Adapt to breaking changes in GitHub Actions + + Special thanks to: + Alexander Bluhm + Berkay Eren ÜrÃŧn + Dag-Erling Smørgrav + Ferenc GÊczi + TaiYou + +Release 2.6.2 Wed March 13 2024 + Security fixes: + #839 #842 CVE-2024-28757 -- Prevent billion laughs attacks with + isolated use of external parsers. Please see the commit + message of commit 1d50b80cf31de87750103656f6eb693746854aa8 + for details. + + Bug fixes: + #839 #841 Reject direct parameter entity recursion + and avoid the related undefined behavior + + Other changes: + #847 Autotools: Fix build for DOCBOOK_TO_MAN containing spaces + #837 Add missing #821 and #824 to 2.6.1 change log + #838 #843 Version info bumped from 10:1:9 (libexpat*.so.1.9.1) + to 10:2:9 (libexpat*.so.1.9.2); see https://verbump.de/ + for what these numbers do + + Special thanks to: + Philippe Antoine + Tomas Korbar + and + Clang UndefinedBehaviorSanitizer + OSS-Fuzz / ClusterFuzz + +Release 2.6.1 Thu February 29 2024 + Bug fixes: + #817 Make tests independent of CPU speed, and thus more robust + #828 #836 Expose billion laughs API with XML_DTD defined and + XML_GE undefined, regression from 2.6.0 + + Other changes: + #829 Hide test-only code behind new internal macro + #833 Autotools: Reject expat_config.h.in defining SIZEOF_VOID_P + #821 #824 Autotools: Fix "make clean" for case: + ./configure --without-docbook && make clean all + #819 Address compiler warnings + #832 #834 Version info bumped from 10:0:9 (libexpat*.so.1.9.0) + to 10:1:9 (libexpat*.so.1.9.1); see https://verbump.de/ + for what these numbers do + + Infrastructure: + #818 CI: Adapt to breaking changes in clang-format + + Special thanks to: + David Hall + Snild Dolkow + +Release 2.6.0 Tue February 6 2024 + Security fixes: + #789 #814 CVE-2023-52425 -- Fix quadratic runtime issues with big tokens + that can cause denial of service, in partial where + dealing with compressed XML input. Applications + that parsed a document in one go -- a single call to + functions XML_Parse or XML_ParseBuffer -- were not affected. + The smaller the chunks/buffers you use for parsing + previously, the bigger the problem prior to the fix. + Backporters should be careful to no omit parts of + pull request #789 and to include earlier pull request #771, + in order to not break the fix. + #777 CVE-2023-52426 -- Fix billion laughs attacks for users + compiling *without* XML_DTD defined (which is not common). + Users with XML_DTD defined have been protected since + Expat >=2.4.0 (and that was CVE-2013-0340 back then). + + Bug fixes: + #753 Fix parse-size-dependent "invalid token" error for + external entities that start with a byte order mark + #780 Fix NULL pointer dereference in setContext via + XML_ExternalEntityParserCreate for compilation with + XML_DTD undefined + #812 #813 Protect against closing entities out of order + + Other changes: + #723 Improve support for arc4random/arc4random_buf + #771 #788 Improve buffer growth in XML_GetBuffer and XML_Parse + #761 #770 xmlwf: Support --help and --version + #759 #770 xmlwf: Support custom buffer size for XML_GetBuffer and read + #744 xmlwf: Improve language and URL clickability in help output + #673 examples: Add new example "element_declarations.c" + #764 Be stricter about macro XML_CONTEXT_BYTES at build time + #765 Make inclusion to expat_config.h consistent + #726 #727 Autotools: configure.ac: Support --disable-maintainer-mode + #678 #705 .. + #706 #733 #792 Autotools: Sync CMake templates with CMake 3.26 + #795 Autotools: Make installation of shipped man page doc/xmlwf.1 + independent of docbook2man availability + #815 Autotools|CMake: Add missing -DXML_STATIC to pkg-config file + section "Cflags.private" in order to fix compilation + against static libexpat using pkg-config on Windows + #724 #751 Autotools|CMake: Require a C99 compiler + (a de-facto requirement already since Expat 2.2.2 of 2017) + #793 Autotools|CMake: Fix PACKAGE_BUGREPORT variable + #750 #786 Autotools|CMake: Make test suite require a C++11 compiler + #749 CMake: Require CMake >=3.5.0 + #672 CMake: Lowercase off_t and size_t to help a bug in Meson + #746 CMake: Sort xmlwf sources alphabetically + #785 CMake|Windows: Fix generation of DLL file version info + #790 CMake: Build tests/benchmark/benchmark.c as well for + a build with -DEXPAT_BUILD_TESTS=ON + #745 #757 docs: Document the importance of isFinal + adjust tests + accordingly + #736 docs: Improve use of "NULL" and "null" + #713 docs: Be specific about version of XML (XML 1.0r4) + and version of C (C99); (XML 1.0r5 will need a sponsor.) + #762 docs: reference.html: Promote function XML_ParseBuffer more + #779 docs: reference.html: Add HTML anchors to XML_* macros + #760 docs: reference.html: Upgrade to OK.css 1.2.0 + #763 #739 docs: Fix typos + #696 docs|CI: Use HTTPS URLs instead of HTTP at various places + #669 #670 .. + #692 #703 .. + #733 #772 Address compiler warnings + #798 #800 Address clang-tidy warnings + #775 #776 Version info bumped from 9:10:8 (libexpat*.so.1.8.10) + to 10:0:9 (libexpat*.so.1.9.0); see https://verbump.de/ + for what these numbers do + + Infrastructure: + #700 #701 docs: Document security policy in file SECURITY.md + #766 docs: Improve parse buffer variables in-code documentation + #674 #738 .. + #740 #747 .. + #748 #781 #782 Refactor coverage and conformance tests + #714 #716 Refactor debug level variables to unsigned long + #671 Improve handling of empty environment variable value + in function getDebugLevel (without visible user effect) + #755 #774 .. + #758 #783 .. + #784 #787 tests: Improve test coverage with regard to parse chunk size + #660 #797 #801 Fuzzing: Improve fuzzing coverage + #367 #799 Fuzzing|CI: Start running OSS-Fuzz fuzzing regression tests + #698 #721 CI: Resolve some Travis CI leftovers + #669 CI: Be robust towards absence of Git tags + #693 #694 CI: Set permissions to "contents: read" for security + #709 CI: Pin all GitHub Actions to specific commits for security + #739 CI: Reject spelling errors using codespell + #798 CI: Enforce clang-tidy clean code + #773 #808 .. + #809 #810 CI: Upgrade Clang from 15 to 18 + #796 CI: Start using Clang's Control Flow Integrity sanitizer + #675 #720 #722 CI: Adapt to breaking changes in GitHub Actions Ubuntu images + #689 CI: Adapt to breaking changes in Clang/LLVM Debian packaging + #763 CI: Adapt to breaking changes in codespell + #803 CI: Adapt to breaking changes in Cppcheck + + Special thanks to: + Ivan Galkin + Joyce Brum + Philippe Antoine + Rhodri James + Snild Dolkow + spookyahell + Steven Garske + and + Clang AddressSanitizer + Clang UndefinedBehaviorSanitizer + codespell + GCC Farm Project + OSS-Fuzz + Sony Mobile + +Release 2.5.0 Tue October 25 2022 + Security fixes: + #616 #649 #650 CVE-2022-43680 -- Fix heap use-after-free after overeager + destruction of a shared DTD in function + XML_ExternalEntityParserCreate in out-of-memory situations. + Expected impact is denial of service or potentially + arbitrary code execution. + + Bug fixes: + #612 #645 Fix corruption from undefined entities + #613 #654 Fix case when parsing was suspended while processing nested + entities + #616 #652 #653 Stop leaking opening tag bindings after a closing tag + mismatch error where a parser is reset through + XML_ParserReset and then reused to parse + #656 CMake: Fix generation of pkg-config file + #658 MinGW|CMake: Fix static library name + + Other changes: + #663 Protect header expat_config.h from multiple inclusion + #666 examples: Make use of XML_GetBuffer and be more + consistent across examples + #648 Address compiler warnings + #667 #668 Version info bumped from 9:9:8 to 9:10:8; + see https://verbump.de/ for what these numbers do + + Special thanks to: + Jann Horn + Mark Brand + Osyotr + Rhodri James + and + Google Project Zero + +Release 2.4.9 Tue September 20 2022 + Security fixes: + #629 #640 CVE-2022-40674 -- Heap use-after-free vulnerability in + function doContent. Expected impact is denial of service + or potentially arbitrary code execution. + + Bug fixes: + #634 MinGW: Fix mis-compilation for -D__USE_MINGW_ANSI_STDIO=0 + #614 docs: Fix documentation on effect of switch XML_DTD on + symbol visibility in doc/reference.html + + Other changes: + #638 MinGW: Make fix-xmltest-log.sh drop more Wine bug output + #596 #625 Autotools: Sync CMake templates with CMake 3.22 + #608 CMake: Migrate from use of CMAKE_*_POSTFIX to + dedicated variables EXPAT_*_POSTFIX to stop affecting + other projects + #597 #599 Windows|CMake: Add missing -DXML_STATIC to test runners + and fuzzers + #512 #621 Windows|CMake: Render .def file from a template to fix + linking with -DEXPAT_DTD=OFF and/or -DEXPAT_ATTR_INFO=ON + #611 #621 MinGW|CMake: Apply MSVC .def file when linking + #622 #624 MinGW|CMake: Sync library name with GNU Autotools, + i.e. produce libexpat-1.dll rather than libexpat.dll + by default. Filename libexpat.dll.a is unaffected. + #632 MinGW|CMake: Set missing variable CMAKE_RC_COMPILER in + toolchain file "cmake/mingw-toolchain.cmake" to avoid + error "windres: Command not found" on e.g. Ubuntu 20.04 + #597 #627 CMake: Unify inconsistent use of set() and option() in + context of public build time options to take need for + set(.. FORCE) in projects using Expat by means of + add_subdirectory(..) off Expat's users' shoulders + #626 #641 Stop exporting API symbols when building a static library + #644 Resolve use of deprecated "fgrep" by "grep -F" + #620 CMake: Make documentation on variables a bit more consistent + #636 CMake: Drop leading whitespace from a #cmakedefine line in + file expat_config.h.cmake + #594 xmlwf: Fix harmless variable mix-up in function nsattcmp + #592 #593 #610 Address Cppcheck warnings + #643 Address Clang 15 compiler warnings + #642 #644 Version info bumped from 9:8:8 to 9:9:8; + see https://verbump.de/ for what these numbers do + + Infrastructure: + #597 #598 CI: Windows: Start covering MSVC 2022 + #619 CI: macOS: Migrate off deprecated macOS 10.15 + #632 CI: Linux: Make migration off deprecated Ubuntu 18.04 work + #643 CI: Upgrade Clang from 14 to 15 + #637 apply-clang-format.sh: Add support for BSD find + #633 coverage.sh: Exclude MinGW headers + #635 coverage.sh: Fix name collision for -funsigned-char + + Special thanks to: + David Faure + Felix Wilhelm + Frank Bergmann + Rhodri James + Rosen Penev + Thijs Schreijer + Vincent Torri + and + Google Project Zero + +Release 2.4.8 Mon March 28 2022 + Other changes: + #587 pkg-config: Move "-lm" to section "Libs.private" + #587 CMake|MSVC: Fix pkg-config section "Libs" + #55 #582 CMake|macOS: Start using linker arguments + "-compatibility_version " and + "-current_version " in a way compatible with + GNU Libtool + #590 #591 Version info bumped from 9:7:8 to 9:8:8; + see https://verbump.de/ for what these numbers do + + Infrastructure: + #589 CI: Upgrade Clang from 13 to 14 + + Special thanks to: + evpobr + Kai Pastor + Sam James + +Release 2.4.7 Fri March 4 2022 + Bug fixes: + #572 #577 Relax fix to CVE-2022-25236 (introduced with release 2.4.5) + with regard to all valid URI characters (RFC 3986), + i.e. the following set (excluding whitespace): + ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz + 0123456789 % -._~ :/?#[]@ !$&'()*+,;= + + Other changes: + #555 #570 #581 CMake|Windows: Store Expat version in the DLL + #577 Document consequences of namespace separator choices not just + in doc/reference.html but also in header + #577 Document Expat's lack of validation of namespace URIs against + RFC 3986, and that the XML 1.0r4 specification doesn't + require Expat to validate namespace URIs, and that Expat + may do more in that regard in future releases. + If you find need for strict RFC 3986 URI validation on + application level today, https://uriparser.github.io/ may + be of interest. + #579 Fix documentation of XML_EndDoctypeDeclHandler in + #575 Document that a call to XML_FreeContentModel can be done at + a later time from outside the element declaration handler + #574 Make hardcoded namespace URIs easier to find in code + #573 Update documentation on use of XML_POOR_ENTOPY on Solaris + #569 #571 tests: Resolve use of macros NAN and INFINITY for GNU G++ + 4.8.2 on Solaris. + #578 #580 Version info bumped from 9:6:8 to 9:7:8; + see https://verbump.de/ for what these numbers do + + Special thanks to: + Jeffrey Walton + Johnny Jazeix + Thijs Schreijer + +Release 2.4.6 Sun February 20 2022 + Bug fixes: + #566 Fix a regression introduced by the fix for CVE-2022-25313 + in release 2.4.5 that affects applications that (1) + call function XML_SetElementDeclHandler and (2) are + parsing XML that contains nested element declarations + (e.g. ""). + + Other changes: + #567 #568 Version info bumped from 9:5:8 to 9:6:8; + see https://verbump.de/ for what these numbers do + + Special thanks to: + Matt Sergeant + Samanta Navarro + Sergei Trofimovich + and + NixOS + Perl XML::Parser + +Release 2.4.5 Fri February 18 2022 + Security fixes: + #562 CVE-2022-25235 -- Passing malformed 2- and 3-byte UTF-8 + sequences (e.g. from start tag names) to the XML + processing application on top of Expat can cause + arbitrary damage (e.g. code execution) depending + on how invalid UTF-8 is handled inside the XML + processor; validation was not their job but Expat's. + Exploits with code execution are known to exist. + #561 CVE-2022-25236 -- Passing (one or more) namespace separator + characters in "xmlns[:prefix]" attribute values + made Expat send malformed tag names to the XML + processor on top of Expat which can cause + arbitrary damage (e.g. code execution) depending + on such unexpectable cases are handled inside the XML + processor; validation was not their job but Expat's. + Exploits with code execution are known to exist. + #558 CVE-2022-25313 -- Fix stack exhaustion in doctype parsing + that could be triggered by e.g. a 2 megabytes + file with a large number of opening braces. + Expected impact is denial of service or potentially + arbitrary code execution. + #560 CVE-2022-25314 -- Fix integer overflow in function copyString; + only affects the encoding name parameter at parser creation + time which is often hardcoded (rather than user input), + takes a value in the gigabytes to trigger, and a 64-bit + machine. Expected impact is denial of service. + #559 CVE-2022-25315 -- Fix integer overflow in function storeRawNames; + needs input in the gigabytes and a 64-bit machine. + Expected impact is denial of service or potentially + arbitrary code execution. + + Other changes: + #557 #564 Version info bumped from 9:4:8 to 9:5:8; + see https://verbump.de/ for what these numbers do + + Special thanks to: + Ivan Fratric + Samanta Navarro + and + Google Project Zero + JetBrains + +Release 2.4.4 Sun January 30 2022 + Security fixes: + #550 CVE-2022-23852 -- Fix signed integer overflow + (undefined behavior) in function XML_GetBuffer + (that is also called by function XML_Parse internally) + for when XML_CONTEXT_BYTES is defined to >0 (which is both + common and default). + Impact is denial of service or more. + #551 CVE-2022-23990 -- Fix unsigned integer overflow in function + doProlog triggered by large content in element type + declarations when there is an element declaration handler + present (from a prior call to XML_SetElementDeclHandler). + Impact is denial of service or more. + + Bug fixes: + #544 #545 xmlwf: Fix a memory leak on output file opening error + + Other changes: + #546 Autotools: Fix broken CMake support under Cygwin + #554 Windows: Add missing files to the installer to fix + compilation with CMake from installed sources + #552 #554 Version info bumped from 9:3:8 to 9:4:8; + see https://verbump.de/ for what these numbers do + + Special thanks to: + Carlo Bramini + hwt0415 + Roland Illig + Samanta Navarro + and + Clang LeakSan and the Clang team + +Release 2.4.3 Sun January 16 2022 + Security fixes: + #531 #534 CVE-2021-45960 -- Fix issues with left shifts by >=29 places + resulting in + a) realloc acting as free + b) realloc allocating too few bytes + c) undefined behavior + depending on architecture and precise value + for XML documents with >=2^27+1 prefixed attributes + on a single XML tag a la + "" + where XML_ParserCreateNS is used to create the parser + (which needs argument "-n" when running xmlwf). + Impact is denial of service, or more. + #532 #538 CVE-2021-46143 (ZDI-CAN-16157) -- Fix integer overflow + on variable m_groupSize in function doProlog leading + to realloc acting as free. + Impact is denial of service or more. + #539 CVE-2022-22822 to CVE-2022-22827 -- Prevent integer overflows + near memory allocation at multiple places. Mitre assigned + a dedicated CVE for each involved internal C function: + - CVE-2022-22822 for function addBinding + - CVE-2022-22823 for function build_model + - CVE-2022-22824 for function defineAttribute + - CVE-2022-22825 for function lookup + - CVE-2022-22826 for function nextScaffoldPart + - CVE-2022-22827 for function storeAtts + Impact is denial of service or more. + + Other changes: + #535 CMake: Make call to file(GENERATE [..]) work for CMake <3.19 + #541 Autotools|CMake: MinGW: Make run.sh(.in) work for Cygwin + and MSYS2 by not going through Wine on these platforms + #527 #528 Address compiler warnings + #533 #543 Version info bumped from 9:2:8 to 9:3:8; + see https://verbump.de/ for what these numbers do + + Infrastructure: + #536 CI: Check for realistic minimum CMake version + #529 #539 CI: Cover compilation with -m32 + #529 CI: Store coverage reports as artifacts for download + #528 CI: Upgrade Clang from 11 to 13 + + Special thanks to: + An anonymous whitehat + Christopher Degawa + J. Peter Mugaas + Tyson Smith + and + GCC Farm Project + Trend Micro Zero Day Initiative + +Release 2.4.2 Sun December 19 2021 + Other changes: + #509 #510 Link againgst libm for function "isnan" + #513 #514 Include expat_config.h as early as possible + #498 Autotools: Include files with release archives: + - buildconf.sh + - fuzz/*.c + #507 #519 Autotools: Sync CMake templates with CMake 3.20 + #495 #524 CMake: MinGW: Fix pkg-config section "Libs" for + - non-release build types (e.g. -DCMAKE_BUILD_TYPE=Debug) + - multi-config CMake generators (e.g. Ninja Multi-Config) + #502 #503 docs: Document that function XML_GetBuffer may return NULL + when asking for a buffer of 0 (zero) bytes size + #522 #523 docs: Fix return value docs for both + XML_SetBillionLaughsAttackProtection* functions + #525 #526 Version info bumped from 9:1:8 to 9:2:8; + see https://verbump.de/ for what these numbers do + + Special thanks to: + Donghee Na + Joergen Ibsen + Kai Pastor + +Release 2.4.1 Sun May 23 2021 + Bug fixes: + #488 #490 Autotools: Fix installed header expat_config.h for multilib + systems; regression introduced in 2.4.0 by pull request #486 + + Other changes: + #491 #492 Version info bumped from 9:0:8 to 9:1:8; + see https://verbump.de/ for what these numbers do + + Special thanks to: + Gentoo's QA check "multilib_check_headers" + +Release 2.4.0 Sun May 23 2021 + Security fixes: + #34 #466 #484 CVE-2013-0340/CWE-776 -- Protect against billion laughs attacks + (denial-of-service; flavors targeting CPU time or RAM or both, + leveraging general entities or parameter entities or both) + by tracking and limiting the input amplification factor + ( := ( + ) / ). + By conservative default, amplification up to a factor of 100.0 + is tolerated and rejection only starts after 8 MiB of output bytes + (= + ) have been processed. + The fix adds the following to the API: + - A new error code XML_ERROR_AMPLIFICATION_LIMIT_BREACH to + signals this specific condition. + - Two new API functions .. + - XML_SetBillionLaughsAttackProtectionMaximumAmplification and + - XML_SetBillionLaughsAttackProtectionActivationThreshold + .. to further tighten billion laughs protection parameters + when desired. Please see file "doc/reference.html" for details. + If you ever need to increase the defaults for non-attack XML + payload, please file a bug report with libexpat. + - Two new XML_FEATURE_* constants .. + - that can be queried using the XML_GetFeatureList function, and + - that are shown in "xmlwf -v" output. + - Two new environment variable switches .. + - EXPAT_ACCOUNTING_DEBUG=(0|1|2|3) and + - EXPAT_ENTITY_DEBUG=(0|1) + .. for runtime debugging of accounting and entity processing. + Specific behavior of these values may change in the future. + - Two new command line arguments "-a FACTOR" and "-b BYTES" + for xmlwf to further tighten billion laughs protection + parameters when desired. + If you ever need to increase the defaults for non-attack XML + payload, please file a bug report with libexpat. + + Bug fixes: + #332 #470 For (non-default) compilation with -DEXPAT_MIN_SIZE=ON (CMake) + or CPPFLAGS=-DXML_MIN_SIZE (GNU Autotools): Fix segfault + for UTF-16 payloads containing CDATA sections. + #485 #486 Autotools: Fix generated CMake files for non-64bit and + non-Linux platforms (e.g. macOS and MinGW in particular) + that were introduced with release 2.3.0 + + Other changes: + #468 #469 xmlwf: Improve help output and the xmlwf man page + #463 xmlwf: Improve maintainability through some refactoring + #477 xmlwf: Fix man page DocBook validity + #456 Autotools: Sync CMake templates with CMake 3.18 + #458 #459 CMake: Support absolute paths for both CMAKE_INSTALL_LIBDIR + and CMAKE_INSTALL_INCLUDEDIR + #471 #481 CMake: Add support for standard variable BUILD_SHARED_LIBS + #457 Unexpose symbol _INTERNAL_trim_to_complete_utf8_characters + #467 Resolve macro HAVE_EXPAT_CONFIG_H + #472 Delete unused legacy helper file "conftools/PrintPath" + #473 #483 Improve attribution + #464 #465 #477 doc/reference.html: Fix XHTML validity + #475 #478 doc/reference.html: Replace the 90s look by OK.css + #479 Version info bumped from 8:0:7 to 9:0:8 + due to addition of new symbols and error codes; + see https://verbump.de/ for what these numbers do + + Infrastructure: + #456 CI: Enable periodic runs + #457 CI: Start covering the list of exported symbols + #474 CI: Isolate coverage task + #476 #482 CI: Adapt to breaking changes in image "ubuntu-18.04" + #477 CI: Cover well-formedness and DocBook/XHTML validity + of doc/reference.html and doc/xmlwf.xml + + Special thanks to: + Dimitry Andric + Eero Helenius + Nick Wellnhofer + Rhodri James + Tomas Korbar + Yury Gribov + and + Clang LeakSan + JetBrains + OSS-Fuzz + +Release 2.3.0 Thu March 25 2021 + Bug fixes: + #438 When calling XML_ParseBuffer without a prior successful call to + XML_GetBuffer as a user, no longer trigger undefined behavior + (by adding an integer to a NULL pointer) but rather return + XML_STATUS_ERROR and set the error code to (new) code + XML_ERROR_NO_BUFFER. Found by UBSan (UndefinedBehaviorSanitizer) + of Clang 11 (but not Clang 9). + #444 xmlwf: Exit status 2 was used for both: + - malformed input files (documented) and + - invalid command-line arguments (undocumented). + The case of invalid command-line arguments now + has its own exit status 4, resolving the ambiguity. + + Other changes: + #439 xmlwf: Add argument -k to allow continuing after + non-fatal errors + #439 xmlwf: Add section about exit status to the -h help output + #422 #426 #447 Windows: Drop support for Visual Studio <=14.0/2015 + #434 Windows: CMake: Detect unsupported Visual Studio at + configure time (rather than at compile time) + #382 #428 testrunner: Make verbose mode (argument "-v") report + about passed tests, and make default mode report about + failures, as well. + #442 CMake: Call "enable_language(CXX)" prior to tinkering + with CMAKE_CXX_* variables + #448 Document use of libexpat from a CMake-based project + #451 Autotools: Install CMake files as generated by CMake 3.19.6 + so that users with "find_package(expat [..] CONFIG [..])" + are served on distributions that are *not* using the CMake + build system inside for libexpat packaging + #436 #437 Autotools: Drop obsolescent macro AC_HEADER_STDC + #450 #452 Autotools: Resolve use of obsolete macro AC_CONFIG_HEADER + #441 Address compiler warnings + #443 Version info bumped from 7:12:6 to 8:0:7 + due to addition of error code XML_ERROR_NO_BUFFER + (see https://verbump.de/ for what these numbers do) + + Infrastructure: + #435 #446 Replace Travis CI by GitHub Actions + + Special thanks to: + Alexander Richardson + Oleksandr Popovych + Thomas Beutlich + Tim Bray + and + Clang LeakSan, Clang 11 UBSan and the Clang team + +Release 2.2.10 Sat October 3 2020 + Bug fixes: + #390 #395 #398 Fix undefined behavior during parsing caused by + pointer arithmetic with NULL pointers + #404 #405 Fix reading uninitialized variable during parsing + #406 xmlwf: Add missing check for malloc NULL return + + Other changes: + #396 Windows: Drop support for Visual Studio <=8.0/2005 + #409 Windows: Add missing file "Changes" to the installer + to fix compilation with CMake from installed sources + #403 xmlwf: Document exit codes in xmlwf manpage and + exit with code 3 (rather than code 1) for output errors + when used with "-d DIRECTORY" + #356 #359 MinGW: Provide declaration of rand_s for mingwrt <5.3.0 + #383 #392 Autotools: Use -Werror while configure tests the compiler + for supported compile flags to avoid false positives + #383 #393 #394 Autotools: Improve handling of user (C|CPP|CXX|LD)FLAGS, + e.g. ensure that they have the last word over flags added + while running ./configure + #360 CMake: Create libexpatw.{dll,so} and expatw.pc (with emphasis + on suffix "w") with -DEXPAT_CHAR_TYPE=(ushort|wchar_t) + #360 CMake: Detect and deny unsupported build combinations + involving -DEXPAT_CHAR_TYPE=(ushort|wchar_t) + #360 CMake: Install pre-compiled shipped xmlwf.1 manpage in case + of -DEXPAT_BUILD_DOCS=OFF + #375 #380 #419 CMake: Fix use of Expat by means of add_subdirectory + #407 #408 CMake: Keep expat target name constant at "expat" + (i.e. refrain from using the target name to control + build artifact filenames) + #385 CMake: Fix compilation with -DEXPAT_SHARED_LIBS=OFF for + Windows + CMake: Expose man page compilation as target "xmlwf-manpage" + #413 #414 CMake: Introduce option EXPAT_BUILD_PKGCONFIG + to control generation of pkg-config file "expat.pc" + #424 CMake: Add minimalistic support for building binary packages + with CMake target "package"; based on CPack + #366 CMake: Add option -DEXPAT_OSSFUZZ_BUILD=(ON|OFF) with + default OFF to build fuzzer code against OSS-Fuzz and + related environment variable LIB_FUZZING_ENGINE + #354 Fix testsuite for -DEXPAT_DTD=OFF and -DEXPAT_NS=OFF, each + #354 #355 .. + #356 #412 Address compiler warnings + #368 #369 Address pngcheck warnings with doc/*.png images + #425 Version info bumped from 7:11:6 to 7:12:6 + + Special thanks to: + asavah + Ben Wagner + Bhargava Shastry + Frank Landgraf + Jeffrey Walton + Joe Orton + Kleber Tarcísio + Ma Lin + Maciej Sroczyński + Mohammed Khajapasha + Vadim Zeitlin + and + Cppcheck 2.0 and the Cppcheck team + +Release 2.2.9 Wed September 25 2019 + Other changes: + examples: Drop executable bits from elements.c + #349 Windows: Change the name of the Windows DLLs from expat*.dll + to libexpat*.dll once more (regression from 2.2.8, first + fixed in 1.95.3, issue #61 on SourceForge today, + was issue #432456 back then); needs a fix due + case-insensitive file systems on Windows and the fact that + Perl's XML::Parser::Expat compiles into Expat.dll. + #347 Windows: Only define _CRT_RAND_S if not defined + Version info bumped from 7:10:6 to 7:11:6 + + Special thanks to: + Ben Wagner + +Release 2.2.8 Fri September 13 2019 + Security fixes: + #317 #318 CVE-2019-15903 -- Fix heap overflow triggered by + XML_GetCurrentLineNumber (or XML_GetCurrentColumnNumber), + and deny internal entities closing the doctype; + fixed in commit c20b758c332d9a13afbbb276d30db1d183a85d43 + + Bug fixes: + #240 Fix cases where XML_StopParser did not have any effect + when called from inside of an end element handler + #341 xmlwf: Fix exit code for operation without "-d DIRECTORY"; + previously, only "-d DIRECTORY" would give you a proper + exit code: + # xmlwf -d . <<<'' 2>/dev/null ; echo $? + 2 + # xmlwf <<<'' 2>/dev/null ; echo $? + 0 + Now both cases return exit code 2. + + Other changes: + #299 #302 Windows: Replace LoadLibrary hack to access + unofficial API function SystemFunction036 (RtlGenRandom) + by using official API function rand_s (needs WinXP+) + #325 Windows: Drop support for Visual Studio <=7.1/2003 + and document supported compilers in README.md + #286 Windows: Remove COM code from xmlwf; in case it turns + out needed later, there will be a dedicated repository + below https://github.com/libexpat/ for that code + #322 Windows: Remove explicit MSVC solution and project files. + You can generate Visual Studio solution files through + CMake, e.g.: cmake -G"Visual Studio 15 2017" . + #338 xmlwf: Make "xmlwf -h" help output more friendly + #339 examples: Improve elements.c + #244 #264 Autotools: Add argument --enable-xml-attr-info + #239 #301 Autotools: Add arguments + --with-getrandom + --without-getrandom + --with-sys-getrandom + --without-sys-getrandom + #312 #343 Autotools: Fix linking issues with "./configure LD=clang" + Autotools: Fix "make run-xmltest" for out-of-source builds + #329 #336 CMake: Pull all options from Expat <=2.2.7 into namespace + prefix EXPAT_ with the exception of DOCBOOK_TO_MAN: + - BUILD_doc -> EXPAT_BUILD_DOCS (plural) + - BUILD_examples -> EXPAT_BUILD_EXAMPLES + - BUILD_shared -> EXPAT_SHARED_LIBS + - BUILD_tests -> EXPAT_BUILD_TESTS + - BUILD_tools -> EXPAT_BUILD_TOOLS + - DOCBOOK_TO_MAN -> DOCBOOK_TO_MAN (unchanged) + - INSTALL -> EXPAT_ENABLE_INSTALL + - MSVC_USE_STATIC_CRT -> EXPAT_MSVC_STATIC_CRT + - USE_libbsd -> EXPAT_WITH_LIBBSD + - WARNINGS_AS_ERRORS -> EXPAT_WARNINGS_AS_ERRORS + - XML_CONTEXT_BYTES -> EXPAT_CONTEXT_BYTES + - XML_DEV_URANDOM -> EXPAT_DEV_URANDOM + - XML_DTD -> EXPAT_DTD + - XML_NS -> EXPAT_NS + - XML_UNICODE -> EXPAT_CHAR_TYPE=ushort (!) + - XML_UNICODE_WCHAR_T -> EXPAT_CHAR_TYPE=wchar_t (!) + #244 #264 CMake: Add argument -DEXPAT_ATTR_INFO=(ON|OFF), + default OFF + #326 CMake: Add argument -DEXPAT_LARGE_SIZE=(ON|OFF), + default OFF + #328 CMake: Add argument -DEXPAT_MIN_SIZE=(ON|OFF), + default OFF + #239 #277 CMake: Add arguments + -DEXPAT_WITH_GETRANDOM=(ON|OFF|AUTO), default AUTO + -DEXPAT_WITH_SYS_GETRANDOM=(ON|OFF|AUTO), default AUTO + #326 CMake: Install expat_config.h to include directory + #326 CMake: Generate and install configuration files for + future find_package(expat [..] CONFIG [..]) + CMake: Now produces a summary of applied configuration + CMake: Require C++ compiler only when tests are enabled + #330 CMake: Fix compilation for 16bit character types, + i.e. ex -DXML_UNICODE=ON (and ex -DXML_UNICODE_WCHAR_T=ON) + #265 CMake: Fix linking with MinGW + #330 CMake: Add full support for MinGW; to enable, use + -DCMAKE_TOOLCHAIN_FILE=[expat]/cmake/mingw-toolchain.cmake + #330 CMake: Port "make run-xmltest" from GNU Autotools to CMake + #316 CMake: Windows: Make binary postfix match MSVC + Old: expat[d].lib + New: expat[w][d][MD|MT].lib + CMake: Migrate files from Windows to Unix line endings + #308 CMake: Integrate OSS-Fuzz fuzzers, option + -DEXPAT_BUILD_FUZZERS=(ON|OFF), default OFF + #14 Drop an OpenVMS support leftover + #235 #268 .. + #270 #310 .. + #313 #331 #333 Address compiler warnings + #282 #283 .. + #284 #285 Address cppcheck warnings + #294 #295 Address Clang Static Analyzer warnings + #24 #293 Mass-apply clang-format 9 (and ensure conformance during CI) + Version info bumped from 7:9:6 to 7:10:6 + + Special thanks to: + David Loffredo + Joonun Jang + Kishore Kunche + Marco Maggi + Mitch Phillips + Mohammed Khajapasha + Rolf Ade + xantares + Zhongyuan Zhou + +Release 2.2.7 Wed June 19 2019 + Security fixes: + #186 #262 CVE-2018-20843 -- Fix extraction of namespace prefixes from + XML names; XML names with multiple colons could end up in + the wrong namespace, and take a high amount of RAM and CPU + resources while processing, opening the door to + use for denial-of-service attacks + + Other changes: + #195 #197 Autotools/CMake: Utilize -fvisibility=hidden to stop + exporting non-API symbols + #227 Autotools: Add --without-examples and --without-tests + #228 Autotools: Modernize configure.ac + #245 #246 Autotools: Fix check for -fvisibility=hidden for Clang + #247 #248 Autotools: Fix compilation for lack of docbook2x-man + #236 #258 Autotools: Produce .tar.{gz,lz,xz} release archives + #212 CMake: Make libdir of pkgconfig expat.pc support multilib + #158 #263 CMake: Build man page in PROJECT_BINARY_DIR not _SOURCE_DIR + #219 Remove fallback to bcopy, assume that memmove(3) exists + #257 Use portable "/usr/bin/env bash" shebang (e.g. for OpenBSD) + #243 Windows: Fix syntax of .def module definition files + Version info bumped from 7:8:6 to 7:9:6 + + Special thanks to: + Benjamin Peterson + CaolÃĄn McNamara + Hanno BÃļck + KangLin + Kishore Kunche + Marco Maggi + Rhodri James + Sebastian DrÃļge + userwithuid + Yury Gribov + +Release 2.2.6 Sun August 12 2018 + Bug fixes: + #170 #206 Avoid doing arithmetic with NULL pointers in XML_GetBuffer + #204 #205 Fix 2.2.5 regression with suspend-resume while parsing + a document like '' + + Other changes: + #165 #168 Autotools: Fix docbook-related configure syntax error + #166 Autotools: Avoid grep option `-q` for Solaris + #167 Autotools: Support + ./configure DOCBOOK_TO_MAN="xmlto man --skip-validation" + #159 #167 Autotools: Support DOCBOOK_TO_MAN command which produces + xmlwf.1 rather than XMLWF.1; also covers case insensitive + file systems + #181 Autotools: Drop -rpath option passed to libtool + #188 Autotools: Detect and deny SGML docbook2man as ours is XML + #188 Autotools/CMake: Support command db2x_docbook2man as well + #174 CMake: Introduce option WARNINGS_AS_ERRORS, defaults to OFF + #184 #185 CMake: Introduce option MSVC_USE_STATIC_CRT, defaults to OFF + #207 #208 CMake: Introduce option XML_UNICODE and XML_UNICODE_WCHAR_T, + both defaulting to OFF + #175 CMake: Prefer check_symbol_exists over check_function_exists + #176 CMake: Create the same pkg-config file as with GNU Autotools + #178 #179 CMake: Use GNUInstallDirs module to set proper defaults for + install directories + #208 CMake: Utilize expat_config.h.cmake for XML_DEV_URANDOM + #180 Windows: Fix compilation of test suite for Visual Studio 2008 + #131 #173 #202 Address compiler warnings + #187 #190 #200 Fix miscellaneous typos + Version info bumped from 7:7:6 to 7:8:6 + + Special thanks to: + Anton Maklakov + Benjamin Peterson + Brad King + Franek Korta + Frank Rast + Joe Orton + luzpaz + Pedro Vicente + Rainer Jung + Rhodri James + Rolf Ade + Rolf Eike Beer + Thomas Beutlich + Tomasz Kłoczko + +Release 2.2.5 Tue October 31 2017 + Bug fixes: + #8 If the parser runs out of memory, make sure its internal + state reflects the memory it actually has, not the memory + it wanted to have. + #11 The default handler wasn't being called when it should for + a SYSTEM or PUBLIC doctype if an entity declaration handler + was registered. + #137 #138 Fix a case of mistakenly reported parsing success where + XML_StopParser was called from an element handler + #162 Function XML_ErrorString was returning NULL rather than + a message for code XML_ERROR_INVALID_ARGUMENT + introduced with release 2.2.1 + + Other changes: + #106 xmlwf: Add argument -N adding notation declarations + #75 #106 Test suite: Resolve expected failure cases where xmlwf + output was incomplete + #127 Windows: Fix test suite compilation + #126 #127 Windows: Fix compilation for Visual Studio 2012 + Windows: Upgrade shipped project files to Visual Studio 2017 + #33 #132 tests: Mass-fix compilation for XML_UNICODE_WCHAR_T + #129 examples: Fix compilation for XML_UNICODE_WCHAR_T + #130 benchmark: Fix compilation for XML_UNICODE_WCHAR_T + #144 xmlwf: Fix compilation for XML_UNICODE_WCHAR_T; still needs + Windows or MinGW for 2-byte wchar_t + #9 Address two Clang Static Analyzer false positives + #59 Resolve troublesome macros hiding parser struct membership + and dereferencing that pointer + #6 Resolve superfluous internal malloc/realloc switch + #153 #155 Improve docbook2x-man detection + #160 Undefine NDEBUG in the test suite (rather than rejecting it) + #161 Address compiler warnings + Version info bumped from 7:6:6 to 7:7:6 + + Special thanks to: + Benbuck Nason + Hans Wennborg + JosÊ GutiÊrrez de la Concha + Pedro Monreal Gonzalez + Rhodri James + Rolf Ade + Stephen Groat + and + Core Infrastructure Initiative + +Release 2.2.4 Sat August 19 2017 + Bug fixes: + #115 Fix copying of partial characters for UTF-8 input + + Other changes: + #109 Fix "make check" for non-x86 architectures that default + to unsigned type char (-128..127 rather than 0..255) + #109 coverage.sh: Cover -funsigned-char + Autotools: Introduce --without-xmlwf argument + #65 Autotools: Replace handwritten Makefile with GNU Automake + #43 CMake: Auto-detect high quality entropy extractors, add new + option USE_libbsd=ON to use arc4random_buf of libbsd + #74 CMake: Add -fno-strict-aliasing only where supported + #114 CMake: Always honor manually set BUILD_* options + #114 CMake: Compile man page if docbook2x-man is available, only + #117 Include file tests/xmltest.log.expected in source tarball + (required for "make run-xmltest") + #117 Include (existing) Visual Studio 2013 files in source tarball + Improve test suite error output + #111 Fix some typos in documentation + Version info bumped from 7:5:6 to 7:6:6 + + Special thanks to: + Jakub Wilk + Joe Orton + Lin Tian + Rolf Eike Beer + +Release 2.2.3 Wed August 2 2017 + Security fixes: + #82 CVE-2017-11742 -- Windows: Fix DLL hijacking vulnerability + using Steve Holme's LoadLibrary wrapper for/of cURL + + Bug fixes: + #85 Fix a dangling pointer issue related to realloc + + Other changes: + Increase code coverage + #91 Linux: Allow getrandom to fail if nonblocking pool has not + yet been initialized and read /dev/urandom then, instead. + This is in line with what recent Python does. + #81 Pre-10.7/Lion macOS: Support entropy from arc4random + #86 Check that a UTF-16 encoding in an XML declaration has the + right endianness + #4 #5 #7 Recover correctly when some reallocations fail + Repair "./configure && make" for systems without any + provider of high quality entropy + and try reading /dev/urandom on those + Ensure that user-defined character encodings have converter + functions when they are needed + Fix mis-leading description of argument -c in xmlwf.1 + Rely on macro HAVE_ARC4RANDOM_BUF (rather than __CloudABI__) + for CloudABI + #100 Fix use of SIPHASH_MAIN in siphash.h + #23 Test suite: Fix memory leaks + Version info bumped from 7:4:6 to 7:5:6 + + Special thanks to: + Chanho Park + Joe Orton + Pascal Cuoq + Rhodri James + Simon McVittie + Vadim Zeitlin + Viktor Szakats + and + Core Infrastructure Initiative + +Release 2.2.2 Wed July 12 2017 + Security fixes: + #43 Protect against compilation without any source of high + quality entropy enabled, e.g. with CMake build system; + commit ff0207e6076e9828e536b8d9cd45c9c92069b895 + #60 Windows with _UNICODE: + Unintended use of LoadLibraryW with a non-wide string + resulted in failure to load advapi32.dll and degradation + in quality of used entropy when compiled with _UNICODE for + Windows; you can launch existing binaries with + EXPAT_ENTROPY_DEBUG=1 in the environment to inspect the + quality of entropy used during runtime; commits + * 95b95032f907ef1cd17ee7a9a1768010a825d61d + * 73a5a2e9c081f49f2d775cf7ced864158b68dc80 + [MOX-006] Fix non-NULL parser parameter validation in XML_Parse; + resulted in NULL dereference, previously; + commit ac256dafdffc9622ab0dc2c62fcecb0dfcfa71fe + + Bug fixes: + #69 Fix improper use of unsigned long long integer literals + + Other changes: + #73 Start requiring a C99 compiler + #49 Fix "==" Bashism in configure script + #50 Fix too eager getrandom detection for Debian GNU/kFreeBSD + #52 and macOS + #51 Address lack of stdint.h in Visual Studio 2003 to 2008 + #58 Address compile warnings + #68 Fix "./buildconf.sh && ./configure" for some versions + of Dash for /bin/sh + #72 CMake: Ease use of Expat in context of a parent project + with multiple CMakeLists.txt files + #72 CMake: Resolve mistaken executable permissions + #76 Address compile warning with -DNDEBUG (not recommended!) + #77 Address compile warning about macro redefinition + + Special thanks to: + Alexander Bluhm + Ben Boeckel + Cătălin Răceanu + Kerin Millar + LÃĄszlÃŗ BÃļszÃļrmÊnyi + S. P. Zeidler + Segev Finer + VÃĄclav Slavík + Victor Stinner + Viktor Szakats + and + Radically Open Security + +Release 2.2.1 Sat June 17 2017 + Security fixes: + CVE-2017-9233 -- External entity infinite loop DoS + Details: https://libexpat.github.io/doc/cve-2017-9233/ + Commit c4bf96bb51dd2a1b0e185374362ee136fe2c9d7f + [MOX-002] CVE-2016-9063 -- Detect integer overflow; commit + d4f735b88d9932bd5039df2335eefdd0723dbe20 + (Fixed version of existing downstream patches!) + (SF.net) #539 Fix regression from fix to CVE-2016-0718 cutting off + longer tag names; commits + * 896b6c1fd3b842f377d1b62135dccf0a579cf65d + * af507cef2c93cb8d40062a0abe43a4f4e9158fb2 + #16 * 0dbbf43fdb20f593ddf4fa1ff67288000dd4a7fd + #25 More integer overflow detection (function poolGrow); commits + * 810b74e4703dcfdd8f404e3cb177d44684775143 + * 44178553f3539ce69d34abee77a05e879a7982ac + [MOX-002] Detect overflow from len=INT_MAX call to XML_Parse; commits + * 4be2cb5afcc018d996f34bbbce6374b7befad47f + * 7e5b71b748491b6e459e5c9a1d090820f94544d8 + [MOX-005] #30 Use high quality entropy for hash initialization: + * arc4random_buf on BSD, systems with libbsd + (when configured with --with-libbsd), CloudABI + * RtlGenRandom on Windows XP / Server 2003 and later + * getrandom on Linux 3.17+ + In a way, that's still part of CVE-2016-5300. + https://github.com/libexpat/libexpat/pull/30/commits + [MOX-005] For the low quality entropy extraction fallback code, + the parser instance address can no longer leak, commit + 04ad658bd3079dd15cb60fc67087900f0ff4b083 + [MOX-003] Prevent use of uninitialised variable; commit + [MOX-004] a4dc944f37b664a3ca7199c624a98ee37babdb4b + Add missing parameter validation to public API functions + and dedicated error code XML_ERROR_INVALID_ARGUMENT: + [MOX-006] * NULL checks; commits + * d37f74b2b7149a3a95a680c4c4cd2a451a51d60a (merge/many) + * 9ed727064b675b7180c98cb3d4f75efba6966681 + * 6a747c837c50114dfa413994e07c0ba477be4534 + * Negative length (XML_Parse); commit + [MOX-002] 70db8d2538a10f4c022655d6895e4c3e78692e7f + [MOX-001] #35 Change hash algorithm to William Ahern's version of SipHash + to go further with fixing CVE-2012-0876. + https://github.com/libexpat/libexpat/pull/39/commits + + Bug fixes: + #32 Fix sharing of hash salt across parsers; + relevant where XML_ExternalEntityParserCreate is called + prior to XML_Parse, in particular (e.g. FBReader) + #28 xmlwf: Auto-disable use of memory-mapping (and parsing + as a single chunk) for files larger than ~1 GB (2^30 bytes) + rather than failing with error "out of memory" + #3 Fix double free after malloc failure in DTD code; commit + 7ae9c3d3af433cd4defe95234eae7dc8ed15637f + #17 Fix memory leak on parser error for unbound XML attribute + prefix with new namespaces defined in the same tag; + found by Google's OSS-Fuzz; commits + * 16f87daae5a16132e479e4f71862128c7a915c73 + * b47dbc9745932c160893d433220e462bd605f8cd + xmlwf on Windows: Add missing calls to CloseHandle + + New features: + #30 Introduced environment switch EXPAT_ENTROPY_DEBUG=1 + for runtime debugging of entropy extraction + + Other changes: + Increase code coverage + #33 Reject use of XML_UNICODE_WCHAR_T with sizeof(wchar_t) != 2; + XML_UNICODE_WCHAR_T was never meant to be used outside + of Windows; 4-byte wchar_t is common on Linux + (SF.net) #538 Start using -fno-strict-aliasing + (SF.net) #540 Support compilation against cloudlibc of CloudABI + Allow MinGW cross-compilation + (SF.net) #534 CMake: Introduce option "BUILD_doc" (enabled by default) + to bypass compilation of the xmlwf.1 man page + (SF.net) pr2 CMake: Introduce option "INSTALL" (enabled by default) + to bypass installation of expat files + CMake: Fix ninja support + Autotools: Add parameters --enable-xml-context [COUNT] + and --disable-xml-context; default of context of 1024 + bytes enabled unchanged + #14 Drop AmigaOS 4.x code and includes + #14 Drop ancient build systems: + * Borland C++ Builder + * OpenVMS + * Open Watcom + * Visual Studio 6.0 + * Pre-X Mac OS (MPW Makefile) + If you happen to rely on some of these, please get in + touch for joining with maintenance. + #10 Move from WIN32 to _WIN32 + #13 Fix "make run-xmltest" order instability + Address compile warnings + Bump version info from 7:2:6 to 7:3:6 + Add AUTHORS file + + Infrastructure: + #1 Migrate from SourceForge to GitHub (except downloads): + https://github.com/libexpat/ + #1 Re-create http://libexpat.org/ project website + Start utilizing Travis CI + + Special thanks to: + Andy Wang + Don Lewis + Ed Schouten + Karl Waclawek + Pascal Cuoq + Rhodri James + Sergei Nikulov + Tobias Taschner + Viktor Szakats + and + Core Infrastructure Initiative + Mozilla Foundation (MOSS Track 3: Secure Open Source) + Radically Open Security + +Release 2.2.0 Tue June 21 2016 + Security fixes: + #537 CVE-2016-0718 -- Fix crash on malformed input + CVE-2016-4472 -- Improve insufficient fix to CVE-2015-1283 / + CVE-2015-2716 introduced with Expat 2.1.1 + #499 CVE-2016-5300 -- Use more entropy for hash initialization + than the original fix to CVE-2012-0876 + #519 CVE-2012-6702 -- Resolve troublesome internal call to srand + that was introduced with Expat 2.1.0 + when addressing CVE-2012-0876 (issue #496) + + Bug fixes: + Fix uninitialized reads of size 1 + (e.g. in little2_updatePosition) + Fix detection of UTF-8 character boundaries + + Other changes: + #532 Fix compilation for Visual Studio 2010 (keyword "C99") + Autotools: Resolve use of "$<" to better support bmake + Autotools: Add QA script "qa.sh" (and make target "qa") + Autotools: Respect CXXFLAGS if given + Autotools: Fix "make run-xmltest" + Autotools: Have "make run-xmltest" check for expected output + p90 CMake: Fix static build (BUILD_shared=OFF) on Windows + #536 CMake: Add soversion, support -DNO_SONAME=yes to bypass + #323 CMake: Add suffix "d" to differentiate debug from release + CMake: Define WIN32 with CMake on Windows + Annotate memory allocators for GCC + Address all currently known compile warnings + Make sure that API symbols remain visible despite + -fvisibility=hidden + Remove executable flag from source files + Resolve COMPILED_FROM_DSP in favor of WIN32 + + Special thanks to: + BjÃļrn Lindahl + Christian Heimes + Cristian Rodríguez + Daniel KrÃŧgler + Gustavo Grieco + Karl Waclawek + LÃĄszlÃŗ BÃļszÃļrmÊnyi + Marco Grassi + Pascal Cuoq + Sergei Nikulov + Thomas Beutlich + Warren Young + Yann Droneaud + +Release 2.1.1 Sat March 12 2016 + Security fixes: + #582: CVE-2015-1283 - Multiple integer overflows in XML_GetBuffer + + Bug fixes: + #502: Fix potential null pointer dereference + #520: Symbol XML_SetHashSalt was not exported + Output of "xmlwf -h" was incomplete + + Other changes: + #503: Document behavior of calling XML_SetHashSalt with salt 0 + Minor improvements to man page xmlwf(1) + Improvements to the experimental CMake build system + libtool now invoked with --verbose + +Release 2.1.0 Sat March 24 2012 + - Security fixes: + #2958794: CVE-2012-1148 - Memory leak in poolGrow. + #2895533: CVE-2012-1147 - Resource leak in readfilemap.c. + #3496608: CVE-2012-0876 - Hash DOS attack. + #2894085: CVE-2009-3560 - Buffer over-read and crash in big2_toUtf8(). + #1990430: CVE-2009-3720 - Parser crash with special UTF-8 sequences. + - Bug Fixes: + #1742315: Harmful XML_ParserCreateNS suggestion. + #1785430: Expat build fails on linux-amd64 with gcc version>=4.1 -O3. + #1983953, 2517952, 2517962, 2649838: + Build modifications using autoreconf instead of buildconf.sh. + #2815947, #2884086: OBJEXT and EXEEXT support while building. + #2517938: xmlwf should return non-zero exit status if not well-formed. + #2517946: Wrong statement about XMLDecl in xmlwf.1 and xmlwf.sgml. + #2855609: Dangling positionPtr after error. + #2990652: CMake support. + #3010819: UNEXPECTED_STATE with a trailing "%" in entity value. + #3206497: Uninitialized memory returned from XML_Parse. + #3287849: make check fails on mingw-w64. + - Patches: + #1749198: pkg-config support. + #3010222: Fix for bug #3010819. + #3312568: CMake support. + #3446384: Report byte offsets for attr names and values. + - New Features / API changes: + Added new API member XML_SetHashSalt() that allows setting an initial + value (salt) for hash calculations. This is part of the fix for + bug #3496608 to randomize hash parameters. + When compiled with XML_ATTR_INFO defined, adds new API member + XML_GetAttributeInfo() that allows retrieving the byte + offsets for attribute names and values (patch #3446384). + Added CMake build system. + See bug #2990652 and patch #3312568. + Added run-benchmark target to Makefile.in - relies on testdata module + present in the same relative location as in the repository. + Release 2.0.1 Tue June 5 2007 - - Fixed bugs #1515266, 1515600: The character data handler's calling + - Fixed bugs #1515266, #1515600: The character data handler's calling of XML_StopParser() was not handled properly; if the parser was stopped and the handler set to NULL, the parser would segfault. - Fixed bug #1690883: Expat failed on EBCDIC systems as it assumed @@ -8,7 +1642,7 @@ Release 2.0.1 Tue June 5 2007 - Fixed xmlwf bug #1513566: "out of memory" error on file size zero. - Fixed outline.c bug #1543233: missing a final XML_ParserFree() call. - Fixes and improvements for Windows platform: - bugs #1409451, #1476160, 1548182, 1602769, 1717322. + bugs #1409451, #1476160, #1548182, #1602769, #1717322. - Build fixes for various platforms: HP-UX, Tru64, Solaris 9: patch #1437840, bug #1196180. All Unix: #1554618 (refreshed config.sub/config.guess). @@ -30,8 +1664,8 @@ Release 2.0.0 Wed Jan 11 2006 byte indexes and line/column numbers. - Updated to use libtool 1.5.22 (the most recent). - Added support for AmigaOS. - - Some mostly minor bug fixes. SF issues include: 1006708, - 1021776, 1023646, 1114960, 1156398, 1221160, 1271642. + - Some mostly minor bug fixes. SF issues include: #1006708, + #1021776, #1023646, #1114960, #1156398, #1221160, #1271642. Release 1.95.8 Fri Jul 23 2004 - Major new feature: suspend/resume. Handlers can now request @@ -40,8 +1674,8 @@ Release 1.95.8 Fri Jul 23 2004 documentation for more details. - Some mostly minor bug fixes, but compilation should no longer generate warnings on most platforms. SF issues - include: 827319, 840173, 846309, 888329, 896188, 923913, - 928113, 961698, 985192. + include: #827319, #840173, #846309, #888329, #896188, #923913, + #928113, #961698, #985192. Release 1.95.7 Mon Oct 20 2003 - Fixed enum XML_Status issue (reported on SourceForge many @@ -54,19 +1688,19 @@ Release 1.95.7 Mon Oct 20 2003 - Improved ability to build without the configure-generated expat_config.h header. This is useful for applications which embed Expat rather than linking in the library. - - Fixed a variety of bugs: see SF issues 458907, 609603, - 676844, 679754, 692878, 692964, 695401, 699323, 699487, - 820946. + - Fixed a variety of bugs: see SF issues #458907, #609603, + #676844, #679754, #692878, #692964, #695401, #699323, #699487, + #820946. - Improved hash table lookups. - Added more regression tests and improved documentation. Release 1.95.6 Tue Jan 28 2003 - Added XML_FreeContentModel(). - Added XML_MemMalloc(), XML_MemRealloc(), XML_MemFree(). - - Fixed a variety of bugs: see SF issues 615606, 616863, - 618199, 653180, 673791. + - Fixed a variety of bugs: see SF issues #615606, #616863, + #618199, #653180, #673791. - Enhanced the regression test suite. - - Man page improvements: includes SF issue 632146. + - Man page improvements: includes SF issue #632146. Release 1.95.5 Fri Sep 6 2002 - Added XML_UseForeignDTD() for improved SAX2 support. @@ -84,9 +1718,9 @@ Release 1.95.5 Fri Sep 6 2002 - Reduced line-length for all source code and headers to be no longer than 80 characters, to help with AS/400 support. - Reduced memory copying during parsing (SF patch #600964). - - Fixed a variety of bugs: see SF issues 580793, 434664, - 483514, 580503, 581069, 584041, 584183, 584832, 585537, - 596555, 596678, 598352, 598944, 599715, 600479, 600971. + - Fixed a variety of bugs: see SF issues #580793, #434664, + #483514, #580503, #581069, #584041, #584183, #584832, #585537, + #596555, #596678, #598352, #598944, #599715, #600479, #600971. Release 1.95.4 Fri Jul 12 2002 - Added support for VMS, contributed by Craig Berry. See @@ -95,14 +1729,14 @@ Release 1.95.4 Fri Jul 12 2002 contributed by Thomas Wegner and Daryle Walker. - Added Borland C++ Builder 5 / BCC 5.5 support, contributed by Patrick McConnell (SF patch #538032). - - Fixed a variety of bugs: see SF issues 441449, 563184, - 564342, 566334, 566901, 569461, 570263, 575168, 579196. + - Fixed a variety of bugs: see SF issues #441449, #563184, + #564342, #566334, #566901, #569461, #570263, #575168, #579196. - Made skippedEntityHandler conform to SAX2 (see source comment) - Re-implemented WFC: Entity Declared from XML 1.0 spec and added a new error "entity declared in parameter entity": - see SF bug report 569461 and SF patch 578161 + see SF bug report #569461 and SF patch #578161 - Re-implemented section 5.1 from XML 1.0 spec: - see SF bug report 570263 and SF patch 578161 + see SF bug report #570263 and SF patch #578161 Release 1.95.3 Mon Jun 3 2002 - Added a project to the MSVC workspace to create a wchar_t @@ -114,9 +1748,9 @@ Release 1.95.3 Mon Jun 3 2002 - Made the XML_UNICODE builds usable (thanks, Karl!). - Allow xmlwf to read from standard input. - Install a man page for xmlwf on Unix systems. - - Fixed many bugs; see SF bug reports 231864, 461380, 464837, - 466885, 469226, 477667, 484419, 487840, 494749, 496505, - 547350. Other bugs which we can't test as easily may also + - Fixed many bugs; see SF bug reports #231864, #461380, #464837, + #466885, #469226, #477667, #484419, #487840, #494749, #496505, + #547350. Other bugs which we can't test as easily may also have been fixed, especially in the area of build support. Release 1.95.2 Fri Jul 27 2001 diff --git a/cextern/expat/MANIFEST b/cextern/expat/MANIFEST deleted file mode 100755 index aa83d5bf16fa..000000000000 --- a/cextern/expat/MANIFEST +++ /dev/null @@ -1,128 +0,0 @@ -amiga/stdlib.c -amiga/launch.c -amiga/expat_vectors.c -amiga/expat_lib.c -amiga/expat.xml -amiga/README.txt -amiga/Makefile -amiga/include/proto/expat.h -amiga/include/libraries/expat.h -amiga/include/interfaces/expat.h -amiga/include/inline4/expat.h -bcb5/README.txt -bcb5/all_projects.bpg -bcb5/elements.bpf -bcb5/elements.bpr -bcb5/elements.mak -bcb5/expat.bpf -bcb5/expat.bpr -bcb5/expat.mak -bcb5/expat_static.bpf -bcb5/expat_static.bpr -bcb5/expat_static.mak -bcb5/expatw.bpf -bcb5/expatw.bpr -bcb5/expatw.mak -bcb5/expatw_static.bpf -bcb5/expatw_static.bpr -bcb5/expatw_static.mak -bcb5/libexpat_mtd.def -bcb5/libexpatw_mtd.def -bcb5/makefile.mak -bcb5/outline.bpf -bcb5/outline.bpr -bcb5/outline.mak -bcb5/setup.bat -bcb5/xmlwf.bpf -bcb5/xmlwf.bpr -bcb5/xmlwf.mak -doc/expat.png -doc/reference.html -doc/style.css -doc/valid-xhtml10.png -doc/xmlwf.1 -doc/xmlwf.sgml -COPYING -Changes -MANIFEST -Makefile.in -README -configure -configure.in -expat_config.h.in -expat.dsw -conftools/PrintPath -conftools/ac_c_bigendian_cross.m4 -conftools/config.guess -conftools/config.sub -conftools/expat.m4 -conftools/get-version.sh -conftools/install-sh -conftools/libtool.m4 -conftools/ltmain.sh -conftools/mkinstalldirs -examples/elements.c -examples/elements.dsp -examples/outline.c -examples/outline.dsp -lib/Makefile.MPW -lib/amigaconfig.h -lib/ascii.h -lib/asciitab.h -lib/expat.dsp -lib/expat.h -lib/expat_external.h -lib/expat_static.dsp -lib/expatw.dsp -lib/expatw_static.dsp -lib/iasciitab.h -lib/internal.h -lib/latin1tab.h -lib/libexpat.def -lib/libexpatw.def -lib/macconfig.h -lib/nametab.h -lib/utf8tab.h -lib/winconfig.h -lib/xmlparse.c -lib/xmlrole.c -lib/xmlrole.h -lib/xmltok.c -lib/xmltok.h -lib/xmltok_impl.c -lib/xmltok_impl.h -lib/xmltok_ns.c -tests/benchmark/README.txt -tests/benchmark/benchmark.c -tests/benchmark/benchmark.dsp -tests/benchmark/benchmark.dsw -tests/README.txt -tests/chardata.c -tests/chardata.h -tests/minicheck.c -tests/minicheck.h -tests/runtests.c -tests/runtestspp.cpp -tests/xmltest.sh -vms/README.vms -vms/descrip.mms -vms/expat_config.h -win32/MANIFEST.txt -win32/README.txt -win32/expat.iss -xmlwf/codepage.c -xmlwf/codepage.h -xmlwf/ct.c -xmlwf/filemap.h -xmlwf/readfilemap.c -xmlwf/unixfilemap.c -xmlwf/win32filemap.c -xmlwf/xmlfile.c -xmlwf/xmlfile.h -xmlwf/xmlmime.c -xmlwf/xmlmime.h -xmlwf/xmltchar.h -xmlwf/xmlurl.h -xmlwf/xmlwf.c -xmlwf/xmlwf.dsp -xmlwf/xmlwin32url.cxx diff --git a/cextern/expat/Makefile.in b/cextern/expat/Makefile.in deleted file mode 100755 index 0e2cc959dce0..000000000000 --- a/cextern/expat/Makefile.in +++ /dev/null @@ -1,187 +0,0 @@ -################################################################ -# Process this file with top-level configure script to produce Makefile -# -# Copyright 2000 Clark Cooper -# -# This file is part of EXPAT. -# -# EXPAT is free software; you can redistribute it and/or modify it -# under the terms of the License (based on the MIT/X license) contained -# in the file COPYING that comes with this distribution. -# -# EXPAT IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -# SOFTWARE OR THE USE OR OTHER DEALINGS IN EXPAT. -# - -SHELL = @SHELL@ - -srcdir = @srcdir@ -top_srcdir = @top_srcdir@ -VPATH = @srcdir@ - -prefix = @prefix@ -exec_prefix = @exec_prefix@ - -bindir = @bindir@ -libdir = @libdir@ -includedir = @includedir@ -man1dir = @mandir@/man1 - -top_builddir = . - - -INSTALL = @INSTALL@ -INSTALL_PROGRAM = @INSTALL_PROGRAM@ -INSTALL_DATA = @INSTALL_DATA@ -mkinstalldirs = $(SHELL) $(top_srcdir)/conftools/mkinstalldirs - -MANFILE = $(srcdir)/doc/xmlwf.1 -APIHEADER = $(srcdir)/lib/expat.h $(srcdir)/lib/expat_external.h -LIBRARY = libexpat.la - -DESTDIR = $(INSTALL_ROOT) - -default: buildlib xmlwf/xmlwf - -buildlib: $(LIBRARY) - -all: $(LIBRARY) xmlwf/xmlwf examples/elements examples/outline - -clean: - cd lib && rm -f $(LIBRARY) *.o *.lo && rm -rf .libs _libs - cd xmlwf && rm -f xmlwf *.o *.lo && rm -rf .libs _libs - cd examples && rm -f elements outline *.o *.lo && rm -rf .libs _libs - cd tests && rm -rf .libs runtests runtests.o runtestspp runtestspp.o - cd tests && rm -f chardata.o minicheck.o - rm -rf .libs libexpat.la - rm -f examples/core tests/core xmlwf/core - -clobber: clean - -distclean: clean - rm -f expat_config.h config.status config.log config.cache libtool - rm -f Makefile - -extraclean: distclean - rm -f expat_config.h.in configure - rm -f conftools/ltconfig conftools/ltmain.sh conftools/libtool.m4 - -check: tests/runtests tests/runtestspp - tests/runtests - tests/runtestspp - -install: xmlwf/xmlwf installlib - $(mkinstalldirs) $(DESTDIR)$(bindir) $(DESTDIR)$(man1dir) - $(LIBTOOL) --mode=install $(INSTALL_PROGRAM) xmlwf/xmlwf $(DESTDIR)$(bindir)/xmlwf - $(INSTALL_DATA) $(MANFILE) $(DESTDIR)$(man1dir) - -installlib: $(LIBRARY) $(APIHEADER) - $(mkinstalldirs) $(DESTDIR)$(libdir) $(DESTDIR)$(includedir) - $(LIBTOOL) --mode=install $(INSTALL) $(LIBRARY) $(DESTDIR)$(libdir)/$(LIBRARY) - for FN in $(APIHEADER) ; do $(INSTALL_DATA) $$FN $(DESTDIR)$(includedir) ; done - -uninstall: uninstalllib - $(LIBTOOL) --mode=uninstall rm -f $(DESTDIR)$(bindir)/xmlwf - rm -f $(DESTDIR)$(man1dir)/xmlwf.1 - -uninstalllib: - $(LIBTOOL) --mode=uninstall rm -f $(DESTDIR)$(libdir)/$(LIBRARY) - rm -f $(DESTDIR)$(includedir)/expat.h - rm -f $(DESTDIR)$(includedir)/expat_external.h - -# for VPATH builds (invoked by configure) -mkdir-init: - @for d in lib xmlwf examples tests ; do \ - (mkdir $$d 2> /dev/null || test 1) ; \ - done - -CC = @CC@ -CXX = @CXX@ -LIBTOOL = @LIBTOOL@ - -INCLUDES = -I$(srcdir)/lib -I. -LDFLAGS = @LDFLAGS@ -CPPFLAGS = @CPPFLAGS@ -DHAVE_EXPAT_CONFIG_H -CFLAGS = @CFLAGS@ -CXXFLAGS = @CXXFLAGS@ -VSNFLAG = -version-info @LIBCURRENT@:@LIBREVISION@:@LIBAGE@ - -### autoconf this? -LTFLAGS = --silent - -COMPILE = $(CC) $(INCLUDES) $(CFLAGS) $(DEFS) $(CPPFLAGS) -CXXCOMPILE = $(CXX) $(INCLUDES) $(CXXFLAGS) $(DEFS) $(CPPFLAGS) -LTCOMPILE = $(LIBTOOL) $(LTFLAGS) --mode=compile $(COMPILE) -LINK_LIB = $(LIBTOOL) $(LTFLAGS) --mode=link $(COMPILE) -no-undefined $(VSNFLAG) -rpath $(libdir) $(LDFLAGS) -o $@ -LINK_EXE = $(LIBTOOL) $(LTFLAGS) --mode=link $(COMPILE) $(LDFLAGS) -o $@ -LINK_CXX_EXE = $(LIBTOOL) $(LTFLAGS) --mode=link $(CXXCOMPILE) $(LDFLAGS) -o $@ - -LIB_OBJS = lib/xmlparse.lo lib/xmltok.lo lib/xmlrole.lo -$(LIBRARY): $(LIB_OBJS) - $(LINK_LIB) $(LIB_OBJS) - -lib/xmlparse.lo: lib/xmlparse.c lib/expat.h lib/xmlrole.h lib/xmltok.h \ - $(top_builddir)/expat_config.h lib/expat_external.h lib/internal.h - -lib/xmlrole.lo: lib/xmlrole.c lib/ascii.h lib/xmlrole.h \ - $(top_builddir)/expat_config.h lib/expat_external.h lib/internal.h - -lib/xmltok.lo: lib/xmltok.c lib/xmltok_impl.c lib/xmltok_ns.c \ - lib/ascii.h lib/asciitab.h lib/iasciitab.h lib/latin1tab.h \ - lib/nametab.h lib/utf8tab.h lib/xmltok.h lib/xmltok_impl.h \ - $(top_builddir)/expat_config.h lib/expat_external.h lib/internal.h - - -XMLWF_OBJS = xmlwf/xmlwf.o xmlwf/xmlfile.o xmlwf/codepage.o xmlwf/@FILEMAP@.o -xmlwf/xmlwf.o: xmlwf/xmlwf.c -xmlwf/xmlfile.o: xmlwf/xmlfile.c -xmlwf/codepage.o: xmlwf/codepage.c -xmlwf/@FILEMAP@.o: xmlwf/@FILEMAP@.c -xmlwf/xmlwf: $(XMLWF_OBJS) $(LIBRARY) - $(LINK_EXE) $(XMLWF_OBJS) $(LIBRARY) - -examples/elements.o: examples/elements.c -examples/elements: examples/elements.o $(LIBRARY) - $(LINK_EXE) $< $(LIBRARY) - -examples/outline.o: examples/outline.c -examples/outline: examples/outline.o $(LIBRARY) - $(LINK_EXE) $< $(LIBRARY) - -tests/chardata.o: tests/chardata.c tests/chardata.h -tests/minicheck.o: tests/minicheck.c tests/minicheck.h -tests/runtests.o: tests/runtests.c tests/chardata.h -tests/runtests: tests/runtests.o tests/chardata.o tests/minicheck.o $(LIBRARY) - $(LINK_EXE) tests/runtests.o tests/chardata.o tests/minicheck.o $(LIBRARY) -tests/runtestspp.o: tests/runtestspp.cpp tests/runtests.c tests/chardata.h -tests/runtestspp: tests/runtestspp.o tests/chardata.o tests/minicheck.o $(LIBRARY) - $(LINK_CXX_EXE) tests/runtestspp.o tests/chardata.o tests/minicheck.o $(LIBRARY) - -tests/xmlts.zip: - wget --output-document=tests/xmlts.zip \ - http://www.w3.org/XML/Test/xmlts20020606.zip - -tests/XML-Test-Suite: tests/xmlts.zip - cd tests && unzip -q xmlts.zip - -run-xmltest: xmlwf/xmlwf tests/XML-Test-Suite - tests/xmltest.sh - -.SUFFIXES: .c .cpp .lo .o - -.cpp.o: - $(CXXCOMPILE) -o $@ -c $< -.c.o: - $(COMPILE) -o $@ -c $< -.c.lo: - $(LTCOMPILE) -o $@ -c $< - -.PHONY: buildlib all \ - clean distclean extraclean maintainer-clean \ - dist distdir \ - install uninstall diff --git a/cextern/expat/README b/cextern/expat/README deleted file mode 100755 index fda282a8f541..000000000000 --- a/cextern/expat/README +++ /dev/null @@ -1,137 +0,0 @@ - - Expat, Release 2.0.1 - -This is Expat, a C library for parsing XML, written by James Clark. -Expat is a stream-oriented XML parser. This means that you register -handlers with the parser before starting the parse. These handlers -are called when the parser discovers the associated structures in the -document being parsed. A start tag is an example of the kind of -structures for which you may register handlers. - -Windows users should use the expat_win32bin package, which includes -both precompiled libraries and executables, and source code for -developers. - -Expat is free software. You may copy, distribute, and modify it under -the terms of the License contained in the file COPYING distributed -with this package. This license is the same as the MIT/X Consortium -license. - -Versions of Expat that have an odd minor version (the middle number in -the release above), are development releases and should be considered -as beta software. Releases with even minor version numbers are -intended to be production grade software. - -If you are building Expat from a check-out from the CVS repository, -you need to run a script that generates the configure script using the -GNU autoconf and libtool tools. To do this, you need to have -autoconf 2.52 or newer and libtool 1.4 or newer (1.5 or newer preferred). -Run the script like this: - - ./buildconf.sh - -Once this has been done, follow the same instructions as for building -from a source distribution. - -To build Expat from a source distribution, you first run the -configuration shell script in the top level distribution directory: - - ./configure - -There are many options which you may provide to configure (which you -can discover by running configure with the --help option). But the -one of most interest is the one that sets the installation directory. -By default, the configure script will set things up to install -libexpat into /usr/local/lib, expat.h into /usr/local/include, and -xmlwf into /usr/local/bin. If, for example, you'd prefer to install -into /home/me/mystuff/lib, /home/me/mystuff/include, and -/home/me/mystuff/bin, you can tell configure about that with: - - ./configure --prefix=/home/me/mystuff - -Another interesting option is to enable 64-bit integer support for -line and column numbers and the over-all byte index: - - ./configure CPPFLAGS=-DXML_LARGE_SIZE - -However, such a modification would be a breaking change to the ABI -and is therefore not recommended for general use - e.g. as part of -a Linux distribution - but rather for builds with special requirements. - -After running the configure script, the "make" command will build -things and "make install" will install things into their proper -location. Have a look at the "Makefile" to learn about additional -"make" options. Note that you need to have write permission into -the directories into which things will be installed. - -If you are interested in building Expat to provide document -information in UTF-16 rather than the default UTF-8, follow these -instructions (after having run "make distclean"): - - 1. For UTF-16 output as unsigned short (and version/error - strings as char), run: - - ./configure CPPFLAGS=-DXML_UNICODE - - For UTF-16 output as wchar_t (incl. version/error strings), - run: - - ./configure CFLAGS="-g -O2 -fshort-wchar" \ - CPPFLAGS=-DXML_UNICODE_WCHAR_T - - 2. Edit the MakeFile, changing: - - LIBRARY = libexpat.la - - to: - - LIBRARY = libexpatw.la - - (Note the additional "w" in the library name.) - - 3. Run "make buildlib" (which builds the library only). - Or, to save step 2, run "make buildlib LIBRARY=libexpatw.la". - - 4. Run "make installlib" (which installs the library only). - Or, if step 2 was omitted, run "make installlib LIBRARY=libexpatw.la". - -Using DESTDIR or INSTALL_ROOT is enabled, with INSTALL_ROOT being the default -value for DESTDIR, and the rest of the make file using only DESTDIR. -It works as follows: - $ make install DESTDIR=/path/to/image -overrides the in-makefile set DESTDIR, while both - $ INSTALL_ROOT=/path/to/image make install - $ make install INSTALL_ROOT=/path/to/image -use DESTDIR=$(INSTALL_ROOT), even if DESTDIR eventually is defined in the -environment, because variable-setting priority is -1) commandline -2) in-makefile -3) environment - -Note for Solaris users: The "ar" command is usually located in -"/usr/ccs/bin", which is not in the default PATH. You will need to -add this to your path for the "make" command, and probably also switch -to GNU make (the "make" found in /usr/ccs/bin does not seem to work -properly -- appearantly it does not understand .PHONY directives). If -you're using ksh or bash, use this command to build: - - PATH=/usr/ccs/bin:$PATH make - -When using Expat with a project using autoconf for configuration, you -can use the probing macro in conftools/expat.m4 to determine how to -include Expat. See the comments at the top of that file for more -information. - -A reference manual is available in the file doc/reference.html in this -distribution. - -The homepage for this project is http://www.libexpat.org/. There -are links there to connect you to the bug reports page. If you need -to report a bug when you don't have access to a browser, you may also -send a bug report by email to expat-bugs@mail.libexpat.org. - -Discussion related to the direction of future expat development takes -place on expat-discuss@mail.libexpat.org. Archives of this list and -other Expat-related lists may be found at: - - http://mail.libexpat.org/mailman/listinfo/ diff --git a/cextern/expat/README.md b/cextern/expat/README.md new file mode 100644 index 000000000000..c2f288ca1242 --- /dev/null +++ b/cextern/expat/README.md @@ -0,0 +1,311 @@ +[![Run Linux CI tasks](https://github.com/libexpat/libexpat/actions/workflows/linux.yml/badge.svg)](https://github.com/libexpat/libexpat/actions/workflows/linux.yml) +[![Packaging status](https://repology.org/badge/tiny-repos/expat.svg)](https://repology.org/metapackage/expat/versions) +[![Downloads SourceForge](https://img.shields.io/sourceforge/dt/expat?label=Downloads%20SourceForge)](https://sourceforge.net/projects/expat/files/) +[![Downloads GitHub](https://img.shields.io/github/downloads/libexpat/libexpat/total?label=Downloads%20GitHub)](https://github.com/libexpat/libexpat/releases) +[![OpenSSF Best Practices](https://www.bestpractices.dev/projects/10205/badge)](https://www.bestpractices.dev/projects/10205) + +> [!CAUTION] +> +> Expat is **understaffed** and without funding. +> There is a [call for help with details](https://github.com/libexpat/libexpat/blob/master/expat/Changes) +> at the top of the `Changes` file. + + +# Expat, Release 2.7.3 + +This is Expat, a C99 library for parsing +[XML 1.0 Fourth Edition](https://www.w3.org/TR/2006/REC-xml-20060816/), started by +[James Clark](https://en.wikipedia.org/wiki/James_Clark_%28programmer%29) in 1997. +Expat is a stream-oriented XML parser. This means that you register +handlers with the parser before starting the parse. These handlers +are called when the parser discovers the associated structures in the +document being parsed. A start tag is an example of the kind of +structures for which you may register handlers. + +Expat supports the following C99 compilers: + +- GNU GCC >=4.5 (for use from C) or GNU GCC >=4.8.1 (for use from C++) +- LLVM Clang >=3.5 +- Microsoft Visual Studio >=17.0/2022 + (the oldest version supported by the [official GitHub Actions Windows images](https://github.com/actions/runner-images)) + +Windows users can use the +[`expat-win32bin-*.*.*.{exe,zip}` download](https://github.com/libexpat/libexpat/releases), +which includes both pre-compiled libraries and executables, and source code for +developers. + +Expat is [free software](https://www.gnu.org/philosophy/free-sw.en.html). +You may copy, distribute, and modify it under the terms of the License +contained in the file +[`COPYING`](https://github.com/libexpat/libexpat/blob/master/expat/COPYING) +distributed with this package. +This license is the same as the MIT/X Consortium license. + + +## Using libexpat in your CMake-Based Project + +There are three documented ways of using libexpat with CMake: + +### a) `find_package` with Module Mode + +This approach leverages CMake's own [module `FindEXPAT`](https://cmake.org/cmake/help/latest/module/FindEXPAT.html). + +Notice the *uppercase* `EXPAT` in the following example: + +```cmake +cmake_minimum_required(VERSION 3.10) + +project(hello VERSION 1.0.0) + +find_package(EXPAT 2.2.8 MODULE REQUIRED) + +add_executable(hello + hello.c +) + +target_link_libraries(hello PUBLIC EXPAT::EXPAT) +``` + +### b) `find_package` with Config Mode + +This approach requires files fromâ€Ļ + +- libexpat >=2.2.8 where packaging uses the CMake build system +or +- libexpat >=2.3.0 where packaging uses the GNU Autotools build system + on Linux +or +- libexpat >=2.4.0 where packaging uses the GNU Autotools build system + on macOS or MinGW. + +Notice the *lowercase* `expat` in the following example: + +```cmake +cmake_minimum_required(VERSION 3.10) + +project(hello VERSION 1.0.0) + +find_package(expat 2.2.8 CONFIG REQUIRED char dtd ns) + +add_executable(hello + hello.c +) + +target_link_libraries(hello PUBLIC expat::expat) +``` + +### c) The `FetchContent` module + +This approach — as demonstrated below — requires CMake >=3.18 for both the +[`FetchContent` module](https://cmake.org/cmake/help/latest/module/FetchContent.html) +and its support for the `SOURCE_SUBDIR` option to be available. + +Please note that: +- Use of the `FetchContent` module with *non-release* SHA1s or `master` + of libexpat is neither advised nor considered officially supported. +- Pinning to a specific commit is great for robust CI. +- Pinning to a specific commit needs updating every time there is a new + release of libexpat — either manually or through automation —, + to not miss out on libexpat security updates. + +For an example that pulls in libexpat via Git: + +```cmake +cmake_minimum_required(VERSION 3.18) + +include(FetchContent) + +project(hello VERSION 1.0.0) + +FetchContent_Declare( + expat + GIT_REPOSITORY https://github.com/libexpat/libexpat/ + GIT_TAG 000000000_GIT_COMMIT_SHA1_HERE_000000000 # i.e. Git tag R_X_Y_Z + SOURCE_SUBDIR expat/ +) + +FetchContent_MakeAvailable(expat) + +add_executable(hello + hello.c +) + +target_link_libraries(hello PUBLIC expat) +``` + + +## Building from a Git Clone + +If you are building Expat from a check-out from the +[Git repository](https://github.com/libexpat/libexpat/), +you need to run a script that generates the configure script using the +GNU autoconf and libtool tools. To do this, you need to have +autoconf 2.58 or newer. Run the script like this: + +```console +./buildconf.sh +``` + +Once this has been done, follow the same instructions as for building +from a source distribution. + + +## Building from a Source Distribution + +### a) Building with the configure script (i.e. GNU Autotools) + +To build Expat from a source distribution, you first run the +configuration shell script in the top level distribution directory: + +```console +./configure +``` + +There are many options which you may provide to configure (which you +can discover by running configure with the `--help` option). But the +one of most interest is the one that sets the installation directory. +By default, the configure script will set things up to install +libexpat into `/usr/local/lib`, `expat.h` into `/usr/local/include`, and +`xmlwf` into `/usr/local/bin`. If, for example, you'd prefer to install +into `/home/me/mystuff/lib`, `/home/me/mystuff/include`, and +`/home/me/mystuff/bin`, you can tell `configure` about that with: + +```console +./configure --prefix=/home/me/mystuff +``` + +Another interesting option is to enable 64-bit integer support for +line and column numbers and the over-all byte index: + +```console +./configure CPPFLAGS=-DXML_LARGE_SIZE +``` + +However, such a modification would be a breaking change to the ABI +and is therefore not recommended for general use — e.g. as part of +a Linux distribution — but rather for builds with special requirements. + +After running the configure script, the `make` command will build +things and `make install` will install things into their proper +location. Have a look at the `Makefile` to learn about additional +`make` options. Note that you need to have write permission into +the directories into which things will be installed. + +If you are interested in building Expat to provide document +information in UTF-16 encoding rather than the default UTF-8, follow +these instructions (after having run `make distclean`). +Please note that we configure with `--without-xmlwf` as xmlwf does not +support this mode of compilation (yet): + +1. Mass-patch `Makefile.am` files to use `libexpatw.la` for a library name: +
+ `find . -name Makefile.am -exec sed + -e 's,libexpat\.la,libexpatw.la,' + -e 's,libexpat_la,libexpatw_la,' + -i.bak {} +` + +1. Run `automake` to re-write `Makefile.in` files:
+ `automake` + +1. For UTF-16 output as unsigned short (and version/error strings as char), + run:
+ `./configure CPPFLAGS=-DXML_UNICODE --without-xmlwf`
+ For UTF-16 output as `wchar_t` (incl. version/error strings), run:
+ `./configure CFLAGS="-g -O2 -fshort-wchar" CPPFLAGS=-DXML_UNICODE_WCHAR_T + --without-xmlwf` +
Note: The latter requires libc compiled with `-fshort-wchar`, as well. + +1. Run `make` (which excludes xmlwf). + +1. Run `make install` (again, excludes xmlwf). + +Using `DESTDIR` is supported. It works as follows: + +```console +make install DESTDIR=/path/to/image +``` + +overrides the in-makefile set `DESTDIR`, because variable-setting priority is + +1. commandline +1. in-makefile +1. environment + +Note: This only applies to the Expat library itself, building UTF-16 versions +of xmlwf and the tests is currently not supported. + +When using Expat with a project using autoconf for configuration, you +can use the probing macro in `conftools/expat.m4` to determine how to +include Expat. See the comments at the top of that file for more +information. + +A reference manual is available in the file `doc/reference.html` in this +distribution. + + +### b) Building with CMake + +The CMake build system is still *experimental* and may replace the primary +build system based on GNU Autotools at some point when it is ready. + + +#### Available Options + +For an idea of the available (non-advanced) options for building with CMake: + +```console +# rm -f CMakeCache.txt ; cmake -D_EXPAT_HELP=ON -LH . | grep -B1 ':.*=' | sed 's,^--$,,' +// Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel ... +CMAKE_BUILD_TYPE:STRING= + +// Install path prefix, prepended onto install directories. +CMAKE_INSTALL_PREFIX:PATH=/usr/local + +// Path to a program. +DOCBOOK_TO_MAN:FILEPATH=/usr/bin/docbook2x-man + +// Build man page for xmlwf +EXPAT_BUILD_DOCS:BOOL=ON + +// Build the examples for expat library +EXPAT_BUILD_EXAMPLES:BOOL=ON + +// Build fuzzers for the expat library +EXPAT_BUILD_FUZZERS:BOOL=OFF + +// Build pkg-config file +EXPAT_BUILD_PKGCONFIG:BOOL=ON + +// Build the tests for expat library +EXPAT_BUILD_TESTS:BOOL=ON + +// Build the xmlwf tool for expat library +EXPAT_BUILD_TOOLS:BOOL=ON + +// Character type to use (char|ushort|wchar_t) [default=char] +EXPAT_CHAR_TYPE:STRING=char + +// Install expat files in cmake install target +EXPAT_ENABLE_INSTALL:BOOL=ON + +// Use /MT flag (static CRT) when compiling in MSVC +EXPAT_MSVC_STATIC_CRT:BOOL=OFF + +// Build fuzzers via OSS-Fuzz for the expat library +EXPAT_OSSFUZZ_BUILD:BOOL=OFF + +// Build a shared expat library +EXPAT_SHARED_LIBS:BOOL=ON + +// Treat all compiler warnings as errors +EXPAT_WARNINGS_AS_ERRORS:BOOL=OFF + +// Make use of getrandom function (ON|OFF|AUTO) [default=AUTO] +EXPAT_WITH_GETRANDOM:STRING=AUTO + +// Utilize libbsd (for arc4random_buf) +EXPAT_WITH_LIBBSD:BOOL=OFF + +// Make use of syscall SYS_getrandom (ON|OFF|AUTO) [default=AUTO] +EXPAT_WITH_SYS_GETRANDOM:STRING=AUTO +``` diff --git a/cextern/expat/amiga/Makefile b/cextern/expat/amiga/Makefile deleted file mode 100755 index 8450dd5ed88a..000000000000 --- a/cextern/expat/amiga/Makefile +++ /dev/null @@ -1,187 +0,0 @@ -# -# Makefile for AmigaOS -# - -.PHONY: help all staticlib sharedlib check clean install - -vpath %.c ../lib ../examples ../xmlwf ../tests -vpath %.h ../lib ../tests - - -# -# Set these for SDK installation. -# -tooldir = SDK:Local/C -staticlibdir = SDK:Local/clib2/lib -staticincludedir = SDK:Local/clib2/include -sharedlibdir = SDK:Local/Libs -sharedincludedir = SDK:Local/common/include - - -ifeq ($(MAKECMDGOALS), staticlib) - DESTDIR = lib -else - DESTDIR = libs -endif - -STATICLIB = $(DESTDIR)/libexpat.a -SHAREDLIB = $(DESTDIR)/expat.library - -XMLWF = $(DESTDIR)/xmlwf -ELEMENTS = $(DESTDIR)/elements -OUTLINE = $(DESTDIR)/outline -RUNTESTS = $(DESTDIR)/runtests -BENCHMARK = $(DESTDIR)/benchmark - - -help: - @echo "Requires:" - @echo " AmigaOS 4.0" - @echo " SDK 51.22" - @echo " clib2 1.202" - @echo "" - @echo "Targets:" - @echo " all - make expat libraries, xmlwf, examples and run test suite" - @echo " install - install expat libraries and tools into SDK" - -all: - -makedir force lib libs - $(MAKE) staticlib - $(MAKE) sharedlib - -staticlib: $(STATICLIB) $(XMLWF) $(ELEMENTS) $(OUTLINE) $(RUNTESTS) $(BENCHMARK) check - @echo done - -sharedlib: $(SHAREDLIB) $(XMLWF) $(ELEMENTS) $(OUTLINE) $(RUNTESTS) $(BENCHMARK) check - @echo done - -check: - $(RUNTESTS) - -clean: - -delete lib/#?.o quiet - -delete libs/#?.o quiet - -install: - copy libs/xmlwf $(tooldir) quiet - copy /lib/expat.h /lib/expat_external.h $(staticincludedir) quiet - copy lib/libexpat.a $(staticlibdir) quiet - copy libs/expat.library $(sharedlibdir) quiet - copy include $(sharedincludedir) quiet all - - -CC = gcc -LIBTOOL = ar -STRIP = strip - -LDFLAGS = -LIBS = -CFLAGS = -DNDEBUG -O2 -LTFLAGS = -crs -STRIPFLAGS = -R.comment -LAUNCH = - -ifeq ($(MAKECMDGOALS), staticlib) - CFLAGS += -mcrt=clib2 - LDFLAGS += -mcrt=clib2 - LIBS += $(STATICLIB) - INCLUDES = -I../lib -endif - -ifeq ($(MAKECMDGOALS), sharedlib) - CFLAGS += -mcrt=newlib -D__USE_INLINE__ - LDFLAGS += -mcrt=newlib - INCLUDES = -I. -Iinclude -Iinclude/libraries -I../lib - LAUNCH = $(DESTDIR)/launch.o -endif - - -COMPILE = $(CC) $(CFLAGS) $(INCLUDES) -c - -LINK = $(CC) $(LDFLAGS) -o $@ - - -$(STATICLIB): $(DESTDIR)/xmlparse.o $(DESTDIR)/xmltok.o $(DESTDIR)/xmlrole.o - $(LIBTOOL) $(LTFLAGS) $@ $^ - -$(DESTDIR)/xmlparse.o: xmlparse.c expat.h xmlrole.h xmltok.h \ - expat_external.h internal.h amigaconfig.h - $(COMPILE) $< -o $@ - -$(DESTDIR)/xmlrole.o: xmlrole.c ascii.h xmlrole.h expat_external.h \ - internal.h amigaconfig.h - $(COMPILE) $< -o $@ - -$(DESTDIR)/xmltok.o: xmltok.c xmltok_impl.c xmltok_ns.c ascii.h asciitab.h \ - iasciitab.h latin1tab.h nametab.h utf8tab.h xmltok.h xmltok_impl.h \ - expat_external.h internal.h amigaconfig.h - $(COMPILE) $< -o $@ - - -$(SHAREDLIB): $(DESTDIR)/expat_lib.o $(DESTDIR)/expat_vectors.o $(DESTDIR)/stdlib.o lib/libexpat.a - $(LINK) -nostartfiles -nostdlib -o $@ $^ - -$(DESTDIR)/expat_lib.o: expat_lib.c - $(COMPILE) $< -o $@ - -$(DESTDIR)/expat_vectors.o: expat_vectors.c - $(COMPILE) $< -o $@ - -$(DESTDIR)/stdlib.o: stdlib.c - $(COMPILE) $< -o $@ - -$(LAUNCH): launch.c - $(COMPILE) $< -o $@ - - -$(XMLWF): $(DESTDIR)/xmlwf.o $(DESTDIR)/xmlfile.o $(DESTDIR)/codepage.o $(DESTDIR)/readfilemap.o $(LAUNCH) - $(LINK) $^ $(LIBS) - $(STRIP) $(STRIPFLAGS) $@ - -$(DESTDIR)/xmlwf.o: xmlwf.c - $(COMPILE) $< -o $@ - -$(DESTDIR)/xmlfile.o: xmlfile.c - $(COMPILE) $< -o $@ - -$(DESTDIR)/codepage.o: codepage.c - $(COMPILE) $< -o $@ - -$(DESTDIR)/readfilemap.o: readfilemap.c - $(COMPILE) $< -o $@ - - -$(ELEMENTS): $(DESTDIR)/elements.o $(LAUNCH) - $(LINK) $^ $(LIBS) - $(STRIP) $(STRIPFLAGS) $@ - -$(DESTDIR)/elements.o: elements.c - $(COMPILE) $< -o $@ - - -$(OUTLINE): $(DESTDIR)/outline.o $(LAUNCH) - $(LINK) $^ $(LIBS) - $(STRIP) $(STRIPFLAGS) $@ - -$(DESTDIR)/outline.o: outline.c - $(COMPILE) $< -o $@ - - -$(RUNTESTS): $(DESTDIR)/runtests.o $(DESTDIR)/chardata.o $(DESTDIR)/minicheck.o $(LAUNCH) - $(LINK) $^ $(LIBS) - -$(DESTDIR)/chardata.o: chardata.c chardata.h - $(COMPILE) $< -o $@ - -$(DESTDIR)/minicheck.o: minicheck.c minicheck.h - $(COMPILE) $< -o $@ - -$(DESTDIR)/runtests.o: runtests.c chardata.h - $(COMPILE) $< -o $@ - - -$(BENCHMARK): $(DESTDIR)/benchmark.o $(LAUNCH) - $(LINK) $^ $(LIBS) -lm - -$(DESTDIR)/benchmark.o: benchmark/benchmark.c - $(COMPILE) $< -o $@ diff --git a/cextern/expat/amiga/README.txt b/cextern/expat/amiga/README.txt deleted file mode 100755 index 149518f746d6..000000000000 --- a/cextern/expat/amiga/README.txt +++ /dev/null @@ -1,66 +0,0 @@ -SUMMARY -======= -This is a port of expat for AmigaOS 4.0 which includes the -SDK, some XML tools and the libraries. - -Both static and shared library versions are supported. - -The static library version is limited to clib2 although it should -be possible to use newlib with the appopriate compile options. - -The shared library version is based on the work of Fredrik Wikstrom -and is currently limited to PPC only. - - -HISTORY -======= -4.2 - updated to correspond to Expat 2.0.1 release - - bumped copyright banners and versions - - simplified amigaconfig.h - - updated include/libraries/expat.h file - - modified launch.c to use contructor/deconstructor - - removed need for amiga_main() from expat utilities - -4.1 - fixed memory freeing bug in shared library version - - now allocates shared memory - -4.0 - updated for corresponding Expat 2.0 release - - some minor CVS related changes - -3.1 - removed obsolete sfd file - - added library description xml file - - refactored Makefile - - removed extraneous VARARGS68K keywords - - reworked default memory handling functions in shared lib - - updated amigaconfig.h - -3.0 - initial release - - based on expat 1.95.8 - - -BUILDING -======== -To build expat.library, xmlwf tool, examples and run the test suite, -simply type 'make all' in the amiga subdirectory. - -The test suite will compile and run for both the static and shared -library versions. - - -INSTALLATION -============ -To install both static and shared versions of expat into the -AmigaOS SDK type 'make install' in the amiga subdirectory. - - -CONFIGURATION -============= -You may want to edit the lib/amigaconfig.h file to remove -DTD and/or XML namespace support if they are not required by your -specific application for a smaller and faster implementation. - - -TO DO -===== -- wide character support (UTF-16) -- provide 68k backwards compatibility diff --git a/cextern/expat/amiga/expat.xml b/cextern/expat/amiga/expat.xml deleted file mode 100755 index d6aeae2e3ab5..000000000000 --- a/cextern/expat/amiga/expat.xml +++ /dev/null @@ -1,264 +0,0 @@ - - - - - libraries/expat.h - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/cextern/expat/amiga/expat_lib.c b/cextern/expat/amiga/expat_lib.c deleted file mode 100755 index 039c48d213ab..000000000000 --- a/cextern/expat/amiga/expat_lib.c +++ /dev/null @@ -1,233 +0,0 @@ -/* -** Copyright (c) 2001-2007 Expat maintainers. -** -** Permission is hereby granted, free of charge, to any person obtaining -** a copy of this software and associated documentation files (the -** "Software"), to deal in the Software without restriction, including -** without limitation the rights to use, copy, modify, merge, publish, -** distribute, sublicense, and/or sell copies of the Software, and to -** permit persons to whom the Software is furnished to do so, subject to -** the following conditions: -** -** The above copyright notice and this permission notice shall be included -** in all copies or substantial portions of the Software. -** -** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -#include -#include - -#define LIBNAME "expat.library" -#define LIBPRI 0 -#define VERSION 4 -#define REVISION 2 -#define VSTRING "expat.library 4.2 (2.6.2007)" /* dd.mm.yyyy */ - - -static const char* __attribute__((used)) verstag = "\0$VER: " VSTRING; - - -struct ExpatBase { - struct Library libNode; - uint16 pad; - BPTR SegList; -}; - - -struct ExpatBase * libInit(struct ExpatBase *libBase, BPTR seglist, struct ExecIFace *ISys); -uint32 libObtain (struct LibraryManagerInterface *Self); -uint32 libRelease (struct LibraryManagerInterface *Self); -struct ExpatBase *libOpen (struct LibraryManagerInterface *Self, uint32 version); -BPTR libClose (struct LibraryManagerInterface *Self); -BPTR libExpunge (struct LibraryManagerInterface *Self); - - -static APTR lib_manager_vectors[] = { - libObtain, - libRelease, - NULL, - NULL, - libOpen, - libClose, - libExpunge, - NULL, - (APTR)-1, -}; - - -static struct TagItem lib_managerTags[] = { - { MIT_Name, (uint32)"__library" }, - { MIT_VectorTable, (uint32)lib_manager_vectors }, - { MIT_Version, 1 }, - { TAG_END, 0 } -}; - - -extern void *main_vectors[]; - -static struct TagItem lib_mainTags[] = { - { MIT_Name, (uint32)"main" }, - { MIT_VectorTable, (uint32)main_vectors }, - { MIT_Version, 1 }, - { TAG_END, 0 } -}; - - -static APTR libInterfaces[] = { - lib_managerTags, - lib_mainTags, - NULL -}; - - -static struct TagItem libCreateTags[] = { - { CLT_DataSize, sizeof(struct ExpatBase) }, - { CLT_InitFunc, (uint32)libInit }, - { CLT_Interfaces, (uint32)libInterfaces }, - { TAG_END, 0 } -}; - - -static struct Resident __attribute__((used)) lib_res = { - RTC_MATCHWORD, // rt_MatchWord - &lib_res, // rt_MatchTag - &lib_res+1, // rt_EndSkip - RTF_NATIVE | RTF_AUTOINIT, // rt_Flags - VERSION, // rt_Version - NT_LIBRARY, // rt_Type - LIBPRI, // rt_Pri - LIBNAME, // rt_Name - VSTRING, // rt_IdString - libCreateTags // rt_Init -}; - - -struct Library *DOSLib = 0; -struct Library *UtilityBase = 0; - -struct ExecIFace *IExec = 0; -struct DOSIFace *IDOS = 0; -struct UtilityIFace *IUtility = 0; - - -void _start() -{ -} - - -struct ExpatBase *libInit(struct ExpatBase *libBase, BPTR seglist, struct ExecIFace *ISys) -{ - libBase->libNode.lib_Node.ln_Type = NT_LIBRARY; - libBase->libNode.lib_Node.ln_Pri = LIBPRI; - libBase->libNode.lib_Node.ln_Name = LIBNAME; - libBase->libNode.lib_Flags = LIBF_SUMUSED|LIBF_CHANGED; - libBase->libNode.lib_Version = VERSION; - libBase->libNode.lib_Revision = REVISION; - libBase->libNode.lib_IdString = VSTRING; - libBase->SegList = seglist; - - IExec = ISys; - - DOSLib = OpenLibrary("dos.library", 51); - if ( DOSLib != 0 ) { - IDOS = (struct DOSIFace *)GetInterface(DOSLib, "main", 1, NULL); - if ( IDOS != 0 ) { - UtilityBase = OpenLibrary("utility.library", 51); - if ( UtilityBase != 0 ) { - IUtility = (struct UtilityIFace*)GetInterface(UtilityBase, "main", 1, NULL); - if ( IUtility != 0 ) { - return libBase; - } - - CloseLibrary(UtilityBase); - } - - DropInterface((struct Interface *)IDOS); - } - - CloseLibrary(DOSLib); - } - - return NULL; -} - - -uint32 libObtain( struct LibraryManagerInterface *Self ) -{ - ++Self->Data.RefCount; - return Self->Data.RefCount; -} - - -uint32 libRelease( struct LibraryManagerInterface *Self ) -{ - --Self->Data.RefCount; - return Self->Data.RefCount; -} - - -struct ExpatBase *libOpen( struct LibraryManagerInterface *Self, uint32 version ) -{ - struct ExpatBase *libBase; - - libBase = (struct ExpatBase *)Self->Data.LibBase; - - ++libBase->libNode.lib_OpenCnt; - libBase->libNode.lib_Flags &= ~LIBF_DELEXP; - - return libBase; -} - - -BPTR libClose( struct LibraryManagerInterface *Self ) -{ - struct ExpatBase *libBase; - - libBase = (struct ExpatBase *)Self->Data.LibBase; - - --libBase->libNode.lib_OpenCnt; - if ( libBase->libNode.lib_OpenCnt ) { - return 0; - } - - if ( libBase->libNode.lib_Flags & LIBF_DELEXP ) { - return (BPTR)Self->LibExpunge(); - } - else { - return 0; - } -} - - -BPTR libExpunge( struct LibraryManagerInterface *Self ) -{ - struct ExpatBase *libBase; - BPTR result = 0; - - libBase = (struct ExpatBase *)Self->Data.LibBase; - - if (libBase->libNode.lib_OpenCnt == 0) { - Remove(&libBase->libNode.lib_Node); - - result = libBase->SegList; - - DropInterface((struct Interface *)IUtility); - CloseLibrary(UtilityBase); - DropInterface((struct Interface *)IDOS); - CloseLibrary(DOSLib); - - DeleteLibrary(&libBase->libNode); - } - else { - libBase->libNode.lib_Flags |= LIBF_DELEXP; - } - - return result; -} diff --git a/cextern/expat/amiga/expat_vectors.c b/cextern/expat/amiga/expat_vectors.c deleted file mode 100755 index 14fb3be59594..000000000000 --- a/cextern/expat/amiga/expat_vectors.c +++ /dev/null @@ -1,505 +0,0 @@ -/* -** Copyright (c) 2001-2007 Expat maintainers. -** -** Permission is hereby granted, free of charge, to any person obtaining -** a copy of this software and associated documentation files (the -** "Software"), to deal in the Software without restriction, including -** without limitation the rights to use, copy, modify, merge, publish, -** distribute, sublicense, and/or sell copies of the Software, and to -** permit persons to whom the Software is furnished to do so, subject to -** the following conditions: -** -** The above copyright notice and this permission notice shall be included -** in all copies or substantial portions of the Software. -** -** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -#include -#include -#include -#include - -extern uint32 _Expat_Obtain(struct ExpatIFace *); -extern uint32 _Expat_Release(struct ExpatIFace *); -extern XML_Parser _Expat_XML_ParserCreate(struct ExpatIFace *, const XML_Char * encodingName); -extern XML_Parser _Expat_XML_ParserCreateNS(struct ExpatIFace *, const XML_Char * encodingName, XML_Char nsSep); -extern XML_Parser _Expat_XML_ParserCreate_MM(struct ExpatIFace *, const XML_Char * encoding, const XML_Memory_Handling_Suite * memsuite, const XML_Char * namespaceSeparator); -extern XML_Parser _Expat_XML_ExternalEntityParserCreate(struct ExpatIFace *, XML_Parser parser, const XML_Char * context, const XML_Char * encoding); -extern void _Expat_XML_ParserFree(struct ExpatIFace *, XML_Parser parser); -extern enum XML_Status _Expat_XML_Parse(struct ExpatIFace *, XML_Parser parser, const char * s, int len, int isFinal); -extern enum XML_Status _Expat_XML_ParseBuffer(struct ExpatIFace *, XML_Parser parser, int len, int isFinal); -extern void * _Expat_XML_GetBuffer(struct ExpatIFace *, XML_Parser parser, int len); -extern void _Expat_XML_SetStartElementHandler(struct ExpatIFace *, XML_Parser parser, XML_StartElementHandler start); -extern void _Expat_XML_SetEndElementHandler(struct ExpatIFace *, XML_Parser parser, XML_EndElementHandler end); -extern void _Expat_XML_SetElementHandler(struct ExpatIFace *, XML_Parser parser, XML_StartElementHandler start, XML_EndElementHandler end); -extern void _Expat_XML_SetCharacterDataHandler(struct ExpatIFace *, XML_Parser parser, XML_CharacterDataHandler handler); -extern void _Expat_XML_SetProcessingInstructionHandler(struct ExpatIFace *, XML_Parser parser, XML_ProcessingInstructionHandler handler); -extern void _Expat_XML_SetCommentHandler(struct ExpatIFace *, XML_Parser parser, XML_CommentHandler handler); -extern void _Expat_XML_SetStartCdataSectionHandler(struct ExpatIFace *, XML_Parser parser, XML_StartCdataSectionHandler start); -extern void _Expat_XML_SetEndCdataSectionHandler(struct ExpatIFace *, XML_Parser parser, XML_EndCdataSectionHandler end); -extern void _Expat_XML_SetCdataSectionHandler(struct ExpatIFace *, XML_Parser parser, XML_StartCdataSectionHandler start, XML_EndCdataSectionHandler end); -extern void _Expat_XML_SetDefaultHandler(struct ExpatIFace *, XML_Parser parser, XML_DefaultHandler handler); -extern void _Expat_XML_SetDefaultHandlerExpand(struct ExpatIFace *, XML_Parser parser, XML_DefaultHandler handler); -extern void _Expat_XML_SetExternalEntityRefHandler(struct ExpatIFace *, XML_Parser parser, XML_ExternalEntityRefHandler handler); -extern void _Expat_XML_SetExternalEntityRefHandlerArg(struct ExpatIFace *, XML_Parser parser, void * arg); -extern void _Expat_XML_SetUnknownEncodingHandler(struct ExpatIFace *, XML_Parser parser, XML_UnknownEncodingHandler handler, void * data); -extern void _Expat_XML_SetStartNamespaceDeclHandler(struct ExpatIFace *, XML_Parser parser, XML_StartNamespaceDeclHandler start); -extern void _Expat_XML_SetEndNamespaceDeclHandler(struct ExpatIFace *, XML_Parser parser, XML_EndNamespaceDeclHandler end); -extern void _Expat_XML_SetNamespaceDeclHandler(struct ExpatIFace *, XML_Parser parser, XML_StartNamespaceDeclHandler start, XML_EndNamespaceDeclHandler end); -extern void _Expat_XML_SetXmlDeclHandler(struct ExpatIFace *, XML_Parser parser, XML_XmlDeclHandler handler); -extern void _Expat_XML_SetStartDoctypeDeclHandler(struct ExpatIFace *, XML_Parser parser, XML_StartDoctypeDeclHandler start); -extern void _Expat_XML_SetEndDoctypeDeclHandler(struct ExpatIFace *, XML_Parser parser, XML_EndDoctypeDeclHandler end); -extern void _Expat_XML_SetDoctypeDeclHandler(struct ExpatIFace *, XML_Parser parser, XML_StartDoctypeDeclHandler start, XML_EndDoctypeDeclHandler end); -extern void _Expat_XML_SetElementDeclHandler(struct ExpatIFace *, XML_Parser parser, XML_ElementDeclHandler eldecl); -extern void _Expat_XML_SetAttlistDeclHandler(struct ExpatIFace *, XML_Parser parser, XML_AttlistDeclHandler attdecl); -extern void _Expat_XML_SetEntityDeclHandler(struct ExpatIFace *, XML_Parser parser, XML_EntityDeclHandler handler); -extern void _Expat_XML_SetUnparsedEntityDeclHandler(struct ExpatIFace *, XML_Parser parser, XML_UnparsedEntityDeclHandler handler); -extern void _Expat_XML_SetNotationDeclHandler(struct ExpatIFace *, XML_Parser parser, XML_NotationDeclHandler handler); -extern void _Expat_XML_SetNotStandaloneHandler(struct ExpatIFace *, XML_Parser parser, XML_NotStandaloneHandler handler); -extern enum XML_Error _Expat_XML_GetErrorCode(struct ExpatIFace *, XML_Parser parser); -extern const XML_LChar * _Expat_XML_ErrorString(struct ExpatIFace *, enum XML_Error code); -extern long _Expat_XML_GetCurrentByteIndex(struct ExpatIFace *, XML_Parser parser); -extern int _Expat_XML_GetCurrentLineNumber(struct ExpatIFace *, XML_Parser parser); -extern int _Expat_XML_GetCurrentColumnNumber(struct ExpatIFace *, XML_Parser parser); -extern int _Expat_XML_GetCurrentByteCount(struct ExpatIFace *, XML_Parser parser); -extern const char * _Expat_XML_GetInputContext(struct ExpatIFace *, XML_Parser parser, int * offset, int * size); -extern void _Expat_XML_SetUserData(struct ExpatIFace *, XML_Parser parser, void * userData); -extern void _Expat_XML_DefaultCurrent(struct ExpatIFace *, XML_Parser parser); -extern void _Expat_XML_UseParserAsHandlerArg(struct ExpatIFace *, XML_Parser parser); -extern enum XML_Status _Expat_XML_SetBase(struct ExpatIFace *, XML_Parser parser, const XML_Char * base); -extern const XML_Char * _Expat_XML_GetBase(struct ExpatIFace *, XML_Parser parser); -extern int _Expat_XML_GetSpecifiedAttributeCount(struct ExpatIFace *, XML_Parser parser); -extern int _Expat_XML_GetIdAttributeIndex(struct ExpatIFace *, XML_Parser parser); -extern enum XML_Status _Expat_XML_SetEncoding(struct ExpatIFace *, XML_Parser parser, const XML_Char * encoding); -extern int _Expat_XML_SetParamEntityParsing(struct ExpatIFace *, XML_Parser parser, enum XML_ParamEntityParsing parsing); -extern void _Expat_XML_SetReturnNSTriplet(struct ExpatIFace *, XML_Parser parser, int do_nst); -extern const XML_LChar * _Expat_XML_ExpatVersion(struct ExpatIFace *); -extern XML_Expat_Version _Expat_XML_ExpatVersionInfo(struct ExpatIFace *); -extern XML_Bool _Expat_XML_ParserReset(struct ExpatIFace *, XML_Parser parser, const XML_Char * encoding); -extern void _Expat_XML_SetSkippedEntityHandler(struct ExpatIFace *, XML_Parser parser, XML_SkippedEntityHandler handler); -extern enum XML_Error _Expat_XML_UseForeignDTD(struct ExpatIFace *, XML_Parser parser, XML_Bool useDTD); -extern const XML_Feature * _Expat_XML_GetFeatureList(struct ExpatIFace *); -extern enum XML_Status _Expat_XML_StopParser(struct ExpatIFace *, XML_Parser parser, XML_Bool resumable); -extern enum XML_Status _Expat_XML_ResumeParser(struct ExpatIFace *, XML_Parser parser); -extern void _Expat_XML_GetParsingStatus(struct ExpatIFace *, XML_Parser parser, XML_ParsingStatus * status); -extern void _Expat_XML_FreeContentModel(struct ExpatIFace *, XML_Parser parser, XML_Content * model); -extern void * _Expat_XML_MemMalloc(struct ExpatIFace *, XML_Parser parser, size_t size); -extern void * _Expat_XML_MemRealloc(struct ExpatIFace *, XML_Parser parser, void * ptr, size_t size); -extern void _Expat_XML_MemFree(struct ExpatIFace *, XML_Parser parser, void * ptr); - - -CONST APTR main_vectors[] = -{ - _Expat_Obtain, - _Expat_Release, - NULL, - NULL, - _Expat_XML_ParserCreate, - _Expat_XML_ParserCreateNS, - _Expat_XML_ParserCreate_MM, - _Expat_XML_ExternalEntityParserCreate, - _Expat_XML_ParserFree, - _Expat_XML_Parse, - _Expat_XML_ParseBuffer, - _Expat_XML_GetBuffer, - _Expat_XML_SetStartElementHandler, - _Expat_XML_SetEndElementHandler, - _Expat_XML_SetElementHandler, - _Expat_XML_SetCharacterDataHandler, - _Expat_XML_SetProcessingInstructionHandler, - _Expat_XML_SetCommentHandler, - _Expat_XML_SetStartCdataSectionHandler, - _Expat_XML_SetEndCdataSectionHandler, - _Expat_XML_SetCdataSectionHandler, - _Expat_XML_SetDefaultHandler, - _Expat_XML_SetDefaultHandlerExpand, - _Expat_XML_SetExternalEntityRefHandler, - _Expat_XML_SetExternalEntityRefHandlerArg, - _Expat_XML_SetUnknownEncodingHandler, - _Expat_XML_SetStartNamespaceDeclHandler, - _Expat_XML_SetEndNamespaceDeclHandler, - _Expat_XML_SetNamespaceDeclHandler, - _Expat_XML_SetXmlDeclHandler, - _Expat_XML_SetStartDoctypeDeclHandler, - _Expat_XML_SetEndDoctypeDeclHandler, - _Expat_XML_SetDoctypeDeclHandler, - _Expat_XML_SetElementDeclHandler, - _Expat_XML_SetAttlistDeclHandler, - _Expat_XML_SetEntityDeclHandler, - _Expat_XML_SetUnparsedEntityDeclHandler, - _Expat_XML_SetNotationDeclHandler, - _Expat_XML_SetNotStandaloneHandler, - _Expat_XML_GetErrorCode, - _Expat_XML_ErrorString, - _Expat_XML_GetCurrentByteIndex, - _Expat_XML_GetCurrentLineNumber, - _Expat_XML_GetCurrentColumnNumber, - _Expat_XML_GetCurrentByteCount, - _Expat_XML_GetInputContext, - _Expat_XML_SetUserData, - _Expat_XML_DefaultCurrent, - _Expat_XML_UseParserAsHandlerArg, - _Expat_XML_SetBase, - _Expat_XML_GetBase, - _Expat_XML_GetSpecifiedAttributeCount, - _Expat_XML_GetIdAttributeIndex, - _Expat_XML_SetEncoding, - _Expat_XML_SetParamEntityParsing, - _Expat_XML_SetReturnNSTriplet, - _Expat_XML_ExpatVersion, - _Expat_XML_ExpatVersionInfo, - _Expat_XML_ParserReset, - _Expat_XML_SetSkippedEntityHandler, - _Expat_XML_UseForeignDTD, - _Expat_XML_GetFeatureList, - _Expat_XML_StopParser, - _Expat_XML_ResumeParser, - _Expat_XML_GetParsingStatus, - _Expat_XML_FreeContentModel, - _Expat_XML_MemMalloc, - _Expat_XML_MemRealloc, - _Expat_XML_MemFree, - (APTR)-1 -}; - -uint32 _Expat_Obtain(struct ExpatIFace *Self) -{ - return ++Self->Data.RefCount; -} - -uint32 _Expat_Release(struct ExpatIFace *Self) -{ - return --Self->Data.RefCount; -} - -XML_Parser _Expat_XML_ParserCreate(struct ExpatIFace * Self, const XML_Char *encoding) -{ - return XML_ParserCreate(encoding); -} - -XML_Parser _Expat_XML_ParserCreateNS(struct ExpatIFace * Self, const XML_Char *encoding, XML_Char nsSep) -{ - return XML_ParserCreateNS(encoding, nsSep); -} - -XML_Parser _Expat_XML_ParserCreate_MM(struct ExpatIFace * Self, const XML_Char *encoding, const XML_Memory_Handling_Suite *memsuite, const XML_Char *namespaceSeparator) -{ - return XML_ParserCreate_MM(encoding, memsuite, namespaceSeparator); -} - -XML_Parser _Expat_XML_ExternalEntityParserCreate(struct ExpatIFace * Self, XML_Parser parser, const XML_Char *context, const XML_Char *encoding) -{ - return XML_ExternalEntityParserCreate(parser, context, encoding); -} - -void _Expat_XML_ParserFree(struct ExpatIFace *Self, XML_Parser parser) -{ - XML_ParserFree(parser); -} - -enum XML_Status _Expat_XML_Parse(struct ExpatIFace * Self, XML_Parser parser, const char * s, int len, int isFinal) -{ - return XML_Parse(parser, s, len, isFinal); -} - -enum XML_Status _Expat_XML_ParseBuffer(struct ExpatIFace * Self, XML_Parser parser, int len, int isFinal) -{ - return XML_ParseBuffer(parser, len, isFinal); -} - -void * _Expat_XML_GetBuffer(struct ExpatIFace * Self, XML_Parser parser, int len) -{ - return XML_GetBuffer(parser, len); -} - -void _Expat_XML_SetStartElementHandler(struct ExpatIFace * Self, XML_Parser parser, XML_StartElementHandler start) -{ - XML_SetStartElementHandler(parser, start); -} - -void _Expat_XML_SetEndElementHandler(struct ExpatIFace * Self, XML_Parser parser, XML_EndElementHandler end) -{ - XML_SetEndElementHandler(parser, end); -} - -void _Expat_XML_SetElementHandler(struct ExpatIFace * Self, XML_Parser parser, XML_StartElementHandler start, XML_EndElementHandler end) -{ - XML_SetElementHandler(parser, start, end); -} - -void _Expat_XML_SetCharacterDataHandler(struct ExpatIFace * Self, XML_Parser parser, XML_CharacterDataHandler handler) -{ - XML_SetCharacterDataHandler(parser, handler); -} - -void _Expat_XML_SetProcessingInstructionHandler(struct ExpatIFace * Self, XML_Parser parser, XML_ProcessingInstructionHandler handler) -{ - XML_SetProcessingInstructionHandler(parser, handler); -} - -void _Expat_XML_SetCommentHandler(struct ExpatIFace * Self, XML_Parser parser, XML_CommentHandler handler) -{ - XML_SetCommentHandler(parser, handler); -} - -void _Expat_XML_SetStartCdataSectionHandler(struct ExpatIFace * Self, XML_Parser parser, XML_StartCdataSectionHandler start) -{ - XML_SetStartCdataSectionHandler(parser, start); -} - -void _Expat_XML_SetEndCdataSectionHandler(struct ExpatIFace * Self, XML_Parser parser, XML_EndCdataSectionHandler end) -{ - XML_SetEndCdataSectionHandler(parser, end); -} - -void _Expat_XML_SetCdataSectionHandler(struct ExpatIFace * Self, XML_Parser parser, XML_StartCdataSectionHandler start, XML_EndCdataSectionHandler end) -{ - XML_SetCdataSectionHandler(parser, start, end); -} - -void _Expat_XML_SetDefaultHandler(struct ExpatIFace * Self, XML_Parser parser, XML_DefaultHandler handler) -{ - XML_SetDefaultHandler(parser, handler); -} - -void _Expat_XML_SetDefaultHandlerExpand(struct ExpatIFace * Self, XML_Parser parser, XML_DefaultHandler handler) -{ - XML_SetDefaultHandlerExpand(parser, handler); -} - -void _Expat_XML_SetExternalEntityRefHandler(struct ExpatIFace * Self, XML_Parser parser, XML_ExternalEntityRefHandler handler) -{ - XML_SetExternalEntityRefHandler(parser, handler); -} - -void _Expat_XML_SetExternalEntityRefHandlerArg(struct ExpatIFace * Self, XML_Parser parser, void * arg) -{ - XML_SetExternalEntityRefHandlerArg(parser, arg); -} - -void _Expat_XML_SetUnknownEncodingHandler(struct ExpatIFace * Self, XML_Parser parser, XML_UnknownEncodingHandler handler, void * data) -{ - XML_SetUnknownEncodingHandler(parser, handler, data); -} - -void _Expat_XML_SetStartNamespaceDeclHandler(struct ExpatIFace * Self, XML_Parser parser, XML_StartNamespaceDeclHandler start) -{ - XML_SetStartNamespaceDeclHandler(parser, start); -} - -void _Expat_XML_SetEndNamespaceDeclHandler(struct ExpatIFace * Self, XML_Parser parser, XML_EndNamespaceDeclHandler end) -{ - XML_SetEndNamespaceDeclHandler(parser, end); -} - -void _Expat_XML_SetNamespaceDeclHandler(struct ExpatIFace * Self, XML_Parser parser, XML_StartNamespaceDeclHandler start, XML_EndNamespaceDeclHandler end) -{ - XML_SetNamespaceDeclHandler(parser, start, end); -} - -void _Expat_XML_SetXmlDeclHandler(struct ExpatIFace * Self, XML_Parser parser, XML_XmlDeclHandler handler) -{ - XML_SetXmlDeclHandler(parser, handler); -} - -void _Expat_XML_SetStartDoctypeDeclHandler(struct ExpatIFace * Self, XML_Parser parser, XML_StartDoctypeDeclHandler start) -{ - XML_SetStartDoctypeDeclHandler(parser, start); -} - -void _Expat_XML_SetEndDoctypeDeclHandler(struct ExpatIFace * Self, XML_Parser parser, XML_EndDoctypeDeclHandler end) -{ - XML_SetEndDoctypeDeclHandler(parser, end); -} - -void _Expat_XML_SetDoctypeDeclHandler(struct ExpatIFace * Self, XML_Parser parser, XML_StartDoctypeDeclHandler start, XML_EndDoctypeDeclHandler end) -{ - XML_SetDoctypeDeclHandler(parser, start, end); -} - -void _Expat_XML_SetElementDeclHandler(struct ExpatIFace * Self, XML_Parser parser, XML_ElementDeclHandler eldecl) -{ - XML_SetElementDeclHandler(parser, eldecl); -} - -void _Expat_XML_SetAttlistDeclHandler(struct ExpatIFace * Self, XML_Parser parser, XML_AttlistDeclHandler attdecl) -{ - XML_SetAttlistDeclHandler(parser, attdecl); -} - -void _Expat_XML_SetEntityDeclHandler(struct ExpatIFace * Self, XML_Parser parser, XML_EntityDeclHandler handler) -{ - XML_SetEntityDeclHandler(parser, handler); -} - -void _Expat_XML_SetUnparsedEntityDeclHandler(struct ExpatIFace * Self, XML_Parser parser, XML_UnparsedEntityDeclHandler handler) -{ - XML_SetUnparsedEntityDeclHandler(parser, handler); -} - -void _Expat_XML_SetNotationDeclHandler(struct ExpatIFace * Self, XML_Parser parser, XML_NotationDeclHandler handler) -{ - XML_SetNotationDeclHandler(parser, handler); -} - -void _Expat_XML_SetNotStandaloneHandler(struct ExpatIFace * Self, XML_Parser parser, XML_NotStandaloneHandler handler) -{ - XML_SetNotStandaloneHandler(parser, handler); -} - -enum XML_Error _Expat_XML_GetErrorCode(struct ExpatIFace * Self, XML_Parser parser) -{ - return XML_GetErrorCode(parser); -} - -const XML_LChar * _Expat_XML_ErrorString(struct ExpatIFace * Self, enum XML_Error code) -{ - return XML_ErrorString(code); -} - -long _Expat_XML_GetCurrentByteIndex(struct ExpatIFace * Self, XML_Parser parser) -{ - return XML_GetCurrentByteIndex(parser); -} - -int _Expat_XML_GetCurrentLineNumber(struct ExpatIFace * Self, XML_Parser parser) -{ - return XML_GetCurrentLineNumber(parser); -} - -int _Expat_XML_GetCurrentColumnNumber(struct ExpatIFace * Self, XML_Parser parser) -{ - return XML_GetCurrentColumnNumber(parser); -} - -int _Expat_XML_GetCurrentByteCount(struct ExpatIFace * Self, XML_Parser parser) -{ - return XML_GetCurrentByteCount(parser); -} - -const char * _Expat_XML_GetInputContext(struct ExpatIFace * Self, XML_Parser parser, int * offset, int * size) -{ - return XML_GetInputContext(parser, offset, size); -} - -void _Expat_XML_SetUserData(struct ExpatIFace * Self, XML_Parser parser, void * userData) -{ - XML_SetUserData(parser, userData); -} - -void _Expat_XML_DefaultCurrent(struct ExpatIFace * Self, XML_Parser parser) -{ - XML_DefaultCurrent(parser); -} - -void _Expat_XML_UseParserAsHandlerArg(struct ExpatIFace * Self, XML_Parser parser) -{ - XML_UseParserAsHandlerArg(parser); -} - -enum XML_Status _Expat_XML_SetBase(struct ExpatIFace * Self, XML_Parser parser, const XML_Char *p) -{ - return XML_SetBase(parser, p); -} - -const XML_Char * _Expat_XML_GetBase(struct ExpatIFace * Self, XML_Parser parser) -{ - return XML_GetBase(parser); -} - -int _Expat_XML_GetSpecifiedAttributeCount(struct ExpatIFace * Self, XML_Parser parser) -{ - return XML_GetSpecifiedAttributeCount(parser); -} - -int _Expat_XML_GetIdAttributeIndex(struct ExpatIFace * Self, XML_Parser parser) -{ - return XML_GetIdAttributeIndex(parser); -} - -enum XML_Status _Expat_XML_SetEncoding(struct ExpatIFace * Self, XML_Parser parser, const XML_Char *encoding) -{ - return XML_SetEncoding(parser, encoding); -} - -int _Expat_XML_SetParamEntityParsing(struct ExpatIFace * Self, XML_Parser parser, enum XML_ParamEntityParsing parsing) -{ - return XML_SetParamEntityParsing(parser, parsing); -} - -void _Expat_XML_SetReturnNSTriplet(struct ExpatIFace * Self, XML_Parser parser, int do_nst) -{ - XML_SetReturnNSTriplet(parser, do_nst); -} - -const XML_LChar * _Expat_XML_ExpatVersion(struct ExpatIFace * Self) -{ - return XML_ExpatVersion(); -} - -XML_Expat_Version _Expat_XML_ExpatVersionInfo(struct ExpatIFace * Self) -{ - return XML_ExpatVersionInfo(); -} - -XML_Bool _Expat_XML_ParserReset(struct ExpatIFace * Self, XML_Parser parser, const XML_Char *encoding) -{ - return XML_ParserReset(parser, encoding); -} - -void _Expat_XML_SetSkippedEntityHandler(struct ExpatIFace * Self, XML_Parser parser, XML_SkippedEntityHandler handler) -{ - XML_SetSkippedEntityHandler(parser, handler); -} - -enum XML_Error _Expat_XML_UseForeignDTD(struct ExpatIFace * Self, XML_Parser parser, XML_Bool useDTD) -{ - return XML_UseForeignDTD(parser, useDTD); -} - -const XML_Feature * _Expat_XML_GetFeatureList(struct ExpatIFace * Self) -{ - return XML_GetFeatureList(); -} - -enum XML_Status _Expat_XML_StopParser(struct ExpatIFace * Self, XML_Parser parser, XML_Bool resumable) -{ - return XML_StopParser(parser, resumable); -} - -enum XML_Status _Expat_XML_ResumeParser(struct ExpatIFace * Self, XML_Parser parser) -{ - return XML_ResumeParser(parser); -} - -void _Expat_XML_GetParsingStatus(struct ExpatIFace * Self, XML_Parser parser, XML_ParsingStatus * status) -{ - XML_GetParsingStatus(parser, status); -} - -void _Expat_XML_FreeContentModel(struct ExpatIFace * Self, XML_Parser parser, XML_Content * model) -{ - XML_FreeContentModel(parser, model); -} - -void * _Expat_XML_MemMalloc(struct ExpatIFace * Self, XML_Parser parser, size_t size) -{ - return XML_MemMalloc(parser, size); -} - -void * _Expat_XML_MemRealloc(struct ExpatIFace * Self, XML_Parser parser, void * ptr, size_t size) -{ - XML_MemRealloc(parser, ptr, size); -} - -void _Expat_XML_MemFree(struct ExpatIFace * Self, XML_Parser parser, void * ptr) -{ - XML_MemFree(parser, ptr); -} diff --git a/cextern/expat/amiga/include/inline4/expat.h b/cextern/expat/amiga/include/inline4/expat.h deleted file mode 100755 index 1e3105d94eb0..000000000000 --- a/cextern/expat/amiga/include/inline4/expat.h +++ /dev/null @@ -1,94 +0,0 @@ -#ifndef INLINE4_EXPAT_H -#define INLINE4_EXPAT_H - -/* -** This file was auto generated by idltool 51.6. -** -** It provides compatibility to OS3 style library -** calls by substituting functions. -** -** Do not edit manually. -*/ - -#ifndef EXEC_TYPES_H -#include -#endif -#ifndef EXEC_EXEC_H -#include -#endif -#ifndef EXEC_INTERFACES_H -#include -#endif - -#ifndef LIBRARIES_EXPAT_H -#include -#endif - -/* Inline macros for Interface "main" */ -#define XML_ParserCreate(encodingName) IExpat->XML_ParserCreate(encodingName) -#define XML_ParserCreateNS(encodingName, nsSep) IExpat->XML_ParserCreateNS(encodingName, nsSep) -#define XML_ParserCreate_MM(encoding, memsuite, namespaceSeparator) IExpat->XML_ParserCreate_MM(encoding, memsuite, namespaceSeparator) -#define XML_ExternalEntityParserCreate(parser, context, encoding) IExpat->XML_ExternalEntityParserCreate(parser, context, encoding) -#define XML_ParserFree(parser) IExpat->XML_ParserFree(parser) -#define XML_Parse(parser, s, len, isFinal) IExpat->XML_Parse(parser, s, len, isFinal) -#define XML_ParseBuffer(parser, len, isFinal) IExpat->XML_ParseBuffer(parser, len, isFinal) -#define XML_GetBuffer(parser, len) IExpat->XML_GetBuffer(parser, len) -#define XML_SetStartElementHandler(parser, start) IExpat->XML_SetStartElementHandler(parser, start) -#define XML_SetEndElementHandler(parser, end) IExpat->XML_SetEndElementHandler(parser, end) -#define XML_SetElementHandler(parser, start, end) IExpat->XML_SetElementHandler(parser, start, end) -#define XML_SetCharacterDataHandler(parser, handler) IExpat->XML_SetCharacterDataHandler(parser, handler) -#define XML_SetProcessingInstructionHandler(parser, handler) IExpat->XML_SetProcessingInstructionHandler(parser, handler) -#define XML_SetCommentHandler(parser, handler) IExpat->XML_SetCommentHandler(parser, handler) -#define XML_SetStartCdataSectionHandler(parser, start) IExpat->XML_SetStartCdataSectionHandler(parser, start) -#define XML_SetEndCdataSectionHandler(parser, end) IExpat->XML_SetEndCdataSectionHandler(parser, end) -#define XML_SetCdataSectionHandler(parser, start, end) IExpat->XML_SetCdataSectionHandler(parser, start, end) -#define XML_SetDefaultHandler(parser, handler) IExpat->XML_SetDefaultHandler(parser, handler) -#define XML_SetDefaultHandlerExpand(parser, handler) IExpat->XML_SetDefaultHandlerExpand(parser, handler) -#define XML_SetExternalEntityRefHandler(parser, handler) IExpat->XML_SetExternalEntityRefHandler(parser, handler) -#define XML_SetExternalEntityRefHandlerArg(parser, arg) IExpat->XML_SetExternalEntityRefHandlerArg(parser, arg) -#define XML_SetUnknownEncodingHandler(parser, handler, data) IExpat->XML_SetUnknownEncodingHandler(parser, handler, data) -#define XML_SetStartNamespaceDeclHandler(parser, start) IExpat->XML_SetStartNamespaceDeclHandler(parser, start) -#define XML_SetEndNamespaceDeclHandler(parser, end) IExpat->XML_SetEndNamespaceDeclHandler(parser, end) -#define XML_SetNamespaceDeclHandler(parser, start, end) IExpat->XML_SetNamespaceDeclHandler(parser, start, end) -#define XML_SetXmlDeclHandler(parser, handler) IExpat->XML_SetXmlDeclHandler(parser, handler) -#define XML_SetStartDoctypeDeclHandler(parser, start) IExpat->XML_SetStartDoctypeDeclHandler(parser, start) -#define XML_SetEndDoctypeDeclHandler(parser, end) IExpat->XML_SetEndDoctypeDeclHandler(parser, end) -#define XML_SetDoctypeDeclHandler(parser, start, end) IExpat->XML_SetDoctypeDeclHandler(parser, start, end) -#define XML_SetElementDeclHandler(parser, eldecl) IExpat->XML_SetElementDeclHandler(parser, eldecl) -#define XML_SetAttlistDeclHandler(parser, attdecl) IExpat->XML_SetAttlistDeclHandler(parser, attdecl) -#define XML_SetEntityDeclHandler(parser, handler) IExpat->XML_SetEntityDeclHandler(parser, handler) -#define XML_SetUnparsedEntityDeclHandler(parser, handler) IExpat->XML_SetUnparsedEntityDeclHandler(parser, handler) -#define XML_SetNotationDeclHandler(parser, handler) IExpat->XML_SetNotationDeclHandler(parser, handler) -#define XML_SetNotStandaloneHandler(parser, handler) IExpat->XML_SetNotStandaloneHandler(parser, handler) -#define XML_GetErrorCode(parser) IExpat->XML_GetErrorCode(parser) -#define XML_ErrorString(code) IExpat->XML_ErrorString(code) -#define XML_GetCurrentByteIndex(parser) IExpat->XML_GetCurrentByteIndex(parser) -#define XML_GetCurrentLineNumber(parser) IExpat->XML_GetCurrentLineNumber(parser) -#define XML_GetCurrentColumnNumber(parser) IExpat->XML_GetCurrentColumnNumber(parser) -#define XML_GetCurrentByteCount(parser) IExpat->XML_GetCurrentByteCount(parser) -#define XML_GetInputContext(parser, offset, size) IExpat->XML_GetInputContext(parser, offset, size) -#define XML_SetUserData(parser, userData) IExpat->XML_SetUserData(parser, userData) -#define XML_DefaultCurrent(parser) IExpat->XML_DefaultCurrent(parser) -#define XML_UseParserAsHandlerArg(parser) IExpat->XML_UseParserAsHandlerArg(parser) -#define XML_SetBase(parser, base) IExpat->XML_SetBase(parser, base) -#define XML_GetBase(parser) IExpat->XML_GetBase(parser) -#define XML_GetSpecifiedAttributeCount(parser) IExpat->XML_GetSpecifiedAttributeCount(parser) -#define XML_GetIdAttributeIndex(parser) IExpat->XML_GetIdAttributeIndex(parser) -#define XML_SetEncoding(parser, encoding) IExpat->XML_SetEncoding(parser, encoding) -#define XML_SetParamEntityParsing(parser, parsing) IExpat->XML_SetParamEntityParsing(parser, parsing) -#define XML_SetReturnNSTriplet(parser, do_nst) IExpat->XML_SetReturnNSTriplet(parser, do_nst) -#define XML_ExpatVersion() IExpat->XML_ExpatVersion() -#define XML_ExpatVersionInfo() IExpat->XML_ExpatVersionInfo() -#define XML_ParserReset(parser, encoding) IExpat->XML_ParserReset(parser, encoding) -#define XML_SetSkippedEntityHandler(parser, handler) IExpat->XML_SetSkippedEntityHandler(parser, handler) -#define XML_UseForeignDTD(parser, useDTD) IExpat->XML_UseForeignDTD(parser, useDTD) -#define XML_GetFeatureList() IExpat->XML_GetFeatureList() -#define XML_StopParser(parser, resumable) IExpat->XML_StopParser(parser, resumable) -#define XML_ResumeParser(parser) IExpat->XML_ResumeParser(parser) -#define XML_GetParsingStatus(parser, status) IExpat->XML_GetParsingStatus(parser, status) -#define XML_FreeContentModel(parser, model) IExpat->XML_FreeContentModel(parser, model) -#define XML_MemMalloc(parser, size) IExpat->XML_MemMalloc(parser, size) -#define XML_MemRealloc(parser, ptr, size) IExpat->XML_MemRealloc(parser, ptr, size) -#define XML_MemFree(parser, ptr) IExpat->XML_MemFree(parser, ptr) - -#endif /* INLINE4_EXPAT_H */ diff --git a/cextern/expat/amiga/include/interfaces/expat.h b/cextern/expat/amiga/include/interfaces/expat.h deleted file mode 100755 index e9bdf4aa12a7..000000000000 --- a/cextern/expat/amiga/include/interfaces/expat.h +++ /dev/null @@ -1,98 +0,0 @@ -#ifndef EXPAT_INTERFACE_DEF_H -#define EXPAT_INTERFACE_DEF_H - -/* -** This file was machine generated by idltool 51.6. -** Do not edit -*/ - -#ifndef EXEC_TYPES_H -#include -#endif -#ifndef EXEC_EXEC_H -#include -#endif -#ifndef EXEC_INTERFACES_H -#include -#endif - -#ifndef LIBRARIES_EXPAT_H -#include -#endif - -struct ExpatIFace -{ - struct InterfaceData Data; - - uint32 APICALL (*Obtain)(struct ExpatIFace *Self); - uint32 APICALL (*Release)(struct ExpatIFace *Self); - void APICALL (*Expunge)(struct ExpatIFace *Self); - struct Interface * APICALL (*Clone)(struct ExpatIFace *Self); - XML_Parser APICALL (*XML_ParserCreate)(struct ExpatIFace *Self, const XML_Char * encodingName); - XML_Parser APICALL (*XML_ParserCreateNS)(struct ExpatIFace *Self, const XML_Char * encodingName, XML_Char nsSep); - XML_Parser APICALL (*XML_ParserCreate_MM)(struct ExpatIFace *Self, const XML_Char * encoding, const XML_Memory_Handling_Suite * memsuite, const XML_Char * namespaceSeparator); - XML_Parser APICALL (*XML_ExternalEntityParserCreate)(struct ExpatIFace *Self, XML_Parser parser, const XML_Char * context, const XML_Char * encoding); - void APICALL (*XML_ParserFree)(struct ExpatIFace *Self, XML_Parser parser); - enum XML_Status APICALL (*XML_Parse)(struct ExpatIFace *Self, XML_Parser parser, const char * s, int len, int isFinal); - enum XML_Status APICALL (*XML_ParseBuffer)(struct ExpatIFace *Self, XML_Parser parser, int len, int isFinal); - void * APICALL (*XML_GetBuffer)(struct ExpatIFace *Self, XML_Parser parser, int len); - void APICALL (*XML_SetStartElementHandler)(struct ExpatIFace *Self, XML_Parser parser, XML_StartElementHandler start); - void APICALL (*XML_SetEndElementHandler)(struct ExpatIFace *Self, XML_Parser parser, XML_EndElementHandler end); - void APICALL (*XML_SetElementHandler)(struct ExpatIFace *Self, XML_Parser parser, XML_StartElementHandler start, XML_EndElementHandler end); - void APICALL (*XML_SetCharacterDataHandler)(struct ExpatIFace *Self, XML_Parser parser, XML_CharacterDataHandler handler); - void APICALL (*XML_SetProcessingInstructionHandler)(struct ExpatIFace *Self, XML_Parser parser, XML_ProcessingInstructionHandler handler); - void APICALL (*XML_SetCommentHandler)(struct ExpatIFace *Self, XML_Parser parser, XML_CommentHandler handler); - void APICALL (*XML_SetStartCdataSectionHandler)(struct ExpatIFace *Self, XML_Parser parser, XML_StartCdataSectionHandler start); - void APICALL (*XML_SetEndCdataSectionHandler)(struct ExpatIFace *Self, XML_Parser parser, XML_EndCdataSectionHandler end); - void APICALL (*XML_SetCdataSectionHandler)(struct ExpatIFace *Self, XML_Parser parser, XML_StartCdataSectionHandler start, XML_EndCdataSectionHandler end); - void APICALL (*XML_SetDefaultHandler)(struct ExpatIFace *Self, XML_Parser parser, XML_DefaultHandler handler); - void APICALL (*XML_SetDefaultHandlerExpand)(struct ExpatIFace *Self, XML_Parser parser, XML_DefaultHandler handler); - void APICALL (*XML_SetExternalEntityRefHandler)(struct ExpatIFace *Self, XML_Parser parser, XML_ExternalEntityRefHandler handler); - void APICALL (*XML_SetExternalEntityRefHandlerArg)(struct ExpatIFace *Self, XML_Parser parser, void * arg); - void APICALL (*XML_SetUnknownEncodingHandler)(struct ExpatIFace *Self, XML_Parser parser, XML_UnknownEncodingHandler handler, void * data); - void APICALL (*XML_SetStartNamespaceDeclHandler)(struct ExpatIFace *Self, XML_Parser parser, XML_StartNamespaceDeclHandler start); - void APICALL (*XML_SetEndNamespaceDeclHandler)(struct ExpatIFace *Self, XML_Parser parser, XML_EndNamespaceDeclHandler end); - void APICALL (*XML_SetNamespaceDeclHandler)(struct ExpatIFace *Self, XML_Parser parser, XML_StartNamespaceDeclHandler start, XML_EndNamespaceDeclHandler end); - void APICALL (*XML_SetXmlDeclHandler)(struct ExpatIFace *Self, XML_Parser parser, XML_XmlDeclHandler handler); - void APICALL (*XML_SetStartDoctypeDeclHandler)(struct ExpatIFace *Self, XML_Parser parser, XML_StartDoctypeDeclHandler start); - void APICALL (*XML_SetEndDoctypeDeclHandler)(struct ExpatIFace *Self, XML_Parser parser, XML_EndDoctypeDeclHandler end); - void APICALL (*XML_SetDoctypeDeclHandler)(struct ExpatIFace *Self, XML_Parser parser, XML_StartDoctypeDeclHandler start, XML_EndDoctypeDeclHandler end); - void APICALL (*XML_SetElementDeclHandler)(struct ExpatIFace *Self, XML_Parser parser, XML_ElementDeclHandler eldecl); - void APICALL (*XML_SetAttlistDeclHandler)(struct ExpatIFace *Self, XML_Parser parser, XML_AttlistDeclHandler attdecl); - void APICALL (*XML_SetEntityDeclHandler)(struct ExpatIFace *Self, XML_Parser parser, XML_EntityDeclHandler handler); - void APICALL (*XML_SetUnparsedEntityDeclHandler)(struct ExpatIFace *Self, XML_Parser parser, XML_UnparsedEntityDeclHandler handler); - void APICALL (*XML_SetNotationDeclHandler)(struct ExpatIFace *Self, XML_Parser parser, XML_NotationDeclHandler handler); - void APICALL (*XML_SetNotStandaloneHandler)(struct ExpatIFace *Self, XML_Parser parser, XML_NotStandaloneHandler handler); - enum XML_Error APICALL (*XML_GetErrorCode)(struct ExpatIFace *Self, XML_Parser parser); - const XML_LChar * APICALL (*XML_ErrorString)(struct ExpatIFace *Self, enum XML_Error code); - long APICALL (*XML_GetCurrentByteIndex)(struct ExpatIFace *Self, XML_Parser parser); - int APICALL (*XML_GetCurrentLineNumber)(struct ExpatIFace *Self, XML_Parser parser); - int APICALL (*XML_GetCurrentColumnNumber)(struct ExpatIFace *Self, XML_Parser parser); - int APICALL (*XML_GetCurrentByteCount)(struct ExpatIFace *Self, XML_Parser parser); - const char * APICALL (*XML_GetInputContext)(struct ExpatIFace *Self, XML_Parser parser, int * offset, int * size); - void APICALL (*XML_SetUserData)(struct ExpatIFace *Self, XML_Parser parser, void * userData); - void APICALL (*XML_DefaultCurrent)(struct ExpatIFace *Self, XML_Parser parser); - void APICALL (*XML_UseParserAsHandlerArg)(struct ExpatIFace *Self, XML_Parser parser); - enum XML_Status APICALL (*XML_SetBase)(struct ExpatIFace *Self, XML_Parser parser, const XML_Char * base); - const XML_Char * APICALL (*XML_GetBase)(struct ExpatIFace *Self, XML_Parser parser); - int APICALL (*XML_GetSpecifiedAttributeCount)(struct ExpatIFace *Self, XML_Parser parser); - int APICALL (*XML_GetIdAttributeIndex)(struct ExpatIFace *Self, XML_Parser parser); - enum XML_Status APICALL (*XML_SetEncoding)(struct ExpatIFace *Self, XML_Parser parser, const XML_Char * encoding); - int APICALL (*XML_SetParamEntityParsing)(struct ExpatIFace *Self, XML_Parser parser, enum XML_ParamEntityParsing parsing); - void APICALL (*XML_SetReturnNSTriplet)(struct ExpatIFace *Self, XML_Parser parser, int do_nst); - const XML_LChar * APICALL (*XML_ExpatVersion)(struct ExpatIFace *Self); - XML_Expat_Version APICALL (*XML_ExpatVersionInfo)(struct ExpatIFace *Self); - XML_Bool APICALL (*XML_ParserReset)(struct ExpatIFace *Self, XML_Parser parser, const XML_Char * encoding); - void APICALL (*XML_SetSkippedEntityHandler)(struct ExpatIFace *Self, XML_Parser parser, XML_SkippedEntityHandler handler); - enum XML_Error APICALL (*XML_UseForeignDTD)(struct ExpatIFace *Self, XML_Parser parser, XML_Bool useDTD); - const XML_Feature * APICALL (*XML_GetFeatureList)(struct ExpatIFace *Self); - enum XML_Status APICALL (*XML_StopParser)(struct ExpatIFace *Self, XML_Parser parser, XML_Bool resumable); - enum XML_Status APICALL (*XML_ResumeParser)(struct ExpatIFace *Self, XML_Parser parser); - void APICALL (*XML_GetParsingStatus)(struct ExpatIFace *Self, XML_Parser parser, XML_ParsingStatus * status); - void APICALL (*XML_FreeContentModel)(struct ExpatIFace *Self, XML_Parser parser, XML_Content * model); - void * APICALL (*XML_MemMalloc)(struct ExpatIFace *Self, XML_Parser parser, size_t size); - void * APICALL (*XML_MemRealloc)(struct ExpatIFace *Self, XML_Parser parser, void * ptr, size_t size); - void APICALL (*XML_MemFree)(struct ExpatIFace *Self, XML_Parser parser, void * ptr); -}; - -#endif /* EXPAT_INTERFACE_DEF_H */ diff --git a/cextern/expat/amiga/include/libraries/expat.h b/cextern/expat/amiga/include/libraries/expat.h deleted file mode 100755 index 1b57387c7b1c..000000000000 --- a/cextern/expat/amiga/include/libraries/expat.h +++ /dev/null @@ -1,566 +0,0 @@ -#ifndef LIBRARIES_EXPAT_H -#define LIBRARIES_EXPAT_H - -/* -** Copyright (c) 2001-2007 Expat maintainers. -** -** Permission is hereby granted, free of charge, to any person obtaining -** a copy of this software and associated documentation files (the -** "Software"), to deal in the Software without restriction, including -** without limitation the rights to use, copy, modify, merge, publish, -** distribute, sublicense, and/or sell copies of the Software, and to -** permit persons to whom the Software is furnished to do so, subject to -** the following conditions: -** -** The above copyright notice and this permission notice shall be included -** in all copies or substantial portions of the Software. -** -** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - - -/****************************************************************************/ - -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef __GNUC__ - #ifdef __PPC__ - #pragma pack(2) - #endif -#elif defined(__VBCC__) - #pragma amiga-align -#endif - -/****************************************************************************/ - - -#include - -#ifndef XMLCALL -#define XMLCALL -#endif - -typedef char XML_Char; -typedef char XML_LChar; -typedef long XML_Index; -typedef unsigned long XML_Size; - -struct XML_ParserStruct; -typedef struct XML_ParserStruct *XML_Parser; - -typedef unsigned char XML_Bool; -#define XML_TRUE ((XML_Bool) 1) -#define XML_FALSE ((XML_Bool) 0) - -enum XML_Status { - XML_STATUS_ERROR = 0, -#define XML_STATUS_ERROR XML_STATUS_ERROR - XML_STATUS_OK = 1, -#define XML_STATUS_OK XML_STATUS_OK - XML_STATUS_SUSPENDED = 2, -#define XML_STATUS_SUSPENDED XML_STATUS_SUSPENDED -}; - -enum XML_Error { - XML_ERROR_NONE, - XML_ERROR_NO_MEMORY, - XML_ERROR_SYNTAX, - XML_ERROR_NO_ELEMENTS, - XML_ERROR_INVALID_TOKEN, - XML_ERROR_UNCLOSED_TOKEN, - XML_ERROR_PARTIAL_CHAR, - XML_ERROR_TAG_MISMATCH, - XML_ERROR_DUPLICATE_ATTRIBUTE, - XML_ERROR_JUNK_AFTER_DOC_ELEMENT, - XML_ERROR_PARAM_ENTITY_REF, - XML_ERROR_UNDEFINED_ENTITY, - XML_ERROR_RECURSIVE_ENTITY_REF, - XML_ERROR_ASYNC_ENTITY, - XML_ERROR_BAD_CHAR_REF, - XML_ERROR_BINARY_ENTITY_REF, - XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF, - XML_ERROR_MISPLACED_XML_PI, - XML_ERROR_UNKNOWN_ENCODING, - XML_ERROR_INCORRECT_ENCODING, - XML_ERROR_UNCLOSED_CDATA_SECTION, - XML_ERROR_EXTERNAL_ENTITY_HANDLING, - XML_ERROR_NOT_STANDALONE, - XML_ERROR_UNEXPECTED_STATE, - XML_ERROR_ENTITY_DECLARED_IN_PE, - XML_ERROR_FEATURE_REQUIRES_XML_DTD, - XML_ERROR_CANT_CHANGE_FEATURE_ONCE_PARSING, - XML_ERROR_UNBOUND_PREFIX, - XML_ERROR_UNDECLARING_PREFIX, - XML_ERROR_INCOMPLETE_PE, - XML_ERROR_XML_DECL, - XML_ERROR_TEXT_DECL, - XML_ERROR_PUBLICID, - XML_ERROR_SUSPENDED, - XML_ERROR_NOT_SUSPENDED, - XML_ERROR_ABORTED, - XML_ERROR_FINISHED, - XML_ERROR_SUSPEND_PE, - XML_ERROR_RESERVED_PREFIX_XML, - XML_ERROR_RESERVED_PREFIX_XMLNS, - XML_ERROR_RESERVED_NAMESPACE_URI -}; - -enum XML_Content_Type { - XML_CTYPE_EMPTY = 1, - XML_CTYPE_ANY, - XML_CTYPE_MIXED, - XML_CTYPE_NAME, - XML_CTYPE_CHOICE, - XML_CTYPE_SEQ -}; - -enum XML_Content_Quant { - XML_CQUANT_NONE, - XML_CQUANT_OPT, - XML_CQUANT_REP, - XML_CQUANT_PLUS -}; - -typedef struct XML_cp XML_Content; - -struct XML_cp { - enum XML_Content_Type type; - enum XML_Content_Quant quant; - XML_Char * name; - unsigned int numchildren; - XML_Content * children; -}; - - -typedef void (*XML_ElementDeclHandler) (void *userData, - const XML_Char *name, - XML_Content *model); - -void -XML_SetElementDeclHandler(XML_Parser parser, - XML_ElementDeclHandler eldecl); - -typedef void (*XML_AttlistDeclHandler) ( - void *userData, - const XML_Char *elname, - const XML_Char *attname, - const XML_Char *att_type, - const XML_Char *dflt, - int isrequired); - -void -XML_SetAttlistDeclHandler(XML_Parser parser, - XML_AttlistDeclHandler attdecl); - -typedef void (*XML_XmlDeclHandler) (void *userData, - const XML_Char *version, - const XML_Char *encoding, - int standalone); - -void -XML_SetXmlDeclHandler(XML_Parser parser, - XML_XmlDeclHandler xmldecl); - - -typedef struct { - void *(*malloc_fcn)(size_t size); - void *(*realloc_fcn)(void *ptr, size_t size); - void (*free_fcn)(void *ptr); -} XML_Memory_Handling_Suite; - -XML_Parser -XML_ParserCreate(const XML_Char *encoding); - -XML_Parser -XML_ParserCreateNS(const XML_Char *encoding, XML_Char namespaceSeparator); - - -XML_Parser -XML_ParserCreate_MM(const XML_Char *encoding, - const XML_Memory_Handling_Suite *memsuite, - const XML_Char *namespaceSeparator); - -XML_Bool -XML_ParserReset(XML_Parser parser, const XML_Char *encoding); - -typedef void (*XML_StartElementHandler) (void *userData, - const XML_Char *name, - const XML_Char **atts); - -typedef void (*XML_EndElementHandler) (void *userData, - const XML_Char *name); - - -typedef void (*XML_CharacterDataHandler) (void *userData, - const XML_Char *s, - int len); - -typedef void (*XML_ProcessingInstructionHandler) ( - void *userData, - const XML_Char *target, - const XML_Char *data); - -typedef void (*XML_CommentHandler) (void *userData, - const XML_Char *data); - -typedef void (*XML_StartCdataSectionHandler) (void *userData); -typedef void (*XML_EndCdataSectionHandler) (void *userData); - -typedef void (*XML_DefaultHandler) (void *userData, - const XML_Char *s, - int len); - -typedef void (*XML_StartDoctypeDeclHandler) ( - void *userData, - const XML_Char *doctypeName, - const XML_Char *sysid, - const XML_Char *pubid, - int has_internal_subset); - -typedef void (*XML_EndDoctypeDeclHandler)(void *userData); - -typedef void (*XML_EntityDeclHandler) ( - void *userData, - const XML_Char *entityName, - int is_parameter_entity, - const XML_Char *value, - int value_length, - const XML_Char *base, - const XML_Char *systemId, - const XML_Char *publicId, - const XML_Char *notationName); - -void -XML_SetEntityDeclHandler(XML_Parser parser, - XML_EntityDeclHandler handler); - -typedef void (*XML_UnparsedEntityDeclHandler) ( - void *userData, - const XML_Char *entityName, - const XML_Char *base, - const XML_Char *systemId, - const XML_Char *publicId, - const XML_Char *notationName); - -typedef void (*XML_NotationDeclHandler) ( - void *userData, - const XML_Char *notationName, - const XML_Char *base, - const XML_Char *systemId, - const XML_Char *publicId); - -typedef void (*XML_StartNamespaceDeclHandler) ( - void *userData, - const XML_Char *prefix, - const XML_Char *uri); - -typedef void (*XML_EndNamespaceDeclHandler) ( - void *userData, - const XML_Char *prefix); - -typedef int (*XML_NotStandaloneHandler) (void *userData); - -typedef int (*XML_ExternalEntityRefHandler) ( - XML_Parser parser, - const XML_Char *context, - const XML_Char *base, - const XML_Char *systemId, - const XML_Char *publicId); - -typedef void (*XML_SkippedEntityHandler) ( - void *userData, - const XML_Char *entityName, - int is_parameter_entity); - -typedef struct { - int map[256]; - void *data; - int (*convert)(void *data, const char *s); - void (*release)(void *data); -} XML_Encoding; - -typedef int (*XML_UnknownEncodingHandler) ( - void *encodingHandlerData, - const XML_Char *name, - XML_Encoding *info); - -void -XML_SetElementHandler(XML_Parser parser, - XML_StartElementHandler start, - XML_EndElementHandler end); - -void -XML_SetStartElementHandler(XML_Parser parser, - XML_StartElementHandler handler); - -void -XML_SetEndElementHandler(XML_Parser parser, - XML_EndElementHandler handler); - -void -XML_SetCharacterDataHandler(XML_Parser parser, - XML_CharacterDataHandler handler); - -void -XML_SetProcessingInstructionHandler(XML_Parser parser, - XML_ProcessingInstructionHandler handler); -void -XML_SetCommentHandler(XML_Parser parser, - XML_CommentHandler handler); - -void -XML_SetCdataSectionHandler(XML_Parser parser, - XML_StartCdataSectionHandler start, - XML_EndCdataSectionHandler end); - -void -XML_SetStartCdataSectionHandler(XML_Parser parser, - XML_StartCdataSectionHandler start); - -void -XML_SetEndCdataSectionHandler(XML_Parser parser, - XML_EndCdataSectionHandler end); - -void -XML_SetDefaultHandler(XML_Parser parser, - XML_DefaultHandler handler); - -void -XML_SetDefaultHandlerExpand(XML_Parser parser, - XML_DefaultHandler handler); - -void -XML_SetDoctypeDeclHandler(XML_Parser parser, - XML_StartDoctypeDeclHandler start, - XML_EndDoctypeDeclHandler end); - -void -XML_SetStartDoctypeDeclHandler(XML_Parser parser, - XML_StartDoctypeDeclHandler start); - -void -XML_SetEndDoctypeDeclHandler(XML_Parser parser, - XML_EndDoctypeDeclHandler end); - -void -XML_SetUnparsedEntityDeclHandler(XML_Parser parser, - XML_UnparsedEntityDeclHandler handler); - -void -XML_SetNotationDeclHandler(XML_Parser parser, - XML_NotationDeclHandler handler); - -void -XML_SetNamespaceDeclHandler(XML_Parser parser, - XML_StartNamespaceDeclHandler start, - XML_EndNamespaceDeclHandler end); - -void -XML_SetStartNamespaceDeclHandler(XML_Parser parser, - XML_StartNamespaceDeclHandler start); - -void -XML_SetEndNamespaceDeclHandler(XML_Parser parser, - XML_EndNamespaceDeclHandler end); - -void -XML_SetNotStandaloneHandler(XML_Parser parser, - XML_NotStandaloneHandler handler); - -void -XML_SetExternalEntityRefHandler(XML_Parser parser, - XML_ExternalEntityRefHandler handler); - -void -XML_SetExternalEntityRefHandlerArg(XML_Parser parser, - void *arg); - -void -XML_SetSkippedEntityHandler(XML_Parser parser, - XML_SkippedEntityHandler handler); - -void -XML_SetUnknownEncodingHandler(XML_Parser parser, - XML_UnknownEncodingHandler handler, - void *encodingHandlerData); - -void -XML_DefaultCurrent(XML_Parser parser); - -void -XML_SetReturnNSTriplet(XML_Parser parser, int do_nst); - -void -XML_SetUserData(XML_Parser parser, void *userData); - -#define XML_GetUserData(parser) (*(void **)(parser)) - -enum XML_Status -XML_SetEncoding(XML_Parser parser, const XML_Char *encoding); - -void -XML_UseParserAsHandlerArg(XML_Parser parser); - -enum XML_Error -XML_UseForeignDTD(XML_Parser parser, XML_Bool useDTD); - - -enum XML_Status -XML_SetBase(XML_Parser parser, const XML_Char *base); - -const XML_Char * -XML_GetBase(XML_Parser parser); - -int -XML_GetSpecifiedAttributeCount(XML_Parser parser); - -int -XML_GetIdAttributeIndex(XML_Parser parser); - -enum XML_Status -XML_Parse(XML_Parser parser, const char *s, int len, int isFinal); - -void * -XML_GetBuffer(XML_Parser parser, int len); - -enum XML_Status -XML_ParseBuffer(XML_Parser parser, int len, int isFinal); - -enum XML_Status -XML_StopParser(XML_Parser parser, XML_Bool resumable); - -enum XML_Status -XML_ResumeParser(XML_Parser parser); - -enum XML_Parsing { - XML_INITIALIZED, - XML_PARSING, - XML_FINISHED, - XML_SUSPENDED -}; - -typedef struct { - enum XML_Parsing parsing; - XML_Bool finalBuffer; -} XML_ParsingStatus; - -void -XML_GetParsingStatus(XML_Parser parser, XML_ParsingStatus *status); - -XML_Parser -XML_ExternalEntityParserCreate(XML_Parser parser, - const XML_Char *context, - const XML_Char *encoding); - -enum XML_ParamEntityParsing { - XML_PARAM_ENTITY_PARSING_NEVER, - XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE, - XML_PARAM_ENTITY_PARSING_ALWAYS -}; - -int -XML_SetParamEntityParsing(XML_Parser parser, - enum XML_ParamEntityParsing parsing); - -enum XML_Error -XML_GetErrorCode(XML_Parser parser); - -int XML_GetCurrentLineNumber(XML_Parser parser); -int XML_GetCurrentColumnNumber(XML_Parser parser); -long XML_GetCurrentByteIndex(XML_Parser parser); - -int -XML_GetCurrentByteCount(XML_Parser parser); - -const char * -XML_GetInputContext(XML_Parser parser, - int *offset, - int *size); - -#define XML_GetErrorLineNumber XML_GetCurrentLineNumber -#define XML_GetErrorColumnNumber XML_GetCurrentColumnNumber -#define XML_GetErrorByteIndex XML_GetCurrentByteIndex - -void -XML_FreeContentModel(XML_Parser parser, XML_Content *model); - -void * -XML_MemMalloc(XML_Parser parser, size_t size); - -void * -XML_MemRealloc(XML_Parser parser, void *ptr, size_t size); - -void -XML_MemFree(XML_Parser parser, void *ptr); - -void -XML_ParserFree(XML_Parser parser); - -const XML_LChar * -XML_ErrorString(enum XML_Error code); - -const XML_LChar * -XML_ExpatVersion(void); - -typedef struct { - int major; - int minor; - int micro; -} XML_Expat_Version; - -XML_Expat_Version -XML_ExpatVersionInfo(void); - -enum XML_FeatureEnum { - XML_FEATURE_END = 0, - XML_FEATURE_UNICODE, - XML_FEATURE_UNICODE_WCHAR_T, - XML_FEATURE_DTD, - XML_FEATURE_CONTEXT_BYTES, - XML_FEATURE_MIN_SIZE, - XML_FEATURE_SIZEOF_XML_CHAR, - XML_FEATURE_SIZEOF_XML_LCHAR, - XML_FEATURE_NS, - XML_FEATURE_LARGE_SIZE -}; - -typedef struct { - enum XML_FeatureEnum feature; - const XML_LChar *name; - long int value; -} XML_Feature; - -const XML_Feature * -XML_GetFeatureList(void); - - -#define XML_MAJOR_VERSION 2 -#define XML_MINOR_VERSION 0 -#define XML_MICRO_VERSION 1 - - -/****************************************************************************/ - -#ifdef __GNUC__ - #ifdef __PPC__ - #pragma pack() - #endif -#elif defined(__VBCC__) - #pragma default-align -#endif - -#ifdef __cplusplus -} -#endif - -/****************************************************************************/ - -#endif /* EXPAT_EXPAT_H */ diff --git a/cextern/expat/amiga/include/proto/expat.h b/cextern/expat/amiga/include/proto/expat.h deleted file mode 100755 index 90bf62c46ea9..000000000000 --- a/cextern/expat/amiga/include/proto/expat.h +++ /dev/null @@ -1,52 +0,0 @@ -#ifndef PROTO_EXPAT_H -#define PROTO_EXPAT_H - -#ifndef LIBRARIES_EXPAT_H -#include -#endif - -/****************************************************************************/ - -#ifndef __NOLIBBASE__ - #ifndef __USE_BASETYPE__ - extern struct Library * ExpatBase; - #else - extern struct Library * ExpatBase; - #endif /* __USE_BASETYPE__ */ -#endif /* __NOLIBBASE__ */ - -/****************************************************************************/ - -#ifdef __amigaos4__ - #include - #ifdef __USE_INLINE__ - #include - #endif /* __USE_INLINE__ */ - #ifndef CLIB_EXPAT_PROTOS_H - #define CLIB_EXPAT_PROTOS_H 1 - #endif /* CLIB_EXPAT_PROTOS_H */ - #ifndef __NOGLOBALIFACE__ - extern struct ExpatIFace *IExpat; - #endif /* __NOGLOBALIFACE__ */ -#else /* __amigaos4__ */ - #ifndef CLIB_EXPAT_PROTOS_H - #include - #endif /* CLIB_EXPAT_PROTOS_H */ - #if defined(__GNUC__) - #ifndef __PPC__ - #include - #else - #include - #endif /* __PPC__ */ - #elif defined(__VBCC__) - #ifndef __PPC__ - #include - #endif /* __PPC__ */ - #else - #include - #endif /* __GNUC__ */ -#endif /* __amigaos4__ */ - -/****************************************************************************/ - -#endif /* PROTO_EXPAT_H */ diff --git a/cextern/expat/amiga/launch.c b/cextern/expat/amiga/launch.c deleted file mode 100755 index 73e526e7c07a..000000000000 --- a/cextern/expat/amiga/launch.c +++ /dev/null @@ -1,56 +0,0 @@ -/* -** Copyright (c) 2001-2007 Expat maintainers. -** -** Permission is hereby granted, free of charge, to any person obtaining -** a copy of this software and associated documentation files (the -** "Software"), to deal in the Software without restriction, including -** without limitation the rights to use, copy, modify, merge, publish, -** distribute, sublicense, and/or sell copies of the Software, and to -** permit persons to whom the Software is furnished to do so, subject to -** the following conditions: -** -** The above copyright notice and this permission notice shall be included -** in all copies or substantial portions of the Software. -** -** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -#include -#include - -struct Library* ExpatBase = 0; -struct ExpatIFace* IExpat = 0; - - -void setup() __attribute__((constructor)); -void cleanup() __attribute__((destructor)); - - -void setup() -{ - ExpatBase = OpenLibrary("expat.library", 4); - IExpat = (struct ExpatIFace*)GetInterface(ExpatBase, "main", 1, NULL); - if ( IExpat == 0 ) { - DebugPrintF("Can't open expat.library\n"); - } -} - - -void cleanup() -{ - if ( IExpat != 0 ) { - DropInterface((struct Interface*)IExpat); - IExpat = 0; - } - - if ( ExpatBase != 0 ) { - CloseLibrary(ExpatBase); - ExpatBase = 0; - } -} diff --git a/cextern/expat/amiga/stdlib.c b/cextern/expat/amiga/stdlib.c deleted file mode 100755 index 8c31f23bd6d6..000000000000 --- a/cextern/expat/amiga/stdlib.c +++ /dev/null @@ -1,109 +0,0 @@ -/* -** Copyright (c) 2001-2007 Expat maintainers. -** -** Permission is hereby granted, free of charge, to any person obtaining -** a copy of this software and associated documentation files (the -** "Software"), to deal in the Software without restriction, including -** without limitation the rights to use, copy, modify, merge, publish, -** distribute, sublicense, and/or sell copies of the Software, and to -** permit persons to whom the Software is furnished to do so, subject to -** the following conditions: -** -** The above copyright notice and this permission notice shall be included -** in all copies or substantial portions of the Software. -** -** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -#include -#include -#include -#include - -void * malloc (size_t len) -{ - uint32 size = sizeof(uint32) + len; - - uint32 *mem = AllocMem(size, MEMF_SHARED); - if ( mem != 0 ) { - *mem = size; - ++mem; - } - - return mem; -} - - -void * realloc (void * mem, size_t len2) -{ - if ( mem == 0 ) { - return malloc(len2); - } - - if ( len2 == 0 ) { - free(mem); - return 0; - } - - void * new_mem = malloc(len2); - if ( new_mem == 0 ) { - return 0; - } - - uint32 mem_size = *(((uint32*)mem) - 1); - CopyMem(mem, new_mem, mem_size); - free(mem); - - return new_mem; -} - - -void free (void * mem) -{ - if ( mem != 0 ) { - uint32 * size_ptr = ((uint32*)mem) - 1; - FreeMem(size_ptr, *size_ptr); - } -} - - -int memcmp (const void * a, const void * b, size_t len) -{ - size_t i; - int diff; - - for ( i = 0; i < len; ++i ) { - diff = *((uint8 *)a++) - *((uint8 *)b++); - if ( diff ) { - return diff; - } - } - - return 0; -} - - -void * memcpy (void * t, const void * a, size_t len) -{ - CopyMem((APTR)a, t, len); - return t; -} - - -void * memmove (void * t1, const void * t2, size_t len) -{ - MoveMem((APTR)t2, t1, len); - return t1; -} - - -void * memset (void * t, int c, size_t len) -{ - return SetMem(t, c, len); -} diff --git a/cextern/expat/bcb5/README.txt b/cextern/expat/bcb5/README.txt deleted file mode 100755 index 486f1ca85d6c..000000000000 --- a/cextern/expat/bcb5/README.txt +++ /dev/null @@ -1,87 +0,0 @@ - - Using a Borland compiler product - -The files in this directory support using both the free Borland command-line -compiler tools and the Borland C++ Builder IDE. The project files have been -tested with both versions 5 and 6 of the C++ Builder product. - - Using the free BCC32 command line compiler - -After downloading and installing the free C++ Builder commandline version, -perform the following steps (assuming it was installed under C:\Borland\BCC55): - -1) Add "C:\Borland\BCC55\BIN" to your path -2) Set the environment variable BCB to "C:\Borland\BCC55". -3) edit makefile.mak: enable or comment out the appropriate commands under - clean & distclean, depending on whether your OS can use deltree /y or - del /s/f/q. - -After that, you should simply cd to the bcb5 directory in your Expat directory -tree (same structure as CVS) and run "make all" or just "make". - - Naming - -The libraries have the base name "libexpat" followed optionally by an "s" -(static) or a "w" (unicode version), then an underscore and optionally -"mt" (multi-threaded) and "d" (dynamic RTL). - -To change the name of the library a project file produces, edit the project -option source (see step 1 under Unicode below) and change the name contained in -the PROJECT tag. In a make file, change the value assigned to the PROJECT -variable. Also, the LIBRARY entry in the .def file has to be changed to -correspond to the new executable name. - - - Unicode Considerations - -There are no facilities in the BCB 5 GUI to create a unicode-enabled -application. Fortunately, it is not hard to do by hand. - -1. The startup .obj system file must be changed to the unicode version. - Go to Project|Edit Option Source, and scroll down to the ALLOBJ tag. Change - c0x32.obj to c0x32w.obj. Editing this file can be quirky, but usually the - following kludge will make the change stick. Close and save the file - (CTRL-F4) then open the options dialog (CTRL-Shift-F11), then click OK on - the dialog immediately without changing anything in it. If this doesn't work, - you will have to close the project completely and edit the .bpr file by hand. - - If you are using a make file, just change the startup .obj file assigned - to the ALLOBJ variable. - -2. Add the macro define XML_UNICODE_WCHAR_T. In the GUI that goes in the options - dialog, Directories/Conditionals tab, in the Conditional define box. In a - make file, put it in the USERDEFINES variable. - -3. Of course, your code has to be written for unicode. As a start, the "main" - function is called "wmain". The tchar macros are an interesting way to - write code that can easily switch between unicode and utf-8. If these macros - are used, then simply adding the conditional define _UNICODE as well as - XML_UNICODE_WCHAR_T will bring in the unicode versions of the tchar macros. - Otherwise the utf-8 versions are used. xmlwf uses its own versions of the - tchar macros which are switched on and off by the XML_UNICODE macro, which - itself is set by the XML_UNICODE_WCHAR_T define. - - Threading - -The libexpat libraries are all built to link with the multi-threaded dynamic RTL's. -That means they require CC32xxMT.DLL present on the installation target. -To create single-threaded libs, do the following: - -1. The compiler option for multi-threading must be turned off. Following the - instructions above to edit the option source, remove the -tWM option from - the CFLAG1 tag. In a make file, remove it from the CFLAG1 variable. - -2. The single threaded RTL must be called. change the RTL in the ALLLIB tag or - variable (GUI or makefile repectively) to the version without the "mt" in the - name. For example, change cw32mti.lib to cw32i.lib. - - Static RTL's - -To build the libs with static RTL's do the following, - -1. For the static expatlibs, in the Tlib tab on the options dialog, uncheck the - "Use dynamic RTL" box. For the dynamic expatlibs, in the Linker tab on the - options dialog, uncheck "Use dynamic RTL". If you are using a make file, - remove the _RTLDLL assignment to the SYSDEFINES variable, and change the RTL - to the version without an "i" in the ALLLIB variable. For example, - cw32mti.lib would become cw32mt.lib. diff --git a/cextern/expat/bcb5/all_projects.bpg b/cextern/expat/bcb5/all_projects.bpg deleted file mode 100755 index 27c9a1cba570..000000000000 --- a/cextern/expat/bcb5/all_projects.bpg +++ /dev/null @@ -1,49 +0,0 @@ -#------------------------------------------------------------------------------ -VERSION = BWS.01 -#------------------------------------------------------------------------------ -!ifndef ROOT -ROOT = $(MAKEDIR)\.. -!endif -#------------------------------------------------------------------------------ -MAKE = $(ROOT)\bin\make.exe -$(MAKEFLAGS) -f$** -DCC = $(ROOT)\bin\dcc32.exe $** -BRCC = $(ROOT)\bin\brcc32.exe $** -#------------------------------------------------------------------------------ -PROJECTS = setup libexpat_mtd.dll libexpats_mtd.lib libexpatw_mtd.dll \ - libexpatws_mtd.lib elements.exe outline.exe xmlwf.exe -#------------------------------------------------------------------------------ -default: $(PROJECTS) -#------------------------------------------------------------------------------ - -libexpat_mtd.dll: expat.bpr - $(ROOT)\bin\bpr2mak $** - $(ROOT)\bin\make -$(MAKEFLAGS) -f$*.mak - -libexpats_mtd.lib: expat_static.bpr - $(ROOT)\bin\bpr2mak -t$(ROOT)\bin\deflib.bmk $** - $(ROOT)\bin\make -$(MAKEFLAGS) -f$*.mak - -libexpatw_mtd.dll: expatw.bpr - $(ROOT)\bin\bpr2mak $** - $(ROOT)\bin\make -$(MAKEFLAGS) -f$*.mak - -libexpatws_mtd.lib: expatw_static.bpr - $(ROOT)\bin\bpr2mak -t$(ROOT)\bin\deflib.bmk $** - $(ROOT)\bin\make -$(MAKEFLAGS) -f$*.mak - -elements.exe: elements.bpr - $(ROOT)\bin\bpr2mak $** - $(ROOT)\bin\make -$(MAKEFLAGS) -f$*.mak - -outline.exe: outline.bpr - $(ROOT)\bin\bpr2mak $** - $(ROOT)\bin\make -$(MAKEFLAGS) -f$*.mak - -xmlwf.exe: xmlwf.bpr - $(ROOT)\bin\bpr2mak $** - $(ROOT)\bin\make -$(MAKEFLAGS) -f$*.mak - -setup: setup.bat - call $** - - diff --git a/cextern/expat/bcb5/elements.bpf b/cextern/expat/bcb5/elements.bpf deleted file mode 100755 index 5c1e878d671c..000000000000 --- a/cextern/expat/bcb5/elements.bpf +++ /dev/null @@ -1,4 +0,0 @@ -USEUNIT("..\examples\elements.c"); -USELIB("Release\libexpats_mtd.lib"); -//--------------------------------------------------------------------------- -main diff --git a/cextern/expat/bcb5/elements.bpr b/cextern/expat/bcb5/elements.bpr deleted file mode 100755 index c21c0f59f299..000000000000 --- a/cextern/expat/bcb5/elements.bpr +++ /dev/null @@ -1,149 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -[Version Info] -IncludeVerInfo=0 -AutoIncBuild=0 -MajorVer=1 -MinorVer=0 -Release=0 -Build=0 -Debug=0 -PreRelease=0 -Special=0 -Private=0 -DLL=0 -Locale=1033 -CodePage=1252 - -[Version Info Keys] -CompanyName= -FileDescription= -FileVersion=1.0.0.0 -InternalName= -LegalCopyright= -LegalTrademarks= -OriginalFilename= -ProductName= -ProductVersion=1.0.0.0 -Comments= - -[HistoryLists\hlIncludePath] -Count=4 -Item0=..\examples;$(BCB)\include -Item1=$(BCB)\include -Item2=$(BCB)\include;$(BCB)\include\mfc;$(BCB)\include\atl -Item3=$(BCB)\include;$(BCB)\include\mfc;$(BCB)\include\atl; - -[HistoryLists\hlLibraryPath] -Count=8 -Item0=..\examples;$(BCB)\lib;$(RELEASELIBPATH) -Item1=..\examples;$(BCB)\lib;..\examples\$(RELEASELIBPATH) -Item2=$(BCB)\lib;$(RELEASELIBPATH) -Item3=$(BCB)\lib;$(RELEASELIBPATH);..\lib\Release-w_static -Item4=$(BCB)\lib;$(RELEASELIBPATH);..\lib\Release_static -Item5=$(BCB)\lib;$(RELEASELIBPATH);C:\src\expat\lib\Release_static -Item6=$(BCB)\lib;$(RELEASELIBPATH);$(BCB)\lib\psdk -Item7=$(BCB)\lib;$(RELEASELIBPATH);;$(BCB)\lib\psdk; - -[HistoryLists\hlDebugSourcePath] -Count=1 -Item0=$(BCB)\source\vcl - -[HistoryLists\hlConditionals] -Count=17 -Item0=WIN32;NDEBUG;_CONSOLE;XML_STATIC -Item1=WIN32;NDEBUG;_CONSOLE;_DEBUG;XML_STATIC -Item2=WIN32;NDEBUG;_CONSOLE;_DEBUG;XML_UNICODE_WCHAR_T;_UNICODE;XML_STATIC -Item3=WIN32;NDEBUG;_CONSOLE;_DEBUG;XML_UNICODE_WCHAR_T;_UNICODE -Item4=WIN32;NDEBUG;_CONSOLE;_DEBUG -Item5=WIN32;NDEBUG;_CONSOLE;XML_STATIC;_DEBUG -Item6=WIN32;NDEBUG;_CONSOLE;XML_STATIC;_DEBUG;_UNICODE -Item7=WIN32;NDEBUG;_CONSOLE;XML_STATIC;_DEBUG;XML_UNICODE_WCHAR_T -Item8=WIN32;NDEBUG;_CONSOLE;_MBCS;XML_STATIC;_DEBUG;XML_UNICODE_WCHAR_T -Item9=WIN32;NDEBUG;_CONSOLE;_UNICODE;XML_STATIC;_DEBUG;XML_UNICODE_WCHAR_T -Item10=WIN32;NDEBUG;_CONSOLE;_UNICODE;XML_STATIC;_DEBUG;XML_UNICODE -Item11=WIN32;NDEBUG;_CONSOLE;_MBCS;XML_STATIC;_DEBUG;XML_UNICODE_WCHAR_T;__WCHAR_T -Item12=WIN32;NDEBUG;_CONSOLE;_MBCS;XML_STATIC;_DEBUG;XML_UNICODE_WCHAR_T;_UNICODE -Item13=WIN32;NDEBUG;_CONSOLE;_MBCS;XML_STATIC;_DEBUG;XML_UNICODE;_UNICODE -Item14=WIN32;NDEBUG;_CONSOLE;_MBCS;XML_STATIC;_DEBUG;XML_UNICODE -Item15=WIN32;NDEBUG;_CONSOLE;_MBCS;XML_STATIC;_DEBUG -Item16=WIN32;NDEBUG;_CONSOLE;_MBCS;XML_STATIC - -[HistoryLists\hlIntOutputDir] -Count=5 -Item0=Release\obj\examples -Item1=Release\obj\elements -Item2=Release\obj\mts -Item3=..\examples\Release -Item4=Release - -[HistoryLists\hlFinalOutputDir] -Count=1 -Item0=Release\ - -[Debugging] -DebugSourceDirs= - -[Parameters] -RunParams= -HostApplication= -RemoteHost= -RemotePath= -RemoteDebug=0 - -[Compiler] -ShowInfoMsgs=0 -LinkDebugVcl=0 -LinkCGLIB=0 - -[Language] -ActiveLang= -ProjectLang= -RootDir= - - \ No newline at end of file diff --git a/cextern/expat/bcb5/elements.mak b/cextern/expat/bcb5/elements.mak deleted file mode 100755 index d4427fd5129b..000000000000 --- a/cextern/expat/bcb5/elements.mak +++ /dev/null @@ -1,186 +0,0 @@ -# --------------------------------------------------------------------------- -!if !$d(BCB) -BCB = $(MAKEDIR)\.. -!endif - -# --------------------------------------------------------------------------- -# IDE SECTION -# --------------------------------------------------------------------------- -# The following section of the project makefile is managed by the BCB IDE. -# It is recommended to use the IDE to change any of the values in this -# section. -# --------------------------------------------------------------------------- - -VERSION = BCB.05.03 -# --------------------------------------------------------------------------- -PROJECT = Release\elements.exe -OBJFILES = Release\obj\examples\elements.obj -RESFILES = -MAINSOURCE = elements.bpf -RESDEPEN = $(RESFILES) -LIBFILES = Release\libexpats_mtd.lib -IDLFILES = -IDLGENFILES = -LIBRARIES = -PACKAGES = VCL50.bpi VCLX50.bpi bcbsmp50.bpi QRPT50.bpi VCLDB50.bpi VCLBDE50.bpi \ - ibsmp50.bpi VCLDBX50.bpi TEEUI50.bpi TEEDB50.bpi TEE50.bpi TEEQR50.bpi \ - VCLIB50.bpi bcbie50.bpi VCLIE50.bpi INETDB50.bpi INET50.bpi NMFAST50.bpi \ - dclocx50.bpi bcb2kaxserver50.bpi dclusr50.bpi -SPARELIBS = -DEFFILE = -# --------------------------------------------------------------------------- -PATHCPP = .;..\examples -PATHASM = .; -PATHPAS = .; -PATHRC = .; -DEBUGLIBPATH = $(BCB)\lib\debug -RELEASELIBPATH = $(BCB)\lib\release -USERDEFINES = WIN32;NDEBUG;_CONSOLE;XML_STATIC -SYSDEFINES = _NO_VCL;_ASSERTE;NO_STRICT;_RTLDLL -INCLUDEPATH = ..\examples;$(BCB)\include -LIBPATH = ..\examples;$(BCB)\lib;$(RELEASELIBPATH) -WARNINGS= -w-par -w-8027 -w-8026 -# --------------------------------------------------------------------------- -CFLAG1 = -O2 -X- -a8 -b -k- -vi -q -I..\lib -c -IDLCFLAGS = -I$(BCB)\include -PFLAGS = -N2Release\obj\examples -N0Release\obj\examples -$Y- -$L- -$D- -RFLAGS = /l 0x409 /d "NDEBUG" /i$(BCB)\include -AFLAGS = /mx /w2 /zn -LFLAGS = -IRelease\obj\examples -D"" -ap -Tpe -x -Gn -q -L..\LIB\RELEASE_STATIC -# --------------------------------------------------------------------------- -ALLOBJ = c0x32.obj $(OBJFILES) -ALLRES = $(RESFILES) -ALLLIB = $(LIBFILES) $(LIBRARIES) import32.lib cw32mti.lib -# --------------------------------------------------------------------------- -!ifdef IDEOPTIONS - -[Version Info] -IncludeVerInfo=0 -AutoIncBuild=0 -MajorVer=1 -MinorVer=0 -Release=0 -Build=0 -Debug=0 -PreRelease=0 -Special=0 -Private=0 -DLL=0 - -[Version Info Keys] -CompanyName= -FileDescription= -FileVersion=1.0.0.0 -InternalName= -LegalCopyright= -LegalTrademarks= -OriginalFilename= -ProductName= -ProductVersion=1.0.0.0 -Comments= - -[Debugging] -DebugSourceDirs=$(BCB)\source\vcl - -!endif - - - - - -# --------------------------------------------------------------------------- -# MAKE SECTION -# --------------------------------------------------------------------------- -# This section of the project file is not used by the BCB IDE. It is for -# the benefit of building from the command-line using the MAKE utility. -# --------------------------------------------------------------------------- - -.autodepend -# --------------------------------------------------------------------------- -!if "$(USERDEFINES)" != "" -AUSERDEFINES = -d$(USERDEFINES:;= -d) -!else -AUSERDEFINES = -!endif - -!if !$d(BCC32) -BCC32 = bcc32 -!endif - -!if !$d(CPP32) -CPP32 = cpp32 -!endif - -!if !$d(DCC32) -DCC32 = dcc32 -!endif - -!if !$d(TASM32) -TASM32 = tasm32 -!endif - -!if !$d(LINKER) -LINKER = ilink32 -!endif - -!if !$d(BRCC32) -BRCC32 = brcc32 -!endif - - -# --------------------------------------------------------------------------- -!if $d(PATHCPP) -.PATH.CPP = $(PATHCPP) -.PATH.C = $(PATHCPP) -!endif - -!if $d(PATHPAS) -.PATH.PAS = $(PATHPAS) -!endif - -!if $d(PATHASM) -.PATH.ASM = $(PATHASM) -!endif - -!if $d(PATHRC) -.PATH.RC = $(PATHRC) -!endif -# --------------------------------------------------------------------------- -$(PROJECT): $(IDLGENFILES) $(OBJFILES) $(RESDEPEN) $(DEFFILE) - $(BCB)\BIN\$(LINKER) @&&! - $(LFLAGS) -L$(LIBPATH) + - $(ALLOBJ), + - $(PROJECT),, + - $(ALLLIB), + - $(DEFFILE), + - $(ALLRES) -! -# --------------------------------------------------------------------------- -.pas.hpp: - $(BCB)\BIN\$(DCC32) $(PFLAGS) -U$(INCLUDEPATH) -D$(USERDEFINES);$(SYSDEFINES) -O$(INCLUDEPATH) --BCB {$< } - -.pas.obj: - $(BCB)\BIN\$(DCC32) $(PFLAGS) -U$(INCLUDEPATH) -D$(USERDEFINES);$(SYSDEFINES) -O$(INCLUDEPATH) --BCB {$< } - -.cpp.obj: - $(BCB)\BIN\$(BCC32) $(CFLAG1) $(WARNINGS) -I$(INCLUDEPATH) -D$(USERDEFINES);$(SYSDEFINES) -n$(@D) {$< } - -.c.obj: - $(BCB)\BIN\$(BCC32) $(CFLAG1) $(WARNINGS) -I$(INCLUDEPATH) -D$(USERDEFINES);$(SYSDEFINES) -n$(@D) {$< } - -.c.i: - $(BCB)\BIN\$(CPP32) $(CFLAG1) $(WARNINGS) -I$(INCLUDEPATH) -D$(USERDEFINES);$(SYSDEFINES) -n. {$< } - -.cpp.i: - $(BCB)\BIN\$(CPP32) $(CFLAG1) $(WARNINGS) -I$(INCLUDEPATH) -D$(USERDEFINES);$(SYSDEFINES) -n. {$< } - -.asm.obj: - $(BCB)\BIN\$(TASM32) $(AFLAGS) -i$(INCLUDEPATH:;= -i) $(AUSERDEFINES) -d$(SYSDEFINES:;= -d) $<, $@ - -.rc.res: - $(BCB)\BIN\$(BRCC32) $(RFLAGS) -I$(INCLUDEPATH) -D$(USERDEFINES);$(SYSDEFINES) -fo$@ $< -# --------------------------------------------------------------------------- - - - - diff --git a/cextern/expat/bcb5/expat.bpf b/cextern/expat/bcb5/expat.bpf deleted file mode 100755 index 2c423283f104..000000000000 --- a/cextern/expat/bcb5/expat.bpf +++ /dev/null @@ -1,6 +0,0 @@ -USEUNIT("..\lib\xmlparse.c"); -USEUNIT("..\lib\xmlrole.c"); -USEUNIT("..\lib\xmltok.c"); -USEDEF("libexpat_mtd.def"); -//--------------------------------------------------------------------------- -#define DllEntryPoint diff --git a/cextern/expat/bcb5/expat.bpr b/cextern/expat/bcb5/expat.bpr deleted file mode 100755 index 291b8cafc1d7..000000000000 --- a/cextern/expat/bcb5/expat.bpr +++ /dev/null @@ -1,140 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -[Version Info] -IncludeVerInfo=0 -AutoIncBuild=0 -MajorVer=1 -MinorVer=0 -Release=0 -Build=0 -Debug=0 -PreRelease=0 -Special=0 -Private=0 -DLL=0 -Locale=1033 -CodePage=1252 - -[Version Info Keys] -CompanyName= -FileDescription= -FileVersion=1.0.0.0 -InternalName= -LegalCopyright= -LegalTrademarks= -OriginalFilename= -ProductName= -ProductVersion=1.0.0.0 -Comments= - -[HistoryLists\hlIncludePath] -Count=4 -Item0=..\lib;$(BCB)\include -Item1=$(BCB)\include -Item2=$(BCB)\include;$(BCB)\include\mfc;$(BCB)\include\atl -Item3=$(BCB)\include;$(BCB)\include\mfc;$(BCB)\include\atl; - -[HistoryLists\hlLibraryPath] -Count=5 -Item0=..\lib;$(BCB)\lib;$(RELEASELIBPATH) -Item1=..\lib;$(BCB)\lib;..\lib\$(RELEASELIBPATH) -Item2=$(BCB)\lib;$(RELEASELIBPATH) -Item3=$(BCB)\lib;$(RELEASELIBPATH);$(BCB)\lib\psdk -Item4=$(BCB)\lib;$(RELEASELIBPATH);;$(BCB)\lib\psdk; - -[HistoryLists\hlDebugSourcePath] -Count=1 -Item0=$(BCB)\source\vcl - -[HistoryLists\hlConditionals] -Count=8 -Item0=_WINDOWS;WIN32;NDEBUG;_USRDLL;COMPILED_FROM_DSP;EXPAT_EXPORTS -Item1=_WINDOWS;WIN32;NDEBUG;_DEBUG;_USRDLL;COMPILED_FROM_DSP;EXPAT_EXPORTS -Item2=WIN32;_WINDOWS;NDEBUG;_DEBUG;_USRDLL;COMPILED_FROM_DSP;EXPAT_EXPORTS -Item3=WIN32;_WINDOWS;NDEBUG;_DEBUG;_USRDLL;EXPAT_EXPORTS;COMPILED_FROM_DSP -Item4=NDEBUG;WIN32;_WINDOWS;_USRDLL;_DEBUG;EXPAT_EXPORTS;COMPILED_FROM_DSP -Item5=NDEBUG;WIN32;_WINDOWS;_USRDLL;EXPAT_EXPORTS;COMPILED_FROM_DSP;_DEBUG -Item6=NDEBUG;WIN32;_WINDOWS;_MBCS;_USRDLL;EXPAT_EXPORTS;COMPILED_FROM_DSP;_DEBUG -Item7=NDEBUG;WIN32;_WINDOWS;_MBCS;_USRDLL;EXPAT_EXPORTS;COMPILED_FROM_DSP - -[HistoryLists\hlIntOutputDir] -Count=7 -Item0=Release\obj\libexpat -Item1=Release\obj\libexpat_static -Item2=Release\obj\mtd -Item3=Release\obj\mt -Item4=Release\obj -Item5=Release -Item6=..\lib\Release - -[HistoryLists\hlFinalOutputDir] -Count=1 -Item0=Release\ - -[Debugging] -DebugSourceDirs= - -[Parameters] -RunParams= -HostApplication= -RemoteHost= -RemotePath= -RemoteDebug=0 - -[Compiler] -ShowInfoMsgs=0 -LinkDebugVcl=0 -LinkCGLIB=0 - -[Language] -ActiveLang= -ProjectLang= -RootDir= - - \ No newline at end of file diff --git a/cextern/expat/bcb5/expat.mak b/cextern/expat/bcb5/expat.mak deleted file mode 100755 index 7c9c23f52b86..000000000000 --- a/cextern/expat/bcb5/expat.mak +++ /dev/null @@ -1,187 +0,0 @@ -# --------------------------------------------------------------------------- -!if !$d(BCB) -BCB = $(MAKEDIR)\.. -!endif - -# --------------------------------------------------------------------------- -# IDE SECTION -# --------------------------------------------------------------------------- -# The following section of the project makefile is managed by the BCB IDE. -# It is recommended to use the IDE to change any of the values in this -# section. -# --------------------------------------------------------------------------- - -VERSION = BCB.05.03 -# --------------------------------------------------------------------------- -PROJECT = Release\libexpat_mtd.dll -OBJFILES = Release\obj\libexpat\xmlparse.obj Release\obj\libexpat\xmlrole.obj \ - Release\obj\libexpat\xmltok.obj -RESFILES = -MAINSOURCE = expat.bpf -RESDEPEN = $(RESFILES) -LIBFILES = -IDLFILES = -IDLGENFILES = -LIBRARIES = -PACKAGES = VCL50.bpi VCLX50.bpi bcbsmp50.bpi QRPT50.bpi VCLDB50.bpi VCLBDE50.bpi \ - ibsmp50.bpi VCLDBX50.bpi TEEUI50.bpi TEEDB50.bpi TEE50.bpi TEEQR50.bpi \ - VCLIB50.bpi bcbie50.bpi VCLIE50.bpi INETDB50.bpi INET50.bpi NMFAST50.bpi \ - dclocx50.bpi bcb2kaxserver50.bpi dclusr50.bpi -SPARELIBS = -DEFFILE = libexpat_mtd.def -# --------------------------------------------------------------------------- -PATHCPP = .;..\lib -PATHASM = .; -PATHPAS = .; -PATHRC = .; -DEBUGLIBPATH = $(BCB)\lib\debug -RELEASELIBPATH = $(BCB)\lib\release -USERDEFINES = _WINDOWS;WIN32;NDEBUG;_USRDLL;COMPILED_FROM_DSP -SYSDEFINES = _NO_VCL;_ASSERTE;NO_STRICT;_RTLDLL -INCLUDEPATH = ..\lib;$(BCB)\include -LIBPATH = ..\lib;$(BCB)\lib;$(RELEASELIBPATH) -WARNINGS= -w-rch -w-par -w-8027 -w-8026 -w-ccc -# --------------------------------------------------------------------------- -CFLAG1 = -WD -O2 -X- -a8 -b -k- -vi -q -tWM -c -tWD -IDLCFLAGS = -I$(BCB)\include -PFLAGS = -N2Release\obj\libexpat -N0Release\obj\libexpat -$Y- -$L- -$D- -RFLAGS = /l 0x409 /d "NDEBUG" /i$(BCB)\include -AFLAGS = /mx /w2 /zn -LFLAGS = -IRelease\obj\libexpat -D"" -aa -Tpd -x -Gn -Gi -q -# --------------------------------------------------------------------------- -ALLOBJ = c0d32.obj $(OBJFILES) -ALLRES = $(RESFILES) -ALLLIB = $(LIBFILES) $(LIBRARIES) import32.lib cw32mti.lib -# --------------------------------------------------------------------------- -!ifdef IDEOPTIONS - -[Version Info] -IncludeVerInfo=0 -AutoIncBuild=0 -MajorVer=1 -MinorVer=0 -Release=0 -Build=0 -Debug=0 -PreRelease=0 -Special=0 -Private=0 -DLL=0 - -[Version Info Keys] -CompanyName= -FileDescription= -FileVersion=1.0.0.0 -InternalName= -LegalCopyright= -LegalTrademarks= -OriginalFilename= -ProductName= -ProductVersion=1.0.0.0 -Comments= - -[Debugging] -DebugSourceDirs=$(BCB)\source\vcl - -!endif - - - - - -# --------------------------------------------------------------------------- -# MAKE SECTION -# --------------------------------------------------------------------------- -# This section of the project file is not used by the BCB IDE. It is for -# the benefit of building from the command-line using the MAKE utility. -# --------------------------------------------------------------------------- - -.autodepend -# --------------------------------------------------------------------------- -!if "$(USERDEFINES)" != "" -AUSERDEFINES = -d$(USERDEFINES:;= -d) -!else -AUSERDEFINES = -!endif - -!if !$d(BCC32) -BCC32 = bcc32 -!endif - -!if !$d(CPP32) -CPP32 = cpp32 -!endif - -!if !$d(DCC32) -DCC32 = dcc32 -!endif - -!if !$d(TASM32) -TASM32 = tasm32 -!endif - -!if !$d(LINKER) -LINKER = ilink32 -!endif - -!if !$d(BRCC32) -BRCC32 = brcc32 -!endif - - -# --------------------------------------------------------------------------- -!if $d(PATHCPP) -.PATH.CPP = $(PATHCPP) -.PATH.C = $(PATHCPP) -!endif - -!if $d(PATHPAS) -.PATH.PAS = $(PATHPAS) -!endif - -!if $d(PATHASM) -.PATH.ASM = $(PATHASM) -!endif - -!if $d(PATHRC) -.PATH.RC = $(PATHRC) -!endif -# --------------------------------------------------------------------------- -$(PROJECT): $(IDLGENFILES) $(OBJFILES) $(RESDEPEN) $(DEFFILE) - $(BCB)\BIN\$(LINKER) @&&! - $(LFLAGS) -L$(LIBPATH) + - $(ALLOBJ), + - $(PROJECT),, + - $(ALLLIB), + - $(DEFFILE), + - $(ALLRES) -! -# --------------------------------------------------------------------------- -.pas.hpp: - $(BCB)\BIN\$(DCC32) $(PFLAGS) -U$(INCLUDEPATH) -D$(USERDEFINES);$(SYSDEFINES) -O$(INCLUDEPATH) --BCB {$< } - -.pas.obj: - $(BCB)\BIN\$(DCC32) $(PFLAGS) -U$(INCLUDEPATH) -D$(USERDEFINES);$(SYSDEFINES) -O$(INCLUDEPATH) --BCB {$< } - -.cpp.obj: - $(BCB)\BIN\$(BCC32) $(CFLAG1) $(WARNINGS) -I$(INCLUDEPATH) -D$(USERDEFINES);$(SYSDEFINES) -n$(@D) {$< } - -.c.obj: - $(BCB)\BIN\$(BCC32) $(CFLAG1) $(WARNINGS) -I$(INCLUDEPATH) -D$(USERDEFINES);$(SYSDEFINES) -n$(@D) {$< } - -.c.i: - $(BCB)\BIN\$(CPP32) $(CFLAG1) $(WARNINGS) -I$(INCLUDEPATH) -D$(USERDEFINES);$(SYSDEFINES) -n. {$< } - -.cpp.i: - $(BCB)\BIN\$(CPP32) $(CFLAG1) $(WARNINGS) -I$(INCLUDEPATH) -D$(USERDEFINES);$(SYSDEFINES) -n. {$< } - -.asm.obj: - $(BCB)\BIN\$(TASM32) $(AFLAGS) -i$(INCLUDEPATH:;= -i) $(AUSERDEFINES) -d$(SYSDEFINES:;= -d) $<, $@ - -.rc.res: - $(BCB)\BIN\$(BRCC32) $(RFLAGS) -I$(INCLUDEPATH) -D$(USERDEFINES);$(SYSDEFINES) -fo$@ $< -# --------------------------------------------------------------------------- - - - - diff --git a/cextern/expat/bcb5/expat_static.bpf b/cextern/expat/bcb5/expat_static.bpf deleted file mode 100755 index 5ca458edcaec..000000000000 --- a/cextern/expat/bcb5/expat_static.bpf +++ /dev/null @@ -1,5 +0,0 @@ -USEUNIT("..\lib\xmlparse.c"); -USEUNIT("..\lib\xmlrole.c"); -USEUNIT("..\lib\xmltok.c"); -//--------------------------------------------------------------------------- -#define Library diff --git a/cextern/expat/bcb5/expat_static.bpr b/cextern/expat/bcb5/expat_static.bpr deleted file mode 100755 index 2f6ec4d3bd9f..000000000000 --- a/cextern/expat/bcb5/expat_static.bpr +++ /dev/null @@ -1,143 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -[Version Info] -IncludeVerInfo=0 -AutoIncBuild=0 -MajorVer=1 -MinorVer=0 -Release=0 -Build=0 -Debug=0 -PreRelease=0 -Special=0 -Private=0 -DLL=0 -Locale=1033 -CodePage=1252 - -[Version Info Keys] -CompanyName= -FileDescription= -FileVersion=1.0.0.0 -InternalName= -LegalCopyright= -LegalTrademarks= -OriginalFilename= -ProductName= -ProductVersion=1.0.0.0 -Comments= - -[HistoryLists\hlIncludePath] -Count=4 -Item0=..\lib;$(BCB)\include -Item1=$(BCB)\include -Item2=$(BCB)\include;$(BCB)\include\mfc;$(BCB)\include\atl -Item3=$(BCB)\include;$(BCB)\include\mfc;$(BCB)\include\atl; - -[HistoryLists\hlLibraryPath] -Count=5 -Item0=..\lib;$(BCB)\lib;$(RELEASELIBPATH) -Item1=..\lib;$(BCB)\lib;..\lib\$(RELEASELIBPATH) -Item2=$(BCB)\lib;$(RELEASELIBPATH) -Item3=$(BCB)\lib;$(RELEASELIBPATH);$(BCB)\lib\psdk -Item4=$(BCB)\lib;$(RELEASELIBPATH);;$(BCB)\lib\psdk; - -[HistoryLists\hlDebugSourcePath] -Count=1 -Item0=$(BCB)\source\vcl - -[HistoryLists\hlConditionals] -Count=7 -Item0=_WINDOWS;WIN32;NDEBUG;_LIB;COMPILED_FROM_DSP;XML_STATIC -Item1=_WINDOWS;WIN32;NDEBUG;_DEBUG;_LIB;COMPILED_FROM_DSP;XML_STATIC -Item2=WIN32;_WINDOWS;NDEBUG;_DEBUG;_LIB;COMPILED_FROM_DSP;XML_STATIC -Item3=WIN32;_WINDOWS;NDEBUG;_LIB;COMPILED_FROM_DSP;_DEBUG -Item4=WIN32;_WINDOWS;NDEBUG;_LIB;COMPILED_FROM_DSP -Item5=WIN32;_WINDOWS;NDEBUG;_LIB;COMPILED_FROM_DSP;_MBCS -Item6=WIN32;_WINDOWS;NDEBUG;_MBCS;_LIB;COMPILED_FROM_DSP - -[HistoryLists\hlIntOutputDir] -Count=6 -Item0=Release\obj\libexpat_static -Item1=Release\obj\mts -Item2=Release\obj\mt -Item3=Release -Item4=..\lib\Release_static -Item5=Release_static - -[HistoryLists\hlFinalOutputDir] -Count=3 -Item0=Release\ -Item1=Release -Item2=Release_static\ - -[HistoryLists\hlTlibPageSize] -Count=1 -Item0=0x0010 - -[Debugging] -DebugSourceDirs= - -[Parameters] -RunParams= -HostApplication= -RemoteHost= -RemotePath= -RemoteDebug=0 - -[Compiler] -ShowInfoMsgs=0 -LinkDebugVcl=0 -LinkCGLIB=0 - -[Language] -ActiveLang= -ProjectLang= -RootDir= - - \ No newline at end of file diff --git a/cextern/expat/bcb5/expat_static.mak b/cextern/expat/bcb5/expat_static.mak deleted file mode 100755 index 9137d3b53461..000000000000 --- a/cextern/expat/bcb5/expat_static.mak +++ /dev/null @@ -1,189 +0,0 @@ -# --------------------------------------------------------------------------- -!if !$d(BCB) -BCB = $(MAKEDIR)\.. -!endif - -# --------------------------------------------------------------------------- -# IDE SECTION -# --------------------------------------------------------------------------- -# The following section of the project makefile is managed by the BCB IDE. -# It is recommended to use the IDE to change any of the values in this -# section. -# --------------------------------------------------------------------------- - -VERSION = BCB.05.03 -# --------------------------------------------------------------------------- -PROJECT = Release\libexpats_mtd.lib -OBJFILES = Release\obj\libexpat_static\xmlparse.obj \ - Release\obj\libexpat_static\xmlrole.obj \ - Release\obj\libexpat_static\xmltok.obj -RESFILES = -MAINSOURCE = expat_static.bpf -RESDEPEN = $(RESFILES) -LIBFILES = -IDLFILES = -IDLGENFILES = -LIBRARIES = -PACKAGES = -SPARELIBS = -DEFFILE = -# --------------------------------------------------------------------------- -PATHCPP = .;..\lib -PATHASM = .; -PATHPAS = .; -PATHRC = .; -LINKER = TLib -DEBUGLIBPATH = $(BCB)\lib\debug -RELEASELIBPATH = $(BCB)\lib\release -USERDEFINES = _WINDOWS;WIN32;NDEBUG;_LIB;COMPILED_FROM_DSP;XML_STATIC -SYSDEFINES = _NO_VCL;_ASSERTE;NO_STRICT;_RTLDLL -INCLUDEPATH = ..\lib;$(BCB)\include -LIBPATH = ..\lib;$(BCB)\lib;$(RELEASELIBPATH) -WARNINGS = -w-rch -w-par -w-8027 -w-8026 -w-ccc -LISTFILE = -# --------------------------------------------------------------------------- -CFLAG1 = -O2 -X- -a8 -b -k- -vi -q -tWM -c -IDLCFLAGS = -I$(BCB)\include -PFLAGS = -N2Release\obj\libexpat_static -N0Release\obj\libexpat_static -$Y- -$L- -$D- -RFLAGS = /l 0x409 /d "NDEBUG" /i$(BCB)\include -AFLAGS = /mx /w2 /zn -LFLAGS = -# --------------------------------------------------------------------------- -ALLOBJ = $(OBJFILES) -ALLRES = $(RESFILES) -ALLLIB = $(LIBFILES) $(LIBRARIES) -# --------------------------------------------------------------------------- -!ifdef IDEOPTIONS - -[Version Info] -IncludeVerInfo=0 -AutoIncBuild=0 -MajorVer=1 -MinorVer=0 -Release=0 -Build=0 -Debug=0 -PreRelease=0 -Special=0 -Private=0 -DLL=0 - -[Version Info Keys] -CompanyName= -FileDescription= -FileVersion=1.0.0.0 -InternalName= -LegalCopyright= -LegalTrademarks= -OriginalFilename= -ProductName= -ProductVersion=1.0.0.0 -Comments= - -[Debugging] -DebugSourceDirs=$(BCB)\source\vcl - -!endif - - - - - -# --------------------------------------------------------------------------- -# MAKE SECTION -# --------------------------------------------------------------------------- -# This section of the project file is not used by the BCB IDE. It is for -# the benefit of building from the command-line using the MAKE utility. -# --------------------------------------------------------------------------- - -.autodepend -# --------------------------------------------------------------------------- -!if "$(USERDEFINES)" != "" -AUSERDEFINES = -d$(USERDEFINES:;= -d) -!else -AUSERDEFINES = -!endif - -!if !$d(BCC32) -BCC32 = bcc32 -!endif - -!if !$d(CPP32) -CPP32 = cpp32 -!endif - -!if !$d(DCC32) -DCC32 = dcc32 -!endif - -!if !$d(TASM32) -TASM32 = tasm32 -!endif - -!if !$d(LINKER) -LINKER = TLib -!endif - -!if !$d(BRCC32) -BRCC32 = brcc32 -!endif - - -# --------------------------------------------------------------------------- -!if $d(PATHCPP) -.PATH.CPP = $(PATHCPP) -.PATH.C = $(PATHCPP) -!endif - -!if $d(PATHPAS) -.PATH.PAS = $(PATHPAS) -!endif - -!if $d(PATHASM) -.PATH.ASM = $(PATHASM) -!endif - -!if $d(PATHRC) -.PATH.RC = $(PATHRC) -!endif -# --------------------------------------------------------------------------- -!if "$(LISTFILE)" == "" -COMMA = -!else -COMMA = , -!endif - -$(PROJECT): $(IDLGENFILES) $(OBJFILES) $(RESDEPEN) $(DEFFILE) - $(BCB)\BIN\$(LINKER) /u $@ @&&! - $(LFLAGS) $? $(COMMA) $(LISTFILE) - -! -# --------------------------------------------------------------------------- -.pas.hpp: - $(BCB)\BIN\$(DCC32) $(PFLAGS) -U$(INCLUDEPATH) -D$(USERDEFINES);$(SYSDEFINES) -O$(INCLUDEPATH) --BCB {$< } - -.pas.obj: - $(BCB)\BIN\$(DCC32) $(PFLAGS) -U$(INCLUDEPATH) -D$(USERDEFINES);$(SYSDEFINES) -O$(INCLUDEPATH) --BCB {$< } - -.cpp.obj: - $(BCB)\BIN\$(BCC32) $(CFLAG1) $(WARNINGS) -I$(INCLUDEPATH) -D$(USERDEFINES);$(SYSDEFINES) -n$(@D) {$< } - -.c.obj: - $(BCB)\BIN\$(BCC32) $(CFLAG1) $(WARNINGS) -I$(INCLUDEPATH) -D$(USERDEFINES);$(SYSDEFINES) -n$(@D) {$< } - -.c.i: - $(BCB)\BIN\$(CPP32) $(CFLAG1) $(WARNINGS) -I$(INCLUDEPATH) -D$(USERDEFINES);$(SYSDEFINES) -n. {$< } - -.cpp.i: - $(BCB)\BIN\$(CPP32) $(CFLAG1) $(WARNINGS) -I$(INCLUDEPATH) -D$(USERDEFINES);$(SYSDEFINES) -n. {$< } - -.asm.obj: - $(BCB)\BIN\$(TASM32) $(AFLAGS) -i$(INCLUDEPATH:;= -i) $(AUSERDEFINES) -d$(SYSDEFINES:;= -d) $<, $@ - -.rc.res: - $(BCB)\BIN\$(BRCC32) $(RFLAGS) -I$(INCLUDEPATH) -D$(USERDEFINES);$(SYSDEFINES) -fo$@ $< -# --------------------------------------------------------------------------- - - - - diff --git a/cextern/expat/bcb5/expatw.bpf b/cextern/expat/bcb5/expatw.bpf deleted file mode 100755 index 188a6d54eadb..000000000000 --- a/cextern/expat/bcb5/expatw.bpf +++ /dev/null @@ -1,6 +0,0 @@ -USEUNIT("..\lib\xmlparse.c"); -USEUNIT("..\lib\xmlrole.c"); -USEUNIT("..\lib\xmltok.c"); -USEDEF("libexpatw_mtd.def"); -//--------------------------------------------------------------------------- -#define DllEntryPoint diff --git a/cextern/expat/bcb5/expatw.bpr b/cextern/expat/bcb5/expatw.bpr deleted file mode 100755 index 9ec50010b723..000000000000 --- a/cextern/expat/bcb5/expatw.bpr +++ /dev/null @@ -1,146 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -[Version Info] -IncludeVerInfo=0 -AutoIncBuild=0 -MajorVer=1 -MinorVer=0 -Release=0 -Build=0 -Debug=0 -PreRelease=0 -Special=0 -Private=0 -DLL=0 -Locale=1033 -CodePage=1252 - -[Version Info Keys] -CompanyName= -FileDescription= -FileVersion=1.0.0.0 -InternalName= -LegalCopyright= -LegalTrademarks= -OriginalFilename= -ProductName= -ProductVersion=1.0.0.0 -Comments= - -[HistoryLists\hlIncludePath] -Count=4 -Item0=..\lib;$(BCB)\include -Item1=$(BCB)\include -Item2=$(BCB)\include;$(BCB)\include\mfc;$(BCB)\include\atl -Item3=$(BCB)\include;$(BCB)\include\mfc;$(BCB)\include\atl; - -[HistoryLists\hlLibraryPath] -Count=5 -Item0=..\lib;$(BCB)\lib;$(RELEASELIBPATH) -Item1=..\lib;$(BCB)\lib;..\lib\$(RELEASELIBPATH) -Item2=$(BCB)\lib;$(RELEASELIBPATH) -Item3=$(BCB)\lib;$(RELEASELIBPATH);$(BCB)\lib\psdk -Item4=$(BCB)\lib;$(RELEASELIBPATH);;$(BCB)\lib\psdk; - -[HistoryLists\hlDebugSourcePath] -Count=1 -Item0=$(BCB)\source\vcl - -[HistoryLists\hlConditionals] -Count=9 -Item0=_WINDOWS;WIN32;NDEBUG;_USRDLL;COMPILED_FROM_DSP;EXPAT_EXPORTS;XML_UNICODE_WCHAR_T -Item1=_WINDOWS;WIN32;NDEBUG;_DEBUG;_USRDLL;COMPILED_FROM_DSP;EXPAT_EXPORTS;XML_UNICODE_WCHAR_T -Item2=_WINDOWS;WIN32;NDEBUG;_DEBUG;_USRDLL;EXPAT_EXPORTS;COMPILED_FROM_DSP;XML_UNICODE_WCHAR_T -Item3=NDEBUG;COMPILED_FROM_DSP;WIN32;_WINDOWS;_USRDLL;EXPAT_EXPORTS;_DEBUG;XML_UNICODE_WCHAR_T -Item4=NDEBUG;COMPILED_FROM_DSP;WIN32;_WINDOWS;_USRDLL;EXPAT_EXPORTS;XML_UNICODE_WCHAR_T;_DEBUG -Item5=NDEBUG;COMPILED_FROM_DSP;WIN32;_WINDOWS;_UNICODE;_USRDLL;EXPAT_EXPORTS;XML_UNICODE_WCHAR_T;_DEBUG -Item6=NDEBUG;COMPILED_FROM_DSP;WIN32;_WINDOWS;_UNICODE;_USRDLL;EXPAT_EXPORTS;XML_UNICODE_WCHAR_T -Item7=NDEBUG;COMPILED_FROM_DSP;WIN32;_WINDOWS;_MBCS;_USRDLL;EXPAT_EXPORTS;XML_UNICODE_WCHAR_T;XML_UNICODE -Item8=NDEBUG;COMPILED_FROM_DSP;WIN32;_WINDOWS;_MBCS;_USRDLL;EXPAT_EXPORTS;XML_UNICODE_WCHAR_T - -[HistoryLists\hlIntOutputDir] -Count=8 -Item0=Release\obj\libexpatw -Item1=Release\obj\libexpat -Item2=Release\obj\mtd -Item3=Release\obj\mt -Item4=Release_w\obj -Item5=Release-w\obj -Item6=Release-w -Item7=..\lib\Release-w - -[HistoryLists\hlFinalOutputDir] -Count=5 -Item0=Release\ -Item1=Release -Item2=Release_w\ -Item3=Release-w\ -Item4=Release-w - -[Debugging] -DebugSourceDirs= - -[Parameters] -RunParams= -HostApplication= -RemoteHost= -RemotePath= -RemoteDebug=0 - -[Compiler] -ShowInfoMsgs=0 -LinkDebugVcl=0 -LinkCGLIB=0 - -[Language] -ActiveLang= -ProjectLang= -RootDir= - - \ No newline at end of file diff --git a/cextern/expat/bcb5/expatw.mak b/cextern/expat/bcb5/expatw.mak deleted file mode 100755 index 08e17d003b8c..000000000000 --- a/cextern/expat/bcb5/expatw.mak +++ /dev/null @@ -1,187 +0,0 @@ -# --------------------------------------------------------------------------- -!if !$d(BCB) -BCB = $(MAKEDIR)\.. -!endif - -# --------------------------------------------------------------------------- -# IDE SECTION -# --------------------------------------------------------------------------- -# The following section of the project makefile is managed by the BCB IDE. -# It is recommended to use the IDE to change any of the values in this -# section. -# --------------------------------------------------------------------------- - -VERSION = BCB.05.03 -# --------------------------------------------------------------------------- -PROJECT = Release\libexpatw_mtd.dll -OBJFILES = Release\obj\libexpatw\xmlparse.obj Release\obj\libexpatw\xmlrole.obj \ - Release\obj\libexpatw\xmltok.obj -RESFILES = -MAINSOURCE = expatw.bpf -RESDEPEN = $(RESFILES) -LIBFILES = -IDLFILES = -IDLGENFILES = -LIBRARIES = -PACKAGES = VCL50.bpi VCLX50.bpi bcbsmp50.bpi QRPT50.bpi VCLDB50.bpi VCLBDE50.bpi \ - ibsmp50.bpi VCLDBX50.bpi TEEUI50.bpi TEEDB50.bpi TEE50.bpi TEEQR50.bpi \ - VCLIB50.bpi bcbie50.bpi VCLIE50.bpi INETDB50.bpi INET50.bpi NMFAST50.bpi \ - dclocx50.bpi bcb2kaxserver50.bpi dclusr50.bpi -SPARELIBS = -DEFFILE = libexpatw_mtd.def -# --------------------------------------------------------------------------- -PATHCPP = .;..\lib -PATHASM = .; -PATHPAS = .; -PATHRC = .; -DEBUGLIBPATH = $(BCB)\lib\debug -RELEASELIBPATH = $(BCB)\lib\release -USERDEFINES = _WINDOWS;WIN32;NDEBUG;_USRDLL;COMPILED_FROM_DSP;XML_UNICODE_WCHAR_T -SYSDEFINES = _NO_VCL;_ASSERTE;NO_STRICT;_RTLDLL -INCLUDEPATH = ..\lib;$(BCB)\include -LIBPATH = ..\lib;$(BCB)\lib;$(RELEASELIBPATH) -WARNINGS= -w-rch -w-par -w-8027 -w-8026 -w-ccc -# --------------------------------------------------------------------------- -CFLAG1 = -WD -O2 -X- -a8 -b -k- -vi -q -tWM -c -tWD -IDLCFLAGS = -I$(BCB)\include -PFLAGS = -N2Release\obj\libexpatw -N0Release\obj\libexpatw -$Y- -$L- -$D- -RFLAGS = /l 0x409 /d "NDEBUG" /i$(BCB)\include -AFLAGS = /mx /w2 /zn -LFLAGS = -IRelease\obj\libexpatw -D"" -aa -Tpd -x -Gn -Gi -w -q -# --------------------------------------------------------------------------- -ALLOBJ = c0d32w.obj $(OBJFILES) -ALLRES = $(RESFILES) -ALLLIB = $(LIBFILES) $(LIBRARIES) import32.lib cw32mti.lib -# --------------------------------------------------------------------------- -!ifdef IDEOPTIONS - -[Version Info] -IncludeVerInfo=0 -AutoIncBuild=0 -MajorVer=1 -MinorVer=0 -Release=0 -Build=0 -Debug=0 -PreRelease=0 -Special=0 -Private=0 -DLL=0 - -[Version Info Keys] -CompanyName= -FileDescription= -FileVersion=1.0.0.0 -InternalName= -LegalCopyright= -LegalTrademarks= -OriginalFilename= -ProductName= -ProductVersion=1.0.0.0 -Comments= - -[Debugging] -DebugSourceDirs=$(BCB)\source\vcl - -!endif - - - - - -# --------------------------------------------------------------------------- -# MAKE SECTION -# --------------------------------------------------------------------------- -# This section of the project file is not used by the BCB IDE. It is for -# the benefit of building from the command-line using the MAKE utility. -# --------------------------------------------------------------------------- - -.autodepend -# --------------------------------------------------------------------------- -!if "$(USERDEFINES)" != "" -AUSERDEFINES = -d$(USERDEFINES:;= -d) -!else -AUSERDEFINES = -!endif - -!if !$d(BCC32) -BCC32 = bcc32 -!endif - -!if !$d(CPP32) -CPP32 = cpp32 -!endif - -!if !$d(DCC32) -DCC32 = dcc32 -!endif - -!if !$d(TASM32) -TASM32 = tasm32 -!endif - -!if !$d(LINKER) -LINKER = ilink32 -!endif - -!if !$d(BRCC32) -BRCC32 = brcc32 -!endif - - -# --------------------------------------------------------------------------- -!if $d(PATHCPP) -.PATH.CPP = $(PATHCPP) -.PATH.C = $(PATHCPP) -!endif - -!if $d(PATHPAS) -.PATH.PAS = $(PATHPAS) -!endif - -!if $d(PATHASM) -.PATH.ASM = $(PATHASM) -!endif - -!if $d(PATHRC) -.PATH.RC = $(PATHRC) -!endif -# --------------------------------------------------------------------------- -$(PROJECT): $(IDLGENFILES) $(OBJFILES) $(RESDEPEN) $(DEFFILE) - $(BCB)\BIN\$(LINKER) @&&! - $(LFLAGS) -L$(LIBPATH) + - $(ALLOBJ), + - $(PROJECT),, + - $(ALLLIB), + - $(DEFFILE), + - $(ALLRES) -! -# --------------------------------------------------------------------------- -.pas.hpp: - $(BCB)\BIN\$(DCC32) $(PFLAGS) -U$(INCLUDEPATH) -D$(USERDEFINES);$(SYSDEFINES) -O$(INCLUDEPATH) --BCB {$< } - -.pas.obj: - $(BCB)\BIN\$(DCC32) $(PFLAGS) -U$(INCLUDEPATH) -D$(USERDEFINES);$(SYSDEFINES) -O$(INCLUDEPATH) --BCB {$< } - -.cpp.obj: - $(BCB)\BIN\$(BCC32) $(CFLAG1) $(WARNINGS) -I$(INCLUDEPATH) -D$(USERDEFINES);$(SYSDEFINES) -n$(@D) {$< } - -.c.obj: - $(BCB)\BIN\$(BCC32) $(CFLAG1) $(WARNINGS) -I$(INCLUDEPATH) -D$(USERDEFINES);$(SYSDEFINES) -n$(@D) {$< } - -.c.i: - $(BCB)\BIN\$(CPP32) $(CFLAG1) $(WARNINGS) -I$(INCLUDEPATH) -D$(USERDEFINES);$(SYSDEFINES) -n. {$< } - -.cpp.i: - $(BCB)\BIN\$(CPP32) $(CFLAG1) $(WARNINGS) -I$(INCLUDEPATH) -D$(USERDEFINES);$(SYSDEFINES) -n. {$< } - -.asm.obj: - $(BCB)\BIN\$(TASM32) $(AFLAGS) -i$(INCLUDEPATH:;= -i) $(AUSERDEFINES) -d$(SYSDEFINES:;= -d) $<, $@ - -.rc.res: - $(BCB)\BIN\$(BRCC32) $(RFLAGS) -I$(INCLUDEPATH) -D$(USERDEFINES);$(SYSDEFINES) -fo$@ $< -# --------------------------------------------------------------------------- - - - - diff --git a/cextern/expat/bcb5/expatw_static.bpf b/cextern/expat/bcb5/expatw_static.bpf deleted file mode 100755 index 5ca458edcaec..000000000000 --- a/cextern/expat/bcb5/expatw_static.bpf +++ /dev/null @@ -1,5 +0,0 @@ -USEUNIT("..\lib\xmlparse.c"); -USEUNIT("..\lib\xmlrole.c"); -USEUNIT("..\lib\xmltok.c"); -//--------------------------------------------------------------------------- -#define Library diff --git a/cextern/expat/bcb5/expatw_static.bpr b/cextern/expat/bcb5/expatw_static.bpr deleted file mode 100755 index 3f12644b64c7..000000000000 --- a/cextern/expat/bcb5/expatw_static.bpr +++ /dev/null @@ -1,152 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -[Version Info] -IncludeVerInfo=0 -AutoIncBuild=0 -MajorVer=1 -MinorVer=0 -Release=0 -Build=0 -Debug=0 -PreRelease=0 -Special=0 -Private=0 -DLL=0 -Locale=1033 -CodePage=1252 - -[Version Info Keys] -CompanyName= -FileDescription= -FileVersion=1.0.0.0 -InternalName= -LegalCopyright= -LegalTrademarks= -OriginalFilename= -ProductName= -ProductVersion=1.0.0.0 -Comments= - -[HistoryLists\hlIncludePath] -Count=4 -Item0=..\lib;$(BCB)\include -Item1=$(BCB)\include -Item2=$(BCB)\include;$(BCB)\include\mfc;$(BCB)\include\atl -Item3=$(BCB)\include;$(BCB)\include\mfc;$(BCB)\include\atl; - -[HistoryLists\hlLibraryPath] -Count=5 -Item0=..\lib;$(BCB)\lib;$(RELEASELIBPATH) -Item1=..\lib;$(BCB)\lib;..\lib\$(RELEASELIBPATH) -Item2=$(BCB)\lib;$(RELEASELIBPATH) -Item3=$(BCB)\lib;$(RELEASELIBPATH);$(BCB)\lib\psdk -Item4=$(BCB)\lib;$(RELEASELIBPATH);;$(BCB)\lib\psdk; - -[HistoryLists\hlDebugSourcePath] -Count=1 -Item0=$(BCB)\source\vcl - -[HistoryLists\hlConditionals] -Count=15 -Item0=_WINDOWS;WIN32;NDEBUG;_LIB;COMPILED_FROM_DSP;XML_STATIC;XML_UNICODE_WCHAR_T -Item1=_WINDOWS;WIN32;NDEBUG;_DEBUG;_LIB;COMPILED_FROM_DSP;XML_STATIC;XML_UNICODE_WCHAR_T -Item2=WIN32;_WINDOWS;NDEBUG;_DEBUG;_LIB;COMPILED_FROM_DSP;XML_STATIC;XML_UNICODE_WCHAR_T -Item3=WIN32;_WINDOWS;NDEBUG;_DEBUG;_LIB;XML_STATIC;COMPILED_FROM_DSP;XML_UNICODE_WCHAR_T -Item4=WIN32;_WINDOWS;NDEBUG;_LIB;COMPILED_FROM_DSP;_DEBUG;XML_UNICODE_WCHAR_T -Item5=WIN32;_WINDOWS;NDEBUG;_UNICODE;_LIB;COMPILED_FROM_DSP;XML_UNICODE_WCHAR_T;_DEBUG -Item6=WIN32;_WINDOWS;NDEBUG;_UNICODE;_LIB;COMPILED_FROM_DSP;XML_UNICODE_WCHAR_T;_DEBUG;__cplusplus -Item7=WIN32;_WINDOWS;NDEBUG;_UNICODE;_LIB;COMPILED_FROM_DSP;XML_UNICODE;_DEBUG -Item8=WIN32;_WINDOWS;NDEBUG;_MBCS;_LIB;COMPILED_FROM_DSP;XML_UNICODE;_DEBUG -Item9=WIN32;_WINDOWS;NDEBUG;_MBCS;_LIB;COMPILED_FROM_DSP;XML_UNICODE_WCHAR_T;_DEBUG;__WCHAR_T -Item10=WIN32;_WINDOWS;NDEBUG;_MBCS;_LIB;COMPILED_FROM_DSP;XML_UNICODE_WCHAR_T;_DEBUG;_UNICODE -Item11=WIN32;_WINDOWS;NDEBUG;_MBCS;_LIB;COMPILED_FROM_DSP;XML_UNICODE;_DEBUG;_UNICODE -Item12=WIN32;_WINDOWS;NDEBUG;_MBCS;_LIB;COMPILED_FROM_DSP;XML_UNICODE_WCHAR_T;_DEBUG -Item13=WIN32;_WINDOWS;NDEBUG;_MBCS;_LIB;COMPILED_FROM_DSP;XML_UNICODE_WCHAR_T -Item14=WIN32;_WINDOWS;NDEBUG;_MBCS;_LIB;COMPILED_FROM_DSP;XML_UNICODE_WCHAR_T;XML_UNICODE - -[HistoryLists\hlIntOutputDir] -Count=6 -Item0=Release\obj\libexpatw_static -Item1=Release\obj\libexpat_static -Item2=Release\obj\mts -Item3=Release\obj\mt -Item4=..\lib\Release-w_static -Item5=Release-w_static - -[HistoryLists\hlFinalOutputDir] -Count=3 -Item0=Release\ -Item1=Release -Item2=Release-w_static\ - -[HistoryLists\hlTlibPageSize] -Count=1 -Item0=0x0010 - -[Debugging] -DebugSourceDirs= - -[Parameters] -RunParams= -HostApplication= -RemoteHost= -RemotePath= -RemoteDebug=0 - -[Compiler] -ShowInfoMsgs=0 -LinkDebugVcl=0 -LinkCGLIB=0 - -[Language] -ActiveLang= -ProjectLang= -RootDir= - - \ No newline at end of file diff --git a/cextern/expat/bcb5/expatw_static.mak b/cextern/expat/bcb5/expatw_static.mak deleted file mode 100755 index 16b7e5b90ec7..000000000000 --- a/cextern/expat/bcb5/expatw_static.mak +++ /dev/null @@ -1,190 +0,0 @@ -# --------------------------------------------------------------------------- -!if !$d(BCB) -BCB = $(MAKEDIR)\.. -!endif - -# --------------------------------------------------------------------------- -# IDE SECTION -# --------------------------------------------------------------------------- -# The following section of the project makefile is managed by the BCB IDE. -# It is recommended to use the IDE to change any of the values in this -# section. -# --------------------------------------------------------------------------- - -VERSION = BCB.05.03 -# --------------------------------------------------------------------------- -PROJECT = Release\libexpatws_mtd.lib -OBJFILES = Release\obj\libexpatw_static\xmlparse.obj \ - Release\obj\libexpatw_static\xmlrole.obj \ - Release\obj\libexpatw_static\xmltok.obj -RESFILES = -MAINSOURCE = expatw_static.bpf -RESDEPEN = $(RESFILES) -LIBFILES = -IDLFILES = -IDLGENFILES = -LIBRARIES = -PACKAGES = -SPARELIBS = -DEFFILE = -# --------------------------------------------------------------------------- -PATHCPP = .;..\lib -PATHASM = .; -PATHPAS = .; -PATHRC = .; -LINKER = TLib -DEBUGLIBPATH = $(BCB)\lib\debug -RELEASELIBPATH = $(BCB)\lib\release -USERDEFINES = _WINDOWS;WIN32;NDEBUG;_LIB;COMPILED_FROM_DSP;XML_STATIC;XML_UNICODE_WCHAR_T -SYSDEFINES = _NO_VCL;_ASSERTE;NO_STRICT;_RTLDLL -INCLUDEPATH = ..\lib;$(BCB)\include -LIBPATH = ..\lib;$(BCB)\lib;$(RELEASELIBPATH) -WARNINGS = -w-rch -w-par -w-8027 -w-8026 -w-ccc -LISTFILE = -# --------------------------------------------------------------------------- -CFLAG1 = -O2 -X- -a8 -b -k- -vi -q -tWM -c -IDLCFLAGS = -I$(BCB)\include -PFLAGS = -N2Release\obj\libexpatw_static -N0Release\obj\libexpatw_static -$Y- -$L- \ - -$D- -RFLAGS = /l 0x409 /d "NDEBUG" /i$(BCB)\include -AFLAGS = /mx /w2 /zn -LFLAGS = -# --------------------------------------------------------------------------- -ALLOBJ = $(OBJFILES) -ALLRES = $(RESFILES) -ALLLIB = $(LIBFILES) $(LIBRARIES) -# --------------------------------------------------------------------------- -!ifdef IDEOPTIONS - -[Version Info] -IncludeVerInfo=0 -AutoIncBuild=0 -MajorVer=1 -MinorVer=0 -Release=0 -Build=0 -Debug=0 -PreRelease=0 -Special=0 -Private=0 -DLL=0 - -[Version Info Keys] -CompanyName= -FileDescription= -FileVersion=1.0.0.0 -InternalName= -LegalCopyright= -LegalTrademarks= -OriginalFilename= -ProductName= -ProductVersion=1.0.0.0 -Comments= - -[Debugging] -DebugSourceDirs=$(BCB)\source\vcl - -!endif - - - - - -# --------------------------------------------------------------------------- -# MAKE SECTION -# --------------------------------------------------------------------------- -# This section of the project file is not used by the BCB IDE. It is for -# the benefit of building from the command-line using the MAKE utility. -# --------------------------------------------------------------------------- - -.autodepend -# --------------------------------------------------------------------------- -!if "$(USERDEFINES)" != "" -AUSERDEFINES = -d$(USERDEFINES:;= -d) -!else -AUSERDEFINES = -!endif - -!if !$d(BCC32) -BCC32 = bcc32 -!endif - -!if !$d(CPP32) -CPP32 = cpp32 -!endif - -!if !$d(DCC32) -DCC32 = dcc32 -!endif - -!if !$d(TASM32) -TASM32 = tasm32 -!endif - -!if !$d(LINKER) -LINKER = TLib -!endif - -!if !$d(BRCC32) -BRCC32 = brcc32 -!endif - - -# --------------------------------------------------------------------------- -!if $d(PATHCPP) -.PATH.CPP = $(PATHCPP) -.PATH.C = $(PATHCPP) -!endif - -!if $d(PATHPAS) -.PATH.PAS = $(PATHPAS) -!endif - -!if $d(PATHASM) -.PATH.ASM = $(PATHASM) -!endif - -!if $d(PATHRC) -.PATH.RC = $(PATHRC) -!endif -# --------------------------------------------------------------------------- -!if "$(LISTFILE)" == "" -COMMA = -!else -COMMA = , -!endif - -$(PROJECT): $(IDLGENFILES) $(OBJFILES) $(RESDEPEN) $(DEFFILE) - $(BCB)\BIN\$(LINKER) /u $@ @&&! - $(LFLAGS) $? $(COMMA) $(LISTFILE) - -! -# --------------------------------------------------------------------------- -.pas.hpp: - $(BCB)\BIN\$(DCC32) $(PFLAGS) -U$(INCLUDEPATH) -D$(USERDEFINES);$(SYSDEFINES) -O$(INCLUDEPATH) --BCB {$< } - -.pas.obj: - $(BCB)\BIN\$(DCC32) $(PFLAGS) -U$(INCLUDEPATH) -D$(USERDEFINES);$(SYSDEFINES) -O$(INCLUDEPATH) --BCB {$< } - -.cpp.obj: - $(BCB)\BIN\$(BCC32) $(CFLAG1) $(WARNINGS) -I$(INCLUDEPATH) -D$(USERDEFINES);$(SYSDEFINES) -n$(@D) {$< } - -.c.obj: - $(BCB)\BIN\$(BCC32) $(CFLAG1) $(WARNINGS) -I$(INCLUDEPATH) -D$(USERDEFINES);$(SYSDEFINES) -n$(@D) {$< } - -.c.i: - $(BCB)\BIN\$(CPP32) $(CFLAG1) $(WARNINGS) -I$(INCLUDEPATH) -D$(USERDEFINES);$(SYSDEFINES) -n. {$< } - -.cpp.i: - $(BCB)\BIN\$(CPP32) $(CFLAG1) $(WARNINGS) -I$(INCLUDEPATH) -D$(USERDEFINES);$(SYSDEFINES) -n. {$< } - -.asm.obj: - $(BCB)\BIN\$(TASM32) $(AFLAGS) -i$(INCLUDEPATH:;= -i) $(AUSERDEFINES) -d$(SYSDEFINES:;= -d) $<, $@ - -.rc.res: - $(BCB)\BIN\$(BRCC32) $(RFLAGS) -I$(INCLUDEPATH) -D$(USERDEFINES);$(SYSDEFINES) -fo$@ $< -# --------------------------------------------------------------------------- - - - - diff --git a/cextern/expat/bcb5/libexpat_mtd.def b/cextern/expat/bcb5/libexpat_mtd.def deleted file mode 100755 index fc1cb9554fa7..000000000000 --- a/cextern/expat/bcb5/libexpat_mtd.def +++ /dev/null @@ -1,141 +0,0 @@ -; DEF file for BCB5 -LIBRARY LIBEXPAT_MTD -DESCRIPTION "Implements an XML parser." -EXPORTS - _XML_DefaultCurrent @1 - _XML_ErrorString @2 - _XML_ExpatVersion @3 - _XML_ExpatVersionInfo @4 - _XML_ExternalEntityParserCreate @5 - _XML_GetBase @6 - _XML_GetBuffer @7 - _XML_GetCurrentByteCount @8 - _XML_GetCurrentByteIndex @9 - _XML_GetCurrentColumnNumber @10 - _XML_GetCurrentLineNumber @11 - _XML_GetErrorCode @12 - _XML_GetIdAttributeIndex @13 - _XML_GetInputContext @14 - _XML_GetSpecifiedAttributeCount @15 - _XML_Parse @16 - _XML_ParseBuffer @17 - _XML_ParserCreate @18 - _XML_ParserCreateNS @19 - _XML_ParserCreate_MM @20 - _XML_ParserFree @21 - _XML_SetAttlistDeclHandler @22 - _XML_SetBase @23 - _XML_SetCdataSectionHandler @24 - _XML_SetCharacterDataHandler @25 - _XML_SetCommentHandler @26 - _XML_SetDefaultHandler @27 - _XML_SetDefaultHandlerExpand @28 - _XML_SetDoctypeDeclHandler @29 - _XML_SetElementDeclHandler @30 - _XML_SetElementHandler @31 - _XML_SetEncoding @32 - _XML_SetEndCdataSectionHandler @33 - _XML_SetEndDoctypeDeclHandler @34 - _XML_SetEndElementHandler @35 - _XML_SetEndNamespaceDeclHandler @36 - _XML_SetEntityDeclHandler @37 - _XML_SetExternalEntityRefHandler @38 - _XML_SetExternalEntityRefHandlerArg @39 - _XML_SetNamespaceDeclHandler @40 - _XML_SetNotStandaloneHandler @41 - _XML_SetNotationDeclHandler @42 - _XML_SetParamEntityParsing @43 - _XML_SetProcessingInstructionHandler @44 - _XML_SetReturnNSTriplet @45 - _XML_SetStartCdataSectionHandler @46 - _XML_SetStartDoctypeDeclHandler @47 - _XML_SetStartElementHandler @48 - _XML_SetStartNamespaceDeclHandler @49 - _XML_SetUnknownEncodingHandler @50 - _XML_SetUnparsedEntityDeclHandler @51 - _XML_SetUserData @52 - _XML_SetXmlDeclHandler @53 - _XML_UseParserAsHandlerArg @54 -; added with version 1.95.3 - _XML_ParserReset @55 - _XML_SetSkippedEntityHandler @56 -; added with version 1.95.5 - _XML_GetFeatureList @57 - _XML_UseForeignDTD @58 -; added with version 1.95.6 - _XML_FreeContentModel @59 - _XML_MemMalloc @60 - _XML_MemRealloc @61 - _XML_MemFree @62 -; added with version 1.95.8 - _XML_StopParser @63 - _XML_ResumeParser @64 - _XML_GetParsingStatus @65 - -; Aliases for MS compatible names - XML_DefaultCurrent = _XML_DefaultCurrent - XML_ErrorString = _XML_ErrorString - XML_ExpatVersion = _XML_ExpatVersion - XML_ExpatVersionInfo = _XML_ExpatVersionInfo - XML_ExternalEntityParserCreate = _XML_ExternalEntityParserCreate - XML_GetBase = _XML_GetBase - XML_GetBuffer = _XML_GetBuffer - XML_GetCurrentByteCount = _XML_GetCurrentByteCount - XML_GetCurrentByteIndex = _XML_GetCurrentByteIndex - XML_GetCurrentColumnNumber = _XML_GetCurrentColumnNumber - XML_GetCurrentLineNumber = _XML_GetCurrentLineNumber - XML_GetErrorCode = _XML_GetErrorCode - XML_GetIdAttributeIndex = _XML_GetIdAttributeIndex - XML_GetInputContext = _XML_GetInputContext - XML_GetSpecifiedAttributeCount = _XML_GetSpecifiedAttributeCount - XML_Parse = _XML_Parse - XML_ParseBuffer = _XML_ParseBuffer - XML_ParserCreate = _XML_ParserCreate - XML_ParserCreateNS = _XML_ParserCreateNS - XML_ParserCreate_MM = _XML_ParserCreate_MM - XML_ParserFree = _XML_ParserFree - XML_SetAttlistDeclHandler = _XML_SetAttlistDeclHandler - XML_SetBase = _XML_SetBase - XML_SetCdataSectionHandler = _XML_SetCdataSectionHandler - XML_SetCharacterDataHandler = _XML_SetCharacterDataHandler - XML_SetCommentHandler = _XML_SetCommentHandler - XML_SetDefaultHandler = _XML_SetDefaultHandler - XML_SetDefaultHandlerExpand = _XML_SetDefaultHandlerExpand - XML_SetDoctypeDeclHandler = _XML_SetDoctypeDeclHandler - XML_SetElementDeclHandler = _XML_SetElementDeclHandler - XML_SetElementHandler = _XML_SetElementHandler - XML_SetEncoding = _XML_SetEncoding - XML_SetEndCdataSectionHandler = _XML_SetEndCdataSectionHandler - XML_SetEndDoctypeDeclHandler = _XML_SetEndDoctypeDeclHandler - XML_SetEndElementHandler = _XML_SetEndElementHandler - XML_SetEndNamespaceDeclHandler = _XML_SetEndNamespaceDeclHandler - XML_SetEntityDeclHandler = _XML_SetEntityDeclHandler - XML_SetExternalEntityRefHandler = _XML_SetExternalEntityRefHandler - XML_SetExternalEntityRefHandlerArg = _XML_SetExternalEntityRefHandlerArg - XML_SetNamespaceDeclHandler = _XML_SetNamespaceDeclHandler - XML_SetNotStandaloneHandler = _XML_SetNotStandaloneHandler - XML_SetNotationDeclHandler = _XML_SetNotationDeclHandler - XML_SetParamEntityParsing = _XML_SetParamEntityParsing - XML_SetProcessingInstructionHandler = _XML_SetProcessingInstructionHandler - XML_SetReturnNSTriplet = _XML_SetReturnNSTriplet - XML_SetStartCdataSectionHandler = _XML_SetStartCdataSectionHandler - XML_SetStartDoctypeDeclHandler = _XML_SetStartDoctypeDeclHandler - XML_SetStartElementHandler = _XML_SetStartElementHandler - XML_SetStartNamespaceDeclHandler = _XML_SetStartNamespaceDeclHandler - XML_SetUnknownEncodingHandler = _XML_SetUnknownEncodingHandler - XML_SetUnparsedEntityDeclHandler = _XML_SetUnparsedEntityDeclHandler - XML_SetUserData = _XML_SetUserData - XML_SetXmlDeclHandler = _XML_SetXmlDeclHandler - XML_UseParserAsHandlerArg = _XML_UseParserAsHandlerArg - XML_ParserReset = _XML_ParserReset - XML_SetSkippedEntityHandler = _XML_SetSkippedEntityHandler - XML_GetFeatureList = _XML_GetFeatureList - XML_UseForeignDTD = _XML_UseForeignDTD - XML_FreeContentModel = _XML_FreeContentModel - XML_MemMalloc = _XML_MemMalloc - XML_MemRealloc = _XML_MemRealloc - XML_MemFree = _XML_MemFree - XML_StopParser = _XML_StopParser - XML_ResumeParser = _XML_ResumeParser - XML_GetParsingStatus = _XML_GetParsingStatus - diff --git a/cextern/expat/bcb5/libexpatw_mtd.def b/cextern/expat/bcb5/libexpatw_mtd.def deleted file mode 100755 index 418462df5a4a..000000000000 --- a/cextern/expat/bcb5/libexpatw_mtd.def +++ /dev/null @@ -1,140 +0,0 @@ -; DEF file for BCB5 -LIBRARY LIBEXPATW_MTD -DESCRIPTION "Implements an XML parser." -EXPORTS - _XML_DefaultCurrent @1 - _XML_ErrorString @2 - _XML_ExpatVersion @3 - _XML_ExpatVersionInfo @4 - _XML_ExternalEntityParserCreate @5 - _XML_GetBase @6 - _XML_GetBuffer @7 - _XML_GetCurrentByteCount @8 - _XML_GetCurrentByteIndex @9 - _XML_GetCurrentColumnNumber @10 - _XML_GetCurrentLineNumber @11 - _XML_GetErrorCode @12 - _XML_GetIdAttributeIndex @13 - _XML_GetInputContext @14 - _XML_GetSpecifiedAttributeCount @15 - _XML_Parse @16 - _XML_ParseBuffer @17 - _XML_ParserCreate @18 - _XML_ParserCreateNS @19 - _XML_ParserCreate_MM @20 - _XML_ParserFree @21 - _XML_SetAttlistDeclHandler @22 - _XML_SetBase @23 - _XML_SetCdataSectionHandler @24 - _XML_SetCharacterDataHandler @25 - _XML_SetCommentHandler @26 - _XML_SetDefaultHandler @27 - _XML_SetDefaultHandlerExpand @28 - _XML_SetDoctypeDeclHandler @29 - _XML_SetElementDeclHandler @30 - _XML_SetElementHandler @31 - _XML_SetEncoding @32 - _XML_SetEndCdataSectionHandler @33 - _XML_SetEndDoctypeDeclHandler @34 - _XML_SetEndElementHandler @35 - _XML_SetEndNamespaceDeclHandler @36 - _XML_SetEntityDeclHandler @37 - _XML_SetExternalEntityRefHandler @38 - _XML_SetExternalEntityRefHandlerArg @39 - _XML_SetNamespaceDeclHandler @40 - _XML_SetNotStandaloneHandler @41 - _XML_SetNotationDeclHandler @42 - _XML_SetParamEntityParsing @43 - _XML_SetProcessingInstructionHandler @44 - _XML_SetReturnNSTriplet @45 - _XML_SetStartCdataSectionHandler @46 - _XML_SetStartDoctypeDeclHandler @47 - _XML_SetStartElementHandler @48 - _XML_SetStartNamespaceDeclHandler @49 - _XML_SetUnknownEncodingHandler @50 - _XML_SetUnparsedEntityDeclHandler @51 - _XML_SetUserData @52 - _XML_SetXmlDeclHandler @53 - _XML_UseParserAsHandlerArg @54 -; added with version 1.95.3 - _XML_ParserReset @55 - _XML_SetSkippedEntityHandler @56 -; added with version 1.95.5 - _XML_GetFeatureList @57 - _XML_UseForeignDTD @58 -; added with version 1.95.6 - _XML_FreeContentModel @59 - _XML_MemMalloc @60 - _XML_MemRealloc @61 - _XML_MemFree @62 -; added with version 1.95.8 - _XML_StopParser @63 - _XML_ResumeParser @64 - _XML_GetParsingStatus @65 - -; Aliases for MS compatible names - XML_DefaultCurrent = _XML_DefaultCurrent - XML_ErrorString = _XML_ErrorString - XML_ExpatVersion = _XML_ExpatVersion - XML_ExpatVersionInfo = _XML_ExpatVersionInfo - XML_ExternalEntityParserCreate = _XML_ExternalEntityParserCreate - XML_GetBase = _XML_GetBase - XML_GetBuffer = _XML_GetBuffer - XML_GetCurrentByteCount = _XML_GetCurrentByteCount - XML_GetCurrentByteIndex = _XML_GetCurrentByteIndex - XML_GetCurrentColumnNumber = _XML_GetCurrentColumnNumber - XML_GetCurrentLineNumber = _XML_GetCurrentLineNumber - XML_GetErrorCode = _XML_GetErrorCode - XML_GetIdAttributeIndex = _XML_GetIdAttributeIndex - XML_GetInputContext = _XML_GetInputContext - XML_GetSpecifiedAttributeCount = _XML_GetSpecifiedAttributeCount - XML_Parse = _XML_Parse - XML_ParseBuffer = _XML_ParseBuffer - XML_ParserCreate = _XML_ParserCreate - XML_ParserCreateNS = _XML_ParserCreateNS - XML_ParserCreate_MM = _XML_ParserCreate_MM - XML_ParserFree = _XML_ParserFree - XML_SetAttlistDeclHandler = _XML_SetAttlistDeclHandler - XML_SetBase = _XML_SetBase - XML_SetCdataSectionHandler = _XML_SetCdataSectionHandler - XML_SetCharacterDataHandler = _XML_SetCharacterDataHandler - XML_SetCommentHandler = _XML_SetCommentHandler - XML_SetDefaultHandler = _XML_SetDefaultHandler - XML_SetDefaultHandlerExpand = _XML_SetDefaultHandlerExpand - XML_SetDoctypeDeclHandler = _XML_SetDoctypeDeclHandler - XML_SetElementDeclHandler = _XML_SetElementDeclHandler - XML_SetElementHandler = _XML_SetElementHandler - XML_SetEncoding = _XML_SetEncoding - XML_SetEndCdataSectionHandler = _XML_SetEndCdataSectionHandler - XML_SetEndDoctypeDeclHandler = _XML_SetEndDoctypeDeclHandler - XML_SetEndElementHandler = _XML_SetEndElementHandler - XML_SetEndNamespaceDeclHandler = _XML_SetEndNamespaceDeclHandler - XML_SetEntityDeclHandler = _XML_SetEntityDeclHandler - XML_SetExternalEntityRefHandler = _XML_SetExternalEntityRefHandler - XML_SetExternalEntityRefHandlerArg = _XML_SetExternalEntityRefHandlerArg - XML_SetNamespaceDeclHandler = _XML_SetNamespaceDeclHandler - XML_SetNotStandaloneHandler = _XML_SetNotStandaloneHandler - XML_SetNotationDeclHandler = _XML_SetNotationDeclHandler - XML_SetParamEntityParsing = _XML_SetParamEntityParsing - XML_SetProcessingInstructionHandler = _XML_SetProcessingInstructionHandler - XML_SetReturnNSTriplet = _XML_SetReturnNSTriplet - XML_SetStartCdataSectionHandler = _XML_SetStartCdataSectionHandler - XML_SetStartDoctypeDeclHandler = _XML_SetStartDoctypeDeclHandler - XML_SetStartElementHandler = _XML_SetStartElementHandler - XML_SetStartNamespaceDeclHandler = _XML_SetStartNamespaceDeclHandler - XML_SetUnknownEncodingHandler = _XML_SetUnknownEncodingHandler - XML_SetUnparsedEntityDeclHandler = _XML_SetUnparsedEntityDeclHandler - XML_SetUserData = _XML_SetUserData - XML_SetXmlDeclHandler = _XML_SetXmlDeclHandler - XML_UseParserAsHandlerArg = _XML_UseParserAsHandlerArg - XML_ParserReset = _XML_ParserReset - XML_SetSkippedEntityHandler = _XML_SetSkippedEntityHandler - XML_GetFeatureList = _XML_GetFeatureList - XML_UseForeignDTD = _XML_UseForeignDTD - XML_FreeContentModel = _XML_FreeContentModel - XML_MemMalloc = _XML_MemMalloc - XML_MemRealloc = _XML_MemRealloc - XML_MemFree = _XML_MemFree - XML_StopParser = _XML_StopParser - XML_ResumeParser = _XML_ResumeParser - XML_GetParsingStatus = _XML_GetParsingStatus diff --git a/cextern/expat/bcb5/makefile.mak b/cextern/expat/bcb5/makefile.mak deleted file mode 100755 index 548815eafe7b..000000000000 --- a/cextern/expat/bcb5/makefile.mak +++ /dev/null @@ -1,37 +0,0 @@ -all: setup expat expatw expat_static expatw_static elements outline xmlwf - -setup: - setup - -expat: - make -l -fexpat.mak - -expatw: - make -l -fexpatw.mak - -expat_static: - make -l -fexpat_static.mak - -expatw_static: - make -l -fexpatw_static.mak - -elements: - make -l -felements.mak - -outline: - make -l -foutline.mak - -xmlwf: - make -l -fxmlwf.mak - -clean: -# works on Win98/ME -# deltree /y release\obj -# works on WinNT/2000 - del /s/f/q release\obj - -distclean: -# works on Win98/ME -# deltree /y release\*.* -# works on WinNT/2000 - del /s/f/q release\* diff --git a/cextern/expat/bcb5/outline.bpf b/cextern/expat/bcb5/outline.bpf deleted file mode 100755 index 52dce830163e..000000000000 --- a/cextern/expat/bcb5/outline.bpf +++ /dev/null @@ -1,4 +0,0 @@ -USEUNIT("..\examples\outline.c"); -USELIB("Release\libexpat_mtd.lib"); -//--------------------------------------------------------------------------- -main diff --git a/cextern/expat/bcb5/outline.bpr b/cextern/expat/bcb5/outline.bpr deleted file mode 100755 index e0ae5cdeebdc..000000000000 --- a/cextern/expat/bcb5/outline.bpr +++ /dev/null @@ -1,132 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -[Version Info] -IncludeVerInfo=0 -AutoIncBuild=0 -MajorVer=1 -MinorVer=0 -Release=0 -Build=0 -Debug=0 -PreRelease=0 -Special=0 -Private=0 -DLL=0 -Locale=1033 -CodePage=1252 - -[Version Info Keys] -CompanyName= -FileDescription= -FileVersion=1.0.0.0 -InternalName= -LegalCopyright= -LegalTrademarks= -OriginalFilename= -ProductName= -ProductVersion=1.0.0.0 -Comments= - -[HistoryLists\hlIncludePath] -Count=3 -Item0=..\examples;$(BCB)\include -Item1=$(BCB)\include -Item2=$(BCB)\include;$(BCB)\include\mfc;$(BCB)\include\atl; - -[HistoryLists\hlLibraryPath] -Count=4 -Item0=..\examples;$(BCB)\lib;$(RELEASELIBPATH) -Item1=..\examples;$(BCB)\lib;..\examples\$(RELEASELIBPATH) -Item2=$(BCB)\lib;$(RELEASELIBPATH) -Item3=$(BCB)\lib;$(RELEASELIBPATH);;$(BCB)\lib\psdk; - -[HistoryLists\hlDebugSourcePath] -Count=1 -Item0=$(BCB)\source\vcl - -[HistoryLists\hlConditionals] -Count=6 -Item0=WIN32;NDEBUG;_CONSOLE -Item1=WIN32;NDEBUG;_CONSOLE;XML_STATIC -Item2=WIN32;NDEBUG;_CONSOLE;_DEBUG;XML_STATIC -Item3=WIN32;NDEBUG;_CONSOLE;_DEBUG;XML_UNICODE_WCHAR_T;_UNICODE;XML_STATIC -Item4=WIN32;NDEBUG;_CONSOLE;_DEBUG;XML_UNICODE_WCHAR_T;_UNICODE -Item5=WIN32;NDEBUG;_CONSOLE;_DEBUG - -[HistoryLists\hlIntOutputDir] -Count=4 -Item0=Release\obj\examples -Item1=Release\obj\outline -Item2=..\examples\Release -Item3=Release - -[HistoryLists\hlFinalOutputDir] -Count=1 -Item0=Release\ - -[Debugging] -DebugSourceDirs= - -[Parameters] -RunParams= -HostApplication= -RemoteHost= -RemotePath= -RemoteDebug=0 - -[Compiler] -ShowInfoMsgs=0 -LinkDebugVcl=0 -LinkCGLIB=0 - -[Language] -ActiveLang= -ProjectLang= -RootDir= - - \ No newline at end of file diff --git a/cextern/expat/bcb5/outline.mak b/cextern/expat/bcb5/outline.mak deleted file mode 100755 index 510b541f6475..000000000000 --- a/cextern/expat/bcb5/outline.mak +++ /dev/null @@ -1,186 +0,0 @@ -# --------------------------------------------------------------------------- -!if !$d(BCB) -BCB = $(MAKEDIR)\.. -!endif - -# --------------------------------------------------------------------------- -# IDE SECTION -# --------------------------------------------------------------------------- -# The following section of the project makefile is managed by the BCB IDE. -# It is recommended to use the IDE to change any of the values in this -# section. -# --------------------------------------------------------------------------- - -VERSION = BCB.05.03 -# --------------------------------------------------------------------------- -PROJECT = Release\outline.exe -OBJFILES = Release\obj\examples\outline.obj -RESFILES = -MAINSOURCE = outline.bpf -RESDEPEN = $(RESFILES) -LIBFILES = Release\libexpat_mtd.lib -IDLFILES = -IDLGENFILES = -LIBRARIES = -PACKAGES = VCL50.bpi VCLX50.bpi bcbsmp50.bpi QRPT50.bpi VCLDB50.bpi VCLBDE50.bpi \ - ibsmp50.bpi VCLDBX50.bpi TEEUI50.bpi TEEDB50.bpi TEE50.bpi TEEQR50.bpi \ - VCLIB50.bpi bcbie50.bpi VCLIE50.bpi INETDB50.bpi INET50.bpi NMFAST50.bpi \ - dclocx50.bpi bcb2kaxserver50.bpi dclusr50.bpi -SPARELIBS = -DEFFILE = -# --------------------------------------------------------------------------- -PATHCPP = .;..\examples -PATHASM = .; -PATHPAS = .; -PATHRC = .; -DEBUGLIBPATH = $(BCB)\lib\debug -RELEASELIBPATH = $(BCB)\lib\release -USERDEFINES = WIN32;NDEBUG;_CONSOLE -SYSDEFINES = _NO_VCL;_ASSERTE;NO_STRICT;_RTLDLL -INCLUDEPATH = ..\examples;$(BCB)\include -LIBPATH = ..\examples;$(BCB)\lib;$(RELEASELIBPATH) -WARNINGS= -w-par -w-8027 -w-8026 -# --------------------------------------------------------------------------- -CFLAG1 = -O2 -X- -a8 -b -k- -vi -q -tWM -I..\lib -c -IDLCFLAGS = -I$(BCB)\include -PFLAGS = -N2Release\obj\examples -N0Release\obj\examples -$Y- -$L- -$D- -RFLAGS = /l 0x409 /d "NDEBUG" /i$(BCB)\include -AFLAGS = /mx /w2 /zn -LFLAGS = -IRelease\obj\examples -D"" -ap -Tpe -x -Gn -q -# --------------------------------------------------------------------------- -ALLOBJ = c0x32.obj $(OBJFILES) -ALLRES = $(RESFILES) -ALLLIB = $(LIBFILES) $(LIBRARIES) import32.lib cw32mti.lib -# --------------------------------------------------------------------------- -!ifdef IDEOPTIONS - -[Version Info] -IncludeVerInfo=0 -AutoIncBuild=0 -MajorVer=1 -MinorVer=0 -Release=0 -Build=0 -Debug=0 -PreRelease=0 -Special=0 -Private=0 -DLL=0 - -[Version Info Keys] -CompanyName= -FileDescription= -FileVersion=1.0.0.0 -InternalName= -LegalCopyright= -LegalTrademarks= -OriginalFilename= -ProductName= -ProductVersion=1.0.0.0 -Comments= - -[Debugging] -DebugSourceDirs=$(BCB)\source\vcl - -!endif - - - - - -# --------------------------------------------------------------------------- -# MAKE SECTION -# --------------------------------------------------------------------------- -# This section of the project file is not used by the BCB IDE. It is for -# the benefit of building from the command-line using the MAKE utility. -# --------------------------------------------------------------------------- - -.autodepend -# --------------------------------------------------------------------------- -!if "$(USERDEFINES)" != "" -AUSERDEFINES = -d$(USERDEFINES:;= -d) -!else -AUSERDEFINES = -!endif - -!if !$d(BCC32) -BCC32 = bcc32 -!endif - -!if !$d(CPP32) -CPP32 = cpp32 -!endif - -!if !$d(DCC32) -DCC32 = dcc32 -!endif - -!if !$d(TASM32) -TASM32 = tasm32 -!endif - -!if !$d(LINKER) -LINKER = ilink32 -!endif - -!if !$d(BRCC32) -BRCC32 = brcc32 -!endif - - -# --------------------------------------------------------------------------- -!if $d(PATHCPP) -.PATH.CPP = $(PATHCPP) -.PATH.C = $(PATHCPP) -!endif - -!if $d(PATHPAS) -.PATH.PAS = $(PATHPAS) -!endif - -!if $d(PATHASM) -.PATH.ASM = $(PATHASM) -!endif - -!if $d(PATHRC) -.PATH.RC = $(PATHRC) -!endif -# --------------------------------------------------------------------------- -$(PROJECT): $(IDLGENFILES) $(OBJFILES) $(RESDEPEN) $(DEFFILE) - $(BCB)\BIN\$(LINKER) @&&! - $(LFLAGS) -L$(LIBPATH) + - $(ALLOBJ), + - $(PROJECT),, + - $(ALLLIB), + - $(DEFFILE), + - $(ALLRES) -! -# --------------------------------------------------------------------------- -.pas.hpp: - $(BCB)\BIN\$(DCC32) $(PFLAGS) -U$(INCLUDEPATH) -D$(USERDEFINES);$(SYSDEFINES) -O$(INCLUDEPATH) --BCB {$< } - -.pas.obj: - $(BCB)\BIN\$(DCC32) $(PFLAGS) -U$(INCLUDEPATH) -D$(USERDEFINES);$(SYSDEFINES) -O$(INCLUDEPATH) --BCB {$< } - -.cpp.obj: - $(BCB)\BIN\$(BCC32) $(CFLAG1) $(WARNINGS) -I$(INCLUDEPATH) -D$(USERDEFINES);$(SYSDEFINES) -n$(@D) {$< } - -.c.obj: - $(BCB)\BIN\$(BCC32) $(CFLAG1) $(WARNINGS) -I$(INCLUDEPATH) -D$(USERDEFINES);$(SYSDEFINES) -n$(@D) {$< } - -.c.i: - $(BCB)\BIN\$(CPP32) $(CFLAG1) $(WARNINGS) -I$(INCLUDEPATH) -D$(USERDEFINES);$(SYSDEFINES) -n. {$< } - -.cpp.i: - $(BCB)\BIN\$(CPP32) $(CFLAG1) $(WARNINGS) -I$(INCLUDEPATH) -D$(USERDEFINES);$(SYSDEFINES) -n. {$< } - -.asm.obj: - $(BCB)\BIN\$(TASM32) $(AFLAGS) -i$(INCLUDEPATH:;= -i) $(AUSERDEFINES) -d$(SYSDEFINES:;= -d) $<, $@ - -.rc.res: - $(BCB)\BIN\$(BRCC32) $(RFLAGS) -I$(INCLUDEPATH) -D$(USERDEFINES);$(SYSDEFINES) -fo$@ $< -# --------------------------------------------------------------------------- - - - - diff --git a/cextern/expat/bcb5/setup.bat b/cextern/expat/bcb5/setup.bat deleted file mode 100755 index 6f4b573179e1..000000000000 --- a/cextern/expat/bcb5/setup.bat +++ /dev/null @@ -1,9 +0,0 @@ -REM CommandInterpreter: $(COMSPEC) -if not exist .\release\nul mkdir release -if not exist .\release\obj\nul mkdir release\obj -if not exist .\release\obj\libexpat\nul mkdir release\obj\libexpat -if not exist .\release\obj\libexpatw\nul mkdir release\obj\libexpatw -if not exist .\release\obj\libexpat_static\nul mkdir release\obj\libexpat_static -if not exist .\release\obj\libexpatw_static\nul mkdir release\obj\libexpatw_static -if not exist .\release\obj\examples\nul mkdir release\obj\examples -if not exist .\release\obj\xmlwf\nul mkdir release\obj\xmlwf diff --git a/cextern/expat/bcb5/xmlwf.bpf b/cextern/expat/bcb5/xmlwf.bpf deleted file mode 100755 index d06208e811b8..000000000000 --- a/cextern/expat/bcb5/xmlwf.bpf +++ /dev/null @@ -1,7 +0,0 @@ -USEUNIT("..\xmlwf\codepage.c"); -USEUNIT("..\xmlwf\win32filemap.c"); -USEUNIT("..\xmlwf\xmlfile.c"); -USEUNIT("..\xmlwf\xmlwf.c"); -USELIB("Release\libexpat_mtd.lib"); -//--------------------------------------------------------------------------- -main diff --git a/cextern/expat/bcb5/xmlwf.bpr b/cextern/expat/bcb5/xmlwf.bpr deleted file mode 100755 index d33a1538764f..000000000000 --- a/cextern/expat/bcb5/xmlwf.bpr +++ /dev/null @@ -1,136 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -[Version Info] -IncludeVerInfo=0 -AutoIncBuild=0 -MajorVer=1 -MinorVer=0 -Release=0 -Build=0 -Debug=0 -PreRelease=0 -Special=0 -Private=0 -DLL=0 -Locale=1033 -CodePage=1252 - -[Version Info Keys] -CompanyName= -FileDescription= -FileVersion=1.0.0.0 -InternalName= -LegalCopyright= -LegalTrademarks= -OriginalFilename= -ProductName= -ProductVersion=1.0.0.0 -Comments= - -[HistoryLists\hlIncludePath] -Count=4 -Item0=..\xmlwf;$(BCB)\include -Item1=$(BCB)\include -Item2=$(BCB)\include;$(BCB)\include\mfc;$(BCB)\include\atl -Item3=$(BCB)\include;$(BCB)\include\mfc;$(BCB)\include\atl; - -[HistoryLists\hlLibraryPath] -Count=5 -Item0=..\xmlwf;$(BCB)\lib;$(RELEASELIBPATH) -Item1=..\xmlwf;$(BCB)\lib;..\xmlwf\$(RELEASELIBPATH) -Item2=$(BCB)\lib;$(RELEASELIBPATH) -Item3=$(BCB)\lib;$(RELEASELIBPATH);$(BCB)\lib\psdk -Item4=$(BCB)\lib;$(RELEASELIBPATH);;$(BCB)\lib\psdk; - -[HistoryLists\hlDebugSourcePath] -Count=1 -Item0=$(BCB)\source\vcl - -[HistoryLists\hlConditionals] -Count=6 -Item0=NDEBUG;WIN32;_CONSOLE;COMPILED_FROM_DSP -Item1=NDEBUG;WIN32;_CONSOLE;COMPILED_FROM_DSP;_DEBUG;XML_UNICODE_WCHAR_T;_UNICODE -Item2=NDEBUG;WIN32;_CONSOLE;COMPILED_FROM_DSP;_DEBUG;XML_UNICODE_WCHAR_T -Item3=NDEBUG;WIN32;_CONSOLE;COMPILED_FROM_DSP;_DEBUG -Item4=NDEBUG;WIN32;_CONSOLE;COMPILED_FROM_DSP;_DEBUG;_UNICODE;XML_UNICODE_WCHAR_T -Item5=NDEBUG;WIN32;_CONSOLE;COMPILED_FROM_DSP;_DEBUG;_UNICODE - -[HistoryLists\hlIntOutputDir] -Count=3 -Item0=Release\obj\xmlwf -Item1=..\xmlwf\Release -Item2=Release - -[HistoryLists\hlFinalOutputDir] -Count=3 -Item0=Release\ -Item1=Release -Item2=.\Release\ - -[Debugging] -DebugSourceDirs= - -[Parameters] -RunParams=sample.xml -HostApplication= -RemoteHost= -RemotePath= -RemoteDebug=0 - -[Compiler] -ShowInfoMsgs=0 -LinkDebugVcl=0 -LinkCGLIB=0 - -[Language] -ActiveLang= -ProjectLang= -RootDir= - - \ No newline at end of file diff --git a/cextern/expat/bcb5/xmlwf.mak b/cextern/expat/bcb5/xmlwf.mak deleted file mode 100755 index f74126e454aa..000000000000 --- a/cextern/expat/bcb5/xmlwf.mak +++ /dev/null @@ -1,187 +0,0 @@ -# --------------------------------------------------------------------------- -!if !$d(BCB) -BCB = $(MAKEDIR)\.. -!endif - -# --------------------------------------------------------------------------- -# IDE SECTION -# --------------------------------------------------------------------------- -# The following section of the project makefile is managed by the BCB IDE. -# It is recommended to use the IDE to change any of the values in this -# section. -# --------------------------------------------------------------------------- - -VERSION = BCB.05.03 -# --------------------------------------------------------------------------- -PROJECT = Release\xmlwf.exe -OBJFILES = Release\obj\xmlwf\codepage.obj Release\obj\xmlwf\win32filemap.obj \ - Release\obj\xmlwf\xmlfile.obj Release\obj\xmlwf\xmlwf.obj -RESFILES = -MAINSOURCE = xmlwf.bpf -RESDEPEN = $(RESFILES) -LIBFILES = Release\libexpat_mtd.lib -IDLFILES = -IDLGENFILES = -LIBRARIES = -PACKAGES = VCL50.bpi VCLX50.bpi bcbsmp50.bpi QRPT50.bpi VCLDB50.bpi VCLBDE50.bpi \ - ibsmp50.bpi VCLDBX50.bpi TEEUI50.bpi TEEDB50.bpi TEE50.bpi TEEQR50.bpi \ - VCLIB50.bpi bcbie50.bpi VCLIE50.bpi INETDB50.bpi INET50.bpi NMFAST50.bpi \ - dclocx50.bpi bcb2kaxserver50.bpi dclusr50.bpi -SPARELIBS = -DEFFILE = -# --------------------------------------------------------------------------- -PATHCPP = .;..\xmlwf -PATHASM = .; -PATHPAS = .; -PATHRC = .; -DEBUGLIBPATH = $(BCB)\lib\debug -RELEASELIBPATH = $(BCB)\lib\release -USERDEFINES = NDEBUG;WIN32;_CONSOLE;COMPILED_FROM_DSP -SYSDEFINES = _NO_VCL;_ASSERTE;NO_STRICT;_RTLDLL -INCLUDEPATH = ..\xmlwf;$(BCB)\include -LIBPATH = ..\xmlwf;$(BCB)\lib;$(RELEASELIBPATH) -WARNINGS= -w-8065 -w-par -w-8027 -w-8026 -# --------------------------------------------------------------------------- -CFLAG1 = -O2 -X- -a8 -b -k- -vi -q -tWM -I..\lib -c -IDLCFLAGS = -I$(BCB)\include -PFLAGS = -N2Release\obj\xmlwf -N0Release\obj\xmlwf -$Y- -$L- -$D- -RFLAGS = /l 0x409 /d "NDEBUG" /i$(BCB)\include -AFLAGS = /mx /w2 /zn -LFLAGS = -IRelease\obj\xmlwf -D"" -ap -Tpe -x -Gn -q -# --------------------------------------------------------------------------- -ALLOBJ = c0x32.obj $(OBJFILES) -ALLRES = $(RESFILES) -ALLLIB = $(LIBFILES) $(LIBRARIES) import32.lib cw32mti.lib -# --------------------------------------------------------------------------- -!ifdef IDEOPTIONS - -[Version Info] -IncludeVerInfo=0 -AutoIncBuild=0 -MajorVer=1 -MinorVer=0 -Release=0 -Build=0 -Debug=0 -PreRelease=0 -Special=0 -Private=0 -DLL=0 - -[Version Info Keys] -CompanyName= -FileDescription= -FileVersion=1.0.0.0 -InternalName= -LegalCopyright= -LegalTrademarks= -OriginalFilename= -ProductName= -ProductVersion=1.0.0.0 -Comments= - -[Debugging] -DebugSourceDirs=$(BCB)\source\vcl - -!endif - - - - - -# --------------------------------------------------------------------------- -# MAKE SECTION -# --------------------------------------------------------------------------- -# This section of the project file is not used by the BCB IDE. It is for -# the benefit of building from the command-line using the MAKE utility. -# --------------------------------------------------------------------------- - -.autodepend -# --------------------------------------------------------------------------- -!if "$(USERDEFINES)" != "" -AUSERDEFINES = -d$(USERDEFINES:;= -d) -!else -AUSERDEFINES = -!endif - -!if !$d(BCC32) -BCC32 = bcc32 -!endif - -!if !$d(CPP32) -CPP32 = cpp32 -!endif - -!if !$d(DCC32) -DCC32 = dcc32 -!endif - -!if !$d(TASM32) -TASM32 = tasm32 -!endif - -!if !$d(LINKER) -LINKER = ilink32 -!endif - -!if !$d(BRCC32) -BRCC32 = brcc32 -!endif - - -# --------------------------------------------------------------------------- -!if $d(PATHCPP) -.PATH.CPP = $(PATHCPP) -.PATH.C = $(PATHCPP) -!endif - -!if $d(PATHPAS) -.PATH.PAS = $(PATHPAS) -!endif - -!if $d(PATHASM) -.PATH.ASM = $(PATHASM) -!endif - -!if $d(PATHRC) -.PATH.RC = $(PATHRC) -!endif -# --------------------------------------------------------------------------- -$(PROJECT): $(IDLGENFILES) $(OBJFILES) $(RESDEPEN) $(DEFFILE) - $(BCB)\BIN\$(LINKER) @&&! - $(LFLAGS) -L$(LIBPATH) + - $(ALLOBJ), + - $(PROJECT),, + - $(ALLLIB), + - $(DEFFILE), + - $(ALLRES) -! -# --------------------------------------------------------------------------- -.pas.hpp: - $(BCB)\BIN\$(DCC32) $(PFLAGS) -U$(INCLUDEPATH) -D$(USERDEFINES);$(SYSDEFINES) -O$(INCLUDEPATH) --BCB {$< } - -.pas.obj: - $(BCB)\BIN\$(DCC32) $(PFLAGS) -U$(INCLUDEPATH) -D$(USERDEFINES);$(SYSDEFINES) -O$(INCLUDEPATH) --BCB {$< } - -.cpp.obj: - $(BCB)\BIN\$(BCC32) $(CFLAG1) $(WARNINGS) -I$(INCLUDEPATH) -D$(USERDEFINES);$(SYSDEFINES) -n$(@D) {$< } - -.c.obj: - $(BCB)\BIN\$(BCC32) $(CFLAG1) $(WARNINGS) -I$(INCLUDEPATH) -D$(USERDEFINES);$(SYSDEFINES) -n$(@D) {$< } - -.c.i: - $(BCB)\BIN\$(CPP32) $(CFLAG1) $(WARNINGS) -I$(INCLUDEPATH) -D$(USERDEFINES);$(SYSDEFINES) -n. {$< } - -.cpp.i: - $(BCB)\BIN\$(CPP32) $(CFLAG1) $(WARNINGS) -I$(INCLUDEPATH) -D$(USERDEFINES);$(SYSDEFINES) -n. {$< } - -.asm.obj: - $(BCB)\BIN\$(TASM32) $(AFLAGS) -i$(INCLUDEPATH:;= -i) $(AUSERDEFINES) -d$(SYSDEFINES:;= -d) $<, $@ - -.rc.res: - $(BCB)\BIN\$(BRCC32) $(RFLAGS) -I$(INCLUDEPATH) -D$(USERDEFINES);$(SYSDEFINES) -fo$@ $< -# --------------------------------------------------------------------------- - - - - diff --git a/cextern/expat/configure b/cextern/expat/configure deleted file mode 100755 index 01ba7a99280a..000000000000 --- a/cextern/expat/configure +++ /dev/null @@ -1,22017 +0,0 @@ -#! /bin/sh -# Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.59 for expat 2.0.1. -# -# Report bugs to . -# -# Copyright (C) 2003 Free Software Foundation, Inc. -# This configure script is free software; the Free Software Foundation -# gives unlimited permission to copy, distribute and modify it. -## --------------------- ## -## M4sh Initialization. ## -## --------------------- ## - -# Be Bourne compatible -if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then - emulate sh - NULLCMD=: - # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which - # is contrary to our usage. Disable this feature. - alias -g '${1+"$@"}'='"$@"' -elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then - set -o posix -fi -DUALCASE=1; export DUALCASE # for MKS sh - -# Support unset when possible. -if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then - as_unset=unset -else - as_unset=false -fi - - -# Work around bugs in pre-3.0 UWIN ksh. -$as_unset ENV MAIL MAILPATH -PS1='$ ' -PS2='> ' -PS4='+ ' - -# NLS nuisances. -for as_var in \ - LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ - LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ - LC_TELEPHONE LC_TIME -do - if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then - eval $as_var=C; export $as_var - else - $as_unset $as_var - fi -done - -# Required to use basename. -if expr a : '\(a\)' >/dev/null 2>&1; then - as_expr=expr -else - as_expr=false -fi - -if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then - as_basename=basename -else - as_basename=false -fi - - -# Name of the executable. -as_me=`$as_basename "$0" || -$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ - X"$0" : 'X\(//\)$' \| \ - X"$0" : 'X\(/\)$' \| \ - . : '\(.\)' 2>/dev/null || -echo X/"$0" | - sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; } - /^X\/\(\/\/\)$/{ s//\1/; q; } - /^X\/\(\/\).*/{ s//\1/; q; } - s/.*/./; q'` - - -# PATH needs CR, and LINENO needs CR and PATH. -# Avoid depending upon Character Ranges. -as_cr_letters='abcdefghijklmnopqrstuvwxyz' -as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' -as_cr_Letters=$as_cr_letters$as_cr_LETTERS -as_cr_digits='0123456789' -as_cr_alnum=$as_cr_Letters$as_cr_digits - -# The user is always right. -if test "${PATH_SEPARATOR+set}" != set; then - echo "#! /bin/sh" >conf$$.sh - echo "exit 0" >>conf$$.sh - chmod +x conf$$.sh - if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then - PATH_SEPARATOR=';' - else - PATH_SEPARATOR=: - fi - rm -f conf$$.sh -fi - - - as_lineno_1=$LINENO - as_lineno_2=$LINENO - as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` - test "x$as_lineno_1" != "x$as_lineno_2" && - test "x$as_lineno_3" = "x$as_lineno_2" || { - # Find who we are. Look in the path if we contain no path at all - # relative or not. - case $0 in - *[\\/]* ) as_myself=$0 ;; - *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break -done - - ;; - esac - # We did not find ourselves, most probably we were run as `sh COMMAND' - # in which case we are not to be found in the path. - if test "x$as_myself" = x; then - as_myself=$0 - fi - if test ! -f "$as_myself"; then - { echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2 - { (exit 1); exit 1; }; } - fi - case $CONFIG_SHELL in - '') - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for as_base in sh bash ksh sh5; do - case $as_dir in - /*) - if ("$as_dir/$as_base" -c ' - as_lineno_1=$LINENO - as_lineno_2=$LINENO - as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` - test "x$as_lineno_1" != "x$as_lineno_2" && - test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then - $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; } - $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; } - CONFIG_SHELL=$as_dir/$as_base - export CONFIG_SHELL - exec "$CONFIG_SHELL" "$0" ${1+"$@"} - fi;; - esac - done -done -;; - esac - - # Create $as_me.lineno as a copy of $as_myself, but with $LINENO - # uniformly replaced by the line number. The first 'sed' inserts a - # line-number line before each line; the second 'sed' does the real - # work. The second script uses 'N' to pair each line-number line - # with the numbered line, and appends trailing '-' during - # substitution so that $LINENO is not a special case at line end. - # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the - # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-) - sed '=' <$as_myself | - sed ' - N - s,$,-, - : loop - s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3, - t loop - s,-$,, - s,^['$as_cr_digits']*\n,, - ' >$as_me.lineno && - chmod +x $as_me.lineno || - { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2 - { (exit 1); exit 1; }; } - - # Don't try to exec as it changes $[0], causing all sort of problems - # (the dirname of $[0] is not the place where we might find the - # original and so on. Autoconf is especially sensible to this). - . ./$as_me.lineno - # Exit status is that of the last command. - exit -} - - -case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in - *c*,-n*) ECHO_N= ECHO_C=' -' ECHO_T=' ' ;; - *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;; - *) ECHO_N= ECHO_C='\c' ECHO_T= ;; -esac - -if expr a : '\(a\)' >/dev/null 2>&1; then - as_expr=expr -else - as_expr=false -fi - -rm -f conf$$ conf$$.exe conf$$.file -echo >conf$$.file -if ln -s conf$$.file conf$$ 2>/dev/null; then - # We could just check for DJGPP; but this test a) works b) is more generic - # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04). - if test -f conf$$.exe; then - # Don't use ln at all; we don't have any links - as_ln_s='cp -p' - else - as_ln_s='ln -s' - fi -elif ln conf$$.file conf$$ 2>/dev/null; then - as_ln_s=ln -else - as_ln_s='cp -p' -fi -rm -f conf$$ conf$$.exe conf$$.file - -if mkdir -p . 2>/dev/null; then - as_mkdir_p=: -else - test -d ./-p && rmdir ./-p - as_mkdir_p=false -fi - -as_executable_p="test -f" - -# Sed expression to map a string onto a valid CPP name. -as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" - -# Sed expression to map a string onto a valid variable name. -as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" - - -# IFS -# We need space, tab and new line, in precisely that order. -as_nl=' -' -IFS=" $as_nl" - -# CDPATH. -$as_unset CDPATH - - - -# Check that we are running under the correct shell. -SHELL=${CONFIG_SHELL-/bin/sh} - -case X$ECHO in -X*--fallback-echo) - # Remove one level of quotation (which was required for Make). - ECHO=`echo "$ECHO" | sed 's,\\\\\$\\$0,'$0','` - ;; -esac - -echo=${ECHO-echo} -if test "X$1" = X--no-reexec; then - # Discard the --no-reexec flag, and continue. - shift -elif test "X$1" = X--fallback-echo; then - # Avoid inline document here, it may be left over - : -elif test "X`($echo '\t') 2>/dev/null`" = 'X\t' ; then - # Yippee, $echo works! - : -else - # Restart under the correct shell. - exec $SHELL "$0" --no-reexec ${1+"$@"} -fi - -if test "X$1" = X--fallback-echo; then - # used as fallback echo - shift - cat </dev/null 2>&1 && unset CDPATH - -if test -z "$ECHO"; then -if test "X${echo_test_string+set}" != Xset; then -# find a string as large as possible, as long as the shell can cope with it - for cmd in 'sed 50q "$0"' 'sed 20q "$0"' 'sed 10q "$0"' 'sed 2q "$0"' 'echo test'; do - # expected sizes: less than 2Kb, 1Kb, 512 bytes, 16 bytes, ... - if (echo_test_string=`eval $cmd`) 2>/dev/null && - echo_test_string=`eval $cmd` && - (test "X$echo_test_string" = "X$echo_test_string") 2>/dev/null - then - break - fi - done -fi - -if test "X`($echo '\t') 2>/dev/null`" = 'X\t' && - echo_testing_string=`($echo "$echo_test_string") 2>/dev/null` && - test "X$echo_testing_string" = "X$echo_test_string"; then - : -else - # The Solaris, AIX, and Digital Unix default echo programs unquote - # backslashes. This makes it impossible to quote backslashes using - # echo "$something" | sed 's/\\/\\\\/g' - # - # So, first we look for a working echo in the user's PATH. - - lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR - for dir in $PATH /usr/ucb; do - IFS="$lt_save_ifs" - if (test -f $dir/echo || test -f $dir/echo$ac_exeext) && - test "X`($dir/echo '\t') 2>/dev/null`" = 'X\t' && - echo_testing_string=`($dir/echo "$echo_test_string") 2>/dev/null` && - test "X$echo_testing_string" = "X$echo_test_string"; then - echo="$dir/echo" - break - fi - done - IFS="$lt_save_ifs" - - if test "X$echo" = Xecho; then - # We didn't find a better echo, so look for alternatives. - if test "X`(print -r '\t') 2>/dev/null`" = 'X\t' && - echo_testing_string=`(print -r "$echo_test_string") 2>/dev/null` && - test "X$echo_testing_string" = "X$echo_test_string"; then - # This shell has a builtin print -r that does the trick. - echo='print -r' - elif (test -f /bin/ksh || test -f /bin/ksh$ac_exeext) && - test "X$CONFIG_SHELL" != X/bin/ksh; then - # If we have ksh, try running configure again with it. - ORIGINAL_CONFIG_SHELL=${CONFIG_SHELL-/bin/sh} - export ORIGINAL_CONFIG_SHELL - CONFIG_SHELL=/bin/ksh - export CONFIG_SHELL - exec $CONFIG_SHELL "$0" --no-reexec ${1+"$@"} - else - # Try using printf. - echo='printf %s\n' - if test "X`($echo '\t') 2>/dev/null`" = 'X\t' && - echo_testing_string=`($echo "$echo_test_string") 2>/dev/null` && - test "X$echo_testing_string" = "X$echo_test_string"; then - # Cool, printf works - : - elif echo_testing_string=`($ORIGINAL_CONFIG_SHELL "$0" --fallback-echo '\t') 2>/dev/null` && - test "X$echo_testing_string" = 'X\t' && - echo_testing_string=`($ORIGINAL_CONFIG_SHELL "$0" --fallback-echo "$echo_test_string") 2>/dev/null` && - test "X$echo_testing_string" = "X$echo_test_string"; then - CONFIG_SHELL=$ORIGINAL_CONFIG_SHELL - export CONFIG_SHELL - SHELL="$CONFIG_SHELL" - export SHELL - echo="$CONFIG_SHELL $0 --fallback-echo" - elif echo_testing_string=`($CONFIG_SHELL "$0" --fallback-echo '\t') 2>/dev/null` && - test "X$echo_testing_string" = 'X\t' && - echo_testing_string=`($CONFIG_SHELL "$0" --fallback-echo "$echo_test_string") 2>/dev/null` && - test "X$echo_testing_string" = "X$echo_test_string"; then - echo="$CONFIG_SHELL $0 --fallback-echo" - else - # maybe with a smaller string... - prev=: - - for cmd in 'echo test' 'sed 2q "$0"' 'sed 10q "$0"' 'sed 20q "$0"' 'sed 50q "$0"'; do - if (test "X$echo_test_string" = "X`eval $cmd`") 2>/dev/null - then - break - fi - prev="$cmd" - done - - if test "$prev" != 'sed 50q "$0"'; then - echo_test_string=`eval $prev` - export echo_test_string - exec ${ORIGINAL_CONFIG_SHELL-${CONFIG_SHELL-/bin/sh}} "$0" ${1+"$@"} - else - # Oops. We lost completely, so just stick with echo. - echo=echo - fi - fi - fi - fi -fi -fi - -# Copy echo and quote the copy suitably for passing to libtool from -# the Makefile, instead of quoting the original, which is used later. -ECHO=$echo -if test "X$ECHO" = "X$CONFIG_SHELL $0 --fallback-echo"; then - ECHO="$CONFIG_SHELL \\\$\$0 --fallback-echo" -fi - - - - -tagnames=${tagnames+${tagnames},}CXX - -tagnames=${tagnames+${tagnames},}F77 - -# Name of the host. -# hostname on some systems (SVR3.2, Linux) returns a bogus exit status, -# so uname gets run too. -ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` - -exec 6>&1 - -# -# Initializations. -# -ac_default_prefix=/usr/local -ac_config_libobj_dir=. -cross_compiling=no -subdirs= -MFLAGS= -MAKEFLAGS= -SHELL=${CONFIG_SHELL-/bin/sh} - -# Maximum number of lines to put in a shell here document. -# This variable seems obsolete. It should probably be removed, and -# only ac_max_sed_lines should be used. -: ${ac_max_here_lines=38} - -# Identity of this package. -PACKAGE_NAME='expat' -PACKAGE_TARNAME='expat' -PACKAGE_VERSION='2.0.1' -PACKAGE_STRING='expat 2.0.1' -PACKAGE_BUGREPORT='expat-bugs@libexpat.org' - -ac_unique_file="Makefile.in" -# Factoring default headers for most tests. -ac_includes_default="\ -#include -#if HAVE_SYS_TYPES_H -# include -#endif -#if HAVE_SYS_STAT_H -# include -#endif -#if STDC_HEADERS -# include -# include -#else -# if HAVE_STDLIB_H -# include -# endif -#endif -#if HAVE_STRING_H -# if !STDC_HEADERS && HAVE_MEMORY_H -# include -# endif -# include -#endif -#if HAVE_STRINGS_H -# include -#endif -#if HAVE_INTTYPES_H -# include -#else -# if HAVE_STDINT_H -# include -# endif -#endif -#if HAVE_UNISTD_H -# include -#endif" - -ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS build build_cpu build_vendor build_os host host_cpu host_vendor host_os CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT EGREP LN_S ECHO AR ac_ct_AR RANLIB ac_ct_RANLIB STRIP ac_ct_STRIP DLLTOOL ac_ct_DLLTOOL AS ac_ct_AS OBJDUMP ac_ct_OBJDUMP CPP CXX CXXFLAGS ac_ct_CXX CXXCPP F77 FFLAGS ac_ct_F77 LIBTOOL LIBCURRENT LIBREVISION LIBAGE INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA FILEMAP LIBOBJS LTLIBOBJS' -ac_subst_files='' - -# Initialize some variables set by options. -ac_init_help= -ac_init_version=false -# The variables have the same names as the options, with -# dashes changed to underlines. -cache_file=/dev/null -exec_prefix=NONE -no_create= -no_recursion= -prefix=NONE -program_prefix=NONE -program_suffix=NONE -program_transform_name=s,x,x, -silent= -site= -srcdir= -verbose= -x_includes=NONE -x_libraries=NONE - -# Installation directory options. -# These are left unexpanded so users can "make install exec_prefix=/foo" -# and all the variables that are supposed to be based on exec_prefix -# by default will actually change. -# Use braces instead of parens because sh, perl, etc. also accept them. -bindir='${exec_prefix}/bin' -sbindir='${exec_prefix}/sbin' -libexecdir='${exec_prefix}/libexec' -datadir='${prefix}/share' -sysconfdir='${prefix}/etc' -sharedstatedir='${prefix}/com' -localstatedir='${prefix}/var' -libdir='${exec_prefix}/lib' -includedir='${prefix}/include' -oldincludedir='/usr/include' -infodir='${prefix}/info' -mandir='${prefix}/man' - -ac_prev= -for ac_option -do - # If the previous option needs an argument, assign it. - if test -n "$ac_prev"; then - eval "$ac_prev=\$ac_option" - ac_prev= - continue - fi - - ac_optarg=`expr "x$ac_option" : 'x[^=]*=\(.*\)'` - - # Accept the important Cygnus configure options, so we can diagnose typos. - - case $ac_option in - - -bindir | --bindir | --bindi | --bind | --bin | --bi) - ac_prev=bindir ;; - -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) - bindir=$ac_optarg ;; - - -build | --build | --buil | --bui | --bu) - ac_prev=build_alias ;; - -build=* | --build=* | --buil=* | --bui=* | --bu=*) - build_alias=$ac_optarg ;; - - -cache-file | --cache-file | --cache-fil | --cache-fi \ - | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) - ac_prev=cache_file ;; - -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ - | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) - cache_file=$ac_optarg ;; - - --config-cache | -C) - cache_file=config.cache ;; - - -datadir | --datadir | --datadi | --datad | --data | --dat | --da) - ac_prev=datadir ;; - -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \ - | --da=*) - datadir=$ac_optarg ;; - - -disable-* | --disable-*) - ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'` - # Reject names that are not valid shell variable names. - expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && - { echo "$as_me: error: invalid feature name: $ac_feature" >&2 - { (exit 1); exit 1; }; } - ac_feature=`echo $ac_feature | sed 's/-/_/g'` - eval "enable_$ac_feature=no" ;; - - -enable-* | --enable-*) - ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` - # Reject names that are not valid shell variable names. - expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && - { echo "$as_me: error: invalid feature name: $ac_feature" >&2 - { (exit 1); exit 1; }; } - ac_feature=`echo $ac_feature | sed 's/-/_/g'` - case $ac_option in - *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;; - *) ac_optarg=yes ;; - esac - eval "enable_$ac_feature='$ac_optarg'" ;; - - -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ - | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ - | --exec | --exe | --ex) - ac_prev=exec_prefix ;; - -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ - | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ - | --exec=* | --exe=* | --ex=*) - exec_prefix=$ac_optarg ;; - - -gas | --gas | --ga | --g) - # Obsolete; use --with-gas. - with_gas=yes ;; - - -help | --help | --hel | --he | -h) - ac_init_help=long ;; - -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) - ac_init_help=recursive ;; - -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) - ac_init_help=short ;; - - -host | --host | --hos | --ho) - ac_prev=host_alias ;; - -host=* | --host=* | --hos=* | --ho=*) - host_alias=$ac_optarg ;; - - -includedir | --includedir | --includedi | --included | --include \ - | --includ | --inclu | --incl | --inc) - ac_prev=includedir ;; - -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ - | --includ=* | --inclu=* | --incl=* | --inc=*) - includedir=$ac_optarg ;; - - -infodir | --infodir | --infodi | --infod | --info | --inf) - ac_prev=infodir ;; - -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) - infodir=$ac_optarg ;; - - -libdir | --libdir | --libdi | --libd) - ac_prev=libdir ;; - -libdir=* | --libdir=* | --libdi=* | --libd=*) - libdir=$ac_optarg ;; - - -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ - | --libexe | --libex | --libe) - ac_prev=libexecdir ;; - -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ - | --libexe=* | --libex=* | --libe=*) - libexecdir=$ac_optarg ;; - - -localstatedir | --localstatedir | --localstatedi | --localstated \ - | --localstate | --localstat | --localsta | --localst \ - | --locals | --local | --loca | --loc | --lo) - ac_prev=localstatedir ;; - -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ - | --localstate=* | --localstat=* | --localsta=* | --localst=* \ - | --locals=* | --local=* | --loca=* | --loc=* | --lo=*) - localstatedir=$ac_optarg ;; - - -mandir | --mandir | --mandi | --mand | --man | --ma | --m) - ac_prev=mandir ;; - -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) - mandir=$ac_optarg ;; - - -nfp | --nfp | --nf) - # Obsolete; use --without-fp. - with_fp=no ;; - - -no-create | --no-create | --no-creat | --no-crea | --no-cre \ - | --no-cr | --no-c | -n) - no_create=yes ;; - - -no-recursion | --no-recursion | --no-recursio | --no-recursi \ - | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) - no_recursion=yes ;; - - -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ - | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ - | --oldin | --oldi | --old | --ol | --o) - ac_prev=oldincludedir ;; - -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ - | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ - | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) - oldincludedir=$ac_optarg ;; - - -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) - ac_prev=prefix ;; - -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) - prefix=$ac_optarg ;; - - -program-prefix | --program-prefix | --program-prefi | --program-pref \ - | --program-pre | --program-pr | --program-p) - ac_prev=program_prefix ;; - -program-prefix=* | --program-prefix=* | --program-prefi=* \ - | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) - program_prefix=$ac_optarg ;; - - -program-suffix | --program-suffix | --program-suffi | --program-suff \ - | --program-suf | --program-su | --program-s) - ac_prev=program_suffix ;; - -program-suffix=* | --program-suffix=* | --program-suffi=* \ - | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) - program_suffix=$ac_optarg ;; - - -program-transform-name | --program-transform-name \ - | --program-transform-nam | --program-transform-na \ - | --program-transform-n | --program-transform- \ - | --program-transform | --program-transfor \ - | --program-transfo | --program-transf \ - | --program-trans | --program-tran \ - | --progr-tra | --program-tr | --program-t) - ac_prev=program_transform_name ;; - -program-transform-name=* | --program-transform-name=* \ - | --program-transform-nam=* | --program-transform-na=* \ - | --program-transform-n=* | --program-transform-=* \ - | --program-transform=* | --program-transfor=* \ - | --program-transfo=* | --program-transf=* \ - | --program-trans=* | --program-tran=* \ - | --progr-tra=* | --program-tr=* | --program-t=*) - program_transform_name=$ac_optarg ;; - - -q | -quiet | --quiet | --quie | --qui | --qu | --q \ - | -silent | --silent | --silen | --sile | --sil) - silent=yes ;; - - -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) - ac_prev=sbindir ;; - -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ - | --sbi=* | --sb=*) - sbindir=$ac_optarg ;; - - -sharedstatedir | --sharedstatedir | --sharedstatedi \ - | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ - | --sharedst | --shareds | --shared | --share | --shar \ - | --sha | --sh) - ac_prev=sharedstatedir ;; - -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ - | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ - | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ - | --sha=* | --sh=*) - sharedstatedir=$ac_optarg ;; - - -site | --site | --sit) - ac_prev=site ;; - -site=* | --site=* | --sit=*) - site=$ac_optarg ;; - - -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) - ac_prev=srcdir ;; - -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) - srcdir=$ac_optarg ;; - - -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ - | --syscon | --sysco | --sysc | --sys | --sy) - ac_prev=sysconfdir ;; - -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ - | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) - sysconfdir=$ac_optarg ;; - - -target | --target | --targe | --targ | --tar | --ta | --t) - ac_prev=target_alias ;; - -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) - target_alias=$ac_optarg ;; - - -v | -verbose | --verbose | --verbos | --verbo | --verb) - verbose=yes ;; - - -version | --version | --versio | --versi | --vers | -V) - ac_init_version=: ;; - - -with-* | --with-*) - ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` - # Reject names that are not valid shell variable names. - expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && - { echo "$as_me: error: invalid package name: $ac_package" >&2 - { (exit 1); exit 1; }; } - ac_package=`echo $ac_package| sed 's/-/_/g'` - case $ac_option in - *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;; - *) ac_optarg=yes ;; - esac - eval "with_$ac_package='$ac_optarg'" ;; - - -without-* | --without-*) - ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'` - # Reject names that are not valid shell variable names. - expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && - { echo "$as_me: error: invalid package name: $ac_package" >&2 - { (exit 1); exit 1; }; } - ac_package=`echo $ac_package | sed 's/-/_/g'` - eval "with_$ac_package=no" ;; - - --x) - # Obsolete; use --with-x. - with_x=yes ;; - - -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ - | --x-incl | --x-inc | --x-in | --x-i) - ac_prev=x_includes ;; - -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ - | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) - x_includes=$ac_optarg ;; - - -x-libraries | --x-libraries | --x-librarie | --x-librari \ - | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) - ac_prev=x_libraries ;; - -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ - | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) - x_libraries=$ac_optarg ;; - - -*) { echo "$as_me: error: unrecognized option: $ac_option -Try \`$0 --help' for more information." >&2 - { (exit 1); exit 1; }; } - ;; - - *=*) - ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` - # Reject names that are not valid shell variable names. - expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null && - { echo "$as_me: error: invalid variable name: $ac_envvar" >&2 - { (exit 1); exit 1; }; } - ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` - eval "$ac_envvar='$ac_optarg'" - export $ac_envvar ;; - - *) - # FIXME: should be removed in autoconf 3.0. - echo "$as_me: WARNING: you should use --build, --host, --target" >&2 - expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && - echo "$as_me: WARNING: invalid host type: $ac_option" >&2 - : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option} - ;; - - esac -done - -if test -n "$ac_prev"; then - ac_option=--`echo $ac_prev | sed 's/_/-/g'` - { echo "$as_me: error: missing argument to $ac_option" >&2 - { (exit 1); exit 1; }; } -fi - -# Be sure to have absolute paths. -for ac_var in exec_prefix prefix -do - eval ac_val=$`echo $ac_var` - case $ac_val in - [\\/$]* | ?:[\\/]* | NONE | '' ) ;; - *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 - { (exit 1); exit 1; }; };; - esac -done - -# Be sure to have absolute paths. -for ac_var in bindir sbindir libexecdir datadir sysconfdir sharedstatedir \ - localstatedir libdir includedir oldincludedir infodir mandir -do - eval ac_val=$`echo $ac_var` - case $ac_val in - [\\/$]* | ?:[\\/]* ) ;; - *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 - { (exit 1); exit 1; }; };; - esac -done - -# There might be people who depend on the old broken behavior: `$host' -# used to hold the argument of --host etc. -# FIXME: To remove some day. -build=$build_alias -host=$host_alias -target=$target_alias - -# FIXME: To remove some day. -if test "x$host_alias" != x; then - if test "x$build_alias" = x; then - cross_compiling=maybe - echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host. - If a cross compiler is detected then cross compile mode will be used." >&2 - elif test "x$build_alias" != "x$host_alias"; then - cross_compiling=yes - fi -fi - -ac_tool_prefix= -test -n "$host_alias" && ac_tool_prefix=$host_alias- - -test "$silent" = yes && exec 6>/dev/null - - -# Find the source files, if location was not specified. -if test -z "$srcdir"; then - ac_srcdir_defaulted=yes - # Try the directory containing this script, then its parent. - ac_confdir=`(dirname "$0") 2>/dev/null || -$as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$0" : 'X\(//\)[^/]' \| \ - X"$0" : 'X\(//\)$' \| \ - X"$0" : 'X\(/\)' \| \ - . : '\(.\)' 2>/dev/null || -echo X"$0" | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } - /^X\(\/\/\)[^/].*/{ s//\1/; q; } - /^X\(\/\/\)$/{ s//\1/; q; } - /^X\(\/\).*/{ s//\1/; q; } - s/.*/./; q'` - srcdir=$ac_confdir - if test ! -r $srcdir/$ac_unique_file; then - srcdir=.. - fi -else - ac_srcdir_defaulted=no -fi -if test ! -r $srcdir/$ac_unique_file; then - if test "$ac_srcdir_defaulted" = yes; then - { echo "$as_me: error: cannot find sources ($ac_unique_file) in $ac_confdir or .." >&2 - { (exit 1); exit 1; }; } - else - { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2 - { (exit 1); exit 1; }; } - fi -fi -(cd $srcdir && test -r ./$ac_unique_file) 2>/dev/null || - { echo "$as_me: error: sources are in $srcdir, but \`cd $srcdir' does not work" >&2 - { (exit 1); exit 1; }; } -srcdir=`echo "$srcdir" | sed 's%\([^\\/]\)[\\/]*$%\1%'` -ac_env_build_alias_set=${build_alias+set} -ac_env_build_alias_value=$build_alias -ac_cv_env_build_alias_set=${build_alias+set} -ac_cv_env_build_alias_value=$build_alias -ac_env_host_alias_set=${host_alias+set} -ac_env_host_alias_value=$host_alias -ac_cv_env_host_alias_set=${host_alias+set} -ac_cv_env_host_alias_value=$host_alias -ac_env_target_alias_set=${target_alias+set} -ac_env_target_alias_value=$target_alias -ac_cv_env_target_alias_set=${target_alias+set} -ac_cv_env_target_alias_value=$target_alias -ac_env_CC_set=${CC+set} -ac_env_CC_value=$CC -ac_cv_env_CC_set=${CC+set} -ac_cv_env_CC_value=$CC -ac_env_CFLAGS_set=${CFLAGS+set} -ac_env_CFLAGS_value=$CFLAGS -ac_cv_env_CFLAGS_set=${CFLAGS+set} -ac_cv_env_CFLAGS_value=$CFLAGS -ac_env_LDFLAGS_set=${LDFLAGS+set} -ac_env_LDFLAGS_value=$LDFLAGS -ac_cv_env_LDFLAGS_set=${LDFLAGS+set} -ac_cv_env_LDFLAGS_value=$LDFLAGS -ac_env_CPPFLAGS_set=${CPPFLAGS+set} -ac_env_CPPFLAGS_value=$CPPFLAGS -ac_cv_env_CPPFLAGS_set=${CPPFLAGS+set} -ac_cv_env_CPPFLAGS_value=$CPPFLAGS -ac_env_CPP_set=${CPP+set} -ac_env_CPP_value=$CPP -ac_cv_env_CPP_set=${CPP+set} -ac_cv_env_CPP_value=$CPP -ac_env_CXX_set=${CXX+set} -ac_env_CXX_value=$CXX -ac_cv_env_CXX_set=${CXX+set} -ac_cv_env_CXX_value=$CXX -ac_env_CXXFLAGS_set=${CXXFLAGS+set} -ac_env_CXXFLAGS_value=$CXXFLAGS -ac_cv_env_CXXFLAGS_set=${CXXFLAGS+set} -ac_cv_env_CXXFLAGS_value=$CXXFLAGS -ac_env_CXXCPP_set=${CXXCPP+set} -ac_env_CXXCPP_value=$CXXCPP -ac_cv_env_CXXCPP_set=${CXXCPP+set} -ac_cv_env_CXXCPP_value=$CXXCPP -ac_env_F77_set=${F77+set} -ac_env_F77_value=$F77 -ac_cv_env_F77_set=${F77+set} -ac_cv_env_F77_value=$F77 -ac_env_FFLAGS_set=${FFLAGS+set} -ac_env_FFLAGS_value=$FFLAGS -ac_cv_env_FFLAGS_set=${FFLAGS+set} -ac_cv_env_FFLAGS_value=$FFLAGS - -# -# Report the --help message. -# -if test "$ac_init_help" = "long"; then - # Omit some internal or obsolete options to make the list less imposing. - # This message is too long to be a string in the A/UX 3.1 sh. - cat <<_ACEOF -\`configure' configures expat 2.0.1 to adapt to many kinds of systems. - -Usage: $0 [OPTION]... [VAR=VALUE]... - -To assign environment variables (e.g., CC, CFLAGS...), specify them as -VAR=VALUE. See below for descriptions of some of the useful variables. - -Defaults for the options are specified in brackets. - -Configuration: - -h, --help display this help and exit - --help=short display options specific to this package - --help=recursive display the short help of all the included packages - -V, --version display version information and exit - -q, --quiet, --silent do not print \`checking...' messages - --cache-file=FILE cache test results in FILE [disabled] - -C, --config-cache alias for \`--cache-file=config.cache' - -n, --no-create do not create output files - --srcdir=DIR find the sources in DIR [configure dir or \`..'] - -_ACEOF - - cat <<_ACEOF -Installation directories: - --prefix=PREFIX install architecture-independent files in PREFIX - [$ac_default_prefix] - --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX - [PREFIX] - -By default, \`make install' will install all the files in -\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify -an installation prefix other than \`$ac_default_prefix' using \`--prefix', -for instance \`--prefix=\$HOME'. - -For better control, use the options below. - -Fine tuning of the installation directories: - --bindir=DIR user executables [EPREFIX/bin] - --sbindir=DIR system admin executables [EPREFIX/sbin] - --libexecdir=DIR program executables [EPREFIX/libexec] - --datadir=DIR read-only architecture-independent data [PREFIX/share] - --sysconfdir=DIR read-only single-machine data [PREFIX/etc] - --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] - --localstatedir=DIR modifiable single-machine data [PREFIX/var] - --libdir=DIR object code libraries [EPREFIX/lib] - --includedir=DIR C header files [PREFIX/include] - --oldincludedir=DIR C header files for non-gcc [/usr/include] - --infodir=DIR info documentation [PREFIX/info] - --mandir=DIR man documentation [PREFIX/man] -_ACEOF - - cat <<\_ACEOF - -System types: - --build=BUILD configure for building on BUILD [guessed] - --host=HOST cross-compile to build programs to run on HOST [BUILD] -_ACEOF -fi - -if test -n "$ac_init_help"; then - case $ac_init_help in - short | recursive ) echo "Configuration of expat 2.0.1:";; - esac - cat <<\_ACEOF - -Optional Features: - --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) - --enable-FEATURE[=ARG] include FEATURE [ARG=yes] - --enable-shared[=PKGS] - build shared libraries [default=yes] - --enable-static[=PKGS] - build static libraries [default=yes] - --enable-fast-install[=PKGS] - optimize for fast installation [default=yes] - --disable-libtool-lock avoid locking (might break parallel builds) - -Optional Packages: - --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] - --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) - --with-gnu-ld assume the C compiler uses GNU ld [default=no] - --with-pic try to use only PIC/non-PIC objects [default=use - both] - --with-tags[=TAGS] - include additional configurations [automatic] - -Some influential environment variables: - CC C compiler command - CFLAGS C compiler flags - LDFLAGS linker flags, e.g. -L if you have libraries in a - nonstandard directory - CPPFLAGS C/C++ preprocessor flags, e.g. -I if you have - headers in a nonstandard directory - CPP C preprocessor - CXX C++ compiler command - CXXFLAGS C++ compiler flags - CXXCPP C++ preprocessor - F77 Fortran 77 compiler command - FFLAGS Fortran 77 compiler flags - -Use these variables to override the choices made by `configure' or to help -it to find libraries and programs with nonstandard names/locations. - -Report bugs to . -_ACEOF -fi - -if test "$ac_init_help" = "recursive"; then - # If there are subdirs, report their specific --help. - ac_popdir=`pwd` - for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue - test -d $ac_dir || continue - ac_builddir=. - -if test "$ac_dir" != .; then - ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` - # A "../" for each directory in $ac_dir_suffix. - ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'` -else - ac_dir_suffix= ac_top_builddir= -fi - -case $srcdir in - .) # No --srcdir option. We are building in place. - ac_srcdir=. - if test -z "$ac_top_builddir"; then - ac_top_srcdir=. - else - ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` - fi ;; - [\\/]* | ?:[\\/]* ) # Absolute path. - ac_srcdir=$srcdir$ac_dir_suffix; - ac_top_srcdir=$srcdir ;; - *) # Relative path. - ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix - ac_top_srcdir=$ac_top_builddir$srcdir ;; -esac - -# Do not use `cd foo && pwd` to compute absolute paths, because -# the directories may not exist. -case `pwd` in -.) ac_abs_builddir="$ac_dir";; -*) - case "$ac_dir" in - .) ac_abs_builddir=`pwd`;; - [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";; - *) ac_abs_builddir=`pwd`/"$ac_dir";; - esac;; -esac -case $ac_abs_builddir in -.) ac_abs_top_builddir=${ac_top_builddir}.;; -*) - case ${ac_top_builddir}. in - .) ac_abs_top_builddir=$ac_abs_builddir;; - [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;; - *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;; - esac;; -esac -case $ac_abs_builddir in -.) ac_abs_srcdir=$ac_srcdir;; -*) - case $ac_srcdir in - .) ac_abs_srcdir=$ac_abs_builddir;; - [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;; - *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;; - esac;; -esac -case $ac_abs_builddir in -.) ac_abs_top_srcdir=$ac_top_srcdir;; -*) - case $ac_top_srcdir in - .) ac_abs_top_srcdir=$ac_abs_builddir;; - [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;; - *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;; - esac;; -esac - - cd $ac_dir - # Check for guested configure; otherwise get Cygnus style configure. - if test -f $ac_srcdir/configure.gnu; then - echo - $SHELL $ac_srcdir/configure.gnu --help=recursive - elif test -f $ac_srcdir/configure; then - echo - $SHELL $ac_srcdir/configure --help=recursive - elif test -f $ac_srcdir/configure.ac || - test -f $ac_srcdir/configure.in; then - echo - $ac_configure --help - else - echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 - fi - cd $ac_popdir - done -fi - -test -n "$ac_init_help" && exit 0 -if $ac_init_version; then - cat <<\_ACEOF -expat configure 2.0.1 -generated by GNU Autoconf 2.59 - -Copyright (C) 2003 Free Software Foundation, Inc. -This configure script is free software; the Free Software Foundation -gives unlimited permission to copy, distribute and modify it. -_ACEOF - exit 0 -fi -exec 5>config.log -cat >&5 <<_ACEOF -This file contains any messages produced by compilers while -running configure, to aid debugging if configure makes a mistake. - -It was created by expat $as_me 2.0.1, which was -generated by GNU Autoconf 2.59. Invocation command line was - - $ $0 $@ - -_ACEOF -{ -cat <<_ASUNAME -## --------- ## -## Platform. ## -## --------- ## - -hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` -uname -m = `(uname -m) 2>/dev/null || echo unknown` -uname -r = `(uname -r) 2>/dev/null || echo unknown` -uname -s = `(uname -s) 2>/dev/null || echo unknown` -uname -v = `(uname -v) 2>/dev/null || echo unknown` - -/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` -/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` - -/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` -/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` -/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` -hostinfo = `(hostinfo) 2>/dev/null || echo unknown` -/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` -/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` -/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` - -_ASUNAME - -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - echo "PATH: $as_dir" -done - -} >&5 - -cat >&5 <<_ACEOF - - -## ----------- ## -## Core tests. ## -## ----------- ## - -_ACEOF - - -# Keep a trace of the command line. -# Strip out --no-create and --no-recursion so they do not pile up. -# Strip out --silent because we don't want to record it for future runs. -# Also quote any args containing shell meta-characters. -# Make two passes to allow for proper duplicate-argument suppression. -ac_configure_args= -ac_configure_args0= -ac_configure_args1= -ac_sep= -ac_must_keep_next=false -for ac_pass in 1 2 -do - for ac_arg - do - case $ac_arg in - -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; - -q | -quiet | --quiet | --quie | --qui | --qu | --q \ - | -silent | --silent | --silen | --sile | --sil) - continue ;; - *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*) - ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; - esac - case $ac_pass in - 1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;; - 2) - ac_configure_args1="$ac_configure_args1 '$ac_arg'" - if test $ac_must_keep_next = true; then - ac_must_keep_next=false # Got value, back to normal. - else - case $ac_arg in - *=* | --config-cache | -C | -disable-* | --disable-* \ - | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ - | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ - | -with-* | --with-* | -without-* | --without-* | --x) - case "$ac_configure_args0 " in - "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; - esac - ;; - -* ) ac_must_keep_next=true ;; - esac - fi - ac_configure_args="$ac_configure_args$ac_sep'$ac_arg'" - # Get rid of the leading space. - ac_sep=" " - ;; - esac - done -done -$as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; } -$as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; } - -# When interrupted or exit'd, cleanup temporary files, and complete -# config.log. We remove comments because anyway the quotes in there -# would cause problems or look ugly. -# WARNING: Be sure not to use single quotes in there, as some shells, -# such as our DU 5.0 friend, will then `close' the trap. -trap 'exit_status=$? - # Save into config.log some information that might help in debugging. - { - echo - - cat <<\_ASBOX -## ---------------- ## -## Cache variables. ## -## ---------------- ## -_ASBOX - echo - # The following way of writing the cache mishandles newlines in values, -{ - (set) 2>&1 | - case `(ac_space='"'"' '"'"'; set | grep ac_space) 2>&1` in - *ac_space=\ *) - sed -n \ - "s/'"'"'/'"'"'\\\\'"'"''"'"'/g; - s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='"'"'\\2'"'"'/p" - ;; - *) - sed -n \ - "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p" - ;; - esac; -} - echo - - cat <<\_ASBOX -## ----------------- ## -## Output variables. ## -## ----------------- ## -_ASBOX - echo - for ac_var in $ac_subst_vars - do - eval ac_val=$`echo $ac_var` - echo "$ac_var='"'"'$ac_val'"'"'" - done | sort - echo - - if test -n "$ac_subst_files"; then - cat <<\_ASBOX -## ------------- ## -## Output files. ## -## ------------- ## -_ASBOX - echo - for ac_var in $ac_subst_files - do - eval ac_val=$`echo $ac_var` - echo "$ac_var='"'"'$ac_val'"'"'" - done | sort - echo - fi - - if test -s confdefs.h; then - cat <<\_ASBOX -## ----------- ## -## confdefs.h. ## -## ----------- ## -_ASBOX - echo - sed "/^$/d" confdefs.h | sort - echo - fi - test "$ac_signal" != 0 && - echo "$as_me: caught signal $ac_signal" - echo "$as_me: exit $exit_status" - } >&5 - rm -f core *.core && - rm -rf conftest* confdefs* conf$$* $ac_clean_files && - exit $exit_status - ' 0 -for ac_signal in 1 2 13 15; do - trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal -done -ac_signal=0 - -# confdefs.h avoids OS command line length limits that DEFS can exceed. -rm -rf conftest* confdefs.h -# AIX cpp loses on an empty file, so make sure it contains at least a newline. -echo >confdefs.h - -# Predefined preprocessor variables. - -cat >>confdefs.h <<_ACEOF -#define PACKAGE_NAME "$PACKAGE_NAME" -_ACEOF - - -cat >>confdefs.h <<_ACEOF -#define PACKAGE_TARNAME "$PACKAGE_TARNAME" -_ACEOF - - -cat >>confdefs.h <<_ACEOF -#define PACKAGE_VERSION "$PACKAGE_VERSION" -_ACEOF - - -cat >>confdefs.h <<_ACEOF -#define PACKAGE_STRING "$PACKAGE_STRING" -_ACEOF - - -cat >>confdefs.h <<_ACEOF -#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" -_ACEOF - - -# Let the site file select an alternate cache file if it wants to. -# Prefer explicitly selected file to automatically selected ones. -if test -z "$CONFIG_SITE"; then - if test "x$prefix" != xNONE; then - CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site" - else - CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" - fi -fi -for ac_site_file in $CONFIG_SITE; do - if test -r "$ac_site_file"; then - { echo "$as_me:$LINENO: loading site script $ac_site_file" >&5 -echo "$as_me: loading site script $ac_site_file" >&6;} - sed 's/^/| /' "$ac_site_file" >&5 - . "$ac_site_file" - fi -done - -if test -r "$cache_file"; then - # Some versions of bash will fail to source /dev/null (special - # files actually), so we avoid doing that. - if test -f "$cache_file"; then - { echo "$as_me:$LINENO: loading cache $cache_file" >&5 -echo "$as_me: loading cache $cache_file" >&6;} - case $cache_file in - [\\/]* | ?:[\\/]* ) . $cache_file;; - *) . ./$cache_file;; - esac - fi -else - { echo "$as_me:$LINENO: creating cache $cache_file" >&5 -echo "$as_me: creating cache $cache_file" >&6;} - >$cache_file -fi - -# Check that the precious variables saved in the cache have kept the same -# value. -ac_cache_corrupted=false -for ac_var in `(set) 2>&1 | - sed -n 's/^ac_env_\([a-zA-Z_0-9]*\)_set=.*/\1/p'`; do - eval ac_old_set=\$ac_cv_env_${ac_var}_set - eval ac_new_set=\$ac_env_${ac_var}_set - eval ac_old_val="\$ac_cv_env_${ac_var}_value" - eval ac_new_val="\$ac_env_${ac_var}_value" - case $ac_old_set,$ac_new_set in - set,) - { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 -echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} - ac_cache_corrupted=: ;; - ,set) - { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5 -echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} - ac_cache_corrupted=: ;; - ,);; - *) - if test "x$ac_old_val" != "x$ac_new_val"; then - { echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5 -echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} - { echo "$as_me:$LINENO: former value: $ac_old_val" >&5 -echo "$as_me: former value: $ac_old_val" >&2;} - { echo "$as_me:$LINENO: current value: $ac_new_val" >&5 -echo "$as_me: current value: $ac_new_val" >&2;} - ac_cache_corrupted=: - fi;; - esac - # Pass precious variables to config.status. - if test "$ac_new_set" = set; then - case $ac_new_val in - *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*) - ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; - *) ac_arg=$ac_var=$ac_new_val ;; - esac - case " $ac_configure_args " in - *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. - *) ac_configure_args="$ac_configure_args '$ac_arg'" ;; - esac - fi -done -if $ac_cache_corrupted; then - { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5 -echo "$as_me: error: changes in the environment can compromise the build" >&2;} - { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5 -echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;} - { (exit 1); exit 1; }; } -fi - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -ac_aux_dir= -for ac_dir in conftools $srcdir/conftools; do - if test -f $ac_dir/install-sh; then - ac_aux_dir=$ac_dir - ac_install_sh="$ac_aux_dir/install-sh -c" - break - elif test -f $ac_dir/install.sh; then - ac_aux_dir=$ac_dir - ac_install_sh="$ac_aux_dir/install.sh -c" - break - elif test -f $ac_dir/shtool; then - ac_aux_dir=$ac_dir - ac_install_sh="$ac_aux_dir/shtool install -c" - break - fi -done -if test -z "$ac_aux_dir"; then - { { echo "$as_me:$LINENO: error: cannot find install-sh or install.sh in conftools $srcdir/conftools" >&5 -echo "$as_me: error: cannot find install-sh or install.sh in conftools $srcdir/conftools" >&2;} - { (exit 1); exit 1; }; } -fi -ac_config_guess="$SHELL $ac_aux_dir/config.guess" -ac_config_sub="$SHELL $ac_aux_dir/config.sub" -ac_configure="$SHELL $ac_aux_dir/configure" # This should be Cygnus configure. - - - - -LIBCURRENT=6 -LIBREVISION=2 -LIBAGE=5 - - ac_config_headers="$ac_config_headers expat_config.h" - - -# libtool.m4 - Configure libtool for the host system. -*-Autoconf-*- -## Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005 -## Free Software Foundation, Inc. -## Originally by Gordon Matzigkeit , 1996 -## -## This file is free software; the Free Software Foundation gives -## unlimited permission to copy and/or distribute it, with or without -## modifications, as long as this notice is preserved. - -# serial 48 AC_PROG_LIBTOOL - - -# AC_PROVIDE_IFELSE(MACRO-NAME, IF-PROVIDED, IF-NOT-PROVIDED) -# ----------------------------------------------------------- -# If this macro is not defined by Autoconf, define it here. - - - -# AC_PROG_LIBTOOL -# --------------- -# AC_PROG_LIBTOOL - - -# _AC_PROG_LIBTOOL -# ---------------- -# _AC_PROG_LIBTOOL - - -# AC_LIBTOOL_SETUP -# ---------------- -# AC_LIBTOOL_SETUP - - -# _LT_AC_SYS_COMPILER -# ------------------- -# _LT_AC_SYS_COMPILER - - -# _LT_CC_BASENAME(CC) -# ------------------- -# Calculate cc_basename. Skip known compiler wrappers and cross-prefix. - - - -# _LT_COMPILER_BOILERPLATE -# ------------------------ -# Check for compiler boilerplate output or warnings with -# the simple compiler test code. -# _LT_COMPILER_BOILERPLATE - - -# _LT_LINKER_BOILERPLATE -# ---------------------- -# Check for linker boilerplate output or warnings with -# the simple link test code. -# _LT_LINKER_BOILERPLATE - - -# _LT_AC_SYS_LIBPATH_AIX -# ---------------------- -# Links a minimal program and checks the executable -# for the system default hardcoded library path. In most cases, -# this is /usr/lib:/lib, but when the MPI compilers are used -# the location of the communication and MPI libs are included too. -# If we don't find anything, use the default library path according -# to the aix ld manual. -# _LT_AC_SYS_LIBPATH_AIX - - -# _LT_AC_SHELL_INIT(ARG) -# ---------------------- -# _LT_AC_SHELL_INIT - - -# _LT_AC_PROG_ECHO_BACKSLASH -# -------------------------- -# Add some code to the start of the generated configure script which -# will find an echo command which doesn't interpret backslashes. -# _LT_AC_PROG_ECHO_BACKSLASH - - -# _LT_AC_LOCK -# ----------- -# _LT_AC_LOCK - - -# AC_LIBTOOL_COMPILER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS, -# [OUTPUT-FILE], [ACTION-SUCCESS], [ACTION-FAILURE]) -# ---------------------------------------------------------------- -# Check whether the given compiler option works -# AC_LIBTOOL_COMPILER_OPTION - - -# AC_LIBTOOL_LINKER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS, -# [ACTION-SUCCESS], [ACTION-FAILURE]) -# ------------------------------------------------------------ -# Check whether the given compiler option works -# AC_LIBTOOL_LINKER_OPTION - - -# AC_LIBTOOL_SYS_MAX_CMD_LEN -# -------------------------- -# AC_LIBTOOL_SYS_MAX_CMD_LEN - - -# _LT_AC_CHECK_DLFCN -# ------------------ -# _LT_AC_CHECK_DLFCN - - -# _LT_AC_TRY_DLOPEN_SELF (ACTION-IF-TRUE, ACTION-IF-TRUE-W-USCORE, -# ACTION-IF-FALSE, ACTION-IF-CROSS-COMPILING) -# --------------------------------------------------------------------- -# _LT_AC_TRY_DLOPEN_SELF - - -# AC_LIBTOOL_DLOPEN_SELF -# ---------------------- -# AC_LIBTOOL_DLOPEN_SELF - - -# AC_LIBTOOL_PROG_CC_C_O([TAGNAME]) -# --------------------------------- -# Check to see if options -c and -o are simultaneously supported by compiler -# AC_LIBTOOL_PROG_CC_C_O - - -# AC_LIBTOOL_SYS_HARD_LINK_LOCKS([TAGNAME]) -# ----------------------------------------- -# Check to see if we can do hard links to lock some files if needed -# AC_LIBTOOL_SYS_HARD_LINK_LOCKS - - -# AC_LIBTOOL_OBJDIR -# ----------------- -# AC_LIBTOOL_OBJDIR - - -# AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH([TAGNAME]) -# ---------------------------------------------- -# Check hardcoding attributes. -# AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH - - -# AC_LIBTOOL_SYS_LIB_STRIP -# ------------------------ -# AC_LIBTOOL_SYS_LIB_STRIP - - -# AC_LIBTOOL_SYS_DYNAMIC_LINKER -# ----------------------------- -# PORTME Fill in your ld.so characteristics -# AC_LIBTOOL_SYS_DYNAMIC_LINKER - - -# _LT_AC_TAGCONFIG -# ---------------- -# _LT_AC_TAGCONFIG - - -# AC_LIBTOOL_DLOPEN -# ----------------- -# enable checks for dlopen support -# AC_LIBTOOL_DLOPEN - - -# AC_LIBTOOL_WIN32_DLL -# -------------------- -# declare package support for building win32 DLLs -# AC_LIBTOOL_WIN32_DLL - - -# AC_ENABLE_SHARED([DEFAULT]) -# --------------------------- -# implement the --enable-shared flag -# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'. -# AC_ENABLE_SHARED - - -# AC_DISABLE_SHARED -# ----------------- -# set the default shared flag to --disable-shared -# AC_DISABLE_SHARED - - -# AC_ENABLE_STATIC([DEFAULT]) -# --------------------------- -# implement the --enable-static flag -# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'. -# AC_ENABLE_STATIC - - -# AC_DISABLE_STATIC -# ----------------- -# set the default static flag to --disable-static -# AC_DISABLE_STATIC - - -# AC_ENABLE_FAST_INSTALL([DEFAULT]) -# --------------------------------- -# implement the --enable-fast-install flag -# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'. -# AC_ENABLE_FAST_INSTALL - - -# AC_DISABLE_FAST_INSTALL -# ----------------------- -# set the default to --disable-fast-install -# AC_DISABLE_FAST_INSTALL - - -# AC_LIBTOOL_PICMODE([MODE]) -# -------------------------- -# implement the --with-pic flag -# MODE is either `yes' or `no'. If omitted, it defaults to `both'. -# AC_LIBTOOL_PICMODE - - -# AC_PROG_EGREP -# ------------- -# This is predefined starting with Autoconf 2.54, so this conditional -# definition can be removed once we require Autoconf 2.54 or later. - - - -# AC_PATH_TOOL_PREFIX -# ------------------- -# find a file program which can recognise shared library -# AC_PATH_TOOL_PREFIX - - -# AC_PATH_MAGIC -# ------------- -# find a file program which can recognise a shared library -# AC_PATH_MAGIC - - -# AC_PROG_LD -# ---------- -# find the pathname to the GNU or non-GNU linker -# AC_PROG_LD - - -# AC_PROG_LD_GNU -# -------------- -# AC_PROG_LD_GNU - - -# AC_PROG_LD_RELOAD_FLAG -# ---------------------- -# find reload flag for linker -# -- PORTME Some linkers may need a different reload flag. -# AC_PROG_LD_RELOAD_FLAG - - -# AC_DEPLIBS_CHECK_METHOD -# ----------------------- -# how to check for library dependencies -# -- PORTME fill in with the dynamic library characteristics -# AC_DEPLIBS_CHECK_METHOD - - -# AC_PROG_NM -# ---------- -# find the pathname to a BSD-compatible name lister -# AC_PROG_NM - - -# AC_CHECK_LIBM -# ------------- -# check for math library -# AC_CHECK_LIBM - - -# AC_LIBLTDL_CONVENIENCE([DIRECTORY]) -# ----------------------------------- -# sets LIBLTDL to the link flags for the libltdl convenience library and -# LTDLINCL to the include flags for the libltdl header and adds -# --enable-ltdl-convenience to the configure arguments. Note that -# AC_CONFIG_SUBDIRS is not called here. If DIRECTORY is not provided, -# it is assumed to be `libltdl'. LIBLTDL will be prefixed with -# '${top_builddir}/' and LTDLINCL will be prefixed with '${top_srcdir}/' -# (note the single quotes!). If your package is not flat and you're not -# using automake, define top_builddir and top_srcdir appropriately in -# the Makefiles. -# AC_LIBLTDL_CONVENIENCE - - -# AC_LIBLTDL_INSTALLABLE([DIRECTORY]) -# ----------------------------------- -# sets LIBLTDL to the link flags for the libltdl installable library and -# LTDLINCL to the include flags for the libltdl header and adds -# --enable-ltdl-install to the configure arguments. Note that -# AC_CONFIG_SUBDIRS is not called here. If DIRECTORY is not provided, -# and an installed libltdl is not found, it is assumed to be `libltdl'. -# LIBLTDL will be prefixed with '${top_builddir}/'# and LTDLINCL with -# '${top_srcdir}/' (note the single quotes!). If your package is not -# flat and you're not using automake, define top_builddir and top_srcdir -# appropriately in the Makefiles. -# In the future, this macro may have to be called after AC_PROG_LIBTOOL. -# AC_LIBLTDL_INSTALLABLE - - -# AC_LIBTOOL_CXX -# -------------- -# enable support for C++ libraries -# AC_LIBTOOL_CXX - - -# _LT_AC_LANG_CXX -# --------------- -# _LT_AC_LANG_CXX - -# _LT_AC_PROG_CXXCPP -# ------------------ -# _LT_AC_PROG_CXXCPP - -# AC_LIBTOOL_F77 -# -------------- -# enable support for Fortran 77 libraries -# AC_LIBTOOL_F77 - - -# _LT_AC_LANG_F77 -# --------------- -# _LT_AC_LANG_F77 - - -# AC_LIBTOOL_GCJ -# -------------- -# enable support for GCJ libraries -# AC_LIBTOOL_GCJ - - -# _LT_AC_LANG_GCJ -# --------------- -# _LT_AC_LANG_GCJ - - -# AC_LIBTOOL_RC -# ------------- -# enable support for Windows resource files -# AC_LIBTOOL_RC - - -# AC_LIBTOOL_LANG_C_CONFIG -# ------------------------ -# Ensure that the configuration vars for the C compiler are -# suitably defined. Those variables are subsequently used by -# AC_LIBTOOL_CONFIG to write the compiler configuration to `libtool'. - -# AC_LIBTOOL_LANG_C_CONFIG - - -# AC_LIBTOOL_LANG_CXX_CONFIG -# -------------------------- -# Ensure that the configuration vars for the C compiler are -# suitably defined. Those variables are subsequently used by -# AC_LIBTOOL_CONFIG to write the compiler configuration to `libtool'. - -# AC_LIBTOOL_LANG_CXX_CONFIG - -# AC_LIBTOOL_POSTDEP_PREDEP([TAGNAME]) -# ------------------------------------ -# Figure out "hidden" library dependencies from verbose -# compiler output when linking a shared library. -# Parse the compiler output and extract the necessary -# objects, libraries and library flags. -# AC_LIBTOOL_POSTDEP_PREDEP - -# AC_LIBTOOL_LANG_F77_CONFIG -# -------------------------- -# Ensure that the configuration vars for the C compiler are -# suitably defined. Those variables are subsequently used by -# AC_LIBTOOL_CONFIG to write the compiler configuration to `libtool'. - -# AC_LIBTOOL_LANG_F77_CONFIG - - -# AC_LIBTOOL_LANG_GCJ_CONFIG -# -------------------------- -# Ensure that the configuration vars for the C compiler are -# suitably defined. Those variables are subsequently used by -# AC_LIBTOOL_CONFIG to write the compiler configuration to `libtool'. - -# AC_LIBTOOL_LANG_GCJ_CONFIG - - -# AC_LIBTOOL_LANG_RC_CONFIG -# ------------------------- -# Ensure that the configuration vars for the Windows resource compiler are -# suitably defined. Those variables are subsequently used by -# AC_LIBTOOL_CONFIG to write the compiler configuration to `libtool'. - -# AC_LIBTOOL_LANG_RC_CONFIG - - -# AC_LIBTOOL_CONFIG([TAGNAME]) -# ---------------------------- -# If TAGNAME is not passed, then create an initial libtool script -# with a default configuration from the untagged config vars. Otherwise -# add code to config.status for appending the configuration named by -# TAGNAME from the matching tagged config vars. -# AC_LIBTOOL_CONFIG - - -# AC_LIBTOOL_PROG_COMPILER_NO_RTTI([TAGNAME]) -# ------------------------------------------- -# AC_LIBTOOL_PROG_COMPILER_NO_RTTI - - -# AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE -# --------------------------------- - # AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE - - -# AC_LIBTOOL_PROG_COMPILER_PIC([TAGNAME]) -# --------------------------------------- - - - -# AC_LIBTOOL_PROG_LD_SHLIBS([TAGNAME]) -# ------------------------------------ -# See if the linker supports building shared libraries. -# AC_LIBTOOL_PROG_LD_SHLIBS - - -# _LT_AC_FILE_LTDLL_C -# ------------------- -# Be careful that the start marker always follows a newline. -# _LT_AC_FILE_LTDLL_C - - -# _LT_AC_TAGVAR(VARNAME, [TAGNAME]) -# --------------------------------- - - - -# old names - - - - - - - - -# This is just to silence aclocal about the macro not being used - - - - - - -############################################################ -# NOTE: This macro has been submitted for inclusion into # -# GNU Autoconf as AC_PROG_SED. When it is available in # -# a released version of Autoconf we should remove this # -# macro and use it instead. # -############################################################ -# LT_AC_PROG_SED -# -------------- -# Check for a fully-functional sed program, that truncates -# as few characters as possible. Prefer GNU sed if found. - - - - - - - -# Check whether --enable-shared or --disable-shared was given. -if test "${enable_shared+set}" = set; then - enableval="$enable_shared" - p=${PACKAGE-default} - case $enableval in - yes) enable_shared=yes ;; - no) enable_shared=no ;; - *) - enable_shared=no - # Look at the argument we got. We use all the common list separators. - lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," - for pkg in $enableval; do - IFS="$lt_save_ifs" - if test "X$pkg" = "X$p"; then - enable_shared=yes - fi - done - IFS="$lt_save_ifs" - ;; - esac -else - enable_shared=yes -fi; - -# Check whether --enable-static or --disable-static was given. -if test "${enable_static+set}" = set; then - enableval="$enable_static" - p=${PACKAGE-default} - case $enableval in - yes) enable_static=yes ;; - no) enable_static=no ;; - *) - enable_static=no - # Look at the argument we got. We use all the common list separators. - lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," - for pkg in $enableval; do - IFS="$lt_save_ifs" - if test "X$pkg" = "X$p"; then - enable_static=yes - fi - done - IFS="$lt_save_ifs" - ;; - esac -else - enable_static=yes -fi; - -# Check whether --enable-fast-install or --disable-fast-install was given. -if test "${enable_fast_install+set}" = set; then - enableval="$enable_fast_install" - p=${PACKAGE-default} - case $enableval in - yes) enable_fast_install=yes ;; - no) enable_fast_install=no ;; - *) - enable_fast_install=no - # Look at the argument we got. We use all the common list separators. - lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," - for pkg in $enableval; do - IFS="$lt_save_ifs" - if test "X$pkg" = "X$p"; then - enable_fast_install=yes - fi - done - IFS="$lt_save_ifs" - ;; - esac -else - enable_fast_install=yes -fi; - -# Make sure we can run config.sub. -$ac_config_sub sun4 >/dev/null 2>&1 || - { { echo "$as_me:$LINENO: error: cannot run $ac_config_sub" >&5 -echo "$as_me: error: cannot run $ac_config_sub" >&2;} - { (exit 1); exit 1; }; } - -echo "$as_me:$LINENO: checking build system type" >&5 -echo $ECHO_N "checking build system type... $ECHO_C" >&6 -if test "${ac_cv_build+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - ac_cv_build_alias=$build_alias -test -z "$ac_cv_build_alias" && - ac_cv_build_alias=`$ac_config_guess` -test -z "$ac_cv_build_alias" && - { { echo "$as_me:$LINENO: error: cannot guess build type; you must specify one" >&5 -echo "$as_me: error: cannot guess build type; you must specify one" >&2;} - { (exit 1); exit 1; }; } -ac_cv_build=`$ac_config_sub $ac_cv_build_alias` || - { { echo "$as_me:$LINENO: error: $ac_config_sub $ac_cv_build_alias failed" >&5 -echo "$as_me: error: $ac_config_sub $ac_cv_build_alias failed" >&2;} - { (exit 1); exit 1; }; } - -fi -echo "$as_me:$LINENO: result: $ac_cv_build" >&5 -echo "${ECHO_T}$ac_cv_build" >&6 -build=$ac_cv_build -build_cpu=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` -build_vendor=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'` -build_os=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` - - -echo "$as_me:$LINENO: checking host system type" >&5 -echo $ECHO_N "checking host system type... $ECHO_C" >&6 -if test "${ac_cv_host+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - ac_cv_host_alias=$host_alias -test -z "$ac_cv_host_alias" && - ac_cv_host_alias=$ac_cv_build_alias -ac_cv_host=`$ac_config_sub $ac_cv_host_alias` || - { { echo "$as_me:$LINENO: error: $ac_config_sub $ac_cv_host_alias failed" >&5 -echo "$as_me: error: $ac_config_sub $ac_cv_host_alias failed" >&2;} - { (exit 1); exit 1; }; } - -fi -echo "$as_me:$LINENO: result: $ac_cv_host" >&5 -echo "${ECHO_T}$ac_cv_host" >&6 -host=$ac_cv_host -host_cpu=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` -host_vendor=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'` -host_os=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` - - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu -if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. -set dummy ${ac_tool_prefix}gcc; ac_word=$2 -echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 -if test "${ac_cv_prog_CC+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_CC="${ac_tool_prefix}gcc" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done -done - -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - echo "$as_me:$LINENO: result: $CC" >&5 -echo "${ECHO_T}$CC" >&6 -else - echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6 -fi - -fi -if test -z "$ac_cv_prog_CC"; then - ac_ct_CC=$CC - # Extract the first word of "gcc", so it can be a program name with args. -set dummy gcc; ac_word=$2 -echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 -if test "${ac_cv_prog_ac_ct_CC+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test -n "$ac_ct_CC"; then - ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_CC="gcc" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done -done - -fi -fi -ac_ct_CC=$ac_cv_prog_ac_ct_CC -if test -n "$ac_ct_CC"; then - echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 -echo "${ECHO_T}$ac_ct_CC" >&6 -else - echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6 -fi - - CC=$ac_ct_CC -else - CC="$ac_cv_prog_CC" -fi - -if test -z "$CC"; then - if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. -set dummy ${ac_tool_prefix}cc; ac_word=$2 -echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 -if test "${ac_cv_prog_CC+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_CC="${ac_tool_prefix}cc" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done -done - -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - echo "$as_me:$LINENO: result: $CC" >&5 -echo "${ECHO_T}$CC" >&6 -else - echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6 -fi - -fi -if test -z "$ac_cv_prog_CC"; then - ac_ct_CC=$CC - # Extract the first word of "cc", so it can be a program name with args. -set dummy cc; ac_word=$2 -echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 -if test "${ac_cv_prog_ac_ct_CC+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test -n "$ac_ct_CC"; then - ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_CC="cc" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done -done - -fi -fi -ac_ct_CC=$ac_cv_prog_ac_ct_CC -if test -n "$ac_ct_CC"; then - echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 -echo "${ECHO_T}$ac_ct_CC" >&6 -else - echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6 -fi - - CC=$ac_ct_CC -else - CC="$ac_cv_prog_CC" -fi - -fi -if test -z "$CC"; then - # Extract the first word of "cc", so it can be a program name with args. -set dummy cc; ac_word=$2 -echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 -if test "${ac_cv_prog_CC+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else - ac_prog_rejected=no -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then - ac_prog_rejected=yes - continue - fi - ac_cv_prog_CC="cc" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done -done - -if test $ac_prog_rejected = yes; then - # We found a bogon in the path, so make sure we never use it. - set dummy $ac_cv_prog_CC - shift - if test $# != 0; then - # We chose a different compiler from the bogus one. - # However, it has the same basename, so the bogon will be chosen - # first if we set CC to just the basename; use the full file name. - shift - ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" - fi -fi -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - echo "$as_me:$LINENO: result: $CC" >&5 -echo "${ECHO_T}$CC" >&6 -else - echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6 -fi - -fi -if test -z "$CC"; then - if test -n "$ac_tool_prefix"; then - for ac_prog in cl - do - # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. -set dummy $ac_tool_prefix$ac_prog; ac_word=$2 -echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 -if test "${ac_cv_prog_CC+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_CC="$ac_tool_prefix$ac_prog" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done -done - -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - echo "$as_me:$LINENO: result: $CC" >&5 -echo "${ECHO_T}$CC" >&6 -else - echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6 -fi - - test -n "$CC" && break - done -fi -if test -z "$CC"; then - ac_ct_CC=$CC - for ac_prog in cl -do - # Extract the first word of "$ac_prog", so it can be a program name with args. -set dummy $ac_prog; ac_word=$2 -echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 -if test "${ac_cv_prog_ac_ct_CC+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test -n "$ac_ct_CC"; then - ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_CC="$ac_prog" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done -done - -fi -fi -ac_ct_CC=$ac_cv_prog_ac_ct_CC -if test -n "$ac_ct_CC"; then - echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 -echo "${ECHO_T}$ac_ct_CC" >&6 -else - echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6 -fi - - test -n "$ac_ct_CC" && break -done - - CC=$ac_ct_CC -fi - -fi - - -test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH -See \`config.log' for more details." >&5 -echo "$as_me: error: no acceptable C compiler found in \$PATH -See \`config.log' for more details." >&2;} - { (exit 1); exit 1; }; } - -# Provide some information about the compiler. -echo "$as_me:$LINENO:" \ - "checking for C compiler version" >&5 -ac_compiler=`set X $ac_compile; echo $2` -{ (eval echo "$as_me:$LINENO: \"$ac_compiler --version &5\"") >&5 - (eval $ac_compiler --version &5) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } -{ (eval echo "$as_me:$LINENO: \"$ac_compiler -v &5\"") >&5 - (eval $ac_compiler -v &5) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } -{ (eval echo "$as_me:$LINENO: \"$ac_compiler -V &5\"") >&5 - (eval $ac_compiler -V &5) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } - -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -ac_clean_files_save=$ac_clean_files -ac_clean_files="$ac_clean_files a.out a.exe b.out" -# Try to create an executable without -o first, disregard a.out. -# It will help us diagnose broken compilers, and finding out an intuition -# of exeext. -echo "$as_me:$LINENO: checking for C compiler default output file name" >&5 -echo $ECHO_N "checking for C compiler default output file name... $ECHO_C" >&6 -ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` -if { (eval echo "$as_me:$LINENO: \"$ac_link_default\"") >&5 - (eval $ac_link_default) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; then - # Find the output, starting from the most likely. This scheme is -# not robust to junk in `.', hence go to wildcards (a.*) only as a last -# resort. - -# Be careful to initialize this variable, since it used to be cached. -# Otherwise an old cache value of `no' led to `EXEEXT = no' in a Makefile. -ac_cv_exeext= -# b.out is created by i960 compilers. -for ac_file in a_out.exe a.exe conftest.exe a.out conftest a.* conftest.* b.out -do - test -f "$ac_file" || continue - case $ac_file in - *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) - ;; - conftest.$ac_ext ) - # This is the source file. - ;; - [ab].out ) - # We found the default executable, but exeext='' is most - # certainly right. - break;; - *.* ) - ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` - # FIXME: I believe we export ac_cv_exeext for Libtool, - # but it would be cool to find out if it's true. Does anybody - # maintain Libtool? --akim. - export ac_cv_exeext - break;; - * ) - break;; - esac -done -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -{ { echo "$as_me:$LINENO: error: C compiler cannot create executables -See \`config.log' for more details." >&5 -echo "$as_me: error: C compiler cannot create executables -See \`config.log' for more details." >&2;} - { (exit 77); exit 77; }; } -fi - -ac_exeext=$ac_cv_exeext -echo "$as_me:$LINENO: result: $ac_file" >&5 -echo "${ECHO_T}$ac_file" >&6 - -# Check the compiler produces executables we can run. If not, either -# the compiler is broken, or we cross compile. -echo "$as_me:$LINENO: checking whether the C compiler works" >&5 -echo $ECHO_N "checking whether the C compiler works... $ECHO_C" >&6 -# FIXME: These cross compiler hacks should be removed for Autoconf 3.0 -# If not cross compiling, check that we can run a simple program. -if test "$cross_compiling" != yes; then - if { ac_try='./$ac_file' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - cross_compiling=no - else - if test "$cross_compiling" = maybe; then - cross_compiling=yes - else - { { echo "$as_me:$LINENO: error: cannot run C compiled programs. -If you meant to cross compile, use \`--host'. -See \`config.log' for more details." >&5 -echo "$as_me: error: cannot run C compiled programs. -If you meant to cross compile, use \`--host'. -See \`config.log' for more details." >&2;} - { (exit 1); exit 1; }; } - fi - fi -fi -echo "$as_me:$LINENO: result: yes" >&5 -echo "${ECHO_T}yes" >&6 - -rm -f a.out a.exe conftest$ac_cv_exeext b.out -ac_clean_files=$ac_clean_files_save -# Check the compiler produces executables we can run. If not, either -# the compiler is broken, or we cross compile. -echo "$as_me:$LINENO: checking whether we are cross compiling" >&5 -echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6 -echo "$as_me:$LINENO: result: $cross_compiling" >&5 -echo "${ECHO_T}$cross_compiling" >&6 - -echo "$as_me:$LINENO: checking for suffix of executables" >&5 -echo $ECHO_N "checking for suffix of executables... $ECHO_C" >&6 -if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 - (eval $ac_link) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; then - # If both `conftest.exe' and `conftest' are `present' (well, observable) -# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will -# work properly (i.e., refer to `conftest.exe'), while it won't with -# `rm'. -for ac_file in conftest.exe conftest conftest.*; do - test -f "$ac_file" || continue - case $ac_file in - *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) ;; - *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` - export ac_cv_exeext - break;; - * ) break;; - esac -done -else - { { echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link -See \`config.log' for more details." >&5 -echo "$as_me: error: cannot compute suffix of executables: cannot compile and link -See \`config.log' for more details." >&2;} - { (exit 1); exit 1; }; } -fi - -rm -f conftest$ac_cv_exeext -echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5 -echo "${ECHO_T}$ac_cv_exeext" >&6 - -rm -f conftest.$ac_ext -EXEEXT=$ac_cv_exeext -ac_exeext=$EXEEXT -echo "$as_me:$LINENO: checking for suffix of object files" >&5 -echo $ECHO_N "checking for suffix of object files... $ECHO_C" >&6 -if test "${ac_cv_objext+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -rm -f conftest.o conftest.obj -if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; then - for ac_file in `(ls conftest.o conftest.obj; ls conftest.*) 2>/dev/null`; do - case $ac_file in - *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg ) ;; - *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` - break;; - esac -done -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -{ { echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile -See \`config.log' for more details." >&5 -echo "$as_me: error: cannot compute suffix of object files: cannot compile -See \`config.log' for more details." >&2;} - { (exit 1); exit 1; }; } -fi - -rm -f conftest.$ac_cv_objext conftest.$ac_ext -fi -echo "$as_me:$LINENO: result: $ac_cv_objext" >&5 -echo "${ECHO_T}$ac_cv_objext" >&6 -OBJEXT=$ac_cv_objext -ac_objext=$OBJEXT -echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5 -echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6 -if test "${ac_cv_c_compiler_gnu+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ - -int -main () -{ -#ifndef __GNUC__ - choke me -#endif - - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext -if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest.$ac_objext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_compiler_gnu=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -ac_compiler_gnu=no -fi -rm -f conftest.err conftest.$ac_objext conftest.$ac_ext -ac_cv_c_compiler_gnu=$ac_compiler_gnu - -fi -echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5 -echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6 -GCC=`test $ac_compiler_gnu = yes && echo yes` -ac_test_CFLAGS=${CFLAGS+set} -ac_save_CFLAGS=$CFLAGS -CFLAGS="-g" -echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5 -echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6 -if test "${ac_cv_prog_cc_g+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext -if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest.$ac_objext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_cv_prog_cc_g=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -ac_cv_prog_cc_g=no -fi -rm -f conftest.err conftest.$ac_objext conftest.$ac_ext -fi -echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5 -echo "${ECHO_T}$ac_cv_prog_cc_g" >&6 -if test "$ac_test_CFLAGS" = set; then - CFLAGS=$ac_save_CFLAGS -elif test $ac_cv_prog_cc_g = yes; then - if test "$GCC" = yes; then - CFLAGS="-g -O2" - else - CFLAGS="-g" - fi -else - if test "$GCC" = yes; then - CFLAGS="-O2" - else - CFLAGS= - fi -fi -echo "$as_me:$LINENO: checking for $CC option to accept ANSI C" >&5 -echo $ECHO_N "checking for $CC option to accept ANSI C... $ECHO_C" >&6 -if test "${ac_cv_prog_cc_stdc+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - ac_cv_prog_cc_stdc=no -ac_save_CC=$CC -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include -#include -#include -#include -/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ -struct buf { int x; }; -FILE * (*rcsopen) (struct buf *, struct stat *, int); -static char *e (p, i) - char **p; - int i; -{ - return p[i]; -} -static char *f (char * (*g) (char **, int), char **p, ...) -{ - char *s; - va_list v; - va_start (v,p); - s = g (p, va_arg (v,int)); - va_end (v); - return s; -} - -/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has - function prototypes and stuff, but not '\xHH' hex character constants. - These don't provoke an error unfortunately, instead are silently treated - as 'x'. The following induces an error, until -std1 is added to get - proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an - array size at least. It's necessary to write '\x00'==0 to get something - that's true only with -std1. */ -int osf4_cc_array ['\x00' == 0 ? 1 : -1]; - -int test (int i, double x); -struct s1 {int (*f) (int a);}; -struct s2 {int (*f) (double a);}; -int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); -int argc; -char **argv; -int -main () -{ -return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; - ; - return 0; -} -_ACEOF -# Don't try gcc -ansi; that turns off useful extensions and -# breaks some systems' header files. -# AIX -qlanglvl=ansi -# Ultrix and OSF/1 -std1 -# HP-UX 10.20 and later -Ae -# HP-UX older versions -Aa -D_HPUX_SOURCE -# SVR4 -Xc -D__EXTENSIONS__ -for ac_arg in "" -qlanglvl=ansi -std1 -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" -do - CC="$ac_save_CC $ac_arg" - rm -f conftest.$ac_objext -if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest.$ac_objext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_cv_prog_cc_stdc=$ac_arg -break -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -fi -rm -f conftest.err conftest.$ac_objext -done -rm -f conftest.$ac_ext conftest.$ac_objext -CC=$ac_save_CC - -fi - -case "x$ac_cv_prog_cc_stdc" in - x|xno) - echo "$as_me:$LINENO: result: none needed" >&5 -echo "${ECHO_T}none needed" >&6 ;; - *) - echo "$as_me:$LINENO: result: $ac_cv_prog_cc_stdc" >&5 -echo "${ECHO_T}$ac_cv_prog_cc_stdc" >&6 - CC="$CC $ac_cv_prog_cc_stdc" ;; -esac - -# Some people use a C++ compiler to compile C. Since we use `exit', -# in C++ we need to declare it. In case someone uses the same compiler -# for both compiling C and C++ we need to have the C++ compiler decide -# the declaration of exit, since it's the most demanding environment. -cat >conftest.$ac_ext <<_ACEOF -#ifndef __cplusplus - choke me -#endif -_ACEOF -rm -f conftest.$ac_objext -if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest.$ac_objext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - for ac_declaration in \ - '' \ - 'extern "C" void std::exit (int) throw (); using std::exit;' \ - 'extern "C" void std::exit (int); using std::exit;' \ - 'extern "C" void exit (int) throw ();' \ - 'extern "C" void exit (int);' \ - 'void exit (int);' -do - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -$ac_declaration -#include -int -main () -{ -exit (42); - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext -if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest.$ac_objext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - : -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -continue -fi -rm -f conftest.err conftest.$ac_objext conftest.$ac_ext - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -$ac_declaration -int -main () -{ -exit (42); - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext -if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest.$ac_objext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - break -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -fi -rm -f conftest.err conftest.$ac_objext conftest.$ac_ext -done -rm -f conftest* -if test -n "$ac_declaration"; then - echo '#ifdef __cplusplus' >>confdefs.h - echo $ac_declaration >>confdefs.h - echo '#endif' >>confdefs.h -fi - -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -fi -rm -f conftest.err conftest.$ac_objext conftest.$ac_ext -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - -echo "$as_me:$LINENO: checking for a sed that does not truncate output" >&5 -echo $ECHO_N "checking for a sed that does not truncate output... $ECHO_C" >&6 -if test "${lt_cv_path_SED+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - # Loop through the user's path and test for sed and gsed. -# Then use that list of sed's as ones to test for truncation. -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for lt_ac_prog in sed gsed; do - for ac_exec_ext in '' $ac_executable_extensions; do - if $as_executable_p "$as_dir/$lt_ac_prog$ac_exec_ext"; then - lt_ac_sed_list="$lt_ac_sed_list $as_dir/$lt_ac_prog$ac_exec_ext" - fi - done - done -done -lt_ac_max=0 -lt_ac_count=0 -# Add /usr/xpg4/bin/sed as it is typically found on Solaris -# along with /bin/sed that truncates output. -for lt_ac_sed in $lt_ac_sed_list /usr/xpg4/bin/sed; do - test ! -f $lt_ac_sed && continue - cat /dev/null > conftest.in - lt_ac_count=0 - echo $ECHO_N "0123456789$ECHO_C" >conftest.in - # Check for GNU sed and select it if it is found. - if "$lt_ac_sed" --version 2>&1 < /dev/null | grep 'GNU' > /dev/null; then - lt_cv_path_SED=$lt_ac_sed - break - fi - while true; do - cat conftest.in conftest.in >conftest.tmp - mv conftest.tmp conftest.in - cp conftest.in conftest.nl - echo >>conftest.nl - $lt_ac_sed -e 's/a$//' < conftest.nl >conftest.out || break - cmp -s conftest.out conftest.nl || break - # 10000 chars as input seems more than enough - test $lt_ac_count -gt 10 && break - lt_ac_count=`expr $lt_ac_count + 1` - if test $lt_ac_count -gt $lt_ac_max; then - lt_ac_max=$lt_ac_count - lt_cv_path_SED=$lt_ac_sed - fi - done -done - -fi - -SED=$lt_cv_path_SED -echo "$as_me:$LINENO: result: $SED" >&5 -echo "${ECHO_T}$SED" >&6 - -echo "$as_me:$LINENO: checking for egrep" >&5 -echo $ECHO_N "checking for egrep... $ECHO_C" >&6 -if test "${ac_cv_prog_egrep+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if echo a | (grep -E '(a|b)') >/dev/null 2>&1 - then ac_cv_prog_egrep='grep -E' - else ac_cv_prog_egrep='egrep' - fi -fi -echo "$as_me:$LINENO: result: $ac_cv_prog_egrep" >&5 -echo "${ECHO_T}$ac_cv_prog_egrep" >&6 - EGREP=$ac_cv_prog_egrep - - - -# Check whether --with-gnu-ld or --without-gnu-ld was given. -if test "${with_gnu_ld+set}" = set; then - withval="$with_gnu_ld" - test "$withval" = no || with_gnu_ld=yes -else - with_gnu_ld=no -fi; -ac_prog=ld -if test "$GCC" = yes; then - # Check if gcc -print-prog-name=ld gives a path. - echo "$as_me:$LINENO: checking for ld used by $CC" >&5 -echo $ECHO_N "checking for ld used by $CC... $ECHO_C" >&6 - case $host in - *-*-mingw*) - # gcc leaves a trailing carriage return which upsets mingw - ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; - *) - ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; - esac - case $ac_prog in - # Accept absolute paths. - [\\/]* | ?:[\\/]*) - re_direlt='/[^/][^/]*/\.\./' - # Canonicalize the pathname of ld - ac_prog=`echo $ac_prog| $SED 's%\\\\%/%g'` - while echo $ac_prog | grep "$re_direlt" > /dev/null 2>&1; do - ac_prog=`echo $ac_prog| $SED "s%$re_direlt%/%"` - done - test -z "$LD" && LD="$ac_prog" - ;; - "") - # If it fails, then pretend we aren't using GCC. - ac_prog=ld - ;; - *) - # If it is relative, then search for the first ld in PATH. - with_gnu_ld=unknown - ;; - esac -elif test "$with_gnu_ld" = yes; then - echo "$as_me:$LINENO: checking for GNU ld" >&5 -echo $ECHO_N "checking for GNU ld... $ECHO_C" >&6 -else - echo "$as_me:$LINENO: checking for non-GNU ld" >&5 -echo $ECHO_N "checking for non-GNU ld... $ECHO_C" >&6 -fi -if test "${lt_cv_path_LD+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test -z "$LD"; then - lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR - for ac_dir in $PATH; do - IFS="$lt_save_ifs" - test -z "$ac_dir" && ac_dir=. - if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then - lt_cv_path_LD="$ac_dir/$ac_prog" - # Check to see if the program is GNU ld. I'd rather use --version, - # but apparently some variants of GNU ld only accept -v. - # Break only if it was the GNU/non-GNU ld that we prefer. - case `"$lt_cv_path_LD" -v 2>&1 &5 -echo "${ECHO_T}$LD" >&6 -else - echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6 -fi -test -z "$LD" && { { echo "$as_me:$LINENO: error: no acceptable ld found in \$PATH" >&5 -echo "$as_me: error: no acceptable ld found in \$PATH" >&2;} - { (exit 1); exit 1; }; } -echo "$as_me:$LINENO: checking if the linker ($LD) is GNU ld" >&5 -echo $ECHO_N "checking if the linker ($LD) is GNU ld... $ECHO_C" >&6 -if test "${lt_cv_prog_gnu_ld+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - # I'd rather use --version here, but apparently some GNU lds only accept -v. -case `$LD -v 2>&1 &5 -echo "${ECHO_T}$lt_cv_prog_gnu_ld" >&6 -with_gnu_ld=$lt_cv_prog_gnu_ld - - -echo "$as_me:$LINENO: checking for $LD option to reload object files" >&5 -echo $ECHO_N "checking for $LD option to reload object files... $ECHO_C" >&6 -if test "${lt_cv_ld_reload_flag+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - lt_cv_ld_reload_flag='-r' -fi -echo "$as_me:$LINENO: result: $lt_cv_ld_reload_flag" >&5 -echo "${ECHO_T}$lt_cv_ld_reload_flag" >&6 -reload_flag=$lt_cv_ld_reload_flag -case $reload_flag in -"" | " "*) ;; -*) reload_flag=" $reload_flag" ;; -esac -reload_cmds='$LD$reload_flag -o $output$reload_objs' -case $host_os in - darwin*) - if test "$GCC" = yes; then - reload_cmds='$LTCC $LTCFLAGS -nostdlib ${wl}-r -o $output$reload_objs' - else - reload_cmds='$LD$reload_flag -o $output$reload_objs' - fi - ;; -esac - -echo "$as_me:$LINENO: checking for BSD-compatible nm" >&5 -echo $ECHO_N "checking for BSD-compatible nm... $ECHO_C" >&6 -if test "${lt_cv_path_NM+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test -n "$NM"; then - # Let the user override the test. - lt_cv_path_NM="$NM" -else - lt_nm_to_check="${ac_tool_prefix}nm" - if test -n "$ac_tool_prefix" && test "$build" = "$host"; then - lt_nm_to_check="$lt_nm_to_check nm" - fi - for lt_tmp_nm in $lt_nm_to_check; do - lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR - for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do - IFS="$lt_save_ifs" - test -z "$ac_dir" && ac_dir=. - tmp_nm="$ac_dir/$lt_tmp_nm" - if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext" ; then - # Check to see if the nm accepts a BSD-compat flag. - # Adding the `sed 1q' prevents false positives on HP-UX, which says: - # nm: unknown option "B" ignored - # Tru64's nm complains that /dev/null is an invalid object file - case `"$tmp_nm" -B /dev/null 2>&1 | sed '1q'` in - */dev/null* | *'Invalid file or object type'*) - lt_cv_path_NM="$tmp_nm -B" - break - ;; - *) - case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in - */dev/null*) - lt_cv_path_NM="$tmp_nm -p" - break - ;; - *) - lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but - continue # so that we can try to find one that supports BSD flags - ;; - esac - ;; - esac - fi - done - IFS="$lt_save_ifs" - done - test -z "$lt_cv_path_NM" && lt_cv_path_NM=nm -fi -fi -echo "$as_me:$LINENO: result: $lt_cv_path_NM" >&5 -echo "${ECHO_T}$lt_cv_path_NM" >&6 -NM="$lt_cv_path_NM" - -echo "$as_me:$LINENO: checking whether ln -s works" >&5 -echo $ECHO_N "checking whether ln -s works... $ECHO_C" >&6 -LN_S=$as_ln_s -if test "$LN_S" = "ln -s"; then - echo "$as_me:$LINENO: result: yes" >&5 -echo "${ECHO_T}yes" >&6 -else - echo "$as_me:$LINENO: result: no, using $LN_S" >&5 -echo "${ECHO_T}no, using $LN_S" >&6 -fi - -echo "$as_me:$LINENO: checking how to recognise dependent libraries" >&5 -echo $ECHO_N "checking how to recognise dependent libraries... $ECHO_C" >&6 -if test "${lt_cv_deplibs_check_method+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - lt_cv_file_magic_cmd='$MAGIC_CMD' -lt_cv_file_magic_test_file= -lt_cv_deplibs_check_method='unknown' -# Need to set the preceding variable on all platforms that support -# interlibrary dependencies. -# 'none' -- dependencies not supported. -# `unknown' -- same as none, but documents that we really don't know. -# 'pass_all' -- all dependencies passed with no checks. -# 'test_compile' -- check by making test program. -# 'file_magic [[regex]]' -- check by looking for files in library path -# which responds to the $file_magic_cmd with a given extended regex. -# If you have `file' or equivalent on your system and you're not sure -# whether `pass_all' will *always* work, you probably want this one. - -case $host_os in -aix4* | aix5*) - lt_cv_deplibs_check_method=pass_all - ;; - -beos*) - lt_cv_deplibs_check_method=pass_all - ;; - -bsdi[45]*) - lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib)' - lt_cv_file_magic_cmd='/usr/bin/file -L' - lt_cv_file_magic_test_file=/shlib/libc.so - ;; - -cygwin*) - # func_win32_libid is a shell function defined in ltmain.sh - lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' - lt_cv_file_magic_cmd='func_win32_libid' - ;; - -mingw* | pw32*) - # Base MSYS/MinGW do not provide the 'file' command needed by - # func_win32_libid shell function, so use a weaker test based on 'objdump'. - lt_cv_deplibs_check_method='file_magic file format pei*-i386(.*architecture: i386)?' - lt_cv_file_magic_cmd='$OBJDUMP -f' - ;; - -darwin* | rhapsody*) - lt_cv_deplibs_check_method=pass_all - ;; - -freebsd* | kfreebsd*-gnu | dragonfly*) - if echo __ELF__ | $CC -E - | grep __ELF__ > /dev/null; then - case $host_cpu in - i*86 ) - # Not sure whether the presence of OpenBSD here was a mistake. - # Let's accept both of them until this is cleared up. - lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[3-9]86 (compact )?demand paged shared library' - lt_cv_file_magic_cmd=/usr/bin/file - lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*` - ;; - esac - else - lt_cv_deplibs_check_method=pass_all - fi - ;; - -gnu*) - lt_cv_deplibs_check_method=pass_all - ;; - -hpux10.20* | hpux11*) - lt_cv_file_magic_cmd=/usr/bin/file - case $host_cpu in - ia64*) - lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - IA64' - lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so - ;; - hppa*64*) - lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - PA-RISC [0-9].[0-9]' - lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl - ;; - *) - lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|PA-RISC[0-9].[0-9]) shared library' - lt_cv_file_magic_test_file=/usr/lib/libc.sl - ;; - esac - ;; - -interix3*) - # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here - lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|\.a)$' - ;; - -irix5* | irix6* | nonstopux*) - case $LD in - *-32|*"-32 ") libmagic=32-bit;; - *-n32|*"-n32 ") libmagic=N32;; - *-64|*"-64 ") libmagic=64-bit;; - *) libmagic=never-match;; - esac - lt_cv_deplibs_check_method=pass_all - ;; - -# This must be Linux ELF. -linux*) - lt_cv_deplibs_check_method=pass_all - ;; - -netbsd*) - if echo __ELF__ | $CC -E - | grep __ELF__ > /dev/null; then - lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$' - else - lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|_pic\.a)$' - fi - ;; - -newos6*) - lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (executable|dynamic lib)' - lt_cv_file_magic_cmd=/usr/bin/file - lt_cv_file_magic_test_file=/usr/lib/libnls.so - ;; - -nto-qnx*) - lt_cv_deplibs_check_method=unknown - ;; - -openbsd*) - if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then - lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|\.so|_pic\.a)$' - else - lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$' - fi - ;; - -osf3* | osf4* | osf5*) - lt_cv_deplibs_check_method=pass_all - ;; - -solaris*) - lt_cv_deplibs_check_method=pass_all - ;; - -sysv4 | sysv4.3*) - case $host_vendor in - motorola) - lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib) M[0-9][0-9]* Version [0-9]' - lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*` - ;; - ncr) - lt_cv_deplibs_check_method=pass_all - ;; - sequent) - lt_cv_file_magic_cmd='/bin/file' - lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [LM]SB (shared object|dynamic lib )' - ;; - sni) - lt_cv_file_magic_cmd='/bin/file' - lt_cv_deplibs_check_method="file_magic ELF [0-9][0-9]*-bit [LM]SB dynamic lib" - lt_cv_file_magic_test_file=/lib/libc.so - ;; - siemens) - lt_cv_deplibs_check_method=pass_all - ;; - pc) - lt_cv_deplibs_check_method=pass_all - ;; - esac - ;; - -sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) - lt_cv_deplibs_check_method=pass_all - ;; -esac - -fi -echo "$as_me:$LINENO: result: $lt_cv_deplibs_check_method" >&5 -echo "${ECHO_T}$lt_cv_deplibs_check_method" >&6 -file_magic_cmd=$lt_cv_file_magic_cmd -deplibs_check_method=$lt_cv_deplibs_check_method -test -z "$deplibs_check_method" && deplibs_check_method=unknown - - - - -# If no C compiler was specified, use CC. -LTCC=${LTCC-"$CC"} - -# If no C compiler flags were specified, use CFLAGS. -LTCFLAGS=${LTCFLAGS-"$CFLAGS"} - -# Allow CC to be a program name with arguments. -compiler=$CC - - -# Check whether --enable-libtool-lock or --disable-libtool-lock was given. -if test "${enable_libtool_lock+set}" = set; then - enableval="$enable_libtool_lock" - -fi; -test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes - -# Some flags need to be propagated to the compiler or linker for good -# libtool support. -case $host in -ia64-*-hpux*) - # Find out which ABI we are using. - echo 'int i;' > conftest.$ac_ext - if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; then - case `/usr/bin/file conftest.$ac_objext` in - *ELF-32*) - HPUX_IA64_MODE="32" - ;; - *ELF-64*) - HPUX_IA64_MODE="64" - ;; - esac - fi - rm -rf conftest* - ;; -*-*-irix6*) - # Find out which ABI we are using. - echo '#line 3564 "configure"' > conftest.$ac_ext - if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; then - if test "$lt_cv_prog_gnu_ld" = yes; then - case `/usr/bin/file conftest.$ac_objext` in - *32-bit*) - LD="${LD-ld} -melf32bsmip" - ;; - *N32*) - LD="${LD-ld} -melf32bmipn32" - ;; - *64-bit*) - LD="${LD-ld} -melf64bmip" - ;; - esac - else - case `/usr/bin/file conftest.$ac_objext` in - *32-bit*) - LD="${LD-ld} -32" - ;; - *N32*) - LD="${LD-ld} -n32" - ;; - *64-bit*) - LD="${LD-ld} -64" - ;; - esac - fi - fi - rm -rf conftest* - ;; - -x86_64-*linux*|ppc*-*linux*|powerpc*-*linux*|s390*-*linux*|sparc*-*linux*) - # Find out which ABI we are using. - echo 'int i;' > conftest.$ac_ext - if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; then - case `/usr/bin/file conftest.o` in - *32-bit*) - case $host in - x86_64-*linux*) - LD="${LD-ld} -m elf_i386" - ;; - ppc64-*linux*|powerpc64-*linux*) - LD="${LD-ld} -m elf32ppclinux" - ;; - s390x-*linux*) - LD="${LD-ld} -m elf_s390" - ;; - sparc64-*linux*) - LD="${LD-ld} -m elf32_sparc" - ;; - esac - ;; - *64-bit*) - case $host in - x86_64-*linux*) - LD="${LD-ld} -m elf_x86_64" - ;; - ppc*-*linux*|powerpc*-*linux*) - LD="${LD-ld} -m elf64ppc" - ;; - s390*-*linux*) - LD="${LD-ld} -m elf64_s390" - ;; - sparc*-*linux*) - LD="${LD-ld} -m elf64_sparc" - ;; - esac - ;; - esac - fi - rm -rf conftest* - ;; - -*-*-sco3.2v5*) - # On SCO OpenServer 5, we need -belf to get full-featured binaries. - SAVE_CFLAGS="$CFLAGS" - CFLAGS="$CFLAGS -belf" - echo "$as_me:$LINENO: checking whether the C compiler needs -belf" >&5 -echo $ECHO_N "checking whether the C compiler needs -belf... $ECHO_C" >&6 -if test "${lt_cv_cc_needs_belf+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 - (eval $ac_link) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest$ac_exeext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - lt_cv_cc_needs_belf=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -lt_cv_cc_needs_belf=no -fi -rm -f conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext - ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - -fi -echo "$as_me:$LINENO: result: $lt_cv_cc_needs_belf" >&5 -echo "${ECHO_T}$lt_cv_cc_needs_belf" >&6 - if test x"$lt_cv_cc_needs_belf" != x"yes"; then - # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf - CFLAGS="$SAVE_CFLAGS" - fi - ;; -sparc*-*solaris*) - # Find out which ABI we are using. - echo 'int i;' > conftest.$ac_ext - if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; then - case `/usr/bin/file conftest.o` in - *64-bit*) - case $lt_cv_prog_gnu_ld in - yes*) LD="${LD-ld} -m elf64_sparc" ;; - *) LD="${LD-ld} -64" ;; - esac - ;; - esac - fi - rm -rf conftest* - ;; - -*-*-cygwin* | *-*-mingw* | *-*-pw32*) - if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}dlltool", so it can be a program name with args. -set dummy ${ac_tool_prefix}dlltool; ac_word=$2 -echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 -if test "${ac_cv_prog_DLLTOOL+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test -n "$DLLTOOL"; then - ac_cv_prog_DLLTOOL="$DLLTOOL" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_DLLTOOL="${ac_tool_prefix}dlltool" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done -done - -fi -fi -DLLTOOL=$ac_cv_prog_DLLTOOL -if test -n "$DLLTOOL"; then - echo "$as_me:$LINENO: result: $DLLTOOL" >&5 -echo "${ECHO_T}$DLLTOOL" >&6 -else - echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6 -fi - -fi -if test -z "$ac_cv_prog_DLLTOOL"; then - ac_ct_DLLTOOL=$DLLTOOL - # Extract the first word of "dlltool", so it can be a program name with args. -set dummy dlltool; ac_word=$2 -echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 -if test "${ac_cv_prog_ac_ct_DLLTOOL+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test -n "$ac_ct_DLLTOOL"; then - ac_cv_prog_ac_ct_DLLTOOL="$ac_ct_DLLTOOL" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_DLLTOOL="dlltool" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done -done - - test -z "$ac_cv_prog_ac_ct_DLLTOOL" && ac_cv_prog_ac_ct_DLLTOOL="false" -fi -fi -ac_ct_DLLTOOL=$ac_cv_prog_ac_ct_DLLTOOL -if test -n "$ac_ct_DLLTOOL"; then - echo "$as_me:$LINENO: result: $ac_ct_DLLTOOL" >&5 -echo "${ECHO_T}$ac_ct_DLLTOOL" >&6 -else - echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6 -fi - - DLLTOOL=$ac_ct_DLLTOOL -else - DLLTOOL="$ac_cv_prog_DLLTOOL" -fi - - if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}as", so it can be a program name with args. -set dummy ${ac_tool_prefix}as; ac_word=$2 -echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 -if test "${ac_cv_prog_AS+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test -n "$AS"; then - ac_cv_prog_AS="$AS" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_AS="${ac_tool_prefix}as" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done -done - -fi -fi -AS=$ac_cv_prog_AS -if test -n "$AS"; then - echo "$as_me:$LINENO: result: $AS" >&5 -echo "${ECHO_T}$AS" >&6 -else - echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6 -fi - -fi -if test -z "$ac_cv_prog_AS"; then - ac_ct_AS=$AS - # Extract the first word of "as", so it can be a program name with args. -set dummy as; ac_word=$2 -echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 -if test "${ac_cv_prog_ac_ct_AS+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test -n "$ac_ct_AS"; then - ac_cv_prog_ac_ct_AS="$ac_ct_AS" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_AS="as" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done -done - - test -z "$ac_cv_prog_ac_ct_AS" && ac_cv_prog_ac_ct_AS="false" -fi -fi -ac_ct_AS=$ac_cv_prog_ac_ct_AS -if test -n "$ac_ct_AS"; then - echo "$as_me:$LINENO: result: $ac_ct_AS" >&5 -echo "${ECHO_T}$ac_ct_AS" >&6 -else - echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6 -fi - - AS=$ac_ct_AS -else - AS="$ac_cv_prog_AS" -fi - - if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}objdump", so it can be a program name with args. -set dummy ${ac_tool_prefix}objdump; ac_word=$2 -echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 -if test "${ac_cv_prog_OBJDUMP+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test -n "$OBJDUMP"; then - ac_cv_prog_OBJDUMP="$OBJDUMP" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_OBJDUMP="${ac_tool_prefix}objdump" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done -done - -fi -fi -OBJDUMP=$ac_cv_prog_OBJDUMP -if test -n "$OBJDUMP"; then - echo "$as_me:$LINENO: result: $OBJDUMP" >&5 -echo "${ECHO_T}$OBJDUMP" >&6 -else - echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6 -fi - -fi -if test -z "$ac_cv_prog_OBJDUMP"; then - ac_ct_OBJDUMP=$OBJDUMP - # Extract the first word of "objdump", so it can be a program name with args. -set dummy objdump; ac_word=$2 -echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 -if test "${ac_cv_prog_ac_ct_OBJDUMP+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test -n "$ac_ct_OBJDUMP"; then - ac_cv_prog_ac_ct_OBJDUMP="$ac_ct_OBJDUMP" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_OBJDUMP="objdump" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done -done - - test -z "$ac_cv_prog_ac_ct_OBJDUMP" && ac_cv_prog_ac_ct_OBJDUMP="false" -fi -fi -ac_ct_OBJDUMP=$ac_cv_prog_ac_ct_OBJDUMP -if test -n "$ac_ct_OBJDUMP"; then - echo "$as_me:$LINENO: result: $ac_ct_OBJDUMP" >&5 -echo "${ECHO_T}$ac_ct_OBJDUMP" >&6 -else - echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6 -fi - - OBJDUMP=$ac_ct_OBJDUMP -else - OBJDUMP="$ac_cv_prog_OBJDUMP" -fi - - ;; - -esac - -need_locks="$enable_libtool_lock" - - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu -echo "$as_me:$LINENO: checking how to run the C preprocessor" >&5 -echo $ECHO_N "checking how to run the C preprocessor... $ECHO_C" >&6 -# On Suns, sometimes $CPP names a directory. -if test -n "$CPP" && test -d "$CPP"; then - CPP= -fi -if test -z "$CPP"; then - if test "${ac_cv_prog_CPP+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - # Double quotes because CPP needs to be expanded - for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" - do - ac_preproc_ok=false -for ac_c_preproc_warn_flag in '' yes -do - # Use a header file that comes with gcc, so configuring glibc - # with a fresh cross-compiler works. - # Prefer to if __STDC__ is defined, since - # exists even on freestanding compilers. - # On the NeXT, cc -E runs the code through the compiler's parser, - # not just through cpp. "Syntax error" is here to catch this case. - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#ifdef __STDC__ -# include -#else -# include -#endif - Syntax error -_ACEOF -if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 - (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } >/dev/null; then - if test -s conftest.err; then - ac_cpp_err=$ac_c_preproc_warn_flag - ac_cpp_err=$ac_cpp_err$ac_c_werror_flag - else - ac_cpp_err= - fi -else - ac_cpp_err=yes -fi -if test -z "$ac_cpp_err"; then - : -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - # Broken: fails on valid input. -continue -fi -rm -f conftest.err conftest.$ac_ext - - # OK, works on sane cases. Now check whether non-existent headers - # can be detected and how. - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include -_ACEOF -if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 - (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } >/dev/null; then - if test -s conftest.err; then - ac_cpp_err=$ac_c_preproc_warn_flag - ac_cpp_err=$ac_cpp_err$ac_c_werror_flag - else - ac_cpp_err= - fi -else - ac_cpp_err=yes -fi -if test -z "$ac_cpp_err"; then - # Broken: success on invalid input. -continue -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - # Passes both tests. -ac_preproc_ok=: -break -fi -rm -f conftest.err conftest.$ac_ext - -done -# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. -rm -f conftest.err conftest.$ac_ext -if $ac_preproc_ok; then - break -fi - - done - ac_cv_prog_CPP=$CPP - -fi - CPP=$ac_cv_prog_CPP -else - ac_cv_prog_CPP=$CPP -fi -echo "$as_me:$LINENO: result: $CPP" >&5 -echo "${ECHO_T}$CPP" >&6 -ac_preproc_ok=false -for ac_c_preproc_warn_flag in '' yes -do - # Use a header file that comes with gcc, so configuring glibc - # with a fresh cross-compiler works. - # Prefer to if __STDC__ is defined, since - # exists even on freestanding compilers. - # On the NeXT, cc -E runs the code through the compiler's parser, - # not just through cpp. "Syntax error" is here to catch this case. - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#ifdef __STDC__ -# include -#else -# include -#endif - Syntax error -_ACEOF -if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 - (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } >/dev/null; then - if test -s conftest.err; then - ac_cpp_err=$ac_c_preproc_warn_flag - ac_cpp_err=$ac_cpp_err$ac_c_werror_flag - else - ac_cpp_err= - fi -else - ac_cpp_err=yes -fi -if test -z "$ac_cpp_err"; then - : -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - # Broken: fails on valid input. -continue -fi -rm -f conftest.err conftest.$ac_ext - - # OK, works on sane cases. Now check whether non-existent headers - # can be detected and how. - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include -_ACEOF -if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 - (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } >/dev/null; then - if test -s conftest.err; then - ac_cpp_err=$ac_c_preproc_warn_flag - ac_cpp_err=$ac_cpp_err$ac_c_werror_flag - else - ac_cpp_err= - fi -else - ac_cpp_err=yes -fi -if test -z "$ac_cpp_err"; then - # Broken: success on invalid input. -continue -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - # Passes both tests. -ac_preproc_ok=: -break -fi -rm -f conftest.err conftest.$ac_ext - -done -# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. -rm -f conftest.err conftest.$ac_ext -if $ac_preproc_ok; then - : -else - { { echo "$as_me:$LINENO: error: C preprocessor \"$CPP\" fails sanity check -See \`config.log' for more details." >&5 -echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check -See \`config.log' for more details." >&2;} - { (exit 1); exit 1; }; } -fi - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - - -echo "$as_me:$LINENO: checking for ANSI C header files" >&5 -echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6 -if test "${ac_cv_header_stdc+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include -#include -#include -#include - -int -main () -{ - - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext -if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest.$ac_objext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_cv_header_stdc=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -ac_cv_header_stdc=no -fi -rm -f conftest.err conftest.$ac_objext conftest.$ac_ext - -if test $ac_cv_header_stdc = yes; then - # SunOS 4.x string.h does not declare mem*, contrary to ANSI. - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include - -_ACEOF -if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | - $EGREP "memchr" >/dev/null 2>&1; then - : -else - ac_cv_header_stdc=no -fi -rm -f conftest* - -fi - -if test $ac_cv_header_stdc = yes; then - # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include - -_ACEOF -if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | - $EGREP "free" >/dev/null 2>&1; then - : -else - ac_cv_header_stdc=no -fi -rm -f conftest* - -fi - -if test $ac_cv_header_stdc = yes; then - # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. - if test "$cross_compiling" = yes; then - : -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include -#if ((' ' & 0x0FF) == 0x020) -# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') -# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) -#else -# define ISLOWER(c) \ - (('a' <= (c) && (c) <= 'i') \ - || ('j' <= (c) && (c) <= 'r') \ - || ('s' <= (c) && (c) <= 'z')) -# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) -#endif - -#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) -int -main () -{ - int i; - for (i = 0; i < 256; i++) - if (XOR (islower (i), ISLOWER (i)) - || toupper (i) != TOUPPER (i)) - exit(2); - exit (0); -} -_ACEOF -rm -f conftest$ac_exeext -if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 - (eval $ac_link) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { ac_try='./conftest$ac_exeext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - : -else - echo "$as_me: program exited with status $ac_status" >&5 -echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -( exit $ac_status ) -ac_cv_header_stdc=no -fi -rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext -fi -fi -fi -echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5 -echo "${ECHO_T}$ac_cv_header_stdc" >&6 -if test $ac_cv_header_stdc = yes; then - -cat >>confdefs.h <<\_ACEOF -#define STDC_HEADERS 1 -_ACEOF - -fi - -# On IRIX 5.3, sys/types and inttypes.h are conflicting. - - - - - - - - - -for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ - inttypes.h stdint.h unistd.h -do -as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` -echo "$as_me:$LINENO: checking for $ac_header" >&5 -echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 -if eval "test \"\${$as_ac_Header+set}\" = set"; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -$ac_includes_default - -#include <$ac_header> -_ACEOF -rm -f conftest.$ac_objext -if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest.$ac_objext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - eval "$as_ac_Header=yes" -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -eval "$as_ac_Header=no" -fi -rm -f conftest.err conftest.$ac_objext conftest.$ac_ext -fi -echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 -echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 -if test `eval echo '${'$as_ac_Header'}'` = yes; then - cat >>confdefs.h <<_ACEOF -#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 -_ACEOF - -fi - -done - - - -for ac_header in dlfcn.h -do -as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` -if eval "test \"\${$as_ac_Header+set}\" = set"; then - echo "$as_me:$LINENO: checking for $ac_header" >&5 -echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 -if eval "test \"\${$as_ac_Header+set}\" = set"; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -fi -echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 -echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 -else - # Is the header compilable? -echo "$as_me:$LINENO: checking $ac_header usability" >&5 -echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -$ac_includes_default -#include <$ac_header> -_ACEOF -rm -f conftest.$ac_objext -if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest.$ac_objext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_header_compiler=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -ac_header_compiler=no -fi -rm -f conftest.err conftest.$ac_objext conftest.$ac_ext -echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 -echo "${ECHO_T}$ac_header_compiler" >&6 - -# Is the header present? -echo "$as_me:$LINENO: checking $ac_header presence" >&5 -echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include <$ac_header> -_ACEOF -if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 - (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } >/dev/null; then - if test -s conftest.err; then - ac_cpp_err=$ac_c_preproc_warn_flag - ac_cpp_err=$ac_cpp_err$ac_c_werror_flag - else - ac_cpp_err= - fi -else - ac_cpp_err=yes -fi -if test -z "$ac_cpp_err"; then - ac_header_preproc=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_header_preproc=no -fi -rm -f conftest.err conftest.$ac_ext -echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 -echo "${ECHO_T}$ac_header_preproc" >&6 - -# So? What about this header? -case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in - yes:no: ) - { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 -echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 -echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} - ac_header_preproc=yes - ;; - no:yes:* ) - { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 -echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 -echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 -echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 -echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 -echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 -echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} - ( - cat <<\_ASBOX -## -------------------------------------- ## -## Report this to expat-bugs@libexpat.org ## -## -------------------------------------- ## -_ASBOX - ) | - sed "s/^/$as_me: WARNING: /" >&2 - ;; -esac -echo "$as_me:$LINENO: checking for $ac_header" >&5 -echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 -if eval "test \"\${$as_ac_Header+set}\" = set"; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - eval "$as_ac_Header=\$ac_header_preproc" -fi -echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 -echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 - -fi -if test `eval echo '${'$as_ac_Header'}'` = yes; then - cat >>confdefs.h <<_ACEOF -#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 -_ACEOF - -fi - -done - -ac_ext=cc -ac_cpp='$CXXCPP $CPPFLAGS' -ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_cxx_compiler_gnu -if test -n "$ac_tool_prefix"; then - for ac_prog in $CCC g++ c++ gpp aCC CC cxx cc++ cl FCC KCC RCC xlC_r xlC - do - # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. -set dummy $ac_tool_prefix$ac_prog; ac_word=$2 -echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 -if test "${ac_cv_prog_CXX+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test -n "$CXX"; then - ac_cv_prog_CXX="$CXX" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_CXX="$ac_tool_prefix$ac_prog" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done -done - -fi -fi -CXX=$ac_cv_prog_CXX -if test -n "$CXX"; then - echo "$as_me:$LINENO: result: $CXX" >&5 -echo "${ECHO_T}$CXX" >&6 -else - echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6 -fi - - test -n "$CXX" && break - done -fi -if test -z "$CXX"; then - ac_ct_CXX=$CXX - for ac_prog in $CCC g++ c++ gpp aCC CC cxx cc++ cl FCC KCC RCC xlC_r xlC -do - # Extract the first word of "$ac_prog", so it can be a program name with args. -set dummy $ac_prog; ac_word=$2 -echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 -if test "${ac_cv_prog_ac_ct_CXX+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test -n "$ac_ct_CXX"; then - ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_CXX="$ac_prog" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done -done - -fi -fi -ac_ct_CXX=$ac_cv_prog_ac_ct_CXX -if test -n "$ac_ct_CXX"; then - echo "$as_me:$LINENO: result: $ac_ct_CXX" >&5 -echo "${ECHO_T}$ac_ct_CXX" >&6 -else - echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6 -fi - - test -n "$ac_ct_CXX" && break -done -test -n "$ac_ct_CXX" || ac_ct_CXX="g++" - - CXX=$ac_ct_CXX -fi - - -# Provide some information about the compiler. -echo "$as_me:$LINENO:" \ - "checking for C++ compiler version" >&5 -ac_compiler=`set X $ac_compile; echo $2` -{ (eval echo "$as_me:$LINENO: \"$ac_compiler --version &5\"") >&5 - (eval $ac_compiler --version &5) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } -{ (eval echo "$as_me:$LINENO: \"$ac_compiler -v &5\"") >&5 - (eval $ac_compiler -v &5) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } -{ (eval echo "$as_me:$LINENO: \"$ac_compiler -V &5\"") >&5 - (eval $ac_compiler -V &5) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } - -echo "$as_me:$LINENO: checking whether we are using the GNU C++ compiler" >&5 -echo $ECHO_N "checking whether we are using the GNU C++ compiler... $ECHO_C" >&6 -if test "${ac_cv_cxx_compiler_gnu+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ - -int -main () -{ -#ifndef __GNUC__ - choke me -#endif - - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext -if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_cxx_werror_flag" - || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest.$ac_objext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_compiler_gnu=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -ac_compiler_gnu=no -fi -rm -f conftest.err conftest.$ac_objext conftest.$ac_ext -ac_cv_cxx_compiler_gnu=$ac_compiler_gnu - -fi -echo "$as_me:$LINENO: result: $ac_cv_cxx_compiler_gnu" >&5 -echo "${ECHO_T}$ac_cv_cxx_compiler_gnu" >&6 -GXX=`test $ac_compiler_gnu = yes && echo yes` -ac_test_CXXFLAGS=${CXXFLAGS+set} -ac_save_CXXFLAGS=$CXXFLAGS -CXXFLAGS="-g" -echo "$as_me:$LINENO: checking whether $CXX accepts -g" >&5 -echo $ECHO_N "checking whether $CXX accepts -g... $ECHO_C" >&6 -if test "${ac_cv_prog_cxx_g+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext -if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_cxx_werror_flag" - || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest.$ac_objext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_cv_prog_cxx_g=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -ac_cv_prog_cxx_g=no -fi -rm -f conftest.err conftest.$ac_objext conftest.$ac_ext -fi -echo "$as_me:$LINENO: result: $ac_cv_prog_cxx_g" >&5 -echo "${ECHO_T}$ac_cv_prog_cxx_g" >&6 -if test "$ac_test_CXXFLAGS" = set; then - CXXFLAGS=$ac_save_CXXFLAGS -elif test $ac_cv_prog_cxx_g = yes; then - if test "$GXX" = yes; then - CXXFLAGS="-g -O2" - else - CXXFLAGS="-g" - fi -else - if test "$GXX" = yes; then - CXXFLAGS="-O2" - else - CXXFLAGS= - fi -fi -for ac_declaration in \ - '' \ - 'extern "C" void std::exit (int) throw (); using std::exit;' \ - 'extern "C" void std::exit (int); using std::exit;' \ - 'extern "C" void exit (int) throw ();' \ - 'extern "C" void exit (int);' \ - 'void exit (int);' -do - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -$ac_declaration -#include -int -main () -{ -exit (42); - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext -if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_cxx_werror_flag" - || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest.$ac_objext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - : -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -continue -fi -rm -f conftest.err conftest.$ac_objext conftest.$ac_ext - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -$ac_declaration -int -main () -{ -exit (42); - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext -if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_cxx_werror_flag" - || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest.$ac_objext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - break -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -fi -rm -f conftest.err conftest.$ac_objext conftest.$ac_ext -done -rm -f conftest* -if test -n "$ac_declaration"; then - echo '#ifdef __cplusplus' >>confdefs.h - echo $ac_declaration >>confdefs.h - echo '#endif' >>confdefs.h -fi - -ac_ext=cc -ac_cpp='$CXXCPP $CPPFLAGS' -ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_cxx_compiler_gnu - - - -if test -n "$CXX" && ( test "X$CXX" != "Xno" && - ( (test "X$CXX" = "Xg++" && `g++ -v >/dev/null 2>&1` ) || - (test "X$CXX" != "Xg++"))) ; then - ac_ext=cc -ac_cpp='$CXXCPP $CPPFLAGS' -ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_cxx_compiler_gnu -echo "$as_me:$LINENO: checking how to run the C++ preprocessor" >&5 -echo $ECHO_N "checking how to run the C++ preprocessor... $ECHO_C" >&6 -if test -z "$CXXCPP"; then - if test "${ac_cv_prog_CXXCPP+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - # Double quotes because CXXCPP needs to be expanded - for CXXCPP in "$CXX -E" "/lib/cpp" - do - ac_preproc_ok=false -for ac_cxx_preproc_warn_flag in '' yes -do - # Use a header file that comes with gcc, so configuring glibc - # with a fresh cross-compiler works. - # Prefer to if __STDC__ is defined, since - # exists even on freestanding compilers. - # On the NeXT, cc -E runs the code through the compiler's parser, - # not just through cpp. "Syntax error" is here to catch this case. - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#ifdef __STDC__ -# include -#else -# include -#endif - Syntax error -_ACEOF -if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 - (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } >/dev/null; then - if test -s conftest.err; then - ac_cpp_err=$ac_cxx_preproc_warn_flag - ac_cpp_err=$ac_cpp_err$ac_cxx_werror_flag - else - ac_cpp_err= - fi -else - ac_cpp_err=yes -fi -if test -z "$ac_cpp_err"; then - : -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - # Broken: fails on valid input. -continue -fi -rm -f conftest.err conftest.$ac_ext - - # OK, works on sane cases. Now check whether non-existent headers - # can be detected and how. - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include -_ACEOF -if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 - (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } >/dev/null; then - if test -s conftest.err; then - ac_cpp_err=$ac_cxx_preproc_warn_flag - ac_cpp_err=$ac_cpp_err$ac_cxx_werror_flag - else - ac_cpp_err= - fi -else - ac_cpp_err=yes -fi -if test -z "$ac_cpp_err"; then - # Broken: success on invalid input. -continue -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - # Passes both tests. -ac_preproc_ok=: -break -fi -rm -f conftest.err conftest.$ac_ext - -done -# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. -rm -f conftest.err conftest.$ac_ext -if $ac_preproc_ok; then - break -fi - - done - ac_cv_prog_CXXCPP=$CXXCPP - -fi - CXXCPP=$ac_cv_prog_CXXCPP -else - ac_cv_prog_CXXCPP=$CXXCPP -fi -echo "$as_me:$LINENO: result: $CXXCPP" >&5 -echo "${ECHO_T}$CXXCPP" >&6 -ac_preproc_ok=false -for ac_cxx_preproc_warn_flag in '' yes -do - # Use a header file that comes with gcc, so configuring glibc - # with a fresh cross-compiler works. - # Prefer to if __STDC__ is defined, since - # exists even on freestanding compilers. - # On the NeXT, cc -E runs the code through the compiler's parser, - # not just through cpp. "Syntax error" is here to catch this case. - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#ifdef __STDC__ -# include -#else -# include -#endif - Syntax error -_ACEOF -if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 - (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } >/dev/null; then - if test -s conftest.err; then - ac_cpp_err=$ac_cxx_preproc_warn_flag - ac_cpp_err=$ac_cpp_err$ac_cxx_werror_flag - else - ac_cpp_err= - fi -else - ac_cpp_err=yes -fi -if test -z "$ac_cpp_err"; then - : -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - # Broken: fails on valid input. -continue -fi -rm -f conftest.err conftest.$ac_ext - - # OK, works on sane cases. Now check whether non-existent headers - # can be detected and how. - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include -_ACEOF -if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 - (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } >/dev/null; then - if test -s conftest.err; then - ac_cpp_err=$ac_cxx_preproc_warn_flag - ac_cpp_err=$ac_cpp_err$ac_cxx_werror_flag - else - ac_cpp_err= - fi -else - ac_cpp_err=yes -fi -if test -z "$ac_cpp_err"; then - # Broken: success on invalid input. -continue -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - # Passes both tests. -ac_preproc_ok=: -break -fi -rm -f conftest.err conftest.$ac_ext - -done -# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. -rm -f conftest.err conftest.$ac_ext -if $ac_preproc_ok; then - : -else - { { echo "$as_me:$LINENO: error: C++ preprocessor \"$CXXCPP\" fails sanity check -See \`config.log' for more details." >&5 -echo "$as_me: error: C++ preprocessor \"$CXXCPP\" fails sanity check -See \`config.log' for more details." >&2;} - { (exit 1); exit 1; }; } -fi - -ac_ext=cc -ac_cpp='$CXXCPP $CPPFLAGS' -ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_cxx_compiler_gnu - -fi - - -ac_ext=f -ac_compile='$F77 -c $FFLAGS conftest.$ac_ext >&5' -ac_link='$F77 -o conftest$ac_exeext $FFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_f77_compiler_gnu -if test -n "$ac_tool_prefix"; then - for ac_prog in g77 f77 xlf frt pgf77 fort77 fl32 af77 f90 xlf90 pgf90 epcf90 f95 fort xlf95 ifc efc pgf95 lf95 gfortran - do - # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. -set dummy $ac_tool_prefix$ac_prog; ac_word=$2 -echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 -if test "${ac_cv_prog_F77+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test -n "$F77"; then - ac_cv_prog_F77="$F77" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_F77="$ac_tool_prefix$ac_prog" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done -done - -fi -fi -F77=$ac_cv_prog_F77 -if test -n "$F77"; then - echo "$as_me:$LINENO: result: $F77" >&5 -echo "${ECHO_T}$F77" >&6 -else - echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6 -fi - - test -n "$F77" && break - done -fi -if test -z "$F77"; then - ac_ct_F77=$F77 - for ac_prog in g77 f77 xlf frt pgf77 fort77 fl32 af77 f90 xlf90 pgf90 epcf90 f95 fort xlf95 ifc efc pgf95 lf95 gfortran -do - # Extract the first word of "$ac_prog", so it can be a program name with args. -set dummy $ac_prog; ac_word=$2 -echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 -if test "${ac_cv_prog_ac_ct_F77+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test -n "$ac_ct_F77"; then - ac_cv_prog_ac_ct_F77="$ac_ct_F77" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_F77="$ac_prog" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done -done - -fi -fi -ac_ct_F77=$ac_cv_prog_ac_ct_F77 -if test -n "$ac_ct_F77"; then - echo "$as_me:$LINENO: result: $ac_ct_F77" >&5 -echo "${ECHO_T}$ac_ct_F77" >&6 -else - echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6 -fi - - test -n "$ac_ct_F77" && break -done - - F77=$ac_ct_F77 -fi - - -# Provide some information about the compiler. -echo "$as_me:5296:" \ - "checking for Fortran 77 compiler version" >&5 -ac_compiler=`set X $ac_compile; echo $2` -{ (eval echo "$as_me:$LINENO: \"$ac_compiler --version &5\"") >&5 - (eval $ac_compiler --version &5) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } -{ (eval echo "$as_me:$LINENO: \"$ac_compiler -v &5\"") >&5 - (eval $ac_compiler -v &5) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } -{ (eval echo "$as_me:$LINENO: \"$ac_compiler -V &5\"") >&5 - (eval $ac_compiler -V &5) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } -rm -f a.out - -# If we don't use `.F' as extension, the preprocessor is not run on the -# input file. (Note that this only needs to work for GNU compilers.) -ac_save_ext=$ac_ext -ac_ext=F -echo "$as_me:$LINENO: checking whether we are using the GNU Fortran 77 compiler" >&5 -echo $ECHO_N "checking whether we are using the GNU Fortran 77 compiler... $ECHO_C" >&6 -if test "${ac_cv_f77_compiler_gnu+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - cat >conftest.$ac_ext <<_ACEOF - program main -#ifndef __GNUC__ - choke me -#endif - - end -_ACEOF -rm -f conftest.$ac_objext -if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_f77_werror_flag" - || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest.$ac_objext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_compiler_gnu=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -ac_compiler_gnu=no -fi -rm -f conftest.err conftest.$ac_objext conftest.$ac_ext -ac_cv_f77_compiler_gnu=$ac_compiler_gnu - -fi -echo "$as_me:$LINENO: result: $ac_cv_f77_compiler_gnu" >&5 -echo "${ECHO_T}$ac_cv_f77_compiler_gnu" >&6 -ac_ext=$ac_save_ext -ac_test_FFLAGS=${FFLAGS+set} -ac_save_FFLAGS=$FFLAGS -FFLAGS= -echo "$as_me:$LINENO: checking whether $F77 accepts -g" >&5 -echo $ECHO_N "checking whether $F77 accepts -g... $ECHO_C" >&6 -if test "${ac_cv_prog_f77_g+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - FFLAGS=-g -cat >conftest.$ac_ext <<_ACEOF - program main - - end -_ACEOF -rm -f conftest.$ac_objext -if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_f77_werror_flag" - || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest.$ac_objext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_cv_prog_f77_g=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -ac_cv_prog_f77_g=no -fi -rm -f conftest.err conftest.$ac_objext conftest.$ac_ext - -fi -echo "$as_me:$LINENO: result: $ac_cv_prog_f77_g" >&5 -echo "${ECHO_T}$ac_cv_prog_f77_g" >&6 -if test "$ac_test_FFLAGS" = set; then - FFLAGS=$ac_save_FFLAGS -elif test $ac_cv_prog_f77_g = yes; then - if test "x$ac_cv_f77_compiler_gnu" = xyes; then - FFLAGS="-g -O2" - else - FFLAGS="-g" - fi -else - if test "x$ac_cv_f77_compiler_gnu" = xyes; then - FFLAGS="-O2" - else - FFLAGS= - fi -fi - -G77=`test $ac_compiler_gnu = yes && echo yes` -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - - - -# Autoconf 2.13's AC_OBJEXT and AC_EXEEXT macros only works for C compilers! - -# find the maximum length of command line arguments -echo "$as_me:$LINENO: checking the maximum length of command line arguments" >&5 -echo $ECHO_N "checking the maximum length of command line arguments... $ECHO_C" >&6 -if test "${lt_cv_sys_max_cmd_len+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - i=0 - teststring="ABCD" - - case $build_os in - msdosdjgpp*) - # On DJGPP, this test can blow up pretty badly due to problems in libc - # (any single argument exceeding 2000 bytes causes a buffer overrun - # during glob expansion). Even if it were fixed, the result of this - # check would be larger than it should be. - lt_cv_sys_max_cmd_len=12288; # 12K is about right - ;; - - gnu*) - # Under GNU Hurd, this test is not required because there is - # no limit to the length of command line arguments. - # Libtool will interpret -1 as no limit whatsoever - lt_cv_sys_max_cmd_len=-1; - ;; - - cygwin* | mingw*) - # On Win9x/ME, this test blows up -- it succeeds, but takes - # about 5 minutes as the teststring grows exponentially. - # Worse, since 9x/ME are not pre-emptively multitasking, - # you end up with a "frozen" computer, even though with patience - # the test eventually succeeds (with a max line length of 256k). - # Instead, let's just punt: use the minimum linelength reported by - # all of the supported platforms: 8192 (on NT/2K/XP). - lt_cv_sys_max_cmd_len=8192; - ;; - - amigaos*) - # On AmigaOS with pdksh, this test takes hours, literally. - # So we just punt and use a minimum line length of 8192. - lt_cv_sys_max_cmd_len=8192; - ;; - - netbsd* | freebsd* | openbsd* | darwin* | dragonfly*) - # This has been around since 386BSD, at least. Likely further. - if test -x /sbin/sysctl; then - lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax` - elif test -x /usr/sbin/sysctl; then - lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax` - else - lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs - fi - # And add a safety zone - lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` - lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` - ;; - - interix*) - # We know the value 262144 and hardcode it with a safety zone (like BSD) - lt_cv_sys_max_cmd_len=196608 - ;; - - osf*) - # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure - # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not - # nice to cause kernel panics so lets avoid the loop below. - # First set a reasonable default. - lt_cv_sys_max_cmd_len=16384 - # - if test -x /sbin/sysconfig; then - case `/sbin/sysconfig -q proc exec_disable_arg_limit` in - *1*) lt_cv_sys_max_cmd_len=-1 ;; - esac - fi - ;; - sco3.2v5*) - lt_cv_sys_max_cmd_len=102400 - ;; - sysv5* | sco5v6* | sysv4.2uw2*) - kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null` - if test -n "$kargmax"; then - lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[ ]//'` - else - lt_cv_sys_max_cmd_len=32768 - fi - ;; - *) - # If test is not a shell built-in, we'll probably end up computing a - # maximum length that is only half of the actual maximum length, but - # we can't tell. - SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}} - while (test "X"`$SHELL $0 --fallback-echo "X$teststring" 2>/dev/null` \ - = "XX$teststring") >/dev/null 2>&1 && - new_result=`expr "X$teststring" : ".*" 2>&1` && - lt_cv_sys_max_cmd_len=$new_result && - test $i != 17 # 1/2 MB should be enough - do - i=`expr $i + 1` - teststring=$teststring$teststring - done - teststring= - # Add a significant safety factor because C++ compilers can tack on massive - # amounts of additional arguments before passing them to the linker. - # It appears as though 1/2 is a usable value. - lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2` - ;; - esac - -fi - -if test -n $lt_cv_sys_max_cmd_len ; then - echo "$as_me:$LINENO: result: $lt_cv_sys_max_cmd_len" >&5 -echo "${ECHO_T}$lt_cv_sys_max_cmd_len" >&6 -else - echo "$as_me:$LINENO: result: none" >&5 -echo "${ECHO_T}none" >&6 -fi - - - - -# Check for command to grab the raw symbol name followed by C symbol from nm. -echo "$as_me:$LINENO: checking command to parse $NM output from $compiler object" >&5 -echo $ECHO_N "checking command to parse $NM output from $compiler object... $ECHO_C" >&6 -if test "${lt_cv_sys_global_symbol_pipe+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - -# These are sane defaults that work on at least a few old systems. -# [They come from Ultrix. What could be older than Ultrix?!! ;)] - -# Character class describing NM global symbol codes. -symcode='[BCDEGRST]' - -# Regexp to match symbols that can be accessed directly from C. -sympat='\([_A-Za-z][_A-Za-z0-9]*\)' - -# Transform an extracted symbol line into a proper C declaration -lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^. .* \(.*\)$/extern int \1;/p'" - -# Transform an extracted symbol line into symbol name and symbol address -lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([^ ]*\) $/ {\\\"\1\\\", (lt_ptr) 0},/p' -e 's/^$symcode \([^ ]*\) \([^ ]*\)$/ {\"\2\", (lt_ptr) \&\2},/p'" - -# Define system-specific variables. -case $host_os in -aix*) - symcode='[BCDT]' - ;; -cygwin* | mingw* | pw32*) - symcode='[ABCDGISTW]' - ;; -hpux*) # Its linker distinguishes data from code symbols - if test "$host_cpu" = ia64; then - symcode='[ABCDEGRST]' - fi - lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^T .* \(.*\)$/extern int \1();/p' -e 's/^$symcode* .* \(.*\)$/extern char \1;/p'" - lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([^ ]*\) $/ {\\\"\1\\\", (lt_ptr) 0},/p' -e 's/^$symcode* \([^ ]*\) \([^ ]*\)$/ {\"\2\", (lt_ptr) \&\2},/p'" - ;; -linux*) - if test "$host_cpu" = ia64; then - symcode='[ABCDGIRSTW]' - lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^T .* \(.*\)$/extern int \1();/p' -e 's/^$symcode* .* \(.*\)$/extern char \1;/p'" - lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([^ ]*\) $/ {\\\"\1\\\", (lt_ptr) 0},/p' -e 's/^$symcode* \([^ ]*\) \([^ ]*\)$/ {\"\2\", (lt_ptr) \&\2},/p'" - fi - ;; -irix* | nonstopux*) - symcode='[BCDEGRST]' - ;; -osf*) - symcode='[BCDEGQRST]' - ;; -solaris*) - symcode='[BDRT]' - ;; -sco3.2v5*) - symcode='[DT]' - ;; -sysv4.2uw2*) - symcode='[DT]' - ;; -sysv5* | sco5v6* | unixware* | OpenUNIX*) - symcode='[ABDT]' - ;; -sysv4) - symcode='[DFNSTU]' - ;; -esac - -# Handle CRLF in mingw tool chain -opt_cr= -case $build_os in -mingw*) - opt_cr=`echo 'x\{0,1\}' | tr x '\015'` # option cr in regexp - ;; -esac - -# If we're using GNU nm, then use its standard symbol codes. -case `$NM -V 2>&1` in -*GNU* | *'with BFD'*) - symcode='[ABCDGIRSTW]' ;; -esac - -# Try without a prefix undercore, then with it. -for ac_symprfx in "" "_"; do - - # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol. - symxfrm="\\1 $ac_symprfx\\2 \\2" - - # Write the raw and C identifiers. - lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[ ]\($symcode$symcode*\)[ ][ ]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'" - - # Check to see that the pipe works correctly. - pipe_works=no - - rm -f conftest* - cat > conftest.$ac_ext <&5 - (eval $ac_compile) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; then - # Now try to grab the symbols. - nlist=conftest.nm - if { (eval echo "$as_me:$LINENO: \"$NM conftest.$ac_objext \| $lt_cv_sys_global_symbol_pipe \> $nlist\"") >&5 - (eval $NM conftest.$ac_objext \| $lt_cv_sys_global_symbol_pipe \> $nlist) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && test -s "$nlist"; then - # Try sorting and uniquifying the output. - if sort "$nlist" | uniq > "$nlist"T; then - mv -f "$nlist"T "$nlist" - else - rm -f "$nlist"T - fi - - # Make sure that we snagged all the symbols we need. - if grep ' nm_test_var$' "$nlist" >/dev/null; then - if grep ' nm_test_func$' "$nlist" >/dev/null; then - cat < conftest.$ac_ext -#ifdef __cplusplus -extern "C" { -#endif - -EOF - # Now generate the symbol file. - eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | grep -v main >> conftest.$ac_ext' - - cat <> conftest.$ac_ext -#if defined (__STDC__) && __STDC__ -# define lt_ptr_t void * -#else -# define lt_ptr_t char * -# define const -#endif - -/* The mapping between symbol names and symbols. */ -const struct { - const char *name; - lt_ptr_t address; -} -lt_preloaded_symbols[] = -{ -EOF - $SED "s/^$symcode$symcode* \(.*\) \(.*\)$/ {\"\2\", (lt_ptr_t) \&\2},/" < "$nlist" | grep -v main >> conftest.$ac_ext - cat <<\EOF >> conftest.$ac_ext - {0, (lt_ptr_t) 0} -}; - -#ifdef __cplusplus -} -#endif -EOF - # Now try linking the two files. - mv conftest.$ac_objext conftstm.$ac_objext - lt_save_LIBS="$LIBS" - lt_save_CFLAGS="$CFLAGS" - LIBS="conftstm.$ac_objext" - CFLAGS="$CFLAGS$lt_prog_compiler_no_builtin_flag" - if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 - (eval $ac_link) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && test -s conftest${ac_exeext}; then - pipe_works=yes - fi - LIBS="$lt_save_LIBS" - CFLAGS="$lt_save_CFLAGS" - else - echo "cannot find nm_test_func in $nlist" >&5 - fi - else - echo "cannot find nm_test_var in $nlist" >&5 - fi - else - echo "cannot run $lt_cv_sys_global_symbol_pipe" >&5 - fi - else - echo "$progname: failed program was:" >&5 - cat conftest.$ac_ext >&5 - fi - rm -f conftest* conftst* - - # Do not use the global_symbol_pipe unless it works. - if test "$pipe_works" = yes; then - break - else - lt_cv_sys_global_symbol_pipe= - fi -done - -fi - -if test -z "$lt_cv_sys_global_symbol_pipe"; then - lt_cv_sys_global_symbol_to_cdecl= -fi -if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then - echo "$as_me:$LINENO: result: failed" >&5 -echo "${ECHO_T}failed" >&6 -else - echo "$as_me:$LINENO: result: ok" >&5 -echo "${ECHO_T}ok" >&6 -fi - -echo "$as_me:$LINENO: checking for objdir" >&5 -echo $ECHO_N "checking for objdir... $ECHO_C" >&6 -if test "${lt_cv_objdir+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - rm -f .libs 2>/dev/null -mkdir .libs 2>/dev/null -if test -d .libs; then - lt_cv_objdir=.libs -else - # MS-DOS does not allow filenames that begin with a dot. - lt_cv_objdir=_libs -fi -rmdir .libs 2>/dev/null -fi -echo "$as_me:$LINENO: result: $lt_cv_objdir" >&5 -echo "${ECHO_T}$lt_cv_objdir" >&6 -objdir=$lt_cv_objdir - - - - - -case $host_os in -aix3*) - # AIX sometimes has problems with the GCC collect2 program. For some - # reason, if we set the COLLECT_NAMES environment variable, the problems - # vanish in a puff of smoke. - if test "X${COLLECT_NAMES+set}" != Xset; then - COLLECT_NAMES= - export COLLECT_NAMES - fi - ;; -esac - -# Sed substitution that helps us do robust quoting. It backslashifies -# metacharacters that are still active within double-quoted strings. -Xsed='sed -e 1s/^X//' -sed_quote_subst='s/\([\\"\\`$\\\\]\)/\\\1/g' - -# Same as above, but do not quote variable references. -double_quote_subst='s/\([\\"\\`\\\\]\)/\\\1/g' - -# Sed substitution to delay expansion of an escaped shell variable in a -# double_quote_subst'ed string. -delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g' - -# Sed substitution to avoid accidental globbing in evaled expressions -no_glob_subst='s/\*/\\\*/g' - -# Constants: -rm="rm -f" - -# Global variables: -default_ofile=libtool -can_build_shared=yes - -# All known linkers require a `.a' archive for static linking (except MSVC, -# which needs '.lib'). -libext=a -ltmain="$ac_aux_dir/ltmain.sh" -ofile="$default_ofile" -with_gnu_ld="$lt_cv_prog_gnu_ld" - -if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}ar", so it can be a program name with args. -set dummy ${ac_tool_prefix}ar; ac_word=$2 -echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 -if test "${ac_cv_prog_AR+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test -n "$AR"; then - ac_cv_prog_AR="$AR" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_AR="${ac_tool_prefix}ar" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done -done - -fi -fi -AR=$ac_cv_prog_AR -if test -n "$AR"; then - echo "$as_me:$LINENO: result: $AR" >&5 -echo "${ECHO_T}$AR" >&6 -else - echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6 -fi - -fi -if test -z "$ac_cv_prog_AR"; then - ac_ct_AR=$AR - # Extract the first word of "ar", so it can be a program name with args. -set dummy ar; ac_word=$2 -echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 -if test "${ac_cv_prog_ac_ct_AR+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test -n "$ac_ct_AR"; then - ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_AR="ar" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done -done - - test -z "$ac_cv_prog_ac_ct_AR" && ac_cv_prog_ac_ct_AR="false" -fi -fi -ac_ct_AR=$ac_cv_prog_ac_ct_AR -if test -n "$ac_ct_AR"; then - echo "$as_me:$LINENO: result: $ac_ct_AR" >&5 -echo "${ECHO_T}$ac_ct_AR" >&6 -else - echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6 -fi - - AR=$ac_ct_AR -else - AR="$ac_cv_prog_AR" -fi - -if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. -set dummy ${ac_tool_prefix}ranlib; ac_word=$2 -echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 -if test "${ac_cv_prog_RANLIB+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test -n "$RANLIB"; then - ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done -done - -fi -fi -RANLIB=$ac_cv_prog_RANLIB -if test -n "$RANLIB"; then - echo "$as_me:$LINENO: result: $RANLIB" >&5 -echo "${ECHO_T}$RANLIB" >&6 -else - echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6 -fi - -fi -if test -z "$ac_cv_prog_RANLIB"; then - ac_ct_RANLIB=$RANLIB - # Extract the first word of "ranlib", so it can be a program name with args. -set dummy ranlib; ac_word=$2 -echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 -if test "${ac_cv_prog_ac_ct_RANLIB+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test -n "$ac_ct_RANLIB"; then - ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_RANLIB="ranlib" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done -done - - test -z "$ac_cv_prog_ac_ct_RANLIB" && ac_cv_prog_ac_ct_RANLIB=":" -fi -fi -ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB -if test -n "$ac_ct_RANLIB"; then - echo "$as_me:$LINENO: result: $ac_ct_RANLIB" >&5 -echo "${ECHO_T}$ac_ct_RANLIB" >&6 -else - echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6 -fi - - RANLIB=$ac_ct_RANLIB -else - RANLIB="$ac_cv_prog_RANLIB" -fi - -if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. -set dummy ${ac_tool_prefix}strip; ac_word=$2 -echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 -if test "${ac_cv_prog_STRIP+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test -n "$STRIP"; then - ac_cv_prog_STRIP="$STRIP" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_STRIP="${ac_tool_prefix}strip" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done -done - -fi -fi -STRIP=$ac_cv_prog_STRIP -if test -n "$STRIP"; then - echo "$as_me:$LINENO: result: $STRIP" >&5 -echo "${ECHO_T}$STRIP" >&6 -else - echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6 -fi - -fi -if test -z "$ac_cv_prog_STRIP"; then - ac_ct_STRIP=$STRIP - # Extract the first word of "strip", so it can be a program name with args. -set dummy strip; ac_word=$2 -echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 -if test "${ac_cv_prog_ac_ct_STRIP+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test -n "$ac_ct_STRIP"; then - ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_STRIP="strip" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done -done - - test -z "$ac_cv_prog_ac_ct_STRIP" && ac_cv_prog_ac_ct_STRIP=":" -fi -fi -ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP -if test -n "$ac_ct_STRIP"; then - echo "$as_me:$LINENO: result: $ac_ct_STRIP" >&5 -echo "${ECHO_T}$ac_ct_STRIP" >&6 -else - echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6 -fi - - STRIP=$ac_ct_STRIP -else - STRIP="$ac_cv_prog_STRIP" -fi - - -old_CC="$CC" -old_CFLAGS="$CFLAGS" - -# Set sane defaults for various variables -test -z "$AR" && AR=ar -test -z "$AR_FLAGS" && AR_FLAGS=cru -test -z "$AS" && AS=as -test -z "$CC" && CC=cc -test -z "$LTCC" && LTCC=$CC -test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS -test -z "$DLLTOOL" && DLLTOOL=dlltool -test -z "$LD" && LD=ld -test -z "$LN_S" && LN_S="ln -s" -test -z "$MAGIC_CMD" && MAGIC_CMD=file -test -z "$NM" && NM=nm -test -z "$SED" && SED=sed -test -z "$OBJDUMP" && OBJDUMP=objdump -test -z "$RANLIB" && RANLIB=: -test -z "$STRIP" && STRIP=: -test -z "$ac_objext" && ac_objext=o - -# Determine commands to create old-style static archives. -old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs$old_deplibs' -old_postinstall_cmds='chmod 644 $oldlib' -old_postuninstall_cmds= - -if test -n "$RANLIB"; then - case $host_os in - openbsd*) - old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$oldlib" - ;; - *) - old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$oldlib" - ;; - esac - old_archive_cmds="$old_archive_cmds~\$RANLIB \$oldlib" -fi - -for cc_temp in $compiler""; do - case $cc_temp in - compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; - distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; - \-*) ;; - *) break;; - esac -done -cc_basename=`$echo "X$cc_temp" | $Xsed -e 's%.*/%%' -e "s%^$host_alias-%%"` - - -# Only perform the check for file, if the check method requires it -case $deplibs_check_method in -file_magic*) - if test "$file_magic_cmd" = '$MAGIC_CMD'; then - echo "$as_me:$LINENO: checking for ${ac_tool_prefix}file" >&5 -echo $ECHO_N "checking for ${ac_tool_prefix}file... $ECHO_C" >&6 -if test "${lt_cv_path_MAGIC_CMD+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - case $MAGIC_CMD in -[\\/*] | ?:[\\/]*) - lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path. - ;; -*) - lt_save_MAGIC_CMD="$MAGIC_CMD" - lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR - ac_dummy="/usr/bin$PATH_SEPARATOR$PATH" - for ac_dir in $ac_dummy; do - IFS="$lt_save_ifs" - test -z "$ac_dir" && ac_dir=. - if test -f $ac_dir/${ac_tool_prefix}file; then - lt_cv_path_MAGIC_CMD="$ac_dir/${ac_tool_prefix}file" - if test -n "$file_magic_test_file"; then - case $deplibs_check_method in - "file_magic "*) - file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` - MAGIC_CMD="$lt_cv_path_MAGIC_CMD" - if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | - $EGREP "$file_magic_regex" > /dev/null; then - : - else - cat <&2 - -*** Warning: the command libtool uses to detect shared libraries, -*** $file_magic_cmd, produces output that libtool cannot recognize. -*** The result is that libtool may fail to recognize shared libraries -*** as such. This will affect the creation of libtool libraries that -*** depend on shared libraries, but programs linked with such libtool -*** libraries will work regardless of this problem. Nevertheless, you -*** may want to report the problem to your system manager and/or to -*** bug-libtool@gnu.org - -EOF - fi ;; - esac - fi - break - fi - done - IFS="$lt_save_ifs" - MAGIC_CMD="$lt_save_MAGIC_CMD" - ;; -esac -fi - -MAGIC_CMD="$lt_cv_path_MAGIC_CMD" -if test -n "$MAGIC_CMD"; then - echo "$as_me:$LINENO: result: $MAGIC_CMD" >&5 -echo "${ECHO_T}$MAGIC_CMD" >&6 -else - echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6 -fi - -if test -z "$lt_cv_path_MAGIC_CMD"; then - if test -n "$ac_tool_prefix"; then - echo "$as_me:$LINENO: checking for file" >&5 -echo $ECHO_N "checking for file... $ECHO_C" >&6 -if test "${lt_cv_path_MAGIC_CMD+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - case $MAGIC_CMD in -[\\/*] | ?:[\\/]*) - lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path. - ;; -*) - lt_save_MAGIC_CMD="$MAGIC_CMD" - lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR - ac_dummy="/usr/bin$PATH_SEPARATOR$PATH" - for ac_dir in $ac_dummy; do - IFS="$lt_save_ifs" - test -z "$ac_dir" && ac_dir=. - if test -f $ac_dir/file; then - lt_cv_path_MAGIC_CMD="$ac_dir/file" - if test -n "$file_magic_test_file"; then - case $deplibs_check_method in - "file_magic "*) - file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` - MAGIC_CMD="$lt_cv_path_MAGIC_CMD" - if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | - $EGREP "$file_magic_regex" > /dev/null; then - : - else - cat <&2 - -*** Warning: the command libtool uses to detect shared libraries, -*** $file_magic_cmd, produces output that libtool cannot recognize. -*** The result is that libtool may fail to recognize shared libraries -*** as such. This will affect the creation of libtool libraries that -*** depend on shared libraries, but programs linked with such libtool -*** libraries will work regardless of this problem. Nevertheless, you -*** may want to report the problem to your system manager and/or to -*** bug-libtool@gnu.org - -EOF - fi ;; - esac - fi - break - fi - done - IFS="$lt_save_ifs" - MAGIC_CMD="$lt_save_MAGIC_CMD" - ;; -esac -fi - -MAGIC_CMD="$lt_cv_path_MAGIC_CMD" -if test -n "$MAGIC_CMD"; then - echo "$as_me:$LINENO: result: $MAGIC_CMD" >&5 -echo "${ECHO_T}$MAGIC_CMD" >&6 -else - echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6 -fi - - else - MAGIC_CMD=: - fi -fi - - fi - ;; -esac - -enable_dlopen=no -enable_win32_dll=yes - -# Check whether --enable-libtool-lock or --disable-libtool-lock was given. -if test "${enable_libtool_lock+set}" = set; then - enableval="$enable_libtool_lock" - -fi; -test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes - - -# Check whether --with-pic or --without-pic was given. -if test "${with_pic+set}" = set; then - withval="$with_pic" - pic_mode="$withval" -else - pic_mode=default -fi; -test -z "$pic_mode" && pic_mode=default - -# Use C for the default configuration in the libtool script -tagname= -lt_save_CC="$CC" -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - - -# Source file extension for C test sources. -ac_ext=c - -# Object file extension for compiled C test sources. -objext=o -objext=$objext - -# Code to be used in simple compile tests -lt_simple_compile_test_code="int some_variable = 0;\n" - -# Code to be used in simple link tests -lt_simple_link_test_code='int main(){return(0);}\n' - - -# If no C compiler was specified, use CC. -LTCC=${LTCC-"$CC"} - -# If no C compiler flags were specified, use CFLAGS. -LTCFLAGS=${LTCFLAGS-"$CFLAGS"} - -# Allow CC to be a program name with arguments. -compiler=$CC - - -# save warnings/boilerplate of simple test code -ac_outfile=conftest.$ac_objext -printf "$lt_simple_compile_test_code" >conftest.$ac_ext -eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err -_lt_compiler_boilerplate=`cat conftest.err` -$rm conftest* - -ac_outfile=conftest.$ac_objext -printf "$lt_simple_link_test_code" >conftest.$ac_ext -eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err -_lt_linker_boilerplate=`cat conftest.err` -$rm conftest* - - -## CAVEAT EMPTOR: -## There is no encapsulation within the following macros, do not change -## the running order or otherwise move them around unless you know exactly -## what you are doing... - -lt_prog_compiler_no_builtin_flag= - -if test "$GCC" = yes; then - lt_prog_compiler_no_builtin_flag=' -fno-builtin' - - -echo "$as_me:$LINENO: checking if $compiler supports -fno-rtti -fno-exceptions" >&5 -echo $ECHO_N "checking if $compiler supports -fno-rtti -fno-exceptions... $ECHO_C" >&6 -if test "${lt_cv_prog_compiler_rtti_exceptions+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - lt_cv_prog_compiler_rtti_exceptions=no - ac_outfile=conftest.$ac_objext - printf "$lt_simple_compile_test_code" > conftest.$ac_ext - lt_compiler_flag="-fno-rtti -fno-exceptions" - # Insert the option either (1) after the last *FLAGS variable, or - # (2) before a word containing "conftest.", or (3) at the end. - # Note that $ac_compile itself does not contain backslashes and begins - # with a dollar sign (not a hyphen), so the echo should work correctly. - # The option is referenced via a variable to avoid confusing sed. - lt_compile=`echo "$ac_compile" | $SED \ - -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ - -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ - -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:6363: $lt_compile\"" >&5) - (eval "$lt_compile" 2>conftest.err) - ac_status=$? - cat conftest.err >&5 - echo "$as_me:6367: \$? = $ac_status" >&5 - if (exit $ac_status) && test -s "$ac_outfile"; then - # The compiler can only warn and ignore the option if not recognized - # So say no if there are warnings other than the usual output. - $echo "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' >conftest.exp - $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 - if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then - lt_cv_prog_compiler_rtti_exceptions=yes - fi - fi - $rm conftest* - -fi -echo "$as_me:$LINENO: result: $lt_cv_prog_compiler_rtti_exceptions" >&5 -echo "${ECHO_T}$lt_cv_prog_compiler_rtti_exceptions" >&6 - -if test x"$lt_cv_prog_compiler_rtti_exceptions" = xyes; then - lt_prog_compiler_no_builtin_flag="$lt_prog_compiler_no_builtin_flag -fno-rtti -fno-exceptions" -else - : -fi - -fi - -lt_prog_compiler_wl= -lt_prog_compiler_pic= -lt_prog_compiler_static= - -echo "$as_me:$LINENO: checking for $compiler option to produce PIC" >&5 -echo $ECHO_N "checking for $compiler option to produce PIC... $ECHO_C" >&6 - - if test "$GCC" = yes; then - lt_prog_compiler_wl='-Wl,' - lt_prog_compiler_static='-static' - - case $host_os in - aix*) - # All AIX code is PIC. - if test "$host_cpu" = ia64; then - # AIX 5 now supports IA64 processor - lt_prog_compiler_static='-Bstatic' - fi - ;; - - amigaos*) - # FIXME: we need at least 68020 code to build shared libraries, but - # adding the `-m68020' flag to GCC prevents building anything better, - # like `-m68040'. - lt_prog_compiler_pic='-m68020 -resident32 -malways-restore-a4' - ;; - - beos* | cygwin* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) - # PIC is the default for these OSes. - ;; - - mingw* | pw32* | os2*) - # This hack is so that the source file can tell whether it is being - # built for inclusion in a dll (and should export symbols for example). - lt_prog_compiler_pic='-DDLL_EXPORT' - ;; - - darwin* | rhapsody*) - # PIC is the default on this platform - # Common symbols not allowed in MH_DYLIB files - lt_prog_compiler_pic='-fno-common' - ;; - - interix3*) - # Interix 3.x gcc -fpic/-fPIC options generate broken code. - # Instead, we relocate shared libraries at runtime. - ;; - - msdosdjgpp*) - # Just because we use GCC doesn't mean we suddenly get shared libraries - # on systems that don't support them. - lt_prog_compiler_can_build_shared=no - enable_shared=no - ;; - - sysv4*MP*) - if test -d /usr/nec; then - lt_prog_compiler_pic=-Kconform_pic - fi - ;; - - hpux*) - # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but - # not for PA HP-UX. - case $host_cpu in - hppa*64*|ia64*) - # +Z the default - ;; - *) - lt_prog_compiler_pic='-fPIC' - ;; - esac - ;; - - *) - lt_prog_compiler_pic='-fPIC' - ;; - esac - else - # PORTME Check for flag to pass linker flags through the system compiler. - case $host_os in - aix*) - lt_prog_compiler_wl='-Wl,' - if test "$host_cpu" = ia64; then - # AIX 5 now supports IA64 processor - lt_prog_compiler_static='-Bstatic' - else - lt_prog_compiler_static='-bnso -bI:/lib/syscalls.exp' - fi - ;; - darwin*) - # PIC is the default on this platform - # Common symbols not allowed in MH_DYLIB files - case $cc_basename in - xlc*) - lt_prog_compiler_pic='-qnocommon' - lt_prog_compiler_wl='-Wl,' - ;; - esac - ;; - - mingw* | pw32* | os2*) - # This hack is so that the source file can tell whether it is being - # built for inclusion in a dll (and should export symbols for example). - lt_prog_compiler_pic='-DDLL_EXPORT' - ;; - - hpux9* | hpux10* | hpux11*) - lt_prog_compiler_wl='-Wl,' - # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but - # not for PA HP-UX. - case $host_cpu in - hppa*64*|ia64*) - # +Z the default - ;; - *) - lt_prog_compiler_pic='+Z' - ;; - esac - # Is there a better lt_prog_compiler_static that works with the bundled CC? - lt_prog_compiler_static='${wl}-a ${wl}archive' - ;; - - irix5* | irix6* | nonstopux*) - lt_prog_compiler_wl='-Wl,' - # PIC (with -KPIC) is the default. - lt_prog_compiler_static='-non_shared' - ;; - - newsos6) - lt_prog_compiler_pic='-KPIC' - lt_prog_compiler_static='-Bstatic' - ;; - - linux*) - case $cc_basename in - icc* | ecc*) - lt_prog_compiler_wl='-Wl,' - lt_prog_compiler_pic='-KPIC' - lt_prog_compiler_static='-static' - ;; - pgcc* | pgf77* | pgf90* | pgf95*) - # Portland Group compilers (*not* the Pentium gcc compiler, - # which looks to be a dead project) - lt_prog_compiler_wl='-Wl,' - lt_prog_compiler_pic='-fpic' - lt_prog_compiler_static='-Bstatic' - ;; - ccc*) - lt_prog_compiler_wl='-Wl,' - # All Alpha code is PIC. - lt_prog_compiler_static='-non_shared' - ;; - esac - ;; - - osf3* | osf4* | osf5*) - lt_prog_compiler_wl='-Wl,' - # All OSF/1 code is PIC. - lt_prog_compiler_static='-non_shared' - ;; - - solaris*) - lt_prog_compiler_pic='-KPIC' - lt_prog_compiler_static='-Bstatic' - case $cc_basename in - f77* | f90* | f95*) - lt_prog_compiler_wl='-Qoption ld ';; - *) - lt_prog_compiler_wl='-Wl,';; - esac - ;; - - sunos4*) - lt_prog_compiler_wl='-Qoption ld ' - lt_prog_compiler_pic='-PIC' - lt_prog_compiler_static='-Bstatic' - ;; - - sysv4 | sysv4.2uw2* | sysv4.3*) - lt_prog_compiler_wl='-Wl,' - lt_prog_compiler_pic='-KPIC' - lt_prog_compiler_static='-Bstatic' - ;; - - sysv4*MP*) - if test -d /usr/nec ;then - lt_prog_compiler_pic='-Kconform_pic' - lt_prog_compiler_static='-Bstatic' - fi - ;; - - sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) - lt_prog_compiler_wl='-Wl,' - lt_prog_compiler_pic='-KPIC' - lt_prog_compiler_static='-Bstatic' - ;; - - unicos*) - lt_prog_compiler_wl='-Wl,' - lt_prog_compiler_can_build_shared=no - ;; - - uts4*) - lt_prog_compiler_pic='-pic' - lt_prog_compiler_static='-Bstatic' - ;; - - *) - lt_prog_compiler_can_build_shared=no - ;; - esac - fi - -echo "$as_me:$LINENO: result: $lt_prog_compiler_pic" >&5 -echo "${ECHO_T}$lt_prog_compiler_pic" >&6 - -# -# Check to make sure the PIC flag actually works. -# -if test -n "$lt_prog_compiler_pic"; then - -echo "$as_me:$LINENO: checking if $compiler PIC flag $lt_prog_compiler_pic works" >&5 -echo $ECHO_N "checking if $compiler PIC flag $lt_prog_compiler_pic works... $ECHO_C" >&6 -if test "${lt_prog_compiler_pic_works+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - lt_prog_compiler_pic_works=no - ac_outfile=conftest.$ac_objext - printf "$lt_simple_compile_test_code" > conftest.$ac_ext - lt_compiler_flag="$lt_prog_compiler_pic -DPIC" - # Insert the option either (1) after the last *FLAGS variable, or - # (2) before a word containing "conftest.", or (3) at the end. - # Note that $ac_compile itself does not contain backslashes and begins - # with a dollar sign (not a hyphen), so the echo should work correctly. - # The option is referenced via a variable to avoid confusing sed. - lt_compile=`echo "$ac_compile" | $SED \ - -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ - -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ - -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:6631: $lt_compile\"" >&5) - (eval "$lt_compile" 2>conftest.err) - ac_status=$? - cat conftest.err >&5 - echo "$as_me:6635: \$? = $ac_status" >&5 - if (exit $ac_status) && test -s "$ac_outfile"; then - # The compiler can only warn and ignore the option if not recognized - # So say no if there are warnings other than the usual output. - $echo "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' >conftest.exp - $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 - if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then - lt_prog_compiler_pic_works=yes - fi - fi - $rm conftest* - -fi -echo "$as_me:$LINENO: result: $lt_prog_compiler_pic_works" >&5 -echo "${ECHO_T}$lt_prog_compiler_pic_works" >&6 - -if test x"$lt_prog_compiler_pic_works" = xyes; then - case $lt_prog_compiler_pic in - "" | " "*) ;; - *) lt_prog_compiler_pic=" $lt_prog_compiler_pic" ;; - esac -else - lt_prog_compiler_pic= - lt_prog_compiler_can_build_shared=no -fi - -fi -case $host_os in - # For platforms which do not support PIC, -DPIC is meaningless: - *djgpp*) - lt_prog_compiler_pic= - ;; - *) - lt_prog_compiler_pic="$lt_prog_compiler_pic -DPIC" - ;; -esac - -# -# Check to make sure the static flag actually works. -# -wl=$lt_prog_compiler_wl eval lt_tmp_static_flag=\"$lt_prog_compiler_static\" -echo "$as_me:$LINENO: checking if $compiler static flag $lt_tmp_static_flag works" >&5 -echo $ECHO_N "checking if $compiler static flag $lt_tmp_static_flag works... $ECHO_C" >&6 -if test "${lt_prog_compiler_static_works+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - lt_prog_compiler_static_works=no - save_LDFLAGS="$LDFLAGS" - LDFLAGS="$LDFLAGS $lt_tmp_static_flag" - printf "$lt_simple_link_test_code" > conftest.$ac_ext - if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then - # The linker can only warn and ignore the option if not recognized - # So say no if there are warnings - if test -s conftest.err; then - # Append any errors to the config.log. - cat conftest.err 1>&5 - $echo "X$_lt_linker_boilerplate" | $Xsed -e '/^$/d' > conftest.exp - $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 - if diff conftest.exp conftest.er2 >/dev/null; then - lt_prog_compiler_static_works=yes - fi - else - lt_prog_compiler_static_works=yes - fi - fi - $rm conftest* - LDFLAGS="$save_LDFLAGS" - -fi -echo "$as_me:$LINENO: result: $lt_prog_compiler_static_works" >&5 -echo "${ECHO_T}$lt_prog_compiler_static_works" >&6 - -if test x"$lt_prog_compiler_static_works" = xyes; then - : -else - lt_prog_compiler_static= -fi - - -echo "$as_me:$LINENO: checking if $compiler supports -c -o file.$ac_objext" >&5 -echo $ECHO_N "checking if $compiler supports -c -o file.$ac_objext... $ECHO_C" >&6 -if test "${lt_cv_prog_compiler_c_o+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - lt_cv_prog_compiler_c_o=no - $rm -r conftest 2>/dev/null - mkdir conftest - cd conftest - mkdir out - printf "$lt_simple_compile_test_code" > conftest.$ac_ext - - lt_compiler_flag="-o out/conftest2.$ac_objext" - # Insert the option either (1) after the last *FLAGS variable, or - # (2) before a word containing "conftest.", or (3) at the end. - # Note that $ac_compile itself does not contain backslashes and begins - # with a dollar sign (not a hyphen), so the echo should work correctly. - lt_compile=`echo "$ac_compile" | $SED \ - -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ - -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ - -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:6735: $lt_compile\"" >&5) - (eval "$lt_compile" 2>out/conftest.err) - ac_status=$? - cat out/conftest.err >&5 - echo "$as_me:6739: \$? = $ac_status" >&5 - if (exit $ac_status) && test -s out/conftest2.$ac_objext - then - # The compiler can only warn and ignore the option if not recognized - # So say no if there are warnings - $echo "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' > out/conftest.exp - $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 - if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then - lt_cv_prog_compiler_c_o=yes - fi - fi - chmod u+w . 2>&5 - $rm conftest* - # SGI C++ compiler will create directory out/ii_files/ for - # template instantiation - test -d out/ii_files && $rm out/ii_files/* && rmdir out/ii_files - $rm out/* && rmdir out - cd .. - rmdir conftest - $rm conftest* - -fi -echo "$as_me:$LINENO: result: $lt_cv_prog_compiler_c_o" >&5 -echo "${ECHO_T}$lt_cv_prog_compiler_c_o" >&6 - - -hard_links="nottested" -if test "$lt_cv_prog_compiler_c_o" = no && test "$need_locks" != no; then - # do not overwrite the value of need_locks provided by the user - echo "$as_me:$LINENO: checking if we can lock with hard links" >&5 -echo $ECHO_N "checking if we can lock with hard links... $ECHO_C" >&6 - hard_links=yes - $rm conftest* - ln conftest.a conftest.b 2>/dev/null && hard_links=no - touch conftest.a - ln conftest.a conftest.b 2>&5 || hard_links=no - ln conftest.a conftest.b 2>/dev/null && hard_links=no - echo "$as_me:$LINENO: result: $hard_links" >&5 -echo "${ECHO_T}$hard_links" >&6 - if test "$hard_links" = no; then - { echo "$as_me:$LINENO: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&5 -echo "$as_me: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&2;} - need_locks=warn - fi -else - need_locks=no -fi - -echo "$as_me:$LINENO: checking whether the $compiler linker ($LD) supports shared libraries" >&5 -echo $ECHO_N "checking whether the $compiler linker ($LD) supports shared libraries... $ECHO_C" >&6 - - runpath_var= - allow_undefined_flag= - enable_shared_with_static_runtimes=no - archive_cmds= - archive_expsym_cmds= - old_archive_From_new_cmds= - old_archive_from_expsyms_cmds= - export_dynamic_flag_spec= - whole_archive_flag_spec= - thread_safe_flag_spec= - hardcode_libdir_flag_spec= - hardcode_libdir_flag_spec_ld= - hardcode_libdir_separator= - hardcode_direct=no - hardcode_minus_L=no - hardcode_shlibpath_var=unsupported - link_all_deplibs=unknown - hardcode_automatic=no - module_cmds= - module_expsym_cmds= - always_export_symbols=no - export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' - # include_expsyms should be a list of space-separated symbols to be *always* - # included in the symbol list - include_expsyms= - # exclude_expsyms can be an extended regexp of symbols to exclude - # it will be wrapped by ` (' and `)$', so one must not match beginning or - # end of line. Example: `a|bc|.*d.*' will exclude the symbols `a' and `bc', - # as well as any symbol that contains `d'. - exclude_expsyms="_GLOBAL_OFFSET_TABLE_" - # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out - # platforms (ab)use it in PIC code, but their linkers get confused if - # the symbol is explicitly referenced. Since portable code cannot - # rely on this symbol name, it's probably fine to never include it in - # preloaded symbol tables. - extract_expsyms_cmds= - # Just being paranoid about ensuring that cc_basename is set. - for cc_temp in $compiler""; do - case $cc_temp in - compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; - distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; - \-*) ;; - *) break;; - esac -done -cc_basename=`$echo "X$cc_temp" | $Xsed -e 's%.*/%%' -e "s%^$host_alias-%%"` - - case $host_os in - cygwin* | mingw* | pw32*) - # FIXME: the MSVC++ port hasn't been tested in a loooong time - # When not using gcc, we currently assume that we are using - # Microsoft Visual C++. - if test "$GCC" != yes; then - with_gnu_ld=no - fi - ;; - interix*) - # we just hope/assume this is gcc and not c89 (= MSVC++) - with_gnu_ld=yes - ;; - openbsd*) - with_gnu_ld=no - ;; - esac - - ld_shlibs=yes - if test "$with_gnu_ld" = yes; then - # If archive_cmds runs LD, not CC, wlarc should be empty - wlarc='${wl}' - - # Set some defaults for GNU ld with shared library support. These - # are reset later if shared libraries are not supported. Putting them - # here allows them to be overridden if necessary. - runpath_var=LD_RUN_PATH - hardcode_libdir_flag_spec='${wl}--rpath ${wl}$libdir' - export_dynamic_flag_spec='${wl}--export-dynamic' - # ancient GNU ld didn't support --whole-archive et. al. - if $LD --help 2>&1 | grep 'no-whole-archive' > /dev/null; then - whole_archive_flag_spec="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' - else - whole_archive_flag_spec= - fi - supports_anon_versioning=no - case `$LD -v 2>/dev/null` in - *\ [01].* | *\ 2.[0-9].* | *\ 2.10.*) ;; # catch versions < 2.11 - *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ... - *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ... - *\ 2.11.*) ;; # other 2.11 versions - *) supports_anon_versioning=yes ;; - esac - - # See if GNU ld supports shared libraries. - case $host_os in - aix3* | aix4* | aix5*) - # On AIX/PPC, the GNU linker is very broken - if test "$host_cpu" != ia64; then - ld_shlibs=no - cat <&2 - -*** Warning: the GNU linker, at least up to release 2.9.1, is reported -*** to be unable to reliably create shared libraries on AIX. -*** Therefore, libtool is disabling shared libraries support. If you -*** really care for shared libraries, you may want to modify your PATH -*** so that a non-GNU linker is found, and then restart. - -EOF - fi - ;; - - amigaos*) - archive_cmds='$rm $output_objdir/a2ixlibrary.data~$echo "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$echo "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$echo "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$echo "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' - hardcode_libdir_flag_spec='-L$libdir' - hardcode_minus_L=yes - - # Samuel A. Falvo II reports - # that the semantics of dynamic libraries on AmigaOS, at least up - # to version 4, is to share data among multiple programs linked - # with the same dynamic library. Since this doesn't match the - # behavior of shared libraries on other platforms, we can't use - # them. - ld_shlibs=no - ;; - - beos*) - if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then - allow_undefined_flag=unsupported - # Joseph Beckenbach says some releases of gcc - # support --undefined. This deserves some investigation. FIXME - archive_cmds='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - else - ld_shlibs=no - fi - ;; - - cygwin* | mingw* | pw32*) - # _LT_AC_TAGVAR(hardcode_libdir_flag_spec, ) is actually meaningless, - # as there is no search path for DLLs. - hardcode_libdir_flag_spec='-L$libdir' - allow_undefined_flag=unsupported - always_export_symbols=no - enable_shared_with_static_runtimes=yes - export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS] /s/.* \([^ ]*\)/\1 DATA/'\'' | $SED -e '\''/^[AITW] /s/.* //'\'' | sort | uniq > $export_symbols' - - if $LD --help 2>&1 | grep 'auto-import' > /dev/null; then - archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' - # If the export-symbols file already is a .def file (1st line - # is EXPORTS), use it as is; otherwise, prepend... - archive_expsym_cmds='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then - cp $export_symbols $output_objdir/$soname.def; - else - echo EXPORTS > $output_objdir/$soname.def; - cat $export_symbols >> $output_objdir/$soname.def; - fi~ - $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' - else - ld_shlibs=no - fi - ;; - - interix3*) - hardcode_direct=no - hardcode_shlibpath_var=no - hardcode_libdir_flag_spec='${wl}-rpath,$libdir' - export_dynamic_flag_spec='${wl}-E' - # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. - # Instead, shared libraries are loaded at an image base (0x10000000 by - # default) and relocated if they conflict, which is a slow very memory - # consuming and fragmenting process. To avoid this, we pick a random, - # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link - # time. Moving up from 0x10000000 also allows more sbrk(2) space. - archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' - archive_expsym_cmds='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' - ;; - - linux*) - if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then - tmp_addflag= - case $cc_basename,$host_cpu in - pgcc*) # Portland Group C compiler - whole_archive_flag_spec='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $echo \"$new_convenience\"` ${wl}--no-whole-archive' - tmp_addflag=' $pic_flag' - ;; - pgf77* | pgf90* | pgf95*) # Portland Group f77 and f90 compilers - whole_archive_flag_spec='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $echo \"$new_convenience\"` ${wl}--no-whole-archive' - tmp_addflag=' $pic_flag -Mnomain' ;; - ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64 - tmp_addflag=' -i_dynamic' ;; - efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64 - tmp_addflag=' -i_dynamic -nofor_main' ;; - ifc* | ifort*) # Intel Fortran compiler - tmp_addflag=' -nofor_main' ;; - esac - archive_cmds='$CC -shared'"$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - - if test $supports_anon_versioning = yes; then - archive_expsym_cmds='$echo "{ global:" > $output_objdir/$libname.ver~ - cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ - $echo "local: *; };" >> $output_objdir/$libname.ver~ - $CC -shared'"$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib' - fi - else - ld_shlibs=no - fi - ;; - - netbsd*) - if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then - archive_cmds='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib' - wlarc= - else - archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' - fi - ;; - - solaris*) - if $LD -v 2>&1 | grep 'BFD 2\.8' > /dev/null; then - ld_shlibs=no - cat <&2 - -*** Warning: The releases 2.8.* of the GNU linker cannot reliably -*** create shared libraries on Solaris systems. Therefore, libtool -*** is disabling shared libraries support. We urge you to upgrade GNU -*** binutils to release 2.9.1 or newer. Another option is to modify -*** your PATH or compiler configuration so that the native linker is -*** used, and then restart. - -EOF - elif $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then - archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' - else - ld_shlibs=no - fi - ;; - - sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) - case `$LD -v 2>&1` in - *\ [01].* | *\ 2.[0-9].* | *\ 2.1[0-5].*) - ld_shlibs=no - cat <<_LT_EOF 1>&2 - -*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 can not -*** reliably create shared libraries on SCO systems. Therefore, libtool -*** is disabling shared libraries support. We urge you to upgrade GNU -*** binutils to release 2.16.91.0.3 or newer. Another option is to modify -*** your PATH or compiler configuration so that the native linker is -*** used, and then restart. - -_LT_EOF - ;; - *) - if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then - hardcode_libdir_flag_spec='`test -z "$SCOABSPATH" && echo ${wl}-rpath,$libdir`' - archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname,\${SCOABSPATH:+${install_libdir}/}$soname -o $lib' - archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname,\${SCOABSPATH:+${install_libdir}/}$soname,-retain-symbols-file,$export_symbols -o $lib' - else - ld_shlibs=no - fi - ;; - esac - ;; - - sunos4*) - archive_cmds='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags' - wlarc= - hardcode_direct=yes - hardcode_shlibpath_var=no - ;; - - *) - if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then - archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' - else - ld_shlibs=no - fi - ;; - esac - - if test "$ld_shlibs" = no; then - runpath_var= - hardcode_libdir_flag_spec= - export_dynamic_flag_spec= - whole_archive_flag_spec= - fi - else - # PORTME fill in a description of your system's linker (not GNU ld) - case $host_os in - aix3*) - allow_undefined_flag=unsupported - always_export_symbols=yes - archive_expsym_cmds='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname' - # Note: this linker hardcodes the directories in LIBPATH if there - # are no directories specified by -L. - hardcode_minus_L=yes - if test "$GCC" = yes && test -z "$lt_prog_compiler_static"; then - # Neither direct hardcoding nor static linking is supported with a - # broken collect2. - hardcode_direct=unsupported - fi - ;; - - aix4* | aix5*) - if test "$host_cpu" = ia64; then - # On IA64, the linker does run time linking by default, so we don't - # have to do anything special. - aix_use_runtimelinking=no - exp_sym_flag='-Bexport' - no_entry_flag="" - else - # If we're using GNU nm, then we don't want the "-C" option. - # -C means demangle to AIX nm, but means don't demangle with GNU nm - if $NM -V 2>&1 | grep 'GNU' > /dev/null; then - export_symbols_cmds='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$2 == "T") || (\$2 == "D") || (\$2 == "B")) && (substr(\$3,1,1) != ".")) { print \$3 } }'\'' | sort -u > $export_symbols' - else - export_symbols_cmds='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$2 == "T") || (\$2 == "D") || (\$2 == "B")) && (substr(\$3,1,1) != ".")) { print \$3 } }'\'' | sort -u > $export_symbols' - fi - aix_use_runtimelinking=no - - # Test if we are trying to use run time linking or normal - # AIX style linking. If -brtl is somewhere in LDFLAGS, we - # need to do runtime linking. - case $host_os in aix4.[23]|aix4.[23].*|aix5*) - for ld_flag in $LDFLAGS; do - if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then - aix_use_runtimelinking=yes - break - fi - done - ;; - esac - - exp_sym_flag='-bexport' - no_entry_flag='-bnoentry' - fi - - # When large executables or shared objects are built, AIX ld can - # have problems creating the table of contents. If linking a library - # or program results in "error TOC overflow" add -mminimal-toc to - # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not - # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. - - archive_cmds='' - hardcode_direct=yes - hardcode_libdir_separator=':' - link_all_deplibs=yes - - if test "$GCC" = yes; then - case $host_os in aix4.[012]|aix4.[012].*) - # We only want to do this on AIX 4.2 and lower, the check - # below for broken collect2 doesn't work under 4.3+ - collect2name=`${CC} -print-prog-name=collect2` - if test -f "$collect2name" && \ - strings "$collect2name" | grep resolve_lib_name >/dev/null - then - # We have reworked collect2 - hardcode_direct=yes - else - # We have old collect2 - hardcode_direct=unsupported - # It fails to find uninstalled libraries when the uninstalled - # path is not listed in the libpath. Setting hardcode_minus_L - # to unsupported forces relinking - hardcode_minus_L=yes - hardcode_libdir_flag_spec='-L$libdir' - hardcode_libdir_separator= - fi - ;; - esac - shared_flag='-shared' - if test "$aix_use_runtimelinking" = yes; then - shared_flag="$shared_flag "'${wl}-G' - fi - else - # not using gcc - if test "$host_cpu" = ia64; then - # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release - # chokes on -Wl,-G. The following line is correct: - shared_flag='-G' - else - if test "$aix_use_runtimelinking" = yes; then - shared_flag='${wl}-G' - else - shared_flag='${wl}-bM:SRE' - fi - fi - fi - - # It seems that -bexpall does not export symbols beginning with - # underscore (_), so it is better to generate a list of symbols to export. - always_export_symbols=yes - if test "$aix_use_runtimelinking" = yes; then - # Warning - without using the other runtime loading flags (-brtl), - # -berok will link without error, but may produce a broken library. - allow_undefined_flag='-berok' - # Determine the default libpath from the value encoded in an empty executable. - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 - (eval $ac_link) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest$ac_exeext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - -aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } -}'` -# Check for a 64-bit object if we didn't find anything. -if test -z "$aix_libpath"; then aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } -}'`; fi -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -fi -rm -f conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi - - hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath" - archive_expsym_cmds="\$CC"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then echo "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag" - else - if test "$host_cpu" = ia64; then - hardcode_libdir_flag_spec='${wl}-R $libdir:/usr/lib:/lib' - allow_undefined_flag="-z nodefs" - archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols" - else - # Determine the default libpath from the value encoded in an empty executable. - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 - (eval $ac_link) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest$ac_exeext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - -aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } -}'` -# Check for a 64-bit object if we didn't find anything. -if test -z "$aix_libpath"; then aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } -}'`; fi -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -fi -rm -f conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi - - hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath" - # Warning - without using the other run time loading flags, - # -berok will link without error, but may produce a broken library. - no_undefined_flag=' ${wl}-bernotok' - allow_undefined_flag=' ${wl}-berok' - # Exported symbols can be pulled into shared objects from archives - whole_archive_flag_spec='$convenience' - archive_cmds_need_lc=yes - # This is similar to how AIX traditionally builds its shared libraries. - archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' - fi - fi - ;; - - amigaos*) - archive_cmds='$rm $output_objdir/a2ixlibrary.data~$echo "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$echo "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$echo "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$echo "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' - hardcode_libdir_flag_spec='-L$libdir' - hardcode_minus_L=yes - # see comment about different semantics on the GNU ld section - ld_shlibs=no - ;; - - bsdi[45]*) - export_dynamic_flag_spec=-rdynamic - ;; - - cygwin* | mingw* | pw32*) - # When not using gcc, we currently assume that we are using - # Microsoft Visual C++. - # hardcode_libdir_flag_spec is actually meaningless, as there is - # no search path for DLLs. - hardcode_libdir_flag_spec=' ' - allow_undefined_flag=unsupported - # Tell ltmain to make .lib files, not .a files. - libext=lib - # Tell ltmain to make .dll files, not .so files. - shrext_cmds=".dll" - # FIXME: Setting linknames here is a bad hack. - archive_cmds='$CC -o $lib $libobjs $compiler_flags `echo "$deplibs" | $SED -e '\''s/ -lc$//'\''` -link -dll~linknames=' - # The linker will automatically build a .lib file if we build a DLL. - old_archive_From_new_cmds='true' - # FIXME: Should let the user specify the lib program. - old_archive_cmds='lib /OUT:$oldlib$oldobjs$old_deplibs' - fix_srcfile_path='`cygpath -w "$srcfile"`' - enable_shared_with_static_runtimes=yes - ;; - - darwin* | rhapsody*) - case $host_os in - rhapsody* | darwin1.[012]) - allow_undefined_flag='${wl}-undefined ${wl}suppress' - ;; - *) # Darwin 1.3 on - if test -z ${MACOSX_DEPLOYMENT_TARGET} ; then - allow_undefined_flag='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' - else - case ${MACOSX_DEPLOYMENT_TARGET} in - 10.[012]) - allow_undefined_flag='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' - ;; - 10.*) - allow_undefined_flag='${wl}-undefined ${wl}dynamic_lookup' - ;; - esac - fi - ;; - esac - archive_cmds_need_lc=no - hardcode_direct=no - hardcode_automatic=yes - hardcode_shlibpath_var=unsupported - whole_archive_flag_spec='' - link_all_deplibs=yes - if test "$GCC" = yes ; then - output_verbose_link_cmd='echo' - archive_cmds='$CC -dynamiclib $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags -install_name $rpath/$soname $verstring' - module_cmds='$CC $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags' - # Don't fix this by using the ld -exported_symbols_list flag, it doesn't exist in older darwin lds - archive_expsym_cmds='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC -dynamiclib $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags -install_name $rpath/$soname $verstring~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' - module_expsym_cmds='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' - else - case $cc_basename in - xlc*) - output_verbose_link_cmd='echo' - archive_cmds='$CC -qmkshrobj $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-install_name ${wl}`echo $rpath/$soname` $verstring' - module_cmds='$CC $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags' - # Don't fix this by using the ld -exported_symbols_list flag, it doesn't exist in older darwin lds - archive_expsym_cmds='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC -qmkshrobj $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-install_name ${wl}$rpath/$soname $verstring~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' - module_expsym_cmds='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' - ;; - *) - ld_shlibs=no - ;; - esac - fi - ;; - - dgux*) - archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - hardcode_libdir_flag_spec='-L$libdir' - hardcode_shlibpath_var=no - ;; - - freebsd1*) - ld_shlibs=no - ;; - - # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor - # support. Future versions do this automatically, but an explicit c++rt0.o - # does not break anything, and helps significantly (at the cost of a little - # extra space). - freebsd2.2*) - archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o' - hardcode_libdir_flag_spec='-R$libdir' - hardcode_direct=yes - hardcode_shlibpath_var=no - ;; - - # Unfortunately, older versions of FreeBSD 2 do not have this feature. - freebsd2*) - archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' - hardcode_direct=yes - hardcode_minus_L=yes - hardcode_shlibpath_var=no - ;; - - # FreeBSD 3 and greater uses gcc -shared to do shared libraries. - freebsd* | kfreebsd*-gnu | dragonfly*) - archive_cmds='$CC -shared -o $lib $libobjs $deplibs $compiler_flags' - hardcode_libdir_flag_spec='-R$libdir' - hardcode_direct=yes - hardcode_shlibpath_var=no - ;; - - hpux9*) - if test "$GCC" = yes; then - archive_cmds='$rm $output_objdir/$soname~$CC -shared -fPIC ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' - else - archive_cmds='$rm $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' - fi - hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' - hardcode_libdir_separator=: - hardcode_direct=yes - - # hardcode_minus_L: Not really in the search PATH, - # but as the default location of the library. - hardcode_minus_L=yes - export_dynamic_flag_spec='${wl}-E' - ;; - - hpux10*) - if test "$GCC" = yes -a "$with_gnu_ld" = no; then - archive_cmds='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' - else - archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' - fi - if test "$with_gnu_ld" = no; then - hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' - hardcode_libdir_separator=: - - hardcode_direct=yes - export_dynamic_flag_spec='${wl}-E' - - # hardcode_minus_L: Not really in the search PATH, - # but as the default location of the library. - hardcode_minus_L=yes - fi - ;; - - hpux11*) - if test "$GCC" = yes -a "$with_gnu_ld" = no; then - case $host_cpu in - hppa*64*) - archive_cmds='$CC -shared ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' - ;; - ia64*) - archive_cmds='$CC -shared ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' - ;; - *) - archive_cmds='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' - ;; - esac - else - case $host_cpu in - hppa*64*) - archive_cmds='$CC -b ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' - ;; - ia64*) - archive_cmds='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' - ;; - *) - archive_cmds='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' - ;; - esac - fi - if test "$with_gnu_ld" = no; then - hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' - hardcode_libdir_separator=: - - case $host_cpu in - hppa*64*|ia64*) - hardcode_libdir_flag_spec_ld='+b $libdir' - hardcode_direct=no - hardcode_shlibpath_var=no - ;; - *) - hardcode_direct=yes - export_dynamic_flag_spec='${wl}-E' - - # hardcode_minus_L: Not really in the search PATH, - # but as the default location of the library. - hardcode_minus_L=yes - ;; - esac - fi - ;; - - irix5* | irix6* | nonstopux*) - if test "$GCC" = yes; then - archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' - else - archive_cmds='$LD -shared $libobjs $deplibs $linker_flags -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib' - hardcode_libdir_flag_spec_ld='-rpath $libdir' - fi - hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' - hardcode_libdir_separator=: - link_all_deplibs=yes - ;; - - netbsd*) - if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then - archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out - else - archive_cmds='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF - fi - hardcode_libdir_flag_spec='-R$libdir' - hardcode_direct=yes - hardcode_shlibpath_var=no - ;; - - newsos6) - archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - hardcode_direct=yes - hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' - hardcode_libdir_separator=: - hardcode_shlibpath_var=no - ;; - - openbsd*) - hardcode_direct=yes - hardcode_shlibpath_var=no - if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then - archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' - archive_expsym_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-retain-symbols-file,$export_symbols' - hardcode_libdir_flag_spec='${wl}-rpath,$libdir' - export_dynamic_flag_spec='${wl}-E' - else - case $host_os in - openbsd[01].* | openbsd2.[0-7] | openbsd2.[0-7].*) - archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' - hardcode_libdir_flag_spec='-R$libdir' - ;; - *) - archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' - hardcode_libdir_flag_spec='${wl}-rpath,$libdir' - ;; - esac - fi - ;; - - os2*) - hardcode_libdir_flag_spec='-L$libdir' - hardcode_minus_L=yes - allow_undefined_flag=unsupported - archive_cmds='$echo "LIBRARY $libname INITINSTANCE" > $output_objdir/$libname.def~$echo "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~$echo DATA >> $output_objdir/$libname.def~$echo " SINGLE NONSHARED" >> $output_objdir/$libname.def~$echo EXPORTS >> $output_objdir/$libname.def~emxexp $libobjs >> $output_objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $compiler_flags $output_objdir/$libname.def' - old_archive_From_new_cmds='emximp -o $output_objdir/$libname.a $output_objdir/$libname.def' - ;; - - osf3*) - if test "$GCC" = yes; then - allow_undefined_flag=' ${wl}-expect_unresolved ${wl}\*' - archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' - else - allow_undefined_flag=' -expect_unresolved \*' - archive_cmds='$LD -shared${allow_undefined_flag} $libobjs $deplibs $linker_flags -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib' - fi - hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' - hardcode_libdir_separator=: - ;; - - osf4* | osf5*) # as osf3* with the addition of -msym flag - if test "$GCC" = yes; then - allow_undefined_flag=' ${wl}-expect_unresolved ${wl}\*' - archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' - hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' - else - allow_undefined_flag=' -expect_unresolved \*' - archive_cmds='$LD -shared${allow_undefined_flag} $libobjs $deplibs $linker_flags -msym -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib' - archive_expsym_cmds='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; echo "-hidden">> $lib.exp~ - $LD -shared${allow_undefined_flag} -input $lib.exp $linker_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib~$rm $lib.exp' - - # Both c and cxx compiler support -rpath directly - hardcode_libdir_flag_spec='-rpath $libdir' - fi - hardcode_libdir_separator=: - ;; - - solaris*) - no_undefined_flag=' -z text' - if test "$GCC" = yes; then - wlarc='${wl}' - archive_cmds='$CC -shared ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' - archive_expsym_cmds='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~ - $CC -shared ${wl}-M ${wl}$lib.exp ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags~$rm $lib.exp' - else - wlarc='' - archive_cmds='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags' - archive_expsym_cmds='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~ - $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$rm $lib.exp' - fi - hardcode_libdir_flag_spec='-R$libdir' - hardcode_shlibpath_var=no - case $host_os in - solaris2.[0-5] | solaris2.[0-5].*) ;; - *) - # The compiler driver will combine linker options so we - # cannot just pass the convience library names through - # without $wl, iff we do not link with $LD. - # Luckily, gcc supports the same syntax we need for Sun Studio. - # Supported since Solaris 2.6 (maybe 2.5.1?) - case $wlarc in - '') - whole_archive_flag_spec='-z allextract$convenience -z defaultextract' ;; - *) - whole_archive_flag_spec='${wl}-z ${wl}allextract`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $echo \"$new_convenience\"` ${wl}-z ${wl}defaultextract' ;; - esac ;; - esac - link_all_deplibs=yes - ;; - - sunos4*) - if test "x$host_vendor" = xsequent; then - # Use $CC to link under sequent, because it throws in some extra .o - # files that make .init and .fini sections work. - archive_cmds='$CC -G ${wl}-h $soname -o $lib $libobjs $deplibs $compiler_flags' - else - archive_cmds='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags' - fi - hardcode_libdir_flag_spec='-L$libdir' - hardcode_direct=yes - hardcode_minus_L=yes - hardcode_shlibpath_var=no - ;; - - sysv4) - case $host_vendor in - sni) - archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - hardcode_direct=yes # is this really true??? - ;; - siemens) - ## LD is ld it makes a PLAMLIB - ## CC just makes a GrossModule. - archive_cmds='$LD -G -o $lib $libobjs $deplibs $linker_flags' - reload_cmds='$CC -r -o $output$reload_objs' - hardcode_direct=no - ;; - motorola) - archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - hardcode_direct=no #Motorola manual says yes, but my tests say they lie - ;; - esac - runpath_var='LD_RUN_PATH' - hardcode_shlibpath_var=no - ;; - - sysv4.3*) - archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - hardcode_shlibpath_var=no - export_dynamic_flag_spec='-Bexport' - ;; - - sysv4*MP*) - if test -d /usr/nec; then - archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - hardcode_shlibpath_var=no - runpath_var=LD_RUN_PATH - hardcode_runpath_var=yes - ld_shlibs=yes - fi - ;; - - sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7*) - no_undefined_flag='${wl}-z,text' - archive_cmds_need_lc=no - hardcode_shlibpath_var=no - runpath_var='LD_RUN_PATH' - - if test "$GCC" = yes; then - archive_cmds='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - archive_expsym_cmds='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - else - archive_cmds='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - archive_expsym_cmds='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - fi - ;; - - sysv5* | sco3.2v5* | sco5v6*) - # Note: We can NOT use -z defs as we might desire, because we do not - # link with -lc, and that would cause any symbols used from libc to - # always be unresolved, which means just about no library would - # ever link correctly. If we're not using GNU ld we use -z text - # though, which does catch some bad symbols but isn't as heavy-handed - # as -z defs. - no_undefined_flag='${wl}-z,text' - allow_undefined_flag='${wl}-z,nodefs' - archive_cmds_need_lc=no - hardcode_shlibpath_var=no - hardcode_libdir_flag_spec='`test -z "$SCOABSPATH" && echo ${wl}-R,$libdir`' - hardcode_libdir_separator=':' - link_all_deplibs=yes - export_dynamic_flag_spec='${wl}-Bexport' - runpath_var='LD_RUN_PATH' - - if test "$GCC" = yes; then - archive_cmds='$CC -shared ${wl}-h,\${SCOABSPATH:+${install_libdir}/}$soname -o $lib $libobjs $deplibs $compiler_flags' - archive_expsym_cmds='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,\${SCOABSPATH:+${install_libdir}/}$soname -o $lib $libobjs $deplibs $compiler_flags' - else - archive_cmds='$CC -G ${wl}-h,\${SCOABSPATH:+${install_libdir}/}$soname -o $lib $libobjs $deplibs $compiler_flags' - archive_expsym_cmds='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,\${SCOABSPATH:+${install_libdir}/}$soname -o $lib $libobjs $deplibs $compiler_flags' - fi - ;; - - uts4*) - archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - hardcode_libdir_flag_spec='-L$libdir' - hardcode_shlibpath_var=no - ;; - - *) - ld_shlibs=no - ;; - esac - fi - -echo "$as_me:$LINENO: result: $ld_shlibs" >&5 -echo "${ECHO_T}$ld_shlibs" >&6 -test "$ld_shlibs" = no && can_build_shared=no - -# -# Do we need to explicitly link libc? -# -case "x$archive_cmds_need_lc" in -x|xyes) - # Assume -lc should be added - archive_cmds_need_lc=yes - - if test "$enable_shared" = yes && test "$GCC" = yes; then - case $archive_cmds in - *'~'*) - # FIXME: we may have to deal with multi-command sequences. - ;; - '$CC '*) - # Test whether the compiler implicitly links with -lc since on some - # systems, -lgcc has to come before -lc. If gcc already passes -lc - # to ld, don't add -lc before -lgcc. - echo "$as_me:$LINENO: checking whether -lc should be explicitly linked in" >&5 -echo $ECHO_N "checking whether -lc should be explicitly linked in... $ECHO_C" >&6 - $rm conftest* - printf "$lt_simple_compile_test_code" > conftest.$ac_ext - - if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } 2>conftest.err; then - soname=conftest - lib=conftest - libobjs=conftest.$ac_objext - deplibs= - wl=$lt_prog_compiler_wl - pic_flag=$lt_prog_compiler_pic - compiler_flags=-v - linker_flags=-v - verstring= - output_objdir=. - libname=conftest - lt_save_allow_undefined_flag=$allow_undefined_flag - allow_undefined_flag= - if { (eval echo "$as_me:$LINENO: \"$archive_cmds 2\>\&1 \| grep \" -lc \" \>/dev/null 2\>\&1\"") >&5 - (eval $archive_cmds 2\>\&1 \| grep \" -lc \" \>/dev/null 2\>\&1) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } - then - archive_cmds_need_lc=no - else - archive_cmds_need_lc=yes - fi - allow_undefined_flag=$lt_save_allow_undefined_flag - else - cat conftest.err 1>&5 - fi - $rm conftest* - echo "$as_me:$LINENO: result: $archive_cmds_need_lc" >&5 -echo "${ECHO_T}$archive_cmds_need_lc" >&6 - ;; - esac - fi - ;; -esac - -echo "$as_me:$LINENO: checking dynamic linker characteristics" >&5 -echo $ECHO_N "checking dynamic linker characteristics... $ECHO_C" >&6 -library_names_spec= -libname_spec='lib$name' -soname_spec= -shrext_cmds=".so" -postinstall_cmds= -postuninstall_cmds= -finish_cmds= -finish_eval= -shlibpath_var= -shlibpath_overrides_runpath=unknown -version_type=none -dynamic_linker="$host_os ld.so" -sys_lib_dlsearch_path_spec="/lib /usr/lib" -if test "$GCC" = yes; then - sys_lib_search_path_spec=`$CC -print-search-dirs | grep "^libraries:" | $SED -e "s/^libraries://" -e "s,=/,/,g"` - if echo "$sys_lib_search_path_spec" | grep ';' >/dev/null ; then - # if the path contains ";" then we assume it to be the separator - # otherwise default to the standard path separator (i.e. ":") - it is - # assumed that no part of a normal pathname contains ";" but that should - # okay in the real world where ";" in dirpaths is itself problematic. - sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` - else - sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` - fi -else - sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" -fi -need_lib_prefix=unknown -hardcode_into_libs=no - -# when you set need_version to no, make sure it does not cause -set_version -# flags to be left without arguments -need_version=unknown - -case $host_os in -aix3*) - version_type=linux - library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a' - shlibpath_var=LIBPATH - - # AIX 3 has no versioning support, so we append a major version to the name. - soname_spec='${libname}${release}${shared_ext}$major' - ;; - -aix4* | aix5*) - version_type=linux - need_lib_prefix=no - need_version=no - hardcode_into_libs=yes - if test "$host_cpu" = ia64; then - # AIX 5 supports IA64 - library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}' - shlibpath_var=LD_LIBRARY_PATH - else - # With GCC up to 2.95.x, collect2 would create an import file - # for dependence libraries. The import file would start with - # the line `#! .'. This would cause the generated library to - # depend on `.', always an invalid library. This was fixed in - # development snapshots of GCC prior to 3.0. - case $host_os in - aix4 | aix4.[01] | aix4.[01].*) - if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' - echo ' yes ' - echo '#endif'; } | ${CC} -E - | grep yes > /dev/null; then - : - else - can_build_shared=no - fi - ;; - esac - # AIX (on Power*) has no versioning support, so currently we can not hardcode correct - # soname into executable. Probably we can add versioning support to - # collect2, so additional links can be useful in future. - if test "$aix_use_runtimelinking" = yes; then - # If using run time linking (on AIX 4.2 or later) use lib.so - # instead of lib.a to let people know that these are not - # typical AIX shared libraries. - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - else - # We preserve .a as extension for shared libraries through AIX4.2 - # and later when we are not doing run time linking. - library_names_spec='${libname}${release}.a $libname.a' - soname_spec='${libname}${release}${shared_ext}$major' - fi - shlibpath_var=LIBPATH - fi - ;; - -amigaos*) - library_names_spec='$libname.ixlibrary $libname.a' - # Create ${libname}_ixlibrary.a entries in /sys/libs. - finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`$echo "X$lib" | $Xsed -e '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; test $rm /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' - ;; - -beos*) - library_names_spec='${libname}${shared_ext}' - dynamic_linker="$host_os ld.so" - shlibpath_var=LIBRARY_PATH - ;; - -bsdi[45]*) - version_type=linux - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' - shlibpath_var=LD_LIBRARY_PATH - sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" - sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" - # the default ld.so.conf also contains /usr/contrib/lib and - # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow - # libtool to hard-code these into programs - ;; - -cygwin* | mingw* | pw32*) - version_type=windows - shrext_cmds=".dll" - need_version=no - need_lib_prefix=no - - case $GCC,$host_os in - yes,cygwin* | yes,mingw* | yes,pw32*) - library_names_spec='$libname.dll.a' - # DLL is installed to $(libdir)/../bin by postinstall_cmds - postinstall_cmds='base_file=`basename \${file}`~ - dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i;echo \$dlname'\''`~ - dldir=$destdir/`dirname \$dlpath`~ - test -d \$dldir || mkdir -p \$dldir~ - $install_prog $dir/$dlname \$dldir/$dlname~ - chmod a+x \$dldir/$dlname' - postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ - dlpath=$dir/\$dldll~ - $rm \$dlpath' - shlibpath_overrides_runpath=yes - - case $host_os in - cygwin*) - # Cygwin DLLs use 'cyg' prefix rather than 'lib' - soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' - sys_lib_search_path_spec="/usr/lib /lib/w32api /lib /usr/local/lib" - ;; - mingw*) - # MinGW DLLs use traditional 'lib' prefix - soname_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' - sys_lib_search_path_spec=`$CC -print-search-dirs | grep "^libraries:" | $SED -e "s/^libraries://" -e "s,=/,/,g"` - if echo "$sys_lib_search_path_spec" | grep ';[c-zC-Z]:/' >/dev/null; then - # It is most probably a Windows format PATH printed by - # mingw gcc, but we are running on Cygwin. Gcc prints its search - # path with ; separators, and with drive letters. We can handle the - # drive letters (cygwin fileutils understands them), so leave them, - # especially as we might pass files found there to a mingw objdump, - # which wouldn't understand a cygwinified path. Ahh. - sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` - else - sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` - fi - ;; - pw32*) - # pw32 DLLs use 'pw' prefix rather than 'lib' - library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' - ;; - esac - ;; - - *) - library_names_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext} $libname.lib' - ;; - esac - dynamic_linker='Win32 ld.exe' - # FIXME: first we should search . and the directory the executable is in - shlibpath_var=PATH - ;; - -darwin* | rhapsody*) - dynamic_linker="$host_os dyld" - version_type=darwin - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${versuffix}$shared_ext ${libname}${release}${major}$shared_ext ${libname}$shared_ext' - soname_spec='${libname}${release}${major}$shared_ext' - shlibpath_overrides_runpath=yes - shlibpath_var=DYLD_LIBRARY_PATH - shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' - # Apple's gcc prints 'gcc -print-search-dirs' doesn't operate the same. - if test "$GCC" = yes; then - sys_lib_search_path_spec=`$CC -print-search-dirs | tr "\n" "$PATH_SEPARATOR" | sed -e 's/libraries:/@libraries:/' | tr "@" "\n" | grep "^libraries:" | sed -e "s/^libraries://" -e "s,=/,/,g" -e "s,$PATH_SEPARATOR, ,g" -e "s,.*,& /lib /usr/lib /usr/local/lib,g"` - else - sys_lib_search_path_spec='/lib /usr/lib /usr/local/lib' - fi - sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' - ;; - -dgux*) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - ;; - -freebsd1*) - dynamic_linker=no - ;; - -kfreebsd*-gnu) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=no - hardcode_into_libs=yes - dynamic_linker='GNU ld.so' - ;; - -freebsd* | dragonfly*) - # DragonFly does not have aout. When/if they implement a new - # versioning mechanism, adjust this. - if test -x /usr/bin/objformat; then - objformat=`/usr/bin/objformat` - else - case $host_os in - freebsd[123]*) objformat=aout ;; - *) objformat=elf ;; - esac - fi - version_type=freebsd-$objformat - case $version_type in - freebsd-elf*) - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' - need_version=no - need_lib_prefix=no - ;; - freebsd-*) - library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix' - need_version=yes - ;; - esac - shlibpath_var=LD_LIBRARY_PATH - case $host_os in - freebsd2*) - shlibpath_overrides_runpath=yes - ;; - freebsd3.[01]* | freebsdelf3.[01]*) - shlibpath_overrides_runpath=yes - hardcode_into_libs=yes - ;; - freebsd3.[2-9]* | freebsdelf3.[2-9]* | \ - freebsd4.[0-5] | freebsdelf4.[0-5] | freebsd4.1.1 | freebsdelf4.1.1) - shlibpath_overrides_runpath=no - hardcode_into_libs=yes - ;; - freebsd*) # from 4.6 on - shlibpath_overrides_runpath=yes - hardcode_into_libs=yes - ;; - esac - ;; - -gnu*) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - hardcode_into_libs=yes - ;; - -hpux9* | hpux10* | hpux11*) - # Give a soname corresponding to the major version so that dld.sl refuses to - # link against other versions. - version_type=sunos - need_lib_prefix=no - need_version=no - case $host_cpu in - ia64*) - shrext_cmds='.so' - hardcode_into_libs=yes - dynamic_linker="$host_os dld.so" - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - if test "X$HPUX_IA64_MODE" = X32; then - sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" - else - sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" - fi - sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec - ;; - hppa*64*) - shrext_cmds='.sl' - hardcode_into_libs=yes - dynamic_linker="$host_os dld.sl" - shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH - shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" - sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec - ;; - *) - shrext_cmds='.sl' - dynamic_linker="$host_os dld.sl" - shlibpath_var=SHLIB_PATH - shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - ;; - esac - # HP-UX runs *really* slowly unless shared libraries are mode 555. - postinstall_cmds='chmod 555 $lib' - ;; - -interix3*) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=no - hardcode_into_libs=yes - ;; - -irix5* | irix6* | nonstopux*) - case $host_os in - nonstopux*) version_type=nonstopux ;; - *) - if test "$lt_cv_prog_gnu_ld" = yes; then - version_type=linux - else - version_type=irix - fi ;; - esac - need_lib_prefix=no - need_version=no - soname_spec='${libname}${release}${shared_ext}$major' - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}' - case $host_os in - irix5* | nonstopux*) - libsuff= shlibsuff= - ;; - *) - case $LD in # libtool.m4 will add one of these switches to LD - *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") - libsuff= shlibsuff= libmagic=32-bit;; - *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") - libsuff=32 shlibsuff=N32 libmagic=N32;; - *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") - libsuff=64 shlibsuff=64 libmagic=64-bit;; - *) libsuff= shlibsuff= libmagic=never-match;; - esac - ;; - esac - shlibpath_var=LD_LIBRARY${shlibsuff}_PATH - shlibpath_overrides_runpath=no - sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}" - sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}" - hardcode_into_libs=yes - ;; - -# No shared lib support for Linux oldld, aout, or coff. -linux*oldld* | linux*aout* | linux*coff*) - dynamic_linker=no - ;; - -# This must be Linux ELF. -linux*) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=no - # This implies no fast_install, which is unacceptable. - # Some rework will be needed to allow for fast_install - # before this can be enabled. - hardcode_into_libs=yes - - # Append ld.so.conf contents to the search path - if test -f /etc/ld.so.conf; then - lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;/^$/d' | tr '\n' ' '` - sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" - fi - - # We used to test for /lib/ld.so.1 and disable shared libraries on - # powerpc, because MkLinux only supported shared libraries with the - # GNU dynamic linker. Since this was broken with cross compilers, - # most powerpc-linux boxes support dynamic linking these days and - # people can always --disable-shared, the test was removed, and we - # assume the GNU/Linux dynamic linker is in use. - dynamic_linker='GNU/Linux ld.so' - ;; - -knetbsd*-gnu) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=no - hardcode_into_libs=yes - dynamic_linker='GNU ld.so' - ;; - -netbsd*) - version_type=sunos - need_lib_prefix=no - need_version=no - if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' - finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' - dynamic_linker='NetBSD (a.out) ld.so' - else - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - dynamic_linker='NetBSD ld.elf_so' - fi - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - hardcode_into_libs=yes - ;; - -newsos6) - version_type=linux - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - ;; - -nto-qnx*) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - ;; - -openbsd*) - version_type=sunos - sys_lib_dlsearch_path_spec="/usr/lib" - need_lib_prefix=no - # Some older versions of OpenBSD (3.3 at least) *do* need versioned libs. - case $host_os in - openbsd3.3 | openbsd3.3.*) need_version=yes ;; - *) need_version=no ;; - esac - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' - finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' - shlibpath_var=LD_LIBRARY_PATH - if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then - case $host_os in - openbsd2.[89] | openbsd2.[89].*) - shlibpath_overrides_runpath=no - ;; - *) - shlibpath_overrides_runpath=yes - ;; - esac - else - shlibpath_overrides_runpath=yes - fi - ;; - -os2*) - libname_spec='$name' - shrext_cmds=".dll" - need_lib_prefix=no - library_names_spec='$libname${shared_ext} $libname.a' - dynamic_linker='OS/2 ld.exe' - shlibpath_var=LIBPATH - ;; - -osf3* | osf4* | osf5*) - version_type=osf - need_lib_prefix=no - need_version=no - soname_spec='${libname}${release}${shared_ext}$major' - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - shlibpath_var=LD_LIBRARY_PATH - sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" - sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec" - ;; - -solaris*) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - hardcode_into_libs=yes - # ldd complains unless libraries are executable - postinstall_cmds='chmod +x $lib' - ;; - -sunos4*) - version_type=sunos - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' - finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - if test "$with_gnu_ld" = yes; then - need_lib_prefix=no - fi - need_version=yes - ;; - -sysv4 | sysv4.3*) - version_type=linux - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - case $host_vendor in - sni) - shlibpath_overrides_runpath=no - need_lib_prefix=no - export_dynamic_flag_spec='${wl}-Blargedynsym' - runpath_var=LD_RUN_PATH - ;; - siemens) - need_lib_prefix=no - ;; - motorola) - need_lib_prefix=no - need_version=no - shlibpath_overrides_runpath=no - sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' - ;; - esac - ;; - -sysv4*MP*) - if test -d /usr/nec ;then - version_type=linux - library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}' - soname_spec='$libname${shared_ext}.$major' - shlibpath_var=LD_LIBRARY_PATH - fi - ;; - -sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) - version_type=freebsd-elf - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - hardcode_into_libs=yes - if test "$with_gnu_ld" = yes; then - sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' - shlibpath_overrides_runpath=no - else - sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' - shlibpath_overrides_runpath=yes - case $host_os in - sco3.2v5*) - sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" - ;; - esac - fi - sys_lib_dlsearch_path_spec='/usr/lib' - ;; - -uts4*) - version_type=linux - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - ;; - -*) - dynamic_linker=no - ;; -esac -echo "$as_me:$LINENO: result: $dynamic_linker" >&5 -echo "${ECHO_T}$dynamic_linker" >&6 -test "$dynamic_linker" = no && can_build_shared=no - -variables_saved_for_relink="PATH $shlibpath_var $runpath_var" -if test "$GCC" = yes; then - variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" -fi - -echo "$as_me:$LINENO: checking how to hardcode library paths into programs" >&5 -echo $ECHO_N "checking how to hardcode library paths into programs... $ECHO_C" >&6 -hardcode_action= -if test -n "$hardcode_libdir_flag_spec" || \ - test -n "$runpath_var" || \ - test "X$hardcode_automatic" = "Xyes" ; then - - # We can hardcode non-existant directories. - if test "$hardcode_direct" != no && - # If the only mechanism to avoid hardcoding is shlibpath_var, we - # have to relink, otherwise we might link with an installed library - # when we should be linking with a yet-to-be-installed one - ## test "$_LT_AC_TAGVAR(hardcode_shlibpath_var, )" != no && - test "$hardcode_minus_L" != no; then - # Linking always hardcodes the temporary library directory. - hardcode_action=relink - else - # We can link without hardcoding, and we can hardcode nonexisting dirs. - hardcode_action=immediate - fi -else - # We cannot hardcode anything, or else we can only hardcode existing - # directories. - hardcode_action=unsupported -fi -echo "$as_me:$LINENO: result: $hardcode_action" >&5 -echo "${ECHO_T}$hardcode_action" >&6 - -if test "$hardcode_action" = relink; then - # Fast installation is not supported - enable_fast_install=no -elif test "$shlibpath_overrides_runpath" = yes || - test "$enable_shared" = no; then - # Fast installation is not necessary - enable_fast_install=needless -fi - -striplib= -old_striplib= -echo "$as_me:$LINENO: checking whether stripping libraries is possible" >&5 -echo $ECHO_N "checking whether stripping libraries is possible... $ECHO_C" >&6 -if test -n "$STRIP" && $STRIP -V 2>&1 | grep "GNU strip" >/dev/null; then - test -z "$old_striplib" && old_striplib="$STRIP --strip-debug" - test -z "$striplib" && striplib="$STRIP --strip-unneeded" - echo "$as_me:$LINENO: result: yes" >&5 -echo "${ECHO_T}yes" >&6 -else -# FIXME - insert some real tests, host_os isn't really good enough - case $host_os in - darwin*) - if test -n "$STRIP" ; then - striplib="$STRIP -x" - echo "$as_me:$LINENO: result: yes" >&5 -echo "${ECHO_T}yes" >&6 - else - echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6 -fi - ;; - *) - echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6 - ;; - esac -fi - -if test "x$enable_dlopen" != xyes; then - enable_dlopen=unknown - enable_dlopen_self=unknown - enable_dlopen_self_static=unknown -else - lt_cv_dlopen=no - lt_cv_dlopen_libs= - - case $host_os in - beos*) - lt_cv_dlopen="load_add_on" - lt_cv_dlopen_libs= - lt_cv_dlopen_self=yes - ;; - - mingw* | pw32*) - lt_cv_dlopen="LoadLibrary" - lt_cv_dlopen_libs= - ;; - - cygwin*) - lt_cv_dlopen="dlopen" - lt_cv_dlopen_libs= - ;; - - darwin*) - # if libdl is installed we need to link against it - echo "$as_me:$LINENO: checking for dlopen in -ldl" >&5 -echo $ECHO_N "checking for dlopen in -ldl... $ECHO_C" >&6 -if test "${ac_cv_lib_dl_dlopen+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-ldl $LIBS" -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ - -/* Override any gcc2 internal prototype to avoid an error. */ -#ifdef __cplusplus -extern "C" -#endif -/* We use char because int might match the return type of a gcc2 - builtin and then its argument prototype would still apply. */ -char dlopen (); -int -main () -{ -dlopen (); - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 - (eval $ac_link) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest$ac_exeext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_cv_lib_dl_dlopen=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -ac_cv_lib_dl_dlopen=no -fi -rm -f conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -echo "$as_me:$LINENO: result: $ac_cv_lib_dl_dlopen" >&5 -echo "${ECHO_T}$ac_cv_lib_dl_dlopen" >&6 -if test $ac_cv_lib_dl_dlopen = yes; then - lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl" -else - - lt_cv_dlopen="dyld" - lt_cv_dlopen_libs= - lt_cv_dlopen_self=yes - -fi - - ;; - - *) - echo "$as_me:$LINENO: checking for shl_load" >&5 -echo $ECHO_N "checking for shl_load... $ECHO_C" >&6 -if test "${ac_cv_func_shl_load+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -/* Define shl_load to an innocuous variant, in case declares shl_load. - For example, HP-UX 11i declares gettimeofday. */ -#define shl_load innocuous_shl_load - -/* System header to define __stub macros and hopefully few prototypes, - which can conflict with char shl_load (); below. - Prefer to if __STDC__ is defined, since - exists even on freestanding compilers. */ - -#ifdef __STDC__ -# include -#else -# include -#endif - -#undef shl_load - -/* Override any gcc2 internal prototype to avoid an error. */ -#ifdef __cplusplus -extern "C" -{ -#endif -/* We use char because int might match the return type of a gcc2 - builtin and then its argument prototype would still apply. */ -char shl_load (); -/* The GNU C library defines this for functions which it implements - to always fail with ENOSYS. Some functions are actually named - something starting with __ and the normal name is an alias. */ -#if defined (__stub_shl_load) || defined (__stub___shl_load) -choke me -#else -char (*f) () = shl_load; -#endif -#ifdef __cplusplus -} -#endif - -int -main () -{ -return f != shl_load; - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 - (eval $ac_link) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest$ac_exeext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_cv_func_shl_load=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -ac_cv_func_shl_load=no -fi -rm -f conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -fi -echo "$as_me:$LINENO: result: $ac_cv_func_shl_load" >&5 -echo "${ECHO_T}$ac_cv_func_shl_load" >&6 -if test $ac_cv_func_shl_load = yes; then - lt_cv_dlopen="shl_load" -else - echo "$as_me:$LINENO: checking for shl_load in -ldld" >&5 -echo $ECHO_N "checking for shl_load in -ldld... $ECHO_C" >&6 -if test "${ac_cv_lib_dld_shl_load+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-ldld $LIBS" -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ - -/* Override any gcc2 internal prototype to avoid an error. */ -#ifdef __cplusplus -extern "C" -#endif -/* We use char because int might match the return type of a gcc2 - builtin and then its argument prototype would still apply. */ -char shl_load (); -int -main () -{ -shl_load (); - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 - (eval $ac_link) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest$ac_exeext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_cv_lib_dld_shl_load=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -ac_cv_lib_dld_shl_load=no -fi -rm -f conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -echo "$as_me:$LINENO: result: $ac_cv_lib_dld_shl_load" >&5 -echo "${ECHO_T}$ac_cv_lib_dld_shl_load" >&6 -if test $ac_cv_lib_dld_shl_load = yes; then - lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-dld" -else - echo "$as_me:$LINENO: checking for dlopen" >&5 -echo $ECHO_N "checking for dlopen... $ECHO_C" >&6 -if test "${ac_cv_func_dlopen+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -/* Define dlopen to an innocuous variant, in case declares dlopen. - For example, HP-UX 11i declares gettimeofday. */ -#define dlopen innocuous_dlopen - -/* System header to define __stub macros and hopefully few prototypes, - which can conflict with char dlopen (); below. - Prefer to if __STDC__ is defined, since - exists even on freestanding compilers. */ - -#ifdef __STDC__ -# include -#else -# include -#endif - -#undef dlopen - -/* Override any gcc2 internal prototype to avoid an error. */ -#ifdef __cplusplus -extern "C" -{ -#endif -/* We use char because int might match the return type of a gcc2 - builtin and then its argument prototype would still apply. */ -char dlopen (); -/* The GNU C library defines this for functions which it implements - to always fail with ENOSYS. Some functions are actually named - something starting with __ and the normal name is an alias. */ -#if defined (__stub_dlopen) || defined (__stub___dlopen) -choke me -#else -char (*f) () = dlopen; -#endif -#ifdef __cplusplus -} -#endif - -int -main () -{ -return f != dlopen; - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 - (eval $ac_link) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest$ac_exeext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_cv_func_dlopen=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -ac_cv_func_dlopen=no -fi -rm -f conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -fi -echo "$as_me:$LINENO: result: $ac_cv_func_dlopen" >&5 -echo "${ECHO_T}$ac_cv_func_dlopen" >&6 -if test $ac_cv_func_dlopen = yes; then - lt_cv_dlopen="dlopen" -else - echo "$as_me:$LINENO: checking for dlopen in -ldl" >&5 -echo $ECHO_N "checking for dlopen in -ldl... $ECHO_C" >&6 -if test "${ac_cv_lib_dl_dlopen+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-ldl $LIBS" -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ - -/* Override any gcc2 internal prototype to avoid an error. */ -#ifdef __cplusplus -extern "C" -#endif -/* We use char because int might match the return type of a gcc2 - builtin and then its argument prototype would still apply. */ -char dlopen (); -int -main () -{ -dlopen (); - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 - (eval $ac_link) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest$ac_exeext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_cv_lib_dl_dlopen=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -ac_cv_lib_dl_dlopen=no -fi -rm -f conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -echo "$as_me:$LINENO: result: $ac_cv_lib_dl_dlopen" >&5 -echo "${ECHO_T}$ac_cv_lib_dl_dlopen" >&6 -if test $ac_cv_lib_dl_dlopen = yes; then - lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl" -else - echo "$as_me:$LINENO: checking for dlopen in -lsvld" >&5 -echo $ECHO_N "checking for dlopen in -lsvld... $ECHO_C" >&6 -if test "${ac_cv_lib_svld_dlopen+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-lsvld $LIBS" -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ - -/* Override any gcc2 internal prototype to avoid an error. */ -#ifdef __cplusplus -extern "C" -#endif -/* We use char because int might match the return type of a gcc2 - builtin and then its argument prototype would still apply. */ -char dlopen (); -int -main () -{ -dlopen (); - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 - (eval $ac_link) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest$ac_exeext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_cv_lib_svld_dlopen=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -ac_cv_lib_svld_dlopen=no -fi -rm -f conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -echo "$as_me:$LINENO: result: $ac_cv_lib_svld_dlopen" >&5 -echo "${ECHO_T}$ac_cv_lib_svld_dlopen" >&6 -if test $ac_cv_lib_svld_dlopen = yes; then - lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-lsvld" -else - echo "$as_me:$LINENO: checking for dld_link in -ldld" >&5 -echo $ECHO_N "checking for dld_link in -ldld... $ECHO_C" >&6 -if test "${ac_cv_lib_dld_dld_link+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-ldld $LIBS" -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ - -/* Override any gcc2 internal prototype to avoid an error. */ -#ifdef __cplusplus -extern "C" -#endif -/* We use char because int might match the return type of a gcc2 - builtin and then its argument prototype would still apply. */ -char dld_link (); -int -main () -{ -dld_link (); - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 - (eval $ac_link) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest$ac_exeext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_cv_lib_dld_dld_link=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -ac_cv_lib_dld_dld_link=no -fi -rm -f conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -echo "$as_me:$LINENO: result: $ac_cv_lib_dld_dld_link" >&5 -echo "${ECHO_T}$ac_cv_lib_dld_dld_link" >&6 -if test $ac_cv_lib_dld_dld_link = yes; then - lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-dld" -fi - - -fi - - -fi - - -fi - - -fi - - -fi - - ;; - esac - - if test "x$lt_cv_dlopen" != xno; then - enable_dlopen=yes - else - enable_dlopen=no - fi - - case $lt_cv_dlopen in - dlopen) - save_CPPFLAGS="$CPPFLAGS" - test "x$ac_cv_header_dlfcn_h" = xyes && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H" - - save_LDFLAGS="$LDFLAGS" - wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\" - - save_LIBS="$LIBS" - LIBS="$lt_cv_dlopen_libs $LIBS" - - echo "$as_me:$LINENO: checking whether a program can dlopen itself" >&5 -echo $ECHO_N "checking whether a program can dlopen itself... $ECHO_C" >&6 -if test "${lt_cv_dlopen_self+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test "$cross_compiling" = yes; then : - lt_cv_dlopen_self=cross -else - lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 - lt_status=$lt_dlunknown - cat > conftest.$ac_ext < -#endif - -#include - -#ifdef RTLD_GLOBAL -# define LT_DLGLOBAL RTLD_GLOBAL -#else -# ifdef DL_GLOBAL -# define LT_DLGLOBAL DL_GLOBAL -# else -# define LT_DLGLOBAL 0 -# endif -#endif - -/* We may have to define LT_DLLAZY_OR_NOW in the command line if we - find out it does not work in some platform. */ -#ifndef LT_DLLAZY_OR_NOW -# ifdef RTLD_LAZY -# define LT_DLLAZY_OR_NOW RTLD_LAZY -# else -# ifdef DL_LAZY -# define LT_DLLAZY_OR_NOW DL_LAZY -# else -# ifdef RTLD_NOW -# define LT_DLLAZY_OR_NOW RTLD_NOW -# else -# ifdef DL_NOW -# define LT_DLLAZY_OR_NOW DL_NOW -# else -# define LT_DLLAZY_OR_NOW 0 -# endif -# endif -# endif -# endif -#endif - -#ifdef __cplusplus -extern "C" void exit (int); -#endif - -void fnord() { int i=42;} -int main () -{ - void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); - int status = $lt_dlunknown; - - if (self) - { - if (dlsym (self,"fnord")) status = $lt_dlno_uscore; - else if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; - /* dlclose (self); */ - } - else - puts (dlerror ()); - - exit (status); -} -EOF - if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 - (eval $ac_link) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && test -s conftest${ac_exeext} 2>/dev/null; then - (./conftest; exit; ) >&5 2>/dev/null - lt_status=$? - case x$lt_status in - x$lt_dlno_uscore) lt_cv_dlopen_self=yes ;; - x$lt_dlneed_uscore) lt_cv_dlopen_self=yes ;; - x$lt_dlunknown|x*) lt_cv_dlopen_self=no ;; - esac - else : - # compilation failed - lt_cv_dlopen_self=no - fi -fi -rm -fr conftest* - - -fi -echo "$as_me:$LINENO: result: $lt_cv_dlopen_self" >&5 -echo "${ECHO_T}$lt_cv_dlopen_self" >&6 - - if test "x$lt_cv_dlopen_self" = xyes; then - wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\" - echo "$as_me:$LINENO: checking whether a statically linked program can dlopen itself" >&5 -echo $ECHO_N "checking whether a statically linked program can dlopen itself... $ECHO_C" >&6 -if test "${lt_cv_dlopen_self_static+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test "$cross_compiling" = yes; then : - lt_cv_dlopen_self_static=cross -else - lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 - lt_status=$lt_dlunknown - cat > conftest.$ac_ext < -#endif - -#include - -#ifdef RTLD_GLOBAL -# define LT_DLGLOBAL RTLD_GLOBAL -#else -# ifdef DL_GLOBAL -# define LT_DLGLOBAL DL_GLOBAL -# else -# define LT_DLGLOBAL 0 -# endif -#endif - -/* We may have to define LT_DLLAZY_OR_NOW in the command line if we - find out it does not work in some platform. */ -#ifndef LT_DLLAZY_OR_NOW -# ifdef RTLD_LAZY -# define LT_DLLAZY_OR_NOW RTLD_LAZY -# else -# ifdef DL_LAZY -# define LT_DLLAZY_OR_NOW DL_LAZY -# else -# ifdef RTLD_NOW -# define LT_DLLAZY_OR_NOW RTLD_NOW -# else -# ifdef DL_NOW -# define LT_DLLAZY_OR_NOW DL_NOW -# else -# define LT_DLLAZY_OR_NOW 0 -# endif -# endif -# endif -# endif -#endif - -#ifdef __cplusplus -extern "C" void exit (int); -#endif - -void fnord() { int i=42;} -int main () -{ - void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); - int status = $lt_dlunknown; - - if (self) - { - if (dlsym (self,"fnord")) status = $lt_dlno_uscore; - else if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; - /* dlclose (self); */ - } - else - puts (dlerror ()); - - exit (status); -} -EOF - if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 - (eval $ac_link) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && test -s conftest${ac_exeext} 2>/dev/null; then - (./conftest; exit; ) >&5 2>/dev/null - lt_status=$? - case x$lt_status in - x$lt_dlno_uscore) lt_cv_dlopen_self_static=yes ;; - x$lt_dlneed_uscore) lt_cv_dlopen_self_static=yes ;; - x$lt_dlunknown|x*) lt_cv_dlopen_self_static=no ;; - esac - else : - # compilation failed - lt_cv_dlopen_self_static=no - fi -fi -rm -fr conftest* - - -fi -echo "$as_me:$LINENO: result: $lt_cv_dlopen_self_static" >&5 -echo "${ECHO_T}$lt_cv_dlopen_self_static" >&6 - fi - - CPPFLAGS="$save_CPPFLAGS" - LDFLAGS="$save_LDFLAGS" - LIBS="$save_LIBS" - ;; - esac - - case $lt_cv_dlopen_self in - yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;; - *) enable_dlopen_self=unknown ;; - esac - - case $lt_cv_dlopen_self_static in - yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;; - *) enable_dlopen_self_static=unknown ;; - esac -fi - - -# Report which library types will actually be built -echo "$as_me:$LINENO: checking if libtool supports shared libraries" >&5 -echo $ECHO_N "checking if libtool supports shared libraries... $ECHO_C" >&6 -echo "$as_me:$LINENO: result: $can_build_shared" >&5 -echo "${ECHO_T}$can_build_shared" >&6 - -echo "$as_me:$LINENO: checking whether to build shared libraries" >&5 -echo $ECHO_N "checking whether to build shared libraries... $ECHO_C" >&6 -test "$can_build_shared" = "no" && enable_shared=no - -# On AIX, shared libraries and static libraries use the same namespace, and -# are all built from PIC. -case $host_os in -aix3*) - test "$enable_shared" = yes && enable_static=no - if test -n "$RANLIB"; then - archive_cmds="$archive_cmds~\$RANLIB \$lib" - postinstall_cmds='$RANLIB $lib' - fi - ;; - -aix4* | aix5*) - if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then - test "$enable_shared" = yes && enable_static=no - fi - ;; -esac -echo "$as_me:$LINENO: result: $enable_shared" >&5 -echo "${ECHO_T}$enable_shared" >&6 - -echo "$as_me:$LINENO: checking whether to build static libraries" >&5 -echo $ECHO_N "checking whether to build static libraries... $ECHO_C" >&6 -# Make sure either enable_shared or enable_static is yes. -test "$enable_shared" = yes || enable_static=yes -echo "$as_me:$LINENO: result: $enable_static" >&5 -echo "${ECHO_T}$enable_static" >&6 - -# The else clause should only fire when bootstrapping the -# libtool distribution, otherwise you forgot to ship ltmain.sh -# with your package, and you will get complaints that there are -# no rules to generate ltmain.sh. -if test -f "$ltmain"; then - # See if we are running on zsh, and set the options which allow our commands through - # without removal of \ escapes. - if test -n "${ZSH_VERSION+set}" ; then - setopt NO_GLOB_SUBST - fi - # Now quote all the things that may contain metacharacters while being - # careful not to overquote the AC_SUBSTed values. We take copies of the - # variables and quote the copies for generation of the libtool script. - for var in echo old_CC old_CFLAGS AR AR_FLAGS EGREP RANLIB LN_S LTCC LTCFLAGS NM \ - SED SHELL STRIP \ - libname_spec library_names_spec soname_spec extract_expsyms_cmds \ - old_striplib striplib file_magic_cmd finish_cmds finish_eval \ - deplibs_check_method reload_flag reload_cmds need_locks \ - lt_cv_sys_global_symbol_pipe lt_cv_sys_global_symbol_to_cdecl \ - lt_cv_sys_global_symbol_to_c_name_address \ - sys_lib_search_path_spec sys_lib_dlsearch_path_spec \ - old_postinstall_cmds old_postuninstall_cmds \ - compiler \ - CC \ - LD \ - lt_prog_compiler_wl \ - lt_prog_compiler_pic \ - lt_prog_compiler_static \ - lt_prog_compiler_no_builtin_flag \ - export_dynamic_flag_spec \ - thread_safe_flag_spec \ - whole_archive_flag_spec \ - enable_shared_with_static_runtimes \ - old_archive_cmds \ - old_archive_from_new_cmds \ - predep_objects \ - postdep_objects \ - predeps \ - postdeps \ - compiler_lib_search_path \ - archive_cmds \ - archive_expsym_cmds \ - postinstall_cmds \ - postuninstall_cmds \ - old_archive_from_expsyms_cmds \ - allow_undefined_flag \ - no_undefined_flag \ - export_symbols_cmds \ - hardcode_libdir_flag_spec \ - hardcode_libdir_flag_spec_ld \ - hardcode_libdir_separator \ - hardcode_automatic \ - module_cmds \ - module_expsym_cmds \ - lt_cv_prog_compiler_c_o \ - exclude_expsyms \ - include_expsyms; do - - case $var in - old_archive_cmds | \ - old_archive_from_new_cmds | \ - archive_cmds | \ - archive_expsym_cmds | \ - module_cmds | \ - module_expsym_cmds | \ - old_archive_from_expsyms_cmds | \ - export_symbols_cmds | \ - extract_expsyms_cmds | reload_cmds | finish_cmds | \ - postinstall_cmds | postuninstall_cmds | \ - old_postinstall_cmds | old_postuninstall_cmds | \ - sys_lib_search_path_spec | sys_lib_dlsearch_path_spec) - # Double-quote double-evaled strings. - eval "lt_$var=\\\"\`\$echo \"X\$$var\" | \$Xsed -e \"\$double_quote_subst\" -e \"\$sed_quote_subst\" -e \"\$delay_variable_subst\"\`\\\"" - ;; - *) - eval "lt_$var=\\\"\`\$echo \"X\$$var\" | \$Xsed -e \"\$sed_quote_subst\"\`\\\"" - ;; - esac - done - - case $lt_echo in - *'\$0 --fallback-echo"') - lt_echo=`$echo "X$lt_echo" | $Xsed -e 's/\\\\\\\$0 --fallback-echo"$/$0 --fallback-echo"/'` - ;; - esac - -cfgfile="${ofile}T" - trap "$rm \"$cfgfile\"; exit 1" 1 2 15 - $rm -f "$cfgfile" - { echo "$as_me:$LINENO: creating $ofile" >&5 -echo "$as_me: creating $ofile" >&6;} - - cat <<__EOF__ >> "$cfgfile" -#! $SHELL - -# `$echo "$cfgfile" | sed 's%^.*/%%'` - Provide generalized library-building support services. -# Generated automatically by $PROGRAM (GNU $PACKAGE $VERSION$TIMESTAMP) -# NOTE: Changes made to this file will be lost: look at ltmain.sh. -# -# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001 -# Free Software Foundation, Inc. -# -# This file is part of GNU Libtool: -# Originally by Gordon Matzigkeit , 1996 -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# As a special exception to the GNU General Public License, if you -# distribute this file as part of a program that contains a -# configuration script generated by Autoconf, you may include it under -# the same distribution terms that you use for the rest of that program. - -# A sed program that does not truncate output. -SED=$lt_SED - -# Sed that helps us avoid accidentally triggering echo(1) options like -n. -Xsed="$SED -e 1s/^X//" - -# The HP-UX ksh and POSIX shell print the target directory to stdout -# if CDPATH is set. -(unset CDPATH) >/dev/null 2>&1 && unset CDPATH - -# The names of the tagged configurations supported by this script. -available_tags= - -# ### BEGIN LIBTOOL CONFIG - -# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`: - -# Shell to use when invoking shell scripts. -SHELL=$lt_SHELL - -# Whether or not to build shared libraries. -build_libtool_libs=$enable_shared - -# Whether or not to build static libraries. -build_old_libs=$enable_static - -# Whether or not to add -lc for building shared libraries. -build_libtool_need_lc=$archive_cmds_need_lc - -# Whether or not to disallow shared libs when runtime libs are static -allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes - -# Whether or not to optimize for fast installation. -fast_install=$enable_fast_install - -# The host system. -host_alias=$host_alias -host=$host -host_os=$host_os - -# The build system. -build_alias=$build_alias -build=$build -build_os=$build_os - -# An echo program that does not interpret backslashes. -echo=$lt_echo - -# The archiver. -AR=$lt_AR -AR_FLAGS=$lt_AR_FLAGS - -# A C compiler. -LTCC=$lt_LTCC - -# LTCC compiler flags. -LTCFLAGS=$lt_LTCFLAGS - -# A language-specific compiler. -CC=$lt_compiler - -# Is the compiler the GNU C compiler? -with_gcc=$GCC - -# An ERE matcher. -EGREP=$lt_EGREP - -# The linker used to build libraries. -LD=$lt_LD - -# Whether we need hard or soft links. -LN_S=$lt_LN_S - -# A BSD-compatible nm program. -NM=$lt_NM - -# A symbol stripping program -STRIP=$lt_STRIP - -# Used to examine libraries when file_magic_cmd begins "file" -MAGIC_CMD=$MAGIC_CMD - -# Used on cygwin: DLL creation program. -DLLTOOL="$DLLTOOL" - -# Used on cygwin: object dumper. -OBJDUMP="$OBJDUMP" - -# Used on cygwin: assembler. -AS="$AS" - -# The name of the directory that contains temporary libtool files. -objdir=$objdir - -# How to create reloadable object files. -reload_flag=$lt_reload_flag -reload_cmds=$lt_reload_cmds - -# How to pass a linker flag through the compiler. -wl=$lt_lt_prog_compiler_wl - -# Object file suffix (normally "o"). -objext="$ac_objext" - -# Old archive suffix (normally "a"). -libext="$libext" - -# Shared library suffix (normally ".so"). -shrext_cmds='$shrext_cmds' - -# Executable file suffix (normally ""). -exeext="$exeext" - -# Additional compiler flags for building library objects. -pic_flag=$lt_lt_prog_compiler_pic -pic_mode=$pic_mode - -# What is the maximum length of a command? -max_cmd_len=$lt_cv_sys_max_cmd_len - -# Does compiler simultaneously support -c and -o options? -compiler_c_o=$lt_lt_cv_prog_compiler_c_o - -# Must we lock files when doing compilation? -need_locks=$lt_need_locks - -# Do we need the lib prefix for modules? -need_lib_prefix=$need_lib_prefix - -# Do we need a version for libraries? -need_version=$need_version - -# Whether dlopen is supported. -dlopen_support=$enable_dlopen - -# Whether dlopen of programs is supported. -dlopen_self=$enable_dlopen_self - -# Whether dlopen of statically linked programs is supported. -dlopen_self_static=$enable_dlopen_self_static - -# Compiler flag to prevent dynamic linking. -link_static_flag=$lt_lt_prog_compiler_static - -# Compiler flag to turn off builtin functions. -no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag - -# Compiler flag to allow reflexive dlopens. -export_dynamic_flag_spec=$lt_export_dynamic_flag_spec - -# Compiler flag to generate shared objects directly from archives. -whole_archive_flag_spec=$lt_whole_archive_flag_spec - -# Compiler flag to generate thread-safe objects. -thread_safe_flag_spec=$lt_thread_safe_flag_spec - -# Library versioning type. -version_type=$version_type - -# Format of library name prefix. -libname_spec=$lt_libname_spec - -# List of archive names. First name is the real one, the rest are links. -# The last name is the one that the linker finds with -lNAME. -library_names_spec=$lt_library_names_spec - -# The coded name of the library, if different from the real name. -soname_spec=$lt_soname_spec - -# Commands used to build and install an old-style archive. -RANLIB=$lt_RANLIB -old_archive_cmds=$lt_old_archive_cmds -old_postinstall_cmds=$lt_old_postinstall_cmds -old_postuninstall_cmds=$lt_old_postuninstall_cmds - -# Create an old-style archive from a shared archive. -old_archive_from_new_cmds=$lt_old_archive_from_new_cmds - -# Create a temporary old-style archive to link instead of a shared archive. -old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds - -# Commands used to build and install a shared archive. -archive_cmds=$lt_archive_cmds -archive_expsym_cmds=$lt_archive_expsym_cmds -postinstall_cmds=$lt_postinstall_cmds -postuninstall_cmds=$lt_postuninstall_cmds - -# Commands used to build a loadable module (assumed same as above if empty) -module_cmds=$lt_module_cmds -module_expsym_cmds=$lt_module_expsym_cmds - -# Commands to strip libraries. -old_striplib=$lt_old_striplib -striplib=$lt_striplib - -# Dependencies to place before the objects being linked to create a -# shared library. -predep_objects=$lt_predep_objects - -# Dependencies to place after the objects being linked to create a -# shared library. -postdep_objects=$lt_postdep_objects - -# Dependencies to place before the objects being linked to create a -# shared library. -predeps=$lt_predeps - -# Dependencies to place after the objects being linked to create a -# shared library. -postdeps=$lt_postdeps - -# The library search path used internally by the compiler when linking -# a shared library. -compiler_lib_search_path=$lt_compiler_lib_search_path - -# Method to check whether dependent libraries are shared objects. -deplibs_check_method=$lt_deplibs_check_method - -# Command to use when deplibs_check_method == file_magic. -file_magic_cmd=$lt_file_magic_cmd - -# Flag that allows shared libraries with undefined symbols to be built. -allow_undefined_flag=$lt_allow_undefined_flag - -# Flag that forces no undefined symbols. -no_undefined_flag=$lt_no_undefined_flag - -# Commands used to finish a libtool library installation in a directory. -finish_cmds=$lt_finish_cmds - -# Same as above, but a single script fragment to be evaled but not shown. -finish_eval=$lt_finish_eval - -# Take the output of nm and produce a listing of raw symbols and C names. -global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe - -# Transform the output of nm in a proper C declaration -global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl - -# Transform the output of nm in a C name address pair -global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address - -# This is the shared library runtime path variable. -runpath_var=$runpath_var - -# This is the shared library path variable. -shlibpath_var=$shlibpath_var - -# Is shlibpath searched before the hard-coded library search path? -shlibpath_overrides_runpath=$shlibpath_overrides_runpath - -# How to hardcode a shared library path into an executable. -hardcode_action=$hardcode_action - -# Whether we should hardcode library paths into libraries. -hardcode_into_libs=$hardcode_into_libs - -# Flag to hardcode \$libdir into a binary during linking. -# This must work even if \$libdir does not exist. -hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec - -# If ld is used when linking, flag to hardcode \$libdir into -# a binary during linking. This must work even if \$libdir does -# not exist. -hardcode_libdir_flag_spec_ld=$lt_hardcode_libdir_flag_spec_ld - -# Whether we need a single -rpath flag with a separated argument. -hardcode_libdir_separator=$lt_hardcode_libdir_separator - -# Set to yes if using DIR/libNAME${shared_ext} during linking hardcodes DIR into the -# resulting binary. -hardcode_direct=$hardcode_direct - -# Set to yes if using the -LDIR flag during linking hardcodes DIR into the -# resulting binary. -hardcode_minus_L=$hardcode_minus_L - -# Set to yes if using SHLIBPATH_VAR=DIR during linking hardcodes DIR into -# the resulting binary. -hardcode_shlibpath_var=$hardcode_shlibpath_var - -# Set to yes if building a shared library automatically hardcodes DIR into the library -# and all subsequent libraries and executables linked against it. -hardcode_automatic=$hardcode_automatic - -# Variables whose values should be saved in libtool wrapper scripts and -# restored at relink time. -variables_saved_for_relink="$variables_saved_for_relink" - -# Whether libtool must link a program against all its dependency libraries. -link_all_deplibs=$link_all_deplibs - -# Compile-time system search path for libraries -sys_lib_search_path_spec=$lt_sys_lib_search_path_spec - -# Run-time system search path for libraries -sys_lib_dlsearch_path_spec=$lt_sys_lib_dlsearch_path_spec - -# Fix the shell variable \$srcfile for the compiler. -fix_srcfile_path="$fix_srcfile_path" - -# Set to yes if exported symbols are required. -always_export_symbols=$always_export_symbols - -# The commands to list exported symbols. -export_symbols_cmds=$lt_export_symbols_cmds - -# The commands to extract the exported symbol list from a shared archive. -extract_expsyms_cmds=$lt_extract_expsyms_cmds - -# Symbols that should not be listed in the preloaded symbols. -exclude_expsyms=$lt_exclude_expsyms - -# Symbols that must always be exported. -include_expsyms=$lt_include_expsyms - -# ### END LIBTOOL CONFIG - -__EOF__ - - - case $host_os in - aix3*) - cat <<\EOF >> "$cfgfile" - -# AIX sometimes has problems with the GCC collect2 program. For some -# reason, if we set the COLLECT_NAMES environment variable, the problems -# vanish in a puff of smoke. -if test "X${COLLECT_NAMES+set}" != Xset; then - COLLECT_NAMES= - export COLLECT_NAMES -fi -EOF - ;; - esac - - # We use sed instead of cat because bash on DJGPP gets confused if - # if finds mixed CR/LF and LF-only lines. Since sed operates in - # text mode, it properly converts lines to CR/LF. This bash problem - # is reportedly fixed, but why not run on old versions too? - sed '$q' "$ltmain" >> "$cfgfile" || (rm -f "$cfgfile"; exit 1) - - mv -f "$cfgfile" "$ofile" || \ - (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile") - chmod +x "$ofile" - -else - # If there is no Makefile yet, we rely on a make rule to execute - # `config.status --recheck' to rerun these tests and create the - # libtool script then. - ltmain_in=`echo $ltmain | sed -e 's/\.sh$/.in/'` - if test -f "$ltmain_in"; then - test -f Makefile && make "$ltmain" - fi -fi - - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - -CC="$lt_save_CC" - - -# Check whether --with-tags or --without-tags was given. -if test "${with_tags+set}" = set; then - withval="$with_tags" - tagnames="$withval" -fi; - -if test -f "$ltmain" && test -n "$tagnames"; then - if test ! -f "${ofile}"; then - { echo "$as_me:$LINENO: WARNING: output file \`$ofile' does not exist" >&5 -echo "$as_me: WARNING: output file \`$ofile' does not exist" >&2;} - fi - - if test -z "$LTCC"; then - eval "`$SHELL ${ofile} --config | grep '^LTCC='`" - if test -z "$LTCC"; then - { echo "$as_me:$LINENO: WARNING: output file \`$ofile' does not look like a libtool script" >&5 -echo "$as_me: WARNING: output file \`$ofile' does not look like a libtool script" >&2;} - else - { echo "$as_me:$LINENO: WARNING: using \`LTCC=$LTCC', extracted from \`$ofile'" >&5 -echo "$as_me: WARNING: using \`LTCC=$LTCC', extracted from \`$ofile'" >&2;} - fi - fi - if test -z "$LTCFLAGS"; then - eval "`$SHELL ${ofile} --config | grep '^LTCFLAGS='`" - fi - - # Extract list of available tagged configurations in $ofile. - # Note that this assumes the entire list is on one line. - available_tags=`grep "^available_tags=" "${ofile}" | $SED -e 's/available_tags=\(.*$\)/\1/' -e 's/\"//g'` - - lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," - for tagname in $tagnames; do - IFS="$lt_save_ifs" - # Check whether tagname contains only valid characters - case `$echo "X$tagname" | $Xsed -e 's:[-_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890,/]::g'` in - "") ;; - *) { { echo "$as_me:$LINENO: error: invalid tag name: $tagname" >&5 -echo "$as_me: error: invalid tag name: $tagname" >&2;} - { (exit 1); exit 1; }; } - ;; - esac - - if grep "^# ### BEGIN LIBTOOL TAG CONFIG: $tagname$" < "${ofile}" > /dev/null - then - { { echo "$as_me:$LINENO: error: tag name \"$tagname\" already exists" >&5 -echo "$as_me: error: tag name \"$tagname\" already exists" >&2;} - { (exit 1); exit 1; }; } - fi - - # Update the list of available tags. - if test -n "$tagname"; then - echo appending configuration tag \"$tagname\" to $ofile - - case $tagname in - CXX) - if test -n "$CXX" && ( test "X$CXX" != "Xno" && - ( (test "X$CXX" = "Xg++" && `g++ -v >/dev/null 2>&1` ) || - (test "X$CXX" != "Xg++"))) ; then - ac_ext=cc -ac_cpp='$CXXCPP $CPPFLAGS' -ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_cxx_compiler_gnu - - - - -archive_cmds_need_lc_CXX=no -allow_undefined_flag_CXX= -always_export_symbols_CXX=no -archive_expsym_cmds_CXX= -export_dynamic_flag_spec_CXX= -hardcode_direct_CXX=no -hardcode_libdir_flag_spec_CXX= -hardcode_libdir_flag_spec_ld_CXX= -hardcode_libdir_separator_CXX= -hardcode_minus_L_CXX=no -hardcode_shlibpath_var_CXX=unsupported -hardcode_automatic_CXX=no -module_cmds_CXX= -module_expsym_cmds_CXX= -link_all_deplibs_CXX=unknown -old_archive_cmds_CXX=$old_archive_cmds -no_undefined_flag_CXX= -whole_archive_flag_spec_CXX= -enable_shared_with_static_runtimes_CXX=no - -# Dependencies to place before and after the object being linked: -predep_objects_CXX= -postdep_objects_CXX= -predeps_CXX= -postdeps_CXX= -compiler_lib_search_path_CXX= - -# Source file extension for C++ test sources. -ac_ext=cpp - -# Object file extension for compiled C++ test sources. -objext=o -objext_CXX=$objext - -# Code to be used in simple compile tests -lt_simple_compile_test_code="int some_variable = 0;\n" - -# Code to be used in simple link tests -lt_simple_link_test_code='int main(int, char *[]) { return(0); }\n' - -# ltmain only uses $CC for tagged configurations so make sure $CC is set. - -# If no C compiler was specified, use CC. -LTCC=${LTCC-"$CC"} - -# If no C compiler flags were specified, use CFLAGS. -LTCFLAGS=${LTCFLAGS-"$CFLAGS"} - -# Allow CC to be a program name with arguments. -compiler=$CC - - -# save warnings/boilerplate of simple test code -ac_outfile=conftest.$ac_objext -printf "$lt_simple_compile_test_code" >conftest.$ac_ext -eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err -_lt_compiler_boilerplate=`cat conftest.err` -$rm conftest* - -ac_outfile=conftest.$ac_objext -printf "$lt_simple_link_test_code" >conftest.$ac_ext -eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err -_lt_linker_boilerplate=`cat conftest.err` -$rm conftest* - - -# Allow CC to be a program name with arguments. -lt_save_CC=$CC -lt_save_LD=$LD -lt_save_GCC=$GCC -GCC=$GXX -lt_save_with_gnu_ld=$with_gnu_ld -lt_save_path_LD=$lt_cv_path_LD -if test -n "${lt_cv_prog_gnu_ldcxx+set}"; then - lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx -else - $as_unset lt_cv_prog_gnu_ld -fi -if test -n "${lt_cv_path_LDCXX+set}"; then - lt_cv_path_LD=$lt_cv_path_LDCXX -else - $as_unset lt_cv_path_LD -fi -test -z "${LDCXX+set}" || LD=$LDCXX -CC=${CXX-"c++"} -compiler=$CC -compiler_CXX=$CC -for cc_temp in $compiler""; do - case $cc_temp in - compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; - distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; - \-*) ;; - *) break;; - esac -done -cc_basename=`$echo "X$cc_temp" | $Xsed -e 's%.*/%%' -e "s%^$host_alias-%%"` - - -# We don't want -fno-exception wen compiling C++ code, so set the -# no_builtin_flag separately -if test "$GXX" = yes; then - lt_prog_compiler_no_builtin_flag_CXX=' -fno-builtin' -else - lt_prog_compiler_no_builtin_flag_CXX= -fi - -if test "$GXX" = yes; then - # Set up default GNU C++ configuration - - -# Check whether --with-gnu-ld or --without-gnu-ld was given. -if test "${with_gnu_ld+set}" = set; then - withval="$with_gnu_ld" - test "$withval" = no || with_gnu_ld=yes -else - with_gnu_ld=no -fi; -ac_prog=ld -if test "$GCC" = yes; then - # Check if gcc -print-prog-name=ld gives a path. - echo "$as_me:$LINENO: checking for ld used by $CC" >&5 -echo $ECHO_N "checking for ld used by $CC... $ECHO_C" >&6 - case $host in - *-*-mingw*) - # gcc leaves a trailing carriage return which upsets mingw - ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; - *) - ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; - esac - case $ac_prog in - # Accept absolute paths. - [\\/]* | ?:[\\/]*) - re_direlt='/[^/][^/]*/\.\./' - # Canonicalize the pathname of ld - ac_prog=`echo $ac_prog| $SED 's%\\\\%/%g'` - while echo $ac_prog | grep "$re_direlt" > /dev/null 2>&1; do - ac_prog=`echo $ac_prog| $SED "s%$re_direlt%/%"` - done - test -z "$LD" && LD="$ac_prog" - ;; - "") - # If it fails, then pretend we aren't using GCC. - ac_prog=ld - ;; - *) - # If it is relative, then search for the first ld in PATH. - with_gnu_ld=unknown - ;; - esac -elif test "$with_gnu_ld" = yes; then - echo "$as_me:$LINENO: checking for GNU ld" >&5 -echo $ECHO_N "checking for GNU ld... $ECHO_C" >&6 -else - echo "$as_me:$LINENO: checking for non-GNU ld" >&5 -echo $ECHO_N "checking for non-GNU ld... $ECHO_C" >&6 -fi -if test "${lt_cv_path_LD+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test -z "$LD"; then - lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR - for ac_dir in $PATH; do - IFS="$lt_save_ifs" - test -z "$ac_dir" && ac_dir=. - if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then - lt_cv_path_LD="$ac_dir/$ac_prog" - # Check to see if the program is GNU ld. I'd rather use --version, - # but apparently some variants of GNU ld only accept -v. - # Break only if it was the GNU/non-GNU ld that we prefer. - case `"$lt_cv_path_LD" -v 2>&1 &5 -echo "${ECHO_T}$LD" >&6 -else - echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6 -fi -test -z "$LD" && { { echo "$as_me:$LINENO: error: no acceptable ld found in \$PATH" >&5 -echo "$as_me: error: no acceptable ld found in \$PATH" >&2;} - { (exit 1); exit 1; }; } -echo "$as_me:$LINENO: checking if the linker ($LD) is GNU ld" >&5 -echo $ECHO_N "checking if the linker ($LD) is GNU ld... $ECHO_C" >&6 -if test "${lt_cv_prog_gnu_ld+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - # I'd rather use --version here, but apparently some GNU lds only accept -v. -case `$LD -v 2>&1 &5 -echo "${ECHO_T}$lt_cv_prog_gnu_ld" >&6 -with_gnu_ld=$lt_cv_prog_gnu_ld - - - - # Check if GNU C++ uses GNU ld as the underlying linker, since the - # archiving commands below assume that GNU ld is being used. - if test "$with_gnu_ld" = yes; then - archive_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' - archive_expsym_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' - - hardcode_libdir_flag_spec_CXX='${wl}--rpath ${wl}$libdir' - export_dynamic_flag_spec_CXX='${wl}--export-dynamic' - - # If archive_cmds runs LD, not CC, wlarc should be empty - # XXX I think wlarc can be eliminated in ltcf-cxx, but I need to - # investigate it a little bit more. (MM) - wlarc='${wl}' - - # ancient GNU ld didn't support --whole-archive et. al. - if eval "`$CC -print-prog-name=ld` --help 2>&1" | \ - grep 'no-whole-archive' > /dev/null; then - whole_archive_flag_spec_CXX="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' - else - whole_archive_flag_spec_CXX= - fi - else - with_gnu_ld=no - wlarc= - - # A generic and very simple default shared library creation - # command for GNU C++ for the case where it uses the native - # linker, instead of GNU ld. If possible, this setting should - # overridden to take advantage of the native linker features on - # the platform it is being used on. - archive_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' - fi - - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep "\-L"' - -else - GXX=no - with_gnu_ld=no - wlarc= -fi - -# PORTME: fill in a description of your system's C++ link characteristics -echo "$as_me:$LINENO: checking whether the $compiler linker ($LD) supports shared libraries" >&5 -echo $ECHO_N "checking whether the $compiler linker ($LD) supports shared libraries... $ECHO_C" >&6 -ld_shlibs_CXX=yes -case $host_os in - aix3*) - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - aix4* | aix5*) - if test "$host_cpu" = ia64; then - # On IA64, the linker does run time linking by default, so we don't - # have to do anything special. - aix_use_runtimelinking=no - exp_sym_flag='-Bexport' - no_entry_flag="" - else - aix_use_runtimelinking=no - - # Test if we are trying to use run time linking or normal - # AIX style linking. If -brtl is somewhere in LDFLAGS, we - # need to do runtime linking. - case $host_os in aix4.[23]|aix4.[23].*|aix5*) - for ld_flag in $LDFLAGS; do - case $ld_flag in - *-brtl*) - aix_use_runtimelinking=yes - break - ;; - esac - done - ;; - esac - - exp_sym_flag='-bexport' - no_entry_flag='-bnoentry' - fi - - # When large executables or shared objects are built, AIX ld can - # have problems creating the table of contents. If linking a library - # or program results in "error TOC overflow" add -mminimal-toc to - # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not - # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. - - archive_cmds_CXX='' - hardcode_direct_CXX=yes - hardcode_libdir_separator_CXX=':' - link_all_deplibs_CXX=yes - - if test "$GXX" = yes; then - case $host_os in aix4.[012]|aix4.[012].*) - # We only want to do this on AIX 4.2 and lower, the check - # below for broken collect2 doesn't work under 4.3+ - collect2name=`${CC} -print-prog-name=collect2` - if test -f "$collect2name" && \ - strings "$collect2name" | grep resolve_lib_name >/dev/null - then - # We have reworked collect2 - hardcode_direct_CXX=yes - else - # We have old collect2 - hardcode_direct_CXX=unsupported - # It fails to find uninstalled libraries when the uninstalled - # path is not listed in the libpath. Setting hardcode_minus_L - # to unsupported forces relinking - hardcode_minus_L_CXX=yes - hardcode_libdir_flag_spec_CXX='-L$libdir' - hardcode_libdir_separator_CXX= - fi - ;; - esac - shared_flag='-shared' - if test "$aix_use_runtimelinking" = yes; then - shared_flag="$shared_flag "'${wl}-G' - fi - else - # not using gcc - if test "$host_cpu" = ia64; then - # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release - # chokes on -Wl,-G. The following line is correct: - shared_flag='-G' - else - if test "$aix_use_runtimelinking" = yes; then - shared_flag='${wl}-G' - else - shared_flag='${wl}-bM:SRE' - fi - fi - fi - - # It seems that -bexpall does not export symbols beginning with - # underscore (_), so it is better to generate a list of symbols to export. - always_export_symbols_CXX=yes - if test "$aix_use_runtimelinking" = yes; then - # Warning - without using the other runtime loading flags (-brtl), - # -berok will link without error, but may produce a broken library. - allow_undefined_flag_CXX='-berok' - # Determine the default libpath from the value encoded in an empty executable. - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 - (eval $ac_link) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_cxx_werror_flag" - || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest$ac_exeext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - -aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } -}'` -# Check for a 64-bit object if we didn't find anything. -if test -z "$aix_libpath"; then aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } -}'`; fi -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -fi -rm -f conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi - - hardcode_libdir_flag_spec_CXX='${wl}-blibpath:$libdir:'"$aix_libpath" - - archive_expsym_cmds_CXX="\$CC"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then echo "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag" - else - if test "$host_cpu" = ia64; then - hardcode_libdir_flag_spec_CXX='${wl}-R $libdir:/usr/lib:/lib' - allow_undefined_flag_CXX="-z nodefs" - archive_expsym_cmds_CXX="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols" - else - # Determine the default libpath from the value encoded in an empty executable. - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 - (eval $ac_link) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_cxx_werror_flag" - || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest$ac_exeext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - -aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } -}'` -# Check for a 64-bit object if we didn't find anything. -if test -z "$aix_libpath"; then aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } -}'`; fi -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -fi -rm -f conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi - - hardcode_libdir_flag_spec_CXX='${wl}-blibpath:$libdir:'"$aix_libpath" - # Warning - without using the other run time loading flags, - # -berok will link without error, but may produce a broken library. - no_undefined_flag_CXX=' ${wl}-bernotok' - allow_undefined_flag_CXX=' ${wl}-berok' - # Exported symbols can be pulled into shared objects from archives - whole_archive_flag_spec_CXX='$convenience' - archive_cmds_need_lc_CXX=yes - # This is similar to how AIX traditionally builds its shared libraries. - archive_expsym_cmds_CXX="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' - fi - fi - ;; - - beos*) - if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then - allow_undefined_flag_CXX=unsupported - # Joseph Beckenbach says some releases of gcc - # support --undefined. This deserves some investigation. FIXME - archive_cmds_CXX='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - else - ld_shlibs_CXX=no - fi - ;; - - chorus*) - case $cc_basename in - *) - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - esac - ;; - - cygwin* | mingw* | pw32*) - # _LT_AC_TAGVAR(hardcode_libdir_flag_spec, CXX) is actually meaningless, - # as there is no search path for DLLs. - hardcode_libdir_flag_spec_CXX='-L$libdir' - allow_undefined_flag_CXX=unsupported - always_export_symbols_CXX=no - enable_shared_with_static_runtimes_CXX=yes - - if $LD --help 2>&1 | grep 'auto-import' > /dev/null; then - archive_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' - # If the export-symbols file already is a .def file (1st line - # is EXPORTS), use it as is; otherwise, prepend... - archive_expsym_cmds_CXX='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then - cp $export_symbols $output_objdir/$soname.def; - else - echo EXPORTS > $output_objdir/$soname.def; - cat $export_symbols >> $output_objdir/$soname.def; - fi~ - $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' - else - ld_shlibs_CXX=no - fi - ;; - darwin* | rhapsody*) - case $host_os in - rhapsody* | darwin1.[012]) - allow_undefined_flag_CXX='${wl}-undefined ${wl}suppress' - ;; - *) # Darwin 1.3 on - if test -z ${MACOSX_DEPLOYMENT_TARGET} ; then - allow_undefined_flag_CXX='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' - else - case ${MACOSX_DEPLOYMENT_TARGET} in - 10.[012]) - allow_undefined_flag_CXX='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' - ;; - 10.*) - allow_undefined_flag_CXX='${wl}-undefined ${wl}dynamic_lookup' - ;; - esac - fi - ;; - esac - archive_cmds_need_lc_CXX=no - hardcode_direct_CXX=no - hardcode_automatic_CXX=yes - hardcode_shlibpath_var_CXX=unsupported - whole_archive_flag_spec_CXX='' - link_all_deplibs_CXX=yes - - if test "$GXX" = yes ; then - lt_int_apple_cc_single_mod=no - output_verbose_link_cmd='echo' - if $CC -dumpspecs 2>&1 | $EGREP 'single_module' >/dev/null ; then - lt_int_apple_cc_single_mod=yes - fi - if test "X$lt_int_apple_cc_single_mod" = Xyes ; then - archive_cmds_CXX='$CC -dynamiclib -single_module $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags -install_name $rpath/$soname $verstring' - else - archive_cmds_CXX='$CC -r -keep_private_externs -nostdlib -o ${lib}-master.o $libobjs~$CC -dynamiclib $allow_undefined_flag -o $lib ${lib}-master.o $deplibs $compiler_flags -install_name $rpath/$soname $verstring' - fi - module_cmds_CXX='$CC $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags' - # Don't fix this by using the ld -exported_symbols_list flag, it doesn't exist in older darwin lds - if test "X$lt_int_apple_cc_single_mod" = Xyes ; then - archive_expsym_cmds_CXX='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC -dynamiclib -single_module $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags -install_name $rpath/$soname $verstring~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' - else - archive_expsym_cmds_CXX='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC -r -keep_private_externs -nostdlib -o ${lib}-master.o $libobjs~$CC -dynamiclib $allow_undefined_flag -o $lib ${lib}-master.o $deplibs $compiler_flags -install_name $rpath/$soname $verstring~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' - fi - module_expsym_cmds_CXX='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' - else - case $cc_basename in - xlc*) - output_verbose_link_cmd='echo' - archive_cmds_CXX='$CC -qmkshrobj ${wl}-single_module $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-install_name ${wl}`echo $rpath/$soname` $verstring' - module_cmds_CXX='$CC $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags' - # Don't fix this by using the ld -exported_symbols_list flag, it doesn't exist in older darwin lds - archive_expsym_cmds_CXX='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC -qmkshrobj ${wl}-single_module $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-install_name ${wl}$rpath/$soname $verstring~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' - module_expsym_cmds_CXX='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' - ;; - *) - ld_shlibs_CXX=no - ;; - esac - fi - ;; - - dgux*) - case $cc_basename in - ec++*) - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - ghcx*) - # Green Hills C++ Compiler - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - *) - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - esac - ;; - freebsd[12]*) - # C++ shared libraries reported to be fairly broken before switch to ELF - ld_shlibs_CXX=no - ;; - freebsd-elf*) - archive_cmds_need_lc_CXX=no - ;; - freebsd* | kfreebsd*-gnu | dragonfly*) - # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF - # conventions - ld_shlibs_CXX=yes - ;; - gnu*) - ;; - hpux9*) - hardcode_libdir_flag_spec_CXX='${wl}+b ${wl}$libdir' - hardcode_libdir_separator_CXX=: - export_dynamic_flag_spec_CXX='${wl}-E' - hardcode_direct_CXX=yes - hardcode_minus_L_CXX=yes # Not in the search PATH, - # but as the default - # location of the library. - - case $cc_basename in - CC*) - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - aCC*) - archive_cmds_CXX='$rm $output_objdir/$soname~$CC -b ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - # - # There doesn't appear to be a way to prevent this compiler from - # explicitly linking system object files so we need to strip them - # from the output so that they don't get included in the library - # dependencies. - output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | grep "[-]L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; echo $list' - ;; - *) - if test "$GXX" = yes; then - archive_cmds_CXX='$rm $output_objdir/$soname~$CC -shared -nostdlib -fPIC ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' - else - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - fi - ;; - esac - ;; - hpux10*|hpux11*) - if test $with_gnu_ld = no; then - hardcode_libdir_flag_spec_CXX='${wl}+b ${wl}$libdir' - hardcode_libdir_separator_CXX=: - - case $host_cpu in - hppa*64*|ia64*) - hardcode_libdir_flag_spec_ld_CXX='+b $libdir' - ;; - *) - export_dynamic_flag_spec_CXX='${wl}-E' - ;; - esac - fi - case $host_cpu in - hppa*64*|ia64*) - hardcode_direct_CXX=no - hardcode_shlibpath_var_CXX=no - ;; - *) - hardcode_direct_CXX=yes - hardcode_minus_L_CXX=yes # Not in the search PATH, - # but as the default - # location of the library. - ;; - esac - - case $cc_basename in - CC*) - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - aCC*) - case $host_cpu in - hppa*64*) - archive_cmds_CXX='$CC -b ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' - ;; - ia64*) - archive_cmds_CXX='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' - ;; - *) - archive_cmds_CXX='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' - ;; - esac - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - # - # There doesn't appear to be a way to prevent this compiler from - # explicitly linking system object files so we need to strip them - # from the output so that they don't get included in the library - # dependencies. - output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | grep "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; echo $list' - ;; - *) - if test "$GXX" = yes; then - if test $with_gnu_ld = no; then - case $host_cpu in - hppa*64*) - archive_cmds_CXX='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' - ;; - ia64*) - archive_cmds_CXX='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' - ;; - *) - archive_cmds_CXX='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' - ;; - esac - fi - else - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - fi - ;; - esac - ;; - interix3*) - hardcode_direct_CXX=no - hardcode_shlibpath_var_CXX=no - hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir' - export_dynamic_flag_spec_CXX='${wl}-E' - # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. - # Instead, shared libraries are loaded at an image base (0x10000000 by - # default) and relocated if they conflict, which is a slow very memory - # consuming and fragmenting process. To avoid this, we pick a random, - # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link - # time. Moving up from 0x10000000 also allows more sbrk(2) space. - archive_cmds_CXX='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' - archive_expsym_cmds_CXX='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' - ;; - irix5* | irix6*) - case $cc_basename in - CC*) - # SGI C++ - archive_cmds_CXX='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib' - - # Archives containing C++ object files must be created using - # "CC -ar", where "CC" is the IRIX C++ compiler. This is - # necessary to make sure instantiated templates are included - # in the archive. - old_archive_cmds_CXX='$CC -ar -WR,-u -o $oldlib $oldobjs' - ;; - *) - if test "$GXX" = yes; then - if test "$with_gnu_ld" = no; then - archive_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' - else - archive_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` -o $lib' - fi - fi - link_all_deplibs_CXX=yes - ;; - esac - hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir' - hardcode_libdir_separator_CXX=: - ;; - linux*) - case $cc_basename in - KCC*) - # Kuck and Associates, Inc. (KAI) C++ Compiler - - # KCC will only create a shared library if the output file - # ends with ".so" (or ".sl" for HP-UX), so rename the library - # to its proper name (with version) after linking. - archive_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' - archive_expsym_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib ${wl}-retain-symbols-file,$export_symbols; mv \$templib $lib' - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - # - # There doesn't appear to be a way to prevent this compiler from - # explicitly linking system object files so we need to strip them - # from the output so that they don't get included in the library - # dependencies. - output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | grep "ld"`; rm -f libconftest$shared_ext; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; echo $list' - - hardcode_libdir_flag_spec_CXX='${wl}--rpath,$libdir' - export_dynamic_flag_spec_CXX='${wl}--export-dynamic' - - # Archives containing C++ object files must be created using - # "CC -Bstatic", where "CC" is the KAI C++ compiler. - old_archive_cmds_CXX='$CC -Bstatic -o $oldlib $oldobjs' - ;; - icpc*) - # Intel C++ - with_gnu_ld=yes - # version 8.0 and above of icpc choke on multiply defined symbols - # if we add $predep_objects and $postdep_objects, however 7.1 and - # earlier do not add the objects themselves. - case `$CC -V 2>&1` in - *"Version 7."*) - archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' - archive_expsym_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' - ;; - *) # Version 8.0 or newer - tmp_idyn= - case $host_cpu in - ia64*) tmp_idyn=' -i_dynamic';; - esac - archive_cmds_CXX='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - archive_expsym_cmds_CXX='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' - ;; - esac - archive_cmds_need_lc_CXX=no - hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir' - export_dynamic_flag_spec_CXX='${wl}--export-dynamic' - whole_archive_flag_spec_CXX='${wl}--whole-archive$convenience ${wl}--no-whole-archive' - ;; - pgCC*) - # Portland Group C++ compiler - archive_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib' - archive_expsym_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib' - - hardcode_libdir_flag_spec_CXX='${wl}--rpath ${wl}$libdir' - export_dynamic_flag_spec_CXX='${wl}--export-dynamic' - whole_archive_flag_spec_CXX='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $echo \"$new_convenience\"` ${wl}--no-whole-archive' - ;; - cxx*) - # Compaq C++ - archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' - archive_expsym_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib ${wl}-retain-symbols-file $wl$export_symbols' - - runpath_var=LD_RUN_PATH - hardcode_libdir_flag_spec_CXX='-rpath $libdir' - hardcode_libdir_separator_CXX=: - - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - # - # There doesn't appear to be a way to prevent this compiler from - # explicitly linking system object files so we need to strip them - # from the output so that they don't get included in the library - # dependencies. - output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep "ld"`; templist=`echo $templist | $SED "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; echo $list' - ;; - esac - ;; - lynxos*) - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - m88k*) - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - mvs*) - case $cc_basename in - cxx*) - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - *) - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - esac - ;; - netbsd*) - if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then - archive_cmds_CXX='$LD -Bshareable -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags' - wlarc= - hardcode_libdir_flag_spec_CXX='-R$libdir' - hardcode_direct_CXX=yes - hardcode_shlibpath_var_CXX=no - fi - # Workaround some broken pre-1.5 toolchains - output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep conftest.$objext | $SED -e "s:-lgcc -lc -lgcc::"' - ;; - openbsd2*) - # C++ shared libraries are fairly broken - ld_shlibs_CXX=no - ;; - openbsd*) - hardcode_direct_CXX=yes - hardcode_shlibpath_var_CXX=no - archive_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' - hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir' - if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then - archive_expsym_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file,$export_symbols -o $lib' - export_dynamic_flag_spec_CXX='${wl}-E' - whole_archive_flag_spec_CXX="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' - fi - output_verbose_link_cmd='echo' - ;; - osf3*) - case $cc_basename in - KCC*) - # Kuck and Associates, Inc. (KAI) C++ Compiler - - # KCC will only create a shared library if the output file - # ends with ".so" (or ".sl" for HP-UX), so rename the library - # to its proper name (with version) after linking. - archive_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' - - hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir' - hardcode_libdir_separator_CXX=: - - # Archives containing C++ object files must be created using - # "CC -Bstatic", where "CC" is the KAI C++ compiler. - old_archive_cmds_CXX='$CC -Bstatic -o $oldlib $oldobjs' - - ;; - RCC*) - # Rational C++ 2.4.1 - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - cxx*) - allow_undefined_flag_CXX=' ${wl}-expect_unresolved ${wl}\*' - archive_cmds_CXX='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $soname `test -n "$verstring" && echo ${wl}-set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib' - - hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir' - hardcode_libdir_separator_CXX=: - - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - # - # There doesn't appear to be a way to prevent this compiler from - # explicitly linking system object files so we need to strip them - # from the output so that they don't get included in the library - # dependencies. - output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep "ld" | grep -v "ld:"`; templist=`echo $templist | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; echo $list' - ;; - *) - if test "$GXX" = yes && test "$with_gnu_ld" = no; then - allow_undefined_flag_CXX=' ${wl}-expect_unresolved ${wl}\*' - archive_cmds_CXX='$CC -shared -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' - - hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir' - hardcode_libdir_separator_CXX=: - - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep "\-L"' - - else - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - fi - ;; - esac - ;; - osf4* | osf5*) - case $cc_basename in - KCC*) - # Kuck and Associates, Inc. (KAI) C++ Compiler - - # KCC will only create a shared library if the output file - # ends with ".so" (or ".sl" for HP-UX), so rename the library - # to its proper name (with version) after linking. - archive_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' - - hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir' - hardcode_libdir_separator_CXX=: - - # Archives containing C++ object files must be created using - # the KAI C++ compiler. - old_archive_cmds_CXX='$CC -o $oldlib $oldobjs' - ;; - RCC*) - # Rational C++ 2.4.1 - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - cxx*) - allow_undefined_flag_CXX=' -expect_unresolved \*' - archive_cmds_CXX='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib' - archive_expsym_cmds_CXX='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~ - echo "-hidden">> $lib.exp~ - $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname -Wl,-input -Wl,$lib.exp `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib~ - $rm $lib.exp' - - hardcode_libdir_flag_spec_CXX='-rpath $libdir' - hardcode_libdir_separator_CXX=: - - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - # - # There doesn't appear to be a way to prevent this compiler from - # explicitly linking system object files so we need to strip them - # from the output so that they don't get included in the library - # dependencies. - output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep "ld" | grep -v "ld:"`; templist=`echo $templist | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; echo $list' - ;; - *) - if test "$GXX" = yes && test "$with_gnu_ld" = no; then - allow_undefined_flag_CXX=' ${wl}-expect_unresolved ${wl}\*' - archive_cmds_CXX='$CC -shared -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' - - hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir' - hardcode_libdir_separator_CXX=: - - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep "\-L"' - - else - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - fi - ;; - esac - ;; - psos*) - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - sunos4*) - case $cc_basename in - CC*) - # Sun C++ 4.x - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - lcc*) - # Lucid - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - *) - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - esac - ;; - solaris*) - case $cc_basename in - CC*) - # Sun C++ 4.2, 5.x and Centerline C++ - archive_cmds_need_lc_CXX=yes - no_undefined_flag_CXX=' -zdefs' - archive_cmds_CXX='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' - archive_expsym_cmds_CXX='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~ - $CC -G${allow_undefined_flag} ${wl}-M ${wl}$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$rm $lib.exp' - - hardcode_libdir_flag_spec_CXX='-R$libdir' - hardcode_shlibpath_var_CXX=no - case $host_os in - solaris2.[0-5] | solaris2.[0-5].*) ;; - *) - # The C++ compiler is used as linker so we must use $wl - # flag to pass the commands to the underlying system - # linker. We must also pass each convience library through - # to the system linker between allextract/defaultextract. - # The C++ compiler will combine linker options so we - # cannot just pass the convience library names through - # without $wl. - # Supported since Solaris 2.6 (maybe 2.5.1?) - whole_archive_flag_spec_CXX='${wl}-z ${wl}allextract`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $echo \"$new_convenience\"` ${wl}-z ${wl}defaultextract' - ;; - esac - link_all_deplibs_CXX=yes - - output_verbose_link_cmd='echo' - - # Archives containing C++ object files must be created using - # "CC -xar", where "CC" is the Sun C++ compiler. This is - # necessary to make sure instantiated templates are included - # in the archive. - old_archive_cmds_CXX='$CC -xar -o $oldlib $oldobjs' - ;; - gcx*) - # Green Hills C++ Compiler - archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' - - # The C++ compiler must be used to create the archive. - old_archive_cmds_CXX='$CC $LDFLAGS -archive -o $oldlib $oldobjs' - ;; - *) - # GNU C++ compiler with Solaris linker - if test "$GXX" = yes && test "$with_gnu_ld" = no; then - no_undefined_flag_CXX=' ${wl}-z ${wl}defs' - if $CC --version | grep -v '^2\.7' > /dev/null; then - archive_cmds_CXX='$CC -shared -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' - archive_expsym_cmds_CXX='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~ - $CC -shared -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$rm $lib.exp' - - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - output_verbose_link_cmd="$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep \"\-L\"" - else - # g++ 2.7 appears to require `-G' NOT `-shared' on this - # platform. - archive_cmds_CXX='$CC -G -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' - archive_expsym_cmds_CXX='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~ - $CC -G -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$rm $lib.exp' - - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - output_verbose_link_cmd="$CC -G $CFLAGS -v conftest.$objext 2>&1 | grep \"\-L\"" - fi - - hardcode_libdir_flag_spec_CXX='${wl}-R $wl$libdir' - fi - ;; - esac - ;; - sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*) - no_undefined_flag_CXX='${wl}-z,text' - archive_cmds_need_lc_CXX=no - hardcode_shlibpath_var_CXX=no - runpath_var='LD_RUN_PATH' - - case $cc_basename in - CC*) - archive_cmds_CXX='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - archive_expsym_cmds_CXX='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - ;; - *) - archive_cmds_CXX='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - archive_expsym_cmds_CXX='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - ;; - esac - ;; - sysv5* | sco3.2v5* | sco5v6*) - # Note: We can NOT use -z defs as we might desire, because we do not - # link with -lc, and that would cause any symbols used from libc to - # always be unresolved, which means just about no library would - # ever link correctly. If we're not using GNU ld we use -z text - # though, which does catch some bad symbols but isn't as heavy-handed - # as -z defs. - # For security reasons, it is highly recommended that you always - # use absolute paths for naming shared libraries, and exclude the - # DT_RUNPATH tag from executables and libraries. But doing so - # requires that you compile everything twice, which is a pain. - # So that behaviour is only enabled if SCOABSPATH is set to a - # non-empty value in the environment. Most likely only useful for - # creating official distributions of packages. - # This is a hack until libtool officially supports absolute path - # names for shared libraries. - no_undefined_flag_CXX='${wl}-z,text' - allow_undefined_flag_CXX='${wl}-z,nodefs' - archive_cmds_need_lc_CXX=no - hardcode_shlibpath_var_CXX=no - hardcode_libdir_flag_spec_CXX='`test -z "$SCOABSPATH" && echo ${wl}-R,$libdir`' - hardcode_libdir_separator_CXX=':' - link_all_deplibs_CXX=yes - export_dynamic_flag_spec_CXX='${wl}-Bexport' - runpath_var='LD_RUN_PATH' - - case $cc_basename in - CC*) - archive_cmds_CXX='$CC -G ${wl}-h,\${SCOABSPATH:+${install_libdir}/}$soname -o $lib $libobjs $deplibs $compiler_flags' - archive_expsym_cmds_CXX='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,\${SCOABSPATH:+${install_libdir}/}$soname -o $lib $libobjs $deplibs $compiler_flags' - ;; - *) - archive_cmds_CXX='$CC -shared ${wl}-h,\${SCOABSPATH:+${install_libdir}/}$soname -o $lib $libobjs $deplibs $compiler_flags' - archive_expsym_cmds_CXX='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,\${SCOABSPATH:+${install_libdir}/}$soname -o $lib $libobjs $deplibs $compiler_flags' - ;; - esac - ;; - tandem*) - case $cc_basename in - NCC*) - # NonStop-UX NCC 3.20 - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - *) - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - esac - ;; - vxworks*) - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - *) - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; -esac -echo "$as_me:$LINENO: result: $ld_shlibs_CXX" >&5 -echo "${ECHO_T}$ld_shlibs_CXX" >&6 -test "$ld_shlibs_CXX" = no && can_build_shared=no - -GCC_CXX="$GXX" -LD_CXX="$LD" - -## CAVEAT EMPTOR: -## There is no encapsulation within the following macros, do not change -## the running order or otherwise move them around unless you know exactly -## what you are doing... - -cat > conftest.$ac_ext <&5 - (eval $ac_compile) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; then - # Parse the compiler output and extract the necessary - # objects, libraries and library flags. - - # Sentinel used to keep track of whether or not we are before - # the conftest object file. - pre_test_object_deps_done=no - - # The `*' in the case matches for architectures that use `case' in - # $output_verbose_cmd can trigger glob expansion during the loop - # eval without this substitution. - output_verbose_link_cmd=`$echo "X$output_verbose_link_cmd" | $Xsed -e "$no_glob_subst"` - - for p in `eval $output_verbose_link_cmd`; do - case $p in - - -L* | -R* | -l*) - # Some compilers place space between "-{L,R}" and the path. - # Remove the space. - if test $p = "-L" \ - || test $p = "-R"; then - prev=$p - continue - else - prev= - fi - - if test "$pre_test_object_deps_done" = no; then - case $p in - -L* | -R*) - # Internal compiler library paths should come after those - # provided the user. The postdeps already come after the - # user supplied libs so there is no need to process them. - if test -z "$compiler_lib_search_path_CXX"; then - compiler_lib_search_path_CXX="${prev}${p}" - else - compiler_lib_search_path_CXX="${compiler_lib_search_path_CXX} ${prev}${p}" - fi - ;; - # The "-l" case would never come before the object being - # linked, so don't bother handling this case. - esac - else - if test -z "$postdeps_CXX"; then - postdeps_CXX="${prev}${p}" - else - postdeps_CXX="${postdeps_CXX} ${prev}${p}" - fi - fi - ;; - - *.$objext) - # This assumes that the test object file only shows up - # once in the compiler output. - if test "$p" = "conftest.$objext"; then - pre_test_object_deps_done=yes - continue - fi - - if test "$pre_test_object_deps_done" = no; then - if test -z "$predep_objects_CXX"; then - predep_objects_CXX="$p" - else - predep_objects_CXX="$predep_objects_CXX $p" - fi - else - if test -z "$postdep_objects_CXX"; then - postdep_objects_CXX="$p" - else - postdep_objects_CXX="$postdep_objects_CXX $p" - fi - fi - ;; - - *) ;; # Ignore the rest. - - esac - done - - # Clean up. - rm -f a.out a.exe -else - echo "libtool.m4: error: problem compiling CXX test program" -fi - -$rm -f confest.$objext - -# PORTME: override above test on systems where it is broken -case $host_os in -interix3*) - # Interix 3.5 installs completely hosed .la files for C++, so rather than - # hack all around it, let's just trust "g++" to DTRT. - predep_objects_CXX= - postdep_objects_CXX= - postdeps_CXX= - ;; - -solaris*) - case $cc_basename in - CC*) - # Adding this requires a known-good setup of shared libraries for - # Sun compiler versions before 5.6, else PIC objects from an old - # archive will be linked into the output, leading to subtle bugs. - postdeps_CXX='-lCstd -lCrun' - ;; - esac - ;; -esac - - -case " $postdeps_CXX " in -*" -lc "*) archive_cmds_need_lc_CXX=no ;; -esac - -lt_prog_compiler_wl_CXX= -lt_prog_compiler_pic_CXX= -lt_prog_compiler_static_CXX= - -echo "$as_me:$LINENO: checking for $compiler option to produce PIC" >&5 -echo $ECHO_N "checking for $compiler option to produce PIC... $ECHO_C" >&6 - - # C++ specific cases for pic, static, wl, etc. - if test "$GXX" = yes; then - lt_prog_compiler_wl_CXX='-Wl,' - lt_prog_compiler_static_CXX='-static' - - case $host_os in - aix*) - # All AIX code is PIC. - if test "$host_cpu" = ia64; then - # AIX 5 now supports IA64 processor - lt_prog_compiler_static_CXX='-Bstatic' - fi - ;; - amigaos*) - # FIXME: we need at least 68020 code to build shared libraries, but - # adding the `-m68020' flag to GCC prevents building anything better, - # like `-m68040'. - lt_prog_compiler_pic_CXX='-m68020 -resident32 -malways-restore-a4' - ;; - beos* | cygwin* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) - # PIC is the default for these OSes. - ;; - mingw* | os2* | pw32*) - # This hack is so that the source file can tell whether it is being - # built for inclusion in a dll (and should export symbols for example). - lt_prog_compiler_pic_CXX='-DDLL_EXPORT' - ;; - darwin* | rhapsody*) - # PIC is the default on this platform - # Common symbols not allowed in MH_DYLIB files - lt_prog_compiler_pic_CXX='-fno-common' - ;; - *djgpp*) - # DJGPP does not support shared libraries at all - lt_prog_compiler_pic_CXX= - ;; - interix3*) - # Interix 3.x gcc -fpic/-fPIC options generate broken code. - # Instead, we relocate shared libraries at runtime. - ;; - sysv4*MP*) - if test -d /usr/nec; then - lt_prog_compiler_pic_CXX=-Kconform_pic - fi - ;; - hpux*) - # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but - # not for PA HP-UX. - case $host_cpu in - hppa*64*|ia64*) - ;; - *) - lt_prog_compiler_pic_CXX='-fPIC' - ;; - esac - ;; - *) - lt_prog_compiler_pic_CXX='-fPIC' - ;; - esac - else - case $host_os in - aix4* | aix5*) - # All AIX code is PIC. - if test "$host_cpu" = ia64; then - # AIX 5 now supports IA64 processor - lt_prog_compiler_static_CXX='-Bstatic' - else - lt_prog_compiler_static_CXX='-bnso -bI:/lib/syscalls.exp' - fi - ;; - chorus*) - case $cc_basename in - cxch68*) - # Green Hills C++ Compiler - # _LT_AC_TAGVAR(lt_prog_compiler_static, CXX)="--no_auto_instantiation -u __main -u __premain -u _abort -r $COOL_DIR/lib/libOrb.a $MVME_DIR/lib/CC/libC.a $MVME_DIR/lib/classix/libcx.s.a" - ;; - esac - ;; - darwin*) - # PIC is the default on this platform - # Common symbols not allowed in MH_DYLIB files - case $cc_basename in - xlc*) - lt_prog_compiler_pic_CXX='-qnocommon' - lt_prog_compiler_wl_CXX='-Wl,' - ;; - esac - ;; - dgux*) - case $cc_basename in - ec++*) - lt_prog_compiler_pic_CXX='-KPIC' - ;; - ghcx*) - # Green Hills C++ Compiler - lt_prog_compiler_pic_CXX='-pic' - ;; - *) - ;; - esac - ;; - freebsd* | kfreebsd*-gnu | dragonfly*) - # FreeBSD uses GNU C++ - ;; - hpux9* | hpux10* | hpux11*) - case $cc_basename in - CC*) - lt_prog_compiler_wl_CXX='-Wl,' - lt_prog_compiler_static_CXX='${wl}-a ${wl}archive' - if test "$host_cpu" != ia64; then - lt_prog_compiler_pic_CXX='+Z' - fi - ;; - aCC*) - lt_prog_compiler_wl_CXX='-Wl,' - lt_prog_compiler_static_CXX='${wl}-a ${wl}archive' - case $host_cpu in - hppa*64*|ia64*) - # +Z the default - ;; - *) - lt_prog_compiler_pic_CXX='+Z' - ;; - esac - ;; - *) - ;; - esac - ;; - interix*) - # This is c89, which is MS Visual C++ (no shared libs) - # Anyone wants to do a port? - ;; - irix5* | irix6* | nonstopux*) - case $cc_basename in - CC*) - lt_prog_compiler_wl_CXX='-Wl,' - lt_prog_compiler_static_CXX='-non_shared' - # CC pic flag -KPIC is the default. - ;; - *) - ;; - esac - ;; - linux*) - case $cc_basename in - KCC*) - # KAI C++ Compiler - lt_prog_compiler_wl_CXX='--backend -Wl,' - lt_prog_compiler_pic_CXX='-fPIC' - ;; - icpc* | ecpc*) - # Intel C++ - lt_prog_compiler_wl_CXX='-Wl,' - lt_prog_compiler_pic_CXX='-KPIC' - lt_prog_compiler_static_CXX='-static' - ;; - pgCC*) - # Portland Group C++ compiler. - lt_prog_compiler_wl_CXX='-Wl,' - lt_prog_compiler_pic_CXX='-fpic' - lt_prog_compiler_static_CXX='-Bstatic' - ;; - cxx*) - # Compaq C++ - # Make sure the PIC flag is empty. It appears that all Alpha - # Linux and Compaq Tru64 Unix objects are PIC. - lt_prog_compiler_pic_CXX= - lt_prog_compiler_static_CXX='-non_shared' - ;; - *) - ;; - esac - ;; - lynxos*) - ;; - m88k*) - ;; - mvs*) - case $cc_basename in - cxx*) - lt_prog_compiler_pic_CXX='-W c,exportall' - ;; - *) - ;; - esac - ;; - netbsd*) - ;; - osf3* | osf4* | osf5*) - case $cc_basename in - KCC*) - lt_prog_compiler_wl_CXX='--backend -Wl,' - ;; - RCC*) - # Rational C++ 2.4.1 - lt_prog_compiler_pic_CXX='-pic' - ;; - cxx*) - # Digital/Compaq C++ - lt_prog_compiler_wl_CXX='-Wl,' - # Make sure the PIC flag is empty. It appears that all Alpha - # Linux and Compaq Tru64 Unix objects are PIC. - lt_prog_compiler_pic_CXX= - lt_prog_compiler_static_CXX='-non_shared' - ;; - *) - ;; - esac - ;; - psos*) - ;; - solaris*) - case $cc_basename in - CC*) - # Sun C++ 4.2, 5.x and Centerline C++ - lt_prog_compiler_pic_CXX='-KPIC' - lt_prog_compiler_static_CXX='-Bstatic' - lt_prog_compiler_wl_CXX='-Qoption ld ' - ;; - gcx*) - # Green Hills C++ Compiler - lt_prog_compiler_pic_CXX='-PIC' - ;; - *) - ;; - esac - ;; - sunos4*) - case $cc_basename in - CC*) - # Sun C++ 4.x - lt_prog_compiler_pic_CXX='-pic' - lt_prog_compiler_static_CXX='-Bstatic' - ;; - lcc*) - # Lucid - lt_prog_compiler_pic_CXX='-pic' - ;; - *) - ;; - esac - ;; - tandem*) - case $cc_basename in - NCC*) - # NonStop-UX NCC 3.20 - lt_prog_compiler_pic_CXX='-KPIC' - ;; - *) - ;; - esac - ;; - sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) - case $cc_basename in - CC*) - lt_prog_compiler_wl_CXX='-Wl,' - lt_prog_compiler_pic_CXX='-KPIC' - lt_prog_compiler_static_CXX='-Bstatic' - ;; - esac - ;; - vxworks*) - ;; - *) - lt_prog_compiler_can_build_shared_CXX=no - ;; - esac - fi - -echo "$as_me:$LINENO: result: $lt_prog_compiler_pic_CXX" >&5 -echo "${ECHO_T}$lt_prog_compiler_pic_CXX" >&6 - -# -# Check to make sure the PIC flag actually works. -# -if test -n "$lt_prog_compiler_pic_CXX"; then - -echo "$as_me:$LINENO: checking if $compiler PIC flag $lt_prog_compiler_pic_CXX works" >&5 -echo $ECHO_N "checking if $compiler PIC flag $lt_prog_compiler_pic_CXX works... $ECHO_C" >&6 -if test "${lt_prog_compiler_pic_works_CXX+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - lt_prog_compiler_pic_works_CXX=no - ac_outfile=conftest.$ac_objext - printf "$lt_simple_compile_test_code" > conftest.$ac_ext - lt_compiler_flag="$lt_prog_compiler_pic_CXX -DPIC" - # Insert the option either (1) after the last *FLAGS variable, or - # (2) before a word containing "conftest.", or (3) at the end. - # Note that $ac_compile itself does not contain backslashes and begins - # with a dollar sign (not a hyphen), so the echo should work correctly. - # The option is referenced via a variable to avoid confusing sed. - lt_compile=`echo "$ac_compile" | $SED \ - -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ - -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ - -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:11524: $lt_compile\"" >&5) - (eval "$lt_compile" 2>conftest.err) - ac_status=$? - cat conftest.err >&5 - echo "$as_me:11528: \$? = $ac_status" >&5 - if (exit $ac_status) && test -s "$ac_outfile"; then - # The compiler can only warn and ignore the option if not recognized - # So say no if there are warnings other than the usual output. - $echo "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' >conftest.exp - $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 - if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then - lt_prog_compiler_pic_works_CXX=yes - fi - fi - $rm conftest* - -fi -echo "$as_me:$LINENO: result: $lt_prog_compiler_pic_works_CXX" >&5 -echo "${ECHO_T}$lt_prog_compiler_pic_works_CXX" >&6 - -if test x"$lt_prog_compiler_pic_works_CXX" = xyes; then - case $lt_prog_compiler_pic_CXX in - "" | " "*) ;; - *) lt_prog_compiler_pic_CXX=" $lt_prog_compiler_pic_CXX" ;; - esac -else - lt_prog_compiler_pic_CXX= - lt_prog_compiler_can_build_shared_CXX=no -fi - -fi -case $host_os in - # For platforms which do not support PIC, -DPIC is meaningless: - *djgpp*) - lt_prog_compiler_pic_CXX= - ;; - *) - lt_prog_compiler_pic_CXX="$lt_prog_compiler_pic_CXX -DPIC" - ;; -esac - -# -# Check to make sure the static flag actually works. -# -wl=$lt_prog_compiler_wl_CXX eval lt_tmp_static_flag=\"$lt_prog_compiler_static_CXX\" -echo "$as_me:$LINENO: checking if $compiler static flag $lt_tmp_static_flag works" >&5 -echo $ECHO_N "checking if $compiler static flag $lt_tmp_static_flag works... $ECHO_C" >&6 -if test "${lt_prog_compiler_static_works_CXX+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - lt_prog_compiler_static_works_CXX=no - save_LDFLAGS="$LDFLAGS" - LDFLAGS="$LDFLAGS $lt_tmp_static_flag" - printf "$lt_simple_link_test_code" > conftest.$ac_ext - if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then - # The linker can only warn and ignore the option if not recognized - # So say no if there are warnings - if test -s conftest.err; then - # Append any errors to the config.log. - cat conftest.err 1>&5 - $echo "X$_lt_linker_boilerplate" | $Xsed -e '/^$/d' > conftest.exp - $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 - if diff conftest.exp conftest.er2 >/dev/null; then - lt_prog_compiler_static_works_CXX=yes - fi - else - lt_prog_compiler_static_works_CXX=yes - fi - fi - $rm conftest* - LDFLAGS="$save_LDFLAGS" - -fi -echo "$as_me:$LINENO: result: $lt_prog_compiler_static_works_CXX" >&5 -echo "${ECHO_T}$lt_prog_compiler_static_works_CXX" >&6 - -if test x"$lt_prog_compiler_static_works_CXX" = xyes; then - : -else - lt_prog_compiler_static_CXX= -fi - - -echo "$as_me:$LINENO: checking if $compiler supports -c -o file.$ac_objext" >&5 -echo $ECHO_N "checking if $compiler supports -c -o file.$ac_objext... $ECHO_C" >&6 -if test "${lt_cv_prog_compiler_c_o_CXX+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - lt_cv_prog_compiler_c_o_CXX=no - $rm -r conftest 2>/dev/null - mkdir conftest - cd conftest - mkdir out - printf "$lt_simple_compile_test_code" > conftest.$ac_ext - - lt_compiler_flag="-o out/conftest2.$ac_objext" - # Insert the option either (1) after the last *FLAGS variable, or - # (2) before a word containing "conftest.", or (3) at the end. - # Note that $ac_compile itself does not contain backslashes and begins - # with a dollar sign (not a hyphen), so the echo should work correctly. - lt_compile=`echo "$ac_compile" | $SED \ - -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ - -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ - -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:11628: $lt_compile\"" >&5) - (eval "$lt_compile" 2>out/conftest.err) - ac_status=$? - cat out/conftest.err >&5 - echo "$as_me:11632: \$? = $ac_status" >&5 - if (exit $ac_status) && test -s out/conftest2.$ac_objext - then - # The compiler can only warn and ignore the option if not recognized - # So say no if there are warnings - $echo "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' > out/conftest.exp - $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 - if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then - lt_cv_prog_compiler_c_o_CXX=yes - fi - fi - chmod u+w . 2>&5 - $rm conftest* - # SGI C++ compiler will create directory out/ii_files/ for - # template instantiation - test -d out/ii_files && $rm out/ii_files/* && rmdir out/ii_files - $rm out/* && rmdir out - cd .. - rmdir conftest - $rm conftest* - -fi -echo "$as_me:$LINENO: result: $lt_cv_prog_compiler_c_o_CXX" >&5 -echo "${ECHO_T}$lt_cv_prog_compiler_c_o_CXX" >&6 - - -hard_links="nottested" -if test "$lt_cv_prog_compiler_c_o_CXX" = no && test "$need_locks" != no; then - # do not overwrite the value of need_locks provided by the user - echo "$as_me:$LINENO: checking if we can lock with hard links" >&5 -echo $ECHO_N "checking if we can lock with hard links... $ECHO_C" >&6 - hard_links=yes - $rm conftest* - ln conftest.a conftest.b 2>/dev/null && hard_links=no - touch conftest.a - ln conftest.a conftest.b 2>&5 || hard_links=no - ln conftest.a conftest.b 2>/dev/null && hard_links=no - echo "$as_me:$LINENO: result: $hard_links" >&5 -echo "${ECHO_T}$hard_links" >&6 - if test "$hard_links" = no; then - { echo "$as_me:$LINENO: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&5 -echo "$as_me: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&2;} - need_locks=warn - fi -else - need_locks=no -fi - -echo "$as_me:$LINENO: checking whether the $compiler linker ($LD) supports shared libraries" >&5 -echo $ECHO_N "checking whether the $compiler linker ($LD) supports shared libraries... $ECHO_C" >&6 - - export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' - case $host_os in - aix4* | aix5*) - # If we're using GNU nm, then we don't want the "-C" option. - # -C means demangle to AIX nm, but means don't demangle with GNU nm - if $NM -V 2>&1 | grep 'GNU' > /dev/null; then - export_symbols_cmds_CXX='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$2 == "T") || (\$2 == "D") || (\$2 == "B")) && (substr(\$3,1,1) != ".")) { print \$3 } }'\'' | sort -u > $export_symbols' - else - export_symbols_cmds_CXX='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$2 == "T") || (\$2 == "D") || (\$2 == "B")) && (substr(\$3,1,1) != ".")) { print \$3 } }'\'' | sort -u > $export_symbols' - fi - ;; - pw32*) - export_symbols_cmds_CXX="$ltdll_cmds" - ;; - cygwin* | mingw*) - export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS] /s/.* \([^ ]*\)/\1 DATA/;/^.* __nm__/s/^.* __nm__\([^ ]*\) [^ ]*/\1 DATA/;/^I /d;/^[AITW] /s/.* //'\'' | sort | uniq > $export_symbols' - ;; - *) - export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' - ;; - esac - -echo "$as_me:$LINENO: result: $ld_shlibs_CXX" >&5 -echo "${ECHO_T}$ld_shlibs_CXX" >&6 -test "$ld_shlibs_CXX" = no && can_build_shared=no - -# -# Do we need to explicitly link libc? -# -case "x$archive_cmds_need_lc_CXX" in -x|xyes) - # Assume -lc should be added - archive_cmds_need_lc_CXX=yes - - if test "$enable_shared" = yes && test "$GCC" = yes; then - case $archive_cmds_CXX in - *'~'*) - # FIXME: we may have to deal with multi-command sequences. - ;; - '$CC '*) - # Test whether the compiler implicitly links with -lc since on some - # systems, -lgcc has to come before -lc. If gcc already passes -lc - # to ld, don't add -lc before -lgcc. - echo "$as_me:$LINENO: checking whether -lc should be explicitly linked in" >&5 -echo $ECHO_N "checking whether -lc should be explicitly linked in... $ECHO_C" >&6 - $rm conftest* - printf "$lt_simple_compile_test_code" > conftest.$ac_ext - - if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } 2>conftest.err; then - soname=conftest - lib=conftest - libobjs=conftest.$ac_objext - deplibs= - wl=$lt_prog_compiler_wl_CXX - pic_flag=$lt_prog_compiler_pic_CXX - compiler_flags=-v - linker_flags=-v - verstring= - output_objdir=. - libname=conftest - lt_save_allow_undefined_flag=$allow_undefined_flag_CXX - allow_undefined_flag_CXX= - if { (eval echo "$as_me:$LINENO: \"$archive_cmds_CXX 2\>\&1 \| grep \" -lc \" \>/dev/null 2\>\&1\"") >&5 - (eval $archive_cmds_CXX 2\>\&1 \| grep \" -lc \" \>/dev/null 2\>\&1) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } - then - archive_cmds_need_lc_CXX=no - else - archive_cmds_need_lc_CXX=yes - fi - allow_undefined_flag_CXX=$lt_save_allow_undefined_flag - else - cat conftest.err 1>&5 - fi - $rm conftest* - echo "$as_me:$LINENO: result: $archive_cmds_need_lc_CXX" >&5 -echo "${ECHO_T}$archive_cmds_need_lc_CXX" >&6 - ;; - esac - fi - ;; -esac - -echo "$as_me:$LINENO: checking dynamic linker characteristics" >&5 -echo $ECHO_N "checking dynamic linker characteristics... $ECHO_C" >&6 -library_names_spec= -libname_spec='lib$name' -soname_spec= -shrext_cmds=".so" -postinstall_cmds= -postuninstall_cmds= -finish_cmds= -finish_eval= -shlibpath_var= -shlibpath_overrides_runpath=unknown -version_type=none -dynamic_linker="$host_os ld.so" -sys_lib_dlsearch_path_spec="/lib /usr/lib" -if test "$GCC" = yes; then - sys_lib_search_path_spec=`$CC -print-search-dirs | grep "^libraries:" | $SED -e "s/^libraries://" -e "s,=/,/,g"` - if echo "$sys_lib_search_path_spec" | grep ';' >/dev/null ; then - # if the path contains ";" then we assume it to be the separator - # otherwise default to the standard path separator (i.e. ":") - it is - # assumed that no part of a normal pathname contains ";" but that should - # okay in the real world where ";" in dirpaths is itself problematic. - sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` - else - sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` - fi -else - sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" -fi -need_lib_prefix=unknown -hardcode_into_libs=no - -# when you set need_version to no, make sure it does not cause -set_version -# flags to be left without arguments -need_version=unknown - -case $host_os in -aix3*) - version_type=linux - library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a' - shlibpath_var=LIBPATH - - # AIX 3 has no versioning support, so we append a major version to the name. - soname_spec='${libname}${release}${shared_ext}$major' - ;; - -aix4* | aix5*) - version_type=linux - need_lib_prefix=no - need_version=no - hardcode_into_libs=yes - if test "$host_cpu" = ia64; then - # AIX 5 supports IA64 - library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}' - shlibpath_var=LD_LIBRARY_PATH - else - # With GCC up to 2.95.x, collect2 would create an import file - # for dependence libraries. The import file would start with - # the line `#! .'. This would cause the generated library to - # depend on `.', always an invalid library. This was fixed in - # development snapshots of GCC prior to 3.0. - case $host_os in - aix4 | aix4.[01] | aix4.[01].*) - if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' - echo ' yes ' - echo '#endif'; } | ${CC} -E - | grep yes > /dev/null; then - : - else - can_build_shared=no - fi - ;; - esac - # AIX (on Power*) has no versioning support, so currently we can not hardcode correct - # soname into executable. Probably we can add versioning support to - # collect2, so additional links can be useful in future. - if test "$aix_use_runtimelinking" = yes; then - # If using run time linking (on AIX 4.2 or later) use lib.so - # instead of lib.a to let people know that these are not - # typical AIX shared libraries. - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - else - # We preserve .a as extension for shared libraries through AIX4.2 - # and later when we are not doing run time linking. - library_names_spec='${libname}${release}.a $libname.a' - soname_spec='${libname}${release}${shared_ext}$major' - fi - shlibpath_var=LIBPATH - fi - ;; - -amigaos*) - library_names_spec='$libname.ixlibrary $libname.a' - # Create ${libname}_ixlibrary.a entries in /sys/libs. - finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`$echo "X$lib" | $Xsed -e '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; test $rm /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' - ;; - -beos*) - library_names_spec='${libname}${shared_ext}' - dynamic_linker="$host_os ld.so" - shlibpath_var=LIBRARY_PATH - ;; - -bsdi[45]*) - version_type=linux - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' - shlibpath_var=LD_LIBRARY_PATH - sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" - sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" - # the default ld.so.conf also contains /usr/contrib/lib and - # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow - # libtool to hard-code these into programs - ;; - -cygwin* | mingw* | pw32*) - version_type=windows - shrext_cmds=".dll" - need_version=no - need_lib_prefix=no - - case $GCC,$host_os in - yes,cygwin* | yes,mingw* | yes,pw32*) - library_names_spec='$libname.dll.a' - # DLL is installed to $(libdir)/../bin by postinstall_cmds - postinstall_cmds='base_file=`basename \${file}`~ - dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i;echo \$dlname'\''`~ - dldir=$destdir/`dirname \$dlpath`~ - test -d \$dldir || mkdir -p \$dldir~ - $install_prog $dir/$dlname \$dldir/$dlname~ - chmod a+x \$dldir/$dlname' - postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ - dlpath=$dir/\$dldll~ - $rm \$dlpath' - shlibpath_overrides_runpath=yes - - case $host_os in - cygwin*) - # Cygwin DLLs use 'cyg' prefix rather than 'lib' - soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' - sys_lib_search_path_spec="/usr/lib /lib/w32api /lib /usr/local/lib" - ;; - mingw*) - # MinGW DLLs use traditional 'lib' prefix - soname_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' - sys_lib_search_path_spec=`$CC -print-search-dirs | grep "^libraries:" | $SED -e "s/^libraries://" -e "s,=/,/,g"` - if echo "$sys_lib_search_path_spec" | grep ';[c-zC-Z]:/' >/dev/null; then - # It is most probably a Windows format PATH printed by - # mingw gcc, but we are running on Cygwin. Gcc prints its search - # path with ; separators, and with drive letters. We can handle the - # drive letters (cygwin fileutils understands them), so leave them, - # especially as we might pass files found there to a mingw objdump, - # which wouldn't understand a cygwinified path. Ahh. - sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` - else - sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` - fi - ;; - pw32*) - # pw32 DLLs use 'pw' prefix rather than 'lib' - library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' - ;; - esac - ;; - - *) - library_names_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext} $libname.lib' - ;; - esac - dynamic_linker='Win32 ld.exe' - # FIXME: first we should search . and the directory the executable is in - shlibpath_var=PATH - ;; - -darwin* | rhapsody*) - dynamic_linker="$host_os dyld" - version_type=darwin - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${versuffix}$shared_ext ${libname}${release}${major}$shared_ext ${libname}$shared_ext' - soname_spec='${libname}${release}${major}$shared_ext' - shlibpath_overrides_runpath=yes - shlibpath_var=DYLD_LIBRARY_PATH - shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' - # Apple's gcc prints 'gcc -print-search-dirs' doesn't operate the same. - if test "$GCC" = yes; then - sys_lib_search_path_spec=`$CC -print-search-dirs | tr "\n" "$PATH_SEPARATOR" | sed -e 's/libraries:/@libraries:/' | tr "@" "\n" | grep "^libraries:" | sed -e "s/^libraries://" -e "s,=/,/,g" -e "s,$PATH_SEPARATOR, ,g" -e "s,.*,& /lib /usr/lib /usr/local/lib,g"` - else - sys_lib_search_path_spec='/lib /usr/lib /usr/local/lib' - fi - sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' - ;; - -dgux*) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - ;; - -freebsd1*) - dynamic_linker=no - ;; - -kfreebsd*-gnu) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=no - hardcode_into_libs=yes - dynamic_linker='GNU ld.so' - ;; - -freebsd* | dragonfly*) - # DragonFly does not have aout. When/if they implement a new - # versioning mechanism, adjust this. - if test -x /usr/bin/objformat; then - objformat=`/usr/bin/objformat` - else - case $host_os in - freebsd[123]*) objformat=aout ;; - *) objformat=elf ;; - esac - fi - version_type=freebsd-$objformat - case $version_type in - freebsd-elf*) - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' - need_version=no - need_lib_prefix=no - ;; - freebsd-*) - library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix' - need_version=yes - ;; - esac - shlibpath_var=LD_LIBRARY_PATH - case $host_os in - freebsd2*) - shlibpath_overrides_runpath=yes - ;; - freebsd3.[01]* | freebsdelf3.[01]*) - shlibpath_overrides_runpath=yes - hardcode_into_libs=yes - ;; - freebsd3.[2-9]* | freebsdelf3.[2-9]* | \ - freebsd4.[0-5] | freebsdelf4.[0-5] | freebsd4.1.1 | freebsdelf4.1.1) - shlibpath_overrides_runpath=no - hardcode_into_libs=yes - ;; - freebsd*) # from 4.6 on - shlibpath_overrides_runpath=yes - hardcode_into_libs=yes - ;; - esac - ;; - -gnu*) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - hardcode_into_libs=yes - ;; - -hpux9* | hpux10* | hpux11*) - # Give a soname corresponding to the major version so that dld.sl refuses to - # link against other versions. - version_type=sunos - need_lib_prefix=no - need_version=no - case $host_cpu in - ia64*) - shrext_cmds='.so' - hardcode_into_libs=yes - dynamic_linker="$host_os dld.so" - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - if test "X$HPUX_IA64_MODE" = X32; then - sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" - else - sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" - fi - sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec - ;; - hppa*64*) - shrext_cmds='.sl' - hardcode_into_libs=yes - dynamic_linker="$host_os dld.sl" - shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH - shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" - sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec - ;; - *) - shrext_cmds='.sl' - dynamic_linker="$host_os dld.sl" - shlibpath_var=SHLIB_PATH - shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - ;; - esac - # HP-UX runs *really* slowly unless shared libraries are mode 555. - postinstall_cmds='chmod 555 $lib' - ;; - -interix3*) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=no - hardcode_into_libs=yes - ;; - -irix5* | irix6* | nonstopux*) - case $host_os in - nonstopux*) version_type=nonstopux ;; - *) - if test "$lt_cv_prog_gnu_ld" = yes; then - version_type=linux - else - version_type=irix - fi ;; - esac - need_lib_prefix=no - need_version=no - soname_spec='${libname}${release}${shared_ext}$major' - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}' - case $host_os in - irix5* | nonstopux*) - libsuff= shlibsuff= - ;; - *) - case $LD in # libtool.m4 will add one of these switches to LD - *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") - libsuff= shlibsuff= libmagic=32-bit;; - *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") - libsuff=32 shlibsuff=N32 libmagic=N32;; - *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") - libsuff=64 shlibsuff=64 libmagic=64-bit;; - *) libsuff= shlibsuff= libmagic=never-match;; - esac - ;; - esac - shlibpath_var=LD_LIBRARY${shlibsuff}_PATH - shlibpath_overrides_runpath=no - sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}" - sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}" - hardcode_into_libs=yes - ;; - -# No shared lib support for Linux oldld, aout, or coff. -linux*oldld* | linux*aout* | linux*coff*) - dynamic_linker=no - ;; - -# This must be Linux ELF. -linux*) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=no - # This implies no fast_install, which is unacceptable. - # Some rework will be needed to allow for fast_install - # before this can be enabled. - hardcode_into_libs=yes - - # Append ld.so.conf contents to the search path - if test -f /etc/ld.so.conf; then - lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;/^$/d' | tr '\n' ' '` - sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" - fi - - # We used to test for /lib/ld.so.1 and disable shared libraries on - # powerpc, because MkLinux only supported shared libraries with the - # GNU dynamic linker. Since this was broken with cross compilers, - # most powerpc-linux boxes support dynamic linking these days and - # people can always --disable-shared, the test was removed, and we - # assume the GNU/Linux dynamic linker is in use. - dynamic_linker='GNU/Linux ld.so' - ;; - -knetbsd*-gnu) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=no - hardcode_into_libs=yes - dynamic_linker='GNU ld.so' - ;; - -netbsd*) - version_type=sunos - need_lib_prefix=no - need_version=no - if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' - finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' - dynamic_linker='NetBSD (a.out) ld.so' - else - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - dynamic_linker='NetBSD ld.elf_so' - fi - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - hardcode_into_libs=yes - ;; - -newsos6) - version_type=linux - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - ;; - -nto-qnx*) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - ;; - -openbsd*) - version_type=sunos - sys_lib_dlsearch_path_spec="/usr/lib" - need_lib_prefix=no - # Some older versions of OpenBSD (3.3 at least) *do* need versioned libs. - case $host_os in - openbsd3.3 | openbsd3.3.*) need_version=yes ;; - *) need_version=no ;; - esac - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' - finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' - shlibpath_var=LD_LIBRARY_PATH - if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then - case $host_os in - openbsd2.[89] | openbsd2.[89].*) - shlibpath_overrides_runpath=no - ;; - *) - shlibpath_overrides_runpath=yes - ;; - esac - else - shlibpath_overrides_runpath=yes - fi - ;; - -os2*) - libname_spec='$name' - shrext_cmds=".dll" - need_lib_prefix=no - library_names_spec='$libname${shared_ext} $libname.a' - dynamic_linker='OS/2 ld.exe' - shlibpath_var=LIBPATH - ;; - -osf3* | osf4* | osf5*) - version_type=osf - need_lib_prefix=no - need_version=no - soname_spec='${libname}${release}${shared_ext}$major' - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - shlibpath_var=LD_LIBRARY_PATH - sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" - sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec" - ;; - -solaris*) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - hardcode_into_libs=yes - # ldd complains unless libraries are executable - postinstall_cmds='chmod +x $lib' - ;; - -sunos4*) - version_type=sunos - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' - finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - if test "$with_gnu_ld" = yes; then - need_lib_prefix=no - fi - need_version=yes - ;; - -sysv4 | sysv4.3*) - version_type=linux - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - case $host_vendor in - sni) - shlibpath_overrides_runpath=no - need_lib_prefix=no - export_dynamic_flag_spec='${wl}-Blargedynsym' - runpath_var=LD_RUN_PATH - ;; - siemens) - need_lib_prefix=no - ;; - motorola) - need_lib_prefix=no - need_version=no - shlibpath_overrides_runpath=no - sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' - ;; - esac - ;; - -sysv4*MP*) - if test -d /usr/nec ;then - version_type=linux - library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}' - soname_spec='$libname${shared_ext}.$major' - shlibpath_var=LD_LIBRARY_PATH - fi - ;; - -sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) - version_type=freebsd-elf - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - hardcode_into_libs=yes - if test "$with_gnu_ld" = yes; then - sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' - shlibpath_overrides_runpath=no - else - sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' - shlibpath_overrides_runpath=yes - case $host_os in - sco3.2v5*) - sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" - ;; - esac - fi - sys_lib_dlsearch_path_spec='/usr/lib' - ;; - -uts4*) - version_type=linux - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - ;; - -*) - dynamic_linker=no - ;; -esac -echo "$as_me:$LINENO: result: $dynamic_linker" >&5 -echo "${ECHO_T}$dynamic_linker" >&6 -test "$dynamic_linker" = no && can_build_shared=no - -variables_saved_for_relink="PATH $shlibpath_var $runpath_var" -if test "$GCC" = yes; then - variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" -fi - -echo "$as_me:$LINENO: checking how to hardcode library paths into programs" >&5 -echo $ECHO_N "checking how to hardcode library paths into programs... $ECHO_C" >&6 -hardcode_action_CXX= -if test -n "$hardcode_libdir_flag_spec_CXX" || \ - test -n "$runpath_var_CXX" || \ - test "X$hardcode_automatic_CXX" = "Xyes" ; then - - # We can hardcode non-existant directories. - if test "$hardcode_direct_CXX" != no && - # If the only mechanism to avoid hardcoding is shlibpath_var, we - # have to relink, otherwise we might link with an installed library - # when we should be linking with a yet-to-be-installed one - ## test "$_LT_AC_TAGVAR(hardcode_shlibpath_var, CXX)" != no && - test "$hardcode_minus_L_CXX" != no; then - # Linking always hardcodes the temporary library directory. - hardcode_action_CXX=relink - else - # We can link without hardcoding, and we can hardcode nonexisting dirs. - hardcode_action_CXX=immediate - fi -else - # We cannot hardcode anything, or else we can only hardcode existing - # directories. - hardcode_action_CXX=unsupported -fi -echo "$as_me:$LINENO: result: $hardcode_action_CXX" >&5 -echo "${ECHO_T}$hardcode_action_CXX" >&6 - -if test "$hardcode_action_CXX" = relink; then - # Fast installation is not supported - enable_fast_install=no -elif test "$shlibpath_overrides_runpath" = yes || - test "$enable_shared" = no; then - # Fast installation is not necessary - enable_fast_install=needless -fi - - -# The else clause should only fire when bootstrapping the -# libtool distribution, otherwise you forgot to ship ltmain.sh -# with your package, and you will get complaints that there are -# no rules to generate ltmain.sh. -if test -f "$ltmain"; then - # See if we are running on zsh, and set the options which allow our commands through - # without removal of \ escapes. - if test -n "${ZSH_VERSION+set}" ; then - setopt NO_GLOB_SUBST - fi - # Now quote all the things that may contain metacharacters while being - # careful not to overquote the AC_SUBSTed values. We take copies of the - # variables and quote the copies for generation of the libtool script. - for var in echo old_CC old_CFLAGS AR AR_FLAGS EGREP RANLIB LN_S LTCC LTCFLAGS NM \ - SED SHELL STRIP \ - libname_spec library_names_spec soname_spec extract_expsyms_cmds \ - old_striplib striplib file_magic_cmd finish_cmds finish_eval \ - deplibs_check_method reload_flag reload_cmds need_locks \ - lt_cv_sys_global_symbol_pipe lt_cv_sys_global_symbol_to_cdecl \ - lt_cv_sys_global_symbol_to_c_name_address \ - sys_lib_search_path_spec sys_lib_dlsearch_path_spec \ - old_postinstall_cmds old_postuninstall_cmds \ - compiler_CXX \ - CC_CXX \ - LD_CXX \ - lt_prog_compiler_wl_CXX \ - lt_prog_compiler_pic_CXX \ - lt_prog_compiler_static_CXX \ - lt_prog_compiler_no_builtin_flag_CXX \ - export_dynamic_flag_spec_CXX \ - thread_safe_flag_spec_CXX \ - whole_archive_flag_spec_CXX \ - enable_shared_with_static_runtimes_CXX \ - old_archive_cmds_CXX \ - old_archive_from_new_cmds_CXX \ - predep_objects_CXX \ - postdep_objects_CXX \ - predeps_CXX \ - postdeps_CXX \ - compiler_lib_search_path_CXX \ - archive_cmds_CXX \ - archive_expsym_cmds_CXX \ - postinstall_cmds_CXX \ - postuninstall_cmds_CXX \ - old_archive_from_expsyms_cmds_CXX \ - allow_undefined_flag_CXX \ - no_undefined_flag_CXX \ - export_symbols_cmds_CXX \ - hardcode_libdir_flag_spec_CXX \ - hardcode_libdir_flag_spec_ld_CXX \ - hardcode_libdir_separator_CXX \ - hardcode_automatic_CXX \ - module_cmds_CXX \ - module_expsym_cmds_CXX \ - lt_cv_prog_compiler_c_o_CXX \ - exclude_expsyms_CXX \ - include_expsyms_CXX; do - - case $var in - old_archive_cmds_CXX | \ - old_archive_from_new_cmds_CXX | \ - archive_cmds_CXX | \ - archive_expsym_cmds_CXX | \ - module_cmds_CXX | \ - module_expsym_cmds_CXX | \ - old_archive_from_expsyms_cmds_CXX | \ - export_symbols_cmds_CXX | \ - extract_expsyms_cmds | reload_cmds | finish_cmds | \ - postinstall_cmds | postuninstall_cmds | \ - old_postinstall_cmds | old_postuninstall_cmds | \ - sys_lib_search_path_spec | sys_lib_dlsearch_path_spec) - # Double-quote double-evaled strings. - eval "lt_$var=\\\"\`\$echo \"X\$$var\" | \$Xsed -e \"\$double_quote_subst\" -e \"\$sed_quote_subst\" -e \"\$delay_variable_subst\"\`\\\"" - ;; - *) - eval "lt_$var=\\\"\`\$echo \"X\$$var\" | \$Xsed -e \"\$sed_quote_subst\"\`\\\"" - ;; - esac - done - - case $lt_echo in - *'\$0 --fallback-echo"') - lt_echo=`$echo "X$lt_echo" | $Xsed -e 's/\\\\\\\$0 --fallback-echo"$/$0 --fallback-echo"/'` - ;; - esac - -cfgfile="$ofile" - - cat <<__EOF__ >> "$cfgfile" -# ### BEGIN LIBTOOL TAG CONFIG: $tagname - -# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`: - -# Shell to use when invoking shell scripts. -SHELL=$lt_SHELL - -# Whether or not to build shared libraries. -build_libtool_libs=$enable_shared - -# Whether or not to build static libraries. -build_old_libs=$enable_static - -# Whether or not to add -lc for building shared libraries. -build_libtool_need_lc=$archive_cmds_need_lc_CXX - -# Whether or not to disallow shared libs when runtime libs are static -allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes_CXX - -# Whether or not to optimize for fast installation. -fast_install=$enable_fast_install - -# The host system. -host_alias=$host_alias -host=$host -host_os=$host_os - -# The build system. -build_alias=$build_alias -build=$build -build_os=$build_os - -# An echo program that does not interpret backslashes. -echo=$lt_echo - -# The archiver. -AR=$lt_AR -AR_FLAGS=$lt_AR_FLAGS - -# A C compiler. -LTCC=$lt_LTCC - -# LTCC compiler flags. -LTCFLAGS=$lt_LTCFLAGS - -# A language-specific compiler. -CC=$lt_compiler_CXX - -# Is the compiler the GNU C compiler? -with_gcc=$GCC_CXX - -# An ERE matcher. -EGREP=$lt_EGREP - -# The linker used to build libraries. -LD=$lt_LD_CXX - -# Whether we need hard or soft links. -LN_S=$lt_LN_S - -# A BSD-compatible nm program. -NM=$lt_NM - -# A symbol stripping program -STRIP=$lt_STRIP - -# Used to examine libraries when file_magic_cmd begins "file" -MAGIC_CMD=$MAGIC_CMD - -# Used on cygwin: DLL creation program. -DLLTOOL="$DLLTOOL" - -# Used on cygwin: object dumper. -OBJDUMP="$OBJDUMP" - -# Used on cygwin: assembler. -AS="$AS" - -# The name of the directory that contains temporary libtool files. -objdir=$objdir - -# How to create reloadable object files. -reload_flag=$lt_reload_flag -reload_cmds=$lt_reload_cmds - -# How to pass a linker flag through the compiler. -wl=$lt_lt_prog_compiler_wl_CXX - -# Object file suffix (normally "o"). -objext="$ac_objext" - -# Old archive suffix (normally "a"). -libext="$libext" - -# Shared library suffix (normally ".so"). -shrext_cmds='$shrext_cmds' - -# Executable file suffix (normally ""). -exeext="$exeext" - -# Additional compiler flags for building library objects. -pic_flag=$lt_lt_prog_compiler_pic_CXX -pic_mode=$pic_mode - -# What is the maximum length of a command? -max_cmd_len=$lt_cv_sys_max_cmd_len - -# Does compiler simultaneously support -c and -o options? -compiler_c_o=$lt_lt_cv_prog_compiler_c_o_CXX - -# Must we lock files when doing compilation? -need_locks=$lt_need_locks - -# Do we need the lib prefix for modules? -need_lib_prefix=$need_lib_prefix - -# Do we need a version for libraries? -need_version=$need_version - -# Whether dlopen is supported. -dlopen_support=$enable_dlopen - -# Whether dlopen of programs is supported. -dlopen_self=$enable_dlopen_self - -# Whether dlopen of statically linked programs is supported. -dlopen_self_static=$enable_dlopen_self_static - -# Compiler flag to prevent dynamic linking. -link_static_flag=$lt_lt_prog_compiler_static_CXX - -# Compiler flag to turn off builtin functions. -no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag_CXX - -# Compiler flag to allow reflexive dlopens. -export_dynamic_flag_spec=$lt_export_dynamic_flag_spec_CXX - -# Compiler flag to generate shared objects directly from archives. -whole_archive_flag_spec=$lt_whole_archive_flag_spec_CXX - -# Compiler flag to generate thread-safe objects. -thread_safe_flag_spec=$lt_thread_safe_flag_spec_CXX - -# Library versioning type. -version_type=$version_type - -# Format of library name prefix. -libname_spec=$lt_libname_spec - -# List of archive names. First name is the real one, the rest are links. -# The last name is the one that the linker finds with -lNAME. -library_names_spec=$lt_library_names_spec - -# The coded name of the library, if different from the real name. -soname_spec=$lt_soname_spec - -# Commands used to build and install an old-style archive. -RANLIB=$lt_RANLIB -old_archive_cmds=$lt_old_archive_cmds_CXX -old_postinstall_cmds=$lt_old_postinstall_cmds -old_postuninstall_cmds=$lt_old_postuninstall_cmds - -# Create an old-style archive from a shared archive. -old_archive_from_new_cmds=$lt_old_archive_from_new_cmds_CXX - -# Create a temporary old-style archive to link instead of a shared archive. -old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds_CXX - -# Commands used to build and install a shared archive. -archive_cmds=$lt_archive_cmds_CXX -archive_expsym_cmds=$lt_archive_expsym_cmds_CXX -postinstall_cmds=$lt_postinstall_cmds -postuninstall_cmds=$lt_postuninstall_cmds - -# Commands used to build a loadable module (assumed same as above if empty) -module_cmds=$lt_module_cmds_CXX -module_expsym_cmds=$lt_module_expsym_cmds_CXX - -# Commands to strip libraries. -old_striplib=$lt_old_striplib -striplib=$lt_striplib - -# Dependencies to place before the objects being linked to create a -# shared library. -predep_objects=$lt_predep_objects_CXX - -# Dependencies to place after the objects being linked to create a -# shared library. -postdep_objects=$lt_postdep_objects_CXX - -# Dependencies to place before the objects being linked to create a -# shared library. -predeps=$lt_predeps_CXX - -# Dependencies to place after the objects being linked to create a -# shared library. -postdeps=$lt_postdeps_CXX - -# The library search path used internally by the compiler when linking -# a shared library. -compiler_lib_search_path=$lt_compiler_lib_search_path_CXX - -# Method to check whether dependent libraries are shared objects. -deplibs_check_method=$lt_deplibs_check_method - -# Command to use when deplibs_check_method == file_magic. -file_magic_cmd=$lt_file_magic_cmd - -# Flag that allows shared libraries with undefined symbols to be built. -allow_undefined_flag=$lt_allow_undefined_flag_CXX - -# Flag that forces no undefined symbols. -no_undefined_flag=$lt_no_undefined_flag_CXX - -# Commands used to finish a libtool library installation in a directory. -finish_cmds=$lt_finish_cmds - -# Same as above, but a single script fragment to be evaled but not shown. -finish_eval=$lt_finish_eval - -# Take the output of nm and produce a listing of raw symbols and C names. -global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe - -# Transform the output of nm in a proper C declaration -global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl - -# Transform the output of nm in a C name address pair -global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address - -# This is the shared library runtime path variable. -runpath_var=$runpath_var - -# This is the shared library path variable. -shlibpath_var=$shlibpath_var - -# Is shlibpath searched before the hard-coded library search path? -shlibpath_overrides_runpath=$shlibpath_overrides_runpath - -# How to hardcode a shared library path into an executable. -hardcode_action=$hardcode_action_CXX - -# Whether we should hardcode library paths into libraries. -hardcode_into_libs=$hardcode_into_libs - -# Flag to hardcode \$libdir into a binary during linking. -# This must work even if \$libdir does not exist. -hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec_CXX - -# If ld is used when linking, flag to hardcode \$libdir into -# a binary during linking. This must work even if \$libdir does -# not exist. -hardcode_libdir_flag_spec_ld=$lt_hardcode_libdir_flag_spec_ld_CXX - -# Whether we need a single -rpath flag with a separated argument. -hardcode_libdir_separator=$lt_hardcode_libdir_separator_CXX - -# Set to yes if using DIR/libNAME${shared_ext} during linking hardcodes DIR into the -# resulting binary. -hardcode_direct=$hardcode_direct_CXX - -# Set to yes if using the -LDIR flag during linking hardcodes DIR into the -# resulting binary. -hardcode_minus_L=$hardcode_minus_L_CXX - -# Set to yes if using SHLIBPATH_VAR=DIR during linking hardcodes DIR into -# the resulting binary. -hardcode_shlibpath_var=$hardcode_shlibpath_var_CXX - -# Set to yes if building a shared library automatically hardcodes DIR into the library -# and all subsequent libraries and executables linked against it. -hardcode_automatic=$hardcode_automatic_CXX - -# Variables whose values should be saved in libtool wrapper scripts and -# restored at relink time. -variables_saved_for_relink="$variables_saved_for_relink" - -# Whether libtool must link a program against all its dependency libraries. -link_all_deplibs=$link_all_deplibs_CXX - -# Compile-time system search path for libraries -sys_lib_search_path_spec=$lt_sys_lib_search_path_spec - -# Run-time system search path for libraries -sys_lib_dlsearch_path_spec=$lt_sys_lib_dlsearch_path_spec - -# Fix the shell variable \$srcfile for the compiler. -fix_srcfile_path="$fix_srcfile_path_CXX" - -# Set to yes if exported symbols are required. -always_export_symbols=$always_export_symbols_CXX - -# The commands to list exported symbols. -export_symbols_cmds=$lt_export_symbols_cmds_CXX - -# The commands to extract the exported symbol list from a shared archive. -extract_expsyms_cmds=$lt_extract_expsyms_cmds - -# Symbols that should not be listed in the preloaded symbols. -exclude_expsyms=$lt_exclude_expsyms_CXX - -# Symbols that must always be exported. -include_expsyms=$lt_include_expsyms_CXX - -# ### END LIBTOOL TAG CONFIG: $tagname - -__EOF__ - - -else - # If there is no Makefile yet, we rely on a make rule to execute - # `config.status --recheck' to rerun these tests and create the - # libtool script then. - ltmain_in=`echo $ltmain | sed -e 's/\.sh$/.in/'` - if test -f "$ltmain_in"; then - test -f Makefile && make "$ltmain" - fi -fi - - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - -CC=$lt_save_CC -LDCXX=$LD -LD=$lt_save_LD -GCC=$lt_save_GCC -with_gnu_ldcxx=$with_gnu_ld -with_gnu_ld=$lt_save_with_gnu_ld -lt_cv_path_LDCXX=$lt_cv_path_LD -lt_cv_path_LD=$lt_save_path_LD -lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld -lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld - - else - tagname="" - fi - ;; - - F77) - if test -n "$F77" && test "X$F77" != "Xno"; then - -ac_ext=f -ac_compile='$F77 -c $FFLAGS conftest.$ac_ext >&5' -ac_link='$F77 -o conftest$ac_exeext $FFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_f77_compiler_gnu - - -archive_cmds_need_lc_F77=no -allow_undefined_flag_F77= -always_export_symbols_F77=no -archive_expsym_cmds_F77= -export_dynamic_flag_spec_F77= -hardcode_direct_F77=no -hardcode_libdir_flag_spec_F77= -hardcode_libdir_flag_spec_ld_F77= -hardcode_libdir_separator_F77= -hardcode_minus_L_F77=no -hardcode_automatic_F77=no -module_cmds_F77= -module_expsym_cmds_F77= -link_all_deplibs_F77=unknown -old_archive_cmds_F77=$old_archive_cmds -no_undefined_flag_F77= -whole_archive_flag_spec_F77= -enable_shared_with_static_runtimes_F77=no - -# Source file extension for f77 test sources. -ac_ext=f - -# Object file extension for compiled f77 test sources. -objext=o -objext_F77=$objext - -# Code to be used in simple compile tests -lt_simple_compile_test_code=" subroutine t\n return\n end\n" - -# Code to be used in simple link tests -lt_simple_link_test_code=" program t\n end\n" - -# ltmain only uses $CC for tagged configurations so make sure $CC is set. - -# If no C compiler was specified, use CC. -LTCC=${LTCC-"$CC"} - -# If no C compiler flags were specified, use CFLAGS. -LTCFLAGS=${LTCFLAGS-"$CFLAGS"} - -# Allow CC to be a program name with arguments. -compiler=$CC - - -# save warnings/boilerplate of simple test code -ac_outfile=conftest.$ac_objext -printf "$lt_simple_compile_test_code" >conftest.$ac_ext -eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err -_lt_compiler_boilerplate=`cat conftest.err` -$rm conftest* - -ac_outfile=conftest.$ac_objext -printf "$lt_simple_link_test_code" >conftest.$ac_ext -eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err -_lt_linker_boilerplate=`cat conftest.err` -$rm conftest* - - -# Allow CC to be a program name with arguments. -lt_save_CC="$CC" -CC=${F77-"f77"} -compiler=$CC -compiler_F77=$CC -for cc_temp in $compiler""; do - case $cc_temp in - compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; - distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; - \-*) ;; - *) break;; - esac -done -cc_basename=`$echo "X$cc_temp" | $Xsed -e 's%.*/%%' -e "s%^$host_alias-%%"` - - -echo "$as_me:$LINENO: checking if libtool supports shared libraries" >&5 -echo $ECHO_N "checking if libtool supports shared libraries... $ECHO_C" >&6 -echo "$as_me:$LINENO: result: $can_build_shared" >&5 -echo "${ECHO_T}$can_build_shared" >&6 - -echo "$as_me:$LINENO: checking whether to build shared libraries" >&5 -echo $ECHO_N "checking whether to build shared libraries... $ECHO_C" >&6 -test "$can_build_shared" = "no" && enable_shared=no - -# On AIX, shared libraries and static libraries use the same namespace, and -# are all built from PIC. -case $host_os in -aix3*) - test "$enable_shared" = yes && enable_static=no - if test -n "$RANLIB"; then - archive_cmds="$archive_cmds~\$RANLIB \$lib" - postinstall_cmds='$RANLIB $lib' - fi - ;; -aix4* | aix5*) - if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then - test "$enable_shared" = yes && enable_static=no - fi - ;; -esac -echo "$as_me:$LINENO: result: $enable_shared" >&5 -echo "${ECHO_T}$enable_shared" >&6 - -echo "$as_me:$LINENO: checking whether to build static libraries" >&5 -echo $ECHO_N "checking whether to build static libraries... $ECHO_C" >&6 -# Make sure either enable_shared or enable_static is yes. -test "$enable_shared" = yes || enable_static=yes -echo "$as_me:$LINENO: result: $enable_static" >&5 -echo "${ECHO_T}$enable_static" >&6 - -GCC_F77="$G77" -LD_F77="$LD" - -lt_prog_compiler_wl_F77= -lt_prog_compiler_pic_F77= -lt_prog_compiler_static_F77= - -echo "$as_me:$LINENO: checking for $compiler option to produce PIC" >&5 -echo $ECHO_N "checking for $compiler option to produce PIC... $ECHO_C" >&6 - - if test "$GCC" = yes; then - lt_prog_compiler_wl_F77='-Wl,' - lt_prog_compiler_static_F77='-static' - - case $host_os in - aix*) - # All AIX code is PIC. - if test "$host_cpu" = ia64; then - # AIX 5 now supports IA64 processor - lt_prog_compiler_static_F77='-Bstatic' - fi - ;; - - amigaos*) - # FIXME: we need at least 68020 code to build shared libraries, but - # adding the `-m68020' flag to GCC prevents building anything better, - # like `-m68040'. - lt_prog_compiler_pic_F77='-m68020 -resident32 -malways-restore-a4' - ;; - - beos* | cygwin* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) - # PIC is the default for these OSes. - ;; - - mingw* | pw32* | os2*) - # This hack is so that the source file can tell whether it is being - # built for inclusion in a dll (and should export symbols for example). - lt_prog_compiler_pic_F77='-DDLL_EXPORT' - ;; - - darwin* | rhapsody*) - # PIC is the default on this platform - # Common symbols not allowed in MH_DYLIB files - lt_prog_compiler_pic_F77='-fno-common' - ;; - - interix3*) - # Interix 3.x gcc -fpic/-fPIC options generate broken code. - # Instead, we relocate shared libraries at runtime. - ;; - - msdosdjgpp*) - # Just because we use GCC doesn't mean we suddenly get shared libraries - # on systems that don't support them. - lt_prog_compiler_can_build_shared_F77=no - enable_shared=no - ;; - - sysv4*MP*) - if test -d /usr/nec; then - lt_prog_compiler_pic_F77=-Kconform_pic - fi - ;; - - hpux*) - # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but - # not for PA HP-UX. - case $host_cpu in - hppa*64*|ia64*) - # +Z the default - ;; - *) - lt_prog_compiler_pic_F77='-fPIC' - ;; - esac - ;; - - *) - lt_prog_compiler_pic_F77='-fPIC' - ;; - esac - else - # PORTME Check for flag to pass linker flags through the system compiler. - case $host_os in - aix*) - lt_prog_compiler_wl_F77='-Wl,' - if test "$host_cpu" = ia64; then - # AIX 5 now supports IA64 processor - lt_prog_compiler_static_F77='-Bstatic' - else - lt_prog_compiler_static_F77='-bnso -bI:/lib/syscalls.exp' - fi - ;; - darwin*) - # PIC is the default on this platform - # Common symbols not allowed in MH_DYLIB files - case $cc_basename in - xlc*) - lt_prog_compiler_pic_F77='-qnocommon' - lt_prog_compiler_wl_F77='-Wl,' - ;; - esac - ;; - - mingw* | pw32* | os2*) - # This hack is so that the source file can tell whether it is being - # built for inclusion in a dll (and should export symbols for example). - lt_prog_compiler_pic_F77='-DDLL_EXPORT' - ;; - - hpux9* | hpux10* | hpux11*) - lt_prog_compiler_wl_F77='-Wl,' - # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but - # not for PA HP-UX. - case $host_cpu in - hppa*64*|ia64*) - # +Z the default - ;; - *) - lt_prog_compiler_pic_F77='+Z' - ;; - esac - # Is there a better lt_prog_compiler_static that works with the bundled CC? - lt_prog_compiler_static_F77='${wl}-a ${wl}archive' - ;; - - irix5* | irix6* | nonstopux*) - lt_prog_compiler_wl_F77='-Wl,' - # PIC (with -KPIC) is the default. - lt_prog_compiler_static_F77='-non_shared' - ;; - - newsos6) - lt_prog_compiler_pic_F77='-KPIC' - lt_prog_compiler_static_F77='-Bstatic' - ;; - - linux*) - case $cc_basename in - icc* | ecc*) - lt_prog_compiler_wl_F77='-Wl,' - lt_prog_compiler_pic_F77='-KPIC' - lt_prog_compiler_static_F77='-static' - ;; - pgcc* | pgf77* | pgf90* | pgf95*) - # Portland Group compilers (*not* the Pentium gcc compiler, - # which looks to be a dead project) - lt_prog_compiler_wl_F77='-Wl,' - lt_prog_compiler_pic_F77='-fpic' - lt_prog_compiler_static_F77='-Bstatic' - ;; - ccc*) - lt_prog_compiler_wl_F77='-Wl,' - # All Alpha code is PIC. - lt_prog_compiler_static_F77='-non_shared' - ;; - esac - ;; - - osf3* | osf4* | osf5*) - lt_prog_compiler_wl_F77='-Wl,' - # All OSF/1 code is PIC. - lt_prog_compiler_static_F77='-non_shared' - ;; - - solaris*) - lt_prog_compiler_pic_F77='-KPIC' - lt_prog_compiler_static_F77='-Bstatic' - case $cc_basename in - f77* | f90* | f95*) - lt_prog_compiler_wl_F77='-Qoption ld ';; - *) - lt_prog_compiler_wl_F77='-Wl,';; - esac - ;; - - sunos4*) - lt_prog_compiler_wl_F77='-Qoption ld ' - lt_prog_compiler_pic_F77='-PIC' - lt_prog_compiler_static_F77='-Bstatic' - ;; - - sysv4 | sysv4.2uw2* | sysv4.3*) - lt_prog_compiler_wl_F77='-Wl,' - lt_prog_compiler_pic_F77='-KPIC' - lt_prog_compiler_static_F77='-Bstatic' - ;; - - sysv4*MP*) - if test -d /usr/nec ;then - lt_prog_compiler_pic_F77='-Kconform_pic' - lt_prog_compiler_static_F77='-Bstatic' - fi - ;; - - sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) - lt_prog_compiler_wl_F77='-Wl,' - lt_prog_compiler_pic_F77='-KPIC' - lt_prog_compiler_static_F77='-Bstatic' - ;; - - unicos*) - lt_prog_compiler_wl_F77='-Wl,' - lt_prog_compiler_can_build_shared_F77=no - ;; - - uts4*) - lt_prog_compiler_pic_F77='-pic' - lt_prog_compiler_static_F77='-Bstatic' - ;; - - *) - lt_prog_compiler_can_build_shared_F77=no - ;; - esac - fi - -echo "$as_me:$LINENO: result: $lt_prog_compiler_pic_F77" >&5 -echo "${ECHO_T}$lt_prog_compiler_pic_F77" >&6 - -# -# Check to make sure the PIC flag actually works. -# -if test -n "$lt_prog_compiler_pic_F77"; then - -echo "$as_me:$LINENO: checking if $compiler PIC flag $lt_prog_compiler_pic_F77 works" >&5 -echo $ECHO_N "checking if $compiler PIC flag $lt_prog_compiler_pic_F77 works... $ECHO_C" >&6 -if test "${lt_prog_compiler_pic_works_F77+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - lt_prog_compiler_pic_works_F77=no - ac_outfile=conftest.$ac_objext - printf "$lt_simple_compile_test_code" > conftest.$ac_ext - lt_compiler_flag="$lt_prog_compiler_pic_F77" - # Insert the option either (1) after the last *FLAGS variable, or - # (2) before a word containing "conftest.", or (3) at the end. - # Note that $ac_compile itself does not contain backslashes and begins - # with a dollar sign (not a hyphen), so the echo should work correctly. - # The option is referenced via a variable to avoid confusing sed. - lt_compile=`echo "$ac_compile" | $SED \ - -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ - -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ - -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:13198: $lt_compile\"" >&5) - (eval "$lt_compile" 2>conftest.err) - ac_status=$? - cat conftest.err >&5 - echo "$as_me:13202: \$? = $ac_status" >&5 - if (exit $ac_status) && test -s "$ac_outfile"; then - # The compiler can only warn and ignore the option if not recognized - # So say no if there are warnings other than the usual output. - $echo "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' >conftest.exp - $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 - if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then - lt_prog_compiler_pic_works_F77=yes - fi - fi - $rm conftest* - -fi -echo "$as_me:$LINENO: result: $lt_prog_compiler_pic_works_F77" >&5 -echo "${ECHO_T}$lt_prog_compiler_pic_works_F77" >&6 - -if test x"$lt_prog_compiler_pic_works_F77" = xyes; then - case $lt_prog_compiler_pic_F77 in - "" | " "*) ;; - *) lt_prog_compiler_pic_F77=" $lt_prog_compiler_pic_F77" ;; - esac -else - lt_prog_compiler_pic_F77= - lt_prog_compiler_can_build_shared_F77=no -fi - -fi -case $host_os in - # For platforms which do not support PIC, -DPIC is meaningless: - *djgpp*) - lt_prog_compiler_pic_F77= - ;; - *) - lt_prog_compiler_pic_F77="$lt_prog_compiler_pic_F77" - ;; -esac - -# -# Check to make sure the static flag actually works. -# -wl=$lt_prog_compiler_wl_F77 eval lt_tmp_static_flag=\"$lt_prog_compiler_static_F77\" -echo "$as_me:$LINENO: checking if $compiler static flag $lt_tmp_static_flag works" >&5 -echo $ECHO_N "checking if $compiler static flag $lt_tmp_static_flag works... $ECHO_C" >&6 -if test "${lt_prog_compiler_static_works_F77+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - lt_prog_compiler_static_works_F77=no - save_LDFLAGS="$LDFLAGS" - LDFLAGS="$LDFLAGS $lt_tmp_static_flag" - printf "$lt_simple_link_test_code" > conftest.$ac_ext - if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then - # The linker can only warn and ignore the option if not recognized - # So say no if there are warnings - if test -s conftest.err; then - # Append any errors to the config.log. - cat conftest.err 1>&5 - $echo "X$_lt_linker_boilerplate" | $Xsed -e '/^$/d' > conftest.exp - $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 - if diff conftest.exp conftest.er2 >/dev/null; then - lt_prog_compiler_static_works_F77=yes - fi - else - lt_prog_compiler_static_works_F77=yes - fi - fi - $rm conftest* - LDFLAGS="$save_LDFLAGS" - -fi -echo "$as_me:$LINENO: result: $lt_prog_compiler_static_works_F77" >&5 -echo "${ECHO_T}$lt_prog_compiler_static_works_F77" >&6 - -if test x"$lt_prog_compiler_static_works_F77" = xyes; then - : -else - lt_prog_compiler_static_F77= -fi - - -echo "$as_me:$LINENO: checking if $compiler supports -c -o file.$ac_objext" >&5 -echo $ECHO_N "checking if $compiler supports -c -o file.$ac_objext... $ECHO_C" >&6 -if test "${lt_cv_prog_compiler_c_o_F77+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - lt_cv_prog_compiler_c_o_F77=no - $rm -r conftest 2>/dev/null - mkdir conftest - cd conftest - mkdir out - printf "$lt_simple_compile_test_code" > conftest.$ac_ext - - lt_compiler_flag="-o out/conftest2.$ac_objext" - # Insert the option either (1) after the last *FLAGS variable, or - # (2) before a word containing "conftest.", or (3) at the end. - # Note that $ac_compile itself does not contain backslashes and begins - # with a dollar sign (not a hyphen), so the echo should work correctly. - lt_compile=`echo "$ac_compile" | $SED \ - -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ - -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ - -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:13302: $lt_compile\"" >&5) - (eval "$lt_compile" 2>out/conftest.err) - ac_status=$? - cat out/conftest.err >&5 - echo "$as_me:13306: \$? = $ac_status" >&5 - if (exit $ac_status) && test -s out/conftest2.$ac_objext - then - # The compiler can only warn and ignore the option if not recognized - # So say no if there are warnings - $echo "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' > out/conftest.exp - $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 - if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then - lt_cv_prog_compiler_c_o_F77=yes - fi - fi - chmod u+w . 2>&5 - $rm conftest* - # SGI C++ compiler will create directory out/ii_files/ for - # template instantiation - test -d out/ii_files && $rm out/ii_files/* && rmdir out/ii_files - $rm out/* && rmdir out - cd .. - rmdir conftest - $rm conftest* - -fi -echo "$as_me:$LINENO: result: $lt_cv_prog_compiler_c_o_F77" >&5 -echo "${ECHO_T}$lt_cv_prog_compiler_c_o_F77" >&6 - - -hard_links="nottested" -if test "$lt_cv_prog_compiler_c_o_F77" = no && test "$need_locks" != no; then - # do not overwrite the value of need_locks provided by the user - echo "$as_me:$LINENO: checking if we can lock with hard links" >&5 -echo $ECHO_N "checking if we can lock with hard links... $ECHO_C" >&6 - hard_links=yes - $rm conftest* - ln conftest.a conftest.b 2>/dev/null && hard_links=no - touch conftest.a - ln conftest.a conftest.b 2>&5 || hard_links=no - ln conftest.a conftest.b 2>/dev/null && hard_links=no - echo "$as_me:$LINENO: result: $hard_links" >&5 -echo "${ECHO_T}$hard_links" >&6 - if test "$hard_links" = no; then - { echo "$as_me:$LINENO: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&5 -echo "$as_me: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&2;} - need_locks=warn - fi -else - need_locks=no -fi - -echo "$as_me:$LINENO: checking whether the $compiler linker ($LD) supports shared libraries" >&5 -echo $ECHO_N "checking whether the $compiler linker ($LD) supports shared libraries... $ECHO_C" >&6 - - runpath_var= - allow_undefined_flag_F77= - enable_shared_with_static_runtimes_F77=no - archive_cmds_F77= - archive_expsym_cmds_F77= - old_archive_From_new_cmds_F77= - old_archive_from_expsyms_cmds_F77= - export_dynamic_flag_spec_F77= - whole_archive_flag_spec_F77= - thread_safe_flag_spec_F77= - hardcode_libdir_flag_spec_F77= - hardcode_libdir_flag_spec_ld_F77= - hardcode_libdir_separator_F77= - hardcode_direct_F77=no - hardcode_minus_L_F77=no - hardcode_shlibpath_var_F77=unsupported - link_all_deplibs_F77=unknown - hardcode_automatic_F77=no - module_cmds_F77= - module_expsym_cmds_F77= - always_export_symbols_F77=no - export_symbols_cmds_F77='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' - # include_expsyms should be a list of space-separated symbols to be *always* - # included in the symbol list - include_expsyms_F77= - # exclude_expsyms can be an extended regexp of symbols to exclude - # it will be wrapped by ` (' and `)$', so one must not match beginning or - # end of line. Example: `a|bc|.*d.*' will exclude the symbols `a' and `bc', - # as well as any symbol that contains `d'. - exclude_expsyms_F77="_GLOBAL_OFFSET_TABLE_" - # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out - # platforms (ab)use it in PIC code, but their linkers get confused if - # the symbol is explicitly referenced. Since portable code cannot - # rely on this symbol name, it's probably fine to never include it in - # preloaded symbol tables. - extract_expsyms_cmds= - # Just being paranoid about ensuring that cc_basename is set. - for cc_temp in $compiler""; do - case $cc_temp in - compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; - distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; - \-*) ;; - *) break;; - esac -done -cc_basename=`$echo "X$cc_temp" | $Xsed -e 's%.*/%%' -e "s%^$host_alias-%%"` - - case $host_os in - cygwin* | mingw* | pw32*) - # FIXME: the MSVC++ port hasn't been tested in a loooong time - # When not using gcc, we currently assume that we are using - # Microsoft Visual C++. - if test "$GCC" != yes; then - with_gnu_ld=no - fi - ;; - interix*) - # we just hope/assume this is gcc and not c89 (= MSVC++) - with_gnu_ld=yes - ;; - openbsd*) - with_gnu_ld=no - ;; - esac - - ld_shlibs_F77=yes - if test "$with_gnu_ld" = yes; then - # If archive_cmds runs LD, not CC, wlarc should be empty - wlarc='${wl}' - - # Set some defaults for GNU ld with shared library support. These - # are reset later if shared libraries are not supported. Putting them - # here allows them to be overridden if necessary. - runpath_var=LD_RUN_PATH - hardcode_libdir_flag_spec_F77='${wl}--rpath ${wl}$libdir' - export_dynamic_flag_spec_F77='${wl}--export-dynamic' - # ancient GNU ld didn't support --whole-archive et. al. - if $LD --help 2>&1 | grep 'no-whole-archive' > /dev/null; then - whole_archive_flag_spec_F77="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' - else - whole_archive_flag_spec_F77= - fi - supports_anon_versioning=no - case `$LD -v 2>/dev/null` in - *\ [01].* | *\ 2.[0-9].* | *\ 2.10.*) ;; # catch versions < 2.11 - *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ... - *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ... - *\ 2.11.*) ;; # other 2.11 versions - *) supports_anon_versioning=yes ;; - esac - - # See if GNU ld supports shared libraries. - case $host_os in - aix3* | aix4* | aix5*) - # On AIX/PPC, the GNU linker is very broken - if test "$host_cpu" != ia64; then - ld_shlibs_F77=no - cat <&2 - -*** Warning: the GNU linker, at least up to release 2.9.1, is reported -*** to be unable to reliably create shared libraries on AIX. -*** Therefore, libtool is disabling shared libraries support. If you -*** really care for shared libraries, you may want to modify your PATH -*** so that a non-GNU linker is found, and then restart. - -EOF - fi - ;; - - amigaos*) - archive_cmds_F77='$rm $output_objdir/a2ixlibrary.data~$echo "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$echo "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$echo "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$echo "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' - hardcode_libdir_flag_spec_F77='-L$libdir' - hardcode_minus_L_F77=yes - - # Samuel A. Falvo II reports - # that the semantics of dynamic libraries on AmigaOS, at least up - # to version 4, is to share data among multiple programs linked - # with the same dynamic library. Since this doesn't match the - # behavior of shared libraries on other platforms, we can't use - # them. - ld_shlibs_F77=no - ;; - - beos*) - if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then - allow_undefined_flag_F77=unsupported - # Joseph Beckenbach says some releases of gcc - # support --undefined. This deserves some investigation. FIXME - archive_cmds_F77='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - else - ld_shlibs_F77=no - fi - ;; - - cygwin* | mingw* | pw32*) - # _LT_AC_TAGVAR(hardcode_libdir_flag_spec, F77) is actually meaningless, - # as there is no search path for DLLs. - hardcode_libdir_flag_spec_F77='-L$libdir' - allow_undefined_flag_F77=unsupported - always_export_symbols_F77=no - enable_shared_with_static_runtimes_F77=yes - export_symbols_cmds_F77='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS] /s/.* \([^ ]*\)/\1 DATA/'\'' | $SED -e '\''/^[AITW] /s/.* //'\'' | sort | uniq > $export_symbols' - - if $LD --help 2>&1 | grep 'auto-import' > /dev/null; then - archive_cmds_F77='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' - # If the export-symbols file already is a .def file (1st line - # is EXPORTS), use it as is; otherwise, prepend... - archive_expsym_cmds_F77='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then - cp $export_symbols $output_objdir/$soname.def; - else - echo EXPORTS > $output_objdir/$soname.def; - cat $export_symbols >> $output_objdir/$soname.def; - fi~ - $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' - else - ld_shlibs_F77=no - fi - ;; - - interix3*) - hardcode_direct_F77=no - hardcode_shlibpath_var_F77=no - hardcode_libdir_flag_spec_F77='${wl}-rpath,$libdir' - export_dynamic_flag_spec_F77='${wl}-E' - # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. - # Instead, shared libraries are loaded at an image base (0x10000000 by - # default) and relocated if they conflict, which is a slow very memory - # consuming and fragmenting process. To avoid this, we pick a random, - # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link - # time. Moving up from 0x10000000 also allows more sbrk(2) space. - archive_cmds_F77='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' - archive_expsym_cmds_F77='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' - ;; - - linux*) - if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then - tmp_addflag= - case $cc_basename,$host_cpu in - pgcc*) # Portland Group C compiler - whole_archive_flag_spec_F77='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $echo \"$new_convenience\"` ${wl}--no-whole-archive' - tmp_addflag=' $pic_flag' - ;; - pgf77* | pgf90* | pgf95*) # Portland Group f77 and f90 compilers - whole_archive_flag_spec_F77='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $echo \"$new_convenience\"` ${wl}--no-whole-archive' - tmp_addflag=' $pic_flag -Mnomain' ;; - ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64 - tmp_addflag=' -i_dynamic' ;; - efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64 - tmp_addflag=' -i_dynamic -nofor_main' ;; - ifc* | ifort*) # Intel Fortran compiler - tmp_addflag=' -nofor_main' ;; - esac - archive_cmds_F77='$CC -shared'"$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - - if test $supports_anon_versioning = yes; then - archive_expsym_cmds_F77='$echo "{ global:" > $output_objdir/$libname.ver~ - cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ - $echo "local: *; };" >> $output_objdir/$libname.ver~ - $CC -shared'"$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib' - fi - else - ld_shlibs_F77=no - fi - ;; - - netbsd*) - if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then - archive_cmds_F77='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib' - wlarc= - else - archive_cmds_F77='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - archive_expsym_cmds_F77='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' - fi - ;; - - solaris*) - if $LD -v 2>&1 | grep 'BFD 2\.8' > /dev/null; then - ld_shlibs_F77=no - cat <&2 - -*** Warning: The releases 2.8.* of the GNU linker cannot reliably -*** create shared libraries on Solaris systems. Therefore, libtool -*** is disabling shared libraries support. We urge you to upgrade GNU -*** binutils to release 2.9.1 or newer. Another option is to modify -*** your PATH or compiler configuration so that the native linker is -*** used, and then restart. - -EOF - elif $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then - archive_cmds_F77='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - archive_expsym_cmds_F77='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' - else - ld_shlibs_F77=no - fi - ;; - - sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) - case `$LD -v 2>&1` in - *\ [01].* | *\ 2.[0-9].* | *\ 2.1[0-5].*) - ld_shlibs_F77=no - cat <<_LT_EOF 1>&2 - -*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 can not -*** reliably create shared libraries on SCO systems. Therefore, libtool -*** is disabling shared libraries support. We urge you to upgrade GNU -*** binutils to release 2.16.91.0.3 or newer. Another option is to modify -*** your PATH or compiler configuration so that the native linker is -*** used, and then restart. - -_LT_EOF - ;; - *) - if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then - hardcode_libdir_flag_spec_F77='`test -z "$SCOABSPATH" && echo ${wl}-rpath,$libdir`' - archive_cmds_F77='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname,\${SCOABSPATH:+${install_libdir}/}$soname -o $lib' - archive_expsym_cmds_F77='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname,\${SCOABSPATH:+${install_libdir}/}$soname,-retain-symbols-file,$export_symbols -o $lib' - else - ld_shlibs_F77=no - fi - ;; - esac - ;; - - sunos4*) - archive_cmds_F77='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags' - wlarc= - hardcode_direct_F77=yes - hardcode_shlibpath_var_F77=no - ;; - - *) - if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then - archive_cmds_F77='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - archive_expsym_cmds_F77='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' - else - ld_shlibs_F77=no - fi - ;; - esac - - if test "$ld_shlibs_F77" = no; then - runpath_var= - hardcode_libdir_flag_spec_F77= - export_dynamic_flag_spec_F77= - whole_archive_flag_spec_F77= - fi - else - # PORTME fill in a description of your system's linker (not GNU ld) - case $host_os in - aix3*) - allow_undefined_flag_F77=unsupported - always_export_symbols_F77=yes - archive_expsym_cmds_F77='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname' - # Note: this linker hardcodes the directories in LIBPATH if there - # are no directories specified by -L. - hardcode_minus_L_F77=yes - if test "$GCC" = yes && test -z "$lt_prog_compiler_static"; then - # Neither direct hardcoding nor static linking is supported with a - # broken collect2. - hardcode_direct_F77=unsupported - fi - ;; - - aix4* | aix5*) - if test "$host_cpu" = ia64; then - # On IA64, the linker does run time linking by default, so we don't - # have to do anything special. - aix_use_runtimelinking=no - exp_sym_flag='-Bexport' - no_entry_flag="" - else - # If we're using GNU nm, then we don't want the "-C" option. - # -C means demangle to AIX nm, but means don't demangle with GNU nm - if $NM -V 2>&1 | grep 'GNU' > /dev/null; then - export_symbols_cmds_F77='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$2 == "T") || (\$2 == "D") || (\$2 == "B")) && (substr(\$3,1,1) != ".")) { print \$3 } }'\'' | sort -u > $export_symbols' - else - export_symbols_cmds_F77='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$2 == "T") || (\$2 == "D") || (\$2 == "B")) && (substr(\$3,1,1) != ".")) { print \$3 } }'\'' | sort -u > $export_symbols' - fi - aix_use_runtimelinking=no - - # Test if we are trying to use run time linking or normal - # AIX style linking. If -brtl is somewhere in LDFLAGS, we - # need to do runtime linking. - case $host_os in aix4.[23]|aix4.[23].*|aix5*) - for ld_flag in $LDFLAGS; do - if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then - aix_use_runtimelinking=yes - break - fi - done - ;; - esac - - exp_sym_flag='-bexport' - no_entry_flag='-bnoentry' - fi - - # When large executables or shared objects are built, AIX ld can - # have problems creating the table of contents. If linking a library - # or program results in "error TOC overflow" add -mminimal-toc to - # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not - # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. - - archive_cmds_F77='' - hardcode_direct_F77=yes - hardcode_libdir_separator_F77=':' - link_all_deplibs_F77=yes - - if test "$GCC" = yes; then - case $host_os in aix4.[012]|aix4.[012].*) - # We only want to do this on AIX 4.2 and lower, the check - # below for broken collect2 doesn't work under 4.3+ - collect2name=`${CC} -print-prog-name=collect2` - if test -f "$collect2name" && \ - strings "$collect2name" | grep resolve_lib_name >/dev/null - then - # We have reworked collect2 - hardcode_direct_F77=yes - else - # We have old collect2 - hardcode_direct_F77=unsupported - # It fails to find uninstalled libraries when the uninstalled - # path is not listed in the libpath. Setting hardcode_minus_L - # to unsupported forces relinking - hardcode_minus_L_F77=yes - hardcode_libdir_flag_spec_F77='-L$libdir' - hardcode_libdir_separator_F77= - fi - ;; - esac - shared_flag='-shared' - if test "$aix_use_runtimelinking" = yes; then - shared_flag="$shared_flag "'${wl}-G' - fi - else - # not using gcc - if test "$host_cpu" = ia64; then - # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release - # chokes on -Wl,-G. The following line is correct: - shared_flag='-G' - else - if test "$aix_use_runtimelinking" = yes; then - shared_flag='${wl}-G' - else - shared_flag='${wl}-bM:SRE' - fi - fi - fi - - # It seems that -bexpall does not export symbols beginning with - # underscore (_), so it is better to generate a list of symbols to export. - always_export_symbols_F77=yes - if test "$aix_use_runtimelinking" = yes; then - # Warning - without using the other runtime loading flags (-brtl), - # -berok will link without error, but may produce a broken library. - allow_undefined_flag_F77='-berok' - # Determine the default libpath from the value encoded in an empty executable. - cat >conftest.$ac_ext <<_ACEOF - program main - - end -_ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 - (eval $ac_link) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_f77_werror_flag" - || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest$ac_exeext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - -aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } -}'` -# Check for a 64-bit object if we didn't find anything. -if test -z "$aix_libpath"; then aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } -}'`; fi -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -fi -rm -f conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi - - hardcode_libdir_flag_spec_F77='${wl}-blibpath:$libdir:'"$aix_libpath" - archive_expsym_cmds_F77="\$CC"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then echo "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag" - else - if test "$host_cpu" = ia64; then - hardcode_libdir_flag_spec_F77='${wl}-R $libdir:/usr/lib:/lib' - allow_undefined_flag_F77="-z nodefs" - archive_expsym_cmds_F77="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols" - else - # Determine the default libpath from the value encoded in an empty executable. - cat >conftest.$ac_ext <<_ACEOF - program main - - end -_ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 - (eval $ac_link) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_f77_werror_flag" - || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest$ac_exeext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - -aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } -}'` -# Check for a 64-bit object if we didn't find anything. -if test -z "$aix_libpath"; then aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } -}'`; fi -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -fi -rm -f conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi - - hardcode_libdir_flag_spec_F77='${wl}-blibpath:$libdir:'"$aix_libpath" - # Warning - without using the other run time loading flags, - # -berok will link without error, but may produce a broken library. - no_undefined_flag_F77=' ${wl}-bernotok' - allow_undefined_flag_F77=' ${wl}-berok' - # Exported symbols can be pulled into shared objects from archives - whole_archive_flag_spec_F77='$convenience' - archive_cmds_need_lc_F77=yes - # This is similar to how AIX traditionally builds its shared libraries. - archive_expsym_cmds_F77="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' - fi - fi - ;; - - amigaos*) - archive_cmds_F77='$rm $output_objdir/a2ixlibrary.data~$echo "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$echo "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$echo "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$echo "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' - hardcode_libdir_flag_spec_F77='-L$libdir' - hardcode_minus_L_F77=yes - # see comment about different semantics on the GNU ld section - ld_shlibs_F77=no - ;; - - bsdi[45]*) - export_dynamic_flag_spec_F77=-rdynamic - ;; - - cygwin* | mingw* | pw32*) - # When not using gcc, we currently assume that we are using - # Microsoft Visual C++. - # hardcode_libdir_flag_spec is actually meaningless, as there is - # no search path for DLLs. - hardcode_libdir_flag_spec_F77=' ' - allow_undefined_flag_F77=unsupported - # Tell ltmain to make .lib files, not .a files. - libext=lib - # Tell ltmain to make .dll files, not .so files. - shrext_cmds=".dll" - # FIXME: Setting linknames here is a bad hack. - archive_cmds_F77='$CC -o $lib $libobjs $compiler_flags `echo "$deplibs" | $SED -e '\''s/ -lc$//'\''` -link -dll~linknames=' - # The linker will automatically build a .lib file if we build a DLL. - old_archive_From_new_cmds_F77='true' - # FIXME: Should let the user specify the lib program. - old_archive_cmds_F77='lib /OUT:$oldlib$oldobjs$old_deplibs' - fix_srcfile_path_F77='`cygpath -w "$srcfile"`' - enable_shared_with_static_runtimes_F77=yes - ;; - - darwin* | rhapsody*) - case $host_os in - rhapsody* | darwin1.[012]) - allow_undefined_flag_F77='${wl}-undefined ${wl}suppress' - ;; - *) # Darwin 1.3 on - if test -z ${MACOSX_DEPLOYMENT_TARGET} ; then - allow_undefined_flag_F77='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' - else - case ${MACOSX_DEPLOYMENT_TARGET} in - 10.[012]) - allow_undefined_flag_F77='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' - ;; - 10.*) - allow_undefined_flag_F77='${wl}-undefined ${wl}dynamic_lookup' - ;; - esac - fi - ;; - esac - archive_cmds_need_lc_F77=no - hardcode_direct_F77=no - hardcode_automatic_F77=yes - hardcode_shlibpath_var_F77=unsupported - whole_archive_flag_spec_F77='' - link_all_deplibs_F77=yes - if test "$GCC" = yes ; then - output_verbose_link_cmd='echo' - archive_cmds_F77='$CC -dynamiclib $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags -install_name $rpath/$soname $verstring' - module_cmds_F77='$CC $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags' - # Don't fix this by using the ld -exported_symbols_list flag, it doesn't exist in older darwin lds - archive_expsym_cmds_F77='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC -dynamiclib $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags -install_name $rpath/$soname $verstring~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' - module_expsym_cmds_F77='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' - else - case $cc_basename in - xlc*) - output_verbose_link_cmd='echo' - archive_cmds_F77='$CC -qmkshrobj $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-install_name ${wl}`echo $rpath/$soname` $verstring' - module_cmds_F77='$CC $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags' - # Don't fix this by using the ld -exported_symbols_list flag, it doesn't exist in older darwin lds - archive_expsym_cmds_F77='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC -qmkshrobj $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-install_name ${wl}$rpath/$soname $verstring~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' - module_expsym_cmds_F77='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' - ;; - *) - ld_shlibs_F77=no - ;; - esac - fi - ;; - - dgux*) - archive_cmds_F77='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - hardcode_libdir_flag_spec_F77='-L$libdir' - hardcode_shlibpath_var_F77=no - ;; - - freebsd1*) - ld_shlibs_F77=no - ;; - - # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor - # support. Future versions do this automatically, but an explicit c++rt0.o - # does not break anything, and helps significantly (at the cost of a little - # extra space). - freebsd2.2*) - archive_cmds_F77='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o' - hardcode_libdir_flag_spec_F77='-R$libdir' - hardcode_direct_F77=yes - hardcode_shlibpath_var_F77=no - ;; - - # Unfortunately, older versions of FreeBSD 2 do not have this feature. - freebsd2*) - archive_cmds_F77='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' - hardcode_direct_F77=yes - hardcode_minus_L_F77=yes - hardcode_shlibpath_var_F77=no - ;; - - # FreeBSD 3 and greater uses gcc -shared to do shared libraries. - freebsd* | kfreebsd*-gnu | dragonfly*) - archive_cmds_F77='$CC -shared -o $lib $libobjs $deplibs $compiler_flags' - hardcode_libdir_flag_spec_F77='-R$libdir' - hardcode_direct_F77=yes - hardcode_shlibpath_var_F77=no - ;; - - hpux9*) - if test "$GCC" = yes; then - archive_cmds_F77='$rm $output_objdir/$soname~$CC -shared -fPIC ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' - else - archive_cmds_F77='$rm $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' - fi - hardcode_libdir_flag_spec_F77='${wl}+b ${wl}$libdir' - hardcode_libdir_separator_F77=: - hardcode_direct_F77=yes - - # hardcode_minus_L: Not really in the search PATH, - # but as the default location of the library. - hardcode_minus_L_F77=yes - export_dynamic_flag_spec_F77='${wl}-E' - ;; - - hpux10*) - if test "$GCC" = yes -a "$with_gnu_ld" = no; then - archive_cmds_F77='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' - else - archive_cmds_F77='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' - fi - if test "$with_gnu_ld" = no; then - hardcode_libdir_flag_spec_F77='${wl}+b ${wl}$libdir' - hardcode_libdir_separator_F77=: - - hardcode_direct_F77=yes - export_dynamic_flag_spec_F77='${wl}-E' - - # hardcode_minus_L: Not really in the search PATH, - # but as the default location of the library. - hardcode_minus_L_F77=yes - fi - ;; - - hpux11*) - if test "$GCC" = yes -a "$with_gnu_ld" = no; then - case $host_cpu in - hppa*64*) - archive_cmds_F77='$CC -shared ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' - ;; - ia64*) - archive_cmds_F77='$CC -shared ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' - ;; - *) - archive_cmds_F77='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' - ;; - esac - else - case $host_cpu in - hppa*64*) - archive_cmds_F77='$CC -b ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' - ;; - ia64*) - archive_cmds_F77='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' - ;; - *) - archive_cmds_F77='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' - ;; - esac - fi - if test "$with_gnu_ld" = no; then - hardcode_libdir_flag_spec_F77='${wl}+b ${wl}$libdir' - hardcode_libdir_separator_F77=: - - case $host_cpu in - hppa*64*|ia64*) - hardcode_libdir_flag_spec_ld_F77='+b $libdir' - hardcode_direct_F77=no - hardcode_shlibpath_var_F77=no - ;; - *) - hardcode_direct_F77=yes - export_dynamic_flag_spec_F77='${wl}-E' - - # hardcode_minus_L: Not really in the search PATH, - # but as the default location of the library. - hardcode_minus_L_F77=yes - ;; - esac - fi - ;; - - irix5* | irix6* | nonstopux*) - if test "$GCC" = yes; then - archive_cmds_F77='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' - else - archive_cmds_F77='$LD -shared $libobjs $deplibs $linker_flags -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib' - hardcode_libdir_flag_spec_ld_F77='-rpath $libdir' - fi - hardcode_libdir_flag_spec_F77='${wl}-rpath ${wl}$libdir' - hardcode_libdir_separator_F77=: - link_all_deplibs_F77=yes - ;; - - netbsd*) - if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then - archive_cmds_F77='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out - else - archive_cmds_F77='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF - fi - hardcode_libdir_flag_spec_F77='-R$libdir' - hardcode_direct_F77=yes - hardcode_shlibpath_var_F77=no - ;; - - newsos6) - archive_cmds_F77='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - hardcode_direct_F77=yes - hardcode_libdir_flag_spec_F77='${wl}-rpath ${wl}$libdir' - hardcode_libdir_separator_F77=: - hardcode_shlibpath_var_F77=no - ;; - - openbsd*) - hardcode_direct_F77=yes - hardcode_shlibpath_var_F77=no - if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then - archive_cmds_F77='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' - archive_expsym_cmds_F77='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-retain-symbols-file,$export_symbols' - hardcode_libdir_flag_spec_F77='${wl}-rpath,$libdir' - export_dynamic_flag_spec_F77='${wl}-E' - else - case $host_os in - openbsd[01].* | openbsd2.[0-7] | openbsd2.[0-7].*) - archive_cmds_F77='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' - hardcode_libdir_flag_spec_F77='-R$libdir' - ;; - *) - archive_cmds_F77='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' - hardcode_libdir_flag_spec_F77='${wl}-rpath,$libdir' - ;; - esac - fi - ;; - - os2*) - hardcode_libdir_flag_spec_F77='-L$libdir' - hardcode_minus_L_F77=yes - allow_undefined_flag_F77=unsupported - archive_cmds_F77='$echo "LIBRARY $libname INITINSTANCE" > $output_objdir/$libname.def~$echo "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~$echo DATA >> $output_objdir/$libname.def~$echo " SINGLE NONSHARED" >> $output_objdir/$libname.def~$echo EXPORTS >> $output_objdir/$libname.def~emxexp $libobjs >> $output_objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $compiler_flags $output_objdir/$libname.def' - old_archive_From_new_cmds_F77='emximp -o $output_objdir/$libname.a $output_objdir/$libname.def' - ;; - - osf3*) - if test "$GCC" = yes; then - allow_undefined_flag_F77=' ${wl}-expect_unresolved ${wl}\*' - archive_cmds_F77='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' - else - allow_undefined_flag_F77=' -expect_unresolved \*' - archive_cmds_F77='$LD -shared${allow_undefined_flag} $libobjs $deplibs $linker_flags -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib' - fi - hardcode_libdir_flag_spec_F77='${wl}-rpath ${wl}$libdir' - hardcode_libdir_separator_F77=: - ;; - - osf4* | osf5*) # as osf3* with the addition of -msym flag - if test "$GCC" = yes; then - allow_undefined_flag_F77=' ${wl}-expect_unresolved ${wl}\*' - archive_cmds_F77='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' - hardcode_libdir_flag_spec_F77='${wl}-rpath ${wl}$libdir' - else - allow_undefined_flag_F77=' -expect_unresolved \*' - archive_cmds_F77='$LD -shared${allow_undefined_flag} $libobjs $deplibs $linker_flags -msym -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib' - archive_expsym_cmds_F77='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; echo "-hidden">> $lib.exp~ - $LD -shared${allow_undefined_flag} -input $lib.exp $linker_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib~$rm $lib.exp' - - # Both c and cxx compiler support -rpath directly - hardcode_libdir_flag_spec_F77='-rpath $libdir' - fi - hardcode_libdir_separator_F77=: - ;; - - solaris*) - no_undefined_flag_F77=' -z text' - if test "$GCC" = yes; then - wlarc='${wl}' - archive_cmds_F77='$CC -shared ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' - archive_expsym_cmds_F77='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~ - $CC -shared ${wl}-M ${wl}$lib.exp ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags~$rm $lib.exp' - else - wlarc='' - archive_cmds_F77='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags' - archive_expsym_cmds_F77='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~ - $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$rm $lib.exp' - fi - hardcode_libdir_flag_spec_F77='-R$libdir' - hardcode_shlibpath_var_F77=no - case $host_os in - solaris2.[0-5] | solaris2.[0-5].*) ;; - *) - # The compiler driver will combine linker options so we - # cannot just pass the convience library names through - # without $wl, iff we do not link with $LD. - # Luckily, gcc supports the same syntax we need for Sun Studio. - # Supported since Solaris 2.6 (maybe 2.5.1?) - case $wlarc in - '') - whole_archive_flag_spec_F77='-z allextract$convenience -z defaultextract' ;; - *) - whole_archive_flag_spec_F77='${wl}-z ${wl}allextract`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $echo \"$new_convenience\"` ${wl}-z ${wl}defaultextract' ;; - esac ;; - esac - link_all_deplibs_F77=yes - ;; - - sunos4*) - if test "x$host_vendor" = xsequent; then - # Use $CC to link under sequent, because it throws in some extra .o - # files that make .init and .fini sections work. - archive_cmds_F77='$CC -G ${wl}-h $soname -o $lib $libobjs $deplibs $compiler_flags' - else - archive_cmds_F77='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags' - fi - hardcode_libdir_flag_spec_F77='-L$libdir' - hardcode_direct_F77=yes - hardcode_minus_L_F77=yes - hardcode_shlibpath_var_F77=no - ;; - - sysv4) - case $host_vendor in - sni) - archive_cmds_F77='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - hardcode_direct_F77=yes # is this really true??? - ;; - siemens) - ## LD is ld it makes a PLAMLIB - ## CC just makes a GrossModule. - archive_cmds_F77='$LD -G -o $lib $libobjs $deplibs $linker_flags' - reload_cmds_F77='$CC -r -o $output$reload_objs' - hardcode_direct_F77=no - ;; - motorola) - archive_cmds_F77='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - hardcode_direct_F77=no #Motorola manual says yes, but my tests say they lie - ;; - esac - runpath_var='LD_RUN_PATH' - hardcode_shlibpath_var_F77=no - ;; - - sysv4.3*) - archive_cmds_F77='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - hardcode_shlibpath_var_F77=no - export_dynamic_flag_spec_F77='-Bexport' - ;; - - sysv4*MP*) - if test -d /usr/nec; then - archive_cmds_F77='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - hardcode_shlibpath_var_F77=no - runpath_var=LD_RUN_PATH - hardcode_runpath_var=yes - ld_shlibs_F77=yes - fi - ;; - - sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7*) - no_undefined_flag_F77='${wl}-z,text' - archive_cmds_need_lc_F77=no - hardcode_shlibpath_var_F77=no - runpath_var='LD_RUN_PATH' - - if test "$GCC" = yes; then - archive_cmds_F77='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - archive_expsym_cmds_F77='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - else - archive_cmds_F77='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - archive_expsym_cmds_F77='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - fi - ;; - - sysv5* | sco3.2v5* | sco5v6*) - # Note: We can NOT use -z defs as we might desire, because we do not - # link with -lc, and that would cause any symbols used from libc to - # always be unresolved, which means just about no library would - # ever link correctly. If we're not using GNU ld we use -z text - # though, which does catch some bad symbols but isn't as heavy-handed - # as -z defs. - no_undefined_flag_F77='${wl}-z,text' - allow_undefined_flag_F77='${wl}-z,nodefs' - archive_cmds_need_lc_F77=no - hardcode_shlibpath_var_F77=no - hardcode_libdir_flag_spec_F77='`test -z "$SCOABSPATH" && echo ${wl}-R,$libdir`' - hardcode_libdir_separator_F77=':' - link_all_deplibs_F77=yes - export_dynamic_flag_spec_F77='${wl}-Bexport' - runpath_var='LD_RUN_PATH' - - if test "$GCC" = yes; then - archive_cmds_F77='$CC -shared ${wl}-h,\${SCOABSPATH:+${install_libdir}/}$soname -o $lib $libobjs $deplibs $compiler_flags' - archive_expsym_cmds_F77='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,\${SCOABSPATH:+${install_libdir}/}$soname -o $lib $libobjs $deplibs $compiler_flags' - else - archive_cmds_F77='$CC -G ${wl}-h,\${SCOABSPATH:+${install_libdir}/}$soname -o $lib $libobjs $deplibs $compiler_flags' - archive_expsym_cmds_F77='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,\${SCOABSPATH:+${install_libdir}/}$soname -o $lib $libobjs $deplibs $compiler_flags' - fi - ;; - - uts4*) - archive_cmds_F77='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - hardcode_libdir_flag_spec_F77='-L$libdir' - hardcode_shlibpath_var_F77=no - ;; - - *) - ld_shlibs_F77=no - ;; - esac - fi - -echo "$as_me:$LINENO: result: $ld_shlibs_F77" >&5 -echo "${ECHO_T}$ld_shlibs_F77" >&6 -test "$ld_shlibs_F77" = no && can_build_shared=no - -# -# Do we need to explicitly link libc? -# -case "x$archive_cmds_need_lc_F77" in -x|xyes) - # Assume -lc should be added - archive_cmds_need_lc_F77=yes - - if test "$enable_shared" = yes && test "$GCC" = yes; then - case $archive_cmds_F77 in - *'~'*) - # FIXME: we may have to deal with multi-command sequences. - ;; - '$CC '*) - # Test whether the compiler implicitly links with -lc since on some - # systems, -lgcc has to come before -lc. If gcc already passes -lc - # to ld, don't add -lc before -lgcc. - echo "$as_me:$LINENO: checking whether -lc should be explicitly linked in" >&5 -echo $ECHO_N "checking whether -lc should be explicitly linked in... $ECHO_C" >&6 - $rm conftest* - printf "$lt_simple_compile_test_code" > conftest.$ac_ext - - if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } 2>conftest.err; then - soname=conftest - lib=conftest - libobjs=conftest.$ac_objext - deplibs= - wl=$lt_prog_compiler_wl_F77 - pic_flag=$lt_prog_compiler_pic_F77 - compiler_flags=-v - linker_flags=-v - verstring= - output_objdir=. - libname=conftest - lt_save_allow_undefined_flag=$allow_undefined_flag_F77 - allow_undefined_flag_F77= - if { (eval echo "$as_me:$LINENO: \"$archive_cmds_F77 2\>\&1 \| grep \" -lc \" \>/dev/null 2\>\&1\"") >&5 - (eval $archive_cmds_F77 2\>\&1 \| grep \" -lc \" \>/dev/null 2\>\&1) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } - then - archive_cmds_need_lc_F77=no - else - archive_cmds_need_lc_F77=yes - fi - allow_undefined_flag_F77=$lt_save_allow_undefined_flag - else - cat conftest.err 1>&5 - fi - $rm conftest* - echo "$as_me:$LINENO: result: $archive_cmds_need_lc_F77" >&5 -echo "${ECHO_T}$archive_cmds_need_lc_F77" >&6 - ;; - esac - fi - ;; -esac - -echo "$as_me:$LINENO: checking dynamic linker characteristics" >&5 -echo $ECHO_N "checking dynamic linker characteristics... $ECHO_C" >&6 -library_names_spec= -libname_spec='lib$name' -soname_spec= -shrext_cmds=".so" -postinstall_cmds= -postuninstall_cmds= -finish_cmds= -finish_eval= -shlibpath_var= -shlibpath_overrides_runpath=unknown -version_type=none -dynamic_linker="$host_os ld.so" -sys_lib_dlsearch_path_spec="/lib /usr/lib" -if test "$GCC" = yes; then - sys_lib_search_path_spec=`$CC -print-search-dirs | grep "^libraries:" | $SED -e "s/^libraries://" -e "s,=/,/,g"` - if echo "$sys_lib_search_path_spec" | grep ';' >/dev/null ; then - # if the path contains ";" then we assume it to be the separator - # otherwise default to the standard path separator (i.e. ":") - it is - # assumed that no part of a normal pathname contains ";" but that should - # okay in the real world where ";" in dirpaths is itself problematic. - sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` - else - sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` - fi -else - sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" -fi -need_lib_prefix=unknown -hardcode_into_libs=no - -# when you set need_version to no, make sure it does not cause -set_version -# flags to be left without arguments -need_version=unknown - -case $host_os in -aix3*) - version_type=linux - library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a' - shlibpath_var=LIBPATH - - # AIX 3 has no versioning support, so we append a major version to the name. - soname_spec='${libname}${release}${shared_ext}$major' - ;; - -aix4* | aix5*) - version_type=linux - need_lib_prefix=no - need_version=no - hardcode_into_libs=yes - if test "$host_cpu" = ia64; then - # AIX 5 supports IA64 - library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}' - shlibpath_var=LD_LIBRARY_PATH - else - # With GCC up to 2.95.x, collect2 would create an import file - # for dependence libraries. The import file would start with - # the line `#! .'. This would cause the generated library to - # depend on `.', always an invalid library. This was fixed in - # development snapshots of GCC prior to 3.0. - case $host_os in - aix4 | aix4.[01] | aix4.[01].*) - if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' - echo ' yes ' - echo '#endif'; } | ${CC} -E - | grep yes > /dev/null; then - : - else - can_build_shared=no - fi - ;; - esac - # AIX (on Power*) has no versioning support, so currently we can not hardcode correct - # soname into executable. Probably we can add versioning support to - # collect2, so additional links can be useful in future. - if test "$aix_use_runtimelinking" = yes; then - # If using run time linking (on AIX 4.2 or later) use lib.so - # instead of lib.a to let people know that these are not - # typical AIX shared libraries. - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - else - # We preserve .a as extension for shared libraries through AIX4.2 - # and later when we are not doing run time linking. - library_names_spec='${libname}${release}.a $libname.a' - soname_spec='${libname}${release}${shared_ext}$major' - fi - shlibpath_var=LIBPATH - fi - ;; - -amigaos*) - library_names_spec='$libname.ixlibrary $libname.a' - # Create ${libname}_ixlibrary.a entries in /sys/libs. - finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`$echo "X$lib" | $Xsed -e '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; test $rm /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' - ;; - -beos*) - library_names_spec='${libname}${shared_ext}' - dynamic_linker="$host_os ld.so" - shlibpath_var=LIBRARY_PATH - ;; - -bsdi[45]*) - version_type=linux - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' - shlibpath_var=LD_LIBRARY_PATH - sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" - sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" - # the default ld.so.conf also contains /usr/contrib/lib and - # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow - # libtool to hard-code these into programs - ;; - -cygwin* | mingw* | pw32*) - version_type=windows - shrext_cmds=".dll" - need_version=no - need_lib_prefix=no - - case $GCC,$host_os in - yes,cygwin* | yes,mingw* | yes,pw32*) - library_names_spec='$libname.dll.a' - # DLL is installed to $(libdir)/../bin by postinstall_cmds - postinstall_cmds='base_file=`basename \${file}`~ - dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i;echo \$dlname'\''`~ - dldir=$destdir/`dirname \$dlpath`~ - test -d \$dldir || mkdir -p \$dldir~ - $install_prog $dir/$dlname \$dldir/$dlname~ - chmod a+x \$dldir/$dlname' - postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ - dlpath=$dir/\$dldll~ - $rm \$dlpath' - shlibpath_overrides_runpath=yes - - case $host_os in - cygwin*) - # Cygwin DLLs use 'cyg' prefix rather than 'lib' - soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' - sys_lib_search_path_spec="/usr/lib /lib/w32api /lib /usr/local/lib" - ;; - mingw*) - # MinGW DLLs use traditional 'lib' prefix - soname_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' - sys_lib_search_path_spec=`$CC -print-search-dirs | grep "^libraries:" | $SED -e "s/^libraries://" -e "s,=/,/,g"` - if echo "$sys_lib_search_path_spec" | grep ';[c-zC-Z]:/' >/dev/null; then - # It is most probably a Windows format PATH printed by - # mingw gcc, but we are running on Cygwin. Gcc prints its search - # path with ; separators, and with drive letters. We can handle the - # drive letters (cygwin fileutils understands them), so leave them, - # especially as we might pass files found there to a mingw objdump, - # which wouldn't understand a cygwinified path. Ahh. - sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` - else - sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` - fi - ;; - pw32*) - # pw32 DLLs use 'pw' prefix rather than 'lib' - library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' - ;; - esac - ;; - - *) - library_names_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext} $libname.lib' - ;; - esac - dynamic_linker='Win32 ld.exe' - # FIXME: first we should search . and the directory the executable is in - shlibpath_var=PATH - ;; - -darwin* | rhapsody*) - dynamic_linker="$host_os dyld" - version_type=darwin - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${versuffix}$shared_ext ${libname}${release}${major}$shared_ext ${libname}$shared_ext' - soname_spec='${libname}${release}${major}$shared_ext' - shlibpath_overrides_runpath=yes - shlibpath_var=DYLD_LIBRARY_PATH - shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' - # Apple's gcc prints 'gcc -print-search-dirs' doesn't operate the same. - if test "$GCC" = yes; then - sys_lib_search_path_spec=`$CC -print-search-dirs | tr "\n" "$PATH_SEPARATOR" | sed -e 's/libraries:/@libraries:/' | tr "@" "\n" | grep "^libraries:" | sed -e "s/^libraries://" -e "s,=/,/,g" -e "s,$PATH_SEPARATOR, ,g" -e "s,.*,& /lib /usr/lib /usr/local/lib,g"` - else - sys_lib_search_path_spec='/lib /usr/lib /usr/local/lib' - fi - sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' - ;; - -dgux*) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - ;; - -freebsd1*) - dynamic_linker=no - ;; - -kfreebsd*-gnu) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=no - hardcode_into_libs=yes - dynamic_linker='GNU ld.so' - ;; - -freebsd* | dragonfly*) - # DragonFly does not have aout. When/if they implement a new - # versioning mechanism, adjust this. - if test -x /usr/bin/objformat; then - objformat=`/usr/bin/objformat` - else - case $host_os in - freebsd[123]*) objformat=aout ;; - *) objformat=elf ;; - esac - fi - version_type=freebsd-$objformat - case $version_type in - freebsd-elf*) - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' - need_version=no - need_lib_prefix=no - ;; - freebsd-*) - library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix' - need_version=yes - ;; - esac - shlibpath_var=LD_LIBRARY_PATH - case $host_os in - freebsd2*) - shlibpath_overrides_runpath=yes - ;; - freebsd3.[01]* | freebsdelf3.[01]*) - shlibpath_overrides_runpath=yes - hardcode_into_libs=yes - ;; - freebsd3.[2-9]* | freebsdelf3.[2-9]* | \ - freebsd4.[0-5] | freebsdelf4.[0-5] | freebsd4.1.1 | freebsdelf4.1.1) - shlibpath_overrides_runpath=no - hardcode_into_libs=yes - ;; - freebsd*) # from 4.6 on - shlibpath_overrides_runpath=yes - hardcode_into_libs=yes - ;; - esac - ;; - -gnu*) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - hardcode_into_libs=yes - ;; - -hpux9* | hpux10* | hpux11*) - # Give a soname corresponding to the major version so that dld.sl refuses to - # link against other versions. - version_type=sunos - need_lib_prefix=no - need_version=no - case $host_cpu in - ia64*) - shrext_cmds='.so' - hardcode_into_libs=yes - dynamic_linker="$host_os dld.so" - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - if test "X$HPUX_IA64_MODE" = X32; then - sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" - else - sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" - fi - sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec - ;; - hppa*64*) - shrext_cmds='.sl' - hardcode_into_libs=yes - dynamic_linker="$host_os dld.sl" - shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH - shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" - sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec - ;; - *) - shrext_cmds='.sl' - dynamic_linker="$host_os dld.sl" - shlibpath_var=SHLIB_PATH - shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - ;; - esac - # HP-UX runs *really* slowly unless shared libraries are mode 555. - postinstall_cmds='chmod 555 $lib' - ;; - -interix3*) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=no - hardcode_into_libs=yes - ;; - -irix5* | irix6* | nonstopux*) - case $host_os in - nonstopux*) version_type=nonstopux ;; - *) - if test "$lt_cv_prog_gnu_ld" = yes; then - version_type=linux - else - version_type=irix - fi ;; - esac - need_lib_prefix=no - need_version=no - soname_spec='${libname}${release}${shared_ext}$major' - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}' - case $host_os in - irix5* | nonstopux*) - libsuff= shlibsuff= - ;; - *) - case $LD in # libtool.m4 will add one of these switches to LD - *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") - libsuff= shlibsuff= libmagic=32-bit;; - *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") - libsuff=32 shlibsuff=N32 libmagic=N32;; - *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") - libsuff=64 shlibsuff=64 libmagic=64-bit;; - *) libsuff= shlibsuff= libmagic=never-match;; - esac - ;; - esac - shlibpath_var=LD_LIBRARY${shlibsuff}_PATH - shlibpath_overrides_runpath=no - sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}" - sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}" - hardcode_into_libs=yes - ;; - -# No shared lib support for Linux oldld, aout, or coff. -linux*oldld* | linux*aout* | linux*coff*) - dynamic_linker=no - ;; - -# This must be Linux ELF. -linux*) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=no - # This implies no fast_install, which is unacceptable. - # Some rework will be needed to allow for fast_install - # before this can be enabled. - hardcode_into_libs=yes - - # Append ld.so.conf contents to the search path - if test -f /etc/ld.so.conf; then - lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;/^$/d' | tr '\n' ' '` - sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" - fi - - # We used to test for /lib/ld.so.1 and disable shared libraries on - # powerpc, because MkLinux only supported shared libraries with the - # GNU dynamic linker. Since this was broken with cross compilers, - # most powerpc-linux boxes support dynamic linking these days and - # people can always --disable-shared, the test was removed, and we - # assume the GNU/Linux dynamic linker is in use. - dynamic_linker='GNU/Linux ld.so' - ;; - -knetbsd*-gnu) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=no - hardcode_into_libs=yes - dynamic_linker='GNU ld.so' - ;; - -netbsd*) - version_type=sunos - need_lib_prefix=no - need_version=no - if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' - finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' - dynamic_linker='NetBSD (a.out) ld.so' - else - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - dynamic_linker='NetBSD ld.elf_so' - fi - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - hardcode_into_libs=yes - ;; - -newsos6) - version_type=linux - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - ;; - -nto-qnx*) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - ;; - -openbsd*) - version_type=sunos - sys_lib_dlsearch_path_spec="/usr/lib" - need_lib_prefix=no - # Some older versions of OpenBSD (3.3 at least) *do* need versioned libs. - case $host_os in - openbsd3.3 | openbsd3.3.*) need_version=yes ;; - *) need_version=no ;; - esac - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' - finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' - shlibpath_var=LD_LIBRARY_PATH - if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then - case $host_os in - openbsd2.[89] | openbsd2.[89].*) - shlibpath_overrides_runpath=no - ;; - *) - shlibpath_overrides_runpath=yes - ;; - esac - else - shlibpath_overrides_runpath=yes - fi - ;; - -os2*) - libname_spec='$name' - shrext_cmds=".dll" - need_lib_prefix=no - library_names_spec='$libname${shared_ext} $libname.a' - dynamic_linker='OS/2 ld.exe' - shlibpath_var=LIBPATH - ;; - -osf3* | osf4* | osf5*) - version_type=osf - need_lib_prefix=no - need_version=no - soname_spec='${libname}${release}${shared_ext}$major' - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - shlibpath_var=LD_LIBRARY_PATH - sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" - sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec" - ;; - -solaris*) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - hardcode_into_libs=yes - # ldd complains unless libraries are executable - postinstall_cmds='chmod +x $lib' - ;; - -sunos4*) - version_type=sunos - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' - finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - if test "$with_gnu_ld" = yes; then - need_lib_prefix=no - fi - need_version=yes - ;; - -sysv4 | sysv4.3*) - version_type=linux - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - case $host_vendor in - sni) - shlibpath_overrides_runpath=no - need_lib_prefix=no - export_dynamic_flag_spec='${wl}-Blargedynsym' - runpath_var=LD_RUN_PATH - ;; - siemens) - need_lib_prefix=no - ;; - motorola) - need_lib_prefix=no - need_version=no - shlibpath_overrides_runpath=no - sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' - ;; - esac - ;; - -sysv4*MP*) - if test -d /usr/nec ;then - version_type=linux - library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}' - soname_spec='$libname${shared_ext}.$major' - shlibpath_var=LD_LIBRARY_PATH - fi - ;; - -sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) - version_type=freebsd-elf - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - hardcode_into_libs=yes - if test "$with_gnu_ld" = yes; then - sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' - shlibpath_overrides_runpath=no - else - sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' - shlibpath_overrides_runpath=yes - case $host_os in - sco3.2v5*) - sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" - ;; - esac - fi - sys_lib_dlsearch_path_spec='/usr/lib' - ;; - -uts4*) - version_type=linux - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - ;; - -*) - dynamic_linker=no - ;; -esac -echo "$as_me:$LINENO: result: $dynamic_linker" >&5 -echo "${ECHO_T}$dynamic_linker" >&6 -test "$dynamic_linker" = no && can_build_shared=no - -variables_saved_for_relink="PATH $shlibpath_var $runpath_var" -if test "$GCC" = yes; then - variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" -fi - -echo "$as_me:$LINENO: checking how to hardcode library paths into programs" >&5 -echo $ECHO_N "checking how to hardcode library paths into programs... $ECHO_C" >&6 -hardcode_action_F77= -if test -n "$hardcode_libdir_flag_spec_F77" || \ - test -n "$runpath_var_F77" || \ - test "X$hardcode_automatic_F77" = "Xyes" ; then - - # We can hardcode non-existant directories. - if test "$hardcode_direct_F77" != no && - # If the only mechanism to avoid hardcoding is shlibpath_var, we - # have to relink, otherwise we might link with an installed library - # when we should be linking with a yet-to-be-installed one - ## test "$_LT_AC_TAGVAR(hardcode_shlibpath_var, F77)" != no && - test "$hardcode_minus_L_F77" != no; then - # Linking always hardcodes the temporary library directory. - hardcode_action_F77=relink - else - # We can link without hardcoding, and we can hardcode nonexisting dirs. - hardcode_action_F77=immediate - fi -else - # We cannot hardcode anything, or else we can only hardcode existing - # directories. - hardcode_action_F77=unsupported -fi -echo "$as_me:$LINENO: result: $hardcode_action_F77" >&5 -echo "${ECHO_T}$hardcode_action_F77" >&6 - -if test "$hardcode_action_F77" = relink; then - # Fast installation is not supported - enable_fast_install=no -elif test "$shlibpath_overrides_runpath" = yes || - test "$enable_shared" = no; then - # Fast installation is not necessary - enable_fast_install=needless -fi - - -# The else clause should only fire when bootstrapping the -# libtool distribution, otherwise you forgot to ship ltmain.sh -# with your package, and you will get complaints that there are -# no rules to generate ltmain.sh. -if test -f "$ltmain"; then - # See if we are running on zsh, and set the options which allow our commands through - # without removal of \ escapes. - if test -n "${ZSH_VERSION+set}" ; then - setopt NO_GLOB_SUBST - fi - # Now quote all the things that may contain metacharacters while being - # careful not to overquote the AC_SUBSTed values. We take copies of the - # variables and quote the copies for generation of the libtool script. - for var in echo old_CC old_CFLAGS AR AR_FLAGS EGREP RANLIB LN_S LTCC LTCFLAGS NM \ - SED SHELL STRIP \ - libname_spec library_names_spec soname_spec extract_expsyms_cmds \ - old_striplib striplib file_magic_cmd finish_cmds finish_eval \ - deplibs_check_method reload_flag reload_cmds need_locks \ - lt_cv_sys_global_symbol_pipe lt_cv_sys_global_symbol_to_cdecl \ - lt_cv_sys_global_symbol_to_c_name_address \ - sys_lib_search_path_spec sys_lib_dlsearch_path_spec \ - old_postinstall_cmds old_postuninstall_cmds \ - compiler_F77 \ - CC_F77 \ - LD_F77 \ - lt_prog_compiler_wl_F77 \ - lt_prog_compiler_pic_F77 \ - lt_prog_compiler_static_F77 \ - lt_prog_compiler_no_builtin_flag_F77 \ - export_dynamic_flag_spec_F77 \ - thread_safe_flag_spec_F77 \ - whole_archive_flag_spec_F77 \ - enable_shared_with_static_runtimes_F77 \ - old_archive_cmds_F77 \ - old_archive_from_new_cmds_F77 \ - predep_objects_F77 \ - postdep_objects_F77 \ - predeps_F77 \ - postdeps_F77 \ - compiler_lib_search_path_F77 \ - archive_cmds_F77 \ - archive_expsym_cmds_F77 \ - postinstall_cmds_F77 \ - postuninstall_cmds_F77 \ - old_archive_from_expsyms_cmds_F77 \ - allow_undefined_flag_F77 \ - no_undefined_flag_F77 \ - export_symbols_cmds_F77 \ - hardcode_libdir_flag_spec_F77 \ - hardcode_libdir_flag_spec_ld_F77 \ - hardcode_libdir_separator_F77 \ - hardcode_automatic_F77 \ - module_cmds_F77 \ - module_expsym_cmds_F77 \ - lt_cv_prog_compiler_c_o_F77 \ - exclude_expsyms_F77 \ - include_expsyms_F77; do - - case $var in - old_archive_cmds_F77 | \ - old_archive_from_new_cmds_F77 | \ - archive_cmds_F77 | \ - archive_expsym_cmds_F77 | \ - module_cmds_F77 | \ - module_expsym_cmds_F77 | \ - old_archive_from_expsyms_cmds_F77 | \ - export_symbols_cmds_F77 | \ - extract_expsyms_cmds | reload_cmds | finish_cmds | \ - postinstall_cmds | postuninstall_cmds | \ - old_postinstall_cmds | old_postuninstall_cmds | \ - sys_lib_search_path_spec | sys_lib_dlsearch_path_spec) - # Double-quote double-evaled strings. - eval "lt_$var=\\\"\`\$echo \"X\$$var\" | \$Xsed -e \"\$double_quote_subst\" -e \"\$sed_quote_subst\" -e \"\$delay_variable_subst\"\`\\\"" - ;; - *) - eval "lt_$var=\\\"\`\$echo \"X\$$var\" | \$Xsed -e \"\$sed_quote_subst\"\`\\\"" - ;; - esac - done - - case $lt_echo in - *'\$0 --fallback-echo"') - lt_echo=`$echo "X$lt_echo" | $Xsed -e 's/\\\\\\\$0 --fallback-echo"$/$0 --fallback-echo"/'` - ;; - esac - -cfgfile="$ofile" - - cat <<__EOF__ >> "$cfgfile" -# ### BEGIN LIBTOOL TAG CONFIG: $tagname - -# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`: - -# Shell to use when invoking shell scripts. -SHELL=$lt_SHELL - -# Whether or not to build shared libraries. -build_libtool_libs=$enable_shared - -# Whether or not to build static libraries. -build_old_libs=$enable_static - -# Whether or not to add -lc for building shared libraries. -build_libtool_need_lc=$archive_cmds_need_lc_F77 - -# Whether or not to disallow shared libs when runtime libs are static -allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes_F77 - -# Whether or not to optimize for fast installation. -fast_install=$enable_fast_install - -# The host system. -host_alias=$host_alias -host=$host -host_os=$host_os - -# The build system. -build_alias=$build_alias -build=$build -build_os=$build_os - -# An echo program that does not interpret backslashes. -echo=$lt_echo - -# The archiver. -AR=$lt_AR -AR_FLAGS=$lt_AR_FLAGS - -# A C compiler. -LTCC=$lt_LTCC - -# LTCC compiler flags. -LTCFLAGS=$lt_LTCFLAGS - -# A language-specific compiler. -CC=$lt_compiler_F77 - -# Is the compiler the GNU C compiler? -with_gcc=$GCC_F77 - -# An ERE matcher. -EGREP=$lt_EGREP - -# The linker used to build libraries. -LD=$lt_LD_F77 - -# Whether we need hard or soft links. -LN_S=$lt_LN_S - -# A BSD-compatible nm program. -NM=$lt_NM - -# A symbol stripping program -STRIP=$lt_STRIP - -# Used to examine libraries when file_magic_cmd begins "file" -MAGIC_CMD=$MAGIC_CMD - -# Used on cygwin: DLL creation program. -DLLTOOL="$DLLTOOL" - -# Used on cygwin: object dumper. -OBJDUMP="$OBJDUMP" - -# Used on cygwin: assembler. -AS="$AS" - -# The name of the directory that contains temporary libtool files. -objdir=$objdir - -# How to create reloadable object files. -reload_flag=$lt_reload_flag -reload_cmds=$lt_reload_cmds - -# How to pass a linker flag through the compiler. -wl=$lt_lt_prog_compiler_wl_F77 - -# Object file suffix (normally "o"). -objext="$ac_objext" - -# Old archive suffix (normally "a"). -libext="$libext" - -# Shared library suffix (normally ".so"). -shrext_cmds='$shrext_cmds' - -# Executable file suffix (normally ""). -exeext="$exeext" - -# Additional compiler flags for building library objects. -pic_flag=$lt_lt_prog_compiler_pic_F77 -pic_mode=$pic_mode - -# What is the maximum length of a command? -max_cmd_len=$lt_cv_sys_max_cmd_len - -# Does compiler simultaneously support -c and -o options? -compiler_c_o=$lt_lt_cv_prog_compiler_c_o_F77 - -# Must we lock files when doing compilation? -need_locks=$lt_need_locks - -# Do we need the lib prefix for modules? -need_lib_prefix=$need_lib_prefix - -# Do we need a version for libraries? -need_version=$need_version - -# Whether dlopen is supported. -dlopen_support=$enable_dlopen - -# Whether dlopen of programs is supported. -dlopen_self=$enable_dlopen_self - -# Whether dlopen of statically linked programs is supported. -dlopen_self_static=$enable_dlopen_self_static - -# Compiler flag to prevent dynamic linking. -link_static_flag=$lt_lt_prog_compiler_static_F77 - -# Compiler flag to turn off builtin functions. -no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag_F77 - -# Compiler flag to allow reflexive dlopens. -export_dynamic_flag_spec=$lt_export_dynamic_flag_spec_F77 - -# Compiler flag to generate shared objects directly from archives. -whole_archive_flag_spec=$lt_whole_archive_flag_spec_F77 - -# Compiler flag to generate thread-safe objects. -thread_safe_flag_spec=$lt_thread_safe_flag_spec_F77 - -# Library versioning type. -version_type=$version_type - -# Format of library name prefix. -libname_spec=$lt_libname_spec - -# List of archive names. First name is the real one, the rest are links. -# The last name is the one that the linker finds with -lNAME. -library_names_spec=$lt_library_names_spec - -# The coded name of the library, if different from the real name. -soname_spec=$lt_soname_spec - -# Commands used to build and install an old-style archive. -RANLIB=$lt_RANLIB -old_archive_cmds=$lt_old_archive_cmds_F77 -old_postinstall_cmds=$lt_old_postinstall_cmds -old_postuninstall_cmds=$lt_old_postuninstall_cmds - -# Create an old-style archive from a shared archive. -old_archive_from_new_cmds=$lt_old_archive_from_new_cmds_F77 - -# Create a temporary old-style archive to link instead of a shared archive. -old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds_F77 - -# Commands used to build and install a shared archive. -archive_cmds=$lt_archive_cmds_F77 -archive_expsym_cmds=$lt_archive_expsym_cmds_F77 -postinstall_cmds=$lt_postinstall_cmds -postuninstall_cmds=$lt_postuninstall_cmds - -# Commands used to build a loadable module (assumed same as above if empty) -module_cmds=$lt_module_cmds_F77 -module_expsym_cmds=$lt_module_expsym_cmds_F77 - -# Commands to strip libraries. -old_striplib=$lt_old_striplib -striplib=$lt_striplib - -# Dependencies to place before the objects being linked to create a -# shared library. -predep_objects=$lt_predep_objects_F77 - -# Dependencies to place after the objects being linked to create a -# shared library. -postdep_objects=$lt_postdep_objects_F77 - -# Dependencies to place before the objects being linked to create a -# shared library. -predeps=$lt_predeps_F77 - -# Dependencies to place after the objects being linked to create a -# shared library. -postdeps=$lt_postdeps_F77 - -# The library search path used internally by the compiler when linking -# a shared library. -compiler_lib_search_path=$lt_compiler_lib_search_path_F77 - -# Method to check whether dependent libraries are shared objects. -deplibs_check_method=$lt_deplibs_check_method - -# Command to use when deplibs_check_method == file_magic. -file_magic_cmd=$lt_file_magic_cmd - -# Flag that allows shared libraries with undefined symbols to be built. -allow_undefined_flag=$lt_allow_undefined_flag_F77 - -# Flag that forces no undefined symbols. -no_undefined_flag=$lt_no_undefined_flag_F77 - -# Commands used to finish a libtool library installation in a directory. -finish_cmds=$lt_finish_cmds - -# Same as above, but a single script fragment to be evaled but not shown. -finish_eval=$lt_finish_eval - -# Take the output of nm and produce a listing of raw symbols and C names. -global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe - -# Transform the output of nm in a proper C declaration -global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl - -# Transform the output of nm in a C name address pair -global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address - -# This is the shared library runtime path variable. -runpath_var=$runpath_var - -# This is the shared library path variable. -shlibpath_var=$shlibpath_var - -# Is shlibpath searched before the hard-coded library search path? -shlibpath_overrides_runpath=$shlibpath_overrides_runpath - -# How to hardcode a shared library path into an executable. -hardcode_action=$hardcode_action_F77 - -# Whether we should hardcode library paths into libraries. -hardcode_into_libs=$hardcode_into_libs - -# Flag to hardcode \$libdir into a binary during linking. -# This must work even if \$libdir does not exist. -hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec_F77 - -# If ld is used when linking, flag to hardcode \$libdir into -# a binary during linking. This must work even if \$libdir does -# not exist. -hardcode_libdir_flag_spec_ld=$lt_hardcode_libdir_flag_spec_ld_F77 - -# Whether we need a single -rpath flag with a separated argument. -hardcode_libdir_separator=$lt_hardcode_libdir_separator_F77 - -# Set to yes if using DIR/libNAME${shared_ext} during linking hardcodes DIR into the -# resulting binary. -hardcode_direct=$hardcode_direct_F77 - -# Set to yes if using the -LDIR flag during linking hardcodes DIR into the -# resulting binary. -hardcode_minus_L=$hardcode_minus_L_F77 - -# Set to yes if using SHLIBPATH_VAR=DIR during linking hardcodes DIR into -# the resulting binary. -hardcode_shlibpath_var=$hardcode_shlibpath_var_F77 - -# Set to yes if building a shared library automatically hardcodes DIR into the library -# and all subsequent libraries and executables linked against it. -hardcode_automatic=$hardcode_automatic_F77 - -# Variables whose values should be saved in libtool wrapper scripts and -# restored at relink time. -variables_saved_for_relink="$variables_saved_for_relink" - -# Whether libtool must link a program against all its dependency libraries. -link_all_deplibs=$link_all_deplibs_F77 - -# Compile-time system search path for libraries -sys_lib_search_path_spec=$lt_sys_lib_search_path_spec - -# Run-time system search path for libraries -sys_lib_dlsearch_path_spec=$lt_sys_lib_dlsearch_path_spec - -# Fix the shell variable \$srcfile for the compiler. -fix_srcfile_path="$fix_srcfile_path_F77" - -# Set to yes if exported symbols are required. -always_export_symbols=$always_export_symbols_F77 - -# The commands to list exported symbols. -export_symbols_cmds=$lt_export_symbols_cmds_F77 - -# The commands to extract the exported symbol list from a shared archive. -extract_expsyms_cmds=$lt_extract_expsyms_cmds - -# Symbols that should not be listed in the preloaded symbols. -exclude_expsyms=$lt_exclude_expsyms_F77 - -# Symbols that must always be exported. -include_expsyms=$lt_include_expsyms_F77 - -# ### END LIBTOOL TAG CONFIG: $tagname - -__EOF__ - - -else - # If there is no Makefile yet, we rely on a make rule to execute - # `config.status --recheck' to rerun these tests and create the - # libtool script then. - ltmain_in=`echo $ltmain | sed -e 's/\.sh$/.in/'` - if test -f "$ltmain_in"; then - test -f Makefile && make "$ltmain" - fi -fi - - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - -CC="$lt_save_CC" - - else - tagname="" - fi - ;; - - GCJ) - if test -n "$GCJ" && test "X$GCJ" != "Xno"; then - - - -# Source file extension for Java test sources. -ac_ext=java - -# Object file extension for compiled Java test sources. -objext=o -objext_GCJ=$objext - -# Code to be used in simple compile tests -lt_simple_compile_test_code="class foo {}\n" - -# Code to be used in simple link tests -lt_simple_link_test_code='public class conftest { public static void main(String[] argv) {}; }\n' - -# ltmain only uses $CC for tagged configurations so make sure $CC is set. - -# If no C compiler was specified, use CC. -LTCC=${LTCC-"$CC"} - -# If no C compiler flags were specified, use CFLAGS. -LTCFLAGS=${LTCFLAGS-"$CFLAGS"} - -# Allow CC to be a program name with arguments. -compiler=$CC - - -# save warnings/boilerplate of simple test code -ac_outfile=conftest.$ac_objext -printf "$lt_simple_compile_test_code" >conftest.$ac_ext -eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err -_lt_compiler_boilerplate=`cat conftest.err` -$rm conftest* - -ac_outfile=conftest.$ac_objext -printf "$lt_simple_link_test_code" >conftest.$ac_ext -eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err -_lt_linker_boilerplate=`cat conftest.err` -$rm conftest* - - -# Allow CC to be a program name with arguments. -lt_save_CC="$CC" -CC=${GCJ-"gcj"} -compiler=$CC -compiler_GCJ=$CC -for cc_temp in $compiler""; do - case $cc_temp in - compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; - distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; - \-*) ;; - *) break;; - esac -done -cc_basename=`$echo "X$cc_temp" | $Xsed -e 's%.*/%%' -e "s%^$host_alias-%%"` - - -# GCJ did not exist at the time GCC didn't implicitly link libc in. -archive_cmds_need_lc_GCJ=no - -old_archive_cmds_GCJ=$old_archive_cmds - -## CAVEAT EMPTOR: -## There is no encapsulation within the following macros, do not change -## the running order or otherwise move them around unless you know exactly -## what you are doing... - -lt_prog_compiler_no_builtin_flag_GCJ= - -if test "$GCC" = yes; then - lt_prog_compiler_no_builtin_flag_GCJ=' -fno-builtin' - - -echo "$as_me:$LINENO: checking if $compiler supports -fno-rtti -fno-exceptions" >&5 -echo $ECHO_N "checking if $compiler supports -fno-rtti -fno-exceptions... $ECHO_C" >&6 -if test "${lt_cv_prog_compiler_rtti_exceptions+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - lt_cv_prog_compiler_rtti_exceptions=no - ac_outfile=conftest.$ac_objext - printf "$lt_simple_compile_test_code" > conftest.$ac_ext - lt_compiler_flag="-fno-rtti -fno-exceptions" - # Insert the option either (1) after the last *FLAGS variable, or - # (2) before a word containing "conftest.", or (3) at the end. - # Note that $ac_compile itself does not contain backslashes and begins - # with a dollar sign (not a hyphen), so the echo should work correctly. - # The option is referenced via a variable to avoid confusing sed. - lt_compile=`echo "$ac_compile" | $SED \ - -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ - -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ - -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:15509: $lt_compile\"" >&5) - (eval "$lt_compile" 2>conftest.err) - ac_status=$? - cat conftest.err >&5 - echo "$as_me:15513: \$? = $ac_status" >&5 - if (exit $ac_status) && test -s "$ac_outfile"; then - # The compiler can only warn and ignore the option if not recognized - # So say no if there are warnings other than the usual output. - $echo "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' >conftest.exp - $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 - if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then - lt_cv_prog_compiler_rtti_exceptions=yes - fi - fi - $rm conftest* - -fi -echo "$as_me:$LINENO: result: $lt_cv_prog_compiler_rtti_exceptions" >&5 -echo "${ECHO_T}$lt_cv_prog_compiler_rtti_exceptions" >&6 - -if test x"$lt_cv_prog_compiler_rtti_exceptions" = xyes; then - lt_prog_compiler_no_builtin_flag_GCJ="$lt_prog_compiler_no_builtin_flag_GCJ -fno-rtti -fno-exceptions" -else - : -fi - -fi - -lt_prog_compiler_wl_GCJ= -lt_prog_compiler_pic_GCJ= -lt_prog_compiler_static_GCJ= - -echo "$as_me:$LINENO: checking for $compiler option to produce PIC" >&5 -echo $ECHO_N "checking for $compiler option to produce PIC... $ECHO_C" >&6 - - if test "$GCC" = yes; then - lt_prog_compiler_wl_GCJ='-Wl,' - lt_prog_compiler_static_GCJ='-static' - - case $host_os in - aix*) - # All AIX code is PIC. - if test "$host_cpu" = ia64; then - # AIX 5 now supports IA64 processor - lt_prog_compiler_static_GCJ='-Bstatic' - fi - ;; - - amigaos*) - # FIXME: we need at least 68020 code to build shared libraries, but - # adding the `-m68020' flag to GCC prevents building anything better, - # like `-m68040'. - lt_prog_compiler_pic_GCJ='-m68020 -resident32 -malways-restore-a4' - ;; - - beos* | cygwin* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) - # PIC is the default for these OSes. - ;; - - mingw* | pw32* | os2*) - # This hack is so that the source file can tell whether it is being - # built for inclusion in a dll (and should export symbols for example). - lt_prog_compiler_pic_GCJ='-DDLL_EXPORT' - ;; - - darwin* | rhapsody*) - # PIC is the default on this platform - # Common symbols not allowed in MH_DYLIB files - lt_prog_compiler_pic_GCJ='-fno-common' - ;; - - interix3*) - # Interix 3.x gcc -fpic/-fPIC options generate broken code. - # Instead, we relocate shared libraries at runtime. - ;; - - msdosdjgpp*) - # Just because we use GCC doesn't mean we suddenly get shared libraries - # on systems that don't support them. - lt_prog_compiler_can_build_shared_GCJ=no - enable_shared=no - ;; - - sysv4*MP*) - if test -d /usr/nec; then - lt_prog_compiler_pic_GCJ=-Kconform_pic - fi - ;; - - hpux*) - # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but - # not for PA HP-UX. - case $host_cpu in - hppa*64*|ia64*) - # +Z the default - ;; - *) - lt_prog_compiler_pic_GCJ='-fPIC' - ;; - esac - ;; - - *) - lt_prog_compiler_pic_GCJ='-fPIC' - ;; - esac - else - # PORTME Check for flag to pass linker flags through the system compiler. - case $host_os in - aix*) - lt_prog_compiler_wl_GCJ='-Wl,' - if test "$host_cpu" = ia64; then - # AIX 5 now supports IA64 processor - lt_prog_compiler_static_GCJ='-Bstatic' - else - lt_prog_compiler_static_GCJ='-bnso -bI:/lib/syscalls.exp' - fi - ;; - darwin*) - # PIC is the default on this platform - # Common symbols not allowed in MH_DYLIB files - case $cc_basename in - xlc*) - lt_prog_compiler_pic_GCJ='-qnocommon' - lt_prog_compiler_wl_GCJ='-Wl,' - ;; - esac - ;; - - mingw* | pw32* | os2*) - # This hack is so that the source file can tell whether it is being - # built for inclusion in a dll (and should export symbols for example). - lt_prog_compiler_pic_GCJ='-DDLL_EXPORT' - ;; - - hpux9* | hpux10* | hpux11*) - lt_prog_compiler_wl_GCJ='-Wl,' - # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but - # not for PA HP-UX. - case $host_cpu in - hppa*64*|ia64*) - # +Z the default - ;; - *) - lt_prog_compiler_pic_GCJ='+Z' - ;; - esac - # Is there a better lt_prog_compiler_static that works with the bundled CC? - lt_prog_compiler_static_GCJ='${wl}-a ${wl}archive' - ;; - - irix5* | irix6* | nonstopux*) - lt_prog_compiler_wl_GCJ='-Wl,' - # PIC (with -KPIC) is the default. - lt_prog_compiler_static_GCJ='-non_shared' - ;; - - newsos6) - lt_prog_compiler_pic_GCJ='-KPIC' - lt_prog_compiler_static_GCJ='-Bstatic' - ;; - - linux*) - case $cc_basename in - icc* | ecc*) - lt_prog_compiler_wl_GCJ='-Wl,' - lt_prog_compiler_pic_GCJ='-KPIC' - lt_prog_compiler_static_GCJ='-static' - ;; - pgcc* | pgf77* | pgf90* | pgf95*) - # Portland Group compilers (*not* the Pentium gcc compiler, - # which looks to be a dead project) - lt_prog_compiler_wl_GCJ='-Wl,' - lt_prog_compiler_pic_GCJ='-fpic' - lt_prog_compiler_static_GCJ='-Bstatic' - ;; - ccc*) - lt_prog_compiler_wl_GCJ='-Wl,' - # All Alpha code is PIC. - lt_prog_compiler_static_GCJ='-non_shared' - ;; - esac - ;; - - osf3* | osf4* | osf5*) - lt_prog_compiler_wl_GCJ='-Wl,' - # All OSF/1 code is PIC. - lt_prog_compiler_static_GCJ='-non_shared' - ;; - - solaris*) - lt_prog_compiler_pic_GCJ='-KPIC' - lt_prog_compiler_static_GCJ='-Bstatic' - case $cc_basename in - f77* | f90* | f95*) - lt_prog_compiler_wl_GCJ='-Qoption ld ';; - *) - lt_prog_compiler_wl_GCJ='-Wl,';; - esac - ;; - - sunos4*) - lt_prog_compiler_wl_GCJ='-Qoption ld ' - lt_prog_compiler_pic_GCJ='-PIC' - lt_prog_compiler_static_GCJ='-Bstatic' - ;; - - sysv4 | sysv4.2uw2* | sysv4.3*) - lt_prog_compiler_wl_GCJ='-Wl,' - lt_prog_compiler_pic_GCJ='-KPIC' - lt_prog_compiler_static_GCJ='-Bstatic' - ;; - - sysv4*MP*) - if test -d /usr/nec ;then - lt_prog_compiler_pic_GCJ='-Kconform_pic' - lt_prog_compiler_static_GCJ='-Bstatic' - fi - ;; - - sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) - lt_prog_compiler_wl_GCJ='-Wl,' - lt_prog_compiler_pic_GCJ='-KPIC' - lt_prog_compiler_static_GCJ='-Bstatic' - ;; - - unicos*) - lt_prog_compiler_wl_GCJ='-Wl,' - lt_prog_compiler_can_build_shared_GCJ=no - ;; - - uts4*) - lt_prog_compiler_pic_GCJ='-pic' - lt_prog_compiler_static_GCJ='-Bstatic' - ;; - - *) - lt_prog_compiler_can_build_shared_GCJ=no - ;; - esac - fi - -echo "$as_me:$LINENO: result: $lt_prog_compiler_pic_GCJ" >&5 -echo "${ECHO_T}$lt_prog_compiler_pic_GCJ" >&6 - -# -# Check to make sure the PIC flag actually works. -# -if test -n "$lt_prog_compiler_pic_GCJ"; then - -echo "$as_me:$LINENO: checking if $compiler PIC flag $lt_prog_compiler_pic_GCJ works" >&5 -echo $ECHO_N "checking if $compiler PIC flag $lt_prog_compiler_pic_GCJ works... $ECHO_C" >&6 -if test "${lt_prog_compiler_pic_works_GCJ+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - lt_prog_compiler_pic_works_GCJ=no - ac_outfile=conftest.$ac_objext - printf "$lt_simple_compile_test_code" > conftest.$ac_ext - lt_compiler_flag="$lt_prog_compiler_pic_GCJ" - # Insert the option either (1) after the last *FLAGS variable, or - # (2) before a word containing "conftest.", or (3) at the end. - # Note that $ac_compile itself does not contain backslashes and begins - # with a dollar sign (not a hyphen), so the echo should work correctly. - # The option is referenced via a variable to avoid confusing sed. - lt_compile=`echo "$ac_compile" | $SED \ - -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ - -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ - -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:15777: $lt_compile\"" >&5) - (eval "$lt_compile" 2>conftest.err) - ac_status=$? - cat conftest.err >&5 - echo "$as_me:15781: \$? = $ac_status" >&5 - if (exit $ac_status) && test -s "$ac_outfile"; then - # The compiler can only warn and ignore the option if not recognized - # So say no if there are warnings other than the usual output. - $echo "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' >conftest.exp - $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 - if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then - lt_prog_compiler_pic_works_GCJ=yes - fi - fi - $rm conftest* - -fi -echo "$as_me:$LINENO: result: $lt_prog_compiler_pic_works_GCJ" >&5 -echo "${ECHO_T}$lt_prog_compiler_pic_works_GCJ" >&6 - -if test x"$lt_prog_compiler_pic_works_GCJ" = xyes; then - case $lt_prog_compiler_pic_GCJ in - "" | " "*) ;; - *) lt_prog_compiler_pic_GCJ=" $lt_prog_compiler_pic_GCJ" ;; - esac -else - lt_prog_compiler_pic_GCJ= - lt_prog_compiler_can_build_shared_GCJ=no -fi - -fi -case $host_os in - # For platforms which do not support PIC, -DPIC is meaningless: - *djgpp*) - lt_prog_compiler_pic_GCJ= - ;; - *) - lt_prog_compiler_pic_GCJ="$lt_prog_compiler_pic_GCJ" - ;; -esac - -# -# Check to make sure the static flag actually works. -# -wl=$lt_prog_compiler_wl_GCJ eval lt_tmp_static_flag=\"$lt_prog_compiler_static_GCJ\" -echo "$as_me:$LINENO: checking if $compiler static flag $lt_tmp_static_flag works" >&5 -echo $ECHO_N "checking if $compiler static flag $lt_tmp_static_flag works... $ECHO_C" >&6 -if test "${lt_prog_compiler_static_works_GCJ+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - lt_prog_compiler_static_works_GCJ=no - save_LDFLAGS="$LDFLAGS" - LDFLAGS="$LDFLAGS $lt_tmp_static_flag" - printf "$lt_simple_link_test_code" > conftest.$ac_ext - if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then - # The linker can only warn and ignore the option if not recognized - # So say no if there are warnings - if test -s conftest.err; then - # Append any errors to the config.log. - cat conftest.err 1>&5 - $echo "X$_lt_linker_boilerplate" | $Xsed -e '/^$/d' > conftest.exp - $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 - if diff conftest.exp conftest.er2 >/dev/null; then - lt_prog_compiler_static_works_GCJ=yes - fi - else - lt_prog_compiler_static_works_GCJ=yes - fi - fi - $rm conftest* - LDFLAGS="$save_LDFLAGS" - -fi -echo "$as_me:$LINENO: result: $lt_prog_compiler_static_works_GCJ" >&5 -echo "${ECHO_T}$lt_prog_compiler_static_works_GCJ" >&6 - -if test x"$lt_prog_compiler_static_works_GCJ" = xyes; then - : -else - lt_prog_compiler_static_GCJ= -fi - - -echo "$as_me:$LINENO: checking if $compiler supports -c -o file.$ac_objext" >&5 -echo $ECHO_N "checking if $compiler supports -c -o file.$ac_objext... $ECHO_C" >&6 -if test "${lt_cv_prog_compiler_c_o_GCJ+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - lt_cv_prog_compiler_c_o_GCJ=no - $rm -r conftest 2>/dev/null - mkdir conftest - cd conftest - mkdir out - printf "$lt_simple_compile_test_code" > conftest.$ac_ext - - lt_compiler_flag="-o out/conftest2.$ac_objext" - # Insert the option either (1) after the last *FLAGS variable, or - # (2) before a word containing "conftest.", or (3) at the end. - # Note that $ac_compile itself does not contain backslashes and begins - # with a dollar sign (not a hyphen), so the echo should work correctly. - lt_compile=`echo "$ac_compile" | $SED \ - -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ - -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ - -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:15881: $lt_compile\"" >&5) - (eval "$lt_compile" 2>out/conftest.err) - ac_status=$? - cat out/conftest.err >&5 - echo "$as_me:15885: \$? = $ac_status" >&5 - if (exit $ac_status) && test -s out/conftest2.$ac_objext - then - # The compiler can only warn and ignore the option if not recognized - # So say no if there are warnings - $echo "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' > out/conftest.exp - $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 - if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then - lt_cv_prog_compiler_c_o_GCJ=yes - fi - fi - chmod u+w . 2>&5 - $rm conftest* - # SGI C++ compiler will create directory out/ii_files/ for - # template instantiation - test -d out/ii_files && $rm out/ii_files/* && rmdir out/ii_files - $rm out/* && rmdir out - cd .. - rmdir conftest - $rm conftest* - -fi -echo "$as_me:$LINENO: result: $lt_cv_prog_compiler_c_o_GCJ" >&5 -echo "${ECHO_T}$lt_cv_prog_compiler_c_o_GCJ" >&6 - - -hard_links="nottested" -if test "$lt_cv_prog_compiler_c_o_GCJ" = no && test "$need_locks" != no; then - # do not overwrite the value of need_locks provided by the user - echo "$as_me:$LINENO: checking if we can lock with hard links" >&5 -echo $ECHO_N "checking if we can lock with hard links... $ECHO_C" >&6 - hard_links=yes - $rm conftest* - ln conftest.a conftest.b 2>/dev/null && hard_links=no - touch conftest.a - ln conftest.a conftest.b 2>&5 || hard_links=no - ln conftest.a conftest.b 2>/dev/null && hard_links=no - echo "$as_me:$LINENO: result: $hard_links" >&5 -echo "${ECHO_T}$hard_links" >&6 - if test "$hard_links" = no; then - { echo "$as_me:$LINENO: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&5 -echo "$as_me: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&2;} - need_locks=warn - fi -else - need_locks=no -fi - -echo "$as_me:$LINENO: checking whether the $compiler linker ($LD) supports shared libraries" >&5 -echo $ECHO_N "checking whether the $compiler linker ($LD) supports shared libraries... $ECHO_C" >&6 - - runpath_var= - allow_undefined_flag_GCJ= - enable_shared_with_static_runtimes_GCJ=no - archive_cmds_GCJ= - archive_expsym_cmds_GCJ= - old_archive_From_new_cmds_GCJ= - old_archive_from_expsyms_cmds_GCJ= - export_dynamic_flag_spec_GCJ= - whole_archive_flag_spec_GCJ= - thread_safe_flag_spec_GCJ= - hardcode_libdir_flag_spec_GCJ= - hardcode_libdir_flag_spec_ld_GCJ= - hardcode_libdir_separator_GCJ= - hardcode_direct_GCJ=no - hardcode_minus_L_GCJ=no - hardcode_shlibpath_var_GCJ=unsupported - link_all_deplibs_GCJ=unknown - hardcode_automatic_GCJ=no - module_cmds_GCJ= - module_expsym_cmds_GCJ= - always_export_symbols_GCJ=no - export_symbols_cmds_GCJ='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' - # include_expsyms should be a list of space-separated symbols to be *always* - # included in the symbol list - include_expsyms_GCJ= - # exclude_expsyms can be an extended regexp of symbols to exclude - # it will be wrapped by ` (' and `)$', so one must not match beginning or - # end of line. Example: `a|bc|.*d.*' will exclude the symbols `a' and `bc', - # as well as any symbol that contains `d'. - exclude_expsyms_GCJ="_GLOBAL_OFFSET_TABLE_" - # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out - # platforms (ab)use it in PIC code, but their linkers get confused if - # the symbol is explicitly referenced. Since portable code cannot - # rely on this symbol name, it's probably fine to never include it in - # preloaded symbol tables. - extract_expsyms_cmds= - # Just being paranoid about ensuring that cc_basename is set. - for cc_temp in $compiler""; do - case $cc_temp in - compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; - distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; - \-*) ;; - *) break;; - esac -done -cc_basename=`$echo "X$cc_temp" | $Xsed -e 's%.*/%%' -e "s%^$host_alias-%%"` - - case $host_os in - cygwin* | mingw* | pw32*) - # FIXME: the MSVC++ port hasn't been tested in a loooong time - # When not using gcc, we currently assume that we are using - # Microsoft Visual C++. - if test "$GCC" != yes; then - with_gnu_ld=no - fi - ;; - interix*) - # we just hope/assume this is gcc and not c89 (= MSVC++) - with_gnu_ld=yes - ;; - openbsd*) - with_gnu_ld=no - ;; - esac - - ld_shlibs_GCJ=yes - if test "$with_gnu_ld" = yes; then - # If archive_cmds runs LD, not CC, wlarc should be empty - wlarc='${wl}' - - # Set some defaults for GNU ld with shared library support. These - # are reset later if shared libraries are not supported. Putting them - # here allows them to be overridden if necessary. - runpath_var=LD_RUN_PATH - hardcode_libdir_flag_spec_GCJ='${wl}--rpath ${wl}$libdir' - export_dynamic_flag_spec_GCJ='${wl}--export-dynamic' - # ancient GNU ld didn't support --whole-archive et. al. - if $LD --help 2>&1 | grep 'no-whole-archive' > /dev/null; then - whole_archive_flag_spec_GCJ="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' - else - whole_archive_flag_spec_GCJ= - fi - supports_anon_versioning=no - case `$LD -v 2>/dev/null` in - *\ [01].* | *\ 2.[0-9].* | *\ 2.10.*) ;; # catch versions < 2.11 - *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ... - *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ... - *\ 2.11.*) ;; # other 2.11 versions - *) supports_anon_versioning=yes ;; - esac - - # See if GNU ld supports shared libraries. - case $host_os in - aix3* | aix4* | aix5*) - # On AIX/PPC, the GNU linker is very broken - if test "$host_cpu" != ia64; then - ld_shlibs_GCJ=no - cat <&2 - -*** Warning: the GNU linker, at least up to release 2.9.1, is reported -*** to be unable to reliably create shared libraries on AIX. -*** Therefore, libtool is disabling shared libraries support. If you -*** really care for shared libraries, you may want to modify your PATH -*** so that a non-GNU linker is found, and then restart. - -EOF - fi - ;; - - amigaos*) - archive_cmds_GCJ='$rm $output_objdir/a2ixlibrary.data~$echo "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$echo "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$echo "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$echo "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' - hardcode_libdir_flag_spec_GCJ='-L$libdir' - hardcode_minus_L_GCJ=yes - - # Samuel A. Falvo II reports - # that the semantics of dynamic libraries on AmigaOS, at least up - # to version 4, is to share data among multiple programs linked - # with the same dynamic library. Since this doesn't match the - # behavior of shared libraries on other platforms, we can't use - # them. - ld_shlibs_GCJ=no - ;; - - beos*) - if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then - allow_undefined_flag_GCJ=unsupported - # Joseph Beckenbach says some releases of gcc - # support --undefined. This deserves some investigation. FIXME - archive_cmds_GCJ='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - else - ld_shlibs_GCJ=no - fi - ;; - - cygwin* | mingw* | pw32*) - # _LT_AC_TAGVAR(hardcode_libdir_flag_spec, GCJ) is actually meaningless, - # as there is no search path for DLLs. - hardcode_libdir_flag_spec_GCJ='-L$libdir' - allow_undefined_flag_GCJ=unsupported - always_export_symbols_GCJ=no - enable_shared_with_static_runtimes_GCJ=yes - export_symbols_cmds_GCJ='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS] /s/.* \([^ ]*\)/\1 DATA/'\'' | $SED -e '\''/^[AITW] /s/.* //'\'' | sort | uniq > $export_symbols' - - if $LD --help 2>&1 | grep 'auto-import' > /dev/null; then - archive_cmds_GCJ='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' - # If the export-symbols file already is a .def file (1st line - # is EXPORTS), use it as is; otherwise, prepend... - archive_expsym_cmds_GCJ='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then - cp $export_symbols $output_objdir/$soname.def; - else - echo EXPORTS > $output_objdir/$soname.def; - cat $export_symbols >> $output_objdir/$soname.def; - fi~ - $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' - else - ld_shlibs_GCJ=no - fi - ;; - - interix3*) - hardcode_direct_GCJ=no - hardcode_shlibpath_var_GCJ=no - hardcode_libdir_flag_spec_GCJ='${wl}-rpath,$libdir' - export_dynamic_flag_spec_GCJ='${wl}-E' - # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. - # Instead, shared libraries are loaded at an image base (0x10000000 by - # default) and relocated if they conflict, which is a slow very memory - # consuming and fragmenting process. To avoid this, we pick a random, - # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link - # time. Moving up from 0x10000000 also allows more sbrk(2) space. - archive_cmds_GCJ='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' - archive_expsym_cmds_GCJ='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' - ;; - - linux*) - if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then - tmp_addflag= - case $cc_basename,$host_cpu in - pgcc*) # Portland Group C compiler - whole_archive_flag_spec_GCJ='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $echo \"$new_convenience\"` ${wl}--no-whole-archive' - tmp_addflag=' $pic_flag' - ;; - pgf77* | pgf90* | pgf95*) # Portland Group f77 and f90 compilers - whole_archive_flag_spec_GCJ='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $echo \"$new_convenience\"` ${wl}--no-whole-archive' - tmp_addflag=' $pic_flag -Mnomain' ;; - ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64 - tmp_addflag=' -i_dynamic' ;; - efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64 - tmp_addflag=' -i_dynamic -nofor_main' ;; - ifc* | ifort*) # Intel Fortran compiler - tmp_addflag=' -nofor_main' ;; - esac - archive_cmds_GCJ='$CC -shared'"$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - - if test $supports_anon_versioning = yes; then - archive_expsym_cmds_GCJ='$echo "{ global:" > $output_objdir/$libname.ver~ - cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ - $echo "local: *; };" >> $output_objdir/$libname.ver~ - $CC -shared'"$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib' - fi - else - ld_shlibs_GCJ=no - fi - ;; - - netbsd*) - if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then - archive_cmds_GCJ='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib' - wlarc= - else - archive_cmds_GCJ='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - archive_expsym_cmds_GCJ='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' - fi - ;; - - solaris*) - if $LD -v 2>&1 | grep 'BFD 2\.8' > /dev/null; then - ld_shlibs_GCJ=no - cat <&2 - -*** Warning: The releases 2.8.* of the GNU linker cannot reliably -*** create shared libraries on Solaris systems. Therefore, libtool -*** is disabling shared libraries support. We urge you to upgrade GNU -*** binutils to release 2.9.1 or newer. Another option is to modify -*** your PATH or compiler configuration so that the native linker is -*** used, and then restart. - -EOF - elif $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then - archive_cmds_GCJ='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - archive_expsym_cmds_GCJ='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' - else - ld_shlibs_GCJ=no - fi - ;; - - sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) - case `$LD -v 2>&1` in - *\ [01].* | *\ 2.[0-9].* | *\ 2.1[0-5].*) - ld_shlibs_GCJ=no - cat <<_LT_EOF 1>&2 - -*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 can not -*** reliably create shared libraries on SCO systems. Therefore, libtool -*** is disabling shared libraries support. We urge you to upgrade GNU -*** binutils to release 2.16.91.0.3 or newer. Another option is to modify -*** your PATH or compiler configuration so that the native linker is -*** used, and then restart. - -_LT_EOF - ;; - *) - if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then - hardcode_libdir_flag_spec_GCJ='`test -z "$SCOABSPATH" && echo ${wl}-rpath,$libdir`' - archive_cmds_GCJ='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname,\${SCOABSPATH:+${install_libdir}/}$soname -o $lib' - archive_expsym_cmds_GCJ='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname,\${SCOABSPATH:+${install_libdir}/}$soname,-retain-symbols-file,$export_symbols -o $lib' - else - ld_shlibs_GCJ=no - fi - ;; - esac - ;; - - sunos4*) - archive_cmds_GCJ='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags' - wlarc= - hardcode_direct_GCJ=yes - hardcode_shlibpath_var_GCJ=no - ;; - - *) - if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then - archive_cmds_GCJ='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - archive_expsym_cmds_GCJ='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' - else - ld_shlibs_GCJ=no - fi - ;; - esac - - if test "$ld_shlibs_GCJ" = no; then - runpath_var= - hardcode_libdir_flag_spec_GCJ= - export_dynamic_flag_spec_GCJ= - whole_archive_flag_spec_GCJ= - fi - else - # PORTME fill in a description of your system's linker (not GNU ld) - case $host_os in - aix3*) - allow_undefined_flag_GCJ=unsupported - always_export_symbols_GCJ=yes - archive_expsym_cmds_GCJ='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname' - # Note: this linker hardcodes the directories in LIBPATH if there - # are no directories specified by -L. - hardcode_minus_L_GCJ=yes - if test "$GCC" = yes && test -z "$lt_prog_compiler_static"; then - # Neither direct hardcoding nor static linking is supported with a - # broken collect2. - hardcode_direct_GCJ=unsupported - fi - ;; - - aix4* | aix5*) - if test "$host_cpu" = ia64; then - # On IA64, the linker does run time linking by default, so we don't - # have to do anything special. - aix_use_runtimelinking=no - exp_sym_flag='-Bexport' - no_entry_flag="" - else - # If we're using GNU nm, then we don't want the "-C" option. - # -C means demangle to AIX nm, but means don't demangle with GNU nm - if $NM -V 2>&1 | grep 'GNU' > /dev/null; then - export_symbols_cmds_GCJ='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$2 == "T") || (\$2 == "D") || (\$2 == "B")) && (substr(\$3,1,1) != ".")) { print \$3 } }'\'' | sort -u > $export_symbols' - else - export_symbols_cmds_GCJ='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$2 == "T") || (\$2 == "D") || (\$2 == "B")) && (substr(\$3,1,1) != ".")) { print \$3 } }'\'' | sort -u > $export_symbols' - fi - aix_use_runtimelinking=no - - # Test if we are trying to use run time linking or normal - # AIX style linking. If -brtl is somewhere in LDFLAGS, we - # need to do runtime linking. - case $host_os in aix4.[23]|aix4.[23].*|aix5*) - for ld_flag in $LDFLAGS; do - if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then - aix_use_runtimelinking=yes - break - fi - done - ;; - esac - - exp_sym_flag='-bexport' - no_entry_flag='-bnoentry' - fi - - # When large executables or shared objects are built, AIX ld can - # have problems creating the table of contents. If linking a library - # or program results in "error TOC overflow" add -mminimal-toc to - # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not - # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. - - archive_cmds_GCJ='' - hardcode_direct_GCJ=yes - hardcode_libdir_separator_GCJ=':' - link_all_deplibs_GCJ=yes - - if test "$GCC" = yes; then - case $host_os in aix4.[012]|aix4.[012].*) - # We only want to do this on AIX 4.2 and lower, the check - # below for broken collect2 doesn't work under 4.3+ - collect2name=`${CC} -print-prog-name=collect2` - if test -f "$collect2name" && \ - strings "$collect2name" | grep resolve_lib_name >/dev/null - then - # We have reworked collect2 - hardcode_direct_GCJ=yes - else - # We have old collect2 - hardcode_direct_GCJ=unsupported - # It fails to find uninstalled libraries when the uninstalled - # path is not listed in the libpath. Setting hardcode_minus_L - # to unsupported forces relinking - hardcode_minus_L_GCJ=yes - hardcode_libdir_flag_spec_GCJ='-L$libdir' - hardcode_libdir_separator_GCJ= - fi - ;; - esac - shared_flag='-shared' - if test "$aix_use_runtimelinking" = yes; then - shared_flag="$shared_flag "'${wl}-G' - fi - else - # not using gcc - if test "$host_cpu" = ia64; then - # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release - # chokes on -Wl,-G. The following line is correct: - shared_flag='-G' - else - if test "$aix_use_runtimelinking" = yes; then - shared_flag='${wl}-G' - else - shared_flag='${wl}-bM:SRE' - fi - fi - fi - - # It seems that -bexpall does not export symbols beginning with - # underscore (_), so it is better to generate a list of symbols to export. - always_export_symbols_GCJ=yes - if test "$aix_use_runtimelinking" = yes; then - # Warning - without using the other runtime loading flags (-brtl), - # -berok will link without error, but may produce a broken library. - allow_undefined_flag_GCJ='-berok' - # Determine the default libpath from the value encoded in an empty executable. - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 - (eval $ac_link) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest$ac_exeext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - -aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } -}'` -# Check for a 64-bit object if we didn't find anything. -if test -z "$aix_libpath"; then aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } -}'`; fi -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -fi -rm -f conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi - - hardcode_libdir_flag_spec_GCJ='${wl}-blibpath:$libdir:'"$aix_libpath" - archive_expsym_cmds_GCJ="\$CC"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then echo "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag" - else - if test "$host_cpu" = ia64; then - hardcode_libdir_flag_spec_GCJ='${wl}-R $libdir:/usr/lib:/lib' - allow_undefined_flag_GCJ="-z nodefs" - archive_expsym_cmds_GCJ="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols" - else - # Determine the default libpath from the value encoded in an empty executable. - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 - (eval $ac_link) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest$ac_exeext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - -aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } -}'` -# Check for a 64-bit object if we didn't find anything. -if test -z "$aix_libpath"; then aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } -}'`; fi -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -fi -rm -f conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi - - hardcode_libdir_flag_spec_GCJ='${wl}-blibpath:$libdir:'"$aix_libpath" - # Warning - without using the other run time loading flags, - # -berok will link without error, but may produce a broken library. - no_undefined_flag_GCJ=' ${wl}-bernotok' - allow_undefined_flag_GCJ=' ${wl}-berok' - # Exported symbols can be pulled into shared objects from archives - whole_archive_flag_spec_GCJ='$convenience' - archive_cmds_need_lc_GCJ=yes - # This is similar to how AIX traditionally builds its shared libraries. - archive_expsym_cmds_GCJ="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' - fi - fi - ;; - - amigaos*) - archive_cmds_GCJ='$rm $output_objdir/a2ixlibrary.data~$echo "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$echo "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$echo "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$echo "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' - hardcode_libdir_flag_spec_GCJ='-L$libdir' - hardcode_minus_L_GCJ=yes - # see comment about different semantics on the GNU ld section - ld_shlibs_GCJ=no - ;; - - bsdi[45]*) - export_dynamic_flag_spec_GCJ=-rdynamic - ;; - - cygwin* | mingw* | pw32*) - # When not using gcc, we currently assume that we are using - # Microsoft Visual C++. - # hardcode_libdir_flag_spec is actually meaningless, as there is - # no search path for DLLs. - hardcode_libdir_flag_spec_GCJ=' ' - allow_undefined_flag_GCJ=unsupported - # Tell ltmain to make .lib files, not .a files. - libext=lib - # Tell ltmain to make .dll files, not .so files. - shrext_cmds=".dll" - # FIXME: Setting linknames here is a bad hack. - archive_cmds_GCJ='$CC -o $lib $libobjs $compiler_flags `echo "$deplibs" | $SED -e '\''s/ -lc$//'\''` -link -dll~linknames=' - # The linker will automatically build a .lib file if we build a DLL. - old_archive_From_new_cmds_GCJ='true' - # FIXME: Should let the user specify the lib program. - old_archive_cmds_GCJ='lib /OUT:$oldlib$oldobjs$old_deplibs' - fix_srcfile_path_GCJ='`cygpath -w "$srcfile"`' - enable_shared_with_static_runtimes_GCJ=yes - ;; - - darwin* | rhapsody*) - case $host_os in - rhapsody* | darwin1.[012]) - allow_undefined_flag_GCJ='${wl}-undefined ${wl}suppress' - ;; - *) # Darwin 1.3 on - if test -z ${MACOSX_DEPLOYMENT_TARGET} ; then - allow_undefined_flag_GCJ='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' - else - case ${MACOSX_DEPLOYMENT_TARGET} in - 10.[012]) - allow_undefined_flag_GCJ='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' - ;; - 10.*) - allow_undefined_flag_GCJ='${wl}-undefined ${wl}dynamic_lookup' - ;; - esac - fi - ;; - esac - archive_cmds_need_lc_GCJ=no - hardcode_direct_GCJ=no - hardcode_automatic_GCJ=yes - hardcode_shlibpath_var_GCJ=unsupported - whole_archive_flag_spec_GCJ='' - link_all_deplibs_GCJ=yes - if test "$GCC" = yes ; then - output_verbose_link_cmd='echo' - archive_cmds_GCJ='$CC -dynamiclib $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags -install_name $rpath/$soname $verstring' - module_cmds_GCJ='$CC $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags' - # Don't fix this by using the ld -exported_symbols_list flag, it doesn't exist in older darwin lds - archive_expsym_cmds_GCJ='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC -dynamiclib $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags -install_name $rpath/$soname $verstring~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' - module_expsym_cmds_GCJ='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' - else - case $cc_basename in - xlc*) - output_verbose_link_cmd='echo' - archive_cmds_GCJ='$CC -qmkshrobj $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-install_name ${wl}`echo $rpath/$soname` $verstring' - module_cmds_GCJ='$CC $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags' - # Don't fix this by using the ld -exported_symbols_list flag, it doesn't exist in older darwin lds - archive_expsym_cmds_GCJ='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC -qmkshrobj $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-install_name ${wl}$rpath/$soname $verstring~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' - module_expsym_cmds_GCJ='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' - ;; - *) - ld_shlibs_GCJ=no - ;; - esac - fi - ;; - - dgux*) - archive_cmds_GCJ='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - hardcode_libdir_flag_spec_GCJ='-L$libdir' - hardcode_shlibpath_var_GCJ=no - ;; - - freebsd1*) - ld_shlibs_GCJ=no - ;; - - # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor - # support. Future versions do this automatically, but an explicit c++rt0.o - # does not break anything, and helps significantly (at the cost of a little - # extra space). - freebsd2.2*) - archive_cmds_GCJ='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o' - hardcode_libdir_flag_spec_GCJ='-R$libdir' - hardcode_direct_GCJ=yes - hardcode_shlibpath_var_GCJ=no - ;; - - # Unfortunately, older versions of FreeBSD 2 do not have this feature. - freebsd2*) - archive_cmds_GCJ='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' - hardcode_direct_GCJ=yes - hardcode_minus_L_GCJ=yes - hardcode_shlibpath_var_GCJ=no - ;; - - # FreeBSD 3 and greater uses gcc -shared to do shared libraries. - freebsd* | kfreebsd*-gnu | dragonfly*) - archive_cmds_GCJ='$CC -shared -o $lib $libobjs $deplibs $compiler_flags' - hardcode_libdir_flag_spec_GCJ='-R$libdir' - hardcode_direct_GCJ=yes - hardcode_shlibpath_var_GCJ=no - ;; - - hpux9*) - if test "$GCC" = yes; then - archive_cmds_GCJ='$rm $output_objdir/$soname~$CC -shared -fPIC ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' - else - archive_cmds_GCJ='$rm $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' - fi - hardcode_libdir_flag_spec_GCJ='${wl}+b ${wl}$libdir' - hardcode_libdir_separator_GCJ=: - hardcode_direct_GCJ=yes - - # hardcode_minus_L: Not really in the search PATH, - # but as the default location of the library. - hardcode_minus_L_GCJ=yes - export_dynamic_flag_spec_GCJ='${wl}-E' - ;; - - hpux10*) - if test "$GCC" = yes -a "$with_gnu_ld" = no; then - archive_cmds_GCJ='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' - else - archive_cmds_GCJ='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' - fi - if test "$with_gnu_ld" = no; then - hardcode_libdir_flag_spec_GCJ='${wl}+b ${wl}$libdir' - hardcode_libdir_separator_GCJ=: - - hardcode_direct_GCJ=yes - export_dynamic_flag_spec_GCJ='${wl}-E' - - # hardcode_minus_L: Not really in the search PATH, - # but as the default location of the library. - hardcode_minus_L_GCJ=yes - fi - ;; - - hpux11*) - if test "$GCC" = yes -a "$with_gnu_ld" = no; then - case $host_cpu in - hppa*64*) - archive_cmds_GCJ='$CC -shared ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' - ;; - ia64*) - archive_cmds_GCJ='$CC -shared ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' - ;; - *) - archive_cmds_GCJ='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' - ;; - esac - else - case $host_cpu in - hppa*64*) - archive_cmds_GCJ='$CC -b ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' - ;; - ia64*) - archive_cmds_GCJ='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' - ;; - *) - archive_cmds_GCJ='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' - ;; - esac - fi - if test "$with_gnu_ld" = no; then - hardcode_libdir_flag_spec_GCJ='${wl}+b ${wl}$libdir' - hardcode_libdir_separator_GCJ=: - - case $host_cpu in - hppa*64*|ia64*) - hardcode_libdir_flag_spec_ld_GCJ='+b $libdir' - hardcode_direct_GCJ=no - hardcode_shlibpath_var_GCJ=no - ;; - *) - hardcode_direct_GCJ=yes - export_dynamic_flag_spec_GCJ='${wl}-E' - - # hardcode_minus_L: Not really in the search PATH, - # but as the default location of the library. - hardcode_minus_L_GCJ=yes - ;; - esac - fi - ;; - - irix5* | irix6* | nonstopux*) - if test "$GCC" = yes; then - archive_cmds_GCJ='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' - else - archive_cmds_GCJ='$LD -shared $libobjs $deplibs $linker_flags -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib' - hardcode_libdir_flag_spec_ld_GCJ='-rpath $libdir' - fi - hardcode_libdir_flag_spec_GCJ='${wl}-rpath ${wl}$libdir' - hardcode_libdir_separator_GCJ=: - link_all_deplibs_GCJ=yes - ;; - - netbsd*) - if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then - archive_cmds_GCJ='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out - else - archive_cmds_GCJ='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF - fi - hardcode_libdir_flag_spec_GCJ='-R$libdir' - hardcode_direct_GCJ=yes - hardcode_shlibpath_var_GCJ=no - ;; - - newsos6) - archive_cmds_GCJ='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - hardcode_direct_GCJ=yes - hardcode_libdir_flag_spec_GCJ='${wl}-rpath ${wl}$libdir' - hardcode_libdir_separator_GCJ=: - hardcode_shlibpath_var_GCJ=no - ;; - - openbsd*) - hardcode_direct_GCJ=yes - hardcode_shlibpath_var_GCJ=no - if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then - archive_cmds_GCJ='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' - archive_expsym_cmds_GCJ='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-retain-symbols-file,$export_symbols' - hardcode_libdir_flag_spec_GCJ='${wl}-rpath,$libdir' - export_dynamic_flag_spec_GCJ='${wl}-E' - else - case $host_os in - openbsd[01].* | openbsd2.[0-7] | openbsd2.[0-7].*) - archive_cmds_GCJ='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' - hardcode_libdir_flag_spec_GCJ='-R$libdir' - ;; - *) - archive_cmds_GCJ='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' - hardcode_libdir_flag_spec_GCJ='${wl}-rpath,$libdir' - ;; - esac - fi - ;; - - os2*) - hardcode_libdir_flag_spec_GCJ='-L$libdir' - hardcode_minus_L_GCJ=yes - allow_undefined_flag_GCJ=unsupported - archive_cmds_GCJ='$echo "LIBRARY $libname INITINSTANCE" > $output_objdir/$libname.def~$echo "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~$echo DATA >> $output_objdir/$libname.def~$echo " SINGLE NONSHARED" >> $output_objdir/$libname.def~$echo EXPORTS >> $output_objdir/$libname.def~emxexp $libobjs >> $output_objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $compiler_flags $output_objdir/$libname.def' - old_archive_From_new_cmds_GCJ='emximp -o $output_objdir/$libname.a $output_objdir/$libname.def' - ;; - - osf3*) - if test "$GCC" = yes; then - allow_undefined_flag_GCJ=' ${wl}-expect_unresolved ${wl}\*' - archive_cmds_GCJ='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' - else - allow_undefined_flag_GCJ=' -expect_unresolved \*' - archive_cmds_GCJ='$LD -shared${allow_undefined_flag} $libobjs $deplibs $linker_flags -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib' - fi - hardcode_libdir_flag_spec_GCJ='${wl}-rpath ${wl}$libdir' - hardcode_libdir_separator_GCJ=: - ;; - - osf4* | osf5*) # as osf3* with the addition of -msym flag - if test "$GCC" = yes; then - allow_undefined_flag_GCJ=' ${wl}-expect_unresolved ${wl}\*' - archive_cmds_GCJ='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' - hardcode_libdir_flag_spec_GCJ='${wl}-rpath ${wl}$libdir' - else - allow_undefined_flag_GCJ=' -expect_unresolved \*' - archive_cmds_GCJ='$LD -shared${allow_undefined_flag} $libobjs $deplibs $linker_flags -msym -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib' - archive_expsym_cmds_GCJ='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; echo "-hidden">> $lib.exp~ - $LD -shared${allow_undefined_flag} -input $lib.exp $linker_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib~$rm $lib.exp' - - # Both c and cxx compiler support -rpath directly - hardcode_libdir_flag_spec_GCJ='-rpath $libdir' - fi - hardcode_libdir_separator_GCJ=: - ;; - - solaris*) - no_undefined_flag_GCJ=' -z text' - if test "$GCC" = yes; then - wlarc='${wl}' - archive_cmds_GCJ='$CC -shared ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' - archive_expsym_cmds_GCJ='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~ - $CC -shared ${wl}-M ${wl}$lib.exp ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags~$rm $lib.exp' - else - wlarc='' - archive_cmds_GCJ='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags' - archive_expsym_cmds_GCJ='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~ - $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$rm $lib.exp' - fi - hardcode_libdir_flag_spec_GCJ='-R$libdir' - hardcode_shlibpath_var_GCJ=no - case $host_os in - solaris2.[0-5] | solaris2.[0-5].*) ;; - *) - # The compiler driver will combine linker options so we - # cannot just pass the convience library names through - # without $wl, iff we do not link with $LD. - # Luckily, gcc supports the same syntax we need for Sun Studio. - # Supported since Solaris 2.6 (maybe 2.5.1?) - case $wlarc in - '') - whole_archive_flag_spec_GCJ='-z allextract$convenience -z defaultextract' ;; - *) - whole_archive_flag_spec_GCJ='${wl}-z ${wl}allextract`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $echo \"$new_convenience\"` ${wl}-z ${wl}defaultextract' ;; - esac ;; - esac - link_all_deplibs_GCJ=yes - ;; - - sunos4*) - if test "x$host_vendor" = xsequent; then - # Use $CC to link under sequent, because it throws in some extra .o - # files that make .init and .fini sections work. - archive_cmds_GCJ='$CC -G ${wl}-h $soname -o $lib $libobjs $deplibs $compiler_flags' - else - archive_cmds_GCJ='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags' - fi - hardcode_libdir_flag_spec_GCJ='-L$libdir' - hardcode_direct_GCJ=yes - hardcode_minus_L_GCJ=yes - hardcode_shlibpath_var_GCJ=no - ;; - - sysv4) - case $host_vendor in - sni) - archive_cmds_GCJ='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - hardcode_direct_GCJ=yes # is this really true??? - ;; - siemens) - ## LD is ld it makes a PLAMLIB - ## CC just makes a GrossModule. - archive_cmds_GCJ='$LD -G -o $lib $libobjs $deplibs $linker_flags' - reload_cmds_GCJ='$CC -r -o $output$reload_objs' - hardcode_direct_GCJ=no - ;; - motorola) - archive_cmds_GCJ='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - hardcode_direct_GCJ=no #Motorola manual says yes, but my tests say they lie - ;; - esac - runpath_var='LD_RUN_PATH' - hardcode_shlibpath_var_GCJ=no - ;; - - sysv4.3*) - archive_cmds_GCJ='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - hardcode_shlibpath_var_GCJ=no - export_dynamic_flag_spec_GCJ='-Bexport' - ;; - - sysv4*MP*) - if test -d /usr/nec; then - archive_cmds_GCJ='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - hardcode_shlibpath_var_GCJ=no - runpath_var=LD_RUN_PATH - hardcode_runpath_var=yes - ld_shlibs_GCJ=yes - fi - ;; - - sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7*) - no_undefined_flag_GCJ='${wl}-z,text' - archive_cmds_need_lc_GCJ=no - hardcode_shlibpath_var_GCJ=no - runpath_var='LD_RUN_PATH' - - if test "$GCC" = yes; then - archive_cmds_GCJ='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - archive_expsym_cmds_GCJ='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - else - archive_cmds_GCJ='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - archive_expsym_cmds_GCJ='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - fi - ;; - - sysv5* | sco3.2v5* | sco5v6*) - # Note: We can NOT use -z defs as we might desire, because we do not - # link with -lc, and that would cause any symbols used from libc to - # always be unresolved, which means just about no library would - # ever link correctly. If we're not using GNU ld we use -z text - # though, which does catch some bad symbols but isn't as heavy-handed - # as -z defs. - no_undefined_flag_GCJ='${wl}-z,text' - allow_undefined_flag_GCJ='${wl}-z,nodefs' - archive_cmds_need_lc_GCJ=no - hardcode_shlibpath_var_GCJ=no - hardcode_libdir_flag_spec_GCJ='`test -z "$SCOABSPATH" && echo ${wl}-R,$libdir`' - hardcode_libdir_separator_GCJ=':' - link_all_deplibs_GCJ=yes - export_dynamic_flag_spec_GCJ='${wl}-Bexport' - runpath_var='LD_RUN_PATH' - - if test "$GCC" = yes; then - archive_cmds_GCJ='$CC -shared ${wl}-h,\${SCOABSPATH:+${install_libdir}/}$soname -o $lib $libobjs $deplibs $compiler_flags' - archive_expsym_cmds_GCJ='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,\${SCOABSPATH:+${install_libdir}/}$soname -o $lib $libobjs $deplibs $compiler_flags' - else - archive_cmds_GCJ='$CC -G ${wl}-h,\${SCOABSPATH:+${install_libdir}/}$soname -o $lib $libobjs $deplibs $compiler_flags' - archive_expsym_cmds_GCJ='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,\${SCOABSPATH:+${install_libdir}/}$soname -o $lib $libobjs $deplibs $compiler_flags' - fi - ;; - - uts4*) - archive_cmds_GCJ='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - hardcode_libdir_flag_spec_GCJ='-L$libdir' - hardcode_shlibpath_var_GCJ=no - ;; - - *) - ld_shlibs_GCJ=no - ;; - esac - fi - -echo "$as_me:$LINENO: result: $ld_shlibs_GCJ" >&5 -echo "${ECHO_T}$ld_shlibs_GCJ" >&6 -test "$ld_shlibs_GCJ" = no && can_build_shared=no - -# -# Do we need to explicitly link libc? -# -case "x$archive_cmds_need_lc_GCJ" in -x|xyes) - # Assume -lc should be added - archive_cmds_need_lc_GCJ=yes - - if test "$enable_shared" = yes && test "$GCC" = yes; then - case $archive_cmds_GCJ in - *'~'*) - # FIXME: we may have to deal with multi-command sequences. - ;; - '$CC '*) - # Test whether the compiler implicitly links with -lc since on some - # systems, -lgcc has to come before -lc. If gcc already passes -lc - # to ld, don't add -lc before -lgcc. - echo "$as_me:$LINENO: checking whether -lc should be explicitly linked in" >&5 -echo $ECHO_N "checking whether -lc should be explicitly linked in... $ECHO_C" >&6 - $rm conftest* - printf "$lt_simple_compile_test_code" > conftest.$ac_ext - - if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } 2>conftest.err; then - soname=conftest - lib=conftest - libobjs=conftest.$ac_objext - deplibs= - wl=$lt_prog_compiler_wl_GCJ - pic_flag=$lt_prog_compiler_pic_GCJ - compiler_flags=-v - linker_flags=-v - verstring= - output_objdir=. - libname=conftest - lt_save_allow_undefined_flag=$allow_undefined_flag_GCJ - allow_undefined_flag_GCJ= - if { (eval echo "$as_me:$LINENO: \"$archive_cmds_GCJ 2\>\&1 \| grep \" -lc \" \>/dev/null 2\>\&1\"") >&5 - (eval $archive_cmds_GCJ 2\>\&1 \| grep \" -lc \" \>/dev/null 2\>\&1) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } - then - archive_cmds_need_lc_GCJ=no - else - archive_cmds_need_lc_GCJ=yes - fi - allow_undefined_flag_GCJ=$lt_save_allow_undefined_flag - else - cat conftest.err 1>&5 - fi - $rm conftest* - echo "$as_me:$LINENO: result: $archive_cmds_need_lc_GCJ" >&5 -echo "${ECHO_T}$archive_cmds_need_lc_GCJ" >&6 - ;; - esac - fi - ;; -esac - -echo "$as_me:$LINENO: checking dynamic linker characteristics" >&5 -echo $ECHO_N "checking dynamic linker characteristics... $ECHO_C" >&6 -library_names_spec= -libname_spec='lib$name' -soname_spec= -shrext_cmds=".so" -postinstall_cmds= -postuninstall_cmds= -finish_cmds= -finish_eval= -shlibpath_var= -shlibpath_overrides_runpath=unknown -version_type=none -dynamic_linker="$host_os ld.so" -sys_lib_dlsearch_path_spec="/lib /usr/lib" -if test "$GCC" = yes; then - sys_lib_search_path_spec=`$CC -print-search-dirs | grep "^libraries:" | $SED -e "s/^libraries://" -e "s,=/,/,g"` - if echo "$sys_lib_search_path_spec" | grep ';' >/dev/null ; then - # if the path contains ";" then we assume it to be the separator - # otherwise default to the standard path separator (i.e. ":") - it is - # assumed that no part of a normal pathname contains ";" but that should - # okay in the real world where ";" in dirpaths is itself problematic. - sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` - else - sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` - fi -else - sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" -fi -need_lib_prefix=unknown -hardcode_into_libs=no - -# when you set need_version to no, make sure it does not cause -set_version -# flags to be left without arguments -need_version=unknown - -case $host_os in -aix3*) - version_type=linux - library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a' - shlibpath_var=LIBPATH - - # AIX 3 has no versioning support, so we append a major version to the name. - soname_spec='${libname}${release}${shared_ext}$major' - ;; - -aix4* | aix5*) - version_type=linux - need_lib_prefix=no - need_version=no - hardcode_into_libs=yes - if test "$host_cpu" = ia64; then - # AIX 5 supports IA64 - library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}' - shlibpath_var=LD_LIBRARY_PATH - else - # With GCC up to 2.95.x, collect2 would create an import file - # for dependence libraries. The import file would start with - # the line `#! .'. This would cause the generated library to - # depend on `.', always an invalid library. This was fixed in - # development snapshots of GCC prior to 3.0. - case $host_os in - aix4 | aix4.[01] | aix4.[01].*) - if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' - echo ' yes ' - echo '#endif'; } | ${CC} -E - | grep yes > /dev/null; then - : - else - can_build_shared=no - fi - ;; - esac - # AIX (on Power*) has no versioning support, so currently we can not hardcode correct - # soname into executable. Probably we can add versioning support to - # collect2, so additional links can be useful in future. - if test "$aix_use_runtimelinking" = yes; then - # If using run time linking (on AIX 4.2 or later) use lib.so - # instead of lib.a to let people know that these are not - # typical AIX shared libraries. - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - else - # We preserve .a as extension for shared libraries through AIX4.2 - # and later when we are not doing run time linking. - library_names_spec='${libname}${release}.a $libname.a' - soname_spec='${libname}${release}${shared_ext}$major' - fi - shlibpath_var=LIBPATH - fi - ;; - -amigaos*) - library_names_spec='$libname.ixlibrary $libname.a' - # Create ${libname}_ixlibrary.a entries in /sys/libs. - finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`$echo "X$lib" | $Xsed -e '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; test $rm /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' - ;; - -beos*) - library_names_spec='${libname}${shared_ext}' - dynamic_linker="$host_os ld.so" - shlibpath_var=LIBRARY_PATH - ;; - -bsdi[45]*) - version_type=linux - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' - shlibpath_var=LD_LIBRARY_PATH - sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" - sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" - # the default ld.so.conf also contains /usr/contrib/lib and - # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow - # libtool to hard-code these into programs - ;; - -cygwin* | mingw* | pw32*) - version_type=windows - shrext_cmds=".dll" - need_version=no - need_lib_prefix=no - - case $GCC,$host_os in - yes,cygwin* | yes,mingw* | yes,pw32*) - library_names_spec='$libname.dll.a' - # DLL is installed to $(libdir)/../bin by postinstall_cmds - postinstall_cmds='base_file=`basename \${file}`~ - dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i;echo \$dlname'\''`~ - dldir=$destdir/`dirname \$dlpath`~ - test -d \$dldir || mkdir -p \$dldir~ - $install_prog $dir/$dlname \$dldir/$dlname~ - chmod a+x \$dldir/$dlname' - postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ - dlpath=$dir/\$dldll~ - $rm \$dlpath' - shlibpath_overrides_runpath=yes - - case $host_os in - cygwin*) - # Cygwin DLLs use 'cyg' prefix rather than 'lib' - soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' - sys_lib_search_path_spec="/usr/lib /lib/w32api /lib /usr/local/lib" - ;; - mingw*) - # MinGW DLLs use traditional 'lib' prefix - soname_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' - sys_lib_search_path_spec=`$CC -print-search-dirs | grep "^libraries:" | $SED -e "s/^libraries://" -e "s,=/,/,g"` - if echo "$sys_lib_search_path_spec" | grep ';[c-zC-Z]:/' >/dev/null; then - # It is most probably a Windows format PATH printed by - # mingw gcc, but we are running on Cygwin. Gcc prints its search - # path with ; separators, and with drive letters. We can handle the - # drive letters (cygwin fileutils understands them), so leave them, - # especially as we might pass files found there to a mingw objdump, - # which wouldn't understand a cygwinified path. Ahh. - sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` - else - sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` - fi - ;; - pw32*) - # pw32 DLLs use 'pw' prefix rather than 'lib' - library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' - ;; - esac - ;; - - *) - library_names_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext} $libname.lib' - ;; - esac - dynamic_linker='Win32 ld.exe' - # FIXME: first we should search . and the directory the executable is in - shlibpath_var=PATH - ;; - -darwin* | rhapsody*) - dynamic_linker="$host_os dyld" - version_type=darwin - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${versuffix}$shared_ext ${libname}${release}${major}$shared_ext ${libname}$shared_ext' - soname_spec='${libname}${release}${major}$shared_ext' - shlibpath_overrides_runpath=yes - shlibpath_var=DYLD_LIBRARY_PATH - shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' - # Apple's gcc prints 'gcc -print-search-dirs' doesn't operate the same. - if test "$GCC" = yes; then - sys_lib_search_path_spec=`$CC -print-search-dirs | tr "\n" "$PATH_SEPARATOR" | sed -e 's/libraries:/@libraries:/' | tr "@" "\n" | grep "^libraries:" | sed -e "s/^libraries://" -e "s,=/,/,g" -e "s,$PATH_SEPARATOR, ,g" -e "s,.*,& /lib /usr/lib /usr/local/lib,g"` - else - sys_lib_search_path_spec='/lib /usr/lib /usr/local/lib' - fi - sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' - ;; - -dgux*) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - ;; - -freebsd1*) - dynamic_linker=no - ;; - -kfreebsd*-gnu) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=no - hardcode_into_libs=yes - dynamic_linker='GNU ld.so' - ;; - -freebsd* | dragonfly*) - # DragonFly does not have aout. When/if they implement a new - # versioning mechanism, adjust this. - if test -x /usr/bin/objformat; then - objformat=`/usr/bin/objformat` - else - case $host_os in - freebsd[123]*) objformat=aout ;; - *) objformat=elf ;; - esac - fi - version_type=freebsd-$objformat - case $version_type in - freebsd-elf*) - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' - need_version=no - need_lib_prefix=no - ;; - freebsd-*) - library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix' - need_version=yes - ;; - esac - shlibpath_var=LD_LIBRARY_PATH - case $host_os in - freebsd2*) - shlibpath_overrides_runpath=yes - ;; - freebsd3.[01]* | freebsdelf3.[01]*) - shlibpath_overrides_runpath=yes - hardcode_into_libs=yes - ;; - freebsd3.[2-9]* | freebsdelf3.[2-9]* | \ - freebsd4.[0-5] | freebsdelf4.[0-5] | freebsd4.1.1 | freebsdelf4.1.1) - shlibpath_overrides_runpath=no - hardcode_into_libs=yes - ;; - freebsd*) # from 4.6 on - shlibpath_overrides_runpath=yes - hardcode_into_libs=yes - ;; - esac - ;; - -gnu*) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - hardcode_into_libs=yes - ;; - -hpux9* | hpux10* | hpux11*) - # Give a soname corresponding to the major version so that dld.sl refuses to - # link against other versions. - version_type=sunos - need_lib_prefix=no - need_version=no - case $host_cpu in - ia64*) - shrext_cmds='.so' - hardcode_into_libs=yes - dynamic_linker="$host_os dld.so" - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - if test "X$HPUX_IA64_MODE" = X32; then - sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" - else - sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" - fi - sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec - ;; - hppa*64*) - shrext_cmds='.sl' - hardcode_into_libs=yes - dynamic_linker="$host_os dld.sl" - shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH - shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" - sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec - ;; - *) - shrext_cmds='.sl' - dynamic_linker="$host_os dld.sl" - shlibpath_var=SHLIB_PATH - shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - ;; - esac - # HP-UX runs *really* slowly unless shared libraries are mode 555. - postinstall_cmds='chmod 555 $lib' - ;; - -interix3*) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=no - hardcode_into_libs=yes - ;; - -irix5* | irix6* | nonstopux*) - case $host_os in - nonstopux*) version_type=nonstopux ;; - *) - if test "$lt_cv_prog_gnu_ld" = yes; then - version_type=linux - else - version_type=irix - fi ;; - esac - need_lib_prefix=no - need_version=no - soname_spec='${libname}${release}${shared_ext}$major' - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}' - case $host_os in - irix5* | nonstopux*) - libsuff= shlibsuff= - ;; - *) - case $LD in # libtool.m4 will add one of these switches to LD - *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") - libsuff= shlibsuff= libmagic=32-bit;; - *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") - libsuff=32 shlibsuff=N32 libmagic=N32;; - *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") - libsuff=64 shlibsuff=64 libmagic=64-bit;; - *) libsuff= shlibsuff= libmagic=never-match;; - esac - ;; - esac - shlibpath_var=LD_LIBRARY${shlibsuff}_PATH - shlibpath_overrides_runpath=no - sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}" - sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}" - hardcode_into_libs=yes - ;; - -# No shared lib support for Linux oldld, aout, or coff. -linux*oldld* | linux*aout* | linux*coff*) - dynamic_linker=no - ;; - -# This must be Linux ELF. -linux*) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=no - # This implies no fast_install, which is unacceptable. - # Some rework will be needed to allow for fast_install - # before this can be enabled. - hardcode_into_libs=yes - - # Append ld.so.conf contents to the search path - if test -f /etc/ld.so.conf; then - lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;/^$/d' | tr '\n' ' '` - sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" - fi - - # We used to test for /lib/ld.so.1 and disable shared libraries on - # powerpc, because MkLinux only supported shared libraries with the - # GNU dynamic linker. Since this was broken with cross compilers, - # most powerpc-linux boxes support dynamic linking these days and - # people can always --disable-shared, the test was removed, and we - # assume the GNU/Linux dynamic linker is in use. - dynamic_linker='GNU/Linux ld.so' - ;; - -knetbsd*-gnu) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=no - hardcode_into_libs=yes - dynamic_linker='GNU ld.so' - ;; - -netbsd*) - version_type=sunos - need_lib_prefix=no - need_version=no - if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' - finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' - dynamic_linker='NetBSD (a.out) ld.so' - else - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - dynamic_linker='NetBSD ld.elf_so' - fi - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - hardcode_into_libs=yes - ;; - -newsos6) - version_type=linux - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - ;; - -nto-qnx*) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - ;; - -openbsd*) - version_type=sunos - sys_lib_dlsearch_path_spec="/usr/lib" - need_lib_prefix=no - # Some older versions of OpenBSD (3.3 at least) *do* need versioned libs. - case $host_os in - openbsd3.3 | openbsd3.3.*) need_version=yes ;; - *) need_version=no ;; - esac - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' - finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' - shlibpath_var=LD_LIBRARY_PATH - if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then - case $host_os in - openbsd2.[89] | openbsd2.[89].*) - shlibpath_overrides_runpath=no - ;; - *) - shlibpath_overrides_runpath=yes - ;; - esac - else - shlibpath_overrides_runpath=yes - fi - ;; - -os2*) - libname_spec='$name' - shrext_cmds=".dll" - need_lib_prefix=no - library_names_spec='$libname${shared_ext} $libname.a' - dynamic_linker='OS/2 ld.exe' - shlibpath_var=LIBPATH - ;; - -osf3* | osf4* | osf5*) - version_type=osf - need_lib_prefix=no - need_version=no - soname_spec='${libname}${release}${shared_ext}$major' - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - shlibpath_var=LD_LIBRARY_PATH - sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" - sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec" - ;; - -solaris*) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - hardcode_into_libs=yes - # ldd complains unless libraries are executable - postinstall_cmds='chmod +x $lib' - ;; - -sunos4*) - version_type=sunos - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' - finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - if test "$with_gnu_ld" = yes; then - need_lib_prefix=no - fi - need_version=yes - ;; - -sysv4 | sysv4.3*) - version_type=linux - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - case $host_vendor in - sni) - shlibpath_overrides_runpath=no - need_lib_prefix=no - export_dynamic_flag_spec='${wl}-Blargedynsym' - runpath_var=LD_RUN_PATH - ;; - siemens) - need_lib_prefix=no - ;; - motorola) - need_lib_prefix=no - need_version=no - shlibpath_overrides_runpath=no - sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' - ;; - esac - ;; - -sysv4*MP*) - if test -d /usr/nec ;then - version_type=linux - library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}' - soname_spec='$libname${shared_ext}.$major' - shlibpath_var=LD_LIBRARY_PATH - fi - ;; - -sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) - version_type=freebsd-elf - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - hardcode_into_libs=yes - if test "$with_gnu_ld" = yes; then - sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' - shlibpath_overrides_runpath=no - else - sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' - shlibpath_overrides_runpath=yes - case $host_os in - sco3.2v5*) - sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" - ;; - esac - fi - sys_lib_dlsearch_path_spec='/usr/lib' - ;; - -uts4*) - version_type=linux - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - ;; - -*) - dynamic_linker=no - ;; -esac -echo "$as_me:$LINENO: result: $dynamic_linker" >&5 -echo "${ECHO_T}$dynamic_linker" >&6 -test "$dynamic_linker" = no && can_build_shared=no - -variables_saved_for_relink="PATH $shlibpath_var $runpath_var" -if test "$GCC" = yes; then - variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" -fi - -echo "$as_me:$LINENO: checking how to hardcode library paths into programs" >&5 -echo $ECHO_N "checking how to hardcode library paths into programs... $ECHO_C" >&6 -hardcode_action_GCJ= -if test -n "$hardcode_libdir_flag_spec_GCJ" || \ - test -n "$runpath_var_GCJ" || \ - test "X$hardcode_automatic_GCJ" = "Xyes" ; then - - # We can hardcode non-existant directories. - if test "$hardcode_direct_GCJ" != no && - # If the only mechanism to avoid hardcoding is shlibpath_var, we - # have to relink, otherwise we might link with an installed library - # when we should be linking with a yet-to-be-installed one - ## test "$_LT_AC_TAGVAR(hardcode_shlibpath_var, GCJ)" != no && - test "$hardcode_minus_L_GCJ" != no; then - # Linking always hardcodes the temporary library directory. - hardcode_action_GCJ=relink - else - # We can link without hardcoding, and we can hardcode nonexisting dirs. - hardcode_action_GCJ=immediate - fi -else - # We cannot hardcode anything, or else we can only hardcode existing - # directories. - hardcode_action_GCJ=unsupported -fi -echo "$as_me:$LINENO: result: $hardcode_action_GCJ" >&5 -echo "${ECHO_T}$hardcode_action_GCJ" >&6 - -if test "$hardcode_action_GCJ" = relink; then - # Fast installation is not supported - enable_fast_install=no -elif test "$shlibpath_overrides_runpath" = yes || - test "$enable_shared" = no; then - # Fast installation is not necessary - enable_fast_install=needless -fi - - -# The else clause should only fire when bootstrapping the -# libtool distribution, otherwise you forgot to ship ltmain.sh -# with your package, and you will get complaints that there are -# no rules to generate ltmain.sh. -if test -f "$ltmain"; then - # See if we are running on zsh, and set the options which allow our commands through - # without removal of \ escapes. - if test -n "${ZSH_VERSION+set}" ; then - setopt NO_GLOB_SUBST - fi - # Now quote all the things that may contain metacharacters while being - # careful not to overquote the AC_SUBSTed values. We take copies of the - # variables and quote the copies for generation of the libtool script. - for var in echo old_CC old_CFLAGS AR AR_FLAGS EGREP RANLIB LN_S LTCC LTCFLAGS NM \ - SED SHELL STRIP \ - libname_spec library_names_spec soname_spec extract_expsyms_cmds \ - old_striplib striplib file_magic_cmd finish_cmds finish_eval \ - deplibs_check_method reload_flag reload_cmds need_locks \ - lt_cv_sys_global_symbol_pipe lt_cv_sys_global_symbol_to_cdecl \ - lt_cv_sys_global_symbol_to_c_name_address \ - sys_lib_search_path_spec sys_lib_dlsearch_path_spec \ - old_postinstall_cmds old_postuninstall_cmds \ - compiler_GCJ \ - CC_GCJ \ - LD_GCJ \ - lt_prog_compiler_wl_GCJ \ - lt_prog_compiler_pic_GCJ \ - lt_prog_compiler_static_GCJ \ - lt_prog_compiler_no_builtin_flag_GCJ \ - export_dynamic_flag_spec_GCJ \ - thread_safe_flag_spec_GCJ \ - whole_archive_flag_spec_GCJ \ - enable_shared_with_static_runtimes_GCJ \ - old_archive_cmds_GCJ \ - old_archive_from_new_cmds_GCJ \ - predep_objects_GCJ \ - postdep_objects_GCJ \ - predeps_GCJ \ - postdeps_GCJ \ - compiler_lib_search_path_GCJ \ - archive_cmds_GCJ \ - archive_expsym_cmds_GCJ \ - postinstall_cmds_GCJ \ - postuninstall_cmds_GCJ \ - old_archive_from_expsyms_cmds_GCJ \ - allow_undefined_flag_GCJ \ - no_undefined_flag_GCJ \ - export_symbols_cmds_GCJ \ - hardcode_libdir_flag_spec_GCJ \ - hardcode_libdir_flag_spec_ld_GCJ \ - hardcode_libdir_separator_GCJ \ - hardcode_automatic_GCJ \ - module_cmds_GCJ \ - module_expsym_cmds_GCJ \ - lt_cv_prog_compiler_c_o_GCJ \ - exclude_expsyms_GCJ \ - include_expsyms_GCJ; do - - case $var in - old_archive_cmds_GCJ | \ - old_archive_from_new_cmds_GCJ | \ - archive_cmds_GCJ | \ - archive_expsym_cmds_GCJ | \ - module_cmds_GCJ | \ - module_expsym_cmds_GCJ | \ - old_archive_from_expsyms_cmds_GCJ | \ - export_symbols_cmds_GCJ | \ - extract_expsyms_cmds | reload_cmds | finish_cmds | \ - postinstall_cmds | postuninstall_cmds | \ - old_postinstall_cmds | old_postuninstall_cmds | \ - sys_lib_search_path_spec | sys_lib_dlsearch_path_spec) - # Double-quote double-evaled strings. - eval "lt_$var=\\\"\`\$echo \"X\$$var\" | \$Xsed -e \"\$double_quote_subst\" -e \"\$sed_quote_subst\" -e \"\$delay_variable_subst\"\`\\\"" - ;; - *) - eval "lt_$var=\\\"\`\$echo \"X\$$var\" | \$Xsed -e \"\$sed_quote_subst\"\`\\\"" - ;; - esac - done - - case $lt_echo in - *'\$0 --fallback-echo"') - lt_echo=`$echo "X$lt_echo" | $Xsed -e 's/\\\\\\\$0 --fallback-echo"$/$0 --fallback-echo"/'` - ;; - esac - -cfgfile="$ofile" - - cat <<__EOF__ >> "$cfgfile" -# ### BEGIN LIBTOOL TAG CONFIG: $tagname - -# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`: - -# Shell to use when invoking shell scripts. -SHELL=$lt_SHELL - -# Whether or not to build shared libraries. -build_libtool_libs=$enable_shared - -# Whether or not to build static libraries. -build_old_libs=$enable_static - -# Whether or not to add -lc for building shared libraries. -build_libtool_need_lc=$archive_cmds_need_lc_GCJ - -# Whether or not to disallow shared libs when runtime libs are static -allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes_GCJ - -# Whether or not to optimize for fast installation. -fast_install=$enable_fast_install - -# The host system. -host_alias=$host_alias -host=$host -host_os=$host_os - -# The build system. -build_alias=$build_alias -build=$build -build_os=$build_os - -# An echo program that does not interpret backslashes. -echo=$lt_echo - -# The archiver. -AR=$lt_AR -AR_FLAGS=$lt_AR_FLAGS - -# A C compiler. -LTCC=$lt_LTCC - -# LTCC compiler flags. -LTCFLAGS=$lt_LTCFLAGS - -# A language-specific compiler. -CC=$lt_compiler_GCJ - -# Is the compiler the GNU C compiler? -with_gcc=$GCC_GCJ - -# An ERE matcher. -EGREP=$lt_EGREP - -# The linker used to build libraries. -LD=$lt_LD_GCJ - -# Whether we need hard or soft links. -LN_S=$lt_LN_S - -# A BSD-compatible nm program. -NM=$lt_NM - -# A symbol stripping program -STRIP=$lt_STRIP - -# Used to examine libraries when file_magic_cmd begins "file" -MAGIC_CMD=$MAGIC_CMD - -# Used on cygwin: DLL creation program. -DLLTOOL="$DLLTOOL" - -# Used on cygwin: object dumper. -OBJDUMP="$OBJDUMP" - -# Used on cygwin: assembler. -AS="$AS" - -# The name of the directory that contains temporary libtool files. -objdir=$objdir - -# How to create reloadable object files. -reload_flag=$lt_reload_flag -reload_cmds=$lt_reload_cmds - -# How to pass a linker flag through the compiler. -wl=$lt_lt_prog_compiler_wl_GCJ - -# Object file suffix (normally "o"). -objext="$ac_objext" - -# Old archive suffix (normally "a"). -libext="$libext" - -# Shared library suffix (normally ".so"). -shrext_cmds='$shrext_cmds' - -# Executable file suffix (normally ""). -exeext="$exeext" - -# Additional compiler flags for building library objects. -pic_flag=$lt_lt_prog_compiler_pic_GCJ -pic_mode=$pic_mode - -# What is the maximum length of a command? -max_cmd_len=$lt_cv_sys_max_cmd_len - -# Does compiler simultaneously support -c and -o options? -compiler_c_o=$lt_lt_cv_prog_compiler_c_o_GCJ - -# Must we lock files when doing compilation? -need_locks=$lt_need_locks - -# Do we need the lib prefix for modules? -need_lib_prefix=$need_lib_prefix - -# Do we need a version for libraries? -need_version=$need_version - -# Whether dlopen is supported. -dlopen_support=$enable_dlopen - -# Whether dlopen of programs is supported. -dlopen_self=$enable_dlopen_self - -# Whether dlopen of statically linked programs is supported. -dlopen_self_static=$enable_dlopen_self_static - -# Compiler flag to prevent dynamic linking. -link_static_flag=$lt_lt_prog_compiler_static_GCJ - -# Compiler flag to turn off builtin functions. -no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag_GCJ - -# Compiler flag to allow reflexive dlopens. -export_dynamic_flag_spec=$lt_export_dynamic_flag_spec_GCJ - -# Compiler flag to generate shared objects directly from archives. -whole_archive_flag_spec=$lt_whole_archive_flag_spec_GCJ - -# Compiler flag to generate thread-safe objects. -thread_safe_flag_spec=$lt_thread_safe_flag_spec_GCJ - -# Library versioning type. -version_type=$version_type - -# Format of library name prefix. -libname_spec=$lt_libname_spec - -# List of archive names. First name is the real one, the rest are links. -# The last name is the one that the linker finds with -lNAME. -library_names_spec=$lt_library_names_spec - -# The coded name of the library, if different from the real name. -soname_spec=$lt_soname_spec - -# Commands used to build and install an old-style archive. -RANLIB=$lt_RANLIB -old_archive_cmds=$lt_old_archive_cmds_GCJ -old_postinstall_cmds=$lt_old_postinstall_cmds -old_postuninstall_cmds=$lt_old_postuninstall_cmds - -# Create an old-style archive from a shared archive. -old_archive_from_new_cmds=$lt_old_archive_from_new_cmds_GCJ - -# Create a temporary old-style archive to link instead of a shared archive. -old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds_GCJ - -# Commands used to build and install a shared archive. -archive_cmds=$lt_archive_cmds_GCJ -archive_expsym_cmds=$lt_archive_expsym_cmds_GCJ -postinstall_cmds=$lt_postinstall_cmds -postuninstall_cmds=$lt_postuninstall_cmds - -# Commands used to build a loadable module (assumed same as above if empty) -module_cmds=$lt_module_cmds_GCJ -module_expsym_cmds=$lt_module_expsym_cmds_GCJ - -# Commands to strip libraries. -old_striplib=$lt_old_striplib -striplib=$lt_striplib - -# Dependencies to place before the objects being linked to create a -# shared library. -predep_objects=$lt_predep_objects_GCJ - -# Dependencies to place after the objects being linked to create a -# shared library. -postdep_objects=$lt_postdep_objects_GCJ - -# Dependencies to place before the objects being linked to create a -# shared library. -predeps=$lt_predeps_GCJ - -# Dependencies to place after the objects being linked to create a -# shared library. -postdeps=$lt_postdeps_GCJ - -# The library search path used internally by the compiler when linking -# a shared library. -compiler_lib_search_path=$lt_compiler_lib_search_path_GCJ - -# Method to check whether dependent libraries are shared objects. -deplibs_check_method=$lt_deplibs_check_method - -# Command to use when deplibs_check_method == file_magic. -file_magic_cmd=$lt_file_magic_cmd - -# Flag that allows shared libraries with undefined symbols to be built. -allow_undefined_flag=$lt_allow_undefined_flag_GCJ - -# Flag that forces no undefined symbols. -no_undefined_flag=$lt_no_undefined_flag_GCJ - -# Commands used to finish a libtool library installation in a directory. -finish_cmds=$lt_finish_cmds - -# Same as above, but a single script fragment to be evaled but not shown. -finish_eval=$lt_finish_eval - -# Take the output of nm and produce a listing of raw symbols and C names. -global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe - -# Transform the output of nm in a proper C declaration -global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl - -# Transform the output of nm in a C name address pair -global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address - -# This is the shared library runtime path variable. -runpath_var=$runpath_var - -# This is the shared library path variable. -shlibpath_var=$shlibpath_var - -# Is shlibpath searched before the hard-coded library search path? -shlibpath_overrides_runpath=$shlibpath_overrides_runpath - -# How to hardcode a shared library path into an executable. -hardcode_action=$hardcode_action_GCJ - -# Whether we should hardcode library paths into libraries. -hardcode_into_libs=$hardcode_into_libs - -# Flag to hardcode \$libdir into a binary during linking. -# This must work even if \$libdir does not exist. -hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec_GCJ - -# If ld is used when linking, flag to hardcode \$libdir into -# a binary during linking. This must work even if \$libdir does -# not exist. -hardcode_libdir_flag_spec_ld=$lt_hardcode_libdir_flag_spec_ld_GCJ - -# Whether we need a single -rpath flag with a separated argument. -hardcode_libdir_separator=$lt_hardcode_libdir_separator_GCJ - -# Set to yes if using DIR/libNAME${shared_ext} during linking hardcodes DIR into the -# resulting binary. -hardcode_direct=$hardcode_direct_GCJ - -# Set to yes if using the -LDIR flag during linking hardcodes DIR into the -# resulting binary. -hardcode_minus_L=$hardcode_minus_L_GCJ - -# Set to yes if using SHLIBPATH_VAR=DIR during linking hardcodes DIR into -# the resulting binary. -hardcode_shlibpath_var=$hardcode_shlibpath_var_GCJ - -# Set to yes if building a shared library automatically hardcodes DIR into the library -# and all subsequent libraries and executables linked against it. -hardcode_automatic=$hardcode_automatic_GCJ - -# Variables whose values should be saved in libtool wrapper scripts and -# restored at relink time. -variables_saved_for_relink="$variables_saved_for_relink" - -# Whether libtool must link a program against all its dependency libraries. -link_all_deplibs=$link_all_deplibs_GCJ - -# Compile-time system search path for libraries -sys_lib_search_path_spec=$lt_sys_lib_search_path_spec - -# Run-time system search path for libraries -sys_lib_dlsearch_path_spec=$lt_sys_lib_dlsearch_path_spec - -# Fix the shell variable \$srcfile for the compiler. -fix_srcfile_path="$fix_srcfile_path_GCJ" - -# Set to yes if exported symbols are required. -always_export_symbols=$always_export_symbols_GCJ - -# The commands to list exported symbols. -export_symbols_cmds=$lt_export_symbols_cmds_GCJ - -# The commands to extract the exported symbol list from a shared archive. -extract_expsyms_cmds=$lt_extract_expsyms_cmds - -# Symbols that should not be listed in the preloaded symbols. -exclude_expsyms=$lt_exclude_expsyms_GCJ - -# Symbols that must always be exported. -include_expsyms=$lt_include_expsyms_GCJ - -# ### END LIBTOOL TAG CONFIG: $tagname - -__EOF__ - - -else - # If there is no Makefile yet, we rely on a make rule to execute - # `config.status --recheck' to rerun these tests and create the - # libtool script then. - ltmain_in=`echo $ltmain | sed -e 's/\.sh$/.in/'` - if test -f "$ltmain_in"; then - test -f Makefile && make "$ltmain" - fi -fi - - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - -CC="$lt_save_CC" - - else - tagname="" - fi - ;; - - RC) - - - -# Source file extension for RC test sources. -ac_ext=rc - -# Object file extension for compiled RC test sources. -objext=o -objext_RC=$objext - -# Code to be used in simple compile tests -lt_simple_compile_test_code='sample MENU { MENUITEM "&Soup", 100, CHECKED }\n' - -# Code to be used in simple link tests -lt_simple_link_test_code="$lt_simple_compile_test_code" - -# ltmain only uses $CC for tagged configurations so make sure $CC is set. - -# If no C compiler was specified, use CC. -LTCC=${LTCC-"$CC"} - -# If no C compiler flags were specified, use CFLAGS. -LTCFLAGS=${LTCFLAGS-"$CFLAGS"} - -# Allow CC to be a program name with arguments. -compiler=$CC - - -# save warnings/boilerplate of simple test code -ac_outfile=conftest.$ac_objext -printf "$lt_simple_compile_test_code" >conftest.$ac_ext -eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err -_lt_compiler_boilerplate=`cat conftest.err` -$rm conftest* - -ac_outfile=conftest.$ac_objext -printf "$lt_simple_link_test_code" >conftest.$ac_ext -eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err -_lt_linker_boilerplate=`cat conftest.err` -$rm conftest* - - -# Allow CC to be a program name with arguments. -lt_save_CC="$CC" -CC=${RC-"windres"} -compiler=$CC -compiler_RC=$CC -for cc_temp in $compiler""; do - case $cc_temp in - compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; - distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; - \-*) ;; - *) break;; - esac -done -cc_basename=`$echo "X$cc_temp" | $Xsed -e 's%.*/%%' -e "s%^$host_alias-%%"` - -lt_cv_prog_compiler_c_o_RC=yes - -# The else clause should only fire when bootstrapping the -# libtool distribution, otherwise you forgot to ship ltmain.sh -# with your package, and you will get complaints that there are -# no rules to generate ltmain.sh. -if test -f "$ltmain"; then - # See if we are running on zsh, and set the options which allow our commands through - # without removal of \ escapes. - if test -n "${ZSH_VERSION+set}" ; then - setopt NO_GLOB_SUBST - fi - # Now quote all the things that may contain metacharacters while being - # careful not to overquote the AC_SUBSTed values. We take copies of the - # variables and quote the copies for generation of the libtool script. - for var in echo old_CC old_CFLAGS AR AR_FLAGS EGREP RANLIB LN_S LTCC LTCFLAGS NM \ - SED SHELL STRIP \ - libname_spec library_names_spec soname_spec extract_expsyms_cmds \ - old_striplib striplib file_magic_cmd finish_cmds finish_eval \ - deplibs_check_method reload_flag reload_cmds need_locks \ - lt_cv_sys_global_symbol_pipe lt_cv_sys_global_symbol_to_cdecl \ - lt_cv_sys_global_symbol_to_c_name_address \ - sys_lib_search_path_spec sys_lib_dlsearch_path_spec \ - old_postinstall_cmds old_postuninstall_cmds \ - compiler_RC \ - CC_RC \ - LD_RC \ - lt_prog_compiler_wl_RC \ - lt_prog_compiler_pic_RC \ - lt_prog_compiler_static_RC \ - lt_prog_compiler_no_builtin_flag_RC \ - export_dynamic_flag_spec_RC \ - thread_safe_flag_spec_RC \ - whole_archive_flag_spec_RC \ - enable_shared_with_static_runtimes_RC \ - old_archive_cmds_RC \ - old_archive_from_new_cmds_RC \ - predep_objects_RC \ - postdep_objects_RC \ - predeps_RC \ - postdeps_RC \ - compiler_lib_search_path_RC \ - archive_cmds_RC \ - archive_expsym_cmds_RC \ - postinstall_cmds_RC \ - postuninstall_cmds_RC \ - old_archive_from_expsyms_cmds_RC \ - allow_undefined_flag_RC \ - no_undefined_flag_RC \ - export_symbols_cmds_RC \ - hardcode_libdir_flag_spec_RC \ - hardcode_libdir_flag_spec_ld_RC \ - hardcode_libdir_separator_RC \ - hardcode_automatic_RC \ - module_cmds_RC \ - module_expsym_cmds_RC \ - lt_cv_prog_compiler_c_o_RC \ - exclude_expsyms_RC \ - include_expsyms_RC; do - - case $var in - old_archive_cmds_RC | \ - old_archive_from_new_cmds_RC | \ - archive_cmds_RC | \ - archive_expsym_cmds_RC | \ - module_cmds_RC | \ - module_expsym_cmds_RC | \ - old_archive_from_expsyms_cmds_RC | \ - export_symbols_cmds_RC | \ - extract_expsyms_cmds | reload_cmds | finish_cmds | \ - postinstall_cmds | postuninstall_cmds | \ - old_postinstall_cmds | old_postuninstall_cmds | \ - sys_lib_search_path_spec | sys_lib_dlsearch_path_spec) - # Double-quote double-evaled strings. - eval "lt_$var=\\\"\`\$echo \"X\$$var\" | \$Xsed -e \"\$double_quote_subst\" -e \"\$sed_quote_subst\" -e \"\$delay_variable_subst\"\`\\\"" - ;; - *) - eval "lt_$var=\\\"\`\$echo \"X\$$var\" | \$Xsed -e \"\$sed_quote_subst\"\`\\\"" - ;; - esac - done - - case $lt_echo in - *'\$0 --fallback-echo"') - lt_echo=`$echo "X$lt_echo" | $Xsed -e 's/\\\\\\\$0 --fallback-echo"$/$0 --fallback-echo"/'` - ;; - esac - -cfgfile="$ofile" - - cat <<__EOF__ >> "$cfgfile" -# ### BEGIN LIBTOOL TAG CONFIG: $tagname - -# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`: - -# Shell to use when invoking shell scripts. -SHELL=$lt_SHELL - -# Whether or not to build shared libraries. -build_libtool_libs=$enable_shared - -# Whether or not to build static libraries. -build_old_libs=$enable_static - -# Whether or not to add -lc for building shared libraries. -build_libtool_need_lc=$archive_cmds_need_lc_RC - -# Whether or not to disallow shared libs when runtime libs are static -allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes_RC - -# Whether or not to optimize for fast installation. -fast_install=$enable_fast_install - -# The host system. -host_alias=$host_alias -host=$host -host_os=$host_os - -# The build system. -build_alias=$build_alias -build=$build -build_os=$build_os - -# An echo program that does not interpret backslashes. -echo=$lt_echo - -# The archiver. -AR=$lt_AR -AR_FLAGS=$lt_AR_FLAGS - -# A C compiler. -LTCC=$lt_LTCC - -# LTCC compiler flags. -LTCFLAGS=$lt_LTCFLAGS - -# A language-specific compiler. -CC=$lt_compiler_RC - -# Is the compiler the GNU C compiler? -with_gcc=$GCC_RC - -# An ERE matcher. -EGREP=$lt_EGREP - -# The linker used to build libraries. -LD=$lt_LD_RC - -# Whether we need hard or soft links. -LN_S=$lt_LN_S - -# A BSD-compatible nm program. -NM=$lt_NM - -# A symbol stripping program -STRIP=$lt_STRIP - -# Used to examine libraries when file_magic_cmd begins "file" -MAGIC_CMD=$MAGIC_CMD - -# Used on cygwin: DLL creation program. -DLLTOOL="$DLLTOOL" - -# Used on cygwin: object dumper. -OBJDUMP="$OBJDUMP" - -# Used on cygwin: assembler. -AS="$AS" - -# The name of the directory that contains temporary libtool files. -objdir=$objdir - -# How to create reloadable object files. -reload_flag=$lt_reload_flag -reload_cmds=$lt_reload_cmds - -# How to pass a linker flag through the compiler. -wl=$lt_lt_prog_compiler_wl_RC - -# Object file suffix (normally "o"). -objext="$ac_objext" - -# Old archive suffix (normally "a"). -libext="$libext" - -# Shared library suffix (normally ".so"). -shrext_cmds='$shrext_cmds' - -# Executable file suffix (normally ""). -exeext="$exeext" - -# Additional compiler flags for building library objects. -pic_flag=$lt_lt_prog_compiler_pic_RC -pic_mode=$pic_mode - -# What is the maximum length of a command? -max_cmd_len=$lt_cv_sys_max_cmd_len - -# Does compiler simultaneously support -c and -o options? -compiler_c_o=$lt_lt_cv_prog_compiler_c_o_RC - -# Must we lock files when doing compilation? -need_locks=$lt_need_locks - -# Do we need the lib prefix for modules? -need_lib_prefix=$need_lib_prefix - -# Do we need a version for libraries? -need_version=$need_version - -# Whether dlopen is supported. -dlopen_support=$enable_dlopen - -# Whether dlopen of programs is supported. -dlopen_self=$enable_dlopen_self - -# Whether dlopen of statically linked programs is supported. -dlopen_self_static=$enable_dlopen_self_static - -# Compiler flag to prevent dynamic linking. -link_static_flag=$lt_lt_prog_compiler_static_RC - -# Compiler flag to turn off builtin functions. -no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag_RC - -# Compiler flag to allow reflexive dlopens. -export_dynamic_flag_spec=$lt_export_dynamic_flag_spec_RC - -# Compiler flag to generate shared objects directly from archives. -whole_archive_flag_spec=$lt_whole_archive_flag_spec_RC - -# Compiler flag to generate thread-safe objects. -thread_safe_flag_spec=$lt_thread_safe_flag_spec_RC - -# Library versioning type. -version_type=$version_type - -# Format of library name prefix. -libname_spec=$lt_libname_spec - -# List of archive names. First name is the real one, the rest are links. -# The last name is the one that the linker finds with -lNAME. -library_names_spec=$lt_library_names_spec - -# The coded name of the library, if different from the real name. -soname_spec=$lt_soname_spec - -# Commands used to build and install an old-style archive. -RANLIB=$lt_RANLIB -old_archive_cmds=$lt_old_archive_cmds_RC -old_postinstall_cmds=$lt_old_postinstall_cmds -old_postuninstall_cmds=$lt_old_postuninstall_cmds - -# Create an old-style archive from a shared archive. -old_archive_from_new_cmds=$lt_old_archive_from_new_cmds_RC - -# Create a temporary old-style archive to link instead of a shared archive. -old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds_RC - -# Commands used to build and install a shared archive. -archive_cmds=$lt_archive_cmds_RC -archive_expsym_cmds=$lt_archive_expsym_cmds_RC -postinstall_cmds=$lt_postinstall_cmds -postuninstall_cmds=$lt_postuninstall_cmds - -# Commands used to build a loadable module (assumed same as above if empty) -module_cmds=$lt_module_cmds_RC -module_expsym_cmds=$lt_module_expsym_cmds_RC - -# Commands to strip libraries. -old_striplib=$lt_old_striplib -striplib=$lt_striplib - -# Dependencies to place before the objects being linked to create a -# shared library. -predep_objects=$lt_predep_objects_RC - -# Dependencies to place after the objects being linked to create a -# shared library. -postdep_objects=$lt_postdep_objects_RC - -# Dependencies to place before the objects being linked to create a -# shared library. -predeps=$lt_predeps_RC - -# Dependencies to place after the objects being linked to create a -# shared library. -postdeps=$lt_postdeps_RC - -# The library search path used internally by the compiler when linking -# a shared library. -compiler_lib_search_path=$lt_compiler_lib_search_path_RC - -# Method to check whether dependent libraries are shared objects. -deplibs_check_method=$lt_deplibs_check_method - -# Command to use when deplibs_check_method == file_magic. -file_magic_cmd=$lt_file_magic_cmd - -# Flag that allows shared libraries with undefined symbols to be built. -allow_undefined_flag=$lt_allow_undefined_flag_RC - -# Flag that forces no undefined symbols. -no_undefined_flag=$lt_no_undefined_flag_RC - -# Commands used to finish a libtool library installation in a directory. -finish_cmds=$lt_finish_cmds - -# Same as above, but a single script fragment to be evaled but not shown. -finish_eval=$lt_finish_eval - -# Take the output of nm and produce a listing of raw symbols and C names. -global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe - -# Transform the output of nm in a proper C declaration -global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl - -# Transform the output of nm in a C name address pair -global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address - -# This is the shared library runtime path variable. -runpath_var=$runpath_var - -# This is the shared library path variable. -shlibpath_var=$shlibpath_var - -# Is shlibpath searched before the hard-coded library search path? -shlibpath_overrides_runpath=$shlibpath_overrides_runpath - -# How to hardcode a shared library path into an executable. -hardcode_action=$hardcode_action_RC - -# Whether we should hardcode library paths into libraries. -hardcode_into_libs=$hardcode_into_libs - -# Flag to hardcode \$libdir into a binary during linking. -# This must work even if \$libdir does not exist. -hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec_RC - -# If ld is used when linking, flag to hardcode \$libdir into -# a binary during linking. This must work even if \$libdir does -# not exist. -hardcode_libdir_flag_spec_ld=$lt_hardcode_libdir_flag_spec_ld_RC - -# Whether we need a single -rpath flag with a separated argument. -hardcode_libdir_separator=$lt_hardcode_libdir_separator_RC - -# Set to yes if using DIR/libNAME${shared_ext} during linking hardcodes DIR into the -# resulting binary. -hardcode_direct=$hardcode_direct_RC - -# Set to yes if using the -LDIR flag during linking hardcodes DIR into the -# resulting binary. -hardcode_minus_L=$hardcode_minus_L_RC - -# Set to yes if using SHLIBPATH_VAR=DIR during linking hardcodes DIR into -# the resulting binary. -hardcode_shlibpath_var=$hardcode_shlibpath_var_RC - -# Set to yes if building a shared library automatically hardcodes DIR into the library -# and all subsequent libraries and executables linked against it. -hardcode_automatic=$hardcode_automatic_RC - -# Variables whose values should be saved in libtool wrapper scripts and -# restored at relink time. -variables_saved_for_relink="$variables_saved_for_relink" - -# Whether libtool must link a program against all its dependency libraries. -link_all_deplibs=$link_all_deplibs_RC - -# Compile-time system search path for libraries -sys_lib_search_path_spec=$lt_sys_lib_search_path_spec - -# Run-time system search path for libraries -sys_lib_dlsearch_path_spec=$lt_sys_lib_dlsearch_path_spec - -# Fix the shell variable \$srcfile for the compiler. -fix_srcfile_path="$fix_srcfile_path_RC" - -# Set to yes if exported symbols are required. -always_export_symbols=$always_export_symbols_RC - -# The commands to list exported symbols. -export_symbols_cmds=$lt_export_symbols_cmds_RC - -# The commands to extract the exported symbol list from a shared archive. -extract_expsyms_cmds=$lt_extract_expsyms_cmds - -# Symbols that should not be listed in the preloaded symbols. -exclude_expsyms=$lt_exclude_expsyms_RC - -# Symbols that must always be exported. -include_expsyms=$lt_include_expsyms_RC - -# ### END LIBTOOL TAG CONFIG: $tagname - -__EOF__ - - -else - # If there is no Makefile yet, we rely on a make rule to execute - # `config.status --recheck' to rerun these tests and create the - # libtool script then. - ltmain_in=`echo $ltmain | sed -e 's/\.sh$/.in/'` - if test -f "$ltmain_in"; then - test -f Makefile && make "$ltmain" - fi -fi - - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - -CC="$lt_save_CC" - - ;; - - *) - { { echo "$as_me:$LINENO: error: Unsupported tag name: $tagname" >&5 -echo "$as_me: error: Unsupported tag name: $tagname" >&2;} - { (exit 1); exit 1; }; } - ;; - esac - - # Append the new tag name to the list of available tags. - if test -n "$tagname" ; then - available_tags="$available_tags $tagname" - fi - fi - done - IFS="$lt_save_ifs" - - # Now substitute the updated list of available tags. - if eval "sed -e 's/^available_tags=.*\$/available_tags=\"$available_tags\"/' \"$ofile\" > \"${ofile}T\""; then - mv "${ofile}T" "$ofile" - chmod +x "$ofile" - else - rm -f "${ofile}T" - { { echo "$as_me:$LINENO: error: unable to update list of available tagged configurations." >&5 -echo "$as_me: error: unable to update list of available tagged configurations." >&2;} - { (exit 1); exit 1; }; } - fi -fi - - - -# This can be used to rebuild libtool when needed -LIBTOOL_DEPS="$ac_aux_dir/ltmain.sh" - -# Always use our own libtool. -LIBTOOL='$(SHELL) $(top_builddir)/libtool' - -# Prevent multiple expansion - - - - - - - - - - - - - - - - - - - - - - - - - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu -if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. -set dummy ${ac_tool_prefix}gcc; ac_word=$2 -echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 -if test "${ac_cv_prog_CC+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_CC="${ac_tool_prefix}gcc" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done -done - -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - echo "$as_me:$LINENO: result: $CC" >&5 -echo "${ECHO_T}$CC" >&6 -else - echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6 -fi - -fi -if test -z "$ac_cv_prog_CC"; then - ac_ct_CC=$CC - # Extract the first word of "gcc", so it can be a program name with args. -set dummy gcc; ac_word=$2 -echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 -if test "${ac_cv_prog_ac_ct_CC+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test -n "$ac_ct_CC"; then - ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_CC="gcc" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done -done - -fi -fi -ac_ct_CC=$ac_cv_prog_ac_ct_CC -if test -n "$ac_ct_CC"; then - echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 -echo "${ECHO_T}$ac_ct_CC" >&6 -else - echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6 -fi - - CC=$ac_ct_CC -else - CC="$ac_cv_prog_CC" -fi - -if test -z "$CC"; then - if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. -set dummy ${ac_tool_prefix}cc; ac_word=$2 -echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 -if test "${ac_cv_prog_CC+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_CC="${ac_tool_prefix}cc" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done -done - -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - echo "$as_me:$LINENO: result: $CC" >&5 -echo "${ECHO_T}$CC" >&6 -else - echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6 -fi - -fi -if test -z "$ac_cv_prog_CC"; then - ac_ct_CC=$CC - # Extract the first word of "cc", so it can be a program name with args. -set dummy cc; ac_word=$2 -echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 -if test "${ac_cv_prog_ac_ct_CC+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test -n "$ac_ct_CC"; then - ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_CC="cc" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done -done - -fi -fi -ac_ct_CC=$ac_cv_prog_ac_ct_CC -if test -n "$ac_ct_CC"; then - echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 -echo "${ECHO_T}$ac_ct_CC" >&6 -else - echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6 -fi - - CC=$ac_ct_CC -else - CC="$ac_cv_prog_CC" -fi - -fi -if test -z "$CC"; then - # Extract the first word of "cc", so it can be a program name with args. -set dummy cc; ac_word=$2 -echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 -if test "${ac_cv_prog_CC+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else - ac_prog_rejected=no -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then - ac_prog_rejected=yes - continue - fi - ac_cv_prog_CC="cc" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done -done - -if test $ac_prog_rejected = yes; then - # We found a bogon in the path, so make sure we never use it. - set dummy $ac_cv_prog_CC - shift - if test $# != 0; then - # We chose a different compiler from the bogus one. - # However, it has the same basename, so the bogon will be chosen - # first if we set CC to just the basename; use the full file name. - shift - ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" - fi -fi -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - echo "$as_me:$LINENO: result: $CC" >&5 -echo "${ECHO_T}$CC" >&6 -else - echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6 -fi - -fi -if test -z "$CC"; then - if test -n "$ac_tool_prefix"; then - for ac_prog in cl - do - # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. -set dummy $ac_tool_prefix$ac_prog; ac_word=$2 -echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 -if test "${ac_cv_prog_CC+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_CC="$ac_tool_prefix$ac_prog" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done -done - -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - echo "$as_me:$LINENO: result: $CC" >&5 -echo "${ECHO_T}$CC" >&6 -else - echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6 -fi - - test -n "$CC" && break - done -fi -if test -z "$CC"; then - ac_ct_CC=$CC - for ac_prog in cl -do - # Extract the first word of "$ac_prog", so it can be a program name with args. -set dummy $ac_prog; ac_word=$2 -echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 -if test "${ac_cv_prog_ac_ct_CC+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test -n "$ac_ct_CC"; then - ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_CC="$ac_prog" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done -done - -fi -fi -ac_ct_CC=$ac_cv_prog_ac_ct_CC -if test -n "$ac_ct_CC"; then - echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 -echo "${ECHO_T}$ac_ct_CC" >&6 -else - echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6 -fi - - test -n "$ac_ct_CC" && break -done - - CC=$ac_ct_CC -fi - -fi - - -test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH -See \`config.log' for more details." >&5 -echo "$as_me: error: no acceptable C compiler found in \$PATH -See \`config.log' for more details." >&2;} - { (exit 1); exit 1; }; } - -# Provide some information about the compiler. -echo "$as_me:$LINENO:" \ - "checking for C compiler version" >&5 -ac_compiler=`set X $ac_compile; echo $2` -{ (eval echo "$as_me:$LINENO: \"$ac_compiler --version &5\"") >&5 - (eval $ac_compiler --version &5) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } -{ (eval echo "$as_me:$LINENO: \"$ac_compiler -v &5\"") >&5 - (eval $ac_compiler -v &5) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } -{ (eval echo "$as_me:$LINENO: \"$ac_compiler -V &5\"") >&5 - (eval $ac_compiler -V &5) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } - -echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5 -echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6 -if test "${ac_cv_c_compiler_gnu+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ - -int -main () -{ -#ifndef __GNUC__ - choke me -#endif - - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext -if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest.$ac_objext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_compiler_gnu=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -ac_compiler_gnu=no -fi -rm -f conftest.err conftest.$ac_objext conftest.$ac_ext -ac_cv_c_compiler_gnu=$ac_compiler_gnu - -fi -echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5 -echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6 -GCC=`test $ac_compiler_gnu = yes && echo yes` -ac_test_CFLAGS=${CFLAGS+set} -ac_save_CFLAGS=$CFLAGS -CFLAGS="-g" -echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5 -echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6 -if test "${ac_cv_prog_cc_g+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext -if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest.$ac_objext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_cv_prog_cc_g=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -ac_cv_prog_cc_g=no -fi -rm -f conftest.err conftest.$ac_objext conftest.$ac_ext -fi -echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5 -echo "${ECHO_T}$ac_cv_prog_cc_g" >&6 -if test "$ac_test_CFLAGS" = set; then - CFLAGS=$ac_save_CFLAGS -elif test $ac_cv_prog_cc_g = yes; then - if test "$GCC" = yes; then - CFLAGS="-g -O2" - else - CFLAGS="-g" - fi -else - if test "$GCC" = yes; then - CFLAGS="-O2" - else - CFLAGS= - fi -fi -echo "$as_me:$LINENO: checking for $CC option to accept ANSI C" >&5 -echo $ECHO_N "checking for $CC option to accept ANSI C... $ECHO_C" >&6 -if test "${ac_cv_prog_cc_stdc+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - ac_cv_prog_cc_stdc=no -ac_save_CC=$CC -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include -#include -#include -#include -/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ -struct buf { int x; }; -FILE * (*rcsopen) (struct buf *, struct stat *, int); -static char *e (p, i) - char **p; - int i; -{ - return p[i]; -} -static char *f (char * (*g) (char **, int), char **p, ...) -{ - char *s; - va_list v; - va_start (v,p); - s = g (p, va_arg (v,int)); - va_end (v); - return s; -} - -/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has - function prototypes and stuff, but not '\xHH' hex character constants. - These don't provoke an error unfortunately, instead are silently treated - as 'x'. The following induces an error, until -std1 is added to get - proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an - array size at least. It's necessary to write '\x00'==0 to get something - that's true only with -std1. */ -int osf4_cc_array ['\x00' == 0 ? 1 : -1]; - -int test (int i, double x); -struct s1 {int (*f) (int a);}; -struct s2 {int (*f) (double a);}; -int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); -int argc; -char **argv; -int -main () -{ -return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; - ; - return 0; -} -_ACEOF -# Don't try gcc -ansi; that turns off useful extensions and -# breaks some systems' header files. -# AIX -qlanglvl=ansi -# Ultrix and OSF/1 -std1 -# HP-UX 10.20 and later -Ae -# HP-UX older versions -Aa -D_HPUX_SOURCE -# SVR4 -Xc -D__EXTENSIONS__ -for ac_arg in "" -qlanglvl=ansi -std1 -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" -do - CC="$ac_save_CC $ac_arg" - rm -f conftest.$ac_objext -if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest.$ac_objext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_cv_prog_cc_stdc=$ac_arg -break -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -fi -rm -f conftest.err conftest.$ac_objext -done -rm -f conftest.$ac_ext conftest.$ac_objext -CC=$ac_save_CC - -fi - -case "x$ac_cv_prog_cc_stdc" in - x|xno) - echo "$as_me:$LINENO: result: none needed" >&5 -echo "${ECHO_T}none needed" >&6 ;; - *) - echo "$as_me:$LINENO: result: $ac_cv_prog_cc_stdc" >&5 -echo "${ECHO_T}$ac_cv_prog_cc_stdc" >&6 - CC="$CC $ac_cv_prog_cc_stdc" ;; -esac - -# Some people use a C++ compiler to compile C. Since we use `exit', -# in C++ we need to declare it. In case someone uses the same compiler -# for both compiling C and C++ we need to have the C++ compiler decide -# the declaration of exit, since it's the most demanding environment. -cat >conftest.$ac_ext <<_ACEOF -#ifndef __cplusplus - choke me -#endif -_ACEOF -rm -f conftest.$ac_objext -if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest.$ac_objext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - for ac_declaration in \ - '' \ - 'extern "C" void std::exit (int) throw (); using std::exit;' \ - 'extern "C" void std::exit (int); using std::exit;' \ - 'extern "C" void exit (int) throw ();' \ - 'extern "C" void exit (int);' \ - 'void exit (int);' -do - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -$ac_declaration -#include -int -main () -{ -exit (42); - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext -if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest.$ac_objext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - : -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -continue -fi -rm -f conftest.err conftest.$ac_objext conftest.$ac_ext - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -$ac_declaration -int -main () -{ -exit (42); - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext -if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest.$ac_objext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - break -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -fi -rm -f conftest.err conftest.$ac_objext conftest.$ac_ext -done -rm -f conftest* -if test -n "$ac_declaration"; then - echo '#ifdef __cplusplus' >>confdefs.h - echo $ac_declaration >>confdefs.h - echo '#endif' >>confdefs.h -fi - -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -fi -rm -f conftest.err conftest.$ac_objext conftest.$ac_ext -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - -# Find a good install program. We prefer a C program (faster), -# so one script is as good as another. But avoid the broken or -# incompatible versions: -# SysV /etc/install, /usr/sbin/install -# SunOS /usr/etc/install -# IRIX /sbin/install -# AIX /bin/install -# AmigaOS /C/install, which installs bootblocks on floppy discs -# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag -# AFS /usr/afsws/bin/install, which mishandles nonexistent args -# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" -# OS/2's system install, which has a completely different semantic -# ./install, which can be erroneously created by make from ./install.sh. -echo "$as_me:$LINENO: checking for a BSD-compatible install" >&5 -echo $ECHO_N "checking for a BSD-compatible install... $ECHO_C" >&6 -if test -z "$INSTALL"; then -if test "${ac_cv_path_install+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - # Account for people who put trailing slashes in PATH elements. -case $as_dir/ in - ./ | .// | /cC/* | \ - /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ - ?:\\/os2\\/install\\/* | ?:\\/OS2\\/INSTALL\\/* | \ - /usr/ucb/* ) ;; - *) - # OSF1 and SCO ODT 3.0 have their own names for install. - # Don't use installbsd from OSF since it installs stuff as root - # by default. - for ac_prog in ginstall scoinst install; do - for ac_exec_ext in '' $ac_executable_extensions; do - if $as_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then - if test $ac_prog = install && - grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then - # AIX install. It has an incompatible calling convention. - : - elif test $ac_prog = install && - grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then - # program-specific install script used by HP pwplus--don't use. - : - else - ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" - break 3 - fi - fi - done - done - ;; -esac -done - - -fi - if test "${ac_cv_path_install+set}" = set; then - INSTALL=$ac_cv_path_install - else - # As a last resort, use the slow shell script. We don't cache a - # path for INSTALL within a source directory, because that will - # break other packages using the cache if that directory is - # removed, or if the path is relative. - INSTALL=$ac_install_sh - fi -fi -echo "$as_me:$LINENO: result: $INSTALL" >&5 -echo "${ECHO_T}$INSTALL" >&6 - -# Use test -z because SunOS4 sh mishandles braces in ${var-val}. -# It thinks the first close brace ends the variable substitution. -test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' - -test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' - -test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' - - -if test "$GCC" = yes ; then - OLDCFLAGS="$CFLAGS -Wall -Wmissing-prototypes -Wstrict-prototypes" - CFLAGS="$OLDCFLAGS -fexceptions" - echo "$as_me:$LINENO: checking whether $CC accepts -fexceptions" >&5 -echo $ECHO_N "checking whether $CC accepts -fexceptions... $ECHO_C" >&6 - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 - (eval $ac_link) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest$ac_exeext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - echo "$as_me:$LINENO: result: yes" >&5 -echo "${ECHO_T}yes" >&6 -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6; CFLAGS="$OLDCFLAGS" -fi -rm -f conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext - CXXFLAGS=`echo "$CFLAGS" | sed 's/ -Wmissing-prototypes -Wstrict-prototypes//'` -fi - -echo "$as_me:$LINENO: checking for ANSI C header files" >&5 -echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6 -if test "${ac_cv_header_stdc+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include -#include -#include -#include - -int -main () -{ - - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext -if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest.$ac_objext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_cv_header_stdc=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -ac_cv_header_stdc=no -fi -rm -f conftest.err conftest.$ac_objext conftest.$ac_ext - -if test $ac_cv_header_stdc = yes; then - # SunOS 4.x string.h does not declare mem*, contrary to ANSI. - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include - -_ACEOF -if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | - $EGREP "memchr" >/dev/null 2>&1; then - : -else - ac_cv_header_stdc=no -fi -rm -f conftest* - -fi - -if test $ac_cv_header_stdc = yes; then - # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include - -_ACEOF -if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | - $EGREP "free" >/dev/null 2>&1; then - : -else - ac_cv_header_stdc=no -fi -rm -f conftest* - -fi - -if test $ac_cv_header_stdc = yes; then - # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. - if test "$cross_compiling" = yes; then - : -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include -#if ((' ' & 0x0FF) == 0x020) -# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') -# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) -#else -# define ISLOWER(c) \ - (('a' <= (c) && (c) <= 'i') \ - || ('j' <= (c) && (c) <= 'r') \ - || ('s' <= (c) && (c) <= 'z')) -# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) -#endif - -#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) -int -main () -{ - int i; - for (i = 0; i < 256; i++) - if (XOR (islower (i), ISLOWER (i)) - || toupper (i) != TOUPPER (i)) - exit(2); - exit (0); -} -_ACEOF -rm -f conftest$ac_exeext -if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 - (eval $ac_link) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { ac_try='./conftest$ac_exeext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - : -else - echo "$as_me: program exited with status $ac_status" >&5 -echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -( exit $ac_status ) -ac_cv_header_stdc=no -fi -rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext -fi -fi -fi -echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5 -echo "${ECHO_T}$ac_cv_header_stdc" >&6 -if test $ac_cv_header_stdc = yes; then - -cat >>confdefs.h <<\_ACEOF -#define STDC_HEADERS 1 -_ACEOF - -fi - - - -echo "$as_me:$LINENO: checking whether byte ordering is bigendian" >&5 -echo $ECHO_N "checking whether byte ordering is bigendian... $ECHO_C" >&6 -if test "${ac_cv_c_bigendian+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - ac_cv_c_bigendian=unknown -# See if sys/param.h defines the BYTE_ORDER macro. -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include -#include -int -main () -{ - -#if !BYTE_ORDER || !BIG_ENDIAN || !LITTLE_ENDIAN - bogus endian macros -#endif - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext -if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest.$ac_objext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - # It does; now see whether it defined to BIG_ENDIAN or not. -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include -#include -int -main () -{ - -#if BYTE_ORDER != BIG_ENDIAN - not big endian -#endif - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext -if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest.$ac_objext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_cv_c_bigendian=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -ac_cv_c_bigendian=no -fi -rm -f conftest.err conftest.$ac_objext conftest.$ac_ext -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -fi -rm -f conftest.err conftest.$ac_objext conftest.$ac_ext -if test $ac_cv_c_bigendian = unknown; then -if test "$cross_compiling" = yes; then - echo $ac_n "cross-compiling... " 2>&6 -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -main () { - /* Are we little or big endian? From Harbison&Steele. */ - union - { - long l; - char c[sizeof (long)]; - } u; - u.l = 1; - exit (u.c[sizeof (long) - 1] == 1); -} -_ACEOF -rm -f conftest$ac_exeext -if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 - (eval $ac_link) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { ac_try='./conftest$ac_exeext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_cv_c_bigendian=no -else - echo "$as_me: program exited with status $ac_status" >&5 -echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -( exit $ac_status ) -ac_cv_c_bigendian=yes -fi -rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext -fi -fi -fi -echo "$as_me:$LINENO: result: $ac_cv_c_bigendian" >&5 -echo "${ECHO_T}$ac_cv_c_bigendian" >&6 -if test $ac_cv_c_bigendian = unknown; then -echo "$as_me:$LINENO: checking to probe for byte ordering" >&5 -echo $ECHO_N "checking to probe for byte ordering... $ECHO_C" >&6 - -cat >conftest.c <&6 - ac_cv_c_bigendian=yes - fi - if test `grep -l LiTTleEnDian conftest.o` ; then - echo $ac_n ' little endian probe OK, ' 1>&6 - if test $ac_cv_c_bigendian = yes ; then - ac_cv_c_bigendian=unknown; - else - ac_cv_c_bigendian=no - fi - fi - echo $ac_n 'guessing bigendian ... ' >&6 - fi - fi -echo "$as_me:$LINENO: result: $ac_cv_c_bigendian" >&5 -echo "${ECHO_T}$ac_cv_c_bigendian" >&6 -fi -if test $ac_cv_c_bigendian = yes; then - -cat >>confdefs.h <<\_ACEOF -#define WORDS_BIGENDIAN 1 -_ACEOF - - BYTEORDER=4321 -else - BYTEORDER=1234 -fi - -cat >>confdefs.h <<_ACEOF -#define BYTEORDER $BYTEORDER -_ACEOF - -if test $ac_cv_c_bigendian = unknown; then - { { echo "$as_me:$LINENO: error: unknown endianess - sorry" >&5 -echo "$as_me: error: unknown endianess - sorry" >&2;} - { (exit please pre-set ac_cv_c_bigendian); exit please pre-set ac_cv_c_bigendian; }; } -fi - - -echo "$as_me:$LINENO: checking for an ANSI C-conforming const" >&5 -echo $ECHO_N "checking for an ANSI C-conforming const... $ECHO_C" >&6 -if test "${ac_cv_c_const+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ - -int -main () -{ -/* FIXME: Include the comments suggested by Paul. */ -#ifndef __cplusplus - /* Ultrix mips cc rejects this. */ - typedef int charset[2]; - const charset x; - /* SunOS 4.1.1 cc rejects this. */ - char const *const *ccp; - char **p; - /* NEC SVR4.0.2 mips cc rejects this. */ - struct point {int x, y;}; - static struct point const zero = {0,0}; - /* AIX XL C 1.02.0.0 rejects this. - It does not let you subtract one const X* pointer from another in - an arm of an if-expression whose if-part is not a constant - expression */ - const char *g = "string"; - ccp = &g + (g ? g-g : 0); - /* HPUX 7.0 cc rejects these. */ - ++ccp; - p = (char**) ccp; - ccp = (char const *const *) p; - { /* SCO 3.2v4 cc rejects this. */ - char *t; - char const *s = 0 ? (char *) 0 : (char const *) 0; - - *t++ = 0; - } - { /* Someone thinks the Sun supposedly-ANSI compiler will reject this. */ - int x[] = {25, 17}; - const int *foo = &x[0]; - ++foo; - } - { /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */ - typedef const int *iptr; - iptr p = 0; - ++p; - } - { /* AIX XL C 1.02.0.0 rejects this saying - "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */ - struct s { int j; const int *ap[3]; }; - struct s *b; b->j = 5; - } - { /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */ - const int foo = 10; - } -#endif - - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext -if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest.$ac_objext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_cv_c_const=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -ac_cv_c_const=no -fi -rm -f conftest.err conftest.$ac_objext conftest.$ac_ext -fi -echo "$as_me:$LINENO: result: $ac_cv_c_const" >&5 -echo "${ECHO_T}$ac_cv_c_const" >&6 -if test $ac_cv_c_const = no; then - -cat >>confdefs.h <<\_ACEOF -#define const -_ACEOF - -fi - -echo "$as_me:$LINENO: checking for size_t" >&5 -echo $ECHO_N "checking for size_t... $ECHO_C" >&6 -if test "${ac_cv_type_size_t+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -$ac_includes_default -int -main () -{ -if ((size_t *) 0) - return 0; -if (sizeof (size_t)) - return 0; - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext -if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest.$ac_objext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_cv_type_size_t=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -ac_cv_type_size_t=no -fi -rm -f conftest.err conftest.$ac_objext conftest.$ac_ext -fi -echo "$as_me:$LINENO: result: $ac_cv_type_size_t" >&5 -echo "${ECHO_T}$ac_cv_type_size_t" >&6 -if test $ac_cv_type_size_t = yes; then - : -else - -cat >>confdefs.h <<_ACEOF -#define size_t unsigned -_ACEOF - -fi - - - -for ac_func in memmove bcopy -do -as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` -echo "$as_me:$LINENO: checking for $ac_func" >&5 -echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 -if eval "test \"\${$as_ac_var+set}\" = set"; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -/* Define $ac_func to an innocuous variant, in case declares $ac_func. - For example, HP-UX 11i declares gettimeofday. */ -#define $ac_func innocuous_$ac_func - -/* System header to define __stub macros and hopefully few prototypes, - which can conflict with char $ac_func (); below. - Prefer to if __STDC__ is defined, since - exists even on freestanding compilers. */ - -#ifdef __STDC__ -# include -#else -# include -#endif - -#undef $ac_func - -/* Override any gcc2 internal prototype to avoid an error. */ -#ifdef __cplusplus -extern "C" -{ -#endif -/* We use char because int might match the return type of a gcc2 - builtin and then its argument prototype would still apply. */ -char $ac_func (); -/* The GNU C library defines this for functions which it implements - to always fail with ENOSYS. Some functions are actually named - something starting with __ and the normal name is an alias. */ -#if defined (__stub_$ac_func) || defined (__stub___$ac_func) -choke me -#else -char (*f) () = $ac_func; -#endif -#ifdef __cplusplus -} -#endif - -int -main () -{ -return f != $ac_func; - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 - (eval $ac_link) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest$ac_exeext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - eval "$as_ac_var=yes" -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -eval "$as_ac_var=no" -fi -rm -f conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -fi -echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 -echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 -if test `eval echo '${'$as_ac_var'}'` = yes; then - cat >>confdefs.h <<_ACEOF -#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 -_ACEOF - -fi -done - - - - -for ac_header in fcntl.h unistd.h -do -as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` -if eval "test \"\${$as_ac_Header+set}\" = set"; then - echo "$as_me:$LINENO: checking for $ac_header" >&5 -echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 -if eval "test \"\${$as_ac_Header+set}\" = set"; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -fi -echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 -echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 -else - # Is the header compilable? -echo "$as_me:$LINENO: checking $ac_header usability" >&5 -echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -$ac_includes_default -#include <$ac_header> -_ACEOF -rm -f conftest.$ac_objext -if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest.$ac_objext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_header_compiler=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -ac_header_compiler=no -fi -rm -f conftest.err conftest.$ac_objext conftest.$ac_ext -echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 -echo "${ECHO_T}$ac_header_compiler" >&6 - -# Is the header present? -echo "$as_me:$LINENO: checking $ac_header presence" >&5 -echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include <$ac_header> -_ACEOF -if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 - (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } >/dev/null; then - if test -s conftest.err; then - ac_cpp_err=$ac_c_preproc_warn_flag - ac_cpp_err=$ac_cpp_err$ac_c_werror_flag - else - ac_cpp_err= - fi -else - ac_cpp_err=yes -fi -if test -z "$ac_cpp_err"; then - ac_header_preproc=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_header_preproc=no -fi -rm -f conftest.err conftest.$ac_ext -echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 -echo "${ECHO_T}$ac_header_preproc" >&6 - -# So? What about this header? -case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in - yes:no: ) - { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 -echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 -echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} - ac_header_preproc=yes - ;; - no:yes:* ) - { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 -echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 -echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 -echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 -echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 -echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 -echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} - ( - cat <<\_ASBOX -## -------------------------------------- ## -## Report this to expat-bugs@libexpat.org ## -## -------------------------------------- ## -_ASBOX - ) | - sed "s/^/$as_me: WARNING: /" >&2 - ;; -esac -echo "$as_me:$LINENO: checking for $ac_header" >&5 -echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 -if eval "test \"\${$as_ac_Header+set}\" = set"; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - eval "$as_ac_Header=\$ac_header_preproc" -fi -echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 -echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 - -fi -if test `eval echo '${'$as_ac_Header'}'` = yes; then - cat >>confdefs.h <<_ACEOF -#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 -_ACEOF - -fi - -done - -echo "$as_me:$LINENO: checking for off_t" >&5 -echo $ECHO_N "checking for off_t... $ECHO_C" >&6 -if test "${ac_cv_type_off_t+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -$ac_includes_default -int -main () -{ -if ((off_t *) 0) - return 0; -if (sizeof (off_t)) - return 0; - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext -if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest.$ac_objext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_cv_type_off_t=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -ac_cv_type_off_t=no -fi -rm -f conftest.err conftest.$ac_objext conftest.$ac_ext -fi -echo "$as_me:$LINENO: result: $ac_cv_type_off_t" >&5 -echo "${ECHO_T}$ac_cv_type_off_t" >&6 -if test $ac_cv_type_off_t = yes; then - : -else - -cat >>confdefs.h <<_ACEOF -#define off_t long -_ACEOF - -fi - - - -for ac_header in stdlib.h unistd.h -do -as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` -if eval "test \"\${$as_ac_Header+set}\" = set"; then - echo "$as_me:$LINENO: checking for $ac_header" >&5 -echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 -if eval "test \"\${$as_ac_Header+set}\" = set"; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -fi -echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 -echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 -else - # Is the header compilable? -echo "$as_me:$LINENO: checking $ac_header usability" >&5 -echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -$ac_includes_default -#include <$ac_header> -_ACEOF -rm -f conftest.$ac_objext -if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest.$ac_objext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_header_compiler=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -ac_header_compiler=no -fi -rm -f conftest.err conftest.$ac_objext conftest.$ac_ext -echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 -echo "${ECHO_T}$ac_header_compiler" >&6 - -# Is the header present? -echo "$as_me:$LINENO: checking $ac_header presence" >&5 -echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include <$ac_header> -_ACEOF -if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 - (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } >/dev/null; then - if test -s conftest.err; then - ac_cpp_err=$ac_c_preproc_warn_flag - ac_cpp_err=$ac_cpp_err$ac_c_werror_flag - else - ac_cpp_err= - fi -else - ac_cpp_err=yes -fi -if test -z "$ac_cpp_err"; then - ac_header_preproc=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_header_preproc=no -fi -rm -f conftest.err conftest.$ac_ext -echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 -echo "${ECHO_T}$ac_header_preproc" >&6 - -# So? What about this header? -case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in - yes:no: ) - { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 -echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 -echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} - ac_header_preproc=yes - ;; - no:yes:* ) - { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 -echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 -echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 -echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 -echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 -echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 -echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} - ( - cat <<\_ASBOX -## -------------------------------------- ## -## Report this to expat-bugs@libexpat.org ## -## -------------------------------------- ## -_ASBOX - ) | - sed "s/^/$as_me: WARNING: /" >&2 - ;; -esac -echo "$as_me:$LINENO: checking for $ac_header" >&5 -echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 -if eval "test \"\${$as_ac_Header+set}\" = set"; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - eval "$as_ac_Header=\$ac_header_preproc" -fi -echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 -echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 - -fi -if test `eval echo '${'$as_ac_Header'}'` = yes; then - cat >>confdefs.h <<_ACEOF -#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 -_ACEOF - -fi - -done - - -for ac_func in getpagesize -do -as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` -echo "$as_me:$LINENO: checking for $ac_func" >&5 -echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 -if eval "test \"\${$as_ac_var+set}\" = set"; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -/* Define $ac_func to an innocuous variant, in case declares $ac_func. - For example, HP-UX 11i declares gettimeofday. */ -#define $ac_func innocuous_$ac_func - -/* System header to define __stub macros and hopefully few prototypes, - which can conflict with char $ac_func (); below. - Prefer to if __STDC__ is defined, since - exists even on freestanding compilers. */ - -#ifdef __STDC__ -# include -#else -# include -#endif - -#undef $ac_func - -/* Override any gcc2 internal prototype to avoid an error. */ -#ifdef __cplusplus -extern "C" -{ -#endif -/* We use char because int might match the return type of a gcc2 - builtin and then its argument prototype would still apply. */ -char $ac_func (); -/* The GNU C library defines this for functions which it implements - to always fail with ENOSYS. Some functions are actually named - something starting with __ and the normal name is an alias. */ -#if defined (__stub_$ac_func) || defined (__stub___$ac_func) -choke me -#else -char (*f) () = $ac_func; -#endif -#ifdef __cplusplus -} -#endif - -int -main () -{ -return f != $ac_func; - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 - (eval $ac_link) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest$ac_exeext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - eval "$as_ac_var=yes" -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -eval "$as_ac_var=no" -fi -rm -f conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -fi -echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 -echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 -if test `eval echo '${'$as_ac_var'}'` = yes; then - cat >>confdefs.h <<_ACEOF -#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 -_ACEOF - -fi -done - -echo "$as_me:$LINENO: checking for working mmap" >&5 -echo $ECHO_N "checking for working mmap... $ECHO_C" >&6 -if test "${ac_cv_func_mmap_fixed_mapped+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test "$cross_compiling" = yes; then - ac_cv_func_mmap_fixed_mapped=no -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -$ac_includes_default -/* malloc might have been renamed as rpl_malloc. */ -#undef malloc - -/* Thanks to Mike Haertel and Jim Avera for this test. - Here is a matrix of mmap possibilities: - mmap private not fixed - mmap private fixed at somewhere currently unmapped - mmap private fixed at somewhere already mapped - mmap shared not fixed - mmap shared fixed at somewhere currently unmapped - mmap shared fixed at somewhere already mapped - For private mappings, we should verify that changes cannot be read() - back from the file, nor mmap's back from the file at a different - address. (There have been systems where private was not correctly - implemented like the infamous i386 svr4.0, and systems where the - VM page cache was not coherent with the file system buffer cache - like early versions of FreeBSD and possibly contemporary NetBSD.) - For shared mappings, we should conversely verify that changes get - propagated back to all the places they're supposed to be. - - Grep wants private fixed already mapped. - The main things grep needs to know about mmap are: - * does it exist and is it safe to write into the mmap'd area - * how to use it (BSD variants) */ - -#include -#include - -#if !STDC_HEADERS && !HAVE_STDLIB_H -char *malloc (); -#endif - -/* This mess was copied from the GNU getpagesize.h. */ -#if !HAVE_GETPAGESIZE -/* Assume that all systems that can run configure have sys/param.h. */ -# if !HAVE_SYS_PARAM_H -# define HAVE_SYS_PARAM_H 1 -# endif - -# ifdef _SC_PAGESIZE -# define getpagesize() sysconf(_SC_PAGESIZE) -# else /* no _SC_PAGESIZE */ -# if HAVE_SYS_PARAM_H -# include -# ifdef EXEC_PAGESIZE -# define getpagesize() EXEC_PAGESIZE -# else /* no EXEC_PAGESIZE */ -# ifdef NBPG -# define getpagesize() NBPG * CLSIZE -# ifndef CLSIZE -# define CLSIZE 1 -# endif /* no CLSIZE */ -# else /* no NBPG */ -# ifdef NBPC -# define getpagesize() NBPC -# else /* no NBPC */ -# ifdef PAGESIZE -# define getpagesize() PAGESIZE -# endif /* PAGESIZE */ -# endif /* no NBPC */ -# endif /* no NBPG */ -# endif /* no EXEC_PAGESIZE */ -# else /* no HAVE_SYS_PARAM_H */ -# define getpagesize() 8192 /* punt totally */ -# endif /* no HAVE_SYS_PARAM_H */ -# endif /* no _SC_PAGESIZE */ - -#endif /* no HAVE_GETPAGESIZE */ - -int -main () -{ - char *data, *data2, *data3; - int i, pagesize; - int fd; - - pagesize = getpagesize (); - - /* First, make a file with some known garbage in it. */ - data = (char *) malloc (pagesize); - if (!data) - exit (1); - for (i = 0; i < pagesize; ++i) - *(data + i) = rand (); - umask (0); - fd = creat ("conftest.mmap", 0600); - if (fd < 0) - exit (1); - if (write (fd, data, pagesize) != pagesize) - exit (1); - close (fd); - - /* Next, try to mmap the file at a fixed address which already has - something else allocated at it. If we can, also make sure that - we see the same garbage. */ - fd = open ("conftest.mmap", O_RDWR); - if (fd < 0) - exit (1); - data2 = (char *) malloc (2 * pagesize); - if (!data2) - exit (1); - data2 += (pagesize - ((long) data2 & (pagesize - 1))) & (pagesize - 1); - if (data2 != mmap (data2, pagesize, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_FIXED, fd, 0L)) - exit (1); - for (i = 0; i < pagesize; ++i) - if (*(data + i) != *(data2 + i)) - exit (1); - - /* Finally, make sure that changes to the mapped area do not - percolate back to the file as seen by read(). (This is a bug on - some variants of i386 svr4.0.) */ - for (i = 0; i < pagesize; ++i) - *(data2 + i) = *(data2 + i) + 1; - data3 = (char *) malloc (pagesize); - if (!data3) - exit (1); - if (read (fd, data3, pagesize) != pagesize) - exit (1); - for (i = 0; i < pagesize; ++i) - if (*(data + i) != *(data3 + i)) - exit (1); - close (fd); - exit (0); -} -_ACEOF -rm -f conftest$ac_exeext -if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 - (eval $ac_link) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { ac_try='./conftest$ac_exeext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_cv_func_mmap_fixed_mapped=yes -else - echo "$as_me: program exited with status $ac_status" >&5 -echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -( exit $ac_status ) -ac_cv_func_mmap_fixed_mapped=no -fi -rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext -fi -fi -echo "$as_me:$LINENO: result: $ac_cv_func_mmap_fixed_mapped" >&5 -echo "${ECHO_T}$ac_cv_func_mmap_fixed_mapped" >&6 -if test $ac_cv_func_mmap_fixed_mapped = yes; then - -cat >>confdefs.h <<\_ACEOF -#define HAVE_MMAP 1 -_ACEOF - -fi -rm -f conftest.mmap - - -if test "$ac_cv_func_mmap_fixed_mapped" = "yes"; then - FILEMAP=unixfilemap -else - FILEMAP=readfilemap -fi - - - -# AC_CPP_FUNC -# ------------------ # -# Checks to see if ANSI C99 CPP variable __func__ works. -# If not, perhaps __FUNCTION__ works instead. -# If not, we'll just define __func__ to "". -# AC_CPP_FUNC - - -echo "$as_me:$LINENO: checking for an ANSI C99-conforming __func__" >&5 -echo $ECHO_N "checking for an ANSI C99-conforming __func__... $ECHO_C" >&6 -if test "${ac_cv_cpp_func+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ - -int -main () -{ -char *foo = __func__; - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext -if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest.$ac_objext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_cv_cpp_func=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ - -int -main () -{ -char *foo = __FUNCTION__; - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext -if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest.$ac_objext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_cv_cpp_func=__FUNCTION__ -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -ac_cv_cpp_func=no -fi -rm -f conftest.err conftest.$ac_objext conftest.$ac_ext -fi -rm -f conftest.err conftest.$ac_objext conftest.$ac_ext -fi -echo "$as_me:$LINENO: result: $ac_cv_cpp_func" >&5 -echo "${ECHO_T}$ac_cv_cpp_func" >&6 -if test $ac_cv_cpp_func = __FUNCTION__; then - -cat >>confdefs.h <<\_ACEOF -#define __func__ __FUNCTION__ -_ACEOF - -elif test $ac_cv_cpp_func = no; then - -cat >>confdefs.h <<\_ACEOF -#define __func__ "" -_ACEOF - -fi - - - - -cat >>confdefs.h <<\_ACEOF -#define XML_NS 1 -_ACEOF - - -cat >>confdefs.h <<\_ACEOF -#define XML_DTD 1 -_ACEOF - - -cat >>confdefs.h <<\_ACEOF -#define XML_CONTEXT_BYTES 1024 -_ACEOF - - - ac_config_files="$ac_config_files Makefile" - -cat >confcache <<\_ACEOF -# This file is a shell script that caches the results of configure -# tests run on this system so they can be shared between configure -# scripts and configure runs, see configure's option --config-cache. -# It is not useful on other systems. If it contains results you don't -# want to keep, you may remove or edit it. -# -# config.status only pays attention to the cache file if you give it -# the --recheck option to rerun configure. -# -# `ac_cv_env_foo' variables (set or unset) will be overridden when -# loading this file, other *unset* `ac_cv_foo' will be assigned the -# following values. - -_ACEOF - -# The following way of writing the cache mishandles newlines in values, -# but we know of no workaround that is simple, portable, and efficient. -# So, don't put newlines in cache variables' values. -# Ultrix sh set writes to stderr and can't be redirected directly, -# and sets the high bit in the cache file unless we assign to the vars. -{ - (set) 2>&1 | - case `(ac_space=' '; set | grep ac_space) 2>&1` in - *ac_space=\ *) - # `set' does not quote correctly, so add quotes (double-quote - # substitution turns \\\\ into \\, and sed turns \\ into \). - sed -n \ - "s/'/'\\\\''/g; - s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" - ;; - *) - # `set' quotes correctly as required by POSIX, so do not add quotes. - sed -n \ - "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p" - ;; - esac; -} | - sed ' - t clear - : clear - s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ - t end - /^ac_cv_env/!s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ - : end' >>confcache -if diff $cache_file confcache >/dev/null 2>&1; then :; else - if test -w $cache_file; then - test "x$cache_file" != "x/dev/null" && echo "updating cache $cache_file" - cat confcache >$cache_file - else - echo "not updating unwritable cache $cache_file" - fi -fi -rm -f confcache - -test "x$prefix" = xNONE && prefix=$ac_default_prefix -# Let make expand exec_prefix. -test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' - -# VPATH may cause trouble with some makes, so we remove $(srcdir), -# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and -# trailing colons and then remove the whole line if VPATH becomes empty -# (actually we leave an empty line to preserve line numbers). -if test "x$srcdir" = x.; then - ac_vpsub='/^[ ]*VPATH[ ]*=/{ -s/:*\$(srcdir):*/:/; -s/:*\${srcdir}:*/:/; -s/:*@srcdir@:*/:/; -s/^\([^=]*=[ ]*\):*/\1/; -s/:*$//; -s/^[^=]*=[ ]*$//; -}' -fi - -DEFS=-DHAVE_CONFIG_H - -ac_libobjs= -ac_ltlibobjs= -for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue - # 1. Remove the extension, and $U if already installed. - ac_i=`echo "$ac_i" | - sed 's/\$U\././;s/\.o$//;s/\.obj$//'` - # 2. Add them. - ac_libobjs="$ac_libobjs $ac_i\$U.$ac_objext" - ac_ltlibobjs="$ac_ltlibobjs $ac_i"'$U.lo' -done -LIBOBJS=$ac_libobjs - -LTLIBOBJS=$ac_ltlibobjs - - - -: ${CONFIG_STATUS=./config.status} -ac_clean_files_save=$ac_clean_files -ac_clean_files="$ac_clean_files $CONFIG_STATUS" -{ echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5 -echo "$as_me: creating $CONFIG_STATUS" >&6;} -cat >$CONFIG_STATUS <<_ACEOF -#! $SHELL -# Generated by $as_me. -# Run this file to recreate the current configuration. -# Compiler output produced by configure, useful for debugging -# configure, is in config.log if it exists. - -debug=false -ac_cs_recheck=false -ac_cs_silent=false -SHELL=\${CONFIG_SHELL-$SHELL} -_ACEOF - -cat >>$CONFIG_STATUS <<\_ACEOF -## --------------------- ## -## M4sh Initialization. ## -## --------------------- ## - -# Be Bourne compatible -if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then - emulate sh - NULLCMD=: - # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which - # is contrary to our usage. Disable this feature. - alias -g '${1+"$@"}'='"$@"' -elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then - set -o posix -fi -DUALCASE=1; export DUALCASE # for MKS sh - -# Support unset when possible. -if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then - as_unset=unset -else - as_unset=false -fi - - -# Work around bugs in pre-3.0 UWIN ksh. -$as_unset ENV MAIL MAILPATH -PS1='$ ' -PS2='> ' -PS4='+ ' - -# NLS nuisances. -for as_var in \ - LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ - LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ - LC_TELEPHONE LC_TIME -do - if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then - eval $as_var=C; export $as_var - else - $as_unset $as_var - fi -done - -# Required to use basename. -if expr a : '\(a\)' >/dev/null 2>&1; then - as_expr=expr -else - as_expr=false -fi - -if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then - as_basename=basename -else - as_basename=false -fi - - -# Name of the executable. -as_me=`$as_basename "$0" || -$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ - X"$0" : 'X\(//\)$' \| \ - X"$0" : 'X\(/\)$' \| \ - . : '\(.\)' 2>/dev/null || -echo X/"$0" | - sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; } - /^X\/\(\/\/\)$/{ s//\1/; q; } - /^X\/\(\/\).*/{ s//\1/; q; } - s/.*/./; q'` - - -# PATH needs CR, and LINENO needs CR and PATH. -# Avoid depending upon Character Ranges. -as_cr_letters='abcdefghijklmnopqrstuvwxyz' -as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' -as_cr_Letters=$as_cr_letters$as_cr_LETTERS -as_cr_digits='0123456789' -as_cr_alnum=$as_cr_Letters$as_cr_digits - -# The user is always right. -if test "${PATH_SEPARATOR+set}" != set; then - echo "#! /bin/sh" >conf$$.sh - echo "exit 0" >>conf$$.sh - chmod +x conf$$.sh - if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then - PATH_SEPARATOR=';' - else - PATH_SEPARATOR=: - fi - rm -f conf$$.sh -fi - - - as_lineno_1=$LINENO - as_lineno_2=$LINENO - as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` - test "x$as_lineno_1" != "x$as_lineno_2" && - test "x$as_lineno_3" = "x$as_lineno_2" || { - # Find who we are. Look in the path if we contain no path at all - # relative or not. - case $0 in - *[\\/]* ) as_myself=$0 ;; - *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break -done - - ;; - esac - # We did not find ourselves, most probably we were run as `sh COMMAND' - # in which case we are not to be found in the path. - if test "x$as_myself" = x; then - as_myself=$0 - fi - if test ! -f "$as_myself"; then - { { echo "$as_me:$LINENO: error: cannot find myself; rerun with an absolute path" >&5 -echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2;} - { (exit 1); exit 1; }; } - fi - case $CONFIG_SHELL in - '') - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for as_base in sh bash ksh sh5; do - case $as_dir in - /*) - if ("$as_dir/$as_base" -c ' - as_lineno_1=$LINENO - as_lineno_2=$LINENO - as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` - test "x$as_lineno_1" != "x$as_lineno_2" && - test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then - $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; } - $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; } - CONFIG_SHELL=$as_dir/$as_base - export CONFIG_SHELL - exec "$CONFIG_SHELL" "$0" ${1+"$@"} - fi;; - esac - done -done -;; - esac - - # Create $as_me.lineno as a copy of $as_myself, but with $LINENO - # uniformly replaced by the line number. The first 'sed' inserts a - # line-number line before each line; the second 'sed' does the real - # work. The second script uses 'N' to pair each line-number line - # with the numbered line, and appends trailing '-' during - # substitution so that $LINENO is not a special case at line end. - # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the - # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-) - sed '=' <$as_myself | - sed ' - N - s,$,-, - : loop - s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3, - t loop - s,-$,, - s,^['$as_cr_digits']*\n,, - ' >$as_me.lineno && - chmod +x $as_me.lineno || - { { echo "$as_me:$LINENO: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&5 -echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2;} - { (exit 1); exit 1; }; } - - # Don't try to exec as it changes $[0], causing all sort of problems - # (the dirname of $[0] is not the place where we might find the - # original and so on. Autoconf is especially sensible to this). - . ./$as_me.lineno - # Exit status is that of the last command. - exit -} - - -case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in - *c*,-n*) ECHO_N= ECHO_C=' -' ECHO_T=' ' ;; - *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;; - *) ECHO_N= ECHO_C='\c' ECHO_T= ;; -esac - -if expr a : '\(a\)' >/dev/null 2>&1; then - as_expr=expr -else - as_expr=false -fi - -rm -f conf$$ conf$$.exe conf$$.file -echo >conf$$.file -if ln -s conf$$.file conf$$ 2>/dev/null; then - # We could just check for DJGPP; but this test a) works b) is more generic - # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04). - if test -f conf$$.exe; then - # Don't use ln at all; we don't have any links - as_ln_s='cp -p' - else - as_ln_s='ln -s' - fi -elif ln conf$$.file conf$$ 2>/dev/null; then - as_ln_s=ln -else - as_ln_s='cp -p' -fi -rm -f conf$$ conf$$.exe conf$$.file - -if mkdir -p . 2>/dev/null; then - as_mkdir_p=: -else - test -d ./-p && rmdir ./-p - as_mkdir_p=false -fi - -as_executable_p="test -f" - -# Sed expression to map a string onto a valid CPP name. -as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" - -# Sed expression to map a string onto a valid variable name. -as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" - - -# IFS -# We need space, tab and new line, in precisely that order. -as_nl=' -' -IFS=" $as_nl" - -# CDPATH. -$as_unset CDPATH - -exec 6>&1 - -# Open the log real soon, to keep \$[0] and so on meaningful, and to -# report actual input values of CONFIG_FILES etc. instead of their -# values after options handling. Logging --version etc. is OK. -exec 5>>config.log -{ - echo - sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX -## Running $as_me. ## -_ASBOX -} >&5 -cat >&5 <<_CSEOF - -This file was extended by expat $as_me 2.0.1, which was -generated by GNU Autoconf 2.59. Invocation command line was - - CONFIG_FILES = $CONFIG_FILES - CONFIG_HEADERS = $CONFIG_HEADERS - CONFIG_LINKS = $CONFIG_LINKS - CONFIG_COMMANDS = $CONFIG_COMMANDS - $ $0 $@ - -_CSEOF -echo "on `(hostname || uname -n) 2>/dev/null | sed 1q`" >&5 -echo >&5 -_ACEOF - -# Files that config.status was made for. -if test -n "$ac_config_files"; then - echo "config_files=\"$ac_config_files\"" >>$CONFIG_STATUS -fi - -if test -n "$ac_config_headers"; then - echo "config_headers=\"$ac_config_headers\"" >>$CONFIG_STATUS -fi - -if test -n "$ac_config_links"; then - echo "config_links=\"$ac_config_links\"" >>$CONFIG_STATUS -fi - -if test -n "$ac_config_commands"; then - echo "config_commands=\"$ac_config_commands\"" >>$CONFIG_STATUS -fi - -cat >>$CONFIG_STATUS <<\_ACEOF - -ac_cs_usage="\ -\`$as_me' instantiates files from templates according to the -current configuration. - -Usage: $0 [OPTIONS] [FILE]... - - -h, --help print this help, then exit - -V, --version print version number, then exit - -q, --quiet do not print progress messages - -d, --debug don't remove temporary files - --recheck update $as_me by reconfiguring in the same conditions - --file=FILE[:TEMPLATE] - instantiate the configuration file FILE - --header=FILE[:TEMPLATE] - instantiate the configuration header FILE - -Configuration files: -$config_files - -Configuration headers: -$config_headers - -Report bugs to ." -_ACEOF - -cat >>$CONFIG_STATUS <<_ACEOF -ac_cs_version="\\ -expat config.status 2.0.1 -configured by $0, generated by GNU Autoconf 2.59, - with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\" - -Copyright (C) 2003 Free Software Foundation, Inc. -This config.status script is free software; the Free Software Foundation -gives unlimited permission to copy, distribute and modify it." -srcdir=$srcdir -INSTALL="$INSTALL" -_ACEOF - -cat >>$CONFIG_STATUS <<\_ACEOF -# If no file are specified by the user, then we need to provide default -# value. By we need to know if files were specified by the user. -ac_need_defaults=: -while test $# != 0 -do - case $1 in - --*=*) - ac_option=`expr "x$1" : 'x\([^=]*\)='` - ac_optarg=`expr "x$1" : 'x[^=]*=\(.*\)'` - ac_shift=: - ;; - -*) - ac_option=$1 - ac_optarg=$2 - ac_shift=shift - ;; - *) # This is not an option, so the user has probably given explicit - # arguments. - ac_option=$1 - ac_need_defaults=false;; - esac - - case $ac_option in - # Handling of the options. -_ACEOF -cat >>$CONFIG_STATUS <<\_ACEOF - -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) - ac_cs_recheck=: ;; - --version | --vers* | -V ) - echo "$ac_cs_version"; exit 0 ;; - --he | --h) - # Conflict between --help and --header - { { echo "$as_me:$LINENO: error: ambiguous option: $1 -Try \`$0 --help' for more information." >&5 -echo "$as_me: error: ambiguous option: $1 -Try \`$0 --help' for more information." >&2;} - { (exit 1); exit 1; }; };; - --help | --hel | -h ) - echo "$ac_cs_usage"; exit 0 ;; - --debug | --d* | -d ) - debug=: ;; - --file | --fil | --fi | --f ) - $ac_shift - CONFIG_FILES="$CONFIG_FILES $ac_optarg" - ac_need_defaults=false;; - --header | --heade | --head | --hea ) - $ac_shift - CONFIG_HEADERS="$CONFIG_HEADERS $ac_optarg" - ac_need_defaults=false;; - -q | -quiet | --quiet | --quie | --qui | --qu | --q \ - | -silent | --silent | --silen | --sile | --sil | --si | --s) - ac_cs_silent=: ;; - - # This is an error. - -*) { { echo "$as_me:$LINENO: error: unrecognized option: $1 -Try \`$0 --help' for more information." >&5 -echo "$as_me: error: unrecognized option: $1 -Try \`$0 --help' for more information." >&2;} - { (exit 1); exit 1; }; } ;; - - *) ac_config_targets="$ac_config_targets $1" ;; - - esac - shift -done - -ac_configure_extra_args= - -if $ac_cs_silent; then - exec 6>/dev/null - ac_configure_extra_args="$ac_configure_extra_args --silent" -fi - -_ACEOF -cat >>$CONFIG_STATUS <<_ACEOF -if \$ac_cs_recheck; then - echo "running $SHELL $0 " $ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6 - exec $SHELL $0 $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion -fi - -_ACEOF - - - - - -cat >>$CONFIG_STATUS <<\_ACEOF -for ac_config_target in $ac_config_targets -do - case "$ac_config_target" in - # Handling of arguments. - "Makefile" ) CONFIG_FILES="$CONFIG_FILES Makefile" ;; - "expat_config.h" ) CONFIG_HEADERS="$CONFIG_HEADERS expat_config.h" ;; - *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5 -echo "$as_me: error: invalid argument: $ac_config_target" >&2;} - { (exit 1); exit 1; }; };; - esac -done - -# If the user did not use the arguments to specify the items to instantiate, -# then the envvar interface is used. Set only those that are not. -# We use the long form for the default assignment because of an extremely -# bizarre bug on SunOS 4.1.3. -if $ac_need_defaults; then - test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files - test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers -fi - -# Have a temporary directory for convenience. Make it in the build tree -# simply because there is no reason to put it here, and in addition, -# creating and moving files from /tmp can sometimes cause problems. -# Create a temporary directory, and hook for its removal unless debugging. -$debug || -{ - trap 'exit_status=$?; rm -rf $tmp && exit $exit_status' 0 - trap '{ (exit 1); exit 1; }' 1 2 13 15 -} - -# Create a (secure) tmp directory for tmp files. - -{ - tmp=`(umask 077 && mktemp -d -q "./confstatXXXXXX") 2>/dev/null` && - test -n "$tmp" && test -d "$tmp" -} || -{ - tmp=./confstat$$-$RANDOM - (umask 077 && mkdir $tmp) -} || -{ - echo "$me: cannot create a temporary directory in ." >&2 - { (exit 1); exit 1; } -} - -_ACEOF - -cat >>$CONFIG_STATUS <<_ACEOF - -# -# CONFIG_FILES section. -# - -# No need to generate the scripts if there are no CONFIG_FILES. -# This happens for instance when ./config.status config.h -if test -n "\$CONFIG_FILES"; then - # Protect against being on the right side of a sed subst in config.status. - sed 's/,@/@@/; s/@,/@@/; s/,;t t\$/@;t t/; /@;t t\$/s/[\\\\&,]/\\\\&/g; - s/@@/,@/; s/@@/@,/; s/@;t t\$/,;t t/' >\$tmp/subs.sed <<\\CEOF -s,@SHELL@,$SHELL,;t t -s,@PATH_SEPARATOR@,$PATH_SEPARATOR,;t t -s,@PACKAGE_NAME@,$PACKAGE_NAME,;t t -s,@PACKAGE_TARNAME@,$PACKAGE_TARNAME,;t t -s,@PACKAGE_VERSION@,$PACKAGE_VERSION,;t t -s,@PACKAGE_STRING@,$PACKAGE_STRING,;t t -s,@PACKAGE_BUGREPORT@,$PACKAGE_BUGREPORT,;t t -s,@exec_prefix@,$exec_prefix,;t t -s,@prefix@,$prefix,;t t -s,@program_transform_name@,$program_transform_name,;t t -s,@bindir@,$bindir,;t t -s,@sbindir@,$sbindir,;t t -s,@libexecdir@,$libexecdir,;t t -s,@datadir@,$datadir,;t t -s,@sysconfdir@,$sysconfdir,;t t -s,@sharedstatedir@,$sharedstatedir,;t t -s,@localstatedir@,$localstatedir,;t t -s,@libdir@,$libdir,;t t -s,@includedir@,$includedir,;t t -s,@oldincludedir@,$oldincludedir,;t t -s,@infodir@,$infodir,;t t -s,@mandir@,$mandir,;t t -s,@build_alias@,$build_alias,;t t -s,@host_alias@,$host_alias,;t t -s,@target_alias@,$target_alias,;t t -s,@DEFS@,$DEFS,;t t -s,@ECHO_C@,$ECHO_C,;t t -s,@ECHO_N@,$ECHO_N,;t t -s,@ECHO_T@,$ECHO_T,;t t -s,@LIBS@,$LIBS,;t t -s,@build@,$build,;t t -s,@build_cpu@,$build_cpu,;t t -s,@build_vendor@,$build_vendor,;t t -s,@build_os@,$build_os,;t t -s,@host@,$host,;t t -s,@host_cpu@,$host_cpu,;t t -s,@host_vendor@,$host_vendor,;t t -s,@host_os@,$host_os,;t t -s,@CC@,$CC,;t t -s,@CFLAGS@,$CFLAGS,;t t -s,@LDFLAGS@,$LDFLAGS,;t t -s,@CPPFLAGS@,$CPPFLAGS,;t t -s,@ac_ct_CC@,$ac_ct_CC,;t t -s,@EXEEXT@,$EXEEXT,;t t -s,@OBJEXT@,$OBJEXT,;t t -s,@EGREP@,$EGREP,;t t -s,@LN_S@,$LN_S,;t t -s,@ECHO@,$ECHO,;t t -s,@AR@,$AR,;t t -s,@ac_ct_AR@,$ac_ct_AR,;t t -s,@RANLIB@,$RANLIB,;t t -s,@ac_ct_RANLIB@,$ac_ct_RANLIB,;t t -s,@STRIP@,$STRIP,;t t -s,@ac_ct_STRIP@,$ac_ct_STRIP,;t t -s,@DLLTOOL@,$DLLTOOL,;t t -s,@ac_ct_DLLTOOL@,$ac_ct_DLLTOOL,;t t -s,@AS@,$AS,;t t -s,@ac_ct_AS@,$ac_ct_AS,;t t -s,@OBJDUMP@,$OBJDUMP,;t t -s,@ac_ct_OBJDUMP@,$ac_ct_OBJDUMP,;t t -s,@CPP@,$CPP,;t t -s,@CXX@,$CXX,;t t -s,@CXXFLAGS@,$CXXFLAGS,;t t -s,@ac_ct_CXX@,$ac_ct_CXX,;t t -s,@CXXCPP@,$CXXCPP,;t t -s,@F77@,$F77,;t t -s,@FFLAGS@,$FFLAGS,;t t -s,@ac_ct_F77@,$ac_ct_F77,;t t -s,@LIBTOOL@,$LIBTOOL,;t t -s,@LIBCURRENT@,$LIBCURRENT,;t t -s,@LIBREVISION@,$LIBREVISION,;t t -s,@LIBAGE@,$LIBAGE,;t t -s,@INSTALL_PROGRAM@,$INSTALL_PROGRAM,;t t -s,@INSTALL_SCRIPT@,$INSTALL_SCRIPT,;t t -s,@INSTALL_DATA@,$INSTALL_DATA,;t t -s,@FILEMAP@,$FILEMAP,;t t -s,@LIBOBJS@,$LIBOBJS,;t t -s,@LTLIBOBJS@,$LTLIBOBJS,;t t -CEOF - -_ACEOF - - cat >>$CONFIG_STATUS <<\_ACEOF - # Split the substitutions into bite-sized pieces for seds with - # small command number limits, like on Digital OSF/1 and HP-UX. - ac_max_sed_lines=48 - ac_sed_frag=1 # Number of current file. - ac_beg=1 # First line for current file. - ac_end=$ac_max_sed_lines # Line after last line for current file. - ac_more_lines=: - ac_sed_cmds= - while $ac_more_lines; do - if test $ac_beg -gt 1; then - sed "1,${ac_beg}d; ${ac_end}q" $tmp/subs.sed >$tmp/subs.frag - else - sed "${ac_end}q" $tmp/subs.sed >$tmp/subs.frag - fi - if test ! -s $tmp/subs.frag; then - ac_more_lines=false - else - # The purpose of the label and of the branching condition is to - # speed up the sed processing (if there are no `@' at all, there - # is no need to browse any of the substitutions). - # These are the two extra sed commands mentioned above. - (echo ':t - /@[a-zA-Z_][a-zA-Z_0-9]*@/!b' && cat $tmp/subs.frag) >$tmp/subs-$ac_sed_frag.sed - if test -z "$ac_sed_cmds"; then - ac_sed_cmds="sed -f $tmp/subs-$ac_sed_frag.sed" - else - ac_sed_cmds="$ac_sed_cmds | sed -f $tmp/subs-$ac_sed_frag.sed" - fi - ac_sed_frag=`expr $ac_sed_frag + 1` - ac_beg=$ac_end - ac_end=`expr $ac_end + $ac_max_sed_lines` - fi - done - if test -z "$ac_sed_cmds"; then - ac_sed_cmds=cat - fi -fi # test -n "$CONFIG_FILES" - -_ACEOF -cat >>$CONFIG_STATUS <<\_ACEOF -for ac_file in : $CONFIG_FILES; do test "x$ac_file" = x: && continue - # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". - case $ac_file in - - | *:- | *:-:* ) # input from stdin - cat >$tmp/stdin - ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` - ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; - *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` - ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; - * ) ac_file_in=$ac_file.in ;; - esac - - # Compute @srcdir@, @top_srcdir@, and @INSTALL@ for subdirectories. - ac_dir=`(dirname "$ac_file") 2>/dev/null || -$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$ac_file" : 'X\(//\)[^/]' \| \ - X"$ac_file" : 'X\(//\)$' \| \ - X"$ac_file" : 'X\(/\)' \| \ - . : '\(.\)' 2>/dev/null || -echo X"$ac_file" | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } - /^X\(\/\/\)[^/].*/{ s//\1/; q; } - /^X\(\/\/\)$/{ s//\1/; q; } - /^X\(\/\).*/{ s//\1/; q; } - s/.*/./; q'` - { if $as_mkdir_p; then - mkdir -p "$ac_dir" - else - as_dir="$ac_dir" - as_dirs= - while test ! -d "$as_dir"; do - as_dirs="$as_dir $as_dirs" - as_dir=`(dirname "$as_dir") 2>/dev/null || -$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$as_dir" : 'X\(//\)[^/]' \| \ - X"$as_dir" : 'X\(//\)$' \| \ - X"$as_dir" : 'X\(/\)' \| \ - . : '\(.\)' 2>/dev/null || -echo X"$as_dir" | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } - /^X\(\/\/\)[^/].*/{ s//\1/; q; } - /^X\(\/\/\)$/{ s//\1/; q; } - /^X\(\/\).*/{ s//\1/; q; } - s/.*/./; q'` - done - test ! -n "$as_dirs" || mkdir $as_dirs - fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5 -echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;} - { (exit 1); exit 1; }; }; } - - ac_builddir=. - -if test "$ac_dir" != .; then - ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` - # A "../" for each directory in $ac_dir_suffix. - ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'` -else - ac_dir_suffix= ac_top_builddir= -fi - -case $srcdir in - .) # No --srcdir option. We are building in place. - ac_srcdir=. - if test -z "$ac_top_builddir"; then - ac_top_srcdir=. - else - ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` - fi ;; - [\\/]* | ?:[\\/]* ) # Absolute path. - ac_srcdir=$srcdir$ac_dir_suffix; - ac_top_srcdir=$srcdir ;; - *) # Relative path. - ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix - ac_top_srcdir=$ac_top_builddir$srcdir ;; -esac - -# Do not use `cd foo && pwd` to compute absolute paths, because -# the directories may not exist. -case `pwd` in -.) ac_abs_builddir="$ac_dir";; -*) - case "$ac_dir" in - .) ac_abs_builddir=`pwd`;; - [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";; - *) ac_abs_builddir=`pwd`/"$ac_dir";; - esac;; -esac -case $ac_abs_builddir in -.) ac_abs_top_builddir=${ac_top_builddir}.;; -*) - case ${ac_top_builddir}. in - .) ac_abs_top_builddir=$ac_abs_builddir;; - [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;; - *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;; - esac;; -esac -case $ac_abs_builddir in -.) ac_abs_srcdir=$ac_srcdir;; -*) - case $ac_srcdir in - .) ac_abs_srcdir=$ac_abs_builddir;; - [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;; - *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;; - esac;; -esac -case $ac_abs_builddir in -.) ac_abs_top_srcdir=$ac_top_srcdir;; -*) - case $ac_top_srcdir in - .) ac_abs_top_srcdir=$ac_abs_builddir;; - [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;; - *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;; - esac;; -esac - - - case $INSTALL in - [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; - *) ac_INSTALL=$ac_top_builddir$INSTALL ;; - esac - - if test x"$ac_file" != x-; then - { echo "$as_me:$LINENO: creating $ac_file" >&5 -echo "$as_me: creating $ac_file" >&6;} - rm -f "$ac_file" - fi - # Let's still pretend it is `configure' which instantiates (i.e., don't - # use $as_me), people would be surprised to read: - # /* config.h. Generated by config.status. */ - if test x"$ac_file" = x-; then - configure_input= - else - configure_input="$ac_file. " - fi - configure_input=$configure_input"Generated from `echo $ac_file_in | - sed 's,.*/,,'` by configure." - - # First look for the input files in the build tree, otherwise in the - # src tree. - ac_file_inputs=`IFS=: - for f in $ac_file_in; do - case $f in - -) echo $tmp/stdin ;; - [\\/$]*) - # Absolute (can't be DOS-style, as IFS=:) - test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 -echo "$as_me: error: cannot find input file: $f" >&2;} - { (exit 1); exit 1; }; } - echo "$f";; - *) # Relative - if test -f "$f"; then - # Build tree - echo "$f" - elif test -f "$srcdir/$f"; then - # Source tree - echo "$srcdir/$f" - else - # /dev/null tree - { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 -echo "$as_me: error: cannot find input file: $f" >&2;} - { (exit 1); exit 1; }; } - fi;; - esac - done` || { (exit 1); exit 1; } -_ACEOF -cat >>$CONFIG_STATUS <<_ACEOF - sed "$ac_vpsub -$extrasub -_ACEOF -cat >>$CONFIG_STATUS <<\_ACEOF -:t -/@[a-zA-Z_][a-zA-Z_0-9]*@/!b -s,@configure_input@,$configure_input,;t t -s,@srcdir@,$ac_srcdir,;t t -s,@abs_srcdir@,$ac_abs_srcdir,;t t -s,@top_srcdir@,$ac_top_srcdir,;t t -s,@abs_top_srcdir@,$ac_abs_top_srcdir,;t t -s,@builddir@,$ac_builddir,;t t -s,@abs_builddir@,$ac_abs_builddir,;t t -s,@top_builddir@,$ac_top_builddir,;t t -s,@abs_top_builddir@,$ac_abs_top_builddir,;t t -s,@INSTALL@,$ac_INSTALL,;t t -" $ac_file_inputs | (eval "$ac_sed_cmds") >$tmp/out - rm -f $tmp/stdin - if test x"$ac_file" != x-; then - mv $tmp/out $ac_file - else - cat $tmp/out - rm -f $tmp/out - fi - -done -_ACEOF -cat >>$CONFIG_STATUS <<\_ACEOF - -# -# CONFIG_HEADER section. -# - -# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where -# NAME is the cpp macro being defined and VALUE is the value it is being given. -# -# ac_d sets the value in "#define NAME VALUE" lines. -ac_dA='s,^\([ ]*\)#\([ ]*define[ ][ ]*\)' -ac_dB='[ ].*$,\1#\2' -ac_dC=' ' -ac_dD=',;t' -# ac_u turns "#undef NAME" without trailing blanks into "#define NAME VALUE". -ac_uA='s,^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)' -ac_uB='$,\1#\2define\3' -ac_uC=' ' -ac_uD=',;t' - -for ac_file in : $CONFIG_HEADERS; do test "x$ac_file" = x: && continue - # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". - case $ac_file in - - | *:- | *:-:* ) # input from stdin - cat >$tmp/stdin - ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` - ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; - *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` - ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; - * ) ac_file_in=$ac_file.in ;; - esac - - test x"$ac_file" != x- && { echo "$as_me:$LINENO: creating $ac_file" >&5 -echo "$as_me: creating $ac_file" >&6;} - - # First look for the input files in the build tree, otherwise in the - # src tree. - ac_file_inputs=`IFS=: - for f in $ac_file_in; do - case $f in - -) echo $tmp/stdin ;; - [\\/$]*) - # Absolute (can't be DOS-style, as IFS=:) - test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 -echo "$as_me: error: cannot find input file: $f" >&2;} - { (exit 1); exit 1; }; } - # Do quote $f, to prevent DOS paths from being IFS'd. - echo "$f";; - *) # Relative - if test -f "$f"; then - # Build tree - echo "$f" - elif test -f "$srcdir/$f"; then - # Source tree - echo "$srcdir/$f" - else - # /dev/null tree - { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 -echo "$as_me: error: cannot find input file: $f" >&2;} - { (exit 1); exit 1; }; } - fi;; - esac - done` || { (exit 1); exit 1; } - # Remove the trailing spaces. - sed 's/[ ]*$//' $ac_file_inputs >$tmp/in - -_ACEOF - -# Transform confdefs.h into two sed scripts, `conftest.defines' and -# `conftest.undefs', that substitutes the proper values into -# config.h.in to produce config.h. The first handles `#define' -# templates, and the second `#undef' templates. -# And first: Protect against being on the right side of a sed subst in -# config.status. Protect against being in an unquoted here document -# in config.status. -rm -f conftest.defines conftest.undefs -# Using a here document instead of a string reduces the quoting nightmare. -# Putting comments in sed scripts is not portable. -# -# `end' is used to avoid that the second main sed command (meant for -# 0-ary CPP macros) applies to n-ary macro definitions. -# See the Autoconf documentation for `clear'. -cat >confdef2sed.sed <<\_ACEOF -s/[\\&,]/\\&/g -s,[\\$`],\\&,g -t clear -: clear -s,^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*\)\(([^)]*)\)[ ]*\(.*\)$,${ac_dA}\1${ac_dB}\1\2${ac_dC}\3${ac_dD},gp -t end -s,^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\)$,${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD},gp -: end -_ACEOF -# If some macros were called several times there might be several times -# the same #defines, which is useless. Nevertheless, we may not want to -# sort them, since we want the *last* AC-DEFINE to be honored. -uniq confdefs.h | sed -n -f confdef2sed.sed >conftest.defines -sed 's/ac_d/ac_u/g' conftest.defines >conftest.undefs -rm -f confdef2sed.sed - -# This sed command replaces #undef with comments. This is necessary, for -# example, in the case of _POSIX_SOURCE, which is predefined and required -# on some systems where configure will not decide to define it. -cat >>conftest.undefs <<\_ACEOF -s,^[ ]*#[ ]*undef[ ][ ]*[a-zA-Z_][a-zA-Z_0-9]*,/* & */, -_ACEOF - -# Break up conftest.defines because some shells have a limit on the size -# of here documents, and old seds have small limits too (100 cmds). -echo ' # Handle all the #define templates only if necessary.' >>$CONFIG_STATUS -echo ' if grep "^[ ]*#[ ]*define" $tmp/in >/dev/null; then' >>$CONFIG_STATUS -echo ' # If there are no defines, we may have an empty if/fi' >>$CONFIG_STATUS -echo ' :' >>$CONFIG_STATUS -rm -f conftest.tail -while grep . conftest.defines >/dev/null -do - # Write a limited-size here document to $tmp/defines.sed. - echo ' cat >$tmp/defines.sed <>$CONFIG_STATUS - # Speed up: don't consider the non `#define' lines. - echo '/^[ ]*#[ ]*define/!b' >>$CONFIG_STATUS - # Work around the forget-to-reset-the-flag bug. - echo 't clr' >>$CONFIG_STATUS - echo ': clr' >>$CONFIG_STATUS - sed ${ac_max_here_lines}q conftest.defines >>$CONFIG_STATUS - echo 'CEOF - sed -f $tmp/defines.sed $tmp/in >$tmp/out - rm -f $tmp/in - mv $tmp/out $tmp/in -' >>$CONFIG_STATUS - sed 1,${ac_max_here_lines}d conftest.defines >conftest.tail - rm -f conftest.defines - mv conftest.tail conftest.defines -done -rm -f conftest.defines -echo ' fi # grep' >>$CONFIG_STATUS -echo >>$CONFIG_STATUS - -# Break up conftest.undefs because some shells have a limit on the size -# of here documents, and old seds have small limits too (100 cmds). -echo ' # Handle all the #undef templates' >>$CONFIG_STATUS -rm -f conftest.tail -while grep . conftest.undefs >/dev/null -do - # Write a limited-size here document to $tmp/undefs.sed. - echo ' cat >$tmp/undefs.sed <>$CONFIG_STATUS - # Speed up: don't consider the non `#undef' - echo '/^[ ]*#[ ]*undef/!b' >>$CONFIG_STATUS - # Work around the forget-to-reset-the-flag bug. - echo 't clr' >>$CONFIG_STATUS - echo ': clr' >>$CONFIG_STATUS - sed ${ac_max_here_lines}q conftest.undefs >>$CONFIG_STATUS - echo 'CEOF - sed -f $tmp/undefs.sed $tmp/in >$tmp/out - rm -f $tmp/in - mv $tmp/out $tmp/in -' >>$CONFIG_STATUS - sed 1,${ac_max_here_lines}d conftest.undefs >conftest.tail - rm -f conftest.undefs - mv conftest.tail conftest.undefs -done -rm -f conftest.undefs - -cat >>$CONFIG_STATUS <<\_ACEOF - # Let's still pretend it is `configure' which instantiates (i.e., don't - # use $as_me), people would be surprised to read: - # /* config.h. Generated by config.status. */ - if test x"$ac_file" = x-; then - echo "/* Generated by configure. */" >$tmp/config.h - else - echo "/* $ac_file. Generated by configure. */" >$tmp/config.h - fi - cat $tmp/in >>$tmp/config.h - rm -f $tmp/in - if test x"$ac_file" != x-; then - if diff $ac_file $tmp/config.h >/dev/null 2>&1; then - { echo "$as_me:$LINENO: $ac_file is unchanged" >&5 -echo "$as_me: $ac_file is unchanged" >&6;} - else - ac_dir=`(dirname "$ac_file") 2>/dev/null || -$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$ac_file" : 'X\(//\)[^/]' \| \ - X"$ac_file" : 'X\(//\)$' \| \ - X"$ac_file" : 'X\(/\)' \| \ - . : '\(.\)' 2>/dev/null || -echo X"$ac_file" | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } - /^X\(\/\/\)[^/].*/{ s//\1/; q; } - /^X\(\/\/\)$/{ s//\1/; q; } - /^X\(\/\).*/{ s//\1/; q; } - s/.*/./; q'` - { if $as_mkdir_p; then - mkdir -p "$ac_dir" - else - as_dir="$ac_dir" - as_dirs= - while test ! -d "$as_dir"; do - as_dirs="$as_dir $as_dirs" - as_dir=`(dirname "$as_dir") 2>/dev/null || -$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$as_dir" : 'X\(//\)[^/]' \| \ - X"$as_dir" : 'X\(//\)$' \| \ - X"$as_dir" : 'X\(/\)' \| \ - . : '\(.\)' 2>/dev/null || -echo X"$as_dir" | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } - /^X\(\/\/\)[^/].*/{ s//\1/; q; } - /^X\(\/\/\)$/{ s//\1/; q; } - /^X\(\/\).*/{ s//\1/; q; } - s/.*/./; q'` - done - test ! -n "$as_dirs" || mkdir $as_dirs - fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5 -echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;} - { (exit 1); exit 1; }; }; } - - rm -f $ac_file - mv $tmp/config.h $ac_file - fi - else - cat $tmp/config.h - rm -f $tmp/config.h - fi -done -_ACEOF - -cat >>$CONFIG_STATUS <<\_ACEOF - -{ (exit 0); exit 0; } -_ACEOF -chmod +x $CONFIG_STATUS -ac_clean_files=$ac_clean_files_save - - -# configure is writing to config.log, and then calls config.status. -# config.status does its own redirection, appending to config.log. -# Unfortunately, on DOS this fails, as config.log is still kept open -# by configure, so config.status won't be able to write to it; its -# output is simply discarded. So we exec the FD to /dev/null, -# effectively closing config.log, so it can be properly (re)opened and -# appended to by config.status. When coming back to configure, we -# need to make the FD available again. -if test "$no_create" != yes; then - ac_cs_success=: - ac_config_status_args= - test "$silent" = yes && - ac_config_status_args="$ac_config_status_args --quiet" - exec 5>/dev/null - $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false - exec 5>>config.log - # Use ||, not &&, to avoid exiting from the if with $? = 1, which - # would make configure fail if this is the last instruction. - $ac_cs_success || { (exit 1); exit 1; } -fi - - -abs_srcdir="`cd $srcdir && pwd`" -abs_builddir="`pwd`" -if test "$abs_srcdir" != "$abs_builddir"; then - make mkdir-init -fi diff --git a/cextern/expat/configure.in b/cextern/expat/configure.in deleted file mode 100755 index 24bcb407e0db..000000000000 --- a/cextern/expat/configure.in +++ /dev/null @@ -1,154 +0,0 @@ -dnl configuration script for expat -dnl Process this file with autoconf to produce a configure script. -dnl -dnl Copyright 2000 Clark Cooper -dnl -dnl This file is part of EXPAT. -dnl -dnl EXPAT is free software; you can redistribute it and/or modify it -dnl under the terms of the License (based on the MIT/X license) contained -dnl in the file COPYING that comes with this distribution. -dnl - -dnl Ensure that Expat is configured with autoconf 2.52 or newer -AC_PREREQ(2.52) - -dnl Get the version number of Expat, using m4's esyscmd() command to run -dnl the command at m4-generation time. This allows us to create an m4 -dnl symbol holding the correct version number. AC_INIT() requires the -dnl version number at m4-time, rather than when ./configure is run, so -dnl all this must happen as part of m4, not as part of the shell code -dnl contained in ./configure. -dnl -dnl NOTE: esyscmd() is a GNU M4 extension. Thus, we wrap it in an appropriate -dnl test. I believe this test will work, but I don't have a place with non- -dnl GNU M4 to test it right now. -define([expat_version], ifdef([__gnu__], - [esyscmd(conftools/get-version.sh lib/expat.h)], - [2.0.x])) -AC_INIT(expat, expat_version, expat-bugs@libexpat.org) -undefine([expat_version]) - -AC_CONFIG_SRCDIR(Makefile.in) -AC_CONFIG_AUX_DIR(conftools) - - -dnl -dnl Increment LIBREVISION if source code has changed at all -dnl -dnl If the API has changed, increment LIBCURRENT and set LIBREVISION to 0 -dnl -dnl If the API changes compatibly (i.e. simply adding a new function -dnl without changing or removing earlier interfaces), then increment LIBAGE. -dnl -dnl If the API changes incompatibly set LIBAGE back to 0 -dnl - -LIBCURRENT=6 -LIBREVISION=2 -LIBAGE=5 - -AC_CONFIG_HEADER(expat_config.h) - -sinclude(conftools/libtool.m4) -sinclude(conftools/ac_c_bigendian_cross.m4) - -AC_LIBTOOL_WIN32_DLL -AC_PROG_LIBTOOL - -AC_SUBST(LIBCURRENT) -AC_SUBST(LIBREVISION) -AC_SUBST(LIBAGE) - -dnl Checks for programs. -AC_PROG_CC -AC_PROG_INSTALL - -if test "$GCC" = yes ; then - dnl - dnl Be careful about adding the -fexceptions option; some versions of - dnl GCC don't support it and it causes extra warnings that are only - dnl distracting; avoid. - dnl - OLDCFLAGS="$CFLAGS -Wall -Wmissing-prototypes -Wstrict-prototypes" - CFLAGS="$OLDCFLAGS -fexceptions" - AC_MSG_CHECKING(whether $CC accepts -fexceptions) - AC_TRY_LINK( , , - AC_MSG_RESULT(yes), - AC_MSG_RESULT(no); CFLAGS="$OLDCFLAGS") - CXXFLAGS=`echo "$CFLAGS" | sed 's/ -Wmissing-prototypes -Wstrict-prototypes//'` -fi - -dnl Checks for header files. -AC_HEADER_STDC - -dnl Checks for typedefs, structures, and compiler characteristics. - -dnl Note: Avoid using AC_C_BIGENDIAN because it does not -dnl work in a cross compile. -AC_C_BIGENDIAN_CROSS - -AC_C_CONST -AC_TYPE_SIZE_T -AC_CHECK_FUNCS(memmove bcopy) - -dnl Only needed for xmlwf: -AC_CHECK_HEADERS(fcntl.h unistd.h) -AC_TYPE_OFF_T -AC_FUNC_MMAP - -if test "$ac_cv_func_mmap_fixed_mapped" = "yes"; then - FILEMAP=unixfilemap -else - FILEMAP=readfilemap -fi -AC_SUBST(FILEMAP) - -dnl Needed for the test support code; this was found at -dnl http://lists.gnu.org/archive/html/bug-autoconf/2002-07/msg00028.html - -# AC_CPP_FUNC -# ------------------ # -# Checks to see if ANSI C99 CPP variable __func__ works. -# If not, perhaps __FUNCTION__ works instead. -# If not, we'll just define __func__ to "". -AC_DEFUN([AC_CPP_FUNC], -[AC_REQUIRE([AC_PROG_CC_STDC])dnl -AC_CACHE_CHECK([for an ANSI C99-conforming __func__], ac_cv_cpp_func, -[AC_COMPILE_IFELSE([AC_LANG_PROGRAM([], -[[char *foo = __func__;]])], - [ac_cv_cpp_func=yes], - [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([], -[[char *foo = __FUNCTION__;]])], - [ac_cv_cpp_func=__FUNCTION__], - [ac_cv_cpp_func=no])])]) -if test $ac_cv_cpp_func = __FUNCTION__; then - AC_DEFINE(__func__,__FUNCTION__, - [Define to __FUNCTION__ or "" if `__func__' does not conform to -ANSI C.]) -elif test $ac_cv_cpp_func = no; then - AC_DEFINE(__func__,"", - [Define to __FUNCTION__ or "" if `__func__' does not conform to -ANSI C.]) -fi -])# AC_CPP_FUNC - -AC_CPP_FUNC - - -dnl Some basic configuration: -AC_DEFINE([XML_NS], 1, - [Define to make XML Namespaces functionality available.]) -AC_DEFINE([XML_DTD], 1, - [Define to make parameter entity parsing functionality available.]) -AC_DEFINE([XML_CONTEXT_BYTES], 1024, - [Define to specify how much context to retain around the current parse point.]) - -AC_CONFIG_FILES(Makefile) -AC_OUTPUT - -abs_srcdir="`cd $srcdir && pwd`" -abs_builddir="`pwd`" -if test "$abs_srcdir" != "$abs_builddir"; then - make mkdir-init -fi diff --git a/cextern/expat/conftools/PrintPath b/cextern/expat/conftools/PrintPath deleted file mode 100755 index e8559a3d643e..000000000000 --- a/cextern/expat/conftools/PrintPath +++ /dev/null @@ -1,116 +0,0 @@ -#!/bin/sh -# Look for program[s] somewhere in $PATH. -# -# Options: -# -s -# Do not print out full pathname. (silent) -# -pPATHNAME -# Look in PATHNAME instead of $PATH -# -# Usage: -# PrintPath [-s] [-pPATHNAME] program [program ...] -# -# Initially written by Jim Jagielski for the Apache configuration mechanism -# (with kudos to Kernighan/Pike) -# -# This script falls under the Apache License. -# See http://www.apache.org/licenses/LICENSE - -## -# Some "constants" -## -pathname=$PATH -echo="yes" - -## -# Find out what OS we are running for later on -## -os=`(uname) 2>/dev/null` - -## -# Parse command line -## -for args in $* -do - case $args in - -s ) echo="no" ;; - -p* ) pathname="`echo $args | sed 's/^..//'`" ;; - * ) programs="$programs $args" ;; - esac -done - -## -# Now we make the adjustments required for OS/2 and everyone -# else :) -# -# First of all, all OS/2 programs have the '.exe' extension. -# Next, we adjust PATH (or what was given to us as PATH) to -# be whitespace separated directories. -# Finally, we try to determine the best flag to use for -# test/[] to look for an executable file. OS/2 just has '-r' -# but with other OSs, we do some funny stuff to check to see -# if test/[] knows about -x, which is the preferred flag. -## - -if [ "x$os" = "xOS/2" ] -then - ext=".exe" - pathname=`echo -E $pathname | - sed 's/^;/.;/ - s/;;/;.;/g - s/;$/;./ - s/;/ /g - s/\\\\/\\//g' ` - test_exec_flag="-r" -else - ext="" # No default extensions - pathname=`echo $pathname | - sed 's/^:/.:/ - s/::/:.:/g - s/:$/:./ - s/:/ /g' ` - # Here is how we test to see if test/[] can handle -x - testfile="pp.t.$$" - - cat > $testfile </dev/null`; then - test_exec_flag="-x" - else - test_exec_flag="-r" - fi - rm -f $testfile -fi - -for program in $programs -do - for path in $pathname - do - if [ $test_exec_flag $path/${program}${ext} ] && \ - [ ! -d $path/${program}${ext} ]; then - if [ "x$echo" = "xyes" ]; then - echo $path/${program}${ext} - fi - exit 0 - fi - -# Next try without extension (if one was used above) - if [ "x$ext" != "x" ]; then - if [ $test_exec_flag $path/${program} ] && \ - [ ! -d $path/${program} ]; then - if [ "x$echo" = "xyes" ]; then - echo $path/${program} - fi - exit 0 - fi - fi - done -done -exit 1 - diff --git a/cextern/expat/conftools/ac_c_bigendian_cross.m4 b/cextern/expat/conftools/ac_c_bigendian_cross.m4 deleted file mode 100755 index 8ed3edb24fbf..000000000000 --- a/cextern/expat/conftools/ac_c_bigendian_cross.m4 +++ /dev/null @@ -1,81 +0,0 @@ -dnl @synopsis AC_C_BIGENDIAN_CROSS -dnl -dnl Check endianess even when crosscompiling -dnl (partially based on the original AC_C_BIGENDIAN). -dnl -dnl The implementation will create a binary, and instead of running -dnl the binary it will be grep'ed for some symbols that will look -dnl different for different endianess of the binary. -dnl -dnl @version $Id: ac_c_bigendian_cross.m4,v 1.2 2001/10/01 20:03:13 fdrake Exp $ -dnl @author Guido Draheim -dnl -AC_DEFUN([AC_C_BIGENDIAN_CROSS], -[AC_CACHE_CHECK(whether byte ordering is bigendian, ac_cv_c_bigendian, -[ac_cv_c_bigendian=unknown -# See if sys/param.h defines the BYTE_ORDER macro. -AC_TRY_COMPILE([#include -#include ], [ -#if !BYTE_ORDER || !BIG_ENDIAN || !LITTLE_ENDIAN - bogus endian macros -#endif], [# It does; now see whether it defined to BIG_ENDIAN or not. -AC_TRY_COMPILE([#include -#include ], [ -#if BYTE_ORDER != BIG_ENDIAN - not big endian -#endif], ac_cv_c_bigendian=yes, ac_cv_c_bigendian=no)]) -if test $ac_cv_c_bigendian = unknown; then -AC_TRY_RUN([main () { - /* Are we little or big endian? From Harbison&Steele. */ - union - { - long l; - char c[sizeof (long)]; - } u; - u.l = 1; - exit (u.c[sizeof (long) - 1] == 1); -}], ac_cv_c_bigendian=no, ac_cv_c_bigendian=yes, -[ echo $ac_n "cross-compiling... " 2>&AC_FD_MSG ]) -fi]) -if test $ac_cv_c_bigendian = unknown; then -AC_MSG_CHECKING(to probe for byte ordering) -[ -cat >conftest.c <&AC_FD_MSG - ac_cv_c_bigendian=yes - fi - if test `grep -l LiTTleEnDian conftest.o` ; then - echo $ac_n ' little endian probe OK, ' 1>&AC_FD_MSG - if test $ac_cv_c_bigendian = yes ; then - ac_cv_c_bigendian=unknown; - else - ac_cv_c_bigendian=no - fi - fi - echo $ac_n 'guessing bigendian ... ' >&AC_FD_MSG - fi - fi -AC_MSG_RESULT($ac_cv_c_bigendian) -fi -if test $ac_cv_c_bigendian = yes; then - AC_DEFINE(WORDS_BIGENDIAN, 1, [whether byteorder is bigendian]) - BYTEORDER=4321 -else - BYTEORDER=1234 -fi -AC_DEFINE_UNQUOTED(BYTEORDER, $BYTEORDER, [1234 = LIL_ENDIAN, 4321 = BIGENDIAN]) -if test $ac_cv_c_bigendian = unknown; then - AC_MSG_ERROR(unknown endianess - sorry, please pre-set ac_cv_c_bigendian) -fi -]) diff --git a/cextern/expat/conftools/config.guess b/cextern/expat/conftools/config.guess deleted file mode 100755 index 396482d6cb50..000000000000 --- a/cextern/expat/conftools/config.guess +++ /dev/null @@ -1,1500 +0,0 @@ -#! /bin/sh -# Attempt to guess a canonical system name. -# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, -# 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, -# Inc. - -timestamp='2006-07-02' - -# This file is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA -# 02110-1301, USA. -# -# As a special exception to the GNU General Public License, if you -# distribute this file as part of a program that contains a -# configuration script generated by Autoconf, you may include it under -# the same distribution terms that you use for the rest of that program. - - -# Originally written by Per Bothner . -# Please send patches to . Submit a context -# diff and a properly formatted ChangeLog entry. -# -# This script attempts to guess a canonical system name similar to -# config.sub. If it succeeds, it prints the system name on stdout, and -# exits with 0. Otherwise, it exits with 1. -# -# The plan is that this can be called by configure scripts if you -# don't specify an explicit build system type. - -me=`echo "$0" | sed -e 's,.*/,,'` - -usage="\ -Usage: $0 [OPTION] - -Output the configuration name of the system \`$me' is run on. - -Operation modes: - -h, --help print this help, then exit - -t, --time-stamp print date of last modification, then exit - -v, --version print version number, then exit - -Report bugs and patches to ." - -version="\ -GNU config.guess ($timestamp) - -Originally written by Per Bothner. -Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 -Free Software Foundation, Inc. - -This is free software; see the source for copying conditions. There is NO -warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." - -help=" -Try \`$me --help' for more information." - -# Parse command line -while test $# -gt 0 ; do - case $1 in - --time-stamp | --time* | -t ) - echo "$timestamp" ; exit ;; - --version | -v ) - echo "$version" ; exit ;; - --help | --h* | -h ) - echo "$usage"; exit ;; - -- ) # Stop option processing - shift; break ;; - - ) # Use stdin as input. - break ;; - -* ) - echo "$me: invalid option $1$help" >&2 - exit 1 ;; - * ) - break ;; - esac -done - -if test $# != 0; then - echo "$me: too many arguments$help" >&2 - exit 1 -fi - -trap 'exit 1' 1 2 15 - -# CC_FOR_BUILD -- compiler used by this script. Note that the use of a -# compiler to aid in system detection is discouraged as it requires -# temporary files to be created and, as you can see below, it is a -# headache to deal with in a portable fashion. - -# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still -# use `HOST_CC' if defined, but it is deprecated. - -# Portable tmp directory creation inspired by the Autoconf team. - -set_cc_for_build=' -trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; -trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; -: ${TMPDIR=/tmp} ; - { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || - { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || - { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || - { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; -dummy=$tmp/dummy ; -tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; -case $CC_FOR_BUILD,$HOST_CC,$CC in - ,,) echo "int x;" > $dummy.c ; - for c in cc gcc c89 c99 ; do - if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then - CC_FOR_BUILD="$c"; break ; - fi ; - done ; - if test x"$CC_FOR_BUILD" = x ; then - CC_FOR_BUILD=no_compiler_found ; - fi - ;; - ,,*) CC_FOR_BUILD=$CC ;; - ,*,*) CC_FOR_BUILD=$HOST_CC ;; -esac ; set_cc_for_build= ;' - -# This is needed to find uname on a Pyramid OSx when run in the BSD universe. -# (ghazi@noc.rutgers.edu 1994-08-24) -if (test -f /.attbin/uname) >/dev/null 2>&1 ; then - PATH=$PATH:/.attbin ; export PATH -fi - -UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown -UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown -UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown -UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown - -# Note: order is significant - the case branches are not exclusive. - -case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in - *:NetBSD:*:*) - # NetBSD (nbsd) targets should (where applicable) match one or - # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*, - # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently - # switched to ELF, *-*-netbsd* would select the old - # object file format. This provides both forward - # compatibility and a consistent mechanism for selecting the - # object file format. - # - # Note: NetBSD doesn't particularly care about the vendor - # portion of the name. We always set it to "unknown". - sysctl="sysctl -n hw.machine_arch" - UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \ - /usr/sbin/$sysctl 2>/dev/null || echo unknown)` - case "${UNAME_MACHINE_ARCH}" in - armeb) machine=armeb-unknown ;; - arm*) machine=arm-unknown ;; - sh3el) machine=shl-unknown ;; - sh3eb) machine=sh-unknown ;; - *) machine=${UNAME_MACHINE_ARCH}-unknown ;; - esac - # The Operating System including object format, if it has switched - # to ELF recently, or will in the future. - case "${UNAME_MACHINE_ARCH}" in - arm*|i386|m68k|ns32k|sh3*|sparc|vax) - eval $set_cc_for_build - if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ - | grep __ELF__ >/dev/null - then - # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). - # Return netbsd for either. FIX? - os=netbsd - else - os=netbsdelf - fi - ;; - *) - os=netbsd - ;; - esac - # The OS release - # Debian GNU/NetBSD machines have a different userland, and - # thus, need a distinct triplet. However, they do not need - # kernel version information, so it can be replaced with a - # suitable tag, in the style of linux-gnu. - case "${UNAME_VERSION}" in - Debian*) - release='-gnu' - ;; - *) - release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` - ;; - esac - # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: - # contains redundant information, the shorter form: - # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. - echo "${machine}-${os}${release}" - exit ;; - *:OpenBSD:*:*) - UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` - echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE} - exit ;; - *:ekkoBSD:*:*) - echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE} - exit ;; - *:SolidBSD:*:*) - echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE} - exit ;; - macppc:MirBSD:*:*) - echo powerpc-unknown-mirbsd${UNAME_RELEASE} - exit ;; - *:MirBSD:*:*) - echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE} - exit ;; - alpha:OSF1:*:*) - case $UNAME_RELEASE in - *4.0) - UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` - ;; - *5.*) - UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` - ;; - esac - # According to Compaq, /usr/sbin/psrinfo has been available on - # OSF/1 and Tru64 systems produced since 1995. I hope that - # covers most systems running today. This code pipes the CPU - # types through head -n 1, so we only detect the type of CPU 0. - ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` - case "$ALPHA_CPU_TYPE" in - "EV4 (21064)") - UNAME_MACHINE="alpha" ;; - "EV4.5 (21064)") - UNAME_MACHINE="alpha" ;; - "LCA4 (21066/21068)") - UNAME_MACHINE="alpha" ;; - "EV5 (21164)") - UNAME_MACHINE="alphaev5" ;; - "EV5.6 (21164A)") - UNAME_MACHINE="alphaev56" ;; - "EV5.6 (21164PC)") - UNAME_MACHINE="alphapca56" ;; - "EV5.7 (21164PC)") - UNAME_MACHINE="alphapca57" ;; - "EV6 (21264)") - UNAME_MACHINE="alphaev6" ;; - "EV6.7 (21264A)") - UNAME_MACHINE="alphaev67" ;; - "EV6.8CB (21264C)") - UNAME_MACHINE="alphaev68" ;; - "EV6.8AL (21264B)") - UNAME_MACHINE="alphaev68" ;; - "EV6.8CX (21264D)") - UNAME_MACHINE="alphaev68" ;; - "EV6.9A (21264/EV69A)") - UNAME_MACHINE="alphaev69" ;; - "EV7 (21364)") - UNAME_MACHINE="alphaev7" ;; - "EV7.9 (21364A)") - UNAME_MACHINE="alphaev79" ;; - esac - # A Pn.n version is a patched version. - # A Vn.n version is a released version. - # A Tn.n version is a released field test version. - # A Xn.n version is an unreleased experimental baselevel. - # 1.2 uses "1.2" for uname -r. - echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` - exit ;; - Alpha\ *:Windows_NT*:*) - # How do we know it's Interix rather than the generic POSIX subsystem? - # Should we change UNAME_MACHINE based on the output of uname instead - # of the specific Alpha model? - echo alpha-pc-interix - exit ;; - 21064:Windows_NT:50:3) - echo alpha-dec-winnt3.5 - exit ;; - Amiga*:UNIX_System_V:4.0:*) - echo m68k-unknown-sysv4 - exit ;; - *:[Aa]miga[Oo][Ss]:*:*) - echo ${UNAME_MACHINE}-unknown-amigaos - exit ;; - *:[Mm]orph[Oo][Ss]:*:*) - echo ${UNAME_MACHINE}-unknown-morphos - exit ;; - *:OS/390:*:*) - echo i370-ibm-openedition - exit ;; - *:z/VM:*:*) - echo s390-ibm-zvmoe - exit ;; - *:OS400:*:*) - echo powerpc-ibm-os400 - exit ;; - arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) - echo arm-acorn-riscix${UNAME_RELEASE} - exit ;; - arm:riscos:*:*|arm:RISCOS:*:*) - echo arm-unknown-riscos - exit ;; - SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) - echo hppa1.1-hitachi-hiuxmpp - exit ;; - Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) - # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. - if test "`(/bin/universe) 2>/dev/null`" = att ; then - echo pyramid-pyramid-sysv3 - else - echo pyramid-pyramid-bsd - fi - exit ;; - NILE*:*:*:dcosx) - echo pyramid-pyramid-svr4 - exit ;; - DRS?6000:unix:4.0:6*) - echo sparc-icl-nx6 - exit ;; - DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) - case `/usr/bin/uname -p` in - sparc) echo sparc-icl-nx7; exit ;; - esac ;; - sun4H:SunOS:5.*:*) - echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit ;; - sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) - echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit ;; - i86pc:SunOS:5.*:*) - echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit ;; - sun4*:SunOS:6*:*) - # According to config.sub, this is the proper way to canonicalize - # SunOS6. Hard to guess exactly what SunOS6 will be like, but - # it's likely to be more like Solaris than SunOS4. - echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit ;; - sun4*:SunOS:*:*) - case "`/usr/bin/arch -k`" in - Series*|S4*) - UNAME_RELEASE=`uname -v` - ;; - esac - # Japanese Language versions have a version number like `4.1.3-JL'. - echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` - exit ;; - sun3*:SunOS:*:*) - echo m68k-sun-sunos${UNAME_RELEASE} - exit ;; - sun*:*:4.2BSD:*) - UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` - test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 - case "`/bin/arch`" in - sun3) - echo m68k-sun-sunos${UNAME_RELEASE} - ;; - sun4) - echo sparc-sun-sunos${UNAME_RELEASE} - ;; - esac - exit ;; - aushp:SunOS:*:*) - echo sparc-auspex-sunos${UNAME_RELEASE} - exit ;; - # The situation for MiNT is a little confusing. The machine name - # can be virtually everything (everything which is not - # "atarist" or "atariste" at least should have a processor - # > m68000). The system name ranges from "MiNT" over "FreeMiNT" - # to the lowercase version "mint" (or "freemint"). Finally - # the system name "TOS" denotes a system which is actually not - # MiNT. But MiNT is downward compatible to TOS, so this should - # be no problem. - atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) - echo m68k-atari-mint${UNAME_RELEASE} - exit ;; - atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) - echo m68k-atari-mint${UNAME_RELEASE} - exit ;; - *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) - echo m68k-atari-mint${UNAME_RELEASE} - exit ;; - milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) - echo m68k-milan-mint${UNAME_RELEASE} - exit ;; - hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) - echo m68k-hades-mint${UNAME_RELEASE} - exit ;; - *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) - echo m68k-unknown-mint${UNAME_RELEASE} - exit ;; - m68k:machten:*:*) - echo m68k-apple-machten${UNAME_RELEASE} - exit ;; - powerpc:machten:*:*) - echo powerpc-apple-machten${UNAME_RELEASE} - exit ;; - RISC*:Mach:*:*) - echo mips-dec-mach_bsd4.3 - exit ;; - RISC*:ULTRIX:*:*) - echo mips-dec-ultrix${UNAME_RELEASE} - exit ;; - VAX*:ULTRIX*:*:*) - echo vax-dec-ultrix${UNAME_RELEASE} - exit ;; - 2020:CLIX:*:* | 2430:CLIX:*:*) - echo clipper-intergraph-clix${UNAME_RELEASE} - exit ;; - mips:*:*:UMIPS | mips:*:*:RISCos) - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c -#ifdef __cplusplus -#include /* for printf() prototype */ - int main (int argc, char *argv[]) { -#else - int main (argc, argv) int argc; char *argv[]; { -#endif - #if defined (host_mips) && defined (MIPSEB) - #if defined (SYSTYPE_SYSV) - printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); - #endif - #if defined (SYSTYPE_SVR4) - printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); - #endif - #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) - printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); - #endif - #endif - exit (-1); - } -EOF - $CC_FOR_BUILD -o $dummy $dummy.c && - dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` && - SYSTEM_NAME=`$dummy $dummyarg` && - { echo "$SYSTEM_NAME"; exit; } - echo mips-mips-riscos${UNAME_RELEASE} - exit ;; - Motorola:PowerMAX_OS:*:*) - echo powerpc-motorola-powermax - exit ;; - Motorola:*:4.3:PL8-*) - echo powerpc-harris-powermax - exit ;; - Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) - echo powerpc-harris-powermax - exit ;; - Night_Hawk:Power_UNIX:*:*) - echo powerpc-harris-powerunix - exit ;; - m88k:CX/UX:7*:*) - echo m88k-harris-cxux7 - exit ;; - m88k:*:4*:R4*) - echo m88k-motorola-sysv4 - exit ;; - m88k:*:3*:R3*) - echo m88k-motorola-sysv3 - exit ;; - AViiON:dgux:*:*) - # DG/UX returns AViiON for all architectures - UNAME_PROCESSOR=`/usr/bin/uname -p` - if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] - then - if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ - [ ${TARGET_BINARY_INTERFACE}x = x ] - then - echo m88k-dg-dgux${UNAME_RELEASE} - else - echo m88k-dg-dguxbcs${UNAME_RELEASE} - fi - else - echo i586-dg-dgux${UNAME_RELEASE} - fi - exit ;; - M88*:DolphinOS:*:*) # DolphinOS (SVR3) - echo m88k-dolphin-sysv3 - exit ;; - M88*:*:R3*:*) - # Delta 88k system running SVR3 - echo m88k-motorola-sysv3 - exit ;; - XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) - echo m88k-tektronix-sysv3 - exit ;; - Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) - echo m68k-tektronix-bsd - exit ;; - *:IRIX*:*:*) - echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` - exit ;; - ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. - echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id - exit ;; # Note that: echo "'`uname -s`'" gives 'AIX ' - i*86:AIX:*:*) - echo i386-ibm-aix - exit ;; - ia64:AIX:*:*) - if [ -x /usr/bin/oslevel ] ; then - IBM_REV=`/usr/bin/oslevel` - else - IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} - fi - echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} - exit ;; - *:AIX:2:3) - if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c - #include - - main() - { - if (!__power_pc()) - exit(1); - puts("powerpc-ibm-aix3.2.5"); - exit(0); - } -EOF - if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` - then - echo "$SYSTEM_NAME" - else - echo rs6000-ibm-aix3.2.5 - fi - elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then - echo rs6000-ibm-aix3.2.4 - else - echo rs6000-ibm-aix3.2 - fi - exit ;; - *:AIX:*:[45]) - IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` - if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then - IBM_ARCH=rs6000 - else - IBM_ARCH=powerpc - fi - if [ -x /usr/bin/oslevel ] ; then - IBM_REV=`/usr/bin/oslevel` - else - IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} - fi - echo ${IBM_ARCH}-ibm-aix${IBM_REV} - exit ;; - *:AIX:*:*) - echo rs6000-ibm-aix - exit ;; - ibmrt:4.4BSD:*|romp-ibm:BSD:*) - echo romp-ibm-bsd4.4 - exit ;; - ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and - echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to - exit ;; # report: romp-ibm BSD 4.3 - *:BOSX:*:*) - echo rs6000-bull-bosx - exit ;; - DPX/2?00:B.O.S.:*:*) - echo m68k-bull-sysv3 - exit ;; - 9000/[34]??:4.3bsd:1.*:*) - echo m68k-hp-bsd - exit ;; - hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) - echo m68k-hp-bsd4.4 - exit ;; - 9000/[34678]??:HP-UX:*:*) - HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` - case "${UNAME_MACHINE}" in - 9000/31? ) HP_ARCH=m68000 ;; - 9000/[34]?? ) HP_ARCH=m68k ;; - 9000/[678][0-9][0-9]) - if [ -x /usr/bin/getconf ]; then - sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` - sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` - case "${sc_cpu_version}" in - 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 - 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 - 532) # CPU_PA_RISC2_0 - case "${sc_kernel_bits}" in - 32) HP_ARCH="hppa2.0n" ;; - 64) HP_ARCH="hppa2.0w" ;; - '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20 - esac ;; - esac - fi - if [ "${HP_ARCH}" = "" ]; then - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c - - #define _HPUX_SOURCE - #include - #include - - int main () - { - #if defined(_SC_KERNEL_BITS) - long bits = sysconf(_SC_KERNEL_BITS); - #endif - long cpu = sysconf (_SC_CPU_VERSION); - - switch (cpu) - { - case CPU_PA_RISC1_0: puts ("hppa1.0"); break; - case CPU_PA_RISC1_1: puts ("hppa1.1"); break; - case CPU_PA_RISC2_0: - #if defined(_SC_KERNEL_BITS) - switch (bits) - { - case 64: puts ("hppa2.0w"); break; - case 32: puts ("hppa2.0n"); break; - default: puts ("hppa2.0"); break; - } break; - #else /* !defined(_SC_KERNEL_BITS) */ - puts ("hppa2.0"); break; - #endif - default: puts ("hppa1.0"); break; - } - exit (0); - } -EOF - (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` - test -z "$HP_ARCH" && HP_ARCH=hppa - fi ;; - esac - if [ ${HP_ARCH} = "hppa2.0w" ] - then - eval $set_cc_for_build - - # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating - # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler - # generating 64-bit code. GNU and HP use different nomenclature: - # - # $ CC_FOR_BUILD=cc ./config.guess - # => hppa2.0w-hp-hpux11.23 - # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess - # => hppa64-hp-hpux11.23 - - if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | - grep __LP64__ >/dev/null - then - HP_ARCH="hppa2.0w" - else - HP_ARCH="hppa64" - fi - fi - echo ${HP_ARCH}-hp-hpux${HPUX_REV} - exit ;; - ia64:HP-UX:*:*) - HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` - echo ia64-hp-hpux${HPUX_REV} - exit ;; - 3050*:HI-UX:*:*) - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c - #include - int - main () - { - long cpu = sysconf (_SC_CPU_VERSION); - /* The order matters, because CPU_IS_HP_MC68K erroneously returns - true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct - results, however. */ - if (CPU_IS_PA_RISC (cpu)) - { - switch (cpu) - { - case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; - case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; - case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; - default: puts ("hppa-hitachi-hiuxwe2"); break; - } - } - else if (CPU_IS_HP_MC68K (cpu)) - puts ("m68k-hitachi-hiuxwe2"); - else puts ("unknown-hitachi-hiuxwe2"); - exit (0); - } -EOF - $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` && - { echo "$SYSTEM_NAME"; exit; } - echo unknown-hitachi-hiuxwe2 - exit ;; - 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) - echo hppa1.1-hp-bsd - exit ;; - 9000/8??:4.3bsd:*:*) - echo hppa1.0-hp-bsd - exit ;; - *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) - echo hppa1.0-hp-mpeix - exit ;; - hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) - echo hppa1.1-hp-osf - exit ;; - hp8??:OSF1:*:*) - echo hppa1.0-hp-osf - exit ;; - i*86:OSF1:*:*) - if [ -x /usr/sbin/sysversion ] ; then - echo ${UNAME_MACHINE}-unknown-osf1mk - else - echo ${UNAME_MACHINE}-unknown-osf1 - fi - exit ;; - parisc*:Lites*:*:*) - echo hppa1.1-hp-lites - exit ;; - C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) - echo c1-convex-bsd - exit ;; - C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) - if getsysinfo -f scalar_acc - then echo c32-convex-bsd - else echo c2-convex-bsd - fi - exit ;; - C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) - echo c34-convex-bsd - exit ;; - C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) - echo c38-convex-bsd - exit ;; - C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) - echo c4-convex-bsd - exit ;; - CRAY*Y-MP:*:*:*) - echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' - exit ;; - CRAY*[A-Z]90:*:*:*) - echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ - | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ - -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ - -e 's/\.[^.]*$/.X/' - exit ;; - CRAY*TS:*:*:*) - echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' - exit ;; - CRAY*T3E:*:*:*) - echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' - exit ;; - CRAY*SV1:*:*:*) - echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' - exit ;; - *:UNICOS/mp:*:*) - echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' - exit ;; - F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) - FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` - FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` - FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` - echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" - exit ;; - 5000:UNIX_System_V:4.*:*) - FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` - FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'` - echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" - exit ;; - i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) - echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} - exit ;; - sparc*:BSD/OS:*:*) - echo sparc-unknown-bsdi${UNAME_RELEASE} - exit ;; - *:BSD/OS:*:*) - echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} - exit ;; - *:FreeBSD:*:*) - case ${UNAME_MACHINE} in - pc98) - echo i386-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; - amd64) - echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; - *) - echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; - esac - exit ;; - i*:CYGWIN*:*) - echo ${UNAME_MACHINE}-pc-cygwin - exit ;; - i*:MINGW*:*) - echo ${UNAME_MACHINE}-pc-mingw32 - exit ;; - i*:windows32*:*) - # uname -m includes "-pc" on this system. - echo ${UNAME_MACHINE}-mingw32 - exit ;; - i*:PW*:*) - echo ${UNAME_MACHINE}-pc-pw32 - exit ;; - x86:Interix*:[3456]*) - echo i586-pc-interix${UNAME_RELEASE} - exit ;; - EM64T:Interix*:[3456]*) - echo x86_64-unknown-interix${UNAME_RELEASE} - exit ;; - [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) - echo i${UNAME_MACHINE}-pc-mks - exit ;; - i*:Windows_NT*:* | Pentium*:Windows_NT*:*) - # How do we know it's Interix rather than the generic POSIX subsystem? - # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we - # UNAME_MACHINE based on the output of uname instead of i386? - echo i586-pc-interix - exit ;; - i*:UWIN*:*) - echo ${UNAME_MACHINE}-pc-uwin - exit ;; - amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) - echo x86_64-unknown-cygwin - exit ;; - p*:CYGWIN*:*) - echo powerpcle-unknown-cygwin - exit ;; - prep*:SunOS:5.*:*) - echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit ;; - *:GNU:*:*) - # the GNU system - echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` - exit ;; - *:GNU/*:*:*) - # other systems with GNU libc and userland - echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu - exit ;; - i*86:Minix:*:*) - echo ${UNAME_MACHINE}-pc-minix - exit ;; - arm*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu - exit ;; - avr32*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu - exit ;; - cris:Linux:*:*) - echo cris-axis-linux-gnu - exit ;; - crisv32:Linux:*:*) - echo crisv32-axis-linux-gnu - exit ;; - frv:Linux:*:*) - echo frv-unknown-linux-gnu - exit ;; - ia64:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu - exit ;; - m32r*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu - exit ;; - m68*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu - exit ;; - mips:Linux:*:*) - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c - #undef CPU - #undef mips - #undef mipsel - #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) - CPU=mipsel - #else - #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) - CPU=mips - #else - CPU= - #endif - #endif -EOF - eval "`$CC_FOR_BUILD -E $dummy.c 2>/dev/null | sed -n ' - /^CPU/{ - s: ::g - p - }'`" - test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; } - ;; - mips64:Linux:*:*) - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c - #undef CPU - #undef mips64 - #undef mips64el - #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) - CPU=mips64el - #else - #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) - CPU=mips64 - #else - CPU= - #endif - #endif -EOF - eval "`$CC_FOR_BUILD -E $dummy.c 2>/dev/null | sed -n ' - /^CPU/{ - s: ::g - p - }'`" - test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; } - ;; - or32:Linux:*:*) - echo or32-unknown-linux-gnu - exit ;; - ppc:Linux:*:*) - echo powerpc-unknown-linux-gnu - exit ;; - ppc64:Linux:*:*) - echo powerpc64-unknown-linux-gnu - exit ;; - alpha:Linux:*:*) - case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in - EV5) UNAME_MACHINE=alphaev5 ;; - EV56) UNAME_MACHINE=alphaev56 ;; - PCA56) UNAME_MACHINE=alphapca56 ;; - PCA57) UNAME_MACHINE=alphapca56 ;; - EV6) UNAME_MACHINE=alphaev6 ;; - EV67) UNAME_MACHINE=alphaev67 ;; - EV68*) UNAME_MACHINE=alphaev68 ;; - esac - objdump --private-headers /bin/sh | grep ld.so.1 >/dev/null - if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi - echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC} - exit ;; - parisc:Linux:*:* | hppa:Linux:*:*) - # Look for CPU level - case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in - PA7*) echo hppa1.1-unknown-linux-gnu ;; - PA8*) echo hppa2.0-unknown-linux-gnu ;; - *) echo hppa-unknown-linux-gnu ;; - esac - exit ;; - parisc64:Linux:*:* | hppa64:Linux:*:*) - echo hppa64-unknown-linux-gnu - exit ;; - s390:Linux:*:* | s390x:Linux:*:*) - echo ${UNAME_MACHINE}-ibm-linux - exit ;; - sh64*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu - exit ;; - sh*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu - exit ;; - sparc:Linux:*:* | sparc64:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu - exit ;; - vax:Linux:*:*) - echo ${UNAME_MACHINE}-dec-linux-gnu - exit ;; - x86_64:Linux:*:*) - echo x86_64-unknown-linux-gnu - exit ;; - i*86:Linux:*:*) - # The BFD linker knows what the default object file format is, so - # first see if it will tell us. cd to the root directory to prevent - # problems with other programs or directories called `ld' in the path. - # Set LC_ALL=C to ensure ld outputs messages in English. - ld_supported_targets=`cd /; LC_ALL=C ld --help 2>&1 \ - | sed -ne '/supported targets:/!d - s/[ ][ ]*/ /g - s/.*supported targets: *// - s/ .*// - p'` - case "$ld_supported_targets" in - elf32-i386) - TENTATIVE="${UNAME_MACHINE}-pc-linux-gnu" - ;; - a.out-i386-linux) - echo "${UNAME_MACHINE}-pc-linux-gnuaout" - exit ;; - coff-i386) - echo "${UNAME_MACHINE}-pc-linux-gnucoff" - exit ;; - "") - # Either a pre-BFD a.out linker (linux-gnuoldld) or - # one that does not give us useful --help. - echo "${UNAME_MACHINE}-pc-linux-gnuoldld" - exit ;; - esac - # Determine whether the default compiler is a.out or elf - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c - #include - #ifdef __ELF__ - # ifdef __GLIBC__ - # if __GLIBC__ >= 2 - LIBC=gnu - # else - LIBC=gnulibc1 - # endif - # else - LIBC=gnulibc1 - # endif - #else - #if defined(__INTEL_COMPILER) || defined(__PGI) || defined(__SUNPRO_C) || defined(__SUNPRO_CC) - LIBC=gnu - #else - LIBC=gnuaout - #endif - #endif - #ifdef __dietlibc__ - LIBC=dietlibc - #endif -EOF - eval "`$CC_FOR_BUILD -E $dummy.c 2>/dev/null | sed -n ' - /^LIBC/{ - s: ::g - p - }'`" - test x"${LIBC}" != x && { - echo "${UNAME_MACHINE}-pc-linux-${LIBC}" - exit - } - test x"${TENTATIVE}" != x && { echo "${TENTATIVE}"; exit; } - ;; - i*86:DYNIX/ptx:4*:*) - # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. - # earlier versions are messed up and put the nodename in both - # sysname and nodename. - echo i386-sequent-sysv4 - exit ;; - i*86:UNIX_SV:4.2MP:2.*) - # Unixware is an offshoot of SVR4, but it has its own version - # number series starting with 2... - # I am not positive that other SVR4 systems won't match this, - # I just have to hope. -- rms. - # Use sysv4.2uw... so that sysv4* matches it. - echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} - exit ;; - i*86:OS/2:*:*) - # If we were able to find `uname', then EMX Unix compatibility - # is probably installed. - echo ${UNAME_MACHINE}-pc-os2-emx - exit ;; - i*86:XTS-300:*:STOP) - echo ${UNAME_MACHINE}-unknown-stop - exit ;; - i*86:atheos:*:*) - echo ${UNAME_MACHINE}-unknown-atheos - exit ;; - i*86:syllable:*:*) - echo ${UNAME_MACHINE}-pc-syllable - exit ;; - i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*) - echo i386-unknown-lynxos${UNAME_RELEASE} - exit ;; - i*86:*DOS:*:*) - echo ${UNAME_MACHINE}-pc-msdosdjgpp - exit ;; - i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) - UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` - if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then - echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} - else - echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} - fi - exit ;; - i*86:*:5:[678]*) - # UnixWare 7.x, OpenUNIX and OpenServer 6. - case `/bin/uname -X | grep "^Machine"` in - *486*) UNAME_MACHINE=i486 ;; - *Pentium) UNAME_MACHINE=i586 ;; - *Pent*|*Celeron) UNAME_MACHINE=i686 ;; - esac - echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} - exit ;; - i*86:*:3.2:*) - if test -f /usr/options/cb.name; then - UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then - UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` - (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 - (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ - && UNAME_MACHINE=i586 - (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ - && UNAME_MACHINE=i686 - (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ - && UNAME_MACHINE=i686 - echo ${UNAME_MACHINE}-pc-sco$UNAME_REL - else - echo ${UNAME_MACHINE}-pc-sysv32 - fi - exit ;; - pc:*:*:*) - # Left here for compatibility: - # uname -m prints for DJGPP always 'pc', but it prints nothing about - # the processor, so we play safe by assuming i386. - echo i386-pc-msdosdjgpp - exit ;; - Intel:Mach:3*:*) - echo i386-pc-mach3 - exit ;; - paragon:*:*:*) - echo i860-intel-osf1 - exit ;; - i860:*:4.*:*) # i860-SVR4 - if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then - echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 - else # Add other i860-SVR4 vendors below as they are discovered. - echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 - fi - exit ;; - mini*:CTIX:SYS*5:*) - # "miniframe" - echo m68010-convergent-sysv - exit ;; - mc68k:UNIX:SYSTEM5:3.51m) - echo m68k-convergent-sysv - exit ;; - M680?0:D-NIX:5.3:*) - echo m68k-diab-dnix - exit ;; - M68*:*:R3V[5678]*:*) - test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; - 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) - OS_REL='' - test -r /etc/.relid \ - && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` - /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ - && { echo i486-ncr-sysv4.3${OS_REL}; exit; } - /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ - && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; - 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) - /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ - && { echo i486-ncr-sysv4; exit; } ;; - m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) - echo m68k-unknown-lynxos${UNAME_RELEASE} - exit ;; - mc68030:UNIX_System_V:4.*:*) - echo m68k-atari-sysv4 - exit ;; - TSUNAMI:LynxOS:2.*:*) - echo sparc-unknown-lynxos${UNAME_RELEASE} - exit ;; - rs6000:LynxOS:2.*:*) - echo rs6000-unknown-lynxos${UNAME_RELEASE} - exit ;; - PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*) - echo powerpc-unknown-lynxos${UNAME_RELEASE} - exit ;; - SM[BE]S:UNIX_SV:*:*) - echo mips-dde-sysv${UNAME_RELEASE} - exit ;; - RM*:ReliantUNIX-*:*:*) - echo mips-sni-sysv4 - exit ;; - RM*:SINIX-*:*:*) - echo mips-sni-sysv4 - exit ;; - *:SINIX-*:*:*) - if uname -p 2>/dev/null >/dev/null ; then - UNAME_MACHINE=`(uname -p) 2>/dev/null` - echo ${UNAME_MACHINE}-sni-sysv4 - else - echo ns32k-sni-sysv - fi - exit ;; - PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort - # says - echo i586-unisys-sysv4 - exit ;; - *:UNIX_System_V:4*:FTX*) - # From Gerald Hewes . - # How about differentiating between stratus architectures? -djm - echo hppa1.1-stratus-sysv4 - exit ;; - *:*:*:FTX*) - # From seanf@swdc.stratus.com. - echo i860-stratus-sysv4 - exit ;; - i*86:VOS:*:*) - # From Paul.Green@stratus.com. - echo ${UNAME_MACHINE}-stratus-vos - exit ;; - *:VOS:*:*) - # From Paul.Green@stratus.com. - echo hppa1.1-stratus-vos - exit ;; - mc68*:A/UX:*:*) - echo m68k-apple-aux${UNAME_RELEASE} - exit ;; - news*:NEWS-OS:6*:*) - echo mips-sony-newsos6 - exit ;; - R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) - if [ -d /usr/nec ]; then - echo mips-nec-sysv${UNAME_RELEASE} - else - echo mips-unknown-sysv${UNAME_RELEASE} - fi - exit ;; - BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. - echo powerpc-be-beos - exit ;; - BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. - echo powerpc-apple-beos - exit ;; - BePC:BeOS:*:*) # BeOS running on Intel PC compatible. - echo i586-pc-beos - exit ;; - SX-4:SUPER-UX:*:*) - echo sx4-nec-superux${UNAME_RELEASE} - exit ;; - SX-5:SUPER-UX:*:*) - echo sx5-nec-superux${UNAME_RELEASE} - exit ;; - SX-6:SUPER-UX:*:*) - echo sx6-nec-superux${UNAME_RELEASE} - exit ;; - Power*:Rhapsody:*:*) - echo powerpc-apple-rhapsody${UNAME_RELEASE} - exit ;; - *:Rhapsody:*:*) - echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} - exit ;; - *:Darwin:*:*) - UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown - case $UNAME_PROCESSOR in - unknown) UNAME_PROCESSOR=powerpc ;; - esac - echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} - exit ;; - *:procnto*:*:* | *:QNX:[0123456789]*:*) - UNAME_PROCESSOR=`uname -p` - if test "$UNAME_PROCESSOR" = "x86"; then - UNAME_PROCESSOR=i386 - UNAME_MACHINE=pc - fi - echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE} - exit ;; - *:QNX:*:4*) - echo i386-pc-qnx - exit ;; - NSE-?:NONSTOP_KERNEL:*:*) - echo nse-tandem-nsk${UNAME_RELEASE} - exit ;; - NSR-?:NONSTOP_KERNEL:*:*) - echo nsr-tandem-nsk${UNAME_RELEASE} - exit ;; - *:NonStop-UX:*:*) - echo mips-compaq-nonstopux - exit ;; - BS2000:POSIX*:*:*) - echo bs2000-siemens-sysv - exit ;; - DS/*:UNIX_System_V:*:*) - echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} - exit ;; - *:Plan9:*:*) - # "uname -m" is not consistent, so use $cputype instead. 386 - # is converted to i386 for consistency with other x86 - # operating systems. - if test "$cputype" = "386"; then - UNAME_MACHINE=i386 - else - UNAME_MACHINE="$cputype" - fi - echo ${UNAME_MACHINE}-unknown-plan9 - exit ;; - *:TOPS-10:*:*) - echo pdp10-unknown-tops10 - exit ;; - *:TENEX:*:*) - echo pdp10-unknown-tenex - exit ;; - KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) - echo pdp10-dec-tops20 - exit ;; - XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) - echo pdp10-xkl-tops20 - exit ;; - *:TOPS-20:*:*) - echo pdp10-unknown-tops20 - exit ;; - *:ITS:*:*) - echo pdp10-unknown-its - exit ;; - SEI:*:*:SEIUX) - echo mips-sei-seiux${UNAME_RELEASE} - exit ;; - *:DragonFly:*:*) - echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` - exit ;; - *:*VMS:*:*) - UNAME_MACHINE=`(uname -p) 2>/dev/null` - case "${UNAME_MACHINE}" in - A*) echo alpha-dec-vms ; exit ;; - I*) echo ia64-dec-vms ; exit ;; - V*) echo vax-dec-vms ; exit ;; - esac ;; - *:XENIX:*:SysV) - echo i386-pc-xenix - exit ;; - i*86:skyos:*:*) - echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//' - exit ;; - i*86:rdos:*:*) - echo ${UNAME_MACHINE}-pc-rdos - exit ;; -esac - -#echo '(No uname command or uname output not recognized.)' 1>&2 -#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2 - -eval $set_cc_for_build -cat >$dummy.c < -# include -#endif -main () -{ -#if defined (sony) -#if defined (MIPSEB) - /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, - I don't know.... */ - printf ("mips-sony-bsd\n"); exit (0); -#else -#include - printf ("m68k-sony-newsos%s\n", -#ifdef NEWSOS4 - "4" -#else - "" -#endif - ); exit (0); -#endif -#endif - -#if defined (__arm) && defined (__acorn) && defined (__unix) - printf ("arm-acorn-riscix\n"); exit (0); -#endif - -#if defined (hp300) && !defined (hpux) - printf ("m68k-hp-bsd\n"); exit (0); -#endif - -#if defined (NeXT) -#if !defined (__ARCHITECTURE__) -#define __ARCHITECTURE__ "m68k" -#endif - int version; - version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; - if (version < 4) - printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); - else - printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); - exit (0); -#endif - -#if defined (MULTIMAX) || defined (n16) -#if defined (UMAXV) - printf ("ns32k-encore-sysv\n"); exit (0); -#else -#if defined (CMU) - printf ("ns32k-encore-mach\n"); exit (0); -#else - printf ("ns32k-encore-bsd\n"); exit (0); -#endif -#endif -#endif - -#if defined (__386BSD__) - printf ("i386-pc-bsd\n"); exit (0); -#endif - -#if defined (sequent) -#if defined (i386) - printf ("i386-sequent-dynix\n"); exit (0); -#endif -#if defined (ns32000) - printf ("ns32k-sequent-dynix\n"); exit (0); -#endif -#endif - -#if defined (_SEQUENT_) - struct utsname un; - - uname(&un); - - if (strncmp(un.version, "V2", 2) == 0) { - printf ("i386-sequent-ptx2\n"); exit (0); - } - if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ - printf ("i386-sequent-ptx1\n"); exit (0); - } - printf ("i386-sequent-ptx\n"); exit (0); - -#endif - -#if defined (vax) -# if !defined (ultrix) -# include -# if defined (BSD) -# if BSD == 43 - printf ("vax-dec-bsd4.3\n"); exit (0); -# else -# if BSD == 199006 - printf ("vax-dec-bsd4.3reno\n"); exit (0); -# else - printf ("vax-dec-bsd\n"); exit (0); -# endif -# endif -# else - printf ("vax-dec-bsd\n"); exit (0); -# endif -# else - printf ("vax-dec-ultrix\n"); exit (0); -# endif -#endif - -#if defined (alliant) && defined (i860) - printf ("i860-alliant-bsd\n"); exit (0); -#endif - - exit (1); -} -EOF - -$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && SYSTEM_NAME=`$dummy` && - { echo "$SYSTEM_NAME"; exit; } - -# Apollos put the system type in the environment. - -test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; } - -# Convex versions that predate uname can use getsysinfo(1) - -if [ -x /usr/convex/getsysinfo ] -then - case `getsysinfo -f cpu_type` in - c1*) - echo c1-convex-bsd - exit ;; - c2*) - if getsysinfo -f scalar_acc - then echo c32-convex-bsd - else echo c2-convex-bsd - fi - exit ;; - c34*) - echo c34-convex-bsd - exit ;; - c38*) - echo c38-convex-bsd - exit ;; - c4*) - echo c4-convex-bsd - exit ;; - esac -fi - -cat >&2 < in order to provide the needed -information to handle your system. - -config.guess timestamp = $timestamp - -uname -m = `(uname -m) 2>/dev/null || echo unknown` -uname -r = `(uname -r) 2>/dev/null || echo unknown` -uname -s = `(uname -s) 2>/dev/null || echo unknown` -uname -v = `(uname -v) 2>/dev/null || echo unknown` - -/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` -/bin/uname -X = `(/bin/uname -X) 2>/dev/null` - -hostinfo = `(hostinfo) 2>/dev/null` -/bin/universe = `(/bin/universe) 2>/dev/null` -/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` -/bin/arch = `(/bin/arch) 2>/dev/null` -/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` -/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` - -UNAME_MACHINE = ${UNAME_MACHINE} -UNAME_RELEASE = ${UNAME_RELEASE} -UNAME_SYSTEM = ${UNAME_SYSTEM} -UNAME_VERSION = ${UNAME_VERSION} -EOF - -exit 1 - -# Local variables: -# eval: (add-hook 'write-file-hooks 'time-stamp) -# time-stamp-start: "timestamp='" -# time-stamp-format: "%:y-%02m-%02d" -# time-stamp-end: "'" -# End: diff --git a/cextern/expat/conftools/config.sub b/cextern/expat/conftools/config.sub deleted file mode 100755 index fab0aa355663..000000000000 --- a/cextern/expat/conftools/config.sub +++ /dev/null @@ -1,1616 +0,0 @@ -#! /bin/sh -# Configuration validation subroutine script. -# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, -# 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, -# Inc. - -timestamp='2006-09-20' - -# This file is (in principle) common to ALL GNU software. -# The presence of a machine in this file suggests that SOME GNU software -# can handle that machine. It does not imply ALL GNU software can. -# -# This file is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA -# 02110-1301, USA. -# -# As a special exception to the GNU General Public License, if you -# distribute this file as part of a program that contains a -# configuration script generated by Autoconf, you may include it under -# the same distribution terms that you use for the rest of that program. - - -# Please send patches to . Submit a context -# diff and a properly formatted ChangeLog entry. -# -# Configuration subroutine to validate and canonicalize a configuration type. -# Supply the specified configuration type as an argument. -# If it is invalid, we print an error message on stderr and exit with code 1. -# Otherwise, we print the canonical config type on stdout and succeed. - -# This file is supposed to be the same for all GNU packages -# and recognize all the CPU types, system types and aliases -# that are meaningful with *any* GNU software. -# Each package is responsible for reporting which valid configurations -# it does not support. The user should be able to distinguish -# a failure to support a valid configuration from a meaningless -# configuration. - -# The goal of this file is to map all the various variations of a given -# machine specification into a single specification in the form: -# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM -# or in some cases, the newer four-part form: -# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM -# It is wrong to echo any other type of specification. - -me=`echo "$0" | sed -e 's,.*/,,'` - -usage="\ -Usage: $0 [OPTION] CPU-MFR-OPSYS - $0 [OPTION] ALIAS - -Canonicalize a configuration name. - -Operation modes: - -h, --help print this help, then exit - -t, --time-stamp print date of last modification, then exit - -v, --version print version number, then exit - -Report bugs and patches to ." - -version="\ -GNU config.sub ($timestamp) - -Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 -Free Software Foundation, Inc. - -This is free software; see the source for copying conditions. There is NO -warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." - -help=" -Try \`$me --help' for more information." - -# Parse command line -while test $# -gt 0 ; do - case $1 in - --time-stamp | --time* | -t ) - echo "$timestamp" ; exit ;; - --version | -v ) - echo "$version" ; exit ;; - --help | --h* | -h ) - echo "$usage"; exit ;; - -- ) # Stop option processing - shift; break ;; - - ) # Use stdin as input. - break ;; - -* ) - echo "$me: invalid option $1$help" - exit 1 ;; - - *local*) - # First pass through any local machine types. - echo $1 - exit ;; - - * ) - break ;; - esac -done - -case $# in - 0) echo "$me: missing argument$help" >&2 - exit 1;; - 1) ;; - *) echo "$me: too many arguments$help" >&2 - exit 1;; -esac - -# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). -# Here we must recognize all the valid KERNEL-OS combinations. -maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` -case $maybe_os in - nto-qnx* | linux-gnu* | linux-dietlibc | linux-newlib* | linux-uclibc* | \ - uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* | \ - storm-chaos* | os2-emx* | rtmk-nova*) - os=-$maybe_os - basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` - ;; - *) - basic_machine=`echo $1 | sed 's/-[^-]*$//'` - if [ $basic_machine != $1 ] - then os=`echo $1 | sed 's/.*-/-/'` - else os=; fi - ;; -esac - -### Let's recognize common machines as not being operating systems so -### that things like config.sub decstation-3100 work. We also -### recognize some manufacturers as not being operating systems, so we -### can provide default operating systems below. -case $os in - -sun*os*) - # Prevent following clause from handling this invalid input. - ;; - -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ - -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ - -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ - -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ - -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ - -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ - -apple | -axis | -knuth | -cray) - os= - basic_machine=$1 - ;; - -sim | -cisco | -oki | -wec | -winbond) - os= - basic_machine=$1 - ;; - -scout) - ;; - -wrs) - os=-vxworks - basic_machine=$1 - ;; - -chorusos*) - os=-chorusos - basic_machine=$1 - ;; - -chorusrdb) - os=-chorusrdb - basic_machine=$1 - ;; - -hiux*) - os=-hiuxwe2 - ;; - -sco6) - os=-sco5v6 - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -sco5) - os=-sco3.2v5 - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -sco4) - os=-sco3.2v4 - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -sco3.2.[4-9]*) - os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -sco3.2v[4-9]*) - # Don't forget version if it is 3.2v4 or newer. - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -sco5v6*) - # Don't forget version if it is 3.2v4 or newer. - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -sco*) - os=-sco3.2v2 - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -udk*) - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -isc) - os=-isc2.2 - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -clix*) - basic_machine=clipper-intergraph - ;; - -isc*) - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -lynx*) - os=-lynxos - ;; - -ptx*) - basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` - ;; - -windowsnt*) - os=`echo $os | sed -e 's/windowsnt/winnt/'` - ;; - -psos*) - os=-psos - ;; - -mint | -mint[0-9]*) - basic_machine=m68k-atari - os=-mint - ;; -esac - -# Decode aliases for certain CPU-COMPANY combinations. -case $basic_machine in - # Recognize the basic CPU types without company name. - # Some are omitted here because they have special meanings below. - 1750a | 580 \ - | a29k \ - | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ - | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ - | am33_2.0 \ - | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr | avr32 \ - | bfin \ - | c4x | clipper \ - | d10v | d30v | dlx | dsp16xx \ - | fr30 | frv \ - | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ - | i370 | i860 | i960 | ia64 \ - | ip2k | iq2000 \ - | m32c | m32r | m32rle | m68000 | m68k | m88k \ - | maxq | mb | microblaze | mcore \ - | mips | mipsbe | mipseb | mipsel | mipsle \ - | mips16 \ - | mips64 | mips64el \ - | mips64vr | mips64vrel \ - | mips64orion | mips64orionel \ - | mips64vr4100 | mips64vr4100el \ - | mips64vr4300 | mips64vr4300el \ - | mips64vr5000 | mips64vr5000el \ - | mips64vr5900 | mips64vr5900el \ - | mipsisa32 | mipsisa32el \ - | mipsisa32r2 | mipsisa32r2el \ - | mipsisa64 | mipsisa64el \ - | mipsisa64r2 | mipsisa64r2el \ - | mipsisa64sb1 | mipsisa64sb1el \ - | mipsisa64sr71k | mipsisa64sr71kel \ - | mipstx39 | mipstx39el \ - | mn10200 | mn10300 \ - | mt \ - | msp430 \ - | nios | nios2 \ - | ns16k | ns32k \ - | or32 \ - | pdp10 | pdp11 | pj | pjl \ - | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \ - | pyramid \ - | score \ - | sh | sh[1234] | sh[24]a | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ - | sh64 | sh64le \ - | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \ - | sparcv8 | sparcv9 | sparcv9b | sparcv9v \ - | spu | strongarm \ - | tahoe | thumb | tic4x | tic80 | tron \ - | v850 | v850e \ - | we32k \ - | x86 | xc16x | xscale | xscalee[bl] | xstormy16 | xtensa \ - | z8k) - basic_machine=$basic_machine-unknown - ;; - m6811 | m68hc11 | m6812 | m68hc12) - # Motorola 68HC11/12. - basic_machine=$basic_machine-unknown - os=-none - ;; - m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) - ;; - ms1) - basic_machine=mt-unknown - ;; - - # We use `pc' rather than `unknown' - # because (1) that's what they normally are, and - # (2) the word "unknown" tends to confuse beginning users. - i*86 | x86_64) - basic_machine=$basic_machine-pc - ;; - # Object if more than one company name word. - *-*-*) - echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 - exit 1 - ;; - # Recognize the basic CPU types with company name. - 580-* \ - | a29k-* \ - | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ - | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ - | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \ - | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ - | avr-* | avr32-* \ - | bfin-* | bs2000-* \ - | c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* | c55x-* | c6x-* \ - | clipper-* | craynv-* | cydra-* \ - | d10v-* | d30v-* | dlx-* \ - | elxsi-* \ - | f30[01]-* | f700-* | fr30-* | frv-* | fx80-* \ - | h8300-* | h8500-* \ - | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ - | i*86-* | i860-* | i960-* | ia64-* \ - | ip2k-* | iq2000-* \ - | m32c-* | m32r-* | m32rle-* \ - | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ - | m88110-* | m88k-* | maxq-* | mcore-* \ - | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ - | mips16-* \ - | mips64-* | mips64el-* \ - | mips64vr-* | mips64vrel-* \ - | mips64orion-* | mips64orionel-* \ - | mips64vr4100-* | mips64vr4100el-* \ - | mips64vr4300-* | mips64vr4300el-* \ - | mips64vr5000-* | mips64vr5000el-* \ - | mips64vr5900-* | mips64vr5900el-* \ - | mipsisa32-* | mipsisa32el-* \ - | mipsisa32r2-* | mipsisa32r2el-* \ - | mipsisa64-* | mipsisa64el-* \ - | mipsisa64r2-* | mipsisa64r2el-* \ - | mipsisa64sb1-* | mipsisa64sb1el-* \ - | mipsisa64sr71k-* | mipsisa64sr71kel-* \ - | mipstx39-* | mipstx39el-* \ - | mmix-* \ - | mt-* \ - | msp430-* \ - | nios-* | nios2-* \ - | none-* | np1-* | ns16k-* | ns32k-* \ - | orion-* \ - | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ - | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \ - | pyramid-* \ - | romp-* | rs6000-* \ - | sh-* | sh[1234]-* | sh[24]a-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ - | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ - | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \ - | sparclite-* \ - | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | strongarm-* | sv1-* | sx?-* \ - | tahoe-* | thumb-* \ - | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ - | tron-* \ - | v850-* | v850e-* | vax-* \ - | we32k-* \ - | x86-* | x86_64-* | xc16x-* | xps100-* | xscale-* | xscalee[bl]-* \ - | xstormy16-* | xtensa-* \ - | ymp-* \ - | z8k-*) - ;; - # Recognize the various machine names and aliases which stand - # for a CPU type and a company and sometimes even an OS. - 386bsd) - basic_machine=i386-unknown - os=-bsd - ;; - 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) - basic_machine=m68000-att - ;; - 3b*) - basic_machine=we32k-att - ;; - a29khif) - basic_machine=a29k-amd - os=-udi - ;; - abacus) - basic_machine=abacus-unknown - ;; - adobe68k) - basic_machine=m68010-adobe - os=-scout - ;; - alliant | fx80) - basic_machine=fx80-alliant - ;; - altos | altos3068) - basic_machine=m68k-altos - ;; - am29k) - basic_machine=a29k-none - os=-bsd - ;; - amd64) - basic_machine=x86_64-pc - ;; - amd64-*) - basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - amdahl) - basic_machine=580-amdahl - os=-sysv - ;; - amiga | amiga-*) - basic_machine=m68k-unknown - ;; - amigaos | amigados) - basic_machine=m68k-unknown - os=-amigaos - ;; - amigaunix | amix) - basic_machine=m68k-unknown - os=-sysv4 - ;; - apollo68) - basic_machine=m68k-apollo - os=-sysv - ;; - apollo68bsd) - basic_machine=m68k-apollo - os=-bsd - ;; - aux) - basic_machine=m68k-apple - os=-aux - ;; - balance) - basic_machine=ns32k-sequent - os=-dynix - ;; - c90) - basic_machine=c90-cray - os=-unicos - ;; - convex-c1) - basic_machine=c1-convex - os=-bsd - ;; - convex-c2) - basic_machine=c2-convex - os=-bsd - ;; - convex-c32) - basic_machine=c32-convex - os=-bsd - ;; - convex-c34) - basic_machine=c34-convex - os=-bsd - ;; - convex-c38) - basic_machine=c38-convex - os=-bsd - ;; - cray | j90) - basic_machine=j90-cray - os=-unicos - ;; - craynv) - basic_machine=craynv-cray - os=-unicosmp - ;; - cr16c) - basic_machine=cr16c-unknown - os=-elf - ;; - crds | unos) - basic_machine=m68k-crds - ;; - crisv32 | crisv32-* | etraxfs*) - basic_machine=crisv32-axis - ;; - cris | cris-* | etrax*) - basic_machine=cris-axis - ;; - crx) - basic_machine=crx-unknown - os=-elf - ;; - da30 | da30-*) - basic_machine=m68k-da30 - ;; - decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) - basic_machine=mips-dec - ;; - decsystem10* | dec10*) - basic_machine=pdp10-dec - os=-tops10 - ;; - decsystem20* | dec20*) - basic_machine=pdp10-dec - os=-tops20 - ;; - delta | 3300 | motorola-3300 | motorola-delta \ - | 3300-motorola | delta-motorola) - basic_machine=m68k-motorola - ;; - delta88) - basic_machine=m88k-motorola - os=-sysv3 - ;; - djgpp) - basic_machine=i586-pc - os=-msdosdjgpp - ;; - dpx20 | dpx20-*) - basic_machine=rs6000-bull - os=-bosx - ;; - dpx2* | dpx2*-bull) - basic_machine=m68k-bull - os=-sysv3 - ;; - ebmon29k) - basic_machine=a29k-amd - os=-ebmon - ;; - elxsi) - basic_machine=elxsi-elxsi - os=-bsd - ;; - encore | umax | mmax) - basic_machine=ns32k-encore - ;; - es1800 | OSE68k | ose68k | ose | OSE) - basic_machine=m68k-ericsson - os=-ose - ;; - fx2800) - basic_machine=i860-alliant - ;; - genix) - basic_machine=ns32k-ns - ;; - gmicro) - basic_machine=tron-gmicro - os=-sysv - ;; - go32) - basic_machine=i386-pc - os=-go32 - ;; - h3050r* | hiux*) - basic_machine=hppa1.1-hitachi - os=-hiuxwe2 - ;; - h8300hms) - basic_machine=h8300-hitachi - os=-hms - ;; - h8300xray) - basic_machine=h8300-hitachi - os=-xray - ;; - h8500hms) - basic_machine=h8500-hitachi - os=-hms - ;; - harris) - basic_machine=m88k-harris - os=-sysv3 - ;; - hp300-*) - basic_machine=m68k-hp - ;; - hp300bsd) - basic_machine=m68k-hp - os=-bsd - ;; - hp300hpux) - basic_machine=m68k-hp - os=-hpux - ;; - hp3k9[0-9][0-9] | hp9[0-9][0-9]) - basic_machine=hppa1.0-hp - ;; - hp9k2[0-9][0-9] | hp9k31[0-9]) - basic_machine=m68000-hp - ;; - hp9k3[2-9][0-9]) - basic_machine=m68k-hp - ;; - hp9k6[0-9][0-9] | hp6[0-9][0-9]) - basic_machine=hppa1.0-hp - ;; - hp9k7[0-79][0-9] | hp7[0-79][0-9]) - basic_machine=hppa1.1-hp - ;; - hp9k78[0-9] | hp78[0-9]) - # FIXME: really hppa2.0-hp - basic_machine=hppa1.1-hp - ;; - hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) - # FIXME: really hppa2.0-hp - basic_machine=hppa1.1-hp - ;; - hp9k8[0-9][13679] | hp8[0-9][13679]) - basic_machine=hppa1.1-hp - ;; - hp9k8[0-9][0-9] | hp8[0-9][0-9]) - basic_machine=hppa1.0-hp - ;; - hppa-next) - os=-nextstep3 - ;; - hppaosf) - basic_machine=hppa1.1-hp - os=-osf - ;; - hppro) - basic_machine=hppa1.1-hp - os=-proelf - ;; - i370-ibm* | ibm*) - basic_machine=i370-ibm - ;; -# I'm not sure what "Sysv32" means. Should this be sysv3.2? - i*86v32) - basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` - os=-sysv32 - ;; - i*86v4*) - basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` - os=-sysv4 - ;; - i*86v) - basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` - os=-sysv - ;; - i*86sol2) - basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` - os=-solaris2 - ;; - i386mach) - basic_machine=i386-mach - os=-mach - ;; - i386-vsta | vsta) - basic_machine=i386-unknown - os=-vsta - ;; - iris | iris4d) - basic_machine=mips-sgi - case $os in - -irix*) - ;; - *) - os=-irix4 - ;; - esac - ;; - isi68 | isi) - basic_machine=m68k-isi - os=-sysv - ;; - m88k-omron*) - basic_machine=m88k-omron - ;; - magnum | m3230) - basic_machine=mips-mips - os=-sysv - ;; - merlin) - basic_machine=ns32k-utek - os=-sysv - ;; - mingw32) - basic_machine=i386-pc - os=-mingw32 - ;; - miniframe) - basic_machine=m68000-convergent - ;; - *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) - basic_machine=m68k-atari - os=-mint - ;; - mips3*-*) - basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` - ;; - mips3*) - basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown - ;; - monitor) - basic_machine=m68k-rom68k - os=-coff - ;; - morphos) - basic_machine=powerpc-unknown - os=-morphos - ;; - msdos) - basic_machine=i386-pc - os=-msdos - ;; - ms1-*) - basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'` - ;; - mvs) - basic_machine=i370-ibm - os=-mvs - ;; - ncr3000) - basic_machine=i486-ncr - os=-sysv4 - ;; - netbsd386) - basic_machine=i386-unknown - os=-netbsd - ;; - netwinder) - basic_machine=armv4l-rebel - os=-linux - ;; - news | news700 | news800 | news900) - basic_machine=m68k-sony - os=-newsos - ;; - news1000) - basic_machine=m68030-sony - os=-newsos - ;; - news-3600 | risc-news) - basic_machine=mips-sony - os=-newsos - ;; - necv70) - basic_machine=v70-nec - os=-sysv - ;; - next | m*-next ) - basic_machine=m68k-next - case $os in - -nextstep* ) - ;; - -ns2*) - os=-nextstep2 - ;; - *) - os=-nextstep3 - ;; - esac - ;; - nh3000) - basic_machine=m68k-harris - os=-cxux - ;; - nh[45]000) - basic_machine=m88k-harris - os=-cxux - ;; - nindy960) - basic_machine=i960-intel - os=-nindy - ;; - mon960) - basic_machine=i960-intel - os=-mon960 - ;; - nonstopux) - basic_machine=mips-compaq - os=-nonstopux - ;; - np1) - basic_machine=np1-gould - ;; - nsr-tandem) - basic_machine=nsr-tandem - ;; - op50n-* | op60c-*) - basic_machine=hppa1.1-oki - os=-proelf - ;; - openrisc | openrisc-*) - basic_machine=or32-unknown - ;; - os400) - basic_machine=powerpc-ibm - os=-os400 - ;; - OSE68000 | ose68000) - basic_machine=m68000-ericsson - os=-ose - ;; - os68k) - basic_machine=m68k-none - os=-os68k - ;; - pa-hitachi) - basic_machine=hppa1.1-hitachi - os=-hiuxwe2 - ;; - paragon) - basic_machine=i860-intel - os=-osf - ;; - pbd) - basic_machine=sparc-tti - ;; - pbb) - basic_machine=m68k-tti - ;; - pc532 | pc532-*) - basic_machine=ns32k-pc532 - ;; - pc98) - basic_machine=i386-pc - ;; - pc98-*) - basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - pentium | p5 | k5 | k6 | nexgen | viac3) - basic_machine=i586-pc - ;; - pentiumpro | p6 | 6x86 | athlon | athlon_*) - basic_machine=i686-pc - ;; - pentiumii | pentium2 | pentiumiii | pentium3) - basic_machine=i686-pc - ;; - pentium4) - basic_machine=i786-pc - ;; - pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) - basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - pentiumpro-* | p6-* | 6x86-* | athlon-*) - basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) - basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - pentium4-*) - basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - pn) - basic_machine=pn-gould - ;; - power) basic_machine=power-ibm - ;; - ppc) basic_machine=powerpc-unknown - ;; - ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - ppcle | powerpclittle | ppc-le | powerpc-little) - basic_machine=powerpcle-unknown - ;; - ppcle-* | powerpclittle-*) - basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - ppc64) basic_machine=powerpc64-unknown - ;; - ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - ppc64le | powerpc64little | ppc64-le | powerpc64-little) - basic_machine=powerpc64le-unknown - ;; - ppc64le-* | powerpc64little-*) - basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - ps2) - basic_machine=i386-ibm - ;; - pw32) - basic_machine=i586-unknown - os=-pw32 - ;; - rdos) - basic_machine=i386-pc - os=-rdos - ;; - rom68k) - basic_machine=m68k-rom68k - os=-coff - ;; - rm[46]00) - basic_machine=mips-siemens - ;; - rtpc | rtpc-*) - basic_machine=romp-ibm - ;; - s390 | s390-*) - basic_machine=s390-ibm - ;; - s390x | s390x-*) - basic_machine=s390x-ibm - ;; - sa29200) - basic_machine=a29k-amd - os=-udi - ;; - sb1) - basic_machine=mipsisa64sb1-unknown - ;; - sb1el) - basic_machine=mipsisa64sb1el-unknown - ;; - sde) - basic_machine=mipsisa32-sde - os=-elf - ;; - sei) - basic_machine=mips-sei - os=-seiux - ;; - sequent) - basic_machine=i386-sequent - ;; - sh) - basic_machine=sh-hitachi - os=-hms - ;; - sh64) - basic_machine=sh64-unknown - ;; - sparclite-wrs | simso-wrs) - basic_machine=sparclite-wrs - os=-vxworks - ;; - sps7) - basic_machine=m68k-bull - os=-sysv2 - ;; - spur) - basic_machine=spur-unknown - ;; - st2000) - basic_machine=m68k-tandem - ;; - stratus) - basic_machine=i860-stratus - os=-sysv4 - ;; - sun2) - basic_machine=m68000-sun - ;; - sun2os3) - basic_machine=m68000-sun - os=-sunos3 - ;; - sun2os4) - basic_machine=m68000-sun - os=-sunos4 - ;; - sun3os3) - basic_machine=m68k-sun - os=-sunos3 - ;; - sun3os4) - basic_machine=m68k-sun - os=-sunos4 - ;; - sun4os3) - basic_machine=sparc-sun - os=-sunos3 - ;; - sun4os4) - basic_machine=sparc-sun - os=-sunos4 - ;; - sun4sol2) - basic_machine=sparc-sun - os=-solaris2 - ;; - sun3 | sun3-*) - basic_machine=m68k-sun - ;; - sun4) - basic_machine=sparc-sun - ;; - sun386 | sun386i | roadrunner) - basic_machine=i386-sun - ;; - sv1) - basic_machine=sv1-cray - os=-unicos - ;; - symmetry) - basic_machine=i386-sequent - os=-dynix - ;; - t3e) - basic_machine=alphaev5-cray - os=-unicos - ;; - t90) - basic_machine=t90-cray - os=-unicos - ;; - tic54x | c54x*) - basic_machine=tic54x-unknown - os=-coff - ;; - tic55x | c55x*) - basic_machine=tic55x-unknown - os=-coff - ;; - tic6x | c6x*) - basic_machine=tic6x-unknown - os=-coff - ;; - tx39) - basic_machine=mipstx39-unknown - ;; - tx39el) - basic_machine=mipstx39el-unknown - ;; - toad1) - basic_machine=pdp10-xkl - os=-tops20 - ;; - tower | tower-32) - basic_machine=m68k-ncr - ;; - tpf) - basic_machine=s390x-ibm - os=-tpf - ;; - udi29k) - basic_machine=a29k-amd - os=-udi - ;; - ultra3) - basic_machine=a29k-nyu - os=-sym1 - ;; - v810 | necv810) - basic_machine=v810-nec - os=-none - ;; - vaxv) - basic_machine=vax-dec - os=-sysv - ;; - vms) - basic_machine=vax-dec - os=-vms - ;; - vpp*|vx|vx-*) - basic_machine=f301-fujitsu - ;; - vxworks960) - basic_machine=i960-wrs - os=-vxworks - ;; - vxworks68) - basic_machine=m68k-wrs - os=-vxworks - ;; - vxworks29k) - basic_machine=a29k-wrs - os=-vxworks - ;; - w65*) - basic_machine=w65-wdc - os=-none - ;; - w89k-*) - basic_machine=hppa1.1-winbond - os=-proelf - ;; - xbox) - basic_machine=i686-pc - os=-mingw32 - ;; - xps | xps100) - basic_machine=xps100-honeywell - ;; - ymp) - basic_machine=ymp-cray - os=-unicos - ;; - z8k-*-coff) - basic_machine=z8k-unknown - os=-sim - ;; - none) - basic_machine=none-none - os=-none - ;; - -# Here we handle the default manufacturer of certain CPU types. It is in -# some cases the only manufacturer, in others, it is the most popular. - w89k) - basic_machine=hppa1.1-winbond - ;; - op50n) - basic_machine=hppa1.1-oki - ;; - op60c) - basic_machine=hppa1.1-oki - ;; - romp) - basic_machine=romp-ibm - ;; - mmix) - basic_machine=mmix-knuth - ;; - rs6000) - basic_machine=rs6000-ibm - ;; - vax) - basic_machine=vax-dec - ;; - pdp10) - # there are many clones, so DEC is not a safe bet - basic_machine=pdp10-unknown - ;; - pdp11) - basic_machine=pdp11-dec - ;; - we32k) - basic_machine=we32k-att - ;; - sh[1234] | sh[24]a | sh[34]eb | sh[1234]le | sh[23]ele) - basic_machine=sh-unknown - ;; - sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v) - basic_machine=sparc-sun - ;; - cydra) - basic_machine=cydra-cydrome - ;; - orion) - basic_machine=orion-highlevel - ;; - orion105) - basic_machine=clipper-highlevel - ;; - mac | mpw | mac-mpw) - basic_machine=m68k-apple - ;; - pmac | pmac-mpw) - basic_machine=powerpc-apple - ;; - *-unknown) - # Make sure to match an already-canonicalized machine name. - ;; - *) - echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 - exit 1 - ;; -esac - -# Here we canonicalize certain aliases for manufacturers. -case $basic_machine in - *-digital*) - basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` - ;; - *-commodore*) - basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` - ;; - *) - ;; -esac - -# Decode manufacturer-specific aliases for certain operating systems. - -if [ x"$os" != x"" ] -then -case $os in - # First match some system type aliases - # that might get confused with valid system types. - # -solaris* is a basic system type, with this one exception. - -solaris1 | -solaris1.*) - os=`echo $os | sed -e 's|solaris1|sunos4|'` - ;; - -solaris) - os=-solaris2 - ;; - -svr4*) - os=-sysv4 - ;; - -unixware*) - os=-sysv4.2uw - ;; - -gnu/linux*) - os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` - ;; - # First accept the basic system types. - # The portable systems comes first. - # Each alternative MUST END IN A *, to match a version number. - # -sysv* is not here because it comes later, after sysvr4. - -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ - | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\ - | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \ - | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ - | -aos* \ - | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ - | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ - | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \ - | -openbsd* | -solidbsd* \ - | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ - | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ - | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ - | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ - | -chorusos* | -chorusrdb* \ - | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ - | -mingw32* | -linux-gnu* | -linux-newlib* | -linux-uclibc* \ - | -uxpv* | -beos* | -mpeix* | -udk* \ - | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ - | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ - | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ - | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ - | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ - | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \ - | -skyos* | -haiku* | -rdos* | -toppers*) - # Remember, each alternative MUST END IN *, to match a version number. - ;; - -qnx*) - case $basic_machine in - x86-* | i*86-*) - ;; - *) - os=-nto$os - ;; - esac - ;; - -nto-qnx*) - ;; - -nto*) - os=`echo $os | sed -e 's|nto|nto-qnx|'` - ;; - -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ - | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \ - | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) - ;; - -mac*) - os=`echo $os | sed -e 's|mac|macos|'` - ;; - -linux-dietlibc) - os=-linux-dietlibc - ;; - -linux*) - os=`echo $os | sed -e 's|linux|linux-gnu|'` - ;; - -sunos5*) - os=`echo $os | sed -e 's|sunos5|solaris2|'` - ;; - -sunos6*) - os=`echo $os | sed -e 's|sunos6|solaris3|'` - ;; - -opened*) - os=-openedition - ;; - -os400*) - os=-os400 - ;; - -wince*) - os=-wince - ;; - -osfrose*) - os=-osfrose - ;; - -osf*) - os=-osf - ;; - -utek*) - os=-bsd - ;; - -dynix*) - os=-bsd - ;; - -acis*) - os=-aos - ;; - -atheos*) - os=-atheos - ;; - -syllable*) - os=-syllable - ;; - -386bsd) - os=-bsd - ;; - -ctix* | -uts*) - os=-sysv - ;; - -nova*) - os=-rtmk-nova - ;; - -ns2 ) - os=-nextstep2 - ;; - -nsk*) - os=-nsk - ;; - # Preserve the version number of sinix5. - -sinix5.*) - os=`echo $os | sed -e 's|sinix|sysv|'` - ;; - -sinix*) - os=-sysv4 - ;; - -tpf*) - os=-tpf - ;; - -triton*) - os=-sysv3 - ;; - -oss*) - os=-sysv3 - ;; - -svr4) - os=-sysv4 - ;; - -svr3) - os=-sysv3 - ;; - -sysvr4) - os=-sysv4 - ;; - # This must come after -sysvr4. - -sysv*) - ;; - -ose*) - os=-ose - ;; - -es1800*) - os=-ose - ;; - -xenix) - os=-xenix - ;; - -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) - os=-mint - ;; - -aros*) - os=-aros - ;; - -kaos*) - os=-kaos - ;; - -zvmoe) - os=-zvmoe - ;; - -none) - ;; - *) - # Get rid of the `-' at the beginning of $os. - os=`echo $os | sed 's/[^-]*-//'` - echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 - exit 1 - ;; -esac -else - -# Here we handle the default operating systems that come with various machines. -# The value should be what the vendor currently ships out the door with their -# machine or put another way, the most popular os provided with the machine. - -# Note that if you're going to try to match "-MANUFACTURER" here (say, -# "-sun"), then you have to tell the case statement up towards the top -# that MANUFACTURER isn't an operating system. Otherwise, code above -# will signal an error saying that MANUFACTURER isn't an operating -# system, and we'll never get to this point. - -case $basic_machine in - score-*) - os=-elf - ;; - spu-*) - os=-elf - ;; - *-acorn) - os=-riscix1.2 - ;; - arm*-rebel) - os=-linux - ;; - arm*-semi) - os=-aout - ;; - c4x-* | tic4x-*) - os=-coff - ;; - # This must come before the *-dec entry. - pdp10-*) - os=-tops20 - ;; - pdp11-*) - os=-none - ;; - *-dec | vax-*) - os=-ultrix4.2 - ;; - m68*-apollo) - os=-domain - ;; - i386-sun) - os=-sunos4.0.2 - ;; - m68000-sun) - os=-sunos3 - # This also exists in the configure program, but was not the - # default. - # os=-sunos4 - ;; - m68*-cisco) - os=-aout - ;; - mips*-cisco) - os=-elf - ;; - mips*-*) - os=-elf - ;; - or32-*) - os=-coff - ;; - *-tti) # must be before sparc entry or we get the wrong os. - os=-sysv3 - ;; - sparc-* | *-sun) - os=-sunos4.1.1 - ;; - *-be) - os=-beos - ;; - *-haiku) - os=-haiku - ;; - *-ibm) - os=-aix - ;; - *-knuth) - os=-mmixware - ;; - *-wec) - os=-proelf - ;; - *-winbond) - os=-proelf - ;; - *-oki) - os=-proelf - ;; - *-hp) - os=-hpux - ;; - *-hitachi) - os=-hiux - ;; - i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) - os=-sysv - ;; - *-cbm) - os=-amigaos - ;; - *-dg) - os=-dgux - ;; - *-dolphin) - os=-sysv3 - ;; - m68k-ccur) - os=-rtu - ;; - m88k-omron*) - os=-luna - ;; - *-next ) - os=-nextstep - ;; - *-sequent) - os=-ptx - ;; - *-crds) - os=-unos - ;; - *-ns) - os=-genix - ;; - i370-*) - os=-mvs - ;; - *-next) - os=-nextstep3 - ;; - *-gould) - os=-sysv - ;; - *-highlevel) - os=-bsd - ;; - *-encore) - os=-bsd - ;; - *-sgi) - os=-irix - ;; - *-siemens) - os=-sysv4 - ;; - *-masscomp) - os=-rtu - ;; - f30[01]-fujitsu | f700-fujitsu) - os=-uxpv - ;; - *-rom68k) - os=-coff - ;; - *-*bug) - os=-coff - ;; - *-apple) - os=-macos - ;; - *-atari*) - os=-mint - ;; - *) - os=-none - ;; -esac -fi - -# Here we handle the case where we know the os, and the CPU type, but not the -# manufacturer. We pick the logical manufacturer. -vendor=unknown -case $basic_machine in - *-unknown) - case $os in - -riscix*) - vendor=acorn - ;; - -sunos*) - vendor=sun - ;; - -aix*) - vendor=ibm - ;; - -beos*) - vendor=be - ;; - -hpux*) - vendor=hp - ;; - -mpeix*) - vendor=hp - ;; - -hiux*) - vendor=hitachi - ;; - -unos*) - vendor=crds - ;; - -dgux*) - vendor=dg - ;; - -luna*) - vendor=omron - ;; - -genix*) - vendor=ns - ;; - -mvs* | -opened*) - vendor=ibm - ;; - -os400*) - vendor=ibm - ;; - -ptx*) - vendor=sequent - ;; - -tpf*) - vendor=ibm - ;; - -vxsim* | -vxworks* | -windiss*) - vendor=wrs - ;; - -aux*) - vendor=apple - ;; - -hms*) - vendor=hitachi - ;; - -mpw* | -macos*) - vendor=apple - ;; - -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) - vendor=atari - ;; - -vos*) - vendor=stratus - ;; - esac - basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` - ;; -esac - -echo $basic_machine$os -exit - -# Local variables: -# eval: (add-hook 'write-file-hooks 'time-stamp) -# time-stamp-start: "timestamp='" -# time-stamp-format: "%:y-%02m-%02d" -# time-stamp-end: "'" -# End: diff --git a/cextern/expat/conftools/expat.m4 b/cextern/expat/conftools/expat.m4 deleted file mode 100755 index e5d4871c9ebb..000000000000 --- a/cextern/expat/conftools/expat.m4 +++ /dev/null @@ -1,43 +0,0 @@ -dnl Check if --with-expat[=PREFIX] is specified and -dnl Expat >= 1.95.0 is installed in the system. -dnl If yes, substitute EXPAT_CFLAGS, EXPAT_LIBS with regard to -dnl the specified PREFIX and set with_expat to PREFIX, or 'yes' if PREFIX -dnl has not been specified. Also HAVE_LIBEXPAT, HAVE_EXPAT_H are defined. -dnl If --with-expat has not been specified, set with_expat to 'no'. -dnl In addition, an Automake conditional EXPAT_INSTALLED is set accordingly. -dnl This is necessary to adapt a whole lot of packages that have expat -dnl bundled as a static library. -AC_DEFUN(AM_WITH_EXPAT, -[ AC_ARG_WITH(expat, - [ --with-expat=PREFIX Use system Expat library], - , with_expat=no) - - AM_CONDITIONAL(EXPAT_INSTALLED, test $with_expat != no) - - EXPAT_CFLAGS= - EXPAT_LIBS= - if test $with_expat != no; then - if test $with_expat != yes; then - EXPAT_CFLAGS="-I$with_expat/include" - EXPAT_LIBS="-L$with_expat/lib" - fi - AC_CHECK_LIB(expat, XML_ParserCreate, - [ EXPAT_LIBS="$EXPAT_LIBS -lexpat" - expat_found=yes ], - [ expat_found=no ], - "$EXPAT_LIBS") - if test $expat_found = no; then - AC_MSG_ERROR([Could not find the Expat library]) - fi - expat_save_CFLAGS="$CFLAGS" - CFLAGS="$CFLAGS $EXPAT_CFLAGS" - AC_CHECK_HEADERS(expat.h, , expat_found=no) - if test $expat_found = no; then - AC_MSG_ERROR([Could not find expat.h]) - fi - CFLAGS="$expat_save_CFLAGS" - fi - - AC_SUBST(EXPAT_CFLAGS) - AC_SUBST(EXPAT_LIBS) -]) diff --git a/cextern/expat/conftools/get-version.sh b/cextern/expat/conftools/get-version.sh deleted file mode 100755 index a70e0fb47a84..000000000000 --- a/cextern/expat/conftools/get-version.sh +++ /dev/null @@ -1,46 +0,0 @@ -#!/bin/sh -# -# USAGE: get-version.sh path/to/expat.h -# -# This script will print Expat's version number on stdout. For example: -# -# $ ./conftools/get-version.sh ./lib/expat.h -# 1.95.3 -# $ -# - -if test $# = 0; then - echo "ERROR: pathname for expat.h was not provided." - echo "" - echo "USAGE: $0 path/to/expat.h" - exit 1 -fi -if test $# != 1; then - echo "ERROR: too many arguments were provided." - echo "" - echo "USAGE: $0 path/to/expat.h" - exit 1 -fi - -hdr="$1" -if test ! -r "$hdr"; then - echo "ERROR: '$hdr' does not exist, or is not readable." - exit 1 -fi - -MAJOR_VERSION="`sed -n -e '/MAJOR_VERSION/s/[^0-9]*//gp' $hdr`" -MINOR_VERSION="`sed -n -e '/MINOR_VERSION/s/[^0-9]*//gp' $hdr`" -MICRO_VERSION="`sed -n -e '/MICRO_VERSION/s/[^0-9]*//gp' $hdr`" - -# Determine how to tell echo not to print the trailing \n. This is -# similar to Autoconf's @ECHO_C@ and @ECHO_N@; however, we don't -# generate this file via autoconf (in fact, get-version.sh is used -# to *create* ./configure), so we just do something similar inline. -case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in - *c*,-n*) ECHO_N= ECHO_C=' -' ;; - *c*,* ) ECHO_N=-n ECHO_C= ;; - *) ECHO_N= ECHO_C='\c' ;; -esac - -echo $ECHO_N "$MAJOR_VERSION.$MINOR_VERSION.$MICRO_VERSION$ECHO_C" diff --git a/cextern/expat/conftools/install-sh b/cextern/expat/conftools/install-sh deleted file mode 100755 index e9de23842dcd..000000000000 --- a/cextern/expat/conftools/install-sh +++ /dev/null @@ -1,251 +0,0 @@ -#!/bin/sh -# -# install - install a program, script, or datafile -# This comes from X11R5 (mit/util/scripts/install.sh). -# -# Copyright 1991 by the Massachusetts Institute of Technology -# -# Permission to use, copy, modify, distribute, and sell this software and its -# documentation for any purpose is hereby granted without fee, provided that -# the above copyright notice appear in all copies and that both that -# copyright notice and this permission notice appear in supporting -# documentation, and that the name of M.I.T. not be used in advertising or -# publicity pertaining to distribution of the software without specific, -# written prior permission. M.I.T. makes no representations about the -# suitability of this software for any purpose. It is provided "as is" -# without express or implied warranty. -# -# Calling this script install-sh is preferred over install.sh, to prevent -# `make' implicit rules from creating a file called install from it -# when there is no Makefile. -# -# This script is compatible with the BSD install script, but was written -# from scratch. It can only install one file at a time, a restriction -# shared with many OS's install programs. - - -# set DOITPROG to echo to test this script - -# Don't use :- since 4.3BSD and earlier shells don't like it. -doit="${DOITPROG-}" - - -# put in absolute paths if you don't have them in your path; or use env. vars. - -mvprog="${MVPROG-mv}" -cpprog="${CPPROG-cp}" -chmodprog="${CHMODPROG-chmod}" -chownprog="${CHOWNPROG-chown}" -chgrpprog="${CHGRPPROG-chgrp}" -stripprog="${STRIPPROG-strip}" -rmprog="${RMPROG-rm}" -mkdirprog="${MKDIRPROG-mkdir}" - -transformbasename="" -transform_arg="" -instcmd="$mvprog" -chmodcmd="$chmodprog 0755" -chowncmd="" -chgrpcmd="" -stripcmd="" -rmcmd="$rmprog -f" -mvcmd="$mvprog" -src="" -dst="" -dir_arg="" - -while [ x"$1" != x ]; do - case $1 in - -c) instcmd="$cpprog" - shift - continue;; - - -d) dir_arg=true - shift - continue;; - - -m) chmodcmd="$chmodprog $2" - shift - shift - continue;; - - -o) chowncmd="$chownprog $2" - shift - shift - continue;; - - -g) chgrpcmd="$chgrpprog $2" - shift - shift - continue;; - - -s) stripcmd="$stripprog" - shift - continue;; - - -t=*) transformarg=`echo $1 | sed 's/-t=//'` - shift - continue;; - - -b=*) transformbasename=`echo $1 | sed 's/-b=//'` - shift - continue;; - - *) if [ x"$src" = x ] - then - src=$1 - else - # this colon is to work around a 386BSD /bin/sh bug - : - dst=$1 - fi - shift - continue;; - esac -done - -if [ x"$src" = x ] -then - echo "install: no input file specified" - exit 1 -else - true -fi - -if [ x"$dir_arg" != x ]; then - dst=$src - src="" - - if [ -d $dst ]; then - instcmd=: - chmodcmd="" - else - instcmd=mkdir - fi -else - -# Waiting for this to be detected by the "$instcmd $src $dsttmp" command -# might cause directories to be created, which would be especially bad -# if $src (and thus $dsttmp) contains '*'. - - if [ -f $src -o -d $src ] - then - true - else - echo "install: $src does not exist" - exit 1 - fi - - if [ x"$dst" = x ] - then - echo "install: no destination specified" - exit 1 - else - true - fi - -# If destination is a directory, append the input filename; if your system -# does not like double slashes in filenames, you may need to add some logic - - if [ -d $dst ] - then - dst="$dst"/`basename $src` - else - true - fi -fi - -## this sed command emulates the dirname command -dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` - -# Make sure that the destination directory exists. -# this part is taken from Noah Friedman's mkinstalldirs script - -# Skip lots of stat calls in the usual case. -if [ ! -d "$dstdir" ]; then -defaultIFS=' -' -IFS="${IFS-${defaultIFS}}" - -oIFS="${IFS}" -# Some sh's can't handle IFS=/ for some reason. -IFS='%' -set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'` -IFS="${oIFS}" - -pathcomp='' - -while [ $# -ne 0 ] ; do - pathcomp="${pathcomp}${1}" - shift - - if [ ! -d "${pathcomp}" ] ; - then - $mkdirprog "${pathcomp}" - else - true - fi - - pathcomp="${pathcomp}/" -done -fi - -if [ x"$dir_arg" != x ] -then - $doit $instcmd $dst && - - if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi && - if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi && - if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi && - if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi -else - -# If we're going to rename the final executable, determine the name now. - - if [ x"$transformarg" = x ] - then - dstfile=`basename $dst` - else - dstfile=`basename $dst $transformbasename | - sed $transformarg`$transformbasename - fi - -# don't allow the sed command to completely eliminate the filename - - if [ x"$dstfile" = x ] - then - dstfile=`basename $dst` - else - true - fi - -# Make a temp file name in the proper directory. - - dsttmp=$dstdir/#inst.$$# - -# Move or copy the file name to the temp name - - $doit $instcmd $src $dsttmp && - - trap "rm -f ${dsttmp}" 0 && - -# and set any options; do chmod last to preserve setuid bits - -# If any of these fail, we abort the whole thing. If we want to -# ignore errors from any of these, just make sure not to ignore -# errors from the above "$doit $instcmd $src $dsttmp" command. - - if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi && - if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi && - if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi && - if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi && - -# Now rename the file to the real destination. - - $doit $rmcmd -f $dstdir/$dstfile && - $doit $mvcmd $dsttmp $dstdir/$dstfile - -fi && - - -exit 0 diff --git a/cextern/expat/conftools/libtool.m4 b/cextern/expat/conftools/libtool.m4 deleted file mode 100755 index 0f53cb592ec0..000000000000 --- a/cextern/expat/conftools/libtool.m4 +++ /dev/null @@ -1,6397 +0,0 @@ -# libtool.m4 - Configure libtool for the host system. -*-Autoconf-*- -## Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005 -## Free Software Foundation, Inc. -## Originally by Gordon Matzigkeit , 1996 -## -## This file is free software; the Free Software Foundation gives -## unlimited permission to copy and/or distribute it, with or without -## modifications, as long as this notice is preserved. - -# serial 48 AC_PROG_LIBTOOL - - -# AC_PROVIDE_IFELSE(MACRO-NAME, IF-PROVIDED, IF-NOT-PROVIDED) -# ----------------------------------------------------------- -# If this macro is not defined by Autoconf, define it here. -m4_ifdef([AC_PROVIDE_IFELSE], - [], - [m4_define([AC_PROVIDE_IFELSE], - [m4_ifdef([AC_PROVIDE_$1], - [$2], [$3])])]) - - -# AC_PROG_LIBTOOL -# --------------- -AC_DEFUN([AC_PROG_LIBTOOL], -[AC_REQUIRE([_AC_PROG_LIBTOOL])dnl -dnl If AC_PROG_CXX has already been expanded, run AC_LIBTOOL_CXX -dnl immediately, otherwise, hook it in at the end of AC_PROG_CXX. - AC_PROVIDE_IFELSE([AC_PROG_CXX], - [AC_LIBTOOL_CXX], - [define([AC_PROG_CXX], defn([AC_PROG_CXX])[AC_LIBTOOL_CXX - ])]) -dnl And a similar setup for Fortran 77 support - AC_PROVIDE_IFELSE([AC_PROG_F77], - [AC_LIBTOOL_F77], - [define([AC_PROG_F77], defn([AC_PROG_F77])[AC_LIBTOOL_F77 -])]) - -dnl Quote A][M_PROG_GCJ so that aclocal doesn't bring it in needlessly. -dnl If either AC_PROG_GCJ or A][M_PROG_GCJ have already been expanded, run -dnl AC_LIBTOOL_GCJ immediately, otherwise, hook it in at the end of both. - AC_PROVIDE_IFELSE([AC_PROG_GCJ], - [AC_LIBTOOL_GCJ], - [AC_PROVIDE_IFELSE([A][M_PROG_GCJ], - [AC_LIBTOOL_GCJ], - [AC_PROVIDE_IFELSE([LT_AC_PROG_GCJ], - [AC_LIBTOOL_GCJ], - [ifdef([AC_PROG_GCJ], - [define([AC_PROG_GCJ], defn([AC_PROG_GCJ])[AC_LIBTOOL_GCJ])]) - ifdef([A][M_PROG_GCJ], - [define([A][M_PROG_GCJ], defn([A][M_PROG_GCJ])[AC_LIBTOOL_GCJ])]) - ifdef([LT_AC_PROG_GCJ], - [define([LT_AC_PROG_GCJ], - defn([LT_AC_PROG_GCJ])[AC_LIBTOOL_GCJ])])])]) -])])# AC_PROG_LIBTOOL - - -# _AC_PROG_LIBTOOL -# ---------------- -AC_DEFUN([_AC_PROG_LIBTOOL], -[AC_REQUIRE([AC_LIBTOOL_SETUP])dnl -AC_BEFORE([$0],[AC_LIBTOOL_CXX])dnl -AC_BEFORE([$0],[AC_LIBTOOL_F77])dnl -AC_BEFORE([$0],[AC_LIBTOOL_GCJ])dnl - -# This can be used to rebuild libtool when needed -LIBTOOL_DEPS="$ac_aux_dir/ltmain.sh" - -# Always use our own libtool. -LIBTOOL='$(SHELL) $(top_builddir)/libtool' -AC_SUBST(LIBTOOL)dnl - -# Prevent multiple expansion -define([AC_PROG_LIBTOOL], []) -])# _AC_PROG_LIBTOOL - - -# AC_LIBTOOL_SETUP -# ---------------- -AC_DEFUN([AC_LIBTOOL_SETUP], -[AC_PREREQ(2.50)dnl -AC_REQUIRE([AC_ENABLE_SHARED])dnl -AC_REQUIRE([AC_ENABLE_STATIC])dnl -AC_REQUIRE([AC_ENABLE_FAST_INSTALL])dnl -AC_REQUIRE([AC_CANONICAL_HOST])dnl -AC_REQUIRE([AC_CANONICAL_BUILD])dnl -AC_REQUIRE([AC_PROG_CC])dnl -AC_REQUIRE([AC_PROG_LD])dnl -AC_REQUIRE([AC_PROG_LD_RELOAD_FLAG])dnl -AC_REQUIRE([AC_PROG_NM])dnl - -AC_REQUIRE([AC_PROG_LN_S])dnl -AC_REQUIRE([AC_DEPLIBS_CHECK_METHOD])dnl -# Autoconf 2.13's AC_OBJEXT and AC_EXEEXT macros only works for C compilers! -AC_REQUIRE([AC_OBJEXT])dnl -AC_REQUIRE([AC_EXEEXT])dnl -dnl - -AC_LIBTOOL_SYS_MAX_CMD_LEN -AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE -AC_LIBTOOL_OBJDIR - -AC_REQUIRE([_LT_AC_SYS_COMPILER])dnl -_LT_AC_PROG_ECHO_BACKSLASH - -case $host_os in -aix3*) - # AIX sometimes has problems with the GCC collect2 program. For some - # reason, if we set the COLLECT_NAMES environment variable, the problems - # vanish in a puff of smoke. - if test "X${COLLECT_NAMES+set}" != Xset; then - COLLECT_NAMES= - export COLLECT_NAMES - fi - ;; -esac - -# Sed substitution that helps us do robust quoting. It backslashifies -# metacharacters that are still active within double-quoted strings. -Xsed='sed -e 1s/^X//' -[sed_quote_subst='s/\([\\"\\`$\\\\]\)/\\\1/g'] - -# Same as above, but do not quote variable references. -[double_quote_subst='s/\([\\"\\`\\\\]\)/\\\1/g'] - -# Sed substitution to delay expansion of an escaped shell variable in a -# double_quote_subst'ed string. -delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g' - -# Sed substitution to avoid accidental globbing in evaled expressions -no_glob_subst='s/\*/\\\*/g' - -# Constants: -rm="rm -f" - -# Global variables: -default_ofile=libtool -can_build_shared=yes - -# All known linkers require a `.a' archive for static linking (except MSVC, -# which needs '.lib'). -libext=a -ltmain="$ac_aux_dir/ltmain.sh" -ofile="$default_ofile" -with_gnu_ld="$lt_cv_prog_gnu_ld" - -AC_CHECK_TOOL(AR, ar, false) -AC_CHECK_TOOL(RANLIB, ranlib, :) -AC_CHECK_TOOL(STRIP, strip, :) - -old_CC="$CC" -old_CFLAGS="$CFLAGS" - -# Set sane defaults for various variables -test -z "$AR" && AR=ar -test -z "$AR_FLAGS" && AR_FLAGS=cru -test -z "$AS" && AS=as -test -z "$CC" && CC=cc -test -z "$LTCC" && LTCC=$CC -test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS -test -z "$DLLTOOL" && DLLTOOL=dlltool -test -z "$LD" && LD=ld -test -z "$LN_S" && LN_S="ln -s" -test -z "$MAGIC_CMD" && MAGIC_CMD=file -test -z "$NM" && NM=nm -test -z "$SED" && SED=sed -test -z "$OBJDUMP" && OBJDUMP=objdump -test -z "$RANLIB" && RANLIB=: -test -z "$STRIP" && STRIP=: -test -z "$ac_objext" && ac_objext=o - -# Determine commands to create old-style static archives. -old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs$old_deplibs' -old_postinstall_cmds='chmod 644 $oldlib' -old_postuninstall_cmds= - -if test -n "$RANLIB"; then - case $host_os in - openbsd*) - old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$oldlib" - ;; - *) - old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$oldlib" - ;; - esac - old_archive_cmds="$old_archive_cmds~\$RANLIB \$oldlib" -fi - -_LT_CC_BASENAME([$compiler]) - -# Only perform the check for file, if the check method requires it -case $deplibs_check_method in -file_magic*) - if test "$file_magic_cmd" = '$MAGIC_CMD'; then - AC_PATH_MAGIC - fi - ;; -esac - -AC_PROVIDE_IFELSE([AC_LIBTOOL_DLOPEN], enable_dlopen=yes, enable_dlopen=no) -AC_PROVIDE_IFELSE([AC_LIBTOOL_WIN32_DLL], -enable_win32_dll=yes, enable_win32_dll=no) - -AC_ARG_ENABLE([libtool-lock], - [AC_HELP_STRING([--disable-libtool-lock], - [avoid locking (might break parallel builds)])]) -test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes - -AC_ARG_WITH([pic], - [AC_HELP_STRING([--with-pic], - [try to use only PIC/non-PIC objects @<:@default=use both@:>@])], - [pic_mode="$withval"], - [pic_mode=default]) -test -z "$pic_mode" && pic_mode=default - -# Use C for the default configuration in the libtool script -tagname= -AC_LIBTOOL_LANG_C_CONFIG -_LT_AC_TAGCONFIG -])# AC_LIBTOOL_SETUP - - -# _LT_AC_SYS_COMPILER -# ------------------- -AC_DEFUN([_LT_AC_SYS_COMPILER], -[AC_REQUIRE([AC_PROG_CC])dnl - -# If no C compiler was specified, use CC. -LTCC=${LTCC-"$CC"} - -# If no C compiler flags were specified, use CFLAGS. -LTCFLAGS=${LTCFLAGS-"$CFLAGS"} - -# Allow CC to be a program name with arguments. -compiler=$CC -])# _LT_AC_SYS_COMPILER - - -# _LT_CC_BASENAME(CC) -# ------------------- -# Calculate cc_basename. Skip known compiler wrappers and cross-prefix. -AC_DEFUN([_LT_CC_BASENAME], -[for cc_temp in $1""; do - case $cc_temp in - compile | *[[\\/]]compile | ccache | *[[\\/]]ccache ) ;; - distcc | *[[\\/]]distcc | purify | *[[\\/]]purify ) ;; - \-*) ;; - *) break;; - esac -done -cc_basename=`$echo "X$cc_temp" | $Xsed -e 's%.*/%%' -e "s%^$host_alias-%%"` -]) - - -# _LT_COMPILER_BOILERPLATE -# ------------------------ -# Check for compiler boilerplate output or warnings with -# the simple compiler test code. -AC_DEFUN([_LT_COMPILER_BOILERPLATE], -[ac_outfile=conftest.$ac_objext -printf "$lt_simple_compile_test_code" >conftest.$ac_ext -eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err -_lt_compiler_boilerplate=`cat conftest.err` -$rm conftest* -])# _LT_COMPILER_BOILERPLATE - - -# _LT_LINKER_BOILERPLATE -# ---------------------- -# Check for linker boilerplate output or warnings with -# the simple link test code. -AC_DEFUN([_LT_LINKER_BOILERPLATE], -[ac_outfile=conftest.$ac_objext -printf "$lt_simple_link_test_code" >conftest.$ac_ext -eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err -_lt_linker_boilerplate=`cat conftest.err` -$rm conftest* -])# _LT_LINKER_BOILERPLATE - - -# _LT_AC_SYS_LIBPATH_AIX -# ---------------------- -# Links a minimal program and checks the executable -# for the system default hardcoded library path. In most cases, -# this is /usr/lib:/lib, but when the MPI compilers are used -# the location of the communication and MPI libs are included too. -# If we don't find anything, use the default library path according -# to the aix ld manual. -AC_DEFUN([_LT_AC_SYS_LIBPATH_AIX], -[AC_LINK_IFELSE(AC_LANG_PROGRAM,[ -aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } -}'` -# Check for a 64-bit object if we didn't find anything. -if test -z "$aix_libpath"; then aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } -}'`; fi],[]) -if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi -])# _LT_AC_SYS_LIBPATH_AIX - - -# _LT_AC_SHELL_INIT(ARG) -# ---------------------- -AC_DEFUN([_LT_AC_SHELL_INIT], -[ifdef([AC_DIVERSION_NOTICE], - [AC_DIVERT_PUSH(AC_DIVERSION_NOTICE)], - [AC_DIVERT_PUSH(NOTICE)]) -$1 -AC_DIVERT_POP -])# _LT_AC_SHELL_INIT - - -# _LT_AC_PROG_ECHO_BACKSLASH -# -------------------------- -# Add some code to the start of the generated configure script which -# will find an echo command which doesn't interpret backslashes. -AC_DEFUN([_LT_AC_PROG_ECHO_BACKSLASH], -[_LT_AC_SHELL_INIT([ -# Check that we are running under the correct shell. -SHELL=${CONFIG_SHELL-/bin/sh} - -case X$ECHO in -X*--fallback-echo) - # Remove one level of quotation (which was required for Make). - ECHO=`echo "$ECHO" | sed 's,\\\\\[$]\\[$]0,'[$]0','` - ;; -esac - -echo=${ECHO-echo} -if test "X[$]1" = X--no-reexec; then - # Discard the --no-reexec flag, and continue. - shift -elif test "X[$]1" = X--fallback-echo; then - # Avoid inline document here, it may be left over - : -elif test "X`($echo '\t') 2>/dev/null`" = 'X\t' ; then - # Yippee, $echo works! - : -else - # Restart under the correct shell. - exec $SHELL "[$]0" --no-reexec ${1+"[$]@"} -fi - -if test "X[$]1" = X--fallback-echo; then - # used as fallback echo - shift - cat </dev/null 2>&1 && unset CDPATH - -if test -z "$ECHO"; then -if test "X${echo_test_string+set}" != Xset; then -# find a string as large as possible, as long as the shell can cope with it - for cmd in 'sed 50q "[$]0"' 'sed 20q "[$]0"' 'sed 10q "[$]0"' 'sed 2q "[$]0"' 'echo test'; do - # expected sizes: less than 2Kb, 1Kb, 512 bytes, 16 bytes, ... - if (echo_test_string=`eval $cmd`) 2>/dev/null && - echo_test_string=`eval $cmd` && - (test "X$echo_test_string" = "X$echo_test_string") 2>/dev/null - then - break - fi - done -fi - -if test "X`($echo '\t') 2>/dev/null`" = 'X\t' && - echo_testing_string=`($echo "$echo_test_string") 2>/dev/null` && - test "X$echo_testing_string" = "X$echo_test_string"; then - : -else - # The Solaris, AIX, and Digital Unix default echo programs unquote - # backslashes. This makes it impossible to quote backslashes using - # echo "$something" | sed 's/\\/\\\\/g' - # - # So, first we look for a working echo in the user's PATH. - - lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR - for dir in $PATH /usr/ucb; do - IFS="$lt_save_ifs" - if (test -f $dir/echo || test -f $dir/echo$ac_exeext) && - test "X`($dir/echo '\t') 2>/dev/null`" = 'X\t' && - echo_testing_string=`($dir/echo "$echo_test_string") 2>/dev/null` && - test "X$echo_testing_string" = "X$echo_test_string"; then - echo="$dir/echo" - break - fi - done - IFS="$lt_save_ifs" - - if test "X$echo" = Xecho; then - # We didn't find a better echo, so look for alternatives. - if test "X`(print -r '\t') 2>/dev/null`" = 'X\t' && - echo_testing_string=`(print -r "$echo_test_string") 2>/dev/null` && - test "X$echo_testing_string" = "X$echo_test_string"; then - # This shell has a builtin print -r that does the trick. - echo='print -r' - elif (test -f /bin/ksh || test -f /bin/ksh$ac_exeext) && - test "X$CONFIG_SHELL" != X/bin/ksh; then - # If we have ksh, try running configure again with it. - ORIGINAL_CONFIG_SHELL=${CONFIG_SHELL-/bin/sh} - export ORIGINAL_CONFIG_SHELL - CONFIG_SHELL=/bin/ksh - export CONFIG_SHELL - exec $CONFIG_SHELL "[$]0" --no-reexec ${1+"[$]@"} - else - # Try using printf. - echo='printf %s\n' - if test "X`($echo '\t') 2>/dev/null`" = 'X\t' && - echo_testing_string=`($echo "$echo_test_string") 2>/dev/null` && - test "X$echo_testing_string" = "X$echo_test_string"; then - # Cool, printf works - : - elif echo_testing_string=`($ORIGINAL_CONFIG_SHELL "[$]0" --fallback-echo '\t') 2>/dev/null` && - test "X$echo_testing_string" = 'X\t' && - echo_testing_string=`($ORIGINAL_CONFIG_SHELL "[$]0" --fallback-echo "$echo_test_string") 2>/dev/null` && - test "X$echo_testing_string" = "X$echo_test_string"; then - CONFIG_SHELL=$ORIGINAL_CONFIG_SHELL - export CONFIG_SHELL - SHELL="$CONFIG_SHELL" - export SHELL - echo="$CONFIG_SHELL [$]0 --fallback-echo" - elif echo_testing_string=`($CONFIG_SHELL "[$]0" --fallback-echo '\t') 2>/dev/null` && - test "X$echo_testing_string" = 'X\t' && - echo_testing_string=`($CONFIG_SHELL "[$]0" --fallback-echo "$echo_test_string") 2>/dev/null` && - test "X$echo_testing_string" = "X$echo_test_string"; then - echo="$CONFIG_SHELL [$]0 --fallback-echo" - else - # maybe with a smaller string... - prev=: - - for cmd in 'echo test' 'sed 2q "[$]0"' 'sed 10q "[$]0"' 'sed 20q "[$]0"' 'sed 50q "[$]0"'; do - if (test "X$echo_test_string" = "X`eval $cmd`") 2>/dev/null - then - break - fi - prev="$cmd" - done - - if test "$prev" != 'sed 50q "[$]0"'; then - echo_test_string=`eval $prev` - export echo_test_string - exec ${ORIGINAL_CONFIG_SHELL-${CONFIG_SHELL-/bin/sh}} "[$]0" ${1+"[$]@"} - else - # Oops. We lost completely, so just stick with echo. - echo=echo - fi - fi - fi - fi -fi -fi - -# Copy echo and quote the copy suitably for passing to libtool from -# the Makefile, instead of quoting the original, which is used later. -ECHO=$echo -if test "X$ECHO" = "X$CONFIG_SHELL [$]0 --fallback-echo"; then - ECHO="$CONFIG_SHELL \\\$\[$]0 --fallback-echo" -fi - -AC_SUBST(ECHO) -])])# _LT_AC_PROG_ECHO_BACKSLASH - - -# _LT_AC_LOCK -# ----------- -AC_DEFUN([_LT_AC_LOCK], -[AC_ARG_ENABLE([libtool-lock], - [AC_HELP_STRING([--disable-libtool-lock], - [avoid locking (might break parallel builds)])]) -test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes - -# Some flags need to be propagated to the compiler or linker for good -# libtool support. -case $host in -ia64-*-hpux*) - # Find out which ABI we are using. - echo 'int i;' > conftest.$ac_ext - if AC_TRY_EVAL(ac_compile); then - case `/usr/bin/file conftest.$ac_objext` in - *ELF-32*) - HPUX_IA64_MODE="32" - ;; - *ELF-64*) - HPUX_IA64_MODE="64" - ;; - esac - fi - rm -rf conftest* - ;; -*-*-irix6*) - # Find out which ABI we are using. - echo '[#]line __oline__ "configure"' > conftest.$ac_ext - if AC_TRY_EVAL(ac_compile); then - if test "$lt_cv_prog_gnu_ld" = yes; then - case `/usr/bin/file conftest.$ac_objext` in - *32-bit*) - LD="${LD-ld} -melf32bsmip" - ;; - *N32*) - LD="${LD-ld} -melf32bmipn32" - ;; - *64-bit*) - LD="${LD-ld} -melf64bmip" - ;; - esac - else - case `/usr/bin/file conftest.$ac_objext` in - *32-bit*) - LD="${LD-ld} -32" - ;; - *N32*) - LD="${LD-ld} -n32" - ;; - *64-bit*) - LD="${LD-ld} -64" - ;; - esac - fi - fi - rm -rf conftest* - ;; - -x86_64-*linux*|ppc*-*linux*|powerpc*-*linux*|s390*-*linux*|sparc*-*linux*) - # Find out which ABI we are using. - echo 'int i;' > conftest.$ac_ext - if AC_TRY_EVAL(ac_compile); then - case `/usr/bin/file conftest.o` in - *32-bit*) - case $host in - x86_64-*linux*) - LD="${LD-ld} -m elf_i386" - ;; - ppc64-*linux*|powerpc64-*linux*) - LD="${LD-ld} -m elf32ppclinux" - ;; - s390x-*linux*) - LD="${LD-ld} -m elf_s390" - ;; - sparc64-*linux*) - LD="${LD-ld} -m elf32_sparc" - ;; - esac - ;; - *64-bit*) - case $host in - x86_64-*linux*) - LD="${LD-ld} -m elf_x86_64" - ;; - ppc*-*linux*|powerpc*-*linux*) - LD="${LD-ld} -m elf64ppc" - ;; - s390*-*linux*) - LD="${LD-ld} -m elf64_s390" - ;; - sparc*-*linux*) - LD="${LD-ld} -m elf64_sparc" - ;; - esac - ;; - esac - fi - rm -rf conftest* - ;; - -*-*-sco3.2v5*) - # On SCO OpenServer 5, we need -belf to get full-featured binaries. - SAVE_CFLAGS="$CFLAGS" - CFLAGS="$CFLAGS -belf" - AC_CACHE_CHECK([whether the C compiler needs -belf], lt_cv_cc_needs_belf, - [AC_LANG_PUSH(C) - AC_TRY_LINK([],[],[lt_cv_cc_needs_belf=yes],[lt_cv_cc_needs_belf=no]) - AC_LANG_POP]) - if test x"$lt_cv_cc_needs_belf" != x"yes"; then - # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf - CFLAGS="$SAVE_CFLAGS" - fi - ;; -sparc*-*solaris*) - # Find out which ABI we are using. - echo 'int i;' > conftest.$ac_ext - if AC_TRY_EVAL(ac_compile); then - case `/usr/bin/file conftest.o` in - *64-bit*) - case $lt_cv_prog_gnu_ld in - yes*) LD="${LD-ld} -m elf64_sparc" ;; - *) LD="${LD-ld} -64" ;; - esac - ;; - esac - fi - rm -rf conftest* - ;; - -AC_PROVIDE_IFELSE([AC_LIBTOOL_WIN32_DLL], -[*-*-cygwin* | *-*-mingw* | *-*-pw32*) - AC_CHECK_TOOL(DLLTOOL, dlltool, false) - AC_CHECK_TOOL(AS, as, false) - AC_CHECK_TOOL(OBJDUMP, objdump, false) - ;; - ]) -esac - -need_locks="$enable_libtool_lock" - -])# _LT_AC_LOCK - - -# AC_LIBTOOL_COMPILER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS, -# [OUTPUT-FILE], [ACTION-SUCCESS], [ACTION-FAILURE]) -# ---------------------------------------------------------------- -# Check whether the given compiler option works -AC_DEFUN([AC_LIBTOOL_COMPILER_OPTION], -[AC_REQUIRE([LT_AC_PROG_SED]) -AC_CACHE_CHECK([$1], [$2], - [$2=no - ifelse([$4], , [ac_outfile=conftest.$ac_objext], [ac_outfile=$4]) - printf "$lt_simple_compile_test_code" > conftest.$ac_ext - lt_compiler_flag="$3" - # Insert the option either (1) after the last *FLAGS variable, or - # (2) before a word containing "conftest.", or (3) at the end. - # Note that $ac_compile itself does not contain backslashes and begins - # with a dollar sign (not a hyphen), so the echo should work correctly. - # The option is referenced via a variable to avoid confusing sed. - lt_compile=`echo "$ac_compile" | $SED \ - -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ - -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \ - -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:__oline__: $lt_compile\"" >&AS_MESSAGE_LOG_FD) - (eval "$lt_compile" 2>conftest.err) - ac_status=$? - cat conftest.err >&AS_MESSAGE_LOG_FD - echo "$as_me:__oline__: \$? = $ac_status" >&AS_MESSAGE_LOG_FD - if (exit $ac_status) && test -s "$ac_outfile"; then - # The compiler can only warn and ignore the option if not recognized - # So say no if there are warnings other than the usual output. - $echo "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' >conftest.exp - $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 - if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then - $2=yes - fi - fi - $rm conftest* -]) - -if test x"[$]$2" = xyes; then - ifelse([$5], , :, [$5]) -else - ifelse([$6], , :, [$6]) -fi -])# AC_LIBTOOL_COMPILER_OPTION - - -# AC_LIBTOOL_LINKER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS, -# [ACTION-SUCCESS], [ACTION-FAILURE]) -# ------------------------------------------------------------ -# Check whether the given compiler option works -AC_DEFUN([AC_LIBTOOL_LINKER_OPTION], -[AC_CACHE_CHECK([$1], [$2], - [$2=no - save_LDFLAGS="$LDFLAGS" - LDFLAGS="$LDFLAGS $3" - printf "$lt_simple_link_test_code" > conftest.$ac_ext - if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then - # The linker can only warn and ignore the option if not recognized - # So say no if there are warnings - if test -s conftest.err; then - # Append any errors to the config.log. - cat conftest.err 1>&AS_MESSAGE_LOG_FD - $echo "X$_lt_linker_boilerplate" | $Xsed -e '/^$/d' > conftest.exp - $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 - if diff conftest.exp conftest.er2 >/dev/null; then - $2=yes - fi - else - $2=yes - fi - fi - $rm conftest* - LDFLAGS="$save_LDFLAGS" -]) - -if test x"[$]$2" = xyes; then - ifelse([$4], , :, [$4]) -else - ifelse([$5], , :, [$5]) -fi -])# AC_LIBTOOL_LINKER_OPTION - - -# AC_LIBTOOL_SYS_MAX_CMD_LEN -# -------------------------- -AC_DEFUN([AC_LIBTOOL_SYS_MAX_CMD_LEN], -[# find the maximum length of command line arguments -AC_MSG_CHECKING([the maximum length of command line arguments]) -AC_CACHE_VAL([lt_cv_sys_max_cmd_len], [dnl - i=0 - teststring="ABCD" - - case $build_os in - msdosdjgpp*) - # On DJGPP, this test can blow up pretty badly due to problems in libc - # (any single argument exceeding 2000 bytes causes a buffer overrun - # during glob expansion). Even if it were fixed, the result of this - # check would be larger than it should be. - lt_cv_sys_max_cmd_len=12288; # 12K is about right - ;; - - gnu*) - # Under GNU Hurd, this test is not required because there is - # no limit to the length of command line arguments. - # Libtool will interpret -1 as no limit whatsoever - lt_cv_sys_max_cmd_len=-1; - ;; - - cygwin* | mingw*) - # On Win9x/ME, this test blows up -- it succeeds, but takes - # about 5 minutes as the teststring grows exponentially. - # Worse, since 9x/ME are not pre-emptively multitasking, - # you end up with a "frozen" computer, even though with patience - # the test eventually succeeds (with a max line length of 256k). - # Instead, let's just punt: use the minimum linelength reported by - # all of the supported platforms: 8192 (on NT/2K/XP). - lt_cv_sys_max_cmd_len=8192; - ;; - - amigaos*) - # On AmigaOS with pdksh, this test takes hours, literally. - # So we just punt and use a minimum line length of 8192. - lt_cv_sys_max_cmd_len=8192; - ;; - - netbsd* | freebsd* | openbsd* | darwin* | dragonfly*) - # This has been around since 386BSD, at least. Likely further. - if test -x /sbin/sysctl; then - lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax` - elif test -x /usr/sbin/sysctl; then - lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax` - else - lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs - fi - # And add a safety zone - lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` - lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` - ;; - - interix*) - # We know the value 262144 and hardcode it with a safety zone (like BSD) - lt_cv_sys_max_cmd_len=196608 - ;; - - osf*) - # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure - # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not - # nice to cause kernel panics so lets avoid the loop below. - # First set a reasonable default. - lt_cv_sys_max_cmd_len=16384 - # - if test -x /sbin/sysconfig; then - case `/sbin/sysconfig -q proc exec_disable_arg_limit` in - *1*) lt_cv_sys_max_cmd_len=-1 ;; - esac - fi - ;; - sco3.2v5*) - lt_cv_sys_max_cmd_len=102400 - ;; - sysv5* | sco5v6* | sysv4.2uw2*) - kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null` - if test -n "$kargmax"; then - lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[[ ]]//'` - else - lt_cv_sys_max_cmd_len=32768 - fi - ;; - *) - # If test is not a shell built-in, we'll probably end up computing a - # maximum length that is only half of the actual maximum length, but - # we can't tell. - SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}} - while (test "X"`$SHELL [$]0 --fallback-echo "X$teststring" 2>/dev/null` \ - = "XX$teststring") >/dev/null 2>&1 && - new_result=`expr "X$teststring" : ".*" 2>&1` && - lt_cv_sys_max_cmd_len=$new_result && - test $i != 17 # 1/2 MB should be enough - do - i=`expr $i + 1` - teststring=$teststring$teststring - done - teststring= - # Add a significant safety factor because C++ compilers can tack on massive - # amounts of additional arguments before passing them to the linker. - # It appears as though 1/2 is a usable value. - lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2` - ;; - esac -]) -if test -n $lt_cv_sys_max_cmd_len ; then - AC_MSG_RESULT($lt_cv_sys_max_cmd_len) -else - AC_MSG_RESULT(none) -fi -])# AC_LIBTOOL_SYS_MAX_CMD_LEN - - -# _LT_AC_CHECK_DLFCN -# ------------------ -AC_DEFUN([_LT_AC_CHECK_DLFCN], -[AC_CHECK_HEADERS(dlfcn.h)dnl -])# _LT_AC_CHECK_DLFCN - - -# _LT_AC_TRY_DLOPEN_SELF (ACTION-IF-TRUE, ACTION-IF-TRUE-W-USCORE, -# ACTION-IF-FALSE, ACTION-IF-CROSS-COMPILING) -# --------------------------------------------------------------------- -AC_DEFUN([_LT_AC_TRY_DLOPEN_SELF], -[AC_REQUIRE([_LT_AC_CHECK_DLFCN])dnl -if test "$cross_compiling" = yes; then : - [$4] -else - lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 - lt_status=$lt_dlunknown - cat > conftest.$ac_ext < -#endif - -#include - -#ifdef RTLD_GLOBAL -# define LT_DLGLOBAL RTLD_GLOBAL -#else -# ifdef DL_GLOBAL -# define LT_DLGLOBAL DL_GLOBAL -# else -# define LT_DLGLOBAL 0 -# endif -#endif - -/* We may have to define LT_DLLAZY_OR_NOW in the command line if we - find out it does not work in some platform. */ -#ifndef LT_DLLAZY_OR_NOW -# ifdef RTLD_LAZY -# define LT_DLLAZY_OR_NOW RTLD_LAZY -# else -# ifdef DL_LAZY -# define LT_DLLAZY_OR_NOW DL_LAZY -# else -# ifdef RTLD_NOW -# define LT_DLLAZY_OR_NOW RTLD_NOW -# else -# ifdef DL_NOW -# define LT_DLLAZY_OR_NOW DL_NOW -# else -# define LT_DLLAZY_OR_NOW 0 -# endif -# endif -# endif -# endif -#endif - -#ifdef __cplusplus -extern "C" void exit (int); -#endif - -void fnord() { int i=42;} -int main () -{ - void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); - int status = $lt_dlunknown; - - if (self) - { - if (dlsym (self,"fnord")) status = $lt_dlno_uscore; - else if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; - /* dlclose (self); */ - } - else - puts (dlerror ()); - - exit (status); -}] -EOF - if AC_TRY_EVAL(ac_link) && test -s conftest${ac_exeext} 2>/dev/null; then - (./conftest; exit; ) >&AS_MESSAGE_LOG_FD 2>/dev/null - lt_status=$? - case x$lt_status in - x$lt_dlno_uscore) $1 ;; - x$lt_dlneed_uscore) $2 ;; - x$lt_dlunknown|x*) $3 ;; - esac - else : - # compilation failed - $3 - fi -fi -rm -fr conftest* -])# _LT_AC_TRY_DLOPEN_SELF - - -# AC_LIBTOOL_DLOPEN_SELF -# ---------------------- -AC_DEFUN([AC_LIBTOOL_DLOPEN_SELF], -[AC_REQUIRE([_LT_AC_CHECK_DLFCN])dnl -if test "x$enable_dlopen" != xyes; then - enable_dlopen=unknown - enable_dlopen_self=unknown - enable_dlopen_self_static=unknown -else - lt_cv_dlopen=no - lt_cv_dlopen_libs= - - case $host_os in - beos*) - lt_cv_dlopen="load_add_on" - lt_cv_dlopen_libs= - lt_cv_dlopen_self=yes - ;; - - mingw* | pw32*) - lt_cv_dlopen="LoadLibrary" - lt_cv_dlopen_libs= - ;; - - cygwin*) - lt_cv_dlopen="dlopen" - lt_cv_dlopen_libs= - ;; - - darwin*) - # if libdl is installed we need to link against it - AC_CHECK_LIB([dl], [dlopen], - [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"],[ - lt_cv_dlopen="dyld" - lt_cv_dlopen_libs= - lt_cv_dlopen_self=yes - ]) - ;; - - *) - AC_CHECK_FUNC([shl_load], - [lt_cv_dlopen="shl_load"], - [AC_CHECK_LIB([dld], [shl_load], - [lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-dld"], - [AC_CHECK_FUNC([dlopen], - [lt_cv_dlopen="dlopen"], - [AC_CHECK_LIB([dl], [dlopen], - [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"], - [AC_CHECK_LIB([svld], [dlopen], - [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-lsvld"], - [AC_CHECK_LIB([dld], [dld_link], - [lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-dld"]) - ]) - ]) - ]) - ]) - ]) - ;; - esac - - if test "x$lt_cv_dlopen" != xno; then - enable_dlopen=yes - else - enable_dlopen=no - fi - - case $lt_cv_dlopen in - dlopen) - save_CPPFLAGS="$CPPFLAGS" - test "x$ac_cv_header_dlfcn_h" = xyes && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H" - - save_LDFLAGS="$LDFLAGS" - wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\" - - save_LIBS="$LIBS" - LIBS="$lt_cv_dlopen_libs $LIBS" - - AC_CACHE_CHECK([whether a program can dlopen itself], - lt_cv_dlopen_self, [dnl - _LT_AC_TRY_DLOPEN_SELF( - lt_cv_dlopen_self=yes, lt_cv_dlopen_self=yes, - lt_cv_dlopen_self=no, lt_cv_dlopen_self=cross) - ]) - - if test "x$lt_cv_dlopen_self" = xyes; then - wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\" - AC_CACHE_CHECK([whether a statically linked program can dlopen itself], - lt_cv_dlopen_self_static, [dnl - _LT_AC_TRY_DLOPEN_SELF( - lt_cv_dlopen_self_static=yes, lt_cv_dlopen_self_static=yes, - lt_cv_dlopen_self_static=no, lt_cv_dlopen_self_static=cross) - ]) - fi - - CPPFLAGS="$save_CPPFLAGS" - LDFLAGS="$save_LDFLAGS" - LIBS="$save_LIBS" - ;; - esac - - case $lt_cv_dlopen_self in - yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;; - *) enable_dlopen_self=unknown ;; - esac - - case $lt_cv_dlopen_self_static in - yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;; - *) enable_dlopen_self_static=unknown ;; - esac -fi -])# AC_LIBTOOL_DLOPEN_SELF - - -# AC_LIBTOOL_PROG_CC_C_O([TAGNAME]) -# --------------------------------- -# Check to see if options -c and -o are simultaneously supported by compiler -AC_DEFUN([AC_LIBTOOL_PROG_CC_C_O], -[AC_REQUIRE([_LT_AC_SYS_COMPILER])dnl -AC_CACHE_CHECK([if $compiler supports -c -o file.$ac_objext], - [_LT_AC_TAGVAR(lt_cv_prog_compiler_c_o, $1)], - [_LT_AC_TAGVAR(lt_cv_prog_compiler_c_o, $1)=no - $rm -r conftest 2>/dev/null - mkdir conftest - cd conftest - mkdir out - printf "$lt_simple_compile_test_code" > conftest.$ac_ext - - lt_compiler_flag="-o out/conftest2.$ac_objext" - # Insert the option either (1) after the last *FLAGS variable, or - # (2) before a word containing "conftest.", or (3) at the end. - # Note that $ac_compile itself does not contain backslashes and begins - # with a dollar sign (not a hyphen), so the echo should work correctly. - lt_compile=`echo "$ac_compile" | $SED \ - -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ - -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \ - -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:__oline__: $lt_compile\"" >&AS_MESSAGE_LOG_FD) - (eval "$lt_compile" 2>out/conftest.err) - ac_status=$? - cat out/conftest.err >&AS_MESSAGE_LOG_FD - echo "$as_me:__oline__: \$? = $ac_status" >&AS_MESSAGE_LOG_FD - if (exit $ac_status) && test -s out/conftest2.$ac_objext - then - # The compiler can only warn and ignore the option if not recognized - # So say no if there are warnings - $echo "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' > out/conftest.exp - $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 - if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then - _LT_AC_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes - fi - fi - chmod u+w . 2>&AS_MESSAGE_LOG_FD - $rm conftest* - # SGI C++ compiler will create directory out/ii_files/ for - # template instantiation - test -d out/ii_files && $rm out/ii_files/* && rmdir out/ii_files - $rm out/* && rmdir out - cd .. - rmdir conftest - $rm conftest* -]) -])# AC_LIBTOOL_PROG_CC_C_O - - -# AC_LIBTOOL_SYS_HARD_LINK_LOCKS([TAGNAME]) -# ----------------------------------------- -# Check to see if we can do hard links to lock some files if needed -AC_DEFUN([AC_LIBTOOL_SYS_HARD_LINK_LOCKS], -[AC_REQUIRE([_LT_AC_LOCK])dnl - -hard_links="nottested" -if test "$_LT_AC_TAGVAR(lt_cv_prog_compiler_c_o, $1)" = no && test "$need_locks" != no; then - # do not overwrite the value of need_locks provided by the user - AC_MSG_CHECKING([if we can lock with hard links]) - hard_links=yes - $rm conftest* - ln conftest.a conftest.b 2>/dev/null && hard_links=no - touch conftest.a - ln conftest.a conftest.b 2>&5 || hard_links=no - ln conftest.a conftest.b 2>/dev/null && hard_links=no - AC_MSG_RESULT([$hard_links]) - if test "$hard_links" = no; then - AC_MSG_WARN([`$CC' does not support `-c -o', so `make -j' may be unsafe]) - need_locks=warn - fi -else - need_locks=no -fi -])# AC_LIBTOOL_SYS_HARD_LINK_LOCKS - - -# AC_LIBTOOL_OBJDIR -# ----------------- -AC_DEFUN([AC_LIBTOOL_OBJDIR], -[AC_CACHE_CHECK([for objdir], [lt_cv_objdir], -[rm -f .libs 2>/dev/null -mkdir .libs 2>/dev/null -if test -d .libs; then - lt_cv_objdir=.libs -else - # MS-DOS does not allow filenames that begin with a dot. - lt_cv_objdir=_libs -fi -rmdir .libs 2>/dev/null]) -objdir=$lt_cv_objdir -])# AC_LIBTOOL_OBJDIR - - -# AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH([TAGNAME]) -# ---------------------------------------------- -# Check hardcoding attributes. -AC_DEFUN([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH], -[AC_MSG_CHECKING([how to hardcode library paths into programs]) -_LT_AC_TAGVAR(hardcode_action, $1)= -if test -n "$_LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)" || \ - test -n "$_LT_AC_TAGVAR(runpath_var, $1)" || \ - test "X$_LT_AC_TAGVAR(hardcode_automatic, $1)" = "Xyes" ; then - - # We can hardcode non-existant directories. - if test "$_LT_AC_TAGVAR(hardcode_direct, $1)" != no && - # If the only mechanism to avoid hardcoding is shlibpath_var, we - # have to relink, otherwise we might link with an installed library - # when we should be linking with a yet-to-be-installed one - ## test "$_LT_AC_TAGVAR(hardcode_shlibpath_var, $1)" != no && - test "$_LT_AC_TAGVAR(hardcode_minus_L, $1)" != no; then - # Linking always hardcodes the temporary library directory. - _LT_AC_TAGVAR(hardcode_action, $1)=relink - else - # We can link without hardcoding, and we can hardcode nonexisting dirs. - _LT_AC_TAGVAR(hardcode_action, $1)=immediate - fi -else - # We cannot hardcode anything, or else we can only hardcode existing - # directories. - _LT_AC_TAGVAR(hardcode_action, $1)=unsupported -fi -AC_MSG_RESULT([$_LT_AC_TAGVAR(hardcode_action, $1)]) - -if test "$_LT_AC_TAGVAR(hardcode_action, $1)" = relink; then - # Fast installation is not supported - enable_fast_install=no -elif test "$shlibpath_overrides_runpath" = yes || - test "$enable_shared" = no; then - # Fast installation is not necessary - enable_fast_install=needless -fi -])# AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH - - -# AC_LIBTOOL_SYS_LIB_STRIP -# ------------------------ -AC_DEFUN([AC_LIBTOOL_SYS_LIB_STRIP], -[striplib= -old_striplib= -AC_MSG_CHECKING([whether stripping libraries is possible]) -if test -n "$STRIP" && $STRIP -V 2>&1 | grep "GNU strip" >/dev/null; then - test -z "$old_striplib" && old_striplib="$STRIP --strip-debug" - test -z "$striplib" && striplib="$STRIP --strip-unneeded" - AC_MSG_RESULT([yes]) -else -# FIXME - insert some real tests, host_os isn't really good enough - case $host_os in - darwin*) - if test -n "$STRIP" ; then - striplib="$STRIP -x" - AC_MSG_RESULT([yes]) - else - AC_MSG_RESULT([no]) -fi - ;; - *) - AC_MSG_RESULT([no]) - ;; - esac -fi -])# AC_LIBTOOL_SYS_LIB_STRIP - - -# AC_LIBTOOL_SYS_DYNAMIC_LINKER -# ----------------------------- -# PORTME Fill in your ld.so characteristics -AC_DEFUN([AC_LIBTOOL_SYS_DYNAMIC_LINKER], -[AC_MSG_CHECKING([dynamic linker characteristics]) -library_names_spec= -libname_spec='lib$name' -soname_spec= -shrext_cmds=".so" -postinstall_cmds= -postuninstall_cmds= -finish_cmds= -finish_eval= -shlibpath_var= -shlibpath_overrides_runpath=unknown -version_type=none -dynamic_linker="$host_os ld.so" -sys_lib_dlsearch_path_spec="/lib /usr/lib" -if test "$GCC" = yes; then - sys_lib_search_path_spec=`$CC -print-search-dirs | grep "^libraries:" | $SED -e "s/^libraries://" -e "s,=/,/,g"` - if echo "$sys_lib_search_path_spec" | grep ';' >/dev/null ; then - # if the path contains ";" then we assume it to be the separator - # otherwise default to the standard path separator (i.e. ":") - it is - # assumed that no part of a normal pathname contains ";" but that should - # okay in the real world where ";" in dirpaths is itself problematic. - sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` - else - sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` - fi -else - sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" -fi -need_lib_prefix=unknown -hardcode_into_libs=no - -# when you set need_version to no, make sure it does not cause -set_version -# flags to be left without arguments -need_version=unknown - -case $host_os in -aix3*) - version_type=linux - library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a' - shlibpath_var=LIBPATH - - # AIX 3 has no versioning support, so we append a major version to the name. - soname_spec='${libname}${release}${shared_ext}$major' - ;; - -aix4* | aix5*) - version_type=linux - need_lib_prefix=no - need_version=no - hardcode_into_libs=yes - if test "$host_cpu" = ia64; then - # AIX 5 supports IA64 - library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}' - shlibpath_var=LD_LIBRARY_PATH - else - # With GCC up to 2.95.x, collect2 would create an import file - # for dependence libraries. The import file would start with - # the line `#! .'. This would cause the generated library to - # depend on `.', always an invalid library. This was fixed in - # development snapshots of GCC prior to 3.0. - case $host_os in - aix4 | aix4.[[01]] | aix4.[[01]].*) - if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' - echo ' yes ' - echo '#endif'; } | ${CC} -E - | grep yes > /dev/null; then - : - else - can_build_shared=no - fi - ;; - esac - # AIX (on Power*) has no versioning support, so currently we can not hardcode correct - # soname into executable. Probably we can add versioning support to - # collect2, so additional links can be useful in future. - if test "$aix_use_runtimelinking" = yes; then - # If using run time linking (on AIX 4.2 or later) use lib.so - # instead of lib.a to let people know that these are not - # typical AIX shared libraries. - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - else - # We preserve .a as extension for shared libraries through AIX4.2 - # and later when we are not doing run time linking. - library_names_spec='${libname}${release}.a $libname.a' - soname_spec='${libname}${release}${shared_ext}$major' - fi - shlibpath_var=LIBPATH - fi - ;; - -amigaos*) - library_names_spec='$libname.ixlibrary $libname.a' - # Create ${libname}_ixlibrary.a entries in /sys/libs. - finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`$echo "X$lib" | $Xsed -e '\''s%^.*/\([[^/]]*\)\.ixlibrary$%\1%'\''`; test $rm /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' - ;; - -beos*) - library_names_spec='${libname}${shared_ext}' - dynamic_linker="$host_os ld.so" - shlibpath_var=LIBRARY_PATH - ;; - -bsdi[[45]]*) - version_type=linux - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' - shlibpath_var=LD_LIBRARY_PATH - sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" - sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" - # the default ld.so.conf also contains /usr/contrib/lib and - # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow - # libtool to hard-code these into programs - ;; - -cygwin* | mingw* | pw32*) - version_type=windows - shrext_cmds=".dll" - need_version=no - need_lib_prefix=no - - case $GCC,$host_os in - yes,cygwin* | yes,mingw* | yes,pw32*) - library_names_spec='$libname.dll.a' - # DLL is installed to $(libdir)/../bin by postinstall_cmds - postinstall_cmds='base_file=`basename \${file}`~ - dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i;echo \$dlname'\''`~ - dldir=$destdir/`dirname \$dlpath`~ - test -d \$dldir || mkdir -p \$dldir~ - $install_prog $dir/$dlname \$dldir/$dlname~ - chmod a+x \$dldir/$dlname' - postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ - dlpath=$dir/\$dldll~ - $rm \$dlpath' - shlibpath_overrides_runpath=yes - - case $host_os in - cygwin*) - # Cygwin DLLs use 'cyg' prefix rather than 'lib' - soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}' - sys_lib_search_path_spec="/usr/lib /lib/w32api /lib /usr/local/lib" - ;; - mingw*) - # MinGW DLLs use traditional 'lib' prefix - soname_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}' - sys_lib_search_path_spec=`$CC -print-search-dirs | grep "^libraries:" | $SED -e "s/^libraries://" -e "s,=/,/,g"` - if echo "$sys_lib_search_path_spec" | [grep ';[c-zC-Z]:/' >/dev/null]; then - # It is most probably a Windows format PATH printed by - # mingw gcc, but we are running on Cygwin. Gcc prints its search - # path with ; separators, and with drive letters. We can handle the - # drive letters (cygwin fileutils understands them), so leave them, - # especially as we might pass files found there to a mingw objdump, - # which wouldn't understand a cygwinified path. Ahh. - sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` - else - sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` - fi - ;; - pw32*) - # pw32 DLLs use 'pw' prefix rather than 'lib' - library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}' - ;; - esac - ;; - - *) - library_names_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext} $libname.lib' - ;; - esac - dynamic_linker='Win32 ld.exe' - # FIXME: first we should search . and the directory the executable is in - shlibpath_var=PATH - ;; - -darwin* | rhapsody*) - dynamic_linker="$host_os dyld" - version_type=darwin - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${versuffix}$shared_ext ${libname}${release}${major}$shared_ext ${libname}$shared_ext' - soname_spec='${libname}${release}${major}$shared_ext' - shlibpath_overrides_runpath=yes - shlibpath_var=DYLD_LIBRARY_PATH - shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' - # Apple's gcc prints 'gcc -print-search-dirs' doesn't operate the same. - if test "$GCC" = yes; then - sys_lib_search_path_spec=`$CC -print-search-dirs | tr "\n" "$PATH_SEPARATOR" | sed -e 's/libraries:/@libraries:/' | tr "@" "\n" | grep "^libraries:" | sed -e "s/^libraries://" -e "s,=/,/,g" -e "s,$PATH_SEPARATOR, ,g" -e "s,.*,& /lib /usr/lib /usr/local/lib,g"` - else - sys_lib_search_path_spec='/lib /usr/lib /usr/local/lib' - fi - sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' - ;; - -dgux*) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - ;; - -freebsd1*) - dynamic_linker=no - ;; - -kfreebsd*-gnu) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=no - hardcode_into_libs=yes - dynamic_linker='GNU ld.so' - ;; - -freebsd* | dragonfly*) - # DragonFly does not have aout. When/if they implement a new - # versioning mechanism, adjust this. - if test -x /usr/bin/objformat; then - objformat=`/usr/bin/objformat` - else - case $host_os in - freebsd[[123]]*) objformat=aout ;; - *) objformat=elf ;; - esac - fi - version_type=freebsd-$objformat - case $version_type in - freebsd-elf*) - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' - need_version=no - need_lib_prefix=no - ;; - freebsd-*) - library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix' - need_version=yes - ;; - esac - shlibpath_var=LD_LIBRARY_PATH - case $host_os in - freebsd2*) - shlibpath_overrides_runpath=yes - ;; - freebsd3.[[01]]* | freebsdelf3.[[01]]*) - shlibpath_overrides_runpath=yes - hardcode_into_libs=yes - ;; - freebsd3.[[2-9]]* | freebsdelf3.[[2-9]]* | \ - freebsd4.[[0-5]] | freebsdelf4.[[0-5]] | freebsd4.1.1 | freebsdelf4.1.1) - shlibpath_overrides_runpath=no - hardcode_into_libs=yes - ;; - freebsd*) # from 4.6 on - shlibpath_overrides_runpath=yes - hardcode_into_libs=yes - ;; - esac - ;; - -gnu*) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - hardcode_into_libs=yes - ;; - -hpux9* | hpux10* | hpux11*) - # Give a soname corresponding to the major version so that dld.sl refuses to - # link against other versions. - version_type=sunos - need_lib_prefix=no - need_version=no - case $host_cpu in - ia64*) - shrext_cmds='.so' - hardcode_into_libs=yes - dynamic_linker="$host_os dld.so" - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - if test "X$HPUX_IA64_MODE" = X32; then - sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" - else - sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" - fi - sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec - ;; - hppa*64*) - shrext_cmds='.sl' - hardcode_into_libs=yes - dynamic_linker="$host_os dld.sl" - shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH - shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" - sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec - ;; - *) - shrext_cmds='.sl' - dynamic_linker="$host_os dld.sl" - shlibpath_var=SHLIB_PATH - shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - ;; - esac - # HP-UX runs *really* slowly unless shared libraries are mode 555. - postinstall_cmds='chmod 555 $lib' - ;; - -interix3*) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=no - hardcode_into_libs=yes - ;; - -irix5* | irix6* | nonstopux*) - case $host_os in - nonstopux*) version_type=nonstopux ;; - *) - if test "$lt_cv_prog_gnu_ld" = yes; then - version_type=linux - else - version_type=irix - fi ;; - esac - need_lib_prefix=no - need_version=no - soname_spec='${libname}${release}${shared_ext}$major' - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}' - case $host_os in - irix5* | nonstopux*) - libsuff= shlibsuff= - ;; - *) - case $LD in # libtool.m4 will add one of these switches to LD - *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") - libsuff= shlibsuff= libmagic=32-bit;; - *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") - libsuff=32 shlibsuff=N32 libmagic=N32;; - *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") - libsuff=64 shlibsuff=64 libmagic=64-bit;; - *) libsuff= shlibsuff= libmagic=never-match;; - esac - ;; - esac - shlibpath_var=LD_LIBRARY${shlibsuff}_PATH - shlibpath_overrides_runpath=no - sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}" - sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}" - hardcode_into_libs=yes - ;; - -# No shared lib support for Linux oldld, aout, or coff. -linux*oldld* | linux*aout* | linux*coff*) - dynamic_linker=no - ;; - -# This must be Linux ELF. -linux*) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=no - # This implies no fast_install, which is unacceptable. - # Some rework will be needed to allow for fast_install - # before this can be enabled. - hardcode_into_libs=yes - - # Append ld.so.conf contents to the search path - if test -f /etc/ld.so.conf; then - lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s", \[$]2)); skip = 1; } { if (!skip) print \[$]0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;/^$/d' | tr '\n' ' '` - sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" - fi - - # We used to test for /lib/ld.so.1 and disable shared libraries on - # powerpc, because MkLinux only supported shared libraries with the - # GNU dynamic linker. Since this was broken with cross compilers, - # most powerpc-linux boxes support dynamic linking these days and - # people can always --disable-shared, the test was removed, and we - # assume the GNU/Linux dynamic linker is in use. - dynamic_linker='GNU/Linux ld.so' - ;; - -knetbsd*-gnu) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=no - hardcode_into_libs=yes - dynamic_linker='GNU ld.so' - ;; - -netbsd*) - version_type=sunos - need_lib_prefix=no - need_version=no - if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' - finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' - dynamic_linker='NetBSD (a.out) ld.so' - else - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - dynamic_linker='NetBSD ld.elf_so' - fi - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - hardcode_into_libs=yes - ;; - -newsos6) - version_type=linux - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - ;; - -nto-qnx*) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - ;; - -openbsd*) - version_type=sunos - sys_lib_dlsearch_path_spec="/usr/lib" - need_lib_prefix=no - # Some older versions of OpenBSD (3.3 at least) *do* need versioned libs. - case $host_os in - openbsd3.3 | openbsd3.3.*) need_version=yes ;; - *) need_version=no ;; - esac - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' - finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' - shlibpath_var=LD_LIBRARY_PATH - if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then - case $host_os in - openbsd2.[[89]] | openbsd2.[[89]].*) - shlibpath_overrides_runpath=no - ;; - *) - shlibpath_overrides_runpath=yes - ;; - esac - else - shlibpath_overrides_runpath=yes - fi - ;; - -os2*) - libname_spec='$name' - shrext_cmds=".dll" - need_lib_prefix=no - library_names_spec='$libname${shared_ext} $libname.a' - dynamic_linker='OS/2 ld.exe' - shlibpath_var=LIBPATH - ;; - -osf3* | osf4* | osf5*) - version_type=osf - need_lib_prefix=no - need_version=no - soname_spec='${libname}${release}${shared_ext}$major' - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - shlibpath_var=LD_LIBRARY_PATH - sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" - sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec" - ;; - -solaris*) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - hardcode_into_libs=yes - # ldd complains unless libraries are executable - postinstall_cmds='chmod +x $lib' - ;; - -sunos4*) - version_type=sunos - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' - finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - if test "$with_gnu_ld" = yes; then - need_lib_prefix=no - fi - need_version=yes - ;; - -sysv4 | sysv4.3*) - version_type=linux - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - case $host_vendor in - sni) - shlibpath_overrides_runpath=no - need_lib_prefix=no - export_dynamic_flag_spec='${wl}-Blargedynsym' - runpath_var=LD_RUN_PATH - ;; - siemens) - need_lib_prefix=no - ;; - motorola) - need_lib_prefix=no - need_version=no - shlibpath_overrides_runpath=no - sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' - ;; - esac - ;; - -sysv4*MP*) - if test -d /usr/nec ;then - version_type=linux - library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}' - soname_spec='$libname${shared_ext}.$major' - shlibpath_var=LD_LIBRARY_PATH - fi - ;; - -sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) - version_type=freebsd-elf - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - hardcode_into_libs=yes - if test "$with_gnu_ld" = yes; then - sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' - shlibpath_overrides_runpath=no - else - sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' - shlibpath_overrides_runpath=yes - case $host_os in - sco3.2v5*) - sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" - ;; - esac - fi - sys_lib_dlsearch_path_spec='/usr/lib' - ;; - -uts4*) - version_type=linux - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - ;; - -*) - dynamic_linker=no - ;; -esac -AC_MSG_RESULT([$dynamic_linker]) -test "$dynamic_linker" = no && can_build_shared=no - -variables_saved_for_relink="PATH $shlibpath_var $runpath_var" -if test "$GCC" = yes; then - variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" -fi -])# AC_LIBTOOL_SYS_DYNAMIC_LINKER - - -# _LT_AC_TAGCONFIG -# ---------------- -AC_DEFUN([_LT_AC_TAGCONFIG], -[AC_ARG_WITH([tags], - [AC_HELP_STRING([--with-tags@<:@=TAGS@:>@], - [include additional configurations @<:@automatic@:>@])], - [tagnames="$withval"]) - -if test -f "$ltmain" && test -n "$tagnames"; then - if test ! -f "${ofile}"; then - AC_MSG_WARN([output file `$ofile' does not exist]) - fi - - if test -z "$LTCC"; then - eval "`$SHELL ${ofile} --config | grep '^LTCC='`" - if test -z "$LTCC"; then - AC_MSG_WARN([output file `$ofile' does not look like a libtool script]) - else - AC_MSG_WARN([using `LTCC=$LTCC', extracted from `$ofile']) - fi - fi - if test -z "$LTCFLAGS"; then - eval "`$SHELL ${ofile} --config | grep '^LTCFLAGS='`" - fi - - # Extract list of available tagged configurations in $ofile. - # Note that this assumes the entire list is on one line. - available_tags=`grep "^available_tags=" "${ofile}" | $SED -e 's/available_tags=\(.*$\)/\1/' -e 's/\"//g'` - - lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," - for tagname in $tagnames; do - IFS="$lt_save_ifs" - # Check whether tagname contains only valid characters - case `$echo "X$tagname" | $Xsed -e 's:[[-_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890,/]]::g'` in - "") ;; - *) AC_MSG_ERROR([invalid tag name: $tagname]) - ;; - esac - - if grep "^# ### BEGIN LIBTOOL TAG CONFIG: $tagname$" < "${ofile}" > /dev/null - then - AC_MSG_ERROR([tag name \"$tagname\" already exists]) - fi - - # Update the list of available tags. - if test -n "$tagname"; then - echo appending configuration tag \"$tagname\" to $ofile - - case $tagname in - CXX) - if test -n "$CXX" && ( test "X$CXX" != "Xno" && - ( (test "X$CXX" = "Xg++" && `g++ -v >/dev/null 2>&1` ) || - (test "X$CXX" != "Xg++"))) ; then - AC_LIBTOOL_LANG_CXX_CONFIG - else - tagname="" - fi - ;; - - F77) - if test -n "$F77" && test "X$F77" != "Xno"; then - AC_LIBTOOL_LANG_F77_CONFIG - else - tagname="" - fi - ;; - - GCJ) - if test -n "$GCJ" && test "X$GCJ" != "Xno"; then - AC_LIBTOOL_LANG_GCJ_CONFIG - else - tagname="" - fi - ;; - - RC) - AC_LIBTOOL_LANG_RC_CONFIG - ;; - - *) - AC_MSG_ERROR([Unsupported tag name: $tagname]) - ;; - esac - - # Append the new tag name to the list of available tags. - if test -n "$tagname" ; then - available_tags="$available_tags $tagname" - fi - fi - done - IFS="$lt_save_ifs" - - # Now substitute the updated list of available tags. - if eval "sed -e 's/^available_tags=.*\$/available_tags=\"$available_tags\"/' \"$ofile\" > \"${ofile}T\""; then - mv "${ofile}T" "$ofile" - chmod +x "$ofile" - else - rm -f "${ofile}T" - AC_MSG_ERROR([unable to update list of available tagged configurations.]) - fi -fi -])# _LT_AC_TAGCONFIG - - -# AC_LIBTOOL_DLOPEN -# ----------------- -# enable checks for dlopen support -AC_DEFUN([AC_LIBTOOL_DLOPEN], - [AC_BEFORE([$0],[AC_LIBTOOL_SETUP]) -])# AC_LIBTOOL_DLOPEN - - -# AC_LIBTOOL_WIN32_DLL -# -------------------- -# declare package support for building win32 DLLs -AC_DEFUN([AC_LIBTOOL_WIN32_DLL], -[AC_BEFORE([$0], [AC_LIBTOOL_SETUP]) -])# AC_LIBTOOL_WIN32_DLL - - -# AC_ENABLE_SHARED([DEFAULT]) -# --------------------------- -# implement the --enable-shared flag -# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'. -AC_DEFUN([AC_ENABLE_SHARED], -[define([AC_ENABLE_SHARED_DEFAULT], ifelse($1, no, no, yes))dnl -AC_ARG_ENABLE([shared], - [AC_HELP_STRING([--enable-shared@<:@=PKGS@:>@], - [build shared libraries @<:@default=]AC_ENABLE_SHARED_DEFAULT[@:>@])], - [p=${PACKAGE-default} - case $enableval in - yes) enable_shared=yes ;; - no) enable_shared=no ;; - *) - enable_shared=no - # Look at the argument we got. We use all the common list separators. - lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," - for pkg in $enableval; do - IFS="$lt_save_ifs" - if test "X$pkg" = "X$p"; then - enable_shared=yes - fi - done - IFS="$lt_save_ifs" - ;; - esac], - [enable_shared=]AC_ENABLE_SHARED_DEFAULT) -])# AC_ENABLE_SHARED - - -# AC_DISABLE_SHARED -# ----------------- -# set the default shared flag to --disable-shared -AC_DEFUN([AC_DISABLE_SHARED], -[AC_BEFORE([$0],[AC_LIBTOOL_SETUP])dnl -AC_ENABLE_SHARED(no) -])# AC_DISABLE_SHARED - - -# AC_ENABLE_STATIC([DEFAULT]) -# --------------------------- -# implement the --enable-static flag -# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'. -AC_DEFUN([AC_ENABLE_STATIC], -[define([AC_ENABLE_STATIC_DEFAULT], ifelse($1, no, no, yes))dnl -AC_ARG_ENABLE([static], - [AC_HELP_STRING([--enable-static@<:@=PKGS@:>@], - [build static libraries @<:@default=]AC_ENABLE_STATIC_DEFAULT[@:>@])], - [p=${PACKAGE-default} - case $enableval in - yes) enable_static=yes ;; - no) enable_static=no ;; - *) - enable_static=no - # Look at the argument we got. We use all the common list separators. - lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," - for pkg in $enableval; do - IFS="$lt_save_ifs" - if test "X$pkg" = "X$p"; then - enable_static=yes - fi - done - IFS="$lt_save_ifs" - ;; - esac], - [enable_static=]AC_ENABLE_STATIC_DEFAULT) -])# AC_ENABLE_STATIC - - -# AC_DISABLE_STATIC -# ----------------- -# set the default static flag to --disable-static -AC_DEFUN([AC_DISABLE_STATIC], -[AC_BEFORE([$0],[AC_LIBTOOL_SETUP])dnl -AC_ENABLE_STATIC(no) -])# AC_DISABLE_STATIC - - -# AC_ENABLE_FAST_INSTALL([DEFAULT]) -# --------------------------------- -# implement the --enable-fast-install flag -# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'. -AC_DEFUN([AC_ENABLE_FAST_INSTALL], -[define([AC_ENABLE_FAST_INSTALL_DEFAULT], ifelse($1, no, no, yes))dnl -AC_ARG_ENABLE([fast-install], - [AC_HELP_STRING([--enable-fast-install@<:@=PKGS@:>@], - [optimize for fast installation @<:@default=]AC_ENABLE_FAST_INSTALL_DEFAULT[@:>@])], - [p=${PACKAGE-default} - case $enableval in - yes) enable_fast_install=yes ;; - no) enable_fast_install=no ;; - *) - enable_fast_install=no - # Look at the argument we got. We use all the common list separators. - lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," - for pkg in $enableval; do - IFS="$lt_save_ifs" - if test "X$pkg" = "X$p"; then - enable_fast_install=yes - fi - done - IFS="$lt_save_ifs" - ;; - esac], - [enable_fast_install=]AC_ENABLE_FAST_INSTALL_DEFAULT) -])# AC_ENABLE_FAST_INSTALL - - -# AC_DISABLE_FAST_INSTALL -# ----------------------- -# set the default to --disable-fast-install -AC_DEFUN([AC_DISABLE_FAST_INSTALL], -[AC_BEFORE([$0],[AC_LIBTOOL_SETUP])dnl -AC_ENABLE_FAST_INSTALL(no) -])# AC_DISABLE_FAST_INSTALL - - -# AC_LIBTOOL_PICMODE([MODE]) -# -------------------------- -# implement the --with-pic flag -# MODE is either `yes' or `no'. If omitted, it defaults to `both'. -AC_DEFUN([AC_LIBTOOL_PICMODE], -[AC_BEFORE([$0],[AC_LIBTOOL_SETUP])dnl -pic_mode=ifelse($#,1,$1,default) -])# AC_LIBTOOL_PICMODE - - -# AC_PROG_EGREP -# ------------- -# This is predefined starting with Autoconf 2.54, so this conditional -# definition can be removed once we require Autoconf 2.54 or later. -m4_ifndef([AC_PROG_EGREP], [AC_DEFUN([AC_PROG_EGREP], -[AC_CACHE_CHECK([for egrep], [ac_cv_prog_egrep], - [if echo a | (grep -E '(a|b)') >/dev/null 2>&1 - then ac_cv_prog_egrep='grep -E' - else ac_cv_prog_egrep='egrep' - fi]) - EGREP=$ac_cv_prog_egrep - AC_SUBST([EGREP]) -])]) - - -# AC_PATH_TOOL_PREFIX -# ------------------- -# find a file program which can recognise shared library -AC_DEFUN([AC_PATH_TOOL_PREFIX], -[AC_REQUIRE([AC_PROG_EGREP])dnl -AC_MSG_CHECKING([for $1]) -AC_CACHE_VAL(lt_cv_path_MAGIC_CMD, -[case $MAGIC_CMD in -[[\\/*] | ?:[\\/]*]) - lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path. - ;; -*) - lt_save_MAGIC_CMD="$MAGIC_CMD" - lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR -dnl $ac_dummy forces splitting on constant user-supplied paths. -dnl POSIX.2 word splitting is done only on the output of word expansions, -dnl not every word. This closes a longstanding sh security hole. - ac_dummy="ifelse([$2], , $PATH, [$2])" - for ac_dir in $ac_dummy; do - IFS="$lt_save_ifs" - test -z "$ac_dir" && ac_dir=. - if test -f $ac_dir/$1; then - lt_cv_path_MAGIC_CMD="$ac_dir/$1" - if test -n "$file_magic_test_file"; then - case $deplibs_check_method in - "file_magic "*) - file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` - MAGIC_CMD="$lt_cv_path_MAGIC_CMD" - if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | - $EGREP "$file_magic_regex" > /dev/null; then - : - else - cat <&2 - -*** Warning: the command libtool uses to detect shared libraries, -*** $file_magic_cmd, produces output that libtool cannot recognize. -*** The result is that libtool may fail to recognize shared libraries -*** as such. This will affect the creation of libtool libraries that -*** depend on shared libraries, but programs linked with such libtool -*** libraries will work regardless of this problem. Nevertheless, you -*** may want to report the problem to your system manager and/or to -*** bug-libtool@gnu.org - -EOF - fi ;; - esac - fi - break - fi - done - IFS="$lt_save_ifs" - MAGIC_CMD="$lt_save_MAGIC_CMD" - ;; -esac]) -MAGIC_CMD="$lt_cv_path_MAGIC_CMD" -if test -n "$MAGIC_CMD"; then - AC_MSG_RESULT($MAGIC_CMD) -else - AC_MSG_RESULT(no) -fi -])# AC_PATH_TOOL_PREFIX - - -# AC_PATH_MAGIC -# ------------- -# find a file program which can recognise a shared library -AC_DEFUN([AC_PATH_MAGIC], -[AC_PATH_TOOL_PREFIX(${ac_tool_prefix}file, /usr/bin$PATH_SEPARATOR$PATH) -if test -z "$lt_cv_path_MAGIC_CMD"; then - if test -n "$ac_tool_prefix"; then - AC_PATH_TOOL_PREFIX(file, /usr/bin$PATH_SEPARATOR$PATH) - else - MAGIC_CMD=: - fi -fi -])# AC_PATH_MAGIC - - -# AC_PROG_LD -# ---------- -# find the pathname to the GNU or non-GNU linker -AC_DEFUN([AC_PROG_LD], -[AC_ARG_WITH([gnu-ld], - [AC_HELP_STRING([--with-gnu-ld], - [assume the C compiler uses GNU ld @<:@default=no@:>@])], - [test "$withval" = no || with_gnu_ld=yes], - [with_gnu_ld=no]) -AC_REQUIRE([LT_AC_PROG_SED])dnl -AC_REQUIRE([AC_PROG_CC])dnl -AC_REQUIRE([AC_CANONICAL_HOST])dnl -AC_REQUIRE([AC_CANONICAL_BUILD])dnl -ac_prog=ld -if test "$GCC" = yes; then - # Check if gcc -print-prog-name=ld gives a path. - AC_MSG_CHECKING([for ld used by $CC]) - case $host in - *-*-mingw*) - # gcc leaves a trailing carriage return which upsets mingw - ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; - *) - ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; - esac - case $ac_prog in - # Accept absolute paths. - [[\\/]]* | ?:[[\\/]]*) - re_direlt='/[[^/]][[^/]]*/\.\./' - # Canonicalize the pathname of ld - ac_prog=`echo $ac_prog| $SED 's%\\\\%/%g'` - while echo $ac_prog | grep "$re_direlt" > /dev/null 2>&1; do - ac_prog=`echo $ac_prog| $SED "s%$re_direlt%/%"` - done - test -z "$LD" && LD="$ac_prog" - ;; - "") - # If it fails, then pretend we aren't using GCC. - ac_prog=ld - ;; - *) - # If it is relative, then search for the first ld in PATH. - with_gnu_ld=unknown - ;; - esac -elif test "$with_gnu_ld" = yes; then - AC_MSG_CHECKING([for GNU ld]) -else - AC_MSG_CHECKING([for non-GNU ld]) -fi -AC_CACHE_VAL(lt_cv_path_LD, -[if test -z "$LD"; then - lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR - for ac_dir in $PATH; do - IFS="$lt_save_ifs" - test -z "$ac_dir" && ac_dir=. - if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then - lt_cv_path_LD="$ac_dir/$ac_prog" - # Check to see if the program is GNU ld. I'd rather use --version, - # but apparently some variants of GNU ld only accept -v. - # Break only if it was the GNU/non-GNU ld that we prefer. - case `"$lt_cv_path_LD" -v 2>&1 &1 /dev/null; then - case $host_cpu in - i*86 ) - # Not sure whether the presence of OpenBSD here was a mistake. - # Let's accept both of them until this is cleared up. - lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[[3-9]]86 (compact )?demand paged shared library' - lt_cv_file_magic_cmd=/usr/bin/file - lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*` - ;; - esac - else - lt_cv_deplibs_check_method=pass_all - fi - ;; - -gnu*) - lt_cv_deplibs_check_method=pass_all - ;; - -hpux10.20* | hpux11*) - lt_cv_file_magic_cmd=/usr/bin/file - case $host_cpu in - ia64*) - lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|ELF-[[0-9]][[0-9]]) shared object file - IA64' - lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so - ;; - hppa*64*) - [lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - PA-RISC [0-9].[0-9]'] - lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl - ;; - *) - lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|PA-RISC[[0-9]].[[0-9]]) shared library' - lt_cv_file_magic_test_file=/usr/lib/libc.sl - ;; - esac - ;; - -interix3*) - # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here - lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|\.a)$' - ;; - -irix5* | irix6* | nonstopux*) - case $LD in - *-32|*"-32 ") libmagic=32-bit;; - *-n32|*"-n32 ") libmagic=N32;; - *-64|*"-64 ") libmagic=64-bit;; - *) libmagic=never-match;; - esac - lt_cv_deplibs_check_method=pass_all - ;; - -# This must be Linux ELF. -linux*) - lt_cv_deplibs_check_method=pass_all - ;; - -netbsd*) - if echo __ELF__ | $CC -E - | grep __ELF__ > /dev/null; then - lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$' - else - lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|_pic\.a)$' - fi - ;; - -newos6*) - lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (executable|dynamic lib)' - lt_cv_file_magic_cmd=/usr/bin/file - lt_cv_file_magic_test_file=/usr/lib/libnls.so - ;; - -nto-qnx*) - lt_cv_deplibs_check_method=unknown - ;; - -openbsd*) - if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then - lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|\.so|_pic\.a)$' - else - lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$' - fi - ;; - -osf3* | osf4* | osf5*) - lt_cv_deplibs_check_method=pass_all - ;; - -solaris*) - lt_cv_deplibs_check_method=pass_all - ;; - -sysv4 | sysv4.3*) - case $host_vendor in - motorola) - lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib) M[[0-9]][[0-9]]* Version [[0-9]]' - lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*` - ;; - ncr) - lt_cv_deplibs_check_method=pass_all - ;; - sequent) - lt_cv_file_magic_cmd='/bin/file' - lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB (shared object|dynamic lib )' - ;; - sni) - lt_cv_file_magic_cmd='/bin/file' - lt_cv_deplibs_check_method="file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB dynamic lib" - lt_cv_file_magic_test_file=/lib/libc.so - ;; - siemens) - lt_cv_deplibs_check_method=pass_all - ;; - pc) - lt_cv_deplibs_check_method=pass_all - ;; - esac - ;; - -sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) - lt_cv_deplibs_check_method=pass_all - ;; -esac -]) -file_magic_cmd=$lt_cv_file_magic_cmd -deplibs_check_method=$lt_cv_deplibs_check_method -test -z "$deplibs_check_method" && deplibs_check_method=unknown -])# AC_DEPLIBS_CHECK_METHOD - - -# AC_PROG_NM -# ---------- -# find the pathname to a BSD-compatible name lister -AC_DEFUN([AC_PROG_NM], -[AC_CACHE_CHECK([for BSD-compatible nm], lt_cv_path_NM, -[if test -n "$NM"; then - # Let the user override the test. - lt_cv_path_NM="$NM" -else - lt_nm_to_check="${ac_tool_prefix}nm" - if test -n "$ac_tool_prefix" && test "$build" = "$host"; then - lt_nm_to_check="$lt_nm_to_check nm" - fi - for lt_tmp_nm in $lt_nm_to_check; do - lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR - for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do - IFS="$lt_save_ifs" - test -z "$ac_dir" && ac_dir=. - tmp_nm="$ac_dir/$lt_tmp_nm" - if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext" ; then - # Check to see if the nm accepts a BSD-compat flag. - # Adding the `sed 1q' prevents false positives on HP-UX, which says: - # nm: unknown option "B" ignored - # Tru64's nm complains that /dev/null is an invalid object file - case `"$tmp_nm" -B /dev/null 2>&1 | sed '1q'` in - */dev/null* | *'Invalid file or object type'*) - lt_cv_path_NM="$tmp_nm -B" - break - ;; - *) - case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in - */dev/null*) - lt_cv_path_NM="$tmp_nm -p" - break - ;; - *) - lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but - continue # so that we can try to find one that supports BSD flags - ;; - esac - ;; - esac - fi - done - IFS="$lt_save_ifs" - done - test -z "$lt_cv_path_NM" && lt_cv_path_NM=nm -fi]) -NM="$lt_cv_path_NM" -])# AC_PROG_NM - - -# AC_CHECK_LIBM -# ------------- -# check for math library -AC_DEFUN([AC_CHECK_LIBM], -[AC_REQUIRE([AC_CANONICAL_HOST])dnl -LIBM= -case $host in -*-*-beos* | *-*-cygwin* | *-*-pw32* | *-*-darwin*) - # These system don't have libm, or don't need it - ;; -*-ncr-sysv4.3*) - AC_CHECK_LIB(mw, _mwvalidcheckl, LIBM="-lmw") - AC_CHECK_LIB(m, cos, LIBM="$LIBM -lm") - ;; -*) - AC_CHECK_LIB(m, cos, LIBM="-lm") - ;; -esac -])# AC_CHECK_LIBM - - -# AC_LIBLTDL_CONVENIENCE([DIRECTORY]) -# ----------------------------------- -# sets LIBLTDL to the link flags for the libltdl convenience library and -# LTDLINCL to the include flags for the libltdl header and adds -# --enable-ltdl-convenience to the configure arguments. Note that -# AC_CONFIG_SUBDIRS is not called here. If DIRECTORY is not provided, -# it is assumed to be `libltdl'. LIBLTDL will be prefixed with -# '${top_builddir}/' and LTDLINCL will be prefixed with '${top_srcdir}/' -# (note the single quotes!). If your package is not flat and you're not -# using automake, define top_builddir and top_srcdir appropriately in -# the Makefiles. -AC_DEFUN([AC_LIBLTDL_CONVENIENCE], -[AC_BEFORE([$0],[AC_LIBTOOL_SETUP])dnl - case $enable_ltdl_convenience in - no) AC_MSG_ERROR([this package needs a convenience libltdl]) ;; - "") enable_ltdl_convenience=yes - ac_configure_args="$ac_configure_args --enable-ltdl-convenience" ;; - esac - LIBLTDL='${top_builddir}/'ifelse($#,1,[$1],['libltdl'])/libltdlc.la - LTDLINCL='-I${top_srcdir}/'ifelse($#,1,[$1],['libltdl']) - # For backwards non-gettext consistent compatibility... - INCLTDL="$LTDLINCL" -])# AC_LIBLTDL_CONVENIENCE - - -# AC_LIBLTDL_INSTALLABLE([DIRECTORY]) -# ----------------------------------- -# sets LIBLTDL to the link flags for the libltdl installable library and -# LTDLINCL to the include flags for the libltdl header and adds -# --enable-ltdl-install to the configure arguments. Note that -# AC_CONFIG_SUBDIRS is not called here. If DIRECTORY is not provided, -# and an installed libltdl is not found, it is assumed to be `libltdl'. -# LIBLTDL will be prefixed with '${top_builddir}/'# and LTDLINCL with -# '${top_srcdir}/' (note the single quotes!). If your package is not -# flat and you're not using automake, define top_builddir and top_srcdir -# appropriately in the Makefiles. -# In the future, this macro may have to be called after AC_PROG_LIBTOOL. -AC_DEFUN([AC_LIBLTDL_INSTALLABLE], -[AC_BEFORE([$0],[AC_LIBTOOL_SETUP])dnl - AC_CHECK_LIB(ltdl, lt_dlinit, - [test x"$enable_ltdl_install" != xyes && enable_ltdl_install=no], - [if test x"$enable_ltdl_install" = xno; then - AC_MSG_WARN([libltdl not installed, but installation disabled]) - else - enable_ltdl_install=yes - fi - ]) - if test x"$enable_ltdl_install" = x"yes"; then - ac_configure_args="$ac_configure_args --enable-ltdl-install" - LIBLTDL='${top_builddir}/'ifelse($#,1,[$1],['libltdl'])/libltdl.la - LTDLINCL='-I${top_srcdir}/'ifelse($#,1,[$1],['libltdl']) - else - ac_configure_args="$ac_configure_args --enable-ltdl-install=no" - LIBLTDL="-lltdl" - LTDLINCL= - fi - # For backwards non-gettext consistent compatibility... - INCLTDL="$LTDLINCL" -])# AC_LIBLTDL_INSTALLABLE - - -# AC_LIBTOOL_CXX -# -------------- -# enable support for C++ libraries -AC_DEFUN([AC_LIBTOOL_CXX], -[AC_REQUIRE([_LT_AC_LANG_CXX]) -])# AC_LIBTOOL_CXX - - -# _LT_AC_LANG_CXX -# --------------- -AC_DEFUN([_LT_AC_LANG_CXX], -[AC_REQUIRE([AC_PROG_CXX]) -AC_REQUIRE([_LT_AC_PROG_CXXCPP]) -_LT_AC_SHELL_INIT([tagnames=${tagnames+${tagnames},}CXX]) -])# _LT_AC_LANG_CXX - -# _LT_AC_PROG_CXXCPP -# ------------------ -AC_DEFUN([_LT_AC_PROG_CXXCPP], -[ -AC_REQUIRE([AC_PROG_CXX]) -if test -n "$CXX" && ( test "X$CXX" != "Xno" && - ( (test "X$CXX" = "Xg++" && `g++ -v >/dev/null 2>&1` ) || - (test "X$CXX" != "Xg++"))) ; then - AC_PROG_CXXCPP -fi -])# _LT_AC_PROG_CXXCPP - -# AC_LIBTOOL_F77 -# -------------- -# enable support for Fortran 77 libraries -AC_DEFUN([AC_LIBTOOL_F77], -[AC_REQUIRE([_LT_AC_LANG_F77]) -])# AC_LIBTOOL_F77 - - -# _LT_AC_LANG_F77 -# --------------- -AC_DEFUN([_LT_AC_LANG_F77], -[AC_REQUIRE([AC_PROG_F77]) -_LT_AC_SHELL_INIT([tagnames=${tagnames+${tagnames},}F77]) -])# _LT_AC_LANG_F77 - - -# AC_LIBTOOL_GCJ -# -------------- -# enable support for GCJ libraries -AC_DEFUN([AC_LIBTOOL_GCJ], -[AC_REQUIRE([_LT_AC_LANG_GCJ]) -])# AC_LIBTOOL_GCJ - - -# _LT_AC_LANG_GCJ -# --------------- -AC_DEFUN([_LT_AC_LANG_GCJ], -[AC_PROVIDE_IFELSE([AC_PROG_GCJ],[], - [AC_PROVIDE_IFELSE([A][M_PROG_GCJ],[], - [AC_PROVIDE_IFELSE([LT_AC_PROG_GCJ],[], - [ifdef([AC_PROG_GCJ],[AC_REQUIRE([AC_PROG_GCJ])], - [ifdef([A][M_PROG_GCJ],[AC_REQUIRE([A][M_PROG_GCJ])], - [AC_REQUIRE([A][C_PROG_GCJ_OR_A][M_PROG_GCJ])])])])])]) -_LT_AC_SHELL_INIT([tagnames=${tagnames+${tagnames},}GCJ]) -])# _LT_AC_LANG_GCJ - - -# AC_LIBTOOL_RC -# ------------- -# enable support for Windows resource files -AC_DEFUN([AC_LIBTOOL_RC], -[AC_REQUIRE([LT_AC_PROG_RC]) -_LT_AC_SHELL_INIT([tagnames=${tagnames+${tagnames},}RC]) -])# AC_LIBTOOL_RC - - -# AC_LIBTOOL_LANG_C_CONFIG -# ------------------------ -# Ensure that the configuration vars for the C compiler are -# suitably defined. Those variables are subsequently used by -# AC_LIBTOOL_CONFIG to write the compiler configuration to `libtool'. -AC_DEFUN([AC_LIBTOOL_LANG_C_CONFIG], [_LT_AC_LANG_C_CONFIG]) -AC_DEFUN([_LT_AC_LANG_C_CONFIG], -[lt_save_CC="$CC" -AC_LANG_PUSH(C) - -# Source file extension for C test sources. -ac_ext=c - -# Object file extension for compiled C test sources. -objext=o -_LT_AC_TAGVAR(objext, $1)=$objext - -# Code to be used in simple compile tests -lt_simple_compile_test_code="int some_variable = 0;\n" - -# Code to be used in simple link tests -lt_simple_link_test_code='int main(){return(0);}\n' - -_LT_AC_SYS_COMPILER - -# save warnings/boilerplate of simple test code -_LT_COMPILER_BOILERPLATE -_LT_LINKER_BOILERPLATE - -## CAVEAT EMPTOR: -## There is no encapsulation within the following macros, do not change -## the running order or otherwise move them around unless you know exactly -## what you are doing... -AC_LIBTOOL_PROG_COMPILER_NO_RTTI($1) -AC_LIBTOOL_PROG_COMPILER_PIC($1) -AC_LIBTOOL_PROG_CC_C_O($1) -AC_LIBTOOL_SYS_HARD_LINK_LOCKS($1) -AC_LIBTOOL_PROG_LD_SHLIBS($1) -AC_LIBTOOL_SYS_DYNAMIC_LINKER($1) -AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH($1) -AC_LIBTOOL_SYS_LIB_STRIP -AC_LIBTOOL_DLOPEN_SELF - -# Report which library types will actually be built -AC_MSG_CHECKING([if libtool supports shared libraries]) -AC_MSG_RESULT([$can_build_shared]) - -AC_MSG_CHECKING([whether to build shared libraries]) -test "$can_build_shared" = "no" && enable_shared=no - -# On AIX, shared libraries and static libraries use the same namespace, and -# are all built from PIC. -case $host_os in -aix3*) - test "$enable_shared" = yes && enable_static=no - if test -n "$RANLIB"; then - archive_cmds="$archive_cmds~\$RANLIB \$lib" - postinstall_cmds='$RANLIB $lib' - fi - ;; - -aix4* | aix5*) - if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then - test "$enable_shared" = yes && enable_static=no - fi - ;; -esac -AC_MSG_RESULT([$enable_shared]) - -AC_MSG_CHECKING([whether to build static libraries]) -# Make sure either enable_shared or enable_static is yes. -test "$enable_shared" = yes || enable_static=yes -AC_MSG_RESULT([$enable_static]) - -AC_LIBTOOL_CONFIG($1) - -AC_LANG_POP -CC="$lt_save_CC" -])# AC_LIBTOOL_LANG_C_CONFIG - - -# AC_LIBTOOL_LANG_CXX_CONFIG -# -------------------------- -# Ensure that the configuration vars for the C compiler are -# suitably defined. Those variables are subsequently used by -# AC_LIBTOOL_CONFIG to write the compiler configuration to `libtool'. -AC_DEFUN([AC_LIBTOOL_LANG_CXX_CONFIG], [_LT_AC_LANG_CXX_CONFIG(CXX)]) -AC_DEFUN([_LT_AC_LANG_CXX_CONFIG], -[AC_LANG_PUSH(C++) -AC_REQUIRE([AC_PROG_CXX]) -AC_REQUIRE([_LT_AC_PROG_CXXCPP]) - -_LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no -_LT_AC_TAGVAR(allow_undefined_flag, $1)= -_LT_AC_TAGVAR(always_export_symbols, $1)=no -_LT_AC_TAGVAR(archive_expsym_cmds, $1)= -_LT_AC_TAGVAR(export_dynamic_flag_spec, $1)= -_LT_AC_TAGVAR(hardcode_direct, $1)=no -_LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)= -_LT_AC_TAGVAR(hardcode_libdir_flag_spec_ld, $1)= -_LT_AC_TAGVAR(hardcode_libdir_separator, $1)= -_LT_AC_TAGVAR(hardcode_minus_L, $1)=no -_LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=unsupported -_LT_AC_TAGVAR(hardcode_automatic, $1)=no -_LT_AC_TAGVAR(module_cmds, $1)= -_LT_AC_TAGVAR(module_expsym_cmds, $1)= -_LT_AC_TAGVAR(link_all_deplibs, $1)=unknown -_LT_AC_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds -_LT_AC_TAGVAR(no_undefined_flag, $1)= -_LT_AC_TAGVAR(whole_archive_flag_spec, $1)= -_LT_AC_TAGVAR(enable_shared_with_static_runtimes, $1)=no - -# Dependencies to place before and after the object being linked: -_LT_AC_TAGVAR(predep_objects, $1)= -_LT_AC_TAGVAR(postdep_objects, $1)= -_LT_AC_TAGVAR(predeps, $1)= -_LT_AC_TAGVAR(postdeps, $1)= -_LT_AC_TAGVAR(compiler_lib_search_path, $1)= - -# Source file extension for C++ test sources. -ac_ext=cpp - -# Object file extension for compiled C++ test sources. -objext=o -_LT_AC_TAGVAR(objext, $1)=$objext - -# Code to be used in simple compile tests -lt_simple_compile_test_code="int some_variable = 0;\n" - -# Code to be used in simple link tests -lt_simple_link_test_code='int main(int, char *[[]]) { return(0); }\n' - -# ltmain only uses $CC for tagged configurations so make sure $CC is set. -_LT_AC_SYS_COMPILER - -# save warnings/boilerplate of simple test code -_LT_COMPILER_BOILERPLATE -_LT_LINKER_BOILERPLATE - -# Allow CC to be a program name with arguments. -lt_save_CC=$CC -lt_save_LD=$LD -lt_save_GCC=$GCC -GCC=$GXX -lt_save_with_gnu_ld=$with_gnu_ld -lt_save_path_LD=$lt_cv_path_LD -if test -n "${lt_cv_prog_gnu_ldcxx+set}"; then - lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx -else - $as_unset lt_cv_prog_gnu_ld -fi -if test -n "${lt_cv_path_LDCXX+set}"; then - lt_cv_path_LD=$lt_cv_path_LDCXX -else - $as_unset lt_cv_path_LD -fi -test -z "${LDCXX+set}" || LD=$LDCXX -CC=${CXX-"c++"} -compiler=$CC -_LT_AC_TAGVAR(compiler, $1)=$CC -_LT_CC_BASENAME([$compiler]) - -# We don't want -fno-exception wen compiling C++ code, so set the -# no_builtin_flag separately -if test "$GXX" = yes; then - _LT_AC_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' -else - _LT_AC_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)= -fi - -if test "$GXX" = yes; then - # Set up default GNU C++ configuration - - AC_PROG_LD - - # Check if GNU C++ uses GNU ld as the underlying linker, since the - # archiving commands below assume that GNU ld is being used. - if test "$with_gnu_ld" = yes; then - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' - - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}--rpath ${wl}$libdir' - _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' - - # If archive_cmds runs LD, not CC, wlarc should be empty - # XXX I think wlarc can be eliminated in ltcf-cxx, but I need to - # investigate it a little bit more. (MM) - wlarc='${wl}' - - # ancient GNU ld didn't support --whole-archive et. al. - if eval "`$CC -print-prog-name=ld` --help 2>&1" | \ - grep 'no-whole-archive' > /dev/null; then - _LT_AC_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' - else - _LT_AC_TAGVAR(whole_archive_flag_spec, $1)= - fi - else - with_gnu_ld=no - wlarc= - - # A generic and very simple default shared library creation - # command for GNU C++ for the case where it uses the native - # linker, instead of GNU ld. If possible, this setting should - # overridden to take advantage of the native linker features on - # the platform it is being used on. - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' - fi - - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep "\-L"' - -else - GXX=no - with_gnu_ld=no - wlarc= -fi - -# PORTME: fill in a description of your system's C++ link characteristics -AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries]) -_LT_AC_TAGVAR(ld_shlibs, $1)=yes -case $host_os in - aix3*) - # FIXME: insert proper C++ library support - _LT_AC_TAGVAR(ld_shlibs, $1)=no - ;; - aix4* | aix5*) - if test "$host_cpu" = ia64; then - # On IA64, the linker does run time linking by default, so we don't - # have to do anything special. - aix_use_runtimelinking=no - exp_sym_flag='-Bexport' - no_entry_flag="" - else - aix_use_runtimelinking=no - - # Test if we are trying to use run time linking or normal - # AIX style linking. If -brtl is somewhere in LDFLAGS, we - # need to do runtime linking. - case $host_os in aix4.[[23]]|aix4.[[23]].*|aix5*) - for ld_flag in $LDFLAGS; do - case $ld_flag in - *-brtl*) - aix_use_runtimelinking=yes - break - ;; - esac - done - ;; - esac - - exp_sym_flag='-bexport' - no_entry_flag='-bnoentry' - fi - - # When large executables or shared objects are built, AIX ld can - # have problems creating the table of contents. If linking a library - # or program results in "error TOC overflow" add -mminimal-toc to - # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not - # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. - - _LT_AC_TAGVAR(archive_cmds, $1)='' - _LT_AC_TAGVAR(hardcode_direct, $1)=yes - _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=':' - _LT_AC_TAGVAR(link_all_deplibs, $1)=yes - - if test "$GXX" = yes; then - case $host_os in aix4.[[012]]|aix4.[[012]].*) - # We only want to do this on AIX 4.2 and lower, the check - # below for broken collect2 doesn't work under 4.3+ - collect2name=`${CC} -print-prog-name=collect2` - if test -f "$collect2name" && \ - strings "$collect2name" | grep resolve_lib_name >/dev/null - then - # We have reworked collect2 - _LT_AC_TAGVAR(hardcode_direct, $1)=yes - else - # We have old collect2 - _LT_AC_TAGVAR(hardcode_direct, $1)=unsupported - # It fails to find uninstalled libraries when the uninstalled - # path is not listed in the libpath. Setting hardcode_minus_L - # to unsupported forces relinking - _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' - _LT_AC_TAGVAR(hardcode_libdir_separator, $1)= - fi - ;; - esac - shared_flag='-shared' - if test "$aix_use_runtimelinking" = yes; then - shared_flag="$shared_flag "'${wl}-G' - fi - else - # not using gcc - if test "$host_cpu" = ia64; then - # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release - # chokes on -Wl,-G. The following line is correct: - shared_flag='-G' - else - if test "$aix_use_runtimelinking" = yes; then - shared_flag='${wl}-G' - else - shared_flag='${wl}-bM:SRE' - fi - fi - fi - - # It seems that -bexpall does not export symbols beginning with - # underscore (_), so it is better to generate a list of symbols to export. - _LT_AC_TAGVAR(always_export_symbols, $1)=yes - if test "$aix_use_runtimelinking" = yes; then - # Warning - without using the other runtime loading flags (-brtl), - # -berok will link without error, but may produce a broken library. - _LT_AC_TAGVAR(allow_undefined_flag, $1)='-berok' - # Determine the default libpath from the value encoded in an empty executable. - _LT_AC_SYS_LIBPATH_AIX - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" - - _LT_AC_TAGVAR(archive_expsym_cmds, $1)="\$CC"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then echo "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag" - else - if test "$host_cpu" = ia64; then - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $libdir:/usr/lib:/lib' - _LT_AC_TAGVAR(allow_undefined_flag, $1)="-z nodefs" - _LT_AC_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols" - else - # Determine the default libpath from the value encoded in an empty executable. - _LT_AC_SYS_LIBPATH_AIX - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" - # Warning - without using the other run time loading flags, - # -berok will link without error, but may produce a broken library. - _LT_AC_TAGVAR(no_undefined_flag, $1)=' ${wl}-bernotok' - _LT_AC_TAGVAR(allow_undefined_flag, $1)=' ${wl}-berok' - # Exported symbols can be pulled into shared objects from archives - _LT_AC_TAGVAR(whole_archive_flag_spec, $1)='$convenience' - _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=yes - # This is similar to how AIX traditionally builds its shared libraries. - _LT_AC_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' - fi - fi - ;; - - beos*) - if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then - _LT_AC_TAGVAR(allow_undefined_flag, $1)=unsupported - # Joseph Beckenbach says some releases of gcc - # support --undefined. This deserves some investigation. FIXME - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - else - _LT_AC_TAGVAR(ld_shlibs, $1)=no - fi - ;; - - chorus*) - case $cc_basename in - *) - # FIXME: insert proper C++ library support - _LT_AC_TAGVAR(ld_shlibs, $1)=no - ;; - esac - ;; - - cygwin* | mingw* | pw32*) - # _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless, - # as there is no search path for DLLs. - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' - _LT_AC_TAGVAR(allow_undefined_flag, $1)=unsupported - _LT_AC_TAGVAR(always_export_symbols, $1)=no - _LT_AC_TAGVAR(enable_shared_with_static_runtimes, $1)=yes - - if $LD --help 2>&1 | grep 'auto-import' > /dev/null; then - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' - # If the export-symbols file already is a .def file (1st line - # is EXPORTS), use it as is; otherwise, prepend... - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then - cp $export_symbols $output_objdir/$soname.def; - else - echo EXPORTS > $output_objdir/$soname.def; - cat $export_symbols >> $output_objdir/$soname.def; - fi~ - $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' - else - _LT_AC_TAGVAR(ld_shlibs, $1)=no - fi - ;; - darwin* | rhapsody*) - case $host_os in - rhapsody* | darwin1.[[012]]) - _LT_AC_TAGVAR(allow_undefined_flag, $1)='${wl}-undefined ${wl}suppress' - ;; - *) # Darwin 1.3 on - if test -z ${MACOSX_DEPLOYMENT_TARGET} ; then - _LT_AC_TAGVAR(allow_undefined_flag, $1)='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' - else - case ${MACOSX_DEPLOYMENT_TARGET} in - 10.[[012]]) - _LT_AC_TAGVAR(allow_undefined_flag, $1)='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' - ;; - 10.*) - _LT_AC_TAGVAR(allow_undefined_flag, $1)='${wl}-undefined ${wl}dynamic_lookup' - ;; - esac - fi - ;; - esac - _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no - _LT_AC_TAGVAR(hardcode_direct, $1)=no - _LT_AC_TAGVAR(hardcode_automatic, $1)=yes - _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=unsupported - _LT_AC_TAGVAR(whole_archive_flag_spec, $1)='' - _LT_AC_TAGVAR(link_all_deplibs, $1)=yes - - if test "$GXX" = yes ; then - lt_int_apple_cc_single_mod=no - output_verbose_link_cmd='echo' - if $CC -dumpspecs 2>&1 | $EGREP 'single_module' >/dev/null ; then - lt_int_apple_cc_single_mod=yes - fi - if test "X$lt_int_apple_cc_single_mod" = Xyes ; then - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -dynamiclib -single_module $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags -install_name $rpath/$soname $verstring' - else - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -r -keep_private_externs -nostdlib -o ${lib}-master.o $libobjs~$CC -dynamiclib $allow_undefined_flag -o $lib ${lib}-master.o $deplibs $compiler_flags -install_name $rpath/$soname $verstring' - fi - _LT_AC_TAGVAR(module_cmds, $1)='$CC $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags' - # Don't fix this by using the ld -exported_symbols_list flag, it doesn't exist in older darwin lds - if test "X$lt_int_apple_cc_single_mod" = Xyes ; then - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC -dynamiclib -single_module $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags -install_name $rpath/$soname $verstring~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' - else - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC -r -keep_private_externs -nostdlib -o ${lib}-master.o $libobjs~$CC -dynamiclib $allow_undefined_flag -o $lib ${lib}-master.o $deplibs $compiler_flags -install_name $rpath/$soname $verstring~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' - fi - _LT_AC_TAGVAR(module_expsym_cmds, $1)='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' - else - case $cc_basename in - xlc*) - output_verbose_link_cmd='echo' - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -qmkshrobj ${wl}-single_module $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-install_name ${wl}`echo $rpath/$soname` $verstring' - _LT_AC_TAGVAR(module_cmds, $1)='$CC $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags' - # Don't fix this by using the ld -exported_symbols_list flag, it doesn't exist in older darwin lds - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC -qmkshrobj ${wl}-single_module $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-install_name ${wl}$rpath/$soname $verstring~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' - _LT_AC_TAGVAR(module_expsym_cmds, $1)='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' - ;; - *) - _LT_AC_TAGVAR(ld_shlibs, $1)=no - ;; - esac - fi - ;; - - dgux*) - case $cc_basename in - ec++*) - # FIXME: insert proper C++ library support - _LT_AC_TAGVAR(ld_shlibs, $1)=no - ;; - ghcx*) - # Green Hills C++ Compiler - # FIXME: insert proper C++ library support - _LT_AC_TAGVAR(ld_shlibs, $1)=no - ;; - *) - # FIXME: insert proper C++ library support - _LT_AC_TAGVAR(ld_shlibs, $1)=no - ;; - esac - ;; - freebsd[[12]]*) - # C++ shared libraries reported to be fairly broken before switch to ELF - _LT_AC_TAGVAR(ld_shlibs, $1)=no - ;; - freebsd-elf*) - _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no - ;; - freebsd* | kfreebsd*-gnu | dragonfly*) - # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF - # conventions - _LT_AC_TAGVAR(ld_shlibs, $1)=yes - ;; - gnu*) - ;; - hpux9*) - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' - _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: - _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' - _LT_AC_TAGVAR(hardcode_direct, $1)=yes - _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH, - # but as the default - # location of the library. - - case $cc_basename in - CC*) - # FIXME: insert proper C++ library support - _LT_AC_TAGVAR(ld_shlibs, $1)=no - ;; - aCC*) - _LT_AC_TAGVAR(archive_cmds, $1)='$rm $output_objdir/$soname~$CC -b ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - # - # There doesn't appear to be a way to prevent this compiler from - # explicitly linking system object files so we need to strip them - # from the output so that they don't get included in the library - # dependencies. - output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | grep "[[-]]L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; echo $list' - ;; - *) - if test "$GXX" = yes; then - _LT_AC_TAGVAR(archive_cmds, $1)='$rm $output_objdir/$soname~$CC -shared -nostdlib -fPIC ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' - else - # FIXME: insert proper C++ library support - _LT_AC_TAGVAR(ld_shlibs, $1)=no - fi - ;; - esac - ;; - hpux10*|hpux11*) - if test $with_gnu_ld = no; then - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' - _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: - - case $host_cpu in - hppa*64*|ia64*) - _LT_AC_TAGVAR(hardcode_libdir_flag_spec_ld, $1)='+b $libdir' - ;; - *) - _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' - ;; - esac - fi - case $host_cpu in - hppa*64*|ia64*) - _LT_AC_TAGVAR(hardcode_direct, $1)=no - _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no - ;; - *) - _LT_AC_TAGVAR(hardcode_direct, $1)=yes - _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH, - # but as the default - # location of the library. - ;; - esac - - case $cc_basename in - CC*) - # FIXME: insert proper C++ library support - _LT_AC_TAGVAR(ld_shlibs, $1)=no - ;; - aCC*) - case $host_cpu in - hppa*64*) - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' - ;; - ia64*) - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' - ;; - *) - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' - ;; - esac - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - # - # There doesn't appear to be a way to prevent this compiler from - # explicitly linking system object files so we need to strip them - # from the output so that they don't get included in the library - # dependencies. - output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | grep "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; echo $list' - ;; - *) - if test "$GXX" = yes; then - if test $with_gnu_ld = no; then - case $host_cpu in - hppa*64*) - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' - ;; - ia64*) - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' - ;; - *) - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' - ;; - esac - fi - else - # FIXME: insert proper C++ library support - _LT_AC_TAGVAR(ld_shlibs, $1)=no - fi - ;; - esac - ;; - interix3*) - _LT_AC_TAGVAR(hardcode_direct, $1)=no - _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' - _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' - # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. - # Instead, shared libraries are loaded at an image base (0x10000000 by - # default) and relocated if they conflict, which is a slow very memory - # consuming and fragmenting process. To avoid this, we pick a random, - # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link - # time. Moving up from 0x10000000 also allows more sbrk(2) space. - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' - ;; - irix5* | irix6*) - case $cc_basename in - CC*) - # SGI C++ - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib' - - # Archives containing C++ object files must be created using - # "CC -ar", where "CC" is the IRIX C++ compiler. This is - # necessary to make sure instantiated templates are included - # in the archive. - _LT_AC_TAGVAR(old_archive_cmds, $1)='$CC -ar -WR,-u -o $oldlib $oldobjs' - ;; - *) - if test "$GXX" = yes; then - if test "$with_gnu_ld" = no; then - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' - else - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` -o $lib' - fi - fi - _LT_AC_TAGVAR(link_all_deplibs, $1)=yes - ;; - esac - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' - _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: - ;; - linux*) - case $cc_basename in - KCC*) - # Kuck and Associates, Inc. (KAI) C++ Compiler - - # KCC will only create a shared library if the output file - # ends with ".so" (or ".sl" for HP-UX), so rename the library - # to its proper name (with version) after linking. - _LT_AC_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib ${wl}-retain-symbols-file,$export_symbols; mv \$templib $lib' - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - # - # There doesn't appear to be a way to prevent this compiler from - # explicitly linking system object files so we need to strip them - # from the output so that they don't get included in the library - # dependencies. - output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | grep "ld"`; rm -f libconftest$shared_ext; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; echo $list' - - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}--rpath,$libdir' - _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' - - # Archives containing C++ object files must be created using - # "CC -Bstatic", where "CC" is the KAI C++ compiler. - _LT_AC_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' - ;; - icpc*) - # Intel C++ - with_gnu_ld=yes - # version 8.0 and above of icpc choke on multiply defined symbols - # if we add $predep_objects and $postdep_objects, however 7.1 and - # earlier do not add the objects themselves. - case `$CC -V 2>&1` in - *"Version 7."*) - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' - ;; - *) # Version 8.0 or newer - tmp_idyn= - case $host_cpu in - ia64*) tmp_idyn=' -i_dynamic';; - esac - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' - ;; - esac - _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' - _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' - _LT_AC_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive$convenience ${wl}--no-whole-archive' - ;; - pgCC*) - # Portland Group C++ compiler - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib' - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib' - - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}--rpath ${wl}$libdir' - _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' - _LT_AC_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $echo \"$new_convenience\"` ${wl}--no-whole-archive' - ;; - cxx*) - # Compaq C++ - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib ${wl}-retain-symbols-file $wl$export_symbols' - - runpath_var=LD_RUN_PATH - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' - _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: - - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - # - # There doesn't appear to be a way to prevent this compiler from - # explicitly linking system object files so we need to strip them - # from the output so that they don't get included in the library - # dependencies. - output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep "ld"`; templist=`echo $templist | $SED "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; echo $list' - ;; - esac - ;; - lynxos*) - # FIXME: insert proper C++ library support - _LT_AC_TAGVAR(ld_shlibs, $1)=no - ;; - m88k*) - # FIXME: insert proper C++ library support - _LT_AC_TAGVAR(ld_shlibs, $1)=no - ;; - mvs*) - case $cc_basename in - cxx*) - # FIXME: insert proper C++ library support - _LT_AC_TAGVAR(ld_shlibs, $1)=no - ;; - *) - # FIXME: insert proper C++ library support - _LT_AC_TAGVAR(ld_shlibs, $1)=no - ;; - esac - ;; - netbsd*) - if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then - _LT_AC_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags' - wlarc= - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' - _LT_AC_TAGVAR(hardcode_direct, $1)=yes - _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no - fi - # Workaround some broken pre-1.5 toolchains - output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep conftest.$objext | $SED -e "s:-lgcc -lc -lgcc::"' - ;; - openbsd2*) - # C++ shared libraries are fairly broken - _LT_AC_TAGVAR(ld_shlibs, $1)=no - ;; - openbsd*) - _LT_AC_TAGVAR(hardcode_direct, $1)=yes - _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' - if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file,$export_symbols -o $lib' - _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' - _LT_AC_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' - fi - output_verbose_link_cmd='echo' - ;; - osf3*) - case $cc_basename in - KCC*) - # Kuck and Associates, Inc. (KAI) C++ Compiler - - # KCC will only create a shared library if the output file - # ends with ".so" (or ".sl" for HP-UX), so rename the library - # to its proper name (with version) after linking. - _LT_AC_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' - - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' - _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: - - # Archives containing C++ object files must be created using - # "CC -Bstatic", where "CC" is the KAI C++ compiler. - _LT_AC_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' - - ;; - RCC*) - # Rational C++ 2.4.1 - # FIXME: insert proper C++ library support - _LT_AC_TAGVAR(ld_shlibs, $1)=no - ;; - cxx*) - _LT_AC_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $soname `test -n "$verstring" && echo ${wl}-set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib' - - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' - _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: - - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - # - # There doesn't appear to be a way to prevent this compiler from - # explicitly linking system object files so we need to strip them - # from the output so that they don't get included in the library - # dependencies. - output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep "ld" | grep -v "ld:"`; templist=`echo $templist | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; echo $list' - ;; - *) - if test "$GXX" = yes && test "$with_gnu_ld" = no; then - _LT_AC_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' - - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' - _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: - - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep "\-L"' - - else - # FIXME: insert proper C++ library support - _LT_AC_TAGVAR(ld_shlibs, $1)=no - fi - ;; - esac - ;; - osf4* | osf5*) - case $cc_basename in - KCC*) - # Kuck and Associates, Inc. (KAI) C++ Compiler - - # KCC will only create a shared library if the output file - # ends with ".so" (or ".sl" for HP-UX), so rename the library - # to its proper name (with version) after linking. - _LT_AC_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' - - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' - _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: - - # Archives containing C++ object files must be created using - # the KAI C++ compiler. - _LT_AC_TAGVAR(old_archive_cmds, $1)='$CC -o $oldlib $oldobjs' - ;; - RCC*) - # Rational C++ 2.4.1 - # FIXME: insert proper C++ library support - _LT_AC_TAGVAR(ld_shlibs, $1)=no - ;; - cxx*) - _LT_AC_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib' - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~ - echo "-hidden">> $lib.exp~ - $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname -Wl,-input -Wl,$lib.exp `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib~ - $rm $lib.exp' - - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' - _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: - - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - # - # There doesn't appear to be a way to prevent this compiler from - # explicitly linking system object files so we need to strip them - # from the output so that they don't get included in the library - # dependencies. - output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep "ld" | grep -v "ld:"`; templist=`echo $templist | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; echo $list' - ;; - *) - if test "$GXX" = yes && test "$with_gnu_ld" = no; then - _LT_AC_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' - - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' - _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: - - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep "\-L"' - - else - # FIXME: insert proper C++ library support - _LT_AC_TAGVAR(ld_shlibs, $1)=no - fi - ;; - esac - ;; - psos*) - # FIXME: insert proper C++ library support - _LT_AC_TAGVAR(ld_shlibs, $1)=no - ;; - sunos4*) - case $cc_basename in - CC*) - # Sun C++ 4.x - # FIXME: insert proper C++ library support - _LT_AC_TAGVAR(ld_shlibs, $1)=no - ;; - lcc*) - # Lucid - # FIXME: insert proper C++ library support - _LT_AC_TAGVAR(ld_shlibs, $1)=no - ;; - *) - # FIXME: insert proper C++ library support - _LT_AC_TAGVAR(ld_shlibs, $1)=no - ;; - esac - ;; - solaris*) - case $cc_basename in - CC*) - # Sun C++ 4.2, 5.x and Centerline C++ - _LT_AC_TAGVAR(archive_cmds_need_lc,$1)=yes - _LT_AC_TAGVAR(no_undefined_flag, $1)=' -zdefs' - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~ - $CC -G${allow_undefined_flag} ${wl}-M ${wl}$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$rm $lib.exp' - - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' - _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no - case $host_os in - solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; - *) - # The C++ compiler is used as linker so we must use $wl - # flag to pass the commands to the underlying system - # linker. We must also pass each convience library through - # to the system linker between allextract/defaultextract. - # The C++ compiler will combine linker options so we - # cannot just pass the convience library names through - # without $wl. - # Supported since Solaris 2.6 (maybe 2.5.1?) - _LT_AC_TAGVAR(whole_archive_flag_spec, $1)='${wl}-z ${wl}allextract`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $echo \"$new_convenience\"` ${wl}-z ${wl}defaultextract' - ;; - esac - _LT_AC_TAGVAR(link_all_deplibs, $1)=yes - - output_verbose_link_cmd='echo' - - # Archives containing C++ object files must be created using - # "CC -xar", where "CC" is the Sun C++ compiler. This is - # necessary to make sure instantiated templates are included - # in the archive. - _LT_AC_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs' - ;; - gcx*) - # Green Hills C++ Compiler - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' - - # The C++ compiler must be used to create the archive. - _LT_AC_TAGVAR(old_archive_cmds, $1)='$CC $LDFLAGS -archive -o $oldlib $oldobjs' - ;; - *) - # GNU C++ compiler with Solaris linker - if test "$GXX" = yes && test "$with_gnu_ld" = no; then - _LT_AC_TAGVAR(no_undefined_flag, $1)=' ${wl}-z ${wl}defs' - if $CC --version | grep -v '^2\.7' > /dev/null; then - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~ - $CC -shared -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$rm $lib.exp' - - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - output_verbose_link_cmd="$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep \"\-L\"" - else - # g++ 2.7 appears to require `-G' NOT `-shared' on this - # platform. - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -G -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~ - $CC -G -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$rm $lib.exp' - - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - output_verbose_link_cmd="$CC -G $CFLAGS -v conftest.$objext 2>&1 | grep \"\-L\"" - fi - - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $wl$libdir' - fi - ;; - esac - ;; - sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*) - _LT_AC_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' - _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no - _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no - runpath_var='LD_RUN_PATH' - - case $cc_basename in - CC*) - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - ;; - *) - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - ;; - esac - ;; - sysv5* | sco3.2v5* | sco5v6*) - # Note: We can NOT use -z defs as we might desire, because we do not - # link with -lc, and that would cause any symbols used from libc to - # always be unresolved, which means just about no library would - # ever link correctly. If we're not using GNU ld we use -z text - # though, which does catch some bad symbols but isn't as heavy-handed - # as -z defs. - # For security reasons, it is highly recommended that you always - # use absolute paths for naming shared libraries, and exclude the - # DT_RUNPATH tag from executables and libraries. But doing so - # requires that you compile everything twice, which is a pain. - # So that behaviour is only enabled if SCOABSPATH is set to a - # non-empty value in the environment. Most likely only useful for - # creating official distributions of packages. - # This is a hack until libtool officially supports absolute path - # names for shared libraries. - _LT_AC_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' - _LT_AC_TAGVAR(allow_undefined_flag, $1)='${wl}-z,nodefs' - _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no - _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='`test -z "$SCOABSPATH" && echo ${wl}-R,$libdir`' - _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=':' - _LT_AC_TAGVAR(link_all_deplibs, $1)=yes - _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Bexport' - runpath_var='LD_RUN_PATH' - - case $cc_basename in - CC*) - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,\${SCOABSPATH:+${install_libdir}/}$soname -o $lib $libobjs $deplibs $compiler_flags' - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,\${SCOABSPATH:+${install_libdir}/}$soname -o $lib $libobjs $deplibs $compiler_flags' - ;; - *) - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,\${SCOABSPATH:+${install_libdir}/}$soname -o $lib $libobjs $deplibs $compiler_flags' - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,\${SCOABSPATH:+${install_libdir}/}$soname -o $lib $libobjs $deplibs $compiler_flags' - ;; - esac - ;; - tandem*) - case $cc_basename in - NCC*) - # NonStop-UX NCC 3.20 - # FIXME: insert proper C++ library support - _LT_AC_TAGVAR(ld_shlibs, $1)=no - ;; - *) - # FIXME: insert proper C++ library support - _LT_AC_TAGVAR(ld_shlibs, $1)=no - ;; - esac - ;; - vxworks*) - # FIXME: insert proper C++ library support - _LT_AC_TAGVAR(ld_shlibs, $1)=no - ;; - *) - # FIXME: insert proper C++ library support - _LT_AC_TAGVAR(ld_shlibs, $1)=no - ;; -esac -AC_MSG_RESULT([$_LT_AC_TAGVAR(ld_shlibs, $1)]) -test "$_LT_AC_TAGVAR(ld_shlibs, $1)" = no && can_build_shared=no - -_LT_AC_TAGVAR(GCC, $1)="$GXX" -_LT_AC_TAGVAR(LD, $1)="$LD" - -## CAVEAT EMPTOR: -## There is no encapsulation within the following macros, do not change -## the running order or otherwise move them around unless you know exactly -## what you are doing... -AC_LIBTOOL_POSTDEP_PREDEP($1) -AC_LIBTOOL_PROG_COMPILER_PIC($1) -AC_LIBTOOL_PROG_CC_C_O($1) -AC_LIBTOOL_SYS_HARD_LINK_LOCKS($1) -AC_LIBTOOL_PROG_LD_SHLIBS($1) -AC_LIBTOOL_SYS_DYNAMIC_LINKER($1) -AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH($1) - -AC_LIBTOOL_CONFIG($1) - -AC_LANG_POP -CC=$lt_save_CC -LDCXX=$LD -LD=$lt_save_LD -GCC=$lt_save_GCC -with_gnu_ldcxx=$with_gnu_ld -with_gnu_ld=$lt_save_with_gnu_ld -lt_cv_path_LDCXX=$lt_cv_path_LD -lt_cv_path_LD=$lt_save_path_LD -lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld -lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld -])# AC_LIBTOOL_LANG_CXX_CONFIG - -# AC_LIBTOOL_POSTDEP_PREDEP([TAGNAME]) -# ------------------------------------ -# Figure out "hidden" library dependencies from verbose -# compiler output when linking a shared library. -# Parse the compiler output and extract the necessary -# objects, libraries and library flags. -AC_DEFUN([AC_LIBTOOL_POSTDEP_PREDEP],[ -dnl we can't use the lt_simple_compile_test_code here, -dnl because it contains code intended for an executable, -dnl not a library. It's possible we should let each -dnl tag define a new lt_????_link_test_code variable, -dnl but it's only used here... -ifelse([$1],[],[cat > conftest.$ac_ext < conftest.$ac_ext < conftest.$ac_ext < conftest.$ac_ext <> "$cfgfile" -ifelse([$1], [], -[#! $SHELL - -# `$echo "$cfgfile" | sed 's%^.*/%%'` - Provide generalized library-building support services. -# Generated automatically by $PROGRAM (GNU $PACKAGE $VERSION$TIMESTAMP) -# NOTE: Changes made to this file will be lost: look at ltmain.sh. -# -# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001 -# Free Software Foundation, Inc. -# -# This file is part of GNU Libtool: -# Originally by Gordon Matzigkeit , 1996 -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# As a special exception to the GNU General Public License, if you -# distribute this file as part of a program that contains a -# configuration script generated by Autoconf, you may include it under -# the same distribution terms that you use for the rest of that program. - -# A sed program that does not truncate output. -SED=$lt_SED - -# Sed that helps us avoid accidentally triggering echo(1) options like -n. -Xsed="$SED -e 1s/^X//" - -# The HP-UX ksh and POSIX shell print the target directory to stdout -# if CDPATH is set. -(unset CDPATH) >/dev/null 2>&1 && unset CDPATH - -# The names of the tagged configurations supported by this script. -available_tags= - -# ### BEGIN LIBTOOL CONFIG], -[# ### BEGIN LIBTOOL TAG CONFIG: $tagname]) - -# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`: - -# Shell to use when invoking shell scripts. -SHELL=$lt_SHELL - -# Whether or not to build shared libraries. -build_libtool_libs=$enable_shared - -# Whether or not to build static libraries. -build_old_libs=$enable_static - -# Whether or not to add -lc for building shared libraries. -build_libtool_need_lc=$_LT_AC_TAGVAR(archive_cmds_need_lc, $1) - -# Whether or not to disallow shared libs when runtime libs are static -allow_libtool_libs_with_static_runtimes=$_LT_AC_TAGVAR(enable_shared_with_static_runtimes, $1) - -# Whether or not to optimize for fast installation. -fast_install=$enable_fast_install - -# The host system. -host_alias=$host_alias -host=$host -host_os=$host_os - -# The build system. -build_alias=$build_alias -build=$build -build_os=$build_os - -# An echo program that does not interpret backslashes. -echo=$lt_echo - -# The archiver. -AR=$lt_AR -AR_FLAGS=$lt_AR_FLAGS - -# A C compiler. -LTCC=$lt_LTCC - -# LTCC compiler flags. -LTCFLAGS=$lt_LTCFLAGS - -# A language-specific compiler. -CC=$lt_[]_LT_AC_TAGVAR(compiler, $1) - -# Is the compiler the GNU C compiler? -with_gcc=$_LT_AC_TAGVAR(GCC, $1) - -# An ERE matcher. -EGREP=$lt_EGREP - -# The linker used to build libraries. -LD=$lt_[]_LT_AC_TAGVAR(LD, $1) - -# Whether we need hard or soft links. -LN_S=$lt_LN_S - -# A BSD-compatible nm program. -NM=$lt_NM - -# A symbol stripping program -STRIP=$lt_STRIP - -# Used to examine libraries when file_magic_cmd begins "file" -MAGIC_CMD=$MAGIC_CMD - -# Used on cygwin: DLL creation program. -DLLTOOL="$DLLTOOL" - -# Used on cygwin: object dumper. -OBJDUMP="$OBJDUMP" - -# Used on cygwin: assembler. -AS="$AS" - -# The name of the directory that contains temporary libtool files. -objdir=$objdir - -# How to create reloadable object files. -reload_flag=$lt_reload_flag -reload_cmds=$lt_reload_cmds - -# How to pass a linker flag through the compiler. -wl=$lt_[]_LT_AC_TAGVAR(lt_prog_compiler_wl, $1) - -# Object file suffix (normally "o"). -objext="$ac_objext" - -# Old archive suffix (normally "a"). -libext="$libext" - -# Shared library suffix (normally ".so"). -shrext_cmds='$shrext_cmds' - -# Executable file suffix (normally ""). -exeext="$exeext" - -# Additional compiler flags for building library objects. -pic_flag=$lt_[]_LT_AC_TAGVAR(lt_prog_compiler_pic, $1) -pic_mode=$pic_mode - -# What is the maximum length of a command? -max_cmd_len=$lt_cv_sys_max_cmd_len - -# Does compiler simultaneously support -c and -o options? -compiler_c_o=$lt_[]_LT_AC_TAGVAR(lt_cv_prog_compiler_c_o, $1) - -# Must we lock files when doing compilation? -need_locks=$lt_need_locks - -# Do we need the lib prefix for modules? -need_lib_prefix=$need_lib_prefix - -# Do we need a version for libraries? -need_version=$need_version - -# Whether dlopen is supported. -dlopen_support=$enable_dlopen - -# Whether dlopen of programs is supported. -dlopen_self=$enable_dlopen_self - -# Whether dlopen of statically linked programs is supported. -dlopen_self_static=$enable_dlopen_self_static - -# Compiler flag to prevent dynamic linking. -link_static_flag=$lt_[]_LT_AC_TAGVAR(lt_prog_compiler_static, $1) - -# Compiler flag to turn off builtin functions. -no_builtin_flag=$lt_[]_LT_AC_TAGVAR(lt_prog_compiler_no_builtin_flag, $1) - -# Compiler flag to allow reflexive dlopens. -export_dynamic_flag_spec=$lt_[]_LT_AC_TAGVAR(export_dynamic_flag_spec, $1) - -# Compiler flag to generate shared objects directly from archives. -whole_archive_flag_spec=$lt_[]_LT_AC_TAGVAR(whole_archive_flag_spec, $1) - -# Compiler flag to generate thread-safe objects. -thread_safe_flag_spec=$lt_[]_LT_AC_TAGVAR(thread_safe_flag_spec, $1) - -# Library versioning type. -version_type=$version_type - -# Format of library name prefix. -libname_spec=$lt_libname_spec - -# List of archive names. First name is the real one, the rest are links. -# The last name is the one that the linker finds with -lNAME. -library_names_spec=$lt_library_names_spec - -# The coded name of the library, if different from the real name. -soname_spec=$lt_soname_spec - -# Commands used to build and install an old-style archive. -RANLIB=$lt_RANLIB -old_archive_cmds=$lt_[]_LT_AC_TAGVAR(old_archive_cmds, $1) -old_postinstall_cmds=$lt_old_postinstall_cmds -old_postuninstall_cmds=$lt_old_postuninstall_cmds - -# Create an old-style archive from a shared archive. -old_archive_from_new_cmds=$lt_[]_LT_AC_TAGVAR(old_archive_from_new_cmds, $1) - -# Create a temporary old-style archive to link instead of a shared archive. -old_archive_from_expsyms_cmds=$lt_[]_LT_AC_TAGVAR(old_archive_from_expsyms_cmds, $1) - -# Commands used to build and install a shared archive. -archive_cmds=$lt_[]_LT_AC_TAGVAR(archive_cmds, $1) -archive_expsym_cmds=$lt_[]_LT_AC_TAGVAR(archive_expsym_cmds, $1) -postinstall_cmds=$lt_postinstall_cmds -postuninstall_cmds=$lt_postuninstall_cmds - -# Commands used to build a loadable module (assumed same as above if empty) -module_cmds=$lt_[]_LT_AC_TAGVAR(module_cmds, $1) -module_expsym_cmds=$lt_[]_LT_AC_TAGVAR(module_expsym_cmds, $1) - -# Commands to strip libraries. -old_striplib=$lt_old_striplib -striplib=$lt_striplib - -# Dependencies to place before the objects being linked to create a -# shared library. -predep_objects=$lt_[]_LT_AC_TAGVAR(predep_objects, $1) - -# Dependencies to place after the objects being linked to create a -# shared library. -postdep_objects=$lt_[]_LT_AC_TAGVAR(postdep_objects, $1) - -# Dependencies to place before the objects being linked to create a -# shared library. -predeps=$lt_[]_LT_AC_TAGVAR(predeps, $1) - -# Dependencies to place after the objects being linked to create a -# shared library. -postdeps=$lt_[]_LT_AC_TAGVAR(postdeps, $1) - -# The library search path used internally by the compiler when linking -# a shared library. -compiler_lib_search_path=$lt_[]_LT_AC_TAGVAR(compiler_lib_search_path, $1) - -# Method to check whether dependent libraries are shared objects. -deplibs_check_method=$lt_deplibs_check_method - -# Command to use when deplibs_check_method == file_magic. -file_magic_cmd=$lt_file_magic_cmd - -# Flag that allows shared libraries with undefined symbols to be built. -allow_undefined_flag=$lt_[]_LT_AC_TAGVAR(allow_undefined_flag, $1) - -# Flag that forces no undefined symbols. -no_undefined_flag=$lt_[]_LT_AC_TAGVAR(no_undefined_flag, $1) - -# Commands used to finish a libtool library installation in a directory. -finish_cmds=$lt_finish_cmds - -# Same as above, but a single script fragment to be evaled but not shown. -finish_eval=$lt_finish_eval - -# Take the output of nm and produce a listing of raw symbols and C names. -global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe - -# Transform the output of nm in a proper C declaration -global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl - -# Transform the output of nm in a C name address pair -global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address - -# This is the shared library runtime path variable. -runpath_var=$runpath_var - -# This is the shared library path variable. -shlibpath_var=$shlibpath_var - -# Is shlibpath searched before the hard-coded library search path? -shlibpath_overrides_runpath=$shlibpath_overrides_runpath - -# How to hardcode a shared library path into an executable. -hardcode_action=$_LT_AC_TAGVAR(hardcode_action, $1) - -# Whether we should hardcode library paths into libraries. -hardcode_into_libs=$hardcode_into_libs - -# Flag to hardcode \$libdir into a binary during linking. -# This must work even if \$libdir does not exist. -hardcode_libdir_flag_spec=$lt_[]_LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1) - -# If ld is used when linking, flag to hardcode \$libdir into -# a binary during linking. This must work even if \$libdir does -# not exist. -hardcode_libdir_flag_spec_ld=$lt_[]_LT_AC_TAGVAR(hardcode_libdir_flag_spec_ld, $1) - -# Whether we need a single -rpath flag with a separated argument. -hardcode_libdir_separator=$lt_[]_LT_AC_TAGVAR(hardcode_libdir_separator, $1) - -# Set to yes if using DIR/libNAME${shared_ext} during linking hardcodes DIR into the -# resulting binary. -hardcode_direct=$_LT_AC_TAGVAR(hardcode_direct, $1) - -# Set to yes if using the -LDIR flag during linking hardcodes DIR into the -# resulting binary. -hardcode_minus_L=$_LT_AC_TAGVAR(hardcode_minus_L, $1) - -# Set to yes if using SHLIBPATH_VAR=DIR during linking hardcodes DIR into -# the resulting binary. -hardcode_shlibpath_var=$_LT_AC_TAGVAR(hardcode_shlibpath_var, $1) - -# Set to yes if building a shared library automatically hardcodes DIR into the library -# and all subsequent libraries and executables linked against it. -hardcode_automatic=$_LT_AC_TAGVAR(hardcode_automatic, $1) - -# Variables whose values should be saved in libtool wrapper scripts and -# restored at relink time. -variables_saved_for_relink="$variables_saved_for_relink" - -# Whether libtool must link a program against all its dependency libraries. -link_all_deplibs=$_LT_AC_TAGVAR(link_all_deplibs, $1) - -# Compile-time system search path for libraries -sys_lib_search_path_spec=$lt_sys_lib_search_path_spec - -# Run-time system search path for libraries -sys_lib_dlsearch_path_spec=$lt_sys_lib_dlsearch_path_spec - -# Fix the shell variable \$srcfile for the compiler. -fix_srcfile_path="$_LT_AC_TAGVAR(fix_srcfile_path, $1)" - -# Set to yes if exported symbols are required. -always_export_symbols=$_LT_AC_TAGVAR(always_export_symbols, $1) - -# The commands to list exported symbols. -export_symbols_cmds=$lt_[]_LT_AC_TAGVAR(export_symbols_cmds, $1) - -# The commands to extract the exported symbol list from a shared archive. -extract_expsyms_cmds=$lt_extract_expsyms_cmds - -# Symbols that should not be listed in the preloaded symbols. -exclude_expsyms=$lt_[]_LT_AC_TAGVAR(exclude_expsyms, $1) - -# Symbols that must always be exported. -include_expsyms=$lt_[]_LT_AC_TAGVAR(include_expsyms, $1) - -ifelse([$1],[], -[# ### END LIBTOOL CONFIG], -[# ### END LIBTOOL TAG CONFIG: $tagname]) - -__EOF__ - -ifelse([$1],[], [ - case $host_os in - aix3*) - cat <<\EOF >> "$cfgfile" - -# AIX sometimes has problems with the GCC collect2 program. For some -# reason, if we set the COLLECT_NAMES environment variable, the problems -# vanish in a puff of smoke. -if test "X${COLLECT_NAMES+set}" != Xset; then - COLLECT_NAMES= - export COLLECT_NAMES -fi -EOF - ;; - esac - - # We use sed instead of cat because bash on DJGPP gets confused if - # if finds mixed CR/LF and LF-only lines. Since sed operates in - # text mode, it properly converts lines to CR/LF. This bash problem - # is reportedly fixed, but why not run on old versions too? - sed '$q' "$ltmain" >> "$cfgfile" || (rm -f "$cfgfile"; exit 1) - - mv -f "$cfgfile" "$ofile" || \ - (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile") - chmod +x "$ofile" -]) -else - # If there is no Makefile yet, we rely on a make rule to execute - # `config.status --recheck' to rerun these tests and create the - # libtool script then. - ltmain_in=`echo $ltmain | sed -e 's/\.sh$/.in/'` - if test -f "$ltmain_in"; then - test -f Makefile && make "$ltmain" - fi -fi -])# AC_LIBTOOL_CONFIG - - -# AC_LIBTOOL_PROG_COMPILER_NO_RTTI([TAGNAME]) -# ------------------------------------------- -AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_NO_RTTI], -[AC_REQUIRE([_LT_AC_SYS_COMPILER])dnl - -_LT_AC_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)= - -if test "$GCC" = yes; then - _LT_AC_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' - - AC_LIBTOOL_COMPILER_OPTION([if $compiler supports -fno-rtti -fno-exceptions], - lt_cv_prog_compiler_rtti_exceptions, - [-fno-rtti -fno-exceptions], [], - [_LT_AC_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)="$_LT_AC_TAGVAR(lt_prog_compiler_no_builtin_flag, $1) -fno-rtti -fno-exceptions"]) -fi -])# AC_LIBTOOL_PROG_COMPILER_NO_RTTI - - -# AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE -# --------------------------------- -AC_DEFUN([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE], -[AC_REQUIRE([AC_CANONICAL_HOST]) -AC_REQUIRE([AC_PROG_NM]) -AC_REQUIRE([AC_OBJEXT]) -# Check for command to grab the raw symbol name followed by C symbol from nm. -AC_MSG_CHECKING([command to parse $NM output from $compiler object]) -AC_CACHE_VAL([lt_cv_sys_global_symbol_pipe], -[ -# These are sane defaults that work on at least a few old systems. -# [They come from Ultrix. What could be older than Ultrix?!! ;)] - -# Character class describing NM global symbol codes. -symcode='[[BCDEGRST]]' - -# Regexp to match symbols that can be accessed directly from C. -sympat='\([[_A-Za-z]][[_A-Za-z0-9]]*\)' - -# Transform an extracted symbol line into a proper C declaration -lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^. .* \(.*\)$/extern int \1;/p'" - -# Transform an extracted symbol line into symbol name and symbol address -lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([[^ ]]*\) $/ {\\\"\1\\\", (lt_ptr) 0},/p' -e 's/^$symcode \([[^ ]]*\) \([[^ ]]*\)$/ {\"\2\", (lt_ptr) \&\2},/p'" - -# Define system-specific variables. -case $host_os in -aix*) - symcode='[[BCDT]]' - ;; -cygwin* | mingw* | pw32*) - symcode='[[ABCDGISTW]]' - ;; -hpux*) # Its linker distinguishes data from code symbols - if test "$host_cpu" = ia64; then - symcode='[[ABCDEGRST]]' - fi - lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^T .* \(.*\)$/extern int \1();/p' -e 's/^$symcode* .* \(.*\)$/extern char \1;/p'" - lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([[^ ]]*\) $/ {\\\"\1\\\", (lt_ptr) 0},/p' -e 's/^$symcode* \([[^ ]]*\) \([[^ ]]*\)$/ {\"\2\", (lt_ptr) \&\2},/p'" - ;; -linux*) - if test "$host_cpu" = ia64; then - symcode='[[ABCDGIRSTW]]' - lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^T .* \(.*\)$/extern int \1();/p' -e 's/^$symcode* .* \(.*\)$/extern char \1;/p'" - lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([[^ ]]*\) $/ {\\\"\1\\\", (lt_ptr) 0},/p' -e 's/^$symcode* \([[^ ]]*\) \([[^ ]]*\)$/ {\"\2\", (lt_ptr) \&\2},/p'" - fi - ;; -irix* | nonstopux*) - symcode='[[BCDEGRST]]' - ;; -osf*) - symcode='[[BCDEGQRST]]' - ;; -solaris*) - symcode='[[BDRT]]' - ;; -sco3.2v5*) - symcode='[[DT]]' - ;; -sysv4.2uw2*) - symcode='[[DT]]' - ;; -sysv5* | sco5v6* | unixware* | OpenUNIX*) - symcode='[[ABDT]]' - ;; -sysv4) - symcode='[[DFNSTU]]' - ;; -esac - -# Handle CRLF in mingw tool chain -opt_cr= -case $build_os in -mingw*) - opt_cr=`echo 'x\{0,1\}' | tr x '\015'` # option cr in regexp - ;; -esac - -# If we're using GNU nm, then use its standard symbol codes. -case `$NM -V 2>&1` in -*GNU* | *'with BFD'*) - symcode='[[ABCDGIRSTW]]' ;; -esac - -# Try without a prefix undercore, then with it. -for ac_symprfx in "" "_"; do - - # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol. - symxfrm="\\1 $ac_symprfx\\2 \\2" - - # Write the raw and C identifiers. - lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[[ ]]\($symcode$symcode*\)[[ ]][[ ]]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'" - - # Check to see that the pipe works correctly. - pipe_works=no - - rm -f conftest* - cat > conftest.$ac_ext < $nlist) && test -s "$nlist"; then - # Try sorting and uniquifying the output. - if sort "$nlist" | uniq > "$nlist"T; then - mv -f "$nlist"T "$nlist" - else - rm -f "$nlist"T - fi - - # Make sure that we snagged all the symbols we need. - if grep ' nm_test_var$' "$nlist" >/dev/null; then - if grep ' nm_test_func$' "$nlist" >/dev/null; then - cat < conftest.$ac_ext -#ifdef __cplusplus -extern "C" { -#endif - -EOF - # Now generate the symbol file. - eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | grep -v main >> conftest.$ac_ext' - - cat <> conftest.$ac_ext -#if defined (__STDC__) && __STDC__ -# define lt_ptr_t void * -#else -# define lt_ptr_t char * -# define const -#endif - -/* The mapping between symbol names and symbols. */ -const struct { - const char *name; - lt_ptr_t address; -} -lt_preloaded_symbols[[]] = -{ -EOF - $SED "s/^$symcode$symcode* \(.*\) \(.*\)$/ {\"\2\", (lt_ptr_t) \&\2},/" < "$nlist" | grep -v main >> conftest.$ac_ext - cat <<\EOF >> conftest.$ac_ext - {0, (lt_ptr_t) 0} -}; - -#ifdef __cplusplus -} -#endif -EOF - # Now try linking the two files. - mv conftest.$ac_objext conftstm.$ac_objext - lt_save_LIBS="$LIBS" - lt_save_CFLAGS="$CFLAGS" - LIBS="conftstm.$ac_objext" - CFLAGS="$CFLAGS$_LT_AC_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)" - if AC_TRY_EVAL(ac_link) && test -s conftest${ac_exeext}; then - pipe_works=yes - fi - LIBS="$lt_save_LIBS" - CFLAGS="$lt_save_CFLAGS" - else - echo "cannot find nm_test_func in $nlist" >&AS_MESSAGE_LOG_FD - fi - else - echo "cannot find nm_test_var in $nlist" >&AS_MESSAGE_LOG_FD - fi - else - echo "cannot run $lt_cv_sys_global_symbol_pipe" >&AS_MESSAGE_LOG_FD - fi - else - echo "$progname: failed program was:" >&AS_MESSAGE_LOG_FD - cat conftest.$ac_ext >&5 - fi - rm -f conftest* conftst* - - # Do not use the global_symbol_pipe unless it works. - if test "$pipe_works" = yes; then - break - else - lt_cv_sys_global_symbol_pipe= - fi -done -]) -if test -z "$lt_cv_sys_global_symbol_pipe"; then - lt_cv_sys_global_symbol_to_cdecl= -fi -if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then - AC_MSG_RESULT(failed) -else - AC_MSG_RESULT(ok) -fi -]) # AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE - - -# AC_LIBTOOL_PROG_COMPILER_PIC([TAGNAME]) -# --------------------------------------- -AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_PIC], -[_LT_AC_TAGVAR(lt_prog_compiler_wl, $1)= -_LT_AC_TAGVAR(lt_prog_compiler_pic, $1)= -_LT_AC_TAGVAR(lt_prog_compiler_static, $1)= - -AC_MSG_CHECKING([for $compiler option to produce PIC]) - ifelse([$1],[CXX],[ - # C++ specific cases for pic, static, wl, etc. - if test "$GXX" = yes; then - _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-static' - - case $host_os in - aix*) - # All AIX code is PIC. - if test "$host_cpu" = ia64; then - # AIX 5 now supports IA64 processor - _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - fi - ;; - amigaos*) - # FIXME: we need at least 68020 code to build shared libraries, but - # adding the `-m68020' flag to GCC prevents building anything better, - # like `-m68040'. - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4' - ;; - beos* | cygwin* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) - # PIC is the default for these OSes. - ;; - mingw* | os2* | pw32*) - # This hack is so that the source file can tell whether it is being - # built for inclusion in a dll (and should export symbols for example). - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT' - ;; - darwin* | rhapsody*) - # PIC is the default on this platform - # Common symbols not allowed in MH_DYLIB files - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' - ;; - *djgpp*) - # DJGPP does not support shared libraries at all - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)= - ;; - interix3*) - # Interix 3.x gcc -fpic/-fPIC options generate broken code. - # Instead, we relocate shared libraries at runtime. - ;; - sysv4*MP*) - if test -d /usr/nec; then - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic - fi - ;; - hpux*) - # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but - # not for PA HP-UX. - case $host_cpu in - hppa*64*|ia64*) - ;; - *) - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' - ;; - esac - ;; - *) - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' - ;; - esac - else - case $host_os in - aix4* | aix5*) - # All AIX code is PIC. - if test "$host_cpu" = ia64; then - # AIX 5 now supports IA64 processor - _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - else - _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp' - fi - ;; - chorus*) - case $cc_basename in - cxch68*) - # Green Hills C++ Compiler - # _LT_AC_TAGVAR(lt_prog_compiler_static, $1)="--no_auto_instantiation -u __main -u __premain -u _abort -r $COOL_DIR/lib/libOrb.a $MVME_DIR/lib/CC/libC.a $MVME_DIR/lib/classix/libcx.s.a" - ;; - esac - ;; - darwin*) - # PIC is the default on this platform - # Common symbols not allowed in MH_DYLIB files - case $cc_basename in - xlc*) - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-qnocommon' - _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - ;; - esac - ;; - dgux*) - case $cc_basename in - ec++*) - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' - ;; - ghcx*) - # Green Hills C++ Compiler - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-pic' - ;; - *) - ;; - esac - ;; - freebsd* | kfreebsd*-gnu | dragonfly*) - # FreeBSD uses GNU C++ - ;; - hpux9* | hpux10* | hpux11*) - case $cc_basename in - CC*) - _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive' - if test "$host_cpu" != ia64; then - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='+Z' - fi - ;; - aCC*) - _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive' - case $host_cpu in - hppa*64*|ia64*) - # +Z the default - ;; - *) - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='+Z' - ;; - esac - ;; - *) - ;; - esac - ;; - interix*) - # This is c89, which is MS Visual C++ (no shared libs) - # Anyone wants to do a port? - ;; - irix5* | irix6* | nonstopux*) - case $cc_basename in - CC*) - _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' - # CC pic flag -KPIC is the default. - ;; - *) - ;; - esac - ;; - linux*) - case $cc_basename in - KCC*) - # KAI C++ Compiler - _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,' - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' - ;; - icpc* | ecpc*) - # Intel C++ - _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' - _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-static' - ;; - pgCC*) - # Portland Group C++ compiler. - _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' - _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - ;; - cxx*) - # Compaq C++ - # Make sure the PIC flag is empty. It appears that all Alpha - # Linux and Compaq Tru64 Unix objects are PIC. - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)= - _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' - ;; - *) - ;; - esac - ;; - lynxos*) - ;; - m88k*) - ;; - mvs*) - case $cc_basename in - cxx*) - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-W c,exportall' - ;; - *) - ;; - esac - ;; - netbsd*) - ;; - osf3* | osf4* | osf5*) - case $cc_basename in - KCC*) - _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,' - ;; - RCC*) - # Rational C++ 2.4.1 - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-pic' - ;; - cxx*) - # Digital/Compaq C++ - _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - # Make sure the PIC flag is empty. It appears that all Alpha - # Linux and Compaq Tru64 Unix objects are PIC. - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)= - _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' - ;; - *) - ;; - esac - ;; - psos*) - ;; - solaris*) - case $cc_basename in - CC*) - # Sun C++ 4.2, 5.x and Centerline C++ - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' - _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' - ;; - gcx*) - # Green Hills C++ Compiler - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' - ;; - *) - ;; - esac - ;; - sunos4*) - case $cc_basename in - CC*) - # Sun C++ 4.x - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-pic' - _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - ;; - lcc*) - # Lucid - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-pic' - ;; - *) - ;; - esac - ;; - tandem*) - case $cc_basename in - NCC*) - # NonStop-UX NCC 3.20 - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' - ;; - *) - ;; - esac - ;; - sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) - case $cc_basename in - CC*) - _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' - _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - ;; - esac - ;; - vxworks*) - ;; - *) - _LT_AC_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no - ;; - esac - fi -], -[ - if test "$GCC" = yes; then - _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-static' - - case $host_os in - aix*) - # All AIX code is PIC. - if test "$host_cpu" = ia64; then - # AIX 5 now supports IA64 processor - _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - fi - ;; - - amigaos*) - # FIXME: we need at least 68020 code to build shared libraries, but - # adding the `-m68020' flag to GCC prevents building anything better, - # like `-m68040'. - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4' - ;; - - beos* | cygwin* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) - # PIC is the default for these OSes. - ;; - - mingw* | pw32* | os2*) - # This hack is so that the source file can tell whether it is being - # built for inclusion in a dll (and should export symbols for example). - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT' - ;; - - darwin* | rhapsody*) - # PIC is the default on this platform - # Common symbols not allowed in MH_DYLIB files - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' - ;; - - interix3*) - # Interix 3.x gcc -fpic/-fPIC options generate broken code. - # Instead, we relocate shared libraries at runtime. - ;; - - msdosdjgpp*) - # Just because we use GCC doesn't mean we suddenly get shared libraries - # on systems that don't support them. - _LT_AC_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no - enable_shared=no - ;; - - sysv4*MP*) - if test -d /usr/nec; then - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic - fi - ;; - - hpux*) - # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but - # not for PA HP-UX. - case $host_cpu in - hppa*64*|ia64*) - # +Z the default - ;; - *) - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' - ;; - esac - ;; - - *) - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' - ;; - esac - else - # PORTME Check for flag to pass linker flags through the system compiler. - case $host_os in - aix*) - _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - if test "$host_cpu" = ia64; then - # AIX 5 now supports IA64 processor - _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - else - _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp' - fi - ;; - darwin*) - # PIC is the default on this platform - # Common symbols not allowed in MH_DYLIB files - case $cc_basename in - xlc*) - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-qnocommon' - _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - ;; - esac - ;; - - mingw* | pw32* | os2*) - # This hack is so that the source file can tell whether it is being - # built for inclusion in a dll (and should export symbols for example). - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT' - ;; - - hpux9* | hpux10* | hpux11*) - _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but - # not for PA HP-UX. - case $host_cpu in - hppa*64*|ia64*) - # +Z the default - ;; - *) - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='+Z' - ;; - esac - # Is there a better lt_prog_compiler_static that works with the bundled CC? - _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive' - ;; - - irix5* | irix6* | nonstopux*) - _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - # PIC (with -KPIC) is the default. - _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' - ;; - - newsos6) - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' - _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - ;; - - linux*) - case $cc_basename in - icc* | ecc*) - _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' - _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-static' - ;; - pgcc* | pgf77* | pgf90* | pgf95*) - # Portland Group compilers (*not* the Pentium gcc compiler, - # which looks to be a dead project) - _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' - _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - ;; - ccc*) - _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - # All Alpha code is PIC. - _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' - ;; - esac - ;; - - osf3* | osf4* | osf5*) - _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - # All OSF/1 code is PIC. - _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' - ;; - - solaris*) - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' - _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - case $cc_basename in - f77* | f90* | f95*) - _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ';; - *) - _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,';; - esac - ;; - - sunos4*) - _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' - _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - ;; - - sysv4 | sysv4.2uw2* | sysv4.3*) - _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' - _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - ;; - - sysv4*MP*) - if test -d /usr/nec ;then - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-Kconform_pic' - _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - fi - ;; - - sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) - _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' - _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - ;; - - unicos*) - _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - _LT_AC_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no - ;; - - uts4*) - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-pic' - _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - ;; - - *) - _LT_AC_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no - ;; - esac - fi -]) -AC_MSG_RESULT([$_LT_AC_TAGVAR(lt_prog_compiler_pic, $1)]) - -# -# Check to make sure the PIC flag actually works. -# -if test -n "$_LT_AC_TAGVAR(lt_prog_compiler_pic, $1)"; then - AC_LIBTOOL_COMPILER_OPTION([if $compiler PIC flag $_LT_AC_TAGVAR(lt_prog_compiler_pic, $1) works], - _LT_AC_TAGVAR(lt_prog_compiler_pic_works, $1), - [$_LT_AC_TAGVAR(lt_prog_compiler_pic, $1)ifelse([$1],[],[ -DPIC],[ifelse([$1],[CXX],[ -DPIC],[])])], [], - [case $_LT_AC_TAGVAR(lt_prog_compiler_pic, $1) in - "" | " "*) ;; - *) _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)=" $_LT_AC_TAGVAR(lt_prog_compiler_pic, $1)" ;; - esac], - [_LT_AC_TAGVAR(lt_prog_compiler_pic, $1)= - _LT_AC_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no]) -fi -case $host_os in - # For platforms which do not support PIC, -DPIC is meaningless: - *djgpp*) - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)= - ;; - *) - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)="$_LT_AC_TAGVAR(lt_prog_compiler_pic, $1)ifelse([$1],[],[ -DPIC],[ifelse([$1],[CXX],[ -DPIC],[])])" - ;; -esac - -# -# Check to make sure the static flag actually works. -# -wl=$_LT_AC_TAGVAR(lt_prog_compiler_wl, $1) eval lt_tmp_static_flag=\"$_LT_AC_TAGVAR(lt_prog_compiler_static, $1)\" -AC_LIBTOOL_LINKER_OPTION([if $compiler static flag $lt_tmp_static_flag works], - _LT_AC_TAGVAR(lt_prog_compiler_static_works, $1), - $lt_tmp_static_flag, - [], - [_LT_AC_TAGVAR(lt_prog_compiler_static, $1)=]) -]) - - -# AC_LIBTOOL_PROG_LD_SHLIBS([TAGNAME]) -# ------------------------------------ -# See if the linker supports building shared libraries. -AC_DEFUN([AC_LIBTOOL_PROG_LD_SHLIBS], -[AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries]) -ifelse([$1],[CXX],[ - _LT_AC_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' - case $host_os in - aix4* | aix5*) - # If we're using GNU nm, then we don't want the "-C" option. - # -C means demangle to AIX nm, but means don't demangle with GNU nm - if $NM -V 2>&1 | grep 'GNU' > /dev/null; then - _LT_AC_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\[$]2 == "T") || (\[$]2 == "D") || (\[$]2 == "B")) && ([substr](\[$]3,1,1) != ".")) { print \[$]3 } }'\'' | sort -u > $export_symbols' - else - _LT_AC_TAGVAR(export_symbols_cmds, $1)='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\[$]2 == "T") || (\[$]2 == "D") || (\[$]2 == "B")) && ([substr](\[$]3,1,1) != ".")) { print \[$]3 } }'\'' | sort -u > $export_symbols' - fi - ;; - pw32*) - _LT_AC_TAGVAR(export_symbols_cmds, $1)="$ltdll_cmds" - ;; - cygwin* | mingw*) - _LT_AC_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]] /s/.* \([[^ ]]*\)/\1 DATA/;/^.* __nm__/s/^.* __nm__\([[^ ]]*\) [[^ ]]*/\1 DATA/;/^I /d;/^[[AITW]] /s/.* //'\'' | sort | uniq > $export_symbols' - ;; - *) - _LT_AC_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' - ;; - esac -],[ - runpath_var= - _LT_AC_TAGVAR(allow_undefined_flag, $1)= - _LT_AC_TAGVAR(enable_shared_with_static_runtimes, $1)=no - _LT_AC_TAGVAR(archive_cmds, $1)= - _LT_AC_TAGVAR(archive_expsym_cmds, $1)= - _LT_AC_TAGVAR(old_archive_From_new_cmds, $1)= - _LT_AC_TAGVAR(old_archive_from_expsyms_cmds, $1)= - _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)= - _LT_AC_TAGVAR(whole_archive_flag_spec, $1)= - _LT_AC_TAGVAR(thread_safe_flag_spec, $1)= - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)= - _LT_AC_TAGVAR(hardcode_libdir_flag_spec_ld, $1)= - _LT_AC_TAGVAR(hardcode_libdir_separator, $1)= - _LT_AC_TAGVAR(hardcode_direct, $1)=no - _LT_AC_TAGVAR(hardcode_minus_L, $1)=no - _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=unsupported - _LT_AC_TAGVAR(link_all_deplibs, $1)=unknown - _LT_AC_TAGVAR(hardcode_automatic, $1)=no - _LT_AC_TAGVAR(module_cmds, $1)= - _LT_AC_TAGVAR(module_expsym_cmds, $1)= - _LT_AC_TAGVAR(always_export_symbols, $1)=no - _LT_AC_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' - # include_expsyms should be a list of space-separated symbols to be *always* - # included in the symbol list - _LT_AC_TAGVAR(include_expsyms, $1)= - # exclude_expsyms can be an extended regexp of symbols to exclude - # it will be wrapped by ` (' and `)$', so one must not match beginning or - # end of line. Example: `a|bc|.*d.*' will exclude the symbols `a' and `bc', - # as well as any symbol that contains `d'. - _LT_AC_TAGVAR(exclude_expsyms, $1)="_GLOBAL_OFFSET_TABLE_" - # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out - # platforms (ab)use it in PIC code, but their linkers get confused if - # the symbol is explicitly referenced. Since portable code cannot - # rely on this symbol name, it's probably fine to never include it in - # preloaded symbol tables. - extract_expsyms_cmds= - # Just being paranoid about ensuring that cc_basename is set. - _LT_CC_BASENAME([$compiler]) - case $host_os in - cygwin* | mingw* | pw32*) - # FIXME: the MSVC++ port hasn't been tested in a loooong time - # When not using gcc, we currently assume that we are using - # Microsoft Visual C++. - if test "$GCC" != yes; then - with_gnu_ld=no - fi - ;; - interix*) - # we just hope/assume this is gcc and not c89 (= MSVC++) - with_gnu_ld=yes - ;; - openbsd*) - with_gnu_ld=no - ;; - esac - - _LT_AC_TAGVAR(ld_shlibs, $1)=yes - if test "$with_gnu_ld" = yes; then - # If archive_cmds runs LD, not CC, wlarc should be empty - wlarc='${wl}' - - # Set some defaults for GNU ld with shared library support. These - # are reset later if shared libraries are not supported. Putting them - # here allows them to be overridden if necessary. - runpath_var=LD_RUN_PATH - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}--rpath ${wl}$libdir' - _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' - # ancient GNU ld didn't support --whole-archive et. al. - if $LD --help 2>&1 | grep 'no-whole-archive' > /dev/null; then - _LT_AC_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' - else - _LT_AC_TAGVAR(whole_archive_flag_spec, $1)= - fi - supports_anon_versioning=no - case `$LD -v 2>/dev/null` in - *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.10.*) ;; # catch versions < 2.11 - *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ... - *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ... - *\ 2.11.*) ;; # other 2.11 versions - *) supports_anon_versioning=yes ;; - esac - - # See if GNU ld supports shared libraries. - case $host_os in - aix3* | aix4* | aix5*) - # On AIX/PPC, the GNU linker is very broken - if test "$host_cpu" != ia64; then - _LT_AC_TAGVAR(ld_shlibs, $1)=no - cat <&2 - -*** Warning: the GNU linker, at least up to release 2.9.1, is reported -*** to be unable to reliably create shared libraries on AIX. -*** Therefore, libtool is disabling shared libraries support. If you -*** really care for shared libraries, you may want to modify your PATH -*** so that a non-GNU linker is found, and then restart. - -EOF - fi - ;; - - amigaos*) - _LT_AC_TAGVAR(archive_cmds, $1)='$rm $output_objdir/a2ixlibrary.data~$echo "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$echo "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$echo "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$echo "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' - _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes - - # Samuel A. Falvo II reports - # that the semantics of dynamic libraries on AmigaOS, at least up - # to version 4, is to share data among multiple programs linked - # with the same dynamic library. Since this doesn't match the - # behavior of shared libraries on other platforms, we can't use - # them. - _LT_AC_TAGVAR(ld_shlibs, $1)=no - ;; - - beos*) - if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then - _LT_AC_TAGVAR(allow_undefined_flag, $1)=unsupported - # Joseph Beckenbach says some releases of gcc - # support --undefined. This deserves some investigation. FIXME - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - else - _LT_AC_TAGVAR(ld_shlibs, $1)=no - fi - ;; - - cygwin* | mingw* | pw32*) - # _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless, - # as there is no search path for DLLs. - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' - _LT_AC_TAGVAR(allow_undefined_flag, $1)=unsupported - _LT_AC_TAGVAR(always_export_symbols, $1)=no - _LT_AC_TAGVAR(enable_shared_with_static_runtimes, $1)=yes - _LT_AC_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]] /s/.* \([[^ ]]*\)/\1 DATA/'\'' | $SED -e '\''/^[[AITW]] /s/.* //'\'' | sort | uniq > $export_symbols' - - if $LD --help 2>&1 | grep 'auto-import' > /dev/null; then - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' - # If the export-symbols file already is a .def file (1st line - # is EXPORTS), use it as is; otherwise, prepend... - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then - cp $export_symbols $output_objdir/$soname.def; - else - echo EXPORTS > $output_objdir/$soname.def; - cat $export_symbols >> $output_objdir/$soname.def; - fi~ - $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' - else - _LT_AC_TAGVAR(ld_shlibs, $1)=no - fi - ;; - - interix3*) - _LT_AC_TAGVAR(hardcode_direct, $1)=no - _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' - _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' - # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. - # Instead, shared libraries are loaded at an image base (0x10000000 by - # default) and relocated if they conflict, which is a slow very memory - # consuming and fragmenting process. To avoid this, we pick a random, - # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link - # time. Moving up from 0x10000000 also allows more sbrk(2) space. - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' - ;; - - linux*) - if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then - tmp_addflag= - case $cc_basename,$host_cpu in - pgcc*) # Portland Group C compiler - _LT_AC_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $echo \"$new_convenience\"` ${wl}--no-whole-archive' - tmp_addflag=' $pic_flag' - ;; - pgf77* | pgf90* | pgf95*) # Portland Group f77 and f90 compilers - _LT_AC_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $echo \"$new_convenience\"` ${wl}--no-whole-archive' - tmp_addflag=' $pic_flag -Mnomain' ;; - ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64 - tmp_addflag=' -i_dynamic' ;; - efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64 - tmp_addflag=' -i_dynamic -nofor_main' ;; - ifc* | ifort*) # Intel Fortran compiler - tmp_addflag=' -nofor_main' ;; - esac - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared'"$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - - if test $supports_anon_versioning = yes; then - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$echo "{ global:" > $output_objdir/$libname.ver~ - cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ - $echo "local: *; };" >> $output_objdir/$libname.ver~ - $CC -shared'"$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib' - fi - else - _LT_AC_TAGVAR(ld_shlibs, $1)=no - fi - ;; - - netbsd*) - if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then - _LT_AC_TAGVAR(archive_cmds, $1)='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib' - wlarc= - else - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' - fi - ;; - - solaris*) - if $LD -v 2>&1 | grep 'BFD 2\.8' > /dev/null; then - _LT_AC_TAGVAR(ld_shlibs, $1)=no - cat <&2 - -*** Warning: The releases 2.8.* of the GNU linker cannot reliably -*** create shared libraries on Solaris systems. Therefore, libtool -*** is disabling shared libraries support. We urge you to upgrade GNU -*** binutils to release 2.9.1 or newer. Another option is to modify -*** your PATH or compiler configuration so that the native linker is -*** used, and then restart. - -EOF - elif $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' - else - _LT_AC_TAGVAR(ld_shlibs, $1)=no - fi - ;; - - sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) - case `$LD -v 2>&1` in - *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.1[[0-5]].*) - _LT_AC_TAGVAR(ld_shlibs, $1)=no - cat <<_LT_EOF 1>&2 - -*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 can not -*** reliably create shared libraries on SCO systems. Therefore, libtool -*** is disabling shared libraries support. We urge you to upgrade GNU -*** binutils to release 2.16.91.0.3 or newer. Another option is to modify -*** your PATH or compiler configuration so that the native linker is -*** used, and then restart. - -_LT_EOF - ;; - *) - if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='`test -z "$SCOABSPATH" && echo ${wl}-rpath,$libdir`' - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname,\${SCOABSPATH:+${install_libdir}/}$soname -o $lib' - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname,\${SCOABSPATH:+${install_libdir}/}$soname,-retain-symbols-file,$export_symbols -o $lib' - else - _LT_AC_TAGVAR(ld_shlibs, $1)=no - fi - ;; - esac - ;; - - sunos4*) - _LT_AC_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags' - wlarc= - _LT_AC_TAGVAR(hardcode_direct, $1)=yes - _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no - ;; - - *) - if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' - else - _LT_AC_TAGVAR(ld_shlibs, $1)=no - fi - ;; - esac - - if test "$_LT_AC_TAGVAR(ld_shlibs, $1)" = no; then - runpath_var= - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)= - _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)= - _LT_AC_TAGVAR(whole_archive_flag_spec, $1)= - fi - else - # PORTME fill in a description of your system's linker (not GNU ld) - case $host_os in - aix3*) - _LT_AC_TAGVAR(allow_undefined_flag, $1)=unsupported - _LT_AC_TAGVAR(always_export_symbols, $1)=yes - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname' - # Note: this linker hardcodes the directories in LIBPATH if there - # are no directories specified by -L. - _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes - if test "$GCC" = yes && test -z "$lt_prog_compiler_static"; then - # Neither direct hardcoding nor static linking is supported with a - # broken collect2. - _LT_AC_TAGVAR(hardcode_direct, $1)=unsupported - fi - ;; - - aix4* | aix5*) - if test "$host_cpu" = ia64; then - # On IA64, the linker does run time linking by default, so we don't - # have to do anything special. - aix_use_runtimelinking=no - exp_sym_flag='-Bexport' - no_entry_flag="" - else - # If we're using GNU nm, then we don't want the "-C" option. - # -C means demangle to AIX nm, but means don't demangle with GNU nm - if $NM -V 2>&1 | grep 'GNU' > /dev/null; then - _LT_AC_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\[$]2 == "T") || (\[$]2 == "D") || (\[$]2 == "B")) && ([substr](\[$]3,1,1) != ".")) { print \[$]3 } }'\'' | sort -u > $export_symbols' - else - _LT_AC_TAGVAR(export_symbols_cmds, $1)='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\[$]2 == "T") || (\[$]2 == "D") || (\[$]2 == "B")) && ([substr](\[$]3,1,1) != ".")) { print \[$]3 } }'\'' | sort -u > $export_symbols' - fi - aix_use_runtimelinking=no - - # Test if we are trying to use run time linking or normal - # AIX style linking. If -brtl is somewhere in LDFLAGS, we - # need to do runtime linking. - case $host_os in aix4.[[23]]|aix4.[[23]].*|aix5*) - for ld_flag in $LDFLAGS; do - if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then - aix_use_runtimelinking=yes - break - fi - done - ;; - esac - - exp_sym_flag='-bexport' - no_entry_flag='-bnoentry' - fi - - # When large executables or shared objects are built, AIX ld can - # have problems creating the table of contents. If linking a library - # or program results in "error TOC overflow" add -mminimal-toc to - # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not - # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. - - _LT_AC_TAGVAR(archive_cmds, $1)='' - _LT_AC_TAGVAR(hardcode_direct, $1)=yes - _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=':' - _LT_AC_TAGVAR(link_all_deplibs, $1)=yes - - if test "$GCC" = yes; then - case $host_os in aix4.[[012]]|aix4.[[012]].*) - # We only want to do this on AIX 4.2 and lower, the check - # below for broken collect2 doesn't work under 4.3+ - collect2name=`${CC} -print-prog-name=collect2` - if test -f "$collect2name" && \ - strings "$collect2name" | grep resolve_lib_name >/dev/null - then - # We have reworked collect2 - _LT_AC_TAGVAR(hardcode_direct, $1)=yes - else - # We have old collect2 - _LT_AC_TAGVAR(hardcode_direct, $1)=unsupported - # It fails to find uninstalled libraries when the uninstalled - # path is not listed in the libpath. Setting hardcode_minus_L - # to unsupported forces relinking - _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' - _LT_AC_TAGVAR(hardcode_libdir_separator, $1)= - fi - ;; - esac - shared_flag='-shared' - if test "$aix_use_runtimelinking" = yes; then - shared_flag="$shared_flag "'${wl}-G' - fi - else - # not using gcc - if test "$host_cpu" = ia64; then - # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release - # chokes on -Wl,-G. The following line is correct: - shared_flag='-G' - else - if test "$aix_use_runtimelinking" = yes; then - shared_flag='${wl}-G' - else - shared_flag='${wl}-bM:SRE' - fi - fi - fi - - # It seems that -bexpall does not export symbols beginning with - # underscore (_), so it is better to generate a list of symbols to export. - _LT_AC_TAGVAR(always_export_symbols, $1)=yes - if test "$aix_use_runtimelinking" = yes; then - # Warning - without using the other runtime loading flags (-brtl), - # -berok will link without error, but may produce a broken library. - _LT_AC_TAGVAR(allow_undefined_flag, $1)='-berok' - # Determine the default libpath from the value encoded in an empty executable. - _LT_AC_SYS_LIBPATH_AIX - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" - _LT_AC_TAGVAR(archive_expsym_cmds, $1)="\$CC"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then echo "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag" - else - if test "$host_cpu" = ia64; then - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $libdir:/usr/lib:/lib' - _LT_AC_TAGVAR(allow_undefined_flag, $1)="-z nodefs" - _LT_AC_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols" - else - # Determine the default libpath from the value encoded in an empty executable. - _LT_AC_SYS_LIBPATH_AIX - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" - # Warning - without using the other run time loading flags, - # -berok will link without error, but may produce a broken library. - _LT_AC_TAGVAR(no_undefined_flag, $1)=' ${wl}-bernotok' - _LT_AC_TAGVAR(allow_undefined_flag, $1)=' ${wl}-berok' - # Exported symbols can be pulled into shared objects from archives - _LT_AC_TAGVAR(whole_archive_flag_spec, $1)='$convenience' - _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=yes - # This is similar to how AIX traditionally builds its shared libraries. - _LT_AC_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' - fi - fi - ;; - - amigaos*) - _LT_AC_TAGVAR(archive_cmds, $1)='$rm $output_objdir/a2ixlibrary.data~$echo "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$echo "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$echo "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$echo "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' - _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes - # see comment about different semantics on the GNU ld section - _LT_AC_TAGVAR(ld_shlibs, $1)=no - ;; - - bsdi[[45]]*) - _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)=-rdynamic - ;; - - cygwin* | mingw* | pw32*) - # When not using gcc, we currently assume that we are using - # Microsoft Visual C++. - # hardcode_libdir_flag_spec is actually meaningless, as there is - # no search path for DLLs. - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' - _LT_AC_TAGVAR(allow_undefined_flag, $1)=unsupported - # Tell ltmain to make .lib files, not .a files. - libext=lib - # Tell ltmain to make .dll files, not .so files. - shrext_cmds=".dll" - # FIXME: Setting linknames here is a bad hack. - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -o $lib $libobjs $compiler_flags `echo "$deplibs" | $SED -e '\''s/ -lc$//'\''` -link -dll~linknames=' - # The linker will automatically build a .lib file if we build a DLL. - _LT_AC_TAGVAR(old_archive_From_new_cmds, $1)='true' - # FIXME: Should let the user specify the lib program. - _LT_AC_TAGVAR(old_archive_cmds, $1)='lib /OUT:$oldlib$oldobjs$old_deplibs' - _LT_AC_TAGVAR(fix_srcfile_path, $1)='`cygpath -w "$srcfile"`' - _LT_AC_TAGVAR(enable_shared_with_static_runtimes, $1)=yes - ;; - - darwin* | rhapsody*) - case $host_os in - rhapsody* | darwin1.[[012]]) - _LT_AC_TAGVAR(allow_undefined_flag, $1)='${wl}-undefined ${wl}suppress' - ;; - *) # Darwin 1.3 on - if test -z ${MACOSX_DEPLOYMENT_TARGET} ; then - _LT_AC_TAGVAR(allow_undefined_flag, $1)='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' - else - case ${MACOSX_DEPLOYMENT_TARGET} in - 10.[[012]]) - _LT_AC_TAGVAR(allow_undefined_flag, $1)='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' - ;; - 10.*) - _LT_AC_TAGVAR(allow_undefined_flag, $1)='${wl}-undefined ${wl}dynamic_lookup' - ;; - esac - fi - ;; - esac - _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no - _LT_AC_TAGVAR(hardcode_direct, $1)=no - _LT_AC_TAGVAR(hardcode_automatic, $1)=yes - _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=unsupported - _LT_AC_TAGVAR(whole_archive_flag_spec, $1)='' - _LT_AC_TAGVAR(link_all_deplibs, $1)=yes - if test "$GCC" = yes ; then - output_verbose_link_cmd='echo' - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -dynamiclib $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags -install_name $rpath/$soname $verstring' - _LT_AC_TAGVAR(module_cmds, $1)='$CC $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags' - # Don't fix this by using the ld -exported_symbols_list flag, it doesn't exist in older darwin lds - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC -dynamiclib $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags -install_name $rpath/$soname $verstring~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' - _LT_AC_TAGVAR(module_expsym_cmds, $1)='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' - else - case $cc_basename in - xlc*) - output_verbose_link_cmd='echo' - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -qmkshrobj $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-install_name ${wl}`echo $rpath/$soname` $verstring' - _LT_AC_TAGVAR(module_cmds, $1)='$CC $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags' - # Don't fix this by using the ld -exported_symbols_list flag, it doesn't exist in older darwin lds - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC -qmkshrobj $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-install_name ${wl}$rpath/$soname $verstring~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' - _LT_AC_TAGVAR(module_expsym_cmds, $1)='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' - ;; - *) - _LT_AC_TAGVAR(ld_shlibs, $1)=no - ;; - esac - fi - ;; - - dgux*) - _LT_AC_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' - _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no - ;; - - freebsd1*) - _LT_AC_TAGVAR(ld_shlibs, $1)=no - ;; - - # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor - # support. Future versions do this automatically, but an explicit c++rt0.o - # does not break anything, and helps significantly (at the cost of a little - # extra space). - freebsd2.2*) - _LT_AC_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o' - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' - _LT_AC_TAGVAR(hardcode_direct, $1)=yes - _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no - ;; - - # Unfortunately, older versions of FreeBSD 2 do not have this feature. - freebsd2*) - _LT_AC_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' - _LT_AC_TAGVAR(hardcode_direct, $1)=yes - _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes - _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no - ;; - - # FreeBSD 3 and greater uses gcc -shared to do shared libraries. - freebsd* | kfreebsd*-gnu | dragonfly*) - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -o $lib $libobjs $deplibs $compiler_flags' - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' - _LT_AC_TAGVAR(hardcode_direct, $1)=yes - _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no - ;; - - hpux9*) - if test "$GCC" = yes; then - _LT_AC_TAGVAR(archive_cmds, $1)='$rm $output_objdir/$soname~$CC -shared -fPIC ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' - else - _LT_AC_TAGVAR(archive_cmds, $1)='$rm $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' - fi - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' - _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: - _LT_AC_TAGVAR(hardcode_direct, $1)=yes - - # hardcode_minus_L: Not really in the search PATH, - # but as the default location of the library. - _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes - _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' - ;; - - hpux10*) - if test "$GCC" = yes -a "$with_gnu_ld" = no; then - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' - else - _LT_AC_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' - fi - if test "$with_gnu_ld" = no; then - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' - _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: - - _LT_AC_TAGVAR(hardcode_direct, $1)=yes - _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' - - # hardcode_minus_L: Not really in the search PATH, - # but as the default location of the library. - _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes - fi - ;; - - hpux11*) - if test "$GCC" = yes -a "$with_gnu_ld" = no; then - case $host_cpu in - hppa*64*) - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' - ;; - ia64*) - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' - ;; - *) - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' - ;; - esac - else - case $host_cpu in - hppa*64*) - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' - ;; - ia64*) - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' - ;; - *) - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' - ;; - esac - fi - if test "$with_gnu_ld" = no; then - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' - _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: - - case $host_cpu in - hppa*64*|ia64*) - _LT_AC_TAGVAR(hardcode_libdir_flag_spec_ld, $1)='+b $libdir' - _LT_AC_TAGVAR(hardcode_direct, $1)=no - _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no - ;; - *) - _LT_AC_TAGVAR(hardcode_direct, $1)=yes - _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' - - # hardcode_minus_L: Not really in the search PATH, - # but as the default location of the library. - _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes - ;; - esac - fi - ;; - - irix5* | irix6* | nonstopux*) - if test "$GCC" = yes; then - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' - else - _LT_AC_TAGVAR(archive_cmds, $1)='$LD -shared $libobjs $deplibs $linker_flags -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib' - _LT_AC_TAGVAR(hardcode_libdir_flag_spec_ld, $1)='-rpath $libdir' - fi - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' - _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: - _LT_AC_TAGVAR(link_all_deplibs, $1)=yes - ;; - - netbsd*) - if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then - _LT_AC_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out - else - _LT_AC_TAGVAR(archive_cmds, $1)='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF - fi - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' - _LT_AC_TAGVAR(hardcode_direct, $1)=yes - _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no - ;; - - newsos6) - _LT_AC_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - _LT_AC_TAGVAR(hardcode_direct, $1)=yes - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' - _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: - _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no - ;; - - openbsd*) - _LT_AC_TAGVAR(hardcode_direct, $1)=yes - _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no - if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-retain-symbols-file,$export_symbols' - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' - _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' - else - case $host_os in - openbsd[[01]].* | openbsd2.[[0-7]] | openbsd2.[[0-7]].*) - _LT_AC_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' - ;; - *) - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' - ;; - esac - fi - ;; - - os2*) - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' - _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes - _LT_AC_TAGVAR(allow_undefined_flag, $1)=unsupported - _LT_AC_TAGVAR(archive_cmds, $1)='$echo "LIBRARY $libname INITINSTANCE" > $output_objdir/$libname.def~$echo "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~$echo DATA >> $output_objdir/$libname.def~$echo " SINGLE NONSHARED" >> $output_objdir/$libname.def~$echo EXPORTS >> $output_objdir/$libname.def~emxexp $libobjs >> $output_objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $compiler_flags $output_objdir/$libname.def' - _LT_AC_TAGVAR(old_archive_From_new_cmds, $1)='emximp -o $output_objdir/$libname.a $output_objdir/$libname.def' - ;; - - osf3*) - if test "$GCC" = yes; then - _LT_AC_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' - else - _LT_AC_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' - _LT_AC_TAGVAR(archive_cmds, $1)='$LD -shared${allow_undefined_flag} $libobjs $deplibs $linker_flags -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib' - fi - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' - _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: - ;; - - osf4* | osf5*) # as osf3* with the addition of -msym flag - if test "$GCC" = yes; then - _LT_AC_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' - else - _LT_AC_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' - _LT_AC_TAGVAR(archive_cmds, $1)='$LD -shared${allow_undefined_flag} $libobjs $deplibs $linker_flags -msym -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib' - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; echo "-hidden">> $lib.exp~ - $LD -shared${allow_undefined_flag} -input $lib.exp $linker_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib~$rm $lib.exp' - - # Both c and cxx compiler support -rpath directly - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' - fi - _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: - ;; - - solaris*) - _LT_AC_TAGVAR(no_undefined_flag, $1)=' -z text' - if test "$GCC" = yes; then - wlarc='${wl}' - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~ - $CC -shared ${wl}-M ${wl}$lib.exp ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags~$rm $lib.exp' - else - wlarc='' - _LT_AC_TAGVAR(archive_cmds, $1)='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags' - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~ - $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$rm $lib.exp' - fi - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' - _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no - case $host_os in - solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; - *) - # The compiler driver will combine linker options so we - # cannot just pass the convience library names through - # without $wl, iff we do not link with $LD. - # Luckily, gcc supports the same syntax we need for Sun Studio. - # Supported since Solaris 2.6 (maybe 2.5.1?) - case $wlarc in - '') - _LT_AC_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract' ;; - *) - _LT_AC_TAGVAR(whole_archive_flag_spec, $1)='${wl}-z ${wl}allextract`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $echo \"$new_convenience\"` ${wl}-z ${wl}defaultextract' ;; - esac ;; - esac - _LT_AC_TAGVAR(link_all_deplibs, $1)=yes - ;; - - sunos4*) - if test "x$host_vendor" = xsequent; then - # Use $CC to link under sequent, because it throws in some extra .o - # files that make .init and .fini sections work. - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h $soname -o $lib $libobjs $deplibs $compiler_flags' - else - _LT_AC_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags' - fi - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' - _LT_AC_TAGVAR(hardcode_direct, $1)=yes - _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes - _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no - ;; - - sysv4) - case $host_vendor in - sni) - _LT_AC_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - _LT_AC_TAGVAR(hardcode_direct, $1)=yes # is this really true??? - ;; - siemens) - ## LD is ld it makes a PLAMLIB - ## CC just makes a GrossModule. - _LT_AC_TAGVAR(archive_cmds, $1)='$LD -G -o $lib $libobjs $deplibs $linker_flags' - _LT_AC_TAGVAR(reload_cmds, $1)='$CC -r -o $output$reload_objs' - _LT_AC_TAGVAR(hardcode_direct, $1)=no - ;; - motorola) - _LT_AC_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - _LT_AC_TAGVAR(hardcode_direct, $1)=no #Motorola manual says yes, but my tests say they lie - ;; - esac - runpath_var='LD_RUN_PATH' - _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no - ;; - - sysv4.3*) - _LT_AC_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no - _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='-Bexport' - ;; - - sysv4*MP*) - if test -d /usr/nec; then - _LT_AC_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no - runpath_var=LD_RUN_PATH - hardcode_runpath_var=yes - _LT_AC_TAGVAR(ld_shlibs, $1)=yes - fi - ;; - - sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7*) - _LT_AC_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' - _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no - _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no - runpath_var='LD_RUN_PATH' - - if test "$GCC" = yes; then - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - else - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - fi - ;; - - sysv5* | sco3.2v5* | sco5v6*) - # Note: We can NOT use -z defs as we might desire, because we do not - # link with -lc, and that would cause any symbols used from libc to - # always be unresolved, which means just about no library would - # ever link correctly. If we're not using GNU ld we use -z text - # though, which does catch some bad symbols but isn't as heavy-handed - # as -z defs. - _LT_AC_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' - _LT_AC_TAGVAR(allow_undefined_flag, $1)='${wl}-z,nodefs' - _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no - _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='`test -z "$SCOABSPATH" && echo ${wl}-R,$libdir`' - _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=':' - _LT_AC_TAGVAR(link_all_deplibs, $1)=yes - _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Bexport' - runpath_var='LD_RUN_PATH' - - if test "$GCC" = yes; then - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,\${SCOABSPATH:+${install_libdir}/}$soname -o $lib $libobjs $deplibs $compiler_flags' - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,\${SCOABSPATH:+${install_libdir}/}$soname -o $lib $libobjs $deplibs $compiler_flags' - else - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,\${SCOABSPATH:+${install_libdir}/}$soname -o $lib $libobjs $deplibs $compiler_flags' - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,\${SCOABSPATH:+${install_libdir}/}$soname -o $lib $libobjs $deplibs $compiler_flags' - fi - ;; - - uts4*) - _LT_AC_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' - _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no - ;; - - *) - _LT_AC_TAGVAR(ld_shlibs, $1)=no - ;; - esac - fi -]) -AC_MSG_RESULT([$_LT_AC_TAGVAR(ld_shlibs, $1)]) -test "$_LT_AC_TAGVAR(ld_shlibs, $1)" = no && can_build_shared=no - -# -# Do we need to explicitly link libc? -# -case "x$_LT_AC_TAGVAR(archive_cmds_need_lc, $1)" in -x|xyes) - # Assume -lc should be added - _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=yes - - if test "$enable_shared" = yes && test "$GCC" = yes; then - case $_LT_AC_TAGVAR(archive_cmds, $1) in - *'~'*) - # FIXME: we may have to deal with multi-command sequences. - ;; - '$CC '*) - # Test whether the compiler implicitly links with -lc since on some - # systems, -lgcc has to come before -lc. If gcc already passes -lc - # to ld, don't add -lc before -lgcc. - AC_MSG_CHECKING([whether -lc should be explicitly linked in]) - $rm conftest* - printf "$lt_simple_compile_test_code" > conftest.$ac_ext - - if AC_TRY_EVAL(ac_compile) 2>conftest.err; then - soname=conftest - lib=conftest - libobjs=conftest.$ac_objext - deplibs= - wl=$_LT_AC_TAGVAR(lt_prog_compiler_wl, $1) - pic_flag=$_LT_AC_TAGVAR(lt_prog_compiler_pic, $1) - compiler_flags=-v - linker_flags=-v - verstring= - output_objdir=. - libname=conftest - lt_save_allow_undefined_flag=$_LT_AC_TAGVAR(allow_undefined_flag, $1) - _LT_AC_TAGVAR(allow_undefined_flag, $1)= - if AC_TRY_EVAL(_LT_AC_TAGVAR(archive_cmds, $1) 2\>\&1 \| grep \" -lc \" \>/dev/null 2\>\&1) - then - _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no - else - _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=yes - fi - _LT_AC_TAGVAR(allow_undefined_flag, $1)=$lt_save_allow_undefined_flag - else - cat conftest.err 1>&5 - fi - $rm conftest* - AC_MSG_RESULT([$_LT_AC_TAGVAR(archive_cmds_need_lc, $1)]) - ;; - esac - fi - ;; -esac -])# AC_LIBTOOL_PROG_LD_SHLIBS - - -# _LT_AC_FILE_LTDLL_C -# ------------------- -# Be careful that the start marker always follows a newline. -AC_DEFUN([_LT_AC_FILE_LTDLL_C], [ -# /* ltdll.c starts here */ -# #define WIN32_LEAN_AND_MEAN -# #include -# #undef WIN32_LEAN_AND_MEAN -# #include -# -# #ifndef __CYGWIN__ -# # ifdef __CYGWIN32__ -# # define __CYGWIN__ __CYGWIN32__ -# # endif -# #endif -# -# #ifdef __cplusplus -# extern "C" { -# #endif -# BOOL APIENTRY DllMain (HINSTANCE hInst, DWORD reason, LPVOID reserved); -# #ifdef __cplusplus -# } -# #endif -# -# #ifdef __CYGWIN__ -# #include -# DECLARE_CYGWIN_DLL( DllMain ); -# #endif -# HINSTANCE __hDllInstance_base; -# -# BOOL APIENTRY -# DllMain (HINSTANCE hInst, DWORD reason, LPVOID reserved) -# { -# __hDllInstance_base = hInst; -# return TRUE; -# } -# /* ltdll.c ends here */ -])# _LT_AC_FILE_LTDLL_C - - -# _LT_AC_TAGVAR(VARNAME, [TAGNAME]) -# --------------------------------- -AC_DEFUN([_LT_AC_TAGVAR], [ifelse([$2], [], [$1], [$1_$2])]) - - -# old names -AC_DEFUN([AM_PROG_LIBTOOL], [AC_PROG_LIBTOOL]) -AC_DEFUN([AM_ENABLE_SHARED], [AC_ENABLE_SHARED($@)]) -AC_DEFUN([AM_ENABLE_STATIC], [AC_ENABLE_STATIC($@)]) -AC_DEFUN([AM_DISABLE_SHARED], [AC_DISABLE_SHARED($@)]) -AC_DEFUN([AM_DISABLE_STATIC], [AC_DISABLE_STATIC($@)]) -AC_DEFUN([AM_PROG_LD], [AC_PROG_LD]) -AC_DEFUN([AM_PROG_NM], [AC_PROG_NM]) - -# This is just to silence aclocal about the macro not being used -ifelse([AC_DISABLE_FAST_INSTALL]) - -AC_DEFUN([LT_AC_PROG_GCJ], -[AC_CHECK_TOOL(GCJ, gcj, no) - test "x${GCJFLAGS+set}" = xset || GCJFLAGS="-g -O2" - AC_SUBST(GCJFLAGS) -]) - -AC_DEFUN([LT_AC_PROG_RC], -[AC_CHECK_TOOL(RC, windres, no) -]) - -############################################################ -# NOTE: This macro has been submitted for inclusion into # -# GNU Autoconf as AC_PROG_SED. When it is available in # -# a released version of Autoconf we should remove this # -# macro and use it instead. # -############################################################ -# LT_AC_PROG_SED -# -------------- -# Check for a fully-functional sed program, that truncates -# as few characters as possible. Prefer GNU sed if found. -AC_DEFUN([LT_AC_PROG_SED], -[AC_MSG_CHECKING([for a sed that does not truncate output]) -AC_CACHE_VAL(lt_cv_path_SED, -[# Loop through the user's path and test for sed and gsed. -# Then use that list of sed's as ones to test for truncation. -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for lt_ac_prog in sed gsed; do - for ac_exec_ext in '' $ac_executable_extensions; do - if $as_executable_p "$as_dir/$lt_ac_prog$ac_exec_ext"; then - lt_ac_sed_list="$lt_ac_sed_list $as_dir/$lt_ac_prog$ac_exec_ext" - fi - done - done -done -lt_ac_max=0 -lt_ac_count=0 -# Add /usr/xpg4/bin/sed as it is typically found on Solaris -# along with /bin/sed that truncates output. -for lt_ac_sed in $lt_ac_sed_list /usr/xpg4/bin/sed; do - test ! -f $lt_ac_sed && continue - cat /dev/null > conftest.in - lt_ac_count=0 - echo $ECHO_N "0123456789$ECHO_C" >conftest.in - # Check for GNU sed and select it if it is found. - if "$lt_ac_sed" --version 2>&1 < /dev/null | grep 'GNU' > /dev/null; then - lt_cv_path_SED=$lt_ac_sed - break - fi - while true; do - cat conftest.in conftest.in >conftest.tmp - mv conftest.tmp conftest.in - cp conftest.in conftest.nl - echo >>conftest.nl - $lt_ac_sed -e 's/a$//' < conftest.nl >conftest.out || break - cmp -s conftest.out conftest.nl || break - # 10000 chars as input seems more than enough - test $lt_ac_count -gt 10 && break - lt_ac_count=`expr $lt_ac_count + 1` - if test $lt_ac_count -gt $lt_ac_max; then - lt_ac_max=$lt_ac_count - lt_cv_path_SED=$lt_ac_sed - fi - done -done -]) -SED=$lt_cv_path_SED -AC_MSG_RESULT([$SED]) -]) diff --git a/cextern/expat/conftools/ltmain.sh b/cextern/expat/conftools/ltmain.sh deleted file mode 100755 index 06823e057a57..000000000000 --- a/cextern/expat/conftools/ltmain.sh +++ /dev/null @@ -1,6863 +0,0 @@ -# ltmain.sh - Provide generalized library-building support services. -# NOTE: Changing this file will not affect anything until you rerun configure. -# -# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005 -# Free Software Foundation, Inc. -# Originally by Gordon Matzigkeit , 1996 -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# As a special exception to the GNU General Public License, if you -# distribute this file as part of a program that contains a -# configuration script generated by Autoconf, you may include it under -# the same distribution terms that you use for the rest of that program. - -basename="s,^.*/,,g" - -# Work around backward compatibility issue on IRIX 6.5. On IRIX 6.4+, sh -# is ksh but when the shell is invoked as "sh" and the current value of -# the _XPG environment variable is not equal to 1 (one), the special -# positional parameter $0, within a function call, is the name of the -# function. -progpath="$0" - -# The name of this program: -progname=`echo "$progpath" | $SED $basename` -modename="$progname" - -# Global variables: -EXIT_SUCCESS=0 -EXIT_FAILURE=1 - -PROGRAM=ltmain.sh -PACKAGE=libtool -VERSION=1.5.22 -TIMESTAMP=" (1.1220.2.365 2005/12/18 22:14:06)" - -# See if we are running on zsh, and set the options which allow our -# commands through without removal of \ escapes. -if test -n "${ZSH_VERSION+set}" ; then - setopt NO_GLOB_SUBST -fi - -# Check that we have a working $echo. -if test "X$1" = X--no-reexec; then - # Discard the --no-reexec flag, and continue. - shift -elif test "X$1" = X--fallback-echo; then - # Avoid inline document here, it may be left over - : -elif test "X`($echo '\t') 2>/dev/null`" = 'X\t'; then - # Yippee, $echo works! - : -else - # Restart under the correct shell, and then maybe $echo will work. - exec $SHELL "$progpath" --no-reexec ${1+"$@"} -fi - -if test "X$1" = X--fallback-echo; then - # used as fallback echo - shift - cat <&2 - $echo "Fatal configuration error. See the $PACKAGE docs for more information." 1>&2 - exit $EXIT_FAILURE -fi - -# Global variables. -mode=$default_mode -nonopt= -prev= -prevopt= -run= -show="$echo" -show_help= -execute_dlfiles= -duplicate_deps=no -preserve_args= -lo2o="s/\\.lo\$/.${objext}/" -o2lo="s/\\.${objext}\$/.lo/" - -##################################### -# Shell function definitions: -# This seems to be the best place for them - -# func_mktempdir [string] -# Make a temporary directory that won't clash with other running -# libtool processes, and avoids race conditions if possible. If -# given, STRING is the basename for that directory. -func_mktempdir () -{ - my_template="${TMPDIR-/tmp}/${1-$progname}" - - if test "$run" = ":"; then - # Return a directory name, but don't create it in dry-run mode - my_tmpdir="${my_template}-$$" - else - - # If mktemp works, use that first and foremost - my_tmpdir=`mktemp -d "${my_template}-XXXXXXXX" 2>/dev/null` - - if test ! -d "$my_tmpdir"; then - # Failing that, at least try and use $RANDOM to avoid a race - my_tmpdir="${my_template}-${RANDOM-0}$$" - - save_mktempdir_umask=`umask` - umask 0077 - $mkdir "$my_tmpdir" - umask $save_mktempdir_umask - fi - - # If we're not in dry-run mode, bomb out on failure - test -d "$my_tmpdir" || { - $echo "cannot create temporary directory \`$my_tmpdir'" 1>&2 - exit $EXIT_FAILURE - } - fi - - $echo "X$my_tmpdir" | $Xsed -} - - -# func_win32_libid arg -# return the library type of file 'arg' -# -# Need a lot of goo to handle *both* DLLs and import libs -# Has to be a shell function in order to 'eat' the argument -# that is supplied when $file_magic_command is called. -func_win32_libid () -{ - win32_libid_type="unknown" - win32_fileres=`file -L $1 2>/dev/null` - case $win32_fileres in - *ar\ archive\ import\ library*) # definitely import - win32_libid_type="x86 archive import" - ;; - *ar\ archive*) # could be an import, or static - if eval $OBJDUMP -f $1 | $SED -e '10q' 2>/dev/null | \ - $EGREP -e 'file format pe-i386(.*architecture: i386)?' >/dev/null ; then - win32_nmres=`eval $NM -f posix -A $1 | \ - $SED -n -e '1,100{/ I /{s,.*,import,;p;q;};}'` - case $win32_nmres in - import*) win32_libid_type="x86 archive import";; - *) win32_libid_type="x86 archive static";; - esac - fi - ;; - *DLL*) - win32_libid_type="x86 DLL" - ;; - *executable*) # but shell scripts are "executable" too... - case $win32_fileres in - *MS\ Windows\ PE\ Intel*) - win32_libid_type="x86 DLL" - ;; - esac - ;; - esac - $echo $win32_libid_type -} - - -# func_infer_tag arg -# Infer tagged configuration to use if any are available and -# if one wasn't chosen via the "--tag" command line option. -# Only attempt this if the compiler in the base compile -# command doesn't match the default compiler. -# arg is usually of the form 'gcc ...' -func_infer_tag () -{ - if test -n "$available_tags" && test -z "$tagname"; then - CC_quoted= - for arg in $CC; do - case $arg in - *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") - arg="\"$arg\"" - ;; - esac - CC_quoted="$CC_quoted $arg" - done - case $@ in - # Blanks in the command may have been stripped by the calling shell, - # but not from the CC environment variable when configure was run. - " $CC "* | "$CC "* | " `$echo $CC` "* | "`$echo $CC` "* | " $CC_quoted"* | "$CC_quoted "* | " `$echo $CC_quoted` "* | "`$echo $CC_quoted` "*) ;; - # Blanks at the start of $base_compile will cause this to fail - # if we don't check for them as well. - *) - for z in $available_tags; do - if grep "^# ### BEGIN LIBTOOL TAG CONFIG: $z$" < "$progpath" > /dev/null; then - # Evaluate the configuration. - eval "`${SED} -n -e '/^# ### BEGIN LIBTOOL TAG CONFIG: '$z'$/,/^# ### END LIBTOOL TAG CONFIG: '$z'$/p' < $progpath`" - CC_quoted= - for arg in $CC; do - # Double-quote args containing other shell metacharacters. - case $arg in - *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") - arg="\"$arg\"" - ;; - esac - CC_quoted="$CC_quoted $arg" - done - case "$@ " in - " $CC "* | "$CC "* | " `$echo $CC` "* | "`$echo $CC` "* | " $CC_quoted"* | "$CC_quoted "* | " `$echo $CC_quoted` "* | "`$echo $CC_quoted` "*) - # The compiler in the base compile command matches - # the one in the tagged configuration. - # Assume this is the tagged configuration we want. - tagname=$z - break - ;; - esac - fi - done - # If $tagname still isn't set, then no tagged configuration - # was found and let the user know that the "--tag" command - # line option must be used. - if test -z "$tagname"; then - $echo "$modename: unable to infer tagged configuration" - $echo "$modename: specify a tag with \`--tag'" 1>&2 - exit $EXIT_FAILURE -# else -# $echo "$modename: using $tagname tagged configuration" - fi - ;; - esac - fi -} - - -# func_extract_an_archive dir oldlib -func_extract_an_archive () -{ - f_ex_an_ar_dir="$1"; shift - f_ex_an_ar_oldlib="$1" - - $show "(cd $f_ex_an_ar_dir && $AR x $f_ex_an_ar_oldlib)" - $run eval "(cd \$f_ex_an_ar_dir && $AR x \$f_ex_an_ar_oldlib)" || exit $? - if ($AR t "$f_ex_an_ar_oldlib" | sort | sort -uc >/dev/null 2>&1); then - : - else - $echo "$modename: ERROR: object name conflicts: $f_ex_an_ar_dir/$f_ex_an_ar_oldlib" 1>&2 - exit $EXIT_FAILURE - fi -} - -# func_extract_archives gentop oldlib ... -func_extract_archives () -{ - my_gentop="$1"; shift - my_oldlibs=${1+"$@"} - my_oldobjs="" - my_xlib="" - my_xabs="" - my_xdir="" - my_status="" - - $show "${rm}r $my_gentop" - $run ${rm}r "$my_gentop" - $show "$mkdir $my_gentop" - $run $mkdir "$my_gentop" - my_status=$? - if test "$my_status" -ne 0 && test ! -d "$my_gentop"; then - exit $my_status - fi - - for my_xlib in $my_oldlibs; do - # Extract the objects. - case $my_xlib in - [\\/]* | [A-Za-z]:[\\/]*) my_xabs="$my_xlib" ;; - *) my_xabs=`pwd`"/$my_xlib" ;; - esac - my_xlib=`$echo "X$my_xlib" | $Xsed -e 's%^.*/%%'` - my_xdir="$my_gentop/$my_xlib" - - $show "${rm}r $my_xdir" - $run ${rm}r "$my_xdir" - $show "$mkdir $my_xdir" - $run $mkdir "$my_xdir" - exit_status=$? - if test "$exit_status" -ne 0 && test ! -d "$my_xdir"; then - exit $exit_status - fi - case $host in - *-darwin*) - $show "Extracting $my_xabs" - # Do not bother doing anything if just a dry run - if test -z "$run"; then - darwin_orig_dir=`pwd` - cd $my_xdir || exit $? - darwin_archive=$my_xabs - darwin_curdir=`pwd` - darwin_base_archive=`$echo "X$darwin_archive" | $Xsed -e 's%^.*/%%'` - darwin_arches=`lipo -info "$darwin_archive" 2>/dev/null | $EGREP Architectures 2>/dev/null` - if test -n "$darwin_arches"; then - darwin_arches=`echo "$darwin_arches" | $SED -e 's/.*are://'` - darwin_arch= - $show "$darwin_base_archive has multiple architectures $darwin_arches" - for darwin_arch in $darwin_arches ; do - mkdir -p "unfat-$$/${darwin_base_archive}-${darwin_arch}" - lipo -thin $darwin_arch -output "unfat-$$/${darwin_base_archive}-${darwin_arch}/${darwin_base_archive}" "${darwin_archive}" - cd "unfat-$$/${darwin_base_archive}-${darwin_arch}" - func_extract_an_archive "`pwd`" "${darwin_base_archive}" - cd "$darwin_curdir" - $rm "unfat-$$/${darwin_base_archive}-${darwin_arch}/${darwin_base_archive}" - done # $darwin_arches - ## Okay now we have a bunch of thin objects, gotta fatten them up :) - darwin_filelist=`find unfat-$$ -type f -name \*.o -print -o -name \*.lo -print| xargs basename | sort -u | $NL2SP` - darwin_file= - darwin_files= - for darwin_file in $darwin_filelist; do - darwin_files=`find unfat-$$ -name $darwin_file -print | $NL2SP` - lipo -create -output "$darwin_file" $darwin_files - done # $darwin_filelist - ${rm}r unfat-$$ - cd "$darwin_orig_dir" - else - cd "$darwin_orig_dir" - func_extract_an_archive "$my_xdir" "$my_xabs" - fi # $darwin_arches - fi # $run - ;; - *) - func_extract_an_archive "$my_xdir" "$my_xabs" - ;; - esac - my_oldobjs="$my_oldobjs "`find $my_xdir -name \*.$objext -print -o -name \*.lo -print | $NL2SP` - done - func_extract_archives_result="$my_oldobjs" -} -# End of Shell function definitions -##################################### - -# Darwin sucks -eval std_shrext=\"$shrext_cmds\" - -disable_libs=no - -# Parse our command line options once, thoroughly. -while test "$#" -gt 0 -do - arg="$1" - shift - - case $arg in - -*=*) optarg=`$echo "X$arg" | $Xsed -e 's/[-_a-zA-Z0-9]*=//'` ;; - *) optarg= ;; - esac - - # If the previous option needs an argument, assign it. - if test -n "$prev"; then - case $prev in - execute_dlfiles) - execute_dlfiles="$execute_dlfiles $arg" - ;; - tag) - tagname="$arg" - preserve_args="${preserve_args}=$arg" - - # Check whether tagname contains only valid characters - case $tagname in - *[!-_A-Za-z0-9,/]*) - $echo "$progname: invalid tag name: $tagname" 1>&2 - exit $EXIT_FAILURE - ;; - esac - - case $tagname in - CC) - # Don't test for the "default" C tag, as we know, it's there, but - # not specially marked. - ;; - *) - if grep "^# ### BEGIN LIBTOOL TAG CONFIG: $tagname$" < "$progpath" > /dev/null; then - taglist="$taglist $tagname" - # Evaluate the configuration. - eval "`${SED} -n -e '/^# ### BEGIN LIBTOOL TAG CONFIG: '$tagname'$/,/^# ### END LIBTOOL TAG CONFIG: '$tagname'$/p' < $progpath`" - else - $echo "$progname: ignoring unknown tag $tagname" 1>&2 - fi - ;; - esac - ;; - *) - eval "$prev=\$arg" - ;; - esac - - prev= - prevopt= - continue - fi - - # Have we seen a non-optional argument yet? - case $arg in - --help) - show_help=yes - ;; - - --version) - $echo "$PROGRAM (GNU $PACKAGE) $VERSION$TIMESTAMP" - $echo - $echo "Copyright (C) 2005 Free Software Foundation, Inc." - $echo "This is free software; see the source for copying conditions. There is NO" - $echo "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." - exit $? - ;; - - --config) - ${SED} -e '1,/^# ### BEGIN LIBTOOL CONFIG/d' -e '/^# ### END LIBTOOL CONFIG/,$d' $progpath - # Now print the configurations for the tags. - for tagname in $taglist; do - ${SED} -n -e "/^# ### BEGIN LIBTOOL TAG CONFIG: $tagname$/,/^# ### END LIBTOOL TAG CONFIG: $tagname$/p" < "$progpath" - done - exit $? - ;; - - --debug) - $echo "$progname: enabling shell trace mode" - set -x - preserve_args="$preserve_args $arg" - ;; - - --dry-run | -n) - run=: - ;; - - --features) - $echo "host: $host" - if test "$build_libtool_libs" = yes; then - $echo "enable shared libraries" - else - $echo "disable shared libraries" - fi - if test "$build_old_libs" = yes; then - $echo "enable static libraries" - else - $echo "disable static libraries" - fi - exit $? - ;; - - --finish) mode="finish" ;; - - --mode) prevopt="--mode" prev=mode ;; - --mode=*) mode="$optarg" ;; - - --preserve-dup-deps) duplicate_deps="yes" ;; - - --quiet | --silent) - show=: - preserve_args="$preserve_args $arg" - ;; - - --tag) - prevopt="--tag" - prev=tag - preserve_args="$preserve_args --tag" - ;; - --tag=*) - set tag "$optarg" ${1+"$@"} - shift - prev=tag - preserve_args="$preserve_args --tag" - ;; - - -dlopen) - prevopt="-dlopen" - prev=execute_dlfiles - ;; - - -*) - $echo "$modename: unrecognized option \`$arg'" 1>&2 - $echo "$help" 1>&2 - exit $EXIT_FAILURE - ;; - - *) - nonopt="$arg" - break - ;; - esac -done - -if test -n "$prevopt"; then - $echo "$modename: option \`$prevopt' requires an argument" 1>&2 - $echo "$help" 1>&2 - exit $EXIT_FAILURE -fi - -case $disable_libs in -no) - ;; -shared) - build_libtool_libs=no - build_old_libs=yes - ;; -static) - build_old_libs=`case $build_libtool_libs in yes) echo no;; *) echo yes;; esac` - ;; -esac - -# If this variable is set in any of the actions, the command in it -# will be execed at the end. This prevents here-documents from being -# left over by shells. -exec_cmd= - -if test -z "$show_help"; then - - # Infer the operation mode. - if test -z "$mode"; then - $echo "*** Warning: inferring the mode of operation is deprecated." 1>&2 - $echo "*** Future versions of Libtool will require --mode=MODE be specified." 1>&2 - case $nonopt in - *cc | cc* | *++ | gcc* | *-gcc* | g++* | xlc*) - mode=link - for arg - do - case $arg in - -c) - mode=compile - break - ;; - esac - done - ;; - *db | *dbx | *strace | *truss) - mode=execute - ;; - *install*|cp|mv) - mode=install - ;; - *rm) - mode=uninstall - ;; - *) - # If we have no mode, but dlfiles were specified, then do execute mode. - test -n "$execute_dlfiles" && mode=execute - - # Just use the default operation mode. - if test -z "$mode"; then - if test -n "$nonopt"; then - $echo "$modename: warning: cannot infer operation mode from \`$nonopt'" 1>&2 - else - $echo "$modename: warning: cannot infer operation mode without MODE-ARGS" 1>&2 - fi - fi - ;; - esac - fi - - # Only execute mode is allowed to have -dlopen flags. - if test -n "$execute_dlfiles" && test "$mode" != execute; then - $echo "$modename: unrecognized option \`-dlopen'" 1>&2 - $echo "$help" 1>&2 - exit $EXIT_FAILURE - fi - - # Change the help message to a mode-specific one. - generic_help="$help" - help="Try \`$modename --help --mode=$mode' for more information." - - # These modes are in order of execution frequency so that they run quickly. - case $mode in - # libtool compile mode - compile) - modename="$modename: compile" - # Get the compilation command and the source file. - base_compile= - srcfile="$nonopt" # always keep a non-empty value in "srcfile" - suppress_opt=yes - suppress_output= - arg_mode=normal - libobj= - later= - - for arg - do - case $arg_mode in - arg ) - # do not "continue". Instead, add this to base_compile - lastarg="$arg" - arg_mode=normal - ;; - - target ) - libobj="$arg" - arg_mode=normal - continue - ;; - - normal ) - # Accept any command-line options. - case $arg in - -o) - if test -n "$libobj" ; then - $echo "$modename: you cannot specify \`-o' more than once" 1>&2 - exit $EXIT_FAILURE - fi - arg_mode=target - continue - ;; - - -static | -prefer-pic | -prefer-non-pic) - later="$later $arg" - continue - ;; - - -no-suppress) - suppress_opt=no - continue - ;; - - -Xcompiler) - arg_mode=arg # the next one goes into the "base_compile" arg list - continue # The current "srcfile" will either be retained or - ;; # replaced later. I would guess that would be a bug. - - -Wc,*) - args=`$echo "X$arg" | $Xsed -e "s/^-Wc,//"` - lastarg= - save_ifs="$IFS"; IFS=',' - for arg in $args; do - IFS="$save_ifs" - - # Double-quote args containing other shell metacharacters. - # Many Bourne shells cannot handle close brackets correctly - # in scan sets, so we specify it separately. - case $arg in - *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") - arg="\"$arg\"" - ;; - esac - lastarg="$lastarg $arg" - done - IFS="$save_ifs" - lastarg=`$echo "X$lastarg" | $Xsed -e "s/^ //"` - - # Add the arguments to base_compile. - base_compile="$base_compile $lastarg" - continue - ;; - - * ) - # Accept the current argument as the source file. - # The previous "srcfile" becomes the current argument. - # - lastarg="$srcfile" - srcfile="$arg" - ;; - esac # case $arg - ;; - esac # case $arg_mode - - # Aesthetically quote the previous argument. - lastarg=`$echo "X$lastarg" | $Xsed -e "$sed_quote_subst"` - - case $lastarg in - # Double-quote args containing other shell metacharacters. - # Many Bourne shells cannot handle close brackets correctly - # in scan sets, and some SunOS ksh mistreat backslash-escaping - # in scan sets (worked around with variable expansion), - # and furthermore cannot handle '|' '&' '(' ')' in scan sets - # at all, so we specify them separately. - *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") - lastarg="\"$lastarg\"" - ;; - esac - - base_compile="$base_compile $lastarg" - done # for arg - - case $arg_mode in - arg) - $echo "$modename: you must specify an argument for -Xcompile" - exit $EXIT_FAILURE - ;; - target) - $echo "$modename: you must specify a target with \`-o'" 1>&2 - exit $EXIT_FAILURE - ;; - *) - # Get the name of the library object. - [ -z "$libobj" ] && libobj=`$echo "X$srcfile" | $Xsed -e 's%^.*/%%'` - ;; - esac - - # Recognize several different file suffixes. - # If the user specifies -o file.o, it is replaced with file.lo - xform='[cCFSifmso]' - case $libobj in - *.ada) xform=ada ;; - *.adb) xform=adb ;; - *.ads) xform=ads ;; - *.asm) xform=asm ;; - *.c++) xform=c++ ;; - *.cc) xform=cc ;; - *.ii) xform=ii ;; - *.class) xform=class ;; - *.cpp) xform=cpp ;; - *.cxx) xform=cxx ;; - *.f90) xform=f90 ;; - *.for) xform=for ;; - *.java) xform=java ;; - esac - - libobj=`$echo "X$libobj" | $Xsed -e "s/\.$xform$/.lo/"` - - case $libobj in - *.lo) obj=`$echo "X$libobj" | $Xsed -e "$lo2o"` ;; - *) - $echo "$modename: cannot determine name of library object from \`$libobj'" 1>&2 - exit $EXIT_FAILURE - ;; - esac - - func_infer_tag $base_compile - - for arg in $later; do - case $arg in - -static) - build_old_libs=yes - continue - ;; - - -prefer-pic) - pic_mode=yes - continue - ;; - - -prefer-non-pic) - pic_mode=no - continue - ;; - esac - done - - qlibobj=`$echo "X$libobj" | $Xsed -e "$sed_quote_subst"` - case $qlibobj in - *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") - qlibobj="\"$qlibobj\"" ;; - esac - test "X$libobj" != "X$qlibobj" \ - && $echo "X$libobj" | grep '[]~#^*{};<>?"'"'"' &()|`$[]' \ - && $echo "$modename: libobj name \`$libobj' may not contain shell special characters." - objname=`$echo "X$obj" | $Xsed -e 's%^.*/%%'` - xdir=`$echo "X$obj" | $Xsed -e 's%/[^/]*$%%'` - if test "X$xdir" = "X$obj"; then - xdir= - else - xdir=$xdir/ - fi - lobj=${xdir}$objdir/$objname - - if test -z "$base_compile"; then - $echo "$modename: you must specify a compilation command" 1>&2 - $echo "$help" 1>&2 - exit $EXIT_FAILURE - fi - - # Delete any leftover library objects. - if test "$build_old_libs" = yes; then - removelist="$obj $lobj $libobj ${libobj}T" - else - removelist="$lobj $libobj ${libobj}T" - fi - - $run $rm $removelist - trap "$run $rm $removelist; exit $EXIT_FAILURE" 1 2 15 - - # On Cygwin there's no "real" PIC flag so we must build both object types - case $host_os in - cygwin* | mingw* | pw32* | os2*) - pic_mode=default - ;; - esac - if test "$pic_mode" = no && test "$deplibs_check_method" != pass_all; then - # non-PIC code in shared libraries is not supported - pic_mode=default - fi - - # Calculate the filename of the output object if compiler does - # not support -o with -c - if test "$compiler_c_o" = no; then - output_obj=`$echo "X$srcfile" | $Xsed -e 's%^.*/%%' -e 's%\.[^.]*$%%'`.${objext} - lockfile="$output_obj.lock" - removelist="$removelist $output_obj $lockfile" - trap "$run $rm $removelist; exit $EXIT_FAILURE" 1 2 15 - else - output_obj= - need_locks=no - lockfile= - fi - - # Lock this critical section if it is needed - # We use this script file to make the link, it avoids creating a new file - if test "$need_locks" = yes; then - until $run ln "$progpath" "$lockfile" 2>/dev/null; do - $show "Waiting for $lockfile to be removed" - sleep 2 - done - elif test "$need_locks" = warn; then - if test -f "$lockfile"; then - $echo "\ -*** ERROR, $lockfile exists and contains: -`cat $lockfile 2>/dev/null` - -This indicates that another process is trying to use the same -temporary object file, and libtool could not work around it because -your compiler does not support \`-c' and \`-o' together. If you -repeat this compilation, it may succeed, by chance, but you had better -avoid parallel builds (make -j) in this platform, or get a better -compiler." - - $run $rm $removelist - exit $EXIT_FAILURE - fi - $echo "$srcfile" > "$lockfile" - fi - - if test -n "$fix_srcfile_path"; then - eval srcfile=\"$fix_srcfile_path\" - fi - qsrcfile=`$echo "X$srcfile" | $Xsed -e "$sed_quote_subst"` - case $qsrcfile in - *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") - qsrcfile="\"$qsrcfile\"" ;; - esac - - $run $rm "$libobj" "${libobj}T" - - # Create a libtool object file (analogous to a ".la" file), - # but don't create it if we're doing a dry run. - test -z "$run" && cat > ${libobj}T </dev/null`" != "X$srcfile"; then - $echo "\ -*** ERROR, $lockfile contains: -`cat $lockfile 2>/dev/null` - -but it should contain: -$srcfile - -This indicates that another process is trying to use the same -temporary object file, and libtool could not work around it because -your compiler does not support \`-c' and \`-o' together. If you -repeat this compilation, it may succeed, by chance, but you had better -avoid parallel builds (make -j) in this platform, or get a better -compiler." - - $run $rm $removelist - exit $EXIT_FAILURE - fi - - # Just move the object if needed, then go on to compile the next one - if test -n "$output_obj" && test "X$output_obj" != "X$lobj"; then - $show "$mv $output_obj $lobj" - if $run $mv $output_obj $lobj; then : - else - error=$? - $run $rm $removelist - exit $error - fi - fi - - # Append the name of the PIC object to the libtool object file. - test -z "$run" && cat >> ${libobj}T <> ${libobj}T </dev/null`" != "X$srcfile"; then - $echo "\ -*** ERROR, $lockfile contains: -`cat $lockfile 2>/dev/null` - -but it should contain: -$srcfile - -This indicates that another process is trying to use the same -temporary object file, and libtool could not work around it because -your compiler does not support \`-c' and \`-o' together. If you -repeat this compilation, it may succeed, by chance, but you had better -avoid parallel builds (make -j) in this platform, or get a better -compiler." - - $run $rm $removelist - exit $EXIT_FAILURE - fi - - # Just move the object if needed - if test -n "$output_obj" && test "X$output_obj" != "X$obj"; then - $show "$mv $output_obj $obj" - if $run $mv $output_obj $obj; then : - else - error=$? - $run $rm $removelist - exit $error - fi - fi - - # Append the name of the non-PIC object the libtool object file. - # Only append if the libtool object file exists. - test -z "$run" && cat >> ${libobj}T <> ${libobj}T <&2 - fi - if test -n "$link_static_flag"; then - dlopen_self=$dlopen_self_static - fi - prefer_static_libs=yes - else - if test -z "$pic_flag" && test -n "$link_static_flag"; then - dlopen_self=$dlopen_self_static - fi - prefer_static_libs=built - fi - build_libtool_libs=no - build_old_libs=yes - break - ;; - esac - done - - # See if our shared archives depend on static archives. - test -n "$old_archive_from_new_cmds" && build_old_libs=yes - - # Go through the arguments, transforming them on the way. - while test "$#" -gt 0; do - arg="$1" - shift - case $arg in - *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") - qarg=\"`$echo "X$arg" | $Xsed -e "$sed_quote_subst"`\" ### testsuite: skip nested quoting test - ;; - *) qarg=$arg ;; - esac - libtool_args="$libtool_args $qarg" - - # If the previous option needs an argument, assign it. - if test -n "$prev"; then - case $prev in - output) - compile_command="$compile_command @OUTPUT@" - finalize_command="$finalize_command @OUTPUT@" - ;; - esac - - case $prev in - dlfiles|dlprefiles) - if test "$preload" = no; then - # Add the symbol object into the linking commands. - compile_command="$compile_command @SYMFILE@" - finalize_command="$finalize_command @SYMFILE@" - preload=yes - fi - case $arg in - *.la | *.lo) ;; # We handle these cases below. - force) - if test "$dlself" = no; then - dlself=needless - export_dynamic=yes - fi - prev= - continue - ;; - self) - if test "$prev" = dlprefiles; then - dlself=yes - elif test "$prev" = dlfiles && test "$dlopen_self" != yes; then - dlself=yes - else - dlself=needless - export_dynamic=yes - fi - prev= - continue - ;; - *) - if test "$prev" = dlfiles; then - dlfiles="$dlfiles $arg" - else - dlprefiles="$dlprefiles $arg" - fi - prev= - continue - ;; - esac - ;; - expsyms) - export_symbols="$arg" - if test ! -f "$arg"; then - $echo "$modename: symbol file \`$arg' does not exist" - exit $EXIT_FAILURE - fi - prev= - continue - ;; - expsyms_regex) - export_symbols_regex="$arg" - prev= - continue - ;; - inst_prefix) - inst_prefix_dir="$arg" - prev= - continue - ;; - precious_regex) - precious_files_regex="$arg" - prev= - continue - ;; - release) - release="-$arg" - prev= - continue - ;; - objectlist) - if test -f "$arg"; then - save_arg=$arg - moreargs= - for fil in `cat $save_arg` - do -# moreargs="$moreargs $fil" - arg=$fil - # A libtool-controlled object. - - # Check to see that this really is a libtool object. - if (${SED} -e '2q' $arg | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then - pic_object= - non_pic_object= - - # Read the .lo file - # If there is no directory component, then add one. - case $arg in - */* | *\\*) . $arg ;; - *) . ./$arg ;; - esac - - if test -z "$pic_object" || \ - test -z "$non_pic_object" || - test "$pic_object" = none && \ - test "$non_pic_object" = none; then - $echo "$modename: cannot find name of object for \`$arg'" 1>&2 - exit $EXIT_FAILURE - fi - - # Extract subdirectory from the argument. - xdir=`$echo "X$arg" | $Xsed -e 's%/[^/]*$%%'` - if test "X$xdir" = "X$arg"; then - xdir= - else - xdir="$xdir/" - fi - - if test "$pic_object" != none; then - # Prepend the subdirectory the object is found in. - pic_object="$xdir$pic_object" - - if test "$prev" = dlfiles; then - if test "$build_libtool_libs" = yes && test "$dlopen_support" = yes; then - dlfiles="$dlfiles $pic_object" - prev= - continue - else - # If libtool objects are unsupported, then we need to preload. - prev=dlprefiles - fi - fi - - # CHECK ME: I think I busted this. -Ossama - if test "$prev" = dlprefiles; then - # Preload the old-style object. - dlprefiles="$dlprefiles $pic_object" - prev= - fi - - # A PIC object. - libobjs="$libobjs $pic_object" - arg="$pic_object" - fi - - # Non-PIC object. - if test "$non_pic_object" != none; then - # Prepend the subdirectory the object is found in. - non_pic_object="$xdir$non_pic_object" - - # A standard non-PIC object - non_pic_objects="$non_pic_objects $non_pic_object" - if test -z "$pic_object" || test "$pic_object" = none ; then - arg="$non_pic_object" - fi - else - # If the PIC object exists, use it instead. - # $xdir was prepended to $pic_object above. - non_pic_object="$pic_object" - non_pic_objects="$non_pic_objects $non_pic_object" - fi - else - # Only an error if not doing a dry-run. - if test -z "$run"; then - $echo "$modename: \`$arg' is not a valid libtool object" 1>&2 - exit $EXIT_FAILURE - else - # Dry-run case. - - # Extract subdirectory from the argument. - xdir=`$echo "X$arg" | $Xsed -e 's%/[^/]*$%%'` - if test "X$xdir" = "X$arg"; then - xdir= - else - xdir="$xdir/" - fi - - pic_object=`$echo "X${xdir}${objdir}/${arg}" | $Xsed -e "$lo2o"` - non_pic_object=`$echo "X${xdir}${arg}" | $Xsed -e "$lo2o"` - libobjs="$libobjs $pic_object" - non_pic_objects="$non_pic_objects $non_pic_object" - fi - fi - done - else - $echo "$modename: link input file \`$save_arg' does not exist" - exit $EXIT_FAILURE - fi - arg=$save_arg - prev= - continue - ;; - rpath | xrpath) - # We need an absolute path. - case $arg in - [\\/]* | [A-Za-z]:[\\/]*) ;; - *) - $echo "$modename: only absolute run-paths are allowed" 1>&2 - exit $EXIT_FAILURE - ;; - esac - if test "$prev" = rpath; then - case "$rpath " in - *" $arg "*) ;; - *) rpath="$rpath $arg" ;; - esac - else - case "$xrpath " in - *" $arg "*) ;; - *) xrpath="$xrpath $arg" ;; - esac - fi - prev= - continue - ;; - xcompiler) - compiler_flags="$compiler_flags $qarg" - prev= - compile_command="$compile_command $qarg" - finalize_command="$finalize_command $qarg" - continue - ;; - xlinker) - linker_flags="$linker_flags $qarg" - compiler_flags="$compiler_flags $wl$qarg" - prev= - compile_command="$compile_command $wl$qarg" - finalize_command="$finalize_command $wl$qarg" - continue - ;; - xcclinker) - linker_flags="$linker_flags $qarg" - compiler_flags="$compiler_flags $qarg" - prev= - compile_command="$compile_command $qarg" - finalize_command="$finalize_command $qarg" - continue - ;; - shrext) - shrext_cmds="$arg" - prev= - continue - ;; - darwin_framework|darwin_framework_skip) - test "$prev" = "darwin_framework" && compiler_flags="$compiler_flags $arg" - compile_command="$compile_command $arg" - finalize_command="$finalize_command $arg" - prev= - continue - ;; - *) - eval "$prev=\"\$arg\"" - prev= - continue - ;; - esac - fi # test -n "$prev" - - prevarg="$arg" - - case $arg in - -all-static) - if test -n "$link_static_flag"; then - compile_command="$compile_command $link_static_flag" - finalize_command="$finalize_command $link_static_flag" - fi - continue - ;; - - -allow-undefined) - # FIXME: remove this flag sometime in the future. - $echo "$modename: \`-allow-undefined' is deprecated because it is the default" 1>&2 - continue - ;; - - -avoid-version) - avoid_version=yes - continue - ;; - - -dlopen) - prev=dlfiles - continue - ;; - - -dlpreopen) - prev=dlprefiles - continue - ;; - - -export-dynamic) - export_dynamic=yes - continue - ;; - - -export-symbols | -export-symbols-regex) - if test -n "$export_symbols" || test -n "$export_symbols_regex"; then - $echo "$modename: more than one -exported-symbols argument is not allowed" - exit $EXIT_FAILURE - fi - if test "X$arg" = "X-export-symbols"; then - prev=expsyms - else - prev=expsyms_regex - fi - continue - ;; - - -framework|-arch|-isysroot) - case " $CC " in - *" ${arg} ${1} "* | *" ${arg} ${1} "*) - prev=darwin_framework_skip ;; - *) compiler_flags="$compiler_flags $arg" - prev=darwin_framework ;; - esac - compile_command="$compile_command $arg" - finalize_command="$finalize_command $arg" - continue - ;; - - -inst-prefix-dir) - prev=inst_prefix - continue - ;; - - # The native IRIX linker understands -LANG:*, -LIST:* and -LNO:* - # so, if we see these flags be careful not to treat them like -L - -L[A-Z][A-Z]*:*) - case $with_gcc/$host in - no/*-*-irix* | /*-*-irix*) - compile_command="$compile_command $arg" - finalize_command="$finalize_command $arg" - ;; - esac - continue - ;; - - -L*) - dir=`$echo "X$arg" | $Xsed -e 's/^-L//'` - # We need an absolute path. - case $dir in - [\\/]* | [A-Za-z]:[\\/]*) ;; - *) - absdir=`cd "$dir" && pwd` - if test -z "$absdir"; then - $echo "$modename: cannot determine absolute directory name of \`$dir'" 1>&2 - absdir="$dir" - notinst_path="$notinst_path $dir" - fi - dir="$absdir" - ;; - esac - case "$deplibs " in - *" -L$dir "*) ;; - *) - deplibs="$deplibs -L$dir" - lib_search_path="$lib_search_path $dir" - ;; - esac - case $host in - *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2*) - testbindir=`$echo "X$dir" | $Xsed -e 's*/lib$*/bin*'` - case :$dllsearchpath: in - *":$dir:"*) ;; - *) dllsearchpath="$dllsearchpath:$dir";; - esac - case :$dllsearchpath: in - *":$testbindir:"*) ;; - *) dllsearchpath="$dllsearchpath:$testbindir";; - esac - ;; - esac - continue - ;; - - -l*) - if test "X$arg" = "X-lc" || test "X$arg" = "X-lm"; then - case $host in - *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-beos*) - # These systems don't actually have a C or math library (as such) - continue - ;; - *-*-os2*) - # These systems don't actually have a C library (as such) - test "X$arg" = "X-lc" && continue - ;; - *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) - # Do not include libc due to us having libc/libc_r. - test "X$arg" = "X-lc" && continue - ;; - *-*-rhapsody* | *-*-darwin1.[012]) - # Rhapsody C and math libraries are in the System framework - deplibs="$deplibs -framework System" - continue - ;; - *-*-sco3.2v5* | *-*-sco5v6*) - # Causes problems with __ctype - test "X$arg" = "X-lc" && continue - ;; - *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*) - # Compiler inserts libc in the correct place for threads to work - test "X$arg" = "X-lc" && continue - ;; - esac - elif test "X$arg" = "X-lc_r"; then - case $host in - *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) - # Do not include libc_r directly, use -pthread flag. - continue - ;; - esac - fi - deplibs="$deplibs $arg" - continue - ;; - - # Tru64 UNIX uses -model [arg] to determine the layout of C++ - # classes, name mangling, and exception handling. - -model) - compile_command="$compile_command $arg" - compiler_flags="$compiler_flags $arg" - finalize_command="$finalize_command $arg" - prev=xcompiler - continue - ;; - - -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe) - compiler_flags="$compiler_flags $arg" - compile_command="$compile_command $arg" - finalize_command="$finalize_command $arg" - continue - ;; - - -module) - module=yes - continue - ;; - - # -64, -mips[0-9] enable 64-bit mode on the SGI compiler - # -r[0-9][0-9]* specifies the processor on the SGI compiler - # -xarch=*, -xtarget=* enable 64-bit mode on the Sun compiler - # +DA*, +DD* enable 64-bit mode on the HP compiler - # -q* pass through compiler args for the IBM compiler - # -m* pass through architecture-specific compiler args for GCC - # -m*, -t[45]*, -txscale* pass through architecture-specific - # compiler args for GCC - # -pg pass through profiling flag for GCC - # @file GCC response files - -64|-mips[0-9]|-r[0-9][0-9]*|-xarch=*|-xtarget=*|+DA*|+DD*|-q*|-m*|-pg| \ - -t[45]*|-txscale*|@*) - - # Unknown arguments in both finalize_command and compile_command need - # to be aesthetically quoted because they are evaled later. - arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"` - case $arg in - *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") - arg="\"$arg\"" - ;; - esac - compile_command="$compile_command $arg" - finalize_command="$finalize_command $arg" - compiler_flags="$compiler_flags $arg" - continue - ;; - - -shrext) - prev=shrext - continue - ;; - - -no-fast-install) - fast_install=no - continue - ;; - - -no-install) - case $host in - *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2*) - # The PATH hackery in wrapper scripts is required on Windows - # in order for the loader to find any dlls it needs. - $echo "$modename: warning: \`-no-install' is ignored for $host" 1>&2 - $echo "$modename: warning: assuming \`-no-fast-install' instead" 1>&2 - fast_install=no - ;; - *) no_install=yes ;; - esac - continue - ;; - - -no-undefined) - allow_undefined=no - continue - ;; - - -objectlist) - prev=objectlist - continue - ;; - - -o) prev=output ;; - - -precious-files-regex) - prev=precious_regex - continue - ;; - - -release) - prev=release - continue - ;; - - -rpath) - prev=rpath - continue - ;; - - -R) - prev=xrpath - continue - ;; - - -R*) - dir=`$echo "X$arg" | $Xsed -e 's/^-R//'` - # We need an absolute path. - case $dir in - [\\/]* | [A-Za-z]:[\\/]*) ;; - *) - $echo "$modename: only absolute run-paths are allowed" 1>&2 - exit $EXIT_FAILURE - ;; - esac - case "$xrpath " in - *" $dir "*) ;; - *) xrpath="$xrpath $dir" ;; - esac - continue - ;; - - -static) - # The effects of -static are defined in a previous loop. - # We used to do the same as -all-static on platforms that - # didn't have a PIC flag, but the assumption that the effects - # would be equivalent was wrong. It would break on at least - # Digital Unix and AIX. - continue - ;; - - -thread-safe) - thread_safe=yes - continue - ;; - - -version-info) - prev=vinfo - continue - ;; - -version-number) - prev=vinfo - vinfo_number=yes - continue - ;; - - -Wc,*) - args=`$echo "X$arg" | $Xsed -e "$sed_quote_subst" -e 's/^-Wc,//'` - arg= - save_ifs="$IFS"; IFS=',' - for flag in $args; do - IFS="$save_ifs" - case $flag in - *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") - flag="\"$flag\"" - ;; - esac - arg="$arg $wl$flag" - compiler_flags="$compiler_flags $flag" - done - IFS="$save_ifs" - arg=`$echo "X$arg" | $Xsed -e "s/^ //"` - ;; - - -Wl,*) - args=`$echo "X$arg" | $Xsed -e "$sed_quote_subst" -e 's/^-Wl,//'` - arg= - save_ifs="$IFS"; IFS=',' - for flag in $args; do - IFS="$save_ifs" - case $flag in - *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") - flag="\"$flag\"" - ;; - esac - arg="$arg $wl$flag" - compiler_flags="$compiler_flags $wl$flag" - linker_flags="$linker_flags $flag" - done - IFS="$save_ifs" - arg=`$echo "X$arg" | $Xsed -e "s/^ //"` - ;; - - -Xcompiler) - prev=xcompiler - continue - ;; - - -Xlinker) - prev=xlinker - continue - ;; - - -XCClinker) - prev=xcclinker - continue - ;; - - # Some other compiler flag. - -* | +*) - # Unknown arguments in both finalize_command and compile_command need - # to be aesthetically quoted because they are evaled later. - arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"` - case $arg in - *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") - arg="\"$arg\"" - ;; - esac - ;; - - *.$objext) - # A standard object. - objs="$objs $arg" - ;; - - *.lo) - # A libtool-controlled object. - - # Check to see that this really is a libtool object. - if (${SED} -e '2q' $arg | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then - pic_object= - non_pic_object= - - # Read the .lo file - # If there is no directory component, then add one. - case $arg in - */* | *\\*) . $arg ;; - *) . ./$arg ;; - esac - - if test -z "$pic_object" || \ - test -z "$non_pic_object" || - test "$pic_object" = none && \ - test "$non_pic_object" = none; then - $echo "$modename: cannot find name of object for \`$arg'" 1>&2 - exit $EXIT_FAILURE - fi - - # Extract subdirectory from the argument. - xdir=`$echo "X$arg" | $Xsed -e 's%/[^/]*$%%'` - if test "X$xdir" = "X$arg"; then - xdir= - else - xdir="$xdir/" - fi - - if test "$pic_object" != none; then - # Prepend the subdirectory the object is found in. - pic_object="$xdir$pic_object" - - if test "$prev" = dlfiles; then - if test "$build_libtool_libs" = yes && test "$dlopen_support" = yes; then - dlfiles="$dlfiles $pic_object" - prev= - continue - else - # If libtool objects are unsupported, then we need to preload. - prev=dlprefiles - fi - fi - - # CHECK ME: I think I busted this. -Ossama - if test "$prev" = dlprefiles; then - # Preload the old-style object. - dlprefiles="$dlprefiles $pic_object" - prev= - fi - - # A PIC object. - libobjs="$libobjs $pic_object" - arg="$pic_object" - fi - - # Non-PIC object. - if test "$non_pic_object" != none; then - # Prepend the subdirectory the object is found in. - non_pic_object="$xdir$non_pic_object" - - # A standard non-PIC object - non_pic_objects="$non_pic_objects $non_pic_object" - if test -z "$pic_object" || test "$pic_object" = none ; then - arg="$non_pic_object" - fi - else - # If the PIC object exists, use it instead. - # $xdir was prepended to $pic_object above. - non_pic_object="$pic_object" - non_pic_objects="$non_pic_objects $non_pic_object" - fi - else - # Only an error if not doing a dry-run. - if test -z "$run"; then - $echo "$modename: \`$arg' is not a valid libtool object" 1>&2 - exit $EXIT_FAILURE - else - # Dry-run case. - - # Extract subdirectory from the argument. - xdir=`$echo "X$arg" | $Xsed -e 's%/[^/]*$%%'` - if test "X$xdir" = "X$arg"; then - xdir= - else - xdir="$xdir/" - fi - - pic_object=`$echo "X${xdir}${objdir}/${arg}" | $Xsed -e "$lo2o"` - non_pic_object=`$echo "X${xdir}${arg}" | $Xsed -e "$lo2o"` - libobjs="$libobjs $pic_object" - non_pic_objects="$non_pic_objects $non_pic_object" - fi - fi - ;; - - *.$libext) - # An archive. - deplibs="$deplibs $arg" - old_deplibs="$old_deplibs $arg" - continue - ;; - - *.la) - # A libtool-controlled library. - - if test "$prev" = dlfiles; then - # This library was specified with -dlopen. - dlfiles="$dlfiles $arg" - prev= - elif test "$prev" = dlprefiles; then - # The library was specified with -dlpreopen. - dlprefiles="$dlprefiles $arg" - prev= - else - deplibs="$deplibs $arg" - fi - continue - ;; - - # Some other compiler argument. - *) - # Unknown arguments in both finalize_command and compile_command need - # to be aesthetically quoted because they are evaled later. - arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"` - case $arg in - *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") - arg="\"$arg\"" - ;; - esac - ;; - esac # arg - - # Now actually substitute the argument into the commands. - if test -n "$arg"; then - compile_command="$compile_command $arg" - finalize_command="$finalize_command $arg" - fi - done # argument parsing loop - - if test -n "$prev"; then - $echo "$modename: the \`$prevarg' option requires an argument" 1>&2 - $echo "$help" 1>&2 - exit $EXIT_FAILURE - fi - - if test "$export_dynamic" = yes && test -n "$export_dynamic_flag_spec"; then - eval arg=\"$export_dynamic_flag_spec\" - compile_command="$compile_command $arg" - finalize_command="$finalize_command $arg" - fi - - oldlibs= - # calculate the name of the file, without its directory - outputname=`$echo "X$output" | $Xsed -e 's%^.*/%%'` - libobjs_save="$libobjs" - - if test -n "$shlibpath_var"; then - # get the directories listed in $shlibpath_var - eval shlib_search_path=\`\$echo \"X\${$shlibpath_var}\" \| \$Xsed -e \'s/:/ /g\'\` - else - shlib_search_path= - fi - eval sys_lib_search_path=\"$sys_lib_search_path_spec\" - eval sys_lib_dlsearch_path=\"$sys_lib_dlsearch_path_spec\" - - output_objdir=`$echo "X$output" | $Xsed -e 's%/[^/]*$%%'` - if test "X$output_objdir" = "X$output"; then - output_objdir="$objdir" - else - output_objdir="$output_objdir/$objdir" - fi - # Create the object directory. - if test ! -d "$output_objdir"; then - $show "$mkdir $output_objdir" - $run $mkdir $output_objdir - exit_status=$? - if test "$exit_status" -ne 0 && test ! -d "$output_objdir"; then - exit $exit_status - fi - fi - - # Determine the type of output - case $output in - "") - $echo "$modename: you must specify an output file" 1>&2 - $echo "$help" 1>&2 - exit $EXIT_FAILURE - ;; - *.$libext) linkmode=oldlib ;; - *.lo | *.$objext) linkmode=obj ;; - *.la) linkmode=lib ;; - *) linkmode=prog ;; # Anything else should be a program. - esac - - case $host in - *cygwin* | *mingw* | *pw32*) - # don't eliminate duplications in $postdeps and $predeps - duplicate_compiler_generated_deps=yes - ;; - *) - duplicate_compiler_generated_deps=$duplicate_deps - ;; - esac - specialdeplibs= - - libs= - # Find all interdependent deplibs by searching for libraries - # that are linked more than once (e.g. -la -lb -la) - for deplib in $deplibs; do - if test "X$duplicate_deps" = "Xyes" ; then - case "$libs " in - *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;; - esac - fi - libs="$libs $deplib" - done - - if test "$linkmode" = lib; then - libs="$predeps $libs $compiler_lib_search_path $postdeps" - - # Compute libraries that are listed more than once in $predeps - # $postdeps and mark them as special (i.e., whose duplicates are - # not to be eliminated). - pre_post_deps= - if test "X$duplicate_compiler_generated_deps" = "Xyes" ; then - for pre_post_dep in $predeps $postdeps; do - case "$pre_post_deps " in - *" $pre_post_dep "*) specialdeplibs="$specialdeplibs $pre_post_deps" ;; - esac - pre_post_deps="$pre_post_deps $pre_post_dep" - done - fi - pre_post_deps= - fi - - deplibs= - newdependency_libs= - newlib_search_path= - need_relink=no # whether we're linking any uninstalled libtool libraries - notinst_deplibs= # not-installed libtool libraries - case $linkmode in - lib) - passes="conv link" - for file in $dlfiles $dlprefiles; do - case $file in - *.la) ;; - *) - $echo "$modename: libraries can \`-dlopen' only libtool libraries: $file" 1>&2 - exit $EXIT_FAILURE - ;; - esac - done - ;; - prog) - compile_deplibs= - finalize_deplibs= - alldeplibs=no - newdlfiles= - newdlprefiles= - passes="conv scan dlopen dlpreopen link" - ;; - *) passes="conv" - ;; - esac - for pass in $passes; do - if test "$linkmode,$pass" = "lib,link" || - test "$linkmode,$pass" = "prog,scan"; then - libs="$deplibs" - deplibs= - fi - if test "$linkmode" = prog; then - case $pass in - dlopen) libs="$dlfiles" ;; - dlpreopen) libs="$dlprefiles" ;; - link) libs="$deplibs %DEPLIBS% $dependency_libs" ;; - esac - fi - if test "$pass" = dlopen; then - # Collect dlpreopened libraries - save_deplibs="$deplibs" - deplibs= - fi - for deplib in $libs; do - lib= - found=no - case $deplib in - -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe) - if test "$linkmode,$pass" = "prog,link"; then - compile_deplibs="$deplib $compile_deplibs" - finalize_deplibs="$deplib $finalize_deplibs" - else - compiler_flags="$compiler_flags $deplib" - fi - continue - ;; - -l*) - if test "$linkmode" != lib && test "$linkmode" != prog; then - $echo "$modename: warning: \`-l' is ignored for archives/objects" 1>&2 - continue - fi - name=`$echo "X$deplib" | $Xsed -e 's/^-l//'` - for searchdir in $newlib_search_path $lib_search_path $sys_lib_search_path $shlib_search_path; do - for search_ext in .la $std_shrext .so .a; do - # Search the libtool library - lib="$searchdir/lib${name}${search_ext}" - if test -f "$lib"; then - if test "$search_ext" = ".la"; then - found=yes - else - found=no - fi - break 2 - fi - done - done - if test "$found" != yes; then - # deplib doesn't seem to be a libtool library - if test "$linkmode,$pass" = "prog,link"; then - compile_deplibs="$deplib $compile_deplibs" - finalize_deplibs="$deplib $finalize_deplibs" - else - deplibs="$deplib $deplibs" - test "$linkmode" = lib && newdependency_libs="$deplib $newdependency_libs" - fi - continue - else # deplib is a libtool library - # If $allow_libtool_libs_with_static_runtimes && $deplib is a stdlib, - # We need to do some special things here, and not later. - if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then - case " $predeps $postdeps " in - *" $deplib "*) - if (${SED} -e '2q' $lib | - grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then - library_names= - old_library= - case $lib in - */* | *\\*) . $lib ;; - *) . ./$lib ;; - esac - for l in $old_library $library_names; do - ll="$l" - done - if test "X$ll" = "X$old_library" ; then # only static version available - found=no - ladir=`$echo "X$lib" | $Xsed -e 's%/[^/]*$%%'` - test "X$ladir" = "X$lib" && ladir="." - lib=$ladir/$old_library - if test "$linkmode,$pass" = "prog,link"; then - compile_deplibs="$deplib $compile_deplibs" - finalize_deplibs="$deplib $finalize_deplibs" - else - deplibs="$deplib $deplibs" - test "$linkmode" = lib && newdependency_libs="$deplib $newdependency_libs" - fi - continue - fi - fi - ;; - *) ;; - esac - fi - fi - ;; # -l - -L*) - case $linkmode in - lib) - deplibs="$deplib $deplibs" - test "$pass" = conv && continue - newdependency_libs="$deplib $newdependency_libs" - newlib_search_path="$newlib_search_path "`$echo "X$deplib" | $Xsed -e 's/^-L//'` - ;; - prog) - if test "$pass" = conv; then - deplibs="$deplib $deplibs" - continue - fi - if test "$pass" = scan; then - deplibs="$deplib $deplibs" - else - compile_deplibs="$deplib $compile_deplibs" - finalize_deplibs="$deplib $finalize_deplibs" - fi - newlib_search_path="$newlib_search_path "`$echo "X$deplib" | $Xsed -e 's/^-L//'` - ;; - *) - $echo "$modename: warning: \`-L' is ignored for archives/objects" 1>&2 - ;; - esac # linkmode - continue - ;; # -L - -R*) - if test "$pass" = link; then - dir=`$echo "X$deplib" | $Xsed -e 's/^-R//'` - # Make sure the xrpath contains only unique directories. - case "$xrpath " in - *" $dir "*) ;; - *) xrpath="$xrpath $dir" ;; - esac - fi - deplibs="$deplib $deplibs" - continue - ;; - *.la) lib="$deplib" ;; - *.$libext) - if test "$pass" = conv; then - deplibs="$deplib $deplibs" - continue - fi - case $linkmode in - lib) - valid_a_lib=no - case $deplibs_check_method in - match_pattern*) - set dummy $deplibs_check_method - match_pattern_regex=`expr "$deplibs_check_method" : "$2 \(.*\)"` - if eval $echo \"$deplib\" 2>/dev/null \ - | $SED 10q \ - | $EGREP "$match_pattern_regex" > /dev/null; then - valid_a_lib=yes - fi - ;; - pass_all) - valid_a_lib=yes - ;; - esac - if test "$valid_a_lib" != yes; then - $echo - $echo "*** Warning: Trying to link with static lib archive $deplib." - $echo "*** I have the capability to make that library automatically link in when" - $echo "*** you link to this library. But I can only do this if you have a" - $echo "*** shared version of the library, which you do not appear to have" - $echo "*** because the file extensions .$libext of this argument makes me believe" - $echo "*** that it is just a static archive that I should not used here." - else - $echo - $echo "*** Warning: Linking the shared library $output against the" - $echo "*** static library $deplib is not portable!" - deplibs="$deplib $deplibs" - fi - continue - ;; - prog) - if test "$pass" != link; then - deplibs="$deplib $deplibs" - else - compile_deplibs="$deplib $compile_deplibs" - finalize_deplibs="$deplib $finalize_deplibs" - fi - continue - ;; - esac # linkmode - ;; # *.$libext - *.lo | *.$objext) - if test "$pass" = conv; then - deplibs="$deplib $deplibs" - elif test "$linkmode" = prog; then - if test "$pass" = dlpreopen || test "$dlopen_support" != yes || test "$build_libtool_libs" = no; then - # If there is no dlopen support or we're linking statically, - # we need to preload. - newdlprefiles="$newdlprefiles $deplib" - compile_deplibs="$deplib $compile_deplibs" - finalize_deplibs="$deplib $finalize_deplibs" - else - newdlfiles="$newdlfiles $deplib" - fi - fi - continue - ;; - %DEPLIBS%) - alldeplibs=yes - continue - ;; - esac # case $deplib - if test "$found" = yes || test -f "$lib"; then : - else - $echo "$modename: cannot find the library \`$lib' or unhandled argument \`$deplib'" 1>&2 - exit $EXIT_FAILURE - fi - - # Check to see that this really is a libtool archive. - if (${SED} -e '2q' $lib | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then : - else - $echo "$modename: \`$lib' is not a valid libtool archive" 1>&2 - exit $EXIT_FAILURE - fi - - ladir=`$echo "X$lib" | $Xsed -e 's%/[^/]*$%%'` - test "X$ladir" = "X$lib" && ladir="." - - dlname= - dlopen= - dlpreopen= - libdir= - library_names= - old_library= - # If the library was installed with an old release of libtool, - # it will not redefine variables installed, or shouldnotlink - installed=yes - shouldnotlink=no - avoidtemprpath= - - - # Read the .la file - case $lib in - */* | *\\*) . $lib ;; - *) . ./$lib ;; - esac - - if test "$linkmode,$pass" = "lib,link" || - test "$linkmode,$pass" = "prog,scan" || - { test "$linkmode" != prog && test "$linkmode" != lib; }; then - test -n "$dlopen" && dlfiles="$dlfiles $dlopen" - test -n "$dlpreopen" && dlprefiles="$dlprefiles $dlpreopen" - fi - - if test "$pass" = conv; then - # Only check for convenience libraries - deplibs="$lib $deplibs" - if test -z "$libdir"; then - if test -z "$old_library"; then - $echo "$modename: cannot find name of link library for \`$lib'" 1>&2 - exit $EXIT_FAILURE - fi - # It is a libtool convenience library, so add in its objects. - convenience="$convenience $ladir/$objdir/$old_library" - old_convenience="$old_convenience $ladir/$objdir/$old_library" - tmp_libs= - for deplib in $dependency_libs; do - deplibs="$deplib $deplibs" - if test "X$duplicate_deps" = "Xyes" ; then - case "$tmp_libs " in - *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;; - esac - fi - tmp_libs="$tmp_libs $deplib" - done - elif test "$linkmode" != prog && test "$linkmode" != lib; then - $echo "$modename: \`$lib' is not a convenience library" 1>&2 - exit $EXIT_FAILURE - fi - continue - fi # $pass = conv - - - # Get the name of the library we link against. - linklib= - for l in $old_library $library_names; do - linklib="$l" - done - if test -z "$linklib"; then - $echo "$modename: cannot find name of link library for \`$lib'" 1>&2 - exit $EXIT_FAILURE - fi - - # This library was specified with -dlopen. - if test "$pass" = dlopen; then - if test -z "$libdir"; then - $echo "$modename: cannot -dlopen a convenience library: \`$lib'" 1>&2 - exit $EXIT_FAILURE - fi - if test -z "$dlname" || - test "$dlopen_support" != yes || - test "$build_libtool_libs" = no; then - # If there is no dlname, no dlopen support or we're linking - # statically, we need to preload. We also need to preload any - # dependent libraries so libltdl's deplib preloader doesn't - # bomb out in the load deplibs phase. - dlprefiles="$dlprefiles $lib $dependency_libs" - else - newdlfiles="$newdlfiles $lib" - fi - continue - fi # $pass = dlopen - - # We need an absolute path. - case $ladir in - [\\/]* | [A-Za-z]:[\\/]*) abs_ladir="$ladir" ;; - *) - abs_ladir=`cd "$ladir" && pwd` - if test -z "$abs_ladir"; then - $echo "$modename: warning: cannot determine absolute directory name of \`$ladir'" 1>&2 - $echo "$modename: passing it literally to the linker, although it might fail" 1>&2 - abs_ladir="$ladir" - fi - ;; - esac - laname=`$echo "X$lib" | $Xsed -e 's%^.*/%%'` - - # Find the relevant object directory and library name. - if test "X$installed" = Xyes; then - if test ! -f "$libdir/$linklib" && test -f "$abs_ladir/$linklib"; then - $echo "$modename: warning: library \`$lib' was moved." 1>&2 - dir="$ladir" - absdir="$abs_ladir" - libdir="$abs_ladir" - else - dir="$libdir" - absdir="$libdir" - fi - test "X$hardcode_automatic" = Xyes && avoidtemprpath=yes - else - if test ! -f "$ladir/$objdir/$linklib" && test -f "$abs_ladir/$linklib"; then - dir="$ladir" - absdir="$abs_ladir" - # Remove this search path later - notinst_path="$notinst_path $abs_ladir" - else - dir="$ladir/$objdir" - absdir="$abs_ladir/$objdir" - # Remove this search path later - notinst_path="$notinst_path $abs_ladir" - fi - fi # $installed = yes - name=`$echo "X$laname" | $Xsed -e 's/\.la$//' -e 's/^lib//'` - - # This library was specified with -dlpreopen. - if test "$pass" = dlpreopen; then - if test -z "$libdir"; then - $echo "$modename: cannot -dlpreopen a convenience library: \`$lib'" 1>&2 - exit $EXIT_FAILURE - fi - # Prefer using a static library (so that no silly _DYNAMIC symbols - # are required to link). - if test -n "$old_library"; then - newdlprefiles="$newdlprefiles $dir/$old_library" - # Otherwise, use the dlname, so that lt_dlopen finds it. - elif test -n "$dlname"; then - newdlprefiles="$newdlprefiles $dir/$dlname" - else - newdlprefiles="$newdlprefiles $dir/$linklib" - fi - fi # $pass = dlpreopen - - if test -z "$libdir"; then - # Link the convenience library - if test "$linkmode" = lib; then - deplibs="$dir/$old_library $deplibs" - elif test "$linkmode,$pass" = "prog,link"; then - compile_deplibs="$dir/$old_library $compile_deplibs" - finalize_deplibs="$dir/$old_library $finalize_deplibs" - else - deplibs="$lib $deplibs" # used for prog,scan pass - fi - continue - fi - - - if test "$linkmode" = prog && test "$pass" != link; then - newlib_search_path="$newlib_search_path $ladir" - deplibs="$lib $deplibs" - - linkalldeplibs=no - if test "$link_all_deplibs" != no || test -z "$library_names" || - test "$build_libtool_libs" = no; then - linkalldeplibs=yes - fi - - tmp_libs= - for deplib in $dependency_libs; do - case $deplib in - -L*) newlib_search_path="$newlib_search_path "`$echo "X$deplib" | $Xsed -e 's/^-L//'`;; ### testsuite: skip nested quoting test - esac - # Need to link against all dependency_libs? - if test "$linkalldeplibs" = yes; then - deplibs="$deplib $deplibs" - else - # Need to hardcode shared library paths - # or/and link against static libraries - newdependency_libs="$deplib $newdependency_libs" - fi - if test "X$duplicate_deps" = "Xyes" ; then - case "$tmp_libs " in - *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;; - esac - fi - tmp_libs="$tmp_libs $deplib" - done # for deplib - continue - fi # $linkmode = prog... - - if test "$linkmode,$pass" = "prog,link"; then - if test -n "$library_names" && - { test "$prefer_static_libs" = no || test -z "$old_library"; }; then - # We need to hardcode the library path - if test -n "$shlibpath_var" && test -z "$avoidtemprpath" ; then - # Make sure the rpath contains only unique directories. - case "$temp_rpath " in - *" $dir "*) ;; - *" $absdir "*) ;; - *) temp_rpath="$temp_rpath $absdir" ;; - esac - fi - - # Hardcode the library path. - # Skip directories that are in the system default run-time - # search path. - case " $sys_lib_dlsearch_path " in - *" $absdir "*) ;; - *) - case "$compile_rpath " in - *" $absdir "*) ;; - *) compile_rpath="$compile_rpath $absdir" - esac - ;; - esac - case " $sys_lib_dlsearch_path " in - *" $libdir "*) ;; - *) - case "$finalize_rpath " in - *" $libdir "*) ;; - *) finalize_rpath="$finalize_rpath $libdir" - esac - ;; - esac - fi # $linkmode,$pass = prog,link... - - if test "$alldeplibs" = yes && - { test "$deplibs_check_method" = pass_all || - { test "$build_libtool_libs" = yes && - test -n "$library_names"; }; }; then - # We only need to search for static libraries - continue - fi - fi - - link_static=no # Whether the deplib will be linked statically - use_static_libs=$prefer_static_libs - if test "$use_static_libs" = built && test "$installed" = yes ; then - use_static_libs=no - fi - if test -n "$library_names" && - { test "$use_static_libs" = no || test -z "$old_library"; }; then - if test "$installed" = no; then - notinst_deplibs="$notinst_deplibs $lib" - need_relink=yes - fi - # This is a shared library - - # Warn about portability, can't link against -module's on - # some systems (darwin) - if test "$shouldnotlink" = yes && test "$pass" = link ; then - $echo - if test "$linkmode" = prog; then - $echo "*** Warning: Linking the executable $output against the loadable module" - else - $echo "*** Warning: Linking the shared library $output against the loadable module" - fi - $echo "*** $linklib is not portable!" - fi - if test "$linkmode" = lib && - test "$hardcode_into_libs" = yes; then - # Hardcode the library path. - # Skip directories that are in the system default run-time - # search path. - case " $sys_lib_dlsearch_path " in - *" $absdir "*) ;; - *) - case "$compile_rpath " in - *" $absdir "*) ;; - *) compile_rpath="$compile_rpath $absdir" - esac - ;; - esac - case " $sys_lib_dlsearch_path " in - *" $libdir "*) ;; - *) - case "$finalize_rpath " in - *" $libdir "*) ;; - *) finalize_rpath="$finalize_rpath $libdir" - esac - ;; - esac - fi - - if test -n "$old_archive_from_expsyms_cmds"; then - # figure out the soname - set dummy $library_names - realname="$2" - shift; shift - libname=`eval \\$echo \"$libname_spec\"` - # use dlname if we got it. it's perfectly good, no? - if test -n "$dlname"; then - soname="$dlname" - elif test -n "$soname_spec"; then - # bleh windows - case $host in - *cygwin* | mingw*) - major=`expr $current - $age` - versuffix="-$major" - ;; - esac - eval soname=\"$soname_spec\" - else - soname="$realname" - fi - - # Make a new name for the extract_expsyms_cmds to use - soroot="$soname" - soname=`$echo $soroot | ${SED} -e 's/^.*\///'` - newlib="libimp-`$echo $soname | ${SED} 's/^lib//;s/\.dll$//'`.a" - - # If the library has no export list, then create one now - if test -f "$output_objdir/$soname-def"; then : - else - $show "extracting exported symbol list from \`$soname'" - save_ifs="$IFS"; IFS='~' - cmds=$extract_expsyms_cmds - for cmd in $cmds; do - IFS="$save_ifs" - eval cmd=\"$cmd\" - $show "$cmd" - $run eval "$cmd" || exit $? - done - IFS="$save_ifs" - fi - - # Create $newlib - if test -f "$output_objdir/$newlib"; then :; else - $show "generating import library for \`$soname'" - save_ifs="$IFS"; IFS='~' - cmds=$old_archive_from_expsyms_cmds - for cmd in $cmds; do - IFS="$save_ifs" - eval cmd=\"$cmd\" - $show "$cmd" - $run eval "$cmd" || exit $? - done - IFS="$save_ifs" - fi - # make sure the library variables are pointing to the new library - dir=$output_objdir - linklib=$newlib - fi # test -n "$old_archive_from_expsyms_cmds" - - if test "$linkmode" = prog || test "$mode" != relink; then - add_shlibpath= - add_dir= - add= - lib_linked=yes - case $hardcode_action in - immediate | unsupported) - if test "$hardcode_direct" = no; then - add="$dir/$linklib" - case $host in - *-*-sco3.2v5.0.[024]*) add_dir="-L$dir" ;; - *-*-sysv4*uw2*) add_dir="-L$dir" ;; - *-*-sysv5OpenUNIX* | *-*-sysv5UnixWare7.[01].[10]* | \ - *-*-unixware7*) add_dir="-L$dir" ;; - *-*-darwin* ) - # if the lib is a module then we can not link against - # it, someone is ignoring the new warnings I added - if /usr/bin/file -L $add 2> /dev/null | - $EGREP ": [^:]* bundle" >/dev/null ; then - $echo "** Warning, lib $linklib is a module, not a shared library" - if test -z "$old_library" ; then - $echo - $echo "** And there doesn't seem to be a static archive available" - $echo "** The link will probably fail, sorry" - else - add="$dir/$old_library" - fi - fi - esac - elif test "$hardcode_minus_L" = no; then - case $host in - *-*-sunos*) add_shlibpath="$dir" ;; - esac - add_dir="-L$dir" - add="-l$name" - elif test "$hardcode_shlibpath_var" = no; then - add_shlibpath="$dir" - add="-l$name" - else - lib_linked=no - fi - ;; - relink) - if test "$hardcode_direct" = yes; then - add="$dir/$linklib" - elif test "$hardcode_minus_L" = yes; then - add_dir="-L$dir" - # Try looking first in the location we're being installed to. - if test -n "$inst_prefix_dir"; then - case $libdir in - [\\/]*) - add_dir="$add_dir -L$inst_prefix_dir$libdir" - ;; - esac - fi - add="-l$name" - elif test "$hardcode_shlibpath_var" = yes; then - add_shlibpath="$dir" - add="-l$name" - else - lib_linked=no - fi - ;; - *) lib_linked=no ;; - esac - - if test "$lib_linked" != yes; then - $echo "$modename: configuration error: unsupported hardcode properties" - exit $EXIT_FAILURE - fi - - if test -n "$add_shlibpath"; then - case :$compile_shlibpath: in - *":$add_shlibpath:"*) ;; - *) compile_shlibpath="$compile_shlibpath$add_shlibpath:" ;; - esac - fi - if test "$linkmode" = prog; then - test -n "$add_dir" && compile_deplibs="$add_dir $compile_deplibs" - test -n "$add" && compile_deplibs="$add $compile_deplibs" - else - test -n "$add_dir" && deplibs="$add_dir $deplibs" - test -n "$add" && deplibs="$add $deplibs" - if test "$hardcode_direct" != yes && \ - test "$hardcode_minus_L" != yes && \ - test "$hardcode_shlibpath_var" = yes; then - case :$finalize_shlibpath: in - *":$libdir:"*) ;; - *) finalize_shlibpath="$finalize_shlibpath$libdir:" ;; - esac - fi - fi - fi - - if test "$linkmode" = prog || test "$mode" = relink; then - add_shlibpath= - add_dir= - add= - # Finalize command for both is simple: just hardcode it. - if test "$hardcode_direct" = yes; then - add="$libdir/$linklib" - elif test "$hardcode_minus_L" = yes; then - add_dir="-L$libdir" - add="-l$name" - elif test "$hardcode_shlibpath_var" = yes; then - case :$finalize_shlibpath: in - *":$libdir:"*) ;; - *) finalize_shlibpath="$finalize_shlibpath$libdir:" ;; - esac - add="-l$name" - elif test "$hardcode_automatic" = yes; then - if test -n "$inst_prefix_dir" && - test -f "$inst_prefix_dir$libdir/$linklib" ; then - add="$inst_prefix_dir$libdir/$linklib" - else - add="$libdir/$linklib" - fi - else - # We cannot seem to hardcode it, guess we'll fake it. - add_dir="-L$libdir" - # Try looking first in the location we're being installed to. - if test -n "$inst_prefix_dir"; then - case $libdir in - [\\/]*) - add_dir="$add_dir -L$inst_prefix_dir$libdir" - ;; - esac - fi - add="-l$name" - fi - - if test "$linkmode" = prog; then - test -n "$add_dir" && finalize_deplibs="$add_dir $finalize_deplibs" - test -n "$add" && finalize_deplibs="$add $finalize_deplibs" - else - test -n "$add_dir" && deplibs="$add_dir $deplibs" - test -n "$add" && deplibs="$add $deplibs" - fi - fi - elif test "$linkmode" = prog; then - # Here we assume that one of hardcode_direct or hardcode_minus_L - # is not unsupported. This is valid on all known static and - # shared platforms. - if test "$hardcode_direct" != unsupported; then - test -n "$old_library" && linklib="$old_library" - compile_deplibs="$dir/$linklib $compile_deplibs" - finalize_deplibs="$dir/$linklib $finalize_deplibs" - else - compile_deplibs="-l$name -L$dir $compile_deplibs" - finalize_deplibs="-l$name -L$dir $finalize_deplibs" - fi - elif test "$build_libtool_libs" = yes; then - # Not a shared library - if test "$deplibs_check_method" != pass_all; then - # We're trying link a shared library against a static one - # but the system doesn't support it. - - # Just print a warning and add the library to dependency_libs so - # that the program can be linked against the static library. - $echo - $echo "*** Warning: This system can not link to static lib archive $lib." - $echo "*** I have the capability to make that library automatically link in when" - $echo "*** you link to this library. But I can only do this if you have a" - $echo "*** shared version of the library, which you do not appear to have." - if test "$module" = yes; then - $echo "*** But as you try to build a module library, libtool will still create " - $echo "*** a static module, that should work as long as the dlopening application" - $echo "*** is linked with the -dlopen flag to resolve symbols at runtime." - if test -z "$global_symbol_pipe"; then - $echo - $echo "*** However, this would only work if libtool was able to extract symbol" - $echo "*** lists from a program, using \`nm' or equivalent, but libtool could" - $echo "*** not find such a program. So, this module is probably useless." - $echo "*** \`nm' from GNU binutils and a full rebuild may help." - fi - if test "$build_old_libs" = no; then - build_libtool_libs=module - build_old_libs=yes - else - build_libtool_libs=no - fi - fi - else - deplibs="$dir/$old_library $deplibs" - link_static=yes - fi - fi # link shared/static library? - - if test "$linkmode" = lib; then - if test -n "$dependency_libs" && - { test "$hardcode_into_libs" != yes || - test "$build_old_libs" = yes || - test "$link_static" = yes; }; then - # Extract -R from dependency_libs - temp_deplibs= - for libdir in $dependency_libs; do - case $libdir in - -R*) temp_xrpath=`$echo "X$libdir" | $Xsed -e 's/^-R//'` - case " $xrpath " in - *" $temp_xrpath "*) ;; - *) xrpath="$xrpath $temp_xrpath";; - esac;; - *) temp_deplibs="$temp_deplibs $libdir";; - esac - done - dependency_libs="$temp_deplibs" - fi - - newlib_search_path="$newlib_search_path $absdir" - # Link against this library - test "$link_static" = no && newdependency_libs="$abs_ladir/$laname $newdependency_libs" - # ... and its dependency_libs - tmp_libs= - for deplib in $dependency_libs; do - newdependency_libs="$deplib $newdependency_libs" - if test "X$duplicate_deps" = "Xyes" ; then - case "$tmp_libs " in - *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;; - esac - fi - tmp_libs="$tmp_libs $deplib" - done - - if test "$link_all_deplibs" != no; then - # Add the search paths of all dependency libraries - for deplib in $dependency_libs; do - case $deplib in - -L*) path="$deplib" ;; - *.la) - dir=`$echo "X$deplib" | $Xsed -e 's%/[^/]*$%%'` - test "X$dir" = "X$deplib" && dir="." - # We need an absolute path. - case $dir in - [\\/]* | [A-Za-z]:[\\/]*) absdir="$dir" ;; - *) - absdir=`cd "$dir" && pwd` - if test -z "$absdir"; then - $echo "$modename: warning: cannot determine absolute directory name of \`$dir'" 1>&2 - absdir="$dir" - fi - ;; - esac - if grep "^installed=no" $deplib > /dev/null; then - path="$absdir/$objdir" - else - eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $deplib` - if test -z "$libdir"; then - $echo "$modename: \`$deplib' is not a valid libtool archive" 1>&2 - exit $EXIT_FAILURE - fi - if test "$absdir" != "$libdir"; then - $echo "$modename: warning: \`$deplib' seems to be moved" 1>&2 - fi - path="$absdir" - fi - depdepl= - case $host in - *-*-darwin*) - # we do not want to link against static libs, - # but need to link against shared - eval deplibrary_names=`${SED} -n -e 's/^library_names=\(.*\)$/\1/p' $deplib` - if test -n "$deplibrary_names" ; then - for tmp in $deplibrary_names ; do - depdepl=$tmp - done - if test -f "$path/$depdepl" ; then - depdepl="$path/$depdepl" - fi - # do not add paths which are already there - case " $newlib_search_path " in - *" $path "*) ;; - *) newlib_search_path="$newlib_search_path $path";; - esac - fi - path="" - ;; - *) - path="-L$path" - ;; - esac - ;; - -l*) - case $host in - *-*-darwin*) - # Again, we only want to link against shared libraries - eval tmp_libs=`$echo "X$deplib" | $Xsed -e "s,^\-l,,"` - for tmp in $newlib_search_path ; do - if test -f "$tmp/lib$tmp_libs.dylib" ; then - eval depdepl="$tmp/lib$tmp_libs.dylib" - break - fi - done - path="" - ;; - *) continue ;; - esac - ;; - *) continue ;; - esac - case " $deplibs " in - *" $path "*) ;; - *) deplibs="$path $deplibs" ;; - esac - case " $deplibs " in - *" $depdepl "*) ;; - *) deplibs="$depdepl $deplibs" ;; - esac - done - fi # link_all_deplibs != no - fi # linkmode = lib - done # for deplib in $libs - dependency_libs="$newdependency_libs" - if test "$pass" = dlpreopen; then - # Link the dlpreopened libraries before other libraries - for deplib in $save_deplibs; do - deplibs="$deplib $deplibs" - done - fi - if test "$pass" != dlopen; then - if test "$pass" != conv; then - # Make sure lib_search_path contains only unique directories. - lib_search_path= - for dir in $newlib_search_path; do - case "$lib_search_path " in - *" $dir "*) ;; - *) lib_search_path="$lib_search_path $dir" ;; - esac - done - newlib_search_path= - fi - - if test "$linkmode,$pass" != "prog,link"; then - vars="deplibs" - else - vars="compile_deplibs finalize_deplibs" - fi - for var in $vars dependency_libs; do - # Add libraries to $var in reverse order - eval tmp_libs=\"\$$var\" - new_libs= - for deplib in $tmp_libs; do - # FIXME: Pedantically, this is the right thing to do, so - # that some nasty dependency loop isn't accidentally - # broken: - #new_libs="$deplib $new_libs" - # Pragmatically, this seems to cause very few problems in - # practice: - case $deplib in - -L*) new_libs="$deplib $new_libs" ;; - -R*) ;; - *) - # And here is the reason: when a library appears more - # than once as an explicit dependence of a library, or - # is implicitly linked in more than once by the - # compiler, it is considered special, and multiple - # occurrences thereof are not removed. Compare this - # with having the same library being listed as a - # dependency of multiple other libraries: in this case, - # we know (pedantically, we assume) the library does not - # need to be listed more than once, so we keep only the - # last copy. This is not always right, but it is rare - # enough that we require users that really mean to play - # such unportable linking tricks to link the library - # using -Wl,-lname, so that libtool does not consider it - # for duplicate removal. - case " $specialdeplibs " in - *" $deplib "*) new_libs="$deplib $new_libs" ;; - *) - case " $new_libs " in - *" $deplib "*) ;; - *) new_libs="$deplib $new_libs" ;; - esac - ;; - esac - ;; - esac - done - tmp_libs= - for deplib in $new_libs; do - case $deplib in - -L*) - case " $tmp_libs " in - *" $deplib "*) ;; - *) tmp_libs="$tmp_libs $deplib" ;; - esac - ;; - *) tmp_libs="$tmp_libs $deplib" ;; - esac - done - eval $var=\"$tmp_libs\" - done # for var - fi - # Last step: remove runtime libs from dependency_libs - # (they stay in deplibs) - tmp_libs= - for i in $dependency_libs ; do - case " $predeps $postdeps $compiler_lib_search_path " in - *" $i "*) - i="" - ;; - esac - if test -n "$i" ; then - tmp_libs="$tmp_libs $i" - fi - done - dependency_libs=$tmp_libs - done # for pass - if test "$linkmode" = prog; then - dlfiles="$newdlfiles" - dlprefiles="$newdlprefiles" - fi - - case $linkmode in - oldlib) - if test -n "$deplibs"; then - $echo "$modename: warning: \`-l' and \`-L' are ignored for archives" 1>&2 - fi - - if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then - $echo "$modename: warning: \`-dlopen' is ignored for archives" 1>&2 - fi - - if test -n "$rpath"; then - $echo "$modename: warning: \`-rpath' is ignored for archives" 1>&2 - fi - - if test -n "$xrpath"; then - $echo "$modename: warning: \`-R' is ignored for archives" 1>&2 - fi - - if test -n "$vinfo"; then - $echo "$modename: warning: \`-version-info/-version-number' is ignored for archives" 1>&2 - fi - - if test -n "$release"; then - $echo "$modename: warning: \`-release' is ignored for archives" 1>&2 - fi - - if test -n "$export_symbols" || test -n "$export_symbols_regex"; then - $echo "$modename: warning: \`-export-symbols' is ignored for archives" 1>&2 - fi - - # Now set the variables for building old libraries. - build_libtool_libs=no - oldlibs="$output" - objs="$objs$old_deplibs" - ;; - - lib) - # Make sure we only generate libraries of the form `libNAME.la'. - case $outputname in - lib*) - name=`$echo "X$outputname" | $Xsed -e 's/\.la$//' -e 's/^lib//'` - eval shared_ext=\"$shrext_cmds\" - eval libname=\"$libname_spec\" - ;; - *) - if test "$module" = no; then - $echo "$modename: libtool library \`$output' must begin with \`lib'" 1>&2 - $echo "$help" 1>&2 - exit $EXIT_FAILURE - fi - if test "$need_lib_prefix" != no; then - # Add the "lib" prefix for modules if required - name=`$echo "X$outputname" | $Xsed -e 's/\.la$//'` - eval shared_ext=\"$shrext_cmds\" - eval libname=\"$libname_spec\" - else - libname=`$echo "X$outputname" | $Xsed -e 's/\.la$//'` - fi - ;; - esac - - if test -n "$objs"; then - if test "$deplibs_check_method" != pass_all; then - $echo "$modename: cannot build libtool library \`$output' from non-libtool objects on this host:$objs" 2>&1 - exit $EXIT_FAILURE - else - $echo - $echo "*** Warning: Linking the shared library $output against the non-libtool" - $echo "*** objects $objs is not portable!" - libobjs="$libobjs $objs" - fi - fi - - if test "$dlself" != no; then - $echo "$modename: warning: \`-dlopen self' is ignored for libtool libraries" 1>&2 - fi - - set dummy $rpath - if test "$#" -gt 2; then - $echo "$modename: warning: ignoring multiple \`-rpath's for a libtool library" 1>&2 - fi - install_libdir="$2" - - oldlibs= - if test -z "$rpath"; then - if test "$build_libtool_libs" = yes; then - # Building a libtool convenience library. - # Some compilers have problems with a `.al' extension so - # convenience libraries should have the same extension an - # archive normally would. - oldlibs="$output_objdir/$libname.$libext $oldlibs" - build_libtool_libs=convenience - build_old_libs=yes - fi - - if test -n "$vinfo"; then - $echo "$modename: warning: \`-version-info/-version-number' is ignored for convenience libraries" 1>&2 - fi - - if test -n "$release"; then - $echo "$modename: warning: \`-release' is ignored for convenience libraries" 1>&2 - fi - else - - # Parse the version information argument. - save_ifs="$IFS"; IFS=':' - set dummy $vinfo 0 0 0 - IFS="$save_ifs" - - if test -n "$8"; then - $echo "$modename: too many parameters to \`-version-info'" 1>&2 - $echo "$help" 1>&2 - exit $EXIT_FAILURE - fi - - # convert absolute version numbers to libtool ages - # this retains compatibility with .la files and attempts - # to make the code below a bit more comprehensible - - case $vinfo_number in - yes) - number_major="$2" - number_minor="$3" - number_revision="$4" - # - # There are really only two kinds -- those that - # use the current revision as the major version - # and those that subtract age and use age as - # a minor version. But, then there is irix - # which has an extra 1 added just for fun - # - case $version_type in - darwin|linux|osf|windows) - current=`expr $number_major + $number_minor` - age="$number_minor" - revision="$number_revision" - ;; - freebsd-aout|freebsd-elf|sunos) - current="$number_major" - revision="$number_minor" - age="0" - ;; - irix|nonstopux) - current=`expr $number_major + $number_minor - 1` - age="$number_minor" - revision="$number_minor" - ;; - esac - ;; - no) - current="$2" - revision="$3" - age="$4" - ;; - esac - - # Check that each of the things are valid numbers. - case $current in - 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; - *) - $echo "$modename: CURRENT \`$current' must be a nonnegative integer" 1>&2 - $echo "$modename: \`$vinfo' is not valid version information" 1>&2 - exit $EXIT_FAILURE - ;; - esac - - case $revision in - 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; - *) - $echo "$modename: REVISION \`$revision' must be a nonnegative integer" 1>&2 - $echo "$modename: \`$vinfo' is not valid version information" 1>&2 - exit $EXIT_FAILURE - ;; - esac - - case $age in - 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; - *) - $echo "$modename: AGE \`$age' must be a nonnegative integer" 1>&2 - $echo "$modename: \`$vinfo' is not valid version information" 1>&2 - exit $EXIT_FAILURE - ;; - esac - - if test "$age" -gt "$current"; then - $echo "$modename: AGE \`$age' is greater than the current interface number \`$current'" 1>&2 - $echo "$modename: \`$vinfo' is not valid version information" 1>&2 - exit $EXIT_FAILURE - fi - - # Calculate the version variables. - major= - versuffix= - verstring= - case $version_type in - none) ;; - - darwin) - # Like Linux, but with the current version available in - # verstring for coding it into the library header - major=.`expr $current - $age` - versuffix="$major.$age.$revision" - # Darwin ld doesn't like 0 for these options... - minor_current=`expr $current + 1` - verstring="${wl}-compatibility_version ${wl}$minor_current ${wl}-current_version ${wl}$minor_current.$revision" - ;; - - freebsd-aout) - major=".$current" - versuffix=".$current.$revision"; - ;; - - freebsd-elf) - major=".$current" - versuffix=".$current"; - ;; - - irix | nonstopux) - major=`expr $current - $age + 1` - - case $version_type in - nonstopux) verstring_prefix=nonstopux ;; - *) verstring_prefix=sgi ;; - esac - verstring="$verstring_prefix$major.$revision" - - # Add in all the interfaces that we are compatible with. - loop=$revision - while test "$loop" -ne 0; do - iface=`expr $revision - $loop` - loop=`expr $loop - 1` - verstring="$verstring_prefix$major.$iface:$verstring" - done - - # Before this point, $major must not contain `.'. - major=.$major - versuffix="$major.$revision" - ;; - - linux) - major=.`expr $current - $age` - versuffix="$major.$age.$revision" - ;; - - osf) - major=.`expr $current - $age` - versuffix=".$current.$age.$revision" - verstring="$current.$age.$revision" - - # Add in all the interfaces that we are compatible with. - loop=$age - while test "$loop" -ne 0; do - iface=`expr $current - $loop` - loop=`expr $loop - 1` - verstring="$verstring:${iface}.0" - done - - # Make executables depend on our current version. - verstring="$verstring:${current}.0" - ;; - - sunos) - major=".$current" - versuffix=".$current.$revision" - ;; - - windows) - # Use '-' rather than '.', since we only want one - # extension on DOS 8.3 filesystems. - major=`expr $current - $age` - versuffix="-$major" - ;; - - *) - $echo "$modename: unknown library version type \`$version_type'" 1>&2 - $echo "Fatal configuration error. See the $PACKAGE docs for more information." 1>&2 - exit $EXIT_FAILURE - ;; - esac - - # Clear the version info if we defaulted, and they specified a release. - if test -z "$vinfo" && test -n "$release"; then - major= - case $version_type in - darwin) - # we can't check for "0.0" in archive_cmds due to quoting - # problems, so we reset it completely - verstring= - ;; - *) - verstring="0.0" - ;; - esac - if test "$need_version" = no; then - versuffix= - else - versuffix=".0.0" - fi - fi - - # Remove version info from name if versioning should be avoided - if test "$avoid_version" = yes && test "$need_version" = no; then - major= - versuffix= - verstring="" - fi - - # Check to see if the archive will have undefined symbols. - if test "$allow_undefined" = yes; then - if test "$allow_undefined_flag" = unsupported; then - $echo "$modename: warning: undefined symbols not allowed in $host shared libraries" 1>&2 - build_libtool_libs=no - build_old_libs=yes - fi - else - # Don't allow undefined symbols. - allow_undefined_flag="$no_undefined_flag" - fi - fi - - if test "$mode" != relink; then - # Remove our outputs, but don't remove object files since they - # may have been created when compiling PIC objects. - removelist= - tempremovelist=`$echo "$output_objdir/*"` - for p in $tempremovelist; do - case $p in - *.$objext) - ;; - $output_objdir/$outputname | $output_objdir/$libname.* | $output_objdir/${libname}${release}.*) - if test "X$precious_files_regex" != "X"; then - if echo $p | $EGREP -e "$precious_files_regex" >/dev/null 2>&1 - then - continue - fi - fi - removelist="$removelist $p" - ;; - *) ;; - esac - done - if test -n "$removelist"; then - $show "${rm}r $removelist" - $run ${rm}r $removelist - fi - fi - - # Now set the variables for building old libraries. - if test "$build_old_libs" = yes && test "$build_libtool_libs" != convenience ; then - oldlibs="$oldlibs $output_objdir/$libname.$libext" - - # Transform .lo files to .o files. - oldobjs="$objs "`$echo "X$libobjs" | $SP2NL | $Xsed -e '/\.'${libext}'$/d' -e "$lo2o" | $NL2SP` - fi - - # Eliminate all temporary directories. - for path in $notinst_path; do - lib_search_path=`$echo "$lib_search_path " | ${SED} -e "s% $path % %g"` - deplibs=`$echo "$deplibs " | ${SED} -e "s% -L$path % %g"` - dependency_libs=`$echo "$dependency_libs " | ${SED} -e "s% -L$path % %g"` - done - - if test -n "$xrpath"; then - # If the user specified any rpath flags, then add them. - temp_xrpath= - for libdir in $xrpath; do - temp_xrpath="$temp_xrpath -R$libdir" - case "$finalize_rpath " in - *" $libdir "*) ;; - *) finalize_rpath="$finalize_rpath $libdir" ;; - esac - done - if test "$hardcode_into_libs" != yes || test "$build_old_libs" = yes; then - dependency_libs="$temp_xrpath $dependency_libs" - fi - fi - - # Make sure dlfiles contains only unique files that won't be dlpreopened - old_dlfiles="$dlfiles" - dlfiles= - for lib in $old_dlfiles; do - case " $dlprefiles $dlfiles " in - *" $lib "*) ;; - *) dlfiles="$dlfiles $lib" ;; - esac - done - - # Make sure dlprefiles contains only unique files - old_dlprefiles="$dlprefiles" - dlprefiles= - for lib in $old_dlprefiles; do - case "$dlprefiles " in - *" $lib "*) ;; - *) dlprefiles="$dlprefiles $lib" ;; - esac - done - - if test "$build_libtool_libs" = yes; then - if test -n "$rpath"; then - case $host in - *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-beos*) - # these systems don't actually have a c library (as such)! - ;; - *-*-rhapsody* | *-*-darwin1.[012]) - # Rhapsody C library is in the System framework - deplibs="$deplibs -framework System" - ;; - *-*-netbsd*) - # Don't link with libc until the a.out ld.so is fixed. - ;; - *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) - # Do not include libc due to us having libc/libc_r. - ;; - *-*-sco3.2v5* | *-*-sco5v6*) - # Causes problems with __ctype - ;; - *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*) - # Compiler inserts libc in the correct place for threads to work - ;; - *) - # Add libc to deplibs on all other systems if necessary. - if test "$build_libtool_need_lc" = "yes"; then - deplibs="$deplibs -lc" - fi - ;; - esac - fi - - # Transform deplibs into only deplibs that can be linked in shared. - name_save=$name - libname_save=$libname - release_save=$release - versuffix_save=$versuffix - major_save=$major - # I'm not sure if I'm treating the release correctly. I think - # release should show up in the -l (ie -lgmp5) so we don't want to - # add it in twice. Is that correct? - release="" - versuffix="" - major="" - newdeplibs= - droppeddeps=no - case $deplibs_check_method in - pass_all) - # Don't check for shared/static. Everything works. - # This might be a little naive. We might want to check - # whether the library exists or not. But this is on - # osf3 & osf4 and I'm not really sure... Just - # implementing what was already the behavior. - newdeplibs=$deplibs - ;; - test_compile) - # This code stresses the "libraries are programs" paradigm to its - # limits. Maybe even breaks it. We compile a program, linking it - # against the deplibs as a proxy for the library. Then we can check - # whether they linked in statically or dynamically with ldd. - $rm conftest.c - cat > conftest.c </dev/null` - for potent_lib in $potential_libs; do - # Follow soft links. - if ls -lLd "$potent_lib" 2>/dev/null \ - | grep " -> " >/dev/null; then - continue - fi - # The statement above tries to avoid entering an - # endless loop below, in case of cyclic links. - # We might still enter an endless loop, since a link - # loop can be closed while we follow links, - # but so what? - potlib="$potent_lib" - while test -h "$potlib" 2>/dev/null; do - potliblink=`ls -ld $potlib | ${SED} 's/.* -> //'` - case $potliblink in - [\\/]* | [A-Za-z]:[\\/]*) potlib="$potliblink";; - *) potlib=`$echo "X$potlib" | $Xsed -e 's,[^/]*$,,'`"$potliblink";; - esac - done - if eval $file_magic_cmd \"\$potlib\" 2>/dev/null \ - | ${SED} 10q \ - | $EGREP "$file_magic_regex" > /dev/null; then - newdeplibs="$newdeplibs $a_deplib" - a_deplib="" - break 2 - fi - done - done - fi - if test -n "$a_deplib" ; then - droppeddeps=yes - $echo - $echo "*** Warning: linker path does not have real file for library $a_deplib." - $echo "*** I have the capability to make that library automatically link in when" - $echo "*** you link to this library. But I can only do this if you have a" - $echo "*** shared version of the library, which you do not appear to have" - $echo "*** because I did check the linker path looking for a file starting" - if test -z "$potlib" ; then - $echo "*** with $libname but no candidates were found. (...for file magic test)" - else - $echo "*** with $libname and none of the candidates passed a file format test" - $echo "*** using a file magic. Last file checked: $potlib" - fi - fi - else - # Add a -L argument. - newdeplibs="$newdeplibs $a_deplib" - fi - done # Gone through all deplibs. - ;; - match_pattern*) - set dummy $deplibs_check_method - match_pattern_regex=`expr "$deplibs_check_method" : "$2 \(.*\)"` - for a_deplib in $deplibs; do - name=`expr $a_deplib : '-l\(.*\)'` - # If $name is empty we are operating on a -L argument. - if test -n "$name" && test "$name" != "0"; then - if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then - case " $predeps $postdeps " in - *" $a_deplib "*) - newdeplibs="$newdeplibs $a_deplib" - a_deplib="" - ;; - esac - fi - if test -n "$a_deplib" ; then - libname=`eval \\$echo \"$libname_spec\"` - for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do - potential_libs=`ls $i/$libname[.-]* 2>/dev/null` - for potent_lib in $potential_libs; do - potlib="$potent_lib" # see symlink-check above in file_magic test - if eval $echo \"$potent_lib\" 2>/dev/null \ - | ${SED} 10q \ - | $EGREP "$match_pattern_regex" > /dev/null; then - newdeplibs="$newdeplibs $a_deplib" - a_deplib="" - break 2 - fi - done - done - fi - if test -n "$a_deplib" ; then - droppeddeps=yes - $echo - $echo "*** Warning: linker path does not have real file for library $a_deplib." - $echo "*** I have the capability to make that library automatically link in when" - $echo "*** you link to this library. But I can only do this if you have a" - $echo "*** shared version of the library, which you do not appear to have" - $echo "*** because I did check the linker path looking for a file starting" - if test -z "$potlib" ; then - $echo "*** with $libname but no candidates were found. (...for regex pattern test)" - else - $echo "*** with $libname and none of the candidates passed a file format test" - $echo "*** using a regex pattern. Last file checked: $potlib" - fi - fi - else - # Add a -L argument. - newdeplibs="$newdeplibs $a_deplib" - fi - done # Gone through all deplibs. - ;; - none | unknown | *) - newdeplibs="" - tmp_deplibs=`$echo "X $deplibs" | $Xsed -e 's/ -lc$//' \ - -e 's/ -[LR][^ ]*//g'` - if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then - for i in $predeps $postdeps ; do - # can't use Xsed below, because $i might contain '/' - tmp_deplibs=`$echo "X $tmp_deplibs" | ${SED} -e "1s,^X,," -e "s,$i,,"` - done - fi - if $echo "X $tmp_deplibs" | $Xsed -e 's/[ ]//g' \ - | grep . >/dev/null; then - $echo - if test "X$deplibs_check_method" = "Xnone"; then - $echo "*** Warning: inter-library dependencies are not supported in this platform." - else - $echo "*** Warning: inter-library dependencies are not known to be supported." - fi - $echo "*** All declared inter-library dependencies are being dropped." - droppeddeps=yes - fi - ;; - esac - versuffix=$versuffix_save - major=$major_save - release=$release_save - libname=$libname_save - name=$name_save - - case $host in - *-*-rhapsody* | *-*-darwin1.[012]) - # On Rhapsody replace the C library is the System framework - newdeplibs=`$echo "X $newdeplibs" | $Xsed -e 's/ -lc / -framework System /'` - ;; - esac - - if test "$droppeddeps" = yes; then - if test "$module" = yes; then - $echo - $echo "*** Warning: libtool could not satisfy all declared inter-library" - $echo "*** dependencies of module $libname. Therefore, libtool will create" - $echo "*** a static module, that should work as long as the dlopening" - $echo "*** application is linked with the -dlopen flag." - if test -z "$global_symbol_pipe"; then - $echo - $echo "*** However, this would only work if libtool was able to extract symbol" - $echo "*** lists from a program, using \`nm' or equivalent, but libtool could" - $echo "*** not find such a program. So, this module is probably useless." - $echo "*** \`nm' from GNU binutils and a full rebuild may help." - fi - if test "$build_old_libs" = no; then - oldlibs="$output_objdir/$libname.$libext" - build_libtool_libs=module - build_old_libs=yes - else - build_libtool_libs=no - fi - else - $echo "*** The inter-library dependencies that have been dropped here will be" - $echo "*** automatically added whenever a program is linked with this library" - $echo "*** or is declared to -dlopen it." - - if test "$allow_undefined" = no; then - $echo - $echo "*** Since this library must not contain undefined symbols," - $echo "*** because either the platform does not support them or" - $echo "*** it was explicitly requested with -no-undefined," - $echo "*** libtool will only create a static version of it." - if test "$build_old_libs" = no; then - oldlibs="$output_objdir/$libname.$libext" - build_libtool_libs=module - build_old_libs=yes - else - build_libtool_libs=no - fi - fi - fi - fi - # Done checking deplibs! - deplibs=$newdeplibs - fi - - - # move library search paths that coincide with paths to not yet - # installed libraries to the beginning of the library search list - new_libs= - for path in $notinst_path; do - case " $new_libs " in - *" -L$path/$objdir "*) ;; - *) - case " $deplibs " in - *" -L$path/$objdir "*) - new_libs="$new_libs -L$path/$objdir" ;; - esac - ;; - esac - done - for deplib in $deplibs; do - case $deplib in - -L*) - case " $new_libs " in - *" $deplib "*) ;; - *) new_libs="$new_libs $deplib" ;; - esac - ;; - *) new_libs="$new_libs $deplib" ;; - esac - done - deplibs="$new_libs" - - - # All the library-specific variables (install_libdir is set above). - library_names= - old_library= - dlname= - - # Test again, we may have decided not to build it any more - if test "$build_libtool_libs" = yes; then - if test "$hardcode_into_libs" = yes; then - # Hardcode the library paths - hardcode_libdirs= - dep_rpath= - rpath="$finalize_rpath" - test "$mode" != relink && rpath="$compile_rpath$rpath" - for libdir in $rpath; do - if test -n "$hardcode_libdir_flag_spec"; then - if test -n "$hardcode_libdir_separator"; then - if test -z "$hardcode_libdirs"; then - hardcode_libdirs="$libdir" - else - # Just accumulate the unique libdirs. - case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in - *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) - ;; - *) - hardcode_libdirs="$hardcode_libdirs$hardcode_libdir_separator$libdir" - ;; - esac - fi - else - eval flag=\"$hardcode_libdir_flag_spec\" - dep_rpath="$dep_rpath $flag" - fi - elif test -n "$runpath_var"; then - case "$perm_rpath " in - *" $libdir "*) ;; - *) perm_rpath="$perm_rpath $libdir" ;; - esac - fi - done - # Substitute the hardcoded libdirs into the rpath. - if test -n "$hardcode_libdir_separator" && - test -n "$hardcode_libdirs"; then - libdir="$hardcode_libdirs" - if test -n "$hardcode_libdir_flag_spec_ld"; then - eval dep_rpath=\"$hardcode_libdir_flag_spec_ld\" - else - eval dep_rpath=\"$hardcode_libdir_flag_spec\" - fi - fi - if test -n "$runpath_var" && test -n "$perm_rpath"; then - # We should set the runpath_var. - rpath= - for dir in $perm_rpath; do - rpath="$rpath$dir:" - done - eval "$runpath_var='$rpath\$$runpath_var'; export $runpath_var" - fi - test -n "$dep_rpath" && deplibs="$dep_rpath $deplibs" - fi - - shlibpath="$finalize_shlibpath" - test "$mode" != relink && shlibpath="$compile_shlibpath$shlibpath" - if test -n "$shlibpath"; then - eval "$shlibpath_var='$shlibpath\$$shlibpath_var'; export $shlibpath_var" - fi - - # Get the real and link names of the library. - eval shared_ext=\"$shrext_cmds\" - eval library_names=\"$library_names_spec\" - set dummy $library_names - realname="$2" - shift; shift - - if test -n "$soname_spec"; then - eval soname=\"$soname_spec\" - else - soname="$realname" - fi - if test -z "$dlname"; then - dlname=$soname - fi - - lib="$output_objdir/$realname" - linknames= - for link - do - linknames="$linknames $link" - done - - # Use standard objects if they are pic - test -z "$pic_flag" && libobjs=`$echo "X$libobjs" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP` - - # Prepare the list of exported symbols - if test -z "$export_symbols"; then - if test "$always_export_symbols" = yes || test -n "$export_symbols_regex"; then - $show "generating symbol list for \`$libname.la'" - export_symbols="$output_objdir/$libname.exp" - $run $rm $export_symbols - cmds=$export_symbols_cmds - save_ifs="$IFS"; IFS='~' - for cmd in $cmds; do - IFS="$save_ifs" - eval cmd=\"$cmd\" - if len=`expr "X$cmd" : ".*"` && - test "$len" -le "$max_cmd_len" || test "$max_cmd_len" -le -1; then - $show "$cmd" - $run eval "$cmd" || exit $? - skipped_export=false - else - # The command line is too long to execute in one step. - $show "using reloadable object file for export list..." - skipped_export=: - # Break out early, otherwise skipped_export may be - # set to false by a later but shorter cmd. - break - fi - done - IFS="$save_ifs" - if test -n "$export_symbols_regex"; then - $show "$EGREP -e \"$export_symbols_regex\" \"$export_symbols\" > \"${export_symbols}T\"" - $run eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"' - $show "$mv \"${export_symbols}T\" \"$export_symbols\"" - $run eval '$mv "${export_symbols}T" "$export_symbols"' - fi - fi - fi - - if test -n "$export_symbols" && test -n "$include_expsyms"; then - $run eval '$echo "X$include_expsyms" | $SP2NL >> "$export_symbols"' - fi - - tmp_deplibs= - for test_deplib in $deplibs; do - case " $convenience " in - *" $test_deplib "*) ;; - *) - tmp_deplibs="$tmp_deplibs $test_deplib" - ;; - esac - done - deplibs="$tmp_deplibs" - - if test -n "$convenience"; then - if test -n "$whole_archive_flag_spec"; then - save_libobjs=$libobjs - eval libobjs=\"\$libobjs $whole_archive_flag_spec\" - else - gentop="$output_objdir/${outputname}x" - generated="$generated $gentop" - - func_extract_archives $gentop $convenience - libobjs="$libobjs $func_extract_archives_result" - fi - fi - - if test "$thread_safe" = yes && test -n "$thread_safe_flag_spec"; then - eval flag=\"$thread_safe_flag_spec\" - linker_flags="$linker_flags $flag" - fi - - # Make a backup of the uninstalled library when relinking - if test "$mode" = relink; then - $run eval '(cd $output_objdir && $rm ${realname}U && $mv $realname ${realname}U)' || exit $? - fi - - # Do each of the archive commands. - if test "$module" = yes && test -n "$module_cmds" ; then - if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then - eval test_cmds=\"$module_expsym_cmds\" - cmds=$module_expsym_cmds - else - eval test_cmds=\"$module_cmds\" - cmds=$module_cmds - fi - else - if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then - eval test_cmds=\"$archive_expsym_cmds\" - cmds=$archive_expsym_cmds - else - eval test_cmds=\"$archive_cmds\" - cmds=$archive_cmds - fi - fi - - if test "X$skipped_export" != "X:" && - len=`expr "X$test_cmds" : ".*" 2>/dev/null` && - test "$len" -le "$max_cmd_len" || test "$max_cmd_len" -le -1; then - : - else - # The command line is too long to link in one step, link piecewise. - $echo "creating reloadable object files..." - - # Save the value of $output and $libobjs because we want to - # use them later. If we have whole_archive_flag_spec, we - # want to use save_libobjs as it was before - # whole_archive_flag_spec was expanded, because we can't - # assume the linker understands whole_archive_flag_spec. - # This may have to be revisited, in case too many - # convenience libraries get linked in and end up exceeding - # the spec. - if test -z "$convenience" || test -z "$whole_archive_flag_spec"; then - save_libobjs=$libobjs - fi - save_output=$output - output_la=`$echo "X$output" | $Xsed -e "$basename"` - - # Clear the reloadable object creation command queue and - # initialize k to one. - test_cmds= - concat_cmds= - objlist= - delfiles= - last_robj= - k=1 - output=$output_objdir/$output_la-${k}.$objext - # Loop over the list of objects to be linked. - for obj in $save_libobjs - do - eval test_cmds=\"$reload_cmds $objlist $last_robj\" - if test "X$objlist" = X || - { len=`expr "X$test_cmds" : ".*" 2>/dev/null` && - test "$len" -le "$max_cmd_len"; }; then - objlist="$objlist $obj" - else - # The command $test_cmds is almost too long, add a - # command to the queue. - if test "$k" -eq 1 ; then - # The first file doesn't have a previous command to add. - eval concat_cmds=\"$reload_cmds $objlist $last_robj\" - else - # All subsequent reloadable object files will link in - # the last one created. - eval concat_cmds=\"\$concat_cmds~$reload_cmds $objlist $last_robj\" - fi - last_robj=$output_objdir/$output_la-${k}.$objext - k=`expr $k + 1` - output=$output_objdir/$output_la-${k}.$objext - objlist=$obj - len=1 - fi - done - # Handle the remaining objects by creating one last - # reloadable object file. All subsequent reloadable object - # files will link in the last one created. - test -z "$concat_cmds" || concat_cmds=$concat_cmds~ - eval concat_cmds=\"\${concat_cmds}$reload_cmds $objlist $last_robj\" - - if ${skipped_export-false}; then - $show "generating symbol list for \`$libname.la'" - export_symbols="$output_objdir/$libname.exp" - $run $rm $export_symbols - libobjs=$output - # Append the command to create the export file. - eval concat_cmds=\"\$concat_cmds~$export_symbols_cmds\" - fi - - # Set up a command to remove the reloadable object files - # after they are used. - i=0 - while test "$i" -lt "$k" - do - i=`expr $i + 1` - delfiles="$delfiles $output_objdir/$output_la-${i}.$objext" - done - - $echo "creating a temporary reloadable object file: $output" - - # Loop through the commands generated above and execute them. - save_ifs="$IFS"; IFS='~' - for cmd in $concat_cmds; do - IFS="$save_ifs" - $show "$cmd" - $run eval "$cmd" || exit $? - done - IFS="$save_ifs" - - libobjs=$output - # Restore the value of output. - output=$save_output - - if test -n "$convenience" && test -n "$whole_archive_flag_spec"; then - eval libobjs=\"\$libobjs $whole_archive_flag_spec\" - fi - # Expand the library linking commands again to reset the - # value of $libobjs for piecewise linking. - - # Do each of the archive commands. - if test "$module" = yes && test -n "$module_cmds" ; then - if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then - cmds=$module_expsym_cmds - else - cmds=$module_cmds - fi - else - if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then - cmds=$archive_expsym_cmds - else - cmds=$archive_cmds - fi - fi - - # Append the command to remove the reloadable object files - # to the just-reset $cmds. - eval cmds=\"\$cmds~\$rm $delfiles\" - fi - save_ifs="$IFS"; IFS='~' - for cmd in $cmds; do - IFS="$save_ifs" - eval cmd=\"$cmd\" - $show "$cmd" - $run eval "$cmd" || { - lt_exit=$? - - # Restore the uninstalled library and exit - if test "$mode" = relink; then - $run eval '(cd $output_objdir && $rm ${realname}T && $mv ${realname}U $realname)' - fi - - exit $lt_exit - } - done - IFS="$save_ifs" - - # Restore the uninstalled library and exit - if test "$mode" = relink; then - $run eval '(cd $output_objdir && $rm ${realname}T && $mv $realname ${realname}T && $mv "$realname"U $realname)' || exit $? - - if test -n "$convenience"; then - if test -z "$whole_archive_flag_spec"; then - $show "${rm}r $gentop" - $run ${rm}r "$gentop" - fi - fi - - exit $EXIT_SUCCESS - fi - - # Create links to the real library. - for linkname in $linknames; do - if test "$realname" != "$linkname"; then - $show "(cd $output_objdir && $rm $linkname && $LN_S $realname $linkname)" - $run eval '(cd $output_objdir && $rm $linkname && $LN_S $realname $linkname)' || exit $? - fi - done - - # If -module or -export-dynamic was specified, set the dlname. - if test "$module" = yes || test "$export_dynamic" = yes; then - # On all known operating systems, these are identical. - dlname="$soname" - fi - fi - ;; - - obj) - if test -n "$deplibs"; then - $echo "$modename: warning: \`-l' and \`-L' are ignored for objects" 1>&2 - fi - - if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then - $echo "$modename: warning: \`-dlopen' is ignored for objects" 1>&2 - fi - - if test -n "$rpath"; then - $echo "$modename: warning: \`-rpath' is ignored for objects" 1>&2 - fi - - if test -n "$xrpath"; then - $echo "$modename: warning: \`-R' is ignored for objects" 1>&2 - fi - - if test -n "$vinfo"; then - $echo "$modename: warning: \`-version-info' is ignored for objects" 1>&2 - fi - - if test -n "$release"; then - $echo "$modename: warning: \`-release' is ignored for objects" 1>&2 - fi - - case $output in - *.lo) - if test -n "$objs$old_deplibs"; then - $echo "$modename: cannot build library object \`$output' from non-libtool objects" 1>&2 - exit $EXIT_FAILURE - fi - libobj="$output" - obj=`$echo "X$output" | $Xsed -e "$lo2o"` - ;; - *) - libobj= - obj="$output" - ;; - esac - - # Delete the old objects. - $run $rm $obj $libobj - - # Objects from convenience libraries. This assumes - # single-version convenience libraries. Whenever we create - # different ones for PIC/non-PIC, this we'll have to duplicate - # the extraction. - reload_conv_objs= - gentop= - # reload_cmds runs $LD directly, so let us get rid of - # -Wl from whole_archive_flag_spec - wl= - - if test -n "$convenience"; then - if test -n "$whole_archive_flag_spec"; then - eval reload_conv_objs=\"\$reload_objs $whole_archive_flag_spec\" - else - gentop="$output_objdir/${obj}x" - generated="$generated $gentop" - - func_extract_archives $gentop $convenience - reload_conv_objs="$reload_objs $func_extract_archives_result" - fi - fi - - # Create the old-style object. - reload_objs="$objs$old_deplibs "`$echo "X$libobjs" | $SP2NL | $Xsed -e '/\.'${libext}$'/d' -e '/\.lib$/d' -e "$lo2o" | $NL2SP`" $reload_conv_objs" ### testsuite: skip nested quoting test - - output="$obj" - cmds=$reload_cmds - save_ifs="$IFS"; IFS='~' - for cmd in $cmds; do - IFS="$save_ifs" - eval cmd=\"$cmd\" - $show "$cmd" - $run eval "$cmd" || exit $? - done - IFS="$save_ifs" - - # Exit if we aren't doing a library object file. - if test -z "$libobj"; then - if test -n "$gentop"; then - $show "${rm}r $gentop" - $run ${rm}r $gentop - fi - - exit $EXIT_SUCCESS - fi - - if test "$build_libtool_libs" != yes; then - if test -n "$gentop"; then - $show "${rm}r $gentop" - $run ${rm}r $gentop - fi - - # Create an invalid libtool object if no PIC, so that we don't - # accidentally link it into a program. - # $show "echo timestamp > $libobj" - # $run eval "echo timestamp > $libobj" || exit $? - exit $EXIT_SUCCESS - fi - - if test -n "$pic_flag" || test "$pic_mode" != default; then - # Only do commands if we really have different PIC objects. - reload_objs="$libobjs $reload_conv_objs" - output="$libobj" - cmds=$reload_cmds - save_ifs="$IFS"; IFS='~' - for cmd in $cmds; do - IFS="$save_ifs" - eval cmd=\"$cmd\" - $show "$cmd" - $run eval "$cmd" || exit $? - done - IFS="$save_ifs" - fi - - if test -n "$gentop"; then - $show "${rm}r $gentop" - $run ${rm}r $gentop - fi - - exit $EXIT_SUCCESS - ;; - - prog) - case $host in - *cygwin*) output=`$echo $output | ${SED} -e 's,.exe$,,;s,$,.exe,'` ;; - esac - if test -n "$vinfo"; then - $echo "$modename: warning: \`-version-info' is ignored for programs" 1>&2 - fi - - if test -n "$release"; then - $echo "$modename: warning: \`-release' is ignored for programs" 1>&2 - fi - - if test "$preload" = yes; then - if test "$dlopen_support" = unknown && test "$dlopen_self" = unknown && - test "$dlopen_self_static" = unknown; then - $echo "$modename: warning: \`AC_LIBTOOL_DLOPEN' not used. Assuming no dlopen support." - fi - fi - - case $host in - *-*-rhapsody* | *-*-darwin1.[012]) - # On Rhapsody replace the C library is the System framework - compile_deplibs=`$echo "X $compile_deplibs" | $Xsed -e 's/ -lc / -framework System /'` - finalize_deplibs=`$echo "X $finalize_deplibs" | $Xsed -e 's/ -lc / -framework System /'` - ;; - esac - - case $host in - *darwin*) - # Don't allow lazy linking, it breaks C++ global constructors - if test "$tagname" = CXX ; then - compile_command="$compile_command ${wl}-bind_at_load" - finalize_command="$finalize_command ${wl}-bind_at_load" - fi - ;; - esac - - - # move library search paths that coincide with paths to not yet - # installed libraries to the beginning of the library search list - new_libs= - for path in $notinst_path; do - case " $new_libs " in - *" -L$path/$objdir "*) ;; - *) - case " $compile_deplibs " in - *" -L$path/$objdir "*) - new_libs="$new_libs -L$path/$objdir" ;; - esac - ;; - esac - done - for deplib in $compile_deplibs; do - case $deplib in - -L*) - case " $new_libs " in - *" $deplib "*) ;; - *) new_libs="$new_libs $deplib" ;; - esac - ;; - *) new_libs="$new_libs $deplib" ;; - esac - done - compile_deplibs="$new_libs" - - - compile_command="$compile_command $compile_deplibs" - finalize_command="$finalize_command $finalize_deplibs" - - if test -n "$rpath$xrpath"; then - # If the user specified any rpath flags, then add them. - for libdir in $rpath $xrpath; do - # This is the magic to use -rpath. - case "$finalize_rpath " in - *" $libdir "*) ;; - *) finalize_rpath="$finalize_rpath $libdir" ;; - esac - done - fi - - # Now hardcode the library paths - rpath= - hardcode_libdirs= - for libdir in $compile_rpath $finalize_rpath; do - if test -n "$hardcode_libdir_flag_spec"; then - if test -n "$hardcode_libdir_separator"; then - if test -z "$hardcode_libdirs"; then - hardcode_libdirs="$libdir" - else - # Just accumulate the unique libdirs. - case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in - *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) - ;; - *) - hardcode_libdirs="$hardcode_libdirs$hardcode_libdir_separator$libdir" - ;; - esac - fi - else - eval flag=\"$hardcode_libdir_flag_spec\" - rpath="$rpath $flag" - fi - elif test -n "$runpath_var"; then - case "$perm_rpath " in - *" $libdir "*) ;; - *) perm_rpath="$perm_rpath $libdir" ;; - esac - fi - case $host in - *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2*) - testbindir=`$echo "X$libdir" | $Xsed -e 's*/lib$*/bin*'` - case :$dllsearchpath: in - *":$libdir:"*) ;; - *) dllsearchpath="$dllsearchpath:$libdir";; - esac - case :$dllsearchpath: in - *":$testbindir:"*) ;; - *) dllsearchpath="$dllsearchpath:$testbindir";; - esac - ;; - esac - done - # Substitute the hardcoded libdirs into the rpath. - if test -n "$hardcode_libdir_separator" && - test -n "$hardcode_libdirs"; then - libdir="$hardcode_libdirs" - eval rpath=\" $hardcode_libdir_flag_spec\" - fi - compile_rpath="$rpath" - - rpath= - hardcode_libdirs= - for libdir in $finalize_rpath; do - if test -n "$hardcode_libdir_flag_spec"; then - if test -n "$hardcode_libdir_separator"; then - if test -z "$hardcode_libdirs"; then - hardcode_libdirs="$libdir" - else - # Just accumulate the unique libdirs. - case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in - *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) - ;; - *) - hardcode_libdirs="$hardcode_libdirs$hardcode_libdir_separator$libdir" - ;; - esac - fi - else - eval flag=\"$hardcode_libdir_flag_spec\" - rpath="$rpath $flag" - fi - elif test -n "$runpath_var"; then - case "$finalize_perm_rpath " in - *" $libdir "*) ;; - *) finalize_perm_rpath="$finalize_perm_rpath $libdir" ;; - esac - fi - done - # Substitute the hardcoded libdirs into the rpath. - if test -n "$hardcode_libdir_separator" && - test -n "$hardcode_libdirs"; then - libdir="$hardcode_libdirs" - eval rpath=\" $hardcode_libdir_flag_spec\" - fi - finalize_rpath="$rpath" - - if test -n "$libobjs" && test "$build_old_libs" = yes; then - # Transform all the library objects into standard objects. - compile_command=`$echo "X$compile_command" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP` - finalize_command=`$echo "X$finalize_command" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP` - fi - - dlsyms= - if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then - if test -n "$NM" && test -n "$global_symbol_pipe"; then - dlsyms="${outputname}S.c" - else - $echo "$modename: not configured to extract global symbols from dlpreopened files" 1>&2 - fi - fi - - if test -n "$dlsyms"; then - case $dlsyms in - "") ;; - *.c) - # Discover the nlist of each of the dlfiles. - nlist="$output_objdir/${outputname}.nm" - - $show "$rm $nlist ${nlist}S ${nlist}T" - $run $rm "$nlist" "${nlist}S" "${nlist}T" - - # Parse the name list into a source file. - $show "creating $output_objdir/$dlsyms" - - test -z "$run" && $echo > "$output_objdir/$dlsyms" "\ -/* $dlsyms - symbol resolution table for \`$outputname' dlsym emulation. */ -/* Generated by $PROGRAM - GNU $PACKAGE $VERSION$TIMESTAMP */ - -#ifdef __cplusplus -extern \"C\" { -#endif - -/* Prevent the only kind of declaration conflicts we can make. */ -#define lt_preloaded_symbols some_other_symbol - -/* External symbol declarations for the compiler. */\ -" - - if test "$dlself" = yes; then - $show "generating symbol list for \`$output'" - - test -z "$run" && $echo ': @PROGRAM@ ' > "$nlist" - - # Add our own program objects to the symbol list. - progfiles=`$echo "X$objs$old_deplibs" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP` - for arg in $progfiles; do - $show "extracting global C symbols from \`$arg'" - $run eval "$NM $arg | $global_symbol_pipe >> '$nlist'" - done - - if test -n "$exclude_expsyms"; then - $run eval '$EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T' - $run eval '$mv "$nlist"T "$nlist"' - fi - - if test -n "$export_symbols_regex"; then - $run eval '$EGREP -e "$export_symbols_regex" "$nlist" > "$nlist"T' - $run eval '$mv "$nlist"T "$nlist"' - fi - - # Prepare the list of exported symbols - if test -z "$export_symbols"; then - export_symbols="$output_objdir/$outputname.exp" - $run $rm $export_symbols - $run eval "${SED} -n -e '/^: @PROGRAM@ $/d' -e 's/^.* \(.*\)$/\1/p' "'< "$nlist" > "$export_symbols"' - case $host in - *cygwin* | *mingw* ) - $run eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' - $run eval 'cat "$export_symbols" >> "$output_objdir/$outputname.def"' - ;; - esac - else - $run eval "${SED} -e 's/\([].[*^$]\)/\\\\\1/g' -e 's/^/ /' -e 's/$/$/'"' < "$export_symbols" > "$output_objdir/$outputname.exp"' - $run eval 'grep -f "$output_objdir/$outputname.exp" < "$nlist" > "$nlist"T' - $run eval 'mv "$nlist"T "$nlist"' - case $host in - *cygwin* | *mingw* ) - $run eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' - $run eval 'cat "$nlist" >> "$output_objdir/$outputname.def"' - ;; - esac - fi - fi - - for arg in $dlprefiles; do - $show "extracting global C symbols from \`$arg'" - name=`$echo "$arg" | ${SED} -e 's%^.*/%%'` - $run eval '$echo ": $name " >> "$nlist"' - $run eval "$NM $arg | $global_symbol_pipe >> '$nlist'" - done - - if test -z "$run"; then - # Make sure we have at least an empty file. - test -f "$nlist" || : > "$nlist" - - if test -n "$exclude_expsyms"; then - $EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T - $mv "$nlist"T "$nlist" - fi - - # Try sorting and uniquifying the output. - if grep -v "^: " < "$nlist" | - if sort -k 3 /dev/null 2>&1; then - sort -k 3 - else - sort +2 - fi | - uniq > "$nlist"S; then - : - else - grep -v "^: " < "$nlist" > "$nlist"S - fi - - if test -f "$nlist"S; then - eval "$global_symbol_to_cdecl"' < "$nlist"S >> "$output_objdir/$dlsyms"' - else - $echo '/* NONE */' >> "$output_objdir/$dlsyms" - fi - - $echo >> "$output_objdir/$dlsyms" "\ - -#undef lt_preloaded_symbols - -#if defined (__STDC__) && __STDC__ -# define lt_ptr void * -#else -# define lt_ptr char * -# define const -#endif - -/* The mapping between symbol names and symbols. */ -" - - case $host in - *cygwin* | *mingw* ) - $echo >> "$output_objdir/$dlsyms" "\ -/* DATA imports from DLLs on WIN32 can't be const, because - runtime relocations are performed -- see ld's documentation - on pseudo-relocs */ -struct { -" - ;; - * ) - $echo >> "$output_objdir/$dlsyms" "\ -const struct { -" - ;; - esac - - - $echo >> "$output_objdir/$dlsyms" "\ - const char *name; - lt_ptr address; -} -lt_preloaded_symbols[] = -{\ -" - - eval "$global_symbol_to_c_name_address" < "$nlist" >> "$output_objdir/$dlsyms" - - $echo >> "$output_objdir/$dlsyms" "\ - {0, (lt_ptr) 0} -}; - -/* This works around a problem in FreeBSD linker */ -#ifdef FREEBSD_WORKAROUND -static const void *lt_preloaded_setup() { - return lt_preloaded_symbols; -} -#endif - -#ifdef __cplusplus -} -#endif\ -" - fi - - pic_flag_for_symtable= - case $host in - # compiling the symbol table file with pic_flag works around - # a FreeBSD bug that causes programs to crash when -lm is - # linked before any other PIC object. But we must not use - # pic_flag when linking with -static. The problem exists in - # FreeBSD 2.2.6 and is fixed in FreeBSD 3.1. - *-*-freebsd2*|*-*-freebsd3.0*|*-*-freebsdelf3.0*) - case "$compile_command " in - *" -static "*) ;; - *) pic_flag_for_symtable=" $pic_flag -DFREEBSD_WORKAROUND";; - esac;; - *-*-hpux*) - case "$compile_command " in - *" -static "*) ;; - *) pic_flag_for_symtable=" $pic_flag";; - esac - esac - - # Now compile the dynamic symbol file. - $show "(cd $output_objdir && $LTCC $LTCFLAGS -c$no_builtin_flag$pic_flag_for_symtable \"$dlsyms\")" - $run eval '(cd $output_objdir && $LTCC $LTCFLAGS -c$no_builtin_flag$pic_flag_for_symtable "$dlsyms")' || exit $? - - # Clean up the generated files. - $show "$rm $output_objdir/$dlsyms $nlist ${nlist}S ${nlist}T" - $run $rm "$output_objdir/$dlsyms" "$nlist" "${nlist}S" "${nlist}T" - - # Transform the symbol file into the correct name. - case $host in - *cygwin* | *mingw* ) - if test -f "$output_objdir/${outputname}.def" ; then - compile_command=`$echo "X$compile_command" | $Xsed -e "s%@SYMFILE@%$output_objdir/${outputname}.def $output_objdir/${outputname}S.${objext}%"` - finalize_command=`$echo "X$finalize_command" | $Xsed -e "s%@SYMFILE@%$output_objdir/${outputname}.def $output_objdir/${outputname}S.${objext}%"` - else - compile_command=`$echo "X$compile_command" | $Xsed -e "s%@SYMFILE@%$output_objdir/${outputname}S.${objext}%"` - finalize_command=`$echo "X$finalize_command" | $Xsed -e "s%@SYMFILE@%$output_objdir/${outputname}S.${objext}%"` - fi - ;; - * ) - compile_command=`$echo "X$compile_command" | $Xsed -e "s%@SYMFILE@%$output_objdir/${outputname}S.${objext}%"` - finalize_command=`$echo "X$finalize_command" | $Xsed -e "s%@SYMFILE@%$output_objdir/${outputname}S.${objext}%"` - ;; - esac - ;; - *) - $echo "$modename: unknown suffix for \`$dlsyms'" 1>&2 - exit $EXIT_FAILURE - ;; - esac - else - # We keep going just in case the user didn't refer to - # lt_preloaded_symbols. The linker will fail if global_symbol_pipe - # really was required. - - # Nullify the symbol file. - compile_command=`$echo "X$compile_command" | $Xsed -e "s% @SYMFILE@%%"` - finalize_command=`$echo "X$finalize_command" | $Xsed -e "s% @SYMFILE@%%"` - fi - - if test "$need_relink" = no || test "$build_libtool_libs" != yes; then - # Replace the output file specification. - compile_command=`$echo "X$compile_command" | $Xsed -e 's%@OUTPUT@%'"$output"'%g'` - link_command="$compile_command$compile_rpath" - - # We have no uninstalled library dependencies, so finalize right now. - $show "$link_command" - $run eval "$link_command" - exit_status=$? - - # Delete the generated files. - if test -n "$dlsyms"; then - $show "$rm $output_objdir/${outputname}S.${objext}" - $run $rm "$output_objdir/${outputname}S.${objext}" - fi - - exit $exit_status - fi - - if test -n "$shlibpath_var"; then - # We should set the shlibpath_var - rpath= - for dir in $temp_rpath; do - case $dir in - [\\/]* | [A-Za-z]:[\\/]*) - # Absolute path. - rpath="$rpath$dir:" - ;; - *) - # Relative path: add a thisdir entry. - rpath="$rpath\$thisdir/$dir:" - ;; - esac - done - temp_rpath="$rpath" - fi - - if test -n "$compile_shlibpath$finalize_shlibpath"; then - compile_command="$shlibpath_var=\"$compile_shlibpath$finalize_shlibpath\$$shlibpath_var\" $compile_command" - fi - if test -n "$finalize_shlibpath"; then - finalize_command="$shlibpath_var=\"$finalize_shlibpath\$$shlibpath_var\" $finalize_command" - fi - - compile_var= - finalize_var= - if test -n "$runpath_var"; then - if test -n "$perm_rpath"; then - # We should set the runpath_var. - rpath= - for dir in $perm_rpath; do - rpath="$rpath$dir:" - done - compile_var="$runpath_var=\"$rpath\$$runpath_var\" " - fi - if test -n "$finalize_perm_rpath"; then - # We should set the runpath_var. - rpath= - for dir in $finalize_perm_rpath; do - rpath="$rpath$dir:" - done - finalize_var="$runpath_var=\"$rpath\$$runpath_var\" " - fi - fi - - if test "$no_install" = yes; then - # We don't need to create a wrapper script. - link_command="$compile_var$compile_command$compile_rpath" - # Replace the output file specification. - link_command=`$echo "X$link_command" | $Xsed -e 's%@OUTPUT@%'"$output"'%g'` - # Delete the old output file. - $run $rm $output - # Link the executable and exit - $show "$link_command" - $run eval "$link_command" || exit $? - exit $EXIT_SUCCESS - fi - - if test "$hardcode_action" = relink; then - # Fast installation is not supported - link_command="$compile_var$compile_command$compile_rpath" - relink_command="$finalize_var$finalize_command$finalize_rpath" - - $echo "$modename: warning: this platform does not like uninstalled shared libraries" 1>&2 - $echo "$modename: \`$output' will be relinked during installation" 1>&2 - else - if test "$fast_install" != no; then - link_command="$finalize_var$compile_command$finalize_rpath" - if test "$fast_install" = yes; then - relink_command=`$echo "X$compile_var$compile_command$compile_rpath" | $Xsed -e 's%@OUTPUT@%\$progdir/\$file%g'` - else - # fast_install is set to needless - relink_command= - fi - else - link_command="$compile_var$compile_command$compile_rpath" - relink_command="$finalize_var$finalize_command$finalize_rpath" - fi - fi - - # Replace the output file specification. - link_command=`$echo "X$link_command" | $Xsed -e 's%@OUTPUT@%'"$output_objdir/$outputname"'%g'` - - # Delete the old output files. - $run $rm $output $output_objdir/$outputname $output_objdir/lt-$outputname - - $show "$link_command" - $run eval "$link_command" || exit $? - - # Now create the wrapper script. - $show "creating $output" - - # Quote the relink command for shipping. - if test -n "$relink_command"; then - # Preserve any variables that may affect compiler behavior - for var in $variables_saved_for_relink; do - if eval test -z \"\${$var+set}\"; then - relink_command="{ test -z \"\${$var+set}\" || unset $var || { $var=; export $var; }; }; $relink_command" - elif eval var_value=\$$var; test -z "$var_value"; then - relink_command="$var=; export $var; $relink_command" - else - var_value=`$echo "X$var_value" | $Xsed -e "$sed_quote_subst"` - relink_command="$var=\"$var_value\"; export $var; $relink_command" - fi - done - relink_command="(cd `pwd`; $relink_command)" - relink_command=`$echo "X$relink_command" | $Xsed -e "$sed_quote_subst"` - fi - - # Quote $echo for shipping. - if test "X$echo" = "X$SHELL $progpath --fallback-echo"; then - case $progpath in - [\\/]* | [A-Za-z]:[\\/]*) qecho="$SHELL $progpath --fallback-echo";; - *) qecho="$SHELL `pwd`/$progpath --fallback-echo";; - esac - qecho=`$echo "X$qecho" | $Xsed -e "$sed_quote_subst"` - else - qecho=`$echo "X$echo" | $Xsed -e "$sed_quote_subst"` - fi - - # Only actually do things if our run command is non-null. - if test -z "$run"; then - # win32 will think the script is a binary if it has - # a .exe suffix, so we strip it off here. - case $output in - *.exe) output=`$echo $output|${SED} 's,.exe$,,'` ;; - esac - # test for cygwin because mv fails w/o .exe extensions - case $host in - *cygwin*) - exeext=.exe - outputname=`$echo $outputname|${SED} 's,.exe$,,'` ;; - *) exeext= ;; - esac - case $host in - *cygwin* | *mingw* ) - output_name=`basename $output` - output_path=`dirname $output` - cwrappersource="$output_path/$objdir/lt-$output_name.c" - cwrapper="$output_path/$output_name.exe" - $rm $cwrappersource $cwrapper - trap "$rm $cwrappersource $cwrapper; exit $EXIT_FAILURE" 1 2 15 - - cat > $cwrappersource <> $cwrappersource<<"EOF" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if defined(PATH_MAX) -# define LT_PATHMAX PATH_MAX -#elif defined(MAXPATHLEN) -# define LT_PATHMAX MAXPATHLEN -#else -# define LT_PATHMAX 1024 -#endif - -#ifndef DIR_SEPARATOR -# define DIR_SEPARATOR '/' -# define PATH_SEPARATOR ':' -#endif - -#if defined (_WIN32) || defined (__MSDOS__) || defined (__DJGPP__) || \ - defined (__OS2__) -# define HAVE_DOS_BASED_FILE_SYSTEM -# ifndef DIR_SEPARATOR_2 -# define DIR_SEPARATOR_2 '\\' -# endif -# ifndef PATH_SEPARATOR_2 -# define PATH_SEPARATOR_2 ';' -# endif -#endif - -#ifndef DIR_SEPARATOR_2 -# define IS_DIR_SEPARATOR(ch) ((ch) == DIR_SEPARATOR) -#else /* DIR_SEPARATOR_2 */ -# define IS_DIR_SEPARATOR(ch) \ - (((ch) == DIR_SEPARATOR) || ((ch) == DIR_SEPARATOR_2)) -#endif /* DIR_SEPARATOR_2 */ - -#ifndef PATH_SEPARATOR_2 -# define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR) -#else /* PATH_SEPARATOR_2 */ -# define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR_2) -#endif /* PATH_SEPARATOR_2 */ - -#define XMALLOC(type, num) ((type *) xmalloc ((num) * sizeof(type))) -#define XFREE(stale) do { \ - if (stale) { free ((void *) stale); stale = 0; } \ -} while (0) - -/* -DDEBUG is fairly common in CFLAGS. */ -#undef DEBUG -#if defined DEBUGWRAPPER -# define DEBUG(format, ...) fprintf(stderr, format, __VA_ARGS__) -#else -# define DEBUG(format, ...) -#endif - -const char *program_name = NULL; - -void * xmalloc (size_t num); -char * xstrdup (const char *string); -const char * base_name (const char *name); -char * find_executable(const char *wrapper); -int check_executable(const char *path); -char * strendzap(char *str, const char *pat); -void lt_fatal (const char *message, ...); - -int -main (int argc, char *argv[]) -{ - char **newargz; - int i; - - program_name = (char *) xstrdup (base_name (argv[0])); - DEBUG("(main) argv[0] : %s\n",argv[0]); - DEBUG("(main) program_name : %s\n",program_name); - newargz = XMALLOC(char *, argc+2); -EOF - - cat >> $cwrappersource <> $cwrappersource <<"EOF" - newargz[1] = find_executable(argv[0]); - if (newargz[1] == NULL) - lt_fatal("Couldn't find %s", argv[0]); - DEBUG("(main) found exe at : %s\n",newargz[1]); - /* we know the script has the same name, without the .exe */ - /* so make sure newargz[1] doesn't end in .exe */ - strendzap(newargz[1],".exe"); - for (i = 1; i < argc; i++) - newargz[i+1] = xstrdup(argv[i]); - newargz[argc+1] = NULL; - - for (i=0; i> $cwrappersource <> $cwrappersource <> $cwrappersource <<"EOF" - return 127; -} - -void * -xmalloc (size_t num) -{ - void * p = (void *) malloc (num); - if (!p) - lt_fatal ("Memory exhausted"); - - return p; -} - -char * -xstrdup (const char *string) -{ - return string ? strcpy ((char *) xmalloc (strlen (string) + 1), string) : NULL -; -} - -const char * -base_name (const char *name) -{ - const char *base; - -#if defined (HAVE_DOS_BASED_FILE_SYSTEM) - /* Skip over the disk name in MSDOS pathnames. */ - if (isalpha ((unsigned char)name[0]) && name[1] == ':') - name += 2; -#endif - - for (base = name; *name; name++) - if (IS_DIR_SEPARATOR (*name)) - base = name + 1; - return base; -} - -int -check_executable(const char * path) -{ - struct stat st; - - DEBUG("(check_executable) : %s\n", path ? (*path ? path : "EMPTY!") : "NULL!"); - if ((!path) || (!*path)) - return 0; - - if ((stat (path, &st) >= 0) && - ( - /* MinGW & native WIN32 do not support S_IXOTH or S_IXGRP */ -#if defined (S_IXOTH) - ((st.st_mode & S_IXOTH) == S_IXOTH) || -#endif -#if defined (S_IXGRP) - ((st.st_mode & S_IXGRP) == S_IXGRP) || -#endif - ((st.st_mode & S_IXUSR) == S_IXUSR)) - ) - return 1; - else - return 0; -} - -/* Searches for the full path of the wrapper. Returns - newly allocated full path name if found, NULL otherwise */ -char * -find_executable (const char* wrapper) -{ - int has_slash = 0; - const char* p; - const char* p_next; - /* static buffer for getcwd */ - char tmp[LT_PATHMAX + 1]; - int tmp_len; - char* concat_name; - - DEBUG("(find_executable) : %s\n", wrapper ? (*wrapper ? wrapper : "EMPTY!") : "NULL!"); - - if ((wrapper == NULL) || (*wrapper == '\0')) - return NULL; - - /* Absolute path? */ -#if defined (HAVE_DOS_BASED_FILE_SYSTEM) - if (isalpha ((unsigned char)wrapper[0]) && wrapper[1] == ':') - { - concat_name = xstrdup (wrapper); - if (check_executable(concat_name)) - return concat_name; - XFREE(concat_name); - } - else - { -#endif - if (IS_DIR_SEPARATOR (wrapper[0])) - { - concat_name = xstrdup (wrapper); - if (check_executable(concat_name)) - return concat_name; - XFREE(concat_name); - } -#if defined (HAVE_DOS_BASED_FILE_SYSTEM) - } -#endif - - for (p = wrapper; *p; p++) - if (*p == '/') - { - has_slash = 1; - break; - } - if (!has_slash) - { - /* no slashes; search PATH */ - const char* path = getenv ("PATH"); - if (path != NULL) - { - for (p = path; *p; p = p_next) - { - const char* q; - size_t p_len; - for (q = p; *q; q++) - if (IS_PATH_SEPARATOR(*q)) - break; - p_len = q - p; - p_next = (*q == '\0' ? q : q + 1); - if (p_len == 0) - { - /* empty path: current directory */ - if (getcwd (tmp, LT_PATHMAX) == NULL) - lt_fatal ("getcwd failed"); - tmp_len = strlen(tmp); - concat_name = XMALLOC(char, tmp_len + 1 + strlen(wrapper) + 1); - memcpy (concat_name, tmp, tmp_len); - concat_name[tmp_len] = '/'; - strcpy (concat_name + tmp_len + 1, wrapper); - } - else - { - concat_name = XMALLOC(char, p_len + 1 + strlen(wrapper) + 1); - memcpy (concat_name, p, p_len); - concat_name[p_len] = '/'; - strcpy (concat_name + p_len + 1, wrapper); - } - if (check_executable(concat_name)) - return concat_name; - XFREE(concat_name); - } - } - /* not found in PATH; assume curdir */ - } - /* Relative path | not found in path: prepend cwd */ - if (getcwd (tmp, LT_PATHMAX) == NULL) - lt_fatal ("getcwd failed"); - tmp_len = strlen(tmp); - concat_name = XMALLOC(char, tmp_len + 1 + strlen(wrapper) + 1); - memcpy (concat_name, tmp, tmp_len); - concat_name[tmp_len] = '/'; - strcpy (concat_name + tmp_len + 1, wrapper); - - if (check_executable(concat_name)) - return concat_name; - XFREE(concat_name); - return NULL; -} - -char * -strendzap(char *str, const char *pat) -{ - size_t len, patlen; - - assert(str != NULL); - assert(pat != NULL); - - len = strlen(str); - patlen = strlen(pat); - - if (patlen <= len) - { - str += len - patlen; - if (strcmp(str, pat) == 0) - *str = '\0'; - } - return str; -} - -static void -lt_error_core (int exit_status, const char * mode, - const char * message, va_list ap) -{ - fprintf (stderr, "%s: %s: ", program_name, mode); - vfprintf (stderr, message, ap); - fprintf (stderr, ".\n"); - - if (exit_status >= 0) - exit (exit_status); -} - -void -lt_fatal (const char *message, ...) -{ - va_list ap; - va_start (ap, message); - lt_error_core (EXIT_FAILURE, "FATAL", message, ap); - va_end (ap); -} -EOF - # we should really use a build-platform specific compiler - # here, but OTOH, the wrappers (shell script and this C one) - # are only useful if you want to execute the "real" binary. - # Since the "real" binary is built for $host, then this - # wrapper might as well be built for $host, too. - $run $LTCC $LTCFLAGS -s -o $cwrapper $cwrappersource - ;; - esac - $rm $output - trap "$rm $output; exit $EXIT_FAILURE" 1 2 15 - - $echo > $output "\ -#! $SHELL - -# $output - temporary wrapper script for $objdir/$outputname -# Generated by $PROGRAM - GNU $PACKAGE $VERSION$TIMESTAMP -# -# The $output program cannot be directly executed until all the libtool -# libraries that it depends on are installed. -# -# This wrapper script should never be moved out of the build directory. -# If it is, it will not operate correctly. - -# Sed substitution that helps us do robust quoting. It backslashifies -# metacharacters that are still active within double-quoted strings. -Xsed='${SED} -e 1s/^X//' -sed_quote_subst='$sed_quote_subst' - -# The HP-UX ksh and POSIX shell print the target directory to stdout -# if CDPATH is set. -(unset CDPATH) >/dev/null 2>&1 && unset CDPATH - -relink_command=\"$relink_command\" - -# This environment variable determines our operation mode. -if test \"\$libtool_install_magic\" = \"$magic\"; then - # install mode needs the following variable: - notinst_deplibs='$notinst_deplibs' -else - # When we are sourced in execute mode, \$file and \$echo are already set. - if test \"\$libtool_execute_magic\" != \"$magic\"; then - echo=\"$qecho\" - file=\"\$0\" - # Make sure echo works. - if test \"X\$1\" = X--no-reexec; then - # Discard the --no-reexec flag, and continue. - shift - elif test \"X\`(\$echo '\t') 2>/dev/null\`\" = 'X\t'; then - # Yippee, \$echo works! - : - else - # Restart under the correct shell, and then maybe \$echo will work. - exec $SHELL \"\$0\" --no-reexec \${1+\"\$@\"} - fi - fi\ -" - $echo >> $output "\ - - # Find the directory that this script lives in. - thisdir=\`\$echo \"X\$file\" | \$Xsed -e 's%/[^/]*$%%'\` - test \"x\$thisdir\" = \"x\$file\" && thisdir=. - - # Follow symbolic links until we get to the real thisdir. - file=\`ls -ld \"\$file\" | ${SED} -n 's/.*-> //p'\` - while test -n \"\$file\"; do - destdir=\`\$echo \"X\$file\" | \$Xsed -e 's%/[^/]*\$%%'\` - - # If there was a directory component, then change thisdir. - if test \"x\$destdir\" != \"x\$file\"; then - case \"\$destdir\" in - [\\\\/]* | [A-Za-z]:[\\\\/]*) thisdir=\"\$destdir\" ;; - *) thisdir=\"\$thisdir/\$destdir\" ;; - esac - fi - - file=\`\$echo \"X\$file\" | \$Xsed -e 's%^.*/%%'\` - file=\`ls -ld \"\$thisdir/\$file\" | ${SED} -n 's/.*-> //p'\` - done - - # Try to get the absolute directory name. - absdir=\`cd \"\$thisdir\" && pwd\` - test -n \"\$absdir\" && thisdir=\"\$absdir\" -" - - if test "$fast_install" = yes; then - $echo >> $output "\ - program=lt-'$outputname'$exeext - progdir=\"\$thisdir/$objdir\" - - if test ! -f \"\$progdir/\$program\" || \\ - { file=\`ls -1dt \"\$progdir/\$program\" \"\$progdir/../\$program\" 2>/dev/null | ${SED} 1q\`; \\ - test \"X\$file\" != \"X\$progdir/\$program\"; }; then - - file=\"\$\$-\$program\" - - if test ! -d \"\$progdir\"; then - $mkdir \"\$progdir\" - else - $rm \"\$progdir/\$file\" - fi" - - $echo >> $output "\ - - # relink executable if necessary - if test -n \"\$relink_command\"; then - if relink_command_output=\`eval \$relink_command 2>&1\`; then : - else - $echo \"\$relink_command_output\" >&2 - $rm \"\$progdir/\$file\" - exit $EXIT_FAILURE - fi - fi - - $mv \"\$progdir/\$file\" \"\$progdir/\$program\" 2>/dev/null || - { $rm \"\$progdir/\$program\"; - $mv \"\$progdir/\$file\" \"\$progdir/\$program\"; } - $rm \"\$progdir/\$file\" - fi" - else - $echo >> $output "\ - program='$outputname' - progdir=\"\$thisdir/$objdir\" -" - fi - - $echo >> $output "\ - - if test -f \"\$progdir/\$program\"; then" - - # Export our shlibpath_var if we have one. - if test "$shlibpath_overrides_runpath" = yes && test -n "$shlibpath_var" && test -n "$temp_rpath"; then - $echo >> $output "\ - # Add our own library path to $shlibpath_var - $shlibpath_var=\"$temp_rpath\$$shlibpath_var\" - - # Some systems cannot cope with colon-terminated $shlibpath_var - # The second colon is a workaround for a bug in BeOS R4 sed - $shlibpath_var=\`\$echo \"X\$$shlibpath_var\" | \$Xsed -e 's/::*\$//'\` - - export $shlibpath_var -" - fi - - # fixup the dll searchpath if we need to. - if test -n "$dllsearchpath"; then - $echo >> $output "\ - # Add the dll search path components to the executable PATH - PATH=$dllsearchpath:\$PATH -" - fi - - $echo >> $output "\ - if test \"\$libtool_execute_magic\" != \"$magic\"; then - # Run the actual program with our arguments. -" - case $host in - # Backslashes separate directories on plain windows - *-*-mingw | *-*-os2*) - $echo >> $output "\ - exec \"\$progdir\\\\\$program\" \${1+\"\$@\"} -" - ;; - - *) - $echo >> $output "\ - exec \"\$progdir/\$program\" \${1+\"\$@\"} -" - ;; - esac - $echo >> $output "\ - \$echo \"\$0: cannot exec \$program \${1+\"\$@\"}\" - exit $EXIT_FAILURE - fi - else - # The program doesn't exist. - \$echo \"\$0: error: \\\`\$progdir/\$program' does not exist\" 1>&2 - \$echo \"This script is just a wrapper for \$program.\" 1>&2 - $echo \"See the $PACKAGE documentation for more information.\" 1>&2 - exit $EXIT_FAILURE - fi -fi\ -" - chmod +x $output - fi - exit $EXIT_SUCCESS - ;; - esac - - # See if we need to build an old-fashioned archive. - for oldlib in $oldlibs; do - - if test "$build_libtool_libs" = convenience; then - oldobjs="$libobjs_save" - addlibs="$convenience" - build_libtool_libs=no - else - if test "$build_libtool_libs" = module; then - oldobjs="$libobjs_save" - build_libtool_libs=no - else - oldobjs="$old_deplibs $non_pic_objects" - fi - addlibs="$old_convenience" - fi - - if test -n "$addlibs"; then - gentop="$output_objdir/${outputname}x" - generated="$generated $gentop" - - func_extract_archives $gentop $addlibs - oldobjs="$oldobjs $func_extract_archives_result" - fi - - # Do each command in the archive commands. - if test -n "$old_archive_from_new_cmds" && test "$build_libtool_libs" = yes; then - cmds=$old_archive_from_new_cmds - else - # POSIX demands no paths to be encoded in archives. We have - # to avoid creating archives with duplicate basenames if we - # might have to extract them afterwards, e.g., when creating a - # static archive out of a convenience library, or when linking - # the entirety of a libtool archive into another (currently - # not supported by libtool). - if (for obj in $oldobjs - do - $echo "X$obj" | $Xsed -e 's%^.*/%%' - done | sort | sort -uc >/dev/null 2>&1); then - : - else - $echo "copying selected object files to avoid basename conflicts..." - - if test -z "$gentop"; then - gentop="$output_objdir/${outputname}x" - generated="$generated $gentop" - - $show "${rm}r $gentop" - $run ${rm}r "$gentop" - $show "$mkdir $gentop" - $run $mkdir "$gentop" - exit_status=$? - if test "$exit_status" -ne 0 && test ! -d "$gentop"; then - exit $exit_status - fi - fi - - save_oldobjs=$oldobjs - oldobjs= - counter=1 - for obj in $save_oldobjs - do - objbase=`$echo "X$obj" | $Xsed -e 's%^.*/%%'` - case " $oldobjs " in - " ") oldobjs=$obj ;; - *[\ /]"$objbase "*) - while :; do - # Make sure we don't pick an alternate name that also - # overlaps. - newobj=lt$counter-$objbase - counter=`expr $counter + 1` - case " $oldobjs " in - *[\ /]"$newobj "*) ;; - *) if test ! -f "$gentop/$newobj"; then break; fi ;; - esac - done - $show "ln $obj $gentop/$newobj || cp $obj $gentop/$newobj" - $run ln "$obj" "$gentop/$newobj" || - $run cp "$obj" "$gentop/$newobj" - oldobjs="$oldobjs $gentop/$newobj" - ;; - *) oldobjs="$oldobjs $obj" ;; - esac - done - fi - - eval cmds=\"$old_archive_cmds\" - - if len=`expr "X$cmds" : ".*"` && - test "$len" -le "$max_cmd_len" || test "$max_cmd_len" -le -1; then - cmds=$old_archive_cmds - else - # the command line is too long to link in one step, link in parts - $echo "using piecewise archive linking..." - save_RANLIB=$RANLIB - RANLIB=: - objlist= - concat_cmds= - save_oldobjs=$oldobjs - - # Is there a better way of finding the last object in the list? - for obj in $save_oldobjs - do - last_oldobj=$obj - done - for obj in $save_oldobjs - do - oldobjs="$objlist $obj" - objlist="$objlist $obj" - eval test_cmds=\"$old_archive_cmds\" - if len=`expr "X$test_cmds" : ".*" 2>/dev/null` && - test "$len" -le "$max_cmd_len"; then - : - else - # the above command should be used before it gets too long - oldobjs=$objlist - if test "$obj" = "$last_oldobj" ; then - RANLIB=$save_RANLIB - fi - test -z "$concat_cmds" || concat_cmds=$concat_cmds~ - eval concat_cmds=\"\${concat_cmds}$old_archive_cmds\" - objlist= - fi - done - RANLIB=$save_RANLIB - oldobjs=$objlist - if test "X$oldobjs" = "X" ; then - eval cmds=\"\$concat_cmds\" - else - eval cmds=\"\$concat_cmds~\$old_archive_cmds\" - fi - fi - fi - save_ifs="$IFS"; IFS='~' - for cmd in $cmds; do - eval cmd=\"$cmd\" - IFS="$save_ifs" - $show "$cmd" - $run eval "$cmd" || exit $? - done - IFS="$save_ifs" - done - - if test -n "$generated"; then - $show "${rm}r$generated" - $run ${rm}r$generated - fi - - # Now create the libtool archive. - case $output in - *.la) - old_library= - test "$build_old_libs" = yes && old_library="$libname.$libext" - $show "creating $output" - - # Preserve any variables that may affect compiler behavior - for var in $variables_saved_for_relink; do - if eval test -z \"\${$var+set}\"; then - relink_command="{ test -z \"\${$var+set}\" || unset $var || { $var=; export $var; }; }; $relink_command" - elif eval var_value=\$$var; test -z "$var_value"; then - relink_command="$var=; export $var; $relink_command" - else - var_value=`$echo "X$var_value" | $Xsed -e "$sed_quote_subst"` - relink_command="$var=\"$var_value\"; export $var; $relink_command" - fi - done - # Quote the link command for shipping. - relink_command="(cd `pwd`; $SHELL $progpath $preserve_args --mode=relink $libtool_args @inst_prefix_dir@)" - relink_command=`$echo "X$relink_command" | $Xsed -e "$sed_quote_subst"` - if test "$hardcode_automatic" = yes ; then - relink_command= - fi - - - # Only create the output if not a dry run. - if test -z "$run"; then - for installed in no yes; do - if test "$installed" = yes; then - if test -z "$install_libdir"; then - break - fi - output="$output_objdir/$outputname"i - # Replace all uninstalled libtool libraries with the installed ones - newdependency_libs= - for deplib in $dependency_libs; do - case $deplib in - *.la) - name=`$echo "X$deplib" | $Xsed -e 's%^.*/%%'` - eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $deplib` - if test -z "$libdir"; then - $echo "$modename: \`$deplib' is not a valid libtool archive" 1>&2 - exit $EXIT_FAILURE - fi - newdependency_libs="$newdependency_libs $libdir/$name" - ;; - *) newdependency_libs="$newdependency_libs $deplib" ;; - esac - done - dependency_libs="$newdependency_libs" - newdlfiles= - for lib in $dlfiles; do - name=`$echo "X$lib" | $Xsed -e 's%^.*/%%'` - eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $lib` - if test -z "$libdir"; then - $echo "$modename: \`$lib' is not a valid libtool archive" 1>&2 - exit $EXIT_FAILURE - fi - newdlfiles="$newdlfiles $libdir/$name" - done - dlfiles="$newdlfiles" - newdlprefiles= - for lib in $dlprefiles; do - name=`$echo "X$lib" | $Xsed -e 's%^.*/%%'` - eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $lib` - if test -z "$libdir"; then - $echo "$modename: \`$lib' is not a valid libtool archive" 1>&2 - exit $EXIT_FAILURE - fi - newdlprefiles="$newdlprefiles $libdir/$name" - done - dlprefiles="$newdlprefiles" - else - newdlfiles= - for lib in $dlfiles; do - case $lib in - [\\/]* | [A-Za-z]:[\\/]*) abs="$lib" ;; - *) abs=`pwd`"/$lib" ;; - esac - newdlfiles="$newdlfiles $abs" - done - dlfiles="$newdlfiles" - newdlprefiles= - for lib in $dlprefiles; do - case $lib in - [\\/]* | [A-Za-z]:[\\/]*) abs="$lib" ;; - *) abs=`pwd`"/$lib" ;; - esac - newdlprefiles="$newdlprefiles $abs" - done - dlprefiles="$newdlprefiles" - fi - $rm $output - # place dlname in correct position for cygwin - tdlname=$dlname - case $host,$output,$installed,$module,$dlname in - *cygwin*,*lai,yes,no,*.dll | *mingw*,*lai,yes,no,*.dll) tdlname=../bin/$dlname ;; - esac - $echo > $output "\ -# $outputname - a libtool library file -# Generated by $PROGRAM - GNU $PACKAGE $VERSION$TIMESTAMP -# -# Please DO NOT delete this file! -# It is necessary for linking the library. - -# The name that we can dlopen(3). -dlname='$tdlname' - -# Names of this library. -library_names='$library_names' - -# The name of the static archive. -old_library='$old_library' - -# Libraries that this one depends upon. -dependency_libs='$dependency_libs' - -# Version information for $libname. -current=$current -age=$age -revision=$revision - -# Is this an already installed library? -installed=$installed - -# Should we warn about portability when linking against -modules? -shouldnotlink=$module - -# Files to dlopen/dlpreopen -dlopen='$dlfiles' -dlpreopen='$dlprefiles' - -# Directory that this library needs to be installed in: -libdir='$install_libdir'" - if test "$installed" = no && test "$need_relink" = yes; then - $echo >> $output "\ -relink_command=\"$relink_command\"" - fi - done - fi - - # Do a symbolic link so that the libtool archive can be found in - # LD_LIBRARY_PATH before the program is installed. - $show "(cd $output_objdir && $rm $outputname && $LN_S ../$outputname $outputname)" - $run eval '(cd $output_objdir && $rm $outputname && $LN_S ../$outputname $outputname)' || exit $? - ;; - esac - exit $EXIT_SUCCESS - ;; - - # libtool install mode - install) - modename="$modename: install" - - # There may be an optional sh(1) argument at the beginning of - # install_prog (especially on Windows NT). - if test "$nonopt" = "$SHELL" || test "$nonopt" = /bin/sh || - # Allow the use of GNU shtool's install command. - $echo "X$nonopt" | grep shtool > /dev/null; then - # Aesthetically quote it. - arg=`$echo "X$nonopt" | $Xsed -e "$sed_quote_subst"` - case $arg in - *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") - arg="\"$arg\"" - ;; - esac - install_prog="$arg " - arg="$1" - shift - else - install_prog= - arg=$nonopt - fi - - # The real first argument should be the name of the installation program. - # Aesthetically quote it. - arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"` - case $arg in - *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") - arg="\"$arg\"" - ;; - esac - install_prog="$install_prog$arg" - - # We need to accept at least all the BSD install flags. - dest= - files= - opts= - prev= - install_type= - isdir=no - stripme= - for arg - do - if test -n "$dest"; then - files="$files $dest" - dest=$arg - continue - fi - - case $arg in - -d) isdir=yes ;; - -f) - case " $install_prog " in - *[\\\ /]cp\ *) ;; - *) prev=$arg ;; - esac - ;; - -g | -m | -o) prev=$arg ;; - -s) - stripme=" -s" - continue - ;; - -*) - ;; - *) - # If the previous option needed an argument, then skip it. - if test -n "$prev"; then - prev= - else - dest=$arg - continue - fi - ;; - esac - - # Aesthetically quote the argument. - arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"` - case $arg in - *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") - arg="\"$arg\"" - ;; - esac - install_prog="$install_prog $arg" - done - - if test -z "$install_prog"; then - $echo "$modename: you must specify an install program" 1>&2 - $echo "$help" 1>&2 - exit $EXIT_FAILURE - fi - - if test -n "$prev"; then - $echo "$modename: the \`$prev' option requires an argument" 1>&2 - $echo "$help" 1>&2 - exit $EXIT_FAILURE - fi - - if test -z "$files"; then - if test -z "$dest"; then - $echo "$modename: no file or destination specified" 1>&2 - else - $echo "$modename: you must specify a destination" 1>&2 - fi - $echo "$help" 1>&2 - exit $EXIT_FAILURE - fi - - # Strip any trailing slash from the destination. - dest=`$echo "X$dest" | $Xsed -e 's%/$%%'` - - # Check to see that the destination is a directory. - test -d "$dest" && isdir=yes - if test "$isdir" = yes; then - destdir="$dest" - destname= - else - destdir=`$echo "X$dest" | $Xsed -e 's%/[^/]*$%%'` - test "X$destdir" = "X$dest" && destdir=. - destname=`$echo "X$dest" | $Xsed -e 's%^.*/%%'` - - # Not a directory, so check to see that there is only one file specified. - set dummy $files - if test "$#" -gt 2; then - $echo "$modename: \`$dest' is not a directory" 1>&2 - $echo "$help" 1>&2 - exit $EXIT_FAILURE - fi - fi - case $destdir in - [\\/]* | [A-Za-z]:[\\/]*) ;; - *) - for file in $files; do - case $file in - *.lo) ;; - *) - $echo "$modename: \`$destdir' must be an absolute directory name" 1>&2 - $echo "$help" 1>&2 - exit $EXIT_FAILURE - ;; - esac - done - ;; - esac - - # This variable tells wrapper scripts just to set variables rather - # than running their programs. - libtool_install_magic="$magic" - - staticlibs= - future_libdirs= - current_libdirs= - for file in $files; do - - # Do each installation. - case $file in - *.$libext) - # Do the static libraries later. - staticlibs="$staticlibs $file" - ;; - - *.la) - # Check to see that this really is a libtool archive. - if (${SED} -e '2q' $file | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then : - else - $echo "$modename: \`$file' is not a valid libtool archive" 1>&2 - $echo "$help" 1>&2 - exit $EXIT_FAILURE - fi - - library_names= - old_library= - relink_command= - # If there is no directory component, then add one. - case $file in - */* | *\\*) . $file ;; - *) . ./$file ;; - esac - - # Add the libdir to current_libdirs if it is the destination. - if test "X$destdir" = "X$libdir"; then - case "$current_libdirs " in - *" $libdir "*) ;; - *) current_libdirs="$current_libdirs $libdir" ;; - esac - else - # Note the libdir as a future libdir. - case "$future_libdirs " in - *" $libdir "*) ;; - *) future_libdirs="$future_libdirs $libdir" ;; - esac - fi - - dir=`$echo "X$file" | $Xsed -e 's%/[^/]*$%%'`/ - test "X$dir" = "X$file/" && dir= - dir="$dir$objdir" - - if test -n "$relink_command"; then - # Determine the prefix the user has applied to our future dir. - inst_prefix_dir=`$echo "$destdir" | $SED "s%$libdir\$%%"` - - # Don't allow the user to place us outside of our expected - # location b/c this prevents finding dependent libraries that - # are installed to the same prefix. - # At present, this check doesn't affect windows .dll's that - # are installed into $libdir/../bin (currently, that works fine) - # but it's something to keep an eye on. - if test "$inst_prefix_dir" = "$destdir"; then - $echo "$modename: error: cannot install \`$file' to a directory not ending in $libdir" 1>&2 - exit $EXIT_FAILURE - fi - - if test -n "$inst_prefix_dir"; then - # Stick the inst_prefix_dir data into the link command. - relink_command=`$echo "$relink_command" | $SED "s%@inst_prefix_dir@%-inst-prefix-dir $inst_prefix_dir%"` - else - relink_command=`$echo "$relink_command" | $SED "s%@inst_prefix_dir@%%"` - fi - - $echo "$modename: warning: relinking \`$file'" 1>&2 - $show "$relink_command" - if $run eval "$relink_command"; then : - else - $echo "$modename: error: relink \`$file' with the above command before installing it" 1>&2 - exit $EXIT_FAILURE - fi - fi - - # See the names of the shared library. - set dummy $library_names - if test -n "$2"; then - realname="$2" - shift - shift - - srcname="$realname" - test -n "$relink_command" && srcname="$realname"T - - # Install the shared library and build the symlinks. - $show "$install_prog $dir/$srcname $destdir/$realname" - $run eval "$install_prog $dir/$srcname $destdir/$realname" || exit $? - if test -n "$stripme" && test -n "$striplib"; then - $show "$striplib $destdir/$realname" - $run eval "$striplib $destdir/$realname" || exit $? - fi - - if test "$#" -gt 0; then - # Delete the old symlinks, and create new ones. - # Try `ln -sf' first, because the `ln' binary might depend on - # the symlink we replace! Solaris /bin/ln does not understand -f, - # so we also need to try rm && ln -s. - for linkname - do - if test "$linkname" != "$realname"; then - $show "(cd $destdir && { $LN_S -f $realname $linkname || { $rm $linkname && $LN_S $realname $linkname; }; })" - $run eval "(cd $destdir && { $LN_S -f $realname $linkname || { $rm $linkname && $LN_S $realname $linkname; }; })" - fi - done - fi - - # Do each command in the postinstall commands. - lib="$destdir/$realname" - cmds=$postinstall_cmds - save_ifs="$IFS"; IFS='~' - for cmd in $cmds; do - IFS="$save_ifs" - eval cmd=\"$cmd\" - $show "$cmd" - $run eval "$cmd" || { - lt_exit=$? - - # Restore the uninstalled library and exit - if test "$mode" = relink; then - $run eval '(cd $output_objdir && $rm ${realname}T && $mv ${realname}U $realname)' - fi - - exit $lt_exit - } - done - IFS="$save_ifs" - fi - - # Install the pseudo-library for information purposes. - name=`$echo "X$file" | $Xsed -e 's%^.*/%%'` - instname="$dir/$name"i - $show "$install_prog $instname $destdir/$name" - $run eval "$install_prog $instname $destdir/$name" || exit $? - - # Maybe install the static library, too. - test -n "$old_library" && staticlibs="$staticlibs $dir/$old_library" - ;; - - *.lo) - # Install (i.e. copy) a libtool object. - - # Figure out destination file name, if it wasn't already specified. - if test -n "$destname"; then - destfile="$destdir/$destname" - else - destfile=`$echo "X$file" | $Xsed -e 's%^.*/%%'` - destfile="$destdir/$destfile" - fi - - # Deduce the name of the destination old-style object file. - case $destfile in - *.lo) - staticdest=`$echo "X$destfile" | $Xsed -e "$lo2o"` - ;; - *.$objext) - staticdest="$destfile" - destfile= - ;; - *) - $echo "$modename: cannot copy a libtool object to \`$destfile'" 1>&2 - $echo "$help" 1>&2 - exit $EXIT_FAILURE - ;; - esac - - # Install the libtool object if requested. - if test -n "$destfile"; then - $show "$install_prog $file $destfile" - $run eval "$install_prog $file $destfile" || exit $? - fi - - # Install the old object if enabled. - if test "$build_old_libs" = yes; then - # Deduce the name of the old-style object file. - staticobj=`$echo "X$file" | $Xsed -e "$lo2o"` - - $show "$install_prog $staticobj $staticdest" - $run eval "$install_prog \$staticobj \$staticdest" || exit $? - fi - exit $EXIT_SUCCESS - ;; - - *) - # Figure out destination file name, if it wasn't already specified. - if test -n "$destname"; then - destfile="$destdir/$destname" - else - destfile=`$echo "X$file" | $Xsed -e 's%^.*/%%'` - destfile="$destdir/$destfile" - fi - - # If the file is missing, and there is a .exe on the end, strip it - # because it is most likely a libtool script we actually want to - # install - stripped_ext="" - case $file in - *.exe) - if test ! -f "$file"; then - file=`$echo $file|${SED} 's,.exe$,,'` - stripped_ext=".exe" - fi - ;; - esac - - # Do a test to see if this is really a libtool program. - case $host in - *cygwin*|*mingw*) - wrapper=`$echo $file | ${SED} -e 's,.exe$,,'` - ;; - *) - wrapper=$file - ;; - esac - if (${SED} -e '4q' $wrapper | grep "^# Generated by .*$PACKAGE")>/dev/null 2>&1; then - notinst_deplibs= - relink_command= - - # Note that it is not necessary on cygwin/mingw to append a dot to - # foo even if both foo and FILE.exe exist: automatic-append-.exe - # behavior happens only for exec(3), not for open(2)! Also, sourcing - # `FILE.' does not work on cygwin managed mounts. - # - # If there is no directory component, then add one. - case $wrapper in - */* | *\\*) . ${wrapper} ;; - *) . ./${wrapper} ;; - esac - - # Check the variables that should have been set. - if test -z "$notinst_deplibs"; then - $echo "$modename: invalid libtool wrapper script \`$wrapper'" 1>&2 - exit $EXIT_FAILURE - fi - - finalize=yes - for lib in $notinst_deplibs; do - # Check to see that each library is installed. - libdir= - if test -f "$lib"; then - # If there is no directory component, then add one. - case $lib in - */* | *\\*) . $lib ;; - *) . ./$lib ;; - esac - fi - libfile="$libdir/"`$echo "X$lib" | $Xsed -e 's%^.*/%%g'` ### testsuite: skip nested quoting test - if test -n "$libdir" && test ! -f "$libfile"; then - $echo "$modename: warning: \`$lib' has not been installed in \`$libdir'" 1>&2 - finalize=no - fi - done - - relink_command= - # Note that it is not necessary on cygwin/mingw to append a dot to - # foo even if both foo and FILE.exe exist: automatic-append-.exe - # behavior happens only for exec(3), not for open(2)! Also, sourcing - # `FILE.' does not work on cygwin managed mounts. - # - # If there is no directory component, then add one. - case $wrapper in - */* | *\\*) . ${wrapper} ;; - *) . ./${wrapper} ;; - esac - - outputname= - if test "$fast_install" = no && test -n "$relink_command"; then - if test "$finalize" = yes && test -z "$run"; then - tmpdir=`func_mktempdir` - file=`$echo "X$file$stripped_ext" | $Xsed -e 's%^.*/%%'` - outputname="$tmpdir/$file" - # Replace the output file specification. - relink_command=`$echo "X$relink_command" | $Xsed -e 's%@OUTPUT@%'"$outputname"'%g'` - - $show "$relink_command" - if $run eval "$relink_command"; then : - else - $echo "$modename: error: relink \`$file' with the above command before installing it" 1>&2 - ${rm}r "$tmpdir" - continue - fi - file="$outputname" - else - $echo "$modename: warning: cannot relink \`$file'" 1>&2 - fi - else - # Install the binary that we compiled earlier. - file=`$echo "X$file$stripped_ext" | $Xsed -e "s%\([^/]*\)$%$objdir/\1%"` - fi - fi - - # remove .exe since cygwin /usr/bin/install will append another - # one anyway - case $install_prog,$host in - */usr/bin/install*,*cygwin*) - case $file:$destfile in - *.exe:*.exe) - # this is ok - ;; - *.exe:*) - destfile=$destfile.exe - ;; - *:*.exe) - destfile=`$echo $destfile | ${SED} -e 's,.exe$,,'` - ;; - esac - ;; - esac - $show "$install_prog$stripme $file $destfile" - $run eval "$install_prog\$stripme \$file \$destfile" || exit $? - test -n "$outputname" && ${rm}r "$tmpdir" - ;; - esac - done - - for file in $staticlibs; do - name=`$echo "X$file" | $Xsed -e 's%^.*/%%'` - - # Set up the ranlib parameters. - oldlib="$destdir/$name" - - $show "$install_prog $file $oldlib" - $run eval "$install_prog \$file \$oldlib" || exit $? - - if test -n "$stripme" && test -n "$old_striplib"; then - $show "$old_striplib $oldlib" - $run eval "$old_striplib $oldlib" || exit $? - fi - - # Do each command in the postinstall commands. - cmds=$old_postinstall_cmds - save_ifs="$IFS"; IFS='~' - for cmd in $cmds; do - IFS="$save_ifs" - eval cmd=\"$cmd\" - $show "$cmd" - $run eval "$cmd" || exit $? - done - IFS="$save_ifs" - done - - if test -n "$future_libdirs"; then - $echo "$modename: warning: remember to run \`$progname --finish$future_libdirs'" 1>&2 - fi - - if test -n "$current_libdirs"; then - # Maybe just do a dry run. - test -n "$run" && current_libdirs=" -n$current_libdirs" - exec_cmd='$SHELL $progpath $preserve_args --finish$current_libdirs' - else - exit $EXIT_SUCCESS - fi - ;; - - # libtool finish mode - finish) - modename="$modename: finish" - libdirs="$nonopt" - admincmds= - - if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then - for dir - do - libdirs="$libdirs $dir" - done - - for libdir in $libdirs; do - if test -n "$finish_cmds"; then - # Do each command in the finish commands. - cmds=$finish_cmds - save_ifs="$IFS"; IFS='~' - for cmd in $cmds; do - IFS="$save_ifs" - eval cmd=\"$cmd\" - $show "$cmd" - $run eval "$cmd" || admincmds="$admincmds - $cmd" - done - IFS="$save_ifs" - fi - if test -n "$finish_eval"; then - # Do the single finish_eval. - eval cmds=\"$finish_eval\" - $run eval "$cmds" || admincmds="$admincmds - $cmds" - fi - done - fi - - # Exit here if they wanted silent mode. - test "$show" = : && exit $EXIT_SUCCESS - - $echo "X----------------------------------------------------------------------" | $Xsed - $echo "Libraries have been installed in:" - for libdir in $libdirs; do - $echo " $libdir" - done - $echo - $echo "If you ever happen to want to link against installed libraries" - $echo "in a given directory, LIBDIR, you must either use libtool, and" - $echo "specify the full pathname of the library, or use the \`-LLIBDIR'" - $echo "flag during linking and do at least one of the following:" - if test -n "$shlibpath_var"; then - $echo " - add LIBDIR to the \`$shlibpath_var' environment variable" - $echo " during execution" - fi - if test -n "$runpath_var"; then - $echo " - add LIBDIR to the \`$runpath_var' environment variable" - $echo " during linking" - fi - if test -n "$hardcode_libdir_flag_spec"; then - libdir=LIBDIR - eval flag=\"$hardcode_libdir_flag_spec\" - - $echo " - use the \`$flag' linker flag" - fi - if test -n "$admincmds"; then - $echo " - have your system administrator run these commands:$admincmds" - fi - if test -f /etc/ld.so.conf; then - $echo " - have your system administrator add LIBDIR to \`/etc/ld.so.conf'" - fi - $echo - $echo "See any operating system documentation about shared libraries for" - $echo "more information, such as the ld(1) and ld.so(8) manual pages." - $echo "X----------------------------------------------------------------------" | $Xsed - exit $EXIT_SUCCESS - ;; - - # libtool execute mode - execute) - modename="$modename: execute" - - # The first argument is the command name. - cmd="$nonopt" - if test -z "$cmd"; then - $echo "$modename: you must specify a COMMAND" 1>&2 - $echo "$help" - exit $EXIT_FAILURE - fi - - # Handle -dlopen flags immediately. - for file in $execute_dlfiles; do - if test ! -f "$file"; then - $echo "$modename: \`$file' is not a file" 1>&2 - $echo "$help" 1>&2 - exit $EXIT_FAILURE - fi - - dir= - case $file in - *.la) - # Check to see that this really is a libtool archive. - if (${SED} -e '2q' $file | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then : - else - $echo "$modename: \`$lib' is not a valid libtool archive" 1>&2 - $echo "$help" 1>&2 - exit $EXIT_FAILURE - fi - - # Read the libtool library. - dlname= - library_names= - - # If there is no directory component, then add one. - case $file in - */* | *\\*) . $file ;; - *) . ./$file ;; - esac - - # Skip this library if it cannot be dlopened. - if test -z "$dlname"; then - # Warn if it was a shared library. - test -n "$library_names" && $echo "$modename: warning: \`$file' was not linked with \`-export-dynamic'" - continue - fi - - dir=`$echo "X$file" | $Xsed -e 's%/[^/]*$%%'` - test "X$dir" = "X$file" && dir=. - - if test -f "$dir/$objdir/$dlname"; then - dir="$dir/$objdir" - else - $echo "$modename: cannot find \`$dlname' in \`$dir' or \`$dir/$objdir'" 1>&2 - exit $EXIT_FAILURE - fi - ;; - - *.lo) - # Just add the directory containing the .lo file. - dir=`$echo "X$file" | $Xsed -e 's%/[^/]*$%%'` - test "X$dir" = "X$file" && dir=. - ;; - - *) - $echo "$modename: warning \`-dlopen' is ignored for non-libtool libraries and objects" 1>&2 - continue - ;; - esac - - # Get the absolute pathname. - absdir=`cd "$dir" && pwd` - test -n "$absdir" && dir="$absdir" - - # Now add the directory to shlibpath_var. - if eval "test -z \"\$$shlibpath_var\""; then - eval "$shlibpath_var=\"\$dir\"" - else - eval "$shlibpath_var=\"\$dir:\$$shlibpath_var\"" - fi - done - - # This variable tells wrapper scripts just to set shlibpath_var - # rather than running their programs. - libtool_execute_magic="$magic" - - # Check if any of the arguments is a wrapper script. - args= - for file - do - case $file in - -*) ;; - *) - # Do a test to see if this is really a libtool program. - if (${SED} -e '4q' $file | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then - # If there is no directory component, then add one. - case $file in - */* | *\\*) . $file ;; - *) . ./$file ;; - esac - - # Transform arg to wrapped name. - file="$progdir/$program" - fi - ;; - esac - # Quote arguments (to preserve shell metacharacters). - file=`$echo "X$file" | $Xsed -e "$sed_quote_subst"` - args="$args \"$file\"" - done - - if test -z "$run"; then - if test -n "$shlibpath_var"; then - # Export the shlibpath_var. - eval "export $shlibpath_var" - fi - - # Restore saved environment variables - if test "${save_LC_ALL+set}" = set; then - LC_ALL="$save_LC_ALL"; export LC_ALL - fi - if test "${save_LANG+set}" = set; then - LANG="$save_LANG"; export LANG - fi - - # Now prepare to actually exec the command. - exec_cmd="\$cmd$args" - else - # Display what would be done. - if test -n "$shlibpath_var"; then - eval "\$echo \"\$shlibpath_var=\$$shlibpath_var\"" - $echo "export $shlibpath_var" - fi - $echo "$cmd$args" - exit $EXIT_SUCCESS - fi - ;; - - # libtool clean and uninstall mode - clean | uninstall) - modename="$modename: $mode" - rm="$nonopt" - files= - rmforce= - exit_status=0 - - # This variable tells wrapper scripts just to set variables rather - # than running their programs. - libtool_install_magic="$magic" - - for arg - do - case $arg in - -f) rm="$rm $arg"; rmforce=yes ;; - -*) rm="$rm $arg" ;; - *) files="$files $arg" ;; - esac - done - - if test -z "$rm"; then - $echo "$modename: you must specify an RM program" 1>&2 - $echo "$help" 1>&2 - exit $EXIT_FAILURE - fi - - rmdirs= - - origobjdir="$objdir" - for file in $files; do - dir=`$echo "X$file" | $Xsed -e 's%/[^/]*$%%'` - if test "X$dir" = "X$file"; then - dir=. - objdir="$origobjdir" - else - objdir="$dir/$origobjdir" - fi - name=`$echo "X$file" | $Xsed -e 's%^.*/%%'` - test "$mode" = uninstall && objdir="$dir" - - # Remember objdir for removal later, being careful to avoid duplicates - if test "$mode" = clean; then - case " $rmdirs " in - *" $objdir "*) ;; - *) rmdirs="$rmdirs $objdir" ;; - esac - fi - - # Don't error if the file doesn't exist and rm -f was used. - if (test -L "$file") >/dev/null 2>&1 \ - || (test -h "$file") >/dev/null 2>&1 \ - || test -f "$file"; then - : - elif test -d "$file"; then - exit_status=1 - continue - elif test "$rmforce" = yes; then - continue - fi - - rmfiles="$file" - - case $name in - *.la) - # Possibly a libtool archive, so verify it. - if (${SED} -e '2q' $file | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then - . $dir/$name - - # Delete the libtool libraries and symlinks. - for n in $library_names; do - rmfiles="$rmfiles $objdir/$n" - done - test -n "$old_library" && rmfiles="$rmfiles $objdir/$old_library" - - case "$mode" in - clean) - case " $library_names " in - # " " in the beginning catches empty $dlname - *" $dlname "*) ;; - *) rmfiles="$rmfiles $objdir/$dlname" ;; - esac - test -n "$libdir" && rmfiles="$rmfiles $objdir/$name $objdir/${name}i" - ;; - uninstall) - if test -n "$library_names"; then - # Do each command in the postuninstall commands. - cmds=$postuninstall_cmds - save_ifs="$IFS"; IFS='~' - for cmd in $cmds; do - IFS="$save_ifs" - eval cmd=\"$cmd\" - $show "$cmd" - $run eval "$cmd" - if test "$?" -ne 0 && test "$rmforce" != yes; then - exit_status=1 - fi - done - IFS="$save_ifs" - fi - - if test -n "$old_library"; then - # Do each command in the old_postuninstall commands. - cmds=$old_postuninstall_cmds - save_ifs="$IFS"; IFS='~' - for cmd in $cmds; do - IFS="$save_ifs" - eval cmd=\"$cmd\" - $show "$cmd" - $run eval "$cmd" - if test "$?" -ne 0 && test "$rmforce" != yes; then - exit_status=1 - fi - done - IFS="$save_ifs" - fi - # FIXME: should reinstall the best remaining shared library. - ;; - esac - fi - ;; - - *.lo) - # Possibly a libtool object, so verify it. - if (${SED} -e '2q' $file | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then - - # Read the .lo file - . $dir/$name - - # Add PIC object to the list of files to remove. - if test -n "$pic_object" \ - && test "$pic_object" != none; then - rmfiles="$rmfiles $dir/$pic_object" - fi - - # Add non-PIC object to the list of files to remove. - if test -n "$non_pic_object" \ - && test "$non_pic_object" != none; then - rmfiles="$rmfiles $dir/$non_pic_object" - fi - fi - ;; - - *) - if test "$mode" = clean ; then - noexename=$name - case $file in - *.exe) - file=`$echo $file|${SED} 's,.exe$,,'` - noexename=`$echo $name|${SED} 's,.exe$,,'` - # $file with .exe has already been added to rmfiles, - # add $file without .exe - rmfiles="$rmfiles $file" - ;; - esac - # Do a test to see if this is a libtool program. - if (${SED} -e '4q' $file | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then - relink_command= - . $dir/$noexename - - # note $name still contains .exe if it was in $file originally - # as does the version of $file that was added into $rmfiles - rmfiles="$rmfiles $objdir/$name $objdir/${name}S.${objext}" - if test "$fast_install" = yes && test -n "$relink_command"; then - rmfiles="$rmfiles $objdir/lt-$name" - fi - if test "X$noexename" != "X$name" ; then - rmfiles="$rmfiles $objdir/lt-${noexename}.c" - fi - fi - fi - ;; - esac - $show "$rm $rmfiles" - $run $rm $rmfiles || exit_status=1 - done - objdir="$origobjdir" - - # Try to remove the ${objdir}s in the directories where we deleted files - for dir in $rmdirs; do - if test -d "$dir"; then - $show "rmdir $dir" - $run rmdir $dir >/dev/null 2>&1 - fi - done - - exit $exit_status - ;; - - "") - $echo "$modename: you must specify a MODE" 1>&2 - $echo "$generic_help" 1>&2 - exit $EXIT_FAILURE - ;; - esac - - if test -z "$exec_cmd"; then - $echo "$modename: invalid operation mode \`$mode'" 1>&2 - $echo "$generic_help" 1>&2 - exit $EXIT_FAILURE - fi -fi # test -z "$show_help" - -if test -n "$exec_cmd"; then - eval exec $exec_cmd - exit $EXIT_FAILURE -fi - -# We need to display help for each of the modes. -case $mode in -"") $echo \ -"Usage: $modename [OPTION]... [MODE-ARG]... - -Provide generalized library-building support services. - - --config show all configuration variables - --debug enable verbose shell tracing --n, --dry-run display commands without modifying any files - --features display basic configuration information and exit - --finish same as \`--mode=finish' - --help display this help message and exit - --mode=MODE use operation mode MODE [default=inferred from MODE-ARGS] - --quiet same as \`--silent' - --silent don't print informational messages - --tag=TAG use configuration variables from tag TAG - --version print version information - -MODE must be one of the following: - - clean remove files from the build directory - compile compile a source file into a libtool object - execute automatically set library path, then run a program - finish complete the installation of libtool libraries - install install libraries or executables - link create a library or an executable - uninstall remove libraries from an installed directory - -MODE-ARGS vary depending on the MODE. Try \`$modename --help --mode=MODE' for -a more detailed description of MODE. - -Report bugs to ." - exit $EXIT_SUCCESS - ;; - -clean) - $echo \ -"Usage: $modename [OPTION]... --mode=clean RM [RM-OPTION]... FILE... - -Remove files from the build directory. - -RM is the name of the program to use to delete files associated with each FILE -(typically \`/bin/rm'). RM-OPTIONS are options (such as \`-f') to be passed -to RM. - -If FILE is a libtool library, object or program, all the files associated -with it are deleted. Otherwise, only FILE itself is deleted using RM." - ;; - -compile) - $echo \ -"Usage: $modename [OPTION]... --mode=compile COMPILE-COMMAND... SOURCEFILE - -Compile a source file into a libtool library object. - -This mode accepts the following additional options: - - -o OUTPUT-FILE set the output file name to OUTPUT-FILE - -prefer-pic try to building PIC objects only - -prefer-non-pic try to building non-PIC objects only - -static always build a \`.o' file suitable for static linking - -COMPILE-COMMAND is a command to be used in creating a \`standard' object file -from the given SOURCEFILE. - -The output file name is determined by removing the directory component from -SOURCEFILE, then substituting the C source code suffix \`.c' with the -library object suffix, \`.lo'." - ;; - -execute) - $echo \ -"Usage: $modename [OPTION]... --mode=execute COMMAND [ARGS]... - -Automatically set library path, then run a program. - -This mode accepts the following additional options: - - -dlopen FILE add the directory containing FILE to the library path - -This mode sets the library path environment variable according to \`-dlopen' -flags. - -If any of the ARGS are libtool executable wrappers, then they are translated -into their corresponding uninstalled binary, and any of their required library -directories are added to the library path. - -Then, COMMAND is executed, with ARGS as arguments." - ;; - -finish) - $echo \ -"Usage: $modename [OPTION]... --mode=finish [LIBDIR]... - -Complete the installation of libtool libraries. - -Each LIBDIR is a directory that contains libtool libraries. - -The commands that this mode executes may require superuser privileges. Use -the \`--dry-run' option if you just want to see what would be executed." - ;; - -install) - $echo \ -"Usage: $modename [OPTION]... --mode=install INSTALL-COMMAND... - -Install executables or libraries. - -INSTALL-COMMAND is the installation command. The first component should be -either the \`install' or \`cp' program. - -The rest of the components are interpreted as arguments to that command (only -BSD-compatible install options are recognized)." - ;; - -link) - $echo \ -"Usage: $modename [OPTION]... --mode=link LINK-COMMAND... - -Link object files or libraries together to form another library, or to -create an executable program. - -LINK-COMMAND is a command using the C compiler that you would use to create -a program from several object files. - -The following components of LINK-COMMAND are treated specially: - - -all-static do not do any dynamic linking at all - -avoid-version do not add a version suffix if possible - -dlopen FILE \`-dlpreopen' FILE if it cannot be dlopened at runtime - -dlpreopen FILE link in FILE and add its symbols to lt_preloaded_symbols - -export-dynamic allow symbols from OUTPUT-FILE to be resolved with dlsym(3) - -export-symbols SYMFILE - try to export only the symbols listed in SYMFILE - -export-symbols-regex REGEX - try to export only the symbols matching REGEX - -LLIBDIR search LIBDIR for required installed libraries - -lNAME OUTPUT-FILE requires the installed library libNAME - -module build a library that can dlopened - -no-fast-install disable the fast-install mode - -no-install link a not-installable executable - -no-undefined declare that a library does not refer to external symbols - -o OUTPUT-FILE create OUTPUT-FILE from the specified objects - -objectlist FILE Use a list of object files found in FILE to specify objects - -precious-files-regex REGEX - don't remove output files matching REGEX - -release RELEASE specify package release information - -rpath LIBDIR the created library will eventually be installed in LIBDIR - -R[ ]LIBDIR add LIBDIR to the runtime path of programs and libraries - -static do not do any dynamic linking of libtool libraries - -version-info CURRENT[:REVISION[:AGE]] - specify library version info [each variable defaults to 0] - -All other options (arguments beginning with \`-') are ignored. - -Every other argument is treated as a filename. Files ending in \`.la' are -treated as uninstalled libtool libraries, other files are standard or library -object files. - -If the OUTPUT-FILE ends in \`.la', then a libtool library is created, -only library objects (\`.lo' files) may be specified, and \`-rpath' is -required, except when creating a convenience library. - -If OUTPUT-FILE ends in \`.a' or \`.lib', then a standard library is created -using \`ar' and \`ranlib', or on Windows using \`lib'. - -If OUTPUT-FILE ends in \`.lo' or \`.${objext}', then a reloadable object file -is created, otherwise an executable program is created." - ;; - -uninstall) - $echo \ -"Usage: $modename [OPTION]... --mode=uninstall RM [RM-OPTION]... FILE... - -Remove libraries from an installation directory. - -RM is the name of the program to use to delete files associated with each FILE -(typically \`/bin/rm'). RM-OPTIONS are options (such as \`-f') to be passed -to RM. - -If FILE is a libtool library, all the files associated with it are deleted. -Otherwise, only FILE itself is deleted using RM." - ;; - -*) - $echo "$modename: invalid operation mode \`$mode'" 1>&2 - $echo "$help" 1>&2 - exit $EXIT_FAILURE - ;; -esac - -$echo -$echo "Try \`$modename --help' for more information about other modes." - -exit $? - -# The TAGs below are defined such that we never get into a situation -# in which we disable both kinds of libraries. Given conflicting -# choices, we go for a static library, that is the most portable, -# since we can't tell whether shared libraries were disabled because -# the user asked for that or because the platform doesn't support -# them. This is particularly important on AIX, because we don't -# support having both static and shared libraries enabled at the same -# time on that platform, so we default to a shared-only configuration. -# If a disable-shared tag is given, we'll fallback to a static-only -# configuration. But we'll never go from static-only to shared-only. - -# ### BEGIN LIBTOOL TAG CONFIG: disable-shared -disable_libs=shared -# ### END LIBTOOL TAG CONFIG: disable-shared - -# ### BEGIN LIBTOOL TAG CONFIG: disable-static -disable_libs=static -# ### END LIBTOOL TAG CONFIG: disable-static - -# Local Variables: -# mode:shell-script -# sh-indentation:2 -# End: diff --git a/cextern/expat/conftools/mkinstalldirs b/cextern/expat/conftools/mkinstalldirs deleted file mode 100755 index c5291db03655..000000000000 --- a/cextern/expat/conftools/mkinstalldirs +++ /dev/null @@ -1,40 +0,0 @@ -#! /bin/sh -# mkinstalldirs --- make directory hierarchy -# Author: Noah Friedman -# Created: 1993-05-16 -# Public domain - -# $Id: mkinstalldirs,v 1.1 2000/09/18 16:26:21 coopercc Exp $ - -errstatus=0 - -for file -do - set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'` - shift - - pathcomp= - for d - do - pathcomp="$pathcomp$d" - case "$pathcomp" in - -* ) pathcomp=./$pathcomp ;; - esac - - if test ! -d "$pathcomp"; then - echo "mkdir $pathcomp" - - mkdir "$pathcomp" || lasterr=$? - - if test ! -d "$pathcomp"; then - errstatus=$lasterr - fi - fi - - pathcomp="$pathcomp/" - done -done - -exit $errstatus - -# mkinstalldirs ends here diff --git a/cextern/expat/doc/expat.png b/cextern/expat/doc/expat.png deleted file mode 100755 index 5bc0726cfd85..000000000000 Binary files a/cextern/expat/doc/expat.png and /dev/null differ diff --git a/cextern/expat/doc/reference.html b/cextern/expat/doc/reference.html deleted file mode 100755 index a315870dd7cf..000000000000 --- a/cextern/expat/doc/reference.html +++ /dev/null @@ -1,2341 +0,0 @@ - - - - - - Expat XML Parser - - - - - - - - - - - - - - -
(Expat logo)
Release 2.0.1
-
- -

Expat is a library, written in C, for parsing XML documents. It's -the underlying XML parser for the open source Mozilla project, Perl's -XML::Parser, Python's xml.parsers.expat, and -other open-source XML parsers.

- -

This library is the creation of James Clark, who's also given us -groff (an nroff look-alike), Jade (an implemention of ISO's DSSSL -stylesheet language for SGML), XP (a Java XML parser package), XT (a -Java XSL engine). James was also the technical lead on the XML -Working Group at W3C that produced the XML specification.

- -

This is free software, licensed under the MIT/X Consortium license. You may download it -from the Expat home page. -

- -

The bulk of this document was originally commissioned as an article -by XML.com. They graciously allowed -Clark Cooper to retain copyright and to distribute it with Expat. -This version has been substantially extended to include documentation -on features which have been added since the original article was -published, and additional information on using the original -interface.

- -
-

Table of Contents

- - -
-

Overview

- -

Expat is a stream-oriented parser. You register callback (or -handler) functions with the parser and then start feeding it the -document. As the parser recognizes parts of the document, it will -call the appropriate handler for that part (if you've registered one.) -The document is fed to the parser in pieces, so you can start parsing -before you have all the document. This also allows you to parse really -huge documents that won't fit into memory.

- -

Expat can be intimidating due to the many kinds of handlers and -options you can set. But you only need to learn four functions in -order to do 90% of what you'll want to do with it:

- -
- -
XML_ParserCreate
-
Create a new parser object.
- -
XML_SetElementHandler
-
Set handlers for start and end tags.
- -
XML_SetCharacterDataHandler
-
Set handler for text.
- -
XML_Parse
-
Pass a buffer full of document to the parser
-
- -

These functions and others are described in the reference part of this document. The reference -section also describes in detail the parameters passed to the -different types of handlers.

- -

Let's look at a very simple example program that only uses 3 of the -above functions (it doesn't need to set a character handler.) The -program outline.c prints an -element outline, indenting child elements to distinguish them from the -parent element that contains them. The start handler does all the -work. It prints two indenting spaces for every level of ancestor -elements, then it prints the element and attribute -information. Finally it increments the global Depth -variable.

- -
-int Depth;
-
-void XMLCALL
-start(void *data, const char *el, const char **attr) {
-  int i;
-
-  for (i = 0; i < Depth; i++)
-    printf("  ");
-
-  printf("%s", el);
-
-  for (i = 0; attr[i]; i += 2) {
-    printf(" %s='%s'", attr[i], attr[i + 1]);
-  }
-
-  printf("\n");
-  Depth++;
-}  /* End of start handler */
-
- -

The end tag simply does the bookkeeping work of decrementing -Depth.

-
-void XMLCALL
-end(void *data, const char *el) {
-  Depth--;
-}  /* End of end handler */
-
- -

Note the XMLCALL annotation used for the callbacks. -This is used to ensure that the Expat and the callbacks are using the -same calling convention in case the compiler options used for Expat -itself and the client code are different. Expat tries not to care -what the default calling convention is, though it may require that it -be compiled with a default convention of "cdecl" on some platforms. -For code which uses Expat, however, the calling convention is -specified by the XMLCALL annotation on most platforms; -callbacks should be defined using this annotation.

- -

The XMLCALL annotation was added in Expat 1.95.7, but -existing working Expat applications don't need to add it (since they -are already using the "cdecl" calling convention, or they wouldn't be -working). The annotation is only needed if the default calling -convention may be something other than "cdecl". To use the annotation -safely with older versions of Expat, you can conditionally define it -after including Expat's header file:

- -
-#include <expat.h>
-
-#ifndef XMLCALL
-#if defined(_MSC_EXTENSIONS) && !defined(__BEOS__) && !defined(__CYGWIN__)
-#define XMLCALL __cdecl
-#elif defined(__GNUC__)
-#define XMLCALL __attribute__((cdecl))
-#else
-#define XMLCALL
-#endif
-#endif
-
- -

After creating the parser, the main program just has the job of -shoveling the document to the parser so that it can do its work.

- -
-

Building and Installing Expat

- -

The Expat distribution comes as a compressed (with GNU gzip) tar -file. You may download the latest version from Source Forge. After -unpacking this, cd into the directory. Then follow either the Win32 -directions or Unix directions below.

- -

Building under Win32

- -

If you're using the GNU compiler under cygwin, follow the Unix -directions in the next section. Otherwise if you have Microsoft's -Developer Studio installed, then from Windows Explorer double-click on -"expat.dsp" in the lib directory and build and install in the usual -manner.

- -

Alternatively, you may download the Win32 binary package that -contains the "expat.h" include file and a pre-built DLL.

- -

Building under Unix (or GNU)

- -

First you'll need to run the configure shell script in order to -configure the Makefiles and headers for your system.

- -

If you're happy with all the defaults that configure picks for you, -and you have permission on your system to install into /usr/local, you -can install Expat with this sequence of commands:

- -
-./configure
-make
-make install
-
- -

There are some options that you can provide to this script, but the -only one we'll mention here is the --prefix option. You -can find out all the options available by running configure with just -the --help option.

- -

By default, the configure script sets things up so that the library -gets installed in /usr/local/lib and the associated -header file in /usr/local/include. But if you were to -give the option, --prefix=/home/me/mystuff, then the -library and header would get installed in -/home/me/mystuff/lib and -/home/me/mystuff/include respectively.

- -

Configuring Expat Using the Pre-Processor

- -

Expat's feature set can be configured using a small number of -pre-processor definitions. The definition of this symbols does not -affect the set of entry points for Expat, only the behavior of the API -and the definition of character types in the case of -XML_UNICODE_WCHAR_T. The symbols are:

- -
-
XML_DTD
-
Include support for using and reporting DTD-based content. If -this is defined, default attribute values from an external DTD subset -are reported and attribute value normalization occurs based on the -type of attributes defined in the external subset. Without -this, Expat has a smaller memory footprint and can be faster, but will -not load external entities or process conditional sections. This does -not affect the set of functions available in the API.
- -
XML_NS
-
When defined, support for the Namespaces in XML -specification is included.
- -
XML_UNICODE
-
When defined, character data reported to the application is -encoded in UTF-16 using wide characters of the type -XML_Char. This is implied if -XML_UNICODE_WCHAR_T is defined.
- -
XML_UNICODE_WCHAR_T
-
If defined, causes the XML_Char character type to be -defined using the wchar_t type; otherwise, unsigned -short is used. Defining this implies -XML_UNICODE.
- -
XML_LARGE_SIZE
-
If defined, causes the XML_Size and XML_Index -integer types to be at least 64 bits in size. This is intended to support -processing of very large input streams, where the return values of -XML_GetCurrentByteIndex, -XML_GetCurrentLineNumber and -XML_GetCurrentColumnNumber -could overflow. It may not be supported by all compilers, and is turned -off by default.
- -
XML_CONTEXT_BYTES
-
The number of input bytes of markup context which the parser will -ensure are available for reporting via XML_GetInputContext. This is -normally set to 1024, and must be set to a positive interger. If this -is not defined, the input context will not be available and XML_GetInputContext will -always report NULL. Without this, Expat has a smaller memory -footprint and can be faster.
- -
XML_STATIC
-
On Windows, this should be set if Expat is going to be linked -statically with the code that calls it; this is required to get all -the right MSVC magic annotations correct. This is ignored on other -platforms.
-
- -
-

Using Expat

- -

Compiling and Linking Against Expat

- -

Unless you installed Expat in a location not expected by your -compiler and linker, all you have to do to use Expat in your programs -is to include the Expat header (#include <expat.h>) -in your files that make calls to it and to tell the linker that it -needs to link against the Expat library. On Unix systems, this would -usually be done with the -lexpat argument. Otherwise, -you'll need to tell the compiler where to look for the Expat header -and the linker where to find the Expat library. You may also need to -take steps to tell the operating system where to find this library at -run time.

- -

On a Unix-based system, here's what a Makefile might look like when -Expat is installed in a standard location:

- -
-CC=cc
-LDFLAGS=
-LIBS= -lexpat
-xmlapp: xmlapp.o
-        $(CC) $(LDFLAGS) -o xmlapp xmlapp.o $(LIBS)
-
- -

If you installed Expat in, say, /home/me/mystuff, then -the Makefile would look like this:

- -
-CC=cc
-CFLAGS= -I/home/me/mystuff/include
-LDFLAGS=
-LIBS= -L/home/me/mystuff/lib -lexpat
-xmlapp: xmlapp.o
-        $(CC) $(LDFLAGS) -o xmlapp xmlapp.o $(LIBS)
-
- -

You'd also have to set the environment variable -LD_LIBRARY_PATH to /home/me/mystuff/lib (or -to ${LD_LIBRARY_PATH}:/home/me/mystuff/lib if -LD_LIBRARY_PATH already has some directories in it) in order to run -your application.

- -

Expat Basics

- -

As we saw in the example in the overview, the first step in parsing -an XML document with Expat is to create a parser object. There are three functions in the Expat API for creating a -parser object. However, only two of these (XML_ParserCreate and XML_ParserCreateNS) can be used for -constructing a parser for a top-level document. The object returned -by these functions is an opaque pointer (i.e. "expat.h" declares it as -void *) to data with further internal structure. In order to free the -memory associated with this object you must call XML_ParserFree. Note that if you have -provided any user data that gets stored in the -parser, then your application is responsible for freeing it prior to -calling XML_ParserFree.

- -

The objects returned by the parser creation functions are good for -parsing only one XML document or external parsed entity. If your -application needs to parse many XML documents, then it needs to create -a parser object for each one. The best way to deal with this is to -create a higher level object that contains all the default -initialization you want for your parser objects.

- -

Walking through a document hierarchy with a stream oriented parser -will require a good stack mechanism in order to keep track of current -context. For instance, to answer the simple question, "What element -does this text belong to?" requires a stack, since the parser may have -descended into other elements that are children of the current one and -has encountered this text on the way out.

- -

The things you're likely to want to keep on a stack are the -currently opened element and it's attributes. You push this -information onto the stack in the start handler and you pop it off in -the end handler.

- -

For some tasks, it is sufficient to just keep information on what -the depth of the stack is (or would be if you had one.) The outline -program shown above presents one example. Another such task would be -skipping over a complete element. When you see the start tag for the -element you want to skip, you set a skip flag and record the depth at -which the element started. When the end tag handler encounters the -same depth, the skipped element has ended and the flag may be -cleared. If you follow the convention that the root element starts at -1, then you can use the same variable for skip flag and skip -depth.

- -
-void
-init_info(Parseinfo *info) {
-  info->skip = 0;
-  info->depth = 1;
-  /* Other initializations here */
-}  /* End of init_info */
-
-void XMLCALL
-rawstart(void *data, const char *el, const char **attr) {
-  Parseinfo *inf = (Parseinfo *) data;
-
-  if (! inf->skip) {
-    if (should_skip(inf, el, attr)) {
-      inf->skip = inf->depth;
-    }
-    else
-      start(inf, el, attr);     /* This does rest of start handling */
-  }
-
-  inf->depth++;
-}  /* End of rawstart */
-
-void XMLCALL
-rawend(void *data, const char *el) {
-  Parseinfo *inf = (Parseinfo *) data;
-
-  inf->depth--;
-
-  if (! inf->skip)
-    end(inf, el);              /* This does rest of end handling */
-
-  if (inf->skip == inf->depth)
-    inf->skip = 0;
-}  /* End rawend */
-
- -

Notice in the above example the difference in how depth is -manipulated in the start and end handlers. The end tag handler should -be the mirror image of the start tag handler. This is necessary to -properly model containment. Since, in the start tag handler, we -incremented depth after the main body of start tag code, then -in the end handler, we need to manipulate it before the main -body. If we'd decided to increment it first thing in the start -handler, then we'd have had to decrement it last thing in the end -handler.

- -

Communicating between handlers

- -

In order to be able to pass information between different handlers -without using globals, you'll need to define a data structure to hold -the shared variables. You can then tell Expat (with the XML_SetUserData function) to pass a -pointer to this structure to the handlers. This is the first -argument received by most handlers. In the reference section, an argument to a callback function is named -userData and have type void * if the user -data is passed; it will have the type XML_Parser if the -parser itself is passed. When the parser is passed, the user data may -be retrieved using XML_GetUserData.

- -

One common case where multiple calls to a single handler may need -to communicate using an application data structure is the case when -content passed to the character data handler (set by XML_SetCharacterDataHandler) needs to be accumulated. A -common first-time mistake with any of the event-oriented interfaces to -an XML parser is to expect all the text contained in an element to be -reported by a single call to the character data handler. Expat, like -many other XML parsers, reports such data as a sequence of calls; -there's no way to know when the end of the sequence is reached until a -different callback is made. A buffer referenced by the user data -structure proves both an effective and convenient place to accumulate -character data.

- - - - -

XML Version

- -

Expat is an XML 1.0 parser, and as such never complains based on -the value of the version pseudo-attribute in the XML -declaration, if present.

- -

If an application needs to check the version number (to support -alternate processing), it should use the XML_SetXmlDeclHandler function to -set a handler that uses the information in the XML declaration to -determine what to do. This example shows how to check that only a -version number of "1.0" is accepted:

- -
-static int wrong_version;
-static XML_Parser parser;
-
-static void XMLCALL
-xmldecl_handler(void            *userData,
-                const XML_Char  *version,
-                const XML_Char  *encoding,
-                int              standalone)
-{
-  static const XML_Char Version_1_0[] = {'1', '.', '0', 0};
-
-  int i;
-
-  for (i = 0; i < (sizeof(Version_1_0) / sizeof(Version_1_0[0])); ++i) {
-    if (version[i] != Version_1_0[i]) {
-      wrong_version = 1;
-      /* also clear all other handlers: */
-      XML_SetCharacterDataHandler(parser, NULL);
-      ...
-      return;
-    }
-  }
-  ...
-}
-
- -

Namespace Processing

- -

When the parser is created using the XML_ParserCreateNS, function, Expat -performs namespace processing. Under namespace processing, Expat -consumes xmlns and xmlns:... attributes, -which declare namespaces for the scope of the element in which they -occur. This means that your start handler will not see these -attributes. Your application can still be informed of these -declarations by setting namespace declaration handlers with XML_SetNamespaceDeclHandler.

- -

Element type and attribute names that belong to a given namespace -are passed to the appropriate handler in expanded form. By default -this expanded form is a concatenation of the namespace URI, the -separator character (which is the 2nd argument to XML_ParserCreateNS), and the local -name (i.e. the part after the colon). Names with undeclared prefixes -are not well-formed when namespace processing is enabled, and will -trigger an error. Unprefixed attribute names are never expanded, -and unprefixed element names are only expanded when they are in the -scope of a default namespace.

- -

However if XML_SetReturnNSTriplet has been called with a non-zero -do_nst parameter, then the expanded form for names with -an explicit prefix is a concatenation of: URI, separator, local name, -separator, prefix.

- -

You can set handlers for the start of a namespace declaration and -for the end of a scope of a declaration with the XML_SetNamespaceDeclHandler -function. The StartNamespaceDeclHandler is called prior to the start -tag handler and the EndNamespaceDeclHandler is called after the -corresponding end tag that ends the namespace's scope. The namespace -start handler gets passed the prefix and URI for the namespace. For a -default namespace declaration (xmlns='...'), the prefix will be null. -The URI will be null for the case where the default namespace is being -unset. The namespace end handler just gets the prefix for the closing -scope.

- -

These handlers are called for each declaration. So if, for -instance, a start tag had three namespace declarations, then the -StartNamespaceDeclHandler would be called three times before the start -tag handler is called, once for each declaration.

- -

Character Encodings

- -

While XML is based on Unicode, and every XML processor is required -to recognized UTF-8 and UTF-16 (1 and 2 byte encodings of Unicode), -other encodings may be declared in XML documents or entities. For the -main document, an XML declaration may contain an encoding -declaration:

-
-<?xml version="1.0" encoding="ISO-8859-2"?>
-
- -

External parsed entities may begin with a text declaration, which -looks like an XML declaration with just an encoding declaration:

-
-<?xml encoding="Big5"?>
-
- -

With Expat, you may also specify an encoding at the time of -creating a parser. This is useful when the encoding information may -come from a source outside the document itself (like a higher level -protocol.)

- -

There are four built-in encodings -in Expat:

-
    -
  • UTF-8
  • -
  • UTF-16
  • -
  • ISO-8859-1
  • -
  • US-ASCII
  • -
- -

Anything else discovered in an encoding declaration or in the -protocol encoding specified in the parser constructor, triggers a call -to the UnknownEncodingHandler. This handler gets passed -the encoding name and a pointer to an XML_Encoding data -structure. Your handler must fill in this structure and return -XML_STATUS_OK if it knows how to deal with the -encoding. Otherwise the handler should return -XML_STATUS_ERROR. The handler also gets passed a pointer -to an optional application data structure that you may indicate when -you set the handler.

- -

Expat places restrictions on character encodings that it can -support by filling in the XML_Encoding structure. -include file:

-
    -
  1. Every ASCII character that can appear in a well-formed XML document -must be represented by a single byte, and that byte must correspond to -it's ASCII encoding (except for the characters $@\^'{}~)
  2. -
  3. Characters must be encoded in 4 bytes or less.
  4. -
  5. All characters encoded must have Unicode scalar values less than or -equal to 65535 (0xFFFF)This does not apply to the built-in support -for UTF-16 and UTF-8
  6. -
  7. No character may be encoded by more that one distinct sequence of -bytes
  8. -
- -

XML_Encoding contains an array of integers that -correspond to the 1st byte of an encoding sequence. If the value in -the array for a byte is zero or positive, then the byte is a single -byte encoding that encodes the Unicode scalar value contained in the -array. A -1 in this array indicates a malformed byte. If the value is --2, -3, or -4, then the byte is the beginning of a 2, 3, or 4 byte -sequence respectively. Multi-byte sequences are sent to the convert -function pointed at in the XML_Encoding structure. This -function should return the Unicode scalar value for the sequence or -1 -if the sequence is malformed.

- -

One pitfall that novice Expat users are likely to fall into is that -although Expat may accept input in various encodings, the strings that -it passes to the handlers are always encoded in UTF-8 or UTF-16 -(depending on how Expat was compiled). Your application is responsible -for any translation of these strings into other encodings.

- -

Handling External Entity References

- -

Expat does not read or parse external entities directly. Note that -any external DTD is a special case of an external entity. If you've -set no ExternalEntityRefHandler, then external entity -references are silently ignored. Otherwise, it calls your handler with -the information needed to read and parse the external entity.

- -

Your handler isn't actually responsible for parsing the entity, but -it is responsible for creating a subsidiary parser with XML_ExternalEntityParserCreate that will do the job. This -returns an instance of XML_Parser that has handlers and -other data structures initialized from the parent parser. You may then -use XML_Parse or XML_ParseBuffer calls against this -parser. Since external entities my refer to other external entities, -your handler should be prepared to be called recursively.

- -

Parsing DTDs

- -

In order to parse parameter entities, before starting the parse, -you must call XML_SetParamEntityParsing with one of the following -arguments:

-
-
XML_PARAM_ENTITY_PARSING_NEVER
-
Don't parse parameter entities or the external subset
-
XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE
-
Parse parameter entites and the external subset unless -standalone was set to "yes" in the XML declaration.
-
XML_PARAM_ENTITY_PARSING_ALWAYS
-
Always parse parameter entities and the external subset
-
- -

In order to read an external DTD, you also have to set an external -entity reference handler as described above.

- -

Temporarily Stopping Parsing

- -

Expat 1.95.8 introduces a new feature: its now possible to stop -parsing temporarily from within a handler function, even if more data -has already been passed into the parser. Applications for this -include

- -
    -
  • Supporting the XInclude specification.
  • - -
  • Delaying further processing until additional information is - available from some other source.
  • - -
  • Adjusting processor load as task priorities shift within an - application.
  • - -
  • Stopping parsing completely (simply free or reset the parser - instead of resuming in the outer parsing loop). This can be useful - if a application-domain error is found in the XML being parsed or if - the result of the parse is determined not to be useful after - all.
  • -
- -

To take advantage of this feature, the main parsing loop of an -application needs to support this specifically. It cannot be -supported with a parsing loop compatible with Expat 1.95.7 or -earlier (though existing loops will continue to work without -supporting the stop/resume feature).

- -

An application that uses this feature for a single parser will have -the rough structure (in pseudo-code):

- -
-fd = open_input()
-p = create_parser()
-
-if parse_xml(p, fd) {
-  /* suspended */
-
-  int suspended = 1;
-
-  while (suspended) {
-    do_something_else()
-    if ready_to_resume() {
-      suspended = continue_parsing(p, fd);
-    }
-  }
-}
-
- -

An application that may resume any of several parsers based on -input (either from the XML being parsed or some other source) will -certainly have more interesting control structures.

- -

This C function could be used for the parse_xml -function mentioned in the pseudo-code above:

- -
-#define BUFF_SIZE 10240
-
-/* Parse a document from the open file descriptor 'fd' until the parse
-   is complete (the document has been completely parsed, or there's
-   been an error), or the parse is stopped.  Return non-zero when
-   the parse is merely suspended.
-*/
-int
-parse_xml(XML_Parser p, int fd)
-{
-  for (;;) {
-    int last_chunk;
-    int bytes_read;
-    enum XML_Status status;
-
-    void *buff = XML_GetBuffer(p, BUFF_SIZE);
-    if (buff == NULL) {
-      /* handle error... */
-      return 0;
-    }
-    bytes_read = read(fd, buff, BUFF_SIZE);
-    if (bytes_read < 0) {
-      /* handle error... */
-      return 0;
-    }
-    status = XML_ParseBuffer(p, bytes_read, bytes_read == 0);
-    switch (status) {
-      case XML_STATUS_ERROR:
-        /* handle error... */
-        return 0;
-      case XML_STATUS_SUSPENDED:
-        return 1;
-    }
-    if (bytes_read == 0)
-      return 0;
-  }
-}
-
- -

The corresponding continue_parsing function is -somewhat simpler, since it only need deal with the return code from -XML_ResumeParser; it can -delegate the input handling to the parse_xml -function:

- -
-/* Continue parsing a document which had been suspended.  The 'p' and
-   'fd' arguments are the same as passed to parse_xml().  Return
-   non-zero when the parse is suspended.
-*/
-int
-continue_parsing(XML_Parser p, int fd)
-{
-  enum XML_Status status = XML_ResumeParser(p);
-  switch (status) {
-    case XML_STATUS_ERROR:
-      /* handle error... */
-      return 0;
-    case XML_ERROR_NOT_SUSPENDED:
-      /* handle error... */
-      return 0;.
-    case XML_STATUS_SUSPENDED:
-      return 1;
-  }
-  return parse_xml(p, fd);
-}
-
- -

Now that we've seen what a mess the top-level parsing loop can -become, what have we gained? Very simply, we can now use the XML_StopParser function to stop -parsing, without having to go to great lengths to avoid additional -processing that we're expecting to ignore. As a bonus, we get to stop -parsing temporarily, and come back to it when we're -ready.

- -

To stop parsing from a handler function, use the XML_StopParser function. This function -takes two arguments; the parser being stopped and a flag indicating -whether the parse can be resumed in the future.

- - - - -
- - -

Expat Reference

- -

Parser Creation

- -
-XML_Parser XMLCALL
-XML_ParserCreate(const XML_Char *encoding);
-
-
-Construct a new parser. If encoding is non-null, it specifies a -character encoding to use for the document. This overrides the document -encoding declaration. There are four built-in encodings: -
    -
  • US-ASCII
  • -
  • UTF-8
  • -
  • UTF-16
  • -
  • ISO-8859-1
  • -
-Any other value will invoke a call to the UnknownEncodingHandler. -
- -
-XML_Parser XMLCALL
-XML_ParserCreateNS(const XML_Char *encoding,
-                   XML_Char sep);
-
-
-Constructs a new parser that has namespace processing in effect. Namespace -expanded element names and attribute names are returned as a concatenation -of the namespace URI, sep, and the local part of the name. This -means that you should pick a character for sep that can't be -part of a legal URI. There is a special case when sep is the null -character '\0': the namespace URI and the local part will be -concatenated without any separator - this is intended to support RDF processors. -It is a programming error to use the null separator with -namespace triplets.
- -
-XML_Parser XMLCALL
-XML_ParserCreate_MM(const XML_Char *encoding,
-                    const XML_Memory_Handling_Suite *ms,
-		    const XML_Char *sep);
-
-
-typedef struct {
-  void *(XMLCALL *malloc_fcn)(size_t size);
-  void *(XMLCALL *realloc_fcn)(void *ptr, size_t size);
-  void (XMLCALL *free_fcn)(void *ptr);
-} XML_Memory_Handling_Suite;
-
-
-

Construct a new parser using the suite of memory handling functions -specified in ms. If ms is NULL, then use the -standard set of memory management functions. If sep is -non NULL, then namespace processing is enabled in the created parser -and the character pointed at by sep is used as the separator between -the namespace URI and the local part of the name.

-
- -
-XML_Parser XMLCALL
-XML_ExternalEntityParserCreate(XML_Parser p,
-                               const XML_Char *context,
-                               const XML_Char *encoding);
-
-
-Construct a new XML_Parser object for parsing an external -general entity. Context is the context argument passed in a call to a -ExternalEntityRefHandler. Other state information such as handlers, -user data, namespace processing is inherited from the parser passed as -the 1st argument. So you shouldn't need to call any of the behavior -changing functions on this parser (unless you want it to act -differently than the parent parser). -
- -
-void XMLCALL
-XML_ParserFree(XML_Parser p);
-
-
-Free memory used by the parser. Your application is responsible for -freeing any memory associated with user data. -
- -
-XML_Bool XMLCALL
-XML_ParserReset(XML_Parser p,
-                const XML_Char *encoding);
-
-
-Clean up the memory structures maintained by the parser so that it may -be used again. After this has been called, parser is -ready to start parsing a new document. All handlers are cleared from -the parser, except for the unknownEncodingHandler. The parser's external -state is re-initialized except for the values of ns and ns_triplets. -This function may not be used on a parser created using XML_ExternalEntityParserCreate; it will return XML_FALSE in that case. Returns -XML_TRUE on success. Your application is responsible for -dealing with any memory associated with user data. -
- -

Parsing

- -

To state the obvious: the three parsing functions XML_Parse, -XML_ParseBuffer and -XML_GetBuffer must not be called from within a handler -unless they operate on a separate parser instance, that is, one that -did not call the handler. For example, it is OK to call the parsing -functions from within an XML_ExternalEntityRefHandler, -if they apply to the parser created by -XML_ExternalEntityParserCreate.

- -

Note: the len argument passed to these functions -should be considerably less than the maximum value for an integer, -as it could create an integer overflow situation if the added -lengths of a buffer and the unprocessed portion of the previous buffer -exceed the maximum integer value. Input data at the end of a buffer -will remain unprocessed if it is part of an XML token for which the -end is not part of that buffer.

- -
-enum XML_Status XMLCALL
-XML_Parse(XML_Parser p,
-          const char *s,
-          int len,
-          int isFinal);
-
-
-enum XML_Status {
-  XML_STATUS_ERROR = 0,
-  XML_STATUS_OK = 1
-};
-
-
-Parse some more of the document. The string s is a buffer -containing part (or perhaps all) of the document. The number of bytes of s -that are part of the document is indicated by len. This means -that s doesn't have to be null terminated. It also means that -if len is larger than the number of bytes in the block of -memory that s points at, then a memory fault is likely. The -isFinal parameter informs the parser that this is the last -piece of the document. Frequently, the last piece is empty (i.e. -len is zero.) -If a parse error occurred, it returns XML_STATUS_ERROR. -Otherwise it returns XML_STATUS_OK value. -
- -
-enum XML_Status XMLCALL
-XML_ParseBuffer(XML_Parser p,
-                int len,
-                int isFinal);
-
-
-This is just like XML_Parse, -except in this case Expat provides the buffer. By obtaining the -buffer from Expat with the XML_GetBuffer function, the application can avoid double -copying of the input. -
- -
-void * XMLCALL
-XML_GetBuffer(XML_Parser p,
-              int len);
-
-
-Obtain a buffer of size len to read a piece of the document -into. A NULL value is returned if Expat can't allocate enough memory for -this buffer. This has to be called prior to every call to -XML_ParseBuffer. A -typical use would look like this: - -
-for (;;) {
-  int bytes_read;
-  void *buff = XML_GetBuffer(p, BUFF_SIZE);
-  if (buff == NULL) {
-    /* handle error */
-  }
-
-  bytes_read = read(docfd, buff, BUFF_SIZE);
-  if (bytes_read < 0) {
-    /* handle error */
-  }
-
-  if (! XML_ParseBuffer(p, bytes_read, bytes_read == 0)) {
-    /* handle parse error */
-  }
-
-  if (bytes_read == 0)
-    break;
-}
-
-
- -
-enum XML_Status XMLCALL
-XML_StopParser(XML_Parser p,
-               XML_Bool resumable);
-
-
- -

Stops parsing, causing XML_Parse or XML_ParseBuffer to return. Must be called from within a -call-back handler, except when aborting (when resumable -is XML_FALSE) an already suspended parser. Some -call-backs may still follow because they would otherwise get -lost, including -

    -
  • the end element handler for empty elements when stopped in the - start element handler,
  • -
  • the end namespace declaration handler when stopped in the end - element handler,
  • -
  • the character data handler when stopped in the character data handler - while making multiple call-backs on a contiguous chunk of characters,
  • -
-and possibly others.

- -

This can be called from most handlers, including DTD related -call-backs, except when parsing an external parameter entity and -resumable is XML_TRUE. Returns -XML_STATUS_OK when successful, -XML_STATUS_ERROR otherwise. The possible error codes -are:

-
-
XML_ERROR_SUSPENDED
-
when suspending an already suspended parser.
-
XML_ERROR_FINISHED
-
when the parser has already finished.
-
XML_ERROR_SUSPEND_PE
-
when suspending while parsing an external PE.
-
- -

Since the stop/resume feature requires application support in the -outer parsing loop, it is an error to call this function for a parser -not being handled appropriately; see Temporarily Stopping Parsing for more information.

- -

When resumable is XML_TRUE then parsing -is suspended, that is, XML_Parse and XML_ParseBuffer return XML_STATUS_SUSPENDED. -Otherwise, parsing is aborted, that is, XML_Parse and XML_ParseBuffer return -XML_STATUS_ERROR with error code -XML_ERROR_ABORTED.

- -

Note: -This will be applied to the current parser instance only, that is, if -there is a parent parser then it will continue parsing when the -external entity reference handler returns. It is up to the -implementation of that handler to call XML_StopParser on the parent parser -(recursively), if one wants to stop parsing altogether.

- -

When suspended, parsing can be resumed by calling XML_ResumeParser.

- -

New in Expat 1.95.8.

-
- -
-enum XML_Status XMLCALL
-XML_ResumeParser(XML_Parser p);
-
-
-

Resumes parsing after it has been suspended with XML_StopParser. Must not be called from -within a handler call-back. Returns same status codes as XML_Parse or XML_ParseBuffer. An additional error -code, XML_ERROR_NOT_SUSPENDED, will be returned if the -parser was not currently suspended.

- -

Note: -This must be called on the most deeply nested child parser instance -first, and on its parent parser only after the child parser has -finished, to be applied recursively until the document entity's parser -is restarted. That is, the parent parser will not resume by itself -and it is up to the application to call XML_ResumeParser on it at the -appropriate moment.

- -

New in Expat 1.95.8.

-
- -
-void XMLCALL
-XML_GetParsingStatus(XML_Parser p,
-                     XML_ParsingStatus *status);
-
-
-enum XML_Parsing {
-  XML_INITIALIZED,
-  XML_PARSING,
-  XML_FINISHED,
-  XML_SUSPENDED
-};
-
-typedef struct {
-  enum XML_Parsing parsing;
-  XML_Bool finalBuffer;
-} XML_ParsingStatus;
-
-
-

Returns status of parser with respect to being initialized, -parsing, finished, or suspended, and whether the final buffer is being -processed. The status parameter must not be -NULL.

- -

New in Expat 1.95.8.

-
- - -

Handler Setting

- -

Although handlers are typically set prior to parsing and left alone, an -application may choose to set or change the handler for a parsing event -while the parse is in progress. For instance, your application may choose -to ignore all text not descended from a para element. One -way it could do this is to set the character handler when a para start tag -is seen, and unset it for the corresponding end tag.

- -

A handler may be unset by providing a NULL pointer to the -appropriate handler setter. None of the handler setting functions have -a return value.

- -

Your handlers will be receiving strings in arrays of type -XML_Char. This type is conditionally defined in expat.h as -either char, wchar_t or unsigned short. -The former implies UTF-8 encoding, the latter two imply UTF-16 encoding. -Note that you'll receive them in this form independent of the original -encoding of the document.

- -
-
-void XMLCALL
-XML_SetStartElementHandler(XML_Parser p,
-                           XML_StartElementHandler start);
-
-
-typedef void
-(XMLCALL *XML_StartElementHandler)(void *userData,
-                                   const XML_Char *name,
-                                   const XML_Char **atts);
-
-

Set handler for start (and empty) tags. Attributes are passed to the start -handler as a pointer to a vector of char pointers. Each attribute seen in -a start (or empty) tag occupies 2 consecutive places in this vector: the -attribute name followed by the attribute value. These pairs are terminated -by a null pointer.

-

Note that an empty tag generates a call to both start and end handlers -(in that order).

-
- -
-
-void XMLCALL
-XML_SetEndElementHandler(XML_Parser p,
-                         XML_EndElementHandler);
-
-
-typedef void
-(XMLCALL *XML_EndElementHandler)(void *userData,
-                                 const XML_Char *name);
-
-

Set handler for end (and empty) tags. As noted above, an empty tag -generates a call to both start and end handlers.

-
- -
-
-void XMLCALL
-XML_SetElementHandler(XML_Parser p,
-                      XML_StartElementHandler start,
-                      XML_EndElementHandler end);
-
-

Set handlers for start and end tags with one call.

-
- -
-
-void XMLCALL
-XML_SetCharacterDataHandler(XML_Parser p,
-                            XML_CharacterDataHandler charhndl)
-
-
-typedef void
-(XMLCALL *XML_CharacterDataHandler)(void *userData,
-                                    const XML_Char *s,
-                                    int len);
-
-

Set a text handler. The string your handler receives -is NOT nul-terminated. You have to use the length argument -to deal with the end of the string. A single block of contiguous text -free of markup may still result in a sequence of calls to this handler. -In other words, if you're searching for a pattern in the text, it may -be split across calls to this handler. Note: Setting this handler to NULL -may NOT immediately terminate call-backs if the parser is currently -processing such a single block of contiguous markup-free text, as the parser -will continue calling back until the end of the block is reached.

-
- -
-
-void XMLCALL
-XML_SetProcessingInstructionHandler(XML_Parser p,
-                                    XML_ProcessingInstructionHandler proc)
-
-
-typedef void
-(XMLCALL *XML_ProcessingInstructionHandler)(void *userData,
-                                            const XML_Char *target,
-                                            const XML_Char *data);
-
-
-

Set a handler for processing instructions. The target is the first word -in the processing instruction. The data is the rest of the characters in -it after skipping all whitespace after the initial word.

-
- -
-
-void XMLCALL
-XML_SetCommentHandler(XML_Parser p,
-                      XML_CommentHandler cmnt)
-
-
-typedef void
-(XMLCALL *XML_CommentHandler)(void *userData,
-                              const XML_Char *data);
-
-

Set a handler for comments. The data is all text inside the comment -delimiters.

-
- -
-
-void XMLCALL
-XML_SetStartCdataSectionHandler(XML_Parser p,
-                                XML_StartCdataSectionHandler start);
-
-
-typedef void
-(XMLCALL *XML_StartCdataSectionHandler)(void *userData);
-
-

Set a handler that gets called at the beginning of a CDATA section.

-
- -
-
-void XMLCALL
-XML_SetEndCdataSectionHandler(XML_Parser p,
-                              XML_EndCdataSectionHandler end);
-
-
-typedef void
-(XMLCALL *XML_EndCdataSectionHandler)(void *userData);
-
-

Set a handler that gets called at the end of a CDATA section.

-
- -
-
-void XMLCALL
-XML_SetCdataSectionHandler(XML_Parser p,
-                           XML_StartCdataSectionHandler start,
-                           XML_EndCdataSectionHandler end)
-
-

Sets both CDATA section handlers with one call.

-
- -
-
-void XMLCALL
-XML_SetDefaultHandler(XML_Parser p,
-                      XML_DefaultHandler hndl)
-
-
-typedef void
-(XMLCALL *XML_DefaultHandler)(void *userData,
-                              const XML_Char *s,
-                              int len);
-
- -

Sets a handler for any characters in the document which wouldn't -otherwise be handled. This includes both data for which no handlers -can be set (like some kinds of DTD declarations) and data which could -be reported but which currently has no handler set. The characters -are passed exactly as they were present in the XML document except -that they will be encoded in UTF-8 or UTF-16. Line boundaries are not -normalized. Note that a byte order mark character is not passed to the -default handler. There are no guarantees about how characters are -divided between calls to the default handler: for example, a comment -might be split between multiple calls. Setting the handler with -this call has the side effect of turning off expansion of references -to internally defined general entities. Instead these references are -passed to the default handler.

- -

See also XML_DefaultCurrent.

-
- -
-
-void XMLCALL
-XML_SetDefaultHandlerExpand(XML_Parser p,
-                            XML_DefaultHandler hndl)
-
-
-typedef void
-(XMLCALL *XML_DefaultHandler)(void *userData,
-                              const XML_Char *s,
-                              int len);
-
-

This sets a default handler, but doesn't inhibit the expansion of -internal entity references. The entity reference will not be passed -to the default handler.

- -

See also XML_DefaultCurrent.

-
- -
-
-void XMLCALL
-XML_SetExternalEntityRefHandler(XML_Parser p,
-                                XML_ExternalEntityRefHandler hndl)
-
-
-typedef int
-(XMLCALL *XML_ExternalEntityRefHandler)(XML_Parser p,
-                                        const XML_Char *context,
-                                        const XML_Char *base,
-                                        const XML_Char *systemId,
-                                        const XML_Char *publicId);
-
-

Set an external entity reference handler. This handler is also -called for processing an external DTD subset if parameter entity parsing -is in effect. (See -XML_SetParamEntityParsing.)

- -

The context parameter specifies the parsing context in -the format expected by the context argument to XML_ExternalEntityParserCreate. code is -valid only until the handler returns, so if the referenced entity is -to be parsed later, it must be copied. context is NULL -only when the entity is a parameter entity, which is how one can -differentiate between general and parameter entities.

- -

The base parameter is the base to use for relative -system identifiers. It is set by XML_SetBase and may be NULL. The -publicId parameter is the public id given in the entity -declaration and may be NULL. systemId is the system -identifier specified in the entity declaration and is never NULL.

- -

There are a couple of ways in which this handler differs from -others. First, this handler returns a status indicator (an -integer). XML_STATUS_OK should be returned for successful -handling of the external entity reference. Returning -XML_STATUS_ERROR indicates failure, and causes the -calling parser to return an -XML_ERROR_EXTERNAL_ENTITY_HANDLING error.

- -

Second, instead of having the user data as its first argument, it -receives the parser that encountered the entity reference. This, along -with the context parameter, may be used as arguments to a call to -XML_ExternalEntityParserCreate. Using the returned -parser, the body of the external entity can be recursively parsed.

- -

Since this handler may be called recursively, it should not be saving -information into global or static variables.

-
- -
-void XMLCALL
-XML_SetExternalEntityRefHandlerArg(XML_Parser p,
-                                   void *arg)
-
-
-

Set the argument passed to the ExternalEntityRefHandler. If -arg is not NULL, it is the new value passed to the -handler set using XML_SetExternalEntityRefHandler; if arg is -NULL, the argument passed to the handler function will be the parser -object itself.

- -

Note: -The type of arg and the type of the first argument to the -ExternalEntityRefHandler do not match. This function takes a -void * to be passed to the handler, while the handler -accepts an XML_Parser. This is a historical accident, -but will not be corrected before Expat 2.0 (at the earliest) to avoid -causing compiler warnings for code that's known to work with this -API. It is the responsibility of the application code to know the -actual type of the argument passed to the handler and to manage it -properly.

-
- -
-
-void XMLCALL
-XML_SetSkippedEntityHandler(XML_Parser p,
-                            XML_SkippedEntityHandler handler)
-
-
-typedef void
-(XMLCALL *XML_SkippedEntityHandler)(void *userData,
-                                    const XML_Char *entityName,
-                                    int is_parameter_entity);
-
-

Set a skipped entity handler. This is called in two situations:

-
    -
  1. An entity reference is encountered for which no declaration - has been read and this is not an error.
  2. -
  3. An internal entity reference is read, but not expanded, because - XML_SetDefaultHandler - has been called.
  4. -
-

The is_parameter_entity argument will be non-zero for -a parameter entity and zero for a general entity.

Note: skipped -parameter entities in declarations and skipped general entities in -attribute values cannot be reported, because the event would be out of -sync with the reporting of the declarations or attribute values

-
- -
-
-void XMLCALL
-XML_SetUnknownEncodingHandler(XML_Parser p,
-                              XML_UnknownEncodingHandler enchandler,
-			      void *encodingHandlerData)
-
-
-typedef int
-(XMLCALL *XML_UnknownEncodingHandler)(void *encodingHandlerData,
-                                      const XML_Char *name,
-                                      XML_Encoding *info);
-
-typedef struct {
-  int map[256];
-  void *data;
-  int (XMLCALL *convert)(void *data, const char *s);
-  void (XMLCALL *release)(void *data);
-} XML_Encoding;
-
-

Set a handler to deal with encodings other than the built in set. This should be done before -XML_Parse or XML_ParseBuffer have been called on the -given parser.

If the handler knows how to deal with an encoding -with the given name, it should fill in the info data -structure and return XML_STATUS_OK. Otherwise it -should return XML_STATUS_ERROR. The handler will be called -at most once per parsed (external) entity. The optional application -data pointer encodingHandlerData will be passed back to -the handler.

- -

The map array contains information for every possible possible leading -byte in a byte sequence. If the corresponding value is >= 0, then it's -a single byte sequence and the byte encodes that Unicode value. If the -value is -1, then that byte is invalid as the initial byte in a sequence. -If the value is -n, where n is an integer > 1, then n is the number of -bytes in the sequence and the actual conversion is accomplished by a -call to the function pointed at by convert. This function may return -1 -if the sequence itself is invalid. The convert pointer may be null if -there are only single byte codes. The data parameter passed to the convert -function is the data pointer from XML_Encoding. The -string s is NOT nul-terminated and points at the sequence of -bytes to be converted.

- -

The function pointed at by release is called by the -parser when it is finished with the encoding. It may be NULL.

-
- -
-
-void XMLCALL
-XML_SetStartNamespaceDeclHandler(XML_Parser p,
-			         XML_StartNamespaceDeclHandler start);
-
-
-typedef void
-(XMLCALL *XML_StartNamespaceDeclHandler)(void *userData,
-                                         const XML_Char *prefix,
-                                         const XML_Char *uri);
-
-

Set a handler to be called when a namespace is declared. Namespace -declarations occur inside start tags. But the namespace declaration start -handler is called before the start tag handler for each namespace declared -in that start tag.

-
- -
-
-void XMLCALL
-XML_SetEndNamespaceDeclHandler(XML_Parser p,
-			       XML_EndNamespaceDeclHandler end);
-
-
-typedef void
-(XMLCALL *XML_EndNamespaceDeclHandler)(void *userData,
-                                       const XML_Char *prefix);
-
-

Set a handler to be called when leaving the scope of a namespace -declaration. This will be called, for each namespace declaration, -after the handler for the end tag of the element in which the -namespace was declared.

-
- -
-
-void XMLCALL
-XML_SetNamespaceDeclHandler(XML_Parser p,
-                            XML_StartNamespaceDeclHandler start,
-                            XML_EndNamespaceDeclHandler end)
-
-

Sets both namespace declaration handlers with a single call.

-
- -
-
-void XMLCALL
-XML_SetXmlDeclHandler(XML_Parser p,
-		      XML_XmlDeclHandler xmldecl);
-
-
-typedef void
-(XMLCALL *XML_XmlDeclHandler)(void            *userData,
-                              const XML_Char  *version,
-                              const XML_Char  *encoding,
-                              int             standalone);
-
-

Sets a handler that is called for XML declarations and also for -text declarations discovered in external entities. The way to -distinguish is that the version parameter will be NULL -for text declarations. The encoding parameter may be NULL -for an XML declaration. The standalone argument will -contain -1, 0, or 1 indicating respectively that there was no -standalone parameter in the declaration, that it was given as no, or -that it was given as yes.

-
- -
-
-void XMLCALL
-XML_SetStartDoctypeDeclHandler(XML_Parser p,
-			       XML_StartDoctypeDeclHandler start);
-
-
-typedef void
-(XMLCALL *XML_StartDoctypeDeclHandler)(void           *userData,
-                                       const XML_Char *doctypeName,
-                                       const XML_Char *sysid,
-                                       const XML_Char *pubid,
-                                       int            has_internal_subset);
-
-

Set a handler that is called at the start of a DOCTYPE declaration, -before any external or internal subset is parsed. Both sysid -and pubid may be NULL. The has_internal_subset -will be non-zero if the DOCTYPE declaration has an internal subset.

-
- -
-
-void XMLCALL
-XML_SetEndDoctypeDeclHandler(XML_Parser p,
-			     XML_EndDoctypeDeclHandler end);
-
-
-typedef void
-(XMLCALL *XML_EndDoctypeDeclHandler)(void *userData);
-
-

Set a handler that is called at the end of a DOCTYPE declaration, -after parsing any external subset.

-
- -
-
-void XMLCALL
-XML_SetDoctypeDeclHandler(XML_Parser p,
-			  XML_StartDoctypeDeclHandler start,
-			  XML_EndDoctypeDeclHandler end);
-
-

Set both doctype handlers with one call.

-
- -
-
-void XMLCALL
-XML_SetElementDeclHandler(XML_Parser p,
-			  XML_ElementDeclHandler eldecl);
-
-
-typedef void
-(XMLCALL *XML_ElementDeclHandler)(void *userData,
-                                  const XML_Char *name,
-                                  XML_Content *model);
-
-
-enum XML_Content_Type {
-  XML_CTYPE_EMPTY = 1,
-  XML_CTYPE_ANY,
-  XML_CTYPE_MIXED,
-  XML_CTYPE_NAME,
-  XML_CTYPE_CHOICE,
-  XML_CTYPE_SEQ
-};
-
-enum XML_Content_Quant {
-  XML_CQUANT_NONE,
-  XML_CQUANT_OPT,
-  XML_CQUANT_REP,
-  XML_CQUANT_PLUS
-};
-
-typedef struct XML_cp XML_Content;
-
-struct XML_cp {
-  enum XML_Content_Type		type;
-  enum XML_Content_Quant	quant;
-  const XML_Char *		name;
-  unsigned int			numchildren;
-  XML_Content *			children;
-};
-
-

Sets a handler for element declarations in a DTD. The handler gets -called with the name of the element in the declaration and a pointer -to a structure that contains the element model. It is the -application's responsibility to free this data structure using -XML_FreeContentModel.

- -

The model argument is the root of a tree of -XML_Content nodes. If type equals -XML_CTYPE_EMPTY or XML_CTYPE_ANY, then -quant will be XML_CQUANT_NONE, and the other -fields will be zero or NULL. If type is -XML_CTYPE_MIXED, then quant will be -XML_CQUANT_NONE or XML_CQUANT_REP and -numchildren will contain the number of elements that are -allowed to be mixed in and children points to an array of -XML_Content structures that will all have type -XML_CTYPE_NAME with no quantification. Only the root node can be type -XML_CTYPE_EMPTY, XML_CTYPE_ANY, or -XML_CTYPE_MIXED.

- -

For type XML_CTYPE_NAME, the name field -points to the name and the numchildren and -children fields will be zero and NULL. The -quant field will indicate any quantifiers placed on the -name.

- -

Types XML_CTYPE_CHOICE and XML_CTYPE_SEQ -indicate a choice or sequence respectively. The -numchildren field indicates how many nodes in the choice -or sequence and children points to the nodes.

-
- -
-
-void XMLCALL
-XML_SetAttlistDeclHandler(XML_Parser p,
-                          XML_AttlistDeclHandler attdecl);
-
-
-typedef void
-(XMLCALL *XML_AttlistDeclHandler)(void           *userData,
-                                  const XML_Char *elname,
-                                  const XML_Char *attname,
-                                  const XML_Char *att_type,
-                                  const XML_Char *dflt,
-                                  int            isrequired);
-
-

Set a handler for attlist declarations in the DTD. This handler is -called for each attribute. So a single attlist declaration -with multiple attributes declared will generate multiple calls to this -handler. The elname parameter returns the name of the -element for which the attribute is being declared. The attribute name -is in the attname parameter. The attribute type is in the -att_type parameter. It is the string representing the -type in the declaration with whitespace removed.

- -

The dflt parameter holds the default value. It will be -NULL in the case of "#IMPLIED" or "#REQUIRED" attributes. You can -distinguish these two cases by checking the isrequired -parameter, which will be true in the case of "#REQUIRED" attributes. -Attributes which are "#FIXED" will have also have a true -isrequired, but they will have the non-NULL fixed value -in the dflt parameter.

-
- -
-
-void XMLCALL
-XML_SetEntityDeclHandler(XML_Parser p,
-			 XML_EntityDeclHandler handler);
-
-
-typedef void
-(XMLCALL *XML_EntityDeclHandler)(void           *userData,
-                                 const XML_Char *entityName,
-                                 int            is_parameter_entity,
-                                 const XML_Char *value,
-                                 int            value_length, 
-                                 const XML_Char *base,
-                                 const XML_Char *systemId,
-                                 const XML_Char *publicId,
-                                 const XML_Char *notationName);
-
-

Sets a handler that will be called for all entity declarations. -The is_parameter_entity argument will be non-zero in the -case of parameter entities and zero otherwise.

- -

For internal entities (<!ENTITY foo "bar">), -value will be non-NULL and systemId, -publicId, and notationName will all be NULL. -The value string is not NULL terminated; the length is -provided in the value_length parameter. Do not use -value_length to test for internal entities, since it is -legal to have zero-length values. Instead check for whether or not -value is NULL.

The notationName -argument will have a non-NULL value only for unparsed entity -declarations.

-
- -
-
-void XMLCALL
-XML_SetUnparsedEntityDeclHandler(XML_Parser p,
-                                 XML_UnparsedEntityDeclHandler h)
-
-
-typedef void
-(XMLCALL *XML_UnparsedEntityDeclHandler)(void *userData,
-                                         const XML_Char *entityName, 
-                                         const XML_Char *base,
-                                         const XML_Char *systemId,
-                                         const XML_Char *publicId,
-                                         const XML_Char *notationName);
-
-

Set a handler that receives declarations of unparsed entities. These -are entity declarations that have a notation (NDATA) field:

- -
-<!ENTITY logo SYSTEM "images/logo.gif" NDATA gif>
-
-

This handler is obsolete and is provided for backwards -compatibility. Use instead XML_SetEntityDeclHandler.

-
- -
-
-void XMLCALL
-XML_SetNotationDeclHandler(XML_Parser p,
-                           XML_NotationDeclHandler h)
-
-
-typedef void
-(XMLCALL *XML_NotationDeclHandler)(void *userData, 
-                                   const XML_Char *notationName,
-                                   const XML_Char *base,
-                                   const XML_Char *systemId,
-                                   const XML_Char *publicId);
-
-

Set a handler that receives notation declarations.

-
- -
-
-void XMLCALL
-XML_SetNotStandaloneHandler(XML_Parser p,
-                            XML_NotStandaloneHandler h)
-
-
-typedef int 
-(XMLCALL *XML_NotStandaloneHandler)(void *userData);
-
-

Set a handler that is called if the document is not "standalone". -This happens when there is an external subset or a reference to a -parameter entity, but does not have standalone set to "yes" in an XML -declaration. If this handler returns XML_STATUS_ERROR, -then the parser will throw an XML_ERROR_NOT_STANDALONE -error.

-
- -

Parse position and error reporting functions

- -

These are the functions you'll want to call when the parse -functions return XML_STATUS_ERROR (a parse error has -occurred), although the position reporting functions are useful outside -of errors. The position reported is the byte position (in the original -document or entity encoding) of the first of the sequence of -characters that generated the current event (or the error that caused -the parse functions to return XML_STATUS_ERROR.) The -exceptions are callbacks trigged by declarations in the document -prologue, in which case they exact position reported is somewhere in the -relevant markup, but not necessarily as meaningful as for other -events.

- -

The position reporting functions are accurate only outside of the -DTD. In other words, they usually return bogus information when -called from within a DTD declaration handler.

- -
-enum XML_Error XMLCALL
-XML_GetErrorCode(XML_Parser p);
-
-
-Return what type of error has occurred. -
- -
-const XML_LChar * XMLCALL
-XML_ErrorString(enum XML_Error code);
-
-
-Return a string describing the error corresponding to code. -The code should be one of the enums that can be returned from -XML_GetErrorCode. -
- -
-XML_Index XMLCALL
-XML_GetCurrentByteIndex(XML_Parser p);
-
-
-Return the byte offset of the position. This always corresponds to -the values returned by XML_GetCurrentLineNumber and XML_GetCurrentColumnNumber. -
- -
-XML_Size XMLCALL
-XML_GetCurrentLineNumber(XML_Parser p);
-
-
-Return the line number of the position. The first line is reported as -1. -
- -
-XML_Size XMLCALL
-XML_GetCurrentColumnNumber(XML_Parser p);
-
-
-Return the offset, from the beginning of the current line, of -the position. -
- -
-int XMLCALL
-XML_GetCurrentByteCount(XML_Parser p);
-
-
-Return the number of bytes in the current event. Returns -0 if the event is inside a reference to an internal -entity and for the end-tag event for empty element tags (the later can -be used to distinguish empty-element tags from empty elements using -separate start and end tags). -
- -
-const char * XMLCALL
-XML_GetInputContext(XML_Parser p,
-                    int *offset,
-                    int *size);
-
-
- -

Returns the parser's input buffer, sets the integer pointed at by -offset to the offset within this buffer of the current -parse position, and set the integer pointed at by size to -the size of the returned buffer.

- -

This should only be called from within a handler during an active -parse and the returned buffer should only be referred to from within -the handler that made the call. This input buffer contains the -untranslated bytes of the input.

- -

Only a limited amount of context is kept, so if the event -triggering a call spans over a very large amount of input, the actual -parse position may be before the beginning of the buffer.

- -

If XML_CONTEXT_BYTES is not defined, this will always -return NULL.

-
- -

Miscellaneous functions

- -

The functions in this section either obtain state information from -the parser or can be used to dynamicly set parser options.

- -
-void XMLCALL
-XML_SetUserData(XML_Parser p,
-                void *userData);
-
-
-This sets the user data pointer that gets passed to handlers. It -overwrites any previous value for this pointer. Note that the -application is responsible for freeing the memory associated with -userData when it is finished with the parser. So if you -call this when there's already a pointer there, and you haven't freed -the memory associated with it, then you've probably just leaked -memory. -
- -
-void * XMLCALL
-XML_GetUserData(XML_Parser p);
-
-
-This returns the user data pointer that gets passed to handlers. -It is actually implemented as a macro. -
- -
-void XMLCALL
-XML_UseParserAsHandlerArg(XML_Parser p);
-
-
-After this is called, handlers receive the parser in their -userData arguments. The user data can still be obtained -using the XML_GetUserData function. -
- -
-enum XML_Status XMLCALL
-XML_SetBase(XML_Parser p,
-            const XML_Char *base);
-
-
-Set the base to be used for resolving relative URIs in system -identifiers. The return value is XML_STATUS_ERROR if -there's no memory to store base, otherwise it's -XML_STATUS_OK. -
- -
-const XML_Char * XMLCALL
-XML_GetBase(XML_Parser p);
-
-
-Return the base for resolving relative URIs. -
- -
-int XMLCALL
-XML_GetSpecifiedAttributeCount(XML_Parser p);
-
-
-When attributes are reported to the start handler in the atts vector, -attributes that were explicitly set in the element occur before any -attributes that receive their value from default information in an -ATTLIST declaration. This function returns the number of attributes -that were explicitly set times two, thus giving the offset in the -atts array passed to the start tag handler of the first -attribute set due to defaults. It supplies information for the last -call to a start handler. If called inside a start handler, then that -means the current call. -
- -
-int XMLCALL
-XML_GetIdAttributeIndex(XML_Parser p);
-
-
-Returns the index of the ID attribute passed in the atts array in the -last call to XML_StartElementHandler, or -1 if there is no ID -attribute. If called inside a start handler, then that means the -current call. -
- -
-enum XML_Status XMLCALL
-XML_SetEncoding(XML_Parser p,
-                const XML_Char *encoding);
-
-
-Set the encoding to be used by the parser. It is equivalent to -passing a non-null encoding argument to the parser creation functions. -It must not be called after XML_Parse or XML_ParseBuffer have been called on the given parser. -Returns XML_STATUS_OK on success or -XML_STATUS_ERROR on error. -
- -
-int XMLCALL
-XML_SetParamEntityParsing(XML_Parser p,
-                          enum XML_ParamEntityParsing code);
-
-
-This enables parsing of parameter entities, including the external -parameter entity that is the external DTD subset, according to -code. -The choices for code are: -
    -
  • XML_PARAM_ENTITY_PARSING_NEVER
  • -
  • XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE
  • -
  • XML_PARAM_ENTITY_PARSING_ALWAYS
  • -
-
- -
-enum XML_Error XMLCALL
-XML_UseForeignDTD(XML_Parser parser, XML_Bool useDTD);
-
-
-

This function allows an application to provide an external subset -for the document type declaration for documents which do not specify -an external subset of their own. For documents which specify an -external subset in their DOCTYPE declaration, the application-provided -subset will be ignored. If the document does not contain a DOCTYPE -declaration at all and useDTD is true, the -application-provided subset will be parsed, but the -startDoctypeDeclHandler and -endDoctypeDeclHandler functions, if set, will not be -called. The setting of parameter entity parsing, controlled using -XML_SetParamEntityParsing, will be honored.

- -

The application-provided external subset is read by calling the -external entity reference handler set via XML_SetExternalEntityRefHandler with both -publicId and systemId set to NULL.

- -

If this function is called after parsing has begun, it returns -XML_ERROR_CANT_CHANGE_FEATURE_ONCE_PARSING and ignores -useDTD. If called when Expat has been compiled without -DTD support, it returns -XML_ERROR_FEATURE_REQUIRES_XML_DTD. Otherwise, it -returns XML_ERROR_NONE.

- -

Note: For the purpose of checking WFC: Entity Declared, passing -useDTD == XML_TRUE will make the parser behave as if -the document had a DTD with an external subset. This holds true even if -the external entity reference handler returns without action.

-
- -
-void XMLCALL
-XML_SetReturnNSTriplet(XML_Parser parser,
-                       int        do_nst);
-
-
-

-This function only has an effect when using a parser created with -XML_ParserCreateNS, -i.e. when namespace processing is in effect. The do_nst -sets whether or not prefixes are returned with names qualified with a -namespace prefix. If this function is called with do_nst -non-zero, then afterwards namespace qualified names (that is qualified -with a prefix as opposed to belonging to a default namespace) are -returned as a triplet with the three parts separated by the namespace -separator specified when the parser was created. The order of -returned parts is URI, local name, and prefix.

If -do_nst is zero, then namespaces are reported in the -default manner, URI then local_name separated by the namespace -separator.

-
- -
-void XMLCALL
-XML_DefaultCurrent(XML_Parser parser);
-
-
-This can be called within a handler for a start element, end element, -processing instruction or character data. It causes the corresponding -markup to be passed to the default handler set by XML_SetDefaultHandler or -XML_SetDefaultHandlerExpand. It does nothing if there is -not a default handler. -
- -
-XML_LChar * XMLCALL
-XML_ExpatVersion();
-
-
-Return the library version as a string (e.g. "expat_1.95.1"). -
- -
-struct XML_Expat_Version XMLCALL
-XML_ExpatVersionInfo();
-
-
-typedef struct {
-  int major;
-  int minor;
-  int micro;
-} XML_Expat_Version;
-
-
-Return the library version information as a structure. -Some macros are also defined that support compile-time tests of the -library version: -
    -
  • XML_MAJOR_VERSION
  • -
  • XML_MINOR_VERSION
  • -
  • XML_MICRO_VERSION
  • -
-Testing these constants is currently the best way to determine if -particular parts of the Expat API are available. -
- -
-const XML_Feature * XMLCALL
-XML_GetFeatureList();
-
-
-enum XML_FeatureEnum {
-  XML_FEATURE_END = 0,
-  XML_FEATURE_UNICODE,
-  XML_FEATURE_UNICODE_WCHAR_T,
-  XML_FEATURE_DTD,
-  XML_FEATURE_CONTEXT_BYTES,
-  XML_FEATURE_MIN_SIZE,
-  XML_FEATURE_SIZEOF_XML_CHAR,
-  XML_FEATURE_SIZEOF_XML_LCHAR,
-  XML_FEATURE_NS,
-  XML_FEATURE_LARGE_SIZE
-};
-
-typedef struct {
-  enum XML_FeatureEnum  feature;
-  XML_LChar            *name;
-  long int              value;
-} XML_Feature;
-
-
-

Returns a list of "feature" records, providing details on how -Expat was configured at compile time. Most applications should not -need to worry about this, but this information is otherwise not -available from Expat. This function allows code that does need to -check these features to do so at runtime.

- -

The return value is an array of XML_Feature, -terminated by a record with a feature of -XML_FEATURE_END and name of NULL, -identifying the feature-test macros Expat was compiled with. Since an -application that requires this kind of information needs to determine -the type of character the name points to, records for the -XML_FEATURE_SIZEOF_XML_CHAR and -XML_FEATURE_SIZEOF_XML_LCHAR will be located at the -beginning of the list, followed by XML_FEATURE_UNICODE -and XML_FEATURE_UNICODE_WCHAR_T, if they are present at -all.

- -

Some features have an associated value. If there isn't an -associated value, the value field is set to 0. At this -time, the following features have been defined to have values:

- -
-
XML_FEATURE_SIZEOF_XML_CHAR
-
The number of bytes occupied by one XML_Char - character.
-
XML_FEATURE_SIZEOF_XML_LCHAR
-
The number of bytes occupied by one XML_LChar - character.
-
XML_FEATURE_CONTEXT_BYTES
-
The maximum number of characters of context which can be - reported by XML_GetInputContext.
-
-
- -
-void XMLCALL
-XML_FreeContentModel(XML_Parser parser, XML_Content *model);
-
-
-Function to deallocate the model argument passed to the -XML_ElementDeclHandler callback set using XML_ElementDeclHandler. -This function should not be used for any other purpose. -
- -

The following functions allow external code to share the memory -allocator an XML_Parser has been configured to use. This -is especially useful for third-party libraries that interact with a -parser object created by application code, or heavily layered -applications. This can be essential when using dynamically loaded -libraries which use different C standard libraries (this can happen on -Windows, at least).

- -
-void * XMLCALL
-XML_MemMalloc(XML_Parser parser, size_t size);
-
-
-Allocate size bytes of memory using the allocator the -parser object has been configured to use. Returns a -pointer to the memory or NULL on failure. Memory allocated in this -way must be freed using XML_MemFree. -
- -
-void * XMLCALL
-XML_MemRealloc(XML_Parser parser, void *ptr, size_t size);
-
-
-Allocate size bytes of memory using the allocator the -parser object has been configured to use. -ptr must point to a block of memory allocated by XML_MemMalloc or -XML_MemRealloc, or be NULL. This function tries to -expand the block pointed to by ptr if possible. Returns -a pointer to the memory or NULL on failure. On success, the original -block has either been expanded or freed. On failure, the original -block has not been freed; the caller is responsible for freeing the -original block. Memory allocated in this way must be freed using -XML_MemFree. -
- -
-void XMLCALL
-XML_MemFree(XML_Parser parser, void *ptr);
-
-
-Free a block of memory pointed to by ptr. The block must -have been allocated by XML_MemMalloc or XML_MemRealloc, or be NULL. -
- -
-

Valid XHTML 1.0!

-
- - diff --git a/cextern/expat/doc/style.css b/cextern/expat/doc/style.css deleted file mode 100755 index 69df30bcecb2..000000000000 --- a/cextern/expat/doc/style.css +++ /dev/null @@ -1,101 +0,0 @@ -body { - background-color: white; - border: 0px; - margin: 0px; - padding: 0px; -} - -.corner { - width: 200px; - height: 80px; - text-align: center; -} - -.banner { - background-color: rgb(110,139,61); - color: rgb(255,236,176); - padding-left: 2em; -} - -.banner h1 { - font-size: 200%; -} - -.content { - padding: 0em 2em 1em 2em; -} - -.releaseno { - background-color: rgb(110,139,61); - color: rgb(255,236,176); - padding-bottom: 0.3em; - padding-top: 0.5em; - text-align: center; - font-weight: bold; -} - -.noborder { - border-width: 0px; -} - -.eg { - padding-left: 1em; - padding-top: .5em; - padding-bottom: .5em; - border: solid thin; - margin: 1em 0; - background-color: tan; - margin-left: 2em; - margin-right: 10%; -} - -.pseudocode { - padding-left: 1em; - padding-top: .5em; - padding-bottom: .5em; - border: solid thin; - margin: 1em 0; - background-color: rgb(250,220,180); - margin-left: 2em; - margin-right: 10%; -} - -.handler { - width: 100%; - border-top-width: thin; - margin-bottom: 1em; -} - -.handler p { - margin-left: 2em; -} - -.setter { - font-weight: bold; -} - -.signature { - color: navy; -} - -.fcndec { - width: 100%; - border-top-width: thin; - font-weight: bold; -} - -.fcndef { - margin-left: 2em; - margin-bottom: 2em; -} - -dd { - margin-bottom: 2em; -} - -.cpp-symbols dt { - font-family: monospace; -} -.cpp-symbols dd { - margin-bottom: 1em; -} diff --git a/cextern/expat/doc/valid-xhtml10.png b/cextern/expat/doc/valid-xhtml10.png deleted file mode 100755 index 4c23f48fe02a..000000000000 Binary files a/cextern/expat/doc/valid-xhtml10.png and /dev/null differ diff --git a/cextern/expat/doc/xmlwf.1 b/cextern/expat/doc/xmlwf.1 deleted file mode 100755 index 174719a709ea..000000000000 --- a/cextern/expat/doc/xmlwf.1 +++ /dev/null @@ -1,251 +0,0 @@ -.\" This manpage has been automatically generated by docbook2man -.\" from a DocBook document. This tool can be found at: -.\" -.\" Please send any bug reports, improvements, comments, patches, -.\" etc. to Steve Cheng . -.TH "XMLWF" "1" "24 January 2003" "" "" -.SH NAME -xmlwf \- Determines if an XML document is well-formed -.SH SYNOPSIS - -\fBxmlwf\fR [ \fB-s\fR] [ \fB-n\fR] [ \fB-p\fR] [ \fB-x\fR] [ \fB-e \fIencoding\fB\fR] [ \fB-w\fR] [ \fB-d \fIoutput-dir\fB\fR] [ \fB-c\fR] [ \fB-m\fR] [ \fB-r\fR] [ \fB-t\fR] [ \fB-v\fR] [ \fBfile ...\fR] - -.SH "DESCRIPTION" -.PP -\fBxmlwf\fR uses the Expat library to -determine if an XML document is well-formed. It is -non-validating. -.PP -If you do not specify any files on the command-line, and you -have a recent version of \fBxmlwf\fR, the -input file will be read from standard input. -.SH "WELL-FORMED DOCUMENTS" -.PP -A well-formed document must adhere to the -following rules: -.TP 0.2i -\(bu -The file begins with an XML declaration. For instance, -. -\fBNOTE:\fR -\fBxmlwf\fR does not currently -check for a valid XML declaration. -.TP 0.2i -\(bu -Every start tag is either empty () -or has a corresponding end tag. -.TP 0.2i -\(bu -There is exactly one root element. This element must contain -all other elements in the document. Only comments, white -space, and processing instructions may come after the close -of the root element. -.TP 0.2i -\(bu -All elements nest properly. -.TP 0.2i -\(bu -All attribute values are enclosed in quotes (either single -or double). -.PP -If the document has a DTD, and it strictly complies with that -DTD, then the document is also considered \fBvalid\fR. -\fBxmlwf\fR is a non-validating parser -- -it does not check the DTD. However, it does support -external entities (see the \fB-x\fR option). -.SH "OPTIONS" -.PP -When an option includes an argument, you may specify the argument either -separately ("\fB-d\fR output") or concatenated with the -option ("\fB-d\fRoutput"). \fBxmlwf\fR -supports both. -.TP -\fB-c\fR -If the input file is well-formed and \fBxmlwf\fR -doesn't encounter any errors, the input file is simply copied to -the output directory unchanged. -This implies no namespaces (turns off \fB-n\fR) and -requires \fB-d\fR to specify an output file. -.TP -\fB-d output-dir\fR -Specifies a directory to contain transformed -representations of the input files. -By default, \fB-d\fR outputs a canonical representation -(described below). -You can select different output formats using \fB-c\fR -and \fB-m\fR. - -The output filenames will -be exactly the same as the input filenames or "STDIN" if the input is -coming from standard input. Therefore, you must be careful that the -output file does not go into the same directory as the input -file. Otherwise, \fBxmlwf\fR will delete the -input file before it generates the output file (just like running -cat < file > file in most shells). - -Two structurally equivalent XML documents have a byte-for-byte -identical canonical XML representation. -Note that ignorable white space is considered significant and -is treated equivalently to data. -More on canonical XML can be found at -http://www.jclark.com/xml/canonxml.html . -.TP -\fB-e encoding\fR -Specifies the character encoding for the document, overriding -any document encoding declaration. \fBxmlwf\fR -supports four built-in encodings: -US-ASCII, -UTF-8, -UTF-16, and -ISO-8859-1. -Also see the \fB-w\fR option. -.TP -\fB-m\fR -Outputs some strange sort of XML file that completely -describes the input file, including character positions. -Requires \fB-d\fR to specify an output file. -.TP -\fB-n\fR -Turns on namespace processing. (describe namespaces) -\fB-c\fR disables namespaces. -.TP -\fB-p\fR -Tells xmlwf to process external DTDs and parameter -entities. - -Normally \fBxmlwf\fR never parses parameter -entities. \fB-p\fR tells it to always parse them. -\fB-p\fR implies \fB-x\fR. -.TP -\fB-r\fR -Normally \fBxmlwf\fR memory-maps the XML file -before parsing; this can result in faster parsing on many -platforms. -\fB-r\fR turns off memory-mapping and uses normal file -IO calls instead. -Of course, memory-mapping is automatically turned off -when reading from standard input. - -Use of memory-mapping can cause some platforms to report -substantially higher memory usage for -\fBxmlwf\fR, but this appears to be a matter of -the operating system reporting memory in a strange way; there is -not a leak in \fBxmlwf\fR. -.TP -\fB-s\fR -Prints an error if the document is not standalone. -A document is standalone if it has no external subset and no -references to parameter entities. -.TP -\fB-t\fR -Turns on timings. This tells Expat to parse the entire file, -but not perform any processing. -This gives a fairly accurate idea of the raw speed of Expat itself -without client overhead. -\fB-t\fR turns off most of the output options -(\fB-d\fR, \fB-m\fR, \fB-c\fR, -\&...). -.TP -\fB-v\fR -Prints the version of the Expat library being used, including some -information on the compile-time configuration of the library, and -then exits. -.TP -\fB-w\fR -Enables support for Windows code pages. -Normally, \fBxmlwf\fR will throw an error if it -runs across an encoding that it is not equipped to handle itself. With -\fB-w\fR, xmlwf will try to use a Windows code -page. See also \fB-e\fR. -.TP -\fB-x\fR -Turns on parsing external entities. - -Non-validating parsers are not required to resolve external -entities, or even expand entities at all. -Expat always expands internal entities (?), -but external entity parsing must be enabled explicitly. - -External entities are simply entities that obtain their -data from outside the XML file currently being parsed. - -This is an example of an internal entity: - -.nf - -.fi - -And here are some examples of external entities: - -.nf - (parsed) - (unparsed) -.fi -.TP -\fB--\fR -(Two hyphens.) -Terminates the list of options. This is only needed if a filename -starts with a hyphen. For example: - -.nf -xmlwf -- -myfile.xml -.fi - -will run \fBxmlwf\fR on the file -\fI-myfile.xml\fR. -.PP -Older versions of \fBxmlwf\fR do not support -reading from standard input. -.SH "OUTPUT" -.PP -If an input file is not well-formed, -\fBxmlwf\fR prints a single line describing -the problem to standard output. If a file is well formed, -\fBxmlwf\fR outputs nothing. -Note that the result code is \fBnot\fR set. -.SH "BUGS" -.PP -According to the W3C standard, an XML file without a -declaration at the beginning is not considered well-formed. -However, \fBxmlwf\fR allows this to pass. -.PP -\fBxmlwf\fR returns a 0 - noerr result, -even if the file is not well-formed. There is no good way for -a program to use \fBxmlwf\fR to quickly -check a file -- it must parse \fBxmlwf\fR's -standard output. -.PP -The errors should go to standard error, not standard output. -.PP -There should be a way to get \fB-d\fR to send its -output to standard output rather than forcing the user to send -it to a file. -.PP -I have no idea why anyone would want to use the -\fB-d\fR, \fB-c\fR, and -\fB-m\fR options. If someone could explain it to -me, I'd like to add this information to this manpage. -.SH "ALTERNATIVES" -.PP -Here are some XML validators on the web: - -.nf -http://www.hcrc.ed.ac.uk/~richard/xml-check.html -http://www.stg.brown.edu/service/xmlvalid/ -http://www.scripting.com/frontier5/xml/code/xmlValidator.html -http://www.xml.com/pub/a/tools/ruwf/check.html -.fi -.SH "SEE ALSO" -.PP - -.nf -The Expat home page: http://www.libexpat.org/ -The W3 XML specification: http://www.w3.org/TR/REC-xml -.fi -.SH "AUTHOR" -.PP -This manual page was written by Scott Bronson for -the Debian GNU/Linux system (but may be used by others). Permission is -granted to copy, distribute and/or modify this document under -the terms of the GNU Free Documentation -License, Version 1.1. diff --git a/cextern/expat/doc/xmlwf.sgml b/cextern/expat/doc/xmlwf.sgml deleted file mode 100755 index 139c95e1f111..000000000000 --- a/cextern/expat/doc/xmlwf.sgml +++ /dev/null @@ -1,473 +0,0 @@ - manpage.1'. You may view - the manual page with: `docbook-to-man manpage.sgml | nroff -man | - less'. A typical entry in a Makefile or Makefile.am is: - -manpage.1: manpage.sgml - docbook-to-man $< > $@ - --> - - - Scott"> - Bronson"> - - December 5, 2001"> - - 1"> - bronson@rinspin.com"> - - XMLWF"> - - - Debian GNU/Linux"> - GNU"> -]> - - - -
- &dhemail; -
- - &dhfirstname; - &dhsurname; - - - 2001 - &dhusername; - - &dhdate; -
- - &dhucpackage; - - &dhsection; - - - &dhpackage; - - Determines if an XML document is well-formed - - - - &dhpackage; - - - - - - - - - - - - - - - - - - file ... - - - - - DESCRIPTION - - - &dhpackage; uses the Expat library to - determine if an XML document is well-formed. It is - non-validating. - - - - If you do not specify any files on the command-line, and you - have a recent version of &dhpackage;, the - input file will be read from standard input. - - - - - - WELL-FORMED DOCUMENTS - - - A well-formed document must adhere to the - following rules: - - - - - The file begins with an XML declaration. For instance, - <?xml version="1.0" standalone="yes"?>. - NOTE: - &dhpackage; does not currently - check for a valid XML declaration. - - - Every start tag is either empty (<tag/>) - or has a corresponding end tag. - - - There is exactly one root element. This element must contain - all other elements in the document. Only comments, white - space, and processing instructions may come after the close - of the root element. - - - All elements nest properly. - - - All attribute values are enclosed in quotes (either single - or double). - - - - - If the document has a DTD, and it strictly complies with that - DTD, then the document is also considered valid. - &dhpackage; is a non-validating parser -- - it does not check the DTD. However, it does support - external entities (see the option). - - - - - OPTIONS - - -When an option includes an argument, you may specify the argument either -separately (" output") or concatenated with the -option ("output"). &dhpackage; -supports both. - - - - - - - - - If the input file is well-formed and &dhpackage; - doesn't encounter any errors, the input file is simply copied to - the output directory unchanged. - This implies no namespaces (turns off ) and - requires to specify an output file. - - - - - - - - - Specifies a directory to contain transformed - representations of the input files. - By default, outputs a canonical representation - (described below). - You can select different output formats using - and . - - - The output filenames will - be exactly the same as the input filenames or "STDIN" if the input is - coming from standard input. Therefore, you must be careful that the - output file does not go into the same directory as the input - file. Otherwise, &dhpackage; will delete the - input file before it generates the output file (just like running - cat < file > file in most shells). - - - Two structurally equivalent XML documents have a byte-for-byte - identical canonical XML representation. - Note that ignorable white space is considered significant and - is treated equivalently to data. - More on canonical XML can be found at - http://www.jclark.com/xml/canonxml.html . - - - - - - - - - Specifies the character encoding for the document, overriding - any document encoding declaration. &dhpackage; - supports four built-in encodings: - US-ASCII, - UTF-8, - UTF-16, and - ISO-8859-1. - Also see the option. - - - - - - - - - Outputs some strange sort of XML file that completely - describes the the input file, including character postitions. - Requires to specify an output file. - - - - - - - - - Turns on namespace processing. (describe namespaces) - disables namespaces. - - - - - - - - - Tells xmlwf to process external DTDs and parameter - entities. - - - Normally &dhpackage; never parses parameter - entities. tells it to always parse them. - implies . - - - - - - - - - Normally &dhpackage; memory-maps the XML file - before parsing; this can result in faster parsing on many - platforms. - turns off memory-mapping and uses normal file - IO calls instead. - Of course, memory-mapping is automatically turned off - when reading from standard input. - - - Use of memory-mapping can cause some platforms to report - substantially higher memory usage for - &dhpackage;, but this appears to be a matter of - the operating system reporting memory in a strange way; there is - not a leak in &dhpackage;. - - - - - - - - - Prints an error if the document is not standalone. - A document is standalone if it has no external subset and no - references to parameter entities. - - - - - - - - - Turns on timings. This tells Expat to parse the entire file, - but not perform any processing. - This gives a fairly accurate idea of the raw speed of Expat itself - without client overhead. - turns off most of the output options - (, , , - ...). - - - - - - - - - Prints the version of the Expat library being used, including some - information on the compile-time configuration of the library, and - then exits. - - - - - - - - - Enables support for Windows code pages. - Normally, &dhpackage; will throw an error if it - runs across an encoding that it is not equipped to handle itself. With - , &dhpackage; will try to use a Windows code - page. See also . - - - - - - - - - Turns on parsing external entities. - - - Non-validating parsers are not required to resolve external - entities, or even expand entities at all. - Expat always expands internal entities (?), - but external entity parsing must be enabled explicitly. - - - External entities are simply entities that obtain their - data from outside the XML file currently being parsed. - - - This is an example of an internal entity: - -<!ENTITY vers '1.0.2'> - - - - And here are some examples of external entities: - - -<!ENTITY header SYSTEM "header-&vers;.xml"> (parsed) -<!ENTITY logo SYSTEM "logo.png" PNG> (unparsed) - - - - - - - - - - - (Two hyphens.) - Terminates the list of options. This is only needed if a filename - starts with a hyphen. For example: - - -&dhpackage; -- -myfile.xml - - - will run &dhpackage; on the file - -myfile.xml. - - - - - - - Older versions of &dhpackage; do not support - reading from standard input. - - - - - OUTPUT - - If an input file is not well-formed, - &dhpackage; prints a single line describing - the problem to standard output. If a file is well formed, - &dhpackage; outputs nothing. - Note that the result code is not set. - - - - - BUGS - - According to the W3C standard, an XML file without a - declaration at the beginning is not considered well-formed. - However, &dhpackage; allows this to pass. - - - &dhpackage; returns a 0 - noerr result, - even if the file is not well-formed. There is no good way for - a program to use &dhpackage; to quickly - check a file -- it must parse &dhpackage;'s - standard output. - - - The errors should go to standard error, not standard output. - - - There should be a way to get to send its - output to standard output rather than forcing the user to send - it to a file. - - - I have no idea why anyone would want to use the - , , and - options. If someone could explain it to - me, I'd like to add this information to this manpage. - - - - - ALTERNATIVES - - Here are some XML validators on the web: - - -http://www.hcrc.ed.ac.uk/~richard/xml-check.html -http://www.stg.brown.edu/service/xmlvalid/ -http://www.scripting.com/frontier5/xml/code/xmlValidator.html -http://www.xml.com/pub/a/tools/ruwf/check.html - - - - - - - SEE ALSO - - - -The Expat home page: http://www.libexpat.org/ -The W3 XML specification: http://www.w3.org/TR/REC-xml - - - - - - - AUTHOR - - This manual page was written by &dhusername; &dhemail; for - the &debian; system (but may be used by others). Permission is - granted to copy, distribute and/or modify this document under - the terms of the GNU Free Documentation - License, Version 1.1. - - -
- - diff --git a/cextern/expat/examples/elements.c b/cextern/expat/examples/elements.c deleted file mode 100755 index 6b8f85501b6c..000000000000 --- a/cextern/expat/examples/elements.c +++ /dev/null @@ -1,65 +0,0 @@ -/* This is simple demonstration of how to use expat. This program - reads an XML document from standard input and writes a line with - the name of each element to standard output indenting child - elements by one tab stop more than their parent element. - It must be used with Expat compiled for UTF-8 output. -*/ - -#include -#include "expat.h" - -#if defined(__amigaos__) && defined(__USE_INLINE__) -#include -#endif - -#ifdef XML_LARGE_SIZE -#if defined(XML_USE_MSC_EXTENSIONS) && _MSC_VER < 1400 -#define XML_FMT_INT_MOD "I64" -#else -#define XML_FMT_INT_MOD "ll" -#endif -#else -#define XML_FMT_INT_MOD "l" -#endif - -static void XMLCALL -startElement(void *userData, const char *name, const char **atts) -{ - int i; - int *depthPtr = (int *)userData; - for (i = 0; i < *depthPtr; i++) - putchar('\t'); - puts(name); - *depthPtr += 1; -} - -static void XMLCALL -endElement(void *userData, const char *name) -{ - int *depthPtr = (int *)userData; - *depthPtr -= 1; -} - -int -main(int argc, char *argv[]) -{ - char buf[BUFSIZ]; - XML_Parser parser = XML_ParserCreate(NULL); - int done; - int depth = 0; - XML_SetUserData(parser, &depth); - XML_SetElementHandler(parser, startElement, endElement); - do { - int len = (int)fread(buf, 1, sizeof(buf), stdin); - done = len < sizeof(buf); - if (XML_Parse(parser, buf, len, done) == XML_STATUS_ERROR) { - fprintf(stderr, - "%s at line %" XML_FMT_INT_MOD "u\n", - XML_ErrorString(XML_GetErrorCode(parser)), - XML_GetCurrentLineNumber(parser)); - return 1; - } - } while (!done); - XML_ParserFree(parser); - return 0; -} diff --git a/cextern/expat/examples/elements.dsp b/cextern/expat/examples/elements.dsp deleted file mode 100755 index 6730358dfc5e..000000000000 --- a/cextern/expat/examples/elements.dsp +++ /dev/null @@ -1,103 +0,0 @@ -# Microsoft Developer Studio Project File - Name="elements" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Console Application" 0x0103 - -CFG=elements - Win32 Debug -!MESSAGE This is not a valid makefile. To build this project using NMAKE, -!MESSAGE use the Export Makefile command and run -!MESSAGE -!MESSAGE NMAKE /f "elements.mak". -!MESSAGE -!MESSAGE You can specify a configuration when running NMAKE -!MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE -!MESSAGE NMAKE /f "elements.mak" CFG="elements - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "elements - Win32 Release" (based on "Win32 (x86) Console Application") -!MESSAGE "elements - Win32 Debug" (based on "Win32 (x86) Console Application") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -CPP=cl.exe -RSC=rc.exe - -!IF "$(CFG)" == "elements - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "Release" -# PROP BASE Intermediate_Dir "Release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "..\win32\bin\Release" -# PROP Intermediate_Dir "..\win32\tmp\Release-elements" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\lib" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /D "XML_STATIC" /FD /c -# SUBTRACT CPP /X /YX -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 -# ADD LINK32 odbccp32.lib libexpatMT.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib /nologo /subsystem:console /pdb:none /machine:I386 /libpath:"..\win32\bin\Release" - -!ELSEIF "$(CFG)" == "elements - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "Debug" -# PROP BASE Intermediate_Dir "Debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "..\win32\bin\Debug" -# PROP Intermediate_Dir "..\win32\tmp\Debug-elements" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W3 /GX /ZI /Od /I "..\lib" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /D "XML_STATIC" /FR /FD /GZ /c -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /d "_DEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept -# ADD LINK32 odbccp32.lib libexpatMT.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib /nologo /subsystem:console /pdb:none /debug /machine:I386 /libpath:"..\win32\bin\Debug" - -!ENDIF - -# Begin Target - -# Name "elements - Win32 Release" -# Name "elements - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=.\elements.c -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# End Group -# Begin Group "Resource Files" - -# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" -# End Group -# End Target -# End Project diff --git a/cextern/expat/examples/outline.c b/cextern/expat/examples/outline.c deleted file mode 100755 index 3a3c8385512a..000000000000 --- a/cextern/expat/examples/outline.c +++ /dev/null @@ -1,106 +0,0 @@ -/***************************************************************** - * outline.c - * - * Copyright 1999, Clark Cooper - * All rights reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the license contained in the - * COPYING file that comes with the expat distribution. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - * Read an XML document from standard input and print an element - * outline on standard output. - * Must be used with Expat compiled for UTF-8 output. - */ - - -#include -#include - -#if defined(__amigaos__) && defined(__USE_INLINE__) -#include -#endif - -#ifdef XML_LARGE_SIZE -#if defined(XML_USE_MSC_EXTENSIONS) && _MSC_VER < 1400 -#define XML_FMT_INT_MOD "I64" -#else -#define XML_FMT_INT_MOD "ll" -#endif -#else -#define XML_FMT_INT_MOD "l" -#endif - -#define BUFFSIZE 8192 - -char Buff[BUFFSIZE]; - -int Depth; - -static void XMLCALL -start(void *data, const char *el, const char **attr) -{ - int i; - - for (i = 0; i < Depth; i++) - printf(" "); - - printf("%s", el); - - for (i = 0; attr[i]; i += 2) { - printf(" %s='%s'", attr[i], attr[i + 1]); - } - - printf("\n"); - Depth++; -} - -static void XMLCALL -end(void *data, const char *el) -{ - Depth--; -} - -int -main(int argc, char *argv[]) -{ - XML_Parser p = XML_ParserCreate(NULL); - if (! p) { - fprintf(stderr, "Couldn't allocate memory for parser\n"); - exit(-1); - } - - XML_SetElementHandler(p, start, end); - - for (;;) { - int done; - int len; - - len = (int)fread(Buff, 1, BUFFSIZE, stdin); - if (ferror(stdin)) { - fprintf(stderr, "Read error\n"); - exit(-1); - } - done = feof(stdin); - - if (XML_Parse(p, Buff, len, done) == XML_STATUS_ERROR) { - fprintf(stderr, "Parse error at line %" XML_FMT_INT_MOD "u:\n%s\n", - XML_GetCurrentLineNumber(p), - XML_ErrorString(XML_GetErrorCode(p))); - exit(-1); - } - - if (done) - break; - } - XML_ParserFree(p); - return 0; -} diff --git a/cextern/expat/examples/outline.dsp b/cextern/expat/examples/outline.dsp deleted file mode 100755 index 62d1932af1fa..000000000000 --- a/cextern/expat/examples/outline.dsp +++ /dev/null @@ -1,103 +0,0 @@ -# Microsoft Developer Studio Project File - Name="outline" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Console Application" 0x0103 - -CFG=outline - Win32 Debug -!MESSAGE This is not a valid makefile. To build this project using NMAKE, -!MESSAGE use the Export Makefile command and run -!MESSAGE -!MESSAGE NMAKE /f "outline.mak". -!MESSAGE -!MESSAGE You can specify a configuration when running NMAKE -!MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE -!MESSAGE NMAKE /f "outline.mak" CFG="outline - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "outline - Win32 Release" (based on "Win32 (x86) Console Application") -!MESSAGE "outline - Win32 Debug" (based on "Win32 (x86) Console Application") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -CPP=cl.exe -RSC=rc.exe - -!IF "$(CFG)" == "outline - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "Release" -# PROP BASE Intermediate_Dir "Release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "..\win32\bin\Release" -# PROP Intermediate_Dir "..\win32\tmp\Release-outline" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\lib" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /FD /c -# SUBTRACT CPP /X /YX -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /pdb:none /machine:I386 - -!ELSEIF "$(CFG)" == "outline - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "Debug" -# PROP BASE Intermediate_Dir "Debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "..\win32\bin\Debug" -# PROP Intermediate_Dir "..\win32\tmp\Debug-outline" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\lib" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FD /GZ /c -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /d "_DEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /pdb:none /debug /machine:I386 - -!ENDIF - -# Begin Target - -# Name "outline - Win32 Release" -# Name "outline - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=.\outline.c -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# End Group -# Begin Group "Resource Files" - -# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" -# End Group -# End Target -# End Project diff --git a/cextern/expat/expat.dsw b/cextern/expat/expat.dsw deleted file mode 100755 index 9282da5a72d4..000000000000 --- a/cextern/expat/expat.dsw +++ /dev/null @@ -1,110 +0,0 @@ -Microsoft Developer Studio Workspace File, Format Version 6.00 -# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! - -############################################################################### - -Project: "elements"=.\examples\elements.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ - Begin Project Dependency - Project_Dep_Name expat_static - End Project Dependency -}}} - -############################################################################### - -Project: "expat"=.\lib\expat.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "expat_static"=.\lib\expat_static.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "expatw"=.\lib\expatw.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "expatw_static"=.\lib\expatw_static.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "outline"=.\examples\outline.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ - Begin Project Dependency - Project_Dep_Name expat - End Project Dependency -}}} - -############################################################################### - -Project: "xmlwf"=.\xmlwf\xmlwf.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ - Begin Project Dependency - Project_Dep_Name expat - End Project Dependency -}}} - -############################################################################### - -Global: - -Package=<5> -{{{ -}}} - -Package=<3> -{{{ -}}} - -############################################################################### - diff --git a/cextern/expat/expat_config.h.in b/cextern/expat/expat_config.h.in deleted file mode 100755 index 8c3698afd93a..000000000000 --- a/cextern/expat/expat_config.h.in +++ /dev/null @@ -1,92 +0,0 @@ -/* expat_config.h.in. Generated from configure.in by autoheader. */ - -/* 1234 = LIL_ENDIAN, 4321 = BIGENDIAN */ -#undef BYTEORDER - -/* Define to 1 if you have the `bcopy' function. */ -#undef HAVE_BCOPY - -/* Define to 1 if you have the header file. */ -#undef HAVE_DLFCN_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_FCNTL_H - -/* Define to 1 if you have the `getpagesize' function. */ -#undef HAVE_GETPAGESIZE - -/* Define to 1 if you have the header file. */ -#undef HAVE_INTTYPES_H - -/* Define to 1 if you have the `memmove' function. */ -#undef HAVE_MEMMOVE - -/* Define to 1 if you have the header file. */ -#undef HAVE_MEMORY_H - -/* Define to 1 if you have a working `mmap' system call. */ -#undef HAVE_MMAP - -/* Define to 1 if you have the header file. */ -#undef HAVE_STDINT_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_STDLIB_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_STRINGS_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_STRING_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_SYS_STAT_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_SYS_TYPES_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_UNISTD_H - -/* Define to the address where bug reports for this package should be sent. */ -#undef PACKAGE_BUGREPORT - -/* Define to the full name of this package. */ -#undef PACKAGE_NAME - -/* Define to the full name and version of this package. */ -#undef PACKAGE_STRING - -/* Define to the one symbol short name of this package. */ -#undef PACKAGE_TARNAME - -/* Define to the version of this package. */ -#undef PACKAGE_VERSION - -/* Define to 1 if you have the ANSI C header files. */ -#undef STDC_HEADERS - -/* whether byteorder is bigendian */ -#undef WORDS_BIGENDIAN - -/* Define to specify how much context to retain around the current parse - point. */ -#undef XML_CONTEXT_BYTES - -/* Define to make parameter entity parsing functionality available. */ -#undef XML_DTD - -/* Define to make XML Namespaces functionality available. */ -#undef XML_NS - -/* Define to __FUNCTION__ or "" if `__func__' does not conform to ANSI C. */ -#undef __func__ - -/* Define to empty if `const' does not conform to ANSI C. */ -#undef const - -/* Define to `long' if does not define. */ -#undef off_t - -/* Define to `unsigned' if does not define. */ -#undef size_t diff --git a/cextern/expat/lib/Makefile.MPW b/cextern/expat/lib/Makefile.MPW deleted file mode 100755 index 046af005147b..000000000000 --- a/cextern/expat/lib/Makefile.MPW +++ /dev/null @@ -1,206 +0,0 @@ -# File: Makefile.MPW -# Targets: All, Dynamic, Static (and Clean, Clean-All) -# Created: Tuesday, July 02, 2002 -# -# MPW Makefile for building expat under the "classic" (i.e. pre-X) Mac OS -# Copyright Š 2002 Daryle Walker -# Portions Copyright Š 2002 Thomas Wegner -# See the COPYING file for distribution information -# -# Description: -# This Makefile lets you build static, dynamic (i.e. shared) and stub -# versions of the expat library as well as the elements.c and outline.c -# examples (built as tools for MPW). This is for PPC only; it should be -# no problem to build a 68K version of the expat library, though. -# -# Usage: -# Buildprogram All -# or Buildprogram Dynamic -# or Buildprogram Static -# -# Note: You first have to rename this file to "Makefile", or the Buildprogram -# commando will not recognize it. -# - -MAKEFILE = Makefile -ĨMondoBuildĨ = {MAKEFILE} # Make blank to avoid rebuilds when makefile is modified - -ObjDir = : -SrcDir = : -HdrDir = : - -ToolDir = ::examples: - -Includes = -i {HdrDir} - -Sym-PPC = -sym off - -Defines = -d MACOS_CLASSIC - -PPCCOptions = {Includes} {Sym-PPC} -w 35 {Defines} - -FragName = libexpat - - -### Source Files ### - -SrcFiles = ļ - "{SrcDir}xmlparse.c" ļ - "{SrcDir}xmlrole.c" ļ - "{SrcDir}xmltok.c" - -ToolSrcFiles = ļ - "{ToolDir}elements.c" ļ - "{ToolDir}outline.c" - - -### Object Files ### - -ObjFiles-PPC = ļ - "{ObjDir}xmlparse.c.o" ļ - "{ObjDir}xmlrole.c.o" ļ - "{ObjDir}xmltok.c.o" - -ElementToolObjFile = "{ObjDir}elements.c.o" - -OutlineToolObjFile = "{ObjDir}outline.c.o" - - -### Libraries ### - -StLibFiles-PPC = ļ - "{PPCLibraries}StdCRuntime.o" ļ - "{PPCLibraries}PPCCRuntime.o" ļ - "{PPCLibraries}PPCToolLibs.o" - -ShLibFiles-PPC = ļ - "{SharedLibraries}InterfaceLib" ļ - "{SharedLibraries}StdCLib" ļ - "{SharedLibraries}MathLib" - -LibFiles-PPC = ļ - {StLibFiles-PPC} ļ - {ShLibFiles-PPC} - - -### Special Files ### - -ExportFile = "{ObjDir}{FragName}.exp" - -StLibFile = "{ObjDir}{FragName}.MrC.o" - -ShLibFile = "{ObjDir}{FragName}" - -StubFile = "{ObjDir}{FragName}.stub" - -ElementsTool = "{ToolDir}elements" - -OutlineTool = "{ToolDir}outline" - - -### Default Rules ### - -.c.o Ä .c {ĨMondoBuildĨ} - {PPCC} {depDir}{default}.c -o {targDir}{default}.c.o {PPCCOptions} - - -### Build Rules ### - -All Ä Dynamic {ElementsTool} {OutlineTool} - -Static Ä {StLibFile} - -Dynamic Ä Static {ShLibFile} {StubFile} - -{StLibFile} ÄÄ {ObjFiles-PPC} {StLibFiles-PPC} {ĨMondoBuildĨ} - PPCLink ļ - -o {Targ} ļ - {ObjFiles-PPC} ļ - {StLibFiles-PPC} ļ - {Sym-PPC} ļ - -mf -d ļ - -t 'XCOF' ļ - -c 'MPS ' ļ - -xm l - -{ShLibFile} ÄÄ {StLibFile} {ShLibFiles-PPC} {ExportFile} {ĨMondoBuildĨ} - PPCLink ļ - -o {Targ} ļ - {StLibFile} ļ - {ShLibFiles-PPC} ļ - {Sym-PPC} ļ - -@export {ExportFile} ļ - -fragname {FragName} ļ - -mf -d ļ - -t 'shlb' ļ - -c '????' ļ - -xm s - -{StubFile} ÄÄ {ShLibFile} {ĨMondoBuildĨ} - shlb2stub -o {Targ} {ShLibFile} - -{ElementsTool} ÄÄ {ElementToolObjFile} {StubFile} {LibFiles-PPC} {ĨMondoBuildĨ} - PPCLink ļ - -o {Targ} ļ - {ElementToolObjFile} ļ - {StLibFile} ļ - {LibFiles-PPC} ļ - {Sym-PPC} ļ - -mf -d ļ - -t 'MPST' ļ - -c 'MPS ' - -{OutlineTool} ÄÄ {OutlineToolObjFile} {StubFile} {LibFiles-PPC} {ĨMondoBuildĨ} - PPCLink ļ - -o {Targ} ļ - {OutlineToolObjFile} ļ - {StLibFile} ļ - {LibFiles-PPC} ļ - {Sym-PPC} ļ - -mf -d ļ - -t 'MPST' ļ - -c 'MPS ' - - -### Special Rules ### - -{ExportFile} ÄÄ "{HdrDir}expat.h" {ĨMondoBuildĨ} - StreamEdit -d ļ - -e "/Ĩ('XMLPARSEAPI('Å') ')Į0,1Č'XML_'([A-Za-z0-9_]+)¨1'('/ Print 'XML_' ¨1" ļ - "{HdrDir}expat.h" > {Targ} - - -### Required Dependencies ### - -"{ObjDir}xmlparse.c.o" Ä "{SrcDir}xmlparse.c" -"{ObjDir}xmlrole.c.o" Ä "{SrcDir}xmlrole.c" -"{ObjDir}xmltok.c.o" Ä "{SrcDir}xmltok.c" - -"{ObjDir}elements.c.o" Ä "{ToolDir}elements.c" -"{ObjDir}outline.c.o" Ä "{ToolDir}outline.c" - - -### Optional Dependencies ### -### Build this target to clean out generated intermediate files. ### - -Clean Ä - Delete {ObjFiles-PPC} {ExportFile} {ElementToolObjFile} {OutlineToolObjFile} - -### Build this target to clean out all generated files. ### - -Clean-All Ä Clean - Delete {StLibFile} {ShLibFile} {StubFile} {ElementsTool} {OutlineTool} - -### Build this target to generate "include file" dependencies. ### - -Dependencies Ä $OutOfDate - MakeDepend ļ - -append {MAKEFILE} ļ - -ignore "{CIncludes}" ļ - -objdir "{ObjDir}" ļ - -objext .o ļ - {Defines} ļ - {Includes} ļ - {SrcFiles} - - diff --git a/cextern/expat/lib/amigaconfig.h b/cextern/expat/lib/amigaconfig.h deleted file mode 100755 index 86c611504025..000000000000 --- a/cextern/expat/lib/amigaconfig.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef AMIGACONFIG_H -#define AMIGACONFIG_H - -/* 1234 = LIL_ENDIAN, 4321 = BIGENDIAN */ -#define BYTEORDER 4321 - -/* Define to 1 if you have the `bcopy' function. */ -#define HAVE_BCOPY 1 - -/* Define to 1 if you have the header file. */ -#undef HAVE_CHECK_H - -/* Define to 1 if you have the `memmove' function. */ -#define HAVE_MEMMOVE 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_UNISTD_H 1 - -/* whether byteorder is bigendian */ -#define WORDS_BIGENDIAN - -/* Define to specify how much context to retain around the current parse - point. */ -#define XML_CONTEXT_BYTES 1024 - -/* Define to make parameter entity parsing functionality available. */ -#define XML_DTD - -/* Define to make XML Namespaces functionality available. */ -#define XML_NS - -#endif /* AMIGACONFIG_H */ diff --git a/cextern/expat/lib/ascii.h b/cextern/expat/lib/ascii.h old mode 100755 new mode 100644 index d10530b09bde..1f594d2e54b4 --- a/cextern/expat/lib/ascii.h +++ b/cextern/expat/lib/ascii.h @@ -1,5 +1,36 @@ -/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd - See the file COPYING for copying permission. +/* + __ __ _ + ___\ \/ /_ __ __ _| |_ + / _ \\ /| '_ \ / _` | __| + | __// \| |_) | (_| | |_ + \___/_/\_\ .__/ \__,_|\__| + |_| XML parser + + Copyright (c) 1999-2000 Thai Open Source Software Center Ltd + Copyright (c) 2000 Clark Cooper + Copyright (c) 2002 Fred L. Drake, Jr. + Copyright (c) 2007 Karl Waclawek + Copyright (c) 2017 Sebastian Pipping + Licensed under the MIT license: + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to permit + persons to whom the Software is furnished to do so, subject to the + following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + USE OR OTHER DEALINGS IN THE SOFTWARE. */ #define ASCII_A 0x41 diff --git a/cextern/expat/lib/asciitab.h b/cextern/expat/lib/asciitab.h old mode 100755 new mode 100644 index 79a15c28ca14..af766fb24785 --- a/cextern/expat/lib/asciitab.h +++ b/cextern/expat/lib/asciitab.h @@ -1,36 +1,66 @@ -/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd - See the file COPYING for copying permission. +/* + __ __ _ + ___\ \/ /_ __ __ _| |_ + / _ \\ /| '_ \ / _` | __| + | __// \| |_) | (_| | |_ + \___/_/\_\ .__/ \__,_|\__| + |_| XML parser + + Copyright (c) 1997-2000 Thai Open Source Software Center Ltd + Copyright (c) 2000 Clark Cooper + Copyright (c) 2002 Fred L. Drake, Jr. + Copyright (c) 2017 Sebastian Pipping + Licensed under the MIT license: + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to permit + persons to whom the Software is furnished to do so, subject to the + following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* 0x00 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, -/* 0x04 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, -/* 0x08 */ BT_NONXML, BT_S, BT_LF, BT_NONXML, -/* 0x0C */ BT_NONXML, BT_CR, BT_NONXML, BT_NONXML, -/* 0x10 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, -/* 0x14 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, -/* 0x18 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, -/* 0x1C */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, -/* 0x20 */ BT_S, BT_EXCL, BT_QUOT, BT_NUM, -/* 0x24 */ BT_OTHER, BT_PERCNT, BT_AMP, BT_APOS, -/* 0x28 */ BT_LPAR, BT_RPAR, BT_AST, BT_PLUS, -/* 0x2C */ BT_COMMA, BT_MINUS, BT_NAME, BT_SOL, -/* 0x30 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT, -/* 0x34 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT, -/* 0x38 */ BT_DIGIT, BT_DIGIT, BT_COLON, BT_SEMI, -/* 0x3C */ BT_LT, BT_EQUALS, BT_GT, BT_QUEST, -/* 0x40 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX, -/* 0x44 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT, -/* 0x48 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, -/* 0x4C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, -/* 0x50 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, -/* 0x54 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, -/* 0x58 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_LSQB, -/* 0x5C */ BT_OTHER, BT_RSQB, BT_OTHER, BT_NMSTRT, -/* 0x60 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX, -/* 0x64 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT, -/* 0x68 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, -/* 0x6C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, -/* 0x70 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, -/* 0x74 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, -/* 0x78 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER, -/* 0x7C */ BT_VERBAR, BT_OTHER, BT_OTHER, BT_OTHER, + /* 0x04 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, + /* 0x08 */ BT_NONXML, BT_S, BT_LF, BT_NONXML, + /* 0x0C */ BT_NONXML, BT_CR, BT_NONXML, BT_NONXML, + /* 0x10 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, + /* 0x14 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, + /* 0x18 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, + /* 0x1C */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, + /* 0x20 */ BT_S, BT_EXCL, BT_QUOT, BT_NUM, + /* 0x24 */ BT_OTHER, BT_PERCNT, BT_AMP, BT_APOS, + /* 0x28 */ BT_LPAR, BT_RPAR, BT_AST, BT_PLUS, + /* 0x2C */ BT_COMMA, BT_MINUS, BT_NAME, BT_SOL, + /* 0x30 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT, + /* 0x34 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT, + /* 0x38 */ BT_DIGIT, BT_DIGIT, BT_COLON, BT_SEMI, + /* 0x3C */ BT_LT, BT_EQUALS, BT_GT, BT_QUEST, + /* 0x40 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX, + /* 0x44 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT, + /* 0x48 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, + /* 0x4C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, + /* 0x50 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, + /* 0x54 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, + /* 0x58 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_LSQB, + /* 0x5C */ BT_OTHER, BT_RSQB, BT_OTHER, BT_NMSTRT, + /* 0x60 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX, + /* 0x64 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT, + /* 0x68 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, + /* 0x6C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, + /* 0x70 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, + /* 0x74 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, + /* 0x78 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER, + /* 0x7C */ BT_VERBAR, BT_OTHER, BT_OTHER, BT_OTHER, diff --git a/cextern/expat/lib/expat.dsp b/cextern/expat/lib/expat.dsp deleted file mode 100755 index b303253be20c..000000000000 --- a/cextern/expat/lib/expat.dsp +++ /dev/null @@ -1,185 +0,0 @@ -# Microsoft Developer Studio Project File - Name="expat" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 - -CFG=expat - Win32 Debug -!MESSAGE This is not a valid makefile. To build this project using NMAKE, -!MESSAGE use the Export Makefile command and run -!MESSAGE -!MESSAGE NMAKE /f "expat.mak". -!MESSAGE -!MESSAGE You can specify a configuration when running NMAKE -!MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE -!MESSAGE NMAKE /f "expat.mak" CFG="expat - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "expat - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") -!MESSAGE "expat - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -CPP=cl.exe -MTL=midl.exe -RSC=rc.exe - -!IF "$(CFG)" == "expat - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "Release" -# PROP BASE Intermediate_Dir "Release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "..\win32\bin\Release" -# PROP Intermediate_Dir "..\win32\tmp\Release" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "EXPAT_EXPORTS" /Yu"stdafx.h" /FD /c -# ADD CPP /nologo /MT /W3 /GX /O2 /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "COMPILED_FROM_DSP" /FD /c -# SUBTRACT CPP /YX /Yc /Yu -# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 -# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /pdb:none /machine:I386 /out:"..\win32\bin\Release/libexpat.dll" - -!ELSEIF "$(CFG)" == "expat - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "Debug" -# PROP BASE Intermediate_Dir "Debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "..\win32\bin\Debug" -# PROP Intermediate_Dir "..\win32\tmp\Debug" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "EXPAT_EXPORTS" /Yu"stdafx.h" /FD /GZ /c -# ADD CPP /nologo /MTd /W3 /GX /ZI /Od /D "_DEBUG" /D "COMPILED_FROM_DSP" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /FR /FD /GZ /c -# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 -# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /d "_DEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /pdb:none /debug /machine:I386 /out:"..\win32\bin\Debug/libexpat.dll" - -!ENDIF - -# Begin Target - -# Name "expat - Win32 Release" -# Name "expat - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=.\libexpat.def -# End Source File -# Begin Source File - -SOURCE=.\xmlparse.c - -!IF "$(CFG)" == "expat - Win32 Release" - -!ELSEIF "$(CFG)" == "expat - Win32 Debug" - -# ADD CPP /GX- /Od - -!ENDIF - -# End Source File -# Begin Source File - -SOURCE=.\xmlrole.c -# End Source File -# Begin Source File - -SOURCE=.\xmltok.c -# End Source File -# Begin Source File - -SOURCE=.\xmltok_impl.c -# End Source File -# Begin Source File - -SOURCE=.\xmltok_ns.c -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE=.\ascii.h -# End Source File -# Begin Source File - -SOURCE=.\asciitab.h -# End Source File -# Begin Source File - -SOURCE=.\expat.h -# End Source File -# Begin Source File - -SOURCE=.\expat_external.h -# End Source File -# Begin Source File - -SOURCE=.\iasciitab.h -# End Source File -# Begin Source File - -SOURCE=.\internal.h -# End Source File -# Begin Source File - -SOURCE=.\latin1tab.h -# End Source File -# Begin Source File - -SOURCE=.\nametab.h -# End Source File -# Begin Source File - -SOURCE=.\utf8tab.h -# End Source File -# Begin Source File - -SOURCE=.\xmlrole.h -# End Source File -# Begin Source File - -SOURCE=.\xmltok.h -# End Source File -# Begin Source File - -SOURCE=.\xmltok_impl.h -# End Source File -# End Group -# Begin Group "Resource Files" - -# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" -# End Group -# End Target -# End Project diff --git a/cextern/expat/lib/expat.h b/cextern/expat/lib/expat.h old mode 100755 new mode 100644 index 20a8278f78d2..290dfeb0f6dd --- a/cextern/expat/lib/expat.h +++ b/cextern/expat/lib/expat.h @@ -1,33 +1,63 @@ -/* Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd - See the file COPYING for copying permission. +/* + __ __ _ + ___\ \/ /_ __ __ _| |_ + / _ \\ /| '_ \ / _` | __| + | __// \| |_) | (_| | |_ + \___/_/\_\ .__/ \__,_|\__| + |_| XML parser + + Copyright (c) 1997-2000 Thai Open Source Software Center Ltd + Copyright (c) 2000 Clark Cooper + Copyright (c) 2000-2005 Fred L. Drake, Jr. + Copyright (c) 2001-2002 Greg Stein + Copyright (c) 2002-2016 Karl Waclawek + Copyright (c) 2016-2025 Sebastian Pipping + Copyright (c) 2016 Cristian Rodríguez + Copyright (c) 2016 Thomas Beutlich + Copyright (c) 2017 Rhodri James + Copyright (c) 2022 Thijs Schreijer + Copyright (c) 2023 Hanno BÃļck + Copyright (c) 2023 Sony Corporation / Snild Dolkow + Copyright (c) 2024 Taichi Haradaguchi <20001722@ymail.ne.jp> + Copyright (c) 2025 Matthew Fernandez + Licensed under the MIT license: + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to permit + persons to whom the Software is furnished to do so, subject to the + following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef Expat_INCLUDED -#define Expat_INCLUDED 1 +# define Expat_INCLUDED 1 -#ifdef __VMS -/* 0 1 2 3 0 1 2 3 - 1234567890123456789012345678901 1234567890123456789012345678901 */ -#define XML_SetProcessingInstructionHandler XML_SetProcessingInstrHandler -#define XML_SetUnparsedEntityDeclHandler XML_SetUnparsedEntDeclHandler -#define XML_SetStartNamespaceDeclHandler XML_SetStartNamespcDeclHandler -#define XML_SetExternalEntityRefHandlerArg XML_SetExternalEntRefHandlerArg -#endif +# include +# include "expat_external.h" -#include -#include "expat_external.h" - -#ifdef __cplusplus +# ifdef __cplusplus extern "C" { -#endif +# endif struct XML_ParserStruct; typedef struct XML_ParserStruct *XML_Parser; -/* Should this be defined using stdbool.h when C99 is available? */ typedef unsigned char XML_Bool; -#define XML_TRUE ((XML_Bool) 1) -#define XML_FALSE ((XML_Bool) 0) +# define XML_TRUE ((XML_Bool)1) +# define XML_FALSE ((XML_Bool)0) /* The XML_Status enum gives the possible return values for several API functions. The preprocessor #defines are included so this @@ -44,11 +74,11 @@ typedef unsigned char XML_Bool; */ enum XML_Status { XML_STATUS_ERROR = 0, -#define XML_STATUS_ERROR XML_STATUS_ERROR +# define XML_STATUS_ERROR XML_STATUS_ERROR XML_STATUS_OK = 1, -#define XML_STATUS_OK XML_STATUS_OK +# define XML_STATUS_OK XML_STATUS_OK XML_STATUS_SUSPENDED = 2 -#define XML_STATUS_SUSPENDED XML_STATUS_SUSPENDED +# define XML_STATUS_SUSPENDED XML_STATUS_SUSPENDED }; enum XML_Error { @@ -95,7 +125,15 @@ enum XML_Error { /* Added in 2.0. */ XML_ERROR_RESERVED_PREFIX_XML, XML_ERROR_RESERVED_PREFIX_XMLNS, - XML_ERROR_RESERVED_NAMESPACE_URI + XML_ERROR_RESERVED_NAMESPACE_URI, + /* Added in 2.2.1. */ + XML_ERROR_INVALID_ARGUMENT, + /* Added in 2.3.0. */ + XML_ERROR_NO_BUFFER, + /* Added in 2.4.0. */ + XML_ERROR_AMPLIFICATION_LIMIT_BREACH, + /* Added in 2.6.4. */ + XML_ERROR_NOT_STARTED, }; enum XML_Content_Type { @@ -135,25 +173,25 @@ enum XML_Content_Quant { typedef struct XML_cp XML_Content; struct XML_cp { - enum XML_Content_Type type; - enum XML_Content_Quant quant; - XML_Char * name; - unsigned int numchildren; - XML_Content * children; + enum XML_Content_Type type; + enum XML_Content_Quant quant; + XML_Char *name; + unsigned int numchildren; + XML_Content *children; }; - /* This is called for an element declaration. See above for - description of the model argument. It's the caller's responsibility - to free model when finished with it. + description of the model argument. It's the user code's responsibility + to free model when finished with it. See XML_FreeContentModel. + There is no need to free the model from the handler, it can be kept + around and freed at a later stage. */ -typedef void (XMLCALL *XML_ElementDeclHandler) (void *userData, - const XML_Char *name, - XML_Content *model); +typedef void(XMLCALL *XML_ElementDeclHandler)(void *userData, + const XML_Char *name, + XML_Content *model); XMLPARSEAPI(void) -XML_SetElementDeclHandler(XML_Parser parser, - XML_ElementDeclHandler eldecl); +XML_SetElementDeclHandler(XML_Parser parser, XML_ElementDeclHandler eldecl); /* The Attlist declaration handler is called for *each* attribute. So a single Attlist declaration with multiple attributes declared will @@ -163,17 +201,12 @@ XML_SetElementDeclHandler(XML_Parser parser, value will be NULL in the case of "#REQUIRED". If "isrequired" is true and default is non-NULL, then this is a "#FIXED" default. */ -typedef void (XMLCALL *XML_AttlistDeclHandler) ( - void *userData, - const XML_Char *elname, - const XML_Char *attname, - const XML_Char *att_type, - const XML_Char *dflt, - int isrequired); +typedef void(XMLCALL *XML_AttlistDeclHandler)( + void *userData, const XML_Char *elname, const XML_Char *attname, + const XML_Char *att_type, const XML_Char *dflt, int isrequired); XMLPARSEAPI(void) -XML_SetAttlistDeclHandler(XML_Parser parser, - XML_AttlistDeclHandler attdecl); +XML_SetAttlistDeclHandler(XML_Parser parser, XML_AttlistDeclHandler attdecl); /* The XML declaration handler is called for *both* XML declarations and text declarations. The way to distinguish is that the version @@ -183,15 +216,13 @@ XML_SetAttlistDeclHandler(XML_Parser parser, was no standalone parameter in the declaration, that it was given as no, or that it was given as yes. */ -typedef void (XMLCALL *XML_XmlDeclHandler) (void *userData, - const XML_Char *version, - const XML_Char *encoding, - int standalone); +typedef void(XMLCALL *XML_XmlDeclHandler)(void *userData, + const XML_Char *version, + const XML_Char *encoding, + int standalone); XMLPARSEAPI(void) -XML_SetXmlDeclHandler(XML_Parser parser, - XML_XmlDeclHandler xmldecl); - +XML_SetXmlDeclHandler(XML_Parser parser, XML_XmlDeclHandler xmldecl); typedef struct { void *(*malloc_fcn)(size_t size); @@ -215,11 +246,21 @@ XML_ParserCreate(const XML_Char *encoding); and the local part will be concatenated without any separator. It is a programming error to use the separator '\0' with namespace triplets (see XML_SetReturnNSTriplet). + If a namespace separator is chosen that can be part of a URI or + part of an XML name, splitting an expanded name back into its + 1, 2 or 3 original parts on application level in the element handler + may end up vulnerable, so these are advised against; sane choices for + a namespace separator are e.g. '\n' (line feed) and '|' (pipe). + + Note that Expat does not validate namespace URIs (beyond encoding) + against RFC 3986 today (and is not required to do so with regard to + the XML 1.0 namespaces specification) but it may start doing that + in future releases. Before that, an application using Expat must + be ready to receive namespace URIs containing non-URI characters. */ XMLPARSEAPI(XML_Parser) XML_ParserCreateNS(const XML_Char *encoding, XML_Char namespaceSeparator); - /* Constructs a new parser using the memory management suite referred to by memsuite. If memsuite is NULL, then use the standard library memory suite. If namespaceSeparator is non-NULL it creates a parser with @@ -234,9 +275,9 @@ XML_ParserCreate_MM(const XML_Char *encoding, const XML_Memory_Handling_Suite *memsuite, const XML_Char *namespaceSeparator); -/* Prepare a parser object to be re-used. This is particularly - valuable when memory allocation overhead is disproportionatly high, - such as when a large number of small documnents need to be parsed. +/* Prepare a parser object to be reused. This is particularly + valuable when memory allocation overhead is disproportionately high, + such as when a large number of small documents need to be parsed. All handlers are cleared from the parser, except for the unknownEncodingHandler. The parser's external state is re-initialized except for the values of ns and ns_triplets. @@ -249,31 +290,27 @@ XML_ParserReset(XML_Parser parser, const XML_Char *encoding); /* atts is array of name/value pairs, terminated by 0; names and values are 0 terminated. */ -typedef void (XMLCALL *XML_StartElementHandler) (void *userData, - const XML_Char *name, - const XML_Char **atts); - -typedef void (XMLCALL *XML_EndElementHandler) (void *userData, - const XML_Char *name); +typedef void(XMLCALL *XML_StartElementHandler)(void *userData, + const XML_Char *name, + const XML_Char **atts); +typedef void(XMLCALL *XML_EndElementHandler)(void *userData, + const XML_Char *name); /* s is not 0 terminated. */ -typedef void (XMLCALL *XML_CharacterDataHandler) (void *userData, - const XML_Char *s, - int len); +typedef void(XMLCALL *XML_CharacterDataHandler)(void *userData, + const XML_Char *s, int len); /* target and data are 0 terminated */ -typedef void (XMLCALL *XML_ProcessingInstructionHandler) ( - void *userData, - const XML_Char *target, - const XML_Char *data); +typedef void(XMLCALL *XML_ProcessingInstructionHandler)(void *userData, + const XML_Char *target, + const XML_Char *data); /* data is 0 terminated */ -typedef void (XMLCALL *XML_CommentHandler) (void *userData, - const XML_Char *data); +typedef void(XMLCALL *XML_CommentHandler)(void *userData, const XML_Char *data); -typedef void (XMLCALL *XML_StartCdataSectionHandler) (void *userData); -typedef void (XMLCALL *XML_EndCdataSectionHandler) (void *userData); +typedef void(XMLCALL *XML_StartCdataSectionHandler)(void *userData); +typedef void(XMLCALL *XML_EndCdataSectionHandler)(void *userData); /* This is called for any characters in the XML document for which there is no applicable handler. This includes both characters that @@ -288,25 +325,23 @@ typedef void (XMLCALL *XML_EndCdataSectionHandler) (void *userData); default handler: for example, a comment might be split between multiple calls. */ -typedef void (XMLCALL *XML_DefaultHandler) (void *userData, - const XML_Char *s, - int len); +typedef void(XMLCALL *XML_DefaultHandler)(void *userData, const XML_Char *s, + int len); /* This is called for the start of the DOCTYPE declaration, before any DTD or internal subset is parsed. */ -typedef void (XMLCALL *XML_StartDoctypeDeclHandler) ( - void *userData, - const XML_Char *doctypeName, - const XML_Char *sysid, - const XML_Char *pubid, - int has_internal_subset); +typedef void(XMLCALL *XML_StartDoctypeDeclHandler)(void *userData, + const XML_Char *doctypeName, + const XML_Char *sysid, + const XML_Char *pubid, + int has_internal_subset); -/* This is called for the start of the DOCTYPE declaration when the +/* This is called for the end of the DOCTYPE declaration when the closing > is encountered, but after processing any external subset. */ -typedef void (XMLCALL *XML_EndDoctypeDeclHandler)(void *userData); +typedef void(XMLCALL *XML_EndDoctypeDeclHandler)(void *userData); /* This is called for entity declarations. The is_parameter_entity argument will be non-zero if the entity is a parameter entity, zero @@ -314,7 +349,7 @@ typedef void (XMLCALL *XML_EndDoctypeDeclHandler)(void *userData); For internal entities (), value will be non-NULL and systemId, publicID, and notationName will be NULL. - The value string is NOT nul-terminated; the length is provided in + The value string is NOT null-terminated; the length is provided in the value_length argument. Since it is legal to have zero-length values, do not use this argument to test for internal entities. @@ -326,23 +361,17 @@ typedef void (XMLCALL *XML_EndDoctypeDeclHandler)(void *userData); Note that is_parameter_entity can't be changed to XML_Bool, since that would break binary compatibility. */ -typedef void (XMLCALL *XML_EntityDeclHandler) ( - void *userData, - const XML_Char *entityName, - int is_parameter_entity, - const XML_Char *value, - int value_length, - const XML_Char *base, - const XML_Char *systemId, - const XML_Char *publicId, - const XML_Char *notationName); +typedef void(XMLCALL *XML_EntityDeclHandler)( + void *userData, const XML_Char *entityName, int is_parameter_entity, + const XML_Char *value, int value_length, const XML_Char *base, + const XML_Char *systemId, const XML_Char *publicId, + const XML_Char *notationName); XMLPARSEAPI(void) -XML_SetEntityDeclHandler(XML_Parser parser, - XML_EntityDeclHandler handler); +XML_SetEntityDeclHandler(XML_Parser parser, XML_EntityDeclHandler handler); /* OBSOLETE -- OBSOLETE -- OBSOLETE - This handler has been superceded by the EntityDeclHandler above. + This handler has been superseded by the EntityDeclHandler above. It is provided here for backward compatibility. This is called for a declaration of an unparsed (NDATA) entity. @@ -350,24 +379,20 @@ XML_SetEntityDeclHandler(XML_Parser parser, entityName, systemId and notationName arguments will never be NULL. The other arguments may be. */ -typedef void (XMLCALL *XML_UnparsedEntityDeclHandler) ( - void *userData, - const XML_Char *entityName, - const XML_Char *base, - const XML_Char *systemId, - const XML_Char *publicId, - const XML_Char *notationName); +typedef void(XMLCALL *XML_UnparsedEntityDeclHandler)( + void *userData, const XML_Char *entityName, const XML_Char *base, + const XML_Char *systemId, const XML_Char *publicId, + const XML_Char *notationName); /* This is called for a declaration of notation. The base argument is whatever was set by XML_SetBase. The notationName will never be NULL. The other arguments can be. */ -typedef void (XMLCALL *XML_NotationDeclHandler) ( - void *userData, - const XML_Char *notationName, - const XML_Char *base, - const XML_Char *systemId, - const XML_Char *publicId); +typedef void(XMLCALL *XML_NotationDeclHandler)(void *userData, + const XML_Char *notationName, + const XML_Char *base, + const XML_Char *systemId, + const XML_Char *publicId); /* When namespace processing is enabled, these are called once for each namespace declaration. The call to the start and end element @@ -375,14 +400,12 @@ typedef void (XMLCALL *XML_NotationDeclHandler) ( declaration handlers. For an xmlns attribute, prefix will be NULL. For an xmlns="" attribute, uri will be NULL. */ -typedef void (XMLCALL *XML_StartNamespaceDeclHandler) ( - void *userData, - const XML_Char *prefix, - const XML_Char *uri); +typedef void(XMLCALL *XML_StartNamespaceDeclHandler)(void *userData, + const XML_Char *prefix, + const XML_Char *uri); -typedef void (XMLCALL *XML_EndNamespaceDeclHandler) ( - void *userData, - const XML_Char *prefix); +typedef void(XMLCALL *XML_EndNamespaceDeclHandler)(void *userData, + const XML_Char *prefix); /* This is called if the document is not standalone, that is, it has an external subset or a reference to a parameter entity, but does not @@ -393,7 +416,7 @@ typedef void (XMLCALL *XML_EndNamespaceDeclHandler) ( conditions above this handler will only be called if the referenced entity was actually read. */ -typedef int (XMLCALL *XML_NotStandaloneHandler) (void *userData); +typedef int(XMLCALL *XML_NotStandaloneHandler)(void *userData); /* This is called for a reference to an external parsed general entity. The referenced entity is not automatically parsed. The @@ -429,12 +452,11 @@ typedef int (XMLCALL *XML_NotStandaloneHandler) (void *userData); Note that unlike other handlers the first argument is the parser, not userData. */ -typedef int (XMLCALL *XML_ExternalEntityRefHandler) ( - XML_Parser parser, - const XML_Char *context, - const XML_Char *base, - const XML_Char *systemId, - const XML_Char *publicId); +typedef int(XMLCALL *XML_ExternalEntityRefHandler)(XML_Parser parser, + const XML_Char *context, + const XML_Char *base, + const XML_Char *systemId, + const XML_Char *publicId); /* This is called in two situations: 1) An entity reference is encountered for which no declaration @@ -446,10 +468,9 @@ typedef int (XMLCALL *XML_ExternalEntityRefHandler) ( the event would be out of sync with the reporting of the declarations or attribute values */ -typedef void (XMLCALL *XML_SkippedEntityHandler) ( - void *userData, - const XML_Char *entityName, - int is_parameter_entity); +typedef void(XMLCALL *XML_SkippedEntityHandler)(void *userData, + const XML_Char *entityName, + int is_parameter_entity); /* This structure is filled in by the XML_UnknownEncodingHandler to provide information to the parser about encodings that are unknown @@ -506,8 +527,8 @@ typedef void (XMLCALL *XML_SkippedEntityHandler) ( typedef struct { int map[256]; void *data; - int (XMLCALL *convert)(void *data, const char *s); - void (XMLCALL *release)(void *data); + int(XMLCALL *convert)(void *data, const char *s); + void(XMLCALL *release)(void *data); } XML_Encoding; /* This is called for an encoding that is unknown to the parser. @@ -523,25 +544,21 @@ typedef struct { Otherwise it must return XML_STATUS_ERROR. If info does not describe a suitable encoding, then the parser will - return an XML_UNKNOWN_ENCODING error. + return an XML_ERROR_UNKNOWN_ENCODING error. */ -typedef int (XMLCALL *XML_UnknownEncodingHandler) ( - void *encodingHandlerData, - const XML_Char *name, - XML_Encoding *info); +typedef int(XMLCALL *XML_UnknownEncodingHandler)(void *encodingHandlerData, + const XML_Char *name, + XML_Encoding *info); XMLPARSEAPI(void) -XML_SetElementHandler(XML_Parser parser, - XML_StartElementHandler start, +XML_SetElementHandler(XML_Parser parser, XML_StartElementHandler start, XML_EndElementHandler end); XMLPARSEAPI(void) -XML_SetStartElementHandler(XML_Parser parser, - XML_StartElementHandler handler); +XML_SetStartElementHandler(XML_Parser parser, XML_StartElementHandler handler); XMLPARSEAPI(void) -XML_SetEndElementHandler(XML_Parser parser, - XML_EndElementHandler handler); +XML_SetEndElementHandler(XML_Parser parser, XML_EndElementHandler handler); XMLPARSEAPI(void) XML_SetCharacterDataHandler(XML_Parser parser, @@ -551,8 +568,7 @@ XMLPARSEAPI(void) XML_SetProcessingInstructionHandler(XML_Parser parser, XML_ProcessingInstructionHandler handler); XMLPARSEAPI(void) -XML_SetCommentHandler(XML_Parser parser, - XML_CommentHandler handler); +XML_SetCommentHandler(XML_Parser parser, XML_CommentHandler handler); XMLPARSEAPI(void) XML_SetCdataSectionHandler(XML_Parser parser, @@ -572,20 +588,17 @@ XML_SetEndCdataSectionHandler(XML_Parser parser, default handler, or to the skipped entity handler, if one is set. */ XMLPARSEAPI(void) -XML_SetDefaultHandler(XML_Parser parser, - XML_DefaultHandler handler); +XML_SetDefaultHandler(XML_Parser parser, XML_DefaultHandler handler); /* This sets the default handler but does not inhibit expansion of internal entities. The entity reference will not be passed to the default handler. */ XMLPARSEAPI(void) -XML_SetDefaultHandlerExpand(XML_Parser parser, - XML_DefaultHandler handler); +XML_SetDefaultHandlerExpand(XML_Parser parser, XML_DefaultHandler handler); XMLPARSEAPI(void) -XML_SetDoctypeDeclHandler(XML_Parser parser, - XML_StartDoctypeDeclHandler start, +XML_SetDoctypeDeclHandler(XML_Parser parser, XML_StartDoctypeDeclHandler start, XML_EndDoctypeDeclHandler end); XMLPARSEAPI(void) @@ -593,16 +606,14 @@ XML_SetStartDoctypeDeclHandler(XML_Parser parser, XML_StartDoctypeDeclHandler start); XMLPARSEAPI(void) -XML_SetEndDoctypeDeclHandler(XML_Parser parser, - XML_EndDoctypeDeclHandler end); +XML_SetEndDoctypeDeclHandler(XML_Parser parser, XML_EndDoctypeDeclHandler end); XMLPARSEAPI(void) XML_SetUnparsedEntityDeclHandler(XML_Parser parser, XML_UnparsedEntityDeclHandler handler); XMLPARSEAPI(void) -XML_SetNotationDeclHandler(XML_Parser parser, - XML_NotationDeclHandler handler); +XML_SetNotationDeclHandler(XML_Parser parser, XML_NotationDeclHandler handler); XMLPARSEAPI(void) XML_SetNamespaceDeclHandler(XML_Parser parser, @@ -630,8 +641,7 @@ XML_SetExternalEntityRefHandler(XML_Parser parser, instead of the parser object. */ XMLPARSEAPI(void) -XML_SetExternalEntityRefHandlerArg(XML_Parser parser, - void *arg); +XML_SetExternalEntityRefHandlerArg(XML_Parser parser, void *arg); XMLPARSEAPI(void) XML_SetSkippedEntityHandler(XML_Parser parser, @@ -671,7 +681,7 @@ XMLPARSEAPI(void) XML_SetUserData(XML_Parser parser, void *userData); /* Returns the last value set by XML_SetUserData or NULL. */ -#define XML_GetUserData(parser) (*(void **)(parser)) +# define XML_GetUserData(parser) (*(void **)(parser)) /* This is equivalent to supplying an encoding argument to XML_ParserCreate. On success XML_SetEncoding returns non-zero, @@ -706,11 +716,11 @@ XML_UseParserAsHandlerArg(XML_Parser parser); be called, despite an external subset being parsed. Note: If XML_DTD is not defined when Expat is compiled, returns XML_ERROR_FEATURE_REQUIRES_XML_DTD. + Note: If parser == NULL, returns XML_ERROR_INVALID_ARGUMENT. */ XMLPARSEAPI(enum XML_Error) XML_UseForeignDTD(XML_Parser parser, XML_Bool useDTD); - /* Sets the base to be used for resolving relative URIs in system identifiers in declarations. Resolving relative identifiers is left to the application: this value will be passed through as the @@ -728,20 +738,44 @@ XML_GetBase(XML_Parser parser); /* Returns the number of the attribute/value pairs passed in last call to the XML_StartElementHandler that were specified in the start-tag rather than defaulted. Each attribute/value pair counts as 2; thus - this correspondds to an index into the atts array passed to the - XML_StartElementHandler. + this corresponds to an index into the atts array passed to the + XML_StartElementHandler. Returns -1 if parser == NULL. */ XMLPARSEAPI(int) XML_GetSpecifiedAttributeCount(XML_Parser parser); /* Returns the index of the ID attribute passed in the last call to - XML_StartElementHandler, or -1 if there is no ID attribute. Each - attribute/value pair counts as 2; thus this correspondds to an - index into the atts array passed to the XML_StartElementHandler. + XML_StartElementHandler, or -1 if there is no ID attribute or + parser == NULL. Each attribute/value pair counts as 2; thus this + corresponds to an index into the atts array passed to the + XML_StartElementHandler. */ XMLPARSEAPI(int) XML_GetIdAttributeIndex(XML_Parser parser); +# ifdef XML_ATTR_INFO +/* Source file byte offsets for the start and end of attribute names and values. + The value indices are exclusive of surrounding quotes; thus in a UTF-8 source + file an attribute value of "blah" will yield: + info->valueEnd - info->valueStart = 4 bytes. +*/ +typedef struct { + XML_Index nameStart; /* Offset to beginning of the attribute name. */ + XML_Index nameEnd; /* Offset after the attribute name's last byte. */ + XML_Index valueStart; /* Offset to beginning of the attribute value. */ + XML_Index valueEnd; /* Offset after the attribute value's last byte. */ +} XML_AttrInfo; + +/* Returns an array of XML_AttrInfo structures for the attribute/value pairs + passed in last call to the XML_StartElementHandler that were specified + in the start-tag rather than defaulted. Each attribute/value pair counts + as 1; thus the number of entries in the array is + XML_GetSpecifiedAttributeCount(parser) / 2. +*/ +XMLPARSEAPI(const XML_AttrInfo *) +XML_GetAttributeInfo(XML_Parser parser); +# endif + /* Parses some input. Returns XML_STATUS_ERROR if a fatal error is detected. The last call to XML_Parse must have isFinal true; len may be zero for this call (or any other). @@ -765,20 +799,20 @@ XML_ParseBuffer(XML_Parser parser, int len, int isFinal); (resumable = 0) an already suspended parser. Some call-backs may still follow because they would otherwise get lost. Examples: - endElementHandler() for empty elements when stopped in - startElementHandler(), - - endNameSpaceDeclHandler() when stopped in endElementHandler(), + startElementHandler(), + - endNameSpaceDeclHandler() when stopped in endElementHandler(), and possibly others. Can be called from most handlers, including DTD related call-backs, except when parsing an external parameter entity and resumable != 0. Returns XML_STATUS_OK when successful, XML_STATUS_ERROR otherwise. - Possible error codes: + Possible error codes: - XML_ERROR_SUSPENDED: when suspending an already suspended parser. - XML_ERROR_FINISHED: when the parser has already finished. - XML_ERROR_SUSPEND_PE: when suspending while parsing an external PE. - When resumable != 0 (true) then parsing is suspended, that is, - XML_Parse() and XML_ParseBuffer() return XML_STATUS_SUSPENDED. + When resumable != 0 (true) then parsing is suspended, that is, + XML_Parse() and XML_ParseBuffer() return XML_STATUS_SUSPENDED. Otherwise, parsing is aborted, that is, XML_Parse() and XML_ParseBuffer() return XML_STATUS_ERROR with error code XML_ERROR_ABORTED. @@ -789,7 +823,7 @@ XML_ParseBuffer(XML_Parser parser, int len, int isFinal); the externalEntityRefHandler() to call XML_StopParser() on the parent parser (recursively), if one wants to stop parsing altogether. - When suspended, parsing can be resumed by calling XML_ResumeParser(). + When suspended, parsing can be resumed by calling XML_ResumeParser(). */ XMLPARSEAPI(enum XML_Status) XML_StopParser(XML_Parser parser, XML_Bool resumable); @@ -797,7 +831,7 @@ XML_StopParser(XML_Parser parser, XML_Bool resumable); /* Resumes parsing after it has been suspended with XML_StopParser(). Must not be called from within a handler call-back. Returns same status codes as XML_Parse() or XML_ParseBuffer(). - Additional error code XML_ERROR_NOT_SUSPENDED possible. + Additional error code XML_ERROR_NOT_SUSPENDED possible. *Note*: This must be called on the most deeply nested child parser instance @@ -809,12 +843,7 @@ XML_StopParser(XML_Parser parser, XML_Bool resumable); XMLPARSEAPI(enum XML_Status) XML_ResumeParser(XML_Parser parser); -enum XML_Parsing { - XML_INITIALIZED, - XML_PARSING, - XML_FINISHED, - XML_SUSPENDED -}; +enum XML_Parsing { XML_INITIALIZED, XML_PARSING, XML_FINISHED, XML_SUSPENDED }; typedef struct { enum XML_Parsing parsing; @@ -846,8 +875,7 @@ XML_GetParsingStatus(XML_Parser parser, XML_ParsingStatus *status); Otherwise returns a new XML_Parser object. */ XMLPARSEAPI(XML_Parser) -XML_ExternalEntityParserCreate(XML_Parser parser, - const XML_Char *context, +XML_ExternalEntityParserCreate(XML_Parser parser, const XML_Char *context, const XML_Char *encoding); enum XML_ParamEntityParsing { @@ -878,11 +906,21 @@ enum XML_ParamEntityParsing { entities is requested; otherwise it will return non-zero. Note: If XML_SetParamEntityParsing is called after XML_Parse or XML_ParseBuffer, then it has no effect and will always return 0. + Note: If parser == NULL, the function will do nothing and return 0. */ XMLPARSEAPI(int) XML_SetParamEntityParsing(XML_Parser parser, enum XML_ParamEntityParsing parsing); +/* Sets the hash salt to use for internal hash calculations. + Helps in preventing DoS attacks based on predicting hash + function behavior. This must be called before parsing is started. + Returns 1 if successful, 0 when called after parsing has started. + Note: If parser == NULL, the function will do nothing and return 0. +*/ +XMLPARSEAPI(int) +XML_SetHashSalt(XML_Parser parser, unsigned long hash_salt); + /* If XML_Parse or XML_ParseBuffer have returned XML_STATUS_ERROR, then XML_GetErrorCode returns information about the error. */ @@ -898,12 +936,16 @@ XML_GetErrorCode(XML_Parser parser); be within the relevant markup. When called outside of the callback functions, the position indicated will be just past the last parse event (regardless of whether there was an associated callback). - + They may also be called after returning from a call to XML_Parse or XML_ParseBuffer. If the return value is XML_STATUS_ERROR then the location is the location of the character at which the error was detected; otherwise the location is the location of the last parse event, as described above. + + Note: XML_GetCurrentLineNumber and XML_GetCurrentColumnNumber + return 0 to indicate an error. + Note: XML_GetCurrentByteIndex returns -1 to indicate an error. */ XMLPARSEAPI(XML_Size) XML_GetCurrentLineNumber(XML_Parser parser); XMLPARSEAPI(XML_Size) XML_GetCurrentColumnNumber(XML_Parser parser); @@ -915,7 +957,7 @@ XMLPARSEAPI(XML_Index) XML_GetCurrentByteIndex(XML_Parser parser); XMLPARSEAPI(int) XML_GetCurrentByteCount(XML_Parser parser); -/* If XML_CONTEXT_BYTES is defined, returns the input buffer, sets +/* If XML_CONTEXT_BYTES is >=1, returns the input buffer, sets the integer pointed to by offset to the offset within this buffer of the current parse position, and sets the integer pointed to by size to the size of this buffer (the number of input bytes). Otherwise @@ -926,14 +968,12 @@ XML_GetCurrentByteCount(XML_Parser parser); the handler that makes the call. */ XMLPARSEAPI(const char *) -XML_GetInputContext(XML_Parser parser, - int *offset, - int *size); +XML_GetInputContext(XML_Parser parser, int *offset, int *size); /* For backwards compatibility with previous versions. */ -#define XML_GetErrorLineNumber XML_GetCurrentLineNumber -#define XML_GetErrorColumnNumber XML_GetCurrentColumnNumber -#define XML_GetErrorByteIndex XML_GetCurrentByteIndex +# define XML_GetErrorLineNumber XML_GetCurrentLineNumber +# define XML_GetErrorColumnNumber XML_GetCurrentColumnNumber +# define XML_GetErrorByteIndex XML_GetCurrentByteIndex /* Frees the content model passed to the element declaration handler */ XMLPARSEAPI(void) @@ -941,9 +981,12 @@ XML_FreeContentModel(XML_Parser parser, XML_Content *model); /* Exposing the memory handling functions used in Expat */ XMLPARSEAPI(void *) +XML_ATTR_MALLOC +XML_ATTR_ALLOC_SIZE(2) XML_MemMalloc(XML_Parser parser, size_t size); XMLPARSEAPI(void *) +XML_ATTR_ALLOC_SIZE(3) XML_MemRealloc(XML_Parser parser, void *ptr, size_t size); XMLPARSEAPI(void) @@ -984,31 +1027,65 @@ enum XML_FeatureEnum { XML_FEATURE_SIZEOF_XML_CHAR, XML_FEATURE_SIZEOF_XML_LCHAR, XML_FEATURE_NS, - XML_FEATURE_LARGE_SIZE + XML_FEATURE_LARGE_SIZE, + XML_FEATURE_ATTR_INFO, + /* Added in Expat 2.4.0. */ + XML_FEATURE_BILLION_LAUGHS_ATTACK_PROTECTION_MAXIMUM_AMPLIFICATION_DEFAULT, + XML_FEATURE_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT, + /* Added in Expat 2.6.0. */ + XML_FEATURE_GE, + /* Added in Expat 2.7.2. */ + XML_FEATURE_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION_DEFAULT, + XML_FEATURE_ALLOC_TRACKER_ACTIVATION_THRESHOLD_DEFAULT, /* Additional features must be added to the end of this enum. */ }; typedef struct { - enum XML_FeatureEnum feature; - const XML_LChar *name; - long int value; + enum XML_FeatureEnum feature; + const XML_LChar *name; + long int value; } XML_Feature; XMLPARSEAPI(const XML_Feature *) XML_GetFeatureList(void); +# if defined(XML_DTD) || (defined(XML_GE) && XML_GE == 1) +/* Added in Expat 2.4.0 for XML_DTD defined and + * added in Expat 2.6.0 for XML_GE == 1. */ +XMLPARSEAPI(XML_Bool) +XML_SetBillionLaughsAttackProtectionMaximumAmplification( + XML_Parser parser, float maximumAmplificationFactor); + +/* Added in Expat 2.4.0 for XML_DTD defined and + * added in Expat 2.6.0 for XML_GE == 1. */ +XMLPARSEAPI(XML_Bool) +XML_SetBillionLaughsAttackProtectionActivationThreshold( + XML_Parser parser, unsigned long long activationThresholdBytes); + +/* Added in Expat 2.7.2. */ +XMLPARSEAPI(XML_Bool) +XML_SetAllocTrackerMaximumAmplification(XML_Parser parser, + float maximumAmplificationFactor); + +/* Added in Expat 2.7.2. */ +XMLPARSEAPI(XML_Bool) +XML_SetAllocTrackerActivationThreshold( + XML_Parser parser, unsigned long long activationThresholdBytes); +# endif + +/* Added in Expat 2.6.0. */ +XMLPARSEAPI(XML_Bool) +XML_SetReparseDeferralEnabled(XML_Parser parser, XML_Bool enabled); -/* Expat follows the GNU/Linux convention of odd number minor version for - beta/development releases and even number minor version for stable - releases. Micro is bumped with each release, and set to 0 with each - change to major or minor version. +/* Expat follows the semantic versioning convention. + See https://semver.org */ -#define XML_MAJOR_VERSION 2 -#define XML_MINOR_VERSION 0 -#define XML_MICRO_VERSION 1 +# define XML_MAJOR_VERSION 2 +# define XML_MINOR_VERSION 7 +# define XML_MICRO_VERSION 3 -#ifdef __cplusplus +# ifdef __cplusplus } -#endif +# endif #endif /* not Expat_INCLUDED */ diff --git a/cextern/expat/lib/expat_external.h b/cextern/expat/lib/expat_external.h old mode 100755 new mode 100644 index 2c03284ea265..96f955eefb6b --- a/cextern/expat/lib/expat_external.h +++ b/cextern/expat/lib/expat_external.h @@ -1,16 +1,47 @@ -/* Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd - See the file COPYING for copying permission. +/* + __ __ _ + ___\ \/ /_ __ __ _| |_ + / _ \\ /| '_ \ / _` | __| + | __// \| |_) | (_| | |_ + \___/_/\_\ .__/ \__,_|\__| + |_| XML parser + + Copyright (c) 1997-2000 Thai Open Source Software Center Ltd + Copyright (c) 2000 Clark Cooper + Copyright (c) 2000-2004 Fred L. Drake, Jr. + Copyright (c) 2001-2002 Greg Stein + Copyright (c) 2002-2006 Karl Waclawek + Copyright (c) 2016 Cristian Rodríguez + Copyright (c) 2016-2019 Sebastian Pipping + Copyright (c) 2017 Rhodri James + Copyright (c) 2018 Yury Gribov + Licensed under the MIT license: + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to permit + persons to whom the Software is furnished to do so, subject to the + following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef Expat_External_INCLUDED -#define Expat_External_INCLUDED 1 +# define Expat_External_INCLUDED 1 /* External API definitions */ -#if defined(_MSC_EXTENSIONS) && !defined(__BEOS__) && !defined(__CYGWIN__) -#define XML_USE_MSC_EXTENSIONS 1 -#endif - /* Expat tries very hard to make the API boundary very specifically defined. There are two macros defined to control this boundary; each of these can be defined before including this header to @@ -33,12 +64,12 @@ compiled with the cdecl calling convention as the default since system headers may assume the cdecl convention. */ -#ifndef XMLCALL -#if defined(_MSC_VER) -#define XMLCALL __cdecl -#elif defined(__GNUC__) && defined(__i386) && !defined(__INTEL_COMPILER) -#define XMLCALL __attribute__((cdecl)) -#else +# ifndef XMLCALL +# if defined(_MSC_VER) +# define XMLCALL __cdecl +# elif defined(__GNUC__) && defined(__i386) && ! defined(__INTEL_COMPILER) +# define XMLCALL __attribute__((cdecl)) +# else /* For any platform which uses this definition and supports more than one calling convention, we need to extend this definition to declare the convention used on that platform, if it's possible to @@ -49,67 +80,87 @@ pre-processor and how to specify the same calling convention as the platform's malloc() implementation. */ -#define XMLCALL -#endif -#endif /* not defined XMLCALL */ - +# define XMLCALL +# endif +# endif /* not defined XMLCALL */ -#if !defined(XML_STATIC) && !defined(XMLIMPORT) -#ifndef XML_BUILDING_EXPAT +# if ! defined(XML_STATIC) && ! defined(XMLIMPORT) +# ifndef XML_BUILDING_EXPAT /* using Expat from an application */ -#ifdef XML_USE_MSC_EXTENSIONS -#define XMLIMPORT __declspec(dllimport) -#endif - -#endif -#endif /* not defined XML_STATIC */ - +# if defined(_MSC_EXTENSIONS) && ! defined(__BEOS__) \ + && ! defined(__CYGWIN__) +# define XMLIMPORT __declspec(dllimport) +# endif -/* If we didn't define it above, define it away: */ -#ifndef XMLIMPORT -#define XMLIMPORT -#endif +# endif +# endif /* not defined XML_STATIC */ +# ifndef XML_ENABLE_VISIBILITY +# define XML_ENABLE_VISIBILITY 0 +# endif -#define XMLPARSEAPI(type) XMLIMPORT type XMLCALL +# if ! defined(XMLIMPORT) && XML_ENABLE_VISIBILITY +# define XMLIMPORT __attribute__((visibility("default"))) +# endif -#ifdef __cplusplus +/* If we didn't define it above, define it away: */ +# ifndef XMLIMPORT +# define XMLIMPORT +# endif + +# if defined(__GNUC__) \ + && (__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 96)) +# define XML_ATTR_MALLOC __attribute__((__malloc__)) +# else +# define XML_ATTR_MALLOC +# endif + +# if defined(__GNUC__) \ + && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) +# define XML_ATTR_ALLOC_SIZE(x) __attribute__((__alloc_size__(x))) +# else +# define XML_ATTR_ALLOC_SIZE(x) +# endif + +# define XMLPARSEAPI(type) XMLIMPORT type XMLCALL + +# ifdef __cplusplus extern "C" { -#endif - -#ifdef XML_UNICODE_WCHAR_T -#define XML_UNICODE -#endif - -#ifdef XML_UNICODE /* Information is UTF-16 encoded. */ -#ifdef XML_UNICODE_WCHAR_T +# endif + +# ifdef XML_UNICODE_WCHAR_T +# ifndef XML_UNICODE +# define XML_UNICODE +# endif +# if defined(__SIZEOF_WCHAR_T__) && (__SIZEOF_WCHAR_T__ != 2) +# error "sizeof(wchar_t) != 2; Need -fshort-wchar for both Expat and libc" +# endif +# endif + +# ifdef XML_UNICODE /* Information is UTF-16 encoded. */ +# ifdef XML_UNICODE_WCHAR_T typedef wchar_t XML_Char; typedef wchar_t XML_LChar; -#else +# else typedef unsigned short XML_Char; typedef char XML_LChar; -#endif /* XML_UNICODE_WCHAR_T */ -#else /* Information is UTF-8 encoded. */ +# endif /* XML_UNICODE_WCHAR_T */ +# else /* Information is UTF-8 encoded. */ typedef char XML_Char; typedef char XML_LChar; -#endif /* XML_UNICODE */ +# endif /* XML_UNICODE */ -#ifdef XML_LARGE_SIZE /* Use large integers for file/stream positions. */ -#if defined(XML_USE_MSC_EXTENSIONS) && _MSC_VER < 1400 -typedef __int64 XML_Index; -typedef unsigned __int64 XML_Size; -#else +# ifdef XML_LARGE_SIZE /* Use large integers for file/stream positions. */ typedef long long XML_Index; typedef unsigned long long XML_Size; -#endif -#else +# else typedef long XML_Index; typedef unsigned long XML_Size; -#endif /* XML_LARGE_SIZE */ +# endif /* XML_LARGE_SIZE */ -#ifdef __cplusplus +# ifdef __cplusplus } -#endif +# endif #endif /* not Expat_External_INCLUDED */ diff --git a/cextern/expat/lib/expat_static.dsp b/cextern/expat/lib/expat_static.dsp deleted file mode 100755 index 1d68fcefb66e..000000000000 --- a/cextern/expat/lib/expat_static.dsp +++ /dev/null @@ -1,162 +0,0 @@ -# Microsoft Developer Studio Project File - Name="expat_static" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Static Library" 0x0104 - -CFG=expat_static - Win32 Debug -!MESSAGE This is not a valid makefile. To build this project using NMAKE, -!MESSAGE use the Export Makefile command and run -!MESSAGE -!MESSAGE NMAKE /f "expat_static.mak". -!MESSAGE -!MESSAGE You can specify a configuration when running NMAKE -!MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE -!MESSAGE NMAKE /f "expat_static.mak" CFG="expat_static - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "expat_static - Win32 Release" (based on "Win32 (x86) Static Library") -!MESSAGE "expat_static - Win32 Debug" (based on "Win32 (x86) Static Library") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -CPP=cl.exe -RSC=rc.exe - -!IF "$(CFG)" == "expat_static - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "expat_static___Win32_Release" -# PROP BASE Intermediate_Dir "expat_static___Win32_Release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "..\win32\bin\Release" -# PROP Intermediate_Dir "..\win32\tmp\Release_static" -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c -# ADD CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "_WINDOWS" /D "NDEBUG" /D "_MBCS" /D "_LIB" /D "COMPILED_FROM_DSP" /FD /c -# SUBTRACT CPP /YX -# ADD BASE RSC /l 0x1009 /d "NDEBUG" -# ADD RSC /l 0x409 /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LIB32=link.exe -lib -# ADD BASE LIB32 /nologo -# ADD LIB32 /nologo /out:"..\win32\bin\Release/libexpatMT.lib" - -!ELSEIF "$(CFG)" == "expat_static - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "expat_static___Win32_Debug" -# PROP BASE Intermediate_Dir "expat_static___Win32_Debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "..\win32\bin\Debug" -# PROP Intermediate_Dir "..\win32\tmp\Debug_static" -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W3 /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "COMPILED_FROM_DSP" /D "_MBCS" /D "_LIB" /FR /FD /GZ /c -# SUBTRACT CPP /YX -# ADD BASE RSC /l 0x1009 /d "_DEBUG" -# ADD RSC /l 0x409 /d "_DEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LIB32=link.exe -lib -# ADD BASE LIB32 /nologo -# ADD LIB32 /nologo /out:"..\win32\bin\Debug\libexpatMT.lib" - -!ENDIF - -# Begin Target - -# Name "expat_static - Win32 Release" -# Name "expat_static - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=.\xmlparse.c -# End Source File -# Begin Source File - -SOURCE=.\xmlrole.c -# End Source File -# Begin Source File - -SOURCE=.\xmltok.c -# End Source File -# Begin Source File - -SOURCE=.\xmltok_impl.c -# End Source File -# Begin Source File - -SOURCE=.\xmltok_ns.c -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE=.\ascii.h -# End Source File -# Begin Source File - -SOURCE=.\asciitab.h -# End Source File -# Begin Source File - -SOURCE=.\expat.h -# End Source File -# Begin Source File - -SOURCE=.\expat_external.h -# End Source File -# Begin Source File - -SOURCE=.\iasciitab.h -# End Source File -# Begin Source File - -SOURCE=.\internal.h -# End Source File -# Begin Source File - -SOURCE=.\latin1tab.h -# End Source File -# Begin Source File - -SOURCE=.\nametab.h -# End Source File -# Begin Source File - -SOURCE=.\utf8tab.h -# End Source File -# Begin Source File - -SOURCE=.\xmlrole.h -# End Source File -# Begin Source File - -SOURCE=.\xmltok.h -# End Source File -# Begin Source File - -SOURCE=.\xmltok_impl.h -# End Source File -# End Group -# End Target -# End Project diff --git a/cextern/expat/lib/expatw.dsp b/cextern/expat/lib/expatw.dsp deleted file mode 100755 index fa40dc55d589..000000000000 --- a/cextern/expat/lib/expatw.dsp +++ /dev/null @@ -1,185 +0,0 @@ -# Microsoft Developer Studio Project File - Name="expatw" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 - -CFG=expatw - Win32 Debug -!MESSAGE This is not a valid makefile. To build this project using NMAKE, -!MESSAGE use the Export Makefile command and run -!MESSAGE -!MESSAGE NMAKE /f "expatw.mak". -!MESSAGE -!MESSAGE You can specify a configuration when running NMAKE -!MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE -!MESSAGE NMAKE /f "expatw.mak" CFG="expatw - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "expatw - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") -!MESSAGE "expatw - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -CPP=cl.exe -MTL=midl.exe -RSC=rc.exe - -!IF "$(CFG)" == "expatw - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "Release" -# PROP BASE Intermediate_Dir "Release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "..\win32\bin\Release" -# PROP Intermediate_Dir "..\win32\tmp\Release-w" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "EXPAT_EXPORTS" /Yu"stdafx.h" /FD /c -# ADD CPP /nologo /MT /W3 /GX /O2 /D "NDEBUG" /D "COMPILED_FROM_DSP" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "XML_UNICODE_WCHAR_T" /FD /c -# SUBTRACT CPP /YX /Yc /Yu -# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 -# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /pdb:none /machine:I386 /out:"..\win32\bin\Release/libexpatw.dll" - -!ELSEIF "$(CFG)" == "expatw - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "Debug" -# PROP BASE Intermediate_Dir "Debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "..\win32\bin\Debug" -# PROP Intermediate_Dir "..\win32\tmp\Debug-w" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "EXPAT_EXPORTS" /Yu"stdafx.h" /FD /GZ /c -# ADD CPP /nologo /MTd /W3 /GX /ZI /Od /D "_DEBUG" /D "COMPILED_FROM_DSP" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "XML_UNICODE_WCHAR_T" /FR /FD /GZ /c -# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 -# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /d "_DEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /pdb:none /debug /machine:I386 /out:"..\win32\bin\Debug/libexpatw.dll" - -!ENDIF - -# Begin Target - -# Name "expatw - Win32 Release" -# Name "expatw - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=.\libexpatw.def -# End Source File -# Begin Source File - -SOURCE=.\xmlparse.c - -!IF "$(CFG)" == "expatw - Win32 Release" - -!ELSEIF "$(CFG)" == "expatw - Win32 Debug" - -# ADD CPP /GX- /Od - -!ENDIF - -# End Source File -# Begin Source File - -SOURCE=.\xmlrole.c -# End Source File -# Begin Source File - -SOURCE=.\xmltok.c -# End Source File -# Begin Source File - -SOURCE=.\xmltok_impl.c -# End Source File -# Begin Source File - -SOURCE=.\xmltok_ns.c -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE=.\ascii.h -# End Source File -# Begin Source File - -SOURCE=.\asciitab.h -# End Source File -# Begin Source File - -SOURCE=.\expat.h -# End Source File -# Begin Source File - -SOURCE=.\expat_external.h -# End Source File -# Begin Source File - -SOURCE=.\iasciitab.h -# End Source File -# Begin Source File - -SOURCE=.\internal.h -# End Source File -# Begin Source File - -SOURCE=.\latin1tab.h -# End Source File -# Begin Source File - -SOURCE=.\nametab.h -# End Source File -# Begin Source File - -SOURCE=.\utf8tab.h -# End Source File -# Begin Source File - -SOURCE=.\xmlrole.h -# End Source File -# Begin Source File - -SOURCE=.\xmltok.h -# End Source File -# Begin Source File - -SOURCE=.\xmltok_impl.h -# End Source File -# End Group -# Begin Group "Resource Files" - -# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" -# End Group -# End Target -# End Project diff --git a/cextern/expat/lib/expatw_static.dsp b/cextern/expat/lib/expatw_static.dsp deleted file mode 100755 index f13dd72978a6..000000000000 --- a/cextern/expat/lib/expatw_static.dsp +++ /dev/null @@ -1,162 +0,0 @@ -# Microsoft Developer Studio Project File - Name="expatw_static" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Static Library" 0x0104 - -CFG=expatw_static - Win32 Debug -!MESSAGE This is not a valid makefile. To build this project using NMAKE, -!MESSAGE use the Export Makefile command and run -!MESSAGE -!MESSAGE NMAKE /f "expatw_static.mak". -!MESSAGE -!MESSAGE You can specify a configuration when running NMAKE -!MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE -!MESSAGE NMAKE /f "expatw_static.mak" CFG="expatw_static - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "expatw_static - Win32 Release" (based on "Win32 (x86) Static Library") -!MESSAGE "expatw_static - Win32 Debug" (based on "Win32 (x86) Static Library") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -CPP=cl.exe -RSC=rc.exe - -!IF "$(CFG)" == "expatw_static - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "expatw_static___Win32_Release" -# PROP BASE Intermediate_Dir "expatw_static___Win32_Release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "..\win32\bin\Release" -# PROP Intermediate_Dir "..\win32\tmp\Release-w_static" -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c -# ADD CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "_WINDOWS" /D "NDEBUG" /D "_MBCS" /D "_LIB" /D "COMPILED_FROM_DSP" /D "XML_UNICODE_WCHAR_T" /FD /c -# SUBTRACT CPP /YX -# ADD BASE RSC /l 0x1009 /d "NDEBUG" -# ADD RSC /l 0x409 /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LIB32=link.exe -lib -# ADD BASE LIB32 /nologo -# ADD LIB32 /nologo /out:"..\win32\bin\Release\libexpatwMT.lib" - -!ELSEIF "$(CFG)" == "expatw_static - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "expatw_static___Win32_Debug" -# PROP BASE Intermediate_Dir "expatw_static___Win32_Debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "..\win32\bin\Debug" -# PROP Intermediate_Dir "..\win32\tmp\Debug-w_static" -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W3 /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_LIB" /D "COMPILED_FROM_DSP" /D "XML_UNICODE_WCHAR_T" /FR /FD /GZ /c -# SUBTRACT CPP /YX -# ADD BASE RSC /l 0x1009 /d "_DEBUG" -# ADD RSC /l 0x409 /d "_DEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LIB32=link.exe -lib -# ADD BASE LIB32 /nologo -# ADD LIB32 /nologo /out:"..\win32\bin\Debug\libexpatwMT.lib" - -!ENDIF - -# Begin Target - -# Name "expatw_static - Win32 Release" -# Name "expatw_static - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=.\xmlparse.c -# End Source File -# Begin Source File - -SOURCE=.\xmlrole.c -# End Source File -# Begin Source File - -SOURCE=.\xmltok.c -# End Source File -# Begin Source File - -SOURCE=.\xmltok_impl.c -# End Source File -# Begin Source File - -SOURCE=.\xmltok_ns.c -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE=.\ascii.h -# End Source File -# Begin Source File - -SOURCE=.\asciitab.h -# End Source File -# Begin Source File - -SOURCE=.\expat.h -# End Source File -# Begin Source File - -SOURCE=.\expat_external.h -# End Source File -# Begin Source File - -SOURCE=.\iasciitab.h -# End Source File -# Begin Source File - -SOURCE=.\internal.h -# End Source File -# Begin Source File - -SOURCE=.\latin1tab.h -# End Source File -# Begin Source File - -SOURCE=.\nametab.h -# End Source File -# Begin Source File - -SOURCE=.\utf8tab.h -# End Source File -# Begin Source File - -SOURCE=.\xmlrole.h -# End Source File -# Begin Source File - -SOURCE=.\xmltok.h -# End Source File -# Begin Source File - -SOURCE=.\xmltok_impl.h -# End Source File -# End Group -# End Target -# End Project diff --git a/cextern/expat/lib/iasciitab.h b/cextern/expat/lib/iasciitab.h old mode 100755 new mode 100644 index 24a1d5ccc9a5..5d8646f2a318 --- a/cextern/expat/lib/iasciitab.h +++ b/cextern/expat/lib/iasciitab.h @@ -1,37 +1,67 @@ -/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd - See the file COPYING for copying permission. +/* + __ __ _ + ___\ \/ /_ __ __ _| |_ + / _ \\ /| '_ \ / _` | __| + | __// \| |_) | (_| | |_ + \___/_/\_\ .__/ \__,_|\__| + |_| XML parser + + Copyright (c) 1997-2000 Thai Open Source Software Center Ltd + Copyright (c) 2000 Clark Cooper + Copyright (c) 2002 Fred L. Drake, Jr. + Copyright (c) 2017 Sebastian Pipping + Licensed under the MIT license: + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to permit + persons to whom the Software is furnished to do so, subject to the + following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* Like asciitab.h, except that 0xD has code BT_S rather than BT_CR */ /* 0x00 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, -/* 0x04 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, -/* 0x08 */ BT_NONXML, BT_S, BT_LF, BT_NONXML, -/* 0x0C */ BT_NONXML, BT_S, BT_NONXML, BT_NONXML, -/* 0x10 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, -/* 0x14 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, -/* 0x18 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, -/* 0x1C */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, -/* 0x20 */ BT_S, BT_EXCL, BT_QUOT, BT_NUM, -/* 0x24 */ BT_OTHER, BT_PERCNT, BT_AMP, BT_APOS, -/* 0x28 */ BT_LPAR, BT_RPAR, BT_AST, BT_PLUS, -/* 0x2C */ BT_COMMA, BT_MINUS, BT_NAME, BT_SOL, -/* 0x30 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT, -/* 0x34 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT, -/* 0x38 */ BT_DIGIT, BT_DIGIT, BT_COLON, BT_SEMI, -/* 0x3C */ BT_LT, BT_EQUALS, BT_GT, BT_QUEST, -/* 0x40 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX, -/* 0x44 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT, -/* 0x48 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, -/* 0x4C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, -/* 0x50 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, -/* 0x54 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, -/* 0x58 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_LSQB, -/* 0x5C */ BT_OTHER, BT_RSQB, BT_OTHER, BT_NMSTRT, -/* 0x60 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX, -/* 0x64 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT, -/* 0x68 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, -/* 0x6C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, -/* 0x70 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, -/* 0x74 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, -/* 0x78 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER, -/* 0x7C */ BT_VERBAR, BT_OTHER, BT_OTHER, BT_OTHER, + /* 0x04 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, + /* 0x08 */ BT_NONXML, BT_S, BT_LF, BT_NONXML, + /* 0x0C */ BT_NONXML, BT_S, BT_NONXML, BT_NONXML, + /* 0x10 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, + /* 0x14 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, + /* 0x18 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, + /* 0x1C */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, + /* 0x20 */ BT_S, BT_EXCL, BT_QUOT, BT_NUM, + /* 0x24 */ BT_OTHER, BT_PERCNT, BT_AMP, BT_APOS, + /* 0x28 */ BT_LPAR, BT_RPAR, BT_AST, BT_PLUS, + /* 0x2C */ BT_COMMA, BT_MINUS, BT_NAME, BT_SOL, + /* 0x30 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT, + /* 0x34 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT, + /* 0x38 */ BT_DIGIT, BT_DIGIT, BT_COLON, BT_SEMI, + /* 0x3C */ BT_LT, BT_EQUALS, BT_GT, BT_QUEST, + /* 0x40 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX, + /* 0x44 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT, + /* 0x48 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, + /* 0x4C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, + /* 0x50 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, + /* 0x54 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, + /* 0x58 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_LSQB, + /* 0x5C */ BT_OTHER, BT_RSQB, BT_OTHER, BT_NMSTRT, + /* 0x60 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX, + /* 0x64 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT, + /* 0x68 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, + /* 0x6C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, + /* 0x70 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, + /* 0x74 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, + /* 0x78 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER, + /* 0x7C */ BT_VERBAR, BT_OTHER, BT_OTHER, BT_OTHER, diff --git a/cextern/expat/lib/internal.h b/cextern/expat/lib/internal.h old mode 100755 new mode 100644 index dd5454831da2..8f5edf48ef7c --- a/cextern/expat/lib/internal.h +++ b/cextern/expat/lib/internal.h @@ -18,9 +18,44 @@ Note: Use of these macros is based on judgement, not hard rules, and therefore subject to change. + __ __ _ + ___\ \/ /_ __ __ _| |_ + / _ \\ /| '_ \ / _` | __| + | __// \| |_) | (_| | |_ + \___/_/\_\ .__/ \__,_|\__| + |_| XML parser + + Copyright (c) 2002-2003 Fred L. Drake, Jr. + Copyright (c) 2002-2006 Karl Waclawek + Copyright (c) 2003 Greg Stein + Copyright (c) 2016-2025 Sebastian Pipping + Copyright (c) 2018 Yury Gribov + Copyright (c) 2019 David Loffredo + Copyright (c) 2023-2024 Sony Corporation / Snild Dolkow + Copyright (c) 2024 Taichi Haradaguchi <20001722@ymail.ne.jp> + Licensed under the MIT license: + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to permit + persons to whom the Software is furnished to do so, subject to the + following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#if defined(__GNUC__) && defined(__i386__) && !defined(__MINGW32__) +#if defined(__GNUC__) && defined(__i386__) && ! defined(__MINGW32__) /* We'll use this version by default only where we know it helps. regparm() generates warnings on Solaris boxes. See SF bug #692878. @@ -30,8 +65,8 @@ #define FASTCALL __attribute__((stdcall, regparm(3))) and let's try this: */ -#define FASTCALL __attribute__((regparm(3))) -#define PTRFASTCALL __attribute__((regparm(3))) +# define FASTCALL __attribute__((regparm(3))) +# define PTRFASTCALL __attribute__((regparm(3))) #endif /* Using __fastcall seems to have an unexpected negative effect under @@ -45,29 +80,114 @@ /* Make sure all of these are defined if they aren't already. */ #ifndef FASTCALL -#define FASTCALL +# define FASTCALL #endif #ifndef PTRCALL -#define PTRCALL +# define PTRCALL #endif #ifndef PTRFASTCALL -#define PTRFASTCALL +# define PTRFASTCALL #endif #ifndef XML_MIN_SIZE -#if !defined(__cplusplus) && !defined(inline) -#ifdef __GNUC__ -#define inline __inline -#endif /* __GNUC__ */ -#endif +# if ! defined(__cplusplus) && ! defined(inline) +# ifdef __GNUC__ +# define inline __inline +# endif /* __GNUC__ */ +# endif #endif /* XML_MIN_SIZE */ #ifdef __cplusplus -#define inline inline +# define inline inline +#else +# ifndef inline +# define inline +# endif +#endif + +#include // ULONG_MAX +#include // size_t + +#if defined(_WIN32) \ + && (! defined(__USE_MINGW_ANSI_STDIO) \ + || (1 - __USE_MINGW_ANSI_STDIO - 1 == 0)) +# define EXPAT_FMT_ULL(midpart) "%" midpart "I64u" +# if defined(_WIN64) // Note: modifiers "td" and "zu" do not work for MinGW +# define EXPAT_FMT_PTRDIFF_T(midpart) "%" midpart "I64d" +# define EXPAT_FMT_SIZE_T(midpart) "%" midpart "I64u" +# else +# define EXPAT_FMT_PTRDIFF_T(midpart) "%" midpart "d" +# define EXPAT_FMT_SIZE_T(midpart) "%" midpart "u" +# endif #else -#ifndef inline -#define inline +# define EXPAT_FMT_ULL(midpart) "%" midpart "llu" +# if ! defined(ULONG_MAX) +# error Compiler did not define ULONG_MAX for us +# elif ULONG_MAX == 18446744073709551615u // 2^64-1 +# define EXPAT_FMT_PTRDIFF_T(midpart) "%" midpart "ld" +# define EXPAT_FMT_SIZE_T(midpart) "%" midpart "lu" +# elif defined(EMSCRIPTEN) // 32bit mode Emscripten +# define EXPAT_FMT_PTRDIFF_T(midpart) "%" midpart "ld" +# define EXPAT_FMT_SIZE_T(midpart) "%" midpart "zu" +# else +# define EXPAT_FMT_PTRDIFF_T(midpart) "%" midpart "d" +# define EXPAT_FMT_SIZE_T(midpart) "%" midpart "u" +# endif +#endif + +#ifndef UNUSED_P +# define UNUSED_P(p) (void)p #endif + +/* NOTE BEGIN If you ever patch these defaults to greater values + for non-attack XML payload in your environment, + please file a bug report with libexpat. Thank you! +*/ +#define EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_MAXIMUM_AMPLIFICATION_DEFAULT \ + 100.0f +#define EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT \ + 8388608 // 8 MiB, 2^23 + +#define EXPAT_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION_DEFAULT 100.0f +#define EXPAT_ALLOC_TRACKER_ACTIVATION_THRESHOLD_DEFAULT \ + 67108864 // 64 MiB, 2^26 + +// NOTE: If function expat_alloc was user facing, EXPAT_MALLOC_ALIGNMENT would +// have to take sizeof(long double) into account +#define EXPAT_MALLOC_ALIGNMENT sizeof(long long) // largest parser (sub)member +#define EXPAT_MALLOC_PADDING ((EXPAT_MALLOC_ALIGNMENT) - sizeof(size_t)) + +/* NOTE END */ + +#include "expat.h" // so we can use type XML_Parser below + +#ifdef __cplusplus +extern "C" { +#endif + +void _INTERNAL_trim_to_complete_utf8_characters(const char *from, + const char **fromLimRef); + +#if defined(XML_GE) && XML_GE == 1 +unsigned long long testingAccountingGetCountBytesDirect(XML_Parser parser); +unsigned long long testingAccountingGetCountBytesIndirect(XML_Parser parser); +const char *unsignedCharToPrintable(unsigned char c); +#endif + +extern +#if ! defined(XML_TESTING) + const +#endif + XML_Bool g_reparseDeferralEnabledDefault; // written ONLY in runtests.c +#if defined(XML_TESTING) +void *expat_malloc(XML_Parser parser, size_t size, int sourceLine); +void expat_free(XML_Parser parser, void *ptr, int sourceLine); +void *expat_realloc(XML_Parser parser, void *ptr, size_t size, int sourceLine); +extern unsigned int g_bytesScanned; // used for testing only +#endif + +#ifdef __cplusplus +} #endif diff --git a/cextern/expat/lib/latin1tab.h b/cextern/expat/lib/latin1tab.h old mode 100755 new mode 100644 index 53c25d76b268..b681d278af65 --- a/cextern/expat/lib/latin1tab.h +++ b/cextern/expat/lib/latin1tab.h @@ -1,36 +1,66 @@ -/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd - See the file COPYING for copying permission. +/* + __ __ _ + ___\ \/ /_ __ __ _| |_ + / _ \\ /| '_ \ / _` | __| + | __// \| |_) | (_| | |_ + \___/_/\_\ .__/ \__,_|\__| + |_| XML parser + + Copyright (c) 1997-2000 Thai Open Source Software Center Ltd + Copyright (c) 2000 Clark Cooper + Copyright (c) 2002 Fred L. Drake, Jr. + Copyright (c) 2017 Sebastian Pipping + Licensed under the MIT license: + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to permit + persons to whom the Software is furnished to do so, subject to the + following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* 0x80 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, -/* 0x84 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, -/* 0x88 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, -/* 0x8C */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, -/* 0x90 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, -/* 0x94 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, -/* 0x98 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, -/* 0x9C */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, -/* 0xA0 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, -/* 0xA4 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, -/* 0xA8 */ BT_OTHER, BT_OTHER, BT_NMSTRT, BT_OTHER, -/* 0xAC */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, -/* 0xB0 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, -/* 0xB4 */ BT_OTHER, BT_NMSTRT, BT_OTHER, BT_NAME, -/* 0xB8 */ BT_OTHER, BT_OTHER, BT_NMSTRT, BT_OTHER, -/* 0xBC */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, -/* 0xC0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, -/* 0xC4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, -/* 0xC8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, -/* 0xCC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, -/* 0xD0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, -/* 0xD4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER, -/* 0xD8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, -/* 0xDC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, -/* 0xE0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, -/* 0xE4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, -/* 0xE8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, -/* 0xEC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, -/* 0xF0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, -/* 0xF4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER, -/* 0xF8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, -/* 0xFC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, + /* 0x84 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, + /* 0x88 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, + /* 0x8C */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, + /* 0x90 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, + /* 0x94 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, + /* 0x98 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, + /* 0x9C */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, + /* 0xA0 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, + /* 0xA4 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, + /* 0xA8 */ BT_OTHER, BT_OTHER, BT_NMSTRT, BT_OTHER, + /* 0xAC */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, + /* 0xB0 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, + /* 0xB4 */ BT_OTHER, BT_NMSTRT, BT_OTHER, BT_NAME, + /* 0xB8 */ BT_OTHER, BT_OTHER, BT_NMSTRT, BT_OTHER, + /* 0xBC */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, + /* 0xC0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, + /* 0xC4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, + /* 0xC8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, + /* 0xCC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, + /* 0xD0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, + /* 0xD4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER, + /* 0xD8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, + /* 0xDC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, + /* 0xE0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, + /* 0xE4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, + /* 0xE8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, + /* 0xEC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, + /* 0xF0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, + /* 0xF4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER, + /* 0xF8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, + /* 0xFC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, diff --git a/cextern/expat/lib/libexpat.def b/cextern/expat/lib/libexpat.def deleted file mode 100755 index 3920bbcf9016..000000000000 --- a/cextern/expat/lib/libexpat.def +++ /dev/null @@ -1,73 +0,0 @@ -; DEF file for MS VC++ - -LIBRARY -EXPORTS - XML_DefaultCurrent @1 - XML_ErrorString @2 - XML_ExpatVersion @3 - XML_ExpatVersionInfo @4 - XML_ExternalEntityParserCreate @5 - XML_GetBase @6 - XML_GetBuffer @7 - XML_GetCurrentByteCount @8 - XML_GetCurrentByteIndex @9 - XML_GetCurrentColumnNumber @10 - XML_GetCurrentLineNumber @11 - XML_GetErrorCode @12 - XML_GetIdAttributeIndex @13 - XML_GetInputContext @14 - XML_GetSpecifiedAttributeCount @15 - XML_Parse @16 - XML_ParseBuffer @17 - XML_ParserCreate @18 - XML_ParserCreateNS @19 - XML_ParserCreate_MM @20 - XML_ParserFree @21 - XML_SetAttlistDeclHandler @22 - XML_SetBase @23 - XML_SetCdataSectionHandler @24 - XML_SetCharacterDataHandler @25 - XML_SetCommentHandler @26 - XML_SetDefaultHandler @27 - XML_SetDefaultHandlerExpand @28 - XML_SetDoctypeDeclHandler @29 - XML_SetElementDeclHandler @30 - XML_SetElementHandler @31 - XML_SetEncoding @32 - XML_SetEndCdataSectionHandler @33 - XML_SetEndDoctypeDeclHandler @34 - XML_SetEndElementHandler @35 - XML_SetEndNamespaceDeclHandler @36 - XML_SetEntityDeclHandler @37 - XML_SetExternalEntityRefHandler @38 - XML_SetExternalEntityRefHandlerArg @39 - XML_SetNamespaceDeclHandler @40 - XML_SetNotStandaloneHandler @41 - XML_SetNotationDeclHandler @42 - XML_SetParamEntityParsing @43 - XML_SetProcessingInstructionHandler @44 - XML_SetReturnNSTriplet @45 - XML_SetStartCdataSectionHandler @46 - XML_SetStartDoctypeDeclHandler @47 - XML_SetStartElementHandler @48 - XML_SetStartNamespaceDeclHandler @49 - XML_SetUnknownEncodingHandler @50 - XML_SetUnparsedEntityDeclHandler @51 - XML_SetUserData @52 - XML_SetXmlDeclHandler @53 - XML_UseParserAsHandlerArg @54 -; added with version 1.95.3 - XML_ParserReset @55 - XML_SetSkippedEntityHandler @56 -; added with version 1.95.5 - XML_GetFeatureList @57 - XML_UseForeignDTD @58 -; added with version 1.95.6 - XML_FreeContentModel @59 - XML_MemMalloc @60 - XML_MemRealloc @61 - XML_MemFree @62 -; added with version 1.95.8 - XML_StopParser @63 - XML_ResumeParser @64 - XML_GetParsingStatus @65 diff --git a/cextern/expat/lib/libexpatw.def b/cextern/expat/lib/libexpatw.def deleted file mode 100755 index 3920bbcf9016..000000000000 --- a/cextern/expat/lib/libexpatw.def +++ /dev/null @@ -1,73 +0,0 @@ -; DEF file for MS VC++ - -LIBRARY -EXPORTS - XML_DefaultCurrent @1 - XML_ErrorString @2 - XML_ExpatVersion @3 - XML_ExpatVersionInfo @4 - XML_ExternalEntityParserCreate @5 - XML_GetBase @6 - XML_GetBuffer @7 - XML_GetCurrentByteCount @8 - XML_GetCurrentByteIndex @9 - XML_GetCurrentColumnNumber @10 - XML_GetCurrentLineNumber @11 - XML_GetErrorCode @12 - XML_GetIdAttributeIndex @13 - XML_GetInputContext @14 - XML_GetSpecifiedAttributeCount @15 - XML_Parse @16 - XML_ParseBuffer @17 - XML_ParserCreate @18 - XML_ParserCreateNS @19 - XML_ParserCreate_MM @20 - XML_ParserFree @21 - XML_SetAttlistDeclHandler @22 - XML_SetBase @23 - XML_SetCdataSectionHandler @24 - XML_SetCharacterDataHandler @25 - XML_SetCommentHandler @26 - XML_SetDefaultHandler @27 - XML_SetDefaultHandlerExpand @28 - XML_SetDoctypeDeclHandler @29 - XML_SetElementDeclHandler @30 - XML_SetElementHandler @31 - XML_SetEncoding @32 - XML_SetEndCdataSectionHandler @33 - XML_SetEndDoctypeDeclHandler @34 - XML_SetEndElementHandler @35 - XML_SetEndNamespaceDeclHandler @36 - XML_SetEntityDeclHandler @37 - XML_SetExternalEntityRefHandler @38 - XML_SetExternalEntityRefHandlerArg @39 - XML_SetNamespaceDeclHandler @40 - XML_SetNotStandaloneHandler @41 - XML_SetNotationDeclHandler @42 - XML_SetParamEntityParsing @43 - XML_SetProcessingInstructionHandler @44 - XML_SetReturnNSTriplet @45 - XML_SetStartCdataSectionHandler @46 - XML_SetStartDoctypeDeclHandler @47 - XML_SetStartElementHandler @48 - XML_SetStartNamespaceDeclHandler @49 - XML_SetUnknownEncodingHandler @50 - XML_SetUnparsedEntityDeclHandler @51 - XML_SetUserData @52 - XML_SetXmlDeclHandler @53 - XML_UseParserAsHandlerArg @54 -; added with version 1.95.3 - XML_ParserReset @55 - XML_SetSkippedEntityHandler @56 -; added with version 1.95.5 - XML_GetFeatureList @57 - XML_UseForeignDTD @58 -; added with version 1.95.6 - XML_FreeContentModel @59 - XML_MemMalloc @60 - XML_MemRealloc @61 - XML_MemFree @62 -; added with version 1.95.8 - XML_StopParser @63 - XML_ResumeParser @64 - XML_GetParsingStatus @65 diff --git a/cextern/expat/lib/macconfig.h b/cextern/expat/lib/macconfig.h deleted file mode 100755 index 2725caaf54a2..000000000000 --- a/cextern/expat/lib/macconfig.h +++ /dev/null @@ -1,53 +0,0 @@ -/*================================================================ -** Copyright 2000, Clark Cooper -** All rights reserved. -** -** This is free software. You are permitted to copy, distribute, or modify -** it under the terms of the MIT/X license (contained in the COPYING file -** with this distribution.) -** -*/ - -#ifndef MACCONFIG_H -#define MACCONFIG_H - - -/* 1234 = LIL_ENDIAN, 4321 = BIGENDIAN */ -#define BYTEORDER 4321 - -/* Define to 1 if you have the `bcopy' function. */ -#undef HAVE_BCOPY - -/* Define to 1 if you have the `memmove' function. */ -#define HAVE_MEMMOVE - -/* Define to 1 if you have a working `mmap' system call. */ -#undef HAVE_MMAP - -/* Define to 1 if you have the header file. */ -#undef HAVE_UNISTD_H - -/* whether byteorder is bigendian */ -#define WORDS_BIGENDIAN - -/* Define to specify how much context to retain around the current parse - point. */ -#undef XML_CONTEXT_BYTES - -/* Define to make parameter entity parsing functionality available. */ -#define XML_DTD - -/* Define to make XML Namespaces functionality available. */ -#define XML_NS - -/* Define to empty if `const' does not conform to ANSI C. */ -#undef const - -/* Define to `long' if does not define. */ -#define off_t long - -/* Define to `unsigned' if does not define. */ -#undef size_t - - -#endif /* ifndef MACCONFIG_H */ diff --git a/cextern/expat/lib/nametab.h b/cextern/expat/lib/nametab.h old mode 100755 new mode 100644 index b05e62c77a6c..63485446b967 --- a/cextern/expat/lib/nametab.h +++ b/cextern/expat/lib/nametab.h @@ -1,150 +1,136 @@ +/* + __ __ _ + ___\ \/ /_ __ __ _| |_ + / _ \\ /| '_ \ / _` | __| + | __// \| |_) | (_| | |_ + \___/_/\_\ .__/ \__,_|\__| + |_| XML parser + + Copyright (c) 2000 Clark Cooper + Copyright (c) 2017 Sebastian Pipping + Licensed under the MIT license: + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to permit + persons to whom the Software is furnished to do so, subject to the + following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + static const unsigned namingBitmap[] = { -0x00000000, 0x00000000, 0x00000000, 0x00000000, -0x00000000, 0x00000000, 0x00000000, 0x00000000, -0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, -0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, -0x00000000, 0x04000000, 0x87FFFFFE, 0x07FFFFFE, -0x00000000, 0x00000000, 0xFF7FFFFF, 0xFF7FFFFF, -0xFFFFFFFF, 0x7FF3FFFF, 0xFFFFFDFE, 0x7FFFFFFF, -0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFE00F, 0xFC31FFFF, -0x00FFFFFF, 0x00000000, 0xFFFF0000, 0xFFFFFFFF, -0xFFFFFFFF, 0xF80001FF, 0x00000003, 0x00000000, -0x00000000, 0x00000000, 0x00000000, 0x00000000, -0xFFFFD740, 0xFFFFFFFB, 0x547F7FFF, 0x000FFFFD, -0xFFFFDFFE, 0xFFFFFFFF, 0xDFFEFFFF, 0xFFFFFFFF, -0xFFFF0003, 0xFFFFFFFF, 0xFFFF199F, 0x033FCFFF, -0x00000000, 0xFFFE0000, 0x027FFFFF, 0xFFFFFFFE, -0x0000007F, 0x00000000, 0xFFFF0000, 0x000707FF, -0x00000000, 0x07FFFFFE, 0x000007FE, 0xFFFE0000, -0xFFFFFFFF, 0x7CFFFFFF, 0x002F7FFF, 0x00000060, -0xFFFFFFE0, 0x23FFFFFF, 0xFF000000, 0x00000003, -0xFFF99FE0, 0x03C5FDFF, 0xB0000000, 0x00030003, -0xFFF987E0, 0x036DFDFF, 0x5E000000, 0x001C0000, -0xFFFBAFE0, 0x23EDFDFF, 0x00000000, 0x00000001, -0xFFF99FE0, 0x23CDFDFF, 0xB0000000, 0x00000003, -0xD63DC7E0, 0x03BFC718, 0x00000000, 0x00000000, -0xFFFDDFE0, 0x03EFFDFF, 0x00000000, 0x00000003, -0xFFFDDFE0, 0x03EFFDFF, 0x40000000, 0x00000003, -0xFFFDDFE0, 0x03FFFDFF, 0x00000000, 0x00000003, -0x00000000, 0x00000000, 0x00000000, 0x00000000, -0xFFFFFFFE, 0x000D7FFF, 0x0000003F, 0x00000000, -0xFEF02596, 0x200D6CAE, 0x0000001F, 0x00000000, -0x00000000, 0x00000000, 0xFFFFFEFF, 0x000003FF, -0x00000000, 0x00000000, 0x00000000, 0x00000000, -0x00000000, 0x00000000, 0x00000000, 0x00000000, -0x00000000, 0xFFFFFFFF, 0xFFFF003F, 0x007FFFFF, -0x0007DAED, 0x50000000, 0x82315001, 0x002C62AB, -0x40000000, 0xF580C900, 0x00000007, 0x02010800, -0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, -0x0FFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x03FFFFFF, -0x3F3FFFFF, 0xFFFFFFFF, 0xAAFF3F3F, 0x3FFFFFFF, -0xFFFFFFFF, 0x5FDFFFFF, 0x0FCF1FDC, 0x1FDC1FFF, -0x00000000, 0x00004C40, 0x00000000, 0x00000000, -0x00000007, 0x00000000, 0x00000000, 0x00000000, -0x00000080, 0x000003FE, 0xFFFFFFFE, 0xFFFFFFFF, -0x001FFFFF, 0xFFFFFFFE, 0xFFFFFFFF, 0x07FFFFFF, -0xFFFFFFE0, 0x00001FFF, 0x00000000, 0x00000000, -0x00000000, 0x00000000, 0x00000000, 0x00000000, -0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, -0xFFFFFFFF, 0x0000003F, 0x00000000, 0x00000000, -0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, -0xFFFFFFFF, 0x0000000F, 0x00000000, 0x00000000, -0x00000000, 0x07FF6000, 0x87FFFFFE, 0x07FFFFFE, -0x00000000, 0x00800000, 0xFF7FFFFF, 0xFF7FFFFF, -0x00FFFFFF, 0x00000000, 0xFFFF0000, 0xFFFFFFFF, -0xFFFFFFFF, 0xF80001FF, 0x00030003, 0x00000000, -0xFFFFFFFF, 0xFFFFFFFF, 0x0000003F, 0x00000003, -0xFFFFD7C0, 0xFFFFFFFB, 0x547F7FFF, 0x000FFFFD, -0xFFFFDFFE, 0xFFFFFFFF, 0xDFFEFFFF, 0xFFFFFFFF, -0xFFFF007B, 0xFFFFFFFF, 0xFFFF199F, 0x033FCFFF, -0x00000000, 0xFFFE0000, 0x027FFFFF, 0xFFFFFFFE, -0xFFFE007F, 0xBBFFFFFB, 0xFFFF0016, 0x000707FF, -0x00000000, 0x07FFFFFE, 0x0007FFFF, 0xFFFF03FF, -0xFFFFFFFF, 0x7CFFFFFF, 0xFFEF7FFF, 0x03FF3DFF, -0xFFFFFFEE, 0xF3FFFFFF, 0xFF1E3FFF, 0x0000FFCF, -0xFFF99FEE, 0xD3C5FDFF, 0xB080399F, 0x0003FFCF, -0xFFF987E4, 0xD36DFDFF, 0x5E003987, 0x001FFFC0, -0xFFFBAFEE, 0xF3EDFDFF, 0x00003BBF, 0x0000FFC1, -0xFFF99FEE, 0xF3CDFDFF, 0xB0C0398F, 0x0000FFC3, -0xD63DC7EC, 0xC3BFC718, 0x00803DC7, 0x0000FF80, -0xFFFDDFEE, 0xC3EFFDFF, 0x00603DDF, 0x0000FFC3, -0xFFFDDFEC, 0xC3EFFDFF, 0x40603DDF, 0x0000FFC3, -0xFFFDDFEC, 0xC3FFFDFF, 0x00803DCF, 0x0000FFC3, -0x00000000, 0x00000000, 0x00000000, 0x00000000, -0xFFFFFFFE, 0x07FF7FFF, 0x03FF7FFF, 0x00000000, -0xFEF02596, 0x3BFF6CAE, 0x03FF3F5F, 0x00000000, -0x03000000, 0xC2A003FF, 0xFFFFFEFF, 0xFFFE03FF, -0xFEBF0FDF, 0x02FE3FFF, 0x00000000, 0x00000000, -0x00000000, 0x00000000, 0x00000000, 0x00000000, -0x00000000, 0x00000000, 0x1FFF0000, 0x00000002, -0x000000A0, 0x003EFFFE, 0xFFFFFFFE, 0xFFFFFFFF, -0x661FFFFF, 0xFFFFFFFE, 0xFFFFFFFF, 0x77FFFFFF, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x04000000, + 0x87FFFFFE, 0x07FFFFFE, 0x00000000, 0x00000000, 0xFF7FFFFF, 0xFF7FFFFF, + 0xFFFFFFFF, 0x7FF3FFFF, 0xFFFFFDFE, 0x7FFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFE00F, 0xFC31FFFF, 0x00FFFFFF, 0x00000000, 0xFFFF0000, 0xFFFFFFFF, + 0xFFFFFFFF, 0xF80001FF, 0x00000003, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xFFFFD740, 0xFFFFFFFB, 0x547F7FFF, 0x000FFFFD, + 0xFFFFDFFE, 0xFFFFFFFF, 0xDFFEFFFF, 0xFFFFFFFF, 0xFFFF0003, 0xFFFFFFFF, + 0xFFFF199F, 0x033FCFFF, 0x00000000, 0xFFFE0000, 0x027FFFFF, 0xFFFFFFFE, + 0x0000007F, 0x00000000, 0xFFFF0000, 0x000707FF, 0x00000000, 0x07FFFFFE, + 0x000007FE, 0xFFFE0000, 0xFFFFFFFF, 0x7CFFFFFF, 0x002F7FFF, 0x00000060, + 0xFFFFFFE0, 0x23FFFFFF, 0xFF000000, 0x00000003, 0xFFF99FE0, 0x03C5FDFF, + 0xB0000000, 0x00030003, 0xFFF987E0, 0x036DFDFF, 0x5E000000, 0x001C0000, + 0xFFFBAFE0, 0x23EDFDFF, 0x00000000, 0x00000001, 0xFFF99FE0, 0x23CDFDFF, + 0xB0000000, 0x00000003, 0xD63DC7E0, 0x03BFC718, 0x00000000, 0x00000000, + 0xFFFDDFE0, 0x03EFFDFF, 0x00000000, 0x00000003, 0xFFFDDFE0, 0x03EFFDFF, + 0x40000000, 0x00000003, 0xFFFDDFE0, 0x03FFFDFF, 0x00000000, 0x00000003, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFE, 0x000D7FFF, + 0x0000003F, 0x00000000, 0xFEF02596, 0x200D6CAE, 0x0000001F, 0x00000000, + 0x00000000, 0x00000000, 0xFFFFFEFF, 0x000003FF, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0xFFFFFFFF, 0xFFFF003F, 0x007FFFFF, 0x0007DAED, 0x50000000, + 0x82315001, 0x002C62AB, 0x40000000, 0xF580C900, 0x00000007, 0x02010800, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x0FFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0x03FFFFFF, 0x3F3FFFFF, 0xFFFFFFFF, 0xAAFF3F3F, 0x3FFFFFFF, + 0xFFFFFFFF, 0x5FDFFFFF, 0x0FCF1FDC, 0x1FDC1FFF, 0x00000000, 0x00004C40, + 0x00000000, 0x00000000, 0x00000007, 0x00000000, 0x00000000, 0x00000000, + 0x00000080, 0x000003FE, 0xFFFFFFFE, 0xFFFFFFFF, 0x001FFFFF, 0xFFFFFFFE, + 0xFFFFFFFF, 0x07FFFFFF, 0xFFFFFFE0, 0x00001FFF, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x0000003F, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x0000000F, + 0x00000000, 0x00000000, 0x00000000, 0x07FF6000, 0x87FFFFFE, 0x07FFFFFE, + 0x00000000, 0x00800000, 0xFF7FFFFF, 0xFF7FFFFF, 0x00FFFFFF, 0x00000000, + 0xFFFF0000, 0xFFFFFFFF, 0xFFFFFFFF, 0xF80001FF, 0x00030003, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x0000003F, 0x00000003, 0xFFFFD7C0, 0xFFFFFFFB, + 0x547F7FFF, 0x000FFFFD, 0xFFFFDFFE, 0xFFFFFFFF, 0xDFFEFFFF, 0xFFFFFFFF, + 0xFFFF007B, 0xFFFFFFFF, 0xFFFF199F, 0x033FCFFF, 0x00000000, 0xFFFE0000, + 0x027FFFFF, 0xFFFFFFFE, 0xFFFE007F, 0xBBFFFFFB, 0xFFFF0016, 0x000707FF, + 0x00000000, 0x07FFFFFE, 0x0007FFFF, 0xFFFF03FF, 0xFFFFFFFF, 0x7CFFFFFF, + 0xFFEF7FFF, 0x03FF3DFF, 0xFFFFFFEE, 0xF3FFFFFF, 0xFF1E3FFF, 0x0000FFCF, + 0xFFF99FEE, 0xD3C5FDFF, 0xB080399F, 0x0003FFCF, 0xFFF987E4, 0xD36DFDFF, + 0x5E003987, 0x001FFFC0, 0xFFFBAFEE, 0xF3EDFDFF, 0x00003BBF, 0x0000FFC1, + 0xFFF99FEE, 0xF3CDFDFF, 0xB0C0398F, 0x0000FFC3, 0xD63DC7EC, 0xC3BFC718, + 0x00803DC7, 0x0000FF80, 0xFFFDDFEE, 0xC3EFFDFF, 0x00603DDF, 0x0000FFC3, + 0xFFFDDFEC, 0xC3EFFDFF, 0x40603DDF, 0x0000FFC3, 0xFFFDDFEC, 0xC3FFFDFF, + 0x00803DCF, 0x0000FFC3, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xFFFFFFFE, 0x07FF7FFF, 0x03FF7FFF, 0x00000000, 0xFEF02596, 0x3BFF6CAE, + 0x03FF3F5F, 0x00000000, 0x03000000, 0xC2A003FF, 0xFFFFFEFF, 0xFFFE03FF, + 0xFEBF0FDF, 0x02FE3FFF, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x1FFF0000, 0x00000002, + 0x000000A0, 0x003EFFFE, 0xFFFFFFFE, 0xFFFFFFFF, 0x661FFFFF, 0xFFFFFFFE, + 0xFFFFFFFF, 0x77FFFFFF, }; static const unsigned char nmstrtPages[] = { -0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x00, -0x00, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, -0x10, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x13, -0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x15, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, -0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, -0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, -0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, -0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, -0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, -0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, -0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, -0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, -0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, -0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x17, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, -0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, -0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, -0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, -0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, -0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x18, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x00, 0x00, 0x09, 0x0A, 0x0B, + 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x13, 0x00, 0x14, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x15, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, }; static const unsigned char namePages[] = { -0x19, 0x03, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x00, -0x00, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, -0x10, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x13, -0x26, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x27, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, -0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, -0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, -0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, -0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, -0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, -0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, -0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, -0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, -0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, -0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x17, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, -0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, -0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, -0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, -0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, -0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x18, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x19, 0x03, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x00, 0x00, 0x1F, 0x20, 0x21, + 0x22, 0x23, 0x24, 0x25, 0x10, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x13, 0x26, 0x14, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x27, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, }; diff --git a/cextern/expat/lib/siphash.h b/cextern/expat/lib/siphash.h new file mode 100644 index 000000000000..04f6f74585b5 --- /dev/null +++ b/cextern/expat/lib/siphash.h @@ -0,0 +1,392 @@ +/* ========================================================================== + * siphash.h - SipHash-2-4 in a single header file + * -------------------------------------------------------------------------- + * Derived by William Ahern from the reference implementation[1] published[2] + * by Jean-Philippe Aumasson and Daniel J. Berstein. + * Minimal changes by Sebastian Pipping and Victor Stinner on top, see below. + * Licensed under the CC0 Public Domain Dedication license. + * + * 1. https://www.131002.net/siphash/siphash24.c + * 2. https://www.131002.net/siphash/ + * -------------------------------------------------------------------------- + * HISTORY: + * + * 2020-10-03 (Sebastian Pipping) + * - Drop support for Visual Studio 9.0/2008 and earlier + * + * 2019-08-03 (Sebastian Pipping) + * - Mark part of sip24_valid as to be excluded from clang-format + * - Re-format code using clang-format 9 + * + * 2018-07-08 (Anton Maklakov) + * - Add "fall through" markers for GCC's -Wimplicit-fallthrough + * + * 2017-11-03 (Sebastian Pipping) + * - Hide sip_tobin and sip_binof unless SIPHASH_TOBIN macro is defined + * + * 2017-07-25 (Vadim Zeitlin) + * - Fix use of SIPHASH_MAIN macro + * + * 2017-07-05 (Sebastian Pipping) + * - Use _SIP_ULL macro to not require a C++11 compiler if compiled as C++ + * - Add const qualifiers at two places + * - Ensure <=80 characters line length (assuming tab width 4) + * + * 2017-06-23 (Victor Stinner) + * - Address Win64 compile warnings + * + * 2017-06-18 (Sebastian Pipping) + * - Clarify license note in the header + * - Address C89 issues: + * - Stop using inline keyword (and let compiler decide) + * - Replace _Bool by int + * - Turn macro siphash24 into a function + * - Address invalid conversion (void pointer) by explicit cast + * - Address lack of stdint.h for Visual Studio 2003 to 2008 + * - Always expose sip24_valid (for self-tests) + * + * 2012-11-04 - Born. (William Ahern) + * -------------------------------------------------------------------------- + * USAGE: + * + * SipHash-2-4 takes as input two 64-bit words as the key, some number of + * message bytes, and outputs a 64-bit word as the message digest. This + * implementation employs two data structures: a struct sipkey for + * representing the key, and a struct siphash for representing the hash + * state. + * + * For converting a 16-byte unsigned char array to a key, use either the + * macro sip_keyof or the routine sip_tokey. The former instantiates a + * compound literal key, while the latter requires a key object as a + * parameter. + * + * unsigned char secret[16]; + * arc4random_buf(secret, sizeof secret); + * struct sipkey *key = sip_keyof(secret); + * + * For hashing a message, use either the convenience macro siphash24 or the + * routines sip24_init, sip24_update, and sip24_final. + * + * struct siphash state; + * void *msg; + * size_t len; + * uint64_t hash; + * + * sip24_init(&state, key); + * sip24_update(&state, msg, len); + * hash = sip24_final(&state); + * + * or + * + * hash = siphash24(msg, len, key); + * + * To convert the 64-bit hash value to a canonical 8-byte little-endian + * binary representation, use either the macro sip_binof or the routine + * sip_tobin. The former instantiates and returns a compound literal array, + * while the latter requires an array object as a parameter. + * -------------------------------------------------------------------------- + * NOTES: + * + * o Neither sip_keyof, sip_binof, nor siphash24 will work with compilers + * lacking compound literal support. Instead, you must use the lower-level + * interfaces which take as parameters the temporary state objects. + * + * o Uppercase macros may evaluate parameters more than once. Lowercase + * macros should not exhibit any such side effects. + * ========================================================================== + */ +#ifndef SIPHASH_H +#define SIPHASH_H + +#include /* size_t */ +#include /* uint64_t uint32_t uint8_t */ + +/* + * Workaround to not require a C++11 compiler for using ULL suffix + * if this code is included and compiled as C++; related GCC warning is: + * warning: use of C++11 long long integer constant [-Wlong-long] + */ +#define SIP_ULL(high, low) ((((uint64_t)high) << 32) | (low)) + +#define SIP_ROTL(x, b) (uint64_t)(((x) << (b)) | ((x) >> (64 - (b)))) + +#define SIP_U32TO8_LE(p, v) \ + (p)[0] = (uint8_t)((v) >> 0); \ + (p)[1] = (uint8_t)((v) >> 8); \ + (p)[2] = (uint8_t)((v) >> 16); \ + (p)[3] = (uint8_t)((v) >> 24); + +#define SIP_U64TO8_LE(p, v) \ + SIP_U32TO8_LE((p) + 0, (uint32_t)((v) >> 0)); \ + SIP_U32TO8_LE((p) + 4, (uint32_t)((v) >> 32)); + +#define SIP_U8TO64_LE(p) \ + (((uint64_t)((p)[0]) << 0) | ((uint64_t)((p)[1]) << 8) \ + | ((uint64_t)((p)[2]) << 16) | ((uint64_t)((p)[3]) << 24) \ + | ((uint64_t)((p)[4]) << 32) | ((uint64_t)((p)[5]) << 40) \ + | ((uint64_t)((p)[6]) << 48) | ((uint64_t)((p)[7]) << 56)) + +#define SIPHASH_INITIALIZER {0, 0, 0, 0, {0}, 0, 0} + +struct siphash { + uint64_t v0, v1, v2, v3; + + unsigned char buf[8], *p; + uint64_t c; +}; /* struct siphash */ + +#define SIP_KEYLEN 16 + +struct sipkey { + uint64_t k[2]; +}; /* struct sipkey */ + +#define sip_keyof(k) sip_tokey(&(struct sipkey){{0}}, (k)) + +static struct sipkey * +sip_tokey(struct sipkey *key, const void *src) { + key->k[0] = SIP_U8TO64_LE((const unsigned char *)src); + key->k[1] = SIP_U8TO64_LE((const unsigned char *)src + 8); + return key; +} /* sip_tokey() */ + +#ifdef SIPHASH_TOBIN + +# define sip_binof(v) sip_tobin((unsigned char[8]){0}, (v)) + +static void * +sip_tobin(void *dst, uint64_t u64) { + SIP_U64TO8_LE((unsigned char *)dst, u64); + return dst; +} /* sip_tobin() */ + +#endif /* SIPHASH_TOBIN */ + +static void +sip_round(struct siphash *H, const int rounds) { + int i; + + for (i = 0; i < rounds; i++) { + H->v0 += H->v1; + H->v1 = SIP_ROTL(H->v1, 13); + H->v1 ^= H->v0; + H->v0 = SIP_ROTL(H->v0, 32); + + H->v2 += H->v3; + H->v3 = SIP_ROTL(H->v3, 16); + H->v3 ^= H->v2; + + H->v0 += H->v3; + H->v3 = SIP_ROTL(H->v3, 21); + H->v3 ^= H->v0; + + H->v2 += H->v1; + H->v1 = SIP_ROTL(H->v1, 17); + H->v1 ^= H->v2; + H->v2 = SIP_ROTL(H->v2, 32); + } +} /* sip_round() */ + +static struct siphash * +sip24_init(struct siphash *H, const struct sipkey *key) { + H->v0 = SIP_ULL(0x736f6d65U, 0x70736575U) ^ key->k[0]; + H->v1 = SIP_ULL(0x646f7261U, 0x6e646f6dU) ^ key->k[1]; + H->v2 = SIP_ULL(0x6c796765U, 0x6e657261U) ^ key->k[0]; + H->v3 = SIP_ULL(0x74656462U, 0x79746573U) ^ key->k[1]; + + H->p = H->buf; + H->c = 0; + + return H; +} /* sip24_init() */ + +#define sip_endof(a) (&(a)[sizeof(a) / sizeof *(a)]) + +static struct siphash * +sip24_update(struct siphash *H, const void *src, size_t len) { + const unsigned char *p = (const unsigned char *)src, *pe = p + len; + uint64_t m; + + do { + while (p < pe && H->p < sip_endof(H->buf)) + *H->p++ = *p++; + + if (H->p < sip_endof(H->buf)) + break; + + m = SIP_U8TO64_LE(H->buf); + H->v3 ^= m; + sip_round(H, 2); + H->v0 ^= m; + + H->p = H->buf; + H->c += 8; + } while (p < pe); + + return H; +} /* sip24_update() */ + +static uint64_t +sip24_final(struct siphash *H) { + const char left = (char)(H->p - H->buf); + uint64_t b = (H->c + left) << 56; + + switch (left) { + case 7: + b |= (uint64_t)H->buf[6] << 48; + /* fall through */ + case 6: + b |= (uint64_t)H->buf[5] << 40; + /* fall through */ + case 5: + b |= (uint64_t)H->buf[4] << 32; + /* fall through */ + case 4: + b |= (uint64_t)H->buf[3] << 24; + /* fall through */ + case 3: + b |= (uint64_t)H->buf[2] << 16; + /* fall through */ + case 2: + b |= (uint64_t)H->buf[1] << 8; + /* fall through */ + case 1: + b |= (uint64_t)H->buf[0] << 0; + /* fall through */ + case 0: + break; + } + + H->v3 ^= b; + sip_round(H, 2); + H->v0 ^= b; + H->v2 ^= 0xff; + sip_round(H, 4); + + return H->v0 ^ H->v1 ^ H->v2 ^ H->v3; +} /* sip24_final() */ + +static uint64_t +siphash24(const void *src, size_t len, const struct sipkey *key) { + struct siphash state = SIPHASH_INITIALIZER; + return sip24_final(sip24_update(sip24_init(&state, key), src, len)); +} /* siphash24() */ + +/* + * SipHash-2-4 output with + * k = 00 01 02 ... + * and + * in = (empty string) + * in = 00 (1 byte) + * in = 00 01 (2 bytes) + * in = 00 01 02 (3 bytes) + * ... + * in = 00 01 02 ... 3e (63 bytes) + */ +static int +sip24_valid(void) { + /* clang-format off */ + static const unsigned char vectors[64][8] = { + { 0x31, 0x0e, 0x0e, 0xdd, 0x47, 0xdb, 0x6f, 0x72, }, + { 0xfd, 0x67, 0xdc, 0x93, 0xc5, 0x39, 0xf8, 0x74, }, + { 0x5a, 0x4f, 0xa9, 0xd9, 0x09, 0x80, 0x6c, 0x0d, }, + { 0x2d, 0x7e, 0xfb, 0xd7, 0x96, 0x66, 0x67, 0x85, }, + { 0xb7, 0x87, 0x71, 0x27, 0xe0, 0x94, 0x27, 0xcf, }, + { 0x8d, 0xa6, 0x99, 0xcd, 0x64, 0x55, 0x76, 0x18, }, + { 0xce, 0xe3, 0xfe, 0x58, 0x6e, 0x46, 0xc9, 0xcb, }, + { 0x37, 0xd1, 0x01, 0x8b, 0xf5, 0x00, 0x02, 0xab, }, + { 0x62, 0x24, 0x93, 0x9a, 0x79, 0xf5, 0xf5, 0x93, }, + { 0xb0, 0xe4, 0xa9, 0x0b, 0xdf, 0x82, 0x00, 0x9e, }, + { 0xf3, 0xb9, 0xdd, 0x94, 0xc5, 0xbb, 0x5d, 0x7a, }, + { 0xa7, 0xad, 0x6b, 0x22, 0x46, 0x2f, 0xb3, 0xf4, }, + { 0xfb, 0xe5, 0x0e, 0x86, 0xbc, 0x8f, 0x1e, 0x75, }, + { 0x90, 0x3d, 0x84, 0xc0, 0x27, 0x56, 0xea, 0x14, }, + { 0xee, 0xf2, 0x7a, 0x8e, 0x90, 0xca, 0x23, 0xf7, }, + { 0xe5, 0x45, 0xbe, 0x49, 0x61, 0xca, 0x29, 0xa1, }, + { 0xdb, 0x9b, 0xc2, 0x57, 0x7f, 0xcc, 0x2a, 0x3f, }, + { 0x94, 0x47, 0xbe, 0x2c, 0xf5, 0xe9, 0x9a, 0x69, }, + { 0x9c, 0xd3, 0x8d, 0x96, 0xf0, 0xb3, 0xc1, 0x4b, }, + { 0xbd, 0x61, 0x79, 0xa7, 0x1d, 0xc9, 0x6d, 0xbb, }, + { 0x98, 0xee, 0xa2, 0x1a, 0xf2, 0x5c, 0xd6, 0xbe, }, + { 0xc7, 0x67, 0x3b, 0x2e, 0xb0, 0xcb, 0xf2, 0xd0, }, + { 0x88, 0x3e, 0xa3, 0xe3, 0x95, 0x67, 0x53, 0x93, }, + { 0xc8, 0xce, 0x5c, 0xcd, 0x8c, 0x03, 0x0c, 0xa8, }, + { 0x94, 0xaf, 0x49, 0xf6, 0xc6, 0x50, 0xad, 0xb8, }, + { 0xea, 0xb8, 0x85, 0x8a, 0xde, 0x92, 0xe1, 0xbc, }, + { 0xf3, 0x15, 0xbb, 0x5b, 0xb8, 0x35, 0xd8, 0x17, }, + { 0xad, 0xcf, 0x6b, 0x07, 0x63, 0x61, 0x2e, 0x2f, }, + { 0xa5, 0xc9, 0x1d, 0xa7, 0xac, 0xaa, 0x4d, 0xde, }, + { 0x71, 0x65, 0x95, 0x87, 0x66, 0x50, 0xa2, 0xa6, }, + { 0x28, 0xef, 0x49, 0x5c, 0x53, 0xa3, 0x87, 0xad, }, + { 0x42, 0xc3, 0x41, 0xd8, 0xfa, 0x92, 0xd8, 0x32, }, + { 0xce, 0x7c, 0xf2, 0x72, 0x2f, 0x51, 0x27, 0x71, }, + { 0xe3, 0x78, 0x59, 0xf9, 0x46, 0x23, 0xf3, 0xa7, }, + { 0x38, 0x12, 0x05, 0xbb, 0x1a, 0xb0, 0xe0, 0x12, }, + { 0xae, 0x97, 0xa1, 0x0f, 0xd4, 0x34, 0xe0, 0x15, }, + { 0xb4, 0xa3, 0x15, 0x08, 0xbe, 0xff, 0x4d, 0x31, }, + { 0x81, 0x39, 0x62, 0x29, 0xf0, 0x90, 0x79, 0x02, }, + { 0x4d, 0x0c, 0xf4, 0x9e, 0xe5, 0xd4, 0xdc, 0xca, }, + { 0x5c, 0x73, 0x33, 0x6a, 0x76, 0xd8, 0xbf, 0x9a, }, + { 0xd0, 0xa7, 0x04, 0x53, 0x6b, 0xa9, 0x3e, 0x0e, }, + { 0x92, 0x59, 0x58, 0xfc, 0xd6, 0x42, 0x0c, 0xad, }, + { 0xa9, 0x15, 0xc2, 0x9b, 0xc8, 0x06, 0x73, 0x18, }, + { 0x95, 0x2b, 0x79, 0xf3, 0xbc, 0x0a, 0xa6, 0xd4, }, + { 0xf2, 0x1d, 0xf2, 0xe4, 0x1d, 0x45, 0x35, 0xf9, }, + { 0x87, 0x57, 0x75, 0x19, 0x04, 0x8f, 0x53, 0xa9, }, + { 0x10, 0xa5, 0x6c, 0xf5, 0xdf, 0xcd, 0x9a, 0xdb, }, + { 0xeb, 0x75, 0x09, 0x5c, 0xcd, 0x98, 0x6c, 0xd0, }, + { 0x51, 0xa9, 0xcb, 0x9e, 0xcb, 0xa3, 0x12, 0xe6, }, + { 0x96, 0xaf, 0xad, 0xfc, 0x2c, 0xe6, 0x66, 0xc7, }, + { 0x72, 0xfe, 0x52, 0x97, 0x5a, 0x43, 0x64, 0xee, }, + { 0x5a, 0x16, 0x45, 0xb2, 0x76, 0xd5, 0x92, 0xa1, }, + { 0xb2, 0x74, 0xcb, 0x8e, 0xbf, 0x87, 0x87, 0x0a, }, + { 0x6f, 0x9b, 0xb4, 0x20, 0x3d, 0xe7, 0xb3, 0x81, }, + { 0xea, 0xec, 0xb2, 0xa3, 0x0b, 0x22, 0xa8, 0x7f, }, + { 0x99, 0x24, 0xa4, 0x3c, 0xc1, 0x31, 0x57, 0x24, }, + { 0xbd, 0x83, 0x8d, 0x3a, 0xaf, 0xbf, 0x8d, 0xb7, }, + { 0x0b, 0x1a, 0x2a, 0x32, 0x65, 0xd5, 0x1a, 0xea, }, + { 0x13, 0x50, 0x79, 0xa3, 0x23, 0x1c, 0xe6, 0x60, }, + { 0x93, 0x2b, 0x28, 0x46, 0xe4, 0xd7, 0x06, 0x66, }, + { 0xe1, 0x91, 0x5f, 0x5c, 0xb1, 0xec, 0xa4, 0x6c, }, + { 0xf3, 0x25, 0x96, 0x5c, 0xa1, 0x6d, 0x62, 0x9f, }, + { 0x57, 0x5f, 0xf2, 0x8e, 0x60, 0x38, 0x1b, 0xe5, }, + { 0x72, 0x45, 0x06, 0xeb, 0x4c, 0x32, 0x8a, 0x95, } + }; + /* clang-format on */ + + unsigned char in[64]; + struct sipkey k; + size_t i; + + sip_tokey(&k, "\000\001\002\003\004\005\006\007\010\011" + "\012\013\014\015\016\017"); + + for (i = 0; i < sizeof in; ++i) { + in[i] = (unsigned char)i; + + if (siphash24(in, i, &k) != SIP_U8TO64_LE(vectors[i])) + return 0; + } + + return 1; +} /* sip24_valid() */ + +#ifdef SIPHASH_MAIN + +# include + +int +main(void) { + const int ok = sip24_valid(); + + if (ok) + puts("OK"); + else + puts("FAIL"); + + return ! ok; +} /* main() */ + +#endif /* SIPHASH_MAIN */ + +#endif /* SIPHASH_H */ diff --git a/cextern/expat/lib/utf8tab.h b/cextern/expat/lib/utf8tab.h old mode 100755 new mode 100644 index 7bb3e77603fa..88efcf91cc16 --- a/cextern/expat/lib/utf8tab.h +++ b/cextern/expat/lib/utf8tab.h @@ -1,37 +1,66 @@ -/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd - See the file COPYING for copying permission. -*/ +/* + __ __ _ + ___\ \/ /_ __ __ _| |_ + / _ \\ /| '_ \ / _` | __| + | __// \| |_) | (_| | |_ + \___/_/\_\ .__/ \__,_|\__| + |_| XML parser + + Copyright (c) 1997-2000 Thai Open Source Software Center Ltd + Copyright (c) 2000 Clark Cooper + Copyright (c) 2002 Fred L. Drake, Jr. + Copyright (c) 2017 Sebastian Pipping + Licensed under the MIT license: + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to permit + persons to whom the Software is furnished to do so, subject to the + following conditions: + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ /* 0x80 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, -/* 0x84 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, -/* 0x88 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, -/* 0x8C */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, -/* 0x90 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, -/* 0x94 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, -/* 0x98 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, -/* 0x9C */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, -/* 0xA0 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, -/* 0xA4 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, -/* 0xA8 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, -/* 0xAC */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, -/* 0xB0 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, -/* 0xB4 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, -/* 0xB8 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, -/* 0xBC */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, -/* 0xC0 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, -/* 0xC4 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, -/* 0xC8 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, -/* 0xCC */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, -/* 0xD0 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, -/* 0xD4 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, -/* 0xD8 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, -/* 0xDC */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, -/* 0xE0 */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3, -/* 0xE4 */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3, -/* 0xE8 */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3, -/* 0xEC */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3, -/* 0xF0 */ BT_LEAD4, BT_LEAD4, BT_LEAD4, BT_LEAD4, -/* 0xF4 */ BT_LEAD4, BT_NONXML, BT_NONXML, BT_NONXML, -/* 0xF8 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, -/* 0xFC */ BT_NONXML, BT_NONXML, BT_MALFORM, BT_MALFORM, + /* 0x84 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, + /* 0x88 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, + /* 0x8C */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, + /* 0x90 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, + /* 0x94 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, + /* 0x98 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, + /* 0x9C */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, + /* 0xA0 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, + /* 0xA4 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, + /* 0xA8 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, + /* 0xAC */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, + /* 0xB0 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, + /* 0xB4 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, + /* 0xB8 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, + /* 0xBC */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, + /* 0xC0 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, + /* 0xC4 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, + /* 0xC8 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, + /* 0xCC */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, + /* 0xD0 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, + /* 0xD4 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, + /* 0xD8 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, + /* 0xDC */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, + /* 0xE0 */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3, + /* 0xE4 */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3, + /* 0xE8 */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3, + /* 0xEC */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3, + /* 0xF0 */ BT_LEAD4, BT_LEAD4, BT_LEAD4, BT_LEAD4, + /* 0xF4 */ BT_LEAD4, BT_NONXML, BT_NONXML, BT_NONXML, + /* 0xF8 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, + /* 0xFC */ BT_NONXML, BT_NONXML, BT_MALFORM, BT_MALFORM, diff --git a/cextern/expat/lib/winconfig.h b/cextern/expat/lib/winconfig.h old mode 100755 new mode 100644 index c1b791d62d0d..05805514ec7f --- a/cextern/expat/lib/winconfig.h +++ b/cextern/expat/lib/winconfig.h @@ -1,30 +1,48 @@ -/*================================================================ -** Copyright 2000, Clark Cooper -** All rights reserved. -** -** This is free software. You are permitted to copy, distribute, or modify -** it under the terms of the MIT/X license (contained in the COPYING file -** with this distribution.) +/* + __ __ _ + ___\ \/ /_ __ __ _| |_ + / _ \\ /| '_ \ / _` | __| + | __// \| |_) | (_| | |_ + \___/_/\_\ .__/ \__,_|\__| + |_| XML parser + + Copyright (c) 2000 Clark Cooper + Copyright (c) 2002 Greg Stein + Copyright (c) 2005 Karl Waclawek + Copyright (c) 2017-2023 Sebastian Pipping + Copyright (c) 2023 Orgad Shaneh + Licensed under the MIT license: + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to permit + persons to whom the Software is furnished to do so, subject to the + following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef WINCONFIG_H #define WINCONFIG_H -#define WIN32_LEAN_AND_MEAN +#ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif #include #undef WIN32_LEAN_AND_MEAN #include #include -#define XML_NS 1 -#define XML_DTD 1 -#define XML_CONTEXT_BYTES 1024 - -/* we will assume all Windows platforms are little endian */ -#define BYTEORDER 1234 - -/* Windows has memmove() available. */ -#define HAVE_MEMMOVE - #endif /* ndef WINCONFIG_H */ diff --git a/cextern/expat/lib/xmlparse.c b/cextern/expat/lib/xmlparse.c old mode 100755 new mode 100644 index 94e31de9da3f..bc4f9ed4e5dd --- a/cextern/expat/lib/xmlparse.c +++ b/cextern/expat/lib/xmlparse.c @@ -1,86 +1,224 @@ -/* Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd - See the file COPYING for copying permission. +/* 28bcd8b1ba7eb595d82822908257fd9c3589b4243e3c922d0369f35bfcd7b506 (2.7.3+) + __ __ _ + ___\ \/ /_ __ __ _| |_ + / _ \\ /| '_ \ / _` | __| + | __// \| |_) | (_| | |_ + \___/_/\_\ .__/ \__,_|\__| + |_| XML parser + + Copyright (c) 1997-2000 Thai Open Source Software Center Ltd + Copyright (c) 2000 Clark Cooper + Copyright (c) 2000-2006 Fred L. Drake, Jr. + Copyright (c) 2001-2002 Greg Stein + Copyright (c) 2002-2016 Karl Waclawek + Copyright (c) 2005-2009 Steven Solie + Copyright (c) 2016 Eric Rahm + Copyright (c) 2016-2025 Sebastian Pipping + Copyright (c) 2016 Gaurav + Copyright (c) 2016 Thomas Beutlich + Copyright (c) 2016 Gustavo Grieco + Copyright (c) 2016 Pascal Cuoq + Copyright (c) 2016 Ed Schouten + Copyright (c) 2017-2022 Rhodri James + Copyright (c) 2017 VÃĄclav Slavík + Copyright (c) 2017 Viktor Szakats + Copyright (c) 2017 Chanho Park + Copyright (c) 2017 Rolf Eike Beer + Copyright (c) 2017 Hans Wennborg + Copyright (c) 2018 Anton Maklakov + Copyright (c) 2018 Benjamin Peterson + Copyright (c) 2018 Marco Maggi + Copyright (c) 2018 Mariusz Zaborski + Copyright (c) 2019 David Loffredo + Copyright (c) 2019-2020 Ben Wagner + Copyright (c) 2019 Vadim Zeitlin + Copyright (c) 2021 Donghee Na + Copyright (c) 2022 Samanta Navarro + Copyright (c) 2022 Jeffrey Walton + Copyright (c) 2022 Jann Horn + Copyright (c) 2022 Sean McBride + Copyright (c) 2023 Owain Davies + Copyright (c) 2023-2024 Sony Corporation / Snild Dolkow + Copyright (c) 2024-2025 Berkay Eren ÜrÃŧn + Copyright (c) 2024 Hanno BÃļck + Copyright (c) 2025 Matthew Fernandez + Licensed under the MIT license: + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to permit + persons to whom the Software is furnished to do so, subject to the + following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#define XML_BUILDING_EXPAT 1 + +#include "expat_config.h" + +#if ! defined(XML_GE) || (1 - XML_GE - 1 == 2) || (XML_GE < 0) || (XML_GE > 1) +# error XML_GE (for general entities) must be defined, non-empty, either 1 or 0 (0 to disable, 1 to enable; 1 is a common default) +#endif + +#if defined(XML_DTD) && XML_GE == 0 +# error Either undefine XML_DTD or define XML_GE to 1. +#endif + +#if ! defined(XML_CONTEXT_BYTES) || (1 - XML_CONTEXT_BYTES - 1 == 2) \ + || (XML_CONTEXT_BYTES + 0 < 0) +# error XML_CONTEXT_BYTES must be defined, non-empty and >=0 (0 to disable, >=1 to enable; 1024 is a common default) +#endif + +#if defined(HAVE_SYSCALL_GETRANDOM) +# if ! defined(_GNU_SOURCE) +# define _GNU_SOURCE 1 /* syscall prototype */ +# endif +#endif + +#ifdef _WIN32 +/* force stdlib to define rand_s() */ +# if ! defined(_CRT_RAND_S) +# define _CRT_RAND_S +# endif +#endif + +#include #include -#include /* memset(), memcpy() */ +#include /* memset(), memcpy() */ #include +#include /* INT_MAX, UINT_MAX */ +#include /* fprintf */ +#include /* getenv, rand_s */ +#include /* SIZE_MAX, uintptr_t */ +#include /* isnan */ + +#ifdef _WIN32 +# define getpid GetCurrentProcessId +#else +# include /* gettimeofday() */ +# include /* getpid() */ +# include /* getpid() */ +# include /* O_RDONLY */ +# include +#endif -#define XML_BUILDING_EXPAT 1 - -#ifdef COMPILED_FROM_DSP -#include "winconfig.h" -#elif defined(MACOS_CLASSIC) -#include "macconfig.h" -#elif defined(__amigaos4__) -#include "amigaconfig.h" -#elif defined(__WATCOMC__) -#include "watcomconfig.h" -#elif defined(HAVE_EXPAT_CONFIG_H) -#include -#endif /* ndef COMPILED_FROM_DSP */ +#ifdef _WIN32 +# include "winconfig.h" +#endif #include "ascii.h" #include "expat.h" +#include "siphash.h" + +#if defined(HAVE_GETRANDOM) || defined(HAVE_SYSCALL_GETRANDOM) +# if defined(HAVE_GETRANDOM) +# include /* getrandom */ +# else +# include /* syscall */ +# include /* SYS_getrandom */ +# endif +# if ! defined(GRND_NONBLOCK) +# define GRND_NONBLOCK 0x0001 +# endif /* defined(GRND_NONBLOCK) */ +#endif /* defined(HAVE_GETRANDOM) || defined(HAVE_SYSCALL_GETRANDOM) */ + +#if defined(HAVE_LIBBSD) \ + && (defined(HAVE_ARC4RANDOM_BUF) || defined(HAVE_ARC4RANDOM)) +# include +#endif + +#if defined(_WIN32) && ! defined(LOAD_LIBRARY_SEARCH_SYSTEM32) +# define LOAD_LIBRARY_SEARCH_SYSTEM32 0x00000800 +#endif + +#if ! defined(HAVE_GETRANDOM) && ! defined(HAVE_SYSCALL_GETRANDOM) \ + && ! defined(HAVE_ARC4RANDOM_BUF) && ! defined(HAVE_ARC4RANDOM) \ + && ! defined(XML_DEV_URANDOM) && ! defined(_WIN32) \ + && ! defined(XML_POOR_ENTROPY) +# error You do not have support for any sources of high quality entropy \ + enabled. For end user security, that is probably not what you want. \ + \ + Your options include: \ + * Linux >=3.17 + glibc >=2.25 (getrandom): HAVE_GETRANDOM, \ + * Linux >=3.17 + glibc (including <2.25) (syscall SYS_getrandom): HAVE_SYSCALL_GETRANDOM, \ + * BSD / macOS >=10.7 / glibc >=2.36 (arc4random_buf): HAVE_ARC4RANDOM_BUF, \ + * BSD / macOS (including <10.7) / glibc >=2.36 (arc4random): HAVE_ARC4RANDOM, \ + * libbsd (arc4random_buf): HAVE_ARC4RANDOM_BUF + HAVE_LIBBSD, \ + * libbsd (arc4random): HAVE_ARC4RANDOM + HAVE_LIBBSD, \ + * Linux (including <3.17) / BSD / macOS (including <10.7) / Solaris >=8 (/dev/urandom): XML_DEV_URANDOM, \ + * Windows >=Vista (rand_s): _WIN32. \ + \ + If insist on not using any of these, bypass this error by defining \ + XML_POOR_ENTROPY; you have been warned. \ + \ + If you have reasons to patch this detection code away or need changes \ + to the build system, please open a bug. Thank you! +#endif #ifdef XML_UNICODE -#define XML_ENCODE_MAX XML_UTF16_ENCODE_MAX -#define XmlConvert XmlUtf16Convert -#define XmlGetInternalEncoding XmlGetUtf16InternalEncoding -#define XmlGetInternalEncodingNS XmlGetUtf16InternalEncodingNS -#define XmlEncode XmlUtf16Encode -/* Using pointer subtraction to convert to integer type. */ -#define MUST_CONVERT(enc, s) (!(enc)->isUtf16 || (((char *)(s) - (char *)NULL) & 1)) +# define XML_ENCODE_MAX XML_UTF16_ENCODE_MAX +# define XmlConvert XmlUtf16Convert +# define XmlGetInternalEncoding XmlGetUtf16InternalEncoding +# define XmlGetInternalEncodingNS XmlGetUtf16InternalEncodingNS +# define XmlEncode XmlUtf16Encode +# define MUST_CONVERT(enc, s) (! (enc)->isUtf16 || (((uintptr_t)(s)) & 1)) typedef unsigned short ICHAR; #else -#define XML_ENCODE_MAX XML_UTF8_ENCODE_MAX -#define XmlConvert XmlUtf8Convert -#define XmlGetInternalEncoding XmlGetUtf8InternalEncoding -#define XmlGetInternalEncodingNS XmlGetUtf8InternalEncodingNS -#define XmlEncode XmlUtf8Encode -#define MUST_CONVERT(enc, s) (!(enc)->isUtf8) +# define XML_ENCODE_MAX XML_UTF8_ENCODE_MAX +# define XmlConvert XmlUtf8Convert +# define XmlGetInternalEncoding XmlGetUtf8InternalEncoding +# define XmlGetInternalEncodingNS XmlGetUtf8InternalEncodingNS +# define XmlEncode XmlUtf8Encode +# define MUST_CONVERT(enc, s) (! (enc)->isUtf8) typedef char ICHAR; #endif - #ifndef XML_NS -#define XmlInitEncodingNS XmlInitEncoding -#define XmlInitUnknownEncodingNS XmlInitUnknownEncoding -#undef XmlGetInternalEncodingNS -#define XmlGetInternalEncodingNS XmlGetInternalEncoding -#define XmlParseXmlDeclNS XmlParseXmlDecl +# define XmlInitEncodingNS XmlInitEncoding +# define XmlInitUnknownEncodingNS XmlInitUnknownEncoding +# undef XmlGetInternalEncodingNS +# define XmlGetInternalEncodingNS XmlGetInternalEncoding +# define XmlParseXmlDeclNS XmlParseXmlDecl #endif #ifdef XML_UNICODE -#ifdef XML_UNICODE_WCHAR_T -#define XML_T(x) (const wchar_t)x -#define XML_L(x) L ## x -#else -#define XML_T(x) (const unsigned short)x -#define XML_L(x) x -#endif +# ifdef XML_UNICODE_WCHAR_T +# define XML_T(x) (const wchar_t) x +# define XML_L(x) L##x +# else +# define XML_T(x) (const unsigned short)x +# define XML_L(x) x +# endif #else -#define XML_T(x) x -#define XML_L(x) x +# define XML_T(x) x +# define XML_L(x) x #endif /* Round up n to be a multiple of sz, where sz is a power of 2. */ #define ROUND_UP(n, sz) (((n) + ((sz) - 1)) & ~((sz) - 1)) -/* Handle the case where memmove() doesn't exist. */ -#ifndef HAVE_MEMMOVE -#ifdef HAVE_BCOPY -#define memmove(d,s,l) bcopy((s),(d),(l)) -#else -#error memmove does not exist on this platform, nor is a substitute available -#endif /* HAVE_BCOPY */ -#endif /* HAVE_MEMMOVE */ +/* Do safe (NULL-aware) pointer arithmetic */ +#define EXPAT_SAFE_PTR_DIFF(p, q) (((p) && (q)) ? ((p) - (q)) : 0) + +#define EXPAT_MIN(a, b) (((a) < (b)) ? (a) : (b)) #include "internal.h" #include "xmltok.h" @@ -97,20 +235,12 @@ typedef struct { unsigned char power; size_t size; size_t used; - const XML_Memory_Handling_Suite *mem; + XML_Parser parser; } HASH_TABLE; -/* Basic character hash algorithm, taken from Python's string hash: - h = h * 1000003 ^ character, the constant being a prime number. +static size_t keylen(KEY s); -*/ -#ifdef XML_UNICODE -#define CHAR_HASH(h, c) \ - (((h) * 0xF4243) ^ (unsigned short)(c)) -#else -#define CHAR_HASH(h, c) \ - (((h) * 0xF4243) ^ (unsigned char)(c)) -#endif +static void copy_salt_to_sipkey(XML_Parser parser, struct sipkey *key); /* For probing (after a collision) we need a step size relative prime to the hash table size, which is a power of 2. We use double-hashing, @@ -120,9 +250,9 @@ typedef struct { We limit the maximum step size to table->size / 4 (mask >> 2) and make it odd, since odd numbers are always relative prime to a power of 2. */ -#define SECOND_HASH(hash, mask, power) \ +#define SECOND_HASH(hash, mask, power) \ ((((hash) & ~(mask)) >> ((power) - 1)) & ((mask) >> 2)) -#define PROBE_STEP(hash, mask, power) \ +#define PROBE_STEP(hash, mask, power) \ ((unsigned char)((SECOND_HASH(hash, mask, power)) | 1)) typedef struct { @@ -130,7 +260,7 @@ typedef struct { NAMED **end; } HASH_TABLE_ITER; -#define INIT_TAG_BUF_SIZE 32 /* must be a multiple of sizeof(XML_Char) */ +#define INIT_TAG_BUF_SIZE 32 /* must be a multiple of sizeof(XML_Char) */ #define INIT_DATA_BUF_SIZE 1024 #define INIT_ATTS_SIZE 16 #define INIT_ATTS_VERSION 0xFFFFFFFF @@ -167,47 +297,51 @@ typedef struct { The name of the element is stored in both the document and API encodings. The memory buffer 'buf' is a separately-allocated memory area which stores the name. During the XML_Parse()/ - XMLParseBuffer() when the element is open, the memory for the 'raw' + XML_ParseBuffer() when the element is open, the memory for the 'raw' version of the name (in the document encoding) is shared with the document buffer. If the element is open across calls to XML_Parse()/XML_ParseBuffer(), the buffer is re-allocated to contain the 'raw' name as well. - A parser re-uses these structures, maintaining a list of allocated + A parser reuses these structures, maintaining a list of allocated TAG objects in a free list. */ typedef struct tag { - struct tag *parent; /* parent of this element */ - const char *rawName; /* tagName in the original encoding */ + struct tag *parent; /* parent of this element */ + const char *rawName; /* tagName in the original encoding */ int rawNameLength; - TAG_NAME name; /* tagName in the API encoding */ - char *buf; /* buffer for name components */ - char *bufEnd; /* end of the buffer */ + TAG_NAME name; /* tagName in the API encoding */ + char *buf; /* buffer for name components */ + char *bufEnd; /* end of the buffer */ BINDING *bindings; } TAG; typedef struct { const XML_Char *name; const XML_Char *textPtr; - int textLen; /* length in XML_Chars */ - int processed; /* # of processed bytes - when suspended */ + int textLen; /* length in XML_Chars */ + int processed; /* # of processed bytes - when suspended */ const XML_Char *systemId; const XML_Char *base; const XML_Char *publicId; const XML_Char *notation; XML_Bool open; + XML_Bool hasMore; /* true if entity has not been completely processed */ + /* An entity can be open while being already completely processed (hasMore == + XML_FALSE). The reason is the delayed closing of entities until their inner + entities are processed and closed */ XML_Bool is_param; XML_Bool is_internal; /* true if declared in internal subset outside PE */ } ENTITY; typedef struct { - enum XML_Content_Type type; - enum XML_Content_Quant quant; - const XML_Char * name; - int firstchild; - int lastchild; - int childcnt; - int nextsib; + enum XML_Content_Type type; + enum XML_Content_Quant quant; + const XML_Char *name; + int firstchild; + int lastchild; + int childcnt; + int nextsib; } CONTENT_SCAFFOLD; #define INIT_SCAFFOLD_ELEMENTS 32 @@ -224,7 +358,7 @@ typedef struct { const XML_Char *end; XML_Char *ptr; XML_Char *start; - const XML_Memory_Handling_Suite *mem; + XML_Parser parser; } STRING_POOL; /* The XML_Char before the name is used to determine whether @@ -286,6 +420,12 @@ typedef struct { int *scaffIndex; } DTD; +enum EntityType { + ENTITY_INTERNAL, + ENTITY_ATTRIBUTE, + ENTITY_VALUE, +}; + typedef struct open_internal_entity { const char *internalEventPtr; const char *internalEventEndPtr; @@ -293,12 +433,44 @@ typedef struct open_internal_entity { ENTITY *entity; int startTagLevel; XML_Bool betweenDecl; /* WFC: PE Between Declarations */ + enum EntityType type; } OPEN_INTERNAL_ENTITY; -typedef enum XML_Error PTRCALL Processor(XML_Parser parser, - const char *start, - const char *end, - const char **endPtr); +enum XML_Account { + XML_ACCOUNT_DIRECT, /* bytes directly passed to the Expat parser */ + XML_ACCOUNT_ENTITY_EXPANSION, /* intermediate bytes produced during entity + expansion */ + XML_ACCOUNT_NONE /* i.e. do not account, was accounted already */ +}; + +#if XML_GE == 1 +typedef unsigned long long XmlBigCount; +typedef struct accounting { + XmlBigCount countBytesDirect; + XmlBigCount countBytesIndirect; + unsigned long debugLevel; + float maximumAmplificationFactor; // >=1.0 + unsigned long long activationThresholdBytes; +} ACCOUNTING; + +typedef struct MALLOC_TRACKER { + XmlBigCount bytesAllocated; + XmlBigCount peakBytesAllocated; // updated live only for debug level >=2 + unsigned long debugLevel; + float maximumAmplificationFactor; // >=1.0 + XmlBigCount activationThresholdBytes; +} MALLOC_TRACKER; + +typedef struct entity_stats { + unsigned int countEverOpened; + unsigned int currentDepth; + unsigned int maximumDepthSeen; + unsigned long debugLevel; +} ENTITY_STATS; +#endif /* XML_GE == 1 */ + +typedef enum XML_Error PTRCALL Processor(XML_Parser parser, const char *start, + const char *end, const char **endPtr); static Processor prologProcessor; static Processor prologInitProcessor; @@ -319,151 +491,202 @@ static Processor externalEntityInitProcessor3; static Processor externalEntityContentProcessor; static Processor internalEntityProcessor; -static enum XML_Error -handleUnknownEncoding(XML_Parser parser, const XML_Char *encodingName); -static enum XML_Error -processXmlDecl(XML_Parser parser, int isGeneralTextEntity, - const char *s, const char *next); -static enum XML_Error -initializeEncoding(XML_Parser parser); -static enum XML_Error -doProlog(XML_Parser parser, const ENCODING *enc, const char *s, - const char *end, int tok, const char *next, const char **nextPtr, - XML_Bool haveMore); -static enum XML_Error -processInternalEntity(XML_Parser parser, ENTITY *entity, - XML_Bool betweenDecl); -static enum XML_Error -doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc, - const char *start, const char *end, const char **endPtr, - XML_Bool haveMore); -static enum XML_Error -doCdataSection(XML_Parser parser, const ENCODING *, const char **startPtr, - const char *end, const char **nextPtr, XML_Bool haveMore); +static enum XML_Error handleUnknownEncoding(XML_Parser parser, + const XML_Char *encodingName); +static enum XML_Error processXmlDecl(XML_Parser parser, int isGeneralTextEntity, + const char *s, const char *next); +static enum XML_Error initializeEncoding(XML_Parser parser); +static enum XML_Error doProlog(XML_Parser parser, const ENCODING *enc, + const char *s, const char *end, int tok, + const char *next, const char **nextPtr, + XML_Bool haveMore, XML_Bool allowClosingDoctype, + enum XML_Account account); +static enum XML_Error processEntity(XML_Parser parser, ENTITY *entity, + XML_Bool betweenDecl, enum EntityType type); +static enum XML_Error doContent(XML_Parser parser, int startTagLevel, + const ENCODING *enc, const char *start, + const char *end, const char **endPtr, + XML_Bool haveMore, enum XML_Account account); +static enum XML_Error doCdataSection(XML_Parser parser, const ENCODING *enc, + const char **startPtr, const char *end, + const char **nextPtr, XML_Bool haveMore, + enum XML_Account account); #ifdef XML_DTD -static enum XML_Error -doIgnoreSection(XML_Parser parser, const ENCODING *, const char **startPtr, - const char *end, const char **nextPtr, XML_Bool haveMore); +static enum XML_Error doIgnoreSection(XML_Parser parser, const ENCODING *enc, + const char **startPtr, const char *end, + const char **nextPtr, XML_Bool haveMore); #endif /* XML_DTD */ +static void freeBindings(XML_Parser parser, BINDING *bindings); +static enum XML_Error storeAtts(XML_Parser parser, const ENCODING *enc, + const char *attStr, TAG_NAME *tagNamePtr, + BINDING **bindingsPtr, + enum XML_Account account); +static enum XML_Error addBinding(XML_Parser parser, PREFIX *prefix, + const ATTRIBUTE_ID *attId, const XML_Char *uri, + BINDING **bindingsPtr); +static int defineAttribute(ELEMENT_TYPE *type, ATTRIBUTE_ID *attId, + XML_Bool isCdata, XML_Bool isId, + const XML_Char *value, XML_Parser parser); +static enum XML_Error storeAttributeValue(XML_Parser parser, + const ENCODING *enc, XML_Bool isCdata, + const char *ptr, const char *end, + STRING_POOL *pool, + enum XML_Account account); static enum XML_Error -storeAtts(XML_Parser parser, const ENCODING *, const char *s, - TAG_NAME *tagNamePtr, BINDING **bindingsPtr); -static enum XML_Error -addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId, - const XML_Char *uri, BINDING **bindingsPtr); -static int -defineAttribute(ELEMENT_TYPE *type, ATTRIBUTE_ID *, XML_Bool isCdata, - XML_Bool isId, const XML_Char *dfltValue, XML_Parser parser); -static enum XML_Error -storeAttributeValue(XML_Parser parser, const ENCODING *, XML_Bool isCdata, - const char *, const char *, STRING_POOL *); -static enum XML_Error -appendAttributeValue(XML_Parser parser, const ENCODING *, XML_Bool isCdata, - const char *, const char *, STRING_POOL *); -static ATTRIBUTE_ID * -getAttributeId(XML_Parser parser, const ENCODING *enc, const char *start, - const char *end); -static int -setElementTypePrefix(XML_Parser parser, ELEMENT_TYPE *); -static enum XML_Error -storeEntityValue(XML_Parser parser, const ENCODING *enc, const char *start, - const char *end); -static int -reportProcessingInstruction(XML_Parser parser, const ENCODING *enc, - const char *start, const char *end); -static int -reportComment(XML_Parser parser, const ENCODING *enc, const char *start, - const char *end); -static void -reportDefault(XML_Parser parser, const ENCODING *enc, const char *start, - const char *end); +appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata, + const char *ptr, const char *end, STRING_POOL *pool, + enum XML_Account account, const char **nextPtr); +static ATTRIBUTE_ID *getAttributeId(XML_Parser parser, const ENCODING *enc, + const char *start, const char *end); +static int setElementTypePrefix(XML_Parser parser, ELEMENT_TYPE *elementType); +#if XML_GE == 1 +static enum XML_Error storeEntityValue(XML_Parser parser, const ENCODING *enc, + const char *start, const char *end, + enum XML_Account account, + const char **nextPtr); +static enum XML_Error callStoreEntityValue(XML_Parser parser, + const ENCODING *enc, + const char *start, const char *end, + enum XML_Account account); +#else +static enum XML_Error storeSelfEntityValue(XML_Parser parser, ENTITY *entity); +#endif +static int reportProcessingInstruction(XML_Parser parser, const ENCODING *enc, + const char *start, const char *end); +static int reportComment(XML_Parser parser, const ENCODING *enc, + const char *start, const char *end); +static void reportDefault(XML_Parser parser, const ENCODING *enc, + const char *start, const char *end); -static const XML_Char * getContext(XML_Parser parser); -static XML_Bool -setContext(XML_Parser parser, const XML_Char *context); +static const XML_Char *getContext(XML_Parser parser); +static XML_Bool setContext(XML_Parser parser, const XML_Char *context); static void FASTCALL normalizePublicId(XML_Char *s); -static DTD * dtdCreate(const XML_Memory_Handling_Suite *ms); -/* do not call if parentParser != NULL */ -static void dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms); -static void -dtdDestroy(DTD *p, XML_Bool isDocEntity, const XML_Memory_Handling_Suite *ms); -static int -dtdCopy(DTD *newDtd, const DTD *oldDtd, const XML_Memory_Handling_Suite *ms); -static int -copyEntityTable(HASH_TABLE *, STRING_POOL *, const HASH_TABLE *); - -static NAMED * -lookup(HASH_TABLE *table, KEY name, size_t createSize); -static void FASTCALL -hashTableInit(HASH_TABLE *, const XML_Memory_Handling_Suite *ms); -static void FASTCALL hashTableClear(HASH_TABLE *); -static void FASTCALL hashTableDestroy(HASH_TABLE *); -static void FASTCALL -hashTableIterInit(HASH_TABLE_ITER *, const HASH_TABLE *); -static NAMED * FASTCALL hashTableIterNext(HASH_TABLE_ITER *); - -static void FASTCALL -poolInit(STRING_POOL *, const XML_Memory_Handling_Suite *ms); -static void FASTCALL poolClear(STRING_POOL *); -static void FASTCALL poolDestroy(STRING_POOL *); -static XML_Char * -poolAppend(STRING_POOL *pool, const ENCODING *enc, - const char *ptr, const char *end); -static XML_Char * -poolStoreString(STRING_POOL *pool, const ENCODING *enc, - const char *ptr, const char *end); +static DTD *dtdCreate(XML_Parser parser); +/* do not call if m_parentParser != NULL */ +static void dtdReset(DTD *p, XML_Parser parser); +static void dtdDestroy(DTD *p, XML_Bool isDocEntity, XML_Parser parser); +static int dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd, + XML_Parser parser); +static int copyEntityTable(XML_Parser oldParser, HASH_TABLE *newTable, + STRING_POOL *newPool, const HASH_TABLE *oldTable); +static NAMED *lookup(XML_Parser parser, HASH_TABLE *table, KEY name, + size_t createSize); +static void FASTCALL hashTableInit(HASH_TABLE *table, XML_Parser parser); +static void FASTCALL hashTableClear(HASH_TABLE *table); +static void FASTCALL hashTableDestroy(HASH_TABLE *table); +static void FASTCALL hashTableIterInit(HASH_TABLE_ITER *iter, + const HASH_TABLE *table); +static NAMED *FASTCALL hashTableIterNext(HASH_TABLE_ITER *iter); + +static void FASTCALL poolInit(STRING_POOL *pool, XML_Parser parser); +static void FASTCALL poolClear(STRING_POOL *pool); +static void FASTCALL poolDestroy(STRING_POOL *pool); +static XML_Char *poolAppend(STRING_POOL *pool, const ENCODING *enc, + const char *ptr, const char *end); +static XML_Char *poolStoreString(STRING_POOL *pool, const ENCODING *enc, + const char *ptr, const char *end); static XML_Bool FASTCALL poolGrow(STRING_POOL *pool); -static const XML_Char * FASTCALL -poolCopyString(STRING_POOL *pool, const XML_Char *s); -static const XML_Char * -poolCopyStringN(STRING_POOL *pool, const XML_Char *s, int n); -static const XML_Char * FASTCALL -poolAppendString(STRING_POOL *pool, const XML_Char *s); +static const XML_Char *FASTCALL poolCopyString(STRING_POOL *pool, + const XML_Char *s); +static const XML_Char *poolCopyStringN(STRING_POOL *pool, const XML_Char *s, + int n); +static const XML_Char *FASTCALL poolAppendString(STRING_POOL *pool, + const XML_Char *s); static int FASTCALL nextScaffoldPart(XML_Parser parser); -static XML_Content * build_model(XML_Parser parser); -static ELEMENT_TYPE * -getElementType(XML_Parser parser, const ENCODING *enc, - const char *ptr, const char *end); - -static XML_Parser -parserCreate(const XML_Char *encodingName, - const XML_Memory_Handling_Suite *memsuite, - const XML_Char *nameSep, - DTD *dtd); -static void -parserInit(XML_Parser parser, const XML_Char *encodingName); +static XML_Content *build_model(XML_Parser parser); +static ELEMENT_TYPE *getElementType(XML_Parser parser, const ENCODING *enc, + const char *ptr, const char *end); + +static XML_Char *copyString(const XML_Char *s, XML_Parser parser); + +static unsigned long generate_hash_secret_salt(XML_Parser parser); +static XML_Bool startParsing(XML_Parser parser); + +static XML_Parser parserCreate(const XML_Char *encodingName, + const XML_Memory_Handling_Suite *memsuite, + const XML_Char *nameSep, DTD *dtd, + XML_Parser parentParser); + +static void parserInit(XML_Parser parser, const XML_Char *encodingName); + +#if XML_GE == 1 +static float accountingGetCurrentAmplification(XML_Parser rootParser); +static void accountingReportStats(XML_Parser originParser, const char *epilog); +static void accountingOnAbort(XML_Parser originParser); +static void accountingReportDiff(XML_Parser rootParser, + unsigned int levelsAwayFromRootParser, + const char *before, const char *after, + ptrdiff_t bytesMore, int source_line, + enum XML_Account account); +static XML_Bool accountingDiffTolerated(XML_Parser originParser, int tok, + const char *before, const char *after, + int source_line, + enum XML_Account account); + +static void entityTrackingReportStats(XML_Parser parser, ENTITY *entity, + const char *action, int sourceLine); +static void entityTrackingOnOpen(XML_Parser parser, ENTITY *entity, + int sourceLine); +static void entityTrackingOnClose(XML_Parser parser, ENTITY *entity, + int sourceLine); +#endif /* XML_GE == 1 */ + +static XML_Parser getRootParserOf(XML_Parser parser, + unsigned int *outLevelDiff); + +static unsigned long getDebugLevel(const char *variableName, + unsigned long defaultDebugLevel); #define poolStart(pool) ((pool)->start) -#define poolEnd(pool) ((pool)->ptr) #define poolLength(pool) ((pool)->ptr - (pool)->start) #define poolChop(pool) ((void)--(pool->ptr)) #define poolLastChar(pool) (((pool)->ptr)[-1]) #define poolDiscard(pool) ((pool)->ptr = (pool)->start) #define poolFinish(pool) ((pool)->start = (pool)->ptr) -#define poolAppendChar(pool, c) \ - (((pool)->ptr == (pool)->end && !poolGrow(pool)) \ - ? 0 \ - : ((*((pool)->ptr)++ = c), 1)) +#define poolAppendChar(pool, c) \ + (((pool)->ptr == (pool)->end && ! poolGrow(pool)) \ + ? 0 \ + : ((*((pool)->ptr)++ = c), 1)) + +#if ! defined(XML_TESTING) +const +#endif + XML_Bool g_reparseDeferralEnabledDefault + = XML_TRUE; // write ONLY in runtests.c +#if defined(XML_TESTING) +unsigned int g_bytesScanned = 0; // used for testing only +#endif struct XML_ParserStruct { - /* The first member must be userData so that the XML_GetUserData + /* The first member must be m_userData so that the XML_GetUserData macro works. */ void *m_userData; void *m_handlerArg; - char *m_buffer; + + // How the four parse buffer pointers below relate in time and space: + // + // m_buffer <= m_bufferPtr <= m_bufferEnd <= m_bufferLim + // | | | | + // <--parsed-->| | | + // <---parsing--->| | + // <--unoccupied-->| + // <---------total-malloced/realloced-------->| + + char *m_buffer; // malloc/realloc base pointer of parse buffer const XML_Memory_Handling_Suite m_mem; - /* first character to be parsed */ - const char *m_bufferPtr; - /* past last character to be parsed */ - char *m_bufferEnd; - /* allocated end of buffer */ - const char *m_bufferLim; + const char *m_bufferPtr; // first character to be parsed + char *m_bufferEnd; // past last character to be parsed + const char *m_bufferLim; // allocated end of m_buffer + XML_Index m_parseEndByteIndex; const char *m_parseEndPtr; + size_t m_partialTokenBytesBefore; /* used in heuristic to avoid O(n^2) */ + XML_Bool m_reparseDeferralEnabled; + int m_lastBufferRequestSize; XML_Char *m_dataBuf; XML_Char *m_dataBufEnd; XML_StartElementHandler m_startElementHandler; @@ -498,7 +721,7 @@ struct XML_ParserStruct { void *m_unknownEncodingMem; void *m_unknownEncodingData; void *m_unknownEncodingHandlerData; - void (XMLCALL *m_unknownEncodingRelease)(void *); + void(XMLCALL *m_unknownEncodingRelease)(void *); PROLOG_STATE m_prologState; Processor *m_processor; enum XML_Error m_errorCode; @@ -507,6 +730,10 @@ struct XML_ParserStruct { const char *m_positionPtr; OPEN_INTERNAL_ENTITY *m_openInternalEntities; OPEN_INTERNAL_ENTITY *m_freeInternalEntities; + OPEN_INTERNAL_ENTITY *m_openAttributeEntities; + OPEN_INTERNAL_ENTITY *m_freeAttributeEntities; + OPEN_INTERNAL_ENTITY *m_openValueEntities; + OPEN_INTERNAL_ENTITY *m_freeValueEntities; XML_Bool m_defaultExpandInternalEntities; int m_tagLevel; ENTITY *m_declEntity; @@ -533,6 +760,9 @@ struct XML_ParserStruct { NS_ATT *m_nsAtts; unsigned long m_nsAttsVersion; unsigned char m_nsAttsPower; +#ifdef XML_ATTR_INFO + XML_AttrInfo *m_attInfo; +#endif POSITION m_position; STRING_POOL m_tempPool; STRING_POOL m_temp2Pool; @@ -546,445 +776,1054 @@ struct XML_ParserStruct { XML_Bool m_useForeignDTD; enum XML_ParamEntityParsing m_paramEntityParsing; #endif + unsigned long m_hash_secret_salt; +#if XML_GE == 1 + ACCOUNTING m_accounting; + MALLOC_TRACKER m_alloc_tracker; + ENTITY_STATS m_entity_stats; +#endif + XML_Bool m_reenter; }; -#define MALLOC(s) (parser->m_mem.malloc_fcn((s))) -#define REALLOC(p,s) (parser->m_mem.realloc_fcn((p),(s))) -#define FREE(p) (parser->m_mem.free_fcn((p))) - -#define userData (parser->m_userData) -#define handlerArg (parser->m_handlerArg) -#define startElementHandler (parser->m_startElementHandler) -#define endElementHandler (parser->m_endElementHandler) -#define characterDataHandler (parser->m_characterDataHandler) -#define processingInstructionHandler \ - (parser->m_processingInstructionHandler) -#define commentHandler (parser->m_commentHandler) -#define startCdataSectionHandler \ - (parser->m_startCdataSectionHandler) -#define endCdataSectionHandler (parser->m_endCdataSectionHandler) -#define defaultHandler (parser->m_defaultHandler) -#define startDoctypeDeclHandler (parser->m_startDoctypeDeclHandler) -#define endDoctypeDeclHandler (parser->m_endDoctypeDeclHandler) -#define unparsedEntityDeclHandler \ - (parser->m_unparsedEntityDeclHandler) -#define notationDeclHandler (parser->m_notationDeclHandler) -#define startNamespaceDeclHandler \ - (parser->m_startNamespaceDeclHandler) -#define endNamespaceDeclHandler (parser->m_endNamespaceDeclHandler) -#define notStandaloneHandler (parser->m_notStandaloneHandler) -#define externalEntityRefHandler \ - (parser->m_externalEntityRefHandler) -#define externalEntityRefHandlerArg \ - (parser->m_externalEntityRefHandlerArg) -#define internalEntityRefHandler \ - (parser->m_internalEntityRefHandler) -#define skippedEntityHandler (parser->m_skippedEntityHandler) -#define unknownEncodingHandler (parser->m_unknownEncodingHandler) -#define elementDeclHandler (parser->m_elementDeclHandler) -#define attlistDeclHandler (parser->m_attlistDeclHandler) -#define entityDeclHandler (parser->m_entityDeclHandler) -#define xmlDeclHandler (parser->m_xmlDeclHandler) -#define encoding (parser->m_encoding) -#define initEncoding (parser->m_initEncoding) -#define internalEncoding (parser->m_internalEncoding) -#define unknownEncodingMem (parser->m_unknownEncodingMem) -#define unknownEncodingData (parser->m_unknownEncodingData) -#define unknownEncodingHandlerData \ - (parser->m_unknownEncodingHandlerData) -#define unknownEncodingRelease (parser->m_unknownEncodingRelease) -#define protocolEncodingName (parser->m_protocolEncodingName) -#define ns (parser->m_ns) -#define ns_triplets (parser->m_ns_triplets) -#define prologState (parser->m_prologState) -#define processor (parser->m_processor) -#define errorCode (parser->m_errorCode) -#define eventPtr (parser->m_eventPtr) -#define eventEndPtr (parser->m_eventEndPtr) -#define positionPtr (parser->m_positionPtr) -#define position (parser->m_position) -#define openInternalEntities (parser->m_openInternalEntities) -#define freeInternalEntities (parser->m_freeInternalEntities) -#define defaultExpandInternalEntities \ - (parser->m_defaultExpandInternalEntities) -#define tagLevel (parser->m_tagLevel) -#define buffer (parser->m_buffer) -#define bufferPtr (parser->m_bufferPtr) -#define bufferEnd (parser->m_bufferEnd) -#define parseEndByteIndex (parser->m_parseEndByteIndex) -#define parseEndPtr (parser->m_parseEndPtr) -#define bufferLim (parser->m_bufferLim) -#define dataBuf (parser->m_dataBuf) -#define dataBufEnd (parser->m_dataBufEnd) -#define _dtd (parser->m_dtd) -#define curBase (parser->m_curBase) -#define declEntity (parser->m_declEntity) -#define doctypeName (parser->m_doctypeName) -#define doctypeSysid (parser->m_doctypeSysid) -#define doctypePubid (parser->m_doctypePubid) -#define declAttributeType (parser->m_declAttributeType) -#define declNotationName (parser->m_declNotationName) -#define declNotationPublicId (parser->m_declNotationPublicId) -#define declElementType (parser->m_declElementType) -#define declAttributeId (parser->m_declAttributeId) -#define declAttributeIsCdata (parser->m_declAttributeIsCdata) -#define declAttributeIsId (parser->m_declAttributeIsId) -#define freeTagList (parser->m_freeTagList) -#define freeBindingList (parser->m_freeBindingList) -#define inheritedBindings (parser->m_inheritedBindings) -#define tagStack (parser->m_tagStack) -#define atts (parser->m_atts) -#define attsSize (parser->m_attsSize) -#define nSpecifiedAtts (parser->m_nSpecifiedAtts) -#define idAttIndex (parser->m_idAttIndex) -#define nsAtts (parser->m_nsAtts) -#define nsAttsVersion (parser->m_nsAttsVersion) -#define nsAttsPower (parser->m_nsAttsPower) -#define tempPool (parser->m_tempPool) -#define temp2Pool (parser->m_temp2Pool) -#define groupConnector (parser->m_groupConnector) -#define groupSize (parser->m_groupSize) -#define namespaceSeparator (parser->m_namespaceSeparator) -#define parentParser (parser->m_parentParser) -#define ps_parsing (parser->m_parsingStatus.parsing) -#define ps_finalBuffer (parser->m_parsingStatus.finalBuffer) -#ifdef XML_DTD -#define isParamEntity (parser->m_isParamEntity) -#define useForeignDTD (parser->m_useForeignDTD) -#define paramEntityParsing (parser->m_paramEntityParsing) -#endif /* XML_DTD */ +#if XML_GE == 1 +# define MALLOC(parser, s) (expat_malloc((parser), (s), __LINE__)) +# define REALLOC(parser, p, s) (expat_realloc((parser), (p), (s), __LINE__)) +# define FREE(parser, p) (expat_free((parser), (p), __LINE__)) +#else +# define MALLOC(parser, s) (parser->m_mem.malloc_fcn((s))) +# define REALLOC(parser, p, s) (parser->m_mem.realloc_fcn((p), (s))) +# define FREE(parser, p) (parser->m_mem.free_fcn((p))) +#endif + +#if XML_GE == 1 +static void +expat_heap_stat(XML_Parser rootParser, char operator, XmlBigCount absDiff, + XmlBigCount newTotal, XmlBigCount peakTotal, int sourceLine) { + // NOTE: This can be +infinity or -nan + const float amplification + = (float)newTotal / (float)rootParser->m_accounting.countBytesDirect; + fprintf( + stderr, + "expat: Allocations(%p): Direct " EXPAT_FMT_ULL("10") ", allocated %c" EXPAT_FMT_ULL( + "10") " to " EXPAT_FMT_ULL("10") " (" EXPAT_FMT_ULL("10") " peak), amplification %8.2f (xmlparse.c:%d)\n", + (void *)rootParser, rootParser->m_accounting.countBytesDirect, operator, + absDiff, newTotal, peakTotal, (double)amplification, sourceLine); +} + +static bool +expat_heap_increase_tolerable(XML_Parser rootParser, XmlBigCount increase, + int sourceLine) { + assert(rootParser != NULL); + assert(increase > 0); + + XmlBigCount newTotal = 0; + bool tolerable = true; + + // Detect integer overflow + if ((XmlBigCount)-1 - rootParser->m_alloc_tracker.bytesAllocated < increase) { + tolerable = false; + } else { + newTotal = rootParser->m_alloc_tracker.bytesAllocated + increase; + + if (newTotal >= rootParser->m_alloc_tracker.activationThresholdBytes) { + assert(newTotal > 0); + // NOTE: This can be +infinity when dividing by zero but not -nan + const float amplification + = (float)newTotal / (float)rootParser->m_accounting.countBytesDirect; + if (amplification + > rootParser->m_alloc_tracker.maximumAmplificationFactor) { + tolerable = false; + } + } + } + + if (! tolerable && (rootParser->m_alloc_tracker.debugLevel >= 1)) { + expat_heap_stat(rootParser, '+', increase, newTotal, newTotal, sourceLine); + } + + return tolerable; +} + +# if defined(XML_TESTING) +void * +# else +static void * +# endif +expat_malloc(XML_Parser parser, size_t size, int sourceLine) { + // Detect integer overflow + if (SIZE_MAX - size < sizeof(size_t) + EXPAT_MALLOC_PADDING) { + return NULL; + } + + const XML_Parser rootParser = getRootParserOf(parser, NULL); + assert(rootParser->m_parentParser == NULL); + + const size_t bytesToAllocate = sizeof(size_t) + EXPAT_MALLOC_PADDING + size; + + if ((XmlBigCount)-1 - rootParser->m_alloc_tracker.bytesAllocated + < bytesToAllocate) { + return NULL; // i.e. signal integer overflow as out-of-memory + } + + if (! expat_heap_increase_tolerable(rootParser, bytesToAllocate, + sourceLine)) { + return NULL; // i.e. signal violation as out-of-memory + } + + // Actually allocate + void *const mallocedPtr = parser->m_mem.malloc_fcn(bytesToAllocate); + + if (mallocedPtr == NULL) { + return NULL; + } + + // Update in-block recorded size + *(size_t *)mallocedPtr = size; + + // Update accounting + rootParser->m_alloc_tracker.bytesAllocated += bytesToAllocate; + + // Report as needed + if (rootParser->m_alloc_tracker.debugLevel >= 2) { + if (rootParser->m_alloc_tracker.bytesAllocated + > rootParser->m_alloc_tracker.peakBytesAllocated) { + rootParser->m_alloc_tracker.peakBytesAllocated + = rootParser->m_alloc_tracker.bytesAllocated; + } + expat_heap_stat(rootParser, '+', bytesToAllocate, + rootParser->m_alloc_tracker.bytesAllocated, + rootParser->m_alloc_tracker.peakBytesAllocated, sourceLine); + } + + return (char *)mallocedPtr + sizeof(size_t) + EXPAT_MALLOC_PADDING; +} + +# if defined(XML_TESTING) +void +# else +static void +# endif +expat_free(XML_Parser parser, void *ptr, int sourceLine) { + assert(parser != NULL); + + if (ptr == NULL) { + return; + } + + const XML_Parser rootParser = getRootParserOf(parser, NULL); + assert(rootParser->m_parentParser == NULL); + + // Extract size (to the eyes of malloc_fcn/realloc_fcn) and + // the original pointer returned by malloc/realloc + void *const mallocedPtr = (char *)ptr - EXPAT_MALLOC_PADDING - sizeof(size_t); + const size_t bytesAllocated + = sizeof(size_t) + EXPAT_MALLOC_PADDING + *(size_t *)mallocedPtr; + + // Update accounting + assert(rootParser->m_alloc_tracker.bytesAllocated >= bytesAllocated); + rootParser->m_alloc_tracker.bytesAllocated -= bytesAllocated; + + // Report as needed + if (rootParser->m_alloc_tracker.debugLevel >= 2) { + expat_heap_stat(rootParser, '-', bytesAllocated, + rootParser->m_alloc_tracker.bytesAllocated, + rootParser->m_alloc_tracker.peakBytesAllocated, sourceLine); + } + + // NOTE: This may be freeing rootParser, so freeing has to come last + parser->m_mem.free_fcn(mallocedPtr); +} + +# if defined(XML_TESTING) +void * +# else +static void * +# endif +expat_realloc(XML_Parser parser, void *ptr, size_t size, int sourceLine) { + assert(parser != NULL); + + if (ptr == NULL) { + return expat_malloc(parser, size, sourceLine); + } + + if (size == 0) { + expat_free(parser, ptr, sourceLine); + return NULL; + } + + const XML_Parser rootParser = getRootParserOf(parser, NULL); + assert(rootParser->m_parentParser == NULL); + + // Extract original size (to the eyes of the caller) and the original + // pointer returned by malloc/realloc + void *mallocedPtr = (char *)ptr - EXPAT_MALLOC_PADDING - sizeof(size_t); + const size_t prevSize = *(size_t *)mallocedPtr; + + // Classify upcoming change + const bool isIncrease = (size > prevSize); + const size_t absDiff + = (size > prevSize) ? (size - prevSize) : (prevSize - size); + + // Ask for permission from accounting + if (isIncrease) { + if (! expat_heap_increase_tolerable(rootParser, absDiff, sourceLine)) { + return NULL; // i.e. signal violation as out-of-memory + } + } + + // NOTE: Integer overflow detection has already been done for us + // by expat_heap_increase_tolerable(..) above + assert(SIZE_MAX - sizeof(size_t) - EXPAT_MALLOC_PADDING >= size); + + // Actually allocate + mallocedPtr = parser->m_mem.realloc_fcn( + mallocedPtr, sizeof(size_t) + EXPAT_MALLOC_PADDING + size); + + if (mallocedPtr == NULL) { + return NULL; + } + + // Update accounting + if (isIncrease) { + assert((XmlBigCount)-1 - rootParser->m_alloc_tracker.bytesAllocated + >= absDiff); + rootParser->m_alloc_tracker.bytesAllocated += absDiff; + } else { // i.e. decrease + assert(rootParser->m_alloc_tracker.bytesAllocated >= absDiff); + rootParser->m_alloc_tracker.bytesAllocated -= absDiff; + } + + // Report as needed + if (rootParser->m_alloc_tracker.debugLevel >= 2) { + if (rootParser->m_alloc_tracker.bytesAllocated + > rootParser->m_alloc_tracker.peakBytesAllocated) { + rootParser->m_alloc_tracker.peakBytesAllocated + = rootParser->m_alloc_tracker.bytesAllocated; + } + expat_heap_stat(rootParser, isIncrease ? '+' : '-', absDiff, + rootParser->m_alloc_tracker.bytesAllocated, + rootParser->m_alloc_tracker.peakBytesAllocated, sourceLine); + } + + // Update in-block recorded size + *(size_t *)mallocedPtr = size; + + return (char *)mallocedPtr + sizeof(size_t) + EXPAT_MALLOC_PADDING; +} +#endif // XML_GE == 1 XML_Parser XMLCALL -XML_ParserCreate(const XML_Char *encodingName) -{ +XML_ParserCreate(const XML_Char *encodingName) { return XML_ParserCreate_MM(encodingName, NULL, NULL); } XML_Parser XMLCALL -XML_ParserCreateNS(const XML_Char *encodingName, XML_Char nsSep) -{ - XML_Char tmp[2]; - *tmp = nsSep; +XML_ParserCreateNS(const XML_Char *encodingName, XML_Char nsSep) { + XML_Char tmp[2] = {nsSep, 0}; return XML_ParserCreate_MM(encodingName, NULL, tmp); } -static const XML_Char implicitContext[] = { - ASCII_x, ASCII_m, ASCII_l, ASCII_EQUALS, ASCII_h, ASCII_t, ASCII_t, ASCII_p, - ASCII_COLON, ASCII_SLASH, ASCII_SLASH, ASCII_w, ASCII_w, ASCII_w, - ASCII_PERIOD, ASCII_w, ASCII_3, ASCII_PERIOD, ASCII_o, ASCII_r, ASCII_g, - ASCII_SLASH, ASCII_X, ASCII_M, ASCII_L, ASCII_SLASH, ASCII_1, ASCII_9, - ASCII_9, ASCII_8, ASCII_SLASH, ASCII_n, ASCII_a, ASCII_m, ASCII_e, - ASCII_s, ASCII_p, ASCII_a, ASCII_c, ASCII_e, '\0' -}; +// "xml=http://www.w3.org/XML/1998/namespace" +static const XML_Char implicitContext[] + = {ASCII_x, ASCII_m, ASCII_l, ASCII_EQUALS, ASCII_h, + ASCII_t, ASCII_t, ASCII_p, ASCII_COLON, ASCII_SLASH, + ASCII_SLASH, ASCII_w, ASCII_w, ASCII_w, ASCII_PERIOD, + ASCII_w, ASCII_3, ASCII_PERIOD, ASCII_o, ASCII_r, + ASCII_g, ASCII_SLASH, ASCII_X, ASCII_M, ASCII_L, + ASCII_SLASH, ASCII_1, ASCII_9, ASCII_9, ASCII_8, + ASCII_SLASH, ASCII_n, ASCII_a, ASCII_m, ASCII_e, + ASCII_s, ASCII_p, ASCII_a, ASCII_c, ASCII_e, + '\0'}; -XML_Parser XMLCALL -XML_ParserCreate_MM(const XML_Char *encodingName, - const XML_Memory_Handling_Suite *memsuite, - const XML_Char *nameSep) -{ - XML_Parser parser = parserCreate(encodingName, memsuite, nameSep, NULL); - if (parser != NULL && ns) { +/* To avoid warnings about unused functions: */ +#if ! defined(HAVE_ARC4RANDOM_BUF) && ! defined(HAVE_ARC4RANDOM) + +# if defined(HAVE_GETRANDOM) || defined(HAVE_SYSCALL_GETRANDOM) + +/* Obtain entropy on Linux 3.17+ */ +static int +writeRandomBytes_getrandom_nonblock(void *target, size_t count) { + int success = 0; /* full count bytes written? */ + size_t bytesWrittenTotal = 0; + const unsigned int getrandomFlags = GRND_NONBLOCK; + + do { + void *const currentTarget = (void *)((char *)target + bytesWrittenTotal); + const size_t bytesToWrite = count - bytesWrittenTotal; + + assert(bytesToWrite <= INT_MAX); + + const int bytesWrittenMore = +# if defined(HAVE_GETRANDOM) + (int)getrandom(currentTarget, bytesToWrite, getrandomFlags); +# else + (int)syscall(SYS_getrandom, currentTarget, bytesToWrite, + getrandomFlags); +# endif + + if (bytesWrittenMore > 0) { + bytesWrittenTotal += bytesWrittenMore; + if (bytesWrittenTotal >= count) + success = 1; + } + } while (! success && (errno == EINTR)); + + return success; +} + +# endif /* defined(HAVE_GETRANDOM) || defined(HAVE_SYSCALL_GETRANDOM) */ + +# if ! defined(_WIN32) && defined(XML_DEV_URANDOM) + +/* Extract entropy from /dev/urandom */ +static int +writeRandomBytes_dev_urandom(void *target, size_t count) { + int success = 0; /* full count bytes written? */ + size_t bytesWrittenTotal = 0; + + const int fd = open("/dev/urandom", O_RDONLY); + if (fd < 0) { + return 0; + } + + do { + void *const currentTarget = (void *)((char *)target + bytesWrittenTotal); + const size_t bytesToWrite = count - bytesWrittenTotal; + + const ssize_t bytesWrittenMore = read(fd, currentTarget, bytesToWrite); + + if (bytesWrittenMore > 0) { + bytesWrittenTotal += bytesWrittenMore; + if (bytesWrittenTotal >= count) + success = 1; + } + } while (! success && (errno == EINTR)); + + close(fd); + return success; +} + +# endif /* ! defined(_WIN32) && defined(XML_DEV_URANDOM) */ + +#endif /* ! defined(HAVE_ARC4RANDOM_BUF) && ! defined(HAVE_ARC4RANDOM) */ + +#if defined(HAVE_ARC4RANDOM) && ! defined(HAVE_ARC4RANDOM_BUF) + +static void +writeRandomBytes_arc4random(void *target, size_t count) { + size_t bytesWrittenTotal = 0; + + while (bytesWrittenTotal < count) { + const uint32_t random32 = arc4random(); + size_t i = 0; + + for (; (i < sizeof(random32)) && (bytesWrittenTotal < count); + i++, bytesWrittenTotal++) { + const uint8_t random8 = (uint8_t)(random32 >> (i * 8)); + ((uint8_t *)target)[bytesWrittenTotal] = random8; + } + } +} + +#endif /* defined(HAVE_ARC4RANDOM) && ! defined(HAVE_ARC4RANDOM_BUF) */ + +#ifdef _WIN32 + +/* Provide declaration of rand_s() for MinGW-32 (not 64, which has it), + as it didn't declare it in its header prior to version 5.3.0 of its + runtime package (mingwrt, containing stdlib.h). The upstream fix + was introduced at https://osdn.net/projects/mingw/ticket/39658 . */ +# if defined(__MINGW32__) && defined(__MINGW32_VERSION) \ + && __MINGW32_VERSION < 5003000L && ! defined(__MINGW64_VERSION_MAJOR) +__declspec(dllimport) int rand_s(unsigned int *); +# endif + +/* Obtain entropy on Windows using the rand_s() function which + * generates cryptographically secure random numbers. Internally it + * uses RtlGenRandom API which is present in Windows XP and later. + */ +static int +writeRandomBytes_rand_s(void *target, size_t count) { + size_t bytesWrittenTotal = 0; + + while (bytesWrittenTotal < count) { + unsigned int random32 = 0; + size_t i = 0; + + if (rand_s(&random32)) + return 0; /* failure */ + + for (; (i < sizeof(random32)) && (bytesWrittenTotal < count); + i++, bytesWrittenTotal++) { + const uint8_t random8 = (uint8_t)(random32 >> (i * 8)); + ((uint8_t *)target)[bytesWrittenTotal] = random8; + } + } + return 1; /* success */ +} + +#endif /* _WIN32 */ + +#if ! defined(HAVE_ARC4RANDOM_BUF) && ! defined(HAVE_ARC4RANDOM) + +static unsigned long +gather_time_entropy(void) { +# ifdef _WIN32 + FILETIME ft; + GetSystemTimeAsFileTime(&ft); /* never fails */ + return ft.dwHighDateTime ^ ft.dwLowDateTime; +# else + struct timeval tv; + int gettimeofday_res; + + gettimeofday_res = gettimeofday(&tv, NULL); + +# if defined(NDEBUG) + (void)gettimeofday_res; +# else + assert(gettimeofday_res == 0); +# endif /* defined(NDEBUG) */ + + /* Microseconds time is <20 bits entropy */ + return tv.tv_usec; +# endif +} + +#endif /* ! defined(HAVE_ARC4RANDOM_BUF) && ! defined(HAVE_ARC4RANDOM) */ + +static unsigned long +ENTROPY_DEBUG(const char *label, unsigned long entropy) { + if (getDebugLevel("EXPAT_ENTROPY_DEBUG", 0) >= 1u) { + fprintf(stderr, "expat: Entropy: %s --> 0x%0*lx (%lu bytes)\n", label, + (int)sizeof(entropy) * 2, entropy, (unsigned long)sizeof(entropy)); + } + return entropy; +} + +static unsigned long +generate_hash_secret_salt(XML_Parser parser) { + unsigned long entropy; + (void)parser; + + /* "Failproof" high quality providers: */ +#if defined(HAVE_ARC4RANDOM_BUF) + arc4random_buf(&entropy, sizeof(entropy)); + return ENTROPY_DEBUG("arc4random_buf", entropy); +#elif defined(HAVE_ARC4RANDOM) + writeRandomBytes_arc4random((void *)&entropy, sizeof(entropy)); + return ENTROPY_DEBUG("arc4random", entropy); +#else + /* Try high quality providers first .. */ +# ifdef _WIN32 + if (writeRandomBytes_rand_s((void *)&entropy, sizeof(entropy))) { + return ENTROPY_DEBUG("rand_s", entropy); + } +# elif defined(HAVE_GETRANDOM) || defined(HAVE_SYSCALL_GETRANDOM) + if (writeRandomBytes_getrandom_nonblock((void *)&entropy, sizeof(entropy))) { + return ENTROPY_DEBUG("getrandom", entropy); + } +# endif +# if ! defined(_WIN32) && defined(XML_DEV_URANDOM) + if (writeRandomBytes_dev_urandom((void *)&entropy, sizeof(entropy))) { + return ENTROPY_DEBUG("/dev/urandom", entropy); + } +# endif /* ! defined(_WIN32) && defined(XML_DEV_URANDOM) */ + /* .. and self-made low quality for backup: */ + + /* Process ID is 0 bits entropy if attacker has local access */ + entropy = gather_time_entropy() ^ getpid(); + + /* Factors are 2^31-1 and 2^61-1 (Mersenne primes M31 and M61) */ + if (sizeof(unsigned long) == 4) { + return ENTROPY_DEBUG("fallback(4)", entropy * 2147483647); + } else { + return ENTROPY_DEBUG("fallback(8)", + entropy * (unsigned long)2305843009213693951ULL); + } +#endif +} + +static unsigned long +get_hash_secret_salt(XML_Parser parser) { + const XML_Parser rootParser = getRootParserOf(parser, NULL); + assert(! rootParser->m_parentParser); + + return rootParser->m_hash_secret_salt; +} + +static enum XML_Error +callProcessor(XML_Parser parser, const char *start, const char *end, + const char **endPtr) { + const size_t have_now = EXPAT_SAFE_PTR_DIFF(end, start); + + if (parser->m_reparseDeferralEnabled + && ! parser->m_parsingStatus.finalBuffer) { + // Heuristic: don't try to parse a partial token again until the amount of + // available data has increased significantly. + const size_t had_before = parser->m_partialTokenBytesBefore; + // ...but *do* try anyway if we're close to causing a reallocation. + size_t available_buffer + = EXPAT_SAFE_PTR_DIFF(parser->m_bufferPtr, parser->m_buffer); +#if XML_CONTEXT_BYTES > 0 + available_buffer -= EXPAT_MIN(available_buffer, XML_CONTEXT_BYTES); +#endif + available_buffer + += EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_bufferEnd); + // m_lastBufferRequestSize is never assigned a value < 0, so the cast is ok + const bool enough + = (have_now >= 2 * had_before) + || ((size_t)parser->m_lastBufferRequestSize > available_buffer); + + if (! enough) { + *endPtr = start; // callers may expect this to be set + return XML_ERROR_NONE; + } + } +#if defined(XML_TESTING) + g_bytesScanned += (unsigned)have_now; +#endif + // Run in a loop to eliminate dangerous recursion depths + enum XML_Error ret; + *endPtr = start; + while (1) { + // Use endPtr as the new start in each iteration, since it will + // be set to the next start point by m_processor. + ret = parser->m_processor(parser, *endPtr, end, endPtr); + + // Make parsing status (and in particular XML_SUSPENDED) take + // precedence over re-enter flag when they disagree + if (parser->m_parsingStatus.parsing != XML_PARSING) { + parser->m_reenter = XML_FALSE; + } + + if (! parser->m_reenter) { + break; + } + + parser->m_reenter = XML_FALSE; + if (ret != XML_ERROR_NONE) + return ret; + } + + if (ret == XML_ERROR_NONE) { + // if we consumed nothing, remember what we had on this parse attempt. + if (*endPtr == start) { + parser->m_partialTokenBytesBefore = have_now; + } else { + parser->m_partialTokenBytesBefore = 0; + } + } + return ret; +} + +static XML_Bool /* only valid for root parser */ +startParsing(XML_Parser parser) { + /* hash functions must be initialized before setContext() is called */ + if (parser->m_hash_secret_salt == 0) + parser->m_hash_secret_salt = generate_hash_secret_salt(parser); + if (parser->m_ns) { /* implicit context only set for root parser, since child parsers (i.e. external entity parsers) will inherit it */ - if (!setContext(parser, implicitContext)) { - XML_ParserFree(parser); - return NULL; - } + return setContext(parser, implicitContext); } - return parser; + return XML_TRUE; +} + +XML_Parser XMLCALL +XML_ParserCreate_MM(const XML_Char *encodingName, + const XML_Memory_Handling_Suite *memsuite, + const XML_Char *nameSep) { + return parserCreate(encodingName, memsuite, nameSep, NULL, NULL); } static XML_Parser parserCreate(const XML_Char *encodingName, - const XML_Memory_Handling_Suite *memsuite, - const XML_Char *nameSep, - DTD *dtd) -{ - XML_Parser parser; + const XML_Memory_Handling_Suite *memsuite, const XML_Char *nameSep, + DTD *dtd, XML_Parser parentParser) { + XML_Parser parser = NULL; + +#if XML_GE == 1 + const size_t increase + = sizeof(size_t) + EXPAT_MALLOC_PADDING + sizeof(struct XML_ParserStruct); + + if (parentParser != NULL) { + const XML_Parser rootParser = getRootParserOf(parentParser, NULL); + if (! expat_heap_increase_tolerable(rootParser, increase, __LINE__)) { + return NULL; + } + } +#else + UNUSED_P(parentParser); +#endif if (memsuite) { XML_Memory_Handling_Suite *mtemp; - parser = (XML_Parser) - memsuite->malloc_fcn(sizeof(struct XML_ParserStruct)); +#if XML_GE == 1 + void *const sizeAndParser + = memsuite->malloc_fcn(sizeof(size_t) + EXPAT_MALLOC_PADDING + + sizeof(struct XML_ParserStruct)); + if (sizeAndParser != NULL) { + *(size_t *)sizeAndParser = sizeof(struct XML_ParserStruct); + parser = (XML_Parser)((char *)sizeAndParser + sizeof(size_t) + + EXPAT_MALLOC_PADDING); +#else + parser = memsuite->malloc_fcn(sizeof(struct XML_ParserStruct)); if (parser != NULL) { +#endif mtemp = (XML_Memory_Handling_Suite *)&(parser->m_mem); mtemp->malloc_fcn = memsuite->malloc_fcn; mtemp->realloc_fcn = memsuite->realloc_fcn; mtemp->free_fcn = memsuite->free_fcn; } - } - else { + } else { XML_Memory_Handling_Suite *mtemp; - parser = (XML_Parser)malloc(sizeof(struct XML_ParserStruct)); +#if XML_GE == 1 + void *const sizeAndParser = malloc(sizeof(size_t) + EXPAT_MALLOC_PADDING + + sizeof(struct XML_ParserStruct)); + if (sizeAndParser != NULL) { + *(size_t *)sizeAndParser = sizeof(struct XML_ParserStruct); + parser = (XML_Parser)((char *)sizeAndParser + sizeof(size_t) + + EXPAT_MALLOC_PADDING); +#else + parser = malloc(sizeof(struct XML_ParserStruct)); if (parser != NULL) { +#endif mtemp = (XML_Memory_Handling_Suite *)&(parser->m_mem); mtemp->malloc_fcn = malloc; mtemp->realloc_fcn = realloc; mtemp->free_fcn = free; } - } + } // cppcheck-suppress[memleak symbolName=sizeAndParser] // Cppcheck >=2.18.0 - if (!parser) + if (! parser) return parser; - buffer = NULL; - bufferLim = NULL; +#if XML_GE == 1 + // Initialize .m_alloc_tracker + memset(&parser->m_alloc_tracker, 0, sizeof(MALLOC_TRACKER)); + if (parentParser == NULL) { + parser->m_alloc_tracker.debugLevel + = getDebugLevel("EXPAT_MALLOC_DEBUG", 0u); + parser->m_alloc_tracker.maximumAmplificationFactor + = EXPAT_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION_DEFAULT; + parser->m_alloc_tracker.activationThresholdBytes + = EXPAT_ALLOC_TRACKER_ACTIVATION_THRESHOLD_DEFAULT; + + // NOTE: This initialization needs to come this early because these fields + // are read by allocation tracking code + parser->m_parentParser = NULL; + parser->m_accounting.countBytesDirect = 0; + } else { + parser->m_parentParser = parentParser; + } + + // Record XML_ParserStruct allocation we did a few lines up before + const XML_Parser rootParser = getRootParserOf(parser, NULL); + assert(rootParser->m_parentParser == NULL); + assert(SIZE_MAX - rootParser->m_alloc_tracker.bytesAllocated >= increase); + rootParser->m_alloc_tracker.bytesAllocated += increase; + + // Report on allocation + if (rootParser->m_alloc_tracker.debugLevel >= 2) { + if (rootParser->m_alloc_tracker.bytesAllocated + > rootParser->m_alloc_tracker.peakBytesAllocated) { + rootParser->m_alloc_tracker.peakBytesAllocated + = rootParser->m_alloc_tracker.bytesAllocated; + } + + expat_heap_stat(rootParser, '+', increase, + rootParser->m_alloc_tracker.bytesAllocated, + rootParser->m_alloc_tracker.peakBytesAllocated, __LINE__); + } +#else + parser->m_parentParser = NULL; +#endif // XML_GE == 1 + + parser->m_buffer = NULL; + parser->m_bufferLim = NULL; - attsSize = INIT_ATTS_SIZE; - atts = (ATTRIBUTE *)MALLOC(attsSize * sizeof(ATTRIBUTE)); - if (atts == NULL) { - FREE(parser); + parser->m_attsSize = INIT_ATTS_SIZE; + parser->m_atts = MALLOC(parser, parser->m_attsSize * sizeof(ATTRIBUTE)); + if (parser->m_atts == NULL) { + FREE(parser, parser); + return NULL; + } +#ifdef XML_ATTR_INFO + parser->m_attInfo = MALLOC(parser, parser->m_attsSize * sizeof(XML_AttrInfo)); + if (parser->m_attInfo == NULL) { + FREE(parser, parser->m_atts); + FREE(parser, parser); return NULL; } - dataBuf = (XML_Char *)MALLOC(INIT_DATA_BUF_SIZE * sizeof(XML_Char)); - if (dataBuf == NULL) { - FREE(atts); - FREE(parser); +#endif + parser->m_dataBuf = MALLOC(parser, INIT_DATA_BUF_SIZE * sizeof(XML_Char)); + if (parser->m_dataBuf == NULL) { + FREE(parser, parser->m_atts); +#ifdef XML_ATTR_INFO + FREE(parser, parser->m_attInfo); +#endif + FREE(parser, parser); return NULL; } - dataBufEnd = dataBuf + INIT_DATA_BUF_SIZE; + parser->m_dataBufEnd = parser->m_dataBuf + INIT_DATA_BUF_SIZE; if (dtd) - _dtd = dtd; + parser->m_dtd = dtd; else { - _dtd = dtdCreate(&parser->m_mem); - if (_dtd == NULL) { - FREE(dataBuf); - FREE(atts); - FREE(parser); + parser->m_dtd = dtdCreate(parser); + if (parser->m_dtd == NULL) { + FREE(parser, parser->m_dataBuf); + FREE(parser, parser->m_atts); +#ifdef XML_ATTR_INFO + FREE(parser, parser->m_attInfo); +#endif + FREE(parser, parser); return NULL; } } - freeBindingList = NULL; - freeTagList = NULL; - freeInternalEntities = NULL; + parser->m_freeBindingList = NULL; + parser->m_freeTagList = NULL; + parser->m_freeInternalEntities = NULL; + parser->m_freeAttributeEntities = NULL; + parser->m_freeValueEntities = NULL; - groupSize = 0; - groupConnector = NULL; + parser->m_groupSize = 0; + parser->m_groupConnector = NULL; - unknownEncodingHandler = NULL; - unknownEncodingHandlerData = NULL; + parser->m_unknownEncodingHandler = NULL; + parser->m_unknownEncodingHandlerData = NULL; - namespaceSeparator = ASCII_EXCL; - ns = XML_FALSE; - ns_triplets = XML_FALSE; + parser->m_namespaceSeparator = ASCII_EXCL; + parser->m_ns = XML_FALSE; + parser->m_ns_triplets = XML_FALSE; - nsAtts = NULL; - nsAttsVersion = 0; - nsAttsPower = 0; + parser->m_nsAtts = NULL; + parser->m_nsAttsVersion = 0; + parser->m_nsAttsPower = 0; - poolInit(&tempPool, &(parser->m_mem)); - poolInit(&temp2Pool, &(parser->m_mem)); + parser->m_protocolEncodingName = NULL; + + poolInit(&parser->m_tempPool, parser); + poolInit(&parser->m_temp2Pool, parser); parserInit(parser, encodingName); - if (encodingName && !protocolEncodingName) { + if (encodingName && ! parser->m_protocolEncodingName) { + if (dtd) { + // We need to stop the upcoming call to XML_ParserFree from happily + // destroying parser->m_dtd because the DTD is shared with the parent + // parser and the only guard that keeps XML_ParserFree from destroying + // parser->m_dtd is parser->m_isParamEntity but it will be set to + // XML_TRUE only later in XML_ExternalEntityParserCreate (or not at all). + parser->m_dtd = NULL; + } XML_ParserFree(parser); return NULL; } if (nameSep) { - ns = XML_TRUE; - internalEncoding = XmlGetInternalEncodingNS(); - namespaceSeparator = *nameSep; - } - else { - internalEncoding = XmlGetInternalEncoding(); + parser->m_ns = XML_TRUE; + parser->m_internalEncoding = XmlGetInternalEncodingNS(); + parser->m_namespaceSeparator = *nameSep; + } else { + parser->m_internalEncoding = XmlGetInternalEncoding(); } return parser; } static void -parserInit(XML_Parser parser, const XML_Char *encodingName) -{ - processor = prologInitProcessor; - XmlPrologStateInit(&prologState); - protocolEncodingName = (encodingName != NULL - ? poolCopyString(&tempPool, encodingName) - : NULL); - curBase = NULL; - XmlInitEncoding(&initEncoding, &encoding, 0); - userData = NULL; - handlerArg = NULL; - startElementHandler = NULL; - endElementHandler = NULL; - characterDataHandler = NULL; - processingInstructionHandler = NULL; - commentHandler = NULL; - startCdataSectionHandler = NULL; - endCdataSectionHandler = NULL; - defaultHandler = NULL; - startDoctypeDeclHandler = NULL; - endDoctypeDeclHandler = NULL; - unparsedEntityDeclHandler = NULL; - notationDeclHandler = NULL; - startNamespaceDeclHandler = NULL; - endNamespaceDeclHandler = NULL; - notStandaloneHandler = NULL; - externalEntityRefHandler = NULL; - externalEntityRefHandlerArg = parser; - skippedEntityHandler = NULL; - elementDeclHandler = NULL; - attlistDeclHandler = NULL; - entityDeclHandler = NULL; - xmlDeclHandler = NULL; - bufferPtr = buffer; - bufferEnd = buffer; - parseEndByteIndex = 0; - parseEndPtr = NULL; - declElementType = NULL; - declAttributeId = NULL; - declEntity = NULL; - doctypeName = NULL; - doctypeSysid = NULL; - doctypePubid = NULL; - declAttributeType = NULL; - declNotationName = NULL; - declNotationPublicId = NULL; - declAttributeIsCdata = XML_FALSE; - declAttributeIsId = XML_FALSE; - memset(&position, 0, sizeof(POSITION)); - errorCode = XML_ERROR_NONE; - eventPtr = NULL; - eventEndPtr = NULL; - positionPtr = NULL; - openInternalEntities = NULL; - defaultExpandInternalEntities = XML_TRUE; - tagLevel = 0; - tagStack = NULL; - inheritedBindings = NULL; - nSpecifiedAtts = 0; - unknownEncodingMem = NULL; - unknownEncodingRelease = NULL; - unknownEncodingData = NULL; - parentParser = NULL; - ps_parsing = XML_INITIALIZED; +parserInit(XML_Parser parser, const XML_Char *encodingName) { + parser->m_processor = prologInitProcessor; + XmlPrologStateInit(&parser->m_prologState); + if (encodingName != NULL) { + parser->m_protocolEncodingName = copyString(encodingName, parser); + } + parser->m_curBase = NULL; + XmlInitEncoding(&parser->m_initEncoding, &parser->m_encoding, 0); + parser->m_userData = NULL; + parser->m_handlerArg = NULL; + parser->m_startElementHandler = NULL; + parser->m_endElementHandler = NULL; + parser->m_characterDataHandler = NULL; + parser->m_processingInstructionHandler = NULL; + parser->m_commentHandler = NULL; + parser->m_startCdataSectionHandler = NULL; + parser->m_endCdataSectionHandler = NULL; + parser->m_defaultHandler = NULL; + parser->m_startDoctypeDeclHandler = NULL; + parser->m_endDoctypeDeclHandler = NULL; + parser->m_unparsedEntityDeclHandler = NULL; + parser->m_notationDeclHandler = NULL; + parser->m_startNamespaceDeclHandler = NULL; + parser->m_endNamespaceDeclHandler = NULL; + parser->m_notStandaloneHandler = NULL; + parser->m_externalEntityRefHandler = NULL; + parser->m_externalEntityRefHandlerArg = parser; + parser->m_skippedEntityHandler = NULL; + parser->m_elementDeclHandler = NULL; + parser->m_attlistDeclHandler = NULL; + parser->m_entityDeclHandler = NULL; + parser->m_xmlDeclHandler = NULL; + parser->m_bufferPtr = parser->m_buffer; + parser->m_bufferEnd = parser->m_buffer; + parser->m_parseEndByteIndex = 0; + parser->m_parseEndPtr = NULL; + parser->m_partialTokenBytesBefore = 0; + parser->m_reparseDeferralEnabled = g_reparseDeferralEnabledDefault; + parser->m_lastBufferRequestSize = 0; + parser->m_declElementType = NULL; + parser->m_declAttributeId = NULL; + parser->m_declEntity = NULL; + parser->m_doctypeName = NULL; + parser->m_doctypeSysid = NULL; + parser->m_doctypePubid = NULL; + parser->m_declAttributeType = NULL; + parser->m_declNotationName = NULL; + parser->m_declNotationPublicId = NULL; + parser->m_declAttributeIsCdata = XML_FALSE; + parser->m_declAttributeIsId = XML_FALSE; + memset(&parser->m_position, 0, sizeof(POSITION)); + parser->m_errorCode = XML_ERROR_NONE; + parser->m_eventPtr = NULL; + parser->m_eventEndPtr = NULL; + parser->m_positionPtr = NULL; + parser->m_openInternalEntities = NULL; + parser->m_openAttributeEntities = NULL; + parser->m_openValueEntities = NULL; + parser->m_defaultExpandInternalEntities = XML_TRUE; + parser->m_tagLevel = 0; + parser->m_tagStack = NULL; + parser->m_inheritedBindings = NULL; + parser->m_nSpecifiedAtts = 0; + parser->m_unknownEncodingMem = NULL; + parser->m_unknownEncodingRelease = NULL; + parser->m_unknownEncodingData = NULL; + parser->m_parsingStatus.parsing = XML_INITIALIZED; + // Reentry can only be triggered inside m_processor calls + parser->m_reenter = XML_FALSE; #ifdef XML_DTD - isParamEntity = XML_FALSE; - useForeignDTD = XML_FALSE; - paramEntityParsing = XML_PARAM_ENTITY_PARSING_NEVER; + parser->m_isParamEntity = XML_FALSE; + parser->m_useForeignDTD = XML_FALSE; + parser->m_paramEntityParsing = XML_PARAM_ENTITY_PARSING_NEVER; +#endif + parser->m_hash_secret_salt = 0; + +#if XML_GE == 1 + memset(&parser->m_accounting, 0, sizeof(ACCOUNTING)); + parser->m_accounting.debugLevel = getDebugLevel("EXPAT_ACCOUNTING_DEBUG", 0u); + parser->m_accounting.maximumAmplificationFactor + = EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_MAXIMUM_AMPLIFICATION_DEFAULT; + parser->m_accounting.activationThresholdBytes + = EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT; + + memset(&parser->m_entity_stats, 0, sizeof(ENTITY_STATS)); + parser->m_entity_stats.debugLevel = getDebugLevel("EXPAT_ENTITY_DEBUG", 0u); #endif } -/* moves list of bindings to freeBindingList */ +/* moves list of bindings to m_freeBindingList */ static void FASTCALL -moveToFreeBindingList(XML_Parser parser, BINDING *bindings) -{ +moveToFreeBindingList(XML_Parser parser, BINDING *bindings) { while (bindings) { BINDING *b = bindings; bindings = bindings->nextTagBinding; - b->nextTagBinding = freeBindingList; - freeBindingList = b; + b->nextTagBinding = parser->m_freeBindingList; + parser->m_freeBindingList = b; } } XML_Bool XMLCALL -XML_ParserReset(XML_Parser parser, const XML_Char *encodingName) -{ +XML_ParserReset(XML_Parser parser, const XML_Char *encodingName) { TAG *tStk; OPEN_INTERNAL_ENTITY *openEntityList; - if (parentParser) + + if (parser == NULL) + return XML_FALSE; + + if (parser->m_parentParser) return XML_FALSE; - /* move tagStack to freeTagList */ - tStk = tagStack; + /* move m_tagStack to m_freeTagList */ + tStk = parser->m_tagStack; while (tStk) { TAG *tag = tStk; tStk = tStk->parent; - tag->parent = freeTagList; + tag->parent = parser->m_freeTagList; moveToFreeBindingList(parser, tag->bindings); tag->bindings = NULL; - freeTagList = tag; + parser->m_freeTagList = tag; } - /* move openInternalEntities to freeInternalEntities */ - openEntityList = openInternalEntities; + /* move m_openInternalEntities to m_freeInternalEntities */ + openEntityList = parser->m_openInternalEntities; while (openEntityList) { OPEN_INTERNAL_ENTITY *openEntity = openEntityList; openEntityList = openEntity->next; - openEntity->next = freeInternalEntities; - freeInternalEntities = openEntity; - } - moveToFreeBindingList(parser, inheritedBindings); - FREE(unknownEncodingMem); - if (unknownEncodingRelease) - unknownEncodingRelease(unknownEncodingData); - poolClear(&tempPool); - poolClear(&temp2Pool); + openEntity->next = parser->m_freeInternalEntities; + parser->m_freeInternalEntities = openEntity; + } + /* move m_openAttributeEntities to m_freeAttributeEntities (i.e. same task but + * for attributes) */ + openEntityList = parser->m_openAttributeEntities; + while (openEntityList) { + OPEN_INTERNAL_ENTITY *openEntity = openEntityList; + openEntityList = openEntity->next; + openEntity->next = parser->m_freeAttributeEntities; + parser->m_freeAttributeEntities = openEntity; + } + /* move m_openValueEntities to m_freeValueEntities (i.e. same task but + * for value entities) */ + openEntityList = parser->m_openValueEntities; + while (openEntityList) { + OPEN_INTERNAL_ENTITY *openEntity = openEntityList; + openEntityList = openEntity->next; + openEntity->next = parser->m_freeValueEntities; + parser->m_freeValueEntities = openEntity; + } + moveToFreeBindingList(parser, parser->m_inheritedBindings); + FREE(parser, parser->m_unknownEncodingMem); + if (parser->m_unknownEncodingRelease) + parser->m_unknownEncodingRelease(parser->m_unknownEncodingData); + poolClear(&parser->m_tempPool); + poolClear(&parser->m_temp2Pool); + FREE(parser, (void *)parser->m_protocolEncodingName); + parser->m_protocolEncodingName = NULL; parserInit(parser, encodingName); - dtdReset(_dtd, &parser->m_mem); - return setContext(parser, implicitContext); + dtdReset(parser->m_dtd, parser); + return XML_TRUE; +} + +static XML_Bool +parserBusy(XML_Parser parser) { + switch (parser->m_parsingStatus.parsing) { + case XML_PARSING: + case XML_SUSPENDED: + return XML_TRUE; + case XML_INITIALIZED: + case XML_FINISHED: + default: + return XML_FALSE; + } } enum XML_Status XMLCALL -XML_SetEncoding(XML_Parser parser, const XML_Char *encodingName) -{ +XML_SetEncoding(XML_Parser parser, const XML_Char *encodingName) { + if (parser == NULL) + return XML_STATUS_ERROR; /* Block after XML_Parse()/XML_ParseBuffer() has been called. XXX There's no way for the caller to determine which of the XXX possible error cases caused the XML_STATUS_ERROR return. */ - if (ps_parsing == XML_PARSING || ps_parsing == XML_SUSPENDED) + if (parserBusy(parser)) return XML_STATUS_ERROR; + + /* Get rid of any previous encoding name */ + FREE(parser, (void *)parser->m_protocolEncodingName); + if (encodingName == NULL) - protocolEncodingName = NULL; + /* No new encoding name */ + parser->m_protocolEncodingName = NULL; else { - protocolEncodingName = poolCopyString(&tempPool, encodingName); - if (!protocolEncodingName) + /* Copy the new encoding name into allocated memory */ + parser->m_protocolEncodingName = copyString(encodingName, parser); + if (! parser->m_protocolEncodingName) return XML_STATUS_ERROR; } return XML_STATUS_OK; } XML_Parser XMLCALL -XML_ExternalEntityParserCreate(XML_Parser oldParser, - const XML_Char *context, - const XML_Char *encodingName) -{ +XML_ExternalEntityParserCreate(XML_Parser oldParser, const XML_Char *context, + const XML_Char *encodingName) { XML_Parser parser = oldParser; DTD *newDtd = NULL; - DTD *oldDtd = _dtd; - XML_StartElementHandler oldStartElementHandler = startElementHandler; - XML_EndElementHandler oldEndElementHandler = endElementHandler; - XML_CharacterDataHandler oldCharacterDataHandler = characterDataHandler; - XML_ProcessingInstructionHandler oldProcessingInstructionHandler - = processingInstructionHandler; - XML_CommentHandler oldCommentHandler = commentHandler; - XML_StartCdataSectionHandler oldStartCdataSectionHandler - = startCdataSectionHandler; - XML_EndCdataSectionHandler oldEndCdataSectionHandler - = endCdataSectionHandler; - XML_DefaultHandler oldDefaultHandler = defaultHandler; - XML_UnparsedEntityDeclHandler oldUnparsedEntityDeclHandler - = unparsedEntityDeclHandler; - XML_NotationDeclHandler oldNotationDeclHandler = notationDeclHandler; - XML_StartNamespaceDeclHandler oldStartNamespaceDeclHandler - = startNamespaceDeclHandler; - XML_EndNamespaceDeclHandler oldEndNamespaceDeclHandler - = endNamespaceDeclHandler; - XML_NotStandaloneHandler oldNotStandaloneHandler = notStandaloneHandler; - XML_ExternalEntityRefHandler oldExternalEntityRefHandler - = externalEntityRefHandler; - XML_SkippedEntityHandler oldSkippedEntityHandler = skippedEntityHandler; - XML_UnknownEncodingHandler oldUnknownEncodingHandler - = unknownEncodingHandler; - XML_ElementDeclHandler oldElementDeclHandler = elementDeclHandler; - XML_AttlistDeclHandler oldAttlistDeclHandler = attlistDeclHandler; - XML_EntityDeclHandler oldEntityDeclHandler = entityDeclHandler; - XML_XmlDeclHandler oldXmlDeclHandler = xmlDeclHandler; - ELEMENT_TYPE * oldDeclElementType = declElementType; - - void *oldUserData = userData; - void *oldHandlerArg = handlerArg; - XML_Bool oldDefaultExpandInternalEntities = defaultExpandInternalEntities; - XML_Parser oldExternalEntityRefHandlerArg = externalEntityRefHandlerArg; + DTD *oldDtd; + XML_StartElementHandler oldStartElementHandler; + XML_EndElementHandler oldEndElementHandler; + XML_CharacterDataHandler oldCharacterDataHandler; + XML_ProcessingInstructionHandler oldProcessingInstructionHandler; + XML_CommentHandler oldCommentHandler; + XML_StartCdataSectionHandler oldStartCdataSectionHandler; + XML_EndCdataSectionHandler oldEndCdataSectionHandler; + XML_DefaultHandler oldDefaultHandler; + XML_UnparsedEntityDeclHandler oldUnparsedEntityDeclHandler; + XML_NotationDeclHandler oldNotationDeclHandler; + XML_StartNamespaceDeclHandler oldStartNamespaceDeclHandler; + XML_EndNamespaceDeclHandler oldEndNamespaceDeclHandler; + XML_NotStandaloneHandler oldNotStandaloneHandler; + XML_ExternalEntityRefHandler oldExternalEntityRefHandler; + XML_SkippedEntityHandler oldSkippedEntityHandler; + XML_UnknownEncodingHandler oldUnknownEncodingHandler; + XML_ElementDeclHandler oldElementDeclHandler; + XML_AttlistDeclHandler oldAttlistDeclHandler; + XML_EntityDeclHandler oldEntityDeclHandler; + XML_XmlDeclHandler oldXmlDeclHandler; + ELEMENT_TYPE *oldDeclElementType; + + void *oldUserData; + void *oldHandlerArg; + XML_Bool oldDefaultExpandInternalEntities; + XML_Parser oldExternalEntityRefHandlerArg; #ifdef XML_DTD - enum XML_ParamEntityParsing oldParamEntityParsing = paramEntityParsing; - int oldInEntityValue = prologState.inEntityValue; + enum XML_ParamEntityParsing oldParamEntityParsing; + int oldInEntityValue; #endif - XML_Bool oldns_triplets = ns_triplets; + XML_Bool oldns_triplets; + /* Note that the new parser shares the same hash secret as the old + parser, so that dtdCopy and copyEntityTable can lookup values + from hash tables associated with either parser without us having + to worry which hash secrets each table has. + */ + unsigned long oldhash_secret_salt; + XML_Bool oldReparseDeferralEnabled; + + /* Validate the oldParser parameter before we pull everything out of it */ + if (oldParser == NULL) + return NULL; + /* Stash the original parser contents on the stack */ + oldDtd = parser->m_dtd; + oldStartElementHandler = parser->m_startElementHandler; + oldEndElementHandler = parser->m_endElementHandler; + oldCharacterDataHandler = parser->m_characterDataHandler; + oldProcessingInstructionHandler = parser->m_processingInstructionHandler; + oldCommentHandler = parser->m_commentHandler; + oldStartCdataSectionHandler = parser->m_startCdataSectionHandler; + oldEndCdataSectionHandler = parser->m_endCdataSectionHandler; + oldDefaultHandler = parser->m_defaultHandler; + oldUnparsedEntityDeclHandler = parser->m_unparsedEntityDeclHandler; + oldNotationDeclHandler = parser->m_notationDeclHandler; + oldStartNamespaceDeclHandler = parser->m_startNamespaceDeclHandler; + oldEndNamespaceDeclHandler = parser->m_endNamespaceDeclHandler; + oldNotStandaloneHandler = parser->m_notStandaloneHandler; + oldExternalEntityRefHandler = parser->m_externalEntityRefHandler; + oldSkippedEntityHandler = parser->m_skippedEntityHandler; + oldUnknownEncodingHandler = parser->m_unknownEncodingHandler; + oldElementDeclHandler = parser->m_elementDeclHandler; + oldAttlistDeclHandler = parser->m_attlistDeclHandler; + oldEntityDeclHandler = parser->m_entityDeclHandler; + oldXmlDeclHandler = parser->m_xmlDeclHandler; + oldDeclElementType = parser->m_declElementType; + + oldUserData = parser->m_userData; + oldHandlerArg = parser->m_handlerArg; + oldDefaultExpandInternalEntities = parser->m_defaultExpandInternalEntities; + oldExternalEntityRefHandlerArg = parser->m_externalEntityRefHandlerArg; #ifdef XML_DTD - if (!context) + oldParamEntityParsing = parser->m_paramEntityParsing; + oldInEntityValue = parser->m_prologState.inEntityValue; +#endif + oldns_triplets = parser->m_ns_triplets; + /* Note that the new parser shares the same hash secret as the old + parser, so that dtdCopy and copyEntityTable can lookup values + from hash tables associated with either parser without us having + to worry which hash secrets each table has. + */ + oldhash_secret_salt = parser->m_hash_secret_salt; + oldReparseDeferralEnabled = parser->m_reparseDeferralEnabled; + +#ifdef XML_DTD + if (! context) newDtd = oldDtd; #endif /* XML_DTD */ @@ -993,911 +1832,1147 @@ XML_ExternalEntityParserCreate(XML_Parser oldParser, here. This makes this function more painful to follow than it would be otherwise. */ - if (ns) { - XML_Char tmp[2]; - *tmp = namespaceSeparator; - parser = parserCreate(encodingName, &parser->m_mem, tmp, newDtd); - } - else { - parser = parserCreate(encodingName, &parser->m_mem, NULL, newDtd); + if (parser->m_ns) { + XML_Char tmp[2] = {parser->m_namespaceSeparator, 0}; + parser = parserCreate(encodingName, &parser->m_mem, tmp, newDtd, oldParser); + } else { + parser + = parserCreate(encodingName, &parser->m_mem, NULL, newDtd, oldParser); } - if (!parser) + if (! parser) return NULL; - startElementHandler = oldStartElementHandler; - endElementHandler = oldEndElementHandler; - characterDataHandler = oldCharacterDataHandler; - processingInstructionHandler = oldProcessingInstructionHandler; - commentHandler = oldCommentHandler; - startCdataSectionHandler = oldStartCdataSectionHandler; - endCdataSectionHandler = oldEndCdataSectionHandler; - defaultHandler = oldDefaultHandler; - unparsedEntityDeclHandler = oldUnparsedEntityDeclHandler; - notationDeclHandler = oldNotationDeclHandler; - startNamespaceDeclHandler = oldStartNamespaceDeclHandler; - endNamespaceDeclHandler = oldEndNamespaceDeclHandler; - notStandaloneHandler = oldNotStandaloneHandler; - externalEntityRefHandler = oldExternalEntityRefHandler; - skippedEntityHandler = oldSkippedEntityHandler; - unknownEncodingHandler = oldUnknownEncodingHandler; - elementDeclHandler = oldElementDeclHandler; - attlistDeclHandler = oldAttlistDeclHandler; - entityDeclHandler = oldEntityDeclHandler; - xmlDeclHandler = oldXmlDeclHandler; - declElementType = oldDeclElementType; - userData = oldUserData; + parser->m_startElementHandler = oldStartElementHandler; + parser->m_endElementHandler = oldEndElementHandler; + parser->m_characterDataHandler = oldCharacterDataHandler; + parser->m_processingInstructionHandler = oldProcessingInstructionHandler; + parser->m_commentHandler = oldCommentHandler; + parser->m_startCdataSectionHandler = oldStartCdataSectionHandler; + parser->m_endCdataSectionHandler = oldEndCdataSectionHandler; + parser->m_defaultHandler = oldDefaultHandler; + parser->m_unparsedEntityDeclHandler = oldUnparsedEntityDeclHandler; + parser->m_notationDeclHandler = oldNotationDeclHandler; + parser->m_startNamespaceDeclHandler = oldStartNamespaceDeclHandler; + parser->m_endNamespaceDeclHandler = oldEndNamespaceDeclHandler; + parser->m_notStandaloneHandler = oldNotStandaloneHandler; + parser->m_externalEntityRefHandler = oldExternalEntityRefHandler; + parser->m_skippedEntityHandler = oldSkippedEntityHandler; + parser->m_unknownEncodingHandler = oldUnknownEncodingHandler; + parser->m_elementDeclHandler = oldElementDeclHandler; + parser->m_attlistDeclHandler = oldAttlistDeclHandler; + parser->m_entityDeclHandler = oldEntityDeclHandler; + parser->m_xmlDeclHandler = oldXmlDeclHandler; + parser->m_declElementType = oldDeclElementType; + parser->m_userData = oldUserData; if (oldUserData == oldHandlerArg) - handlerArg = userData; + parser->m_handlerArg = parser->m_userData; else - handlerArg = parser; + parser->m_handlerArg = parser; if (oldExternalEntityRefHandlerArg != oldParser) - externalEntityRefHandlerArg = oldExternalEntityRefHandlerArg; - defaultExpandInternalEntities = oldDefaultExpandInternalEntities; - ns_triplets = oldns_triplets; - parentParser = oldParser; + parser->m_externalEntityRefHandlerArg = oldExternalEntityRefHandlerArg; + parser->m_defaultExpandInternalEntities = oldDefaultExpandInternalEntities; + parser->m_ns_triplets = oldns_triplets; + parser->m_hash_secret_salt = oldhash_secret_salt; + parser->m_reparseDeferralEnabled = oldReparseDeferralEnabled; + parser->m_parentParser = oldParser; #ifdef XML_DTD - paramEntityParsing = oldParamEntityParsing; - prologState.inEntityValue = oldInEntityValue; + parser->m_paramEntityParsing = oldParamEntityParsing; + parser->m_prologState.inEntityValue = oldInEntityValue; if (context) { #endif /* XML_DTD */ - if (!dtdCopy(_dtd, oldDtd, &parser->m_mem) - || !setContext(parser, context)) { + if (! dtdCopy(oldParser, parser->m_dtd, oldDtd, parser) + || ! setContext(parser, context)) { XML_ParserFree(parser); return NULL; } - processor = externalEntityInitProcessor; + parser->m_processor = externalEntityInitProcessor; #ifdef XML_DTD - } - else { - /* The DTD instance referenced by _dtd is shared between the document's - root parser and external PE parsers, therefore one does not need to - call setContext. In addition, one also *must* not call setContext, - because this would overwrite existing prefix->binding pointers in - _dtd with ones that get destroyed with the external PE parser. - This would leave those prefixes with dangling pointers. + } else { + /* The DTD instance referenced by parser->m_dtd is shared between the + document's root parser and external PE parsers, therefore one does not + need to call setContext. In addition, one also *must* not call + setContext, because this would overwrite existing prefix->binding + pointers in parser->m_dtd with ones that get destroyed with the external + PE parser. This would leave those prefixes with dangling pointers. */ - isParamEntity = XML_TRUE; - XmlPrologStateInitExternalEntity(&prologState); - processor = externalParEntInitProcessor; + parser->m_isParamEntity = XML_TRUE; + XmlPrologStateInitExternalEntity(&parser->m_prologState); + parser->m_processor = externalParEntInitProcessor; } #endif /* XML_DTD */ return parser; } static void FASTCALL -destroyBindings(BINDING *bindings, XML_Parser parser) -{ +destroyBindings(BINDING *bindings, XML_Parser parser) { for (;;) { BINDING *b = bindings; - if (!b) + if (! b) break; bindings = b->nextTagBinding; - FREE(b->uri); - FREE(b); + FREE(parser, b->uri); + FREE(parser, b); } } void XMLCALL -XML_ParserFree(XML_Parser parser) -{ +XML_ParserFree(XML_Parser parser) { TAG *tagList; OPEN_INTERNAL_ENTITY *entityList; if (parser == NULL) return; - /* free tagStack and freeTagList */ - tagList = tagStack; + /* free m_tagStack and m_freeTagList */ + tagList = parser->m_tagStack; for (;;) { TAG *p; if (tagList == NULL) { - if (freeTagList == NULL) + if (parser->m_freeTagList == NULL) break; - tagList = freeTagList; - freeTagList = NULL; + tagList = parser->m_freeTagList; + parser->m_freeTagList = NULL; } p = tagList; tagList = tagList->parent; - FREE(p->buf); + FREE(parser, p->buf); destroyBindings(p->bindings, parser); - FREE(p); + FREE(parser, p); } - /* free openInternalEntities and freeInternalEntities */ - entityList = openInternalEntities; + /* free m_openInternalEntities and m_freeInternalEntities */ + entityList = parser->m_openInternalEntities; for (;;) { OPEN_INTERNAL_ENTITY *openEntity; if (entityList == NULL) { - if (freeInternalEntities == NULL) + if (parser->m_freeInternalEntities == NULL) break; - entityList = freeInternalEntities; - freeInternalEntities = NULL; + entityList = parser->m_freeInternalEntities; + parser->m_freeInternalEntities = NULL; } openEntity = entityList; entityList = entityList->next; - FREE(openEntity); + FREE(parser, openEntity); } - - destroyBindings(freeBindingList, parser); - destroyBindings(inheritedBindings, parser); - poolDestroy(&tempPool); - poolDestroy(&temp2Pool); + /* free m_openAttributeEntities and m_freeAttributeEntities */ + entityList = parser->m_openAttributeEntities; + for (;;) { + OPEN_INTERNAL_ENTITY *openEntity; + if (entityList == NULL) { + if (parser->m_freeAttributeEntities == NULL) + break; + entityList = parser->m_freeAttributeEntities; + parser->m_freeAttributeEntities = NULL; + } + openEntity = entityList; + entityList = entityList->next; + FREE(parser, openEntity); + } + /* free m_openValueEntities and m_freeValueEntities */ + entityList = parser->m_openValueEntities; + for (;;) { + OPEN_INTERNAL_ENTITY *openEntity; + if (entityList == NULL) { + if (parser->m_freeValueEntities == NULL) + break; + entityList = parser->m_freeValueEntities; + parser->m_freeValueEntities = NULL; + } + openEntity = entityList; + entityList = entityList->next; + FREE(parser, openEntity); + } + destroyBindings(parser->m_freeBindingList, parser); + destroyBindings(parser->m_inheritedBindings, parser); + poolDestroy(&parser->m_tempPool); + poolDestroy(&parser->m_temp2Pool); + FREE(parser, (void *)parser->m_protocolEncodingName); #ifdef XML_DTD /* external parameter entity parsers share the DTD structure parser->m_dtd with the root parser, so we must not destroy it */ - if (!isParamEntity && _dtd) + if (! parser->m_isParamEntity && parser->m_dtd) #else - if (_dtd) + if (parser->m_dtd) #endif /* XML_DTD */ - dtdDestroy(_dtd, (XML_Bool)!parentParser, &parser->m_mem); - FREE((void *)atts); - FREE(groupConnector); - FREE(buffer); - FREE(dataBuf); - FREE(nsAtts); - FREE(unknownEncodingMem); - if (unknownEncodingRelease) - unknownEncodingRelease(unknownEncodingData); - FREE(parser); + dtdDestroy(parser->m_dtd, (XML_Bool)! parser->m_parentParser, parser); + FREE(parser, parser->m_atts); +#ifdef XML_ATTR_INFO + FREE(parser, parser->m_attInfo); +#endif + FREE(parser, parser->m_groupConnector); + // NOTE: We are avoiding FREE(..) here because parser->m_buffer + // is not being allocated with MALLOC(..) but with plain + // .malloc_fcn(..). + parser->m_mem.free_fcn(parser->m_buffer); + FREE(parser, parser->m_dataBuf); + FREE(parser, parser->m_nsAtts); + FREE(parser, parser->m_unknownEncodingMem); + if (parser->m_unknownEncodingRelease) + parser->m_unknownEncodingRelease(parser->m_unknownEncodingData); + FREE(parser, parser); } void XMLCALL -XML_UseParserAsHandlerArg(XML_Parser parser) -{ - handlerArg = parser; +XML_UseParserAsHandlerArg(XML_Parser parser) { + if (parser != NULL) + parser->m_handlerArg = parser; } enum XML_Error XMLCALL -XML_UseForeignDTD(XML_Parser parser, XML_Bool useDTD) -{ +XML_UseForeignDTD(XML_Parser parser, XML_Bool useDTD) { + if (parser == NULL) + return XML_ERROR_INVALID_ARGUMENT; #ifdef XML_DTD /* block after XML_Parse()/XML_ParseBuffer() has been called */ - if (ps_parsing == XML_PARSING || ps_parsing == XML_SUSPENDED) + if (parserBusy(parser)) return XML_ERROR_CANT_CHANGE_FEATURE_ONCE_PARSING; - useForeignDTD = useDTD; + parser->m_useForeignDTD = useDTD; return XML_ERROR_NONE; #else + UNUSED_P(useDTD); return XML_ERROR_FEATURE_REQUIRES_XML_DTD; #endif } void XMLCALL -XML_SetReturnNSTriplet(XML_Parser parser, int do_nst) -{ +XML_SetReturnNSTriplet(XML_Parser parser, int do_nst) { + if (parser == NULL) + return; /* block after XML_Parse()/XML_ParseBuffer() has been called */ - if (ps_parsing == XML_PARSING || ps_parsing == XML_SUSPENDED) + if (parserBusy(parser)) return; - ns_triplets = do_nst ? XML_TRUE : XML_FALSE; + parser->m_ns_triplets = do_nst ? XML_TRUE : XML_FALSE; } void XMLCALL -XML_SetUserData(XML_Parser parser, void *p) -{ - if (handlerArg == userData) - handlerArg = userData = p; +XML_SetUserData(XML_Parser parser, void *p) { + if (parser == NULL) + return; + if (parser->m_handlerArg == parser->m_userData) + parser->m_handlerArg = parser->m_userData = p; else - userData = p; + parser->m_userData = p; } enum XML_Status XMLCALL -XML_SetBase(XML_Parser parser, const XML_Char *p) -{ +XML_SetBase(XML_Parser parser, const XML_Char *p) { + if (parser == NULL) + return XML_STATUS_ERROR; if (p) { - p = poolCopyString(&_dtd->pool, p); - if (!p) + p = poolCopyString(&parser->m_dtd->pool, p); + if (! p) return XML_STATUS_ERROR; - curBase = p; - } - else - curBase = NULL; + parser->m_curBase = p; + } else + parser->m_curBase = NULL; return XML_STATUS_OK; } -const XML_Char * XMLCALL -XML_GetBase(XML_Parser parser) -{ - return curBase; +const XML_Char *XMLCALL +XML_GetBase(XML_Parser parser) { + if (parser == NULL) + return NULL; + return parser->m_curBase; } int XMLCALL -XML_GetSpecifiedAttributeCount(XML_Parser parser) -{ - return nSpecifiedAtts; +XML_GetSpecifiedAttributeCount(XML_Parser parser) { + if (parser == NULL) + return -1; + return parser->m_nSpecifiedAtts; } int XMLCALL -XML_GetIdAttributeIndex(XML_Parser parser) -{ - return idAttIndex; +XML_GetIdAttributeIndex(XML_Parser parser) { + if (parser == NULL) + return -1; + return parser->m_idAttIndex; } +#ifdef XML_ATTR_INFO +const XML_AttrInfo *XMLCALL +XML_GetAttributeInfo(XML_Parser parser) { + if (parser == NULL) + return NULL; + return parser->m_attInfo; +} +#endif + void XMLCALL -XML_SetElementHandler(XML_Parser parser, - XML_StartElementHandler start, - XML_EndElementHandler end) -{ - startElementHandler = start; - endElementHandler = end; +XML_SetElementHandler(XML_Parser parser, XML_StartElementHandler start, + XML_EndElementHandler end) { + if (parser == NULL) + return; + parser->m_startElementHandler = start; + parser->m_endElementHandler = end; } void XMLCALL -XML_SetStartElementHandler(XML_Parser parser, - XML_StartElementHandler start) { - startElementHandler = start; +XML_SetStartElementHandler(XML_Parser parser, XML_StartElementHandler start) { + if (parser != NULL) + parser->m_startElementHandler = start; } void XMLCALL -XML_SetEndElementHandler(XML_Parser parser, - XML_EndElementHandler end) { - endElementHandler = end; +XML_SetEndElementHandler(XML_Parser parser, XML_EndElementHandler end) { + if (parser != NULL) + parser->m_endElementHandler = end; } void XMLCALL XML_SetCharacterDataHandler(XML_Parser parser, - XML_CharacterDataHandler handler) -{ - characterDataHandler = handler; + XML_CharacterDataHandler handler) { + if (parser != NULL) + parser->m_characterDataHandler = handler; } void XMLCALL XML_SetProcessingInstructionHandler(XML_Parser parser, - XML_ProcessingInstructionHandler handler) -{ - processingInstructionHandler = handler; + XML_ProcessingInstructionHandler handler) { + if (parser != NULL) + parser->m_processingInstructionHandler = handler; } void XMLCALL -XML_SetCommentHandler(XML_Parser parser, - XML_CommentHandler handler) -{ - commentHandler = handler; +XML_SetCommentHandler(XML_Parser parser, XML_CommentHandler handler) { + if (parser != NULL) + parser->m_commentHandler = handler; } void XMLCALL XML_SetCdataSectionHandler(XML_Parser parser, XML_StartCdataSectionHandler start, - XML_EndCdataSectionHandler end) -{ - startCdataSectionHandler = start; - endCdataSectionHandler = end; + XML_EndCdataSectionHandler end) { + if (parser == NULL) + return; + parser->m_startCdataSectionHandler = start; + parser->m_endCdataSectionHandler = end; } void XMLCALL XML_SetStartCdataSectionHandler(XML_Parser parser, XML_StartCdataSectionHandler start) { - startCdataSectionHandler = start; + if (parser != NULL) + parser->m_startCdataSectionHandler = start; } void XMLCALL XML_SetEndCdataSectionHandler(XML_Parser parser, XML_EndCdataSectionHandler end) { - endCdataSectionHandler = end; + if (parser != NULL) + parser->m_endCdataSectionHandler = end; } void XMLCALL -XML_SetDefaultHandler(XML_Parser parser, - XML_DefaultHandler handler) -{ - defaultHandler = handler; - defaultExpandInternalEntities = XML_FALSE; +XML_SetDefaultHandler(XML_Parser parser, XML_DefaultHandler handler) { + if (parser == NULL) + return; + parser->m_defaultHandler = handler; + parser->m_defaultExpandInternalEntities = XML_FALSE; } void XMLCALL -XML_SetDefaultHandlerExpand(XML_Parser parser, - XML_DefaultHandler handler) -{ - defaultHandler = handler; - defaultExpandInternalEntities = XML_TRUE; +XML_SetDefaultHandlerExpand(XML_Parser parser, XML_DefaultHandler handler) { + if (parser == NULL) + return; + parser->m_defaultHandler = handler; + parser->m_defaultExpandInternalEntities = XML_TRUE; } void XMLCALL -XML_SetDoctypeDeclHandler(XML_Parser parser, - XML_StartDoctypeDeclHandler start, - XML_EndDoctypeDeclHandler end) -{ - startDoctypeDeclHandler = start; - endDoctypeDeclHandler = end; +XML_SetDoctypeDeclHandler(XML_Parser parser, XML_StartDoctypeDeclHandler start, + XML_EndDoctypeDeclHandler end) { + if (parser == NULL) + return; + parser->m_startDoctypeDeclHandler = start; + parser->m_endDoctypeDeclHandler = end; } void XMLCALL XML_SetStartDoctypeDeclHandler(XML_Parser parser, XML_StartDoctypeDeclHandler start) { - startDoctypeDeclHandler = start; + if (parser != NULL) + parser->m_startDoctypeDeclHandler = start; } void XMLCALL -XML_SetEndDoctypeDeclHandler(XML_Parser parser, - XML_EndDoctypeDeclHandler end) { - endDoctypeDeclHandler = end; +XML_SetEndDoctypeDeclHandler(XML_Parser parser, XML_EndDoctypeDeclHandler end) { + if (parser != NULL) + parser->m_endDoctypeDeclHandler = end; } void XMLCALL XML_SetUnparsedEntityDeclHandler(XML_Parser parser, - XML_UnparsedEntityDeclHandler handler) -{ - unparsedEntityDeclHandler = handler; + XML_UnparsedEntityDeclHandler handler) { + if (parser != NULL) + parser->m_unparsedEntityDeclHandler = handler; } void XMLCALL -XML_SetNotationDeclHandler(XML_Parser parser, - XML_NotationDeclHandler handler) -{ - notationDeclHandler = handler; +XML_SetNotationDeclHandler(XML_Parser parser, XML_NotationDeclHandler handler) { + if (parser != NULL) + parser->m_notationDeclHandler = handler; } void XMLCALL XML_SetNamespaceDeclHandler(XML_Parser parser, XML_StartNamespaceDeclHandler start, - XML_EndNamespaceDeclHandler end) -{ - startNamespaceDeclHandler = start; - endNamespaceDeclHandler = end; + XML_EndNamespaceDeclHandler end) { + if (parser == NULL) + return; + parser->m_startNamespaceDeclHandler = start; + parser->m_endNamespaceDeclHandler = end; } void XMLCALL XML_SetStartNamespaceDeclHandler(XML_Parser parser, XML_StartNamespaceDeclHandler start) { - startNamespaceDeclHandler = start; + if (parser != NULL) + parser->m_startNamespaceDeclHandler = start; } void XMLCALL XML_SetEndNamespaceDeclHandler(XML_Parser parser, XML_EndNamespaceDeclHandler end) { - endNamespaceDeclHandler = end; + if (parser != NULL) + parser->m_endNamespaceDeclHandler = end; } void XMLCALL XML_SetNotStandaloneHandler(XML_Parser parser, - XML_NotStandaloneHandler handler) -{ - notStandaloneHandler = handler; + XML_NotStandaloneHandler handler) { + if (parser != NULL) + parser->m_notStandaloneHandler = handler; } void XMLCALL XML_SetExternalEntityRefHandler(XML_Parser parser, - XML_ExternalEntityRefHandler handler) -{ - externalEntityRefHandler = handler; + XML_ExternalEntityRefHandler handler) { + if (parser != NULL) + parser->m_externalEntityRefHandler = handler; } void XMLCALL -XML_SetExternalEntityRefHandlerArg(XML_Parser parser, void *arg) -{ +XML_SetExternalEntityRefHandlerArg(XML_Parser parser, void *arg) { + if (parser == NULL) + return; if (arg) - externalEntityRefHandlerArg = (XML_Parser)arg; + parser->m_externalEntityRefHandlerArg = (XML_Parser)arg; else - externalEntityRefHandlerArg = parser; + parser->m_externalEntityRefHandlerArg = parser; } void XMLCALL XML_SetSkippedEntityHandler(XML_Parser parser, - XML_SkippedEntityHandler handler) -{ - skippedEntityHandler = handler; + XML_SkippedEntityHandler handler) { + if (parser != NULL) + parser->m_skippedEntityHandler = handler; } void XMLCALL XML_SetUnknownEncodingHandler(XML_Parser parser, - XML_UnknownEncodingHandler handler, - void *data) -{ - unknownEncodingHandler = handler; - unknownEncodingHandlerData = data; + XML_UnknownEncodingHandler handler, void *data) { + if (parser == NULL) + return; + parser->m_unknownEncodingHandler = handler; + parser->m_unknownEncodingHandlerData = data; } void XMLCALL -XML_SetElementDeclHandler(XML_Parser parser, - XML_ElementDeclHandler eldecl) -{ - elementDeclHandler = eldecl; +XML_SetElementDeclHandler(XML_Parser parser, XML_ElementDeclHandler eldecl) { + if (parser != NULL) + parser->m_elementDeclHandler = eldecl; } void XMLCALL -XML_SetAttlistDeclHandler(XML_Parser parser, - XML_AttlistDeclHandler attdecl) -{ - attlistDeclHandler = attdecl; +XML_SetAttlistDeclHandler(XML_Parser parser, XML_AttlistDeclHandler attdecl) { + if (parser != NULL) + parser->m_attlistDeclHandler = attdecl; } void XMLCALL -XML_SetEntityDeclHandler(XML_Parser parser, - XML_EntityDeclHandler handler) -{ - entityDeclHandler = handler; +XML_SetEntityDeclHandler(XML_Parser parser, XML_EntityDeclHandler handler) { + if (parser != NULL) + parser->m_entityDeclHandler = handler; } void XMLCALL -XML_SetXmlDeclHandler(XML_Parser parser, - XML_XmlDeclHandler handler) { - xmlDeclHandler = handler; +XML_SetXmlDeclHandler(XML_Parser parser, XML_XmlDeclHandler handler) { + if (parser != NULL) + parser->m_xmlDeclHandler = handler; } int XMLCALL XML_SetParamEntityParsing(XML_Parser parser, - enum XML_ParamEntityParsing peParsing) -{ + enum XML_ParamEntityParsing peParsing) { + if (parser == NULL) + return 0; /* block after XML_Parse()/XML_ParseBuffer() has been called */ - if (ps_parsing == XML_PARSING || ps_parsing == XML_SUSPENDED) + if (parserBusy(parser)) return 0; #ifdef XML_DTD - paramEntityParsing = peParsing; + parser->m_paramEntityParsing = peParsing; return 1; #else return peParsing == XML_PARAM_ENTITY_PARSING_NEVER; #endif } +int XMLCALL +XML_SetHashSalt(XML_Parser parser, unsigned long hash_salt) { + if (parser == NULL) + return 0; + + const XML_Parser rootParser = getRootParserOf(parser, NULL); + assert(! rootParser->m_parentParser); + + /* block after XML_Parse()/XML_ParseBuffer() has been called */ + if (parserBusy(rootParser)) + return 0; + rootParser->m_hash_secret_salt = hash_salt; + return 1; +} + enum XML_Status XMLCALL -XML_Parse(XML_Parser parser, const char *s, int len, int isFinal) -{ - switch (ps_parsing) { +XML_Parse(XML_Parser parser, const char *s, int len, int isFinal) { + if ((parser == NULL) || (len < 0) || ((s == NULL) && (len != 0))) { + if (parser != NULL) + parser->m_errorCode = XML_ERROR_INVALID_ARGUMENT; + return XML_STATUS_ERROR; + } + switch (parser->m_parsingStatus.parsing) { case XML_SUSPENDED: - errorCode = XML_ERROR_SUSPENDED; + parser->m_errorCode = XML_ERROR_SUSPENDED; return XML_STATUS_ERROR; case XML_FINISHED: - errorCode = XML_ERROR_FINISHED; + parser->m_errorCode = XML_ERROR_FINISHED; return XML_STATUS_ERROR; + case XML_INITIALIZED: + if (parser->m_parentParser == NULL && ! startParsing(parser)) { + parser->m_errorCode = XML_ERROR_NO_MEMORY; + return XML_STATUS_ERROR; + } + /* fall through */ default: - ps_parsing = XML_PARSING; + parser->m_parsingStatus.parsing = XML_PARSING; } - if (len == 0) { - ps_finalBuffer = (XML_Bool)isFinal; - if (!isFinal) - return XML_STATUS_OK; - positionPtr = bufferPtr; - parseEndPtr = bufferEnd; - - /* If data are left over from last buffer, and we now know that these - data are the final chunk of input, then we have to check them again - to detect errors based on that fact. - */ - errorCode = processor(parser, bufferPtr, parseEndPtr, &bufferPtr); - - if (errorCode == XML_ERROR_NONE) { - switch (ps_parsing) { - case XML_SUSPENDED: - XmlUpdatePosition(encoding, positionPtr, bufferPtr, &position); - positionPtr = bufferPtr; - return XML_STATUS_SUSPENDED; - case XML_INITIALIZED: - case XML_PARSING: - ps_parsing = XML_FINISHED; - /* fall through */ - default: - return XML_STATUS_OK; - } - } - eventEndPtr = eventPtr; - processor = errorProcessor; - return XML_STATUS_ERROR; - } -#ifndef XML_CONTEXT_BYTES - else if (bufferPtr == bufferEnd) { +#if XML_CONTEXT_BYTES == 0 + if (parser->m_bufferPtr == parser->m_bufferEnd) { const char *end; int nLeftOver; - enum XML_Error result; - parseEndByteIndex += len; - positionPtr = s; - ps_finalBuffer = (XML_Bool)isFinal; - - errorCode = processor(parser, s, parseEndPtr = s + len, &end); - - if (errorCode != XML_ERROR_NONE) { - eventEndPtr = eventPtr; - processor = errorProcessor; + enum XML_Status result; + /* Detect overflow (a+b > MAX <==> b > MAX-a) */ + if ((XML_Size)len > ((XML_Size)-1) / 2 - parser->m_parseEndByteIndex) { + parser->m_errorCode = XML_ERROR_NO_MEMORY; + parser->m_eventPtr = parser->m_eventEndPtr = NULL; + parser->m_processor = errorProcessor; return XML_STATUS_ERROR; } - else { - switch (ps_parsing) { + // though this isn't a buffer request, we assume that `len` is the app's + // preferred buffer fill size, and therefore save it here. + parser->m_lastBufferRequestSize = len; + parser->m_parseEndByteIndex += len; + parser->m_positionPtr = s; + parser->m_parsingStatus.finalBuffer = (XML_Bool)isFinal; + + parser->m_errorCode + = callProcessor(parser, s, parser->m_parseEndPtr = s + len, &end); + + if (parser->m_errorCode != XML_ERROR_NONE) { + parser->m_eventEndPtr = parser->m_eventPtr; + parser->m_processor = errorProcessor; + return XML_STATUS_ERROR; + } else { + switch (parser->m_parsingStatus.parsing) { case XML_SUSPENDED: result = XML_STATUS_SUSPENDED; break; case XML_INITIALIZED: case XML_PARSING: - result = XML_STATUS_OK; if (isFinal) { - ps_parsing = XML_FINISHED; - return result; + parser->m_parsingStatus.parsing = XML_FINISHED; + return XML_STATUS_OK; } + /* fall through */ + default: + result = XML_STATUS_OK; } } - XmlUpdatePosition(encoding, positionPtr, end, &position); + XmlUpdatePosition(parser->m_encoding, parser->m_positionPtr, end, + &parser->m_position); nLeftOver = s + len - end; if (nLeftOver) { - if (buffer == NULL || nLeftOver > bufferLim - buffer) { - /* FIXME avoid integer overflow */ - char *temp; - temp = (buffer == NULL - ? (char *)MALLOC(len * 2) - : (char *)REALLOC(buffer, len * 2)); - if (temp == NULL) { - errorCode = XML_ERROR_NO_MEMORY; - return XML_STATUS_ERROR; - } - buffer = temp; - if (!buffer) { - errorCode = XML_ERROR_NO_MEMORY; - eventPtr = eventEndPtr = NULL; - processor = errorProcessor; - return XML_STATUS_ERROR; - } - bufferLim = buffer + len * 2; + // Back up and restore the parsing status to avoid XML_ERROR_SUSPENDED + // (and XML_ERROR_FINISHED) from XML_GetBuffer. + const enum XML_Parsing originalStatus = parser->m_parsingStatus.parsing; + parser->m_parsingStatus.parsing = XML_PARSING; + void *const temp = XML_GetBuffer(parser, nLeftOver); + parser->m_parsingStatus.parsing = originalStatus; + // GetBuffer may have overwritten this, but we want to remember what the + // app requested, not how many bytes were left over after parsing. + parser->m_lastBufferRequestSize = len; + if (temp == NULL) { + // NOTE: parser->m_errorCode has already been set by XML_GetBuffer(). + parser->m_eventPtr = parser->m_eventEndPtr = NULL; + parser->m_processor = errorProcessor; + return XML_STATUS_ERROR; } - memcpy(buffer, end, nLeftOver); + // Since we know that the buffer was empty and XML_CONTEXT_BYTES is 0, we + // don't have any data to preserve, and can copy straight into the start + // of the buffer rather than the GetBuffer return pointer (which may be + // pointing further into the allocated buffer). + memcpy(parser->m_buffer, end, nLeftOver); } - bufferPtr = buffer; - bufferEnd = buffer + nLeftOver; - positionPtr = bufferPtr; - parseEndPtr = bufferEnd; - eventPtr = bufferPtr; - eventEndPtr = bufferPtr; + parser->m_bufferPtr = parser->m_buffer; + parser->m_bufferEnd = parser->m_buffer + nLeftOver; + parser->m_positionPtr = parser->m_bufferPtr; + parser->m_parseEndPtr = parser->m_bufferEnd; + parser->m_eventPtr = parser->m_bufferPtr; + parser->m_eventEndPtr = parser->m_bufferPtr; return result; } -#endif /* not defined XML_CONTEXT_BYTES */ - else { - void *buff = XML_GetBuffer(parser, len); - if (buff == NULL) - return XML_STATUS_ERROR; - else { - memcpy(buff, s, len); - return XML_ParseBuffer(parser, len, isFinal); - } +#endif /* XML_CONTEXT_BYTES == 0 */ + void *buff = XML_GetBuffer(parser, len); + if (buff == NULL) + return XML_STATUS_ERROR; + if (len > 0) { + assert(s != NULL); // make sure s==NULL && len!=0 was rejected above + memcpy(buff, s, len); } + return XML_ParseBuffer(parser, len, isFinal); } enum XML_Status XMLCALL -XML_ParseBuffer(XML_Parser parser, int len, int isFinal) -{ +XML_ParseBuffer(XML_Parser parser, int len, int isFinal) { const char *start; enum XML_Status result = XML_STATUS_OK; - switch (ps_parsing) { + if (parser == NULL) + return XML_STATUS_ERROR; + + if (len < 0) { + parser->m_errorCode = XML_ERROR_INVALID_ARGUMENT; + return XML_STATUS_ERROR; + } + + switch (parser->m_parsingStatus.parsing) { case XML_SUSPENDED: - errorCode = XML_ERROR_SUSPENDED; + parser->m_errorCode = XML_ERROR_SUSPENDED; return XML_STATUS_ERROR; case XML_FINISHED: - errorCode = XML_ERROR_FINISHED; + parser->m_errorCode = XML_ERROR_FINISHED; return XML_STATUS_ERROR; + case XML_INITIALIZED: + /* Has someone called XML_GetBuffer successfully before? */ + if (! parser->m_bufferPtr) { + parser->m_errorCode = XML_ERROR_NO_BUFFER; + return XML_STATUS_ERROR; + } + + if (parser->m_parentParser == NULL && ! startParsing(parser)) { + parser->m_errorCode = XML_ERROR_NO_MEMORY; + return XML_STATUS_ERROR; + } + /* fall through */ default: - ps_parsing = XML_PARSING; + parser->m_parsingStatus.parsing = XML_PARSING; } - start = bufferPtr; - positionPtr = start; - bufferEnd += len; - parseEndPtr = bufferEnd; - parseEndByteIndex += len; - ps_finalBuffer = (XML_Bool)isFinal; + start = parser->m_bufferPtr; + parser->m_positionPtr = start; + parser->m_bufferEnd += len; + parser->m_parseEndPtr = parser->m_bufferEnd; + parser->m_parseEndByteIndex += len; + parser->m_parsingStatus.finalBuffer = (XML_Bool)isFinal; - errorCode = processor(parser, start, parseEndPtr, &bufferPtr); + parser->m_errorCode = callProcessor(parser, start, parser->m_parseEndPtr, + &parser->m_bufferPtr); - if (errorCode != XML_ERROR_NONE) { - eventEndPtr = eventPtr; - processor = errorProcessor; + if (parser->m_errorCode != XML_ERROR_NONE) { + parser->m_eventEndPtr = parser->m_eventPtr; + parser->m_processor = errorProcessor; return XML_STATUS_ERROR; - } - else { - switch (ps_parsing) { + } else { + switch (parser->m_parsingStatus.parsing) { case XML_SUSPENDED: result = XML_STATUS_SUSPENDED; break; - case XML_INITIALIZED: + case XML_INITIALIZED: case XML_PARSING: if (isFinal) { - ps_parsing = XML_FINISHED; + parser->m_parsingStatus.parsing = XML_FINISHED; return result; } - default: ; /* should not happen */ + default:; /* should not happen */ } } - XmlUpdatePosition(encoding, positionPtr, bufferPtr, &position); - positionPtr = bufferPtr; + XmlUpdatePosition(parser->m_encoding, parser->m_positionPtr, + parser->m_bufferPtr, &parser->m_position); + parser->m_positionPtr = parser->m_bufferPtr; return result; } -void * XMLCALL -XML_GetBuffer(XML_Parser parser, int len) -{ - switch (ps_parsing) { +void *XMLCALL +XML_GetBuffer(XML_Parser parser, int len) { + if (parser == NULL) + return NULL; + if (len < 0) { + parser->m_errorCode = XML_ERROR_NO_MEMORY; + return NULL; + } + switch (parser->m_parsingStatus.parsing) { case XML_SUSPENDED: - errorCode = XML_ERROR_SUSPENDED; + parser->m_errorCode = XML_ERROR_SUSPENDED; return NULL; case XML_FINISHED: - errorCode = XML_ERROR_FINISHED; + parser->m_errorCode = XML_ERROR_FINISHED; return NULL; - default: ; - } - - if (len > bufferLim - bufferEnd) { - /* FIXME avoid integer overflow */ - int neededSize = len + (int)(bufferEnd - bufferPtr); -#ifdef XML_CONTEXT_BYTES - int keep = (int)(bufferPtr - buffer); - + default:; + } + + // whether or not the request succeeds, `len` seems to be the app's preferred + // buffer fill size; remember it. + parser->m_lastBufferRequestSize = len; + if (len > EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_bufferEnd) + || parser->m_buffer == NULL) { +#if XML_CONTEXT_BYTES > 0 + int keep; +#endif /* XML_CONTEXT_BYTES > 0 */ + /* Do not invoke signed arithmetic overflow: */ + int neededSize = (int)((unsigned)len + + (unsigned)EXPAT_SAFE_PTR_DIFF( + parser->m_bufferEnd, parser->m_bufferPtr)); + if (neededSize < 0) { + parser->m_errorCode = XML_ERROR_NO_MEMORY; + return NULL; + } +#if XML_CONTEXT_BYTES > 0 + keep = (int)EXPAT_SAFE_PTR_DIFF(parser->m_bufferPtr, parser->m_buffer); if (keep > XML_CONTEXT_BYTES) keep = XML_CONTEXT_BYTES; + /* Detect and prevent integer overflow */ + if (keep > INT_MAX - neededSize) { + parser->m_errorCode = XML_ERROR_NO_MEMORY; + return NULL; + } neededSize += keep; -#endif /* defined XML_CONTEXT_BYTES */ - if (neededSize <= bufferLim - buffer) { -#ifdef XML_CONTEXT_BYTES - if (keep < bufferPtr - buffer) { - int offset = (int)(bufferPtr - buffer) - keep; - memmove(buffer, &buffer[offset], bufferEnd - bufferPtr + keep); - bufferEnd -= offset; - bufferPtr -= offset; +#endif /* XML_CONTEXT_BYTES > 0 */ + if (parser->m_buffer && parser->m_bufferPtr + && neededSize + <= EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_buffer)) { +#if XML_CONTEXT_BYTES > 0 + if (keep < EXPAT_SAFE_PTR_DIFF(parser->m_bufferPtr, parser->m_buffer)) { + int offset + = (int)EXPAT_SAFE_PTR_DIFF(parser->m_bufferPtr, parser->m_buffer) + - keep; + /* The buffer pointers cannot be NULL here; we have at least some bytes + * in the buffer */ + memmove(parser->m_buffer, &parser->m_buffer[offset], + parser->m_bufferEnd - parser->m_bufferPtr + keep); + parser->m_bufferEnd -= offset; + parser->m_bufferPtr -= offset; } #else - memmove(buffer, bufferPtr, bufferEnd - bufferPtr); - bufferEnd = buffer + (bufferEnd - bufferPtr); - bufferPtr = buffer; -#endif /* not defined XML_CONTEXT_BYTES */ - } - else { + memmove(parser->m_buffer, parser->m_bufferPtr, + EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr)); + parser->m_bufferEnd + = parser->m_buffer + + EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr); + parser->m_bufferPtr = parser->m_buffer; +#endif /* XML_CONTEXT_BYTES > 0 */ + } else { char *newBuf; - int bufferSize = (int)(bufferLim - bufferPtr); + int bufferSize + = (int)EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_buffer); if (bufferSize == 0) bufferSize = INIT_BUFFER_SIZE; do { - bufferSize *= 2; - } while (bufferSize < neededSize); - newBuf = (char *)MALLOC(bufferSize); - if (newBuf == 0) { - errorCode = XML_ERROR_NO_MEMORY; + /* Do not invoke signed arithmetic overflow: */ + bufferSize = (int)(2U * (unsigned)bufferSize); + } while (bufferSize < neededSize && bufferSize > 0); + if (bufferSize <= 0) { + parser->m_errorCode = XML_ERROR_NO_MEMORY; return NULL; } - bufferLim = newBuf + bufferSize; -#ifdef XML_CONTEXT_BYTES - if (bufferPtr) { - int keep = (int)(bufferPtr - buffer); - if (keep > XML_CONTEXT_BYTES) - keep = XML_CONTEXT_BYTES; - memcpy(newBuf, &bufferPtr[-keep], bufferEnd - bufferPtr + keep); - FREE(buffer); - buffer = newBuf; - bufferEnd = buffer + (bufferEnd - bufferPtr) + keep; - bufferPtr = buffer + keep; + // NOTE: We are avoiding MALLOC(..) here to leave limiting + // the input size to the application using Expat. + newBuf = parser->m_mem.malloc_fcn(bufferSize); + if (newBuf == 0) { + parser->m_errorCode = XML_ERROR_NO_MEMORY; + return NULL; } - else { - bufferEnd = newBuf + (bufferEnd - bufferPtr); - bufferPtr = buffer = newBuf; + parser->m_bufferLim = newBuf + bufferSize; +#if XML_CONTEXT_BYTES > 0 + if (parser->m_bufferPtr) { + memcpy(newBuf, &parser->m_bufferPtr[-keep], + EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr) + + keep); + // NOTE: We are avoiding FREE(..) here because parser->m_buffer + // is not being allocated with MALLOC(..) but with plain + // .malloc_fcn(..). + parser->m_mem.free_fcn(parser->m_buffer); + parser->m_buffer = newBuf; + parser->m_bufferEnd + = parser->m_buffer + + EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr) + + keep; + parser->m_bufferPtr = parser->m_buffer + keep; + } else { + /* This must be a brand new buffer with no data in it yet */ + parser->m_bufferEnd = newBuf; + parser->m_bufferPtr = parser->m_buffer = newBuf; } #else - if (bufferPtr) { - memcpy(newBuf, bufferPtr, bufferEnd - bufferPtr); - FREE(buffer); + if (parser->m_bufferPtr) { + memcpy(newBuf, parser->m_bufferPtr, + EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr)); + // NOTE: We are avoiding FREE(..) here because parser->m_buffer + // is not being allocated with MALLOC(..) but with plain + // .malloc_fcn(..). + parser->m_mem.free_fcn(parser->m_buffer); + parser->m_bufferEnd + = newBuf + + EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr); + } else { + /* This must be a brand new buffer with no data in it yet */ + parser->m_bufferEnd = newBuf; } - bufferEnd = newBuf + (bufferEnd - bufferPtr); - bufferPtr = buffer = newBuf; -#endif /* not defined XML_CONTEXT_BYTES */ + parser->m_bufferPtr = parser->m_buffer = newBuf; +#endif /* XML_CONTEXT_BYTES > 0 */ } + parser->m_eventPtr = parser->m_eventEndPtr = NULL; + parser->m_positionPtr = NULL; } - return bufferEnd; + return parser->m_bufferEnd; +} + +static void +triggerReenter(XML_Parser parser) { + parser->m_reenter = XML_TRUE; } enum XML_Status XMLCALL -XML_StopParser(XML_Parser parser, XML_Bool resumable) -{ - switch (ps_parsing) { +XML_StopParser(XML_Parser parser, XML_Bool resumable) { + if (parser == NULL) + return XML_STATUS_ERROR; + switch (parser->m_parsingStatus.parsing) { + case XML_INITIALIZED: + parser->m_errorCode = XML_ERROR_NOT_STARTED; + return XML_STATUS_ERROR; case XML_SUSPENDED: if (resumable) { - errorCode = XML_ERROR_SUSPENDED; + parser->m_errorCode = XML_ERROR_SUSPENDED; return XML_STATUS_ERROR; } - ps_parsing = XML_FINISHED; + parser->m_parsingStatus.parsing = XML_FINISHED; break; case XML_FINISHED: - errorCode = XML_ERROR_FINISHED; + parser->m_errorCode = XML_ERROR_FINISHED; return XML_STATUS_ERROR; - default: + case XML_PARSING: if (resumable) { #ifdef XML_DTD - if (isParamEntity) { - errorCode = XML_ERROR_SUSPEND_PE; + if (parser->m_isParamEntity) { + parser->m_errorCode = XML_ERROR_SUSPEND_PE; return XML_STATUS_ERROR; } #endif - ps_parsing = XML_SUSPENDED; - } - else - ps_parsing = XML_FINISHED; + parser->m_parsingStatus.parsing = XML_SUSPENDED; + } else + parser->m_parsingStatus.parsing = XML_FINISHED; + break; + default: + assert(0); } return XML_STATUS_OK; } enum XML_Status XMLCALL -XML_ResumeParser(XML_Parser parser) -{ +XML_ResumeParser(XML_Parser parser) { enum XML_Status result = XML_STATUS_OK; - if (ps_parsing != XML_SUSPENDED) { - errorCode = XML_ERROR_NOT_SUSPENDED; + if (parser == NULL) + return XML_STATUS_ERROR; + if (parser->m_parsingStatus.parsing != XML_SUSPENDED) { + parser->m_errorCode = XML_ERROR_NOT_SUSPENDED; return XML_STATUS_ERROR; } - ps_parsing = XML_PARSING; + parser->m_parsingStatus.parsing = XML_PARSING; - errorCode = processor(parser, bufferPtr, parseEndPtr, &bufferPtr); + parser->m_errorCode = callProcessor( + parser, parser->m_bufferPtr, parser->m_parseEndPtr, &parser->m_bufferPtr); - if (errorCode != XML_ERROR_NONE) { - eventEndPtr = eventPtr; - processor = errorProcessor; + if (parser->m_errorCode != XML_ERROR_NONE) { + parser->m_eventEndPtr = parser->m_eventPtr; + parser->m_processor = errorProcessor; return XML_STATUS_ERROR; - } - else { - switch (ps_parsing) { + } else { + switch (parser->m_parsingStatus.parsing) { case XML_SUSPENDED: result = XML_STATUS_SUSPENDED; break; - case XML_INITIALIZED: + case XML_INITIALIZED: case XML_PARSING: - if (ps_finalBuffer) { - ps_parsing = XML_FINISHED; + if (parser->m_parsingStatus.finalBuffer) { + parser->m_parsingStatus.parsing = XML_FINISHED; return result; } - default: ; + default:; } } - XmlUpdatePosition(encoding, positionPtr, bufferPtr, &position); - positionPtr = bufferPtr; + XmlUpdatePosition(parser->m_encoding, parser->m_positionPtr, + parser->m_bufferPtr, &parser->m_position); + parser->m_positionPtr = parser->m_bufferPtr; return result; } void XMLCALL -XML_GetParsingStatus(XML_Parser parser, XML_ParsingStatus *status) -{ +XML_GetParsingStatus(XML_Parser parser, XML_ParsingStatus *status) { + if (parser == NULL) + return; assert(status != NULL); *status = parser->m_parsingStatus; } enum XML_Error XMLCALL -XML_GetErrorCode(XML_Parser parser) -{ - return errorCode; +XML_GetErrorCode(XML_Parser parser) { + if (parser == NULL) + return XML_ERROR_INVALID_ARGUMENT; + return parser->m_errorCode; } XML_Index XMLCALL -XML_GetCurrentByteIndex(XML_Parser parser) -{ - if (eventPtr) - return parseEndByteIndex - (parseEndPtr - eventPtr); +XML_GetCurrentByteIndex(XML_Parser parser) { + if (parser == NULL) + return -1; + if (parser->m_eventPtr) + return (XML_Index)(parser->m_parseEndByteIndex + - (parser->m_parseEndPtr - parser->m_eventPtr)); return -1; } int XMLCALL -XML_GetCurrentByteCount(XML_Parser parser) -{ - if (eventEndPtr && eventPtr) - return (int)(eventEndPtr - eventPtr); +XML_GetCurrentByteCount(XML_Parser parser) { + if (parser == NULL) + return 0; + if (parser->m_eventEndPtr && parser->m_eventPtr) + return (int)(parser->m_eventEndPtr - parser->m_eventPtr); return 0; } -const char * XMLCALL -XML_GetInputContext(XML_Parser parser, int *offset, int *size) -{ -#ifdef XML_CONTEXT_BYTES - if (eventPtr && buffer) { - *offset = (int)(eventPtr - buffer); - *size = (int)(bufferEnd - buffer); - return buffer; +const char *XMLCALL +XML_GetInputContext(XML_Parser parser, int *offset, int *size) { +#if XML_CONTEXT_BYTES > 0 + if (parser == NULL) + return NULL; + if (parser->m_eventPtr && parser->m_buffer) { + if (offset != NULL) + *offset = (int)(parser->m_eventPtr - parser->m_buffer); + if (size != NULL) + *size = (int)(parser->m_bufferEnd - parser->m_buffer); + return parser->m_buffer; } -#endif /* defined XML_CONTEXT_BYTES */ - return (char *) 0; +#else + (void)parser; + (void)offset; + (void)size; +#endif /* XML_CONTEXT_BYTES > 0 */ + return (const char *)0; } XML_Size XMLCALL -XML_GetCurrentLineNumber(XML_Parser parser) -{ - if (eventPtr && eventPtr >= positionPtr) { - XmlUpdatePosition(encoding, positionPtr, eventPtr, &position); - positionPtr = eventPtr; +XML_GetCurrentLineNumber(XML_Parser parser) { + if (parser == NULL) + return 0; + if (parser->m_eventPtr && parser->m_eventPtr >= parser->m_positionPtr) { + XmlUpdatePosition(parser->m_encoding, parser->m_positionPtr, + parser->m_eventPtr, &parser->m_position); + parser->m_positionPtr = parser->m_eventPtr; } - return position.lineNumber + 1; + return parser->m_position.lineNumber + 1; } XML_Size XMLCALL -XML_GetCurrentColumnNumber(XML_Parser parser) -{ - if (eventPtr && eventPtr >= positionPtr) { - XmlUpdatePosition(encoding, positionPtr, eventPtr, &position); - positionPtr = eventPtr; +XML_GetCurrentColumnNumber(XML_Parser parser) { + if (parser == NULL) + return 0; + if (parser->m_eventPtr && parser->m_eventPtr >= parser->m_positionPtr) { + XmlUpdatePosition(parser->m_encoding, parser->m_positionPtr, + parser->m_eventPtr, &parser->m_position); + parser->m_positionPtr = parser->m_eventPtr; } - return position.columnNumber; + return parser->m_position.columnNumber; } void XMLCALL -XML_FreeContentModel(XML_Parser parser, XML_Content *model) -{ - FREE(model); +XML_FreeContentModel(XML_Parser parser, XML_Content *model) { + if (parser == NULL) + return; + + // NOTE: We are avoiding FREE(..) here because the content model + // has been created using plain .malloc_fcn(..) rather than MALLOC(..). + parser->m_mem.free_fcn(model); } -void * XMLCALL -XML_MemMalloc(XML_Parser parser, size_t size) -{ - return MALLOC(size); +void *XMLCALL +XML_MemMalloc(XML_Parser parser, size_t size) { + if (parser == NULL) + return NULL; + + // NOTE: We are avoiding MALLOC(..) here to not include + // user allocations with allocation tracking and limiting. + return parser->m_mem.malloc_fcn(size); } -void * XMLCALL -XML_MemRealloc(XML_Parser parser, void *ptr, size_t size) -{ - return REALLOC(ptr, size); +void *XMLCALL +XML_MemRealloc(XML_Parser parser, void *ptr, size_t size) { + if (parser == NULL) + return NULL; + + // NOTE: We are avoiding REALLOC(..) here to not include + // user allocations with allocation tracking and limiting. + return parser->m_mem.realloc_fcn(ptr, size); } void XMLCALL -XML_MemFree(XML_Parser parser, void *ptr) -{ - FREE(ptr); +XML_MemFree(XML_Parser parser, void *ptr) { + if (parser == NULL) + return; + + // NOTE: We are avoiding FREE(..) here because XML_MemMalloc and + // XML_MemRealloc are not using MALLOC(..) and REALLOC(..) + // but plain .malloc_fcn(..) and .realloc_fcn(..), internally. + parser->m_mem.free_fcn(ptr); } void XMLCALL -XML_DefaultCurrent(XML_Parser parser) -{ - if (defaultHandler) { - if (openInternalEntities) - reportDefault(parser, - internalEncoding, - openInternalEntities->internalEventPtr, - openInternalEntities->internalEventEndPtr); +XML_DefaultCurrent(XML_Parser parser) { + if (parser == NULL) + return; + if (parser->m_defaultHandler) { + if (parser->m_openInternalEntities) + reportDefault(parser, parser->m_internalEncoding, + parser->m_openInternalEntities->internalEventPtr, + parser->m_openInternalEntities->internalEventEndPtr); else - reportDefault(parser, encoding, eventPtr, eventEndPtr); - } -} - -const XML_LChar * XMLCALL -XML_ErrorString(enum XML_Error code) -{ - static const XML_LChar* const message[] = { - 0, - XML_L("out of memory"), - XML_L("syntax error"), - XML_L("no element found"), - XML_L("not well-formed (invalid token)"), - XML_L("unclosed token"), - XML_L("partial character"), - XML_L("mismatched tag"), - XML_L("duplicate attribute"), - XML_L("junk after document element"), - XML_L("illegal parameter entity reference"), - XML_L("undefined entity"), - XML_L("recursive entity reference"), - XML_L("asynchronous entity"), - XML_L("reference to invalid character number"), - XML_L("reference to binary entity"), - XML_L("reference to external entity in attribute"), - XML_L("XML or text declaration not at start of entity"), - XML_L("unknown encoding"), - XML_L("encoding specified in XML declaration is incorrect"), - XML_L("unclosed CDATA section"), - XML_L("error in processing external entity reference"), - XML_L("document is not standalone"), - XML_L("unexpected parser state - please send a bug report"), - XML_L("entity declared in parameter entity"), - XML_L("requested feature requires XML_DTD support in Expat"), - XML_L("cannot change setting once parsing has begun"), - XML_L("unbound prefix"), - XML_L("must not undeclare prefix"), - XML_L("incomplete markup in parameter entity"), - XML_L("XML declaration not well-formed"), - XML_L("text declaration not well-formed"), - XML_L("illegal character(s) in public id"), - XML_L("parser suspended"), - XML_L("parser not suspended"), - XML_L("parsing aborted"), - XML_L("parsing finished"), - XML_L("cannot suspend in external parameter entity"), - XML_L("reserved prefix (xml) must not be undeclared or bound to another namespace name"), - XML_L("reserved prefix (xmlns) must not be declared or undeclared"), - XML_L("prefix must not be bound to one of the reserved namespace names") - }; - if (code > 0 && code < sizeof(message)/sizeof(message[0])) - return message[code]; + reportDefault(parser, parser->m_encoding, parser->m_eventPtr, + parser->m_eventEndPtr); + } +} + +const XML_LChar *XMLCALL +XML_ErrorString(enum XML_Error code) { + switch (code) { + case XML_ERROR_NONE: + return NULL; + case XML_ERROR_NO_MEMORY: + return XML_L("out of memory"); + case XML_ERROR_SYNTAX: + return XML_L("syntax error"); + case XML_ERROR_NO_ELEMENTS: + return XML_L("no element found"); + case XML_ERROR_INVALID_TOKEN: + return XML_L("not well-formed (invalid token)"); + case XML_ERROR_UNCLOSED_TOKEN: + return XML_L("unclosed token"); + case XML_ERROR_PARTIAL_CHAR: + return XML_L("partial character"); + case XML_ERROR_TAG_MISMATCH: + return XML_L("mismatched tag"); + case XML_ERROR_DUPLICATE_ATTRIBUTE: + return XML_L("duplicate attribute"); + case XML_ERROR_JUNK_AFTER_DOC_ELEMENT: + return XML_L("junk after document element"); + case XML_ERROR_PARAM_ENTITY_REF: + return XML_L("illegal parameter entity reference"); + case XML_ERROR_UNDEFINED_ENTITY: + return XML_L("undefined entity"); + case XML_ERROR_RECURSIVE_ENTITY_REF: + return XML_L("recursive entity reference"); + case XML_ERROR_ASYNC_ENTITY: + return XML_L("asynchronous entity"); + case XML_ERROR_BAD_CHAR_REF: + return XML_L("reference to invalid character number"); + case XML_ERROR_BINARY_ENTITY_REF: + return XML_L("reference to binary entity"); + case XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF: + return XML_L("reference to external entity in attribute"); + case XML_ERROR_MISPLACED_XML_PI: + return XML_L("XML or text declaration not at start of entity"); + case XML_ERROR_UNKNOWN_ENCODING: + return XML_L("unknown encoding"); + case XML_ERROR_INCORRECT_ENCODING: + return XML_L("encoding specified in XML declaration is incorrect"); + case XML_ERROR_UNCLOSED_CDATA_SECTION: + return XML_L("unclosed CDATA section"); + case XML_ERROR_EXTERNAL_ENTITY_HANDLING: + return XML_L("error in processing external entity reference"); + case XML_ERROR_NOT_STANDALONE: + return XML_L("document is not standalone"); + case XML_ERROR_UNEXPECTED_STATE: + return XML_L("unexpected parser state - please send a bug report"); + case XML_ERROR_ENTITY_DECLARED_IN_PE: + return XML_L("entity declared in parameter entity"); + case XML_ERROR_FEATURE_REQUIRES_XML_DTD: + return XML_L("requested feature requires XML_DTD support in Expat"); + case XML_ERROR_CANT_CHANGE_FEATURE_ONCE_PARSING: + return XML_L("cannot change setting once parsing has begun"); + /* Added in 1.95.7. */ + case XML_ERROR_UNBOUND_PREFIX: + return XML_L("unbound prefix"); + /* Added in 1.95.8. */ + case XML_ERROR_UNDECLARING_PREFIX: + return XML_L("must not undeclare prefix"); + case XML_ERROR_INCOMPLETE_PE: + return XML_L("incomplete markup in parameter entity"); + case XML_ERROR_XML_DECL: + return XML_L("XML declaration not well-formed"); + case XML_ERROR_TEXT_DECL: + return XML_L("text declaration not well-formed"); + case XML_ERROR_PUBLICID: + return XML_L("illegal character(s) in public id"); + case XML_ERROR_SUSPENDED: + return XML_L("parser suspended"); + case XML_ERROR_NOT_SUSPENDED: + return XML_L("parser not suspended"); + case XML_ERROR_ABORTED: + return XML_L("parsing aborted"); + case XML_ERROR_FINISHED: + return XML_L("parsing finished"); + case XML_ERROR_SUSPEND_PE: + return XML_L("cannot suspend in external parameter entity"); + /* Added in 2.0.0. */ + case XML_ERROR_RESERVED_PREFIX_XML: + return XML_L( + "reserved prefix (xml) must not be undeclared or bound to another namespace name"); + case XML_ERROR_RESERVED_PREFIX_XMLNS: + return XML_L("reserved prefix (xmlns) must not be declared or undeclared"); + case XML_ERROR_RESERVED_NAMESPACE_URI: + return XML_L( + "prefix must not be bound to one of the reserved namespace names"); + /* Added in 2.2.5. */ + case XML_ERROR_INVALID_ARGUMENT: /* Constant added in 2.2.1, already */ + return XML_L("invalid argument"); + /* Added in 2.3.0. */ + case XML_ERROR_NO_BUFFER: + return XML_L( + "a successful prior call to function XML_GetBuffer is required"); + /* Added in 2.4.0. */ + case XML_ERROR_AMPLIFICATION_LIMIT_BREACH: + return XML_L( + "limit on input amplification factor (from DTD and entities) breached"); + /* Added in 2.6.4. */ + case XML_ERROR_NOT_STARTED: + return XML_L("parser not started"); + } return NULL; } -const XML_LChar * XMLCALL +const XML_LChar *XMLCALL XML_ExpatVersion(void) { - /* V1 is used to string-ize the version number. However, it would string-ize the actual version macro *names* unless we get them substituted before being passed to V1. CPP is defined to expand @@ -1906,8 +2981,8 @@ XML_ExpatVersion(void) { with the correct numerals. */ /* ### I'm assuming cpp is portable in this respect... */ -#define V1(a,b,c) XML_L(#a)XML_L(".")XML_L(#b)XML_L(".")XML_L(#c) -#define V2(a,b,c) XML_L("expat_")V1(a,b,c) +#define V1(a, b, c) XML_L(#a) XML_L(".") XML_L(#b) XML_L(".") XML_L(#c) +#define V2(a, b, c) XML_L("expat_") V1(a, b, c) return V2(XML_MAJOR_VERSION, XML_MINOR_VERSION, XML_MICRO_VERSION); @@ -1916,8 +2991,7 @@ XML_ExpatVersion(void) { } XML_Expat_Version XMLCALL -XML_ExpatVersionInfo(void) -{ +XML_ExpatVersionInfo(void) { XML_Expat_Version version; version.major = XML_MAJOR_VERSION; @@ -1927,68 +3001,149 @@ XML_ExpatVersionInfo(void) return version; } -const XML_Feature * XMLCALL -XML_GetFeatureList(void) -{ +const XML_Feature *XMLCALL +XML_GetFeatureList(void) { static const XML_Feature features[] = { - {XML_FEATURE_SIZEOF_XML_CHAR, XML_L("sizeof(XML_Char)"), - sizeof(XML_Char)}, - {XML_FEATURE_SIZEOF_XML_LCHAR, XML_L("sizeof(XML_LChar)"), - sizeof(XML_LChar)}, + {XML_FEATURE_SIZEOF_XML_CHAR, XML_L("sizeof(XML_Char)"), + sizeof(XML_Char)}, + {XML_FEATURE_SIZEOF_XML_LCHAR, XML_L("sizeof(XML_LChar)"), + sizeof(XML_LChar)}, #ifdef XML_UNICODE - {XML_FEATURE_UNICODE, XML_L("XML_UNICODE"), 0}, + {XML_FEATURE_UNICODE, XML_L("XML_UNICODE"), 0}, #endif #ifdef XML_UNICODE_WCHAR_T - {XML_FEATURE_UNICODE_WCHAR_T, XML_L("XML_UNICODE_WCHAR_T"), 0}, + {XML_FEATURE_UNICODE_WCHAR_T, XML_L("XML_UNICODE_WCHAR_T"), 0}, #endif #ifdef XML_DTD - {XML_FEATURE_DTD, XML_L("XML_DTD"), 0}, + {XML_FEATURE_DTD, XML_L("XML_DTD"), 0}, #endif -#ifdef XML_CONTEXT_BYTES - {XML_FEATURE_CONTEXT_BYTES, XML_L("XML_CONTEXT_BYTES"), - XML_CONTEXT_BYTES}, +#if XML_CONTEXT_BYTES > 0 + {XML_FEATURE_CONTEXT_BYTES, XML_L("XML_CONTEXT_BYTES"), + XML_CONTEXT_BYTES}, #endif #ifdef XML_MIN_SIZE - {XML_FEATURE_MIN_SIZE, XML_L("XML_MIN_SIZE"), 0}, + {XML_FEATURE_MIN_SIZE, XML_L("XML_MIN_SIZE"), 0}, #endif #ifdef XML_NS - {XML_FEATURE_NS, XML_L("XML_NS"), 0}, + {XML_FEATURE_NS, XML_L("XML_NS"), 0}, #endif #ifdef XML_LARGE_SIZE - {XML_FEATURE_LARGE_SIZE, XML_L("XML_LARGE_SIZE"), 0}, -#endif - {XML_FEATURE_END, NULL, 0} - }; + {XML_FEATURE_LARGE_SIZE, XML_L("XML_LARGE_SIZE"), 0}, +#endif +#ifdef XML_ATTR_INFO + {XML_FEATURE_ATTR_INFO, XML_L("XML_ATTR_INFO"), 0}, +#endif +#if XML_GE == 1 + /* Added in Expat 2.4.0 for XML_DTD defined and + * added in Expat 2.6.0 for XML_GE == 1. */ + {XML_FEATURE_BILLION_LAUGHS_ATTACK_PROTECTION_MAXIMUM_AMPLIFICATION_DEFAULT, + XML_L("XML_BLAP_MAX_AMP"), + (long int) + EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_MAXIMUM_AMPLIFICATION_DEFAULT}, + {XML_FEATURE_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT, + XML_L("XML_BLAP_ACT_THRES"), + EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT}, + /* Added in Expat 2.6.0. */ + {XML_FEATURE_GE, XML_L("XML_GE"), 0}, + /* Added in Expat 2.7.2. */ + {XML_FEATURE_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION_DEFAULT, + XML_L("XML_AT_MAX_AMP"), + (long int)EXPAT_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION_DEFAULT}, + {XML_FEATURE_ALLOC_TRACKER_ACTIVATION_THRESHOLD_DEFAULT, + XML_L("XML_AT_ACT_THRES"), + (long int)EXPAT_ALLOC_TRACKER_ACTIVATION_THRESHOLD_DEFAULT}, +#endif + {XML_FEATURE_END, NULL, 0}}; return features; } +#if XML_GE == 1 +XML_Bool XMLCALL +XML_SetBillionLaughsAttackProtectionMaximumAmplification( + XML_Parser parser, float maximumAmplificationFactor) { + if ((parser == NULL) || (parser->m_parentParser != NULL) + || isnan(maximumAmplificationFactor) + || (maximumAmplificationFactor < 1.0f)) { + return XML_FALSE; + } + parser->m_accounting.maximumAmplificationFactor = maximumAmplificationFactor; + return XML_TRUE; +} + +XML_Bool XMLCALL +XML_SetBillionLaughsAttackProtectionActivationThreshold( + XML_Parser parser, unsigned long long activationThresholdBytes) { + if ((parser == NULL) || (parser->m_parentParser != NULL)) { + return XML_FALSE; + } + parser->m_accounting.activationThresholdBytes = activationThresholdBytes; + return XML_TRUE; +} + +XML_Bool XMLCALL +XML_SetAllocTrackerMaximumAmplification(XML_Parser parser, + float maximumAmplificationFactor) { + if ((parser == NULL) || (parser->m_parentParser != NULL) + || isnan(maximumAmplificationFactor) + || (maximumAmplificationFactor < 1.0f)) { + return XML_FALSE; + } + parser->m_alloc_tracker.maximumAmplificationFactor + = maximumAmplificationFactor; + return XML_TRUE; +} + +XML_Bool XMLCALL +XML_SetAllocTrackerActivationThreshold( + XML_Parser parser, unsigned long long activationThresholdBytes) { + if ((parser == NULL) || (parser->m_parentParser != NULL)) { + return XML_FALSE; + } + parser->m_alloc_tracker.activationThresholdBytes = activationThresholdBytes; + return XML_TRUE; +} +#endif /* XML_GE == 1 */ + +XML_Bool XMLCALL +XML_SetReparseDeferralEnabled(XML_Parser parser, XML_Bool enabled) { + if (parser != NULL && (enabled == XML_TRUE || enabled == XML_FALSE)) { + parser->m_reparseDeferralEnabled = enabled; + return XML_TRUE; + } + return XML_FALSE; +} + /* Initially tag->rawName always points into the parse buffer; for those TAG instances opened while the current parse buffer was processed, and not yet closed, we need to store tag->rawName in a more permanent location, since the parse buffer is about to be discarded. */ static XML_Bool -storeRawNames(XML_Parser parser) -{ - TAG *tag = tagStack; +storeRawNames(XML_Parser parser) { + TAG *tag = parser->m_tagStack; while (tag) { - int bufSize; - int nameLen = sizeof(XML_Char) * (tag->name.strLen + 1); + size_t bufSize; + size_t nameLen = sizeof(XML_Char) * (tag->name.strLen + 1); + size_t rawNameLen; char *rawNameBuf = tag->buf + nameLen; - /* Stop if already stored. Since tagStack is a stack, we can stop + /* Stop if already stored. Since m_tagStack is a stack, we can stop at the first entry that has already been copied; everything below it in the stack is already been accounted for in a previous call to this function. */ if (tag->rawName == rawNameBuf) break; - /* For re-use purposes we need to ensure that the + /* For reuse purposes we need to ensure that the size of tag->buf is a multiple of sizeof(XML_Char). */ - bufSize = nameLen + ROUND_UP(tag->rawNameLength, sizeof(XML_Char)); - if (bufSize > tag->bufEnd - tag->buf) { - char *temp = (char *)REALLOC(tag->buf, bufSize); + rawNameLen = ROUND_UP(tag->rawNameLength, sizeof(XML_Char)); + /* Detect and prevent integer overflow. */ + if (rawNameLen > (size_t)INT_MAX - nameLen) + return XML_FALSE; + bufSize = nameLen + rawNameLen; + if (bufSize > (size_t)(tag->bufEnd - tag->buf)) { + char *temp = REALLOC(parser, tag->buf, bufSize); if (temp == NULL) return XML_FALSE; /* if tag->name.str points to tag->buf (only when namespace @@ -2000,8 +3155,8 @@ storeRawNames(XML_Parser parser) then update it as well, since it will always point into tag->buf */ if (tag->name.localPart) - tag->name.localPart = (XML_Char *)temp + (tag->name.localPart - - (XML_Char *)tag->buf); + tag->name.localPart + = (XML_Char *)temp + (tag->name.localPart - (XML_Char *)tag->buf); tag->buf = temp; tag->bufEnd = temp + bufSize; rawNameBuf = temp + nameLen; @@ -2014,163 +3169,172 @@ storeRawNames(XML_Parser parser) } static enum XML_Error PTRCALL -contentProcessor(XML_Parser parser, - const char *start, - const char *end, - const char **endPtr) -{ - enum XML_Error result = doContent(parser, 0, encoding, start, end, - endPtr, (XML_Bool)!ps_finalBuffer); +contentProcessor(XML_Parser parser, const char *start, const char *end, + const char **endPtr) { + enum XML_Error result = doContent( + parser, parser->m_parentParser ? 1 : 0, parser->m_encoding, start, end, + endPtr, (XML_Bool)! parser->m_parsingStatus.finalBuffer, + XML_ACCOUNT_DIRECT); if (result == XML_ERROR_NONE) { - if (!storeRawNames(parser)) + if (! storeRawNames(parser)) return XML_ERROR_NO_MEMORY; } return result; } static enum XML_Error PTRCALL -externalEntityInitProcessor(XML_Parser parser, - const char *start, - const char *end, - const char **endPtr) -{ +externalEntityInitProcessor(XML_Parser parser, const char *start, + const char *end, const char **endPtr) { enum XML_Error result = initializeEncoding(parser); if (result != XML_ERROR_NONE) return result; - processor = externalEntityInitProcessor2; + parser->m_processor = externalEntityInitProcessor2; return externalEntityInitProcessor2(parser, start, end, endPtr); } static enum XML_Error PTRCALL -externalEntityInitProcessor2(XML_Parser parser, - const char *start, - const char *end, - const char **endPtr) -{ +externalEntityInitProcessor2(XML_Parser parser, const char *start, + const char *end, const char **endPtr) { const char *next = start; /* XmlContentTok doesn't always set the last arg */ - int tok = XmlContentTok(encoding, start, end, &next); + int tok = XmlContentTok(parser->m_encoding, start, end, &next); switch (tok) { case XML_TOK_BOM: +#if XML_GE == 1 + if (! accountingDiffTolerated(parser, tok, start, next, __LINE__, + XML_ACCOUNT_DIRECT)) { + accountingOnAbort(parser); + return XML_ERROR_AMPLIFICATION_LIMIT_BREACH; + } +#endif /* XML_GE == 1 */ + /* If we are at the end of the buffer, this would cause the next stage, i.e. externalEntityInitProcessor3, to pass control directly to doContent (by detecting XML_TOK_NONE) without processing any xml text declaration - causing the error XML_ERROR_MISPLACED_XML_PI in doContent. */ - if (next == end && !ps_finalBuffer) { + if (next == end && ! parser->m_parsingStatus.finalBuffer) { *endPtr = next; return XML_ERROR_NONE; } start = next; break; case XML_TOK_PARTIAL: - if (!ps_finalBuffer) { + if (! parser->m_parsingStatus.finalBuffer) { *endPtr = start; return XML_ERROR_NONE; } - eventPtr = start; + parser->m_eventPtr = start; return XML_ERROR_UNCLOSED_TOKEN; case XML_TOK_PARTIAL_CHAR: - if (!ps_finalBuffer) { + if (! parser->m_parsingStatus.finalBuffer) { *endPtr = start; return XML_ERROR_NONE; } - eventPtr = start; + parser->m_eventPtr = start; return XML_ERROR_PARTIAL_CHAR; } - processor = externalEntityInitProcessor3; + parser->m_processor = externalEntityInitProcessor3; return externalEntityInitProcessor3(parser, start, end, endPtr); } static enum XML_Error PTRCALL -externalEntityInitProcessor3(XML_Parser parser, - const char *start, - const char *end, - const char **endPtr) -{ +externalEntityInitProcessor3(XML_Parser parser, const char *start, + const char *end, const char **endPtr) { int tok; const char *next = start; /* XmlContentTok doesn't always set the last arg */ - eventPtr = start; - tok = XmlContentTok(encoding, start, end, &next); - eventEndPtr = next; + parser->m_eventPtr = start; + tok = XmlContentTok(parser->m_encoding, start, end, &next); + /* Note: These bytes are accounted later in: + - processXmlDecl + - externalEntityContentProcessor + */ + parser->m_eventEndPtr = next; switch (tok) { - case XML_TOK_XML_DECL: - { - enum XML_Error result; - result = processXmlDecl(parser, 1, start, next); - if (result != XML_ERROR_NONE) - return result; - switch (ps_parsing) { - case XML_SUSPENDED: - *endPtr = next; - return XML_ERROR_NONE; - case XML_FINISHED: - return XML_ERROR_ABORTED; - default: - start = next; + case XML_TOK_XML_DECL: { + enum XML_Error result; + result = processXmlDecl(parser, 1, start, next); + if (result != XML_ERROR_NONE) + return result; + switch (parser->m_parsingStatus.parsing) { + case XML_SUSPENDED: + *endPtr = next; + return XML_ERROR_NONE; + case XML_FINISHED: + return XML_ERROR_ABORTED; + case XML_PARSING: + if (parser->m_reenter) { + return XML_ERROR_UNEXPECTED_STATE; // LCOV_EXCL_LINE } + /* Fall through */ + default: + start = next; } - break; + } break; case XML_TOK_PARTIAL: - if (!ps_finalBuffer) { + if (! parser->m_parsingStatus.finalBuffer) { *endPtr = start; return XML_ERROR_NONE; } return XML_ERROR_UNCLOSED_TOKEN; case XML_TOK_PARTIAL_CHAR: - if (!ps_finalBuffer) { + if (! parser->m_parsingStatus.finalBuffer) { *endPtr = start; return XML_ERROR_NONE; } return XML_ERROR_PARTIAL_CHAR; } - processor = externalEntityContentProcessor; - tagLevel = 1; + parser->m_processor = externalEntityContentProcessor; + parser->m_tagLevel = 1; return externalEntityContentProcessor(parser, start, end, endPtr); } static enum XML_Error PTRCALL -externalEntityContentProcessor(XML_Parser parser, - const char *start, - const char *end, - const char **endPtr) -{ - enum XML_Error result = doContent(parser, 1, encoding, start, end, - endPtr, (XML_Bool)!ps_finalBuffer); +externalEntityContentProcessor(XML_Parser parser, const char *start, + const char *end, const char **endPtr) { + enum XML_Error result + = doContent(parser, 1, parser->m_encoding, start, end, endPtr, + (XML_Bool)! parser->m_parsingStatus.finalBuffer, + XML_ACCOUNT_ENTITY_EXPANSION); if (result == XML_ERROR_NONE) { - if (!storeRawNames(parser)) + if (! storeRawNames(parser)) return XML_ERROR_NO_MEMORY; } return result; } static enum XML_Error -doContent(XML_Parser parser, - int startTagLevel, - const ENCODING *enc, - const char *s, - const char *end, - const char **nextPtr, - XML_Bool haveMore) -{ +doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc, + const char *s, const char *end, const char **nextPtr, + XML_Bool haveMore, enum XML_Account account) { /* save one level of indirection */ - DTD * const dtd = _dtd; + DTD *const dtd = parser->m_dtd; const char **eventPP; const char **eventEndPP; - if (enc == encoding) { - eventPP = &eventPtr; - eventEndPP = &eventEndPtr; - } - else { - eventPP = &(openInternalEntities->internalEventPtr); - eventEndPP = &(openInternalEntities->internalEventEndPtr); + if (enc == parser->m_encoding) { + eventPP = &parser->m_eventPtr; + eventEndPP = &parser->m_eventEndPtr; + } else { + eventPP = &(parser->m_openInternalEntities->internalEventPtr); + eventEndPP = &(parser->m_openInternalEntities->internalEventEndPtr); } *eventPP = s; for (;;) { const char *next = s; /* XmlContentTok doesn't always set the last arg */ int tok = XmlContentTok(enc, s, end, &next); +#if XML_GE == 1 + const char *accountAfter + = ((tok == XML_TOK_TRAILING_RSQB) || (tok == XML_TOK_TRAILING_CR)) + ? (haveMore ? s /* i.e. 0 bytes */ : end) + : next; + if (! accountingDiffTolerated(parser, tok, s, accountAfter, __LINE__, + account)) { + accountingOnAbort(parser); + return XML_ERROR_AMPLIFICATION_LIMIT_BREACH; + } +#endif *eventEndPP = next; switch (tok) { case XML_TOK_TRAILING_CR: @@ -2179,18 +3343,17 @@ doContent(XML_Parser parser, return XML_ERROR_NONE; } *eventEndPP = end; - if (characterDataHandler) { + if (parser->m_characterDataHandler) { XML_Char c = 0xA; - characterDataHandler(handlerArg, &c, 1); - } - else if (defaultHandler) + parser->m_characterDataHandler(parser->m_handlerArg, &c, 1); + } else if (parser->m_defaultHandler) reportDefault(parser, enc, s, end); - /* We are at the end of the final buffer, should we check for - XML_SUSPENDED, XML_FINISHED? + /* We are at the end of the final buffer, should we check for + XML_SUSPENDED, XML_FINISHED? */ if (startTagLevel == 0) return XML_ERROR_NO_ELEMENTS; - if (tagLevel != startTagLevel) + if (parser->m_tagLevel != startTagLevel) return XML_ERROR_ASYNC_ENTITY; *nextPtr = end; return XML_ERROR_NONE; @@ -2200,7 +3363,7 @@ doContent(XML_Parser parser, return XML_ERROR_NONE; } if (startTagLevel > 0) { - if (tagLevel != startTagLevel) + if (parser->m_tagLevel != startTagLevel) return XML_ERROR_ASYNC_ENTITY; *nextPtr = s; return XML_ERROR_NONE; @@ -2221,386 +3384,429 @@ doContent(XML_Parser parser, return XML_ERROR_NONE; } return XML_ERROR_PARTIAL_CHAR; - case XML_TOK_ENTITY_REF: - { - const XML_Char *name; - ENTITY *entity; - XML_Char ch = (XML_Char) XmlPredefinedEntityName(enc, - s + enc->minBytesPerChar, - next - enc->minBytesPerChar); - if (ch) { - if (characterDataHandler) - characterDataHandler(handlerArg, &ch, 1); - else if (defaultHandler) + case XML_TOK_ENTITY_REF: { + const XML_Char *name; + ENTITY *entity; + XML_Char ch = (XML_Char)XmlPredefinedEntityName( + enc, s + enc->minBytesPerChar, next - enc->minBytesPerChar); + if (ch) { +#if XML_GE == 1 + /* NOTE: We are replacing 4-6 characters original input for 1 character + * so there is no amplification and hence recording without + * protection. */ + accountingDiffTolerated(parser, tok, (char *)&ch, + ((char *)&ch) + sizeof(XML_Char), __LINE__, + XML_ACCOUNT_ENTITY_EXPANSION); +#endif /* XML_GE == 1 */ + if (parser->m_characterDataHandler) + parser->m_characterDataHandler(parser->m_handlerArg, &ch, 1); + else if (parser->m_defaultHandler) + reportDefault(parser, enc, s, next); + break; + } + name = poolStoreString(&dtd->pool, enc, s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (! name) + return XML_ERROR_NO_MEMORY; + entity = (ENTITY *)lookup(parser, &dtd->generalEntities, name, 0); + poolDiscard(&dtd->pool); + /* First, determine if a check for an existing declaration is needed; + if yes, check that the entity exists, and that it is internal, + otherwise call the skipped entity or default handler. + */ + if (! dtd->hasParamEntityRefs || dtd->standalone) { + if (! entity) + return XML_ERROR_UNDEFINED_ENTITY; + else if (! entity->is_internal) + return XML_ERROR_ENTITY_DECLARED_IN_PE; + } else if (! entity) { + if (parser->m_skippedEntityHandler) + parser->m_skippedEntityHandler(parser->m_handlerArg, name, 0); + else if (parser->m_defaultHandler) + reportDefault(parser, enc, s, next); + break; + } + if (entity->open) + return XML_ERROR_RECURSIVE_ENTITY_REF; + if (entity->notation) + return XML_ERROR_BINARY_ENTITY_REF; + if (entity->textPtr) { + enum XML_Error result; + if (! parser->m_defaultExpandInternalEntities) { + if (parser->m_skippedEntityHandler) + parser->m_skippedEntityHandler(parser->m_handlerArg, entity->name, + 0); + else if (parser->m_defaultHandler) reportDefault(parser, enc, s, next); break; } - name = poolStoreString(&dtd->pool, enc, - s + enc->minBytesPerChar, - next - enc->minBytesPerChar); - if (!name) + result = processEntity(parser, entity, XML_FALSE, ENTITY_INTERNAL); + if (result != XML_ERROR_NONE) + return result; + } else if (parser->m_externalEntityRefHandler) { + const XML_Char *context; + entity->open = XML_TRUE; + context = getContext(parser); + entity->open = XML_FALSE; + if (! context) return XML_ERROR_NO_MEMORY; - entity = (ENTITY *)lookup(&dtd->generalEntities, name, 0); - poolDiscard(&dtd->pool); - /* First, determine if a check for an existing declaration is needed; - if yes, check that the entity exists, and that it is internal, - otherwise call the skipped entity or default handler. - */ - if (!dtd->hasParamEntityRefs || dtd->standalone) { - if (!entity) - return XML_ERROR_UNDEFINED_ENTITY; - else if (!entity->is_internal) - return XML_ERROR_ENTITY_DECLARED_IN_PE; - } - else if (!entity) { - if (skippedEntityHandler) - skippedEntityHandler(handlerArg, name, 0); - else if (defaultHandler) - reportDefault(parser, enc, s, next); - break; - } - if (entity->open) - return XML_ERROR_RECURSIVE_ENTITY_REF; - if (entity->notation) - return XML_ERROR_BINARY_ENTITY_REF; - if (entity->textPtr) { - enum XML_Error result; - if (!defaultExpandInternalEntities) { - if (skippedEntityHandler) - skippedEntityHandler(handlerArg, entity->name, 0); - else if (defaultHandler) - reportDefault(parser, enc, s, next); - break; - } - result = processInternalEntity(parser, entity, XML_FALSE); - if (result != XML_ERROR_NONE) - return result; - } - else if (externalEntityRefHandler) { - const XML_Char *context; - entity->open = XML_TRUE; - context = getContext(parser); - entity->open = XML_FALSE; - if (!context) - return XML_ERROR_NO_MEMORY; - if (!externalEntityRefHandler(externalEntityRefHandlerArg, - context, - entity->base, - entity->systemId, - entity->publicId)) - return XML_ERROR_EXTERNAL_ENTITY_HANDLING; - poolDiscard(&tempPool); - } - else if (defaultHandler) - reportDefault(parser, enc, s, next); - break; - } + if (! parser->m_externalEntityRefHandler( + parser->m_externalEntityRefHandlerArg, context, entity->base, + entity->systemId, entity->publicId)) + return XML_ERROR_EXTERNAL_ENTITY_HANDLING; + poolDiscard(&parser->m_tempPool); + } else if (parser->m_defaultHandler) + reportDefault(parser, enc, s, next); + break; + } case XML_TOK_START_TAG_NO_ATTS: /* fall through */ - case XML_TOK_START_TAG_WITH_ATTS: - { - TAG *tag; - enum XML_Error result; - XML_Char *toPtr; - if (freeTagList) { - tag = freeTagList; - freeTagList = freeTagList->parent; + case XML_TOK_START_TAG_WITH_ATTS: { + TAG *tag; + enum XML_Error result; + XML_Char *toPtr; + if (parser->m_freeTagList) { + tag = parser->m_freeTagList; + parser->m_freeTagList = parser->m_freeTagList->parent; + } else { + tag = MALLOC(parser, sizeof(TAG)); + if (! tag) + return XML_ERROR_NO_MEMORY; + tag->buf = MALLOC(parser, INIT_TAG_BUF_SIZE); + if (! tag->buf) { + FREE(parser, tag); + return XML_ERROR_NO_MEMORY; } - else { - tag = (TAG *)MALLOC(sizeof(TAG)); - if (!tag) - return XML_ERROR_NO_MEMORY; - tag->buf = (char *)MALLOC(INIT_TAG_BUF_SIZE); - if (!tag->buf) { - FREE(tag); - return XML_ERROR_NO_MEMORY; + tag->bufEnd = tag->buf + INIT_TAG_BUF_SIZE; + } + tag->bindings = NULL; + tag->parent = parser->m_tagStack; + parser->m_tagStack = tag; + tag->name.localPart = NULL; + tag->name.prefix = NULL; + tag->rawName = s + enc->minBytesPerChar; + tag->rawNameLength = XmlNameLength(enc, tag->rawName); + ++parser->m_tagLevel; + { + const char *rawNameEnd = tag->rawName + tag->rawNameLength; + const char *fromPtr = tag->rawName; + toPtr = (XML_Char *)tag->buf; + for (;;) { + int bufSize; + int convLen; + const enum XML_Convert_Result convert_res + = XmlConvert(enc, &fromPtr, rawNameEnd, (ICHAR **)&toPtr, + (ICHAR *)tag->bufEnd - 1); + convLen = (int)(toPtr - (XML_Char *)tag->buf); + if ((fromPtr >= rawNameEnd) + || (convert_res == XML_CONVERT_INPUT_INCOMPLETE)) { + tag->name.strLen = convLen; + break; } - tag->bufEnd = tag->buf + INIT_TAG_BUF_SIZE; - } - tag->bindings = NULL; - tag->parent = tagStack; - tagStack = tag; - tag->name.localPart = NULL; - tag->name.prefix = NULL; - tag->rawName = s + enc->minBytesPerChar; - tag->rawNameLength = XmlNameLength(enc, tag->rawName); - ++tagLevel; - { - const char *rawNameEnd = tag->rawName + tag->rawNameLength; - const char *fromPtr = tag->rawName; - toPtr = (XML_Char *)tag->buf; - for (;;) { - int bufSize; - int convLen; - XmlConvert(enc, - &fromPtr, rawNameEnd, - (ICHAR **)&toPtr, (ICHAR *)tag->bufEnd - 1); - convLen = (int)(toPtr - (XML_Char *)tag->buf); - if (fromPtr == rawNameEnd) { - tag->name.strLen = convLen; - break; - } - bufSize = (int)(tag->bufEnd - tag->buf) << 1; - { - char *temp = (char *)REALLOC(tag->buf, bufSize); - if (temp == NULL) - return XML_ERROR_NO_MEMORY; - tag->buf = temp; - tag->bufEnd = temp + bufSize; - toPtr = (XML_Char *)temp + convLen; - } + bufSize = (int)(tag->bufEnd - tag->buf) << 1; + { + char *temp = REALLOC(parser, tag->buf, bufSize); + if (temp == NULL) + return XML_ERROR_NO_MEMORY; + tag->buf = temp; + tag->bufEnd = temp + bufSize; + toPtr = (XML_Char *)temp + convLen; } } - tag->name.str = (XML_Char *)tag->buf; - *toPtr = XML_T('\0'); - result = storeAtts(parser, enc, s, &(tag->name), &(tag->bindings)); - if (result) - return result; - if (startElementHandler) - startElementHandler(handlerArg, tag->name.str, - (const XML_Char **)atts); - else if (defaultHandler) - reportDefault(parser, enc, s, next); - poolClear(&tempPool); - break; } + tag->name.str = (XML_Char *)tag->buf; + *toPtr = XML_T('\0'); + result + = storeAtts(parser, enc, s, &(tag->name), &(tag->bindings), account); + if (result) + return result; + if (parser->m_startElementHandler) + parser->m_startElementHandler(parser->m_handlerArg, tag->name.str, + (const XML_Char **)parser->m_atts); + else if (parser->m_defaultHandler) + reportDefault(parser, enc, s, next); + poolClear(&parser->m_tempPool); + break; + } case XML_TOK_EMPTY_ELEMENT_NO_ATTS: /* fall through */ - case XML_TOK_EMPTY_ELEMENT_WITH_ATTS: - { - const char *rawName = s + enc->minBytesPerChar; - enum XML_Error result; - BINDING *bindings = NULL; - XML_Bool noElmHandlers = XML_TRUE; - TAG_NAME name; - name.str = poolStoreString(&tempPool, enc, rawName, - rawName + XmlNameLength(enc, rawName)); - if (!name.str) - return XML_ERROR_NO_MEMORY; - poolFinish(&tempPool); - result = storeAtts(parser, enc, s, &name, &bindings); - if (result) - return result; - poolFinish(&tempPool); - if (startElementHandler) { - startElementHandler(handlerArg, name.str, (const XML_Char **)atts); - noElmHandlers = XML_FALSE; - } - if (endElementHandler) { - if (startElementHandler) - *eventPP = *eventEndPP; - endElementHandler(handlerArg, name.str); - noElmHandlers = XML_FALSE; - } - if (noElmHandlers && defaultHandler) - reportDefault(parser, enc, s, next); - poolClear(&tempPool); - while (bindings) { - BINDING *b = bindings; - if (endNamespaceDeclHandler) - endNamespaceDeclHandler(handlerArg, b->prefix->name); - bindings = bindings->nextTagBinding; - b->nextTagBinding = freeBindingList; - freeBindingList = b; - b->prefix->binding = b->prevPrefixBinding; - } + case XML_TOK_EMPTY_ELEMENT_WITH_ATTS: { + const char *rawName = s + enc->minBytesPerChar; + enum XML_Error result; + BINDING *bindings = NULL; + XML_Bool noElmHandlers = XML_TRUE; + TAG_NAME name; + name.str = poolStoreString(&parser->m_tempPool, enc, rawName, + rawName + XmlNameLength(enc, rawName)); + if (! name.str) + return XML_ERROR_NO_MEMORY; + poolFinish(&parser->m_tempPool); + result = storeAtts(parser, enc, s, &name, &bindings, + XML_ACCOUNT_NONE /* token spans whole start tag */); + if (result != XML_ERROR_NONE) { + freeBindings(parser, bindings); + return result; + } + poolFinish(&parser->m_tempPool); + if (parser->m_startElementHandler) { + parser->m_startElementHandler(parser->m_handlerArg, name.str, + (const XML_Char **)parser->m_atts); + noElmHandlers = XML_FALSE; + } + if (parser->m_endElementHandler) { + if (parser->m_startElementHandler) + *eventPP = *eventEndPP; + parser->m_endElementHandler(parser->m_handlerArg, name.str); + noElmHandlers = XML_FALSE; + } + if (noElmHandlers && parser->m_defaultHandler) + reportDefault(parser, enc, s, next); + poolClear(&parser->m_tempPool); + freeBindings(parser, bindings); + } + if ((parser->m_tagLevel == 0) + && (parser->m_parsingStatus.parsing != XML_FINISHED)) { + if (parser->m_parsingStatus.parsing == XML_SUSPENDED + || (parser->m_parsingStatus.parsing == XML_PARSING + && parser->m_reenter)) + parser->m_processor = epilogProcessor; + else + return epilogProcessor(parser, next, end, nextPtr); } - if (tagLevel == 0) - return epilogProcessor(parser, next, end, nextPtr); break; case XML_TOK_END_TAG: - if (tagLevel == startTagLevel) + if (parser->m_tagLevel == startTagLevel) return XML_ERROR_ASYNC_ENTITY; else { int len; const char *rawName; - TAG *tag = tagStack; - tagStack = tag->parent; - tag->parent = freeTagList; - freeTagList = tag; - rawName = s + enc->minBytesPerChar*2; + TAG *tag = parser->m_tagStack; + rawName = s + enc->minBytesPerChar * 2; len = XmlNameLength(enc, rawName); if (len != tag->rawNameLength || memcmp(tag->rawName, rawName, len) != 0) { *eventPP = rawName; return XML_ERROR_TAG_MISMATCH; } - --tagLevel; - if (endElementHandler) { + parser->m_tagStack = tag->parent; + tag->parent = parser->m_freeTagList; + parser->m_freeTagList = tag; + --parser->m_tagLevel; + if (parser->m_endElementHandler) { const XML_Char *localPart; const XML_Char *prefix; XML_Char *uri; localPart = tag->name.localPart; - if (ns && localPart) { + if (parser->m_ns && localPart) { /* localPart and prefix may have been overwritten in tag->name.str, since this points to the binding->uri - buffer which gets re-used; so we have to add them again + buffer which gets reused; so we have to add them again */ uri = (XML_Char *)tag->name.str + tag->name.uriLen; /* don't need to check for space - already done in storeAtts() */ - while (*localPart) *uri++ = *localPart++; - prefix = (XML_Char *)tag->name.prefix; - if (ns_triplets && prefix) { - *uri++ = namespaceSeparator; - while (*prefix) *uri++ = *prefix++; - } + while (*localPart) + *uri++ = *localPart++; + prefix = tag->name.prefix; + if (parser->m_ns_triplets && prefix) { + *uri++ = parser->m_namespaceSeparator; + while (*prefix) + *uri++ = *prefix++; + } *uri = XML_T('\0'); } - endElementHandler(handlerArg, tag->name.str); - } - else if (defaultHandler) + parser->m_endElementHandler(parser->m_handlerArg, tag->name.str); + } else if (parser->m_defaultHandler) reportDefault(parser, enc, s, next); while (tag->bindings) { BINDING *b = tag->bindings; - if (endNamespaceDeclHandler) - endNamespaceDeclHandler(handlerArg, b->prefix->name); + if (parser->m_endNamespaceDeclHandler) + parser->m_endNamespaceDeclHandler(parser->m_handlerArg, + b->prefix->name); tag->bindings = tag->bindings->nextTagBinding; - b->nextTagBinding = freeBindingList; - freeBindingList = b; + b->nextTagBinding = parser->m_freeBindingList; + parser->m_freeBindingList = b; b->prefix->binding = b->prevPrefixBinding; } - if (tagLevel == 0) - return epilogProcessor(parser, next, end, nextPtr); - } - break; - case XML_TOK_CHAR_REF: - { - int n = XmlCharRefNumber(enc, s); - if (n < 0) - return XML_ERROR_BAD_CHAR_REF; - if (characterDataHandler) { - XML_Char buf[XML_ENCODE_MAX]; - characterDataHandler(handlerArg, buf, XmlEncode(n, (ICHAR *)buf)); + if ((parser->m_tagLevel == 0) + && (parser->m_parsingStatus.parsing != XML_FINISHED)) { + if (parser->m_parsingStatus.parsing == XML_SUSPENDED + || (parser->m_parsingStatus.parsing == XML_PARSING + && parser->m_reenter)) + parser->m_processor = epilogProcessor; + else + return epilogProcessor(parser, next, end, nextPtr); } - else if (defaultHandler) - reportDefault(parser, enc, s, next); } break; + case XML_TOK_CHAR_REF: { + int n = XmlCharRefNumber(enc, s); + if (n < 0) + return XML_ERROR_BAD_CHAR_REF; + if (parser->m_characterDataHandler) { + XML_Char buf[XML_ENCODE_MAX]; + parser->m_characterDataHandler(parser->m_handlerArg, buf, + XmlEncode(n, (ICHAR *)buf)); + } else if (parser->m_defaultHandler) + reportDefault(parser, enc, s, next); + } break; case XML_TOK_XML_DECL: return XML_ERROR_MISPLACED_XML_PI; case XML_TOK_DATA_NEWLINE: - if (characterDataHandler) { + if (parser->m_characterDataHandler) { XML_Char c = 0xA; - characterDataHandler(handlerArg, &c, 1); - } - else if (defaultHandler) + parser->m_characterDataHandler(parser->m_handlerArg, &c, 1); + } else if (parser->m_defaultHandler) reportDefault(parser, enc, s, next); break; - case XML_TOK_CDATA_SECT_OPEN: - { - enum XML_Error result; - if (startCdataSectionHandler) - startCdataSectionHandler(handlerArg); -#if 0 - /* Suppose you doing a transformation on a document that involves - changing only the character data. You set up a defaultHandler - and a characterDataHandler. The defaultHandler simply copies - characters through. The characterDataHandler does the - transformation and writes the characters out escaping them as - necessary. This case will fail to work if we leave out the - following two lines (because & and < inside CDATA sections will - be incorrectly escaped). - - However, now we have a start/endCdataSectionHandler, so it seems - easier to let the user deal with this. - */ - else if (characterDataHandler) - characterDataHandler(handlerArg, dataBuf, 0); -#endif - else if (defaultHandler) - reportDefault(parser, enc, s, next); - result = doCdataSection(parser, enc, &next, end, nextPtr, haveMore); - if (result != XML_ERROR_NONE) - return result; - else if (!next) { - processor = cdataSectionProcessor; - return result; - } + case XML_TOK_CDATA_SECT_OPEN: { + enum XML_Error result; + if (parser->m_startCdataSectionHandler) + parser->m_startCdataSectionHandler(parser->m_handlerArg); + /* BEGIN disabled code */ + /* Suppose you doing a transformation on a document that involves + changing only the character data. You set up a defaultHandler + and a characterDataHandler. The defaultHandler simply copies + characters through. The characterDataHandler does the + transformation and writes the characters out escaping them as + necessary. This case will fail to work if we leave out the + following two lines (because & and < inside CDATA sections will + be incorrectly escaped). + + However, now we have a start/endCdataSectionHandler, so it seems + easier to let the user deal with this. + */ + else if ((0) && parser->m_characterDataHandler) + parser->m_characterDataHandler(parser->m_handlerArg, parser->m_dataBuf, + 0); + /* END disabled code */ + else if (parser->m_defaultHandler) + reportDefault(parser, enc, s, next); + result + = doCdataSection(parser, enc, &next, end, nextPtr, haveMore, account); + if (result != XML_ERROR_NONE) + return result; + else if (! next) { + parser->m_processor = cdataSectionProcessor; + return result; } - break; + } break; case XML_TOK_TRAILING_RSQB: if (haveMore) { *nextPtr = s; return XML_ERROR_NONE; } - if (characterDataHandler) { + if (parser->m_characterDataHandler) { if (MUST_CONVERT(enc, s)) { - ICHAR *dataPtr = (ICHAR *)dataBuf; - XmlConvert(enc, &s, end, &dataPtr, (ICHAR *)dataBufEnd); - characterDataHandler(handlerArg, dataBuf, - (int)(dataPtr - (ICHAR *)dataBuf)); - } - else - characterDataHandler(handlerArg, - (XML_Char *)s, - (int)((XML_Char *)end - (XML_Char *)s)); - } - else if (defaultHandler) + ICHAR *dataPtr = (ICHAR *)parser->m_dataBuf; + XmlConvert(enc, &s, end, &dataPtr, (ICHAR *)parser->m_dataBufEnd); + parser->m_characterDataHandler( + parser->m_handlerArg, parser->m_dataBuf, + (int)(dataPtr - (ICHAR *)parser->m_dataBuf)); + } else + parser->m_characterDataHandler( + parser->m_handlerArg, (const XML_Char *)s, + (int)((const XML_Char *)end - (const XML_Char *)s)); + } else if (parser->m_defaultHandler) reportDefault(parser, enc, s, end); - /* We are at the end of the final buffer, should we check for - XML_SUSPENDED, XML_FINISHED? + /* We are at the end of the final buffer, should we check for + XML_SUSPENDED, XML_FINISHED? */ if (startTagLevel == 0) { *eventPP = end; return XML_ERROR_NO_ELEMENTS; } - if (tagLevel != startTagLevel) { + if (parser->m_tagLevel != startTagLevel) { *eventPP = end; return XML_ERROR_ASYNC_ENTITY; } *nextPtr = end; return XML_ERROR_NONE; - case XML_TOK_DATA_CHARS: - { - XML_CharacterDataHandler charDataHandler = characterDataHandler; - if (charDataHandler) { - if (MUST_CONVERT(enc, s)) { - for (;;) { - ICHAR *dataPtr = (ICHAR *)dataBuf; - XmlConvert(enc, &s, next, &dataPtr, (ICHAR *)dataBufEnd); - *eventEndPP = s; - charDataHandler(handlerArg, dataBuf, - (int)(dataPtr - (ICHAR *)dataBuf)); - if (s == next) - break; - *eventPP = s; - } + case XML_TOK_DATA_CHARS: { + XML_CharacterDataHandler charDataHandler = parser->m_characterDataHandler; + if (charDataHandler) { + if (MUST_CONVERT(enc, s)) { + for (;;) { + ICHAR *dataPtr = (ICHAR *)parser->m_dataBuf; + const enum XML_Convert_Result convert_res = XmlConvert( + enc, &s, next, &dataPtr, (ICHAR *)parser->m_dataBufEnd); + *eventEndPP = s; + charDataHandler(parser->m_handlerArg, parser->m_dataBuf, + (int)(dataPtr - (ICHAR *)parser->m_dataBuf)); + if ((convert_res == XML_CONVERT_COMPLETED) + || (convert_res == XML_CONVERT_INPUT_INCOMPLETE)) + break; + *eventPP = s; } - else - charDataHandler(handlerArg, - (XML_Char *)s, - (int)((XML_Char *)next - (XML_Char *)s)); - } - else if (defaultHandler) - reportDefault(parser, enc, s, next); - } - break; + } else + charDataHandler(parser->m_handlerArg, (const XML_Char *)s, + (int)((const XML_Char *)next - (const XML_Char *)s)); + } else if (parser->m_defaultHandler) + reportDefault(parser, enc, s, next); + } break; case XML_TOK_PI: - if (!reportProcessingInstruction(parser, enc, s, next)) + if (! reportProcessingInstruction(parser, enc, s, next)) return XML_ERROR_NO_MEMORY; break; case XML_TOK_COMMENT: - if (!reportComment(parser, enc, s, next)) + if (! reportComment(parser, enc, s, next)) return XML_ERROR_NO_MEMORY; break; default: - if (defaultHandler) + /* All of the tokens produced by XmlContentTok() have their own + * explicit cases, so this default is not strictly necessary. + * However it is a useful safety net, so we retain the code and + * simply exclude it from the coverage tests. + * + * LCOV_EXCL_START + */ + if (parser->m_defaultHandler) reportDefault(parser, enc, s, next); break; + /* LCOV_EXCL_STOP */ } - *eventPP = s = next; - switch (ps_parsing) { - case XML_SUSPENDED: + switch (parser->m_parsingStatus.parsing) { + case XML_SUSPENDED: + *eventPP = next; *nextPtr = next; return XML_ERROR_NONE; case XML_FINISHED: + *eventPP = next; return XML_ERROR_ABORTED; - default: ; + case XML_PARSING: + if (parser->m_reenter) { + *nextPtr = next; + return XML_ERROR_NONE; + } + /* Fall through */ + default:; + *eventPP = s = next; } } /* not reached */ } +/* This function does not call free() on the allocated memory, merely + * moving it to the parser's m_freeBindingList where it can be freed or + * reused as appropriate. + */ +static void +freeBindings(XML_Parser parser, BINDING *bindings) { + while (bindings) { + BINDING *b = bindings; + + /* m_startNamespaceDeclHandler will have been called for this + * binding in addBindings(), so call the end handler now. + */ + if (parser->m_endNamespaceDeclHandler) + parser->m_endNamespaceDeclHandler(parser->m_handlerArg, b->prefix->name); + + bindings = bindings->nextTagBinding; + b->nextTagBinding = parser->m_freeBindingList; + parser->m_freeBindingList = b; + b->prefix->binding = b->prevPrefixBinding; + } +} + /* Precondition: all arguments must be non-NULL; Purpose: - normalize attributes @@ -2612,14 +3818,13 @@ doContent(XML_Parser parser, - generate namespace aware element name (URI, prefix) */ static enum XML_Error -storeAtts(XML_Parser parser, const ENCODING *enc, - const char *attStr, TAG_NAME *tagNamePtr, - BINDING **bindingsPtr) -{ - DTD * const dtd = _dtd; /* save one level of indirection */ +storeAtts(XML_Parser parser, const ENCODING *enc, const char *attStr, + TAG_NAME *tagNamePtr, BINDING **bindingsPtr, + enum XML_Account account) { + DTD *const dtd = parser->m_dtd; /* save one level of indirection */ ELEMENT_TYPE *elementType; int nDefaultAtts; - const XML_Char **appAtts; /* the attribute list for the application */ + const XML_Char **appAtts; /* the attribute list for the application */ int attIndex = 0; int prefixLen; int i; @@ -2630,54 +3835,120 @@ storeAtts(XML_Parser parser, const ENCODING *enc, const XML_Char *localPart; /* lookup the element type name */ - elementType = (ELEMENT_TYPE *)lookup(&dtd->elementTypes, tagNamePtr->str,0); - if (!elementType) { + elementType + = (ELEMENT_TYPE *)lookup(parser, &dtd->elementTypes, tagNamePtr->str, 0); + if (! elementType) { const XML_Char *name = poolCopyString(&dtd->pool, tagNamePtr->str); - if (!name) + if (! name) return XML_ERROR_NO_MEMORY; - elementType = (ELEMENT_TYPE *)lookup(&dtd->elementTypes, name, + elementType = (ELEMENT_TYPE *)lookup(parser, &dtd->elementTypes, name, sizeof(ELEMENT_TYPE)); - if (!elementType) + if (! elementType) return XML_ERROR_NO_MEMORY; - if (ns && !setElementTypePrefix(parser, elementType)) + if (parser->m_ns && ! setElementTypePrefix(parser, elementType)) return XML_ERROR_NO_MEMORY; } nDefaultAtts = elementType->nDefaultAtts; /* get the attributes from the tokenizer */ - n = XmlGetAttributes(enc, attStr, attsSize, atts); - if (n + nDefaultAtts > attsSize) { - int oldAttsSize = attsSize; + n = XmlGetAttributes(enc, attStr, parser->m_attsSize, parser->m_atts); + + /* Detect and prevent integer overflow */ + if (n > INT_MAX - nDefaultAtts) { + return XML_ERROR_NO_MEMORY; + } + + if (n + nDefaultAtts > parser->m_attsSize) { + int oldAttsSize = parser->m_attsSize; ATTRIBUTE *temp; - attsSize = n + nDefaultAtts + INIT_ATTS_SIZE; - temp = (ATTRIBUTE *)REALLOC((void *)atts, attsSize * sizeof(ATTRIBUTE)); - if (temp == NULL) +#ifdef XML_ATTR_INFO + XML_AttrInfo *temp2; +#endif + + /* Detect and prevent integer overflow */ + if ((nDefaultAtts > INT_MAX - INIT_ATTS_SIZE) + || (n > INT_MAX - (nDefaultAtts + INIT_ATTS_SIZE))) { + return XML_ERROR_NO_MEMORY; + } + + parser->m_attsSize = n + nDefaultAtts + INIT_ATTS_SIZE; + + /* Detect and prevent integer overflow. + * The preprocessor guard addresses the "always false" warning + * from -Wtype-limits on platforms where + * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */ +#if UINT_MAX >= SIZE_MAX + if ((unsigned)parser->m_attsSize > SIZE_MAX / sizeof(ATTRIBUTE)) { + parser->m_attsSize = oldAttsSize; + return XML_ERROR_NO_MEMORY; + } +#endif + + temp = REALLOC(parser, parser->m_atts, + parser->m_attsSize * sizeof(ATTRIBUTE)); + if (temp == NULL) { + parser->m_attsSize = oldAttsSize; + return XML_ERROR_NO_MEMORY; + } + parser->m_atts = temp; +#ifdef XML_ATTR_INFO + /* Detect and prevent integer overflow. + * The preprocessor guard addresses the "always false" warning + * from -Wtype-limits on platforms where + * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */ +# if UINT_MAX >= SIZE_MAX + if ((unsigned)parser->m_attsSize > SIZE_MAX / sizeof(XML_AttrInfo)) { + parser->m_attsSize = oldAttsSize; return XML_ERROR_NO_MEMORY; - atts = temp; + } +# endif + + temp2 = REALLOC(parser, parser->m_attInfo, + parser->m_attsSize * sizeof(XML_AttrInfo)); + if (temp2 == NULL) { + parser->m_attsSize = oldAttsSize; + return XML_ERROR_NO_MEMORY; + } + parser->m_attInfo = temp2; +#endif if (n > oldAttsSize) - XmlGetAttributes(enc, attStr, n, atts); + XmlGetAttributes(enc, attStr, n, parser->m_atts); } - appAtts = (const XML_Char **)atts; + appAtts = (const XML_Char **)parser->m_atts; for (i = 0; i < n; i++) { + ATTRIBUTE *currAtt = &parser->m_atts[i]; +#ifdef XML_ATTR_INFO + XML_AttrInfo *currAttInfo = &parser->m_attInfo[i]; +#endif /* add the name and value to the attribute list */ - ATTRIBUTE_ID *attId = getAttributeId(parser, enc, atts[i].name, - atts[i].name - + XmlNameLength(enc, atts[i].name)); - if (!attId) + ATTRIBUTE_ID *attId + = getAttributeId(parser, enc, currAtt->name, + currAtt->name + XmlNameLength(enc, currAtt->name)); + if (! attId) return XML_ERROR_NO_MEMORY; +#ifdef XML_ATTR_INFO + currAttInfo->nameStart + = parser->m_parseEndByteIndex - (parser->m_parseEndPtr - currAtt->name); + currAttInfo->nameEnd + = currAttInfo->nameStart + XmlNameLength(enc, currAtt->name); + currAttInfo->valueStart = parser->m_parseEndByteIndex + - (parser->m_parseEndPtr - currAtt->valuePtr); + currAttInfo->valueEnd = parser->m_parseEndByteIndex + - (parser->m_parseEndPtr - currAtt->valueEnd); +#endif /* Detect duplicate attributes by their QNames. This does not work when namespace processing is turned on and different prefixes for the same namespace are used. For this case we have a check further down. */ if ((attId->name)[-1]) { - if (enc == encoding) - eventPtr = atts[i].name; + if (enc == parser->m_encoding) + parser->m_eventPtr = parser->m_atts[i].name; return XML_ERROR_DUPLICATE_ATTRIBUTE; } (attId->name)[-1] = 1; appAtts[attIndex++] = attId->name; - if (!atts[i].normalized) { + if (! parser->m_atts[i].normalized) { enum XML_Error result; XML_Bool isCdata = XML_TRUE; @@ -2693,21 +3964,21 @@ storeAtts(XML_Parser parser, const ENCODING *enc, } /* normalize the attribute value */ - result = storeAttributeValue(parser, enc, isCdata, - atts[i].valuePtr, atts[i].valueEnd, - &tempPool); + result = storeAttributeValue( + parser, enc, isCdata, parser->m_atts[i].valuePtr, + parser->m_atts[i].valueEnd, &parser->m_tempPool, account); if (result) return result; - appAtts[attIndex] = poolStart(&tempPool); - poolFinish(&tempPool); - } - else { + appAtts[attIndex] = poolStart(&parser->m_tempPool); + poolFinish(&parser->m_tempPool); + } else { /* the value did not need normalizing */ - appAtts[attIndex] = poolStoreString(&tempPool, enc, atts[i].valuePtr, - atts[i].valueEnd); + appAtts[attIndex] = poolStoreString(&parser->m_tempPool, enc, + parser->m_atts[i].valuePtr, + parser->m_atts[i].valueEnd); if (appAtts[attIndex] == 0) return XML_ERROR_NO_MEMORY; - poolFinish(&tempPool); + poolFinish(&parser->m_tempPool); } /* handle prefixed attribute names */ if (attId->prefix) { @@ -2718,49 +3989,44 @@ storeAtts(XML_Parser parser, const ENCODING *enc, if (result) return result; --attIndex; - } - else { + } else { /* deal with other prefixed names later */ attIndex++; nPrefixes++; (attId->name)[-1] = 2; } - } - else + } else attIndex++; } /* set-up for XML_GetSpecifiedAttributeCount and XML_GetIdAttributeIndex */ - nSpecifiedAtts = attIndex; + parser->m_nSpecifiedAtts = attIndex; if (elementType->idAtt && (elementType->idAtt->name)[-1]) { for (i = 0; i < attIndex; i += 2) if (appAtts[i] == elementType->idAtt->name) { - idAttIndex = i; + parser->m_idAttIndex = i; break; } - } - else - idAttIndex = -1; + } else + parser->m_idAttIndex = -1; /* do attribute defaulting */ for (i = 0; i < nDefaultAtts; i++) { const DEFAULT_ATTRIBUTE *da = elementType->defaultAtts + i; - if (!(da->id->name)[-1] && da->value) { + if (! (da->id->name)[-1] && da->value) { if (da->id->prefix) { if (da->id->xmlns) { enum XML_Error result = addBinding(parser, da->id->prefix, da->id, da->value, bindingsPtr); if (result) return result; - } - else { + } else { (da->id->name)[-1] = 2; nPrefixes++; appAtts[attIndex++] = da->id->name; appAtts[attIndex++] = da->value; } - } - else { + } else { (da->id->name)[-1] = 1; appAtts[attIndex++] = da->id->name; appAtts[attIndex++] = da->value; @@ -2773,108 +4039,168 @@ storeAtts(XML_Parser parser, const ENCODING *enc, and clear flags that say whether attributes were specified */ i = 0; if (nPrefixes) { - int j; /* hash table index */ - unsigned long version = nsAttsVersion; - int nsAttsSize = (int)1 << nsAttsPower; + unsigned int j; /* hash table index */ + unsigned long version = parser->m_nsAttsVersion; + + /* Detect and prevent invalid shift */ + if (parser->m_nsAttsPower >= sizeof(unsigned int) * 8 /* bits per byte */) { + return XML_ERROR_NO_MEMORY; + } + + unsigned int nsAttsSize = 1u << parser->m_nsAttsPower; + unsigned char oldNsAttsPower = parser->m_nsAttsPower; /* size of hash table must be at least 2 * (# of prefixed attributes) */ - if ((nPrefixes << 1) >> nsAttsPower) { /* true for nsAttsPower = 0 */ + if ((nPrefixes << 1) + >> parser->m_nsAttsPower) { /* true for m_nsAttsPower = 0 */ NS_ATT *temp; /* hash table size must also be a power of 2 and >= 8 */ - while (nPrefixes >> nsAttsPower++); - if (nsAttsPower < 3) - nsAttsPower = 3; - nsAttsSize = (int)1 << nsAttsPower; - temp = (NS_ATT *)REALLOC(nsAtts, nsAttsSize * sizeof(NS_ATT)); - if (!temp) + while (nPrefixes >> parser->m_nsAttsPower++) + ; + if (parser->m_nsAttsPower < 3) + parser->m_nsAttsPower = 3; + + /* Detect and prevent invalid shift */ + if (parser->m_nsAttsPower >= sizeof(nsAttsSize) * 8 /* bits per byte */) { + /* Restore actual size of memory in m_nsAtts */ + parser->m_nsAttsPower = oldNsAttsPower; + return XML_ERROR_NO_MEMORY; + } + + nsAttsSize = 1u << parser->m_nsAttsPower; + + /* Detect and prevent integer overflow. + * The preprocessor guard addresses the "always false" warning + * from -Wtype-limits on platforms where + * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */ +#if UINT_MAX >= SIZE_MAX + if (nsAttsSize > SIZE_MAX / sizeof(NS_ATT)) { + /* Restore actual size of memory in m_nsAtts */ + parser->m_nsAttsPower = oldNsAttsPower; + return XML_ERROR_NO_MEMORY; + } +#endif + + temp = REALLOC(parser, parser->m_nsAtts, nsAttsSize * sizeof(NS_ATT)); + if (! temp) { + /* Restore actual size of memory in m_nsAtts */ + parser->m_nsAttsPower = oldNsAttsPower; return XML_ERROR_NO_MEMORY; - nsAtts = temp; - version = 0; /* force re-initialization of nsAtts hash table */ + } + parser->m_nsAtts = temp; + version = 0; /* force re-initialization of m_nsAtts hash table */ } - /* using a version flag saves us from initializing nsAtts every time */ - if (!version) { /* initialize version flags when version wraps around */ + /* using a version flag saves us from initializing m_nsAtts every time */ + if (! version) { /* initialize version flags when version wraps around */ version = INIT_ATTS_VERSION; - for (j = nsAttsSize; j != 0; ) - nsAtts[--j].version = version; + for (j = nsAttsSize; j != 0;) + parser->m_nsAtts[--j].version = version; } - nsAttsVersion = --version; + parser->m_nsAttsVersion = --version; /* expand prefixed names and check for duplicates */ for (; i < attIndex; i += 2) { const XML_Char *s = appAtts[i]; - if (s[-1] == 2) { /* prefixed */ + if (s[-1] == 2) { /* prefixed */ ATTRIBUTE_ID *id; const BINDING *b; - unsigned long uriHash = 0; - ((XML_Char *)s)[-1] = 0; /* clear flag */ - id = (ATTRIBUTE_ID *)lookup(&dtd->attributeIds, s, 0); + unsigned long uriHash; + struct siphash sip_state; + struct sipkey sip_key; + + copy_salt_to_sipkey(parser, &sip_key); + sip24_init(&sip_state, &sip_key); + + ((XML_Char *)s)[-1] = 0; /* clear flag */ + id = (ATTRIBUTE_ID *)lookup(parser, &dtd->attributeIds, s, 0); + if (! id || ! id->prefix) { + /* This code is walking through the appAtts array, dealing + * with (in this case) a prefixed attribute name. To be in + * the array, the attribute must have already been bound, so + * has to have passed through the hash table lookup once + * already. That implies that an entry for it already + * exists, so the lookup above will return a pointer to + * already allocated memory. There is no opportunaity for + * the allocator to fail, so the condition above cannot be + * fulfilled. + * + * Since it is difficult to be certain that the above + * analysis is complete, we retain the test and merely + * remove the code from coverage tests. + */ + return XML_ERROR_NO_MEMORY; /* LCOV_EXCL_LINE */ + } b = id->prefix->binding; - if (!b) + if (! b) return XML_ERROR_UNBOUND_PREFIX; - /* as we expand the name we also calculate its hash value */ - for (j = 0; j < b->uriLen; j++) { + for (j = 0; j < (unsigned int)b->uriLen; j++) { const XML_Char c = b->uri[j]; - if (!poolAppendChar(&tempPool, c)) + if (! poolAppendChar(&parser->m_tempPool, c)) return XML_ERROR_NO_MEMORY; - uriHash = CHAR_HASH(uriHash, c); } + + sip24_update(&sip_state, b->uri, b->uriLen * sizeof(XML_Char)); + while (*s++ != XML_T(ASCII_COLON)) ; - do { /* copies null terminator */ - const XML_Char c = *s; - if (!poolAppendChar(&tempPool, *s)) + + sip24_update(&sip_state, s, keylen(s) * sizeof(XML_Char)); + + do { /* copies null terminator */ + if (! poolAppendChar(&parser->m_tempPool, *s)) return XML_ERROR_NO_MEMORY; - uriHash = CHAR_HASH(uriHash, c); } while (*s++); + uriHash = (unsigned long)sip24_final(&sip_state); + { /* Check hash table for duplicate of expanded name (uriName). - Derived from code in lookup(HASH_TABLE *table, ...). + Derived from code in lookup(parser, HASH_TABLE *table, ...). */ unsigned char step = 0; unsigned long mask = nsAttsSize - 1; - j = uriHash & mask; /* index into hash table */ - while (nsAtts[j].version == version) { + j = uriHash & mask; /* index into hash table */ + while (parser->m_nsAtts[j].version == version) { /* for speed we compare stored hash values first */ - if (uriHash == nsAtts[j].hash) { - const XML_Char *s1 = poolStart(&tempPool); - const XML_Char *s2 = nsAtts[j].uriName; + if (uriHash == parser->m_nsAtts[j].hash) { + const XML_Char *s1 = poolStart(&parser->m_tempPool); + const XML_Char *s2 = parser->m_nsAtts[j].uriName; /* s1 is null terminated, but not s2 */ - for (; *s1 == *s2 && *s1 != 0; s1++, s2++); + for (; *s1 == *s2 && *s1 != 0; s1++, s2++) + ; if (*s1 == 0) return XML_ERROR_DUPLICATE_ATTRIBUTE; } - if (!step) - step = PROBE_STEP(uriHash, mask, nsAttsPower); + if (! step) + step = PROBE_STEP(uriHash, mask, parser->m_nsAttsPower); j < step ? (j += nsAttsSize - step) : (j -= step); } } - if (ns_triplets) { /* append namespace separator and prefix */ - tempPool.ptr[-1] = namespaceSeparator; + if (parser->m_ns_triplets) { /* append namespace separator and prefix */ + parser->m_tempPool.ptr[-1] = parser->m_namespaceSeparator; s = b->prefix->name; do { - if (!poolAppendChar(&tempPool, *s)) + if (! poolAppendChar(&parser->m_tempPool, *s)) return XML_ERROR_NO_MEMORY; } while (*s++); } /* store expanded name in attribute list */ - s = poolStart(&tempPool); - poolFinish(&tempPool); + s = poolStart(&parser->m_tempPool); + poolFinish(&parser->m_tempPool); appAtts[i] = s; /* fill empty slot with new version, uriName and hash value */ - nsAtts[j].version = version; - nsAtts[j].hash = uriHash; - nsAtts[j].uriName = s; + parser->m_nsAtts[j].version = version; + parser->m_nsAtts[j].hash = uriHash; + parser->m_nsAtts[j].uriName = s; - if (!--nPrefixes) { + if (! --nPrefixes) { i += 2; break; } - } - else /* not prefixed */ - ((XML_Char *)s)[-1] = 0; /* clear flag */ + } else /* not prefixed */ + ((XML_Char *)s)[-1] = 0; /* clear flag */ } } /* clear flags for the remaining attributes */ @@ -2883,93 +4209,224 @@ storeAtts(XML_Parser parser, const ENCODING *enc, for (binding = *bindingsPtr; binding; binding = binding->nextTagBinding) binding->attId->name[-1] = 0; - if (!ns) + if (! parser->m_ns) return XML_ERROR_NONE; /* expand the element type name */ if (elementType->prefix) { binding = elementType->prefix->binding; - if (!binding) + if (! binding) return XML_ERROR_UNBOUND_PREFIX; localPart = tagNamePtr->str; while (*localPart++ != XML_T(ASCII_COLON)) ; - } - else if (dtd->defaultPrefix.binding) { + } else if (dtd->defaultPrefix.binding) { binding = dtd->defaultPrefix.binding; localPart = tagNamePtr->str; - } - else + } else return XML_ERROR_NONE; prefixLen = 0; - if (ns_triplets && binding->prefix->name) { - for (; binding->prefix->name[prefixLen++];) - ; /* prefixLen includes null terminator */ + if (parser->m_ns_triplets && binding->prefix->name) { + while (binding->prefix->name[prefixLen++]) + ; /* prefixLen includes null terminator */ } tagNamePtr->localPart = localPart; tagNamePtr->uriLen = binding->uriLen; tagNamePtr->prefix = binding->prefix->name; tagNamePtr->prefixLen = prefixLen; for (i = 0; localPart[i++];) - ; /* i includes null terminator */ + ; /* i includes null terminator */ + + /* Detect and prevent integer overflow */ + if (binding->uriLen > INT_MAX - prefixLen + || i > INT_MAX - (binding->uriLen + prefixLen)) { + return XML_ERROR_NO_MEMORY; + } + n = i + binding->uriLen + prefixLen; if (n > binding->uriAlloc) { TAG *p; - uri = (XML_Char *)MALLOC((n + EXPAND_SPARE) * sizeof(XML_Char)); - if (!uri) + + /* Detect and prevent integer overflow */ + if (n > INT_MAX - EXPAND_SPARE) { + return XML_ERROR_NO_MEMORY; + } + /* Detect and prevent integer overflow. + * The preprocessor guard addresses the "always false" warning + * from -Wtype-limits on platforms where + * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */ +#if UINT_MAX >= SIZE_MAX + if ((unsigned)(n + EXPAND_SPARE) > SIZE_MAX / sizeof(XML_Char)) { + return XML_ERROR_NO_MEMORY; + } +#endif + + uri = MALLOC(parser, (n + EXPAND_SPARE) * sizeof(XML_Char)); + if (! uri) return XML_ERROR_NO_MEMORY; binding->uriAlloc = n + EXPAND_SPARE; memcpy(uri, binding->uri, binding->uriLen * sizeof(XML_Char)); - for (p = tagStack; p; p = p->parent) + for (p = parser->m_tagStack; p; p = p->parent) if (p->name.str == binding->uri) p->name.str = uri; - FREE(binding->uri); + FREE(parser, binding->uri); binding->uri = uri; } - /* if namespaceSeparator != '\0' then uri includes it already */ + /* if m_namespaceSeparator != '\0' then uri includes it already */ uri = binding->uri + binding->uriLen; memcpy(uri, localPart, i * sizeof(XML_Char)); /* we always have a namespace separator between localPart and prefix */ if (prefixLen) { uri += i - 1; - *uri = namespaceSeparator; /* replace null terminator */ + *uri = parser->m_namespaceSeparator; /* replace null terminator */ memcpy(uri + 1, binding->prefix->name, prefixLen * sizeof(XML_Char)); } tagNamePtr->str = binding->uri; return XML_ERROR_NONE; } +static XML_Bool +is_rfc3986_uri_char(XML_Char candidate) { + // For the RFC 3986 ANBF grammar see + // https://datatracker.ietf.org/doc/html/rfc3986#appendix-A + + switch (candidate) { + // From rule "ALPHA" (uppercase half) + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + case 'G': + case 'H': + case 'I': + case 'J': + case 'K': + case 'L': + case 'M': + case 'N': + case 'O': + case 'P': + case 'Q': + case 'R': + case 'S': + case 'T': + case 'U': + case 'V': + case 'W': + case 'X': + case 'Y': + case 'Z': + + // From rule "ALPHA" (lowercase half) + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + case 'g': + case 'h': + case 'i': + case 'j': + case 'k': + case 'l': + case 'm': + case 'n': + case 'o': + case 'p': + case 'q': + case 'r': + case 's': + case 't': + case 'u': + case 'v': + case 'w': + case 'x': + case 'y': + case 'z': + + // From rule "DIGIT" + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + + // From rule "pct-encoded" + case '%': + + // From rule "unreserved" + case '-': + case '.': + case '_': + case '~': + + // From rule "gen-delims" + case ':': + case '/': + case '?': + case '#': + case '[': + case ']': + case '@': + + // From rule "sub-delims" + case '!': + case '$': + case '&': + case '\'': + case '(': + case ')': + case '*': + case '+': + case ',': + case ';': + case '=': + return XML_TRUE; + + default: + return XML_FALSE; + } +} + /* addBinding() overwrites the value of prefix->binding without checking. Therefore one must keep track of the old value outside of addBinding(). */ static enum XML_Error addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId, - const XML_Char *uri, BINDING **bindingsPtr) -{ - static const XML_Char xmlNamespace[] = { - ASCII_h, ASCII_t, ASCII_t, ASCII_p, ASCII_COLON, ASCII_SLASH, ASCII_SLASH, - ASCII_w, ASCII_w, ASCII_w, ASCII_PERIOD, ASCII_w, ASCII_3, ASCII_PERIOD, - ASCII_o, ASCII_r, ASCII_g, ASCII_SLASH, ASCII_X, ASCII_M, ASCII_L, - ASCII_SLASH, ASCII_1, ASCII_9, ASCII_9, ASCII_8, ASCII_SLASH, - ASCII_n, ASCII_a, ASCII_m, ASCII_e, ASCII_s, ASCII_p, ASCII_a, ASCII_c, - ASCII_e, '\0' - }; - static const int xmlLen = - (int)sizeof(xmlNamespace)/sizeof(XML_Char) - 1; - static const XML_Char xmlnsNamespace[] = { - ASCII_h, ASCII_t, ASCII_t, ASCII_p, ASCII_COLON, ASCII_SLASH, ASCII_SLASH, - ASCII_w, ASCII_w, ASCII_w, ASCII_PERIOD, ASCII_w, ASCII_3, ASCII_PERIOD, - ASCII_o, ASCII_r, ASCII_g, ASCII_SLASH, ASCII_2, ASCII_0, ASCII_0, - ASCII_0, ASCII_SLASH, ASCII_x, ASCII_m, ASCII_l, ASCII_n, ASCII_s, - ASCII_SLASH, '\0' - }; - static const int xmlnsLen = - (int)sizeof(xmlnsNamespace)/sizeof(XML_Char) - 1; + const XML_Char *uri, BINDING **bindingsPtr) { + // "http://www.w3.org/XML/1998/namespace" + static const XML_Char xmlNamespace[] + = {ASCII_h, ASCII_t, ASCII_t, ASCII_p, ASCII_COLON, + ASCII_SLASH, ASCII_SLASH, ASCII_w, ASCII_w, ASCII_w, + ASCII_PERIOD, ASCII_w, ASCII_3, ASCII_PERIOD, ASCII_o, + ASCII_r, ASCII_g, ASCII_SLASH, ASCII_X, ASCII_M, + ASCII_L, ASCII_SLASH, ASCII_1, ASCII_9, ASCII_9, + ASCII_8, ASCII_SLASH, ASCII_n, ASCII_a, ASCII_m, + ASCII_e, ASCII_s, ASCII_p, ASCII_a, ASCII_c, + ASCII_e, '\0'}; + static const int xmlLen = (int)sizeof(xmlNamespace) / sizeof(XML_Char) - 1; + // "http://www.w3.org/2000/xmlns/" + static const XML_Char xmlnsNamespace[] + = {ASCII_h, ASCII_t, ASCII_t, ASCII_p, ASCII_COLON, ASCII_SLASH, + ASCII_SLASH, ASCII_w, ASCII_w, ASCII_w, ASCII_PERIOD, ASCII_w, + ASCII_3, ASCII_PERIOD, ASCII_o, ASCII_r, ASCII_g, ASCII_SLASH, + ASCII_2, ASCII_0, ASCII_0, ASCII_0, ASCII_SLASH, ASCII_x, + ASCII_m, ASCII_l, ASCII_n, ASCII_s, ASCII_SLASH, '\0'}; + static const int xmlnsLen + = (int)sizeof(xmlnsNamespace) / sizeof(XML_Char) - 1; XML_Bool mustBeXML = XML_FALSE; XML_Bool isXML = XML_TRUE; XML_Bool isXMLNS = XML_TRUE; - + BINDING *b; int len; @@ -2977,14 +4434,11 @@ addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId, if (*uri == XML_T('\0') && prefix->name) return XML_ERROR_UNDECLARING_PREFIX; - if (prefix->name - && prefix->name[0] == XML_T(ASCII_x) + if (prefix->name && prefix->name[0] == XML_T(ASCII_x) && prefix->name[1] == XML_T(ASCII_m) && prefix->name[2] == XML_T(ASCII_l)) { - /* Not allowed to bind xmlns */ - if (prefix->name[3] == XML_T(ASCII_n) - && prefix->name[4] == XML_T(ASCII_s) + if (prefix->name[3] == XML_T(ASCII_n) && prefix->name[4] == XML_T(ASCII_s) && prefix->name[5] == XML_T('\0')) return XML_ERROR_RESERVED_PREFIX_XMLNS; @@ -2996,9 +4450,32 @@ addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId, if (isXML && (len > xmlLen || uri[len] != xmlNamespace[len])) isXML = XML_FALSE; - if (!mustBeXML && isXMLNS + if (! mustBeXML && isXMLNS && (len > xmlnsLen || uri[len] != xmlnsNamespace[len])) isXMLNS = XML_FALSE; + + // NOTE: While Expat does not validate namespace URIs against RFC 3986 + // today (and is not REQUIRED to do so with regard to the XML 1.0 + // namespaces specification) we have to at least make sure, that + // the application on top of Expat (that is likely splitting expanded + // element names ("qualified names") of form + // "[uri sep] local [sep prefix] '\0'" back into 1, 2 or 3 pieces + // in its element handler code) cannot be confused by an attacker + // putting additional namespace separator characters into namespace + // declarations. That would be ambiguous and not to be expected. + // + // While the HTML API docs of function XML_ParserCreateNS have been + // advising against use of a namespace separator character that can + // appear in a URI for >20 years now, some widespread applications + // are using URI characters (':' (colon) in particular) for a + // namespace separator, in practice. To keep these applications + // functional, we only reject namespaces URIs containing the + // application-chosen namespace separator if the chosen separator + // is a non-URI character with regard to RFC 3986. + if (parser->m_ns && (uri[len] == parser->m_namespaceSeparator) + && ! is_rfc3986_uri_char(uri[len])) { + return XML_ERROR_SYNTAX; + } } isXML = isXML && len == xmlLen; isXMLNS = isXMLNS && len == xmlnsLen; @@ -3010,49 +4487,78 @@ addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId, if (isXMLNS) return XML_ERROR_RESERVED_NAMESPACE_URI; - if (namespaceSeparator) + if (parser->m_namespaceSeparator) len++; - if (freeBindingList) { - b = freeBindingList; + if (parser->m_freeBindingList) { + b = parser->m_freeBindingList; if (len > b->uriAlloc) { - XML_Char *temp = (XML_Char *)REALLOC(b->uri, - sizeof(XML_Char) * (len + EXPAND_SPARE)); + /* Detect and prevent integer overflow */ + if (len > INT_MAX - EXPAND_SPARE) { + return XML_ERROR_NO_MEMORY; + } + + /* Detect and prevent integer overflow. + * The preprocessor guard addresses the "always false" warning + * from -Wtype-limits on platforms where + * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */ +#if UINT_MAX >= SIZE_MAX + if ((unsigned)(len + EXPAND_SPARE) > SIZE_MAX / sizeof(XML_Char)) { + return XML_ERROR_NO_MEMORY; + } +#endif + + XML_Char *temp + = REALLOC(parser, b->uri, sizeof(XML_Char) * (len + EXPAND_SPARE)); if (temp == NULL) return XML_ERROR_NO_MEMORY; b->uri = temp; b->uriAlloc = len + EXPAND_SPARE; } - freeBindingList = b->nextTagBinding; - } - else { - b = (BINDING *)MALLOC(sizeof(BINDING)); - if (!b) + parser->m_freeBindingList = b->nextTagBinding; + } else { + b = MALLOC(parser, sizeof(BINDING)); + if (! b) + return XML_ERROR_NO_MEMORY; + + /* Detect and prevent integer overflow */ + if (len > INT_MAX - EXPAND_SPARE) { + return XML_ERROR_NO_MEMORY; + } + /* Detect and prevent integer overflow. + * The preprocessor guard addresses the "always false" warning + * from -Wtype-limits on platforms where + * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */ +#if UINT_MAX >= SIZE_MAX + if ((unsigned)(len + EXPAND_SPARE) > SIZE_MAX / sizeof(XML_Char)) { return XML_ERROR_NO_MEMORY; - b->uri = (XML_Char *)MALLOC(sizeof(XML_Char) * (len + EXPAND_SPARE)); - if (!b->uri) { - FREE(b); + } +#endif + + b->uri = MALLOC(parser, sizeof(XML_Char) * (len + EXPAND_SPARE)); + if (! b->uri) { + FREE(parser, b); return XML_ERROR_NO_MEMORY; } b->uriAlloc = len + EXPAND_SPARE; } b->uriLen = len; memcpy(b->uri, uri, len * sizeof(XML_Char)); - if (namespaceSeparator) - b->uri[len - 1] = namespaceSeparator; + if (parser->m_namespaceSeparator) + b->uri[len - 1] = parser->m_namespaceSeparator; b->prefix = prefix; b->attId = attId; b->prevPrefixBinding = prefix->binding; /* NULL binding when default namespace undeclared */ - if (*uri == XML_T('\0') && prefix == &_dtd->defaultPrefix) + if (*uri == XML_T('\0') && prefix == &parser->m_dtd->defaultPrefix) prefix->binding = NULL; else prefix->binding = b; b->nextTagBinding = *bindingsPtr; *bindingsPtr = b; /* if attId == NULL then we are not starting a namespace scope */ - if (attId && startNamespaceDeclHandler) - startNamespaceDeclHandler(handlerArg, prefix->name, - prefix->binding ? uri : 0); + if (attId && parser->m_startNamespaceDeclHandler) + parser->m_startNamespaceDeclHandler(parser->m_handlerArg, prefix->name, + prefix->binding ? uri : 0); return XML_ERROR_NONE; } @@ -3060,22 +4566,19 @@ addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId, the whole file is parsed with one call. */ static enum XML_Error PTRCALL -cdataSectionProcessor(XML_Parser parser, - const char *start, - const char *end, - const char **endPtr) -{ - enum XML_Error result = doCdataSection(parser, encoding, &start, end, - endPtr, (XML_Bool)!ps_finalBuffer); +cdataSectionProcessor(XML_Parser parser, const char *start, const char *end, + const char **endPtr) { + enum XML_Error result = doCdataSection( + parser, parser->m_encoding, &start, end, endPtr, + (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_ACCOUNT_DIRECT); if (result != XML_ERROR_NONE) return result; if (start) { - if (parentParser) { /* we are parsing an external entity */ - processor = externalEntityContentProcessor; + if (parser->m_parentParser) { /* we are parsing an external entity */ + parser->m_processor = externalEntityContentProcessor; return externalEntityContentProcessor(parser, start, end, endPtr); - } - else { - processor = contentProcessor; + } else { + parser->m_processor = contentProcessor; return contentProcessor(parser, start, end, endPtr); } } @@ -3086,82 +4589,82 @@ cdataSectionProcessor(XML_Parser parser, the section is not yet closed. */ static enum XML_Error -doCdataSection(XML_Parser parser, - const ENCODING *enc, - const char **startPtr, - const char *end, - const char **nextPtr, - XML_Bool haveMore) -{ +doCdataSection(XML_Parser parser, const ENCODING *enc, const char **startPtr, + const char *end, const char **nextPtr, XML_Bool haveMore, + enum XML_Account account) { const char *s = *startPtr; const char **eventPP; const char **eventEndPP; - if (enc == encoding) { - eventPP = &eventPtr; + if (enc == parser->m_encoding) { + eventPP = &parser->m_eventPtr; *eventPP = s; - eventEndPP = &eventEndPtr; - } - else { - eventPP = &(openInternalEntities->internalEventPtr); - eventEndPP = &(openInternalEntities->internalEventEndPtr); + eventEndPP = &parser->m_eventEndPtr; + } else { + eventPP = &(parser->m_openInternalEntities->internalEventPtr); + eventEndPP = &(parser->m_openInternalEntities->internalEventEndPtr); } *eventPP = s; *startPtr = NULL; for (;;) { - const char *next; + const char *next = s; /* in case of XML_TOK_NONE or XML_TOK_PARTIAL */ int tok = XmlCdataSectionTok(enc, s, end, &next); +#if XML_GE == 1 + if (! accountingDiffTolerated(parser, tok, s, next, __LINE__, account)) { + accountingOnAbort(parser); + return XML_ERROR_AMPLIFICATION_LIMIT_BREACH; + } +#else + UNUSED_P(account); +#endif *eventEndPP = next; switch (tok) { case XML_TOK_CDATA_SECT_CLOSE: - if (endCdataSectionHandler) - endCdataSectionHandler(handlerArg); -#if 0 + if (parser->m_endCdataSectionHandler) + parser->m_endCdataSectionHandler(parser->m_handlerArg); + /* BEGIN disabled code */ /* see comment under XML_TOK_CDATA_SECT_OPEN */ - else if (characterDataHandler) - characterDataHandler(handlerArg, dataBuf, 0); -#endif - else if (defaultHandler) + else if ((0) && parser->m_characterDataHandler) + parser->m_characterDataHandler(parser->m_handlerArg, parser->m_dataBuf, + 0); + /* END disabled code */ + else if (parser->m_defaultHandler) reportDefault(parser, enc, s, next); *startPtr = next; *nextPtr = next; - if (ps_parsing == XML_FINISHED) + if (parser->m_parsingStatus.parsing == XML_FINISHED) return XML_ERROR_ABORTED; else return XML_ERROR_NONE; case XML_TOK_DATA_NEWLINE: - if (characterDataHandler) { + if (parser->m_characterDataHandler) { XML_Char c = 0xA; - characterDataHandler(handlerArg, &c, 1); - } - else if (defaultHandler) + parser->m_characterDataHandler(parser->m_handlerArg, &c, 1); + } else if (parser->m_defaultHandler) reportDefault(parser, enc, s, next); break; - case XML_TOK_DATA_CHARS: - { - XML_CharacterDataHandler charDataHandler = characterDataHandler; - if (charDataHandler) { - if (MUST_CONVERT(enc, s)) { - for (;;) { - ICHAR *dataPtr = (ICHAR *)dataBuf; - XmlConvert(enc, &s, next, &dataPtr, (ICHAR *)dataBufEnd); - *eventEndPP = next; - charDataHandler(handlerArg, dataBuf, - (int)(dataPtr - (ICHAR *)dataBuf)); - if (s == next) - break; - *eventPP = s; - } + case XML_TOK_DATA_CHARS: { + XML_CharacterDataHandler charDataHandler = parser->m_characterDataHandler; + if (charDataHandler) { + if (MUST_CONVERT(enc, s)) { + for (;;) { + ICHAR *dataPtr = (ICHAR *)parser->m_dataBuf; + const enum XML_Convert_Result convert_res = XmlConvert( + enc, &s, next, &dataPtr, (ICHAR *)parser->m_dataBufEnd); + *eventEndPP = next; + charDataHandler(parser->m_handlerArg, parser->m_dataBuf, + (int)(dataPtr - (ICHAR *)parser->m_dataBuf)); + if ((convert_res == XML_CONVERT_COMPLETED) + || (convert_res == XML_CONVERT_INPUT_INCOMPLETE)) + break; + *eventPP = s; } - else - charDataHandler(handlerArg, - (XML_Char *)s, - (int)((XML_Char *)next - (XML_Char *)s)); - } - else if (defaultHandler) - reportDefault(parser, enc, s, next); - } - break; + } else + charDataHandler(parser->m_handlerArg, (const XML_Char *)s, + (int)((const XML_Char *)next - (const XML_Char *)s)); + } else if (parser->m_defaultHandler) + reportDefault(parser, enc, s, next); + } break; case XML_TOK_INVALID: *eventPP = next; return XML_ERROR_INVALID_TOKEN; @@ -3179,18 +4682,33 @@ doCdataSection(XML_Parser parser, } return XML_ERROR_UNCLOSED_CDATA_SECTION; default: + /* Every token returned by XmlCdataSectionTok() has its own + * explicit case, so this default case will never be executed. + * We retain it as a safety net and exclude it from the coverage + * statistics. + * + * LCOV_EXCL_START + */ *eventPP = next; return XML_ERROR_UNEXPECTED_STATE; + /* LCOV_EXCL_STOP */ } - *eventPP = s = next; - switch (ps_parsing) { + switch (parser->m_parsingStatus.parsing) { case XML_SUSPENDED: + *eventPP = next; *nextPtr = next; return XML_ERROR_NONE; case XML_FINISHED: + *eventPP = next; return XML_ERROR_ABORTED; - default: ; + case XML_PARSING: + if (parser->m_reenter) { + return XML_ERROR_UNEXPECTED_STATE; // LCOV_EXCL_LINE + } + /* Fall through */ + default:; + *eventPP = s = next; } } /* not reached */ @@ -3202,17 +4720,15 @@ doCdataSection(XML_Parser parser, the whole file is parsed with one call. */ static enum XML_Error PTRCALL -ignoreSectionProcessor(XML_Parser parser, - const char *start, - const char *end, - const char **endPtr) -{ - enum XML_Error result = doIgnoreSection(parser, encoding, &start, end, - endPtr, (XML_Bool)!ps_finalBuffer); +ignoreSectionProcessor(XML_Parser parser, const char *start, const char *end, + const char **endPtr) { + enum XML_Error result + = doIgnoreSection(parser, parser->m_encoding, &start, end, endPtr, + (XML_Bool)! parser->m_parsingStatus.finalBuffer); if (result != XML_ERROR_NONE) return result; if (start) { - processor = prologProcessor; + parser->m_processor = prologProcessor; return prologProcessor(parser, start, end, endPtr); } return result; @@ -3222,38 +4738,51 @@ ignoreSectionProcessor(XML_Parser parser, if the section is not yet closed. */ static enum XML_Error -doIgnoreSection(XML_Parser parser, - const ENCODING *enc, - const char **startPtr, - const char *end, - const char **nextPtr, - XML_Bool haveMore) -{ - const char *next; +doIgnoreSection(XML_Parser parser, const ENCODING *enc, const char **startPtr, + const char *end, const char **nextPtr, XML_Bool haveMore) { + const char *next = *startPtr; /* in case of XML_TOK_NONE or XML_TOK_PARTIAL */ int tok; const char *s = *startPtr; const char **eventPP; const char **eventEndPP; - if (enc == encoding) { - eventPP = &eventPtr; + if (enc == parser->m_encoding) { + eventPP = &parser->m_eventPtr; *eventPP = s; - eventEndPP = &eventEndPtr; - } - else { - eventPP = &(openInternalEntities->internalEventPtr); - eventEndPP = &(openInternalEntities->internalEventEndPtr); + eventEndPP = &parser->m_eventEndPtr; + } else { + /* It's not entirely clear, but it seems the following two lines + * of code cannot be executed. The only occasions on which 'enc' + * is not 'encoding' are when this function is called + * from the internal entity processing, and IGNORE sections are an + * error in internal entities. + * + * Since it really isn't clear that this is true, we keep the code + * and just remove it from our coverage tests. + * + * LCOV_EXCL_START + */ + eventPP = &(parser->m_openInternalEntities->internalEventPtr); + eventEndPP = &(parser->m_openInternalEntities->internalEventEndPtr); + /* LCOV_EXCL_STOP */ } *eventPP = s; *startPtr = NULL; tok = XmlIgnoreSectionTok(enc, s, end, &next); +# if XML_GE == 1 + if (! accountingDiffTolerated(parser, tok, s, next, __LINE__, + XML_ACCOUNT_DIRECT)) { + accountingOnAbort(parser); + return XML_ERROR_AMPLIFICATION_LIMIT_BREACH; + } +# endif *eventEndPP = next; switch (tok) { case XML_TOK_IGNORE_SECT: - if (defaultHandler) + if (parser->m_defaultHandler) reportDefault(parser, enc, s, next); *startPtr = next; *nextPtr = next; - if (ps_parsing == XML_FINISHED) + if (parser->m_parsingStatus.parsing == XML_FINISHED) return XML_ERROR_ABORTED; else return XML_ERROR_NONE; @@ -3274,8 +4803,16 @@ doIgnoreSection(XML_Parser parser, } return XML_ERROR_SYNTAX; /* XML_ERROR_UNCLOSED_IGNORE_SECTION */ default: + /* All of the tokens that XmlIgnoreSectionTok() returns have + * explicit cases to handle them, so this default case is never + * executed. We keep it as a safety net anyway, and remove it + * from our test coverage statistics. + * + * LCOV_EXCL_START + */ *eventPP = next; return XML_ERROR_UNEXPECTED_STATE; + /* LCOV_EXCL_STOP */ } /* not reached */ } @@ -3283,127 +4820,130 @@ doIgnoreSection(XML_Parser parser, #endif /* XML_DTD */ static enum XML_Error -initializeEncoding(XML_Parser parser) -{ +initializeEncoding(XML_Parser parser) { const char *s; #ifdef XML_UNICODE char encodingBuf[128]; - if (!protocolEncodingName) + /* See comments about `protocolEncodingName` in parserInit() */ + if (! parser->m_protocolEncodingName) s = NULL; else { int i; - for (i = 0; protocolEncodingName[i]; i++) { + for (i = 0; parser->m_protocolEncodingName[i]; i++) { if (i == sizeof(encodingBuf) - 1 - || (protocolEncodingName[i] & ~0x7f) != 0) { + || (parser->m_protocolEncodingName[i] & ~0x7f) != 0) { encodingBuf[0] = '\0'; break; } - encodingBuf[i] = (char)protocolEncodingName[i]; + encodingBuf[i] = (char)parser->m_protocolEncodingName[i]; } encodingBuf[i] = '\0'; s = encodingBuf; } #else - s = protocolEncodingName; + s = parser->m_protocolEncodingName; #endif - if ((ns ? XmlInitEncodingNS : XmlInitEncoding)(&initEncoding, &encoding, s)) + if ((parser->m_ns ? XmlInitEncodingNS : XmlInitEncoding)( + &parser->m_initEncoding, &parser->m_encoding, s)) return XML_ERROR_NONE; - return handleUnknownEncoding(parser, protocolEncodingName); + return handleUnknownEncoding(parser, parser->m_protocolEncodingName); } static enum XML_Error -processXmlDecl(XML_Parser parser, int isGeneralTextEntity, - const char *s, const char *next) -{ +processXmlDecl(XML_Parser parser, int isGeneralTextEntity, const char *s, + const char *next) { const char *encodingName = NULL; const XML_Char *storedEncName = NULL; const ENCODING *newEncoding = NULL; const char *version = NULL; - const char *versionend; + const char *versionend = NULL; const XML_Char *storedversion = NULL; int standalone = -1; - if (!(ns - ? XmlParseXmlDeclNS - : XmlParseXmlDecl)(isGeneralTextEntity, - encoding, - s, - next, - &eventPtr, - &version, - &versionend, - &encodingName, - &newEncoding, - &standalone)) { + +#if XML_GE == 1 + if (! accountingDiffTolerated(parser, XML_TOK_XML_DECL, s, next, __LINE__, + XML_ACCOUNT_DIRECT)) { + accountingOnAbort(parser); + return XML_ERROR_AMPLIFICATION_LIMIT_BREACH; + } +#endif + + if (! (parser->m_ns ? XmlParseXmlDeclNS : XmlParseXmlDecl)( + isGeneralTextEntity, parser->m_encoding, s, next, &parser->m_eventPtr, + &version, &versionend, &encodingName, &newEncoding, &standalone)) { if (isGeneralTextEntity) return XML_ERROR_TEXT_DECL; else return XML_ERROR_XML_DECL; } - if (!isGeneralTextEntity && standalone == 1) { - _dtd->standalone = XML_TRUE; + if (! isGeneralTextEntity && standalone == 1) { + parser->m_dtd->standalone = XML_TRUE; #ifdef XML_DTD - if (paramEntityParsing == XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE) - paramEntityParsing = XML_PARAM_ENTITY_PARSING_NEVER; + if (parser->m_paramEntityParsing + == XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE) + parser->m_paramEntityParsing = XML_PARAM_ENTITY_PARSING_NEVER; #endif /* XML_DTD */ } - if (xmlDeclHandler) { + if (parser->m_xmlDeclHandler) { if (encodingName != NULL) { - storedEncName = poolStoreString(&temp2Pool, - encoding, - encodingName, - encodingName - + XmlNameLength(encoding, encodingName)); - if (!storedEncName) - return XML_ERROR_NO_MEMORY; - poolFinish(&temp2Pool); + storedEncName = poolStoreString( + &parser->m_temp2Pool, parser->m_encoding, encodingName, + encodingName + XmlNameLength(parser->m_encoding, encodingName)); + if (! storedEncName) + return XML_ERROR_NO_MEMORY; + poolFinish(&parser->m_temp2Pool); } if (version) { - storedversion = poolStoreString(&temp2Pool, - encoding, - version, - versionend - encoding->minBytesPerChar); - if (!storedversion) + storedversion + = poolStoreString(&parser->m_temp2Pool, parser->m_encoding, version, + versionend - parser->m_encoding->minBytesPerChar); + if (! storedversion) return XML_ERROR_NO_MEMORY; } - xmlDeclHandler(handlerArg, storedversion, storedEncName, standalone); - } - else if (defaultHandler) - reportDefault(parser, encoding, s, next); - if (protocolEncodingName == NULL) { + parser->m_xmlDeclHandler(parser->m_handlerArg, storedversion, storedEncName, + standalone); + } else if (parser->m_defaultHandler) + reportDefault(parser, parser->m_encoding, s, next); + if (parser->m_protocolEncodingName == NULL) { if (newEncoding) { - if (newEncoding->minBytesPerChar != encoding->minBytesPerChar) { - eventPtr = encodingName; + /* Check that the specified encoding does not conflict with what + * the parser has already deduced. Do we have the same number + * of bytes in the smallest representation of a character? If + * this is UTF-16, is it the same endianness? + */ + if (newEncoding->minBytesPerChar != parser->m_encoding->minBytesPerChar + || (newEncoding->minBytesPerChar == 2 + && newEncoding != parser->m_encoding)) { + parser->m_eventPtr = encodingName; return XML_ERROR_INCORRECT_ENCODING; } - encoding = newEncoding; - } - else if (encodingName) { + parser->m_encoding = newEncoding; + } else if (encodingName) { enum XML_Error result; - if (!storedEncName) { + if (! storedEncName) { storedEncName = poolStoreString( - &temp2Pool, encoding, encodingName, - encodingName + XmlNameLength(encoding, encodingName)); - if (!storedEncName) + &parser->m_temp2Pool, parser->m_encoding, encodingName, + encodingName + XmlNameLength(parser->m_encoding, encodingName)); + if (! storedEncName) return XML_ERROR_NO_MEMORY; } result = handleUnknownEncoding(parser, storedEncName); - poolClear(&temp2Pool); + poolClear(&parser->m_temp2Pool); if (result == XML_ERROR_UNKNOWN_ENCODING) - eventPtr = encodingName; + parser->m_eventPtr = encodingName; return result; } } if (storedEncName || storedversion) - poolClear(&temp2Pool); + poolClear(&parser->m_temp2Pool); return XML_ERROR_NONE; } static enum XML_Error -handleUnknownEncoding(XML_Parser parser, const XML_Char *encodingName) -{ - if (unknownEncodingHandler) { +handleUnknownEncoding(XML_Parser parser, const XML_Char *encodingName) { + if (parser->m_unknownEncodingHandler) { XML_Encoding info; int i; for (i = 0; i < 256; i++) @@ -3411,25 +4951,21 @@ handleUnknownEncoding(XML_Parser parser, const XML_Char *encodingName) info.convert = NULL; info.data = NULL; info.release = NULL; - if (unknownEncodingHandler(unknownEncodingHandlerData, encodingName, - &info)) { + if (parser->m_unknownEncodingHandler(parser->m_unknownEncodingHandlerData, + encodingName, &info)) { ENCODING *enc; - unknownEncodingMem = MALLOC(XmlSizeOfUnknownEncoding()); - if (!unknownEncodingMem) { + parser->m_unknownEncodingMem = MALLOC(parser, XmlSizeOfUnknownEncoding()); + if (! parser->m_unknownEncodingMem) { if (info.release) info.release(info.data); return XML_ERROR_NO_MEMORY; } - enc = (ns - ? XmlInitUnknownEncodingNS - : XmlInitUnknownEncoding)(unknownEncodingMem, - info.map, - info.convert, - info.data); + enc = (parser->m_ns ? XmlInitUnknownEncodingNS : XmlInitUnknownEncoding)( + parser->m_unknownEncodingMem, info.map, info.convert, info.data); if (enc) { - unknownEncodingData = info.data; - unknownEncodingRelease = info.release; - encoding = enc; + parser->m_unknownEncodingData = info.data; + parser->m_unknownEncodingRelease = info.release; + parser->m_encoding = enc; return XML_ERROR_NONE; } } @@ -3440,60 +4976,54 @@ handleUnknownEncoding(XML_Parser parser, const XML_Char *encodingName) } static enum XML_Error PTRCALL -prologInitProcessor(XML_Parser parser, - const char *s, - const char *end, - const char **nextPtr) -{ +prologInitProcessor(XML_Parser parser, const char *s, const char *end, + const char **nextPtr) { enum XML_Error result = initializeEncoding(parser); if (result != XML_ERROR_NONE) return result; - processor = prologProcessor; + parser->m_processor = prologProcessor; return prologProcessor(parser, s, end, nextPtr); } #ifdef XML_DTD static enum XML_Error PTRCALL -externalParEntInitProcessor(XML_Parser parser, - const char *s, - const char *end, - const char **nextPtr) -{ +externalParEntInitProcessor(XML_Parser parser, const char *s, const char *end, + const char **nextPtr) { enum XML_Error result = initializeEncoding(parser); if (result != XML_ERROR_NONE) return result; /* we know now that XML_Parse(Buffer) has been called, so we consider the external parameter entity read */ - _dtd->paramEntityRead = XML_TRUE; + parser->m_dtd->paramEntityRead = XML_TRUE; - if (prologState.inEntityValue) { - processor = entityValueInitProcessor; + if (parser->m_prologState.inEntityValue) { + parser->m_processor = entityValueInitProcessor; return entityValueInitProcessor(parser, s, end, nextPtr); - } - else { - processor = externalParEntProcessor; + } else { + parser->m_processor = externalParEntProcessor; return externalParEntProcessor(parser, s, end, nextPtr); } } static enum XML_Error PTRCALL -entityValueInitProcessor(XML_Parser parser, - const char *s, - const char *end, - const char **nextPtr) -{ +entityValueInitProcessor(XML_Parser parser, const char *s, const char *end, + const char **nextPtr) { int tok; const char *start = s; const char *next = start; - eventPtr = start; + parser->m_eventPtr = start; - for (;;) { - tok = XmlPrologTok(encoding, start, end, &next); - eventEndPtr = next; + for (;;) { + tok = XmlPrologTok(parser->m_encoding, start, end, &next); + /* Note: Except for XML_TOK_BOM below, these bytes are accounted later in: + - storeEntityValue + - processXmlDecl + */ + parser->m_eventEndPtr = next; if (tok <= 0) { - if (!ps_finalBuffer && tok != XML_TOK_INVALID) { + if (! parser->m_parsingStatus.finalBuffer && tok != XML_TOK_INVALID) { *nextPtr = s; return XML_ERROR_NONE; } @@ -3504,59 +5034,72 @@ entityValueInitProcessor(XML_Parser parser, return XML_ERROR_UNCLOSED_TOKEN; case XML_TOK_PARTIAL_CHAR: return XML_ERROR_PARTIAL_CHAR; - case XML_TOK_NONE: /* start == end */ + case XML_TOK_NONE: /* start == end */ default: break; } /* found end of entity value - can store it now */ - return storeEntityValue(parser, encoding, s, end); - } - else if (tok == XML_TOK_XML_DECL) { + return storeEntityValue(parser, parser->m_encoding, s, end, + XML_ACCOUNT_DIRECT, NULL); + } else if (tok == XML_TOK_XML_DECL) { enum XML_Error result; result = processXmlDecl(parser, 0, start, next); if (result != XML_ERROR_NONE) return result; - switch (ps_parsing) { - case XML_SUSPENDED: - *nextPtr = next; - return XML_ERROR_NONE; - case XML_FINISHED: + /* At this point, m_parsingStatus.parsing cannot be XML_SUSPENDED. For + * that to happen, a parameter entity parsing handler must have attempted + * to suspend the parser, which fails and raises an error. The parser can + * be aborted, but can't be suspended. + */ + if (parser->m_parsingStatus.parsing == XML_FINISHED) return XML_ERROR_ABORTED; - default: - *nextPtr = next; - } + *nextPtr = next; /* stop scanning for text declaration - we found one */ - processor = entityValueProcessor; + parser->m_processor = entityValueProcessor; return entityValueProcessor(parser, next, end, nextPtr); } - /* If we are at the end of the buffer, this would cause XmlPrologTok to - return XML_TOK_NONE on the next call, which would then cause the - function to exit with *nextPtr set to s - that is what we want for other - tokens, but not for the BOM - we would rather like to skip it; - then, when this routine is entered the next time, XmlPrologTok will - return XML_TOK_INVALID, since the BOM is still in the buffer + /* XmlPrologTok has now set the encoding based on the BOM it found, and we + must move s and nextPtr forward to consume the BOM. + + If we didn't, and got XML_TOK_NONE from the next XmlPrologTok call, we + would leave the BOM in the buffer and return. On the next call to this + function, our XmlPrologTok call would return XML_TOK_INVALID, since it + is not valid to have multiple BOMs. */ - else if (tok == XML_TOK_BOM && next == end && !ps_finalBuffer) { + else if (tok == XML_TOK_BOM) { +# if XML_GE == 1 + if (! accountingDiffTolerated(parser, tok, s, next, __LINE__, + XML_ACCOUNT_DIRECT)) { + accountingOnAbort(parser); + return XML_ERROR_AMPLIFICATION_LIMIT_BREACH; + } +# endif + *nextPtr = next; - return XML_ERROR_NONE; + s = next; + } + /* If we get this token, we have the start of what might be a + normal tag, but not a declaration (i.e. it doesn't begin with + "m_eventPtr = start; } } static enum XML_Error PTRCALL -externalParEntProcessor(XML_Parser parser, - const char *s, - const char *end, - const char **nextPtr) -{ +externalParEntProcessor(XML_Parser parser, const char *s, const char *end, + const char **nextPtr) { const char *next = s; int tok; - tok = XmlPrologTok(encoding, s, end, &next); + tok = XmlPrologTok(parser->m_encoding, s, end, &next); if (tok <= 0) { - if (!ps_finalBuffer && tok != XML_TOK_INVALID) { + if (! parser->m_parsingStatus.finalBuffer && tok != XML_TOK_INVALID) { *nextPtr = s; return XML_ERROR_NONE; } @@ -3567,40 +5110,48 @@ externalParEntProcessor(XML_Parser parser, return XML_ERROR_UNCLOSED_TOKEN; case XML_TOK_PARTIAL_CHAR: return XML_ERROR_PARTIAL_CHAR; - case XML_TOK_NONE: /* start == end */ + case XML_TOK_NONE: /* start == end */ default: break; } } /* This would cause the next stage, i.e. doProlog to be passed XML_TOK_BOM. However, when parsing an external subset, doProlog will not accept a BOM - as valid, and report a syntax error, so we have to skip the BOM + as valid, and report a syntax error, so we have to skip the BOM, and + account for the BOM bytes. */ else if (tok == XML_TOK_BOM) { + if (! accountingDiffTolerated(parser, tok, s, next, __LINE__, + XML_ACCOUNT_DIRECT)) { + accountingOnAbort(parser); + return XML_ERROR_AMPLIFICATION_LIMIT_BREACH; + } + s = next; - tok = XmlPrologTok(encoding, s, end, &next); + tok = XmlPrologTok(parser->m_encoding, s, end, &next); } - processor = prologProcessor; - return doProlog(parser, encoding, s, end, tok, next, - nextPtr, (XML_Bool)!ps_finalBuffer); + parser->m_processor = prologProcessor; + return doProlog(parser, parser->m_encoding, s, end, tok, next, nextPtr, + (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_TRUE, + XML_ACCOUNT_DIRECT); } static enum XML_Error PTRCALL -entityValueProcessor(XML_Parser parser, - const char *s, - const char *end, - const char **nextPtr) -{ +entityValueProcessor(XML_Parser parser, const char *s, const char *end, + const char **nextPtr) { const char *start = s; const char *next = s; - const ENCODING *enc = encoding; + const ENCODING *enc = parser->m_encoding; int tok; for (;;) { tok = XmlPrologTok(enc, start, end, &next); + /* Note: These bytes are accounted later in: + - storeEntityValue + */ if (tok <= 0) { - if (!ps_finalBuffer && tok != XML_TOK_INVALID) { + if (! parser->m_parsingStatus.finalBuffer && tok != XML_TOK_INVALID) { *nextPtr = s; return XML_ERROR_NONE; } @@ -3611,12 +5162,12 @@ entityValueProcessor(XML_Parser parser, return XML_ERROR_UNCLOSED_TOKEN; case XML_TOK_PARTIAL_CHAR: return XML_ERROR_PARTIAL_CHAR; - case XML_TOK_NONE: /* start == end */ + case XML_TOK_NONE: /* start == end */ default: break; } /* found end of entity value - can store it now */ - return storeEntityValue(parser, enc, s, end); + return storeEntityValue(parser, enc, s, end, XML_ACCOUNT_DIRECT, NULL); } start = next; } @@ -3625,64 +5176,62 @@ entityValueProcessor(XML_Parser parser, #endif /* XML_DTD */ static enum XML_Error PTRCALL -prologProcessor(XML_Parser parser, - const char *s, - const char *end, - const char **nextPtr) -{ +prologProcessor(XML_Parser parser, const char *s, const char *end, + const char **nextPtr) { const char *next = s; - int tok = XmlPrologTok(encoding, s, end, &next); - return doProlog(parser, encoding, s, end, tok, next, - nextPtr, (XML_Bool)!ps_finalBuffer); + int tok = XmlPrologTok(parser->m_encoding, s, end, &next); + return doProlog(parser, parser->m_encoding, s, end, tok, next, nextPtr, + (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_TRUE, + XML_ACCOUNT_DIRECT); } static enum XML_Error -doProlog(XML_Parser parser, - const ENCODING *enc, - const char *s, - const char *end, - int tok, - const char *next, - const char **nextPtr, - XML_Bool haveMore) -{ +doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end, + int tok, const char *next, const char **nextPtr, XML_Bool haveMore, + XML_Bool allowClosingDoctype, enum XML_Account account) { #ifdef XML_DTD - static const XML_Char externalSubsetName[] = { ASCII_HASH , '\0' }; + static const XML_Char externalSubsetName[] = {ASCII_HASH, '\0'}; #endif /* XML_DTD */ - static const XML_Char atypeCDATA[] = - { ASCII_C, ASCII_D, ASCII_A, ASCII_T, ASCII_A, '\0' }; - static const XML_Char atypeID[] = { ASCII_I, ASCII_D, '\0' }; - static const XML_Char atypeIDREF[] = - { ASCII_I, ASCII_D, ASCII_R, ASCII_E, ASCII_F, '\0' }; - static const XML_Char atypeIDREFS[] = - { ASCII_I, ASCII_D, ASCII_R, ASCII_E, ASCII_F, ASCII_S, '\0' }; - static const XML_Char atypeENTITY[] = - { ASCII_E, ASCII_N, ASCII_T, ASCII_I, ASCII_T, ASCII_Y, '\0' }; - static const XML_Char atypeENTITIES[] = { ASCII_E, ASCII_N, - ASCII_T, ASCII_I, ASCII_T, ASCII_I, ASCII_E, ASCII_S, '\0' }; - static const XML_Char atypeNMTOKEN[] = { - ASCII_N, ASCII_M, ASCII_T, ASCII_O, ASCII_K, ASCII_E, ASCII_N, '\0' }; - static const XML_Char atypeNMTOKENS[] = { ASCII_N, ASCII_M, ASCII_T, - ASCII_O, ASCII_K, ASCII_E, ASCII_N, ASCII_S, '\0' }; - static const XML_Char notationPrefix[] = { ASCII_N, ASCII_O, ASCII_T, - ASCII_A, ASCII_T, ASCII_I, ASCII_O, ASCII_N, ASCII_LPAREN, '\0' }; - static const XML_Char enumValueSep[] = { ASCII_PIPE, '\0' }; - static const XML_Char enumValueStart[] = { ASCII_LPAREN, '\0' }; + static const XML_Char atypeCDATA[] + = {ASCII_C, ASCII_D, ASCII_A, ASCII_T, ASCII_A, '\0'}; + static const XML_Char atypeID[] = {ASCII_I, ASCII_D, '\0'}; + static const XML_Char atypeIDREF[] + = {ASCII_I, ASCII_D, ASCII_R, ASCII_E, ASCII_F, '\0'}; + static const XML_Char atypeIDREFS[] + = {ASCII_I, ASCII_D, ASCII_R, ASCII_E, ASCII_F, ASCII_S, '\0'}; + static const XML_Char atypeENTITY[] + = {ASCII_E, ASCII_N, ASCII_T, ASCII_I, ASCII_T, ASCII_Y, '\0'}; + static const XML_Char atypeENTITIES[] + = {ASCII_E, ASCII_N, ASCII_T, ASCII_I, ASCII_T, + ASCII_I, ASCII_E, ASCII_S, '\0'}; + static const XML_Char atypeNMTOKEN[] + = {ASCII_N, ASCII_M, ASCII_T, ASCII_O, ASCII_K, ASCII_E, ASCII_N, '\0'}; + static const XML_Char atypeNMTOKENS[] + = {ASCII_N, ASCII_M, ASCII_T, ASCII_O, ASCII_K, + ASCII_E, ASCII_N, ASCII_S, '\0'}; + static const XML_Char notationPrefix[] + = {ASCII_N, ASCII_O, ASCII_T, ASCII_A, ASCII_T, + ASCII_I, ASCII_O, ASCII_N, ASCII_LPAREN, '\0'}; + static const XML_Char enumValueSep[] = {ASCII_PIPE, '\0'}; + static const XML_Char enumValueStart[] = {ASCII_LPAREN, '\0'}; + +#ifndef XML_DTD + UNUSED_P(account); +#endif /* save one level of indirection */ - DTD * const dtd = _dtd; + DTD *const dtd = parser->m_dtd; const char **eventPP; const char **eventEndPP; enum XML_Content_Quant quant; - if (enc == encoding) { - eventPP = &eventPtr; - eventEndPP = &eventEndPtr; - } - else { - eventPP = &(openInternalEntities->internalEventPtr); - eventEndPP = &(openInternalEntities->internalEventEndPtr); + if (enc == parser->m_encoding) { + eventPP = &parser->m_eventPtr; + eventEndPP = &parser->m_eventEndPtr; + } else { + eventPP = &(parser->m_openInternalEntities->internalEventPtr); + eventEndPP = &(parser->m_openInternalEntities->internalEventEndPtr); } for (;;) { @@ -3703,10 +5252,14 @@ doProlog(XML_Parser parser, return XML_ERROR_UNCLOSED_TOKEN; case XML_TOK_PARTIAL_CHAR: return XML_ERROR_PARTIAL_CHAR; + case -XML_TOK_PROLOG_S: + tok = -tok; + break; case XML_TOK_NONE: #ifdef XML_DTD /* for internal PE NOT referenced between declarations */ - if (enc != encoding && !openInternalEntities->betweenDecl) { + if (enc != parser->m_encoding + && ! parser->m_openInternalEntities->betweenDecl) { *nextPtr = s; return XML_ERROR_NONE; } @@ -3714,8 +5267,8 @@ doProlog(XML_Parser parser, complete markup, not only for external PEs, but also for internal PEs if the reference occurs between declarations. */ - if (isParamEntity || enc != encoding) { - if (XmlTokenRole(&prologState, XML_TOK_NONE, end, end, enc) + if (parser->m_isParamEntity || enc != parser->m_encoding) { + if (XmlTokenRole(&parser->m_prologState, XML_TOK_NONE, end, end, enc) == XML_ROLE_ERROR) return XML_ERROR_INCOMPLETE_PE; *nextPtr = s; @@ -3729,138 +5282,163 @@ doProlog(XML_Parser parser, break; } } - role = XmlTokenRole(&prologState, tok, s, next, enc); + role = XmlTokenRole(&parser->m_prologState, tok, s, next, enc); +#if XML_GE == 1 switch (role) { - case XML_ROLE_XML_DECL: - { - enum XML_Error result = processXmlDecl(parser, 0, s, next); - if (result != XML_ERROR_NONE) - return result; - enc = encoding; - handleDefault = XML_FALSE; - } + case XML_ROLE_INSTANCE_START: // bytes accounted in contentProcessor + case XML_ROLE_XML_DECL: // bytes accounted in processXmlDecl +# ifdef XML_DTD + case XML_ROLE_TEXT_DECL: // bytes accounted in processXmlDecl +# endif break; + default: + if (! accountingDiffTolerated(parser, tok, s, next, __LINE__, account)) { + accountingOnAbort(parser); + return XML_ERROR_AMPLIFICATION_LIMIT_BREACH; + } + } +#endif + switch (role) { + case XML_ROLE_XML_DECL: { + enum XML_Error result = processXmlDecl(parser, 0, s, next); + if (result != XML_ERROR_NONE) + return result; + enc = parser->m_encoding; + handleDefault = XML_FALSE; + } break; case XML_ROLE_DOCTYPE_NAME: - if (startDoctypeDeclHandler) { - doctypeName = poolStoreString(&tempPool, enc, s, next); - if (!doctypeName) + if (parser->m_startDoctypeDeclHandler) { + parser->m_doctypeName + = poolStoreString(&parser->m_tempPool, enc, s, next); + if (! parser->m_doctypeName) return XML_ERROR_NO_MEMORY; - poolFinish(&tempPool); - doctypePubid = NULL; + poolFinish(&parser->m_tempPool); + parser->m_doctypePubid = NULL; handleDefault = XML_FALSE; } - doctypeSysid = NULL; /* always initialize to NULL */ + parser->m_doctypeSysid = NULL; /* always initialize to NULL */ break; case XML_ROLE_DOCTYPE_INTERNAL_SUBSET: - if (startDoctypeDeclHandler) { - startDoctypeDeclHandler(handlerArg, doctypeName, doctypeSysid, - doctypePubid, 1); - doctypeName = NULL; - poolClear(&tempPool); + if (parser->m_startDoctypeDeclHandler) { + parser->m_startDoctypeDeclHandler( + parser->m_handlerArg, parser->m_doctypeName, parser->m_doctypeSysid, + parser->m_doctypePubid, 1); + parser->m_doctypeName = NULL; + poolClear(&parser->m_tempPool); handleDefault = XML_FALSE; } break; #ifdef XML_DTD - case XML_ROLE_TEXT_DECL: - { - enum XML_Error result = processXmlDecl(parser, 1, s, next); - if (result != XML_ERROR_NONE) - return result; - enc = encoding; - handleDefault = XML_FALSE; - } - break; + case XML_ROLE_TEXT_DECL: { + enum XML_Error result = processXmlDecl(parser, 1, s, next); + if (result != XML_ERROR_NONE) + return result; + enc = parser->m_encoding; + handleDefault = XML_FALSE; + } break; #endif /* XML_DTD */ case XML_ROLE_DOCTYPE_PUBLIC_ID: #ifdef XML_DTD - useForeignDTD = XML_FALSE; - declEntity = (ENTITY *)lookup(&dtd->paramEntities, - externalSubsetName, - sizeof(ENTITY)); - if (!declEntity) + parser->m_useForeignDTD = XML_FALSE; + parser->m_declEntity = (ENTITY *)lookup( + parser, &dtd->paramEntities, externalSubsetName, sizeof(ENTITY)); + if (! parser->m_declEntity) return XML_ERROR_NO_MEMORY; #endif /* XML_DTD */ dtd->hasParamEntityRefs = XML_TRUE; - if (startDoctypeDeclHandler) { - if (!XmlIsPublicId(enc, s, next, eventPP)) + if (parser->m_startDoctypeDeclHandler) { + XML_Char *pubId; + if (! XmlIsPublicId(enc, s, next, eventPP)) return XML_ERROR_PUBLICID; - doctypePubid = poolStoreString(&tempPool, enc, - s + enc->minBytesPerChar, - next - enc->minBytesPerChar); - if (!doctypePubid) + pubId = poolStoreString(&parser->m_tempPool, enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (! pubId) return XML_ERROR_NO_MEMORY; - normalizePublicId((XML_Char *)doctypePubid); - poolFinish(&tempPool); + normalizePublicId(pubId); + poolFinish(&parser->m_tempPool); + parser->m_doctypePubid = pubId; handleDefault = XML_FALSE; goto alreadyChecked; } /* fall through */ case XML_ROLE_ENTITY_PUBLIC_ID: - if (!XmlIsPublicId(enc, s, next, eventPP)) + if (! XmlIsPublicId(enc, s, next, eventPP)) return XML_ERROR_PUBLICID; alreadyChecked: - if (dtd->keepProcessing && declEntity) { - XML_Char *tem = poolStoreString(&dtd->pool, - enc, - s + enc->minBytesPerChar, - next - enc->minBytesPerChar); - if (!tem) + if (dtd->keepProcessing && parser->m_declEntity) { + XML_Char *tem + = poolStoreString(&dtd->pool, enc, s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (! tem) return XML_ERROR_NO_MEMORY; normalizePublicId(tem); - declEntity->publicId = tem; + parser->m_declEntity->publicId = tem; poolFinish(&dtd->pool); - if (entityDeclHandler) + /* Don't suppress the default handler if we fell through from + * the XML_ROLE_DOCTYPE_PUBLIC_ID case. + */ + if (parser->m_entityDeclHandler && role == XML_ROLE_ENTITY_PUBLIC_ID) handleDefault = XML_FALSE; } break; case XML_ROLE_DOCTYPE_CLOSE: - if (doctypeName) { - startDoctypeDeclHandler(handlerArg, doctypeName, - doctypeSysid, doctypePubid, 0); - poolClear(&tempPool); + if (allowClosingDoctype != XML_TRUE) { + /* Must not close doctype from within expanded parameter entities */ + return XML_ERROR_INVALID_TOKEN; + } + + if (parser->m_doctypeName) { + parser->m_startDoctypeDeclHandler( + parser->m_handlerArg, parser->m_doctypeName, parser->m_doctypeSysid, + parser->m_doctypePubid, 0); + poolClear(&parser->m_tempPool); handleDefault = XML_FALSE; } - /* doctypeSysid will be non-NULL in the case of a previous - XML_ROLE_DOCTYPE_SYSTEM_ID, even if startDoctypeDeclHandler + /* parser->m_doctypeSysid will be non-NULL in the case of a previous + XML_ROLE_DOCTYPE_SYSTEM_ID, even if parser->m_startDoctypeDeclHandler was not set, indicating an external subset */ #ifdef XML_DTD - if (doctypeSysid || useForeignDTD) { + if (parser->m_doctypeSysid || parser->m_useForeignDTD) { XML_Bool hadParamEntityRefs = dtd->hasParamEntityRefs; dtd->hasParamEntityRefs = XML_TRUE; - if (paramEntityParsing && externalEntityRefHandler) { - ENTITY *entity = (ENTITY *)lookup(&dtd->paramEntities, - externalSubsetName, - sizeof(ENTITY)); - if (!entity) - return XML_ERROR_NO_MEMORY; - if (useForeignDTD) - entity->base = curBase; + if (parser->m_paramEntityParsing + && parser->m_externalEntityRefHandler) { + ENTITY *entity = (ENTITY *)lookup(parser, &dtd->paramEntities, + externalSubsetName, sizeof(ENTITY)); + if (! entity) { + /* The external subset name "#" will have already been + * inserted into the hash table at the start of the + * external entity parsing, so no allocation will happen + * and lookup() cannot fail. + */ + return XML_ERROR_NO_MEMORY; /* LCOV_EXCL_LINE */ + } + if (parser->m_useForeignDTD) + entity->base = parser->m_curBase; dtd->paramEntityRead = XML_FALSE; - if (!externalEntityRefHandler(externalEntityRefHandlerArg, - 0, - entity->base, - entity->systemId, - entity->publicId)) + if (! parser->m_externalEntityRefHandler( + parser->m_externalEntityRefHandlerArg, 0, entity->base, + entity->systemId, entity->publicId)) return XML_ERROR_EXTERNAL_ENTITY_HANDLING; if (dtd->paramEntityRead) { - if (!dtd->standalone && - notStandaloneHandler && - !notStandaloneHandler(handlerArg)) + if (! dtd->standalone && parser->m_notStandaloneHandler + && ! parser->m_notStandaloneHandler(parser->m_handlerArg)) return XML_ERROR_NOT_STANDALONE; } /* if we didn't read the foreign DTD then this means that there is no external subset and we must reset dtd->hasParamEntityRefs */ - else if (!doctypeSysid) + else if (! parser->m_doctypeSysid) dtd->hasParamEntityRefs = hadParamEntityRefs; /* end of DTD - no need to update dtd->keepProcessing */ } - useForeignDTD = XML_FALSE; + parser->m_useForeignDTD = XML_FALSE; } #endif /* XML_DTD */ - if (endDoctypeDeclHandler) { - endDoctypeDeclHandler(handlerArg); + if (parser->m_endDoctypeDeclHandler) { + parser->m_endDoctypeDeclHandler(parser->m_handlerArg); handleDefault = XML_FALSE; } break; @@ -3869,27 +5447,24 @@ doProlog(XML_Parser parser, /* if there is no DOCTYPE declaration then now is the last chance to read the foreign DTD */ - if (useForeignDTD) { + if (parser->m_useForeignDTD) { XML_Bool hadParamEntityRefs = dtd->hasParamEntityRefs; dtd->hasParamEntityRefs = XML_TRUE; - if (paramEntityParsing && externalEntityRefHandler) { - ENTITY *entity = (ENTITY *)lookup(&dtd->paramEntities, - externalSubsetName, - sizeof(ENTITY)); - if (!entity) + if (parser->m_paramEntityParsing + && parser->m_externalEntityRefHandler) { + ENTITY *entity = (ENTITY *)lookup(parser, &dtd->paramEntities, + externalSubsetName, sizeof(ENTITY)); + if (! entity) return XML_ERROR_NO_MEMORY; - entity->base = curBase; + entity->base = parser->m_curBase; dtd->paramEntityRead = XML_FALSE; - if (!externalEntityRefHandler(externalEntityRefHandlerArg, - 0, - entity->base, - entity->systemId, - entity->publicId)) + if (! parser->m_externalEntityRefHandler( + parser->m_externalEntityRefHandlerArg, 0, entity->base, + entity->systemId, entity->publicId)) return XML_ERROR_EXTERNAL_ENTITY_HANDLING; if (dtd->paramEntityRead) { - if (!dtd->standalone && - notStandaloneHandler && - !notStandaloneHandler(handlerArg)) + if (! dtd->standalone && parser->m_notStandaloneHandler + && ! parser->m_notStandaloneHandler(parser->m_handlerArg)) return XML_ERROR_NOT_STANDALONE; } /* if we didn't read the foreign DTD then this means that there @@ -3901,389 +5476,405 @@ doProlog(XML_Parser parser, } } #endif /* XML_DTD */ - processor = contentProcessor; + parser->m_processor = contentProcessor; return contentProcessor(parser, s, end, nextPtr); case XML_ROLE_ATTLIST_ELEMENT_NAME: - declElementType = getElementType(parser, enc, s, next); - if (!declElementType) + parser->m_declElementType = getElementType(parser, enc, s, next); + if (! parser->m_declElementType) return XML_ERROR_NO_MEMORY; goto checkAttListDeclHandler; case XML_ROLE_ATTRIBUTE_NAME: - declAttributeId = getAttributeId(parser, enc, s, next); - if (!declAttributeId) + parser->m_declAttributeId = getAttributeId(parser, enc, s, next); + if (! parser->m_declAttributeId) return XML_ERROR_NO_MEMORY; - declAttributeIsCdata = XML_FALSE; - declAttributeType = NULL; - declAttributeIsId = XML_FALSE; + parser->m_declAttributeIsCdata = XML_FALSE; + parser->m_declAttributeType = NULL; + parser->m_declAttributeIsId = XML_FALSE; goto checkAttListDeclHandler; case XML_ROLE_ATTRIBUTE_TYPE_CDATA: - declAttributeIsCdata = XML_TRUE; - declAttributeType = atypeCDATA; + parser->m_declAttributeIsCdata = XML_TRUE; + parser->m_declAttributeType = atypeCDATA; goto checkAttListDeclHandler; case XML_ROLE_ATTRIBUTE_TYPE_ID: - declAttributeIsId = XML_TRUE; - declAttributeType = atypeID; + parser->m_declAttributeIsId = XML_TRUE; + parser->m_declAttributeType = atypeID; goto checkAttListDeclHandler; case XML_ROLE_ATTRIBUTE_TYPE_IDREF: - declAttributeType = atypeIDREF; + parser->m_declAttributeType = atypeIDREF; goto checkAttListDeclHandler; case XML_ROLE_ATTRIBUTE_TYPE_IDREFS: - declAttributeType = atypeIDREFS; + parser->m_declAttributeType = atypeIDREFS; goto checkAttListDeclHandler; case XML_ROLE_ATTRIBUTE_TYPE_ENTITY: - declAttributeType = atypeENTITY; + parser->m_declAttributeType = atypeENTITY; goto checkAttListDeclHandler; case XML_ROLE_ATTRIBUTE_TYPE_ENTITIES: - declAttributeType = atypeENTITIES; + parser->m_declAttributeType = atypeENTITIES; goto checkAttListDeclHandler; case XML_ROLE_ATTRIBUTE_TYPE_NMTOKEN: - declAttributeType = atypeNMTOKEN; + parser->m_declAttributeType = atypeNMTOKEN; goto checkAttListDeclHandler; case XML_ROLE_ATTRIBUTE_TYPE_NMTOKENS: - declAttributeType = atypeNMTOKENS; + parser->m_declAttributeType = atypeNMTOKENS; checkAttListDeclHandler: - if (dtd->keepProcessing && attlistDeclHandler) + if (dtd->keepProcessing && parser->m_attlistDeclHandler) handleDefault = XML_FALSE; break; case XML_ROLE_ATTRIBUTE_ENUM_VALUE: case XML_ROLE_ATTRIBUTE_NOTATION_VALUE: - if (dtd->keepProcessing && attlistDeclHandler) { + if (dtd->keepProcessing && parser->m_attlistDeclHandler) { const XML_Char *prefix; - if (declAttributeType) { + if (parser->m_declAttributeType) { prefix = enumValueSep; + } else { + prefix = (role == XML_ROLE_ATTRIBUTE_NOTATION_VALUE ? notationPrefix + : enumValueStart); } - else { - prefix = (role == XML_ROLE_ATTRIBUTE_NOTATION_VALUE - ? notationPrefix - : enumValueStart); - } - if (!poolAppendString(&tempPool, prefix)) + if (! poolAppendString(&parser->m_tempPool, prefix)) return XML_ERROR_NO_MEMORY; - if (!poolAppend(&tempPool, enc, s, next)) + if (! poolAppend(&parser->m_tempPool, enc, s, next)) return XML_ERROR_NO_MEMORY; - declAttributeType = tempPool.start; + parser->m_declAttributeType = parser->m_tempPool.start; handleDefault = XML_FALSE; } break; case XML_ROLE_IMPLIED_ATTRIBUTE_VALUE: case XML_ROLE_REQUIRED_ATTRIBUTE_VALUE: if (dtd->keepProcessing) { - if (!defineAttribute(declElementType, declAttributeId, - declAttributeIsCdata, declAttributeIsId, - 0, parser)) + if (! defineAttribute(parser->m_declElementType, + parser->m_declAttributeId, + parser->m_declAttributeIsCdata, + parser->m_declAttributeIsId, 0, parser)) return XML_ERROR_NO_MEMORY; - if (attlistDeclHandler && declAttributeType) { - if (*declAttributeType == XML_T(ASCII_LPAREN) - || (*declAttributeType == XML_T(ASCII_N) - && declAttributeType[1] == XML_T(ASCII_O))) { + if (parser->m_attlistDeclHandler && parser->m_declAttributeType) { + if (*parser->m_declAttributeType == XML_T(ASCII_LPAREN) + || (*parser->m_declAttributeType == XML_T(ASCII_N) + && parser->m_declAttributeType[1] == XML_T(ASCII_O))) { /* Enumerated or Notation type */ - if (!poolAppendChar(&tempPool, XML_T(ASCII_RPAREN)) - || !poolAppendChar(&tempPool, XML_T('\0'))) + if (! poolAppendChar(&parser->m_tempPool, XML_T(ASCII_RPAREN)) + || ! poolAppendChar(&parser->m_tempPool, XML_T('\0'))) return XML_ERROR_NO_MEMORY; - declAttributeType = tempPool.start; - poolFinish(&tempPool); + parser->m_declAttributeType = parser->m_tempPool.start; + poolFinish(&parser->m_tempPool); } *eventEndPP = s; - attlistDeclHandler(handlerArg, declElementType->name, - declAttributeId->name, declAttributeType, - 0, role == XML_ROLE_REQUIRED_ATTRIBUTE_VALUE); - poolClear(&tempPool); + parser->m_attlistDeclHandler( + parser->m_handlerArg, parser->m_declElementType->name, + parser->m_declAttributeId->name, parser->m_declAttributeType, 0, + role == XML_ROLE_REQUIRED_ATTRIBUTE_VALUE); handleDefault = XML_FALSE; } } + poolClear(&parser->m_tempPool); break; case XML_ROLE_DEFAULT_ATTRIBUTE_VALUE: case XML_ROLE_FIXED_ATTRIBUTE_VALUE: if (dtd->keepProcessing) { const XML_Char *attVal; - enum XML_Error result = - storeAttributeValue(parser, enc, declAttributeIsCdata, - s + enc->minBytesPerChar, - next - enc->minBytesPerChar, - &dtd->pool); + enum XML_Error result = storeAttributeValue( + parser, enc, parser->m_declAttributeIsCdata, + s + enc->minBytesPerChar, next - enc->minBytesPerChar, &dtd->pool, + XML_ACCOUNT_NONE); if (result) return result; attVal = poolStart(&dtd->pool); poolFinish(&dtd->pool); /* ID attributes aren't allowed to have a default */ - if (!defineAttribute(declElementType, declAttributeId, - declAttributeIsCdata, XML_FALSE, attVal, parser)) + if (! defineAttribute( + parser->m_declElementType, parser->m_declAttributeId, + parser->m_declAttributeIsCdata, XML_FALSE, attVal, parser)) return XML_ERROR_NO_MEMORY; - if (attlistDeclHandler && declAttributeType) { - if (*declAttributeType == XML_T(ASCII_LPAREN) - || (*declAttributeType == XML_T(ASCII_N) - && declAttributeType[1] == XML_T(ASCII_O))) { + if (parser->m_attlistDeclHandler && parser->m_declAttributeType) { + if (*parser->m_declAttributeType == XML_T(ASCII_LPAREN) + || (*parser->m_declAttributeType == XML_T(ASCII_N) + && parser->m_declAttributeType[1] == XML_T(ASCII_O))) { /* Enumerated or Notation type */ - if (!poolAppendChar(&tempPool, XML_T(ASCII_RPAREN)) - || !poolAppendChar(&tempPool, XML_T('\0'))) + if (! poolAppendChar(&parser->m_tempPool, XML_T(ASCII_RPAREN)) + || ! poolAppendChar(&parser->m_tempPool, XML_T('\0'))) return XML_ERROR_NO_MEMORY; - declAttributeType = tempPool.start; - poolFinish(&tempPool); + parser->m_declAttributeType = parser->m_tempPool.start; + poolFinish(&parser->m_tempPool); } *eventEndPP = s; - attlistDeclHandler(handlerArg, declElementType->name, - declAttributeId->name, declAttributeType, - attVal, - role == XML_ROLE_FIXED_ATTRIBUTE_VALUE); - poolClear(&tempPool); + parser->m_attlistDeclHandler( + parser->m_handlerArg, parser->m_declElementType->name, + parser->m_declAttributeId->name, parser->m_declAttributeType, + attVal, role == XML_ROLE_FIXED_ATTRIBUTE_VALUE); + poolClear(&parser->m_tempPool); handleDefault = XML_FALSE; } } break; case XML_ROLE_ENTITY_VALUE: if (dtd->keepProcessing) { - enum XML_Error result = storeEntityValue(parser, enc, - s + enc->minBytesPerChar, - next - enc->minBytesPerChar); - if (declEntity) { - declEntity->textPtr = poolStart(&dtd->entityValuePool); - declEntity->textLen = (int)(poolLength(&dtd->entityValuePool)); +#if XML_GE == 1 + // This will store the given replacement text in + // parser->m_declEntity->textPtr. + enum XML_Error result = callStoreEntityValue( + parser, enc, s + enc->minBytesPerChar, next - enc->minBytesPerChar, + XML_ACCOUNT_NONE); + if (parser->m_declEntity) { + parser->m_declEntity->textPtr = poolStart(&dtd->entityValuePool); + parser->m_declEntity->textLen + = (int)(poolLength(&dtd->entityValuePool)); poolFinish(&dtd->entityValuePool); - if (entityDeclHandler) { + if (parser->m_entityDeclHandler) { *eventEndPP = s; - entityDeclHandler(handlerArg, - declEntity->name, - declEntity->is_param, - declEntity->textPtr, - declEntity->textLen, - curBase, 0, 0, 0); + parser->m_entityDeclHandler( + parser->m_handlerArg, parser->m_declEntity->name, + parser->m_declEntity->is_param, parser->m_declEntity->textPtr, + parser->m_declEntity->textLen, parser->m_curBase, 0, 0, 0); handleDefault = XML_FALSE; } - } - else + } else poolDiscard(&dtd->entityValuePool); if (result != XML_ERROR_NONE) return result; +#else + // This will store "&entity123;" in parser->m_declEntity->textPtr + // to end up as "&entity123;" in the handler. + if (parser->m_declEntity != NULL) { + const enum XML_Error result + = storeSelfEntityValue(parser, parser->m_declEntity); + if (result != XML_ERROR_NONE) + return result; + + if (parser->m_entityDeclHandler) { + *eventEndPP = s; + parser->m_entityDeclHandler( + parser->m_handlerArg, parser->m_declEntity->name, + parser->m_declEntity->is_param, parser->m_declEntity->textPtr, + parser->m_declEntity->textLen, parser->m_curBase, 0, 0, 0); + handleDefault = XML_FALSE; + } + } +#endif } break; case XML_ROLE_DOCTYPE_SYSTEM_ID: #ifdef XML_DTD - useForeignDTD = XML_FALSE; + parser->m_useForeignDTD = XML_FALSE; #endif /* XML_DTD */ dtd->hasParamEntityRefs = XML_TRUE; - if (startDoctypeDeclHandler) { - doctypeSysid = poolStoreString(&tempPool, enc, - s + enc->minBytesPerChar, - next - enc->minBytesPerChar); - if (doctypeSysid == NULL) + if (parser->m_startDoctypeDeclHandler) { + parser->m_doctypeSysid = poolStoreString(&parser->m_tempPool, enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (parser->m_doctypeSysid == NULL) return XML_ERROR_NO_MEMORY; - poolFinish(&tempPool); + poolFinish(&parser->m_tempPool); handleDefault = XML_FALSE; } #ifdef XML_DTD else - /* use externalSubsetName to make doctypeSysid non-NULL - for the case where no startDoctypeDeclHandler is set */ - doctypeSysid = externalSubsetName; + /* use externalSubsetName to make parser->m_doctypeSysid non-NULL + for the case where no parser->m_startDoctypeDeclHandler is set */ + parser->m_doctypeSysid = externalSubsetName; #endif /* XML_DTD */ - if (!dtd->standalone + if (! dtd->standalone #ifdef XML_DTD - && !paramEntityParsing + && ! parser->m_paramEntityParsing #endif /* XML_DTD */ - && notStandaloneHandler - && !notStandaloneHandler(handlerArg)) + && parser->m_notStandaloneHandler + && ! parser->m_notStandaloneHandler(parser->m_handlerArg)) return XML_ERROR_NOT_STANDALONE; #ifndef XML_DTD break; -#else /* XML_DTD */ - if (!declEntity) { - declEntity = (ENTITY *)lookup(&dtd->paramEntities, - externalSubsetName, - sizeof(ENTITY)); - if (!declEntity) +#else /* XML_DTD */ + if (! parser->m_declEntity) { + parser->m_declEntity = (ENTITY *)lookup( + parser, &dtd->paramEntities, externalSubsetName, sizeof(ENTITY)); + if (! parser->m_declEntity) return XML_ERROR_NO_MEMORY; - declEntity->publicId = NULL; + parser->m_declEntity->publicId = NULL; } - /* fall through */ #endif /* XML_DTD */ + /* fall through */ case XML_ROLE_ENTITY_SYSTEM_ID: - if (dtd->keepProcessing && declEntity) { - declEntity->systemId = poolStoreString(&dtd->pool, enc, - s + enc->minBytesPerChar, - next - enc->minBytesPerChar); - if (!declEntity->systemId) + if (dtd->keepProcessing && parser->m_declEntity) { + parser->m_declEntity->systemId + = poolStoreString(&dtd->pool, enc, s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (! parser->m_declEntity->systemId) return XML_ERROR_NO_MEMORY; - declEntity->base = curBase; + parser->m_declEntity->base = parser->m_curBase; poolFinish(&dtd->pool); - if (entityDeclHandler) + /* Don't suppress the default handler if we fell through from + * the XML_ROLE_DOCTYPE_SYSTEM_ID case. + */ + if (parser->m_entityDeclHandler && role == XML_ROLE_ENTITY_SYSTEM_ID) handleDefault = XML_FALSE; } break; case XML_ROLE_ENTITY_COMPLETE: - if (dtd->keepProcessing && declEntity && entityDeclHandler) { +#if XML_GE == 0 + // This will store "&entity123;" in entity->textPtr + // to end up as "&entity123;" in the handler. + if (parser->m_declEntity != NULL) { + const enum XML_Error result + = storeSelfEntityValue(parser, parser->m_declEntity); + if (result != XML_ERROR_NONE) + return result; + } +#endif + if (dtd->keepProcessing && parser->m_declEntity + && parser->m_entityDeclHandler) { *eventEndPP = s; - entityDeclHandler(handlerArg, - declEntity->name, - declEntity->is_param, - 0,0, - declEntity->base, - declEntity->systemId, - declEntity->publicId, - 0); + parser->m_entityDeclHandler( + parser->m_handlerArg, parser->m_declEntity->name, + parser->m_declEntity->is_param, 0, 0, parser->m_declEntity->base, + parser->m_declEntity->systemId, parser->m_declEntity->publicId, 0); handleDefault = XML_FALSE; } break; case XML_ROLE_ENTITY_NOTATION_NAME: - if (dtd->keepProcessing && declEntity) { - declEntity->notation = poolStoreString(&dtd->pool, enc, s, next); - if (!declEntity->notation) + if (dtd->keepProcessing && parser->m_declEntity) { + parser->m_declEntity->notation + = poolStoreString(&dtd->pool, enc, s, next); + if (! parser->m_declEntity->notation) return XML_ERROR_NO_MEMORY; poolFinish(&dtd->pool); - if (unparsedEntityDeclHandler) { + if (parser->m_unparsedEntityDeclHandler) { *eventEndPP = s; - unparsedEntityDeclHandler(handlerArg, - declEntity->name, - declEntity->base, - declEntity->systemId, - declEntity->publicId, - declEntity->notation); + parser->m_unparsedEntityDeclHandler( + parser->m_handlerArg, parser->m_declEntity->name, + parser->m_declEntity->base, parser->m_declEntity->systemId, + parser->m_declEntity->publicId, parser->m_declEntity->notation); handleDefault = XML_FALSE; - } - else if (entityDeclHandler) { + } else if (parser->m_entityDeclHandler) { *eventEndPP = s; - entityDeclHandler(handlerArg, - declEntity->name, - 0,0,0, - declEntity->base, - declEntity->systemId, - declEntity->publicId, - declEntity->notation); + parser->m_entityDeclHandler( + parser->m_handlerArg, parser->m_declEntity->name, 0, 0, 0, + parser->m_declEntity->base, parser->m_declEntity->systemId, + parser->m_declEntity->publicId, parser->m_declEntity->notation); handleDefault = XML_FALSE; } } break; - case XML_ROLE_GENERAL_ENTITY_NAME: - { - if (XmlPredefinedEntityName(enc, s, next)) { - declEntity = NULL; - break; - } - if (dtd->keepProcessing) { - const XML_Char *name = poolStoreString(&dtd->pool, enc, s, next); - if (!name) - return XML_ERROR_NO_MEMORY; - declEntity = (ENTITY *)lookup(&dtd->generalEntities, name, - sizeof(ENTITY)); - if (!declEntity) - return XML_ERROR_NO_MEMORY; - if (declEntity->name != name) { - poolDiscard(&dtd->pool); - declEntity = NULL; - } - else { - poolFinish(&dtd->pool); - declEntity->publicId = NULL; - declEntity->is_param = XML_FALSE; - /* if we have a parent parser or are reading an internal parameter - entity, then the entity declaration is not considered "internal" - */ - declEntity->is_internal = !(parentParser || openInternalEntities); - if (entityDeclHandler) - handleDefault = XML_FALSE; - } - } - else { + case XML_ROLE_GENERAL_ENTITY_NAME: { + if (XmlPredefinedEntityName(enc, s, next)) { + parser->m_declEntity = NULL; + break; + } + if (dtd->keepProcessing) { + const XML_Char *name = poolStoreString(&dtd->pool, enc, s, next); + if (! name) + return XML_ERROR_NO_MEMORY; + parser->m_declEntity = (ENTITY *)lookup(parser, &dtd->generalEntities, + name, sizeof(ENTITY)); + if (! parser->m_declEntity) + return XML_ERROR_NO_MEMORY; + if (parser->m_declEntity->name != name) { poolDiscard(&dtd->pool); - declEntity = NULL; + parser->m_declEntity = NULL; + } else { + poolFinish(&dtd->pool); + parser->m_declEntity->publicId = NULL; + parser->m_declEntity->is_param = XML_FALSE; + /* if we have a parent parser or are reading an internal parameter + entity, then the entity declaration is not considered "internal" + */ + parser->m_declEntity->is_internal + = ! (parser->m_parentParser || parser->m_openInternalEntities); + if (parser->m_entityDeclHandler) + handleDefault = XML_FALSE; } + } else { + poolDiscard(&dtd->pool); + parser->m_declEntity = NULL; } - break; + } break; case XML_ROLE_PARAM_ENTITY_NAME: #ifdef XML_DTD if (dtd->keepProcessing) { const XML_Char *name = poolStoreString(&dtd->pool, enc, s, next); - if (!name) + if (! name) return XML_ERROR_NO_MEMORY; - declEntity = (ENTITY *)lookup(&dtd->paramEntities, - name, sizeof(ENTITY)); - if (!declEntity) + parser->m_declEntity = (ENTITY *)lookup(parser, &dtd->paramEntities, + name, sizeof(ENTITY)); + if (! parser->m_declEntity) return XML_ERROR_NO_MEMORY; - if (declEntity->name != name) { + if (parser->m_declEntity->name != name) { poolDiscard(&dtd->pool); - declEntity = NULL; - } - else { + parser->m_declEntity = NULL; + } else { poolFinish(&dtd->pool); - declEntity->publicId = NULL; - declEntity->is_param = XML_TRUE; + parser->m_declEntity->publicId = NULL; + parser->m_declEntity->is_param = XML_TRUE; /* if we have a parent parser or are reading an internal parameter entity, then the entity declaration is not considered "internal" */ - declEntity->is_internal = !(parentParser || openInternalEntities); - if (entityDeclHandler) + parser->m_declEntity->is_internal + = ! (parser->m_parentParser || parser->m_openInternalEntities); + if (parser->m_entityDeclHandler) handleDefault = XML_FALSE; } - } - else { + } else { poolDiscard(&dtd->pool); - declEntity = NULL; + parser->m_declEntity = NULL; } -#else /* not XML_DTD */ - declEntity = NULL; +#else /* not XML_DTD */ + parser->m_declEntity = NULL; #endif /* XML_DTD */ break; case XML_ROLE_NOTATION_NAME: - declNotationPublicId = NULL; - declNotationName = NULL; - if (notationDeclHandler) { - declNotationName = poolStoreString(&tempPool, enc, s, next); - if (!declNotationName) + parser->m_declNotationPublicId = NULL; + parser->m_declNotationName = NULL; + if (parser->m_notationDeclHandler) { + parser->m_declNotationName + = poolStoreString(&parser->m_tempPool, enc, s, next); + if (! parser->m_declNotationName) return XML_ERROR_NO_MEMORY; - poolFinish(&tempPool); + poolFinish(&parser->m_tempPool); handleDefault = XML_FALSE; } break; case XML_ROLE_NOTATION_PUBLIC_ID: - if (!XmlIsPublicId(enc, s, next, eventPP)) + if (! XmlIsPublicId(enc, s, next, eventPP)) return XML_ERROR_PUBLICID; - if (declNotationName) { /* means notationDeclHandler != NULL */ - XML_Char *tem = poolStoreString(&tempPool, - enc, + if (parser + ->m_declNotationName) { /* means m_notationDeclHandler != NULL */ + XML_Char *tem = poolStoreString(&parser->m_tempPool, enc, s + enc->minBytesPerChar, next - enc->minBytesPerChar); - if (!tem) + if (! tem) return XML_ERROR_NO_MEMORY; normalizePublicId(tem); - declNotationPublicId = tem; - poolFinish(&tempPool); + parser->m_declNotationPublicId = tem; + poolFinish(&parser->m_tempPool); handleDefault = XML_FALSE; } break; case XML_ROLE_NOTATION_SYSTEM_ID: - if (declNotationName && notationDeclHandler) { - const XML_Char *systemId - = poolStoreString(&tempPool, enc, - s + enc->minBytesPerChar, - next - enc->minBytesPerChar); - if (!systemId) + if (parser->m_declNotationName && parser->m_notationDeclHandler) { + const XML_Char *systemId = poolStoreString(&parser->m_tempPool, enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (! systemId) return XML_ERROR_NO_MEMORY; *eventEndPP = s; - notationDeclHandler(handlerArg, - declNotationName, - curBase, - systemId, - declNotationPublicId); + parser->m_notationDeclHandler( + parser->m_handlerArg, parser->m_declNotationName, parser->m_curBase, + systemId, parser->m_declNotationPublicId); handleDefault = XML_FALSE; } - poolClear(&tempPool); + poolClear(&parser->m_tempPool); break; case XML_ROLE_NOTATION_NO_SYSTEM_ID: - if (declNotationPublicId && notationDeclHandler) { + if (parser->m_declNotationPublicId && parser->m_notationDeclHandler) { *eventEndPP = s; - notationDeclHandler(handlerArg, - declNotationName, - curBase, - 0, - declNotationPublicId); + parser->m_notationDeclHandler( + parser->m_handlerArg, parser->m_declNotationName, parser->m_curBase, + 0, parser->m_declNotationPublicId); handleDefault = XML_FALSE; } - poolClear(&tempPool); + poolClear(&parser->m_tempPool); break; case XML_ROLE_ERROR: switch (tok) { case XML_TOK_PARAM_ENTITY_REF: /* PE references in internal subset are - not allowed within declarations. */ + not allowed within declarations. */ return XML_ERROR_PARAM_ENTITY_REF; case XML_TOK_XML_DECL: return XML_ERROR_MISPLACED_XML_PI; @@ -4291,111 +5882,150 @@ doProlog(XML_Parser parser, return XML_ERROR_SYNTAX; } #ifdef XML_DTD - case XML_ROLE_IGNORE_SECT: - { - enum XML_Error result; - if (defaultHandler) - reportDefault(parser, enc, s, next); - handleDefault = XML_FALSE; - result = doIgnoreSection(parser, enc, &next, end, nextPtr, haveMore); - if (result != XML_ERROR_NONE) - return result; - else if (!next) { - processor = ignoreSectionProcessor; - return result; - } + case XML_ROLE_IGNORE_SECT: { + enum XML_Error result; + if (parser->m_defaultHandler) + reportDefault(parser, enc, s, next); + handleDefault = XML_FALSE; + result = doIgnoreSection(parser, enc, &next, end, nextPtr, haveMore); + if (result != XML_ERROR_NONE) + return result; + else if (! next) { + parser->m_processor = ignoreSectionProcessor; + return result; } - break; + } break; #endif /* XML_DTD */ case XML_ROLE_GROUP_OPEN: - if (prologState.level >= groupSize) { - if (groupSize) { - char *temp = (char *)REALLOC(groupConnector, groupSize *= 2); - if (temp == NULL) - return XML_ERROR_NO_MEMORY; - groupConnector = temp; + if (parser->m_prologState.level >= parser->m_groupSize) { + if (parser->m_groupSize) { + { + /* Detect and prevent integer overflow */ + if (parser->m_groupSize > (unsigned int)(-1) / 2u) { + return XML_ERROR_NO_MEMORY; + } + + char *const new_connector = REALLOC( + parser, parser->m_groupConnector, parser->m_groupSize *= 2); + if (new_connector == NULL) { + parser->m_groupSize /= 2; + return XML_ERROR_NO_MEMORY; + } + parser->m_groupConnector = new_connector; + } + if (dtd->scaffIndex) { - int *temp = (int *)REALLOC(dtd->scaffIndex, - groupSize * sizeof(int)); - if (temp == NULL) + /* Detect and prevent integer overflow. + * The preprocessor guard addresses the "always false" warning + * from -Wtype-limits on platforms where + * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */ +#if UINT_MAX >= SIZE_MAX + if (parser->m_groupSize > SIZE_MAX / sizeof(int)) { + return XML_ERROR_NO_MEMORY; + } +#endif + + int *const new_scaff_index = REALLOC( + parser, dtd->scaffIndex, parser->m_groupSize * sizeof(int)); + if (new_scaff_index == NULL) return XML_ERROR_NO_MEMORY; - dtd->scaffIndex = temp; + dtd->scaffIndex = new_scaff_index; } - } - else { - groupConnector = (char *)MALLOC(groupSize = 32); - if (!groupConnector) + } else { + parser->m_groupConnector = MALLOC(parser, parser->m_groupSize = 32); + if (! parser->m_groupConnector) { + parser->m_groupSize = 0; return XML_ERROR_NO_MEMORY; + } } } - groupConnector[prologState.level] = 0; + parser->m_groupConnector[parser->m_prologState.level] = 0; if (dtd->in_eldecl) { int myindex = nextScaffoldPart(parser); if (myindex < 0) return XML_ERROR_NO_MEMORY; + assert(dtd->scaffIndex != NULL); dtd->scaffIndex[dtd->scaffLevel] = myindex; dtd->scaffLevel++; dtd->scaffold[myindex].type = XML_CTYPE_SEQ; - if (elementDeclHandler) + if (parser->m_elementDeclHandler) handleDefault = XML_FALSE; } break; case XML_ROLE_GROUP_SEQUENCE: - if (groupConnector[prologState.level] == ASCII_PIPE) + if (parser->m_groupConnector[parser->m_prologState.level] == ASCII_PIPE) return XML_ERROR_SYNTAX; - groupConnector[prologState.level] = ASCII_COMMA; - if (dtd->in_eldecl && elementDeclHandler) + parser->m_groupConnector[parser->m_prologState.level] = ASCII_COMMA; + if (dtd->in_eldecl && parser->m_elementDeclHandler) handleDefault = XML_FALSE; break; case XML_ROLE_GROUP_CHOICE: - if (groupConnector[prologState.level] == ASCII_COMMA) + if (parser->m_groupConnector[parser->m_prologState.level] == ASCII_COMMA) return XML_ERROR_SYNTAX; if (dtd->in_eldecl - && !groupConnector[prologState.level] + && ! parser->m_groupConnector[parser->m_prologState.level] && (dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel - 1]].type - != XML_CTYPE_MIXED) - ) { + != XML_CTYPE_MIXED)) { dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel - 1]].type = XML_CTYPE_CHOICE; - if (elementDeclHandler) + if (parser->m_elementDeclHandler) handleDefault = XML_FALSE; } - groupConnector[prologState.level] = ASCII_PIPE; + parser->m_groupConnector[parser->m_prologState.level] = ASCII_PIPE; break; case XML_ROLE_PARAM_ENTITY_REF: #ifdef XML_DTD case XML_ROLE_INNER_PARAM_ENTITY_REF: dtd->hasParamEntityRefs = XML_TRUE; - if (!paramEntityParsing) + if (! parser->m_paramEntityParsing) dtd->keepProcessing = dtd->standalone; else { const XML_Char *name; ENTITY *entity; - name = poolStoreString(&dtd->pool, enc, - s + enc->minBytesPerChar, - next - enc->minBytesPerChar); - if (!name) + name = poolStoreString(&dtd->pool, enc, s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (! name) return XML_ERROR_NO_MEMORY; - entity = (ENTITY *)lookup(&dtd->paramEntities, name, 0); + entity = (ENTITY *)lookup(parser, &dtd->paramEntities, name, 0); poolDiscard(&dtd->pool); /* first, determine if a check for an existing declaration is needed; if yes, check that the entity exists, and that it is internal, otherwise call the skipped entity handler */ - if (prologState.documentEntity && - (dtd->standalone - ? !openInternalEntities - : !dtd->hasParamEntityRefs)) { - if (!entity) + if (parser->m_prologState.documentEntity + && (dtd->standalone ? ! parser->m_openInternalEntities + : ! dtd->hasParamEntityRefs)) { + if (! entity) return XML_ERROR_UNDEFINED_ENTITY; - else if (!entity->is_internal) - return XML_ERROR_ENTITY_DECLARED_IN_PE; - } - else if (!entity) { + else if (! entity->is_internal) { + /* It's hard to exhaustively search the code to be sure, + * but there doesn't seem to be a way of executing the + * following line. There are two cases: + * + * If 'standalone' is false, the DTD must have no + * parameter entities or we wouldn't have passed the outer + * 'if' statement. That means the only entity in the hash + * table is the external subset name "#" which cannot be + * given as a parameter entity name in XML syntax, so the + * lookup must have returned NULL and we don't even reach + * the test for an internal entity. + * + * If 'standalone' is true, it does not seem to be + * possible to create entities taking this code path that + * are not internal entities, so fail the test above. + * + * Because this analysis is very uncertain, the code is + * being left in place and merely removed from the + * coverage test statistics. + */ + return XML_ERROR_ENTITY_DECLARED_IN_PE; /* LCOV_EXCL_LINE */ + } + } else if (! entity) { dtd->keepProcessing = dtd->standalone; /* cannot report skipped entities in declarations */ - if ((role == XML_ROLE_PARAM_ENTITY_REF) && skippedEntityHandler) { - skippedEntityHandler(handlerArg, name, 1); + if ((role == XML_ROLE_PARAM_ENTITY_REF) + && parser->m_skippedEntityHandler) { + parser->m_skippedEntityHandler(parser->m_handlerArg, name, 1); handleDefault = XML_FALSE; } break; @@ -4404,50 +6034,49 @@ doProlog(XML_Parser parser, return XML_ERROR_RECURSIVE_ENTITY_REF; if (entity->textPtr) { enum XML_Error result; - XML_Bool betweenDecl = - (role == XML_ROLE_PARAM_ENTITY_REF ? XML_TRUE : XML_FALSE); - result = processInternalEntity(parser, entity, betweenDecl); + XML_Bool betweenDecl + = (role == XML_ROLE_PARAM_ENTITY_REF ? XML_TRUE : XML_FALSE); + result = processEntity(parser, entity, betweenDecl, ENTITY_INTERNAL); if (result != XML_ERROR_NONE) return result; handleDefault = XML_FALSE; break; } - if (externalEntityRefHandler) { + if (parser->m_externalEntityRefHandler) { dtd->paramEntityRead = XML_FALSE; entity->open = XML_TRUE; - if (!externalEntityRefHandler(externalEntityRefHandlerArg, - 0, - entity->base, - entity->systemId, - entity->publicId)) { + entityTrackingOnOpen(parser, entity, __LINE__); + if (! parser->m_externalEntityRefHandler( + parser->m_externalEntityRefHandlerArg, 0, entity->base, + entity->systemId, entity->publicId)) { + entityTrackingOnClose(parser, entity, __LINE__); entity->open = XML_FALSE; return XML_ERROR_EXTERNAL_ENTITY_HANDLING; } + entityTrackingOnClose(parser, entity, __LINE__); entity->open = XML_FALSE; handleDefault = XML_FALSE; - if (!dtd->paramEntityRead) { + if (! dtd->paramEntityRead) { dtd->keepProcessing = dtd->standalone; break; } - } - else { + } else { dtd->keepProcessing = dtd->standalone; break; } } #endif /* XML_DTD */ - if (!dtd->standalone && - notStandaloneHandler && - !notStandaloneHandler(handlerArg)) + if (! dtd->standalone && parser->m_notStandaloneHandler + && ! parser->m_notStandaloneHandler(parser->m_handlerArg)) return XML_ERROR_NOT_STANDALONE; break; - /* Element declaration stuff */ + /* Element declaration stuff */ case XML_ROLE_ELEMENT_NAME: - if (elementDeclHandler) { - declElementType = getElementType(parser, enc, s, next); - if (!declElementType) + if (parser->m_elementDeclHandler) { + parser->m_declElementType = getElementType(parser, enc, s, next); + if (! parser->m_declElementType) return XML_ERROR_NO_MEMORY; dtd->scaffLevel = 0; dtd->scaffCount = 0; @@ -4459,19 +6088,23 @@ doProlog(XML_Parser parser, case XML_ROLE_CONTENT_ANY: case XML_ROLE_CONTENT_EMPTY: if (dtd->in_eldecl) { - if (elementDeclHandler) { - XML_Content * content = (XML_Content *) MALLOC(sizeof(XML_Content)); - if (!content) + if (parser->m_elementDeclHandler) { + // NOTE: We are avoiding MALLOC(..) here to so that + // applications that are not using XML_FreeContentModel but + // plain free(..) or .free_fcn() to free the content model's + // memory are safe. + XML_Content *content = parser->m_mem.malloc_fcn(sizeof(XML_Content)); + if (! content) return XML_ERROR_NO_MEMORY; content->quant = XML_CQUANT_NONE; content->name = NULL; content->numchildren = 0; content->children = NULL; - content->type = ((role == XML_ROLE_CONTENT_ANY) ? - XML_CTYPE_ANY : - XML_CTYPE_EMPTY); + content->type = ((role == XML_ROLE_CONTENT_ANY) ? XML_CTYPE_ANY + : XML_CTYPE_EMPTY); *eventEndPP = s; - elementDeclHandler(handlerArg, declElementType->name, content); + parser->m_elementDeclHandler( + parser->m_handlerArg, parser->m_declElementType->name, content); handleDefault = XML_FALSE; } dtd->in_eldecl = XML_FALSE; @@ -4482,7 +6115,7 @@ doProlog(XML_Parser parser, if (dtd->in_eldecl) { dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel - 1]].type = XML_CTYPE_MIXED; - if (elementDeclHandler) + if (parser->m_elementDeclHandler) handleDefault = XML_FALSE; } break; @@ -4502,24 +6135,30 @@ doProlog(XML_Parser parser, if (dtd->in_eldecl) { ELEMENT_TYPE *el; const XML_Char *name; - int nameLen; - const char *nxt = (quant == XML_CQUANT_NONE - ? next - : next - enc->minBytesPerChar); + size_t nameLen; + const char *nxt + = (quant == XML_CQUANT_NONE ? next : next - enc->minBytesPerChar); int myindex = nextScaffoldPart(parser); if (myindex < 0) return XML_ERROR_NO_MEMORY; dtd->scaffold[myindex].type = XML_CTYPE_NAME; dtd->scaffold[myindex].quant = quant; el = getElementType(parser, enc, s, nxt); - if (!el) + if (! el) return XML_ERROR_NO_MEMORY; name = el->name; dtd->scaffold[myindex].name = name; nameLen = 0; - for (; name[nameLen++]; ); - dtd->contentStringLen += nameLen; - if (elementDeclHandler) + while (name[nameLen++]) + ; + + /* Detect and prevent integer overflow */ + if (nameLen > UINT_MAX - dtd->contentStringLen) { + return XML_ERROR_NO_MEMORY; + } + + dtd->contentStringLen += (unsigned)nameLen; + if (parser->m_elementDeclHandler) handleDefault = XML_FALSE; } break; @@ -4537,17 +6176,18 @@ doProlog(XML_Parser parser, quant = XML_CQUANT_PLUS; closeGroup: if (dtd->in_eldecl) { - if (elementDeclHandler) + if (parser->m_elementDeclHandler) handleDefault = XML_FALSE; dtd->scaffLevel--; dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel]].quant = quant; if (dtd->scaffLevel == 0) { - if (!handleDefault) { + if (! handleDefault) { XML_Content *model = build_model(parser); - if (!model) + if (! model) return XML_ERROR_NO_MEMORY; *eventEndPP = s; - elementDeclHandler(handlerArg, declElementType->name, model); + parser->m_elementDeclHandler( + parser->m_handlerArg, parser->m_declElementType->name, model); } dtd->in_eldecl = XML_FALSE; dtd->contentStringLen = 0; @@ -4557,12 +6197,12 @@ doProlog(XML_Parser parser, /* End element declaration stuff */ case XML_ROLE_PI: - if (!reportProcessingInstruction(parser, enc, s, next)) + if (! reportProcessingInstruction(parser, enc, s, next)) return XML_ERROR_NO_MEMORY; handleDefault = XML_FALSE; break; case XML_ROLE_COMMENT: - if (!reportComment(parser, enc, s, next)) + if (! reportComment(parser, enc, s, next)) return XML_ERROR_NO_MEMORY; handleDefault = XML_FALSE; break; @@ -4574,36 +6214,42 @@ doProlog(XML_Parser parser, } break; case XML_ROLE_DOCTYPE_NONE: - if (startDoctypeDeclHandler) + if (parser->m_startDoctypeDeclHandler) handleDefault = XML_FALSE; break; case XML_ROLE_ENTITY_NONE: - if (dtd->keepProcessing && entityDeclHandler) + if (dtd->keepProcessing && parser->m_entityDeclHandler) handleDefault = XML_FALSE; break; case XML_ROLE_NOTATION_NONE: - if (notationDeclHandler) + if (parser->m_notationDeclHandler) handleDefault = XML_FALSE; break; case XML_ROLE_ATTLIST_NONE: - if (dtd->keepProcessing && attlistDeclHandler) + if (dtd->keepProcessing && parser->m_attlistDeclHandler) handleDefault = XML_FALSE; break; case XML_ROLE_ELEMENT_NONE: - if (elementDeclHandler) + if (parser->m_elementDeclHandler) handleDefault = XML_FALSE; break; } /* end of big switch */ - if (handleDefault && defaultHandler) + if (handleDefault && parser->m_defaultHandler) reportDefault(parser, enc, s, next); - switch (ps_parsing) { - case XML_SUSPENDED: + switch (parser->m_parsingStatus.parsing) { + case XML_SUSPENDED: *nextPtr = next; return XML_ERROR_NONE; case XML_FINISHED: return XML_ERROR_ABORTED; + case XML_PARSING: + if (parser->m_reenter) { + *nextPtr = next; + return XML_ERROR_NONE; + } + /* Fall through */ default: s = next; tok = XmlPrologTok(enc, s, end, &next); @@ -4613,23 +6259,27 @@ doProlog(XML_Parser parser, } static enum XML_Error PTRCALL -epilogProcessor(XML_Parser parser, - const char *s, - const char *end, - const char **nextPtr) -{ - processor = epilogProcessor; - eventPtr = s; +epilogProcessor(XML_Parser parser, const char *s, const char *end, + const char **nextPtr) { + parser->m_processor = epilogProcessor; + parser->m_eventPtr = s; for (;;) { const char *next = NULL; - int tok = XmlPrologTok(encoding, s, end, &next); - eventEndPtr = next; + int tok = XmlPrologTok(parser->m_encoding, s, end, &next); +#if XML_GE == 1 + if (! accountingDiffTolerated(parser, tok, s, next, __LINE__, + XML_ACCOUNT_DIRECT)) { + accountingOnAbort(parser); + return XML_ERROR_AMPLIFICATION_LIMIT_BREACH; + } +#endif + parser->m_eventEndPtr = next; switch (tok) { /* report partial linebreak - it might be the last token */ case -XML_TOK_PROLOG_S: - if (defaultHandler) { - reportDefault(parser, encoding, s, next); - if (ps_parsing == XML_FINISHED) + if (parser->m_defaultHandler) { + reportDefault(parser, parser->m_encoding, s, next); + if (parser->m_parsingStatus.parsing == XML_FINISHED) return XML_ERROR_ABORTED; } *nextPtr = next; @@ -4638,28 +6288,28 @@ epilogProcessor(XML_Parser parser, *nextPtr = s; return XML_ERROR_NONE; case XML_TOK_PROLOG_S: - if (defaultHandler) - reportDefault(parser, encoding, s, next); + if (parser->m_defaultHandler) + reportDefault(parser, parser->m_encoding, s, next); break; case XML_TOK_PI: - if (!reportProcessingInstruction(parser, encoding, s, next)) + if (! reportProcessingInstruction(parser, parser->m_encoding, s, next)) return XML_ERROR_NO_MEMORY; break; case XML_TOK_COMMENT: - if (!reportComment(parser, encoding, s, next)) + if (! reportComment(parser, parser->m_encoding, s, next)) return XML_ERROR_NO_MEMORY; break; case XML_TOK_INVALID: - eventPtr = next; + parser->m_eventPtr = next; return XML_ERROR_INVALID_TOKEN; case XML_TOK_PARTIAL: - if (!ps_finalBuffer) { + if (! parser->m_parsingStatus.finalBuffer) { *nextPtr = s; return XML_ERROR_NONE; } return XML_ERROR_UNCLOSED_TOKEN; case XML_TOK_PARTIAL_CHAR: - if (!ps_finalBuffer) { + if (! parser->m_parsingStatus.finalBuffer) { *nextPtr = s; return XML_ERROR_NONE; } @@ -4667,209 +6317,316 @@ epilogProcessor(XML_Parser parser, default: return XML_ERROR_JUNK_AFTER_DOC_ELEMENT; } - eventPtr = s = next; - switch (ps_parsing) { - case XML_SUSPENDED: + switch (parser->m_parsingStatus.parsing) { + case XML_SUSPENDED: + parser->m_eventPtr = next; *nextPtr = next; return XML_ERROR_NONE; case XML_FINISHED: + parser->m_eventPtr = next; return XML_ERROR_ABORTED; - default: ; + case XML_PARSING: + if (parser->m_reenter) { + return XML_ERROR_UNEXPECTED_STATE; // LCOV_EXCL_LINE + } + /* Fall through */ + default:; + parser->m_eventPtr = s = next; } } } static enum XML_Error -processInternalEntity(XML_Parser parser, ENTITY *entity, - XML_Bool betweenDecl) -{ - const char *textStart, *textEnd; - const char *next; - enum XML_Error result; - OPEN_INTERNAL_ENTITY *openEntity; - - if (freeInternalEntities) { - openEntity = freeInternalEntities; - freeInternalEntities = openEntity->next; +processEntity(XML_Parser parser, ENTITY *entity, XML_Bool betweenDecl, + enum EntityType type) { + OPEN_INTERNAL_ENTITY *openEntity, **openEntityList, **freeEntityList; + switch (type) { + case ENTITY_INTERNAL: + parser->m_processor = internalEntityProcessor; + openEntityList = &parser->m_openInternalEntities; + freeEntityList = &parser->m_freeInternalEntities; + break; + case ENTITY_ATTRIBUTE: + openEntityList = &parser->m_openAttributeEntities; + freeEntityList = &parser->m_freeAttributeEntities; + break; + case ENTITY_VALUE: + openEntityList = &parser->m_openValueEntities; + freeEntityList = &parser->m_freeValueEntities; + break; + /* default case serves merely as a safety net in case of a + * wrong entityType. Therefore we exclude the following lines + * from the test coverage. + * + * LCOV_EXCL_START + */ + default: + // Should not reach here + assert(0); + /* LCOV_EXCL_STOP */ } - else { - openEntity = (OPEN_INTERNAL_ENTITY *)MALLOC(sizeof(OPEN_INTERNAL_ENTITY)); - if (!openEntity) + + if (*freeEntityList) { + openEntity = *freeEntityList; + *freeEntityList = openEntity->next; + } else { + openEntity = MALLOC(parser, sizeof(OPEN_INTERNAL_ENTITY)); + if (! openEntity) return XML_ERROR_NO_MEMORY; } entity->open = XML_TRUE; + entity->hasMore = XML_TRUE; +#if XML_GE == 1 + entityTrackingOnOpen(parser, entity, __LINE__); +#endif entity->processed = 0; - openEntity->next = openInternalEntities; - openInternalEntities = openEntity; + openEntity->next = *openEntityList; + *openEntityList = openEntity; openEntity->entity = entity; - openEntity->startTagLevel = tagLevel; + openEntity->type = type; + openEntity->startTagLevel = parser->m_tagLevel; openEntity->betweenDecl = betweenDecl; openEntity->internalEventPtr = NULL; openEntity->internalEventEndPtr = NULL; - textStart = (char *)entity->textPtr; - textEnd = (char *)(entity->textPtr + entity->textLen); - -#ifdef XML_DTD - if (entity->is_param) { - int tok = XmlPrologTok(internalEncoding, textStart, textEnd, &next); - result = doProlog(parser, internalEncoding, textStart, textEnd, tok, - next, &next, XML_FALSE); - } - else -#endif /* XML_DTD */ - result = doContent(parser, tagLevel, internalEncoding, textStart, - textEnd, &next, XML_FALSE); - if (result == XML_ERROR_NONE) { - if (textEnd != next && ps_parsing == XML_SUSPENDED) { - entity->processed = (int)(next - textStart); - processor = internalEntityProcessor; - } - else { - entity->open = XML_FALSE; - openInternalEntities = openEntity->next; - /* put openEntity back in list of free instances */ - openEntity->next = freeInternalEntities; - freeInternalEntities = openEntity; - } + // Only internal entities make use of the reenter flag + // therefore no need to set it for other entity types + if (type == ENTITY_INTERNAL) { + triggerReenter(parser); } - return result; + return XML_ERROR_NONE; } static enum XML_Error PTRCALL -internalEntityProcessor(XML_Parser parser, - const char *s, - const char *end, - const char **nextPtr) -{ +internalEntityProcessor(XML_Parser parser, const char *s, const char *end, + const char **nextPtr) { + UNUSED_P(s); + UNUSED_P(end); + UNUSED_P(nextPtr); ENTITY *entity; const char *textStart, *textEnd; const char *next; enum XML_Error result; - OPEN_INTERNAL_ENTITY *openEntity = openInternalEntities; - if (!openEntity) + OPEN_INTERNAL_ENTITY *openEntity = parser->m_openInternalEntities; + if (! openEntity) return XML_ERROR_UNEXPECTED_STATE; entity = openEntity->entity; - textStart = ((char *)entity->textPtr) + entity->processed; - textEnd = (char *)(entity->textPtr + entity->textLen); -#ifdef XML_DTD - if (entity->is_param) { - int tok = XmlPrologTok(internalEncoding, textStart, textEnd, &next); - result = doProlog(parser, internalEncoding, textStart, textEnd, tok, - next, &next, XML_FALSE); - } - else -#endif /* XML_DTD */ - result = doContent(parser, openEntity->startTagLevel, internalEncoding, - textStart, textEnd, &next, XML_FALSE); + // This will return early + if (entity->hasMore) { + textStart = ((const char *)entity->textPtr) + entity->processed; + textEnd = (const char *)(entity->textPtr + entity->textLen); + /* Set a safe default value in case 'next' does not get set */ + next = textStart; + + if (entity->is_param) { + int tok + = XmlPrologTok(parser->m_internalEncoding, textStart, textEnd, &next); + result = doProlog(parser, parser->m_internalEncoding, textStart, textEnd, + tok, next, &next, XML_FALSE, XML_FALSE, + XML_ACCOUNT_ENTITY_EXPANSION); + } else { + result = doContent(parser, openEntity->startTagLevel, + parser->m_internalEncoding, textStart, textEnd, &next, + XML_FALSE, XML_ACCOUNT_ENTITY_EXPANSION); + } - if (result != XML_ERROR_NONE) - return result; - else if (textEnd != next && ps_parsing == XML_SUSPENDED) { - entity->processed = (int)(next - (char *)entity->textPtr); + if (result != XML_ERROR_NONE) + return result; + // Check if entity is complete, if not, mark down how much of it is + // processed + if (textEnd != next + && (parser->m_parsingStatus.parsing == XML_SUSPENDED + || (parser->m_parsingStatus.parsing == XML_PARSING + && parser->m_reenter))) { + entity->processed = (int)(next - (const char *)entity->textPtr); + return result; + } + + // Entity is complete. We cannot close it here since we need to first + // process its possible inner entities (which are added to the + // m_openInternalEntities during doProlog or doContent calls above) + entity->hasMore = XML_FALSE; + if (! entity->is_param + && (openEntity->startTagLevel != parser->m_tagLevel)) { + return XML_ERROR_ASYNC_ENTITY; + } + triggerReenter(parser); return result; - } - else { - entity->open = XML_FALSE; - openInternalEntities = openEntity->next; - /* put openEntity back in list of free instances */ - openEntity->next = freeInternalEntities; - freeInternalEntities = openEntity; - } + } // End of entity processing, "if" block will return here -#ifdef XML_DTD - if (entity->is_param) { - int tok; - processor = prologProcessor; - tok = XmlPrologTok(encoding, s, end, &next); - return doProlog(parser, encoding, s, end, tok, next, nextPtr, - (XML_Bool)!ps_finalBuffer); - } - else -#endif /* XML_DTD */ - { - processor = contentProcessor; - /* see externalEntityContentProcessor vs contentProcessor */ - return doContent(parser, parentParser ? 1 : 0, encoding, s, end, - nextPtr, (XML_Bool)!ps_finalBuffer); - } + // Remove fully processed openEntity from open entity list. +#if XML_GE == 1 + entityTrackingOnClose(parser, entity, __LINE__); +#endif + // openEntity is m_openInternalEntities' head, as we set it at the start of + // this function and we skipped doProlog and doContent calls with hasMore set + // to false. This means we can directly remove the head of + // m_openInternalEntities + assert(parser->m_openInternalEntities == openEntity); + entity->open = XML_FALSE; + parser->m_openInternalEntities = parser->m_openInternalEntities->next; + + /* put openEntity back in list of free instances */ + openEntity->next = parser->m_freeInternalEntities; + parser->m_freeInternalEntities = openEntity; + + if (parser->m_openInternalEntities == NULL) { + parser->m_processor = entity->is_param ? prologProcessor : contentProcessor; + } + triggerReenter(parser); + return XML_ERROR_NONE; } static enum XML_Error PTRCALL -errorProcessor(XML_Parser parser, - const char *s, - const char *end, - const char **nextPtr) -{ - return errorCode; +errorProcessor(XML_Parser parser, const char *s, const char *end, + const char **nextPtr) { + UNUSED_P(s); + UNUSED_P(end); + UNUSED_P(nextPtr); + return parser->m_errorCode; } static enum XML_Error storeAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata, - const char *ptr, const char *end, - STRING_POOL *pool) -{ - enum XML_Error result = appendAttributeValue(parser, enc, isCdata, ptr, - end, pool); + const char *ptr, const char *end, STRING_POOL *pool, + enum XML_Account account) { + const char *next = ptr; + enum XML_Error result = XML_ERROR_NONE; + + while (1) { + if (! parser->m_openAttributeEntities) { + result = appendAttributeValue(parser, enc, isCdata, next, end, pool, + account, &next); + } else { + OPEN_INTERNAL_ENTITY *const openEntity = parser->m_openAttributeEntities; + if (! openEntity) + return XML_ERROR_UNEXPECTED_STATE; + + ENTITY *const entity = openEntity->entity; + const char *const textStart + = ((const char *)entity->textPtr) + entity->processed; + const char *const textEnd + = (const char *)(entity->textPtr + entity->textLen); + /* Set a safe default value in case 'next' does not get set */ + const char *nextInEntity = textStart; + if (entity->hasMore) { + result = appendAttributeValue( + parser, parser->m_internalEncoding, isCdata, textStart, textEnd, + pool, XML_ACCOUNT_ENTITY_EXPANSION, &nextInEntity); + if (result != XML_ERROR_NONE) + break; + // Check if entity is complete, if not, mark down how much of it is + // processed. A XML_SUSPENDED check here is not required as + // appendAttributeValue will never suspend the parser. + if (textEnd != nextInEntity) { + entity->processed + = (int)(nextInEntity - (const char *)entity->textPtr); + continue; + } + + // Entity is complete. We cannot close it here since we need to first + // process its possible inner entities (which are added to the + // m_openAttributeEntities during appendAttributeValue) + entity->hasMore = XML_FALSE; + continue; + } // End of entity processing, "if" block skips the rest + + // Remove fully processed openEntity from open entity list. +#if XML_GE == 1 + entityTrackingOnClose(parser, entity, __LINE__); +#endif + // openEntity is m_openAttributeEntities' head, since we set it at the + // start of this function and because we skipped appendAttributeValue call + // with hasMore set to false. This means we can directly remove the head + // of m_openAttributeEntities + assert(parser->m_openAttributeEntities == openEntity); + entity->open = XML_FALSE; + parser->m_openAttributeEntities = parser->m_openAttributeEntities->next; + + /* put openEntity back in list of free instances */ + openEntity->next = parser->m_freeAttributeEntities; + parser->m_freeAttributeEntities = openEntity; + } + + // Break if an error occurred or there is nothing left to process + if (result || (parser->m_openAttributeEntities == NULL && end == next)) { + break; + } + } + if (result) return result; - if (!isCdata && poolLength(pool) && poolLastChar(pool) == 0x20) + if (! isCdata && poolLength(pool) && poolLastChar(pool) == 0x20) poolChop(pool); - if (!poolAppendChar(pool, XML_T('\0'))) + if (! poolAppendChar(pool, XML_T('\0'))) return XML_ERROR_NO_MEMORY; return XML_ERROR_NONE; } static enum XML_Error appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata, - const char *ptr, const char *end, - STRING_POOL *pool) -{ - DTD * const dtd = _dtd; /* save one level of indirection */ + const char *ptr, const char *end, STRING_POOL *pool, + enum XML_Account account, const char **nextPtr) { + DTD *const dtd = parser->m_dtd; /* save one level of indirection */ +#ifndef XML_DTD + UNUSED_P(account); +#endif + for (;;) { - const char *next; + const char *next + = ptr; /* XmlAttributeValueTok doesn't always set the last arg */ int tok = XmlAttributeValueTok(enc, ptr, end, &next); +#if XML_GE == 1 + if (! accountingDiffTolerated(parser, tok, ptr, next, __LINE__, account)) { + accountingOnAbort(parser); + return XML_ERROR_AMPLIFICATION_LIMIT_BREACH; + } +#endif switch (tok) { case XML_TOK_NONE: + if (nextPtr) { + *nextPtr = next; + } return XML_ERROR_NONE; case XML_TOK_INVALID: - if (enc == encoding) - eventPtr = next; + if (enc == parser->m_encoding) + parser->m_eventPtr = next; return XML_ERROR_INVALID_TOKEN; case XML_TOK_PARTIAL: - if (enc == encoding) - eventPtr = ptr; + if (enc == parser->m_encoding) + parser->m_eventPtr = ptr; return XML_ERROR_INVALID_TOKEN; - case XML_TOK_CHAR_REF: - { - XML_Char buf[XML_ENCODE_MAX]; - int i; - int n = XmlCharRefNumber(enc, ptr); - if (n < 0) { - if (enc == encoding) - eventPtr = ptr; - return XML_ERROR_BAD_CHAR_REF; - } - if (!isCdata - && n == 0x20 /* space */ - && (poolLength(pool) == 0 || poolLastChar(pool) == 0x20)) - break; - n = XmlEncode(n, (ICHAR *)buf); - if (!n) { - if (enc == encoding) - eventPtr = ptr; - return XML_ERROR_BAD_CHAR_REF; - } - for (i = 0; i < n; i++) { - if (!poolAppendChar(pool, buf[i])) - return XML_ERROR_NO_MEMORY; - } + case XML_TOK_CHAR_REF: { + XML_Char buf[XML_ENCODE_MAX]; + int i; + int n = XmlCharRefNumber(enc, ptr); + if (n < 0) { + if (enc == parser->m_encoding) + parser->m_eventPtr = ptr; + return XML_ERROR_BAD_CHAR_REF; } - break; + if (! isCdata && n == 0x20 /* space */ + && (poolLength(pool) == 0 || poolLastChar(pool) == 0x20)) + break; + n = XmlEncode(n, (ICHAR *)buf); + /* The XmlEncode() functions can never return 0 here. That + * error return happens if the code point passed in is either + * negative or greater than or equal to 0x110000. The + * XmlCharRefNumber() functions will all return a number + * strictly less than 0x110000 or a negative value if an error + * occurred. The negative value is intercepted above, so + * XmlEncode() is never passed a value it might return an + * error for. + */ + for (i = 0; i < n; i++) { + if (! poolAppendChar(pool, buf[i])) + return XML_ERROR_NO_MEMORY; + } + } break; case XML_TOK_DATA_CHARS: - if (!poolAppend(pool, enc, ptr, next)) + if (! poolAppend(pool, enc, ptr, next)) return XML_ERROR_NO_MEMORY; break; case XML_TOK_TRAILING_CR: @@ -4877,193 +6634,225 @@ appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata, /* fall through */ case XML_TOK_ATTRIBUTE_VALUE_S: case XML_TOK_DATA_NEWLINE: - if (!isCdata && (poolLength(pool) == 0 || poolLastChar(pool) == 0x20)) + if (! isCdata && (poolLength(pool) == 0 || poolLastChar(pool) == 0x20)) break; - if (!poolAppendChar(pool, 0x20)) + if (! poolAppendChar(pool, 0x20)) return XML_ERROR_NO_MEMORY; break; - case XML_TOK_ENTITY_REF: - { - const XML_Char *name; - ENTITY *entity; - char checkEntityDecl; - XML_Char ch = (XML_Char) XmlPredefinedEntityName(enc, - ptr + enc->minBytesPerChar, - next - enc->minBytesPerChar); - if (ch) { - if (!poolAppendChar(pool, ch)) - return XML_ERROR_NO_MEMORY; - break; - } - name = poolStoreString(&temp2Pool, enc, - ptr + enc->minBytesPerChar, - next - enc->minBytesPerChar); - if (!name) + case XML_TOK_ENTITY_REF: { + const XML_Char *name; + ENTITY *entity; + bool checkEntityDecl; + XML_Char ch = (XML_Char)XmlPredefinedEntityName( + enc, ptr + enc->minBytesPerChar, next - enc->minBytesPerChar); + if (ch) { +#if XML_GE == 1 + /* NOTE: We are replacing 4-6 characters original input for 1 character + * so there is no amplification and hence recording without + * protection. */ + accountingDiffTolerated(parser, tok, (char *)&ch, + ((char *)&ch) + sizeof(XML_Char), __LINE__, + XML_ACCOUNT_ENTITY_EXPANSION); +#endif /* XML_GE == 1 */ + if (! poolAppendChar(pool, ch)) return XML_ERROR_NO_MEMORY; - entity = (ENTITY *)lookup(&dtd->generalEntities, name, 0); - poolDiscard(&temp2Pool); - /* First, determine if a check for an existing declaration is needed; - if yes, check that the entity exists, and that it is internal. - */ - if (pool == &dtd->pool) /* are we called from prolog? */ - checkEntityDecl = + break; + } + name = poolStoreString(&parser->m_temp2Pool, enc, + ptr + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (! name) + return XML_ERROR_NO_MEMORY; + entity = (ENTITY *)lookup(parser, &dtd->generalEntities, name, 0); + poolDiscard(&parser->m_temp2Pool); + /* First, determine if a check for an existing declaration is needed; + if yes, check that the entity exists, and that it is internal. + */ + if (pool == &dtd->pool) /* are we called from prolog? */ + checkEntityDecl = #ifdef XML_DTD - prologState.documentEntity && + parser->m_prologState.documentEntity && #endif /* XML_DTD */ - (dtd->standalone - ? !openInternalEntities - : !dtd->hasParamEntityRefs); - else /* if (pool == &tempPool): we are called from content */ - checkEntityDecl = !dtd->hasParamEntityRefs || dtd->standalone; - if (checkEntityDecl) { - if (!entity) - return XML_ERROR_UNDEFINED_ENTITY; - else if (!entity->is_internal) - return XML_ERROR_ENTITY_DECLARED_IN_PE; - } - else if (!entity) { - /* Cannot report skipped entity here - see comments on - skippedEntityHandler. - if (skippedEntityHandler) - skippedEntityHandler(handlerArg, name, 0); - */ - /* Cannot call the default handler because this would be - out of sync with the call to the startElementHandler. - if ((pool == &tempPool) && defaultHandler) - reportDefault(parser, enc, ptr, next); - */ - break; - } - if (entity->open) { - if (enc == encoding) - eventPtr = ptr; - return XML_ERROR_RECURSIVE_ENTITY_REF; - } - if (entity->notation) { - if (enc == encoding) - eventPtr = ptr; - return XML_ERROR_BINARY_ENTITY_REF; - } - if (!entity->textPtr) { - if (enc == encoding) - eventPtr = ptr; - return XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF; + (dtd->standalone ? ! parser->m_openInternalEntities + : ! dtd->hasParamEntityRefs); + else /* if (pool == &parser->m_tempPool): we are called from content */ + checkEntityDecl = ! dtd->hasParamEntityRefs || dtd->standalone; + if (checkEntityDecl) { + if (! entity) + return XML_ERROR_UNDEFINED_ENTITY; + else if (! entity->is_internal) + return XML_ERROR_ENTITY_DECLARED_IN_PE; + } else if (! entity) { + /* Cannot report skipped entity here - see comments on + parser->m_skippedEntityHandler. + if (parser->m_skippedEntityHandler) + parser->m_skippedEntityHandler(parser->m_handlerArg, name, 0); + */ + /* Cannot call the default handler because this would be + out of sync with the call to the startElementHandler. + if ((pool == &parser->m_tempPool) && parser->m_defaultHandler) + reportDefault(parser, enc, ptr, next); + */ + break; + } + if (entity->open) { + if (enc == parser->m_encoding) { + /* It does not appear that this line can be executed. + * + * The "if (entity->open)" check catches recursive entity + * definitions. In order to be called with an open + * entity, it must have gone through this code before and + * been through the recursive call to + * appendAttributeValue() some lines below. That call + * sets the local encoding ("enc") to the parser's + * internal encoding (internal_utf8 or internal_utf16), + * which can never be the same as the principle encoding. + * It doesn't appear there is another code path that gets + * here with entity->open being TRUE. + * + * Since it is not certain that this logic is watertight, + * we keep the line and merely exclude it from coverage + * tests. + */ + parser->m_eventPtr = ptr; /* LCOV_EXCL_LINE */ } - else { - enum XML_Error result; - const XML_Char *textEnd = entity->textPtr + entity->textLen; - entity->open = XML_TRUE; - result = appendAttributeValue(parser, internalEncoding, isCdata, - (char *)entity->textPtr, - (char *)textEnd, pool); - entity->open = XML_FALSE; - if (result) - return result; + return XML_ERROR_RECURSIVE_ENTITY_REF; + } + if (entity->notation) { + if (enc == parser->m_encoding) + parser->m_eventPtr = ptr; + return XML_ERROR_BINARY_ENTITY_REF; + } + if (! entity->textPtr) { + if (enc == parser->m_encoding) + parser->m_eventPtr = ptr; + return XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF; + } else { + enum XML_Error result; + result = processEntity(parser, entity, XML_FALSE, ENTITY_ATTRIBUTE); + if ((result == XML_ERROR_NONE) && (nextPtr != NULL)) { + *nextPtr = next; } + return result; } - break; + } break; default: - if (enc == encoding) - eventPtr = ptr; + /* The only token returned by XmlAttributeValueTok() that does + * not have an explicit case here is XML_TOK_PARTIAL_CHAR. + * Getting that would require an entity name to contain an + * incomplete XML character (e.g. \xE2\x82); however previous + * tokenisers will have already recognised and rejected such + * names before XmlAttributeValueTok() gets a look-in. This + * default case should be retained as a safety net, but the code + * excluded from coverage tests. + * + * LCOV_EXCL_START + */ + if (enc == parser->m_encoding) + parser->m_eventPtr = ptr; return XML_ERROR_UNEXPECTED_STATE; + /* LCOV_EXCL_STOP */ } ptr = next; } /* not reached */ } +#if XML_GE == 1 static enum XML_Error -storeEntityValue(XML_Parser parser, - const ENCODING *enc, - const char *entityTextPtr, - const char *entityTextEnd) -{ - DTD * const dtd = _dtd; /* save one level of indirection */ +storeEntityValue(XML_Parser parser, const ENCODING *enc, + const char *entityTextPtr, const char *entityTextEnd, + enum XML_Account account, const char **nextPtr) { + DTD *const dtd = parser->m_dtd; /* save one level of indirection */ STRING_POOL *pool = &(dtd->entityValuePool); enum XML_Error result = XML_ERROR_NONE; -#ifdef XML_DTD - int oldInEntityValue = prologState.inEntityValue; - prologState.inEntityValue = 1; -#endif /* XML_DTD */ +# ifdef XML_DTD + int oldInEntityValue = parser->m_prologState.inEntityValue; + parser->m_prologState.inEntityValue = 1; +# else + UNUSED_P(account); +# endif /* XML_DTD */ /* never return Null for the value argument in EntityDeclHandler, since this would indicate an external entity; therefore we have to make sure that entityValuePool.start is not null */ - if (!pool->blocks) { - if (!poolGrow(pool)) + if (! pool->blocks) { + if (! poolGrow(pool)) return XML_ERROR_NO_MEMORY; } + const char *next; for (;;) { - const char *next; + next + = entityTextPtr; /* XmlEntityValueTok doesn't always set the last arg */ int tok = XmlEntityValueTok(enc, entityTextPtr, entityTextEnd, &next); + + if (! accountingDiffTolerated(parser, tok, entityTextPtr, next, __LINE__, + account)) { + accountingOnAbort(parser); + result = XML_ERROR_AMPLIFICATION_LIMIT_BREACH; + goto endEntityValue; + } + switch (tok) { case XML_TOK_PARAM_ENTITY_REF: -#ifdef XML_DTD - if (isParamEntity || enc != encoding) { +# ifdef XML_DTD + if (parser->m_isParamEntity || enc != parser->m_encoding) { const XML_Char *name; ENTITY *entity; - name = poolStoreString(&tempPool, enc, + name = poolStoreString(&parser->m_tempPool, enc, entityTextPtr + enc->minBytesPerChar, next - enc->minBytesPerChar); - if (!name) { + if (! name) { result = XML_ERROR_NO_MEMORY; goto endEntityValue; } - entity = (ENTITY *)lookup(&dtd->paramEntities, name, 0); - poolDiscard(&tempPool); - if (!entity) { + entity = (ENTITY *)lookup(parser, &dtd->paramEntities, name, 0); + poolDiscard(&parser->m_tempPool); + if (! entity) { /* not a well-formedness error - see XML 1.0: WFC Entity Declared */ /* cannot report skipped entity here - see comments on - skippedEntityHandler - if (skippedEntityHandler) - skippedEntityHandler(handlerArg, name, 0); + parser->m_skippedEntityHandler + if (parser->m_skippedEntityHandler) + parser->m_skippedEntityHandler(parser->m_handlerArg, name, 0); */ dtd->keepProcessing = dtd->standalone; goto endEntityValue; } - if (entity->open) { - if (enc == encoding) - eventPtr = entityTextPtr; + if (entity->open || (entity == parser->m_declEntity)) { + if (enc == parser->m_encoding) + parser->m_eventPtr = entityTextPtr; result = XML_ERROR_RECURSIVE_ENTITY_REF; goto endEntityValue; } if (entity->systemId) { - if (externalEntityRefHandler) { + if (parser->m_externalEntityRefHandler) { dtd->paramEntityRead = XML_FALSE; entity->open = XML_TRUE; - if (!externalEntityRefHandler(externalEntityRefHandlerArg, - 0, - entity->base, - entity->systemId, - entity->publicId)) { + entityTrackingOnOpen(parser, entity, __LINE__); + if (! parser->m_externalEntityRefHandler( + parser->m_externalEntityRefHandlerArg, 0, entity->base, + entity->systemId, entity->publicId)) { + entityTrackingOnClose(parser, entity, __LINE__); entity->open = XML_FALSE; result = XML_ERROR_EXTERNAL_ENTITY_HANDLING; goto endEntityValue; } + entityTrackingOnClose(parser, entity, __LINE__); entity->open = XML_FALSE; - if (!dtd->paramEntityRead) + if (! dtd->paramEntityRead) dtd->keepProcessing = dtd->standalone; - } - else + } else dtd->keepProcessing = dtd->standalone; - } - else { - entity->open = XML_TRUE; - result = storeEntityValue(parser, - internalEncoding, - (char *)entity->textPtr, - (char *)(entity->textPtr - + entity->textLen)); - entity->open = XML_FALSE; - if (result) - goto endEntityValue; + } else { + result = processEntity(parser, entity, XML_FALSE, ENTITY_VALUE); + goto endEntityValue; } break; } -#endif /* XML_DTD */ +# endif /* XML_DTD */ /* In the internal subset, PE references are not legal within markup declarations, e.g entity values in this case. */ - eventPtr = entityTextPtr; + parser->m_eventPtr = entityTextPtr; result = XML_ERROR_PARAM_ENTITY_REF; goto endEntityValue; case XML_TOK_NONE: @@ -5071,7 +6860,7 @@ storeEntityValue(XML_Parser parser, goto endEntityValue; case XML_TOK_ENTITY_REF: case XML_TOK_DATA_CHARS: - if (!poolAppend(pool, enc, entityTextPtr, next)) { + if (! poolAppend(pool, enc, entityTextPtr, next)) { result = XML_ERROR_NO_MEMORY; goto endEntityValue; } @@ -5080,67 +6869,176 @@ storeEntityValue(XML_Parser parser, next = entityTextPtr + enc->minBytesPerChar; /* fall through */ case XML_TOK_DATA_NEWLINE: - if (pool->end == pool->ptr && !poolGrow(pool)) { - result = XML_ERROR_NO_MEMORY; + if (pool->end == pool->ptr && ! poolGrow(pool)) { + result = XML_ERROR_NO_MEMORY; goto endEntityValue; } *(pool->ptr)++ = 0xA; break; - case XML_TOK_CHAR_REF: - { - XML_Char buf[XML_ENCODE_MAX]; - int i; - int n = XmlCharRefNumber(enc, entityTextPtr); - if (n < 0) { - if (enc == encoding) - eventPtr = entityTextPtr; - result = XML_ERROR_BAD_CHAR_REF; - goto endEntityValue; - } - n = XmlEncode(n, (ICHAR *)buf); - if (!n) { - if (enc == encoding) - eventPtr = entityTextPtr; - result = XML_ERROR_BAD_CHAR_REF; + case XML_TOK_CHAR_REF: { + XML_Char buf[XML_ENCODE_MAX]; + int i; + int n = XmlCharRefNumber(enc, entityTextPtr); + if (n < 0) { + if (enc == parser->m_encoding) + parser->m_eventPtr = entityTextPtr; + result = XML_ERROR_BAD_CHAR_REF; + goto endEntityValue; + } + n = XmlEncode(n, (ICHAR *)buf); + /* The XmlEncode() functions can never return 0 here. That + * error return happens if the code point passed in is either + * negative or greater than or equal to 0x110000. The + * XmlCharRefNumber() functions will all return a number + * strictly less than 0x110000 or a negative value if an error + * occurred. The negative value is intercepted above, so + * XmlEncode() is never passed a value it might return an + * error for. + */ + for (i = 0; i < n; i++) { + if (pool->end == pool->ptr && ! poolGrow(pool)) { + result = XML_ERROR_NO_MEMORY; goto endEntityValue; } - for (i = 0; i < n; i++) { - if (pool->end == pool->ptr && !poolGrow(pool)) { - result = XML_ERROR_NO_MEMORY; - goto endEntityValue; - } - *(pool->ptr)++ = buf[i]; - } + *(pool->ptr)++ = buf[i]; } - break; + } break; case XML_TOK_PARTIAL: - if (enc == encoding) - eventPtr = entityTextPtr; + if (enc == parser->m_encoding) + parser->m_eventPtr = entityTextPtr; result = XML_ERROR_INVALID_TOKEN; goto endEntityValue; case XML_TOK_INVALID: - if (enc == encoding) - eventPtr = next; + if (enc == parser->m_encoding) + parser->m_eventPtr = next; result = XML_ERROR_INVALID_TOKEN; goto endEntityValue; default: - if (enc == encoding) - eventPtr = entityTextPtr; + /* This default case should be unnecessary -- all the tokens + * that XmlEntityValueTok() can return have their own explicit + * cases -- but should be retained for safety. We do however + * exclude it from the coverage statistics. + * + * LCOV_EXCL_START + */ + if (enc == parser->m_encoding) + parser->m_eventPtr = entityTextPtr; result = XML_ERROR_UNEXPECTED_STATE; goto endEntityValue; + /* LCOV_EXCL_STOP */ } entityTextPtr = next; } endEntityValue: -#ifdef XML_DTD - prologState.inEntityValue = oldInEntityValue; -#endif /* XML_DTD */ +# ifdef XML_DTD + parser->m_prologState.inEntityValue = oldInEntityValue; +# endif /* XML_DTD */ + // If 'nextPtr' is given, it should be updated during the processing + if (nextPtr != NULL) { + *nextPtr = next; + } + return result; +} + +static enum XML_Error +callStoreEntityValue(XML_Parser parser, const ENCODING *enc, + const char *entityTextPtr, const char *entityTextEnd, + enum XML_Account account) { + const char *next = entityTextPtr; + enum XML_Error result = XML_ERROR_NONE; + while (1) { + if (! parser->m_openValueEntities) { + result + = storeEntityValue(parser, enc, next, entityTextEnd, account, &next); + } else { + OPEN_INTERNAL_ENTITY *const openEntity = parser->m_openValueEntities; + if (! openEntity) + return XML_ERROR_UNEXPECTED_STATE; + + ENTITY *const entity = openEntity->entity; + const char *const textStart + = ((const char *)entity->textPtr) + entity->processed; + const char *const textEnd + = (const char *)(entity->textPtr + entity->textLen); + /* Set a safe default value in case 'next' does not get set */ + const char *nextInEntity = textStart; + if (entity->hasMore) { + result = storeEntityValue(parser, parser->m_internalEncoding, textStart, + textEnd, XML_ACCOUNT_ENTITY_EXPANSION, + &nextInEntity); + if (result != XML_ERROR_NONE) + break; + // Check if entity is complete, if not, mark down how much of it is + // processed. A XML_SUSPENDED check here is not required as + // appendAttributeValue will never suspend the parser. + if (textEnd != nextInEntity) { + entity->processed + = (int)(nextInEntity - (const char *)entity->textPtr); + continue; + } + + // Entity is complete. We cannot close it here since we need to first + // process its possible inner entities (which are added to the + // m_openValueEntities during storeEntityValue) + entity->hasMore = XML_FALSE; + continue; + } // End of entity processing, "if" block skips the rest + + // Remove fully processed openEntity from open entity list. +# if XML_GE == 1 + entityTrackingOnClose(parser, entity, __LINE__); +# endif + // openEntity is m_openValueEntities' head, since we set it at the + // start of this function and because we skipped storeEntityValue call + // with hasMore set to false. This means we can directly remove the head + // of m_openValueEntities + assert(parser->m_openValueEntities == openEntity); + entity->open = XML_FALSE; + parser->m_openValueEntities = parser->m_openValueEntities->next; + + /* put openEntity back in list of free instances */ + openEntity->next = parser->m_freeValueEntities; + parser->m_freeValueEntities = openEntity; + } + + // Break if an error occurred or there is nothing left to process + if (result + || (parser->m_openValueEntities == NULL && entityTextEnd == next)) { + break; + } + } + return result; } +#else /* XML_GE == 0 */ + +static enum XML_Error +storeSelfEntityValue(XML_Parser parser, ENTITY *entity) { + // This will store "&entity123;" in entity->textPtr + // to end up as "&entity123;" in the handler. + const char *const entity_start = "&"; + const char *const entity_end = ";"; + + STRING_POOL *const pool = &(parser->m_dtd->entityValuePool); + if (! poolAppendString(pool, entity_start) + || ! poolAppendString(pool, entity->name) + || ! poolAppendString(pool, entity_end)) { + poolDiscard(pool); + return XML_ERROR_NO_MEMORY; + } + + entity->textPtr = poolStart(pool); + entity->textLen = (int)(poolLength(pool)); + poolFinish(pool); + + return XML_ERROR_NONE; +} + +#endif /* XML_GE == 0 */ + static void FASTCALL -normalizeLines(XML_Char *s) -{ +normalizeLines(XML_Char *s) { XML_Char *p; for (;; s++) { if (*s == XML_T('\0')) @@ -5154,8 +7052,7 @@ normalizeLines(XML_Char *s) *p++ = 0xA; if (*++s == 0xA) s++; - } - else + } else *p++ = *s++; } while (*s); *p = XML_T('\0'); @@ -5163,87 +7060,101 @@ normalizeLines(XML_Char *s) static int reportProcessingInstruction(XML_Parser parser, const ENCODING *enc, - const char *start, const char *end) -{ + const char *start, const char *end) { const XML_Char *target; XML_Char *data; const char *tem; - if (!processingInstructionHandler) { - if (defaultHandler) + if (! parser->m_processingInstructionHandler) { + if (parser->m_defaultHandler) reportDefault(parser, enc, start, end); return 1; } start += enc->minBytesPerChar * 2; tem = start + XmlNameLength(enc, start); - target = poolStoreString(&tempPool, enc, start, tem); - if (!target) + target = poolStoreString(&parser->m_tempPool, enc, start, tem); + if (! target) return 0; - poolFinish(&tempPool); - data = poolStoreString(&tempPool, enc, - XmlSkipS(enc, tem), - end - enc->minBytesPerChar*2); - if (!data) + poolFinish(&parser->m_tempPool); + data = poolStoreString(&parser->m_tempPool, enc, XmlSkipS(enc, tem), + end - enc->minBytesPerChar * 2); + if (! data) return 0; normalizeLines(data); - processingInstructionHandler(handlerArg, target, data); - poolClear(&tempPool); + parser->m_processingInstructionHandler(parser->m_handlerArg, target, data); + poolClear(&parser->m_tempPool); return 1; } static int -reportComment(XML_Parser parser, const ENCODING *enc, - const char *start, const char *end) -{ +reportComment(XML_Parser parser, const ENCODING *enc, const char *start, + const char *end) { XML_Char *data; - if (!commentHandler) { - if (defaultHandler) + if (! parser->m_commentHandler) { + if (parser->m_defaultHandler) reportDefault(parser, enc, start, end); return 1; } - data = poolStoreString(&tempPool, - enc, + data = poolStoreString(&parser->m_tempPool, enc, start + enc->minBytesPerChar * 4, end - enc->minBytesPerChar * 3); - if (!data) + if (! data) return 0; normalizeLines(data); - commentHandler(handlerArg, data); - poolClear(&tempPool); + parser->m_commentHandler(parser->m_handlerArg, data); + poolClear(&parser->m_tempPool); return 1; } static void -reportDefault(XML_Parser parser, const ENCODING *enc, - const char *s, const char *end) -{ +reportDefault(XML_Parser parser, const ENCODING *enc, const char *s, + const char *end) { if (MUST_CONVERT(enc, s)) { + enum XML_Convert_Result convert_res; const char **eventPP; const char **eventEndPP; - if (enc == encoding) { - eventPP = &eventPtr; - eventEndPP = &eventEndPtr; - } - else { - eventPP = &(openInternalEntities->internalEventPtr); - eventEndPP = &(openInternalEntities->internalEventEndPtr); + if (enc == parser->m_encoding) { + eventPP = &parser->m_eventPtr; + eventEndPP = &parser->m_eventEndPtr; + } else { + /* To get here, two things must be true; the parser must be + * using a character encoding that is not the same as the + * encoding passed in, and the encoding passed in must need + * conversion to the internal format (UTF-8 unless XML_UNICODE + * is defined). The only occasions on which the encoding passed + * in is not the same as the parser's encoding are when it is + * the internal encoding (e.g. a previously defined parameter + * entity, already converted to internal format). This by + * definition doesn't need conversion, so the whole branch never + * gets executed. + * + * For safety's sake we don't delete these lines and merely + * exclude them from coverage statistics. + * + * LCOV_EXCL_START + */ + eventPP = &(parser->m_openInternalEntities->internalEventPtr); + eventEndPP = &(parser->m_openInternalEntities->internalEventEndPtr); + /* LCOV_EXCL_STOP */ } do { - ICHAR *dataPtr = (ICHAR *)dataBuf; - XmlConvert(enc, &s, end, &dataPtr, (ICHAR *)dataBufEnd); + ICHAR *dataPtr = (ICHAR *)parser->m_dataBuf; + convert_res + = XmlConvert(enc, &s, end, &dataPtr, (ICHAR *)parser->m_dataBufEnd); *eventEndPP = s; - defaultHandler(handlerArg, dataBuf, (int)(dataPtr - (ICHAR *)dataBuf)); + parser->m_defaultHandler(parser->m_handlerArg, parser->m_dataBuf, + (int)(dataPtr - (ICHAR *)parser->m_dataBuf)); *eventPP = s; - } while (s != end); - } - else - defaultHandler(handlerArg, (XML_Char *)s, (int)((XML_Char *)end - (XML_Char *)s)); + } while ((convert_res != XML_CONVERT_COMPLETED) + && (convert_res != XML_CONVERT_INPUT_INCOMPLETE)); + } else + parser->m_defaultHandler( + parser->m_handlerArg, (const XML_Char *)s, + (int)((const XML_Char *)end - (const XML_Char *)s)); } - static int defineAttribute(ELEMENT_TYPE *type, ATTRIBUTE_ID *attId, XML_Bool isCdata, - XML_Bool isId, const XML_Char *value, XML_Parser parser) -{ + XML_Bool isId, const XML_Char *value, XML_Parser parser) { DEFAULT_ATTRIBUTE *att; if (value || isId) { /* The handling of default attributes gets messed up if we have @@ -5252,22 +7163,40 @@ defineAttribute(ELEMENT_TYPE *type, ATTRIBUTE_ID *attId, XML_Bool isCdata, for (i = 0; i < type->nDefaultAtts; i++) if (attId == type->defaultAtts[i].id) return 1; - if (isId && !type->idAtt && !attId->xmlns) + if (isId && ! type->idAtt && ! attId->xmlns) type->idAtt = attId; } if (type->nDefaultAtts == type->allocDefaultAtts) { if (type->allocDefaultAtts == 0) { type->allocDefaultAtts = 8; - type->defaultAtts = (DEFAULT_ATTRIBUTE *)MALLOC(type->allocDefaultAtts - * sizeof(DEFAULT_ATTRIBUTE)); - if (!type->defaultAtts) + type->defaultAtts + = MALLOC(parser, type->allocDefaultAtts * sizeof(DEFAULT_ATTRIBUTE)); + if (! type->defaultAtts) { + type->allocDefaultAtts = 0; return 0; - } - else { + } + } else { DEFAULT_ATTRIBUTE *temp; + + /* Detect and prevent integer overflow */ + if (type->allocDefaultAtts > INT_MAX / 2) { + return 0; + } + int count = type->allocDefaultAtts * 2; - temp = (DEFAULT_ATTRIBUTE *) - REALLOC(type->defaultAtts, (count * sizeof(DEFAULT_ATTRIBUTE))); + + /* Detect and prevent integer overflow. + * The preprocessor guard addresses the "always false" warning + * from -Wtype-limits on platforms where + * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */ +#if UINT_MAX >= SIZE_MAX + if ((unsigned)count > SIZE_MAX / sizeof(DEFAULT_ATTRIBUTE)) { + return 0; + } +#endif + + temp = REALLOC(parser, type->defaultAtts, + (count * sizeof(DEFAULT_ATTRIBUTE))); if (temp == NULL) return 0; type->allocDefaultAtts = count; @@ -5278,91 +7207,90 @@ defineAttribute(ELEMENT_TYPE *type, ATTRIBUTE_ID *attId, XML_Bool isCdata, att->id = attId; att->value = value; att->isCdata = isCdata; - if (!isCdata) + if (! isCdata) attId->maybeTokenized = XML_TRUE; type->nDefaultAtts += 1; return 1; } static int -setElementTypePrefix(XML_Parser parser, ELEMENT_TYPE *elementType) -{ - DTD * const dtd = _dtd; /* save one level of indirection */ +setElementTypePrefix(XML_Parser parser, ELEMENT_TYPE *elementType) { + DTD *const dtd = parser->m_dtd; /* save one level of indirection */ const XML_Char *name; for (name = elementType->name; *name; name++) { if (*name == XML_T(ASCII_COLON)) { PREFIX *prefix; const XML_Char *s; for (s = elementType->name; s != name; s++) { - if (!poolAppendChar(&dtd->pool, *s)) + if (! poolAppendChar(&dtd->pool, *s)) return 0; } - if (!poolAppendChar(&dtd->pool, XML_T('\0'))) + if (! poolAppendChar(&dtd->pool, XML_T('\0'))) return 0; - prefix = (PREFIX *)lookup(&dtd->prefixes, poolStart(&dtd->pool), + prefix = (PREFIX *)lookup(parser, &dtd->prefixes, poolStart(&dtd->pool), sizeof(PREFIX)); - if (!prefix) + if (! prefix) return 0; if (prefix->name == poolStart(&dtd->pool)) poolFinish(&dtd->pool); else poolDiscard(&dtd->pool); elementType->prefix = prefix; - + break; } } return 1; } static ATTRIBUTE_ID * -getAttributeId(XML_Parser parser, const ENCODING *enc, - const char *start, const char *end) -{ - DTD * const dtd = _dtd; /* save one level of indirection */ +getAttributeId(XML_Parser parser, const ENCODING *enc, const char *start, + const char *end) { + DTD *const dtd = parser->m_dtd; /* save one level of indirection */ ATTRIBUTE_ID *id; const XML_Char *name; - if (!poolAppendChar(&dtd->pool, XML_T('\0'))) + if (! poolAppendChar(&dtd->pool, XML_T('\0'))) return NULL; name = poolStoreString(&dtd->pool, enc, start, end); - if (!name) + if (! name) return NULL; - /* skip quotation mark - its storage will be re-used (like in name[-1]) */ + /* skip quotation mark - its storage will be reused (like in name[-1]) */ ++name; - id = (ATTRIBUTE_ID *)lookup(&dtd->attributeIds, name, sizeof(ATTRIBUTE_ID)); - if (!id) + id = (ATTRIBUTE_ID *)lookup(parser, &dtd->attributeIds, name, + sizeof(ATTRIBUTE_ID)); + if (! id) return NULL; if (id->name != name) poolDiscard(&dtd->pool); else { poolFinish(&dtd->pool); - if (!ns) + if (! parser->m_ns) ; - else if (name[0] == XML_T(ASCII_x) - && name[1] == XML_T(ASCII_m) - && name[2] == XML_T(ASCII_l) - && name[3] == XML_T(ASCII_n) - && name[4] == XML_T(ASCII_s) - && (name[5] == XML_T('\0') || name[5] == XML_T(ASCII_COLON))) { + else if (name[0] == XML_T(ASCII_x) && name[1] == XML_T(ASCII_m) + && name[2] == XML_T(ASCII_l) && name[3] == XML_T(ASCII_n) + && name[4] == XML_T(ASCII_s) + && (name[5] == XML_T('\0') || name[5] == XML_T(ASCII_COLON))) { if (name[5] == XML_T('\0')) id->prefix = &dtd->defaultPrefix; else - id->prefix = (PREFIX *)lookup(&dtd->prefixes, name + 6, sizeof(PREFIX)); + id->prefix = (PREFIX *)lookup(parser, &dtd->prefixes, name + 6, + sizeof(PREFIX)); id->xmlns = XML_TRUE; - } - else { + } else { int i; for (i = 0; name[i]; i++) { /* attributes without prefix are *not* in the default namespace */ if (name[i] == XML_T(ASCII_COLON)) { int j; for (j = 0; j < i; j++) { - if (!poolAppendChar(&dtd->pool, name[j])) + if (! poolAppendChar(&dtd->pool, name[j])) return NULL; } - if (!poolAppendChar(&dtd->pool, XML_T('\0'))) + if (! poolAppendChar(&dtd->pool, XML_T('\0'))) + return NULL; + id->prefix = (PREFIX *)lookup(parser, &dtd->prefixes, + poolStart(&dtd->pool), sizeof(PREFIX)); + if (! id->prefix) return NULL; - id->prefix = (PREFIX *)lookup(&dtd->prefixes, poolStart(&dtd->pool), - sizeof(PREFIX)); if (id->prefix->name == poolStart(&dtd->pool)) poolFinish(&dtd->pool); else @@ -5378,23 +7306,44 @@ getAttributeId(XML_Parser parser, const ENCODING *enc, #define CONTEXT_SEP XML_T(ASCII_FF) static const XML_Char * -getContext(XML_Parser parser) -{ - DTD * const dtd = _dtd; /* save one level of indirection */ +getContext(XML_Parser parser) { + DTD *const dtd = parser->m_dtd; /* save one level of indirection */ HASH_TABLE_ITER iter; XML_Bool needSep = XML_FALSE; if (dtd->defaultPrefix.binding) { int i; int len; - if (!poolAppendChar(&tempPool, XML_T(ASCII_EQUALS))) + if (! poolAppendChar(&parser->m_tempPool, XML_T(ASCII_EQUALS))) return NULL; len = dtd->defaultPrefix.binding->uriLen; - if (namespaceSeparator) + if (parser->m_namespaceSeparator) len--; - for (i = 0; i < len; i++) - if (!poolAppendChar(&tempPool, dtd->defaultPrefix.binding->uri[i])) - return NULL; + for (i = 0; i < len; i++) { + if (! poolAppendChar(&parser->m_tempPool, + dtd->defaultPrefix.binding->uri[i])) { + /* Because of memory caching, I don't believe this line can be + * executed. + * + * This is part of a loop copying the default prefix binding + * URI into the parser's temporary string pool. Previously, + * that URI was copied into the same string pool, with a + * terminating NUL character, as part of setContext(). When + * the pool was cleared, that leaves a block definitely big + * enough to hold the URI on the free block list of the pool. + * The URI copy in getContext() therefore cannot run out of + * memory. + * + * If the pool is used between the setContext() and + * getContext() calls, the worst it can do is leave a bigger + * block on the front of the free list. Given that this is + * all somewhat inobvious and program logic can be changed, we + * don't delete the line but we do exclude it from the test + * coverage statistics. + */ + return NULL; /* LCOV_EXCL_LINE */ + } + } needSep = XML_TRUE; } @@ -5404,102 +7353,111 @@ getContext(XML_Parser parser) int len; const XML_Char *s; PREFIX *prefix = (PREFIX *)hashTableIterNext(&iter); - if (!prefix) + if (! prefix) break; - if (!prefix->binding) - continue; - if (needSep && !poolAppendChar(&tempPool, CONTEXT_SEP)) + if (! prefix->binding) { + /* This test appears to be (justifiable) paranoia. There does + * not seem to be a way of injecting a prefix without a binding + * that doesn't get errored long before this function is called. + * The test should remain for safety's sake, so we instead + * exclude the following line from the coverage statistics. + */ + continue; /* LCOV_EXCL_LINE */ + } + if (needSep && ! poolAppendChar(&parser->m_tempPool, CONTEXT_SEP)) return NULL; for (s = prefix->name; *s; s++) - if (!poolAppendChar(&tempPool, *s)) + if (! poolAppendChar(&parser->m_tempPool, *s)) return NULL; - if (!poolAppendChar(&tempPool, XML_T(ASCII_EQUALS))) + if (! poolAppendChar(&parser->m_tempPool, XML_T(ASCII_EQUALS))) return NULL; len = prefix->binding->uriLen; - if (namespaceSeparator) + if (parser->m_namespaceSeparator) len--; for (i = 0; i < len; i++) - if (!poolAppendChar(&tempPool, prefix->binding->uri[i])) + if (! poolAppendChar(&parser->m_tempPool, prefix->binding->uri[i])) return NULL; needSep = XML_TRUE; } - hashTableIterInit(&iter, &(dtd->generalEntities)); for (;;) { const XML_Char *s; ENTITY *e = (ENTITY *)hashTableIterNext(&iter); - if (!e) + if (! e) break; - if (!e->open) + if (! e->open) continue; - if (needSep && !poolAppendChar(&tempPool, CONTEXT_SEP)) + if (needSep && ! poolAppendChar(&parser->m_tempPool, CONTEXT_SEP)) return NULL; for (s = e->name; *s; s++) - if (!poolAppendChar(&tempPool, *s)) + if (! poolAppendChar(&parser->m_tempPool, *s)) return 0; needSep = XML_TRUE; } - if (!poolAppendChar(&tempPool, XML_T('\0'))) + if (! poolAppendChar(&parser->m_tempPool, XML_T('\0'))) return NULL; - return tempPool.start; + return parser->m_tempPool.start; } static XML_Bool -setContext(XML_Parser parser, const XML_Char *context) -{ - DTD * const dtd = _dtd; /* save one level of indirection */ +setContext(XML_Parser parser, const XML_Char *context) { + if (context == NULL) { + return XML_FALSE; + } + + DTD *const dtd = parser->m_dtd; /* save one level of indirection */ const XML_Char *s = context; while (*context != XML_T('\0')) { if (*s == CONTEXT_SEP || *s == XML_T('\0')) { ENTITY *e; - if (!poolAppendChar(&tempPool, XML_T('\0'))) + if (! poolAppendChar(&parser->m_tempPool, XML_T('\0'))) return XML_FALSE; - e = (ENTITY *)lookup(&dtd->generalEntities, poolStart(&tempPool), 0); + e = (ENTITY *)lookup(parser, &dtd->generalEntities, + poolStart(&parser->m_tempPool), 0); if (e) e->open = XML_TRUE; if (*s != XML_T('\0')) s++; context = s; - poolDiscard(&tempPool); - } - else if (*s == XML_T(ASCII_EQUALS)) { + poolDiscard(&parser->m_tempPool); + } else if (*s == XML_T(ASCII_EQUALS)) { PREFIX *prefix; - if (poolLength(&tempPool) == 0) + if (poolLength(&parser->m_tempPool) == 0) prefix = &dtd->defaultPrefix; else { - if (!poolAppendChar(&tempPool, XML_T('\0'))) + if (! poolAppendChar(&parser->m_tempPool, XML_T('\0'))) return XML_FALSE; - prefix = (PREFIX *)lookup(&dtd->prefixes, poolStart(&tempPool), - sizeof(PREFIX)); - if (!prefix) + prefix + = (PREFIX *)lookup(parser, &dtd->prefixes, + poolStart(&parser->m_tempPool), sizeof(PREFIX)); + if (! prefix) return XML_FALSE; - if (prefix->name == poolStart(&tempPool)) { + if (prefix->name == poolStart(&parser->m_tempPool)) { prefix->name = poolCopyString(&dtd->pool, prefix->name); - if (!prefix->name) + if (! prefix->name) return XML_FALSE; } - poolDiscard(&tempPool); + poolDiscard(&parser->m_tempPool); } - for (context = s + 1; - *context != CONTEXT_SEP && *context != XML_T('\0'); + for (context = s + 1; *context != CONTEXT_SEP && *context != XML_T('\0'); context++) - if (!poolAppendChar(&tempPool, *context)) + if (! poolAppendChar(&parser->m_tempPool, *context)) return XML_FALSE; - if (!poolAppendChar(&tempPool, XML_T('\0'))) + if (! poolAppendChar(&parser->m_tempPool, XML_T('\0'))) return XML_FALSE; - if (addBinding(parser, prefix, NULL, poolStart(&tempPool), - &inheritedBindings) != XML_ERROR_NONE) + if (addBinding(parser, prefix, NULL, poolStart(&parser->m_tempPool), + &parser->m_inheritedBindings) + != XML_ERROR_NONE) return XML_FALSE; - poolDiscard(&tempPool); + poolDiscard(&parser->m_tempPool); if (*context != XML_T('\0')) ++context; s = context; - } - else { - if (!poolAppendChar(&tempPool, *s)) + } else { + if (! poolAppendChar(&parser->m_tempPool, *s)) return XML_FALSE; s++; } @@ -5508,8 +7466,7 @@ setContext(XML_Parser parser, const XML_Char *context) } static void FASTCALL -normalizePublicId(XML_Char *publicId) -{ +normalizePublicId(XML_Char *publicId) { XML_Char *p = publicId; XML_Char *s; for (s = publicId; *s; s++) { @@ -5530,20 +7487,19 @@ normalizePublicId(XML_Char *publicId) } static DTD * -dtdCreate(const XML_Memory_Handling_Suite *ms) -{ - DTD *p = (DTD *)ms->malloc_fcn(sizeof(DTD)); +dtdCreate(XML_Parser parser) { + DTD *p = MALLOC(parser, sizeof(DTD)); if (p == NULL) return p; - poolInit(&(p->pool), ms); - poolInit(&(p->entityValuePool), ms); - hashTableInit(&(p->generalEntities), ms); - hashTableInit(&(p->elementTypes), ms); - hashTableInit(&(p->attributeIds), ms); - hashTableInit(&(p->prefixes), ms); + poolInit(&(p->pool), parser); + poolInit(&(p->entityValuePool), parser); + hashTableInit(&(p->generalEntities), parser); + hashTableInit(&(p->elementTypes), parser); + hashTableInit(&(p->attributeIds), parser); + hashTableInit(&(p->prefixes), parser); #ifdef XML_DTD p->paramEntityRead = XML_FALSE; - hashTableInit(&(p->paramEntities), ms); + hashTableInit(&(p->paramEntities), parser); #endif /* XML_DTD */ p->defaultPrefix.name = NULL; p->defaultPrefix.binding = NULL; @@ -5563,16 +7519,15 @@ dtdCreate(const XML_Memory_Handling_Suite *ms) } static void -dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms) -{ +dtdReset(DTD *p, XML_Parser parser) { HASH_TABLE_ITER iter; hashTableIterInit(&iter, &(p->elementTypes)); for (;;) { ELEMENT_TYPE *e = (ELEMENT_TYPE *)hashTableIterNext(&iter); - if (!e) + if (! e) break; if (e->allocDefaultAtts != 0) - ms->free_fcn(e->defaultAtts); + FREE(parser, e->defaultAtts); } hashTableClear(&(p->generalEntities)); #ifdef XML_DTD @@ -5589,9 +7544,9 @@ dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms) p->in_eldecl = XML_FALSE; - ms->free_fcn(p->scaffIndex); + FREE(parser, p->scaffIndex); p->scaffIndex = NULL; - ms->free_fcn(p->scaffold); + FREE(parser, p->scaffold); p->scaffold = NULL; p->scaffLevel = 0; @@ -5605,16 +7560,15 @@ dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms) } static void -dtdDestroy(DTD *p, XML_Bool isDocEntity, const XML_Memory_Handling_Suite *ms) -{ +dtdDestroy(DTD *p, XML_Bool isDocEntity, XML_Parser parser) { HASH_TABLE_ITER iter; hashTableIterInit(&iter, &(p->elementTypes)); for (;;) { ELEMENT_TYPE *e = (ELEMENT_TYPE *)hashTableIterNext(&iter); - if (!e) + if (! e) break; if (e->allocDefaultAtts != 0) - ms->free_fcn(e->defaultAtts); + FREE(parser, e->defaultAtts); } hashTableDestroy(&(p->generalEntities)); #ifdef XML_DTD @@ -5626,18 +7580,18 @@ dtdDestroy(DTD *p, XML_Bool isDocEntity, const XML_Memory_Handling_Suite *ms) poolDestroy(&(p->pool)); poolDestroy(&(p->entityValuePool)); if (isDocEntity) { - ms->free_fcn(p->scaffIndex); - ms->free_fcn(p->scaffold); + FREE(parser, p->scaffIndex); + FREE(parser, p->scaffold); } - ms->free_fcn(p); + FREE(parser, p); } /* Do a deep copy of the DTD. Return 0 for out of memory, non-zero otherwise. The new DTD has already been initialized. */ static int -dtdCopy(DTD *newDtd, const DTD *oldDtd, const XML_Memory_Handling_Suite *ms) -{ +dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd, + XML_Parser parser) { HASH_TABLE_ITER iter; /* Copy the prefix table. */ @@ -5646,12 +7600,12 @@ dtdCopy(DTD *newDtd, const DTD *oldDtd, const XML_Memory_Handling_Suite *ms) for (;;) { const XML_Char *name; const PREFIX *oldP = (PREFIX *)hashTableIterNext(&iter); - if (!oldP) + if (! oldP) break; name = poolCopyString(&(newDtd->pool), oldP->name); - if (!name) + if (! name) return 0; - if (!lookup(&(newDtd->prefixes), name, sizeof(PREFIX))) + if (! lookup(oldParser, &(newDtd->prefixes), name, sizeof(PREFIX))) return 0; } @@ -5664,18 +7618,18 @@ dtdCopy(DTD *newDtd, const DTD *oldDtd, const XML_Memory_Handling_Suite *ms) const XML_Char *name; const ATTRIBUTE_ID *oldA = (ATTRIBUTE_ID *)hashTableIterNext(&iter); - if (!oldA) + if (! oldA) break; /* Remember to allocate the scratch byte before the name. */ - if (!poolAppendChar(&(newDtd->pool), XML_T('\0'))) + if (! poolAppendChar(&(newDtd->pool), XML_T('\0'))) return 0; name = poolCopyString(&(newDtd->pool), oldA->name); - if (!name) + if (! name) return 0; ++name; - newA = (ATTRIBUTE_ID *)lookup(&(newDtd->attributeIds), name, + newA = (ATTRIBUTE_ID *)lookup(oldParser, &(newDtd->attributeIds), name, sizeof(ATTRIBUTE_ID)); - if (!newA) + if (! newA) return 0; newA->maybeTokenized = oldA->maybeTokenized; if (oldA->prefix) { @@ -5683,7 +7637,7 @@ dtdCopy(DTD *newDtd, const DTD *oldDtd, const XML_Memory_Handling_Suite *ms) if (oldA->prefix == &oldDtd->defaultPrefix) newA->prefix = &newDtd->defaultPrefix; else - newA->prefix = (PREFIX *)lookup(&(newDtd->prefixes), + newA->prefix = (PREFIX *)lookup(oldParser, &(newDtd->prefixes), oldA->prefix->name, 0); } } @@ -5697,56 +7651,61 @@ dtdCopy(DTD *newDtd, const DTD *oldDtd, const XML_Memory_Handling_Suite *ms) ELEMENT_TYPE *newE; const XML_Char *name; const ELEMENT_TYPE *oldE = (ELEMENT_TYPE *)hashTableIterNext(&iter); - if (!oldE) + if (! oldE) break; name = poolCopyString(&(newDtd->pool), oldE->name); - if (!name) + if (! name) return 0; - newE = (ELEMENT_TYPE *)lookup(&(newDtd->elementTypes), name, + newE = (ELEMENT_TYPE *)lookup(oldParser, &(newDtd->elementTypes), name, sizeof(ELEMENT_TYPE)); - if (!newE) + if (! newE) return 0; if (oldE->nDefaultAtts) { - newE->defaultAtts = (DEFAULT_ATTRIBUTE *) - ms->malloc_fcn(oldE->nDefaultAtts * sizeof(DEFAULT_ATTRIBUTE)); - if (!newE->defaultAtts) { - ms->free_fcn(newE); + /* Detect and prevent integer overflow. + * The preprocessor guard addresses the "always false" warning + * from -Wtype-limits on platforms where + * sizeof(int) < sizeof(size_t), e.g. on x86_64. */ +#if UINT_MAX >= SIZE_MAX + if ((size_t)oldE->nDefaultAtts > SIZE_MAX / sizeof(DEFAULT_ATTRIBUTE)) { + return 0; + } +#endif + newE->defaultAtts + = MALLOC(parser, oldE->nDefaultAtts * sizeof(DEFAULT_ATTRIBUTE)); + if (! newE->defaultAtts) { return 0; } } if (oldE->idAtt) - newE->idAtt = (ATTRIBUTE_ID *) - lookup(&(newDtd->attributeIds), oldE->idAtt->name, 0); + newE->idAtt = (ATTRIBUTE_ID *)lookup(oldParser, &(newDtd->attributeIds), + oldE->idAtt->name, 0); newE->allocDefaultAtts = newE->nDefaultAtts = oldE->nDefaultAtts; if (oldE->prefix) - newE->prefix = (PREFIX *)lookup(&(newDtd->prefixes), + newE->prefix = (PREFIX *)lookup(oldParser, &(newDtd->prefixes), oldE->prefix->name, 0); for (i = 0; i < newE->nDefaultAtts; i++) { - newE->defaultAtts[i].id = (ATTRIBUTE_ID *) - lookup(&(newDtd->attributeIds), oldE->defaultAtts[i].id->name, 0); + newE->defaultAtts[i].id = (ATTRIBUTE_ID *)lookup( + oldParser, &(newDtd->attributeIds), oldE->defaultAtts[i].id->name, 0); newE->defaultAtts[i].isCdata = oldE->defaultAtts[i].isCdata; if (oldE->defaultAtts[i].value) { newE->defaultAtts[i].value = poolCopyString(&(newDtd->pool), oldE->defaultAtts[i].value); - if (!newE->defaultAtts[i].value) + if (! newE->defaultAtts[i].value) return 0; - } - else + } else newE->defaultAtts[i].value = NULL; } } /* Copy the entity tables. */ - if (!copyEntityTable(&(newDtd->generalEntities), - &(newDtd->pool), - &(oldDtd->generalEntities))) - return 0; + if (! copyEntityTable(oldParser, &(newDtd->generalEntities), &(newDtd->pool), + &(oldDtd->generalEntities))) + return 0; #ifdef XML_DTD - if (!copyEntityTable(&(newDtd->paramEntities), - &(newDtd->pool), - &(oldDtd->paramEntities))) - return 0; + if (! copyEntityTable(oldParser, &(newDtd->paramEntities), &(newDtd->pool), + &(oldDtd->paramEntities))) + return 0; newDtd->paramEntityRead = oldDtd->paramEntityRead; #endif /* XML_DTD */ @@ -5763,13 +7722,11 @@ dtdCopy(DTD *newDtd, const DTD *oldDtd, const XML_Memory_Handling_Suite *ms) newDtd->scaffIndex = oldDtd->scaffIndex; return 1; -} /* End dtdCopy */ +} /* End dtdCopy */ static int -copyEntityTable(HASH_TABLE *newTable, - STRING_POOL *newPool, - const HASH_TABLE *oldTable) -{ +copyEntityTable(XML_Parser oldParser, HASH_TABLE *newTable, + STRING_POOL *newPool, const HASH_TABLE *oldTable) { HASH_TABLE_ITER iter; const XML_Char *cachedOldBase = NULL; const XML_Char *cachedNewBase = NULL; @@ -5780,17 +7737,17 @@ copyEntityTable(HASH_TABLE *newTable, ENTITY *newE; const XML_Char *name; const ENTITY *oldE = (ENTITY *)hashTableIterNext(&iter); - if (!oldE) + if (! oldE) break; name = poolCopyString(newPool, oldE->name); - if (!name) + if (! name) return 0; - newE = (ENTITY *)lookup(newTable, name, sizeof(ENTITY)); - if (!newE) + newE = (ENTITY *)lookup(oldParser, newTable, name, sizeof(ENTITY)); + if (! newE) return 0; if (oldE->systemId) { const XML_Char *tem = poolCopyString(newPool, oldE->systemId); - if (!tem) + if (! tem) return 0; newE->systemId = tem; if (oldE->base) { @@ -5799,29 +7756,28 @@ copyEntityTable(HASH_TABLE *newTable, else { cachedOldBase = oldE->base; tem = poolCopyString(newPool, cachedOldBase); - if (!tem) + if (! tem) return 0; cachedNewBase = newE->base = tem; } } if (oldE->publicId) { tem = poolCopyString(newPool, oldE->publicId); - if (!tem) + if (! tem) return 0; newE->publicId = tem; } - } - else { - const XML_Char *tem = poolCopyStringN(newPool, oldE->textPtr, - oldE->textLen); - if (!tem) + } else { + const XML_Char *tem + = poolCopyStringN(newPool, oldE->textPtr, oldE->textLen); + if (! tem) return 0; newE->textPtr = tem; newE->textLen = oldE->textLen; } if (oldE->notation) { const XML_Char *tem = poolCopyString(newPool, oldE->notation); - if (!tem) + if (! tem) return 0; newE->notation = tem; } @@ -5834,95 +7790,120 @@ copyEntityTable(HASH_TABLE *newTable, #define INIT_POWER 6 static XML_Bool FASTCALL -keyeq(KEY s1, KEY s2) -{ +keyeq(KEY s1, KEY s2) { for (; *s1 == *s2; s1++, s2++) if (*s1 == 0) return XML_TRUE; return XML_FALSE; } +static size_t +keylen(KEY s) { + size_t len = 0; + for (; *s; s++, len++) + ; + return len; +} + +static void +copy_salt_to_sipkey(XML_Parser parser, struct sipkey *key) { + key->k[0] = 0; + key->k[1] = get_hash_secret_salt(parser); +} + static unsigned long FASTCALL -hash(KEY s) -{ - unsigned long h = 0; - while (*s) - h = CHAR_HASH(h, *s++); - return h; +hash(XML_Parser parser, KEY s) { + struct siphash state; + struct sipkey key; + (void)sip24_valid; + copy_salt_to_sipkey(parser, &key); + sip24_init(&state, &key); + sip24_update(&state, s, keylen(s) * sizeof(XML_Char)); + return (unsigned long)sip24_final(&state); } static NAMED * -lookup(HASH_TABLE *table, KEY name, size_t createSize) -{ +lookup(XML_Parser parser, HASH_TABLE *table, KEY name, size_t createSize) { size_t i; if (table->size == 0) { size_t tsize; - if (!createSize) + if (! createSize) return NULL; table->power = INIT_POWER; /* table->size is a power of 2 */ table->size = (size_t)1 << INIT_POWER; tsize = table->size * sizeof(NAMED *); - table->v = (NAMED **)table->mem->malloc_fcn(tsize); - if (!table->v) { + table->v = MALLOC(table->parser, tsize); + if (! table->v) { table->size = 0; return NULL; } memset(table->v, 0, tsize); - i = hash(name) & ((unsigned long)table->size - 1); - } - else { - unsigned long h = hash(name); + i = hash(parser, name) & ((unsigned long)table->size - 1); + } else { + unsigned long h = hash(parser, name); unsigned long mask = (unsigned long)table->size - 1; unsigned char step = 0; i = h & mask; while (table->v[i]) { if (keyeq(name, table->v[i]->name)) return table->v[i]; - if (!step) + if (! step) step = PROBE_STEP(h, mask, table->power); i < step ? (i += table->size - step) : (i -= step); } - if (!createSize) + if (! createSize) return NULL; /* check for overflow (table is half full) */ if (table->used >> (table->power - 1)) { unsigned char newPower = table->power + 1; + + /* Detect and prevent invalid shift */ + if (newPower >= sizeof(unsigned long) * 8 /* bits per byte */) { + return NULL; + } + size_t newSize = (size_t)1 << newPower; unsigned long newMask = (unsigned long)newSize - 1; + + /* Detect and prevent integer overflow */ + if (newSize > SIZE_MAX / sizeof(NAMED *)) { + return NULL; + } + size_t tsize = newSize * sizeof(NAMED *); - NAMED **newV = (NAMED **)table->mem->malloc_fcn(tsize); - if (!newV) + NAMED **newV = MALLOC(table->parser, tsize); + if (! newV) return NULL; memset(newV, 0, tsize); for (i = 0; i < table->size; i++) if (table->v[i]) { - unsigned long newHash = hash(table->v[i]->name); + unsigned long newHash = hash(parser, table->v[i]->name); size_t j = newHash & newMask; step = 0; while (newV[j]) { - if (!step) + if (! step) step = PROBE_STEP(newHash, newMask, newPower); j < step ? (j += newSize - step) : (j -= step); } newV[j] = table->v[i]; } - table->mem->free_fcn(table->v); + FREE(table->parser, table->v); table->v = newV; table->power = newPower; table->size = newSize; i = h & newMask; step = 0; while (table->v[i]) { - if (!step) + if (! step) step = PROBE_STEP(h, newMask, newPower); i < step ? (i += newSize - step) : (i -= step); } } } - table->v[i] = (NAMED *)table->mem->malloc_fcn(createSize); - if (!table->v[i]) + table->v[i] = MALLOC(table->parser, createSize); + if (! table->v[i]) return NULL; memset(table->v[i], 0, createSize); table->v[i]->name = name; @@ -5931,45 +7912,40 @@ lookup(HASH_TABLE *table, KEY name, size_t createSize) } static void FASTCALL -hashTableClear(HASH_TABLE *table) -{ +hashTableClear(HASH_TABLE *table) { size_t i; for (i = 0; i < table->size; i++) { - table->mem->free_fcn(table->v[i]); + FREE(table->parser, table->v[i]); table->v[i] = NULL; } table->used = 0; } static void FASTCALL -hashTableDestroy(HASH_TABLE *table) -{ +hashTableDestroy(HASH_TABLE *table) { size_t i; for (i = 0; i < table->size; i++) - table->mem->free_fcn(table->v[i]); - table->mem->free_fcn(table->v); + FREE(table->parser, table->v[i]); + FREE(table->parser, table->v); } static void FASTCALL -hashTableInit(HASH_TABLE *p, const XML_Memory_Handling_Suite *ms) -{ +hashTableInit(HASH_TABLE *p, XML_Parser parser) { p->power = 0; p->size = 0; p->used = 0; p->v = NULL; - p->mem = ms; + p->parser = parser; } static void FASTCALL -hashTableIterInit(HASH_TABLE_ITER *iter, const HASH_TABLE *table) -{ +hashTableIterInit(HASH_TABLE_ITER *iter, const HASH_TABLE *table) { iter->p = table->v; - iter->end = iter->p + table->size; + iter->end = iter->p ? iter->p + table->size : NULL; } -static NAMED * FASTCALL -hashTableIterNext(HASH_TABLE_ITER *iter) -{ +static NAMED *FASTCALL +hashTableIterNext(HASH_TABLE_ITER *iter) { while (iter->p != iter->end) { NAMED *tem = *(iter->p)++; if (tem) @@ -5979,20 +7955,18 @@ hashTableIterNext(HASH_TABLE_ITER *iter) } static void FASTCALL -poolInit(STRING_POOL *pool, const XML_Memory_Handling_Suite *ms) -{ +poolInit(STRING_POOL *pool, XML_Parser parser) { pool->blocks = NULL; pool->freeBlocks = NULL; pool->start = NULL; pool->ptr = NULL; pool->end = NULL; - pool->mem = ms; + pool->parser = parser; } static void FASTCALL -poolClear(STRING_POOL *pool) -{ - if (!pool->freeBlocks) +poolClear(STRING_POOL *pool) { + if (! pool->freeBlocks) pool->freeBlocks = pool->blocks; else { BLOCK *p = pool->blocks; @@ -6010,43 +7984,42 @@ poolClear(STRING_POOL *pool) } static void FASTCALL -poolDestroy(STRING_POOL *pool) -{ +poolDestroy(STRING_POOL *pool) { BLOCK *p = pool->blocks; while (p) { BLOCK *tem = p->next; - pool->mem->free_fcn(p); + FREE(pool->parser, p); p = tem; } p = pool->freeBlocks; while (p) { BLOCK *tem = p->next; - pool->mem->free_fcn(p); + FREE(pool->parser, p); p = tem; } } static XML_Char * -poolAppend(STRING_POOL *pool, const ENCODING *enc, - const char *ptr, const char *end) -{ - if (!pool->ptr && !poolGrow(pool)) +poolAppend(STRING_POOL *pool, const ENCODING *enc, const char *ptr, + const char *end) { + if (! pool->ptr && ! poolGrow(pool)) return NULL; for (;;) { - XmlConvert(enc, &ptr, end, (ICHAR **)&(pool->ptr), (ICHAR *)pool->end); - if (ptr == end) + const enum XML_Convert_Result convert_res = XmlConvert( + enc, &ptr, end, (ICHAR **)&(pool->ptr), (const ICHAR *)pool->end); + if ((convert_res == XML_CONVERT_COMPLETED) + || (convert_res == XML_CONVERT_INPUT_INCOMPLETE)) break; - if (!poolGrow(pool)) + if (! poolGrow(pool)) return NULL; } return pool->start; } -static const XML_Char * FASTCALL -poolCopyString(STRING_POOL *pool, const XML_Char *s) -{ +static const XML_Char *FASTCALL +poolCopyString(STRING_POOL *pool, const XML_Char *s) { do { - if (!poolAppendChar(pool, *s)) + if (! poolAppendChar(pool, *s)) return NULL; } while (*s++); s = pool->start; @@ -6055,12 +8028,23 @@ poolCopyString(STRING_POOL *pool, const XML_Char *s) } static const XML_Char * -poolCopyStringN(STRING_POOL *pool, const XML_Char *s, int n) -{ - if (!pool->ptr && !poolGrow(pool)) - return NULL; +poolCopyStringN(STRING_POOL *pool, const XML_Char *s, int n) { + if (! pool->ptr && ! poolGrow(pool)) { + /* The following line is unreachable given the current usage of + * poolCopyStringN(). Currently it is called from exactly one + * place to copy the text of a simple general entity. By that + * point, the name of the entity is already stored in the pool, so + * pool->ptr cannot be NULL. + * + * If poolCopyStringN() is used elsewhere as it well might be, + * this line may well become executable again. Regardless, this + * sort of check shouldn't be removed lightly, so we just exclude + * it from the coverage statistics. + */ + return NULL; /* LCOV_EXCL_LINE */ + } for (; n > 0; --n, s++) { - if (!poolAppendChar(pool, *s)) + if (! poolAppendChar(pool, *s)) return NULL; } s = pool->start; @@ -6068,11 +8052,10 @@ poolCopyStringN(STRING_POOL *pool, const XML_Char *s, int n) return s; } -static const XML_Char * FASTCALL -poolAppendString(STRING_POOL *pool, const XML_Char *s) -{ +static const XML_Char *FASTCALL +poolAppendString(STRING_POOL *pool, const XML_Char *s) { while (*s) { - if (!poolAppendChar(pool, *s)) + if (! poolAppendChar(pool, *s)) return NULL; s++; } @@ -6080,20 +8063,46 @@ poolAppendString(STRING_POOL *pool, const XML_Char *s) } static XML_Char * -poolStoreString(STRING_POOL *pool, const ENCODING *enc, - const char *ptr, const char *end) -{ - if (!poolAppend(pool, enc, ptr, end)) +poolStoreString(STRING_POOL *pool, const ENCODING *enc, const char *ptr, + const char *end) { + if (! poolAppend(pool, enc, ptr, end)) return NULL; - if (pool->ptr == pool->end && !poolGrow(pool)) + if (pool->ptr == pool->end && ! poolGrow(pool)) return NULL; *(pool->ptr)++ = 0; return pool->start; } +static size_t +poolBytesToAllocateFor(int blockSize) { + /* Unprotected math would be: + ** return offsetof(BLOCK, s) + blockSize * sizeof(XML_Char); + ** + ** Detect overflow, avoiding _signed_ overflow undefined behavior + ** For a + b * c we check b * c in isolation first, so that addition of a + ** on top has no chance of making us accept a small non-negative number + */ + const size_t stretch = sizeof(XML_Char); /* can be 4 bytes */ + + if (blockSize <= 0) + return 0; + + if (blockSize > (int)(INT_MAX / stretch)) + return 0; + + { + const int stretchedBlockSize = blockSize * (int)stretch; + const int bytesToAllocate + = (int)(offsetof(BLOCK, s) + (unsigned)stretchedBlockSize); + if (bytesToAllocate < 0) + return 0; + + return (size_t)bytesToAllocate; + } +} + static XML_Bool FASTCALL -poolGrow(STRING_POOL *pool) -{ +poolGrow(STRING_POOL *pool) { if (pool->freeBlocks) { if (pool->start == 0) { pool->blocks = pool->freeBlocks; @@ -6118,35 +8127,76 @@ poolGrow(STRING_POOL *pool) } } if (pool->blocks && pool->start == pool->blocks->s) { - int blockSize = (int)(pool->end - pool->start)*2; - pool->blocks = (BLOCK *) - pool->mem->realloc_fcn(pool->blocks, - (offsetof(BLOCK, s) - + blockSize * sizeof(XML_Char))); - if (pool->blocks == NULL) + BLOCK *temp; + int blockSize = (int)((unsigned)(pool->end - pool->start) * 2U); + size_t bytesToAllocate; + + /* NOTE: Needs to be calculated prior to calling `realloc` + to avoid dangling pointers: */ + const ptrdiff_t offsetInsideBlock = pool->ptr - pool->start; + + if (blockSize < 0) { + /* This condition traps a situation where either more than + * INT_MAX/2 bytes have already been allocated. This isn't + * readily testable, since it is unlikely that an average + * machine will have that much memory, so we exclude it from the + * coverage statistics. + */ + return XML_FALSE; /* LCOV_EXCL_LINE */ + } + + bytesToAllocate = poolBytesToAllocateFor(blockSize); + if (bytesToAllocate == 0) + return XML_FALSE; + + temp = REALLOC(pool->parser, pool->blocks, bytesToAllocate); + if (temp == NULL) return XML_FALSE; + pool->blocks = temp; pool->blocks->size = blockSize; - pool->ptr = pool->blocks->s + (pool->ptr - pool->start); + pool->ptr = pool->blocks->s + offsetInsideBlock; pool->start = pool->blocks->s; pool->end = pool->start + blockSize; - } - else { + } else { BLOCK *tem; int blockSize = (int)(pool->end - pool->start); + size_t bytesToAllocate; + + if (blockSize < 0) { + /* This condition traps a situation where either more than + * INT_MAX bytes have already been allocated (which is prevented + * by various pieces of program logic, not least this one, never + * mind the unlikelihood of actually having that much memory) or + * the pool control fields have been corrupted (which could + * conceivably happen in an extremely buggy user handler + * function). Either way it isn't readily testable, so we + * exclude it from the coverage statistics. + */ + return XML_FALSE; /* LCOV_EXCL_LINE */ + } + if (blockSize < INIT_BLOCK_SIZE) blockSize = INIT_BLOCK_SIZE; - else + else { + /* Detect overflow, avoiding _signed_ overflow undefined behavior */ + if ((int)((unsigned)blockSize * 2U) < 0) { + return XML_FALSE; + } blockSize *= 2; - tem = (BLOCK *)pool->mem->malloc_fcn(offsetof(BLOCK, s) - + blockSize * sizeof(XML_Char)); - if (!tem) + } + + bytesToAllocate = poolBytesToAllocateFor(blockSize); + if (bytesToAllocate == 0) + return XML_FALSE; + + tem = MALLOC(pool->parser, bytesToAllocate); + if (! tem) return XML_FALSE; tem->size = blockSize; tem->next = pool->blocks; pool->blocks = tem; if (pool->ptr != pool->start) - memcpy(tem->s, pool->start, - (pool->ptr - pool->start) * sizeof(XML_Char)); + memcpy(tem->s, pool->start, (pool->ptr - pool->start) * sizeof(XML_Char)); pool->ptr = tem->s + (pool->ptr - pool->start); pool->start = tem->s; pool->end = tem->s + blockSize; @@ -6155,45 +8205,71 @@ poolGrow(STRING_POOL *pool) } static int FASTCALL -nextScaffoldPart(XML_Parser parser) -{ - DTD * const dtd = _dtd; /* save one level of indirection */ - CONTENT_SCAFFOLD * me; +nextScaffoldPart(XML_Parser parser) { + DTD *const dtd = parser->m_dtd; /* save one level of indirection */ + CONTENT_SCAFFOLD *me; int next; - if (!dtd->scaffIndex) { - dtd->scaffIndex = (int *)MALLOC(groupSize * sizeof(int)); - if (!dtd->scaffIndex) + if (! dtd->scaffIndex) { + /* Detect and prevent integer overflow. + * The preprocessor guard addresses the "always false" warning + * from -Wtype-limits on platforms where + * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */ +#if UINT_MAX >= SIZE_MAX + if (parser->m_groupSize > SIZE_MAX / sizeof(int)) { + return -1; + } +#endif + dtd->scaffIndex = MALLOC(parser, parser->m_groupSize * sizeof(int)); + if (! dtd->scaffIndex) return -1; dtd->scaffIndex[0] = 0; } + // Will casting to int be safe further down? + if (dtd->scaffCount > INT_MAX) { + return -1; + } + if (dtd->scaffCount >= dtd->scaffSize) { CONTENT_SCAFFOLD *temp; if (dtd->scaffold) { - temp = (CONTENT_SCAFFOLD *) - REALLOC(dtd->scaffold, dtd->scaffSize * 2 * sizeof(CONTENT_SCAFFOLD)); + /* Detect and prevent integer overflow */ + if (dtd->scaffSize > UINT_MAX / 2u) { + return -1; + } + /* Detect and prevent integer overflow. + * The preprocessor guard addresses the "always false" warning + * from -Wtype-limits on platforms where + * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */ +#if UINT_MAX >= SIZE_MAX + if (dtd->scaffSize > SIZE_MAX / 2u / sizeof(CONTENT_SCAFFOLD)) { + return -1; + } +#endif + + temp = REALLOC(parser, dtd->scaffold, + dtd->scaffSize * 2 * sizeof(CONTENT_SCAFFOLD)); if (temp == NULL) return -1; dtd->scaffSize *= 2; - } - else { - temp = (CONTENT_SCAFFOLD *)MALLOC(INIT_SCAFFOLD_ELEMENTS - * sizeof(CONTENT_SCAFFOLD)); + } else { + temp = MALLOC(parser, INIT_SCAFFOLD_ELEMENTS * sizeof(CONTENT_SCAFFOLD)); if (temp == NULL) return -1; dtd->scaffSize = INIT_SCAFFOLD_ELEMENTS; } dtd->scaffold = temp; } - next = dtd->scaffCount++; + next = (int)dtd->scaffCount++; me = &dtd->scaffold[next]; if (dtd->scaffLevel) { - CONTENT_SCAFFOLD *parent = &dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel-1]]; + CONTENT_SCAFFOLD *parent + = &dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel - 1]]; if (parent->lastchild) { dtd->scaffold[parent->lastchild].nextsib = next; } - if (!parent->childcnt) + if (! parent->childcnt) parent->firstchild = next; parent->lastchild = next; parent->childcnt++; @@ -6202,86 +8278,938 @@ nextScaffoldPart(XML_Parser parser) return next; } -static void -build_node(XML_Parser parser, - int src_node, - XML_Content *dest, - XML_Content **contpos, - XML_Char **strpos) -{ - DTD * const dtd = _dtd; /* save one level of indirection */ - dest->type = dtd->scaffold[src_node].type; - dest->quant = dtd->scaffold[src_node].quant; - if (dest->type == XML_CTYPE_NAME) { - const XML_Char *src; - dest->name = *strpos; - src = dtd->scaffold[src_node].name; - for (;;) { - *(*strpos)++ = *src; - if (!*src) - break; - src++; - } - dest->numchildren = 0; - dest->children = NULL; +static XML_Content * +build_model(XML_Parser parser) { + /* Function build_model transforms the existing parser->m_dtd->scaffold + * array of CONTENT_SCAFFOLD tree nodes into a new array of + * XML_Content tree nodes followed by a gapless list of zero-terminated + * strings. */ + DTD *const dtd = parser->m_dtd; /* save one level of indirection */ + XML_Content *ret; + XML_Char *str; /* the current string writing location */ + + /* Detect and prevent integer overflow. + * The preprocessor guard addresses the "always false" warning + * from -Wtype-limits on platforms where + * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */ +#if UINT_MAX >= SIZE_MAX + if (dtd->scaffCount > SIZE_MAX / sizeof(XML_Content)) { + return NULL; } - else { - unsigned int i; - int cn; - dest->numchildren = dtd->scaffold[src_node].childcnt; - dest->children = *contpos; - *contpos += dest->numchildren; - for (i = 0, cn = dtd->scaffold[src_node].firstchild; - i < dest->numchildren; - i++, cn = dtd->scaffold[cn].nextsib) { - build_node(parser, cn, &(dest->children[i]), contpos, strpos); - } - dest->name = NULL; + if (dtd->contentStringLen > SIZE_MAX / sizeof(XML_Char)) { + return NULL; + } +#endif + if (dtd->scaffCount * sizeof(XML_Content) + > SIZE_MAX - dtd->contentStringLen * sizeof(XML_Char)) { + return NULL; } -} -static XML_Content * -build_model (XML_Parser parser) -{ - DTD * const dtd = _dtd; /* save one level of indirection */ - XML_Content *ret; - XML_Content *cpos; - XML_Char * str; - int allocsize = (dtd->scaffCount * sizeof(XML_Content) - + (dtd->contentStringLen * sizeof(XML_Char))); + const size_t allocsize = (dtd->scaffCount * sizeof(XML_Content) + + (dtd->contentStringLen * sizeof(XML_Char))); - ret = (XML_Content *)MALLOC(allocsize); - if (!ret) + // NOTE: We are avoiding MALLOC(..) here to so that + // applications that are not using XML_FreeContentModel but plain + // free(..) or .free_fcn() to free the content model's memory are safe. + ret = parser->m_mem.malloc_fcn(allocsize); + if (! ret) return NULL; - str = (XML_Char *) (&ret[dtd->scaffCount]); - cpos = &ret[1]; + /* What follows is an iterative implementation (of what was previously done + * recursively in a dedicated function called "build_node". The old recursive + * build_node could be forced into stack exhaustion from input as small as a + * few megabyte, and so that was a security issue. Hence, a function call + * stack is avoided now by resolving recursion.) + * + * The iterative approach works as follows: + * + * - We have two writing pointers, both walking up the result array; one does + * the work, the other creates "jobs" for its colleague to do, and leads + * the way: + * + * - The faster one, pointer jobDest, always leads and writes "what job + * to do" by the other, once they reach that place in the + * array: leader "jobDest" stores the source node array index (relative + * to array dtd->scaffold) in field "numchildren". + * + * - The slower one, pointer dest, looks at the value stored in the + * "numchildren" field (which actually holds a source node array index + * at that time) and puts the real data from dtd->scaffold in. + * + * - Before the loop starts, jobDest writes source array index 0 + * (where the root node is located) so that dest will have something to do + * when it starts operation. + * + * - Whenever nodes with children are encountered, jobDest appends + * them as new jobs, in order. As a result, tree node siblings are + * adjacent in the resulting array, for example: + * + * [0] root, has two children + * [1] first child of 0, has three children + * [3] first child of 1, does not have children + * [4] second child of 1, does not have children + * [5] third child of 1, does not have children + * [2] second child of 0, does not have children + * + * Or (the same data) presented in flat array view: + * + * [0] root, has two children + * + * [1] first child of 0, has three children + * [2] second child of 0, does not have children + * + * [3] first child of 1, does not have children + * [4] second child of 1, does not have children + * [5] third child of 1, does not have children + * + * - The algorithm repeats until all target array indices have been processed. + */ + XML_Content *dest = ret; /* tree node writing location, moves upwards */ + XML_Content *const destLimit = &ret[dtd->scaffCount]; + XML_Content *jobDest = ret; /* next free writing location in target array */ + str = (XML_Char *)&ret[dtd->scaffCount]; + + /* Add the starting job, the root node (index 0) of the source tree */ + (jobDest++)->numchildren = 0; + + for (; dest < destLimit; dest++) { + /* Retrieve source tree array index from job storage */ + const int src_node = (int)dest->numchildren; + + /* Convert item */ + dest->type = dtd->scaffold[src_node].type; + dest->quant = dtd->scaffold[src_node].quant; + if (dest->type == XML_CTYPE_NAME) { + const XML_Char *src; + dest->name = str; + src = dtd->scaffold[src_node].name; + for (;;) { + *str++ = *src; + if (! *src) + break; + src++; + } + dest->numchildren = 0; + dest->children = NULL; + } else { + unsigned int i; + int cn; + dest->name = NULL; + dest->numchildren = dtd->scaffold[src_node].childcnt; + dest->children = jobDest; + + /* Append scaffold indices of children to array */ + for (i = 0, cn = dtd->scaffold[src_node].firstchild; + i < dest->numchildren; i++, cn = dtd->scaffold[cn].nextsib) + (jobDest++)->numchildren = (unsigned int)cn; + } + } - build_node(parser, 0, ret, &cpos, &str); return ret; } static ELEMENT_TYPE * -getElementType(XML_Parser parser, - const ENCODING *enc, - const char *ptr, - const char *end) -{ - DTD * const dtd = _dtd; /* save one level of indirection */ +getElementType(XML_Parser parser, const ENCODING *enc, const char *ptr, + const char *end) { + DTD *const dtd = parser->m_dtd; /* save one level of indirection */ const XML_Char *name = poolStoreString(&dtd->pool, enc, ptr, end); ELEMENT_TYPE *ret; - if (!name) + if (! name) return NULL; - ret = (ELEMENT_TYPE *) lookup(&dtd->elementTypes, name, sizeof(ELEMENT_TYPE)); - if (!ret) + ret = (ELEMENT_TYPE *)lookup(parser, &dtd->elementTypes, name, + sizeof(ELEMENT_TYPE)); + if (! ret) return NULL; if (ret->name != name) poolDiscard(&dtd->pool); else { poolFinish(&dtd->pool); - if (!setElementTypePrefix(parser, ret)) + if (! setElementTypePrefix(parser, ret)) return NULL; } return ret; } + +static XML_Char * +copyString(const XML_Char *s, XML_Parser parser) { + size_t charsRequired = 0; + XML_Char *result; + + /* First determine how long the string is */ + while (s[charsRequired] != 0) { + charsRequired++; + } + /* Include the terminator */ + charsRequired++; + + /* Now allocate space for the copy */ + result = MALLOC(parser, charsRequired * sizeof(XML_Char)); + if (result == NULL) + return NULL; + /* Copy the original into place */ + memcpy(result, s, charsRequired * sizeof(XML_Char)); + return result; +} + +#if XML_GE == 1 + +static float +accountingGetCurrentAmplification(XML_Parser rootParser) { + // 1.........1.........12 => 22 + const size_t lenOfShortestInclude = sizeof("") - 1; + const XmlBigCount countBytesOutput + = rootParser->m_accounting.countBytesDirect + + rootParser->m_accounting.countBytesIndirect; + const float amplificationFactor + = rootParser->m_accounting.countBytesDirect + ? ((float)countBytesOutput + / (float)(rootParser->m_accounting.countBytesDirect)) + : ((float)(lenOfShortestInclude + + rootParser->m_accounting.countBytesIndirect) + / (float)lenOfShortestInclude); + assert(! rootParser->m_parentParser); + return amplificationFactor; +} + +static void +accountingReportStats(XML_Parser originParser, const char *epilog) { + const XML_Parser rootParser = getRootParserOf(originParser, NULL); + assert(! rootParser->m_parentParser); + + if (rootParser->m_accounting.debugLevel == 0u) { + return; + } + + const float amplificationFactor + = accountingGetCurrentAmplification(rootParser); + fprintf(stderr, + "expat: Accounting(%p): Direct " EXPAT_FMT_ULL( + "10") ", indirect " EXPAT_FMT_ULL("10") ", amplification %8.2f%s", + (void *)rootParser, rootParser->m_accounting.countBytesDirect, + rootParser->m_accounting.countBytesIndirect, + (double)amplificationFactor, epilog); +} + +static void +accountingOnAbort(XML_Parser originParser) { + accountingReportStats(originParser, " ABORTING\n"); +} + +static void +accountingReportDiff(XML_Parser rootParser, + unsigned int levelsAwayFromRootParser, const char *before, + const char *after, ptrdiff_t bytesMore, int source_line, + enum XML_Account account) { + assert(! rootParser->m_parentParser); + + fprintf(stderr, + " (+" EXPAT_FMT_PTRDIFF_T("6") " bytes %s|%u, xmlparse.c:%d) %*s\"", + bytesMore, (account == XML_ACCOUNT_DIRECT) ? "DIR" : "EXP", + levelsAwayFromRootParser, source_line, 10, ""); + + const char ellipis[] = "[..]"; + const size_t ellipsisLength = sizeof(ellipis) /* because compile-time */ - 1; + const unsigned int contextLength = 10; + + /* Note: Performance is of no concern here */ + const char *walker = before; + if ((rootParser->m_accounting.debugLevel >= 3u) + || (after - before) + <= (ptrdiff_t)(contextLength + ellipsisLength + contextLength)) { + for (; walker < after; walker++) { + fprintf(stderr, "%s", unsignedCharToPrintable(walker[0])); + } + } else { + for (; walker < before + contextLength; walker++) { + fprintf(stderr, "%s", unsignedCharToPrintable(walker[0])); + } + fprintf(stderr, ellipis); + walker = after - contextLength; + for (; walker < after; walker++) { + fprintf(stderr, "%s", unsignedCharToPrintable(walker[0])); + } + } + fprintf(stderr, "\"\n"); +} + +static XML_Bool +accountingDiffTolerated(XML_Parser originParser, int tok, const char *before, + const char *after, int source_line, + enum XML_Account account) { + /* Note: We need to check the token type *first* to be sure that + * we can even access variable , safely. + * E.g. for XML_TOK_NONE may hold an invalid pointer. */ + switch (tok) { + case XML_TOK_INVALID: + case XML_TOK_PARTIAL: + case XML_TOK_PARTIAL_CHAR: + case XML_TOK_NONE: + return XML_TRUE; + } + + if (account == XML_ACCOUNT_NONE) + return XML_TRUE; /* because these bytes have been accounted for, already */ + + unsigned int levelsAwayFromRootParser; + const XML_Parser rootParser + = getRootParserOf(originParser, &levelsAwayFromRootParser); + assert(! rootParser->m_parentParser); + + const int isDirect + = (account == XML_ACCOUNT_DIRECT) && (originParser == rootParser); + const ptrdiff_t bytesMore = after - before; + + XmlBigCount *const additionTarget + = isDirect ? &rootParser->m_accounting.countBytesDirect + : &rootParser->m_accounting.countBytesIndirect; + + /* Detect and avoid integer overflow */ + if (*additionTarget > (XmlBigCount)(-1) - (XmlBigCount)bytesMore) + return XML_FALSE; + *additionTarget += bytesMore; + + const XmlBigCount countBytesOutput + = rootParser->m_accounting.countBytesDirect + + rootParser->m_accounting.countBytesIndirect; + const float amplificationFactor + = accountingGetCurrentAmplification(rootParser); + const XML_Bool tolerated + = (countBytesOutput < rootParser->m_accounting.activationThresholdBytes) + || (amplificationFactor + <= rootParser->m_accounting.maximumAmplificationFactor); + + if (rootParser->m_accounting.debugLevel >= 2u) { + accountingReportStats(rootParser, ""); + accountingReportDiff(rootParser, levelsAwayFromRootParser, before, after, + bytesMore, source_line, account); + } + + return tolerated; +} + +unsigned long long +testingAccountingGetCountBytesDirect(XML_Parser parser) { + if (! parser) + return 0; + return parser->m_accounting.countBytesDirect; +} + +unsigned long long +testingAccountingGetCountBytesIndirect(XML_Parser parser) { + if (! parser) + return 0; + return parser->m_accounting.countBytesIndirect; +} + +static void +entityTrackingReportStats(XML_Parser rootParser, ENTITY *entity, + const char *action, int sourceLine) { + assert(! rootParser->m_parentParser); + if (rootParser->m_entity_stats.debugLevel == 0u) + return; + +# if defined(XML_UNICODE) + const char *const entityName = "[..]"; +# else + const char *const entityName = entity->name; +# endif + + fprintf( + stderr, + "expat: Entities(%p): Count %9u, depth %2u/%2u %*s%s%s; %s length %d (xmlparse.c:%d)\n", + (void *)rootParser, rootParser->m_entity_stats.countEverOpened, + rootParser->m_entity_stats.currentDepth, + rootParser->m_entity_stats.maximumDepthSeen, + ((int)rootParser->m_entity_stats.currentDepth - 1) * 2, "", + entity->is_param ? "%" : "&", entityName, action, entity->textLen, + sourceLine); +} + +static void +entityTrackingOnOpen(XML_Parser originParser, ENTITY *entity, int sourceLine) { + const XML_Parser rootParser = getRootParserOf(originParser, NULL); + assert(! rootParser->m_parentParser); + + rootParser->m_entity_stats.countEverOpened++; + rootParser->m_entity_stats.currentDepth++; + if (rootParser->m_entity_stats.currentDepth + > rootParser->m_entity_stats.maximumDepthSeen) { + rootParser->m_entity_stats.maximumDepthSeen++; + } + + entityTrackingReportStats(rootParser, entity, "OPEN ", sourceLine); +} + +static void +entityTrackingOnClose(XML_Parser originParser, ENTITY *entity, int sourceLine) { + const XML_Parser rootParser = getRootParserOf(originParser, NULL); + assert(! rootParser->m_parentParser); + + entityTrackingReportStats(rootParser, entity, "CLOSE", sourceLine); + rootParser->m_entity_stats.currentDepth--; +} + +#endif /* XML_GE == 1 */ + +static XML_Parser +getRootParserOf(XML_Parser parser, unsigned int *outLevelDiff) { + XML_Parser rootParser = parser; + unsigned int stepsTakenUpwards = 0; + while (rootParser->m_parentParser) { + rootParser = rootParser->m_parentParser; + stepsTakenUpwards++; + } + assert(! rootParser->m_parentParser); + if (outLevelDiff != NULL) { + *outLevelDiff = stepsTakenUpwards; + } + return rootParser; +} + +#if XML_GE == 1 + +const char * +unsignedCharToPrintable(unsigned char c) { + switch (c) { + case 0: + return "\\0"; + case 1: + return "\\x1"; + case 2: + return "\\x2"; + case 3: + return "\\x3"; + case 4: + return "\\x4"; + case 5: + return "\\x5"; + case 6: + return "\\x6"; + case 7: + return "\\x7"; + case 8: + return "\\x8"; + case 9: + return "\\t"; + case 10: + return "\\n"; + case 11: + return "\\xB"; + case 12: + return "\\xC"; + case 13: + return "\\r"; + case 14: + return "\\xE"; + case 15: + return "\\xF"; + case 16: + return "\\x10"; + case 17: + return "\\x11"; + case 18: + return "\\x12"; + case 19: + return "\\x13"; + case 20: + return "\\x14"; + case 21: + return "\\x15"; + case 22: + return "\\x16"; + case 23: + return "\\x17"; + case 24: + return "\\x18"; + case 25: + return "\\x19"; + case 26: + return "\\x1A"; + case 27: + return "\\x1B"; + case 28: + return "\\x1C"; + case 29: + return "\\x1D"; + case 30: + return "\\x1E"; + case 31: + return "\\x1F"; + case 32: + return " "; + case 33: + return "!"; + case 34: + return "\\\""; + case 35: + return "#"; + case 36: + return "$"; + case 37: + return "%"; + case 38: + return "&"; + case 39: + return "'"; + case 40: + return "("; + case 41: + return ")"; + case 42: + return "*"; + case 43: + return "+"; + case 44: + return ","; + case 45: + return "-"; + case 46: + return "."; + case 47: + return "/"; + case 48: + return "0"; + case 49: + return "1"; + case 50: + return "2"; + case 51: + return "3"; + case 52: + return "4"; + case 53: + return "5"; + case 54: + return "6"; + case 55: + return "7"; + case 56: + return "8"; + case 57: + return "9"; + case 58: + return ":"; + case 59: + return ";"; + case 60: + return "<"; + case 61: + return "="; + case 62: + return ">"; + case 63: + return "?"; + case 64: + return "@"; + case 65: + return "A"; + case 66: + return "B"; + case 67: + return "C"; + case 68: + return "D"; + case 69: + return "E"; + case 70: + return "F"; + case 71: + return "G"; + case 72: + return "H"; + case 73: + return "I"; + case 74: + return "J"; + case 75: + return "K"; + case 76: + return "L"; + case 77: + return "M"; + case 78: + return "N"; + case 79: + return "O"; + case 80: + return "P"; + case 81: + return "Q"; + case 82: + return "R"; + case 83: + return "S"; + case 84: + return "T"; + case 85: + return "U"; + case 86: + return "V"; + case 87: + return "W"; + case 88: + return "X"; + case 89: + return "Y"; + case 90: + return "Z"; + case 91: + return "["; + case 92: + return "\\\\"; + case 93: + return "]"; + case 94: + return "^"; + case 95: + return "_"; + case 96: + return "`"; + case 97: + return "a"; + case 98: + return "b"; + case 99: + return "c"; + case 100: + return "d"; + case 101: + return "e"; + case 102: + return "f"; + case 103: + return "g"; + case 104: + return "h"; + case 105: + return "i"; + case 106: + return "j"; + case 107: + return "k"; + case 108: + return "l"; + case 109: + return "m"; + case 110: + return "n"; + case 111: + return "o"; + case 112: + return "p"; + case 113: + return "q"; + case 114: + return "r"; + case 115: + return "s"; + case 116: + return "t"; + case 117: + return "u"; + case 118: + return "v"; + case 119: + return "w"; + case 120: + return "x"; + case 121: + return "y"; + case 122: + return "z"; + case 123: + return "{"; + case 124: + return "|"; + case 125: + return "}"; + case 126: + return "~"; + case 127: + return "\\x7F"; + case 128: + return "\\x80"; + case 129: + return "\\x81"; + case 130: + return "\\x82"; + case 131: + return "\\x83"; + case 132: + return "\\x84"; + case 133: + return "\\x85"; + case 134: + return "\\x86"; + case 135: + return "\\x87"; + case 136: + return "\\x88"; + case 137: + return "\\x89"; + case 138: + return "\\x8A"; + case 139: + return "\\x8B"; + case 140: + return "\\x8C"; + case 141: + return "\\x8D"; + case 142: + return "\\x8E"; + case 143: + return "\\x8F"; + case 144: + return "\\x90"; + case 145: + return "\\x91"; + case 146: + return "\\x92"; + case 147: + return "\\x93"; + case 148: + return "\\x94"; + case 149: + return "\\x95"; + case 150: + return "\\x96"; + case 151: + return "\\x97"; + case 152: + return "\\x98"; + case 153: + return "\\x99"; + case 154: + return "\\x9A"; + case 155: + return "\\x9B"; + case 156: + return "\\x9C"; + case 157: + return "\\x9D"; + case 158: + return "\\x9E"; + case 159: + return "\\x9F"; + case 160: + return "\\xA0"; + case 161: + return "\\xA1"; + case 162: + return "\\xA2"; + case 163: + return "\\xA3"; + case 164: + return "\\xA4"; + case 165: + return "\\xA5"; + case 166: + return "\\xA6"; + case 167: + return "\\xA7"; + case 168: + return "\\xA8"; + case 169: + return "\\xA9"; + case 170: + return "\\xAA"; + case 171: + return "\\xAB"; + case 172: + return "\\xAC"; + case 173: + return "\\xAD"; + case 174: + return "\\xAE"; + case 175: + return "\\xAF"; + case 176: + return "\\xB0"; + case 177: + return "\\xB1"; + case 178: + return "\\xB2"; + case 179: + return "\\xB3"; + case 180: + return "\\xB4"; + case 181: + return "\\xB5"; + case 182: + return "\\xB6"; + case 183: + return "\\xB7"; + case 184: + return "\\xB8"; + case 185: + return "\\xB9"; + case 186: + return "\\xBA"; + case 187: + return "\\xBB"; + case 188: + return "\\xBC"; + case 189: + return "\\xBD"; + case 190: + return "\\xBE"; + case 191: + return "\\xBF"; + case 192: + return "\\xC0"; + case 193: + return "\\xC1"; + case 194: + return "\\xC2"; + case 195: + return "\\xC3"; + case 196: + return "\\xC4"; + case 197: + return "\\xC5"; + case 198: + return "\\xC6"; + case 199: + return "\\xC7"; + case 200: + return "\\xC8"; + case 201: + return "\\xC9"; + case 202: + return "\\xCA"; + case 203: + return "\\xCB"; + case 204: + return "\\xCC"; + case 205: + return "\\xCD"; + case 206: + return "\\xCE"; + case 207: + return "\\xCF"; + case 208: + return "\\xD0"; + case 209: + return "\\xD1"; + case 210: + return "\\xD2"; + case 211: + return "\\xD3"; + case 212: + return "\\xD4"; + case 213: + return "\\xD5"; + case 214: + return "\\xD6"; + case 215: + return "\\xD7"; + case 216: + return "\\xD8"; + case 217: + return "\\xD9"; + case 218: + return "\\xDA"; + case 219: + return "\\xDB"; + case 220: + return "\\xDC"; + case 221: + return "\\xDD"; + case 222: + return "\\xDE"; + case 223: + return "\\xDF"; + case 224: + return "\\xE0"; + case 225: + return "\\xE1"; + case 226: + return "\\xE2"; + case 227: + return "\\xE3"; + case 228: + return "\\xE4"; + case 229: + return "\\xE5"; + case 230: + return "\\xE6"; + case 231: + return "\\xE7"; + case 232: + return "\\xE8"; + case 233: + return "\\xE9"; + case 234: + return "\\xEA"; + case 235: + return "\\xEB"; + case 236: + return "\\xEC"; + case 237: + return "\\xED"; + case 238: + return "\\xEE"; + case 239: + return "\\xEF"; + case 240: + return "\\xF0"; + case 241: + return "\\xF1"; + case 242: + return "\\xF2"; + case 243: + return "\\xF3"; + case 244: + return "\\xF4"; + case 245: + return "\\xF5"; + case 246: + return "\\xF6"; + case 247: + return "\\xF7"; + case 248: + return "\\xF8"; + case 249: + return "\\xF9"; + case 250: + return "\\xFA"; + case 251: + return "\\xFB"; + case 252: + return "\\xFC"; + case 253: + return "\\xFD"; + case 254: + return "\\xFE"; + case 255: + return "\\xFF"; + // LCOV_EXCL_START + default: + assert(0); /* never gets here */ + return "dead code"; + } + assert(0); /* never gets here */ + // LCOV_EXCL_STOP +} + +#endif /* XML_GE == 1 */ + +static unsigned long +getDebugLevel(const char *variableName, unsigned long defaultDebugLevel) { + const char *const valueOrNull = getenv(variableName); + if (valueOrNull == NULL) { + return defaultDebugLevel; + } + const char *const value = valueOrNull; + + errno = 0; + char *afterValue = NULL; + unsigned long debugLevel = strtoul(value, &afterValue, 10); + if ((errno != 0) || (afterValue == value) || (afterValue[0] != '\0')) { + errno = 0; + return defaultDebugLevel; + } + + return debugLevel; +} diff --git a/cextern/expat/lib/xmlrole.c b/cextern/expat/lib/xmlrole.c old mode 100755 new mode 100644 index 9c5e25b179f5..2c48bf408679 --- a/cextern/expat/lib/xmlrole.c +++ b/cextern/expat/lib/xmlrole.c @@ -1,22 +1,50 @@ -/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd - See the file COPYING for copying permission. +/* + __ __ _ + ___\ \/ /_ __ __ _| |_ + / _ \\ /| '_ \ / _` | __| + | __// \| |_) | (_| | |_ + \___/_/\_\ .__/ \__,_|\__| + |_| XML parser + + Copyright (c) 1997-2000 Thai Open Source Software Center Ltd + Copyright (c) 2000 Clark Cooper + Copyright (c) 2002 Greg Stein + Copyright (c) 2002-2006 Karl Waclawek + Copyright (c) 2002-2003 Fred L. Drake, Jr. + Copyright (c) 2005-2009 Steven Solie + Copyright (c) 2016-2023 Sebastian Pipping + Copyright (c) 2017 Rhodri James + Copyright (c) 2019 David Loffredo + Copyright (c) 2021 Donghee Na + Licensed under the MIT license: + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to permit + persons to whom the Software is furnished to do so, subject to the + following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#include "expat_config.h" + #include -#ifdef COMPILED_FROM_DSP -#include "winconfig.h" -#elif defined(MACOS_CLASSIC) -#include "macconfig.h" -#elif defined(__amigaos4__) -#include "amigaconfig.h" -#elif defined(__WATCOMC__) -#include "watcomconfig.h" -#else -#ifdef HAVE_EXPAT_CONFIG_H -#include +#ifdef _WIN32 +# include "winconfig.h" #endif -#endif /* ndef COMPILED_FROM_DSP */ #include "expat_external.h" #include "internal.h" @@ -30,107 +58,88 @@ */ -static const char KW_ANY[] = { - ASCII_A, ASCII_N, ASCII_Y, '\0' }; -static const char KW_ATTLIST[] = { - ASCII_A, ASCII_T, ASCII_T, ASCII_L, ASCII_I, ASCII_S, ASCII_T, '\0' }; -static const char KW_CDATA[] = { - ASCII_C, ASCII_D, ASCII_A, ASCII_T, ASCII_A, '\0' }; -static const char KW_DOCTYPE[] = { - ASCII_D, ASCII_O, ASCII_C, ASCII_T, ASCII_Y, ASCII_P, ASCII_E, '\0' }; -static const char KW_ELEMENT[] = { - ASCII_E, ASCII_L, ASCII_E, ASCII_M, ASCII_E, ASCII_N, ASCII_T, '\0' }; -static const char KW_EMPTY[] = { - ASCII_E, ASCII_M, ASCII_P, ASCII_T, ASCII_Y, '\0' }; -static const char KW_ENTITIES[] = { - ASCII_E, ASCII_N, ASCII_T, ASCII_I, ASCII_T, ASCII_I, ASCII_E, ASCII_S, - '\0' }; -static const char KW_ENTITY[] = { - ASCII_E, ASCII_N, ASCII_T, ASCII_I, ASCII_T, ASCII_Y, '\0' }; -static const char KW_FIXED[] = { - ASCII_F, ASCII_I, ASCII_X, ASCII_E, ASCII_D, '\0' }; -static const char KW_ID[] = { - ASCII_I, ASCII_D, '\0' }; -static const char KW_IDREF[] = { - ASCII_I, ASCII_D, ASCII_R, ASCII_E, ASCII_F, '\0' }; -static const char KW_IDREFS[] = { - ASCII_I, ASCII_D, ASCII_R, ASCII_E, ASCII_F, ASCII_S, '\0' }; +static const char KW_ANY[] = {ASCII_A, ASCII_N, ASCII_Y, '\0'}; +static const char KW_ATTLIST[] + = {ASCII_A, ASCII_T, ASCII_T, ASCII_L, ASCII_I, ASCII_S, ASCII_T, '\0'}; +static const char KW_CDATA[] + = {ASCII_C, ASCII_D, ASCII_A, ASCII_T, ASCII_A, '\0'}; +static const char KW_DOCTYPE[] + = {ASCII_D, ASCII_O, ASCII_C, ASCII_T, ASCII_Y, ASCII_P, ASCII_E, '\0'}; +static const char KW_ELEMENT[] + = {ASCII_E, ASCII_L, ASCII_E, ASCII_M, ASCII_E, ASCII_N, ASCII_T, '\0'}; +static const char KW_EMPTY[] + = {ASCII_E, ASCII_M, ASCII_P, ASCII_T, ASCII_Y, '\0'}; +static const char KW_ENTITIES[] = {ASCII_E, ASCII_N, ASCII_T, ASCII_I, ASCII_T, + ASCII_I, ASCII_E, ASCII_S, '\0'}; +static const char KW_ENTITY[] + = {ASCII_E, ASCII_N, ASCII_T, ASCII_I, ASCII_T, ASCII_Y, '\0'}; +static const char KW_FIXED[] + = {ASCII_F, ASCII_I, ASCII_X, ASCII_E, ASCII_D, '\0'}; +static const char KW_ID[] = {ASCII_I, ASCII_D, '\0'}; +static const char KW_IDREF[] + = {ASCII_I, ASCII_D, ASCII_R, ASCII_E, ASCII_F, '\0'}; +static const char KW_IDREFS[] + = {ASCII_I, ASCII_D, ASCII_R, ASCII_E, ASCII_F, ASCII_S, '\0'}; #ifdef XML_DTD -static const char KW_IGNORE[] = { - ASCII_I, ASCII_G, ASCII_N, ASCII_O, ASCII_R, ASCII_E, '\0' }; +static const char KW_IGNORE[] + = {ASCII_I, ASCII_G, ASCII_N, ASCII_O, ASCII_R, ASCII_E, '\0'}; #endif -static const char KW_IMPLIED[] = { - ASCII_I, ASCII_M, ASCII_P, ASCII_L, ASCII_I, ASCII_E, ASCII_D, '\0' }; +static const char KW_IMPLIED[] + = {ASCII_I, ASCII_M, ASCII_P, ASCII_L, ASCII_I, ASCII_E, ASCII_D, '\0'}; #ifdef XML_DTD -static const char KW_INCLUDE[] = { - ASCII_I, ASCII_N, ASCII_C, ASCII_L, ASCII_U, ASCII_D, ASCII_E, '\0' }; +static const char KW_INCLUDE[] + = {ASCII_I, ASCII_N, ASCII_C, ASCII_L, ASCII_U, ASCII_D, ASCII_E, '\0'}; #endif -static const char KW_NDATA[] = { - ASCII_N, ASCII_D, ASCII_A, ASCII_T, ASCII_A, '\0' }; -static const char KW_NMTOKEN[] = { - ASCII_N, ASCII_M, ASCII_T, ASCII_O, ASCII_K, ASCII_E, ASCII_N, '\0' }; -static const char KW_NMTOKENS[] = { - ASCII_N, ASCII_M, ASCII_T, ASCII_O, ASCII_K, ASCII_E, ASCII_N, ASCII_S, - '\0' }; -static const char KW_NOTATION[] = - { ASCII_N, ASCII_O, ASCII_T, ASCII_A, ASCII_T, ASCII_I, ASCII_O, ASCII_N, - '\0' }; -static const char KW_PCDATA[] = { - ASCII_P, ASCII_C, ASCII_D, ASCII_A, ASCII_T, ASCII_A, '\0' }; -static const char KW_PUBLIC[] = { - ASCII_P, ASCII_U, ASCII_B, ASCII_L, ASCII_I, ASCII_C, '\0' }; -static const char KW_REQUIRED[] = { - ASCII_R, ASCII_E, ASCII_Q, ASCII_U, ASCII_I, ASCII_R, ASCII_E, ASCII_D, - '\0' }; -static const char KW_SYSTEM[] = { - ASCII_S, ASCII_Y, ASCII_S, ASCII_T, ASCII_E, ASCII_M, '\0' }; +static const char KW_NDATA[] + = {ASCII_N, ASCII_D, ASCII_A, ASCII_T, ASCII_A, '\0'}; +static const char KW_NMTOKEN[] + = {ASCII_N, ASCII_M, ASCII_T, ASCII_O, ASCII_K, ASCII_E, ASCII_N, '\0'}; +static const char KW_NMTOKENS[] = {ASCII_N, ASCII_M, ASCII_T, ASCII_O, ASCII_K, + ASCII_E, ASCII_N, ASCII_S, '\0'}; +static const char KW_NOTATION[] = {ASCII_N, ASCII_O, ASCII_T, ASCII_A, ASCII_T, + ASCII_I, ASCII_O, ASCII_N, '\0'}; +static const char KW_PCDATA[] + = {ASCII_P, ASCII_C, ASCII_D, ASCII_A, ASCII_T, ASCII_A, '\0'}; +static const char KW_PUBLIC[] + = {ASCII_P, ASCII_U, ASCII_B, ASCII_L, ASCII_I, ASCII_C, '\0'}; +static const char KW_REQUIRED[] = {ASCII_R, ASCII_E, ASCII_Q, ASCII_U, ASCII_I, + ASCII_R, ASCII_E, ASCII_D, '\0'}; +static const char KW_SYSTEM[] + = {ASCII_S, ASCII_Y, ASCII_S, ASCII_T, ASCII_E, ASCII_M, '\0'}; #ifndef MIN_BYTES_PER_CHAR -#define MIN_BYTES_PER_CHAR(enc) ((enc)->minBytesPerChar) +# define MIN_BYTES_PER_CHAR(enc) ((enc)->minBytesPerChar) #endif #ifdef XML_DTD -#define setTopLevel(state) \ - ((state)->handler = ((state)->documentEntity \ - ? internalSubset \ - : externalSubset1)) +# define setTopLevel(state) \ + ((state)->handler \ + = ((state)->documentEntity ? internalSubset : externalSubset1)) #else /* not XML_DTD */ -#define setTopLevel(state) ((state)->handler = internalSubset) +# define setTopLevel(state) ((state)->handler = internalSubset) #endif /* not XML_DTD */ -typedef int PTRCALL PROLOG_HANDLER(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, +typedef int PTRCALL PROLOG_HANDLER(PROLOG_STATE *state, int tok, + const char *ptr, const char *end, const ENCODING *enc); -static PROLOG_HANDLER - prolog0, prolog1, prolog2, - doctype0, doctype1, doctype2, doctype3, doctype4, doctype5, - internalSubset, - entity0, entity1, entity2, entity3, entity4, entity5, entity6, - entity7, entity8, entity9, entity10, - notation0, notation1, notation2, notation3, notation4, - attlist0, attlist1, attlist2, attlist3, attlist4, attlist5, attlist6, - attlist7, attlist8, attlist9, - element0, element1, element2, element3, element4, element5, element6, - element7, +static PROLOG_HANDLER prolog0, prolog1, prolog2, doctype0, doctype1, doctype2, + doctype3, doctype4, doctype5, internalSubset, entity0, entity1, entity2, + entity3, entity4, entity5, entity6, entity7, entity8, entity9, entity10, + notation0, notation1, notation2, notation3, notation4, attlist0, attlist1, + attlist2, attlist3, attlist4, attlist5, attlist6, attlist7, attlist8, + attlist9, element0, element1, element2, element3, element4, element5, + element6, element7, #ifdef XML_DTD - externalSubset0, externalSubset1, - condSect0, condSect1, condSect2, + externalSubset0, externalSubset1, condSect0, condSect1, condSect2, #endif /* XML_DTD */ - declClose, - error; + declClose, error; static int FASTCALL common(PROLOG_STATE *state, int tok); static int PTRCALL -prolog0(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ +prolog0(PROLOG_STATE *state, int tok, const char *ptr, const char *end, + const ENCODING *enc) { switch (tok) { case XML_TOK_PROLOG_S: state->handler = prolog1; @@ -147,10 +156,8 @@ prolog0(PROLOG_STATE *state, case XML_TOK_BOM: return XML_ROLE_NONE; case XML_TOK_DECL_OPEN: - if (!XmlNameMatchesAscii(enc, - ptr + 2 * MIN_BYTES_PER_CHAR(enc), - end, - KW_DOCTYPE)) + if (! XmlNameMatchesAscii(enc, ptr + 2 * MIN_BYTES_PER_CHAR(enc), end, + KW_DOCTYPE)) break; state->handler = doctype0; return XML_ROLE_DOCTYPE_NONE; @@ -162,12 +169,8 @@ prolog0(PROLOG_STATE *state, } static int PTRCALL -prolog1(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ +prolog1(PROLOG_STATE *state, int tok, const char *ptr, const char *end, + const ENCODING *enc) { switch (tok) { case XML_TOK_PROLOG_S: return XML_ROLE_NONE; @@ -176,12 +179,17 @@ prolog1(PROLOG_STATE *state, case XML_TOK_COMMENT: return XML_ROLE_COMMENT; case XML_TOK_BOM: - return XML_ROLE_NONE; + /* This case can never arise. To reach this role function, the + * parse must have passed through prolog0 and therefore have had + * some form of input, even if only a space. At that point, a + * byte order mark is no longer a valid character (though + * technically it should be interpreted as a non-breaking space), + * so will be rejected by the tokenizing stages. + */ + return XML_ROLE_NONE; /* LCOV_EXCL_LINE */ case XML_TOK_DECL_OPEN: - if (!XmlNameMatchesAscii(enc, - ptr + 2 * MIN_BYTES_PER_CHAR(enc), - end, - KW_DOCTYPE)) + if (! XmlNameMatchesAscii(enc, ptr + 2 * MIN_BYTES_PER_CHAR(enc), end, + KW_DOCTYPE)) break; state->handler = doctype0; return XML_ROLE_DOCTYPE_NONE; @@ -193,12 +201,11 @@ prolog1(PROLOG_STATE *state, } static int PTRCALL -prolog2(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ +prolog2(PROLOG_STATE *state, int tok, const char *ptr, const char *end, + const ENCODING *enc) { + UNUSED_P(ptr); + UNUSED_P(end); + UNUSED_P(enc); switch (tok) { case XML_TOK_PROLOG_S: return XML_ROLE_NONE; @@ -214,12 +221,11 @@ prolog2(PROLOG_STATE *state, } static int PTRCALL -doctype0(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ +doctype0(PROLOG_STATE *state, int tok, const char *ptr, const char *end, + const ENCODING *enc) { + UNUSED_P(ptr); + UNUSED_P(end); + UNUSED_P(enc); switch (tok) { case XML_TOK_PROLOG_S: return XML_ROLE_DOCTYPE_NONE; @@ -232,12 +238,8 @@ doctype0(PROLOG_STATE *state, } static int PTRCALL -doctype1(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ +doctype1(PROLOG_STATE *state, int tok, const char *ptr, const char *end, + const ENCODING *enc) { switch (tok) { case XML_TOK_PROLOG_S: return XML_ROLE_DOCTYPE_NONE; @@ -262,12 +264,11 @@ doctype1(PROLOG_STATE *state, } static int PTRCALL -doctype2(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ +doctype2(PROLOG_STATE *state, int tok, const char *ptr, const char *end, + const ENCODING *enc) { + UNUSED_P(ptr); + UNUSED_P(end); + UNUSED_P(enc); switch (tok) { case XML_TOK_PROLOG_S: return XML_ROLE_DOCTYPE_NONE; @@ -279,12 +280,11 @@ doctype2(PROLOG_STATE *state, } static int PTRCALL -doctype3(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ +doctype3(PROLOG_STATE *state, int tok, const char *ptr, const char *end, + const ENCODING *enc) { + UNUSED_P(ptr); + UNUSED_P(end); + UNUSED_P(enc); switch (tok) { case XML_TOK_PROLOG_S: return XML_ROLE_DOCTYPE_NONE; @@ -296,12 +296,11 @@ doctype3(PROLOG_STATE *state, } static int PTRCALL -doctype4(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ +doctype4(PROLOG_STATE *state, int tok, const char *ptr, const char *end, + const ENCODING *enc) { + UNUSED_P(ptr); + UNUSED_P(end); + UNUSED_P(enc); switch (tok) { case XML_TOK_PROLOG_S: return XML_ROLE_DOCTYPE_NONE; @@ -316,12 +315,11 @@ doctype4(PROLOG_STATE *state, } static int PTRCALL -doctype5(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ +doctype5(PROLOG_STATE *state, int tok, const char *ptr, const char *end, + const ENCODING *enc) { + UNUSED_P(ptr); + UNUSED_P(end); + UNUSED_P(enc); switch (tok) { case XML_TOK_PROLOG_S: return XML_ROLE_DOCTYPE_NONE; @@ -333,40 +331,28 @@ doctype5(PROLOG_STATE *state, } static int PTRCALL -internalSubset(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ +internalSubset(PROLOG_STATE *state, int tok, const char *ptr, const char *end, + const ENCODING *enc) { switch (tok) { case XML_TOK_PROLOG_S: return XML_ROLE_NONE; case XML_TOK_DECL_OPEN: - if (XmlNameMatchesAscii(enc, - ptr + 2 * MIN_BYTES_PER_CHAR(enc), - end, + if (XmlNameMatchesAscii(enc, ptr + 2 * MIN_BYTES_PER_CHAR(enc), end, KW_ENTITY)) { state->handler = entity0; return XML_ROLE_ENTITY_NONE; } - if (XmlNameMatchesAscii(enc, - ptr + 2 * MIN_BYTES_PER_CHAR(enc), - end, + if (XmlNameMatchesAscii(enc, ptr + 2 * MIN_BYTES_PER_CHAR(enc), end, KW_ATTLIST)) { state->handler = attlist0; return XML_ROLE_ATTLIST_NONE; } - if (XmlNameMatchesAscii(enc, - ptr + 2 * MIN_BYTES_PER_CHAR(enc), - end, + if (XmlNameMatchesAscii(enc, ptr + 2 * MIN_BYTES_PER_CHAR(enc), end, KW_ELEMENT)) { state->handler = element0; return XML_ROLE_ELEMENT_NONE; } - if (XmlNameMatchesAscii(enc, - ptr + 2 * MIN_BYTES_PER_CHAR(enc), - end, + if (XmlNameMatchesAscii(enc, ptr + 2 * MIN_BYTES_PER_CHAR(enc), end, KW_NOTATION)) { state->handler = notation0; return XML_ROLE_NOTATION_NONE; @@ -390,12 +376,8 @@ internalSubset(PROLOG_STATE *state, #ifdef XML_DTD static int PTRCALL -externalSubset0(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ +externalSubset0(PROLOG_STATE *state, int tok, const char *ptr, const char *end, + const ENCODING *enc) { state->handler = externalSubset1; if (tok == XML_TOK_XML_DECL) return XML_ROLE_TEXT_DECL; @@ -403,12 +385,8 @@ externalSubset0(PROLOG_STATE *state, } static int PTRCALL -externalSubset1(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ +externalSubset1(PROLOG_STATE *state, int tok, const char *ptr, const char *end, + const ENCODING *enc) { switch (tok) { case XML_TOK_COND_SECT_OPEN: state->handler = condSect0; @@ -435,12 +413,11 @@ externalSubset1(PROLOG_STATE *state, #endif /* XML_DTD */ static int PTRCALL -entity0(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ +entity0(PROLOG_STATE *state, int tok, const char *ptr, const char *end, + const ENCODING *enc) { + UNUSED_P(ptr); + UNUSED_P(end); + UNUSED_P(enc); switch (tok) { case XML_TOK_PROLOG_S: return XML_ROLE_ENTITY_NONE; @@ -455,12 +432,11 @@ entity0(PROLOG_STATE *state, } static int PTRCALL -entity1(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ +entity1(PROLOG_STATE *state, int tok, const char *ptr, const char *end, + const ENCODING *enc) { + UNUSED_P(ptr); + UNUSED_P(end); + UNUSED_P(enc); switch (tok) { case XML_TOK_PROLOG_S: return XML_ROLE_ENTITY_NONE; @@ -472,12 +448,8 @@ entity1(PROLOG_STATE *state, } static int PTRCALL -entity2(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ +entity2(PROLOG_STATE *state, int tok, const char *ptr, const char *end, + const ENCODING *enc) { switch (tok) { case XML_TOK_PROLOG_S: return XML_ROLE_ENTITY_NONE; @@ -500,12 +472,11 @@ entity2(PROLOG_STATE *state, } static int PTRCALL -entity3(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ +entity3(PROLOG_STATE *state, int tok, const char *ptr, const char *end, + const ENCODING *enc) { + UNUSED_P(ptr); + UNUSED_P(end); + UNUSED_P(enc); switch (tok) { case XML_TOK_PROLOG_S: return XML_ROLE_ENTITY_NONE; @@ -517,12 +488,11 @@ entity3(PROLOG_STATE *state, } static int PTRCALL -entity4(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ +entity4(PROLOG_STATE *state, int tok, const char *ptr, const char *end, + const ENCODING *enc) { + UNUSED_P(ptr); + UNUSED_P(end); + UNUSED_P(enc); switch (tok) { case XML_TOK_PROLOG_S: return XML_ROLE_ENTITY_NONE; @@ -534,12 +504,8 @@ entity4(PROLOG_STATE *state, } static int PTRCALL -entity5(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ +entity5(PROLOG_STATE *state, int tok, const char *ptr, const char *end, + const ENCODING *enc) { switch (tok) { case XML_TOK_PROLOG_S: return XML_ROLE_ENTITY_NONE; @@ -557,12 +523,11 @@ entity5(PROLOG_STATE *state, } static int PTRCALL -entity6(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ +entity6(PROLOG_STATE *state, int tok, const char *ptr, const char *end, + const ENCODING *enc) { + UNUSED_P(ptr); + UNUSED_P(end); + UNUSED_P(enc); switch (tok) { case XML_TOK_PROLOG_S: return XML_ROLE_ENTITY_NONE; @@ -575,12 +540,8 @@ entity6(PROLOG_STATE *state, } static int PTRCALL -entity7(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ +entity7(PROLOG_STATE *state, int tok, const char *ptr, const char *end, + const ENCODING *enc) { switch (tok) { case XML_TOK_PROLOG_S: return XML_ROLE_ENTITY_NONE; @@ -603,12 +564,11 @@ entity7(PROLOG_STATE *state, } static int PTRCALL -entity8(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ +entity8(PROLOG_STATE *state, int tok, const char *ptr, const char *end, + const ENCODING *enc) { + UNUSED_P(ptr); + UNUSED_P(end); + UNUSED_P(enc); switch (tok) { case XML_TOK_PROLOG_S: return XML_ROLE_ENTITY_NONE; @@ -620,12 +580,11 @@ entity8(PROLOG_STATE *state, } static int PTRCALL -entity9(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ +entity9(PROLOG_STATE *state, int tok, const char *ptr, const char *end, + const ENCODING *enc) { + UNUSED_P(ptr); + UNUSED_P(end); + UNUSED_P(enc); switch (tok) { case XML_TOK_PROLOG_S: return XML_ROLE_ENTITY_NONE; @@ -637,12 +596,11 @@ entity9(PROLOG_STATE *state, } static int PTRCALL -entity10(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ +entity10(PROLOG_STATE *state, int tok, const char *ptr, const char *end, + const ENCODING *enc) { + UNUSED_P(ptr); + UNUSED_P(end); + UNUSED_P(enc); switch (tok) { case XML_TOK_PROLOG_S: return XML_ROLE_ENTITY_NONE; @@ -654,12 +612,11 @@ entity10(PROLOG_STATE *state, } static int PTRCALL -notation0(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ +notation0(PROLOG_STATE *state, int tok, const char *ptr, const char *end, + const ENCODING *enc) { + UNUSED_P(ptr); + UNUSED_P(end); + UNUSED_P(enc); switch (tok) { case XML_TOK_PROLOG_S: return XML_ROLE_NOTATION_NONE; @@ -671,12 +628,8 @@ notation0(PROLOG_STATE *state, } static int PTRCALL -notation1(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ +notation1(PROLOG_STATE *state, int tok, const char *ptr, const char *end, + const ENCODING *enc) { switch (tok) { case XML_TOK_PROLOG_S: return XML_ROLE_NOTATION_NONE; @@ -695,12 +648,11 @@ notation1(PROLOG_STATE *state, } static int PTRCALL -notation2(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ +notation2(PROLOG_STATE *state, int tok, const char *ptr, const char *end, + const ENCODING *enc) { + UNUSED_P(ptr); + UNUSED_P(end); + UNUSED_P(enc); switch (tok) { case XML_TOK_PROLOG_S: return XML_ROLE_NOTATION_NONE; @@ -712,12 +664,11 @@ notation2(PROLOG_STATE *state, } static int PTRCALL -notation3(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ +notation3(PROLOG_STATE *state, int tok, const char *ptr, const char *end, + const ENCODING *enc) { + UNUSED_P(ptr); + UNUSED_P(end); + UNUSED_P(enc); switch (tok) { case XML_TOK_PROLOG_S: return XML_ROLE_NOTATION_NONE; @@ -730,12 +681,11 @@ notation3(PROLOG_STATE *state, } static int PTRCALL -notation4(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ +notation4(PROLOG_STATE *state, int tok, const char *ptr, const char *end, + const ENCODING *enc) { + UNUSED_P(ptr); + UNUSED_P(end); + UNUSED_P(enc); switch (tok) { case XML_TOK_PROLOG_S: return XML_ROLE_NOTATION_NONE; @@ -751,12 +701,11 @@ notation4(PROLOG_STATE *state, } static int PTRCALL -attlist0(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ +attlist0(PROLOG_STATE *state, int tok, const char *ptr, const char *end, + const ENCODING *enc) { + UNUSED_P(ptr); + UNUSED_P(end); + UNUSED_P(enc); switch (tok) { case XML_TOK_PROLOG_S: return XML_ROLE_ATTLIST_NONE; @@ -769,12 +718,11 @@ attlist0(PROLOG_STATE *state, } static int PTRCALL -attlist1(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ +attlist1(PROLOG_STATE *state, int tok, const char *ptr, const char *end, + const ENCODING *enc) { + UNUSED_P(ptr); + UNUSED_P(end); + UNUSED_P(enc); switch (tok) { case XML_TOK_PROLOG_S: return XML_ROLE_ATTLIST_NONE; @@ -790,34 +738,23 @@ attlist1(PROLOG_STATE *state, } static int PTRCALL -attlist2(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ +attlist2(PROLOG_STATE *state, int tok, const char *ptr, const char *end, + const ENCODING *enc) { switch (tok) { case XML_TOK_PROLOG_S: return XML_ROLE_ATTLIST_NONE; - case XML_TOK_NAME: - { - static const char * const types[] = { - KW_CDATA, - KW_ID, - KW_IDREF, - KW_IDREFS, - KW_ENTITY, - KW_ENTITIES, - KW_NMTOKEN, - KW_NMTOKENS, - }; - int i; - for (i = 0; i < (int)(sizeof(types)/sizeof(types[0])); i++) - if (XmlNameMatchesAscii(enc, ptr, end, types[i])) { - state->handler = attlist8; - return XML_ROLE_ATTRIBUTE_TYPE_CDATA + i; - } - } + case XML_TOK_NAME: { + static const char *const types[] = { + KW_CDATA, KW_ID, KW_IDREF, KW_IDREFS, + KW_ENTITY, KW_ENTITIES, KW_NMTOKEN, KW_NMTOKENS, + }; + int i; + for (i = 0; i < (int)(sizeof(types) / sizeof(types[0])); i++) + if (XmlNameMatchesAscii(enc, ptr, end, types[i])) { + state->handler = attlist8; + return XML_ROLE_ATTRIBUTE_TYPE_CDATA + i; + } + } if (XmlNameMatchesAscii(enc, ptr, end, KW_NOTATION)) { state->handler = attlist5; return XML_ROLE_ATTLIST_NONE; @@ -831,12 +768,11 @@ attlist2(PROLOG_STATE *state, } static int PTRCALL -attlist3(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ +attlist3(PROLOG_STATE *state, int tok, const char *ptr, const char *end, + const ENCODING *enc) { + UNUSED_P(ptr); + UNUSED_P(end); + UNUSED_P(enc); switch (tok) { case XML_TOK_PROLOG_S: return XML_ROLE_ATTLIST_NONE; @@ -850,12 +786,11 @@ attlist3(PROLOG_STATE *state, } static int PTRCALL -attlist4(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ +attlist4(PROLOG_STATE *state, int tok, const char *ptr, const char *end, + const ENCODING *enc) { + UNUSED_P(ptr); + UNUSED_P(end); + UNUSED_P(enc); switch (tok) { case XML_TOK_PROLOG_S: return XML_ROLE_ATTLIST_NONE; @@ -870,12 +805,11 @@ attlist4(PROLOG_STATE *state, } static int PTRCALL -attlist5(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ +attlist5(PROLOG_STATE *state, int tok, const char *ptr, const char *end, + const ENCODING *enc) { + UNUSED_P(ptr); + UNUSED_P(end); + UNUSED_P(enc); switch (tok) { case XML_TOK_PROLOG_S: return XML_ROLE_ATTLIST_NONE; @@ -887,12 +821,11 @@ attlist5(PROLOG_STATE *state, } static int PTRCALL -attlist6(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ +attlist6(PROLOG_STATE *state, int tok, const char *ptr, const char *end, + const ENCODING *enc) { + UNUSED_P(ptr); + UNUSED_P(end); + UNUSED_P(enc); switch (tok) { case XML_TOK_PROLOG_S: return XML_ROLE_ATTLIST_NONE; @@ -904,12 +837,11 @@ attlist6(PROLOG_STATE *state, } static int PTRCALL -attlist7(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ +attlist7(PROLOG_STATE *state, int tok, const char *ptr, const char *end, + const ENCODING *enc) { + UNUSED_P(ptr); + UNUSED_P(end); + UNUSED_P(enc); switch (tok) { case XML_TOK_PROLOG_S: return XML_ROLE_ATTLIST_NONE; @@ -925,33 +857,23 @@ attlist7(PROLOG_STATE *state, /* default value */ static int PTRCALL -attlist8(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ +attlist8(PROLOG_STATE *state, int tok, const char *ptr, const char *end, + const ENCODING *enc) { switch (tok) { case XML_TOK_PROLOG_S: return XML_ROLE_ATTLIST_NONE; case XML_TOK_POUND_NAME: - if (XmlNameMatchesAscii(enc, - ptr + MIN_BYTES_PER_CHAR(enc), - end, + if (XmlNameMatchesAscii(enc, ptr + MIN_BYTES_PER_CHAR(enc), end, KW_IMPLIED)) { state->handler = attlist1; return XML_ROLE_IMPLIED_ATTRIBUTE_VALUE; } - if (XmlNameMatchesAscii(enc, - ptr + MIN_BYTES_PER_CHAR(enc), - end, + if (XmlNameMatchesAscii(enc, ptr + MIN_BYTES_PER_CHAR(enc), end, KW_REQUIRED)) { state->handler = attlist1; return XML_ROLE_REQUIRED_ATTRIBUTE_VALUE; } - if (XmlNameMatchesAscii(enc, - ptr + MIN_BYTES_PER_CHAR(enc), - end, + if (XmlNameMatchesAscii(enc, ptr + MIN_BYTES_PER_CHAR(enc), end, KW_FIXED)) { state->handler = attlist9; return XML_ROLE_ATTLIST_NONE; @@ -965,12 +887,11 @@ attlist8(PROLOG_STATE *state, } static int PTRCALL -attlist9(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ +attlist9(PROLOG_STATE *state, int tok, const char *ptr, const char *end, + const ENCODING *enc) { + UNUSED_P(ptr); + UNUSED_P(end); + UNUSED_P(enc); switch (tok) { case XML_TOK_PROLOG_S: return XML_ROLE_ATTLIST_NONE; @@ -982,12 +903,11 @@ attlist9(PROLOG_STATE *state, } static int PTRCALL -element0(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ +element0(PROLOG_STATE *state, int tok, const char *ptr, const char *end, + const ENCODING *enc) { + UNUSED_P(ptr); + UNUSED_P(end); + UNUSED_P(enc); switch (tok) { case XML_TOK_PROLOG_S: return XML_ROLE_ELEMENT_NONE; @@ -1000,12 +920,8 @@ element0(PROLOG_STATE *state, } static int PTRCALL -element1(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ +element1(PROLOG_STATE *state, int tok, const char *ptr, const char *end, + const ENCODING *enc) { switch (tok) { case XML_TOK_PROLOG_S: return XML_ROLE_ELEMENT_NONE; @@ -1030,19 +946,13 @@ element1(PROLOG_STATE *state, } static int PTRCALL -element2(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ +element2(PROLOG_STATE *state, int tok, const char *ptr, const char *end, + const ENCODING *enc) { switch (tok) { case XML_TOK_PROLOG_S: return XML_ROLE_ELEMENT_NONE; case XML_TOK_POUND_NAME: - if (XmlNameMatchesAscii(enc, - ptr + MIN_BYTES_PER_CHAR(enc), - end, + if (XmlNameMatchesAscii(enc, ptr + MIN_BYTES_PER_CHAR(enc), end, KW_PCDATA)) { state->handler = element3; return XML_ROLE_CONTENT_PCDATA; @@ -1070,12 +980,11 @@ element2(PROLOG_STATE *state, } static int PTRCALL -element3(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ +element3(PROLOG_STATE *state, int tok, const char *ptr, const char *end, + const ENCODING *enc) { + UNUSED_P(ptr); + UNUSED_P(end); + UNUSED_P(enc); switch (tok) { case XML_TOK_PROLOG_S: return XML_ROLE_ELEMENT_NONE; @@ -1095,12 +1004,11 @@ element3(PROLOG_STATE *state, } static int PTRCALL -element4(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ +element4(PROLOG_STATE *state, int tok, const char *ptr, const char *end, + const ENCODING *enc) { + UNUSED_P(ptr); + UNUSED_P(end); + UNUSED_P(enc); switch (tok) { case XML_TOK_PROLOG_S: return XML_ROLE_ELEMENT_NONE; @@ -1113,12 +1021,11 @@ element4(PROLOG_STATE *state, } static int PTRCALL -element5(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ +element5(PROLOG_STATE *state, int tok, const char *ptr, const char *end, + const ENCODING *enc) { + UNUSED_P(ptr); + UNUSED_P(end); + UNUSED_P(enc); switch (tok) { case XML_TOK_PROLOG_S: return XML_ROLE_ELEMENT_NONE; @@ -1134,12 +1041,11 @@ element5(PROLOG_STATE *state, } static int PTRCALL -element6(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ +element6(PROLOG_STATE *state, int tok, const char *ptr, const char *end, + const ENCODING *enc) { + UNUSED_P(ptr); + UNUSED_P(end); + UNUSED_P(enc); switch (tok) { case XML_TOK_PROLOG_S: return XML_ROLE_ELEMENT_NONE; @@ -1164,12 +1070,11 @@ element6(PROLOG_STATE *state, } static int PTRCALL -element7(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ +element7(PROLOG_STATE *state, int tok, const char *ptr, const char *end, + const ENCODING *enc) { + UNUSED_P(ptr); + UNUSED_P(end); + UNUSED_P(enc); switch (tok) { case XML_TOK_PROLOG_S: return XML_ROLE_ELEMENT_NONE; @@ -1214,12 +1119,8 @@ element7(PROLOG_STATE *state, #ifdef XML_DTD static int PTRCALL -condSect0(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ +condSect0(PROLOG_STATE *state, int tok, const char *ptr, const char *end, + const ENCODING *enc) { switch (tok) { case XML_TOK_PROLOG_S: return XML_ROLE_NONE; @@ -1238,12 +1139,11 @@ condSect0(PROLOG_STATE *state, } static int PTRCALL -condSect1(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ +condSect1(PROLOG_STATE *state, int tok, const char *ptr, const char *end, + const ENCODING *enc) { + UNUSED_P(ptr); + UNUSED_P(end); + UNUSED_P(enc); switch (tok) { case XML_TOK_PROLOG_S: return XML_ROLE_NONE; @@ -1256,12 +1156,11 @@ condSect1(PROLOG_STATE *state, } static int PTRCALL -condSect2(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ +condSect2(PROLOG_STATE *state, int tok, const char *ptr, const char *end, + const ENCODING *enc) { + UNUSED_P(ptr); + UNUSED_P(end); + UNUSED_P(enc); switch (tok) { case XML_TOK_PROLOG_S: return XML_ROLE_NONE; @@ -1275,12 +1174,11 @@ condSect2(PROLOG_STATE *state, #endif /* XML_DTD */ static int PTRCALL -declClose(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ +declClose(PROLOG_STATE *state, int tok, const char *ptr, const char *end, + const ENCODING *enc) { + UNUSED_P(ptr); + UNUSED_P(end); + UNUSED_P(enc); switch (tok) { case XML_TOK_PROLOG_S: return state->role_none; @@ -1291,30 +1189,52 @@ declClose(PROLOG_STATE *state, return common(state, tok); } +/* This function will only be invoked if the internal logic of the + * parser has broken down. It is used in two cases: + * + * 1: When the XML prolog has been finished. At this point the + * processor (the parser level above these role handlers) should + * switch from prologProcessor to contentProcessor and reinitialise + * the handler function. + * + * 2: When an error has been detected (via common() below). At this + * point again the processor should be switched to errorProcessor, + * which will never call a handler. + * + * The result of this is that error() can only be called if the + * processor switch failed to happen, which is an internal error and + * therefore we shouldn't be able to provoke it simply by using the + * library. It is a necessary backstop, however, so we merely exclude + * it from the coverage statistics. + * + * LCOV_EXCL_START + */ static int PTRCALL -error(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ +error(PROLOG_STATE *state, int tok, const char *ptr, const char *end, + const ENCODING *enc) { + UNUSED_P(state); + UNUSED_P(tok); + UNUSED_P(ptr); + UNUSED_P(end); + UNUSED_P(enc); return XML_ROLE_NONE; } +/* LCOV_EXCL_STOP */ static int FASTCALL -common(PROLOG_STATE *state, int tok) -{ +common(PROLOG_STATE *state, int tok) { #ifdef XML_DTD - if (!state->documentEntity && tok == XML_TOK_PARAM_ENTITY_REF) + if (! state->documentEntity && tok == XML_TOK_PARAM_ENTITY_REF) return XML_ROLE_INNER_PARAM_ENTITY_REF; +#else + UNUSED_P(tok); #endif state->handler = error; return XML_ROLE_ERROR; } void -XmlPrologStateInit(PROLOG_STATE *state) -{ +XmlPrologStateInit(PROLOG_STATE *state) { state->handler = prolog0; #ifdef XML_DTD state->documentEntity = 1; @@ -1326,8 +1246,7 @@ XmlPrologStateInit(PROLOG_STATE *state) #ifdef XML_DTD void -XmlPrologStateInitExternalEntity(PROLOG_STATE *state) -{ +XmlPrologStateInitExternalEntity(PROLOG_STATE *state) { state->handler = externalSubset0; state->documentEntity = 0; state->includeLevel = 0; diff --git a/cextern/expat/lib/xmlrole.h b/cextern/expat/lib/xmlrole.h old mode 100755 new mode 100644 index 4dd9f06f9767..9d0d4ff11b7f --- a/cextern/expat/lib/xmlrole.h +++ b/cextern/expat/lib/xmlrole.h @@ -1,21 +1,46 @@ -/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd - See the file COPYING for copying permission. +/* + __ __ _ + ___\ \/ /_ __ __ _| |_ + / _ \\ /| '_ \ / _` | __| + | __// \| |_) | (_| | |_ + \___/_/\_\ .__/ \__,_|\__| + |_| XML parser + + Copyright (c) 1997-2000 Thai Open Source Software Center Ltd + Copyright (c) 2000 Clark Cooper + Copyright (c) 2002 Karl Waclawek + Copyright (c) 2002 Fred L. Drake, Jr. + Copyright (c) 2017-2025 Sebastian Pipping + Licensed under the MIT license: + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to permit + persons to whom the Software is furnished to do so, subject to the + following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef XmlRole_INCLUDED -#define XmlRole_INCLUDED 1 - -#ifdef __VMS -/* 0 1 2 3 0 1 2 3 - 1234567890123456789012345678901 1234567890123456789012345678901 */ -#define XmlPrologStateInitExternalEntity XmlPrologStateInitExternalEnt -#endif +# define XmlRole_INCLUDED 1 -#include "xmltok.h" +# include "xmltok.h" -#ifdef __cplusplus +# ifdef __cplusplus extern "C" { -#endif +# endif enum { XML_ROLE_ERROR = -1, @@ -76,39 +101,36 @@ enum { XML_ROLE_CONTENT_ELEMENT_PLUS, XML_ROLE_PI, XML_ROLE_COMMENT, -#ifdef XML_DTD +# ifdef XML_DTD XML_ROLE_TEXT_DECL, XML_ROLE_IGNORE_SECT, XML_ROLE_INNER_PARAM_ENTITY_REF, -#endif /* XML_DTD */ +# endif /* XML_DTD */ XML_ROLE_PARAM_ENTITY_REF }; typedef struct prolog_state { - int (PTRCALL *handler) (struct prolog_state *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc); + int(PTRCALL *handler)(struct prolog_state *state, int tok, const char *ptr, + const char *end, const ENCODING *enc); unsigned level; int role_none; -#ifdef XML_DTD +# ifdef XML_DTD unsigned includeLevel; int documentEntity; int inEntityValue; -#endif /* XML_DTD */ +# endif /* XML_DTD */ } PROLOG_STATE; -void XmlPrologStateInit(PROLOG_STATE *); -#ifdef XML_DTD -void XmlPrologStateInitExternalEntity(PROLOG_STATE *); -#endif /* XML_DTD */ +void XmlPrologStateInit(PROLOG_STATE *state); +# ifdef XML_DTD +void XmlPrologStateInitExternalEntity(PROLOG_STATE *state); +# endif /* XML_DTD */ -#define XmlTokenRole(state, tok, ptr, end, enc) \ - (((state)->handler)(state, tok, ptr, end, enc)) +# define XmlTokenRole(state, tok, ptr, end, enc) \ + (((state)->handler)(state, tok, ptr, end, enc)) -#ifdef __cplusplus +# ifdef __cplusplus } -#endif +# endif #endif /* not XmlRole_INCLUDED */ diff --git a/cextern/expat/lib/xmltok.c b/cextern/expat/lib/xmltok.c old mode 100755 new mode 100644 index 068afdec6e1e..95d5e84b67f1 --- a/cextern/expat/lib/xmltok.c +++ b/cextern/expat/lib/xmltok.c @@ -1,22 +1,60 @@ -/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd - See the file COPYING for copying permission. +/* + __ __ _ + ___\ \/ /_ __ __ _| |_ + / _ \\ /| '_ \ / _` | __| + | __// \| |_) | (_| | |_ + \___/_/\_\ .__/ \__,_|\__| + |_| XML parser + + Copyright (c) 1997-2000 Thai Open Source Software Center Ltd + Copyright (c) 2000 Clark Cooper + Copyright (c) 2001-2003 Fred L. Drake, Jr. + Copyright (c) 2002 Greg Stein + Copyright (c) 2002-2016 Karl Waclawek + Copyright (c) 2005-2009 Steven Solie + Copyright (c) 2016-2024 Sebastian Pipping + Copyright (c) 2016 Pascal Cuoq + Copyright (c) 2016 Don Lewis + Copyright (c) 2017 Rhodri James + Copyright (c) 2017 Alexander Bluhm + Copyright (c) 2017 Benbuck Nason + Copyright (c) 2017 JosÊ GutiÊrrez de la Concha + Copyright (c) 2019 David Loffredo + Copyright (c) 2021 Donghee Na + Copyright (c) 2022 Martin Ettl + Copyright (c) 2022 Sean McBride + Copyright (c) 2023 Hanno BÃļck + Licensed under the MIT license: + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to permit + persons to whom the Software is furnished to do so, subject to the + following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#include "expat_config.h" + #include +#include /* memcpy */ +#include -#ifdef COMPILED_FROM_DSP -#include "winconfig.h" -#elif defined(MACOS_CLASSIC) -#include "macconfig.h" -#elif defined(__amigaos4__) -#include "amigaconfig.h" -#elif defined(__WATCOMC__) -#include "watcomconfig.h" -#else -#ifdef HAVE_EXPAT_CONFIG_H -#include +#ifdef _WIN32 +# include "winconfig.h" #endif -#endif /* ndef COMPILED_FROM_DSP */ #include "expat_external.h" #include "internal.h" @@ -24,62 +62,47 @@ #include "nametab.h" #ifdef XML_DTD -#define IGNORE_SECTION_TOK_VTABLE , PREFIX(ignoreSectionTok) +# define IGNORE_SECTION_TOK_VTABLE , PREFIX(ignoreSectionTok) #else -#define IGNORE_SECTION_TOK_VTABLE /* as nothing */ +# define IGNORE_SECTION_TOK_VTABLE /* as nothing */ #endif -#define VTABLE1 \ - { PREFIX(prologTok), PREFIX(contentTok), \ - PREFIX(cdataSectionTok) IGNORE_SECTION_TOK_VTABLE }, \ - { PREFIX(attributeValueTok), PREFIX(entityValueTok) }, \ - PREFIX(sameName), \ - PREFIX(nameMatchesAscii), \ - PREFIX(nameLength), \ - PREFIX(skipS), \ - PREFIX(getAtts), \ - PREFIX(charRefNumber), \ - PREFIX(predefinedEntityName), \ - PREFIX(updatePosition), \ - PREFIX(isPublicId) +#define VTABLE1 \ + {PREFIX(prologTok), PREFIX(contentTok), \ + PREFIX(cdataSectionTok) IGNORE_SECTION_TOK_VTABLE}, \ + {PREFIX(attributeValueTok), PREFIX(entityValueTok)}, \ + PREFIX(nameMatchesAscii), PREFIX(nameLength), PREFIX(skipS), \ + PREFIX(getAtts), PREFIX(charRefNumber), PREFIX(predefinedEntityName), \ + PREFIX(updatePosition), PREFIX(isPublicId) #define VTABLE VTABLE1, PREFIX(toUtf8), PREFIX(toUtf16) -#define UCS2_GET_NAMING(pages, hi, lo) \ - (namingBitmap[(pages[hi] << 3) + ((lo) >> 5)] & (1 << ((lo) & 0x1F))) +#define UCS2_GET_NAMING(pages, hi, lo) \ + (namingBitmap[(pages[hi] << 3) + ((lo) >> 5)] & (1u << ((lo) & 0x1F))) /* A 2 byte UTF-8 representation splits the characters 11 bits between the bottom 5 and 6 bits of the bytes. We need 8 bits to index into pages, 3 bits to add to that index and 5 bits to generate the mask. */ -#define UTF8_GET_NAMING2(pages, byte) \ - (namingBitmap[((pages)[(((byte)[0]) >> 2) & 7] << 3) \ - + ((((byte)[0]) & 3) << 1) \ - + ((((byte)[1]) >> 5) & 1)] \ - & (1 << (((byte)[1]) & 0x1F))) +#define UTF8_GET_NAMING2(pages, byte) \ + (namingBitmap[((pages)[(((byte)[0]) >> 2) & 7] << 3) \ + + ((((byte)[0]) & 3) << 1) + ((((byte)[1]) >> 5) & 1)] \ + & (1u << (((byte)[1]) & 0x1F))) /* A 3 byte UTF-8 representation splits the characters 16 bits between the bottom 4, 6 and 6 bits of the bytes. We need 8 bits to index into pages, 3 bits to add to that index and 5 bits to generate the mask. */ -#define UTF8_GET_NAMING3(pages, byte) \ - (namingBitmap[((pages)[((((byte)[0]) & 0xF) << 4) \ - + ((((byte)[1]) >> 2) & 0xF)] \ - << 3) \ - + ((((byte)[1]) & 3) << 1) \ - + ((((byte)[2]) >> 5) & 1)] \ - & (1 << (((byte)[2]) & 0x1F))) - -#define UTF8_GET_NAMING(pages, p, n) \ - ((n) == 2 \ - ? UTF8_GET_NAMING2(pages, (const unsigned char *)(p)) \ - : ((n) == 3 \ - ? UTF8_GET_NAMING3(pages, (const unsigned char *)(p)) \ - : 0)) +#define UTF8_GET_NAMING3(pages, byte) \ + (namingBitmap \ + [((pages)[((((byte)[0]) & 0xF) << 4) + ((((byte)[1]) >> 2) & 0xF)] \ + << 3) \ + + ((((byte)[1]) & 3) << 1) + ((((byte)[2]) >> 5) & 1)] \ + & (1u << (((byte)[2]) & 0x1F))) /* Detection of invalid UTF-8 sequences is based on Table 3.1B - of Unicode 3.2: http://www.unicode.org/unicode/reports/tr28/ + of Unicode 3.2: https://www.unicode.org/unicode/reports/tr28/ with the additional restriction of not allowing the Unicode code points 0xFFFF and 0xFFFE (sequences EF,BF,BF and EF,BF,BE). Implementation details: @@ -88,88 +111,76 @@ (A & 0xC0) == 0xC0 means A > 0xBF */ -#define UTF8_INVALID2(p) \ +#define UTF8_INVALID2(p) \ ((*p) < 0xC2 || ((p)[1] & 0x80) == 0 || ((p)[1] & 0xC0) == 0xC0) -#define UTF8_INVALID3(p) \ - (((p)[2] & 0x80) == 0 \ - || \ - ((*p) == 0xEF && (p)[1] == 0xBF \ - ? \ - (p)[2] > 0xBD \ - : \ - ((p)[2] & 0xC0) == 0xC0) \ - || \ - ((*p) == 0xE0 \ - ? \ - (p)[1] < 0xA0 || ((p)[1] & 0xC0) == 0xC0 \ - : \ - ((p)[1] & 0x80) == 0 \ - || \ - ((*p) == 0xED ? (p)[1] > 0x9F : ((p)[1] & 0xC0) == 0xC0))) - -#define UTF8_INVALID4(p) \ - (((p)[3] & 0x80) == 0 || ((p)[3] & 0xC0) == 0xC0 \ - || \ - ((p)[2] & 0x80) == 0 || ((p)[2] & 0xC0) == 0xC0 \ - || \ - ((*p) == 0xF0 \ - ? \ - (p)[1] < 0x90 || ((p)[1] & 0xC0) == 0xC0 \ - : \ - ((p)[1] & 0x80) == 0 \ - || \ - ((*p) == 0xF4 ? (p)[1] > 0x8F : ((p)[1] & 0xC0) == 0xC0))) +#define UTF8_INVALID3(p) \ + (((p)[2] & 0x80) == 0 \ + || ((*p) == 0xEF && (p)[1] == 0xBF ? (p)[2] > 0xBD \ + : ((p)[2] & 0xC0) == 0xC0) \ + || ((*p) == 0xE0 \ + ? (p)[1] < 0xA0 || ((p)[1] & 0xC0) == 0xC0 \ + : ((p)[1] & 0x80) == 0 \ + || ((*p) == 0xED ? (p)[1] > 0x9F : ((p)[1] & 0xC0) == 0xC0))) + +#define UTF8_INVALID4(p) \ + (((p)[3] & 0x80) == 0 || ((p)[3] & 0xC0) == 0xC0 || ((p)[2] & 0x80) == 0 \ + || ((p)[2] & 0xC0) == 0xC0 \ + || ((*p) == 0xF0 \ + ? (p)[1] < 0x90 || ((p)[1] & 0xC0) == 0xC0 \ + : ((p)[1] & 0x80) == 0 \ + || ((*p) == 0xF4 ? (p)[1] > 0x8F : ((p)[1] & 0xC0) == 0xC0))) static int PTRFASTCALL -isNever(const ENCODING *enc, const char *p) -{ +isNever(const ENCODING *enc, const char *p) { + UNUSED_P(enc); + UNUSED_P(p); return 0; } static int PTRFASTCALL -utf8_isName2(const ENCODING *enc, const char *p) -{ +utf8_isName2(const ENCODING *enc, const char *p) { + UNUSED_P(enc); return UTF8_GET_NAMING2(namePages, (const unsigned char *)p); } static int PTRFASTCALL -utf8_isName3(const ENCODING *enc, const char *p) -{ +utf8_isName3(const ENCODING *enc, const char *p) { + UNUSED_P(enc); return UTF8_GET_NAMING3(namePages, (const unsigned char *)p); } #define utf8_isName4 isNever static int PTRFASTCALL -utf8_isNmstrt2(const ENCODING *enc, const char *p) -{ +utf8_isNmstrt2(const ENCODING *enc, const char *p) { + UNUSED_P(enc); return UTF8_GET_NAMING2(nmstrtPages, (const unsigned char *)p); } static int PTRFASTCALL -utf8_isNmstrt3(const ENCODING *enc, const char *p) -{ +utf8_isNmstrt3(const ENCODING *enc, const char *p) { + UNUSED_P(enc); return UTF8_GET_NAMING3(nmstrtPages, (const unsigned char *)p); } #define utf8_isNmstrt4 isNever static int PTRFASTCALL -utf8_isInvalid2(const ENCODING *enc, const char *p) -{ +utf8_isInvalid2(const ENCODING *enc, const char *p) { + UNUSED_P(enc); return UTF8_INVALID2((const unsigned char *)p); } static int PTRFASTCALL -utf8_isInvalid3(const ENCODING *enc, const char *p) -{ +utf8_isInvalid3(const ENCODING *enc, const char *p) { + UNUSED_P(enc); return UTF8_INVALID3((const unsigned char *)p); } static int PTRFASTCALL -utf8_isInvalid4(const ENCODING *enc, const char *p) -{ +utf8_isInvalid4(const ENCODING *enc, const char *p) { + UNUSED_P(enc); return UTF8_INVALID4((const unsigned char *)p); } @@ -177,126 +188,121 @@ struct normal_encoding { ENCODING enc; unsigned char type[256]; #ifdef XML_MIN_SIZE - int (PTRFASTCALL *byteType)(const ENCODING *, const char *); - int (PTRFASTCALL *isNameMin)(const ENCODING *, const char *); - int (PTRFASTCALL *isNmstrtMin)(const ENCODING *, const char *); - int (PTRFASTCALL *byteToAscii)(const ENCODING *, const char *); - int (PTRCALL *charMatches)(const ENCODING *, const char *, int); + int(PTRFASTCALL *byteType)(const ENCODING *, const char *); + int(PTRFASTCALL *isNameMin)(const ENCODING *, const char *); + int(PTRFASTCALL *isNmstrtMin)(const ENCODING *, const char *); + int(PTRFASTCALL *byteToAscii)(const ENCODING *, const char *); + int(PTRCALL *charMatches)(const ENCODING *, const char *, int); #endif /* XML_MIN_SIZE */ - int (PTRFASTCALL *isName2)(const ENCODING *, const char *); - int (PTRFASTCALL *isName3)(const ENCODING *, const char *); - int (PTRFASTCALL *isName4)(const ENCODING *, const char *); - int (PTRFASTCALL *isNmstrt2)(const ENCODING *, const char *); - int (PTRFASTCALL *isNmstrt3)(const ENCODING *, const char *); - int (PTRFASTCALL *isNmstrt4)(const ENCODING *, const char *); - int (PTRFASTCALL *isInvalid2)(const ENCODING *, const char *); - int (PTRFASTCALL *isInvalid3)(const ENCODING *, const char *); - int (PTRFASTCALL *isInvalid4)(const ENCODING *, const char *); + int(PTRFASTCALL *isName2)(const ENCODING *, const char *); + int(PTRFASTCALL *isName3)(const ENCODING *, const char *); + int(PTRFASTCALL *isName4)(const ENCODING *, const char *); + int(PTRFASTCALL *isNmstrt2)(const ENCODING *, const char *); + int(PTRFASTCALL *isNmstrt3)(const ENCODING *, const char *); + int(PTRFASTCALL *isNmstrt4)(const ENCODING *, const char *); + int(PTRFASTCALL *isInvalid2)(const ENCODING *, const char *); + int(PTRFASTCALL *isInvalid3)(const ENCODING *, const char *); + int(PTRFASTCALL *isInvalid4)(const ENCODING *, const char *); }; -#define AS_NORMAL_ENCODING(enc) ((const struct normal_encoding *) (enc)) +#define AS_NORMAL_ENCODING(enc) ((const struct normal_encoding *)(enc)) #ifdef XML_MIN_SIZE -#define STANDARD_VTABLE(E) \ - E ## byteType, \ - E ## isNameMin, \ - E ## isNmstrtMin, \ - E ## byteToAscii, \ - E ## charMatches, +# define STANDARD_VTABLE(E) \ + E##byteType, E##isNameMin, E##isNmstrtMin, E##byteToAscii, E##charMatches, #else -#define STANDARD_VTABLE(E) /* as nothing */ +# define STANDARD_VTABLE(E) /* as nothing */ #endif -#define NORMAL_VTABLE(E) \ - E ## isName2, \ - E ## isName3, \ - E ## isName4, \ - E ## isNmstrt2, \ - E ## isNmstrt3, \ - E ## isNmstrt4, \ - E ## isInvalid2, \ - E ## isInvalid3, \ - E ## isInvalid4 +#define NORMAL_VTABLE(E) \ + E##isName2, E##isName3, E##isName4, E##isNmstrt2, E##isNmstrt3, \ + E##isNmstrt4, E##isInvalid2, E##isInvalid3, E##isInvalid4 + +#define NULL_VTABLE \ + /* isName2 */ NULL, /* isName3 */ NULL, /* isName4 */ NULL, \ + /* isNmstrt2 */ NULL, /* isNmstrt3 */ NULL, /* isNmstrt4 */ NULL, \ + /* isInvalid2 */ NULL, /* isInvalid3 */ NULL, /* isInvalid4 */ NULL -static int FASTCALL checkCharRefNumber(int); +static int FASTCALL checkCharRefNumber(int result); #include "xmltok_impl.h" #include "ascii.h" #ifdef XML_MIN_SIZE -#define sb_isNameMin isNever -#define sb_isNmstrtMin isNever +# define sb_isNameMin isNever +# define sb_isNmstrtMin isNever #endif #ifdef XML_MIN_SIZE -#define MINBPC(enc) ((enc)->minBytesPerChar) +# define MINBPC(enc) ((enc)->minBytesPerChar) #else /* minimum bytes per character */ -#define MINBPC(enc) 1 +# define MINBPC(enc) 1 #endif -#define SB_BYTE_TYPE(enc, p) \ - (((struct normal_encoding *)(enc))->type[(unsigned char)*(p)]) +#define SB_BYTE_TYPE(enc, p) \ + (((const struct normal_encoding *)(enc))->type[(unsigned char)*(p)]) #ifdef XML_MIN_SIZE static int PTRFASTCALL -sb_byteType(const ENCODING *enc, const char *p) -{ +sb_byteType(const ENCODING *enc, const char *p) { return SB_BYTE_TYPE(enc, p); } -#define BYTE_TYPE(enc, p) \ - (AS_NORMAL_ENCODING(enc)->byteType(enc, p)) +# define BYTE_TYPE(enc, p) (AS_NORMAL_ENCODING(enc)->byteType(enc, p)) #else -#define BYTE_TYPE(enc, p) SB_BYTE_TYPE(enc, p) +# define BYTE_TYPE(enc, p) SB_BYTE_TYPE(enc, p) #endif #ifdef XML_MIN_SIZE -#define BYTE_TO_ASCII(enc, p) \ - (AS_NORMAL_ENCODING(enc)->byteToAscii(enc, p)) +# define BYTE_TO_ASCII(enc, p) (AS_NORMAL_ENCODING(enc)->byteToAscii(enc, p)) static int PTRFASTCALL -sb_byteToAscii(const ENCODING *enc, const char *p) -{ +sb_byteToAscii(const ENCODING *enc, const char *p) { + UNUSED_P(enc); return *p; } #else -#define BYTE_TO_ASCII(enc, p) (*(p)) +# define BYTE_TO_ASCII(enc, p) (*(p)) #endif -#define IS_NAME_CHAR(enc, p, n) \ - (AS_NORMAL_ENCODING(enc)->isName ## n(enc, p)) -#define IS_NMSTRT_CHAR(enc, p, n) \ - (AS_NORMAL_ENCODING(enc)->isNmstrt ## n(enc, p)) -#define IS_INVALID_CHAR(enc, p, n) \ - (AS_NORMAL_ENCODING(enc)->isInvalid ## n(enc, p)) +#define IS_NAME_CHAR(enc, p, n) (AS_NORMAL_ENCODING(enc)->isName##n(enc, p)) +#define IS_NMSTRT_CHAR(enc, p, n) (AS_NORMAL_ENCODING(enc)->isNmstrt##n(enc, p)) +#ifdef XML_MIN_SIZE +# define IS_INVALID_CHAR(enc, p, n) \ + (AS_NORMAL_ENCODING(enc)->isInvalid##n \ + && AS_NORMAL_ENCODING(enc)->isInvalid##n(enc, p)) +#else +# define IS_INVALID_CHAR(enc, p, n) \ + (AS_NORMAL_ENCODING(enc)->isInvalid##n(enc, p)) +#endif #ifdef XML_MIN_SIZE -#define IS_NAME_CHAR_MINBPC(enc, p) \ - (AS_NORMAL_ENCODING(enc)->isNameMin(enc, p)) -#define IS_NMSTRT_CHAR_MINBPC(enc, p) \ - (AS_NORMAL_ENCODING(enc)->isNmstrtMin(enc, p)) +# define IS_NAME_CHAR_MINBPC(enc, p) \ + (AS_NORMAL_ENCODING(enc)->isNameMin(enc, p)) +# define IS_NMSTRT_CHAR_MINBPC(enc, p) \ + (AS_NORMAL_ENCODING(enc)->isNmstrtMin(enc, p)) #else -#define IS_NAME_CHAR_MINBPC(enc, p) (0) -#define IS_NMSTRT_CHAR_MINBPC(enc, p) (0) +# define IS_NAME_CHAR_MINBPC(enc, p) (0) +# define IS_NMSTRT_CHAR_MINBPC(enc, p) (0) #endif #ifdef XML_MIN_SIZE -#define CHAR_MATCHES(enc, p, c) \ - (AS_NORMAL_ENCODING(enc)->charMatches(enc, p, c)) +# define CHAR_MATCHES(enc, p, c) \ + (AS_NORMAL_ENCODING(enc)->charMatches(enc, p, c)) static int PTRCALL -sb_charMatches(const ENCODING *enc, const char *p, int c) -{ +sb_charMatches(const ENCODING *enc, const char *p, int c) { + UNUSED_P(enc); return *p == c; } #else /* c is an ASCII character */ -#define CHAR_MATCHES(enc, p, c) (*(p) == c) +# define CHAR_MATCHES(enc, p, c) (*(p) == (c)) #endif -#define PREFIX(ident) normal_ ## ident +#define PREFIX(ident) normal_##ident #define XML_TOK_IMPL_C #include "xmltok_impl.c" #undef XML_TOK_IMPL_C @@ -311,223 +317,301 @@ sb_charMatches(const ENCODING *enc, const char *p, int c) #undef IS_NMSTRT_CHAR_MINBPC #undef IS_INVALID_CHAR -enum { /* UTF8_cvalN is value of masked first byte of N byte sequence */ - UTF8_cval1 = 0x00, - UTF8_cval2 = 0xc0, - UTF8_cval3 = 0xe0, - UTF8_cval4 = 0xf0 +enum { /* UTF8_cvalN is value of masked first byte of N byte sequence */ + UTF8_cval1 = 0x00, + UTF8_cval2 = 0xc0, + UTF8_cval3 = 0xe0, + UTF8_cval4 = 0xf0 }; -static void PTRCALL -utf8_toUtf8(const ENCODING *enc, - const char **fromP, const char *fromLim, - char **toP, const char *toLim) -{ - char *to; - const char *from; - if (fromLim - *fromP > toLim - *toP) { - /* Avoid copying partial characters. */ - for (fromLim = *fromP + (toLim - *toP); fromLim > *fromP; fromLim--) - if (((unsigned char)fromLim[-1] & 0xc0) != 0x80) +void +_INTERNAL_trim_to_complete_utf8_characters(const char *from, + const char **fromLimRef) { + const char *fromLim = *fromLimRef; + size_t walked = 0; + for (; fromLim > from; fromLim--, walked++) { + const unsigned char prev = (unsigned char)fromLim[-1]; + if ((prev & 0xf8u) + == 0xf0u) { /* 4-byte character, lead by 0b11110xxx byte */ + if (walked + 1 >= 4) { + fromLim += 4 - 1; + break; + } else { + walked = 0; + } + } else if ((prev & 0xf0u) + == 0xe0u) { /* 3-byte character, lead by 0b1110xxxx byte */ + if (walked + 1 >= 3) { + fromLim += 3 - 1; break; + } else { + walked = 0; + } + } else if ((prev & 0xe0u) + == 0xc0u) { /* 2-byte character, lead by 0b110xxxxx byte */ + if (walked + 1 >= 2) { + fromLim += 2 - 1; + break; + } else { + walked = 0; + } + } else if ((prev & 0x80u) + == 0x00u) { /* 1-byte character, matching 0b0xxxxxxx */ + break; + } } - for (to = *toP, from = *fromP; from != fromLim; from++, to++) - *to = *from; - *fromP = from; - *toP = to; + *fromLimRef = fromLim; } -static void PTRCALL -utf8_toUtf16(const ENCODING *enc, - const char **fromP, const char *fromLim, - unsigned short **toP, const unsigned short *toLim) -{ +static enum XML_Convert_Result PTRCALL +utf8_toUtf8(const ENCODING *enc, const char **fromP, const char *fromLim, + char **toP, const char *toLim) { + bool input_incomplete = false; + bool output_exhausted = false; + + /* Avoid copying partial characters (due to limited space). */ + const ptrdiff_t bytesAvailable = fromLim - *fromP; + const ptrdiff_t bytesStorable = toLim - *toP; + UNUSED_P(enc); + if (bytesAvailable > bytesStorable) { + fromLim = *fromP + bytesStorable; + output_exhausted = true; + } + + /* Avoid copying partial characters (from incomplete input). */ + { + const char *const fromLimBefore = fromLim; + _INTERNAL_trim_to_complete_utf8_characters(*fromP, &fromLim); + if (fromLim < fromLimBefore) { + input_incomplete = true; + } + } + + { + const ptrdiff_t bytesToCopy = fromLim - *fromP; + memcpy(*toP, *fromP, bytesToCopy); + *fromP += bytesToCopy; + *toP += bytesToCopy; + } + + if (output_exhausted) /* needs to go first */ + return XML_CONVERT_OUTPUT_EXHAUSTED; + else if (input_incomplete) + return XML_CONVERT_INPUT_INCOMPLETE; + else + return XML_CONVERT_COMPLETED; +} + +static enum XML_Convert_Result PTRCALL +utf8_toUtf16(const ENCODING *enc, const char **fromP, const char *fromLim, + unsigned short **toP, const unsigned short *toLim) { + enum XML_Convert_Result res = XML_CONVERT_COMPLETED; unsigned short *to = *toP; const char *from = *fromP; - while (from != fromLim && to != toLim) { - switch (((struct normal_encoding *)enc)->type[(unsigned char)*from]) { + while (from < fromLim && to < toLim) { + switch (SB_BYTE_TYPE(enc, from)) { case BT_LEAD2: + if (fromLim - from < 2) { + res = XML_CONVERT_INPUT_INCOMPLETE; + goto after; + } *to++ = (unsigned short)(((from[0] & 0x1f) << 6) | (from[1] & 0x3f)); from += 2; break; case BT_LEAD3: - *to++ = (unsigned short)(((from[0] & 0xf) << 12) - | ((from[1] & 0x3f) << 6) | (from[2] & 0x3f)); + if (fromLim - from < 3) { + res = XML_CONVERT_INPUT_INCOMPLETE; + goto after; + } + *to++ = (unsigned short)(((from[0] & 0xf) << 12) | ((from[1] & 0x3f) << 6) + | (from[2] & 0x3f)); from += 3; break; - case BT_LEAD4: - { - unsigned long n; - if (to + 1 == toLim) - goto after; - n = ((from[0] & 0x7) << 18) | ((from[1] & 0x3f) << 12) - | ((from[2] & 0x3f) << 6) | (from[3] & 0x3f); - n -= 0x10000; - to[0] = (unsigned short)((n >> 10) | 0xD800); - to[1] = (unsigned short)((n & 0x3FF) | 0xDC00); - to += 2; - from += 4; + case BT_LEAD4: { + unsigned long n; + if (toLim - to < 2) { + res = XML_CONVERT_OUTPUT_EXHAUSTED; + goto after; } - break; + if (fromLim - from < 4) { + res = XML_CONVERT_INPUT_INCOMPLETE; + goto after; + } + n = ((from[0] & 0x7) << 18) | ((from[1] & 0x3f) << 12) + | ((from[2] & 0x3f) << 6) | (from[3] & 0x3f); + n -= 0x10000; + to[0] = (unsigned short)((n >> 10) | 0xD800); + to[1] = (unsigned short)((n & 0x3FF) | 0xDC00); + to += 2; + from += 4; + } break; default: *to++ = *from++; break; } } + if (from < fromLim) + res = XML_CONVERT_OUTPUT_EXHAUSTED; after: *fromP = from; *toP = to; + return res; } #ifdef XML_NS -static const struct normal_encoding utf8_encoding_ns = { - { VTABLE1, utf8_toUtf8, utf8_toUtf16, 1, 1, 0 }, - { -#include "asciitab.h" -#include "utf8tab.h" - }, - STANDARD_VTABLE(sb_) NORMAL_VTABLE(utf8_) -}; +static const struct normal_encoding utf8_encoding_ns + = {{VTABLE1, utf8_toUtf8, utf8_toUtf16, 1, 1, 0}, + { +# include "asciitab.h" +# include "utf8tab.h" + }, + STANDARD_VTABLE(sb_) NORMAL_VTABLE(utf8_)}; #endif -static const struct normal_encoding utf8_encoding = { - { VTABLE1, utf8_toUtf8, utf8_toUtf16, 1, 1, 0 }, - { +static const struct normal_encoding utf8_encoding + = {{VTABLE1, utf8_toUtf8, utf8_toUtf16, 1, 1, 0}, + { #define BT_COLON BT_NMSTRT #include "asciitab.h" #undef BT_COLON #include "utf8tab.h" - }, - STANDARD_VTABLE(sb_) NORMAL_VTABLE(utf8_) -}; + }, + STANDARD_VTABLE(sb_) NORMAL_VTABLE(utf8_)}; #ifdef XML_NS -static const struct normal_encoding internal_utf8_encoding_ns = { - { VTABLE1, utf8_toUtf8, utf8_toUtf16, 1, 1, 0 }, - { -#include "iasciitab.h" -#include "utf8tab.h" - }, - STANDARD_VTABLE(sb_) NORMAL_VTABLE(utf8_) -}; +static const struct normal_encoding internal_utf8_encoding_ns + = {{VTABLE1, utf8_toUtf8, utf8_toUtf16, 1, 1, 0}, + { +# include "iasciitab.h" +# include "utf8tab.h" + }, + STANDARD_VTABLE(sb_) NORMAL_VTABLE(utf8_)}; #endif -static const struct normal_encoding internal_utf8_encoding = { - { VTABLE1, utf8_toUtf8, utf8_toUtf16, 1, 1, 0 }, - { +static const struct normal_encoding internal_utf8_encoding + = {{VTABLE1, utf8_toUtf8, utf8_toUtf16, 1, 1, 0}, + { #define BT_COLON BT_NMSTRT #include "iasciitab.h" #undef BT_COLON #include "utf8tab.h" - }, - STANDARD_VTABLE(sb_) NORMAL_VTABLE(utf8_) -}; + }, + STANDARD_VTABLE(sb_) NORMAL_VTABLE(utf8_)}; -static void PTRCALL -latin1_toUtf8(const ENCODING *enc, - const char **fromP, const char *fromLim, - char **toP, const char *toLim) -{ +static enum XML_Convert_Result PTRCALL +latin1_toUtf8(const ENCODING *enc, const char **fromP, const char *fromLim, + char **toP, const char *toLim) { + UNUSED_P(enc); for (;;) { unsigned char c; if (*fromP == fromLim) - break; + return XML_CONVERT_COMPLETED; c = (unsigned char)**fromP; if (c & 0x80) { if (toLim - *toP < 2) - break; + return XML_CONVERT_OUTPUT_EXHAUSTED; *(*toP)++ = (char)((c >> 6) | UTF8_cval2); *(*toP)++ = (char)((c & 0x3f) | 0x80); (*fromP)++; - } - else { + } else { if (*toP == toLim) - break; + return XML_CONVERT_OUTPUT_EXHAUSTED; *(*toP)++ = *(*fromP)++; } } } -static void PTRCALL -latin1_toUtf16(const ENCODING *enc, - const char **fromP, const char *fromLim, - unsigned short **toP, const unsigned short *toLim) -{ - while (*fromP != fromLim && *toP != toLim) +static enum XML_Convert_Result PTRCALL +latin1_toUtf16(const ENCODING *enc, const char **fromP, const char *fromLim, + unsigned short **toP, const unsigned short *toLim) { + UNUSED_P(enc); + while (*fromP < fromLim && *toP < toLim) *(*toP)++ = (unsigned char)*(*fromP)++; + + if ((*toP == toLim) && (*fromP < fromLim)) + return XML_CONVERT_OUTPUT_EXHAUSTED; + else + return XML_CONVERT_COMPLETED; } #ifdef XML_NS -static const struct normal_encoding latin1_encoding_ns = { - { VTABLE1, latin1_toUtf8, latin1_toUtf16, 1, 0, 0 }, - { -#include "asciitab.h" -#include "latin1tab.h" - }, - STANDARD_VTABLE(sb_) -}; +static const struct normal_encoding latin1_encoding_ns + = {{VTABLE1, latin1_toUtf8, latin1_toUtf16, 1, 0, 0}, + { +# include "asciitab.h" +# include "latin1tab.h" + }, + STANDARD_VTABLE(sb_) NULL_VTABLE}; #endif -static const struct normal_encoding latin1_encoding = { - { VTABLE1, latin1_toUtf8, latin1_toUtf16, 1, 0, 0 }, - { +static const struct normal_encoding latin1_encoding + = {{VTABLE1, latin1_toUtf8, latin1_toUtf16, 1, 0, 0}, + { #define BT_COLON BT_NMSTRT #include "asciitab.h" #undef BT_COLON #include "latin1tab.h" - }, - STANDARD_VTABLE(sb_) -}; - -static void PTRCALL -ascii_toUtf8(const ENCODING *enc, - const char **fromP, const char *fromLim, - char **toP, const char *toLim) -{ - while (*fromP != fromLim && *toP != toLim) + }, + STANDARD_VTABLE(sb_) NULL_VTABLE}; + +static enum XML_Convert_Result PTRCALL +ascii_toUtf8(const ENCODING *enc, const char **fromP, const char *fromLim, + char **toP, const char *toLim) { + UNUSED_P(enc); + while (*fromP < fromLim && *toP < toLim) *(*toP)++ = *(*fromP)++; + + if ((*toP == toLim) && (*fromP < fromLim)) + return XML_CONVERT_OUTPUT_EXHAUSTED; + else + return XML_CONVERT_COMPLETED; } #ifdef XML_NS -static const struct normal_encoding ascii_encoding_ns = { - { VTABLE1, ascii_toUtf8, latin1_toUtf16, 1, 1, 0 }, - { -#include "asciitab.h" -/* BT_NONXML == 0 */ - }, - STANDARD_VTABLE(sb_) -}; +static const struct normal_encoding ascii_encoding_ns + = {{VTABLE1, ascii_toUtf8, latin1_toUtf16, 1, 1, 0}, + { +# include "asciitab.h" + /* BT_NONXML == 0 */ + }, + STANDARD_VTABLE(sb_) NULL_VTABLE}; #endif -static const struct normal_encoding ascii_encoding = { - { VTABLE1, ascii_toUtf8, latin1_toUtf16, 1, 1, 0 }, - { +static const struct normal_encoding ascii_encoding + = {{VTABLE1, ascii_toUtf8, latin1_toUtf16, 1, 1, 0}, + { #define BT_COLON BT_NMSTRT #include "asciitab.h" #undef BT_COLON -/* BT_NONXML == 0 */ - }, - STANDARD_VTABLE(sb_) -}; + /* BT_NONXML == 0 */ + }, + STANDARD_VTABLE(sb_) NULL_VTABLE}; static int PTRFASTCALL -unicode_byte_type(char hi, char lo) -{ +unicode_byte_type(char hi, char lo) { switch ((unsigned char)hi) { - case 0xD8: case 0xD9: case 0xDA: case 0xDB: + /* 0xD800-0xDBFF first 16-bit code unit or high surrogate (W1) */ + case 0xD8: + case 0xD9: + case 0xDA: + case 0xDB: return BT_LEAD4; - case 0xDC: case 0xDD: case 0xDE: case 0xDF: + /* 0xDC00-0xDFFF second 16-bit code unit or low surrogate (W2) */ + case 0xDC: + case 0xDD: + case 0xDE: + case 0xDF: return BT_TRAIL; case 0xFF: switch ((unsigned char)lo) { - case 0xFF: - case 0xFE: + case 0xFF: /* noncharacter-FFFF */ + case 0xFE: /* noncharacter-FFFE */ return BT_NONXML; } break; @@ -535,418 +619,420 @@ unicode_byte_type(char hi, char lo) return BT_NONASCII; } -#define DEFINE_UTF16_TO_UTF8(E) \ -static void PTRCALL \ -E ## toUtf8(const ENCODING *enc, \ - const char **fromP, const char *fromLim, \ - char **toP, const char *toLim) \ -{ \ - const char *from; \ - for (from = *fromP; from != fromLim; from += 2) { \ - int plane; \ - unsigned char lo2; \ - unsigned char lo = GET_LO(from); \ - unsigned char hi = GET_HI(from); \ - switch (hi) { \ - case 0: \ - if (lo < 0x80) { \ - if (*toP == toLim) { \ - *fromP = from; \ - return; \ - } \ - *(*toP)++ = lo; \ - break; \ - } \ - /* fall through */ \ - case 0x1: case 0x2: case 0x3: \ - case 0x4: case 0x5: case 0x6: case 0x7: \ - if (toLim - *toP < 2) { \ - *fromP = from; \ - return; \ - } \ - *(*toP)++ = ((lo >> 6) | (hi << 2) | UTF8_cval2); \ - *(*toP)++ = ((lo & 0x3f) | 0x80); \ - break; \ - default: \ - if (toLim - *toP < 3) { \ - *fromP = from; \ - return; \ - } \ - /* 16 bits divided 4, 6, 6 amongst 3 bytes */ \ - *(*toP)++ = ((hi >> 4) | UTF8_cval3); \ - *(*toP)++ = (((hi & 0xf) << 2) | (lo >> 6) | 0x80); \ - *(*toP)++ = ((lo & 0x3f) | 0x80); \ - break; \ - case 0xD8: case 0xD9: case 0xDA: case 0xDB: \ - if (toLim - *toP < 4) { \ - *fromP = from; \ - return; \ - } \ - plane = (((hi & 0x3) << 2) | ((lo >> 6) & 0x3)) + 1; \ - *(*toP)++ = ((plane >> 2) | UTF8_cval4); \ - *(*toP)++ = (((lo >> 2) & 0xF) | ((plane & 0x3) << 4) | 0x80); \ - from += 2; \ - lo2 = GET_LO(from); \ - *(*toP)++ = (((lo & 0x3) << 4) \ - | ((GET_HI(from) & 0x3) << 2) \ - | (lo2 >> 6) \ - | 0x80); \ - *(*toP)++ = ((lo2 & 0x3f) | 0x80); \ - break; \ - } \ - } \ - *fromP = from; \ -} +#define DEFINE_UTF16_TO_UTF8(E) \ + static enum XML_Convert_Result PTRCALL E##toUtf8( \ + const ENCODING *enc, const char **fromP, const char *fromLim, \ + char **toP, const char *toLim) { \ + const char *from = *fromP; \ + UNUSED_P(enc); \ + fromLim = from + (((fromLim - from) >> 1) << 1); /* shrink to even */ \ + for (; from < fromLim; from += 2) { \ + int plane; \ + unsigned char lo2; \ + unsigned char lo = GET_LO(from); \ + unsigned char hi = GET_HI(from); \ + switch (hi) { \ + case 0: \ + if (lo < 0x80) { \ + if (*toP == toLim) { \ + *fromP = from; \ + return XML_CONVERT_OUTPUT_EXHAUSTED; \ + } \ + *(*toP)++ = lo; \ + break; \ + } \ + /* fall through */ \ + case 0x1: \ + case 0x2: \ + case 0x3: \ + case 0x4: \ + case 0x5: \ + case 0x6: \ + case 0x7: \ + if (toLim - *toP < 2) { \ + *fromP = from; \ + return XML_CONVERT_OUTPUT_EXHAUSTED; \ + } \ + *(*toP)++ = ((lo >> 6) | (hi << 2) | UTF8_cval2); \ + *(*toP)++ = ((lo & 0x3f) | 0x80); \ + break; \ + default: \ + if (toLim - *toP < 3) { \ + *fromP = from; \ + return XML_CONVERT_OUTPUT_EXHAUSTED; \ + } \ + /* 16 bits divided 4, 6, 6 amongst 3 bytes */ \ + *(*toP)++ = ((hi >> 4) | UTF8_cval3); \ + *(*toP)++ = (((hi & 0xf) << 2) | (lo >> 6) | 0x80); \ + *(*toP)++ = ((lo & 0x3f) | 0x80); \ + break; \ + case 0xD8: \ + case 0xD9: \ + case 0xDA: \ + case 0xDB: \ + if (toLim - *toP < 4) { \ + *fromP = from; \ + return XML_CONVERT_OUTPUT_EXHAUSTED; \ + } \ + if (fromLim - from < 4) { \ + *fromP = from; \ + return XML_CONVERT_INPUT_INCOMPLETE; \ + } \ + plane = (((hi & 0x3) << 2) | ((lo >> 6) & 0x3)) + 1; \ + *(*toP)++ = (char)((plane >> 2) | UTF8_cval4); \ + *(*toP)++ = (((lo >> 2) & 0xF) | ((plane & 0x3) << 4) | 0x80); \ + from += 2; \ + lo2 = GET_LO(from); \ + *(*toP)++ = (((lo & 0x3) << 4) | ((GET_HI(from) & 0x3) << 2) \ + | (lo2 >> 6) | 0x80); \ + *(*toP)++ = ((lo2 & 0x3f) | 0x80); \ + break; \ + } \ + } \ + *fromP = from; \ + if (from < fromLim) \ + return XML_CONVERT_INPUT_INCOMPLETE; \ + else \ + return XML_CONVERT_COMPLETED; \ + } -#define DEFINE_UTF16_TO_UTF16(E) \ -static void PTRCALL \ -E ## toUtf16(const ENCODING *enc, \ - const char **fromP, const char *fromLim, \ - unsigned short **toP, const unsigned short *toLim) \ -{ \ - /* Avoid copying first half only of surrogate */ \ - if (fromLim - *fromP > ((toLim - *toP) << 1) \ - && (GET_HI(fromLim - 2) & 0xF8) == 0xD8) \ - fromLim -= 2; \ - for (; *fromP != fromLim && *toP != toLim; *fromP += 2) \ - *(*toP)++ = (GET_HI(*fromP) << 8) | GET_LO(*fromP); \ -} +#define DEFINE_UTF16_TO_UTF16(E) \ + static enum XML_Convert_Result PTRCALL E##toUtf16( \ + const ENCODING *enc, const char **fromP, const char *fromLim, \ + unsigned short **toP, const unsigned short *toLim) { \ + enum XML_Convert_Result res = XML_CONVERT_COMPLETED; \ + UNUSED_P(enc); \ + fromLim = *fromP + (((fromLim - *fromP) >> 1) << 1); /* shrink to even */ \ + /* Avoid copying first half only of surrogate */ \ + if (fromLim - *fromP > ((toLim - *toP) << 1) \ + && (GET_HI(fromLim - 2) & 0xF8) == 0xD8) { \ + fromLim -= 2; \ + res = XML_CONVERT_INPUT_INCOMPLETE; \ + } \ + for (; *fromP < fromLim && *toP < toLim; *fromP += 2) \ + *(*toP)++ = (GET_HI(*fromP) << 8) | GET_LO(*fromP); \ + if ((*toP == toLim) && (*fromP < fromLim)) \ + return XML_CONVERT_OUTPUT_EXHAUSTED; \ + else \ + return res; \ + } -#define SET2(ptr, ch) \ - (((ptr)[0] = ((ch) & 0xff)), ((ptr)[1] = ((ch) >> 8))) #define GET_LO(ptr) ((unsigned char)(ptr)[0]) #define GET_HI(ptr) ((unsigned char)(ptr)[1]) DEFINE_UTF16_TO_UTF8(little2_) DEFINE_UTF16_TO_UTF16(little2_) -#undef SET2 #undef GET_LO #undef GET_HI -#define SET2(ptr, ch) \ - (((ptr)[0] = ((ch) >> 8)), ((ptr)[1] = ((ch) & 0xFF))) #define GET_LO(ptr) ((unsigned char)(ptr)[1]) #define GET_HI(ptr) ((unsigned char)(ptr)[0]) DEFINE_UTF16_TO_UTF8(big2_) DEFINE_UTF16_TO_UTF16(big2_) -#undef SET2 #undef GET_LO #undef GET_HI -#define LITTLE2_BYTE_TYPE(enc, p) \ - ((p)[1] == 0 \ - ? ((struct normal_encoding *)(enc))->type[(unsigned char)*(p)] \ - : unicode_byte_type((p)[1], (p)[0])) -#define LITTLE2_BYTE_TO_ASCII(enc, p) ((p)[1] == 0 ? (p)[0] : -1) -#define LITTLE2_CHAR_MATCHES(enc, p, c) ((p)[1] == 0 && (p)[0] == c) -#define LITTLE2_IS_NAME_CHAR_MINBPC(enc, p) \ +#define LITTLE2_BYTE_TYPE(enc, p) \ + ((p)[1] == 0 ? SB_BYTE_TYPE(enc, p) : unicode_byte_type((p)[1], (p)[0])) +#define LITTLE2_BYTE_TO_ASCII(p) ((p)[1] == 0 ? (p)[0] : -1) +#define LITTLE2_CHAR_MATCHES(p, c) ((p)[1] == 0 && (p)[0] == (c)) +#define LITTLE2_IS_NAME_CHAR_MINBPC(p) \ UCS2_GET_NAMING(namePages, (unsigned char)p[1], (unsigned char)p[0]) -#define LITTLE2_IS_NMSTRT_CHAR_MINBPC(enc, p) \ +#define LITTLE2_IS_NMSTRT_CHAR_MINBPC(p) \ UCS2_GET_NAMING(nmstrtPages, (unsigned char)p[1], (unsigned char)p[0]) #ifdef XML_MIN_SIZE static int PTRFASTCALL -little2_byteType(const ENCODING *enc, const char *p) -{ +little2_byteType(const ENCODING *enc, const char *p) { return LITTLE2_BYTE_TYPE(enc, p); } static int PTRFASTCALL -little2_byteToAscii(const ENCODING *enc, const char *p) -{ - return LITTLE2_BYTE_TO_ASCII(enc, p); +little2_byteToAscii(const ENCODING *enc, const char *p) { + UNUSED_P(enc); + return LITTLE2_BYTE_TO_ASCII(p); } static int PTRCALL -little2_charMatches(const ENCODING *enc, const char *p, int c) -{ - return LITTLE2_CHAR_MATCHES(enc, p, c); +little2_charMatches(const ENCODING *enc, const char *p, int c) { + UNUSED_P(enc); + return LITTLE2_CHAR_MATCHES(p, c); } static int PTRFASTCALL -little2_isNameMin(const ENCODING *enc, const char *p) -{ - return LITTLE2_IS_NAME_CHAR_MINBPC(enc, p); +little2_isNameMin(const ENCODING *enc, const char *p) { + UNUSED_P(enc); + return LITTLE2_IS_NAME_CHAR_MINBPC(p); } static int PTRFASTCALL -little2_isNmstrtMin(const ENCODING *enc, const char *p) -{ - return LITTLE2_IS_NMSTRT_CHAR_MINBPC(enc, p); +little2_isNmstrtMin(const ENCODING *enc, const char *p) { + UNUSED_P(enc); + return LITTLE2_IS_NMSTRT_CHAR_MINBPC(p); } -#undef VTABLE -#define VTABLE VTABLE1, little2_toUtf8, little2_toUtf16 +# undef VTABLE +# define VTABLE VTABLE1, little2_toUtf8, little2_toUtf16 #else /* not XML_MIN_SIZE */ -#undef PREFIX -#define PREFIX(ident) little2_ ## ident -#define MINBPC(enc) 2 +# undef PREFIX +# define PREFIX(ident) little2_##ident +# define MINBPC(enc) 2 /* CHAR_MATCHES is guaranteed to have MINBPC bytes available. */ -#define BYTE_TYPE(enc, p) LITTLE2_BYTE_TYPE(enc, p) -#define BYTE_TO_ASCII(enc, p) LITTLE2_BYTE_TO_ASCII(enc, p) -#define CHAR_MATCHES(enc, p, c) LITTLE2_CHAR_MATCHES(enc, p, c) -#define IS_NAME_CHAR(enc, p, n) 0 -#define IS_NAME_CHAR_MINBPC(enc, p) LITTLE2_IS_NAME_CHAR_MINBPC(enc, p) -#define IS_NMSTRT_CHAR(enc, p, n) (0) -#define IS_NMSTRT_CHAR_MINBPC(enc, p) LITTLE2_IS_NMSTRT_CHAR_MINBPC(enc, p) - -#define XML_TOK_IMPL_C -#include "xmltok_impl.c" -#undef XML_TOK_IMPL_C - -#undef MINBPC -#undef BYTE_TYPE -#undef BYTE_TO_ASCII -#undef CHAR_MATCHES -#undef IS_NAME_CHAR -#undef IS_NAME_CHAR_MINBPC -#undef IS_NMSTRT_CHAR -#undef IS_NMSTRT_CHAR_MINBPC -#undef IS_INVALID_CHAR +# define BYTE_TYPE(enc, p) LITTLE2_BYTE_TYPE(enc, p) +# define BYTE_TO_ASCII(enc, p) LITTLE2_BYTE_TO_ASCII(p) +# define CHAR_MATCHES(enc, p, c) LITTLE2_CHAR_MATCHES(p, c) +# define IS_NAME_CHAR(enc, p, n) 0 +# define IS_NAME_CHAR_MINBPC(enc, p) LITTLE2_IS_NAME_CHAR_MINBPC(p) +# define IS_NMSTRT_CHAR(enc, p, n) (0) +# define IS_NMSTRT_CHAR_MINBPC(enc, p) LITTLE2_IS_NMSTRT_CHAR_MINBPC(p) + +# define XML_TOK_IMPL_C +# include "xmltok_impl.c" +# undef XML_TOK_IMPL_C + +# undef MINBPC +# undef BYTE_TYPE +# undef BYTE_TO_ASCII +# undef CHAR_MATCHES +# undef IS_NAME_CHAR +# undef IS_NAME_CHAR_MINBPC +# undef IS_NMSTRT_CHAR +# undef IS_NMSTRT_CHAR_MINBPC +# undef IS_INVALID_CHAR #endif /* not XML_MIN_SIZE */ #ifdef XML_NS -static const struct normal_encoding little2_encoding_ns = { - { VTABLE, 2, 0, -#if BYTEORDER == 1234 - 1 -#else - 0 -#endif - }, - { -#include "asciitab.h" -#include "latin1tab.h" - }, - STANDARD_VTABLE(little2_) -}; +static const struct normal_encoding little2_encoding_ns + = {{VTABLE, 2, 0, +# if BYTEORDER == 1234 + 1 +# else + 0 +# endif + }, + { +# include "asciitab.h" +# include "latin1tab.h" + }, + STANDARD_VTABLE(little2_) NULL_VTABLE}; #endif -static const struct normal_encoding little2_encoding = { - { VTABLE, 2, 0, +static const struct normal_encoding little2_encoding + = {{VTABLE, 2, 0, #if BYTEORDER == 1234 - 1 + 1 #else - 0 + 0 #endif - }, - { + }, + { #define BT_COLON BT_NMSTRT #include "asciitab.h" #undef BT_COLON #include "latin1tab.h" - }, - STANDARD_VTABLE(little2_) -}; + }, + STANDARD_VTABLE(little2_) NULL_VTABLE}; #if BYTEORDER != 4321 -#ifdef XML_NS +# ifdef XML_NS -static const struct normal_encoding internal_little2_encoding_ns = { - { VTABLE, 2, 0, 1 }, - { -#include "iasciitab.h" -#include "latin1tab.h" - }, - STANDARD_VTABLE(little2_) -}; +static const struct normal_encoding internal_little2_encoding_ns + = {{VTABLE, 2, 0, 1}, + { +# include "iasciitab.h" +# include "latin1tab.h" + }, + STANDARD_VTABLE(little2_) NULL_VTABLE}; -#endif +# endif -static const struct normal_encoding internal_little2_encoding = { - { VTABLE, 2, 0, 1 }, - { -#define BT_COLON BT_NMSTRT -#include "iasciitab.h" -#undef BT_COLON -#include "latin1tab.h" - }, - STANDARD_VTABLE(little2_) -}; +static const struct normal_encoding internal_little2_encoding + = {{VTABLE, 2, 0, 1}, + { +# define BT_COLON BT_NMSTRT +# include "iasciitab.h" +# undef BT_COLON +# include "latin1tab.h" + }, + STANDARD_VTABLE(little2_) NULL_VTABLE}; #endif - -#define BIG2_BYTE_TYPE(enc, p) \ - ((p)[0] == 0 \ - ? ((struct normal_encoding *)(enc))->type[(unsigned char)(p)[1]] \ - : unicode_byte_type((p)[0], (p)[1])) -#define BIG2_BYTE_TO_ASCII(enc, p) ((p)[0] == 0 ? (p)[1] : -1) -#define BIG2_CHAR_MATCHES(enc, p, c) ((p)[0] == 0 && (p)[1] == c) -#define BIG2_IS_NAME_CHAR_MINBPC(enc, p) \ +#define BIG2_BYTE_TYPE(enc, p) \ + ((p)[0] == 0 ? SB_BYTE_TYPE(enc, p + 1) : unicode_byte_type((p)[0], (p)[1])) +#define BIG2_BYTE_TO_ASCII(p) ((p)[0] == 0 ? (p)[1] : -1) +#define BIG2_CHAR_MATCHES(p, c) ((p)[0] == 0 && (p)[1] == (c)) +#define BIG2_IS_NAME_CHAR_MINBPC(p) \ UCS2_GET_NAMING(namePages, (unsigned char)p[0], (unsigned char)p[1]) -#define BIG2_IS_NMSTRT_CHAR_MINBPC(enc, p) \ +#define BIG2_IS_NMSTRT_CHAR_MINBPC(p) \ UCS2_GET_NAMING(nmstrtPages, (unsigned char)p[0], (unsigned char)p[1]) #ifdef XML_MIN_SIZE static int PTRFASTCALL -big2_byteType(const ENCODING *enc, const char *p) -{ +big2_byteType(const ENCODING *enc, const char *p) { return BIG2_BYTE_TYPE(enc, p); } static int PTRFASTCALL -big2_byteToAscii(const ENCODING *enc, const char *p) -{ - return BIG2_BYTE_TO_ASCII(enc, p); +big2_byteToAscii(const ENCODING *enc, const char *p) { + UNUSED_P(enc); + return BIG2_BYTE_TO_ASCII(p); } static int PTRCALL -big2_charMatches(const ENCODING *enc, const char *p, int c) -{ - return BIG2_CHAR_MATCHES(enc, p, c); +big2_charMatches(const ENCODING *enc, const char *p, int c) { + UNUSED_P(enc); + return BIG2_CHAR_MATCHES(p, c); } static int PTRFASTCALL -big2_isNameMin(const ENCODING *enc, const char *p) -{ - return BIG2_IS_NAME_CHAR_MINBPC(enc, p); +big2_isNameMin(const ENCODING *enc, const char *p) { + UNUSED_P(enc); + return BIG2_IS_NAME_CHAR_MINBPC(p); } static int PTRFASTCALL -big2_isNmstrtMin(const ENCODING *enc, const char *p) -{ - return BIG2_IS_NMSTRT_CHAR_MINBPC(enc, p); +big2_isNmstrtMin(const ENCODING *enc, const char *p) { + UNUSED_P(enc); + return BIG2_IS_NMSTRT_CHAR_MINBPC(p); } -#undef VTABLE -#define VTABLE VTABLE1, big2_toUtf8, big2_toUtf16 +# undef VTABLE +# define VTABLE VTABLE1, big2_toUtf8, big2_toUtf16 #else /* not XML_MIN_SIZE */ -#undef PREFIX -#define PREFIX(ident) big2_ ## ident -#define MINBPC(enc) 2 +# undef PREFIX +# define PREFIX(ident) big2_##ident +# define MINBPC(enc) 2 /* CHAR_MATCHES is guaranteed to have MINBPC bytes available. */ -#define BYTE_TYPE(enc, p) BIG2_BYTE_TYPE(enc, p) -#define BYTE_TO_ASCII(enc, p) BIG2_BYTE_TO_ASCII(enc, p) -#define CHAR_MATCHES(enc, p, c) BIG2_CHAR_MATCHES(enc, p, c) -#define IS_NAME_CHAR(enc, p, n) 0 -#define IS_NAME_CHAR_MINBPC(enc, p) BIG2_IS_NAME_CHAR_MINBPC(enc, p) -#define IS_NMSTRT_CHAR(enc, p, n) (0) -#define IS_NMSTRT_CHAR_MINBPC(enc, p) BIG2_IS_NMSTRT_CHAR_MINBPC(enc, p) - -#define XML_TOK_IMPL_C -#include "xmltok_impl.c" -#undef XML_TOK_IMPL_C - -#undef MINBPC -#undef BYTE_TYPE -#undef BYTE_TO_ASCII -#undef CHAR_MATCHES -#undef IS_NAME_CHAR -#undef IS_NAME_CHAR_MINBPC -#undef IS_NMSTRT_CHAR -#undef IS_NMSTRT_CHAR_MINBPC -#undef IS_INVALID_CHAR +# define BYTE_TYPE(enc, p) BIG2_BYTE_TYPE(enc, p) +# define BYTE_TO_ASCII(enc, p) BIG2_BYTE_TO_ASCII(p) +# define CHAR_MATCHES(enc, p, c) BIG2_CHAR_MATCHES(p, c) +# define IS_NAME_CHAR(enc, p, n) 0 +# define IS_NAME_CHAR_MINBPC(enc, p) BIG2_IS_NAME_CHAR_MINBPC(p) +# define IS_NMSTRT_CHAR(enc, p, n) (0) +# define IS_NMSTRT_CHAR_MINBPC(enc, p) BIG2_IS_NMSTRT_CHAR_MINBPC(p) + +# define XML_TOK_IMPL_C +# include "xmltok_impl.c" +# undef XML_TOK_IMPL_C + +# undef MINBPC +# undef BYTE_TYPE +# undef BYTE_TO_ASCII +# undef CHAR_MATCHES +# undef IS_NAME_CHAR +# undef IS_NAME_CHAR_MINBPC +# undef IS_NMSTRT_CHAR +# undef IS_NMSTRT_CHAR_MINBPC +# undef IS_INVALID_CHAR #endif /* not XML_MIN_SIZE */ #ifdef XML_NS -static const struct normal_encoding big2_encoding_ns = { - { VTABLE, 2, 0, -#if BYTEORDER == 4321 - 1 -#else - 0 -#endif - }, - { -#include "asciitab.h" -#include "latin1tab.h" - }, - STANDARD_VTABLE(big2_) -}; +static const struct normal_encoding big2_encoding_ns + = {{VTABLE, 2, 0, +# if BYTEORDER == 4321 + 1 +# else + 0 +# endif + }, + { +# include "asciitab.h" +# include "latin1tab.h" + }, + STANDARD_VTABLE(big2_) NULL_VTABLE}; #endif -static const struct normal_encoding big2_encoding = { - { VTABLE, 2, 0, +static const struct normal_encoding big2_encoding + = {{VTABLE, 2, 0, #if BYTEORDER == 4321 - 1 + 1 #else - 0 + 0 #endif - }, - { + }, + { #define BT_COLON BT_NMSTRT #include "asciitab.h" #undef BT_COLON #include "latin1tab.h" - }, - STANDARD_VTABLE(big2_) -}; + }, + STANDARD_VTABLE(big2_) NULL_VTABLE}; #if BYTEORDER != 1234 -#ifdef XML_NS +# ifdef XML_NS -static const struct normal_encoding internal_big2_encoding_ns = { - { VTABLE, 2, 0, 1 }, - { -#include "iasciitab.h" -#include "latin1tab.h" - }, - STANDARD_VTABLE(big2_) -}; +static const struct normal_encoding internal_big2_encoding_ns + = {{VTABLE, 2, 0, 1}, + { +# include "iasciitab.h" +# include "latin1tab.h" + }, + STANDARD_VTABLE(big2_) NULL_VTABLE}; -#endif +# endif -static const struct normal_encoding internal_big2_encoding = { - { VTABLE, 2, 0, 1 }, - { -#define BT_COLON BT_NMSTRT -#include "iasciitab.h" -#undef BT_COLON -#include "latin1tab.h" - }, - STANDARD_VTABLE(big2_) -}; +static const struct normal_encoding internal_big2_encoding + = {{VTABLE, 2, 0, 1}, + { +# define BT_COLON BT_NMSTRT +# include "iasciitab.h" +# undef BT_COLON +# include "latin1tab.h" + }, + STANDARD_VTABLE(big2_) NULL_VTABLE}; #endif #undef PREFIX static int FASTCALL -streqci(const char *s1, const char *s2) -{ +streqci(const char *s1, const char *s2) { for (;;) { char c1 = *s1++; char c2 = *s2++; if (ASCII_a <= c1 && c1 <= ASCII_z) c1 += ASCII_A - ASCII_a; if (ASCII_a <= c2 && c2 <= ASCII_z) - c2 += ASCII_A - ASCII_a; + /* The following line will never get executed. streqci() is + * only called from two places, both of which guarantee to put + * upper-case strings into s2. + */ + c2 += ASCII_A - ASCII_a; /* LCOV_EXCL_LINE */ if (c1 != c2) return 0; - if (!c1) + if (! c1) break; } return 1; } static void PTRCALL -initUpdatePosition(const ENCODING *enc, const char *ptr, - const char *end, POSITION *pos) -{ +initUpdatePosition(const ENCODING *enc, const char *ptr, const char *end, + POSITION *pos) { + UNUSED_P(enc); normal_updatePosition(&utf8_encoding.enc, ptr, end, pos); } static int -toAscii(const ENCODING *enc, const char *ptr, const char *end) -{ +toAscii(const ENCODING *enc, const char *ptr, const char *end) { char buf[1]; char *p = buf; XmlUtf8Convert(enc, &ptr, end, &p, p + 1); @@ -957,8 +1043,7 @@ toAscii(const ENCODING *enc, const char *ptr, const char *end) } static int FASTCALL -isSpace(int c) -{ +isSpace(int c) { switch (c) { case 0x20: case 0xD: @@ -973,21 +1058,16 @@ isSpace(int c) followed by name=val. */ static int -parsePseudoAttribute(const ENCODING *enc, - const char *ptr, - const char *end, - const char **namePtr, - const char **nameEndPtr, - const char **valPtr, - const char **nextTokPtr) -{ +parsePseudoAttribute(const ENCODING *enc, const char *ptr, const char *end, + const char **namePtr, const char **nameEndPtr, + const char **valPtr, const char **nextTokPtr) { int c; char open; if (ptr == end) { *namePtr = NULL; return 1; } - if (!isSpace(toAscii(enc, ptr, end))) { + if (! isSpace(toAscii(enc, ptr, end))) { *nextTokPtr = ptr; return 0; } @@ -1043,12 +1123,9 @@ parsePseudoAttribute(const ENCODING *enc, c = toAscii(enc, ptr, end); if (c == open) break; - if (!(ASCII_a <= c && c <= ASCII_z) - && !(ASCII_A <= c && c <= ASCII_Z) - && !(ASCII_0 <= c && c <= ASCII_9) - && c != ASCII_PERIOD - && c != ASCII_MINUS - && c != ASCII_UNDERSCORE) { + if (! (ASCII_a <= c && c <= ASCII_z) && ! (ASCII_A <= c && c <= ASCII_Z) + && ! (ASCII_0 <= c && c <= ASCII_9) && c != ASCII_PERIOD + && c != ASCII_MINUS && c != ASCII_UNDERSCORE) { *nextTokPtr = ptr; return 0; } @@ -1057,68 +1134,52 @@ parsePseudoAttribute(const ENCODING *enc, return 1; } -static const char KW_version[] = { - ASCII_v, ASCII_e, ASCII_r, ASCII_s, ASCII_i, ASCII_o, ASCII_n, '\0' -}; +static const char KW_version[] + = {ASCII_v, ASCII_e, ASCII_r, ASCII_s, ASCII_i, ASCII_o, ASCII_n, '\0'}; -static const char KW_encoding[] = { - ASCII_e, ASCII_n, ASCII_c, ASCII_o, ASCII_d, ASCII_i, ASCII_n, ASCII_g, '\0' -}; +static const char KW_encoding[] = {ASCII_e, ASCII_n, ASCII_c, ASCII_o, ASCII_d, + ASCII_i, ASCII_n, ASCII_g, '\0'}; -static const char KW_standalone[] = { - ASCII_s, ASCII_t, ASCII_a, ASCII_n, ASCII_d, ASCII_a, ASCII_l, ASCII_o, - ASCII_n, ASCII_e, '\0' -}; +static const char KW_standalone[] + = {ASCII_s, ASCII_t, ASCII_a, ASCII_n, ASCII_d, ASCII_a, + ASCII_l, ASCII_o, ASCII_n, ASCII_e, '\0'}; -static const char KW_yes[] = { - ASCII_y, ASCII_e, ASCII_s, '\0' -}; +static const char KW_yes[] = {ASCII_y, ASCII_e, ASCII_s, '\0'}; -static const char KW_no[] = { - ASCII_n, ASCII_o, '\0' -}; +static const char KW_no[] = {ASCII_n, ASCII_o, '\0'}; static int -doParseXmlDecl(const ENCODING *(*encodingFinder)(const ENCODING *, - const char *, +doParseXmlDecl(const ENCODING *(*encodingFinder)(const ENCODING *, const char *, const char *), - int isGeneralTextEntity, - const ENCODING *enc, - const char *ptr, - const char *end, - const char **badPtr, - const char **versionPtr, - const char **versionEndPtr, - const char **encodingName, - const ENCODING **encoding, - int *standalone) -{ + int isGeneralTextEntity, const ENCODING *enc, const char *ptr, + const char *end, const char **badPtr, const char **versionPtr, + const char **versionEndPtr, const char **encodingName, + const ENCODING **encoding, int *standalone) { const char *val = NULL; const char *name = NULL; const char *nameEnd = NULL; ptr += 5 * enc->minBytesPerChar; end -= 2 * enc->minBytesPerChar; - if (!parsePseudoAttribute(enc, ptr, end, &name, &nameEnd, &val, &ptr) - || !name) { + if (! parsePseudoAttribute(enc, ptr, end, &name, &nameEnd, &val, &ptr) + || ! name) { *badPtr = ptr; return 0; } - if (!XmlNameMatchesAscii(enc, name, nameEnd, KW_version)) { - if (!isGeneralTextEntity) { + if (! XmlNameMatchesAscii(enc, name, nameEnd, KW_version)) { + if (! isGeneralTextEntity) { *badPtr = name; return 0; } - } - else { + } else { if (versionPtr) *versionPtr = val; if (versionEndPtr) *versionEndPtr = ptr; - if (!parsePseudoAttribute(enc, ptr, end, &name, &nameEnd, &val, &ptr)) { + if (! parsePseudoAttribute(enc, ptr, end, &name, &nameEnd, &val, &ptr)) { *badPtr = ptr; return 0; } - if (!name) { + if (! name) { if (isGeneralTextEntity) { /* a TextDecl must have an EncodingDecl */ *badPtr = ptr; @@ -1129,7 +1190,7 @@ doParseXmlDecl(const ENCODING *(*encodingFinder)(const ENCODING *, } if (XmlNameMatchesAscii(enc, name, nameEnd, KW_encoding)) { int c = toAscii(enc, val, end); - if (!(ASCII_a <= c && c <= ASCII_z) && !(ASCII_A <= c && c <= ASCII_Z)) { + if (! (ASCII_a <= c && c <= ASCII_z) && ! (ASCII_A <= c && c <= ASCII_Z)) { *badPtr = val; return 0; } @@ -1137,14 +1198,14 @@ doParseXmlDecl(const ENCODING *(*encodingFinder)(const ENCODING *, *encodingName = val; if (encoding) *encoding = encodingFinder(enc, val, ptr - enc->minBytesPerChar); - if (!parsePseudoAttribute(enc, ptr, end, &name, &nameEnd, &val, &ptr)) { + if (! parsePseudoAttribute(enc, ptr, end, &name, &nameEnd, &val, &ptr)) { *badPtr = ptr; return 0; } - if (!name) + if (! name) return 1; } - if (!XmlNameMatchesAscii(enc, name, nameEnd, KW_standalone) + if (! XmlNameMatchesAscii(enc, name, nameEnd, KW_standalone) || isGeneralTextEntity) { *badPtr = name; return 0; @@ -1152,12 +1213,10 @@ doParseXmlDecl(const ENCODING *(*encodingFinder)(const ENCODING *, if (XmlNameMatchesAscii(enc, val, ptr - enc->minBytesPerChar, KW_yes)) { if (standalone) *standalone = 1; - } - else if (XmlNameMatchesAscii(enc, val, ptr - enc->minBytesPerChar, KW_no)) { + } else if (XmlNameMatchesAscii(enc, val, ptr - enc->minBytesPerChar, KW_no)) { if (standalone) *standalone = 0; - } - else { + } else { *badPtr = val; return 0; } @@ -1171,11 +1230,16 @@ doParseXmlDecl(const ENCODING *(*encodingFinder)(const ENCODING *, } static int FASTCALL -checkCharRefNumber(int result) -{ +checkCharRefNumber(int result) { switch (result >> 8) { - case 0xD8: case 0xD9: case 0xDA: case 0xDB: - case 0xDC: case 0xDD: case 0xDE: case 0xDF: + case 0xD8: + case 0xD9: + case 0xDA: + case 0xDB: + case 0xDC: + case 0xDD: + case 0xDE: + case 0xDF: return -1; case 0: if (latin1_encoding.type[result] == BT_NONXML) @@ -1190,8 +1254,7 @@ checkCharRefNumber(int result) } int FASTCALL -XmlUtf8Encode(int c, char *buf) -{ +XmlUtf8Encode(int c, char *buf) { enum { /* minN is minimum legal resulting value for N byte sequence */ min2 = 0x80, @@ -1200,7 +1263,7 @@ XmlUtf8Encode(int c, char *buf) }; if (c < 0) - return 0; + return 0; /* LCOV_EXCL_LINE: this case is always eliminated beforehand */ if (c < min2) { buf[0] = (char)(c | UTF8_cval1); return 1; @@ -1223,12 +1286,11 @@ XmlUtf8Encode(int c, char *buf) buf[3] = (char)((c & 0x3f) | 0x80); return 4; } - return 0; + return 0; /* LCOV_EXCL_LINE: this case too is eliminated before calling */ } int FASTCALL -XmlUtf16Encode(int charNum, unsigned short *buf) -{ +XmlUtf16Encode(int charNum, unsigned short *buf) { if (charNum < 0) return 0; if (charNum < 0x10000) { @@ -1252,17 +1314,15 @@ struct unknown_encoding { char utf8[256][4]; }; -#define AS_UNKNOWN_ENCODING(enc) ((const struct unknown_encoding *) (enc)) +#define AS_UNKNOWN_ENCODING(enc) ((const struct unknown_encoding *)(enc)) int -XmlSizeOfUnknownEncoding(void) -{ +XmlSizeOfUnknownEncoding(void) { return sizeof(struct unknown_encoding); } static int PTRFASTCALL -unknown_isName(const ENCODING *enc, const char *p) -{ +unknown_isName(const ENCODING *enc, const char *p) { const struct unknown_encoding *uenc = AS_UNKNOWN_ENCODING(enc); int c = uenc->convert(uenc->userData, p); if (c & ~0xFFFF) @@ -1271,8 +1331,7 @@ unknown_isName(const ENCODING *enc, const char *p) } static int PTRFASTCALL -unknown_isNmstrt(const ENCODING *enc, const char *p) -{ +unknown_isNmstrt(const ENCODING *enc, const char *p) { const struct unknown_encoding *uenc = AS_UNKNOWN_ENCODING(enc); int c = uenc->convert(uenc->userData, p); if (c & ~0xFFFF) @@ -1281,81 +1340,72 @@ unknown_isNmstrt(const ENCODING *enc, const char *p) } static int PTRFASTCALL -unknown_isInvalid(const ENCODING *enc, const char *p) -{ +unknown_isInvalid(const ENCODING *enc, const char *p) { const struct unknown_encoding *uenc = AS_UNKNOWN_ENCODING(enc); int c = uenc->convert(uenc->userData, p); return (c & ~0xFFFF) || checkCharRefNumber(c) < 0; } -static void PTRCALL -unknown_toUtf8(const ENCODING *enc, - const char **fromP, const char *fromLim, - char **toP, const char *toLim) -{ +static enum XML_Convert_Result PTRCALL +unknown_toUtf8(const ENCODING *enc, const char **fromP, const char *fromLim, + char **toP, const char *toLim) { const struct unknown_encoding *uenc = AS_UNKNOWN_ENCODING(enc); char buf[XML_UTF8_ENCODE_MAX]; for (;;) { const char *utf8; int n; if (*fromP == fromLim) - break; + return XML_CONVERT_COMPLETED; utf8 = uenc->utf8[(unsigned char)**fromP]; n = *utf8++; if (n == 0) { int c = uenc->convert(uenc->userData, *fromP); n = XmlUtf8Encode(c, buf); if (n > toLim - *toP) - break; + return XML_CONVERT_OUTPUT_EXHAUSTED; utf8 = buf; *fromP += (AS_NORMAL_ENCODING(enc)->type[(unsigned char)**fromP] - (BT_LEAD2 - 2)); - } - else { + } else { if (n > toLim - *toP) - break; + return XML_CONVERT_OUTPUT_EXHAUSTED; (*fromP)++; } - do { - *(*toP)++ = *utf8++; - } while (--n != 0); + memcpy(*toP, utf8, n); + *toP += n; } } -static void PTRCALL -unknown_toUtf16(const ENCODING *enc, - const char **fromP, const char *fromLim, - unsigned short **toP, const unsigned short *toLim) -{ +static enum XML_Convert_Result PTRCALL +unknown_toUtf16(const ENCODING *enc, const char **fromP, const char *fromLim, + unsigned short **toP, const unsigned short *toLim) { const struct unknown_encoding *uenc = AS_UNKNOWN_ENCODING(enc); - while (*fromP != fromLim && *toP != toLim) { + while (*fromP < fromLim && *toP < toLim) { unsigned short c = uenc->utf16[(unsigned char)**fromP]; if (c == 0) { - c = (unsigned short) - uenc->convert(uenc->userData, *fromP); + c = (unsigned short)uenc->convert(uenc->userData, *fromP); *fromP += (AS_NORMAL_ENCODING(enc)->type[(unsigned char)**fromP] - (BT_LEAD2 - 2)); - } - else + } else (*fromP)++; *(*toP)++ = c; } + + if ((*toP == toLim) && (*fromP < fromLim)) + return XML_CONVERT_OUTPUT_EXHAUSTED; + else + return XML_CONVERT_COMPLETED; } ENCODING * -XmlInitUnknownEncoding(void *mem, - int *table, - CONVERTER convert, - void *userData) -{ +XmlInitUnknownEncoding(void *mem, const int *table, CONVERTER convert, + void *userData) { int i; struct unknown_encoding *e = (struct unknown_encoding *)mem; - for (i = 0; i < (int)sizeof(struct normal_encoding); i++) - ((char *)mem)[i] = ((char *)&latin1_encoding)[i]; + memcpy(mem, &latin1_encoding, sizeof(struct normal_encoding)); for (i = 0; i < 128; i++) if (latin1_encoding.type[i] != BT_OTHER - && latin1_encoding.type[i] != BT_NONXML - && table[i] != i) + && latin1_encoding.type[i] != BT_NONXML && table[i] != i) return 0; for (i = 0; i < 256; i++) { int c = table[i]; @@ -1365,32 +1415,30 @@ XmlInitUnknownEncoding(void *mem, e->utf16[i] = 0xFFFF; e->utf8[i][0] = 1; e->utf8[i][1] = 0; - } - else if (c < 0) { + } else if (c < 0) { if (c < -4) return 0; + /* Multi-byte sequences need a converter function */ + if (! convert) + return 0; e->normal.type[i] = (unsigned char)(BT_LEAD2 - (c + 2)); e->utf8[i][0] = 0; e->utf16[i] = 0; - } - else if (c < 0x80) { + } else if (c < 0x80) { if (latin1_encoding.type[c] != BT_OTHER - && latin1_encoding.type[c] != BT_NONXML - && c != i) + && latin1_encoding.type[c] != BT_NONXML && c != i) return 0; e->normal.type[i] = latin1_encoding.type[c]; e->utf8[i][0] = 1; e->utf8[i][1] = (char)c; e->utf16[i] = (unsigned short)(c == 0 ? 0xFFFF : c); - } - else if (checkCharRefNumber(c) < 0) { + } else if (checkCharRefNumber(c) < 0) { e->normal.type[i] = BT_NONXML; /* This shouldn't really get used. */ e->utf16[i] = 0xFFFF; e->utf8[i][0] = 1; e->utf8[i][1] = 0; - } - else { + } else { if (c > 0xFFFF) return 0; if (UCS2_GET_NAMING(nmstrtPages, c >> 8, c & 0xff)) @@ -1435,44 +1483,32 @@ enum { NO_ENC }; -static const char KW_ISO_8859_1[] = { - ASCII_I, ASCII_S, ASCII_O, ASCII_MINUS, ASCII_8, ASCII_8, ASCII_5, ASCII_9, - ASCII_MINUS, ASCII_1, '\0' -}; -static const char KW_US_ASCII[] = { - ASCII_U, ASCII_S, ASCII_MINUS, ASCII_A, ASCII_S, ASCII_C, ASCII_I, ASCII_I, - '\0' -}; -static const char KW_UTF_8[] = { - ASCII_U, ASCII_T, ASCII_F, ASCII_MINUS, ASCII_8, '\0' -}; -static const char KW_UTF_16[] = { - ASCII_U, ASCII_T, ASCII_F, ASCII_MINUS, ASCII_1, ASCII_6, '\0' -}; -static const char KW_UTF_16BE[] = { - ASCII_U, ASCII_T, ASCII_F, ASCII_MINUS, ASCII_1, ASCII_6, ASCII_B, ASCII_E, - '\0' -}; -static const char KW_UTF_16LE[] = { - ASCII_U, ASCII_T, ASCII_F, ASCII_MINUS, ASCII_1, ASCII_6, ASCII_L, ASCII_E, - '\0' -}; +static const char KW_ISO_8859_1[] + = {ASCII_I, ASCII_S, ASCII_O, ASCII_MINUS, ASCII_8, ASCII_8, + ASCII_5, ASCII_9, ASCII_MINUS, ASCII_1, '\0'}; +static const char KW_US_ASCII[] + = {ASCII_U, ASCII_S, ASCII_MINUS, ASCII_A, ASCII_S, + ASCII_C, ASCII_I, ASCII_I, '\0'}; +static const char KW_UTF_8[] + = {ASCII_U, ASCII_T, ASCII_F, ASCII_MINUS, ASCII_8, '\0'}; +static const char KW_UTF_16[] + = {ASCII_U, ASCII_T, ASCII_F, ASCII_MINUS, ASCII_1, ASCII_6, '\0'}; +static const char KW_UTF_16BE[] + = {ASCII_U, ASCII_T, ASCII_F, ASCII_MINUS, ASCII_1, + ASCII_6, ASCII_B, ASCII_E, '\0'}; +static const char KW_UTF_16LE[] + = {ASCII_U, ASCII_T, ASCII_F, ASCII_MINUS, ASCII_1, + ASCII_6, ASCII_L, ASCII_E, '\0'}; static int FASTCALL -getEncodingIndex(const char *name) -{ - static const char * const encodingNames[] = { - KW_ISO_8859_1, - KW_US_ASCII, - KW_UTF_8, - KW_UTF_16, - KW_UTF_16BE, - KW_UTF_16LE, +getEncodingIndex(const char *name) { + static const char *const encodingNames[] = { + KW_ISO_8859_1, KW_US_ASCII, KW_UTF_8, KW_UTF_16, KW_UTF_16BE, KW_UTF_16LE, }; int i; if (name == NULL) return NO_ENC; - for (i = 0; i < (int)(sizeof(encodingNames)/sizeof(encodingNames[0])); i++) + for (i = 0; i < (int)(sizeof(encodingNames) / sizeof(encodingNames[0])); i++) if (streqci(name, encodingNames[i])) return i; return UNKNOWN_ENC; @@ -1492,18 +1528,12 @@ getEncodingIndex(const char *name) XML_PROLOG_STATE otherwise. */ - static int -initScan(const ENCODING * const *encodingTable, - const INIT_ENCODING *enc, - int state, - const char *ptr, - const char *end, - const char **nextTokPtr) -{ +initScan(const ENCODING *const *encodingTable, const INIT_ENCODING *enc, + int state, const char *ptr, const char *end, const char **nextTokPtr) { const ENCODING **encPtr; - if (ptr == end) + if (ptr >= end) return XML_TOK_NONE; encPtr = enc->encPtr; if (ptr + 1 == end) { @@ -1525,20 +1555,17 @@ initScan(const ENCODING * const *encodingTable, case 0xFE: case 0xFF: case 0xEF: /* possibly first byte of UTF-8 BOM */ - if (INIT_ENC_INDEX(enc) == ISO_8859_1_ENC - && state == XML_CONTENT_STATE) + if (INIT_ENC_INDEX(enc) == ISO_8859_1_ENC && state == XML_CONTENT_STATE) break; /* fall through */ case 0x00: case 0x3C: return XML_TOK_PARTIAL; } - } - else { + } else { switch (((unsigned char)ptr[0] << 8) | (unsigned char)ptr[1]) { case 0xFEFF: - if (INIT_ENC_INDEX(enc) == ISO_8859_1_ENC - && state == XML_CONTENT_STATE) + if (INIT_ENC_INDEX(enc) == ISO_8859_1_ENC && state == XML_CONTENT_STATE) break; *nextTokPtr = ptr + 2; *encPtr = encodingTable[UTF_16BE_ENC]; @@ -1552,8 +1579,7 @@ initScan(const ENCODING * const *encodingTable, *encPtr = encodingTable[UTF_16LE_ENC]; return XmlTok(*encPtr, state, ptr, end, nextTokPtr); case 0xFFFE: - if (INIT_ENC_INDEX(enc) == ISO_8859_1_ENC - && state == XML_CONTENT_STATE) + if (INIT_ENC_INDEX(enc) == ISO_8859_1_ENC && state == XML_CONTENT_STATE) break; *nextTokPtr = ptr + 2; *encPtr = encodingTable[UTF_16LE_ENC]; @@ -1568,8 +1594,8 @@ initScan(const ENCODING * const *encodingTable, */ if (state == XML_CONTENT_STATE) { int e = INIT_ENC_INDEX(enc); - if (e == ISO_8859_1_ENC || e == UTF_16BE_ENC - || e == UTF_16LE_ENC || e == UTF_16_ENC) + if (e == ISO_8859_1_ENC || e == UTF_16BE_ENC || e == UTF_16LE_ENC + || e == UTF_16_ENC) break; } if (ptr + 2 == end) @@ -1592,8 +1618,7 @@ initScan(const ENCODING * const *encodingTable, break; *encPtr = encodingTable[UTF_16BE_ENC]; return XmlTok(*encPtr, state, ptr, end, nextTokPtr); - } - else if (ptr[1] == '\0') { + } else if (ptr[1] == '\0') { /* We could recover here in the case: - parsing an external entity - second byte is 0 @@ -1615,7 +1640,6 @@ initScan(const ENCODING * const *encodingTable, return XmlTok(*encPtr, state, ptr, end, nextTokPtr); } - #define NS(x) x #define ns(x) x #define XML_TOK_NS_C @@ -1626,22 +1650,19 @@ initScan(const ENCODING * const *encodingTable, #ifdef XML_NS -#define NS(x) x ## NS -#define ns(x) x ## _ns +# define NS(x) x##NS +# define ns(x) x##_ns -#define XML_TOK_NS_C -#include "xmltok_ns.c" -#undef XML_TOK_NS_C +# define XML_TOK_NS_C +# include "xmltok_ns.c" +# undef XML_TOK_NS_C -#undef NS -#undef ns +# undef NS +# undef ns ENCODING * -XmlInitUnknownEncodingNS(void *mem, - int *table, - CONVERTER convert, - void *userData) -{ +XmlInitUnknownEncodingNS(void *mem, const int *table, CONVERTER convert, + void *userData) { ENCODING *enc = XmlInitUnknownEncoding(mem, table, convert, userData); if (enc) ((struct normal_encoding *)enc)->type[ASCII_COLON] = BT_COLON; diff --git a/cextern/expat/lib/xmltok.h b/cextern/expat/lib/xmltok.h old mode 100755 new mode 100644 index ca867aa6b429..79a9fb76871f --- a/cextern/expat/lib/xmltok.h +++ b/cextern/expat/lib/xmltok.h @@ -1,113 +1,147 @@ -/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd - See the file COPYING for copying permission. +/* + __ __ _ + ___\ \/ /_ __ __ _| |_ + / _ \\ /| '_ \ / _` | __| + | __// \| |_) | (_| | |_ + \___/_/\_\ .__/ \__,_|\__| + |_| XML parser + + Copyright (c) 1997-2000 Thai Open Source Software Center Ltd + Copyright (c) 2000 Clark Cooper + Copyright (c) 2002 Fred L. Drake, Jr. + Copyright (c) 2002-2005 Karl Waclawek + Copyright (c) 2016-2024 Sebastian Pipping + Copyright (c) 2017 Rhodri James + Licensed under the MIT license: + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to permit + persons to whom the Software is furnished to do so, subject to the + following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef XmlTok_INCLUDED -#define XmlTok_INCLUDED 1 +# define XmlTok_INCLUDED 1 -#ifdef __cplusplus +# ifdef __cplusplus extern "C" { -#endif +# endif /* The following token may be returned by XmlContentTok */ -#define XML_TOK_TRAILING_RSQB -5 /* ] or ]] at the end of the scan; might be - start of illegal ]]> sequence */ +# define XML_TOK_TRAILING_RSQB \ + -5 /* ] or ]] at the end of the scan; might be \ + start of illegal ]]> sequence */ /* The following tokens may be returned by both XmlPrologTok and XmlContentTok. */ -#define XML_TOK_NONE -4 /* The string to be scanned is empty */ -#define XML_TOK_TRAILING_CR -3 /* A CR at the end of the scan; - might be part of CRLF sequence */ -#define XML_TOK_PARTIAL_CHAR -2 /* only part of a multibyte sequence */ -#define XML_TOK_PARTIAL -1 /* only part of a token */ -#define XML_TOK_INVALID 0 +# define XML_TOK_NONE -4 /* The string to be scanned is empty */ +# define XML_TOK_TRAILING_CR \ + -3 /* A CR at the end of the scan; \ + might be part of CRLF sequence */ +# define XML_TOK_PARTIAL_CHAR -2 /* only part of a multibyte sequence */ +# define XML_TOK_PARTIAL -1 /* only part of a token */ +# define XML_TOK_INVALID 0 /* The following tokens are returned by XmlContentTok; some are also returned by XmlAttributeValueTok, XmlEntityTok, XmlCdataSectionTok. */ -#define XML_TOK_START_TAG_WITH_ATTS 1 -#define XML_TOK_START_TAG_NO_ATTS 2 -#define XML_TOK_EMPTY_ELEMENT_WITH_ATTS 3 /* empty element tag */ -#define XML_TOK_EMPTY_ELEMENT_NO_ATTS 4 -#define XML_TOK_END_TAG 5 -#define XML_TOK_DATA_CHARS 6 -#define XML_TOK_DATA_NEWLINE 7 -#define XML_TOK_CDATA_SECT_OPEN 8 -#define XML_TOK_ENTITY_REF 9 -#define XML_TOK_CHAR_REF 10 /* numeric character reference */ +# define XML_TOK_START_TAG_WITH_ATTS 1 +# define XML_TOK_START_TAG_NO_ATTS 2 +# define XML_TOK_EMPTY_ELEMENT_WITH_ATTS 3 /* empty element tag */ +# define XML_TOK_EMPTY_ELEMENT_NO_ATTS 4 +# define XML_TOK_END_TAG 5 +# define XML_TOK_DATA_CHARS 6 +# define XML_TOK_DATA_NEWLINE 7 +# define XML_TOK_CDATA_SECT_OPEN 8 +# define XML_TOK_ENTITY_REF 9 +# define XML_TOK_CHAR_REF 10 /* numeric character reference */ /* The following tokens may be returned by both XmlPrologTok and XmlContentTok. */ -#define XML_TOK_PI 11 /* processing instruction */ -#define XML_TOK_XML_DECL 12 /* XML decl or text decl */ -#define XML_TOK_COMMENT 13 -#define XML_TOK_BOM 14 /* Byte order mark */ +# define XML_TOK_PI 11 /* processing instruction */ +# define XML_TOK_XML_DECL 12 /* XML decl or text decl */ +# define XML_TOK_COMMENT 13 +# define XML_TOK_BOM 14 /* Byte order mark */ /* The following tokens are returned only by XmlPrologTok */ -#define XML_TOK_PROLOG_S 15 -#define XML_TOK_DECL_OPEN 16 /* */ -#define XML_TOK_NAME 18 -#define XML_TOK_NMTOKEN 19 -#define XML_TOK_POUND_NAME 20 /* #name */ -#define XML_TOK_OR 21 /* | */ -#define XML_TOK_PERCENT 22 -#define XML_TOK_OPEN_PAREN 23 -#define XML_TOK_CLOSE_PAREN 24 -#define XML_TOK_OPEN_BRACKET 25 -#define XML_TOK_CLOSE_BRACKET 26 -#define XML_TOK_LITERAL 27 -#define XML_TOK_PARAM_ENTITY_REF 28 -#define XML_TOK_INSTANCE_START 29 +# define XML_TOK_PROLOG_S 15 +# define XML_TOK_DECL_OPEN 16 /* */ +# define XML_TOK_NAME 18 +# define XML_TOK_NMTOKEN 19 +# define XML_TOK_POUND_NAME 20 /* #name */ +# define XML_TOK_OR 21 /* | */ +# define XML_TOK_PERCENT 22 +# define XML_TOK_OPEN_PAREN 23 +# define XML_TOK_CLOSE_PAREN 24 +# define XML_TOK_OPEN_BRACKET 25 +# define XML_TOK_CLOSE_BRACKET 26 +# define XML_TOK_LITERAL 27 +# define XML_TOK_PARAM_ENTITY_REF 28 +# define XML_TOK_INSTANCE_START 29 /* The following occur only in element type declarations */ -#define XML_TOK_NAME_QUESTION 30 /* name? */ -#define XML_TOK_NAME_ASTERISK 31 /* name* */ -#define XML_TOK_NAME_PLUS 32 /* name+ */ -#define XML_TOK_COND_SECT_OPEN 33 /* */ -#define XML_TOK_CLOSE_PAREN_QUESTION 35 /* )? */ -#define XML_TOK_CLOSE_PAREN_ASTERISK 36 /* )* */ -#define XML_TOK_CLOSE_PAREN_PLUS 37 /* )+ */ -#define XML_TOK_COMMA 38 +# define XML_TOK_NAME_QUESTION 30 /* name? */ +# define XML_TOK_NAME_ASTERISK 31 /* name* */ +# define XML_TOK_NAME_PLUS 32 /* name+ */ +# define XML_TOK_COND_SECT_OPEN 33 /* */ +# define XML_TOK_CLOSE_PAREN_QUESTION 35 /* )? */ +# define XML_TOK_CLOSE_PAREN_ASTERISK 36 /* )* */ +# define XML_TOK_CLOSE_PAREN_PLUS 37 /* )+ */ +# define XML_TOK_COMMA 38 /* The following token is returned only by XmlAttributeValueTok */ -#define XML_TOK_ATTRIBUTE_VALUE_S 39 +# define XML_TOK_ATTRIBUTE_VALUE_S 39 /* The following token is returned only by XmlCdataSectionTok */ -#define XML_TOK_CDATA_SECT_CLOSE 40 +# define XML_TOK_CDATA_SECT_CLOSE 40 /* With namespace processing this is returned by XmlPrologTok for a name with a colon. */ -#define XML_TOK_PREFIXED_NAME 41 +# define XML_TOK_PREFIXED_NAME 41 -#ifdef XML_DTD -#define XML_TOK_IGNORE_SECT 42 -#endif /* XML_DTD */ +# ifdef XML_DTD +# define XML_TOK_IGNORE_SECT 42 +# endif /* XML_DTD */ -#ifdef XML_DTD -#define XML_N_STATES 4 -#else /* not XML_DTD */ -#define XML_N_STATES 3 -#endif /* not XML_DTD */ +# ifdef XML_DTD +# define XML_N_STATES 4 +# else /* not XML_DTD */ +# define XML_N_STATES 3 +# endif /* not XML_DTD */ -#define XML_PROLOG_STATE 0 -#define XML_CONTENT_STATE 1 -#define XML_CDATA_SECTION_STATE 2 -#ifdef XML_DTD -#define XML_IGNORE_SECTION_STATE 3 -#endif /* XML_DTD */ +# define XML_PROLOG_STATE 0 +# define XML_CONTENT_STATE 1 +# define XML_CDATA_SECTION_STATE 2 +# ifdef XML_DTD +# define XML_IGNORE_SECTION_STATE 3 +# endif /* XML_DTD */ -#define XML_N_LITERAL_TYPES 2 -#define XML_ATTRIBUTE_VALUE_LITERAL 0 -#define XML_ENTITY_VALUE_LITERAL 1 +# define XML_N_LITERAL_TYPES 2 +# define XML_ATTRIBUTE_VALUE_LITERAL 0 +# define XML_ENTITY_VALUE_LITERAL 1 /* The size of the buffer passed to XmlUtf8Encode must be at least this. */ -#define XML_UTF8_ENCODE_MAX 4 +# define XML_UTF8_ENCODE_MAX 4 /* The size of the buffer passed to XmlUtf16Encode must be at least this. */ -#define XML_UTF16_ENCODE_MAX 2 +# define XML_UTF16_ENCODE_MAX 2 typedef struct position { /* first line and first column are 0 not 1 */ @@ -125,49 +159,41 @@ typedef struct { struct encoding; typedef struct encoding ENCODING; -typedef int (PTRCALL *SCANNER)(const ENCODING *, - const char *, - const char *, - const char **); +typedef int(PTRCALL *SCANNER)(const ENCODING *, const char *, const char *, + const char **); + +enum XML_Convert_Result { + XML_CONVERT_COMPLETED = 0, + XML_CONVERT_INPUT_INCOMPLETE = 1, + XML_CONVERT_OUTPUT_EXHAUSTED + = 2 /* and therefore potentially input remaining as well */ +}; struct encoding { SCANNER scanners[XML_N_STATES]; SCANNER literalScanners[XML_N_LITERAL_TYPES]; - int (PTRCALL *sameName)(const ENCODING *, - const char *, - const char *); - int (PTRCALL *nameMatchesAscii)(const ENCODING *, - const char *, - const char *, - const char *); - int (PTRFASTCALL *nameLength)(const ENCODING *, const char *); + int(PTRCALL *nameMatchesAscii)(const ENCODING *, const char *, const char *, + const char *); + int(PTRFASTCALL *nameLength)(const ENCODING *, const char *); const char *(PTRFASTCALL *skipS)(const ENCODING *, const char *); - int (PTRCALL *getAtts)(const ENCODING *enc, - const char *ptr, - int attsMax, - ATTRIBUTE *atts); - int (PTRFASTCALL *charRefNumber)(const ENCODING *enc, const char *ptr); - int (PTRCALL *predefinedEntityName)(const ENCODING *, - const char *, - const char *); - void (PTRCALL *updatePosition)(const ENCODING *, - const char *ptr, - const char *end, - POSITION *); - int (PTRCALL *isPublicId)(const ENCODING *enc, - const char *ptr, - const char *end, - const char **badPtr); - void (PTRCALL *utf8Convert)(const ENCODING *enc, - const char **fromP, - const char *fromLim, - char **toP, - const char *toLim); - void (PTRCALL *utf16Convert)(const ENCODING *enc, - const char **fromP, - const char *fromLim, - unsigned short **toP, - const unsigned short *toLim); + int(PTRCALL *getAtts)(const ENCODING *enc, const char *ptr, int attsMax, + ATTRIBUTE *atts); + int(PTRFASTCALL *charRefNumber)(const ENCODING *enc, const char *ptr); + int(PTRCALL *predefinedEntityName)(const ENCODING *, const char *, + const char *); + void(PTRCALL *updatePosition)(const ENCODING *, const char *ptr, + const char *end, POSITION *); + int(PTRCALL *isPublicId)(const ENCODING *enc, const char *ptr, + const char *end, const char **badPtr); + enum XML_Convert_Result(PTRCALL *utf8Convert)(const ENCODING *enc, + const char **fromP, + const char *fromLim, char **toP, + const char *toLim); + enum XML_Convert_Result(PTRCALL *utf16Convert)(const ENCODING *enc, + const char **fromP, + const char *fromLim, + unsigned short **toP, + const unsigned short *toLim); int minBytesPerChar; char isUtf8; char isUtf16; @@ -194,123 +220,102 @@ struct encoding { the prolog outside literals, comments and processing instructions. */ +# define XmlTok(enc, state, ptr, end, nextTokPtr) \ + (((enc)->scanners[state])(enc, ptr, end, nextTokPtr)) -#define XmlTok(enc, state, ptr, end, nextTokPtr) \ - (((enc)->scanners[state])(enc, ptr, end, nextTokPtr)) - -#define XmlPrologTok(enc, ptr, end, nextTokPtr) \ - XmlTok(enc, XML_PROLOG_STATE, ptr, end, nextTokPtr) +# define XmlPrologTok(enc, ptr, end, nextTokPtr) \ + XmlTok(enc, XML_PROLOG_STATE, ptr, end, nextTokPtr) -#define XmlContentTok(enc, ptr, end, nextTokPtr) \ - XmlTok(enc, XML_CONTENT_STATE, ptr, end, nextTokPtr) +# define XmlContentTok(enc, ptr, end, nextTokPtr) \ + XmlTok(enc, XML_CONTENT_STATE, ptr, end, nextTokPtr) -#define XmlCdataSectionTok(enc, ptr, end, nextTokPtr) \ - XmlTok(enc, XML_CDATA_SECTION_STATE, ptr, end, nextTokPtr) +# define XmlCdataSectionTok(enc, ptr, end, nextTokPtr) \ + XmlTok(enc, XML_CDATA_SECTION_STATE, ptr, end, nextTokPtr) -#ifdef XML_DTD +# ifdef XML_DTD -#define XmlIgnoreSectionTok(enc, ptr, end, nextTokPtr) \ - XmlTok(enc, XML_IGNORE_SECTION_STATE, ptr, end, nextTokPtr) +# define XmlIgnoreSectionTok(enc, ptr, end, nextTokPtr) \ + XmlTok(enc, XML_IGNORE_SECTION_STATE, ptr, end, nextTokPtr) -#endif /* XML_DTD */ +# endif /* XML_DTD */ /* This is used for performing a 2nd-level tokenization on the content of a literal that has already been returned by XmlTok. */ -#define XmlLiteralTok(enc, literalType, ptr, end, nextTokPtr) \ - (((enc)->literalScanners[literalType])(enc, ptr, end, nextTokPtr)) +# define XmlLiteralTok(enc, literalType, ptr, end, nextTokPtr) \ + (((enc)->literalScanners[literalType])(enc, ptr, end, nextTokPtr)) -#define XmlAttributeValueTok(enc, ptr, end, nextTokPtr) \ - XmlLiteralTok(enc, XML_ATTRIBUTE_VALUE_LITERAL, ptr, end, nextTokPtr) +# define XmlAttributeValueTok(enc, ptr, end, nextTokPtr) \ + XmlLiteralTok(enc, XML_ATTRIBUTE_VALUE_LITERAL, ptr, end, nextTokPtr) -#define XmlEntityValueTok(enc, ptr, end, nextTokPtr) \ - XmlLiteralTok(enc, XML_ENTITY_VALUE_LITERAL, ptr, end, nextTokPtr) +# define XmlEntityValueTok(enc, ptr, end, nextTokPtr) \ + XmlLiteralTok(enc, XML_ENTITY_VALUE_LITERAL, ptr, end, nextTokPtr) -#define XmlSameName(enc, ptr1, ptr2) (((enc)->sameName)(enc, ptr1, ptr2)) +# define XmlNameMatchesAscii(enc, ptr1, end1, ptr2) \ + (((enc)->nameMatchesAscii)(enc, ptr1, end1, ptr2)) -#define XmlNameMatchesAscii(enc, ptr1, end1, ptr2) \ - (((enc)->nameMatchesAscii)(enc, ptr1, end1, ptr2)) +# define XmlNameLength(enc, ptr) (((enc)->nameLength)(enc, ptr)) -#define XmlNameLength(enc, ptr) \ - (((enc)->nameLength)(enc, ptr)) +# define XmlSkipS(enc, ptr) (((enc)->skipS)(enc, ptr)) -#define XmlSkipS(enc, ptr) \ - (((enc)->skipS)(enc, ptr)) +# define XmlGetAttributes(enc, ptr, attsMax, atts) \ + (((enc)->getAtts)(enc, ptr, attsMax, atts)) -#define XmlGetAttributes(enc, ptr, attsMax, atts) \ - (((enc)->getAtts)(enc, ptr, attsMax, atts)) +# define XmlCharRefNumber(enc, ptr) (((enc)->charRefNumber)(enc, ptr)) -#define XmlCharRefNumber(enc, ptr) \ - (((enc)->charRefNumber)(enc, ptr)) +# define XmlPredefinedEntityName(enc, ptr, end) \ + (((enc)->predefinedEntityName)(enc, ptr, end)) -#define XmlPredefinedEntityName(enc, ptr, end) \ - (((enc)->predefinedEntityName)(enc, ptr, end)) +# define XmlUpdatePosition(enc, ptr, end, pos) \ + (((enc)->updatePosition)(enc, ptr, end, pos)) -#define XmlUpdatePosition(enc, ptr, end, pos) \ - (((enc)->updatePosition)(enc, ptr, end, pos)) +# define XmlIsPublicId(enc, ptr, end, badPtr) \ + (((enc)->isPublicId)(enc, ptr, end, badPtr)) -#define XmlIsPublicId(enc, ptr, end, badPtr) \ - (((enc)->isPublicId)(enc, ptr, end, badPtr)) +# define XmlUtf8Convert(enc, fromP, fromLim, toP, toLim) \ + (((enc)->utf8Convert)(enc, fromP, fromLim, toP, toLim)) -#define XmlUtf8Convert(enc, fromP, fromLim, toP, toLim) \ - (((enc)->utf8Convert)(enc, fromP, fromLim, toP, toLim)) - -#define XmlUtf16Convert(enc, fromP, fromLim, toP, toLim) \ - (((enc)->utf16Convert)(enc, fromP, fromLim, toP, toLim)) +# define XmlUtf16Convert(enc, fromP, fromLim, toP, toLim) \ + (((enc)->utf16Convert)(enc, fromP, fromLim, toP, toLim)) typedef struct { ENCODING initEnc; const ENCODING **encPtr; } INIT_ENCODING; -int XmlParseXmlDecl(int isGeneralTextEntity, - const ENCODING *enc, - const char *ptr, - const char *end, - const char **badPtr, - const char **versionPtr, - const char **versionEndPtr, +int XmlParseXmlDecl(int isGeneralTextEntity, const ENCODING *enc, + const char *ptr, const char *end, const char **badPtr, + const char **versionPtr, const char **versionEndPtr, const char **encodingNamePtr, - const ENCODING **namedEncodingPtr, - int *standalonePtr); + const ENCODING **namedEncodingPtr, int *standalonePtr); -int XmlInitEncoding(INIT_ENCODING *, const ENCODING **, const char *name); +int XmlInitEncoding(INIT_ENCODING *p, const ENCODING **encPtr, + const char *name); const ENCODING *XmlGetUtf8InternalEncoding(void); const ENCODING *XmlGetUtf16InternalEncoding(void); int FASTCALL XmlUtf8Encode(int charNumber, char *buf); int FASTCALL XmlUtf16Encode(int charNumber, unsigned short *buf); int XmlSizeOfUnknownEncoding(void); +typedef int(XMLCALL *CONVERTER)(void *userData, const char *p); -typedef int (XMLCALL *CONVERTER) (void *userData, const char *p); - -ENCODING * -XmlInitUnknownEncoding(void *mem, - int *table, - CONVERTER convert, - void *userData); +ENCODING *XmlInitUnknownEncoding(void *mem, const int *table, CONVERTER convert, + void *userData); -int XmlParseXmlDeclNS(int isGeneralTextEntity, - const ENCODING *enc, - const char *ptr, - const char *end, - const char **badPtr, - const char **versionPtr, - const char **versionEndPtr, +int XmlParseXmlDeclNS(int isGeneralTextEntity, const ENCODING *enc, + const char *ptr, const char *end, const char **badPtr, + const char **versionPtr, const char **versionEndPtr, const char **encodingNamePtr, - const ENCODING **namedEncodingPtr, - int *standalonePtr); + const ENCODING **namedEncodingPtr, int *standalonePtr); -int XmlInitEncodingNS(INIT_ENCODING *, const ENCODING **, const char *name); +int XmlInitEncodingNS(INIT_ENCODING *p, const ENCODING **encPtr, + const char *name); const ENCODING *XmlGetUtf8InternalEncodingNS(void); const ENCODING *XmlGetUtf16InternalEncodingNS(void); -ENCODING * -XmlInitUnknownEncodingNS(void *mem, - int *table, - CONVERTER convert, - void *userData); -#ifdef __cplusplus +ENCODING *XmlInitUnknownEncodingNS(void *mem, const int *table, + CONVERTER convert, void *userData); +# ifdef __cplusplus } -#endif +# endif #endif /* not XmlTok_INCLUDED */ diff --git a/cextern/expat/lib/xmltok_impl.c b/cextern/expat/lib/xmltok_impl.c old mode 100755 new mode 100644 index 126881925222..239a2d06c451 --- a/cextern/expat/lib/xmltok_impl.c +++ b/cextern/expat/lib/xmltok_impl.c @@ -1,114 +1,165 @@ -/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd - See the file COPYING for copying permission. +/* This file is included (from xmltok.c, 1-3 times depending on XML_MIN_SIZE)! + __ __ _ + ___\ \/ /_ __ __ _| |_ + / _ \\ /| '_ \ / _` | __| + | __// \| |_) | (_| | |_ + \___/_/\_\ .__/ \__,_|\__| + |_| XML parser + + Copyright (c) 1997-2000 Thai Open Source Software Center Ltd + Copyright (c) 2000 Clark Cooper + Copyright (c) 2002 Fred L. Drake, Jr. + Copyright (c) 2002-2016 Karl Waclawek + Copyright (c) 2016-2022 Sebastian Pipping + Copyright (c) 2017 Rhodri James + Copyright (c) 2018 Benjamin Peterson + Copyright (c) 2018 Anton Maklakov + Copyright (c) 2019 David Loffredo + Copyright (c) 2020 Boris Kolpackov + Copyright (c) 2022 Martin Ettl + Licensed under the MIT license: + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to permit + persons to whom the Software is furnished to do so, subject to the + following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/* This file is included! */ #ifdef XML_TOK_IMPL_C -#ifndef IS_INVALID_CHAR -#define IS_INVALID_CHAR(enc, ptr, n) (0) -#endif +# ifndef IS_INVALID_CHAR // i.e. for UTF-16 and XML_MIN_SIZE not defined +# define IS_INVALID_CHAR(enc, ptr, n) (0) +# endif -#define INVALID_LEAD_CASE(n, ptr, nextTokPtr) \ - case BT_LEAD ## n: \ - if (end - ptr < n) \ - return XML_TOK_PARTIAL_CHAR; \ - if (IS_INVALID_CHAR(enc, ptr, n)) { \ - *(nextTokPtr) = (ptr); \ - return XML_TOK_INVALID; \ - } \ - ptr += n; \ - break; +# define INVALID_LEAD_CASE(n, ptr, nextTokPtr) \ + case BT_LEAD##n: \ + if (end - ptr < n) \ + return XML_TOK_PARTIAL_CHAR; \ + if (IS_INVALID_CHAR(enc, ptr, n)) { \ + *(nextTokPtr) = (ptr); \ + return XML_TOK_INVALID; \ + } \ + ptr += n; \ + break; -#define INVALID_CASES(ptr, nextTokPtr) \ - INVALID_LEAD_CASE(2, ptr, nextTokPtr) \ - INVALID_LEAD_CASE(3, ptr, nextTokPtr) \ - INVALID_LEAD_CASE(4, ptr, nextTokPtr) \ - case BT_NONXML: \ - case BT_MALFORM: \ - case BT_TRAIL: \ - *(nextTokPtr) = (ptr); \ +# define INVALID_CASES(ptr, nextTokPtr) \ + INVALID_LEAD_CASE(2, ptr, nextTokPtr) \ + INVALID_LEAD_CASE(3, ptr, nextTokPtr) \ + INVALID_LEAD_CASE(4, ptr, nextTokPtr) \ + case BT_NONXML: \ + case BT_MALFORM: \ + case BT_TRAIL: \ + *(nextTokPtr) = (ptr); \ return XML_TOK_INVALID; -#define CHECK_NAME_CASE(n, enc, ptr, end, nextTokPtr) \ - case BT_LEAD ## n: \ - if (end - ptr < n) \ - return XML_TOK_PARTIAL_CHAR; \ - if (!IS_NAME_CHAR(enc, ptr, n)) { \ - *nextTokPtr = ptr; \ - return XML_TOK_INVALID; \ - } \ - ptr += n; \ - break; +# define CHECK_NAME_CASE(n, enc, ptr, end, nextTokPtr) \ + case BT_LEAD##n: \ + if (end - ptr < n) \ + return XML_TOK_PARTIAL_CHAR; \ + if (IS_INVALID_CHAR(enc, ptr, n) || ! IS_NAME_CHAR(enc, ptr, n)) { \ + *nextTokPtr = ptr; \ + return XML_TOK_INVALID; \ + } \ + ptr += n; \ + break; + +# define CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) \ + case BT_NONASCII: \ + if (! IS_NAME_CHAR_MINBPC(enc, ptr)) { \ + *nextTokPtr = ptr; \ + return XML_TOK_INVALID; \ + } \ + /* fall through */ \ + case BT_NMSTRT: \ + case BT_HEX: \ + case BT_DIGIT: \ + case BT_NAME: \ + case BT_MINUS: \ + ptr += MINBPC(enc); \ + break; \ + CHECK_NAME_CASE(2, enc, ptr, end, nextTokPtr) \ + CHECK_NAME_CASE(3, enc, ptr, end, nextTokPtr) \ + CHECK_NAME_CASE(4, enc, ptr, end, nextTokPtr) -#define CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) \ - case BT_NONASCII: \ - if (!IS_NAME_CHAR_MINBPC(enc, ptr)) { \ - *nextTokPtr = ptr; \ - return XML_TOK_INVALID; \ - } \ - case BT_NMSTRT: \ - case BT_HEX: \ - case BT_DIGIT: \ - case BT_NAME: \ - case BT_MINUS: \ - ptr += MINBPC(enc); \ - break; \ - CHECK_NAME_CASE(2, enc, ptr, end, nextTokPtr) \ - CHECK_NAME_CASE(3, enc, ptr, end, nextTokPtr) \ - CHECK_NAME_CASE(4, enc, ptr, end, nextTokPtr) +# define CHECK_NMSTRT_CASE(n, enc, ptr, end, nextTokPtr) \ + case BT_LEAD##n: \ + if ((end) - (ptr) < (n)) \ + return XML_TOK_PARTIAL_CHAR; \ + if (IS_INVALID_CHAR(enc, ptr, n) || ! IS_NMSTRT_CHAR(enc, ptr, n)) { \ + *nextTokPtr = ptr; \ + return XML_TOK_INVALID; \ + } \ + ptr += n; \ + break; -#define CHECK_NMSTRT_CASE(n, enc, ptr, end, nextTokPtr) \ - case BT_LEAD ## n: \ - if (end - ptr < n) \ - return XML_TOK_PARTIAL_CHAR; \ - if (!IS_NMSTRT_CHAR(enc, ptr, n)) { \ - *nextTokPtr = ptr; \ - return XML_TOK_INVALID; \ - } \ - ptr += n; \ - break; +# define CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr) \ + case BT_NONASCII: \ + if (! IS_NMSTRT_CHAR_MINBPC(enc, ptr)) { \ + *nextTokPtr = ptr; \ + return XML_TOK_INVALID; \ + } \ + /* fall through */ \ + case BT_NMSTRT: \ + case BT_HEX: \ + ptr += MINBPC(enc); \ + break; \ + CHECK_NMSTRT_CASE(2, enc, ptr, end, nextTokPtr) \ + CHECK_NMSTRT_CASE(3, enc, ptr, end, nextTokPtr) \ + CHECK_NMSTRT_CASE(4, enc, ptr, end, nextTokPtr) -#define CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr) \ - case BT_NONASCII: \ - if (!IS_NMSTRT_CHAR_MINBPC(enc, ptr)) { \ - *nextTokPtr = ptr; \ - return XML_TOK_INVALID; \ - } \ - case BT_NMSTRT: \ - case BT_HEX: \ - ptr += MINBPC(enc); \ - break; \ - CHECK_NMSTRT_CASE(2, enc, ptr, end, nextTokPtr) \ - CHECK_NMSTRT_CASE(3, enc, ptr, end, nextTokPtr) \ - CHECK_NMSTRT_CASE(4, enc, ptr, end, nextTokPtr) +# ifndef PREFIX +# define PREFIX(ident) ident +# endif -#ifndef PREFIX -#define PREFIX(ident) ident -#endif +# define HAS_CHARS(enc, ptr, end, count) \ + ((end) - (ptr) >= ((count) * MINBPC(enc))) + +# define HAS_CHAR(enc, ptr, end) HAS_CHARS(enc, ptr, end, 1) + +# define REQUIRE_CHARS(enc, ptr, end, count) \ + { \ + if (! HAS_CHARS(enc, ptr, end, count)) { \ + return XML_TOK_PARTIAL; \ + } \ + } + +# define REQUIRE_CHAR(enc, ptr, end) REQUIRE_CHARS(enc, ptr, end, 1) /* ptr points to character following " */ switch (BYTE_TYPE(enc, ptr + MINBPC(enc))) { - case BT_S: case BT_CR: case BT_LF: case BT_PERCNT: + case BT_S: + case BT_CR: + case BT_LF: + case BT_PERCNT: *nextTokPtr = ptr; return XML_TOK_INVALID; } /* fall through */ - case BT_S: case BT_CR: case BT_LF: + case BT_S: + case BT_CR: + case BT_LF: *nextTokPtr = ptr; return XML_TOK_DECL_OPEN; case BT_NMSTRT: @@ -175,12 +228,12 @@ PREFIX(scanDecl)(const ENCODING *enc, const char *ptr, } static int PTRCALL -PREFIX(checkPiTarget)(const ENCODING *enc, const char *ptr, - const char *end, int *tokPtr) -{ +PREFIX(checkPiTarget)(const ENCODING *enc, const char *ptr, const char *end, + int *tokPtr) { int upper = 0; + UNUSED_P(enc); *tokPtr = XML_TOK_PI; - if (end - ptr != MINBPC(enc)*3) + if (end - ptr != MINBPC(enc) * 3) return 1; switch (BYTE_TO_ASCII(enc, ptr)) { case ASCII_x: @@ -220,35 +273,34 @@ PREFIX(checkPiTarget)(const ENCODING *enc, const char *ptr, /* ptr points to character following "= end) return XML_TOK_NONE; if (MINBPC(enc) > 1) { size_t n = end - ptr; @@ -319,14 +368,12 @@ PREFIX(cdataSectionTok)(const ENCODING *enc, const char *ptr, switch (BYTE_TYPE(enc, ptr)) { case BT_RSQB: ptr += MINBPC(enc); - if (ptr == end) - return XML_TOK_PARTIAL; - if (!CHAR_MATCHES(enc, ptr, ASCII_RSQB)) + REQUIRE_CHAR(enc, ptr, end); + if (! CHAR_MATCHES(enc, ptr, ASCII_RSQB)) break; ptr += MINBPC(enc); - if (ptr == end) - return XML_TOK_PARTIAL; - if (!CHAR_MATCHES(enc, ptr, ASCII_GT)) { + REQUIRE_CHAR(enc, ptr, end); + if (! CHAR_MATCHES(enc, ptr, ASCII_GT)) { ptr -= MINBPC(enc); break; } @@ -334,8 +381,7 @@ PREFIX(cdataSectionTok)(const ENCODING *enc, const char *ptr, return XML_TOK_CDATA_SECT_CLOSE; case BT_CR: ptr += MINBPC(enc); - if (ptr == end) - return XML_TOK_PARTIAL; + REQUIRE_CHAR(enc, ptr, end); if (BYTE_TYPE(enc, ptr) == BT_LF) ptr += MINBPC(enc); *nextTokPtr = ptr; @@ -343,23 +389,25 @@ PREFIX(cdataSectionTok)(const ENCODING *enc, const char *ptr, case BT_LF: *nextTokPtr = ptr + MINBPC(enc); return XML_TOK_DATA_NEWLINE; - INVALID_CASES(ptr, nextTokPtr) + INVALID_CASES(ptr, nextTokPtr) default: ptr += MINBPC(enc); break; } - while (ptr != end) { + while (HAS_CHAR(enc, ptr, end)) { switch (BYTE_TYPE(enc, ptr)) { -#define LEAD_CASE(n) \ - case BT_LEAD ## n: \ - if (end - ptr < n || IS_INVALID_CHAR(enc, ptr, n)) { \ - *nextTokPtr = ptr; \ - return XML_TOK_DATA_CHARS; \ - } \ - ptr += n; \ - break; - LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) -#undef LEAD_CASE +# define LEAD_CASE(n) \ + case BT_LEAD##n: \ + if (end - ptr < n || IS_INVALID_CHAR(enc, ptr, n)) { \ + *nextTokPtr = ptr; \ + return XML_TOK_DATA_CHARS; \ + } \ + ptr += n; \ + break; + LEAD_CASE(2) + LEAD_CASE(3) + LEAD_CASE(4) +# undef LEAD_CASE case BT_NONXML: case BT_MALFORM: case BT_TRAIL: @@ -380,24 +428,26 @@ PREFIX(cdataSectionTok)(const ENCODING *enc, const char *ptr, /* ptr points to character following "= end) return XML_TOK_NONE; if (MINBPC(enc) > 1) { size_t n = end - ptr; @@ -803,7 +840,7 @@ PREFIX(contentTok)(const ENCODING *enc, const char *ptr, const char *end, return PREFIX(scanRef)(enc, ptr + MINBPC(enc), end, nextTokPtr); case BT_CR: ptr += MINBPC(enc); - if (ptr == end) + if (! HAS_CHAR(enc, ptr, end)) return XML_TOK_TRAILING_CR; if (BYTE_TYPE(enc, ptr) == BT_LF) ptr += MINBPC(enc); @@ -814,50 +851,52 @@ PREFIX(contentTok)(const ENCODING *enc, const char *ptr, const char *end, return XML_TOK_DATA_NEWLINE; case BT_RSQB: ptr += MINBPC(enc); - if (ptr == end) + if (! HAS_CHAR(enc, ptr, end)) return XML_TOK_TRAILING_RSQB; - if (!CHAR_MATCHES(enc, ptr, ASCII_RSQB)) + if (! CHAR_MATCHES(enc, ptr, ASCII_RSQB)) break; ptr += MINBPC(enc); - if (ptr == end) + if (! HAS_CHAR(enc, ptr, end)) return XML_TOK_TRAILING_RSQB; - if (!CHAR_MATCHES(enc, ptr, ASCII_GT)) { + if (! CHAR_MATCHES(enc, ptr, ASCII_GT)) { ptr -= MINBPC(enc); break; } *nextTokPtr = ptr; return XML_TOK_INVALID; - INVALID_CASES(ptr, nextTokPtr) + INVALID_CASES(ptr, nextTokPtr) default: ptr += MINBPC(enc); break; } - while (ptr != end) { + while (HAS_CHAR(enc, ptr, end)) { switch (BYTE_TYPE(enc, ptr)) { -#define LEAD_CASE(n) \ - case BT_LEAD ## n: \ - if (end - ptr < n || IS_INVALID_CHAR(enc, ptr, n)) { \ - *nextTokPtr = ptr; \ - return XML_TOK_DATA_CHARS; \ - } \ - ptr += n; \ - break; - LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) -#undef LEAD_CASE +# define LEAD_CASE(n) \ + case BT_LEAD##n: \ + if (end - ptr < n || IS_INVALID_CHAR(enc, ptr, n)) { \ + *nextTokPtr = ptr; \ + return XML_TOK_DATA_CHARS; \ + } \ + ptr += n; \ + break; + LEAD_CASE(2) + LEAD_CASE(3) + LEAD_CASE(4) +# undef LEAD_CASE case BT_RSQB: - if (ptr + MINBPC(enc) != end) { - if (!CHAR_MATCHES(enc, ptr + MINBPC(enc), ASCII_RSQB)) { - ptr += MINBPC(enc); - break; - } - if (ptr + 2*MINBPC(enc) != end) { - if (!CHAR_MATCHES(enc, ptr + 2*MINBPC(enc), ASCII_GT)) { - ptr += MINBPC(enc); - break; - } - *nextTokPtr = ptr + 2*MINBPC(enc); - return XML_TOK_INVALID; - } + if (HAS_CHARS(enc, ptr, end, 2)) { + if (! CHAR_MATCHES(enc, ptr + MINBPC(enc), ASCII_RSQB)) { + ptr += MINBPC(enc); + break; + } + if (HAS_CHARS(enc, ptr, end, 3)) { + if (! CHAR_MATCHES(enc, ptr + 2 * MINBPC(enc), ASCII_GT)) { + ptr += MINBPC(enc); + break; + } + *nextTokPtr = ptr + 2 * MINBPC(enc); + return XML_TOK_INVALID; + } } /* fall through */ case BT_AMP: @@ -882,22 +921,23 @@ PREFIX(contentTok)(const ENCODING *enc, const char *ptr, const char *end, static int PTRCALL PREFIX(scanPercent)(const ENCODING *enc, const char *ptr, const char *end, - const char **nextTokPtr) -{ - if (ptr == end) - return -XML_TOK_PERCENT; + const char **nextTokPtr) { + REQUIRE_CHAR(enc, ptr, end); switch (BYTE_TYPE(enc, ptr)) { - CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr) - case BT_S: case BT_LF: case BT_CR: case BT_PERCNT: + CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr) + case BT_S: + case BT_LF: + case BT_CR: + case BT_PERCNT: *nextTokPtr = ptr; return XML_TOK_PERCENT; default: *nextTokPtr = ptr; return XML_TOK_INVALID; } - while (ptr != end) { + while (HAS_CHAR(enc, ptr, end)) { switch (BYTE_TYPE(enc, ptr)) { - CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) + CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) case BT_SEMI: *nextTokPtr = ptr + MINBPC(enc); return XML_TOK_PARAM_ENTITY_REF; @@ -911,21 +951,24 @@ PREFIX(scanPercent)(const ENCODING *enc, const char *ptr, const char *end, static int PTRCALL PREFIX(scanPoundName)(const ENCODING *enc, const char *ptr, const char *end, - const char **nextTokPtr) -{ - if (ptr == end) - return XML_TOK_PARTIAL; + const char **nextTokPtr) { + REQUIRE_CHAR(enc, ptr, end); switch (BYTE_TYPE(enc, ptr)) { - CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr) + CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr) default: *nextTokPtr = ptr; return XML_TOK_INVALID; } - while (ptr != end) { + while (HAS_CHAR(enc, ptr, end)) { switch (BYTE_TYPE(enc, ptr)) { - CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) - case BT_CR: case BT_LF: case BT_S: - case BT_RPAR: case BT_GT: case BT_PERCNT: case BT_VERBAR: + CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) + case BT_CR: + case BT_LF: + case BT_S: + case BT_RPAR: + case BT_GT: + case BT_PERCNT: + case BT_VERBAR: *nextTokPtr = ptr; return XML_TOK_POUND_NAME; default: @@ -937,25 +980,27 @@ PREFIX(scanPoundName)(const ENCODING *enc, const char *ptr, const char *end, } static int PTRCALL -PREFIX(scanLit)(int open, const ENCODING *enc, - const char *ptr, const char *end, - const char **nextTokPtr) -{ - while (ptr != end) { +PREFIX(scanLit)(int open, const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) { + while (HAS_CHAR(enc, ptr, end)) { int t = BYTE_TYPE(enc, ptr); switch (t) { - INVALID_CASES(ptr, nextTokPtr) + INVALID_CASES(ptr, nextTokPtr) case BT_QUOT: case BT_APOS: ptr += MINBPC(enc); if (t != open) break; - if (ptr == end) + if (! HAS_CHAR(enc, ptr, end)) return -XML_TOK_LITERAL; *nextTokPtr = ptr; switch (BYTE_TYPE(enc, ptr)) { - case BT_S: case BT_CR: case BT_LF: - case BT_GT: case BT_PERCNT: case BT_LSQB: + case BT_S: + case BT_CR: + case BT_LF: + case BT_GT: + case BT_PERCNT: + case BT_LSQB: return XML_TOK_LITERAL; default: return XML_TOK_INVALID; @@ -970,10 +1015,9 @@ PREFIX(scanLit)(int open, const ENCODING *enc, static int PTRCALL PREFIX(prologTok)(const ENCODING *enc, const char *ptr, const char *end, - const char **nextTokPtr) -{ + const char **nextTokPtr) { int tok; - if (ptr == end) + if (ptr >= end) return XML_TOK_NONE; if (MINBPC(enc) > 1) { size_t n = end - ptr; @@ -989,28 +1033,26 @@ PREFIX(prologTok)(const ENCODING *enc, const char *ptr, const char *end, return PREFIX(scanLit)(BT_QUOT, enc, ptr + MINBPC(enc), end, nextTokPtr); case BT_APOS: return PREFIX(scanLit)(BT_APOS, enc, ptr + MINBPC(enc), end, nextTokPtr); - case BT_LT: - { - ptr += MINBPC(enc); - if (ptr == end) - return XML_TOK_PARTIAL; - switch (BYTE_TYPE(enc, ptr)) { - case BT_EXCL: - return PREFIX(scanDecl)(enc, ptr + MINBPC(enc), end, nextTokPtr); - case BT_QUEST: - return PREFIX(scanPi)(enc, ptr + MINBPC(enc), end, nextTokPtr); - case BT_NMSTRT: - case BT_HEX: - case BT_NONASCII: - case BT_LEAD2: - case BT_LEAD3: - case BT_LEAD4: - *nextTokPtr = ptr - MINBPC(enc); - return XML_TOK_INSTANCE_START; - } - *nextTokPtr = ptr; - return XML_TOK_INVALID; + case BT_LT: { + ptr += MINBPC(enc); + REQUIRE_CHAR(enc, ptr, end); + switch (BYTE_TYPE(enc, ptr)) { + case BT_EXCL: + return PREFIX(scanDecl)(enc, ptr + MINBPC(enc), end, nextTokPtr); + case BT_QUEST: + return PREFIX(scanPi)(enc, ptr + MINBPC(enc), end, nextTokPtr); + case BT_NMSTRT: + case BT_HEX: + case BT_NONASCII: + case BT_LEAD2: + case BT_LEAD3: + case BT_LEAD4: + *nextTokPtr = ptr - MINBPC(enc); + return XML_TOK_INSTANCE_START; } + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } case BT_CR: if (ptr + MINBPC(enc) == end) { *nextTokPtr = end; @@ -1018,13 +1060,15 @@ PREFIX(prologTok)(const ENCODING *enc, const char *ptr, const char *end, return -XML_TOK_PROLOG_S; } /* fall through */ - case BT_S: case BT_LF: + case BT_S: + case BT_LF: for (;;) { ptr += MINBPC(enc); - if (ptr == end) + if (! HAS_CHAR(enc, ptr, end)) break; switch (BYTE_TYPE(enc, ptr)) { - case BT_S: case BT_LF: + case BT_S: + case BT_LF: break; case BT_CR: /* don't split CR/LF pair */ @@ -1048,13 +1092,12 @@ PREFIX(prologTok)(const ENCODING *enc, const char *ptr, const char *end, return XML_TOK_OPEN_BRACKET; case BT_RSQB: ptr += MINBPC(enc); - if (ptr == end) + if (! HAS_CHAR(enc, ptr, end)) return -XML_TOK_CLOSE_BRACKET; if (CHAR_MATCHES(enc, ptr, ASCII_RSQB)) { - if (ptr + MINBPC(enc) == end) - return XML_TOK_PARTIAL; + REQUIRE_CHARS(enc, ptr, end, 2); if (CHAR_MATCHES(enc, ptr + MINBPC(enc), ASCII_GT)) { - *nextTokPtr = ptr + 2*MINBPC(enc); + *nextTokPtr = ptr + 2 * MINBPC(enc); return XML_TOK_COND_SECT_CLOSE; } } @@ -1065,7 +1108,7 @@ PREFIX(prologTok)(const ENCODING *enc, const char *ptr, const char *end, return XML_TOK_OPEN_PAREN; case BT_RPAR: ptr += MINBPC(enc); - if (ptr == end) + if (! HAS_CHAR(enc, ptr, end)) return -XML_TOK_CLOSE_PAREN; switch (BYTE_TYPE(enc, ptr)) { case BT_AST: @@ -1077,8 +1120,12 @@ PREFIX(prologTok)(const ENCODING *enc, const char *ptr, const char *end, case BT_PLUS: *nextTokPtr = ptr + MINBPC(enc); return XML_TOK_CLOSE_PAREN_PLUS; - case BT_CR: case BT_LF: case BT_S: - case BT_GT: case BT_COMMA: case BT_VERBAR: + case BT_CR: + case BT_LF: + case BT_S: + case BT_GT: + case BT_COMMA: + case BT_VERBAR: case BT_RPAR: *nextTokPtr = ptr; return XML_TOK_CLOSE_PAREN; @@ -1093,24 +1140,30 @@ PREFIX(prologTok)(const ENCODING *enc, const char *ptr, const char *end, return XML_TOK_DECL_CLOSE; case BT_NUM: return PREFIX(scanPoundName)(enc, ptr + MINBPC(enc), end, nextTokPtr); -#define LEAD_CASE(n) \ - case BT_LEAD ## n: \ - if (end - ptr < n) \ - return XML_TOK_PARTIAL_CHAR; \ - if (IS_NMSTRT_CHAR(enc, ptr, n)) { \ - ptr += n; \ - tok = XML_TOK_NAME; \ - break; \ - } \ - if (IS_NAME_CHAR(enc, ptr, n)) { \ - ptr += n; \ - tok = XML_TOK_NMTOKEN; \ - break; \ - } \ - *nextTokPtr = ptr; \ +# define LEAD_CASE(n) \ + case BT_LEAD##n: \ + if (end - ptr < n) \ + return XML_TOK_PARTIAL_CHAR; \ + if (IS_INVALID_CHAR(enc, ptr, n)) { \ + *nextTokPtr = ptr; \ + return XML_TOK_INVALID; \ + } \ + if (IS_NMSTRT_CHAR(enc, ptr, n)) { \ + ptr += n; \ + tok = XML_TOK_NAME; \ + break; \ + } \ + if (IS_NAME_CHAR(enc, ptr, n)) { \ + ptr += n; \ + tok = XML_TOK_NMTOKEN; \ + break; \ + } \ + *nextTokPtr = ptr; \ return XML_TOK_INVALID; - LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) -#undef LEAD_CASE + LEAD_CASE(2) + LEAD_CASE(3) + LEAD_CASE(4) +# undef LEAD_CASE case BT_NMSTRT: case BT_HEX: tok = XML_TOK_NAME; @@ -1119,9 +1172,9 @@ PREFIX(prologTok)(const ENCODING *enc, const char *ptr, const char *end, case BT_DIGIT: case BT_NAME: case BT_MINUS: -#ifdef XML_NS +# ifdef XML_NS case BT_COLON: -#endif +# endif tok = XML_TOK_NMTOKEN; ptr += MINBPC(enc); break; @@ -1141,24 +1194,29 @@ PREFIX(prologTok)(const ENCODING *enc, const char *ptr, const char *end, *nextTokPtr = ptr; return XML_TOK_INVALID; } - while (ptr != end) { + while (HAS_CHAR(enc, ptr, end)) { switch (BYTE_TYPE(enc, ptr)) { - CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) - case BT_GT: case BT_RPAR: case BT_COMMA: - case BT_VERBAR: case BT_LSQB: case BT_PERCNT: - case BT_S: case BT_CR: case BT_LF: + CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) + case BT_GT: + case BT_RPAR: + case BT_COMMA: + case BT_VERBAR: + case BT_LSQB: + case BT_PERCNT: + case BT_S: + case BT_CR: + case BT_LF: *nextTokPtr = ptr; return tok; -#ifdef XML_NS +# ifdef XML_NS case BT_COLON: ptr += MINBPC(enc); switch (tok) { case XML_TOK_NAME: - if (ptr == end) - return XML_TOK_PARTIAL; + REQUIRE_CHAR(enc, ptr, end); tok = XML_TOK_PREFIXED_NAME; switch (BYTE_TYPE(enc, ptr)) { - CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) + CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) default: tok = XML_TOK_NMTOKEN; break; @@ -1169,23 +1227,23 @@ PREFIX(prologTok)(const ENCODING *enc, const char *ptr, const char *end, break; } break; -#endif +# endif case BT_PLUS: - if (tok == XML_TOK_NMTOKEN) { + if (tok == XML_TOK_NMTOKEN) { *nextTokPtr = ptr; return XML_TOK_INVALID; } *nextTokPtr = ptr + MINBPC(enc); return XML_TOK_NAME_PLUS; case BT_AST: - if (tok == XML_TOK_NMTOKEN) { + if (tok == XML_TOK_NMTOKEN) { *nextTokPtr = ptr; return XML_TOK_INVALID; } *nextTokPtr = ptr + MINBPC(enc); return XML_TOK_NAME_ASTERISK; case BT_QUEST: - if (tok == XML_TOK_NMTOKEN) { + if (tok == XML_TOK_NMTOKEN) { *nextTokPtr = ptr; return XML_TOK_INVALID; } @@ -1200,19 +1258,30 @@ PREFIX(prologTok)(const ENCODING *enc, const char *ptr, const char *end, } static int PTRCALL -PREFIX(attributeValueTok)(const ENCODING *enc, const char *ptr, - const char *end, const char **nextTokPtr) -{ +PREFIX(attributeValueTok)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) { const char *start; - if (ptr == end) + if (ptr >= end) return XML_TOK_NONE; + else if (! HAS_CHAR(enc, ptr, end)) { + /* This line cannot be executed. The incoming data has already + * been tokenized once, so incomplete characters like this have + * already been eliminated from the input. Retaining the paranoia + * check is still valuable, however. + */ + return XML_TOK_PARTIAL; /* LCOV_EXCL_LINE */ + } start = ptr; - while (ptr != end) { + while (HAS_CHAR(enc, ptr, end)) { switch (BYTE_TYPE(enc, ptr)) { -#define LEAD_CASE(n) \ - case BT_LEAD ## n: ptr += n; break; - LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) -#undef LEAD_CASE +# define LEAD_CASE(n) \ + case BT_LEAD##n: \ + ptr += n; /* NOTE: The encoding has already been validated. */ \ + break; + LEAD_CASE(2) + LEAD_CASE(3) + LEAD_CASE(4) +# undef LEAD_CASE case BT_AMP: if (ptr == start) return PREFIX(scanRef)(enc, ptr + MINBPC(enc), end, nextTokPtr); @@ -1232,7 +1301,7 @@ PREFIX(attributeValueTok)(const ENCODING *enc, const char *ptr, case BT_CR: if (ptr == start) { ptr += MINBPC(enc); - if (ptr == end) + if (! HAS_CHAR(enc, ptr, end)) return XML_TOK_TRAILING_CR; if (BYTE_TYPE(enc, ptr) == BT_LF) ptr += MINBPC(enc); @@ -1258,19 +1327,30 @@ PREFIX(attributeValueTok)(const ENCODING *enc, const char *ptr, } static int PTRCALL -PREFIX(entityValueTok)(const ENCODING *enc, const char *ptr, - const char *end, const char **nextTokPtr) -{ +PREFIX(entityValueTok)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) { const char *start; - if (ptr == end) + if (ptr >= end) return XML_TOK_NONE; + else if (! HAS_CHAR(enc, ptr, end)) { + /* This line cannot be executed. The incoming data has already + * been tokenized once, so incomplete characters like this have + * already been eliminated from the input. Retaining the paranoia + * check is still valuable, however. + */ + return XML_TOK_PARTIAL; /* LCOV_EXCL_LINE */ + } start = ptr; - while (ptr != end) { + while (HAS_CHAR(enc, ptr, end)) { switch (BYTE_TYPE(enc, ptr)) { -#define LEAD_CASE(n) \ - case BT_LEAD ## n: ptr += n; break; - LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) -#undef LEAD_CASE +# define LEAD_CASE(n) \ + case BT_LEAD##n: \ + ptr += n; /* NOTE: The encoding has already been validated. */ \ + break; + LEAD_CASE(2) + LEAD_CASE(3) + LEAD_CASE(4) +# undef LEAD_CASE case BT_AMP: if (ptr == start) return PREFIX(scanRef)(enc, ptr + MINBPC(enc), end, nextTokPtr); @@ -1278,8 +1358,7 @@ PREFIX(entityValueTok)(const ENCODING *enc, const char *ptr, return XML_TOK_DATA_CHARS; case BT_PERCNT: if (ptr == start) { - int tok = PREFIX(scanPercent)(enc, ptr + MINBPC(enc), - end, nextTokPtr); + int tok = PREFIX(scanPercent)(enc, ptr + MINBPC(enc), end, nextTokPtr); return (tok == XML_TOK_PERCENT) ? XML_TOK_INVALID : tok; } *nextTokPtr = ptr; @@ -1294,7 +1373,7 @@ PREFIX(entityValueTok)(const ENCODING *enc, const char *ptr, case BT_CR: if (ptr == start) { ptr += MINBPC(enc); - if (ptr == end) + if (! HAS_CHAR(enc, ptr, end)) return XML_TOK_TRAILING_CR; if (BYTE_TYPE(enc, ptr) == BT_LF) ptr += MINBPC(enc); @@ -1312,12 +1391,11 @@ PREFIX(entityValueTok)(const ENCODING *enc, const char *ptr, return XML_TOK_DATA_CHARS; } -#ifdef XML_DTD +# ifdef XML_DTD static int PTRCALL -PREFIX(ignoreSectionTok)(const ENCODING *enc, const char *ptr, - const char *end, const char **nextTokPtr) -{ +PREFIX(ignoreSectionTok)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) { int level = 0; if (MINBPC(enc) > 1) { size_t n = end - ptr; @@ -1326,15 +1404,15 @@ PREFIX(ignoreSectionTok)(const ENCODING *enc, const char *ptr, end = ptr + n; } } - while (ptr != end) { + while (HAS_CHAR(enc, ptr, end)) { switch (BYTE_TYPE(enc, ptr)) { - INVALID_CASES(ptr, nextTokPtr) + INVALID_CASES(ptr, nextTokPtr) case BT_LT: - if ((ptr += MINBPC(enc)) == end) - return XML_TOK_PARTIAL; + ptr += MINBPC(enc); + REQUIRE_CHAR(enc, ptr, end); if (CHAR_MATCHES(enc, ptr, ASCII_EXCL)) { - if ((ptr += MINBPC(enc)) == end) - return XML_TOK_PARTIAL; + ptr += MINBPC(enc); + REQUIRE_CHAR(enc, ptr, end); if (CHAR_MATCHES(enc, ptr, ASCII_LSQB)) { ++level; ptr += MINBPC(enc); @@ -1342,11 +1420,11 @@ PREFIX(ignoreSectionTok)(const ENCODING *enc, const char *ptr, } break; case BT_RSQB: - if ((ptr += MINBPC(enc)) == end) - return XML_TOK_PARTIAL; + ptr += MINBPC(enc); + REQUIRE_CHAR(enc, ptr, end); if (CHAR_MATCHES(enc, ptr, ASCII_RSQB)) { - if ((ptr += MINBPC(enc)) == end) - return XML_TOK_PARTIAL; + ptr += MINBPC(enc); + REQUIRE_CHAR(enc, ptr, end); if (CHAR_MATCHES(enc, ptr, ASCII_GT)) { ptr += MINBPC(enc); if (level == 0) { @@ -1365,15 +1443,14 @@ PREFIX(ignoreSectionTok)(const ENCODING *enc, const char *ptr, return XML_TOK_PARTIAL; } -#endif /* XML_DTD */ +# endif /* XML_DTD */ static int PTRCALL PREFIX(isPublicId)(const ENCODING *enc, const char *ptr, const char *end, - const char **badPtr) -{ + const char **badPtr) { ptr += MINBPC(enc); end -= MINBPC(enc); - for (; ptr != end; ptr += MINBPC(enc)) { + for (; HAS_CHAR(enc, ptr, end); ptr += MINBPC(enc)) { switch (BYTE_TYPE(enc, ptr)) { case BT_DIGIT: case BT_HEX: @@ -1393,9 +1470,9 @@ PREFIX(isPublicId)(const ENCODING *enc, const char *ptr, const char *end, case BT_AST: case BT_PERCNT: case BT_NUM: -#ifdef XML_NS +# ifdef XML_NS case BT_COLON: -#endif +# endif break; case BT_S: if (CHAR_MATCHES(enc, ptr, ASCII_TAB)) { @@ -1405,8 +1482,9 @@ PREFIX(isPublicId)(const ENCODING *enc, const char *ptr, const char *end, break; case BT_NAME: case BT_NMSTRT: - if (!(BYTE_TO_ASCII(enc, ptr) & ~0x7f)) + if (! (BYTE_TO_ASCII(enc, ptr) & ~0x7f)) break; + /* fall through */ default: switch (BYTE_TO_ASCII(enc, ptr)) { case 0x24: /* $ */ @@ -1428,9 +1506,8 @@ PREFIX(isPublicId)(const ENCODING *enc, const char *ptr, const char *end, */ static int PTRCALL -PREFIX(getAtts)(const ENCODING *enc, const char *ptr, - int attsMax, ATTRIBUTE *atts) -{ +PREFIX(getAtts)(const ENCODING *enc, const char *ptr, int attsMax, + ATTRIBUTE *atts) { enum { other, inName, inValue } state = inName; int nAtts = 0; int open = 0; /* defined when state == inValue; @@ -1438,32 +1515,35 @@ PREFIX(getAtts)(const ENCODING *enc, const char *ptr, for (ptr += MINBPC(enc);; ptr += MINBPC(enc)) { switch (BYTE_TYPE(enc, ptr)) { -#define START_NAME \ - if (state == other) { \ - if (nAtts < attsMax) { \ - atts[nAtts].name = ptr; \ - atts[nAtts].normalized = 1; \ - } \ - state = inName; \ - } -#define LEAD_CASE(n) \ - case BT_LEAD ## n: START_NAME ptr += (n - MINBPC(enc)); break; - LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) -#undef LEAD_CASE +# define START_NAME \ + if (state == other) { \ + if (nAtts < attsMax) { \ + atts[nAtts].name = ptr; \ + atts[nAtts].normalized = 1; \ + } \ + state = inName; \ + } +# define LEAD_CASE(n) \ + case BT_LEAD##n: /* NOTE: The encoding has already been validated. */ \ + START_NAME ptr += (n - MINBPC(enc)); \ + break; + LEAD_CASE(2) + LEAD_CASE(3) + LEAD_CASE(4) +# undef LEAD_CASE case BT_NONASCII: case BT_NMSTRT: case BT_HEX: START_NAME break; -#undef START_NAME +# undef START_NAME case BT_QUOT: if (state != inValue) { if (nAtts < attsMax) atts[nAtts].valuePtr = ptr + MINBPC(enc); state = inValue; open = BT_QUOT; - } - else if (open == BT_QUOT) { + } else if (open == BT_QUOT) { state = other; if (nAtts < attsMax) atts[nAtts].valueEnd = ptr; @@ -1476,8 +1556,7 @@ PREFIX(getAtts)(const ENCODING *enc, const char *ptr, atts[nAtts].valuePtr = ptr + MINBPC(enc); state = inValue; open = BT_APOS; - } - else if (open == BT_APOS) { + } else if (open == BT_APOS) { state = other; if (nAtts < attsMax) atts[nAtts].valueEnd = ptr; @@ -1491,16 +1570,15 @@ PREFIX(getAtts)(const ENCODING *enc, const char *ptr, case BT_S: if (state == inName) state = other; - else if (state == inValue - && nAtts < attsMax - && atts[nAtts].normalized + else if (state == inValue && nAtts < attsMax && atts[nAtts].normalized && (ptr == atts[nAtts].valuePtr || BYTE_TO_ASCII(enc, ptr) != ASCII_SPACE || BYTE_TO_ASCII(enc, ptr + MINBPC(enc)) == ASCII_SPACE || BYTE_TYPE(enc, ptr + MINBPC(enc)) == open)) atts[nAtts].normalized = 0; break; - case BT_CR: case BT_LF: + case BT_CR: + case BT_LF: /* This case ensures that the first attribute name is counted Apart from that we could just change state on the quote. */ if (state == inName) @@ -1521,29 +1599,44 @@ PREFIX(getAtts)(const ENCODING *enc, const char *ptr, } static int PTRFASTCALL -PREFIX(charRefNumber)(const ENCODING *enc, const char *ptr) -{ +PREFIX(charRefNumber)(const ENCODING *enc, const char *ptr) { int result = 0; /* skip &# */ - ptr += 2*MINBPC(enc); + UNUSED_P(enc); + ptr += 2 * MINBPC(enc); if (CHAR_MATCHES(enc, ptr, ASCII_x)) { - for (ptr += MINBPC(enc); - !CHAR_MATCHES(enc, ptr, ASCII_SEMI); + for (ptr += MINBPC(enc); ! CHAR_MATCHES(enc, ptr, ASCII_SEMI); ptr += MINBPC(enc)) { int c = BYTE_TO_ASCII(enc, ptr); switch (c) { - case ASCII_0: case ASCII_1: case ASCII_2: case ASCII_3: case ASCII_4: - case ASCII_5: case ASCII_6: case ASCII_7: case ASCII_8: case ASCII_9: + case ASCII_0: + case ASCII_1: + case ASCII_2: + case ASCII_3: + case ASCII_4: + case ASCII_5: + case ASCII_6: + case ASCII_7: + case ASCII_8: + case ASCII_9: result <<= 4; result |= (c - ASCII_0); break; - case ASCII_A: case ASCII_B: case ASCII_C: - case ASCII_D: case ASCII_E: case ASCII_F: + case ASCII_A: + case ASCII_B: + case ASCII_C: + case ASCII_D: + case ASCII_E: + case ASCII_F: result <<= 4; result += 10 + (c - ASCII_A); break; - case ASCII_a: case ASCII_b: case ASCII_c: - case ASCII_d: case ASCII_e: case ASCII_f: + case ASCII_a: + case ASCII_b: + case ASCII_c: + case ASCII_d: + case ASCII_e: + case ASCII_f: result <<= 4; result += 10 + (c - ASCII_a); break; @@ -1551,9 +1644,8 @@ PREFIX(charRefNumber)(const ENCODING *enc, const char *ptr) if (result >= 0x110000) return -1; } - } - else { - for (; !CHAR_MATCHES(enc, ptr, ASCII_SEMI); ptr += MINBPC(enc)) { + } else { + for (; ! CHAR_MATCHES(enc, ptr, ASCII_SEMI); ptr += MINBPC(enc)) { int c = BYTE_TO_ASCII(enc, ptr); result *= 10; result += (c - ASCII_0); @@ -1566,9 +1658,9 @@ PREFIX(charRefNumber)(const ENCODING *enc, const char *ptr) static int PTRCALL PREFIX(predefinedEntityName)(const ENCODING *enc, const char *ptr, - const char *end) -{ - switch ((end - ptr)/MINBPC(enc)) { + const char *end) { + UNUSED_P(enc); + switch ((end - ptr) / MINBPC(enc)) { case 2: if (CHAR_MATCHES(enc, ptr + MINBPC(enc), ASCII_t)) { switch (BYTE_TO_ASCII(enc, ptr)) { @@ -1618,98 +1710,43 @@ PREFIX(predefinedEntityName)(const ENCODING *enc, const char *ptr, return 0; } -static int PTRCALL -PREFIX(sameName)(const ENCODING *enc, const char *ptr1, const char *ptr2) -{ - for (;;) { - switch (BYTE_TYPE(enc, ptr1)) { -#define LEAD_CASE(n) \ - case BT_LEAD ## n: \ - if (*ptr1++ != *ptr2++) \ - return 0; - LEAD_CASE(4) LEAD_CASE(3) LEAD_CASE(2) -#undef LEAD_CASE - /* fall through */ - if (*ptr1++ != *ptr2++) - return 0; - break; - case BT_NONASCII: - case BT_NMSTRT: -#ifdef XML_NS - case BT_COLON: -#endif - case BT_HEX: - case BT_DIGIT: - case BT_NAME: - case BT_MINUS: - if (*ptr2++ != *ptr1++) - return 0; - if (MINBPC(enc) > 1) { - if (*ptr2++ != *ptr1++) - return 0; - if (MINBPC(enc) > 2) { - if (*ptr2++ != *ptr1++) - return 0; - if (MINBPC(enc) > 3) { - if (*ptr2++ != *ptr1++) - return 0; - } - } - } - break; - default: - if (MINBPC(enc) == 1 && *ptr1 == *ptr2) - return 1; - switch (BYTE_TYPE(enc, ptr2)) { - case BT_LEAD2: - case BT_LEAD3: - case BT_LEAD4: - case BT_NONASCII: - case BT_NMSTRT: -#ifdef XML_NS - case BT_COLON: -#endif - case BT_HEX: - case BT_DIGIT: - case BT_NAME: - case BT_MINUS: - return 0; - default: - return 1; - } - } - } - /* not reached */ -} - static int PTRCALL PREFIX(nameMatchesAscii)(const ENCODING *enc, const char *ptr1, - const char *end1, const char *ptr2) -{ + const char *end1, const char *ptr2) { + UNUSED_P(enc); for (; *ptr2; ptr1 += MINBPC(enc), ptr2++) { - if (ptr1 == end1) - return 0; - if (!CHAR_MATCHES(enc, ptr1, *ptr2)) + if (end1 - ptr1 < MINBPC(enc)) { + /* This line cannot be executed. The incoming data has already + * been tokenized once, so incomplete characters like this have + * already been eliminated from the input. Retaining the + * paranoia check is still valuable, however. + */ + return 0; /* LCOV_EXCL_LINE */ + } + if (! CHAR_MATCHES(enc, ptr1, *ptr2)) return 0; } return ptr1 == end1; } static int PTRFASTCALL -PREFIX(nameLength)(const ENCODING *enc, const char *ptr) -{ +PREFIX(nameLength)(const ENCODING *enc, const char *ptr) { const char *start = ptr; for (;;) { switch (BYTE_TYPE(enc, ptr)) { -#define LEAD_CASE(n) \ - case BT_LEAD ## n: ptr += n; break; - LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) -#undef LEAD_CASE +# define LEAD_CASE(n) \ + case BT_LEAD##n: \ + ptr += n; /* NOTE: The encoding has already been validated. */ \ + break; + LEAD_CASE(2) + LEAD_CASE(3) + LEAD_CASE(4) +# undef LEAD_CASE case BT_NONASCII: case BT_NMSTRT: -#ifdef XML_NS +# ifdef XML_NS case BT_COLON: -#endif +# endif case BT_HEX: case BT_DIGIT: case BT_NAME: @@ -1722,9 +1759,8 @@ PREFIX(nameLength)(const ENCODING *enc, const char *ptr) } } -static const char * PTRFASTCALL -PREFIX(skipS)(const ENCODING *enc, const char *ptr) -{ +static const char *PTRFASTCALL +PREFIX(skipS)(const ENCODING *enc, const char *ptr) { for (;;) { switch (BYTE_TYPE(enc, ptr)) { case BT_LF: @@ -1739,45 +1775,45 @@ PREFIX(skipS)(const ENCODING *enc, const char *ptr) } static void PTRCALL -PREFIX(updatePosition)(const ENCODING *enc, - const char *ptr, - const char *end, - POSITION *pos) -{ - while (ptr != end) { +PREFIX(updatePosition)(const ENCODING *enc, const char *ptr, const char *end, + POSITION *pos) { + while (HAS_CHAR(enc, ptr, end)) { switch (BYTE_TYPE(enc, ptr)) { -#define LEAD_CASE(n) \ - case BT_LEAD ## n: \ - ptr += n; \ - break; - LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) -#undef LEAD_CASE +# define LEAD_CASE(n) \ + case BT_LEAD##n: \ + ptr += n; /* NOTE: The encoding has already been validated. */ \ + pos->columnNumber++; \ + break; + LEAD_CASE(2) + LEAD_CASE(3) + LEAD_CASE(4) +# undef LEAD_CASE case BT_LF: - pos->columnNumber = (XML_Size)-1; + pos->columnNumber = 0; pos->lineNumber++; ptr += MINBPC(enc); break; case BT_CR: pos->lineNumber++; ptr += MINBPC(enc); - if (ptr != end && BYTE_TYPE(enc, ptr) == BT_LF) + if (HAS_CHAR(enc, ptr, end) && BYTE_TYPE(enc, ptr) == BT_LF) ptr += MINBPC(enc); - pos->columnNumber = (XML_Size)-1; + pos->columnNumber = 0; break; default: ptr += MINBPC(enc); + pos->columnNumber++; break; } - pos->columnNumber++; } } -#undef DO_LEAD_CASE -#undef MULTIBYTE_CASES -#undef INVALID_CASES -#undef CHECK_NAME_CASE -#undef CHECK_NAME_CASES -#undef CHECK_NMSTRT_CASE -#undef CHECK_NMSTRT_CASES +# undef DO_LEAD_CASE +# undef MULTIBYTE_CASES +# undef INVALID_CASES +# undef CHECK_NAME_CASE +# undef CHECK_NAME_CASES +# undef CHECK_NMSTRT_CASE +# undef CHECK_NMSTRT_CASES #endif /* XML_TOK_IMPL_C */ diff --git a/cextern/expat/lib/xmltok_impl.h b/cextern/expat/lib/xmltok_impl.h old mode 100755 new mode 100644 index da0ea60a657d..3469c4ae138c --- a/cextern/expat/lib/xmltok_impl.h +++ b/cextern/expat/lib/xmltok_impl.h @@ -1,46 +1,74 @@ /* -Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd -See the file COPYING for copying permission. + __ __ _ + ___\ \/ /_ __ __ _| |_ + / _ \\ /| '_ \ / _` | __| + | __// \| |_) | (_| | |_ + \___/_/\_\ .__/ \__,_|\__| + |_| XML parser + + Copyright (c) 1997-2000 Thai Open Source Software Center Ltd + Copyright (c) 2000 Clark Cooper + Copyright (c) 2017-2019 Sebastian Pipping + Licensed under the MIT license: + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to permit + persons to whom the Software is furnished to do so, subject to the + following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + USE OR OTHER DEALINGS IN THE SOFTWARE. */ enum { - BT_NONXML, - BT_MALFORM, - BT_LT, - BT_AMP, - BT_RSQB, - BT_LEAD2, - BT_LEAD3, - BT_LEAD4, - BT_TRAIL, - BT_CR, - BT_LF, - BT_GT, - BT_QUOT, - BT_APOS, - BT_EQUALS, - BT_QUEST, - BT_EXCL, - BT_SOL, - BT_SEMI, - BT_NUM, - BT_LSQB, - BT_S, - BT_NMSTRT, - BT_COLON, - BT_HEX, - BT_DIGIT, - BT_NAME, - BT_MINUS, - BT_OTHER, /* known not to be a name or name start character */ + BT_NONXML, /* e.g. noncharacter-FFFF */ + BT_MALFORM, /* illegal, with regard to encoding */ + BT_LT, /* less than = "<" */ + BT_AMP, /* ampersand = "&" */ + BT_RSQB, /* right square bracket = "[" */ + BT_LEAD2, /* lead byte of a 2-byte UTF-8 character */ + BT_LEAD3, /* lead byte of a 3-byte UTF-8 character */ + BT_LEAD4, /* lead byte of a 4-byte UTF-8 character */ + BT_TRAIL, /* trailing unit, e.g. second 16-bit unit of a 4-byte char. */ + BT_CR, /* carriage return = "\r" */ + BT_LF, /* line feed = "\n" */ + BT_GT, /* greater than = ">" */ + BT_QUOT, /* quotation character = "\"" */ + BT_APOS, /* apostrophe = "'" */ + BT_EQUALS, /* equal sign = "=" */ + BT_QUEST, /* question mark = "?" */ + BT_EXCL, /* exclamation mark = "!" */ + BT_SOL, /* solidus, slash = "/" */ + BT_SEMI, /* semicolon = ";" */ + BT_NUM, /* number sign = "#" */ + BT_LSQB, /* left square bracket = "[" */ + BT_S, /* white space, e.g. "\t", " "[, "\r"] */ + BT_NMSTRT, /* non-hex name start letter = "G".."Z" + "g".."z" + "_" */ + BT_COLON, /* colon = ":" */ + BT_HEX, /* hex letter = "A".."F" + "a".."f" */ + BT_DIGIT, /* digit = "0".."9" */ + BT_NAME, /* dot and middle dot = "." + chr(0xb7) */ + BT_MINUS, /* minus = "-" */ + BT_OTHER, /* known not to be a name or name start character */ BT_NONASCII, /* might be a name or name start character */ - BT_PERCNT, - BT_LPAR, - BT_RPAR, - BT_AST, - BT_PLUS, - BT_COMMA, - BT_VERBAR + BT_PERCNT, /* percent sign = "%" */ + BT_LPAR, /* left parenthesis = "(" */ + BT_RPAR, /* right parenthesis = "(" */ + BT_AST, /* asterisk = "*" */ + BT_PLUS, /* plus sign = "+" */ + BT_COMMA, /* comma = "," */ + BT_VERBAR /* vertical bar = "|" */ }; #include diff --git a/cextern/expat/lib/xmltok_ns.c b/cextern/expat/lib/xmltok_ns.c old mode 100755 new mode 100644 index c3b88fdf4e3e..fbdd3e3c7b79 --- a/cextern/expat/lib/xmltok_ns.c +++ b/cextern/expat/lib/xmltok_ns.c @@ -1,61 +1,83 @@ -/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd - See the file COPYING for copying permission. +/* This file is included! + __ __ _ + ___\ \/ /_ __ __ _| |_ + / _ \\ /| '_ \ / _` | __| + | __// \| |_) | (_| | |_ + \___/_/\_\ .__/ \__,_|\__| + |_| XML parser + + Copyright (c) 1997-2000 Thai Open Source Software Center Ltd + Copyright (c) 2000 Clark Cooper + Copyright (c) 2002 Greg Stein + Copyright (c) 2002 Fred L. Drake, Jr. + Copyright (c) 2002-2006 Karl Waclawek + Copyright (c) 2017-2021 Sebastian Pipping + Licensed under the MIT license: + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to permit + persons to whom the Software is furnished to do so, subject to the + following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/* This file is included! */ #ifdef XML_TOK_NS_C const ENCODING * -NS(XmlGetUtf8InternalEncoding)(void) -{ +NS(XmlGetUtf8InternalEncoding)(void) { return &ns(internal_utf8_encoding).enc; } const ENCODING * -NS(XmlGetUtf16InternalEncoding)(void) -{ -#if BYTEORDER == 1234 +NS(XmlGetUtf16InternalEncoding)(void) { +# if BYTEORDER == 1234 return &ns(internal_little2_encoding).enc; -#elif BYTEORDER == 4321 +# elif BYTEORDER == 4321 return &ns(internal_big2_encoding).enc; -#else +# else const short n = 1; - return (*(const char *)&n - ? &ns(internal_little2_encoding).enc - : &ns(internal_big2_encoding).enc); -#endif + return (*(const char *)&n ? &ns(internal_little2_encoding).enc + : &ns(internal_big2_encoding).enc); +# endif } -static const ENCODING * const NS(encodings)[] = { - &ns(latin1_encoding).enc, - &ns(ascii_encoding).enc, - &ns(utf8_encoding).enc, - &ns(big2_encoding).enc, - &ns(big2_encoding).enc, - &ns(little2_encoding).enc, - &ns(utf8_encoding).enc /* NO_ENC */ +static const ENCODING *const NS(encodings)[] = { + &ns(latin1_encoding).enc, &ns(ascii_encoding).enc, + &ns(utf8_encoding).enc, &ns(big2_encoding).enc, + &ns(big2_encoding).enc, &ns(little2_encoding).enc, + &ns(utf8_encoding).enc /* NO_ENC */ }; static int PTRCALL NS(initScanProlog)(const ENCODING *enc, const char *ptr, const char *end, - const char **nextTokPtr) -{ - return initScan(NS(encodings), (const INIT_ENCODING *)enc, - XML_PROLOG_STATE, ptr, end, nextTokPtr); + const char **nextTokPtr) { + return initScan(NS(encodings), (const INIT_ENCODING *)enc, XML_PROLOG_STATE, + ptr, end, nextTokPtr); } static int PTRCALL NS(initScanContent)(const ENCODING *enc, const char *ptr, const char *end, - const char **nextTokPtr) -{ - return initScan(NS(encodings), (const INIT_ENCODING *)enc, - XML_CONTENT_STATE, ptr, end, nextTokPtr); + const char **nextTokPtr) { + return initScan(NS(encodings), (const INIT_ENCODING *)enc, XML_CONTENT_STATE, + ptr, end, nextTokPtr); } int NS(XmlInitEncoding)(INIT_ENCODING *p, const ENCODING **encPtr, - const char *name) -{ + const char *name) { int i = getEncodingIndex(name); if (i == UNKNOWN_ENC) return 0; @@ -69,10 +91,9 @@ NS(XmlInitEncoding)(INIT_ENCODING *p, const ENCODING **encPtr, } static const ENCODING * -NS(findEncoding)(const ENCODING *enc, const char *ptr, const char *end) -{ -#define ENCODING_MAX 128 - char buf[ENCODING_MAX]; +NS(findEncoding)(const ENCODING *enc, const char *ptr, const char *end) { +# define ENCODING_MAX 128 + char buf[ENCODING_MAX] = ""; char *p = buf; int i; XmlUtf8Convert(enc, &ptr, end, &p, p + ENCODING_MAX - 1); @@ -88,28 +109,14 @@ NS(findEncoding)(const ENCODING *enc, const char *ptr, const char *end) } int -NS(XmlParseXmlDecl)(int isGeneralTextEntity, - const ENCODING *enc, - const char *ptr, - const char *end, - const char **badPtr, - const char **versionPtr, - const char **versionEndPtr, - const char **encodingName, - const ENCODING **encoding, - int *standalone) -{ - return doParseXmlDecl(NS(findEncoding), - isGeneralTextEntity, - enc, - ptr, - end, - badPtr, - versionPtr, - versionEndPtr, - encodingName, - encoding, - standalone); +NS(XmlParseXmlDecl)(int isGeneralTextEntity, const ENCODING *enc, + const char *ptr, const char *end, const char **badPtr, + const char **versionPtr, const char **versionEndPtr, + const char **encodingName, const ENCODING **encoding, + int *standalone) { + return doParseXmlDecl(NS(findEncoding), isGeneralTextEntity, enc, ptr, end, + badPtr, versionPtr, versionEndPtr, encodingName, + encoding, standalone); } #endif /* XML_TOK_NS_C */ diff --git a/cextern/expat/tests/README.txt b/cextern/expat/tests/README.txt deleted file mode 100755 index 33a47c34abc4..000000000000 --- a/cextern/expat/tests/README.txt +++ /dev/null @@ -1,14 +0,0 @@ -This directory contains the (fledgling) test suite for Expat. The -tests provide general unit testing and regression coverage. The tests -are not expected to be useful examples of Expat usage; see the -examples/ directory for that. - -The Expat tests use a partial internal implementation of the "Check" -unit testing framework for C. More information on Check can be found at: - - http://check.sourceforge.net/ - -Expat must be built and installed before "make check" can be executed. - -Since both Check and this test suite are young, it can all change in a -later version. diff --git a/cextern/expat/tests/benchmark/README.txt b/cextern/expat/tests/benchmark/README.txt deleted file mode 100755 index 7f9cca037ec4..000000000000 --- a/cextern/expat/tests/benchmark/README.txt +++ /dev/null @@ -1,16 +0,0 @@ -Use this benchmark command line utility as follows: - - benchmark [-n] <# iterations> - -The command line arguments are: - - -n ... optional; if supplied, namespace processing is turned on - ... name/path of test xml file - ... size of processing buffer; - the file is parsed in chunks of this size - <# iterations> ... how often will the file be parsed - -Returns: - - The time (in seconds) it takes to parse the test file, - averaged over the number of iterations. \ No newline at end of file diff --git a/cextern/expat/tests/benchmark/benchmark.c b/cextern/expat/tests/benchmark/benchmark.c deleted file mode 100755 index 0f0fd18c1d50..000000000000 --- a/cextern/expat/tests/benchmark/benchmark.c +++ /dev/null @@ -1,114 +0,0 @@ -#include -#include -#include -#include -#include "expat.h" - -#if defined(__amigaos__) && defined(__USE_INLINE__) -#include -#endif - -#ifdef XML_LARGE_SIZE -#define XML_FMT_INT_MOD "ll" -#else -#define XML_FMT_INT_MOD "l" -#endif - -static void -usage(const char *prog, int rc) -{ - fprintf(stderr, - "usage: %s [-n] filename bufferSize nr_of_loops\n", prog); - exit(rc); -} - -int main (int argc, char *argv[]) -{ - XML_Parser parser; - char *XMLBuf, *XMLBufEnd, *XMLBufPtr; - FILE *fd; - struct stat fileAttr; - int nrOfLoops, bufferSize, fileSize, i, isFinal; - int j = 0, ns = 0; - clock_t tstart, tend; - double cpuTime = 0.0; - - if (argc > 1) { - if (argv[1][0] == '-') { - if (argv[1][1] == 'n' && argv[1][2] == '\0') { - ns = 1; - j = 1; - } - else - usage(argv[0], 1); - } - } - - if (argc != j + 4) - usage(argv[0], 1); - - if (stat (argv[j + 1], &fileAttr) != 0) { - fprintf (stderr, "could not access file '%s'\n", argv[j + 1]); - return 2; - } - - fd = fopen (argv[j + 1], "r"); - if (!fd) { - fprintf (stderr, "could not open file '%s'\n", argv[j + 1]); - exit(2); - } - - bufferSize = atoi (argv[j + 2]); - nrOfLoops = atoi (argv[j + 3]); - if (bufferSize <= 0 || nrOfLoops <= 0) { - fprintf (stderr, - "buffer size and nr of loops must be greater than zero.\n"); - exit(3); - } - - XMLBuf = malloc (fileAttr.st_size); - fileSize = fread (XMLBuf, sizeof (char), fileAttr.st_size, fd); - fclose (fd); - - if (ns) - parser = XML_ParserCreateNS(NULL, '!'); - else - parser = XML_ParserCreate(NULL); - - i = 0; - XMLBufEnd = XMLBuf + fileSize; - while (i < nrOfLoops) { - XMLBufPtr = XMLBuf; - isFinal = 0; - tstart = clock(); - do { - int parseBufferSize = XMLBufEnd - XMLBufPtr; - if (parseBufferSize <= bufferSize) - isFinal = 1; - else - parseBufferSize = bufferSize; - if (!XML_Parse (parser, XMLBufPtr, parseBufferSize, isFinal)) { - fprintf (stderr, "error '%s' at line %" XML_FMT_INT_MOD \ - "u character %" XML_FMT_INT_MOD "u\n", - XML_ErrorString (XML_GetErrorCode (parser)), - XML_GetCurrentLineNumber (parser), - XML_GetCurrentColumnNumber (parser)); - free (XMLBuf); - XML_ParserFree (parser); - exit (4); - } - XMLBufPtr += bufferSize; - } while (!isFinal); - tend = clock(); - cpuTime += ((double) (tend - tstart)) / CLOCKS_PER_SEC; - XML_ParserReset(parser, NULL); - i++; - } - - XML_ParserFree (parser); - free (XMLBuf); - - printf ("%d loops, with buffer size %d. Average time per loop: %f\n", - nrOfLoops, bufferSize, cpuTime / (double) nrOfLoops); - return 0; -} diff --git a/cextern/expat/tests/benchmark/benchmark.dsp b/cextern/expat/tests/benchmark/benchmark.dsp deleted file mode 100755 index 6f310d0a931f..000000000000 --- a/cextern/expat/tests/benchmark/benchmark.dsp +++ /dev/null @@ -1,88 +0,0 @@ -# Microsoft Developer Studio Project File - Name="benchmark" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Console Application" 0x0103 - -CFG=benchmark - Win32 Debug -!MESSAGE This is not a valid makefile. To build this project using NMAKE, -!MESSAGE use the Export Makefile command and run -!MESSAGE -!MESSAGE NMAKE /f "benchmark.mak". -!MESSAGE -!MESSAGE You can specify a configuration when running NMAKE -!MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE -!MESSAGE NMAKE /f "benchmark.mak" CFG="benchmark - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "benchmark - Win32 Release" (based on "Win32 (x86) Console Application") -!MESSAGE "benchmark - Win32 Debug" (based on "Win32 (x86) Console Application") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -CPP=cl.exe -RSC=rc.exe - -!IF "$(CFG)" == "benchmark - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "Release" -# PROP BASE Intermediate_Dir "Release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "Release" -# PROP Intermediate_Dir "Release" -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /W3 /GX /O2 /I "..\..\lib" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD BASE RSC /l 0x1009 /d "NDEBUG" -# ADD RSC /l 0x1009 /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 - -!ELSEIF "$(CFG)" == "benchmark - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "Debug" -# PROP BASE Intermediate_Dir "Debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "Debug" -# PROP Intermediate_Dir "Debug" -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\..\lib" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c -# ADD BASE RSC /l 0x1009 /d "_DEBUG" -# ADD RSC /l 0x1009 /d "_DEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept - -!ENDIF - -# Begin Target - -# Name "benchmark - Win32 Release" -# Name "benchmark - Win32 Debug" -# Begin Source File - -SOURCE=.\benchmark.c -# End Source File -# End Target -# End Project diff --git a/cextern/expat/tests/benchmark/benchmark.dsw b/cextern/expat/tests/benchmark/benchmark.dsw deleted file mode 100755 index 3346a9ad9863..000000000000 --- a/cextern/expat/tests/benchmark/benchmark.dsw +++ /dev/null @@ -1,44 +0,0 @@ -Microsoft Developer Studio Workspace File, Format Version 6.00 -# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! - -############################################################################### - -Project: "benchmark"=.\benchmark.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ - Begin Project Dependency - Project_Dep_Name expat - End Project Dependency -}}} - -############################################################################### - -Project: "expat"=..\..\lib\expat.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Global: - -Package=<5> -{{{ -}}} - -Package=<3> -{{{ -}}} - -############################################################################### - diff --git a/cextern/expat/tests/chardata.c b/cextern/expat/tests/chardata.c deleted file mode 100755 index 5fb0299d88e0..000000000000 --- a/cextern/expat/tests/chardata.c +++ /dev/null @@ -1,131 +0,0 @@ -/* Copyright (c) 1998-2003 Thai Open Source Software Center Ltd - See the file COPYING for copying permission. - - chardata.c -*/ - -#ifdef HAVE_EXPAT_CONFIG_H -#include -#endif -#ifdef HAVE_CHECK_H -#include -#else -#include "minicheck.h" -#endif - -#include -#include -#include - -#include "chardata.h" - - -static int -xmlstrlen(const XML_Char *s) -{ - int len = 0; - assert(s != NULL); - while (s[len] != 0) - ++len; - return len; -} - - -void -CharData_Init(CharData *storage) -{ - assert(storage != NULL); - storage->count = -1; -} - -void -CharData_AppendString(CharData *storage, const char *s) -{ - int maxchars = sizeof(storage->data) / sizeof(storage->data[0]); - int len; - - assert(s != NULL); - len = strlen(s); - if (storage->count < 0) - storage->count = 0; - if ((len + storage->count) > maxchars) { - len = (maxchars - storage->count); - } - if (len + storage->count < sizeof(storage->data)) { - memcpy(storage->data + storage->count, s, len); - storage->count += len; - } -} - -void -CharData_AppendXMLChars(CharData *storage, const XML_Char *s, int len) -{ - int maxchars; - - assert(storage != NULL); - assert(s != NULL); - maxchars = sizeof(storage->data) / sizeof(storage->data[0]); - if (storage->count < 0) - storage->count = 0; - if (len < 0) - len = xmlstrlen(s); - if ((len + storage->count) > maxchars) { - len = (maxchars - storage->count); - } - if (len + storage->count < sizeof(storage->data)) { - memcpy(storage->data + storage->count, s, - len * sizeof(storage->data[0])); - storage->count += len; - } -} - -int -CharData_CheckString(CharData *storage, const char *expected) -{ - char buffer[1280]; - int len; - int count; - - assert(storage != NULL); - assert(expected != NULL); - count = (storage->count < 0) ? 0 : storage->count; - len = strlen(expected); - if (len != count) { - if (sizeof(XML_Char) == 1) - sprintf(buffer, "wrong number of data characters:" - " got %d, expected %d:\n%s", count, len, storage->data); - else - sprintf(buffer, - "wrong number of data characters: got %d, expected %d", - count, len); - fail(buffer); - return 0; - } - if (memcmp(expected, storage->data, len) != 0) { - fail("got bad data bytes"); - return 0; - } - return 1; -} - -int -CharData_CheckXMLChars(CharData *storage, const XML_Char *expected) -{ - char buffer[1024]; - int len = xmlstrlen(expected); - int count; - - assert(storage != NULL); - count = (storage->count < 0) ? 0 : storage->count; - if (len != count) { - sprintf(buffer, "wrong number of data characters: got %d, expected %d", - count, len); - fail(buffer); - return 0; - } - if (memcmp(expected, storage->data, len * sizeof(storage->data[0])) != 0) { - fail("got bad data bytes"); - return 0; - } - return 1; -} diff --git a/cextern/expat/tests/chardata.h b/cextern/expat/tests/chardata.h deleted file mode 100755 index e8dc4ce22c42..000000000000 --- a/cextern/expat/tests/chardata.h +++ /dev/null @@ -1,40 +0,0 @@ -/* chardata.h - - Interface to some helper routines used to accumulate and check text - and attribute content. -*/ - -#ifdef __cplusplus -extern "C" { -#endif - -#ifndef XML_CHARDATA_H -#define XML_CHARDATA_H 1 - -#ifndef XML_VERSION -#include "expat.h" /* need XML_Char */ -#endif - - -typedef struct { - int count; /* # of chars, < 0 if not set */ - XML_Char data[1024]; -} CharData; - - -void CharData_Init(CharData *storage); - -void CharData_AppendString(CharData *storage, const char *s); - -void CharData_AppendXMLChars(CharData *storage, const XML_Char *s, int len); - -int CharData_CheckString(CharData *storage, const char *s); - -int CharData_CheckXMLChars(CharData *storage, const XML_Char *s); - - -#endif /* XML_CHARDATA_H */ - -#ifdef __cplusplus -} -#endif diff --git a/cextern/expat/tests/minicheck.c b/cextern/expat/tests/minicheck.c deleted file mode 100755 index d2f4295ffb9e..000000000000 --- a/cextern/expat/tests/minicheck.c +++ /dev/null @@ -1,182 +0,0 @@ -/* Miniature re-implementation of the "check" library. - * - * This is intended to support just enough of check to run the Expat - * tests. This interface is based entirely on the portion of the - * check library being used. - */ - -#include -#include -#include -#include - -#include "minicheck.h" - -Suite * -suite_create(char *name) -{ - Suite *suite = (Suite *) calloc(1, sizeof(Suite)); - if (suite != NULL) { - suite->name = name; - } - return suite; -} - -TCase * -tcase_create(char *name) -{ - TCase *tc = (TCase *) calloc(1, sizeof(TCase)); - if (tc != NULL) { - tc->name = name; - } - return tc; -} - -void -suite_add_tcase(Suite *suite, TCase *tc) -{ - assert(suite != NULL); - assert(tc != NULL); - assert(tc->next_tcase == NULL); - - tc->next_tcase = suite->tests; - suite->tests = tc; -} - -void -tcase_add_checked_fixture(TCase *tc, - tcase_setup_function setup, - tcase_teardown_function teardown) -{ - assert(tc != NULL); - tc->setup = setup; - tc->teardown = teardown; -} - -void -tcase_add_test(TCase *tc, tcase_test_function test) -{ - assert(tc != NULL); - if (tc->allocated == tc->ntests) { - int nalloc = tc->allocated + 100; - size_t new_size = sizeof(tcase_test_function) * nalloc; - tcase_test_function *new_tests = realloc(tc->tests, new_size); - assert(new_tests != NULL); - if (new_tests != tc->tests) { - free(tc->tests); - tc->tests = new_tests; - } - tc->allocated = nalloc; - } - tc->tests[tc->ntests] = test; - tc->ntests++; -} - -SRunner * -srunner_create(Suite *suite) -{ - SRunner *runner = calloc(1, sizeof(SRunner)); - if (runner != NULL) { - runner->suite = suite; - } - return runner; -} - -static jmp_buf env; - -static char const *_check_current_function = NULL; -static int _check_current_lineno = -1; -static char const *_check_current_filename = NULL; - -void -_check_set_test_info(char const *function, char const *filename, int lineno) -{ - _check_current_function = function; - _check_current_lineno = lineno; - _check_current_filename = filename; -} - - -static void -add_failure(SRunner *runner, int verbosity) -{ - runner->nfailures++; - if (verbosity >= CK_VERBOSE) { - printf("%s:%d: %s\n", _check_current_filename, - _check_current_lineno, _check_current_function); - } -} - -void -srunner_run_all(SRunner *runner, int verbosity) -{ - Suite *suite; - TCase *tc; - assert(runner != NULL); - suite = runner->suite; - tc = suite->tests; - while (tc != NULL) { - int i; - for (i = 0; i < tc->ntests; ++i) { - runner->nchecks++; - - if (tc->setup != NULL) { - /* setup */ - if (setjmp(env)) { - add_failure(runner, verbosity); - continue; - } - tc->setup(); - } - /* test */ - if (setjmp(env)) { - add_failure(runner, verbosity); - continue; - } - (tc->tests[i])(); - - /* teardown */ - if (tc->teardown != NULL) { - if (setjmp(env)) { - add_failure(runner, verbosity); - continue; - } - tc->teardown(); - } - } - tc = tc->next_tcase; - } - if (verbosity) { - int passed = runner->nchecks - runner->nfailures; - double percentage = ((double) passed) / runner->nchecks; - int display = (int) (percentage * 100); - printf("%d%%: Checks: %d, Failed: %d\n", - display, runner->nchecks, runner->nfailures); - } -} - -void -_fail_unless(int condition, const char *file, int line, char *msg) -{ - /* Always print the error message so it isn't lost. In this case, - we have a failure, so there's no reason to be quiet about what - it is. - */ - if (msg != NULL) - printf("%s", msg); - longjmp(env, 1); -} - -int -srunner_ntests_failed(SRunner *runner) -{ - assert(runner != NULL); - return runner->nfailures; -} - -void -srunner_free(SRunner *runner) -{ - free(runner->suite); - free(runner); -} diff --git a/cextern/expat/tests/minicheck.h b/cextern/expat/tests/minicheck.h deleted file mode 100755 index f846bead1a8e..000000000000 --- a/cextern/expat/tests/minicheck.h +++ /dev/null @@ -1,89 +0,0 @@ -/* Miniature re-implementation of the "check" library. - * - * This is intended to support just enough of check to run the Expat - * tests. This interface is based entirely on the portion of the - * check library being used. - * - * This is *source* compatible, but not necessary *link* compatible. - */ - -#ifdef __cplusplus -extern "C" { -#endif - -#define CK_NOFORK 0 -#define CK_FORK 1 - -#define CK_SILENT 0 -#define CK_NORMAL 1 -#define CK_VERBOSE 2 - -/* Workaround for Tru64 Unix systems where the C compiler has a working - __func__, but the C++ compiler only has a working __FUNCTION__. This - could be fixed in configure.in, but it's not worth it right now. */ -#if defined(__osf__) && defined(__cplusplus) -#define __func__ __FUNCTION__ -#endif - -#define START_TEST(testname) static void testname(void) { \ - _check_set_test_info(__func__, __FILE__, __LINE__); \ - { -#define END_TEST } } - -#define fail(msg) _fail_unless(0, __FILE__, __LINE__, msg) - -typedef void (*tcase_setup_function)(void); -typedef void (*tcase_teardown_function)(void); -typedef void (*tcase_test_function)(void); - -typedef struct SRunner SRunner; -typedef struct Suite Suite; -typedef struct TCase TCase; - -struct SRunner { - Suite *suite; - int nchecks; - int nfailures; -}; - -struct Suite { - char *name; - TCase *tests; -}; - -struct TCase { - char *name; - tcase_setup_function setup; - tcase_teardown_function teardown; - tcase_test_function *tests; - int ntests; - int allocated; - TCase *next_tcase; -}; - - -/* Internal helper. */ -void _check_set_test_info(char const *function, - char const *filename, int lineno); - - -/* - * Prototypes for the actual implementation. - */ - -void _fail_unless(int condition, const char *file, int line, char *msg); -Suite *suite_create(char *name); -TCase *tcase_create(char *name); -void suite_add_tcase(Suite *suite, TCase *tc); -void tcase_add_checked_fixture(TCase *, - tcase_setup_function, - tcase_teardown_function); -void tcase_add_test(TCase *tc, tcase_test_function test); -SRunner *srunner_create(Suite *suite); -void srunner_run_all(SRunner *runner, int verbosity); -int srunner_ntests_failed(SRunner *runner); -void srunner_free(SRunner *runner); - -#ifdef __cplusplus -} -#endif diff --git a/cextern/expat/tests/runtests.c b/cextern/expat/tests/runtests.c deleted file mode 100755 index 3521c086bbbb..000000000000 --- a/cextern/expat/tests/runtests.c +++ /dev/null @@ -1,1514 +0,0 @@ -/* Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd - See the file COPYING for copying permission. - - runtest.c : run the Expat test suite -*/ - -#ifdef HAVE_EXPAT_CONFIG_H -#include -#endif - -#include -#include -#include -#include - -#include "expat.h" -#include "chardata.h" -#include "minicheck.h" - -#if defined(__amigaos__) && defined(__USE_INLINE__) -#include -#endif - -#ifdef XML_LARGE_SIZE -#define XML_FMT_INT_MOD "ll" -#else -#define XML_FMT_INT_MOD "l" -#endif - -static XML_Parser parser; - - -static void -basic_setup(void) -{ - parser = XML_ParserCreate(NULL); - if (parser == NULL) - fail("Parser not created."); -} - -static void -basic_teardown(void) -{ - if (parser != NULL) - XML_ParserFree(parser); -} - -/* Generate a failure using the parser state to create an error message; - this should be used when the parser reports an error we weren't - expecting. -*/ -static void -_xml_failure(XML_Parser parser, const char *file, int line) -{ - char buffer[1024]; - enum XML_Error err = XML_GetErrorCode(parser); - sprintf(buffer, - " %d: %s (line %" XML_FMT_INT_MOD "u, offset %"\ - XML_FMT_INT_MOD "u)\n reported from %s, line %d\n", - err, - XML_ErrorString(err), - XML_GetCurrentLineNumber(parser), - XML_GetCurrentColumnNumber(parser), - file, line); - _fail_unless(0, file, line, buffer); -} - -#define xml_failure(parser) _xml_failure((parser), __FILE__, __LINE__) - -static void -_expect_failure(char *text, enum XML_Error errorCode, char *errorMessage, - char *file, int lineno) -{ - if (XML_Parse(parser, text, strlen(text), XML_TRUE) == XML_STATUS_OK) - /* Hackish use of _fail_unless() macro, but let's us report - the right filename and line number. */ - _fail_unless(0, file, lineno, errorMessage); - if (XML_GetErrorCode(parser) != errorCode) - _xml_failure(parser, file, lineno); -} - -#define expect_failure(text, errorCode, errorMessage) \ - _expect_failure((text), (errorCode), (errorMessage), \ - __FILE__, __LINE__) - -/* Dummy handlers for when we need to set a handler to tickle a bug, - but it doesn't need to do anything. -*/ - -static void XMLCALL -dummy_start_doctype_handler(void *userData, - const XML_Char *doctypeName, - const XML_Char *sysid, - const XML_Char *pubid, - int has_internal_subset) -{} - -static void XMLCALL -dummy_end_doctype_handler(void *userData) -{} - -static void XMLCALL -dummy_entity_decl_handler(void *userData, - const XML_Char *entityName, - int is_parameter_entity, - const XML_Char *value, - int value_length, - const XML_Char *base, - const XML_Char *systemId, - const XML_Char *publicId, - const XML_Char *notationName) -{} - -static void XMLCALL -dummy_notation_decl_handler(void *userData, - const XML_Char *notationName, - const XML_Char *base, - const XML_Char *systemId, - const XML_Char *publicId) -{} - -static void XMLCALL -dummy_element_decl_handler(void *userData, - const XML_Char *name, - XML_Content *model) -{} - -static void XMLCALL -dummy_attlist_decl_handler(void *userData, - const XML_Char *elname, - const XML_Char *attname, - const XML_Char *att_type, - const XML_Char *dflt, - int isrequired) -{} - -static void XMLCALL -dummy_comment_handler(void *userData, const XML_Char *data) -{} - -static void XMLCALL -dummy_pi_handler(void *userData, const XML_Char *target, const XML_Char *data) -{} - -static void XMLCALL -dummy_start_element(void *userData, - const XML_Char *name, const XML_Char **atts) -{} - - -/* - * Character & encoding tests. - */ - -START_TEST(test_nul_byte) -{ - char text[] = "\0"; - - /* test that a NUL byte (in US-ASCII data) is an error */ - if (XML_Parse(parser, text, sizeof(text) - 1, XML_TRUE) == XML_STATUS_OK) - fail("Parser did not report error on NUL-byte."); - if (XML_GetErrorCode(parser) != XML_ERROR_INVALID_TOKEN) - xml_failure(parser); -} -END_TEST - - -START_TEST(test_u0000_char) -{ - /* test that a NUL byte (in US-ASCII data) is an error */ - expect_failure("", - XML_ERROR_BAD_CHAR_REF, - "Parser did not report error on NUL-byte."); -} -END_TEST - -START_TEST(test_bom_utf8) -{ - /* This test is really just making sure we don't core on a UTF-8 BOM. */ - char *text = "\357\273\277"; - - if (XML_Parse(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) - xml_failure(parser); -} -END_TEST - -START_TEST(test_bom_utf16_be) -{ - char text[] = "\376\377\0<\0e\0/\0>"; - - if (XML_Parse(parser, text, sizeof(text)-1, XML_TRUE) == XML_STATUS_ERROR) - xml_failure(parser); -} -END_TEST - -START_TEST(test_bom_utf16_le) -{ - char text[] = "\377\376<\0e\0/\0>\0"; - - if (XML_Parse(parser, text, sizeof(text)-1, XML_TRUE) == XML_STATUS_ERROR) - xml_failure(parser); -} -END_TEST - -static void XMLCALL -accumulate_characters(void *userData, const XML_Char *s, int len) -{ - CharData_AppendXMLChars((CharData *)userData, s, len); -} - -static void XMLCALL -accumulate_attribute(void *userData, const XML_Char *name, - const XML_Char **atts) -{ - CharData *storage = (CharData *)userData; - if (storage->count < 0 && atts != NULL && atts[0] != NULL) { - /* "accumulate" the value of the first attribute we see */ - CharData_AppendXMLChars(storage, atts[1], -1); - } -} - - -static void -_run_character_check(XML_Char *text, XML_Char *expected, - const char *file, int line) -{ - CharData storage; - - CharData_Init(&storage); - XML_SetUserData(parser, &storage); - XML_SetCharacterDataHandler(parser, accumulate_characters); - if (XML_Parse(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) - _xml_failure(parser, file, line); - CharData_CheckXMLChars(&storage, expected); -} - -#define run_character_check(text, expected) \ - _run_character_check(text, expected, __FILE__, __LINE__) - -static void -_run_attribute_check(XML_Char *text, XML_Char *expected, - const char *file, int line) -{ - CharData storage; - - CharData_Init(&storage); - XML_SetUserData(parser, &storage); - XML_SetStartElementHandler(parser, accumulate_attribute); - if (XML_Parse(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) - _xml_failure(parser, file, line); - CharData_CheckXMLChars(&storage, expected); -} - -#define run_attribute_check(text, expected) \ - _run_attribute_check(text, expected, __FILE__, __LINE__) - -/* Regression test for SF bug #491986. */ -START_TEST(test_danish_latin1) -{ - char *text = - "\n" - "J\xF8rgen \xE6\xF8\xE5\xC6\xD8\xC5"; - run_character_check(text, - "J\xC3\xB8rgen \xC3\xA6\xC3\xB8\xC3\xA5\xC3\x86\xC3\x98\xC3\x85"); -} -END_TEST - - -/* Regression test for SF bug #514281. */ -START_TEST(test_french_charref_hexidecimal) -{ - char *text = - "\n" - "éèàçêÈ"; - run_character_check(text, - "\xC3\xA9\xC3\xA8\xC3\xA0\xC3\xA7\xC3\xAA\xC3\x88"); -} -END_TEST - -START_TEST(test_french_charref_decimal) -{ - char *text = - "\n" - "éèàçêÈ"; - run_character_check(text, - "\xC3\xA9\xC3\xA8\xC3\xA0\xC3\xA7\xC3\xAA\xC3\x88"); -} -END_TEST - -START_TEST(test_french_latin1) -{ - char *text = - "\n" - "\xE9\xE8\xE0\xE7\xEa\xC8"; - run_character_check(text, - "\xC3\xA9\xC3\xA8\xC3\xA0\xC3\xA7\xC3\xAA\xC3\x88"); -} -END_TEST - -START_TEST(test_french_utf8) -{ - char *text = - "\n" - "\xC3\xA9"; - run_character_check(text, "\xC3\xA9"); -} -END_TEST - -/* Regression test for SF bug #600479. - XXX There should be a test that exercises all legal XML Unicode - characters as PCDATA and attribute value content, and XML Name - characters as part of element and attribute names. -*/ -START_TEST(test_utf8_false_rejection) -{ - char *text = "\xEF\xBA\xBF"; - run_character_check(text, "\xEF\xBA\xBF"); -} -END_TEST - -/* Regression test for SF bug #477667. - This test assures that any 8-bit character followed by a 7-bit - character will not be mistakenly interpreted as a valid UTF-8 - sequence. -*/ -START_TEST(test_illegal_utf8) -{ - char text[100]; - int i; - - for (i = 128; i <= 255; ++i) { - sprintf(text, "%ccd", i); - if (XML_Parse(parser, text, strlen(text), XML_TRUE) == XML_STATUS_OK) { - sprintf(text, - "expected token error for '%c' (ordinal %d) in UTF-8 text", - i, i); - fail(text); - } - else if (XML_GetErrorCode(parser) != XML_ERROR_INVALID_TOKEN) - xml_failure(parser); - /* Reset the parser since we use the same parser repeatedly. */ - XML_ParserReset(parser, NULL); - } -} -END_TEST - -START_TEST(test_utf16) -{ - /* - some text - */ - char text[] = - "\000<\000?\000x\000m\000\154\000 \000v\000e\000r\000s\000i\000o" - "\000n\000=\000'\0001\000.\000\060\000'\000 \000e\000n\000c\000o" - "\000d\000i\000n\000g\000=\000'\000U\000T\000F\000-\0001\000\066" - "\000'\000?\000>\000\n" - "\000<\000d\000o\000c\000 \000a\000=\000'\0001\0002\0003\000'" - "\000>\000s\000o\000m\000e\000 \000t\000e\000x\000t\000<\000/" - "\000d\000o\000c\000>"; - if (XML_Parse(parser, text, sizeof(text)-1, XML_TRUE) == XML_STATUS_ERROR) - xml_failure(parser); -} -END_TEST - -START_TEST(test_utf16_le_epilog_newline) -{ - unsigned int first_chunk_bytes = 17; - char text[] = - "\xFF\xFE" /* BOM */ - "<\000e\000/\000>\000" /* document element */ - "\r\000\n\000\r\000\n\000"; /* epilog */ - - if (first_chunk_bytes >= sizeof(text) - 1) - fail("bad value of first_chunk_bytes"); - if ( XML_Parse(parser, text, first_chunk_bytes, XML_FALSE) - == XML_STATUS_ERROR) - xml_failure(parser); - else { - enum XML_Status rc; - rc = XML_Parse(parser, text + first_chunk_bytes, - sizeof(text) - first_chunk_bytes - 1, XML_TRUE); - if (rc == XML_STATUS_ERROR) - xml_failure(parser); - } -} -END_TEST - -/* Regression test for SF bug #481609, #774028. */ -START_TEST(test_latin1_umlauts) -{ - char *text = - "\n" - "\xE4 \xF6 \xFC ä ö ü ä ö ü >"; - char *utf8 = - "\xC3\xA4 \xC3\xB6 \xC3\xBC " - "\xC3\xA4 \xC3\xB6 \xC3\xBC " - "\xC3\xA4 \xC3\xB6 \xC3\xBC >"; - run_character_check(text, utf8); - XML_ParserReset(parser, NULL); - run_attribute_check(text, utf8); -} -END_TEST - -/* Regression test #1 for SF bug #653180. */ -START_TEST(test_line_number_after_parse) -{ - char *text = - "\n" - "\n" - "\n"; - XML_Size lineno; - - if (XML_Parse(parser, text, strlen(text), XML_FALSE) == XML_STATUS_ERROR) - xml_failure(parser); - lineno = XML_GetCurrentLineNumber(parser); - if (lineno != 4) { - char buffer[100]; - sprintf(buffer, - "expected 4 lines, saw %" XML_FMT_INT_MOD "u", lineno); - fail(buffer); - } -} -END_TEST - -/* Regression test #2 for SF bug #653180. */ -START_TEST(test_column_number_after_parse) -{ - char *text = ""; - XML_Size colno; - - if (XML_Parse(parser, text, strlen(text), XML_FALSE) == XML_STATUS_ERROR) - xml_failure(parser); - colno = XML_GetCurrentColumnNumber(parser); - if (colno != 11) { - char buffer[100]; - sprintf(buffer, - "expected 11 columns, saw %" XML_FMT_INT_MOD "u", colno); - fail(buffer); - } -} -END_TEST - -static void XMLCALL -start_element_event_handler2(void *userData, const XML_Char *name, - const XML_Char **attr) -{ - CharData *storage = (CharData *) userData; - char buffer[100]; - - sprintf(buffer, - "<%s> at col:%" XML_FMT_INT_MOD "u line:%"\ - XML_FMT_INT_MOD "u\n", name, - XML_GetCurrentColumnNumber(parser), - XML_GetCurrentLineNumber(parser)); - CharData_AppendString(storage, buffer); -} - -static void XMLCALL -end_element_event_handler2(void *userData, const XML_Char *name) -{ - CharData *storage = (CharData *) userData; - char buffer[100]; - - sprintf(buffer, - " at col:%" XML_FMT_INT_MOD "u line:%"\ - XML_FMT_INT_MOD "u\n", name, - XML_GetCurrentColumnNumber(parser), - XML_GetCurrentLineNumber(parser)); - CharData_AppendString(storage, buffer); -} - -/* Regression test #3 for SF bug #653180. */ -START_TEST(test_line_and_column_numbers_inside_handlers) -{ - char *text = - "\n" /* Unix end-of-line */ - " \r\n" /* Windows end-of-line */ - " \r" /* Mac OS end-of-line */ - " \n" - " \n" - " \n" - " \n" - ""; - char *expected = - " at col:0 line:1\n" - " at col:2 line:2\n" - " at col:4 line:3\n" - " at col:8 line:3\n" - " at col:2 line:4\n" - " at col:2 line:5\n" - " at col:4 line:6\n" - " at col:8 line:6\n" - " at col:2 line:7\n" - " at col:0 line:8\n"; - CharData storage; - - CharData_Init(&storage); - XML_SetUserData(parser, &storage); - XML_SetStartElementHandler(parser, start_element_event_handler2); - XML_SetEndElementHandler(parser, end_element_event_handler2); - if (XML_Parse(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) - xml_failure(parser); - - CharData_CheckString(&storage, expected); -} -END_TEST - -/* Regression test #4 for SF bug #653180. */ -START_TEST(test_line_number_after_error) -{ - char *text = - "\n" - " \n" - " "; /* missing */ - XML_Size lineno; - if (XML_Parse(parser, text, strlen(text), XML_FALSE) != XML_STATUS_ERROR) - fail("Expected a parse error"); - - lineno = XML_GetCurrentLineNumber(parser); - if (lineno != 3) { - char buffer[100]; - sprintf(buffer, "expected 3 lines, saw %" XML_FMT_INT_MOD "u", lineno); - fail(buffer); - } -} -END_TEST - -/* Regression test #5 for SF bug #653180. */ -START_TEST(test_column_number_after_error) -{ - char *text = - "\n" - " \n" - " "; /* missing */ - XML_Size colno; - if (XML_Parse(parser, text, strlen(text), XML_FALSE) != XML_STATUS_ERROR) - fail("Expected a parse error"); - - colno = XML_GetCurrentColumnNumber(parser); - if (colno != 4) { - char buffer[100]; - sprintf(buffer, - "expected 4 columns, saw %" XML_FMT_INT_MOD "u", colno); - fail(buffer); - } -} -END_TEST - -/* Regression test for SF bug #478332. */ -START_TEST(test_really_long_lines) -{ - /* This parses an input line longer than INIT_DATA_BUF_SIZE - characters long (defined to be 1024 in xmlparse.c). We take a - really cheesy approach to building the input buffer, because - this avoids writing bugs in buffer-filling code. - */ - char *text = - "" - /* 64 chars */ - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+" - /* until we have at least 1024 characters on the line: */ - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+" - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+" - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+" - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+" - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+" - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+" - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+" - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+" - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+" - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+" - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+" - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+" - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+" - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+" - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+" - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+" - ""; - if (XML_Parse(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) - xml_failure(parser); -} -END_TEST - - -/* - * Element event tests. - */ - -static void XMLCALL -end_element_event_handler(void *userData, const XML_Char *name) -{ - CharData *storage = (CharData *) userData; - CharData_AppendString(storage, "/"); - CharData_AppendXMLChars(storage, name, -1); -} - -START_TEST(test_end_element_events) -{ - char *text = ""; - char *expected = "/c/b/f/d/a"; - CharData storage; - - CharData_Init(&storage); - XML_SetUserData(parser, &storage); - XML_SetEndElementHandler(parser, end_element_event_handler); - if (XML_Parse(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) - xml_failure(parser); - CharData_CheckString(&storage, expected); -} -END_TEST - - -/* - * Attribute tests. - */ - -/* Helpers used by the following test; this checks any "attr" and "refs" - attributes to make sure whitespace has been normalized. - - Return true if whitespace has been normalized in a string, using - the rules for attribute value normalization. The 'is_cdata' flag - is needed since CDATA attributes don't need to have multiple - whitespace characters collapsed to a single space, while other - attribute data types do. (Section 3.3.3 of the recommendation.) -*/ -static int -is_whitespace_normalized(const XML_Char *s, int is_cdata) -{ - int blanks = 0; - int at_start = 1; - while (*s) { - if (*s == ' ') - ++blanks; - else if (*s == '\t' || *s == '\n' || *s == '\r') - return 0; - else { - if (at_start) { - at_start = 0; - if (blanks && !is_cdata) - /* illegal leading blanks */ - return 0; - } - else if (blanks > 1 && !is_cdata) - return 0; - blanks = 0; - } - ++s; - } - if (blanks && !is_cdata) - return 0; - return 1; -} - -/* Check the attribute whitespace checker: */ -static void -testhelper_is_whitespace_normalized(void) -{ - assert(is_whitespace_normalized("abc", 0)); - assert(is_whitespace_normalized("abc", 1)); - assert(is_whitespace_normalized("abc def ghi", 0)); - assert(is_whitespace_normalized("abc def ghi", 1)); - assert(!is_whitespace_normalized(" abc def ghi", 0)); - assert(is_whitespace_normalized(" abc def ghi", 1)); - assert(!is_whitespace_normalized("abc def ghi", 0)); - assert(is_whitespace_normalized("abc def ghi", 1)); - assert(!is_whitespace_normalized("abc def ghi ", 0)); - assert(is_whitespace_normalized("abc def ghi ", 1)); - assert(!is_whitespace_normalized(" ", 0)); - assert(is_whitespace_normalized(" ", 1)); - assert(!is_whitespace_normalized("\t", 0)); - assert(!is_whitespace_normalized("\t", 1)); - assert(!is_whitespace_normalized("\n", 0)); - assert(!is_whitespace_normalized("\n", 1)); - assert(!is_whitespace_normalized("\r", 0)); - assert(!is_whitespace_normalized("\r", 1)); - assert(!is_whitespace_normalized("abc\t def", 1)); -} - -static void XMLCALL -check_attr_contains_normalized_whitespace(void *userData, - const XML_Char *name, - const XML_Char **atts) -{ - int i; - for (i = 0; atts[i] != NULL; i += 2) { - const XML_Char *attrname = atts[i]; - const XML_Char *value = atts[i + 1]; - if (strcmp("attr", attrname) == 0 - || strcmp("ents", attrname) == 0 - || strcmp("refs", attrname) == 0) { - if (!is_whitespace_normalized(value, 0)) { - char buffer[256]; - sprintf(buffer, "attribute value not normalized: %s='%s'", - attrname, value); - fail(buffer); - } - } - } -} - -START_TEST(test_attr_whitespace_normalization) -{ - char *text = - "\n" - "]>\n" - "\n" - " \n" - " \n" - ""; - - XML_SetStartElementHandler(parser, - check_attr_contains_normalized_whitespace); - if (XML_Parse(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) - xml_failure(parser); -} -END_TEST - - -/* - * XML declaration tests. - */ - -START_TEST(test_xmldecl_misplaced) -{ - expect_failure("\n" - "\n" - "", - XML_ERROR_MISPLACED_XML_PI, - "failed to report misplaced XML declaration"); -} -END_TEST - -/* Regression test for SF bug #584832. */ -static int XMLCALL -UnknownEncodingHandler(void *data,const XML_Char *encoding,XML_Encoding *info) -{ - if (strcmp(encoding,"unsupported-encoding") == 0) { - int i; - for (i = 0; i < 256; ++i) - info->map[i] = i; - info->data = NULL; - info->convert = NULL; - info->release = NULL; - return XML_STATUS_OK; - } - return XML_STATUS_ERROR; -} - -START_TEST(test_unknown_encoding_internal_entity) -{ - char *text = - "\n" - "]>\n" - ""; - - XML_SetUnknownEncodingHandler(parser, UnknownEncodingHandler, NULL); - if (XML_Parse(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) - xml_failure(parser); -} -END_TEST - -/* Regression test for SF bug #620106. */ -static int XMLCALL -external_entity_loader_set_encoding(XML_Parser parser, - const XML_Char *context, - const XML_Char *base, - const XML_Char *systemId, - const XML_Char *publicId) -{ - /* This text says it's an unsupported encoding, but it's really - UTF-8, which we tell Expat using XML_SetEncoding(). - */ - char *text = - "" - "\xC3\xA9"; - XML_Parser extparser; - - extparser = XML_ExternalEntityParserCreate(parser, context, NULL); - if (extparser == NULL) - fail("Could not create external entity parser."); - if (!XML_SetEncoding(extparser, "utf-8")) - fail("XML_SetEncoding() ignored for external entity"); - if ( XML_Parse(extparser, text, strlen(text), XML_TRUE) - == XML_STATUS_ERROR) { - xml_failure(parser); - return 0; - } - return 1; -} - -START_TEST(test_ext_entity_set_encoding) -{ - char *text = - "\n" - "]>\n" - "&en;"; - - XML_SetExternalEntityRefHandler(parser, - external_entity_loader_set_encoding); - run_character_check(text, "\xC3\xA9"); -} -END_TEST - -/* Test that no error is reported for unknown entities if we don't - read an external subset. This was fixed in Expat 1.95.5. -*/ -START_TEST(test_wfc_undeclared_entity_unread_external_subset) { - char *text = - "\n" - "&entity;"; - - if (XML_Parse(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) - xml_failure(parser); -} -END_TEST - -/* Test that an error is reported for unknown entities if we don't - have an external subset. -*/ -START_TEST(test_wfc_undeclared_entity_no_external_subset) { - expect_failure("&entity;", - XML_ERROR_UNDEFINED_ENTITY, - "Parser did not report undefined entity w/out a DTD."); -} -END_TEST - -/* Test that an error is reported for unknown entities if we don't - read an external subset, but have been declared standalone. -*/ -START_TEST(test_wfc_undeclared_entity_standalone) { - char *text = - "\n" - "\n" - "&entity;"; - - expect_failure(text, - XML_ERROR_UNDEFINED_ENTITY, - "Parser did not report undefined entity (standalone)."); -} -END_TEST - -static int XMLCALL -external_entity_loader(XML_Parser parser, - const XML_Char *context, - const XML_Char *base, - const XML_Char *systemId, - const XML_Char *publicId) -{ - char *text = (char *)XML_GetUserData(parser); - XML_Parser extparser; - - extparser = XML_ExternalEntityParserCreate(parser, context, NULL); - if (extparser == NULL) - fail("Could not create external entity parser."); - if ( XML_Parse(extparser, text, strlen(text), XML_TRUE) - == XML_STATUS_ERROR) { - xml_failure(parser); - return XML_STATUS_ERROR; - } - return XML_STATUS_OK; -} - -/* Test that an error is reported for unknown entities if we have read - an external subset, and standalone is true. -*/ -START_TEST(test_wfc_undeclared_entity_with_external_subset_standalone) { - char *text = - "\n" - "\n" - "&entity;"; - char *foo_text = - ""; - - XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); - XML_SetUserData(parser, foo_text); - XML_SetExternalEntityRefHandler(parser, external_entity_loader); - expect_failure(text, - XML_ERROR_UNDEFINED_ENTITY, - "Parser did not report undefined entity (external DTD)."); -} -END_TEST - -/* Test that no error is reported for unknown entities if we have read - an external subset, and standalone is false. -*/ -START_TEST(test_wfc_undeclared_entity_with_external_subset) { - char *text = - "\n" - "\n" - "&entity;"; - char *foo_text = - ""; - - XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); - XML_SetUserData(parser, foo_text); - XML_SetExternalEntityRefHandler(parser, external_entity_loader); - if (XML_Parse(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) - xml_failure(parser); -} -END_TEST - -START_TEST(test_wfc_no_recursive_entity_refs) -{ - char *text = - "\n" - "]>\n" - "&entity;"; - - expect_failure(text, - XML_ERROR_RECURSIVE_ENTITY_REF, - "Parser did not report recursive entity reference."); -} -END_TEST - -/* Regression test for SF bug #483514. */ -START_TEST(test_dtd_default_handling) -{ - char *text = - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "]>"; - - XML_SetDefaultHandler(parser, accumulate_characters); - XML_SetDoctypeDeclHandler(parser, - dummy_start_doctype_handler, - dummy_end_doctype_handler); - XML_SetEntityDeclHandler(parser, dummy_entity_decl_handler); - XML_SetNotationDeclHandler(parser, dummy_notation_decl_handler); - XML_SetElementDeclHandler(parser, dummy_element_decl_handler); - XML_SetAttlistDeclHandler(parser, dummy_attlist_decl_handler); - XML_SetProcessingInstructionHandler(parser, dummy_pi_handler); - XML_SetCommentHandler(parser, dummy_comment_handler); - run_character_check(text, "\n\n\n\n\n\n\n"); -} -END_TEST - -/* See related SF bug #673791. - When namespace processing is enabled, setting the namespace URI for - a prefix is not allowed; this test ensures that it *is* allowed - when namespace processing is not enabled. - (See Namespaces in XML, section 2.) -*/ -START_TEST(test_empty_ns_without_namespaces) -{ - char *text = - "\n" - " \n" - ""; - - if (XML_Parse(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) - xml_failure(parser); -} -END_TEST - -/* Regression test for SF bug #824420. - Checks that an xmlns:prefix attribute set in an attribute's default - value isn't misinterpreted. -*/ -START_TEST(test_ns_in_attribute_default_without_namespaces) -{ - char *text = - "\n" - " ]>\n" - ""; - - if (XML_Parse(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) - xml_failure(parser); -} -END_TEST - -static char *long_character_data_text = - "" - "012345678901234567890123456789012345678901234567890123456789" - "012345678901234567890123456789012345678901234567890123456789" - "012345678901234567890123456789012345678901234567890123456789" - "012345678901234567890123456789012345678901234567890123456789" - "012345678901234567890123456789012345678901234567890123456789" - "012345678901234567890123456789012345678901234567890123456789" - "012345678901234567890123456789012345678901234567890123456789" - "012345678901234567890123456789012345678901234567890123456789" - "012345678901234567890123456789012345678901234567890123456789" - "012345678901234567890123456789012345678901234567890123456789" - "012345678901234567890123456789012345678901234567890123456789" - "012345678901234567890123456789012345678901234567890123456789" - "012345678901234567890123456789012345678901234567890123456789" - "012345678901234567890123456789012345678901234567890123456789" - "012345678901234567890123456789012345678901234567890123456789" - "012345678901234567890123456789012345678901234567890123456789" - "012345678901234567890123456789012345678901234567890123456789" - "012345678901234567890123456789012345678901234567890123456789" - "012345678901234567890123456789012345678901234567890123456789" - "012345678901234567890123456789012345678901234567890123456789" - ""; - -static XML_Bool resumable = XML_FALSE; - -static void -clearing_aborting_character_handler(void *userData, - const XML_Char *s, int len) -{ - XML_StopParser(parser, resumable); - XML_SetCharacterDataHandler(parser, NULL); -} - -/* Regression test for SF bug #1515266: missing check of stopped - parser in doContext() 'for' loop. */ -START_TEST(test_stop_parser_between_char_data_calls) -{ - /* The sample data must be big enough that there are two calls to - the character data handler from within the inner "for" loop of - the XML_TOK_DATA_CHARS case in doContent(), and the character - handler must stop the parser and clear the character data - handler. - */ - char *text = long_character_data_text; - - XML_SetCharacterDataHandler(parser, clearing_aborting_character_handler); - resumable = XML_FALSE; - if (XML_Parse(parser, text, strlen(text), XML_TRUE) != XML_STATUS_ERROR) - xml_failure(parser); - if (XML_GetErrorCode(parser) != XML_ERROR_ABORTED) - xml_failure(parser); -} -END_TEST - -/* Regression test for SF bug #1515266: missing check of stopped - parser in doContext() 'for' loop. */ -START_TEST(test_suspend_parser_between_char_data_calls) -{ - /* The sample data must be big enough that there are two calls to - the character data handler from within the inner "for" loop of - the XML_TOK_DATA_CHARS case in doContent(), and the character - handler must stop the parser and clear the character data - handler. - */ - char *text = long_character_data_text; - - XML_SetCharacterDataHandler(parser, clearing_aborting_character_handler); - resumable = XML_TRUE; - if (XML_Parse(parser, text, strlen(text), XML_TRUE) != XML_STATUS_SUSPENDED) - xml_failure(parser); - if (XML_GetErrorCode(parser) != XML_ERROR_NONE) - xml_failure(parser); -} -END_TEST - - -/* - * Namespaces tests. - */ - -static void -namespace_setup(void) -{ - parser = XML_ParserCreateNS(NULL, ' '); - if (parser == NULL) - fail("Parser not created."); -} - -static void -namespace_teardown(void) -{ - basic_teardown(); -} - -/* Check that an element name and attribute name match the expected values. - The expected values are passed as an array reference of string pointers - provided as the userData argument; the first is the expected - element name, and the second is the expected attribute name. -*/ -static void XMLCALL -triplet_start_checker(void *userData, const XML_Char *name, - const XML_Char **atts) -{ - char **elemstr = (char **)userData; - char buffer[1024]; - if (strcmp(elemstr[0], name) != 0) { - sprintf(buffer, "unexpected start string: '%s'", name); - fail(buffer); - } - if (strcmp(elemstr[1], atts[0]) != 0) { - sprintf(buffer, "unexpected attribute string: '%s'", atts[0]); - fail(buffer); - } -} - -/* Check that the element name passed to the end-element handler matches - the expected value. The expected value is passed as the first element - in an array of strings passed as the userData argument. -*/ -static void XMLCALL -triplet_end_checker(void *userData, const XML_Char *name) -{ - char **elemstr = (char **)userData; - if (strcmp(elemstr[0], name) != 0) { - char buffer[1024]; - sprintf(buffer, "unexpected end string: '%s'", name); - fail(buffer); - } -} - -START_TEST(test_return_ns_triplet) -{ - char *text = - ""; - char *elemstr[] = { - "http://expat.sf.net/ e foo", - "http://expat.sf.net/ a bar" - }; - XML_SetReturnNSTriplet(parser, XML_TRUE); - XML_SetUserData(parser, elemstr); - XML_SetElementHandler(parser, triplet_start_checker, triplet_end_checker); - if (XML_Parse(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) - xml_failure(parser); -} -END_TEST - -static void XMLCALL -overwrite_start_checker(void *userData, const XML_Char *name, - const XML_Char **atts) -{ - CharData *storage = (CharData *) userData; - CharData_AppendString(storage, "start "); - CharData_AppendXMLChars(storage, name, -1); - while (*atts != NULL) { - CharData_AppendString(storage, "\nattribute "); - CharData_AppendXMLChars(storage, *atts, -1); - atts += 2; - } - CharData_AppendString(storage, "\n"); -} - -static void XMLCALL -overwrite_end_checker(void *userData, const XML_Char *name) -{ - CharData *storage = (CharData *) userData; - CharData_AppendString(storage, "end "); - CharData_AppendXMLChars(storage, name, -1); - CharData_AppendString(storage, "\n"); -} - -static void -run_ns_tagname_overwrite_test(char *text, char *result) -{ - CharData storage; - CharData_Init(&storage); - XML_SetUserData(parser, &storage); - XML_SetElementHandler(parser, - overwrite_start_checker, overwrite_end_checker); - if (XML_Parse(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) - xml_failure(parser); - CharData_CheckString(&storage, result); -} - -/* Regression test for SF bug #566334. */ -START_TEST(test_ns_tagname_overwrite) -{ - char *text = - "\n" - " \n" - " \n" - ""; - char *result = - "start http://xml.libexpat.org/ e\n" - "start http://xml.libexpat.org/ f\n" - "attribute http://xml.libexpat.org/ attr\n" - "end http://xml.libexpat.org/ f\n" - "start http://xml.libexpat.org/ g\n" - "attribute http://xml.libexpat.org/ attr2\n" - "end http://xml.libexpat.org/ g\n" - "end http://xml.libexpat.org/ e\n"; - run_ns_tagname_overwrite_test(text, result); -} -END_TEST - -/* Regression test for SF bug #566334. */ -START_TEST(test_ns_tagname_overwrite_triplet) -{ - char *text = - "\n" - " \n" - " \n" - ""; - char *result = - "start http://xml.libexpat.org/ e n\n" - "start http://xml.libexpat.org/ f n\n" - "attribute http://xml.libexpat.org/ attr n\n" - "end http://xml.libexpat.org/ f n\n" - "start http://xml.libexpat.org/ g n\n" - "attribute http://xml.libexpat.org/ attr2 n\n" - "end http://xml.libexpat.org/ g n\n" - "end http://xml.libexpat.org/ e n\n"; - XML_SetReturnNSTriplet(parser, XML_TRUE); - run_ns_tagname_overwrite_test(text, result); -} -END_TEST - - -/* Regression test for SF bug #620343. */ -static void XMLCALL -start_element_fail(void *userData, - const XML_Char *name, const XML_Char **atts) -{ - /* We should never get here. */ - fail("should never reach start_element_fail()"); -} - -static void XMLCALL -start_ns_clearing_start_element(void *userData, - const XML_Char *prefix, - const XML_Char *uri) -{ - XML_SetStartElementHandler((XML_Parser) userData, NULL); -} - -START_TEST(test_start_ns_clears_start_element) -{ - /* This needs to use separate start/end tags; using the empty tag - syntax doesn't cause the problematic path through Expat to be - taken. - */ - char *text = ""; - - XML_SetStartElementHandler(parser, start_element_fail); - XML_SetStartNamespaceDeclHandler(parser, start_ns_clearing_start_element); - XML_UseParserAsHandlerArg(parser); - if (XML_Parse(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) - xml_failure(parser); -} -END_TEST - -/* Regression test for SF bug #616863. */ -static int XMLCALL -external_entity_handler(XML_Parser parser, - const XML_Char *context, - const XML_Char *base, - const XML_Char *systemId, - const XML_Char *publicId) -{ - long callno = 1 + (long)XML_GetUserData(parser); - char *text; - XML_Parser p2; - - if (callno == 1) - text = ("\n" - "\n" - "\n"); - else - text = ("" - ""); - - XML_SetUserData(parser, (void *) callno); - p2 = XML_ExternalEntityParserCreate(parser, context, NULL); - if (XML_Parse(p2, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) { - xml_failure(p2); - return 0; - } - XML_ParserFree(p2); - return 1; -} - -START_TEST(test_default_ns_from_ext_subset_and_ext_ge) -{ - char *text = - "\n" - "\n" - "]>\n" - "\n" - "&en;\n" - ""; - - XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); - XML_SetExternalEntityRefHandler(parser, external_entity_handler); - /* We actually need to set this handler to tickle this bug. */ - XML_SetStartElementHandler(parser, dummy_start_element); - XML_SetUserData(parser, NULL); - if (XML_Parse(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) - xml_failure(parser); -} -END_TEST - -/* Regression test #1 for SF bug #673791. */ -START_TEST(test_ns_prefix_with_empty_uri_1) -{ - char *text = - "\n" - " \n" - ""; - - expect_failure(text, - XML_ERROR_UNDECLARING_PREFIX, - "Did not report re-setting namespace" - " URI with prefix to ''."); -} -END_TEST - -/* Regression test #2 for SF bug #673791. */ -START_TEST(test_ns_prefix_with_empty_uri_2) -{ - char *text = - "\n" - ""; - - expect_failure(text, - XML_ERROR_UNDECLARING_PREFIX, - "Did not report setting namespace URI with prefix to ''."); -} -END_TEST - -/* Regression test #3 for SF bug #673791. */ -START_TEST(test_ns_prefix_with_empty_uri_3) -{ - char *text = - "\n" - " \n" - "]>\n" - ""; - - expect_failure(text, - XML_ERROR_UNDECLARING_PREFIX, - "Didn't report attr default setting NS w/ prefix to ''."); -} -END_TEST - -/* Regression test #4 for SF bug #673791. */ -START_TEST(test_ns_prefix_with_empty_uri_4) -{ - char *text = - "\n" - " \n" - "]>\n" - ""; - /* Packaged info expected by the end element handler; - the weird structuring lets us re-use the triplet_end_checker() - function also used for another test. */ - char *elemstr[] = { - "http://xml.libexpat.org/ doc prefix" - }; - XML_SetReturnNSTriplet(parser, XML_TRUE); - XML_SetUserData(parser, elemstr); - XML_SetEndElementHandler(parser, triplet_end_checker); - if (XML_Parse(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) - xml_failure(parser); -} -END_TEST - -START_TEST(test_ns_default_with_empty_uri) -{ - char *text = - "\n" - " \n" - ""; - if (XML_Parse(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) - xml_failure(parser); -} -END_TEST - -/* Regression test for SF bug #692964: two prefixes for one namespace. */ -START_TEST(test_ns_duplicate_attrs_diff_prefixes) -{ - char *text = - ""; - expect_failure(text, - XML_ERROR_DUPLICATE_ATTRIBUTE, - "did not report multiple attributes with same URI+name"); -} -END_TEST - -/* Regression test for SF bug #695401: unbound prefix. */ -START_TEST(test_ns_unbound_prefix_on_attribute) -{ - char *text = ""; - expect_failure(text, - XML_ERROR_UNBOUND_PREFIX, - "did not report unbound prefix on attribute"); -} -END_TEST - -/* Regression test for SF bug #695401: unbound prefix. */ -START_TEST(test_ns_unbound_prefix_on_element) -{ - char *text = ""; - expect_failure(text, - XML_ERROR_UNBOUND_PREFIX, - "did not report unbound prefix on element"); -} -END_TEST - -static Suite * -make_suite(void) -{ - Suite *s = suite_create("basic"); - TCase *tc_basic = tcase_create("basic tests"); - TCase *tc_namespace = tcase_create("XML namespaces"); - - suite_add_tcase(s, tc_basic); - tcase_add_checked_fixture(tc_basic, basic_setup, basic_teardown); - tcase_add_test(tc_basic, test_nul_byte); - tcase_add_test(tc_basic, test_u0000_char); - tcase_add_test(tc_basic, test_bom_utf8); - tcase_add_test(tc_basic, test_bom_utf16_be); - tcase_add_test(tc_basic, test_bom_utf16_le); - tcase_add_test(tc_basic, test_illegal_utf8); - tcase_add_test(tc_basic, test_utf16); - tcase_add_test(tc_basic, test_utf16_le_epilog_newline); - tcase_add_test(tc_basic, test_latin1_umlauts); - /* Regression test for SF bug #491986. */ - tcase_add_test(tc_basic, test_danish_latin1); - /* Regression test for SF bug #514281. */ - tcase_add_test(tc_basic, test_french_charref_hexidecimal); - tcase_add_test(tc_basic, test_french_charref_decimal); - tcase_add_test(tc_basic, test_french_latin1); - tcase_add_test(tc_basic, test_french_utf8); - tcase_add_test(tc_basic, test_utf8_false_rejection); - tcase_add_test(tc_basic, test_line_number_after_parse); - tcase_add_test(tc_basic, test_column_number_after_parse); - tcase_add_test(tc_basic, test_line_and_column_numbers_inside_handlers); - tcase_add_test(tc_basic, test_line_number_after_error); - tcase_add_test(tc_basic, test_column_number_after_error); - tcase_add_test(tc_basic, test_really_long_lines); - tcase_add_test(tc_basic, test_end_element_events); - tcase_add_test(tc_basic, test_attr_whitespace_normalization); - tcase_add_test(tc_basic, test_xmldecl_misplaced); - tcase_add_test(tc_basic, test_unknown_encoding_internal_entity); - tcase_add_test(tc_basic, - test_wfc_undeclared_entity_unread_external_subset); - tcase_add_test(tc_basic, test_wfc_undeclared_entity_no_external_subset); - tcase_add_test(tc_basic, test_wfc_undeclared_entity_standalone); - tcase_add_test(tc_basic, test_wfc_undeclared_entity_with_external_subset); - tcase_add_test(tc_basic, - test_wfc_undeclared_entity_with_external_subset_standalone); - tcase_add_test(tc_basic, test_wfc_no_recursive_entity_refs); - tcase_add_test(tc_basic, test_ext_entity_set_encoding); - tcase_add_test(tc_basic, test_dtd_default_handling); - tcase_add_test(tc_basic, test_empty_ns_without_namespaces); - tcase_add_test(tc_basic, test_ns_in_attribute_default_without_namespaces); - tcase_add_test(tc_basic, test_stop_parser_between_char_data_calls); - tcase_add_test(tc_basic, test_suspend_parser_between_char_data_calls); - - suite_add_tcase(s, tc_namespace); - tcase_add_checked_fixture(tc_namespace, - namespace_setup, namespace_teardown); - tcase_add_test(tc_namespace, test_return_ns_triplet); - tcase_add_test(tc_namespace, test_ns_tagname_overwrite); - tcase_add_test(tc_namespace, test_ns_tagname_overwrite_triplet); - tcase_add_test(tc_namespace, test_start_ns_clears_start_element); - tcase_add_test(tc_namespace, test_default_ns_from_ext_subset_and_ext_ge); - tcase_add_test(tc_namespace, test_ns_prefix_with_empty_uri_1); - tcase_add_test(tc_namespace, test_ns_prefix_with_empty_uri_2); - tcase_add_test(tc_namespace, test_ns_prefix_with_empty_uri_3); - tcase_add_test(tc_namespace, test_ns_prefix_with_empty_uri_4); - tcase_add_test(tc_namespace, test_ns_default_with_empty_uri); - tcase_add_test(tc_namespace, test_ns_duplicate_attrs_diff_prefixes); - tcase_add_test(tc_namespace, test_ns_unbound_prefix_on_attribute); - tcase_add_test(tc_namespace, test_ns_unbound_prefix_on_element); - - return s; -} - - -int -main(int argc, char *argv[]) -{ - int i, nf; - int verbosity = CK_NORMAL; - Suite *s = make_suite(); - SRunner *sr = srunner_create(s); - - /* run the tests for internal helper functions */ - testhelper_is_whitespace_normalized(); - - for (i = 1; i < argc; ++i) { - char *opt = argv[i]; - if (strcmp(opt, "-v") == 0 || strcmp(opt, "--verbose") == 0) - verbosity = CK_VERBOSE; - else if (strcmp(opt, "-q") == 0 || strcmp(opt, "--quiet") == 0) - verbosity = CK_SILENT; - else { - fprintf(stderr, "runtests: unknown option '%s'\n", opt); - return 2; - } - } - if (verbosity != CK_SILENT) - printf("Expat version: %s\n", XML_ExpatVersion()); - srunner_run_all(sr, verbosity); - nf = srunner_ntests_failed(sr); - srunner_free(sr); - - return (nf == 0) ? EXIT_SUCCESS : EXIT_FAILURE; -} diff --git a/cextern/expat/tests/runtestspp.cpp b/cextern/expat/tests/runtestspp.cpp deleted file mode 100755 index c35dc583d57a..000000000000 --- a/cextern/expat/tests/runtestspp.cpp +++ /dev/null @@ -1,6 +0,0 @@ -// C++ compilation harness for the test suite. -// -// This is used to ensure the Expat headers can be included from C++ -// and have everything work as expected. -// -#include "runtests.c" diff --git a/cextern/expat/tests/xmltest.sh b/cextern/expat/tests/xmltest.sh deleted file mode 100755 index 725441edb69e..000000000000 --- a/cextern/expat/tests/xmltest.sh +++ /dev/null @@ -1,141 +0,0 @@ -#! /bin/sh - -# EXPAT TEST SCRIPT FOR W3C XML TEST SUITE - -# This script can be used to exercise Expat against the -# w3c.org xml test suite, available from -# http://www.w3.org/XML/Test/xmlts20020606.zip. - -# To run this script, first set XMLWF so that xmlwf can be -# found, then set the output directory with OUTPUT. - -# The script lists all test cases where Expat shows a discrepancy -# from the expected result. Test cases where only the canonical -# output differs are prefixed with "Output differs:", and a diff file -# is generated in the appropriate subdirectory under $OUTPUT. - -# If there are output files provided, the script will use -# output from xmlwf and compare the desired output against it. -# However, one has to take into account that the canonical output -# produced by xmlwf conforms to an older definition of canonical XML -# and does not generate notation declarations. - -MYDIR="`dirname \"$0\"`" -cd "$MYDIR" -MYDIR="`pwd`" -XMLWF="`dirname \"$MYDIR\"`/xmlwf/xmlwf" -# XMLWF=/usr/local/bin/xmlwf -TS="$MYDIR/XML-Test-Suite" -# OUTPUT must terminate with the directory separator. -OUTPUT="$TS/out/" -# OUTPUT=/home/tmp/xml-testsuite-out/ - - -# RunXmlwfNotWF file reldir -# reldir includes trailing slash -RunXmlwfNotWF() { - file="$1" - reldir="$2" - $XMLWF -p "$file" > outfile || return $? - read outdata < outfile - if test "$outdata" = "" ; then - echo "Expected well-formed: $reldir$file" - return 1 - else - return 0 - fi -} - -# RunXmlwfWF file reldir -# reldir includes trailing slash -RunXmlwfWF() { - file="$1" - reldir="$2" - $XMLWF -p -d "$OUTPUT$reldir" "$file" > outfile || return $? - read outdata < outfile - if test "$outdata" = "" ; then - if [ -f "out/$file" ] ; then - diff "$OUTPUT$reldir$file" "out/$file" > outfile - if [ -s outfile ] ; then - cp outfile "$OUTPUT$reldir$file.diff" - echo "Output differs: $reldir$file" - return 1 - fi - fi - return 0 - else - echo "In $reldir: $outdata" - return 1 - fi -} - -SUCCESS=0 -ERROR=0 - -UpdateStatus() { - if [ "$1" -eq 0 ] ; then - SUCCESS=`expr $SUCCESS + 1` - else - ERROR=`expr $ERROR + 1` - fi -} - -########################## -# well-formed test cases # -########################## - -cd "$TS/xmlconf" -for xmldir in ibm/valid/P* \ - ibm/invalid/P* \ - xmltest/valid/ext-sa \ - xmltest/valid/not-sa \ - xmltest/invalid \ - xmltest/invalid/not-sa \ - xmltest/valid/sa \ - sun/valid \ - sun/invalid ; do - cd "$TS/xmlconf/$xmldir" - mkdir -p "$OUTPUT$xmldir" - for xmlfile in *.xml ; do - RunXmlwfWF "$xmlfile" "$xmldir/" - UpdateStatus $? - done - rm outfile -done - -cd "$TS/xmlconf/oasis" -mkdir -p "$OUTPUT"oasis -for xmlfile in *pass*.xml ; do - RunXmlwfWF "$xmlfile" "oasis/" - UpdateStatus $? -done -rm outfile - -############################## -# not well-formed test cases # -############################## - -cd "$TS/xmlconf" -for xmldir in ibm/not-wf/P* \ - ibm/not-wf/misc \ - xmltest/not-wf/ext-sa \ - xmltest/not-wf/not-sa \ - xmltest/not-wf/sa \ - sun/not-wf ; do - cd "$TS/xmlconf/$xmldir" - for xmlfile in *.xml ; do - RunXmlwfNotWF "$xmlfile" "$xmldir/" - UpdateStatus $? - done - rm outfile -done - -cd "$TS/xmlconf/oasis" -for xmlfile in *fail*.xml ; do - RunXmlwfNotWF "$xmlfile" "oasis/" - UpdateStatus $? -done -rm outfile - -echo "Passed: $SUCCESS" -echo "Failed: $ERROR" diff --git a/cextern/expat/vms/README.vms b/cextern/expat/vms/README.vms deleted file mode 100755 index 3dd2ca73fee2..000000000000 --- a/cextern/expat/vms/README.vms +++ /dev/null @@ -1,23 +0,0 @@ -4-jun-2002 Craig A. Berry - Added rudimentary build procedures for - OpenVMS based on work by Martin Vorlaender. - - -You'll need MMS or its freeware equivalent MMK. Just go to the -top-level directory and type - -$ MMS/DESCRIPTION=[.vms] - - or - -$ MMK/DESCRIPTION=[.vms] - -You'll end up with the object library expat.olb. For now, installation -consists merely of copying the object library, include files, and -documentation to a suitable location. - -To-do list: - - -- create a shareable image - -- build and run the tests and build the xmlwf utility - -- create an install target diff --git a/cextern/expat/vms/descrip.mms b/cextern/expat/vms/descrip.mms deleted file mode 100755 index d9ee3d98e412..000000000000 --- a/cextern/expat/vms/descrip.mms +++ /dev/null @@ -1,70 +0,0 @@ -# Bare bones description file (Makefile) for OpenVMS - -PACKAGE = expat -VERSION = 1.95.8 -EXPAT_MAJOR_VERSION=1 -EXPAT_MINOR_VERSION=95 -EXPAT_EDIT=8 - -O = .obj -OLB = .olb - -LIBRARY = expat$(OLB) -LIBDIR = [.lib] -SOURCES = $(LIBDIR)xmlparse.c $(LIBDIR)xmltok.c $(LIBDIR)xmlrole.c -OBJECTS = xmlparse$(O) xmltok$(O) xmlrole$(O) - -TEMPLATES = xmltok_impl.c xmltok_ns.c -APIHEADER = $(LIBDIR)expat.h -HEADERS = $(LIBDIR)ascii.h $(LIBDIR)iasciitab.h $(LIBDIR)utf8tab.h $(LIBDIR)xmltok.h \ - $(LIBDIR)asciitab.h $(LIBDIR)latin1tab.h \ - $(LIBDIR)nametab.h $(LIBDIR)xmldef.h $(LIBDIR)xmlrole.h $(LIBDIR)xmltok_impl.h - -CONFIG_HEADER = expat_config.h -INCLUDES = /INCLUDE=([],[.lib]) -DEFS = /DEFINE=(PACKAGE="""$(PACKAGE)""",VERSION="""$(PACKAGE)_$(VERSION)""",HAVE_EXPAT_CONFIG_H) -LIBREVISION = 0 -LIBCURRENT = 1 -LIBAGE = 0 -# -COMPILE = $(CC) $(DEFS) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -# -# DISTFILES = $(DIST_COMMON) $(SOURCES) $(TEMPLATES) $(APIHEADER) $(HEADERS) -# -# TAR = gtar -# GZIP_ENV = --best -# -.FIRST : - IF F$SEARCH("$(LIBRARY)") .EQS. "" THEN $(LIBR) /CREATE /OBJECT $(LIBRARY) - -all : $(LIBRARY) - @ write sys$output "All made." - -.SUFFIXES : -.SUFFIXES : $(OLB) $(O) .C .H - -.c$(O) : - $(COMPILE) $(MMS$SOURCE) - -$(O)$(OLB) : - @ IF F$SEARCH("$(MMS$TARGET)") .EQS. "" - - THEN LIBRARY/CREATE/LOG $(MMS$TARGET) - @ LIBRARY /REPLACE /LOG $(MMS$TARGET) $(MMS$SOURCE) - -clean : - DELETE $(LIBRARY);*,*$(O);* - -$(LIBRARY) : $(LIBRARY)( $(OBJECTS) ) - $(LIBR) /COMPRESS $(MMS$TARGET) - -$(CONFIG_HEADER) : [.vms]expat_config.h - COPY/LOG $(MMS$SOURCE) $(MMS$TARGET) - -xmlparse$(O) : $(LIBDIR)xmlparse.c $(LIBDIR)expat.h $(LIBDIR)xmlrole.h $(LIBDIR)xmltok.h $(CONFIG_HEADER) - -xmlrole$(O) : $(LIBDIR)xmlrole.c $(LIBDIR)ascii.h $(LIBDIR)xmlrole.h $(CONFIG_HEADER) - -xmltok$(O) : $(LIBDIR)xmltok.c $(LIBDIR)xmltok_impl.c $(LIBDIR)xmltok_ns.c \ - $(LIBDIR)ascii.h $(LIBDIR)asciitab.h $(LIBDIR)iasciitab.h $(LIBDIR)latin1tab.h \ - $(LIBDIR)nametab.h $(LIBDIR)utf8tab.h $(LIBDIR)xmltok.h $(LIBDIR)xmltok_impl.h $(CONFIG_HEADER) - diff --git a/cextern/expat/vms/expat_config.h b/cextern/expat/vms/expat_config.h deleted file mode 100755 index d5db89e56c66..000000000000 --- a/cextern/expat/vms/expat_config.h +++ /dev/null @@ -1,52 +0,0 @@ -/* Copyright 2000, Clark Cooper - All rights reserved. - - This is free software. You are permitted to copy, distribute, or modify - it under the terms of the MIT/X license (contained in the COPYING file - with this distribution.) -*/ - -/* Define to empty if the keyword does not work. */ -#undef const - -/* Define if you have a working `mmap' system call. */ -#undef HAVE_MMAP - -/* Define to `long' if doesn't define. */ -#undef off_t - -/* Define to `unsigned' if doesn't define. */ -#undef size_t - -/* Define if your processor stores words with the most significant - byte first (like Motorola and SPARC, unlike Intel and VAX). */ -#undef WORDS_BIGENDIAN - -/* Define if you have the bcopy function. */ -#undef HAVE_BCOPY - -/* Define if you have the memmove function. */ -#define HAVE_MEMMOVE 1 - -/* Define if you have the header file. */ -#define HAVE_UNISTD_H 1 - -#define XML_NS -#define XML_DTD - -#ifdef WORDS_BIGENDIAN -#define XML_BYTE_ORDER 21 -#else -#define XML_BYTE_ORDER 12 -#endif - -#define XML_CONTEXT_BYTES 1024 - -#ifndef HAVE_MEMMOVE -#ifdef HAVE_BCOPY -#define memmove(d,s,l) bcopy((s),(d),(l)) -#else -#define memmove(d,s,l) ;punting on memmove; -#endif - -#endif diff --git a/cextern/expat/win32/MANIFEST.txt b/cextern/expat/win32/MANIFEST.txt deleted file mode 100755 index b7cd3954385c..000000000000 --- a/cextern/expat/win32/MANIFEST.txt +++ /dev/null @@ -1,27 +0,0 @@ - Overview of the Expat distribution - -The Expat distribution creates several subdirectories on your system. -Some of these directories contain components of interest to all Expat -users, and some contain material of interest to developers who wish to -use Expat in their applications. In the list below, is the -directory you specified to the installer. - - Directory Contents - --------------------------------------------------------------------- - \ Some general information files. - - \Doc\ API documentation for developers. - - \Bin\ Pre-compiled dynamic libraries for developers. - Pre-compiled static libraries for developers (*MT.lib). - The XML well-formedness checker xmlwf. - - \Source\ Source code, which may interest some developers, - including a workspace for Microsft Visual C++. - The source code includes the parser, the well- - formedness checker, and a couple of small sample - applications. - - \Source\bcb5\ Project files for Borland C++ Builder 5 and BCC 5.5. - - diff --git a/cextern/expat/win32/README.txt b/cextern/expat/win32/README.txt deleted file mode 100755 index 77a94bc73dee..000000000000 --- a/cextern/expat/win32/README.txt +++ /dev/null @@ -1,80 +0,0 @@ - -Expat can be built on Windows in three ways: - using MS Visual C++ (6.0 or .NET), Borland C++ Builder 5 or Cygwin. - -* Cygwin: - This follows the Unix build procedures. - -* C++ Builder 5: - Possible with make files in the BCB5 subdirectory. - Details can be found in the ReadMe file located there. - -* MS Visual C++ 6: - Based on the workspace file expat.dsw. The related project - files (.dsp) are located in the lib subdirectory. - -* MS Visual Studio .NET 2002, 2003, 2005: - The VC++ 6 workspace file (expat.dsw) and project files (.dsp) - can be opened and imported in VS.NET without problems. - -* All MS C/C++ compilers: - The output for all projects will be generated in the win32\bin - directory, intermediate files will be located in project-specific - subdirectories of win32\tmp. - -* Creating MinGW dynamic libraries from MS VC++ DLLs: - - On the command line, execute these steps: - pexports libexpat.dll > expat.def - pexports libexpatw.dll > expatw.def - dlltool -d expat.def -l libexpat.a - dlltool -d expatw.def -l libexpatw.a - - The *.a files are mingw libraries. - -* Special note about MS VC++ and runtime libraries: - - There are three possible configurations: using the - single threaded or multithreaded run-time library, - or using the multi-threaded run-time Dll. That is, - one can build three different Expat libraries depending - on the needs of the application. - - Dynamic Linking: - - By default the Expat Dlls are built to link statically - with the multi-threaded run-time library. - The libraries are named - - libexpat(w).dll - - libexpat(w).lib (import library) - The "w" indicates the UTF-16 version of the library. - - One rarely uses other versions of the Dll, but they can - be built easily by specifying a different RTL linkage in - the IDE on the C/C++ tab under the category Code Generation. - - Static Linking: - - The libraries should be named like this: - Single-theaded: libexpat(w)ML.lib - Multi-threaded: libexpat(w)MT.lib - Multi-threaded Dll: libexpat(w)MD.lib - The suffixes conform to the compiler switch settings - /ML, /MT and /MD for MS VC++. - - Note: In Visual Studio 2005 (Visual C++ 8.0) and later, the - single-threaded runtime library is not supported anymore. - - By default, the expat-static and expatw-static projects are set up - to link statically against the multithreaded run-time library, - so they will build libexpatMT.lib or libexpatwMT.lib files. - - To build the other versions of the static library, - go to Project - Settings: - - specify a different RTL linkage on the C/C++ tab - under the category Code Generation. - - then, on the Library tab, change the output file name - accordingly, as described above - - An application linking to the static libraries must - have the global macro XML_STATIC defined. diff --git a/cextern/expat/win32/expat.iss b/cextern/expat/win32/expat.iss deleted file mode 100755 index 400b315b3dcc..000000000000 --- a/cextern/expat/win32/expat.iss +++ /dev/null @@ -1,69 +0,0 @@ -; Basic setup script for the Inno Setup installer builder. For more -; information on the free installer builder, see www.jrsoftware.org. -; -; This script was contributed by Tim Peters. -; The current version is used with Inno Setup 2.0.19. - -[Setup] -AppName=Expat -AppId=expat -AppVersion=2.0.1 -AppVerName=Expat 2.0.1 -AppCopyright=Copyright Š 1998-2006 Thai Open Source Software Center, Clark Cooper, and the Expat maintainers -AppPublisher=The Expat Developers -AppPublisherURL=http://www.libexpat.org/ -AppSupportURL=http://www.libexpat.org/ -AppUpdatesURL=http://www.libexpat.org/ -UninstallDisplayName=Expat XML Parser 2.0.1 -VersionInfoVersion=2.0.1 - -DefaultDirName={pf}\Expat 2.0.1 -UninstallFilesDir={app}\Uninstall - -Compression=lzma -SolidCompression=yes -SourceDir=.. -OutputDir=win32 -DisableStartupPrompt=yes -AllowNoIcons=yes -DisableProgramGroupPage=yes -DisableReadyPage=yes - -[Files] -Flags: ignoreversion; Source: win32\bin\Release\xmlwf.exe; DestDir: "{app}\Bin" -Flags: ignoreversion; Source: win32\MANIFEST.txt; DestDir: "{app}" -Flags: ignoreversion; Source: Changes; DestDir: "{app}"; DestName: Changes.txt -Flags: ignoreversion; Source: COPYING; DestDir: "{app}"; DestName: COPYING.txt -Flags: ignoreversion; Source: README; DestDir: "{app}"; DestName: README.txt -Flags: ignoreversion; Source: doc\*.html; DestDir: "{app}\Doc" -Flags: ignoreversion; Source: doc\*.css; DestDir: "{app}\Doc" -Flags: ignoreversion; Source: doc\*.png; DestDir: "{app}\Doc" -Flags: ignoreversion; Source: win32\bin\Release\*.dll; DestDir: "{app}\Bin" -Flags: ignoreversion; Source: win32\bin\Release\*.lib; DestDir: "{app}\Bin" -Flags: ignoreversion; Source: expat.dsw; DestDir: "{app}\Source" -Flags: ignoreversion; Source: win32\README.txt; DestDir: "{app}\Source" -Flags: ignoreversion; Source: bcb5\*.bp*; DestDir: "{app}\Source\bcb5" -Flags: ignoreversion; Source: bcb5\*.mak; DestDir: "{app}\Source\bcb5" -Flags: ignoreversion; Source: bcb5\*.def; DestDir: "{app}\Source\bcb5" -Flags: ignoreversion; Source: bcb5\*.txt; DestDir: "{app}\Source\bcb5" -Flags: ignoreversion; Source: bcb5\*.bat; DestDir: "{app}\Source\bcb5" -Flags: ignoreversion; Source: lib\*.c; DestDir: "{app}\Source\lib" -Flags: ignoreversion; Source: lib\*.h; DestDir: "{app}\Source\lib" -Flags: ignoreversion; Source: lib\*.def; DestDir: "{app}\Source\lib" -Flags: ignoreversion; Source: lib\*.dsp; DestDir: "{app}\Source\lib" -Flags: ignoreversion; Source: examples\*.c; DestDir: "{app}\Source\examples" -Flags: ignoreversion; Source: examples\*.dsp; DestDir: "{app}\Source\examples" -Flags: ignoreversion; Source: tests\*.c; DestDir: "{app}\Source\tests" -Flags: ignoreversion; Source: tests\*.cpp; DestDir: "{app}\Source\tests" -Flags: ignoreversion; Source: tests\*.h; DestDir: "{app}\Source\tests" -Flags: ignoreversion; Source: tests\README.txt; DestDir: "{app}\Source\tests" -Flags: ignoreversion; Source: tests\benchmark\*.c; DestDir: "{app}\Source\tests\benchmark" -Flags: ignoreversion; Source: tests\benchmark\*.ds*; DestDir: "{app}\Source\tests\benchmark" -Flags: ignoreversion; Source: tests\benchmark\README.txt; DestDir: "{app}\Source\tests\benchmark" -Flags: ignoreversion; Source: xmlwf\*.c*; DestDir: "{app}\Source\xmlwf" -Flags: ignoreversion; Source: xmlwf\*.h; DestDir: "{app}\Source\xmlwf" -Flags: ignoreversion; Source: xmlwf\*.dsp; DestDir: "{app}\Source\xmlwf" - -[Messages] -WelcomeLabel1=Welcome to the Expat XML Parser Setup Wizard -WelcomeLabel2=This will install [name/ver] on your computer.%n%nExpat is an XML parser with a C-language API, and is primarily made available to allow developers to build applications which use XML using a portable API and fast implementation.%n%nIt is strongly recommended that you close all other applications you have running before continuing. This will help prevent any conflicts during the installation process. diff --git a/cextern/expat/xmlwf/codepage.c b/cextern/expat/xmlwf/codepage.c deleted file mode 100755 index 57e48ff2d12e..000000000000 --- a/cextern/expat/xmlwf/codepage.c +++ /dev/null @@ -1,68 +0,0 @@ -/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd - See the file COPYING for copying permission. -*/ - -#include "codepage.h" - -#if (defined(WIN32) || (defined(__WATCOMC__) && defined(__NT__))) -#define STRICT 1 -#define WIN32_LEAN_AND_MEAN 1 - -#include - -int -codepageMap(int cp, int *map) -{ - int i; - CPINFO info; - if (!GetCPInfo(cp, &info) || info.MaxCharSize > 2) - return 0; - for (i = 0; i < 256; i++) - map[i] = -1; - if (info.MaxCharSize > 1) { - for (i = 0; i < MAX_LEADBYTES; i+=2) { - int j, lim; - if (info.LeadByte[i] == 0 && info.LeadByte[i + 1] == 0) - break; - lim = info.LeadByte[i + 1]; - for (j = info.LeadByte[i]; j <= lim; j++) - map[j] = -2; - } - } - for (i = 0; i < 256; i++) { - if (map[i] == -1) { - char c = (char)i; - unsigned short n; - if (MultiByteToWideChar(cp, MB_PRECOMPOSED|MB_ERR_INVALID_CHARS, - &c, 1, &n, 1) == 1) - map[i] = n; - } - } - return 1; -} - -int -codepageConvert(int cp, const char *p) -{ - unsigned short c; - if (MultiByteToWideChar(cp, MB_PRECOMPOSED|MB_ERR_INVALID_CHARS, - p, 2, &c, 1) == 1) - return c; - return -1; -} - -#else /* not WIN32 */ - -int -codepageMap(int cp, int *map) -{ - return 0; -} - -int -codepageConvert(int cp, const char *p) -{ - return -1; -} - -#endif /* not WIN32 */ diff --git a/cextern/expat/xmlwf/codepage.h b/cextern/expat/xmlwf/codepage.h deleted file mode 100755 index 6a4df68883e4..000000000000 --- a/cextern/expat/xmlwf/codepage.h +++ /dev/null @@ -1,6 +0,0 @@ -/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd - See the file COPYING for copying permission. -*/ - -int codepageMap(int cp, int *map); -int codepageConvert(int cp, const char *p); diff --git a/cextern/expat/xmlwf/ct.c b/cextern/expat/xmlwf/ct.c deleted file mode 100755 index 95903a34500a..000000000000 --- a/cextern/expat/xmlwf/ct.c +++ /dev/null @@ -1,147 +0,0 @@ -#define CHARSET_MAX 41 - -static const char * -getTok(const char **pp) -{ - enum { inAtom, inString, init, inComment }; - int state = init; - const char *tokStart = 0; - for (;;) { - switch (**pp) { - case '\0': - return 0; - case ' ': - case '\r': - case '\t': - case '\n': - if (state == inAtom) - return tokStart; - break; - case '(': - if (state == inAtom) - return tokStart; - if (state != inString) - state++; - break; - case ')': - if (state > init) - --state; - else if (state != inString) - return 0; - break; - case ';': - case '/': - case '=': - if (state == inAtom) - return tokStart; - if (state == init) - return (*pp)++; - break; - case '\\': - ++*pp; - if (**pp == '\0') - return 0; - break; - case '"': - switch (state) { - case inString: - ++*pp; - return tokStart; - case inAtom: - return tokStart; - case init: - tokStart = *pp; - state = inString; - break; - } - break; - default: - if (state == init) { - tokStart = *pp; - state = inAtom; - } - break; - } - ++*pp; - } - /* not reached */ -} - -/* key must be lowercase ASCII */ - -static int -matchkey(const char *start, const char *end, const char *key) -{ - if (!start) - return 0; - for (; start != end; start++, key++) - if (*start != *key && *start != 'A' + (*key - 'a')) - return 0; - return *key == '\0'; -} - -void -getXMLCharset(const char *buf, char *charset) -{ - const char *next, *p; - - charset[0] = '\0'; - next = buf; - p = getTok(&next); - if (matchkey(p, next, "text")) - strcpy(charset, "us-ascii"); - else if (!matchkey(p, next, "application")) - return; - p = getTok(&next); - if (!p || *p != '/') - return; - p = getTok(&next); - if (matchkey(p, next, "xml")) - isXml = 1; - p = getTok(&next); - while (p) { - if (*p == ';') { - p = getTok(&next); - if (matchkey(p, next, "charset")) { - p = getTok(&next); - if (p && *p == '=') { - p = getTok(&next); - if (p) { - char *s = charset; - if (*p == '"') { - while (++p != next - 1) { - if (*p == '\\') - ++p; - if (s == charset + CHARSET_MAX - 1) { - charset[0] = '\0'; - break; - } - *s++ = *p; - } - *s++ = '\0'; - } - else { - if (next - p > CHARSET_MAX - 1) - break; - while (p != next) - *s++ = *p++; - *s = 0; - break; - } - } - } - } - } - else - p = getTok(&next); - } -} - -int -main(int argc, char **argv) -{ - char buf[CHARSET_MAX]; - getXMLCharset(argv[1], buf); - printf("charset = \"%s\"\n", buf); - return 0; -} diff --git a/cextern/expat/xmlwf/filemap.h b/cextern/expat/xmlwf/filemap.h deleted file mode 100755 index 814edec2528c..000000000000 --- a/cextern/expat/xmlwf/filemap.h +++ /dev/null @@ -1,17 +0,0 @@ -/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd - See the file COPYING for copying permission. -*/ - -#include - -#ifdef XML_UNICODE -int filemap(const wchar_t *name, - void (*processor)(const void *, size_t, - const wchar_t *, void *arg), - void *arg); -#else -int filemap(const char *name, - void (*processor)(const void *, size_t, - const char *, void *arg), - void *arg); -#endif diff --git a/cextern/expat/xmlwf/readfilemap.c b/cextern/expat/xmlwf/readfilemap.c deleted file mode 100755 index 088dda5c23de..000000000000 --- a/cextern/expat/xmlwf/readfilemap.c +++ /dev/null @@ -1,98 +0,0 @@ -/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd - See the file COPYING for copying permission. -*/ - -#include -#include -#include -#include -#include - -#ifdef __WATCOMC__ -#ifndef __LINUX__ -#include -#else -#include -#endif -#endif - -#ifdef __BEOS__ -#include -#endif - -#ifndef S_ISREG -#ifndef S_IFREG -#define S_IFREG _S_IFREG -#endif -#ifndef S_IFMT -#define S_IFMT _S_IFMT -#endif -#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) -#endif /* not S_ISREG */ - -#ifndef O_BINARY -#ifdef _O_BINARY -#define O_BINARY _O_BINARY -#else -#define O_BINARY 0 -#endif -#endif - -#include "filemap.h" - -int -filemap(const char *name, - void (*processor)(const void *, size_t, const char *, void *arg), - void *arg) -{ - size_t nbytes; - int fd; - int n; - struct stat sb; - void *p; - - fd = open(name, O_RDONLY|O_BINARY); - if (fd < 0) { - perror(name); - return 0; - } - if (fstat(fd, &sb) < 0) { - perror(name); - return 0; - } - if (!S_ISREG(sb.st_mode)) { - fprintf(stderr, "%s: not a regular file\n", name); - return 0; - } - nbytes = sb.st_size; - /* malloc will return NULL with nbytes == 0, handle files with size 0 */ - if (nbytes == 0) { - static const char c = '\0'; - processor(&c, 0, name, arg); - close(fd); - return 1; - } - p = malloc(nbytes); - if (!p) { - fprintf(stderr, "%s: out of memory\n", name); - close(fd); - return 0; - } - n = read(fd, p, nbytes); - if (n < 0) { - perror(name); - free(p); - close(fd); - return 0; - } - if (n != nbytes) { - fprintf(stderr, "%s: read unexpected number of bytes\n", name); - free(p); - close(fd); - return 0; - } - processor(p, nbytes, name, arg); - free(p); - close(fd); - return 1; -} diff --git a/cextern/expat/xmlwf/unixfilemap.c b/cextern/expat/xmlwf/unixfilemap.c deleted file mode 100755 index 93adce32e826..000000000000 --- a/cextern/expat/xmlwf/unixfilemap.c +++ /dev/null @@ -1,65 +0,0 @@ -/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd - See the file COPYING for copying permission. -*/ - -#include -#include -#include -#include -#include -#include -#include -#include - -#ifndef MAP_FILE -#define MAP_FILE 0 -#endif - -#include "filemap.h" - -int -filemap(const char *name, - void (*processor)(const void *, size_t, const char *, void *arg), - void *arg) -{ - int fd; - size_t nbytes; - struct stat sb; - void *p; - - fd = open(name, O_RDONLY); - if (fd < 0) { - perror(name); - return 0; - } - if (fstat(fd, &sb) < 0) { - perror(name); - close(fd); - return 0; - } - if (!S_ISREG(sb.st_mode)) { - close(fd); - fprintf(stderr, "%s: not a regular file\n", name); - return 0; - } - - nbytes = sb.st_size; - /* mmap fails for zero length files */ - if (nbytes == 0) { - static const char c = '\0'; - processor(&c, 0, name, arg); - close(fd); - return 1; - } - p = (void *)mmap((caddr_t)0, (size_t)nbytes, PROT_READ, - MAP_FILE|MAP_PRIVATE, fd, (off_t)0); - if (p == (void *)-1) { - perror(name); - close(fd); - return 0; - } - processor(p, nbytes, name, arg); - munmap((caddr_t)p, nbytes); - close(fd); - return 1; -} diff --git a/cextern/expat/xmlwf/win32filemap.c b/cextern/expat/xmlwf/win32filemap.c deleted file mode 100755 index 41dc35b614db..000000000000 --- a/cextern/expat/xmlwf/win32filemap.c +++ /dev/null @@ -1,96 +0,0 @@ -/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd - See the file COPYING for copying permission. -*/ - -#define STRICT 1 -#define WIN32_LEAN_AND_MEAN 1 - -#ifdef XML_UNICODE_WCHAR_T -#ifndef XML_UNICODE -#define XML_UNICODE -#endif -#endif - -#ifdef XML_UNICODE -#define UNICODE -#define _UNICODE -#endif /* XML_UNICODE */ -#include -#include -#include -#include "filemap.h" - -static void win32perror(const TCHAR *); - -int -filemap(const TCHAR *name, - void (*processor)(const void *, size_t, const TCHAR *, void *arg), - void *arg) -{ - HANDLE f; - HANDLE m; - DWORD size; - DWORD sizeHi; - void *p; - - f = CreateFile(name, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, - FILE_FLAG_SEQUENTIAL_SCAN, NULL); - if (f == INVALID_HANDLE_VALUE) { - win32perror(name); - return 0; - } - size = GetFileSize(f, &sizeHi); - if (size == (DWORD)-1) { - win32perror(name); - return 0; - } - if (sizeHi) { - _ftprintf(stderr, _T("%s: bigger than 2Gb\n"), name); - return 0; - } - /* CreateFileMapping barfs on zero length files */ - if (size == 0) { - static const char c = '\0'; - processor(&c, 0, name, arg); - CloseHandle(f); - return 1; - } - m = CreateFileMapping(f, NULL, PAGE_READONLY, 0, 0, NULL); - if (m == NULL) { - win32perror(name); - CloseHandle(f); - return 0; - } - p = MapViewOfFile(m, FILE_MAP_READ, 0, 0, 0); - if (p == NULL) { - win32perror(name); - CloseHandle(m); - CloseHandle(f); - return 0; - } - processor(p, size, name, arg); - UnmapViewOfFile(p); - CloseHandle(m); - CloseHandle(f); - return 1; -} - -static void -win32perror(const TCHAR *s) -{ - LPVOID buf; - if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER - | FORMAT_MESSAGE_FROM_SYSTEM, - NULL, - GetLastError(), - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPTSTR) &buf, - 0, - NULL)) { - _ftprintf(stderr, _T("%s: %s"), s, buf); - fflush(stderr); - LocalFree(buf); - } - else - _ftprintf(stderr, _T("%s: unknown Windows error\n"), s); -} diff --git a/cextern/expat/xmlwf/xmlfile.c b/cextern/expat/xmlwf/xmlfile.c deleted file mode 100755 index 99eeeaaef28d..000000000000 --- a/cextern/expat/xmlwf/xmlfile.c +++ /dev/null @@ -1,244 +0,0 @@ -/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd - See the file COPYING for copying permission. -*/ - -#include -#include -#include -#include -#include - -#ifdef COMPILED_FROM_DSP -#include "winconfig.h" -#elif defined(MACOS_CLASSIC) -#include "macconfig.h" -#elif defined(__amigaos__) -#include "amigaconfig.h" -#elif defined(__WATCOMC__) -#include "watcomconfig.h" -#elif defined(HAVE_EXPAT_CONFIG_H) -#include -#endif /* ndef COMPILED_FROM_DSP */ - -#include "expat.h" -#include "xmlfile.h" -#include "xmltchar.h" -#include "filemap.h" - -#if (defined(_MSC_VER) || (defined(__WATCOMC__) && !defined(__LINUX__))) -#include -#endif - -#if defined(__amigaos__) && defined(__USE_INLINE__) -#include -#endif - -#ifdef HAVE_UNISTD_H -#include -#endif - -#ifndef O_BINARY -#ifdef _O_BINARY -#define O_BINARY _O_BINARY -#else -#define O_BINARY 0 -#endif -#endif - -#ifdef _DEBUG -#define READ_SIZE 16 -#else -#define READ_SIZE (1024*8) -#endif - - -typedef struct { - XML_Parser parser; - int *retPtr; -} PROCESS_ARGS; - -static void -reportError(XML_Parser parser, const XML_Char *filename) -{ - enum XML_Error code = XML_GetErrorCode(parser); - const XML_Char *message = XML_ErrorString(code); - if (message) - ftprintf(stdout, T("%s:%" XML_FMT_INT_MOD "u:%" XML_FMT_INT_MOD "u: %s\n"), - filename, - XML_GetErrorLineNumber(parser), - XML_GetErrorColumnNumber(parser), - message); - else - ftprintf(stderr, T("%s: (unknown message %d)\n"), filename, code); -} - -/* This implementation will give problems on files larger than INT_MAX. */ -static void -processFile(const void *data, size_t size, - const XML_Char *filename, void *args) -{ - XML_Parser parser = ((PROCESS_ARGS *)args)->parser; - int *retPtr = ((PROCESS_ARGS *)args)->retPtr; - if (XML_Parse(parser, (const char *)data, (int)size, 1) == XML_STATUS_ERROR) { - reportError(parser, filename); - *retPtr = 0; - } - else - *retPtr = 1; -} - -#if (defined(WIN32) || defined(__WATCOMC__)) - -static int -isAsciiLetter(XML_Char c) -{ - return (T('a') <= c && c <= T('z')) || (T('A') <= c && c <= T('Z')); -} - -#endif /* WIN32 */ - -static const XML_Char * -resolveSystemId(const XML_Char *base, const XML_Char *systemId, - XML_Char **toFree) -{ - XML_Char *s; - *toFree = 0; - if (!base - || *systemId == T('/') -#if (defined(WIN32) || defined(__WATCOMC__)) - || *systemId == T('\\') - || (isAsciiLetter(systemId[0]) && systemId[1] == T(':')) -#endif - ) - return systemId; - *toFree = (XML_Char *)malloc((tcslen(base) + tcslen(systemId) + 2) - * sizeof(XML_Char)); - if (!*toFree) - return systemId; - tcscpy(*toFree, base); - s = *toFree; - if (tcsrchr(s, T('/'))) - s = tcsrchr(s, T('/')) + 1; -#if (defined(WIN32) || defined(__WATCOMC__)) - if (tcsrchr(s, T('\\'))) - s = tcsrchr(s, T('\\')) + 1; -#endif - tcscpy(s, systemId); - return *toFree; -} - -static int -externalEntityRefFilemap(XML_Parser parser, - const XML_Char *context, - const XML_Char *base, - const XML_Char *systemId, - const XML_Char *publicId) -{ - int result; - XML_Char *s; - const XML_Char *filename; - XML_Parser entParser = XML_ExternalEntityParserCreate(parser, context, 0); - PROCESS_ARGS args; - args.retPtr = &result; - args.parser = entParser; - filename = resolveSystemId(base, systemId, &s); - XML_SetBase(entParser, filename); - if (!filemap(filename, processFile, &args)) - result = 0; - free(s); - XML_ParserFree(entParser); - return result; -} - -static int -processStream(const XML_Char *filename, XML_Parser parser) -{ - /* passing NULL for filename means read intput from stdin */ - int fd = 0; /* 0 is the fileno for stdin */ - - if (filename != NULL) { - fd = topen(filename, O_BINARY|O_RDONLY); - if (fd < 0) { - tperror(filename); - return 0; - } - } - for (;;) { - int nread; - char *buf = (char *)XML_GetBuffer(parser, READ_SIZE); - if (!buf) { - if (filename != NULL) - close(fd); - ftprintf(stderr, T("%s: out of memory\n"), - filename != NULL ? filename : "xmlwf"); - return 0; - } - nread = read(fd, buf, READ_SIZE); - if (nread < 0) { - tperror(filename != NULL ? filename : "STDIN"); - if (filename != NULL) - close(fd); - return 0; - } - if (XML_ParseBuffer(parser, nread, nread == 0) == XML_STATUS_ERROR) { - reportError(parser, filename != NULL ? filename : "STDIN"); - if (filename != NULL) - close(fd); - return 0; - } - if (nread == 0) { - if (filename != NULL) - close(fd); - break;; - } - } - return 1; -} - -static int -externalEntityRefStream(XML_Parser parser, - const XML_Char *context, - const XML_Char *base, - const XML_Char *systemId, - const XML_Char *publicId) -{ - XML_Char *s; - const XML_Char *filename; - int ret; - XML_Parser entParser = XML_ExternalEntityParserCreate(parser, context, 0); - filename = resolveSystemId(base, systemId, &s); - XML_SetBase(entParser, filename); - ret = processStream(filename, entParser); - free(s); - XML_ParserFree(entParser); - return ret; -} - -int -XML_ProcessFile(XML_Parser parser, - const XML_Char *filename, - unsigned flags) -{ - int result; - - if (!XML_SetBase(parser, filename)) { - ftprintf(stderr, T("%s: out of memory"), filename); - exit(1); - } - - if (flags & XML_EXTERNAL_ENTITIES) - XML_SetExternalEntityRefHandler(parser, - (flags & XML_MAP_FILE) - ? externalEntityRefFilemap - : externalEntityRefStream); - if (flags & XML_MAP_FILE) { - PROCESS_ARGS args; - args.retPtr = &result; - args.parser = parser; - if (!filemap(filename, processFile, &args)) - result = 0; - } - else - result = processStream(filename, parser); - return result; -} diff --git a/cextern/expat/xmlwf/xmlfile.h b/cextern/expat/xmlwf/xmlfile.h deleted file mode 100755 index d093ecc06f9c..000000000000 --- a/cextern/expat/xmlwf/xmlfile.h +++ /dev/null @@ -1,20 +0,0 @@ -/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd - See the file COPYING for copying permission. -*/ - -#define XML_MAP_FILE 01 -#define XML_EXTERNAL_ENTITIES 02 - -#ifdef XML_LARGE_SIZE -#if defined(XML_USE_MSC_EXTENSIONS) && _MSC_VER < 1400 -#define XML_FMT_INT_MOD "I64" -#else -#define XML_FMT_INT_MOD "ll" -#endif -#else -#define XML_FMT_INT_MOD "l" -#endif - -extern int XML_ProcessFile(XML_Parser parser, - const XML_Char *filename, - unsigned flags); diff --git a/cextern/expat/xmlwf/xmlmime.c b/cextern/expat/xmlwf/xmlmime.c deleted file mode 100755 index 56a0e7f40e49..000000000000 --- a/cextern/expat/xmlwf/xmlmime.c +++ /dev/null @@ -1,163 +0,0 @@ -#include -#include "xmlmime.h" - -static const char * -getTok(const char **pp) -{ - /* inComment means one level of nesting; inComment+1 means two levels etc */ - enum { inAtom, inString, init, inComment }; - int state = init; - const char *tokStart = 0; - for (;;) { - switch (**pp) { - case '\0': - if (state == inAtom) - return tokStart; - return 0; - case ' ': - case '\r': - case '\t': - case '\n': - if (state == inAtom) - return tokStart; - break; - case '(': - if (state == inAtom) - return tokStart; - if (state != inString) - state++; - break; - case ')': - if (state > init) - --state; - else if (state != inString) - return 0; - break; - case ';': - case '/': - case '=': - if (state == inAtom) - return tokStart; - if (state == init) - return (*pp)++; - break; - case '\\': - ++*pp; - if (**pp == '\0') - return 0; - break; - case '"': - switch (state) { - case inString: - ++*pp; - return tokStart; - case inAtom: - return tokStart; - case init: - tokStart = *pp; - state = inString; - break; - } - break; - default: - if (state == init) { - tokStart = *pp; - state = inAtom; - } - break; - } - ++*pp; - } - /* not reached */ -} - -/* key must be lowercase ASCII */ - -static int -matchkey(const char *start, const char *end, const char *key) -{ - if (!start) - return 0; - for (; start != end; start++, key++) - if (*start != *key && *start != 'A' + (*key - 'a')) - return 0; - return *key == '\0'; -} - -void -getXMLCharset(const char *buf, char *charset) -{ - const char *next, *p; - - charset[0] = '\0'; - next = buf; - p = getTok(&next); - if (matchkey(p, next, "text")) - strcpy(charset, "us-ascii"); - else if (!matchkey(p, next, "application")) - return; - p = getTok(&next); - if (!p || *p != '/') - return; - p = getTok(&next); -#if 0 - if (!matchkey(p, next, "xml") && charset[0] == '\0') - return; -#endif - p = getTok(&next); - while (p) { - if (*p == ';') { - p = getTok(&next); - if (matchkey(p, next, "charset")) { - p = getTok(&next); - if (p && *p == '=') { - p = getTok(&next); - if (p) { - char *s = charset; - if (*p == '"') { - while (++p != next - 1) { - if (*p == '\\') - ++p; - if (s == charset + CHARSET_MAX - 1) { - charset[0] = '\0'; - break; - } - *s++ = *p; - } - *s++ = '\0'; - } - else { - if (next - p > CHARSET_MAX - 1) - break; - while (p != next) - *s++ = *p++; - *s = 0; - break; - } - } - } - break; - } - } - else - p = getTok(&next); - } -} - -#ifdef TEST - -#include - -int -main(int argc, char *argv[]) -{ - char buf[CHARSET_MAX]; - if (argc <= 1) - return 1; - printf("%s\n", argv[1]); - getXMLCharset(argv[1], buf); - printf("charset=\"%s\"\n", buf); - return 0; -} - -#endif /* TEST */ diff --git a/cextern/expat/xmlwf/xmlmime.h b/cextern/expat/xmlwf/xmlmime.h deleted file mode 100755 index bf0356df0d91..000000000000 --- a/cextern/expat/xmlwf/xmlmime.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifdef __cplusplus -extern "C" { -#endif - -/* Registered charset names are at most 40 characters long. */ - -#define CHARSET_MAX 41 - -/* Figure out the charset to use from the ContentType. - buf contains the body of the header field (the part after "Content-Type:"). - charset gets the charset to use. It must be at least CHARSET_MAX chars - long. charset will be empty if the default charset should be used. -*/ - -void getXMLCharset(const char *buf, char *charset); - -#ifdef __cplusplus -} -#endif diff --git a/cextern/expat/xmlwf/xmltchar.h b/cextern/expat/xmlwf/xmltchar.h deleted file mode 100755 index 1088575512de..000000000000 --- a/cextern/expat/xmlwf/xmltchar.h +++ /dev/null @@ -1,36 +0,0 @@ -#ifdef XML_UNICODE -#ifndef XML_UNICODE_WCHAR_T -#error xmlwf requires a 16-bit Unicode-compatible wchar_t -#endif -#define T(x) L ## x -#define ftprintf fwprintf -#define tfopen _wfopen -#define fputts fputws -#define puttc putwc -#define tcscmp wcscmp -#define tcscpy wcscpy -#define tcscat wcscat -#define tcschr wcschr -#define tcsrchr wcsrchr -#define tcslen wcslen -#define tperror _wperror -#define topen _wopen -#define tmain wmain -#define tremove _wremove -#else /* not XML_UNICODE */ -#define T(x) x -#define ftprintf fprintf -#define tfopen fopen -#define fputts fputs -#define puttc putc -#define tcscmp strcmp -#define tcscpy strcpy -#define tcscat strcat -#define tcschr strchr -#define tcsrchr strrchr -#define tcslen strlen -#define tperror perror -#define topen open -#define tmain main -#define tremove remove -#endif /* not XML_UNICODE */ diff --git a/cextern/expat/xmlwf/xmlurl.h b/cextern/expat/xmlwf/xmlurl.h deleted file mode 100755 index d329913ac853..000000000000 --- a/cextern/expat/xmlwf/xmlurl.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifdef __cplusplus -extern "C" { -#endif - -int XML_URLInit(); -void XML_URLUninit(); -int XML_ProcessURL(XML_Parser parser, - const XML_Char *url, - unsigned flags); - -#ifdef __cplusplus -} -#endif diff --git a/cextern/expat/xmlwf/xmlwf.c b/cextern/expat/xmlwf/xmlwf.c deleted file mode 100755 index 41a433d3cede..000000000000 --- a/cextern/expat/xmlwf/xmlwf.c +++ /dev/null @@ -1,859 +0,0 @@ -/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd - See the file COPYING for copying permission. -*/ - -#include -#include -#include -#include - -#include "expat.h" -#include "codepage.h" -#include "xmlfile.h" -#include "xmltchar.h" - -#ifdef _MSC_VER -#include -#endif - -#if defined(__amigaos__) && defined(__USE_INLINE__) -#include -#endif - -/* This ensures proper sorting. */ - -#define NSSEP T('\001') - -static void XMLCALL -characterData(void *userData, const XML_Char *s, int len) -{ - FILE *fp = (FILE *)userData; - for (; len > 0; --len, ++s) { - switch (*s) { - case T('&'): - fputts(T("&"), fp); - break; - case T('<'): - fputts(T("<"), fp); - break; - case T('>'): - fputts(T(">"), fp); - break; -#ifdef W3C14N - case 13: - fputts(T(" "), fp); - break; -#else - case T('"'): - fputts(T("""), fp); - break; - case 9: - case 10: - case 13: - ftprintf(fp, T("&#%d;"), *s); - break; -#endif - default: - puttc(*s, fp); - break; - } - } -} - -static void -attributeValue(FILE *fp, const XML_Char *s) -{ - puttc(T('='), fp); - puttc(T('"'), fp); - for (;;) { - switch (*s) { - case 0: - case NSSEP: - puttc(T('"'), fp); - return; - case T('&'): - fputts(T("&"), fp); - break; - case T('<'): - fputts(T("<"), fp); - break; - case T('"'): - fputts(T("""), fp); - break; -#ifdef W3C14N - case 9: - fputts(T(" "), fp); - break; - case 10: - fputts(T(" "), fp); - break; - case 13: - fputts(T(" "), fp); - break; -#else - case T('>'): - fputts(T(">"), fp); - break; - case 9: - case 10: - case 13: - ftprintf(fp, T("&#%d;"), *s); - break; -#endif - default: - puttc(*s, fp); - break; - } - s++; - } -} - -/* Lexicographically comparing UTF-8 encoded attribute values, -is equivalent to lexicographically comparing based on the character number. */ - -static int -attcmp(const void *att1, const void *att2) -{ - return tcscmp(*(const XML_Char **)att1, *(const XML_Char **)att2); -} - -static void XMLCALL -startElement(void *userData, const XML_Char *name, const XML_Char **atts) -{ - int nAtts; - const XML_Char **p; - FILE *fp = (FILE *)userData; - puttc(T('<'), fp); - fputts(name, fp); - - p = atts; - while (*p) - ++p; - nAtts = (int)((p - atts) >> 1); - if (nAtts > 1) - qsort((void *)atts, nAtts, sizeof(XML_Char *) * 2, attcmp); - while (*atts) { - puttc(T(' '), fp); - fputts(*atts++, fp); - attributeValue(fp, *atts); - atts++; - } - puttc(T('>'), fp); -} - -static void XMLCALL -endElement(void *userData, const XML_Char *name) -{ - FILE *fp = (FILE *)userData; - puttc(T('<'), fp); - puttc(T('/'), fp); - fputts(name, fp); - puttc(T('>'), fp); -} - -static int -nsattcmp(const void *p1, const void *p2) -{ - const XML_Char *att1 = *(const XML_Char **)p1; - const XML_Char *att2 = *(const XML_Char **)p2; - int sep1 = (tcsrchr(att1, NSSEP) != 0); - int sep2 = (tcsrchr(att1, NSSEP) != 0); - if (sep1 != sep2) - return sep1 - sep2; - return tcscmp(att1, att2); -} - -static void XMLCALL -startElementNS(void *userData, const XML_Char *name, const XML_Char **atts) -{ - int nAtts; - int nsi; - const XML_Char **p; - FILE *fp = (FILE *)userData; - const XML_Char *sep; - puttc(T('<'), fp); - - sep = tcsrchr(name, NSSEP); - if (sep) { - fputts(T("n1:"), fp); - fputts(sep + 1, fp); - fputts(T(" xmlns:n1"), fp); - attributeValue(fp, name); - nsi = 2; - } - else { - fputts(name, fp); - nsi = 1; - } - - p = atts; - while (*p) - ++p; - nAtts = (int)((p - atts) >> 1); - if (nAtts > 1) - qsort((void *)atts, nAtts, sizeof(XML_Char *) * 2, nsattcmp); - while (*atts) { - name = *atts++; - sep = tcsrchr(name, NSSEP); - puttc(T(' '), fp); - if (sep) { - ftprintf(fp, T("n%d:"), nsi); - fputts(sep + 1, fp); - } - else - fputts(name, fp); - attributeValue(fp, *atts); - if (sep) { - ftprintf(fp, T(" xmlns:n%d"), nsi++); - attributeValue(fp, name); - } - atts++; - } - puttc(T('>'), fp); -} - -static void XMLCALL -endElementNS(void *userData, const XML_Char *name) -{ - FILE *fp = (FILE *)userData; - const XML_Char *sep; - puttc(T('<'), fp); - puttc(T('/'), fp); - sep = tcsrchr(name, NSSEP); - if (sep) { - fputts(T("n1:"), fp); - fputts(sep + 1, fp); - } - else - fputts(name, fp); - puttc(T('>'), fp); -} - -#ifndef W3C14N - -static void XMLCALL -processingInstruction(void *userData, const XML_Char *target, - const XML_Char *data) -{ - FILE *fp = (FILE *)userData; - puttc(T('<'), fp); - puttc(T('?'), fp); - fputts(target, fp); - puttc(T(' '), fp); - fputts(data, fp); - puttc(T('?'), fp); - puttc(T('>'), fp); -} - -#endif /* not W3C14N */ - -static void XMLCALL -defaultCharacterData(void *userData, const XML_Char *s, int len) -{ - XML_DefaultCurrent((XML_Parser) userData); -} - -static void XMLCALL -defaultStartElement(void *userData, const XML_Char *name, - const XML_Char **atts) -{ - XML_DefaultCurrent((XML_Parser) userData); -} - -static void XMLCALL -defaultEndElement(void *userData, const XML_Char *name) -{ - XML_DefaultCurrent((XML_Parser) userData); -} - -static void XMLCALL -defaultProcessingInstruction(void *userData, const XML_Char *target, - const XML_Char *data) -{ - XML_DefaultCurrent((XML_Parser) userData); -} - -static void XMLCALL -nopCharacterData(void *userData, const XML_Char *s, int len) -{ -} - -static void XMLCALL -nopStartElement(void *userData, const XML_Char *name, const XML_Char **atts) -{ -} - -static void XMLCALL -nopEndElement(void *userData, const XML_Char *name) -{ -} - -static void XMLCALL -nopProcessingInstruction(void *userData, const XML_Char *target, - const XML_Char *data) -{ -} - -static void XMLCALL -markup(void *userData, const XML_Char *s, int len) -{ - FILE *fp = (FILE *)XML_GetUserData((XML_Parser) userData); - for (; len > 0; --len, ++s) - puttc(*s, fp); -} - -static void -metaLocation(XML_Parser parser) -{ - const XML_Char *uri = XML_GetBase(parser); - if (uri) - ftprintf((FILE *)XML_GetUserData(parser), T(" uri=\"%s\""), uri); - ftprintf((FILE *)XML_GetUserData(parser), - T(" byte=\"%" XML_FMT_INT_MOD "d\" nbytes=\"%d\" \ - line=\"%" XML_FMT_INT_MOD "u\" col=\"%" XML_FMT_INT_MOD "u\""), - XML_GetCurrentByteIndex(parser), - XML_GetCurrentByteCount(parser), - XML_GetCurrentLineNumber(parser), - XML_GetCurrentColumnNumber(parser)); -} - -static void -metaStartDocument(void *userData) -{ - fputts(T("\n"), (FILE *)XML_GetUserData((XML_Parser) userData)); -} - -static void -metaEndDocument(void *userData) -{ - fputts(T("\n"), (FILE *)XML_GetUserData((XML_Parser) userData)); -} - -static void XMLCALL -metaStartElement(void *userData, const XML_Char *name, - const XML_Char **atts) -{ - XML_Parser parser = (XML_Parser) userData; - FILE *fp = (FILE *)XML_GetUserData(parser); - const XML_Char **specifiedAttsEnd - = atts + XML_GetSpecifiedAttributeCount(parser); - const XML_Char **idAttPtr; - int idAttIndex = XML_GetIdAttributeIndex(parser); - if (idAttIndex < 0) - idAttPtr = 0; - else - idAttPtr = atts + idAttIndex; - - ftprintf(fp, T("\n"), fp); - do { - ftprintf(fp, T("= specifiedAttsEnd) - fputts(T("\" defaulted=\"yes\"/>\n"), fp); - else if (atts == idAttPtr) - fputts(T("\" id=\"yes\"/>\n"), fp); - else - fputts(T("\"/>\n"), fp); - } while (*(atts += 2)); - fputts(T("\n"), fp); - } - else - fputts(T("/>\n"), fp); -} - -static void XMLCALL -metaEndElement(void *userData, const XML_Char *name) -{ - XML_Parser parser = (XML_Parser) userData; - FILE *fp = (FILE *)XML_GetUserData(parser); - ftprintf(fp, T("\n"), fp); -} - -static void XMLCALL -metaProcessingInstruction(void *userData, const XML_Char *target, - const XML_Char *data) -{ - XML_Parser parser = (XML_Parser) userData; - FILE *fp = (FILE *)XML_GetUserData(parser); - ftprintf(fp, T("\n"), fp); -} - -static void XMLCALL -metaComment(void *userData, const XML_Char *data) -{ - XML_Parser parser = (XML_Parser) userData; - FILE *fp = (FILE *)XML_GetUserData(parser); - fputts(T("\n"), fp); -} - -static void XMLCALL -metaStartCdataSection(void *userData) -{ - XML_Parser parser = (XML_Parser) userData; - FILE *fp = (FILE *)XML_GetUserData(parser); - fputts(T("\n"), fp); -} - -static void XMLCALL -metaEndCdataSection(void *userData) -{ - XML_Parser parser = (XML_Parser) userData; - FILE *fp = (FILE *)XML_GetUserData(parser); - fputts(T("\n"), fp); -} - -static void XMLCALL -metaCharacterData(void *userData, const XML_Char *s, int len) -{ - XML_Parser parser = (XML_Parser) userData; - FILE *fp = (FILE *)XML_GetUserData(parser); - fputts(T("\n"), fp); -} - -static void XMLCALL -metaStartDoctypeDecl(void *userData, - const XML_Char *doctypeName, - const XML_Char *sysid, - const XML_Char *pubid, - int has_internal_subset) -{ - XML_Parser parser = (XML_Parser) userData; - FILE *fp = (FILE *)XML_GetUserData(parser); - ftprintf(fp, T("\n"), fp); -} - -static void XMLCALL -metaEndDoctypeDecl(void *userData) -{ - XML_Parser parser = (XML_Parser) userData; - FILE *fp = (FILE *)XML_GetUserData(parser); - fputts(T("\n"), fp); -} - -static void XMLCALL -metaNotationDecl(void *userData, - const XML_Char *notationName, - const XML_Char *base, - const XML_Char *systemId, - const XML_Char *publicId) -{ - XML_Parser parser = (XML_Parser) userData; - FILE *fp = (FILE *)XML_GetUserData(parser); - ftprintf(fp, T("\n"), fp); -} - - -static void XMLCALL -metaEntityDecl(void *userData, - const XML_Char *entityName, - int is_param, - const XML_Char *value, - int value_length, - const XML_Char *base, - const XML_Char *systemId, - const XML_Char *publicId, - const XML_Char *notationName) -{ - XML_Parser parser = (XML_Parser) userData; - FILE *fp = (FILE *)XML_GetUserData(parser); - - if (value) { - ftprintf(fp, T("'), fp); - characterData(fp, value, value_length); - fputts(T("\n"), fp); - } - else if (notationName) { - ftprintf(fp, T("\n"), fp); - } - else { - ftprintf(fp, T("\n"), fp); - } -} - -static void XMLCALL -metaStartNamespaceDecl(void *userData, - const XML_Char *prefix, - const XML_Char *uri) -{ - XML_Parser parser = (XML_Parser) userData; - FILE *fp = (FILE *)XML_GetUserData(parser); - fputts(T("\n"), fp); - } - else - fputts(T("/>\n"), fp); -} - -static void XMLCALL -metaEndNamespaceDecl(void *userData, const XML_Char *prefix) -{ - XML_Parser parser = (XML_Parser) userData; - FILE *fp = (FILE *)XML_GetUserData(parser); - if (!prefix) - fputts(T("\n"), fp); - else - ftprintf(fp, T("\n"), prefix); -} - -static int XMLCALL -unknownEncodingConvert(void *data, const char *p) -{ - return codepageConvert(*(int *)data, p); -} - -static int XMLCALL -unknownEncoding(void *userData, const XML_Char *name, XML_Encoding *info) -{ - int cp; - static const XML_Char prefixL[] = T("windows-"); - static const XML_Char prefixU[] = T("WINDOWS-"); - int i; - - for (i = 0; prefixU[i]; i++) - if (name[i] != prefixU[i] && name[i] != prefixL[i]) - return 0; - - cp = 0; - for (; name[i]; i++) { - static const XML_Char digits[] = T("0123456789"); - const XML_Char *s = tcschr(digits, name[i]); - if (!s) - return 0; - cp *= 10; - cp += (int)(s - digits); - if (cp >= 0x10000) - return 0; - } - if (!codepageMap(cp, info->map)) - return 0; - info->convert = unknownEncodingConvert; - /* We could just cast the code page integer to a void *, - and avoid the use of release. */ - info->release = free; - info->data = malloc(sizeof(int)); - if (!info->data) - return 0; - *(int *)info->data = cp; - return 1; -} - -static int XMLCALL -notStandalone(void *userData) -{ - return 0; -} - -static void -showVersion(XML_Char *prog) -{ - XML_Char *s = prog; - XML_Char ch; - const XML_Feature *features = XML_GetFeatureList(); - while ((ch = *s) != 0) { - if (ch == '/' -#if (defined(WIN32) || defined(__WATCOMC__)) - || ch == '\\' -#endif - ) - prog = s + 1; - ++s; - } - ftprintf(stdout, T("%s using %s\n"), prog, XML_ExpatVersion()); - if (features != NULL && features[0].feature != XML_FEATURE_END) { - int i = 1; - ftprintf(stdout, T("%s"), features[0].name); - if (features[0].value) - ftprintf(stdout, T("=%ld"), features[0].value); - while (features[i].feature != XML_FEATURE_END) { - ftprintf(stdout, T(", %s"), features[i].name); - if (features[i].value) - ftprintf(stdout, T("=%ld"), features[i].value); - ++i; - } - ftprintf(stdout, T("\n")); - } -} - -static void -usage(const XML_Char *prog, int rc) -{ - ftprintf(stderr, - T("usage: %s [-n] [-p] [-r] [-s] [-w] [-x] [-d output-dir] " - "[-e encoding] file ...\n"), prog); - exit(rc); -} - -int -tmain(int argc, XML_Char **argv) -{ - int i, j; - const XML_Char *outputDir = NULL; - const XML_Char *encoding = NULL; - unsigned processFlags = XML_MAP_FILE; - int windowsCodePages = 0; - int outputType = 0; - int useNamespaces = 0; - int requireStandalone = 0; - enum XML_ParamEntityParsing paramEntityParsing = - XML_PARAM_ENTITY_PARSING_NEVER; - int useStdin = 0; - -#ifdef _MSC_VER - _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF|_CRTDBG_LEAK_CHECK_DF); -#endif - - i = 1; - j = 0; - while (i < argc) { - if (j == 0) { - if (argv[i][0] != T('-')) - break; - if (argv[i][1] == T('-') && argv[i][2] == T('\0')) { - i++; - break; - } - j++; - } - switch (argv[i][j]) { - case T('r'): - processFlags &= ~XML_MAP_FILE; - j++; - break; - case T('s'): - requireStandalone = 1; - j++; - break; - case T('n'): - useNamespaces = 1; - j++; - break; - case T('p'): - paramEntityParsing = XML_PARAM_ENTITY_PARSING_ALWAYS; - /* fall through */ - case T('x'): - processFlags |= XML_EXTERNAL_ENTITIES; - j++; - break; - case T('w'): - windowsCodePages = 1; - j++; - break; - case T('m'): - outputType = 'm'; - j++; - break; - case T('c'): - outputType = 'c'; - useNamespaces = 0; - j++; - break; - case T('t'): - outputType = 't'; - j++; - break; - case T('d'): - if (argv[i][j + 1] == T('\0')) { - if (++i == argc) - usage(argv[0], 2); - outputDir = argv[i]; - } - else - outputDir = argv[i] + j + 1; - i++; - j = 0; - break; - case T('e'): - if (argv[i][j + 1] == T('\0')) { - if (++i == argc) - usage(argv[0], 2); - encoding = argv[i]; - } - else - encoding = argv[i] + j + 1; - i++; - j = 0; - break; - case T('h'): - usage(argv[0], 0); - return 0; - case T('v'): - showVersion(argv[0]); - return 0; - case T('\0'): - if (j > 1) { - i++; - j = 0; - break; - } - /* fall through */ - default: - usage(argv[0], 2); - } - } - if (i == argc) { - useStdin = 1; - processFlags &= ~XML_MAP_FILE; - i--; - } - for (; i < argc; i++) { - FILE *fp = 0; - XML_Char *outName = 0; - int result; - XML_Parser parser; - if (useNamespaces) - parser = XML_ParserCreateNS(encoding, NSSEP); - else - parser = XML_ParserCreate(encoding); - if (requireStandalone) - XML_SetNotStandaloneHandler(parser, notStandalone); - XML_SetParamEntityParsing(parser, paramEntityParsing); - if (outputType == 't') { - /* This is for doing timings; this gives a more realistic estimate of - the parsing time. */ - outputDir = 0; - XML_SetElementHandler(parser, nopStartElement, nopEndElement); - XML_SetCharacterDataHandler(parser, nopCharacterData); - XML_SetProcessingInstructionHandler(parser, nopProcessingInstruction); - } - else if (outputDir) { - const XML_Char * delim = T("/"); - const XML_Char *file = useStdin ? T("STDIN") : argv[i]; - if (!useStdin) { - /* Jump after last (back)slash */ - const XML_Char * lastDelim = tcsrchr(file, delim[0]); - if (lastDelim) - file = lastDelim + 1; -#if (defined(WIN32) || defined(__WATCOMC__)) - else { - const XML_Char * winDelim = T("\\"); - lastDelim = tcsrchr(file, winDelim[0]); - if (lastDelim) { - file = lastDelim + 1; - delim = winDelim; - } - } -#endif - } - outName = (XML_Char *)malloc((tcslen(outputDir) + tcslen(file) + 2) - * sizeof(XML_Char)); - tcscpy(outName, outputDir); - tcscat(outName, delim); - tcscat(outName, file); - fp = tfopen(outName, T("wb")); - if (!fp) { - tperror(outName); - exit(1); - } - setvbuf(fp, NULL, _IOFBF, 16384); -#ifdef XML_UNICODE - puttc(0xFEFF, fp); -#endif - XML_SetUserData(parser, fp); - switch (outputType) { - case 'm': - XML_UseParserAsHandlerArg(parser); - XML_SetElementHandler(parser, metaStartElement, metaEndElement); - XML_SetProcessingInstructionHandler(parser, metaProcessingInstruction); - XML_SetCommentHandler(parser, metaComment); - XML_SetCdataSectionHandler(parser, metaStartCdataSection, - metaEndCdataSection); - XML_SetCharacterDataHandler(parser, metaCharacterData); - XML_SetDoctypeDeclHandler(parser, metaStartDoctypeDecl, - metaEndDoctypeDecl); - XML_SetEntityDeclHandler(parser, metaEntityDecl); - XML_SetNotationDeclHandler(parser, metaNotationDecl); - XML_SetNamespaceDeclHandler(parser, metaStartNamespaceDecl, - metaEndNamespaceDecl); - metaStartDocument(parser); - break; - case 'c': - XML_UseParserAsHandlerArg(parser); - XML_SetDefaultHandler(parser, markup); - XML_SetElementHandler(parser, defaultStartElement, defaultEndElement); - XML_SetCharacterDataHandler(parser, defaultCharacterData); - XML_SetProcessingInstructionHandler(parser, - defaultProcessingInstruction); - break; - default: - if (useNamespaces) - XML_SetElementHandler(parser, startElementNS, endElementNS); - else - XML_SetElementHandler(parser, startElement, endElement); - XML_SetCharacterDataHandler(parser, characterData); -#ifndef W3C14N - XML_SetProcessingInstructionHandler(parser, processingInstruction); -#endif /* not W3C14N */ - break; - } - } - if (windowsCodePages) - XML_SetUnknownEncodingHandler(parser, unknownEncoding, 0); - result = XML_ProcessFile(parser, useStdin ? NULL : argv[i], processFlags); - if (outputDir) { - if (outputType == 'm') - metaEndDocument(parser); - fclose(fp); - if (!result) - tremove(outName); - free(outName); - } - XML_ParserFree(parser); - } - return 0; -} diff --git a/cextern/expat/xmlwf/xmlwf.dsp b/cextern/expat/xmlwf/xmlwf.dsp deleted file mode 100755 index cbcff43b1710..000000000000 --- a/cextern/expat/xmlwf/xmlwf.dsp +++ /dev/null @@ -1,139 +0,0 @@ -# Microsoft Developer Studio Project File - Name="xmlwf" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Console Application" 0x0103 - -CFG=xmlwf - Win32 Release -!MESSAGE This is not a valid makefile. To build this project using NMAKE, -!MESSAGE use the Export Makefile command and run -!MESSAGE -!MESSAGE NMAKE /f "xmlwf.mak". -!MESSAGE -!MESSAGE You can specify a configuration when running NMAKE -!MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE -!MESSAGE NMAKE /f "xmlwf.mak" CFG="xmlwf - Win32 Release" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "xmlwf - Win32 Release" (based on "Win32 (x86) Console Application") -!MESSAGE "xmlwf - Win32 Debug" (based on "Win32 (x86) Console Application") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -CPP=cl.exe -RSC=rc.exe - -!IF "$(CFG)" == "xmlwf - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir ".\Release" -# PROP BASE Intermediate_Dir ".\Release" -# PROP BASE Target_Dir "." -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "..\win32\bin\Release" -# PROP Intermediate_Dir "..\win32\tmp\Release-xmlwf" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "." -# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c -# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\lib" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "COMPILED_FROM_DSP" /FD /c -# SUBTRACT CPP /YX /Yc /Yu -# ADD BASE RSC /l 0x809 /d "NDEBUG" -# ADD RSC /l 0x409 /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 -# ADD LINK32 setargv.obj kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /pdb:none /machine:I386 -# SUBTRACT LINK32 /nodefaultlib - -!ELSEIF "$(CFG)" == "xmlwf - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir ".\Debug" -# PROP BASE Intermediate_Dir ".\Debug" -# PROP BASE Target_Dir "." -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "..\win32\bin\Debug" -# PROP Intermediate_Dir "..\win32\tmp\Debug-xmlwf" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "." -# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /c -# ADD CPP /nologo /MTd /W3 /GX /ZI /Od /I "..\lib" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "COMPILED_FROM_DSP" /FD /c -# SUBTRACT CPP /Fr /YX -# ADD BASE RSC /l 0x809 /d "_DEBUG" -# ADD RSC /l 0x409 /d "_DEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 -# ADD LINK32 setargv.obj kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /pdb:none /debug /machine:I386 - -!ENDIF - -# Begin Target - -# Name "xmlwf - Win32 Release" -# Name "xmlwf - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat;for;f90" -# Begin Source File - -SOURCE=.\codepage.c -# End Source File -# Begin Source File - -SOURCE=.\readfilemap.c -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=.\unixfilemap.c -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=.\win32filemap.c -# End Source File -# Begin Source File - -SOURCE=.\xmlfile.c -# End Source File -# Begin Source File - -SOURCE=.\xmlwf.c -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl;fi;fd" -# Begin Source File - -SOURCE=.\codepage.h -# End Source File -# Begin Source File - -SOURCE=.\xmlfile.h -# End Source File -# Begin Source File - -SOURCE=.\xmltchar.h -# End Source File -# End Group -# Begin Group "Resource Files" - -# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe" -# End Group -# End Target -# End Project diff --git a/cextern/expat/xmlwf/xmlwin32url.cxx b/cextern/expat/xmlwf/xmlwin32url.cxx deleted file mode 100755 index bbfcce22c964..000000000000 --- a/cextern/expat/xmlwf/xmlwin32url.cxx +++ /dev/null @@ -1,395 +0,0 @@ -#include "expat.h" -#ifdef XML_UNICODE -#define UNICODE -#endif -#include -#include -#include -#include -#include -#include "xmlurl.h" -#include "xmlmime.h" - -static int -processURL(XML_Parser parser, IMoniker *baseMoniker, const XML_Char *url); - -typedef void (*StopHandler)(void *, HRESULT); - -class Callback : public IBindStatusCallback { -public: - // IUnknown methods - STDMETHODIMP QueryInterface(REFIID,void **); - STDMETHODIMP_(ULONG) AddRef(); - STDMETHODIMP_(ULONG) Release(); - // IBindStatusCallback methods - STDMETHODIMP OnStartBinding(DWORD, IBinding *); - STDMETHODIMP GetPriority(LONG *); - STDMETHODIMP OnLowResource(DWORD); - STDMETHODIMP OnProgress(ULONG, ULONG, ULONG, LPCWSTR); - STDMETHODIMP OnStopBinding(HRESULT, LPCWSTR); - STDMETHODIMP GetBindInfo(DWORD *, BINDINFO *); - STDMETHODIMP OnDataAvailable(DWORD, DWORD, FORMATETC *, STGMEDIUM *); - STDMETHODIMP OnObjectAvailable(REFIID, IUnknown *); - Callback(XML_Parser, IMoniker *, StopHandler, void *); - ~Callback(); - int externalEntityRef(const XML_Char *context, - const XML_Char *systemId, const XML_Char *publicId); -private: - XML_Parser parser_; - IMoniker *baseMoniker_; - DWORD totalRead_; - ULONG ref_; - IBinding *pBinding_; - StopHandler stopHandler_; - void *stopArg_; -}; - -STDMETHODIMP_(ULONG) -Callback::AddRef() -{ - return ref_++; -} - -STDMETHODIMP_(ULONG) -Callback::Release() -{ - if (--ref_ == 0) { - delete this; - return 0; - } - return ref_; -} - -STDMETHODIMP -Callback::QueryInterface(REFIID riid, void** ppv) -{ - if (IsEqualGUID(riid, IID_IUnknown)) - *ppv = (IUnknown *)this; - else if (IsEqualGUID(riid, IID_IBindStatusCallback)) - *ppv = (IBindStatusCallback *)this; - else - return E_NOINTERFACE; - ((LPUNKNOWN)*ppv)->AddRef(); - return S_OK; -} - -STDMETHODIMP -Callback::OnStartBinding(DWORD, IBinding* pBinding) -{ - pBinding_ = pBinding; - pBinding->AddRef(); - return S_OK; -} - -STDMETHODIMP -Callback::GetPriority(LONG *) -{ - return E_NOTIMPL; -} - -STDMETHODIMP -Callback::OnLowResource(DWORD) -{ - return E_NOTIMPL; -} - -STDMETHODIMP -Callback::OnProgress(ULONG, ULONG, ULONG, LPCWSTR) -{ - return S_OK; -} - -STDMETHODIMP -Callback::OnStopBinding(HRESULT hr, LPCWSTR szError) -{ - if (pBinding_) { - pBinding_->Release(); - pBinding_ = 0; - } - if (baseMoniker_) { - baseMoniker_->Release(); - baseMoniker_ = 0; - } - stopHandler_(stopArg_, hr); - return S_OK; -} - -STDMETHODIMP -Callback::GetBindInfo(DWORD* pgrfBINDF, BINDINFO* pbindinfo) -{ - *pgrfBINDF = BINDF_ASYNCHRONOUS; - return S_OK; -} - -static void -reportError(XML_Parser parser) -{ - int code = XML_GetErrorCode(parser); - const XML_Char *message = XML_ErrorString(code); - if (message) - _ftprintf(stderr, _T("%s:%d:%ld: %s\n"), - XML_GetBase(parser), - XML_GetErrorLineNumber(parser), - XML_GetErrorColumnNumber(parser), - message); - else - _ftprintf(stderr, _T("%s: (unknown message %d)\n"), - XML_GetBase(parser), code); -} - -STDMETHODIMP -Callback::OnDataAvailable(DWORD grfBSCF, - DWORD dwSize, - FORMATETC *pfmtetc, - STGMEDIUM* pstgmed) -{ - if (grfBSCF & BSCF_FIRSTDATANOTIFICATION) { - IWinInetHttpInfo *hp; - HRESULT hr = pBinding_->QueryInterface(IID_IWinInetHttpInfo, - (void **)&hp); - if (SUCCEEDED(hr)) { - char contentType[1024]; - DWORD bufSize = sizeof(contentType); - DWORD flags = 0; - contentType[0] = 0; - hr = hp->QueryInfo(HTTP_QUERY_CONTENT_TYPE, contentType, - &bufSize, 0, NULL); - if (SUCCEEDED(hr)) { - char charset[CHARSET_MAX]; - getXMLCharset(contentType, charset); - if (charset[0]) { -#ifdef XML_UNICODE - XML_Char wcharset[CHARSET_MAX]; - XML_Char *p1 = wcharset; - const char *p2 = charset; - while ((*p1++ = (unsigned char)*p2++) != 0) - ; - XML_SetEncoding(parser_, wcharset); -#else - XML_SetEncoding(parser_, charset); -#endif - } - } - hp->Release(); - } - } - if (!parser_) - return E_ABORT; - if (pstgmed->tymed == TYMED_ISTREAM) { - while (totalRead_ < dwSize) { -#define READ_MAX (64*1024) - DWORD nToRead = dwSize - totalRead_; - if (nToRead > READ_MAX) - nToRead = READ_MAX; - void *buf = XML_GetBuffer(parser_, nToRead); - if (!buf) { - _ftprintf(stderr, _T("out of memory\n")); - return E_ABORT; - } - DWORD nRead; - HRESULT hr = pstgmed->pstm->Read(buf, nToRead, &nRead); - if (SUCCEEDED(hr)) { - totalRead_ += nRead; - if (!XML_ParseBuffer(parser_, - nRead, - (grfBSCF & BSCF_LASTDATANOTIFICATION) != 0 - && totalRead_ == dwSize)) { - reportError(parser_); - return E_ABORT; - } - } - } - } - return S_OK; -} - -STDMETHODIMP -Callback::OnObjectAvailable(REFIID, IUnknown *) -{ - return S_OK; -} - -int -Callback::externalEntityRef(const XML_Char *context, - const XML_Char *systemId, - const XML_Char *publicId) -{ - XML_Parser entParser = XML_ExternalEntityParserCreate(parser_, context, 0); - XML_SetBase(entParser, systemId); - int ret = processURL(entParser, baseMoniker_, systemId); - XML_ParserFree(entParser); - return ret; -} - -Callback::Callback(XML_Parser parser, IMoniker *baseMoniker, - StopHandler stopHandler, void *stopArg) -: parser_(parser), - baseMoniker_(baseMoniker), - ref_(0), - pBinding_(0), - totalRead_(0), - stopHandler_(stopHandler), - stopArg_(stopArg) -{ - if (baseMoniker_) - baseMoniker_->AddRef(); -} - -Callback::~Callback() -{ - if (pBinding_) - pBinding_->Release(); - if (baseMoniker_) - baseMoniker_->Release(); -} - -static int -externalEntityRef(void *arg, - const XML_Char *context, - const XML_Char *base, - const XML_Char *systemId, - const XML_Char *publicId) -{ - return ((Callback *)arg)->externalEntityRef(context, systemId, publicId); -} - - -static HRESULT -openStream(XML_Parser parser, - IMoniker *baseMoniker, - const XML_Char *uri, - StopHandler stopHandler, void *stopArg) -{ - if (!XML_SetBase(parser, uri)) - return E_OUTOFMEMORY; - HRESULT hr; - IMoniker *m; -#ifdef XML_UNICODE - hr = CreateURLMoniker(0, uri, &m); -#else - LPWSTR uriw = new wchar_t[strlen(uri) + 1]; - for (int i = 0;; i++) { - uriw[i] = uri[i]; - if (uriw[i] == 0) - break; - } - hr = CreateURLMoniker(baseMoniker, uriw, &m); - delete [] uriw; -#endif - if (FAILED(hr)) - return hr; - IBindStatusCallback *cb = new Callback(parser, m, stopHandler, stopArg); - XML_SetExternalEntityRefHandler(parser, externalEntityRef); - XML_SetExternalEntityRefHandlerArg(parser, cb); - cb->AddRef(); - IBindCtx *b; - if (FAILED(hr = CreateAsyncBindCtx(0, cb, 0, &b))) { - cb->Release(); - m->Release(); - return hr; - } - cb->Release(); - IStream *pStream; - hr = m->BindToStorage(b, 0, IID_IStream, (void **)&pStream); - if (SUCCEEDED(hr)) { - if (pStream) - pStream->Release(); - } - if (hr == MK_S_ASYNCHRONOUS) - hr = S_OK; - m->Release(); - b->Release(); - return hr; -} - -struct QuitInfo { - const XML_Char *url; - HRESULT hr; - int stop; -}; - -static void -winPerror(const XML_Char *url, HRESULT hr) -{ - LPVOID buf; - if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER - | FORMAT_MESSAGE_FROM_HMODULE, - GetModuleHandleA("urlmon.dll"), - hr, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPTSTR) &buf, - 0, - NULL) - || FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER - | FORMAT_MESSAGE_FROM_SYSTEM, - 0, - hr, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPTSTR) &buf, - 0, - NULL)) { - /* The system error messages seem to end with a newline. */ - _ftprintf(stderr, _T("%s: %s"), url, buf); - fflush(stderr); - LocalFree(buf); - } - else - _ftprintf(stderr, _T("%s: error %x\n"), url, hr); -} - -static void -threadQuit(void *p, HRESULT hr) -{ - QuitInfo *qi = (QuitInfo *)p; - qi->hr = hr; - qi->stop = 1; -} - -extern "C" -int -XML_URLInit(void) -{ - return SUCCEEDED(CoInitialize(0)); -} - -extern "C" -void -XML_URLUninit(void) -{ - CoUninitialize(); -} - -static int -processURL(XML_Parser parser, IMoniker *baseMoniker, - const XML_Char *url) -{ - QuitInfo qi; - qi.stop = 0; - qi.url = url; - - XML_SetBase(parser, url); - HRESULT hr = openStream(parser, baseMoniker, url, threadQuit, &qi); - if (FAILED(hr)) { - winPerror(url, hr); - return 0; - } - else if (FAILED(qi.hr)) { - winPerror(url, qi.hr); - return 0; - } - MSG msg; - while (!qi.stop && GetMessage (&msg, NULL, 0, 0)) { - TranslateMessage (&msg); - DispatchMessage (&msg); - } - return 1; -} - -extern "C" -int -XML_ProcessURL(XML_Parser parser, - const XML_Char *url, - unsigned flags) -{ - return processURL(parser, 0, url); -} diff --git a/cextern/trim_cfitsio.sh b/cextern/trim_cfitsio.sh new file mode 100755 index 000000000000..5fbcbf9925b7 --- /dev/null +++ b/cextern/trim_cfitsio.sh @@ -0,0 +1,75 @@ +#!/bin/sh + +set -euv + +# This script should be run every time cfitsio is updated. +# This moves all the code needed for the actual library to lib +# and deletes everything else (except License.txt and ChangeLog) + +# So, the standard update would be to execute, from this directory, +# rm -rf cfitsio +# tar xvf # (e.g., cfitsio-4.2.0.tar.gz) +# mv cfitsio-?.?.? cfitsio # (e.g., mv cfitsio-4.2.0 cfitsio) +# ./trim_cfitsio.sh + +if [ ! -d cfitsio/lib ]; then + mkdir cfitsio/lib +fi + +mv cfitsio/fits_hcompress.c cfitsio/lib/ +mv cfitsio/fits_hdecompress.c cfitsio/lib/ +mv cfitsio/pliocomp.c cfitsio/lib/ +mv cfitsio/quantize.c cfitsio/lib/ +mv cfitsio/ricecomp.c cfitsio/lib/ + +rm -f cfitsio/.gitattributes +rm -f cfitsio/.gitignore +rm -f cfitsio/README +rm -f cfitsio/INSTALL +rm -f cfitsio/configure +rm -f cfitsio/install-sh +rm -f cfitsio/run-testprog +rm -rf cfitsio/.github +rm -rf cfitsio/docs/ +rm -rf cfitsio/[^L]*.* +rm -rf cfitsio/utilities +rm -rf cfitsio/cmake +rm -rf cfitsio/config +rm -rf cfitsio/m4 + +# We only use a very small subset of fitsio2.h, so here we generate that +# file. If there are compilation issues after updating, it may be that +# the definitions below need tweaking or that some definitions need to be +# removed or added. +cat < cfitsio/lib/fitsio2.h +#ifndef LONGLONG_TYPE +typedef long long LONGLONG; +typedef unsigned long long ULONGLONG; +#define LONGLONG_TYPE +#endif + +# define DATA_COMPRESSION_ERR 413 +# define DATA_DECOMPRESSION_ERR 414 + +void ffpmsg(const char *err_message); + +#define FFLOCK +#define FFUNLOCK + +#define N_RANDOM 10000 + +#define MEMORY_ALLOCATION 113 + +#define NO_DITHER -1 +#define SUBTRACTIVE_DITHER_1 1 +#define SUBTRACTIVE_DITHER_2 2 + +int fits_init_randoms(void); +EOF + + +cat <cfitsio/README.rst +This directory only contains the small subset of files from CFITSIO which are +required for the astropy.io.fits.hdu.compressed._tiled_compression package. All files are +copied verbatim from CFITSIO and can easily be updated if needed. +EOF diff --git a/cextern/trim_wcslib.sh b/cextern/trim_wcslib.sh index 007e265be8f1..3422caa0331a 100755 --- a/cextern/trim_wcslib.sh +++ b/cextern/trim_wcslib.sh @@ -4,10 +4,14 @@ # This removes extra large files from wcslib that aren't needed. +rm -rf wcslib/C/cyril +rm -rf wcslib/C/flexed/RCS +rm -rf wcslib/C/RCS rm -rf wcslib/C/test rm -rf wcslib/doxygen rm -rf wcslib/Fortran rm -rf wcslib/html rm -rf wcslib/pgsbox +rm -rf wcslib/RCS/ rm -rf wcslib/utils rm wcslib/*.pdf diff --git a/cextern/wcslib/C/GNUmakefile b/cextern/wcslib/C/GNUmakefile index 574134a1c410..1f2a77b06b83 100644 --- a/cextern/wcslib/C/GNUmakefile +++ b/cextern/wcslib/C/GNUmakefile @@ -1,5 +1,5 @@ #----------------------------------------------------------------------------- -# GNU makefile for building WCSLIB 4.14 and its test suite. +# GNU makefile for building WCSLIB 8.6 and its test suite. # # Summary of the main targets # --------------------------- @@ -10,7 +10,7 @@ # cleaner: clean, and also delete the test executables. # # cleanest (distclean or realclean): cleaner, and also delete the object -# library the C source files generated by 'flex'. +# library and the C source files generated by 'flex'. # # check (or test): Compile and run the test programs. By default they are # executed in batch mode, and non-graphical tests only report @@ -30,10 +30,11 @@ # instead and re-run configure. # # Author: Mark Calabretta, Australia Telescope National Facility, CSIRO. -# http://www.atnf.csiro.au/people/Mark.Calabretta -# $Id: GNUmakefile,v 4.14 2012/07/13 10:02:27 cal103 Exp $ +# http://www.atnf.csiro.au/computing/software/wcs +# $Id: GNUmakefile,v 8.6 2026/03/29 13:53:56 mcalabre Exp $ #----------------------------------------------------------------------------- # Get configure settings. +SUBDIR := C include ../makedefs FLEXMODS := $(patsubst %.l,%.c,$(wildcard *.l)) @@ -50,6 +51,8 @@ else endif endif +LIBLOCK := lib.lock + # For building the sharable library. PICLIB := libwcs-PIC.a @@ -62,46 +65,65 @@ vpath %.in .. # For building and exercising the test suite # ------------------------------------------ -# Signals tfitshdr to use wcshdr(). -ifneq "$(DO_WCSHDR)" "" - CPPFLAGS += -DDO_WCSHDR -endif - # Test programs that don't require CFITSIO or PGPLOT... -TEST_N := tlin tlog tprj1 tsph tsphdpa tspx ttab1 twcs twcssub tpih1 tbth1 \ - tfitshdr tunits twcsfix +TEST_N := tlin tdis1 tdis2 tlog tprj1 tsph tsphdpa tspx ttab1 twcs twcssub \ + tpih1 tbth1 tfitshdr tunits twcsfix twcscompare # ...and unofficial test programs. -TEST_n := tspcaips tspctrne twcs_locale +TEST_n := tdisiter tspcaips tspcspxe tspctrne twcs_locale # Test programs that require CFITSIO (they don't need PGPLOT). -TEST_C := twcstab twcshdr +TEST_C := twcstab twcshdr tdis3 twcslint # Test programs that require PGPLOT but not PGSBOX. TEST_P := tspc tprj2 tcel1 tcel2 ttab2 ttab3 twcsmix -# Test programs that require PGPLOT and PGSBOX. +# Test programs that require PGSBOX (and therefore PGPLOT). TEST_B := tpih2 -# Test programs that aren't automatically exercised. +# Test programs for POSIX threads. +TEST_T := tpih_pthread twcs_pthread + +# Test programs that are compiled but not automatically exercised. TEST_X := tsphdpa twcshdr + TESTS := $(TEST_N) -# Add test programs that require CFITSIO if we have it. -ifneq "$(CFITSIOINC)" "" -ifneq "$(CFITSIOLIB)" "" - TESTS += $(TEST_C) +# Do we have CFITSIO? +DO_CFITSIO := 1 +ifeq "$(CFITSIOINC)" "" + DO_CFITSIO := 0 +else ifeq "$(CFITSIOLIB)" "" + DO_CFITSIO := 0 endif + +ifeq "$(DO_CFITSIO)" "1" + # Yes, add test programs that use it. + TESTS += $(TEST_C) + CFITSIO_CFLAGS := $(filter-out -Wpadded,$(CFLAGS)) +else + # No, amend TEST_X. + TEST_X := $(filter-out $(TEST_C),$(TEST_X)) endif -# Add test programs that require PGPLOT if we have it. +# Do we have PGPLOT? +DO_PGPLOT := 0 ifneq "$(CHECK)" "nopgplot" -ifneq "$(PGPLOTINC)" "" -ifneq "$(PGPLOTLIB)" "" - TESTS += $(TEST_P) $(TEST_B) -endif -endif + DO_PGPLOT := 1 + ifeq "$(PGPLOTINC)" "" + DO_PGPLOT := 0 + else ifeq "$(PGPLOTLIB)" "" + DO_PGPLOT := 0 + endif + + ifeq "$(DO_PGPLOT)" "1" + # Yes, add test programs that use it. + TESTS += $(TEST_P) $(TEST_B) + else + # No, amend TEST_X. + TEST_X := $(filter-out $(TEST_P) $(TEST_B),$(TEST_X)) + endif endif # Remove tests that aren't automatically exercised. @@ -109,6 +131,8 @@ TESTS := $(filter-out $(TEST_X), $(TESTS)) PGSBOXLIB := ../pgsbox/libpgsbox-$(LIBVER).a +ADDRE := 0x[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]* + # Pattern rules #-------------- @@ -127,15 +151,23 @@ endif $(WCSLIB)(%.o) : %.c -@ echo '' $(CC) $(CPPFLAGS) $(CFLAGS) -c $< - $(AR) r $(WCSLIB) $% - -@ $(RM) $% + @ if [ ! -f $(LIBLOCK) ] ; then \ + echo $(AR) r$(ARFLAGS) $(WCSLIB) $% ; \ + $(AR) r$(ARFLAGS) $(WCSLIB) $% ; \ + $(RM) $% ; \ + fi -$(PICLIB)(%.o) : %.c +$(PICLIB)(%.o) : $(WCSLIB)(%.o) -@ echo '' - $(CC) $(CPPFLAGS) $(CFLAGS) $(SHRFLAGS) -c $< - $(AR) r $(PICLIB) $% - -@ $(RM) $% + $(CC) $(CPPFLAGS) $(CFLAGS) $(SHRFLAGS) -c $(%:.o=.c) + @ if [ ! -f $(LIBLOCK) ] ; then \ + echo $(AR) r$(ARFLAGS) $(PICLIB) $% ; \ + $(AR) r$(ARFLAGS) $(PICLIB) $% ; \ + $(RM) $% ; \ + fi +# May need to create temporary symlinks to include file directories for +# CFITSIO, etc. for the following two rules. %.i : %.c -@ echo '' -@ $(RM) $@ @@ -146,9 +178,15 @@ $(PICLIB)(%.o) : %.c -@ echo '' -@ $(CPP) $(CPPFLAGS) $(CFLAGS) $< | \ sed -n -e 's|^# 1 "\([^/].*\.h\)".*|\1|p' | \ + sed -e 's|.*/||' | \ sort -u -# Use 'make FLAVOUR=Linux run_%' to have VALGRIND defined (from flavours). +%.fits : test/%.keyrec ../utils/tofits + ../utils/tofits < $< > $@ + +# Use 'make VALGRIND=T run_%' to have VALGRIND defined (from flavours). +# Use 'make VALGRIND=T check < /dev/null |& tee check_valgrind.log' to run +# valgrind on the lot. run_% : % -@ echo '' -@ $(TIMER) @@ -177,22 +215,28 @@ run_% : % ./$< < /dev/null > $<.out 2>&1 ; \ fi ; \ fi ; \ - if grep 'PASS:' $<.out > /dev/null ; then \ + if grep 'FAIL:' $<.out > /dev/null ; then \ + if [ '$(MODE)' != interactive ] ; then \ + head -2 $<.out ; \ + grep 'FAIL:' $<.out ; \ + fi ; \ + echo 'FAIL: C/$<' >> test_results ; \ + elif grep 'PASS:' $<.out > /dev/null ; then \ if [ '$(MODE)' != interactive ] ; then \ head -2 $<.out ; \ grep 'PASS:' $<.out ; \ fi ; \ echo 'PASS: C/$<' >> test_results ; \ elif [ -f 'test/$<.out' ] ; then \ - trap 'rm -f run_test.tmp' 0 1 2 3 15 ; \ - sed -e 's/0x[0-9a-f][0-9a-f][0-9a-f]*/0x
/g' $<.out > \ - run_test.tmp ; \ - mv -f run_test.tmp $<.out ; \ + trap 'rm -f run_$<.tmp' 0 1 2 3 15 ; \ + sed -e 's/$(ADDRE)/0x
/g' \ + -e 's/chksum:.*/chksum: /' $<.out > \ + run_$<.tmp ; \ + mv -f run_$<.tmp $<.out ; \ if cmp -s $<.out test/$<.out ; then \ if [ '$(MODE)' != interactive ] ; then \ - head -1 $<.out ; \ + head -2 $<.out ; \ fi ; \ - echo '' ; \ echo 'PASS: Output agrees with C/test/$<.out' ; \ echo 'PASS: C/$<' >> test_results ; \ else \ @@ -223,15 +267,29 @@ run_% : % #-------------------------------- .PHONY : build check clean cleaner cleanest distclean install lib realclean \ - run_% test tests + run_% test tests uninstall build : lib -lib : $(FLEXMODS) $(WCSLIB) $(SHRLIB) +lib : $(FLEXMODS) + -@ echo '' + -@ echo 'Building WCSLIB C library...' + @ $(MAKE) --no-print-directory $(WCSLIB) -$(WCSLIB) : $(MODULES:%=$(WCSLIB)(%)) +$(WCSLIB) : $(LIBLOCK) $(MODULES:%=$(WCSLIB)(%)) -@ echo '' - $(RANLIB) $@ + @ set *.o ; \ + if [ "$$1" != "*.o" ] ; then \ + echo $(AR) r$(ARFLAGS) $@ *.o ; \ + $(AR) r$(ARFLAGS) $@ *.o ; \ + echo $(RANLIB) $@ ; \ + $(RANLIB) $@ ; \ + $(RM) *.o ; \ + fi + -@ $(RM) $< + @ if [ "$(SHRLIB)" != "" ] ; then \ + $(MAKE) --no-print-directory $(SHRLIB) ; \ + fi $(SHRLIB) : $(PICLIB) -@ echo '' @@ -243,20 +301,44 @@ $(SHRLIB) : $(PICLIB) $(SHRLD) -o $@ *.o $(LDFLAGS) $(LIBS) && \ mv $@ .. -$(PICLIB) : $(MODULES:%.o=$(PICLIB)(%.o)) ; +$(PICLIB) : $(LIBLOCK) $(MODULES:%=$(PICLIB)(%)) + -@ echo '' + @ set *.o ; \ + if [ "$$1" != "*.o" ] ; then \ + echo $(AR) r$(ARFLAGS) $@ *.o ; \ + $(AR) r$(ARFLAGS) $@ *.o ; \ + $(RM) *.o ; \ + fi + -@ $(RM) $< + +$(LIBLOCK) : FORCE + @ $(RM) *.o + @ touch $@ install : build - if [ ! -d "$(LIBDIR)" ] ; then \ - $(INSTALL) -d -m 2775 $(LIBDIR) ; \ + $(INSTALL) -d -m 775 $(LIBDIR) ; \ + fi + if [ "$(ARFLAGS)" = U ] ; then \ + $(RM) -r tmp ; \ + mkdir tmp && \ + cd tmp && \ + trap 'cd .. ; $(RM) -r tmp' 0 1 2 3 15 ; \ + $(AR) x ../$(WCSLIB) && \ + $(AR) rD $(WCSLIB) *.o && \ + $(INSTALL) -m 644 $(WCSLIB) $(LIBDIR) ; \ + cd .. ; \ + $(RM) -r tmp ; \ + else \ + $(INSTALL) -m 644 $(WCSLIB) $(LIBDIR) ; \ fi - $(INSTALL) -m 644 $(WCSLIB) $(LIBDIR) $(RANLIB) $(LIBDIR)/$(WCSLIB) - if [ -h "$(LIBDIR)/libwcs.a" ] ; then \ $(RM) $(LIBDIR)/libwcs.a ; \ fi - - $(LN_S) $(WCSLIB) $(LIBDIR)/libwcs.a - - if [ "$(SHRLIB)" != "" ] ; then \ - $(INSTALL) -m 644 $(SHRLIB) $(LIBDIR) ; \ + $(LN_S) $(WCSLIB) $(LIBDIR)/libwcs.a + if [ "$(SHRLIB)" != "" ] ; then \ + $(INSTALL) -m 755 $(SHRLIB) $(LIBDIR) ; \ if [ -h "$(LIBDIR)/$(SONAME)" ] ; then \ $(RM) $(LIBDIR)/$(SONAME) ; \ fi ; \ @@ -269,20 +351,28 @@ install : build fi ; \ fi - if [ ! -d "$(INCDIR)" ] ; then \ - $(INSTALL) -d -m 2775 $(INCDIR) ; \ + $(INSTALL) -d -m 775 $(INCDIR) ; \ fi $(INSTALL) -m 444 *.h $(INCDIR) - $(RM) $(INCLINK) + - $(RM) $(INCLINK) $(LN_S) $(notdir $(INCDIR)) $(INCLINK) +uninstall : + - cd $(LIBDIR) && $(RM) $(WCSLIB) $(SHRLN) $(SONAME) $(SHRLIB) + - $(RM) $(INCDIR) + - $(RM) $(INCLINK) + clean : - - $(RM) -r *.o *.i a.out t*.out core *.dSYM $(EXTRA_CLEAN) + - $(RM) *.o $(LIBLOCK) *.i a.out t*.out core *.dSYM + - $(RM) -r $(EXTRA_CLEAN) cleaner : clean - $(RM) .gdb_history - - $(RM) $(TEST_N) $(TEST_n) $(TEST_X) - - $(RM) $(TEST_P) tpih2 twcstab twcshdr - - $(RM) tofits bth.fits pih.fits wcstab.fits + - $(RM) $(TEST_N) $(TEST_n) $(TEST_T) $(TEST_X) + - $(RM) $(TEST_P) tdis3 tpih2 twcshdr twcslint twcstab + - $(RM) bth.fits fitshdr.fits pih.fits wcslint.fits wcspcx.fits + - $(RM) wcstab.fits SIP.fits SIPTPV.fits TPV3.fits TPV5.fits + - $(RM) TPV7.fits DSS.fits TNX.fits ZPX.fits - $(RM) t*_cfitsio test_results cleanest distclean realclean : cleaner @@ -294,17 +384,50 @@ check test : tests $(TESTS:%=run_%) tests : $(TESTS) $(TEST_X) +# TEST_N and TEST_n programs (no special libraries required). $(TEST_N) $(TEST_n) : % : test/%.c $(WCSLIB) -@ echo '' $(CC) $(CPPFLAGS) $(CFLAGS) -o $@ $< $(LDFLAGS) $(WCSLIB) $(LIBS) -@ $(RM) $@.o +# TEST_N programs (optionally using CFITSIO). +tpih1_cfitsio tbth1_cfitsio tfitshdr_cfitsio : %_cfitsio : test/%.c $(WCSLIB) + -@ echo '' + $(CC) -DDO_CFITSIO $(CPPFLAGS) $(CFITSIOINC) $(CFITSIO_CFLAGS) \ + -o $@ $< $(LDFLAGS) $(CFITSIOLIB) $(WCSLIB) $(LIBS) + -@ $(RM) $@.o + +# TEST_C programs (using CFITSIO). +twcstab : test/twcstab.c $(WCSLIB) $(GETWCSTAB) + -@ echo '' + $(CC) $(CPPFLAGS) $(CFITSIOINC) $(CFITSIO_CFLAGS) -o $@ $< \ + $(GETWCSTAB) $(LDFLAGS) $(CFITSIOLIB) $(WCSLIB) $(LIBS) + -@ $(RM) $@.o $(GETWCSTAB) + +twcshdr : test/twcshdr.c $(WCSLIB) $(GETWCSTAB) + -@ echo '' + $(CC) $(CPPFLAGS) $(CFITSIOINC) $(CFITSIO_CFLAGS) -o $@ $< \ + $(GETWCSTAB) $(LDFLAGS) $(CFITSIOLIB) $(WCSLIB) $(LIBS) + -@ $(RM) $@.o $(GETWCSTAB) + +tdis3 : test/tdis3 + -@ echo '' + cp $< . + -@ chmod a+x $@ + +twcslint : test/twcslint + -@ echo '' + cp $< . + -@ chmod a+x $@ + +# TEST_P programs (using PGPLOT). $(TEST_P) : % : test/%.c $(WCSLIB) -@ echo '' $(CC) $(CPPFLAGS) $(PGPLOTINC) $(CFLAGS) -c -o $@.o $< $(LD) -o $@ $@.o $(LDFLAGS) $(PGPLOTLIB) $(WCSLIB) $(FLIBS) $(LIBS) -@ $(RM) $@.o +# TEST_B programs (PGSBOX and PGPLOT). tpih2 : test/tpih2.c $(PGSBOXLIB) $(WCSLIB) -@ echo '' $(CC) $(CPPFLAGS) -I../pgsbox $(PGPLOTINC) $(CFLAGS) -c -o $@.o $< @@ -312,48 +435,31 @@ tpih2 : test/tpih2.c $(PGSBOXLIB) $(WCSLIB) $(FLIBS) $(LIBS) -@ $(RM) $@.o -tfitshdr_cfitsio tpih1_cfitsio tbth1_cfitsio : %_cfitsio : test/%.c $(WCSLIB) - -@ echo '' - $(CC) -DDO_CFITSIO $(CPPFLAGS) $(CFITSIOINC) $(CFLAGS) \ - -o $@ $< $(LDFLAGS) $(CFITSIOLIB) $(WCSLIB) $(LIBS) - -@ $(RM) $@.o - tpih2_cfitsio : test/tpih2.c $(PGSBOXLIB) $(WCSLIB) -@ echo '' $(CC) -DDO_CFITSIO $(CPPFLAGS) -I../pgsbox $(PGPLOTINC) \ - $(CFITSIOINC) $(CFLAGS) -c -o $@.o $< + $(CFITSIOINC) $(CFITSIO_CFLAGS) -c -o $@.o $< $(LD) -o $@ $@.o $(LDFLAGS) $(PGSBOXLIB) $(PGPLOTLIB) \ $(CFITSIOLIB) $(WCSLIB) $(FLIBS) $(LIBS) -@ $(RM) $@.o -twcstab : test/twcstab.c $(GETWCSTAB) $(WCSLIB) - -@ echo '' - $(CC) $(CPPFLAGS) $(CFITSIOINC) $(CFLAGS) -o $@ $< $(GETWCSTAB) \ - $(LDFLAGS) $(CFITSIOLIB) $(WCSLIB) $(LIBS) - -@ $(RM) $@.o - -twcshdr : test/twcshdr.c $(GETWCSTAB) $(WCSLIB) +# POSIX threads test programs. +$(TEST_T) : %_pthread : test/%_pthread.c $(WCSLIB) -@ echo '' - $(CC) $(CPPFLAGS) $(CFITSIOINC) $(CFLAGS) -o $@ $< $(GETWCSTAB) \ - $(LDFLAGS) $(CFITSIOLIB) $(WCSLIB) $(LIBS) + $(CC) $(CPPFLAGS) $(CFLAGS) -pthread -c -o $@.o $< + $(LD) -o $@ $@.o $(LDFLAGS) -pthread $(WCSLIB) -lpthread $(LIBS) -@ $(RM) $@.o getwcstab.o : getwcstab.c getwcstab.h -@ echo '' - $(CC) $(CPPFLAGS) $(CFLAGS) $(CFITSIOINC) -c $< + $(CC) $(CPPFLAGS) $(CFITSIO_CFLAGS) $(CFITSIOINC) -c $< $(PGSBOXLIB) : -@ echo '' - $(MAKE) -C ../pgsbox lib - -tofits : test/tofits.c - $(CC) $(CPPFLAGS) $(CFLAGS) -o $@ $< - -pih.fits : test/pih.keyrec tofits - ./tofits < $< > $@ + $(MAKE) -C ../pgsbox $(notdir $@) -bth.fits : test/bth.keyrec tofits - ./tofits < $< > $@ +../utils/tofits : ../utils/tofits.c + $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ $< GNUmakefile : ../makedefs ; @@ -363,82 +469,99 @@ GNUmakefile : ../makedefs ; cd .. && ./config.status show :: - -@ -@ echo ' FLEXMODS := $(FLEXMODS)' - -@ -@ echo ' MODULES := $(MODULES)' + -@ echo ' FLEXMODS := $(FLEXMODS)' + -@ echo ' MODULES := $(MODULES)' + -@ echo ' DO_CFITSIO := $(DO_CFITSIO)' + -@ echo ' DO_PGPLOT := $(DO_PGPLOT)' + -@ echo ' TESTS := $(TESTS)' + -@ echo ' TEST_X := $(TEST_X)' # Dependencies (use the %.d pattern rule to list them) #----------------------------------------------------- $(WCSLIB)(cel.o) : cel.h prj.h sph.h wcsconfig.h wcserr.h wcsmath.h \ wcsprintf.h wcstrig.h -$(WCSLIB)(fitshdr.o) : wcsconfig.h fitshdr.h wcsutil.h -$(WCSLIB)(lin.o) : lin.h wcserr.h wcsprintf.h +$(WCSLIB)(dis.o) : dis.h wcserr.h wcsprintf.h wcsutil.h +$(WCSLIB)(fitshdr.o) : fitshdr.h wcsconfig.h wcsutil.h +$(WCSLIB)(lin.o) : dis.h lin.h wcserr.h wcsprintf.h $(WCSLIB)(log.o) : log.h $(WCSLIB)(prj.o) : prj.h wcsconfig.h wcserr.h wcsmath.h wcsprintf.h \ wcstrig.h wcsutil.h -$(WCSLIB)(spc.o) : spc.h spx.h wcserr.h wcsmath.h wcsprintf.h wcstrig.h \ - wcsutil.h +$(WCSLIB)(spc.o) : spc.h spx.h wcsconfig.h wcserr.h wcsmath.h \ + wcsprintf.h wcstrig.h wcsutil.h $(WCSLIB)(sph.o) : sph.h wcsconfig.h wcstrig.h $(WCSLIB)(spx.o) : spx.h wcserr.h wcsmath.h -$(WCSLIB)(tab.o) : tab.h wcserr.h wcsmath.h wcsprintf.h -$(WCSLIB)(wcs.o) : cel.h lin.h log.h prj.h spc.h sph.h spx.h tab.h \ - wcs.h wcsconfig.h wcserr.h wcsmath.h wcsprintf.h \ - wcstrig.h wcsunits.h wcsutil.h -$(WCSLIB)(wcsbth.o) : cel.h lin.h prj.h spc.h spx.h tab.h wcs.h wcserr.h \ - wcshdr.h wcsmath.h wcsutil.h +$(WCSLIB)(tab.o) : tab.h wcserr.h wcsmath.h wcsprintf.h wcsutil.h +$(WCSLIB)(wcs.o) : cel.h dis.h lin.h log.h prj.h spc.h sph.h spx.h \ + tab.h wcs.h wcsconfig.h wcserr.h wcsmath.h \ + wcsprintf.h wcstrig.h wcsunits.h wcsutil.h +$(WCSLIB)(wcsbth.o) : cel.h lin.h prj.h spc.h spx.h wcs.h wcshdr.h \ + wcsmath.h wcsprintf.h wcsutil.h $(WCSLIB)(wcserr.o) : wcserr.h wcsprintf.h -$(WCSLIB)(wcsfix.o) : cel.h lin.h prj.h spc.h sph.h spx.h tab.h wcs.h \ - wcserr.h wcsfix.h wcsmath.h wcsunits.h wcsutil.h -$(WCSLIB)(wcshdr.o) : cel.h lin.h prj.h spc.h spx.h tab.h wcs.h wcserr.h \ - wcshdr.h wcsmath.h wcsutil.h -$(WCSLIB)(wcspih.o) : cel.h lin.h prj.h spc.h spx.h tab.h wcs.h wcserr.h \ - wcshdr.h wcsmath.h wcsutil.h +$(WCSLIB)(wcsfix.o) : cel.h lin.h prj.h spc.h sph.h spx.h wcs.h wcserr.h \ + wcsfix.h wcsmath.h wcsunits.h wcsutil.h +$(WCSLIB)(wcshdr.o) : cel.h dis.h lin.h prj.h spc.h spx.h tab.h wcs.h \ + wcserr.h wcshdr.h wcsmath.h wcsutil.h +$(WCSLIB)(wcspih.o) : cel.h dis.h lin.h prj.h spc.h spx.h wcs.h wcshdr.h \ + wcsmath.h wcsprintf.h wcsutil.h $(WCSLIB)(wcsprintf.o): wcsprintf.h $(WCSLIB)(wcstrig.o) : wcsconfig.h wcsmath.h wcstrig.h $(WCSLIB)(wcsulex.o) : wcserr.h wcsmath.h wcsunits.h wcsutil.h $(WCSLIB)(wcsunits.o) : wcserr.h wcsunits.h -$(WCSLIB)(wcsutil.o) : wcsutil.h +$(WCSLIB)(wcsutil.o) : wcsmath.h wcsutil.h $(WCSLIB)(wcsutrn.o) : wcserr.h wcsunits.h -tbth1 tbth1_cfitsio : cel.h lin.h prj.h spc.h spx.h tab.h wcs.h wcsconfig.h \ +tbth1 tbth1_cfitsio : cel.h lin.h prj.h spc.h spx.h wcs.h wcsconfig.h \ wcsconfig_tests.h wcserr.h wcsfix.h wcshdr.h -tcel1 : cel.h prj.h wcserr.h -tcel2 : cel.h prj.h wcserr.h -tfitshdr tfitshdr_cfitsio : fitshdr.h wcsconfig.h wcsconfig_tests.h -tlin : lin.h wcserr.h +tcel1 : cel.h prj.h +tcel2 : cel.h prj.h +tfitshdr tfitshdr_cfitsio : cel.h fitshdr.h lin.h prj.h spc.h spx.h wcs.h \ + wcsconfig.h wcsconfig_tests.h wcshdr.h +tlin : lin.h +tdis1 : cel.h dis.h lin.h prj.h spc.h spx.h wcs.h wcserr.h wcshdr.h \ + wcsprintf.h +tdis2 : cel.h lin.h prj.h spc.h spx.h wcs.h wcserr.h wcshdr.h wcsprintf.h +tdisiter: cel.h dis.h lin.h prj.h spc.h spx.h wcs.h wcserr.h wcshdr.h \ + wcsprintf.h tlog : log.h -tpih1 tpih1_cfitsio : cel.h lin.h prj.h spc.h spx.h tab.h wcs.h wcsconfig.h \ +tpih1 tpih1_cfitsio : cel.h lin.h prj.h spc.h spx.h wcs.h wcsconfig.h \ wcsconfig_tests.h wcserr.h wcsfix.h wcshdr.h wcsprintf.h -tpih2 tpih2_cfitsio : cel.h lin.h prj.h spc.h spx.h tab.h wcs.h wcsconfig.h \ - wcsconfig_tests.h wcserr.h wcshdr.h -tprj1 : prj.h wcsconfig.h wcserr.h wcstrig.h -tprj2 : prj.h wcserr.h -tspc : spc.h spx.h wcsconfig.h wcserr.h wcstrig.h -tspcaips: spc.h -tspctrne: spc.h wcserr.h +tpih2 tpih2_cfitsio : cel.h lin.h prj.h spc.h spx.h wcs.h wcsconfig.h \ + wcsconfig_tests.h wcshdr.h +tprj1 : prj.h wcsconfig.h wcstrig.h +tprj2 : prj.h +tspc : spc.h spx.h wcsconfig.h wcstrig.h +tspcaips: spc.h spx.h +tspctrne: spc.h spx.h wcserr.h tsph : sph.h wcsconfig.h wcstrig.h tsphdpa : sph.h -tspx : spx.h wcserr.h -ttab1 : tab.h wcserr.h -ttab2 : tab.h wcserr.h -ttab3 : prj.h tab.h wcserr.h +tspx : spx.h +ttab1 : tab.h +ttab2 : tab.h +ttab3 : prj.h tab.h tunits : wcserr.h wcsunits.h -twcs : cel.h lin.h log.h prj.h spc.h sph.h spx.h tab.h wcs.h wcsconfig.h \ - wcsconfig_tests.h wcserr.h wcsfix.h wcshdr.h wcslib.h wcsmath.h \ - wcsprintf.h wcstrig.h wcsunits.h wcsutil.h -twcs_locale : wcs.h wcserr.h wcshdr.h wcsprintf.h -twcsfix : cel.h lin.h prj.h spc.h spx.h tab.h wcs.h wcserr.h wcsfix.h \ - wcsunits.h -twcshdr : cel.h fitshdr.h getwcstab.h lin.h log.h prj.h spc.h sph.h spx.h \ - tab.h wcs.h wcsconfig.h wcserr.h wcsfix.h wcshdr.h wcslib.h \ +twcs : cel.h dis.h fitshdr.h lin.h log.h prj.h spc.h sph.h spx.h tab.h \ + wcs.h wcsconfig.h wcsconfig_tests.h wcserr.h wcsfix.h wcshdr.h \ + wcslib.h wcsmath.h wcsprintf.h wcstrig.h wcsunits.h wcsutil.h +twcs_locale : cel.h lin.h prj.h spc.h spx.h wcs.h wcserr.h wcshdr.h \ + wcsprintf.h +twcsfix : cel.h lin.h prj.h spc.h spx.h wcs.h wcserr.h wcsfix.h \ + wcsprintf.h wcsunits.h +twcshdr : cel.h dis.h fitshdr.h getwcstab.h lin.h log.h prj.h spc.h sph.h \ + spx.h tab.h wcs.h wcsconfig.h wcserr.h wcsfix.h wcshdr.h wcslib.h \ wcsmath.h wcsprintf.h wcstrig.h wcsunits.h wcsutil.h -twcsmix : cel.h lin.h prj.h spc.h sph.h spx.h tab.h wcs.h wcserr.h -twcssub : cel.h lin.h prj.h spc.h spx.h tab.h wcs.h wcserr.h -twcstab : cel.h fitshdr.h getwcstab.h lin.h log.h prj.h spc.h sph.h spx.h \ - tab.h wcs.h wcsconfig.h wcserr.h wcsfix.h wcshdr.h wcslib.h \ +twcsmix : cel.h lin.h prj.h spc.h sph.h spx.h wcs.h +twcssub : cel.h lin.h prj.h spc.h spx.h wcs.h wcserr.h +twcstab : cel.h dis.h fitshdr.h getwcstab.h lin.h log.h prj.h spc.h sph.h \ + spx.h tab.h wcs.h wcsconfig.h wcserr.h wcsfix.h wcshdr.h wcslib.h \ wcsmath.h wcsprintf.h wcstrig.h wcsunits.h wcsutil.h +run_tdis1 : TPV3.fits TPV5.fits TPV7.fits +run_tdis2 : SIP.fits +run_tdis3 : DSS.fits SIPTPV.fits TNX.fits ZPX.fits run_tbth1 run_tbth1_cfitsio : bth.fits -run_tfitshdr run_tfitshdr_cfitsio : pih.fits +run_tfitshdr run_tfitshdr_cfitsio : fitshdr.fits run_tpih1 run_tpih1_cfitsio : pih.fits run_tpih2 run_tpih2_cfitsio : pih.fits +run_twcslint : wcslint.fits +run_twcs_locale : pih.fits diff --git a/cextern/wcslib/C/cel.c b/cextern/wcslib/C/cel.c index 4a46f8a4c146..6884f92944d9 100644 --- a/cextern/wcslib/C/cel.c +++ b/cextern/wcslib/C/cel.c @@ -1,7 +1,6 @@ /*============================================================================ - - WCSLIB 4.14 - an implementation of the FITS WCS standard. - Copyright (C) 1995-2012, Mark Calabretta + WCSLIB 8.6 - an implementation of the FITS WCS standard. + Copyright (C) 1995-2026, Mark Calabretta This file is part of WCSLIB. @@ -18,11 +17,9 @@ You should have received a copy of the GNU Lesser General Public License along with WCSLIB. If not, see http://www.gnu.org/licenses. - Direct correspondence concerning WCSLIB to mark@calabretta.id.au - Author: Mark Calabretta, Australia Telescope National Facility, CSIRO. - http://www.atnf.csiro.au/people/Mark.Calabretta - $Id: cel.c,v 4.14 2012/07/13 10:02:27 cal103 Exp $ + http://www.atnf.csiro.au/computing/software/wcs + $Id: cel.c,v 8.6 2026/03/29 13:53:56 mcalabre Exp $ *===========================================================================*/ #include @@ -36,9 +33,7 @@ #include "sph.h" #include "cel.h" -const int CELSET = 137; - -/* Map status return value to message. */ +// Map status return value to message. const char *cel_errmsg[] = { "Success", "Null celprm pointer passed", @@ -48,22 +43,27 @@ const char *cel_errmsg[] = { "One or more of the (x,y) coordinates were invalid", "One or more of the (lng,lat) coordinates were invalid"}; -/* Convenience macro for invoking wcserr_set(). */ -#define CEL_ERRMSG(status) WCSERR_SET(status), cel_errmsg[status] +// Map error returns for lower-level routines. +const int cel_prjerr[] = { + CELERR_SUCCESS, // 0: PRJERR_SUCCESS + CELERR_NULL_POINTER, // 1: PRJERR_NULL_POINTER + CELERR_BAD_PARAM, // 2: PRJERR_BAD_PARAM + CELERR_BAD_PIX, // 3: PRJERR_BAD_PIX + CELERR_BAD_WORLD // 4: PRJERR_BAD_WORLD +}; -/*--------------------------------------------------------------------------*/ +static const int CELSET = 137; -int celini(cel) +// Convenience macro for invoking wcserr_set(). +#define CEL_ERRMSG(status) WCSERR_SET(status), cel_errmsg[status] -struct celprm *cel; +//---------------------------------------------------------------------------- -{ - register int k; +int celini(struct celprm *cel) +{ if (cel == 0x0) return CELERR_NULL_POINTER; - cel->flag = 0; - cel->offset = 0; cel->phi0 = UNDEFINED; cel->theta0 = UNDEFINED; @@ -72,46 +72,90 @@ struct celprm *cel; cel->ref[2] = UNDEFINED; cel->ref[3] = +90.0; - for (k = 0; k < 5; cel->euler[k++] = 0.0); + for (int k = 0; k < 5; cel->euler[k++] = 0.0); cel->latpreq = -1; + cel->isolat = 0; cel->err = 0x0; - return prjini(&(cel->prj)); -} + cel->flag = 0; -/*--------------------------------------------------------------------------*/ + return cel_prjerr[prjini(&(cel->prj))]; +} -int celfree(cel) +//---------------------------------------------------------------------------- -struct celprm *cel; +int celfree(struct celprm *cel) { if (cel == 0x0) return CELERR_NULL_POINTER; - if (cel->err) { - free(cel->err); - cel->err = 0x0; + wcserr_clear(&(cel->err)); + + return cel_prjerr[prjfree(&(cel->prj))]; +} + +//---------------------------------------------------------------------------- + +int celsize(const struct celprm *cel, int sizes[2]) + +{ + if (cel == 0x0) { + sizes[0] = sizes[1] = 0; + return 0; } - prjfree(&(cel->prj)); + // Base size, in bytes. + sizes[0] = sizeof(struct celprm); + + // Total size of allocated memory, in bytes. + sizes[1] = 0; + + // celprm::prj. + int exsizes[2]; + prjsize(&(cel->prj), exsizes); + sizes[1] += exsizes[1]; + + // celprm::err. + wcserr_size(cel->err, exsizes); + sizes[1] += exsizes[0] + exsizes[1]; return 0; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- -int celprt(cel) - -const struct celprm *cel; +int celenq(const struct celprm *cel, int enquiry) { - int i; + // Initialize. + if (cel == 0x0) return CELERR_NULL_POINTER; + + int answer = 0; + + if (enquiry & CELENQ_SET) { + if (abs(cel->flag) != CELSET) return 0; + answer = 1; + } + + if (enquiry & CELENQ_BYP) { + if (cel->flag != 1 && cel->flag != -CELSET) return 0; + answer = 1; + } + return answer; +} + +//---------------------------------------------------------------------------- + +int celprt(const struct celprm *cel) + +{ if (cel == 0x0) return CELERR_NULL_POINTER; - wcsprintf(" flag: %d\n", cel->flag); - wcsprintf(" offset: %d\n", cel->offset); + // Parameters supplied. + wcsprintf(" flag: %d\n", cel->flag); + wcsprintf(" offset: %d\n", cel->offset); if (undefined(cel->phi0)) { wcsprintf(" phi0: UNDEFINED\n"); } else { @@ -122,16 +166,17 @@ const struct celprm *cel; } else { wcsprintf(" theta0: %9f\n", cel->theta0); } - wcsprintf(" ref:"); - for (i = 0; i < 4; i++) { - wcsprintf(" %- 11.5g", cel->ref[i]); + wcsprintf(" ref:"); + for (int i = 0; i < 4; i++) { + wcsprintf(" %#- 11.5g", cel->ref[i]); } wcsprintf("\n"); - wcsprintf(" prj: (see below)\n"); + wcsprintf(" prj: (see below)\n"); - wcsprintf(" euler:"); - for (i = 0; i < 5; i++) { - wcsprintf(" %- 11.5g", cel->euler[i]); + // Derived values. + wcsprintf(" euler:"); + for (int i = 0; i < 5; i++) { + wcsprintf(" %#- 11.5g", cel->euler[i]); } wcsprintf("\n"); wcsprintf(" latpreq: %d", cel->latpreq); @@ -146,11 +191,13 @@ const struct celprm *cel; } wcsprintf(" isolat: %d\n", cel->isolat); + // Error handling. WCSPRINTF_PTR(" err: ", cel->err, "\n"); if (cel->err) { wcserr_prt(cel->err, " "); } + // Projection parameters (from above). wcsprintf("\n"); wcsprintf(" prj.*\n"); prjprt(&(cel->prj)); @@ -158,41 +205,51 @@ const struct celprm *cel; return 0; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- -int celset(cel) +int celperr(const struct celprm *cel, const char *prefix) -struct celprm *cel; +{ + if (cel == 0x0) return CELERR_NULL_POINTER; + + if (cel->err && wcserr_prt(cel->err, prefix) == 0) { + wcserr_prt(cel->prj.err, prefix); + } + + return 0; +} + +//---------------------------------------------------------------------------- + +int celset(struct celprm *cel) { static const char *function = "celset"; const double tol = 1.0e-10; - double clat0, cphip, cthe0, lat0, lng0, phip, slat0, slz, sphip, sthe0; - double latp, latp1, latp2, lngp; - double u, v, x, y, z; - struct prjprm *celprj; - struct wcserr **err; if (cel == 0x0) return CELERR_NULL_POINTER; - err = &(cel->err); + if (cel->flag == -CELSET) return 0; + struct wcserr **err = &(cel->err); - /* Initialize the projection driver routines. */ - celprj = &(cel->prj); + // Initialize the projection driver routines. + struct prjprm *celprj = &(cel->prj); if (cel->offset) { celprj->phi0 = cel->phi0; celprj->theta0 = cel->theta0; } else { - /* Ensure that these are undefined - no fiducial offset. */ + // Ensure that these are undefined - no fiducial offset. celprj->phi0 = UNDEFINED; celprj->theta0 = UNDEFINED; } - if (prjset(celprj)) { - return wcserr_set(CEL_ERRMSG(CELERR_BAD_PARAM)); + celprj->flag = 0; + int status; + if ((status = prjset(celprj))) { + return wcserr_set(CEL_ERRMSG(cel_prjerr[status])); } - /* Defaults set by the projection routines. */ + // Defaults set by the projection routines. if (undefined(cel->phi0)) { cel->phi0 = celprj->phi0; } @@ -214,12 +271,13 @@ struct celprm *cel; } - lng0 = cel->ref[0]; - lat0 = cel->ref[1]; - phip = cel->ref[2]; - latp = cel->ref[3]; + double lng0 = cel->ref[0]; + double lat0 = cel->ref[1]; + double phip = cel->ref[2]; + double lngp; + double latp = cel->ref[3]; - /* Set default for native longitude of the celestial pole? */ + // Set default for native longitude of the celestial pole? if (undefined(phip) || phip == 999.0) { phip = (lat0 < cel->theta0) ? 180.0 : 0.0; phip += cel->phi0; @@ -234,18 +292,20 @@ struct celprm *cel; } - /* Compute celestial coordinates of the native pole. */ + // Compute celestial coordinates of the native pole. cel->latpreq = 0; if (cel->theta0 == 90.0) { - /* Fiducial point at the native pole. */ + // Fiducial point at the native pole. lngp = lng0; latp = lat0; } else { - /* Fiducial point away from the native pole. */ + // Fiducial point away from the native pole. + double slat0, clat0, sthe0, cthe0; sincosd(lat0, &slat0, &clat0); sincosd(cel->theta0, &sthe0, &cthe0); + double cphip, sphip, u, v; if (phip == cel->phi0) { sphip = 0.0; cphip = 1.0; @@ -256,9 +316,9 @@ struct celprm *cel; } else { sincosd(phip - cel->phi0, &sphip, &cphip); - x = cthe0*cphip; - y = sthe0; - z = sqrt(x*x + y*y); + double x = cthe0*cphip; + double y = sthe0; + double z = sqrt(x*x + y*y); if (z == 0.0) { if (slat0 != 0.0) { return wcserr_set(WCSERR_SET(CELERR_BAD_COORD_TRANS), @@ -266,7 +326,7 @@ struct celprm *cel; "lat0 == 0 is required for |phip - phi0| = 90 and theta0 == 0"); } - /* latp determined solely by LATPOLEa in this case. */ + // latp determined solely by LATPOLEa in this case. cel->latpreq = 2; if (latp > 90.0) { latp = 90.0; @@ -274,8 +334,11 @@ struct celprm *cel; latp = -90.0; } + // Avert a spurious compiler warning. + u = v = 0.0; + } else { - slz = slat0/z; + double slz = slat0/z; if (fabs(slz) > 1.0) { if ((fabs(slz) - 1.0) < tol) { if (slz > 0.0) { @@ -296,14 +359,14 @@ struct celprm *cel; } if (cel->latpreq == 0) { - latp1 = u + v; + double latp1 = u + v; if (latp1 > 180.0) { latp1 -= 360.0; } else if (latp1 < -180.0) { latp1 += 360.0; } - latp2 = u - v; + double latp2 = u - v; if (latp2 > 180.0) { latp2 -= 360.0; } else if (latp2 < -180.0) { @@ -312,7 +375,7 @@ struct celprm *cel; if (fabs(latp1) < 90.0+tol && fabs(latp2) < 90.0+tol) { - /* There are two valid solutions for latp. */ + // There are two valid solutions for latp. cel->latpreq = 1; } @@ -330,7 +393,7 @@ struct celprm *cel; } } - /* Account for rounding error. */ + // Account for rounding error. if (fabs(latp) < 90.0+tol) { if (latp > 90.0) { latp = 90.0; @@ -340,34 +403,34 @@ struct celprm *cel; } } - z = cosd(latp)*clat0; + double z = cosd(latp)*clat0; if (fabs(z) < tol) { if (fabs(clat0) < tol) { - /* Celestial pole at the fiducial point. */ + // Celestial pole at the fiducial point. lngp = lng0; } else if (latp > 0.0) { - /* Celestial north pole at the native pole.*/ + // Celestial north pole at the native pole. lngp = lng0 + phip - cel->phi0 - 180.0; } else { - /* Celestial south pole at the native pole. */ + // Celestial south pole at the native pole. lngp = lng0 - phip + cel->phi0; } } else { - x = (sthe0 - sind(latp)*slat0)/z; - y = sphip*cthe0/clat0; + double x = (sthe0 - sind(latp)*slat0)/z; + double y = sphip*cthe0/clat0; if (x == 0.0 && y == 0.0) { - /* Sanity check (shouldn't be possible). */ + // Sanity check (shouldn't be possible). return wcserr_set(WCSERR_SET(CELERR_BAD_COORD_TRANS), "Invalid coordinate transformation parameters, internal error"); } lngp = lng0 - atan2d(y,x); } - /* Make celestial longitude of the native pole the same sign as at the - fiducial point. */ + // Make celestial longitude of the native pole the same sign as at the + // fiducial point. if (lng0 >= 0.0) { if (lngp < 0.0) { lngp += 360.0; @@ -383,105 +446,111 @@ struct celprm *cel; } } - /* Reset LATPOLEa. */ + // Reset LATPOLEa. cel->ref[3] = latp; - /* Set the Euler angles. */ + // Set the Euler angles. cel->euler[0] = lngp; cel->euler[1] = 90.0 - latp; cel->euler[2] = phip; sincosd(cel->euler[1], &cel->euler[4], &cel->euler[3]); cel->isolat = (cel->euler[4] == 0.0); - cel->flag = CELSET; - /* Check for ill-conditioned parameters. */ + // Check for ill-conditioned parameters. if (fabs(latp) > 90.0+tol) { return wcserr_set(WCSERR_SET(CELERR_ILL_COORD_TRANS), "Ill-conditioned coordinate transformation parameters\nNo valid " "solution for latp for these values of phip, phi0, and theta0"); } + cel->flag = (cel->flag == 1) ? -CELSET : CELSET; + return 0; } -/*--------------------------------------------------------------------------*/ - -int celx2s(cel, nx, ny, sxy, sll, x, y, phi, theta, lng, lat, stat) - -struct celprm *cel; -int nx, ny, sxy, sll; -const double x[], y[]; -double phi[], theta[]; -double lng[], lat[]; -int stat[]; +//---------------------------------------------------------------------------- + +int celx2s( + struct celprm *cel, + int nx, + int ny, + int sxy, + int sll, + const double x[], + const double y[], + double phi[], + double theta[], + double lng[], + double lat[], + int stat[]) { static const char *function = "celx2s"; - int nphi, status; - struct prjprm *celprj; - struct wcserr **err; - - /* Initialize. */ + // Initialize. if (cel == 0x0) return CELERR_NULL_POINTER; - err = &(cel->err); + struct wcserr **err = &(cel->err); - if (cel->flag != CELSET) { + int status = 0; + if (abs(cel->flag) != CELSET) { if ((status = celset(cel))) return status; } - /* Apply spherical deprojection. */ - celprj = &(cel->prj); - if ((status = celprj->prjx2s(celprj, nx, ny, sxy, 1, x, y, phi, theta, + // Apply spherical deprojection. + int istat; + struct prjprm *celprj = &(cel->prj); + if ((istat = celprj->prjx2s(celprj, nx, ny, sxy, 1, x, y, phi, theta, stat))) { - if (status == PRJERR_BAD_PIX) { - status = CELERR_BAD_PIX; + if (istat) { + status = wcserr_set(CEL_ERRMSG(cel_prjerr[istat])); + if (status != CELERR_BAD_PIX) { + return status; + } } - - wcserr_set(CEL_ERRMSG(status)); - - if (status != CELERR_BAD_PIX) return status; } - nphi = (ny > 0) ? (nx*ny) : nx; + int nphi = (ny > 0) ? (nx*ny) : nx; - /* Compute celestial coordinates. */ + // Compute celestial coordinates. sphx2s(cel->euler, nphi, 0, 1, sll, phi, theta, lng, lat); return status; } -/*--------------------------------------------------------------------------*/ - -int cels2x(cel, nlng, nlat, sll, sxy, lng, lat, phi, theta, x, y, stat) - -struct celprm *cel; -int nlng, nlat, sll, sxy; -const double lng[], lat[]; -double phi[], theta[]; -double x[], y[]; -int stat[]; +//---------------------------------------------------------------------------- + +int cels2x( + struct celprm *cel, + int nlng, + int nlat, + int sll, + int sxy, + const double lng[], + const double lat[], + double phi[], + double theta[], + double x[], + double y[], + int stat[]) { static const char *function = "cels2x"; - int nphi, ntheta, status; - struct prjprm *celprj; - struct wcserr **err; - - /* Initialize. */ + // Initialize. if (cel == 0x0) return CELERR_NULL_POINTER; - err = &(cel->err); + struct wcserr **err = &(cel->err); - if (cel->flag != CELSET) { + int status = 0; + if (abs(cel->flag) != CELSET) { if ((status = celset(cel))) return status; } - /* Compute native coordinates. */ + // Compute native coordinates. sphs2x(cel->euler, nlng, nlat, sll, 1, lng, lat, phi, theta); + int nphi, ntheta; if (cel->isolat) { - /* Constant celestial latitude -> constant native latitude. */ + // Constant celestial latitude -> constant native latitude. nphi = nlng; ntheta = nlat; } else { @@ -489,16 +558,18 @@ int stat[]; ntheta = 0; } - /* Apply the spherical projection. */ - celprj = &(cel->prj); - if ((status = celprj->prjs2x(celprj, nphi, ntheta, 1, sxy, phi, theta, x, y, + // Apply the spherical projection. + int istat; + struct prjprm *celprj = &(cel->prj); + if ((istat = celprj->prjs2x(celprj, nphi, ntheta, 1, sxy, phi, theta, x, y, stat))) { - if (status != PRJERR_BAD_PARAM) { - status = CELERR_BAD_WORLD; + if (istat) { + status = wcserr_set(CEL_ERRMSG(cel_prjerr[istat])); + if (status != CELERR_BAD_WORLD) { + return status; + } } - - return wcserr_set(CEL_ERRMSG(status)); } - return 0; + return status; } diff --git a/cextern/wcslib/C/cel.h b/cextern/wcslib/C/cel.h index 6a583a6b87e3..ef68ce78d19c 100644 --- a/cextern/wcslib/C/cel.h +++ b/cextern/wcslib/C/cel.h @@ -1,7 +1,6 @@ /*============================================================================ - - WCSLIB 4.14 - an implementation of the FITS WCS standard. - Copyright (C) 1995-2012, Mark Calabretta + WCSLIB 8.6 - an implementation of the FITS WCS standard. + Copyright (C) 1995-2026, Mark Calabretta This file is part of WCSLIB. @@ -18,40 +17,43 @@ You should have received a copy of the GNU Lesser General Public License along with WCSLIB. If not, see http://www.gnu.org/licenses. - Direct correspondence concerning WCSLIB to mark@calabretta.id.au - Author: Mark Calabretta, Australia Telescope National Facility, CSIRO. - http://www.atnf.csiro.au/people/Mark.Calabretta - $Id: cel.h,v 4.14 2012/07/13 10:02:27 cal103 Exp $ + http://www.atnf.csiro.au/computing/software/wcs + $Id: cel.h,v 8.6 2026/03/29 13:53:56 mcalabre Exp $ *============================================================================= * -* WCSLIB 4.14 - C routines that implement the FITS World Coordinate System -* (WCS) standard. Refer to -* -* "Representations of world coordinates in FITS", -* Greisen, E.W., & Calabretta, M.R. 2002, A&A, 395, 1061 (Paper I) -* -* "Representations of celestial coordinates in FITS", -* Calabretta, M.R., & Greisen, E.W. 2002, A&A, 395, 1077 (Paper II) -* -* Refer to the README file provided with WCSLIB for an overview of the -* library. +* WCSLIB 8.6 - C routines that implement the FITS World Coordinate System +* (WCS) standard. Refer to the README file provided with WCSLIB for an +* overview of the library. * * * Summary of the cel routines * --------------------------- -* These routines implement the part of the FITS World Coordinate System (WCS) -* standard that deals with celestial coordinates. They define methods to be -* used for computing celestial world coordinates from intermediate world -* coordinates (a linear transformation of image pixel coordinates), and vice -* versa. They are based on the celprm struct which contains all information -* needed for the computations. This struct contains some elements that must -* be set by the user, and others that are maintained by these routines, -* somewhat like a C++ class but with no encapsulation. +* Routines in this suite implement the part of the FITS World Coordinate +* System (WCS) standard that deals with celestial coordinates, as described in +* += "Representations of world coordinates in FITS", += Greisen, E.W., & Calabretta, M.R. 2002, A&A, 395, 1061 (WCS Paper I) += += "Representations of celestial coordinates in FITS", += Calabretta, M.R., & Greisen, E.W. 2002, A&A, 395, 1077 (WCS Paper II) +* +* These routines define methods to be used for computing celestial world +* coordinates from intermediate world coordinates (a linear transformation +* of image pixel coordinates), and vice versa. They are based on the celprm +* struct which contains all information needed for the computations. This +* struct contains some elements that must be set by the user, and others that +* are maintained by these routines, somewhat like a C++ class but with no +* encapsulation. * * Routine celini() is provided to initialize the celprm struct with default * values, celfree() reclaims any memory that may have been allocated to store -* an error message, and celprt() prints its contents. +* an error message, celsize() computes its total size including allocated +* memory, celenq() returns information about the state of the struct, and +* celprt() prints its contents. +* +* celperr() prints the error message(s), if any, stored in a celprm struct and +* the prjprm struct that it contains. * * A setup routine, celset(), computes intermediate values in the celprm struct * from parameters in it that were supplied by the user. The struct always @@ -69,6 +71,11 @@ * celini() sets all members of a celprm struct to default values. It should * be used to initialize every celprm struct. * +* PLEASE NOTE: If the celprm struct has already been initialized, then before +* reinitializing, it celfree() should be used to free any memory that may have +* been allocated to store an error message. A memory leak may otherwise +* result. +* * Returned: * cel struct celprm* * Celestial transformation parameters. @@ -84,7 +91,7 @@ * celfree() frees any memory that may have been allocated to store an error * message in the celprm struct. * -* Given: +* Given and returned: * cel struct celprm* * Celestial transformation parameters. * @@ -94,6 +101,53 @@ * 1: Null celprm pointer passed. * * +* celsize() - Compute the size of a celprm struct +* ----------------------------------------------- +* celsize() computes the full size of a celprm struct, including allocated +* memory. +* +* Given: +* cel const struct celprm* +* Celestial transformation parameters. +* +* If NULL, the base size of the struct and the allocated +* size are both set to zero. +* +* Returned: +* sizes int[2] The first element is the base size of the struct as +* returned by sizeof(struct celprm). The second element +* is the total allocated size, in bytes. This figure +* includes memory allocated for the constituent struct, +* celprm::err. +* +* It is not an error for the struct not to have been set +* up via celset(). +* +* Function return value: +* int Status return value: +* 0: Success. +* +* +* celenq() - enquire about the state of a celprm struct +* ----------------------------------------------------- +* celenq() may be used to obtain information about the state of a celprm +* struct. The function returns a true/false answer for the enquiry asked. +* +* Given: +* cel const struct celprm* +* Celestial transformation parameters. +* +* enquiry int Enquiry according to the following parameters: +* CELENQ_SET: the struct has been set up by celset(). +* CELENQ_BYP: the struct is in bypass mode (see +* celset()). +* +* Function return value: +* int Enquiry result: +* 0: No. +* 1: Yes. +* +* * celprt() - Print routine for the celprm struct * ---------------------------------------------- * celprt() prints the contents of a celprm struct using wcsprintf(). Mainly @@ -109,6 +163,26 @@ * 1: Null celprm pointer passed. * * +* celperr() - Print error messages from a celprm struct +* ----------------------------------------------------- +* celperr() prints the error message(s), if any, stored in a celprm struct and +* the prjprm struct that it contains. If there are no errors then nothing is +* printed. It uses wcserr_prt(), q.v. +* +* Given: +* cel const struct celprm* +* Coordinate transformation parameters. +* +* prefix const char * +* If non-NULL, each output line will be prefixed with +* this string. +* +* Function return value: +* int Status return value: +* 0: Success. +* 1: Null celprm pointer passed. +* +* * celset() - Setup routine for the celprm struct * ---------------------------------------------- * celset() sets up a celprm struct according to information supplied within @@ -117,6 +191,13 @@ * Note that this routine need not be called directly; it will be invoked by * celx2s() and cels2x() if celprm::flag is anything other than a predefined * magic value. + +* celset() normally operates regardless of the value of celprm::flag; i.e. +* even if a struct was previously set up it will be reset unconditionally. +* However, a celprm struct may be put into "bypass" mode by invoking celset() +* initially with celprm::flag == 1 (rather than 0). celset() will return +* immediately if invoked on a struct in that state. To take a struct out of +* bypass mode, simply reset celprm::flag to zero. See also celenq(). * * Given and returned: * cel struct celprm* @@ -232,8 +313,8 @@ * Returned celprm struct members must not be modified by the user. * * int flag -* (Given and returned) This flag must be set to zero whenever any of the -* following celprm struct members are set or changed: +* (Given and returned) This flag must be set to zero (or 1, see celset()) +* whenever any of the following celprm struct members are set or changed: * * - celprm::offset, * - celprm::phi0, @@ -329,12 +410,13 @@ * computation. * * struct wcserr *err -* (Returned) If enabled, when an error status is returned this struct +* (Returned) If enabled, when an error status is returned, this struct * contains detailed information about the error, see wcserr_enable(). * * void *padding * (An unused variable inserted for alignment purposes only.) * +* * Global variable: const char *cel_errmsg[] - Status return messages * ------------------------------------------------------------------ * Status messages to match the status value returned from each function. @@ -345,60 +427,64 @@ #define WCSLIB_CEL #include "prj.h" -#include "wcserr.h" #ifdef __cplusplus extern "C" { #endif +enum celenq_enum { + CELENQ_SET = 2, // celprm struct has been set up. + CELENQ_BYP = 4, // celprm struct is in bypass mode. +}; extern const char *cel_errmsg[]; enum cel_errmsg_enum { - CELERR_SUCCESS = 0, /* Success. */ - CELERR_NULL_POINTER = 1, /* Null celprm pointer passed. */ - CELERR_BAD_PARAM = 2, /* Invalid projection parameters. */ - CELERR_BAD_COORD_TRANS = 3, /* Invalid coordinate transformation - parameters. */ - CELERR_ILL_COORD_TRANS = 4, /* Ill-conditioned coordinated transformation - parameters. */ - CELERR_BAD_PIX = 5, /* One or more of the (x,y) coordinates were - invalid. */ - CELERR_BAD_WORLD = 6 /* One or more of the (lng,lat) coordinates - were invalid. */ + CELERR_SUCCESS = 0, // Success. + CELERR_NULL_POINTER = 1, // Null celprm pointer passed. + CELERR_BAD_PARAM = 2, // Invalid projection parameters. + CELERR_BAD_COORD_TRANS = 3, // Invalid coordinate transformation + // parameters. + CELERR_ILL_COORD_TRANS = 4, // Ill-conditioned coordinated transformation + // parameters. + CELERR_BAD_PIX = 5, // One or more of the (x,y) coordinates were + // invalid. + CELERR_BAD_WORLD = 6 // One or more of the (lng,lat) coordinates + // were invalid. }; struct celprm { - /* Initialization flag (see the prologue above). */ - /*------------------------------------------------------------------------*/ - int flag; /* Set to zero to force initialization. */ - - /* Parameters to be provided (see the prologue above). */ - /*------------------------------------------------------------------------*/ - int offset; /* Force (x,y) = (0,0) at (phi_0,theta_0). */ - double phi0, theta0; /* Native coordinates of fiducial point. */ - double ref[4]; /* Celestial coordinates of fiducial */ - /* point and native coordinates of */ - /* celestial pole. */ - - struct prjprm prj; /* Projection parameters (see prj.h). */ - - /* Information derived from the parameters supplied. */ - /*------------------------------------------------------------------------*/ - double euler[5]; /* Euler angles and functions thereof. */ - int latpreq; /* LATPOLEa requirement. */ - int isolat; /* True if |latitude| is preserved. */ - - /* Error handling */ - /*------------------------------------------------------------------------*/ - struct wcserr *err; - - /* Private */ - /*------------------------------------------------------------------------*/ - void *padding; /* (Dummy inserted for alignment purposes.) */ + // Initialization flag (see the prologue above). + //-------------------------------------------------------------------------- + int flag; // Set to zero to force initialization. + + // Parameters to be provided (see the prologue above). + //-------------------------------------------------------------------------- + int offset; // Force (x,y) = (0,0) at (phi_0,theta_0). + double phi0, theta0; // Native coordinates of fiducial point. + double ref[4]; // Celestial coordinates of fiducial + // point and native coordinates of + // celestial pole. + + struct prjprm prj; // Projection parameters (see prj.h). + + // Information derived from the parameters supplied. + //-------------------------------------------------------------------------- + double euler[5]; // Euler angles and functions thereof. + int latpreq; // LATPOLEa requirement. + int isolat; // True if |latitude| is preserved. + + // Error messaging, if enabled. + //-------------------------------------------------------------------------- + struct wcserr *err; // Error handling, if enabled. + + //-------------------------------------------------------------------------- + // Private - the remainder are for internal use. + //-------------------------------------------------------------------------- + void *padding; // (Dummy inserted for alignment purposes.) }; -/* Size of the celprm struct in int units, used by the Fortran wrappers. */ +// Size of the celprm struct in int units, used by the Fortran wrappers. #define CELLEN (sizeof(struct celprm)/sizeof(int)) @@ -406,8 +492,14 @@ int celini(struct celprm *cel); int celfree(struct celprm *cel); +int celsize(const struct celprm *cel, int sizes[2]); + +int celenq(const struct celprm *cel, int enquiry); + int celprt(const struct celprm *cel); +int celperr(const struct celprm *cel, const char *prefix); + int celset(struct celprm *cel); int celx2s(struct celprm *cel, int nx, int ny, int sxy, int sll, @@ -421,7 +513,7 @@ int cels2x(struct celprm *cel, int nlng, int nlat, int sll, int sxy, int stat[]); -/* Deprecated. */ +// Deprecated. #define celini_errmsg cel_errmsg #define celprt_errmsg cel_errmsg #define celset_errmsg cel_errmsg @@ -432,4 +524,4 @@ int cels2x(struct celprm *cel, int nlng, int nlat, int sll, int sxy, } #endif -#endif /* WCSLIB_CEL */ +#endif // WCSLIB_CEL diff --git a/cextern/wcslib/C/dis.c b/cextern/wcslib/C/dis.c new file mode 100644 index 000000000000..cd665aaaa651 --- /dev/null +++ b/cextern/wcslib/C/dis.c @@ -0,0 +1,3725 @@ +/*============================================================================ + WCSLIB 8.6 - an implementation of the FITS WCS standard. + Copyright (C) 1995-2026, Mark Calabretta + + This file is part of WCSLIB. + + WCSLIB is free software: you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + WCSLIB is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for + more details. + + You should have received a copy of the GNU Lesser General Public License + along with WCSLIB. If not, see http://www.gnu.org/licenses. + + Author: Mark Calabretta, Australia Telescope National Facility, CSIRO. + http://www.atnf.csiro.au/computing/software/wcs + $Id: dis.c,v 8.6 2026/03/29 13:53:56 mcalabre Exp $ +*===========================================================================*/ + +#include +#include +#include +#include + +#include "wcserr.h" +#include "wcsprintf.h" +#include "wcsutil.h" +#include "dis.h" + +// Maximum number of DPja or DQia keywords. +int NDPMAX = 256; + +// Map status return value to message. +const char *dis_errmsg[] = { + "Success", + "Null disprm pointer passed", + "Memory allocation failed", + "Invalid parameter value", + "Distort error", + "De-distort error"}; + +static const int DISSET = 137; + +static const int DIS_TPD = 1; +static const int DIS_POLYNOMIAL = 2; +static const int DIS_DOTPD = 1024; + +// Internal helper functions, not for general use. +static int polyset(int j, struct disprm *dis); +static int tpdset(int j, struct disprm *dis); + +static int pol2tpd(int j, struct disprm *dis); +static int tpvset(int j, struct disprm *dis); +static int sipset(int j, struct disprm *dis); +static int dssset(int j, struct disprm *dis); +static int watset(int j, struct disprm *dis); +static int cheleg(int type, int m, int n, double coeffm[], double coeffn[]); + +static int dispoly(DISP2X_ARGS); +static int tpd1(DISP2X_ARGS); +static int tpd2(DISP2X_ARGS); +static int tpd3(DISP2X_ARGS); +static int tpd4(DISP2X_ARGS); +static int tpd5(DISP2X_ARGS); +static int tpd6(DISP2X_ARGS); +static int tpd7(DISP2X_ARGS); +static int tpd8(DISP2X_ARGS); +static int tpd9(DISP2X_ARGS); + +// The first three iparm indices have meanings common to all distortion +// functions. They are used by disp2x(), disx2p(), disprt(), and dishdo(). +#define I_DTYPE 0 // Distortion type code. +#define I_NIPARM 1 // Full (allocated) length of iparm[]. +#define I_NDPARM 2 // No. of parameters in dparm[], excl. work space. + +// Convenience macro for invoking wcserr_set(). +#define DIS_ERRMSG(status) WCSERR_SET(status), dis_errmsg[status] + +//---------------------------------------------------------------------------- + +int disndp(int ndpmax) { if (ndpmax >= 0) NDPMAX = ndpmax; return NDPMAX; } + +//---------------------------------------------------------------------------- + +int dpfill( + struct dpkey *dp, + const char *keyword, + const char *field, + int j, + int type, + int i, + double f) + +{ + if (keyword) { + if (field) { + if (j && 2 <= strlen(keyword)) { + // Fill in the axis number from the value given. + if (keyword[2] == '\0') { + sprintf(dp->field, "%s%d.%s", keyword, j, field); + } else { + // Take care not to overwrite any alternate code. + char axno[8]; + sprintf(dp->field, "%s.%s", keyword, field); + sprintf(axno, "%d", j); + dp->field[2] = axno[0]; + } + + } else { + sprintf(dp->field, "%s.%s", keyword, field); + } + } else { + strcpy(dp->field, keyword); + } + } else if (field) { + strcpy(dp->field, field); + } + + if (j) { + dp->j = j; + } else { + // The field name must either be given or preset. + char *cp; + if ((cp = strpbrk(dp->field, "0123456789")) != 0x0) { + sscanf(cp, "%d.", &(dp->j)); + } + } + + if ((dp->type = type)) { + dp->value.f = f; + } else { + dp->value.i = i; + } + + return 0; +} + +//---------------------------------------------------------------------------- + +int dpkeyi(const struct dpkey *dp) + +{ + if (dp->type != 0) { + return (int)dp->value.f; + } + + return dp->value.i; +} + +//---------------------------------------------------------------------------- + +double dpkeyd(const struct dpkey *dp) + +{ + if (dp->type == 0) { + return (double)dp->value.i; + } + + return dp->value.f; +} + +//---------------------------------------------------------------------------- + +int disini(int alloc, int naxis, struct disprm *dis) + +{ + return disinit(alloc, naxis, dis, -1); +} + +//---------------------------------------------------------------------------- + +int disinit(int alloc, int naxis, struct disprm *dis, int ndpmax) + +{ + static const char *function = "disinit"; + + // Check inputs. + if (dis == 0x0) return DISERR_NULL_POINTER; + + if (ndpmax < 0) ndpmax = disndp(-1); + + + // Initialize error message handling. + if (dis->flag == -1) { + dis->err = 0x0; + } + struct wcserr **err = &(dis->err); + wcserr_clear(err); + + + // Initialize pointers. + if (dis->flag == -1 || dis->m_flag != DISSET) { + if (dis->flag == -1) { + dis->docorr = 0x0; + dis->Nhat = 0x0; + + dis->axmap = 0x0; + dis->offset = 0x0; + dis->scale = 0x0; + dis->iparm = 0x0; + dis->dparm = 0x0; + + dis->disp2x = 0x0; + dis->disx2p = 0x0; + + dis->i_naxis = 0; + } + + // Initialize memory management. + dis->m_flag = 0; + dis->m_naxis = 0; + dis->m_dtype = 0x0; + dis->m_dp = 0x0; + dis->m_maxdis = 0x0; + } + + if (naxis < 0) { + return wcserr_set(WCSERR_SET(DISERR_MEMORY), + "naxis must not be negative (got %d)", naxis); + } + + + // Allocate memory for arrays if required. + if (alloc || + dis->dtype == 0x0 || + (ndpmax && dis->dp == 0x0) || + dis->maxdis == 0x0) { + + // Was sufficient allocated previously? + if (dis->m_flag == DISSET && + (dis->m_naxis < naxis || + dis->ndpmax < ndpmax)) { + // No, free it. + disfree(dis); + } + + if (alloc || dis->dtype == 0x0) { + if (dis->m_dtype) { + // In case the caller fiddled with it. + dis->dtype = dis->m_dtype; + + } else { + if ((dis->dtype = calloc(naxis, sizeof(char [72]))) == 0x0) { + disfree(dis); + return wcserr_set(DIS_ERRMSG(DISERR_MEMORY)); + } + + dis->m_flag = DISSET; + dis->m_naxis = naxis; + dis->m_dtype = dis->dtype; + } + } + + if (alloc || dis->dp == 0x0) { + if (dis->m_dp) { + // In case the caller fiddled with it. + dis->dp = dis->m_dp; + + } else { + if (ndpmax) { + if ((dis->dp = calloc(ndpmax, sizeof(struct dpkey))) == 0x0) { + disfree(dis); + return wcserr_set(DIS_ERRMSG(DISERR_MEMORY)); + } + } else { + dis->dp = 0x0; + } + + dis->ndpmax = ndpmax; + + dis->m_flag = DISSET; + dis->m_naxis = naxis; + dis->m_dp = dis->dp; + } + } + + if (alloc || dis->maxdis == 0x0) { + if (dis->m_maxdis) { + // In case the caller fiddled with it. + dis->maxdis = dis->m_maxdis; + + } else { + if ((dis->maxdis = calloc(naxis, sizeof(double))) == 0x0) { + disfree(dis); + return wcserr_set(DIS_ERRMSG(DISERR_MEMORY)); + } + + dis->m_flag = DISSET; + dis->m_naxis = naxis; + dis->m_maxdis = dis->maxdis; + } + } + } + + + // Set defaults. + dis->naxis = naxis; + + if (naxis) { + memset(dis->dtype, 0, naxis*sizeof(char [72])); + } + + dis->ndp = 0; + if (ndpmax) { + memset(dis->dp, 0, ndpmax*sizeof(struct dpkey)); + } + + dis->totdis = 0.0; + if (naxis) { + memset(dis->maxdis, 0, naxis*sizeof(double)); + } + + dis->flag = 0; + + return 0; +} + + +//---------------------------------------------------------------------------- + +int discpy(int alloc, const struct disprm *dissrc, struct disprm *disdst) + +{ + static const char *function = "discpy"; + + if (dissrc == 0x0) return DISERR_NULL_POINTER; + if (disdst == 0x0) return DISERR_NULL_POINTER; + struct wcserr **err = &(disdst->err); + + int naxis = dissrc->naxis; + if (naxis < 1) { + return wcserr_set(WCSERR_SET(DISERR_MEMORY), + "naxis must be positive (got %d)", naxis); + } + + int status; + if ((status = disinit(alloc, naxis, disdst, dissrc->ndpmax))) { + return status; + } + + memcpy(disdst->dtype, dissrc->dtype, naxis*sizeof(char [72])); + + disdst->ndp = dissrc->ndp; + memcpy(disdst->dp, dissrc->dp, dissrc->ndpmax*sizeof(struct dpkey)); + + disdst->totdis = dissrc->totdis; + memcpy(disdst->maxdis, dissrc->maxdis, naxis*sizeof(double)); + + return 0; +} + +//---------------------------------------------------------------------------- + +int disfree(struct disprm *dis) + +{ + if (dis == 0x0) return DISERR_NULL_POINTER; + + if (dis->flag != -1) { + // Optionally allocated by disinit() for given parameters. + if (dis->m_flag == DISSET) { + if (dis->dtype == dis->m_dtype) dis->dtype = 0x0; + if (dis->dp == dis->m_dp) dis->dp = 0x0; + if (dis->maxdis == dis->m_maxdis) dis->maxdis = 0x0; + + if (dis->m_dtype) free(dis->m_dtype); + if (dis->m_dp) free(dis->m_dp); + if (dis->m_maxdis) free(dis->m_maxdis); + } + + // The remainder were allocated by disset(). + if (dis->docorr) free(dis->docorr); + if (dis->Nhat) free(dis->Nhat); + + // Recall that axmap, offset, and scale were allocated in bulk. + if (dis->axmap && dis->axmap[0]) free(dis->axmap[0]); + if (dis->offset && dis->offset[0]) free(dis->offset[0]); + if (dis->scale && dis->scale[0]) free(dis->scale[0]); + + if (dis->axmap) free(dis->axmap); + if (dis->offset) free(dis->offset); + if (dis->scale) free(dis->scale); + + if (dis->iparm) { + for (int j = 0; j < dis->i_naxis; j++) { + if (dis->iparm[j]) free(dis->iparm[j]); + } + free(dis->iparm); + } + + if (dis->dparm) { + for (int j = 0; j < dis->i_naxis; j++) { + if (dis->dparm[j]) free(dis->dparm[j]); + } + free(dis->dparm); + } + + if (dis->disp2x) free(dis->disp2x); + if (dis->disx2p) free(dis->disx2p); + } + + dis->m_flag = 0; + dis->m_naxis = 0; + dis->m_dtype = 0x0; + dis->m_dp = 0x0; + dis->m_maxdis = 0x0; + + dis->docorr = 0x0; + dis->Nhat = 0x0; + dis->axmap = 0x0; + dis->offset = 0x0; + dis->scale = 0x0; + dis->iparm = 0x0; + dis->dparm = 0x0; + dis->disp2x = 0x0; + dis->disx2p = 0x0; + + wcserr_clear(&(dis->err)); + + dis->flag = 0; + + return 0; +} + +//---------------------------------------------------------------------------- + +int dissize(const struct disprm *dis, int sizes[2]) + +{ + if (dis == 0x0) { + sizes[0] = sizes[1] = 0; + return DISERR_NULL_POINTER; + } + + // Base size, in bytes. + sizes[0] = sizeof(struct disprm); + + // Total size of allocated memory, in bytes. + sizes[1] = 0; + + int naxis = dis->naxis; + + // disprm::dtype[]. + sizes[1] += naxis * sizeof(char [72]); + + // disprm::dp[]. + sizes[1] += dis->ndpmax * sizeof(struct dpkey); + + // disprm::maxdis[]. + sizes[1] += naxis * sizeof(double); + + // dis::err[]. + int exsizes[2]; + wcserr_size(dis->err, exsizes); + sizes[1] += exsizes[0] + exsizes[1]; + + // The remaining arrays are allocated by disset(). + if (abs(dis->flag) != DISSET) { + return 0; + } + + // dis::docorr[]. + sizes[1] += naxis * sizeof(int *); + + // dis::Nhat[]. + sizes[1] += naxis * sizeof(int *); + + // dis::axmap[][]. + sizes[1] += naxis * sizeof(int *); + sizes[1] += naxis*naxis * sizeof(int); + + // dis::offset[][]. + sizes[1] += naxis * sizeof(double *); + sizes[1] += naxis*naxis * sizeof(double); + + // dis::scale[][]. + sizes[1] += naxis * sizeof(double *); + sizes[1] += naxis*naxis * sizeof(double); + + // dis::iparm[][]. + sizes[1] += naxis * sizeof(int *); + for (int j = 0; j < naxis; j++) { + if (dis->iparm[j]) { + sizes[1] += dis->iparm[j][I_NIPARM] * sizeof(int); + } + } + + // dis::dparm[][]. + sizes[1] += naxis * sizeof(double *); + for (int j = 0; j < naxis; j++) { + if (dis->dparm[j]) { + sizes[1] += dis->dparm[j][I_NDPARM] * sizeof(double); + } + } + + // dis::disp2x[]. + sizes[1] += naxis * sizeof(int (*)(DISP2X_ARGS)); + + // dis::disx2p[]. + sizes[1] += naxis * sizeof(int (*)(DISX2P_ARGS)); + + return 0; +} + +//---------------------------------------------------------------------------- + +int disenq(const struct disprm *dis, int enquiry) + +{ + // Initialize. + if (dis == 0x0) return DISERR_NULL_POINTER; + + int answer = 0; + + if (enquiry & DISENQ_MEM) { + if (dis->m_flag != DISSET) return 0; + answer = 1; + } + + if (enquiry & DISENQ_SET) { + if (abs(dis->flag) != DISSET) return 0; + answer = 1; + } + + if (enquiry & DISENQ_BYP) { + if (dis->flag != 1 && dis->flag != -DISSET) return 0; + answer = 1; + } + + return answer; +} + +//---------------------------------------------------------------------------- + +int disprt(const struct disprm *dis) + +{ + if (dis == 0x0) return DISERR_NULL_POINTER; + + if (abs(dis->flag) != DISSET) { + wcsprintf("The disprm struct is UNINITIALIZED.\n"); + return 0; + } + + int naxis = dis->naxis; + + + // Parameters supplied. + wcsprintf(" flag: %d\n", dis->flag); + wcsprintf(" naxis: %d\n", naxis); + + WCSPRINTF_PTR(" dtype: ", dis->dtype, "\n"); + for (int j = 0; j < naxis; j++) { + wcsprintf(" \"%s\"\n", dis->dtype[j]); + } + + wcsprintf(" ndp: %d\n", dis->ndp); + wcsprintf(" ndpmax: %d\n", dis->ndpmax); + WCSPRINTF_PTR(" dp: ", dis->dp, "\n"); + for (int i = 0; i < dis->ndp; i++) { + if (dis->dp[i].type) { + wcsprintf(" %3d%3d %#- 11.5g %.32s\n", + dis->dp[i].j, dis->dp[i].type, dis->dp[i].value.f, dis->dp[i].field); + } else { + wcsprintf(" %3d%3d %11d %.32s\n", + dis->dp[i].j, dis->dp[i].type, dis->dp[i].value.i, dis->dp[i].field); + } + } + + wcsprintf(" totdis: %#- 11.5g\n", dis->totdis); + + WCSPRINTF_PTR(" maxdis: ", dis->maxdis, "\n"); + wcsprintf(" "); + for (int j = 0; j < naxis; j++) { + wcsprintf(" %#- 11.5g", dis->maxdis[j]); + } + wcsprintf("\n"); + + // Derived values. + WCSPRINTF_PTR(" docorr: ", dis->docorr, "\n"); + wcsprintf(" "); + for (int j = 0; j < naxis; j++) { + wcsprintf("%6d", dis->docorr[j]); + } + wcsprintf("\n"); + + WCSPRINTF_PTR(" Nhat: ", dis->Nhat, "\n"); + wcsprintf(" "); + for (int j = 0; j < naxis; j++) { + wcsprintf("%6d", dis->Nhat[j]); + } + wcsprintf("\n"); + + WCSPRINTF_PTR(" axmap: ", dis->axmap, "\n"); + for (int j = 0; j < naxis; j++) { + wcsprintf(" axmap[%d][]:", j); + for (int jhat = 0; jhat < naxis; jhat++) { + wcsprintf("%6d", dis->axmap[j][jhat]); + } + wcsprintf("\n"); + } + + WCSPRINTF_PTR(" offset: ", dis->offset, "\n"); + for (int j = 0; j < naxis; j++) { + wcsprintf("offset[%d][]:", j); + for (int jhat = 0; jhat < naxis; jhat++) { + wcsprintf(" %#- 11.5g", dis->offset[j][jhat]); + } + wcsprintf("\n"); + } + + WCSPRINTF_PTR(" scale: ", dis->scale, "\n"); + for (int j = 0; j < naxis; j++) { + wcsprintf(" scale[%d][]:", j); + for (int jhat = 0; jhat < naxis; jhat++) { + wcsprintf(" %#- 11.5g", dis->scale[j][jhat]); + } + wcsprintf("\n"); + } + + WCSPRINTF_PTR(" iparm: ", dis->iparm, "\n"); + for (int j = 0; j < naxis; j++) { + wcsprintf(" iparm[%d] : ", j); + WCSPRINTF_PTR("", dis->iparm[j], "\n"); + + if (dis->iparm[j]) { + wcsprintf(" iparm[%d][]:", j); + for (int k = 0; k < dis->iparm[j][I_NIPARM]; k++) { + if (k && k%5 == 0) { + wcsprintf("\n "); + } + wcsprintf(" %11d", dis->iparm[j][k]); + } + wcsprintf("\n"); + } + } + + WCSPRINTF_PTR(" dparm: ", dis->dparm, "\n"); + for (int j = 0; j < naxis; j++) { + wcsprintf(" dparm[%d] : ", j); + WCSPRINTF_PTR("", dis->dparm[j], "\n"); + + if (dis->dparm[j]) { + wcsprintf(" dparm[%d][]:", j); + for (int k = 0; k < dis->iparm[j][I_NDPARM]; k++) { + if (k && k%5 == 0) { + wcsprintf("\n "); + } + wcsprintf(" %#- 11.5g", dis->dparm[j][k]); + } + wcsprintf("\n"); + } + } + + wcsprintf(" i_naxis: %d\n", dis->i_naxis); + wcsprintf(" ndis: %d\n", dis->ndis); + + // Error handling. + WCSPRINTF_PTR(" err: ", dis->err, "\n"); + if (dis->err) { + wcserr_prt(dis->err, " "); + } + + // Pointers to distortion functions. + char hext[32]; + WCSPRINTF_PTR(" disp2x: ", dis->disp2x, "\n"); + for (int j = 0; j < naxis; j++) { + wcsprintf(" disp2x[%d]: %s", j, + wcsutil_fptr2str((void (*)(void))dis->disp2x[j], hext)); + if (dis->disp2x[j] == dispoly) { + wcsprintf(" (= dispoly)\n"); + } else if (dis->disp2x[j] == tpd1) { + wcsprintf(" (= tpd1)\n"); + } else if (dis->disp2x[j] == tpd2) { + wcsprintf(" (= tpd2)\n"); + } else if (dis->disp2x[j] == tpd3) { + wcsprintf(" (= tpd3)\n"); + } else if (dis->disp2x[j] == tpd4) { + wcsprintf(" (= tpd4)\n"); + } else if (dis->disp2x[j] == tpd5) { + wcsprintf(" (= tpd5)\n"); + } else if (dis->disp2x[j] == tpd6) { + wcsprintf(" (= tpd6)\n"); + } else if (dis->disp2x[j] == tpd7) { + wcsprintf(" (= tpd7)\n"); + } else if (dis->disp2x[j] == tpd8) { + wcsprintf(" (= tpd8)\n"); + } else if (dis->disp2x[j] == tpd9) { + wcsprintf(" (= tpd9)\n"); + } else { + wcsprintf("\n"); + } + } + + // Pointers to inverse distortion functions. + WCSPRINTF_PTR(" disx2p: ", dis->disx2p, "\n"); + for (int j = 0; j < naxis; j++) { + wcsprintf(" disx2p[%d]: %s\n", j, + wcsutil_fptr2str((void (*)(void))dis->disx2p[j], hext)); + } + + // Memory management. + wcsprintf(" m_flag: %d\n", dis->m_flag); + wcsprintf(" m_naxis: %d\n", dis->m_naxis); + WCSPRINTF_PTR(" m_dtype: ", dis->m_dtype, ""); + if (dis->m_dtype == dis->dtype) wcsprintf(" (= dtype)"); + wcsprintf("\n"); + WCSPRINTF_PTR(" m_dp: ", dis->m_dp, ""); + if (dis->m_dp == dis->dp) wcsprintf(" (= dp)"); + wcsprintf("\n"); + WCSPRINTF_PTR(" m_maxdis: ", dis->m_maxdis, ""); + if (dis->m_maxdis == dis->maxdis) wcsprintf(" (= maxdis)"); + wcsprintf("\n"); + + return 0; +} + +//---------------------------------------------------------------------------- + +int disperr(const struct disprm *dis, const char *prefix) + +{ + if (dis == 0x0) return DISERR_NULL_POINTER; + + if (dis->err) { + wcserr_prt(dis->err, prefix); + } + + return 0; +} + +//---------------------------------------------------------------------------- + +int dishdo(struct disprm *dis) + +{ + static const char *function = "dishdo"; + + if (dis == 0x0) return DISERR_NULL_POINTER; + struct wcserr **err = &(dis->err); + + int status = 0; + for (int j = 0; j < dis->naxis; j++) { + if (dis->iparm[j][I_DTYPE]) { + if (dis->iparm[j][I_DTYPE] == DIS_TPD) { + // Implemented as TPD... + if (strcmp(dis->dtype[j], "TPD") != 0) { + // ... but isn't TPD. + dis->iparm[j][I_DTYPE] |= DIS_DOTPD; + } + } else { + // Must be a Polynomial that can't be implemented as TPD. + status = wcserr_set(WCSERR_SET(DISERR_BAD_PARAM), + "Translation of %s to TPD is not possible", dis->dtype[j]); + } + } + } + + return status; +} + +//---------------------------------------------------------------------------- + +int disset(struct disprm *dis) + +{ + static const char *function = "disset"; + + if (dis == 0x0) return DISERR_NULL_POINTER; + if (dis->flag == -DISSET) return 0; + struct wcserr **err = &(dis->err); + + // Do basic checks. + if (dis->ndp < 0) { + return wcserr_set(WCSERR_SET(DISERR_BAD_PARAM), + "disprm::ndp is negative (%d)", dis->ndp); + } + + int naxis = dis->naxis; + int ndis = 0; + for (int j = 0; j < naxis; j++) { + if (strlen(dis->dtype[j])) { + ndis++; + break; + } + } + + char *dpq; + if (dis->ndp) { + // Is it prior or sequent? + if (dis->dp[0].field[1] == 'P') { + dpq = "DPja"; + } else if (dis->dp[0].field[1] == 'Q') { + dpq = "DQia"; + } else { + return wcserr_set(WCSERR_SET(DISERR_BAD_PARAM), + "disprm::dp[0].field (%s) is invalid", dis->dp[0].field); + } + + } else { + if (ndis) { + return wcserr_set(WCSERR_SET(DISERR_BAD_PARAM), + "No DPja or DQia keywords, NAXES at least is required for each " + "distortion"); + } + + // A Clayton's distortion. Avert compiler warnings about possible use of + // uninitialized variables. + dpq = 0x0; + } + + + // Free memory allocated separately for each axis. + for (int j = 0; j < dis->i_naxis; j++) { + if (dis->iparm[j]) free(dis->iparm[j]); + if (dis->dparm[j]) free(dis->dparm[j]); + dis->iparm[j] = 0x0; + dis->dparm[j] = 0x0; + } + + // Allocate or reallocate memory, if necessary, for derived parameter and + // work arrays sized according to the number of axes. + if (dis->i_naxis < naxis) { + if (dis->i_naxis) { + free(dis->docorr); + free(dis->Nhat); + + // Noting that axmap, offset, and scale are allocated in bulk. + free(dis->axmap[0]); + free(dis->axmap); + free(dis->offset[0]); + free(dis->offset); + free(dis->scale[0]); + free(dis->scale); + + free(dis->iparm); + free(dis->dparm); + + free(dis->disp2x); + free(dis->disx2p); + } + + if ((dis->docorr = calloc(naxis, sizeof(int *))) == 0x0) { + disfree(dis); + return wcserr_set(DIS_ERRMSG(DISERR_MEMORY)); + } + + if ((dis->Nhat = calloc(naxis, sizeof(int *))) == 0x0) { + disfree(dis); + return wcserr_set(DIS_ERRMSG(DISERR_MEMORY)); + } + + // Allocate axmap[][] in bulk and then carve it up. + if ((dis->axmap = calloc(naxis, sizeof(int *))) == 0x0) { + disfree(dis); + return wcserr_set(DIS_ERRMSG(DISERR_MEMORY)); + } + + if ((dis->axmap[0] = calloc(naxis*naxis, sizeof(int))) == 0x0) { + disfree(dis); + return wcserr_set(DIS_ERRMSG(DISERR_MEMORY)); + } + + for (int j = 1; j < naxis; j++) { + dis->axmap[j] = dis->axmap[j-1] + naxis; + } + + // Allocate offset[][] in bulk and then carve it up. + if ((dis->offset = calloc(naxis, sizeof(double *))) == 0x0) { + disfree(dis); + return wcserr_set(DIS_ERRMSG(DISERR_MEMORY)); + } + + if ((dis->offset[0] = calloc(naxis*naxis, sizeof(double))) == 0x0) { + disfree(dis); + return wcserr_set(DIS_ERRMSG(DISERR_MEMORY)); + } + + for (int j = 1; j < naxis; j++) { + dis->offset[j] = dis->offset[j-1] + naxis; + } + + // Allocate scale[][] in bulk and then carve it up. + if ((dis->scale = calloc(naxis, sizeof(double *))) == 0x0) { + disfree(dis); + return wcserr_set(DIS_ERRMSG(DISERR_MEMORY)); + } + + if ((dis->scale[0] = calloc(naxis*naxis, sizeof(double))) == 0x0) { + disfree(dis); + return wcserr_set(DIS_ERRMSG(DISERR_MEMORY)); + } + + for (int j = 1; j < naxis; j++) { + dis->scale[j] = dis->scale[j-1] + naxis; + } + + if ((dis->iparm = calloc(naxis, sizeof(int *))) == 0x0) { + disfree(dis); + return wcserr_set(DIS_ERRMSG(DISERR_MEMORY)); + } + + if ((dis->dparm = calloc(naxis, sizeof(double *))) == 0x0) { + disfree(dis); + return wcserr_set(DIS_ERRMSG(DISERR_MEMORY)); + } + + if ((dis->disp2x = calloc(naxis, sizeof(int (*)(DISP2X_ARGS)))) == 0x0) { + disfree(dis); + return wcserr_set(DIS_ERRMSG(DISERR_MEMORY)); + } + + if ((dis->disx2p = calloc(naxis, sizeof(int (*)(DISX2P_ARGS)))) == 0x0) { + disfree(dis); + return wcserr_set(DIS_ERRMSG(DISERR_MEMORY)); + } + + dis->i_naxis = naxis; + } + + // Start with a clean slate. + for (int j = 0; j < naxis; j++) { + dis->docorr[j] = 1; + } + + memset(dis->Nhat, 0, naxis*sizeof(int)); + + for (int jhat = 0; jhat < naxis*naxis; jhat++) { + dis->axmap[0][jhat] = -1; + } + + memset(dis->offset[0], 0, naxis*naxis*sizeof(double)); + + for (int jhat = 0; jhat < naxis*naxis; jhat++) { + dis->scale[0][jhat] = 1.0; + } + + // polyset() etc. must look after iparm[][] and dparm[][]. + + dis->i_naxis = naxis; + dis->ndis = 0; + + memset(dis->disp2x, 0, naxis*sizeof(int (*)(DISP2X_ARGS))); + memset(dis->disx2p, 0, naxis*sizeof(int (*)(DISX2P_ARGS))); + + + // Handle DPja or DQia keywords common to all distortions. + struct dpkey *keyp = dis->dp; + for (int idp = 0; idp < dis->ndp; idp++, keyp++) { + // Check that they're all one kind or the other. + if (keyp->field[1] != dpq[1]) { + return wcserr_set(WCSERR_SET(DISERR_BAD_PARAM), + "disprm::dp appears to contain a mix of DPja and DQia keys"); + } + + int j = keyp->j; + + if (j < 1 || naxis < j) { + return wcserr_set(WCSERR_SET(DISERR_BAD_PARAM), + "Invalid axis number (%d) in %s", j, keyp->field); + } + + char *fp; + if ((fp = strchr(keyp->field, '.')) == 0x0) { + return wcserr_set(WCSERR_SET(DISERR_BAD_PARAM), + "Invalid record field name: %s", j, keyp->field); + } + fp++; + + // Convert to 0-relative axis number. + j--; + + if (strncmp(fp, "DOCORR", 7) == 0) { + if (dpkeyi(keyp) == 0) { + dis->docorr[j] = 0; + } + + } else if (strncmp(fp, "NAXES", 6) == 0) { + int Nhat = dpkeyi(keyp); + if (Nhat < 0 || naxis < Nhat) { + return wcserr_set(WCSERR_SET(DISERR_BAD_PARAM), + "Invalid value of Nhat for %s distortion in %s: %d", dis->dtype[j], + keyp->field, Nhat); + } + + dis->Nhat[j] = Nhat; + + } else if (strncmp(fp, "AXIS.", 5) == 0) { + int jhat; + sscanf(fp+5, "%d", &jhat); + if (jhat < 1 || naxis < jhat) { + return wcserr_set(WCSERR_SET(DISERR_BAD_PARAM), + "Invalid axis in axis map for %s distortion in %s: %d", + dis->dtype[j], keyp->field, jhat); + } + + // N.B. axis numbers in the map are 0-relative. + dis->axmap[j][jhat-1] = dpkeyi(keyp) - 1; + + } else if (strncmp(fp, "OFFSET.", 7) == 0) { + int jhat; + sscanf(fp+7, "%d", &jhat); + dis->offset[j][jhat-1] = dpkeyd(keyp); + + } else if (strncmp(fp, "SCALE.", 6) == 0) { + int jhat; + sscanf(fp+6, "%d", &jhat); + dis->scale[j][jhat-1] = dpkeyd(keyp); + } + } + + // Set defaults and do sanity checks on axmap[][]. + for (int j = 0; j < naxis; j++) { + if (strlen(dis->dtype[j]) == 0) { + // No distortion on this axis, check that there are no parameters. + keyp = dis->dp; + for (int idp = 0; idp < dis->ndp; idp++, keyp++) { + if (keyp->j == j+1) { + return wcserr_set(WCSERR_SET(DISERR_BAD_PARAM), + "No distortion type, yet %s keyvalues are present for axis %d", + dpq, j+1); + } + } + + continue; + } + + // N.B. NAXES (Nhat) has no default value. + if (dis->Nhat[j] <= 0) { + return wcserr_set(WCSERR_SET(DISERR_BAD_PARAM), + "%s.NAXES was not set (or bad) for %s distortion on axis %d", + dpq, dis->dtype[j], j+1); + } + + // Set defaults for axmap[][]. + int Nhat = dis->Nhat[j]; + for (int jhat = 0; jhat < Nhat; jhat++) { + if (dis->axmap[j][jhat] == -1) { + dis->axmap[j][jhat] = jhat; + } + } + + // Sanity check on the length of the axis map. + Nhat = 0; + for (int jhat = 0; jhat < naxis; jhat++) { + if (dis->axmap[j][jhat] != -1) Nhat = jhat+1; + } + + if (Nhat != dis->Nhat[j]) { + return wcserr_set(WCSERR_SET(DISERR_BAD_PARAM), + "Mismatch in length of axis map for %s distortion on axis %d", + dis->dtype[j], j+1); + } + + // Check uniqueness of entries in the axis map. + for (int jhat = 0; jhat < Nhat; jhat++) { + for (int k = 0; k < jhat; k++) { + if (dis->axmap[j][jhat] == dis->axmap[j][k]) { + return wcserr_set(WCSERR_SET(DISERR_BAD_PARAM), + "Duplicated entry in axis map for %s distortion on axis %d", + dis->dtype[j], j+1); + } + } + } + } + + + // Identify the distortion functions. + ndis = 0; + for (int j = 0; j < naxis; j++) { + if (strlen(dis->dtype[j]) == 0) { + // No distortion on this axis. + continue; + } + + if (dis->Nhat[j] == 0) { + return wcserr_set(WCSERR_SET(DISERR_BAD_PARAM), + "Empty axis map for %s distortion on axis %d", dis->dtype[j], j+1); + } + + // Invoke the specific setup functions for each distortion. + int status; + if (strcmp(dis->dtype[j], "TPD") == 0) { + // Template Polynomial Distortion. + if ((status = tpdset(j, dis))) { + // (Preserve the error message set by tpdset().) + return status; + } + + } else if (strcmp(dis->dtype[j], "TPV") == 0) { + // TPV "projection". + if ((status = tpvset(j, dis))) { + // (Preserve the error message set by tpvset().) + return status; + } + + } else if (strcmp(dis->dtype[j], "SIP") == 0) { + // Simple Imaging Polynomial (SIP). + if ((status = sipset(j, dis))) { + // (Preserve the error message set by sipset().) + return status; + } + + } else if (strcmp(dis->dtype[j], "DSS") == 0) { + // Digitized Sky Survey (DSS). + if ((status = dssset(j, dis))) { + // (Preserve the error message set by dssset().) + return status; + } + + } else if (strncmp(dis->dtype[j], "WAT", 3) == 0) { + // WAT (TNX or ZPX "projections"). + if ((status = watset(j, dis))) { + // (Preserve the error message set by watset().) + return status; + } + + } else if (strcmp(dis->dtype[j], "Polynomial") == 0 || + strcmp(dis->dtype[j], "Polynomial*") == 0) { + // General polynomial distortion. + if ((status = polyset(j, dis))) { + // (Preserve the error message set by polyset().) + return status; + } + + } else { + return wcserr_set(WCSERR_SET(DISERR_BAD_PARAM), + "Unrecognized/unimplemented distortion function: %s", dis->dtype[j]); + } + + ndis++; + } + + dis->ndis = ndis; + + dis->flag = (dis->flag == 1) ? -DISSET : DISSET; + + return 0; +} + +//---------------------------------------------------------------------------- + +int disp2x( + struct disprm *dis, + const double rawcrd[], + double discrd[]) + +{ + static const char *function = "disp2x"; + + // Initialize. + if (dis == 0x0) return DISERR_NULL_POINTER; + struct wcserr **err = &(dis->err); + + int status = 0; + if (abs(dis->flag) != DISSET) { + if ((status = disset(dis))) return status; + } + + int naxis = dis->naxis; + + double *tmpcrd; + if ((tmpcrd = calloc(naxis, sizeof(double))) == 0x0) { + status = wcserr_set(DIS_ERRMSG(DISERR_MEMORY)); + goto cleanup; + } + + + // Invoke the distortion functions for each axis. + for (int j = 0; j < naxis; j++) { + if (dis->disp2x[j]) { + double *offset = dis->offset[j]; + double *scale = dis->scale[j]; + + int Nhat = dis->Nhat[j]; + for (int jhat = 0; jhat < Nhat; jhat++) { + int axisj = dis->axmap[j][jhat]; + tmpcrd[jhat] = (rawcrd[axisj] - offset[jhat])*scale[jhat]; + } + + double dtmp; + if ((dis->disp2x[j])(0, dis->iparm[j], dis->dparm[j], Nhat, tmpcrd, + &dtmp)) { + status = wcserr_set(DIS_ERRMSG(DISERR_DISTORT)); + goto cleanup; + } + + if (dis->docorr[j]) { + // Distortion function computes a correction to be applied. + discrd[j] = rawcrd[j] + dtmp; + } else { + // Distortion function computes corrected coordinates directly. + discrd[j] = dtmp; + } + + } else { + discrd[j] = rawcrd[j]; + } + } + +cleanup: + if (tmpcrd) free(tmpcrd); + return status; +} + +//---------------------------------------------------------------------------- +// This function is intended for debugging purposes only. +// No documentation or prototype is provided in dis.h. + +int disitermax(int itermax) + +{ + static int ITERMAX = 30; + + if (itermax >= 0) { + ITERMAX = itermax; + } + + return ITERMAX; +} + +//---------------------------------------------------------------------------- + +int disx2p( + struct disprm *dis, + const double discrd[], + double rawcrd[]) + +{ + static const char *function = "disx2p"; + + const double TOL = 1.0e-13; + + // Initialize. + if (dis == 0x0) return DISERR_NULL_POINTER; + struct wcserr **err = &(dis->err); + + int status = 0; + if (abs(dis->flag) != DISSET) { + if ((status = disset(dis))) return status; + } + + int naxis = dis->naxis; + + double *tmpmem; + if ((tmpmem = calloc(5*naxis, sizeof(double))) == 0x0) { + status = wcserr_set(DIS_ERRMSG(DISERR_MEMORY)); + goto cleanup; + } + + // Carve up working memory. + double *tmpcrd = tmpmem; + double *dcrd0 = tmpcrd + naxis; + double *dcrd1 = dcrd0 + naxis; + double *rcrd1 = dcrd1 + naxis; + double *delta = rcrd1 + naxis; + + + // Zeroth approximation. The assumption here and below is that the + // distortion is small so that, to first order in the neighbourhood of + // the solution, discrd[j] ~= a + b*rawcrd[j], i.e. independent of + // rawcrd[i], where i != j. This is effectively equivalent to assuming + // that the distortion functions are separable to first order. + // Furthermore, a is assumed to be small, and b close to unity. + memcpy(rawcrd, discrd, naxis*sizeof(double)); + + // If available, use disprm::disx2p to improve the zeroth approximation. + for (int j = 0; j < naxis; j++) { + if (dis->disx2p[j]) { + double *offset = dis->offset[j]; + double *scale = dis->scale[j]; + + int Nhat = dis->Nhat[j]; + for (int jhat = 0; jhat < Nhat; jhat++) { + int axisj = dis->axmap[j][jhat]; + tmpcrd[jhat] = (discrd[axisj] - offset[jhat])*scale[jhat]; + } + + double rtmp; + if ((status = (dis->disx2p[j])(1, dis->iparm[j], dis->dparm[j], Nhat, + tmpcrd, &rtmp))) { + status = wcserr_set(DIS_ERRMSG(DISERR_DEDISTORT)); + goto cleanup; + } + + if (dis->docorr[j]) { + // Inverse distortion function computes a correction to be applied. + rawcrd[j] = discrd[j] + rtmp; + } else { + // Inverse distortion function computes corrected coordinates directly. + rawcrd[j] = rtmp; + } + } + } + + // Quick return debugging hook, assumes inverse functions were defined. + int itermax; + if ((itermax = disitermax(-1)) == 0) { + goto cleanup; + } + + + // Iteratively invert the (well-behaved!) distortion function. + int convergence = 0, iter; + for (iter = 0; iter < itermax; iter++) { + if ((status = disp2x(dis, rawcrd, dcrd0))) { + wcserr_set(DIS_ERRMSG(status)); + goto cleanup; + } + + // Check for convergence. + convergence = 1; + for (int j = 0; j < naxis; j++) { + delta[j] = discrd[j] - dcrd0[j]; + + double dd; + if (fabs(discrd[j]) < 1.0) { + dd = delta[j]; + } else { + // TOL may be below the precision achievable from floating point + // subtraction, so switch to a fractional tolerance. + dd = delta[j] / discrd[j]; + } + + if (TOL < fabs(dd)) { + // No convergence yet on this axis. + convergence = 0; + } + } + + if (convergence) break; + + // Determine a suitable test point for computing the gradient. + for (int j = 0; j < naxis; j++) { + // Constrain the displacement. + delta[j] /= 2.0; + if (fabs(delta[j]) < 1.0e-6) { + if (delta[j] < 0.0) { + delta[j] = -1.0e-6; + } else { + delta[j] = 1.0e-6; + } + } else if (1.0 < fabs(delta[j])) { + if (delta[j] < 0.0) { + delta[j] = -1.0; + } else { + delta[j] = 1.0; + } + } + } + + if (iter < itermax/2) { + // With the assumption of small distortions (as above), the gradient + // of discrd[j] should be dominated by the partial derivative with + // respect to rawcrd[j], and we can neglect partials with respect + // to rawcrd[i], where i != j. Thus only one test point is needed, + // not one for each axis. + for (int j = 0; j < naxis; j++) { + rcrd1[j] = rawcrd[j] + delta[j]; + } + + // Compute discrd[] at the test point. + if ((status = disp2x(dis, rcrd1, dcrd1))) { + wcserr_set(DIS_ERRMSG(status)); + goto cleanup; + } + + // Compute the next approximation. + for (int j = 0; j < naxis; j++) { + rawcrd[j] += (discrd[j] - dcrd0[j]) * + (delta[j]/(dcrd1[j] - dcrd0[j])); + } + + } else { + // Convergence should not take more than seven or so iterations. As + // it is slow, try computing the gradient in full. + memcpy(rcrd1, rawcrd, naxis*sizeof(double)); + + for (int j = 0; j < naxis; j++) { + rcrd1[j] += delta[j]; + + // Compute discrd[] at the test point. + if ((status = disp2x(dis, rcrd1, dcrd1))) { + wcserr_set(DIS_ERRMSG(status)); + goto cleanup; + } + + // Compute the next approximation. + rawcrd[j] += (discrd[j] - dcrd0[j]) * + (delta[j]/(dcrd1[j] - dcrd0[j])); + + rcrd1[j] -= delta[j]; + } + } + } + + + if (!convergence) { + double residual = 0.0; + for (int j = 0; j < naxis; j++) { + double dd = discrd[j] - dcrd0[j] ; + residual += dd*dd; + } + residual = sqrt(residual); + + status = wcserr_set(WCSERR_SET(DISERR_DEDISTORT), + "Convergence not achieved after %d iterations, residual %#7.2g", iter, + residual); + goto cleanup; + } + + +cleanup: + if (tmpmem) free(tmpmem); + return status; +} + +//---------------------------------------------------------------------------- + +int diswarp( + struct disprm *dis, + const double pixblc[], + const double pixtrc[], + const double pixsamp[], + int *nsamp, + double maxdis[], + double *maxtot, + double avgdis[], + double *avgtot, + double rmsdis[], + double *rmstot) + +{ + static const char *function = "diswarp"; + + int status = 0; + + // Initialize. + if (dis == 0x0) return DISERR_NULL_POINTER; + struct wcserr **err = &(dis->err); + + int naxis = dis->naxis; + + if (nsamp) *nsamp = 0; + for (int j = 0; j < naxis; j++) { + if (maxdis) maxdis[j] = 0.0; + if (avgdis) avgdis[j] = 0.0; + if (rmsdis) rmsdis[j] = 0.0; + } + if (maxtot) *maxtot = 0.0; + if (avgtot) *avgtot = 0.0; + if (rmstot) *rmstot = 0.0; + + // Quick return if no distortions. + if (dis->ndis == 0) return 0; + + double *tmpmem; + if ((tmpmem = calloc(4*(size_t)naxis, sizeof(double))) == 0x0) { + status = wcserr_set(DIS_ERRMSG(DISERR_MEMORY)); + goto cleanup; + } + + // Carve up working memory. + double *pixinc = tmpmem; + double *pixend = pixinc + naxis; + double *sumdis = pixend + naxis; + double *ssqdis = sumdis + naxis; + + // Work out increments on each axis. + for (int j = 0; j < naxis; j++) { + double pixspan = pixtrc[j] - (pixblc ? pixblc[j] : 1.0); + + if (pixsamp == 0x0) { + pixinc[j] = 1.0; + } else if (pixsamp[j] == 0.0) { + pixinc[j] = 1.0; + } else if (pixsamp[j] > 0.0) { + pixinc[j] = pixsamp[j]; + } else if (pixsamp[j] > -1.5) { + pixinc[j] = 2.0*pixspan; + } else { + pixinc[j] = pixspan / ((int)(-pixsamp[j] - 0.5)); + } + } + + // Get some more memory for coordinate vectors. + double *pix0, *pix1; + if ((pix0 = calloc(2*(size_t)naxis, sizeof(double))) == 0x0) { + status = wcserr_set(DIS_ERRMSG(DISERR_MEMORY)); + goto cleanup; + } + + pix1 = pix0 + naxis; + + + // Set up the array of pixel coordinates. + for (int j = 0; j < naxis; j++) { + pix0[j] = pixblc ? pixblc[j] : 1.0; + pixend[j] = pixtrc[j] + 0.5*pixinc[j]; + } + + // Initialize accumulators. + for (int j = 0; j < naxis; j++) { + sumdis[j] = 0.0; + ssqdis[j] = 0.0; + } + double sumtot = 0.0; + double ssqtot = 0.0; + + + // Loop over N dimensions. + int carry = 0; + while (carry == 0) { + if ((status = disp2x(dis, pix0, pix1))) { + // (Preserve the error message set by disp2x().) + goto cleanup; + } + + // Accumulate statistics. + (*nsamp)++; + + double dssq = 0.0; + for (int j = 0; j < naxis; j++) { + double dpix = pix1[j] - pix0[j]; + double dpx2 = dpix*dpix; + + sumdis[j] += dpix; + ssqdis[j] += dpx2; + + if (maxdis && (dpix = fabs(dpix)) > maxdis[j]) { + maxdis[j] = dpix; + } + + dssq += dpx2; + } + + double totdis = sqrt(dssq); + sumtot += totdis; + ssqtot += totdis*totdis; + + if (maxtot && *maxtot < totdis) { + *maxtot = totdis; + } + + // Next pixel. + for (int j = 0; j < naxis; j++) { + pix0[j] += pixinc[j]; + if (pix0[j] < pixend[j]) { + carry = 0; + break; + } + + pix0[j] = pixblc ? pixblc[j] : 1.0; + carry = 1; + } + } + + + // Compute the means and RMSs. + for (int j = 0; j < naxis; j++) { + ssqdis[j] /= *nsamp; + sumdis[j] /= *nsamp; + if (avgdis) avgdis[j] = sumdis[j]; + if (rmsdis) rmsdis[j] = sqrt(ssqdis[j] - sumdis[j]*sumdis[j]); + } + + ssqtot /= *nsamp; + sumtot /= *nsamp; + if (avgtot) *avgtot = sumtot; + if (rmstot) *rmstot = sqrt(ssqtot - sumtot*sumtot); + + +cleanup: + if (tmpmem) { + free(tmpmem); + if (pix0) free(pix0); + } + + return status; +} + +//---------------------------------------------------------------------------- + +int polyset(int j, struct disprm *dis) + +{ + static const char *function = "polyset"; + + // Initialize. + if (dis == 0x0) return DISERR_NULL_POINTER; + struct wcserr **err = &(dis->err); + + int naxis = dis->naxis; + + char id[32]; + sprintf(id, "Polynomial on axis %d", j+1); + + + // Find the number of auxiliary variables and terms. + int K = 0; + int M = 0; + struct dpkey *keyp = dis->dp; + for (int idp = 0; idp < dis->ndp; idp++, keyp++) { + if (keyp->j-1 != j) continue; + + char *fp; + if ((fp = strchr(keyp->field, '.')) == 0x0) { + return wcserr_set(WCSERR_SET(DISERR_BAD_PARAM), + "Invalid field name for %s: %s", id, keyp->field); + } + fp++; + + if (strcmp(fp, "NAUX") == 0) { + K = dpkeyi(keyp); + } else if (strcmp(fp, "NTERMS") == 0) { + M = dpkeyi(keyp); + } + } + + if (K < 0) { + return wcserr_set(WCSERR_SET(DISERR_BAD_PARAM), + "Invalid number of auxiliaries (%d) for %s", K, id); + } + + if (M <= 0) { + return wcserr_set(WCSERR_SET(DISERR_BAD_PARAM), + "Invalid number of terms (%d) for %s", M, id); + } + + int Nhat = dis->Nhat[j]; + int nKparm = 2*(Nhat + 1); + int nVar = Nhat + K; + int nTparm = 1 + nVar; + int ndparm = K*nKparm + M*nTparm; + +// These iparm indices are specific to Polynomial. +#define I_NIDX 3 // No. of indexes in iparm[]. +#define I_LENDP 4 // Full (allocated) length of dparm[]. +#define I_K 5 // No. of auxiliary variables. +#define I_M 6 // No. of terms in the polynomial. +#define I_NKPARM 7 // No. of parameters used to define each auxiliary. +#define I_NTPARM 8 // No. of parameters used to define each term. +#define I_NVAR 9 // No. of independent + auxiliary variables. +#define I_MNVAR 10 // No. of powers (exponents) in the polynomial. +#define I_DPOLY 11 // dparm offset for polynomial coefficients. +#define I_DAUX 12 // dparm offset for auxiliary coefficients. +#define I_DVPOW 13 // dparm offset for integral powers of variables. +#define I_MAXPOW 14 // iparm offset for max powers. +#define I_DPOFF 15 // iparm offset for dparm offsets. +#define I_FLAGS 16 // iparm offset for flags. +#define I_IPOW 17 // iparm offset for integral powers. +#define I_NPOLY 18 + + // Add extra for handling integer exponents. See "Optimization" below. + int niparm = I_NPOLY + (2 + 2*M)*nVar; + + // Add extra memory for temporaries. + int lendp = ndparm + K; + + // Allocate memory for the indexes and parameter array. + if ((dis->iparm[j] = calloc(niparm, sizeof(int))) == 0x0) { + return wcserr_set(DIS_ERRMSG(DISERR_MEMORY)); + } + + if ((dis->dparm[j] = calloc(lendp, sizeof(double))) == 0x0) { + return wcserr_set(DIS_ERRMSG(DISERR_MEMORY)); + } + + // These help a bit to stop the code from turning into hieroglyphics. + int *iparm = dis->iparm[j]; + double *dparm = dis->dparm[j]; + + + // Record the indexing parameters. The first three are more widely used. + iparm[I_DTYPE] = DIS_POLYNOMIAL; + iparm[I_NIPARM] = niparm; + iparm[I_NDPARM] = ndparm; + + iparm[I_NIDX] = I_NPOLY; + iparm[I_LENDP] = lendp; + iparm[I_K] = K; + iparm[I_M] = M; + iparm[I_NKPARM] = nKparm; + iparm[I_NTPARM] = nTparm; + iparm[I_NVAR] = nVar; + iparm[I_MNVAR] = M*nVar; + iparm[I_DPOLY] = K*nKparm; + iparm[I_DAUX] = ndparm; + iparm[I_DVPOW] = ndparm + K; + iparm[I_MAXPOW] = iparm[I_NIDX]; + iparm[I_DPOFF] = iparm[I_MAXPOW] + nVar; + iparm[I_FLAGS] = iparm[I_DPOFF] + nVar; + iparm[I_IPOW] = iparm[I_FLAGS] + M*nVar; + + // Set default values of POWER for the auxiliary variables. + double *dptr = dparm + (1 + Nhat); + for (int k = 0; k < K; k++) { + for (int jhat = 0; jhat <= Nhat; jhat++) { + dptr[jhat] = 1.0; + } + dptr += nKparm; + } + + // Set default values of COEFF for the independent variables. + dptr = dparm + iparm[I_DPOLY]; + for (int m = 0; m < M; m++) { + *dptr = 1.0; + dptr += nTparm; + } + + // Extract parameter values from DPja or DQia. + int i, k, m; + k = m = 0; + keyp = dis->dp; + for (int idp = 0; idp < dis->ndp; idp++, keyp++) { + // N.B. keyp->j is 1-relative, but j is 0-relative. + if (keyp->j-1 != j) continue; + + char *fp = strchr(keyp->field, '.') + 1; + + if (strncmp(fp, "AUX.", 4) == 0) { + // N.B. k here is 1-relative. + fp += 4; + sscanf(fp, "%d", &k); + if (k < 1 || K < k) { + return wcserr_set(WCSERR_SET(DISERR_BAD_PARAM), + "Bad auxiliary variable (%d) for %s: %s", k, id, keyp->field); + } + + if ((fp = strchr(fp, '.')) == 0x0) { + return wcserr_set(WCSERR_SET(DISERR_BAD_PARAM), + "Invalid field name for %s: %s", id, keyp->field); + } + fp++; + + int offset; + if (strncmp(fp, "COEFF.", 6) == 0) { + offset = 0; + + } else if (strncmp(fp, "POWER.", 6) == 0) { + offset = 1 + Nhat; + + } else { + return wcserr_set(WCSERR_SET(DISERR_BAD_PARAM), + "Unrecognized field name for %s: %s", id, keyp->field); + } + + fp += 6; + int jhat; + sscanf(fp, "%d", &jhat); + if (jhat < 0 || naxis < jhat) { + // N.B. jhat == 0 is ok. + return wcserr_set(WCSERR_SET(DISERR_BAD_PARAM), + "Invalid axis number (%d) for %s: %s", jhat, id, keyp->field); + } + + i = (k-1)*nKparm + offset + jhat; + dparm[i] = dpkeyd(keyp); + + } else if (strncmp(fp, "TERM.", 5) == 0) { + // N.B. m here is 1-relative. + fp += 5; + sscanf(fp, "%d", &m); + if (m < 1 || M < m) { + return wcserr_set(WCSERR_SET(DISERR_BAD_PARAM), + "Bad term (%d) for %s: %s", m, id, keyp->field); + } + + if ((fp = strchr(fp, '.')) == 0x0) { + return wcserr_set(WCSERR_SET(DISERR_BAD_PARAM), + "Invalid field name for %s: %s", id, keyp->field); + } + fp++; + + if (strcmp(fp, "COEFF") == 0) { + i = iparm[I_DPOLY] + (m-1)*nTparm; + dparm[i] = dpkeyd(keyp); + + } else if (strncmp(fp, "VAR.", 4) == 0) { + // N.B. jhat here is 1-relative. + fp += 4; + int jhat; + sscanf(fp, "%d", &jhat); + if (jhat < 1 || naxis < jhat) { + return wcserr_set(WCSERR_SET(DISERR_BAD_PARAM), + "Invalid axis number (%d) for %s: %s", jhat, id, keyp->field); + } + + i = iparm[I_DPOLY] + (m-1)*nTparm + 1 + (jhat-1); + double power = dpkeyd(keyp); + dparm[i] = power; + + } else if (strncmp(fp, "AUX.", 4) == 0) { + // N.B. k here is 1-relative. + fp += 4; + sscanf(fp, "%d", &k); + if (k < 1 || K < k) { + return wcserr_set(WCSERR_SET(DISERR_BAD_PARAM), + "Bad auxiliary variable (%d) for %s: %s", k, id, keyp->field); + } + + i = iparm[I_DPOLY] + (m-1)*nTparm + 1 + Nhat + (k-1); + double power = dpkeyd(keyp); + dparm[i] = power; + + } else { + return wcserr_set(WCSERR_SET(DISERR_BAD_PARAM), + "Unrecognized field name for %s: %s", id, keyp->field); + } + + } else if (strcmp(fp, "DOCORR") && + strcmp(fp, "NAXES") && + strncmp(fp, "AXIS.", 5) && + strncmp(fp, "OFFSET.", 7) && + strncmp(fp, "SCALE.", 6) && + strcmp(fp, "NAUX") && + strcmp(fp, "NTERMS")) { + return wcserr_set(WCSERR_SET(DISERR_BAD_PARAM), + "Unrecognized field name for %s: %s", id, keyp->field); + } + } + + + // Optimization: when the power is integral, it is faster to multiply + // ------------ repeatedly than call pow(). iparm[] is constructed as + // follows: + // I_NPOLY indexing parameters, as above, + // nVar elements record the largest integral power for each variable, + // nVar elements record offsets into dparm for each variable, + // M*nVar flags to signal whether the power is integral, + // M*nVar integral powers. + for (int ivar = 0; ivar < nVar; ivar++) { + // Want at least the first degree power for all variables. + i = iparm[I_MAXPOW] + ivar; + iparm[i] = 1; + } + + for (int ivar = 0; ivar < nVar; ivar++) { + for (m = 0; m < M; m++) { + i = iparm[I_DPOLY] + m*nTparm + 1 + ivar; + double power = dparm[i]; + + // Is it integral? (Positive, negative, or zero.) + int ipow = (int)power; + if (power == (double)ipow) { + // Signal that the power is integral. + i = iparm[I_FLAGS] + m*nVar + ivar; + if (ipow == 0) { + iparm[i] = 3; + } else { + iparm[i] = 1; + } + + // The integral power itself. + i = iparm[I_IPOW] + m*nVar + ivar; + iparm[i] = ipow; + } + + // Record the largest integral power for each variable. + i = iparm[I_MAXPOW] + ivar; + if (iparm[i] < abs(ipow)) { + iparm[i] = abs(ipow); + } + } + } + + // How many of all powers of each variable will there be? + int npow = 0; + for (int ivar = 0; ivar < nVar; ivar++) { + // Offset into dparm. + i = iparm[I_DPOFF] + ivar; + iparm[i] = lendp + npow; + + i = iparm[I_MAXPOW] + ivar; + npow += iparm[i]; + } + + // Expand dparm to store the extra powers. + if (npow) { + lendp += npow; + iparm[I_LENDP] = lendp; + if ((dis->dparm[j] = realloc(dparm, lendp*sizeof(double))) == 0x0) { + return wcserr_set(DIS_ERRMSG(DISERR_MEMORY)); + } + } + + // No specialist de-distortions. + dis->disp2x[j] = dispoly; + dis->disx2p[j] = 0x0; + + // Translate Polynomial to TPD if possible, it's much faster. + // However don't do it if the name was given as "Polynomial*". + if (strcmp(dis->dtype[j], "Polynomial") == 0) { + pol2tpd(j, dis); + } + + return 0; +} + +//---------------------------------------------------------------------------- + +int tpdset(int j, struct disprm *dis) + +{ + static const char *function = "tpdset"; + + if (dis == 0x0) return DISERR_NULL_POINTER; + struct wcserr **err = &(dis->err); + + char id[32]; + sprintf(id, "TPD on axis %d", j+1); + + + // TPD distortion. + if (dis->Nhat[j] < 1 || 2 < dis->Nhat[j]) { + return wcserr_set(WCSERR_SET(DISERR_BAD_PARAM), + "Axis map for %s must contain 1 or 2 entries, not %d", id, + dis->Nhat[j]); + } + + // Find the number of parameters. + int ncoeff[2] = {0, 0}; + int doaux = 0; + int doradial = 0; + struct dpkey *keyp = dis->dp; + for (int idp = 0; idp < dis->ndp; idp++, keyp++) { + if (keyp->j-1 != j) continue; + + char *fp = strchr(keyp->field, '.') + 1; + + if (strncmp(fp, "TPD.", 4) == 0) { + fp += 4; + int idis; + if (strncmp(fp, "FWD.", 4) == 0) { + idis = 0; + + } else if (strncmp(fp, "REV.", 4) == 0) { + // TPD may provide a polynomial approximation for the inverse. + idis = 1; + + } else { + return wcserr_set(WCSERR_SET(DISERR_BAD_PARAM), + "Unrecognized field name for %s: %s", id, keyp->field); + } + + int k; + sscanf(fp+4, "%d", &k); + if (0 <= k && k <= 59) { + if (ncoeff[idis] < k+1) ncoeff[idis] = k+1; + + // Any radial terms? + if (k == 3 || k == 11 || k == 23 || k == 39 || k == 59) { + doradial = 1; + } + + } else { + return wcserr_set(WCSERR_SET(DISERR_BAD_PARAM), + "Invalid parameter number (%d) for %s: %s", k, id, keyp->field); + } + + } else if (strncmp(fp, "AUX.", 4) == 0) { + // Flag usage of auxiliary variables. + doaux = 1; + + } else if (strcmp(fp, "DOCORR") && + strcmp(fp, "NAXES") && + strncmp(fp, "AXIS.", 5) && + strncmp(fp, "OFFSET.", 7) && + strncmp(fp, "SCALE.", 6)) { + return wcserr_set(WCSERR_SET(DISERR_BAD_PARAM), + "Unrecognized field name for %s: %s", id, keyp->field); + } + } + + int (*(distpd[2]))(DISP2X_ARGS) = {0x0, 0x0}; + for (int idis = 0; idis < 2; idis++) { + if (ncoeff[idis] <= 4) { + if (idis) { + // No inverse polynomial. + break; + } + + // First degree. + ncoeff[idis] = 4; + distpd[idis] = tpd1; + } else if (ncoeff[idis] <= 7) { + // Second degree. + ncoeff[idis] = 7; + distpd[idis] = tpd2; + } else if (ncoeff[idis] <= 12) { + // Third degree. + ncoeff[idis] = 12; + distpd[idis] = tpd3; + } else if (ncoeff[idis] <= 17) { + // Fourth degree. + ncoeff[idis] = 17; + distpd[idis] = tpd4; + } else if (ncoeff[idis] <= 24) { + // Fifth degree. + ncoeff[idis] = 24; + distpd[idis] = tpd5; + } else if (ncoeff[idis] <= 31) { + // Sixth degree. + ncoeff[idis] = 31; + distpd[idis] = tpd6; + } else if (ncoeff[idis] <= 40) { + // Seventh degree. + ncoeff[idis] = 40; + distpd[idis] = tpd7; + } else if (ncoeff[idis] <= 49) { + // Eighth degree. + ncoeff[idis] = 49; + distpd[idis] = tpd8; + } else if (ncoeff[idis] <= 60) { + // Ninth degree. + ncoeff[idis] = 60; + distpd[idis] = tpd9; + } else { + return wcserr_set(WCSERR_SET(DISERR_BAD_PARAM), + "Invalid number of parameters (%d) for %s", ncoeff[idis], id); + } + } + + // disx2p() only uses the inverse TPD, if present, to provide a better + // zeroth approximation. + dis->disp2x[j] = distpd[0]; + dis->disx2p[j] = distpd[1]; + + +// These iparm indices are specific to TPD (matching definitions in wcshdr.c). +#define I_TPDNCO 3 // No. of TPD coefficients, forward... +#define I_TPDINV 4 // ...and inverse. +#define I_TPDAUX 5 // True if auxiliary variables are used. +#define I_TPDRAD 6 // True if the radial variable is used. +#define I_NTPD 7 + + // Record indexing parameters. + int niparm = I_NTPD; + if ((dis->iparm[j] = calloc(niparm, sizeof(int))) == 0x0) { + return wcserr_set(DIS_ERRMSG(DISERR_MEMORY)); + } + + int ndparm = (doaux?6:0) + ncoeff[0] + ncoeff[1]; + + // The first three are more widely used. + dis->iparm[j][I_DTYPE] = DIS_TPD; + dis->iparm[j][I_NIPARM] = niparm; + dis->iparm[j][I_NDPARM] = ndparm; + + // Number of TPD coefficients. + dis->iparm[j][I_TPDNCO] = ncoeff[0]; + dis->iparm[j][I_TPDINV] = ncoeff[1]; + + // Flag for presence of auxiliary variables. + dis->iparm[j][I_TPDAUX] = doaux; + + // Flag for presence of radial terms. + dis->iparm[j][I_TPDRAD] = doradial; + + + // Allocate memory for the polynomial coefficients and fill it. + if ((dis->dparm[j] = calloc(ndparm, sizeof(double))) == 0x0) { + return wcserr_set(DIS_ERRMSG(DISERR_MEMORY)); + } + + // Set default auxiliary coefficients. + if (doaux) { + dis->dparm[j][1] = 1.0; + dis->dparm[j][5] = 1.0; + } + + keyp = dis->dp; + for (int idp = 0; idp < dis->ndp; idp++, keyp++) { + if (keyp->j-1 != j) continue; + + char *fp = strchr(keyp->field, '.') + 1; + + if (strncmp(fp, "AUX.", 4) == 0) { + // Auxiliary variables. + fp += 4; + int k; + sscanf(fp, "%d", &k); + if (k < 1 || 2 < k) { + return wcserr_set(WCSERR_SET(DISERR_BAD_PARAM), + "Bad auxiliary variable (%d) for %s: %s", k, id, keyp->field); + } + + if ((fp = strchr(fp, '.')) == 0x0) { + return wcserr_set(WCSERR_SET(DISERR_BAD_PARAM), + "Invalid field name for %s: %s", id, keyp->field); + } + fp++; + + if (strncmp(fp, "COEFF.", 6) != 0) { + return wcserr_set(WCSERR_SET(DISERR_BAD_PARAM), + "Unrecognized field name for %s: %s", id, keyp->field); + } + + fp += 6; + int m; + sscanf(fp, "%d", &m); + if (m < 0 || 2 < m) { + return wcserr_set(WCSERR_SET(DISERR_BAD_PARAM), + "Invalid coefficient number (%d) for %s: %s", m, id, keyp->field); + } + + int idis = 3*(k-1) + m; + dis->dparm[j][idis] = dpkeyd(keyp); + + } else if (strncmp(fp, "TPD.", 4) == 0) { + fp += 4; + int idis = (doaux?6:0); + if (strncmp(fp, "REV.", 4) == 0) { + idis += ncoeff[0]; + } + + int k; + sscanf(fp+4, "%d", &k); + dis->dparm[j][idis+k] = dpkeyd(keyp); + } + } + + return 0; +} + +//---------------------------------------------------------------------------- + +int pol2tpd(int j, struct disprm *dis) + +{ + static const char *function = "pol2tpd"; + + static const int map[][10] = {{ 0, 2, 6, 10, 16, 22, 30, 38, 48, 58}, + { 1, 5, 9, 15, 21, 29, 37, 47, 57, -1}, + { 4, 8, 14, 20, 28, 36, 46, 56, -1, -1}, + { 7, 13, 19, 27, 35, 45, 55, -1, -1, -1}, + {12, 18, 26, 34, 44, 54, -1, -1, -1, -1}, + {17, 25, 33, 43, 53, -1, -1, -1, -1, -1}, + {24, 32, 42, 52, -1, -1, -1, -1, -1, -1}, + {31, 41, 51, -1, -1, -1, -1, -1, -1, -1}, + {40, 50, -1, -1, -1, -1, -1, -1, -1, -1}, + {49, -1, -1, -1, -1, -1, -1, -1, -1, -1}}; + + // Initialize. + if (dis == 0x0) return DISERR_NULL_POINTER; + struct wcserr **err = &(dis->err); + + int *iparm = dis->iparm[j]; + double *dparm = dis->dparm[j]; + + + // Check the number of independent variables, no more than two. + int Nhat = dis->Nhat[j]; + if (2 < Nhat) return -1; + + // Check auxiliaries: only one is allowed... + int K = iparm[I_K]; + if (1 < K) return -1; + if (K) { + // ...and it must be radial. + if (dparm[0] != 0.0) return -1; + if (dparm[1] != 1.0) return -1; + if (dparm[2] != 1.0) return -1; + if (dparm[3] != 0.5) return -1; + if (dparm[4] != 2.0) return -1; + if (dparm[5] != 2.0) return -1; + } + + // Check powers... + int *iflgp = iparm + iparm[I_FLAGS]; + int *ipowp = iparm + iparm[I_IPOW]; + int degree = 0; + for (int m = 0; m < iparm[I_M]; m++) { + int deg = 0; + for (int jhat = 0; jhat < Nhat; jhat++) { + // ...they must be positive integral. + if (*iflgp == 0) return -1; + if (*ipowp < 0) return -1; + deg += *ipowp; + iflgp++; + ipowp++; + } + + // The polynomial degree can't be greater than 9. + if (9 < deg) return -1; + + if (K) { + // Likewise for the radial variable. + if (*iflgp == 0) return -1; + if (*ipowp) { + if (*ipowp < 0) return -1; + if (9 < *ipowp) return -1; + + // Can't mix the radial and other terms. + if (deg) return -1; + + // Can't have even powers of the radial variable. + deg = *ipowp; + if (!(deg%2)) return -1; + } + iflgp++; + ipowp++; + } + + if (degree < deg) degree = deg; + } + + + // OK, it ticks all the boxes. Now translate it. + int ndparm = 0; + if (degree == 1) { + ndparm = 4; + dis->disp2x[j] = tpd1; + } else if (degree == 2) { + ndparm = 7; + dis->disp2x[j] = tpd2; + } else if (degree == 3) { + ndparm = 12; + dis->disp2x[j] = tpd3; + } else if (degree == 4) { + ndparm = 17; + dis->disp2x[j] = tpd4; + } else if (degree == 5) { + ndparm = 24; + dis->disp2x[j] = tpd5; + } else if (degree == 6) { + ndparm = 31; + dis->disp2x[j] = tpd6; + } else if (degree == 7) { + ndparm = 40; + dis->disp2x[j] = tpd7; + } else if (degree == 8) { + ndparm = 49; + dis->disp2x[j] = tpd8; + } else if (degree == 9) { + ndparm = 60; + dis->disp2x[j] = tpd9; + } + + // No specialist de-distortions. + dis->disx2p[j] = 0x0; + + // Record indexing parameters. + int niparm = I_NTPD; + int *tpd_iparm; + if ((tpd_iparm = calloc(niparm, sizeof(int))) == 0x0) { + return wcserr_set(DIS_ERRMSG(DISERR_MEMORY)); + } + + // The first three are more widely used. + tpd_iparm[I_DTYPE] = DIS_TPD; + tpd_iparm[I_NIPARM] = niparm; + tpd_iparm[I_NDPARM] = ndparm; + + // Number of TPD coefficients. + tpd_iparm[I_TPDNCO] = ndparm; + tpd_iparm[I_TPDINV] = 0; + + // No auxiliary variables yet. + tpd_iparm[I_TPDAUX] = 0; + + // Flag for presence of radial terms. + tpd_iparm[I_TPDRAD] = K; + + + // Allocate memory for the polynomial coefficients and fill it. + double *tpd_dparm; + if ((tpd_dparm = calloc(ndparm, sizeof(double))) == 0x0) { + return wcserr_set(DIS_ERRMSG(DISERR_MEMORY)); + } + + ipowp = iparm + iparm[I_IPOW]; + double *dpolp = dparm + iparm[I_DPOLY]; + for (int m = 0; m < iparm[I_M]; m++) { + if (K && ipowp[Nhat]) { + // The radial variable. + switch (ipowp[Nhat]) { + case 1: + tpd_dparm[3] = *dpolp; + break; + case 3: + tpd_dparm[11] = *dpolp; + break; + case 5: + tpd_dparm[23] = *dpolp; + break; + case 7: + tpd_dparm[39] = *dpolp; + break; + case 9: + tpd_dparm[59] = *dpolp; + break; + } + + } else { + // The independent variables. + int p[] = {0, 0}; + for (int jhat = 0; jhat < Nhat; jhat++) { + p[jhat] = ipowp[jhat]; + } + + int n = map[p[0]][p[1]]; + tpd_dparm[n] = *dpolp; + } + + + ipowp += iparm[I_NVAR]; + dpolp += iparm[I_NVAR] + 1; + } + + + // Switch from Polynomial to TPD. + free(iparm); + free(dparm); + dis->iparm[j] = tpd_iparm; + dis->dparm[j] = tpd_dparm; + + return 0; +} + +//---------------------------------------------------------------------------- + +int tpvset(int j, struct disprm *dis) + +{ + static const char *function = "tpvset"; + + // Initialize. + if (dis == 0x0) return DISERR_NULL_POINTER; + struct wcserr **err = &(dis->err); + + // TPV "projection". + char id[32]; + sprintf(id, "TPV on axis %d", j+1); + + // TPV is a sequent distortion, applied to intermediate world coordinates + // (normally used with CDi_ja). It computes corrected coordinates directly. + dis->docorr[j] = 0; + + if (dis->Nhat[j] != 2) { + return wcserr_set(WCSERR_SET(DISERR_BAD_PARAM), + "Axis map for %s must contain 2 entries, not %d", id, dis->Nhat[j]); + } + + // Find the number of parameters. + int ndparm = 0; + int doradial = 0; + struct dpkey *keyp = dis->dp; + for (int idp = 0; idp < dis->ndp; idp++, keyp++) { + if (keyp->j-1 != j) continue; + + char *fp = strchr(keyp->field, '.') + 1; + + if (strncmp(fp, "TPV.", 4) == 0) { + int k; + sscanf(fp+4, "%d", &k); + if (0 <= k && k <= 39) { + if (ndparm < k+1) ndparm = k+1; + + // Any radial terms? + if (k == 3 || k == 11 || k == 23 || k == 39 || k == 59) { + doradial = 1; + } + + } else { + return wcserr_set(WCSERR_SET(DISERR_BAD_PARAM), + "Invalid parameter number (%d) for %s: %s", k, id, keyp->field); + } + + } else if (strcmp(fp, "NAXES") && + strncmp(fp, "AXIS.", 5) && + strncmp(fp, "OFFSET.", 7) && + strncmp(fp, "SCALE.", 6)) { + return wcserr_set(WCSERR_SET(DISERR_BAD_PARAM), + "Unrecognized field name for %s: %s", id, keyp->field); + } + } + + // TPD is going to do the dirty work. + if (ndparm <= 4) { + // First degree. + ndparm = 4; + dis->disp2x[j] = tpd1; + } else if (ndparm <= 7) { + // Second degree. + ndparm = 7; + dis->disp2x[j] = tpd2; + } else if (ndparm <= 12) { + // Third degree. + ndparm = 12; + dis->disp2x[j] = tpd3; + } else if (ndparm <= 17) { + // Fourth degree. + ndparm = 17; + dis->disp2x[j] = tpd4; + } else if (ndparm <= 24) { + // Fifth degree. + ndparm = 24; + dis->disp2x[j] = tpd5; + } else if (ndparm <= 31) { + // Sixth degree. + ndparm = 31; + dis->disp2x[j] = tpd6; + } else if (ndparm <= 40) { + // Seventh degree. + ndparm = 40; + dis->disp2x[j] = tpd7; + } else { + // Could go to ninth degree, but that wouldn't be legit. + return wcserr_set(WCSERR_SET(DISERR_BAD_PARAM), + "Invalid number of parameters (%d) for %s", ndparm, id); + } + + // No specialist de-distortions. + dis->disx2p[j] = 0x0; + + // Record indexing parameters. + int niparm = I_NTPD; + if ((dis->iparm[j] = calloc(niparm, sizeof(int))) == 0x0) { + return wcserr_set(DIS_ERRMSG(DISERR_MEMORY)); + } + + // The first three are more widely used. + dis->iparm[j][I_DTYPE] = DIS_TPD; + dis->iparm[j][I_NIPARM] = niparm; + dis->iparm[j][I_NDPARM] = ndparm; + + // Number of TPD coefficients. + dis->iparm[j][I_TPDNCO] = ndparm; + dis->iparm[j][I_TPDINV] = 0; + + // TPV never needs auxiliary variables. + dis->iparm[j][I_TPDAUX] = 0; + + // Flag for presence of radial terms. + dis->iparm[j][I_TPDRAD] = doradial; + + + // Allocate memory for the polynomial coefficients and fill it. + if ((dis->dparm[j] = calloc(ndparm, sizeof(double))) == 0x0) { + return wcserr_set(DIS_ERRMSG(DISERR_MEMORY)); + } + + keyp = dis->dp; + for (int idp = 0; idp < dis->ndp; idp++, keyp++) { + if (keyp->j-1 != j) continue; + + char *fp = strchr(keyp->field, '.') + 1; + + // One-to-one correspondence between TPV and TPD coefficients. + if (strncmp(fp, "TPV.", 4) == 0) { + int k; + sscanf(fp+4, "%d", &k); + dis->dparm[j][k] = dpkeyd(keyp); + } + } + + return 0; +} + +//---------------------------------------------------------------------------- + +int sipset(int j, struct disprm *dis) + +{ + static const char *function = "sipset"; + + static const int map[][10] = {{ 0, 2, 6, 10, 16, 22, 30, 38, 48, 58}, + { 1, 5, 9, 15, 21, 29, 37, 47, 57, -1}, + { 4, 8, 14, 20, 28, 36, 46, 56, -1, -1}, + { 7, 13, 19, 27, 35, 45, 55, -1, -1, -1}, + {12, 18, 26, 34, 44, 54, -1, -1, -1, -1}, + {17, 25, 33, 43, 53, -1, -1, -1, -1, -1}, + {24, 32, 42, 52, -1, -1, -1, -1, -1, -1}, + {31, 41, 51, -1, -1, -1, -1, -1, -1, -1}, + {40, 50, -1, -1, -1, -1, -1, -1, -1, -1}, + {49, -1, -1, -1, -1, -1, -1, -1, -1, -1}}; + + // Initialize. + if (dis == 0x0) return DISERR_NULL_POINTER; + struct wcserr **err = &(dis->err); + + // Simple Imaging Polynomial. + char id[32]; + sprintf(id, "SIP on axis %d", j+1); + + + // SIP is a prior distortion that computes an additive correction. + dis->docorr[j] = 1; + + if (dis->Nhat[j] != 2) { + return wcserr_set(WCSERR_SET(DISERR_BAD_PARAM), + "Axis map for %s must contain 2 entries, not %d", id, dis->Nhat[j]); + } + + // Find the polynomial degree, at least 1 for the forward function. + int degree[2] = {1, -1}; + struct dpkey *keyp = dis->dp; + for (int idp = 0; idp < dis->ndp; idp++, keyp++) { + if (keyp->j-1 != j) continue; + + char *fp = strchr(keyp->field, '.') + 1; + + if (strncmp(fp, "SIP.", 4) == 0) { + fp += 4; + int idis; + if (strncmp(fp, "FWD.", 4) == 0) { + idis = 0; + + } else if (strncmp(fp, "REV.", 4) == 0) { + // SIP uses a polynomial approximation for the inverse. + idis = 1; + + } else { + return wcserr_set(WCSERR_SET(DISERR_BAD_PARAM), + "Unrecognized field name for %s: %s", id, keyp->field); + } + + fp += 4; + int p, q; + sscanf(fp, "%d_%d", &p, &q); + int deg = p + q; + if (p < 0 || 9 < p || q < 0 || 9 < q || 9 < deg) { + return wcserr_set(WCSERR_SET(DISERR_BAD_PARAM), + "Invalid powers (%d, %d) for %s: %s", p, q, id, keyp->field); + } + + if (degree[idis] < deg) degree[idis] = deg; + + } else if (strcmp(fp, "NAXES") && + strncmp(fp, "AXIS.", 5) && + strncmp(fp, "OFFSET.", 7) && + strncmp(fp, "SCALE.", 6)) { + return wcserr_set(WCSERR_SET(DISERR_BAD_PARAM), + "Unrecognized field name for %s: %s", id, keyp->field); + } + } + + if (degree[1] == 0 ) degree[1] = 1; + + // TPD is going to do the dirty work. + int (*(distpd[2]))(DISP2X_ARGS) = {0x0, 0x0}, ncoeff[2]; + for (int idis = 0; idis < 2; idis++) { + ncoeff[idis] = 0; + if (degree[idis] == 1) { + ncoeff[idis] = 4; + distpd[idis] = tpd1; + } else if (degree[idis] == 2) { + ncoeff[idis] = 7; + distpd[idis] = tpd2; + } else if (degree[idis] == 3) { + ncoeff[idis] = 12; + distpd[idis] = tpd3; + } else if (degree[idis] == 4) { + ncoeff[idis] = 17; + distpd[idis] = tpd4; + } else if (degree[idis] == 5) { + ncoeff[idis] = 24; + distpd[idis] = tpd5; + } else if (degree[idis] == 6) { + ncoeff[idis] = 31; + distpd[idis] = tpd6; + } else if (degree[idis] == 7) { + ncoeff[idis] = 40; + distpd[idis] = tpd7; + } else if (degree[idis] == 8) { + ncoeff[idis] = 49; + distpd[idis] = tpd8; + } else if (degree[idis] == 9) { + ncoeff[idis] = 60; + distpd[idis] = tpd9; + } + } + + // SIP uses a polynomial approximation to the inverse. It's not very + // accurate but may provide disx2p() with a better zeroth approximation. + dis->disp2x[j] = distpd[0]; + dis->disx2p[j] = distpd[1]; + + + // Record indexing parameters. + int niparm = I_NTPD; + if ((dis->iparm[j] = calloc(niparm, sizeof(int))) == 0x0) { + return wcserr_set(DIS_ERRMSG(DISERR_MEMORY)); + } + + int ndparm = ncoeff[0] + ncoeff[1]; + + // The first three are more widely used. + dis->iparm[j][I_DTYPE] = DIS_TPD; + dis->iparm[j][I_NIPARM] = niparm; + dis->iparm[j][I_NDPARM] = ndparm; + + // Number of TPD coefficients. + dis->iparm[j][I_TPDNCO] = ncoeff[0]; + dis->iparm[j][I_TPDINV] = ncoeff[1]; + + // SIP never needs auxiliary variables. + dis->iparm[j][I_TPDAUX] = 0; + + // SIP never needs the radial terms. + dis->iparm[j][I_TPDRAD] = 0; + + + // Allocate memory for the polynomial coefficients and fill it. + if ((dis->dparm[j] = calloc(ndparm, sizeof(double))) == 0x0) { + return wcserr_set(DIS_ERRMSG(DISERR_MEMORY)); + } + + keyp = dis->dp; + for (int idp = 0; idp < dis->ndp; idp++, keyp++) { + if (keyp->j-1 != j) continue; + + char *fp = strchr(keyp->field, '.') + 1; + + if (strncmp(fp, "SIP.", 4) == 0) { + fp += 4; + int idis; + if (strncmp(fp, "FWD.", 4) == 0) { + idis = 0; + } else { + idis = ncoeff[0]; + } + + int p, q; + sscanf(fp+4, "%d_%d", &p, &q); + + // Map to TPD coefficient number. + idis += map[p][q]; + + dis->dparm[j][idis] = dpkeyd(keyp); + } + } + + + return 0; +} + +//---------------------------------------------------------------------------- + +int dssset(int j, struct disprm *dis) + +{ + static const char *function = "dssset"; + + // Initialize. + if (dis == 0x0) return DISERR_NULL_POINTER; + struct wcserr **err = &(dis->err); + + // Digitized Sky Survey. + char id[32]; + sprintf(id, "DSS on axis %d", j+1); + + + // DSS is translated into a sequent distortion, applied to intermediate + // pixel coordinates. It computes corrected coordinates directly. + dis->docorr[j] = 0; + + if (dis->Nhat[j] != 2) { + return wcserr_set(WCSERR_SET(DISERR_BAD_PARAM), + "Axis map for %s must contain 2 entries, not %d", id, dis->Nhat[j]); + } + + // Safe to assume the polynomial degree is 5 (or less). + int ncoeff = 24; + dis->disp2x[j] = tpd5; + + // No specialist de-distortions. + dis->disx2p[j] = 0x0; + + + // Record indexing parameters. + int niparm = I_NTPD; + if ((dis->iparm[j] = calloc(niparm, sizeof(int))) == 0x0) { + return wcserr_set(DIS_ERRMSG(DISERR_MEMORY)); + } + + int ndparm = 6 + ncoeff; + + // The first three are more widely used. + dis->iparm[j][I_DTYPE] = DIS_TPD; + dis->iparm[j][I_NIPARM] = niparm; + dis->iparm[j][I_NDPARM] = ndparm; + + // Number of TPD coefficients. + dis->iparm[j][I_TPDNCO] = ncoeff; + dis->iparm[j][I_TPDINV] = 0; + + // DSS always needs auxiliary variables. + dis->iparm[j][I_TPDAUX] = 1; + + // DSS never needs the radial terms. + dis->iparm[j][I_TPDRAD] = 0; + + + // Allocate memory for the polynomial coefficients and fill it. + if ((dis->dparm[j] = calloc(ndparm, sizeof(double))) == 0x0) { + return wcserr_set(DIS_ERRMSG(DISERR_MEMORY)); + } + + // This translation follows WCS Paper IV, Sect. 5.2 using the same + // variable names. Find A1, A2, A3, B1, B2, and B3. + double A1, A2, A3, B1, B2, B3; + A1 = A2 = A3 = 0.0; + B1 = B2 = B3 = 0.0; + struct dpkey *keyp = dis->dp; + for (int idp = 0; idp < dis->ndp; idp++, keyp++) { + char *fp = strchr(keyp->field, '.') + 1; + if (strncmp(fp, "DSS.AMD.", 8) == 0) { + fp += 8; + int m; + sscanf(fp, "%d", &m); + + if (m == 1) { + if (keyp->j == 1) { + A1 = dpkeyd(keyp); + } else { + B1 = dpkeyd(keyp); + } + } else if (m == 2) { + if (keyp->j == 1) { + A2 = dpkeyd(keyp); + } else { + B2 = dpkeyd(keyp); + } + } else if (m == 3) { + if (keyp->j == 1) { + A3 = dpkeyd(keyp); + } else { + B3 = dpkeyd(keyp); + } + } + } + } + + double X0 = (A2*B3 - A3*B1) / (A1*B1 - A2*B2); + double Y0 = (A3*B2 - A1*B3) / (A1*B1 - A2*B2); + + double S = sqrt(fabs(A1*B1 - A2*B2)); + if (S == 0.0) { + return wcserr_set(WCSERR_SET(DISERR_BAD_PARAM), + "Coefficient scale for %s is zero.", id); + } + + // Coefficients for the auxiliary variables. + double *dparm = dis->dparm[j]; + if (j == 0) { + dparm[0] = X0; + dparm[1] = -B1/S; + dparm[2] = -A2/S; + dparm[3] = Y0; + dparm[4] = B2/S; + dparm[5] = A1/S; + + // Change the sign of S for scaling the A coefficients. + S *= -1.0; + + } else { + dparm[0] = Y0; + dparm[1] = B2/S; + dparm[2] = A1/S; + dparm[3] = X0; + dparm[4] = -B1/S; + dparm[5] = -A2/S; + } + + // Translate DSS coefficients to TPD. + dparm += 6; + int degree = 3; + keyp = dis->dp; + for (int idp = 0; idp < dis->ndp; idp++, keyp++) { + if (keyp->j-1 != j) continue; + + char *fp = strchr(keyp->field, '.') + 1; + + if (strncmp(fp, "DSS.AMD.", 8) == 0) { + // Skip zero coefficients. + double coeff = dpkeyd(keyp); + if (coeff == 0.0) continue; + + fp += 8; + int m; + sscanf(fp, "%d", &m); + + // Apply the coefficient scale factor. + coeff /= S; + + if (m == 1) { + dparm[1] = coeff; + } else if (m == 2) { + dparm[2] = coeff; + } else if (m == 3) { + dparm[0] = coeff; + } else if (m == 4) { + dparm[4] += coeff; + } else if (m == 5) { + dparm[5] = coeff; + } else if (m == 6) { + dparm[6] += coeff; + } else if (m == 7) { + dparm[4] += coeff; + dparm[6] += coeff; + } else if (m == 8) { + dparm[7] += coeff; + } else if (m == 9) { + dparm[8] = coeff; + } else if (m == 10) { + dparm[9] += coeff; + } else if (m == 11) { + dparm[10] = coeff; + } else if (m == 12) { + dparm[7] += coeff; + dparm[9] += coeff; + } else if (m == 13) { + dparm[17] = coeff; + dparm[19] = coeff * 2.0; + dparm[21] = coeff; + degree = 5; + } else if (coeff != 0.0) { + return wcserr_set(WCSERR_SET(DISERR_BAD_PARAM), + "Invalid parameter for %s: %s", m, id, keyp->field); + } + + } else if (strcmp(fp, "NAXES") && + strncmp(fp, "AXIS.", 5) && + strncmp(fp, "OFFSET.", 7) && + strncmp(fp, "SCALE.", 6)) { + return wcserr_set(WCSERR_SET(DISERR_BAD_PARAM), + "Unrecognized field name for %s: %s", id, keyp->field); + } + } + + // The DSS polynomial doesn't have 4th degree terms, and the 5th degree + // coefficient is often zero. + if (degree == 3) { + dis->iparm[j][I_TPDNCO] = 12; + dis->disp2x[j] = tpd3; + } + + return 0; +} + +//---------------------------------------------------------------------------- + +#define CHEBYSHEV 1 +#define LEGENDRE 2 +#define MONOMIAL 3 + +int watset(int j, struct disprm *dis) + +{ + static const char *function = "watset"; + + static const int map[][10] = {{ 0, 2, 6, 10, 16, 22, 30, 38, 48, 58}, + { 1, 5, 9, 15, 21, 29, 37, 47, 57, -1}, + { 4, 8, 14, 20, 28, 36, 46, 56, -1, -1}, + { 7, 13, 19, 27, 35, 45, 55, -1, -1, -1}, + {12, 18, 26, 34, 44, 54, -1, -1, -1, -1}, + {17, 25, 33, 43, 53, -1, -1, -1, -1, -1}, + {24, 32, 42, 52, -1, -1, -1, -1, -1, -1}, + {31, 41, 51, -1, -1, -1, -1, -1, -1, -1}, + {40, 50, -1, -1, -1, -1, -1, -1, -1, -1}, + {49, -1, -1, -1, -1, -1, -1, -1, -1, -1}}; + + // Initialize. + if (dis == 0x0) return DISERR_NULL_POINTER; + struct wcserr **err = &(dis->err); + + // WAT (TNX or ZPX) Polynomial. + char id[32]; + sprintf(id, "WAT (%s) on axis %d", dis->dtype[0]+4, j+1); + + + // WAT is a sequent distortion, applied to intermediate world coordinates + // (normally used with CDi_ja). It computes an additive correction. + dis->docorr[j] = 1; + + if (dis->Nhat[j] != 2) { + return wcserr_set(WCSERR_SET(DISERR_BAD_PARAM), + "Axis map for %s must contain 2 entries, not %d", id, dis->Nhat[j]); + } + + // Find the polynomial degree (at least 1), kind, and domain. + int degree = 1; + int kind = 0; + double xmin = 0.0; + double xmax = 0.0; + double ymin = 0.0; + double ymax = 0.0; + struct dpkey *keyp = dis->dp; + for (int idp = 0; idp < dis->ndp; idp++, keyp++) { + if (keyp->j-1 != j) continue; + + char *fp = strchr(keyp->field, '.') + 1; + + if (strncmp(fp, "WAT.", 4) == 0) { + fp += 4; + if (strncmp(fp, "CHBY.", 5) == 0 || + strncmp(fp, "LEGR.", 5) == 0 || + strncmp(fp, "MONO.", 5) == 0) { + + fp += 5; + int m, n; + sscanf(fp, "%d_%d", &m, &n); + int deg = m + n; + if (m < 0 || 9 < m || n < 0 || 9 < n || 9 < deg) { + return wcserr_set(WCSERR_SET(DISERR_BAD_PARAM), + "Invalid powers (%d, %d) for %s: %s", m, n, id, keyp->field); + } + + if (degree < deg) degree = deg; + + } else if (strcmp(fp, "POLY") == 0) { + kind = dpkeyi(keyp); + + } else if (strcmp(fp, "XMIN") == 0) { + xmin = dpkeyd(keyp); + + } else if (strcmp(fp, "XMAX") == 0) { + xmax = dpkeyd(keyp); + + } else if (strcmp(fp, "YMIN") == 0) { + ymin = dpkeyd(keyp); + + } else if (strcmp(fp, "YMAX") == 0) { + ymax = dpkeyd(keyp); + } + + } else if (strcmp(fp, "NAXES") && + strncmp(fp, "AXIS.", 5) && + strncmp(fp, "OFFSET.", 7) && + strncmp(fp, "SCALE.", 6)) { + return wcserr_set(WCSERR_SET(DISERR_BAD_PARAM), + "Unrecognized field name for %s: %s", id, keyp->field); + } + } + + int doaux = (kind == 1 || kind == 2); + + // TPD is going to do the dirty work. + int ncoeff = 0; + if (degree == 1) { + // First degree. + ncoeff = 4; + dis->disp2x[j] = tpd1; + } else if (degree == 2) { + // Second degree. + ncoeff = 7; + dis->disp2x[j] = tpd2; + } else if (degree == 3) { + // Third degree. + ncoeff = 12; + dis->disp2x[j] = tpd3; + } else if (degree == 4) { + // Fourth degree. + ncoeff = 17; + dis->disp2x[j] = tpd4; + } else if (degree == 5) { + // Fifth degree. + ncoeff = 24; + dis->disp2x[j] = tpd5; + } else if (degree == 6) { + // Sixth degree. + ncoeff = 31; + dis->disp2x[j] = tpd6; + } else if (degree == 7) { + // Seventh degree. + ncoeff = 40; + dis->disp2x[j] = tpd7; + } else if (degree == 8) { + // Eighth degree. + ncoeff = 49; + dis->disp2x[j] = tpd8; + } else if (degree == 9) { + // Ninth degree. + ncoeff = 60; + dis->disp2x[j] = tpd9; + } + + // No specialist de-distortions. + dis->disx2p[j] = 0x0; + + + // Record indexing parameters. + int niparm = I_NTPD; + if ((dis->iparm[j] = calloc(niparm, sizeof(int))) == 0x0) { + return wcserr_set(DIS_ERRMSG(DISERR_MEMORY)); + } + + int *iparm = dis->iparm[j]; + + int ndparm = 6 + ncoeff; + + // The first three are more widely used. + iparm[I_DTYPE] = DIS_TPD; + iparm[I_NIPARM] = niparm; + iparm[I_NDPARM] = ndparm; + + // Number of TPD coefficients. + iparm[I_TPDNCO] = ncoeff; + iparm[I_TPDINV] = 0; + + // The Chebyshev and Legendre polynomials use auxiliary variables. + iparm[I_TPDAUX] = doaux; + + // WAT never needs the radial terms. + iparm[I_TPDRAD] = 0; + + + // Allocate memory for the polynomial coefficients and fill it. + if ((dis->dparm[j] = calloc(ndparm, sizeof(double))) == 0x0) { + return wcserr_set(DIS_ERRMSG(DISERR_MEMORY)); + } + + double *dparm = dis->dparm[j]; + + + // Coefficients for the auxiliary variables. + if (doaux) { + double x0 = (xmax + xmin)/2.0; + double dx = (xmax - xmin)/2.0; + if (dx == 0.0) { + return wcserr_set(WCSERR_SET(DISERR_BAD_PARAM), + "X-span for %s is zero", id); + } + + dparm[0] = -x0/dx; + dparm[1] = 1.0/dx; + dparm[2] = 0.0; + + double y0 = (ymax + ymin)/2.0; + double dy = (ymax - ymin)/2.0; + if (dy == 0.0) { + return wcserr_set(WCSERR_SET(DISERR_BAD_PARAM), + "Y-span for %s is zero", id); + } + + dparm[3] = -y0/dy; + dparm[4] = 0.0; + dparm[5] = 1.0/dy; + + dparm += 6; + } + + + // Unpack the polynomial coefficients. + keyp = dis->dp; + for (int idp = 0; idp < dis->ndp; idp++, keyp++) { + if (keyp->j-1 != j) continue; + + char *fp = strchr(keyp->field, '.') + 1; + + if ((kind == CHEBYSHEV && strncmp(fp, "WAT.CHBY.", 9) == 0) || + (kind == LEGENDRE && strncmp(fp, "WAT.LEGR.", 9) == 0) || + (kind == MONOMIAL && strncmp(fp, "WAT.MONO.", 9) == 0)) { + fp += 9; + + int m, n; + sscanf(fp, "%d_%d", &m, &n); + + if (kind == MONOMIAL) { + // Monomial coefficient, maps simply to TPD coefficient number. + int idis = map[m][n]; + dparm[idis] = dpkeyd(keyp); + + } else { + // Coefficient of the product of two Chebyshev or two Legendre + // polynomials. Find the corresponding monomial coefficients. + double coeff = dpkeyd(keyp); + + double coeffm[10], coeffn[10]; + cheleg(kind, m, n, coeffm, coeffn); + for (int im = 0; im <= m; im++) { + if (coeffm[im] == 0.0) continue; + + for (int in = 0; in <= n; in++) { + if (coeffn[in] == 0.0) continue; + + int idis = map[im][in]; + dparm[idis] += coeff*coeffm[im]*coeffn[in]; + } + } + } + } + } + + return 0; +} + +//---------------------------------------------------------------------------- +// Compute the coefficients of Chebyshev or Legendre polynomials of degree +// m and n. + +int cheleg(int kind, int m, int n, double coeffm[], double coeffn[]) + +{ + int N = (m > n) ? m : n; + + // Allocate work arrays. + double *coeff[3]; + coeff[0] = calloc(3*(N+1), sizeof(double)); + coeff[1] = coeff[0] + (N+1); + coeff[2] = coeff[1] + (N+1); + + for (int j = 0; j <= N; j++) { + int j0 = j%3; + + if (j == 0) { + coeff[0][0] = 1.0; + + } else if (j == 1) { + coeff[1][1] = 1.0; + + } else { + // Cyclic buffer indices. + int j1 = (j-1)%3; + int j2 = (j-2)%3; + + memset(coeff[j0], 0, (N+1)*sizeof(double)); + + double d = (double)j; + for (int k = 0; k < N; k++) { + if (kind == CHEBYSHEV) { + coeff[j0][k+1] = 2.0 * coeff[j1][k]; + coeff[j0][k] -= coeff[j2][k]; + } else if (kind == LEGENDRE) { + coeff[j0][k+1] = ((2.0*d - 1.0) * coeff[j1][k]) / d; + coeff[j0][k] -= ((d - 1.0) * coeff[j2][k]) / d; + } + } + } + + if (j == m) memcpy(coeffm, coeff[j0], (m+1)*sizeof(double)); + if (j == n) memcpy(coeffn, coeff[j0], (n+1)*sizeof(double)); + } + + free(coeff[0]); + + return 0; +} + +//---------------------------------------------------------------------------- + +int dispoly( + int dummy, + const int iparm[], + const double dparm[], + int Nhat, + const double rawcrd[], + double *discrd) + +{ + // Avert nuisance compiler warnings about unused parameters. + (void)dummy; + + // Check for zeroes. + for (int jhat = 0; jhat < Nhat; jhat++) { + if (rawcrd[jhat] == 0.0) { + *discrd = 0.0; + return 0; + } + } + + // Working memory for auxiliaries &c. was allocated at the end of p[]. + double *aux = (double *)(dparm + iparm[I_DAUX]); + + // Compute the auxiliary variables. + for (int k = 0; k < iparm[I_K]; k++) { + const double *cptr = dparm + k*iparm[I_NKPARM]; + const double *pptr = cptr + (1+Nhat); + + aux[k] = *(cptr++); + double auxp0 = *(pptr++); + + for (int jhat = 0; jhat < Nhat; jhat++) { + aux[k] += *(cptr++)*pow(rawcrd[jhat], *(pptr++)); + } + + aux[k] = pow(aux[k], auxp0); + + // Check for zeroes. + if (aux[k] == 0.0) { + *discrd = 0.0; + return 0; + } + } + + + // Compute all required integral powers of the variables. + const int *imaxpow = iparm + iparm[I_MAXPOW]; + double *dvarpow = (double *)(dparm + iparm[I_DVPOW]); + + const int *imaxp = imaxpow; + double *dpowp = dvarpow; + for (int jhat = 0; jhat < Nhat; jhat++, imaxp++) { + double var = 1.0; + for (int ip = 0; ip < *imaxp; ip++, dpowp++) { + var *= rawcrd[jhat]; + *dpowp = var; + } + } + + for (int k = 0; k < iparm[I_K]; k++, imaxp++) { + double var = 1.0; + for (int ip = 0; ip < *imaxp; ip++, dpowp++) { + var *= aux[k]; + *dpowp = var; + } + } + + // Loop for each term of the polynomial. + *discrd = 0.0; + const int *iflgp = iparm + iparm[I_FLAGS]; + const int *ipowp = iparm + iparm[I_IPOW]; + const double *dpolp = dparm + iparm[I_DPOLY]; + for (int m = 0; m < iparm[I_M]; m++) { + double term = *(dpolp++); + + // Loop over all variables. + imaxp = imaxpow; + dpowp = dvarpow - 1; + for (int ivar = 0; ivar < iparm[I_NVAR]; ivar++) { + if (*iflgp & 2) { + // Nothing (zero power). + + } else if (*iflgp) { + // Integral power. + if (*ipowp < 0) { + // Negative. + term /= dpowp[*ipowp]; + } else { + // Positive. + term *= dpowp[*ipowp]; + } + + } else { + // Fractional power. + term *= pow(dpowp[0], *dpolp); + } + + iflgp++; + ipowp++; + dpolp++; + + dpowp += *imaxp; + imaxp++; + } + + *discrd += term; + } + + return 0; +} + +//---------------------------------------------------------------------------- + +int tpd1( + int inverse, + const int i[], + const double p[], + int Nhat, + const double rawcrd[], + double *discrd) + +{ + if (i[I_TPDNCO+inverse] != 4 || 2 < Nhat) { + return 1; + } + + double r, s; + double u = rawcrd[0]; + double v = rawcrd[1]; + + // Auxiliary variables? + if (i[I_TPDAUX]) { + r = p[0] + p[1]*u + p[2]*v; + v = p[3] + p[4]*u + p[5]*v; + u = r; + p += 6; + } + + if (inverse) p += i[I_TPDNCO]; + + // First degree. + *discrd = p[0] + u*p[1]; + + if (Nhat == 1) return 0; + + *discrd += v*p[2]; + + // Radial terms? + if (i[I_TPDRAD]) { + s = u*u + v*v; + r = sqrt(s); + + *discrd += r*p[3]; + } + + return 0; +} + +//---------------------------------------------------------------------------- + +int tpd2( + int inverse, + const int i[], + const double p[], + int Nhat, + const double rawcrd[], + double *discrd) + +{ + if (i[I_TPDNCO+inverse] != 7 || 2 < Nhat) { + return 1; + } + + double r, s; + double u = rawcrd[0]; + double v = rawcrd[1]; + + // Auxiliary variables? + if (i[I_TPDAUX]) { + r = p[0] + p[1]*u + p[2]*v; + v = p[3] + p[4]*u + p[5]*v; + u = r; + p += 6; + } + + if (inverse) p += i[I_TPDNCO]; + + // Second degree. + *discrd = p[0] + u*(p[1] + u*(p[4])); + + if (Nhat == 1) return 0; + + *discrd += + v*(p[2] + v*(p[6])) + + u*(p[5])*v; + + // Radial terms? + if (i[I_TPDRAD]) { + s = u*u + v*v; + r = sqrt(s); + + *discrd += r*p[3]; + } + + return 0; +} + +//---------------------------------------------------------------------------- + +int tpd3( + int inverse, + const int i[], + const double p[], + int Nhat, + const double rawcrd[], + double *discrd) + +{ + if (i[I_TPDNCO+inverse] != 12 || 2 < Nhat) { + return 1; + } + + double r, s; + double u = rawcrd[0]; + double v = rawcrd[1]; + + // Auxiliary variables? + if (i[I_TPDAUX]) { + r = p[0] + p[1]*u + p[2]*v; + v = p[3] + p[4]*u + p[5]*v; + u = r; + p += 6; + } + + if (inverse) p += i[I_TPDNCO]; + + // Third degree. + *discrd = p[0] + u*(p[1] + u*(p[4] + u*(p[7]))); + + if (Nhat == 1) return 0; + + *discrd += + v*(p[2] + v*(p[6] + v*(p[10]))) + + u*(p[5] + v*(p[9]) + + u*(p[8]))*v; + + // Radial terms? + if (i[I_TPDRAD]) { + s = u*u + v*v; + r = sqrt(s); + + *discrd += r*(p[3] + s*(p[11])); + } + + return 0; +} + +//---------------------------------------------------------------------------- + +int tpd4( + int inverse, + const int i[], + const double p[], + int Nhat, + const double rawcrd[], + double *discrd) + +{ + if (i[I_TPDNCO+inverse] != 17 || 2 < Nhat) { + return 1; + } + + double r, s; + double u = rawcrd[0]; + double v = rawcrd[1]; + + // Auxiliary variables? + if (i[I_TPDAUX]) { + r = p[0] + p[1]*u + p[2]*v; + v = p[3] + p[4]*u + p[5]*v; + u = r; + p += 6; + } + + if (inverse) p += i[I_TPDNCO]; + + // Fourth degree. + *discrd = p[0] + u*(p[1] + u*(p[4] + u*(p[7] + u*(p[12])))); + + if (Nhat == 1) return 0; + + *discrd += + v*(p[2] + v*(p[6] + v*(p[10] + v*(p[16])))) + + u*(p[5] + v*(p[9] + v*(p[15])) + + u*(p[8] + v*(p[14]) + + u*(p[13])))*v; + + // Radial terms? + if (i[I_TPDRAD]) { + s = u*u + v*v; + r = sqrt(s); + + *discrd += r*(p[3] + s*(p[11])); + } + + return 0; +} + +//---------------------------------------------------------------------------- + +int tpd5( + int inverse, + const int i[], + const double p[], + int Nhat, + const double rawcrd[], + double *discrd) + +{ + if (i[I_TPDNCO+inverse] != 24 || 2 < Nhat) { + return 1; + } + + double r, s; + double u = rawcrd[0]; + double v = rawcrd[1]; + + // Auxiliary variables? + if (i[I_TPDAUX]) { + r = p[0] + p[1]*u + p[2]*v; + v = p[3] + p[4]*u + p[5]*v; + u = r; + p += 6; + } + + if (inverse) p += i[I_TPDNCO]; + + // Fifth degree. + *discrd = p[0] + u*(p[1] + u*(p[4] + u*(p[7] + u*(p[12] + u*(p[17]))))); + + if (Nhat == 1) return 0; + + *discrd += + v*(p[2] + v*(p[6] + v*(p[10] + v*(p[16] + v*(p[22]))))) + + u*(p[5] + v*(p[9] + v*(p[15] + v*(p[21]))) + + u*(p[8] + v*(p[14] + v*(p[20])) + + u*(p[13] + v*(p[19]) + + u*(p[18]))))*v; + + // Radial terms? + if (i[I_TPDRAD]) { + s = u*u + v*v; + r = sqrt(s); + + *discrd += r*(p[3] + s*(p[11] + s*(p[23]))); + } + + return 0; +} + +//---------------------------------------------------------------------------- + +int tpd6( + int inverse, + const int i[], + const double p[], + int Nhat, + const double rawcrd[], + double *discrd) + +{ + if (i[I_TPDNCO+inverse] != 31 || 2 < Nhat) { + return 1; + } + + double r, s; + double u = rawcrd[0]; + double v = rawcrd[1]; + + // Auxiliary variables? + if (i[I_TPDAUX]) { + r = p[0] + p[1]*u + p[2]*v; + v = p[3] + p[4]*u + p[5]*v; + u = r; + p += 6; + } + + if (inverse) p += i[I_TPDNCO]; + + // Sixth degree. + *discrd = p[0] + u*(p[1] + u*(p[4] + u*(p[7] + u*(p[12] + u*(p[17] + u*(p[24])))))); + + if (Nhat == 1) return 0; + + *discrd += + v*(p[2] + v*(p[6] + v*(p[10] + v*(p[16] + v*(p[22] + v*(p[30])))))) + + u*(p[5] + v*(p[9] + v*(p[15] + v*(p[21] + v*(p[29])))) + + u*(p[8] + v*(p[14] + v*(p[20] + v*(p[28]))) + + u*(p[13] + v*(p[19] + v*(p[27])) + + u*(p[18] + v*(p[26]) + + u*(p[25])))))*v; + + // Radial terms? + if (i[I_TPDRAD]) { + s = u*u + v*v; + r = sqrt(s); + + *discrd += r*(p[3] + s*(p[11] + s*(p[23]))); + } + + return 0; +} + +//---------------------------------------------------------------------------- + +int tpd7( + int inverse, + const int i[], + const double p[], + int Nhat, + const double rawcrd[], + double *discrd) + +{ + if (i[I_TPDNCO+inverse] != 40 || 2 < Nhat) { + return 1; + } + + double r, s; + double u = rawcrd[0]; + double v = rawcrd[1]; + + // Auxiliary variables? + if (i[I_TPDAUX]) { + r = p[0] + p[1]*u + p[2]*v; + v = p[3] + p[4]*u + p[5]*v; + u = r; + p += 6; + } + + if (inverse) p += i[I_TPDNCO]; + + // Seventh degree. + *discrd = p[0] + u*(p[1] + u*(p[4] + u*(p[7] + u*(p[12] + u*(p[17] + u*(p[24] + u*(p[31]))))))); + + if (Nhat == 1) return 0; + + *discrd += + v*(p[2] + v*(p[6] + v*(p[10] + v*(p[16] + v*(p[22] + v*(p[30] + v*(p[38]))))))) + + u*(p[5] + v*(p[9] + v*(p[15] + v*(p[21] + v*(p[29] + v*(p[37]))))) + + u*(p[8] + v*(p[14] + v*(p[20] + v*(p[28] + v*(p[36])))) + + u*(p[13] + v*(p[19] + v*(p[27] + v*(p[35]))) + + u*(p[18] + v*(p[26] + v*(p[34])) + + u*(p[25] + v*(p[33]) + + u*(p[32]))))))*v; + + // Radial terms? + if (i[I_TPDRAD]) { + s = u*u + v*v; + r = sqrt(s); + + *discrd += r*(p[3] + s*(p[11] + s*(p[23] + s*(p[39])))); + } + + return 0; +} + +//---------------------------------------------------------------------------- + +int tpd8( + int inverse, + const int i[], + const double p[], + int Nhat, + const double rawcrd[], + double *discrd) + +{ + if (i[I_TPDNCO+inverse] != 49 || 2 < Nhat) { + return 1; + } + + double r, s; + double u = rawcrd[0]; + double v = rawcrd[1]; + + // Auxiliary variables? + if (i[I_TPDAUX]) { + r = p[0] + p[1]*u + p[2]*v; + v = p[3] + p[4]*u + p[5]*v; + u = r; + p += 6; + } + + if (inverse) p += i[I_TPDNCO]; + + // Eighth degree. + *discrd = p[0] + u*(p[1] + u*(p[4] + u*(p[7] + u*(p[12] + u*(p[17] + u*(p[24] + u*(p[31] + u*(p[40])))))))); + + if (Nhat == 1) return 0; + + *discrd += + v*(p[2] + v*(p[6] + v*(p[10] + v*(p[16] + v*(p[22] + v*(p[30] + v*(p[38] + v*(p[48])))))))) + + u*(p[5] + v*(p[9] + v*(p[15] + v*(p[21] + v*(p[29] + v*(p[37] + v*(p[47])))))) + + u*(p[8] + v*(p[14] + v*(p[20] + v*(p[28] + v*(p[36] + v*(p[46]))))) + + u*(p[13] + v*(p[19] + v*(p[27] + v*(p[35] + v*(p[45])))) + + u*(p[18] + v*(p[26] + v*(p[34] + v*(p[44]))) + + u*(p[25] + v*(p[33] + v*(p[43])) + + u*(p[32] + v*(p[42]) + + u*(p[41])))))))*v; + + // Radial terms? + if (i[I_TPDRAD]) { + s = u*u + v*v; + r = sqrt(s); + + *discrd += r*(p[3] + s*(p[11] + s*(p[23] + s*(p[39])))); + } + + return 0; +} + +//---------------------------------------------------------------------------- + +int tpd9( + int inverse, + const int i[], + const double p[], + int Nhat, + const double rawcrd[], + double *discrd) + +{ + if (i[I_TPDNCO+inverse] != 60 || 2 < Nhat) { + return 1; + } + + double r, s; + double u = rawcrd[0]; + double v = rawcrd[1]; + + // Auxiliary variables? + if (i[I_TPDAUX]) { + r = p[0] + p[1]*u + p[2]*v; + v = p[3] + p[4]*u + p[5]*v; + u = r; + p += 6; + } + + if (inverse) p += i[I_TPDNCO]; + + // Ninth degree. + *discrd = p[0] + u*(p[1] + u*(p[4] + u*(p[7] + u*(p[12] + u*(p[17] + u*(p[24] + u*(p[31] + u*(p[40] + u*(p[49]))))))))); + + if (Nhat == 1) return 0; + + *discrd += + v*(p[2] + v*(p[6] + v*(p[10] + v*(p[16] + v*(p[22] + v*(p[30] + v*(p[38] + v*(p[48] + v*(p[58]))))))))) + + u*(p[5] + v*(p[9] + v*(p[15] + v*(p[21] + v*(p[29] + v*(p[37] + v*(p[47] + v*(p[57]))))))) + + u*(p[8] + v*(p[14] + v*(p[20] + v*(p[28] + v*(p[36] + v*(p[46] + v*(p[56])))))) + + u*(p[13] + v*(p[19] + v*(p[27] + v*(p[35] + v*(p[45] + v*(p[55]))))) + + u*(p[18] + v*(p[26] + v*(p[34] + v*(p[44] + v*(p[54])))) + + u*(p[25] + v*(p[33] + v*(p[43] + v*(p[53]))) + + u*(p[32] + v*(p[42] + v*(p[52])) + + u*(p[41] + v*(p[51]) + + u*(p[50]))))))))*v; + + // Radial terms? + if (i[I_TPDRAD]) { + s = u*u + v*v; + r = sqrt(s); + + *discrd += r*(p[3] + s*(p[11] + s*(p[23] + s*(p[39] + s*(p[59]))))); + } + + return 0; +} diff --git a/cextern/wcslib/C/dis.h b/cextern/wcslib/C/dis.h new file mode 100644 index 000000000000..ed9f3e505b9f --- /dev/null +++ b/cextern/wcslib/C/dis.h @@ -0,0 +1,1222 @@ +/*============================================================================ + WCSLIB 8.6 - an implementation of the FITS WCS standard. + Copyright (C) 1995-2026, Mark Calabretta + + This file is part of WCSLIB. + + WCSLIB is free software: you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + WCSLIB is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for + more details. + + You should have received a copy of the GNU Lesser General Public License + along with WCSLIB. If not, see http://www.gnu.org/licenses. + + Author: Mark Calabretta, Australia Telescope National Facility, CSIRO. + http://www.atnf.csiro.au/computing/software/wcs + $Id: dis.h,v 8.6 2026/03/29 13:53:56 mcalabre Exp $ +*============================================================================= +* +* WCSLIB 8.6 - C routines that implement the FITS World Coordinate System +* (WCS) standard. Refer to the README file provided with WCSLIB for an +* overview of the library. +* +* +* Summary of the dis routines +* --------------------------- +* Routines in this suite implement extensions to the FITS World Coordinate +* System (WCS) standard proposed by +* += "Representations of distortions in FITS world coordinate systems", += Calabretta, M.R. et al. (WCS Paper IV, draft dated 2004/04/22), += available from http://www.atnf.csiro.au/computing/software/wcs +* +* In brief, a distortion function may occupy one of two positions in the WCS +* algorithm chain. Prior distortions precede the linear transformation +* matrix, whether it be PCi_ja or CDi_ja, and sequent distortions follow it. +* WCS Paper IV defines FITS keywords used to specify parameters for predefined +* distortion functions. The following are used for prior distortions: +* += CPDISja ...(string-valued, identifies the distortion function) += DPja ...(record-valued, parameters) += CPERRja ...(floating-valued, maximum value) +* +* Their counterparts for sequent distortions are CQDISia, DQia, and CQERRia. +* An additional floating-valued keyword, DVERRa, records the maximum value of +* the combined distortions. +* +* DPja and DQia are "record-valued". Syntactically, the keyvalues are +* standard FITS strings, but they are to be interpreted in a special way. +* The general form is +* += DPja = ': ' +* +* where the field-specifier consists of a sequence of fields separated by +* periods, and the ': ' between the field-specifier and the floating-point +* value is part of the record syntax. For example: +* += DP1 = 'AXIS.1: 1' +* +* Certain field-specifiers are defined for all distortion functions, while +* others are defined only for particular distortions. Refer to WCS Paper IV +* for further details. wcspih() parses all distortion keywords and loads them +* into a disprm struct for analysis by disset() which knows (or possibly does +* not know) how to interpret them. Of the Paper IV distortion functions, only +* the general Polynomial distortion is currently implemented here. +* +* TPV - the TPV "projection": +* --------------------------- +* The distortion function component of the TPV celestial "projection" is also +* supported. The TPV projection, originally proposed in a draft of WCS Paper +* II, consists of a TAN projection with sequent polynomial distortion, the +* coefficients of which are encoded in PVi_ma keyrecords. Full details may be += http://fits.gsfc.nasa.gov/registry/tpvwcs/tpv.html +* +* Internally, wcsset() changes TPV to a TAN projection, translates the PVi_ma +* keywords to DQia and loads them into a disprm struct. These DQia keyrecords +* have the form +* += DQia = 'TPV.m: ' +* +* where i, a, m, and the value for each DQia match each PVi_ma. Consequently, +* WCSLIB would handle a FITS header containing these keywords, along with +* CQDISia = 'TPV' and the required DQia.NAXES and DQia.AXIS.ihat keywords. +* +* Note that, as defined, TPV assumes that CDi_ja is used to define the linear +* transformation. The section on historical idiosyncrasies (below) cautions +* about translating CDi_ja to PCi_ja plus CDELTia in this case. +* +* SIP - Simple Imaging Polynomial: +* -------------------------------- +* These routines also support the Simple Imaging Polynomial (SIP), whose +* design was influenced by early drafts of WCS Paper IV. It is described in +* detail in +* += http://fits.gsfc.nasa.gov/registry/sip.html +* +* SIP, which is defined only as a prior distortion for 2-D celestial images, +* has the interesting feature that it records an approximation to the inverse +* polynomial distortion function. This is used by disx2p() to provide an +* initial estimate for its more precise iterative inversion. The +* special-purpose keywords used by SIP are parsed and translated by wcspih() +* as follows: +* += A_p_q = -> DP1 = 'SIP.FWD.p_q: ' += AP_p_q = -> DP1 = 'SIP.REV.p_q: ' += B_p_q = -> DP2 = 'SIP.FWD.p_q: ' += BP_p_q = -> DP2 = 'SIP.REV.p_q: ' += A_DMAX = -> DPERR1 = += B_DMAX = -> DPERR2 = +* +* SIP's A_ORDER and B_ORDER keywords are not used. WCSLIB would recognise a +* FITS header containing the above keywords, along with CPDISja = 'SIP' and +* the required DPja.NAXES keywords. +* +* DSS - Digitized Sky Survey: +* --------------------------- +* The Digitized Sky Survey resulted from the production of the Guide Star +* Catalogue for the Hubble Space Telescope. Plate solutions based on a +* polynomial distortion function were encoded in FITS using non-standard +* keywords. Sect. 5.2 of WCS Paper IV describes how DSS coordinates may be +* translated to a sequent Polynomial distortion using two auxiliary variables. +* That translation is based on optimising the non-distortion component of the +* plate solution. +* +* Following Paper IV, wcspih() translates the non-distortion component of DSS +* coordinates to standard WCS keywords (CRPIXja, PCi_ja, CRVALia, etc), and +* fills a wcsprm struct with their values. It encodes the DSS polynomial +* coefficients as +* += AMDXm = -> DQ1 = 'AMD.m: ' += AMDYm = -> DQ2 = 'AMD.m: ' +* +* WCSLIB would recognise a FITS header containing the above keywords, along +* with CQDISia = 'DSS' and the required DQia.NAXES keywords. +* +* WAT - the TNX and ZPX "projections": +* ------------------------------------ +* The TNX and ZPX "projections" add a polynomial distortion function to the +* standard TAN and ZPN projections respectively. Unusually, the polynomial +* may be expressed as the sum of Chebyshev or Legendre polynomials, or as a +* simple sum of monomials, as described in +* += http://fits.gsfc.nasa.gov/registry/tnx/tnx-doc.html += http://fits.gsfc.nasa.gov/registry/zpxwcs/zpx.html +* +* The polynomial coefficients are encoded in special-purpose WATi_n keywords +* as a set of continued strings, thus providing the name for this distortion +* type. WATi_n are parsed and translated by wcspih() into the following set: +* += DQi = 'WAT.POLY: ' += DQi = 'WAT.XMIN: ' += DQi = 'WAT.XMAX: ' += DQi = 'WAT.YMIN: ' += DQi = 'WAT.YMAX: ' += DQi = 'WAT.CHBY.m_n: ' or += DQi = 'WAT.LEGR.m_n: ' or += DQi = 'WAT.MONO.m_n: ' +* +* along with CQDISia = 'WAT' and the required DPja.NAXES keywords. For ZPX, +* the ZPN projection parameters are also encoded in WATi_n, and wcspih() +* translates these to standard PVi_ma. +* +* Note that, as defined, TNX and ZPX assume that CDi_ja is used to define the +* linear transformation. The section on historical idiosyncrasies (below) +* cautions about translating CDi_ja to PCi_ja plus CDELTia in this case. +* +* TPD - Template Polynomial Distortion: +* ------------------------------------- +* The "Template Polynomial Distortion" (TPD) is a superset of the TPV, SIP, +* DSS, and WAT (TNX & ZPX) polynomial distortions that also supports 1-D usage +* and inversions. Like TPV, SIP, and DSS, the form of the polynomial is fixed +* (the "template") and only the coefficients for the required terms are set +* non-zero. TPD generalizes TPV in going to 9th degree, SIP by accomodating +* TPV's linear and radial terms, and DSS in both respects. While in theory +* the degree of the WAT polynomial distortion in unconstrained, in practice it +* is limited to values that can be handled by TPD. +* +* Within WCSLIB, TPV, SIP, DSS, and WAT are all implemented as special cases +* of TPD. Indeed, TPD was developed precisely for that purpose. WAT +* distortions expressed as the sum of Chebyshev or Legendre polynomials are +* expanded for TPD as a simple sum of monomials. Moreover, the general +* Polynomial distortion is translated and implemented internally as TPD +* whenever possible. +* +* However, WCSLIB also recognizes 'TPD' as a distortion function in its own +* right (i.e. a recognized value of CPDISja or CQDISia), for use as both prior +* and sequent distortions. Its DPja and DQia keyrecords have the form +* += DPja = 'TPD.FWD.m: ' += DPja = 'TPD.REV.m: ' +* +* for the forward and reverse distortion functions. Moreover, like the +* general Polynomial distortion, TPD supports auxiliary variables, though only +* as a linear transformation of pixel coordinates (p1,p2): +* += x = a0 + a1*p1 + a2*p2 += y = b0 + b1*p1 + b2*p2 +* +* where the coefficients of the auxiliary variables (x,y) are recorded as +* += DPja = 'AUX.1.COEFF.0: a0' ...default 0.0 += DPja = 'AUX.1.COEFF.1: a1' ...default 1.0 += DPja = 'AUX.1.COEFF.2: a2' ...default 0.0 += DPja = 'AUX.2.COEFF.0: b0' ...default 0.0 += DPja = 'AUX.2.COEFF.1: b1' ...default 0.0 += DPja = 'AUX.2.COEFF.2: b2' ...default 1.0 +* +* Though nowhere near as powerful, in typical applications TPD is considerably +* faster than the general Polynomial distortion. As TPD has a finite and not +* too large number of possible terms (60), the coefficients for each can be +* stored (by disset()) in a fixed location in the disprm::dparm[] array. A +* large part of the speedup then arises from evaluating the polynomial using +* Horner's scheme. +* +* Separate implementations for polynomials of each degree, and conditionals +* for 1-D polynomials and 2-D polynomials with and without the radial +* variable, ensure that unused terms mostly do not impose a significant +* computational overhead. +* +* The TPD terms are as follows +* += 0: 1 4: xx 12: xxxx 24: xxxxxx 40: xxxxxxxx += 5: xy 13: xxxy 25: xxxxxy 41: xxxxxxxy += 1: x 6: yy 14: xxyy 26: xxxxyy 42: xxxxxxyy += 2: y 15: xyyy 27: xxxyyy 43: xxxxxyyy += 3: r 7: xxx 16: yyyy 28: xxyyyy 44: xxxxyyyy += 8: xxy 29: xyyyyy 45: xxxyyyyy += 9: xyy 17: xxxxx 30: yyyyyy 46: xxyyyyyy += 10: yyy 18: xxxxy 47: xyyyyyyy += 11: rrr 19: xxxyy 31: xxxxxxx 48: yyyyyyyy += 20: xxyyy 32: xxxxxxy += 21: xyyyy 33: xxxxxyy 49: xxxxxxxxx += 22: yyyyy 34: xxxxyyy 50: xxxxxxxxy += 23: rrrrr 35: xxxyyyy 51: xxxxxxxyy += 36: xxyyyyy 52: xxxxxxyyy += 37: xyyyyyy 53: xxxxxyyyy += 38: yyyyyyy 54: xxxxyyyyy += 39: rrrrrrr 55: xxxyyyyyy += 56: xxyyyyyyy += 57: xyyyyyyyy += 58: yyyyyyyyy += 59: rrrrrrrrr +* +* where r = sqrt(xx + yy). Note that even powers of r are excluded since they +* can be accomodated by powers of (xx + yy). +* +* Note here that "x" refers to the axis to which the distortion function is +* attached, with "y" being the complementary axis. So, for example, with +* longitude on axis 1 and latitude on axis 2, for TPD attached to axis 1, "x" +* refers to axis 1 and "y" to axis 2. For TPD attached to axis 2, "x" refers +* to axis 2, and "y" to axis 1. +* +* TPV uses all terms up to 39. The m in its PVi_ma keywords translates +* directly to the TPD coefficient number. +* +* SIP uses all terms except for 0, 3, 11, 23, 39, and 59, with terms 1 and 2 +* only used for the inverse. Its A_p_q, etc. keywords must be translated +* using a map. +* +* DSS uses terms 0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 17, 19, and 21. The presence +* of a non-zero constant term arises through the use of auxiliary variables +* with origin offset from the reference point of the TAN projection. However, +* in the translation given by WCS Paper IV, the distortion polynomial is zero, +* or very close to zero, at the reference pixel itself. The mapping between +* DSS's AMDXm (or AMDYm) keyvalues and TPD coefficients, while still simple, +* is not quite as straightforward as for TPV and SIP. +* +* WAT uses all but the radial terms, namely 3, 11, 23, 39, and 59. While the +* mapping between WAT's monomial coefficients and TPD is fairly simple, for +* its expression in terms of a sum of Chebyshev or Legendre polynomials it is +* much less so. +* +* Historical idiosyncrasies: +* -------------------------- +* In addition to the above, some historical distortion functions have further +* idiosyncrasies that must be taken into account when translating them to TPD. +* +* WCS Paper IV specifies that a distortion function returns a correction to be +* added to pixel coordinates (prior distortion) or intermediate pixel +* coordinates (sequent distortion). The correction is meant to be small so +* that ignoring the distortion function, i.e. setting the correction to zero, +* produces a commensurately small error. +* +* However, rather than an additive correction, some historical distortion +* functions (TPV, DSS) define a polynomial that returns the corrected +* coordinates directly. +* +* The difference between the two approaches is readily accounted for simply by +* adding or subtracting 1 from the coefficient of the first degree term of the +* polynomial. However, it opens the way for considerable confusion. +* +* Additional to the formalism of WCS Paper IV, both the Polynomial and TPD +* distortion functions recognise a keyword +* += DPja = 'DOCORR: 0' +* +* which is meant to apply generally to indicate that the distortion function +* returns the corrected coordinates directly. Any other value for DOCORR (or +* its absence) indicates that the distortion function returns an additive +* correction. +* +* WCS Paper IV also specifies that the independent variables of a distortion +* function are pixel coordinates (prior distortion) or intermediate pixel +* coordinates (sequent distortion). +* +* On the contrary, the independent variables of the SIP polynomial are pixel +* coordinate offsets from the reference pixel. This is readily handled via +* the renormalisation parameters +* += DPja = 'OFFSET.jhat: ' +* +* where the value corresponds to CRPIXja. +* +* Likewise, because TPV, TNX, and ZPX are defined in terms of CDi_ja, the +* independent variables of the polynomial are intermediate world coordinates +* rather than intermediate pixel coordinates. Because sequent distortions +* are always applied before CDELTia, if CDi_ja is translated to PCi_ja plus +* CDELTia, then either CDELTia must be unity, or the distortion polynomial +* coefficients must be adjusted to account for the change of scale. +* +* Summary of the dis routines: +* ---------------------------- +* These routines apply the distortion functions defined by the extension to +* the FITS WCS standard proposed in Paper IV. They are based on the disprm +* struct which contains all information needed for the computations. The +* struct contains some members that must be set by the user, and others that +* are maintained by these routines, somewhat like a C++ class but with no +* encapsulation. +* +* dpfill(), dpkeyi(), and dpkeyd() are provided to manage the dpkey struct. +* +* disndp(), disini(), disinit(), discpy(), and disfree() are provided to +* manage the disprm struct, dissize() computes its total size including +* allocated memory, disenq() returns information about the state of the +* struct, and disprt() prints its contents. +* +* disperr() prints the error message(s) (if any) stored in a disprm struct. +* +* wcshdo() normally writes SIP and TPV headers in their native form if at all +* possible. However, dishdo() may be used to set a flag that tells it to +* write the header in the form of the TPD translation used internally. +* +* A setup routine, disset(), computes intermediate values in the disprm struct +* from parameters in it that were supplied by the user. The struct always +* needs to be set up by disset(), though disset() need not be called +* explicitly - refer to the explanation of disprm::flag. +* +* disp2x() and disx2p() implement the WCS distortion functions, disp2x() using +* separate functions, such as dispoly() and tpd7(), to do the computation. +* +* An auxiliary routine, diswarp(), computes various measures of the distortion +* over a specified range of coordinates. +* +* PLEASE NOTE: Distortions are not yet handled by wcsbth(), or wcscompare(). +* +* +* disndp() - Memory allocation for DPja and DQia +* ---------------------------------------------- +* disndp() sets or gets the value of NDPMAX (default 256). This global +* variable controls the maximum number of dpkey structs, for holding DPja or +* DQia keyvalues, that disini() should allocate space for. It is also used by +* disinit() as the default value of ndpmax. +* +* PLEASE NOTE: This function is not thread-safe. +* +* Given: +* n int Value of NDPMAX; ignored if < 0. Use a value less +* than zero to get the current value. +* +* Function return value: +* int Current value of NDPMAX. +* +* +* dpfill() - Fill the contents of a dpkey struct +* ---------------------------------------------- +* dpfill() is a utility routine to aid in filling the contents of the dpkey +* struct. No checks are done on the validity of the inputs. +* +* WCS Paper IV specifies the syntax of a record-valued keyword as +* += keyword = ': ' +* +* However, some DPja and DQia record values, such as those of DPja.NAXES and +* DPja.AXIS.j, are intrinsically integer-valued. While FITS header parsers +* are not expected to know in advance which of DPja and DQia are integral and +* which are floating point, if the record's value parses as an integer (i.e. +* without decimal point or exponent), then preferably enter it into the dpkey +* struct as an integer. Either way, it doesn't matter as disset() accepts +* either data type for all record values. +* +* Given and returned: +* dp struct dpkey* +* Store for DPja and DQia keyvalues. +* +* Given: +* keyword const char * +* field const char * +* These arguments are concatenated with an intervening +* "." to construct the full record field name, i.e. +* including the keyword name, DPja or DQia (but +* excluding the colon delimiter which is NOT part of the +* name). Either may be given as a NULL pointer. Set +* both NULL to omit setting this component of the +* struct. +* +* j int Axis number (1-relative), i.e. the j in DPja or +* i in DQia. Can be given as 0, in which case the axis +* number will be obtained from the keyword component of +* the field name which must either have been given or +* preset. +* +* If j is non-zero, and keyword was given, then the +* value of j will be used to fill in the axis number. +* +* type int Data type of the record's value +* 0: Integer, +* 1: Floating point. +* +* i int For type == 0, the integer value of the record. +* +* f double For type == 1, the floating point value of the record. +* +* Function return value: +* int Status return value: +* 0: Success. +* +* +* dpkeyi() - Get the data value in a dpkey struct as int +* ------------------------------------------------------ +* dpkeyi() returns the data value in a dpkey struct as an integer value. +* +* Given and returned: +* dp const struct dpkey * +* Parsed contents of a DPja or DQia keyrecord. +* +* Function return value: +* int The record's value as int. +* +* +* dpkeyd() - Get the data value in a dpkey struct as double +* --------------------------------------------------------- +* dpkeyd() returns the data value in a dpkey struct as a floating point +* value. +* +* Given and returned: +* dp const struct dpkey * +* Parsed contents of a DPja or DQia keyrecord. +* +* Function return value: +* double The record's value as double. +* +* +* disini() - Default constructor for the disprm struct +* ---------------------------------------------------- +* disini() is a thin wrapper on disinit(). It invokes it with ndpmax set +* to -1 which causes it to use the value of the global variable NDPMAX. It +* is thereby potentially thread-unsafe if NDPMAX is altered dynamically via +* disndp(). Use disinit() for a thread-safe alternative in this case. +* +* +* disinit() - Default constructor for the disprm struct +* ---------------------------------------------------- +* disinit() allocates memory for arrays in a disprm struct and sets all +* members of the struct to default values. +* +* PLEASE NOTE: every disprm struct must be initialized by disinit(), possibly +* repeatedly. On the first invokation, and only the first invokation, +* disprm::flag must be set to -1 to initialize memory management, regardless +* of whether disinit() will actually be used to allocate memory. +* +* Given: +* alloc int If true, allocate memory unconditionally for arrays in +* the disprm struct. +* +* If false, it is assumed that pointers to these arrays +* have been set by the user except if they are null +* pointers in which case memory will be allocated for +* them regardless. (In other words, setting alloc true +* saves having to initalize these pointers to zero.) +* +* naxis int The number of world coordinate axes, used to determine +* array sizes. +* +* Given and returned: +* dis struct disprm* +* Distortion function parameters. Note that, in order +* to initialize memory management disprm::flag must be +* set to -1 when dis is initialized for the first time +* (memory leaks may result if it had already been +* initialized). +* +* Given: +* ndpmax int The number of DPja or DQia keywords to allocate space +* for. If set to -1, the value of the global variable +* NDPMAX will be used. This is potentially +* thread-unsafe if disndp() is being used dynamically to +* alter its value. +* +* Function return value: +* int Status return value: +* 0: Success. +* 1: Null disprm pointer passed. +* 2: Memory allocation failed. +* +* For returns > 1, a detailed error message is set in +* disprm::err if enabled, see wcserr_enable(). +* +* +* discpy() - Copy routine for the disprm struct +* --------------------------------------------- +* discpy() does a deep copy of one disprm struct to another, using disinit() +* to allocate memory unconditionally for its arrays if required. Only the +* "information to be provided" part of the struct is copied; a call to +* disset() is required to initialize the remainder. +* +* Given: +* alloc int If true, allocate memory unconditionally for arrays in +* the destination. Otherwise, it is assumed that +* pointers to these arrays have been set by the user +* except if they are null pointers in which case memory +* will be allocated for them regardless. +* +* dissrc const struct disprm* +* Struct to copy from. +* +* Given and returned: +* disdst struct disprm* +* Struct to copy to. disprm::flag should be set to -1 +* if disdst was not previously initialized (memory leaks +* may result if it was previously initialized). +* +* Function return value: +* int Status return value: +* 0: Success. +* 1: Null disprm pointer passed. +* 2: Memory allocation failed. +* +* For returns > 1, a detailed error message is set in +* disprm::err if enabled, see wcserr_enable(). +* +* +* disfree() - Destructor for the disprm struct +* -------------------------------------------- +* disfree() frees memory allocated for the disprm arrays by disinit(). +* disinit() keeps a record of the memory it allocates and disfree() will only +* attempt to free this. +* +* PLEASE NOTE: disfree() must not be invoked on a disprm struct that was not +* initialized by disinit(). +* +* Given and returned: +* dis struct disprm* +* Distortion function parameters. +* +* Function return value: +* int Status return value: +* 0: Success. +* 1: Null disprm pointer passed. +* +* +* dissize() - Compute the size of a disprm struct +* ----------------------------------------------- +* dissize() computes the full size of a disprm struct, including allocated +* memory. +* +* Given: +* dis const struct disprm* +* Distortion function parameters. +* +* If NULL, the base size of the struct and the allocated +* size are both set to zero. +* +* Returned: +* sizes int[2] The first element is the base size of the struct as +* returned by sizeof(struct disprm). The second element +* is the total allocated size, in bytes, assuming that +* the allocation was done by disini(). This figure +* includes memory allocated for members of constituent +* structs, such as disprm::dp. +* +* It is not an error for the struct not to have been set +* up via tabset(), which normally results in additional +* memory allocation. +* +* Function return value: +* int Status return value: +* 0: Success. +* +* +* disenq() - enquire about the state of a disprm struct +* ----------------------------------------------------- +* disenq() may be used to obtain information about the state of a disprm +* struct. The function returns a true/false answer for the enquiry asked. +* +* Given: +* dis const struct disprm* +* Distortion function parameters. +* +* enquiry int Enquiry according to the following parameters: +* DISENQ_MEM: memory in the struct is being managed by +* WCSLIB (see disinit()). +* DISENQ_SET: the struct has been set up by disset(). +* DISENQ_BYP: the struct is in bypass mode (see +* disset()). +* These may be combined by logical OR, e.g. +* DISENQ_MEM | DISENQ_SET. The enquiry result will be +* the logical AND of the individual results. +* +* Function return value: +* int Enquiry result: +* 0: No. +* 1: Yes. +* +* +* disprt() - Print routine for the disprm struct +* ---------------------------------------------- +* disprt() prints the contents of a disprm struct using wcsprintf(). Mainly +* intended for diagnostic purposes. +* +* Given: +* dis const struct disprm* +* Distortion function parameters. +* +* Function return value: +* int Status return value: +* 0: Success. +* 1: Null disprm pointer passed. +* +* +* disperr() - Print error messages from a disprm struct +* ----------------------------------------------------- +* disperr() prints the error message(s) (if any) stored in a disprm struct. +* If there are no errors then nothing is printed. It uses wcserr_prt(), q.v. +* +* Given: +* dis const struct disprm* +* Distortion function parameters. +* +* prefix const char * +* If non-NULL, each output line will be prefixed with +* this string. +* +* Function return value: +* int Status return value: +* 0: Success. +* 1: Null disprm pointer passed. +* +* +* dishdo() - write FITS headers using TPD +* --------------------------------------- +* dishdo() sets a flag that tells wcshdo() to write FITS headers in the form +* of the TPD translation used internally. Normally SIP and TPV would be +* written in their native form if at all possible. +* +* Given and returned: +* dis struct disprm* +* Distortion function parameters. +* +* Function return value: +* int Status return value: +* 0: Success. +* 1: Null disprm pointer passed. +* 3: No TPD translation. +* +* +* disset() - Setup routine for the disprm struct +* ---------------------------------------------- +* disset(), sets up the disprm struct according to information supplied within +* it - refer to the explanation of disprm::flag. +* +* Note that this routine need not be called directly; it will be invoked by +* disp2x() and disx2p() if the disprm::flag is anything other than a +* predefined magic value. +* +* disset() normally operates regardless of the value of disprm::flag; i.e. +* even if a struct was previously set up it will be reset unconditionally. +* However, a disprm struct may be put into "bypass" mode by invoking disset() +* initially with disprm::flag == 1 (rather than 0). disset() will return +* immediately if invoked on a struct in that state. To take a struct out of +* bypass mode, simply reset disprm::flag to zero. See also disenq(). +* +* Given and returned: +* dis struct disprm* +* Distortion function parameters. +* +* Function return value: +* int Status return value: +* 0: Success. +* 1: Null disprm pointer passed. +* 2: Memory allocation failed. +* 3: Invalid parameter. +* +* For returns > 1, a detailed error message is set in +* disprm::err if enabled, see wcserr_enable(). +* +* +* disp2x() - Apply distortion function +* ------------------------------------ +* disp2x() applies the distortion functions. By definition, the distortion +* is in the pixel-to-world direction. +* +* Depending on the point in the algorithm chain at which it is invoked, +* disp2x() may transform pixel coordinates to corrected pixel coordinates, or +* intermediate pixel coordinates to corrected intermediate pixel coordinates, +* or image coordinates to corrected image coordinates. +* +* +* Given and returned: +* dis struct disprm* +* Distortion function parameters. +* +* Given: +* rawcrd const double[naxis] +* Array of coordinates. +* +* Returned: +* discrd double[naxis] +* Array of coordinates to which the distortion functions +* have been applied. +* +* Function return value: +* int Status return value: +* 0: Success. +* 1: Null disprm pointer passed. +* 2: Memory allocation failed. +* 3: Invalid parameter. +* 4: Distort error. +* +* For returns > 1, a detailed error message is set in +* disprm::err if enabled, see wcserr_enable(). +* +* +* disx2p() - Apply de-distortion function +* --------------------------------------- +* disx2p() applies the inverse of the distortion functions. By definition, +* the de-distortion is in the world-to-pixel direction. +* +* Depending on the point in the algorithm chain at which it is invoked, +* disx2p() may transform corrected pixel coordinates to pixel coordinates, or +* corrected intermediate pixel coordinates to intermediate pixel coordinates, +* or corrected image coordinates to image coordinates. +* +* disx2p() iteratively solves for the inverse using disp2x(). It assumes +* that the distortion is small and the functions are well-behaved, being +* continuous and with continuous derivatives. Also that, to first order +* in the neighbourhood of the solution, discrd[j] ~= a + b*rawcrd[j], i.e. +* independent of rawcrd[i], where i != j. This is effectively equivalent to +* assuming that the distortion functions are separable to first order. +* Furthermore, a is assumed to be small, and b close to unity. +* +* If disprm::disx2p() is defined, then disx2p() uses it to provide an initial +* estimate for its more precise iterative inversion. +* +* Given and returned: +* dis struct disprm* +* Distortion function parameters. +* +* Given: +* discrd const double[naxis] +* Array of coordinates. +* +* Returned: +* rawcrd double[naxis] +* Array of coordinates to which the inverse distortion +* functions have been applied. +* +* Function return value: +* int Status return value: +* 0: Success. +* 1: Null disprm pointer passed. +* 2: Memory allocation failed. +* 3: Invalid parameter. +* 5: De-distort error. +* +* For returns > 1, a detailed error message is set in +* disprm::err if enabled, see wcserr_enable(). +* +* +* diswarp() - Compute measures of distortion +* ------------------------------------------ +* diswarp() computes various measures of the distortion over a specified range +* of coordinates. +* +* For prior distortions, the measures may be interpreted simply as an offset +* in pixel coordinates. For sequent distortions, the interpretation depends +* on the nature of the linear transformation matrix (PCi_ja or CDi_ja). If +* the latter introduces a scaling, then the measures will also be scaled. +* Note also that the image domain, which is rectangular in pixel coordinates, +* may be rotated, skewed, and/or stretched in intermediate pixel coordinates, +* and in general cannot be defined using pixblc[] and pixtrc[]. +* +* PLEASE NOTE: the measures of total distortion may be essentially meaningless +* if there are multiple sequent distortions with different scaling. +* +* See also linwarp(). +* +* Given and returned: +* dis struct disprm* +* Distortion function parameters. +* +* Given: +* pixblc const double[naxis] +* Start of the range of pixel coordinates (for prior +* distortions), or intermediate pixel coordinates (for +* sequent distortions). May be specified as a NULL +* pointer which is interpreted as (1,1,...). +* +* pixtrc const double[naxis] +* End of the range of pixel coordinates (prior) or +* intermediate pixel coordinates (sequent). +* +* pixsamp const double[naxis] +* If positive or zero, the increment on the particular +* axis, starting at pixblc[]. Zero is interpreted as a +* unit increment. pixsamp may also be specified as a +* NULL pointer which is interpreted as all zeroes, i.e. +* unit increments on all axes. +* +* If negative, the grid size on the particular axis (the +* absolute value being rounded to the nearest integer). +* For example, if pixsamp is (-128.0,-128.0,...) then +* each axis will be sampled at 128 points between +* pixblc[] and pixtrc[] inclusive. Use caution when +* using this option on non-square images. +* +* Returned: +* nsamp int* The number of pixel coordinates sampled. +* +* Can be specified as a NULL pointer if not required. +* +* maxdis double[naxis] +* For each individual distortion function, the +* maximum absolute value of the distortion. +* +* Can be specified as a NULL pointer if not required. +* +* maxtot double* For the combination of all distortion functions, the +* maximum absolute value of the distortion. +* +* Can be specified as a NULL pointer if not required. +* +* avgdis double[naxis] +* For each individual distortion function, the +* mean value of the distortion. +* +* Can be specified as a NULL pointer if not required. +* +* avgtot double* For the combination of all distortion functions, the +* mean value of the distortion. +* +* Can be specified as a NULL pointer if not required. +* +* rmsdis double[naxis] +* For each individual distortion function, the +* root mean square deviation of the distortion. +* +* Can be specified as a NULL pointer if not required. +* +* rmstot double* For the combination of all distortion functions, the +* root mean square deviation of the distortion. +* +* Can be specified as a NULL pointer if not required. +* +* Function return value: +* int Status return value: +* 0: Success. +* 1: Null disprm pointer passed. +* 2: Memory allocation failed. +* 3: Invalid parameter. +* 4: Distort error. +* +* +* disprm struct - Distortion parameters +* ------------------------------------- +* The disprm struct contains all of the information required to apply a set of +* distortion functions. It consists of certain members that must be set by +* the user ("given") and others that are set by the WCSLIB routines +* ("returned"). While the addresses of the arrays themselves may be set by +* disinit() if it (optionally) allocates memory, their contents must be set by +* the user. +* +* int flag +* (Given and returned) This flag must be set to zero (or 1, see disset()) +* whenever any of the following disprm members are set or changed: +* +* - disprm::naxis, +* - disprm::dtype, +* - disprm::ndp, +* - disprm::dp. +* +* This signals the initialization routine, disset(), to recompute the +* returned members of the disprm struct. disset() will reset flag to +* indicate that this has been done. +* +* PLEASE NOTE: flag must be set to -1 when disinit() is called for the +* first time for a particular disprm struct in order to initialize memory +* management. It must ONLY be used on the first initialization otherwise +* memory leaks may result. +* +* int naxis +* (Given or returned) Number of pixel and world coordinate elements. +* +* If disinit() is used to initialize the disprm struct (as would normally +* be the case) then it will set naxis from the value passed to it as a +* function argument. The user should not subsequently modify it. +* +* char (*dtype)[72] +* (Given) Pointer to the first element of an array of char[72] containing +* the name of the distortion function for each axis. +* +* int ndp +* (Given) The number of entries in the disprm::dp[] array. +* +* int ndpmax +* (Given) The length of the disprm::dp[] array. +* +* ndpmax will be set by disinit() if it allocates memory for disprm::dp[], +* otherwise it must be set by the user. See also disndp(). +* +* struct dpkey dp +* (Given) Address of the first element of an array of length ndpmax of +* dpkey structs. +* +* As a FITS header parser encounters each DPja or DQia keyword it should +* load it into a dpkey struct in the array and increment ndp. However, +* note that a single disprm struct must hold only DPja or DQia keyvalues, +* not both. disset() interprets them as required by the particular +* distortion function. +* +* double *maxdis +* (Given) Pointer to the first element of an array of double specifying +* the maximum absolute value of the distortion for each axis computed over +* the whole image. +* +* It is not necessary to reset the disprm struct (via disset()) when +* disprm::maxdis is changed. +* +* double totdis +* (Given) The maximum absolute value of the combination of all distortion +* functions specified as an offset in pixel coordinates computed over the +* whole image. +* +* It is not necessary to reset the disprm struct (via disset()) when +* disprm::totdis is changed. +* +* int *docorr +* (Returned) Pointer to the first element of an array of int containing +* flags that indicate the mode of correction for each axis. +* +* If docorr is zero, the distortion function returns the corrected +* coordinates directly. Any other value indicates that the distortion +* function computes a correction to be added to pixel coordinates (prior +* distortion) or intermediate pixel coordinates (sequent distortion). +* +* int *Nhat +* (Returned) Pointer to the first element of an array of int containing +* the number of coordinate axes that form the independent variables of the +* distortion function for each axis. +* +* int **axmap +* (Returned) Pointer to the first element of an array of int* containing +* pointers to the first elements of the axis mapping arrays for each axis. +* +* An axis mapping associates the independent variables of a distortion +* function with the 0-relative image axis number. For example, consider +* an image with a spectrum on the first axis (axis 0), followed by RA +* (axis 1), Dec (axis2), and time (axis 3) axes. For a distortion in +* (RA,Dec) and no distortion on the spectral or time axes, the axis +* mapping arrays, axmap[j][], would be +* += j=0: [-1, -1, -1, -1] ...no distortion on spectral axis, += 1: [ 1, 2, -1, -1] ...RA distortion depends on RA and Dec, += 2: [ 2, 1, -1, -1] ...Dec distortion depends on Dec and RA, += 3: [-1, -1, -1, -1] ...no distortion on time axis, +* +* where -1 indicates that there is no corresponding independent +* variable. +* +* double **offset +* (Returned) Pointer to the first element of an array of double* +* containing pointers to the first elements of arrays of offsets used to +* renormalize the independent variables of the distortion function for +* each axis. +* +* The offsets are subtracted from the independent variables before +* scaling. +* +* double **scale +* (Returned) Pointer to the first element of an array of double* +* containing pointers to the first elements of arrays of scales used to +* renormalize the independent variables of the distortion function for +* each axis. +* +* The scale is applied to the independent variables after the offsets are +* subtracted. +* +* int **iparm +* (Returned) Pointer to the first element of an array of int* +* containing pointers to the first elements of the arrays of integer +* distortion parameters for each axis. +* +* double **dparm +* (Returned) Pointer to the first element of an array of double* +* containing pointers to the first elements of the arrays of floating +* point distortion parameters for each axis. +* +* int i_naxis +* (Returned) Dimension of the internal arrays (normally equal to naxis). +* +* int ndis +* (Returned) The number of distortion functions. +* +* struct wcserr *err +* (Returned) If enabled, when an error status is returned, this struct +* contains detailed information about the error, see wcserr_enable(). +* +* int (**disp2x)(DISP2X_ARGS) +* (For internal use only.) +* int (**disx2p)(DISX2P_ARGS) +* (For internal use only.) +* int m_flag +* (For internal use only.) +* int m_naxis +* (For internal use only.) +* char (*m_dtype)[72] +* (For internal use only.) +* double **m_dp +* (For internal use only.) +* double *m_maxdis +* (For internal use only.) +* +* +* dpkey struct - Store for DPja and DQia keyvalues +* ------------------------------------------------ +* The dpkey struct is used to pass the parsed contents of DPja or DQia +* keyrecords to disset() via the disprm struct. A disprm struct must hold +* only DPja or DQia keyvalues, not both. +* +* All members of this struct are to be set by the user. +* +* char field[72] +* (Given) The full field name of the record, including the keyword name. +* Note that the colon delimiter separating the field name and the value in +* record-valued keyvalues is not part of the field name. For example, in +* the following: +* += DP3A = 'AXIS.1: 2' +* +* the full record field name is "DP3A.AXIS.1", and the record's value +* is 2. +* +* int j +* (Given) Axis number (1-relative), i.e. the j in DPja or i in DQia. +* +* int type +* (Given) The data type of the record's value +* - 0: Integer (stored as an int), +* - 1: Floating point (stored as a double). +* +* union value +* (Given) A union comprised of +* - dpkey::i, +* - dpkey::f, +* +* the record's value. +* +* +* Global variable: const char *dis_errmsg[] - Status return messages +* ------------------------------------------------------------------ +* Error messages to match the status value returned from each function. +* +*===========================================================================*/ + +#ifndef WCSLIB_DIS +#define WCSLIB_DIS + +#ifdef __cplusplus +extern "C" { +#endif + +enum disenq_enum { + DISENQ_MEM = 1, // disprm struct memory is managed by WCSLIB. + DISENQ_SET = 2, // disprm struct has been set up. + DISENQ_BYP = 4, // disprm struct is in bypass mode. +}; + +extern const char *dis_errmsg[]; + +enum dis_errmsg_enum { + DISERR_SUCCESS = 0, // Success. + DISERR_NULL_POINTER = 1, // Null disprm pointer passed. + DISERR_MEMORY = 2, // Memory allocation failed. + DISERR_BAD_PARAM = 3, // Invalid parameter value. + DISERR_DISTORT = 4, // Distortion error. + DISERR_DEDISTORT = 5 // De-distortion error. +}; + +// For use in declaring distortion function prototypes (= DISX2P_ARGS). +#define DISP2X_ARGS int inverse, const int iparm[], const double dparm[], \ +int ncrd, const double rawcrd[], double *discrd + +// For use in declaring de-distortion function prototypes (= DISP2X_ARGS). +#define DISX2P_ARGS int inverse, const int iparm[], const double dparm[], \ +int ncrd, const double discrd[], double *rawcrd + + +// Struct used for storing DPja and DQia keyvalues. +struct dpkey { + char field[72]; // Full record field name (no colon). + int j; // Axis number, as in DPja (1-relative). + int type; // Data type of value. + union { + int i; // Integer record value. + double f; // Floating point record value. + } value; // Record value. +}; + +// Size of the dpkey struct in int units, used by the Fortran wrappers. +#define DPLEN (sizeof(struct dpkey)/sizeof(int)) + + +struct disprm { + // Initialization flag (see the prologue above). + //-------------------------------------------------------------------------- + int flag; // Set to zero to force initialization. + + // Parameters to be provided (see the prologue above). + //-------------------------------------------------------------------------- + int naxis; // The number of pixel coordinate elements, + // given by NAXIS. + char (*dtype)[72]; // For each axis, the distortion type. + int ndp; // Number of DPja or DQia keywords, and the + int ndpmax; // number for which space was allocated. + struct dpkey *dp; // DPja or DQia keyvalues (not both). + double totdis; // The maximum combined distortion. + double *maxdis; // For each axis, the maximum distortion. + + // Information derived from the parameters supplied. + //-------------------------------------------------------------------------- + int *docorr; // For each axis, the mode of correction. + int *Nhat; // For each axis, the number of coordinate + // axes that form the independent variables + // of the distortion function. + int **axmap; // For each axis, the axis mapping array. + double **offset; // For each axis, renormalization offsets. + double **scale; // For each axis, renormalization scales. + int **iparm; // For each axis, the array of integer + // distortion parameters. + double **dparm; // For each axis, the array of floating + // point distortion parameters. + int i_naxis; // Dimension of the internal arrays. + int ndis; // The number of distortion functions. + + // Error messaging, if enabled. + //-------------------------------------------------------------------------- + struct wcserr *err; + + //-------------------------------------------------------------------------- + // Private - the remainder are for internal use. + //-------------------------------------------------------------------------- + int (**disp2x)(DISP2X_ARGS); // For each axis, pointers to the + int (**disx2p)(DISX2P_ARGS); // distortion function and its inverse. + + int m_flag, m_naxis; // The remainder are for memory management. + char (*m_dtype)[72]; + struct dpkey *m_dp; + double *m_maxdis; +}; + +// Size of the disprm struct in int units, used by the Fortran wrappers. +#define DISLEN (sizeof(struct disprm)/sizeof(int)) + + +int disndp(int n); + +int dpfill(struct dpkey *dp, const char *keyword, const char *field, int j, + int type, int i, double f); + +int dpkeyi(const struct dpkey *dp); + +double dpkeyd(const struct dpkey *dp); + +int disini(int alloc, int naxis, struct disprm *dis); + +int disinit(int alloc, int naxis, struct disprm *dis, int ndpmax); + +int discpy(int alloc, const struct disprm *dissrc, struct disprm *disdst); + +int disfree(struct disprm *dis); + +int dissize(const struct disprm *dis, int sizes[2]); + +int disenq(const struct disprm *dis, int enquiry); + +int disprt(const struct disprm *dis); + +int disperr(const struct disprm *dis, const char *prefix); + +int dishdo(struct disprm *dis); + +int disset(struct disprm *dis); + +int disp2x(struct disprm *dis, const double rawcrd[], double discrd[]); + +int disx2p(struct disprm *dis, const double discrd[], double rawcrd[]); + +int diswarp(struct disprm *dis, const double pixblc[], const double pixtrc[], + const double pixsamp[], int *nsamp, + double maxdis[], double *maxtot, + double avgdis[], double *avgtot, + double rmsdis[], double *rmstot); + +#ifdef __cplusplus +} +#endif + +#endif // WCSLIB_DIS diff --git a/cextern/wcslib/C/fitshdr.h b/cextern/wcslib/C/fitshdr.h index d207d97f5d62..8e1e314ff9b2 100644 --- a/cextern/wcslib/C/fitshdr.h +++ b/cextern/wcslib/C/fitshdr.h @@ -1,7 +1,6 @@ /*============================================================================ - - WCSLIB 4.14 - an implementation of the FITS WCS standard. - Copyright (C) 1995-2012, Mark Calabretta + WCSLIB 8.6 - an implementation of the FITS WCS standard. + Copyright (C) 1995-2026, Mark Calabretta This file is part of WCSLIB. @@ -18,28 +17,27 @@ You should have received a copy of the GNU Lesser General Public License along with WCSLIB. If not, see http://www.gnu.org/licenses. - Direct correspondence concerning WCSLIB to mark@calabretta.id.au - Author: Mark Calabretta, Australia Telescope National Facility, CSIRO. - http://www.atnf.csiro.au/people/Mark.Calabretta - $Id: fitshdr.h,v 4.14 2012/07/13 10:02:27 cal103 Exp $ + http://www.atnf.csiro.au/computing/software/wcs + $Id: fitshdr.h,v 8.6 2026/03/29 13:53:56 mcalabre Exp $ *============================================================================= * -* The Flexible Image Transport System (FITS), a data format widely used in -* astronomy for data interchange and archive, is described in +* WCSLIB 8.6 - C routines that implement the FITS World Coordinate System +* (WCS) standard. Refer to the README file provided with WCSLIB for an +* overview of the library. * -* "Definition of The Flexible Image Transport System (FITS)", -* Hanisch, R.J., Farris, A., Greisen, E.W., et al. 2001, A&A, 376, 359 * -* which formalizes NOST 100-2.0, a document produced by the NASA/Science -* Office of Standards and Technology, see http://fits.gsfc.nasa.gov. +* Summary of the fitshdr routines +* ------------------------------- +* The Flexible Image Transport System (FITS), is a data format widely used in +* astronomy for data interchange and archive. It is described in * -* Refer to the README file provided with WCSLIB for an overview of the -* library. += "Definition of the Flexible Image Transport System (FITS), version 3.0", += Pence, W.D., Chiappetti, L., Page, C.G., Shaw, R.A., & Stobie, E. 2010, += A&A, 524, A42 - http://dx.doi.org/10.1051/0004-6361/201015362 * +* See also http://fits.gsfc.nasa.gov * -* Summary of the fitshdr routines -* ------------------------------- * fitshdr() is a generic FITS header parser provided to handle keyrecords that * are ignored by the WCS header parsers, wcspih() and wcsbth(). Typically the * latter may be set to remove WCS keyrecords from a header leaving fitshdr() @@ -73,7 +71,7 @@ * keyids[] provides a convienient way of indexing them. * The fitskeyid struct contains three members; * fitskeyid::name must be set by the user while -* fitskeyid::count and fitskeyid::name are returned by +* fitskeyid::count and fitskeyid::idx are returned by * fitshdr(). All matched keywords will have their * fitskey::keyno member negated. * @@ -87,8 +85,7 @@ * the header. * * Memory for the array is allocated by fitshdr() and -* this must be freed by the user by invoking free() on -* the array. +* this must be freed by the user. See wcsdealloc(). * * Function return value: * int Status return value: @@ -96,6 +93,7 @@ * 1: Null fitskey pointer passed. * 2: Memory allocation failed. * 3: Fatal error returned by Flex parser. +* 4: Unrecognised data type. * * Notes: * 1: Keyword parsing is done in accordance with the syntax defined by @@ -133,7 +131,7 @@ * * f: Sect. 5.2.3 offers no comment on the size of an integer keyvalue * except indirectly in limiting it to 70 digits. The parser will -* translates an integer keyvalue to a 32-bit signed integer if it +* translate an integer keyvalue to a 32-bit signed integer if it * lies in the range -2147483648 to +2147483647, otherwise it * interprets it as a 64-bit signed integer if possible, or else a * "very long" integer (see fitskey::type). @@ -249,7 +247,7 @@ * * int type * (Returned) Keyvalue data type: -* - 0: No keyvalue. +* - 0: No keyvalue (both the value and type are undefined). * - 1: Logical, represented as int. * - 2: 32-bit signed integer. * - 3: 64-bit signed integer (see below). @@ -384,12 +382,20 @@ extern "C" { #define FITSHDR_KEYVALUE 0x02 #define FITSHDR_COMMENT 0x04 #define FITSHDR_KEYREC 0x08 -#define FITSHDR_CARD 0x08 /* Alias for backwards compatibility. */ +#define FITSHDR_CARD 0x08 // Alias for backwards compatibility. #define FITSHDR_TRAILER 0x10 extern const char *fitshdr_errmsg[]; +enum fitshdr_errmsg_enum { + FITSHDRERR_SUCCESS = 0, // Success. + FITSHDRERR_NULL_POINTER = 1, // Null fitskey pointer passed. + FITSHDRERR_MEMORY = 2, // Memory allocation failed. + FITSHDRERR_FLEX_PARSER = 3, // Fatal error returned by Flex parser. + FITSHDRERR_DATA_TYPE = 4 // Unrecognised data type. +}; + #ifdef WCSLIB_INT64 typedef WCSLIB_INT64 int64; #else @@ -397,38 +403,38 @@ extern const char *fitshdr_errmsg[]; #endif -/* Struct used for indexing the keywords. */ +// Struct used for indexing the keywords. struct fitskeyid { - char name[12]; /* Keyword name, null-terminated. */ - int count; /* Number of occurrences of keyword. */ - int idx[2]; /* Indices into fitskey array. */ + char name[12]; // Keyword name, null-terminated. + int count; // Number of occurrences of keyword. + int idx[2]; // Indices into fitskey array. }; -/* Size of the fitskeyid struct in int units, used by the Fortran wrappers. */ +// Size of the fitskeyid struct in int units, used by the Fortran wrappers. #define KEYIDLEN (sizeof(struct fitskeyid)/sizeof(int)) -/* Struct used for storing FITS keywords. */ +// Struct used for storing FITS keywords. struct fitskey { - int keyno; /* Header keyrecord sequence number (1-rel).*/ - int keyid; /* Index into fitskeyid[]. */ - int status; /* Header keyrecord status bit flags. */ - char keyword[12]; /* Keyword name, null-filled. */ - int type; /* Keyvalue type (see above). */ - int padding; /* (Dummy inserted for alignment purposes.) */ + int keyno; // Header keyrecord sequence number (1-rel). + int keyid; // Index into fitskeyid[]. + int status; // Header keyrecord status bit flags. + char keyword[12]; // Keyword name, null-filled. + int type; // Keyvalue type (see above). + int padding; // (Dummy inserted for alignment purposes.) union { - int i; /* 32-bit integer and logical values. */ - int64 k; /* 64-bit integer values. */ - int l[8]; /* Very long signed integer values. */ - double f; /* Floating point values. */ - double c[2]; /* Complex values. */ - char s[72]; /* String values, null-terminated. */ - } keyvalue; /* Keyvalue. */ - int ulen; /* Length of units string. */ - char comment[84]; /* Comment (or keyrecord), null-terminated. */ + int i; // 32-bit integer and logical values. + int64 k; // 64-bit integer values. + int l[8]; // Very long signed integer values. + double f; // Floating point values. + double c[2]; // Complex values. + char s[72]; // String values, null-terminated. + } keyvalue; // Keyvalue. + int ulen; // Length of units string. + char comment[84]; // Comment (or keyrecord), null-terminated. }; -/* Size of the fitskey struct in int units, used by the Fortran wrappers. */ +// Size of the fitskey struct in int units, used by the Fortran wrappers. #define KEYLEN (sizeof(struct fitskey)/sizeof(int)) @@ -440,4 +446,4 @@ int fitshdr(const char header[], int nkeyrec, int nkeyids, } #endif -#endif /* WCSLIB_FITSHDR */ +#endif // WCSLIB_FITSHDR diff --git a/cextern/wcslib/C/fitshdr.l b/cextern/wcslib/C/fitshdr.l index d19735d8496f..32cfcf06bbde 100644 --- a/cextern/wcslib/C/fitshdr.l +++ b/cextern/wcslib/C/fitshdr.l @@ -1,7 +1,6 @@ /*============================================================================ - - WCSLIB 4.14 - an implementation of the FITS WCS standard. - Copyright (C) 1995-2012, Mark Calabretta + WCSLIB 8.6 - an implementation of the FITS WCS standard. + Copyright (C) 1995-2026, Mark Calabretta This file is part of WCSLIB. @@ -18,11 +17,9 @@ You should have received a copy of the GNU Lesser General Public License along with WCSLIB. If not, see http://www.gnu.org/licenses. - Direct correspondence concerning WCSLIB to mark@calabretta.id.au - Author: Mark Calabretta, Australia Telescope National Facility, CSIRO. - http://www.atnf.csiro.au/people/Mark.Calabretta - $Id: fitshdr.l,v 4.14 2012/07/13 10:02:27 cal103 Exp $ + http://www.atnf.csiro.au/computing/software/wcs + $Id: fitshdr.l,v 8.6 2026/03/29 13:53:56 mcalabre Exp $ *============================================================================= * * fitshdr.l is a Flex description file containing a lexical scanner @@ -38,10 +35,13 @@ /* Options. */ %option full %option never-interactive +%option noinput %option nounput %option noyywrap %option outfile="fitshdr.c" %option prefix="fitshdr" +%option reentrant +%option extra-type="struct fitshdr_extra *" /* Keywords. */ KEYCHR [-_A-Z0-9] @@ -61,7 +61,7 @@ INT32 [+-]?0*[0-9]{1,9} INT64 [+-]?0*[0-9]{10,18} INTVL [+-]?0*[0-9]{19,} INTEGER [+-]?[0-9]+ -FLOAT [+-]?([0-9]+\.?[0-9]*|\.[0-9]+)([eE][+-]?[0-9]+)? +FLOAT [+-]?([0-9]+\.?[0-9]*|\.[0-9]+)([eEdD][+-]?[0-9]+)? ICOMPLX \(" "*{INTEGER}" "*," "*{INTEGER}" "*\) FCOMPLX \(" "*{FLOAT}" "*," "*{FLOAT}" "*\) STRING '([^']|'')*' @@ -82,32 +82,41 @@ UNITSTR \[[-+*/^(). 0-9a-zA-Z]+\] #include "fitshdr.h" #include "wcsutil.h" -#define YY_DECL int fitshdr(const char header[], int nkeyrec, int nkeyids, \ - struct fitskeyid keyids[], int *nreject, \ - struct fitskey **keys) +// User data associated with yyscanner. +struct fitshdr_extra { + // Values passed to YY_INPUT. + const char *hdr; + int nkeyrec; + + // Used in preempting the call to exit() by yy_fatal_error(). + jmp_buf abort_jmp_env; +}; + +#define YY_DECL int fitshdr_scanner(const char header[], int nkeyrec, \ + int nkeyids, struct fitskeyid keyids[], int *nreject, \ + struct fitskey **keys, yyscan_t yyscanner) #define YY_INPUT(inbuff, count, bufsize) \ { \ - if (fitshdr_nkeyrec) { \ - strncpy(inbuff, fitshdr_hdr, 80); \ + if (yyextra->nkeyrec) { \ + strncpy(inbuff, yyextra->hdr, 80); \ inbuff[80] = '\n'; \ - fitshdr_hdr += 80; \ - fitshdr_nkeyrec--; \ + yyextra->hdr += 80; \ + yyextra->nkeyrec--; \ count = 81; \ } else { \ count = YY_NULL; \ } \ } -/* These global variables are required by YY_INPUT. */ -const char *fitshdr_hdr; -int fitshdr_nkeyrec; +// Preempt the call to exit() by yy_fatal_error(). +#define exit(status) longjmp(yyextra->abort_jmp_env, status); -/* Used in preempting the call to exit() by yy_fatal_error(). */ -jmp_buf fitshdr_abort_jmp_env; -#define exit(status) longjmp(fitshdr_abort_jmp_env, status) +// Internal helper functions. +static YY_DECL; +static void nullfill(char cptr[], int len); -/* Map status return value to message. */ +// Map status return value to message. const char *fitshdr_errmsg[] = { "Success", "Null fitskey pointer-pointer passed", @@ -117,51 +126,65 @@ const char *fitshdr_errmsg[] = { %} %% - char *cptr, ctmp[72]; - int blank, continuation, end, j, k, keyno; - double dtmp; - struct fitskey *kptr; - struct fitskeyid *iptr; - void nullfill(char cptr[], int len); - int yylex_destroy(void); - - fitshdr_hdr = header; - fitshdr_nkeyrec = nkeyrec; - - *nreject = 0; - keyno = 0; + char ctmp[72]; if (keys == 0x0) { - return 1; + return FITSHDRERR_NULL_POINTER; } - /* Allocate memory for the required number of fitskey structs. */ - /* Recall that calloc() initializes allocated memory to zero. */ + // Allocate memory for the required number of fitskey structs. + // Recall that calloc() initializes allocated memory to zero. + struct fitskey *kptr; if (!(kptr = *keys = calloc(nkeyrec, sizeof(struct fitskey)))) { - return 2; + return FITSHDRERR_MEMORY; } - /* Initialize keyids[]. */ - iptr = keyids; - for (j = 0; j < nkeyids; j++, iptr++) { + // Initialize returned values. + *nreject = 0; + + // Initialize keyids[]. + struct fitskeyid *iptr = keyids; + for (int j = 0; j < nkeyids; j++, iptr++) { iptr->count = 0; iptr->idx[0] = -1; iptr->idx[1] = -1; } + + int keyno = 0; - blank = 0; - continuation = 0; - end = 0; + int blank = 0; + int continuation = 0; + int end = 0; - /* Return here via longjmp() invoked by yy_fatal_error(). */ - if (setjmp(fitshdr_abort_jmp_env)) { - return 3; + #ifdef WCSLIB_INT64 + #define asString(S) stringize(S) + #define stringize(S) #S + + const char *int64fmt; + if (strcmp(asString(WCSLIB_INT64), "long long int") == 0) { + int64fmt = "%lld"; + } else if (strcmp(asString(WCSLIB_INT64), "long int") == 0) { + int64fmt = "%ld"; + } else if (strcmp(asString(WCSLIB_INT64), "int") == 0) { + int64fmt = "%d"; + } else { + return FITSHDRERR_DATA_TYPE; + } + #endif + + // User data associated with yyscanner. + yyextra->hdr = header; + yyextra->nkeyrec = nkeyrec; + + // Return here via longjmp() invoked by yy_fatal_error(). + if (setjmp(yyextra->abort_jmp_env)) { + return FITSHDRERR_FLEX_PARSER; } BEGIN(INITIAL); ^" "{80} { - /* A completely blank keyrecord. */ + // A completely blank keyrecord. strncpy(kptr->keyword, yytext, 8); yyless(0); blank = 1; @@ -180,14 +203,14 @@ const char *fitshdr_errmsg[] = { } ^END" "{5}=" "+ { - /* Illegal END keyrecord. */ + // Illegal END keyrecord. strncpy(kptr->keyword, yytext, 8); kptr->status |= FITSHDR_KEYREC; BEGIN(VALUE); } ^END" "{5} { - /* Illegal END keyrecord. */ + // Illegal END keyrecord. strncpy(kptr->keyword, yytext, 8); kptr->status |= FITSHDR_KEYREC; BEGIN(COMMENT); @@ -199,57 +222,58 @@ const char *fitshdr_errmsg[] = { } ^CONTINUE" "+{STRING} { - /* Continued string keyvalue. */ + // Continued string keyvalue. strncpy(kptr->keyword, yytext, 8); if (keyno > 0 && (kptr-1)->type%10 == 8) { - /* Put back the string keyvalue. */ + // Put back the string keyvalue. + int k; for (k = 10; yytext[k] != '\''; k++); yyless(k); continuation = 1; BEGIN(VALUE); } else { - /* Not a valid continuation. */ + // Not a valid continuation. yyless(8); BEGIN(COMMENT); } } ^{KEYWORD} { - /* Keyword without value. */ + // Keyword without value. strncpy(kptr->keyword, yytext, 8); BEGIN(COMMENT); } ^.{8}=" "+ { - /* Illegal keyword, carry on regardless. */ + // Illegal keyword, carry on regardless. strncpy(kptr->keyword, yytext, 8); kptr->status |= FITSHDR_KEYWORD; BEGIN(VALUE); } ^.{8} { - /* Illegal keyword, carry on regardless. */ + // Illegal keyword, carry on regardless. strncpy(kptr->keyword, yytext, 8); kptr->status |= FITSHDR_KEYWORD; BEGIN(COMMENT); } " "*/\/ { - /* Null keyvalue. */ + // Null keyvalue. BEGIN(INLINE); } {LOGICAL} { - /* Logical keyvalue. */ + // Logical keyvalue. kptr->type = 1; kptr->keyvalue.i = (*yytext == 'T'); BEGIN(INLINE); } {INT32} { - /* 32-bit signed integer keyvalue. */ + // 32-bit signed integer keyvalue. kptr->type = 2; if (sscanf(yytext, "%d", &(kptr->keyvalue.i)) < 1) { kptr->status |= FITSHDR_KEYVALUE; @@ -260,13 +284,14 @@ const char *fitshdr_errmsg[] = { } {INT64} { - /* 64-bit signed integer keyvalue (up to 18 digits). */ - if (wcsutil_str2double(yytext, "%lf", &dtmp)) { + // 64-bit signed integer keyvalue (up to 18 digits). + double dtmp; + if (wcsutil_str2double(yytext, &dtmp)) { kptr->status |= FITSHDR_KEYVALUE; BEGIN(ERROR); } else if (INT_MIN <= dtmp && dtmp <= INT_MAX) { - /* Can be accomodated as a 32-bit signed integer. */ + // Can be accomodated as a 32-bit signed integer. kptr->type = 2; if (sscanf(yytext, "%d", &(kptr->keyvalue.i)) < 1) { kptr->status |= FITSHDR_KEYVALUE; @@ -274,16 +299,16 @@ const char *fitshdr_errmsg[] = { } } else { - /* 64-bit signed integer. */ + // 64-bit signed integer. kptr->type = 3; #ifdef WCSLIB_INT64 - /* Native 64-bit integer is available. */ - if (sscanf(yytext, "%lld", &(kptr->keyvalue.k)) < 1) { + // Native 64-bit integer is available. + if (sscanf(yytext, int64fmt, &(kptr->keyvalue.k)) < 1) { kptr->status |= FITSHDR_KEYVALUE; BEGIN(ERROR); } #else - /* 64-bit integer (up to 18 digits) implemented as int[3]. */ + // 64-bit integer (up to 18 digits) implemented as int[3]. kptr->keyvalue.k[2] = 0; sprintf(ctmp, "%%%dd%%9d", yyleng-9); @@ -301,12 +326,12 @@ const char *fitshdr_errmsg[] = { } {INTVL} { - /* Very long integer keyvalue (and 19-digit int64). */ + // Very long integer keyvalue (and 19-digit int64). kptr->type = 4; strcpy(ctmp, yytext); - k = yyleng; + int j, k = yyleng; for (j = 0; j < 8; j++) { - /* Read it backwards. */ + // Read it backwards. k -= 9; if (k < 0) k = 0; if (sscanf(ctmp+k, "%d", kptr->keyvalue.l+j) < 1) { @@ -321,7 +346,7 @@ const char *fitshdr_errmsg[] = { ctmp[k] = '\0'; } - /* Can it be accomodated as a 64-bit signed integer? */ + // Can it be accomodated as a 64-bit signed integer? if (j == 2 && abs(kptr->keyvalue.l[2]) <= 9 && abs(kptr->keyvalue.l[1]) <= 223372036 && kptr->keyvalue.l[0] <= 854775807 && @@ -329,9 +354,9 @@ const char *fitshdr_errmsg[] = { kptr->type = 3; #ifdef WCSLIB_INT64 - /* Native 64-bit integer is available. */ + // Native 64-bit integer is available. kptr->keyvalue.l[2] = 0; - if (sscanf(yytext, "%lld", &(kptr->keyvalue.k)) < 1) { + if (sscanf(yytext, int64fmt, &(kptr->keyvalue.k)) < 1) { kptr->status |= FITSHDR_KEYVALUE; BEGIN(ERROR); } @@ -342,9 +367,9 @@ const char *fitshdr_errmsg[] = { } {FLOAT} { - /* Float keyvalue. */ + // Float keyvalue. kptr->type = 5; - if (wcsutil_str2double(yytext, "%lf", &(kptr->keyvalue.f))) { + if (wcsutil_str2double(yytext, &(kptr->keyvalue.f))) { kptr->status |= FITSHDR_KEYVALUE; BEGIN(ERROR); } @@ -353,7 +378,7 @@ const char *fitshdr_errmsg[] = { } {ICOMPLX} { - /* Integer complex keyvalue. */ + // Integer complex keyvalue. kptr->type = 6; if (sscanf(yytext, "(%lf,%lf)", kptr->keyvalue.c, kptr->keyvalue.c+1) < 2) { @@ -365,15 +390,17 @@ const char *fitshdr_errmsg[] = { } {FCOMPLX} { - /* Floating point complex keyvalue. */ + // Floating point complex keyvalue. kptr->type = 7; + char *cptr; + int k; for (cptr = ctmp, k = 1; yytext[k] != ','; cptr++, k++) { *cptr = yytext[k]; } *cptr = '\0'; - if (wcsutil_str2double(ctmp, "%lf", kptr->keyvalue.c)) { + if (wcsutil_str2double(ctmp, kptr->keyvalue.c)) { kptr->status |= FITSHDR_KEYVALUE; BEGIN(ERROR); } @@ -383,7 +410,7 @@ const char *fitshdr_errmsg[] = { } *cptr = '\0'; - if (wcsutil_str2double(ctmp, "%lf", kptr->keyvalue.c+1)) { + if (wcsutil_str2double(ctmp, kptr->keyvalue.c+1)) { kptr->status |= FITSHDR_KEYVALUE; BEGIN(ERROR); } @@ -392,14 +419,14 @@ const char *fitshdr_errmsg[] = { } {STRING} { - /* String keyvalue. */ + // String keyvalue. kptr->type = 8; - cptr = kptr->keyvalue.s; + char *cptr = kptr->keyvalue.s; strcpy(cptr, yytext+1); - /* Squeeze out repeated quotes. */ - k = 0; - for (j = 0; j < 72; j++) { + // Squeeze out repeated quotes. + int k = 0; + for (int j = 0; j < 72; j++) { if (k < j) { cptr[k] = cptr[j]; } @@ -415,7 +442,7 @@ const char *fitshdr_errmsg[] = { } if (*cptr) { - /* Retain the initial blank in all-blank strings. */ + // Retain the initial blank in all-blank strings. nullfill(cptr+1, 71); } else { nullfill(cptr, 72); @@ -447,7 +474,7 @@ const char *fitshdr_errmsg[] = { } . { - /* Keyvalue parsing must now also be suspect. */ + // Keyvalue parsing must now also be suspect. kptr->status |= FITSHDR_COMMENT; kptr->type = 0; BEGIN(ERROR); @@ -473,7 +500,7 @@ const char *fitshdr_errmsg[] = { .* { if (!continuation) kptr->type = -abs(kptr->type); - sprintf(kptr->comment, "%.80s", fitshdr_hdr-80); + sprintf(kptr->comment, "%.80s", yyextra->hdr-80); kptr->comment[80] = '\0'; nullfill(kptr->comment+80, 4); @@ -481,18 +508,19 @@ const char *fitshdr_errmsg[] = { } .*\n { - /* Discard the rest of the input line. */ + // Discard the rest of the input line. kptr->keyno = ++keyno; - /* Null-fill the keyword. */ + // Null-fill the keyword. kptr->keyword[8] = '\0'; nullfill(kptr->keyword, 12); - /* Do indexing. */ + // Do indexing. iptr = keyids; kptr->keyid = -1; - for (j = 0; j < nkeyids; j++, iptr++) { - cptr = iptr->name; + for (int j = 0; j < nkeyids; j++, iptr++) { + int k; + char *cptr = iptr->name; cptr[8] = '\0'; nullfill(cptr, 12); for (k = 0; k < 8; k++, cptr++) { @@ -500,7 +528,7 @@ const char *fitshdr_errmsg[] = { } if (k == 8) { - /* Found a match. */ + // Found a match. iptr->count++; if (iptr->idx[0] == -1) { iptr->idx[0] = keyno-1; @@ -513,17 +541,17 @@ const char *fitshdr_errmsg[] = { } } - /* Deal with continued strings. */ + // Deal with continued strings. if (continuation) { - /* Tidy up the previous string keyvalue. */ + // Tidy up the previous string keyvalue. if ((kptr-1)->type == 8) (kptr-1)->type += 10; - cptr = (kptr-1)->keyvalue.s; + char *cptr = (kptr-1)->keyvalue.s; if (cptr[strlen(cptr)-1] == '&') cptr[strlen(cptr)-1] = '\0'; kptr->type = (kptr-1)->type + 10; } - /* Check for keyrecords following the END keyrecord. */ + // Check for keyrecords following the END keyrecord. if (end && (end++ > 1) && !blank) { kptr->status |= FITSHDR_TRAILER; } @@ -537,31 +565,59 @@ const char *fitshdr_errmsg[] = { } <> { - /* End-of-input. */ - yylex_destroy(); + // End-of-input. return 0; } %% -/*--------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------- +* External interface to the scanner. +*---------------------------------------------------------------------------*/ -void nullfill(char cptr[], int len) +int fitshdr( + const char header[], + int nkeyrec, + int nkeyids, + struct fitskeyid keyids[], + int *nreject, + struct fitskey **keys) { - int j, k; + // Function prototypes. + int yylex_init_extra(YY_EXTRA_TYPE extra, yyscan_t *yyscanner); + int yylex_destroy(yyscan_t yyscanner); + + struct fitshdr_extra extra; + yyscan_t yyscanner; + yylex_init_extra(&extra, &yyscanner); + int status = fitshdr_scanner(header, nkeyrec, nkeyids, keyids, nreject, + keys, yyscanner); + yylex_destroy(yyscanner); + + return status; +} + +/*---------------------------------------------------------------------------- +* Pad a string with null characters. +*---------------------------------------------------------------------------*/ - /* Null-fill the string. */ +void nullfill(char cptr[], int len) + +{ + // Propagate the terminating null to the end of the string. + int j; for (j = 0; j < len; j++) { if (cptr[j] == '\0') { - for (k = j+1; k < len; k++) { + for (int k = j+1; k < len; k++) { cptr[k] = '\0'; } break; } } - for (k = j-1; k >= 0; k--) { + // Remove trailing blanks. + for (int k = j-1; k >= 0; k--) { if (cptr[k] != ' ') break; cptr[k] = '\0'; } diff --git a/cextern/wcslib/C/flexed/README b/cextern/wcslib/C/flexed/README index 8ca23ad438d8..17ba34b6a07f 100644 --- a/cextern/wcslib/C/flexed/README +++ b/cextern/wcslib/C/flexed/README @@ -1,4 +1,6 @@ -This directory contains C code generated by flex 2.5.35 under Debian -lenny from the Flex description files (*.l) in the parent directory. +This directory contains C code generated by flex 2.6.4 under KDE Neon User +Edition 5.19 (Kubuntu 18.04) from the Flex description files (*.l) in the +parent directory. + These pre-generated source files may be used during installation if Flex 2.5.9 or later is not available on the build host. diff --git a/cextern/wcslib/C/flexed/fitshdr.c b/cextern/wcslib/C/flexed/fitshdr.c index 8f0710b728c0..dc39ecacca23 100644 --- a/cextern/wcslib/C/flexed/fitshdr.c +++ b/cextern/wcslib/C/flexed/fitshdr.c @@ -2,35 +2,227 @@ #line 4 "fitshdr.c" +#define _POSIX_C_SOURCE 1 #define YY_INT_ALIGNED short int /* A lexical scanner generated by flex */ +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 6 +#define YY_FLEX_SUBMINOR_VERSION 4 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +#ifdef yy_create_buffer +#define fitshdr_create_buffer_ALREADY_DEFINED +#else #define yy_create_buffer fitshdr_create_buffer +#endif + +#ifdef yy_delete_buffer +#define fitshdr_delete_buffer_ALREADY_DEFINED +#else #define yy_delete_buffer fitshdr_delete_buffer -#define yy_flex_debug fitshdr_flex_debug +#endif + +#ifdef yy_scan_buffer +#define fitshdr_scan_buffer_ALREADY_DEFINED +#else +#define yy_scan_buffer fitshdr_scan_buffer +#endif + +#ifdef yy_scan_string +#define fitshdr_scan_string_ALREADY_DEFINED +#else +#define yy_scan_string fitshdr_scan_string +#endif + +#ifdef yy_scan_bytes +#define fitshdr_scan_bytes_ALREADY_DEFINED +#else +#define yy_scan_bytes fitshdr_scan_bytes +#endif + +#ifdef yy_init_buffer +#define fitshdr_init_buffer_ALREADY_DEFINED +#else #define yy_init_buffer fitshdr_init_buffer +#endif + +#ifdef yy_flush_buffer +#define fitshdr_flush_buffer_ALREADY_DEFINED +#else #define yy_flush_buffer fitshdr_flush_buffer +#endif + +#ifdef yy_load_buffer_state +#define fitshdr_load_buffer_state_ALREADY_DEFINED +#else #define yy_load_buffer_state fitshdr_load_buffer_state +#endif + +#ifdef yy_switch_to_buffer +#define fitshdr_switch_to_buffer_ALREADY_DEFINED +#else #define yy_switch_to_buffer fitshdr_switch_to_buffer -#define yyin fitshdrin -#define yyleng fitshdrleng +#endif + +#ifdef yypush_buffer_state +#define fitshdrpush_buffer_state_ALREADY_DEFINED +#else +#define yypush_buffer_state fitshdrpush_buffer_state +#endif + +#ifdef yypop_buffer_state +#define fitshdrpop_buffer_state_ALREADY_DEFINED +#else +#define yypop_buffer_state fitshdrpop_buffer_state +#endif + +#ifdef yyensure_buffer_stack +#define fitshdrensure_buffer_stack_ALREADY_DEFINED +#else +#define yyensure_buffer_stack fitshdrensure_buffer_stack +#endif + +#ifdef yylex +#define fitshdrlex_ALREADY_DEFINED +#else #define yylex fitshdrlex -#define yylineno fitshdrlineno -#define yyout fitshdrout +#endif + +#ifdef yyrestart +#define fitshdrrestart_ALREADY_DEFINED +#else #define yyrestart fitshdrrestart -#define yytext fitshdrtext +#endif + +#ifdef yylex_init +#define fitshdrlex_init_ALREADY_DEFINED +#else +#define yylex_init fitshdrlex_init +#endif + +#ifdef yylex_init_extra +#define fitshdrlex_init_extra_ALREADY_DEFINED +#else +#define yylex_init_extra fitshdrlex_init_extra +#endif + +#ifdef yylex_destroy +#define fitshdrlex_destroy_ALREADY_DEFINED +#else +#define yylex_destroy fitshdrlex_destroy +#endif + +#ifdef yyget_debug +#define fitshdrget_debug_ALREADY_DEFINED +#else +#define yyget_debug fitshdrget_debug +#endif + +#ifdef yyset_debug +#define fitshdrset_debug_ALREADY_DEFINED +#else +#define yyset_debug fitshdrset_debug +#endif + +#ifdef yyget_extra +#define fitshdrget_extra_ALREADY_DEFINED +#else +#define yyget_extra fitshdrget_extra +#endif + +#ifdef yyset_extra +#define fitshdrset_extra_ALREADY_DEFINED +#else +#define yyset_extra fitshdrset_extra +#endif + +#ifdef yyget_in +#define fitshdrget_in_ALREADY_DEFINED +#else +#define yyget_in fitshdrget_in +#endif + +#ifdef yyset_in +#define fitshdrset_in_ALREADY_DEFINED +#else +#define yyset_in fitshdrset_in +#endif + +#ifdef yyget_out +#define fitshdrget_out_ALREADY_DEFINED +#else +#define yyget_out fitshdrget_out +#endif + +#ifdef yyset_out +#define fitshdrset_out_ALREADY_DEFINED +#else +#define yyset_out fitshdrset_out +#endif + +#ifdef yyget_leng +#define fitshdrget_leng_ALREADY_DEFINED +#else +#define yyget_leng fitshdrget_leng +#endif + +#ifdef yyget_text +#define fitshdrget_text_ALREADY_DEFINED +#else +#define yyget_text fitshdrget_text +#endif + +#ifdef yyget_lineno +#define fitshdrget_lineno_ALREADY_DEFINED +#else +#define yyget_lineno fitshdrget_lineno +#endif + +#ifdef yyset_lineno +#define fitshdrset_lineno_ALREADY_DEFINED +#else +#define yyset_lineno fitshdrset_lineno +#endif + +#ifdef yyget_column +#define fitshdrget_column_ALREADY_DEFINED +#else +#define yyget_column fitshdrget_column +#endif + +#ifdef yyset_column +#define fitshdrset_column_ALREADY_DEFINED +#else +#define yyset_column fitshdrset_column +#endif + +#ifdef yywrap +#define fitshdrwrap_ALREADY_DEFINED +#else #define yywrap fitshdrwrap +#endif + +#ifdef yyalloc +#define fitshdralloc_ALREADY_DEFINED +#else #define yyalloc fitshdralloc +#endif + +#ifdef yyrealloc +#define fitshdrrealloc_ALREADY_DEFINED +#else #define yyrealloc fitshdrrealloc -#define yyfree fitshdrfree +#endif -#define FLEX_SCANNER -#define YY_FLEX_MAJOR_VERSION 2 -#define YY_FLEX_MINOR_VERSION 5 -#define YY_FLEX_SUBMINOR_VERSION 35 -#if YY_FLEX_SUBMINOR_VERSION > 0 -#define FLEX_BETA +#ifdef yyfree +#define fitshdrfree_ALREADY_DEFINED +#else +#define yyfree fitshdrfree #endif /* First, we deal with platform-specific or compiler-specific issues. */ @@ -103,60 +295,65 @@ typedef unsigned int flex_uint32_t; #define UINT32_MAX (4294967295U) #endif +#ifndef SIZE_MAX +#define SIZE_MAX (~(size_t)0) +#endif + #endif /* ! C99 */ #endif /* ! FLEXINT_H */ -#ifdef __cplusplus - -/* The "const" storage-class-modifier is valid. */ -#define YY_USE_CONST - -#else /* ! __cplusplus */ - -/* C99 requires __STDC__ to be defined as 1. */ -#if defined (__STDC__) +/* begin standard C++ headers. */ -#define YY_USE_CONST - -#endif /* defined (__STDC__) */ -#endif /* ! __cplusplus */ - -#ifdef YY_USE_CONST +/* TODO: this is always defined, so inline it */ #define yyconst const + +#if defined(__GNUC__) && __GNUC__ >= 3 +#define yynoreturn __attribute__((__noreturn__)) #else -#define yyconst +#define yynoreturn #endif /* Returned upon end-of-file. */ #define YY_NULL 0 -/* Promotes a possibly negative, possibly signed char to an unsigned - * integer for use as an array index. If the signed char is negative, - * we want to instead treat it as an 8-bit unsigned char, hence the - * double cast. +/* Promotes a possibly negative, possibly signed char to an + * integer in range [0..255] for use as an array index. */ -#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) +#define YY_SC_TO_UI(c) ((YY_CHAR) (c)) + +/* An opaque pointer. */ +#ifndef YY_TYPEDEF_YY_SCANNER_T +#define YY_TYPEDEF_YY_SCANNER_T +typedef void* yyscan_t; +#endif + +/* For convenience, these vars (plus the bison vars far below) + are macros in the reentrant scanner. */ +#define yyin yyg->yyin_r +#define yyout yyg->yyout_r +#define yyextra yyg->yyextra_r +#define yyleng yyg->yyleng_r +#define yytext yyg->yytext_r +#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno) +#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column) +#define yy_flex_debug yyg->yy_flex_debug_r /* Enter a start condition. This macro really ought to take a parameter, * but we do it the disgusting crufty way forced on us by the ()-less * definition of BEGIN. */ -#define BEGIN (yy_start) = 1 + 2 * - +#define BEGIN yyg->yy_start = 1 + 2 * /* Translate the current start state into a value that can be later handed * to BEGIN to return to the state. The YYSTATE alias is for lex * compatibility. */ -#define YY_START (((yy_start) - 1) / 2) +#define YY_START ((yyg->yy_start - 1) / 2) #define YYSTATE YY_START - /* Action number for EOF rule of a given start state. */ #define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) - /* Special action meaning "start processing a new file". */ -#define YY_NEW_FILE fitshdrrestart(fitshdrin ) - +#define YY_NEW_FILE yyrestart( yyin , yyscanner ) #define YY_END_OF_BUFFER_CHAR 0 /* Size of default input buffer. */ @@ -181,36 +378,32 @@ typedef unsigned int flex_uint32_t; typedef struct yy_buffer_state *YY_BUFFER_STATE; #endif -extern int fitshdrleng; - -extern FILE *fitshdrin, *fitshdrout; +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef size_t yy_size_t; +#endif #define EOB_ACT_CONTINUE_SCAN 0 #define EOB_ACT_END_OF_FILE 1 #define EOB_ACT_LAST_MATCH 2 - + #define YY_LESS_LINENO(n) +#define YY_LINENO_REWIND_TO(ptr) /* Return all but the first "n" matched characters back to the input stream. */ #define yyless(n) \ do \ { \ - /* Undo effects of setting up fitshdrtext. */ \ + /* Undo effects of setting up yytext. */ \ int yyless_macro_arg = (n); \ YY_LESS_LINENO(yyless_macro_arg);\ - *yy_cp = (yy_hold_char); \ + *yy_cp = yyg->yy_hold_char; \ YY_RESTORE_YY_MORE_OFFSET \ - (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ - YY_DO_BEFORE_ACTION; /* set up fitshdrtext again */ \ + yyg->yy_c_buf_p = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ + YY_DO_BEFORE_ACTION; /* set up yytext again */ \ } \ while ( 0 ) - -#define unput(c) yyunput( c, (yytext_ptr) ) - -#ifndef YY_TYPEDEF_YY_SIZE_T -#define YY_TYPEDEF_YY_SIZE_T -typedef size_t yy_size_t; -#endif +#define unput(c) yyunput( c, yyg->yytext_ptr , yyscanner ) #ifndef YY_STRUCT_YY_BUFFER_STATE #define YY_STRUCT_YY_BUFFER_STATE @@ -224,7 +417,7 @@ struct yy_buffer_state /* Size of input buffer in bytes, not including room for EOB * characters. */ - yy_size_t yy_buf_size; + int yy_buf_size; /* Number of characters read into yy_ch_buf, not including EOB * characters. @@ -252,7 +445,7 @@ struct yy_buffer_state int yy_bs_lineno; /**< The line count. */ int yy_bs_column; /**< The column count. */ - + /* Whether to try to fill the input buffer when we reach the * end of it. */ @@ -269,113 +462,81 @@ struct yy_buffer_state * possible backing-up. * * When we actually see the EOF, we change the status to "new" - * (via fitshdrrestart()), so that the user can continue scanning by - * just pointing fitshdrin at a new input file. + * (via yyrestart()), so that the user can continue scanning by + * just pointing yyin at a new input file. */ #define YY_BUFFER_EOF_PENDING 2 }; #endif /* !YY_STRUCT_YY_BUFFER_STATE */ -/* Stack of input buffers. */ -static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */ -static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */ -static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */ - /* We provide macros for accessing buffer states in case in the * future we want to put the buffer states in a more general * "scanner state". * * Returns the top of the stack, or NULL. */ -#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \ - ? (yy_buffer_stack)[(yy_buffer_stack_top)] \ +#define YY_CURRENT_BUFFER ( yyg->yy_buffer_stack \ + ? yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] \ : NULL) - /* Same as previous macro, but useful when we know that the buffer stack is not * NULL or when we need an lvalue. For internal use only. */ -#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)] - -/* yy_hold_char holds the character lost when fitshdrtext is formed. */ -static char yy_hold_char; -static int yy_n_chars; /* number of characters read into yy_ch_buf */ -int fitshdrleng; - -/* Points to current character in buffer. */ -static char *yy_c_buf_p = (char *) 0; -static int yy_init = 0; /* whether we need to initialize */ -static int yy_start = 0; /* start state number */ - -/* Flag which is used to allow fitshdrwrap()'s to do buffer switches - * instead of setting up a fresh fitshdrin. A bit of a hack ... - */ -static int yy_did_buffer_switch_on_eof; - -void fitshdrrestart (FILE *input_file ); -void fitshdr_switch_to_buffer (YY_BUFFER_STATE new_buffer ); -YY_BUFFER_STATE fitshdr_create_buffer (FILE *file,int size ); -void fitshdr_delete_buffer (YY_BUFFER_STATE b ); -void fitshdr_flush_buffer (YY_BUFFER_STATE b ); -void fitshdrpush_buffer_state (YY_BUFFER_STATE new_buffer ); -void fitshdrpop_buffer_state (void ); - -static void fitshdrensure_buffer_stack (void ); -static void fitshdr_load_buffer_state (void ); -static void fitshdr_init_buffer (YY_BUFFER_STATE b,FILE *file ); - -#define YY_FLUSH_BUFFER fitshdr_flush_buffer(YY_CURRENT_BUFFER ) - -YY_BUFFER_STATE fitshdr_scan_buffer (char *base,yy_size_t size ); -YY_BUFFER_STATE fitshdr_scan_string (yyconst char *yy_str ); -YY_BUFFER_STATE fitshdr_scan_bytes (yyconst char *bytes,int len ); - -void *fitshdralloc (yy_size_t ); -void *fitshdrrealloc (void *,yy_size_t ); -void fitshdrfree (void * ); - -#define yy_new_buffer fitshdr_create_buffer - +#define YY_CURRENT_BUFFER_LVALUE yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] + +void yyrestart ( FILE *input_file , yyscan_t yyscanner ); +void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size , yyscan_t yyscanner ); +void yy_delete_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yy_flush_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yypush_buffer_state ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +void yypop_buffer_state ( yyscan_t yyscanner ); + +static void yyensure_buffer_stack ( yyscan_t yyscanner ); +static void yy_load_buffer_state ( yyscan_t yyscanner ); +static void yy_init_buffer ( YY_BUFFER_STATE b, FILE *file , yyscan_t yyscanner ); +#define YY_FLUSH_BUFFER yy_flush_buffer( YY_CURRENT_BUFFER , yyscanner) + +YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_string ( const char *yy_str , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len , yyscan_t yyscanner ); + +void *yyalloc ( yy_size_t , yyscan_t yyscanner ); +void *yyrealloc ( void *, yy_size_t , yyscan_t yyscanner ); +void yyfree ( void * , yyscan_t yyscanner ); + +#define yy_new_buffer yy_create_buffer #define yy_set_interactive(is_interactive) \ { \ if ( ! YY_CURRENT_BUFFER ){ \ - fitshdrensure_buffer_stack (); \ + yyensure_buffer_stack (yyscanner); \ YY_CURRENT_BUFFER_LVALUE = \ - fitshdr_create_buffer(fitshdrin,YY_BUF_SIZE ); \ + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); \ } \ YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ } - #define yy_set_bol(at_bol) \ { \ if ( ! YY_CURRENT_BUFFER ){\ - fitshdrensure_buffer_stack (); \ + yyensure_buffer_stack (yyscanner); \ YY_CURRENT_BUFFER_LVALUE = \ - fitshdr_create_buffer(fitshdrin,YY_BUF_SIZE ); \ + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); \ } \ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ } - #define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) /* Begin user sect3 */ -#define fitshdrwrap(n) 1 +#define fitshdrwrap(yyscanner) (/*CONSTCOND*/1) #define YY_SKIP_YYWRAP - -typedef char YY_CHAR; - -FILE *fitshdrin = (FILE *) 0, *fitshdrout = (FILE *) 0; +typedef flex_uint8_t YY_CHAR; typedef int yy_state_type; -extern int fitshdrlineno; - -int fitshdrlineno = 1; +#define yytext_ptr yytext_r -extern char *fitshdrtext; -#define yytext_ptr fitshdrtext -static yyconst flex_int16_t yy_nxt[][128] = +static const flex_int16_t yy_nxt[][128] = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -903,12 +1064,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, 62, -30, 63, 64, 64, 64, 64, 64, 64, 64, 64, 64, -30, -30, - -30, -30, -30, -30, -30, -30, -30, -30, -30, 65, + -30, -30, -30, -30, -30, -30, -30, -30, 65, 65, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, - -30, 65, -30, -30, -30, -30, -30, -30, -30, -30, + 65, 65, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30 }, @@ -920,12 +1081,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, 62, -31, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, -31, -31, - -31, -31, -31, -31, -31, -31, -31, -31, -31, 65, + -31, -31, -31, -31, -31, -31, -31, -31, 65, 65, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, - -31, 65, -31, -31, -31, -31, -31, -31, -31, -31, + 65, 65, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31 }, @@ -1370,12 +1531,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -57, -57, 87, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, 88, -57, 89, -57, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, -57, -57, - -57, -57, -57, -57, -57, -57, -57, -57, -57, 91, + -57, -57, -57, -57, -57, -57, -57, -57, 91, 91, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, - -57, 91, -57, -57, -57, -57, -57, -57, -57, -57, + 91, 91, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57 }, @@ -1405,11 +1566,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, 62, -59, 63, 64, 64, 64, 64, 64, 64, 64, 64, 64, -59, -59, - -59, -59, -59, -59, -59, -59, -59, -59, -59, 65, + -59, -59, -59, -59, -59, -59, -59, -59, 65, 65, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, - -59, 65, -59, -59, -59, -59, -59, -59, -59, -59, + 65, 65, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59 @@ -1422,12 +1583,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, 62, -60, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, -60, -60, - -60, -60, -60, -60, -60, -60, -60, -60, -60, 65, + -60, -60, -60, -60, -60, -60, -60, -60, 65, 65, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, - -60, 65, -60, -60, -60, -60, -60, -60, -60, -60, + 65, 65, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60 }, @@ -1439,12 +1600,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, -61, -61, - -61, -61, -61, -61, -61, -61, -61, -61, -61, 65, + -61, -61, -61, -61, -61, -61, -61, -61, 65, 65, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, - -61, 65, -61, -61, -61, -61, -61, -61, -61, -61, + 65, 65, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61 }, @@ -1457,11 +1618,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -62, -62, -62, -62, -62, -62, -62, -62, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, -62, -62, - -62, -62, -62, -62, -62, -62, -62, -62, -62, 65, + -62, -62, -62, -62, -62, -62, -62, -62, 65, 65, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, - -62, 65, -62, -62, -62, -62, -62, -62, -62, -62, + 65, 65, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62 }, @@ -1474,11 +1635,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, 62, -63, 93, 94, 94, 94, 94, 94, 94, 94, 94, 94, -63, -63, - -63, -63, -63, -63, -63, -63, -63, -63, -63, 65, + -63, -63, -63, -63, -63, -63, -63, -63, 65, 65, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, - -63, 65, -63, -63, -63, -63, -63, -63, -63, -63, + 65, 65, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63 @@ -1491,12 +1652,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, 62, -64, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, -64, -64, - -64, -64, -64, -64, -64, -64, -64, -64, -64, 65, + -64, -64, -64, -64, -64, -64, -64, -64, 65, 65, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, - -64, 65, -64, -64, -64, -64, -64, -64, -64, -64, + 65, 65, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64 }, @@ -1526,11 +1687,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, 62, -66, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, -66, -66, - -66, -66, -66, -66, -66, -66, -66, -66, -66, 65, + -66, -66, -66, -66, -66, -66, -66, -66, 65, 65, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, - -66, 65, -66, -66, -66, -66, -66, -66, -66, -66, + 65, 65, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66 @@ -1872,11 +2033,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -86, -86, 110, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, 111, -86, -86, -86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, -86, -86, - -86, -86, -86, -86, -86, -86, -86, -86, -86, 91, + -86, -86, -86, -86, -86, -86, -86, -86, 91, 91, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, - -86, 91, -86, -86, -86, -86, -86, -86, -86, -86, + 91, 91, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86 @@ -1924,11 +2085,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -89, -89, 110, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, 111, -89, -89, -89, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, -89, -89, - -89, -89, -89, -89, -89, -89, -89, -89, -89, 91, + -89, -89, -89, -89, -89, -89, -89, -89, 91, 91, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, - -89, 91, -89, -89, -89, -89, -89, -89, -89, -89, + 91, 91, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89 @@ -1941,12 +2102,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -90, -90, 87, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, 88, -90, 89, -90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, -90, -90, - -90, -90, -90, -90, -90, -90, -90, -90, -90, 91, + -90, -90, -90, -90, -90, -90, -90, -90, 91, 91, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, - -90, 91, -90, -90, -90, -90, -90, -90, -90, -90, + 91, 91, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90 }, @@ -1976,11 +2137,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -92, -92, -92, -92, -92, -92, -92, -92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, -92, -92, - -92, -92, -92, -92, -92, -92, -92, -92, -92, 65, + -92, -92, -92, -92, -92, -92, -92, -92, 65, 65, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, - -92, 65, -92, -92, -92, -92, -92, -92, -92, -92, + 65, 65, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92 }, @@ -1993,11 +2154,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, 62, -93, 119, 120, 120, 120, 120, 120, 120, 120, 120, 120, -93, -93, - -93, -93, -93, -93, -93, -93, -93, -93, -93, 65, + -93, -93, -93, -93, -93, -93, -93, -93, 65, 65, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, - -93, 65, -93, -93, -93, -93, -93, -93, -93, -93, + 65, 65, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93 @@ -2010,12 +2171,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, 62, -94, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, -94, -94, - -94, -94, -94, -94, -94, -94, -94, -94, -94, 65, + -94, -94, -94, -94, -94, -94, -94, -94, 65, 65, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, - -94, 65, -94, -94, -94, -94, -94, -94, -94, -94, + 65, 65, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94 }, @@ -2028,11 +2189,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -95, -95, -95, -95, -95, -95, 62, -95, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, -95, -95, - -95, -95, -95, -95, -95, -95, -95, -95, -95, 65, + -95, -95, -95, -95, -95, -95, -95, -95, 65, 65, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, - -95, 65, -95, -95, -95, -95, -95, -95, -95, -95, + 65, 65, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95 }, @@ -2080,11 +2241,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -98, -98, -98, -98, -98, -98, 62, -98, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, -98, -98, - -98, -98, -98, -98, -98, -98, -98, -98, -98, 65, + -98, -98, -98, -98, -98, -98, -98, -98, 65, 65, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, - -98, 65, -98, -98, -98, -98, -98, -98, -98, -98, + 65, 65, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98 }, @@ -2374,11 +2535,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -115, 140, -115, -115, -115, -115, 141, -115, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, -115, -115, - -115, -115, -115, -115, -115, -115, -115, -115, -115, 143, + -115, -115, -115, -115, -115, -115, -115, -115, 143, 143, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, - -115, 143, -115, -115, -115, -115, -115, -115, -115, -115, + 143, 143, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115 }, @@ -2391,11 +2552,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -116, -116, 110, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, 111, -116, -116, -116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, -116, -116, - -116, -116, -116, -116, -116, -116, -116, -116, -116, 91, + -116, -116, -116, -116, -116, -116, -116, -116, 91, 91, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, - -116, 91, -116, -116, -116, -116, -116, -116, -116, -116, + 91, 91, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116 @@ -2443,11 +2604,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, 62, -119, 144, 145, 145, 145, 145, 145, 145, 145, 145, 145, -119, -119, - -119, -119, -119, -119, -119, -119, -119, -119, -119, 65, + -119, -119, -119, -119, -119, -119, -119, -119, 65, 65, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, - -119, 65, -119, -119, -119, -119, -119, -119, -119, -119, + 65, 65, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119 @@ -2460,12 +2621,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, 62, -120, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, -120, -120, - -120, -120, -120, -120, -120, -120, -120, -120, -120, 65, + -120, -120, -120, -120, -120, -120, -120, -120, 65, 65, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, - -120, 65, -120, -120, -120, -120, -120, -120, -120, -120, + 65, 65, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120 }, @@ -2477,12 +2638,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, 62, -121, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, -121, -121, - -121, -121, -121, -121, -121, -121, -121, -121, -121, 65, + -121, -121, -121, -121, -121, -121, -121, -121, 65, 65, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, - -121, 65, -121, -121, -121, -121, -121, -121, -121, -121, + 65, 65, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121 }, @@ -2495,11 +2656,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -122, -122, -122, -122, -122, -122, 62, -122, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, -122, -122, - -122, -122, -122, -122, -122, -122, -122, -122, -122, 65, + -122, -122, -122, -122, -122, -122, -122, -122, 65, 65, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, - -122, 65, -122, -122, -122, -122, -122, -122, -122, -122, + 65, 65, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122 }, @@ -2512,11 +2673,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, 62, -123, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, -123, -123, - -123, -123, -123, -123, -123, -123, -123, -123, -123, 65, + -123, -123, -123, -123, -123, -123, -123, -123, 65, 65, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, - -123, 65, -123, -123, -123, -123, -123, -123, -123, -123, + 65, 65, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123 @@ -2754,12 +2915,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -137, -137, 162, -137, -137, -137, -137, -137, -137, -137, -137, 163, -137, -137, -137, -137, 141, -137, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, -137, -137, - -137, -137, -137, -137, -137, -137, -137, -137, -137, 143, + -137, -137, -137, -137, -137, -137, -137, -137, 143, 143, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, - -137, 143, -137, -137, -137, -137, -137, -137, -137, -137, + 143, 143, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137 }, @@ -2772,11 +2933,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -138, 163, -138, -138, -138, -138, -138, -138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, -138, -138, - -138, -138, -138, -138, -138, -138, -138, -138, -138, 143, + -138, -138, -138, -138, -138, -138, -138, -138, 143, 143, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, - -138, 143, -138, -138, -138, -138, -138, -138, -138, -138, + 143, 143, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138 }, @@ -2823,12 +2984,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -141, -141, 162, -141, -141, -141, -141, -141, -141, -141, -141, 163, -141, -141, -141, -141, -141, -141, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, -141, -141, - -141, -141, -141, -141, -141, -141, -141, -141, -141, 143, + -141, -141, -141, -141, -141, -141, -141, -141, 143, 143, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, - -141, 143, -141, -141, -141, -141, -141, -141, -141, -141, + 143, 143, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141 }, @@ -2841,11 +3002,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -142, 140, -142, -142, -142, -142, 141, -142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, -142, -142, - -142, -142, -142, -142, -142, -142, -142, -142, -142, 143, + -142, -142, -142, -142, -142, -142, -142, -142, 143, 143, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, - -142, 143, -142, -142, -142, -142, -142, -142, -142, -142, + 143, 143, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142 }, @@ -2875,12 +3036,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, 62, -144, 168, 169, 169, 169, 169, 169, 169, 169, 169, 169, -144, -144, - -144, -144, -144, -144, -144, -144, -144, -144, -144, 65, + -144, -144, -144, -144, -144, -144, -144, -144, 65, 65, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, - -144, 65, -144, -144, -144, -144, -144, -144, -144, -144, + 65, 65, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144 }, @@ -2893,11 +3054,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -145, -145, -145, -145, -145, -145, 62, -145, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, -145, -145, - -145, -145, -145, -145, -145, -145, -145, -145, -145, 65, + -145, -145, -145, -145, -145, -145, -145, -145, 65, 65, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, - -145, 65, -145, -145, -145, -145, -145, -145, -145, -145, + 65, 65, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145 }, @@ -2910,11 +3071,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, 62, -146, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, -146, -146, - -146, -146, -146, -146, -146, -146, -146, -146, -146, 65, + -146, -146, -146, -146, -146, -146, -146, -146, 65, 65, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, - -146, 65, -146, -146, -146, -146, -146, -146, -146, -146, + 65, 65, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146 @@ -2927,12 +3088,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, 62, -147, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, -147, -147, - -147, -147, -147, -147, -147, -147, -147, -147, -147, 65, + -147, -147, -147, -147, -147, -147, -147, -147, 65, 65, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, - -147, 65, -147, -147, -147, -147, -147, -147, -147, -147, + 65, 65, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147 }, @@ -2945,11 +3106,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -148, -148, -148, -148, -148, -148, 62, -148, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, -148, -148, - -148, -148, -148, -148, -148, -148, -148, -148, -148, 65, + -148, -148, -148, -148, -148, -148, -148, -148, 65, 65, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, - -148, 65, -148, -148, -148, -148, -148, -148, -148, -148, + 65, 65, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148 }, @@ -2962,11 +3123,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, 62, -149, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, -149, -149, - -149, -149, -149, -149, -149, -149, -149, -149, -149, 65, + -149, -149, -149, -149, -149, -149, -149, -149, 65, 65, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, - -149, 65, -149, -149, -149, -149, -149, -149, -149, -149, + 65, 65, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149 @@ -3221,12 +3382,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -164, -164, 162, -164, -164, -164, -164, -164, -164, -164, -164, 163, -164, -164, -164, -164, 141, -164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, -164, -164, - -164, -164, -164, -164, -164, -164, -164, -164, -164, 143, + -164, -164, -164, -164, -164, -164, -164, -164, 143, 143, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, - -164, 143, -164, -164, -164, -164, -164, -164, -164, -164, + 143, 143, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164 }, @@ -3239,11 +3400,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -165, 163, -165, -165, -165, -165, -165, -165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, -165, -165, - -165, -165, -165, -165, -165, -165, -165, -165, -165, 143, + -165, -165, -165, -165, -165, -165, -165, -165, 143, 143, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, - -165, 143, -165, -165, -165, -165, -165, -165, -165, -165, + 143, 143, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165 }, @@ -3291,11 +3452,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -168, -168, -168, -168, -168, -168, 62, -168, 187, 188, 188, 188, 188, 188, 188, 188, 188, 188, -168, -168, - -168, -168, -168, -168, -168, -168, -168, -168, -168, 65, + -168, -168, -168, -168, -168, -168, -168, -168, 65, 65, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, - -168, 65, -168, -168, -168, -168, -168, -168, -168, -168, + 65, 65, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168 }, @@ -3308,11 +3469,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, 62, -169, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, -169, -169, - -169, -169, -169, -169, -169, -169, -169, -169, -169, 65, + -169, -169, -169, -169, -169, -169, -169, -169, 65, 65, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, - -169, 65, -169, -169, -169, -169, -169, -169, -169, -169, + 65, 65, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169 @@ -3325,12 +3486,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, 62, -170, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, -170, -170, - -170, -170, -170, -170, -170, -170, -170, -170, -170, 65, + -170, -170, -170, -170, -170, -170, -170, -170, 65, 65, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, - -170, 65, -170, -170, -170, -170, -170, -170, -170, -170, + 65, 65, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170 }, @@ -3342,12 +3503,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, 62, -171, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, -171, -171, - -171, -171, -171, -171, -171, -171, -171, -171, -171, 65, + -171, -171, -171, -171, -171, -171, -171, -171, 65, 65, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, - -171, 65, -171, -171, -171, -171, -171, -171, -171, -171, + 65, 65, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171 }, @@ -3360,11 +3521,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -172, -172, -172, -172, -172, -172, 62, -172, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, -172, -172, - -172, -172, -172, -172, -172, -172, -172, -172, -172, 65, + -172, -172, -172, -172, -172, -172, -172, -172, 65, 65, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, - -172, 65, -172, -172, -172, -172, -172, -172, -172, -172, + 65, 65, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172 }, @@ -3377,11 +3538,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, 62, -173, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, -173, -173, - -173, -173, -173, -173, -173, -173, -173, -173, -173, 65, + -173, -173, -173, -173, -173, -173, -173, -173, 65, 65, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, - -173, 65, -173, -173, -173, -173, -173, -173, -173, -173, + 65, 65, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173 @@ -3394,12 +3555,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, 62, -174, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, -174, -174, - -174, -174, -174, -174, -174, -174, -174, -174, -174, 65, + -174, -174, -174, -174, -174, -174, -174, -174, 65, 65, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, - -174, 65, -174, -174, -174, -174, -174, -174, -174, -174, + 65, 65, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174 }, @@ -3619,12 +3780,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, 62, -187, 201, 202, 202, 202, 202, 202, 202, 202, 202, 202, -187, -187, - -187, -187, -187, -187, -187, -187, -187, -187, -187, 65, + -187, -187, -187, -187, -187, -187, -187, -187, 65, 65, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, - -187, 65, -187, -187, -187, -187, -187, -187, -187, -187, + 65, 65, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187 }, @@ -3637,11 +3798,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -188, -188, -188, -188, -188, -188, 62, -188, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, -188, -188, - -188, -188, -188, -188, -188, -188, -188, -188, -188, 65, + -188, -188, -188, -188, -188, -188, -188, -188, 65, 65, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, - -188, 65, -188, -188, -188, -188, -188, -188, -188, -188, + 65, 65, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188 }, @@ -3654,11 +3815,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, 62, -189, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, -189, -189, - -189, -189, -189, -189, -189, -189, -189, -189, -189, 65, + -189, -189, -189, -189, -189, -189, -189, -189, 65, 65, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, - -189, 65, -189, -189, -189, -189, -189, -189, -189, -189, + 65, 65, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189 @@ -3671,12 +3832,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, 62, -190, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, -190, -190, - -190, -190, -190, -190, -190, -190, -190, -190, -190, 65, + -190, -190, -190, -190, -190, -190, -190, -190, 65, 65, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, - -190, 65, -190, -190, -190, -190, -190, -190, -190, -190, + 65, 65, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190 }, @@ -3688,12 +3849,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, 62, -191, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, -191, -191, - -191, -191, -191, -191, -191, -191, -191, -191, -191, 65, + -191, -191, -191, -191, -191, -191, -191, -191, 65, 65, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, - -191, 65, -191, -191, -191, -191, -191, -191, -191, -191, + 65, 65, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191 }, @@ -3706,11 +3867,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -192, -192, -192, -192, -192, -192, 62, -192, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, -192, -192, - -192, -192, -192, -192, -192, -192, -192, -192, -192, 65, + -192, -192, -192, -192, -192, -192, -192, -192, 65, 65, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, - -192, 65, -192, -192, -192, -192, -192, -192, -192, -192, + 65, 65, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192 }, @@ -3723,11 +3884,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, 62, -193, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, -193, -193, - -193, -193, -193, -193, -193, -193, -193, -193, -193, 65, + -193, -193, -193, -193, -193, -193, -193, -193, 65, 65, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, - -193, 65, -193, -193, -193, -193, -193, -193, -193, -193, + 65, 65, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193 @@ -3740,12 +3901,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, 62, -194, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, -194, -194, - -194, -194, -194, -194, -194, -194, -194, -194, -194, 65, + -194, -194, -194, -194, -194, -194, -194, -194, 65, 65, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, - -194, 65, -194, -194, -194, -194, -194, -194, -194, -194, + 65, 65, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194 }, @@ -3861,12 +4022,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, 62, -201, 216, 217, 217, 217, 217, 217, 217, 217, 217, 217, -201, -201, - -201, -201, -201, -201, -201, -201, -201, -201, -201, 65, + -201, -201, -201, -201, -201, -201, -201, -201, 65, 65, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, - -201, 65, -201, -201, -201, -201, -201, -201, -201, -201, + 65, 65, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201 }, @@ -3879,11 +4040,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -202, -202, -202, -202, -202, -202, 62, -202, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, -202, -202, - -202, -202, -202, -202, -202, -202, -202, -202, -202, 65, + -202, -202, -202, -202, -202, -202, -202, -202, 65, 65, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, - -202, 65, -202, -202, -202, -202, -202, -202, -202, -202, + 65, 65, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202 }, @@ -3896,11 +4057,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, 62, -203, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, -203, -203, - -203, -203, -203, -203, -203, -203, -203, -203, -203, 65, + -203, -203, -203, -203, -203, -203, -203, -203, 65, 65, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, - -203, 65, -203, -203, -203, -203, -203, -203, -203, -203, + 65, 65, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203 @@ -3913,12 +4074,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, 62, -204, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, -204, -204, - -204, -204, -204, -204, -204, -204, -204, -204, -204, 65, + -204, -204, -204, -204, -204, -204, -204, -204, 65, 65, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, - -204, 65, -204, -204, -204, -204, -204, -204, -204, -204, + 65, 65, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204 }, @@ -3931,11 +4092,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -205, -205, -205, -205, -205, -205, 62, -205, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, -205, -205, - -205, -205, -205, -205, -205, -205, -205, -205, -205, 65, + -205, -205, -205, -205, -205, -205, -205, -205, 65, 65, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, - -205, 65, -205, -205, -205, -205, -205, -205, -205, -205, + 65, 65, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205 }, @@ -3948,11 +4109,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, 62, -206, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, -206, -206, - -206, -206, -206, -206, -206, -206, -206, -206, -206, 65, + -206, -206, -206, -206, -206, -206, -206, -206, 65, 65, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, - -206, 65, -206, -206, -206, -206, -206, -206, -206, -206, + 65, 65, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206 @@ -3965,12 +4126,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, 62, -207, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, -207, -207, - -207, -207, -207, -207, -207, -207, -207, -207, -207, 65, + -207, -207, -207, -207, -207, -207, -207, -207, 65, 65, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, - -207, 65, -207, -207, -207, -207, -207, -207, -207, -207, + 65, 65, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207 }, @@ -3983,11 +4144,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -208, -208, -208, -208, -208, -208, 62, -208, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, -208, -208, - -208, -208, -208, -208, -208, -208, -208, -208, -208, 65, + -208, -208, -208, -208, -208, -208, -208, -208, 65, 65, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, - -208, 65, -208, -208, -208, -208, -208, -208, -208, -208, + 65, 65, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208 }, @@ -4000,11 +4161,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, 62, -209, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, -209, -209, - -209, -209, -209, -209, -209, -209, -209, -209, -209, 65, + -209, -209, -209, -209, -209, -209, -209, -209, 65, 65, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, - -209, 65, -209, -209, -209, -209, -209, -209, -209, -209, + 65, 65, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209 @@ -4121,11 +4282,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, 62, -216, 232, 233, 233, 233, 233, 233, 233, 233, 233, 233, -216, -216, - -216, -216, -216, -216, -216, -216, -216, -216, -216, 65, + -216, -216, -216, -216, -216, -216, -216, -216, 65, 65, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, - -216, 65, -216, -216, -216, -216, -216, -216, -216, -216, + 65, 65, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216 @@ -4138,12 +4299,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, 62, -217, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, -217, -217, - -217, -217, -217, -217, -217, -217, -217, -217, -217, 65, + -217, -217, -217, -217, -217, -217, -217, -217, 65, 65, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, - -217, 65, -217, -217, -217, -217, -217, -217, -217, -217, + 65, 65, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217 }, @@ -4156,11 +4317,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -218, -218, -218, -218, -218, -218, 62, -218, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, -218, -218, - -218, -218, -218, -218, -218, -218, -218, -218, -218, 65, + -218, -218, -218, -218, -218, -218, -218, -218, 65, 65, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, - -218, 65, -218, -218, -218, -218, -218, -218, -218, -218, + 65, 65, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218 }, @@ -4173,11 +4334,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, 62, -219, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, -219, -219, - -219, -219, -219, -219, -219, -219, -219, -219, -219, 65, + -219, -219, -219, -219, -219, -219, -219, -219, 65, 65, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, - -219, 65, -219, -219, -219, -219, -219, -219, -219, -219, + 65, 65, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219 @@ -4190,12 +4351,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, 62, -220, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, -220, -220, - -220, -220, -220, -220, -220, -220, -220, -220, -220, 65, + -220, -220, -220, -220, -220, -220, -220, -220, 65, 65, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, - -220, 65, -220, -220, -220, -220, -220, -220, -220, -220, + 65, 65, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220 }, @@ -4207,12 +4368,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, 62, -221, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, -221, -221, - -221, -221, -221, -221, -221, -221, -221, -221, -221, 65, + -221, -221, -221, -221, -221, -221, -221, -221, 65, 65, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, - -221, 65, -221, -221, -221, -221, -221, -221, -221, -221, + 65, 65, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221 }, @@ -4225,11 +4386,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -222, -222, -222, -222, -222, -222, 62, -222, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, -222, -222, - -222, -222, -222, -222, -222, -222, -222, -222, -222, 65, + -222, -222, -222, -222, -222, -222, -222, -222, 65, 65, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, - -222, 65, -222, -222, -222, -222, -222, -222, -222, -222, + 65, 65, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222 }, @@ -4242,11 +4403,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, 62, -223, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, -223, -223, - -223, -223, -223, -223, -223, -223, -223, -223, -223, 65, + -223, -223, -223, -223, -223, -223, -223, -223, 65, 65, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, - -223, 65, -223, -223, -223, -223, -223, -223, -223, -223, + 65, 65, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223 @@ -4259,12 +4420,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, 62, -224, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, -224, -224, - -224, -224, -224, -224, -224, -224, -224, -224, -224, 65, + -224, -224, -224, -224, -224, -224, -224, -224, 65, 65, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, - -224, 65, -224, -224, -224, -224, -224, -224, -224, -224, + 65, 65, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224 }, @@ -4277,11 +4438,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -225, -225, -225, -225, -225, -225, 62, -225, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, -225, -225, - -225, -225, -225, -225, -225, -225, -225, -225, -225, 65, + -225, -225, -225, -225, -225, -225, -225, -225, 65, 65, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, - -225, 65, -225, -225, -225, -225, -225, -225, -225, -225, + 65, 65, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225 }, @@ -4398,11 +4559,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -232, -232, -232, -232, -232, -232, 62, -232, 246, 247, 247, 247, 247, 247, 247, 247, 247, 247, -232, -232, - -232, -232, -232, -232, -232, -232, -232, -232, -232, 65, + -232, -232, -232, -232, -232, -232, -232, -232, 65, 65, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, - -232, 65, -232, -232, -232, -232, -232, -232, -232, -232, + 65, 65, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232 }, @@ -4415,11 +4576,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, 62, -233, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, -233, -233, - -233, -233, -233, -233, -233, -233, -233, -233, -233, 65, + -233, -233, -233, -233, -233, -233, -233, -233, 65, 65, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, - -233, 65, -233, -233, -233, -233, -233, -233, -233, -233, + 65, 65, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233 @@ -4432,12 +4593,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, 62, -234, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, -234, -234, - -234, -234, -234, -234, -234, -234, -234, -234, -234, 65, + -234, -234, -234, -234, -234, -234, -234, -234, 65, 65, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, - -234, 65, -234, -234, -234, -234, -234, -234, -234, -234, + 65, 65, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234 }, @@ -4450,11 +4611,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -235, -235, -235, -235, -235, -235, 62, -235, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, -235, -235, - -235, -235, -235, -235, -235, -235, -235, -235, -235, 65, + -235, -235, -235, -235, -235, -235, -235, -235, 65, 65, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, - -235, 65, -235, -235, -235, -235, -235, -235, -235, -235, + 65, 65, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235 }, @@ -4467,11 +4628,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, 62, -236, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, -236, -236, - -236, -236, -236, -236, -236, -236, -236, -236, -236, 65, + -236, -236, -236, -236, -236, -236, -236, -236, 65, 65, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, - -236, 65, -236, -236, -236, -236, -236, -236, -236, -236, + 65, 65, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236 @@ -4484,12 +4645,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, 62, -237, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, -237, -237, - -237, -237, -237, -237, -237, -237, -237, -237, -237, 65, + -237, -237, -237, -237, -237, -237, -237, -237, 65, 65, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, - -237, 65, -237, -237, -237, -237, -237, -237, -237, -237, + 65, 65, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237 }, @@ -4502,11 +4663,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -238, -238, -238, -238, -238, -238, 62, -238, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, -238, -238, - -238, -238, -238, -238, -238, -238, -238, -238, -238, 65, + -238, -238, -238, -238, -238, -238, -238, -238, 65, 65, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, - -238, 65, -238, -238, -238, -238, -238, -238, -238, -238, + 65, 65, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238 }, @@ -4519,11 +4680,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, 62, -239, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, -239, -239, - -239, -239, -239, -239, -239, -239, -239, -239, -239, 65, + -239, -239, -239, -239, -239, -239, -239, -239, 65, 65, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, - -239, 65, -239, -239, -239, -239, -239, -239, -239, -239, + 65, 65, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239 @@ -4536,12 +4697,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, 62, -240, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, -240, -240, - -240, -240, -240, -240, -240, -240, -240, -240, -240, 65, + -240, -240, -240, -240, -240, -240, -240, -240, 65, 65, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, - -240, 65, -240, -240, -240, -240, -240, -240, -240, -240, + 65, 65, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240 }, @@ -4553,12 +4714,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, 62, -241, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, -241, -241, - -241, -241, -241, -241, -241, -241, -241, -241, -241, 65, + -241, -241, -241, -241, -241, -241, -241, -241, 65, 65, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, - -241, 65, -241, -241, -241, -241, -241, -241, -241, -241, + 65, 65, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241 }, @@ -4571,11 +4732,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -242, -242, -242, -242, -242, -242, 62, -242, 257, 257, 257, 257, 257, 257, 257, 257, 257, 257, -242, -242, - -242, -242, -242, -242, -242, -242, -242, -242, -242, 65, + -242, -242, -242, -242, -242, -242, -242, -242, 65, 65, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, - -242, 65, -242, -242, -242, -242, -242, -242, -242, -242, + 65, 65, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242 }, @@ -4640,11 +4801,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, 62, -246, 262, 263, 263, 263, 263, 263, 263, 263, 263, 263, -246, -246, - -246, -246, -246, -246, -246, -246, -246, -246, -246, 65, + -246, -246, -246, -246, -246, -246, -246, -246, 65, 65, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, - -246, 65, -246, -246, -246, -246, -246, -246, -246, -246, + 65, 65, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246 @@ -4657,12 +4818,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, 62, -247, 264, 264, 264, 264, 264, 264, 264, 264, 264, 264, -247, -247, - -247, -247, -247, -247, -247, -247, -247, -247, -247, 65, + -247, -247, -247, -247, -247, -247, -247, -247, 65, 65, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, - -247, 65, -247, -247, -247, -247, -247, -247, -247, -247, + 65, 65, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247 }, @@ -4675,11 +4836,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -248, -248, -248, -248, -248, -248, 62, -248, 265, 265, 265, 265, 265, 265, 265, 265, 265, 265, -248, -248, - -248, -248, -248, -248, -248, -248, -248, -248, -248, 65, + -248, -248, -248, -248, -248, -248, -248, -248, 65, 65, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, - -248, 65, -248, -248, -248, -248, -248, -248, -248, -248, + 65, 65, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248 }, @@ -4692,11 +4853,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, 62, -249, 266, 266, 266, 266, 266, 266, 266, 266, 266, 266, -249, -249, - -249, -249, -249, -249, -249, -249, -249, -249, -249, 65, + -249, -249, -249, -249, -249, -249, -249, -249, 65, 65, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, - -249, 65, -249, -249, -249, -249, -249, -249, -249, -249, + 65, 65, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249 @@ -4709,12 +4870,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, 62, -250, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, -250, -250, - -250, -250, -250, -250, -250, -250, -250, -250, -250, 65, + -250, -250, -250, -250, -250, -250, -250, -250, 65, 65, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, - -250, 65, -250, -250, -250, -250, -250, -250, -250, -250, + 65, 65, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250 }, @@ -4726,12 +4887,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, 62, -251, 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, -251, -251, - -251, -251, -251, -251, -251, -251, -251, -251, -251, 65, + -251, -251, -251, -251, -251, -251, -251, -251, 65, 65, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, - -251, 65, -251, -251, -251, -251, -251, -251, -251, -251, + 65, 65, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251 }, @@ -4744,11 +4905,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -252, -252, -252, -252, -252, -252, 62, -252, 269, 269, 269, 269, 269, 269, 269, 269, 269, 269, -252, -252, - -252, -252, -252, -252, -252, -252, -252, -252, -252, 65, + -252, -252, -252, -252, -252, -252, -252, -252, 65, 65, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, - -252, 65, -252, -252, -252, -252, -252, -252, -252, -252, + 65, 65, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252 }, @@ -4761,11 +4922,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, 62, -253, 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, -253, -253, - -253, -253, -253, -253, -253, -253, -253, -253, -253, 65, + -253, -253, -253, -253, -253, -253, -253, -253, 65, 65, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, - -253, 65, -253, -253, -253, -253, -253, -253, -253, -253, + 65, 65, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253 @@ -4778,12 +4939,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, 62, -254, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, -254, -254, - -254, -254, -254, -254, -254, -254, -254, -254, -254, 65, + -254, -254, -254, -254, -254, -254, -254, -254, 65, 65, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, - -254, 65, -254, -254, -254, -254, -254, -254, -254, -254, + 65, 65, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254 }, @@ -4796,11 +4957,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -255, -255, -255, -255, -255, -255, 62, -255, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, -255, -255, - -255, -255, -255, -255, -255, -255, -255, -255, -255, 65, + -255, -255, -255, -255, -255, -255, -255, -255, 65, 65, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, - -255, 65, -255, -255, -255, -255, -255, -255, -255, -255, + 65, 65, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255 }, @@ -4813,11 +4974,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, 62, -256, 273, 273, 273, 273, 273, 273, 273, 273, 273, 273, -256, -256, - -256, -256, -256, -256, -256, -256, -256, -256, -256, 65, + -256, -256, -256, -256, -256, -256, -256, -256, 65, 65, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, - -256, 65, -256, -256, -256, -256, -256, -256, -256, -256, + 65, 65, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256 @@ -4830,12 +4991,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, 62, -257, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, -257, -257, - -257, -257, -257, -257, -257, -257, -257, -257, -257, 65, + -257, -257, -257, -257, -257, -257, -257, -257, 65, 65, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, - -257, 65, -257, -257, -257, -257, -257, -257, -257, -257, + 65, 65, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257 }, @@ -4917,11 +5078,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -262, -262, -262, -262, -262, -262, 62, -262, 277, 278, 278, 278, 278, 278, 278, 278, 278, 278, -262, -262, - -262, -262, -262, -262, -262, -262, -262, -262, -262, 65, + -262, -262, -262, -262, -262, -262, -262, -262, 65, 65, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, - -262, 65, -262, -262, -262, -262, -262, -262, -262, -262, + 65, 65, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262 }, @@ -4934,11 +5095,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, 62, -263, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, -263, -263, - -263, -263, -263, -263, -263, -263, -263, -263, -263, 65, + -263, -263, -263, -263, -263, -263, -263, -263, 65, 65, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, - -263, 65, -263, -263, -263, -263, -263, -263, -263, -263, + 65, 65, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263 @@ -4951,12 +5112,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, 62, -264, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, -264, -264, - -264, -264, -264, -264, -264, -264, -264, -264, -264, 65, + -264, -264, -264, -264, -264, -264, -264, -264, 65, 65, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, - -264, 65, -264, -264, -264, -264, -264, -264, -264, -264, + 65, 65, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264 }, @@ -4969,11 +5130,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -265, -265, -265, -265, -265, -265, 62, -265, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, -265, -265, - -265, -265, -265, -265, -265, -265, -265, -265, -265, 65, + -265, -265, -265, -265, -265, -265, -265, -265, 65, 65, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, - -265, 65, -265, -265, -265, -265, -265, -265, -265, -265, + 65, 65, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265 }, @@ -4986,11 +5147,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, 62, -266, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, -266, -266, - -266, -266, -266, -266, -266, -266, -266, -266, -266, 65, + -266, -266, -266, -266, -266, -266, -266, -266, 65, 65, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, - -266, 65, -266, -266, -266, -266, -266, -266, -266, -266, + 65, 65, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266 @@ -5003,12 +5164,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, 62, -267, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, -267, -267, - -267, -267, -267, -267, -267, -267, -267, -267, -267, 65, + -267, -267, -267, -267, -267, -267, -267, -267, 65, 65, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, - -267, 65, -267, -267, -267, -267, -267, -267, -267, -267, + 65, 65, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267 }, @@ -5021,11 +5182,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -268, -268, -268, -268, -268, -268, 62, -268, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, -268, -268, - -268, -268, -268, -268, -268, -268, -268, -268, -268, 65, + -268, -268, -268, -268, -268, -268, -268, -268, 65, 65, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, - -268, 65, -268, -268, -268, -268, -268, -268, -268, -268, + 65, 65, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268 }, @@ -5038,11 +5199,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, 62, -269, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, -269, -269, - -269, -269, -269, -269, -269, -269, -269, -269, -269, 65, + -269, -269, -269, -269, -269, -269, -269, -269, 65, 65, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, - -269, 65, -269, -269, -269, -269, -269, -269, -269, -269, + 65, 65, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269 @@ -5055,12 +5216,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, 62, -270, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, -270, -270, - -270, -270, -270, -270, -270, -270, -270, -270, -270, 65, + -270, -270, -270, -270, -270, -270, -270, -270, 65, 65, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, - -270, 65, -270, -270, -270, -270, -270, -270, -270, -270, + 65, 65, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270 }, @@ -5072,12 +5233,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, 62, -271, 287, 287, 287, 287, 287, 287, 287, 287, 287, 287, -271, -271, - -271, -271, -271, -271, -271, -271, -271, -271, -271, 65, + -271, -271, -271, -271, -271, -271, -271, -271, 65, 65, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, - -271, 65, -271, -271, -271, -271, -271, -271, -271, -271, + 65, 65, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271 }, @@ -5090,11 +5251,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -272, -272, -272, -272, -272, -272, 62, -272, 288, 288, 288, 288, 288, 288, 288, 288, 288, 288, -272, -272, - -272, -272, -272, -272, -272, -272, -272, -272, -272, 65, + -272, -272, -272, -272, -272, -272, -272, -272, 65, 65, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, - -272, 65, -272, -272, -272, -272, -272, -272, -272, -272, + 65, 65, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272 }, @@ -5107,11 +5268,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, 62, -273, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, -273, -273, - -273, -273, -273, -273, -273, -273, -273, -273, -273, 65, + -273, -273, -273, -273, -273, -273, -273, -273, 65, 65, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, - -273, 65, -273, -273, -273, -273, -273, -273, -273, -273, + 65, 65, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273 @@ -5124,12 +5285,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, 62, -274, 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, -274, -274, - -274, -274, -274, -274, -274, -274, -274, -274, -274, 65, + -274, -274, -274, -274, -274, -274, -274, -274, 65, 65, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, - -274, 65, -274, -274, -274, -274, -274, -274, -274, -274, + 65, 65, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274 }, @@ -5176,12 +5337,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, 62, -277, 293, 294, 294, 294, 294, 294, 294, 294, 294, 294, -277, -277, - -277, -277, -277, -277, -277, -277, -277, -277, -277, 65, + -277, -277, -277, -277, -277, -277, -277, -277, 65, 65, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, - -277, 65, -277, -277, -277, -277, -277, -277, -277, -277, + 65, 65, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277 }, @@ -5194,11 +5355,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -278, -278, -278, -278, -278, -278, 62, -278, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, -278, -278, - -278, -278, -278, -278, -278, -278, -278, -278, -278, 65, + -278, -278, -278, -278, -278, -278, -278, -278, 65, 65, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, - -278, 65, -278, -278, -278, -278, -278, -278, -278, -278, + 65, 65, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278 }, @@ -5211,11 +5372,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, 62, -279, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, -279, -279, - -279, -279, -279, -279, -279, -279, -279, -279, -279, 65, + -279, -279, -279, -279, -279, -279, -279, -279, 65, 65, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, - -279, 65, -279, -279, -279, -279, -279, -279, -279, -279, + 65, 65, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279 @@ -5228,12 +5389,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, 62, -280, 297, 297, 297, 297, 297, 297, 297, 297, 297, 297, -280, -280, - -280, -280, -280, -280, -280, -280, -280, -280, -280, 65, + -280, -280, -280, -280, -280, -280, -280, -280, 65, 65, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, - -280, 65, -280, -280, -280, -280, -280, -280, -280, -280, + 65, 65, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280 }, @@ -5245,12 +5406,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, 62, -281, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, -281, -281, - -281, -281, -281, -281, -281, -281, -281, -281, -281, 65, + -281, -281, -281, -281, -281, -281, -281, -281, 65, 65, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, - -281, 65, -281, -281, -281, -281, -281, -281, -281, -281, + 65, 65, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281 }, @@ -5263,11 +5424,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -282, -282, -282, -282, -282, -282, 62, -282, 299, 299, 299, 299, 299, 299, 299, 299, 299, 299, -282, -282, - -282, -282, -282, -282, -282, -282, -282, -282, -282, 65, + -282, -282, -282, -282, -282, -282, -282, -282, 65, 65, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, - -282, 65, -282, -282, -282, -282, -282, -282, -282, -282, + 65, 65, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282 }, @@ -5280,11 +5441,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, 62, -283, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, -283, -283, - -283, -283, -283, -283, -283, -283, -283, -283, -283, 65, + -283, -283, -283, -283, -283, -283, -283, -283, 65, 65, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, - -283, 65, -283, -283, -283, -283, -283, -283, -283, -283, + 65, 65, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283 @@ -5297,12 +5458,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, 62, -284, 301, 301, 301, 301, 301, 301, 301, 301, 301, 301, -284, -284, - -284, -284, -284, -284, -284, -284, -284, -284, -284, 65, + -284, -284, -284, -284, -284, -284, -284, -284, 65, 65, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, - -284, 65, -284, -284, -284, -284, -284, -284, -284, -284, + 65, 65, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284 }, @@ -5315,11 +5476,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -285, -285, -285, -285, -285, -285, 62, -285, 302, 302, 302, 302, 302, 302, 302, 302, 302, 302, -285, -285, - -285, -285, -285, -285, -285, -285, -285, -285, -285, 65, + -285, -285, -285, -285, -285, -285, -285, -285, 65, 65, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, - -285, 65, -285, -285, -285, -285, -285, -285, -285, -285, + 65, 65, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285 }, @@ -5332,11 +5493,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, 62, -286, 303, 303, 303, 303, 303, 303, 303, 303, 303, 303, -286, -286, - -286, -286, -286, -286, -286, -286, -286, -286, -286, 65, + -286, -286, -286, -286, -286, -286, -286, -286, 65, 65, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, - -286, 65, -286, -286, -286, -286, -286, -286, -286, -286, + 65, 65, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286 @@ -5349,12 +5510,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, 62, -287, 304, 304, 304, 304, 304, 304, 304, 304, 304, 304, -287, -287, - -287, -287, -287, -287, -287, -287, -287, -287, -287, 65, + -287, -287, -287, -287, -287, -287, -287, -287, 65, 65, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, - -287, 65, -287, -287, -287, -287, -287, -287, -287, -287, + 65, 65, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287 }, @@ -5367,11 +5528,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -288, -288, -288, -288, -288, -288, 62, -288, 305, 305, 305, 305, 305, 305, 305, 305, 305, 305, -288, -288, - -288, -288, -288, -288, -288, -288, -288, -288, -288, 65, + -288, -288, -288, -288, -288, -288, -288, -288, 65, 65, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, - -288, 65, -288, -288, -288, -288, -288, -288, -288, -288, + 65, 65, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288 }, @@ -5384,11 +5545,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, 62, -289, 306, 306, 306, 306, 306, 306, 306, 306, 306, 306, -289, -289, - -289, -289, -289, -289, -289, -289, -289, -289, -289, 65, + -289, -289, -289, -289, -289, -289, -289, -289, 65, 65, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, - -289, 65, -289, -289, -289, -289, -289, -289, -289, -289, + 65, 65, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289 @@ -5401,12 +5562,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, 62, -290, 307, 307, 307, 307, 307, 307, 307, 307, 307, 307, -290, -290, - -290, -290, -290, -290, -290, -290, -290, -290, -290, 65, + -290, -290, -290, -290, -290, -290, -290, -290, 65, 65, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, - -290, 65, -290, -290, -290, -290, -290, -290, -290, -290, + 65, 65, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290 }, @@ -5453,11 +5614,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, 62, -293, 310, 311, 311, 311, 311, 311, 311, 311, 311, 311, -293, -293, - -293, -293, -293, -293, -293, -293, -293, -293, -293, 65, + -293, -293, -293, -293, -293, -293, -293, -293, 65, 65, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, - -293, 65, -293, -293, -293, -293, -293, -293, -293, -293, + 65, 65, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293 @@ -5470,12 +5631,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, 62, -294, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, -294, -294, - -294, -294, -294, -294, -294, -294, -294, -294, -294, 65, + -294, -294, -294, -294, -294, -294, -294, -294, 65, 65, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, - -294, 65, -294, -294, -294, -294, -294, -294, -294, -294, + 65, 65, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294 }, @@ -5488,11 +5649,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -295, -295, -295, -295, -295, -295, 62, -295, 313, 313, 313, 313, 313, 313, 313, 313, 313, 313, -295, -295, - -295, -295, -295, -295, -295, -295, -295, -295, -295, 65, + -295, -295, -295, -295, -295, -295, -295, -295, 65, 65, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, - -295, 65, -295, -295, -295, -295, -295, -295, -295, -295, + 65, 65, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295 }, @@ -5505,11 +5666,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, 62, -296, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, -296, -296, - -296, -296, -296, -296, -296, -296, -296, -296, -296, 65, + -296, -296, -296, -296, -296, -296, -296, -296, 65, 65, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, - -296, 65, -296, -296, -296, -296, -296, -296, -296, -296, + 65, 65, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296 @@ -5522,12 +5683,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, 62, -297, 315, 315, 315, 315, 315, 315, 315, 315, 315, 315, -297, -297, - -297, -297, -297, -297, -297, -297, -297, -297, -297, 65, + -297, -297, -297, -297, -297, -297, -297, -297, 65, 65, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, - -297, 65, -297, -297, -297, -297, -297, -297, -297, -297, + 65, 65, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297 }, @@ -5540,11 +5701,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -298, -298, -298, -298, -298, -298, 62, -298, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, -298, -298, - -298, -298, -298, -298, -298, -298, -298, -298, -298, 65, + -298, -298, -298, -298, -298, -298, -298, -298, 65, 65, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, - -298, 65, -298, -298, -298, -298, -298, -298, -298, -298, + 65, 65, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298 }, @@ -5557,11 +5718,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, 62, -299, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, -299, -299, - -299, -299, -299, -299, -299, -299, -299, -299, -299, 65, + -299, -299, -299, -299, -299, -299, -299, -299, 65, 65, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, - -299, 65, -299, -299, -299, -299, -299, -299, -299, -299, + 65, 65, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299 @@ -5574,12 +5735,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, 62, -300, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, -300, -300, - -300, -300, -300, -300, -300, -300, -300, -300, -300, 65, + -300, -300, -300, -300, -300, -300, -300, -300, 65, 65, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, - -300, 65, -300, -300, -300, -300, -300, -300, -300, -300, + 65, 65, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300 }, @@ -5591,12 +5752,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, 62, -301, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, -301, -301, - -301, -301, -301, -301, -301, -301, -301, -301, -301, 65, + -301, -301, -301, -301, -301, -301, -301, -301, 65, 65, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, - -301, 65, -301, -301, -301, -301, -301, -301, -301, -301, + 65, 65, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301 }, @@ -5609,11 +5770,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -302, -302, -302, -302, -302, -302, 62, -302, 320, 320, 320, 320, 320, 320, 320, 320, 320, 320, -302, -302, - -302, -302, -302, -302, -302, -302, -302, -302, -302, 65, + -302, -302, -302, -302, -302, -302, -302, -302, 65, 65, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, - -302, 65, -302, -302, -302, -302, -302, -302, -302, -302, + 65, 65, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302 }, @@ -5626,11 +5787,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, 62, -303, 321, 321, 321, 321, 321, 321, 321, 321, 321, 321, -303, -303, - -303, -303, -303, -303, -303, -303, -303, -303, -303, 65, + -303, -303, -303, -303, -303, -303, -303, -303, 65, 65, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, - -303, 65, -303, -303, -303, -303, -303, -303, -303, -303, + 65, 65, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303 @@ -5643,12 +5804,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, 62, -304, 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, -304, -304, - -304, -304, -304, -304, -304, -304, -304, -304, -304, 65, + -304, -304, -304, -304, -304, -304, -304, -304, 65, 65, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, - -304, 65, -304, -304, -304, -304, -304, -304, -304, -304, + 65, 65, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304 }, @@ -5661,11 +5822,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -305, -305, -305, -305, -305, -305, 62, -305, 323, 323, 323, 323, 323, 323, 323, 323, 323, 323, -305, -305, - -305, -305, -305, -305, -305, -305, -305, -305, -305, 65, + -305, -305, -305, -305, -305, -305, -305, -305, 65, 65, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, - -305, 65, -305, -305, -305, -305, -305, -305, -305, -305, + 65, 65, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305 }, @@ -5678,11 +5839,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, 62, -306, 324, 324, 324, 324, 324, 324, 324, 324, 324, 324, -306, -306, - -306, -306, -306, -306, -306, -306, -306, -306, -306, 65, + -306, -306, -306, -306, -306, -306, -306, -306, 65, 65, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, - -306, 65, -306, -306, -306, -306, -306, -306, -306, -306, + 65, 65, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306 @@ -5695,12 +5856,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, 62, -307, 325, 325, 325, 325, 325, 325, 325, 325, 325, 325, -307, -307, - -307, -307, -307, -307, -307, -307, -307, -307, -307, 65, + -307, -307, -307, -307, -307, -307, -307, -307, 65, 65, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, - -307, 65, -307, -307, -307, -307, -307, -307, -307, -307, + 65, 65, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307 }, @@ -5747,12 +5908,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, 62, -310, 328, 329, 329, 329, 329, 329, 329, 329, 329, 329, -310, -310, - -310, -310, -310, -310, -310, -310, -310, -310, -310, 65, + -310, -310, -310, -310, -310, -310, -310, -310, 65, 65, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, - -310, 65, -310, -310, -310, -310, -310, -310, -310, -310, + 65, 65, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310 }, @@ -5764,12 +5925,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, 62, -311, 330, 330, 330, 330, 330, 330, 330, 330, 330, 330, -311, -311, - -311, -311, -311, -311, -311, -311, -311, -311, -311, 65, + -311, -311, -311, -311, -311, -311, -311, -311, 65, 65, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, - -311, 65, -311, -311, -311, -311, -311, -311, -311, -311, + 65, 65, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311 }, @@ -5782,11 +5943,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -312, -312, -312, -312, -312, -312, 62, -312, 331, 331, 331, 331, 331, 331, 331, 331, 331, 331, -312, -312, - -312, -312, -312, -312, -312, -312, -312, -312, -312, 65, + -312, -312, -312, -312, -312, -312, -312, -312, 65, 65, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, - -312, 65, -312, -312, -312, -312, -312, -312, -312, -312, + 65, 65, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312 }, @@ -5799,11 +5960,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, 62, -313, 332, 332, 332, 332, 332, 332, 332, 332, 332, 332, -313, -313, - -313, -313, -313, -313, -313, -313, -313, -313, -313, 65, + -313, -313, -313, -313, -313, -313, -313, -313, 65, 65, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, - -313, 65, -313, -313, -313, -313, -313, -313, -313, -313, + 65, 65, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313 @@ -5816,12 +5977,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, 62, -314, 333, 333, 333, 333, 333, 333, 333, 333, 333, 333, -314, -314, - -314, -314, -314, -314, -314, -314, -314, -314, -314, 65, + -314, -314, -314, -314, -314, -314, -314, -314, 65, 65, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, - -314, 65, -314, -314, -314, -314, -314, -314, -314, -314, + 65, 65, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314 }, @@ -5834,11 +5995,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -315, -315, -315, -315, -315, -315, 62, -315, 334, 334, 334, 334, 334, 334, 334, 334, 334, 334, -315, -315, - -315, -315, -315, -315, -315, -315, -315, -315, -315, 65, + -315, -315, -315, -315, -315, -315, -315, -315, 65, 65, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, - -315, 65, -315, -315, -315, -315, -315, -315, -315, -315, + 65, 65, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315 }, @@ -5851,11 +6012,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, 62, -316, 335, 335, 335, 335, 335, 335, 335, 335, 335, 335, -316, -316, - -316, -316, -316, -316, -316, -316, -316, -316, -316, 65, + -316, -316, -316, -316, -316, -316, -316, -316, 65, 65, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, - -316, 65, -316, -316, -316, -316, -316, -316, -316, -316, + 65, 65, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316 @@ -5868,12 +6029,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, 62, -317, 336, 336, 336, 336, 336, 336, 336, 336, 336, 336, -317, -317, - -317, -317, -317, -317, -317, -317, -317, -317, -317, 65, + -317, -317, -317, -317, -317, -317, -317, -317, 65, 65, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, - -317, 65, -317, -317, -317, -317, -317, -317, -317, -317, + 65, 65, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317 }, @@ -5886,11 +6047,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -318, -318, -318, -318, -318, -318, 62, -318, 337, 337, 337, 337, 337, 337, 337, 337, 337, 337, -318, -318, - -318, -318, -318, -318, -318, -318, -318, -318, -318, 65, + -318, -318, -318, -318, -318, -318, -318, -318, 65, 65, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, - -318, 65, -318, -318, -318, -318, -318, -318, -318, -318, + 65, 65, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318 }, @@ -5903,11 +6064,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, 62, -319, 338, 338, 338, 338, 338, 338, 338, 338, 338, 338, -319, -319, - -319, -319, -319, -319, -319, -319, -319, -319, -319, 65, + -319, -319, -319, -319, -319, -319, -319, -319, 65, 65, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, - -319, 65, -319, -319, -319, -319, -319, -319, -319, -319, + 65, 65, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319 @@ -5920,12 +6081,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, 62, -320, 339, 339, 339, 339, 339, 339, 339, 339, 339, 339, -320, -320, - -320, -320, -320, -320, -320, -320, -320, -320, -320, 65, + -320, -320, -320, -320, -320, -320, -320, -320, 65, 65, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, - -320, 65, -320, -320, -320, -320, -320, -320, -320, -320, + 65, 65, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320 }, @@ -5937,12 +6098,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, 62, -321, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, -321, -321, - -321, -321, -321, -321, -321, -321, -321, -321, -321, 65, + -321, -321, -321, -321, -321, -321, -321, -321, 65, 65, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, - -321, 65, -321, -321, -321, -321, -321, -321, -321, -321, + 65, 65, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321 }, @@ -5955,11 +6116,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -322, -322, -322, -322, -322, -322, 62, -322, 341, 341, 341, 341, 341, 341, 341, 341, 341, 341, -322, -322, - -322, -322, -322, -322, -322, -322, -322, -322, -322, 65, + -322, -322, -322, -322, -322, -322, -322, -322, 65, 65, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, - -322, 65, -322, -322, -322, -322, -322, -322, -322, -322, + 65, 65, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322 }, @@ -5972,11 +6133,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, 62, -323, 342, 342, 342, 342, 342, 342, 342, 342, 342, 342, -323, -323, - -323, -323, -323, -323, -323, -323, -323, -323, -323, 65, + -323, -323, -323, -323, -323, -323, -323, -323, 65, 65, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, - -323, 65, -323, -323, -323, -323, -323, -323, -323, -323, + 65, 65, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323 @@ -5989,12 +6150,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, 62, -324, 343, 343, 343, 343, 343, 343, 343, 343, 343, 343, -324, -324, - -324, -324, -324, -324, -324, -324, -324, -324, -324, 65, + -324, -324, -324, -324, -324, -324, -324, -324, 65, 65, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, - -324, 65, -324, -324, -324, -324, -324, -324, -324, -324, + 65, 65, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324 }, @@ -6007,11 +6168,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -325, -325, -325, -325, -325, -325, 62, -325, 344, 344, 344, 344, 344, 344, 344, 344, 344, 344, -325, -325, - -325, -325, -325, -325, -325, -325, -325, -325, -325, 65, + -325, -325, -325, -325, -325, -325, -325, -325, 65, 65, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, - -325, 65, -325, -325, -325, -325, -325, -325, -325, -325, + 65, 65, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325 }, @@ -6059,11 +6220,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -328, -328, -328, -328, -328, -328, 62, -328, 347, 348, 348, 348, 348, 348, 348, 348, 348, 348, -328, -328, - -328, -328, -328, -328, -328, -328, -328, -328, -328, 65, + -328, -328, -328, -328, -328, -328, -328, -328, 65, 65, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, - -328, 65, -328, -328, -328, -328, -328, -328, -328, -328, + 65, 65, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328 }, @@ -6076,11 +6237,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, 62, -329, 349, 349, 349, 349, 349, 349, 349, 349, 349, 349, -329, -329, - -329, -329, -329, -329, -329, -329, -329, -329, -329, 65, + -329, -329, -329, -329, -329, -329, -329, -329, 65, 65, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, - -329, 65, -329, -329, -329, -329, -329, -329, -329, -329, + 65, 65, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329 @@ -6093,12 +6254,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, 62, -330, 350, 350, 350, 350, 350, 350, 350, 350, 350, 350, -330, -330, - -330, -330, -330, -330, -330, -330, -330, -330, -330, 65, + -330, -330, -330, -330, -330, -330, -330, -330, 65, 65, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, - -330, 65, -330, -330, -330, -330, -330, -330, -330, -330, + 65, 65, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330 }, @@ -6110,12 +6271,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, 62, -331, 351, 351, 351, 351, 351, 351, 351, 351, 351, 351, -331, -331, - -331, -331, -331, -331, -331, -331, -331, -331, -331, 65, + -331, -331, -331, -331, -331, -331, -331, -331, 65, 65, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, - -331, 65, -331, -331, -331, -331, -331, -331, -331, -331, + 65, 65, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331 }, @@ -6128,11 +6289,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -332, -332, -332, -332, -332, -332, 62, -332, 352, 352, 352, 352, 352, 352, 352, 352, 352, 352, -332, -332, - -332, -332, -332, -332, -332, -332, -332, -332, -332, 65, + -332, -332, -332, -332, -332, -332, -332, -332, 65, 65, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, - -332, 65, -332, -332, -332, -332, -332, -332, -332, -332, + 65, 65, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332 }, @@ -6145,11 +6306,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, 62, -333, 353, 353, 353, 353, 353, 353, 353, 353, 353, 353, -333, -333, - -333, -333, -333, -333, -333, -333, -333, -333, -333, 65, + -333, -333, -333, -333, -333, -333, -333, -333, 65, 65, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, - -333, 65, -333, -333, -333, -333, -333, -333, -333, -333, + 65, 65, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333 @@ -6162,12 +6323,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, 62, -334, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, -334, -334, - -334, -334, -334, -334, -334, -334, -334, -334, -334, 65, + -334, -334, -334, -334, -334, -334, -334, -334, 65, 65, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, - -334, 65, -334, -334, -334, -334, -334, -334, -334, -334, + 65, 65, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334 }, @@ -6180,11 +6341,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -335, -335, -335, -335, -335, -335, 62, -335, 355, 355, 355, 355, 355, 355, 355, 355, 355, 355, -335, -335, - -335, -335, -335, -335, -335, -335, -335, -335, -335, 65, + -335, -335, -335, -335, -335, -335, -335, -335, 65, 65, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, - -335, 65, -335, -335, -335, -335, -335, -335, -335, -335, + 65, 65, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335 }, @@ -6197,11 +6358,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, 62, -336, 356, 356, 356, 356, 356, 356, 356, 356, 356, 356, -336, -336, - -336, -336, -336, -336, -336, -336, -336, -336, -336, 65, + -336, -336, -336, -336, -336, -336, -336, -336, 65, 65, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, - -336, 65, -336, -336, -336, -336, -336, -336, -336, -336, + 65, 65, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336 @@ -6214,12 +6375,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, 62, -337, 357, 357, 357, 357, 357, 357, 357, 357, 357, 357, -337, -337, - -337, -337, -337, -337, -337, -337, -337, -337, -337, 65, + -337, -337, -337, -337, -337, -337, -337, -337, 65, 65, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, - -337, 65, -337, -337, -337, -337, -337, -337, -337, -337, + 65, 65, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337 }, @@ -6232,11 +6393,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -338, -338, -338, -338, -338, -338, 62, -338, 358, 358, 358, 358, 358, 358, 358, 358, 358, 358, -338, -338, - -338, -338, -338, -338, -338, -338, -338, -338, -338, 65, + -338, -338, -338, -338, -338, -338, -338, -338, 65, 65, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, - -338, 65, -338, -338, -338, -338, -338, -338, -338, -338, + 65, 65, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338 }, @@ -6249,11 +6410,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, 62, -339, 359, 359, 359, 359, 359, 359, 359, 359, 359, 359, -339, -339, - -339, -339, -339, -339, -339, -339, -339, -339, -339, 65, + -339, -339, -339, -339, -339, -339, -339, -339, 65, 65, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, - -339, 65, -339, -339, -339, -339, -339, -339, -339, -339, + 65, 65, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339 @@ -6266,12 +6427,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, 62, -340, 360, 360, 360, 360, 360, 360, 360, 360, 360, 360, -340, -340, - -340, -340, -340, -340, -340, -340, -340, -340, -340, 65, + -340, -340, -340, -340, -340, -340, -340, -340, 65, 65, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, - -340, 65, -340, -340, -340, -340, -340, -340, -340, -340, + 65, 65, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340 }, @@ -6283,12 +6444,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, 62, -341, 361, 361, 361, 361, 361, 361, 361, 361, 361, 361, -341, -341, - -341, -341, -341, -341, -341, -341, -341, -341, -341, 65, + -341, -341, -341, -341, -341, -341, -341, -341, 65, 65, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, - -341, 65, -341, -341, -341, -341, -341, -341, -341, -341, + 65, 65, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341 }, @@ -6301,11 +6462,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -342, -342, -342, -342, -342, -342, 62, -342, 362, 362, 362, 362, 362, 362, 362, 362, 362, 362, -342, -342, - -342, -342, -342, -342, -342, -342, -342, -342, -342, 65, + -342, -342, -342, -342, -342, -342, -342, -342, 65, 65, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, - -342, 65, -342, -342, -342, -342, -342, -342, -342, -342, + 65, 65, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342 }, @@ -6318,11 +6479,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, 62, -343, 363, 363, 363, 363, 363, 363, 363, 363, 363, 363, -343, -343, - -343, -343, -343, -343, -343, -343, -343, -343, -343, 65, + -343, -343, -343, -343, -343, -343, -343, -343, 65, 65, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, - -343, 65, -343, -343, -343, -343, -343, -343, -343, -343, + 65, 65, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343 @@ -6335,12 +6496,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, 62, -344, 364, 364, 364, 364, 364, 364, 364, 364, 364, 364, -344, -344, - -344, -344, -344, -344, -344, -344, -344, -344, -344, 65, + -344, -344, -344, -344, -344, -344, -344, -344, 65, 65, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, - -344, 65, -344, -344, -344, -344, -344, -344, -344, -344, + 65, 65, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344 }, @@ -6387,12 +6548,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, 62, -347, 367, 368, 368, 368, 368, 368, 368, 368, 368, 368, -347, -347, - -347, -347, -347, -347, -347, -347, -347, -347, -347, 65, + -347, -347, -347, -347, -347, -347, -347, -347, 65, 65, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, - -347, 65, -347, -347, -347, -347, -347, -347, -347, -347, + 65, 65, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347 }, @@ -6405,11 +6566,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -348, -348, -348, -348, -348, -348, 62, -348, 369, 369, 369, 369, 369, 369, 369, 369, 369, 369, -348, -348, - -348, -348, -348, -348, -348, -348, -348, -348, -348, 65, + -348, -348, -348, -348, -348, -348, -348, -348, 65, 65, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, - -348, 65, -348, -348, -348, -348, -348, -348, -348, -348, + 65, 65, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348 }, @@ -6422,11 +6583,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, 62, -349, 370, 370, 370, 370, 370, 370, 370, 370, 370, 370, -349, -349, - -349, -349, -349, -349, -349, -349, -349, -349, -349, 65, + -349, -349, -349, -349, -349, -349, -349, -349, 65, 65, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, - -349, 65, -349, -349, -349, -349, -349, -349, -349, -349, + 65, 65, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349 @@ -6439,12 +6600,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, 62, -350, 371, 371, 371, 371, 371, 371, 371, 371, 371, 371, -350, -350, - -350, -350, -350, -350, -350, -350, -350, -350, -350, 65, + -350, -350, -350, -350, -350, -350, -350, -350, 65, 65, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, - -350, 65, -350, -350, -350, -350, -350, -350, -350, -350, + 65, 65, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350 }, @@ -6456,12 +6617,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, 62, -351, 372, 372, 372, 372, 372, 372, 372, 372, 372, 372, -351, -351, - -351, -351, -351, -351, -351, -351, -351, -351, -351, 65, + -351, -351, -351, -351, -351, -351, -351, -351, 65, 65, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, - -351, 65, -351, -351, -351, -351, -351, -351, -351, -351, + 65, 65, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351 }, @@ -6474,11 +6635,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -352, -352, -352, -352, -352, -352, 62, -352, 373, 373, 373, 373, 373, 373, 373, 373, 373, 373, -352, -352, - -352, -352, -352, -352, -352, -352, -352, -352, -352, 65, + -352, -352, -352, -352, -352, -352, -352, -352, 65, 65, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, - -352, 65, -352, -352, -352, -352, -352, -352, -352, -352, + 65, 65, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352 }, @@ -6491,11 +6652,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, 62, -353, 374, 374, 374, 374, 374, 374, 374, 374, 374, 374, -353, -353, - -353, -353, -353, -353, -353, -353, -353, -353, -353, 65, + -353, -353, -353, -353, -353, -353, -353, -353, 65, 65, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, - -353, 65, -353, -353, -353, -353, -353, -353, -353, -353, + 65, 65, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353 @@ -6508,12 +6669,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, 62, -354, 375, 375, 375, 375, 375, 375, 375, 375, 375, 375, -354, -354, - -354, -354, -354, -354, -354, -354, -354, -354, -354, 65, + -354, -354, -354, -354, -354, -354, -354, -354, 65, 65, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, - -354, 65, -354, -354, -354, -354, -354, -354, -354, -354, + 65, 65, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354 }, @@ -6526,11 +6687,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -355, -355, -355, -355, -355, -355, 62, -355, 376, 376, 376, 376, 376, 376, 376, 376, 376, 376, -355, -355, - -355, -355, -355, -355, -355, -355, -355, -355, -355, 65, + -355, -355, -355, -355, -355, -355, -355, -355, 65, 65, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, - -355, 65, -355, -355, -355, -355, -355, -355, -355, -355, + 65, 65, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355 }, @@ -6543,11 +6704,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, 62, -356, 377, 377, 377, 377, 377, 377, 377, 377, 377, 377, -356, -356, - -356, -356, -356, -356, -356, -356, -356, -356, -356, 65, + -356, -356, -356, -356, -356, -356, -356, -356, 65, 65, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, - -356, 65, -356, -356, -356, -356, -356, -356, -356, -356, + 65, 65, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356 @@ -6560,12 +6721,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, 62, -357, 378, 378, 378, 378, 378, 378, 378, 378, 378, 378, -357, -357, - -357, -357, -357, -357, -357, -357, -357, -357, -357, 65, + -357, -357, -357, -357, -357, -357, -357, -357, 65, 65, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, - -357, 65, -357, -357, -357, -357, -357, -357, -357, -357, + 65, 65, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357 }, @@ -6578,11 +6739,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -358, -358, -358, -358, -358, -358, 62, -358, 379, 379, 379, 379, 379, 379, 379, 379, 379, 379, -358, -358, - -358, -358, -358, -358, -358, -358, -358, -358, -358, 65, + -358, -358, -358, -358, -358, -358, -358, -358, 65, 65, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, - -358, 65, -358, -358, -358, -358, -358, -358, -358, -358, + 65, 65, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358 }, @@ -6595,11 +6756,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, 62, -359, 380, 380, 380, 380, 380, 380, 380, 380, 380, 380, -359, -359, - -359, -359, -359, -359, -359, -359, -359, -359, -359, 65, + -359, -359, -359, -359, -359, -359, -359, -359, 65, 65, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, - -359, 65, -359, -359, -359, -359, -359, -359, -359, -359, + 65, 65, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359 @@ -6612,12 +6773,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, 62, -360, 381, 381, 381, 381, 381, 381, 381, 381, 381, 381, -360, -360, - -360, -360, -360, -360, -360, -360, -360, -360, -360, 65, + -360, -360, -360, -360, -360, -360, -360, -360, 65, 65, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, - -360, 65, -360, -360, -360, -360, -360, -360, -360, -360, + 65, 65, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360 }, @@ -6629,12 +6790,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, 62, -361, 382, 382, 382, 382, 382, 382, 382, 382, 382, 382, -361, -361, - -361, -361, -361, -361, -361, -361, -361, -361, -361, 65, + -361, -361, -361, -361, -361, -361, -361, -361, 65, 65, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, - -361, 65, -361, -361, -361, -361, -361, -361, -361, -361, + 65, 65, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361 }, @@ -6647,11 +6808,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -362, -362, -362, -362, -362, -362, 62, -362, 383, 383, 383, 383, 383, 383, 383, 383, 383, 383, -362, -362, - -362, -362, -362, -362, -362, -362, -362, -362, -362, 65, + -362, -362, -362, -362, -362, -362, -362, -362, 65, 65, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, - -362, 65, -362, -362, -362, -362, -362, -362, -362, -362, + 65, 65, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362 }, @@ -6664,11 +6825,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, 62, -363, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, -363, -363, - -363, -363, -363, -363, -363, -363, -363, -363, -363, 65, + -363, -363, -363, -363, -363, -363, -363, -363, 65, 65, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, - -363, 65, -363, -363, -363, -363, -363, -363, -363, -363, + 65, 65, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363 @@ -6681,12 +6842,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, 62, -364, 385, 385, 385, 385, 385, 385, 385, 385, 385, 385, -364, -364, - -364, -364, -364, -364, -364, -364, -364, -364, -364, 65, + -364, -364, -364, -364, -364, -364, -364, -364, 65, 65, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, - -364, 65, -364, -364, -364, -364, -364, -364, -364, -364, + 65, 65, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364 }, @@ -6733,12 +6894,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, 62, -367, 388, 389, 389, 389, 389, 389, 389, 389, 389, 389, -367, -367, - -367, -367, -367, -367, -367, -367, -367, -367, -367, 65, + -367, -367, -367, -367, -367, -367, -367, -367, 65, 65, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, - -367, 65, -367, -367, -367, -367, -367, -367, -367, -367, + 65, 65, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367 }, @@ -6751,11 +6912,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -368, -368, -368, -368, -368, -368, 62, -368, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, -368, -368, - -368, -368, -368, -368, -368, -368, -368, -368, -368, 65, + -368, -368, -368, -368, -368, -368, -368, -368, 65, 65, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, - -368, 65, -368, -368, -368, -368, -368, -368, -368, -368, + 65, 65, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368 }, @@ -6768,11 +6929,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, 62, -369, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, -369, -369, - -369, -369, -369, -369, -369, -369, -369, -369, -369, 65, + -369, -369, -369, -369, -369, -369, -369, -369, 65, 65, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, - -369, 65, -369, -369, -369, -369, -369, -369, -369, -369, + 65, 65, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369 @@ -6785,12 +6946,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, 62, -370, 392, 392, 392, 392, 392, 392, 392, 392, 392, 392, -370, -370, - -370, -370, -370, -370, -370, -370, -370, -370, -370, 65, + -370, -370, -370, -370, -370, -370, -370, -370, 65, 65, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, - -370, 65, -370, -370, -370, -370, -370, -370, -370, -370, + 65, 65, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370 }, @@ -6802,12 +6963,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, 62, -371, 393, 393, 393, 393, 393, 393, 393, 393, 393, 393, -371, -371, - -371, -371, -371, -371, -371, -371, -371, -371, -371, 65, + -371, -371, -371, -371, -371, -371, -371, -371, 65, 65, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, - -371, 65, -371, -371, -371, -371, -371, -371, -371, -371, + 65, 65, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371 }, @@ -6820,11 +6981,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -372, -372, -372, -372, -372, -372, 62, -372, 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, -372, -372, - -372, -372, -372, -372, -372, -372, -372, -372, -372, 65, + -372, -372, -372, -372, -372, -372, -372, -372, 65, 65, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, - -372, 65, -372, -372, -372, -372, -372, -372, -372, -372, + 65, 65, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372 }, @@ -6837,11 +6998,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, 62, -373, 395, 395, 395, 395, 395, 395, 395, 395, 395, 395, -373, -373, - -373, -373, -373, -373, -373, -373, -373, -373, -373, 65, + -373, -373, -373, -373, -373, -373, -373, -373, 65, 65, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, - -373, 65, -373, -373, -373, -373, -373, -373, -373, -373, + 65, 65, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373 @@ -6854,12 +7015,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, 62, -374, 396, 396, 396, 396, 396, 396, 396, 396, 396, 396, -374, -374, - -374, -374, -374, -374, -374, -374, -374, -374, -374, 65, + -374, -374, -374, -374, -374, -374, -374, -374, 65, 65, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, - -374, 65, -374, -374, -374, -374, -374, -374, -374, -374, + 65, 65, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374 }, @@ -6872,11 +7033,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -375, -375, -375, -375, -375, -375, 62, -375, 397, 397, 397, 397, 397, 397, 397, 397, 397, 397, -375, -375, - -375, -375, -375, -375, -375, -375, -375, -375, -375, 65, + -375, -375, -375, -375, -375, -375, -375, -375, 65, 65, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, - -375, 65, -375, -375, -375, -375, -375, -375, -375, -375, + 65, 65, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375 }, @@ -6889,11 +7050,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, 62, -376, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, -376, -376, - -376, -376, -376, -376, -376, -376, -376, -376, -376, 65, + -376, -376, -376, -376, -376, -376, -376, -376, 65, 65, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, - -376, 65, -376, -376, -376, -376, -376, -376, -376, -376, + 65, 65, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376 @@ -6906,12 +7067,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, 62, -377, 399, 399, 399, 399, 399, 399, 399, 399, 399, 399, -377, -377, - -377, -377, -377, -377, -377, -377, -377, -377, -377, 65, + -377, -377, -377, -377, -377, -377, -377, -377, 65, 65, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, - -377, 65, -377, -377, -377, -377, -377, -377, -377, -377, + 65, 65, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377 }, @@ -6924,11 +7085,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -378, -378, -378, -378, -378, -378, 62, -378, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, -378, -378, - -378, -378, -378, -378, -378, -378, -378, -378, -378, 65, + -378, -378, -378, -378, -378, -378, -378, -378, 65, 65, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, - -378, 65, -378, -378, -378, -378, -378, -378, -378, -378, + 65, 65, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378 }, @@ -6941,11 +7102,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, 62, -379, 401, 401, 401, 401, 401, 401, 401, 401, 401, 401, -379, -379, - -379, -379, -379, -379, -379, -379, -379, -379, -379, 65, + -379, -379, -379, -379, -379, -379, -379, -379, 65, 65, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, - -379, 65, -379, -379, -379, -379, -379, -379, -379, -379, + 65, 65, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379 @@ -6958,12 +7119,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, 62, -380, 402, 402, 402, 402, 402, 402, 402, 402, 402, 402, -380, -380, - -380, -380, -380, -380, -380, -380, -380, -380, -380, 65, + -380, -380, -380, -380, -380, -380, -380, -380, 65, 65, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, - -380, 65, -380, -380, -380, -380, -380, -380, -380, -380, + 65, 65, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380 }, @@ -6975,12 +7136,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, 62, -381, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, -381, -381, - -381, -381, -381, -381, -381, -381, -381, -381, -381, 65, + -381, -381, -381, -381, -381, -381, -381, -381, 65, 65, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, - -381, 65, -381, -381, -381, -381, -381, -381, -381, -381, + 65, 65, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381 }, @@ -6993,11 +7154,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -382, -382, -382, -382, -382, -382, 62, -382, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, -382, -382, - -382, -382, -382, -382, -382, -382, -382, -382, -382, 65, + -382, -382, -382, -382, -382, -382, -382, -382, 65, 65, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, - -382, 65, -382, -382, -382, -382, -382, -382, -382, -382, + 65, 65, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382 }, @@ -7010,11 +7171,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, 62, -383, 405, 405, 405, 405, 405, 405, 405, 405, 405, 405, -383, -383, - -383, -383, -383, -383, -383, -383, -383, -383, -383, 65, + -383, -383, -383, -383, -383, -383, -383, -383, 65, 65, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, - -383, 65, -383, -383, -383, -383, -383, -383, -383, -383, + 65, 65, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383 @@ -7027,12 +7188,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, 62, -384, 406, 406, 406, 406, 406, 406, 406, 406, 406, 406, -384, -384, - -384, -384, -384, -384, -384, -384, -384, -384, -384, 65, + -384, -384, -384, -384, -384, -384, -384, -384, 65, 65, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, - -384, 65, -384, -384, -384, -384, -384, -384, -384, -384, + 65, 65, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384 }, @@ -7045,11 +7206,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -385, -385, -385, -385, -385, -385, 62, -385, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, -385, -385, - -385, -385, -385, -385, -385, -385, -385, -385, -385, 65, + -385, -385, -385, -385, -385, -385, -385, -385, 65, 65, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, - -385, 65, -385, -385, -385, -385, -385, -385, -385, -385, + 65, 65, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385 }, @@ -7097,11 +7258,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -388, -388, -388, -388, -388, -388, 62, -388, 410, 411, 411, 411, 411, 411, 411, 411, 411, 411, -388, -388, - -388, -388, -388, -388, -388, -388, -388, -388, -388, 65, + -388, -388, -388, -388, -388, -388, -388, -388, 65, 65, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, - -388, 65, -388, -388, -388, -388, -388, -388, -388, -388, + 65, 65, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388 }, @@ -7114,11 +7275,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, 62, -389, 412, 412, 412, 412, 412, 412, 412, 412, 412, 412, -389, -389, - -389, -389, -389, -389, -389, -389, -389, -389, -389, 65, + -389, -389, -389, -389, -389, -389, -389, -389, 65, 65, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, - -389, 65, -389, -389, -389, -389, -389, -389, -389, -389, + 65, 65, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389 @@ -7131,12 +7292,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, 62, -390, 413, 413, 413, 413, 413, 413, 413, 413, 413, 413, -390, -390, - -390, -390, -390, -390, -390, -390, -390, -390, -390, 65, + -390, -390, -390, -390, -390, -390, -390, -390, 65, 65, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, - -390, 65, -390, -390, -390, -390, -390, -390, -390, -390, + 65, 65, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390 }, @@ -7148,12 +7309,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, 62, -391, 414, 414, 414, 414, 414, 414, 414, 414, 414, 414, -391, -391, - -391, -391, -391, -391, -391, -391, -391, -391, -391, 65, + -391, -391, -391, -391, -391, -391, -391, -391, 65, 65, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, - -391, 65, -391, -391, -391, -391, -391, -391, -391, -391, + 65, 65, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391 }, @@ -7166,11 +7327,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -392, -392, -392, -392, -392, -392, 62, -392, 415, 415, 415, 415, 415, 415, 415, 415, 415, 415, -392, -392, - -392, -392, -392, -392, -392, -392, -392, -392, -392, 65, + -392, -392, -392, -392, -392, -392, -392, -392, 65, 65, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, - -392, 65, -392, -392, -392, -392, -392, -392, -392, -392, + 65, 65, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392 }, @@ -7183,11 +7344,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, 62, -393, 416, 416, 416, 416, 416, 416, 416, 416, 416, 416, -393, -393, - -393, -393, -393, -393, -393, -393, -393, -393, -393, 65, + -393, -393, -393, -393, -393, -393, -393, -393, 65, 65, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, - -393, 65, -393, -393, -393, -393, -393, -393, -393, -393, + 65, 65, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393 @@ -7200,12 +7361,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, 62, -394, 417, 417, 417, 417, 417, 417, 417, 417, 417, 417, -394, -394, - -394, -394, -394, -394, -394, -394, -394, -394, -394, 65, + -394, -394, -394, -394, -394, -394, -394, -394, 65, 65, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, - -394, 65, -394, -394, -394, -394, -394, -394, -394, -394, + 65, 65, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394 }, @@ -7218,11 +7379,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -395, -395, -395, -395, -395, -395, 62, -395, 418, 418, 418, 418, 418, 418, 418, 418, 418, 418, -395, -395, - -395, -395, -395, -395, -395, -395, -395, -395, -395, 65, + -395, -395, -395, -395, -395, -395, -395, -395, 65, 65, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, - -395, 65, -395, -395, -395, -395, -395, -395, -395, -395, + 65, 65, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395 }, @@ -7235,11 +7396,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, 62, -396, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, -396, -396, - -396, -396, -396, -396, -396, -396, -396, -396, -396, 65, + -396, -396, -396, -396, -396, -396, -396, -396, 65, 65, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, - -396, 65, -396, -396, -396, -396, -396, -396, -396, -396, + 65, 65, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396 @@ -7252,12 +7413,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, 62, -397, 420, 420, 420, 420, 420, 420, 420, 420, 420, 420, -397, -397, - -397, -397, -397, -397, -397, -397, -397, -397, -397, 65, + -397, -397, -397, -397, -397, -397, -397, -397, 65, 65, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, - -397, 65, -397, -397, -397, -397, -397, -397, -397, -397, + 65, 65, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397 }, @@ -7270,11 +7431,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -398, -398, -398, -398, -398, -398, 62, -398, 421, 421, 421, 421, 421, 421, 421, 421, 421, 421, -398, -398, - -398, -398, -398, -398, -398, -398, -398, -398, -398, 65, + -398, -398, -398, -398, -398, -398, -398, -398, 65, 65, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, - -398, 65, -398, -398, -398, -398, -398, -398, -398, -398, + 65, 65, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398 }, @@ -7287,11 +7448,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, 62, -399, 422, 422, 422, 422, 422, 422, 422, 422, 422, 422, -399, -399, - -399, -399, -399, -399, -399, -399, -399, -399, -399, 65, + -399, -399, -399, -399, -399, -399, -399, -399, 65, 65, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, - -399, 65, -399, -399, -399, -399, -399, -399, -399, -399, + 65, 65, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399 @@ -7304,12 +7465,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, 62, -400, 423, 423, 423, 423, 423, 423, 423, 423, 423, 423, -400, -400, - -400, -400, -400, -400, -400, -400, -400, -400, -400, 65, + -400, -400, -400, -400, -400, -400, -400, -400, 65, 65, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, - -400, 65, -400, -400, -400, -400, -400, -400, -400, -400, + 65, 65, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400 }, @@ -7321,12 +7482,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, 62, -401, 424, 424, 424, 424, 424, 424, 424, 424, 424, 424, -401, -401, - -401, -401, -401, -401, -401, -401, -401, -401, -401, 65, + -401, -401, -401, -401, -401, -401, -401, -401, 65, 65, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, - -401, 65, -401, -401, -401, -401, -401, -401, -401, -401, + 65, 65, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401 }, @@ -7339,11 +7500,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -402, -402, -402, -402, -402, -402, 62, -402, 425, 425, 425, 425, 425, 425, 425, 425, 425, 425, -402, -402, - -402, -402, -402, -402, -402, -402, -402, -402, -402, 65, + -402, -402, -402, -402, -402, -402, -402, -402, 65, 65, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, - -402, 65, -402, -402, -402, -402, -402, -402, -402, -402, + 65, 65, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402 }, @@ -7356,11 +7517,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, 62, -403, 426, 426, 426, 426, 426, 426, 426, 426, 426, 426, -403, -403, - -403, -403, -403, -403, -403, -403, -403, -403, -403, 65, + -403, -403, -403, -403, -403, -403, -403, -403, 65, 65, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, - -403, 65, -403, -403, -403, -403, -403, -403, -403, -403, + 65, 65, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403 @@ -7373,12 +7534,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, 62, -404, 427, 427, 427, 427, 427, 427, 427, 427, 427, 427, -404, -404, - -404, -404, -404, -404, -404, -404, -404, -404, -404, 65, + -404, -404, -404, -404, -404, -404, -404, -404, 65, 65, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, - -404, 65, -404, -404, -404, -404, -404, -404, -404, -404, + 65, 65, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404 }, @@ -7391,11 +7552,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -405, -405, -405, -405, -405, -405, 62, -405, 428, 428, 428, 428, 428, 428, 428, 428, 428, 428, -405, -405, - -405, -405, -405, -405, -405, -405, -405, -405, -405, 65, + -405, -405, -405, -405, -405, -405, -405, -405, 65, 65, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, - -405, 65, -405, -405, -405, -405, -405, -405, -405, -405, + 65, 65, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405 }, @@ -7408,11 +7569,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, 62, -406, 429, 429, 429, 429, 429, 429, 429, 429, 429, 429, -406, -406, - -406, -406, -406, -406, -406, -406, -406, -406, -406, 65, + -406, -406, -406, -406, -406, -406, -406, -406, 65, 65, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, - -406, 65, -406, -406, -406, -406, -406, -406, -406, -406, + 65, 65, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406 @@ -7425,12 +7586,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, 62, -407, 430, 430, 430, 430, 430, 430, 430, 430, 430, 430, -407, -407, - -407, -407, -407, -407, -407, -407, -407, -407, -407, 65, + -407, -407, -407, -407, -407, -407, -407, -407, 65, 65, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, - -407, 65, -407, -407, -407, -407, -407, -407, -407, -407, + 65, 65, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407 }, @@ -7477,12 +7638,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, 62, -410, 410, 411, 411, 411, 411, 411, 411, 411, 411, 411, -410, -410, - -410, -410, -410, -410, -410, -410, -410, -410, -410, 65, + -410, -410, -410, -410, -410, -410, -410, -410, 65, 65, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, - -410, 65, -410, -410, -410, -410, -410, -410, -410, -410, + 65, 65, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410 }, @@ -7494,12 +7655,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, 62, -411, 412, 412, 412, 412, 412, 412, 412, 412, 412, 412, -411, -411, - -411, -411, -411, -411, -411, -411, -411, -411, -411, 65, + -411, -411, -411, -411, -411, -411, -411, -411, 65, 65, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, - -411, 65, -411, -411, -411, -411, -411, -411, -411, -411, + 65, 65, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411 }, @@ -7512,11 +7673,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -412, -412, -412, -412, -412, -412, 62, -412, 413, 413, 413, 413, 413, 413, 413, 413, 413, 413, -412, -412, - -412, -412, -412, -412, -412, -412, -412, -412, -412, 65, + -412, -412, -412, -412, -412, -412, -412, -412, 65, 65, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, - -412, 65, -412, -412, -412, -412, -412, -412, -412, -412, + 65, 65, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412 }, @@ -7529,11 +7690,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, 62, -413, 414, 414, 414, 414, 414, 414, 414, 414, 414, 414, -413, -413, - -413, -413, -413, -413, -413, -413, -413, -413, -413, 65, + -413, -413, -413, -413, -413, -413, -413, -413, 65, 65, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, - -413, 65, -413, -413, -413, -413, -413, -413, -413, -413, + 65, 65, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413 @@ -7546,12 +7707,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, 62, -414, 415, 415, 415, 415, 415, 415, 415, 415, 415, 415, -414, -414, - -414, -414, -414, -414, -414, -414, -414, -414, -414, 65, + -414, -414, -414, -414, -414, -414, -414, -414, 65, 65, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, - -414, 65, -414, -414, -414, -414, -414, -414, -414, -414, + 65, 65, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414 }, @@ -7564,11 +7725,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -415, -415, -415, -415, -415, -415, 62, -415, 416, 416, 416, 416, 416, 416, 416, 416, 416, 416, -415, -415, - -415, -415, -415, -415, -415, -415, -415, -415, -415, 65, + -415, -415, -415, -415, -415, -415, -415, -415, 65, 65, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, - -415, 65, -415, -415, -415, -415, -415, -415, -415, -415, + 65, 65, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415 }, @@ -7581,11 +7742,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, 62, -416, 417, 417, 417, 417, 417, 417, 417, 417, 417, 417, -416, -416, - -416, -416, -416, -416, -416, -416, -416, -416, -416, 65, + -416, -416, -416, -416, -416, -416, -416, -416, 65, 65, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, - -416, 65, -416, -416, -416, -416, -416, -416, -416, -416, + 65, 65, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416 @@ -7598,12 +7759,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, 62, -417, 418, 418, 418, 418, 418, 418, 418, 418, 418, 418, -417, -417, - -417, -417, -417, -417, -417, -417, -417, -417, -417, 65, + -417, -417, -417, -417, -417, -417, -417, -417, 65, 65, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, - -417, 65, -417, -417, -417, -417, -417, -417, -417, -417, + 65, 65, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417 }, @@ -7616,11 +7777,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -418, -418, -418, -418, -418, -418, 62, -418, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, -418, -418, - -418, -418, -418, -418, -418, -418, -418, -418, -418, 65, + -418, -418, -418, -418, -418, -418, -418, -418, 65, 65, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, - -418, 65, -418, -418, -418, -418, -418, -418, -418, -418, + 65, 65, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418 }, @@ -7633,11 +7794,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, 62, -419, 420, 420, 420, 420, 420, 420, 420, 420, 420, 420, -419, -419, - -419, -419, -419, -419, -419, -419, -419, -419, -419, 65, + -419, -419, -419, -419, -419, -419, -419, -419, 65, 65, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, - -419, 65, -419, -419, -419, -419, -419, -419, -419, -419, + 65, 65, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419 @@ -7650,12 +7811,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, 62, -420, 421, 421, 421, 421, 421, 421, 421, 421, 421, 421, -420, -420, - -420, -420, -420, -420, -420, -420, -420, -420, -420, 65, + -420, -420, -420, -420, -420, -420, -420, -420, 65, 65, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, - -420, 65, -420, -420, -420, -420, -420, -420, -420, -420, + 65, 65, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420 }, @@ -7667,12 +7828,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, 62, -421, 422, 422, 422, 422, 422, 422, 422, 422, 422, 422, -421, -421, - -421, -421, -421, -421, -421, -421, -421, -421, -421, 65, + -421, -421, -421, -421, -421, -421, -421, -421, 65, 65, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, - -421, 65, -421, -421, -421, -421, -421, -421, -421, -421, + 65, 65, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421 }, @@ -7685,11 +7846,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -422, -422, -422, -422, -422, -422, 62, -422, 423, 423, 423, 423, 423, 423, 423, 423, 423, 423, -422, -422, - -422, -422, -422, -422, -422, -422, -422, -422, -422, 65, + -422, -422, -422, -422, -422, -422, -422, -422, 65, 65, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, - -422, 65, -422, -422, -422, -422, -422, -422, -422, -422, + 65, 65, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422 }, @@ -7702,11 +7863,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, 62, -423, 424, 424, 424, 424, 424, 424, 424, 424, 424, 424, -423, -423, - -423, -423, -423, -423, -423, -423, -423, -423, -423, 65, + -423, -423, -423, -423, -423, -423, -423, -423, 65, 65, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, - -423, 65, -423, -423, -423, -423, -423, -423, -423, -423, + 65, 65, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423 @@ -7719,12 +7880,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, 62, -424, 425, 425, 425, 425, 425, 425, 425, 425, 425, 425, -424, -424, - -424, -424, -424, -424, -424, -424, -424, -424, -424, 65, + -424, -424, -424, -424, -424, -424, -424, -424, 65, 65, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, - -424, 65, -424, -424, -424, -424, -424, -424, -424, -424, + 65, 65, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424 }, @@ -7737,11 +7898,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -425, -425, -425, -425, -425, -425, 62, -425, 426, 426, 426, 426, 426, 426, 426, 426, 426, 426, -425, -425, - -425, -425, -425, -425, -425, -425, -425, -425, -425, 65, + -425, -425, -425, -425, -425, -425, -425, -425, 65, 65, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, - -425, 65, -425, -425, -425, -425, -425, -425, -425, -425, + 65, 65, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425 }, @@ -7754,11 +7915,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, 62, -426, 427, 427, 427, 427, 427, 427, 427, 427, 427, 427, -426, -426, - -426, -426, -426, -426, -426, -426, -426, -426, -426, 65, + -426, -426, -426, -426, -426, -426, -426, -426, 65, 65, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, - -426, 65, -426, -426, -426, -426, -426, -426, -426, -426, + 65, 65, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426 @@ -7771,12 +7932,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, 62, -427, 428, 428, 428, 428, 428, 428, 428, 428, 428, 428, -427, -427, - -427, -427, -427, -427, -427, -427, -427, -427, -427, 65, + -427, -427, -427, -427, -427, -427, -427, -427, 65, 65, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, - -427, 65, -427, -427, -427, -427, -427, -427, -427, -427, + 65, 65, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427 }, @@ -7789,11 +7950,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -428, -428, -428, -428, -428, -428, 62, -428, 429, 429, 429, 429, 429, 429, 429, 429, 429, 429, -428, -428, - -428, -428, -428, -428, -428, -428, -428, -428, -428, 65, + -428, -428, -428, -428, -428, -428, -428, -428, 65, 65, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, - -428, 65, -428, -428, -428, -428, -428, -428, -428, -428, + 65, 65, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428 }, @@ -7806,11 +7967,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, 62, -429, 430, 430, 430, 430, 430, 430, 430, 430, 430, 430, -429, -429, - -429, -429, -429, -429, -429, -429, -429, -429, -429, 65, + -429, -429, -429, -429, -429, -429, -429, -429, 65, 65, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, - -429, 65, -429, -429, -429, -429, -429, -429, -429, -429, + 65, 65, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429 @@ -7823,12 +7984,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, 62, -430, 430, 430, 430, 430, 430, 430, 430, 430, 430, 430, -430, -430, - -430, -430, -430, -430, -430, -430, -430, -430, -430, 65, + -430, -430, -430, -430, -430, -430, -430, -430, 65, 65, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, - -430, 65, -430, -430, -430, -430, -430, -430, -430, -430, + 65, 65, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430 }, @@ -9911,22 +10072,21 @@ static yyconst flex_int16_t yy_nxt[][128] = } ; -static yy_state_type yy_get_previous_state (void ); -static yy_state_type yy_try_NUL_trans (yy_state_type current_state ); -static int yy_get_next_buffer (void ); -static void yy_fatal_error (yyconst char msg[] ); +static yy_state_type yy_get_previous_state ( yyscan_t yyscanner ); +static yy_state_type yy_try_NUL_trans ( yy_state_type current_state , yyscan_t yyscanner); +static int yy_get_next_buffer ( yyscan_t yyscanner ); +static void yynoreturn yy_fatal_error ( const char* msg , yyscan_t yyscanner ); /* Done after the current pattern has been matched and before the - * corresponding action - sets up fitshdrtext. + * corresponding action - sets up yytext. */ #define YY_DO_BEFORE_ACTION \ - (yytext_ptr) = yy_bp; \ - (yytext_ptr) -= (yy_more_len); \ - fitshdrleng = (size_t) (yy_cp - (yytext_ptr)); \ - (yy_hold_char) = *yy_cp; \ + yyg->yytext_ptr = yy_bp; \ + yyg->yytext_ptr -= yyg->yy_more_len; \ + yyleng = (int) (yy_cp - yyg->yytext_ptr); \ + yyg->yy_hold_char = *yy_cp; \ *yy_cp = '\0'; \ - (yy_c_buf_p) = yy_cp; - + yyg->yy_c_buf_p = yy_cp; #define YY_NUM_RULES 31 #define YY_END_OF_BUFFER 32 /* This struct is not used in this scanner, @@ -9936,7 +10096,7 @@ struct yy_trans_info flex_int32_t yy_verify; flex_int32_t yy_nxt; }; -static yyconst flex_int16_t yy_accept[551] = +static const flex_int16_t yy_accept[551] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 28, 29, 29, 0, 0, 32, 31, 31, 31, 31, 31, @@ -10000,10 +10160,7 @@ static yyconst flex_int16_t yy_accept[551] = 0, 0, 0, 0, 0, 0, 0, 0, 1, 3 } ; -static yy_state_type yy_last_accepting_state; -static char *yy_last_accepting_cpos; - -static yyconst yy_state_type yy_NUL_trans[551] = +static const yy_state_type yy_NUL_trans[551] = { 0, 16, 17, 23, 23, 33, 33, 37, 37, 39, 39, 40, 40, 41, 41, 0, 0, 43, 43, 43, 43, @@ -10067,24 +10224,17 @@ static yyconst yy_state_type yy_NUL_trans[551] = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } ; -extern int fitshdr_flex_debug; -int fitshdr_flex_debug = 0; - /* The intent behind this definition is that it'll catch * any uses of REJECT which flex missed. */ #define REJECT reject_used_but_not_detected -static int yy_more_flag = 0; -static int yy_more_len = 0; -#define yymore() ((yy_more_flag) = 1) -#define YY_MORE_ADJ (yy_more_len) +#define yymore() (yyg->yy_more_flag = 1) +#define YY_MORE_ADJ yyg->yy_more_len #define YY_RESTORE_YY_MORE_OFFSET -char *fitshdrtext; #line 1 "fitshdr.l" /*============================================================================ - - WCSLIB 4.14 - an implementation of the FITS WCS standard. - Copyright (C) 1995-2012, Mark Calabretta + WCSLIB 8.6 - an implementation of the FITS WCS standard. + Copyright (C) 1995-2025, Mark Calabretta This file is part of WCSLIB. @@ -10101,11 +10251,9 @@ char *fitshdrtext; You should have received a copy of the GNU Lesser General Public License along with WCSLIB. If not, see http://www.gnu.org/licenses. - Direct correspondence concerning WCSLIB to mark@calabretta.id.au - Author: Mark Calabretta, Australia Telescope National Facility, CSIRO. - http://www.atnf.csiro.au/people/Mark.Calabretta - $Id: fitshdr.c,v 4.14 2012/07/13 10:02:31 cal103 Exp $ + http://www.atnf.csiro.au/computing/software/wcs + $Id: fitshdr.c,v 8.6 2026/03/29 13:53:56 mcalabre Exp $ *============================================================================= * * fitshdr.l is a Flex description file containing a lexical scanner @@ -10118,6 +10266,7 @@ char *fitshdrtext; * *===========================================================================*/ /* Options. */ +#define YY_NO_INPUT 1 /* Keywords. */ /* Keyvalue data types. */ /* Characters forming standard unit strings (jwBIQX are not used). */ @@ -10133,39 +10282,49 @@ char *fitshdrtext; #include "fitshdr.h" #include "wcsutil.h" -#define YY_DECL int fitshdr(const char header[], int nkeyrec, int nkeyids, \ - struct fitskeyid keyids[], int *nreject, \ - struct fitskey **keys) +// User data associated with yyscanner. +struct fitshdr_extra { + // Values passed to YY_INPUT. + const char *hdr; + int nkeyrec; + + // Used in preempting the call to exit() by yy_fatal_error(). + jmp_buf abort_jmp_env; +}; + +#define YY_DECL int fitshdr_scanner(const char header[], int nkeyrec, \ + int nkeyids, struct fitskeyid keyids[], int *nreject, \ + struct fitskey **keys, yyscan_t yyscanner) #define YY_INPUT(inbuff, count, bufsize) \ { \ - if (fitshdr_nkeyrec) { \ - strncpy(inbuff, fitshdr_hdr, 80); \ + if (yyextra->nkeyrec) { \ + strncpy(inbuff, yyextra->hdr, 80); \ inbuff[80] = '\n'; \ - fitshdr_hdr += 80; \ - fitshdr_nkeyrec--; \ + yyextra->hdr += 80; \ + yyextra->nkeyrec--; \ count = 81; \ } else { \ count = YY_NULL; \ } \ } -/* These global variables are required by YY_INPUT. */ -const char *fitshdr_hdr; -int fitshdr_nkeyrec; +// Preempt the call to exit() by yy_fatal_error(). +#define exit(status) longjmp(yyextra->abort_jmp_env, status); -/* Used in preempting the call to exit() by yy_fatal_error(). */ -jmp_buf fitshdr_abort_jmp_env; -#define exit(status) longjmp(fitshdr_abort_jmp_env, status) +// Internal helper functions. +static YY_DECL; +static void nullfill(char cptr[], int len); -/* Map status return value to message. */ +// Map status return value to message. const char *fitshdr_errmsg[] = { "Success", "Null fitskey pointer-pointer passed", "Memory allocation failed", "Fatal error returned by Flex parser"}; -#line 10169 "fitshdr.c" +#line 10327 "fitshdr.c" +#line 10328 "fitshdr.c" #define INITIAL 0 #define VALUE 1 @@ -10183,40 +10342,80 @@ const char *fitshdr_errmsg[] = { #include #endif -#ifndef YY_EXTRA_TYPE -#define YY_EXTRA_TYPE void * -#endif +#define YY_EXTRA_TYPE struct fitshdr_extra * + +/* Holds the entire state of the reentrant scanner. */ +struct yyguts_t + { + + /* User-defined. Not touched by flex. */ + YY_EXTRA_TYPE yyextra_r; + + /* The rest are the same as the globals declared in the non-reentrant scanner. */ + FILE *yyin_r, *yyout_r; + size_t yy_buffer_stack_top; /**< index of top of stack. */ + size_t yy_buffer_stack_max; /**< capacity of stack. */ + YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */ + char yy_hold_char; + int yy_n_chars; + int yyleng_r; + char *yy_c_buf_p; + int yy_init; + int yy_start; + int yy_did_buffer_switch_on_eof; + int yy_start_stack_ptr; + int yy_start_stack_depth; + int *yy_start_stack; + yy_state_type yy_last_accepting_state; + char* yy_last_accepting_cpos; + + int yylineno_r; + int yy_flex_debug_r; + + char *yytext_r; + int yy_more_flag; + int yy_more_len; + + }; /* end struct yyguts_t */ + +static int yy_init_globals ( yyscan_t yyscanner ); + +int yylex_init (yyscan_t* scanner); -static int yy_init_globals (void ); +int yylex_init_extra ( YY_EXTRA_TYPE user_defined, yyscan_t* scanner); /* Accessor methods to globals. These are made visible to non-reentrant scanners for convenience. */ -int fitshdrlex_destroy (void ); +int yylex_destroy ( yyscan_t yyscanner ); -int fitshdrget_debug (void ); +int yyget_debug ( yyscan_t yyscanner ); -void fitshdrset_debug (int debug_flag ); +void yyset_debug ( int debug_flag , yyscan_t yyscanner ); -YY_EXTRA_TYPE fitshdrget_extra (void ); +YY_EXTRA_TYPE yyget_extra ( yyscan_t yyscanner ); -void fitshdrset_extra (YY_EXTRA_TYPE user_defined ); +void yyset_extra ( YY_EXTRA_TYPE user_defined , yyscan_t yyscanner ); -FILE *fitshdrget_in (void ); +FILE *yyget_in ( yyscan_t yyscanner ); -void fitshdrset_in (FILE * in_str ); +void yyset_in ( FILE * _in_str , yyscan_t yyscanner ); -FILE *fitshdrget_out (void ); +FILE *yyget_out ( yyscan_t yyscanner ); -void fitshdrset_out (FILE * out_str ); +void yyset_out ( FILE * _out_str , yyscan_t yyscanner ); -int fitshdrget_leng (void ); + int yyget_leng ( yyscan_t yyscanner ); -char *fitshdrget_text (void ); +char *yyget_text ( yyscan_t yyscanner ); -int fitshdrget_lineno (void ); +int yyget_lineno ( yyscan_t yyscanner ); -void fitshdrset_lineno (int line_number ); +void yyset_lineno ( int _line_number , yyscan_t yyscanner ); + +int yyget_column ( yyscan_t yyscanner ); + +void yyset_column ( int _column_no , yyscan_t yyscanner ); /* Macros after this point can all be overridden by user definitions in * section 1. @@ -10224,26 +10423,29 @@ void fitshdrset_lineno (int line_number ); #ifndef YY_SKIP_YYWRAP #ifdef __cplusplus -extern "C" int fitshdrwrap (void ); +extern "C" int yywrap ( yyscan_t yyscanner ); #else -extern int fitshdrwrap (void ); +extern int yywrap ( yyscan_t yyscanner ); +#endif #endif + +#ifndef YY_NO_UNPUT + #endif #ifndef yytext_ptr -static void yy_flex_strncpy (char *,yyconst char *,int ); +static void yy_flex_strncpy ( char *, const char *, int , yyscan_t yyscanner); #endif #ifdef YY_NEED_STRLEN -static int yy_flex_strlen (yyconst char * ); +static int yy_flex_strlen ( const char * , yyscan_t yyscanner); #endif #ifndef YY_NO_INPUT - #ifdef __cplusplus -static int yyinput (void ); +static int yyinput ( yyscan_t yyscanner ); #else -static int input (void ); +static int input ( yyscan_t yyscanner ); #endif #endif @@ -10263,7 +10465,7 @@ static int input (void ); /* This used to be an fputs(), but since the string might contain NUL's, * we now use fwrite(). */ -#define ECHO do { if (fwrite( fitshdrtext, fitshdrleng, 1, fitshdrout )) {} } while (0) +#define ECHO do { if (fwrite( yytext, (size_t) yyleng, 1, yyout )) {} } while (0) #endif /* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, @@ -10272,7 +10474,7 @@ static int input (void ); #ifndef YY_INPUT #define YY_INPUT(buf,result,max_size) \ errno=0; \ - while ( (result = read( fileno(fitshdrin), (char *) buf, max_size )) < 0 ) \ + while ( (result = (int) read( fileno(yyin), buf, (yy_size_t) max_size )) < 0 ) \ { \ if( errno != EINTR) \ { \ @@ -10280,7 +10482,7 @@ static int input (void ); break; \ } \ errno=0; \ - clearerr(fitshdrin); \ + clearerr(yyin); \ }\ \ @@ -10301,7 +10503,7 @@ static int input (void ); /* Report a fatal error. */ #ifndef YY_FATAL_ERROR -#define YY_FATAL_ERROR(msg) yy_fatal_error( msg ) +#define YY_FATAL_ERROR(msg) yy_fatal_error( msg , yyscanner) #endif /* end tables serialization structures and prototypes */ @@ -10312,12 +10514,12 @@ static int input (void ); #ifndef YY_DECL #define YY_DECL_IS_OURS 1 -extern int fitshdrlex (void); +extern int yylex (yyscan_t yyscanner); -#define YY_DECL int fitshdrlex (void) +#define YY_DECL int yylex (yyscan_t yyscanner) #endif /* !YY_DECL */ -/* Code executed at the beginning of each rule, after fitshdrtext and fitshdrleng +/* Code executed at the beginning of each rule, after yytext and yyleng * have been set up. */ #ifndef YY_USER_ACTION @@ -10326,123 +10528,140 @@ extern int fitshdrlex (void); /* Code executed at the end of each rule. */ #ifndef YY_BREAK -#define YY_BREAK break; +#define YY_BREAK /*LINTED*/break; #endif #define YY_RULE_SETUP \ - if ( fitshdrleng > 0 ) \ + if ( yyleng > 0 ) \ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = \ - (fitshdrtext[fitshdrleng - 1] == '\n'); \ + (yytext[yyleng - 1] == '\n'); \ YY_USER_ACTION /** The main scanner function which does all the work. */ YY_DECL { - register yy_state_type yy_current_state; - register char *yy_cp, *yy_bp; - register int yy_act; - -#line 119 "fitshdr.l" + yy_state_type yy_current_state; + char *yy_cp, *yy_bp; + int yy_act; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; - char *cptr, ctmp[72]; - int blank, continuation, end, j, k, keyno; - double dtmp; - struct fitskey *kptr; - struct fitskeyid *iptr; - void nullfill(char cptr[], int len); - int fitshdrlex_destroy(void); - - fitshdr_hdr = header; - fitshdr_nkeyrec = nkeyrec; - - *nreject = 0; - keyno = 0; + if ( !yyg->yy_init ) + { + yyg->yy_init = 1; + +#ifdef YY_USER_INIT + YY_USER_INIT; +#endif + + if ( ! yyg->yy_start ) + yyg->yy_start = 1; /* first start state */ + + if ( ! yyin ) + yyin = stdin; + + if ( ! yyout ) + yyout = stdout; + + if ( ! YY_CURRENT_BUFFER ) { + yyensure_buffer_stack (yyscanner); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); + } + + yy_load_buffer_state( yyscanner ); + } + + { +#line 128 "fitshdr.l" + +#line 130 "fitshdr.l" + char ctmp[72]; if (keys == 0x0) { - return 1; + return FITSHDRERR_NULL_POINTER; } - /* Allocate memory for the required number of fitskey structs. */ - /* Recall that calloc() initializes allocated memory to zero. */ + // Allocate memory for the required number of fitskey structs. + // Recall that calloc() initializes allocated memory to zero. + struct fitskey *kptr; if (!(kptr = *keys = calloc(nkeyrec, sizeof(struct fitskey)))) { - return 2; + return FITSHDRERR_MEMORY; } - /* Initialize keyids[]. */ - iptr = keyids; - for (j = 0; j < nkeyids; j++, iptr++) { + // Initialize returned values. + *nreject = 0; + + // Initialize keyids[]. + struct fitskeyid *iptr = keyids; + for (int j = 0; j < nkeyids; j++, iptr++) { iptr->count = 0; iptr->idx[0] = -1; iptr->idx[1] = -1; } + + int keyno = 0; + + int blank = 0; + int continuation = 0; + int end = 0; + +#ifdef WCSLIB_INT64 +#define asString(S) stringize(S) +#define stringize(S) #S - blank = 0; - continuation = 0; - end = 0; + const char *int64fmt; + if (strcmp(asString(WCSLIB_INT64), "long long int") == 0) { + int64fmt = "%lld"; + } else if (strcmp(asString(WCSLIB_INT64), "long int") == 0) { + int64fmt = "%ld"; + } else if (strcmp(asString(WCSLIB_INT64), "int") == 0) { + int64fmt = "%d"; + } else { + return FITSHDRERR_DATA_TYPE; + } +#endif - /* Return here via longjmp() invoked by yy_fatal_error(). */ - if (setjmp(fitshdr_abort_jmp_env)) { - return 3; + // User data associated with yyscanner. + yyextra->hdr = header; + yyextra->nkeyrec = nkeyrec; + + // Return here via longjmp() invoked by yy_fatal_error(). + if (setjmp(yyextra->abort_jmp_env)) { + return FITSHDRERR_FLEX_PARSER; } BEGIN(INITIAL); -#line 10392 "fitshdr.c" - - if ( !(yy_init) ) - { - (yy_init) = 1; - -#ifdef YY_USER_INIT - YY_USER_INIT; -#endif - - if ( ! (yy_start) ) - (yy_start) = 1; /* first start state */ - - if ( ! fitshdrin ) - fitshdrin = stdin; - - if ( ! fitshdrout ) - fitshdrout = stdout; - - if ( ! YY_CURRENT_BUFFER ) { - fitshdrensure_buffer_stack (); - YY_CURRENT_BUFFER_LVALUE = - fitshdr_create_buffer(fitshdrin,YY_BUF_SIZE ); - } +#line 10637 "fitshdr.c" - fitshdr_load_buffer_state( ); - } - - while ( 1 ) /* loops until end-of-file is reached */ + while ( /*CONSTCOND*/1 ) /* loops until end-of-file is reached */ { - (yy_more_len) = 0; - if ( (yy_more_flag) ) + yyg->yy_more_len = 0; + if ( yyg->yy_more_flag ) { - (yy_more_len) = (yy_c_buf_p) - (yytext_ptr); - (yy_more_flag) = 0; + yyg->yy_more_len = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr); + yyg->yy_more_flag = 0; } - yy_cp = (yy_c_buf_p); + yy_cp = yyg->yy_c_buf_p; - /* Support of fitshdrtext. */ - *yy_cp = (yy_hold_char); + /* Support of yytext. */ + *yy_cp = yyg->yy_hold_char; /* yy_bp points to the position in yy_ch_buf of the start of * the current run. */ yy_bp = yy_cp; - yy_current_state = (yy_start); + yy_current_state = yyg->yy_start; yy_current_state += YY_AT_BOL(); yy_match: while ( (yy_current_state = yy_nxt[yy_current_state][ YY_SC_TO_UI(*yy_cp) ]) > 0 ) { if ( yy_accept[yy_current_state] ) { - (yy_last_accepting_state) = yy_current_state; - (yy_last_accepting_cpos) = yy_cp; + yyg->yy_last_accepting_state = yy_current_state; + yyg->yy_last_accepting_cpos = yy_cp; } ++yy_cp; @@ -10461,17 +10680,17 @@ YY_DECL { /* beginning of action switch */ case 0: /* must back up */ /* undo the effects of YY_DO_BEFORE_ACTION */ - *yy_cp = (yy_hold_char); - yy_cp = (yy_last_accepting_cpos) + 1; - yy_current_state = (yy_last_accepting_state); + *yy_cp = yyg->yy_hold_char; + yy_cp = yyg->yy_last_accepting_cpos + 1; + yy_current_state = yyg->yy_last_accepting_state; goto yy_find_action; case 1: YY_RULE_SETUP -#line 163 "fitshdr.l" +#line 187 "fitshdr.l" { - /* A completely blank keyrecord. */ - strncpy(kptr->keyword, fitshdrtext, 8); + // A completely blank keyrecord. + strncpy(kptr->keyword, yytext, 8); yyless(0); blank = 1; BEGIN(COMMENT); @@ -10479,66 +10698,67 @@ YY_RULE_SETUP YY_BREAK case 2: YY_RULE_SETUP -#line 171 "fitshdr.l" +#line 195 "fitshdr.l" { - strncpy(kptr->keyword, fitshdrtext, 8); + strncpy(kptr->keyword, yytext, 8); BEGIN(COMMENT); } YY_BREAK case 3: YY_RULE_SETUP -#line 176 "fitshdr.l" +#line 200 "fitshdr.l" { - strncpy(kptr->keyword, fitshdrtext, 8); + strncpy(kptr->keyword, yytext, 8); end = 1; BEGIN(FLUSH); } YY_BREAK case 4: YY_RULE_SETUP -#line 182 "fitshdr.l" +#line 206 "fitshdr.l" { - /* Illegal END keyrecord. */ - strncpy(kptr->keyword, fitshdrtext, 8); + // Illegal END keyrecord. + strncpy(kptr->keyword, yytext, 8); kptr->status |= FITSHDR_KEYREC; BEGIN(VALUE); } YY_BREAK case 5: YY_RULE_SETUP -#line 189 "fitshdr.l" +#line 213 "fitshdr.l" { - /* Illegal END keyrecord. */ - strncpy(kptr->keyword, fitshdrtext, 8); + // Illegal END keyrecord. + strncpy(kptr->keyword, yytext, 8); kptr->status |= FITSHDR_KEYREC; BEGIN(COMMENT); } YY_BREAK case 6: YY_RULE_SETUP -#line 196 "fitshdr.l" +#line 220 "fitshdr.l" { - strncpy(kptr->keyword, fitshdrtext, 8); + strncpy(kptr->keyword, yytext, 8); BEGIN(VALUE); } YY_BREAK case 7: /* rule 7 can match eol */ YY_RULE_SETUP -#line 201 "fitshdr.l" +#line 225 "fitshdr.l" { - /* Continued string keyvalue. */ - strncpy(kptr->keyword, fitshdrtext, 8); + // Continued string keyvalue. + strncpy(kptr->keyword, yytext, 8); if (keyno > 0 && (kptr-1)->type%10 == 8) { - /* Put back the string keyvalue. */ - for (k = 10; fitshdrtext[k] != '\''; k++); + // Put back the string keyvalue. + int k; + for (k = 10; yytext[k] != '\''; k++); yyless(k); continuation = 1; BEGIN(VALUE); } else { - /* Not a valid continuation. */ + // Not a valid continuation. yyless(8); BEGIN(COMMENT); } @@ -10546,61 +10766,61 @@ YY_RULE_SETUP YY_BREAK case 8: YY_RULE_SETUP -#line 219 "fitshdr.l" +#line 244 "fitshdr.l" { - /* Keyword without value. */ - strncpy(kptr->keyword, fitshdrtext, 8); + // Keyword without value. + strncpy(kptr->keyword, yytext, 8); BEGIN(COMMENT); } YY_BREAK case 9: YY_RULE_SETUP -#line 225 "fitshdr.l" +#line 250 "fitshdr.l" { - /* Illegal keyword, carry on regardless. */ - strncpy(kptr->keyword, fitshdrtext, 8); + // Illegal keyword, carry on regardless. + strncpy(kptr->keyword, yytext, 8); kptr->status |= FITSHDR_KEYWORD; BEGIN(VALUE); } YY_BREAK case 10: YY_RULE_SETUP -#line 232 "fitshdr.l" +#line 257 "fitshdr.l" { - /* Illegal keyword, carry on regardless. */ - strncpy(kptr->keyword, fitshdrtext, 8); + // Illegal keyword, carry on regardless. + strncpy(kptr->keyword, yytext, 8); kptr->status |= FITSHDR_KEYWORD; BEGIN(COMMENT); } YY_BREAK case 11: -*yy_cp = (yy_hold_char); /* undo effects of setting up fitshdrtext */ -(yy_c_buf_p) = yy_cp -= 1; -YY_DO_BEFORE_ACTION; /* set up fitshdrtext again */ +*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ +yyg->yy_c_buf_p = yy_cp -= 1; +YY_DO_BEFORE_ACTION; /* set up yytext again */ YY_RULE_SETUP -#line 239 "fitshdr.l" +#line 264 "fitshdr.l" { - /* Null keyvalue. */ + // Null keyvalue. BEGIN(INLINE); } YY_BREAK case 12: YY_RULE_SETUP -#line 244 "fitshdr.l" +#line 269 "fitshdr.l" { - /* Logical keyvalue. */ + // Logical keyvalue. kptr->type = 1; - kptr->keyvalue.i = (*fitshdrtext == 'T'); + kptr->keyvalue.i = (*yytext == 'T'); BEGIN(INLINE); } YY_BREAK case 13: YY_RULE_SETUP -#line 251 "fitshdr.l" +#line 276 "fitshdr.l" { - /* 32-bit signed integer keyvalue. */ + // 32-bit signed integer keyvalue. kptr->type = 2; - if (sscanf(fitshdrtext, "%d", &(kptr->keyvalue.i)) < 1) { + if (sscanf(yytext, "%d", &(kptr->keyvalue.i)) < 1) { kptr->status |= FITSHDR_KEYVALUE; BEGIN(ERROR); } @@ -10610,40 +10830,41 @@ YY_RULE_SETUP YY_BREAK case 14: YY_RULE_SETUP -#line 262 "fitshdr.l" +#line 287 "fitshdr.l" { - /* 64-bit signed integer keyvalue (up to 18 digits). */ - if (wcsutil_str2double(fitshdrtext, "%lf", &dtmp)) { + // 64-bit signed integer keyvalue (up to 18 digits). + double dtmp; + if (wcsutil_str2double(yytext, &dtmp)) { kptr->status |= FITSHDR_KEYVALUE; BEGIN(ERROR); } else if (INT_MIN <= dtmp && dtmp <= INT_MAX) { - /* Can be accomodated as a 32-bit signed integer. */ + // Can be accomodated as a 32-bit signed integer. kptr->type = 2; - if (sscanf(fitshdrtext, "%d", &(kptr->keyvalue.i)) < 1) { + if (sscanf(yytext, "%d", &(kptr->keyvalue.i)) < 1) { kptr->status |= FITSHDR_KEYVALUE; BEGIN(ERROR); } } else { - /* 64-bit signed integer. */ + // 64-bit signed integer. kptr->type = 3; #ifdef WCSLIB_INT64 - /* Native 64-bit integer is available. */ - if (sscanf(fitshdrtext, "%lld", &(kptr->keyvalue.k)) < 1) { + // Native 64-bit integer is available. + if (sscanf(yytext, int64fmt, &(kptr->keyvalue.k)) < 1) { kptr->status |= FITSHDR_KEYVALUE; BEGIN(ERROR); } #else - /* 64-bit integer (up to 18 digits) implemented as int[3]. */ + // 64-bit integer (up to 18 digits) implemented as int[3]. kptr->keyvalue.k[2] = 0; - sprintf(ctmp, "%%%dd%%9d", fitshdrleng-9); - if (sscanf(fitshdrtext, ctmp, kptr->keyvalue.k+1, + sprintf(ctmp, "%%%dd%%9d", yyleng-9); + if (sscanf(yytext, ctmp, kptr->keyvalue.k+1, kptr->keyvalue.k) < 1) { kptr->status |= FITSHDR_KEYVALUE; BEGIN(ERROR); - } else if (*fitshdrtext == '-') { + } else if (*yytext == '-') { kptr->keyvalue.k[0] *= -1; } #endif @@ -10654,21 +10875,21 @@ YY_RULE_SETUP YY_BREAK case 15: YY_RULE_SETUP -#line 303 "fitshdr.l" +#line 329 "fitshdr.l" { - /* Very long integer keyvalue (and 19-digit int64). */ + // Very long integer keyvalue (and 19-digit int64). kptr->type = 4; - strcpy(ctmp, fitshdrtext); - k = fitshdrleng; + strcpy(ctmp, yytext); + int j, k = yyleng; for (j = 0; j < 8; j++) { - /* Read it backwards. */ + // Read it backwards. k -= 9; if (k < 0) k = 0; if (sscanf(ctmp+k, "%d", kptr->keyvalue.l+j) < 1) { kptr->status |= FITSHDR_KEYVALUE; BEGIN(ERROR); } - if (*fitshdrtext == '-') { + if (*yytext == '-') { kptr->keyvalue.l[j] = -abs(kptr->keyvalue.l[j]); } @@ -10676,7 +10897,7 @@ YY_RULE_SETUP ctmp[k] = '\0'; } - /* Can it be accomodated as a 64-bit signed integer? */ + // Can it be accomodated as a 64-bit signed integer? if (j == 2 && abs(kptr->keyvalue.l[2]) <= 9 && abs(kptr->keyvalue.l[1]) <= 223372036 && kptr->keyvalue.l[0] <= 854775807 && @@ -10684,9 +10905,9 @@ YY_RULE_SETUP kptr->type = 3; #ifdef WCSLIB_INT64 - /* Native 64-bit integer is available. */ + // Native 64-bit integer is available. kptr->keyvalue.l[2] = 0; - if (sscanf(fitshdrtext, "%lld", &(kptr->keyvalue.k)) < 1) { + if (sscanf(yytext, int64fmt, &(kptr->keyvalue.k)) < 1) { kptr->status |= FITSHDR_KEYVALUE; BEGIN(ERROR); } @@ -10698,11 +10919,11 @@ YY_RULE_SETUP YY_BREAK case 16: YY_RULE_SETUP -#line 344 "fitshdr.l" +#line 370 "fitshdr.l" { - /* Float keyvalue. */ + // Float keyvalue. kptr->type = 5; - if (wcsutil_str2double(fitshdrtext, "%lf", &(kptr->keyvalue.f))) { + if (wcsutil_str2double(yytext, &(kptr->keyvalue.f))) { kptr->status |= FITSHDR_KEYVALUE; BEGIN(ERROR); } @@ -10712,11 +10933,11 @@ YY_RULE_SETUP YY_BREAK case 17: YY_RULE_SETUP -#line 355 "fitshdr.l" +#line 381 "fitshdr.l" { - /* Integer complex keyvalue. */ + // Integer complex keyvalue. kptr->type = 6; - if (sscanf(fitshdrtext, "(%lf,%lf)", kptr->keyvalue.c, + if (sscanf(yytext, "(%lf,%lf)", kptr->keyvalue.c, kptr->keyvalue.c+1) < 2) { kptr->status |= FITSHDR_KEYVALUE; BEGIN(ERROR); @@ -10727,27 +10948,29 @@ YY_RULE_SETUP YY_BREAK case 18: YY_RULE_SETUP -#line 367 "fitshdr.l" +#line 393 "fitshdr.l" { - /* Floating point complex keyvalue. */ + // Floating point complex keyvalue. kptr->type = 7; - for (cptr = ctmp, k = 1; fitshdrtext[k] != ','; cptr++, k++) { - *cptr = fitshdrtext[k]; + char *cptr; + int k; + for (cptr = ctmp, k = 1; yytext[k] != ','; cptr++, k++) { + *cptr = yytext[k]; } *cptr = '\0'; - if (wcsutil_str2double(ctmp, "%lf", kptr->keyvalue.c)) { + if (wcsutil_str2double(ctmp, kptr->keyvalue.c)) { kptr->status |= FITSHDR_KEYVALUE; BEGIN(ERROR); } - for (cptr = ctmp, k++; fitshdrtext[k] != ')'; cptr++, k++) { - *cptr = fitshdrtext[k]; + for (cptr = ctmp, k++; yytext[k] != ')'; cptr++, k++) { + *cptr = yytext[k]; } *cptr = '\0'; - if (wcsutil_str2double(ctmp, "%lf", kptr->keyvalue.c+1)) { + if (wcsutil_str2double(ctmp, kptr->keyvalue.c+1)) { kptr->status |= FITSHDR_KEYVALUE; BEGIN(ERROR); } @@ -10758,16 +10981,16 @@ YY_RULE_SETUP case 19: /* rule 19 can match eol */ YY_RULE_SETUP -#line 394 "fitshdr.l" +#line 422 "fitshdr.l" { - /* String keyvalue. */ + // String keyvalue. kptr->type = 8; - cptr = kptr->keyvalue.s; - strcpy(cptr, fitshdrtext+1); + char *cptr = kptr->keyvalue.s; + strcpy(cptr, yytext+1); - /* Squeeze out repeated quotes. */ - k = 0; - for (j = 0; j < 72; j++) { + // Squeeze out repeated quotes. + int k = 0; + for (int j = 0; j < 72; j++) { if (k < j) { cptr[k] = cptr[j]; } @@ -10783,7 +11006,7 @@ YY_RULE_SETUP } if (*cptr) { - /* Retain the initial blank in all-blank strings. */ + // Retain the initial blank in all-blank strings. nullfill(cptr+1, 71); } else { nullfill(cptr, 72); @@ -10794,42 +11017,42 @@ YY_RULE_SETUP YY_BREAK case 20: YY_RULE_SETUP -#line 427 "fitshdr.l" +#line 455 "fitshdr.l" { kptr->status |= FITSHDR_KEYVALUE; BEGIN(ERROR); } YY_BREAK case 21: -*yy_cp = (yy_hold_char); /* undo effects of setting up fitshdrtext */ -(yy_c_buf_p) = yy_cp -= 1; -YY_DO_BEFORE_ACTION; /* set up fitshdrtext again */ +*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ +yyg->yy_c_buf_p = yy_cp -= 1; +YY_DO_BEFORE_ACTION; /* set up yytext again */ YY_RULE_SETUP -#line 432 "fitshdr.l" +#line 460 "fitshdr.l" { BEGIN(FLUSH); } YY_BREAK case 22: -*yy_cp = (yy_hold_char); /* undo effects of setting up fitshdrtext */ -(yy_c_buf_p) = yy_cp -= 1; -YY_DO_BEFORE_ACTION; /* set up fitshdrtext again */ +*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ +yyg->yy_c_buf_p = yy_cp -= 1; +YY_DO_BEFORE_ACTION; /* set up yytext again */ YY_RULE_SETUP -#line 436 "fitshdr.l" +#line 464 "fitshdr.l" { BEGIN(FLUSH); } YY_BREAK case 23: YY_RULE_SETUP -#line 440 "fitshdr.l" +#line 468 "fitshdr.l" { BEGIN(UNITS); } YY_BREAK case 24: YY_RULE_SETUP -#line 444 "fitshdr.l" +#line 472 "fitshdr.l" { kptr->status |= FITSHDR_COMMENT; BEGIN(ERROR); @@ -10837,9 +11060,9 @@ YY_RULE_SETUP YY_BREAK case 25: YY_RULE_SETUP -#line 449 "fitshdr.l" +#line 477 "fitshdr.l" { - /* Keyvalue parsing must now also be suspect. */ + // Keyvalue parsing must now also be suspect. kptr->status |= FITSHDR_COMMENT; kptr->type = 0; BEGIN(ERROR); @@ -10847,16 +11070,16 @@ YY_RULE_SETUP YY_BREAK case 26: YY_RULE_SETUP -#line 456 "fitshdr.l" +#line 484 "fitshdr.l" { - kptr->ulen = fitshdrleng; + kptr->ulen = yyleng; yymore(); BEGIN(COMMENT); } YY_BREAK case 27: YY_RULE_SETUP -#line 462 "fitshdr.l" +#line 490 "fitshdr.l" { yymore(); BEGIN(COMMENT); @@ -10864,20 +11087,20 @@ YY_RULE_SETUP YY_BREAK case 28: YY_RULE_SETUP -#line 467 "fitshdr.l" +#line 495 "fitshdr.l" { - strcpy(kptr->comment, fitshdrtext); + strcpy(kptr->comment, yytext); nullfill(kptr->comment, 84); BEGIN(FLUSH); } YY_BREAK case 29: YY_RULE_SETUP -#line 473 "fitshdr.l" +#line 501 "fitshdr.l" { if (!continuation) kptr->type = -abs(kptr->type); - sprintf(kptr->comment, "%.80s", fitshdr_hdr-80); + sprintf(kptr->comment, "%.80s", yyextra->hdr-80); kptr->comment[80] = '\0'; nullfill(kptr->comment+80, 4); @@ -10887,20 +11110,21 @@ YY_RULE_SETUP case 30: /* rule 30 can match eol */ YY_RULE_SETUP -#line 483 "fitshdr.l" +#line 511 "fitshdr.l" { - /* Discard the rest of the input line. */ + // Discard the rest of the input line. kptr->keyno = ++keyno; - /* Null-fill the keyword. */ + // Null-fill the keyword. kptr->keyword[8] = '\0'; nullfill(kptr->keyword, 12); - /* Do indexing. */ + // Do indexing. iptr = keyids; kptr->keyid = -1; - for (j = 0; j < nkeyids; j++, iptr++) { - cptr = iptr->name; + for (int j = 0; j < nkeyids; j++, iptr++) { + int k; + char *cptr = iptr->name; cptr[8] = '\0'; nullfill(cptr, 12); for (k = 0; k < 8; k++, cptr++) { @@ -10908,7 +11132,7 @@ YY_RULE_SETUP } if (k == 8) { - /* Found a match. */ + // Found a match. iptr->count++; if (iptr->idx[0] == -1) { iptr->idx[0] = keyno-1; @@ -10921,17 +11145,17 @@ YY_RULE_SETUP } } - /* Deal with continued strings. */ + // Deal with continued strings. if (continuation) { - /* Tidy up the previous string keyvalue. */ + // Tidy up the previous string keyvalue. if ((kptr-1)->type == 8) (kptr-1)->type += 10; - cptr = (kptr-1)->keyvalue.s; + char *cptr = (kptr-1)->keyvalue.s; if (cptr[strlen(cptr)-1] == '&') cptr[strlen(cptr)-1] = '\0'; kptr->type = (kptr-1)->type + 10; } - /* Check for keyrecords following the END keyrecord. */ + // Check for keyrecords following the END keyrecord. if (end && (end++ > 1) && !blank) { kptr->status |= FITSHDR_TRAILER; } @@ -10951,42 +11175,41 @@ case YY_STATE_EOF(UNITS): case YY_STATE_EOF(COMMENT): case YY_STATE_EOF(ERROR): case YY_STATE_EOF(FLUSH): -#line 539 "fitshdr.l" +#line 568 "fitshdr.l" { - /* End-of-input. */ - fitshdrlex_destroy(); + // End-of-input. return 0; } YY_BREAK case 31: YY_RULE_SETUP -#line 545 "fitshdr.l" +#line 573 "fitshdr.l" ECHO; YY_BREAK -#line 10967 "fitshdr.c" +#line 11190 "fitshdr.c" case YY_END_OF_BUFFER: { /* Amount of text matched not including the EOB char. */ - int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1; + int yy_amount_of_matched_text = (int) (yy_cp - yyg->yytext_ptr) - 1; /* Undo the effects of YY_DO_BEFORE_ACTION. */ - *yy_cp = (yy_hold_char); + *yy_cp = yyg->yy_hold_char; YY_RESTORE_YY_MORE_OFFSET if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) { /* We're scanning a new file or input source. It's * possible that this happened because the user - * just pointed fitshdrin at a new source and called - * fitshdrlex(). If so, then we have to assure + * just pointed yyin at a new source and called + * yylex(). If so, then we have to assure * consistency between YY_CURRENT_BUFFER and our * globals. Here is the right place to do so, because * this is the first action (other than possibly a * back-up) that will match for the new input source. */ - (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; - YY_CURRENT_BUFFER_LVALUE->yy_input_file = fitshdrin; + yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin; YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; } @@ -10997,13 +11220,13 @@ ECHO; * end-of-buffer state). Contrast this with the test * in input(). */ - if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) + if ( yyg->yy_c_buf_p <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) { /* This was really a NUL. */ yy_state_type yy_next_state; - (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text; + yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text; - yy_current_state = yy_get_previous_state( ); + yy_current_state = yy_get_previous_state( yyscanner ); /* Okay, we're now positioned to make the NUL * transition. We couldn't have @@ -11014,43 +11237,43 @@ ECHO; * will run more slowly). */ - yy_next_state = yy_try_NUL_trans( yy_current_state ); + yy_next_state = yy_try_NUL_trans( yy_current_state , yyscanner); - yy_bp = (yytext_ptr) + YY_MORE_ADJ; + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; if ( yy_next_state ) { /* Consume the NUL. */ - yy_cp = ++(yy_c_buf_p); + yy_cp = ++yyg->yy_c_buf_p; yy_current_state = yy_next_state; goto yy_match; } else { - yy_cp = (yy_c_buf_p); + yy_cp = yyg->yy_c_buf_p; goto yy_find_action; } } - else switch ( yy_get_next_buffer( ) ) + else switch ( yy_get_next_buffer( yyscanner ) ) { case EOB_ACT_END_OF_FILE: { - (yy_did_buffer_switch_on_eof) = 0; + yyg->yy_did_buffer_switch_on_eof = 0; - if ( fitshdrwrap( ) ) + if ( yywrap( yyscanner ) ) { /* Note: because we've taken care in * yy_get_next_buffer() to have set up - * fitshdrtext, we can now set up + * yytext, we can now set up * yy_c_buf_p so that if some total * hoser (like flex itself) wants to * call the scanner after we return the * YY_NULL, it'll still work - another * YY_NULL will get returned. */ - (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ; + yyg->yy_c_buf_p = yyg->yytext_ptr + YY_MORE_ADJ; yy_act = YY_STATE_EOF(YY_START); goto do_action; @@ -11058,30 +11281,30 @@ ECHO; else { - if ( ! (yy_did_buffer_switch_on_eof) ) + if ( ! yyg->yy_did_buffer_switch_on_eof ) YY_NEW_FILE; } break; } case EOB_ACT_CONTINUE_SCAN: - (yy_c_buf_p) = - (yytext_ptr) + yy_amount_of_matched_text; + yyg->yy_c_buf_p = + yyg->yytext_ptr + yy_amount_of_matched_text; - yy_current_state = yy_get_previous_state( ); + yy_current_state = yy_get_previous_state( yyscanner ); - yy_cp = (yy_c_buf_p); - yy_bp = (yytext_ptr) + YY_MORE_ADJ; + yy_cp = yyg->yy_c_buf_p; + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; goto yy_match; case EOB_ACT_LAST_MATCH: - (yy_c_buf_p) = - &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)]; + yyg->yy_c_buf_p = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars]; - yy_current_state = yy_get_previous_state( ); + yy_current_state = yy_get_previous_state( yyscanner ); - yy_cp = (yy_c_buf_p); - yy_bp = (yytext_ptr) + YY_MORE_ADJ; + yy_cp = yyg->yy_c_buf_p; + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; goto yy_find_action; } break; @@ -11092,7 +11315,8 @@ ECHO; "fatal flex scanner internal error--no action found" ); } /* end of action switch */ } /* end of scanning one token */ -} /* end of fitshdrlex */ + } /* end of user's declarations */ +} /* end of yylex */ /* yy_get_next_buffer - try to read in a new buffer * @@ -11101,20 +11325,21 @@ ECHO; * EOB_ACT_CONTINUE_SCAN - continue scanning from current position * EOB_ACT_END_OF_FILE - end of file */ -static int yy_get_next_buffer (void) +static int yy_get_next_buffer (yyscan_t yyscanner) { - register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; - register char *source = (yytext_ptr); - register int number_to_move, i; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; + char *source = yyg->yytext_ptr; + int number_to_move, i; int ret_val; - if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] ) + if ( yyg->yy_c_buf_p > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] ) YY_FATAL_ERROR( "fatal flex scanner internal error--end of buffer missed" ); if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) { /* Don't try to fill the buffer, so this is an EOF. */ - if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 ) + if ( yyg->yy_c_buf_p - yyg->yytext_ptr - YY_MORE_ADJ == 1 ) { /* We matched a single character, the EOB, so * treat this as a final EOF. @@ -11134,7 +11359,7 @@ static int yy_get_next_buffer (void) /* Try to read more data. */ /* First move last chars to start of buffer. */ - number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr)) - 1; + number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr - 1); for ( i = 0; i < number_to_move; ++i ) *(dest++) = *(source++); @@ -11143,7 +11368,7 @@ static int yy_get_next_buffer (void) /* don't do the read, it's not guaranteed to return an EOF, * just force an EOF */ - YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0; + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars = 0; else { @@ -11154,10 +11379,10 @@ static int yy_get_next_buffer (void) { /* Not enough room in the buffer - grow it. */ /* just a shorter name for the current buffer */ - YY_BUFFER_STATE b = YY_CURRENT_BUFFER; + YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE; int yy_c_buf_p_offset = - (int) ((yy_c_buf_p) - b->yy_ch_buf); + (int) (yyg->yy_c_buf_p - b->yy_ch_buf); if ( b->yy_is_our_buffer ) { @@ -11170,17 +11395,18 @@ static int yy_get_next_buffer (void) b->yy_ch_buf = (char *) /* Include room in for 2 EOB chars. */ - fitshdrrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 ); + yyrealloc( (void *) b->yy_ch_buf, + (yy_size_t) (b->yy_buf_size + 2) , yyscanner ); } else /* Can't grow it, we don't own it. */ - b->yy_ch_buf = 0; + b->yy_ch_buf = NULL; if ( ! b->yy_ch_buf ) YY_FATAL_ERROR( "fatal error - scanner input buffer overflow" ); - (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset]; + yyg->yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset]; num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; @@ -11192,17 +11418,17 @@ static int yy_get_next_buffer (void) /* Read in more data. */ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), - (yy_n_chars), (size_t) num_to_read ); + yyg->yy_n_chars, num_to_read ); - YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; } - if ( (yy_n_chars) == 0 ) + if ( yyg->yy_n_chars == 0 ) { if ( number_to_move == YY_MORE_ADJ ) { ret_val = EOB_ACT_END_OF_FILE; - fitshdrrestart(fitshdrin ); + yyrestart( yyin , yyscanner); } else @@ -11216,34 +11442,38 @@ static int yy_get_next_buffer (void) else ret_val = EOB_ACT_CONTINUE_SCAN; - if ((yy_size_t) ((yy_n_chars) + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { + if ((yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { /* Extend the array by 50%, plus the number we really need. */ - yy_size_t new_size = (yy_n_chars) + number_to_move + ((yy_n_chars) >> 1); - YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) fitshdrrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size ); + int new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1); + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc( + (void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf, (yy_size_t) new_size , yyscanner ); if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); + /* "- 2" to take care of EOB's */ + YY_CURRENT_BUFFER_LVALUE->yy_buf_size = (int) (new_size - 2); } - (yy_n_chars) += number_to_move; - YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR; - YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR; + yyg->yy_n_chars += number_to_move; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] = YY_END_OF_BUFFER_CHAR; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR; - (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; + yyg->yytext_ptr = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; return ret_val; } /* yy_get_previous_state - get the state just before the EOB char was reached */ - static yy_state_type yy_get_previous_state (void) + static yy_state_type yy_get_previous_state (yyscan_t yyscanner) { - register yy_state_type yy_current_state; - register char *yy_cp; - - yy_current_state = (yy_start); + yy_state_type yy_current_state; + char *yy_cp; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + yy_current_state = yyg->yy_start; yy_current_state += YY_AT_BOL(); - for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp ) + for ( yy_cp = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp ) { if ( *yy_cp ) { @@ -11253,8 +11483,8 @@ static int yy_get_next_buffer (void) yy_current_state = yy_NUL_trans[yy_current_state]; if ( yy_accept[yy_current_state] ) { - (yy_last_accepting_state) = yy_current_state; - (yy_last_accepting_cpos) = yy_cp; + yyg->yy_last_accepting_state = yy_current_state; + yyg->yy_last_accepting_cpos = yy_cp; } } @@ -11266,10 +11496,11 @@ static int yy_get_next_buffer (void) * synopsis * next_state = yy_try_NUL_trans( current_state ); */ - static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state ) + static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state , yyscan_t yyscanner) { - register int yy_is_jam; - register char *yy_cp = (yy_c_buf_p); + int yy_is_jam; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */ + char *yy_cp = yyg->yy_c_buf_p; yy_current_state = yy_NUL_trans[yy_current_state]; yy_is_jam = (yy_current_state == 0); @@ -11278,42 +11509,48 @@ static int yy_get_next_buffer (void) { if ( yy_accept[yy_current_state] ) { - (yy_last_accepting_state) = yy_current_state; - (yy_last_accepting_cpos) = yy_cp; + yyg->yy_last_accepting_state = yy_current_state; + yyg->yy_last_accepting_cpos = yy_cp; } } + (void)yyg; return yy_is_jam ? 0 : yy_current_state; } +#ifndef YY_NO_UNPUT + +#endif + #ifndef YY_NO_INPUT #ifdef __cplusplus - static int yyinput (void) + static int yyinput (yyscan_t yyscanner) #else - static int input (void) + static int input (yyscan_t yyscanner) #endif { int c; - - *(yy_c_buf_p) = (yy_hold_char); + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + *yyg->yy_c_buf_p = yyg->yy_hold_char; - if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR ) + if ( *yyg->yy_c_buf_p == YY_END_OF_BUFFER_CHAR ) { /* yy_c_buf_p now points to the character we want to return. * If this occurs *before* the EOB characters, then it's a * valid NUL; if not, then we've hit the end of the buffer. */ - if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) + if ( yyg->yy_c_buf_p < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) /* This was really a NUL. */ - *(yy_c_buf_p) = '\0'; + *yyg->yy_c_buf_p = '\0'; else { /* need more input */ - int offset = (yy_c_buf_p) - (yytext_ptr); - ++(yy_c_buf_p); + int offset = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr); + ++yyg->yy_c_buf_p; - switch ( yy_get_next_buffer( ) ) + switch ( yy_get_next_buffer( yyscanner ) ) { case EOB_ACT_LAST_MATCH: /* This happens because yy_g_n_b() @@ -11327,34 +11564,34 @@ static int yy_get_next_buffer (void) */ /* Reset buffer status. */ - fitshdrrestart(fitshdrin ); + yyrestart( yyin , yyscanner); /*FALLTHROUGH*/ case EOB_ACT_END_OF_FILE: { - if ( fitshdrwrap( ) ) - return EOF; + if ( yywrap( yyscanner ) ) + return 0; - if ( ! (yy_did_buffer_switch_on_eof) ) + if ( ! yyg->yy_did_buffer_switch_on_eof ) YY_NEW_FILE; #ifdef __cplusplus - return yyinput(); + return yyinput(yyscanner); #else - return input(); + return input(yyscanner); #endif } case EOB_ACT_CONTINUE_SCAN: - (yy_c_buf_p) = (yytext_ptr) + offset; + yyg->yy_c_buf_p = yyg->yytext_ptr + offset; break; } } } - c = *(unsigned char *) (yy_c_buf_p); /* cast for 8-bit char's */ - *(yy_c_buf_p) = '\0'; /* preserve fitshdrtext */ - (yy_hold_char) = *++(yy_c_buf_p); + c = *(unsigned char *) yyg->yy_c_buf_p; /* cast for 8-bit char's */ + *yyg->yy_c_buf_p = '\0'; /* preserve yytext */ + yyg->yy_hold_char = *++yyg->yy_c_buf_p; YY_CURRENT_BUFFER_LVALUE->yy_at_bol = (c == '\n'); @@ -11364,102 +11601,106 @@ static int yy_get_next_buffer (void) /** Immediately switch to a different input stream. * @param input_file A readable stream. - * + * @param yyscanner The scanner object. * @note This function does not reset the start condition to @c INITIAL . */ - void fitshdrrestart (FILE * input_file ) + void yyrestart (FILE * input_file , yyscan_t yyscanner) { - + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if ( ! YY_CURRENT_BUFFER ){ - fitshdrensure_buffer_stack (); + yyensure_buffer_stack (yyscanner); YY_CURRENT_BUFFER_LVALUE = - fitshdr_create_buffer(fitshdrin,YY_BUF_SIZE ); + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); } - fitshdr_init_buffer(YY_CURRENT_BUFFER,input_file ); - fitshdr_load_buffer_state( ); + yy_init_buffer( YY_CURRENT_BUFFER, input_file , yyscanner); + yy_load_buffer_state( yyscanner ); } /** Switch to a different input buffer. * @param new_buffer The new input buffer. - * + * @param yyscanner The scanner object. */ - void fitshdr_switch_to_buffer (YY_BUFFER_STATE new_buffer ) + void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) { - + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + /* TODO. We should be able to replace this entire function body * with - * fitshdrpop_buffer_state(); - * fitshdrpush_buffer_state(new_buffer); + * yypop_buffer_state(); + * yypush_buffer_state(new_buffer); */ - fitshdrensure_buffer_stack (); + yyensure_buffer_stack (yyscanner); if ( YY_CURRENT_BUFFER == new_buffer ) return; if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ - *(yy_c_buf_p) = (yy_hold_char); - YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); - YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + *yyg->yy_c_buf_p = yyg->yy_hold_char; + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; } YY_CURRENT_BUFFER_LVALUE = new_buffer; - fitshdr_load_buffer_state( ); + yy_load_buffer_state( yyscanner ); /* We don't actually know whether we did this switch during - * EOF (fitshdrwrap()) processing, but the only time this flag - * is looked at is after fitshdrwrap() is called, so it's safe + * EOF (yywrap()) processing, but the only time this flag + * is looked at is after yywrap() is called, so it's safe * to go ahead and always set it. */ - (yy_did_buffer_switch_on_eof) = 1; + yyg->yy_did_buffer_switch_on_eof = 1; } -static void fitshdr_load_buffer_state (void) +static void yy_load_buffer_state (yyscan_t yyscanner) { - (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; - (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; - fitshdrin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; - (yy_hold_char) = *(yy_c_buf_p); + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + yyg->yytext_ptr = yyg->yy_c_buf_p = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; + yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; + yyg->yy_hold_char = *yyg->yy_c_buf_p; } /** Allocate and initialize an input buffer state. * @param file A readable stream. * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. - * + * @param yyscanner The scanner object. * @return the allocated buffer state. */ - YY_BUFFER_STATE fitshdr_create_buffer (FILE * file, int size ) + YY_BUFFER_STATE yy_create_buffer (FILE * file, int size , yyscan_t yyscanner) { YY_BUFFER_STATE b; - b = (YY_BUFFER_STATE) fitshdralloc(sizeof( struct yy_buffer_state ) ); + b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner ); if ( ! b ) - YY_FATAL_ERROR( "out of dynamic memory in fitshdr_create_buffer()" ); + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); b->yy_buf_size = size; /* yy_ch_buf has to be 2 characters longer than the size given because * we need to put in 2 end-of-buffer characters. */ - b->yy_ch_buf = (char *) fitshdralloc(b->yy_buf_size + 2 ); + b->yy_ch_buf = (char *) yyalloc( (yy_size_t) (b->yy_buf_size + 2) , yyscanner ); if ( ! b->yy_ch_buf ) - YY_FATAL_ERROR( "out of dynamic memory in fitshdr_create_buffer()" ); + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); b->yy_is_our_buffer = 1; - fitshdr_init_buffer(b,file ); + yy_init_buffer( b, file , yyscanner); return b; } /** Destroy the buffer. - * @param b a buffer created with fitshdr_create_buffer() - * + * @param b a buffer created with yy_create_buffer() + * @param yyscanner The scanner object. */ - void fitshdr_delete_buffer (YY_BUFFER_STATE b ) + void yy_delete_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) { - + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if ( ! b ) return; @@ -11467,27 +11708,28 @@ static void fitshdr_load_buffer_state (void) YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; if ( b->yy_is_our_buffer ) - fitshdrfree((void *) b->yy_ch_buf ); + yyfree( (void *) b->yy_ch_buf , yyscanner ); - fitshdrfree((void *) b ); + yyfree( (void *) b , yyscanner ); } /* Initializes or reinitializes a buffer. * This function is sometimes called more than once on the same buffer, - * such as during a fitshdrrestart() or at EOF. + * such as during a yyrestart() or at EOF. */ - static void fitshdr_init_buffer (YY_BUFFER_STATE b, FILE * file ) + static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file , yyscan_t yyscanner) { int oerrno = errno; - - fitshdr_flush_buffer(b ); + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + yy_flush_buffer( b , yyscanner); b->yy_input_file = file; b->yy_fill_buffer = 1; - /* If b is the current buffer, then fitshdr_init_buffer was _probably_ - * called from fitshdrrestart() or through yy_get_next_buffer. + /* If b is the current buffer, then yy_init_buffer was _probably_ + * called from yyrestart() or through yy_get_next_buffer. * In that case, we don't want to reset the lineno or column. */ if (b != YY_CURRENT_BUFFER){ @@ -11502,11 +11744,12 @@ static void fitshdr_load_buffer_state (void) /** Discard all buffered characters. On the next scan, YY_INPUT will be called. * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. - * + * @param yyscanner The scanner object. */ - void fitshdr_flush_buffer (YY_BUFFER_STATE b ) + void yy_flush_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) { - if ( ! b ) + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if ( ! b ) return; b->yy_n_chars = 0; @@ -11524,114 +11767,117 @@ static void fitshdr_load_buffer_state (void) b->yy_buffer_status = YY_BUFFER_NEW; if ( b == YY_CURRENT_BUFFER ) - fitshdr_load_buffer_state( ); + yy_load_buffer_state( yyscanner ); } /** Pushes the new state onto the stack. The new state becomes * the current state. This function will allocate the stack * if necessary. * @param new_buffer The new state. - * + * @param yyscanner The scanner object. */ -void fitshdrpush_buffer_state (YY_BUFFER_STATE new_buffer ) +void yypush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) { - if (new_buffer == NULL) + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if (new_buffer == NULL) return; - fitshdrensure_buffer_stack(); + yyensure_buffer_stack(yyscanner); - /* This block is copied from fitshdr_switch_to_buffer. */ + /* This block is copied from yy_switch_to_buffer. */ if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ - *(yy_c_buf_p) = (yy_hold_char); - YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); - YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + *yyg->yy_c_buf_p = yyg->yy_hold_char; + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; } /* Only push if top exists. Otherwise, replace top. */ if (YY_CURRENT_BUFFER) - (yy_buffer_stack_top)++; + yyg->yy_buffer_stack_top++; YY_CURRENT_BUFFER_LVALUE = new_buffer; - /* copied from fitshdr_switch_to_buffer. */ - fitshdr_load_buffer_state( ); - (yy_did_buffer_switch_on_eof) = 1; + /* copied from yy_switch_to_buffer. */ + yy_load_buffer_state( yyscanner ); + yyg->yy_did_buffer_switch_on_eof = 1; } /** Removes and deletes the top of the stack, if present. * The next element becomes the new top. - * + * @param yyscanner The scanner object. */ -void fitshdrpop_buffer_state (void) +void yypop_buffer_state (yyscan_t yyscanner) { - if (!YY_CURRENT_BUFFER) + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if (!YY_CURRENT_BUFFER) return; - fitshdr_delete_buffer(YY_CURRENT_BUFFER ); + yy_delete_buffer(YY_CURRENT_BUFFER , yyscanner); YY_CURRENT_BUFFER_LVALUE = NULL; - if ((yy_buffer_stack_top) > 0) - --(yy_buffer_stack_top); + if (yyg->yy_buffer_stack_top > 0) + --yyg->yy_buffer_stack_top; if (YY_CURRENT_BUFFER) { - fitshdr_load_buffer_state( ); - (yy_did_buffer_switch_on_eof) = 1; + yy_load_buffer_state( yyscanner ); + yyg->yy_did_buffer_switch_on_eof = 1; } } /* Allocates the stack if it does not exist. * Guarantees space for at least one push. */ -static void fitshdrensure_buffer_stack (void) +static void yyensure_buffer_stack (yyscan_t yyscanner) { - int num_to_alloc; - - if (!(yy_buffer_stack)) { + yy_size_t num_to_alloc; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (!yyg->yy_buffer_stack) { /* First allocation is just for 2 elements, since we don't know if this * scanner will even need a stack. We use 2 instead of 1 to avoid an * immediate realloc on the next call. */ - num_to_alloc = 1; - (yy_buffer_stack) = (struct yy_buffer_state**)fitshdralloc + num_to_alloc = 1; /* After all that talk, this was set to 1 anyways... */ + yyg->yy_buffer_stack = (struct yy_buffer_state**)yyalloc (num_to_alloc * sizeof(struct yy_buffer_state*) - ); - if ( ! (yy_buffer_stack) ) - YY_FATAL_ERROR( "out of dynamic memory in fitshdrensure_buffer_stack()" ); - - memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*)); - - (yy_buffer_stack_max) = num_to_alloc; - (yy_buffer_stack_top) = 0; + , yyscanner); + if ( ! yyg->yy_buffer_stack ) + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); + + memset(yyg->yy_buffer_stack, 0, num_to_alloc * sizeof(struct yy_buffer_state*)); + + yyg->yy_buffer_stack_max = num_to_alloc; + yyg->yy_buffer_stack_top = 0; return; } - if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){ + if (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){ /* Increase the buffer to prepare for a possible push. */ - int grow_size = 8 /* arbitrary grow size */; + yy_size_t grow_size = 8 /* arbitrary grow size */; - num_to_alloc = (yy_buffer_stack_max) + grow_size; - (yy_buffer_stack) = (struct yy_buffer_state**)fitshdrrealloc - ((yy_buffer_stack), + num_to_alloc = yyg->yy_buffer_stack_max + grow_size; + yyg->yy_buffer_stack = (struct yy_buffer_state**)yyrealloc + (yyg->yy_buffer_stack, num_to_alloc * sizeof(struct yy_buffer_state*) - ); - if ( ! (yy_buffer_stack) ) - YY_FATAL_ERROR( "out of dynamic memory in fitshdrensure_buffer_stack()" ); + , yyscanner); + if ( ! yyg->yy_buffer_stack ) + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); /* zero only the new slots.*/ - memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*)); - (yy_buffer_stack_max) = num_to_alloc; + memset(yyg->yy_buffer_stack + yyg->yy_buffer_stack_max, 0, grow_size * sizeof(struct yy_buffer_state*)); + yyg->yy_buffer_stack_max = num_to_alloc; } } /** Setup the input buffer state to scan directly from a user-specified character buffer. * @param base the character buffer * @param size the size in bytes of the character buffer - * - * @return the newly allocated buffer state object. + * @param yyscanner The scanner object. + * @return the newly allocated buffer state object. */ -YY_BUFFER_STATE fitshdr_scan_buffer (char * base, yy_size_t size ) +YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size , yyscan_t yyscanner) { YY_BUFFER_STATE b; @@ -11639,49 +11885,49 @@ YY_BUFFER_STATE fitshdr_scan_buffer (char * base, yy_size_t size ) base[size-2] != YY_END_OF_BUFFER_CHAR || base[size-1] != YY_END_OF_BUFFER_CHAR ) /* They forgot to leave room for the EOB's. */ - return 0; + return NULL; - b = (YY_BUFFER_STATE) fitshdralloc(sizeof( struct yy_buffer_state ) ); + b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner ); if ( ! b ) - YY_FATAL_ERROR( "out of dynamic memory in fitshdr_scan_buffer()" ); + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); - b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */ + b->yy_buf_size = (int) (size - 2); /* "- 2" to take care of EOB's */ b->yy_buf_pos = b->yy_ch_buf = base; b->yy_is_our_buffer = 0; - b->yy_input_file = 0; + b->yy_input_file = NULL; b->yy_n_chars = b->yy_buf_size; b->yy_is_interactive = 0; b->yy_at_bol = 1; b->yy_fill_buffer = 0; b->yy_buffer_status = YY_BUFFER_NEW; - fitshdr_switch_to_buffer(b ); + yy_switch_to_buffer( b , yyscanner ); return b; } -/** Setup the input buffer state to scan a string. The next call to fitshdrlex() will +/** Setup the input buffer state to scan a string. The next call to yylex() will * scan from a @e copy of @a str. * @param yystr a NUL-terminated string to scan - * + * @param yyscanner The scanner object. * @return the newly allocated buffer state object. * @note If you want to scan bytes that may contain NUL values, then use - * fitshdr_scan_bytes() instead. + * yy_scan_bytes() instead. */ -YY_BUFFER_STATE fitshdr_scan_string (yyconst char * yystr ) +YY_BUFFER_STATE yy_scan_string (const char * yystr , yyscan_t yyscanner) { - return fitshdr_scan_bytes(yystr,strlen(yystr) ); + return yy_scan_bytes( yystr, (int) strlen(yystr) , yyscanner); } -/** Setup the input buffer state to scan the given bytes. The next call to fitshdrlex() will +/** Setup the input buffer state to scan the given bytes. The next call to yylex() will * scan from a @e copy of @a bytes. * @param yybytes the byte buffer to scan * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes. - * + * @param yyscanner The scanner object. * @return the newly allocated buffer state object. */ -YY_BUFFER_STATE fitshdr_scan_bytes (yyconst char * yybytes, int _yybytes_len ) +YY_BUFFER_STATE yy_scan_bytes (const char * yybytes, int _yybytes_len , yyscan_t yyscanner) { YY_BUFFER_STATE b; char *buf; @@ -11689,19 +11935,19 @@ YY_BUFFER_STATE fitshdr_scan_bytes (yyconst char * yybytes, int _yybytes_len ) int i; /* Get memory for full buffer, including space for trailing EOB's. */ - n = _yybytes_len + 2; - buf = (char *) fitshdralloc(n ); + n = (yy_size_t) (_yybytes_len + 2); + buf = (char *) yyalloc( n , yyscanner ); if ( ! buf ) - YY_FATAL_ERROR( "out of dynamic memory in fitshdr_scan_bytes()" ); + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" ); for ( i = 0; i < _yybytes_len; ++i ) buf[i] = yybytes[i]; buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; - b = fitshdr_scan_buffer(buf,n ); + b = yy_scan_buffer( buf, n , yyscanner); if ( ! b ) - YY_FATAL_ERROR( "bad buffer in fitshdr_scan_bytes()" ); + YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" ); /* It's okay to grow etc. this buffer, and we should throw it * away when we're done. @@ -11715,9 +11961,11 @@ YY_BUFFER_STATE fitshdr_scan_bytes (yyconst char * yybytes, int _yybytes_len ) #define YY_EXIT_FAILURE 2 #endif -static void yy_fatal_error (yyconst char* msg ) +static void yynoreturn yy_fatal_error (const char* msg , yyscan_t yyscanner) { - (void) fprintf( stderr, "%s\n", msg ); + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + fprintf( stderr, "%s\n", msg ); exit( YY_EXIT_FAILURE ); } @@ -11727,144 +11975,283 @@ static void yy_fatal_error (yyconst char* msg ) #define yyless(n) \ do \ { \ - /* Undo effects of setting up fitshdrtext. */ \ + /* Undo effects of setting up yytext. */ \ int yyless_macro_arg = (n); \ YY_LESS_LINENO(yyless_macro_arg);\ - fitshdrtext[fitshdrleng] = (yy_hold_char); \ - (yy_c_buf_p) = fitshdrtext + yyless_macro_arg; \ - (yy_hold_char) = *(yy_c_buf_p); \ - *(yy_c_buf_p) = '\0'; \ - fitshdrleng = yyless_macro_arg; \ + yytext[yyleng] = yyg->yy_hold_char; \ + yyg->yy_c_buf_p = yytext + yyless_macro_arg; \ + yyg->yy_hold_char = *yyg->yy_c_buf_p; \ + *yyg->yy_c_buf_p = '\0'; \ + yyleng = yyless_macro_arg; \ } \ while ( 0 ) /* Accessor methods (get/set functions) to struct members. */ +/** Get the user-defined data for this scanner. + * @param yyscanner The scanner object. + */ +YY_EXTRA_TYPE yyget_extra (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyextra; +} + /** Get the current line number. - * + * @param yyscanner The scanner object. + */ +int yyget_lineno (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (! YY_CURRENT_BUFFER) + return 0; + + return yylineno; +} + +/** Get the current column number. + * @param yyscanner The scanner object. */ -int fitshdrget_lineno (void) +int yyget_column (yyscan_t yyscanner) { - - return fitshdrlineno; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (! YY_CURRENT_BUFFER) + return 0; + + return yycolumn; } /** Get the input stream. - * + * @param yyscanner The scanner object. */ -FILE *fitshdrget_in (void) +FILE *yyget_in (yyscan_t yyscanner) { - return fitshdrin; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyin; } /** Get the output stream. - * + * @param yyscanner The scanner object. */ -FILE *fitshdrget_out (void) +FILE *yyget_out (yyscan_t yyscanner) { - return fitshdrout; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyout; } /** Get the length of the current token. - * + * @param yyscanner The scanner object. */ -int fitshdrget_leng (void) +int yyget_leng (yyscan_t yyscanner) { - return fitshdrleng; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyleng; } /** Get the current token. - * + * @param yyscanner The scanner object. */ -char *fitshdrget_text (void) +char *yyget_text (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yytext; +} + +/** Set the user-defined data. This data is never touched by the scanner. + * @param user_defined The data to be associated with this scanner. + * @param yyscanner The scanner object. + */ +void yyset_extra (YY_EXTRA_TYPE user_defined , yyscan_t yyscanner) { - return fitshdrtext; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyextra = user_defined ; } /** Set the current line number. - * @param line_number - * + * @param _line_number line number + * @param yyscanner The scanner object. */ -void fitshdrset_lineno (int line_number ) +void yyset_lineno (int _line_number , yyscan_t yyscanner) { + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* lineno is only valid if an input buffer exists. */ + if (! YY_CURRENT_BUFFER ) + YY_FATAL_ERROR( "yyset_lineno called with no buffer" ); - fitshdrlineno = line_number; + yylineno = _line_number; +} + +/** Set the current column. + * @param _column_no column number + * @param yyscanner The scanner object. + */ +void yyset_column (int _column_no , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* column is only valid if an input buffer exists. */ + if (! YY_CURRENT_BUFFER ) + YY_FATAL_ERROR( "yyset_column called with no buffer" ); + + yycolumn = _column_no; } /** Set the input stream. This does not discard the current * input buffer. - * @param in_str A readable stream. - * - * @see fitshdr_switch_to_buffer + * @param _in_str A readable stream. + * @param yyscanner The scanner object. + * @see yy_switch_to_buffer */ -void fitshdrset_in (FILE * in_str ) +void yyset_in (FILE * _in_str , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyin = _in_str ; +} + +void yyset_out (FILE * _out_str , yyscan_t yyscanner) { - fitshdrin = in_str ; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyout = _out_str ; } -void fitshdrset_out (FILE * out_str ) +int yyget_debug (yyscan_t yyscanner) { - fitshdrout = out_str ; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yy_flex_debug; } -int fitshdrget_debug (void) +void yyset_debug (int _bdebug , yyscan_t yyscanner) { - return fitshdr_flex_debug; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yy_flex_debug = _bdebug ; } -void fitshdrset_debug (int bdebug ) +/* Accessor methods for yylval and yylloc */ + +/* User-visible API */ + +/* yylex_init is special because it creates the scanner itself, so it is + * the ONLY reentrant function that doesn't take the scanner as the last argument. + * That's why we explicitly handle the declaration, instead of using our macros. + */ +int yylex_init(yyscan_t* ptr_yy_globals) { - fitshdr_flex_debug = bdebug ; + if (ptr_yy_globals == NULL){ + errno = EINVAL; + return 1; + } + + *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), NULL ); + + if (*ptr_yy_globals == NULL){ + errno = ENOMEM; + return 1; + } + + /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */ + memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); + + return yy_init_globals ( *ptr_yy_globals ); } -static int yy_init_globals (void) +/* yylex_init_extra has the same functionality as yylex_init, but follows the + * convention of taking the scanner as the last argument. Note however, that + * this is a *pointer* to a scanner, as it will be allocated by this call (and + * is the reason, too, why this function also must handle its own declaration). + * The user defined value in the first argument will be available to yyalloc in + * the yyextra field. + */ +int yylex_init_extra( YY_EXTRA_TYPE yy_user_defined, yyscan_t* ptr_yy_globals ) { - /* Initialization is the same as for the non-reentrant scanner. - * This function is called from fitshdrlex_destroy(), so don't allocate here. + struct yyguts_t dummy_yyguts; + + yyset_extra (yy_user_defined, &dummy_yyguts); + + if (ptr_yy_globals == NULL){ + errno = EINVAL; + return 1; + } + + *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), &dummy_yyguts ); + + if (*ptr_yy_globals == NULL){ + errno = ENOMEM; + return 1; + } + + /* By setting to 0xAA, we expose bugs in + yy_init_globals. Leave at 0x00 for releases. */ + memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); + + yyset_extra (yy_user_defined, *ptr_yy_globals); + + return yy_init_globals ( *ptr_yy_globals ); +} + +static int yy_init_globals (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + /* Initialization is the same as for the non-reentrant scanner. + * This function is called from yylex_destroy(), so don't allocate here. */ - (yy_buffer_stack) = 0; - (yy_buffer_stack_top) = 0; - (yy_buffer_stack_max) = 0; - (yy_c_buf_p) = (char *) 0; - (yy_init) = 0; - (yy_start) = 0; + yyg->yy_buffer_stack = NULL; + yyg->yy_buffer_stack_top = 0; + yyg->yy_buffer_stack_max = 0; + yyg->yy_c_buf_p = NULL; + yyg->yy_init = 0; + yyg->yy_start = 0; + + yyg->yy_start_stack_ptr = 0; + yyg->yy_start_stack_depth = 0; + yyg->yy_start_stack = NULL; /* Defined in main.c */ #ifdef YY_STDINIT - fitshdrin = stdin; - fitshdrout = stdout; + yyin = stdin; + yyout = stdout; #else - fitshdrin = (FILE *) 0; - fitshdrout = (FILE *) 0; + yyin = NULL; + yyout = NULL; #endif /* For future reference: Set errno on error, since we are called by - * fitshdrlex_init() + * yylex_init() */ return 0; } -/* fitshdrlex_destroy is for both reentrant and non-reentrant scanners. */ -int fitshdrlex_destroy (void) +/* yylex_destroy is for both reentrant and non-reentrant scanners. */ +int yylex_destroy (yyscan_t yyscanner) { - + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + /* Pop the buffer stack, destroying each element. */ while(YY_CURRENT_BUFFER){ - fitshdr_delete_buffer(YY_CURRENT_BUFFER ); + yy_delete_buffer( YY_CURRENT_BUFFER , yyscanner ); YY_CURRENT_BUFFER_LVALUE = NULL; - fitshdrpop_buffer_state(); + yypop_buffer_state(yyscanner); } /* Destroy the stack itself. */ - fitshdrfree((yy_buffer_stack) ); - (yy_buffer_stack) = NULL; + yyfree(yyg->yy_buffer_stack , yyscanner); + yyg->yy_buffer_stack = NULL; + + /* Destroy the start condition stack. */ + yyfree( yyg->yy_start_stack , yyscanner ); + yyg->yy_start_stack = NULL; /* Reset the globals. This is important in a non-reentrant scanner so the next time - * fitshdrlex() is called, initialization will occur. */ - yy_init_globals( ); + * yylex() is called, initialization will occur. */ + yy_init_globals( yyscanner); + /* Destroy the main struct (reentrant only). */ + yyfree ( yyscanner , yyscanner ); + yyscanner = NULL; return 0; } @@ -11873,18 +12260,21 @@ int fitshdrlex_destroy (void) */ #ifndef yytext_ptr -static void yy_flex_strncpy (char* s1, yyconst char * s2, int n ) +static void yy_flex_strncpy (char* s1, const char * s2, int n , yyscan_t yyscanner) { - register int i; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + + int i; for ( i = 0; i < n; ++i ) s1[i] = s2[i]; } #endif #ifdef YY_NEED_STRLEN -static int yy_flex_strlen (yyconst char * s ) +static int yy_flex_strlen (const char * s , yyscan_t yyscanner) { - register int n; + int n; for ( n = 0; s[n]; ++n ) ; @@ -11892,13 +12282,18 @@ static int yy_flex_strlen (yyconst char * s ) } #endif -void *fitshdralloc (yy_size_t size ) +void *yyalloc (yy_size_t size , yyscan_t yyscanner) { - return (void *) malloc( size ); + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + return malloc(size); } -void *fitshdrrealloc (void * ptr, yy_size_t size ) +void *yyrealloc (void * ptr, yy_size_t size , yyscan_t yyscanner) { + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + /* The cast to (char *) in the following accommodates both * implementations that use char* generic pointers, and those * that use void* generic pointers. It works with the latter @@ -11906,38 +12301,68 @@ void *fitshdrrealloc (void * ptr, yy_size_t size ) * any pointer type to void*, and deal with argument conversions * as though doing an assignment. */ - return (void *) realloc( (char *) ptr, size ); + return realloc(ptr, size); } -void fitshdrfree (void * ptr ) +void yyfree (void * ptr , yyscan_t yyscanner) { - free( (char *) ptr ); /* see fitshdrrealloc() for (char *) cast */ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + free( (char *) ptr ); /* see yyrealloc() for (char *) cast */ } #define YYTABLES_NAME "yytables" -#line 545 "fitshdr.l" +#line 573 "fitshdr.l" +/*---------------------------------------------------------------------------- +* External interface to the scanner. +*---------------------------------------------------------------------------*/ -/*--------------------------------------------------------------------------*/ +int fitshdr( + const char header[], + int nkeyrec, + int nkeyids, + struct fitskeyid keyids[], + int *nreject, + struct fitskey **keys) + +{ + // Function prototypes. + int yylex_init_extra(YY_EXTRA_TYPE extra, yyscan_t *yyscanner); + int yylex_destroy(yyscan_t yyscanner); + + struct fitshdr_extra extra; + yyscan_t yyscanner; + yylex_init_extra(&extra, &yyscanner); + int status = fitshdr_scanner(header, nkeyrec, nkeyids, keyids, nreject, + keys, yyscanner); + yylex_destroy(yyscanner); + + return status; +} + +/*---------------------------------------------------------------------------- +* Pad a string with null characters. +*---------------------------------------------------------------------------*/ void nullfill(char cptr[], int len) { - int j, k; - - /* Null-fill the string. */ + // Propagate the terminating null to the end of the string. + int j; for (j = 0; j < len; j++) { if (cptr[j] == '\0') { - for (k = j+1; k < len; k++) { + for (int k = j+1; k < len; k++) { cptr[k] = '\0'; } break; } } - for (k = j-1; k >= 0; k--) { + // Remove trailing blanks. + for (int k = j-1; k >= 0; k--) { if (cptr[k] != ' ') break; cptr[k] = '\0'; } diff --git a/cextern/wcslib/C/flexed/wcsbth.c b/cextern/wcslib/C/flexed/wcsbth.c index e2c3403167f2..a2896e7ac813 100644 --- a/cextern/wcslib/C/flexed/wcsbth.c +++ b/cextern/wcslib/C/flexed/wcsbth.c @@ -2,35 +2,227 @@ #line 4 "wcsbth.c" +#define _POSIX_C_SOURCE 1 #define YY_INT_ALIGNED short int /* A lexical scanner generated by flex */ +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 6 +#define YY_FLEX_SUBMINOR_VERSION 4 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +#ifdef yy_create_buffer +#define wcsbth_create_buffer_ALREADY_DEFINED +#else #define yy_create_buffer wcsbth_create_buffer +#endif + +#ifdef yy_delete_buffer +#define wcsbth_delete_buffer_ALREADY_DEFINED +#else #define yy_delete_buffer wcsbth_delete_buffer -#define yy_flex_debug wcsbth_flex_debug +#endif + +#ifdef yy_scan_buffer +#define wcsbth_scan_buffer_ALREADY_DEFINED +#else +#define yy_scan_buffer wcsbth_scan_buffer +#endif + +#ifdef yy_scan_string +#define wcsbth_scan_string_ALREADY_DEFINED +#else +#define yy_scan_string wcsbth_scan_string +#endif + +#ifdef yy_scan_bytes +#define wcsbth_scan_bytes_ALREADY_DEFINED +#else +#define yy_scan_bytes wcsbth_scan_bytes +#endif + +#ifdef yy_init_buffer +#define wcsbth_init_buffer_ALREADY_DEFINED +#else #define yy_init_buffer wcsbth_init_buffer +#endif + +#ifdef yy_flush_buffer +#define wcsbth_flush_buffer_ALREADY_DEFINED +#else #define yy_flush_buffer wcsbth_flush_buffer +#endif + +#ifdef yy_load_buffer_state +#define wcsbth_load_buffer_state_ALREADY_DEFINED +#else #define yy_load_buffer_state wcsbth_load_buffer_state +#endif + +#ifdef yy_switch_to_buffer +#define wcsbth_switch_to_buffer_ALREADY_DEFINED +#else #define yy_switch_to_buffer wcsbth_switch_to_buffer -#define yyin wcsbthin -#define yyleng wcsbthleng +#endif + +#ifdef yypush_buffer_state +#define wcsbthpush_buffer_state_ALREADY_DEFINED +#else +#define yypush_buffer_state wcsbthpush_buffer_state +#endif + +#ifdef yypop_buffer_state +#define wcsbthpop_buffer_state_ALREADY_DEFINED +#else +#define yypop_buffer_state wcsbthpop_buffer_state +#endif + +#ifdef yyensure_buffer_stack +#define wcsbthensure_buffer_stack_ALREADY_DEFINED +#else +#define yyensure_buffer_stack wcsbthensure_buffer_stack +#endif + +#ifdef yylex +#define wcsbthlex_ALREADY_DEFINED +#else #define yylex wcsbthlex -#define yylineno wcsbthlineno -#define yyout wcsbthout +#endif + +#ifdef yyrestart +#define wcsbthrestart_ALREADY_DEFINED +#else #define yyrestart wcsbthrestart -#define yytext wcsbthtext +#endif + +#ifdef yylex_init +#define wcsbthlex_init_ALREADY_DEFINED +#else +#define yylex_init wcsbthlex_init +#endif + +#ifdef yylex_init_extra +#define wcsbthlex_init_extra_ALREADY_DEFINED +#else +#define yylex_init_extra wcsbthlex_init_extra +#endif + +#ifdef yylex_destroy +#define wcsbthlex_destroy_ALREADY_DEFINED +#else +#define yylex_destroy wcsbthlex_destroy +#endif + +#ifdef yyget_debug +#define wcsbthget_debug_ALREADY_DEFINED +#else +#define yyget_debug wcsbthget_debug +#endif + +#ifdef yyset_debug +#define wcsbthset_debug_ALREADY_DEFINED +#else +#define yyset_debug wcsbthset_debug +#endif + +#ifdef yyget_extra +#define wcsbthget_extra_ALREADY_DEFINED +#else +#define yyget_extra wcsbthget_extra +#endif + +#ifdef yyset_extra +#define wcsbthset_extra_ALREADY_DEFINED +#else +#define yyset_extra wcsbthset_extra +#endif + +#ifdef yyget_in +#define wcsbthget_in_ALREADY_DEFINED +#else +#define yyget_in wcsbthget_in +#endif + +#ifdef yyset_in +#define wcsbthset_in_ALREADY_DEFINED +#else +#define yyset_in wcsbthset_in +#endif + +#ifdef yyget_out +#define wcsbthget_out_ALREADY_DEFINED +#else +#define yyget_out wcsbthget_out +#endif + +#ifdef yyset_out +#define wcsbthset_out_ALREADY_DEFINED +#else +#define yyset_out wcsbthset_out +#endif + +#ifdef yyget_leng +#define wcsbthget_leng_ALREADY_DEFINED +#else +#define yyget_leng wcsbthget_leng +#endif + +#ifdef yyget_text +#define wcsbthget_text_ALREADY_DEFINED +#else +#define yyget_text wcsbthget_text +#endif + +#ifdef yyget_lineno +#define wcsbthget_lineno_ALREADY_DEFINED +#else +#define yyget_lineno wcsbthget_lineno +#endif + +#ifdef yyset_lineno +#define wcsbthset_lineno_ALREADY_DEFINED +#else +#define yyset_lineno wcsbthset_lineno +#endif + +#ifdef yyget_column +#define wcsbthget_column_ALREADY_DEFINED +#else +#define yyget_column wcsbthget_column +#endif + +#ifdef yyset_column +#define wcsbthset_column_ALREADY_DEFINED +#else +#define yyset_column wcsbthset_column +#endif + +#ifdef yywrap +#define wcsbthwrap_ALREADY_DEFINED +#else #define yywrap wcsbthwrap +#endif + +#ifdef yyalloc +#define wcsbthalloc_ALREADY_DEFINED +#else #define yyalloc wcsbthalloc +#endif + +#ifdef yyrealloc +#define wcsbthrealloc_ALREADY_DEFINED +#else #define yyrealloc wcsbthrealloc -#define yyfree wcsbthfree +#endif -#define FLEX_SCANNER -#define YY_FLEX_MAJOR_VERSION 2 -#define YY_FLEX_MINOR_VERSION 5 -#define YY_FLEX_SUBMINOR_VERSION 35 -#if YY_FLEX_SUBMINOR_VERSION > 0 -#define FLEX_BETA +#ifdef yyfree +#define wcsbthfree_ALREADY_DEFINED +#else +#define yyfree wcsbthfree #endif /* First, we deal with platform-specific or compiler-specific issues. */ @@ -103,60 +295,65 @@ typedef unsigned int flex_uint32_t; #define UINT32_MAX (4294967295U) #endif +#ifndef SIZE_MAX +#define SIZE_MAX (~(size_t)0) +#endif + #endif /* ! C99 */ #endif /* ! FLEXINT_H */ -#ifdef __cplusplus - -/* The "const" storage-class-modifier is valid. */ -#define YY_USE_CONST - -#else /* ! __cplusplus */ - -/* C99 requires __STDC__ to be defined as 1. */ -#if defined (__STDC__) +/* begin standard C++ headers. */ -#define YY_USE_CONST - -#endif /* defined (__STDC__) */ -#endif /* ! __cplusplus */ - -#ifdef YY_USE_CONST +/* TODO: this is always defined, so inline it */ #define yyconst const + +#if defined(__GNUC__) && __GNUC__ >= 3 +#define yynoreturn __attribute__((__noreturn__)) #else -#define yyconst +#define yynoreturn #endif /* Returned upon end-of-file. */ #define YY_NULL 0 -/* Promotes a possibly negative, possibly signed char to an unsigned - * integer for use as an array index. If the signed char is negative, - * we want to instead treat it as an 8-bit unsigned char, hence the - * double cast. +/* Promotes a possibly negative, possibly signed char to an + * integer in range [0..255] for use as an array index. */ -#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) +#define YY_SC_TO_UI(c) ((YY_CHAR) (c)) + +/* An opaque pointer. */ +#ifndef YY_TYPEDEF_YY_SCANNER_T +#define YY_TYPEDEF_YY_SCANNER_T +typedef void* yyscan_t; +#endif + +/* For convenience, these vars (plus the bison vars far below) + are macros in the reentrant scanner. */ +#define yyin yyg->yyin_r +#define yyout yyg->yyout_r +#define yyextra yyg->yyextra_r +#define yyleng yyg->yyleng_r +#define yytext yyg->yytext_r +#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno) +#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column) +#define yy_flex_debug yyg->yy_flex_debug_r /* Enter a start condition. This macro really ought to take a parameter, * but we do it the disgusting crufty way forced on us by the ()-less * definition of BEGIN. */ -#define BEGIN (yy_start) = 1 + 2 * - +#define BEGIN yyg->yy_start = 1 + 2 * /* Translate the current start state into a value that can be later handed * to BEGIN to return to the state. The YYSTATE alias is for lex * compatibility. */ -#define YY_START (((yy_start) - 1) / 2) +#define YY_START ((yyg->yy_start - 1) / 2) #define YYSTATE YY_START - /* Action number for EOF rule of a given start state. */ #define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) - /* Special action meaning "start processing a new file". */ -#define YY_NEW_FILE wcsbthrestart(wcsbthin ) - +#define YY_NEW_FILE yyrestart( yyin , yyscanner ) #define YY_END_OF_BUFFER_CHAR 0 /* Size of default input buffer. */ @@ -181,36 +378,32 @@ typedef unsigned int flex_uint32_t; typedef struct yy_buffer_state *YY_BUFFER_STATE; #endif -extern int wcsbthleng; - -extern FILE *wcsbthin, *wcsbthout; +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef size_t yy_size_t; +#endif #define EOB_ACT_CONTINUE_SCAN 0 #define EOB_ACT_END_OF_FILE 1 #define EOB_ACT_LAST_MATCH 2 - + #define YY_LESS_LINENO(n) +#define YY_LINENO_REWIND_TO(ptr) /* Return all but the first "n" matched characters back to the input stream. */ #define yyless(n) \ do \ { \ - /* Undo effects of setting up wcsbthtext. */ \ + /* Undo effects of setting up yytext. */ \ int yyless_macro_arg = (n); \ YY_LESS_LINENO(yyless_macro_arg);\ - *yy_cp = (yy_hold_char); \ + *yy_cp = yyg->yy_hold_char; \ YY_RESTORE_YY_MORE_OFFSET \ - (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ - YY_DO_BEFORE_ACTION; /* set up wcsbthtext again */ \ + yyg->yy_c_buf_p = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ + YY_DO_BEFORE_ACTION; /* set up yytext again */ \ } \ while ( 0 ) - -#define unput(c) yyunput( c, (yytext_ptr) ) - -#ifndef YY_TYPEDEF_YY_SIZE_T -#define YY_TYPEDEF_YY_SIZE_T -typedef size_t yy_size_t; -#endif +#define unput(c) yyunput( c, yyg->yytext_ptr , yyscanner ) #ifndef YY_STRUCT_YY_BUFFER_STATE #define YY_STRUCT_YY_BUFFER_STATE @@ -224,7 +417,7 @@ struct yy_buffer_state /* Size of input buffer in bytes, not including room for EOB * characters. */ - yy_size_t yy_buf_size; + int yy_buf_size; /* Number of characters read into yy_ch_buf, not including EOB * characters. @@ -252,7 +445,7 @@ struct yy_buffer_state int yy_bs_lineno; /**< The line count. */ int yy_bs_column; /**< The column count. */ - + /* Whether to try to fill the input buffer when we reach the * end of it. */ @@ -269,113 +462,81 @@ struct yy_buffer_state * possible backing-up. * * When we actually see the EOF, we change the status to "new" - * (via wcsbthrestart()), so that the user can continue scanning by - * just pointing wcsbthin at a new input file. + * (via yyrestart()), so that the user can continue scanning by + * just pointing yyin at a new input file. */ #define YY_BUFFER_EOF_PENDING 2 }; #endif /* !YY_STRUCT_YY_BUFFER_STATE */ -/* Stack of input buffers. */ -static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */ -static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */ -static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */ - /* We provide macros for accessing buffer states in case in the * future we want to put the buffer states in a more general * "scanner state". * * Returns the top of the stack, or NULL. */ -#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \ - ? (yy_buffer_stack)[(yy_buffer_stack_top)] \ +#define YY_CURRENT_BUFFER ( yyg->yy_buffer_stack \ + ? yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] \ : NULL) - /* Same as previous macro, but useful when we know that the buffer stack is not * NULL or when we need an lvalue. For internal use only. */ -#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)] - -/* yy_hold_char holds the character lost when wcsbthtext is formed. */ -static char yy_hold_char; -static int yy_n_chars; /* number of characters read into yy_ch_buf */ -int wcsbthleng; - -/* Points to current character in buffer. */ -static char *yy_c_buf_p = (char *) 0; -static int yy_init = 0; /* whether we need to initialize */ -static int yy_start = 0; /* start state number */ - -/* Flag which is used to allow wcsbthwrap()'s to do buffer switches - * instead of setting up a fresh wcsbthin. A bit of a hack ... - */ -static int yy_did_buffer_switch_on_eof; - -void wcsbthrestart (FILE *input_file ); -void wcsbth_switch_to_buffer (YY_BUFFER_STATE new_buffer ); -YY_BUFFER_STATE wcsbth_create_buffer (FILE *file,int size ); -void wcsbth_delete_buffer (YY_BUFFER_STATE b ); -void wcsbth_flush_buffer (YY_BUFFER_STATE b ); -void wcsbthpush_buffer_state (YY_BUFFER_STATE new_buffer ); -void wcsbthpop_buffer_state (void ); - -static void wcsbthensure_buffer_stack (void ); -static void wcsbth_load_buffer_state (void ); -static void wcsbth_init_buffer (YY_BUFFER_STATE b,FILE *file ); - -#define YY_FLUSH_BUFFER wcsbth_flush_buffer(YY_CURRENT_BUFFER ) - -YY_BUFFER_STATE wcsbth_scan_buffer (char *base,yy_size_t size ); -YY_BUFFER_STATE wcsbth_scan_string (yyconst char *yy_str ); -YY_BUFFER_STATE wcsbth_scan_bytes (yyconst char *bytes,int len ); - -void *wcsbthalloc (yy_size_t ); -void *wcsbthrealloc (void *,yy_size_t ); -void wcsbthfree (void * ); - -#define yy_new_buffer wcsbth_create_buffer - +#define YY_CURRENT_BUFFER_LVALUE yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] + +void yyrestart ( FILE *input_file , yyscan_t yyscanner ); +void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size , yyscan_t yyscanner ); +void yy_delete_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yy_flush_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yypush_buffer_state ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +void yypop_buffer_state ( yyscan_t yyscanner ); + +static void yyensure_buffer_stack ( yyscan_t yyscanner ); +static void yy_load_buffer_state ( yyscan_t yyscanner ); +static void yy_init_buffer ( YY_BUFFER_STATE b, FILE *file , yyscan_t yyscanner ); +#define YY_FLUSH_BUFFER yy_flush_buffer( YY_CURRENT_BUFFER , yyscanner) + +YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_string ( const char *yy_str , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len , yyscan_t yyscanner ); + +void *yyalloc ( yy_size_t , yyscan_t yyscanner ); +void *yyrealloc ( void *, yy_size_t , yyscan_t yyscanner ); +void yyfree ( void * , yyscan_t yyscanner ); + +#define yy_new_buffer yy_create_buffer #define yy_set_interactive(is_interactive) \ { \ if ( ! YY_CURRENT_BUFFER ){ \ - wcsbthensure_buffer_stack (); \ + yyensure_buffer_stack (yyscanner); \ YY_CURRENT_BUFFER_LVALUE = \ - wcsbth_create_buffer(wcsbthin,YY_BUF_SIZE ); \ + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); \ } \ YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ } - #define yy_set_bol(at_bol) \ { \ if ( ! YY_CURRENT_BUFFER ){\ - wcsbthensure_buffer_stack (); \ + yyensure_buffer_stack (yyscanner); \ YY_CURRENT_BUFFER_LVALUE = \ - wcsbth_create_buffer(wcsbthin,YY_BUF_SIZE ); \ + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); \ } \ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ } - #define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) /* Begin user sect3 */ -#define wcsbthwrap(n) 1 +#define wcsbthwrap(yyscanner) (/*CONSTCOND*/1) #define YY_SKIP_YYWRAP - -typedef char YY_CHAR; - -FILE *wcsbthin = (FILE *) 0, *wcsbthout = (FILE *) 0; +typedef flex_uint8_t YY_CHAR; typedef int yy_state_type; -extern int wcsbthlineno; +#define yytext_ptr yytext_r -int wcsbthlineno = 1; - -extern char *wcsbthtext; -#define yytext_ptr wcsbthtext -static yyconst flex_int16_t yy_nxt[][128] = +static const flex_int16_t yy_nxt[][128] = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -395,205 +556,67 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, 68, 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, - - 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68 - }, - - { - 67, 69, 69, 69, 69, 69, 69, 69, 69, 69, - 68, 69, 69, 69, 69, 69, 69, 69, 69, 69, - 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, - 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, - - 69, 69, 69, 69, 69, 69, 69, 69, 69, 70, - 70, 70, 70, 70, 70, 70, 70, 70, 69, 69, - 69, 69, 69, 69, 69, 69, 69, 71, 72, 73, - 69, 69, 69, 69, 69, 69, 74, 75, 69, 76, - 77, 69, 78, 79, 80, 69, 81, 82, 69, 69, - 83, 69, 69, 69, 69, 69, 69, 69, 69, 69, - 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, - 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, - 69, 69, 69, 69, 69, 69, 69, 69 - }, - - { - 67, 68, 68, 68, 68, 68, 68, 68, 68, 68, - - 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, 68, 84, - 84, 84, 84, 84, 84, 84, 84, 84, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, - - 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68 - }, - - { - 67, 68, 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, 68, 84, - 84, 84, 84, 84, 84, 84, 84, 84, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, - - 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68 - }, - - { - 67, 85, 85, 85, 85, 85, 85, 85, 85, 85, - 68, 85, 85, 85, 85, 85, 85, 85, 85, 85, - 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, - 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, - 85, 85, 85, 85, 85, 85, 85, 85, 85, 86, - - 86, 86, 86, 86, 86, 86, 86, 86, 85, 85, - 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, - 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, - 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, - 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, - 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, - 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, - 85, 85, 85, 85, 85, 85, 85, 85 - }, - - { - 67, 85, 85, 85, 85, 85, 85, 85, 85, 85, - 68, 85, 85, 85, 85, 85, 85, 85, 85, 85, - - 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, - 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, - 85, 85, 85, 85, 85, 85, 85, 85, 85, 86, - 86, 86, 86, 86, 86, 86, 86, 86, 85, 85, - 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, - 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, - 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, - 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, - 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, - 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, - - 85, 85, 85, 85, 85, 85, 85, 85 - }, - - { - 67, 87, 87, 87, 87, 87, 87, 87, 87, 87, - 68, 87, 87, 87, 87, 87, 87, 87, 87, 87, - 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, - 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, - 87, 87, 87, 87, 87, 87, 87, 87, 87, 88, - 88, 88, 88, 88, 88, 88, 88, 88, 87, 87, - 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, - 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, - 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, - - 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, - 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, - 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, - 87, 87, 87, 87, 87, 87, 87, 87 - }, - - { - 67, 87, 87, 87, 87, 87, 87, 87, 87, 87, - 68, 87, 87, 87, 87, 87, 87, 87, 87, 87, - 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, - 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, - 87, 87, 87, 87, 87, 87, 87, 87, 87, 88, - 88, 88, 88, 88, 88, 88, 88, 88, 87, 87, + 69, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, - 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, - 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, - 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, - 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, - 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, - 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, - 87, 87, 87, 87, 87, 87, 87, 87 + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70 }, { - 67, 89, 89, 89, 89, 89, 89, 89, 89, 89, - 68, 89, 89, 89, 89, 89, 89, 89, 89, 89, - 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, - - 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, - 89, 89, 89, 89, 89, 89, 89, 89, 89, 90, - 90, 90, 90, 90, 90, 90, 90, 90, 89, 89, - 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, - 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, - 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, - 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, - 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, - 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, - 89, 89, 89, 89, 89, 89, 89, 89 + 69, 71, 71, 71, 71, 71, 71, 71, 71, 71, + 70, 71, 71, 71, 71, 71, 71, 71, 71, 71, + 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, + 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, + 71, 71, 71, 71, 71, 71, 71, 71, 71, 72, + 72, 72, 72, 72, 72, 72, 72, 72, 71, 71, + 71, 71, 71, 71, 71, 73, 74, 75, 76, 77, + 71, 71, 78, 71, 79, 71, 80, 81, 71, 82, + 83, 71, 84, 85, 86, 71, 87, 88, 89, 71, + 90, 71, 71, 71, 71, 71, 71, 71, 71, 71, + 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, + 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, + 71, 71, 71, 71, 71, 71, 71, 71 }, { - 67, 89, 89, 89, 89, 89, 89, 89, 89, 89, - 68, 89, 89, 89, 89, 89, 89, 89, 89, 89, - 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, - 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, - 89, 89, 89, 89, 89, 89, 89, 89, 89, 90, - 90, 90, 90, 90, 90, 90, 90, 90, 89, 89, - 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, - 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, - 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, - 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, - - 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, - 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, - 89, 89, 89, 89, 89, 89, 89, 89 - }, + 69, 91, 91, 91, 91, 91, 91, 91, 91, 91, - { - 67, 91, 91, 91, 91, 91, 91, 91, 91, 91, - 68, 91, 91, 91, 91, 91, 91, 91, 91, 91, + 70, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, - 91, 91, 91, 91, 91, 91, 91, 91, 91, 92, - 92, 92, 92, 92, 92, 92, 92, 92, 91, 91, + 91, 91, 91, 91, 91, 91, 91, 91, 92, 93, + 93, 93, 93, 93, 93, 93, 93, 93, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, - 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91 }, { - 67, 91, 91, 91, 91, 91, 91, 91, 91, 91, - 68, 91, 91, 91, 91, 91, 91, 91, 91, 91, + 69, 91, 91, 91, 91, 91, 91, 91, 91, 91, + 70, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, - - 91, 91, 91, 91, 91, 91, 91, 91, 91, 92, - 92, 92, 92, 92, 92, 92, 92, 92, 91, 91, + 91, 91, 91, 91, 91, 91, 91, 91, 92, 93, + 93, 93, 93, 93, 93, 93, 93, 93, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, @@ -602,463 +625,428 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, 93, 93, 93, 93, 93, 93, 93, 93, 93, - - 68, 93, 93, 93, 93, 93, 93, 93, 93, 93, - 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, - 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, - 93, 93, 93, 93, 93, 93, 93, 93, 94, 95, - 95, 95, 95, 95, 95, 95, 95, 95, 93, 93, - 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, - 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, - 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, - 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, - 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, + 69, 94, 94, 94, 94, 94, 94, 94, 94, 94, + 70, 94, 94, 94, 94, 94, 94, 94, 94, 94, + 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, + 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, + 94, 94, 94, 94, 94, 94, 94, 94, 94, 95, - 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, - 93, 93, 93, 93, 93, 93, 93, 93 + 95, 95, 95, 95, 95, 95, 95, 95, 94, 94, + 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, + 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, + 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, + 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, + 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, + 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, + 94, 94, 94, 94, 94, 94, 94, 94 }, { - 67, 93, 93, 93, 93, 93, 93, 93, 93, 93, - 68, 93, 93, 93, 93, 93, 93, 93, 93, 93, - 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, - 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, - 93, 93, 93, 93, 93, 93, 93, 93, 94, 95, - 95, 95, 95, 95, 95, 95, 95, 95, 93, 93, - 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, - 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, + 69, 94, 94, 94, 94, 94, 94, 94, 94, 94, + 70, 94, 94, 94, 94, 94, 94, 94, 94, 94, - 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, - 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, - 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, - 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, - 93, 93, 93, 93, 93, 93, 93, 93 + 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, + 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, + 94, 94, 94, 94, 94, 94, 94, 94, 94, 95, + 95, 95, 95, 95, 95, 95, 95, 95, 94, 94, + 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, + 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, + 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, + 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, + 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, + 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, + + 94, 94, 94, 94, 94, 94, 94, 94 }, { - 67, 68, 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, 68, 96, + 69, 96, 96, 96, 96, 96, 96, 96, 96, 96, + 70, 96, 96, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 96, 96, 96, 96, 96, 96, 97, + 97, 97, 97, 97, 97, 97, 97, 97, 96, 96, + 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, - 96, 96, 96, 96, 96, 96, 96, 96, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68 + 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 96, 96, 96, 96, 96 }, { - 67, 68, 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, - - 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, 68, 96, - 96, 96, 96, 96, 96, 96, 96, 96, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 69, 96, 96, 96, 96, 96, 96, 96, 96, 96, + 70, 96, 96, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 96, 96, 96, 96, 96, 96, 97, + 97, 97, 97, 97, 97, 97, 97, 97, 96, 96, - 68, 68, 68, 68, 68, 68, 68, 68 + 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 96, 96, 96, 96, 96 }, { - 67, 97, 97, 97, 97, 97, 97, 97, 97, 97, - 68, 97, 97, 97, 97, 97, 97, 97, 97, 97, - 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, - 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, - 97, 97, 97, 97, 97, 97, 97, 97, 97, 98, - 98, 98, 98, 98, 98, 98, 98, 98, 97, 97, - 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, - 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, - 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, + 69, 98, 98, 98, 98, 98, 98, 98, 98, 98, + 70, 98, 98, 98, 98, 98, 98, 98, 98, 98, + 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, + + 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, + 98, 98, 98, 98, 98, 98, 98, 98, 98, 99, + 99, 99, 99, 99, 99, 99, 99, 99, 98, 98, + 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, + 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, + 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, + 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, + 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, + 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, + 98, 98, 98, 98, 98, 98, 98, 98 - 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, - 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, - 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, - 97, 97, 97, 97, 97, 97, 97, 97 }, { - 67, 97, 97, 97, 97, 97, 97, 97, 97, 97, - 68, 97, 97, 97, 97, 97, 97, 97, 97, 97, - 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, - 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, - 97, 97, 97, 97, 97, 97, 97, 97, 97, 98, - 98, 98, 98, 98, 98, 98, 98, 98, 97, 97, - - 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, - 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, - 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, - 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, - 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, - 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, - 97, 97, 97, 97, 97, 97, 97, 97 - }, - - { - 67, 99, 99, 99, 99, 99, 99, 99, 99, 99, - 68, 99, 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, + 69, 98, 98, 98, 98, 98, 98, 98, 98, 98, + 70, 98, 98, 98, 98, 98, 98, 98, 98, 98, + 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, + 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, + 98, 98, 98, 98, 98, 98, 98, 98, 98, 99, + 99, 99, 99, 99, 99, 99, 99, 99, 98, 98, + 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, + 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, + 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, + 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, - 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, 99, 100, - 100, 100, 100, 100, 100, 100, 100, 100, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99 - - }, - - { - 67, 99, 99, 99, 99, 99, 99, 99, 99, 99, - 68, 99, 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, 99, 100, - 100, 100, 100, 100, 100, 100, 100, 100, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, + 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, + 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, + 98, 98, 98, 98, 98, 98, 98, 98 + }, + + { + 69, 100, 100, 100, 100, 100, 100, 100, 100, 100, + 70, 100, 100, 100, 100, 100, 100, 100, 100, 100, + 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, + 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, + 100, 100, 100, 100, 100, 100, 100, 100, 100, 101, + 101, 101, 101, 101, 101, 101, 101, 101, 100, 100, + 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, - 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99 - }, - - { - 67, 101, 101, 101, 101, 101, 101, 101, 101, 101, - 68, 101, 101, 101, 101, 101, 101, 101, 101, 101, - 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, - 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, - 101, 101, 101, 101, 101, 101, 101, 101, 101, 102, - 102, 102, 102, 102, 102, 102, 102, 102, 101, 101, - 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, + 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, + 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, + 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, + 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, + 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, + 100, 100, 100, 100, 100, 100, 100, 100 + }, + + { + 69, 100, 100, 100, 100, 100, 100, 100, 100, 100, + 70, 100, 100, 100, 100, 100, 100, 100, 100, 100, + 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, + 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, - 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, - 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, - 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, - 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, - 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, - 101, 101, 101, 101, 101, 101, 101, 101 - }, - - { - 67, 101, 101, 101, 101, 101, 101, 101, 101, 101, - 68, 101, 101, 101, 101, 101, 101, 101, 101, 101, - 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, - 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, + 100, 100, 100, 100, 100, 100, 100, 100, 100, 101, + 101, 101, 101, 101, 101, 101, 101, 101, 100, 100, + 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, + 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, + 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, + 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, + 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, + 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, + 100, 100, 100, 100, 100, 100, 100, 100 + }, + + { + 69, 102, 102, 102, 102, 102, 102, 102, 102, 102, + + 70, 102, 102, 102, 102, 102, 102, 102, 102, 102, + 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, + 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, + 102, 102, 102, 102, 102, 102, 102, 102, 103, 104, + 104, 104, 104, 104, 104, 104, 104, 104, 102, 102, + 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, + 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, + 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, + 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, + 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, - 101, 101, 101, 101, 101, 101, 101, 101, 101, 102, - 102, 102, 102, 102, 102, 102, 102, 102, 101, 101, - 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, - 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, - 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, - 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, - 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, - 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, - 101, 101, 101, 101, 101, 101, 101, 101 - }, - - { - 67, 103, 103, 103, 103, 103, 103, 103, 103, 103, - - 68, 103, 103, 103, 103, 103, 103, 103, 103, 103, - 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, - 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, - 103, 103, 103, 103, 103, 103, 103, 103, 103, 104, - 104, 104, 104, 104, 104, 104, 104, 104, 103, 103, - 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, - 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, - 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, - 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, - 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, + 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, + 102, 102, 102, 102, 102, 102, 102, 102 + }, + + { + 69, 102, 102, 102, 102, 102, 102, 102, 102, 102, + 70, 102, 102, 102, 102, 102, 102, 102, 102, 102, + 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, + 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, + 102, 102, 102, 102, 102, 102, 102, 102, 103, 104, + 104, 104, 104, 104, 104, 104, 104, 104, 102, 102, + 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, + 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, - 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, - 103, 103, 103, 103, 103, 103, 103, 103 - }, - - { - 67, 103, 103, 103, 103, 103, 103, 103, 103, 103, - 68, 103, 103, 103, 103, 103, 103, 103, 103, 103, - 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, - 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, - 103, 103, 103, 103, 103, 103, 103, 103, 103, 104, - 104, 104, 104, 104, 104, 104, 104, 104, 103, 103, - 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, - 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, + 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, + 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, + 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, + 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, + 102, 102, 102, 102, 102, 102, 102, 102 + }, + + { + 69, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 105, - 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, - 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, - 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, - 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, - 103, 103, 103, 103, 103, 103, 103, 103 - }, - - { - 67, 105, 105, 105, 105, 105, 105, 105, 105, 105, - 68, 105, 105, 105, 105, 105, 105, 105, 105, 105, - 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, - 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, - 105, 105, 105, 105, 105, 105, 105, 105, 105, 106, + 105, 105, 105, 105, 105, 105, 105, 105, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70 + }, + + { + 69, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, - 106, 106, 106, 106, 106, 106, 106, 106, 105, 105, - 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, - 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, - 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, - 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, - 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, - 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, - 105, 105, 105, 105, 105, 105, 105, 105 - }, - - { - 67, 105, 105, 105, 105, 105, 105, 105, 105, 105, - 68, 105, 105, 105, 105, 105, 105, 105, 105, 105, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 105, + 105, 105, 105, 105, 105, 105, 105, 105, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + + 70, 70, 70, 70, 70, 70, 70, 70 + }, + + { + 69, 106, 106, 106, 106, 106, 106, 106, 106, 106, + 70, 106, 106, 106, 106, 106, 106, 106, 106, 106, + 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, + 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, + 106, 106, 106, 106, 106, 106, 106, 106, 106, 107, + 107, 107, 107, 107, 107, 107, 107, 107, 106, 106, + 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, + 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, + 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, - 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, - 105, 105, 105, 105, 105, 105, 105, 105, 105, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 105, 105, - 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, - 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, - 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, - 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, - 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, - 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, - - 105, 105, 105, 105, 105, 105, 105, 105 - }, - - { - 67, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 68, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 108, - 108, 108, 108, 108, 108, 108, 108, 108, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, + 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, + 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, + 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, + 106, 106, 106, 106, 106, 106, 106, 106 + }, + + { + 69, 106, 106, 106, 106, 106, 106, 106, 106, 106, + 70, 106, 106, 106, 106, 106, 106, 106, 106, 106, + 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, + 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, + 106, 106, 106, 106, 106, 106, 106, 106, 106, 107, + 107, 107, 107, 107, 107, 107, 107, 107, 106, 106, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107 - }, - - { - 67, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 68, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 108, - 108, 108, 108, 108, 108, 108, 108, 108, 107, 107, + 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, + 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, + 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, + 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, + 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, + 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, + 106, 106, 106, 106, 106, 106, 106, 106 + }, + + { + 69, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 70, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107 - }, - - { - 67, 109, 109, 109, 109, 109, 109, 109, 109, 109, - 68, 109, 109, 109, 109, 109, 109, 109, 109, 109, - 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 109, + 109, 109, 109, 109, 109, 109, 109, 109, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108 + + }, + + { + 69, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 70, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 109, + 109, 109, 109, 109, 109, 109, 109, 109, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, - 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, - 109, 109, 109, 109, 109, 109, 109, 109, 109, 110, - 110, 110, 110, 110, 110, 110, 110, 110, 109, 109, - 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, - 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, - 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, - 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, - 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, - 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, - 109, 109, 109, 109, 109, 109, 109, 109 - - }, - - { - 67, 109, 109, 109, 109, 109, 109, 109, 109, 109, - 68, 109, 109, 109, 109, 109, 109, 109, 109, 109, - 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, - 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, - 109, 109, 109, 109, 109, 109, 109, 109, 109, 110, - 110, 110, 110, 110, 110, 110, 110, 110, 109, 109, - 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, - 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, - 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, - 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108 + }, + + { + 69, 110, 110, 110, 110, 110, 110, 110, 110, 110, + 70, 110, 110, 110, 110, 110, 110, 110, 110, 110, + 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, + 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, + 110, 110, 110, 110, 110, 110, 110, 110, 111, 111, + 111, 111, 111, 111, 111, 111, 111, 111, 110, 110, + 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, - 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, - 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, - 109, 109, 109, 109, 109, 109, 109, 109 - }, - - { - 67, 111, 111, 111, 111, 111, 111, 111, 111, 111, - 68, 111, 111, 111, 111, 111, 111, 111, 111, 111, - 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, - 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, - 111, 111, 111, 111, 111, 111, 111, 111, 111, 112, - 112, 112, 112, 112, 112, 112, 112, 112, 111, 111, - 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, + 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, + 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, + 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, + 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, + 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, + 110, 110, 110, 110, 110, 110, 110, 110 + }, + + { + 69, 110, 110, 110, 110, 110, 110, 110, 110, 110, + 70, 110, 110, 110, 110, 110, 110, 110, 110, 110, + 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, + 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, - 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, - 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, - 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, - 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, - 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, - 111, 111, 111, 111, 111, 111, 111, 111 - }, - - { - 67, 111, 111, 111, 111, 111, 111, 111, 111, 111, - 68, 111, 111, 111, 111, 111, 111, 111, 111, 111, - 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, - 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, + 110, 110, 110, 110, 110, 110, 110, 110, 111, 111, + 111, 111, 111, 111, 111, 111, 111, 111, 110, 110, + 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, + 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, + 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, + 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, + 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, + 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, + 110, 110, 110, 110, 110, 110, 110, 110 + }, + + { + 69, 112, 112, 112, 112, 112, 112, 112, 112, 112, + + 70, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 113, + 113, 113, 113, 113, 113, 113, 113, 113, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, - 111, 111, 111, 111, 111, 111, 111, 111, 111, 112, - 112, 112, 112, 112, 112, 112, 112, 112, 111, 111, - 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, - 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, - 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, - 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, - 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, - 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, - 111, 111, 111, 111, 111, 111, 111, 111 - }, - - { - 67, 113, 113, 113, 113, 113, 113, 113, 113, 113, - - 68, 113, 113, 113, 113, 113, 113, 113, 113, 113, - 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, - 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, - 113, 113, 113, 113, 113, 113, 113, 113, 113, 114, - 114, 114, 114, 114, 114, 114, 114, 114, 113, 113, - 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, - 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, - 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, - 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, - 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, - - 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, - 113, 113, 113, 113, 113, 113, 113, 113 + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112 }, { - 67, 113, 113, 113, 113, 113, 113, 113, 113, 113, - 68, 113, 113, 113, 113, 113, 113, 113, 113, 113, - 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, - 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, - 113, 113, 113, 113, 113, 113, 113, 113, 113, 114, - 114, 114, 114, 114, 114, 114, 114, 114, 113, 113, - 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, - 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, + 69, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 70, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 113, + 113, 113, 113, 113, 113, 113, 113, 113, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, - 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, - 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, - 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, - 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, - 113, 113, 113, 113, 113, 113, 113, 113 + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112 }, { - 67, 115, 115, 115, 115, 115, 115, 115, 115, 115, - 68, 115, 115, 115, 115, 115, 115, 115, 115, 115, - 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, - 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, - 115, 115, 115, 115, 115, 115, 115, 115, 115, 116, - - 116, 116, 116, 116, 116, 116, 116, 116, 115, 115, - 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, - 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, - 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, - 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, - 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, - 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, - 115, 115, 115, 115, 115, 115, 115, 115 - }, + 69, 114, 114, 114, 114, 114, 114, 114, 114, 114, + 70, 114, 114, 114, 114, 114, 114, 114, 114, 114, + 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, + 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, + 114, 114, 114, 114, 114, 114, 114, 114, 114, 115, + + 115, 115, 115, 115, 115, 115, 115, 115, 114, 114, + 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, + 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, + 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, + 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, + 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, + 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, + 114, 114, 114, 114, 114, 114, 114, 114 + }, { - 67, 115, 115, 115, 115, 115, 115, 115, 115, 115, - 68, 115, 115, 115, 115, 115, 115, 115, 115, 115, + 69, 114, 114, 114, 114, 114, 114, 114, 114, 114, + 70, 114, 114, 114, 114, 114, 114, 114, 114, 114, - 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, - 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, - 115, 115, 115, 115, 115, 115, 115, 115, 115, 116, - 116, 116, 116, 116, 116, 116, 116, 116, 115, 115, - 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, - 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, - 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, - 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, - 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, - 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, + 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, + 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, + 114, 114, 114, 114, 114, 114, 114, 114, 114, 115, + 115, 115, 115, 115, 115, 115, 115, 115, 114, 114, + 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, + 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, + 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, + 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, + 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, + 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, - 115, 115, 115, 115, 115, 115, 115, 115 + 114, 114, 114, 114, 114, 114, 114, 114 }, { - 67, 117, 117, 117, 117, 117, 117, 117, 117, 117, - 68, 117, 117, 117, 117, 117, 117, 117, 117, 117, - 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, - 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, - 117, 117, 117, 117, 117, 117, 117, 117, 118, 118, - 118, 118, 118, 118, 118, 118, 118, 118, 117, 117, - 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, - 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, - 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, + 69, 116, 116, 116, 116, 116, 116, 116, 116, 116, + 70, 116, 116, 116, 116, 116, 116, 116, 116, 116, + 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, + 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, + 116, 116, 116, 116, 116, 116, 116, 116, 117, 118, + 118, 118, 118, 118, 118, 118, 118, 118, 116, 116, + 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, + 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, + 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, - 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, - 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, - 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, - 117, 117, 117, 117, 117, 117, 117, 117 + 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, + 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, + 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, + 116, 116, 116, 116, 116, 116, 116, 116 }, { - 67, 117, 117, 117, 117, 117, 117, 117, 117, 117, - 68, 117, 117, 117, 117, 117, 117, 117, 117, 117, - 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, - 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, - 117, 117, 117, 117, 117, 117, 117, 117, 118, 118, - 118, 118, 118, 118, 118, 118, 118, 118, 117, 117, + 69, 116, 116, 116, 116, 116, 116, 116, 116, 116, + 70, 116, 116, 116, 116, 116, 116, 116, 116, 116, + 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, + 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, + 116, 116, 116, 116, 116, 116, 116, 116, 117, 118, + 118, 118, 118, 118, 118, 118, 118, 118, 116, 116, - 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, - 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, - 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, - 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, - 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, - 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, - 117, 117, 117, 117, 117, 117, 117, 117 + 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, + 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, + 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, + 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, + 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, + 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, + 116, 116, 116, 116, 116, 116, 116, 116 }, - + { - 67, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 68, 119, 119, 119, 119, 119, 119, 119, 119, 119, + 69, 119, 119, 119, 119, 119, 119, 119, 119, 119, + 70, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, 119, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, @@ -1070,12 +1058,12 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 68, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, + 69, 119, 119, 119, 119, 119, 119, 119, 119, 119, + 70, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, 119, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, @@ -1087,220 +1075,220 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 68, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 121, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 121, 121, 121, 121, 121, + 69, 121, 121, 121, 121, 121, 121, 121, 121, 121, + 70, 121, 121, 121, 121, 121, 121, 121, 121, 121, + 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, + 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, + 121, 121, 121, 121, 121, 121, 121, 121, 121, 122, + 122, 122, 122, 122, 122, 122, 122, 122, 121, 121, + 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, - 121, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120 + 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, + 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, + 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, + 121, 121, 121, 121, 121, 121, 121, 121 }, { - 67, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 68, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 121, 120, 120, 120, 120, 120, 120, 120, + 69, 121, 121, 121, 121, 121, 121, 121, 121, 121, + 70, 121, 121, 121, 121, 121, 121, 121, 121, 121, + 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, + 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 121, 121, 121, 121, 121, + 121, 121, 121, 121, 121, 121, 121, 121, 121, 122, + 122, 122, 122, 122, 122, 122, 122, 122, 121, 121, + 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, - 121, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120 + 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, + 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, + 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, + 121, 121, 121, 121, 121, 121, 121, 121 }, { - 67, 122, 122, 122, 122, 122, 122, 122, 122, 122, + 69, 123, 123, 123, 123, 123, 123, 123, 123, 123, - 68, 122, 122, 122, 122, 122, 122, 122, 122, 122, - 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, - 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, - 122, 122, 122, 122, 122, 122, 122, 122, 122, 123, - 123, 123, 123, 123, 123, 123, 123, 123, 122, 122, - 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, - 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, - 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, - 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, - 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, + 70, 123, 123, 123, 123, 123, 123, 123, 123, 123, + 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, + 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, + 123, 123, 123, 123, 123, 123, 123, 123, 123, 124, + 124, 124, 124, 124, 124, 124, 124, 124, 123, 123, + 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, + 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, + 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, + 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, + 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, - 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, - 122, 122, 122, 122, 122, 122, 122, 122 + 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, + 123, 123, 123, 123, 123, 123, 123, 123 }, - { - 67, 122, 122, 122, 122, 122, 122, 122, 122, 122, - 68, 122, 122, 122, 122, 122, 122, 122, 122, 122, - 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, - 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, - 122, 122, 122, 122, 122, 122, 122, 122, 122, 123, - 123, 123, 123, 123, 123, 123, 123, 123, 122, 122, - 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, - 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, - - 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, - 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, - 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, - 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, - 122, 122, 122, 122, 122, 122, 122, 122 + { + 69, 123, 123, 123, 123, 123, 123, 123, 123, 123, + 70, 123, 123, 123, 123, 123, 123, 123, 123, 123, + 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, + 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, + 123, 123, 123, 123, 123, 123, 123, 123, 123, 124, + 124, 124, 124, 124, 124, 124, 124, 124, 123, 123, + 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, + 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, + + 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, + 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, + 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, + 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, + 123, 123, 123, 123, 123, 123, 123, 123 }, { - 67, 124, 124, 124, 124, 124, 124, 124, 124, 124, - 68, 124, 124, 124, 124, 124, 124, 124, 124, 124, - 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, - 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, - 124, 124, 124, 124, 124, 124, 124, 124, 124, 125, + 69, 125, 125, 125, 125, 125, 125, 125, 125, 125, + 70, 125, 125, 125, 125, 125, 125, 125, 125, 125, + 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, + 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, + 125, 125, 125, 125, 125, 125, 125, 125, 125, 126, - 125, 125, 125, 125, 125, 125, 125, 125, 124, 124, - 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, - 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, - 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, - 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, - 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, - 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, - 124, 124, 124, 124, 124, 124, 124, 124 + 126, 126, 126, 126, 126, 126, 126, 126, 125, 125, + 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, + 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, + 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, + 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, + 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, + 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, + 125, 125, 125, 125, 125, 125, 125, 125 }, { - 67, 124, 124, 124, 124, 124, 124, 124, 124, 124, - 68, 124, 124, 124, 124, 124, 124, 124, 124, 124, + 69, 125, 125, 125, 125, 125, 125, 125, 125, 125, + 70, 125, 125, 125, 125, 125, 125, 125, 125, 125, - 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, - 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, - 124, 124, 124, 124, 124, 124, 124, 124, 124, 125, - 125, 125, 125, 125, 125, 125, 125, 125, 124, 124, - 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, - 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, - 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, - 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, - 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, - 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, + 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, + 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, + 125, 125, 125, 125, 125, 125, 125, 125, 125, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 125, 125, + 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, + 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, + 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, + 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, + 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, + 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, - 124, 124, 124, 124, 124, 124, 124, 124 + 125, 125, 125, 125, 125, 125, 125, 125 }, { - 67, 126, 126, 126, 126, 126, 126, 126, 126, 126, - 68, 126, 126, 126, 126, 126, 126, 126, 126, 126, - 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, - 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, - 126, 126, 126, 126, 126, 126, 126, 126, 126, 127, - 127, 127, 127, 127, 127, 127, 127, 127, 126, 126, - 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, - 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, - 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 69, 127, 127, 127, 127, 127, 127, 127, 127, 127, + 70, 127, 127, 127, 127, 127, 127, 127, 127, 127, + 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, + 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, + 127, 127, 127, 127, 127, 127, 127, 127, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, 127, 127, + 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, + 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, + 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, - 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, - 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, - 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, - 126, 126, 126, 126, 126, 126, 126, 126 + 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, + 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, + 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, + 127, 127, 127, 127, 127, 127, 127, 127 }, { - 67, 126, 126, 126, 126, 126, 126, 126, 126, 126, - 68, 126, 126, 126, 126, 126, 126, 126, 126, 126, - 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, - 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, - 126, 126, 126, 126, 126, 126, 126, 126, 126, 127, - 127, 127, 127, 127, 127, 127, 127, 127, 126, 126, + 69, 127, 127, 127, 127, 127, 127, 127, 127, 127, + 70, 127, 127, 127, 127, 127, 127, 127, 127, 127, + 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, + 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, + 127, 127, 127, 127, 127, 127, 127, 127, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, 127, 127, - 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, - 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, - 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, - 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, - 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, - 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, - 126, 126, 126, 126, 126, 126, 126, 126 + 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, + 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, + 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, + 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, + 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, + 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, + 127, 127, 127, 127, 127, 127, 127, 127 }, { - 67, 128, 128, 128, 128, 128, 128, 128, 128, 128, - 68, 128, 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, + 69, 129, 129, 129, 129, 129, 129, 129, 129, 129, + 70, 129, 129, 129, 129, 129, 129, 129, 129, 129, + 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, - 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, 128, 129, - 129, 129, 129, 129, 129, 129, 129, 129, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128 + 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, + 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, + 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, + 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, + 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, + 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, + 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, + 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, + 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, + 129, 129, 129, 129, 129, 129, 129, 129 }, { - 67, 128, 128, 128, 128, 128, 128, 128, 128, 128, - 68, 128, 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, 128, 129, - 129, 129, 129, 129, 129, 129, 129, 129, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, + 69, 129, 129, 129, 129, 129, 129, 129, 129, 129, + 70, 129, 129, 129, 129, 129, 129, 129, 129, 129, + 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, + 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, + 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, + 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, + 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, + 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, + 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, + 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, - 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128 + 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, + 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, + 129, 129, 129, 129, 129, 129, 129, 129 }, { - 67, 130, 130, 130, 130, 130, 130, 130, 130, 130, - 68, 130, 130, 130, 130, 130, 130, 130, 130, 130, - 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, + 69, 130, 130, 130, 130, 130, 130, 130, 130, 130, + 70, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, + 130, 130, 131, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, - 130, 131, 130, 130, 130, 130, 130, 130, 130, 130, + 130, 130, 130, 130, 130, 131, 131, 131, 131, 131, - 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, - 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, - 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130 }, { - 67, 130, 130, 130, 130, 130, 130, 130, 130, 130, - 68, 130, 130, 130, 130, 130, 130, 130, 130, 130, - 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, + 69, 130, 130, 130, 130, 130, 130, 130, 130, 130, + 70, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, + 130, 130, 131, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, - 130, 131, 130, 130, 130, 130, 130, 130, 130, 130, - 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, - 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, - 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, + 130, 130, 130, 130, 130, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130 }, { - 67, 132, 132, 132, 132, 132, 132, 132, 132, 132, + 69, 132, 132, 132, 132, 132, 132, 132, 132, 132, - 68, 132, 132, 132, 132, 132, 132, 132, 132, 132, + 70, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, - 132, 132, 132, 133, 132, 133, 132, 132, 134, 134, - 134, 134, 134, 134, 134, 134, 134, 134, 132, 132, + 132, 132, 132, 132, 132, 132, 132, 132, 132, 133, + 133, 133, 133, 133, 133, 133, 133, 133, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, @@ -1312,12 +1300,12 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, 132, 132, 132, 132, 132, 132, 132, 132, 132, - 68, 132, 132, 132, 132, 132, 132, 132, 132, 132, + 69, 132, 132, 132, 132, 132, 132, 132, 132, 132, + 70, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, - 132, 132, 132, 133, 132, 133, 132, 132, 134, 134, - 134, 134, 134, 134, 134, 134, 134, 134, 132, 132, + 132, 132, 132, 132, 132, 132, 132, 132, 132, 133, + 133, 133, 133, 133, 133, 133, 133, 133, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, @@ -1329,253 +1317,426 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, 135, 135, 135, 135, 135, 135, 135, 135, 135, - 68, 135, 135, 135, 135, 135, 135, 135, 135, 135, - 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, - 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, - 135, 135, 135, 136, 135, 136, 137, 135, 138, 138, + 69, 134, 134, 134, 134, 134, 134, 134, 134, 134, + 70, 134, 134, 134, 134, 134, 134, 134, 134, 134, + 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, + 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, + 134, 134, 134, 134, 134, 134, 134, 134, 134, 135, - 138, 138, 138, 138, 138, 138, 138, 138, 135, 135, - 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, - 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, - 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, - 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, - 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, - 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, - 135, 135, 135, 135, 135, 135, 135, 135 + 135, 135, 135, 135, 135, 135, 135, 135, 134, 134, + 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, + 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, + 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, + 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, + 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, + 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, + 134, 134, 134, 134, 134, 134, 134, 134 }, { - 67, 135, 135, 135, 135, 135, 135, 135, 135, 135, - 68, 135, 135, 135, 135, 135, 135, 135, 135, 135, + 69, 134, 134, 134, 134, 134, 134, 134, 134, 134, + 70, 134, 134, 134, 134, 134, 134, 134, 134, 134, - 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, - 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, - 135, 135, 135, 136, 135, 136, 137, 135, 138, 138, - 138, 138, 138, 138, 138, 138, 138, 138, 135, 135, - 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, - 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, - 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, - 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, - 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, - 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, + 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, + 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, + 134, 134, 134, 134, 134, 134, 134, 134, 134, 135, + 135, 135, 135, 135, 135, 135, 135, 135, 134, 134, + 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, + 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, + 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, + 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, + 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, + 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, - 135, 135, 135, 135, 135, 135, 135, 135 + 134, 134, 134, 134, 134, 134, 134, 134 }, { - 67, 139, 139, 139, 139, 139, 139, 139, 139, 139, - 68, 139, 139, 139, 139, 139, 139, 139, 139, 139, - 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, - 139, 139, 139, 139, 139, 139, 139, 139, 139, 140, - 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, - 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, - 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, - 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, - 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, + 69, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 70, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 137, + 137, 137, 137, 137, 137, 137, 137, 137, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, - 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, - 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, - 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, - 139, 139, 139, 139, 139, 139, 139, 139 + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136 }, { - 67, 139, 139, 139, 139, 139, 139, 139, 139, 139, - 68, 139, 139, 139, 139, 139, 139, 139, 139, 139, - 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, - 139, 139, 139, 139, 139, 139, 139, 139, 139, 140, - 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, - 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, + 69, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 70, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 137, + 137, 137, 137, 137, 137, 137, 137, 137, 136, 136, - 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, - 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, - 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, - 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, - 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, - 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, - 139, 139, 139, 139, 139, 139, 139, 139 + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136 }, { - 67, 141, 141, 141, 141, 141, 141, 141, 141, 141, - 68, 141, 141, 141, 141, 141, 141, 141, 141, 141, - 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, + 69, 138, 138, 138, 138, 138, 138, 138, 138, 138, + 70, 138, 138, 138, 138, 138, 138, 138, 138, 138, + 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, - 141, 141, 142, 141, 141, 141, 141, 141, 141, 141, - 141, 141, 141, 141, 141, 141, 141, 143, 141, 141, - 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, - 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, - 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, - 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, - 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, - 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, - 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, - 141, 141, 141, 141, 141, 141, 141, 141 + 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, + 138, 138, 138, 138, 138, 138, 138, 138, 138, 139, + 139, 139, 139, 139, 139, 139, 139, 139, 138, 138, + 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, + 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, + 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, + 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, + 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, + 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, + 138, 138, 138, 138, 138, 138, 138, 138 }, { - 67, 141, 141, 141, 141, 141, 141, 141, 141, 141, - 68, 141, 141, 141, 141, 141, 141, 141, 141, 141, - 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, - 141, 141, 142, 141, 141, 141, 141, 141, 141, 141, - 141, 141, 141, 141, 141, 141, 141, 143, 141, 141, - 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, - 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, - 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, - 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, - 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, + 69, 138, 138, 138, 138, 138, 138, 138, 138, 138, + 70, 138, 138, 138, 138, 138, 138, 138, 138, 138, + 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, + 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, + 138, 138, 138, 138, 138, 138, 138, 138, 138, 139, + 139, 139, 139, 139, 139, 139, 139, 139, 138, 138, + 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, + 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, + 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, + 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, - 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, - 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, - 141, 141, 141, 141, 141, 141, 141, 141 + 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, + 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, + 138, 138, 138, 138, 138, 138, 138, 138 }, { - 67, 144, 144, 144, 144, 144, 144, 144, 144, 144, - 68, 144, 144, 144, 144, 144, 144, 144, 144, 144, - 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, - 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, - 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, - 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, - 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, + 69, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 70, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 140, 141, 140, 140, 140, 140, 140, 140, 140, 140, - 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, - 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, - 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, - 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, - 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, - 144, 144, 144, 144, 144, 144, 144, 144 + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140 + }, + + { + 69, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 70, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 140, 141, 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140 }, { - 67, 144, 144, 144, 144, 144, 144, 144, 144, 144, - 68, 144, 144, 144, 144, 144, 144, 144, 144, 144, - 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, - 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, + 69, 142, 142, 142, 142, 142, 142, 142, 142, 142, - 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, - 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, - 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, - 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, - 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, - 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, - 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, - 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, - 144, 144, 144, 144, 144, 144, 144, 144 + 70, 142, 142, 142, 142, 142, 142, 142, 142, 142, + 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, + 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, + 142, 142, 142, 143, 142, 143, 142, 142, 144, 144, + 144, 144, 144, 144, 144, 144, 144, 144, 142, 142, + 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, + 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, + 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, + 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, + 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, + + 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, + 142, 142, 142, 142, 142, 142, 142, 142 }, { - 67, 145, 145, 145, 145, 145, 145, 145, 145, 145, + 69, 142, 142, 142, 142, 142, 142, 142, 142, 142, + 70, 142, 142, 142, 142, 142, 142, 142, 142, 142, + 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, + 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, + 142, 142, 142, 143, 142, 143, 142, 142, 144, 144, + 144, 144, 144, 144, 144, 144, 144, 144, 142, 142, + 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, + 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, - 68, 145, 145, 145, 145, 145, 145, 145, 145, 145, - 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, - 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, + 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, + 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, + 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, + 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, + 142, 142, 142, 142, 142, 142, 142, 142 + }, + + { + 69, 145, 145, 145, 145, 145, 145, 145, 145, 145, + 70, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, + 145, 145, 145, 146, 145, 146, 147, 145, 148, 148, + + 148, 148, 148, 148, 148, 148, 148, 148, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, - 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145 }, { - 67, 145, 145, 145, 145, 145, 145, 145, 145, 145, - 68, 145, 145, 145, 145, 145, 145, 145, 145, 145, - 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, - 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, + 69, 145, 145, 145, 145, 145, 145, 145, 145, 145, + 70, 145, 145, 145, 145, 145, 145, 145, 145, 145, + 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, + 145, 145, 145, 146, 145, 146, 147, 145, 148, 148, + 148, 148, 148, 148, 148, 148, 148, 148, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, - 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, + 145, 145, 145, 145, 145, 145, 145, 145 }, { - 67, 146, 146, 146, 146, 146, 146, 146, 146, 146, - 147, 146, 146, 146, 146, 146, 146, 146, 146, 146, - 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, - 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, - 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, + 69, 149, 149, 149, 149, 149, 149, 149, 149, 149, + 70, 149, 149, 149, 149, 149, 149, 149, 149, 149, + 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, + 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, + 149, 149, 149, 150, 149, 150, 151, 149, 152, 152, + 152, 152, 152, 152, 152, 152, 152, 152, 149, 149, + 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, + 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, + 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, + + 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, + 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, + 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, + 149, 149, 149, 149, 149, 149, 149, 149 + }, + + { + 69, 149, 149, 149, 149, 149, 149, 149, 149, 149, + 70, 149, 149, 149, 149, 149, 149, 149, 149, 149, + 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, + 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, + 149, 149, 149, 150, 149, 150, 151, 149, 152, 152, + 152, 152, 152, 152, 152, 152, 152, 152, 149, 149, - 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, - 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, - 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, - 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, - 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, - 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, - 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, - 146, 146, 146, 146, 146, 146, 146, 146 + 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, + 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, + 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, + 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, + 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, + 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, + 149, 149, 149, 149, 149, 149, 149, 149 }, { - 67, 146, 146, 146, 146, 146, 146, 146, 146, 146, - 147, 146, 146, 146, 146, 146, 146, 146, 146, 146, + 69, 153, 153, 153, 153, 153, 153, 153, 153, 153, + 70, 153, 153, 153, 153, 153, 153, 153, 153, 153, + 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, - 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, - 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, - 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, - 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, - 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, - 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, - 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, - 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, - 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, - 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, + 153, 153, 153, 153, 153, 153, 153, 153, 153, 154, + 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, + 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, + 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, + 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, + 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, + 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, + 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, + 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, + 153, 153, 153, 153, 153, 153, 153, 153 - 146, 146, 146, 146, 146, 146, 146, 146 }, { - -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, - -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, - -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, - -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, - -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, - -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, - -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, - -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, - -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, + 69, 153, 153, 153, 153, 153, 153, 153, 153, 153, + 70, 153, 153, 153, 153, 153, 153, 153, 153, 153, + 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, + 153, 153, 153, 153, 153, 153, 153, 153, 153, 154, + 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, + 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, + 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, + 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, + 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, + 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, - -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, - -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, - -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, - -67, -67, -67, -67, -67, -67, -67, -67 + 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, + 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, + 153, 153, 153, 153, 153, 153, 153, 153 }, { - 67, -68, -68, -68, -68, -68, -68, -68, -68, -68, - -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, - -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, - -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, - -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, - -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, + 69, 155, 155, 155, 155, 155, 155, 155, 155, 155, + 156, 155, 155, 155, 155, 155, 155, 155, 155, 155, + 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, + 155, 155, 157, 155, 155, 155, 155, 155, 155, 155, + 155, 155, 155, 155, 155, 155, 155, 158, 155, 155, + 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, + 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, - -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, - -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, - -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, - -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, - -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, - -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, - -68, -68, -68, -68, -68, -68, -68, -68 + 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, + 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, + 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, + 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, + 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, + 155, 155, 155, 155, 155, 155, 155, 155 }, { - 67, -69, -69, -69, -69, -69, -69, -69, -69, -69, - -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, - -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, + 69, 155, 155, 155, 155, 155, 155, 155, 155, 155, + 156, 155, 155, 155, 155, 155, 155, 155, 155, 155, + 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, + 155, 155, 157, 155, 155, 155, 155, 155, 155, 155, - -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, + 155, 155, 155, 155, 155, 155, 155, 158, 155, 155, + 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, + 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, + 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, + 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, + 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, + 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, + 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, + 155, 155, 155, 155, 155, 155, 155, 155 + }, + + { + 69, 159, 159, 159, 159, 159, 159, 159, 159, 159, + + 160, 159, 159, 159, 159, 159, 159, 159, 159, 159, + 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, + 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, + 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, + 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, + 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, + 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, + 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, + 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, + 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, + + 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, + 159, 159, 159, 159, 159, 159, 159, 159 + }, + + { + 69, 159, 159, 159, 159, 159, 159, 159, 159, 159, + 160, 159, 159, 159, 159, 159, 159, 159, 159, 159, + 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, + 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, + 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, + 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, + 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, + 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, + + 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, + 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, + 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, + 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, + 159, 159, 159, 159, 159, 159, 159, 159 + }, + + { + 69, 161, 161, 161, 161, 161, 161, 161, 161, 161, + 162, 161, 161, 161, 161, 161, 161, 161, 161, 161, + 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, + 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, + 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, + + 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, + 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, + 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, + 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, + 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, + 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, + 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, + 161, 161, 161, 161, 161, 161, 161, 161 + }, + + { + 69, 161, 161, 161, 161, 161, 161, 161, 161, 161, + 162, 161, 161, 161, 161, 161, 161, 161, 161, 161, + + 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, + 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, + 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, + 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, + 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, + 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, + 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, + 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, + 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, + 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, + + 161, 161, 161, 161, 161, 161, 161, 161 + }, + + { + 69, 163, 163, 163, 163, 163, 163, 163, 163, 163, + 164, 163, 163, 163, 163, 163, 163, 163, 163, 163, + 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, + 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, + 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, + 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, + 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, + 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, + 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, + + 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, + 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, + 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, + 163, 163, 163, 163, 163, 163, 163, 163 + }, + + { + 69, 163, 163, 163, 163, 163, 163, 163, 163, 163, + 164, 163, 163, 163, 163, 163, 163, 163, 163, 163, + 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, + 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, + 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, + 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, + + 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, + 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, + 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, + 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, + 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, + 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, + 163, 163, 163, 163, 163, 163, 163, 163 + }, + + { + -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, + -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, + -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, + + -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, @@ -1589,15 +1750,15 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -70, -70, -70, -70, -70, -70, -70, -70, -70, + 69, -70, -70, -70, -70, -70, -70, -70, -70, -70, + -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, + -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, + -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, + -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, - -70, -70, -70, -70, -70, -70, -70, -70, 148, 148, - 148, 148, 148, 148, 148, 148, 148, 148, -70, -70, - -70, -70, -70, -70, -70, -70, -70, 149, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, - 150, -70, -70, 151, -70, -70, 152, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, @@ -1606,16 +1767,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -71, -71, -71, -71, -71, -71, -71, -71, -71, + 69, -71, -71, -71, -71, -71, -71, -71, -71, -71, + -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, - -71, -71, -71, -71, -71, -71, -71, -71, 153, -71, - -71, -71, -71, -71, -71, -71, -71, -71, 154, -71, - -71, -71, 155, 156, 157, 158, -71, -71, -71, -71, + -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, + -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, @@ -1623,16 +1784,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -72, -72, -72, -72, -72, -72, -72, -72, -72, + 69, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, + -72, -72, -72, -72, -72, -72, -72, -72, 165, 165, + 165, 165, 165, 165, 165, 165, 165, 165, -72, -72, + -72, -72, -72, -72, -72, -72, -72, 166, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, - -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, - -72, -72, -72, -72, -72, 159, -72, -72, -72, -72, - -72, -72, -72, -72, -72, -72, -72, -72, -72, 160, - -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, + 167, -72, -72, 168, -72, -72, 169, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, @@ -1640,7 +1801,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -73, -73, -73, -73, -73, -73, -73, -73, -73, + 69, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, @@ -1648,60 +1809,60 @@ static yyconst flex_int16_t yy_nxt[][128] = -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, - -73, -73, -73, -73, -73, -73, -73, -73, 161, -73, - 162, 163, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, + -73, -73, -73, -73, -73, 170, -73, -73, -73, -73, + -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73 }, { - 67, -74, -74, -74, -74, -74, -74, -74, -74, -74, + 69, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, - -74, -74, -74, -74, -74, 164, -74, -74, -74, -74, - -74, -74, -74, -74, -74, -74, -74, -74, -74, 165, + -74, -74, -74, -74, -74, -74, -74, -74, 171, 172, + -74, -74, -74, -74, -74, -74, 173, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, - -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, + -74, -74, -74, -74, -74, 174, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74 }, { - 67, -75, -75, -75, -75, -75, -75, -75, -75, -75, + 69, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, - -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, - -75, -75, -75, -75, 166, -75, -75, -75, -75, -75, - -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, - -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, + -75, -75, -75, -75, -75, -75, -75, -75, 175, -75, + -75, -75, -75, -75, -75, -75, -75, -75, 176, -75, + 177, -75, 178, 179, 180, 181, -75, -75, -75, -75, + 182, -75, -75, -75, -75, 183, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75 }, { - 67, -76, -76, -76, -76, -76, -76, -76, -76, -76, + 69, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, - -76, -76, -76, -76, -76, -76, 167, -76, -76, -76, - -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, - -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, + -76, -76, -76, -76, -76, 184, -76, -76, -76, -76, + -76, -76, -76, -76, -76, -76, -76, -76, -76, 185, + -76, -76, -76, 186, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, @@ -1710,15 +1871,15 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -77, -77, -77, -77, -77, -77, -77, -77, -77, + 69, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, - -77, -77, -77, -77, -77, -77, -77, 168, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, - -77, -77, 169, 170, -77, -77, 171, -77, -77, -77, + -77, -77, -77, -77, -77, -77, -77, -77, 187, -77, + 188, 189, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, @@ -1727,16 +1888,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -78, -78, -78, -78, -78, -78, -78, -78, -78, + 69, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, - -78, -78, -78, -78, -78, 172, -78, -78, -78, 173, - 174, -78, -78, -78, -78, -78, -78, -78, -78, -78, - -78, -78, -78, -78, -78, -78, -78, 175, -78, -78, + -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, + -78, 190, -78, -78, -78, -78, -78, -78, -78, -78, + -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, @@ -1744,16 +1905,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -79, -79, -79, -79, -79, -79, -79, -79, -79, + 69, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, + -79, -79, -79, -79, -79, -79, -79, -79, 191, 192, + -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, - -79, -79, -79, -79, -79, -79, -79, -79, -79, 176, - 177, -79, -79, 178, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, @@ -1762,15 +1923,15 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -80, -80, -80, -80, -80, -80, -80, -80, -80, + 69, -80, -80, -80, -80, -80, -80, -80, -80, -80, + -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, + -80, -80, -80, -80, -80, 193, -80, -80, -80, -80, + -80, -80, -80, -80, -80, -80, -80, -80, -80, 194, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, - -80, -80, -80, -80, -80, -80, -80, 179, -80, -80, - 180, -80, -80, -80, -80, -80, -80, -80, -80, -80, - 181, -80, -80, 182, -80, -80, 183, 184, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, @@ -1779,16 +1940,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -81, -81, -81, -81, -81, -81, -81, -81, -81, + 69, -81, -81, -81, -81, -81, -81, -81, -81, -81, + -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, - -81, -81, -81, -81, -81, 185, -81, -81, -81, 186, + -81, -81, -81, -81, 195, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, - -81, -81, -81, 187, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, @@ -1796,14 +1957,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -82, -82, -82, -82, -82, -82, -82, -82, -82, + 69, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, - -82, -82, -82, -82, -82, -82, -82, 188, -82, -82, + -82, -82, -82, -82, -82, -82, 196, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, @@ -1813,16 +1974,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -83, -83, -83, -83, -83, -83, -83, -83, -83, + 69, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, - -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, - -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, - -83, -83, -83, 189, -83, -83, -83, -83, -83, -83, + -83, -83, -83, -83, -83, -83, -83, 197, -83, -83, + -83, -83, -83, -83, -83, -83, 198, -83, -83, -83, + -83, -83, 199, 200, -83, -83, 201, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, @@ -1831,24 +1992,24 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -84, -84, -84, -84, -84, -84, -84, -84, -84, + 69, -84, -84, -84, -84, -84, -84, -84, -84, -84, + -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, + -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, + -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, - -84, -84, 190, -84, -84, -84, -84, -84, -84, -84, - -84, -84, -84, -84, -84, -84, -84, -84, 191, 191, - 191, 191, 191, 191, 191, 191, 191, 191, -84, -84, - -84, -84, -84, -84, -84, 190, 190, 190, 190, 190, - 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, - - 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, - 190, -84, -84, -84, -84, -84, -84, -84, -84, -84, + -84, -84, -84, -84, -84, 202, -84, -84, -84, 203, + 204, -84, -84, -84, -84, -84, -84, -84, -84, -84, + + -84, -84, -84, 205, -84, -84, -84, 206, -84, -84, + -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84 }, { - 67, -85, -85, -85, -85, -85, -85, -85, -85, -85, + 69, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, @@ -1856,8 +2017,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, - -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, - -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, + -85, -85, -85, -85, -85, -85, -85, -85, -85, 207, + 208, -85, -85, 209, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, @@ -1865,17 +2026,17 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -86, -86, -86, -86, -86, -86, -86, -86, -86, + 69, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, - -86, -86, 192, -86, -86, -86, -86, -86, -86, -86, - -86, -86, -86, -86, -86, -86, -86, -86, 193, 193, - 193, 193, 193, 193, 193, 193, 193, 193, -86, -86, - -86, -86, -86, -86, -86, 192, 192, 192, 192, 192, - 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, - 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, - 192, -86, -86, -86, -86, -86, -86, -86, -86, -86, + -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, + -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, + -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, + -86, -86, -86, -86, -86, -86, -86, 210, -86, 211, + 212, -86, -86, 213, -86, -86, -86, -86, -86, -86, + 214, -86, 215, 216, -86, -86, 217, 218, -86, -86, + -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, @@ -1883,15 +2044,15 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -87, -87, -87, -87, -87, -87, -87, -87, -87, - -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, - -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, + 69, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, + -87, -87, -87, -87, -87, 219, -87, -87, -87, 220, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, + -87, -87, -87, 221, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, @@ -1900,24 +2061,24 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -88, -88, -88, -88, -88, -88, -88, -88, -88, + 69, -88, -88, -88, -88, -88, -88, -88, -88, -88, + -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, + -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, + -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, + -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, + -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, + + -88, -88, -88, -88, -88, -88, -88, 222, -88, -88, + -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, - -88, -88, 194, -88, -88, -88, -88, -88, -88, -88, - -88, -88, -88, -88, -88, -88, -88, -88, 195, 195, - 195, 195, 195, 195, 195, 195, 195, 195, -88, -88, - - -88, -88, -88, -88, -88, 196, 196, 196, 196, 196, - 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, - 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, - 196, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88 }, { - 67, -89, -89, -89, -89, -89, -89, -89, -89, -89, + 69, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, @@ -1926,7 +2087,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, - -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, + 223, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, @@ -1935,16 +2096,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -90, -90, -90, -90, -90, -90, -90, -90, -90, + 69, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, - -90, -90, 197, -90, -90, -90, -90, -90, -90, -90, - -90, -90, -90, -90, -90, -90, -90, -90, 198, 198, - 198, 198, 198, 198, 198, 198, 198, 198, -90, -90, - -90, -90, -90, -90, -90, 197, 197, 197, 197, 197, - 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, - 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, - 197, -90, -90, -90, -90, -90, -90, -90, -90, -90, + -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, + -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, + -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, + -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, + -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, + -90, -90, -90, 224, -90, -90, -90, -90, -90, -90, + -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, @@ -1952,7 +2113,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -91, -91, -91, -91, -91, -91, -91, -91, -91, + 69, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, @@ -1969,34 +2130,34 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -92, -92, -92, -92, -92, -92, -92, -92, -92, + 69, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, - -92, -92, 199, -92, -92, -92, -92, -92, -92, -92, - - -92, -92, -92, -92, -92, -92, -92, -92, 200, 200, - 200, 200, 200, 200, 200, 200, 200, 200, -92, -92, - -92, -92, -92, -92, -92, 201, 201, 201, 201, 201, - 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, - 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, - 201, -92, -92, -92, -92, -92, -92, -92, -92, -92, + -92, -92, 225, -92, -92, -92, -92, -92, -92, -92, + + -92, -92, -92, -92, -92, -92, -92, -92, 226, 227, + 227, 227, 227, 227, 227, 227, 227, 227, -92, -92, + -92, -92, -92, -92, -92, 225, 225, 225, 225, 225, + 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, + 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, + 225, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92 }, { - 67, -93, -93, -93, -93, -93, -93, -93, -93, -93, + 69, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, - -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, - -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, - -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, - -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, - -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, - -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, - -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, + -93, -93, 228, -93, -93, -93, -93, -93, -93, -93, + -93, -93, -93, -93, -93, -93, -93, -93, 229, 229, + 229, 229, 229, 229, 229, 229, 229, 229, -93, -93, + -93, -93, -93, -93, -93, 228, 228, 228, 228, 228, + 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, + 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, + 228, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, @@ -2004,12 +2165,12 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -94, -94, -94, -94, -94, -94, -94, -94, -94, + 69, -94, -94, -94, -94, -94, -94, -94, -94, -94, + -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, + -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, - -94, -94, -94, -94, -94, -94, -94, -94, 202, 202, - 202, 202, 202, 202, 202, 202, 202, 202, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, @@ -2021,34 +2182,34 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -95, -95, -95, -95, -95, -95, -95, -95, -95, - -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, - -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, - -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, - -95, -95, -95, -95, -95, -95, -95, -95, 203, 203, - - 203, 203, 203, 203, 203, 203, 203, 203, -95, -95, - -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, + 69, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, - -95, -95, -95, -95, -95, 204, -95, -95, -95, -95, + -95, -95, 230, -95, -95, -95, -95, -95, -95, -95, + -95, -95, -95, -95, -95, -95, -95, -95, 231, 231, + + 231, 231, 231, 231, 231, 231, 231, 231, -95, -95, + -95, -95, -95, -95, -95, 230, 230, 230, 230, 230, + 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, + 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, + 230, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95 }, { - 67, -96, -96, -96, -96, -96, -96, -96, -96, -96, + 69, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, - -96, -96, 205, -96, -96, -96, -96, -96, -96, -96, - -96, -96, -96, -96, -96, -96, -96, -96, 206, 206, - 206, 206, 206, 206, 206, 206, 206, 206, -96, -96, - -96, -96, -96, -96, -96, 205, 205, 205, 205, 205, - 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, - 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, - 205, -96, -96, -96, -96, -96, -96, -96, -96, -96, + -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, + -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, + -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, + -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, + -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, + -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, + -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, @@ -2056,51 +2217,51 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -97, -97, -97, -97, -97, -97, -97, -97, -97, - -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, + 69, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, - -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, - -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, - -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, - -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, - -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, - - -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, + -97, -97, 232, -97, -97, -97, -97, -97, -97, -97, + -97, -97, -97, -97, -97, -97, -97, -97, 233, 233, + 233, 233, 233, 233, 233, 233, 233, 233, -97, -97, + -97, -97, -97, -97, -97, 234, 234, 234, 234, 234, + 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, + 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, + + 234, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97 }, { - 67, -98, -98, -98, -98, -98, -98, -98, -98, -98, + 69, -98, -98, -98, -98, -98, -98, -98, -98, -98, + -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, + -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, - -98, -98, -98, -98, -98, -98, -98, -98, 207, 207, - 207, 207, 207, 207, 207, 207, 207, 207, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, - -98, -98, -98, -98, -98, 208, -98, -98, -98, -98, + -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98 }, { - 67, -99, -99, -99, -99, -99, -99, -99, -99, -99, + 69, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, - -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, - -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, - -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, - -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, - -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, - -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, - -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, + -99, -99, 235, -99, -99, -99, -99, -99, -99, -99, + -99, -99, -99, -99, -99, -99, -99, -99, 236, 236, + 236, 236, 236, 236, 236, 236, 236, 236, -99, -99, + -99, -99, -99, -99, -99, 235, 235, 235, 235, 235, + 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, + 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, + 235, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99 @@ -2108,16 +2269,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -100, -100, -100, -100, -100, -100, -100, -100, -100, + 69, -100, -100, -100, -100, -100, -100, -100, -100, -100, + -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, + -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, + -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, - -100, -100, -100, -100, -100, -100, -100, -100, 209, 209, - 209, 209, 209, 209, 209, 209, 209, 209, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, - -100, -100, -100, -100, -100, 210, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, @@ -2125,51 +2286,51 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -101, -101, -101, -101, -101, -101, -101, -101, -101, - -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, - -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, - -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, - -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, - -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, - -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, - - -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, + 69, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, + -101, -101, 237, -101, -101, -101, -101, -101, -101, -101, + -101, -101, -101, -101, -101, -101, -101, -101, 238, 238, + 238, 238, 238, 238, 238, 238, 238, 238, -101, -101, + -101, -101, -101, -101, -101, 239, 239, 239, 239, 239, + + 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, + 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, + 239, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101 }, { - 67, -102, -102, -102, -102, -102, -102, -102, -102, -102, + 69, -102, -102, -102, -102, -102, -102, -102, -102, -102, + -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, + -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, + -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, + + -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, + -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, + -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, + -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, - -102, -102, 211, -102, -102, -102, -102, -102, -102, -102, - - -102, -102, -102, -102, -102, -102, -102, -102, 212, 212, - 212, 212, 212, 212, 212, 212, 212, 212, -102, -102, - -102, -102, -102, -102, -102, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102 }, { - 67, -103, -103, -103, -103, -103, -103, -103, -103, -103, + 69, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, + -103, -103, -103, -103, -103, 240, -103, -103, 241, 242, + 242, 242, 242, 242, 242, 242, 242, 242, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, - -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, - -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, - -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, + -103, -103, -103, -103, -103, 243, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, @@ -2177,51 +2338,51 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -104, -104, -104, -104, -104, -104, -104, -104, -104, + 69, -104, -104, -104, -104, -104, -104, -104, -104, -104, + -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, - -104, -104, 214, -104, -104, -104, -104, -104, -104, -104, - -104, -104, -104, -104, -104, -104, -104, -104, 215, 215, - 215, 215, 215, 215, 215, 215, 215, 215, -104, -104, - -104, -104, -104, -104, -104, 216, 216, 216, 216, 216, - 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, - - 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, - 216, -104, -104, -104, -104, -104, -104, -104, -104, -104, + -104, -104, -104, -104, -104, 240, -104, -104, 244, 244, + 244, 244, 244, 244, 244, 244, 244, 244, -104, -104, + -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, + -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, + + -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, + -104, -104, -104, -104, -104, 245, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104 }, { - 67, -105, -105, -105, -105, -105, -105, -105, -105, -105, - -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, - -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, - -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, - -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, - - -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, - -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, - -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, + 69, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, + -105, -105, 246, -105, -105, -105, -105, -105, -105, -105, + -105, -105, -105, -105, -105, -105, -105, -105, 247, 247, + + 247, 247, 247, 247, 247, 247, 247, 247, -105, -105, + -105, -105, -105, -105, -105, 246, 246, 246, 246, 246, + 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, + 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, + 246, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105 }, { - 67, -106, -106, -106, -106, -106, -106, -106, -106, -106, + 69, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, - -106, -106, 217, -106, -106, -106, -106, -106, -106, -106, - -106, -106, -106, -106, -106, -106, -106, -106, 218, 218, - 218, 218, 218, 218, 218, 218, 218, 218, -106, -106, - -106, -106, -106, -106, -106, 219, 219, 219, 219, 219, - 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, - 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, - 219, -106, -106, -106, -106, -106, -106, -106, -106, -106, + -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, + -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, + -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, + -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, + -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, + -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, + -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, @@ -2229,51 +2390,51 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -107, -107, -107, -107, -107, -107, -107, -107, -107, - -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, - -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, + 69, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, + -107, -107, -107, -107, -107, -107, -107, -107, 248, 248, + 248, 248, 248, 248, 248, 248, 248, 248, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, - -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, + -107, -107, -107, -107, -107, 249, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107 }, { - 67, -108, -108, -108, -108, -108, -108, -108, -108, -108, + 69, -108, -108, -108, -108, -108, -108, -108, -108, -108, + -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, + -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, - -108, -108, -108, -108, -108, -108, -108, -108, 220, 220, - 220, 220, 220, 220, 220, 220, 220, 220, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, - -108, -108, -108, -108, -108, 221, -108, -108, -108, -108, + -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108 }, { - 67, -109, -109, -109, -109, -109, -109, -109, -109, -109, + 69, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, + -109, -109, -109, -109, -109, -109, -109, -109, 250, 250, + 250, 250, 250, 250, 250, 250, 250, 250, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, - -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, - -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, - -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, + -109, -109, -109, -109, -109, 251, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109 @@ -2281,16 +2442,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -110, -110, -110, -110, -110, -110, -110, -110, -110, + 69, -110, -110, -110, -110, -110, -110, -110, -110, -110, + -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, + -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, + -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, - -110, -110, -110, -110, -110, -110, -110, -110, 222, 222, - 222, 222, 222, 222, 222, 222, 222, 222, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, - -110, -110, -110, -110, -110, 223, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, @@ -2298,51 +2459,51 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -111, -111, -111, -111, -111, -111, -111, -111, -111, - -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, - -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, - -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, - -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, + 69, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, + -111, -111, 252, -111, -111, -111, -111, -111, -111, -111, + -111, -111, -111, -111, -111, -111, -111, -111, 253, 253, + 253, 253, 253, 253, 253, 253, 253, 253, -111, -111, + -111, -111, -111, -111, -111, 252, 252, 252, 252, 252, - -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, - -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, - -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, + 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, + 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, + 252, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111 }, { - 67, -112, -112, -112, -112, -112, -112, -112, -112, -112, + 69, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, - -112, -112, -112, -112, -112, -112, -112, -112, 224, 224, - 224, 224, 224, 224, 224, 224, 224, 224, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, - -112, -112, -112, -112, -112, 225, -112, -112, -112, -112, + -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, + -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, + -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112 }, { - 67, -113, -113, -113, -113, -113, -113, -113, -113, -113, + 69, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, - -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, - -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, - -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, - -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, - -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, - -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, - -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, + -113, -113, 254, -113, -113, -113, -113, -113, -113, -113, + -113, -113, -113, -113, -113, -113, -113, -113, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, -113, -113, + -113, -113, -113, -113, -113, 256, 256, 256, 256, 256, + 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, + 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, + 256, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, @@ -2350,51 +2511,51 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -114, -114, -114, -114, -114, -114, -114, -114, -114, + 69, -114, -114, -114, -114, -114, -114, -114, -114, -114, + -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, + -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, - -114, -114, -114, -114, -114, -114, -114, -114, 226, 226, - 226, 226, 226, 226, 226, 226, 226, 226, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, - -114, -114, -114, -114, -114, 227, -114, -114, -114, -114, + -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114 }, { - 67, -115, -115, -115, -115, -115, -115, -115, -115, -115, - -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, - -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, - -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, - -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, - - -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, - -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, - -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, + 69, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, + -115, -115, 257, -115, -115, -115, -115, -115, -115, -115, + -115, -115, -115, -115, -115, -115, -115, -115, 258, 258, + + 258, 258, 258, 258, 258, 258, 258, 258, -115, -115, + -115, -115, -115, -115, -115, 259, 259, 259, 259, 259, + 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, + 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, + 259, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115 }, { - 67, -116, -116, -116, -116, -116, -116, -116, -116, -116, + 69, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, - -116, -116, -116, -116, -116, -116, -116, -116, 228, 228, - 228, 228, 228, 228, 228, 228, 228, 228, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, - -116, -116, -116, -116, -116, 229, -116, -116, -116, -116, + -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, + -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, + -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, @@ -2402,41 +2563,41 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -117, -117, -117, -117, -117, -117, -117, -117, -117, - -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, - -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, + 69, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, + -117, -117, -117, -117, -117, 260, -117, -117, 261, 262, + 262, 262, 262, 262, 262, 262, 262, 262, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, - -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, + -117, -117, -117, -117, -117, 263, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117 }, { - 67, -118, -118, -118, -118, -118, -118, -118, -118, -118, - -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, + 69, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, - -118, -118, 230, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, + -118, -118, -118, -118, -118, 260, -118, -118, 264, 264, + 264, 264, 264, 264, 264, 264, 264, 264, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, - -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, + -118, -118, -118, -118, -118, 265, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118 }, { - 67, -119, -119, -119, -119, -119, -119, -119, -119, -119, + 69, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, @@ -2454,16 +2615,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -120, -120, -120, -120, -120, -120, -120, -120, -120, - -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, - -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, - -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, + 69, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, + -120, -120, -120, -120, -120, -120, -120, -120, 266, 266, + 266, 266, 266, 266, 266, 266, 266, 266, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, + -120, -120, -120, -120, -120, 267, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, @@ -2471,7 +2632,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -121, -121, -121, -121, -121, -121, -121, -121, -121, + 69, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, @@ -2488,34 +2649,34 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -122, -122, -122, -122, -122, -122, -122, -122, -122, + 69, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, + -122, -122, -122, -122, -122, -122, -122, -122, 268, 268, + 268, 268, 268, 268, 268, 268, 268, 268, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, - -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, - -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, - -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, + -122, -122, -122, -122, -122, 269, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122 }, { - 67, -123, -123, -123, -123, -123, -123, -123, -123, -123, + 69, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, - -123, -123, 231, -123, -123, -123, -123, -123, -123, -123, - -123, -123, -123, -123, -123, -123, -123, -123, 232, 232, - 232, 232, 232, 232, 232, 232, 232, 232, -123, -123, - -123, -123, -123, -123, -123, 231, 231, 231, 231, 231, - 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, - 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, - 231, -123, -123, -123, -123, -123, -123, -123, -123, -123, + -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, + -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, + -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, + -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, + -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, + -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, + -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, @@ -2523,51 +2684,51 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -124, -124, -124, -124, -124, -124, -124, -124, -124, - -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, - -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, + 69, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, + -124, -124, -124, -124, -124, -124, -124, -124, 270, 270, + 270, 270, 270, 270, 270, 270, 270, 270, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, - -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, + -124, -124, -124, -124, -124, 271, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124 }, { - 67, -125, -125, -125, -125, -125, -125, -125, -125, -125, + 69, -125, -125, -125, -125, -125, -125, -125, -125, -125, + -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, + -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, + -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, + -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, + + -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, + -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, + -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, - -125, -125, 233, -125, -125, -125, -125, -125, -125, -125, - -125, -125, -125, -125, -125, -125, -125, -125, 234, 234, - - 234, 234, 234, 234, 234, 234, 234, 234, -125, -125, - -125, -125, -125, -125, -125, 233, 233, 233, 233, 233, - 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, - 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, - 233, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125 }, { - 67, -126, -126, -126, -126, -126, -126, -126, -126, -126, + 69, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, + -126, -126, -126, -126, -126, -126, -126, -126, 272, 272, + 272, 272, 272, 272, 272, 272, 272, 272, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, - -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, - -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, - -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, + -126, -126, -126, -126, -126, 273, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, @@ -2575,12 +2736,12 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -127, -127, -127, -127, -127, -127, -127, -127, -127, + 69, -127, -127, -127, -127, -127, -127, -127, -127, -127, + -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, + -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, + -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, - -127, -127, 235, -127, -127, -127, -127, -127, -127, -127, - -127, -127, -127, -127, -127, -127, -127, -127, 236, 236, - 236, 236, 236, 236, 236, 236, 236, 236, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, @@ -2592,12 +2753,12 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -128, -128, -128, -128, -128, -128, -128, -128, -128, - -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, - -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, - -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, + 69, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, + -128, -128, 274, -128, -128, -128, -128, -128, -128, -128, + -128, -128, -128, -128, -128, -128, -128, -128, 275, 275, + 275, 275, 275, 275, 275, 275, 275, 275, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, @@ -2609,13 +2770,13 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -129, -129, -129, -129, -129, -129, -129, -129, -129, + 69, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, - -129, -129, 237, -129, -129, -129, -129, -129, -129, -129, - -129, -129, -129, -129, -129, -129, -129, -129, 238, 238, - 238, 238, 238, 238, 238, 238, 238, 238, -129, -129, + -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, + -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, + -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, @@ -2627,7 +2788,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -130, -130, -130, -130, -130, -130, -130, -130, -130, + 69, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, @@ -2644,10 +2805,10 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -131, -131, -131, -131, -131, -131, -131, -131, -131, + 69, -131, -131, -131, -131, -131, -131, -131, -131, -131, + -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, - -131, -131, 239, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, @@ -2661,7 +2822,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -132, -132, -132, -132, -132, -132, -132, -132, -132, + 69, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, @@ -2678,17 +2839,17 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -133, -133, -133, -133, -133, -133, -133, -133, -133, + 69, -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, - -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, - -133, -133, -133, -133, -133, -133, -133, -133, 240, 240, - 240, 240, 240, 240, 240, 240, 240, 240, -133, -133, - -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, - -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, - -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, - -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, + -133, -133, 276, -133, -133, -133, -133, -133, -133, -133, + -133, -133, -133, -133, -133, -133, -133, -133, 277, 277, + 277, 277, 277, 277, 277, 277, 277, 277, -133, -133, + -133, -133, -133, -133, -133, 276, 276, 276, 276, 276, + 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, + 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, + 276, -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, @@ -2696,15 +2857,15 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -134, -134, -134, -134, -134, -134, -134, -134, -134, + 69, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, - -134, -134, -134, -134, -134, -134, -134, -134, 240, 240, - 240, 240, 240, 240, 240, 240, 240, 240, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, - + -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, + -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, + -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, @@ -2713,30 +2874,30 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -135, -135, -135, -135, -135, -135, -135, -135, -135, - -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, - -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, - -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, - -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, - - -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, - -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, - -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, + 69, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, + -135, -135, 278, -135, -135, -135, -135, -135, -135, -135, + -135, -135, -135, -135, -135, -135, -135, -135, 279, 279, + + 279, 279, 279, 279, 279, 279, 279, 279, -135, -135, + -135, -135, -135, -135, -135, 278, 278, 278, 278, 278, + 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, + 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, + 278, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135 }, { - 67, -136, -136, -136, -136, -136, -136, -136, -136, -136, + 69, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, - -136, -136, -136, -136, -136, -136, 241, -136, 242, 242, - 242, 242, 242, 242, 242, 242, 242, 242, -136, -136, + -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, + -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, @@ -2748,12 +2909,12 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -137, -137, -137, -137, -137, -137, -137, -137, -137, - -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, + 69, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, - -137, -137, -137, -137, -137, -137, -137, -137, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, -137, -137, + -137, -137, 280, -137, -137, -137, -137, -137, -137, -137, + -137, -137, -137, -137, -137, -137, -137, -137, 281, 281, + 281, 281, 281, 281, 281, 281, 281, 281, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, @@ -2765,30 +2926,30 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -138, -138, -138, -138, -138, -138, -138, -138, -138, + 69, -138, -138, -138, -138, -138, -138, -138, -138, -138, + -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, + -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, - -138, -138, -138, -138, -138, -138, 244, -138, 245, 245, - 245, 245, 245, 245, 245, 245, 245, 245, -138, -138, - -138, -138, -138, -138, -138, -138, -138, -138, -138, 246, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, - -138, 246, -138, -138, -138, -138, -138, -138, -138, -138, + -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, + -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138 }, { - 67, -139, -139, -139, -139, -139, -139, -139, -139, -139, + 69, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, - -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, - -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, - -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, + -139, -139, 282, -139, -139, -139, -139, -139, -139, -139, + -139, -139, -139, -139, -139, -139, -139, -139, 283, 283, + 283, 283, 283, 283, 283, 283, 283, 283, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, @@ -2800,27 +2961,27 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, 247, 247, 247, 247, 247, 247, 247, 247, 247, - 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, - 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, - 247, 247, 247, 247, 247, 247, 247, 247, 247, 248, - 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, - 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, - 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, - 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, - 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, - 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, + 69, -140, -140, -140, -140, -140, -140, -140, -140, -140, + -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, + -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, + -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, + -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, + -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, + -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, + -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, + -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, + -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, - 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, - 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, - 247, 247, 247, 247, 247, 247, 247, 247 + -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, + -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, + -140, -140, -140, -140, -140, -140, -140, -140 }, { - 67, -141, -141, -141, -141, -141, -141, -141, -141, -141, - -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, + 69, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, + -141, -141, 284, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, @@ -2834,12 +2995,12 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -142, -142, -142, -142, -142, -142, -142, -142, -142, + 69, -142, -142, -142, -142, -142, -142, -142, -142, -142, + -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, - -142, -142, 249, -142, -142, -142, -142, -142, -142, -142, - -142, -142, -142, -142, -142, -142, -142, 250, -142, -142, + -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, @@ -2851,82 +3012,82 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, 251, 251, 251, 251, 251, 251, 251, 251, 251, + 69, -143, -143, -143, -143, -143, -143, -143, -143, -143, - -143, 251, 251, 251, 251, 251, 251, 251, 251, 251, - 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, - 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, - 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, - 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, - 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, - 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, - 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, - 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, - 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, + -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, + -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, + -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, + -143, -143, -143, -143, -143, -143, -143, -143, 285, 285, + 285, 285, 285, 285, 285, 285, 285, 285, -143, -143, + -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, + -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, + -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, + -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, + -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, - 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, - 251, 251, 251, 251, 251, 251, 251, 251 + -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, + -143, -143, -143, -143, -143, -143, -143, -143 }, { - 67, 252, 252, 252, 252, 252, 252, 252, 252, 252, - -144, 252, 252, 252, 252, 252, 252, 252, 252, 252, - 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, - 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, - 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, - 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, - 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, - 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, + 69, -144, -144, -144, -144, -144, -144, -144, -144, -144, + -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, + -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, + -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, + -144, -144, -144, -144, -144, -144, -144, -144, 285, 285, + 285, 285, 285, 285, 285, 285, 285, 285, -144, -144, + -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, + -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, - 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, - 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, - 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, - 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, - 252, 252, 252, 252, 252, 252, 252, 252 + -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, + -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, + -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, + -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, + -144, -144, -144, -144, -144, -144, -144, -144 }, { - 67, 253, 253, 253, 253, 253, 253, 253, 253, 253, - -145, 253, 253, 253, 253, 253, 253, 253, 253, 253, - 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, - 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, - 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, + 69, -145, -145, -145, -145, -145, -145, -145, -145, -145, + -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, + -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, + -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, + -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, - 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, - 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, - 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, - 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, - 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, - 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, - 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, - 253, 253, 253, 253, 253, 253, 253, 253 + -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, + -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, + -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, + -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, + -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, + -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, + -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, + -145, -145, -145, -145, -145, -145, -145, -145 }, { - 67, 254, 254, 254, 254, 254, 254, 254, 254, 254, - 255, 254, 254, 254, 254, 254, 254, 254, 254, 254, + 69, -146, -146, -146, -146, -146, -146, -146, -146, -146, + -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, - 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, - 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, - 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, - 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, - 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, - 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, - 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, - 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, - 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, - 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, + -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, + -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, + -146, -146, -146, -146, -146, -146, 286, -146, 287, 287, + 287, 287, 287, 287, 287, 287, 287, 287, -146, -146, + -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, + -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, + -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, + -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, + -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, + -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, - 254, 254, 254, 254, 254, 254, 254, 254 + -146, -146, -146, -146, -146, -146, -146, -146 }, { - 67, -147, -147, -147, -147, -147, -147, -147, -147, -147, - -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, - -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, + 69, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, + -147, -147, -147, -147, -147, -147, -147, -147, 288, 288, + 288, 288, 288, 288, 288, 288, 288, 288, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, @@ -2938,33 +3099,33 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -148, -148, -148, -148, -148, -148, -148, -148, -148, - -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, - -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, + 69, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, + -148, -148, -148, -148, -148, -148, 289, -148, 290, 290, + 290, 290, 290, 290, 290, 290, 290, 290, -148, -148, - -148, -148, -148, -148, -148, -148, -148, 256, -148, -148, + -148, -148, -148, -148, -148, -148, -148, -148, 291, 291, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, - 257, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, + 291, 291, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148 }, { - 67, -149, -149, -149, -149, -149, -149, -149, -149, -149, + 69, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, - -149, -149, -149, -149, -149, -149, -149, -149, 258, -149, - -149, -149, -149, -149, -149, -149, -149, -149, 259, -149, - -149, -149, 260, 261, 262, 263, -149, -149, -149, -149, + -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, + -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, + -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, @@ -2973,15 +3134,15 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -150, -150, -150, -150, -150, -150, -150, -150, -150, - -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, + 69, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, + -150, -150, -150, -150, -150, -150, 292, -150, 293, 293, + 293, 293, 293, 293, 293, 293, 293, 293, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, - -150, -150, -150, 264, -150, -150, 265, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, @@ -2990,12 +3151,12 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -151, -151, -151, -151, -151, -151, -151, -151, -151, - -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, - -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, + 69, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, + -151, -151, -151, -151, -151, -151, -151, -151, 294, 294, + 294, 294, 294, 294, 294, 294, 294, 294, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, @@ -3007,31 +3168,31 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -152, -152, -152, -152, -152, -152, -152, -152, -152, + 69, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, + -152, -152, -152, -152, -152, -152, 295, -152, 296, 296, + 296, 296, 296, 296, 296, 296, 296, 296, -152, -152, + -152, -152, -152, -152, -152, -152, -152, -152, 297, 297, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, - -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, - -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, - -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, - -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, + 297, 297, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152 }, { - 67, -153, -153, -153, -153, -153, -153, -153, -153, -153, + 69, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, - -153, -153, -153, -153, -153, -153, -153, -153, -153, 266, + -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, @@ -3042,41 +3203,41 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -154, -154, -154, -154, -154, -154, -154, -154, -154, - -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, - -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, - -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, - -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, - -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, - -154, -154, -154, -154, -154, 267, -154, -154, -154, -154, - -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, + 69, 298, 298, 298, 298, 298, 298, 298, 298, 298, + 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, + 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, + 298, 298, 298, 298, 298, 298, 298, 298, 298, 299, + 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, + 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, + 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, + 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, - -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, - -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, - -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, - -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, - -154, -154, -154, -154, -154, -154, -154, -154 + 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, + 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, + 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, + 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, + 298, 298, 298, 298, 298, 298, 298, 298 }, { - 67, -155, -155, -155, -155, -155, -155, -155, -155, -155, - -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, - -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, - -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, - -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, + 69, 300, 300, 300, 300, 300, 300, 300, 300, 300, + 301, 300, 300, 300, 300, 300, 300, 300, 300, 300, + 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, + 300, 300, 302, 300, 300, 300, 300, 300, 300, 300, + 300, 300, 300, 300, 300, 300, 300, 303, 300, 300, - -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, - -155, -155, -155, -155, -155, -155, -155, -155, 268, -155, - -155, -155, -155, -155, -155, -155, -155, -155, -155, 269, - 270, -155, -155, -155, -155, -155, 271, -155, -155, -155, - -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, - -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, - -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, - -155, -155, -155, -155, -155, -155, -155, -155 + 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, + 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, + 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, + 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, + 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, + 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, + 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, + 300, 300, 300, 300, 300, 300, 300, 300 }, { - 67, -156, -156, -156, -156, -156, -156, -156, -156, -156, + 69, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, @@ -3085,7 +3246,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, - -156, -156, -156, -156, -156, -156, -156, -156, -156, 272, + -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, @@ -3094,65 +3255,65 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -157, -157, -157, -157, -157, -157, -157, -157, -157, - -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, - -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, - -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, - -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, - -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, - -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, - -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, - -157, -157, -157, -157, -157, -157, -157, -157, -157, 273, + 69, 304, 304, 304, 304, 304, 304, 304, 304, 304, + 305, 304, 304, 304, 304, 304, 304, 304, 304, 304, + 304, 304, 304, 304, 304, 304, 304, 304, 304, 304, + 304, 304, 306, 304, 304, 304, 304, 304, 304, 304, + 304, 304, 304, 304, 304, 304, 304, 307, 304, 304, + 304, 304, 304, 304, 304, 304, 304, 304, 304, 304, + 304, 304, 304, 304, 304, 304, 304, 304, 304, 304, + 304, 304, 304, 304, 304, 304, 304, 304, 304, 304, + 304, 304, 304, 304, 304, 304, 304, 304, 304, 304, - -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, - -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, - -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, - -157, -157, -157, -157, -157, -157, -157, -157 + 304, 304, 304, 304, 304, 304, 304, 304, 304, 304, + 304, 304, 304, 304, 304, 304, 304, 304, 304, 304, + 304, 304, 304, 304, 304, 304, 304, 304, 304, 304, + 304, 304, 304, 304, 304, 304, 304, 304 }, { - 67, -158, -158, -158, -158, -158, -158, -158, -158, -158, - -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, - -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, - -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, - -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, - -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, + 69, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 309, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 310, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 308, 308, 308, 308, 308, 311, 308, 308, + 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, - -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, - -158, -158, -158, -158, -158, -158, -158, -158, 274, -158, - -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, - -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, - -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, - -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, - -158, -158, -158, -158, -158, -158, -158, -158 + 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 308, 308, 308, 308, 308, 308 }, { - 67, -159, -159, -159, -159, -159, -159, -159, -159, -159, - -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, - -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, + 69, 312, 312, 312, 312, 312, 312, 312, 312, 312, + 313, 312, 312, 312, 312, 312, 312, 312, 312, 312, + 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, - -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, - -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, - -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, - -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, - -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, - -159, -159, -159, -159, 275, -159, 276, -159, -159, -159, - -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, - -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, - -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, - -159, -159, -159, -159, -159, -159, -159, -159 + 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, + 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, + 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, + 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, + 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, + 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, + 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, + 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, + 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, + 312, 312, 312, 312, 312, 312, 312, 312 }, { - 67, -160, -160, -160, -160, -160, -160, -160, -160, -160, + 69, -160, -160, -160, -160, -160, -160, -160, -160, -160, + -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, - -160, -160, -160, -160, -160, -160, 277, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, @@ -3163,24 +3324,24 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -161, -161, -161, -161, -161, -161, -161, -161, -161, - -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, - -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, - -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, - -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, - -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, - -161, -161, -161, -161, -161, -161, -161, -161, 278, -161, + 69, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 315, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, - -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, - -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, - -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, - -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, - -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, - -161, -161, -161, -161, -161, -161, -161, -161 + 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 314, 314, 314, 314 }, { - 67, -162, -162, -162, -162, -162, -162, -162, -162, -162, + 69, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, @@ -3188,7 +3349,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, - -162, -162, -162, -162, -162, -162, -162, -162, -162, 279, + -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, @@ -3197,25 +3358,25 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -163, -163, -163, -163, -163, -163, -163, -163, -163, + 69, 316, 316, 316, 316, 316, 316, 316, 316, 316, - -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, - -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, - -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, - -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, - -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, - -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, - -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, - -163, -163, -163, -163, -163, 280, -163, -163, -163, -163, - -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, - -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, + 317, 316, 316, 316, 316, 316, 316, 316, 316, 316, + 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, + 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, + 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, + 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, + 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, + 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, + 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, + 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, + 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, - -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, - -163, -163, -163, -163, -163, -163, -163, -163 + 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, + 316, 316, 316, 316, 316, 316, 316, 316 }, { - 67, -164, -164, -164, -164, -164, -164, -164, -164, -164, + 69, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, @@ -3224,7 +3385,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, - -164, -164, -164, -164, 281, -164, -164, -164, -164, -164, + -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, @@ -3232,16 +3393,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -165, -165, -165, -165, -165, -165, -165, -165, -165, + 69, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, + -165, -165, -165, -165, -165, -165, -165, 318, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, - -165, -165, -165, -165, -165, -165, -165, -165, 282, -165, - -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, + 319, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, @@ -3249,17 +3410,17 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -166, -166, -166, -166, -166, -166, -166, -166, -166, + 69, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, - -166, -166, -166, -166, -166, -166, -166, -166, 283, -166, - -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, - -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, - -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, + -166, -166, -166, -166, -166, -166, -166, -166, 320, -166, + -166, -166, -166, -166, -166, -166, -166, -166, 321, -166, + 322, -166, 323, 324, 325, 326, -166, -166, -166, -166, + 327, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, @@ -3267,7 +3428,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -167, -167, -167, -167, -167, -167, -167, -167, -167, + 69, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, @@ -3275,7 +3436,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, - -167, -167, -167, 284, -167, -167, -167, -167, -167, -167, + -167, -167, -167, 328, -167, -167, 329, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, @@ -3284,7 +3445,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -168, -168, -168, -168, -168, -168, -168, -168, -168, + 69, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, @@ -3301,7 +3462,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -169, -169, -169, -169, -169, -169, -169, -169, -169, + 69, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, @@ -3309,7 +3470,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, - -169, -169, -169, -169, -169, -169, -169, -169, -169, 285, + -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, @@ -3319,8 +3480,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -170, -170, -170, -170, -170, -170, -170, -170, -170, - -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, + 69, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, @@ -3328,6 +3488,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, + -170, -170, 330, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, @@ -3336,7 +3497,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -171, -171, -171, -171, -171, -171, -171, -171, -171, + 69, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, @@ -3344,7 +3505,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, - -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, + -171, -171, -171, 331, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, @@ -3353,16 +3514,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -172, -172, -172, -172, -172, -172, -172, -172, -172, + 69, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, - -172, -172, -172, -172, -172, -172, -172, -172, 286, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, + 332, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, @@ -3370,17 +3531,17 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -173, -173, -173, -173, -173, -173, -173, -173, -173, + 69, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, + -173, -173, -173, -173, -173, 333, -173, -173, -173, -173, + -173, -173, -173, -173, -173, -173, -173, -173, -173, 334, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, - -173, -173, -173, 287, -173, -173, -173, -173, -173, -173, - -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, @@ -3388,7 +3549,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -174, -174, -174, -174, -174, -174, -174, -174, -174, + 69, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, @@ -3397,7 +3558,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, - -174, -174, 288, -174, -174, -174, -174, -174, -174, -174, + -174, -174, 335, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, @@ -3405,14 +3566,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -175, -175, -175, -175, -175, -175, -175, -175, -175, + 69, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, - -175, -175, -175, -175, -175, 289, -175, -175, -175, -175, + -175, -175, -175, -175, -175, -175, -175, -175, -175, 336, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, @@ -3422,14 +3583,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -176, -176, -176, -176, -176, -176, -176, -176, -176, + 69, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, - -176, -176, -176, -176, -176, -176, 290, -176, -176, -176, + -176, -176, -176, -176, -176, 337, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, @@ -3440,13 +3601,13 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -177, -177, -177, -177, -177, -177, -177, -177, -177, + 69, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, - -177, -177, -177, -177, -177, -177, -177, -177, -177, 291, + -177, -177, -177, -177, -177, -177, -177, -177, -177, 338, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, @@ -3457,16 +3618,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -178, -178, -178, -178, -178, -178, -178, -178, -178, + 69, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, - -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, - -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, - -178, -178, 292, -178, -178, -178, -178, -178, -178, 293, + -178, -178, -178, -178, -178, -178, -178, -178, 339, -178, + -178, -178, -178, -178, -178, -178, 340, -178, -178, 341, + 342, -178, -178, -178, -178, -178, 343, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, @@ -3474,16 +3635,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -179, -179, -179, -179, -179, -179, -179, -179, -179, + 69, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, - -179, -179, -179, -179, -179, -179, -179, -179, 294, -179, - -179, -179, -179, -179, -179, -179, -179, -179, 295, -179, - -179, -179, 296, 297, 298, 299, -179, -179, -179, -179, + -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, + -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, + -179, -179, -179, -179, -179, -179, -179, -179, -179, 344, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, @@ -3492,15 +3653,15 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -180, -180, -180, -180, -180, -180, -180, -180, -180, + 69, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, - -180, -180, -180, 300, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, + -180, -180, -180, -180, -180, -180, -180, -180, -180, 345, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, @@ -3509,16 +3670,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -181, -181, -181, -181, -181, -181, -181, -181, -181, + 69, -181, -181, -181, -181, -181, -181, -181, -181, -181, + -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, - -181, -181, -181, -181, -181, -181, -181, 301, -181, -181, + -181, -181, -181, -181, -181, -181, -181, -181, 346, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, - -181, -181, -181, 302, -181, -181, 303, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, @@ -3526,7 +3687,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -182, -182, -182, -182, -182, -182, -182, -182, -182, + 69, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, @@ -3535,7 +3696,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, - -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, + 347, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, @@ -3543,7 +3704,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -183, -183, -183, -183, -183, -183, -183, -183, -183, + 69, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, @@ -3552,7 +3713,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, - -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, + -183, -183, 348, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, @@ -3561,16 +3722,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -184, -184, -184, -184, -184, -184, -184, -184, -184, + 69, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, - -184, -184, -184, -184, -184, -184, -184, 304, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, - -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, + + -184, -184, -184, -184, 349, -184, 350, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, @@ -3578,15 +3739,15 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -185, -185, -185, -185, -185, -185, -185, -185, -185, + 69, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, + -185, -185, -185, -185, -185, -185, 351, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, - -185, -185, -185, -185, -185, -185, -185, -185, 305, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, @@ -3595,7 +3756,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -186, -186, -186, -186, -186, -186, -186, -186, -186, + 69, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, @@ -3603,8 +3764,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, - -186, -186, -186, -186, -186, -186, 306, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, + -186, -186, -186, -186, -186, 352, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, @@ -3613,15 +3774,15 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -187, -187, -187, -187, -187, -187, -187, -187, -187, + 69, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, + -187, -187, -187, -187, -187, -187, -187, -187, 353, -187, + -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, - -187, -187, -187, -187, -187, -187, -187, -187, -187, 307, - -187, -187, -187, -187, -187, -187, -187, -187, -187, 308, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, @@ -3630,16 +3791,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -188, -188, -188, -188, -188, -188, -188, -188, -188, + 69, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, - -188, -188, -188, -188, -188, 309, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, - -188, -188, -188, 310, -188, -188, -188, -188, -188, -188, + -188, -188, -188, -188, -188, -188, -188, -188, -188, 354, + -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, @@ -3647,7 +3808,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -189, -189, -189, -189, -189, -189, -189, -189, -189, + 69, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, @@ -3655,8 +3816,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, - -189, -189, -189, -189, -189, -189, -189, -189, -189, 311, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, + -189, -189, -189, -189, -189, 355, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, @@ -3665,14 +3826,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -190, -190, -190, -190, -190, -190, -190, -190, -190, + 69, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, - -190, -190, 312, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, + -190, -190, -190, -190, -190, -190, 356, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, @@ -3682,33 +3843,33 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -191, -191, -191, -191, -191, -191, -191, -191, -191, + 69, -191, -191, -191, -191, -191, -191, -191, -191, -191, + -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, + -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, + -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, + -191, -191, -191, -191, -191, 357, -191, -191, -191, -191, + -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, + -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, + -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, + -191, -191, 358, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, - -191, -191, 313, -191, -191, -191, -191, -191, -191, -191, - -191, -191, -191, -191, -191, -191, -191, -191, 314, 314, - 314, 314, 314, 314, 314, 314, 314, 314, -191, -191, - -191, -191, -191, -191, -191, 313, 313, 313, 313, 313, - - 313, 313, 313, 313, 313, 313, 313, 313, 313, 313, - 313, 313, 313, 313, 313, 313, 313, 313, 313, 313, - 313, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191 }, { - 67, -192, -192, -192, -192, -192, -192, -192, -192, -192, + 69, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, - -192, -192, 315, -192, -192, -192, -192, -192, -192, -192, - -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, + -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, + 359, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, @@ -3716,17 +3877,17 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -193, -193, -193, -193, -193, -193, -193, -193, -193, + 69, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, - -193, -193, 316, -193, -193, -193, -193, -193, -193, -193, - -193, -193, -193, -193, -193, -193, -193, -193, 317, 317, - 317, 317, 317, 317, 317, 317, 317, 317, -193, -193, - -193, -193, -193, -193, -193, 316, 316, 316, 316, 316, - 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, - 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, - 316, -193, -193, -193, -193, -193, -193, -193, -193, -193, + -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, + -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, + -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, + -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, + -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, + -193, -193, -193, -193, 360, -193, -193, -193, -193, -193, + -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, @@ -3734,14 +3895,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -194, -194, -194, -194, -194, -194, -194, -194, -194, + 69, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, - -194, -194, 318, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, + -194, -194, -194, -194, -194, -194, -194, -194, 361, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, @@ -3751,33 +3912,33 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -195, -195, -195, -195, -195, -195, -195, -195, -195, + 69, -195, -195, -195, -195, -195, -195, -195, -195, -195, + -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, + -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, + -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, + -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, + + -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, + -195, -195, -195, -195, -195, -195, -195, -195, 362, -195, + -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, - -195, -195, 319, -195, -195, -195, -195, -195, -195, -195, - -195, -195, -195, -195, -195, -195, -195, -195, 320, 320, - - 320, 320, 320, 320, 320, 320, 320, 320, -195, -195, - -195, -195, -195, -195, -195, 321, 321, 321, 321, 321, - 321, 321, 321, 321, 321, 321, 321, 321, 321, 321, - 321, 321, 321, 321, 321, 321, 321, 321, 321, 321, - 321, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195 }, { - 67, -196, -196, -196, -196, -196, -196, -196, -196, -196, + 69, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, - -196, -196, 322, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, + -196, -196, -196, 363, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, @@ -3786,10 +3947,10 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -197, -197, -197, -197, -197, -197, -197, -197, -197, + 69, -197, -197, -197, -197, -197, -197, -197, -197, -197, + -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, - -197, -197, 323, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, @@ -3803,32 +3964,32 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -198, -198, -198, -198, -198, -198, -198, -198, -198, + 69, -198, -198, -198, -198, -198, -198, -198, -198, -198, + -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, + -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, + -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, + -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, + -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, + + -198, -198, -198, -198, -198, -198, -198, -198, -198, 364, + -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, - -198, -198, 324, -198, -198, -198, -198, -198, -198, -198, - -198, -198, -198, -198, -198, -198, -198, -198, 325, 325, - 325, 325, 325, 325, 325, 325, 325, 325, -198, -198, - - -198, -198, -198, -198, -198, 324, 324, 324, 324, 324, - 324, 324, 324, 324, 324, 324, 324, 324, 324, 324, - 324, 324, 324, 324, 324, 324, 324, 324, 324, 324, - 324, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198 }, { - 67, -199, -199, -199, -199, -199, -199, -199, -199, -199, + 69, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, - -199, -199, 326, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, + -199, -199, -199, -199, -199, -199, -199, -199, -199, 365, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, @@ -3838,16 +3999,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -200, -200, -200, -200, -200, -200, -200, -200, -200, + 69, -200, -200, -200, -200, -200, -200, -200, -200, -200, + -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, + -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, + -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, + -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, + -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, + -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, + -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, - -200, -200, 327, -200, -200, -200, -200, -200, -200, -200, - -200, -200, -200, -200, -200, -200, -200, -200, 328, 328, - 328, 328, 328, 328, 328, 328, 328, 328, -200, -200, - -200, -200, -200, -200, -200, 329, 329, 329, 329, 329, - 329, 329, 329, 329, 329, 329, 329, 329, 329, 329, - 329, 329, 329, 329, 329, 329, 329, 329, 329, 329, - 329, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, @@ -3855,10 +4016,10 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -201, -201, -201, -201, -201, -201, -201, -201, -201, + 69, -201, -201, -201, -201, -201, -201, -201, -201, -201, + -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, - -201, -201, 330, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, @@ -3872,15 +4033,15 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -202, -202, -202, -202, -202, -202, -202, -202, -202, + 69, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, - -202, -202, -202, -202, -202, -202, -202, -202, 331, 331, - 331, 331, 331, 331, 331, 331, 331, 331, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, + -202, -202, -202, -202, -202, -202, -202, -202, 366, -202, + -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, @@ -3889,17 +4050,17 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -203, -203, -203, -203, -203, -203, -203, -203, -203, + 69, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, - -203, -203, -203, -203, -203, -203, -203, -203, 332, 332, - 332, 332, 332, 332, 332, 332, 332, 332, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, - -203, -203, -203, -203, -203, 333, -203, -203, -203, -203, + -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, + -203, -203, -203, 367, -203, -203, -203, -203, -203, -203, + -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, @@ -3907,16 +4068,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -204, -204, -204, -204, -204, -204, -204, -204, -204, + 69, -204, -204, -204, -204, -204, -204, -204, -204, -204, + -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, - -204, -204, -204, -204, -204, -204, -204, -204, -204, 334, - 334, 334, 334, 334, 334, 334, 334, 334, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, - -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, + + -204, -204, 368, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, @@ -3924,16 +4085,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -205, -205, -205, -205, -205, -205, -205, -205, -205, + 69, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, - -205, -205, 335, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, - -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, + -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, + -205, -205, -205, -205, -205, 369, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, @@ -3941,17 +4102,17 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -206, -206, -206, -206, -206, -206, -206, -206, -206, + 69, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, - -206, -206, 336, -206, -206, -206, -206, -206, -206, -206, - -206, -206, -206, -206, -206, -206, -206, -206, 337, 337, - 337, 337, 337, 337, 337, 337, 337, 337, -206, -206, - -206, -206, -206, -206, -206, 336, 336, 336, 336, 336, - 336, 336, 336, 336, 336, 336, 336, 336, 336, 336, - 336, 336, 336, 336, 336, 336, 336, 336, 336, 336, - 336, -206, -206, -206, -206, -206, -206, -206, -206, -206, + -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, + -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, + -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, + -206, -206, -206, -206, -206, 370, -206, -206, -206, -206, + -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, + -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, + -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, @@ -3959,32 +4120,32 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -207, -207, -207, -207, -207, -207, -207, -207, -207, + 69, -207, -207, -207, -207, -207, -207, -207, -207, -207, + -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, - -207, -207, -207, -207, -207, -207, -207, -207, 338, 338, - 338, 338, 338, 338, 338, 338, 338, 338, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, + -207, -207, -207, -207, -207, -207, 371, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, - -207, -207, -207, -207, -207, 339, -207, -207, -207, -207, + -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207 }, { - 67, -208, -208, -208, -208, -208, -208, -208, -208, -208, + 69, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, - -208, -208, -208, -208, -208, -208, -208, -208, -208, 340, - 340, 340, 340, 340, 340, 340, 340, 340, -208, -208, - -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, + + -208, -208, -208, -208, -208, -208, -208, -208, -208, 372, + -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, @@ -3993,17 +4154,17 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -209, -209, -209, -209, -209, -209, -209, -209, -209, + 69, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, - -209, -209, -209, -209, -209, -209, -209, -209, 341, 341, - 341, 341, 341, 341, 341, 341, 341, 341, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, - -209, -209, -209, -209, -209, 342, -209, -209, -209, -209, + -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, + -209, -209, 373, -209, -209, -209, -209, -209, -209, 374, + -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209 @@ -4011,16 +4172,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -210, -210, -210, -210, -210, -210, -210, -210, -210, - -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, - -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, + 69, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, - -210, -210, -210, -210, -210, -210, -210, -210, -210, 343, - 343, 343, 343, 343, 343, 343, 343, 343, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, + -210, -210, -210, -210, -210, -210, -210, -210, 375, -210, + -210, -210, -210, -210, -210, -210, -210, -210, 376, -210, + 377, -210, 378, 379, 380, 381, -210, -210, -210, -210, + 382, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, @@ -4028,15 +4189,15 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -211, -211, -211, -211, -211, -211, -211, -211, -211, + 69, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, - -211, -211, 344, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, - -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, + + -211, -211, -211, -211, -211, -211, 383, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, @@ -4045,32 +4206,32 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -212, -212, -212, -212, -212, -212, -212, -212, -212, + 69, -212, -212, -212, -212, -212, -212, -212, -212, -212, + -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, - -212, -212, 345, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, - -212, -212, -212, -212, -212, 346, 346, 346, 346, 346, - 346, 346, 346, 346, 346, 346, 346, 346, 346, 346, - 346, 346, 346, 346, 346, 346, 346, 346, 346, 346, - 346, -212, -212, -212, -212, -212, -212, -212, -212, -212, + -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, + -212, -212, -212, 384, -212, -212, -212, -212, -212, -212, + -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, + -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212 }, { - 67, -213, -213, -213, -213, -213, -213, -213, -213, -213, + 69, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, - -213, -213, 347, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, + -213, -213, -213, -213, -213, -213, -213, 385, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, @@ -4080,16 +4241,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -214, -214, -214, -214, -214, -214, -214, -214, -214, + 69, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, - -214, -214, 348, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, + -214, -214, -214, -214, -214, -214, -214, 386, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, - -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, + -214, -214, -214, 387, -214, -214, 388, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, @@ -4097,33 +4258,33 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -215, -215, -215, -215, -215, -215, -215, -215, -215, + 69, -215, -215, -215, -215, -215, -215, -215, -215, -215, + -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, - -215, -215, 349, -215, -215, -215, -215, -215, -215, -215, - -215, -215, -215, -215, -215, -215, -215, -215, 350, 350, - - 350, 350, 350, 350, 350, 350, 350, 350, -215, -215, - -215, -215, -215, -215, -215, 351, 351, 351, 351, 351, - 351, 351, 351, 351, 351, 351, 351, 351, 351, 351, - 351, 351, 351, 351, 351, 351, 351, 351, 351, 351, - 351, -215, -215, -215, -215, -215, -215, -215, -215, -215, + -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, + + -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, + -215, -215, -215, -215, -215, -215, -215, -215, 389, 390, + -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, + 391, -215, -215, -215, -215, -215, -215, -215, -215, -215, + -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215 }, { - 67, -216, -216, -216, -216, -216, -216, -216, -216, -216, + 69, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, - -216, -216, 352, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, + -216, -216, -216, -216, 392, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, @@ -4132,10 +4293,10 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -217, -217, -217, -217, -217, -217, -217, -217, -217, + 69, -217, -217, -217, -217, -217, -217, -217, -217, -217, + -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, - -217, -217, 353, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, @@ -4149,32 +4310,32 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -218, -218, -218, -218, -218, -218, -218, -218, -218, + 69, -218, -218, -218, -218, -218, -218, -218, -218, -218, + -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, + -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, + -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, + -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, + -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, + + -218, -218, -218, -218, -218, -218, -218, 393, -218, -218, + -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, - -218, -218, 354, -218, -218, -218, -218, -218, -218, -218, - -218, -218, -218, -218, -218, -218, -218, -218, 355, 355, - 355, 355, 355, 355, 355, 355, 355, 355, -218, -218, - - -218, -218, -218, -218, -218, 356, 356, 356, 356, 356, - 356, 356, 356, 356, 356, 356, 356, 356, 356, 356, - 356, 356, 356, 356, 356, 356, 356, 356, 356, 356, - 356, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218 }, { - 67, -219, -219, -219, -219, -219, -219, -219, -219, -219, + 69, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, - -219, -219, 357, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, + -219, -219, -219, -219, -219, -219, -219, -219, 394, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, @@ -4184,16 +4345,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -220, -220, -220, -220, -220, -220, -220, -220, -220, + 69, -220, -220, -220, -220, -220, -220, -220, -220, -220, + -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, - -220, -220, -220, -220, -220, -220, -220, -220, 358, 358, - 358, 358, 358, 358, 358, 358, 358, 358, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, + -220, -220, -220, -220, -220, -220, 395, -220, -220, -220, + -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, - -220, -220, -220, -220, -220, 359, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, @@ -4201,16 +4362,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -221, -221, -221, -221, -221, -221, -221, -221, -221, + 69, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, - -221, -221, -221, -221, -221, -221, -221, -221, 360, 361, - 361, 361, 361, 361, 361, 361, 361, 361, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, - -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, + + -221, -221, -221, -221, -221, -221, -221, -221, -221, 396, + -221, -221, -221, -221, -221, -221, -221, -221, -221, 397, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, @@ -4218,33 +4379,33 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -222, -222, -222, -222, -222, -222, -222, -222, -222, + 69, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, - -222, -222, -222, -222, -222, -222, -222, -222, 362, 362, - 362, 362, 362, 362, 362, 362, 362, 362, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, + -222, -222, -222, -222, -222, 398, -222, -222, -222, -222, + -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, + -222, -222, -222, 399, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, - -222, -222, -222, -222, -222, 363, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222 }, { - 67, -223, -223, -223, -223, -223, -223, -223, -223, -223, + 69, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, - -223, -223, -223, -223, -223, -223, -223, -223, 364, 365, - 365, 365, 365, 365, 365, 365, 365, 365, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, + -223, -223, -223, -223, -223, -223, -223, -223, -223, 400, + -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, @@ -4253,30 +4414,30 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -224, -224, -224, -224, -224, -224, -224, -224, -224, + 69, -224, -224, -224, -224, -224, -224, -224, -224, -224, + -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, - -224, -224, -224, -224, -224, -224, -224, -224, 366, 366, - 366, 366, 366, 366, 366, 366, 366, 366, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, + -224, -224, -224, -224, -224, -224, -224, -224, -224, 401, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, - -224, -224, -224, -224, -224, 367, -224, -224, -224, -224, + -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224 }, { - 67, -225, -225, -225, -225, -225, -225, -225, -225, -225, + 69, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, + -225, -225, 402, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, - -225, -225, -225, -225, -225, -225, -225, -225, 368, 369, - 369, 369, 369, 369, 369, 369, 369, 369, -225, -225, + -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, @@ -4287,17 +4448,17 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -226, -226, -226, -226, -226, -226, -226, -226, -226, + 69, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, - -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, - -226, -226, -226, -226, -226, -226, -226, -226, 370, 370, - 370, 370, 370, 370, 370, 370, 370, 370, -226, -226, - -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, - -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, - -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, - -226, -226, -226, -226, -226, 371, -226, -226, -226, -226, + -226, -226, 403, -226, -226, -226, -226, -226, -226, -226, + -226, -226, -226, -226, -226, -226, -226, -226, 404, 405, + 405, 405, 405, 405, 405, 405, 405, 405, -226, -226, + -226, -226, -226, -226, -226, 403, 403, 403, 403, 403, + 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, + 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, + 403, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, @@ -4305,51 +4466,51 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -227, -227, -227, -227, -227, -227, -227, -227, -227, - -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, - -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, + 69, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, - -227, -227, -227, -227, -227, -227, -227, -227, 372, 373, - 373, 373, 373, 373, 373, 373, 373, 373, -227, -227, - -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, - -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, - -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, - -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, + -227, -227, 406, -227, -227, -227, -227, -227, -227, -227, + -227, -227, -227, -227, -227, -227, -227, -227, 404, 404, + 404, 404, 404, 404, 404, 404, 404, 404, -227, -227, + -227, -227, -227, -227, -227, 406, 406, 406, 406, 406, + 406, 406, 406, 406, 406, 406, 406, 406, 406, 406, + 406, 406, 406, 406, 406, 406, 406, 406, 406, 406, + + 406, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227 }, { - 67, -228, -228, -228, -228, -228, -228, -228, -228, -228, + 69, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, + -228, -228, 407, -228, -228, -228, -228, -228, -228, -228, + -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, - -228, -228, -228, -228, -228, -228, -228, -228, 374, 374, - 374, 374, 374, 374, 374, 374, 374, 374, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, - -228, -228, -228, -228, -228, 375, -228, -228, -228, -228, + -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228 }, { - 67, -229, -229, -229, -229, -229, -229, -229, -229, -229, + 69, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, - -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, - -229, -229, -229, -229, -229, -229, -229, -229, 376, 377, - 377, 377, 377, 377, 377, 377, 377, 377, -229, -229, - -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, - -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, - -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, - -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, + -229, -229, 408, -229, -229, -229, -229, -229, -229, -229, + -229, -229, -229, -229, -229, -229, -229, -229, 404, 404, + 404, 404, 404, 404, 404, 404, 404, 404, -229, -229, + -229, -229, -229, -229, -229, 408, 408, 408, 408, 408, + 408, 408, 408, 408, 408, 408, 408, 408, 408, 408, + 408, 408, 408, 408, 408, 408, 408, 408, 408, 408, + 408, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229 @@ -4357,10 +4518,10 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -230, -230, -230, -230, -230, -230, -230, -230, -230, + 69, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, - -230, -230, 378, -230, -230, -230, -230, -230, -230, -230, + -230, -230, 409, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, @@ -4374,51 +4535,51 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -231, -231, -231, -231, -231, -231, -231, -231, -231, - -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, - -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, - -231, -231, 379, -231, -231, -231, -231, -231, -231, -231, - -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, - -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, - -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, - - -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, + 69, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, + -231, -231, 410, -231, -231, -231, -231, -231, -231, -231, + -231, -231, -231, -231, -231, -231, -231, -231, 411, 411, + 411, 411, 411, 411, 411, 411, 411, 411, -231, -231, + -231, -231, -231, -231, -231, 410, 410, 410, 410, 410, + + 410, 410, 410, 410, 410, 410, 410, 410, 410, 410, + 410, 410, 410, 410, 410, 410, 410, 410, 410, 410, + 410, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231 }, { - 67, -232, -232, -232, -232, -232, -232, -232, -232, -232, + 69, -232, -232, -232, -232, -232, -232, -232, -232, -232, + -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, + -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, + -232, -232, 412, -232, -232, -232, -232, -232, -232, -232, + + -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, + -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, + -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, + -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, - -232, -232, 380, -232, -232, -232, -232, -232, -232, -232, - - -232, -232, -232, -232, -232, -232, -232, -232, 381, 381, - 381, 381, 381, 381, 381, 381, 381, 381, -232, -232, - -232, -232, -232, -232, -232, 380, 380, 380, 380, 380, - 380, 380, 380, 380, 380, 380, 380, 380, 380, 380, - 380, 380, 380, 380, 380, 380, 380, 380, 380, 380, - 380, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232 }, { - 67, -233, -233, -233, -233, -233, -233, -233, -233, -233, + 69, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, - -233, -233, 382, -233, -233, -233, -233, -233, -233, -233, - -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, - -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, - -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, - -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, - -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, - -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, + -233, -233, 413, -233, -233, -233, -233, -233, -233, -233, + -233, -233, -233, -233, -233, -233, -233, -233, 414, 414, + 414, 414, 414, 414, 414, 414, 414, 414, -233, -233, + -233, -233, -233, -233, -233, 415, 415, 415, 415, 415, + 415, 415, 415, 415, 415, 415, 415, 415, 415, 415, + 415, 415, 415, 415, 415, 415, 415, 415, 415, 415, + 415, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, @@ -4426,27 +4587,27 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -234, -234, -234, -234, -234, -234, -234, -234, -234, + 69, -234, -234, -234, -234, -234, -234, -234, -234, -234, + -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, + -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, + -234, -234, 416, -234, -234, -234, -234, -234, -234, -234, + -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, + -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, + -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, + -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, + -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, - -234, -234, 383, -234, -234, -234, -234, -234, -234, -234, - -234, -234, -234, -234, -234, -234, -234, -234, 384, 384, - 384, 384, 384, 384, 384, 384, 384, 384, -234, -234, - -234, -234, -234, -234, -234, 383, 383, 383, 383, 383, - 383, 383, 383, 383, 383, 383, 383, 383, 383, 383, - - 383, 383, 383, 383, 383, 383, 383, 383, 383, 383, - 383, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234 }, { - 67, -235, -235, -235, -235, -235, -235, -235, -235, -235, + 69, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, - -235, -235, 385, -235, -235, -235, -235, -235, -235, -235, + -235, -235, 417, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, @@ -4460,17 +4621,17 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -236, -236, -236, -236, -236, -236, -236, -236, -236, + 69, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, - -236, -236, 386, -236, -236, -236, -236, -236, -236, -236, - -236, -236, -236, -236, -236, -236, -236, -236, 387, 387, - 387, 387, 387, 387, 387, 387, 387, 387, -236, -236, - -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, - -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, - -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, - -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, + -236, -236, 418, -236, -236, -236, -236, -236, -236, -236, + -236, -236, -236, -236, -236, -236, -236, -236, 419, 419, + 419, 419, 419, 419, 419, 419, 419, 419, -236, -236, + -236, -236, -236, -236, -236, 418, 418, 418, 418, 418, + 418, 418, 418, 418, 418, 418, 418, 418, 418, 418, + 418, 418, 418, 418, 418, 418, 418, 418, 418, 418, + 418, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, @@ -4478,10 +4639,10 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -237, -237, -237, -237, -237, -237, -237, -237, -237, + 69, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, - -237, -237, 388, -237, -237, -237, -237, -237, -237, -237, + -237, -237, 420, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, @@ -4495,28 +4656,28 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -238, -238, -238, -238, -238, -238, -238, -238, -238, - -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, - -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, - -238, -238, 389, -238, -238, -238, -238, -238, -238, -238, - -238, -238, -238, -238, -238, -238, -238, -238, 390, 390, - 390, 390, 390, 390, 390, 390, 390, 390, -238, -238, - - -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, - -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, + 69, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, + -238, -238, 421, -238, -238, -238, -238, -238, -238, -238, + -238, -238, -238, -238, -238, -238, -238, -238, 422, 422, + 422, 422, 422, 422, 422, 422, 422, 422, -238, -238, + + -238, -238, -238, -238, -238, 423, 423, 423, 423, 423, + 423, 423, 423, 423, 423, 423, 423, 423, 423, 423, + 423, 423, 423, 423, 423, 423, 423, 423, 423, 423, + 423, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238 }, { - 67, -239, -239, -239, -239, -239, -239, -239, -239, -239, + 69, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, - -239, -239, 239, -239, -239, -239, -239, -239, -239, -239, + -239, -239, 424, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, @@ -4530,12 +4691,12 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -240, -240, -240, -240, -240, -240, -240, -240, -240, + 69, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, - -240, -240, -240, -240, -240, -240, -240, -240, 240, 240, - 240, 240, 240, 240, 240, 240, 240, 240, -240, -240, + -240, -240, -240, -240, -240, -240, -240, -240, 425, 425, + 425, 425, 425, 425, 425, 425, 425, 425, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, @@ -4547,99 +4708,99 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -241, -241, -241, -241, -241, -241, -241, -241, -241, + 69, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, - -241, -241, -241, -241, -241, -241, -241, -241, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, -241, -241, + -241, -241, -241, -241, -241, 426, -241, -241, 427, 428, + 428, 428, 428, 428, 428, 428, 428, 428, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, - -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, + -241, -241, -241, -241, -241, 429, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241 }, { - 67, -242, -242, -242, -242, -242, -242, -242, -242, -242, + 69, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, - -242, -242, -242, -242, -242, -242, 244, -242, 245, 245, - 245, 245, 245, 245, 245, 245, 245, 245, -242, -242, - -242, -242, -242, -242, -242, -242, -242, -242, -242, 246, + -242, -242, -242, -242, -242, 426, -242, -242, 430, 430, + 430, 430, 430, 430, 430, 430, 430, 430, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, - -242, 246, -242, -242, -242, -242, -242, -242, -242, -242, + -242, -242, -242, -242, -242, 431, -242, -242, -242, -242, + -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242 }, { - 67, -243, -243, -243, -243, -243, -243, -243, -243, -243, + 69, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, - -243, -243, -243, -243, -243, -243, -243, -243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, -243, -243, - -243, -243, -243, -243, -243, -243, -243, -243, -243, 246, + -243, -243, -243, -243, -243, -243, -243, -243, 432, 432, + 432, 432, 432, 432, 432, 432, 432, 432, -243, -243, + -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, + -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, - -243, 246, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243 }, { - 67, -244, -244, -244, -244, -244, -244, -244, -244, -244, + 69, -244, -244, -244, -244, -244, -244, -244, -244, -244, + -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, + -244, -244, -244, -244, -244, 426, -244, -244, 433, 433, + 433, 433, 433, 433, 433, 433, 433, 433, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, - -244, -244, -244, -244, -244, -244, -244, -244, 391, 391, - 391, 391, 391, 391, 391, 391, 391, 391, -244, -244, - -244, -244, -244, -244, -244, -244, -244, -244, -244, 246, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, + -244, -244, -244, -244, -244, 434, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, - -244, 246, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244 }, { - 67, -245, -245, -245, -245, -245, -245, -245, -245, -245, + 69, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, - -245, -245, -245, -245, -245, -245, 244, -245, 245, 245, + -245, -245, -245, -245, -245, -245, -245, -245, 435, 436, - 245, 245, 245, 245, 245, 245, 245, 245, -245, -245, - -245, -245, -245, -245, -245, -245, -245, -245, -245, 246, + 436, 436, 436, 436, 436, 436, 436, 436, -245, -245, + -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, + -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, - -245, 246, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245 }, { - 67, -246, -246, -246, -246, -246, -246, -246, -246, -246, + 69, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, + -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, + -246, -246, 437, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, - -246, -246, -246, 392, -246, 392, -246, -246, 393, 393, - 393, 393, 393, 393, 393, 393, 393, 393, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, @@ -4651,47 +4812,47 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, 247, 247, 247, 247, 247, 247, 247, 247, 247, - 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, - 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, - 247, 247, 247, 247, 247, 247, 247, 247, 247, 248, - 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, - 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, - 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, - 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, - 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, + 69, -247, -247, -247, -247, -247, -247, -247, -247, -247, + -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, + -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, + -247, -247, 438, -247, -247, -247, -247, -247, -247, -247, + -247, -247, -247, -247, -247, -247, -247, -247, 439, 439, + 439, 439, 439, 439, 439, 439, 439, 439, -247, -247, + -247, -247, -247, -247, -247, 438, 438, 438, 438, 438, + 438, 438, 438, 438, 438, 438, 438, 438, 438, 438, + 438, 438, 438, 438, 438, 438, 438, 438, 438, 438, - 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, - 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, - 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, - 247, 247, 247, 247, 247, 247, 247, 247 + 438, -247, -247, -247, -247, -247, -247, -247, -247, -247, + -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, + -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, + -247, -247, -247, -247, -247, -247, -247, -247 }, { - 67, -248, -248, -248, -248, -248, -248, -248, -248, -248, - -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, + 69, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, - -248, -248, -248, -248, -248, -248, -248, -248, -248, 247, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, + -248, -248, -248, -248, -248, -248, -248, -248, 440, 440, + 440, 440, 440, 440, 440, 440, 440, 440, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, - -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, + -248, -248, -248, -248, -248, 441, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248 }, { - 67, -249, -249, -249, -249, -249, -249, -249, -249, -249, + 69, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, - -249, -249, 249, -249, -249, -249, -249, -249, -249, -249, - -249, -249, -249, -249, -249, -249, -249, 250, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, + -249, -249, -249, -249, -249, -249, -249, -249, -249, 442, + 442, 442, 442, 442, 442, 442, 442, 442, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, @@ -4703,117 +4864,117 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, 251, 251, 251, 251, 251, 251, 251, 251, 251, - -250, 251, 251, 251, 251, 251, 251, 251, 251, 251, - 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, - 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, - 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, - 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, - 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, - 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, - 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, - 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, + 69, -250, -250, -250, -250, -250, -250, -250, -250, -250, + -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, + -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, + -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, + -250, -250, -250, -250, -250, -250, -250, -250, 443, 443, + 443, 443, 443, 443, 443, 443, 443, 443, -250, -250, + -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, + -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, + -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, + -250, -250, -250, -250, -250, 444, -250, -250, -250, -250, - 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, - 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, - 251, 251, 251, 251, 251, 251, 251, 251 + -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, + -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, + -250, -250, -250, -250, -250, -250, -250, -250 }, { - 67, 251, 251, 251, 251, 251, 251, 251, 251, 251, - -251, 251, 251, 251, 251, 251, 251, 251, 251, 251, - 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, - 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, - 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, - 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, - 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, + 69, -251, -251, -251, -251, -251, -251, -251, -251, -251, + -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, + -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, + -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, + -251, -251, -251, -251, -251, -251, -251, -251, -251, 445, + 445, 445, 445, 445, 445, 445, 445, 445, -251, -251, + -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, - 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, - 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, - 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, - 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, - 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, - 251, 251, 251, 251, 251, 251, 251, 251 + -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, + -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, + -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, + -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, + -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, + -251, -251, -251, -251, -251, -251, -251, -251 }, { - 67, 252, 252, 252, 252, 252, 252, 252, 252, 252, - -252, 252, 252, 252, 252, 252, 252, 252, 252, 252, - 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, - 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, + 69, -252, -252, -252, -252, -252, -252, -252, -252, -252, + -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, + -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, + -252, -252, 446, -252, -252, -252, -252, -252, -252, -252, - 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, - 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, - 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, - 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, - 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, - 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, - 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, - 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, - 252, 252, 252, 252, 252, 252, 252, 252 + -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, + -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, + -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, + -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, + -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, + -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, + -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, + -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, + -252, -252, -252, -252, -252, -252, -252, -252 }, { - 67, 253, 253, 253, 253, 253, 253, 253, 253, 253, + 69, -253, -253, -253, -253, -253, -253, -253, -253, -253, - -253, 253, 253, 253, 253, 253, 253, 253, 253, 253, - 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, - 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, - 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, - 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, - 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, - 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, - 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, - 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, - 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, + -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, + -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, + -253, -253, 447, -253, -253, -253, -253, -253, -253, -253, + -253, -253, -253, -253, -253, -253, -253, -253, 448, 448, + 448, 448, 448, 448, 448, 448, 448, 448, -253, -253, + -253, -253, -253, -253, -253, 447, 447, 447, 447, 447, + 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, + 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, + 447, -253, -253, -253, -253, -253, -253, -253, -253, -253, + -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, - 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, - 253, 253, 253, 253, 253, 253, 253, 253 + -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, + -253, -253, -253, -253, -253, -253, -253, -253 }, { - 67, 254, 254, 254, 254, 254, 254, 254, 254, 254, - 255, 254, 254, 254, 254, 254, 254, 254, 254, 254, - 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, - 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, - 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, - 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, - 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, - 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, + 69, -254, -254, -254, -254, -254, -254, -254, -254, -254, + -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, + -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, + -254, -254, 449, -254, -254, -254, -254, -254, -254, -254, + -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, + -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, + -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, + -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, - 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, - 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, - 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, - 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, - 254, 254, 254, 254, 254, 254, 254, 254 + -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, + -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, + -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, + -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, + -254, -254, -254, -254, -254, -254, -254, -254 }, { - 67, -255, -255, -255, -255, -255, -255, -255, -255, -255, - -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, - -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, - -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, - -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, - - -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, - -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, - -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, + 69, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, + -255, -255, 450, -255, -255, -255, -255, -255, -255, -255, + -255, -255, -255, -255, -255, -255, -255, -255, 451, 451, + + 451, 451, 451, 451, 451, 451, 451, 451, -255, -255, + -255, -255, -255, -255, -255, 452, 452, 452, 452, 452, + 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, + 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, + 452, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255 }, { - 67, -256, -256, -256, -256, -256, -256, -256, -256, -256, + 69, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, + -256, -256, 453, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, - -256, -256, -256, -256, -256, -256, -256, -256, 394, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, @@ -4824,13 +4985,13 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -257, -257, -257, -257, -257, -257, -257, -257, -257, + 69, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, + -257, -257, 454, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, - -257, -257, -257, -257, -257, -257, -257, 395, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, @@ -4841,31 +5002,31 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -258, -258, -258, -258, -258, -258, -258, -258, -258, - -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, - -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, - -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, - -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, - -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, - - -258, -258, -258, -258, -258, -258, -258, -258, -258, 396, - -258, -258, -258, -258, -258, -258, 397, -258, -258, -258, + 69, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, + -258, -258, 455, -258, -258, -258, -258, -258, -258, -258, + -258, -258, -258, -258, -258, -258, -258, -258, 456, 456, + 456, 456, 456, 456, 456, 456, 456, 456, -258, -258, + + -258, -258, -258, -258, -258, 457, 457, 457, 457, 457, + 457, 457, 457, 457, 457, 457, 457, 457, 457, 457, + 457, 457, 457, 457, 457, 457, 457, 457, 457, 457, + 457, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258 }, { - 67, -259, -259, -259, -259, -259, -259, -259, -259, -259, + 69, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, + -259, -259, 458, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, - -259, -259, -259, -259, -259, 398, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, @@ -4876,15 +5037,15 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -260, -260, -260, -260, -260, -260, -260, -260, -260, + 69, -260, -260, -260, -260, -260, -260, -260, -260, -260, + -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, + -260, -260, -260, -260, -260, -260, -260, -260, 459, 459, + 459, 459, 459, 459, 459, 459, 459, 459, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, - -260, -260, -260, -260, -260, -260, -260, -260, 399, -260, - -260, -260, -260, -260, -260, -260, -260, -260, -260, 400, - 401, -260, -260, -260, -260, -260, 402, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, @@ -4893,50 +5054,50 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -261, -261, -261, -261, -261, -261, -261, -261, -261, - -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, - -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, + 69, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, + -261, -261, -261, -261, -261, 460, -261, -261, 461, 462, + 462, 462, 462, 462, 462, 462, 462, 462, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, - -261, -261, -261, -261, -261, -261, -261, -261, -261, 403, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, + -261, -261, -261, -261, -261, 463, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261 }, { - 67, -262, -262, -262, -262, -262, -262, -262, -262, -262, + 69, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, + -262, -262, -262, -262, -262, 460, -262, -262, 464, 464, + 464, 464, 464, 464, 464, 464, 464, 464, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, - -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, - -262, -262, -262, -262, -262, -262, -262, -262, -262, 404, - -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, + -262, -262, -262, -262, -262, 465, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262 }, { - 67, -263, -263, -263, -263, -263, -263, -263, -263, -263, + 69, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, + -263, -263, -263, -263, -263, -263, -263, -263, 466, 466, + 466, 466, 466, 466, 466, 466, 466, 466, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, - -263, -263, -263, -263, -263, -263, -263, -263, 405, -263, - -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, @@ -4945,30 +5106,30 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -264, -264, -264, -264, -264, -264, -264, -264, -264, - -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, - -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, + 69, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, + -264, -264, -264, -264, -264, 460, -264, -264, 467, 467, + 467, 467, 467, 467, 467, 467, 467, 467, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, - -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, + -264, -264, -264, -264, -264, 468, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264 }, { - 67, -265, -265, -265, -265, -265, -265, -265, -265, -265, - -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, + 69, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, + -265, -265, -265, -265, -265, -265, -265, -265, 469, 470, - -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, + 470, 470, 470, 470, 470, 470, 470, 470, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, @@ -4979,17 +5140,17 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -266, -266, -266, -266, -266, -266, -266, -266, -266, + 69, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, + -266, -266, -266, -266, -266, -266, -266, -266, 471, 471, + 471, 471, 471, 471, 471, 471, 471, 471, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, - -266, -266, -266, -266, -266, -266, 406, -266, -266, -266, - -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, - -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, + -266, -266, -266, -266, -266, 472, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, @@ -4997,15 +5158,15 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -267, -267, -267, -267, -267, -267, -267, -267, -267, + 69, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, + -267, -267, -267, -267, -267, -267, -267, -267, 473, 474, + 474, 474, 474, 474, 474, 474, 474, 474, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, - -267, -267, -267, -267, -267, -267, -267, 407, -267, -267, - -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, @@ -5014,34 +5175,34 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -268, -268, -268, -268, -268, -268, -268, -268, -268, - -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, - -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, + 69, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, + -268, -268, -268, -268, -268, -268, -268, -268, 475, 475, + 475, 475, 475, 475, 475, 475, 475, 475, -268, -268, - -268, -268, -268, -268, -268, -268, -268, -268, -268, 408, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, + -268, -268, -268, -268, -268, 476, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268 }, { - 67, -269, -269, -269, -269, -269, -269, -269, -269, -269, + 69, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, + -269, -269, -269, -269, -269, -269, -269, -269, 477, 478, + 478, 478, 478, 478, 478, 478, 478, 478, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, - -269, -269, -269, -269, 409, -269, -269, -269, -269, -269, - -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269 @@ -5049,16 +5210,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -270, -270, -270, -270, -270, -270, -270, -270, -270, - -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, - -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, + 69, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, + -270, -270, -270, -270, -270, -270, -270, -270, 479, 479, + 479, 479, 479, 479, 479, 479, 479, 479, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, - -270, -270, -270, 410, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, + -270, -270, -270, -270, -270, 480, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, @@ -5066,13 +5227,13 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -271, -271, -271, -271, -271, -271, -271, -271, -271, + 69, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, + -271, -271, -271, -271, -271, -271, -271, -271, 481, 482, + 482, 482, 482, 482, 482, 482, 482, 482, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, - -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, - -271, -271, -271, -271, -271, 411, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, @@ -5083,33 +5244,33 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -272, -272, -272, -272, -272, -272, -272, -272, -272, + 69, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, - -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, - -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, - -272, -272, -272, -272, -272, -272, -272, -272, -272, 412, + -272, -272, -272, -272, -272, -272, -272, -272, 483, 483, + 483, 483, 483, 483, 483, 483, 483, 483, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, + -272, -272, -272, -272, -272, 484, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272 }, { - 67, -273, -273, -273, -273, -273, -273, -273, -273, -273, + 69, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, + -273, -273, -273, -273, -273, -273, -273, -273, 485, 486, + 486, 486, 486, 486, 486, 486, 486, 486, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, - -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, - 413, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, @@ -5118,14 +5279,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -274, -274, -274, -274, -274, -274, -274, -274, -274, + 69, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, + -274, -274, 487, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, - -274, -274, -274, 414, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, @@ -5135,14 +5296,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -275, -275, -275, -275, -275, -275, -275, -275, -275, - -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, - -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, + 69, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, + -275, -275, 488, -275, -275, -275, -275, -275, -275, -275, + -275, -275, -275, -275, -275, -275, -275, -275, 489, 489, + 489, 489, 489, 489, 489, 489, 489, 489, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, - -275, -275, -275, -275, -275, -275, -275, -275, -275, 415, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, @@ -5152,15 +5313,15 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -276, -276, -276, -276, -276, -276, -276, -276, -276, + 69, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, + -276, -276, 490, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, - -276, 416, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, @@ -5170,27 +5331,27 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -277, -277, -277, -277, -277, -277, -277, -277, -277, - -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, - -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, + 69, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, - -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, - -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, - -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, - -277, -277, -277, 417, -277, -277, -277, -277, -277, -277, - - -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, + -277, -277, 491, -277, -277, -277, -277, -277, -277, -277, + -277, -277, -277, -277, -277, -277, -277, -277, 492, 492, + 492, 492, 492, 492, 492, 492, 492, 492, -277, -277, + -277, -277, -277, -277, -277, 491, 491, 491, 491, 491, + 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, + 491, 491, 491, 491, 491, 491, 491, 491, 491, 491, + + 491, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277 }, { - 67, -278, -278, -278, -278, -278, -278, -278, -278, -278, + 69, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, - -278, -278, 418, -278, -278, -278, -278, -278, -278, -278, + -278, -278, 493, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, @@ -5204,17 +5365,17 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -279, -279, -279, -279, -279, -279, -279, -279, -279, + 69, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, - -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, - -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, - -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, - -279, -279, -279, -279, -279, -279, -279, 419, -279, -279, - -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, - -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, - -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, + -279, -279, 494, -279, -279, -279, -279, -279, -279, -279, + -279, -279, -279, -279, -279, -279, -279, -279, 495, 495, + 495, 495, 495, 495, 495, 495, 495, 495, -279, -279, + -279, -279, -279, -279, -279, 494, 494, 494, 494, 494, + 494, 494, 494, 494, 494, 494, 494, 494, 494, 494, + 494, 494, 494, 494, 494, 494, 494, 494, 494, 494, + 494, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279 @@ -5222,14 +5383,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -280, -280, -280, -280, -280, -280, -280, -280, -280, + 69, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, + -280, -280, 496, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, - -280, -280, -280, 420, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, @@ -5239,16 +5400,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -281, -281, -281, -281, -281, -281, -281, -281, -281, - -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, - -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, - -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, + 69, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, + -281, -281, 497, -281, -281, -281, -281, -281, -281, -281, + -281, -281, -281, -281, -281, -281, -281, -281, 498, 498, + 498, 498, 498, 498, 498, 498, 498, 498, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, - 421, -281, -281, -281, -281, -281, -281, -281, -281, -281, + -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, @@ -5256,16 +5417,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -282, -282, -282, -282, -282, -282, -282, -282, -282, - -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, + 69, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, + -282, -282, 499, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, - 422, -282, -282, -282, -282, -282, -282, -282, -282, -282, + -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, @@ -5273,15 +5434,15 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -283, -283, -283, -283, -283, -283, -283, -283, -283, + 69, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, + -283, -283, 500, -283, -283, -283, -283, -283, -283, -283, + -283, -283, -283, -283, -283, -283, -283, -283, 501, 501, + 501, 501, 501, 501, 501, 501, 501, 501, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, - -283, -283, -283, -283, -283, 423, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, - -283, -283, -283, -283, -283, 424, -283, -283, -283, -283, - -283, -283, -283, -283, -283, -283, -283, -283, -283, 425, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, @@ -5291,14 +5452,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -284, -284, -284, -284, -284, -284, -284, -284, -284, + 69, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, + -284, -284, 284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, - -284, 426, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, @@ -5308,15 +5469,15 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -285, -285, -285, -285, -285, -285, -285, -285, -285, - -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, + 69, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, + -285, -285, -285, -285, -285, -285, -285, -285, 285, 285, + 285, 285, 285, 285, 285, 285, 285, 285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, - -285, -285, -285, -285, 427, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, @@ -5325,15 +5486,15 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -286, -286, -286, -286, -286, -286, -286, -286, -286, + 69, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, + -286, -286, -286, -286, -286, -286, -286, -286, 288, 288, + 288, 288, 288, 288, 288, 288, 288, 288, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, - -286, -286, -286, -286, -286, -286, -286, -286, -286, 428, - -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, @@ -5343,82 +5504,82 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -287, -287, -287, -287, -287, -287, -287, -287, -287, - -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, + 69, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, + -287, -287, -287, -287, -287, -287, 289, -287, 290, 290, + 290, 290, 290, 290, 290, 290, 290, 290, -287, -287, + -287, -287, -287, -287, -287, -287, -287, -287, 291, 291, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, - -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, - -287, -287, -287, -287, 429, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, - -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, + 291, 291, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287 }, { - 67, -288, -288, -288, -288, -288, -288, -288, -288, -288, - -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, - -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, + 69, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, + -288, -288, -288, -288, -288, -288, -288, -288, 288, 288, + 288, 288, 288, 288, 288, 288, 288, 288, -288, -288, + -288, -288, -288, -288, -288, -288, -288, -288, 291, 291, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, - -288, 430, -288, -288, -288, -288, -288, -288, -288, -288, - -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, + 291, 291, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288 }, { - 67, -289, -289, -289, -289, -289, -289, -289, -289, -289, + 69, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, + -289, -289, -289, -289, -289, -289, -289, -289, 502, 502, + 502, 502, 502, 502, 502, 502, 502, 502, -289, -289, + -289, -289, -289, -289, -289, -289, -289, -289, 291, 291, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, - -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, - -289, -289, -289, -289, -289, -289, 431, -289, -289, -289, - -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, - -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, + 291, 291, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289 }, { - 67, -290, -290, -290, -290, -290, -290, -290, -290, -290, - -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, - -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, + 69, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, + -290, -290, -290, -290, -290, -290, 289, -290, 290, 290, + 290, 290, 290, 290, 290, 290, 290, 290, -290, -290, + -290, -290, -290, -290, -290, -290, -290, -290, 291, 291, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, - -290, -290, -290, 432, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, - -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, + 291, 291, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290 }, { - 67, -291, -291, -291, -291, -291, -291, -291, -291, -291, - -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, + 69, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, + -291, -291, -291, 503, -291, 503, -291, -291, 504, 504, + 504, 504, 504, 504, 504, 504, 504, 504, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, - -291, -291, -291, -291, -291, -291, -291, 433, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, @@ -5429,15 +5590,15 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -292, -292, -292, -292, -292, -292, -292, -292, -292, + 69, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, + -292, -292, -292, -292, -292, -292, -292, -292, 294, 294, + 294, 294, 294, 294, 294, 294, 294, 294, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, - -292, -292, -292, -292, -292, -292, -292, 434, -292, -292, - -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, @@ -5446,85 +5607,85 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -293, -293, -293, -293, -293, -293, -293, -293, -293, + 69, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, + -293, -293, -293, -293, -293, -293, 295, -293, 296, 296, + 296, 296, 296, 296, 296, 296, 296, 296, -293, -293, + -293, -293, -293, -293, -293, -293, -293, -293, 297, 297, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, - -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, - -293, -293, -293, 435, -293, -293, -293, -293, -293, -293, - -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, - -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, + 297, 297, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293 }, { - 67, -294, -294, -294, -294, -294, -294, -294, -294, -294, + 69, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, + -294, -294, -294, -294, -294, -294, -294, -294, 294, 294, + 294, 294, 294, 294, 294, 294, 294, 294, -294, -294, + -294, -294, -294, -294, -294, -294, -294, -294, 297, 297, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, - -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, - -294, -294, -294, -294, -294, -294, -294, -294, -294, 436, - -294, -294, -294, -294, -294, -294, 437, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, - -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, + 297, 297, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294 }, { - 67, -295, -295, -295, -295, -295, -295, -295, -295, -295, - -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, + 69, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, + -295, -295, -295, -295, -295, -295, -295, -295, 505, 505, - -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, - -295, -295, -295, -295, -295, 438, -295, -295, -295, -295, - -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, + 505, 505, 505, 505, 505, 505, 505, 505, -295, -295, + -295, -295, -295, -295, -295, -295, -295, -295, 297, 297, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, + 297, 297, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295 }, { - 67, -296, -296, -296, -296, -296, -296, -296, -296, -296, + 69, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, + -296, -296, -296, -296, -296, -296, 295, -296, 296, 296, + 296, 296, 296, 296, 296, 296, 296, 296, -296, -296, + -296, -296, -296, -296, -296, -296, -296, -296, 297, 297, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, - -296, -296, -296, -296, -296, -296, -296, -296, 439, -296, - -296, -296, -296, -296, -296, -296, -296, -296, -296, 440, - 441, -296, -296, -296, -296, -296, 442, -296, -296, -296, - -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, + 297, 297, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296 }, { - 67, -297, -297, -297, -297, -297, -297, -297, -297, -297, + 69, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, + -297, -297, -297, 506, -297, 506, -297, -297, 507, 507, + 507, 507, 507, 507, 507, 507, 507, 507, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, - -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, - -297, -297, -297, -297, -297, -297, -297, -297, -297, 443, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, @@ -5533,32 +5694,32 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -298, -298, -298, -298, -298, -298, -298, -298, -298, - -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, - -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, - -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, - -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, - -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, + 69, 298, 298, 298, 298, 298, 298, 298, 298, 298, + 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, + 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, + 298, 298, 298, 298, 298, 298, 298, 298, 298, 299, + 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, + 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, - -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, - -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, - -298, -298, -298, -298, -298, -298, -298, -298, -298, 444, - -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, - -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, - -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, - -298, -298, -298, -298, -298, -298, -298, -298 + 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, + 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, + 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, + 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, + 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, + 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, + 298, 298, 298, 298, 298, 298, 298, 298 }, { - 67, -299, -299, -299, -299, -299, -299, -299, -299, -299, + 69, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, + -299, -299, -299, -299, -299, -299, -299, -299, -299, 298, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, - -299, -299, -299, -299, -299, -299, -299, -299, 445, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, @@ -5568,24 +5729,24 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -300, -300, -300, -300, -300, -300, -300, -300, -300, - -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, - -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, - -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, - -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, - -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, - -300, -300, -300, -300, -300, -300, -300, -300, -300, 446, - -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, - -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, - -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, + 69, 300, 300, 300, 300, 300, 300, 300, 300, 300, + 301, 300, 300, 300, 300, 300, 300, 300, 300, 300, + 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, + 300, 300, 302, 300, 300, 300, 300, 300, 300, 300, + 300, 300, 300, 300, 300, 300, 300, 303, 300, 300, + 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, + 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, + 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, + 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, + 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, - -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, - -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, - -300, -300, -300, -300, -300, -300, -300, -300 + 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, + 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, + 300, 300, 300, 300, 300, 300, 300, 300 }, { - 67, -301, -301, -301, -301, -301, -301, -301, -301, -301, + 69, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, @@ -5602,59 +5763,59 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -302, -302, -302, -302, -302, -302, -302, -302, -302, - -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, - -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, - -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, + 69, 508, 508, 508, 508, 508, 508, 508, 508, 508, + 301, 508, 508, 508, 508, 508, 508, 508, 508, 508, + 508, 508, 508, 508, 508, 508, 508, 508, 508, 508, + 508, 508, 302, 508, 508, 508, 508, 508, 508, 508, - -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, - -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, - -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, - -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, - -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, - -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, - -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, - -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, - -302, -302, -302, -302, -302, -302, -302, -302 + 508, 508, 508, 508, 508, 508, 508, 303, 508, 508, + 508, 508, 508, 508, 508, 508, 508, 508, 508, 508, + 508, 508, 508, 508, 508, 508, 508, 508, 508, 508, + 508, 508, 508, 508, 508, 508, 508, 508, 508, 508, + 508, 508, 508, 508, 508, 508, 508, 508, 508, 508, + 508, 508, 508, 508, 508, 508, 508, 508, 508, 508, + 508, 508, 508, 508, 508, 508, 508, 508, 508, 508, + 508, 508, 508, 508, 508, 508, 508, 508, 508, 508, + 508, 508, 508, 508, 508, 508, 508, 508 }, { - 67, -303, -303, -303, -303, -303, -303, -303, -303, -303, + 69, 509, 509, 509, 509, 509, 509, 509, 509, 509, - -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, - -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, - -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, - -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, - -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, - -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, - -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, - -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, - -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, - -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, + 510, 509, 509, 509, 509, 509, 509, 509, 509, 509, + 509, 509, 509, 509, 509, 509, 509, 509, 509, 509, + 509, 509, 511, 509, 509, 509, 509, 509, 509, 509, + 509, 509, 509, 509, 509, 509, 509, 512, 509, 509, + 509, 509, 509, 509, 509, 509, 509, 509, 509, 509, + 509, 509, 509, 509, 509, 509, 509, 509, 509, 509, + 509, 509, 509, 509, 509, 509, 509, 509, 509, 509, + 509, 509, 509, 509, 509, 509, 509, 509, 509, 509, + 509, 509, 509, 509, 509, 509, 509, 509, 509, 509, + 509, 509, 509, 509, 509, 509, 509, 509, 509, 509, - -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, - -303, -303, -303, -303, -303, -303, -303, -303 + 509, 509, 509, 509, 509, 509, 509, 509, 509, 509, + 509, 509, 509, 509, 509, 509, 509, 509 }, { - 67, -304, -304, -304, -304, -304, -304, -304, -304, -304, - -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, - -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, - -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, - -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, - -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, - -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, - -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, + 69, 513, 513, 513, 513, 513, 513, 513, 513, 513, + 514, 513, 513, 513, 513, 513, 513, 513, 513, 513, + 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, + 513, 513, 515, 513, 513, 513, 513, 513, 513, 513, + 513, 513, 513, 513, 513, 513, 513, 516, 513, 513, + 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, + 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, + 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, - -304, -304, -304, 447, -304, -304, -304, -304, -304, -304, - -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, - -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, - -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, - -304, -304, -304, -304, -304, -304, -304, -304 + 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, + 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, + 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, + 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, + 513, 513, 513, 513, 513, 513, 513, 513 }, { - 67, -305, -305, -305, -305, -305, -305, -305, -305, -305, + 69, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, @@ -5662,7 +5823,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, - -305, 448, -305, -305, -305, -305, -305, -305, -305, -305, + -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, @@ -5671,59 +5832,59 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -306, -306, -306, -306, -306, -306, -306, -306, -306, - -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, + 69, 517, 517, 517, 517, 517, 517, 517, 517, 517, + 518, 517, 517, 517, 517, 517, 517, 517, 517, 517, - -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, - -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, - -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, - -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, - -306, -306, -306, -306, -306, 449, -306, -306, -306, -306, - -306, -306, -306, -306, -306, -306, -306, -306, -306, 450, - -306, -306, 451, -306, -306, -306, -306, -306, -306, -306, - -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, - -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, - -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, + 517, 517, 517, 517, 517, 517, 517, 517, 517, 517, + 517, 517, 519, 517, 517, 517, 517, 517, 517, 517, + 517, 517, 517, 517, 517, 517, 517, 520, 517, 517, + 517, 517, 517, 517, 517, 517, 517, 517, 517, 517, + 517, 517, 517, 517, 517, 517, 517, 517, 517, 517, + 517, 517, 517, 517, 517, 517, 517, 517, 517, 517, + 517, 517, 517, 517, 517, 517, 517, 517, 517, 517, + 517, 517, 517, 517, 517, 517, 517, 517, 517, 517, + 517, 517, 517, 517, 517, 517, 517, 517, 517, 517, + 517, 517, 517, 517, 517, 517, 517, 517, 517, 517, - -306, -306, -306, -306, -306, -306, -306, -306 + 517, 517, 517, 517, 517, 517, 517, 517 }, { - 67, -307, -307, -307, -307, -307, -307, -307, -307, -307, - -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, - -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, - -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, - -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, - -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, - -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, - -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, - -307, -307, -307, -307, -307, 452, -307, -307, -307, -307, + 69, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 309, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 310, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 308, 308, 308, 308, 308, 311, 308, 308, + 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, - -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, - -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, - -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, - -307, -307, -307, -307, -307, -307, -307, -307 + 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 308, 308, 308, 308, 308, 308 }, { - 67, -308, -308, -308, -308, -308, -308, -308, -308, -308, - -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, - -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, - -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, - -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, - -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, + 69, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 309, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 310, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 308, 308, 308, 308, 308, 311, 308, 308, + 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, - -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, - -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, - -308, -308, -308, 453, -308, -308, -308, -308, -308, -308, - -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, - -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, - -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, - -308, -308, -308, -308, -308, -308, -308, -308 + 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 308, 308, 308, 308, 308, 308 }, { - 67, -309, -309, -309, -309, -309, -309, -309, -309, -309, + 69, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, @@ -5732,7 +5893,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, - -309, -309, -309, -309, -309, -309, -309, -309, 454, -309, + -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, @@ -5741,58 +5902,58 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -310, -310, -310, -310, -310, -310, -310, -310, -310, - -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, - -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, - -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, - -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, - -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, - -310, -310, -310, -310, -310, 455, -310, -310, -310, -310, - -310, -310, -310, -310, -310, -310, -310, -310, 456, -310, - -310, -310, -310, -310, 457, -310, -310, -310, 458, -310, - -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, + 69, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 309, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 310, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 308, 308, 308, 308, 308, 311, 308, 308, + 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, - -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, - -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, - -310, -310, -310, -310, -310, -310, -310, -310 + 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 308, 308, 308, 308, 308, 308 }, { - 67, -311, -311, -311, -311, -311, -311, -311, -311, -311, - -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, - -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, - -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, - -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, - -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, - -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, + 69, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 309, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 310, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 308, 308, 308, 308, 308, 311, 308, 308, + 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, - -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, - -311, -311, -311, -311, -311, 459, -311, -311, -311, -311, - -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, - -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, - -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, - -311, -311, -311, -311, -311, -311, -311, -311 + 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 308, 308, 308, 308, 308, 308 }, { - 67, -312, -312, -312, -312, -312, -312, -312, -312, -312, - -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, - -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, - -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, + 69, 312, 312, 312, 312, 312, 312, 312, 312, 312, + 313, 312, 312, 312, 312, 312, 312, 312, 312, 312, + 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, + 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, - -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, - -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, - -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, - -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, - -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, - -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, - -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, - -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, - -312, -312, -312, -312, -312, -312, -312, -312 + 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, + 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, + 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, + 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, + 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, + 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, + 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, + 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, + 312, 312, 312, 312, 312, 312, 312, 312 }, { - 67, -313, -313, -313, -313, -313, -313, -313, -313, -313, + 69, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, @@ -5810,27 +5971,27 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -314, -314, -314, -314, -314, -314, -314, -314, -314, - -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, - -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, - -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, - -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, - -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, - -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, - -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, + 69, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 315, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, - -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, - -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, - -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, - -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, - -314, -314, -314, -314, -314, -314, -314, -314 + 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 314, 314, 314, 314 }, { - 67, -315, -315, -315, -315, -315, -315, -315, -315, -315, + 69, -315, -315, -315, -315, -315, -315, -315, -315, -315, + -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, - -315, -315, 460, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, @@ -5844,49 +6005,49 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -316, -316, -316, -316, -316, -316, -316, -316, -316, - -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, + 69, 316, 316, 316, 316, 316, 316, 316, 316, 316, + 317, 316, 316, 316, 316, 316, 316, 316, 316, 316, - -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, - -316, -316, 461, -316, -316, -316, -316, -316, -316, -316, - -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, - -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, - -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, - -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, - -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, - -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, - -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, - -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, + 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, + 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, + 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, + 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, + 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, + 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, + 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, + 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, + 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, + 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, - -316, -316, -316, -316, -316, -316, -316, -316 + 316, 316, 316, 316, 316, 316, 316, 316 }, { - 67, -317, -317, -317, -317, -317, -317, -317, -317, -317, + 69, -317, -317, -317, -317, -317, -317, -317, -317, -317, + -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, + -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, + -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, + -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, - -317, -317, 462, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, - -317, -317, -317, -317, -317, 462, 462, 462, 462, 462, - 462, 462, 462, 462, 462, 462, 462, 462, 462, 462, - 462, 462, 462, 462, 462, 462, 462, 462, 462, 462, - 462, -317, -317, -317, -317, -317, -317, -317, -317, -317, + -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317 }, { - 67, -318, -318, -318, -318, -318, -318, -318, -318, -318, + 69, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, - -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, + -318, -318, -318, -318, -318, -318, -318, -318, 521, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, @@ -5896,14 +6057,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -319, -319, -319, -319, -319, -319, -319, -319, -319, + 69, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, - -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, + -319, -319, -319, -319, -319, -319, -319, 522, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, @@ -5914,14 +6075,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -320, -320, -320, -320, -320, -320, -320, -320, -320, - -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, - -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, + 69, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, + -320, -320, -320, -320, -320, -320, -320, -320, -320, 523, + -320, -320, -320, -320, -320, -320, 524, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, @@ -5931,13 +6092,13 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -321, -321, -321, -321, -321, -321, -321, -321, -321, - -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, + 69, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, + -321, -321, -321, -321, -321, 525, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, @@ -5948,16 +6109,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -322, -322, -322, -322, -322, -322, -322, -322, -322, + 69, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, + -322, -322, -322, -322, -322, -322, -322, -322, -322, 526, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, - -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, - -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, + -322, -322, 527, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, @@ -5965,16 +6126,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -323, -323, -323, -323, -323, -323, -323, -323, -323, + 69, -323, -323, -323, -323, -323, -323, -323, -323, -323, - -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, - -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, - -323, -323, 463, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, + -323, -323, -323, -323, -323, -323, -323, -323, 528, -323, + -323, -323, -323, -323, -323, -323, -323, -323, -323, 529, + 530, -323, -323, -323, -323, -323, 531, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, @@ -5983,16 +6144,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -324, -324, -324, -324, -324, -324, -324, -324, -324, + 69, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, - -324, -324, 464, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, - -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, + + -324, -324, -324, -324, -324, -324, -324, -324, -324, 532, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, @@ -6000,24 +6161,24 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -325, -325, -325, -325, -325, -325, -325, -325, -325, + 69, -325, -325, -325, -325, -325, -325, -325, -325, -325, + -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, - -325, -325, 465, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, - -325, -325, -325, -325, -325, 465, 465, 465, 465, 465, - 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, - 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, - 465, -325, -325, -325, -325, -325, -325, -325, -325, -325, + -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, + -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, + -325, -325, -325, -325, -325, -325, -325, -325, -325, 533, + -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325 }, { - 67, -326, -326, -326, -326, -326, -326, -326, -326, -326, + 69, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, @@ -6025,7 +6186,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, - -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, + -326, -326, -326, -326, -326, -326, -326, -326, 534, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, @@ -6035,8 +6196,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -327, -327, -327, -327, -327, -327, -327, -327, -327, - -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, + 69, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, @@ -6044,6 +6204,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, + 535, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, @@ -6052,7 +6213,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -328, -328, -328, -328, -328, -328, -328, -328, -328, + 69, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, @@ -6069,7 +6230,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -329, -329, -329, -329, -329, -329, -329, -329, -329, + 69, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, @@ -6087,13 +6248,13 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -330, -330, -330, -330, -330, -330, -330, -330, -330, - -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, + 69, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, + -330, -330, -330, -330, -330, 536, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, @@ -6104,16 +6265,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -331, -331, -331, -331, -331, -331, -331, -331, -331, + 69, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, - -331, -331, -331, -331, -331, -331, -331, -331, 466, 466, - 466, 466, 466, 466, 466, 466, 466, 466, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, - -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, + + -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, + -331, -331, -331, 537, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, @@ -6121,34 +6282,34 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -332, -332, -332, -332, -332, -332, -332, -332, -332, + 69, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, - -332, -332, -332, -332, -332, -332, -332, -332, 467, 467, - 467, 467, 467, 467, 467, 467, 467, 467, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, - -332, -332, -332, -332, -332, 468, -332, -332, -332, -332, + -332, -332, -332, -332, -332, -332, -332, -332, -332, 538, + -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, + -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332 }, { - 67, -333, -333, -333, -333, -333, -333, -333, -333, -333, + 69, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, - -333, -333, -333, -333, -333, -333, -333, -333, -333, 469, - 469, 469, 469, 469, 469, 469, 469, 469, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, + -333, -333, -333, -333, 539, -333, -333, -333, -333, -333, + -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, @@ -6156,31 +6317,31 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -334, -334, -334, -334, -334, -334, -334, -334, -334, + 69, -334, -334, -334, -334, -334, -334, -334, -334, -334, + -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, + -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, + -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, + -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, + -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, + -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, + -334, -334, -334, -334, -334, -334, -334, -334, 540, -334, + -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, - -334, -334, 470, -334, -334, -334, -334, -334, -334, -334, - -334, -334, -334, -334, -334, -334, -334, -334, 471, 471, - 471, 471, 471, 471, 471, 471, 471, 471, -334, -334, - -334, -334, -334, -334, -334, 470, 470, 470, 470, 470, - 470, 470, 470, 470, 470, 470, 470, 470, 470, 470, - - 470, 470, 470, 470, 470, 470, 470, 470, 470, 470, - 470, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334 }, { - 67, -335, -335, -335, -335, -335, -335, -335, -335, -335, + 69, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, - -335, -335, 472, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, - -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, + -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, + -335, -335, -335, -335, -335, 541, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, @@ -6190,15 +6351,15 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -336, -336, -336, -336, -336, -336, -336, -336, -336, + 69, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, - -336, -336, 473, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, + -336, -336, -336, -336, -336, -336, 542, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, @@ -6208,49 +6369,49 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -337, -337, -337, -337, -337, -337, -337, -337, -337, + 69, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, - -337, -337, 474, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, - -337, -337, -337, -337, -337, 474, 474, 474, 474, 474, - 474, 474, 474, 474, 474, 474, 474, 474, 474, 474, - 474, 474, 474, 474, 474, 474, 474, 474, 474, 474, + -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, + -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, + -337, -337, -337, -337, -337, -337, -337, 543, -337, -337, + -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, - 474, -337, -337, -337, -337, -337, -337, -337, -337, -337, + -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337 }, { - 67, -338, -338, -338, -338, -338, -338, -338, -338, -338, + 69, -338, -338, -338, -338, -338, -338, -338, -338, -338, + -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, + -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, - -338, -338, -338, -338, -338, -338, -338, -338, 475, 475, - 475, 475, 475, 475, 475, 475, 475, 475, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, + -338, -338, 544, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, - -338, -338, -338, -338, -338, 476, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338 }, { - 67, -339, -339, -339, -339, -339, -339, -339, -339, -339, + 69, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, - -339, -339, -339, -339, -339, -339, -339, -339, -339, 477, - 477, 477, 477, 477, 477, 477, 477, 477, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, + -339, -339, -339, -339, -339, -339, -339, -339, -339, 545, + -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, @@ -6260,16 +6421,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -340, -340, -340, -340, -340, -340, -340, -340, -340, + 69, -340, -340, -340, -340, -340, -340, -340, -340, -340, + -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, - -340, -340, 478, -340, -340, -340, -340, -340, -340, -340, - -340, -340, -340, -340, -340, -340, -340, -340, 479, 479, - 479, 479, 479, 479, 479, 479, 479, 479, -340, -340, - -340, -340, -340, -340, -340, 478, 478, 478, 478, 478, - 478, 478, 478, 478, 478, 478, 478, 478, 478, 478, - 478, 478, 478, 478, 478, 478, 478, 478, 478, 478, - 478, -340, -340, -340, -340, -340, -340, -340, -340, -340, + -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, + -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, + -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, + -340, -340, -340, -340, -340, -340, -340, -340, 546, -340, + -340, -340, -340, -340, 547, -340, -340, -340, -340, -340, + -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, @@ -6277,7 +6438,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -341, -341, -341, -341, -341, -341, -341, -341, -341, + 69, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, @@ -6286,24 +6447,24 @@ static yyconst flex_int16_t yy_nxt[][128] = -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, + -341, -341, -341, -341, 548, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, - -341, -341, -341, -341, -341, 480, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341 }, { - 67, -342, -342, -342, -342, -342, -342, -342, -342, -342, + 69, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, - -342, -342, -342, -342, -342, -342, -342, -342, -342, 481, - 481, 481, 481, 481, 481, 481, 481, 481, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, + -342, -342, -342, 549, -342, -342, -342, -342, -342, -342, + -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, @@ -6311,17 +6472,17 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -343, -343, -343, -343, -343, -343, -343, -343, -343, + 69, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, - -343, -343, 482, -343, -343, -343, -343, -343, -343, -343, - -343, -343, -343, -343, -343, -343, -343, -343, 483, 483, - 483, 483, 483, 483, 483, 483, 483, 483, -343, -343, - -343, -343, -343, -343, -343, 482, 482, 482, 482, 482, - 482, 482, 482, 482, 482, 482, 482, 482, 482, 482, - 482, 482, 482, 482, 482, 482, 482, 482, 482, 482, - 482, -343, -343, -343, -343, -343, -343, -343, -343, -343, + -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, + -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, + -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, + -343, -343, -343, -343, -343, 550, -343, -343, -343, -343, + -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, + -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, + -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, @@ -6329,13 +6490,13 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -344, -344, -344, -344, -344, -344, -344, -344, -344, - -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, + 69, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, + -344, -344, -344, -344, -344, -344, -344, -344, -344, 551, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, @@ -6346,7 +6507,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -345, -345, -345, -345, -345, -345, -345, -345, -345, + 69, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, @@ -6355,7 +6516,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, - -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, + 552, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, @@ -6363,7 +6524,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -346, -346, -346, -346, -346, -346, -346, -346, -346, + 69, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, @@ -6371,7 +6532,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, - -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, + -346, -346, -346, 553, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, @@ -6381,14 +6542,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -347, -347, -347, -347, -347, -347, -347, -347, -347, - -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, + 69, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, + -347, -347, 554, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, @@ -6398,14 +6559,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -348, -348, -348, -348, -348, -348, -348, -348, -348, + 69, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, - -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, + -348, -348, -348, -348, -348, 555, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, @@ -6415,14 +6576,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -349, -349, -349, -349, -349, -349, -349, -349, -349, + 69, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, - -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, + -349, -349, -349, -349, -349, -349, -349, -349, -349, 556, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, @@ -6433,14 +6594,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -350, -350, -350, -350, -350, -350, -350, -350, -350, - -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, + 69, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, + -350, 557, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, @@ -6450,7 +6611,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -351, -351, -351, -351, -351, -351, -351, -351, -351, + 69, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, @@ -6459,7 +6620,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, - -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, + -351, -351, -351, 558, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, @@ -6467,7 +6628,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -352, -352, -352, -352, -352, -352, -352, -352, -352, + 69, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, @@ -6475,7 +6636,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, - -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, + -352, -352, -352, -352, -352, -352, -352, -352, 559, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, @@ -6484,11 +6645,11 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -353, -353, -353, -353, -353, -353, -353, -353, -353, + 69, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, - -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, + -353, -353, 560, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, @@ -6502,13 +6663,13 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -354, -354, -354, -354, -354, -354, -354, -354, -354, - -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, + 69, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, + -354, -354, -354, -354, -354, -354, -354, 561, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, @@ -6519,7 +6680,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -355, -355, -355, -355, -355, -355, -355, -355, -355, + 69, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, @@ -6527,7 +6688,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, - -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, + -355, -355, -355, 562, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, @@ -6536,7 +6697,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -356, -356, -356, -356, -356, -356, -356, -356, -356, + 69, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, @@ -6544,8 +6705,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, - -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, - -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, + -356, -356, -356, -356, -356, -356, -356, -356, 563, -356, + -356, -356, -356, -356, 564, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, @@ -6554,8 +6715,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -357, -357, -357, -357, -357, -357, -357, -357, -357, - -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, + 69, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, @@ -6563,6 +6723,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, + -357, -357, 565, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, @@ -6571,33 +6732,33 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -358, -358, -358, -358, -358, -358, -358, -358, -358, + 69, -358, -358, -358, -358, -358, -358, -358, -358, -358, + -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, + -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, - -358, -358, -358, -358, -358, -358, -358, -358, 484, 484, - 484, 484, 484, 484, 484, 484, 484, 484, -358, -358, + -358, -358, -358, -358, -358, -358, -358, -358, -358, 566, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, - -358, -358, -358, -358, -358, 485, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358 }, { - 67, -359, -359, -359, -359, -359, -359, -359, -359, -359, + 69, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, - -359, -359, -359, -359, -359, -359, -359, -359, 486, 487, - 487, 487, 487, 487, 487, 487, 487, 487, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, + -359, -359, -359, -359, -359, -359, -359, -359, -359, 567, + -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, @@ -6606,16 +6767,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -360, -360, -360, -360, -360, -360, -360, -360, -360, + 69, -360, -360, -360, -360, -360, -360, -360, -360, -360, + -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, + -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, + -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, - -360, -360, 488, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, - -360, -360, -360, -360, -360, 488, 488, 488, 488, 488, - 488, 488, 488, 488, 488, 488, 488, 488, 488, 488, - 488, 488, 488, 488, 488, 488, 488, 488, 488, 488, - 488, -360, -360, -360, -360, -360, -360, -360, -360, -360, + 568, -360, -360, -360, -360, -360, -360, -360, -360, -360, + -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, @@ -6623,50 +6784,50 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -361, -361, -361, -361, -361, -361, -361, -361, -361, + 69, -361, -361, -361, -361, -361, -361, -361, -361, -361, + -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, + -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, + -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, + -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, + -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, + -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, + -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, + 569, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, - -361, -361, 488, -361, -361, -361, -361, -361, -361, -361, - -361, -361, -361, -361, -361, -361, -361, -361, 489, 489, - 489, 489, 489, 489, 489, 489, 489, 489, -361, -361, - -361, -361, -361, -361, -361, 488, 488, 488, 488, 488, - - 488, 488, 488, 488, 488, 488, 488, 488, 488, 488, - 488, 488, 488, 488, 488, 488, 488, 488, 488, 488, - 488, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361 }, { - 67, -362, -362, -362, -362, -362, -362, -362, -362, -362, + 69, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, - -362, -362, -362, -362, -362, -362, -362, -362, 490, 490, - 490, 490, 490, 490, 490, 490, 490, 490, -362, -362, + -362, -362, -362, -362, -362, 570, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, + -362, -362, -362, -362, -362, 571, -362, -362, -362, -362, + -362, -362, -362, -362, -362, -362, -362, -362, -362, 572, + -362, -362, 573, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, - -362, -362, -362, -362, -362, 491, -362, -362, -362, -362, - -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362 }, { - 67, -363, -363, -363, -363, -363, -363, -363, -363, -363, + 69, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, - -363, -363, -363, -363, -363, -363, -363, -363, 492, 493, - 493, 493, 493, 493, 493, 493, 493, 493, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, + -363, 574, -363, -363, -363, -363, -363, -363, -363, 575, + -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, @@ -6675,51 +6836,51 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -364, -364, -364, -364, -364, -364, -364, -364, -364, + 69, -364, -364, -364, -364, -364, -364, -364, -364, -364, + -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, + -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, + -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, - -364, -364, 494, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, - -364, -364, -364, -364, -364, 494, 494, 494, 494, 494, - 494, 494, 494, 494, 494, 494, 494, 494, 494, 494, - 494, 494, 494, 494, 494, 494, 494, 494, 494, 494, - 494, -364, -364, -364, -364, -364, -364, -364, -364, -364, + 576, -364, -364, -364, -364, -364, -364, -364, -364, -364, + -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364 }, { - 67, -365, -365, -365, -365, -365, -365, -365, -365, -365, + 69, -365, -365, -365, -365, -365, -365, -365, -365, -365, + -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, + -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, - -365, -365, 494, -365, -365, -365, -365, -365, -365, -365, - -365, -365, -365, -365, -365, -365, -365, -365, 495, 495, - 495, 495, 495, 495, 495, 495, 495, 495, -365, -365, - -365, -365, -365, -365, -365, 494, 494, 494, 494, 494, - 494, 494, 494, 494, 494, 494, 494, 494, 494, 494, - 494, 494, 494, 494, 494, 494, 494, 494, 494, 494, - 494, -365, -365, -365, -365, -365, -365, -365, -365, -365, + -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, + -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, + -365, -365, -365, -365, 577, -365, -365, -365, -365, -365, + -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, + -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365 }, { - 67, -366, -366, -366, -366, -366, -366, -366, -366, -366, + 69, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, + -366, -366, -366, -366, -366, -366, -366, -366, -366, 578, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, - -366, -366, -366, -366, -366, 496, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, @@ -6727,15 +6888,15 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -367, -367, -367, -367, -367, -367, -367, -367, -367, + 69, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, - -367, -367, -367, -367, -367, -367, -367, -367, 497, 498, - 498, 498, 498, 498, 498, 498, 498, 498, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, + -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, + -367, -367, -367, -367, 579, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, @@ -6744,34 +6905,34 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -368, -368, -368, -368, -368, -368, -368, -368, -368, + 69, -368, -368, -368, -368, -368, -368, -368, -368, -368, + -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, - -368, -368, 499, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, - -368, -368, -368, -368, -368, 499, 499, 499, 499, 499, - 499, 499, 499, 499, 499, 499, 499, 499, 499, 499, - 499, 499, 499, 499, 499, 499, 499, 499, 499, 499, - 499, -368, -368, -368, -368, -368, -368, -368, -368, -368, + -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, + -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, + -368, 580, -368, -368, -368, -368, -368, -368, -368, -368, + -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368 }, { - 67, -369, -369, -369, -369, -369, -369, -369, -369, -369, + 69, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, - -369, -369, 499, -369, -369, -369, -369, -369, -369, -369, - -369, -369, -369, -369, -369, -369, -369, -369, 500, 500, - 500, 500, 500, 500, 500, 500, 500, 500, -369, -369, - -369, -369, -369, -369, -369, 499, 499, 499, 499, 499, - 499, 499, 499, 499, 499, 499, 499, 499, 499, 499, - 499, 499, 499, 499, 499, 499, 499, 499, 499, 499, - 499, -369, -369, -369, -369, -369, -369, -369, -369, -369, + -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, + -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, + -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, + -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, + -369, -369, -369, -369, -369, -369, -369, -369, 581, -369, + -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, + -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369 @@ -6779,16 +6940,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -370, -370, -370, -370, -370, -370, -370, -370, -370, + 69, -370, -370, -370, -370, -370, -370, -370, -370, -370, + -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, - -370, -370, -370, -370, -370, -370, -370, -370, 501, 501, - 501, 501, 501, 501, 501, 501, 501, 501, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, - -370, -370, -370, -370, -370, 502, -370, -370, -370, -370, + -370, -370, -370, -370, -370, -370, 582, -370, -370, -370, + -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, @@ -6796,16 +6957,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -371, -371, -371, -371, -371, -371, -371, -371, -371, + 69, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, - -371, -371, -371, -371, -371, -371, -371, -371, 503, 504, - 504, 504, 504, 504, 504, 504, 504, 504, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, - -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, + + -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, + -371, -371, -371, 583, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, @@ -6813,34 +6974,34 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -372, -372, -372, -372, -372, -372, -372, -372, -372, + 69, -372, -372, -372, -372, -372, -372, -372, -372, -372, + -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, - -372, -372, 505, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, - -372, -372, -372, -372, -372, 505, 505, 505, 505, 505, - 505, 505, 505, 505, 505, 505, 505, 505, 505, 505, - 505, 505, 505, 505, 505, 505, 505, 505, 505, 505, - 505, -372, -372, -372, -372, -372, -372, -372, -372, -372, + -372, -372, -372, -372, -372, -372, -372, 584, -372, -372, + -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, + -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, + -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372 }, { - 67, -373, -373, -373, -373, -373, -373, -373, -373, -373, + 69, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, - -373, -373, 505, -373, -373, -373, -373, -373, -373, -373, - -373, -373, -373, -373, -373, -373, -373, -373, 506, 506, - 506, 506, 506, 506, 506, 506, 506, 506, -373, -373, - -373, -373, -373, -373, -373, 505, 505, 505, 505, 505, - 505, 505, 505, 505, 505, 505, 505, 505, 505, 505, - 505, 505, 505, 505, 505, 505, 505, 505, 505, 505, - 505, -373, -373, -373, -373, -373, -373, -373, -373, -373, + -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, + -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, + -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, + -373, -373, -373, -373, -373, -373, -373, 585, -373, -373, + -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, + -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, + -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, @@ -6848,7 +7009,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -374, -374, -374, -374, -374, -374, -374, -374, -374, + 69, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, @@ -6857,23 +7018,23 @@ static yyconst flex_int16_t yy_nxt[][128] = -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, + -374, -374, -374, 586, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, - -374, -374, -374, -374, -374, 507, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374 }, { - 67, -375, -375, -375, -375, -375, -375, -375, -375, -375, + 69, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, - -375, -375, -375, -375, -375, -375, -375, -375, 508, 509, - - 509, 509, 509, 509, 509, 509, 509, 509, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, + -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, + -375, -375, -375, -375, -375, -375, -375, -375, -375, 587, + -375, -375, -375, -375, -375, -375, 588, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, @@ -6882,17 +7043,17 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -376, -376, -376, -376, -376, -376, -376, -376, -376, + 69, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, - -376, -376, 510, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, - -376, -376, -376, -376, -376, 510, 510, 510, 510, 510, - 510, 510, 510, 510, 510, 510, 510, 510, 510, 510, - 510, 510, 510, 510, 510, 510, 510, 510, 510, 510, - 510, -376, -376, -376, -376, -376, -376, -376, -376, -376, + -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, + -376, -376, -376, -376, -376, 589, -376, -376, -376, -376, + -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, + -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, + -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, @@ -6900,33 +7061,33 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -377, -377, -377, -377, -377, -377, -377, -377, -377, + 69, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, - -377, -377, 510, -377, -377, -377, -377, -377, -377, -377, - -377, -377, -377, -377, -377, -377, -377, -377, 511, 511, - 511, 511, 511, 511, 511, 511, 511, 511, -377, -377, - -377, -377, -377, -377, -377, 510, 510, 510, 510, 510, - 510, 510, 510, 510, 510, 510, 510, 510, 510, 510, - 510, 510, 510, 510, 510, 510, 510, 510, 510, 510, - - 510, -377, -377, -377, -377, -377, -377, -377, -377, -377, + -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, + -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, + -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, + -377, -377, -377, -377, -377, -377, -377, -377, -377, 590, + -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, + -377, -377, 591, -377, -377, -377, -377, -377, -377, -377, + + -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377 }, { - 67, -378, -378, -378, -378, -378, -378, -378, -378, -378, + 69, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, - -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, - -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, - -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, + -378, -378, -378, -378, -378, -378, -378, -378, 592, -378, + -378, -378, -378, -378, -378, -378, -378, -378, -378, 593, + 594, -378, -378, -378, -378, -378, 595, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, @@ -6934,16 +7095,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -379, -379, -379, -379, -379, -379, -379, -379, -379, + 69, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, - -379, -379, 512, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, + -379, -379, -379, -379, -379, -379, -379, -379, -379, 596, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, @@ -6952,15 +7113,15 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -380, -380, -380, -380, -380, -380, -380, -380, -380, + 69, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, - -380, -380, 513, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, + -380, -380, -380, -380, -380, -380, -380, -380, -380, 597, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, @@ -6969,24 +7130,24 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -381, -381, -381, -381, -381, -381, -381, -381, -381, + 69, -381, -381, -381, -381, -381, -381, -381, -381, -381, + -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, + -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, - -381, -381, 514, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, - -381, -381, -381, -381, -381, 514, 514, 514, 514, 514, - 514, 514, 514, 514, 514, 514, 514, 514, 514, 514, - 514, 514, 514, 514, 514, 514, 514, 514, 514, 514, - 514, -381, -381, -381, -381, -381, -381, -381, -381, -381, + -381, -381, -381, -381, -381, -381, -381, -381, 598, -381, + -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, + -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381 }, { - 67, -382, -382, -382, -382, -382, -382, -382, -382, -382, + 69, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, @@ -6995,7 +7156,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, - -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, + 599, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, @@ -7003,14 +7164,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -383, -383, -383, -383, -383, -383, -383, -383, -383, + 69, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, - -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, + -383, -383, -383, -383, -383, 600, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, @@ -7021,13 +7182,13 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -384, -384, -384, -384, -384, -384, -384, -384, -384, - -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, + 69, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, + -384, -384, -384, -384, -384, -384, -384, -384, -384, 601, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, @@ -7038,16 +7199,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -385, -385, -385, -385, -385, -385, -385, -385, -385, + 69, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, - -385, -385, 515, -385, -385, -385, -385, -385, -385, -385, - -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, - -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, + -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, + -385, -385, -385, -385, -385, -385, -385, -385, -385, 602, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, + -385, -385, 603, 604, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, @@ -7055,11 +7216,11 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -386, -386, -386, -386, -386, -386, -386, -386, -386, + 69, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, - -386, -386, 516, -386, -386, -386, -386, -386, -386, -386, + -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, @@ -7073,12 +7234,12 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -387, -387, -387, -387, -387, -387, -387, -387, -387, + 69, -387, -387, -387, -387, -387, -387, -387, -387, -387, + -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, + -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, + -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, - -387, -387, 517, -387, -387, -387, -387, -387, -387, -387, - -387, -387, -387, -387, -387, -387, -387, -387, 518, 518, - 518, 518, 518, 518, 518, 518, 518, 518, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, @@ -7090,7 +7251,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -388, -388, -388, -388, -388, -388, -388, -388, -388, + 69, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, @@ -7107,7 +7268,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -389, -389, -389, -389, -389, -389, -389, -389, -389, + 69, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, @@ -7115,7 +7276,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, - -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, + -389, -389, -389, 605, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, @@ -7125,14 +7286,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -390, -390, -390, -390, -390, -390, -390, -390, -390, - -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, + 69, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, + 606, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, @@ -7142,32 +7303,32 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -391, -391, -391, -391, -391, -391, -391, -391, -391, + 69, -391, -391, -391, -391, -391, -391, -391, -391, -391, + -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, + -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, + -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, - -391, -391, -391, -391, -391, -391, -391, -391, 391, 391, - 391, 391, 391, 391, 391, 391, 391, 391, -391, -391, - -391, -391, -391, -391, -391, -391, -391, -391, -391, 246, + -391, -391, -391, -391, -391, -391, -391, -391, -391, 607, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, - -391, 246, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391 }, { - 67, -392, -392, -392, -392, -392, -392, -392, -392, -392, + 69, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, - -392, -392, -392, -392, -392, -392, -392, -392, 393, 393, - 393, 393, 393, 393, 393, 393, 393, 393, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, + -392, -392, -392, -392, -392, 608, -392, -392, -392, -392, + -392, -392, -392, -392, -392, -392, -392, -392, -392, 609, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, @@ -7176,17 +7337,17 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -393, -393, -393, -393, -393, -393, -393, -393, -393, + 69, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, - -393, -393, -393, -393, -393, -393, -393, -393, 393, 393, - 393, 393, 393, 393, 393, 393, 393, 393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, + -393, -393, -393, 610, -393, -393, -393, -393, -393, -393, + -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, @@ -7194,14 +7355,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -394, -394, -394, -394, -394, -394, -394, -394, -394, - -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, + 69, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, + -394, 611, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, @@ -7211,16 +7372,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -395, -395, -395, -395, -395, -395, -395, -395, -395, + 69, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, - -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, - -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, - -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, + -395, -395, -395, -395, -395, 612, -395, -395, -395, -395, + -395, -395, -395, -395, -395, -395, -395, -395, -395, 613, + -395, -395, 614, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, @@ -7228,7 +7389,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -396, -396, -396, -396, -396, -396, -396, -396, -396, + 69, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, @@ -7237,7 +7398,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, - -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, + -396, -396, -396, -396, -396, 615, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, @@ -7246,7 +7407,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -397, -397, -397, -397, -397, -397, -397, -397, -397, + 69, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, @@ -7254,7 +7415,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, - -397, -397, -397, -397, 519, -397, -397, -397, -397, -397, + -397, -397, -397, 616, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, @@ -7263,7 +7424,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -398, -398, -398, -398, -398, -398, -398, -398, -398, + 69, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, @@ -7271,8 +7432,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, - -398, -398, -398, -398, -398, -398, -398, 520, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, + -398, -398, -398, -398, -398, -398, -398, -398, 617, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, @@ -7280,16 +7441,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -399, -399, -399, -399, -399, -399, -399, -399, -399, + 69, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, - -399, -399, -399, -399, -399, -399, -399, -399, -399, 521, - -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, - -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, + -399, -399, -399, -399, -399, 618, -399, -399, -399, -399, + -399, -399, -399, -399, -399, -399, -399, -399, 619, -399, + -399, -399, -399, -399, 620, -399, -399, -399, 621, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, @@ -7298,7 +7459,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -400, -400, -400, -400, -400, -400, -400, -400, -400, + 69, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, @@ -7306,7 +7467,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, - -400, -400, -400, -400, 522, -400, -400, -400, -400, -400, + -400, -400, -400, 622, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, @@ -7315,7 +7476,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -401, -401, -401, -401, -401, -401, -401, -401, -401, + 69, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, @@ -7324,7 +7485,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, - -401, -401, -401, -401, -401, -401, -401, -401, 523, -401, + -401, -401, -401, -401, -401, 623, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, @@ -7332,7 +7493,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -402, -402, -402, -402, -402, -402, -402, -402, -402, + 69, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, @@ -7340,7 +7501,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, - -402, -402, -402, -402, -402, -402, 524, -402, -402, -402, + -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, @@ -7349,14 +7510,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -403, -403, -403, -403, -403, -403, -403, -403, -403, + 69, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, - -403, -403, -403, -403, -403, -403, -403, -403, -403, 525, + -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, @@ -7367,7 +7528,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -404, -404, -404, -404, -404, -404, -404, -404, -404, + 69, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, @@ -7376,7 +7537,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, - 526, -404, -404, -404, -404, -404, -404, -404, -404, -404, + -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, @@ -7384,7 +7545,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -405, -405, -405, -405, -405, -405, -405, -405, -405, + 69, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, @@ -7392,7 +7553,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, - -405, -405, -405, 527, -405, -405, -405, -405, -405, -405, + -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, @@ -7401,7 +7562,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -406, -406, -406, -406, -406, -406, -406, -406, -406, + 69, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, @@ -7410,7 +7571,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, - -406, -406, -406, -406, 528, -406, -406, -406, -406, -406, + -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, @@ -7419,13 +7580,13 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -407, -407, -407, -407, -407, -407, -407, -407, -407, + 69, -407, -407, -407, -407, -407, -407, -407, -407, -407, + -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, - -407, -407, -407, -407, -407, -407, -407, -407, -407, 529, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, @@ -7436,7 +7597,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -408, -408, -408, -408, -408, -408, -408, -408, -408, + 69, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, @@ -7445,7 +7606,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, - -408, -408, 530, -408, -408, -408, -408, -408, -408, -408, + -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, @@ -7453,14 +7614,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -409, -409, -409, -409, -409, -409, -409, -409, -409, + 69, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, + -409, -409, 624, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, - -409, -409, -409, -409, -409, 531, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, @@ -7471,15 +7632,15 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -410, -410, -410, -410, -410, -410, -410, -410, -410, + 69, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, + -410, -410, 625, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, - -410, -410, -410, -410, -410, -410, -410, -410, 532, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, @@ -7488,24 +7649,24 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -411, -411, -411, -411, -411, -411, -411, -411, -411, - -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, - -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, + 69, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, + -411, -411, 626, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, + -411, -411, -411, -411, -411, 626, 626, 626, 626, 626, - -411, -411, -411, -411, -411, -411, 533, -411, -411, -411, - -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, - -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, + 626, 626, 626, 626, 626, 626, 626, 626, 626, 626, + 626, 626, 626, 626, 626, 626, 626, 626, 626, 626, + 626, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, -411 }, { - 67, -412, -412, -412, -412, -412, -412, -412, -412, -412, + 69, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, @@ -7514,7 +7675,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, - -412, -412, 534, -412, -412, -412, -412, -412, -412, -412, + -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, @@ -7522,14 +7683,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -413, -413, -413, -413, -413, -413, -413, -413, -413, + 69, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, - -413, -413, -413, -413, -413, -413, -413, -413, -413, 535, + -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, @@ -7540,7 +7701,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -414, -414, -414, -414, -414, -414, -414, -414, -414, + 69, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, @@ -7549,7 +7710,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, - -414, -414, -414, -414, 536, -414, -414, -414, -414, -414, + -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, @@ -7557,11 +7718,11 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -415, -415, -415, -415, -415, -415, -415, -415, -415, + 69, -415, -415, -415, -415, -415, -415, -415, -415, -415, + -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, - -415, -415, -415, -415, -415, 537, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, @@ -7574,7 +7735,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -416, -416, -416, -416, -416, -416, -416, -416, -416, + 69, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, @@ -7592,12 +7753,12 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -417, -417, -417, -417, -417, -417, -417, -417, -417, + 69, -417, -417, -417, -417, -417, -417, -417, -417, -417, + -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, + -417, -417, 627, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, - -417, -417, -417, -417, -417, -417, -417, -417, -417, 538, - 538, 538, 538, 538, 538, 538, 538, 538, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, @@ -7609,10 +7770,10 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -418, -418, -418, -418, -418, -418, -418, -418, -418, + 69, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, - -418, -418, 539, -418, -418, -418, -418, -418, -418, -418, + -418, -418, 628, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, @@ -7626,17 +7787,17 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -419, -419, -419, -419, -419, -419, -419, -419, -419, + 69, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, + -419, -419, 629, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, - -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, - -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, - -419, -419, 540, -419, -419, -419, -419, -419, -419, -419, - -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, - -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, + -419, -419, -419, -419, -419, 629, 629, 629, 629, 629, + 629, 629, 629, 629, 629, 629, 629, 629, 629, 629, + 629, 629, 629, 629, 629, 629, 629, 629, 629, 629, + 629, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419 @@ -7644,14 +7805,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -420, -420, -420, -420, -420, -420, -420, -420, -420, + 69, -420, -420, -420, -420, -420, -420, -420, -420, -420, + -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, - -420, -420, -420, -420, -420, -420, -420, -420, 541, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, @@ -7661,7 +7822,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -421, -421, -421, -421, -421, -421, -421, -421, -421, + 69, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, @@ -7669,7 +7830,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, - -421, -421, -421, -421, -421, -421, -421, -421, -421, 542, + -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, @@ -7678,7 +7839,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -422, -422, -422, -422, -422, -422, -422, -422, -422, + 69, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, @@ -7686,7 +7847,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, - -422, -422, -422, -422, -422, -422, -422, -422, -422, 543, + -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, @@ -7695,15 +7856,15 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -423, -423, -423, -423, -423, -423, -423, -423, -423, + 69, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, - -423, -423, -423, -423, -423, 544, -423, -423, -423, -423, - -423, -423, -423, -423, -423, -423, -423, -423, -423, 545, + -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, + -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, @@ -7713,7 +7874,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -424, -424, -424, -424, -424, -424, -424, -424, -424, + 69, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, @@ -7730,34 +7891,34 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -425, -425, -425, -425, -425, -425, -425, -425, -425, - -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, - -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, - -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, - -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, - - -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, - -425, -425, -425, -425, -425, -425, 546, -425, -425, -425, - -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, + 69, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, + -425, -425, 630, -425, -425, -425, -425, -425, -425, -425, + -425, -425, -425, -425, -425, -425, -425, -425, 631, 631, + + 631, 631, 631, 631, 631, 631, 631, 631, -425, -425, + -425, -425, -425, -425, -425, 630, 630, 630, 630, 630, + 630, 630, 630, 630, 630, 630, 630, 630, 630, 630, + 630, 630, 630, 630, 630, 630, 630, 630, 630, 630, + 630, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425 }, { - 67, -426, -426, -426, -426, -426, -426, -426, -426, -426, + 69, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, + -426, -426, -426, -426, -426, -426, -426, -426, 632, 632, + 632, 632, 632, 632, 632, 632, 632, 632, -426, -426, + -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, - -426, -426, -426, -426, -426, -426, -426, -426, -426, 547, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, - -426, -426, -426, -426, -426, -426, -426, -426, 548, 549, - 550, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, @@ -7765,50 +7926,50 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -427, -427, -427, -427, -427, -427, -427, -427, -427, - -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, + 69, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, + -427, -427, -427, -427, -427, 633, -427, -427, 634, 635, + 635, 635, 635, 635, 635, 635, 635, 635, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, - 551, -427, -427, -427, -427, -427, -427, -427, -427, -427, - -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, + -427, -427, -427, -427, -427, 636, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427 }, { - 67, -428, -428, -428, -428, -428, -428, -428, -428, -428, - -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, - -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, + 69, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, + -428, -428, -428, -428, -428, 633, -428, -428, 637, 637, + 637, 637, 637, 637, 637, 637, 637, 637, -428, -428, - -428, -428, -428, -428, -428, -428, -428, 552, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, - -428, -428, -428, 553, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, + -428, -428, -428, -428, -428, 638, -428, -428, -428, -428, + -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428 }, { - 67, -429, -429, -429, -429, -429, -429, -429, -429, -429, + 69, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, + -429, -429, -429, -429, -429, -429, -429, -429, 639, 639, + 639, 639, 639, 639, 639, 639, 639, 639, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, - 554, -429, -429, -429, -429, -429, -429, -429, -429, -429, - -429, -429, -429, -429, -429, -429, -429, 555, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, @@ -7817,16 +7978,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -430, -430, -430, -430, -430, -430, -430, -430, -430, - -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, - -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, - -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, + 69, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, + -430, -430, -430, -430, -430, 633, -430, -430, 634, 634, + 634, 634, 634, 634, 634, 634, 634, 634, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, + -430, -430, -430, -430, -430, 640, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, @@ -7834,12 +7995,12 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -431, -431, -431, -431, -431, -431, -431, -431, -431, - -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, - -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, + 69, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, + -431, -431, -431, -431, -431, -431, -431, -431, 641, 642, + 642, 642, 642, 642, 642, 642, 642, 642, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, @@ -7851,34 +8012,34 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -432, -432, -432, -432, -432, -432, -432, -432, -432, - -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, - -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, - -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, - - -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, - -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, - -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, - -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, + 69, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, + -432, -432, 643, -432, -432, -432, -432, -432, -432, -432, + + -432, -432, -432, -432, -432, -432, -432, -432, 644, 644, + 644, 644, 644, 644, 644, 644, 644, 644, -432, -432, + -432, -432, -432, -432, -432, 643, 643, 643, 643, 643, + 643, 643, 643, 643, 643, 643, 643, 643, 643, 643, + 643, 643, 643, 643, 643, 643, 643, 643, 643, 643, + 643, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432 }, { - 67, -433, -433, -433, -433, -433, -433, -433, -433, -433, + 69, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, + -433, -433, -433, -433, -433, 633, -433, -433, 634, 634, + 634, 634, 634, 634, 634, 634, 634, 634, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, - -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, - -433, -433, -433, 556, -433, -433, -433, -433, -433, -433, - -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, + -433, -433, -433, -433, -433, 636, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, @@ -7886,12 +8047,12 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -434, -434, -434, -434, -434, -434, -434, -434, -434, - -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, - -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, + 69, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, + -434, -434, -434, -434, -434, -434, -434, -434, 645, 646, + 646, 646, 646, 646, 646, 646, 646, 646, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, @@ -7903,34 +8064,34 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -435, -435, -435, -435, -435, -435, -435, -435, -435, - -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, + 69, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, - -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, - - -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, - -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, - -435, -435, -435, -435, -435, -435, -435, -435, -435, 557, - -435, -435, -435, 558, -435, -435, -435, -435, -435, -435, - -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, + -435, -435, 643, -435, -435, -435, -435, -435, -435, -435, + -435, -435, -435, -435, -435, -435, -435, -435, 647, 648, + + 648, 648, 648, 648, 648, 648, 648, 648, -435, -435, + -435, -435, -435, -435, -435, 643, 643, 643, 643, 643, + 643, 643, 643, 643, 643, 643, 643, 643, 643, 643, + 643, 643, 643, 643, 643, 643, 643, 643, 643, 643, + 643, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435 }, { - 67, -436, -436, -436, -436, -436, -436, -436, -436, -436, + 69, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, - -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, - -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, - -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, - -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, - -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, - -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, - -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, + -436, -436, 649, -436, -436, -436, -436, -436, -436, -436, + -436, -436, -436, -436, -436, -436, -436, -436, 650, 650, + 650, 650, 650, 650, 650, 650, 650, 650, -436, -436, + -436, -436, -436, -436, -436, 649, 649, 649, 649, 649, + 649, 649, 649, 649, 649, 649, 649, 649, 649, 649, + 649, 649, 649, 649, 649, 649, 649, 649, 649, 649, + 649, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, @@ -7938,15 +8099,15 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -437, -437, -437, -437, -437, -437, -437, -437, -437, + 69, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, + -437, -437, 651, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, - -437, -437, -437, -437, 559, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, @@ -7955,15 +8116,15 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -438, -438, -438, -438, -438, -438, -438, -438, -438, - -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, + 69, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, + -438, -438, 652, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, - -438, -438, -438, -438, -438, -438, -438, 560, -438, -438, + -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, @@ -7972,17 +8133,17 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -439, -439, -439, -439, -439, -439, -439, -439, -439, + 69, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, + -439, -439, 653, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, - -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, - -439, -439, -439, -439, -439, -439, -439, -439, -439, 561, - -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, - -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, - -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, + -439, -439, -439, -439, -439, 653, 653, 653, 653, 653, + 653, 653, 653, 653, 653, 653, 653, 653, 653, 653, + 653, 653, 653, 653, 653, 653, 653, 653, 653, 653, + 653, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439 @@ -7990,16 +8151,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -440, -440, -440, -440, -440, -440, -440, -440, -440, - -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, - -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, + 69, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, + -440, -440, -440, -440, -440, -440, -440, -440, 654, 654, + 654, 654, 654, 654, 654, 654, 654, 654, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, - -440, -440, -440, -440, 562, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, + -440, -440, -440, -440, -440, 655, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, @@ -8007,16 +8168,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -441, -441, -441, -441, -441, -441, -441, -441, -441, - -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, - -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, + 69, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, + -441, -441, -441, -441, -441, -441, -441, -441, -441, 656, + 656, 656, 656, 656, 656, 656, 656, 656, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, - -441, -441, -441, -441, -441, -441, -441, -441, 563, -441, + -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, @@ -8024,34 +8185,34 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -442, -442, -442, -442, -442, -442, -442, -442, -442, - -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, - -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, - -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, - - -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, - -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, - -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, - -442, -442, -442, -442, -442, -442, 564, -442, -442, -442, + 69, -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, + -442, -442, 657, -442, -442, -442, -442, -442, -442, -442, + + -442, -442, -442, -442, -442, -442, -442, -442, 658, 658, + 658, 658, 658, 658, 658, 658, 658, 658, -442, -442, + -442, -442, -442, -442, -442, 657, 657, 657, 657, 657, + 657, 657, 657, 657, 657, 657, 657, 657, 657, 657, + 657, 657, 657, 657, 657, 657, 657, 657, 657, 657, + 657, -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, -442 }, { - 67, -443, -443, -443, -443, -443, -443, -443, -443, -443, + 69, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, - -443, -443, -443, -443, -443, -443, -443, -443, -443, 565, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, + -443, -443, -443, -443, -443, 659, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, @@ -8059,16 +8220,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -444, -444, -444, -444, -444, -444, -444, -444, -444, - -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, - -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, + 69, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, + -444, -444, -444, -444, -444, -444, -444, -444, -444, 660, + 660, 660, 660, 660, 660, 660, 660, 660, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, - 566, -444, -444, -444, -444, -444, -444, -444, -444, -444, + -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, @@ -8076,24 +8237,24 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -445, -445, -445, -445, -445, -445, -445, -445, -445, - -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, - -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, - -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, - -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, - - -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, - -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, - -445, -445, -445, 567, -445, -445, -445, -445, -445, -445, + 69, -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, + -445, -445, 661, -445, -445, -445, -445, -445, -445, -445, + -445, -445, -445, -445, -445, -445, -445, -445, 662, 662, + + 662, 662, 662, 662, 662, 662, 662, 662, -445, -445, + -445, -445, -445, -445, -445, 661, 661, 661, 661, 661, + 661, 661, 661, 661, 661, 661, 661, 661, 661, 661, + 661, 661, 661, 661, 661, 661, 661, 661, 661, 661, + 661, -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, -445 }, { - 67, -446, -446, -446, -446, -446, -446, -446, -446, -446, + 69, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, @@ -8101,7 +8262,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, - -446, -446, -446, -446, -446, -446, 568, -446, -446, -446, + -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, @@ -8111,7 +8272,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -447, -447, -447, -447, -447, -447, -447, -447, -447, + 69, -447, -447, -447, -447, -447, -447, -447, -447, -447, -447, -447, -447, -447, -447, -447, -447, -447, -447, -447, -447, -447, -447, -447, -447, -447, -447, -447, -447, -447, -447, -447, -447, -447, -447, -447, -447, -447, -447, -447, @@ -8128,7 +8289,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -448, -448, -448, -448, -448, -448, -448, -448, -448, + 69, -448, -448, -448, -448, -448, -448, -448, -448, -448, -448, -448, -448, -448, -448, -448, -448, -448, -448, -448, -448, -448, -448, -448, -448, -448, -448, -448, -448, -448, -448, -448, -448, -448, -448, -448, -448, -448, -448, -448, @@ -8145,7 +8306,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -449, -449, -449, -449, -449, -449, -449, -449, -449, + 69, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, @@ -8153,7 +8314,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, - -449, -449, -449, -449, -449, -449, -449, -449, 569, -449, + -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, @@ -8163,7 +8324,8 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -450, -450, -450, -450, -450, -450, -450, -450, -450, + 69, -450, -450, -450, -450, -450, -450, -450, -450, -450, + -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, @@ -8171,7 +8333,6 @@ static yyconst flex_int16_t yy_nxt[][128] = -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, - -450, -450, -450, 570, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, @@ -8180,13 +8341,13 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -451, -451, -451, -451, -451, -451, -451, -451, -451, + 69, -451, -451, -451, -451, -451, -451, -451, -451, -451, + -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, - -451, -451, -451, -451, -451, -451, -451, -451, -451, 571, -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, @@ -8197,16 +8358,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -452, -452, -452, -452, -452, -452, -452, -452, -452, + 69, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, - -452, -452, -452, -452, -452, -452, -452, -452, -452, 572, - 572, 572, 572, 572, 572, 572, 572, 572, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, - -452, -452, 573, -452, -452, -452, -452, -452, -452, -452, + -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, + -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, + -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, @@ -8214,7 +8375,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -453, -453, -453, -453, -453, -453, -453, -453, -453, + 69, -453, -453, -453, -453, -453, -453, -453, -453, -453, -453, -453, -453, -453, -453, -453, -453, -453, -453, -453, -453, -453, -453, -453, -453, -453, -453, -453, -453, -453, @@ -8232,12 +8393,12 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -454, -454, -454, -454, -454, -454, -454, -454, -454, + 69, -454, -454, -454, -454, -454, -454, -454, -454, -454, + -454, -454, -454, -454, -454, -454, -454, -454, -454, -454, + -454, -454, -454, -454, -454, -454, -454, -454, -454, -454, -454, -454, -454, -454, -454, -454, -454, -454, -454, -454, -454, -454, -454, -454, -454, -454, -454, -454, -454, -454, -454, -454, -454, -454, -454, -454, -454, -454, -454, -454, - -454, -454, -454, -454, -454, -454, -454, -454, -454, 574, - 574, 574, 574, 574, 574, 574, 574, 574, -454, -454, -454, -454, -454, -454, -454, -454, -454, -454, -454, -454, -454, -454, -454, -454, -454, -454, -454, -454, -454, -454, @@ -8249,7 +8410,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -455, -455, -455, -455, -455, -455, -455, -455, -455, + 69, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, @@ -8258,7 +8419,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, - -455, -455, -455, -455, -455, -455, -455, -455, 575, -455, + -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, @@ -8266,14 +8427,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -456, -456, -456, -456, -456, -456, -456, -456, -456, + 69, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, - -456, -456, -456, -456, -456, 576, -456, -456, -456, -456, + -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, @@ -8284,12 +8445,12 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -457, -457, -457, -457, -457, -457, -457, -457, -457, + 69, -457, -457, -457, -457, -457, -457, -457, -457, -457, + -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, + -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, - -457, -457, -457, -457, -457, -457, -457, -457, -457, 577, - 577, 577, 577, 577, 577, 577, 577, 577, -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, @@ -8301,12 +8462,12 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -458, -458, -458, -458, -458, -458, -458, -458, -458, + 69, -458, -458, -458, -458, -458, -458, -458, -458, -458, + -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, + -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, - -458, -458, -458, -458, -458, -458, -458, -458, -458, 578, - 578, 578, 578, 578, 578, 578, 578, 578, -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, @@ -8318,17 +8479,17 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -459, -459, -459, -459, -459, -459, -459, -459, -459, + 69, -459, -459, -459, -459, -459, -459, -459, -459, -459, -459, -459, -459, -459, -459, -459, -459, -459, -459, -459, -459, -459, -459, -459, -459, -459, -459, -459, -459, -459, - -459, -459, -459, -459, -459, -459, -459, -459, -459, -459, - -459, -459, -459, -459, -459, -459, -459, -459, -459, -459, - -459, -459, -459, -459, -459, -459, -459, -459, -459, -459, - -459, -459, -459, -459, -459, -459, -459, -459, -459, -459, - -459, -459, -459, -459, -459, -459, -459, -459, -459, -459, - -459, -459, 579, -459, -459, -459, -459, -459, -459, -459, - -459, -459, -459, -459, -459, -459, -459, -459, -459, -459, + -459, -459, 663, -459, -459, -459, -459, -459, -459, -459, + -459, -459, -459, -459, -459, -459, -459, -459, 664, 664, + 664, 664, 664, 664, 664, 664, 664, 664, -459, -459, + -459, -459, -459, -459, -459, 663, 663, 663, 663, 663, + 663, 663, 663, 663, 663, 663, 663, 663, 663, 663, + 663, 663, 663, 663, 663, 663, 663, 663, 663, 663, + 663, -459, -459, -459, -459, -459, -459, -459, -459, -459, -459, -459, -459, -459, -459, -459, -459, -459, -459, -459, -459, -459, -459, -459, -459, -459, -459, -459, -459, -459, -459, -459, -459, -459, -459, -459, -459, -459 @@ -8336,12 +8497,12 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -460, -460, -460, -460, -460, -460, -460, -460, -460, - -460, -460, -460, -460, -460, -460, -460, -460, -460, -460, - -460, -460, -460, -460, -460, -460, -460, -460, -460, -460, + 69, -460, -460, -460, -460, -460, -460, -460, -460, -460, -460, -460, -460, -460, -460, -460, -460, -460, -460, -460, -460, -460, -460, -460, -460, -460, -460, -460, -460, -460, -460, -460, -460, -460, -460, -460, -460, -460, -460, -460, + -460, -460, -460, -460, -460, -460, -460, -460, 665, 665, + 665, 665, 665, 665, 665, 665, 665, 665, -460, -460, -460, -460, -460, -460, -460, -460, -460, -460, -460, -460, -460, -460, -460, -460, -460, -460, -460, -460, -460, -460, -460, -460, -460, -460, -460, -460, -460, -460, -460, -460, @@ -8353,47 +8514,47 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -461, -461, -461, -461, -461, -461, -461, -461, -461, - -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, - -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, + 69, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, + -461, -461, -461, -461, -461, 666, -461, -461, 667, 668, + 668, 668, 668, 668, 668, 668, 668, 668, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, - -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, + -461, -461, -461, -461, -461, 669, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, -461 }, { - 67, -462, -462, -462, -462, -462, -462, -462, -462, -462, + 69, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, + -462, -462, -462, -462, -462, 666, -462, -462, 670, 670, + 670, 670, 670, 670, 670, 670, 670, 670, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, - -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, - -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, - -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, + -462, -462, -462, -462, -462, 671, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -462 }, { - 67, -463, -463, -463, -463, -463, -463, -463, -463, -463, + 69, -463, -463, -463, -463, -463, -463, -463, -463, -463, -463, -463, -463, -463, -463, -463, -463, -463, -463, -463, -463, -463, -463, -463, -463, -463, -463, -463, -463, -463, -463, -463, -463, -463, -463, -463, -463, -463, -463, -463, - -463, -463, -463, -463, -463, -463, -463, -463, -463, -463, - -463, -463, -463, -463, -463, -463, -463, -463, -463, -463, + -463, -463, -463, -463, -463, -463, -463, -463, 672, 672, + 672, 672, 672, 672, 672, 672, 672, 672, -463, -463, -463, -463, -463, -463, -463, -463, -463, -463, -463, -463, -463, -463, -463, -463, -463, -463, -463, -463, -463, -463, -463, -463, -463, -463, -463, -463, -463, -463, -463, -463, @@ -8405,30 +8566,30 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -464, -464, -464, -464, -464, -464, -464, -464, -464, - -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, - -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, + 69, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, + -464, -464, -464, -464, -464, 666, -464, -464, 667, 667, + 667, 667, 667, 667, 667, 667, 667, 667, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, - -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, + -464, -464, -464, -464, -464, 673, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, -464 }, { - 67, -465, -465, -465, -465, -465, -465, -465, -465, -465, - -465, -465, -465, -465, -465, -465, -465, -465, -465, -465, + 69, -465, -465, -465, -465, -465, -465, -465, -465, -465, -465, -465, -465, -465, -465, -465, -465, -465, -465, -465, -465, -465, -465, -465, -465, -465, -465, -465, -465, -465, -465, -465, -465, -465, -465, -465, -465, -465, -465, -465, + -465, -465, -465, -465, -465, -465, -465, -465, 674, 675, - -465, -465, -465, -465, -465, -465, -465, -465, -465, -465, + 675, 675, 675, 675, 675, 675, 675, 675, -465, -465, -465, -465, -465, -465, -465, -465, -465, -465, -465, -465, -465, -465, -465, -465, -465, -465, -465, -465, -465, -465, -465, -465, -465, -465, -465, -465, -465, -465, -465, -465, @@ -8439,17 +8600,17 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -466, -466, -466, -466, -466, -466, -466, -466, -466, + 69, -466, -466, -466, -466, -466, -466, -466, -466, -466, -466, -466, -466, -466, -466, -466, -466, -466, -466, -466, -466, -466, -466, -466, -466, -466, -466, -466, -466, -466, - -466, -466, -466, -466, -466, -466, -466, -466, -466, -466, - -466, -466, -466, -466, -466, -466, -466, -466, 580, 580, - 580, 580, 580, 580, 580, 580, 580, 580, -466, -466, - -466, -466, -466, -466, -466, -466, -466, -466, -466, -466, - -466, -466, -466, -466, -466, -466, -466, -466, -466, -466, - -466, -466, -466, -466, -466, -466, -466, -466, -466, -466, - -466, -466, -466, -466, -466, -466, -466, -466, -466, -466, + -466, -466, 676, -466, -466, -466, -466, -466, -466, -466, + -466, -466, -466, -466, -466, -466, -466, -466, 677, 677, + 677, 677, 677, 677, 677, 677, 677, 677, -466, -466, + -466, -466, -466, -466, -466, 676, 676, 676, 676, 676, + 676, 676, 676, 676, 676, 676, 676, 676, 676, 676, + 676, 676, 676, 676, 676, 676, 676, 676, 676, 676, + 676, -466, -466, -466, -466, -466, -466, -466, -466, -466, -466, -466, -466, -466, -466, -466, -466, -466, -466, -466, -466, -466, -466, -466, -466, -466, -466, -466, -466, -466, @@ -8457,29 +8618,29 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -467, -467, -467, -467, -467, -467, -467, -467, -467, + 69, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, - -467, -467, -467, -467, -467, -467, -467, -467, 580, 580, - 580, 580, 580, 580, 580, 580, 580, 580, -467, -467, + -467, -467, -467, -467, -467, 666, -467, -467, 667, 667, + 667, 667, 667, 667, 667, 667, 667, 667, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, - -467, -467, -467, -467, -467, 581, -467, -467, -467, -467, + -467, -467, -467, -467, -467, 669, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -467 }, { - 67, -468, -468, -468, -468, -468, -468, -468, -468, -468, + 69, -468, -468, -468, -468, -468, -468, -468, -468, -468, -468, -468, -468, -468, -468, -468, -468, -468, -468, -468, -468, -468, -468, -468, -468, -468, -468, -468, -468, -468, -468, -468, -468, -468, -468, -468, -468, -468, -468, -468, - -468, -468, -468, -468, -468, -468, -468, -468, -468, 582, - 582, 582, 582, 582, 582, 582, 582, 582, -468, -468, + -468, -468, -468, -468, -468, -468, -468, -468, 678, 679, + 679, 679, 679, 679, 679, 679, 679, 679, -468, -468, -468, -468, -468, -468, -468, -468, -468, -468, -468, -468, -468, -468, -468, -468, -468, -468, -468, -468, -468, -468, @@ -8491,17 +8652,17 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -469, -469, -469, -469, -469, -469, -469, -469, -469, + 69, -469, -469, -469, -469, -469, -469, -469, -469, -469, -469, -469, -469, -469, -469, -469, -469, -469, -469, -469, -469, -469, -469, -469, -469, -469, -469, -469, -469, -469, - -469, -469, 583, -469, -469, -469, -469, -469, -469, -469, - -469, -469, -469, -469, -469, -469, -469, -469, 584, 584, - 584, 584, 584, 584, 584, 584, 584, 584, -469, -469, - -469, -469, -469, -469, -469, 583, 583, 583, 583, 583, - 583, 583, 583, 583, 583, 583, 583, 583, 583, 583, - 583, 583, 583, 583, 583, 583, 583, 583, 583, 583, - 583, -469, -469, -469, -469, -469, -469, -469, -469, -469, + -469, -469, 680, -469, -469, -469, -469, -469, -469, -469, + -469, -469, -469, -469, -469, -469, -469, -469, 681, 682, + 682, 682, 682, 682, 682, 682, 682, 682, -469, -469, + -469, -469, -469, -469, -469, 680, 680, 680, 680, 680, + 680, 680, 680, 680, 680, 680, 680, 680, 680, 680, + 680, 680, 680, 680, 680, 680, 680, 680, 680, 680, + 680, -469, -469, -469, -469, -469, -469, -469, -469, -469, -469, -469, -469, -469, -469, -469, -469, -469, -469, -469, -469, -469, -469, -469, -469, -469, -469, -469, -469, -469, -469, -469, -469, -469, -469, -469, -469, -469 @@ -8509,16 +8670,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -470, -470, -470, -470, -470, -470, -470, -470, -470, - -470, -470, -470, -470, -470, -470, -470, -470, -470, -470, - -470, -470, -470, -470, -470, -470, -470, -470, -470, -470, - -470, -470, 585, -470, -470, -470, -470, -470, -470, -470, - -470, -470, -470, -470, -470, -470, -470, -470, -470, -470, - -470, -470, -470, -470, -470, -470, -470, -470, -470, -470, - -470, -470, -470, -470, -470, -470, -470, -470, -470, -470, - -470, -470, -470, -470, -470, -470, -470, -470, -470, -470, + 69, -470, -470, -470, -470, -470, -470, -470, -470, -470, -470, -470, -470, -470, -470, -470, -470, -470, -470, -470, -470, -470, -470, -470, -470, -470, -470, -470, -470, -470, + -470, -470, 680, -470, -470, -470, -470, -470, -470, -470, + -470, -470, -470, -470, -470, -470, -470, -470, 683, 683, + 683, 683, 683, 683, 683, 683, 683, 683, -470, -470, + -470, -470, -470, -470, -470, 680, 680, 680, 680, 680, + 680, 680, 680, 680, 680, 680, 680, 680, 680, 680, + 680, 680, 680, 680, 680, 680, 680, 680, 680, 680, + 680, -470, -470, -470, -470, -470, -470, -470, -470, -470, -470, -470, -470, -470, -470, -470, -470, -470, -470, -470, -470, -470, -470, -470, -470, -470, -470, -470, -470, -470, @@ -8526,30 +8687,30 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -471, -471, -471, -471, -471, -471, -471, -471, -471, + 69, -471, -471, -471, -471, -471, -471, -471, -471, -471, + -471, -471, -471, -471, -471, -471, -471, -471, -471, -471, + -471, -471, -471, -471, -471, -471, -471, -471, -471, -471, + -471, -471, -471, -471, -471, -471, -471, -471, -471, -471, + -471, -471, -471, -471, -471, -471, -471, -471, 684, 684, + 684, 684, 684, 684, 684, 684, 684, 684, -471, -471, + -471, -471, -471, -471, -471, -471, -471, -471, -471, -471, + -471, -471, -471, -471, -471, -471, -471, -471, -471, -471, -471, -471, -471, -471, -471, -471, -471, -471, -471, -471, - -471, -471, 586, -471, -471, -471, -471, -471, -471, -471, - -471, -471, -471, -471, -471, -471, -471, -471, 587, 587, - 587, 587, 587, 587, 587, 587, 587, 587, -471, -471, - -471, -471, -471, -471, -471, 586, 586, 586, 586, 586, - - 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, - 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, - 586, -471, -471, -471, -471, -471, -471, -471, -471, -471, + -471, -471, -471, -471, -471, 685, -471, -471, -471, -471, -471, -471, -471, -471, -471, -471, -471, -471, -471, -471, -471, -471, -471, -471, -471, -471, -471, -471, -471, -471, -471, -471, -471, -471, -471, -471, -471, -471 }, { - 67, -472, -472, -472, -472, -472, -472, -472, -472, -472, + 69, -472, -472, -472, -472, -472, -472, -472, -472, -472, -472, -472, -472, -472, -472, -472, -472, -472, -472, -472, -472, -472, -472, -472, -472, -472, -472, -472, -472, -472, -472, -472, -472, -472, -472, -472, -472, -472, -472, -472, - -472, -472, -472, -472, -472, -472, -472, -472, -472, -472, - -472, -472, -472, -472, -472, -472, -472, -472, -472, -472, + -472, -472, -472, -472, -472, -472, -472, -472, 686, 687, + 687, 687, 687, 687, 687, 687, 687, 687, -472, -472, -472, -472, -472, -472, -472, -472, -472, -472, -472, -472, -472, -472, -472, -472, -472, -472, -472, -472, -472, -472, -472, -472, -472, -472, -472, -472, -472, -472, -472, -472, @@ -8560,17 +8721,17 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -473, -473, -473, -473, -473, -473, -473, -473, -473, + 69, -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, + -473, -473, 688, -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, - -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, - -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, - -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, - -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, - -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, + -473, -473, -473, -473, -473, 688, 688, 688, 688, 688, + 688, 688, 688, 688, 688, 688, 688, 688, 688, 688, + 688, 688, 688, 688, 688, 688, 688, 688, 688, 688, + 688, -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, @@ -8578,24 +8739,24 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -474, -474, -474, -474, -474, -474, -474, -474, -474, - -474, -474, -474, -474, -474, -474, -474, -474, -474, -474, - -474, -474, -474, -474, -474, -474, -474, -474, -474, -474, - -474, -474, -474, -474, -474, -474, -474, -474, -474, -474, - -474, -474, -474, -474, -474, -474, -474, -474, -474, -474, - -474, -474, -474, -474, -474, -474, -474, -474, -474, -474, - -474, -474, -474, -474, -474, -474, -474, -474, -474, -474, - -474, -474, -474, -474, -474, -474, -474, -474, -474, -474, - + 69, -474, -474, -474, -474, -474, -474, -474, -474, -474, -474, -474, -474, -474, -474, -474, -474, -474, -474, -474, -474, -474, -474, -474, -474, -474, -474, -474, -474, -474, + -474, -474, 688, -474, -474, -474, -474, -474, -474, -474, + -474, -474, -474, -474, -474, -474, -474, -474, 689, 689, + 689, 689, 689, 689, 689, 689, 689, 689, -474, -474, + -474, -474, -474, -474, -474, 688, 688, 688, 688, 688, + 688, 688, 688, 688, 688, 688, 688, 688, 688, 688, + + 688, 688, 688, 688, 688, 688, 688, 688, 688, 688, + 688, -474, -474, -474, -474, -474, -474, -474, -474, -474, -474, -474, -474, -474, -474, -474, -474, -474, -474, -474, -474, -474, -474, -474, -474, -474, -474, -474, -474, -474, -474, -474, -474, -474, -474, -474, -474, -474 }, { - 67, -475, -475, -475, -475, -475, -475, -475, -475, -475, + 69, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, @@ -8605,20 +8766,20 @@ static yyconst flex_int16_t yy_nxt[][128] = -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, - -475, -475, -475, -475, -475, 588, -475, -475, -475, -475, + -475, -475, -475, -475, -475, 690, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -475 }, { - 67, -476, -476, -476, -476, -476, -476, -476, -476, -476, + 69, -476, -476, -476, -476, -476, -476, -476, -476, -476, -476, -476, -476, -476, -476, -476, -476, -476, -476, -476, -476, -476, -476, -476, -476, -476, -476, -476, -476, -476, -476, -476, -476, -476, -476, -476, -476, -476, -476, -476, - -476, -476, -476, -476, -476, -476, -476, -476, -476, 589, - 589, 589, 589, 589, 589, 589, 589, 589, -476, -476, + -476, -476, -476, -476, -476, -476, -476, -476, 691, 692, + 692, 692, 692, 692, 692, 692, 692, 692, -476, -476, -476, -476, -476, -476, -476, -476, -476, -476, -476, -476, -476, -476, -476, -476, -476, -476, -476, -476, -476, -476, -476, -476, -476, -476, -476, -476, -476, -476, -476, -476, @@ -8630,51 +8791,51 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -477, -477, -477, -477, -477, -477, -477, -477, -477, + 69, -477, -477, -477, -477, -477, -477, -477, -477, -477, + -477, -477, -477, -477, -477, -477, -477, -477, -477, -477, -477, -477, -477, -477, -477, -477, -477, -477, -477, -477, + -477, -477, 693, -477, -477, -477, -477, -477, -477, -477, -477, -477, -477, -477, -477, -477, -477, -477, -477, -477, - -477, -477, 590, -477, -477, -477, -477, -477, -477, -477, - -477, -477, -477, -477, -477, -477, -477, -477, 591, 591, - 591, 591, 591, 591, 591, 591, 591, 591, -477, -477, - -477, -477, -477, -477, -477, 590, 590, 590, 590, 590, - 590, 590, 590, 590, 590, 590, 590, 590, 590, 590, - 590, 590, 590, 590, 590, 590, 590, 590, 590, 590, - - 590, -477, -477, -477, -477, -477, -477, -477, -477, -477, + -477, -477, -477, -477, -477, -477, -477, -477, -477, -477, + -477, -477, -477, -477, -477, 693, 693, 693, 693, 693, + 693, 693, 693, 693, 693, 693, 693, 693, 693, 693, + 693, 693, 693, 693, 693, 693, 693, 693, 693, 693, + + 693, -477, -477, -477, -477, -477, -477, -477, -477, -477, -477, -477, -477, -477, -477, -477, -477, -477, -477, -477, -477, -477, -477, -477, -477, -477, -477, -477, -477, -477, -477, -477, -477, -477, -477, -477, -477, -477 }, { - 67, -478, -478, -478, -478, -478, -478, -478, -478, -478, - -478, -478, -478, -478, -478, -478, -478, -478, -478, -478, - -478, -478, -478, -478, -478, -478, -478, -478, -478, -478, - -478, -478, 592, -478, -478, -478, -478, -478, -478, -478, - -478, -478, -478, -478, -478, -478, -478, -478, -478, -478, - -478, -478, -478, -478, -478, -478, -478, -478, -478, -478, - - -478, -478, -478, -478, -478, -478, -478, -478, -478, -478, - -478, -478, -478, -478, -478, -478, -478, -478, -478, -478, + 69, -478, -478, -478, -478, -478, -478, -478, -478, -478, -478, -478, -478, -478, -478, -478, -478, -478, -478, -478, -478, -478, -478, -478, -478, -478, -478, -478, -478, -478, + -478, -478, 693, -478, -478, -478, -478, -478, -478, -478, + -478, -478, -478, -478, -478, -478, -478, -478, 694, 694, + 694, 694, 694, 694, 694, 694, 694, 694, -478, -478, + + -478, -478, -478, -478, -478, 693, 693, 693, 693, 693, + 693, 693, 693, 693, 693, 693, 693, 693, 693, 693, + 693, 693, 693, 693, 693, 693, 693, 693, 693, 693, + 693, -478, -478, -478, -478, -478, -478, -478, -478, -478, -478, -478, -478, -478, -478, -478, -478, -478, -478, -478, -478, -478, -478, -478, -478, -478, -478, -478, -478, -478, -478, -478, -478, -478, -478, -478, -478, -478 }, { - 67, -479, -479, -479, -479, -479, -479, -479, -479, -479, + 69, -479, -479, -479, -479, -479, -479, -479, -479, -479, -479, -479, -479, -479, -479, -479, -479, -479, -479, -479, -479, -479, -479, -479, -479, -479, -479, -479, -479, -479, - -479, -479, 593, -479, -479, -479, -479, -479, -479, -479, - -479, -479, -479, -479, -479, -479, -479, -479, 594, 594, - 594, 594, 594, 594, 594, 594, 594, 594, -479, -479, - -479, -479, -479, -479, -479, 593, 593, 593, 593, 593, - 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, - 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, - 593, -479, -479, -479, -479, -479, -479, -479, -479, -479, + -479, -479, -479, -479, -479, -479, -479, -479, -479, -479, + -479, -479, -479, -479, -479, -479, -479, -479, 695, 695, + 695, 695, 695, 695, 695, 695, 695, 695, -479, -479, + -479, -479, -479, -479, -479, -479, -479, -479, -479, -479, + -479, -479, -479, -479, -479, -479, -479, -479, -479, -479, + -479, -479, -479, -479, -479, -479, -479, -479, -479, -479, + -479, -479, -479, -479, -479, 696, -479, -479, -479, -479, -479, -479, -479, -479, -479, -479, -479, -479, -479, -479, -479, -479, -479, -479, -479, -479, -479, -479, -479, -479, -479, -479, -479, -479, -479, -479, -479, -479 @@ -8682,12 +8843,12 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -480, -480, -480, -480, -480, -480, -480, -480, -480, + 69, -480, -480, -480, -480, -480, -480, -480, -480, -480, -480, -480, -480, -480, -480, -480, -480, -480, -480, -480, -480, -480, -480, -480, -480, -480, -480, -480, -480, -480, -480, -480, -480, -480, -480, -480, -480, -480, -480, -480, - -480, -480, -480, -480, -480, -480, -480, -480, -480, 595, - 595, 595, 595, 595, 595, 595, 595, 595, -480, -480, + -480, -480, -480, -480, -480, -480, -480, -480, 697, 698, + 698, 698, 698, 698, 698, 698, 698, 698, -480, -480, -480, -480, -480, -480, -480, -480, -480, -480, -480, -480, -480, -480, -480, -480, -480, -480, -480, -480, -480, -480, -480, -480, -480, -480, -480, -480, -480, -480, -480, -480, @@ -8699,51 +8860,51 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -481, -481, -481, -481, -481, -481, -481, -481, -481, + 69, -481, -481, -481, -481, -481, -481, -481, -481, -481, + -481, -481, -481, -481, -481, -481, -481, -481, -481, -481, -481, -481, -481, -481, -481, -481, -481, -481, -481, -481, + -481, -481, 699, -481, -481, -481, -481, -481, -481, -481, -481, -481, -481, -481, -481, -481, -481, -481, -481, -481, - -481, -481, 596, -481, -481, -481, -481, -481, -481, -481, - -481, -481, -481, -481, -481, -481, -481, -481, 597, 597, - 597, 597, 597, 597, 597, 597, 597, 597, -481, -481, - -481, -481, -481, -481, -481, 596, 596, 596, 596, 596, - - 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, - 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, - 596, -481, -481, -481, -481, -481, -481, -481, -481, -481, + -481, -481, -481, -481, -481, -481, -481, -481, -481, -481, + -481, -481, -481, -481, -481, 699, 699, 699, 699, 699, + + 699, 699, 699, 699, 699, 699, 699, 699, 699, 699, + 699, 699, 699, 699, 699, 699, 699, 699, 699, 699, + 699, -481, -481, -481, -481, -481, -481, -481, -481, -481, -481, -481, -481, -481, -481, -481, -481, -481, -481, -481, -481, -481, -481, -481, -481, -481, -481, -481, -481, -481, -481, -481, -481, -481, -481, -481, -481, -481 }, { - 67, -482, -482, -482, -482, -482, -482, -482, -482, -482, - -482, -482, -482, -482, -482, -482, -482, -482, -482, -482, - -482, -482, -482, -482, -482, -482, -482, -482, -482, -482, - -482, -482, 598, -482, -482, -482, -482, -482, -482, -482, - - -482, -482, -482, -482, -482, -482, -482, -482, -482, -482, - -482, -482, -482, -482, -482, -482, -482, -482, -482, -482, - -482, -482, -482, -482, -482, -482, -482, -482, -482, -482, - -482, -482, -482, -482, -482, -482, -482, -482, -482, -482, + 69, -482, -482, -482, -482, -482, -482, -482, -482, -482, -482, -482, -482, -482, -482, -482, -482, -482, -482, -482, -482, -482, -482, -482, -482, -482, -482, -482, -482, -482, + -482, -482, 699, -482, -482, -482, -482, -482, -482, -482, + + -482, -482, -482, -482, -482, -482, -482, -482, 700, 700, + 700, 700, 700, 700, 700, 700, 700, 700, -482, -482, + -482, -482, -482, -482, -482, 699, 699, 699, 699, 699, + 699, 699, 699, 699, 699, 699, 699, 699, 699, 699, + 699, 699, 699, 699, 699, 699, 699, 699, 699, 699, + 699, -482, -482, -482, -482, -482, -482, -482, -482, -482, -482, -482, -482, -482, -482, -482, -482, -482, -482, -482, -482, -482, -482, -482, -482, -482, -482, -482, -482, -482, -482, -482, -482, -482, -482, -482, -482, -482 }, { - 67, -483, -483, -483, -483, -483, -483, -483, -483, -483, + 69, -483, -483, -483, -483, -483, -483, -483, -483, -483, -483, -483, -483, -483, -483, -483, -483, -483, -483, -483, -483, -483, -483, -483, -483, -483, -483, -483, -483, -483, - -483, -483, 599, -483, -483, -483, -483, -483, -483, -483, - -483, -483, -483, -483, -483, -483, -483, -483, 600, 600, - 600, 600, 600, 600, 600, 600, 600, 600, -483, -483, - -483, -483, -483, -483, -483, 599, 599, 599, 599, 599, - 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, - 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, - 599, -483, -483, -483, -483, -483, -483, -483, -483, -483, + -483, -483, -483, -483, -483, -483, -483, -483, -483, -483, + -483, -483, -483, -483, -483, -483, -483, -483, -483, -483, + -483, -483, -483, -483, -483, -483, -483, -483, -483, -483, + -483, -483, -483, -483, -483, -483, -483, -483, -483, -483, + -483, -483, -483, -483, -483, -483, -483, -483, -483, -483, + -483, -483, -483, -483, -483, -483, -483, -483, -483, -483, + -483, -483, -483, -483, -483, 701, -483, -483, -483, -483, -483, -483, -483, -483, -483, -483, -483, -483, -483, -483, -483, -483, -483, -483, -483, -483, -483, -483, -483, -483, @@ -8751,51 +8912,51 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -484, -484, -484, -484, -484, -484, -484, -484, -484, - -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, - -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, + 69, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, + -484, -484, -484, -484, -484, -484, -484, -484, 702, 703, + 703, 703, 703, 703, 703, 703, 703, 703, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, - -484, -484, -484, -484, -484, 601, -484, -484, -484, -484, + -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -484 }, { - 67, -485, -485, -485, -485, -485, -485, -485, -485, -485, + 69, -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, + -485, -485, 704, -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, - -485, -485, -485, -485, -485, -485, -485, -485, 602, 603, - 603, 603, 603, 603, 603, 603, 603, 603, -485, -485, - -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, - -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, - -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, + -485, -485, -485, -485, -485, 704, 704, 704, 704, 704, + 704, 704, 704, 704, 704, 704, 704, 704, 704, 704, + 704, 704, 704, 704, 704, 704, 704, 704, 704, 704, + 704, -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, -485 }, { - 67, -486, -486, -486, -486, -486, -486, -486, -486, -486, + 69, -486, -486, -486, -486, -486, -486, -486, -486, -486, -486, -486, -486, -486, -486, -486, -486, -486, -486, -486, -486, -486, -486, -486, -486, -486, -486, -486, -486, -486, - -486, -486, 604, -486, -486, -486, -486, -486, -486, -486, - -486, -486, -486, -486, -486, -486, -486, -486, -486, -486, - -486, -486, -486, -486, -486, -486, -486, -486, -486, -486, - -486, -486, -486, -486, -486, 604, 604, 604, 604, 604, - 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, - 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, - 604, -486, -486, -486, -486, -486, -486, -486, -486, -486, + -486, -486, 704, -486, -486, -486, -486, -486, -486, -486, + -486, -486, -486, -486, -486, -486, -486, -486, 705, 705, + 705, 705, 705, 705, 705, 705, 705, 705, -486, -486, + -486, -486, -486, -486, -486, 704, 704, 704, 704, 704, + 704, 704, 704, 704, 704, 704, 704, 704, 704, 704, + 704, 704, 704, 704, 704, 704, 704, 704, 704, 704, + 704, -486, -486, -486, -486, -486, -486, -486, -486, -486, -486, -486, -486, -486, -486, -486, -486, -486, -486, -486, -486, -486, -486, -486, -486, -486, -486, -486, -486, -486, @@ -8803,27 +8964,27 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -487, -487, -487, -487, -487, -487, -487, -487, -487, + 69, -487, -487, -487, -487, -487, -487, -487, -487, -487, + -487, -487, -487, -487, -487, -487, -487, -487, -487, -487, -487, -487, -487, -487, -487, -487, -487, -487, -487, -487, -487, -487, -487, -487, -487, -487, -487, -487, -487, -487, - -487, -487, 604, -487, -487, -487, -487, -487, -487, -487, - -487, -487, -487, -487, -487, -487, -487, -487, 605, 605, - 605, 605, 605, 605, 605, 605, 605, 605, -487, -487, - -487, -487, -487, -487, -487, 604, 604, 604, 604, 604, - 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, - 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, - - 604, -487, -487, -487, -487, -487, -487, -487, -487, -487, + -487, -487, -487, -487, -487, -487, -487, -487, -487, -487, + -487, -487, -487, -487, -487, -487, -487, -487, -487, -487, + -487, -487, -487, -487, -487, -487, -487, -487, -487, -487, + -487, -487, -487, -487, -487, -487, -487, -487, -487, -487, + -487, -487, -487, -487, -487, -487, -487, -487, -487, -487, + + -487, -487, -487, -487, -487, -487, -487, -487, -487, -487, -487, -487, -487, -487, -487, -487, -487, -487, -487, -487, -487, -487, -487, -487, -487, -487, -487, -487, -487, -487, -487, -487, -487, -487, -487, -487, -487, -487 }, { - 67, -488, -488, -488, -488, -488, -488, -488, -488, -488, + 69, -488, -488, -488, -488, -488, -488, -488, -488, -488, + -488, -488, -488, -488, -488, -488, -488, -488, -488, -488, -488, -488, -488, -488, -488, -488, -488, -488, -488, -488, -488, -488, -488, -488, -488, -488, -488, -488, -488, -488, - -488, -488, 606, -488, -488, -488, -488, -488, -488, -488, -488, -488, -488, -488, -488, -488, -488, -488, -488, -488, -488, -488, -488, -488, -488, -488, -488, -488, -488, -488, @@ -8837,17 +8998,17 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -489, -489, -489, -489, -489, -489, -489, -489, -489, + 69, -489, -489, -489, -489, -489, -489, -489, -489, -489, -489, -489, -489, -489, -489, -489, -489, -489, -489, -489, -489, -489, -489, -489, -489, -489, -489, -489, -489, -489, - -489, -489, 607, -489, -489, -489, -489, -489, -489, -489, - -489, -489, -489, -489, -489, -489, -489, -489, 608, 608, - 608, 608, 608, 608, 608, 608, 608, 608, -489, -489, - -489, -489, -489, -489, -489, 607, 607, 607, 607, 607, - 607, 607, 607, 607, 607, 607, 607, 607, 607, 607, - 607, 607, 607, 607, 607, 607, 607, 607, 607, 607, - 607, -489, -489, -489, -489, -489, -489, -489, -489, -489, + -489, -489, -489, -489, -489, -489, -489, -489, -489, -489, + -489, -489, -489, -489, -489, -489, -489, -489, -489, -489, + -489, -489, -489, -489, -489, -489, -489, -489, -489, -489, + -489, -489, -489, -489, -489, -489, -489, -489, -489, -489, + -489, -489, -489, -489, -489, -489, -489, -489, -489, -489, + -489, -489, -489, -489, -489, -489, -489, -489, -489, -489, + -489, -489, -489, -489, -489, -489, -489, -489, -489, -489, -489, -489, -489, -489, -489, -489, -489, -489, -489, -489, -489, -489, -489, -489, -489, -489, -489, -489, -489, -489, -489, -489, -489, -489, -489, -489, -489, -489 @@ -8855,16 +9016,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -490, -490, -490, -490, -490, -490, -490, -490, -490, + 69, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, + -490, -490, 706, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, - -490, -490, -490, -490, -490, 609, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, @@ -8872,12 +9033,12 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -491, -491, -491, -491, -491, -491, -491, -491, -491, + 69, -491, -491, -491, -491, -491, -491, -491, -491, -491, + -491, -491, -491, -491, -491, -491, -491, -491, -491, -491, -491, -491, -491, -491, -491, -491, -491, -491, -491, -491, + -491, -491, 707, -491, -491, -491, -491, -491, -491, -491, -491, -491, -491, -491, -491, -491, -491, -491, -491, -491, -491, -491, -491, -491, -491, -491, -491, -491, -491, -491, - -491, -491, -491, -491, -491, -491, -491, -491, 610, 611, - 611, 611, 611, 611, 611, 611, 611, 611, -491, -491, -491, -491, -491, -491, -491, -491, -491, -491, -491, -491, -491, -491, -491, -491, -491, -491, -491, -491, -491, -491, @@ -8889,34 +9050,34 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -492, -492, -492, -492, -492, -492, -492, -492, -492, + 69, -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, - -492, -492, 612, -492, -492, -492, -492, -492, -492, -492, + -492, -492, 708, -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, - -492, -492, -492, -492, -492, 612, 612, 612, 612, 612, - 612, 612, 612, 612, 612, 612, 612, 612, 612, 612, - 612, 612, 612, 612, 612, 612, 612, 612, 612, 612, - 612, -492, -492, -492, -492, -492, -492, -492, -492, -492, + -492, -492, -492, -492, -492, 708, 708, 708, 708, 708, + 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, + 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, + 708, -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, -492 }, { - 67, -493, -493, -493, -493, -493, -493, -493, -493, -493, + 69, -493, -493, -493, -493, -493, -493, -493, -493, -493, -493, -493, -493, -493, -493, -493, -493, -493, -493, -493, -493, -493, -493, -493, -493, -493, -493, -493, -493, -493, - -493, -493, 612, -493, -493, -493, -493, -493, -493, -493, - -493, -493, -493, -493, -493, -493, -493, -493, 613, 613, - 613, 613, 613, 613, 613, 613, 613, 613, -493, -493, - -493, -493, -493, -493, -493, 612, 612, 612, 612, 612, - 612, 612, 612, 612, 612, 612, 612, 612, 612, 612, - 612, 612, 612, 612, 612, 612, 612, 612, 612, 612, - 612, -493, -493, -493, -493, -493, -493, -493, -493, -493, + -493, -493, -493, -493, -493, -493, -493, -493, -493, -493, + -493, -493, -493, -493, -493, -493, -493, -493, -493, -493, + -493, -493, -493, -493, -493, -493, -493, -493, -493, -493, + -493, -493, -493, -493, -493, -493, -493, -493, -493, -493, + -493, -493, -493, -493, -493, -493, -493, -493, -493, -493, + -493, -493, -493, -493, -493, -493, -493, -493, -493, -493, + -493, -493, -493, -493, -493, -493, -493, -493, -493, -493, -493, -493, -493, -493, -493, -493, -493, -493, -493, -493, -493, -493, -493, -493, -493, -493, -493, -493, -493, -493, @@ -8924,10 +9085,10 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -494, -494, -494, -494, -494, -494, -494, -494, -494, + 69, -494, -494, -494, -494, -494, -494, -494, -494, -494, + -494, -494, -494, -494, -494, -494, -494, -494, -494, -494, -494, -494, -494, -494, -494, -494, -494, -494, -494, -494, -494, -494, -494, -494, -494, -494, -494, -494, -494, -494, - -494, -494, 614, -494, -494, -494, -494, -494, -494, -494, -494, -494, -494, -494, -494, -494, -494, -494, -494, -494, -494, -494, -494, -494, -494, -494, -494, -494, -494, -494, -494, -494, -494, -494, -494, -494, -494, -494, -494, -494, @@ -8941,30 +9102,30 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -495, -495, -495, -495, -495, -495, -495, -495, -495, + 69, -495, -495, -495, -495, -495, -495, -495, -495, -495, + -495, -495, -495, -495, -495, -495, -495, -495, -495, -495, + -495, -495, -495, -495, -495, -495, -495, -495, -495, -495, + -495, -495, -495, -495, -495, -495, -495, -495, -495, -495, + -495, -495, -495, -495, -495, -495, -495, -495, -495, -495, + + -495, -495, -495, -495, -495, -495, -495, -495, -495, -495, + -495, -495, -495, -495, -495, -495, -495, -495, -495, -495, + -495, -495, -495, -495, -495, -495, -495, -495, -495, -495, -495, -495, -495, -495, -495, -495, -495, -495, -495, -495, -495, -495, -495, -495, -495, -495, -495, -495, -495, -495, - -495, -495, 615, -495, -495, -495, -495, -495, -495, -495, - -495, -495, -495, -495, -495, -495, -495, -495, 616, 616, - - 616, 616, 616, 616, 616, 616, 616, 616, -495, -495, - -495, -495, -495, -495, -495, 615, 615, 615, 615, 615, - 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, - 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, - 615, -495, -495, -495, -495, -495, -495, -495, -495, -495, -495, -495, -495, -495, -495, -495, -495, -495, -495, -495, -495, -495, -495, -495, -495, -495, -495, -495, -495, -495, -495, -495, -495, -495, -495, -495, -495, -495 }, { - 67, -496, -496, -496, -496, -496, -496, -496, -496, -496, + 69, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, + -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, + -496, -496, 709, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, - -496, -496, -496, -496, -496, -496, -496, -496, 617, 617, - 617, 617, 617, 617, 617, 617, 617, 617, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, @@ -8976,45 +9137,45 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -497, -497, -497, -497, -497, -497, -497, -497, -497, + 69, -497, -497, -497, -497, -497, -497, -497, -497, -497, + -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, + -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, + -497, -497, 710, -497, -497, -497, -497, -497, -497, -497, + -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, - -497, -497, 618, -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, - -497, -497, -497, -497, -497, 618, 618, 618, 618, 618, - 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, - 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, - 618, -497, -497, -497, -497, -497, -497, -497, -497, -497, + -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, -497 }, { - 67, -498, -498, -498, -498, -498, -498, -498, -498, -498, + 69, -498, -498, -498, -498, -498, -498, -498, -498, -498, -498, -498, -498, -498, -498, -498, -498, -498, -498, -498, -498, -498, -498, -498, -498, -498, -498, -498, -498, -498, - -498, -498, 618, -498, -498, -498, -498, -498, -498, -498, - -498, -498, -498, -498, -498, -498, -498, -498, 619, 619, - 619, 619, 619, 619, 619, 619, 619, 619, -498, -498, - - -498, -498, -498, -498, -498, 618, 618, 618, 618, 618, - 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, - 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, - 618, -498, -498, -498, -498, -498, -498, -498, -498, -498, + -498, -498, 711, -498, -498, -498, -498, -498, -498, -498, + -498, -498, -498, -498, -498, -498, -498, -498, 712, 712, + 712, 712, 712, 712, 712, 712, 712, 712, -498, -498, + -498, -498, -498, -498, -498, -498, -498, -498, -498, -498, -498, -498, -498, -498, -498, -498, -498, -498, -498, -498, - -498, -498, -498, -498, -498, -498, -498, -498 - }, + -498, -498, -498, -498, -498, -498, -498, -498, -498, -498, + -498, -498, -498, -498, -498, -498, -498, -498, -498, -498, + -498, -498, -498, -498, -498, -498, -498, -498, -498, -498, + -498, -498, -498, -498, -498, -498, -498, -498, -498, -498, + -498, -498, -498, -498, -498, -498, -498, -498 + }, { - 67, -499, -499, -499, -499, -499, -499, -499, -499, -499, + 69, -499, -499, -499, -499, -499, -499, -499, -499, -499, -499, -499, -499, -499, -499, -499, -499, -499, -499, -499, -499, -499, -499, -499, -499, -499, -499, -499, -499, -499, - -499, -499, 620, -499, -499, -499, -499, -499, -499, -499, + -499, -499, -499, -499, -499, -499, -499, -499, -499, -499, -499, -499, -499, -499, -499, -499, -499, -499, -499, -499, -499, -499, -499, -499, -499, -499, -499, -499, -499, -499, -499, -499, -499, -499, -499, -499, -499, -499, -499, -499, @@ -9028,16 +9189,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -500, -500, -500, -500, -500, -500, -500, -500, -500, + 69, -500, -500, -500, -500, -500, -500, -500, -500, -500, + -500, -500, -500, -500, -500, -500, -500, -500, -500, -500, + -500, -500, -500, -500, -500, -500, -500, -500, -500, -500, + -500, -500, -500, -500, -500, -500, -500, -500, -500, -500, + -500, -500, -500, -500, -500, -500, -500, -500, -500, -500, + -500, -500, -500, -500, -500, -500, -500, -500, -500, -500, + -500, -500, -500, -500, -500, -500, -500, -500, -500, -500, + -500, -500, -500, -500, -500, -500, -500, -500, -500, -500, -500, -500, -500, -500, -500, -500, -500, -500, -500, -500, -500, -500, -500, -500, -500, -500, -500, -500, -500, -500, - -500, -500, 621, -500, -500, -500, -500, -500, -500, -500, - -500, -500, -500, -500, -500, -500, -500, -500, 622, 622, - 622, 622, 622, 622, 622, 622, 622, 622, -500, -500, - -500, -500, -500, -500, -500, 621, 621, 621, 621, 621, - 621, 621, 621, 621, 621, 621, 621, 621, 621, 621, - 621, 621, 621, 621, 621, 621, 621, 621, 621, 621, - 621, -500, -500, -500, -500, -500, -500, -500, -500, -500, -500, -500, -500, -500, -500, -500, -500, -500, -500, -500, -500, -500, -500, -500, -500, -500, -500, -500, -500, -500, @@ -9045,7 +9206,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -501, -501, -501, -501, -501, -501, -501, -501, -501, + 69, -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, @@ -9055,41 +9216,41 @@ static yyconst flex_int16_t yy_nxt[][128] = -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, - -501, -501, -501, -501, -501, 623, -501, -501, -501, -501, + -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, -501 }, { - 67, -502, -502, -502, -502, -502, -502, -502, -502, -502, + 69, -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, - -502, -502, -502, -502, -502, -502, -502, -502, 624, 625, - 625, 625, 625, 625, 625, 625, 625, 625, -502, -502, - -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, - -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, + -502, -502, -502, -502, -502, -502, -502, -502, 502, 502, + 502, 502, 502, 502, 502, 502, 502, 502, -502, -502, + -502, -502, -502, -502, -502, -502, -502, -502, 291, 291, -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, + 291, 291, -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, -502 }, { - 67, -503, -503, -503, -503, -503, -503, -503, -503, -503, + 69, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, - -503, -503, 626, -503, -503, -503, -503, -503, -503, -503, + -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, + -503, -503, -503, -503, -503, -503, -503, -503, 504, 504, + 504, 504, 504, 504, 504, 504, 504, 504, -503, -503, + -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, + -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, - -503, -503, -503, -503, -503, 626, 626, 626, 626, 626, - 626, 626, 626, 626, 626, 626, 626, 626, 626, 626, - 626, 626, 626, 626, 626, 626, 626, 626, 626, 626, - 626, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, @@ -9097,51 +9258,51 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -504, -504, -504, -504, -504, -504, -504, -504, -504, + 69, -504, -504, -504, -504, -504, -504, -504, -504, -504, + -504, -504, -504, -504, -504, -504, -504, -504, -504, -504, + -504, -504, -504, -504, -504, -504, -504, -504, -504, -504, + -504, -504, -504, -504, -504, -504, -504, -504, -504, -504, + -504, -504, -504, -504, -504, -504, -504, -504, 504, 504, + 504, 504, 504, 504, 504, 504, 504, 504, -504, -504, -504, -504, -504, -504, -504, -504, -504, -504, -504, -504, -504, -504, -504, -504, -504, -504, -504, -504, -504, -504, - -504, -504, 626, -504, -504, -504, -504, -504, -504, -504, - -504, -504, -504, -504, -504, -504, -504, -504, 627, 627, - 627, 627, 627, 627, 627, 627, 627, 627, -504, -504, - -504, -504, -504, -504, -504, 626, 626, 626, 626, 626, - 626, 626, 626, 626, 626, 626, 626, 626, 626, 626, - 626, 626, 626, 626, 626, 626, 626, 626, 626, 626, - 626, -504, -504, -504, -504, -504, -504, -504, -504, -504, + -504, -504, -504, -504, -504, -504, -504, -504, -504, -504, + -504, -504, -504, -504, -504, -504, -504, -504, -504, -504, -504, -504, -504, -504, -504, -504, -504, -504, -504, -504, -504, -504, -504, -504, -504, -504, -504, -504, -504, -504, -504, -504, -504, -504, -504, -504, -504, -504 }, { - 67, -505, -505, -505, -505, -505, -505, -505, -505, -505, + 69, -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, - -505, -505, 628, -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, + -505, -505, -505, -505, -505, -505, -505, -505, 505, 505, + 505, 505, 505, 505, 505, 505, 505, 505, -505, -505, + -505, -505, -505, -505, -505, -505, -505, -505, 297, 297, -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, - -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, - -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, - -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, + 297, 297, -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, -505 }, { - 67, -506, -506, -506, -506, -506, -506, -506, -506, -506, + 69, -506, -506, -506, -506, -506, -506, -506, -506, -506, -506, -506, -506, -506, -506, -506, -506, -506, -506, -506, -506, -506, -506, -506, -506, -506, -506, -506, -506, -506, - -506, -506, 629, -506, -506, -506, -506, -506, -506, -506, - -506, -506, -506, -506, -506, -506, -506, -506, 630, 630, - 630, 630, 630, 630, 630, 630, 630, 630, -506, -506, - -506, -506, -506, -506, -506, 629, 629, 629, 629, 629, - 629, 629, 629, 629, 629, 629, 629, 629, 629, 629, - 629, 629, 629, 629, 629, 629, 629, 629, 629, 629, - 629, -506, -506, -506, -506, -506, -506, -506, -506, -506, + -506, -506, -506, -506, -506, -506, -506, -506, -506, -506, + -506, -506, -506, -506, -506, -506, -506, -506, 507, 507, + 507, 507, 507, 507, 507, 507, 507, 507, -506, -506, + -506, -506, -506, -506, -506, -506, -506, -506, -506, -506, + -506, -506, -506, -506, -506, -506, -506, -506, -506, -506, + -506, -506, -506, -506, -506, -506, -506, -506, -506, -506, + -506, -506, -506, -506, -506, -506, -506, -506, -506, -506, -506, -506, -506, -506, -506, -506, -506, -506, -506, -506, -506, -506, -506, -506, -506, -506, -506, -506, -506, -506, @@ -9149,12 +9310,12 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -507, -507, -507, -507, -507, -507, -507, -507, -507, + 69, -507, -507, -507, -507, -507, -507, -507, -507, -507, -507, -507, -507, -507, -507, -507, -507, -507, -507, -507, -507, -507, -507, -507, -507, -507, -507, -507, -507, -507, -507, -507, -507, -507, -507, -507, -507, -507, -507, -507, - -507, -507, -507, -507, -507, -507, -507, -507, 631, 631, - 631, 631, 631, 631, 631, 631, 631, 631, -507, -507, + -507, -507, -507, -507, -507, -507, -507, -507, 507, 507, + 507, 507, 507, 507, 507, 507, 507, 507, -507, -507, -507, -507, -507, -507, -507, -507, -507, -507, -507, -507, -507, -507, -507, -507, -507, -507, -507, -507, -507, -507, -507, -507, -507, -507, -507, -507, -507, -507, -507, -507, @@ -9166,45 +9327,45 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -508, -508, -508, -508, -508, -508, -508, -508, -508, - -508, -508, -508, -508, -508, -508, -508, -508, -508, -508, - -508, -508, -508, -508, -508, -508, -508, -508, -508, -508, - -508, -508, 632, -508, -508, -508, -508, -508, -508, -508, - -508, -508, -508, -508, -508, -508, -508, -508, -508, -508, - -508, -508, -508, -508, -508, -508, -508, -508, -508, -508, + 69, 508, 508, 508, 508, 508, 508, 508, 508, 508, + 713, 508, 508, 508, 508, 508, 508, 508, 508, 508, + 508, 508, 508, 508, 508, 508, 508, 508, 508, 508, + 508, 508, 714, 508, 508, 508, 508, 508, 508, 508, + 508, 508, 508, 508, 508, 508, 508, 715, 508, 508, + 508, 508, 508, 508, 508, 508, 508, 508, 508, 508, - -508, -508, -508, -508, -508, 632, 632, 632, 632, 632, - 632, 632, 632, 632, 632, 632, 632, 632, 632, 632, - 632, 632, 632, 632, 632, 632, 632, 632, 632, 632, - 632, -508, -508, -508, -508, -508, -508, -508, -508, -508, - -508, -508, -508, -508, -508, -508, -508, -508, -508, -508, - -508, -508, -508, -508, -508, -508, -508, -508, -508, -508, - -508, -508, -508, -508, -508, -508, -508, -508 + 508, 508, 508, 508, 508, 508, 508, 508, 508, 508, + 508, 508, 508, 508, 508, 508, 508, 508, 508, 508, + 508, 508, 508, 508, 508, 508, 508, 508, 508, 508, + 508, 508, 508, 508, 508, 508, 508, 508, 508, 508, + 508, 508, 508, 508, 508, 508, 508, 508, 508, 508, + 508, 508, 508, 508, 508, 508, 508, 508, 508, 508, + 508, 508, 508, 508, 508, 508, 508, 508 }, { - 67, -509, -509, -509, -509, -509, -509, -509, -509, -509, - -509, -509, -509, -509, -509, -509, -509, -509, -509, -509, - -509, -509, -509, -509, -509, -509, -509, -509, -509, -509, + 69, 509, 509, 509, 509, 509, 509, 509, 509, 509, + 510, 509, 509, 509, 509, 509, 509, 509, 509, 509, + 509, 509, 509, 509, 509, 509, 509, 509, 509, 509, - -509, -509, 632, -509, -509, -509, -509, -509, -509, -509, - -509, -509, -509, -509, -509, -509, -509, -509, 633, 633, - 633, 633, 633, 633, 633, 633, 633, 633, -509, -509, - -509, -509, -509, -509, -509, 632, 632, 632, 632, 632, - 632, 632, 632, 632, 632, 632, 632, 632, 632, 632, - 632, 632, 632, 632, 632, 632, 632, 632, 632, 632, - 632, -509, -509, -509, -509, -509, -509, -509, -509, -509, - -509, -509, -509, -509, -509, -509, -509, -509, -509, -509, - -509, -509, -509, -509, -509, -509, -509, -509, -509, -509, - -509, -509, -509, -509, -509, -509, -509, -509 + 509, 509, 511, 509, 509, 509, 509, 509, 509, 509, + 509, 509, 509, 509, 509, 509, 509, 512, 509, 509, + 509, 509, 509, 509, 509, 509, 509, 509, 509, 509, + 509, 509, 509, 509, 509, 509, 509, 509, 509, 509, + 509, 509, 509, 509, 509, 509, 509, 509, 509, 509, + 509, 509, 509, 509, 509, 509, 509, 509, 509, 509, + 509, 509, 509, 509, 509, 509, 509, 509, 509, 509, + 509, 509, 509, 509, 509, 509, 509, 509, 509, 509, + 509, 509, 509, 509, 509, 509, 509, 509, 509, 509, + 509, 509, 509, 509, 509, 509, 509, 509 }, { - 67, -510, -510, -510, -510, -510, -510, -510, -510, -510, + 69, -510, -510, -510, -510, -510, -510, -510, -510, -510, + -510, -510, -510, -510, -510, -510, -510, -510, -510, -510, -510, -510, -510, -510, -510, -510, -510, -510, -510, -510, -510, -510, -510, -510, -510, -510, -510, -510, -510, -510, - -510, -510, 634, -510, -510, -510, -510, -510, -510, -510, -510, -510, -510, -510, -510, -510, -510, -510, -510, -510, -510, -510, -510, -510, -510, -510, -510, -510, -510, -510, -510, -510, -510, -510, -510, -510, -510, -510, -510, -510, @@ -9218,59 +9379,59 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -511, -511, -511, -511, -511, -511, -511, -511, -511, - -511, -511, -511, -511, -511, -511, -511, -511, -511, -511, - -511, -511, -511, -511, -511, -511, -511, -511, -511, -511, - -511, -511, 635, -511, -511, -511, -511, -511, -511, -511, - -511, -511, -511, -511, -511, -511, -511, -511, 636, 636, - 636, 636, 636, 636, 636, 636, 636, 636, -511, -511, - -511, -511, -511, -511, -511, 635, 635, 635, 635, 635, + 69, 509, 509, 509, 509, 509, 509, 509, 509, 509, + 510, 509, 509, 509, 509, 509, 509, 509, 509, 509, + 509, 509, 509, 509, 509, 509, 509, 509, 509, 509, + 509, 509, 511, 509, 509, 509, 509, 509, 509, 509, + 509, 509, 509, 509, 509, 509, 509, 512, 509, 509, + 509, 509, 509, 509, 509, 509, 509, 509, 509, 509, + 509, 509, 509, 509, 509, 509, 509, 509, 509, 509, - 635, 635, 635, 635, 635, 635, 635, 635, 635, 635, - 635, 635, 635, 635, 635, 635, 635, 635, 635, 635, - 635, -511, -511, -511, -511, -511, -511, -511, -511, -511, - -511, -511, -511, -511, -511, -511, -511, -511, -511, -511, - -511, -511, -511, -511, -511, -511, -511, -511, -511, -511, - -511, -511, -511, -511, -511, -511, -511, -511 + 509, 509, 509, 509, 509, 509, 509, 509, 509, 509, + 509, 509, 509, 509, 509, 509, 509, 509, 509, 509, + 509, 509, 509, 509, 509, 509, 509, 509, 509, 509, + 509, 509, 509, 509, 509, 509, 509, 509, 509, 509, + 509, 509, 509, 509, 509, 509, 509, 509, 509, 509, + 509, 509, 509, 509, 509, 509, 509, 509 }, { - 67, -512, -512, -512, -512, -512, -512, -512, -512, -512, - -512, -512, -512, -512, -512, -512, -512, -512, -512, -512, - -512, -512, -512, -512, -512, -512, -512, -512, -512, -512, - -512, -512, -512, -512, -512, -512, -512, -512, -512, -512, + 69, 509, 509, 509, 509, 509, 509, 509, 509, 509, + 510, 509, 509, 509, 509, 509, 509, 509, 509, 509, + 509, 509, 509, 509, 509, 509, 509, 509, 509, 509, + 509, 509, 511, 509, 509, 509, 509, 509, 509, 509, - -512, -512, -512, -512, -512, -512, -512, -512, -512, -512, - -512, -512, -512, -512, -512, -512, -512, -512, -512, -512, - -512, -512, -512, -512, -512, -512, -512, -512, -512, -512, - -512, -512, -512, -512, -512, -512, -512, -512, -512, -512, - -512, -512, -512, -512, -512, -512, -512, -512, -512, -512, - -512, -512, -512, -512, -512, -512, -512, -512, -512, -512, - -512, -512, -512, -512, -512, -512, -512, -512, -512, -512, - -512, -512, -512, -512, -512, -512, -512, -512, -512, -512, - -512, -512, -512, -512, -512, -512, -512, -512 + 509, 509, 509, 509, 509, 509, 509, 512, 509, 509, + 509, 509, 509, 509, 509, 509, 509, 509, 509, 509, + 509, 509, 509, 509, 509, 509, 509, 509, 509, 509, + 509, 509, 509, 509, 509, 509, 509, 509, 509, 509, + 509, 509, 509, 509, 509, 509, 509, 509, 509, 509, + 509, 509, 509, 509, 509, 509, 509, 509, 509, 509, + 509, 509, 509, 509, 509, 509, 509, 509, 509, 509, + 509, 509, 509, 509, 509, 509, 509, 509, 509, 509, + 509, 509, 509, 509, 509, 509, 509, 509 }, { - 67, -513, -513, -513, -513, -513, -513, -513, -513, -513, + 69, 513, 513, 513, 513, 513, 513, 513, 513, 513, - -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, - -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, - -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, - -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, - -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, - -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, - -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, - -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, - -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, - -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, + 514, 513, 513, 513, 513, 513, 513, 513, 513, 513, + 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, + 513, 513, 515, 513, 513, 513, 513, 513, 513, 513, + 513, 513, 513, 513, 513, 513, 513, 516, 513, 513, + 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, + 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, + 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, + 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, + 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, + 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, - -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, - -513, -513, -513, -513, -513, -513, -513, -513 + 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, + 513, 513, 513, 513, 513, 513, 513, 513 }, { - 67, -514, -514, -514, -514, -514, -514, -514, -514, -514, + 69, -514, -514, -514, -514, -514, -514, -514, -514, -514, -514, -514, -514, -514, -514, -514, -514, -514, -514, -514, -514, -514, -514, -514, -514, -514, -514, -514, -514, -514, -514, -514, -514, -514, -514, -514, -514, -514, -514, -514, @@ -9287,59 +9448,59 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -515, -515, -515, -515, -515, -515, -515, -515, -515, - -515, -515, -515, -515, -515, -515, -515, -515, -515, -515, - -515, -515, -515, -515, -515, -515, -515, -515, -515, -515, - -515, -515, -515, -515, -515, -515, -515, -515, -515, -515, - -515, -515, -515, -515, -515, -515, -515, -515, -515, -515, + 69, 513, 513, 513, 513, 513, 513, 513, 513, 513, + 514, 513, 513, 513, 513, 513, 513, 513, 513, 513, + 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, + 513, 513, 515, 513, 513, 513, 513, 513, 513, 513, + 513, 513, 513, 513, 513, 513, 513, 516, 513, 513, - -515, -515, -515, -515, -515, -515, -515, -515, -515, -515, - -515, -515, -515, -515, -515, -515, -515, -515, -515, -515, - -515, -515, -515, -515, -515, -515, -515, -515, -515, -515, - -515, -515, -515, -515, -515, -515, -515, -515, -515, -515, - -515, -515, -515, -515, -515, -515, -515, -515, -515, -515, - -515, -515, -515, -515, -515, -515, -515, -515, -515, -515, - -515, -515, -515, -515, -515, -515, -515, -515, -515, -515, - -515, -515, -515, -515, -515, -515, -515, -515 + 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, + 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, + 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, + 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, + 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, + 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, + 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, + 513, 513, 513, 513, 513, 513, 513, 513 }, { - 67, -516, -516, -516, -516, -516, -516, -516, -516, -516, - -516, -516, -516, -516, -516, -516, -516, -516, -516, -516, + 69, 716, 716, 716, 716, 716, 716, 716, 716, 716, + 717, 716, 716, 716, 716, 716, 716, 716, 716, 716, - -516, -516, -516, -516, -516, -516, -516, -516, -516, -516, - -516, -516, -516, -516, -516, -516, -516, -516, -516, -516, - -516, -516, -516, -516, -516, -516, -516, -516, -516, -516, - -516, -516, -516, -516, -516, -516, -516, -516, -516, -516, - -516, -516, -516, -516, -516, -516, -516, -516, -516, -516, - -516, -516, -516, -516, -516, -516, -516, -516, -516, -516, - -516, -516, -516, -516, -516, -516, -516, -516, -516, -516, - -516, -516, -516, -516, -516, -516, -516, -516, -516, -516, - -516, -516, -516, -516, -516, -516, -516, -516, -516, -516, - -516, -516, -516, -516, -516, -516, -516, -516, -516, -516, + 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, + 716, 716, 718, 716, 716, 716, 716, 716, 716, 716, + 716, 716, 716, 716, 716, 716, 716, 719, 716, 716, + 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, + 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, + 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, + 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, + 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, + 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, + 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, - -516, -516, -516, -516, -516, -516, -516, -516 + 716, 716, 716, 716, 716, 716, 716, 716 }, { - 67, -517, -517, -517, -517, -517, -517, -517, -517, -517, - -517, -517, -517, -517, -517, -517, -517, -517, -517, -517, - -517, -517, -517, -517, -517, -517, -517, -517, -517, -517, - -517, -517, -517, -517, -517, -517, -517, -517, -517, -517, - -517, -517, -517, -517, -517, -517, -517, -517, -517, -517, - -517, -517, -517, -517, -517, -517, -517, -517, -517, -517, - -517, -517, -517, -517, -517, -517, -517, -517, -517, -517, - -517, -517, -517, -517, -517, -517, -517, -517, -517, -517, - -517, -517, -517, -517, -517, -517, -517, -517, -517, -517, + 69, 513, 513, 513, 513, 513, 513, 513, 513, 513, + 514, 513, 513, 513, 513, 513, 513, 513, 513, 513, + 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, + 513, 513, 515, 513, 513, 513, 513, 513, 513, 513, + 513, 513, 513, 513, 513, 513, 513, 516, 513, 513, + 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, + 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, + 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, + 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, - -517, -517, -517, -517, -517, -517, -517, -517, -517, -517, - -517, -517, -517, -517, -517, -517, -517, -517, -517, -517, - -517, -517, -517, -517, -517, -517, -517, -517, -517, -517, - -517, -517, -517, -517, -517, -517, -517, -517 + 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, + 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, + 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, + 513, 513, 513, 513, 513, 513, 513, 513 }, { - 67, -518, -518, -518, -518, -518, -518, -518, -518, -518, + 69, -518, -518, -518, -518, -518, -518, -518, -518, -518, -518, -518, -518, -518, -518, -518, -518, -518, -518, -518, -518, -518, -518, -518, -518, -518, -518, -518, -518, -518, -518, -518, -518, -518, -518, -518, -518, -518, -518, -518, @@ -9356,42 +9517,42 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -519, -519, -519, -519, -519, -519, -519, -519, -519, - -519, -519, -519, -519, -519, -519, -519, -519, -519, -519, - -519, -519, -519, -519, -519, -519, -519, -519, -519, -519, + 69, 517, 517, 517, 517, 517, 517, 517, 517, 517, + 518, 517, 517, 517, 517, 517, 517, 517, 517, 517, + 517, 517, 517, 517, 517, 517, 517, 517, 517, 517, - -519, -519, -519, -519, -519, -519, -519, -519, -519, -519, - -519, -519, -519, -519, -519, -519, -519, -519, -519, -519, - -519, -519, -519, -519, -519, -519, -519, -519, -519, -519, - -519, -519, -519, -519, -519, -519, -519, -519, -519, -519, - -519, -519, -519, -519, -519, -519, -519, -519, -519, -519, - -519, -519, -519, -519, -519, -519, -519, -519, -519, -519, - -519, -519, -519, -519, -519, -519, -519, -519, -519, -519, - -519, -519, -519, -519, -519, -519, -519, -519, -519, -519, - -519, -519, -519, -519, -519, -519, -519, -519, -519, -519, - -519, -519, -519, -519, -519, -519, -519, -519 + 517, 517, 519, 517, 517, 517, 517, 517, 517, 517, + 517, 517, 517, 517, 517, 517, 517, 520, 517, 517, + 517, 517, 517, 517, 517, 517, 517, 517, 517, 517, + 517, 517, 517, 517, 517, 517, 517, 517, 517, 517, + 517, 517, 517, 517, 517, 517, 517, 517, 517, 517, + 517, 517, 517, 517, 517, 517, 517, 517, 517, 517, + 517, 517, 517, 517, 517, 517, 517, 517, 517, 517, + 517, 517, 517, 517, 517, 517, 517, 517, 517, 517, + 517, 517, 517, 517, 517, 517, 517, 517, 517, 517, + 517, 517, 517, 517, 517, 517, 517, 517 }, { - 67, -520, -520, -520, -520, -520, -520, -520, -520, -520, - -520, -520, -520, -520, -520, -520, -520, -520, -520, -520, - -520, -520, -520, -520, -520, -520, -520, -520, -520, -520, - -520, -520, -520, -520, -520, -520, -520, -520, -520, -520, - -520, -520, -520, -520, -520, -520, -520, -520, -520, -520, - -520, -520, -520, -520, -520, -520, -520, -520, -520, -520, - -520, -520, -520, -520, -520, -520, -520, -520, -520, -520, - -520, -520, -520, -520, -520, -520, -520, -520, -520, -520, - -520, -520, -520, -520, -520, -520, -520, -520, -520, -520, - -520, -520, -520, -520, -520, -520, -520, -520, -520, -520, + 69, 720, 720, 720, 720, 720, 720, 720, 720, 720, + 721, 720, 720, 720, 720, 720, 720, 720, 720, 720, + 720, 720, 720, 720, 720, 720, 720, 720, 720, 720, + 720, 720, 722, 720, 720, 720, 720, 720, 720, 720, + 720, 720, 720, 720, 720, 720, 720, 723, 720, 720, + 720, 720, 720, 720, 720, 720, 720, 720, 720, 720, + 720, 720, 720, 720, 720, 720, 720, 720, 720, 720, + 720, 720, 720, 720, 720, 720, 720, 720, 720, 720, + 720, 720, 720, 720, 720, 720, 720, 720, 720, 720, + 720, 720, 720, 720, 720, 720, 720, 720, 720, 720, - -520, -520, -520, -520, -520, -520, -520, -520, -520, -520, - -520, -520, -520, -520, -520, -520, -520, -520, -520, -520, - -520, -520, -520, -520, -520, -520, -520, -520 + 720, 720, 720, 720, 720, 720, 720, 720, 720, 720, + 720, 720, 720, 720, 720, 720, 720, 720, 720, 720, + 720, 720, 720, 720, 720, 720, 720, 720 }, { - 67, -521, -521, -521, -521, -521, -521, -521, -521, -521, + 69, -521, -521, -521, -521, -521, -521, -521, -521, -521, -521, -521, -521, -521, -521, -521, -521, -521, -521, -521, -521, -521, -521, -521, -521, -521, -521, -521, -521, -521, -521, -521, -521, -521, -521, -521, -521, -521, -521, -521, @@ -9408,7 +9569,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -522, -522, -522, -522, -522, -522, -522, -522, -522, + 69, -522, -522, -522, -522, -522, -522, -522, -522, -522, -522, -522, -522, -522, -522, -522, -522, -522, -522, -522, -522, -522, -522, -522, -522, -522, -522, -522, -522, -522, -522, -522, -522, -522, -522, -522, -522, -522, -522, -522, @@ -9425,7 +9586,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -523, -523, -523, -523, -523, -523, -523, -523, -523, + 69, -523, -523, -523, -523, -523, -523, -523, -523, -523, -523, -523, -523, -523, -523, -523, -523, -523, -523, -523, -523, -523, -523, -523, -523, -523, -523, -523, -523, -523, @@ -9443,7 +9604,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -524, -524, -524, -524, -524, -524, -524, -524, -524, + 69, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, @@ -9452,7 +9613,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, - -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, + -524, -524, -524, -524, 724, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, @@ -9460,7 +9621,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -525, -525, -525, -525, -525, -525, -525, -525, -525, + 69, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, @@ -9468,7 +9629,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, - -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, + -525, -525, -525, -525, -525, -525, -525, 725, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, @@ -9477,7 +9638,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -526, -526, -526, -526, -526, -526, -526, -526, -526, + 69, -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, @@ -9486,7 +9647,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, - -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, + -526, -526, 726, -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, @@ -9495,7 +9656,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -527, -527, -527, -527, -527, -527, -527, -527, -527, + 69, -527, -527, -527, -527, -527, -527, -527, -527, -527, -527, -527, -527, -527, -527, -527, -527, -527, -527, -527, -527, -527, -527, -527, -527, -527, -527, -527, -527, -527, -527, -527, -527, -527, -527, -527, -527, -527, -527, -527, @@ -9512,14 +9673,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -528, -528, -528, -528, -528, -528, -528, -528, -528, + 69, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, - -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, + -528, -528, -528, -528, -528, -528, -528, -528, -528, 727, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, @@ -9529,7 +9690,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -529, -529, -529, -529, -529, -529, -529, -529, -529, + 69, -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, @@ -9538,7 +9699,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, - -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, + -529, -529, -529, -529, 728, -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, @@ -9547,8 +9708,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -530, -530, -530, -530, -530, -530, -530, -530, -530, - -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, + 69, -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, @@ -9556,6 +9716,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, + -530, -530, -530, -530, -530, -530, -530, -530, 729, -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, @@ -9564,7 +9725,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -531, -531, -531, -531, -531, -531, -531, -531, -531, + 69, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, @@ -9572,7 +9733,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, - -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, + -531, -531, -531, -531, -531, -531, 730, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, @@ -9581,14 +9742,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -532, -532, -532, -532, -532, -532, -532, -532, -532, + 69, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, - -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, + -532, -532, -532, -532, -532, -532, -532, -532, -532, 731, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, @@ -9598,7 +9759,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -533, -533, -533, -533, -533, -533, -533, -533, -533, + 69, -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, @@ -9607,7 +9768,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, - -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, + 732, -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, @@ -9616,14 +9777,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -534, -534, -534, -534, -534, -534, -534, -534, -534, - -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, + 69, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, + -534, -534, -534, 733, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, @@ -9633,7 +9794,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -535, -535, -535, -535, -535, -535, -535, -535, -535, + 69, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, @@ -9641,7 +9802,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, - -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, + -535, -535, 734, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, @@ -9650,14 +9811,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -536, -536, -536, -536, -536, -536, -536, -536, -536, + 69, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, - -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, + -536, -536, -536, -536, -536, -536, -536, -536, 735, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, @@ -9668,33 +9829,33 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -537, -537, -537, -537, -537, -537, -537, -537, -537, + 69, -537, -537, -537, -537, -537, -537, -537, -537, -537, + -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, - -537, -537, -537, -537, -537, 637, -537, -537, -537, -537, - -537, -537, -537, -537, -537, -537, -537, -537, -537, 638, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, - -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, + + -537, -537, -537, -537, -537, 736, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, -537 }, { - 67, -538, -538, -538, -538, -538, -538, -538, -538, -538, + 69, -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, - -538, -538, 639, -538, -538, -538, -538, -538, -538, -538, - -538, -538, -538, -538, -538, -538, -538, -538, 640, 640, - 640, 640, 640, 640, 640, 640, 640, 640, -538, -538, - -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, + + -538, -538, -538, -538, -538, -538, -538, 737, -538, -538, + -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, + -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, @@ -9702,17 +9863,17 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -539, -539, -539, -539, -539, -539, -539, -539, -539, + 69, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, - -539, -539, 641, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, + -539, -539, -539, -539, -539, 738, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, -539 @@ -9720,16 +9881,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -540, -540, -540, -540, -540, -540, -540, -540, -540, + 69, -540, -540, -540, -540, -540, -540, -540, -540, -540, + -540, -540, -540, -540, -540, -540, -540, -540, -540, -540, -540, -540, -540, -540, -540, -540, -540, -540, -540, -540, -540, -540, -540, -540, -540, -540, -540, -540, -540, -540, - -540, -540, 642, -540, -540, -540, -540, -540, -540, -540, -540, -540, -540, -540, -540, -540, -540, -540, -540, -540, -540, -540, -540, -540, -540, -540, -540, -540, -540, -540, - -540, -540, -540, -540, -540, 642, 642, 642, 642, 642, - 642, 642, 642, 642, 642, 642, 642, 642, 642, 642, - 642, 642, 642, 642, 642, 642, 642, 642, 642, 642, - 642, -540, -540, -540, -540, -540, -540, -540, -540, -540, + -540, -540, -540, -540, -540, -540, -540, -540, -540, -540, + -540, -540, -540, -540, -540, -540, -540, -540, -540, -540, + -540, -540, -540, -540, -540, -540, -540, -540, -540, -540, + -540, -540, -540, -540, -540, 739, -540, -540, -540, -540, -540, -540, -540, -540, -540, -540, -540, -540, -540, -540, -540, -540, -540, -540, -540, -540, -540, -540, -540, -540, @@ -9737,15 +9898,15 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -541, -541, -541, -541, -541, -541, -541, -541, -541, - -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, + 69, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, + -541, -541, -541, -541, -541, -541, -541, -541, 740, -541, - -541, -541, -541, -541, -541, -541, -541, -541, -541, 643, + -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, @@ -9754,7 +9915,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -542, -542, -542, -542, -542, -542, -542, -542, -542, + 69, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, @@ -9762,8 +9923,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, - -542, -542, -542, -542, -542, -542, 644, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, + -542, -542, -542, -542, 741, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, @@ -9771,15 +9932,15 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -543, -543, -543, -543, -543, -543, -543, -543, -543, + 69, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, + -543, -543, -543, -543, -543, -543, -543, -543, -543, 742, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, - -543, -543, -543, -543, -543, -543, 645, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, @@ -9789,16 +9950,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -544, -544, -544, -544, -544, -544, -544, -544, -544, - -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, + 69, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, + -544, -544, -544, 743, -544, -544, -544, -544, -544, -544, - -544, -544, -544, -544, -544, -544, 646, -544, -544, -544, + -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, @@ -9806,16 +9967,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -545, -545, -545, -545, -545, -545, -545, -545, -545, + 69, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, - -545, -545, -545, -545, -545, -545, 647, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, + -545, -545, 744, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, @@ -9823,7 +9984,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -546, -546, -546, -546, -546, -546, -546, -546, -546, + 69, -546, -546, -546, -546, -546, -546, -546, -546, -546, -546, -546, -546, -546, -546, -546, -546, -546, -546, -546, -546, -546, -546, -546, -546, -546, -546, -546, -546, -546, @@ -9833,7 +9994,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -546, -546, -546, -546, -546, -546, -546, -546, -546, -546, -546, -546, -546, -546, -546, -546, -546, -546, -546, -546, -546, -546, -546, -546, -546, -546, -546, -546, -546, -546, - -546, -546, -546, -546, -546, -546, -546, -546, -546, -546, + -546, -546, -546, -546, -546, 745, -546, -546, -546, -546, -546, -546, -546, -546, -546, -546, -546, -546, -546, -546, -546, -546, -546, -546, -546, -546, -546, -546, -546, -546, @@ -9841,31 +10002,31 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -547, -547, -547, -547, -547, -547, -547, -547, -547, + 69, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, - -547, -547, -547, -547, -547, -547, -547, -547, -547, 648, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, - -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, + + -547, -547, -547, -547, -547, 746, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, -547 }, { - 67, -548, -548, -548, -548, -548, -548, -548, -548, -548, + 69, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, - -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, + -548, -548, -548, -548, -548, 747, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, @@ -9875,7 +10036,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -549, -549, -549, -549, -549, -549, -549, -549, -549, + 69, -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, @@ -9884,7 +10045,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, - -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, + -549, -549, -549, -549, -549, -549, -549, -549, 748, -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, @@ -9893,14 +10054,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -550, -550, -550, -550, -550, -550, -550, -550, -550, - -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, + 69, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, + -550, -550, -550, -550, -550, -550, 749, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, -550, @@ -9910,7 +10071,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -551, -551, -551, -551, -551, -551, -551, -551, -551, + 69, -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, @@ -9919,7 +10080,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, - -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, + -551, -551, 750, -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, @@ -9927,16 +10088,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -552, -552, -552, -552, -552, -552, -552, -552, -552, + 69, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, + -552, -552, -552, -552, -552, -552, -552, -552, -552, 751, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, - -552, -552, -552, 649, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, @@ -9944,7 +10105,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -553, -553, -553, -553, -553, -553, -553, -553, -553, + 69, -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, @@ -9953,7 +10114,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, - -553, -553, -553, -553, -553, -553, -553, -553, -553, 650, + -553, -553, -553, -553, 752, -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, -553, @@ -9962,7 +10123,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -554, -554, -554, -554, -554, -554, -554, -554, -554, + 69, -554, -554, -554, -554, -554, -554, -554, -554, -554, -554, -554, -554, -554, -554, -554, -554, -554, -554, -554, -554, -554, -554, -554, -554, -554, -554, -554, -554, -554, -554, -554, -554, -554, -554, -554, -554, -554, -554, -554, @@ -9971,7 +10132,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -554, -554, -554, -554, -554, -554, -554, -554, -554, -554, -554, -554, -554, -554, -554, -554, -554, -554, -554, -554, - -554, -554, 651, -554, -554, -554, -554, -554, -554, -554, + -554, -554, -554, 753, -554, -554, -554, -554, -554, -554, -554, -554, -554, -554, -554, -554, -554, -554, -554, -554, -554, -554, -554, -554, -554, -554, -554, -554, -554, -554, -554, -554, -554, -554, -554, -554, -554, -554, -554, -554, @@ -9979,14 +10140,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -555, -555, -555, -555, -555, -555, -555, -555, -555, + 69, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, - -555, -555, -555, -555, -555, 652, -555, -555, -555, -555, + -555, -555, -555, -555, -555, -555, -555, -555, 754, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, -555, @@ -9996,16 +10157,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -556, -556, -556, -556, -556, -556, -556, -556, -556, + 69, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, + -556, -556, -556, -556, -556, 755, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, - -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, - -556, -556, -556, -556, -556, -556, -556, -556, -556, 653, + -556, -556, 756, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, @@ -10014,13 +10175,13 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -557, -557, -557, -557, -557, -557, -557, -557, -557, + 69, -557, -557, -557, -557, -557, -557, -557, -557, -557, + -557, -557, -557, -557, -557, -557, -557, -557, -557, -557, -557, -557, -557, -557, -557, -557, -557, -557, -557, -557, -557, -557, -557, -557, -557, -557, -557, -557, -557, -557, -557, -557, -557, -557, -557, -557, -557, -557, -557, -557, -557, -557, -557, -557, -557, -557, -557, -557, -557, -557, -557, -557, -557, -557, -557, -557, -557, -557, -557, -557, - -557, -557, -557, -557, -557, -557, 654, -557, -557, -557, -557, -557, -557, -557, -557, -557, -557, -557, -557, -557, -557, -557, -557, -557, -557, -557, -557, -557, -557, -557, @@ -10031,16 +10192,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -558, -558, -558, -558, -558, -558, -558, -558, -558, - -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, - -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, + 69, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, + -558, -558, -558, -558, -558, -558, -558, -558, -558, 757, + 757, 757, 757, 757, 757, 757, 757, 757, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, - -558, -558, 655, -558, -558, -558, -558, -558, -558, -558, + -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, -558, @@ -10048,7 +10209,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -559, -559, -559, -559, -559, -559, -559, -559, -559, + 69, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, @@ -10058,7 +10219,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, - -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, + -559, -559, -559, -559, -559, 758, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559, -559 @@ -10066,10 +10227,10 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -560, -560, -560, -560, -560, -560, -560, -560, -560, - -560, -560, -560, -560, -560, -560, -560, -560, -560, -560, + 69, -560, -560, -560, -560, -560, -560, -560, -560, -560, -560, -560, -560, -560, -560, -560, -560, -560, -560, -560, -560, -560, -560, -560, -560, -560, -560, -560, -560, -560, + -560, -560, 759, -560, -560, -560, -560, -560, -560, -560, -560, -560, -560, -560, -560, -560, -560, -560, -560, -560, -560, -560, -560, -560, -560, -560, -560, -560, -560, -560, -560, -560, -560, -560, -560, -560, -560, -560, -560, -560, @@ -10083,7 +10244,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -561, -561, -561, -561, -561, -561, -561, -561, -561, + 69, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, @@ -10091,7 +10252,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, - -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, + -561, -561, 760, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, -561, @@ -10100,7 +10261,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -562, -562, -562, -562, -562, -562, -562, -562, -562, + 69, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, @@ -10108,7 +10269,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, - -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, + -562, -562, -562, -562, -562, -562, -562, -562, 761, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, -562, @@ -10117,7 +10278,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -563, -563, -563, -563, -563, -563, -563, -563, -563, + 69, -563, -563, -563, -563, -563, -563, -563, -563, -563, -563, -563, -563, -563, -563, -563, -563, -563, -563, -563, -563, -563, -563, -563, -563, -563, -563, -563, -563, -563, @@ -10127,7 +10288,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -563, -563, -563, -563, -563, -563, -563, -563, -563, -563, -563, -563, -563, -563, -563, -563, -563, -563, -563, -563, -563, -563, -563, -563, -563, -563, -563, -563, -563, -563, - -563, -563, -563, -563, -563, -563, -563, -563, -563, -563, + -563, -563, -563, -563, -563, 762, -563, -563, -563, -563, -563, -563, -563, -563, -563, -563, -563, -563, -563, -563, -563, -563, -563, -563, -563, -563, -563, -563, -563, -563, @@ -10135,7 +10296,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -564, -564, -564, -564, -564, -564, -564, -564, -564, + 69, -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, @@ -10145,21 +10306,21 @@ static yyconst flex_int16_t yy_nxt[][128] = -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, - -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, + -564, -564, -564, -564, -564, 763, -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, -564 }, { - 67, -565, -565, -565, -565, -565, -565, -565, -565, -565, + 69, -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, - -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, + -565, -565, -565, -565, -565, -565, -565, -565, -565, 764, -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, @@ -10169,7 +10330,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -566, -566, -566, -566, -566, -566, -566, -566, -566, + 69, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, @@ -10177,7 +10338,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, - -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, + 765, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, @@ -10187,13 +10348,13 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -567, -567, -567, -567, -567, -567, -567, -567, -567, - -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, + 69, -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, + -567, -567, -567, -567, -567, -567, -567, 766, -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, @@ -10204,15 +10365,15 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -568, -568, -568, -568, -568, -568, -568, -568, -568, + 69, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, - -568, -568, -568, -568, -568, -568, -568, -568, 656, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, + -568, -568, -568, -568, -568, -568, -568, -568, -568, 767, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, @@ -10221,7 +10382,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -569, -569, -569, -569, -569, -569, -569, -569, -569, + 69, -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, @@ -10229,7 +10390,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, - -569, 657, -569, -569, -569, -569, -569, -569, -569, -569, + -569, -569, -569, -569, -569, -569, -569, -569, -569, 768, -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, @@ -10239,15 +10400,15 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -570, -570, -570, -570, -570, -570, -570, -570, -570, + 69, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, - -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, - -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, - -570, -570, -570, -570, -570, -570, -570, -570, -570, 658, + -570, -570, -570, -570, -570, 769, 770, -570, -570, 771, + -570, -570, -570, -570, -570, -570, -570, -570, -570, 772, + -570, -570, 773, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, @@ -10256,7 +10417,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -571, -571, -571, -571, -571, -571, -571, -571, -571, + 69, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, @@ -10264,7 +10425,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, - 659, -571, -571, -571, -571, -571, -571, -571, -571, -571, + -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, @@ -10273,31 +10434,31 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -572, -572, -572, -572, -572, -572, -572, -572, -572, + 69, -572, -572, -572, -572, -572, -572, -572, -572, -572, + -572, -572, -572, -572, -572, -572, -572, -572, -572, -572, + -572, -572, -572, -572, -572, -572, -572, -572, -572, -572, + -572, -572, -572, -572, -572, -572, -572, -572, -572, -572, + + -572, -572, -572, -572, -572, -572, -572, -572, -572, -572, + -572, -572, -572, -572, -572, -572, -572, -572, -572, -572, + -572, -572, -572, -572, -572, -572, 774, -572, -572, -572, + -572, -572, -572, -572, -572, -572, -572, -572, -572, -572, -572, -572, -572, -572, -572, -572, -572, -572, -572, -572, -572, -572, -572, -572, -572, -572, -572, -572, -572, -572, - -572, -572, 660, -572, -572, -572, -572, -572, -572, -572, - - -572, -572, -572, -572, -572, -572, -572, -572, 661, 661, - 661, 661, 661, 661, 661, 661, 661, 661, -572, -572, - -572, -572, -572, -572, -572, 660, 660, 660, 660, 660, - 660, 660, 660, 660, 660, 660, 660, 660, 660, 660, - 660, 660, 660, 660, 660, 660, 660, 660, 660, 660, - 660, -572, -572, -572, -572, -572, -572, -572, -572, -572, -572, -572, -572, -572, -572, -572, -572, -572, -572, -572, -572, -572, -572, -572, -572, -572, -572, -572, -572, -572, -572, -572, -572, -572, -572, -572, -572, -572 }, { - 67, -573, -573, -573, -573, -573, -573, -573, -573, -573, + 69, -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, - -573, -573, -573, -573, -573, -573, -573, 662, -573, -573, + -573, -573, -573, -573, -573, -573, -573, -573, -573, 775, -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, @@ -10308,33 +10469,33 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -574, -574, -574, -574, -574, -574, -574, -574, -574, + 69, -574, -574, -574, -574, -574, -574, -574, -574, -574, -574, -574, -574, -574, -574, -574, -574, -574, -574, -574, -574, -574, -574, -574, -574, -574, -574, -574, -574, -574, - -574, -574, 663, -574, -574, -574, -574, -574, -574, -574, - -574, -574, -574, -574, -574, -574, -574, -574, 664, 664, - 664, 664, 664, 664, 664, 664, 664, 664, -574, -574, - -574, -574, -574, -574, -574, 663, 663, 663, 663, 663, - 663, 663, 663, 663, 663, 663, 663, 663, 663, 663, + -574, -574, -574, -574, -574, -574, -574, -574, -574, -574, + -574, -574, -574, -574, -574, -574, -574, -574, -574, -574, + -574, -574, -574, -574, -574, -574, -574, -574, -574, -574, + -574, -574, -574, -574, -574, -574, 776, -574, -574, 777, + -574, -574, 778, -574, -574, -574, 779, -574, -574, -574, - 663, 663, 663, 663, 663, 663, 663, 663, 663, 663, - 663, -574, -574, -574, -574, -574, -574, -574, -574, -574, + -574, -574, -574, -574, -574, -574, -574, -574, 780, 781, + 782, -574, -574, -574, -574, -574, -574, -574, -574, -574, -574, -574, -574, -574, -574, -574, -574, -574, -574, -574, -574, -574, -574, -574, -574, -574, -574, -574, -574, -574, -574, -574, -574, -574, -574, -574, -574, -574 }, { - 67, -575, -575, -575, -575, -575, -575, -575, -575, -575, + 69, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, - -575, -575, -575, -575, -575, -575, -575, -575, -575, 665, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, + -575, -575, 783, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, @@ -10342,7 +10503,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -576, -576, -576, -576, -576, -576, -576, -576, -576, + 69, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, @@ -10350,7 +10511,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, - -576, -576, -576, -576, -576, -576, -576, 666, -576, -576, + -576, -576, 784, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, @@ -10360,50 +10521,50 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -577, -577, -577, -577, -577, -577, -577, -577, -577, + 69, -577, -577, -577, -577, -577, -577, -577, -577, -577, + -577, -577, -577, -577, -577, -577, -577, -577, -577, -577, -577, -577, -577, -577, -577, -577, -577, -577, -577, -577, -577, -577, -577, -577, -577, -577, -577, -577, -577, -577, - -577, -577, 667, -577, -577, -577, -577, -577, -577, -577, - -577, -577, -577, -577, -577, -577, -577, -577, 668, 668, - 668, 668, 668, 668, 668, 668, 668, 668, -577, -577, - -577, -577, -577, -577, -577, 667, 667, 667, 667, 667, - 667, 667, 667, 667, 667, 667, 667, 667, 667, 667, - 667, 667, 667, 667, 667, 667, 667, 667, 667, 667, - - 667, -577, -577, -577, -577, -577, -577, -577, -577, -577, + -577, -577, -577, -577, -577, -577, -577, -577, -577, -577, + -577, -577, -577, -577, -577, -577, -577, -577, -577, -577, + -577, -577, -577, -577, -577, -577, -577, -577, -577, -577, + -577, -577, -577, -577, -577, -577, -577, -577, -577, -577, + 785, -577, -577, -577, -577, -577, -577, -577, -577, -577, + + -577, -577, -577, -577, -577, -577, -577, -577, -577, -577, -577, -577, -577, -577, -577, -577, -577, -577, -577, -577, -577, -577, -577, -577, -577, -577, -577, -577, -577, -577, -577, -577, -577, -577, -577, -577, -577, -577 }, { - 67, -578, -578, -578, -578, -578, -578, -578, -578, -578, + 69, -578, -578, -578, -578, -578, -578, -578, -578, -578, + -578, -578, -578, -578, -578, -578, -578, -578, -578, -578, + -578, -578, -578, -578, -578, -578, -578, -578, -578, -578, -578, -578, -578, -578, -578, -578, -578, -578, -578, -578, -578, -578, -578, -578, -578, -578, -578, -578, -578, -578, - -578, -578, 669, -578, -578, -578, -578, -578, -578, -578, - -578, -578, -578, -578, -578, -578, -578, -578, 670, 670, - 670, 670, 670, 670, 670, 670, 670, 670, -578, -578, - - -578, -578, -578, -578, -578, 669, 669, 669, 669, 669, - 669, 669, 669, 669, 669, 669, 669, 669, 669, 669, - 669, 669, 669, 669, 669, 669, 669, 669, 669, 669, - 669, -578, -578, -578, -578, -578, -578, -578, -578, -578, + -578, -578, -578, -578, -578, -578, -578, -578, -578, -578, + + -578, -578, -578, -578, -578, -578, -578, 786, -578, -578, + -578, -578, -578, -578, -578, -578, -578, -578, -578, -578, + -578, -578, -578, 787, -578, -578, -578, -578, -578, -578, + -578, -578, -578, -578, -578, -578, -578, -578, -578, -578, -578, -578, -578, -578, -578, -578, -578, -578, -578, -578, -578, -578, -578, -578, -578, -578, -578, -578, -578, -578, -578, -578, -578, -578, -578, -578, -578, -578 }, { - 67, -579, -579, -579, -579, -579, -579, -579, -579, -579, + 69, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, - -579, -579, -579, -579, -579, -579, -579, 671, -579, -579, - -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, + 788, -579, -579, -579, -579, -579, -579, -579, -579, -579, + -579, -579, -579, -579, -579, -579, -579, 789, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, @@ -10412,12 +10573,12 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -580, -580, -580, -580, -580, -580, -580, -580, -580, + 69, -580, -580, -580, -580, -580, -580, -580, -580, -580, + -580, -580, -580, -580, -580, -580, -580, -580, -580, -580, + -580, -580, -580, -580, -580, -580, -580, -580, -580, -580, -580, -580, -580, -580, -580, -580, -580, -580, -580, -580, -580, -580, -580, -580, -580, -580, -580, -580, -580, -580, -580, -580, -580, -580, -580, -580, -580, -580, -580, -580, - -580, -580, -580, -580, -580, -580, -580, -580, 672, 672, - 672, 672, 672, 672, 672, 672, 672, 672, -580, -580, -580, -580, -580, -580, -580, -580, -580, -580, -580, -580, -580, -580, -580, -580, -580, -580, -580, -580, -580, -580, -580, -580, -580, -580, -580, -580, -580, -580, -580, -580, @@ -10429,45 +10590,45 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -581, -581, -581, -581, -581, -581, -581, -581, -581, + 69, -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, - -581, -581, -581, -581, -581, -581, -581, -581, -581, 673, - 673, 673, 673, 673, 673, 673, 673, 673, -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, - -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, + -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, + -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, + -581, -581, -581, -581, -581, 790, -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, -581 }, { - 67, -582, -582, -582, -582, -582, -582, -582, -582, -582, + 69, -582, -582, -582, -582, -582, -582, -582, -582, -582, + -582, -582, -582, -582, -582, -582, -582, -582, -582, -582, + -582, -582, -582, -582, -582, -582, -582, -582, -582, -582, + -582, -582, -582, -582, -582, -582, -582, -582, -582, -582, + + -582, -582, -582, -582, -582, -582, -582, -582, -582, -582, + -582, -582, -582, -582, -582, -582, -582, -582, -582, -582, + -582, -582, -582, -582, -582, -582, -582, -582, -582, -582, + -582, -582, -582, -582, -582, -582, -582, -582, -582, -582, -582, -582, -582, -582, -582, -582, -582, -582, -582, -582, -582, -582, -582, -582, -582, -582, -582, -582, -582, -582, - -582, -582, 674, -582, -582, -582, -582, -582, -582, -582, - - -582, -582, -582, -582, -582, -582, -582, -582, 675, 675, - 675, 675, 675, 675, 675, 675, 675, 675, -582, -582, - -582, -582, -582, -582, -582, 674, 674, 674, 674, 674, - 674, 674, 674, 674, 674, 674, 674, 674, 674, 674, - 674, 674, 674, 674, 674, 674, 674, 674, 674, 674, - 674, -582, -582, -582, -582, -582, -582, -582, -582, -582, -582, -582, -582, -582, -582, -582, -582, -582, -582, -582, -582, -582, -582, -582, -582, -582, -582, -582, -582, -582, -582, -582, -582, -582, -582, -582, -582, -582 }, { - 67, -583, -583, -583, -583, -583, -583, -583, -583, -583, + 69, -583, -583, -583, -583, -583, -583, -583, -583, -583, -583, -583, -583, -583, -583, -583, -583, -583, -583, -583, -583, -583, -583, -583, -583, -583, -583, -583, -583, -583, - -583, -583, 676, -583, -583, -583, -583, -583, -583, -583, + -583, -583, -583, -583, -583, -583, -583, -583, -583, -583, -583, -583, -583, -583, -583, -583, -583, -583, -583, -583, -583, -583, -583, -583, -583, -583, -583, -583, -583, -583, -583, -583, -583, -583, -583, -583, -583, -583, -583, -583, @@ -10481,27 +10642,27 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -584, -584, -584, -584, -584, -584, -584, -584, -584, + 69, -584, -584, -584, -584, -584, -584, -584, -584, -584, + -584, -584, -584, -584, -584, -584, -584, -584, -584, -584, + -584, -584, -584, -584, -584, -584, -584, -584, -584, -584, + -584, -584, -584, -584, -584, -584, -584, -584, -584, -584, + -584, -584, -584, -584, -584, -584, -584, -584, -584, -584, + -584, -584, -584, -584, -584, -584, -584, -584, -584, -584, -584, -584, -584, -584, -584, -584, -584, -584, -584, -584, -584, -584, -584, -584, -584, -584, -584, -584, -584, -584, - -584, -584, 677, -584, -584, -584, -584, -584, -584, -584, - -584, -584, -584, -584, -584, -584, -584, -584, 678, 678, - 678, 678, 678, 678, 678, 678, 678, 678, -584, -584, - -584, -584, -584, -584, -584, 677, 677, 677, 677, 677, - 677, 677, 677, 677, 677, 677, 677, 677, 677, 677, - - 677, 677, 677, 677, 677, 677, 677, 677, 677, 677, - 677, -584, -584, -584, -584, -584, -584, -584, -584, -584, + + -584, -584, -584, 791, -584, -584, -584, -584, -584, -584, + -584, -584, -584, -584, -584, -584, -584, -584, -584, -584, -584, -584, -584, -584, -584, -584, -584, -584, -584, -584, -584, -584, -584, -584, -584, -584, -584, -584, -584, -584, -584, -584, -584, -584, -584, -584, -584, -584 }, { - 67, -585, -585, -585, -585, -585, -585, -585, -585, -585, + 69, -585, -585, -585, -585, -585, -585, -585, -585, -585, + -585, -585, -585, -585, -585, -585, -585, -585, -585, -585, -585, -585, -585, -585, -585, -585, -585, -585, -585, -585, -585, -585, -585, -585, -585, -585, -585, -585, -585, -585, - -585, -585, 679, -585, -585, -585, -585, -585, -585, -585, -585, -585, -585, -585, -585, -585, -585, -585, -585, -585, -585, -585, -585, -585, -585, -585, -585, -585, -585, -585, @@ -10515,16 +10676,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -586, -586, -586, -586, -586, -586, -586, -586, -586, + 69, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, - -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, - -586, -586, 680, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, + -586, -586, -586, -586, -586, -586, -586, -586, -586, 792, + -586, -586, -586, 793, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, @@ -10533,33 +10694,33 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -587, -587, -587, -587, -587, -587, -587, -587, -587, + 69, -587, -587, -587, -587, -587, -587, -587, -587, -587, + -587, -587, -587, -587, -587, -587, -587, -587, -587, -587, + -587, -587, -587, -587, -587, -587, -587, -587, -587, -587, + -587, -587, -587, -587, -587, -587, -587, -587, -587, -587, + -587, -587, -587, -587, -587, -587, -587, -587, -587, -587, -587, -587, -587, -587, -587, -587, -587, -587, -587, -587, -587, -587, -587, -587, -587, -587, -587, -587, -587, -587, - -587, -587, 681, -587, -587, -587, -587, -587, -587, -587, - -587, -587, -587, -587, -587, -587, -587, -587, 682, 682, - 682, 682, 682, 682, 682, 682, 682, 682, -587, -587, - -587, -587, -587, -587, -587, 681, 681, 681, 681, 681, - 681, 681, 681, 681, 681, 681, 681, 681, 681, 681, - 681, 681, 681, 681, 681, 681, 681, 681, 681, 681, - - 681, -587, -587, -587, -587, -587, -587, -587, -587, -587, + -587, -587, -587, -587, -587, -587, -587, -587, -587, -587, + -587, -587, -587, -587, -587, -587, -587, -587, -587, -587, + + -587, -587, -587, -587, -587, -587, -587, -587, -587, -587, -587, -587, -587, -587, -587, -587, -587, -587, -587, -587, -587, -587, -587, -587, -587, -587, -587, -587, -587, -587, -587, -587, -587, -587, -587, -587, -587, -587 }, { - 67, -588, -588, -588, -588, -588, -588, -588, -588, -588, + 69, -588, -588, -588, -588, -588, -588, -588, -588, -588, + -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, - -588, -588, -588, -588, -588, -588, -588, -588, -588, 683, - 683, 683, 683, 683, 683, 683, 683, 683, -588, -588, - -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, + -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, + -588, -588, -588, -588, 794, -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, @@ -10567,17 +10728,17 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -589, -589, -589, -589, -589, -589, -589, -589, -589, + 69, -589, -589, -589, -589, -589, -589, -589, -589, -589, -589, -589, -589, -589, -589, -589, -589, -589, -589, -589, -589, -589, -589, -589, -589, -589, -589, -589, -589, -589, - -589, -589, 684, -589, -589, -589, -589, -589, -589, -589, - -589, -589, -589, -589, -589, -589, -589, -589, 685, 685, - 685, 685, 685, 685, 685, 685, 685, 685, -589, -589, - -589, -589, -589, -589, -589, 684, 684, 684, 684, 684, - 684, 684, 684, 684, 684, 684, 684, 684, 684, 684, - 684, 684, 684, 684, 684, 684, 684, 684, 684, 684, - 684, -589, -589, -589, -589, -589, -589, -589, -589, -589, + -589, -589, -589, -589, -589, -589, -589, -589, -589, -589, + -589, -589, -589, -589, -589, -589, -589, -589, -589, -589, + -589, -589, -589, -589, -589, -589, -589, -589, -589, -589, + -589, -589, -589, -589, -589, -589, -589, -589, -589, -589, + -589, -589, -589, -589, -589, -589, -589, 795, -589, -589, + -589, -589, -589, -589, -589, -589, -589, -589, -589, -589, + -589, -589, -589, -589, -589, -589, -589, -589, -589, -589, -589, -589, -589, -589, -589, -589, -589, -589, -589, -589, -589, -589, -589, -589, -589, -589, -589, -589, -589, -589, -589, -589, -589, -589, -589, -589, -589, -589 @@ -10585,15 +10746,15 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -590, -590, -590, -590, -590, -590, -590, -590, -590, + 69, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, - -590, -590, 686, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, + -590, -590, 796, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, @@ -10602,31 +10763,31 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -591, -591, -591, -591, -591, -591, -591, -591, -591, + 69, -591, -591, -591, -591, -591, -591, -591, -591, -591, + -591, -591, -591, -591, -591, -591, -591, -591, -591, -591, + -591, -591, -591, -591, -591, -591, -591, -591, -591, -591, + -591, -591, -591, -591, -591, -591, -591, -591, -591, -591, + -591, -591, -591, -591, -591, -591, -591, -591, -591, -591, + -591, -591, -591, -591, -591, -591, -591, -591, -591, -591, + -591, -591, -591, -591, -591, -591, -591, -591, -591, -591, + + -591, -591, -591, -591, -591, -591, -591, -591, -591, -591, -591, -591, -591, -591, -591, -591, -591, -591, -591, -591, -591, -591, -591, -591, -591, -591, -591, -591, -591, -591, - -591, -591, 687, -591, -591, -591, -591, -591, -591, -591, - -591, -591, -591, -591, -591, -591, -591, -591, 688, 688, - 688, 688, 688, 688, 688, 688, 688, 688, -591, -591, - -591, -591, -591, -591, -591, 687, 687, 687, 687, 687, - - 687, 687, 687, 687, 687, 687, 687, 687, 687, 687, - 687, 687, 687, 687, 687, 687, 687, 687, 687, 687, - 687, -591, -591, -591, -591, -591, -591, -591, -591, -591, -591, -591, -591, -591, -591, -591, -591, -591, -591, -591, -591, -591, -591, -591, -591, -591, -591, -591, -591, -591, -591, -591, -591, -591, -591, -591, -591, -591 }, { - 67, -592, -592, -592, -592, -592, -592, -592, -592, -592, + 69, -592, -592, -592, -592, -592, -592, -592, -592, -592, -592, -592, -592, -592, -592, -592, -592, -592, -592, -592, -592, -592, -592, -592, -592, -592, -592, -592, -592, -592, - -592, -592, 689, -592, -592, -592, -592, -592, -592, -592, - -592, -592, -592, -592, -592, -592, -592, -592, -592, -592, + -592, -592, -592, -592, -592, -592, -592, -592, -592, -592, -592, -592, -592, -592, -592, -592, -592, -592, -592, -592, + -592, -592, -592, -592, -592, -592, -592, -592, -592, 797, -592, -592, -592, -592, -592, -592, -592, -592, -592, -592, -592, -592, -592, -592, -592, -592, -592, -592, -592, -592, -592, -592, -592, -592, -592, -592, -592, -592, -592, -592, @@ -10636,16 +10797,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -593, -593, -593, -593, -593, -593, -593, -593, -593, + 69, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, - -593, -593, 690, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, + -593, -593, -593, -593, 798, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, @@ -10654,24 +10815,24 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -594, -594, -594, -594, -594, -594, -594, -594, -594, + 69, -594, -594, -594, -594, -594, -594, -594, -594, -594, + -594, -594, -594, -594, -594, -594, -594, -594, -594, -594, -594, -594, -594, -594, -594, -594, -594, -594, -594, -594, -594, -594, -594, -594, -594, -594, -594, -594, -594, -594, - -594, -594, 691, -594, -594, -594, -594, -594, -594, -594, - -594, -594, -594, -594, -594, -594, -594, -594, 692, 692, - 692, 692, 692, 692, 692, 692, 692, 692, -594, -594, - -594, -594, -594, -594, -594, 691, 691, 691, 691, 691, - 691, 691, 691, 691, 691, 691, 691, 691, 691, 691, - - 691, 691, 691, 691, 691, 691, 691, 691, 691, 691, - 691, -594, -594, -594, -594, -594, -594, -594, -594, -594, + -594, -594, -594, -594, -594, -594, -594, -594, -594, -594, + -594, -594, -594, -594, -594, -594, -594, -594, -594, -594, + -594, -594, -594, -594, -594, -594, -594, -594, -594, -594, + -594, -594, -594, -594, -594, -594, -594, -594, -594, -594, + + -594, -594, -594, -594, -594, -594, -594, -594, 799, -594, + -594, -594, -594, -594, -594, -594, -594, -594, -594, -594, -594, -594, -594, -594, -594, -594, -594, -594, -594, -594, -594, -594, -594, -594, -594, -594, -594, -594, -594, -594, -594, -594, -594, -594, -594, -594, -594, -594 }, { - 67, -595, -595, -595, -595, -595, -595, -595, -595, -595, + 69, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, @@ -10679,7 +10840,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, - -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, + -595, -595, -595, -595, -595, -595, 800, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, @@ -10688,14 +10849,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -596, -596, -596, -596, -596, -596, -596, -596, -596, + 69, -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, - -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, + -596, -596, -596, -596, -596, -596, -596, -596, -596, 801, -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, @@ -10706,8 +10867,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -597, -597, -597, -597, -597, -597, -597, -597, -597, - -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, + 69, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, @@ -10715,6 +10875,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, + 802, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, @@ -10723,7 +10884,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -598, -598, -598, -598, -598, -598, -598, -598, -598, + 69, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, @@ -10731,7 +10892,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, - -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, + -598, -598, -598, 803, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, @@ -10740,7 +10901,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -599, -599, -599, -599, -599, -599, -599, -599, -599, + 69, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, @@ -10748,7 +10909,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, - -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, + -599, -599, 804, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, @@ -10758,8 +10919,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -600, -600, -600, -600, -600, -600, -600, -600, -600, - -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, + 69, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, @@ -10767,6 +10927,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, + 805, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, @@ -10775,15 +10936,15 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -601, -601, -601, -601, -601, -601, -601, -601, -601, + 69, -601, -601, -601, -601, -601, -601, -601, -601, -601, + -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, - -601, -601, -601, -601, -601, -601, -601, -601, 693, 693, - 693, 693, 693, 693, 693, 693, 693, 693, -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, - -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, + + -601, -601, -601, -601, -601, -601, 806, -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, @@ -10792,34 +10953,34 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -602, -602, -602, -602, -602, -602, -602, -602, -602, + 69, -602, -602, -602, -602, -602, -602, -602, -602, -602, + -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, - -602, -602, 694, -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, - -602, -602, -602, -602, -602, 694, 694, 694, 694, 694, - 694, 694, 694, 694, 694, 694, 694, 694, 694, 694, - 694, 694, 694, 694, 694, 694, 694, 694, 694, 694, - 694, -602, -602, -602, -602, -602, -602, -602, -602, -602, + -602, -602, -602, -602, -602, -602, -602, -602, 807, -602, + -602, -602, -602, -602, -602, -602, -602, -602, -602, 808, + 809, -602, -602, 810, -602, 811, -602, -602, -602, -602, + -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, -602 }, { - 67, -603, -603, -603, -603, -603, -603, -603, -603, -603, + 69, -603, -603, -603, -603, -603, -603, -603, -603, -603, -603, -603, -603, -603, -603, -603, -603, -603, -603, -603, -603, -603, -603, -603, -603, -603, -603, -603, -603, -603, - -603, -603, 694, -603, -603, -603, -603, -603, -603, -603, - -603, -603, -603, -603, -603, -603, -603, -603, 695, 695, - 695, 695, 695, 695, 695, 695, 695, 695, -603, -603, - -603, -603, -603, -603, -603, 694, 694, 694, 694, 694, - 694, 694, 694, 694, 694, 694, 694, 694, 694, 694, - 694, 694, 694, 694, 694, 694, 694, 694, 694, 694, - 694, -603, -603, -603, -603, -603, -603, -603, -603, -603, + -603, -603, -603, -603, -603, -603, -603, -603, -603, -603, + -603, -603, -603, -603, -603, -603, -603, -603, -603, -603, + -603, -603, -603, -603, -603, -603, -603, -603, -603, -603, + -603, -603, -603, -603, -603, -603, -603, -603, 812, -603, + -603, -603, -603, -603, -603, -603, -603, -603, -603, -603, + -603, -603, -603, -603, -603, -603, -603, -603, -603, -603, + -603, -603, -603, -603, -603, -603, -603, -603, -603, -603, -603, -603, -603, -603, -603, -603, -603, -603, -603, -603, -603, -603, -603, -603, -603, -603, -603, -603, -603, -603, @@ -10827,16 +10988,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -604, -604, -604, -604, -604, -604, -604, -604, -604, + 69, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, - -604, -604, 696, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, - -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, + + -604, -604, -604, -604, -604, -604, -604, -604, -604, 813, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, @@ -10844,33 +11005,33 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -605, -605, -605, -605, -605, -605, -605, -605, -605, + 69, -605, -605, -605, -605, -605, -605, -605, -605, -605, + -605, -605, -605, -605, -605, -605, -605, -605, -605, -605, + -605, -605, -605, -605, -605, -605, -605, -605, -605, -605, + -605, -605, -605, -605, -605, -605, -605, -605, -605, -605, + -605, -605, -605, -605, -605, -605, -605, -605, -605, -605, + + -605, -605, -605, -605, -605, -605, -605, -605, -605, -605, + -605, -605, -605, -605, -605, -605, -605, -605, -605, -605, -605, -605, -605, -605, -605, -605, -605, -605, -605, -605, + -605, -605, 814, -605, -605, -605, -605, -605, -605, -605, -605, -605, -605, -605, -605, -605, -605, -605, -605, -605, - -605, -605, 697, -605, -605, -605, -605, -605, -605, -605, - -605, -605, -605, -605, -605, -605, -605, -605, 698, 698, - - 698, 698, 698, 698, 698, 698, 698, 698, -605, -605, - -605, -605, -605, -605, -605, 697, 697, 697, 697, 697, - 697, 697, 697, 697, 697, 697, 697, 697, 697, 697, - 697, 697, 697, 697, 697, 697, 697, 697, 697, 697, - 697, -605, -605, -605, -605, -605, -605, -605, -605, -605, -605, -605, -605, -605, -605, -605, -605, -605, -605, -605, -605, -605, -605, -605, -605, -605, -605, -605, -605, -605, -605, -605, -605, -605, -605, -605, -605, -605 }, { - 67, -606, -606, -606, -606, -606, -606, -606, -606, -606, + 69, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, - -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, - -606, -606, 699, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, + -606, -606, -606, -606, -606, -606, -606, -606, 815, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, + 816, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, @@ -10879,15 +11040,15 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -607, -607, -607, -607, -607, -607, -607, -607, -607, + 69, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, - -607, -607, 700, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, + -607, -607, -607, 817, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, @@ -10896,33 +11057,33 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -608, -608, -608, -608, -608, -608, -608, -608, -608, + 69, -608, -608, -608, -608, -608, -608, -608, -608, -608, + -608, -608, -608, -608, -608, -608, -608, -608, -608, -608, + -608, -608, -608, -608, -608, -608, -608, -608, -608, -608, + -608, -608, -608, -608, -608, -608, -608, -608, -608, -608, + -608, -608, -608, -608, -608, -608, -608, -608, -608, -608, + -608, -608, -608, -608, -608, -608, -608, -608, -608, -608, + + -608, -608, -608, -608, -608, -608, -608, -608, -608, -608, -608, -608, -608, -608, -608, -608, -608, -608, -608, -608, + -608, -608, 818, -608, -608, -608, -608, -608, -608, -608, -608, -608, -608, -608, -608, -608, -608, -608, -608, -608, - -608, -608, 701, -608, -608, -608, -608, -608, -608, -608, - -608, -608, -608, -608, -608, -608, -608, -608, 702, 702, - 702, 702, 702, 702, 702, 702, 702, 702, -608, -608, - - -608, -608, -608, -608, -608, 701, 701, 701, 701, 701, - 701, 701, 701, 701, 701, 701, 701, 701, 701, 701, - 701, 701, 701, 701, 701, 701, 701, 701, 701, 701, - 701, -608, -608, -608, -608, -608, -608, -608, -608, -608, -608, -608, -608, -608, -608, -608, -608, -608, -608, -608, -608, -608, -608, -608, -608, -608, -608, -608, -608, -608, -608, -608, -608, -608, -608, -608, -608, -608 }, { - 67, -609, -609, -609, -609, -609, -609, -609, -609, -609, + 69, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, - -609, -609, -609, -609, -609, -609, -609, -609, 703, 703, - 703, 703, 703, 703, 703, 703, 703, 703, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, + -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, + 819, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, @@ -10931,16 +11092,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -610, -610, -610, -610, -610, -610, -610, -610, -610, + 69, -610, -610, -610, -610, -610, -610, -610, -610, -610, + -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, + -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, + -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, + -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, + -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, - -610, -610, 704, -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, - -610, -610, -610, -610, -610, 704, 704, 704, 704, 704, - 704, 704, 704, 704, 704, 704, 704, 704, 704, 704, - 704, 704, 704, 704, 704, 704, 704, 704, 704, 704, - 704, -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, @@ -10948,32 +11109,32 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -611, -611, -611, -611, -611, -611, -611, -611, -611, + 69, -611, -611, -611, -611, -611, -611, -611, -611, -611, + -611, -611, -611, -611, -611, -611, -611, -611, -611, -611, + -611, -611, -611, -611, -611, -611, -611, -611, -611, -611, + -611, -611, -611, -611, -611, -611, -611, -611, -611, -611, + -611, -611, -611, -611, -611, -611, -611, -611, -611, -611, -611, -611, -611, -611, -611, -611, -611, -611, -611, -611, -611, -611, -611, -611, -611, -611, -611, -611, -611, -611, - -611, -611, 704, -611, -611, -611, -611, -611, -611, -611, - -611, -611, -611, -611, -611, -611, -611, -611, 705, 705, - 705, 705, 705, 705, 705, 705, 705, 705, -611, -611, - -611, -611, -611, -611, -611, 704, 704, 704, 704, 704, - 704, 704, 704, 704, 704, 704, 704, 704, 704, 704, - 704, 704, 704, 704, 704, 704, 704, 704, 704, 704, - 704, -611, -611, -611, -611, -611, -611, -611, -611, -611, + -611, -611, -611, -611, -611, -611, -611, -611, -611, -611, + -611, -611, -611, -611, -611, -611, -611, -611, -611, -611, + -611, -611, -611, -611, -611, -611, -611, -611, -611, -611, -611, -611, -611, -611, -611, -611, -611, -611, -611, -611, -611, -611, -611, -611, -611, -611, -611, -611, -611, -611, -611, -611, -611, -611, -611, -611, -611, -611 }, { - 67, -612, -612, -612, -612, -612, -612, -612, -612, -612, + 69, -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, - -612, -612, 706, -612, -612, -612, -612, -612, -612, -612, - -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, + -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, + -612, -612, -612, -612, -612, -612, -612, -612, 820, -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, @@ -10982,17 +11143,17 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -613, -613, -613, -613, -613, -613, -613, -613, -613, + 69, -613, -613, -613, -613, -613, -613, -613, -613, -613, -613, -613, -613, -613, -613, -613, -613, -613, -613, -613, -613, -613, -613, -613, -613, -613, -613, -613, -613, -613, - -613, -613, 707, -613, -613, -613, -613, -613, -613, -613, - -613, -613, -613, -613, -613, -613, -613, -613, 708, 708, - 708, 708, 708, 708, 708, 708, 708, 708, -613, -613, - -613, -613, -613, -613, -613, 707, 707, 707, 707, 707, - 707, 707, 707, 707, 707, 707, 707, 707, 707, 707, - 707, 707, 707, 707, 707, 707, 707, 707, 707, 707, - 707, -613, -613, -613, -613, -613, -613, -613, -613, -613, + -613, -613, -613, -613, -613, -613, -613, -613, -613, -613, + -613, -613, -613, -613, -613, -613, -613, -613, -613, -613, + -613, -613, -613, -613, -613, -613, -613, -613, -613, -613, + -613, -613, -613, -613, -613, -613, -613, -613, -613, -613, + -613, -613, -613, -613, -613, -613, -613, -613, -613, -613, + -613, -613, -613, 821, -613, -613, -613, -613, -613, -613, + -613, -613, -613, -613, -613, -613, -613, -613, -613, -613, -613, -613, -613, -613, -613, -613, -613, -613, -613, -613, -613, -613, -613, -613, -613, -613, -613, -613, -613, -613, @@ -11000,13 +11161,13 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -614, -614, -614, -614, -614, -614, -614, -614, -614, + 69, -614, -614, -614, -614, -614, -614, -614, -614, -614, -614, -614, -614, -614, -614, -614, -614, -614, -614, -614, -614, -614, -614, -614, -614, -614, -614, -614, -614, -614, - -614, -614, 709, -614, -614, -614, -614, -614, -614, -614, -614, -614, -614, -614, -614, -614, -614, -614, -614, -614, -614, -614, -614, -614, -614, -614, -614, -614, -614, -614, -614, -614, -614, -614, -614, -614, -614, -614, -614, -614, + -614, -614, -614, -614, -614, -614, -614, -614, -614, 822, -614, -614, -614, -614, -614, -614, -614, -614, -614, -614, -614, -614, -614, -614, -614, -614, -614, -614, -614, -614, @@ -11017,16 +11178,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -615, -615, -615, -615, -615, -615, -615, -615, -615, + 69, -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, - -615, -615, 710, -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, + -615, -615, -615, -615, -615, -615, -615, -615, -615, 823, + 823, 823, 823, 823, 823, 823, 823, 823, -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, - -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, - -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, + -615, -615, 824, -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, @@ -11034,17 +11195,17 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -616, -616, -616, -616, -616, -616, -616, -616, -616, + 69, -616, -616, -616, -616, -616, -616, -616, -616, -616, -616, -616, -616, -616, -616, -616, -616, -616, -616, -616, -616, -616, -616, -616, -616, -616, -616, -616, -616, -616, - -616, -616, 711, -616, -616, -616, -616, -616, -616, -616, - -616, -616, -616, -616, -616, -616, -616, -616, 712, 712, - 712, 712, 712, 712, 712, 712, 712, 712, -616, -616, - -616, -616, -616, -616, -616, 711, 711, 711, 711, 711, - 711, 711, 711, 711, 711, 711, 711, 711, 711, 711, - 711, 711, 711, 711, 711, 711, 711, 711, 711, 711, - 711, -616, -616, -616, -616, -616, -616, -616, -616, -616, + -616, -616, -616, -616, -616, -616, -616, -616, -616, -616, + -616, -616, -616, -616, -616, -616, -616, -616, -616, -616, + -616, -616, -616, -616, -616, -616, -616, -616, -616, -616, + -616, -616, -616, -616, -616, -616, -616, -616, -616, -616, + -616, -616, -616, -616, -616, -616, -616, -616, -616, -616, + -616, -616, -616, -616, -616, -616, -616, -616, -616, -616, + -616, -616, -616, -616, -616, -616, -616, -616, -616, -616, -616, -616, -616, -616, -616, -616, -616, -616, -616, -616, -616, -616, -616, -616, -616, -616, -616, -616, -616, -616, @@ -11052,12 +11213,12 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -617, -617, -617, -617, -617, -617, -617, -617, -617, - -617, -617, -617, -617, -617, -617, -617, -617, -617, -617, - -617, -617, -617, -617, -617, -617, -617, -617, -617, -617, + 69, -617, -617, -617, -617, -617, -617, -617, -617, -617, -617, -617, -617, -617, -617, -617, -617, -617, -617, -617, -617, -617, -617, -617, -617, -617, -617, -617, -617, -617, -617, -617, -617, -617, -617, -617, -617, -617, -617, -617, + -617, -617, -617, -617, -617, -617, -617, -617, -617, 825, + 825, 825, 825, 825, 825, 825, 825, 825, -617, -617, -617, -617, -617, -617, -617, -617, -617, -617, -617, -617, -617, -617, -617, -617, -617, -617, -617, -617, -617, -617, -617, -617, -617, -617, -617, -617, -617, -617, -617, -617, @@ -11069,7 +11230,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -618, -618, -618, -618, -618, -618, -618, -618, -618, + 69, -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, @@ -11078,7 +11239,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, - -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, + -618, -618, -618, -618, -618, -618, -618, -618, 826, -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, @@ -11086,14 +11247,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -619, -619, -619, -619, -619, -619, -619, -619, -619, + 69, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, - -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, + -619, -619, -619, -619, -619, 827, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, @@ -11104,12 +11265,12 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -620, -620, -620, -620, -620, -620, -620, -620, -620, - -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, - -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, + 69, -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, + -620, -620, -620, -620, -620, -620, -620, -620, -620, 828, + 828, 828, 828, 828, 828, 828, 828, 828, -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, @@ -11121,12 +11282,12 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -621, -621, -621, -621, -621, -621, -621, -621, -621, - -621, -621, -621, -621, -621, -621, -621, -621, -621, -621, - -621, -621, -621, -621, -621, -621, -621, -621, -621, -621, + 69, -621, -621, -621, -621, -621, -621, -621, -621, -621, -621, -621, -621, -621, -621, -621, -621, -621, -621, -621, -621, -621, -621, -621, -621, -621, -621, -621, -621, -621, -621, -621, -621, -621, -621, -621, -621, -621, -621, -621, + -621, -621, -621, -621, -621, -621, -621, -621, -621, 829, + 829, 829, 829, 829, 829, 829, 829, 829, -621, -621, -621, -621, -621, -621, -621, -621, -621, -621, -621, -621, -621, -621, -621, -621, -621, -621, -621, -621, -621, -621, @@ -11138,7 +11299,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -622, -622, -622, -622, -622, -622, -622, -622, -622, + 69, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, @@ -11147,7 +11308,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, - -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, + -622, -622, -622, -622, -622, 830, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, @@ -11155,17 +11316,17 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -623, -623, -623, -623, -623, -623, -623, -623, -623, + 69, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, - -623, -623, -623, -623, -623, -623, -623, -623, 713, 713, - 713, 713, 713, 713, 713, 713, 713, 713, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, + -623, -623, 831, -623, -623, -623, -623, -623, -623, -623, + -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, @@ -11173,45 +11334,45 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -624, -624, -624, -624, -624, -624, -624, -624, -624, + 69, -624, -624, -624, -624, -624, -624, -624, -624, -624, + -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, - -624, -624, 714, -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, - -624, -624, -624, -624, -624, 714, 714, 714, 714, 714, - 714, 714, 714, 714, 714, 714, 714, 714, 714, 714, - - 714, 714, 714, 714, 714, 714, 714, 714, 714, 714, - 714, -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, - -624, -624, -624, -624, -624, -624, -624, -624 - }, + + -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, + -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, + -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, + -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, + -624, -624, -624, -624, -624, -624, -624, -624 + }, { - 67, -625, -625, -625, -625, -625, -625, -625, -625, -625, + 69, -625, -625, -625, -625, -625, -625, -625, -625, -625, + -625, -625, -625, -625, -625, -625, -625, -625, -625, -625, + -625, -625, -625, -625, -625, -625, -625, -625, -625, -625, + -625, -625, -625, -625, -625, -625, -625, -625, -625, -625, + -625, -625, -625, -625, -625, -625, -625, -625, -625, -625, + + -625, -625, -625, -625, -625, -625, -625, -625, -625, -625, + -625, -625, -625, -625, -625, -625, -625, -625, -625, -625, + -625, -625, -625, -625, -625, -625, -625, -625, -625, -625, -625, -625, -625, -625, -625, -625, -625, -625, -625, -625, -625, -625, -625, -625, -625, -625, -625, -625, -625, -625, - -625, -625, 714, -625, -625, -625, -625, -625, -625, -625, - -625, -625, -625, -625, -625, -625, -625, -625, 715, 715, - - 715, 715, 715, 715, 715, 715, 715, 715, -625, -625, - -625, -625, -625, -625, -625, 714, 714, 714, 714, 714, - 714, 714, 714, 714, 714, 714, 714, 714, 714, 714, - 714, 714, 714, 714, 714, 714, 714, 714, 714, 714, - 714, -625, -625, -625, -625, -625, -625, -625, -625, -625, -625, -625, -625, -625, -625, -625, -625, -625, -625, -625, -625, -625, -625, -625, -625, -625, -625, -625, -625, -625, -625, -625, -625, -625, -625, -625, -625, -625 }, { - 67, -626, -626, -626, -626, -626, -626, -626, -626, -626, + 69, -626, -626, -626, -626, -626, -626, -626, -626, -626, -626, -626, -626, -626, -626, -626, -626, -626, -626, -626, -626, -626, -626, -626, -626, -626, -626, -626, -626, -626, - -626, -626, 716, -626, -626, -626, -626, -626, -626, -626, + -626, -626, -626, -626, -626, -626, -626, -626, -626, -626, -626, -626, -626, -626, -626, -626, -626, -626, -626, -626, -626, -626, -626, -626, -626, -626, -626, -626, -626, -626, -626, -626, -626, -626, -626, -626, -626, -626, -626, -626, @@ -11225,27 +11386,27 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -627, -627, -627, -627, -627, -627, -627, -627, -627, + 69, -627, -627, -627, -627, -627, -627, -627, -627, -627, -627, -627, -627, -627, -627, -627, -627, -627, -627, -627, -627, -627, -627, -627, -627, -627, -627, -627, -627, -627, - -627, -627, 717, -627, -627, -627, -627, -627, -627, -627, - -627, -627, -627, -627, -627, -627, -627, -627, 718, 718, - 718, 718, 718, 718, 718, 718, 718, 718, -627, -627, - -627, -627, -627, -627, -627, 717, 717, 717, 717, 717, - 717, 717, 717, 717, 717, 717, 717, 717, 717, 717, - 717, 717, 717, 717, 717, 717, 717, 717, 717, 717, - - 717, -627, -627, -627, -627, -627, -627, -627, -627, -627, + -627, -627, -627, -627, -627, -627, -627, -627, -627, -627, + -627, -627, -627, -627, -627, -627, -627, -627, -627, -627, + -627, -627, -627, -627, -627, -627, -627, -627, -627, -627, + -627, -627, -627, -627, -627, -627, -627, -627, -627, -627, + -627, -627, -627, -627, -627, -627, -627, -627, -627, -627, + -627, -627, -627, -627, -627, -627, -627, -627, -627, -627, + + -627, -627, -627, -627, -627, -627, -627, -627, -627, -627, -627, -627, -627, -627, -627, -627, -627, -627, -627, -627, -627, -627, -627, -627, -627, -627, -627, -627, -627, -627, -627, -627, -627, -627, -627, -627, -627, -627 }, { - 67, -628, -628, -628, -628, -628, -628, -628, -628, -628, + 69, -628, -628, -628, -628, -628, -628, -628, -628, -628, + -628, -628, -628, -628, -628, -628, -628, -628, -628, -628, -628, -628, -628, -628, -628, -628, -628, -628, -628, -628, -628, -628, -628, -628, -628, -628, -628, -628, -628, -628, - -628, -628, 719, -628, -628, -628, -628, -628, -628, -628, -628, -628, -628, -628, -628, -628, -628, -628, -628, -628, -628, -628, -628, -628, -628, -628, -628, -628, -628, -628, @@ -11259,11 +11420,11 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -629, -629, -629, -629, -629, -629, -629, -629, -629, + 69, -629, -629, -629, -629, -629, -629, -629, -629, -629, -629, -629, -629, -629, -629, -629, -629, -629, -629, -629, -629, -629, -629, -629, -629, -629, -629, -629, -629, -629, - -629, -629, 720, -629, -629, -629, -629, -629, -629, -629, + -629, -629, -629, -629, -629, -629, -629, -629, -629, -629, -629, -629, -629, -629, -629, -629, -629, -629, -629, -629, -629, -629, -629, -629, -629, -629, -629, -629, -629, -629, -629, -629, -629, -629, -629, -629, -629, -629, -629, -629, @@ -11277,16 +11438,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -630, -630, -630, -630, -630, -630, -630, -630, -630, + 69, -630, -630, -630, -630, -630, -630, -630, -630, -630, + -630, -630, -630, -630, -630, -630, -630, -630, -630, -630, + -630, -630, -630, -630, -630, -630, -630, -630, -630, -630, + -630, -630, 832, -630, -630, -630, -630, -630, -630, -630, + -630, -630, -630, -630, -630, -630, -630, -630, -630, -630, + -630, -630, -630, -630, -630, -630, -630, -630, -630, -630, + -630, -630, -630, -630, -630, -630, -630, -630, -630, -630, + -630, -630, -630, -630, -630, -630, -630, -630, -630, -630, -630, -630, -630, -630, -630, -630, -630, -630, -630, -630, -630, -630, -630, -630, -630, -630, -630, -630, -630, -630, - -630, -630, 721, -630, -630, -630, -630, -630, -630, -630, - -630, -630, -630, -630, -630, -630, -630, -630, 722, 722, - 722, 722, 722, 722, 722, 722, 722, 722, -630, -630, - -630, -630, -630, -630, -630, 721, 721, 721, 721, 721, - 721, 721, 721, 721, 721, 721, 721, 721, 721, 721, - 721, 721, 721, 721, 721, 721, 721, 721, 721, 721, - 721, -630, -630, -630, -630, -630, -630, -630, -630, -630, -630, -630, -630, -630, -630, -630, -630, -630, -630, -630, -630, -630, -630, -630, -630, -630, -630, -630, -630, -630, @@ -11294,47 +11455,47 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -631, -631, -631, -631, -631, -631, -631, -631, -631, - -631, -631, -631, -631, -631, -631, -631, -631, -631, -631, - -631, -631, -631, -631, -631, -631, -631, -631, -631, -631, - -631, -631, -631, -631, -631, -631, -631, -631, -631, -631, - -631, -631, -631, -631, -631, -631, -631, -631, -631, -631, - -631, -631, -631, -631, -631, -631, -631, -631, -631, -631, - -631, -631, -631, -631, -631, -631, -631, -631, -631, -631, - - -631, -631, -631, -631, -631, -631, -631, -631, -631, -631, + 69, -631, -631, -631, -631, -631, -631, -631, -631, -631, -631, -631, -631, -631, -631, -631, -631, -631, -631, -631, -631, -631, -631, -631, -631, -631, -631, -631, -631, -631, + -631, -631, 833, -631, -631, -631, -631, -631, -631, -631, + -631, -631, -631, -631, -631, -631, -631, -631, 834, 834, + 834, 834, 834, 834, 834, 834, 834, 834, -631, -631, + -631, -631, -631, -631, -631, 833, 833, 833, 833, 833, + + 833, 833, 833, 833, 833, 833, 833, 833, 833, 833, + 833, 833, 833, 833, 833, 833, 833, 833, 833, 833, + 833, -631, -631, -631, -631, -631, -631, -631, -631, -631, -631, -631, -631, -631, -631, -631, -631, -631, -631, -631, -631, -631, -631, -631, -631, -631, -631, -631, -631, -631, -631, -631, -631, -631, -631, -631, -631, -631 }, { - 67, -632, -632, -632, -632, -632, -632, -632, -632, -632, - -632, -632, -632, -632, -632, -632, -632, -632, -632, -632, - -632, -632, -632, -632, -632, -632, -632, -632, -632, -632, - -632, -632, -632, -632, -632, -632, -632, -632, -632, -632, - - -632, -632, -632, -632, -632, -632, -632, -632, -632, -632, - -632, -632, -632, -632, -632, -632, -632, -632, -632, -632, - -632, -632, -632, -632, -632, -632, -632, -632, -632, -632, - -632, -632, -632, -632, -632, -632, -632, -632, -632, -632, + 69, -632, -632, -632, -632, -632, -632, -632, -632, -632, -632, -632, -632, -632, -632, -632, -632, -632, -632, -632, -632, -632, -632, -632, -632, -632, -632, -632, -632, -632, + -632, -632, 835, -632, -632, -632, -632, -632, -632, -632, + + -632, -632, -632, -632, -632, -632, -632, -632, 836, 836, + 836, 836, 836, 836, 836, 836, 836, 836, -632, -632, + -632, -632, -632, -632, -632, 835, 835, 835, 835, 835, + 835, 835, 835, 835, 835, 835, 835, 835, 835, 835, + 835, 835, 835, 835, 835, 835, 835, 835, 835, 835, + 835, -632, -632, -632, -632, -632, -632, -632, -632, -632, -632, -632, -632, -632, -632, -632, -632, -632, -632, -632, -632, -632, -632, -632, -632, -632, -632, -632, -632, -632, -632, -632, -632, -632, -632, -632, -632, -632 }, { - 67, -633, -633, -633, -633, -633, -633, -633, -633, -633, + 69, -633, -633, -633, -633, -633, -633, -633, -633, -633, -633, -633, -633, -633, -633, -633, -633, -633, -633, -633, -633, -633, -633, -633, -633, -633, -633, -633, -633, -633, -633, -633, -633, -633, -633, -633, -633, -633, -633, -633, - -633, -633, -633, -633, -633, -633, -633, -633, -633, -633, - -633, -633, -633, -633, -633, -633, -633, -633, -633, -633, + -633, -633, -633, -633, -633, -633, -633, -633, 837, 837, + 837, 837, 837, 837, 837, 837, 837, 837, -633, -633, -633, -633, -633, -633, -633, -633, -633, -633, -633, -633, -633, -633, -633, -633, -633, -633, -633, -633, -633, -633, -633, -633, -633, -633, -633, -633, -633, -633, -633, -633, @@ -11346,47 +11507,47 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -634, -634, -634, -634, -634, -634, -634, -634, -634, - -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, - -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, + 69, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, + -634, -634, -634, -634, -634, 838, -634, -634, 839, 839, + 839, 839, 839, 839, 839, 839, 839, 839, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, - -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, + -634, -634, -634, -634, -634, 840, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, -634 }, { - 67, -635, -635, -635, -635, -635, -635, -635, -635, -635, - -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, + 69, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, + -635, -635, -635, -635, -635, 838, -635, -635, 839, 839, + 839, 839, 839, 839, 839, 839, 839, 839, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, - -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, - -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, + -635, -635, -635, -635, -635, 841, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, -635 }, { - 67, -636, -636, -636, -636, -636, -636, -636, -636, -636, + 69, -636, -636, -636, -636, -636, -636, -636, -636, -636, -636, -636, -636, -636, -636, -636, -636, -636, -636, -636, -636, -636, -636, -636, -636, -636, -636, -636, -636, -636, -636, -636, -636, -636, -636, -636, -636, -636, -636, -636, - -636, -636, -636, -636, -636, -636, -636, -636, -636, -636, - -636, -636, -636, -636, -636, -636, -636, -636, -636, -636, + -636, -636, -636, -636, -636, -636, -636, -636, 842, 842, + 842, 842, 842, 842, 842, 842, 842, 842, -636, -636, -636, -636, -636, -636, -636, -636, -636, -636, -636, -636, -636, -636, -636, -636, -636, -636, -636, -636, -636, -636, -636, -636, -636, -636, -636, -636, -636, -636, -636, -636, @@ -11398,31 +11559,31 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -637, -637, -637, -637, -637, -637, -637, -637, -637, + 69, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, + -637, -637, -637, -637, -637, 838, -637, -637, 839, 839, + 839, 839, 839, 839, 839, 839, 839, 839, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, - -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, - -637, -637, -637, -637, -637, -637, 723, -637, -637, -637, - -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, + -637, -637, -637, -637, -637, 843, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, -637 }, { - 67, -638, -638, -638, -638, -638, -638, -638, -638, -638, - -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, - -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, + 69, -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, + -638, -638, -638, -638, -638, -638, -638, -638, 844, 845, + 845, 845, 845, 845, 845, 845, 845, 845, -638, -638, - -638, -638, -638, -638, -638, -638, 724, -638, -638, -638, + -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, @@ -11432,17 +11593,17 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -639, -639, -639, -639, -639, -639, -639, -639, -639, + 69, -639, -639, -639, -639, -639, -639, -639, -639, -639, -639, -639, -639, -639, -639, -639, -639, -639, -639, -639, -639, -639, -639, -639, -639, -639, -639, -639, -639, -639, - -639, -639, 725, -639, -639, -639, -639, -639, -639, -639, - -639, -639, -639, -639, -639, -639, -639, -639, -639, -639, - -639, -639, -639, -639, -639, -639, -639, -639, -639, -639, - -639, -639, -639, -639, -639, -639, -639, -639, -639, -639, - -639, -639, -639, -639, -639, -639, -639, -639, -639, -639, - -639, -639, -639, -639, -639, -639, -639, -639, -639, -639, - -639, -639, -639, -639, -639, -639, -639, -639, -639, -639, + -639, -639, 846, -639, -639, -639, -639, -639, -639, -639, + -639, -639, -639, -639, -639, -639, -639, -639, 847, 847, + 847, 847, 847, 847, 847, 847, 847, 847, -639, -639, + -639, -639, -639, -639, -639, 846, 846, 846, 846, 846, + 846, 846, 846, 846, 846, 846, 846, 846, 846, 846, + 846, 846, 846, 846, 846, 846, 846, 846, 846, 846, + 846, -639, -639, -639, -639, -639, -639, -639, -639, -639, -639, -639, -639, -639, -639, -639, -639, -639, -639, -639, -639, -639, -639, -639, -639, -639, -639, -639, -639, -639, -639, -639, -639, -639, -639, -639, -639, -639 @@ -11450,12 +11611,12 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -640, -640, -640, -640, -640, -640, -640, -640, -640, + 69, -640, -640, -640, -640, -640, -640, -640, -640, -640, + -640, -640, -640, -640, -640, -640, -640, -640, -640, -640, -640, -640, -640, -640, -640, -640, -640, -640, -640, -640, -640, -640, -640, -640, -640, -640, -640, -640, -640, -640, - -640, -640, 726, -640, -640, -640, -640, -640, -640, -640, - -640, -640, -640, -640, -640, -640, -640, -640, 727, 727, - 727, 727, 727, 727, 727, 727, 727, 727, -640, -640, + -640, -640, -640, -640, -640, -640, -640, -640, 848, 849, + 849, 849, 849, 849, 849, 849, 849, 849, -640, -640, -640, -640, -640, -640, -640, -640, -640, -640, -640, -640, -640, -640, -640, -640, -640, -640, -640, -640, -640, -640, -640, -640, -640, -640, -640, -640, -640, -640, -640, -640, @@ -11467,50 +11628,50 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -641, -641, -641, -641, -641, -641, -641, -641, -641, - -641, -641, -641, -641, -641, -641, -641, -641, -641, -641, - -641, -641, -641, -641, -641, -641, -641, -641, -641, -641, - -641, -641, 728, -641, -641, -641, -641, -641, -641, -641, - -641, -641, -641, -641, -641, -641, -641, -641, -641, -641, - -641, -641, -641, -641, -641, -641, -641, -641, -641, -641, - -641, -641, -641, -641, -641, -641, -641, -641, -641, -641, - - -641, -641, -641, -641, -641, -641, -641, -641, -641, -641, + 69, -641, -641, -641, -641, -641, -641, -641, -641, -641, -641, -641, -641, -641, -641, -641, -641, -641, -641, -641, -641, -641, -641, -641, -641, -641, -641, -641, -641, -641, + -641, -641, 846, -641, -641, -641, -641, -641, -641, -641, + -641, -641, -641, -641, -641, -641, -641, -641, 850, 851, + 851, 851, 851, 851, 851, 851, 851, 851, -641, -641, + -641, -641, -641, -641, -641, 846, 846, 846, 846, 846, + + 846, 846, 846, 846, 846, 846, 846, 846, 846, 846, + 846, 846, 846, 846, 846, 846, 846, 846, 846, 846, + 846, -641, -641, -641, -641, -641, -641, -641, -641, -641, -641, -641, -641, -641, -641, -641, -641, -641, -641, -641, -641, -641, -641, -641, -641, -641, -641, -641, -641, -641, -641, -641, -641, -641, -641, -641, -641, -641 }, { - 67, -642, -642, -642, -642, -642, -642, -642, -642, -642, - -642, -642, -642, -642, -642, -642, -642, -642, -642, -642, - -642, -642, -642, -642, -642, -642, -642, -642, -642, -642, - -642, -642, 729, -642, -642, -642, -642, -642, -642, -642, - - -642, -642, -642, -642, -642, -642, -642, -642, -642, -642, - -642, -642, -642, -642, -642, -642, -642, -642, -642, -642, - -642, -642, -642, -642, -642, -642, -642, -642, -642, -642, - -642, -642, -642, -642, -642, -642, -642, -642, -642, -642, + 69, -642, -642, -642, -642, -642, -642, -642, -642, -642, -642, -642, -642, -642, -642, -642, -642, -642, -642, -642, -642, -642, -642, -642, -642, -642, -642, -642, -642, -642, + -642, -642, 852, -642, -642, -642, -642, -642, -642, -642, + + -642, -642, -642, -642, -642, -642, -642, -642, 853, 853, + 853, 853, 853, 853, 853, 853, 853, 853, -642, -642, + -642, -642, -642, -642, -642, 852, 852, 852, 852, 852, + 852, 852, 852, 852, 852, 852, 852, 852, 852, 852, + 852, 852, 852, 852, 852, 852, 852, 852, 852, 852, + 852, -642, -642, -642, -642, -642, -642, -642, -642, -642, -642, -642, -642, -642, -642, -642, -642, -642, -642, -642, -642, -642, -642, -642, -642, -642, -642, -642, -642, -642, -642, -642, -642, -642, -642, -642, -642, -642 }, { - 67, -643, -643, -643, -643, -643, -643, -643, -643, -643, + 69, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, + -643, -643, 854, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, - -643, -643, -643, -643, -643, -643, -643, -643, 730, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, @@ -11519,51 +11680,51 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -644, -644, -644, -644, -644, -644, -644, -644, -644, - -644, -644, -644, -644, -644, -644, -644, -644, -644, -644, - -644, -644, -644, -644, -644, -644, -644, -644, -644, -644, - -644, -644, -644, -644, -644, -644, -644, -644, -644, -644, - -644, -644, -644, -644, -644, -644, -644, -644, -644, -644, + 69, -644, -644, -644, -644, -644, -644, -644, -644, -644, -644, -644, -644, -644, -644, -644, -644, -644, -644, -644, - -644, -644, -644, -644, -644, -644, -644, -644, -644, 731, -644, -644, -644, -644, -644, -644, -644, -644, -644, -644, + -644, -644, 855, -644, -644, -644, -644, -644, -644, -644, + -644, -644, -644, -644, -644, -644, -644, -644, 856, 856, + 856, 856, 856, 856, 856, 856, 856, 856, -644, -644, + -644, -644, -644, -644, -644, 855, 855, 855, 855, 855, + 855, 855, 855, 855, 855, 855, 855, 855, 855, 855, - -644, -644, -644, -644, -644, -644, -644, -644, -644, -644, - -644, -644, -644, -644, -644, -644, -644, -644, -644, -644, + 855, 855, 855, 855, 855, 855, 855, 855, 855, 855, + 855, -644, -644, -644, -644, -644, -644, -644, -644, -644, -644, -644, -644, -644, -644, -644, -644, -644, -644, -644, -644, -644, -644, -644, -644, -644, -644, -644, -644, -644, -644, -644, -644, -644, -644, -644, -644, -644 }, { - 67, -645, -645, -645, -645, -645, -645, -645, -645, -645, - -645, -645, -645, -645, -645, -645, -645, -645, -645, -645, - -645, -645, -645, -645, -645, -645, -645, -645, -645, -645, - -645, -645, -645, -645, -645, -645, -645, -645, -645, -645, - -645, -645, -645, -645, -645, -645, -645, -645, -645, -645, - - -645, -645, -645, -645, -645, -645, -645, -645, -645, -645, - -645, -645, -645, -645, -645, -645, -645, -645, -645, 732, - -645, -645, -645, -645, -645, -645, -645, -645, -645, -645, + 69, -645, -645, -645, -645, -645, -645, -645, -645, -645, -645, -645, -645, -645, -645, -645, -645, -645, -645, -645, -645, -645, -645, -645, -645, -645, -645, -645, -645, -645, + -645, -645, 846, -645, -645, -645, -645, -645, -645, -645, + -645, -645, -645, -645, -645, -645, -645, -645, 857, 858, + + 858, 858, 858, 858, 858, 858, 858, 858, -645, -645, + -645, -645, -645, -645, -645, 846, 846, 846, 846, 846, + 846, 846, 846, 846, 846, 846, 846, 846, 846, 846, + 846, 846, 846, 846, 846, 846, 846, 846, 846, 846, + 846, -645, -645, -645, -645, -645, -645, -645, -645, -645, -645, -645, -645, -645, -645, -645, -645, -645, -645, -645, -645, -645, -645, -645, -645, -645, -645, -645, -645, -645, -645, -645, -645, -645, -645, -645, -645, -645 }, { - 67, -646, -646, -646, -646, -646, -646, -646, -646, -646, + 69, -646, -646, -646, -646, -646, -646, -646, -646, -646, -646, -646, -646, -646, -646, -646, -646, -646, -646, -646, -646, -646, -646, -646, -646, -646, -646, -646, -646, -646, - -646, -646, -646, -646, -646, -646, -646, -646, -646, -646, - -646, -646, -646, -646, -646, -646, -646, -646, -646, -646, - -646, -646, -646, -646, -646, -646, -646, -646, -646, -646, - -646, -646, -646, -646, -646, -646, -646, -646, -646, -646, - -646, 733, -646, -646, -646, -646, -646, -646, -646, -646, - -646, -646, -646, -646, -646, -646, -646, -646, -646, -646, - -646, -646, -646, -646, -646, -646, -646, -646, -646, -646, + -646, -646, 859, -646, -646, -646, -646, -646, -646, -646, + -646, -646, -646, -646, -646, -646, -646, -646, 860, 860, + 860, 860, 860, 860, 860, 860, 860, 860, -646, -646, + -646, -646, -646, -646, -646, 859, 859, 859, 859, 859, + 859, 859, 859, 859, 859, 859, 859, 859, 859, 859, + 859, 859, 859, 859, 859, 859, 859, 859, 859, 859, + 859, -646, -646, -646, -646, -646, -646, -646, -646, -646, -646, -646, -646, -646, -646, -646, -646, -646, -646, -646, -646, -646, -646, -646, -646, -646, -646, -646, -646, -646, @@ -11571,50 +11732,50 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -647, -647, -647, -647, -647, -647, -647, -647, -647, - -647, -647, -647, -647, -647, -647, -647, -647, -647, -647, - -647, -647, -647, -647, -647, -647, -647, -647, -647, -647, - -647, -647, -647, -647, -647, -647, -647, -647, -647, -647, + 69, -647, -647, -647, -647, -647, -647, -647, -647, -647, -647, -647, -647, -647, -647, -647, -647, -647, -647, -647, -647, -647, -647, -647, -647, -647, -647, -647, -647, -647, - -647, -647, -647, -647, -647, -647, -647, -647, -647, -647, - -647, -647, -647, -647, -647, -647, -647, -647, -647, -647, - -647, -647, -647, 734, -647, -647, -647, -647, -647, -647, + -647, -647, 855, -647, -647, -647, -647, -647, -647, -647, + -647, -647, -647, -647, -647, -647, -647, -647, 861, 862, + 862, 862, 862, 862, 862, 862, 862, 862, -647, -647, + -647, -647, -647, -647, -647, 855, 855, 855, 855, 855, + 855, 855, 855, 855, 855, 855, 855, 855, 855, 855, + 855, 855, 855, 855, 855, 855, 855, 855, 855, 855, - -647, -647, -647, -647, -647, -647, -647, -647, -647, -647, + 855, -647, -647, -647, -647, -647, -647, -647, -647, -647, -647, -647, -647, -647, -647, -647, -647, -647, -647, -647, -647, -647, -647, -647, -647, -647, -647, -647, -647, -647, -647, -647, -647, -647, -647, -647, -647, -647 }, { - 67, -648, -648, -648, -648, -648, -648, -648, -648, -648, - -648, -648, -648, -648, -648, -648, -648, -648, -648, -648, - -648, -648, -648, -648, -648, -648, -648, -648, -648, -648, - -648, -648, -648, -648, -648, -648, -648, -648, -648, -648, - -648, -648, -648, -648, -648, 735, -648, -648, -648, -648, - -648, -648, -648, -648, -648, -648, -648, -648, -648, -648, - - -648, -648, -648, -648, -648, -648, -648, -648, -648, -648, - -648, -648, -648, -648, -648, -648, -648, -648, -648, -648, + 69, -648, -648, -648, -648, -648, -648, -648, -648, -648, -648, -648, -648, -648, -648, -648, -648, -648, -648, -648, -648, -648, -648, -648, -648, -648, -648, -648, -648, -648, + -648, -648, 863, -648, -648, -648, -648, -648, -648, -648, + -648, -648, -648, -648, -648, -648, -648, -648, 864, 864, + 864, 864, 864, 864, 864, 864, 864, 864, -648, -648, + + -648, -648, -648, -648, -648, 863, 863, 863, 863, 863, + 863, 863, 863, 863, 863, 863, 863, 863, 863, 863, + 863, 863, 863, 863, 863, 863, 863, 863, 863, 863, + 863, -648, -648, -648, -648, -648, -648, -648, -648, -648, -648, -648, -648, -648, -648, -648, -648, -648, -648, -648, -648, -648, -648, -648, -648, -648, -648, -648, -648, -648, -648, -648, -648, -648, -648, -648, -648, -648 }, { - 67, -649, -649, -649, -649, -649, -649, -649, -649, -649, + 69, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, + -649, -649, 865, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, - -649, -649, -649, -649, -649, -649, -649, -649, -649, 736, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, @@ -11623,16 +11784,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -650, -650, -650, -650, -650, -650, -650, -650, -650, + 69, -650, -650, -650, -650, -650, -650, -650, -650, -650, -650, -650, -650, -650, -650, -650, -650, -650, -650, -650, -650, -650, -650, -650, -650, -650, -650, -650, -650, -650, - -650, -650, -650, -650, -650, -650, -650, -650, -650, -650, - -650, -650, -650, -650, -650, -650, -650, -650, -650, -650, - -650, -650, -650, -650, -650, -650, -650, -650, -650, -650, - -650, -650, -650, -650, -650, -650, -650, -650, -650, -650, - -650, -650, -650, -650, -650, -650, -650, -650, -650, -650, - -650, -650, -650, 737, -650, -650, -650, -650, -650, -650, - -650, -650, -650, -650, -650, -650, -650, -650, -650, -650, + -650, -650, 866, -650, -650, -650, -650, -650, -650, -650, + -650, -650, -650, -650, -650, -650, -650, -650, 856, 856, + 856, 856, 856, 856, 856, 856, 856, 856, -650, -650, + -650, -650, -650, -650, -650, 866, 866, 866, 866, 866, + 866, 866, 866, 866, 866, 866, 866, 866, 866, 866, + 866, 866, 866, 866, 866, 866, 866, 866, 866, 866, + 866, -650, -650, -650, -650, -650, -650, -650, -650, -650, -650, -650, -650, -650, -650, -650, -650, -650, -650, -650, -650, -650, -650, -650, -650, -650, -650, -650, -650, -650, @@ -11640,16 +11801,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -651, -651, -651, -651, -651, -651, -651, -651, -651, + 69, -651, -651, -651, -651, -651, -651, -651, -651, -651, + -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, - -651, -651, -651, -651, -651, -651, -651, -651, -651, 738, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, - -651, 739, -651, -651, -651, -651, -651, -651, -651, -651, + -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, @@ -11657,7 +11818,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -652, -652, -652, -652, -652, -652, -652, -652, -652, + 69, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, @@ -11666,7 +11827,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, - -652, -652, -652, -652, -652, -652, 740, -652, -652, -652, + -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, @@ -11674,7 +11835,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -653, -653, -653, -653, -653, -653, -653, -653, -653, + 69, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, @@ -11683,7 +11844,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, - -653, -653, -653, 741, -653, -653, -653, -653, -653, -653, + -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, @@ -11692,7 +11853,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -654, -654, -654, -654, -654, -654, -654, -654, -654, + 69, -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, @@ -11701,22 +11862,22 @@ static yyconst flex_int16_t yy_nxt[][128] = -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, - -654, -654, -654, 742, -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, + -654, -654, -654, -654, -654, 867, -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, -654 }, { - 67, -655, -655, -655, -655, -655, -655, -655, -655, -655, - -655, -655, -655, -655, -655, -655, -655, -655, -655, -655, + 69, -655, -655, -655, -655, -655, -655, -655, -655, -655, -655, -655, -655, -655, -655, -655, -655, -655, -655, -655, -655, -655, -655, -655, -655, -655, -655, -655, -655, -655, -655, -655, -655, -655, -655, -655, -655, -655, -655, -655, + -655, -655, -655, -655, -655, -655, -655, -655, -655, 868, + 868, 868, 868, 868, 868, 868, 868, 868, -655, -655, -655, -655, -655, -655, -655, -655, -655, -655, -655, -655, - -655, -655, -655, -655, -655, -655, -655, 743, -655, -655, -655, -655, -655, -655, -655, -655, -655, -655, -655, -655, -655, -655, -655, -655, -655, -655, -655, -655, -655, -655, -655, -655, -655, -655, -655, -655, -655, -655, -655, -655, @@ -11726,17 +11887,17 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -656, -656, -656, -656, -656, -656, -656, -656, -656, + 69, -656, -656, -656, -656, -656, -656, -656, -656, -656, -656, -656, -656, -656, -656, -656, -656, -656, -656, -656, -656, -656, -656, -656, -656, -656, -656, -656, -656, -656, - -656, -656, -656, -656, -656, -656, -656, -656, -656, -656, - -656, -656, -656, -656, -656, -656, -656, -656, -656, -656, - -656, -656, -656, -656, -656, -656, -656, -656, -656, -656, - -656, -656, -656, -656, -656, -656, -656, -656, -656, -656, - -656, -656, -656, -656, -656, -656, -656, -656, -656, -656, - -656, -656, -656, 744, -656, -656, -656, -656, -656, -656, - -656, -656, -656, -656, -656, -656, -656, -656, -656, -656, + -656, -656, 869, -656, -656, -656, -656, -656, -656, -656, + -656, -656, -656, -656, -656, -656, -656, -656, 870, 870, + 870, 870, 870, 870, 870, 870, 870, 870, -656, -656, + -656, -656, -656, -656, -656, 869, 869, 869, 869, 869, + 869, 869, 869, 869, 869, 869, 869, 869, 869, 869, + 869, 869, 869, 869, 869, 869, 869, 869, 869, 869, + 869, -656, -656, -656, -656, -656, -656, -656, -656, -656, -656, -656, -656, -656, -656, -656, -656, -656, -656, -656, -656, -656, -656, -656, -656, -656, -656, -656, -656, -656, @@ -11744,14 +11905,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -657, -657, -657, -657, -657, -657, -657, -657, -657, + 69, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, + -657, -657, 871, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, - -657, -657, -657, -657, -657, -657, 745, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, @@ -11761,34 +11922,34 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -658, -658, -658, -658, -658, -658, -658, -658, -658, - -658, -658, -658, -658, -658, -658, -658, -658, -658, -658, - -658, -658, -658, -658, -658, -658, -658, -658, -658, -658, - -658, -658, -658, -658, -658, -658, -658, -658, -658, -658, - -658, -658, -658, -658, -658, -658, -658, -658, -658, -658, - -658, -658, -658, -658, -658, -658, -658, -658, -658, -658, - - -658, -658, -658, -658, -658, -658, -658, -658, -658, -658, + 69, -658, -658, -658, -658, -658, -658, -658, -658, -658, -658, -658, -658, -658, -658, -658, -658, -658, -658, -658, - -658, -658, -658, 746, -658, -658, -658, -658, -658, -658, -658, -658, -658, -658, -658, -658, -658, -658, -658, -658, + -658, -658, 872, -658, -658, -658, -658, -658, -658, -658, + -658, -658, -658, -658, -658, -658, -658, -658, 873, 873, + 873, 873, 873, 873, 873, 873, 873, 873, -658, -658, + + -658, -658, -658, -658, -658, 872, 872, 872, 872, 872, + 872, 872, 872, 872, 872, 872, 872, 872, 872, 872, + 872, 872, 872, 872, 872, 872, 872, 872, 872, 872, + 872, -658, -658, -658, -658, -658, -658, -658, -658, -658, -658, -658, -658, -658, -658, -658, -658, -658, -658, -658, -658, -658, -658, -658, -658, -658, -658, -658, -658, -658, -658, -658, -658, -658, -658, -658, -658, -658 }, { - 67, -659, -659, -659, -659, -659, -659, -659, -659, -659, + 69, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, - -659, -659, 747, -659, -659, -659, -659, -659, -659, -659, + -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, + -659, -659, -659, -659, -659, -659, -659, -659, -659, 874, + 874, 874, 874, 874, 874, 874, 874, 874, -659, -659, + -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, + -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, - -659, -659, -659, -659, -659, 747, 747, 747, 747, 747, - 747, 747, 747, 747, 747, 747, 747, 747, 747, 747, - 747, 747, 747, 747, 747, 747, 747, 747, 747, 747, - 747, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, -659 @@ -11796,16 +11957,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -660, -660, -660, -660, -660, -660, -660, -660, -660, - -660, -660, -660, -660, -660, -660, -660, -660, -660, -660, - -660, -660, -660, -660, -660, -660, -660, -660, -660, -660, - -660, -660, 748, -660, -660, -660, -660, -660, -660, -660, - -660, -660, -660, -660, -660, -660, -660, -660, -660, -660, - -660, -660, -660, -660, -660, -660, -660, -660, -660, -660, - -660, -660, -660, -660, -660, -660, -660, -660, -660, -660, - -660, -660, -660, -660, -660, -660, -660, -660, -660, -660, + 69, -660, -660, -660, -660, -660, -660, -660, -660, -660, -660, -660, -660, -660, -660, -660, -660, -660, -660, -660, -660, -660, -660, -660, -660, -660, -660, -660, -660, -660, + -660, -660, 875, -660, -660, -660, -660, -660, -660, -660, + -660, -660, -660, -660, -660, -660, -660, -660, 876, 876, + 876, 876, 876, 876, 876, 876, 876, 876, -660, -660, + -660, -660, -660, -660, -660, 875, 875, 875, 875, 875, + 875, 875, 875, 875, 875, 875, 875, 875, 875, 875, + 875, 875, 875, 875, 875, 875, 875, 875, 875, 875, + 875, -660, -660, -660, -660, -660, -660, -660, -660, -660, -660, -660, -660, -660, -660, -660, -660, -660, -660, -660, -660, -660, -660, -660, -660, -660, -660, -660, -660, -660, @@ -11813,45 +11974,45 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -661, -661, -661, -661, -661, -661, -661, -661, -661, + 69, -661, -661, -661, -661, -661, -661, -661, -661, -661, + -661, -661, -661, -661, -661, -661, -661, -661, -661, -661, + -661, -661, -661, -661, -661, -661, -661, -661, -661, -661, + -661, -661, 877, -661, -661, -661, -661, -661, -661, -661, + -661, -661, -661, -661, -661, -661, -661, -661, -661, -661, + -661, -661, -661, -661, -661, -661, -661, -661, -661, -661, + -661, -661, -661, -661, -661, -661, -661, -661, -661, -661, + + -661, -661, -661, -661, -661, -661, -661, -661, -661, -661, -661, -661, -661, -661, -661, -661, -661, -661, -661, -661, -661, -661, -661, -661, -661, -661, -661, -661, -661, -661, - -661, -661, 749, -661, -661, -661, -661, -661, -661, -661, - -661, -661, -661, -661, -661, -661, -661, -661, 750, 750, - 750, 750, 750, 750, 750, 750, 750, 750, -661, -661, - -661, -661, -661, -661, -661, 749, 749, 749, 749, 749, - - 749, 749, 749, 749, 749, 749, 749, 749, 749, 749, - 749, 749, 749, 749, 749, 749, 749, 749, 749, 749, - 749, -661, -661, -661, -661, -661, -661, -661, -661, -661, -661, -661, -661, -661, -661, -661, -661, -661, -661, -661, -661, -661, -661, -661, -661, -661, -661, -661, -661, -661, -661, -661, -661, -661, -661, -661, -661, -661 }, { - 67, -662, -662, -662, -662, -662, -662, -662, -662, -662, - -662, -662, -662, -662, -662, -662, -662, -662, -662, -662, - -662, -662, -662, -662, -662, -662, -662, -662, -662, -662, - -662, -662, -662, -662, -662, -662, -662, -662, -662, -662, - - -662, -662, -662, -662, -662, -662, -662, -662, -662, -662, - -662, -662, -662, -662, -662, -662, -662, -662, -662, -662, - -662, -662, -662, -662, -662, -662, -662, -662, -662, 751, - -662, -662, -662, -662, -662, -662, -662, -662, -662, -662, + 69, -662, -662, -662, -662, -662, -662, -662, -662, -662, -662, -662, -662, -662, -662, -662, -662, -662, -662, -662, -662, -662, -662, -662, -662, -662, -662, -662, -662, -662, + -662, -662, 878, -662, -662, -662, -662, -662, -662, -662, + + -662, -662, -662, -662, -662, -662, -662, -662, 879, 879, + 879, 879, 879, 879, 879, 879, 879, 879, -662, -662, + -662, -662, -662, -662, -662, 878, 878, 878, 878, 878, + 878, 878, 878, 878, 878, 878, 878, 878, 878, 878, + 878, 878, 878, 878, 878, 878, 878, 878, 878, 878, + 878, -662, -662, -662, -662, -662, -662, -662, -662, -662, -662, -662, -662, -662, -662, -662, -662, -662, -662, -662, -662, -662, -662, -662, -662, -662, -662, -662, -662, -662, -662, -662, -662, -662, -662, -662, -662, -662 }, { - 67, -663, -663, -663, -663, -663, -663, -663, -663, -663, + 69, -663, -663, -663, -663, -663, -663, -663, -663, -663, -663, -663, -663, -663, -663, -663, -663, -663, -663, -663, -663, -663, -663, -663, -663, -663, -663, -663, -663, -663, - -663, -663, 752, -663, -663, -663, -663, -663, -663, -663, + -663, -663, 880, -663, -663, -663, -663, -663, -663, -663, -663, -663, -663, -663, -663, -663, -663, -663, -663, -663, -663, -663, -663, -663, -663, -663, -663, -663, -663, -663, -663, -663, -663, -663, -663, -663, -663, -663, -663, -663, @@ -11865,49 +12026,49 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -664, -664, -664, -664, -664, -664, -664, -664, -664, + 69, -664, -664, -664, -664, -664, -664, -664, -664, -664, -664, -664, -664, -664, -664, -664, -664, -664, -664, -664, -664, -664, -664, -664, -664, -664, -664, -664, -664, -664, - -664, -664, 753, -664, -664, -664, -664, -664, -664, -664, - -664, -664, -664, -664, -664, -664, -664, -664, 754, 754, - 754, 754, 754, 754, 754, 754, 754, 754, -664, -664, - -664, -664, -664, -664, -664, 753, 753, 753, 753, 753, - 753, 753, 753, 753, 753, 753, 753, 753, 753, 753, - - 753, 753, 753, 753, 753, 753, 753, 753, 753, 753, - 753, -664, -664, -664, -664, -664, -664, -664, -664, -664, + -664, -664, 881, -664, -664, -664, -664, -664, -664, -664, + -664, -664, -664, -664, -664, -664, -664, -664, 882, 882, + 882, 882, 882, 882, 882, 882, 882, 882, -664, -664, + -664, -664, -664, -664, -664, 881, 881, 881, 881, 881, + 881, 881, 881, 881, 881, 881, 881, 881, 881, 881, + + 881, 881, 881, 881, 881, 881, 881, 881, 881, 881, + 881, -664, -664, -664, -664, -664, -664, -664, -664, -664, -664, -664, -664, -664, -664, -664, -664, -664, -664, -664, -664, -664, -664, -664, -664, -664, -664, -664, -664, -664, -664, -664, -664, -664, -664, -664, -664, -664 }, { - 67, -665, -665, -665, -665, -665, -665, -665, -665, -665, - -665, -665, -665, -665, -665, -665, -665, -665, -665, -665, + 69, -665, -665, -665, -665, -665, -665, -665, -665, -665, -665, -665, -665, -665, -665, -665, -665, -665, -665, -665, -665, -665, -665, -665, -665, -665, -665, -665, -665, -665, - -665, -665, -665, -665, -665, -665, -665, -665, -665, -665, - - -665, -665, -665, -665, -665, -665, -665, -665, -665, -665, - -665, -665, -665, -665, -665, -665, -665, -665, -665, -665, - -665, -665, -665, -665, -665, -665, -665, -665, -665, -665, - -665, -665, -665, 755, -665, -665, -665, -665, -665, -665, - -665, -665, -665, -665, -665, -665, -665, -665, -665, -665, + -665, -665, 883, -665, -665, -665, -665, -665, -665, -665, + -665, -665, -665, -665, -665, -665, -665, -665, 884, 884, + + 884, 884, 884, 884, 884, 884, 884, 884, -665, -665, + -665, -665, -665, -665, -665, 883, 883, 883, 883, 883, + 883, 883, 883, 883, 883, 883, 883, 883, 883, 883, + 883, 883, 883, 883, 883, 883, 883, 883, 883, 883, + 883, -665, -665, -665, -665, -665, -665, -665, -665, -665, -665, -665, -665, -665, -665, -665, -665, -665, -665, -665, -665, -665, -665, -665, -665, -665, -665, -665, -665, -665, -665, -665, -665, -665, -665, -665, -665, -665 }, { - 67, -666, -666, -666, -666, -666, -666, -666, -666, -666, + 69, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, + -666, -666, -666, -666, -666, -666, -666, -666, 885, 885, + 885, 885, 885, 885, 885, 885, 885, 885, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, - -666, -666, -666, -666, -666, -666, -666, -666, -666, 756, - -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, @@ -11917,47 +12078,47 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -667, -667, -667, -667, -667, -667, -667, -667, -667, + 69, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, - -667, -667, 757, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, + -667, -667, -667, -667, -667, 886, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, - -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, + -667, -667, -667, -667, -667, 887, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, -667 }, { - 67, -668, -668, -668, -668, -668, -668, -668, -668, -668, + 69, -668, -668, -668, -668, -668, -668, -668, -668, -668, -668, -668, -668, -668, -668, -668, -668, -668, -668, -668, -668, -668, -668, -668, -668, -668, -668, -668, -668, -668, - -668, -668, 758, -668, -668, -668, -668, -668, -668, -668, - -668, -668, -668, -668, -668, -668, -668, -668, 759, 759, - 759, 759, 759, 759, 759, 759, 759, 759, -668, -668, - - -668, -668, -668, -668, -668, 758, 758, 758, 758, 758, - 758, 758, 758, 758, 758, 758, 758, 758, 758, 758, - 758, 758, 758, 758, 758, 758, 758, 758, 758, 758, - 758, -668, -668, -668, -668, -668, -668, -668, -668, -668, + -668, -668, -668, -668, -668, -668, -668, -668, -668, -668, + -668, -668, -668, -668, -668, 886, -668, -668, -668, -668, + -668, -668, -668, -668, -668, -668, -668, -668, -668, -668, + + -668, -668, -668, -668, -668, -668, -668, -668, -668, -668, + -668, -668, -668, -668, -668, -668, -668, -668, -668, -668, + -668, -668, -668, -668, -668, -668, -668, -668, -668, -668, + -668, -668, -668, -668, -668, 888, -668, -668, -668, -668, -668, -668, -668, -668, -668, -668, -668, -668, -668, -668, -668, -668, -668, -668, -668, -668, -668, -668, -668, -668, -668, -668, -668, -668, -668, -668, -668, -668 }, { - 67, -669, -669, -669, -669, -669, -669, -669, -669, -669, + 69, -669, -669, -669, -669, -669, -669, -669, -669, -669, -669, -669, -669, -669, -669, -669, -669, -669, -669, -669, -669, -669, -669, -669, -669, -669, -669, -669, -669, -669, - -669, -669, 760, -669, -669, -669, -669, -669, -669, -669, - -669, -669, -669, -669, -669, -669, -669, -669, -669, -669, -669, -669, -669, -669, -669, -669, -669, -669, -669, -669, + -669, -669, -669, -669, -669, -669, -669, -669, 889, 889, + 889, 889, 889, 889, 889, 889, 889, 889, -669, -669, -669, -669, -669, -669, -669, -669, -669, -669, -669, -669, -669, -669, -669, -669, -669, -669, -669, -669, -669, -669, -669, -669, -669, -669, -669, -669, -669, -669, -669, -669, @@ -11969,16 +12130,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -670, -670, -670, -670, -670, -670, -670, -670, -670, + 69, -670, -670, -670, -670, -670, -670, -670, -670, -670, + -670, -670, -670, -670, -670, -670, -670, -670, -670, -670, -670, -670, -670, -670, -670, -670, -670, -670, -670, -670, -670, -670, -670, -670, -670, -670, -670, -670, -670, -670, - -670, -670, 761, -670, -670, -670, -670, -670, -670, -670, - -670, -670, -670, -670, -670, -670, -670, -670, 762, 762, - 762, 762, 762, 762, 762, 762, 762, 762, -670, -670, - -670, -670, -670, -670, -670, 761, 761, 761, 761, 761, - 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, - 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, - 761, -670, -670, -670, -670, -670, -670, -670, -670, -670, + -670, -670, -670, -670, -670, 886, -670, -670, -670, -670, + -670, -670, -670, -670, -670, -670, -670, -670, -670, -670, + -670, -670, -670, -670, -670, -670, -670, -670, -670, -670, + -670, -670, -670, -670, -670, -670, -670, -670, -670, -670, + -670, -670, -670, -670, -670, -670, -670, -670, -670, -670, + -670, -670, -670, -670, -670, 890, -670, -670, -670, -670, -670, -670, -670, -670, -670, -670, -670, -670, -670, -670, -670, -670, -670, -670, -670, -670, -670, -670, -670, -670, @@ -11986,13 +12147,13 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -671, -671, -671, -671, -671, -671, -671, -671, -671, + 69, -671, -671, -671, -671, -671, -671, -671, -671, -671, -671, -671, -671, -671, -671, -671, -671, -671, -671, -671, -671, -671, -671, -671, -671, -671, -671, -671, -671, -671, -671, -671, -671, -671, -671, -671, -671, -671, -671, -671, + -671, -671, -671, -671, -671, -671, -671, -671, 891, 892, + 892, 892, 892, 892, 892, 892, 892, 892, -671, -671, -671, -671, -671, -671, -671, -671, -671, -671, -671, -671, - -671, -671, -671, -671, -671, -671, -671, -671, -671, -671, - -671, -671, -671, -671, -671, -671, -671, -671, -671, 763, -671, -671, -671, -671, -671, -671, -671, -671, -671, -671, -671, -671, -671, -671, -671, -671, -671, -671, -671, -671, @@ -12003,30 +12164,30 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -672, -672, -672, -672, -672, -672, -672, -672, -672, - -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, - -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, - -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, - - -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, - -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, - -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, - -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, + 69, -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, + -672, -672, 893, -672, -672, -672, -672, -672, -672, -672, + + -672, -672, -672, -672, -672, -672, -672, -672, 894, 894, + 894, 894, 894, 894, 894, 894, 894, 894, -672, -672, + -672, -672, -672, -672, -672, 893, 893, 893, 893, 893, + 893, 893, 893, 893, 893, 893, 893, 893, 893, 893, + 893, 893, 893, 893, 893, 893, 893, 893, 893, 893, + 893, -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, -672 }, { - 67, -673, -673, -673, -673, -673, -673, -673, -673, -673, + 69, -673, -673, -673, -673, -673, -673, -673, -673, -673, -673, -673, -673, -673, -673, -673, -673, -673, -673, -673, -673, -673, -673, -673, -673, -673, -673, -673, -673, -673, -673, -673, -673, -673, -673, -673, -673, -673, -673, -673, - -673, -673, -673, -673, -673, -673, -673, -673, -673, -673, - -673, -673, -673, -673, -673, -673, -673, -673, -673, -673, + -673, -673, -673, -673, -673, -673, -673, -673, 895, 896, + 896, 896, 896, 896, 896, 896, 896, 896, -673, -673, -673, -673, -673, -673, -673, -673, -673, -673, -673, -673, -673, -673, -673, -673, -673, -673, -673, -673, -673, -673, -673, -673, -673, -673, -673, -673, -673, -673, -673, -673, @@ -12038,45 +12199,45 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -674, -674, -674, -674, -674, -674, -674, -674, -674, - -674, -674, -674, -674, -674, -674, -674, -674, -674, -674, - -674, -674, -674, -674, -674, -674, -674, -674, -674, -674, - -674, -674, -674, -674, -674, -674, -674, -674, -674, -674, - -674, -674, -674, -674, -674, -674, -674, -674, -674, -674, - -674, -674, -674, -674, -674, -674, -674, -674, -674, -674, - -674, -674, -674, -674, -674, -674, -674, -674, -674, -674, - -674, -674, -674, -674, -674, -674, -674, -674, -674, -674, - + 69, -674, -674, -674, -674, -674, -674, -674, -674, -674, -674, -674, -674, -674, -674, -674, -674, -674, -674, -674, -674, -674, -674, -674, -674, -674, -674, -674, -674, -674, + -674, -674, 897, -674, -674, -674, -674, -674, -674, -674, + -674, -674, -674, -674, -674, -674, -674, -674, 898, 899, + 899, 899, 899, 899, 899, 899, 899, 899, -674, -674, + -674, -674, -674, -674, -674, 897, 897, 897, 897, 897, + 897, 897, 897, 897, 897, 897, 897, 897, 897, 897, + + 897, 897, 897, 897, 897, 897, 897, 897, 897, 897, + 897, -674, -674, -674, -674, -674, -674, -674, -674, -674, -674, -674, -674, -674, -674, -674, -674, -674, -674, -674, -674, -674, -674, -674, -674, -674, -674, -674, -674, -674, -674, -674, -674, -674, -674, -674, -674, -674 }, { - 67, -675, -675, -675, -675, -675, -675, -675, -675, -675, - -675, -675, -675, -675, -675, -675, -675, -675, -675, -675, - -675, -675, -675, -675, -675, -675, -675, -675, -675, -675, - -675, -675, -675, -675, -675, -675, -675, -675, -675, -675, - -675, -675, -675, -675, -675, -675, -675, -675, -675, -675, - - -675, -675, -675, -675, -675, -675, -675, -675, -675, -675, - -675, -675, -675, -675, -675, -675, -675, -675, -675, -675, - -675, -675, -675, -675, -675, -675, -675, -675, -675, -675, + 69, -675, -675, -675, -675, -675, -675, -675, -675, -675, -675, -675, -675, -675, -675, -675, -675, -675, -675, -675, -675, -675, -675, -675, -675, -675, -675, -675, -675, -675, + -675, -675, 897, -675, -675, -675, -675, -675, -675, -675, + -675, -675, -675, -675, -675, -675, -675, -675, 900, 900, + + 900, 900, 900, 900, 900, 900, 900, 900, -675, -675, + -675, -675, -675, -675, -675, 897, 897, 897, 897, 897, + 897, 897, 897, 897, 897, 897, 897, 897, 897, 897, + 897, 897, 897, 897, 897, 897, 897, 897, 897, 897, + 897, -675, -675, -675, -675, -675, -675, -675, -675, -675, -675, -675, -675, -675, -675, -675, -675, -675, -675, -675, -675, -675, -675, -675, -675, -675, -675, -675, -675, -675, -675, -675, -675, -675, -675, -675, -675, -675 }, { - 67, -676, -676, -676, -676, -676, -676, -676, -676, -676, + 69, -676, -676, -676, -676, -676, -676, -676, -676, -676, -676, -676, -676, -676, -676, -676, -676, -676, -676, -676, -676, -676, -676, -676, -676, -676, -676, -676, -676, -676, - -676, -676, -676, -676, -676, -676, -676, -676, -676, -676, + -676, -676, 901, -676, -676, -676, -676, -676, -676, -676, -676, -676, -676, -676, -676, -676, -676, -676, -676, -676, -676, -676, -676, -676, -676, -676, -676, -676, -676, -676, -676, -676, -676, -676, -676, -676, -676, -676, -676, -676, @@ -12090,51 +12251,51 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -677, -677, -677, -677, -677, -677, -677, -677, -677, + 69, -677, -677, -677, -677, -677, -677, -677, -677, -677, -677, -677, -677, -677, -677, -677, -677, -677, -677, -677, -677, -677, -677, -677, -677, -677, -677, -677, -677, -677, - -677, -677, -677, -677, -677, -677, -677, -677, -677, -677, - -677, -677, -677, -677, -677, -677, -677, -677, -677, -677, - -677, -677, -677, -677, -677, -677, -677, -677, -677, -677, - -677, -677, -677, -677, -677, -677, -677, -677, -677, -677, - -677, -677, -677, -677, -677, -677, -677, -677, -677, -677, - -677, -677, -677, -677, -677, -677, -677, -677, -677, -677, - - -677, -677, -677, -677, -677, -677, -677, -677, -677, -677, + -677, -677, 902, -677, -677, -677, -677, -677, -677, -677, + -677, -677, -677, -677, -677, -677, -677, -677, 903, 903, + 903, 903, 903, 903, 903, 903, 903, 903, -677, -677, + -677, -677, -677, -677, -677, 902, 902, 902, 902, 902, + 902, 902, 902, 902, 902, 902, 902, 902, 902, 902, + 902, 902, 902, 902, 902, 902, 902, 902, 902, 902, + + 902, -677, -677, -677, -677, -677, -677, -677, -677, -677, -677, -677, -677, -677, -677, -677, -677, -677, -677, -677, -677, -677, -677, -677, -677, -677, -677, -677, -677, -677, -677, -677, -677, -677, -677, -677, -677, -677 }, { - 67, -678, -678, -678, -678, -678, -678, -678, -678, -678, - -678, -678, -678, -678, -678, -678, -678, -678, -678, -678, - -678, -678, -678, -678, -678, -678, -678, -678, -678, -678, - -678, -678, -678, -678, -678, -678, -678, -678, -678, -678, - -678, -678, -678, -678, -678, -678, -678, -678, -678, -678, - -678, -678, -678, -678, -678, -678, -678, -678, -678, -678, - - -678, -678, -678, -678, -678, -678, -678, -678, -678, -678, - -678, -678, -678, -678, -678, -678, -678, -678, -678, -678, + 69, -678, -678, -678, -678, -678, -678, -678, -678, -678, -678, -678, -678, -678, -678, -678, -678, -678, -678, -678, -678, -678, -678, -678, -678, -678, -678, -678, -678, -678, + -678, -678, 904, -678, -678, -678, -678, -678, -678, -678, + -678, -678, -678, -678, -678, -678, -678, -678, 905, 906, + 906, 906, 906, 906, 906, 906, 906, 906, -678, -678, + + -678, -678, -678, -678, -678, 904, 904, 904, 904, 904, + 904, 904, 904, 904, 904, 904, 904, 904, 904, 904, + 904, 904, 904, 904, 904, 904, 904, 904, 904, 904, + 904, -678, -678, -678, -678, -678, -678, -678, -678, -678, -678, -678, -678, -678, -678, -678, -678, -678, -678, -678, -678, -678, -678, -678, -678, -678, -678, -678, -678, -678, -678, -678, -678, -678, -678, -678, -678, -678 }, { - 67, -679, -679, -679, -679, -679, -679, -679, -679, -679, + 69, -679, -679, -679, -679, -679, -679, -679, -679, -679, -679, -679, -679, -679, -679, -679, -679, -679, -679, -679, -679, -679, -679, -679, -679, -679, -679, -679, -679, -679, - -679, -679, -679, -679, -679, -679, -679, -679, -679, -679, - -679, -679, -679, -679, -679, -679, -679, -679, -679, -679, - -679, -679, -679, -679, -679, -679, -679, -679, -679, -679, - -679, -679, -679, -679, -679, -679, -679, -679, -679, -679, - -679, -679, -679, -679, -679, -679, -679, -679, -679, -679, - -679, -679, -679, -679, -679, -679, -679, -679, -679, -679, - -679, -679, -679, -679, -679, -679, -679, -679, -679, -679, + -679, -679, 904, -679, -679, -679, -679, -679, -679, -679, + -679, -679, -679, -679, -679, -679, -679, -679, 907, 907, + 907, 907, 907, 907, 907, 907, 907, 907, -679, -679, + -679, -679, -679, -679, -679, 904, 904, 904, 904, 904, + 904, 904, 904, 904, 904, 904, 904, 904, 904, 904, + 904, 904, 904, 904, 904, 904, 904, 904, 904, 904, + 904, -679, -679, -679, -679, -679, -679, -679, -679, -679, -679, -679, -679, -679, -679, -679, -679, -679, -679, -679, -679, -679, -679, -679, -679, -679, -679, -679, -679, -679, -679, -679, -679, -679, -679, -679, -679, -679 @@ -12142,10 +12303,10 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -680, -680, -680, -680, -680, -680, -680, -680, -680, - -680, -680, -680, -680, -680, -680, -680, -680, -680, -680, + 69, -680, -680, -680, -680, -680, -680, -680, -680, -680, -680, -680, -680, -680, -680, -680, -680, -680, -680, -680, -680, -680, -680, -680, -680, -680, -680, -680, -680, -680, + -680, -680, 908, -680, -680, -680, -680, -680, -680, -680, -680, -680, -680, -680, -680, -680, -680, -680, -680, -680, -680, -680, -680, -680, -680, -680, -680, -680, -680, -680, -680, -680, -680, -680, -680, -680, -680, -680, -680, -680, @@ -12159,51 +12320,51 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -681, -681, -681, -681, -681, -681, -681, -681, -681, - -681, -681, -681, -681, -681, -681, -681, -681, -681, -681, - -681, -681, -681, -681, -681, -681, -681, -681, -681, -681, - -681, -681, -681, -681, -681, -681, -681, -681, -681, -681, - -681, -681, -681, -681, -681, -681, -681, -681, -681, -681, - -681, -681, -681, -681, -681, -681, -681, -681, -681, -681, - -681, -681, -681, -681, -681, -681, -681, -681, -681, -681, - - -681, -681, -681, -681, -681, -681, -681, -681, -681, -681, + 69, -681, -681, -681, -681, -681, -681, -681, -681, -681, -681, -681, -681, -681, -681, -681, -681, -681, -681, -681, -681, -681, -681, -681, -681, -681, -681, -681, -681, -681, + -681, -681, 909, -681, -681, -681, -681, -681, -681, -681, + -681, -681, -681, -681, -681, -681, -681, -681, 910, 911, + 911, 911, 911, 911, 911, 911, 911, 911, -681, -681, + -681, -681, -681, -681, -681, 909, 909, 909, 909, 909, + + 909, 909, 909, 909, 909, 909, 909, 909, 909, 909, + 909, 909, 909, 909, 909, 909, 909, 909, 909, 909, + 909, -681, -681, -681, -681, -681, -681, -681, -681, -681, -681, -681, -681, -681, -681, -681, -681, -681, -681, -681, -681, -681, -681, -681, -681, -681, -681, -681, -681, -681, -681, -681, -681, -681, -681, -681, -681, -681 }, { - 67, -682, -682, -682, -682, -682, -682, -682, -682, -682, - -682, -682, -682, -682, -682, -682, -682, -682, -682, -682, - -682, -682, -682, -682, -682, -682, -682, -682, -682, -682, - -682, -682, -682, -682, -682, -682, -682, -682, -682, -682, - - -682, -682, -682, -682, -682, -682, -682, -682, -682, -682, - -682, -682, -682, -682, -682, -682, -682, -682, -682, -682, - -682, -682, -682, -682, -682, -682, -682, -682, -682, -682, - -682, -682, -682, -682, -682, -682, -682, -682, -682, -682, + 69, -682, -682, -682, -682, -682, -682, -682, -682, -682, -682, -682, -682, -682, -682, -682, -682, -682, -682, -682, -682, -682, -682, -682, -682, -682, -682, -682, -682, -682, + -682, -682, 909, -682, -682, -682, -682, -682, -682, -682, + + -682, -682, -682, -682, -682, -682, -682, -682, 912, 912, + 912, 912, 912, 912, 912, 912, 912, 912, -682, -682, + -682, -682, -682, -682, -682, 909, 909, 909, 909, 909, + 909, 909, 909, 909, 909, 909, 909, 909, 909, 909, + 909, 909, 909, 909, 909, 909, 909, 909, 909, 909, + 909, -682, -682, -682, -682, -682, -682, -682, -682, -682, -682, -682, -682, -682, -682, -682, -682, -682, -682, -682, -682, -682, -682, -682, -682, -682, -682, -682, -682, -682, -682, -682, -682, -682, -682, -682, -682, -682 }, { - 67, -683, -683, -683, -683, -683, -683, -683, -683, -683, + 69, -683, -683, -683, -683, -683, -683, -683, -683, -683, -683, -683, -683, -683, -683, -683, -683, -683, -683, -683, -683, -683, -683, -683, -683, -683, -683, -683, -683, -683, - -683, -683, -683, -683, -683, -683, -683, -683, -683, -683, - -683, -683, -683, -683, -683, -683, -683, -683, -683, -683, - -683, -683, -683, -683, -683, -683, -683, -683, -683, -683, - -683, -683, -683, -683, -683, -683, -683, -683, -683, -683, - -683, -683, -683, -683, -683, -683, -683, -683, -683, -683, - -683, -683, -683, -683, -683, -683, -683, -683, -683, -683, - -683, -683, -683, -683, -683, -683, -683, -683, -683, -683, + -683, -683, 913, -683, -683, -683, -683, -683, -683, -683, + -683, -683, -683, -683, -683, -683, -683, -683, 903, 903, + 903, 903, 903, 903, 903, 903, 903, 903, -683, -683, + -683, -683, -683, -683, -683, 913, 913, 913, 913, 913, + 913, 913, 913, 913, 913, 913, 913, 913, 913, 913, + 913, 913, 913, 913, 913, 913, 913, 913, 913, 913, + 913, -683, -683, -683, -683, -683, -683, -683, -683, -683, -683, -683, -683, -683, -683, -683, -683, -683, -683, -683, -683, -683, -683, -683, -683, -683, -683, -683, -683, -683, @@ -12211,7 +12372,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -684, -684, -684, -684, -684, -684, -684, -684, -684, + 69, -684, -684, -684, -684, -684, -684, -684, -684, -684, -684, -684, -684, -684, -684, -684, -684, -684, -684, -684, -684, -684, -684, -684, -684, -684, -684, -684, -684, -684, -684, -684, -684, -684, -684, -684, -684, -684, -684, -684, @@ -12221,20 +12382,20 @@ static yyconst flex_int16_t yy_nxt[][128] = -684, -684, -684, -684, -684, -684, -684, -684, -684, -684, -684, -684, -684, -684, -684, -684, -684, -684, -684, -684, - -684, -684, -684, -684, -684, -684, -684, -684, -684, -684, + -684, -684, -684, -684, -684, 914, -684, -684, -684, -684, -684, -684, -684, -684, -684, -684, -684, -684, -684, -684, -684, -684, -684, -684, -684, -684, -684, -684, -684, -684, -684, -684, -684, -684, -684, -684, -684, -684 }, { - 67, -685, -685, -685, -685, -685, -685, -685, -685, -685, - -685, -685, -685, -685, -685, -685, -685, -685, -685, -685, + 69, -685, -685, -685, -685, -685, -685, -685, -685, -685, -685, -685, -685, -685, -685, -685, -685, -685, -685, -685, -685, -685, -685, -685, -685, -685, -685, -685, -685, -685, -685, -685, -685, -685, -685, -685, -685, -685, -685, -685, + -685, -685, -685, -685, -685, -685, -685, -685, 915, 916, - -685, -685, -685, -685, -685, -685, -685, -685, -685, -685, + 916, 916, 916, 916, 916, 916, 916, 916, -685, -685, -685, -685, -685, -685, -685, -685, -685, -685, -685, -685, -685, -685, -685, -685, -685, -685, -685, -685, -685, -685, -685, -685, -685, -685, -685, -685, -685, -685, -685, -685, @@ -12245,17 +12406,17 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -686, -686, -686, -686, -686, -686, -686, -686, -686, + 69, -686, -686, -686, -686, -686, -686, -686, -686, -686, -686, -686, -686, -686, -686, -686, -686, -686, -686, -686, -686, -686, -686, -686, -686, -686, -686, -686, -686, -686, + -686, -686, 917, -686, -686, -686, -686, -686, -686, -686, -686, -686, -686, -686, -686, -686, -686, -686, -686, -686, -686, -686, -686, -686, -686, -686, -686, -686, -686, -686, - -686, -686, -686, -686, -686, -686, -686, -686, -686, -686, - -686, -686, -686, -686, -686, -686, -686, -686, -686, -686, - -686, -686, -686, -686, -686, -686, -686, -686, -686, -686, - -686, -686, -686, -686, -686, -686, -686, -686, -686, -686, - -686, -686, -686, -686, -686, -686, -686, -686, -686, -686, + -686, -686, -686, -686, -686, 917, 917, 917, 917, 917, + 917, 917, 917, 917, 917, 917, 917, 917, 917, 917, + 917, 917, 917, 917, 917, 917, 917, 917, 917, 917, + 917, -686, -686, -686, -686, -686, -686, -686, -686, -686, -686, -686, -686, -686, -686, -686, -686, -686, -686, -686, -686, -686, -686, -686, -686, -686, -686, -686, -686, -686, @@ -12263,27 +12424,27 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -687, -687, -687, -687, -687, -687, -687, -687, -687, - -687, -687, -687, -687, -687, -687, -687, -687, -687, -687, + 69, -687, -687, -687, -687, -687, -687, -687, -687, -687, -687, -687, -687, -687, -687, -687, -687, -687, -687, -687, -687, -687, -687, -687, -687, -687, -687, -687, -687, -687, - -687, -687, -687, -687, -687, -687, -687, -687, -687, -687, - -687, -687, -687, -687, -687, -687, -687, -687, -687, -687, - -687, -687, -687, -687, -687, -687, -687, -687, -687, -687, - -687, -687, -687, -687, -687, -687, -687, -687, -687, -687, - -687, -687, -687, -687, -687, -687, -687, -687, -687, -687, - - -687, -687, -687, -687, -687, -687, -687, -687, -687, -687, + -687, -687, 917, -687, -687, -687, -687, -687, -687, -687, + -687, -687, -687, -687, -687, -687, -687, -687, 918, 918, + 918, 918, 918, 918, 918, 918, 918, 918, -687, -687, + -687, -687, -687, -687, -687, 917, 917, 917, 917, 917, + 917, 917, 917, 917, 917, 917, 917, 917, 917, 917, + 917, 917, 917, 917, 917, 917, 917, 917, 917, 917, + + 917, -687, -687, -687, -687, -687, -687, -687, -687, -687, -687, -687, -687, -687, -687, -687, -687, -687, -687, -687, -687, -687, -687, -687, -687, -687, -687, -687, -687, -687, -687, -687, -687, -687, -687, -687, -687, -687 }, { - 67, -688, -688, -688, -688, -688, -688, -688, -688, -688, - -688, -688, -688, -688, -688, -688, -688, -688, -688, -688, + 69, -688, -688, -688, -688, -688, -688, -688, -688, -688, -688, -688, -688, -688, -688, -688, -688, -688, -688, -688, -688, -688, -688, -688, -688, -688, -688, -688, -688, -688, + -688, -688, 919, -688, -688, -688, -688, -688, -688, -688, -688, -688, -688, -688, -688, -688, -688, -688, -688, -688, -688, -688, -688, -688, -688, -688, -688, -688, -688, -688, @@ -12297,17 +12458,17 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -689, -689, -689, -689, -689, -689, -689, -689, -689, + 69, -689, -689, -689, -689, -689, -689, -689, -689, -689, -689, -689, -689, -689, -689, -689, -689, -689, -689, -689, -689, -689, -689, -689, -689, -689, -689, -689, -689, -689, - -689, -689, -689, -689, -689, -689, -689, -689, -689, -689, - -689, -689, -689, -689, -689, -689, -689, -689, -689, -689, - -689, -689, -689, -689, -689, -689, -689, -689, -689, -689, - -689, -689, -689, -689, -689, -689, -689, -689, -689, -689, - -689, -689, -689, -689, -689, -689, -689, -689, -689, -689, - -689, -689, -689, -689, -689, -689, -689, -689, -689, -689, - -689, -689, -689, -689, -689, -689, -689, -689, -689, -689, + -689, -689, 920, -689, -689, -689, -689, -689, -689, -689, + -689, -689, -689, -689, -689, -689, -689, -689, 921, 921, + 921, 921, 921, 921, 921, 921, 921, 921, -689, -689, + -689, -689, -689, -689, -689, 920, 920, 920, 920, 920, + 920, 920, 920, 920, 920, 920, 920, 920, 920, 920, + 920, 920, 920, 920, 920, 920, 920, 920, 920, 920, + 920, -689, -689, -689, -689, -689, -689, -689, -689, -689, -689, -689, -689, -689, -689, -689, -689, -689, -689, -689, -689, -689, -689, -689, -689, -689, -689, -689, -689, -689, -689, -689, -689, -689, -689, -689, -689, -689 @@ -12315,12 +12476,12 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -690, -690, -690, -690, -690, -690, -690, -690, -690, - -690, -690, -690, -690, -690, -690, -690, -690, -690, -690, - -690, -690, -690, -690, -690, -690, -690, -690, -690, -690, + 69, -690, -690, -690, -690, -690, -690, -690, -690, -690, -690, -690, -690, -690, -690, -690, -690, -690, -690, -690, -690, -690, -690, -690, -690, -690, -690, -690, -690, -690, -690, -690, -690, -690, -690, -690, -690, -690, -690, -690, + -690, -690, -690, -690, -690, -690, -690, -690, 922, 922, + 922, 922, 922, 922, 922, 922, 922, 922, -690, -690, -690, -690, -690, -690, -690, -690, -690, -690, -690, -690, -690, -690, -690, -690, -690, -690, -690, -690, -690, -690, -690, -690, -690, -690, -690, -690, -690, -690, -690, -690, @@ -12332,45 +12493,45 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -691, -691, -691, -691, -691, -691, -691, -691, -691, - -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, - -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, + 69, -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, + -691, -691, 923, -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, + -691, -691, -691, -691, -691, 923, 923, 923, 923, 923, - -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, - -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, - -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, + 923, 923, 923, 923, 923, 923, 923, 923, 923, 923, + 923, 923, 923, 923, 923, 923, 923, 923, 923, 923, + 923, -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, -691 }, { - 67, -692, -692, -692, -692, -692, -692, -692, -692, -692, - -692, -692, -692, -692, -692, -692, -692, -692, -692, -692, - -692, -692, -692, -692, -692, -692, -692, -692, -692, -692, - -692, -692, -692, -692, -692, -692, -692, -692, -692, -692, - - -692, -692, -692, -692, -692, -692, -692, -692, -692, -692, - -692, -692, -692, -692, -692, -692, -692, -692, -692, -692, - -692, -692, -692, -692, -692, -692, -692, -692, -692, -692, - -692, -692, -692, -692, -692, -692, -692, -692, -692, -692, + 69, -692, -692, -692, -692, -692, -692, -692, -692, -692, -692, -692, -692, -692, -692, -692, -692, -692, -692, -692, -692, -692, -692, -692, -692, -692, -692, -692, -692, -692, + -692, -692, 923, -692, -692, -692, -692, -692, -692, -692, + + -692, -692, -692, -692, -692, -692, -692, -692, 924, 924, + 924, 924, 924, 924, 924, 924, 924, 924, -692, -692, + -692, -692, -692, -692, -692, 923, 923, 923, 923, 923, + 923, 923, 923, 923, 923, 923, 923, 923, 923, 923, + 923, 923, 923, 923, 923, 923, 923, 923, 923, 923, + 923, -692, -692, -692, -692, -692, -692, -692, -692, -692, -692, -692, -692, -692, -692, -692, -692, -692, -692, -692, -692, -692, -692, -692, -692, -692, -692, -692, -692, -692, -692, -692, -692, -692, -692, -692, -692, -692 }, { - 67, -693, -693, -693, -693, -693, -693, -693, -693, -693, + 69, -693, -693, -693, -693, -693, -693, -693, -693, -693, -693, -693, -693, -693, -693, -693, -693, -693, -693, -693, -693, -693, -693, -693, -693, -693, -693, -693, -693, -693, - -693, -693, -693, -693, -693, -693, -693, -693, -693, -693, + -693, -693, 925, -693, -693, -693, -693, -693, -693, -693, -693, -693, -693, -693, -693, -693, -693, -693, -693, -693, -693, -693, -693, -693, -693, -693, -693, -693, -693, -693, -693, -693, -693, -693, -693, -693, -693, -693, -693, -693, @@ -12384,24 +12545,24 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -694, -694, -694, -694, -694, -694, -694, -694, -694, - -694, -694, -694, -694, -694, -694, -694, -694, -694, -694, - -694, -694, -694, -694, -694, -694, -694, -694, -694, -694, - -694, -694, -694, -694, -694, -694, -694, -694, -694, -694, - -694, -694, -694, -694, -694, -694, -694, -694, -694, -694, - -694, -694, -694, -694, -694, -694, -694, -694, -694, -694, - -694, -694, -694, -694, -694, -694, -694, -694, -694, -694, - -694, -694, -694, -694, -694, -694, -694, -694, -694, -694, - + 69, -694, -694, -694, -694, -694, -694, -694, -694, -694, -694, -694, -694, -694, -694, -694, -694, -694, -694, -694, -694, -694, -694, -694, -694, -694, -694, -694, -694, -694, + -694, -694, 926, -694, -694, -694, -694, -694, -694, -694, + -694, -694, -694, -694, -694, -694, -694, -694, 927, 927, + 927, 927, 927, 927, 927, 927, 927, 927, -694, -694, + -694, -694, -694, -694, -694, 926, 926, 926, 926, 926, + 926, 926, 926, 926, 926, 926, 926, 926, 926, 926, + + 926, 926, 926, 926, 926, 926, 926, 926, 926, 926, + 926, -694, -694, -694, -694, -694, -694, -694, -694, -694, -694, -694, -694, -694, -694, -694, -694, -694, -694, -694, -694, -694, -694, -694, -694, -694, -694, -694, -694, -694, -694, -694, -694, -694, -694, -694, -694, -694 }, { - 67, -695, -695, -695, -695, -695, -695, -695, -695, -695, + 69, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, @@ -12411,20 +12572,20 @@ static yyconst flex_int16_t yy_nxt[][128] = -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, - -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, + -695, -695, -695, -695, -695, 928, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, -695 }, { - 67, -696, -696, -696, -696, -696, -696, -696, -696, -696, + 69, -696, -696, -696, -696, -696, -696, -696, -696, -696, -696, -696, -696, -696, -696, -696, -696, -696, -696, -696, -696, -696, -696, -696, -696, -696, -696, -696, -696, -696, -696, -696, -696, -696, -696, -696, -696, -696, -696, -696, - -696, -696, -696, -696, -696, -696, -696, -696, -696, -696, - -696, -696, -696, -696, -696, -696, -696, -696, -696, -696, + -696, -696, -696, -696, -696, -696, -696, -696, 929, 930, + 930, 930, 930, 930, 930, 930, 930, 930, -696, -696, -696, -696, -696, -696, -696, -696, -696, -696, -696, -696, -696, -696, -696, -696, -696, -696, -696, -696, -696, -696, -696, -696, -696, -696, -696, -696, -696, -696, -696, -696, @@ -12436,45 +12597,45 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -697, -697, -697, -697, -697, -697, -697, -697, -697, - -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, - -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, - -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, - -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, + 69, -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, + -697, -697, 931, -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, + -697, -697, -697, -697, -697, 931, 931, 931, 931, 931, + 931, 931, 931, 931, 931, 931, 931, 931, 931, 931, + 931, 931, 931, 931, 931, 931, 931, 931, 931, 931, - -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, + 931, -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, -697 }, { - 67, -698, -698, -698, -698, -698, -698, -698, -698, -698, - -698, -698, -698, -698, -698, -698, -698, -698, -698, -698, - -698, -698, -698, -698, -698, -698, -698, -698, -698, -698, - -698, -698, -698, -698, -698, -698, -698, -698, -698, -698, - -698, -698, -698, -698, -698, -698, -698, -698, -698, -698, - -698, -698, -698, -698, -698, -698, -698, -698, -698, -698, - - -698, -698, -698, -698, -698, -698, -698, -698, -698, -698, - -698, -698, -698, -698, -698, -698, -698, -698, -698, -698, + 69, -698, -698, -698, -698, -698, -698, -698, -698, -698, -698, -698, -698, -698, -698, -698, -698, -698, -698, -698, -698, -698, -698, -698, -698, -698, -698, -698, -698, -698, + -698, -698, 931, -698, -698, -698, -698, -698, -698, -698, + -698, -698, -698, -698, -698, -698, -698, -698, 932, 932, + 932, 932, 932, 932, 932, 932, 932, 932, -698, -698, + + -698, -698, -698, -698, -698, 931, 931, 931, 931, 931, + 931, 931, 931, 931, 931, 931, 931, 931, 931, 931, + 931, 931, 931, 931, 931, 931, 931, 931, 931, 931, + 931, -698, -698, -698, -698, -698, -698, -698, -698, -698, -698, -698, -698, -698, -698, -698, -698, -698, -698, -698, -698, -698, -698, -698, -698, -698, -698, -698, -698, -698, -698, -698, -698, -698, -698, -698, -698, -698 }, { - 67, -699, -699, -699, -699, -699, -699, -699, -699, -699, + 69, -699, -699, -699, -699, -699, -699, -699, -699, -699, -699, -699, -699, -699, -699, -699, -699, -699, -699, -699, -699, -699, -699, -699, -699, -699, -699, -699, -699, -699, - -699, -699, -699, -699, -699, -699, -699, -699, -699, -699, + -699, -699, 933, -699, -699, -699, -699, -699, -699, -699, -699, -699, -699, -699, -699, -699, -699, -699, -699, -699, -699, -699, -699, -699, -699, -699, -699, -699, -699, -699, -699, -699, -699, -699, -699, -699, -699, -699, -699, -699, @@ -12488,16 +12649,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -700, -700, -700, -700, -700, -700, -700, -700, -700, - -700, -700, -700, -700, -700, -700, -700, -700, -700, -700, - -700, -700, -700, -700, -700, -700, -700, -700, -700, -700, - -700, -700, -700, -700, -700, -700, -700, -700, -700, -700, - -700, -700, -700, -700, -700, -700, -700, -700, -700, -700, - -700, -700, -700, -700, -700, -700, -700, -700, -700, -700, - -700, -700, -700, -700, -700, -700, -700, -700, -700, -700, - -700, -700, -700, -700, -700, -700, -700, -700, -700, -700, + 69, -700, -700, -700, -700, -700, -700, -700, -700, -700, -700, -700, -700, -700, -700, -700, -700, -700, -700, -700, -700, -700, -700, -700, -700, -700, -700, -700, -700, -700, + -700, -700, 934, -700, -700, -700, -700, -700, -700, -700, + -700, -700, -700, -700, -700, -700, -700, -700, 935, 935, + 935, 935, 935, 935, 935, 935, 935, 935, -700, -700, + -700, -700, -700, -700, -700, 934, 934, 934, 934, 934, + 934, 934, 934, 934, 934, 934, 934, 934, 934, 934, + 934, 934, 934, 934, 934, 934, 934, 934, 934, 934, + 934, -700, -700, -700, -700, -700, -700, -700, -700, -700, -700, -700, -700, -700, -700, -700, -700, -700, -700, -700, -700, -700, -700, -700, -700, -700, -700, -700, -700, -700, @@ -12505,12 +12666,12 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -701, -701, -701, -701, -701, -701, -701, -701, -701, - -701, -701, -701, -701, -701, -701, -701, -701, -701, -701, - -701, -701, -701, -701, -701, -701, -701, -701, -701, -701, + 69, -701, -701, -701, -701, -701, -701, -701, -701, -701, -701, -701, -701, -701, -701, -701, -701, -701, -701, -701, -701, -701, -701, -701, -701, -701, -701, -701, -701, -701, -701, -701, -701, -701, -701, -701, -701, -701, -701, -701, + -701, -701, -701, -701, -701, -701, -701, -701, 936, 936, + 936, 936, 936, 936, 936, 936, 936, 936, -701, -701, -701, -701, -701, -701, -701, -701, -701, -701, -701, -701, -701, -701, -701, -701, -701, -701, -701, -701, -701, -701, @@ -12522,34 +12683,34 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -702, -702, -702, -702, -702, -702, -702, -702, -702, - -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, + 69, -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, + -702, -702, 937, -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, - -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, - -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, - -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, - -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, + -702, -702, -702, -702, -702, 937, 937, 937, 937, 937, + 937, 937, 937, 937, 937, 937, 937, 937, 937, 937, + 937, 937, 937, 937, 937, 937, 937, 937, 937, 937, + 937, -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, -702 }, { - 67, -703, -703, -703, -703, -703, -703, -703, -703, -703, + 69, -703, -703, -703, -703, -703, -703, -703, -703, -703, -703, -703, -703, -703, -703, -703, -703, -703, -703, -703, -703, -703, -703, -703, -703, -703, -703, -703, -703, -703, - -703, -703, -703, -703, -703, -703, -703, -703, -703, -703, - -703, -703, -703, -703, -703, -703, -703, -703, -703, -703, - -703, -703, -703, -703, -703, -703, -703, -703, -703, -703, - -703, -703, -703, -703, -703, -703, -703, -703, -703, -703, - -703, -703, -703, -703, -703, -703, -703, -703, -703, -703, - -703, -703, -703, -703, -703, -703, -703, -703, -703, -703, - -703, -703, -703, -703, -703, -703, -703, -703, -703, -703, + -703, -703, 937, -703, -703, -703, -703, -703, -703, -703, + -703, -703, -703, -703, -703, -703, -703, -703, 938, 938, + 938, 938, 938, 938, 938, 938, 938, 938, -703, -703, + -703, -703, -703, -703, -703, 937, 937, 937, 937, 937, + 937, 937, 937, 937, 937, 937, 937, 937, 937, 937, + 937, 937, 937, 937, 937, 937, 937, 937, 937, 937, + 937, -703, -703, -703, -703, -703, -703, -703, -703, -703, -703, -703, -703, -703, -703, -703, -703, -703, -703, -703, -703, -703, -703, -703, -703, -703, -703, -703, -703, -703, @@ -12557,10 +12718,10 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -704, -704, -704, -704, -704, -704, -704, -704, -704, - -704, -704, -704, -704, -704, -704, -704, -704, -704, -704, + 69, -704, -704, -704, -704, -704, -704, -704, -704, -704, -704, -704, -704, -704, -704, -704, -704, -704, -704, -704, -704, -704, -704, -704, -704, -704, -704, -704, -704, -704, + -704, -704, 939, -704, -704, -704, -704, -704, -704, -704, -704, -704, -704, -704, -704, -704, -704, -704, -704, -704, -704, -704, -704, -704, -704, -704, -704, -704, -704, -704, -704, -704, -704, -704, -704, -704, -704, -704, -704, -704, @@ -12574,24 +12735,24 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -705, -705, -705, -705, -705, -705, -705, -705, -705, - -705, -705, -705, -705, -705, -705, -705, -705, -705, -705, - -705, -705, -705, -705, -705, -705, -705, -705, -705, -705, - -705, -705, -705, -705, -705, -705, -705, -705, -705, -705, - -705, -705, -705, -705, -705, -705, -705, -705, -705, -705, - - -705, -705, -705, -705, -705, -705, -705, -705, -705, -705, - -705, -705, -705, -705, -705, -705, -705, -705, -705, -705, - -705, -705, -705, -705, -705, -705, -705, -705, -705, -705, + 69, -705, -705, -705, -705, -705, -705, -705, -705, -705, -705, -705, -705, -705, -705, -705, -705, -705, -705, -705, -705, -705, -705, -705, -705, -705, -705, -705, -705, -705, + -705, -705, 940, -705, -705, -705, -705, -705, -705, -705, + -705, -705, -705, -705, -705, -705, -705, -705, 941, 941, + + 941, 941, 941, 941, 941, 941, 941, 941, -705, -705, + -705, -705, -705, -705, -705, 940, 940, 940, 940, 940, + 940, 940, 940, 940, 940, 940, 940, 940, 940, 940, + 940, 940, 940, 940, 940, 940, 940, 940, 940, 940, + 940, -705, -705, -705, -705, -705, -705, -705, -705, -705, -705, -705, -705, -705, -705, -705, -705, -705, -705, -705, -705, -705, -705, -705, -705, -705, -705, -705, -705, -705, -705, -705, -705, -705, -705, -705, -705, -705 }, { - 67, -706, -706, -706, -706, -706, -706, -706, -706, -706, + 69, -706, -706, -706, -706, -706, -706, -706, -706, -706, -706, -706, -706, -706, -706, -706, -706, -706, -706, -706, -706, -706, -706, -706, -706, -706, -706, -706, -706, -706, @@ -12609,7 +12770,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -707, -707, -707, -707, -707, -707, -707, -707, -707, + 69, -707, -707, -707, -707, -707, -707, -707, -707, -707, -707, -707, -707, -707, -707, -707, -707, -707, -707, -707, -707, -707, -707, -707, -707, -707, -707, -707, -707, -707, -707, -707, -707, -707, -707, -707, -707, -707, -707, -707, @@ -12626,7 +12787,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -708, -708, -708, -708, -708, -708, -708, -708, -708, + 69, -708, -708, -708, -708, -708, -708, -708, -708, -708, -708, -708, -708, -708, -708, -708, -708, -708, -708, -708, -708, -708, -708, -708, -708, -708, -708, -708, -708, -708, -708, -708, -708, -708, -708, -708, -708, -708, -708, -708, @@ -12643,7 +12804,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -709, -709, -709, -709, -709, -709, -709, -709, -709, + 69, -709, -709, -709, -709, -709, -709, -709, -709, -709, -709, -709, -709, -709, -709, -709, -709, -709, -709, -709, -709, -709, -709, -709, -709, -709, -709, -709, -709, -709, @@ -12661,7 +12822,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -710, -710, -710, -710, -710, -710, -710, -710, -710, + 69, -710, -710, -710, -710, -710, -710, -710, -710, -710, -710, -710, -710, -710, -710, -710, -710, -710, -710, -710, -710, -710, -710, -710, -710, -710, -710, -710, -710, -710, -710, -710, -710, -710, -710, -710, -710, -710, -710, -710, @@ -12678,7 +12839,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -711, -711, -711, -711, -711, -711, -711, -711, -711, + 69, -711, -711, -711, -711, -711, -711, -711, -711, -711, -711, -711, -711, -711, -711, -711, -711, -711, -711, -711, -711, -711, -711, -711, -711, -711, -711, -711, -711, -711, -711, -711, -711, -711, -711, -711, -711, -711, -711, -711, @@ -12695,7 +12856,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -712, -712, -712, -712, -712, -712, -712, -712, -712, + 69, -712, -712, -712, -712, -712, -712, -712, -712, -712, -712, -712, -712, -712, -712, -712, -712, -712, -712, -712, -712, -712, -712, -712, -712, -712, -712, -712, -712, -712, -712, -712, -712, -712, -712, -712, -712, -712, -712, -712, @@ -12712,7 +12873,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -713, -713, -713, -713, -713, -713, -713, -713, -713, + 69, -713, -713, -713, -713, -713, -713, -713, -713, -713, -713, -713, -713, -713, -713, -713, -713, -713, -713, -713, -713, -713, -713, -713, -713, -713, -713, -713, -713, -713, @@ -12730,59 +12891,59 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -714, -714, -714, -714, -714, -714, -714, -714, -714, - -714, -714, -714, -714, -714, -714, -714, -714, -714, -714, - -714, -714, -714, -714, -714, -714, -714, -714, -714, -714, - -714, -714, -714, -714, -714, -714, -714, -714, -714, -714, - -714, -714, -714, -714, -714, -714, -714, -714, -714, -714, - -714, -714, -714, -714, -714, -714, -714, -714, -714, -714, - -714, -714, -714, -714, -714, -714, -714, -714, -714, -714, - -714, -714, -714, -714, -714, -714, -714, -714, -714, -714, + 69, 508, 508, 508, 508, 508, 508, 508, 508, 508, + 713, 508, 508, 508, 508, 508, 508, 508, 508, 508, + 508, 508, 508, 508, 508, 508, 508, 508, 508, 508, + 508, 508, 714, 508, 508, 508, 508, 508, 508, 508, + 508, 508, 508, 508, 508, 508, 508, 715, 508, 508, + 508, 508, 508, 508, 508, 508, 508, 508, 508, 508, + 508, 508, 508, 508, 508, 508, 508, 508, 508, 508, + 508, 508, 508, 508, 508, 508, 508, 508, 508, 508, - -714, -714, -714, -714, -714, -714, -714, -714, -714, -714, - -714, -714, -714, -714, -714, -714, -714, -714, -714, -714, - -714, -714, -714, -714, -714, -714, -714, -714, -714, -714, - -714, -714, -714, -714, -714, -714, -714, -714, -714, -714, - -714, -714, -714, -714, -714, -714, -714, -714 + 508, 508, 508, 508, 508, 508, 508, 508, 508, 508, + 508, 508, 508, 508, 508, 508, 508, 508, 508, 508, + 508, 508, 508, 508, 508, 508, 508, 508, 508, 508, + 508, 508, 508, 508, 508, 508, 508, 508, 508, 508, + 508, 508, 508, 508, 508, 508, 508, 508 }, { - 67, -715, -715, -715, -715, -715, -715, -715, -715, -715, - -715, -715, -715, -715, -715, -715, -715, -715, -715, -715, - -715, -715, -715, -715, -715, -715, -715, -715, -715, -715, - -715, -715, -715, -715, -715, -715, -715, -715, -715, -715, - -715, -715, -715, -715, -715, -715, -715, -715, -715, -715, + 69, 942, 942, 942, 942, 942, 942, 942, 942, 942, + 943, 942, 942, 942, 942, 942, 942, 942, 942, 942, + 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, + 942, 942, 944, 942, 942, 942, 942, 942, 942, 942, + 942, 942, 942, 942, 942, 942, 942, 945, 942, 942, - -715, -715, -715, -715, -715, -715, -715, -715, -715, -715, - -715, -715, -715, -715, -715, -715, -715, -715, -715, -715, - -715, -715, -715, -715, -715, -715, -715, -715, -715, -715, - -715, -715, -715, -715, -715, -715, -715, -715, -715, -715, - -715, -715, -715, -715, -715, -715, -715, -715, -715, -715, - -715, -715, -715, -715, -715, -715, -715, -715, -715, -715, - -715, -715, -715, -715, -715, -715, -715, -715, -715, -715, - -715, -715, -715, -715, -715, -715, -715, -715 + 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, + 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, + 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, + 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, + 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, + 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, + 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, + 942, 942, 942, 942, 942, 942, 942, 942 }, { - 67, -716, -716, -716, -716, -716, -716, -716, -716, -716, - -716, -716, -716, -716, -716, -716, -716, -716, -716, -716, + 69, 716, 716, 716, 716, 716, 716, 716, 716, 716, + 717, 716, 716, 716, 716, 716, 716, 716, 716, 716, - -716, -716, -716, -716, -716, -716, -716, -716, -716, -716, - -716, -716, -716, -716, -716, -716, -716, -716, -716, -716, - -716, -716, -716, -716, -716, -716, -716, -716, -716, -716, - -716, -716, -716, -716, -716, -716, -716, -716, -716, -716, - -716, -716, -716, -716, -716, -716, -716, -716, -716, -716, - -716, -716, -716, -716, -716, -716, -716, -716, -716, -716, - -716, -716, -716, -716, -716, -716, -716, -716, -716, -716, - -716, -716, -716, -716, -716, -716, -716, -716, -716, -716, - -716, -716, -716, -716, -716, -716, -716, -716, -716, -716, - -716, -716, -716, -716, -716, -716, -716, -716, -716, -716, + 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, + 716, 716, 718, 716, 716, 716, 716, 716, 716, 716, + 716, 716, 716, 716, 716, 716, 716, 719, 716, 716, + 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, + 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, + 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, + 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, + 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, + 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, + 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, - -716, -716, -716, -716, -716, -716, -716, -716 + 716, 716, 716, 716, 716, 716, 716, 716 }, { - 67, -717, -717, -717, -717, -717, -717, -717, -717, -717, + 69, -717, -717, -717, -717, -717, -717, -717, -717, -717, -717, -717, -717, -717, -717, -717, -717, -717, -717, -717, -717, -717, -717, -717, -717, -717, -717, -717, -717, -717, -717, -717, -717, -717, -717, -717, -717, -717, -717, -717, @@ -12799,59 +12960,59 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -718, -718, -718, -718, -718, -718, -718, -718, -718, - -718, -718, -718, -718, -718, -718, -718, -718, -718, -718, - -718, -718, -718, -718, -718, -718, -718, -718, -718, -718, - -718, -718, -718, -718, -718, -718, -718, -718, -718, -718, - -718, -718, -718, -718, -718, -718, -718, -718, -718, -718, - -718, -718, -718, -718, -718, -718, -718, -718, -718, -718, + 69, 716, 716, 716, 716, 716, 716, 716, 716, 716, + 717, 716, 716, 716, 716, 716, 716, 716, 716, 716, + 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, + 716, 716, 718, 716, 716, 716, 716, 716, 716, 716, + 716, 716, 716, 716, 716, 716, 716, 719, 716, 716, + 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, - -718, -718, -718, -718, -718, -718, -718, -718, -718, -718, - -718, -718, -718, -718, -718, -718, -718, -718, -718, -718, - -718, -718, -718, -718, -718, -718, -718, -718, -718, -718, - -718, -718, -718, -718, -718, -718, -718, -718, -718, -718, - -718, -718, -718, -718, -718, -718, -718, -718, -718, -718, - -718, -718, -718, -718, -718, -718, -718, -718, -718, -718, - -718, -718, -718, -718, -718, -718, -718, -718 + 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, + 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, + 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, + 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, + 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, + 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, + 716, 716, 716, 716, 716, 716, 716, 716 }, { - 67, -719, -719, -719, -719, -719, -719, -719, -719, -719, - -719, -719, -719, -719, -719, -719, -719, -719, -719, -719, - -719, -719, -719, -719, -719, -719, -719, -719, -719, -719, + 69, 716, 716, 716, 716, 716, 716, 716, 716, 716, + 717, 716, 716, 716, 716, 716, 716, 716, 716, 716, + 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, - -719, -719, -719, -719, -719, -719, -719, -719, -719, -719, - -719, -719, -719, -719, -719, -719, -719, -719, -719, -719, - -719, -719, -719, -719, -719, -719, -719, -719, -719, -719, - -719, -719, -719, -719, -719, -719, -719, -719, -719, -719, - -719, -719, -719, -719, -719, -719, -719, -719, -719, -719, - -719, -719, -719, -719, -719, -719, -719, -719, -719, -719, - -719, -719, -719, -719, -719, -719, -719, -719, -719, -719, - -719, -719, -719, -719, -719, -719, -719, -719, -719, -719, - -719, -719, -719, -719, -719, -719, -719, -719, -719, -719, - -719, -719, -719, -719, -719, -719, -719, -719 + 716, 716, 718, 716, 716, 716, 716, 716, 716, 716, + 716, 716, 716, 716, 716, 716, 716, 719, 716, 716, + 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, + 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, + 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, + 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, + 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, + 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, + 716, 716, 716, 716, 716, 716, 716, 716, 716, 716, + 716, 716, 716, 716, 716, 716, 716, 716 }, { - 67, -720, -720, -720, -720, -720, -720, -720, -720, -720, - -720, -720, -720, -720, -720, -720, -720, -720, -720, -720, - -720, -720, -720, -720, -720, -720, -720, -720, -720, -720, - -720, -720, -720, -720, -720, -720, -720, -720, -720, -720, - -720, -720, -720, -720, -720, -720, -720, -720, -720, -720, - -720, -720, -720, -720, -720, -720, -720, -720, -720, -720, - -720, -720, -720, -720, -720, -720, -720, -720, -720, -720, - -720, -720, -720, -720, -720, -720, -720, -720, -720, -720, - -720, -720, -720, -720, -720, -720, -720, -720, -720, -720, - -720, -720, -720, -720, -720, -720, -720, -720, -720, -720, + 69, 720, 720, 720, 720, 720, 720, 720, 720, 720, + 721, 720, 720, 720, 720, 720, 720, 720, 720, 720, + 720, 720, 720, 720, 720, 720, 720, 720, 720, 720, + 720, 720, 722, 720, 720, 720, 720, 720, 720, 720, + 720, 720, 720, 720, 720, 720, 720, 723, 720, 720, + 720, 720, 720, 720, 720, 720, 720, 720, 720, 720, + 720, 720, 720, 720, 720, 720, 720, 720, 720, 720, + 720, 720, 720, 720, 720, 720, 720, 720, 720, 720, + 720, 720, 720, 720, 720, 720, 720, 720, 720, 720, + 720, 720, 720, 720, 720, 720, 720, 720, 720, 720, - -720, -720, -720, -720, -720, -720, -720, -720, -720, -720, - -720, -720, -720, -720, -720, -720, -720, -720, -720, -720, - -720, -720, -720, -720, -720, -720, -720, -720 + 720, 720, 720, 720, 720, 720, 720, 720, 720, 720, + 720, 720, 720, 720, 720, 720, 720, 720, 720, 720, + 720, 720, 720, 720, 720, 720, 720, 720 }, { - 67, -721, -721, -721, -721, -721, -721, -721, -721, -721, + 69, -721, -721, -721, -721, -721, -721, -721, -721, -721, -721, -721, -721, -721, -721, -721, -721, -721, -721, -721, -721, -721, -721, -721, -721, -721, -721, -721, -721, -721, -721, -721, -721, -721, -721, -721, -721, -721, -721, -721, @@ -12868,42 +13029,42 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -722, -722, -722, -722, -722, -722, -722, -722, -722, - -722, -722, -722, -722, -722, -722, -722, -722, -722, -722, - -722, -722, -722, -722, -722, -722, -722, -722, -722, -722, - -722, -722, -722, -722, -722, -722, -722, -722, -722, -722, + 69, 720, 720, 720, 720, 720, 720, 720, 720, 720, + 721, 720, 720, 720, 720, 720, 720, 720, 720, 720, + 720, 720, 720, 720, 720, 720, 720, 720, 720, 720, + 720, 720, 722, 720, 720, 720, 720, 720, 720, 720, - -722, -722, -722, -722, -722, -722, -722, -722, -722, -722, - -722, -722, -722, -722, -722, -722, -722, -722, -722, -722, - -722, -722, -722, -722, -722, -722, -722, -722, -722, -722, - -722, -722, -722, -722, -722, -722, -722, -722, -722, -722, - -722, -722, -722, -722, -722, -722, -722, -722, -722, -722, - -722, -722, -722, -722, -722, -722, -722, -722, -722, -722, - -722, -722, -722, -722, -722, -722, -722, -722, -722, -722, - -722, -722, -722, -722, -722, -722, -722, -722, -722, -722, - -722, -722, -722, -722, -722, -722, -722, -722 + 720, 720, 720, 720, 720, 720, 720, 723, 720, 720, + 720, 720, 720, 720, 720, 720, 720, 720, 720, 720, + 720, 720, 720, 720, 720, 720, 720, 720, 720, 720, + 720, 720, 720, 720, 720, 720, 720, 720, 720, 720, + 720, 720, 720, 720, 720, 720, 720, 720, 720, 720, + 720, 720, 720, 720, 720, 720, 720, 720, 720, 720, + 720, 720, 720, 720, 720, 720, 720, 720, 720, 720, + 720, 720, 720, 720, 720, 720, 720, 720, 720, 720, + 720, 720, 720, 720, 720, 720, 720, 720 }, { - 67, -723, -723, -723, -723, -723, -723, -723, -723, -723, + 69, 720, 720, 720, 720, 720, 720, 720, 720, 720, - -723, -723, -723, -723, -723, -723, -723, -723, -723, -723, - -723, -723, -723, -723, -723, -723, -723, -723, -723, -723, - -723, -723, -723, -723, -723, -723, -723, -723, -723, -723, - -723, -723, -723, -723, -723, -723, -723, -723, -723, -723, - -723, -723, -723, -723, -723, -723, -723, -723, -723, -723, - -723, -723, -723, -723, -723, -723, -723, -723, -723, -723, - -723, 764, -723, -723, -723, -723, -723, -723, -723, -723, - -723, -723, -723, -723, -723, -723, -723, -723, -723, -723, - -723, -723, -723, -723, -723, -723, -723, -723, -723, -723, - -723, -723, -723, -723, -723, -723, -723, -723, -723, -723, + 721, 720, 720, 720, 720, 720, 720, 720, 720, 720, + 720, 720, 720, 720, 720, 720, 720, 720, 720, 720, + 720, 720, 722, 720, 720, 720, 720, 720, 720, 720, + 720, 720, 720, 720, 720, 720, 720, 723, 720, 720, + 720, 720, 720, 720, 720, 720, 720, 720, 720, 720, + 720, 720, 720, 720, 720, 720, 720, 720, 720, 720, + 720, 720, 720, 720, 720, 720, 720, 720, 720, 720, + 720, 720, 720, 720, 720, 720, 720, 720, 720, 720, + 720, 720, 720, 720, 720, 720, 720, 720, 720, 720, + 720, 720, 720, 720, 720, 720, 720, 720, 720, 720, - -723, -723, -723, -723, -723, -723, -723, -723, -723, -723, - -723, -723, -723, -723, -723, -723, -723, -723 + 720, 720, 720, 720, 720, 720, 720, 720, 720, 720, + 720, 720, 720, 720, 720, 720, 720, 720 }, { - 67, -724, -724, -724, -724, -724, -724, -724, -724, -724, + 69, -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, @@ -12912,7 +13073,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, - -724, -724, -724, 765, -724, -724, -724, -724, -724, -724, + -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, @@ -12920,10 +13081,10 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -725, -725, -725, -725, -725, -725, -725, -725, -725, + 69, -725, -725, -725, -725, -725, -725, -725, -725, -725, + -725, -725, -725, -725, -725, -725, -725, -725, -725, -725, -725, -725, -725, -725, -725, -725, -725, -725, -725, -725, -725, -725, -725, -725, -725, -725, -725, -725, -725, -725, - -725, -725, 766, -725, -725, -725, -725, -725, -725, -725, -725, -725, -725, -725, -725, -725, -725, -725, -725, -725, -725, -725, -725, -725, -725, -725, -725, -725, -725, -725, @@ -12937,11 +13098,11 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -726, -726, -726, -726, -726, -726, -726, -726, -726, + 69, -726, -726, -726, -726, -726, -726, -726, -726, -726, -726, -726, -726, -726, -726, -726, -726, -726, -726, -726, -726, -726, -726, -726, -726, -726, -726, -726, -726, -726, - -726, -726, 767, -726, -726, -726, -726, -726, -726, -726, + -726, -726, -726, -726, -726, -726, -726, -726, -726, -726, -726, -726, -726, -726, -726, -726, -726, -726, -726, -726, -726, -726, -726, -726, -726, -726, -726, -726, -726, -726, -726, -726, -726, -726, -726, -726, -726, -726, -726, -726, @@ -12955,10 +13116,10 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -727, -727, -727, -727, -727, -727, -727, -727, -727, + 69, -727, -727, -727, -727, -727, -727, -727, -727, -727, + -727, -727, -727, -727, -727, -727, -727, -727, -727, -727, -727, -727, -727, -727, -727, -727, -727, -727, -727, -727, -727, -727, -727, -727, -727, -727, -727, -727, -727, -727, - -727, -727, 768, -727, -727, -727, -727, -727, -727, -727, -727, -727, -727, -727, -727, -727, -727, -727, -727, -727, -727, -727, -727, -727, -727, -727, -727, -727, -727, -727, -727, -727, -727, -727, -727, -727, -727, -727, -727, -727, @@ -12972,10 +13133,10 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -728, -728, -728, -728, -728, -728, -728, -728, -728, + 69, -728, -728, -728, -728, -728, -728, -728, -728, -728, + -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, - -728, -728, 769, -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, @@ -12989,11 +13150,11 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -729, -729, -729, -729, -729, -729, -729, -729, -729, + 69, -729, -729, -729, -729, -729, -729, -729, -729, -729, -729, -729, -729, -729, -729, -729, -729, -729, -729, -729, -729, -729, -729, -729, -729, -729, -729, -729, -729, -729, - -729, -729, 770, -729, -729, -729, -729, -729, -729, -729, + -729, -729, -729, -729, -729, -729, -729, -729, -729, -729, -729, -729, -729, -729, -729, -729, -729, -729, -729, -729, -729, -729, -729, -729, -729, -729, -729, -729, -729, -729, -729, -729, -729, -729, -729, -729, -729, -729, -729, -729, @@ -13007,7 +13168,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -730, -730, -730, -730, -730, -730, -730, -730, -730, + 69, -730, -730, -730, -730, -730, -730, -730, -730, -730, -730, -730, -730, -730, -730, -730, -730, -730, -730, -730, -730, -730, -730, -730, -730, -730, -730, -730, -730, -730, -730, -730, -730, -730, -730, -730, -730, -730, -730, -730, @@ -13024,7 +13185,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -731, -731, -731, -731, -731, -731, -731, -731, -731, + 69, -731, -731, -731, -731, -731, -731, -731, -731, -731, -731, -731, -731, -731, -731, -731, -731, -731, -731, -731, -731, -731, -731, -731, -731, -731, -731, -731, -731, -731, -731, -731, -731, -731, -731, -731, -731, -731, -731, -731, @@ -13041,7 +13202,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -732, -732, -732, -732, -732, -732, -732, -732, -732, + 69, -732, -732, -732, -732, -732, -732, -732, -732, -732, -732, -732, -732, -732, -732, -732, -732, -732, -732, -732, -732, -732, -732, -732, -732, -732, -732, -732, -732, -732, -732, -732, -732, -732, -732, -732, -732, -732, -732, -732, @@ -13058,11 +13219,11 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -733, -733, -733, -733, -733, -733, -733, -733, -733, + 69, -733, -733, -733, -733, -733, -733, -733, -733, -733, -733, -733, -733, -733, -733, -733, -733, -733, -733, -733, -733, -733, -733, -733, -733, -733, -733, -733, -733, -733, - -733, -733, 771, -733, -733, -733, -733, -733, -733, -733, + -733, -733, -733, -733, -733, -733, -733, -733, -733, -733, -733, -733, -733, -733, -733, -733, -733, -733, -733, -733, -733, -733, -733, -733, -733, -733, -733, -733, -733, -733, -733, -733, -733, -733, -733, -733, -733, -733, -733, -733, @@ -13076,10 +13237,10 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -734, -734, -734, -734, -734, -734, -734, -734, -734, + 69, -734, -734, -734, -734, -734, -734, -734, -734, -734, + -734, -734, -734, -734, -734, -734, -734, -734, -734, -734, -734, -734, -734, -734, -734, -734, -734, -734, -734, -734, -734, -734, -734, -734, -734, -734, -734, -734, -734, -734, - -734, -734, 772, -734, -734, -734, -734, -734, -734, -734, -734, -734, -734, -734, -734, -734, -734, -734, -734, -734, -734, -734, -734, -734, -734, -734, -734, -734, -734, -734, -734, -734, -734, -734, -734, -734, -734, -734, -734, -734, @@ -13093,7 +13254,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -735, -735, -735, -735, -735, -735, -735, -735, -735, + 69, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, @@ -13101,16 +13262,16 @@ static yyconst flex_int16_t yy_nxt[][128] = -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, + -735, -735, -735, 946, -735, -735, -735, -735, -735, -735, + -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, - -735, -735, -735, -735, -735, -735, -735, -735, 773, 774, - 775, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, -735 }, { - 67, -736, -736, -736, -736, -736, -736, -736, -736, -736, + 69, -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, @@ -13118,8 +13279,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, + -736, -736, -736, -736, -736, -736, -736, -736, -736, 947, -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, - -736, -736, -736, 776, -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, @@ -13128,14 +13289,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -737, -737, -737, -737, -737, -737, -737, -737, -737, - -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, + 69, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, + -737, -737, 948, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, @@ -13145,7 +13306,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -738, -738, -738, -738, -738, -738, -738, -738, -738, + 69, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, @@ -13153,8 +13314,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, + -738, -738, -738, -738, -738, -738, -738, -738, -738, 949, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, - -738, 777, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, -738, @@ -13162,7 +13323,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -739, -739, -739, -739, -739, -739, -739, -739, -739, + 69, -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, @@ -13170,7 +13331,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, - -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, + -739, -739, -739, -739, -739, -739, -739, -739, -739, 950, -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, @@ -13180,14 +13341,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -740, -740, -740, -740, -740, -740, -740, -740, -740, - -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, + 69, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, + -740, -740, -740, 951, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, -740, @@ -13197,7 +13358,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -741, -741, -741, -741, -741, -741, -741, -741, -741, + 69, -741, -741, -741, -741, -741, -741, -741, -741, -741, -741, -741, -741, -741, -741, -741, -741, -741, -741, -741, -741, -741, -741, -741, -741, -741, -741, -741, -741, -741, -741, -741, -741, -741, -741, -741, -741, -741, -741, -741, @@ -13214,7 +13375,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -742, -742, -742, -742, -742, -742, -742, -742, -742, + 69, -742, -742, -742, -742, -742, -742, -742, -742, -742, -742, -742, -742, -742, -742, -742, -742, -742, -742, -742, -742, -742, -742, -742, -742, -742, -742, -742, -742, -742, -742, -742, -742, -742, -742, -742, -742, -742, -742, -742, @@ -13231,7 +13392,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -743, -743, -743, -743, -743, -743, -743, -743, -743, + 69, -743, -743, -743, -743, -743, -743, -743, -743, -743, -743, -743, -743, -743, -743, -743, -743, -743, -743, -743, -743, -743, -743, -743, -743, -743, -743, -743, -743, -743, @@ -13249,10 +13410,10 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -744, -744, -744, -744, -744, -744, -744, -744, -744, + 69, -744, -744, -744, -744, -744, -744, -744, -744, -744, + -744, -744, -744, -744, -744, -744, -744, -744, -744, -744, -744, -744, -744, -744, -744, -744, -744, -744, -744, -744, -744, -744, -744, -744, -744, -744, -744, -744, -744, -744, - -744, -744, 778, -744, -744, -744, -744, -744, -744, -744, -744, -744, -744, -744, -744, -744, -744, -744, -744, -744, -744, -744, -744, -744, -744, -744, -744, -744, -744, -744, -744, -744, -744, -744, -744, -744, -744, -744, -744, -744, @@ -13266,7 +13427,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -745, -745, -745, -745, -745, -745, -745, -745, -745, + 69, -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, @@ -13274,7 +13435,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, - -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, + -745, -745, -745, -745, -745, -745, -745, -745, -745, 952, -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, @@ -13283,7 +13444,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -746, -746, -746, -746, -746, -746, -746, -746, -746, + 69, -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, @@ -13291,7 +13452,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, - -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, + -746, -746, -746, -746, -746, -746, -746, -746, -746, 953, -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, @@ -13301,10 +13462,10 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -747, -747, -747, -747, -747, -747, -747, -747, -747, + 69, -747, -747, -747, -747, -747, -747, -747, -747, -747, + -747, -747, -747, -747, -747, -747, -747, -747, -747, -747, -747, -747, -747, -747, -747, -747, -747, -747, -747, -747, -747, -747, -747, -747, -747, -747, -747, -747, -747, -747, - -747, -747, 779, -747, -747, -747, -747, -747, -747, -747, -747, -747, -747, -747, -747, -747, -747, -747, -747, -747, -747, -747, -747, -747, -747, -747, -747, -747, -747, -747, -747, -747, -747, -747, -747, -747, -747, -747, -747, -747, @@ -13318,10 +13479,10 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -748, -748, -748, -748, -748, -748, -748, -748, -748, + 69, -748, -748, -748, -748, -748, -748, -748, -748, -748, + -748, -748, -748, -748, -748, -748, -748, -748, -748, -748, -748, -748, -748, -748, -748, -748, -748, -748, -748, -748, -748, -748, -748, -748, -748, -748, -748, -748, -748, -748, - -748, -748, 780, -748, -748, -748, -748, -748, -748, -748, -748, -748, -748, -748, -748, -748, -748, -748, -748, -748, -748, -748, -748, -748, -748, -748, -748, -748, -748, -748, @@ -13335,11 +13496,11 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -749, -749, -749, -749, -749, -749, -749, -749, -749, + 69, -749, -749, -749, -749, -749, -749, -749, -749, -749, -749, -749, -749, -749, -749, -749, -749, -749, -749, -749, -749, -749, -749, -749, -749, -749, -749, -749, -749, -749, - -749, -749, 781, -749, -749, -749, -749, -749, -749, -749, + -749, -749, -749, -749, -749, -749, -749, -749, -749, -749, -749, -749, -749, -749, -749, -749, -749, -749, -749, -749, -749, -749, -749, -749, -749, -749, -749, -749, -749, -749, -749, -749, -749, -749, -749, -749, -749, -749, -749, -749, @@ -13353,16 +13514,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -750, -750, -750, -750, -750, -750, -750, -750, -750, + 69, -750, -750, -750, -750, -750, -750, -750, -750, -750, + -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, + -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, + -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, + -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, + -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, - -750, -750, 782, -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, - -750, -750, -750, -750, -750, 782, 782, 782, 782, 782, - 782, 782, 782, 782, 782, 782, 782, 782, 782, 782, - 782, 782, 782, 782, 782, 782, 782, 782, 782, 782, - 782, -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, @@ -13370,27 +13531,27 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -751, -751, -751, -751, -751, -751, -751, -751, -751, + 69, -751, -751, -751, -751, -751, -751, -751, -751, -751, + -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, + -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, - -751, -751, 783, -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, - -751, -751, -751, -751, -751, 783, 783, 783, 783, 783, - 783, 783, 783, 783, 783, 783, 783, 783, 783, 783, - 783, 783, 783, 783, 783, 783, 783, 783, 783, 783, - 783, -751, -751, -751, -751, -751, -751, -751, -751, -751, + -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, + -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, + -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, -751 }, { - 67, -752, -752, -752, -752, -752, -752, -752, -752, -752, + 69, -752, -752, -752, -752, -752, -752, -752, -752, -752, + -752, -752, -752, -752, -752, -752, -752, -752, -752, -752, -752, -752, -752, -752, -752, -752, -752, -752, -752, -752, -752, -752, -752, -752, -752, -752, -752, -752, -752, -752, - -752, -752, 784, -752, -752, -752, -752, -752, -752, -752, -752, -752, -752, -752, -752, -752, -752, -752, -752, -752, -752, -752, -752, -752, -752, -752, -752, -752, -752, -752, @@ -13404,11 +13565,11 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -753, -753, -753, -753, -753, -753, -753, -753, -753, + 69, -753, -753, -753, -753, -753, -753, -753, -753, -753, -753, -753, -753, -753, -753, -753, -753, -753, -753, -753, -753, -753, -753, -753, -753, -753, -753, -753, -753, -753, - -753, -753, 785, -753, -753, -753, -753, -753, -753, -753, + -753, -753, -753, -753, -753, -753, -753, -753, -753, -753, -753, -753, -753, -753, -753, -753, -753, -753, -753, -753, -753, -753, -753, -753, -753, -753, -753, -753, -753, -753, -753, -753, -753, -753, -753, -753, -753, -753, -753, -753, @@ -13422,48 +13583,48 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -754, -754, -754, -754, -754, -754, -754, -754, -754, + 69, -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, - -754, -754, 786, -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, - -754, -754, -754, -754, -754, 786, 786, 786, 786, 786, - 786, 786, 786, 786, 786, 786, 786, 786, 786, 786, + -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, + -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, + -754, -754, -754, 954, -754, -754, -754, -754, -754, -754, - 786, 786, 786, 786, 786, 786, 786, 786, 786, 786, - 786, -754, -754, -754, -754, -754, -754, -754, -754, -754, + -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, + -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, -754 }, { - 67, -755, -755, -755, -755, -755, -755, -755, -755, -755, + 69, -755, -755, -755, -755, -755, -755, -755, -755, -755, + -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, - -755, -755, 787, -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, - -755, -755, -755, -755, -755, 787, 787, 787, 787, 787, - 787, 787, 787, 787, 787, 787, 787, 787, 787, 787, - 787, 787, 787, 787, 787, 787, 787, 787, 787, 787, - 787, -755, -755, -755, -755, -755, -755, -755, -755, -755, + -755, -755, -755, -755, -755, 955, 956, -755, -755, 957, + -755, -755, -755, -755, -755, -755, -755, -755, -755, 958, + -755, -755, 959, -755, -755, -755, -755, -755, -755, -755, + -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, -755 }, { - 67, -756, -756, -756, -756, -756, -756, -756, -756, -756, + 69, -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, - -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, + -756, -756, -756, -756, -756, -756, -756, -756, -756, 960, -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, @@ -13474,12 +13635,12 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -757, -757, -757, -757, -757, -757, -757, -757, -757, - -757, -757, -757, -757, -757, -757, -757, -757, -757, -757, - -757, -757, -757, -757, -757, -757, -757, -757, -757, -757, - -757, -757, 788, -757, -757, -757, -757, -757, -757, -757, + 69, -757, -757, -757, -757, -757, -757, -757, -757, -757, -757, -757, -757, -757, -757, -757, -757, -757, -757, -757, -757, -757, -757, -757, -757, -757, -757, -757, -757, -757, + -757, -757, 961, -757, -757, -757, -757, -757, -757, -757, + -757, -757, -757, -757, -757, -757, -757, -757, 962, 962, + 962, 962, 962, 962, 962, 962, 962, 962, -757, -757, -757, -757, -757, -757, -757, -757, -757, -757, -757, -757, -757, -757, -757, -757, -757, -757, -757, -757, -757, -757, -757, -757, -757, -757, -757, -757, -757, -757, -757, -757, @@ -13491,15 +13652,15 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -758, -758, -758, -758, -758, -758, -758, -758, -758, + 69, -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, - -758, -758, 789, -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, - -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, + -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, + -758, -758, -758, -758, -758, -758, -758, -758, -758, 963, -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, @@ -13508,17 +13669,17 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -759, -759, -759, -759, -759, -759, -759, -759, -759, + 69, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, - -759, -759, 790, -759, -759, -759, -759, -759, -759, -759, + -759, -759, 964, -759, -759, -759, -759, -759, -759, -759, + -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, + -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, + -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, + -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, - -759, -759, -759, -759, -759, 790, 790, 790, 790, 790, - 790, 790, 790, 790, 790, 790, 790, 790, 790, 790, - 790, 790, 790, 790, 790, 790, 790, 790, 790, 790, - 790, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, -759 @@ -13526,16 +13687,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -760, -760, -760, -760, -760, -760, -760, -760, -760, - -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, - -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, - -760, -760, 791, -760, -760, -760, -760, -760, -760, -760, - -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, - -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, + 69, -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, + -760, -760, 965, -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, + -760, -760, -760, -760, -760, 965, 965, 965, 965, 965, + 965, 965, 965, 965, 965, 965, 965, 965, 965, 965, + 965, 965, 965, 965, 965, 965, 965, 965, 965, 965, + 965, -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, @@ -13543,15 +13704,15 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -761, -761, -761, -761, -761, -761, -761, -761, -761, + 69, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, - -761, -761, 792, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, - -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, + + -761, -761, -761, -761, -761, -761, -761, -761, -761, 966, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, @@ -13560,24 +13721,24 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -762, -762, -762, -762, -762, -762, -762, -762, -762, + 69, -762, -762, -762, -762, -762, -762, -762, -762, -762, + -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, - -762, -762, 793, -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, - -762, -762, -762, -762, -762, 793, 793, 793, 793, 793, - 793, 793, 793, 793, 793, 793, 793, 793, 793, 793, - 793, 793, 793, 793, 793, 793, 793, 793, 793, 793, - 793, -762, -762, -762, -762, -762, -762, -762, -762, -762, + -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, + -762, -762, -762, -762, -762, -762, -762, -762, -762, 967, + -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, + -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, -762 }, { - 67, -763, -763, -763, -763, -763, -763, -763, -763, -763, + 69, -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, @@ -13585,7 +13746,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, - -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, + -763, -763, -763, -763, -763, -763, -763, -763, -763, 968, -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, @@ -13595,14 +13756,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -764, -764, -764, -764, -764, -764, -764, -764, -764, - -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, + 69, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, + 969, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, @@ -13612,15 +13773,15 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -765, -765, -765, -765, -765, -765, -765, -765, -765, - -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, + 69, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, + -765, -765, 970, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, - -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, + 971, -765, -765, 972, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, @@ -13629,7 +13790,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -766, -766, -766, -766, -766, -766, -766, -766, -766, + 69, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, @@ -13637,7 +13798,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, - -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, + -766, -766, 973, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, @@ -13647,14 +13808,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -767, -767, -767, -767, -767, -767, -767, -767, -767, - -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, + 69, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, + -767, -767, -767, -767, -767, -767, 974, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, @@ -13664,7 +13825,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -768, -768, -768, -768, -768, -768, -768, -768, -768, + 69, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, @@ -13672,7 +13833,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, - -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, + -768, -768, -768, -768, -768, -768, 975, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, @@ -13681,16 +13842,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -769, -769, -769, -769, -769, -769, -769, -769, -769, + 69, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, - -769, -769, 794, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, + -769, -769, -769, -769, -769, -769, 976, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, @@ -13699,13 +13860,13 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -770, -770, -770, -770, -770, -770, -770, -770, -770, - -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, + 69, -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, + -770, -770, -770, -770, -770, -770, -770, -770, -770, 977, -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, @@ -13716,7 +13877,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -771, -771, -771, -771, -771, -771, -771, -771, -771, + 69, -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, @@ -13724,7 +13885,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, - -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, + -771, -771, -771, -771, -771, -771, -771, -771, 978, -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, @@ -13733,14 +13894,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -772, -772, -772, -772, -772, -772, -772, -772, -772, + 69, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, - -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, + -772, -772, -772, -772, -772, -772, 979, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, @@ -13750,14 +13911,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -773, -773, -773, -773, -773, -773, -773, -773, -773, + 69, -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, - -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, + -773, -773, -773, -773, -773, -773, -773, -773, -773, 980, -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, @@ -13768,7 +13929,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -774, -774, -774, -774, -774, -774, -774, -774, -774, + 69, -774, -774, -774, -774, -774, -774, -774, -774, -774, -774, -774, -774, -774, -774, -774, -774, -774, -774, -774, -774, -774, -774, -774, -774, -774, -774, -774, -774, -774, -774, -774, -774, -774, -774, -774, -774, -774, -774, -774, @@ -13785,7 +13946,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -775, -775, -775, -775, -775, -775, -775, -775, -775, + 69, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, @@ -13793,7 +13954,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, - -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, + 981, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, @@ -13802,13 +13963,13 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -776, -776, -776, -776, -776, -776, -776, -776, -776, + 69, -776, -776, -776, -776, -776, -776, -776, -776, -776, -776, -776, -776, -776, -776, -776, -776, -776, -776, -776, -776, -776, -776, -776, -776, -776, -776, -776, -776, -776, -776, -776, -776, -776, -776, -776, -776, -776, -776, -776, - -776, -776, -776, -776, -776, -776, -776, -776, -776, -776, - -776, -776, -776, -776, -776, -776, -776, -776, -776, -776, + -776, -776, -776, -776, -776, -776, -776, -776, -776, 982, + 982, 982, 982, 982, 982, 982, 982, 982, -776, -776, -776, -776, -776, -776, -776, -776, -776, -776, -776, -776, -776, -776, -776, -776, -776, -776, -776, -776, -776, -776, -776, -776, -776, -776, -776, -776, -776, -776, -776, -776, @@ -13820,14 +13981,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -777, -777, -777, -777, -777, -777, -777, -777, -777, - -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, + 69, -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, + -777, -777, -777, -777, -777, -777, -777, -777, -777, 983, -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, @@ -13837,14 +13998,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -778, -778, -778, -778, -778, -778, -778, -778, -778, - -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, - -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, + 69, -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, + -778, -778, -778, -778, -778, -778, -778, -778, -778, 984, + 984, 984, 984, 984, 984, 984, 984, 984, -778, -778, - -778, 795, -778, -778, -778, -778, -778, -778, -778, -778, + -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, @@ -13854,13 +14015,13 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -779, -779, -779, -779, -779, -779, -779, -779, -779, + 69, -779, -779, -779, -779, -779, -779, -779, -779, -779, -779, -779, -779, -779, -779, -779, -779, -779, -779, -779, -779, -779, -779, -779, -779, -779, -779, -779, -779, -779, -779, -779, -779, -779, -779, -779, -779, -779, -779, -779, - -779, -779, -779, -779, -779, -779, -779, -779, -779, -779, - -779, -779, -779, -779, -779, -779, -779, -779, -779, -779, + -779, -779, -779, -779, -779, -779, -779, -779, -779, 985, + 985, 985, 985, 985, 985, 985, 985, 985, -779, -779, -779, -779, -779, -779, -779, -779, -779, -779, -779, -779, -779, -779, -779, -779, -779, -779, -779, -779, -779, -779, -779, -779, -779, -779, -779, -779, -779, -779, -779, -779, @@ -13872,7 +14033,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -780, -780, -780, -780, -780, -780, -780, -780, -780, + 69, -780, -780, -780, -780, -780, -780, -780, -780, -780, -780, -780, -780, -780, -780, -780, -780, -780, -780, -780, -780, -780, -780, -780, -780, -780, -780, -780, -780, -780, -780, -780, -780, -780, -780, -780, -780, -780, -780, -780, @@ -13889,7 +14050,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -781, -781, -781, -781, -781, -781, -781, -781, -781, + 69, -781, -781, -781, -781, -781, -781, -781, -781, -781, -781, -781, -781, -781, -781, -781, -781, -781, -781, -781, -781, -781, -781, -781, -781, -781, -781, -781, -781, -781, -781, -781, -781, -781, -781, -781, -781, -781, -781, -781, @@ -13906,7 +14067,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -782, -782, -782, -782, -782, -782, -782, -782, -782, + 69, -782, -782, -782, -782, -782, -782, -782, -782, -782, -782, -782, -782, -782, -782, -782, -782, -782, -782, -782, -782, -782, -782, -782, -782, -782, -782, -782, -782, -782, -782, -782, -782, -782, -782, -782, -782, -782, -782, -782, @@ -13923,14 +14084,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -783, -783, -783, -783, -783, -783, -783, -783, -783, + 69, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, - -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, + -783, -783, -783, -783, -783, -783, 986, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, @@ -13941,13 +14102,13 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -784, -784, -784, -784, -784, -784, -784, -784, -784, + 69, -784, -784, -784, -784, -784, -784, -784, -784, -784, -784, -784, -784, -784, -784, -784, -784, -784, -784, -784, -784, -784, -784, -784, -784, -784, -784, -784, -784, -784, -784, -784, -784, -784, -784, -784, -784, -784, -784, -784, -784, -784, -784, -784, -784, -784, -784, -784, -784, -784, -784, -784, -784, -784, -784, -784, -784, -784, -784, -784, - -784, 796, -784, -784, -784, -784, -784, -784, -784, -784, + -784, -784, -784, -784, -784, -784, -784, -784, -784, 987, -784, -784, -784, -784, -784, -784, -784, -784, -784, -784, -784, -784, -784, -784, -784, -784, -784, -784, -784, -784, @@ -13958,14 +14119,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -785, -785, -785, -785, -785, -785, -785, -785, -785, + 69, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, - -785, 797, -785, -785, -785, -785, -785, -785, -785, -785, + -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, @@ -13975,16 +14136,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -786, -786, -786, -786, -786, -786, -786, -786, -786, + 69, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, - -786, 798, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, + -786, -786, -786, 988, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, @@ -13993,15 +14154,15 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -787, -787, -787, -787, -787, -787, -787, -787, -787, + 69, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, - -787, 799, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, + -787, -787, -787, -787, -787, -787, -787, -787, -787, 989, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, @@ -14010,16 +14171,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -788, -788, -788, -788, -788, -788, -788, -788, -788, + 69, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, - -788, 800, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, + -788, -788, 990, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, @@ -14027,14 +14188,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -789, -789, -789, -789, -789, -789, -789, -789, -789, + 69, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, - -789, 801, -789, -789, -789, -789, -789, -789, -789, -789, + -789, -789, -789, -789, -789, 991, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, @@ -14045,15 +14206,15 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -790, -790, -790, -790, -790, -790, -790, -790, -790, + 69, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, - -790, 802, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, + -790, -790, 992, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, @@ -14062,16 +14223,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -791, -791, -791, -791, -791, -791, -791, -791, -791, + 69, -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, - -791, 803, -791, -791, -791, -791, -791, -791, -791, -791, - -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, + -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, + -791, -791, -791, -791, -791, -791, -791, -791, -791, 993, -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, @@ -14079,14 +14240,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -792, -792, -792, -792, -792, -792, -792, -792, -792, + 69, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, - -792, 804, -792, -792, -792, -792, -792, -792, -792, -792, + -792, -792, -792, -792, -792, -792, 994, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, @@ -14096,16 +14257,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -793, -793, -793, -793, -793, -793, -793, -793, -793, + 69, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, - -793, 805, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, + -793, -793, 995, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, @@ -14114,10 +14275,10 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -794, -794, -794, -794, -794, -794, -794, -794, -794, + 69, -794, -794, -794, -794, -794, -794, -794, -794, -794, + -794, -794, -794, -794, -794, -794, -794, -794, -794, -794, -794, -794, -794, -794, -794, -794, -794, -794, -794, -794, -794, -794, -794, -794, -794, -794, -794, -794, -794, -794, - -794, -794, 806, -794, -794, -794, -794, -794, -794, -794, -794, -794, -794, -794, -794, -794, -794, -794, -794, -794, -794, -794, -794, -794, -794, -794, -794, -794, -794, -794, -794, -794, -794, -794, -794, -794, -794, -794, -794, -794, @@ -14131,10 +14292,10 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -795, -795, -795, -795, -795, -795, -795, -795, -795, + 69, -795, -795, -795, -795, -795, -795, -795, -795, -795, + -795, -795, -795, -795, -795, -795, -795, -795, -795, -795, -795, -795, -795, -795, -795, -795, -795, -795, -795, -795, -795, -795, -795, -795, -795, -795, -795, -795, -795, -795, - -795, -795, 807, -795, -795, -795, -795, -795, -795, -795, -795, -795, -795, -795, -795, -795, -795, -795, -795, -795, -795, -795, -795, -795, -795, -795, -795, -795, -795, -795, @@ -14148,11 +14309,11 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -796, -796, -796, -796, -796, -796, -796, -796, -796, + 69, -796, -796, -796, -796, -796, -796, -796, -796, -796, -796, -796, -796, -796, -796, -796, -796, -796, -796, -796, -796, -796, -796, -796, -796, -796, -796, -796, -796, -796, - -796, -796, 808, -796, -796, -796, -796, -796, -796, -796, + -796, -796, -796, -796, -796, -796, -796, -796, -796, -796, -796, -796, -796, -796, -796, -796, -796, -796, -796, -796, -796, -796, -796, -796, -796, -796, -796, -796, -796, -796, -796, -796, -796, -796, -796, -796, -796, -796, -796, -796, @@ -14166,10 +14327,10 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -797, -797, -797, -797, -797, -797, -797, -797, -797, + 69, -797, -797, -797, -797, -797, -797, -797, -797, -797, + -797, -797, -797, -797, -797, -797, -797, -797, -797, -797, -797, -797, -797, -797, -797, -797, -797, -797, -797, -797, -797, -797, -797, -797, -797, -797, -797, -797, -797, -797, - -797, -797, 809, -797, -797, -797, -797, -797, -797, -797, -797, -797, -797, -797, -797, -797, -797, -797, -797, -797, -797, -797, -797, -797, -797, -797, -797, -797, -797, -797, -797, -797, -797, -797, -797, -797, -797, -797, -797, -797, @@ -14183,10 +14344,10 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -798, -798, -798, -798, -798, -798, -798, -798, -798, + 69, -798, -798, -798, -798, -798, -798, -798, -798, -798, + -798, -798, -798, -798, -798, -798, -798, -798, -798, -798, -798, -798, -798, -798, -798, -798, -798, -798, -798, -798, -798, -798, -798, -798, -798, -798, -798, -798, -798, -798, - -798, -798, 810, -798, -798, -798, -798, -798, -798, -798, -798, -798, -798, -798, -798, -798, -798, -798, -798, -798, -798, -798, -798, -798, -798, -798, -798, -798, -798, -798, @@ -14200,11 +14361,11 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -799, -799, -799, -799, -799, -799, -799, -799, -799, + 69, -799, -799, -799, -799, -799, -799, -799, -799, -799, -799, -799, -799, -799, -799, -799, -799, -799, -799, -799, -799, -799, -799, -799, -799, -799, -799, -799, -799, -799, - -799, -799, 811, -799, -799, -799, -799, -799, -799, -799, + -799, -799, -799, -799, -799, -799, -799, -799, -799, -799, -799, -799, -799, -799, -799, -799, -799, -799, -799, -799, -799, -799, -799, -799, -799, -799, -799, -799, -799, -799, -799, -799, -799, -799, -799, -799, -799, -799, -799, -799, @@ -14218,10 +14379,10 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -800, -800, -800, -800, -800, -800, -800, -800, -800, + 69, -800, -800, -800, -800, -800, -800, -800, -800, -800, + -800, -800, -800, -800, -800, -800, -800, -800, -800, -800, -800, -800, -800, -800, -800, -800, -800, -800, -800, -800, -800, -800, -800, -800, -800, -800, -800, -800, -800, -800, - -800, -800, 812, -800, -800, -800, -800, -800, -800, -800, -800, -800, -800, -800, -800, -800, -800, -800, -800, -800, -800, -800, -800, -800, -800, -800, -800, -800, -800, -800, -800, -800, -800, -800, -800, -800, -800, -800, -800, -800, @@ -14235,10 +14396,10 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -801, -801, -801, -801, -801, -801, -801, -801, -801, + 69, -801, -801, -801, -801, -801, -801, -801, -801, -801, + -801, -801, -801, -801, -801, -801, -801, -801, -801, -801, -801, -801, -801, -801, -801, -801, -801, -801, -801, -801, -801, -801, -801, -801, -801, -801, -801, -801, -801, -801, - -801, -801, 813, -801, -801, -801, -801, -801, -801, -801, -801, -801, -801, -801, -801, -801, -801, -801, -801, -801, -801, -801, -801, -801, -801, -801, -801, -801, -801, -801, -801, -801, -801, -801, -801, -801, -801, -801, -801, -801, @@ -14252,10 +14413,10 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -802, -802, -802, -802, -802, -802, -802, -802, -802, + 69, -802, -802, -802, -802, -802, -802, -802, -802, -802, + -802, -802, -802, -802, -802, -802, -802, -802, -802, -802, -802, -802, -802, -802, -802, -802, -802, -802, -802, -802, -802, -802, -802, -802, -802, -802, -802, -802, -802, -802, - -802, -802, 814, -802, -802, -802, -802, -802, -802, -802, -802, -802, -802, -802, -802, -802, -802, -802, -802, -802, -802, -802, -802, -802, -802, -802, -802, -802, -802, -802, @@ -14269,11 +14430,11 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -803, -803, -803, -803, -803, -803, -803, -803, -803, + 69, -803, -803, -803, -803, -803, -803, -803, -803, -803, -803, -803, -803, -803, -803, -803, -803, -803, -803, -803, -803, -803, -803, -803, -803, -803, -803, -803, -803, -803, - -803, -803, 815, -803, -803, -803, -803, -803, -803, -803, + -803, -803, -803, -803, -803, -803, -803, -803, -803, -803, -803, -803, -803, -803, -803, -803, -803, -803, -803, -803, -803, -803, -803, -803, -803, -803, -803, -803, -803, -803, -803, -803, -803, -803, -803, -803, -803, -803, -803, -803, @@ -14287,10 +14448,10 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -804, -804, -804, -804, -804, -804, -804, -804, -804, + 69, -804, -804, -804, -804, -804, -804, -804, -804, -804, + -804, -804, -804, -804, -804, -804, -804, -804, -804, -804, -804, -804, -804, -804, -804, -804, -804, -804, -804, -804, -804, -804, -804, -804, -804, -804, -804, -804, -804, -804, - -804, -804, 816, -804, -804, -804, -804, -804, -804, -804, -804, -804, -804, -804, -804, -804, -804, -804, -804, -804, -804, -804, -804, -804, -804, -804, -804, -804, -804, -804, -804, -804, -804, -804, -804, -804, -804, -804, -804, -804, @@ -14304,16 +14465,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -805, -805, -805, -805, -805, -805, -805, -805, -805, + 69, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, - -805, -805, 817, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, - -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, + -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, + -805, -805, -805, 996, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, @@ -14321,14 +14482,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -806, -806, -806, -806, -806, -806, -806, -806, -806, + 69, -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, - -806, -806, 818, -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, + -806, -806, -806, -806, -806, -806, -806, -806, 997, -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, @@ -14339,13 +14500,13 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -807, -807, -807, -807, -807, -807, -807, -807, -807, + 69, -807, -807, -807, -807, -807, -807, -807, -807, -807, + -807, -807, -807, -807, -807, -807, -807, -807, -807, -807, + -807, -807, -807, -807, -807, -807, -807, -807, -807, -807, -807, -807, -807, -807, -807, -807, -807, -807, -807, -807, -807, -807, -807, -807, -807, -807, -807, -807, -807, -807, - -807, -807, 819, -807, -807, -807, -807, -807, -807, -807, - -807, -807, -807, 820, -807, 820, -807, -807, 821, 821, - 821, 821, 821, 821, 821, 821, 821, 821, -807, -807, -807, -807, -807, -807, -807, -807, -807, -807, -807, -807, + -807, -807, -807, -807, -807, -807, -807, -807, -807, 998, -807, -807, -807, -807, -807, -807, -807, -807, -807, -807, -807, -807, -807, -807, -807, -807, -807, -807, -807, -807, @@ -14356,15 +14517,15 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -808, -808, -808, -808, -808, -808, -808, -808, -808, + 69, -808, -808, -808, -808, -808, -808, -808, -808, -808, + -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, - -808, -808, 822, -808, -808, -808, -808, -808, -808, -808, - -808, -808, -808, 823, -808, 823, -808, -808, 824, 824, - 824, 824, 824, 824, 824, 824, 824, 824, -808, -808, - -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, + + -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, + 999, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, @@ -14373,17 +14534,17 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -809, -809, -809, -809, -809, -809, -809, -809, -809, + 69, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, - -809, -809, 825, -809, -809, -809, -809, -809, -809, -809, - -809, -809, -809, 826, -809, 826, -809, -809, 827, 827, - 827, 827, 827, 827, 827, 827, 827, 827, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, + -809, -809, -809, 1000, -809, -809, -809, -809, -809, -809, + -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, + -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, -809 @@ -14391,15 +14552,15 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -810, -810, -810, -810, -810, -810, -810, -810, -810, + 69, -810, -810, -810, -810, -810, -810, -810, -810, -810, + -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, + -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, - -810, -810, 828, -810, -810, -810, -810, -810, -810, -810, - -810, -810, -810, 829, -810, 829, -810, -810, 830, 830, - 830, 830, 830, 830, 830, 830, 830, 830, -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, + -810, -810, -810, -810, -810, -810, -810, -810, -810, 1001, -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, @@ -14408,16 +14569,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -811, -811, -811, -811, -811, -811, -811, -811, -811, + 69, -811, -811, -811, -811, -811, -811, -811, -811, -811, + -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, - -811, -811, 831, -811, -811, -811, -811, -811, -811, -811, - -811, -811, -811, 832, -811, 832, -811, -811, 833, 833, - 833, 833, 833, 833, 833, 833, 833, 833, -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, - -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, + + -811, -811, -811, -811, -811, -811, -811, -811, 1002, -811, + -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, @@ -14425,14 +14586,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -812, -812, -812, -812, -812, -812, -812, -812, -812, + 69, -812, -812, -812, -812, -812, -812, -812, -812, -812, -812, -812, -812, -812, -812, -812, -812, -812, -812, -812, -812, -812, -812, -812, -812, -812, -812, -812, -812, -812, - -812, -812, 834, -812, -812, -812, -812, -812, -812, 835, - -812, -812, -812, -812, -812, -812, -812, -812, -812, -812, + -812, -812, -812, -812, -812, -812, -812, -812, -812, -812, -812, -812, -812, -812, -812, -812, -812, -812, -812, -812, + -812, -812, -812, -812, -812, -812, -812, -812, -812, 1003, -812, -812, -812, -812, -812, -812, -812, -812, -812, -812, -812, -812, -812, -812, -812, -812, -812, -812, -812, -812, -812, -812, -812, -812, -812, -812, -812, -812, -812, -812, @@ -14442,14 +14603,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -813, -813, -813, -813, -813, -813, -813, -813, -813, + 69, -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, - -813, -813, 836, -813, -813, -813, -813, -813, -813, 837, -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, + -813, -813, -813, -813, -813, -813, -813, -813, -813, 1004, -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, @@ -14460,10 +14621,10 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -814, -814, -814, -814, -814, -814, -814, -814, -814, + 69, -814, -814, -814, -814, -814, -814, -814, -814, -814, + -814, -814, -814, -814, -814, -814, -814, -814, -814, -814, -814, -814, -814, -814, -814, -814, -814, -814, -814, -814, -814, -814, -814, -814, -814, -814, -814, -814, -814, -814, - -814, -814, 838, -814, -814, -814, -814, -814, -814, 839, -814, -814, -814, -814, -814, -814, -814, -814, -814, -814, -814, -814, -814, -814, -814, -814, -814, -814, -814, -814, -814, -814, -814, -814, -814, -814, -814, -814, -814, -814, @@ -14477,15 +14638,15 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -815, -815, -815, -815, -815, -815, -815, -815, -815, + 69, -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, - -815, -815, 840, -815, -815, -815, -815, -815, -815, 841, -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, - -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, + -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, + -815, -815, -815, 1005, -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, @@ -14494,15 +14655,15 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -816, -816, -816, -816, -816, -816, -816, -816, -816, + 69, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, - -816, -816, 842, -816, -816, -816, -816, -816, -816, 843, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, + -816, -816, -816, -816, -816, -816, -816, -816, -816, 1006, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, @@ -14512,10 +14673,10 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -817, -817, -817, -817, -817, -817, -817, -817, -817, + 69, -817, -817, -817, -817, -817, -817, -817, -817, -817, + -817, -817, -817, -817, -817, -817, -817, -817, -817, -817, -817, -817, -817, -817, -817, -817, -817, -817, -817, -817, -817, -817, -817, -817, -817, -817, -817, -817, -817, -817, - -817, -817, 844, -817, -817, -817, -817, -817, -817, 845, -817, -817, -817, -817, -817, -817, -817, -817, -817, -817, -817, -817, -817, -817, -817, -817, -817, -817, -817, -817, -817, -817, -817, -817, -817, -817, -817, -817, -817, -817, @@ -14529,16 +14690,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -818, -818, -818, -818, -818, -818, -818, -818, -818, + 69, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, - -818, -818, 846, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, - -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, + -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, + -818, -818, -818, -818, 1007, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, @@ -14546,13 +14707,13 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -819, -819, -819, -819, -819, -819, -819, -819, -819, + 69, -819, -819, -819, -819, -819, -819, -819, -819, -819, -819, -819, -819, -819, -819, -819, -819, -819, -819, -819, -819, -819, -819, -819, -819, -819, -819, -819, -819, -819, - -819, -819, 819, -819, -819, -819, -819, -819, -819, -819, - -819, -819, -819, 820, -819, 820, -819, -819, 821, 821, - 821, 821, 821, 821, 821, 821, 821, 821, -819, -819, + -819, -819, 1008, -819, -819, -819, -819, -819, -819, -819, + -819, -819, -819, -819, -819, -819, -819, -819, -819, -819, + -819, -819, -819, -819, -819, -819, -819, -819, -819, -819, -819, -819, -819, -819, -819, -819, -819, -819, -819, -819, -819, -819, -819, -819, -819, -819, -819, -819, -819, -819, -819, -819, -819, -819, -819, -819, -819, -819, -819, -819, @@ -14564,15 +14725,15 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -820, -820, -820, -820, -820, -820, -820, -820, -820, + 69, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, - -820, -820, -820, -820, -820, -820, -820, -820, 821, 821, - 821, 821, 821, 821, 821, 821, 821, 821, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, + -820, 1009, -820, -820, -820, -820, -820, -820, -820, -820, + -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, @@ -14581,16 +14742,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -821, -821, -821, -821, -821, -821, -821, -821, -821, + 69, -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, - -821, -821, -821, -821, -821, -821, -821, -821, 821, 821, - 821, 821, 821, 821, 821, 821, 821, 821, -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, - -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, + + -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, + -821, -821, -821, -821, -821, -821, -821, -821, -821, 1010, -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, @@ -14598,16 +14759,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -822, -822, -822, -822, -822, -822, -822, -822, -822, + 69, -822, -822, -822, -822, -822, -822, -822, -822, -822, + -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, - -822, -822, 822, -822, -822, -822, -822, -822, -822, -822, - -822, -822, -822, 823, -822, 823, -822, -822, 824, 824, - 824, 824, 824, 824, 824, 824, 824, 824, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, + 1011, -822, -822, -822, -822, -822, -822, -822, -822, -822, + -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, @@ -14615,17 +14776,17 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -823, -823, -823, -823, -823, -823, -823, -823, -823, + 69, -823, -823, -823, -823, -823, -823, -823, -823, -823, -823, -823, -823, -823, -823, -823, -823, -823, -823, -823, -823, -823, -823, -823, -823, -823, -823, -823, -823, -823, - -823, -823, -823, -823, -823, -823, -823, -823, -823, -823, - -823, -823, -823, -823, -823, -823, -823, -823, 824, 824, - 824, 824, 824, 824, 824, 824, 824, 824, -823, -823, - -823, -823, -823, -823, -823, -823, -823, -823, -823, -823, - -823, -823, -823, -823, -823, -823, -823, -823, -823, -823, - -823, -823, -823, -823, -823, -823, -823, -823, -823, -823, - -823, -823, -823, -823, -823, -823, -823, -823, -823, -823, + -823, -823, 1012, -823, -823, -823, -823, -823, -823, -823, + -823, -823, -823, -823, -823, -823, -823, -823, 1013, 1013, + 1013, 1013, 1013, 1013, 1013, 1013, 1013, 1013, -823, -823, + -823, -823, -823, -823, -823, 1012, 1012, 1012, 1012, 1012, + 1012, 1012, 1012, 1012, 1012, 1012, 1012, 1012, 1012, 1012, + 1012, 1012, 1012, 1012, 1012, 1012, 1012, 1012, 1012, 1012, + 1012, -823, -823, -823, -823, -823, -823, -823, -823, -823, -823, -823, -823, -823, -823, -823, -823, -823, -823, -823, -823, -823, -823, -823, -823, -823, -823, -823, -823, -823, @@ -14633,13 +14794,13 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -824, -824, -824, -824, -824, -824, -824, -824, -824, + 69, -824, -824, -824, -824, -824, -824, -824, -824, -824, + -824, -824, -824, -824, -824, -824, -824, -824, -824, -824, -824, -824, -824, -824, -824, -824, -824, -824, -824, -824, -824, -824, -824, -824, -824, -824, -824, -824, -824, -824, -824, -824, -824, -824, -824, -824, -824, -824, -824, -824, - -824, -824, -824, -824, -824, -824, -824, -824, 824, 824, - 824, 824, 824, 824, 824, 824, 824, 824, -824, -824, -824, -824, -824, -824, -824, -824, -824, -824, -824, -824, + -824, -824, -824, -824, -824, -824, -824, 1014, -824, -824, -824, -824, -824, -824, -824, -824, -824, -824, -824, -824, -824, -824, -824, -824, -824, -824, -824, -824, -824, -824, @@ -14650,32 +14811,32 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -825, -825, -825, -825, -825, -825, -825, -825, -825, - -825, -825, -825, -825, -825, -825, -825, -825, -825, -825, - -825, -825, -825, -825, -825, -825, -825, -825, -825, -825, - -825, -825, 825, -825, -825, -825, -825, -825, -825, -825, - -825, -825, -825, 826, -825, 826, -825, -825, 827, 827, - - 827, 827, 827, 827, 827, 827, 827, 827, -825, -825, - -825, -825, -825, -825, -825, -825, -825, -825, -825, -825, - -825, -825, -825, -825, -825, -825, -825, -825, -825, -825, + 69, -825, -825, -825, -825, -825, -825, -825, -825, -825, -825, -825, -825, -825, -825, -825, -825, -825, -825, -825, -825, -825, -825, -825, -825, -825, -825, -825, -825, -825, + -825, -825, 1015, -825, -825, -825, -825, -825, -825, -825, + -825, -825, -825, -825, -825, -825, -825, -825, 1016, 1016, + + 1016, 1016, 1016, 1016, 1016, 1016, 1016, 1016, -825, -825, + -825, -825, -825, -825, -825, 1015, 1015, 1015, 1015, 1015, + 1015, 1015, 1015, 1015, 1015, 1015, 1015, 1015, 1015, 1015, + 1015, 1015, 1015, 1015, 1015, 1015, 1015, 1015, 1015, 1015, + 1015, -825, -825, -825, -825, -825, -825, -825, -825, -825, -825, -825, -825, -825, -825, -825, -825, -825, -825, -825, -825, -825, -825, -825, -825, -825, -825, -825, -825, -825, -825, -825, -825, -825, -825, -825, -825, -825 }, { - 67, -826, -826, -826, -826, -826, -826, -826, -826, -826, + 69, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, - -826, -826, -826, -826, -826, -826, -826, -826, 827, 827, - 827, 827, 827, 827, 827, 827, 827, 827, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, + -826, -826, -826, -826, -826, -826, -826, -826, -826, 1017, + -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, @@ -14685,15 +14846,15 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -827, -827, -827, -827, -827, -827, -827, -827, -827, + 69, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, - -827, -827, -827, -827, -827, -827, -827, -827, 827, 827, - 827, 827, 827, 827, 827, 827, 827, 827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, + -827, -827, -827, -827, -827, -827, -827, 1018, -827, -827, + -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, @@ -14702,34 +14863,34 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -828, -828, -828, -828, -828, -828, -828, -828, -828, - -828, -828, -828, -828, -828, -828, -828, -828, -828, -828, - -828, -828, -828, -828, -828, -828, -828, -828, -828, -828, - -828, -828, 828, -828, -828, -828, -828, -828, -828, -828, - -828, -828, -828, 829, -828, 829, -828, -828, 830, 830, - 830, 830, 830, 830, 830, 830, 830, 830, -828, -828, - - -828, -828, -828, -828, -828, -828, -828, -828, -828, -828, - -828, -828, -828, -828, -828, -828, -828, -828, -828, -828, + 69, -828, -828, -828, -828, -828, -828, -828, -828, -828, -828, -828, -828, -828, -828, -828, -828, -828, -828, -828, -828, -828, -828, -828, -828, -828, -828, -828, -828, -828, + -828, -828, 1019, -828, -828, -828, -828, -828, -828, -828, + -828, -828, -828, -828, -828, -828, -828, -828, 1020, 1020, + 1020, 1020, 1020, 1020, 1020, 1020, 1020, 1020, -828, -828, + + -828, -828, -828, -828, -828, 1019, 1019, 1019, 1019, 1019, + 1019, 1019, 1019, 1019, 1019, 1019, 1019, 1019, 1019, 1019, + 1019, 1019, 1019, 1019, 1019, 1019, 1019, 1019, 1019, 1019, + 1019, -828, -828, -828, -828, -828, -828, -828, -828, -828, -828, -828, -828, -828, -828, -828, -828, -828, -828, -828, -828, -828, -828, -828, -828, -828, -828, -828, -828, -828, -828, -828, -828, -828, -828, -828, -828, -828 }, { - 67, -829, -829, -829, -829, -829, -829, -829, -829, -829, + 69, -829, -829, -829, -829, -829, -829, -829, -829, -829, -829, -829, -829, -829, -829, -829, -829, -829, -829, -829, -829, -829, -829, -829, -829, -829, -829, -829, -829, -829, - -829, -829, -829, -829, -829, -829, -829, -829, -829, -829, - -829, -829, -829, -829, -829, -829, -829, -829, 830, 830, - 830, 830, 830, 830, 830, 830, 830, 830, -829, -829, - -829, -829, -829, -829, -829, -829, -829, -829, -829, -829, - -829, -829, -829, -829, -829, -829, -829, -829, -829, -829, - -829, -829, -829, -829, -829, -829, -829, -829, -829, -829, - -829, -829, -829, -829, -829, -829, -829, -829, -829, -829, + -829, -829, 1021, -829, -829, -829, -829, -829, -829, -829, + -829, -829, -829, -829, -829, -829, -829, -829, 1022, 1022, + 1022, 1022, 1022, 1022, 1022, 1022, 1022, 1022, -829, -829, + -829, -829, -829, -829, -829, 1021, 1021, 1021, 1021, 1021, + 1021, 1021, 1021, 1021, 1021, 1021, 1021, 1021, 1021, 1021, + 1021, 1021, 1021, 1021, 1021, 1021, 1021, 1021, 1021, 1021, + 1021, -829, -829, -829, -829, -829, -829, -829, -829, -829, -829, -829, -829, -829, -829, -829, -829, -829, -829, -829, -829, -829, -829, -829, -829, -829, -829, -829, -829, -829, -829, -829, -829, -829, -829, -829, -829, -829 @@ -14737,15 +14898,15 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -830, -830, -830, -830, -830, -830, -830, -830, -830, + 69, -830, -830, -830, -830, -830, -830, -830, -830, -830, + -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, - -830, -830, -830, -830, -830, -830, -830, -830, 830, 830, - 830, 830, 830, 830, 830, 830, 830, 830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, + -830, -830, 1023, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, @@ -14754,13 +14915,13 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -831, -831, -831, -831, -831, -831, -831, -831, -831, + 69, -831, -831, -831, -831, -831, -831, -831, -831, -831, -831, -831, -831, -831, -831, -831, -831, -831, -831, -831, -831, -831, -831, -831, -831, -831, -831, -831, -831, -831, - -831, -831, 831, -831, -831, -831, -831, -831, -831, -831, - -831, -831, -831, 832, -831, 832, -831, -831, 833, 833, - 833, 833, 833, 833, 833, 833, 833, 833, -831, -831, -831, -831, -831, -831, -831, -831, -831, -831, -831, -831, + -831, -831, -831, -831, -831, -831, -831, -831, -831, -831, + -831, -831, -831, -831, -831, -831, -831, -831, -831, -831, + -831, -831, -831, -831, -831, -831, -831, 1024, -831, -831, -831, -831, -831, -831, -831, -831, -831, -831, -831, -831, -831, -831, -831, -831, -831, -831, -831, -831, -831, -831, @@ -14771,13 +14932,13 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -832, -832, -832, -832, -832, -832, -832, -832, -832, - -832, -832, -832, -832, -832, -832, -832, -832, -832, -832, + 69, -832, -832, -832, -832, -832, -832, -832, -832, -832, -832, -832, -832, -832, -832, -832, -832, -832, -832, -832, -832, -832, -832, -832, -832, -832, -832, -832, -832, -832, + -832, -832, 1025, -832, -832, -832, -832, -832, -832, -832, - -832, -832, -832, -832, -832, -832, -832, -832, 833, 833, - 833, 833, 833, 833, 833, 833, 833, 833, -832, -832, + -832, -832, -832, -832, -832, -832, -832, -832, -832, -832, + -832, -832, -832, -832, -832, -832, -832, -832, -832, -832, -832, -832, -832, -832, -832, -832, -832, -832, -832, -832, -832, -832, -832, -832, -832, -832, -832, -832, -832, -832, -832, -832, -832, -832, -832, -832, -832, -832, -832, -832, @@ -14788,13 +14949,13 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -833, -833, -833, -833, -833, -833, -833, -833, -833, + 69, -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, + -833, -833, 1026, -833, -833, -833, -833, -833, -833, -833, + -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, - -833, -833, -833, -833, -833, -833, -833, -833, 833, 833, - 833, 833, 833, 833, 833, 833, 833, 833, -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, @@ -14806,51 +14967,51 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -834, -834, -834, -834, -834, -834, -834, -834, -834, - -834, -834, -834, -834, -834, -834, -834, -834, -834, -834, - -834, -834, -834, -834, -834, -834, -834, -834, -834, -834, - -834, -834, 834, -834, -834, -834, -834, -834, -834, 835, - -834, -834, -834, -834, -834, -834, -834, -834, -834, -834, - -834, -834, -834, -834, -834, -834, -834, -834, -834, -834, - -834, -834, -834, -834, -834, -834, -834, -834, -834, -834, - -834, -834, -834, -834, -834, -834, -834, -834, -834, -834, - + 69, -834, -834, -834, -834, -834, -834, -834, -834, -834, -834, -834, -834, -834, -834, -834, -834, -834, -834, -834, -834, -834, -834, -834, -834, -834, -834, -834, -834, -834, + -834, -834, 1027, -834, -834, -834, -834, -834, -834, -834, + -834, -834, -834, -834, -834, -834, -834, -834, 1028, 1028, + 1028, 1028, 1028, 1028, 1028, 1028, 1028, 1028, -834, -834, + -834, -834, -834, -834, -834, 1027, 1027, 1027, 1027, 1027, + 1027, 1027, 1027, 1027, 1027, 1027, 1027, 1027, 1027, 1027, + + 1027, 1027, 1027, 1027, 1027, 1027, 1027, 1027, 1027, 1027, + 1027, -834, -834, -834, -834, -834, -834, -834, -834, -834, -834, -834, -834, -834, -834, -834, -834, -834, -834, -834, -834, -834, -834, -834, -834, -834, -834, -834, -834, -834, -834, -834, -834, -834, -834, -834, -834, -834 }, { - 67, 847, 847, 847, 847, 847, 847, 847, 847, 847, - 847, 847, 847, 847, 847, 847, 847, 847, 847, 847, - 847, 847, 847, 847, 847, 847, 847, 847, 847, 847, - 847, 847, 847, 847, 847, 847, 847, 847, 847, 848, - 847, 847, 847, 847, 847, 847, 847, 847, 847, 847, + 69, -835, -835, -835, -835, -835, -835, -835, -835, -835, + -835, -835, -835, -835, -835, -835, -835, -835, -835, -835, + -835, -835, -835, -835, -835, -835, -835, -835, -835, -835, + -835, -835, 1029, -835, -835, -835, -835, -835, -835, -835, + -835, -835, -835, -835, -835, -835, -835, -835, -835, -835, - 847, 847, 847, 847, 847, 847, 847, 847, 847, 847, - 847, 847, 847, 847, 847, 847, 847, 847, 847, 847, - 847, 847, 847, 847, 847, 847, 847, 847, 847, 847, - 847, 847, 847, 847, 847, 847, 847, 847, 847, 847, - 847, 847, 847, 847, 847, 847, 847, 847, 847, 847, - 847, 847, 847, 847, 847, 847, 847, 847, 847, 847, - 847, 847, 847, 847, 847, 847, 847, 847, 847, 847, - 847, 847, 847, 847, 847, 847, 847, 847 + -835, -835, -835, -835, -835, -835, -835, -835, -835, -835, + -835, -835, -835, -835, -835, -835, -835, -835, -835, -835, + -835, -835, -835, -835, -835, -835, -835, -835, -835, -835, + -835, -835, -835, -835, -835, -835, -835, -835, -835, -835, + -835, -835, -835, -835, -835, -835, -835, -835, -835, -835, + -835, -835, -835, -835, -835, -835, -835, -835, -835, -835, + -835, -835, -835, -835, -835, -835, -835, -835, -835, -835, + -835, -835, -835, -835, -835, -835, -835, -835 }, { - 67, -836, -836, -836, -836, -836, -836, -836, -836, -836, + 69, -836, -836, -836, -836, -836, -836, -836, -836, -836, -836, -836, -836, -836, -836, -836, -836, -836, -836, -836, -836, -836, -836, -836, -836, -836, -836, -836, -836, -836, - -836, -836, 836, -836, -836, -836, -836, -836, -836, 837, - -836, -836, -836, -836, -836, -836, -836, -836, -836, -836, - -836, -836, -836, -836, -836, -836, -836, -836, -836, -836, - -836, -836, -836, -836, -836, -836, -836, -836, -836, -836, - -836, -836, -836, -836, -836, -836, -836, -836, -836, -836, - -836, -836, -836, -836, -836, -836, -836, -836, -836, -836, - -836, -836, -836, -836, -836, -836, -836, -836, -836, -836, + -836, -836, 1030, -836, -836, -836, -836, -836, -836, -836, + -836, -836, -836, -836, -836, -836, -836, -836, 1031, 1031, + 1031, 1031, 1031, 1031, 1031, 1031, 1031, 1031, -836, -836, + -836, -836, -836, -836, -836, 1030, 1030, 1030, 1030, 1030, + 1030, 1030, 1030, 1030, 1030, 1030, 1030, 1030, 1030, 1030, + 1030, 1030, 1030, 1030, 1030, 1030, 1030, 1030, 1030, 1030, + 1030, -836, -836, -836, -836, -836, -836, -836, -836, -836, -836, -836, -836, -836, -836, -836, -836, -836, -836, -836, -836, -836, -836, -836, -836, -836, -836, -836, -836, -836, @@ -14858,29 +15019,29 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, 849, 849, 849, 849, 849, 849, 849, 849, 849, - 849, 849, 849, 849, 849, 849, 849, 849, 849, 849, - 849, 849, 849, 849, 849, 849, 849, 849, 849, 849, - 849, 849, 849, 849, 849, 849, 849, 849, 849, 850, - 849, 849, 849, 849, 849, 849, 849, 849, 849, 849, - 849, 849, 849, 849, 849, 849, 849, 849, 849, 849, - 849, 849, 849, 849, 849, 849, 849, 849, 849, 849, - 849, 849, 849, 849, 849, 849, 849, 849, 849, 849, - 849, 849, 849, 849, 849, 849, 849, 849, 849, 849, + 69, -837, -837, -837, -837, -837, -837, -837, -837, -837, + -837, -837, -837, -837, -837, -837, -837, -837, -837, -837, + -837, -837, -837, -837, -837, -837, -837, -837, -837, -837, + -837, -837, 1032, -837, -837, -837, -837, -837, -837, -837, + -837, -837, -837, -837, -837, -837, -837, -837, 1033, 1033, + 1033, 1033, 1033, 1033, 1033, 1033, 1033, 1033, -837, -837, + -837, -837, -837, -837, -837, 1032, 1032, 1032, 1032, 1032, + 1032, 1032, 1032, 1032, 1032, 1032, 1032, 1032, 1032, 1032, + 1032, 1032, 1032, 1032, 1032, 1032, 1032, 1032, 1032, 1032, - 849, 849, 849, 849, 849, 849, 849, 849, 849, 849, - 849, 849, 849, 849, 849, 849, 849, 849, 849, 849, - 849, 849, 849, 849, 849, 849, 849, 849, 849, 849, - 849, 849, 849, 849, 849, 849, 849, 849 + 1032, -837, -837, -837, -837, -837, -837, -837, -837, -837, + -837, -837, -837, -837, -837, -837, -837, -837, -837, -837, + -837, -837, -837, -837, -837, -837, -837, -837, -837, -837, + -837, -837, -837, -837, -837, -837, -837, -837 }, { - 67, -838, -838, -838, -838, -838, -838, -838, -838, -838, - -838, -838, -838, -838, -838, -838, -838, -838, -838, -838, + 69, -838, -838, -838, -838, -838, -838, -838, -838, -838, -838, -838, -838, -838, -838, -838, -838, -838, -838, -838, - -838, -838, 838, -838, -838, -838, -838, -838, -838, 839, -838, -838, -838, -838, -838, -838, -838, -838, -838, -838, -838, -838, -838, -838, -838, -838, -838, -838, -838, -838, + -838, -838, -838, -838, -838, -838, -838, -838, 1034, 1034, + 1034, 1034, 1034, 1034, 1034, 1034, 1034, 1034, -838, -838, -838, -838, -838, -838, -838, -838, -838, -838, -838, -838, -838, -838, -838, -838, -838, -838, -838, -838, -838, -838, @@ -14892,30 +15053,30 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, 851, 851, 851, 851, 851, 851, 851, 851, 851, - 851, 851, 851, 851, 851, 851, 851, 851, 851, 851, - 851, 851, 851, 851, 851, 851, 851, 851, 851, 851, + 69, -839, -839, -839, -839, -839, -839, -839, -839, -839, + -839, -839, -839, -839, -839, -839, -839, -839, -839, -839, + -839, -839, -839, -839, -839, -839, -839, -839, -839, -839, - 851, 851, 851, 851, 851, 851, 851, 851, 851, 852, - 851, 851, 851, 851, 851, 851, 851, 851, 851, 851, - 851, 851, 851, 851, 851, 851, 851, 851, 851, 851, - 851, 851, 851, 851, 851, 851, 851, 851, 851, 851, - 851, 851, 851, 851, 851, 851, 851, 851, 851, 851, - 851, 851, 851, 851, 851, 851, 851, 851, 851, 851, - 851, 851, 851, 851, 851, 851, 851, 851, 851, 851, - 851, 851, 851, 851, 851, 851, 851, 851, 851, 851, - 851, 851, 851, 851, 851, 851, 851, 851, 851, 851, - 851, 851, 851, 851, 851, 851, 851, 851 + -839, -839, -839, -839, -839, -839, -839, -839, -839, -839, + -839, -839, -839, -839, -839, -839, -839, -839, 1035, 1035, + 1035, 1035, 1035, 1035, 1035, 1035, 1035, 1035, -839, -839, + -839, -839, -839, -839, -839, -839, -839, -839, -839, -839, + -839, -839, -839, -839, -839, -839, -839, -839, -839, -839, + -839, -839, -839, -839, -839, -839, -839, -839, -839, -839, + -839, -839, -839, -839, -839, -839, -839, -839, -839, -839, + -839, -839, -839, -839, -839, -839, -839, -839, -839, -839, + -839, -839, -839, -839, -839, -839, -839, -839, -839, -839, + -839, -839, -839, -839, -839, -839, -839, -839 }, { - 67, -840, -840, -840, -840, -840, -840, -840, -840, -840, + 69, -840, -840, -840, -840, -840, -840, -840, -840, -840, -840, -840, -840, -840, -840, -840, -840, -840, -840, -840, -840, -840, -840, -840, -840, -840, -840, -840, -840, -840, - -840, -840, 840, -840, -840, -840, -840, -840, -840, 841, - -840, -840, -840, -840, -840, -840, -840, -840, -840, -840, -840, -840, -840, -840, -840, -840, -840, -840, -840, -840, + -840, -840, -840, -840, -840, -840, -840, -840, 1036, 1036, + 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, -840, -840, -840, -840, -840, -840, -840, -840, -840, -840, -840, -840, -840, -840, -840, -840, -840, -840, -840, -840, -840, -840, -840, -840, -840, -840, -840, -840, -840, -840, -840, -840, @@ -14927,97 +15088,97 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, 853, 853, 853, 853, 853, 853, 853, 853, 853, - 853, 853, 853, 853, 853, 853, 853, 853, 853, 853, - 853, 853, 853, 853, 853, 853, 853, 853, 853, 853, - 853, 853, 853, 853, 853, 853, 853, 853, 853, 854, - 853, 853, 853, 853, 853, 853, 853, 853, 853, 853, - 853, 853, 853, 853, 853, 853, 853, 853, 853, 853, - 853, 853, 853, 853, 853, 853, 853, 853, 853, 853, + 69, -841, -841, -841, -841, -841, -841, -841, -841, -841, + -841, -841, -841, -841, -841, -841, -841, -841, -841, -841, + -841, -841, -841, -841, -841, -841, -841, -841, -841, -841, + -841, -841, -841, -841, -841, -841, -841, -841, -841, -841, + -841, -841, -841, -841, -841, -841, -841, -841, 1036, 1037, + 1037, 1037, 1037, 1037, 1037, 1037, 1037, 1037, -841, -841, + -841, -841, -841, -841, -841, -841, -841, -841, -841, -841, - 853, 853, 853, 853, 853, 853, 853, 853, 853, 853, - 853, 853, 853, 853, 853, 853, 853, 853, 853, 853, - 853, 853, 853, 853, 853, 853, 853, 853, 853, 853, - 853, 853, 853, 853, 853, 853, 853, 853, 853, 853, - 853, 853, 853, 853, 853, 853, 853, 853, 853, 853, - 853, 853, 853, 853, 853, 853, 853, 853 + -841, -841, -841, -841, -841, -841, -841, -841, -841, -841, + -841, -841, -841, -841, -841, -841, -841, -841, -841, -841, + -841, -841, -841, -841, -841, -841, -841, -841, -841, -841, + -841, -841, -841, -841, -841, -841, -841, -841, -841, -841, + -841, -841, -841, -841, -841, -841, -841, -841, -841, -841, + -841, -841, -841, -841, -841, -841, -841, -841 }, { - 67, -842, -842, -842, -842, -842, -842, -842, -842, -842, - -842, -842, -842, -842, -842, -842, -842, -842, -842, -842, - -842, -842, -842, -842, -842, -842, -842, -842, -842, -842, - -842, -842, 842, -842, -842, -842, -842, -842, -842, 843, - - -842, -842, -842, -842, -842, -842, -842, -842, -842, -842, - -842, -842, -842, -842, -842, -842, -842, -842, -842, -842, - -842, -842, -842, -842, -842, -842, -842, -842, -842, -842, - -842, -842, -842, -842, -842, -842, -842, -842, -842, -842, + 69, -842, -842, -842, -842, -842, -842, -842, -842, -842, -842, -842, -842, -842, -842, -842, -842, -842, -842, -842, -842, -842, -842, -842, -842, -842, -842, -842, -842, -842, + -842, -842, 1038, -842, -842, -842, -842, -842, -842, -842, + + -842, -842, -842, -842, -842, -842, -842, -842, 1039, 1039, + 1039, 1039, 1039, 1039, 1039, 1039, 1039, 1039, -842, -842, + -842, -842, -842, -842, -842, 1038, 1038, 1038, 1038, 1038, + 1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, + 1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, + 1038, -842, -842, -842, -842, -842, -842, -842, -842, -842, -842, -842, -842, -842, -842, -842, -842, -842, -842, -842, -842, -842, -842, -842, -842, -842, -842, -842, -842, -842, -842, -842, -842, -842, -842, -842, -842, -842 }, { - 67, 855, 855, 855, 855, 855, 855, 855, 855, 855, + 69, -843, -843, -843, -843, -843, -843, -843, -843, -843, - 855, 855, 855, 855, 855, 855, 855, 855, 855, 855, - 855, 855, 855, 855, 855, 855, 855, 855, 855, 855, - 855, 855, 855, 855, 855, 855, 855, 855, 855, 856, - 855, 855, 855, 855, 855, 855, 855, 855, 855, 855, - 855, 855, 855, 855, 855, 855, 855, 855, 855, 855, - 855, 855, 855, 855, 855, 855, 855, 855, 855, 855, - 855, 855, 855, 855, 855, 855, 855, 855, 855, 855, - 855, 855, 855, 855, 855, 855, 855, 855, 855, 855, - 855, 855, 855, 855, 855, 855, 855, 855, 855, 855, - 855, 855, 855, 855, 855, 855, 855, 855, 855, 855, + -843, -843, -843, -843, -843, -843, -843, -843, -843, -843, + -843, -843, -843, -843, -843, -843, -843, -843, -843, -843, + -843, -843, -843, -843, -843, -843, -843, -843, -843, -843, + -843, -843, -843, -843, -843, -843, -843, -843, 1036, 1040, + 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, -843, -843, + -843, -843, -843, -843, -843, -843, -843, -843, -843, -843, + -843, -843, -843, -843, -843, -843, -843, -843, -843, -843, + -843, -843, -843, -843, -843, -843, -843, -843, -843, -843, + -843, -843, -843, -843, -843, -843, -843, -843, -843, -843, + -843, -843, -843, -843, -843, -843, -843, -843, -843, -843, - 855, 855, 855, 855, 855, 855, 855, 855, 855, 855, - 855, 855, 855, 855, 855, 855, 855, 855 + -843, -843, -843, -843, -843, -843, -843, -843, -843, -843, + -843, -843, -843, -843, -843, -843, -843, -843 }, { - 67, -844, -844, -844, -844, -844, -844, -844, -844, -844, - -844, -844, -844, -844, -844, -844, -844, -844, -844, -844, - -844, -844, -844, -844, -844, -844, -844, -844, -844, -844, - -844, -844, 844, -844, -844, -844, -844, -844, -844, 845, - -844, -844, -844, -844, -844, -844, -844, -844, -844, -844, - -844, -844, -844, -844, -844, -844, -844, -844, -844, -844, - -844, -844, -844, -844, -844, -844, -844, -844, -844, -844, - -844, -844, -844, -844, -844, -844, -844, -844, -844, -844, - + 69, -844, -844, -844, -844, -844, -844, -844, -844, -844, -844, -844, -844, -844, -844, -844, -844, -844, -844, -844, -844, -844, -844, -844, -844, -844, -844, -844, -844, -844, + -844, -844, 1038, -844, -844, -844, -844, -844, -844, -844, + -844, -844, -844, -844, -844, -844, -844, -844, 1039, 1041, + 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, -844, -844, + -844, -844, -844, -844, -844, 1038, 1038, 1038, 1038, 1038, + 1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, + + 1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, + 1038, -844, -844, -844, -844, -844, -844, -844, -844, -844, -844, -844, -844, -844, -844, -844, -844, -844, -844, -844, -844, -844, -844, -844, -844, -844, -844, -844, -844, -844, -844, -844, -844, -844, -844, -844, -844, -844 }, { - 67, 857, 857, 857, 857, 857, 857, 857, 857, 857, - 857, 857, 857, 857, 857, 857, 857, 857, 857, 857, - 857, 857, 857, 857, 857, 857, 857, 857, 857, 857, - 857, 857, 857, 857, 857, 857, 857, 857, 857, 858, - 857, 857, 857, 857, 857, 857, 857, 857, 857, 857, + 69, -845, -845, -845, -845, -845, -845, -845, -845, -845, + -845, -845, -845, -845, -845, -845, -845, -845, -845, -845, + -845, -845, -845, -845, -845, -845, -845, -845, -845, -845, + -845, -845, 1042, -845, -845, -845, -845, -845, -845, -845, + -845, -845, -845, -845, -845, -845, -845, -845, 1043, 1043, - 857, 857, 857, 857, 857, 857, 857, 857, 857, 857, - 857, 857, 857, 857, 857, 857, 857, 857, 857, 857, - 857, 857, 857, 857, 857, 857, 857, 857, 857, 857, - 857, 857, 857, 857, 857, 857, 857, 857, 857, 857, - 857, 857, 857, 857, 857, 857, 857, 857, 857, 857, - 857, 857, 857, 857, 857, 857, 857, 857, 857, 857, - 857, 857, 857, 857, 857, 857, 857, 857, 857, 857, - 857, 857, 857, 857, 857, 857, 857, 857 + 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, -845, -845, + -845, -845, -845, -845, -845, 1042, 1042, 1042, 1042, 1042, + 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, + 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, + 1042, -845, -845, -845, -845, -845, -845, -845, -845, -845, + -845, -845, -845, -845, -845, -845, -845, -845, -845, -845, + -845, -845, -845, -845, -845, -845, -845, -845, -845, -845, + -845, -845, -845, -845, -845, -845, -845, -845 }, { - 67, -846, -846, -846, -846, -846, -846, -846, -846, -846, + 69, -846, -846, -846, -846, -846, -846, -846, -846, -846, -846, -846, -846, -846, -846, -846, -846, -846, -846, -846, -846, -846, -846, -846, -846, -846, -846, -846, -846, -846, - -846, -846, 859, -846, -846, -846, -846, -846, -846, -846, + -846, -846, 1044, -846, -846, -846, -846, -846, -846, -846, -846, -846, -846, -846, -846, -846, -846, -846, -846, -846, -846, -846, -846, -846, -846, -846, -846, -846, -846, -846, -846, -846, -846, -846, -846, -846, -846, -846, -846, -846, @@ -15031,68 +15192,68 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, 847, 847, 847, 847, 847, 847, 847, 847, 847, - 847, 847, 847, 847, 847, 847, 847, 847, 847, 847, - 847, 847, 847, 847, 847, 847, 847, 847, 847, 847, - 847, 847, 847, 847, 847, 847, 847, 847, 847, 848, - 847, 847, 847, 847, 847, 847, 847, 847, 847, 847, - 847, 847, 847, 847, 847, 847, 847, 847, 847, 847, - 847, 847, 847, 847, 847, 847, 847, 847, 847, 847, - 847, 847, 847, 847, 847, 847, 847, 847, 847, 847, - 847, 847, 847, 847, 847, 847, 847, 847, 847, 847, + 69, -847, -847, -847, -847, -847, -847, -847, -847, -847, + -847, -847, -847, -847, -847, -847, -847, -847, -847, -847, + -847, -847, -847, -847, -847, -847, -847, -847, -847, -847, + -847, -847, 1045, -847, -847, -847, -847, -847, -847, -847, + -847, -847, -847, -847, -847, -847, -847, -847, 1046, 1046, + 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, -847, -847, + -847, -847, -847, -847, -847, 1045, 1045, 1045, 1045, 1045, + 1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, + 1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, - 847, 847, 847, 847, 847, 847, 847, 847, 847, 847, - 847, 847, 847, 847, 847, 847, 847, 847, 847, 847, - 847, 847, 847, 847, 847, 847, 847, 847, 847, 847, - 847, 847, 847, 847, 847, 847, 847, 847 + 1045, -847, -847, -847, -847, -847, -847, -847, -847, -847, + -847, -847, -847, -847, -847, -847, -847, -847, -847, -847, + -847, -847, -847, -847, -847, -847, -847, -847, -847, -847, + -847, -847, -847, -847, -847, -847, -847, -847 }, { - 67, -848, -848, -848, -848, -848, -848, -848, -848, -848, - -848, -848, -848, -848, -848, -848, -848, -848, -848, -848, - -848, -848, -848, -848, -848, -848, -848, -848, -848, -848, - -848, -848, -848, -848, -848, -848, -848, -848, -848, 847, - -848, -848, -848, -848, -848, -848, -848, -848, -848, -848, - -848, -848, -848, -848, -848, -848, -848, -848, -848, -848, - - -848, -848, -848, -848, -848, -848, -848, -848, -848, -848, - -848, -848, -848, -848, -848, -848, -848, -848, -848, -848, + 69, -848, -848, -848, -848, -848, -848, -848, -848, -848, -848, -848, -848, -848, -848, -848, -848, -848, -848, -848, -848, -848, -848, -848, -848, -848, -848, -848, -848, -848, + -848, -848, 1038, -848, -848, -848, -848, -848, -848, -848, + -848, -848, -848, -848, -848, -848, -848, -848, 1039, 1047, + 1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, -848, -848, + + -848, -848, -848, -848, -848, 1038, 1038, 1038, 1038, 1038, + 1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, + 1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, + 1038, -848, -848, -848, -848, -848, -848, -848, -848, -848, -848, -848, -848, -848, -848, -848, -848, -848, -848, -848, -848, -848, -848, -848, -848, -848, -848, -848, -848, -848, -848, -848, -848, -848, -848, -848, -848, -848 }, { - 67, 849, 849, 849, 849, 849, 849, 849, 849, 849, - 849, 849, 849, 849, 849, 849, 849, 849, 849, 849, - 849, 849, 849, 849, 849, 849, 849, 849, 849, 849, + 69, -849, -849, -849, -849, -849, -849, -849, -849, -849, + -849, -849, -849, -849, -849, -849, -849, -849, -849, -849, + -849, -849, -849, -849, -849, -849, -849, -849, -849, -849, - 849, 849, 849, 849, 849, 849, 849, 849, 849, 850, - 849, 849, 849, 849, 849, 849, 849, 849, 849, 849, - 849, 849, 849, 849, 849, 849, 849, 849, 849, 849, - 849, 849, 849, 849, 849, 849, 849, 849, 849, 849, - 849, 849, 849, 849, 849, 849, 849, 849, 849, 849, - 849, 849, 849, 849, 849, 849, 849, 849, 849, 849, - 849, 849, 849, 849, 849, 849, 849, 849, 849, 849, - 849, 849, 849, 849, 849, 849, 849, 849, 849, 849, - 849, 849, 849, 849, 849, 849, 849, 849, 849, 849, - 849, 849, 849, 849, 849, 849, 849, 849 + -849, -849, 1048, -849, -849, -849, -849, -849, -849, -849, + -849, -849, -849, -849, -849, -849, -849, -849, 1049, 1049, + 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, -849, -849, + -849, -849, -849, -849, -849, 1048, 1048, 1048, 1048, 1048, + 1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, + 1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, + 1048, -849, -849, -849, -849, -849, -849, -849, -849, -849, + -849, -849, -849, -849, -849, -849, -849, -849, -849, -849, + -849, -849, -849, -849, -849, -849, -849, -849, -849, -849, + -849, -849, -849, -849, -849, -849, -849, -849 }, { - 67, -850, -850, -850, -850, -850, -850, -850, -850, -850, - -850, -850, -850, -850, -850, -850, -850, -850, -850, -850, - -850, -850, -850, -850, -850, -850, -850, -850, -850, -850, - -850, -850, -850, -850, -850, -850, -850, -850, -850, 849, - -850, -850, -850, -850, -850, -850, -850, -850, -850, -850, - -850, -850, -850, -850, -850, -850, -850, -850, -850, -850, - -850, -850, -850, -850, -850, -850, -850, -850, -850, -850, - -850, -850, -850, -850, -850, -850, -850, -850, -850, -850, + 69, -850, -850, -850, -850, -850, -850, -850, -850, -850, -850, -850, -850, -850, -850, -850, -850, -850, -850, -850, -850, -850, -850, -850, -850, -850, -850, -850, -850, -850, + -850, -850, 1045, -850, -850, -850, -850, -850, -850, -850, + -850, -850, -850, -850, -850, -850, -850, -850, 1046, 1050, + 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, -850, -850, + -850, -850, -850, -850, -850, 1045, 1045, 1045, 1045, 1045, + 1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, + 1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, + 1045, -850, -850, -850, -850, -850, -850, -850, -850, -850, -850, -850, -850, -850, -850, -850, -850, -850, -850, -850, -850, -850, -850, -850, -850, -850, -850, -850, -850, -850, @@ -15100,27 +15261,27 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, 851, 851, 851, 851, 851, 851, 851, 851, 851, - 851, 851, 851, 851, 851, 851, 851, 851, 851, 851, - 851, 851, 851, 851, 851, 851, 851, 851, 851, 851, - 851, 851, 851, 851, 851, 851, 851, 851, 851, 852, - 851, 851, 851, 851, 851, 851, 851, 851, 851, 851, - 851, 851, 851, 851, 851, 851, 851, 851, 851, 851, - 851, 851, 851, 851, 851, 851, 851, 851, 851, 851, + 69, -851, -851, -851, -851, -851, -851, -851, -851, -851, + -851, -851, -851, -851, -851, -851, -851, -851, -851, -851, + -851, -851, -851, -851, -851, -851, -851, -851, -851, -851, + -851, -851, 1051, -851, -851, -851, -851, -851, -851, -851, + -851, -851, -851, -851, -851, -851, -851, -851, 1052, 1052, + 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, -851, -851, + -851, -851, -851, -851, -851, 1051, 1051, 1051, 1051, 1051, - 851, 851, 851, 851, 851, 851, 851, 851, 851, 851, - 851, 851, 851, 851, 851, 851, 851, 851, 851, 851, - 851, 851, 851, 851, 851, 851, 851, 851, 851, 851, - 851, 851, 851, 851, 851, 851, 851, 851, 851, 851, - 851, 851, 851, 851, 851, 851, 851, 851, 851, 851, - 851, 851, 851, 851, 851, 851, 851, 851 + 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, + 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, + 1051, -851, -851, -851, -851, -851, -851, -851, -851, -851, + -851, -851, -851, -851, -851, -851, -851, -851, -851, -851, + -851, -851, -851, -851, -851, -851, -851, -851, -851, -851, + -851, -851, -851, -851, -851, -851, -851, -851 }, { - 67, -852, -852, -852, -852, -852, -852, -852, -852, -852, + 69, -852, -852, -852, -852, -852, -852, -852, -852, -852, -852, -852, -852, -852, -852, -852, -852, -852, -852, -852, -852, -852, -852, -852, -852, -852, -852, -852, -852, -852, - -852, -852, -852, -852, -852, -852, -852, -852, -852, 851, + -852, -852, 1053, -852, -852, -852, -852, -852, -852, -852, -852, -852, -852, -852, -852, -852, -852, -852, -852, -852, -852, -852, -852, -852, -852, -852, -852, -852, -852, -852, @@ -15134,28 +15295,28 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, 853, 853, 853, 853, 853, 853, 853, 853, 853, + 69, -853, -853, -853, -853, -853, -853, -853, -853, -853, - 853, 853, 853, 853, 853, 853, 853, 853, 853, 853, - 853, 853, 853, 853, 853, 853, 853, 853, 853, 853, - 853, 853, 853, 853, 853, 853, 853, 853, 853, 854, - 853, 853, 853, 853, 853, 853, 853, 853, 853, 853, - 853, 853, 853, 853, 853, 853, 853, 853, 853, 853, - 853, 853, 853, 853, 853, 853, 853, 853, 853, 853, - 853, 853, 853, 853, 853, 853, 853, 853, 853, 853, - 853, 853, 853, 853, 853, 853, 853, 853, 853, 853, - 853, 853, 853, 853, 853, 853, 853, 853, 853, 853, - 853, 853, 853, 853, 853, 853, 853, 853, 853, 853, + -853, -853, -853, -853, -853, -853, -853, -853, -853, -853, + -853, -853, -853, -853, -853, -853, -853, -853, -853, -853, + -853, -853, 1054, -853, -853, -853, -853, -853, -853, -853, + -853, -853, -853, -853, -853, -853, -853, -853, 1046, 1046, + 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, -853, -853, + -853, -853, -853, -853, -853, 1054, 1054, 1054, 1054, 1054, + 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, + 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, + 1054, -853, -853, -853, -853, -853, -853, -853, -853, -853, + -853, -853, -853, -853, -853, -853, -853, -853, -853, -853, - 853, 853, 853, 853, 853, 853, 853, 853, 853, 853, - 853, 853, 853, 853, 853, 853, 853, 853 + -853, -853, -853, -853, -853, -853, -853, -853, -853, -853, + -853, -853, -853, -853, -853, -853, -853, -853 }, { - 67, -854, -854, -854, -854, -854, -854, -854, -854, -854, + 69, -854, -854, -854, -854, -854, -854, -854, -854, -854, -854, -854, -854, -854, -854, -854, -854, -854, -854, -854, -854, -854, -854, -854, -854, -854, -854, -854, -854, -854, - -854, -854, -854, -854, -854, -854, -854, -854, -854, 853, + -854, -854, 1055, -854, -854, -854, -854, -854, -854, -854, -854, -854, -854, -854, -854, -854, -854, -854, -854, -854, -854, -854, -854, -854, -854, -854, -854, -854, -854, -854, -854, -854, -854, -854, -854, -854, -854, -854, -854, -854, @@ -15169,34 +15330,34 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, 855, 855, 855, 855, 855, 855, 855, 855, 855, - 855, 855, 855, 855, 855, 855, 855, 855, 855, 855, - 855, 855, 855, 855, 855, 855, 855, 855, 855, 855, - 855, 855, 855, 855, 855, 855, 855, 855, 855, 856, - 855, 855, 855, 855, 855, 855, 855, 855, 855, 855, + 69, -855, -855, -855, -855, -855, -855, -855, -855, -855, + -855, -855, -855, -855, -855, -855, -855, -855, -855, -855, + -855, -855, -855, -855, -855, -855, -855, -855, -855, -855, + -855, -855, 1056, -855, -855, -855, -855, -855, -855, -855, + -855, -855, -855, -855, -855, -855, -855, -855, -855, -855, - 855, 855, 855, 855, 855, 855, 855, 855, 855, 855, - 855, 855, 855, 855, 855, 855, 855, 855, 855, 855, - 855, 855, 855, 855, 855, 855, 855, 855, 855, 855, - 855, 855, 855, 855, 855, 855, 855, 855, 855, 855, - 855, 855, 855, 855, 855, 855, 855, 855, 855, 855, - 855, 855, 855, 855, 855, 855, 855, 855, 855, 855, - 855, 855, 855, 855, 855, 855, 855, 855, 855, 855, - 855, 855, 855, 855, 855, 855, 855, 855 + -855, -855, -855, -855, -855, -855, -855, -855, -855, -855, + -855, -855, -855, -855, -855, -855, -855, -855, -855, -855, + -855, -855, -855, -855, -855, -855, -855, -855, -855, -855, + -855, -855, -855, -855, -855, -855, -855, -855, -855, -855, + -855, -855, -855, -855, -855, -855, -855, -855, -855, -855, + -855, -855, -855, -855, -855, -855, -855, -855, -855, -855, + -855, -855, -855, -855, -855, -855, -855, -855, -855, -855, + -855, -855, -855, -855, -855, -855, -855, -855 }, { - 67, -856, -856, -856, -856, -856, -856, -856, -856, -856, + 69, -856, -856, -856, -856, -856, -856, -856, -856, -856, -856, -856, -856, -856, -856, -856, -856, -856, -856, -856, -856, -856, -856, -856, -856, -856, -856, -856, -856, -856, - -856, -856, -856, -856, -856, -856, -856, -856, -856, 855, - -856, -856, -856, -856, -856, -856, -856, -856, -856, -856, - -856, -856, -856, -856, -856, -856, -856, -856, -856, -856, - -856, -856, -856, -856, -856, -856, -856, -856, -856, -856, - -856, -856, -856, -856, -856, -856, -856, -856, -856, -856, - -856, -856, -856, -856, -856, -856, -856, -856, -856, -856, - -856, -856, -856, -856, -856, -856, -856, -856, -856, -856, + -856, -856, 1057, -856, -856, -856, -856, -856, -856, -856, + -856, -856, -856, -856, -856, -856, -856, -856, 1058, 1058, + 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, -856, -856, + -856, -856, -856, -856, -856, 1057, 1057, 1057, 1057, 1057, + 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, + 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, + 1057, -856, -856, -856, -856, -856, -856, -856, -856, -856, -856, -856, -856, -856, -856, -856, -856, -856, -856, -856, -856, -856, -856, -856, -856, -856, -856, -856, -856, -856, @@ -15204,45 +15365,45 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, 857, 857, 857, 857, 857, 857, 857, 857, 857, - 857, 857, 857, 857, 857, 857, 857, 857, 857, 857, - 857, 857, 857, 857, 857, 857, 857, 857, 857, 857, - 857, 857, 857, 857, 857, 857, 857, 857, 857, 858, - 857, 857, 857, 857, 857, 857, 857, 857, 857, 857, - 857, 857, 857, 857, 857, 857, 857, 857, 857, 857, - 857, 857, 857, 857, 857, 857, 857, 857, 857, 857, - 857, 857, 857, 857, 857, 857, 857, 857, 857, 857, - 857, 857, 857, 857, 857, 857, 857, 857, 857, 857, + 69, -857, -857, -857, -857, -857, -857, -857, -857, -857, + -857, -857, -857, -857, -857, -857, -857, -857, -857, -857, + -857, -857, -857, -857, -857, -857, -857, -857, -857, -857, + -857, -857, 1045, -857, -857, -857, -857, -857, -857, -857, + -857, -857, -857, -857, -857, -857, -857, -857, 1046, 1059, + 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, -857, -857, + -857, -857, -857, -857, -857, 1045, 1045, 1045, 1045, 1045, + 1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, + 1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, - 857, 857, 857, 857, 857, 857, 857, 857, 857, 857, - 857, 857, 857, 857, 857, 857, 857, 857, 857, 857, - 857, 857, 857, 857, 857, 857, 857, 857, 857, 857, - 857, 857, 857, 857, 857, 857, 857, 857 + 1045, -857, -857, -857, -857, -857, -857, -857, -857, -857, + -857, -857, -857, -857, -857, -857, -857, -857, -857, -857, + -857, -857, -857, -857, -857, -857, -857, -857, -857, -857, + -857, -857, -857, -857, -857, -857, -857, -857 }, { - 67, -858, -858, -858, -858, -858, -858, -858, -858, -858, - -858, -858, -858, -858, -858, -858, -858, -858, -858, -858, - -858, -858, -858, -858, -858, -858, -858, -858, -858, -858, - -858, -858, -858, -858, -858, -858, -858, -858, -858, 857, - -858, -858, -858, -858, -858, -858, -858, -858, -858, -858, - -858, -858, -858, -858, -858, -858, -858, -858, -858, -858, - - -858, -858, -858, -858, -858, -858, -858, -858, -858, -858, - -858, -858, -858, -858, -858, -858, -858, -858, -858, -858, + 69, -858, -858, -858, -858, -858, -858, -858, -858, -858, -858, -858, -858, -858, -858, -858, -858, -858, -858, -858, -858, -858, -858, -858, -858, -858, -858, -858, -858, -858, + -858, -858, 1060, -858, -858, -858, -858, -858, -858, -858, + -858, -858, -858, -858, -858, -858, -858, -858, 1061, 1061, + 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, -858, -858, + + -858, -858, -858, -858, -858, 1060, 1060, 1060, 1060, 1060, + 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, + 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, + 1060, -858, -858, -858, -858, -858, -858, -858, -858, -858, -858, -858, -858, -858, -858, -858, -858, -858, -858, -858, -858, -858, -858, -858, -858, -858, -858, -858, -858, -858, -858, -858, -858, -858, -858, -858, -858, -858 }, { - 67, -859, -859, -859, -859, -859, -859, -859, -859, -859, + 69, -859, -859, -859, -859, -859, -859, -859, -859, -859, -859, -859, -859, -859, -859, -859, -859, -859, -859, -859, -859, -859, -859, -859, -859, -859, -859, -859, -859, -859, - -859, -859, 860, -859, -859, -859, -859, -859, -859, -859, + -859, -859, 1062, -859, -859, -859, -859, -859, -859, -859, -859, -859, -859, -859, -859, -859, -859, -859, -859, -859, -859, -859, -859, -859, -859, -859, -859, -859, -859, -859, -859, -859, -859, -859, -859, -859, -859, -859, -859, -859, @@ -15256,16 +15417,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -860, -860, -860, -860, -860, -860, -860, -860, -860, - -860, -860, -860, -860, -860, -860, -860, -860, -860, -860, - -860, -860, -860, -860, -860, -860, -860, -860, -860, -860, - -860, -860, 861, -860, -860, -860, -860, -860, -860, -860, - -860, -860, -860, -860, -860, -860, -860, -860, -860, -860, - -860, -860, -860, -860, -860, -860, -860, -860, -860, -860, - -860, -860, -860, -860, -860, -860, -860, -860, -860, -860, - -860, -860, -860, -860, -860, -860, -860, -860, -860, -860, + 69, -860, -860, -860, -860, -860, -860, -860, -860, -860, -860, -860, -860, -860, -860, -860, -860, -860, -860, -860, -860, -860, -860, -860, -860, -860, -860, -860, -860, -860, + -860, -860, 1063, -860, -860, -860, -860, -860, -860, -860, + -860, -860, -860, -860, -860, -860, -860, -860, 1046, 1046, + 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, -860, -860, + -860, -860, -860, -860, -860, 1063, 1063, 1063, 1063, 1063, + 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, + 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, + 1063, -860, -860, -860, -860, -860, -860, -860, -860, -860, -860, -860, -860, -860, -860, -860, -860, -860, -860, -860, -860, -860, -860, -860, -860, -860, -860, -860, -860, -860, @@ -15273,45 +15434,45 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -861, -861, -861, -861, -861, -861, -861, -861, -861, - -861, -861, -861, -861, -861, -861, -861, -861, -861, -861, - -861, -861, -861, -861, -861, -861, -861, -861, -861, -861, - -861, -861, 862, -861, -861, -861, -861, -861, -861, -861, - -861, -861, -861, -861, -861, -861, -861, -861, -861, -861, - -861, -861, -861, -861, -861, -861, -861, -861, -861, -861, - -861, -861, -861, -861, -861, -861, -861, -861, -861, -861, - - -861, -861, -861, -861, -861, -861, -861, -861, -861, -861, + 69, -861, -861, -861, -861, -861, -861, -861, -861, -861, -861, -861, -861, -861, -861, -861, -861, -861, -861, -861, -861, -861, -861, -861, -861, -861, -861, -861, -861, -861, + -861, -861, 1057, -861, -861, -861, -861, -861, -861, -861, + -861, -861, -861, -861, -861, -861, -861, -861, 1058, 1064, + 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, -861, -861, + -861, -861, -861, -861, -861, 1057, 1057, 1057, 1057, 1057, + + 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, + 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, + 1057, -861, -861, -861, -861, -861, -861, -861, -861, -861, -861, -861, -861, -861, -861, -861, -861, -861, -861, -861, -861, -861, -861, -861, -861, -861, -861, -861, -861, -861, -861, -861, -861, -861, -861, -861, -861, -861 }, { - 67, -862, -862, -862, -862, -862, -862, -862, -862, -862, - -862, -862, -862, -862, -862, -862, -862, -862, -862, -862, - -862, -862, -862, -862, -862, -862, -862, -862, -862, -862, - -862, -862, 863, -862, -862, -862, -862, -862, -862, -862, - - -862, -862, -862, -862, -862, -862, -862, -862, -862, -862, - -862, -862, -862, -862, -862, -862, -862, -862, -862, -862, - -862, -862, -862, -862, -862, -862, -862, -862, -862, -862, - -862, -862, -862, -862, -862, -862, -862, -862, -862, -862, + 69, -862, -862, -862, -862, -862, -862, -862, -862, -862, -862, -862, -862, -862, -862, -862, -862, -862, -862, -862, -862, -862, -862, -862, -862, -862, -862, -862, -862, -862, + -862, -862, 1065, -862, -862, -862, -862, -862, -862, -862, + + -862, -862, -862, -862, -862, -862, -862, -862, 1066, 1066, + 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, -862, -862, + -862, -862, -862, -862, -862, 1065, 1065, 1065, 1065, 1065, + 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, + 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, + 1065, -862, -862, -862, -862, -862, -862, -862, -862, -862, -862, -862, -862, -862, -862, -862, -862, -862, -862, -862, -862, -862, -862, -862, -862, -862, -862, -862, -862, -862, -862, -862, -862, -862, -862, -862, -862, -862 }, { - 67, -863, -863, -863, -863, -863, -863, -863, -863, -863, + 69, -863, -863, -863, -863, -863, -863, -863, -863, -863, -863, -863, -863, -863, -863, -863, -863, -863, -863, -863, -863, -863, -863, -863, -863, -863, -863, -863, -863, -863, - -863, -863, 864, -863, -863, -863, -863, -863, -863, -863, + -863, -863, 1067, -863, -863, -863, -863, -863, -863, -863, -863, -863, -863, -863, -863, -863, -863, -863, -863, -863, -863, -863, -863, -863, -863, -863, -863, -863, -863, -863, -863, -863, -863, -863, -863, -863, -863, -863, -863, -863, @@ -15325,27 +15486,27 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -864, -864, -864, -864, -864, -864, -864, -864, -864, - -864, -864, -864, -864, -864, -864, -864, -864, -864, -864, - -864, -864, -864, -864, -864, -864, -864, -864, -864, -864, - -864, -864, 865, -864, -864, -864, -864, -864, -864, -864, - -864, -864, -864, -864, -864, -864, -864, -864, -864, -864, - -864, -864, -864, -864, -864, -864, -864, -864, -864, -864, - -864, -864, -864, -864, -864, -864, -864, -864, -864, -864, - -864, -864, -864, -864, -864, -864, -864, -864, -864, -864, - + 69, -864, -864, -864, -864, -864, -864, -864, -864, -864, -864, -864, -864, -864, -864, -864, -864, -864, -864, -864, -864, -864, -864, -864, -864, -864, -864, -864, -864, -864, + -864, -864, 1068, -864, -864, -864, -864, -864, -864, -864, + -864, -864, -864, -864, -864, -864, -864, -864, 1058, 1058, + 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, -864, -864, + -864, -864, -864, -864, -864, 1068, 1068, 1068, 1068, 1068, + 1068, 1068, 1068, 1068, 1068, 1068, 1068, 1068, 1068, 1068, + + 1068, 1068, 1068, 1068, 1068, 1068, 1068, 1068, 1068, 1068, + 1068, -864, -864, -864, -864, -864, -864, -864, -864, -864, -864, -864, -864, -864, -864, -864, -864, -864, -864, -864, -864, -864, -864, -864, -864, -864, -864, -864, -864, -864, -864, -864, -864, -864, -864, -864, -864, -864 }, { - 67, -865, -865, -865, -865, -865, -865, -865, -865, -865, + 69, -865, -865, -865, -865, -865, -865, -865, -865, -865, -865, -865, -865, -865, -865, -865, -865, -865, -865, -865, -865, -865, -865, -865, -865, -865, -865, -865, -865, -865, - -865, -865, 866, -865, -865, -865, -865, -865, -865, -865, + -865, -865, 1069, -865, -865, -865, -865, -865, -865, -865, -865, -865, -865, -865, -865, -865, -865, -865, -865, -865, -865, -865, -865, -865, -865, -865, -865, -865, -865, -865, @@ -15359,11 +15520,11 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -866, -866, -866, -866, -866, -866, -866, -866, -866, + 69, -866, -866, -866, -866, -866, -866, -866, -866, -866, -866, -866, -866, -866, -866, -866, -866, -866, -866, -866, -866, -866, -866, -866, -866, -866, -866, -866, -866, -866, - -866, -866, 867, -866, -866, -866, -866, -866, -866, -866, + -866, -866, 1070, -866, -866, -866, -866, -866, -866, -866, -866, -866, -866, -866, -866, -866, -866, -866, -866, -866, -866, -866, -866, -866, -866, -866, -866, -866, -866, -866, -866, -866, -866, -866, -866, -866, -866, -866, -866, -866, @@ -15377,12 +15538,12 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -867, -867, -867, -867, -867, -867, -867, -867, -867, + 69, -867, -867, -867, -867, -867, -867, -867, -867, -867, -867, -867, -867, -867, -867, -867, -867, -867, -867, -867, -867, -867, -867, -867, -867, -867, -867, -867, -867, -867, - -867, -867, 868, -867, -867, -867, -867, -867, -867, -867, - -867, -867, -867, -867, -867, -867, -867, -867, -867, -867, -867, -867, -867, -867, -867, -867, -867, -867, -867, -867, + -867, -867, -867, -867, -867, -867, -867, -867, -867, 1071, + 1071, 1071, 1071, 1071, 1071, 1071, 1071, 1071, -867, -867, -867, -867, -867, -867, -867, -867, -867, -867, -867, -867, -867, -867, -867, -867, -867, -867, -867, -867, -867, -867, -867, -867, -867, -867, -867, -867, -867, -867, -867, -867, @@ -15394,28 +15555,28 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -868, -868, -868, -868, -868, -868, -868, -868, -868, - -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, - -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, - -868, -868, 869, -868, -868, -868, -868, -868, -868, -868, - -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, - -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, - - -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, - -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, + 69, -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, + -868, -868, 1072, -868, -868, -868, -868, -868, -868, -868, + -868, -868, -868, -868, -868, -868, -868, -868, 1073, 1073, + 1073, 1073, 1073, 1073, 1073, 1073, 1073, 1073, -868, -868, + + -868, -868, -868, -868, -868, 1072, 1072, 1072, 1072, 1072, + 1072, 1072, 1072, 1072, 1072, 1072, 1072, 1072, 1072, 1072, + 1072, 1072, 1072, 1072, 1072, 1072, 1072, 1072, 1072, 1072, + 1072, -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, -868 }, { - 67, -869, -869, -869, -869, -869, -869, -869, -869, -869, + 69, -869, -869, -869, -869, -869, -869, -869, -869, -869, -869, -869, -869, -869, -869, -869, -869, -869, -869, -869, -869, -869, -869, -869, -869, -869, -869, -869, -869, -869, - -869, -869, 870, -869, -869, -869, -869, -869, -869, -869, + -869, -869, 1074, -869, -869, -869, -869, -869, -869, -869, -869, -869, -869, -869, -869, -869, -869, -869, -869, -869, -869, -869, -869, -869, -869, -869, -869, -869, -869, -869, -869, -869, -869, -869, -869, -869, -869, -869, -869, -869, @@ -15429,16 +15590,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -870, -870, -870, -870, -870, -870, -870, -870, -870, - -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, - -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, - -870, -870, 871, -870, -870, -870, -870, -870, -870, -870, - -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, - -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, - -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, - -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, + 69, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, + -870, -870, 1075, -870, -870, -870, -870, -870, -870, -870, + -870, -870, -870, -870, -870, -870, -870, -870, 1076, 1076, + 1076, 1076, 1076, 1076, 1076, 1076, 1076, 1076, -870, -870, + -870, -870, -870, -870, -870, 1075, 1075, 1075, 1075, 1075, + 1075, 1075, 1075, 1075, 1075, 1075, 1075, 1075, 1075, 1075, + 1075, 1075, 1075, 1075, 1075, 1075, 1075, 1075, 1075, 1075, + 1075, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, @@ -15446,10 +15607,10 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -871, -871, -871, -871, -871, -871, -871, -871, -871, + 69, -871, -871, -871, -871, -871, -871, -871, -871, -871, -871, -871, -871, -871, -871, -871, -871, -871, -871, -871, -871, -871, -871, -871, -871, -871, -871, -871, -871, -871, - -871, -871, 872, -871, -871, -871, -871, -871, -871, -871, + -871, -871, 1077, -871, -871, -871, -871, -871, -871, -871, -871, -871, -871, -871, -871, -871, -871, -871, -871, -871, -871, -871, -871, -871, -871, -871, -871, -871, -871, -871, -871, -871, -871, -871, -871, -871, -871, -871, -871, -871, @@ -15463,10 +15624,10 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -872, -872, -872, -872, -872, -872, -872, -872, -872, + 69, -872, -872, -872, -872, -872, -872, -872, -872, -872, -872, -872, -872, -872, -872, -872, -872, -872, -872, -872, -872, -872, -872, -872, -872, -872, -872, -872, -872, -872, - -872, -872, 873, -872, -872, -872, -872, -872, -872, -872, + -872, -872, 1078, -872, -872, -872, -872, -872, -872, -872, -872, -872, -872, -872, -872, -872, -872, -872, -872, -872, -872, -872, -872, -872, -872, -872, -872, -872, -872, -872, @@ -15480,17 +15641,17 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -873, -873, -873, -873, -873, -873, -873, -873, -873, + 69, -873, -873, -873, -873, -873, -873, -873, -873, -873, -873, -873, -873, -873, -873, -873, -873, -873, -873, -873, -873, -873, -873, -873, -873, -873, -873, -873, -873, -873, - -873, -873, 874, -873, -873, -873, -873, -873, -873, -873, - -873, -873, -873, -873, -873, -873, -873, -873, -873, -873, - -873, -873, -873, -873, -873, -873, -873, -873, -873, -873, - -873, -873, -873, -873, -873, -873, -873, -873, -873, -873, - -873, -873, -873, -873, -873, -873, -873, -873, -873, -873, - -873, -873, -873, -873, -873, -873, -873, -873, -873, -873, - -873, -873, -873, -873, -873, -873, -873, -873, -873, -873, + -873, -873, 1079, -873, -873, -873, -873, -873, -873, -873, + -873, -873, -873, -873, -873, -873, -873, -873, 1080, 1080, + 1080, 1080, 1080, 1080, 1080, 1080, 1080, 1080, -873, -873, + -873, -873, -873, -873, -873, 1079, 1079, 1079, 1079, 1079, + 1079, 1079, 1079, 1079, 1079, 1079, 1079, 1079, 1079, 1079, + 1079, 1079, 1079, 1079, 1079, 1079, 1079, 1079, 1079, 1079, + 1079, -873, -873, -873, -873, -873, -873, -873, -873, -873, -873, -873, -873, -873, -873, -873, -873, -873, -873, -873, -873, -873, -873, -873, -873, -873, -873, -873, -873, -873, @@ -15498,10 +15659,10 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -874, -874, -874, -874, -874, -874, -874, -874, -874, + 69, -874, -874, -874, -874, -874, -874, -874, -874, -874, + -874, -874, -874, -874, -874, -874, -874, -874, -874, -874, -874, -874, -874, -874, -874, -874, -874, -874, -874, -874, -874, -874, -874, -874, -874, -874, -874, -874, -874, -874, - -874, -874, 875, -874, -874, -874, -874, -874, -874, -874, -874, -874, -874, -874, -874, -874, -874, -874, -874, -874, -874, -874, -874, -874, -874, -874, -874, -874, -874, -874, -874, -874, -874, -874, -874, -874, -874, -874, -874, -874, @@ -15515,10 +15676,10 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -875, -875, -875, -875, -875, -875, -875, -875, -875, + 69, -875, -875, -875, -875, -875, -875, -875, -875, -875, + -875, -875, -875, -875, -875, -875, -875, -875, -875, -875, -875, -875, -875, -875, -875, -875, -875, -875, -875, -875, -875, -875, -875, -875, -875, -875, -875, -875, -875, -875, - -875, -875, 876, -875, -875, -875, -875, -875, -875, -875, -875, -875, -875, -875, -875, -875, -875, -875, -875, -875, -875, -875, -875, -875, -875, -875, -875, -875, -875, -875, @@ -15532,11 +15693,11 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -876, -876, -876, -876, -876, -876, -876, -876, -876, + 69, -876, -876, -876, -876, -876, -876, -876, -876, -876, -876, -876, -876, -876, -876, -876, -876, -876, -876, -876, -876, -876, -876, -876, -876, -876, -876, -876, -876, -876, - -876, -876, 877, -876, -876, -876, -876, -876, -876, -876, + -876, -876, -876, -876, -876, -876, -876, -876, -876, -876, -876, -876, -876, -876, -876, -876, -876, -876, -876, -876, -876, -876, -876, -876, -876, -876, -876, -876, -876, -876, -876, -876, -876, -876, -876, -876, -876, -876, -876, -876, @@ -15550,10 +15711,10 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -877, -877, -877, -877, -877, -877, -877, -877, -877, + 69, -877, -877, -877, -877, -877, -877, -877, -877, -877, + -877, -877, -877, -877, -877, -877, -877, -877, -877, -877, -877, -877, -877, -877, -877, -877, -877, -877, -877, -877, -877, -877, -877, -877, -877, -877, -877, -877, -877, -877, - -877, -877, 878, -877, -877, -877, -877, -877, -877, -877, -877, -877, -877, -877, -877, -877, -877, -877, -877, -877, -877, -877, -877, -877, -877, -877, -877, -877, -877, -877, -877, -877, -877, -877, -877, -877, -877, -877, -877, -877, @@ -15567,10 +15728,10 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -878, -878, -878, -878, -878, -878, -878, -878, -878, + 69, -878, -878, -878, -878, -878, -878, -878, -878, -878, + -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, - -878, -878, 879, -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, @@ -15584,11 +15745,11 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -879, -879, -879, -879, -879, -879, -879, -879, -879, + 69, -879, -879, -879, -879, -879, -879, -879, -879, -879, -879, -879, -879, -879, -879, -879, -879, -879, -879, -879, -879, -879, -879, -879, -879, -879, -879, -879, -879, -879, - -879, -879, 880, -879, -879, -879, -879, -879, -879, -879, + -879, -879, -879, -879, -879, -879, -879, -879, -879, -879, -879, -879, -879, -879, -879, -879, -879, -879, -879, -879, -879, -879, -879, -879, -879, -879, -879, -879, -879, -879, -879, -879, -879, -879, -879, -879, -879, -879, -879, -879, @@ -15602,10 +15763,10 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -880, -880, -880, -880, -880, -880, -880, -880, -880, + 69, -880, -880, -880, -880, -880, -880, -880, -880, -880, -880, -880, -880, -880, -880, -880, -880, -880, -880, -880, -880, -880, -880, -880, -880, -880, -880, -880, -880, -880, - -880, -880, 881, -880, -880, -880, -880, -880, -880, -880, + -880, -880, 1081, -880, -880, -880, -880, -880, -880, -880, -880, -880, -880, -880, -880, -880, -880, -880, -880, -880, -880, -880, -880, -880, -880, -880, -880, -880, -880, -880, -880, -880, -880, -880, -880, -880, -880, -880, -880, -880, @@ -15619,10 +15780,10 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -881, -881, -881, -881, -881, -881, -881, -881, -881, + 69, -881, -881, -881, -881, -881, -881, -881, -881, -881, -881, -881, -881, -881, -881, -881, -881, -881, -881, -881, -881, -881, -881, -881, -881, -881, -881, -881, -881, -881, - -881, -881, 882, -881, -881, -881, -881, -881, -881, -881, + -881, -881, 1082, -881, -881, -881, -881, -881, -881, -881, -881, -881, -881, -881, -881, -881, -881, -881, -881, -881, -881, -881, -881, -881, -881, -881, -881, -881, -881, -881, -881, -881, -881, -881, -881, -881, -881, -881, -881, -881, @@ -15636,28 +15797,28 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -882, -882, -882, -882, -882, -882, -882, -882, -882, - -882, -882, -882, -882, -882, -882, -882, -882, -882, -882, - -882, -882, -882, -882, -882, -882, -882, -882, -882, -882, - -882, -882, 883, -882, -882, -882, -882, -882, -882, -882, - - -882, -882, -882, -882, -882, -882, -882, -882, -882, -882, - -882, -882, -882, -882, -882, -882, -882, -882, -882, -882, - -882, -882, -882, -882, -882, -882, -882, -882, -882, -882, - -882, -882, -882, -882, -882, -882, -882, -882, -882, -882, + 69, -882, -882, -882, -882, -882, -882, -882, -882, -882, -882, -882, -882, -882, -882, -882, -882, -882, -882, -882, -882, -882, -882, -882, -882, -882, -882, -882, -882, -882, + -882, -882, 1083, -882, -882, -882, -882, -882, -882, -882, + + -882, -882, -882, -882, -882, -882, -882, -882, 1084, 1084, + 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, -882, -882, + -882, -882, -882, -882, -882, 1083, 1083, 1083, 1083, 1083, + 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, + 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, + 1083, -882, -882, -882, -882, -882, -882, -882, -882, -882, -882, -882, -882, -882, -882, -882, -882, -882, -882, -882, -882, -882, -882, -882, -882, -882, -882, -882, -882, -882, -882, -882, -882, -882, -882, -882, -882, -882 }, { - 67, -883, -883, -883, -883, -883, -883, -883, -883, -883, + 69, -883, -883, -883, -883, -883, -883, -883, -883, -883, -883, -883, -883, -883, -883, -883, -883, -883, -883, -883, -883, -883, -883, -883, -883, -883, -883, -883, -883, -883, - -883, -883, 884, -883, -883, -883, -883, -883, -883, -883, + -883, -883, 1085, -883, -883, -883, -883, -883, -883, -883, -883, -883, -883, -883, -883, -883, -883, -883, -883, -883, -883, -883, -883, -883, -883, -883, -883, -883, -883, -883, -883, -883, -883, -883, -883, -883, -883, -883, -883, -883, @@ -15671,47 +15832,47 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -884, -884, -884, -884, -884, -884, -884, -884, -884, - -884, -884, -884, -884, -884, -884, -884, -884, -884, -884, - -884, -884, -884, -884, -884, -884, -884, -884, -884, -884, - -884, -884, 885, -884, -884, -884, -884, -884, -884, -884, - -884, -884, -884, -884, -884, -884, -884, -884, -884, -884, - -884, -884, -884, -884, -884, -884, -884, -884, -884, -884, - -884, -884, -884, -884, -884, -884, -884, -884, -884, -884, - -884, -884, -884, -884, -884, -884, -884, -884, -884, -884, - + 69, -884, -884, -884, -884, -884, -884, -884, -884, -884, -884, -884, -884, -884, -884, -884, -884, -884, -884, -884, -884, -884, -884, -884, -884, -884, -884, -884, -884, -884, + -884, -884, 1086, -884, -884, -884, -884, -884, -884, -884, + -884, -884, -884, -884, -884, -884, -884, -884, 1087, 1087, + 1087, 1087, 1087, 1087, 1087, 1087, 1087, 1087, -884, -884, + -884, -884, -884, -884, -884, 1086, 1086, 1086, 1086, 1086, + 1086, 1086, 1086, 1086, 1086, 1086, 1086, 1086, 1086, 1086, + + 1086, 1086, 1086, 1086, 1086, 1086, 1086, 1086, 1086, 1086, + 1086, -884, -884, -884, -884, -884, -884, -884, -884, -884, -884, -884, -884, -884, -884, -884, -884, -884, -884, -884, -884, -884, -884, -884, -884, -884, -884, -884, -884, -884, -884, -884, -884, -884, -884, -884, -884, -884 }, { - 67, -885, -885, -885, -885, -885, -885, -885, -885, -885, - -885, -885, -885, -885, -885, -885, -885, -885, -885, -885, - -885, -885, -885, -885, -885, -885, -885, -885, -885, -885, - -885, -885, 886, -885, -885, -885, -885, -885, -885, -885, - -885, -885, -885, -885, -885, -885, -885, -885, -885, -885, - - -885, -885, -885, -885, -885, -885, -885, -885, -885, -885, - -885, -885, -885, -885, -885, -885, -885, -885, -885, -885, - -885, -885, -885, -885, -885, -885, -885, -885, -885, -885, + 69, -885, -885, -885, -885, -885, -885, -885, -885, -885, -885, -885, -885, -885, -885, -885, -885, -885, -885, -885, -885, -885, -885, -885, -885, -885, -885, -885, -885, -885, + -885, -885, 1088, -885, -885, -885, -885, -885, -885, -885, + -885, -885, -885, -885, -885, -885, -885, -885, 1089, 1089, + + 1089, 1089, 1089, 1089, 1089, 1089, 1089, 1089, -885, -885, + -885, -885, -885, -885, -885, 1088, 1088, 1088, 1088, 1088, + 1088, 1088, 1088, 1088, 1088, 1088, 1088, 1088, 1088, 1088, + 1088, 1088, 1088, 1088, 1088, 1088, 1088, 1088, 1088, 1088, + 1088, -885, -885, -885, -885, -885, -885, -885, -885, -885, -885, -885, -885, -885, -885, -885, -885, -885, -885, -885, -885, -885, -885, -885, -885, -885, -885, -885, -885, -885, -885, -885, -885, -885, -885, -885, -885, -885 }, { - 67, -886, -886, -886, -886, -886, -886, -886, -886, -886, + 69, -886, -886, -886, -886, -886, -886, -886, -886, -886, -886, -886, -886, -886, -886, -886, -886, -886, -886, -886, - -886, -886, -886, -886, -886, -886, -886, -886, -886, -886, - -886, -886, 887, -886, -886, -886, -886, -886, -886, -886, -886, -886, -886, -886, -886, -886, -886, -886, -886, -886, -886, -886, -886, -886, -886, -886, -886, -886, -886, -886, + -886, -886, -886, -886, -886, -886, -886, -886, 1090, 1090, + 1090, 1090, 1090, 1090, 1090, 1090, 1090, 1090, -886, -886, -886, -886, -886, -886, -886, -886, -886, -886, -886, -886, -886, -886, -886, -886, -886, -886, -886, -886, -886, -886, -886, -886, -886, -886, -886, -886, -886, -886, -886, -886, @@ -15723,12 +15884,12 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -887, -887, -887, -887, -887, -887, -887, -887, -887, + 69, -887, -887, -887, -887, -887, -887, -887, -887, -887, -887, -887, -887, -887, -887, -887, -887, -887, -887, -887, -887, -887, -887, -887, -887, -887, -887, -887, -887, -887, - -887, -887, 888, -887, -887, -887, -887, -887, -887, -887, - -887, -887, -887, -887, -887, -887, -887, -887, -887, -887, -887, -887, -887, -887, -887, -887, -887, -887, -887, -887, + -887, -887, -887, -887, -887, -887, -887, -887, 1091, 1091, + 1091, 1091, 1091, 1091, 1091, 1091, 1091, 1091, -887, -887, -887, -887, -887, -887, -887, -887, -887, -887, -887, -887, -887, -887, -887, -887, -887, -887, -887, -887, -887, -887, -887, -887, -887, -887, -887, -887, -887, -887, -887, -887, @@ -15740,12 +15901,12 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -888, -888, -888, -888, -888, -888, -888, -888, -888, - -888, -888, -888, -888, -888, -888, -888, -888, -888, -888, + 69, -888, -888, -888, -888, -888, -888, -888, -888, -888, -888, -888, -888, -888, -888, -888, -888, -888, -888, -888, - -888, -888, 889, -888, -888, -888, -888, -888, -888, -888, -888, -888, -888, -888, -888, -888, -888, -888, -888, -888, -888, -888, -888, -888, -888, -888, -888, -888, -888, -888, + -888, -888, -888, -888, -888, -888, -888, -888, 1092, 1092, + 1092, 1092, 1092, 1092, 1092, 1092, 1092, 1092, -888, -888, -888, -888, -888, -888, -888, -888, -888, -888, -888, -888, -888, -888, -888, -888, -888, -888, -888, -888, -888, -888, @@ -15757,17 +15918,17 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -889, -889, -889, -889, -889, -889, -889, -889, -889, + 69, -889, -889, -889, -889, -889, -889, -889, -889, -889, -889, -889, -889, -889, -889, -889, -889, -889, -889, -889, -889, -889, -889, -889, -889, -889, -889, -889, -889, -889, - -889, -889, 890, -889, -889, -889, -889, -889, -889, -889, - -889, -889, -889, -889, -889, -889, -889, -889, -889, -889, - -889, -889, -889, -889, -889, -889, -889, -889, -889, -889, - -889, -889, -889, -889, -889, -889, -889, -889, -889, -889, - -889, -889, -889, -889, -889, -889, -889, -889, -889, -889, - -889, -889, -889, -889, -889, -889, -889, -889, -889, -889, - -889, -889, -889, -889, -889, -889, -889, -889, -889, -889, + -889, -889, 1093, -889, -889, -889, -889, -889, -889, -889, + -889, -889, -889, -889, -889, -889, -889, -889, 1094, 1094, + 1094, 1094, 1094, 1094, 1094, 1094, 1094, 1094, -889, -889, + -889, -889, -889, -889, -889, 1093, 1093, 1093, 1093, 1093, + 1093, 1093, 1093, 1093, 1093, 1093, 1093, 1093, 1093, 1093, + 1093, 1093, 1093, 1093, 1093, 1093, 1093, 1093, 1093, 1093, + 1093, -889, -889, -889, -889, -889, -889, -889, -889, -889, -889, -889, -889, -889, -889, -889, -889, -889, -889, -889, -889, -889, -889, -889, -889, -889, -889, -889, -889, -889, -889, -889, -889, -889, -889, -889, -889, -889 @@ -15775,12 +15936,12 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -890, -890, -890, -890, -890, -890, -890, -890, -890, + 69, -890, -890, -890, -890, -890, -890, -890, -890, -890, -890, -890, -890, -890, -890, -890, -890, -890, -890, -890, -890, -890, -890, -890, -890, -890, -890, -890, -890, -890, - -890, -890, 891, -890, -890, -890, -890, -890, -890, -890, - -890, -890, -890, -890, -890, -890, -890, -890, -890, -890, -890, -890, -890, -890, -890, -890, -890, -890, -890, -890, + -890, -890, -890, -890, -890, -890, -890, -890, 1095, 1095, + 1095, 1095, 1095, 1095, 1095, 1095, 1095, 1095, -890, -890, -890, -890, -890, -890, -890, -890, -890, -890, -890, -890, -890, -890, -890, -890, -890, -890, -890, -890, -890, -890, -890, -890, -890, -890, -890, -890, -890, -890, -890, -890, @@ -15792,45 +15953,45 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -891, -891, -891, -891, -891, -891, -891, -891, -891, - -891, -891, -891, -891, -891, -891, -891, -891, -891, -891, - -891, -891, -891, -891, -891, -891, -891, -891, -891, -891, - -891, -891, 892, -891, -891, -891, -891, -891, -891, -891, - -891, -891, -891, -891, -891, -891, -891, -891, -891, -891, - -891, -891, -891, -891, -891, -891, -891, -891, -891, -891, - -891, -891, -891, -891, -891, -891, -891, -891, -891, -891, - - -891, -891, -891, -891, -891, -891, -891, -891, -891, -891, + 69, -891, -891, -891, -891, -891, -891, -891, -891, -891, -891, -891, -891, -891, -891, -891, -891, -891, -891, -891, -891, -891, -891, -891, -891, -891, -891, -891, -891, -891, + -891, -891, 1096, -891, -891, -891, -891, -891, -891, -891, + -891, -891, -891, -891, -891, -891, -891, -891, 1097, 1097, + 1097, 1097, 1097, 1097, 1097, 1097, 1097, 1097, -891, -891, + -891, -891, -891, -891, -891, 1096, 1096, 1096, 1096, 1096, + + 1096, 1096, 1096, 1096, 1096, 1096, 1096, 1096, 1096, 1096, + 1096, 1096, 1096, 1096, 1096, 1096, 1096, 1096, 1096, 1096, + 1096, -891, -891, -891, -891, -891, -891, -891, -891, -891, -891, -891, -891, -891, -891, -891, -891, -891, -891, -891, -891, -891, -891, -891, -891, -891, -891, -891, -891, -891, -891, -891, -891, -891, -891, -891, -891, -891 }, { - 67, -892, -892, -892, -892, -892, -892, -892, -892, -892, - -892, -892, -892, -892, -892, -892, -892, -892, -892, -892, - -892, -892, -892, -892, -892, -892, -892, -892, -892, -892, - -892, -892, 893, -892, -892, -892, -892, -892, -892, -892, - - -892, -892, -892, -892, -892, -892, -892, -892, -892, -892, - -892, -892, -892, -892, -892, -892, -892, -892, -892, -892, - -892, -892, -892, -892, -892, -892, -892, -892, -892, -892, - -892, -892, -892, -892, -892, -892, -892, -892, -892, -892, + 69, -892, -892, -892, -892, -892, -892, -892, -892, -892, -892, -892, -892, -892, -892, -892, -892, -892, -892, -892, -892, -892, -892, -892, -892, -892, -892, -892, -892, -892, + -892, -892, 1096, -892, -892, -892, -892, -892, -892, -892, + + -892, -892, -892, -892, -892, -892, -892, -892, 1098, 1098, + 1098, 1098, 1098, 1098, 1098, 1098, 1098, 1098, -892, -892, + -892, -892, -892, -892, -892, 1096, 1096, 1096, 1096, 1096, + 1096, 1096, 1096, 1096, 1096, 1096, 1096, 1096, 1096, 1096, + 1096, 1096, 1096, 1096, 1096, 1096, 1096, 1096, 1096, 1096, + 1096, -892, -892, -892, -892, -892, -892, -892, -892, -892, -892, -892, -892, -892, -892, -892, -892, -892, -892, -892, -892, -892, -892, -892, -892, -892, -892, -892, -892, -892, -892, -892, -892, -892, -892, -892, -892, -892 }, { - 67, -893, -893, -893, -893, -893, -893, -893, -893, -893, + 69, -893, -893, -893, -893, -893, -893, -893, -893, -893, -893, -893, -893, -893, -893, -893, -893, -893, -893, -893, -893, -893, -893, -893, -893, -893, -893, -893, -893, -893, - -893, -893, 894, -893, -893, -893, -893, -893, -893, -893, + -893, -893, 1099, -893, -893, -893, -893, -893, -893, -893, -893, -893, -893, -893, -893, -893, -893, -893, -893, -893, -893, -893, -893, -893, -893, -893, -893, -893, -893, -893, -893, -893, -893, -893, -893, -893, -893, -893, -893, -893, @@ -15844,51 +16005,51 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -894, -894, -894, -894, -894, -894, -894, -894, -894, - -894, -894, -894, -894, -894, -894, -894, -894, -894, -894, - -894, -894, -894, -894, -894, -894, -894, -894, -894, -894, - -894, -894, 895, -894, -894, -894, -894, -894, -894, -894, - -894, -894, -894, -894, -894, -894, -894, -894, -894, -894, - -894, -894, -894, -894, -894, -894, -894, -894, -894, -894, - -894, -894, -894, -894, -894, -894, -894, -894, -894, -894, - -894, -894, -894, -894, -894, -894, -894, -894, -894, -894, - + 69, -894, -894, -894, -894, -894, -894, -894, -894, -894, -894, -894, -894, -894, -894, -894, -894, -894, -894, -894, -894, -894, -894, -894, -894, -894, -894, -894, -894, -894, + -894, -894, 1100, -894, -894, -894, -894, -894, -894, -894, + -894, -894, -894, -894, -894, -894, -894, -894, 1101, 1101, + 1101, 1101, 1101, 1101, 1101, 1101, 1101, 1101, -894, -894, + -894, -894, -894, -894, -894, 1100, 1100, 1100, 1100, 1100, + 1100, 1100, 1100, 1100, 1100, 1100, 1100, 1100, 1100, 1100, + + 1100, 1100, 1100, 1100, 1100, 1100, 1100, 1100, 1100, 1100, + 1100, -894, -894, -894, -894, -894, -894, -894, -894, -894, -894, -894, -894, -894, -894, -894, -894, -894, -894, -894, -894, -894, -894, -894, -894, -894, -894, -894, -894, -894, -894, -894, -894, -894, -894, -894, -894, -894 }, { - 67, -895, -895, -895, -895, -895, -895, -895, -895, -895, - -895, -895, -895, -895, -895, -895, -895, -895, -895, -895, - -895, -895, -895, -895, -895, -895, -895, -895, -895, -895, - -895, -895, 896, -895, -895, -895, -895, -895, -895, -895, - -895, -895, -895, -895, -895, -895, -895, -895, -895, -895, - - -895, -895, -895, -895, -895, -895, -895, -895, -895, -895, - -895, -895, -895, -895, -895, -895, -895, -895, -895, -895, - -895, -895, -895, -895, -895, -895, -895, -895, -895, -895, + 69, -895, -895, -895, -895, -895, -895, -895, -895, -895, -895, -895, -895, -895, -895, -895, -895, -895, -895, -895, -895, -895, -895, -895, -895, -895, -895, -895, -895, -895, + -895, -895, 1102, -895, -895, -895, -895, -895, -895, -895, + -895, -895, -895, -895, -895, -895, -895, -895, 1103, 1103, + + 1103, 1103, 1103, 1103, 1103, 1103, 1103, 1103, -895, -895, + -895, -895, -895, -895, -895, 1102, 1102, 1102, 1102, 1102, + 1102, 1102, 1102, 1102, 1102, 1102, 1102, 1102, 1102, 1102, + 1102, 1102, 1102, 1102, 1102, 1102, 1102, 1102, 1102, 1102, + 1102, -895, -895, -895, -895, -895, -895, -895, -895, -895, -895, -895, -895, -895, -895, -895, -895, -895, -895, -895, -895, -895, -895, -895, -895, -895, -895, -895, -895, -895, -895, -895, -895, -895, -895, -895, -895, -895 }, { - 67, -896, -896, -896, -896, -896, -896, -896, -896, -896, + 69, -896, -896, -896, -896, -896, -896, -896, -896, -896, -896, -896, -896, -896, -896, -896, -896, -896, -896, -896, -896, -896, -896, -896, -896, -896, -896, -896, -896, -896, - -896, -896, 897, -896, -896, -896, -896, -896, -896, -896, - -896, -896, -896, -896, -896, -896, -896, -896, -896, -896, - -896, -896, -896, -896, -896, -896, -896, -896, -896, -896, - -896, -896, -896, -896, -896, -896, -896, -896, -896, -896, - -896, -896, -896, -896, -896, -896, -896, -896, -896, -896, - -896, -896, -896, -896, -896, -896, -896, -896, -896, -896, - -896, -896, -896, -896, -896, -896, -896, -896, -896, -896, + -896, -896, 1102, -896, -896, -896, -896, -896, -896, -896, + -896, -896, -896, -896, -896, -896, -896, -896, 1104, 1104, + 1104, 1104, 1104, 1104, 1104, 1104, 1104, 1104, -896, -896, + -896, -896, -896, -896, -896, 1102, 1102, 1102, 1102, 1102, + 1102, 1102, 1102, 1102, 1102, 1102, 1102, 1102, 1102, 1102, + 1102, 1102, 1102, 1102, 1102, 1102, 1102, 1102, 1102, 1102, + 1102, -896, -896, -896, -896, -896, -896, -896, -896, -896, -896, -896, -896, -896, -896, -896, -896, -896, -896, -896, -896, -896, -896, -896, -896, -896, -896, -896, -896, -896, @@ -15896,10 +16057,10 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -897, -897, -897, -897, -897, -897, -897, -897, -897, + 69, -897, -897, -897, -897, -897, -897, -897, -897, -897, -897, -897, -897, -897, -897, -897, -897, -897, -897, -897, -897, -897, -897, -897, -897, -897, -897, -897, -897, -897, - -897, -897, 898, -897, -897, -897, -897, -897, -897, -897, + -897, -897, 1105, -897, -897, -897, -897, -897, -897, -897, -897, -897, -897, -897, -897, -897, -897, -897, -897, -897, -897, -897, -897, -897, -897, -897, -897, -897, -897, -897, -897, -897, -897, -897, -897, -897, -897, -897, -897, -897, @@ -15913,34 +16074,34 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -898, -898, -898, -898, -898, -898, -898, -898, -898, - -898, -898, -898, -898, -898, -898, -898, -898, -898, -898, - -898, -898, -898, -898, -898, -898, -898, -898, -898, -898, - -898, -898, 899, -898, -898, -898, -898, -898, -898, -898, - -898, -898, -898, -898, -898, -898, -898, -898, -898, -898, - -898, -898, -898, -898, -898, -898, -898, -898, -898, -898, - - -898, -898, -898, -898, -898, -898, -898, -898, -898, -898, - -898, -898, -898, -898, -898, -898, -898, -898, -898, -898, + 69, -898, -898, -898, -898, -898, -898, -898, -898, -898, -898, -898, -898, -898, -898, -898, -898, -898, -898, -898, -898, -898, -898, -898, -898, -898, -898, -898, -898, -898, + -898, -898, 1106, -898, -898, -898, -898, -898, -898, -898, + -898, -898, -898, -898, -898, -898, -898, -898, 1107, 1107, + 1107, 1107, 1107, 1107, 1107, 1107, 1107, 1107, -898, -898, + + -898, -898, -898, -898, -898, 1106, 1106, 1106, 1106, 1106, + 1106, 1106, 1106, 1106, 1106, 1106, 1106, 1106, 1106, 1106, + 1106, 1106, 1106, 1106, 1106, 1106, 1106, 1106, 1106, 1106, + 1106, -898, -898, -898, -898, -898, -898, -898, -898, -898, -898, -898, -898, -898, -898, -898, -898, -898, -898, -898, -898, -898, -898, -898, -898, -898, -898, -898, -898, -898, -898, -898, -898, -898, -898, -898, -898, -898 }, { - 67, -899, -899, -899, -899, -899, -899, -899, -899, -899, + 69, -899, -899, -899, -899, -899, -899, -899, -899, -899, -899, -899, -899, -899, -899, -899, -899, -899, -899, -899, -899, -899, -899, -899, -899, -899, -899, -899, -899, -899, - -899, -899, 900, -899, -899, -899, -899, -899, -899, -899, - -899, -899, -899, -899, -899, -899, -899, -899, -899, -899, - -899, -899, -899, -899, -899, -899, -899, -899, -899, -899, - -899, -899, -899, -899, -899, -899, -899, -899, -899, -899, - -899, -899, -899, -899, -899, -899, -899, -899, -899, -899, - -899, -899, -899, -899, -899, -899, -899, -899, -899, -899, - -899, -899, -899, -899, -899, -899, -899, -899, -899, -899, + -899, -899, 1106, -899, -899, -899, -899, -899, -899, -899, + -899, -899, -899, -899, -899, -899, -899, -899, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, -899, -899, + -899, -899, -899, -899, -899, 1106, 1106, 1106, 1106, 1106, + 1106, 1106, 1106, 1106, 1106, 1106, 1106, 1106, 1106, 1106, + 1106, 1106, 1106, 1106, 1106, 1106, 1106, 1106, 1106, 1106, + 1106, -899, -899, -899, -899, -899, -899, -899, -899, -899, -899, -899, -899, -899, -899, -899, -899, -899, -899, -899, -899, -899, -899, -899, -899, -899, -899, -899, -899, -899, -899, -899, -899, -899, -899, -899, -899, -899 @@ -15948,16 +16109,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -900, -900, -900, -900, -900, -900, -900, -900, -900, - -900, -900, -900, -900, -900, -900, -900, -900, -900, -900, - -900, -900, -900, -900, -900, -900, -900, -900, -900, -900, - -900, -900, 901, -900, -900, -900, -900, -900, -900, -900, - -900, -900, -900, -900, -900, -900, -900, -900, -900, -900, - -900, -900, -900, -900, -900, -900, -900, -900, -900, -900, - -900, -900, -900, -900, -900, -900, -900, -900, -900, -900, - -900, -900, -900, -900, -900, -900, -900, -900, -900, -900, + 69, -900, -900, -900, -900, -900, -900, -900, -900, -900, -900, -900, -900, -900, -900, -900, -900, -900, -900, -900, -900, -900, -900, -900, -900, -900, -900, -900, -900, -900, + -900, -900, 1109, -900, -900, -900, -900, -900, -900, -900, + -900, -900, -900, -900, -900, -900, -900, -900, 1101, 1101, + 1101, 1101, 1101, 1101, 1101, 1101, 1101, 1101, -900, -900, + -900, -900, -900, -900, -900, 1109, 1109, 1109, 1109, 1109, + 1109, 1109, 1109, 1109, 1109, 1109, 1109, 1109, 1109, 1109, + 1109, 1109, 1109, 1109, 1109, 1109, 1109, 1109, 1109, 1109, + 1109, -900, -900, -900, -900, -900, -900, -900, -900, -900, -900, -900, -900, -900, -900, -900, -900, -900, -900, -900, -900, -900, -900, -900, -900, -900, -900, -900, -900, -900, @@ -15965,10 +16126,10 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -901, -901, -901, -901, -901, -901, -901, -901, -901, + 69, -901, -901, -901, -901, -901, -901, -901, -901, -901, -901, -901, -901, -901, -901, -901, -901, -901, -901, -901, -901, -901, -901, -901, -901, -901, -901, -901, -901, -901, - -901, -901, 902, -901, -901, -901, -901, -901, -901, -901, + -901, -901, 1110, -901, -901, -901, -901, -901, -901, -901, -901, -901, -901, -901, -901, -901, -901, -901, -901, -901, -901, -901, -901, -901, -901, -901, -901, -901, -901, -901, -901, -901, -901, -901, -901, -901, -901, -901, -901, -901, @@ -15982,10 +16143,10 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -902, -902, -902, -902, -902, -902, -902, -902, -902, + 69, -902, -902, -902, -902, -902, -902, -902, -902, -902, -902, -902, -902, -902, -902, -902, -902, -902, -902, -902, -902, -902, -902, -902, -902, -902, -902, -902, -902, -902, - -902, -902, 903, -902, -902, -902, -902, -902, -902, -902, + -902, -902, 1111, -902, -902, -902, -902, -902, -902, -902, -902, -902, -902, -902, -902, -902, -902, -902, -902, -902, -902, -902, -902, -902, -902, -902, -902, -902, -902, -902, @@ -15999,17 +16160,17 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -903, -903, -903, -903, -903, -903, -903, -903, -903, + 69, -903, -903, -903, -903, -903, -903, -903, -903, -903, -903, -903, -903, -903, -903, -903, -903, -903, -903, -903, -903, -903, -903, -903, -903, -903, -903, -903, -903, -903, - -903, -903, 904, -903, -903, -903, -903, -903, -903, -903, - -903, -903, -903, -903, -903, -903, -903, -903, -903, -903, - -903, -903, -903, -903, -903, -903, -903, -903, -903, -903, - -903, -903, -903, -903, -903, -903, -903, -903, -903, -903, - -903, -903, -903, -903, -903, -903, -903, -903, -903, -903, - -903, -903, -903, -903, -903, -903, -903, -903, -903, -903, - -903, -903, -903, -903, -903, -903, -903, -903, -903, -903, + -903, -903, 1112, -903, -903, -903, -903, -903, -903, -903, + -903, -903, -903, -903, -903, -903, -903, -903, 1113, 1113, + 1113, 1113, 1113, 1113, 1113, 1113, 1113, 1113, -903, -903, + -903, -903, -903, -903, -903, 1112, 1112, 1112, 1112, 1112, + 1112, 1112, 1112, 1112, 1112, 1112, 1112, 1112, 1112, 1112, + 1112, 1112, 1112, 1112, 1112, 1112, 1112, 1112, 1112, 1112, + 1112, -903, -903, -903, -903, -903, -903, -903, -903, -903, -903, -903, -903, -903, -903, -903, -903, -903, -903, -903, -903, -903, -903, -903, -903, -903, -903, -903, -903, -903, @@ -16017,10 +16178,10 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -904, -904, -904, -904, -904, -904, -904, -904, -904, + 69, -904, -904, -904, -904, -904, -904, -904, -904, -904, -904, -904, -904, -904, -904, -904, -904, -904, -904, -904, -904, -904, -904, -904, -904, -904, -904, -904, -904, -904, - -904, -904, 905, -904, -904, -904, -904, -904, -904, -904, + -904, -904, 1114, -904, -904, -904, -904, -904, -904, -904, -904, -904, -904, -904, -904, -904, -904, -904, -904, -904, -904, -904, -904, -904, -904, -904, -904, -904, -904, -904, -904, -904, -904, -904, -904, -904, -904, -904, -904, -904, @@ -16034,34 +16195,34 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -905, -905, -905, -905, -905, -905, -905, -905, -905, - -905, -905, -905, -905, -905, -905, -905, -905, -905, -905, - -905, -905, -905, -905, -905, -905, -905, -905, -905, -905, - -905, -905, 906, -905, -905, -905, -905, -905, -905, -905, - -905, -905, -905, -905, -905, -905, -905, -905, -905, -905, - - -905, -905, -905, -905, -905, -905, -905, -905, -905, -905, - -905, -905, -905, -905, -905, -905, -905, -905, -905, -905, - -905, -905, -905, -905, -905, -905, -905, -905, -905, -905, + 69, -905, -905, -905, -905, -905, -905, -905, -905, -905, -905, -905, -905, -905, -905, -905, -905, -905, -905, -905, -905, -905, -905, -905, -905, -905, -905, -905, -905, -905, + -905, -905, 1115, -905, -905, -905, -905, -905, -905, -905, + -905, -905, -905, -905, -905, -905, -905, -905, 1116, 1116, + + 1116, 1116, 1116, 1116, 1116, 1116, 1116, 1116, -905, -905, + -905, -905, -905, -905, -905, 1115, 1115, 1115, 1115, 1115, + 1115, 1115, 1115, 1115, 1115, 1115, 1115, 1115, 1115, 1115, + 1115, 1115, 1115, 1115, 1115, 1115, 1115, 1115, 1115, 1115, + 1115, -905, -905, -905, -905, -905, -905, -905, -905, -905, -905, -905, -905, -905, -905, -905, -905, -905, -905, -905, -905, -905, -905, -905, -905, -905, -905, -905, -905, -905, -905, -905, -905, -905, -905, -905, -905, -905 }, { - 67, -906, -906, -906, -906, -906, -906, -906, -906, -906, + 69, -906, -906, -906, -906, -906, -906, -906, -906, -906, -906, -906, -906, -906, -906, -906, -906, -906, -906, -906, -906, -906, -906, -906, -906, -906, -906, -906, -906, -906, - -906, -906, 907, -906, -906, -906, -906, -906, -906, -906, - -906, -906, -906, -906, -906, -906, -906, -906, -906, -906, - -906, -906, -906, -906, -906, -906, -906, -906, -906, -906, - -906, -906, -906, -906, -906, -906, -906, -906, -906, -906, - -906, -906, -906, -906, -906, -906, -906, -906, -906, -906, - -906, -906, -906, -906, -906, -906, -906, -906, -906, -906, - -906, -906, -906, -906, -906, -906, -906, -906, -906, -906, + -906, -906, 1115, -906, -906, -906, -906, -906, -906, -906, + -906, -906, -906, -906, -906, -906, -906, -906, 1117, 1117, + 1117, 1117, 1117, 1117, 1117, 1117, 1117, 1117, -906, -906, + -906, -906, -906, -906, -906, 1115, 1115, 1115, 1115, 1115, + 1115, 1115, 1115, 1115, 1115, 1115, 1115, 1115, 1115, 1115, + 1115, 1115, 1115, 1115, 1115, 1115, 1115, 1115, 1115, 1115, + 1115, -906, -906, -906, -906, -906, -906, -906, -906, -906, -906, -906, -906, -906, -906, -906, -906, -906, -906, -906, -906, -906, -906, -906, -906, -906, -906, -906, -906, -906, @@ -16069,27 +16230,27 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -907, -907, -907, -907, -907, -907, -907, -907, -907, - -907, -907, -907, -907, -907, -907, -907, -907, -907, -907, - -907, -907, -907, -907, -907, -907, -907, -907, -907, -907, - -907, -907, 908, -907, -907, -907, -907, -907, -907, -907, - -907, -907, -907, -907, -907, -907, -907, -907, -907, -907, + 69, -907, -907, -907, -907, -907, -907, -907, -907, -907, -907, -907, -907, -907, -907, -907, -907, -907, -907, -907, -907, -907, -907, -907, -907, -907, -907, -907, -907, -907, - -907, -907, -907, -907, -907, -907, -907, -907, -907, -907, - -907, -907, -907, -907, -907, -907, -907, -907, -907, -907, - - -907, -907, -907, -907, -907, -907, -907, -907, -907, -907, + -907, -907, 1118, -907, -907, -907, -907, -907, -907, -907, + -907, -907, -907, -907, -907, -907, -907, -907, 1101, 1101, + 1101, 1101, 1101, 1101, 1101, 1101, 1101, 1101, -907, -907, + -907, -907, -907, -907, -907, 1118, 1118, 1118, 1118, 1118, + 1118, 1118, 1118, 1118, 1118, 1118, 1118, 1118, 1118, 1118, + 1118, 1118, 1118, 1118, 1118, 1118, 1118, 1118, 1118, 1118, + + 1118, -907, -907, -907, -907, -907, -907, -907, -907, -907, -907, -907, -907, -907, -907, -907, -907, -907, -907, -907, -907, -907, -907, -907, -907, -907, -907, -907, -907, -907, -907, -907, -907, -907, -907, -907, -907, -907 }, { - 67, -908, -908, -908, -908, -908, -908, -908, -908, -908, + 69, -908, -908, -908, -908, -908, -908, -908, -908, -908, -908, -908, -908, -908, -908, -908, -908, -908, -908, -908, -908, -908, -908, -908, -908, -908, -908, -908, -908, -908, - -908, -908, 909, -908, -908, -908, -908, -908, -908, -908, + -908, -908, 1119, -908, -908, -908, -908, -908, -908, -908, -908, -908, -908, -908, -908, -908, -908, -908, -908, -908, -908, -908, -908, -908, -908, -908, -908, -908, -908, -908, @@ -16103,11 +16264,11 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -909, -909, -909, -909, -909, -909, -909, -909, -909, + 69, -909, -909, -909, -909, -909, -909, -909, -909, -909, -909, -909, -909, -909, -909, -909, -909, -909, -909, -909, -909, -909, -909, -909, -909, -909, -909, -909, -909, -909, - -909, -909, 910, -909, -909, -909, -909, -909, -909, -909, + -909, -909, 1120, -909, -909, -909, -909, -909, -909, -909, -909, -909, -909, -909, -909, -909, -909, -909, -909, -909, -909, -909, -909, -909, -909, -909, -909, -909, -909, -909, -909, -909, -909, -909, -909, -909, -909, -909, -909, -909, @@ -16121,16 +16282,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -910, -910, -910, -910, -910, -910, -910, -910, -910, - -910, -910, -910, -910, -910, -910, -910, -910, -910, -910, - -910, -910, -910, -910, -910, -910, -910, -910, -910, -910, - -910, -910, 911, -910, -910, -910, -910, -910, -910, -910, - -910, -910, -910, -910, -910, -910, -910, -910, -910, -910, - -910, -910, -910, -910, -910, -910, -910, -910, -910, -910, - -910, -910, -910, -910, -910, -910, -910, -910, -910, -910, - -910, -910, -910, -910, -910, -910, -910, -910, -910, -910, + 69, -910, -910, -910, -910, -910, -910, -910, -910, -910, -910, -910, -910, -910, -910, -910, -910, -910, -910, -910, -910, -910, -910, -910, -910, -910, -910, -910, -910, -910, + -910, -910, 1121, -910, -910, -910, -910, -910, -910, -910, + -910, -910, -910, -910, -910, -910, -910, -910, 1122, 1122, + 1122, 1122, 1122, 1122, 1122, 1122, 1122, 1122, -910, -910, + -910, -910, -910, -910, -910, 1121, 1121, 1121, 1121, 1121, + 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, + 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, + 1121, -910, -910, -910, -910, -910, -910, -910, -910, -910, -910, -910, -910, -910, -910, -910, -910, -910, -910, -910, -910, -910, -910, -910, -910, -910, -910, -910, -910, -910, @@ -16138,45 +16299,45 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -911, -911, -911, -911, -911, -911, -911, -911, -911, - -911, -911, -911, -911, -911, -911, -911, -911, -911, -911, - -911, -911, -911, -911, -911, -911, -911, -911, -911, -911, - -911, -911, 912, -911, -911, -911, -911, -911, -911, -911, - -911, -911, -911, -911, -911, -911, -911, -911, -911, -911, - -911, -911, -911, -911, -911, -911, -911, -911, -911, -911, - -911, -911, -911, -911, -911, -911, -911, -911, -911, -911, - - -911, -911, -911, -911, -911, -911, -911, -911, -911, -911, + 69, -911, -911, -911, -911, -911, -911, -911, -911, -911, -911, -911, -911, -911, -911, -911, -911, -911, -911, -911, -911, -911, -911, -911, -911, -911, -911, -911, -911, -911, + -911, -911, 1121, -911, -911, -911, -911, -911, -911, -911, + -911, -911, -911, -911, -911, -911, -911, -911, 1123, 1123, + 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, -911, -911, + -911, -911, -911, -911, -911, 1121, 1121, 1121, 1121, 1121, + + 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, + 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, + 1121, -911, -911, -911, -911, -911, -911, -911, -911, -911, -911, -911, -911, -911, -911, -911, -911, -911, -911, -911, -911, -911, -911, -911, -911, -911, -911, -911, -911, -911, -911, -911, -911, -911, -911, -911, -911, -911 }, { - 67, -912, -912, -912, -912, -912, -912, -912, -912, -912, - -912, -912, -912, -912, -912, -912, -912, -912, -912, -912, - -912, -912, -912, -912, -912, -912, -912, -912, -912, -912, - -912, -912, 913, -912, -912, -912, -912, -912, -912, -912, - - -912, -912, -912, -912, -912, -912, -912, -912, -912, -912, - -912, -912, -912, -912, -912, -912, -912, -912, -912, -912, - -912, -912, -912, -912, -912, -912, -912, -912, -912, -912, - -912, -912, -912, -912, -912, -912, -912, -912, -912, -912, + 69, -912, -912, -912, -912, -912, -912, -912, -912, -912, -912, -912, -912, -912, -912, -912, -912, -912, -912, -912, -912, -912, -912, -912, -912, -912, -912, -912, -912, -912, + -912, -912, 1124, -912, -912, -912, -912, -912, -912, -912, + + -912, -912, -912, -912, -912, -912, -912, -912, 1113, 1113, + 1113, 1113, 1113, 1113, 1113, 1113, 1113, 1113, -912, -912, + -912, -912, -912, -912, -912, 1124, 1124, 1124, 1124, 1124, + 1124, 1124, 1124, 1124, 1124, 1124, 1124, 1124, 1124, 1124, + 1124, 1124, 1124, 1124, 1124, 1124, 1124, 1124, 1124, 1124, + 1124, -912, -912, -912, -912, -912, -912, -912, -912, -912, -912, -912, -912, -912, -912, -912, -912, -912, -912, -912, -912, -912, -912, -912, -912, -912, -912, -912, -912, -912, -912, -912, -912, -912, -912, -912, -912, -912 }, { - 67, -913, -913, -913, -913, -913, -913, -913, -913, -913, + 69, -913, -913, -913, -913, -913, -913, -913, -913, -913, -913, -913, -913, -913, -913, -913, -913, -913, -913, -913, -913, -913, -913, -913, -913, -913, -913, -913, -913, -913, - -913, -913, 914, -913, -913, -913, -913, -913, -913, -913, + -913, -913, 1125, -913, -913, -913, -913, -913, -913, -913, -913, -913, -913, -913, -913, -913, -913, -913, -913, -913, -913, -913, -913, -913, -913, -913, -913, -913, -913, -913, -913, -913, -913, -913, -913, -913, -913, -913, -913, -913, @@ -16190,12 +16351,12 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -914, -914, -914, -914, -914, -914, -914, -914, -914, + 69, -914, -914, -914, -914, -914, -914, -914, -914, -914, -914, -914, -914, -914, -914, -914, -914, -914, -914, -914, -914, -914, -914, -914, -914, -914, -914, -914, -914, -914, - -914, -914, 915, -914, -914, -914, -914, -914, -914, -914, - -914, -914, -914, -914, -914, -914, -914, -914, -914, -914, -914, -914, -914, -914, -914, -914, -914, -914, -914, -914, + -914, -914, -914, -914, -914, -914, -914, -914, 1126, 1126, + 1126, 1126, 1126, 1126, 1126, 1126, 1126, 1126, -914, -914, -914, -914, -914, -914, -914, -914, -914, -914, -914, -914, -914, -914, -914, -914, -914, -914, -914, -914, -914, -914, @@ -16207,34 +16368,34 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -915, -915, -915, -915, -915, -915, -915, -915, -915, + 69, -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, - -915, -915, 916, -915, -915, -915, -915, -915, -915, -915, + -915, -915, 1127, -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, - -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, - -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, - -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, - -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, + -915, -915, -915, -915, -915, 1127, 1127, 1127, 1127, 1127, + 1127, 1127, 1127, 1127, 1127, 1127, 1127, 1127, 1127, 1127, + 1127, 1127, 1127, 1127, 1127, 1127, 1127, 1127, 1127, 1127, + 1127, -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, -915 }, { - 67, -916, -916, -916, -916, -916, -916, -916, -916, -916, + 69, -916, -916, -916, -916, -916, -916, -916, -916, -916, -916, -916, -916, -916, -916, -916, -916, -916, -916, -916, -916, -916, -916, -916, -916, -916, -916, -916, -916, -916, - -916, -916, 917, -916, -916, -916, -916, -916, -916, -916, - -916, -916, -916, -916, -916, -916, -916, -916, -916, -916, - -916, -916, -916, -916, -916, -916, -916, -916, -916, -916, - -916, -916, -916, -916, -916, -916, -916, -916, -916, -916, - -916, -916, -916, -916, -916, -916, -916, -916, -916, -916, - -916, -916, -916, -916, -916, -916, -916, -916, -916, -916, - -916, -916, -916, -916, -916, -916, -916, -916, -916, -916, + -916, -916, 1127, -916, -916, -916, -916, -916, -916, -916, + -916, -916, -916, -916, -916, -916, -916, -916, 1128, 1128, + 1128, 1128, 1128, 1128, 1128, 1128, 1128, 1128, -916, -916, + -916, -916, -916, -916, -916, 1127, 1127, 1127, 1127, 1127, + 1127, 1127, 1127, 1127, 1127, 1127, 1127, 1127, 1127, 1127, + 1127, 1127, 1127, 1127, 1127, 1127, 1127, 1127, 1127, 1127, + 1127, -916, -916, -916, -916, -916, -916, -916, -916, -916, -916, -916, -916, -916, -916, -916, -916, -916, -916, -916, -916, -916, -916, -916, -916, -916, -916, -916, -916, -916, @@ -16242,10 +16403,10 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -917, -917, -917, -917, -917, -917, -917, -917, -917, + 69, -917, -917, -917, -917, -917, -917, -917, -917, -917, -917, -917, -917, -917, -917, -917, -917, -917, -917, -917, -917, -917, -917, -917, -917, -917, -917, -917, -917, -917, - -917, -917, 918, -917, -917, -917, -917, -917, -917, -917, + -917, -917, 1129, -917, -917, -917, -917, -917, -917, -917, -917, -917, -917, -917, -917, -917, -917, -917, -917, -917, -917, -917, -917, -917, -917, -917, -917, -917, -917, -917, -917, -917, -917, -917, -917, -917, -917, -917, -917, -917, @@ -16259,28 +16420,28 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -918, -918, -918, -918, -918, -918, -918, -918, -918, - -918, -918, -918, -918, -918, -918, -918, -918, -918, -918, - -918, -918, -918, -918, -918, -918, -918, -918, -918, -918, - -918, -918, 919, -918, -918, -918, -918, -918, -918, -918, - -918, -918, -918, -918, -918, -918, -918, -918, -918, -918, - -918, -918, -918, -918, -918, -918, -918, -918, -918, -918, - - -918, -918, -918, -918, -918, -918, -918, -918, -918, -918, - -918, -918, -918, -918, -918, -918, -918, -918, -918, -918, + 69, -918, -918, -918, -918, -918, -918, -918, -918, -918, -918, -918, -918, -918, -918, -918, -918, -918, -918, -918, -918, -918, -918, -918, -918, -918, -918, -918, -918, -918, + -918, -918, 1130, -918, -918, -918, -918, -918, -918, -918, + -918, -918, -918, -918, -918, -918, -918, -918, 1131, 1131, + 1131, 1131, 1131, 1131, 1131, 1131, 1131, 1131, -918, -918, + + -918, -918, -918, -918, -918, 1130, 1130, 1130, 1130, 1130, + 1130, 1130, 1130, 1130, 1130, 1130, 1130, 1130, 1130, 1130, + 1130, 1130, 1130, 1130, 1130, 1130, 1130, 1130, 1130, 1130, + 1130, -918, -918, -918, -918, -918, -918, -918, -918, -918, -918, -918, -918, -918, -918, -918, -918, -918, -918, -918, -918, -918, -918, -918, -918, -918, -918, -918, -918, -918, -918, -918, -918, -918, -918, -918, -918, -918 }, { - 67, -919, -919, -919, -919, -919, -919, -919, -919, -919, + 69, -919, -919, -919, -919, -919, -919, -919, -919, -919, -919, -919, -919, -919, -919, -919, -919, -919, -919, -919, -919, -919, -919, -919, -919, -919, -919, -919, -919, -919, - -919, -919, 920, -919, -919, -919, -919, -919, -919, -919, + -919, -919, 1132, -919, -919, -919, -919, -919, -919, -919, -919, -919, -919, -919, -919, -919, -919, -919, -919, -919, -919, -919, -919, -919, -919, -919, -919, -919, -919, -919, -919, -919, -919, -919, -919, -919, -919, -919, -919, -919, @@ -16294,10 +16455,10 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -920, -920, -920, -920, -920, -920, -920, -920, -920, + 69, -920, -920, -920, -920, -920, -920, -920, -920, -920, -920, -920, -920, -920, -920, -920, -920, -920, -920, -920, -920, -920, -920, -920, -920, -920, -920, -920, -920, -920, - -920, -920, 921, -920, -920, -920, -920, -920, -920, -920, + -920, -920, 1133, -920, -920, -920, -920, -920, -920, -920, -920, -920, -920, -920, -920, -920, -920, -920, -920, -920, -920, -920, -920, -920, -920, -920, -920, -920, -920, -920, -920, -920, -920, -920, -920, -920, -920, -920, -920, -920, @@ -16311,27 +16472,27 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -921, -921, -921, -921, -921, -921, -921, -921, -921, - -921, -921, -921, -921, -921, -921, -921, -921, -921, -921, - -921, -921, -921, -921, -921, -921, -921, -921, -921, -921, - -921, -921, 922, -921, -921, -921, -921, -921, -921, -921, - -921, -921, -921, -921, -921, -921, -921, -921, -921, -921, - -921, -921, -921, -921, -921, -921, -921, -921, -921, -921, - -921, -921, -921, -921, -921, -921, -921, -921, -921, -921, - - -921, -921, -921, -921, -921, -921, -921, -921, -921, -921, + 69, -921, -921, -921, -921, -921, -921, -921, -921, -921, -921, -921, -921, -921, -921, -921, -921, -921, -921, -921, -921, -921, -921, -921, -921, -921, -921, -921, -921, -921, + -921, -921, 1134, -921, -921, -921, -921, -921, -921, -921, + -921, -921, -921, -921, -921, -921, -921, -921, 1135, 1135, + 1135, 1135, 1135, 1135, 1135, 1135, 1135, 1135, -921, -921, + -921, -921, -921, -921, -921, 1134, 1134, 1134, 1134, 1134, + + 1134, 1134, 1134, 1134, 1134, 1134, 1134, 1134, 1134, 1134, + 1134, 1134, 1134, 1134, 1134, 1134, 1134, 1134, 1134, 1134, + 1134, -921, -921, -921, -921, -921, -921, -921, -921, -921, -921, -921, -921, -921, -921, -921, -921, -921, -921, -921, -921, -921, -921, -921, -921, -921, -921, -921, -921, -921, -921, -921, -921, -921, -921, -921, -921, -921 }, { - 67, -922, -922, -922, -922, -922, -922, -922, -922, -922, + 69, -922, -922, -922, -922, -922, -922, -922, -922, -922, + -922, -922, -922, -922, -922, -922, -922, -922, -922, -922, -922, -922, -922, -922, -922, -922, -922, -922, -922, -922, -922, -922, -922, -922, -922, -922, -922, -922, -922, -922, - -922, -922, 923, -922, -922, -922, -922, -922, -922, -922, -922, -922, -922, -922, -922, -922, -922, -922, -922, -922, -922, -922, -922, -922, -922, -922, -922, -922, -922, -922, @@ -16345,11 +16506,11 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -923, -923, -923, -923, -923, -923, -923, -923, -923, + 69, -923, -923, -923, -923, -923, -923, -923, -923, -923, -923, -923, -923, -923, -923, -923, -923, -923, -923, -923, -923, -923, -923, -923, -923, -923, -923, -923, -923, -923, - -923, -923, 924, -923, -923, -923, -923, -923, -923, -923, + -923, -923, -923, -923, -923, -923, -923, -923, -923, -923, -923, -923, -923, -923, -923, -923, -923, -923, -923, -923, -923, -923, -923, -923, -923, -923, -923, -923, -923, -923, -923, -923, -923, -923, -923, -923, -923, -923, -923, -923, @@ -16363,10 +16524,10 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -924, -924, -924, -924, -924, -924, -924, -924, -924, + 69, -924, -924, -924, -924, -924, -924, -924, -924, -924, + -924, -924, -924, -924, -924, -924, -924, -924, -924, -924, -924, -924, -924, -924, -924, -924, -924, -924, -924, -924, -924, -924, -924, -924, -924, -924, -924, -924, -924, -924, - -924, -924, 925, -924, -924, -924, -924, -924, -924, -924, -924, -924, -924, -924, -924, -924, -924, -924, -924, -924, -924, -924, -924, -924, -924, -924, -924, -924, -924, -924, -924, -924, -924, -924, -924, -924, -924, -924, -924, -924, @@ -16380,10 +16541,10 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -925, -925, -925, -925, -925, -925, -925, -925, -925, + 69, -925, -925, -925, -925, -925, -925, -925, -925, -925, + -925, -925, -925, -925, -925, -925, -925, -925, -925, -925, -925, -925, -925, -925, -925, -925, -925, -925, -925, -925, -925, -925, -925, -925, -925, -925, -925, -925, -925, -925, - -925, -925, 926, -925, -925, -925, -925, -925, -925, -925, -925, -925, -925, -925, -925, -925, -925, -925, -925, -925, -925, -925, -925, -925, -925, -925, -925, -925, -925, -925, @@ -16397,7 +16558,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 67, -926, -926, -926, -926, -926, -926, -926, -926, -926, + 69, -926, -926, -926, -926, -926, -926, -926, -926, -926, -926, -926, -926, -926, -926, -926, -926, -926, -926, -926, -926, -926, -926, -926, -926, -926, -926, -926, -926, -926, @@ -16414,2299 +16575,12719 @@ static yyconst flex_int16_t yy_nxt[][128] = -926, -926, -926, -926, -926, -926, -926, -926 }, - } ; + { + 69, -927, -927, -927, -927, -927, -927, -927, -927, -927, + -927, -927, -927, -927, -927, -927, -927, -927, -927, -927, + -927, -927, -927, -927, -927, -927, -927, -927, -927, -927, + -927, -927, -927, -927, -927, -927, -927, -927, -927, -927, + -927, -927, -927, -927, -927, -927, -927, -927, -927, -927, + -927, -927, -927, -927, -927, -927, -927, -927, -927, -927, + -927, -927, -927, -927, -927, -927, -927, -927, -927, -927, + -927, -927, -927, -927, -927, -927, -927, -927, -927, -927, + -927, -927, -927, -927, -927, -927, -927, -927, -927, -927, -static yy_state_type yy_get_previous_state (void ); -static yy_state_type yy_try_NUL_trans (yy_state_type current_state ); -static int yy_get_next_buffer (void ); -static void yy_fatal_error (yyconst char msg[] ); + -927, -927, -927, -927, -927, -927, -927, -927, -927, -927, + -927, -927, -927, -927, -927, -927, -927, -927, -927, -927, + -927, -927, -927, -927, -927, -927, -927, -927, -927, -927, + -927, -927, -927, -927, -927, -927, -927, -927 + }, -/* Done after the current pattern has been matched and before the - * corresponding action - sets up wcsbthtext. - */ -#define YY_DO_BEFORE_ACTION \ - (yytext_ptr) = yy_bp; \ - wcsbthleng = (size_t) (yy_cp - yy_bp); \ - (yy_hold_char) = *yy_cp; \ - *yy_cp = '\0'; \ - (yy_c_buf_p) = yy_cp; + { + 69, -928, -928, -928, -928, -928, -928, -928, -928, -928, + -928, -928, -928, -928, -928, -928, -928, -928, -928, -928, + -928, -928, -928, -928, -928, -928, -928, -928, -928, -928, + -928, -928, -928, -928, -928, -928, -928, -928, -928, -928, + -928, -928, -928, -928, -928, -928, -928, -928, 1136, 1136, + 1136, 1136, 1136, 1136, 1136, 1136, 1136, 1136, -928, -928, -#define YY_NUM_RULES 283 -#define YY_END_OF_BUFFER 284 -/* This struct is not used in this scanner, - but its presence is necessary. */ -struct yy_trans_info - { - flex_int32_t yy_verify; - flex_int32_t yy_nxt; - }; -static yyconst flex_int16_t yy_accept[927] = - { 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, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 278, 278, - 280, 280, 281, 281, 0, 0, 284, 283, 126, 126, - 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, - 126, 126, 126, 283, 148, 148, 140, 140, 149, 149, - 141, 141, 161, 161, 161, 283, 182, 182, 171, 171, - - 187, 187, 198, 198, 199, 199, 230, 230, 265, 265, - 243, 243, 266, 266, 244, 244, 268, 268, 201, 202, - 200, 209, 209, 210, 210, 218, 218, 219, 219, 270, - 270, 272, 272, 271, 274, 274, 274, 273, 276, 276, - 279, 278, 277, 280, 281, 283, 282, 0, 0, 0, - 64, 58, 21, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 17, 0, 63, - 57, 0, 0, 0, 0, 0, 0, 0, 23, 0, - 19, 66, 60, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + -928, -928, -928, -928, -928, -928, -928, -928, -928, -928, + -928, -928, -928, -928, -928, -928, -928, -928, -928, -928, + -928, -928, -928, -928, -928, -928, -928, -928, -928, -928, + -928, -928, -928, -928, -928, -928, -928, -928, -928, -928, + -928, -928, -928, -928, -928, -928, -928, -928, -928, -928, + -928, -928, -928, -928, -928, -928, -928, -928, -928, -928, + -928, -928, -928, -928, -928, -928, -928, -928 + }, - 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, 269, 271, - 0, 273, 273, 273, 273, 0, 0, 275, 278, 277, - 277, 280, 281, 0, 282, 0, 0, 0, 0, 0, - 0, 0, 0, 65, 59, 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, 24, 0, 0, 0, 0, 0, 0, + { + 69, -929, -929, -929, -929, -929, -929, -929, -929, -929, + -929, -929, -929, -929, -929, -929, -929, -929, -929, -929, + -929, -929, -929, -929, -929, -929, -929, -929, -929, -929, - 20, 67, 61, 0, 0, 0, 0, 0, 0, 0, - 0, 127, 128, 129, 0, 0, 0, 130, 131, 132, - 137, 136, 0, 0, 0, 133, 134, 135, 139, 138, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 183, 184, 186, 185, 188, 189, 190, - 195, 194, 191, 192, 193, 197, 196, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 267, 0, 0, - 0, 206, 207, 208, 0, 0, 0, 215, 216, 217, - 273, 0, 273, 22, 18, 26, 0, 69, 74, 0, - - 13, 44, 79, 39, 34, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 84, 0, 0, 0, 91, - 51, 49, 0, 93, 0, 0, 0, 103, 0, 54, - 56, 108, 106, 110, 0, 28, 0, 71, 76, 0, - 15, 46, 81, 41, 36, 0, 122, 114, 0, 0, - 0, 0, 112, 0, 0, 121, 0, 0, 124, 142, - 143, 144, 145, 146, 147, 0, 0, 0, 0, 0, - 0, 162, 163, 164, 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, + -929, -929, 1137, -929, -929, -929, -929, -929, -929, -929, + -929, -929, -929, -929, -929, -929, -929, -929, -929, -929, + -929, -929, -929, -929, -929, -929, -929, -929, -929, -929, + -929, -929, -929, -929, -929, 1137, 1137, 1137, 1137, 1137, + 1137, 1137, 1137, 1137, 1137, 1137, 1137, 1137, 1137, 1137, + 1137, 1137, 1137, 1137, 1137, 1137, 1137, 1137, 1137, 1137, + 1137, -929, -929, -929, -929, -929, -929, -929, -929, -929, + -929, -929, -929, -929, -929, -929, -929, -929, -929, -929, + -929, -929, -929, -929, -929, -929, -929, -929, -929, -929, + -929, -929, -929, -929, -929, -929, -929, -929 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 203, 204, 205, 211, 212, 213, 214, 27, 70, - 75, 31, 14, 45, 80, 40, 35, 25, 68, 73, - 30, 12, 43, 78, 38, 33, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 95, 0, 97, 99, 101, - 62, 0, 0, 0, 0, 0, 0, 0, 29, 72, - 77, 32, 16, 47, 82, 42, 37, 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, 170, 167, 169, 165, 166, 168, + }, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 236, 234, 235, 231, - 232, 233, 0, 0, 0, 0, 0, 0, 0, 0, - 242, 240, 241, 237, 238, 239, 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, 160, 159, 155, 158, 152, 153, 157, 150, 151, - 154, 156, 181, 177, 180, 174, 176, 179, 172, 173, - 175, 178, 229, 225, 228, 222, 223, 227, 220, 221, - - 224, 226, 260, 250, 259, 248, 249, 258, 245, 246, - 247, 257, 264, 256, 263, 254, 255, 262, 251, 252, - 253, 261, 0, 0, 0, 0, 0, 0, 0, 90, - 50, 48, 0, 0, 0, 0, 102, 0, 52, 55, - 105, 107, 109, 0, 113, 111, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 120, 0, 0, 0, 0, - 0, 0, 123, 83, 85, 86, 87, 88, 0, 89, - 92, 94, 96, 98, 100, 104, 53, 0, 115, 117, - 118, 119, 116, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + { + 69, -930, -930, -930, -930, -930, -930, -930, -930, -930, + -930, -930, -930, -930, -930, -930, -930, -930, -930, -930, + -930, -930, -930, -930, -930, -930, -930, -930, -930, -930, + -930, -930, 1137, -930, -930, -930, -930, -930, -930, -930, + -930, -930, -930, -930, -930, -930, -930, -930, 1138, 1138, + 1138, 1138, 1138, 1138, 1138, 1138, 1138, 1138, -930, -930, + -930, -930, -930, -930, -930, 1137, 1137, 1137, 1137, 1137, + 1137, 1137, 1137, 1137, 1137, 1137, 1137, 1137, 1137, 1137, + 1137, 1137, 1137, 1137, 1137, 1137, 1137, 1137, 1137, 1137, + 1137, -930, -930, -930, -930, -930, -930, -930, -930, -930, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 0, 0, 3, 0, 0, 4, 0, 0, 5, - 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 6, 0, 7, - 0, 8, 0, 9, 0, 10, 0, 11, 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, 0, 0, + -930, -930, -930, -930, -930, -930, -930, -930, -930, -930, + -930, -930, -930, -930, -930, -930, -930, -930, -930, -930, + -930, -930, -930, -930, -930, -930, -930, -930 + }, - 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, 125 - } ; + { + 69, -931, -931, -931, -931, -931, -931, -931, -931, -931, + -931, -931, -931, -931, -931, -931, -931, -931, -931, -931, + -931, -931, -931, -931, -931, -931, -931, -931, -931, -931, + -931, -931, 1139, -931, -931, -931, -931, -931, -931, -931, + -931, -931, -931, -931, -931, -931, -931, -931, -931, -931, + -931, -931, -931, -931, -931, -931, -931, -931, -931, -931, + -931, -931, -931, -931, -931, -931, -931, -931, -931, -931, -static yy_state_type yy_last_accepting_state; -static char *yy_last_accepting_cpos; + -931, -931, -931, -931, -931, -931, -931, -931, -931, -931, + -931, -931, -931, -931, -931, -931, -931, -931, -931, -931, + -931, -931, -931, -931, -931, -931, -931, -931, -931, -931, + -931, -931, -931, -931, -931, -931, -931, -931, -931, -931, + -931, -931, -931, -931, -931, -931, -931, -931, -931, -931, + -931, -931, -931, -931, -931, -931, -931, -931 + }, -static yyconst yy_state_type yy_NUL_trans[927] = - { 0, - 68, 69, 68, 68, 85, 85, 87, 87, 89, 89, - 91, 91, 93, 93, 68, 68, 97, 97, 99, 99, - 101, 101, 103, 103, 105, 105, 107, 107, 109, 109, - 111, 111, 113, 113, 115, 115, 117, 117, 119, 119, - 120, 120, 122, 122, 124, 124, 126, 126, 128, 128, - 130, 130, 132, 132, 135, 135, 139, 139, 141, 141, - 144, 144, 145, 145, 146, 146, 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, + { + 69, -932, -932, -932, -932, -932, -932, -932, -932, -932, + -932, -932, -932, -932, -932, -932, -932, -932, -932, -932, + -932, -932, -932, -932, -932, -932, -932, -932, -932, -932, + -932, -932, 1140, -932, -932, -932, -932, -932, -932, -932, - 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, 247, - 0, 0, 251, 252, 253, 254, 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, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + -932, -932, -932, -932, -932, -932, -932, -932, 1141, 1141, + 1141, 1141, 1141, 1141, 1141, 1141, 1141, 1141, -932, -932, + -932, -932, -932, -932, -932, 1140, 1140, 1140, 1140, 1140, + 1140, 1140, 1140, 1140, 1140, 1140, 1140, 1140, 1140, 1140, + 1140, 1140, 1140, 1140, 1140, 1140, 1140, 1140, 1140, 1140, + 1140, -932, -932, -932, -932, -932, -932, -932, -932, -932, + -932, -932, -932, -932, -932, -932, -932, -932, -932, -932, + -932, -932, -932, -932, -932, -932, -932, -932, -932, -932, + -932, -932, -932, -932, -932, -932, -932, -932 + }, + + { + 69, -933, -933, -933, -933, -933, -933, -933, -933, -933, + + -933, -933, -933, -933, -933, -933, -933, -933, -933, -933, + -933, -933, -933, -933, -933, -933, -933, -933, -933, -933, + -933, -933, 1142, -933, -933, -933, -933, -933, -933, -933, + -933, -933, -933, -933, -933, -933, -933, -933, -933, -933, + -933, -933, -933, -933, -933, -933, -933, -933, -933, -933, + -933, -933, -933, -933, -933, -933, -933, -933, -933, -933, + -933, -933, -933, -933, -933, -933, -933, -933, -933, -933, + -933, -933, -933, -933, -933, -933, -933, -933, -933, -933, + -933, -933, -933, -933, -933, -933, -933, -933, -933, -933, + -933, -933, -933, -933, -933, -933, -933, -933, -933, -933, - 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, - 0, 0, 0, 0, 0, 0, 247, 0, 0, 251, - 251, 252, 253, 254, 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, 0, 0, 0, 0, 0, 0, + -933, -933, -933, -933, -933, -933, -933, -933, -933, -933, + -933, -933, -933, -933, -933, -933, -933, -933 + }, + + { + 69, -934, -934, -934, -934, -934, -934, -934, -934, -934, + -934, -934, -934, -934, -934, -934, -934, -934, -934, -934, + -934, -934, -934, -934, -934, -934, -934, -934, -934, -934, + -934, -934, 1143, -934, -934, -934, -934, -934, -934, -934, + -934, -934, -934, -934, -934, -934, -934, -934, -934, -934, + -934, -934, -934, -934, -934, -934, -934, -934, -934, -934, + -934, -934, -934, -934, -934, -934, -934, -934, -934, -934, + -934, -934, -934, -934, -934, -934, -934, -934, -934, -934, - 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, - 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, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + -934, -934, -934, -934, -934, -934, -934, -934, -934, -934, + -934, -934, -934, -934, -934, -934, -934, -934, -934, -934, + -934, -934, -934, -934, -934, -934, -934, -934, -934, -934, + -934, -934, -934, -934, -934, -934, -934, -934, -934, -934, + -934, -934, -934, -934, -934, -934, -934, -934 + }, + + { + 69, -935, -935, -935, -935, -935, -935, -935, -935, -935, + -935, -935, -935, -935, -935, -935, -935, -935, -935, -935, + -935, -935, -935, -935, -935, -935, -935, -935, -935, -935, + -935, -935, 1144, -935, -935, -935, -935, -935, -935, -935, + -935, -935, -935, -935, -935, -935, -935, -935, 1145, 1145, - 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, - 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, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, -935, -935, + -935, -935, -935, -935, -935, 1144, 1144, 1144, 1144, 1144, + 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, + 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, + 1144, -935, -935, -935, -935, -935, -935, -935, -935, -935, + -935, -935, -935, -935, -935, -935, -935, -935, -935, -935, + -935, -935, -935, -935, -935, -935, -935, -935, -935, -935, + -935, -935, -935, -935, -935, -935, -935, -935 + }, + + { + 69, -936, -936, -936, -936, -936, -936, -936, -936, -936, + -936, -936, -936, -936, -936, -936, -936, -936, -936, -936, - 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, - 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, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + -936, -936, -936, -936, -936, -936, -936, -936, -936, -936, + -936, -936, -936, -936, -936, -936, -936, -936, -936, -936, + -936, -936, -936, -936, -936, -936, -936, -936, -936, -936, + -936, -936, -936, -936, -936, -936, -936, -936, -936, -936, + -936, -936, -936, -936, -936, -936, -936, -936, -936, -936, + -936, -936, -936, -936, -936, -936, -936, -936, -936, -936, + -936, -936, -936, -936, -936, -936, -936, -936, -936, -936, + -936, -936, -936, -936, -936, -936, -936, -936, -936, -936, + -936, -936, -936, -936, -936, -936, -936, -936, -936, -936, + -936, -936, -936, -936, -936, -936, -936, -936, -936, -936, + + -936, -936, -936, -936, -936, -936, -936, -936 + }, + + { + 69, -937, -937, -937, -937, -937, -937, -937, -937, -937, + -937, -937, -937, -937, -937, -937, -937, -937, -937, -937, + -937, -937, -937, -937, -937, -937, -937, -937, -937, -937, + -937, -937, -937, -937, -937, -937, -937, -937, -937, -937, + -937, -937, -937, -937, -937, -937, -937, -937, -937, -937, + -937, -937, -937, -937, -937, -937, -937, -937, -937, -937, + -937, -937, -937, -937, -937, -937, -937, -937, -937, -937, + -937, -937, -937, -937, -937, -937, -937, -937, -937, -937, + -937, -937, -937, -937, -937, -937, -937, -937, -937, -937, - 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, - 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, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + -937, -937, -937, -937, -937, -937, -937, -937, -937, -937, + -937, -937, -937, -937, -937, -937, -937, -937, -937, -937, + -937, -937, -937, -937, -937, -937, -937, -937, -937, -937, + -937, -937, -937, -937, -937, -937, -937, -937 + }, + + { + 69, -938, -938, -938, -938, -938, -938, -938, -938, -938, + -938, -938, -938, -938, -938, -938, -938, -938, -938, -938, + -938, -938, -938, -938, -938, -938, -938, -938, -938, -938, + -938, -938, -938, -938, -938, -938, -938, -938, -938, -938, + -938, -938, -938, -938, -938, -938, -938, -938, -938, -938, + -938, -938, -938, -938, -938, -938, -938, -938, -938, -938, - 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, - 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, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + -938, -938, -938, -938, -938, -938, -938, -938, -938, -938, + -938, -938, -938, -938, -938, -938, -938, -938, -938, -938, + -938, -938, -938, -938, -938, -938, -938, -938, -938, -938, + -938, -938, -938, -938, -938, -938, -938, -938, -938, -938, + -938, -938, -938, -938, -938, -938, -938, -938, -938, -938, + -938, -938, -938, -938, -938, -938, -938, -938, -938, -938, + -938, -938, -938, -938, -938, -938, -938, -938 + }, + + { + 69, -939, -939, -939, -939, -939, -939, -939, -939, -939, + -939, -939, -939, -939, -939, -939, -939, -939, -939, -939, + -939, -939, -939, -939, -939, -939, -939, -939, -939, -939, - 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, 847, 0, 849, 0, 851, 0, - 853, 0, 855, 0, 857, 0, 847, 0, 849, 0, - 851, 0, 853, 0, 855, 0, 857, 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, 0, 0, 0, + -939, -939, -939, -939, -939, -939, -939, -939, -939, -939, + -939, -939, -939, -939, -939, -939, -939, -939, -939, -939, + -939, -939, -939, -939, -939, -939, -939, -939, -939, -939, + -939, -939, -939, -939, -939, -939, -939, -939, -939, -939, + -939, -939, -939, -939, -939, -939, -939, -939, -939, -939, + -939, -939, -939, -939, -939, -939, -939, -939, -939, -939, + -939, -939, -939, -939, -939, -939, -939, -939, -939, -939, + -939, -939, -939, -939, -939, -939, -939, -939, -939, -939, + -939, -939, -939, -939, -939, -939, -939, -939, -939, -939, + -939, -939, -939, -939, -939, -939, -939, -939 + + }, + + { + 69, -940, -940, -940, -940, -940, -940, -940, -940, -940, + -940, -940, -940, -940, -940, -940, -940, -940, -940, -940, + -940, -940, -940, -940, -940, -940, -940, -940, -940, -940, + -940, -940, -940, -940, -940, -940, -940, -940, -940, -940, + -940, -940, -940, -940, -940, -940, -940, -940, -940, -940, + -940, -940, -940, -940, -940, -940, -940, -940, -940, -940, + -940, -940, -940, -940, -940, -940, -940, -940, -940, -940, + -940, -940, -940, -940, -940, -940, -940, -940, -940, -940, + -940, -940, -940, -940, -940, -940, -940, -940, -940, -940, + -940, -940, -940, -940, -940, -940, -940, -940, -940, -940, - 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 - } ; + -940, -940, -940, -940, -940, -940, -940, -940, -940, -940, + -940, -940, -940, -940, -940, -940, -940, -940, -940, -940, + -940, -940, -940, -940, -940, -940, -940, -940 + }, + + { + 69, -941, -941, -941, -941, -941, -941, -941, -941, -941, + -941, -941, -941, -941, -941, -941, -941, -941, -941, -941, + -941, -941, -941, -941, -941, -941, -941, -941, -941, -941, + -941, -941, -941, -941, -941, -941, -941, -941, -941, -941, + -941, -941, -941, -941, -941, -941, -941, -941, -941, -941, + -941, -941, -941, -941, -941, -941, -941, -941, -941, -941, + -941, -941, -941, -941, -941, -941, -941, -941, -941, -941, -extern int wcsbth_flex_debug; -int wcsbth_flex_debug = 0; + -941, -941, -941, -941, -941, -941, -941, -941, -941, -941, + -941, -941, -941, -941, -941, -941, -941, -941, -941, -941, + -941, -941, -941, -941, -941, -941, -941, -941, -941, -941, + -941, -941, -941, -941, -941, -941, -941, -941, -941, -941, + -941, -941, -941, -941, -941, -941, -941, -941, -941, -941, + -941, -941, -941, -941, -941, -941, -941, -941 + }, + + { + 69, 942, 942, 942, 942, 942, 942, 942, 942, 942, + 943, 942, 942, 942, 942, 942, 942, 942, 942, 942, + 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, + 942, 942, 944, 942, 942, 942, 942, 942, 942, 942, -/* The intent behind this definition is that it'll catch - * any uses of REJECT which flex missed. - */ -#define REJECT reject_used_but_not_detected -#define yymore() yymore_used_but_not_detected -#define YY_MORE_ADJ 0 -#define YY_RESTORE_YY_MORE_OFFSET -char *wcsbthtext; -#line 1 "wcsbth.l" -/*============================================================================ + 942, 942, 942, 942, 942, 942, 942, 945, 942, 942, + 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, + 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, + 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, + 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, + 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, + 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, + 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, + 942, 942, 942, 942, 942, 942, 942, 942 + }, + + { + 69, -943, -943, -943, -943, -943, -943, -943, -943, -943, + + -943, -943, -943, -943, -943, -943, -943, -943, -943, -943, + -943, -943, -943, -943, -943, -943, -943, -943, -943, -943, + -943, -943, -943, -943, -943, -943, -943, -943, -943, -943, + -943, -943, -943, -943, -943, -943, -943, -943, -943, -943, + -943, -943, -943, -943, -943, -943, -943, -943, -943, -943, + -943, -943, -943, -943, -943, -943, -943, -943, -943, -943, + -943, -943, -943, -943, -943, -943, -943, -943, -943, -943, + -943, -943, -943, -943, -943, -943, -943, -943, -943, -943, + -943, -943, -943, -943, -943, -943, -943, -943, -943, -943, + -943, -943, -943, -943, -943, -943, -943, -943, -943, -943, - WCSLIB 4.14 - an implementation of the FITS WCS standard. - Copyright (C) 1995-2012, Mark Calabretta + -943, -943, -943, -943, -943, -943, -943, -943, -943, -943, + -943, -943, -943, -943, -943, -943, -943, -943 + }, + + { + 69, 942, 942, 942, 942, 942, 942, 942, 942, 942, + 943, 942, 942, 942, 942, 942, 942, 942, 942, 942, + 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, + 942, 942, 944, 942, 942, 942, 942, 942, 942, 942, + 942, 942, 942, 942, 942, 942, 942, 945, 942, 942, + 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, + 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, + 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, - This file is part of WCSLIB. + 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, + 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, + 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, + 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, + 942, 942, 942, 942, 942, 942, 942, 942 + }, + + { + 69, 942, 942, 942, 942, 942, 942, 942, 942, 942, + 943, 942, 942, 942, 942, 942, 942, 942, 942, 942, + 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, + 942, 942, 944, 942, 942, 942, 942, 942, 942, 942, + 942, 942, 942, 942, 942, 942, 942, 945, 942, 942, - WCSLIB is free software: you can redistribute it and/or modify it under the - terms of the GNU Lesser General Public License as published by the Free - Software Foundation, either version 3 of the License, or (at your option) - any later version. + 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, + 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, + 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, + 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, + 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, + 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, + 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, + 942, 942, 942, 942, 942, 942, 942, 942 + }, + + { + 69, -946, -946, -946, -946, -946, -946, -946, -946, -946, + -946, -946, -946, -946, -946, -946, -946, -946, -946, -946, - WCSLIB is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for - more details. + -946, -946, -946, -946, -946, -946, -946, -946, -946, -946, + -946, -946, -946, -946, -946, -946, -946, -946, -946, -946, + -946, -946, -946, -946, -946, -946, -946, -946, -946, -946, + -946, -946, -946, -946, -946, -946, -946, -946, -946, -946, + -946, -946, -946, -946, -946, -946, -946, -946, -946, -946, + -946, -946, -946, -946, -946, -946, -946, -946, -946, -946, + -946, -946, -946, -946, -946, 1146, -946, -946, -946, -946, + -946, -946, -946, -946, -946, -946, -946, -946, -946, -946, + -946, -946, -946, -946, -946, -946, -946, -946, -946, -946, + -946, -946, -946, -946, -946, -946, -946, -946, -946, -946, + + -946, -946, -946, -946, -946, -946, -946, -946 + }, + + { + 69, -947, -947, -947, -947, -947, -947, -947, -947, -947, + -947, -947, -947, -947, -947, -947, -947, -947, -947, -947, + -947, -947, -947, -947, -947, -947, -947, -947, -947, -947, + -947, -947, -947, -947, -947, -947, -947, -947, -947, -947, + -947, -947, -947, -947, -947, -947, -947, -947, -947, -947, + -947, -947, -947, -947, -947, -947, -947, -947, -947, -947, + -947, -947, -947, -947, -947, -947, 1147, -947, -947, -947, + -947, -947, -947, -947, -947, -947, -947, -947, -947, -947, + -947, -947, -947, -947, -947, -947, -947, -947, -947, -947, - You should have received a copy of the GNU Lesser General Public License - along with WCSLIB. If not, see http://www.gnu.org/licenses. + -947, -947, -947, -947, -947, -947, -947, -947, -947, -947, + -947, -947, -947, -947, -947, -947, -947, -947, -947, -947, + -947, -947, -947, -947, -947, -947, -947, -947, -947, -947, + -947, -947, -947, -947, -947, -947, -947, -947 + }, + + { + 69, -948, -948, -948, -948, -948, -948, -948, -948, -948, + -948, -948, -948, -948, -948, -948, -948, -948, -948, -948, + -948, -948, -948, -948, -948, -948, -948, -948, -948, -948, + -948, -948, 1148, -948, -948, -948, -948, -948, -948, -948, + -948, -948, -948, -948, -948, -948, -948, -948, -948, -948, + -948, -948, -948, -948, -948, -948, -948, -948, -948, -948, - Direct correspondence concerning WCSLIB to mark@calabretta.id.au + -948, -948, -948, -948, -948, -948, -948, -948, -948, -948, + -948, -948, -948, -948, -948, -948, -948, -948, -948, -948, + -948, -948, -948, -948, -948, -948, -948, -948, -948, -948, + -948, -948, -948, -948, -948, -948, -948, -948, -948, -948, + -948, -948, -948, -948, -948, -948, -948, -948, -948, -948, + -948, -948, -948, -948, -948, -948, -948, -948, -948, -948, + -948, -948, -948, -948, -948, -948, -948, -948 + }, + + { + 69, -949, -949, -949, -949, -949, -949, -949, -949, -949, + -949, -949, -949, -949, -949, -949, -949, -949, -949, -949, + -949, -949, -949, -949, -949, -949, -949, -949, -949, -949, - Author: Mark Calabretta, Australia Telescope National Facility, CSIRO. - http://www.atnf.csiro.au/people/Mark.Calabretta - $Id: wcsbth.c,v 4.14 2012/07/13 10:02:31 cal103 Exp $ -*============================================================================= -* -* wcsbth.l is a Flex description file containing the definition of a lexical -* scanner for parsing the WCS keyrecords for one or more image arrays and/or -* pixel lists in a FITS binary table header. It can also handle primary image -* and image extension headers. -* -* wcsbth.l requires Flex v2.5.4 or later. Refer to wcshdr.h for a description -* of the user interface and operating notes. -* -* Implementation notes -* -------------------- -* wcsbth() may be invoked with an option that causes it to recognise the -* image-header form of WCS keywords as defaults for each alternate coordinate -* representation (up to 27). By design, with this option enabled wcsbth() can -* also handle primary image and image extension headers, effectively treating -* them as a single-column binary table though with WCS keywords of a different -* form. -* -* NAXIS is always 2 for binary tables, it refers to the two-dimensional nature -* of the table. Thus NAXIS does not count the number of image axes in either -* image arrays or pixels lists and for the latter there is not even a formal -* equivalent of WCSAXESa. Hence NAXIS is always ignored and a first pass -* through the header is required to determine the number of images, the number -* of alternate coordinate representations for each image (up to 27), and the -* number of coordinate axes in each representation; this pass also counts the -* number of iPVn_ma and iPSn_ma or TVk_ma and TSk_ma keywords in each -* representation. -* -* On completion of the first pass, the association between column number and -* axis number is defined for each representation of a pixel list. Memory is -* allocated for an array of the required number of wcsprm structs and each of -* these is initialized appropriately. These structs are filled in the second -* pass. -* -* It is permissible for a scalar table column to contain degenerate (single- -* point) image arrays and simultaneously form one axis of a pixel list. -* -* The parser does not check for duplicated keywords, for most keywords it -* accepts the last encountered. -* -* wcsbth() does not currently handle the Green Bank convention. -* -*===========================================================================*/ -/* Options. */ -/* Indices for parameterized keywords. */ -/* Alternate coordinate system identifier. */ -/* Keyvalue data types. */ -/* Exclusive start states. */ + -949, -949, -949, -949, -949, -949, -949, -949, -949, -949, + -949, -949, -949, -949, -949, -949, -949, -949, -949, -949, + -949, -949, -949, -949, -949, -949, -949, -949, -949, -949, + -949, -949, -949, -949, -949, -949, 1149, -949, -949, -949, + -949, -949, -949, -949, -949, -949, -949, -949, -949, -949, + -949, -949, -949, -949, -949, -949, -949, -949, -949, -949, + -949, -949, -949, -949, -949, -949, -949, -949, -949, -949, + -949, -949, -949, -949, -949, -949, -949, -949, -949, -949, + -949, -949, -949, -949, -949, -949, -949, -949, -949, -949, + -949, -949, -949, -949, -949, -949, -949, -949 + + }, + + { + 69, -950, -950, -950, -950, -950, -950, -950, -950, -950, + -950, -950, -950, -950, -950, -950, -950, -950, -950, -950, + -950, -950, -950, -950, -950, -950, -950, -950, -950, -950, + -950, -950, -950, -950, -950, -950, -950, -950, -950, -950, + -950, -950, -950, -950, -950, -950, -950, -950, -950, -950, + -950, -950, -950, -950, -950, -950, -950, -950, -950, -950, + -950, -950, -950, -950, -950, -950, 1150, -950, -950, -950, + -950, -950, -950, -950, -950, -950, -950, -950, -950, -950, + -950, -950, -950, -950, -950, -950, -950, -950, -950, -950, + -950, -950, -950, -950, -950, -950, -950, -950, -950, -950, + -950, -950, -950, -950, -950, -950, -950, -950, -950, -950, + -950, -950, -950, -950, -950, -950, -950, -950, -950, -950, + -950, -950, -950, -950, -950, -950, -950, -950 + }, + + { + 69, -951, -951, -951, -951, -951, -951, -951, -951, -951, + -951, -951, -951, -951, -951, -951, -951, -951, -951, -951, + -951, -951, -951, -951, -951, -951, -951, -951, -951, -951, + -951, -951, -951, -951, -951, -951, -951, -951, -951, -951, + -951, -951, -951, -951, -951, -951, -951, -951, -951, -951, + -951, -951, -951, -951, -951, -951, -951, -951, -951, -951, + -951, -951, -951, -951, -951, -951, -951, -951, -951, -951, + -951, -951, -951, -951, -951, -951, -951, -951, -951, -951, + -951, -951, -951, -951, -951, 1151, -951, -951, -951, -951, + -951, -951, -951, -951, -951, -951, -951, -951, -951, -951, + -951, -951, -951, -951, -951, -951, -951, -951, -951, -951, + -951, -951, -951, -951, -951, -951, -951, -951, -951, -951, + -951, -951, -951, -951, -951, -951, -951, -951 + }, + + { + 69, -952, -952, -952, -952, -952, -952, -952, -952, -952, + -952, -952, -952, -952, -952, -952, -952, -952, -952, -952, + -952, -952, -952, -952, -952, -952, -952, -952, -952, -952, + -952, -952, -952, -952, -952, -952, -952, -952, -952, -952, + -952, -952, -952, -952, -952, -952, -952, -952, -952, -952, + -952, -952, -952, -952, -952, -952, -952, -952, -952, -952, + -952, -952, -952, -952, -952, -952, 1152, -952, -952, -952, + -952, -952, -952, -952, -952, -952, -952, -952, -952, -952, + -952, -952, -952, -952, -952, -952, -952, -952, -952, -952, + -952, -952, -952, -952, -952, -952, -952, -952, -952, -952, + -952, -952, -952, -952, -952, -952, -952, -952, -952, -952, + -952, -952, -952, -952, -952, -952, -952, -952, -952, -952, + -952, -952, -952, -952, -952, -952, -952, -952 + }, + + { + 69, -953, -953, -953, -953, -953, -953, -953, -953, -953, + + -953, -953, -953, -953, -953, -953, -953, -953, -953, -953, + -953, -953, -953, -953, -953, -953, -953, -953, -953, -953, + -953, -953, -953, -953, -953, -953, -953, -953, -953, -953, + -953, -953, -953, -953, -953, -953, -953, -953, -953, -953, + -953, -953, -953, -953, -953, -953, -953, -953, -953, -953, + -953, -953, -953, -953, -953, -953, 1153, -953, -953, -953, + -953, -953, -953, -953, -953, -953, -953, -953, -953, -953, + -953, -953, -953, -953, -953, -953, -953, -953, -953, -953, + -953, -953, -953, -953, -953, -953, -953, -953, -953, -953, + -953, -953, -953, -953, -953, -953, -953, -953, -953, -953, + -953, -953, -953, -953, -953, -953, -953, -953, -953, -953, + -953, -953, -953, -953, -953, -953, -953, -953 + }, + + { + 69, -954, -954, -954, -954, -954, -954, -954, -954, -954, + -954, -954, -954, -954, -954, -954, -954, -954, -954, -954, + -954, -954, -954, -954, -954, -954, -954, -954, -954, -954, + -954, -954, -954, -954, -954, -954, -954, -954, -954, -954, + -954, -954, -954, -954, -954, -954, -954, -954, -954, -954, + -954, -954, -954, -954, -954, -954, -954, -954, -954, -954, + -954, -954, -954, -954, -954, -954, -954, -954, -954, -954, + -954, -954, -954, -954, -954, -954, -954, -954, -954, -954, + -954, -954, -954, -954, -954, 1154, -954, -954, -954, -954, + -954, -954, -954, -954, -954, -954, -954, -954, -954, -954, + -954, -954, -954, -954, -954, -954, -954, -954, -954, -954, + -954, -954, -954, -954, -954, -954, -954, -954, -954, -954, + -954, -954, -954, -954, -954, -954, -954, -954 + }, + + { + 69, -955, -955, -955, -955, -955, -955, -955, -955, -955, + -955, -955, -955, -955, -955, -955, -955, -955, -955, -955, + -955, -955, -955, -955, -955, -955, -955, -955, -955, -955, + -955, -955, -955, -955, -955, -955, -955, -955, -955, -955, + -955, -955, -955, -955, -955, -955, -955, -955, -955, -955, + -955, -955, -955, -955, -955, -955, -955, -955, -955, -955, + -955, -955, -955, -955, -955, -955, -955, -955, -955, -955, + -955, -955, -955, -955, -955, -955, -955, -955, -955, -955, + -955, -955, -955, -955, -955, -955, 1155, -955, -955, -955, + -955, -955, -955, -955, -955, -955, -955, -955, -955, -955, + -955, -955, -955, -955, -955, -955, -955, -955, -955, -955, + -955, -955, -955, -955, -955, -955, -955, -955, -955, -955, + -955, -955, -955, -955, -955, -955, -955, -955 + }, + + { + 69, -956, -956, -956, -956, -956, -956, -956, -956, -956, + -956, -956, -956, -956, -956, -956, -956, -956, -956, -956, + -956, -956, -956, -956, -956, -956, -956, -956, -956, -956, + -956, -956, -956, -956, -956, -956, -956, -956, -956, -956, + -956, -956, -956, -956, -956, -956, -956, -956, -956, -956, + -956, -956, -956, -956, -956, -956, -956, -956, -956, -956, + -956, -956, -956, -956, -956, -956, -956, -956, -956, 1156, + -956, -956, -956, -956, -956, -956, -956, -956, -956, -956, + -956, -956, -956, -956, -956, -956, -956, -956, -956, -956, + -956, -956, -956, -956, -956, -956, -956, -956, -956, -956, + -956, -956, -956, -956, -956, -956, -956, -956, -956, -956, + -956, -956, -956, -956, -956, -956, -956, -956, -956, -956, + + -956, -956, -956, -956, -956, -956, -956, -956 + }, + + { + 69, -957, -957, -957, -957, -957, -957, -957, -957, -957, + -957, -957, -957, -957, -957, -957, -957, -957, -957, -957, + -957, -957, -957, -957, -957, -957, -957, -957, -957, -957, + -957, -957, -957, -957, -957, -957, -957, -957, -957, -957, + -957, -957, -957, -957, -957, -957, -957, -957, -957, -957, + -957, -957, -957, -957, -957, -957, -957, -957, -957, -957, + -957, -957, -957, -957, -957, -957, -957, -957, -957, -957, + -957, -957, -957, -957, -957, -957, -957, -957, 1157, -957, + -957, -957, -957, -957, -957, -957, -957, -957, -957, -957, + -957, -957, -957, -957, -957, -957, -957, -957, -957, -957, + -957, -957, -957, -957, -957, -957, -957, -957, -957, -957, + -957, -957, -957, -957, -957, -957, -957, -957, -957, -957, + -957, -957, -957, -957, -957, -957, -957, -957 + }, + + { + 69, -958, -958, -958, -958, -958, -958, -958, -958, -958, + -958, -958, -958, -958, -958, -958, -958, -958, -958, -958, + -958, -958, -958, -958, -958, -958, -958, -958, -958, -958, + -958, -958, -958, -958, -958, -958, -958, -958, -958, -958, + -958, -958, -958, -958, -958, -958, -958, -958, -958, -958, + -958, -958, -958, -958, -958, -958, -958, -958, -958, -958, + -958, -958, -958, -958, -958, -958, 1158, -958, -958, -958, + -958, -958, -958, -958, -958, -958, -958, -958, -958, -958, + -958, -958, -958, -958, -958, -958, -958, -958, -958, -958, + -958, -958, -958, -958, -958, -958, -958, -958, -958, -958, + -958, -958, -958, -958, -958, -958, -958, -958, -958, -958, + -958, -958, -958, -958, -958, -958, -958, -958, -958, -958, + -958, -958, -958, -958, -958, -958, -958, -958 + }, + + { + 69, -959, -959, -959, -959, -959, -959, -959, -959, -959, + -959, -959, -959, -959, -959, -959, -959, -959, -959, -959, + -959, -959, -959, -959, -959, -959, -959, -959, -959, -959, -#line 106 "wcsbth.l" -#include -#include -#include -#include -#include + -959, -959, -959, -959, -959, -959, -959, -959, -959, -959, + -959, -959, -959, -959, -959, -959, -959, -959, -959, -959, + -959, -959, -959, -959, -959, -959, -959, -959, -959, -959, + -959, -959, -959, -959, -959, -959, -959, -959, -959, 1159, + -959, -959, -959, -959, -959, -959, -959, -959, -959, -959, + -959, -959, -959, -959, -959, -959, -959, -959, -959, -959, + -959, -959, -959, -959, -959, -959, -959, -959, -959, -959, + -959, -959, -959, -959, -959, -959, -959, -959, -959, -959, + -959, -959, -959, -959, -959, -959, -959, -959, -959, -959, + -959, -959, -959, -959, -959, -959, -959, -959 + + }, + + { + 69, -960, -960, -960, -960, -960, -960, -960, -960, -960, + -960, -960, -960, -960, -960, -960, -960, -960, -960, -960, + -960, -960, -960, -960, -960, -960, -960, -960, -960, -960, + -960, -960, -960, -960, -960, -960, -960, -960, -960, -960, + -960, -960, -960, -960, -960, -960, -960, -960, -960, -960, + -960, -960, -960, -960, -960, -960, -960, -960, -960, -960, + -960, -960, -960, -960, -960, -960, -960, -960, -960, -960, + 1160, -960, -960, -960, -960, -960, -960, -960, -960, -960, + -960, -960, -960, -960, -960, -960, -960, -960, -960, -960, + -960, -960, -960, -960, -960, -960, -960, -960, -960, -960, -#include "wcs.h" -#include "wcshdr.h" -#include "wcsmath.h" -#include "wcsutil.h" + -960, -960, -960, -960, -960, -960, -960, -960, -960, -960, + -960, -960, -960, -960, -960, -960, -960, -960, -960, -960, + -960, -960, -960, -960, -960, -960, -960, -960 + }, + + { + 69, -961, -961, -961, -961, -961, -961, -961, -961, -961, + -961, -961, -961, -961, -961, -961, -961, -961, -961, -961, + -961, -961, -961, -961, -961, -961, -961, -961, -961, -961, + -961, -961, 1161, -961, -961, -961, -961, -961, -961, -961, + -961, -961, -961, -961, -961, -961, -961, -961, -961, -961, + -961, -961, -961, -961, -961, -961, -961, -961, -961, -961, + -961, -961, -961, -961, -961, -961, -961, -961, -961, -961, - /* Codes used for keyvalue data types. */ -#define INTEGER 0 -#define FLOAT 1 -#define STRING 2 - - /* Bit masks used for keyword types: */ -#define IMGAUX 0x1 /* Auxiliary image header, e.g. LONPOLEa or */ - /* DATE-OBS. */ -#define IMGAXIS 0x2 /* Image header with axis number, e.g. */ - /* CTYPEia. */ -#define IMGHEAD 0x3 /* Image header of either type. */ -#define BIMGARR 0x4 /* Binary table image array with axis */ - /* number, e.g. iCTYna. */ -#define PIXLIST 0x8 /* Pixel list, e.g. TCTYna. */ -#define BINTAB 0xC /* Shared binary table image array (without */ - /* axis number) or pixel list, e.g. LONPna */ - /* or OBSGXn. */ - -#define YY_DECL int wcsbth(char *header, int nkeyrec, int relax, int ctrl, \ - int keysel, int *colsel, int *nreject, int *nwcs, \ - struct wcsprm **wcs) + -961, -961, -961, -961, -961, -961, -961, -961, -961, -961, + -961, -961, -961, -961, -961, -961, -961, -961, -961, -961, + -961, -961, -961, -961, -961, -961, -961, -961, -961, -961, + -961, -961, -961, -961, -961, -961, -961, -961, -961, -961, + -961, -961, -961, -961, -961, -961, -961, -961, -961, -961, + -961, -961, -961, -961, -961, -961, -961, -961 + }, + + { + 69, -962, -962, -962, -962, -962, -962, -962, -962, -962, + -962, -962, -962, -962, -962, -962, -962, -962, -962, -962, + -962, -962, -962, -962, -962, -962, -962, -962, -962, -962, + -962, -962, 1162, -962, -962, -962, -962, -962, -962, -962, -#define YY_INPUT(inbuff, count, bufsize) \ - { \ - if (wcsbth_nkeyrec) { \ - strncpy(inbuff, wcsbth_hdr, 80); \ - inbuff[80] = '\n'; \ - wcsbth_hdr += 80; \ - wcsbth_nkeyrec--; \ - count = 81; \ - } else { \ - count = YY_NULL; \ - } \ - } + -962, -962, -962, -962, -962, -962, -962, -962, 1163, 1163, + 1163, 1163, 1163, 1163, 1163, 1163, 1163, 1163, -962, -962, + -962, -962, -962, -962, -962, -962, -962, -962, -962, -962, + -962, -962, -962, -962, -962, -962, -962, -962, -962, -962, + -962, -962, -962, -962, -962, -962, -962, -962, -962, -962, + -962, -962, -962, -962, -962, -962, -962, -962, -962, -962, + -962, -962, -962, -962, -962, -962, -962, -962, -962, -962, + -962, -962, -962, -962, -962, -962, -962, -962, -962, -962, + -962, -962, -962, -962, -962, -962, -962, -962 + }, + + { + 69, -963, -963, -963, -963, -963, -963, -963, -963, -963, + + -963, -963, -963, -963, -963, -963, -963, -963, -963, -963, + -963, -963, -963, -963, -963, -963, -963, -963, -963, -963, + -963, -963, -963, -963, -963, -963, -963, -963, -963, -963, + -963, -963, -963, -963, -963, -963, -963, -963, -963, -963, + -963, -963, -963, -963, -963, -963, -963, -963, -963, -963, + -963, -963, -963, -963, -963, -963, 1164, -963, -963, -963, + -963, -963, -963, -963, -963, -963, -963, -963, -963, -963, + -963, -963, -963, -963, -963, -963, -963, -963, -963, -963, + -963, -963, -963, -963, -963, -963, -963, -963, -963, -963, + -963, -963, -963, -963, -963, -963, -963, -963, -963, -963, -/* A convenience macro to get around incompatibilities between unput() and - yyless(): put wcsbthtext followed by a blank back onto the input stream. */ -#define WCSBTH_PUTBACK \ - sprintf(stmp, "%s ", wcsbthtext); \ - itmp = strlen(stmp); \ - while (itmp) unput(stmp[--itmp]); + -963, -963, -963, -963, -963, -963, -963, -963, -963, -963, + -963, -963, -963, -963, -963, -963, -963, -963 + }, + + { + 69, -964, -964, -964, -964, -964, -964, -964, -964, -964, + -964, -964, -964, -964, -964, -964, -964, -964, -964, -964, + -964, -964, -964, -964, -964, -964, -964, -964, -964, -964, + -964, -964, 1165, -964, -964, -964, -964, -964, -964, -964, + -964, -964, -964, -964, -964, -964, -964, -964, -964, -964, + -964, -964, -964, -964, -964, -964, -964, -964, -964, -964, + -964, -964, -964, -964, -964, -964, -964, -964, -964, -964, + -964, -964, -964, -964, -964, -964, -964, -964, -964, -964, -/* These global variables are required by YY_INPUT. */ -char *wcsbth_hdr; -int wcsbth_nkeyrec; + -964, -964, -964, -964, -964, -964, -964, -964, -964, -964, + -964, -964, -964, -964, -964, -964, -964, -964, -964, -964, + -964, -964, -964, -964, -964, -964, -964, -964, -964, -964, + -964, -964, -964, -964, -964, -964, -964, -964, -964, -964, + -964, -964, -964, -964, -964, -964, -964, -964 + }, + + { + 69, -965, -965, -965, -965, -965, -965, -965, -965, -965, + -965, -965, -965, -965, -965, -965, -965, -965, -965, -965, + -965, -965, -965, -965, -965, -965, -965, -965, -965, -965, + -965, -965, 1166, -965, -965, -965, -965, -965, -965, -965, + -965, -965, -965, -965, -965, -965, -965, -965, -965, -965, -/* Used in preempting the call to exit() by yy_fatal_error(). */ -jmp_buf wcsbth_abort_jmp_env; -#define exit(status) longjmp(wcsbth_abort_jmp_env, status) + -965, -965, -965, -965, -965, -965, -965, -965, -965, -965, + -965, -965, -965, -965, -965, -965, -965, -965, -965, -965, + -965, -965, -965, -965, -965, -965, -965, -965, -965, -965, + -965, -965, -965, -965, -965, -965, -965, -965, -965, -965, + -965, -965, -965, -965, -965, -965, -965, -965, -965, -965, + -965, -965, -965, -965, -965, -965, -965, -965, -965, -965, + -965, -965, -965, -965, -965, -965, -965, -965, -965, -965, + -965, -965, -965, -965, -965, -965, -965, -965 + }, + + { + 69, -966, -966, -966, -966, -966, -966, -966, -966, -966, + -966, -966, -966, -966, -966, -966, -966, -966, -966, -966, -/* Struct used internally for header bookkeeping. */ -struct wcsbth_alts { - int ncol, ialt, icol, imgherit; - short int (*arridx)[27]; - short int pixidx[27]; - short int pad1; - unsigned int *pixlist; + -966, -966, -966, -966, -966, -966, -966, -966, -966, -966, + -966, -966, -966, -966, -966, -966, -966, -966, -966, -966, + -966, -966, -966, -966, -966, -966, -966, -966, -966, -966, + -966, -966, -966, -966, -966, -966, -966, -966, -966, -966, + -966, -966, -966, -966, -966, -966, -966, -966, -966, -966, + -966, -966, -966, -966, -966, -966, -966, -966, -966, -966, + -966, -966, -966, -966, -966, -966, -966, -966, 1167, -966, + -966, -966, -966, -966, -966, -966, -966, -966, -966, -966, + -966, -966, -966, -966, -966, -966, -966, -966, -966, -966, + -966, -966, -966, -966, -966, -966, -966, -966, -966, -966, + + -966, -966, -966, -966, -966, -966, -966, -966 + }, + + { + 69, -967, -967, -967, -967, -967, -967, -967, -967, -967, + -967, -967, -967, -967, -967, -967, -967, -967, -967, -967, + -967, -967, -967, -967, -967, -967, -967, -967, -967, -967, + -967, -967, -967, -967, -967, -967, -967, -967, -967, -967, + -967, -967, -967, -967, -967, -967, -967, -967, -967, -967, + -967, -967, -967, -967, -967, -967, -967, -967, -967, -967, + -967, -967, -967, -967, -967, -967, 1168, -967, -967, -967, + -967, -967, -967, -967, -967, -967, -967, -967, -967, -967, + -967, -967, -967, -967, -967, -967, -967, -967, -967, -967, - unsigned char (*npv)[27]; - unsigned char (*nps)[27]; - unsigned char pixnpv[27]; - unsigned char pixnps[27]; - unsigned char pad2[2]; -}; + -967, -967, -967, -967, -967, -967, -967, -967, -967, -967, + -967, -967, -967, -967, -967, -967, -967, -967, -967, -967, + -967, -967, -967, -967, -967, -967, -967, -967, -967, -967, + -967, -967, -967, -967, -967, -967, -967, -967 + }, + + { + 69, -968, -968, -968, -968, -968, -968, -968, -968, -968, + -968, -968, -968, -968, -968, -968, -968, -968, -968, -968, + -968, -968, -968, -968, -968, -968, -968, -968, -968, -968, + -968, -968, -968, -968, -968, -968, -968, -968, -968, -968, + -968, -968, -968, -968, -968, -968, -968, -968, -968, -968, + -968, -968, -968, -968, -968, -968, -968, -968, -968, -968, -int wcsbth_pass1(int keytype, int i, int j, int n, int k, char a, char ptype, - struct wcsbth_alts *alts); -int wcsbth_init1(struct wcsbth_alts *alts, int *nwcs, struct wcsprm **wcs); + -968, -968, -968, -968, -968, -968, 1169, -968, -968, -968, + -968, -968, -968, -968, -968, -968, -968, -968, -968, -968, + -968, -968, -968, -968, -968, -968, -968, -968, -968, -968, + -968, -968, -968, -968, -968, -968, -968, -968, -968, -968, + -968, -968, -968, -968, -968, -968, -968, -968, -968, -968, + -968, -968, -968, -968, -968, -968, -968, -968, -968, -968, + -968, -968, -968, -968, -968, -968, -968, -968 + }, + + { + 69, -969, -969, -969, -969, -969, -969, -969, -969, -969, + -969, -969, -969, -969, -969, -969, -969, -969, -969, -969, + -969, -969, -969, -969, -969, -969, -969, -969, -969, -969, -struct wcsprm *wcsbth_idx(struct wcsprm *wcs, struct wcsbth_alts *alts, - int keytype, int n, char a); -int wcsbth_colax(struct wcsprm *wcs, struct wcsbth_alts *alts, int k, char a); + -969, -969, 1170, -969, -969, -969, -969, -969, -969, -969, + -969, -969, -969, -969, -969, -969, -969, -969, -969, -969, + -969, -969, -969, -969, -969, -969, -969, -969, -969, -969, + -969, -969, -969, -969, -969, -969, -969, -969, -969, -969, + 1171, -969, -969, 1172, -969, -969, -969, -969, -969, -969, + -969, -969, -969, -969, -969, -969, -969, -969, -969, -969, + -969, -969, -969, -969, -969, -969, -969, -969, -969, -969, + -969, -969, -969, -969, -969, -969, -969, -969, -969, -969, + -969, -969, -969, -969, -969, -969, -969, -969, -969, -969, + -969, -969, -969, -969, -969, -969, -969, -969 + + }, + + { + 69, -970, -970, -970, -970, -970, -970, -970, -970, -970, + -970, -970, -970, -970, -970, -970, -970, -970, -970, -970, + -970, -970, -970, -970, -970, -970, -970, -970, -970, -970, + -970, -970, 1173, -970, -970, -970, -970, -970, -970, -970, + -970, -970, -970, -970, -970, -970, -970, -970, -970, -970, + -970, -970, -970, -970, -970, -970, -970, -970, -970, -970, + -970, -970, -970, -970, -970, -970, -970, -970, -970, -970, + -970, -970, -970, -970, -970, -970, -970, -970, -970, -970, + -970, -970, -970, -970, -970, -970, -970, -970, -970, -970, + -970, -970, -970, -970, -970, -970, -970, -970, -970, -970, -int wcsbth_epoch(void *wptr); -int wcsbth_vsource(void *wptr); + -970, -970, -970, -970, -970, -970, -970, -970, -970, -970, + -970, -970, -970, -970, -970, -970, -970, -970, -970, -970, + -970, -970, -970, -970, -970, -970, -970, -970 + }, + + { + 69, -971, -971, -971, -971, -971, -971, -971, -971, -971, + -971, -971, -971, -971, -971, -971, -971, -971, -971, -971, + -971, -971, -971, -971, -971, -971, -971, -971, -971, -971, + -971, -971, 1174, -971, -971, -971, -971, -971, -971, -971, + -971, -971, -971, -971, -971, -971, -971, -971, -971, -971, + -971, -971, -971, -971, -971, -971, -971, -971, -971, -971, + -971, -971, -971, -971, -971, -971, -971, -971, -971, -971, -int wcsbth_final(struct wcsbth_alts *alts, int *nwcs, struct wcsprm **wcs); + -971, -971, -971, -971, -971, -971, -971, -971, -971, -971, + -971, -971, -971, -971, -971, -971, -971, -971, -971, -971, + -971, -971, -971, -971, -971, -971, -971, -971, -971, -971, + -971, -971, -971, -971, -971, -971, -971, -971, -971, -971, + -971, -971, -971, -971, -971, -971, -971, -971, -971, -971, + -971, -971, -971, -971, -971, -971, -971, -971 + }, + + { + 69, -972, -972, -972, -972, -972, -972, -972, -972, -972, + -972, -972, -972, -972, -972, -972, -972, -972, -972, -972, + -972, -972, -972, -972, -972, -972, -972, -972, -972, -972, + -972, -972, 1175, -972, -972, -972, -972, -972, -972, -972, -#line 16845 "wcsbth.c" + -972, -972, -972, -972, -972, -972, -972, -972, -972, -972, + -972, -972, -972, -972, -972, -972, -972, -972, -972, -972, + -972, -972, -972, -972, -972, -972, -972, -972, -972, -972, + -972, -972, -972, -972, -972, -972, -972, -972, -972, -972, + -972, -972, -972, -972, -972, -972, -972, -972, -972, -972, + -972, -972, -972, -972, -972, -972, -972, -972, -972, -972, + -972, -972, -972, -972, -972, -972, -972, -972, -972, -972, + -972, -972, -972, -972, -972, -972, -972, -972, -972, -972, + -972, -972, -972, -972, -972, -972, -972, -972 + }, + + { + 69, -973, -973, -973, -973, -973, -973, -973, -973, -973, + + -973, -973, -973, -973, -973, -973, -973, -973, -973, -973, + -973, -973, -973, -973, -973, -973, -973, -973, -973, -973, + -973, -973, 1176, -973, -973, -973, -973, -973, -973, -973, + -973, -973, -973, -973, -973, -973, -973, -973, -973, -973, + -973, -973, -973, -973, -973, -973, -973, -973, -973, -973, + -973, -973, -973, -973, -973, -973, -973, -973, -973, -973, + -973, -973, -973, -973, -973, -973, -973, -973, -973, -973, + -973, -973, -973, -973, -973, -973, -973, -973, -973, -973, + -973, -973, -973, -973, -973, -973, -973, -973, -973, -973, + -973, -973, -973, -973, -973, -973, -973, -973, -973, -973, -#define INITIAL 0 -#define CCCCCia 1 -#define iCCCna 2 -#define iCCCCn 3 -#define TCCCna 4 -#define TCCCCn 5 -#define CCi_ja 6 -#define ijCCna 7 -#define TCn_ka 8 -#define TCCn_ka 9 -#define CROTAi 10 -#define iCROTn 11 -#define TCROTn 12 -#define CCi_ma 13 -#define iCn_ma 14 -#define iCCn_ma 15 -#define TCn_ma 16 -#define TCCn_ma 17 -#define PROJPm 18 -#define CCCCCCCC 19 -#define CCCCCCCa 20 -#define CCCCna 21 -#define CCCCCna 22 -#define CCCCn 23 -#define CCCCCn 24 -#define VALUE 25 -#define INTEGER_VAL 26 -#define FLOAT_VAL 27 -#define STRING_VAL 28 -#define COMMENT 29 -#define DISCARD 30 -#define ERROR 31 -#define FLUSH 32 + -973, -973, -973, -973, -973, -973, -973, -973, -973, -973, + -973, -973, -973, -973, -973, -973, -973, -973 + }, + + { + 69, -974, -974, -974, -974, -974, -974, -974, -974, -974, + -974, -974, -974, -974, -974, -974, -974, -974, -974, -974, + -974, -974, -974, -974, -974, -974, -974, -974, -974, -974, + -974, -974, -974, -974, -974, -974, -974, -974, -974, -974, + -974, -974, -974, -974, -974, -974, -974, -974, -974, -974, + -974, -974, -974, -974, -974, -974, -974, -974, -974, -974, + -974, -974, -974, -974, -974, -974, -974, -974, -974, 1177, + -974, -974, -974, -974, -974, -974, -974, -974, -974, -974, -#ifndef YY_NO_UNISTD_H -/* Special case for "unistd.h", since it is non-ANSI. We include it way - * down here because we want the user's section 1 to have been scanned first. - * The user has a chance to override it with an option. - */ -#include -#endif + -974, -974, -974, -974, -974, -974, -974, -974, -974, -974, + -974, -974, -974, -974, -974, -974, -974, -974, -974, -974, + -974, -974, -974, -974, -974, -974, -974, -974, -974, -974, + -974, -974, -974, -974, -974, -974, -974, -974, -974, -974, + -974, -974, -974, -974, -974, -974, -974, -974 + }, + + { + 69, -975, -975, -975, -975, -975, -975, -975, -975, -975, + -975, -975, -975, -975, -975, -975, -975, -975, -975, -975, + -975, -975, -975, -975, -975, -975, -975, -975, -975, -975, + -975, -975, -975, -975, -975, -975, -975, -975, -975, -975, + -975, -975, -975, -975, -975, -975, -975, -975, -975, -975, -#ifndef YY_EXTRA_TYPE -#define YY_EXTRA_TYPE void * -#endif + -975, -975, -975, -975, -975, -975, -975, -975, -975, -975, + -975, -975, -975, -975, -975, -975, -975, -975, -975, 1178, + -975, -975, -975, -975, -975, -975, -975, -975, -975, -975, + -975, -975, -975, -975, -975, -975, -975, -975, -975, -975, + -975, -975, -975, -975, -975, -975, -975, -975, -975, -975, + -975, -975, -975, -975, -975, -975, -975, -975, -975, -975, + -975, -975, -975, -975, -975, -975, -975, -975, -975, -975, + -975, -975, -975, -975, -975, -975, -975, -975 + }, + + { + 69, -976, -976, -976, -976, -976, -976, -976, -976, -976, + -976, -976, -976, -976, -976, -976, -976, -976, -976, -976, -static int yy_init_globals (void ); + -976, -976, -976, -976, -976, -976, -976, -976, -976, -976, + -976, -976, -976, -976, -976, -976, -976, -976, -976, -976, + -976, -976, -976, -976, -976, -976, -976, -976, -976, -976, + -976, -976, -976, -976, -976, -976, -976, -976, -976, -976, + -976, -976, -976, -976, -976, -976, -976, -976, -976, -976, + -976, 1179, -976, -976, -976, -976, -976, -976, -976, -976, + -976, -976, -976, -976, -976, -976, -976, -976, -976, -976, + -976, -976, -976, -976, -976, -976, -976, -976, -976, -976, + -976, -976, -976, -976, -976, -976, -976, -976, -976, -976, + -976, -976, -976, -976, -976, -976, -976, -976, -976, -976, + + -976, -976, -976, -976, -976, -976, -976, -976 + }, + + { + 69, -977, -977, -977, -977, -977, -977, -977, -977, -977, + -977, -977, -977, -977, -977, -977, -977, -977, -977, -977, + -977, -977, -977, -977, -977, -977, -977, -977, -977, -977, + -977, -977, -977, -977, -977, -977, -977, -977, -977, -977, + -977, -977, -977, -977, -977, -977, -977, -977, -977, -977, + -977, -977, -977, -977, -977, -977, -977, -977, -977, -977, + -977, -977, -977, -977, -977, -977, -977, -977, -977, -977, + -977, 1180, -977, -977, -977, -977, -977, -977, -977, -977, + -977, -977, -977, -977, -977, -977, -977, -977, -977, -977, -/* Accessor methods to globals. - These are made visible to non-reentrant scanners for convenience. */ + -977, -977, -977, -977, -977, -977, -977, -977, -977, -977, + -977, -977, -977, -977, -977, -977, -977, -977, -977, -977, + -977, -977, -977, -977, -977, -977, -977, -977, -977, -977, + -977, -977, -977, -977, -977, -977, -977, -977 + }, + + { + 69, -978, -978, -978, -978, -978, -978, -978, -978, -978, + -978, -978, -978, -978, -978, -978, -978, -978, -978, -978, + -978, -978, -978, -978, -978, -978, -978, -978, -978, -978, + -978, -978, -978, -978, -978, -978, -978, -978, -978, -978, + -978, -978, -978, -978, -978, -978, -978, -978, -978, -978, + -978, -978, -978, -978, -978, -978, -978, -978, -978, -978, -int wcsbthlex_destroy (void ); + -978, -978, -978, -978, -978, -978, -978, -978, 1181, -978, + -978, -978, -978, -978, -978, -978, -978, -978, -978, -978, + -978, -978, -978, -978, -978, -978, -978, -978, -978, -978, + -978, -978, -978, -978, -978, -978, -978, -978, -978, -978, + -978, -978, -978, -978, -978, -978, -978, -978, -978, -978, + -978, -978, -978, -978, -978, -978, -978, -978, -978, -978, + -978, -978, -978, -978, -978, -978, -978, -978 + }, + + { + 69, -979, -979, -979, -979, -979, -979, -979, -979, -979, + -979, -979, -979, -979, -979, -979, -979, -979, -979, -979, + -979, -979, -979, -979, -979, -979, -979, -979, -979, -979, -int wcsbthget_debug (void ); + -979, -979, -979, -979, -979, -979, -979, -979, -979, -979, + -979, -979, -979, -979, -979, -979, -979, -979, -979, -979, + -979, -979, -979, -979, -979, -979, -979, -979, -979, -979, + -979, -979, -979, -979, -979, -979, -979, -979, -979, -979, + -979, -979, -979, -979, -979, -979, -979, -979, -979, -979, + -979, -979, -979, 1182, -979, -979, -979, -979, -979, -979, + -979, -979, -979, -979, -979, -979, -979, -979, -979, -979, + -979, -979, -979, -979, -979, -979, -979, -979, -979, -979, + -979, -979, -979, -979, -979, -979, -979, -979, -979, -979, + -979, -979, -979, -979, -979, -979, -979, -979 + + }, + + { + 69, -980, -980, -980, -980, -980, -980, -980, -980, -980, + -980, -980, -980, -980, -980, -980, -980, -980, -980, -980, + -980, -980, -980, -980, -980, -980, -980, -980, -980, -980, + -980, -980, -980, -980, -980, -980, -980, -980, -980, -980, + -980, -980, -980, -980, -980, -980, -980, -980, -980, -980, + -980, -980, -980, -980, -980, -980, -980, -980, -980, -980, + -980, -980, -980, -980, -980, -980, -980, -980, -980, -980, + 1183, -980, -980, -980, -980, -980, -980, -980, -980, -980, + -980, -980, -980, -980, -980, -980, -980, -980, -980, -980, + -980, -980, -980, -980, -980, -980, -980, -980, -980, -980, -void wcsbthset_debug (int debug_flag ); + -980, -980, -980, -980, -980, -980, -980, -980, -980, -980, + -980, -980, -980, -980, -980, -980, -980, -980, -980, -980, + -980, -980, -980, -980, -980, -980, -980, -980 + }, + + { + 69, -981, -981, -981, -981, -981, -981, -981, -981, -981, + -981, -981, -981, -981, -981, -981, -981, -981, -981, -981, + -981, -981, -981, -981, -981, -981, -981, -981, -981, -981, + -981, -981, 1184, -981, -981, -981, -981, -981, -981, -981, + -981, -981, -981, -981, -981, -981, -981, -981, -981, -981, + -981, -981, -981, -981, -981, -981, -981, -981, -981, -981, + -981, -981, -981, -981, -981, -981, -981, -981, -981, -981, -YY_EXTRA_TYPE wcsbthget_extra (void ); + 1185, -981, -981, 1186, -981, -981, -981, -981, -981, -981, + -981, -981, -981, -981, -981, -981, -981, -981, -981, -981, + -981, -981, -981, -981, -981, -981, -981, -981, -981, -981, + -981, -981, -981, -981, -981, -981, -981, -981, -981, -981, + -981, -981, -981, -981, -981, -981, -981, -981, -981, -981, + -981, -981, -981, -981, -981, -981, -981, -981 + }, + + { + 69, -982, -982, -982, -982, -982, -982, -982, -982, -982, + -982, -982, -982, -982, -982, -982, -982, -982, -982, -982, + -982, -982, -982, -982, -982, -982, -982, -982, -982, -982, + -982, -982, 1187, -982, -982, -982, -982, -982, -982, -982, -void wcsbthset_extra (YY_EXTRA_TYPE user_defined ); + -982, -982, -982, -982, -982, -982, -982, -982, 1188, 1188, + 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, -982, -982, + -982, -982, -982, -982, -982, -982, -982, -982, -982, -982, + -982, -982, -982, -982, -982, -982, -982, -982, -982, -982, + -982, -982, -982, -982, -982, -982, -982, -982, -982, -982, + -982, -982, -982, -982, -982, -982, -982, -982, -982, -982, + -982, -982, -982, -982, -982, -982, -982, -982, -982, -982, + -982, -982, -982, -982, -982, -982, -982, -982, -982, -982, + -982, -982, -982, -982, -982, -982, -982, -982 + }, + + { + 69, -983, -983, -983, -983, -983, -983, -983, -983, -983, + + -983, -983, -983, -983, -983, -983, -983, -983, -983, -983, + -983, -983, -983, -983, -983, -983, -983, -983, -983, -983, + -983, -983, -983, -983, -983, -983, -983, -983, -983, -983, + -983, -983, -983, -983, -983, 1189, -983, -983, -983, -983, + -983, -983, -983, -983, -983, -983, -983, -983, -983, -983, + -983, -983, -983, -983, -983, -983, -983, -983, -983, -983, + -983, -983, -983, -983, -983, -983, -983, -983, -983, -983, + -983, -983, -983, -983, -983, -983, -983, -983, -983, -983, + -983, -983, -983, -983, -983, -983, -983, -983, -983, -983, + -983, -983, -983, -983, -983, -983, -983, -983, -983, -983, -FILE *wcsbthget_in (void ); + -983, -983, -983, -983, -983, -983, -983, -983, -983, -983, + -983, -983, -983, -983, -983, -983, -983, -983 + }, + + { + 69, -984, -984, -984, -984, -984, -984, -984, -984, -984, + -984, -984, -984, -984, -984, -984, -984, -984, -984, -984, + -984, -984, -984, -984, -984, -984, -984, -984, -984, -984, + -984, -984, 1190, -984, -984, -984, -984, -984, -984, -984, + -984, -984, -984, -984, -984, -984, -984, -984, 1191, 1191, + 1191, 1191, 1191, 1191, 1191, 1191, 1191, 1191, -984, -984, + -984, -984, -984, -984, -984, -984, -984, -984, -984, -984, + -984, -984, -984, -984, -984, -984, -984, -984, -984, -984, -void wcsbthset_in (FILE * in_str ); + -984, -984, -984, -984, -984, -984, -984, -984, -984, -984, + -984, -984, -984, -984, -984, -984, -984, -984, -984, -984, + -984, -984, -984, -984, -984, -984, -984, -984, -984, -984, + -984, -984, -984, -984, -984, -984, -984, -984, -984, -984, + -984, -984, -984, -984, -984, -984, -984, -984 + }, + + { + 69, -985, -985, -985, -985, -985, -985, -985, -985, -985, + -985, -985, -985, -985, -985, -985, -985, -985, -985, -985, + -985, -985, -985, -985, -985, -985, -985, -985, -985, -985, + -985, -985, 1192, -985, -985, -985, -985, -985, -985, -985, + -985, -985, -985, -985, -985, -985, -985, -985, 1193, 1193, -FILE *wcsbthget_out (void ); + 1193, 1193, 1193, 1193, 1193, 1193, 1193, 1193, -985, -985, + -985, -985, -985, -985, -985, -985, -985, -985, -985, -985, + -985, -985, -985, -985, -985, -985, -985, -985, -985, -985, + -985, -985, -985, -985, -985, -985, -985, -985, -985, -985, + -985, -985, -985, -985, -985, -985, -985, -985, -985, -985, + -985, -985, -985, -985, -985, -985, -985, -985, -985, -985, + -985, -985, -985, -985, -985, -985, -985, -985, -985, -985, + -985, -985, -985, -985, -985, -985, -985, -985 + }, + + { + 69, -986, -986, -986, -986, -986, -986, -986, -986, -986, + -986, -986, -986, -986, -986, -986, -986, -986, -986, -986, -void wcsbthset_out (FILE * out_str ); + -986, -986, -986, -986, -986, -986, -986, -986, -986, -986, + -986, -986, -986, -986, -986, -986, -986, -986, -986, -986, + -986, -986, -986, -986, -986, -986, -986, -986, -986, -986, + -986, -986, -986, -986, -986, -986, -986, -986, -986, -986, + -986, -986, -986, -986, -986, -986, -986, -986, -986, -986, + -986, -986, -986, 1194, -986, -986, -986, -986, -986, -986, + -986, -986, -986, -986, -986, -986, -986, -986, -986, -986, + -986, -986, -986, -986, -986, -986, -986, -986, -986, -986, + -986, -986, -986, -986, -986, -986, -986, -986, -986, -986, + -986, -986, -986, -986, -986, -986, -986, -986, -986, -986, + + -986, -986, -986, -986, -986, -986, -986, -986 + }, + + { + 69, -987, -987, -987, -987, -987, -987, -987, -987, -987, + -987, -987, -987, -987, -987, -987, -987, -987, -987, -987, + -987, -987, -987, -987, -987, -987, -987, -987, -987, -987, + -987, -987, -987, -987, -987, -987, -987, -987, -987, -987, + -987, -987, -987, -987, -987, -987, -987, -987, -987, -987, + -987, -987, -987, -987, -987, -987, -987, -987, -987, -987, + -987, -987, -987, -987, -987, -987, -987, -987, -987, -987, + -987, -987, -987, -987, -987, -987, -987, 1195, -987, -987, + -987, -987, -987, -987, -987, -987, -987, -987, -987, -987, -int wcsbthget_leng (void ); + -987, -987, -987, -987, -987, -987, -987, -987, -987, -987, + -987, -987, -987, -987, -987, -987, -987, -987, -987, -987, + -987, -987, -987, -987, -987, -987, -987, -987, -987, -987, + -987, -987, -987, -987, -987, -987, -987, -987 + }, + + { + 69, -988, -988, -988, -988, -988, -988, -988, -988, -988, + -988, -988, -988, -988, -988, -988, -988, -988, -988, -988, + -988, -988, -988, -988, -988, -988, -988, -988, -988, -988, + -988, -988, -988, -988, -988, -988, -988, -988, -988, -988, + -988, -988, -988, -988, -988, -988, -988, -988, -988, -988, + -988, -988, -988, -988, -988, -988, -988, -988, -988, -988, -char *wcsbthget_text (void ); + -988, -988, -988, -988, -988, -988, -988, -988, -988, -988, + -988, -988, -988, -988, -988, -988, -988, -988, -988, -988, + -988, -988, -988, -988, -988, -988, -988, -988, -988, 1196, + -988, -988, -988, -988, -988, -988, -988, -988, -988, -988, + -988, -988, -988, -988, -988, -988, -988, -988, -988, -988, + -988, -988, -988, -988, -988, -988, -988, -988, -988, -988, + -988, -988, -988, -988, -988, -988, -988, -988 + }, + + { + 69, -989, -989, -989, -989, -989, -989, -989, -989, -989, + -989, -989, -989, -989, -989, -989, -989, -989, -989, -989, + -989, -989, -989, -989, -989, -989, -989, -989, -989, -989, -int wcsbthget_lineno (void ); + -989, -989, -989, -989, -989, -989, -989, -989, -989, -989, + -989, -989, -989, -989, -989, -989, -989, -989, -989, -989, + -989, -989, -989, -989, -989, -989, -989, -989, -989, -989, + -989, -989, -989, -989, -989, -989, -989, -989, -989, -989, + -989, -989, -989, -989, -989, -989, -989, -989, -989, -989, + -989, -989, -989, 1197, -989, -989, -989, -989, -989, -989, + -989, -989, -989, -989, -989, -989, -989, -989, -989, -989, + -989, -989, -989, -989, -989, -989, -989, -989, -989, -989, + -989, -989, -989, -989, -989, -989, -989, -989, -989, -989, + -989, -989, -989, -989, -989, -989, -989, -989 + + }, + + { + 69, -990, -990, -990, -990, -990, -990, -990, -990, -990, + -990, -990, -990, -990, -990, -990, -990, -990, -990, -990, + -990, -990, -990, -990, -990, -990, -990, -990, -990, -990, + -990, -990, -990, -990, -990, -990, -990, -990, -990, -990, + -990, -990, -990, -990, -990, -990, -990, -990, -990, -990, + -990, -990, -990, -990, -990, -990, -990, -990, -990, -990, + -990, -990, -990, -990, -990, -990, -990, -990, -990, 1198, + -990, -990, -990, -990, -990, -990, -990, -990, -990, -990, + -990, 1199, -990, -990, -990, -990, -990, -990, -990, -990, + -990, -990, -990, -990, -990, -990, -990, -990, -990, -990, -void wcsbthset_lineno (int line_number ); + -990, -990, -990, -990, -990, -990, -990, -990, -990, -990, + -990, -990, -990, -990, -990, -990, -990, -990, -990, -990, + -990, -990, -990, -990, -990, -990, -990, -990 + }, + + { + 69, -991, -991, -991, -991, -991, -991, -991, -991, -991, + -991, -991, -991, -991, -991, -991, -991, -991, -991, -991, + -991, -991, -991, -991, -991, -991, -991, -991, -991, -991, + -991, -991, -991, -991, -991, -991, -991, -991, -991, -991, + -991, -991, -991, -991, -991, -991, -991, -991, -991, -991, + -991, -991, -991, -991, -991, -991, -991, -991, -991, -991, + -991, -991, -991, -991, -991, -991, -991, -991, -991, -991, -/* Macros after this point can all be overridden by user definitions in - * section 1. - */ + -991, -991, -991, -991, -991, -991, -991, -991, -991, -991, + -991, -991, -991, -991, -991, -991, 1200, -991, -991, -991, + -991, -991, -991, -991, -991, -991, -991, -991, -991, -991, + -991, -991, -991, -991, -991, -991, -991, -991, -991, -991, + -991, -991, -991, -991, -991, -991, -991, -991, -991, -991, + -991, -991, -991, -991, -991, -991, -991, -991 + }, + + { + 69, -992, -992, -992, -992, -992, -992, -992, -992, -992, + -992, -992, -992, -992, -992, -992, -992, -992, -992, -992, + -992, -992, -992, -992, -992, -992, -992, -992, -992, -992, + -992, -992, -992, -992, -992, -992, -992, -992, -992, -992, -#ifndef YY_SKIP_YYWRAP -#ifdef __cplusplus -extern "C" int wcsbthwrap (void ); -#else -extern int wcsbthwrap (void ); -#endif -#endif + -992, -992, -992, -992, -992, -992, -992, -992, -992, -992, + -992, -992, -992, -992, -992, -992, -992, -992, -992, -992, + -992, -992, -992, -992, -992, -992, -992, -992, -992, 1201, + -992, -992, -992, -992, -992, -992, -992, -992, -992, -992, + -992, -992, -992, -992, -992, -992, -992, -992, -992, -992, + -992, -992, -992, -992, -992, -992, -992, -992, -992, -992, + -992, -992, -992, -992, -992, -992, -992, -992, -992, -992, + -992, -992, -992, -992, -992, -992, -992, -992, -992, -992, + -992, -992, -992, -992, -992, -992, -992, -992 + }, + + { + 69, -993, -993, -993, -993, -993, -993, -993, -993, -993, + + -993, -993, -993, -993, -993, -993, -993, -993, -993, -993, + -993, -993, -993, -993, -993, -993, -993, -993, -993, -993, + -993, -993, -993, -993, -993, -993, -993, -993, -993, -993, + -993, -993, -993, -993, -993, -993, -993, -993, -993, -993, + -993, -993, -993, -993, -993, -993, -993, -993, -993, -993, + -993, -993, -993, -993, -993, -993, -993, -993, -993, -993, + -993, -993, -993, -993, -993, -993, -993, -993, -993, -993, + -993, -993, -993, 1202, -993, -993, -993, -993, -993, -993, + -993, -993, -993, -993, -993, -993, -993, -993, -993, -993, + -993, -993, -993, -993, -993, -993, -993, -993, -993, -993, - static void yyunput (int c,char *buf_ptr ); - -#ifndef yytext_ptr -static void yy_flex_strncpy (char *,yyconst char *,int ); -#endif + -993, -993, -993, -993, -993, -993, -993, -993, -993, -993, + -993, -993, -993, -993, -993, -993, -993, -993 + }, + + { + 69, -994, -994, -994, -994, -994, -994, -994, -994, -994, + -994, -994, -994, -994, -994, -994, -994, -994, -994, -994, + -994, -994, -994, -994, -994, -994, -994, -994, -994, -994, + -994, -994, -994, -994, -994, -994, -994, -994, -994, -994, + -994, -994, -994, -994, -994, -994, -994, -994, -994, -994, + -994, -994, -994, -994, -994, -994, -994, -994, -994, -994, + -994, -994, -994, -994, -994, -994, -994, -994, -994, -994, + -994, -994, -994, -994, -994, -994, -994, -994, -994, -994, -#ifdef YY_NEED_STRLEN -static int yy_flex_strlen (yyconst char * ); -#endif + -994, -994, -994, 1203, -994, -994, -994, -994, -994, -994, + -994, -994, -994, -994, -994, -994, -994, -994, -994, -994, + -994, -994, -994, -994, -994, -994, -994, -994, -994, -994, + -994, -994, -994, -994, -994, -994, -994, -994, -994, -994, + -994, -994, -994, -994, -994, -994, -994, -994 + }, + + { + 69, -995, -995, -995, -995, -995, -995, -995, -995, -995, + -995, -995, -995, -995, -995, -995, -995, -995, -995, -995, + -995, -995, -995, -995, -995, -995, -995, -995, -995, -995, + -995, -995, -995, -995, -995, -995, -995, -995, -995, -995, + -995, -995, -995, -995, -995, -995, -995, -995, -995, -995, -#ifndef YY_NO_INPUT + -995, -995, -995, -995, -995, -995, -995, -995, -995, -995, + -995, -995, -995, -995, -995, -995, -995, 1204, -995, -995, + -995, -995, -995, -995, -995, -995, -995, -995, -995, -995, + -995, -995, -995, -995, -995, -995, -995, -995, -995, -995, + -995, -995, -995, -995, -995, -995, -995, -995, -995, -995, + -995, -995, -995, -995, -995, -995, -995, -995, -995, -995, + -995, -995, -995, -995, -995, -995, -995, -995, -995, -995, + -995, -995, -995, -995, -995, -995, -995, -995 + }, + + { + 69, -996, -996, -996, -996, -996, -996, -996, -996, -996, + -996, -996, -996, -996, -996, -996, -996, -996, -996, -996, -#ifdef __cplusplus -static int yyinput (void ); -#else -static int input (void ); -#endif + -996, -996, -996, -996, -996, -996, -996, -996, -996, -996, + -996, -996, -996, -996, -996, -996, -996, -996, -996, -996, + -996, -996, -996, -996, -996, -996, -996, -996, -996, -996, + -996, -996, -996, -996, -996, -996, -996, -996, -996, -996, + -996, -996, -996, -996, -996, -996, -996, -996, -996, 1205, + -996, -996, -996, -996, -996, -996, -996, -996, -996, -996, + -996, -996, -996, -996, -996, -996, -996, -996, -996, -996, + -996, -996, -996, -996, -996, -996, -996, -996, -996, -996, + -996, -996, -996, -996, -996, -996, -996, -996, -996, -996, + -996, -996, -996, -996, -996, -996, -996, -996, -996, -996, + + -996, -996, -996, -996, -996, -996, -996, -996 + }, + + { + 69, -997, -997, -997, -997, -997, -997, -997, -997, -997, + -997, -997, -997, -997, -997, -997, -997, -997, -997, -997, + -997, -997, -997, -997, -997, -997, -997, -997, -997, -997, + -997, -997, -997, -997, -997, -997, -997, -997, -997, -997, + -997, -997, -997, -997, -997, -997, -997, -997, -997, -997, + -997, -997, -997, -997, -997, -997, -997, -997, -997, -997, + -997, -997, -997, -997, -997, -997, -997, -997, -997, -997, + -997, -997, -997, -997, -997, -997, -997, -997, -997, -997, + -997, -997, -997, 1206, -997, -997, -997, -997, -997, -997, -#endif + -997, -997, -997, -997, -997, -997, -997, -997, -997, -997, + -997, -997, -997, -997, -997, -997, -997, -997, -997, -997, + -997, -997, -997, -997, -997, -997, -997, -997, -997, -997, + -997, -997, -997, -997, -997, -997, -997, -997 + }, + + { + 69, -998, -998, -998, -998, -998, -998, -998, -998, -998, + -998, -998, -998, -998, -998, -998, -998, -998, -998, -998, + -998, -998, -998, -998, -998, -998, -998, -998, -998, -998, + -998, -998, -998, -998, -998, -998, -998, -998, -998, -998, + -998, -998, -998, -998, -998, -998, -998, -998, -998, -998, + -998, -998, -998, -998, -998, -998, -998, -998, -998, -998, -/* Amount of stuff to slurp up with each read. */ -#ifndef YY_READ_BUF_SIZE -#ifdef __ia64__ -/* On IA-64, the buffer size is 16k, not 8k */ -#define YY_READ_BUF_SIZE 16384 -#else -#define YY_READ_BUF_SIZE 8192 -#endif /* __ia64__ */ -#endif + -998, -998, -998, -998, -998, -998, -998, -998, -998, -998, + -998, -998, -998, -998, -998, -998, 1207, -998, -998, -998, + -998, -998, -998, -998, -998, -998, -998, -998, -998, -998, + -998, -998, -998, -998, -998, -998, -998, -998, -998, -998, + -998, -998, -998, -998, -998, -998, -998, -998, -998, -998, + -998, -998, -998, -998, -998, -998, -998, -998, -998, -998, + -998, -998, -998, -998, -998, -998, -998, -998 + }, + + { + 69, -999, -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -/* Copy whatever the last rule matched to the standard output. */ -#ifndef ECHO -/* This used to be an fputs(), but since the string might contain NUL's, - * we now use fwrite(). - */ -#define ECHO do { if (fwrite( wcsbthtext, wcsbthleng, 1, wcsbthout )) {} } while (0) -#endif + -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, + 1208, -999, -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999 + + }, + + { + 69,-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000, + -1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000, + -1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000, + -1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000, + -1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000, + -1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000, + -1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000, + -1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000, + -1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000, 1209,-1000, + -1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000, -/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, - * is returned in "result". - */ -#ifndef YY_INPUT -#define YY_INPUT(buf,result,max_size) \ - errno=0; \ - while ( (result = read( fileno(wcsbthin), (char *) buf, max_size )) < 0 ) \ - { \ - if( errno != EINTR) \ - { \ - YY_FATAL_ERROR( "input in flex scanner failed" ); \ - break; \ - } \ - errno=0; \ - clearerr(wcsbthin); \ - }\ -\ + -1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000, + -1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000, + -1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000 + }, + + { + 69,-1001,-1001,-1001,-1001,-1001,-1001,-1001,-1001,-1001, + -1001,-1001,-1001,-1001,-1001,-1001,-1001,-1001,-1001,-1001, + -1001,-1001,-1001,-1001,-1001,-1001,-1001,-1001,-1001,-1001, + -1001,-1001,-1001,-1001,-1001,-1001,-1001,-1001,-1001,-1001, + -1001,-1001,-1001,-1001,-1001,-1001,-1001,-1001,-1001,-1001, + -1001,-1001,-1001,-1001,-1001,-1001,-1001,-1001,-1001,-1001, + -1001,-1001,-1001,-1001,-1001,-1001,-1001,-1001,-1001,-1001, -#endif + -1001,-1001,-1001,-1001,-1001,-1001,-1001,-1001,-1001,-1001, + -1001,-1001,-1001, 1210,-1001,-1001,-1001,-1001,-1001,-1001, + -1001,-1001,-1001,-1001,-1001,-1001,-1001,-1001,-1001,-1001, + -1001,-1001,-1001,-1001,-1001,-1001,-1001,-1001,-1001,-1001, + -1001,-1001,-1001,-1001,-1001,-1001,-1001,-1001,-1001,-1001, + -1001,-1001,-1001,-1001,-1001,-1001,-1001,-1001 + }, + + { + 69,-1002,-1002,-1002,-1002,-1002,-1002,-1002,-1002,-1002, + -1002,-1002,-1002,-1002,-1002,-1002,-1002,-1002,-1002,-1002, + -1002,-1002,-1002,-1002,-1002,-1002,-1002,-1002,-1002,-1002, + -1002,-1002,-1002,-1002,-1002,-1002,-1002,-1002,-1002,-1002, -/* No semi-colon after return; correct usage is to write "yyterminate();" - - * we don't want an extra ';' after the "return" because that will cause - * some compilers to complain about unreachable statements. - */ -#ifndef yyterminate -#define yyterminate() return YY_NULL -#endif + -1002,-1002,-1002,-1002,-1002,-1002,-1002,-1002,-1002,-1002, + -1002,-1002,-1002,-1002,-1002,-1002,-1002,-1002,-1002,-1002, + -1002,-1002,-1002,-1002,-1002,-1002,-1002,-1002,-1002,-1002, + -1002,-1002,-1002, 1211,-1002,-1002,-1002,-1002,-1002,-1002, + -1002,-1002,-1002,-1002,-1002,-1002,-1002,-1002,-1002,-1002, + -1002,-1002,-1002,-1002,-1002,-1002,-1002,-1002,-1002,-1002, + -1002,-1002,-1002,-1002,-1002,-1002,-1002,-1002,-1002,-1002, + -1002,-1002,-1002,-1002,-1002,-1002,-1002,-1002,-1002,-1002, + -1002,-1002,-1002,-1002,-1002,-1002,-1002,-1002 + }, + + { + 69,-1003,-1003,-1003,-1003,-1003,-1003,-1003,-1003,-1003, + + -1003,-1003,-1003,-1003,-1003,-1003,-1003,-1003,-1003,-1003, + -1003,-1003,-1003,-1003,-1003,-1003,-1003,-1003,-1003,-1003, + -1003,-1003,-1003,-1003,-1003,-1003,-1003,-1003,-1003,-1003, + -1003,-1003,-1003,-1003,-1003,-1003,-1003,-1003,-1003,-1003, + -1003,-1003,-1003,-1003,-1003,-1003,-1003,-1003,-1003,-1003, + -1003,-1003,-1003,-1003,-1003,-1003,-1003,-1003,-1003,-1003, + -1003,-1003,-1003,-1003,-1003,-1003,-1003,-1003,-1003,-1003, + -1003,-1003, 1212,-1003,-1003,-1003,-1003,-1003,-1003,-1003, + -1003,-1003,-1003,-1003,-1003,-1003,-1003,-1003,-1003,-1003, + -1003,-1003,-1003,-1003,-1003,-1003,-1003,-1003,-1003,-1003, -/* Number of entries by which start-condition stack grows. */ -#ifndef YY_START_STACK_INCR -#define YY_START_STACK_INCR 25 -#endif + -1003,-1003,-1003,-1003,-1003,-1003,-1003,-1003,-1003,-1003, + -1003,-1003,-1003,-1003,-1003,-1003,-1003,-1003 + }, + + { + 69,-1004,-1004,-1004,-1004,-1004,-1004,-1004,-1004,-1004, + -1004,-1004,-1004,-1004,-1004,-1004,-1004,-1004,-1004,-1004, + -1004,-1004,-1004,-1004,-1004,-1004,-1004,-1004,-1004,-1004, + -1004,-1004,-1004,-1004,-1004,-1004,-1004,-1004,-1004,-1004, + -1004,-1004,-1004,-1004,-1004,-1004,-1004,-1004,-1004,-1004, + -1004,-1004,-1004,-1004,-1004,-1004,-1004,-1004,-1004,-1004, + -1004,-1004,-1004,-1004,-1004,-1004,-1004,-1004,-1004,-1004, + -1004,-1004,-1004,-1004,-1004,-1004,-1004,-1004,-1004,-1004, -/* Report a fatal error. */ -#ifndef YY_FATAL_ERROR -#define YY_FATAL_ERROR(msg) yy_fatal_error( msg ) -#endif + -1004,-1004, 1213,-1004,-1004,-1004,-1004,-1004,-1004,-1004, + -1004,-1004,-1004,-1004,-1004,-1004,-1004,-1004,-1004,-1004, + -1004,-1004,-1004,-1004,-1004,-1004,-1004,-1004,-1004,-1004, + -1004,-1004,-1004,-1004,-1004,-1004,-1004,-1004,-1004,-1004, + -1004,-1004,-1004,-1004,-1004,-1004,-1004,-1004 + }, + + { + 69,-1005,-1005,-1005,-1005,-1005,-1005,-1005,-1005,-1005, + -1005,-1005,-1005,-1005,-1005,-1005,-1005,-1005,-1005,-1005, + -1005,-1005,-1005,-1005,-1005,-1005,-1005,-1005,-1005,-1005, + -1005,-1005,-1005,-1005,-1005,-1005,-1005,-1005,-1005,-1005, + -1005,-1005,-1005,-1005,-1005,-1005,-1005,-1005,-1005,-1005, -/* end tables serialization structures and prototypes */ + -1005,-1005,-1005,-1005,-1005,-1005,-1005,-1005,-1005,-1005, + -1005,-1005,-1005,-1005,-1005,-1005,-1005,-1005,-1005,-1005, + -1005,-1005,-1005,-1005,-1005,-1005,-1005,-1005,-1005,-1005, + -1005,-1005, 1214,-1005,-1005,-1005,-1005,-1005,-1005,-1005, + -1005,-1005,-1005,-1005,-1005,-1005,-1005,-1005,-1005,-1005, + -1005,-1005,-1005,-1005,-1005,-1005,-1005,-1005,-1005,-1005, + -1005,-1005,-1005,-1005,-1005,-1005,-1005,-1005,-1005,-1005, + -1005,-1005,-1005,-1005,-1005,-1005,-1005,-1005 + }, + + { + 69,-1006,-1006,-1006,-1006,-1006,-1006,-1006,-1006,-1006, + -1006,-1006,-1006,-1006,-1006,-1006,-1006,-1006,-1006,-1006, -/* Default declaration of generated scanner - a define so the user can - * easily add parameters. - */ -#ifndef YY_DECL -#define YY_DECL_IS_OURS 1 + -1006,-1006,-1006,-1006,-1006,-1006,-1006,-1006,-1006,-1006, + -1006,-1006,-1006,-1006,-1006,-1006,-1006,-1006,-1006,-1006, + -1006,-1006,-1006,-1006,-1006,-1006,-1006,-1006,-1006,-1006, + -1006,-1006,-1006,-1006,-1006,-1006,-1006,-1006,-1006,-1006, + -1006,-1006,-1006,-1006,-1006,-1006,-1006,-1006,-1006,-1006, + -1006,-1006,-1006,-1006,-1006,-1006,-1006,-1006,-1006,-1006, + -1006,-1006,-1006, 1215,-1006,-1006,-1006,-1006,-1006,-1006, + -1006,-1006,-1006,-1006,-1006,-1006,-1006,-1006,-1006,-1006, + -1006,-1006,-1006,-1006,-1006,-1006,-1006,-1006,-1006,-1006, + -1006,-1006,-1006,-1006,-1006,-1006,-1006,-1006,-1006,-1006, + + -1006,-1006,-1006,-1006,-1006,-1006,-1006,-1006 + }, + + { + 69,-1007,-1007,-1007,-1007,-1007,-1007,-1007,-1007,-1007, + -1007,-1007,-1007,-1007,-1007,-1007,-1007,-1007,-1007,-1007, + -1007,-1007,-1007,-1007,-1007,-1007,-1007,-1007,-1007,-1007, + -1007,-1007, 1216,-1007,-1007,-1007,-1007,-1007,-1007,-1007, + -1007,-1007,-1007,-1007,-1007,-1007,-1007,-1007,-1007,-1007, + -1007,-1007,-1007,-1007,-1007,-1007,-1007,-1007,-1007,-1007, + -1007,-1007,-1007,-1007,-1007,-1007,-1007,-1007,-1007,-1007, + -1007,-1007,-1007,-1007,-1007,-1007,-1007,-1007,-1007,-1007, + -1007,-1007,-1007,-1007,-1007,-1007,-1007,-1007,-1007,-1007, -extern int wcsbthlex (void); + -1007,-1007,-1007,-1007,-1007,-1007,-1007,-1007,-1007,-1007, + -1007,-1007,-1007,-1007,-1007,-1007,-1007,-1007,-1007,-1007, + -1007,-1007,-1007,-1007,-1007,-1007,-1007,-1007,-1007,-1007, + -1007,-1007,-1007,-1007,-1007,-1007,-1007,-1007 + }, + + { + 69,-1008,-1008,-1008,-1008,-1008,-1008,-1008,-1008,-1008, + -1008,-1008,-1008,-1008,-1008,-1008,-1008,-1008,-1008,-1008, + -1008,-1008,-1008,-1008,-1008,-1008,-1008,-1008,-1008,-1008, + -1008,-1008, 1217,-1008,-1008,-1008,-1008,-1008,-1008,-1008, + -1008,-1008,-1008,-1008,-1008,-1008,-1008,-1008,-1008,-1008, + -1008,-1008,-1008,-1008,-1008,-1008,-1008,-1008,-1008,-1008, -#define YY_DECL int wcsbthlex (void) -#endif /* !YY_DECL */ + -1008,-1008,-1008,-1008,-1008,-1008,-1008,-1008,-1008,-1008, + -1008,-1008,-1008,-1008,-1008,-1008,-1008,-1008,-1008,-1008, + -1008,-1008,-1008,-1008,-1008,-1008,-1008,-1008,-1008,-1008, + -1008,-1008,-1008,-1008,-1008,-1008,-1008,-1008,-1008,-1008, + -1008,-1008,-1008,-1008,-1008,-1008,-1008,-1008,-1008,-1008, + -1008,-1008,-1008,-1008,-1008,-1008,-1008,-1008,-1008,-1008, + -1008,-1008,-1008,-1008,-1008,-1008,-1008,-1008 + }, + + { + 69,-1009,-1009,-1009,-1009,-1009,-1009,-1009,-1009,-1009, + -1009,-1009,-1009,-1009,-1009,-1009,-1009,-1009,-1009,-1009, + -1009,-1009,-1009,-1009,-1009,-1009,-1009,-1009,-1009,-1009, -/* Code executed at the beginning of each rule, after wcsbthtext and wcsbthleng - * have been set up. - */ -#ifndef YY_USER_ACTION -#define YY_USER_ACTION -#endif + -1009,-1009,-1009,-1009,-1009,-1009,-1009,-1009,-1009,-1009, + -1009,-1009,-1009,-1009,-1009,-1009,-1009,-1009,-1009,-1009, + -1009,-1009,-1009,-1009,-1009,-1009,-1009,-1009,-1009,-1009, + -1009,-1009,-1009,-1009,-1009,-1009,-1009,-1009,-1009,-1009, + -1009,-1009,-1009,-1009,-1009,-1009, 1218,-1009,-1009,-1009, + -1009,-1009,-1009,-1009,-1009,-1009,-1009,-1009,-1009,-1009, + -1009,-1009,-1009,-1009,-1009,-1009,-1009,-1009,-1009,-1009, + -1009,-1009,-1009,-1009,-1009,-1009,-1009,-1009,-1009,-1009, + -1009,-1009,-1009,-1009,-1009,-1009,-1009,-1009,-1009,-1009, + -1009,-1009,-1009,-1009,-1009,-1009,-1009,-1009 + + }, + + { + 69,-1010,-1010,-1010,-1010,-1010,-1010,-1010,-1010,-1010, + -1010,-1010,-1010,-1010,-1010,-1010,-1010,-1010,-1010,-1010, + -1010,-1010,-1010,-1010,-1010,-1010,-1010,-1010,-1010,-1010, + -1010,-1010,-1010,-1010,-1010,-1010,-1010,-1010,-1010,-1010, + -1010,-1010,-1010,-1010,-1010,-1010,-1010,-1010,-1010,-1010, + -1010,-1010,-1010,-1010,-1010,-1010,-1010,-1010,-1010,-1010, + -1010,-1010,-1010,-1010,-1010,-1010,-1010,-1010,-1010,-1010, + -1010,-1010,-1010,-1010,-1010,-1010,-1010,-1010,-1010,-1010, + -1010,-1010,-1010, 1219,-1010,-1010,-1010,-1010,-1010,-1010, + -1010,-1010,-1010,-1010,-1010,-1010,-1010,-1010,-1010,-1010, -/* Code executed at the end of each rule. */ -#ifndef YY_BREAK -#define YY_BREAK break; -#endif + -1010,-1010,-1010,-1010,-1010,-1010,-1010,-1010,-1010,-1010, + -1010,-1010,-1010,-1010,-1010,-1010,-1010,-1010,-1010,-1010, + -1010,-1010,-1010,-1010,-1010,-1010,-1010,-1010 + }, + + { + 69,-1011,-1011,-1011,-1011,-1011,-1011,-1011,-1011,-1011, + -1011,-1011,-1011,-1011,-1011,-1011,-1011,-1011,-1011,-1011, + -1011,-1011,-1011,-1011,-1011,-1011,-1011,-1011,-1011,-1011, + -1011,-1011, 1220,-1011,-1011,-1011,-1011,-1011,-1011,-1011, + -1011,-1011,-1011,-1011,-1011,-1011,-1011,-1011,-1011,-1011, + -1011,-1011,-1011,-1011,-1011,-1011,-1011,-1011,-1011,-1011, + -1011,-1011,-1011,-1011,-1011, 1220, 1220, 1220, 1220, 1220, -#define YY_RULE_SETUP \ - if ( wcsbthleng > 0 ) \ - YY_CURRENT_BUFFER_LVALUE->yy_at_bol = \ - (wcsbthtext[wcsbthleng - 1] == '\n'); \ - YY_USER_ACTION + 1220, 1220, 1220, 1220, 1220, 1220, 1220, 1220, 1220, 1220, + 1220, 1220, 1220, 1220, 1220, 1220, 1220, 1220, 1220, 1220, + 1220,-1011,-1011,-1011,-1011,-1011,-1011,-1011,-1011,-1011, + -1011,-1011,-1011,-1011,-1011,-1011,-1011,-1011,-1011,-1011, + -1011,-1011,-1011,-1011,-1011,-1011,-1011,-1011,-1011,-1011, + -1011,-1011,-1011,-1011,-1011,-1011,-1011,-1011 + }, + + { + 69,-1012,-1012,-1012,-1012,-1012,-1012,-1012,-1012,-1012, + -1012,-1012,-1012,-1012,-1012,-1012,-1012,-1012,-1012,-1012, + -1012,-1012,-1012,-1012,-1012,-1012,-1012,-1012,-1012,-1012, + -1012,-1012, 1221,-1012,-1012,-1012,-1012,-1012,-1012,-1012, -/** The main scanner function which does all the work. - */ -YY_DECL -{ - register yy_state_type yy_current_state; - register char *yy_cp, *yy_bp; - register int yy_act; - -#line 197 "wcsbth.l" + -1012,-1012,-1012,-1012,-1012,-1012,-1012,-1012,-1012,-1012, + -1012,-1012,-1012,-1012,-1012,-1012,-1012,-1012,-1012,-1012, + -1012,-1012,-1012,-1012,-1012,-1012,-1012,-1012,-1012,-1012, + -1012,-1012,-1012,-1012,-1012,-1012,-1012,-1012,-1012,-1012, + -1012,-1012,-1012,-1012,-1012,-1012,-1012,-1012,-1012,-1012, + -1012,-1012,-1012,-1012,-1012,-1012,-1012,-1012,-1012,-1012, + -1012,-1012,-1012,-1012,-1012,-1012,-1012,-1012,-1012,-1012, + -1012,-1012,-1012,-1012,-1012,-1012,-1012,-1012,-1012,-1012, + -1012,-1012,-1012,-1012,-1012,-1012,-1012,-1012 + }, + + { + 69,-1013,-1013,-1013,-1013,-1013,-1013,-1013,-1013,-1013, + + -1013,-1013,-1013,-1013,-1013,-1013,-1013,-1013,-1013,-1013, + -1013,-1013,-1013,-1013,-1013,-1013,-1013,-1013,-1013,-1013, + -1013,-1013, 1222,-1013,-1013,-1013,-1013,-1013,-1013,-1013, + -1013,-1013,-1013,-1013,-1013,-1013,-1013,-1013, 1223, 1223, + 1223, 1223, 1223, 1223, 1223, 1223, 1223, 1223,-1013,-1013, + -1013,-1013,-1013,-1013,-1013, 1222, 1222, 1222, 1222, 1222, + 1222, 1222, 1222, 1222, 1222, 1222, 1222, 1222, 1222, 1222, + 1222, 1222, 1222, 1222, 1222, 1222, 1222, 1222, 1222, 1222, + 1222,-1013,-1013,-1013,-1013,-1013,-1013,-1013,-1013,-1013, + -1013,-1013,-1013,-1013,-1013,-1013,-1013,-1013,-1013,-1013, - /* Keyword indices, as used in the WCS papers, e.g. iVn_ma, TPn_ka. */ - char a; - int i, j, k, m, n; - - char *cptr, *errmsg, errtxt[80], exclude[1000], *extkey, *hptr, ptype, - stmp[16]; - int altlin, ialt, icol, incl, ipass, ipx, itmp, ix, jx, keytype, - nsel, npass, status, valtype, voff; - void *vptr, *wptr; - struct wcsbth_alts alts; - struct wcsprm *wcsp, wcstem; - int (*special)(void *); - int wcsbthlex_destroy(void); - - /* The data structures produced. */ - *nwcs = 0; - *wcs = 0x0; - - /* Parameters used to implement YY_INPUT. */ - wcsbth_hdr = header; - wcsbth_nkeyrec = nkeyrec; - - /* Our handle on the input stream. */ - hptr = header; - *nreject = 0; - - /* Keyword parameters. */ - i = j = 0; - n = k = 0; - m = 0; - a = ' '; - - /* Header bookkeeping. */ - alts.ncol = 0; - alts.arridx = 0x0; - alts.pixlist = 0x0; - alts.npv = 0x0; - alts.nps = 0x0; - - for (ialt = 0; ialt < 27; ialt++) { - alts.pixidx[ialt] = 0; - alts.pixnpv[ialt] = 0; - alts.pixnps[ialt] = 0; - } - - /* For decoding the keyvalue. */ - keytype = 0; - valtype = -1; - vptr = 0x0; - - /* For keywords that require special handling. */ - altlin = 0; - ptype = ' '; - special = 0x0; - - /* Selection by column number. */ - nsel = colsel ? colsel[0] : 0; - incl = (nsel > 0); - for (icol = 0; icol < 1000; icol++) { - exclude[icol] = incl; - } - for (icol = 1; icol <= abs(nsel); icol++) { - itmp = colsel[icol]; - if (0 < itmp && itmp < 1000) { - exclude[itmp] = !incl; - } - } - exclude[0] = 0; - - /* Selection by keyword type. */ - itmp = keysel; - keysel = 0; - if (itmp) { - if (itmp & WCSHDR_IMGHEAD) keysel |= IMGHEAD; - if (itmp & WCSHDR_BIMGARR) keysel |= BIMGARR; - if (itmp & WCSHDR_PIXLIST) keysel |= PIXLIST; - } - if (keysel == 0) { - keysel = IMGHEAD | BINTAB; - } - - /* Control variables. */ - ipass = 1; - npass = 2; - - /* Return here via longjmp() invoked by yy_fatal_error(). */ - if (setjmp(wcsbth_abort_jmp_env)) { - return 4; - } - - BEGIN(INITIAL); + -1013,-1013,-1013,-1013,-1013,-1013,-1013,-1013,-1013,-1013, + -1013,-1013,-1013,-1013,-1013,-1013,-1013,-1013 + }, + + { + 69,-1014,-1014,-1014,-1014,-1014,-1014,-1014,-1014,-1014, + -1014,-1014,-1014,-1014,-1014,-1014,-1014,-1014,-1014,-1014, + -1014,-1014,-1014,-1014,-1014,-1014,-1014,-1014,-1014,-1014, + -1014,-1014,-1014,-1014,-1014,-1014,-1014,-1014,-1014,-1014, + -1014,-1014,-1014,-1014,-1014,-1014,-1014,-1014,-1014,-1014, + -1014,-1014,-1014,-1014,-1014,-1014,-1014,-1014,-1014,-1014, + -1014,-1014,-1014,-1014,-1014,-1014,-1014,-1014,-1014, 1224, + -1014,-1014,-1014,-1014,-1014,-1014,-1014,-1014,-1014,-1014, + -1014,-1014,-1014,-1014,-1014,-1014,-1014,-1014,-1014,-1014, + -1014,-1014,-1014,-1014,-1014,-1014,-1014,-1014,-1014,-1014, + -1014,-1014,-1014,-1014,-1014,-1014,-1014,-1014,-1014,-1014, + -1014,-1014,-1014,-1014,-1014,-1014,-1014,-1014,-1014,-1014, + -1014,-1014,-1014,-1014,-1014,-1014,-1014,-1014 + }, + + { + 69,-1015,-1015,-1015,-1015,-1015,-1015,-1015,-1015,-1015, + -1015,-1015,-1015,-1015,-1015,-1015,-1015,-1015,-1015,-1015, + -1015,-1015,-1015,-1015,-1015,-1015,-1015,-1015,-1015,-1015, + -1015,-1015, 1225,-1015,-1015,-1015,-1015,-1015,-1015,-1015, + -1015,-1015,-1015,-1015,-1015,-1015,-1015,-1015,-1015,-1015, -#line 17146 "wcsbth.c" + -1015,-1015,-1015,-1015,-1015,-1015,-1015,-1015,-1015,-1015, + -1015,-1015,-1015,-1015,-1015,-1015,-1015,-1015,-1015,-1015, + -1015,-1015,-1015,-1015,-1015,-1015,-1015,-1015,-1015,-1015, + -1015,-1015,-1015,-1015,-1015,-1015,-1015,-1015,-1015,-1015, + -1015,-1015,-1015,-1015,-1015,-1015,-1015,-1015,-1015,-1015, + -1015,-1015,-1015,-1015,-1015,-1015,-1015,-1015,-1015,-1015, + -1015,-1015,-1015,-1015,-1015,-1015,-1015,-1015,-1015,-1015, + -1015,-1015,-1015,-1015,-1015,-1015,-1015,-1015 + }, + + { + 69,-1016,-1016,-1016,-1016,-1016,-1016,-1016,-1016,-1016, + -1016,-1016,-1016,-1016,-1016,-1016,-1016,-1016,-1016,-1016, - if ( !(yy_init) ) - { - (yy_init) = 1; + -1016,-1016,-1016,-1016,-1016,-1016,-1016,-1016,-1016,-1016, + -1016,-1016, 1226,-1016,-1016,-1016,-1016,-1016,-1016,-1016, + -1016,-1016,-1016,-1016,-1016,-1016,-1016,-1016, 1227, 1227, + 1227, 1227, 1227, 1227, 1227, 1227, 1227, 1227,-1016,-1016, + -1016,-1016,-1016,-1016,-1016, 1226, 1226, 1226, 1226, 1226, + 1226, 1226, 1226, 1226, 1226, 1226, 1226, 1226, 1226, 1226, + 1226, 1226, 1226, 1226, 1226, 1226, 1226, 1226, 1226, 1226, + 1226,-1016,-1016,-1016,-1016,-1016,-1016,-1016,-1016,-1016, + -1016,-1016,-1016,-1016,-1016,-1016,-1016,-1016,-1016,-1016, + -1016,-1016,-1016,-1016,-1016,-1016,-1016,-1016,-1016,-1016, + + -1016,-1016,-1016,-1016,-1016,-1016,-1016,-1016 + }, + + { + 69,-1017,-1017,-1017,-1017,-1017,-1017,-1017,-1017,-1017, + -1017,-1017,-1017,-1017,-1017,-1017,-1017,-1017,-1017,-1017, + -1017,-1017,-1017,-1017,-1017,-1017,-1017,-1017,-1017,-1017, + -1017,-1017,-1017,-1017,-1017,-1017,-1017,-1017,-1017,-1017, + -1017,-1017,-1017,-1017,-1017,-1017,-1017,-1017,-1017,-1017, + -1017,-1017,-1017,-1017,-1017,-1017,-1017,-1017,-1017,-1017, + -1017,-1017,-1017,-1017,-1017,-1017,-1017,-1017,-1017,-1017, + -1017,-1017,-1017,-1017,-1017,-1017,-1017,-1017,-1017,-1017, + -1017,-1017,-1017, 1228,-1017,-1017,-1017,-1017,-1017,-1017, -#ifdef YY_USER_INIT - YY_USER_INIT; -#endif + -1017,-1017,-1017,-1017,-1017,-1017,-1017,-1017,-1017,-1017, + -1017,-1017,-1017,-1017,-1017,-1017,-1017,-1017,-1017,-1017, + -1017,-1017,-1017,-1017,-1017,-1017,-1017,-1017,-1017,-1017, + -1017,-1017,-1017,-1017,-1017,-1017,-1017,-1017 + }, + + { + 69,-1018,-1018,-1018,-1018,-1018,-1018,-1018,-1018,-1018, + -1018,-1018,-1018,-1018,-1018,-1018,-1018,-1018,-1018,-1018, + -1018,-1018,-1018,-1018,-1018,-1018,-1018,-1018,-1018,-1018, + -1018,-1018,-1018,-1018,-1018,-1018,-1018,-1018,-1018,-1018, + -1018,-1018,-1018,-1018,-1018,-1018,-1018,-1018,-1018,-1018, + -1018,-1018,-1018,-1018,-1018,-1018,-1018,-1018,-1018,-1018, - if ( ! (yy_start) ) - (yy_start) = 1; /* first start state */ + -1018,-1018,-1018,-1018,-1018,-1018,-1018,-1018,-1018, 1229, + -1018,-1018,-1018,-1018,-1018,-1018,-1018,-1018,-1018,-1018, + -1018,-1018,-1018,-1018,-1018,-1018,-1018,-1018,-1018,-1018, + -1018,-1018,-1018,-1018,-1018,-1018,-1018,-1018,-1018,-1018, + -1018,-1018,-1018,-1018,-1018,-1018,-1018,-1018,-1018,-1018, + -1018,-1018,-1018,-1018,-1018,-1018,-1018,-1018,-1018,-1018, + -1018,-1018,-1018,-1018,-1018,-1018,-1018,-1018 + }, + + { + 69,-1019,-1019,-1019,-1019,-1019,-1019,-1019,-1019,-1019, + -1019,-1019,-1019,-1019,-1019,-1019,-1019,-1019,-1019,-1019, + -1019,-1019,-1019,-1019,-1019,-1019,-1019,-1019,-1019,-1019, - if ( ! wcsbthin ) - wcsbthin = stdin; + -1019,-1019, 1230,-1019,-1019,-1019,-1019,-1019,-1019,-1019, + -1019,-1019,-1019,-1019,-1019,-1019,-1019,-1019,-1019,-1019, + -1019,-1019,-1019,-1019,-1019,-1019,-1019,-1019,-1019,-1019, + -1019,-1019,-1019,-1019,-1019,-1019,-1019,-1019,-1019,-1019, + -1019,-1019,-1019,-1019,-1019,-1019,-1019,-1019,-1019,-1019, + -1019,-1019,-1019,-1019,-1019,-1019,-1019,-1019,-1019,-1019, + -1019,-1019,-1019,-1019,-1019,-1019,-1019,-1019,-1019,-1019, + -1019,-1019,-1019,-1019,-1019,-1019,-1019,-1019,-1019,-1019, + -1019,-1019,-1019,-1019,-1019,-1019,-1019,-1019,-1019,-1019, + -1019,-1019,-1019,-1019,-1019,-1019,-1019,-1019 + + }, + + { + 69,-1020,-1020,-1020,-1020,-1020,-1020,-1020,-1020,-1020, + -1020,-1020,-1020,-1020,-1020,-1020,-1020,-1020,-1020,-1020, + -1020,-1020,-1020,-1020,-1020,-1020,-1020,-1020,-1020,-1020, + -1020,-1020, 1231,-1020,-1020,-1020,-1020,-1020,-1020,-1020, + -1020,-1020,-1020,-1020,-1020,-1020,-1020,-1020, 1232, 1232, + 1232, 1232, 1232, 1232, 1232, 1232, 1232, 1232,-1020,-1020, + -1020,-1020,-1020,-1020,-1020, 1231, 1231, 1231, 1231, 1231, + 1231, 1231, 1231, 1231, 1231, 1231, 1231, 1231, 1231, 1231, + 1231, 1231, 1231, 1231, 1231, 1231, 1231, 1231, 1231, 1231, + 1231,-1020,-1020,-1020,-1020,-1020,-1020,-1020,-1020,-1020, - if ( ! wcsbthout ) - wcsbthout = stdout; + -1020,-1020,-1020,-1020,-1020,-1020,-1020,-1020,-1020,-1020, + -1020,-1020,-1020,-1020,-1020,-1020,-1020,-1020,-1020,-1020, + -1020,-1020,-1020,-1020,-1020,-1020,-1020,-1020 + }, + + { + 69,-1021,-1021,-1021,-1021,-1021,-1021,-1021,-1021,-1021, + -1021,-1021,-1021,-1021,-1021,-1021,-1021,-1021,-1021,-1021, + -1021,-1021,-1021,-1021,-1021,-1021,-1021,-1021,-1021,-1021, + -1021,-1021, 1233,-1021,-1021,-1021,-1021,-1021,-1021,-1021, + -1021,-1021,-1021,-1021,-1021,-1021,-1021,-1021,-1021,-1021, + -1021,-1021,-1021,-1021,-1021,-1021,-1021,-1021,-1021,-1021, + -1021,-1021,-1021,-1021,-1021,-1021,-1021,-1021,-1021,-1021, - if ( ! YY_CURRENT_BUFFER ) { - wcsbthensure_buffer_stack (); - YY_CURRENT_BUFFER_LVALUE = - wcsbth_create_buffer(wcsbthin,YY_BUF_SIZE ); - } + -1021,-1021,-1021,-1021,-1021,-1021,-1021,-1021,-1021,-1021, + -1021,-1021,-1021,-1021,-1021,-1021,-1021,-1021,-1021,-1021, + -1021,-1021,-1021,-1021,-1021,-1021,-1021,-1021,-1021,-1021, + -1021,-1021,-1021,-1021,-1021,-1021,-1021,-1021,-1021,-1021, + -1021,-1021,-1021,-1021,-1021,-1021,-1021,-1021,-1021,-1021, + -1021,-1021,-1021,-1021,-1021,-1021,-1021,-1021 + }, + + { + 69,-1022,-1022,-1022,-1022,-1022,-1022,-1022,-1022,-1022, + -1022,-1022,-1022,-1022,-1022,-1022,-1022,-1022,-1022,-1022, + -1022,-1022,-1022,-1022,-1022,-1022,-1022,-1022,-1022,-1022, + -1022,-1022, 1234,-1022,-1022,-1022,-1022,-1022,-1022,-1022, - wcsbth_load_buffer_state( ); - } + -1022,-1022,-1022,-1022,-1022,-1022,-1022,-1022, 1235, 1235, + 1235, 1235, 1235, 1235, 1235, 1235, 1235, 1235,-1022,-1022, + -1022,-1022,-1022,-1022,-1022, 1234, 1234, 1234, 1234, 1234, + 1234, 1234, 1234, 1234, 1234, 1234, 1234, 1234, 1234, 1234, + 1234, 1234, 1234, 1234, 1234, 1234, 1234, 1234, 1234, 1234, + 1234,-1022,-1022,-1022,-1022,-1022,-1022,-1022,-1022,-1022, + -1022,-1022,-1022,-1022,-1022,-1022,-1022,-1022,-1022,-1022, + -1022,-1022,-1022,-1022,-1022,-1022,-1022,-1022,-1022,-1022, + -1022,-1022,-1022,-1022,-1022,-1022,-1022,-1022 + }, + + { + 69,-1023,-1023,-1023,-1023,-1023,-1023,-1023,-1023,-1023, + + -1023,-1023,-1023,-1023,-1023,-1023,-1023,-1023,-1023,-1023, + -1023,-1023,-1023,-1023,-1023,-1023,-1023,-1023,-1023,-1023, + -1023,-1023,-1023,-1023,-1023,-1023,-1023,-1023,-1023,-1023, + -1023,-1023,-1023,-1023,-1023,-1023,-1023,-1023,-1023,-1023, + -1023,-1023,-1023,-1023,-1023,-1023,-1023,-1023,-1023,-1023, + -1023,-1023,-1023,-1023,-1023,-1023,-1023,-1023,-1023, 1236, + -1023,-1023,-1023,-1023,-1023,-1023,-1023,-1023,-1023,-1023, + -1023,-1023,-1023,-1023,-1023,-1023,-1023,-1023,-1023,-1023, + -1023,-1023,-1023,-1023,-1023,-1023,-1023,-1023,-1023,-1023, + -1023,-1023,-1023,-1023,-1023,-1023,-1023,-1023,-1023,-1023, - while ( 1 ) /* loops until end-of-file is reached */ - { - yy_cp = (yy_c_buf_p); + -1023,-1023,-1023,-1023,-1023,-1023,-1023,-1023,-1023,-1023, + -1023,-1023,-1023,-1023,-1023,-1023,-1023,-1023 + }, + + { + 69,-1024,-1024,-1024,-1024,-1024,-1024,-1024,-1024,-1024, + -1024,-1024,-1024,-1024,-1024,-1024,-1024,-1024,-1024,-1024, + -1024,-1024,-1024,-1024,-1024,-1024,-1024,-1024,-1024,-1024, + -1024,-1024,-1024,-1024,-1024,-1024,-1024,-1024,-1024,-1024, + -1024,-1024,-1024,-1024,-1024,-1024,-1024,-1024,-1024,-1024, + -1024,-1024,-1024,-1024,-1024,-1024,-1024,-1024,-1024,-1024, + -1024,-1024,-1024,-1024,-1024,-1024,-1024,-1024,-1024, 1237, + -1024,-1024,-1024,-1024,-1024,-1024,-1024,-1024,-1024,-1024, - /* Support of wcsbthtext. */ - *yy_cp = (yy_hold_char); + -1024,-1024,-1024,-1024,-1024,-1024,-1024,-1024,-1024,-1024, + -1024,-1024,-1024,-1024,-1024,-1024,-1024,-1024,-1024,-1024, + -1024,-1024,-1024,-1024,-1024,-1024,-1024,-1024,-1024,-1024, + -1024,-1024,-1024,-1024,-1024,-1024,-1024,-1024,-1024,-1024, + -1024,-1024,-1024,-1024,-1024,-1024,-1024,-1024 + }, + + { + 69,-1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025, + -1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025, + -1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025, + -1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025, + -1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025, - /* yy_bp points to the position in yy_ch_buf of the start of - * the current run. - */ - yy_bp = yy_cp; + -1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025, + -1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025, + -1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025, + -1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025, + -1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025, + -1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025, + -1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025, + -1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025 + }, + + { + 69,-1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026, + -1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026, - yy_current_state = (yy_start); - yy_current_state += YY_AT_BOL(); -yy_match: - while ( (yy_current_state = yy_nxt[yy_current_state][ YY_SC_TO_UI(*yy_cp) ]) > 0 ) - { - if ( yy_accept[yy_current_state] ) - { - (yy_last_accepting_state) = yy_current_state; - (yy_last_accepting_cpos) = yy_cp; - } + -1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026, + -1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026, + -1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026, + -1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026, + -1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026, + -1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026, + -1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026, + -1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026, + -1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026, + -1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026, + + -1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026 + }, + + { + 69,-1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027, + -1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027, + -1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027, + -1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027, + -1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027, + -1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027, + -1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027, + -1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027, + -1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027, - ++yy_cp; - } + -1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027, + -1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027, + -1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027, + -1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027 + }, + + { + 69,-1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028, + -1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028, + -1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028, + -1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028, + -1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028, + -1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028, - yy_current_state = -yy_current_state; + -1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028, + -1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028, + -1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028, + -1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028, + -1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028, + -1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028, + -1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028 + }, + + { + 69,-1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029, + -1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029, + -1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029, -yy_find_action: - yy_act = yy_accept[yy_current_state]; + -1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029, + -1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029, + -1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029, + -1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029, + -1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029, + -1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029, + -1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029, + -1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029, + -1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029, + -1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029 + + }, + + { + 69,-1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030, + -1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030, + -1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030, + -1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030, + -1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030, + -1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030, + -1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030, + -1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030, + -1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030, + -1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030, - YY_DO_BEFORE_ACTION; + -1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030, + -1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030, + -1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030 + }, + + { + 69,-1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031, + -1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031, + -1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031, + -1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031, + -1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031, + -1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031, + -1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031, -do_action: /* This label is used only to access EOF actions. */ + -1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031, + -1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031, + -1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031, + -1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031, + -1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031, + -1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031 + }, + + { + 69,-1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032, + -1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032, + -1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032, + -1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032, - switch ( yy_act ) - { /* beginning of action switch */ - case 0: /* must back up */ - /* undo the effects of YY_DO_BEFORE_ACTION */ - *yy_cp = (yy_hold_char); - yy_cp = (yy_last_accepting_cpos) + 1; - yy_current_state = (yy_last_accepting_state); - goto yy_find_action; + -1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032, + -1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032, + -1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032, + -1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032, + -1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032, + -1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032, + -1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032, + -1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032, + -1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032 + }, + + { + 69,-1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033, + + -1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033, + -1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033, + -1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033, + -1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033, + -1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033, + -1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033, + -1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033, + -1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033, + -1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033, + -1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033, -case 1: -YY_RULE_SETUP -#line 291 "wcsbth.l" -{ - if (ipass == 1) { - if (alts.ncol == 0) { - sscanf(wcsbthtext, "TFIELDS = %d", &(alts.ncol)); - BEGIN(FLUSH); - } else { - errmsg = "Duplicate or out-of-sequence TFIELDS keyword"; - BEGIN(ERROR); - } + -1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033, + -1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033 + }, + + { + 69,-1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034, + -1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034, + -1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034, + -1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034, + -1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034, + -1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034, + -1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034, + -1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034, + + -1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034, + -1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034, + -1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034, + -1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034, + -1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034 + }, + + { + 69,-1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035, + -1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035, + -1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035, + -1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035, + -1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035, + + -1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035, + -1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035, + -1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035, + -1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035, + -1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035, + -1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035, + -1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035, + -1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035 + }, + + { + 69,-1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036, + -1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036, + + -1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036, + -1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036, + -1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036, + -1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036, + -1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036, + -1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036, + -1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036, + -1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036, + -1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036, + -1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036, + + -1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036 + }, + + { + 69,-1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037, + -1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037, + -1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037, + -1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037, + -1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037, + -1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037, + -1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037, + -1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037, + -1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037, + + -1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037, + -1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037, + -1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037, + -1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037 + }, + + { + 69,-1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038, + -1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038, + -1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038, + -1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038, + -1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038, + -1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038, + + -1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038, + -1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038, + -1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038, + -1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038, + -1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038, + -1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038, + -1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038 + }, + + { + 69,-1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039, + -1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039, + -1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039, + + -1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039, + -1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039, + -1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039, + -1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039, + -1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039, + -1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039, + -1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039, + -1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039, + -1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039, + -1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039 + + }, + + { + 69,-1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040, + -1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040, + -1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040, + -1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040, + -1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040, + -1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040, + -1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040, + -1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040, + -1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040, + -1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040, + + -1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040, + -1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040, + -1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040 + }, + + { + 69,-1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041, + -1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041, + -1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041, + -1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041, + -1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041, + -1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041, + -1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041, + + -1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041, + -1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041, + -1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041, + -1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041, + -1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041, + -1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041 + }, + + { + 69,-1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042, + -1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042, + -1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042, + -1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042, + + -1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042, + -1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042, + -1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042, + -1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042, + -1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042, + -1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042, + -1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042, + -1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042, + -1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042 + }, + + { + 69,-1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043, + + -1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043, + -1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043, + -1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043, + -1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043, + -1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043, + -1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043, + -1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043, + -1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043, + -1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043, + -1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043, + + -1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043, + -1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043 + }, + + { + 69,-1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044, + -1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044, + -1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044, + -1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044, + -1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044, + -1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044, + -1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044, + -1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044, + + -1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044, + -1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044, + -1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044, + -1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044, + -1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044 + }, + + { + 69,-1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045, + -1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045, + -1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045, + -1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045, + -1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045, + + -1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045, + -1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045, + -1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045, + -1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045, + -1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045, + -1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045, + -1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045, + -1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045 + }, + + { + 69,-1046,-1046,-1046,-1046,-1046,-1046,-1046,-1046,-1046, + -1046,-1046,-1046,-1046,-1046,-1046,-1046,-1046,-1046,-1046, + + -1046,-1046,-1046,-1046,-1046,-1046,-1046,-1046,-1046,-1046, + -1046,-1046,-1046,-1046,-1046,-1046,-1046,-1046,-1046,-1046, + -1046,-1046,-1046,-1046,-1046,-1046,-1046,-1046,-1046,-1046, + -1046,-1046,-1046,-1046,-1046,-1046,-1046,-1046,-1046,-1046, + -1046,-1046,-1046,-1046,-1046,-1046,-1046,-1046,-1046,-1046, + -1046,-1046,-1046,-1046,-1046,-1046,-1046,-1046,-1046,-1046, + -1046,-1046,-1046,-1046,-1046,-1046,-1046,-1046,-1046,-1046, + -1046,-1046,-1046,-1046,-1046,-1046,-1046,-1046,-1046,-1046, + -1046,-1046,-1046,-1046,-1046,-1046,-1046,-1046,-1046,-1046, + -1046,-1046,-1046,-1046,-1046,-1046,-1046,-1046,-1046,-1046, + + -1046,-1046,-1046,-1046,-1046,-1046,-1046,-1046 + }, + + { + 69,-1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047, + -1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047, + -1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047, + -1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047, + -1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047, + -1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047, + -1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047, + -1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047, + -1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047, + + -1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047, + -1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047, + -1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047, + -1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047 + }, + + { + 69,-1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048, + -1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048, + -1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048, + -1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048, + -1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048, + -1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048, + + -1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048, + -1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048, + -1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048, + -1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048, + -1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048, + -1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048, + -1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048 + }, + + { + 69,-1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049, + -1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049, + -1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049, + + -1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049, + -1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049, + -1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049, + -1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049, + -1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049, + -1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049, + -1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049, + -1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049, + -1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049, + -1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049 + + }, + + { + 69,-1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050, + -1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050, + -1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050, + -1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050, + -1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050, + -1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050, + -1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050, + -1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050, + -1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050, + -1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050, + + -1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050, + -1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050, + -1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050 + }, + + { + 69,-1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051, + -1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051, + -1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051, + -1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051, + -1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051, + -1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051, + -1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051, + + -1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051, + -1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051, + -1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051, + -1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051, + -1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051, + -1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051 + }, + + { + 69,-1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052, + -1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052, + -1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052, + -1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052, + + -1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052, + -1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052, + -1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052, + -1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052, + -1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052, + -1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052, + -1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052, + -1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052, + -1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052 + }, + + { + 69,-1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053, + + -1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053, + -1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053, + -1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053, + -1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053, + -1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053, + -1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053, + -1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053, + -1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053, + -1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053, + -1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053, + + -1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053, + -1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053 + }, + + { + 69,-1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054, + -1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054, + -1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054, + -1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054, + -1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054, + -1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054, + -1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054, + -1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054, + + -1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054, + -1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054, + -1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054, + -1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054, + -1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054 + }, + + { + 69,-1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055, + -1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055, + -1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055, + -1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055, + -1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055, + + -1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055, + -1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055, + -1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055, + -1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055, + -1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055, + -1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055, + -1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055, + -1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055 + }, + + { + 69,-1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056, + -1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056, + + -1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056, + -1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056, + -1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056, + -1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056, + -1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056, + -1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056, + -1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056, + -1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056, + -1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056, + -1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056, + + -1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056 + }, + + { + 69,-1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057, + -1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057, + -1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057, + -1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057, + -1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057, + -1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057, + -1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057, + -1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057, + -1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057, + + -1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057, + -1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057, + -1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057, + -1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057 + }, + + { + 69,-1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058, + -1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058, + -1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058, + -1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058, + -1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058, + -1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058, + + -1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058, + -1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058, + -1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058, + -1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058, + -1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058, + -1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058, + -1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058 + }, + + { + 69,-1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059, + -1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059, + -1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059, + + -1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059, + -1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059, + -1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059, + -1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059, + -1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059, + -1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059, + -1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059, + -1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059, + -1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059, + -1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059 + + }, + + { + 69,-1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060, + -1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060, + -1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060, + -1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060, + -1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060, + -1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060, + -1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060, + -1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060, + -1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060, + -1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060, + + -1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060, + -1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060, + -1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060 + }, + + { + 69,-1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061, + -1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061, + -1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061, + -1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061, + -1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061, + -1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061, + -1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061, + + -1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061, + -1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061, + -1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061, + -1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061, + -1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061, + -1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061 + }, + + { + 69,-1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062, + -1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062, + -1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062, + -1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062, + + -1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062, + -1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062, + -1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062, + -1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062, + -1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062, + -1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062, + -1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062, + -1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062, + -1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062 + }, + + { + 69,-1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063, + + -1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063, + -1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063, + -1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063, + -1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063, + -1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063, + -1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063, + -1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063, + -1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063, + -1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063, + -1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063, + + -1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063, + -1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063 + }, + + { + 69,-1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064, + -1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064, + -1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064, + -1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064, + -1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064, + -1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064, + -1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064, + -1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064, + + -1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064, + -1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064, + -1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064, + -1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064, + -1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064 + }, + + { + 69,-1065,-1065,-1065,-1065,-1065,-1065,-1065,-1065,-1065, + -1065,-1065,-1065,-1065,-1065,-1065,-1065,-1065,-1065,-1065, + -1065,-1065,-1065,-1065,-1065,-1065,-1065,-1065,-1065,-1065, + -1065,-1065,-1065,-1065,-1065,-1065,-1065,-1065,-1065,-1065, + -1065,-1065,-1065,-1065,-1065,-1065,-1065,-1065,-1065,-1065, + + -1065,-1065,-1065,-1065,-1065,-1065,-1065,-1065,-1065,-1065, + -1065,-1065,-1065,-1065,-1065,-1065,-1065,-1065,-1065,-1065, + -1065,-1065,-1065,-1065,-1065,-1065,-1065,-1065,-1065,-1065, + -1065,-1065,-1065,-1065,-1065,-1065,-1065,-1065,-1065,-1065, + -1065,-1065,-1065,-1065,-1065,-1065,-1065,-1065,-1065,-1065, + -1065,-1065,-1065,-1065,-1065,-1065,-1065,-1065,-1065,-1065, + -1065,-1065,-1065,-1065,-1065,-1065,-1065,-1065,-1065,-1065, + -1065,-1065,-1065,-1065,-1065,-1065,-1065,-1065 + }, + + { + 69,-1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066, + -1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066, + + -1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066, + -1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066, + -1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066, + -1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066, + -1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066, + -1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066, + -1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066, + -1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066, + -1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066, + -1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066, + + -1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066 + }, + + { + 69,-1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067, + -1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067, + -1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067, + -1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067, + -1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067, + -1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067, + -1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067, + -1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067, + -1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067, + + -1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067, + -1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067, + -1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067, + -1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067 + }, + + { + 69,-1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068, + -1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068, + -1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068, + -1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068, + -1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068, + -1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068, + + -1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068, + -1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068, + -1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068, + -1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068, + -1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068, + -1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068, + -1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068 + }, + + { + 69,-1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069, + -1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069, + -1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069, + + -1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069, + -1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069, + -1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069, + -1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069, + -1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069, + -1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069, + -1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069, + -1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069, + -1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069, + -1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069 + + }, + + { + 69,-1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070, + -1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070, + -1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070, + -1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070, + -1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070, + -1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070, + -1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070, + -1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070, + -1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070, + -1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070, + + -1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070, + -1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070, + -1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070 + }, + + { + 69,-1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071, + -1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071, + -1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071, + -1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071, + -1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071, + -1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071, + -1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071, + + -1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071, + -1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071, + -1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071, + -1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071, + -1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071, + -1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071 + }, + + { + 69,-1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072, + -1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072, + -1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072, + -1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072, + + -1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072, + -1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072, + -1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072, + -1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072, + -1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072, + -1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072, + -1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072, + -1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072, + -1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072 + }, + + { + 69,-1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073, + + -1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073, + -1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073, + -1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073, + -1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073, + -1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073, + -1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073, + -1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073, + -1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073, + -1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073, + -1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073, + + -1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073, + -1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073 + }, + + { + 69,-1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074, + -1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074, + -1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074, + -1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074, + -1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074, + -1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074, + -1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074, + -1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074, + + -1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074, + -1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074, + -1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074, + -1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074, + -1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074 + }, + + { + 69,-1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075, + -1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075, + -1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075, + -1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075, + -1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075, + + -1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075, + -1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075, + -1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075, + -1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075, + -1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075, + -1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075, + -1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075, + -1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075 + }, + + { + 69,-1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076, + -1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076, + + -1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076, + -1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076, + -1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076, + -1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076, + -1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076, + -1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076, + -1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076, + -1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076, + -1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076, + -1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076, + + -1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076 + }, + + { + 69,-1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077, + -1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077, + -1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077, + -1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077, + -1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077, + -1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077, + -1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077, + -1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077, + -1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077, + + -1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077, + -1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077, + -1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077, + -1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077 + }, + + { + 69,-1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078, + -1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078, + -1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078, + -1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078, + -1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078, + -1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078, + + -1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078, + -1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078, + -1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078, + -1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078, + -1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078, + -1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078, + -1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078 + }, + + { + 69,-1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079, + -1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079, + -1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079, + + -1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079, + -1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079, + -1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079, + -1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079, + -1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079, + -1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079, + -1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079, + -1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079, + -1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079, + -1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079 + + }, + + { + 69,-1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080, + -1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080, + -1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080, + -1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080, + -1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080, + -1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080, + -1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080, + -1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080, + -1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080, + -1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080, + + -1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080, + -1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080, + -1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080 + }, + + { + 69,-1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081, + -1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081, + -1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081, + -1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081, + -1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081, + -1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081, + -1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081, + + -1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081, + -1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081, + -1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081, + -1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081, + -1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081, + -1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081 + }, + + { + 69,-1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082, + -1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082, + -1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082, + -1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082, + + -1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082, + -1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082, + -1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082, + -1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082, + -1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082, + -1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082, + -1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082, + -1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082, + -1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082 + }, + + { + 69,-1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083, + + -1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083, + -1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083, + -1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083, + -1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083, + -1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083, + -1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083, + -1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083, + -1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083, + -1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083, + -1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083, + + -1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083, + -1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083 + }, + + { + 69,-1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084, + -1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084, + -1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084, + -1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084, + -1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084, + -1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084, + -1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084, + -1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084, + + -1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084, + -1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084, + -1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084, + -1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084, + -1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084 + }, + + { + 69,-1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085, + -1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085, + -1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085, + -1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085, + -1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085, + + -1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085, + -1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085, + -1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085, + -1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085, + -1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085, + -1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085, + -1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085, + -1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085 + }, + + { + 69,-1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086, + -1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086, + + -1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086, + -1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086, + -1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086, + -1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086, + -1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086, + -1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086, + -1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086, + -1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086, + -1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086, + -1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086, + + -1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086 + }, + + { + 69,-1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087, + -1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087, + -1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087, + -1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087, + -1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087, + -1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087, + -1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087, + -1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087, + -1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087, + + -1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087, + -1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087, + -1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087, + -1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087 + }, + + { + 69,-1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088, + -1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088, + -1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088, + -1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088, + -1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088, + -1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088, + + -1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088, + -1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088, + -1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088, + -1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088, + -1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088, + -1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088, + -1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088 + }, + + { + 69,-1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089, + -1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089, + -1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089, + + -1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089, + -1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089, + -1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089, + -1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089, + -1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089, + -1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089, + -1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089, + -1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089, + -1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089, + -1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089 + + }, + + { + 69,-1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090, + -1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090, + -1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090, + -1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090, + -1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090, + -1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090, + -1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090, + -1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090, + -1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090, + -1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090, + + -1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090, + -1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090, + -1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090 + }, + + { + 69,-1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091, + -1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091, + -1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091, + -1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091, + -1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091, + -1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091, + -1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091, + + -1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091, + -1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091, + -1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091, + -1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091, + -1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091, + -1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091 + }, + + { + 69,-1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092, + -1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092, + -1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092, + -1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092, + + -1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092, + -1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092, + -1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092, + -1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092, + -1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092, + -1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092, + -1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092, + -1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092, + -1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092 + }, + + { + 69,-1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093, + + -1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093, + -1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093, + -1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093, + -1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093, + -1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093, + -1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093, + -1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093, + -1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093, + -1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093, + -1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093, + + -1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093, + -1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093 + }, + + { + 69,-1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094, + -1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094, + -1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094, + -1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094, + -1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094, + -1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094, + -1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094, + -1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094, + + -1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094, + -1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094, + -1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094, + -1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094, + -1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094 + }, + + { + 69,-1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095, + -1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095, + -1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095, + -1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095, + -1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095, + + -1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095, + -1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095, + -1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095, + -1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095, + -1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095, + -1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095, + -1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095, + -1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095 + }, + + { + 69,-1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096, + -1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096, + + -1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096, + -1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096, + -1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096, + -1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096, + -1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096, + -1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096, + -1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096, + -1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096, + -1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096, + -1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096, + + -1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096 + }, + + { + 69,-1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097, + -1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097, + -1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097, + -1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097, + -1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097, + -1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097, + -1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097, + -1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097, + -1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097, + + -1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097, + -1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097, + -1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097, + -1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097 + }, + + { + 69,-1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098, + -1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098, + -1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098, + -1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098, + -1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098, + -1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098, + + -1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098, + -1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098, + -1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098, + -1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098, + -1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098, + -1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098, + -1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098 + }, + + { + 69,-1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099, + -1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099, + -1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099, + + -1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099, + -1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099, + -1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099, + -1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099, + -1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099, + -1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099, + -1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099, + -1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099, + -1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099, + -1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099 + + }, + + { + 69,-1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100, + -1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100, + -1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100, + -1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100, + -1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100, + -1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100, + -1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100, + -1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100, + -1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100, + -1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100, + + -1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100, + -1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100, + -1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100 + }, + + { + 69,-1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101, + -1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101, + -1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101, + -1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101, + -1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101, + -1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101, + -1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101, + + -1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101, + -1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101, + -1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101, + -1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101, + -1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101, + -1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101 + }, + + { + 69,-1102,-1102,-1102,-1102,-1102,-1102,-1102,-1102,-1102, + -1102,-1102,-1102,-1102,-1102,-1102,-1102,-1102,-1102,-1102, + -1102,-1102,-1102,-1102,-1102,-1102,-1102,-1102,-1102,-1102, + -1102,-1102,-1102,-1102,-1102,-1102,-1102,-1102,-1102,-1102, + + -1102,-1102,-1102,-1102,-1102,-1102,-1102,-1102,-1102,-1102, + -1102,-1102,-1102,-1102,-1102,-1102,-1102,-1102,-1102,-1102, + -1102,-1102,-1102,-1102,-1102,-1102,-1102,-1102,-1102,-1102, + -1102,-1102,-1102,-1102,-1102,-1102,-1102,-1102,-1102,-1102, + -1102,-1102,-1102,-1102,-1102,-1102,-1102,-1102,-1102,-1102, + -1102,-1102,-1102,-1102,-1102,-1102,-1102,-1102,-1102,-1102, + -1102,-1102,-1102,-1102,-1102,-1102,-1102,-1102,-1102,-1102, + -1102,-1102,-1102,-1102,-1102,-1102,-1102,-1102,-1102,-1102, + -1102,-1102,-1102,-1102,-1102,-1102,-1102,-1102 + }, + + { + 69,-1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103, + + -1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103, + -1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103, + -1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103, + -1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103, + -1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103, + -1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103, + -1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103, + -1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103, + -1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103, + -1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103, + + -1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103, + -1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103 + }, + + { + 69,-1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104, + -1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104, + -1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104, + -1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104, + -1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104, + -1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104, + -1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104, + -1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104, + + -1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104, + -1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104, + -1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104, + -1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104, + -1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104 + }, + + { + 69,-1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105, + -1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105, + -1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105, + -1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105, + -1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105, + + -1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105, + -1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105, + -1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105, + -1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105, + -1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105, + -1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105, + -1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105, + -1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105 + }, + + { + 69,-1106,-1106,-1106,-1106,-1106,-1106,-1106,-1106,-1106, + -1106,-1106,-1106,-1106,-1106,-1106,-1106,-1106,-1106,-1106, + + -1106,-1106,-1106,-1106,-1106,-1106,-1106,-1106,-1106,-1106, + -1106,-1106,-1106,-1106,-1106,-1106,-1106,-1106,-1106,-1106, + -1106,-1106,-1106,-1106,-1106,-1106,-1106,-1106,-1106,-1106, + -1106,-1106,-1106,-1106,-1106,-1106,-1106,-1106,-1106,-1106, + -1106,-1106,-1106,-1106,-1106,-1106,-1106,-1106,-1106,-1106, + -1106,-1106,-1106,-1106,-1106,-1106,-1106,-1106,-1106,-1106, + -1106,-1106,-1106,-1106,-1106,-1106,-1106,-1106,-1106,-1106, + -1106,-1106,-1106,-1106,-1106,-1106,-1106,-1106,-1106,-1106, + -1106,-1106,-1106,-1106,-1106,-1106,-1106,-1106,-1106,-1106, + -1106,-1106,-1106,-1106,-1106,-1106,-1106,-1106,-1106,-1106, + + -1106,-1106,-1106,-1106,-1106,-1106,-1106,-1106 + }, + + { + 69,-1107,-1107,-1107,-1107,-1107,-1107,-1107,-1107,-1107, + -1107,-1107,-1107,-1107,-1107,-1107,-1107,-1107,-1107,-1107, + -1107,-1107,-1107,-1107,-1107,-1107,-1107,-1107,-1107,-1107, + -1107,-1107,-1107,-1107,-1107,-1107,-1107,-1107,-1107,-1107, + -1107,-1107,-1107,-1107,-1107,-1107,-1107,-1107,-1107,-1107, + -1107,-1107,-1107,-1107,-1107,-1107,-1107,-1107,-1107,-1107, + -1107,-1107,-1107,-1107,-1107,-1107,-1107,-1107,-1107,-1107, + -1107,-1107,-1107,-1107,-1107,-1107,-1107,-1107,-1107,-1107, + -1107,-1107,-1107,-1107,-1107,-1107,-1107,-1107,-1107,-1107, + + -1107,-1107,-1107,-1107,-1107,-1107,-1107,-1107,-1107,-1107, + -1107,-1107,-1107,-1107,-1107,-1107,-1107,-1107,-1107,-1107, + -1107,-1107,-1107,-1107,-1107,-1107,-1107,-1107,-1107,-1107, + -1107,-1107,-1107,-1107,-1107,-1107,-1107,-1107 + }, + + { + 69,-1108,-1108,-1108,-1108,-1108,-1108,-1108,-1108,-1108, + -1108,-1108,-1108,-1108,-1108,-1108,-1108,-1108,-1108,-1108, + -1108,-1108,-1108,-1108,-1108,-1108,-1108,-1108,-1108,-1108, + -1108,-1108,-1108,-1108,-1108,-1108,-1108,-1108,-1108,-1108, + -1108,-1108,-1108,-1108,-1108,-1108,-1108,-1108,-1108,-1108, + -1108,-1108,-1108,-1108,-1108,-1108,-1108,-1108,-1108,-1108, + + -1108,-1108,-1108,-1108,-1108,-1108,-1108,-1108,-1108,-1108, + -1108,-1108,-1108,-1108,-1108,-1108,-1108,-1108,-1108,-1108, + -1108,-1108,-1108,-1108,-1108,-1108,-1108,-1108,-1108,-1108, + -1108,-1108,-1108,-1108,-1108,-1108,-1108,-1108,-1108,-1108, + -1108,-1108,-1108,-1108,-1108,-1108,-1108,-1108,-1108,-1108, + -1108,-1108,-1108,-1108,-1108,-1108,-1108,-1108,-1108,-1108, + -1108,-1108,-1108,-1108,-1108,-1108,-1108,-1108 + }, + + { + 69,-1109,-1109,-1109,-1109,-1109,-1109,-1109,-1109,-1109, + -1109,-1109,-1109,-1109,-1109,-1109,-1109,-1109,-1109,-1109, + -1109,-1109,-1109,-1109,-1109,-1109,-1109,-1109,-1109,-1109, + + -1109,-1109,-1109,-1109,-1109,-1109,-1109,-1109,-1109,-1109, + -1109,-1109,-1109,-1109,-1109,-1109,-1109,-1109,-1109,-1109, + -1109,-1109,-1109,-1109,-1109,-1109,-1109,-1109,-1109,-1109, + -1109,-1109,-1109,-1109,-1109,-1109,-1109,-1109,-1109,-1109, + -1109,-1109,-1109,-1109,-1109,-1109,-1109,-1109,-1109,-1109, + -1109,-1109,-1109,-1109,-1109,-1109,-1109,-1109,-1109,-1109, + -1109,-1109,-1109,-1109,-1109,-1109,-1109,-1109,-1109,-1109, + -1109,-1109,-1109,-1109,-1109,-1109,-1109,-1109,-1109,-1109, + -1109,-1109,-1109,-1109,-1109,-1109,-1109,-1109,-1109,-1109, + -1109,-1109,-1109,-1109,-1109,-1109,-1109,-1109 + + }, + + { + 69,-1110,-1110,-1110,-1110,-1110,-1110,-1110,-1110,-1110, + -1110,-1110,-1110,-1110,-1110,-1110,-1110,-1110,-1110,-1110, + -1110,-1110,-1110,-1110,-1110,-1110,-1110,-1110,-1110,-1110, + -1110,-1110,-1110,-1110,-1110,-1110,-1110,-1110,-1110,-1110, + -1110,-1110,-1110,-1110,-1110,-1110,-1110,-1110,-1110,-1110, + -1110,-1110,-1110,-1110,-1110,-1110,-1110,-1110,-1110,-1110, + -1110,-1110,-1110,-1110,-1110,-1110,-1110,-1110,-1110,-1110, + -1110,-1110,-1110,-1110,-1110,-1110,-1110,-1110,-1110,-1110, + -1110,-1110,-1110,-1110,-1110,-1110,-1110,-1110,-1110,-1110, + -1110,-1110,-1110,-1110,-1110,-1110,-1110,-1110,-1110,-1110, + + -1110,-1110,-1110,-1110,-1110,-1110,-1110,-1110,-1110,-1110, + -1110,-1110,-1110,-1110,-1110,-1110,-1110,-1110,-1110,-1110, + -1110,-1110,-1110,-1110,-1110,-1110,-1110,-1110 + }, + + { + 69,-1111,-1111,-1111,-1111,-1111,-1111,-1111,-1111,-1111, + -1111,-1111,-1111,-1111,-1111,-1111,-1111,-1111,-1111,-1111, + -1111,-1111,-1111,-1111,-1111,-1111,-1111,-1111,-1111,-1111, + -1111,-1111,-1111,-1111,-1111,-1111,-1111,-1111,-1111,-1111, + -1111,-1111,-1111,-1111,-1111,-1111,-1111,-1111,-1111,-1111, + -1111,-1111,-1111,-1111,-1111,-1111,-1111,-1111,-1111,-1111, + -1111,-1111,-1111,-1111,-1111,-1111,-1111,-1111,-1111,-1111, + + -1111,-1111,-1111,-1111,-1111,-1111,-1111,-1111,-1111,-1111, + -1111,-1111,-1111,-1111,-1111,-1111,-1111,-1111,-1111,-1111, + -1111,-1111,-1111,-1111,-1111,-1111,-1111,-1111,-1111,-1111, + -1111,-1111,-1111,-1111,-1111,-1111,-1111,-1111,-1111,-1111, + -1111,-1111,-1111,-1111,-1111,-1111,-1111,-1111,-1111,-1111, + -1111,-1111,-1111,-1111,-1111,-1111,-1111,-1111 + }, + + { + 69,-1112,-1112,-1112,-1112,-1112,-1112,-1112,-1112,-1112, + -1112,-1112,-1112,-1112,-1112,-1112,-1112,-1112,-1112,-1112, + -1112,-1112,-1112,-1112,-1112,-1112,-1112,-1112,-1112,-1112, + -1112,-1112,-1112,-1112,-1112,-1112,-1112,-1112,-1112,-1112, + + -1112,-1112,-1112,-1112,-1112,-1112,-1112,-1112,-1112,-1112, + -1112,-1112,-1112,-1112,-1112,-1112,-1112,-1112,-1112,-1112, + -1112,-1112,-1112,-1112,-1112,-1112,-1112,-1112,-1112,-1112, + -1112,-1112,-1112,-1112,-1112,-1112,-1112,-1112,-1112,-1112, + -1112,-1112,-1112,-1112,-1112,-1112,-1112,-1112,-1112,-1112, + -1112,-1112,-1112,-1112,-1112,-1112,-1112,-1112,-1112,-1112, + -1112,-1112,-1112,-1112,-1112,-1112,-1112,-1112,-1112,-1112, + -1112,-1112,-1112,-1112,-1112,-1112,-1112,-1112,-1112,-1112, + -1112,-1112,-1112,-1112,-1112,-1112,-1112,-1112 + }, + + { + 69,-1113,-1113,-1113,-1113,-1113,-1113,-1113,-1113,-1113, + + -1113,-1113,-1113,-1113,-1113,-1113,-1113,-1113,-1113,-1113, + -1113,-1113,-1113,-1113,-1113,-1113,-1113,-1113,-1113,-1113, + -1113,-1113,-1113,-1113,-1113,-1113,-1113,-1113,-1113,-1113, + -1113,-1113,-1113,-1113,-1113,-1113,-1113,-1113,-1113,-1113, + -1113,-1113,-1113,-1113,-1113,-1113,-1113,-1113,-1113,-1113, + -1113,-1113,-1113,-1113,-1113,-1113,-1113,-1113,-1113,-1113, + -1113,-1113,-1113,-1113,-1113,-1113,-1113,-1113,-1113,-1113, + -1113,-1113,-1113,-1113,-1113,-1113,-1113,-1113,-1113,-1113, + -1113,-1113,-1113,-1113,-1113,-1113,-1113,-1113,-1113,-1113, + -1113,-1113,-1113,-1113,-1113,-1113,-1113,-1113,-1113,-1113, + + -1113,-1113,-1113,-1113,-1113,-1113,-1113,-1113,-1113,-1113, + -1113,-1113,-1113,-1113,-1113,-1113,-1113,-1113 + }, + + { + 69,-1114,-1114,-1114,-1114,-1114,-1114,-1114,-1114,-1114, + -1114,-1114,-1114,-1114,-1114,-1114,-1114,-1114,-1114,-1114, + -1114,-1114,-1114,-1114,-1114,-1114,-1114,-1114,-1114,-1114, + -1114,-1114,-1114,-1114,-1114,-1114,-1114,-1114,-1114,-1114, + -1114,-1114,-1114,-1114,-1114,-1114,-1114,-1114,-1114,-1114, + -1114,-1114,-1114,-1114,-1114,-1114,-1114,-1114,-1114,-1114, + -1114,-1114,-1114,-1114,-1114,-1114,-1114,-1114,-1114,-1114, + -1114,-1114,-1114,-1114,-1114,-1114,-1114,-1114,-1114,-1114, + + -1114,-1114,-1114,-1114,-1114,-1114,-1114,-1114,-1114,-1114, + -1114,-1114,-1114,-1114,-1114,-1114,-1114,-1114,-1114,-1114, + -1114,-1114,-1114,-1114,-1114,-1114,-1114,-1114,-1114,-1114, + -1114,-1114,-1114,-1114,-1114,-1114,-1114,-1114,-1114,-1114, + -1114,-1114,-1114,-1114,-1114,-1114,-1114,-1114 + }, + + { + 69,-1115,-1115,-1115,-1115,-1115,-1115,-1115,-1115,-1115, + -1115,-1115,-1115,-1115,-1115,-1115,-1115,-1115,-1115,-1115, + -1115,-1115,-1115,-1115,-1115,-1115,-1115,-1115,-1115,-1115, + -1115,-1115,-1115,-1115,-1115,-1115,-1115,-1115,-1115,-1115, + -1115,-1115,-1115,-1115,-1115,-1115,-1115,-1115,-1115,-1115, + + -1115,-1115,-1115,-1115,-1115,-1115,-1115,-1115,-1115,-1115, + -1115,-1115,-1115,-1115,-1115,-1115,-1115,-1115,-1115,-1115, + -1115,-1115,-1115,-1115,-1115,-1115,-1115,-1115,-1115,-1115, + -1115,-1115,-1115,-1115,-1115,-1115,-1115,-1115,-1115,-1115, + -1115,-1115,-1115,-1115,-1115,-1115,-1115,-1115,-1115,-1115, + -1115,-1115,-1115,-1115,-1115,-1115,-1115,-1115,-1115,-1115, + -1115,-1115,-1115,-1115,-1115,-1115,-1115,-1115,-1115,-1115, + -1115,-1115,-1115,-1115,-1115,-1115,-1115,-1115 + }, + + { + 69,-1116,-1116,-1116,-1116,-1116,-1116,-1116,-1116,-1116, + -1116,-1116,-1116,-1116,-1116,-1116,-1116,-1116,-1116,-1116, + + -1116,-1116,-1116,-1116,-1116,-1116,-1116,-1116,-1116,-1116, + -1116,-1116,-1116,-1116,-1116,-1116,-1116,-1116,-1116,-1116, + -1116,-1116,-1116,-1116,-1116,-1116,-1116,-1116,-1116,-1116, + -1116,-1116,-1116,-1116,-1116,-1116,-1116,-1116,-1116,-1116, + -1116,-1116,-1116,-1116,-1116,-1116,-1116,-1116,-1116,-1116, + -1116,-1116,-1116,-1116,-1116,-1116,-1116,-1116,-1116,-1116, + -1116,-1116,-1116,-1116,-1116,-1116,-1116,-1116,-1116,-1116, + -1116,-1116,-1116,-1116,-1116,-1116,-1116,-1116,-1116,-1116, + -1116,-1116,-1116,-1116,-1116,-1116,-1116,-1116,-1116,-1116, + -1116,-1116,-1116,-1116,-1116,-1116,-1116,-1116,-1116,-1116, + + -1116,-1116,-1116,-1116,-1116,-1116,-1116,-1116 + }, + + { + 69,-1117,-1117,-1117,-1117,-1117,-1117,-1117,-1117,-1117, + -1117,-1117,-1117,-1117,-1117,-1117,-1117,-1117,-1117,-1117, + -1117,-1117,-1117,-1117,-1117,-1117,-1117,-1117,-1117,-1117, + -1117,-1117,-1117,-1117,-1117,-1117,-1117,-1117,-1117,-1117, + -1117,-1117,-1117,-1117,-1117,-1117,-1117,-1117,-1117,-1117, + -1117,-1117,-1117,-1117,-1117,-1117,-1117,-1117,-1117,-1117, + -1117,-1117,-1117,-1117,-1117,-1117,-1117,-1117,-1117,-1117, + -1117,-1117,-1117,-1117,-1117,-1117,-1117,-1117,-1117,-1117, + -1117,-1117,-1117,-1117,-1117,-1117,-1117,-1117,-1117,-1117, + + -1117,-1117,-1117,-1117,-1117,-1117,-1117,-1117,-1117,-1117, + -1117,-1117,-1117,-1117,-1117,-1117,-1117,-1117,-1117,-1117, + -1117,-1117,-1117,-1117,-1117,-1117,-1117,-1117,-1117,-1117, + -1117,-1117,-1117,-1117,-1117,-1117,-1117,-1117 + }, + + { + 69,-1118,-1118,-1118,-1118,-1118,-1118,-1118,-1118,-1118, + -1118,-1118,-1118,-1118,-1118,-1118,-1118,-1118,-1118,-1118, + -1118,-1118,-1118,-1118,-1118,-1118,-1118,-1118,-1118,-1118, + -1118,-1118,-1118,-1118,-1118,-1118,-1118,-1118,-1118,-1118, + -1118,-1118,-1118,-1118,-1118,-1118,-1118,-1118,-1118,-1118, + -1118,-1118,-1118,-1118,-1118,-1118,-1118,-1118,-1118,-1118, + + -1118,-1118,-1118,-1118,-1118,-1118,-1118,-1118,-1118,-1118, + -1118,-1118,-1118,-1118,-1118,-1118,-1118,-1118,-1118,-1118, + -1118,-1118,-1118,-1118,-1118,-1118,-1118,-1118,-1118,-1118, + -1118,-1118,-1118,-1118,-1118,-1118,-1118,-1118,-1118,-1118, + -1118,-1118,-1118,-1118,-1118,-1118,-1118,-1118,-1118,-1118, + -1118,-1118,-1118,-1118,-1118,-1118,-1118,-1118,-1118,-1118, + -1118,-1118,-1118,-1118,-1118,-1118,-1118,-1118 + }, + + { + 69,-1119,-1119,-1119,-1119,-1119,-1119,-1119,-1119,-1119, + -1119,-1119,-1119,-1119,-1119,-1119,-1119,-1119,-1119,-1119, + -1119,-1119,-1119,-1119,-1119,-1119,-1119,-1119,-1119,-1119, + + -1119,-1119,-1119,-1119,-1119,-1119,-1119,-1119,-1119,-1119, + -1119,-1119,-1119,-1119,-1119,-1119,-1119,-1119,-1119,-1119, + -1119,-1119,-1119,-1119,-1119,-1119,-1119,-1119,-1119,-1119, + -1119,-1119,-1119,-1119,-1119,-1119,-1119,-1119,-1119,-1119, + -1119,-1119,-1119,-1119,-1119,-1119,-1119,-1119,-1119,-1119, + -1119,-1119,-1119,-1119,-1119,-1119,-1119,-1119,-1119,-1119, + -1119,-1119,-1119,-1119,-1119,-1119,-1119,-1119,-1119,-1119, + -1119,-1119,-1119,-1119,-1119,-1119,-1119,-1119,-1119,-1119, + -1119,-1119,-1119,-1119,-1119,-1119,-1119,-1119,-1119,-1119, + -1119,-1119,-1119,-1119,-1119,-1119,-1119,-1119 + + }, + + { + 69,-1120,-1120,-1120,-1120,-1120,-1120,-1120,-1120,-1120, + -1120,-1120,-1120,-1120,-1120,-1120,-1120,-1120,-1120,-1120, + -1120,-1120,-1120,-1120,-1120,-1120,-1120,-1120,-1120,-1120, + -1120,-1120,-1120,-1120,-1120,-1120,-1120,-1120,-1120,-1120, + -1120,-1120,-1120,-1120,-1120,-1120,-1120,-1120,-1120,-1120, + -1120,-1120,-1120,-1120,-1120,-1120,-1120,-1120,-1120,-1120, + -1120,-1120,-1120,-1120,-1120,-1120,-1120,-1120,-1120,-1120, + -1120,-1120,-1120,-1120,-1120,-1120,-1120,-1120,-1120,-1120, + -1120,-1120,-1120,-1120,-1120,-1120,-1120,-1120,-1120,-1120, + -1120,-1120,-1120,-1120,-1120,-1120,-1120,-1120,-1120,-1120, + + -1120,-1120,-1120,-1120,-1120,-1120,-1120,-1120,-1120,-1120, + -1120,-1120,-1120,-1120,-1120,-1120,-1120,-1120,-1120,-1120, + -1120,-1120,-1120,-1120,-1120,-1120,-1120,-1120 + }, + + { + 69,-1121,-1121,-1121,-1121,-1121,-1121,-1121,-1121,-1121, + -1121,-1121,-1121,-1121,-1121,-1121,-1121,-1121,-1121,-1121, + -1121,-1121,-1121,-1121,-1121,-1121,-1121,-1121,-1121,-1121, + -1121,-1121,-1121,-1121,-1121,-1121,-1121,-1121,-1121,-1121, + -1121,-1121,-1121,-1121,-1121,-1121,-1121,-1121,-1121,-1121, + -1121,-1121,-1121,-1121,-1121,-1121,-1121,-1121,-1121,-1121, + -1121,-1121,-1121,-1121,-1121,-1121,-1121,-1121,-1121,-1121, + + -1121,-1121,-1121,-1121,-1121,-1121,-1121,-1121,-1121,-1121, + -1121,-1121,-1121,-1121,-1121,-1121,-1121,-1121,-1121,-1121, + -1121,-1121,-1121,-1121,-1121,-1121,-1121,-1121,-1121,-1121, + -1121,-1121,-1121,-1121,-1121,-1121,-1121,-1121,-1121,-1121, + -1121,-1121,-1121,-1121,-1121,-1121,-1121,-1121,-1121,-1121, + -1121,-1121,-1121,-1121,-1121,-1121,-1121,-1121 + }, + + { + 69,-1122,-1122,-1122,-1122,-1122,-1122,-1122,-1122,-1122, + -1122,-1122,-1122,-1122,-1122,-1122,-1122,-1122,-1122,-1122, + -1122,-1122,-1122,-1122,-1122,-1122,-1122,-1122,-1122,-1122, + -1122,-1122,-1122,-1122,-1122,-1122,-1122,-1122,-1122,-1122, + + -1122,-1122,-1122,-1122,-1122,-1122,-1122,-1122,-1122,-1122, + -1122,-1122,-1122,-1122,-1122,-1122,-1122,-1122,-1122,-1122, + -1122,-1122,-1122,-1122,-1122,-1122,-1122,-1122,-1122,-1122, + -1122,-1122,-1122,-1122,-1122,-1122,-1122,-1122,-1122,-1122, + -1122,-1122,-1122,-1122,-1122,-1122,-1122,-1122,-1122,-1122, + -1122,-1122,-1122,-1122,-1122,-1122,-1122,-1122,-1122,-1122, + -1122,-1122,-1122,-1122,-1122,-1122,-1122,-1122,-1122,-1122, + -1122,-1122,-1122,-1122,-1122,-1122,-1122,-1122,-1122,-1122, + -1122,-1122,-1122,-1122,-1122,-1122,-1122,-1122 + }, + + { + 69,-1123,-1123,-1123,-1123,-1123,-1123,-1123,-1123,-1123, + + -1123,-1123,-1123,-1123,-1123,-1123,-1123,-1123,-1123,-1123, + -1123,-1123,-1123,-1123,-1123,-1123,-1123,-1123,-1123,-1123, + -1123,-1123,-1123,-1123,-1123,-1123,-1123,-1123,-1123,-1123, + -1123,-1123,-1123,-1123,-1123,-1123,-1123,-1123,-1123,-1123, + -1123,-1123,-1123,-1123,-1123,-1123,-1123,-1123,-1123,-1123, + -1123,-1123,-1123,-1123,-1123,-1123,-1123,-1123,-1123,-1123, + -1123,-1123,-1123,-1123,-1123,-1123,-1123,-1123,-1123,-1123, + -1123,-1123,-1123,-1123,-1123,-1123,-1123,-1123,-1123,-1123, + -1123,-1123,-1123,-1123,-1123,-1123,-1123,-1123,-1123,-1123, + -1123,-1123,-1123,-1123,-1123,-1123,-1123,-1123,-1123,-1123, + + -1123,-1123,-1123,-1123,-1123,-1123,-1123,-1123,-1123,-1123, + -1123,-1123,-1123,-1123,-1123,-1123,-1123,-1123 + }, + + { + 69,-1124,-1124,-1124,-1124,-1124,-1124,-1124,-1124,-1124, + -1124,-1124,-1124,-1124,-1124,-1124,-1124,-1124,-1124,-1124, + -1124,-1124,-1124,-1124,-1124,-1124,-1124,-1124,-1124,-1124, + -1124,-1124,-1124,-1124,-1124,-1124,-1124,-1124,-1124,-1124, + -1124,-1124,-1124,-1124,-1124,-1124,-1124,-1124,-1124,-1124, + -1124,-1124,-1124,-1124,-1124,-1124,-1124,-1124,-1124,-1124, + -1124,-1124,-1124,-1124,-1124,-1124,-1124,-1124,-1124,-1124, + -1124,-1124,-1124,-1124,-1124,-1124,-1124,-1124,-1124,-1124, + + -1124,-1124,-1124,-1124,-1124,-1124,-1124,-1124,-1124,-1124, + -1124,-1124,-1124,-1124,-1124,-1124,-1124,-1124,-1124,-1124, + -1124,-1124,-1124,-1124,-1124,-1124,-1124,-1124,-1124,-1124, + -1124,-1124,-1124,-1124,-1124,-1124,-1124,-1124,-1124,-1124, + -1124,-1124,-1124,-1124,-1124,-1124,-1124,-1124 + }, + + { + 69,-1125,-1125,-1125,-1125,-1125,-1125,-1125,-1125,-1125, + -1125,-1125,-1125,-1125,-1125,-1125,-1125,-1125,-1125,-1125, + -1125,-1125,-1125,-1125,-1125,-1125,-1125,-1125,-1125,-1125, + -1125,-1125,-1125,-1125,-1125,-1125,-1125,-1125,-1125,-1125, + -1125,-1125,-1125,-1125,-1125,-1125,-1125,-1125,-1125,-1125, + + -1125,-1125,-1125,-1125,-1125,-1125,-1125,-1125,-1125,-1125, + -1125,-1125,-1125,-1125,-1125,-1125,-1125,-1125,-1125,-1125, + -1125,-1125,-1125,-1125,-1125,-1125,-1125,-1125,-1125,-1125, + -1125,-1125,-1125,-1125,-1125,-1125,-1125,-1125,-1125,-1125, + -1125,-1125,-1125,-1125,-1125,-1125,-1125,-1125,-1125,-1125, + -1125,-1125,-1125,-1125,-1125,-1125,-1125,-1125,-1125,-1125, + -1125,-1125,-1125,-1125,-1125,-1125,-1125,-1125,-1125,-1125, + -1125,-1125,-1125,-1125,-1125,-1125,-1125,-1125 + }, + + { + 69,-1126,-1126,-1126,-1126,-1126,-1126,-1126,-1126,-1126, + -1126,-1126,-1126,-1126,-1126,-1126,-1126,-1126,-1126,-1126, + + -1126,-1126,-1126,-1126,-1126,-1126,-1126,-1126,-1126,-1126, + -1126,-1126,-1126,-1126,-1126,-1126,-1126,-1126,-1126,-1126, + -1126,-1126,-1126,-1126,-1126,-1126,-1126,-1126,-1126,-1126, + -1126,-1126,-1126,-1126,-1126,-1126,-1126,-1126,-1126,-1126, + -1126,-1126,-1126,-1126,-1126,-1126,-1126,-1126,-1126,-1126, + -1126,-1126,-1126,-1126,-1126,-1126,-1126,-1126,-1126,-1126, + -1126,-1126,-1126,-1126,-1126,-1126,-1126,-1126,-1126,-1126, + -1126,-1126,-1126,-1126,-1126,-1126,-1126,-1126,-1126,-1126, + -1126,-1126,-1126,-1126,-1126,-1126,-1126,-1126,-1126,-1126, + -1126,-1126,-1126,-1126,-1126,-1126,-1126,-1126,-1126,-1126, + + -1126,-1126,-1126,-1126,-1126,-1126,-1126,-1126 + }, + + { + 69,-1127,-1127,-1127,-1127,-1127,-1127,-1127,-1127,-1127, + -1127,-1127,-1127,-1127,-1127,-1127,-1127,-1127,-1127,-1127, + -1127,-1127,-1127,-1127,-1127,-1127,-1127,-1127,-1127,-1127, + -1127,-1127,-1127,-1127,-1127,-1127,-1127,-1127,-1127,-1127, + -1127,-1127,-1127,-1127,-1127,-1127,-1127,-1127,-1127,-1127, + -1127,-1127,-1127,-1127,-1127,-1127,-1127,-1127,-1127,-1127, + -1127,-1127,-1127,-1127,-1127,-1127,-1127,-1127,-1127,-1127, + -1127,-1127,-1127,-1127,-1127,-1127,-1127,-1127,-1127,-1127, + -1127,-1127,-1127,-1127,-1127,-1127,-1127,-1127,-1127,-1127, + + -1127,-1127,-1127,-1127,-1127,-1127,-1127,-1127,-1127,-1127, + -1127,-1127,-1127,-1127,-1127,-1127,-1127,-1127,-1127,-1127, + -1127,-1127,-1127,-1127,-1127,-1127,-1127,-1127,-1127,-1127, + -1127,-1127,-1127,-1127,-1127,-1127,-1127,-1127 + }, + + { + 69,-1128,-1128,-1128,-1128,-1128,-1128,-1128,-1128,-1128, + -1128,-1128,-1128,-1128,-1128,-1128,-1128,-1128,-1128,-1128, + -1128,-1128,-1128,-1128,-1128,-1128,-1128,-1128,-1128,-1128, + -1128,-1128,-1128,-1128,-1128,-1128,-1128,-1128,-1128,-1128, + -1128,-1128,-1128,-1128,-1128,-1128,-1128,-1128,-1128,-1128, + -1128,-1128,-1128,-1128,-1128,-1128,-1128,-1128,-1128,-1128, + + -1128,-1128,-1128,-1128,-1128,-1128,-1128,-1128,-1128,-1128, + -1128,-1128,-1128,-1128,-1128,-1128,-1128,-1128,-1128,-1128, + -1128,-1128,-1128,-1128,-1128,-1128,-1128,-1128,-1128,-1128, + -1128,-1128,-1128,-1128,-1128,-1128,-1128,-1128,-1128,-1128, + -1128,-1128,-1128,-1128,-1128,-1128,-1128,-1128,-1128,-1128, + -1128,-1128,-1128,-1128,-1128,-1128,-1128,-1128,-1128,-1128, + -1128,-1128,-1128,-1128,-1128,-1128,-1128,-1128 + }, + + { + 69,-1129,-1129,-1129,-1129,-1129,-1129,-1129,-1129,-1129, + -1129,-1129,-1129,-1129,-1129,-1129,-1129,-1129,-1129,-1129, + -1129,-1129,-1129,-1129,-1129,-1129,-1129,-1129,-1129,-1129, + + -1129,-1129,-1129,-1129,-1129,-1129,-1129,-1129,-1129,-1129, + -1129,-1129,-1129,-1129,-1129,-1129,-1129,-1129,-1129,-1129, + -1129,-1129,-1129,-1129,-1129,-1129,-1129,-1129,-1129,-1129, + -1129,-1129,-1129,-1129,-1129,-1129,-1129,-1129,-1129,-1129, + -1129,-1129,-1129,-1129,-1129,-1129,-1129,-1129,-1129,-1129, + -1129,-1129,-1129,-1129,-1129,-1129,-1129,-1129,-1129,-1129, + -1129,-1129,-1129,-1129,-1129,-1129,-1129,-1129,-1129,-1129, + -1129,-1129,-1129,-1129,-1129,-1129,-1129,-1129,-1129,-1129, + -1129,-1129,-1129,-1129,-1129,-1129,-1129,-1129,-1129,-1129, + -1129,-1129,-1129,-1129,-1129,-1129,-1129,-1129 + + }, + + { + 69,-1130,-1130,-1130,-1130,-1130,-1130,-1130,-1130,-1130, + -1130,-1130,-1130,-1130,-1130,-1130,-1130,-1130,-1130,-1130, + -1130,-1130,-1130,-1130,-1130,-1130,-1130,-1130,-1130,-1130, + -1130,-1130,-1130,-1130,-1130,-1130,-1130,-1130,-1130,-1130, + -1130,-1130,-1130,-1130,-1130,-1130,-1130,-1130,-1130,-1130, + -1130,-1130,-1130,-1130,-1130,-1130,-1130,-1130,-1130,-1130, + -1130,-1130,-1130,-1130,-1130,-1130,-1130,-1130,-1130,-1130, + -1130,-1130,-1130,-1130,-1130,-1130,-1130,-1130,-1130,-1130, + -1130,-1130,-1130,-1130,-1130,-1130,-1130,-1130,-1130,-1130, + -1130,-1130,-1130,-1130,-1130,-1130,-1130,-1130,-1130,-1130, + + -1130,-1130,-1130,-1130,-1130,-1130,-1130,-1130,-1130,-1130, + -1130,-1130,-1130,-1130,-1130,-1130,-1130,-1130,-1130,-1130, + -1130,-1130,-1130,-1130,-1130,-1130,-1130,-1130 + }, + + { + 69,-1131,-1131,-1131,-1131,-1131,-1131,-1131,-1131,-1131, + -1131,-1131,-1131,-1131,-1131,-1131,-1131,-1131,-1131,-1131, + -1131,-1131,-1131,-1131,-1131,-1131,-1131,-1131,-1131,-1131, + -1131,-1131,-1131,-1131,-1131,-1131,-1131,-1131,-1131,-1131, + -1131,-1131,-1131,-1131,-1131,-1131,-1131,-1131,-1131,-1131, + -1131,-1131,-1131,-1131,-1131,-1131,-1131,-1131,-1131,-1131, + -1131,-1131,-1131,-1131,-1131,-1131,-1131,-1131,-1131,-1131, + + -1131,-1131,-1131,-1131,-1131,-1131,-1131,-1131,-1131,-1131, + -1131,-1131,-1131,-1131,-1131,-1131,-1131,-1131,-1131,-1131, + -1131,-1131,-1131,-1131,-1131,-1131,-1131,-1131,-1131,-1131, + -1131,-1131,-1131,-1131,-1131,-1131,-1131,-1131,-1131,-1131, + -1131,-1131,-1131,-1131,-1131,-1131,-1131,-1131,-1131,-1131, + -1131,-1131,-1131,-1131,-1131,-1131,-1131,-1131 + }, + + { + 69,-1132,-1132,-1132,-1132,-1132,-1132,-1132,-1132,-1132, + -1132,-1132,-1132,-1132,-1132,-1132,-1132,-1132,-1132,-1132, + -1132,-1132,-1132,-1132,-1132,-1132,-1132,-1132,-1132,-1132, + -1132,-1132,-1132,-1132,-1132,-1132,-1132,-1132,-1132,-1132, + + -1132,-1132,-1132,-1132,-1132,-1132,-1132,-1132,-1132,-1132, + -1132,-1132,-1132,-1132,-1132,-1132,-1132,-1132,-1132,-1132, + -1132,-1132,-1132,-1132,-1132,-1132,-1132,-1132,-1132,-1132, + -1132,-1132,-1132,-1132,-1132,-1132,-1132,-1132,-1132,-1132, + -1132,-1132,-1132,-1132,-1132,-1132,-1132,-1132,-1132,-1132, + -1132,-1132,-1132,-1132,-1132,-1132,-1132,-1132,-1132,-1132, + -1132,-1132,-1132,-1132,-1132,-1132,-1132,-1132,-1132,-1132, + -1132,-1132,-1132,-1132,-1132,-1132,-1132,-1132,-1132,-1132, + -1132,-1132,-1132,-1132,-1132,-1132,-1132,-1132 + }, + + { + 69,-1133,-1133,-1133,-1133,-1133,-1133,-1133,-1133,-1133, + + -1133,-1133,-1133,-1133,-1133,-1133,-1133,-1133,-1133,-1133, + -1133,-1133,-1133,-1133,-1133,-1133,-1133,-1133,-1133,-1133, + -1133,-1133,-1133,-1133,-1133,-1133,-1133,-1133,-1133,-1133, + -1133,-1133,-1133,-1133,-1133,-1133,-1133,-1133,-1133,-1133, + -1133,-1133,-1133,-1133,-1133,-1133,-1133,-1133,-1133,-1133, + -1133,-1133,-1133,-1133,-1133,-1133,-1133,-1133,-1133,-1133, + -1133,-1133,-1133,-1133,-1133,-1133,-1133,-1133,-1133,-1133, + -1133,-1133,-1133,-1133,-1133,-1133,-1133,-1133,-1133,-1133, + -1133,-1133,-1133,-1133,-1133,-1133,-1133,-1133,-1133,-1133, + -1133,-1133,-1133,-1133,-1133,-1133,-1133,-1133,-1133,-1133, + + -1133,-1133,-1133,-1133,-1133,-1133,-1133,-1133,-1133,-1133, + -1133,-1133,-1133,-1133,-1133,-1133,-1133,-1133 + }, + + { + 69,-1134,-1134,-1134,-1134,-1134,-1134,-1134,-1134,-1134, + -1134,-1134,-1134,-1134,-1134,-1134,-1134,-1134,-1134,-1134, + -1134,-1134,-1134,-1134,-1134,-1134,-1134,-1134,-1134,-1134, + -1134,-1134,-1134,-1134,-1134,-1134,-1134,-1134,-1134,-1134, + -1134,-1134,-1134,-1134,-1134,-1134,-1134,-1134,-1134,-1134, + -1134,-1134,-1134,-1134,-1134,-1134,-1134,-1134,-1134,-1134, + -1134,-1134,-1134,-1134,-1134,-1134,-1134,-1134,-1134,-1134, + -1134,-1134,-1134,-1134,-1134,-1134,-1134,-1134,-1134,-1134, + + -1134,-1134,-1134,-1134,-1134,-1134,-1134,-1134,-1134,-1134, + -1134,-1134,-1134,-1134,-1134,-1134,-1134,-1134,-1134,-1134, + -1134,-1134,-1134,-1134,-1134,-1134,-1134,-1134,-1134,-1134, + -1134,-1134,-1134,-1134,-1134,-1134,-1134,-1134,-1134,-1134, + -1134,-1134,-1134,-1134,-1134,-1134,-1134,-1134 + }, + + { + 69,-1135,-1135,-1135,-1135,-1135,-1135,-1135,-1135,-1135, + -1135,-1135,-1135,-1135,-1135,-1135,-1135,-1135,-1135,-1135, + -1135,-1135,-1135,-1135,-1135,-1135,-1135,-1135,-1135,-1135, + -1135,-1135,-1135,-1135,-1135,-1135,-1135,-1135,-1135,-1135, + -1135,-1135,-1135,-1135,-1135,-1135,-1135,-1135,-1135,-1135, + + -1135,-1135,-1135,-1135,-1135,-1135,-1135,-1135,-1135,-1135, + -1135,-1135,-1135,-1135,-1135,-1135,-1135,-1135,-1135,-1135, + -1135,-1135,-1135,-1135,-1135,-1135,-1135,-1135,-1135,-1135, + -1135,-1135,-1135,-1135,-1135,-1135,-1135,-1135,-1135,-1135, + -1135,-1135,-1135,-1135,-1135,-1135,-1135,-1135,-1135,-1135, + -1135,-1135,-1135,-1135,-1135,-1135,-1135,-1135,-1135,-1135, + -1135,-1135,-1135,-1135,-1135,-1135,-1135,-1135,-1135,-1135, + -1135,-1135,-1135,-1135,-1135,-1135,-1135,-1135 + }, + + { + 69,-1136,-1136,-1136,-1136,-1136,-1136,-1136,-1136,-1136, + -1136,-1136,-1136,-1136,-1136,-1136,-1136,-1136,-1136,-1136, + + -1136,-1136,-1136,-1136,-1136,-1136,-1136,-1136,-1136,-1136, + -1136,-1136,-1136,-1136,-1136,-1136,-1136,-1136,-1136,-1136, + -1136,-1136,-1136,-1136,-1136,-1136,-1136,-1136,-1136,-1136, + -1136,-1136,-1136,-1136,-1136,-1136,-1136,-1136,-1136,-1136, + -1136,-1136,-1136,-1136,-1136,-1136,-1136,-1136,-1136,-1136, + -1136,-1136,-1136,-1136,-1136,-1136,-1136,-1136,-1136,-1136, + -1136,-1136,-1136,-1136,-1136,-1136,-1136,-1136,-1136,-1136, + -1136,-1136,-1136,-1136,-1136,-1136,-1136,-1136,-1136,-1136, + -1136,-1136,-1136,-1136,-1136,-1136,-1136,-1136,-1136,-1136, + -1136,-1136,-1136,-1136,-1136,-1136,-1136,-1136,-1136,-1136, + + -1136,-1136,-1136,-1136,-1136,-1136,-1136,-1136 + }, + + { + 69,-1137,-1137,-1137,-1137,-1137,-1137,-1137,-1137,-1137, + -1137,-1137,-1137,-1137,-1137,-1137,-1137,-1137,-1137,-1137, + -1137,-1137,-1137,-1137,-1137,-1137,-1137,-1137,-1137,-1137, + -1137,-1137,-1137,-1137,-1137,-1137,-1137,-1137,-1137,-1137, + -1137,-1137,-1137,-1137,-1137,-1137,-1137,-1137,-1137,-1137, + -1137,-1137,-1137,-1137,-1137,-1137,-1137,-1137,-1137,-1137, + -1137,-1137,-1137,-1137,-1137,-1137,-1137,-1137,-1137,-1137, + -1137,-1137,-1137,-1137,-1137,-1137,-1137,-1137,-1137,-1137, + -1137,-1137,-1137,-1137,-1137,-1137,-1137,-1137,-1137,-1137, + + -1137,-1137,-1137,-1137,-1137,-1137,-1137,-1137,-1137,-1137, + -1137,-1137,-1137,-1137,-1137,-1137,-1137,-1137,-1137,-1137, + -1137,-1137,-1137,-1137,-1137,-1137,-1137,-1137,-1137,-1137, + -1137,-1137,-1137,-1137,-1137,-1137,-1137,-1137 + }, + + { + 69,-1138,-1138,-1138,-1138,-1138,-1138,-1138,-1138,-1138, + -1138,-1138,-1138,-1138,-1138,-1138,-1138,-1138,-1138,-1138, + -1138,-1138,-1138,-1138,-1138,-1138,-1138,-1138,-1138,-1138, + -1138,-1138,-1138,-1138,-1138,-1138,-1138,-1138,-1138,-1138, + -1138,-1138,-1138,-1138,-1138,-1138,-1138,-1138,-1138,-1138, + -1138,-1138,-1138,-1138,-1138,-1138,-1138,-1138,-1138,-1138, + + -1138,-1138,-1138,-1138,-1138,-1138,-1138,-1138,-1138,-1138, + -1138,-1138,-1138,-1138,-1138,-1138,-1138,-1138,-1138,-1138, + -1138,-1138,-1138,-1138,-1138,-1138,-1138,-1138,-1138,-1138, + -1138,-1138,-1138,-1138,-1138,-1138,-1138,-1138,-1138,-1138, + -1138,-1138,-1138,-1138,-1138,-1138,-1138,-1138,-1138,-1138, + -1138,-1138,-1138,-1138,-1138,-1138,-1138,-1138,-1138,-1138, + -1138,-1138,-1138,-1138,-1138,-1138,-1138,-1138 + }, + + { + 69,-1139,-1139,-1139,-1139,-1139,-1139,-1139,-1139,-1139, + -1139,-1139,-1139,-1139,-1139,-1139,-1139,-1139,-1139,-1139, + -1139,-1139,-1139,-1139,-1139,-1139,-1139,-1139,-1139,-1139, + + -1139,-1139,-1139,-1139,-1139,-1139,-1139,-1139,-1139,-1139, + -1139,-1139,-1139,-1139,-1139,-1139,-1139,-1139,-1139,-1139, + -1139,-1139,-1139,-1139,-1139,-1139,-1139,-1139,-1139,-1139, + -1139,-1139,-1139,-1139,-1139,-1139,-1139,-1139,-1139,-1139, + -1139,-1139,-1139,-1139,-1139,-1139,-1139,-1139,-1139,-1139, + -1139,-1139,-1139,-1139,-1139,-1139,-1139,-1139,-1139,-1139, + -1139,-1139,-1139,-1139,-1139,-1139,-1139,-1139,-1139,-1139, + -1139,-1139,-1139,-1139,-1139,-1139,-1139,-1139,-1139,-1139, + -1139,-1139,-1139,-1139,-1139,-1139,-1139,-1139,-1139,-1139, + -1139,-1139,-1139,-1139,-1139,-1139,-1139,-1139 + + }, + + { + 69,-1140,-1140,-1140,-1140,-1140,-1140,-1140,-1140,-1140, + -1140,-1140,-1140,-1140,-1140,-1140,-1140,-1140,-1140,-1140, + -1140,-1140,-1140,-1140,-1140,-1140,-1140,-1140,-1140,-1140, + -1140,-1140,-1140,-1140,-1140,-1140,-1140,-1140,-1140,-1140, + -1140,-1140,-1140,-1140,-1140,-1140,-1140,-1140,-1140,-1140, + -1140,-1140,-1140,-1140,-1140,-1140,-1140,-1140,-1140,-1140, + -1140,-1140,-1140,-1140,-1140,-1140,-1140,-1140,-1140,-1140, + -1140,-1140,-1140,-1140,-1140,-1140,-1140,-1140,-1140,-1140, + -1140,-1140,-1140,-1140,-1140,-1140,-1140,-1140,-1140,-1140, + -1140,-1140,-1140,-1140,-1140,-1140,-1140,-1140,-1140,-1140, + + -1140,-1140,-1140,-1140,-1140,-1140,-1140,-1140,-1140,-1140, + -1140,-1140,-1140,-1140,-1140,-1140,-1140,-1140,-1140,-1140, + -1140,-1140,-1140,-1140,-1140,-1140,-1140,-1140 + }, + + { + 69,-1141,-1141,-1141,-1141,-1141,-1141,-1141,-1141,-1141, + -1141,-1141,-1141,-1141,-1141,-1141,-1141,-1141,-1141,-1141, + -1141,-1141,-1141,-1141,-1141,-1141,-1141,-1141,-1141,-1141, + -1141,-1141,-1141,-1141,-1141,-1141,-1141,-1141,-1141,-1141, + -1141,-1141,-1141,-1141,-1141,-1141,-1141,-1141,-1141,-1141, + -1141,-1141,-1141,-1141,-1141,-1141,-1141,-1141,-1141,-1141, + -1141,-1141,-1141,-1141,-1141,-1141,-1141,-1141,-1141,-1141, + + -1141,-1141,-1141,-1141,-1141,-1141,-1141,-1141,-1141,-1141, + -1141,-1141,-1141,-1141,-1141,-1141,-1141,-1141,-1141,-1141, + -1141,-1141,-1141,-1141,-1141,-1141,-1141,-1141,-1141,-1141, + -1141,-1141,-1141,-1141,-1141,-1141,-1141,-1141,-1141,-1141, + -1141,-1141,-1141,-1141,-1141,-1141,-1141,-1141,-1141,-1141, + -1141,-1141,-1141,-1141,-1141,-1141,-1141,-1141 + }, + + { + 69,-1142,-1142,-1142,-1142,-1142,-1142,-1142,-1142,-1142, + -1142,-1142,-1142,-1142,-1142,-1142,-1142,-1142,-1142,-1142, + -1142,-1142,-1142,-1142,-1142,-1142,-1142,-1142,-1142,-1142, + -1142,-1142,-1142,-1142,-1142,-1142,-1142,-1142,-1142,-1142, + + -1142,-1142,-1142,-1142,-1142,-1142,-1142,-1142,-1142,-1142, + -1142,-1142,-1142,-1142,-1142,-1142,-1142,-1142,-1142,-1142, + -1142,-1142,-1142,-1142,-1142,-1142,-1142,-1142,-1142,-1142, + -1142,-1142,-1142,-1142,-1142,-1142,-1142,-1142,-1142,-1142, + -1142,-1142,-1142,-1142,-1142,-1142,-1142,-1142,-1142,-1142, + -1142,-1142,-1142,-1142,-1142,-1142,-1142,-1142,-1142,-1142, + -1142,-1142,-1142,-1142,-1142,-1142,-1142,-1142,-1142,-1142, + -1142,-1142,-1142,-1142,-1142,-1142,-1142,-1142,-1142,-1142, + -1142,-1142,-1142,-1142,-1142,-1142,-1142,-1142 + }, + + { + 69,-1143,-1143,-1143,-1143,-1143,-1143,-1143,-1143,-1143, + + -1143,-1143,-1143,-1143,-1143,-1143,-1143,-1143,-1143,-1143, + -1143,-1143,-1143,-1143,-1143,-1143,-1143,-1143,-1143,-1143, + -1143,-1143,-1143,-1143,-1143,-1143,-1143,-1143,-1143,-1143, + -1143,-1143,-1143,-1143,-1143,-1143,-1143,-1143,-1143,-1143, + -1143,-1143,-1143,-1143,-1143,-1143,-1143,-1143,-1143,-1143, + -1143,-1143,-1143,-1143,-1143,-1143,-1143,-1143,-1143,-1143, + -1143,-1143,-1143,-1143,-1143,-1143,-1143,-1143,-1143,-1143, + -1143,-1143,-1143,-1143,-1143,-1143,-1143,-1143,-1143,-1143, + -1143,-1143,-1143,-1143,-1143,-1143,-1143,-1143,-1143,-1143, + -1143,-1143,-1143,-1143,-1143,-1143,-1143,-1143,-1143,-1143, + + -1143,-1143,-1143,-1143,-1143,-1143,-1143,-1143,-1143,-1143, + -1143,-1143,-1143,-1143,-1143,-1143,-1143,-1143 + }, + + { + 69,-1144,-1144,-1144,-1144,-1144,-1144,-1144,-1144,-1144, + -1144,-1144,-1144,-1144,-1144,-1144,-1144,-1144,-1144,-1144, + -1144,-1144,-1144,-1144,-1144,-1144,-1144,-1144,-1144,-1144, + -1144,-1144,-1144,-1144,-1144,-1144,-1144,-1144,-1144,-1144, + -1144,-1144,-1144,-1144,-1144,-1144,-1144,-1144,-1144,-1144, + -1144,-1144,-1144,-1144,-1144,-1144,-1144,-1144,-1144,-1144, + -1144,-1144,-1144,-1144,-1144,-1144,-1144,-1144,-1144,-1144, + -1144,-1144,-1144,-1144,-1144,-1144,-1144,-1144,-1144,-1144, + + -1144,-1144,-1144,-1144,-1144,-1144,-1144,-1144,-1144,-1144, + -1144,-1144,-1144,-1144,-1144,-1144,-1144,-1144,-1144,-1144, + -1144,-1144,-1144,-1144,-1144,-1144,-1144,-1144,-1144,-1144, + -1144,-1144,-1144,-1144,-1144,-1144,-1144,-1144,-1144,-1144, + -1144,-1144,-1144,-1144,-1144,-1144,-1144,-1144 + }, + + { + 69,-1145,-1145,-1145,-1145,-1145,-1145,-1145,-1145,-1145, + -1145,-1145,-1145,-1145,-1145,-1145,-1145,-1145,-1145,-1145, + -1145,-1145,-1145,-1145,-1145,-1145,-1145,-1145,-1145,-1145, + -1145,-1145,-1145,-1145,-1145,-1145,-1145,-1145,-1145,-1145, + -1145,-1145,-1145,-1145,-1145,-1145,-1145,-1145,-1145,-1145, + + -1145,-1145,-1145,-1145,-1145,-1145,-1145,-1145,-1145,-1145, + -1145,-1145,-1145,-1145,-1145,-1145,-1145,-1145,-1145,-1145, + -1145,-1145,-1145,-1145,-1145,-1145,-1145,-1145,-1145,-1145, + -1145,-1145,-1145,-1145,-1145,-1145,-1145,-1145,-1145,-1145, + -1145,-1145,-1145,-1145,-1145,-1145,-1145,-1145,-1145,-1145, + -1145,-1145,-1145,-1145,-1145,-1145,-1145,-1145,-1145,-1145, + -1145,-1145,-1145,-1145,-1145,-1145,-1145,-1145,-1145,-1145, + -1145,-1145,-1145,-1145,-1145,-1145,-1145,-1145 + }, + + { + 69,-1146,-1146,-1146,-1146,-1146,-1146,-1146,-1146,-1146, + -1146,-1146,-1146,-1146,-1146,-1146,-1146,-1146,-1146,-1146, + + -1146,-1146,-1146,-1146,-1146,-1146,-1146,-1146,-1146,-1146, + -1146,-1146,-1146,-1146,-1146,-1146,-1146,-1146,-1146,-1146, + -1146,-1146,-1146,-1146,-1146,-1146,-1146,-1146,-1146,-1146, + -1146,-1146,-1146,-1146,-1146,-1146,-1146,-1146,-1146,-1146, + -1146,-1146,-1146,-1146,-1146,-1146,-1146,-1146,-1146,-1146, + -1146,-1146,-1146,-1146,-1146,-1146,-1146,-1146,-1146,-1146, + -1146,-1146,-1146, 1238,-1146,-1146,-1146,-1146,-1146,-1146, + -1146,-1146,-1146,-1146,-1146,-1146,-1146,-1146,-1146,-1146, + -1146,-1146,-1146,-1146,-1146,-1146,-1146,-1146,-1146,-1146, + -1146,-1146,-1146,-1146,-1146,-1146,-1146,-1146,-1146,-1146, + + -1146,-1146,-1146,-1146,-1146,-1146,-1146,-1146 + }, + + { + 69,-1147,-1147,-1147,-1147,-1147,-1147,-1147,-1147,-1147, + -1147,-1147,-1147,-1147,-1147,-1147,-1147,-1147,-1147,-1147, + -1147,-1147,-1147,-1147,-1147,-1147,-1147,-1147,-1147,-1147, + -1147,-1147,-1147,-1147,-1147,-1147,-1147,-1147,-1147,-1147, + -1147,-1147,-1147,-1147,-1147,-1147,-1147,-1147,-1147,-1147, + -1147,-1147,-1147,-1147,-1147,-1147,-1147,-1147,-1147,-1147, + -1147,-1147,-1147,-1147,-1147,-1147,-1147,-1147,-1147,-1147, + -1147,-1147,-1147,-1147,-1147,-1147,-1147,-1147,-1147,-1147, + -1147,-1147,-1147, 1239,-1147,-1147,-1147,-1147,-1147,-1147, + + -1147,-1147,-1147,-1147,-1147,-1147,-1147,-1147,-1147,-1147, + -1147,-1147,-1147,-1147,-1147,-1147,-1147,-1147,-1147,-1147, + -1147,-1147,-1147,-1147,-1147,-1147,-1147,-1147,-1147,-1147, + -1147,-1147,-1147,-1147,-1147,-1147,-1147,-1147 + }, + + { + 69,-1148,-1148,-1148,-1148,-1148,-1148,-1148,-1148,-1148, + -1148,-1148,-1148,-1148,-1148,-1148,-1148,-1148,-1148,-1148, + -1148,-1148,-1148,-1148,-1148,-1148,-1148,-1148,-1148,-1148, + -1148,-1148, 1240,-1148,-1148,-1148,-1148,-1148,-1148,-1148, + -1148,-1148,-1148,-1148,-1148,-1148,-1148,-1148,-1148,-1148, + -1148,-1148,-1148,-1148,-1148,-1148,-1148,-1148,-1148,-1148, + + -1148,-1148,-1148,-1148,-1148,-1148,-1148,-1148,-1148,-1148, + -1148,-1148,-1148,-1148,-1148,-1148,-1148,-1148,-1148,-1148, + -1148,-1148,-1148,-1148,-1148,-1148,-1148,-1148,-1148,-1148, + -1148,-1148,-1148,-1148,-1148,-1148,-1148,-1148,-1148,-1148, + -1148,-1148,-1148,-1148,-1148,-1148,-1148,-1148,-1148,-1148, + -1148,-1148,-1148,-1148,-1148,-1148,-1148,-1148,-1148,-1148, + -1148,-1148,-1148,-1148,-1148,-1148,-1148,-1148 + }, + + { + 69,-1149,-1149,-1149,-1149,-1149,-1149,-1149,-1149,-1149, + -1149,-1149,-1149,-1149,-1149,-1149,-1149,-1149,-1149,-1149, + -1149,-1149,-1149,-1149,-1149,-1149,-1149,-1149,-1149,-1149, + + -1149,-1149,-1149,-1149,-1149,-1149,-1149,-1149,-1149,-1149, + -1149,-1149,-1149,-1149,-1149,-1149,-1149,-1149,-1149,-1149, + -1149,-1149,-1149,-1149,-1149,-1149,-1149,-1149,-1149,-1149, + -1149,-1149,-1149,-1149,-1149,-1149,-1149,-1149,-1149,-1149, + -1149,-1149,-1149,-1149,-1149,-1149,-1149,-1149,-1149,-1149, + -1149,-1149,-1149, 1241,-1149,-1149,-1149,-1149,-1149,-1149, + -1149,-1149,-1149,-1149,-1149,-1149,-1149,-1149,-1149,-1149, + -1149,-1149,-1149,-1149,-1149,-1149,-1149,-1149,-1149,-1149, + -1149,-1149,-1149,-1149,-1149,-1149,-1149,-1149,-1149,-1149, + -1149,-1149,-1149,-1149,-1149,-1149,-1149,-1149 + + }, + + { + 69,-1150,-1150,-1150,-1150,-1150,-1150,-1150,-1150,-1150, + -1150,-1150,-1150,-1150,-1150,-1150,-1150,-1150,-1150,-1150, + -1150,-1150,-1150,-1150,-1150,-1150,-1150,-1150,-1150,-1150, + -1150,-1150,-1150,-1150,-1150,-1150,-1150,-1150,-1150,-1150, + -1150,-1150,-1150,-1150,-1150,-1150,-1150,-1150,-1150,-1150, + -1150,-1150,-1150,-1150,-1150,-1150,-1150,-1150,-1150,-1150, + -1150,-1150,-1150,-1150,-1150,-1150,-1150,-1150,-1150,-1150, + -1150,-1150,-1150,-1150,-1150,-1150,-1150,-1150,-1150,-1150, + -1150,-1150,-1150, 1242,-1150,-1150,-1150,-1150,-1150,-1150, + -1150,-1150,-1150,-1150,-1150,-1150,-1150,-1150,-1150,-1150, + + -1150,-1150,-1150,-1150,-1150,-1150,-1150,-1150,-1150,-1150, + -1150,-1150,-1150,-1150,-1150,-1150,-1150,-1150,-1150,-1150, + -1150,-1150,-1150,-1150,-1150,-1150,-1150,-1150 + }, + + { + 69,-1151,-1151,-1151,-1151,-1151,-1151,-1151,-1151,-1151, + -1151,-1151,-1151,-1151,-1151,-1151,-1151,-1151,-1151,-1151, + -1151,-1151,-1151,-1151,-1151,-1151,-1151,-1151,-1151,-1151, + -1151,-1151,-1151,-1151,-1151,-1151,-1151,-1151,-1151,-1151, + -1151,-1151,-1151,-1151,-1151,-1151,-1151,-1151,-1151,-1151, + -1151,-1151,-1151,-1151,-1151,-1151,-1151,-1151,-1151,-1151, + -1151,-1151,-1151,-1151,-1151,-1151,-1151,-1151,-1151,-1151, + + -1151,-1151,-1151,-1151,-1151,-1151,-1151,-1151,-1151,-1151, + -1151,-1151,-1151, 1243,-1151,-1151,-1151,-1151,-1151,-1151, + -1151,-1151,-1151,-1151,-1151,-1151,-1151,-1151,-1151,-1151, + -1151,-1151,-1151,-1151,-1151,-1151,-1151,-1151,-1151,-1151, + -1151,-1151,-1151,-1151,-1151,-1151,-1151,-1151,-1151,-1151, + -1151,-1151,-1151,-1151,-1151,-1151,-1151,-1151 + }, + + { + 69,-1152,-1152,-1152,-1152,-1152,-1152,-1152,-1152,-1152, + -1152,-1152,-1152,-1152,-1152,-1152,-1152,-1152,-1152,-1152, + -1152,-1152,-1152,-1152,-1152,-1152,-1152,-1152,-1152,-1152, + -1152,-1152,-1152,-1152,-1152,-1152,-1152,-1152,-1152,-1152, + + -1152,-1152,-1152,-1152,-1152,-1152,-1152,-1152,-1152,-1152, + -1152,-1152,-1152,-1152,-1152,-1152,-1152,-1152,-1152,-1152, + -1152,-1152,-1152,-1152,-1152,-1152,-1152,-1152,-1152,-1152, + -1152,-1152,-1152,-1152,-1152,-1152,-1152,-1152,-1152,-1152, + -1152,-1152,-1152, 1244,-1152,-1152,-1152,-1152,-1152,-1152, + -1152,-1152,-1152,-1152,-1152,-1152,-1152,-1152,-1152,-1152, + -1152,-1152,-1152,-1152,-1152,-1152,-1152,-1152,-1152,-1152, + -1152,-1152,-1152,-1152,-1152,-1152,-1152,-1152,-1152,-1152, + -1152,-1152,-1152,-1152,-1152,-1152,-1152,-1152 + }, + + { + 69,-1153,-1153,-1153,-1153,-1153,-1153,-1153,-1153,-1153, + + -1153,-1153,-1153,-1153,-1153,-1153,-1153,-1153,-1153,-1153, + -1153,-1153,-1153,-1153,-1153,-1153,-1153,-1153,-1153,-1153, + -1153,-1153,-1153,-1153,-1153,-1153,-1153,-1153,-1153,-1153, + -1153,-1153,-1153,-1153,-1153,-1153,-1153,-1153,-1153,-1153, + -1153,-1153,-1153,-1153,-1153,-1153,-1153,-1153,-1153,-1153, + -1153,-1153,-1153,-1153,-1153,-1153,-1153,-1153,-1153,-1153, + -1153,-1153,-1153,-1153,-1153,-1153,-1153,-1153,-1153,-1153, + -1153,-1153,-1153, 1245,-1153,-1153,-1153,-1153,-1153,-1153, + -1153,-1153,-1153,-1153,-1153,-1153,-1153,-1153,-1153,-1153, + -1153,-1153,-1153,-1153,-1153,-1153,-1153,-1153,-1153,-1153, + + -1153,-1153,-1153,-1153,-1153,-1153,-1153,-1153,-1153,-1153, + -1153,-1153,-1153,-1153,-1153,-1153,-1153,-1153 + }, + + { + 69,-1154,-1154,-1154,-1154,-1154,-1154,-1154,-1154,-1154, + -1154,-1154,-1154,-1154,-1154,-1154,-1154,-1154,-1154,-1154, + -1154,-1154,-1154,-1154,-1154,-1154,-1154,-1154,-1154,-1154, + -1154,-1154,-1154,-1154,-1154,-1154,-1154,-1154,-1154,-1154, + -1154,-1154,-1154,-1154,-1154,-1154,-1154,-1154,-1154,-1154, + -1154,-1154,-1154,-1154,-1154,-1154,-1154,-1154,-1154,-1154, + -1154,-1154,-1154,-1154,-1154,-1154,-1154,-1154,-1154,-1154, + -1154,-1154,-1154,-1154,-1154,-1154,-1154,-1154,-1154,-1154, + + -1154,-1154,-1154, 1246,-1154,-1154,-1154,-1154,-1154,-1154, + -1154,-1154,-1154,-1154,-1154,-1154,-1154,-1154,-1154,-1154, + -1154,-1154,-1154,-1154,-1154,-1154,-1154,-1154,-1154,-1154, + -1154,-1154,-1154,-1154,-1154,-1154,-1154,-1154,-1154,-1154, + -1154,-1154,-1154,-1154,-1154,-1154,-1154,-1154 + }, + + { + 69,-1155,-1155,-1155,-1155,-1155,-1155,-1155,-1155,-1155, + -1155,-1155,-1155,-1155,-1155,-1155,-1155,-1155,-1155,-1155, + -1155,-1155,-1155,-1155,-1155,-1155,-1155,-1155,-1155,-1155, + -1155,-1155,-1155,-1155,-1155,-1155,-1155,-1155,-1155,-1155, + -1155,-1155,-1155,-1155,-1155,-1155,-1155,-1155,-1155,-1155, + + -1155,-1155,-1155,-1155,-1155,-1155,-1155,-1155,-1155,-1155, + -1155,-1155,-1155,-1155,-1155,-1155,-1155,-1155,-1155,-1155, + -1155, 1247,-1155,-1155,-1155,-1155,-1155,-1155,-1155,-1155, + -1155,-1155,-1155,-1155,-1155,-1155,-1155,-1155,-1155,-1155, + -1155,-1155,-1155,-1155,-1155,-1155,-1155,-1155,-1155,-1155, + -1155,-1155,-1155,-1155,-1155,-1155,-1155,-1155,-1155,-1155, + -1155,-1155,-1155,-1155,-1155,-1155,-1155,-1155,-1155,-1155, + -1155,-1155,-1155,-1155,-1155,-1155,-1155,-1155 + }, + + { + 69,-1156,-1156,-1156,-1156,-1156,-1156,-1156,-1156,-1156, + -1156,-1156,-1156,-1156,-1156,-1156,-1156,-1156,-1156,-1156, + + -1156,-1156,-1156,-1156,-1156,-1156,-1156,-1156,-1156,-1156, + -1156,-1156,-1156,-1156,-1156,-1156,-1156,-1156,-1156,-1156, + -1156,-1156,-1156,-1156,-1156,-1156,-1156,-1156,-1156,-1156, + -1156,-1156,-1156,-1156,-1156,-1156,-1156,-1156,-1156,-1156, + -1156,-1156,-1156,-1156,-1156,-1156,-1156,-1156,-1156,-1156, + -1156, 1248,-1156,-1156,-1156,-1156,-1156,-1156,-1156,-1156, + -1156,-1156,-1156,-1156,-1156,-1156,-1156,-1156,-1156,-1156, + -1156,-1156,-1156,-1156,-1156,-1156,-1156,-1156,-1156,-1156, + -1156,-1156,-1156,-1156,-1156,-1156,-1156,-1156,-1156,-1156, + -1156,-1156,-1156,-1156,-1156,-1156,-1156,-1156,-1156,-1156, + + -1156,-1156,-1156,-1156,-1156,-1156,-1156,-1156 + }, + + { + 69,-1157,-1157,-1157,-1157,-1157,-1157,-1157,-1157,-1157, + -1157,-1157,-1157,-1157,-1157,-1157,-1157,-1157,-1157,-1157, + -1157,-1157,-1157,-1157,-1157,-1157,-1157,-1157,-1157,-1157, + -1157,-1157,-1157,-1157,-1157,-1157,-1157,-1157,-1157,-1157, + -1157,-1157,-1157,-1157,-1157,-1157,-1157,-1157,-1157,-1157, + -1157,-1157,-1157,-1157,-1157,-1157,-1157,-1157,-1157,-1157, + -1157,-1157,-1157,-1157,-1157,-1157,-1157,-1157, 1249,-1157, + -1157,-1157,-1157,-1157,-1157,-1157,-1157,-1157,-1157,-1157, + -1157,-1157,-1157,-1157,-1157,-1157,-1157,-1157,-1157,-1157, + + -1157,-1157,-1157,-1157,-1157,-1157,-1157,-1157,-1157,-1157, + -1157,-1157,-1157,-1157,-1157,-1157,-1157,-1157,-1157,-1157, + -1157,-1157,-1157,-1157,-1157,-1157,-1157,-1157,-1157,-1157, + -1157,-1157,-1157,-1157,-1157,-1157,-1157,-1157 + }, + + { + 69,-1158,-1158,-1158,-1158,-1158,-1158,-1158,-1158,-1158, + -1158,-1158,-1158,-1158,-1158,-1158,-1158,-1158,-1158,-1158, + -1158,-1158,-1158,-1158,-1158,-1158,-1158,-1158,-1158,-1158, + -1158,-1158,-1158,-1158,-1158,-1158,-1158,-1158,-1158,-1158, + -1158,-1158,-1158,-1158,-1158,-1158,-1158,-1158,-1158,-1158, + -1158,-1158,-1158,-1158,-1158,-1158,-1158,-1158,-1158,-1158, + + -1158,-1158,-1158,-1158,-1158,-1158,-1158,-1158,-1158,-1158, + -1158,-1158,-1158,-1158,-1158,-1158,-1158,-1158,-1158,-1158, + -1158,-1158,-1158, 1250,-1158,-1158,-1158,-1158,-1158,-1158, + -1158,-1158,-1158,-1158,-1158,-1158,-1158,-1158,-1158,-1158, + -1158,-1158,-1158,-1158,-1158,-1158,-1158,-1158,-1158,-1158, + -1158,-1158,-1158,-1158,-1158,-1158,-1158,-1158,-1158,-1158, + -1158,-1158,-1158,-1158,-1158,-1158,-1158,-1158 + }, + + { + 69,-1159,-1159,-1159,-1159,-1159,-1159,-1159,-1159,-1159, + -1159,-1159,-1159,-1159,-1159,-1159,-1159,-1159,-1159,-1159, + -1159,-1159,-1159,-1159,-1159,-1159,-1159,-1159,-1159,-1159, + + -1159,-1159,-1159,-1159,-1159,-1159,-1159,-1159,-1159,-1159, + -1159,-1159,-1159,-1159,-1159,-1159,-1159,-1159,-1159,-1159, + -1159,-1159,-1159,-1159,-1159,-1159,-1159,-1159,-1159,-1159, + -1159,-1159,-1159,-1159,-1159,-1159,-1159,-1159,-1159,-1159, + 1251,-1159,-1159,-1159,-1159,-1159,-1159,-1159,-1159,-1159, + -1159,-1159,-1159,-1159,-1159,-1159,-1159,-1159,-1159,-1159, + -1159,-1159,-1159,-1159,-1159,-1159,-1159,-1159,-1159,-1159, + -1159,-1159,-1159,-1159,-1159,-1159,-1159,-1159,-1159,-1159, + -1159,-1159,-1159,-1159,-1159,-1159,-1159,-1159,-1159,-1159, + -1159,-1159,-1159,-1159,-1159,-1159,-1159,-1159 + + }, + + { + 69,-1160,-1160,-1160,-1160,-1160,-1160,-1160,-1160,-1160, + -1160,-1160,-1160,-1160,-1160,-1160,-1160,-1160,-1160,-1160, + -1160,-1160,-1160,-1160,-1160,-1160,-1160,-1160,-1160,-1160, + -1160,-1160, 1252,-1160,-1160,-1160,-1160,-1160,-1160,-1160, + -1160,-1160,-1160,-1160,-1160,-1160,-1160,-1160,-1160,-1160, + -1160,-1160,-1160,-1160,-1160,-1160,-1160,-1160,-1160,-1160, + -1160,-1160,-1160,-1160,-1160,-1160,-1160,-1160,-1160,-1160, + -1160,-1160,-1160,-1160,-1160,-1160,-1160,-1160,-1160,-1160, + -1160,-1160,-1160,-1160,-1160,-1160,-1160,-1160,-1160,-1160, + -1160,-1160,-1160,-1160,-1160,-1160,-1160,-1160,-1160,-1160, + + -1160,-1160,-1160,-1160,-1160,-1160,-1160,-1160,-1160,-1160, + -1160,-1160,-1160,-1160,-1160,-1160,-1160,-1160,-1160,-1160, + -1160,-1160,-1160,-1160,-1160,-1160,-1160,-1160 + }, + + { + 69,-1161,-1161,-1161,-1161,-1161,-1161,-1161,-1161,-1161, + -1161,-1161,-1161,-1161,-1161,-1161,-1161,-1161,-1161,-1161, + -1161,-1161,-1161,-1161,-1161,-1161,-1161,-1161,-1161,-1161, + -1161,-1161, 1253,-1161,-1161,-1161,-1161,-1161,-1161,-1161, + -1161,-1161,-1161,-1161,-1161,-1161,-1161,-1161,-1161,-1161, + -1161,-1161,-1161,-1161,-1161,-1161,-1161,-1161,-1161,-1161, + -1161,-1161,-1161,-1161,-1161,-1161,-1161,-1161,-1161,-1161, + + -1161,-1161,-1161,-1161,-1161,-1161,-1161,-1161,-1161,-1161, + -1161,-1161,-1161,-1161,-1161,-1161,-1161,-1161,-1161,-1161, + -1161,-1161,-1161,-1161,-1161,-1161,-1161,-1161,-1161,-1161, + -1161,-1161,-1161,-1161,-1161,-1161,-1161,-1161,-1161,-1161, + -1161,-1161,-1161,-1161,-1161,-1161,-1161,-1161,-1161,-1161, + -1161,-1161,-1161,-1161,-1161,-1161,-1161,-1161 + }, + + { + 69,-1162,-1162,-1162,-1162,-1162,-1162,-1162,-1162,-1162, + -1162,-1162,-1162,-1162,-1162,-1162,-1162,-1162,-1162,-1162, + -1162,-1162,-1162,-1162,-1162,-1162,-1162,-1162,-1162,-1162, + -1162,-1162, 1254,-1162,-1162,-1162,-1162,-1162,-1162,-1162, + + -1162,-1162,-1162,-1162,-1162,-1162,-1162,-1162,-1162,-1162, + -1162,-1162,-1162,-1162,-1162,-1162,-1162,-1162,-1162,-1162, + -1162,-1162,-1162,-1162,-1162,-1162,-1162,-1162,-1162,-1162, + -1162,-1162,-1162,-1162,-1162,-1162,-1162,-1162,-1162,-1162, + -1162,-1162,-1162,-1162,-1162,-1162,-1162,-1162,-1162,-1162, + -1162,-1162,-1162,-1162,-1162,-1162,-1162,-1162,-1162,-1162, + -1162,-1162,-1162,-1162,-1162,-1162,-1162,-1162,-1162,-1162, + -1162,-1162,-1162,-1162,-1162,-1162,-1162,-1162,-1162,-1162, + -1162,-1162,-1162,-1162,-1162,-1162,-1162,-1162 + }, + + { + 69,-1163,-1163,-1163,-1163,-1163,-1163,-1163,-1163,-1163, + + -1163,-1163,-1163,-1163,-1163,-1163,-1163,-1163,-1163,-1163, + -1163,-1163,-1163,-1163,-1163,-1163,-1163,-1163,-1163,-1163, + -1163,-1163, 1255,-1163,-1163,-1163,-1163,-1163,-1163,-1163, + -1163,-1163,-1163,-1163,-1163,-1163,-1163,-1163,-1163,-1163, + -1163,-1163,-1163,-1163,-1163,-1163,-1163,-1163,-1163,-1163, + -1163,-1163,-1163,-1163,-1163,-1163,-1163,-1163,-1163,-1163, + -1163,-1163,-1163,-1163,-1163,-1163,-1163,-1163,-1163,-1163, + -1163,-1163,-1163,-1163,-1163,-1163,-1163,-1163,-1163,-1163, + -1163,-1163,-1163,-1163,-1163,-1163,-1163,-1163,-1163,-1163, + -1163,-1163,-1163,-1163,-1163,-1163,-1163,-1163,-1163,-1163, + + -1163,-1163,-1163,-1163,-1163,-1163,-1163,-1163,-1163,-1163, + -1163,-1163,-1163,-1163,-1163,-1163,-1163,-1163 + }, + + { + 69,-1164,-1164,-1164,-1164,-1164,-1164,-1164,-1164,-1164, + -1164,-1164,-1164,-1164,-1164,-1164,-1164,-1164,-1164,-1164, + -1164,-1164,-1164,-1164,-1164,-1164,-1164,-1164,-1164,-1164, + -1164,-1164,-1164,-1164,-1164,-1164,-1164,-1164,-1164,-1164, + -1164,-1164,-1164,-1164,-1164,-1164,-1164,-1164,-1164,-1164, + -1164,-1164,-1164,-1164,-1164,-1164,-1164,-1164,-1164,-1164, + -1164,-1164,-1164,-1164,-1164,-1164,-1164,-1164,-1164,-1164, + -1164,-1164,-1164,-1164,-1164,-1164,-1164,-1164,-1164,-1164, + + -1164,-1164,-1164, 1256,-1164,-1164,-1164,-1164,-1164,-1164, + -1164,-1164,-1164,-1164,-1164,-1164,-1164,-1164,-1164,-1164, + -1164,-1164,-1164,-1164,-1164,-1164,-1164,-1164,-1164,-1164, + -1164,-1164,-1164,-1164,-1164,-1164,-1164,-1164,-1164,-1164, + -1164,-1164,-1164,-1164,-1164,-1164,-1164,-1164 + }, + + { + 69,-1165,-1165,-1165,-1165,-1165,-1165,-1165,-1165,-1165, + -1165,-1165,-1165,-1165,-1165,-1165,-1165,-1165,-1165,-1165, + -1165,-1165,-1165,-1165,-1165,-1165,-1165,-1165,-1165,-1165, + -1165,-1165, 1257,-1165,-1165,-1165,-1165,-1165,-1165,-1165, + -1165,-1165,-1165,-1165,-1165,-1165,-1165,-1165,-1165,-1165, + + -1165,-1165,-1165,-1165,-1165,-1165,-1165,-1165,-1165,-1165, + -1165,-1165,-1165,-1165,-1165,-1165,-1165,-1165,-1165,-1165, + -1165,-1165,-1165,-1165,-1165,-1165,-1165,-1165,-1165,-1165, + -1165,-1165,-1165,-1165,-1165,-1165,-1165,-1165,-1165,-1165, + -1165,-1165,-1165,-1165,-1165,-1165,-1165,-1165,-1165,-1165, + -1165,-1165,-1165,-1165,-1165,-1165,-1165,-1165,-1165,-1165, + -1165,-1165,-1165,-1165,-1165,-1165,-1165,-1165,-1165,-1165, + -1165,-1165,-1165,-1165,-1165,-1165,-1165,-1165 + }, + + { + 69,-1166,-1166,-1166,-1166,-1166,-1166,-1166,-1166,-1166, + -1166,-1166,-1166,-1166,-1166,-1166,-1166,-1166,-1166,-1166, + + -1166,-1166,-1166,-1166,-1166,-1166,-1166,-1166,-1166,-1166, + -1166,-1166, 1258,-1166,-1166,-1166,-1166,-1166,-1166,-1166, + -1166,-1166,-1166,-1166,-1166,-1166,-1166,-1166,-1166,-1166, + -1166,-1166,-1166,-1166,-1166,-1166,-1166,-1166,-1166,-1166, + -1166,-1166,-1166,-1166,-1166,-1166,-1166,-1166,-1166,-1166, + -1166,-1166,-1166,-1166,-1166,-1166,-1166,-1166,-1166,-1166, + -1166,-1166,-1166,-1166,-1166,-1166,-1166,-1166,-1166,-1166, + -1166,-1166,-1166,-1166,-1166,-1166,-1166,-1166,-1166,-1166, + -1166,-1166,-1166,-1166,-1166,-1166,-1166,-1166,-1166,-1166, + -1166,-1166,-1166,-1166,-1166,-1166,-1166,-1166,-1166,-1166, + + -1166,-1166,-1166,-1166,-1166,-1166,-1166,-1166 + }, + + { + 69,-1167,-1167,-1167,-1167,-1167,-1167,-1167,-1167,-1167, + -1167,-1167,-1167,-1167,-1167,-1167,-1167,-1167,-1167,-1167, + -1167,-1167,-1167,-1167,-1167,-1167,-1167,-1167,-1167,-1167, + -1167,-1167,-1167,-1167,-1167,-1167,-1167,-1167,-1167,-1167, + -1167,-1167,-1167,-1167,-1167,-1167,-1167,-1167,-1167,-1167, + -1167,-1167,-1167,-1167,-1167,-1167,-1167,-1167,-1167,-1167, + -1167,-1167,-1167,-1167,-1167,-1167,-1167,-1167,-1167,-1167, + -1167,-1167,-1167,-1167,-1167,-1167,-1167,-1167,-1167,-1167, + -1167,-1167,-1167,-1167,-1167,-1167,-1167,-1167,-1167,-1167, + + -1167,-1167,-1167,-1167,-1167,-1167,-1167,-1167,-1167,-1167, + -1167,-1167,-1167,-1167,-1167,-1167,-1167,-1167,-1167,-1167, + -1167,-1167,-1167,-1167,-1167,-1167,-1167,-1167,-1167,-1167, + -1167,-1167,-1167,-1167,-1167,-1167,-1167,-1167 + }, + + { + 69,-1168,-1168,-1168,-1168,-1168,-1168,-1168,-1168,-1168, + -1168,-1168,-1168,-1168,-1168,-1168,-1168,-1168,-1168,-1168, + -1168,-1168,-1168,-1168,-1168,-1168,-1168,-1168,-1168,-1168, + -1168,-1168,-1168,-1168,-1168,-1168,-1168,-1168,-1168,-1168, + -1168,-1168,-1168,-1168,-1168,-1168,-1168,-1168,-1168,-1168, + -1168,-1168,-1168,-1168,-1168,-1168,-1168,-1168,-1168,-1168, + + -1168,-1168,-1168,-1168,-1168,-1168,-1168,-1168,-1168,-1168, + -1168,-1168,-1168,-1168,-1168,-1168,-1168,-1168,-1168,-1168, + -1168,-1168,-1168, 1259,-1168,-1168,-1168,-1168,-1168,-1168, + -1168,-1168,-1168,-1168,-1168,-1168,-1168,-1168,-1168,-1168, + -1168,-1168,-1168,-1168,-1168,-1168,-1168,-1168,-1168,-1168, + -1168,-1168,-1168,-1168,-1168,-1168,-1168,-1168,-1168,-1168, + -1168,-1168,-1168,-1168,-1168,-1168,-1168,-1168 + }, + + { + 69,-1169,-1169,-1169,-1169,-1169,-1169,-1169,-1169,-1169, + -1169,-1169,-1169,-1169,-1169,-1169,-1169,-1169,-1169,-1169, + -1169,-1169,-1169,-1169,-1169,-1169,-1169,-1169,-1169,-1169, + + -1169,-1169,-1169,-1169,-1169,-1169,-1169,-1169,-1169,-1169, + -1169,-1169,-1169,-1169,-1169,-1169,-1169,-1169,-1169,-1169, + -1169,-1169,-1169,-1169,-1169,-1169,-1169,-1169,-1169,-1169, + -1169,-1169,-1169,-1169,-1169,-1169,-1169,-1169,-1169,-1169, + -1169,-1169,-1169,-1169,-1169,-1169,-1169,-1169,-1169,-1169, + -1169,-1169,-1169, 1260,-1169,-1169,-1169,-1169,-1169,-1169, + -1169,-1169,-1169,-1169,-1169,-1169,-1169,-1169,-1169,-1169, + -1169,-1169,-1169,-1169,-1169,-1169,-1169,-1169,-1169,-1169, + -1169,-1169,-1169,-1169,-1169,-1169,-1169,-1169,-1169,-1169, + -1169,-1169,-1169,-1169,-1169,-1169,-1169,-1169 + + }, + + { + 69,-1170,-1170,-1170,-1170,-1170,-1170,-1170,-1170,-1170, + -1170,-1170,-1170,-1170,-1170,-1170,-1170,-1170,-1170,-1170, + -1170,-1170,-1170,-1170,-1170,-1170,-1170,-1170,-1170,-1170, + -1170,-1170, 1261,-1170,-1170,-1170,-1170,-1170,-1170,-1170, + -1170,-1170,-1170,-1170,-1170,-1170,-1170,-1170,-1170,-1170, + -1170,-1170,-1170,-1170,-1170,-1170,-1170,-1170,-1170,-1170, + -1170,-1170,-1170,-1170,-1170,-1170,-1170,-1170,-1170,-1170, + -1170,-1170,-1170,-1170,-1170,-1170,-1170,-1170,-1170,-1170, + -1170,-1170,-1170,-1170,-1170,-1170,-1170,-1170,-1170,-1170, + -1170,-1170,-1170,-1170,-1170,-1170,-1170,-1170,-1170,-1170, + + -1170,-1170,-1170,-1170,-1170,-1170,-1170,-1170,-1170,-1170, + -1170,-1170,-1170,-1170,-1170,-1170,-1170,-1170,-1170,-1170, + -1170,-1170,-1170,-1170,-1170,-1170,-1170,-1170 + }, + + { + 69,-1171,-1171,-1171,-1171,-1171,-1171,-1171,-1171,-1171, + -1171,-1171,-1171,-1171,-1171,-1171,-1171,-1171,-1171,-1171, + -1171,-1171,-1171,-1171,-1171,-1171,-1171,-1171,-1171,-1171, + -1171,-1171,-1171,-1171,-1171,-1171,-1171,-1171,-1171,-1171, + -1171,-1171,-1171,-1171,-1171,-1171,-1171,-1171,-1171,-1171, + -1171,-1171,-1171,-1171,-1171,-1171,-1171,-1171,-1171,-1171, + -1171,-1171,-1171,-1171,-1171,-1171,-1171,-1171,-1171,-1171, + + -1171,-1171,-1171,-1171,-1171,-1171,-1171,-1171,-1171,-1171, + -1171,-1171,-1171,-1171,-1171,-1171,-1171,-1171,-1171,-1171, + -1171,-1171,-1171,-1171,-1171,-1171,-1171,-1171,-1171,-1171, + -1171,-1171,-1171,-1171,-1171,-1171,-1171,-1171,-1171,-1171, + -1171,-1171,-1171,-1171,-1171,-1171,-1171,-1171,-1171,-1171, + -1171,-1171,-1171,-1171,-1171,-1171,-1171,-1171 + }, + + { + 69,-1172,-1172,-1172,-1172,-1172,-1172,-1172,-1172,-1172, + -1172,-1172,-1172,-1172,-1172,-1172,-1172,-1172,-1172,-1172, + -1172,-1172,-1172,-1172,-1172,-1172,-1172,-1172,-1172,-1172, + -1172,-1172,-1172,-1172,-1172,-1172,-1172,-1172,-1172,-1172, + + -1172,-1172,-1172,-1172,-1172,-1172,-1172,-1172,-1172,-1172, + -1172,-1172,-1172,-1172,-1172,-1172,-1172,-1172,-1172,-1172, + -1172,-1172,-1172,-1172,-1172,-1172,-1172,-1172,-1172,-1172, + -1172,-1172,-1172,-1172,-1172,-1172,-1172,-1172,-1172,-1172, + -1172,-1172,-1172,-1172,-1172,-1172,-1172,-1172,-1172,-1172, + -1172,-1172,-1172,-1172,-1172,-1172,-1172,-1172,-1172,-1172, + -1172,-1172,-1172,-1172,-1172,-1172,-1172,-1172,-1172,-1172, + -1172,-1172,-1172,-1172,-1172,-1172,-1172,-1172,-1172,-1172, + -1172,-1172,-1172,-1172,-1172,-1172,-1172,-1172 + }, + + { + 69,-1173,-1173,-1173,-1173,-1173,-1173,-1173,-1173,-1173, + + -1173,-1173,-1173,-1173,-1173,-1173,-1173,-1173,-1173,-1173, + -1173,-1173,-1173,-1173,-1173,-1173,-1173,-1173,-1173,-1173, + -1173,-1173, 1262,-1173,-1173,-1173,-1173,-1173,-1173,-1173, + -1173,-1173,-1173,-1173,-1173,-1173,-1173,-1173,-1173,-1173, + -1173,-1173,-1173,-1173,-1173,-1173,-1173,-1173,-1173,-1173, + -1173,-1173,-1173,-1173,-1173,-1173,-1173,-1173,-1173,-1173, + -1173,-1173,-1173,-1173,-1173,-1173,-1173,-1173,-1173,-1173, + -1173,-1173,-1173,-1173,-1173,-1173,-1173,-1173,-1173,-1173, + -1173,-1173,-1173,-1173,-1173,-1173,-1173,-1173,-1173,-1173, + -1173,-1173,-1173,-1173,-1173,-1173,-1173,-1173,-1173,-1173, + + -1173,-1173,-1173,-1173,-1173,-1173,-1173,-1173,-1173,-1173, + -1173,-1173,-1173,-1173,-1173,-1173,-1173,-1173 + }, + + { + 69,-1174,-1174,-1174,-1174,-1174,-1174,-1174,-1174,-1174, + -1174,-1174,-1174,-1174,-1174,-1174,-1174,-1174,-1174,-1174, + -1174,-1174,-1174,-1174,-1174,-1174,-1174,-1174,-1174,-1174, + -1174,-1174, 1263,-1174,-1174,-1174,-1174,-1174,-1174,-1174, + -1174,-1174,-1174,-1174,-1174,-1174,-1174,-1174,-1174,-1174, + -1174,-1174,-1174,-1174,-1174,-1174,-1174,-1174,-1174,-1174, + -1174,-1174,-1174,-1174,-1174,-1174,-1174,-1174,-1174,-1174, + -1174,-1174,-1174,-1174,-1174,-1174,-1174,-1174,-1174,-1174, + + -1174,-1174,-1174,-1174,-1174,-1174,-1174,-1174,-1174,-1174, + -1174,-1174,-1174,-1174,-1174,-1174,-1174,-1174,-1174,-1174, + -1174,-1174,-1174,-1174,-1174,-1174,-1174,-1174,-1174,-1174, + -1174,-1174,-1174,-1174,-1174,-1174,-1174,-1174,-1174,-1174, + -1174,-1174,-1174,-1174,-1174,-1174,-1174,-1174 + }, + + { + 69,-1175,-1175,-1175,-1175,-1175,-1175,-1175,-1175,-1175, + -1175,-1175,-1175,-1175,-1175,-1175,-1175,-1175,-1175,-1175, + -1175,-1175,-1175,-1175,-1175,-1175,-1175,-1175,-1175,-1175, + -1175,-1175, 1264,-1175,-1175,-1175,-1175,-1175,-1175,-1175, + -1175,-1175,-1175,-1175,-1175,-1175,-1175,-1175,-1175,-1175, + + -1175,-1175,-1175,-1175,-1175,-1175,-1175,-1175,-1175,-1175, + -1175,-1175,-1175,-1175,-1175,-1175,-1175,-1175,-1175,-1175, + -1175,-1175,-1175,-1175,-1175,-1175,-1175,-1175,-1175,-1175, + -1175,-1175,-1175,-1175,-1175,-1175,-1175,-1175,-1175,-1175, + -1175,-1175,-1175,-1175,-1175,-1175,-1175,-1175,-1175,-1175, + -1175,-1175,-1175,-1175,-1175,-1175,-1175,-1175,-1175,-1175, + -1175,-1175,-1175,-1175,-1175,-1175,-1175,-1175,-1175,-1175, + -1175,-1175,-1175,-1175,-1175,-1175,-1175,-1175 + }, + + { + 69,-1176,-1176,-1176,-1176,-1176,-1176,-1176,-1176,-1176, + -1176,-1176,-1176,-1176,-1176,-1176,-1176,-1176,-1176,-1176, + + -1176,-1176,-1176,-1176,-1176,-1176,-1176,-1176,-1176,-1176, + -1176,-1176, 1265,-1176,-1176,-1176,-1176,-1176,-1176,-1176, + -1176,-1176,-1176,-1176,-1176,-1176,-1176,-1176,-1176,-1176, + -1176,-1176,-1176,-1176,-1176,-1176,-1176,-1176,-1176,-1176, + -1176,-1176,-1176,-1176,-1176,-1176,-1176,-1176,-1176,-1176, + -1176,-1176,-1176,-1176,-1176,-1176,-1176,-1176,-1176,-1176, + -1176,-1176,-1176,-1176,-1176,-1176,-1176,-1176,-1176,-1176, + -1176,-1176,-1176,-1176,-1176,-1176,-1176,-1176,-1176,-1176, + -1176,-1176,-1176,-1176,-1176,-1176,-1176,-1176,-1176,-1176, + -1176,-1176,-1176,-1176,-1176,-1176,-1176,-1176,-1176,-1176, + + -1176,-1176,-1176,-1176,-1176,-1176,-1176,-1176 + }, + + { + 69,-1177,-1177,-1177,-1177,-1177,-1177,-1177,-1177,-1177, + -1177,-1177,-1177,-1177,-1177,-1177,-1177,-1177,-1177,-1177, + -1177,-1177,-1177,-1177,-1177,-1177,-1177,-1177,-1177,-1177, + -1177,-1177,-1177,-1177,-1177,-1177,-1177,-1177,-1177,-1177, + -1177,-1177,-1177,-1177,-1177,-1177,-1177,-1177,-1177,-1177, + -1177,-1177,-1177,-1177,-1177,-1177,-1177,-1177,-1177,-1177, + -1177,-1177,-1177,-1177,-1177,-1177,-1177,-1177,-1177,-1177, + -1177,-1177,-1177,-1177,-1177,-1177,-1177,-1177,-1177,-1177, + -1177,-1177,-1177,-1177,-1177,-1177,-1177,-1177,-1177,-1177, + + -1177,-1177,-1177,-1177,-1177,-1177,-1177,-1177,-1177,-1177, + -1177,-1177,-1177,-1177,-1177,-1177,-1177,-1177,-1177,-1177, + -1177,-1177,-1177,-1177,-1177,-1177,-1177,-1177,-1177,-1177, + -1177,-1177,-1177,-1177,-1177,-1177,-1177,-1177 + }, + + { + 69,-1178,-1178,-1178,-1178,-1178,-1178,-1178,-1178,-1178, + -1178,-1178,-1178,-1178,-1178,-1178,-1178,-1178,-1178,-1178, + -1178,-1178,-1178,-1178,-1178,-1178,-1178,-1178,-1178,-1178, + -1178,-1178,-1178,-1178,-1178,-1178,-1178,-1178,-1178,-1178, + -1178,-1178,-1178,-1178,-1178,-1178,-1178,-1178,-1178,-1178, + -1178,-1178,-1178,-1178,-1178,-1178,-1178,-1178,-1178,-1178, + + -1178,-1178,-1178,-1178,-1178,-1178,-1178,-1178,-1178,-1178, + -1178,-1178,-1178,-1178,-1178,-1178,-1178,-1178,-1178,-1178, + -1178,-1178,-1178,-1178,-1178,-1178,-1178,-1178,-1178,-1178, + -1178,-1178,-1178,-1178,-1178,-1178,-1178,-1178,-1178,-1178, + -1178,-1178,-1178,-1178,-1178,-1178,-1178,-1178,-1178,-1178, + -1178,-1178,-1178,-1178,-1178,-1178,-1178,-1178,-1178,-1178, + -1178,-1178,-1178,-1178,-1178,-1178,-1178,-1178 + }, + + { + 69,-1179,-1179,-1179,-1179,-1179,-1179,-1179,-1179,-1179, + -1179,-1179,-1179,-1179,-1179,-1179,-1179,-1179,-1179,-1179, + -1179,-1179,-1179,-1179,-1179,-1179,-1179,-1179,-1179,-1179, + + -1179,-1179, 1266,-1179,-1179,-1179,-1179,-1179,-1179,-1179, + -1179,-1179,-1179,-1179,-1179,-1179,-1179,-1179,-1179,-1179, + -1179,-1179,-1179,-1179,-1179,-1179,-1179,-1179,-1179,-1179, + -1179,-1179,-1179,-1179,-1179,-1179,-1179,-1179,-1179,-1179, + -1179,-1179,-1179,-1179,-1179,-1179,-1179,-1179,-1179,-1179, + -1179,-1179,-1179,-1179,-1179,-1179,-1179,-1179,-1179,-1179, + -1179,-1179,-1179,-1179,-1179,-1179,-1179,-1179,-1179,-1179, + -1179,-1179,-1179,-1179,-1179,-1179,-1179,-1179,-1179,-1179, + -1179,-1179,-1179,-1179,-1179,-1179,-1179,-1179,-1179,-1179, + -1179,-1179,-1179,-1179,-1179,-1179,-1179,-1179 + + }, + + { + 69,-1180,-1180,-1180,-1180,-1180,-1180,-1180,-1180,-1180, + -1180,-1180,-1180,-1180,-1180,-1180,-1180,-1180,-1180,-1180, + -1180,-1180,-1180,-1180,-1180,-1180,-1180,-1180,-1180,-1180, + -1180,-1180, 1267,-1180,-1180,-1180,-1180,-1180,-1180,-1180, + -1180,-1180,-1180,-1180,-1180,-1180,-1180,-1180,-1180,-1180, + -1180,-1180,-1180,-1180,-1180,-1180,-1180,-1180,-1180,-1180, + -1180,-1180,-1180,-1180,-1180,-1180,-1180,-1180,-1180,-1180, + -1180,-1180,-1180,-1180,-1180,-1180,-1180,-1180,-1180,-1180, + -1180,-1180,-1180,-1180,-1180,-1180,-1180,-1180,-1180,-1180, + -1180,-1180,-1180,-1180,-1180,-1180,-1180,-1180,-1180,-1180, + + -1180,-1180,-1180,-1180,-1180,-1180,-1180,-1180,-1180,-1180, + -1180,-1180,-1180,-1180,-1180,-1180,-1180,-1180,-1180,-1180, + -1180,-1180,-1180,-1180,-1180,-1180,-1180,-1180 + }, + + { + 69,-1181,-1181,-1181,-1181,-1181,-1181,-1181,-1181,-1181, + -1181,-1181,-1181,-1181,-1181,-1181,-1181,-1181,-1181,-1181, + -1181,-1181,-1181,-1181,-1181,-1181,-1181,-1181,-1181,-1181, + -1181,-1181, 1268,-1181,-1181,-1181,-1181,-1181,-1181,-1181, + -1181,-1181,-1181,-1181,-1181,-1181,-1181,-1181,-1181,-1181, + -1181,-1181,-1181,-1181,-1181,-1181,-1181,-1181,-1181,-1181, + -1181,-1181,-1181,-1181,-1181,-1181,-1181,-1181,-1181,-1181, + + -1181,-1181,-1181,-1181,-1181,-1181,-1181,-1181,-1181,-1181, + -1181,-1181,-1181,-1181,-1181,-1181,-1181,-1181,-1181,-1181, + -1181,-1181,-1181,-1181,-1181,-1181,-1181,-1181,-1181,-1181, + -1181,-1181,-1181,-1181,-1181,-1181,-1181,-1181,-1181,-1181, + -1181,-1181,-1181,-1181,-1181,-1181,-1181,-1181,-1181,-1181, + -1181,-1181,-1181,-1181,-1181,-1181,-1181,-1181 + }, + + { + 69,-1182,-1182,-1182,-1182,-1182,-1182,-1182,-1182,-1182, + -1182,-1182,-1182,-1182,-1182,-1182,-1182,-1182,-1182,-1182, + -1182,-1182,-1182,-1182,-1182,-1182,-1182,-1182,-1182,-1182, + -1182,-1182, 1269,-1182,-1182,-1182,-1182,-1182,-1182,-1182, + + -1182,-1182,-1182,-1182,-1182,-1182,-1182,-1182,-1182,-1182, + -1182,-1182,-1182,-1182,-1182,-1182,-1182,-1182,-1182,-1182, + -1182,-1182,-1182,-1182,-1182,-1182,-1182,-1182,-1182,-1182, + -1182,-1182,-1182,-1182,-1182,-1182,-1182,-1182,-1182,-1182, + -1182,-1182,-1182,-1182,-1182,-1182,-1182,-1182,-1182,-1182, + -1182,-1182,-1182,-1182,-1182,-1182,-1182,-1182,-1182,-1182, + -1182,-1182,-1182,-1182,-1182,-1182,-1182,-1182,-1182,-1182, + -1182,-1182,-1182,-1182,-1182,-1182,-1182,-1182,-1182,-1182, + -1182,-1182,-1182,-1182,-1182,-1182,-1182,-1182 + }, + + { + 69,-1183,-1183,-1183,-1183,-1183,-1183,-1183,-1183,-1183, + + -1183,-1183,-1183,-1183,-1183,-1183,-1183,-1183,-1183,-1183, + -1183,-1183,-1183,-1183,-1183,-1183,-1183,-1183,-1183,-1183, + -1183,-1183, 1270,-1183,-1183,-1183,-1183,-1183,-1183,-1183, + -1183,-1183,-1183,-1183,-1183,-1183,-1183,-1183,-1183,-1183, + -1183,-1183,-1183,-1183,-1183,-1183,-1183,-1183,-1183,-1183, + -1183,-1183,-1183,-1183,-1183,-1183,-1183,-1183,-1183,-1183, + 1271,-1183,-1183, 1272,-1183,-1183,-1183,-1183,-1183,-1183, + -1183,-1183,-1183,-1183,-1183,-1183,-1183,-1183,-1183,-1183, + -1183,-1183,-1183,-1183,-1183,-1183,-1183,-1183,-1183,-1183, + -1183,-1183,-1183,-1183,-1183,-1183,-1183,-1183,-1183,-1183, + + -1183,-1183,-1183,-1183,-1183,-1183,-1183,-1183,-1183,-1183, + -1183,-1183,-1183,-1183,-1183,-1183,-1183,-1183 + }, + + { + 69,-1184,-1184,-1184,-1184,-1184,-1184,-1184,-1184,-1184, + -1184,-1184,-1184,-1184,-1184,-1184,-1184,-1184,-1184,-1184, + -1184,-1184,-1184,-1184,-1184,-1184,-1184,-1184,-1184,-1184, + -1184,-1184, 1273,-1184,-1184,-1184,-1184,-1184,-1184,-1184, + -1184,-1184,-1184,-1184,-1184,-1184,-1184,-1184,-1184,-1184, + -1184,-1184,-1184,-1184,-1184,-1184,-1184,-1184,-1184,-1184, + -1184,-1184,-1184,-1184,-1184,-1184,-1184,-1184,-1184,-1184, + -1184,-1184,-1184,-1184,-1184,-1184,-1184,-1184,-1184,-1184, + + -1184,-1184,-1184,-1184,-1184,-1184,-1184,-1184,-1184,-1184, + -1184,-1184,-1184,-1184,-1184,-1184,-1184,-1184,-1184,-1184, + -1184,-1184,-1184,-1184,-1184,-1184,-1184,-1184,-1184,-1184, + -1184,-1184,-1184,-1184,-1184,-1184,-1184,-1184,-1184,-1184, + -1184,-1184,-1184,-1184,-1184,-1184,-1184,-1184 + }, + + { + 69,-1185,-1185,-1185,-1185,-1185,-1185,-1185,-1185,-1185, + -1185,-1185,-1185,-1185,-1185,-1185,-1185,-1185,-1185,-1185, + -1185,-1185,-1185,-1185,-1185,-1185,-1185,-1185,-1185,-1185, + -1185,-1185, 1274,-1185,-1185,-1185,-1185,-1185,-1185,-1185, + -1185,-1185,-1185,-1185,-1185,-1185,-1185,-1185,-1185,-1185, + + -1185,-1185,-1185,-1185,-1185,-1185,-1185,-1185,-1185,-1185, + -1185,-1185,-1185,-1185,-1185,-1185,-1185,-1185,-1185,-1185, + -1185,-1185,-1185,-1185,-1185,-1185,-1185,-1185,-1185,-1185, + -1185,-1185,-1185,-1185,-1185,-1185,-1185,-1185,-1185,-1185, + -1185,-1185,-1185,-1185,-1185,-1185,-1185,-1185,-1185,-1185, + -1185,-1185,-1185,-1185,-1185,-1185,-1185,-1185,-1185,-1185, + -1185,-1185,-1185,-1185,-1185,-1185,-1185,-1185,-1185,-1185, + -1185,-1185,-1185,-1185,-1185,-1185,-1185,-1185 + }, + + { + 69,-1186,-1186,-1186,-1186,-1186,-1186,-1186,-1186,-1186, + -1186,-1186,-1186,-1186,-1186,-1186,-1186,-1186,-1186,-1186, + + -1186,-1186,-1186,-1186,-1186,-1186,-1186,-1186,-1186,-1186, + -1186,-1186, 1275,-1186,-1186,-1186,-1186,-1186,-1186,-1186, + -1186,-1186,-1186,-1186,-1186,-1186,-1186,-1186,-1186,-1186, + -1186,-1186,-1186,-1186,-1186,-1186,-1186,-1186,-1186,-1186, + -1186,-1186,-1186,-1186,-1186,-1186,-1186,-1186,-1186,-1186, + -1186,-1186,-1186,-1186,-1186,-1186,-1186,-1186,-1186,-1186, + -1186,-1186,-1186,-1186,-1186,-1186,-1186,-1186,-1186,-1186, + -1186,-1186,-1186,-1186,-1186,-1186,-1186,-1186,-1186,-1186, + -1186,-1186,-1186,-1186,-1186,-1186,-1186,-1186,-1186,-1186, + -1186,-1186,-1186,-1186,-1186,-1186,-1186,-1186,-1186,-1186, + + -1186,-1186,-1186,-1186,-1186,-1186,-1186,-1186 + }, + + { + 69,-1187,-1187,-1187,-1187,-1187,-1187,-1187,-1187,-1187, + -1187,-1187,-1187,-1187,-1187,-1187,-1187,-1187,-1187,-1187, + -1187,-1187,-1187,-1187,-1187,-1187,-1187,-1187,-1187,-1187, + -1187,-1187, 1276,-1187,-1187,-1187,-1187,-1187,-1187,-1187, + -1187,-1187,-1187,-1187,-1187,-1187,-1187,-1187,-1187,-1187, + -1187,-1187,-1187,-1187,-1187,-1187,-1187,-1187,-1187,-1187, + -1187,-1187,-1187,-1187,-1187,-1187,-1187,-1187,-1187,-1187, + -1187,-1187,-1187,-1187,-1187,-1187,-1187,-1187,-1187,-1187, + -1187,-1187,-1187,-1187,-1187,-1187,-1187,-1187,-1187,-1187, + + -1187,-1187,-1187,-1187,-1187,-1187,-1187,-1187,-1187,-1187, + -1187,-1187,-1187,-1187,-1187,-1187,-1187,-1187,-1187,-1187, + -1187,-1187,-1187,-1187,-1187,-1187,-1187,-1187,-1187,-1187, + -1187,-1187,-1187,-1187,-1187,-1187,-1187,-1187 + }, + + { + 69,-1188,-1188,-1188,-1188,-1188,-1188,-1188,-1188,-1188, + -1188,-1188,-1188,-1188,-1188,-1188,-1188,-1188,-1188,-1188, + -1188,-1188,-1188,-1188,-1188,-1188,-1188,-1188,-1188,-1188, + -1188,-1188, 1277,-1188,-1188,-1188,-1188,-1188,-1188,-1188, + -1188,-1188,-1188,-1188,-1188,-1188,-1188,-1188, 1278, 1278, + 1278, 1278, 1278, 1278, 1278, 1278, 1278, 1278,-1188,-1188, + + -1188,-1188,-1188,-1188,-1188,-1188,-1188,-1188,-1188,-1188, + -1188,-1188,-1188,-1188,-1188,-1188,-1188,-1188,-1188,-1188, + -1188,-1188,-1188,-1188,-1188,-1188,-1188,-1188,-1188,-1188, + -1188,-1188,-1188,-1188,-1188,-1188,-1188,-1188,-1188,-1188, + -1188,-1188,-1188,-1188,-1188,-1188,-1188,-1188,-1188,-1188, + -1188,-1188,-1188,-1188,-1188,-1188,-1188,-1188,-1188,-1188, + -1188,-1188,-1188,-1188,-1188,-1188,-1188,-1188 + }, + + { + 69,-1189,-1189,-1189,-1189,-1189,-1189,-1189,-1189,-1189, + -1189,-1189,-1189,-1189,-1189,-1189,-1189,-1189,-1189,-1189, + -1189,-1189,-1189,-1189,-1189,-1189,-1189,-1189,-1189,-1189, + + -1189,-1189,-1189,-1189,-1189,-1189,-1189,-1189,-1189,-1189, + -1189,-1189,-1189,-1189,-1189,-1189,-1189,-1189,-1189,-1189, + -1189,-1189,-1189,-1189,-1189,-1189,-1189,-1189,-1189,-1189, + -1189,-1189,-1189,-1189,-1189,-1189, 1279,-1189,-1189,-1189, + -1189,-1189, 1280,-1189,-1189,-1189, 1281,-1189,-1189,-1189, + -1189,-1189,-1189,-1189,-1189,-1189,-1189,-1189, 1282, 1283, + 1284,-1189,-1189,-1189,-1189,-1189,-1189,-1189,-1189,-1189, + -1189,-1189,-1189,-1189,-1189,-1189,-1189,-1189,-1189,-1189, + -1189,-1189,-1189,-1189,-1189,-1189,-1189,-1189,-1189,-1189, + -1189,-1189,-1189,-1189,-1189,-1189,-1189,-1189 + + }, + + { + 69,-1190,-1190,-1190,-1190,-1190,-1190,-1190,-1190,-1190, + -1190,-1190,-1190,-1190,-1190,-1190,-1190,-1190,-1190,-1190, + -1190,-1190,-1190,-1190,-1190,-1190,-1190,-1190,-1190,-1190, + -1190,-1190, 1285,-1190,-1190,-1190,-1190,-1190,-1190,-1190, + -1190,-1190,-1190,-1190,-1190,-1190,-1190,-1190,-1190,-1190, + -1190,-1190,-1190,-1190,-1190,-1190,-1190,-1190,-1190,-1190, + -1190,-1190,-1190,-1190,-1190,-1190,-1190,-1190,-1190,-1190, + -1190,-1190,-1190,-1190,-1190,-1190,-1190,-1190,-1190,-1190, + -1190,-1190,-1190,-1190,-1190,-1190,-1190,-1190,-1190,-1190, + -1190,-1190,-1190,-1190,-1190,-1190,-1190,-1190,-1190,-1190, + + -1190,-1190,-1190,-1190,-1190,-1190,-1190,-1190,-1190,-1190, + -1190,-1190,-1190,-1190,-1190,-1190,-1190,-1190,-1190,-1190, + -1190,-1190,-1190,-1190,-1190,-1190,-1190,-1190 + }, + + { + 69,-1191,-1191,-1191,-1191,-1191,-1191,-1191,-1191,-1191, + -1191,-1191,-1191,-1191,-1191,-1191,-1191,-1191,-1191,-1191, + -1191,-1191,-1191,-1191,-1191,-1191,-1191,-1191,-1191,-1191, + -1191,-1191, 1286,-1191,-1191,-1191,-1191,-1191,-1191,-1191, + -1191,-1191,-1191,-1191,-1191,-1191,-1191,-1191, 1287, 1287, + 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287,-1191,-1191, + -1191,-1191,-1191,-1191,-1191,-1191,-1191,-1191,-1191,-1191, + + -1191,-1191,-1191,-1191,-1191,-1191,-1191,-1191,-1191,-1191, + -1191,-1191,-1191,-1191,-1191,-1191,-1191,-1191,-1191,-1191, + -1191,-1191,-1191,-1191,-1191,-1191,-1191,-1191,-1191,-1191, + -1191,-1191,-1191,-1191,-1191,-1191,-1191,-1191,-1191,-1191, + -1191,-1191,-1191,-1191,-1191,-1191,-1191,-1191,-1191,-1191, + -1191,-1191,-1191,-1191,-1191,-1191,-1191,-1191 + }, + + { + 69,-1192,-1192,-1192,-1192,-1192,-1192,-1192,-1192,-1192, + -1192,-1192,-1192,-1192,-1192,-1192,-1192,-1192,-1192,-1192, + -1192,-1192,-1192,-1192,-1192,-1192,-1192,-1192,-1192,-1192, + -1192,-1192, 1288,-1192,-1192,-1192,-1192,-1192,-1192,-1192, + + -1192,-1192,-1192,-1192,-1192,-1192,-1192,-1192,-1192,-1192, + -1192,-1192,-1192,-1192,-1192,-1192,-1192,-1192,-1192,-1192, + -1192,-1192,-1192,-1192,-1192,-1192,-1192,-1192,-1192,-1192, + -1192,-1192,-1192,-1192,-1192,-1192,-1192,-1192,-1192,-1192, + -1192,-1192,-1192,-1192,-1192,-1192,-1192,-1192,-1192,-1192, + -1192,-1192,-1192,-1192,-1192,-1192,-1192,-1192,-1192,-1192, + -1192,-1192,-1192,-1192,-1192,-1192,-1192,-1192,-1192,-1192, + -1192,-1192,-1192,-1192,-1192,-1192,-1192,-1192,-1192,-1192, + -1192,-1192,-1192,-1192,-1192,-1192,-1192,-1192 + }, + + { + 69,-1193,-1193,-1193,-1193,-1193,-1193,-1193,-1193,-1193, + + -1193,-1193,-1193,-1193,-1193,-1193,-1193,-1193,-1193,-1193, + -1193,-1193,-1193,-1193,-1193,-1193,-1193,-1193,-1193,-1193, + -1193,-1193, 1289,-1193,-1193,-1193,-1193,-1193,-1193,-1193, + -1193,-1193,-1193,-1193,-1193,-1193,-1193,-1193, 1290, 1290, + 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290,-1193,-1193, + -1193,-1193,-1193,-1193,-1193,-1193,-1193,-1193,-1193,-1193, + -1193,-1193,-1193,-1193,-1193,-1193,-1193,-1193,-1193,-1193, + -1193,-1193,-1193,-1193,-1193,-1193,-1193,-1193,-1193,-1193, + -1193,-1193,-1193,-1193,-1193,-1193,-1193,-1193,-1193,-1193, + -1193,-1193,-1193,-1193,-1193,-1193,-1193,-1193,-1193,-1193, + + -1193,-1193,-1193,-1193,-1193,-1193,-1193,-1193,-1193,-1193, + -1193,-1193,-1193,-1193,-1193,-1193,-1193,-1193 + }, + + { + 69,-1194,-1194,-1194,-1194,-1194,-1194,-1194,-1194,-1194, + -1194,-1194,-1194,-1194,-1194,-1194,-1194,-1194,-1194,-1194, + -1194,-1194,-1194,-1194,-1194,-1194,-1194,-1194,-1194,-1194, + -1194,-1194,-1194,-1194,-1194,-1194,-1194,-1194,-1194,-1194, + -1194,-1194,-1194,-1194,-1194,-1194,-1194,-1194,-1194,-1194, + -1194,-1194,-1194,-1194,-1194,-1194,-1194,-1194,-1194,-1194, + -1194,-1194,-1194,-1194,-1194,-1194,-1194,-1194,-1194,-1194, + -1194,-1194,-1194,-1194,-1194,-1194,-1194,-1194,-1194,-1194, + + -1194,-1194,-1194,-1194, 1291,-1194,-1194,-1194,-1194,-1194, + -1194,-1194,-1194,-1194,-1194,-1194,-1194,-1194,-1194,-1194, + -1194,-1194,-1194,-1194,-1194,-1194,-1194,-1194,-1194,-1194, + -1194,-1194,-1194,-1194,-1194,-1194,-1194,-1194,-1194,-1194, + -1194,-1194,-1194,-1194,-1194,-1194,-1194,-1194 + }, + + { + 69,-1195,-1195,-1195,-1195,-1195,-1195,-1195,-1195,-1195, + -1195,-1195,-1195,-1195,-1195,-1195,-1195,-1195,-1195,-1195, + -1195,-1195,-1195,-1195,-1195,-1195,-1195,-1195,-1195,-1195, + -1195,-1195, 1292,-1195,-1195,-1195,-1195,-1195,-1195,-1195, + -1195,-1195,-1195,-1195,-1195,-1195,-1195,-1195,-1195,-1195, + + -1195,-1195,-1195,-1195,-1195,-1195,-1195,-1195,-1195,-1195, + -1195,-1195,-1195,-1195,-1195,-1195,-1195,-1195,-1195,-1195, + -1195,-1195,-1195,-1195,-1195,-1195,-1195,-1195,-1195,-1195, + -1195,-1195,-1195,-1195,-1195,-1195,-1195,-1195,-1195,-1195, + -1195,-1195,-1195,-1195,-1195,-1195,-1195,-1195,-1195,-1195, + -1195,-1195,-1195,-1195,-1195,-1195,-1195,-1195,-1195,-1195, + -1195,-1195,-1195,-1195,-1195,-1195,-1195,-1195,-1195,-1195, + -1195,-1195,-1195,-1195,-1195,-1195,-1195,-1195 + }, + + { + 69,-1196,-1196,-1196,-1196,-1196,-1196,-1196,-1196,-1196, + -1196,-1196,-1196,-1196,-1196,-1196,-1196,-1196,-1196,-1196, + + -1196,-1196,-1196,-1196,-1196,-1196,-1196,-1196,-1196,-1196, + -1196,-1196,-1196,-1196,-1196,-1196,-1196,-1196,-1196,-1196, + -1196,-1196,-1196,-1196,-1196,-1196,-1196,-1196,-1196,-1196, + -1196,-1196,-1196,-1196,-1196,-1196,-1196,-1196,-1196,-1196, + -1196,-1196,-1196,-1196,-1196,-1196,-1196,-1196,-1196,-1196, + -1196,-1196,-1196,-1196,-1196,-1196,-1196,-1196,-1196,-1196, + -1196,-1196,-1196, 1293,-1196,-1196,-1196,-1196,-1196,-1196, + -1196,-1196,-1196,-1196,-1196,-1196,-1196,-1196,-1196,-1196, + -1196,-1196,-1196,-1196,-1196,-1196,-1196,-1196,-1196,-1196, + -1196,-1196,-1196,-1196,-1196,-1196,-1196,-1196,-1196,-1196, + + -1196,-1196,-1196,-1196,-1196,-1196,-1196,-1196 + }, + + { + 69,-1197,-1197,-1197,-1197,-1197,-1197,-1197,-1197,-1197, + -1197,-1197,-1197,-1197,-1197,-1197,-1197,-1197,-1197,-1197, + -1197,-1197,-1197,-1197,-1197,-1197,-1197,-1197,-1197,-1197, + -1197,-1197,-1197,-1197,-1197,-1197,-1197,-1197,-1197,-1197, + -1197,-1197,-1197,-1197,-1197,-1197,-1197,-1197,-1197,-1197, + -1197,-1197,-1197,-1197,-1197,-1197,-1197,-1197,-1197,-1197, + -1197,-1197,-1197,-1197,-1197,-1197,-1197,-1197,-1197,-1197, + -1197,-1197,-1197,-1197,-1197,-1197,-1197,-1197,-1197,-1197, + -1197,-1197,-1197,-1197,-1197,-1197,-1197,-1197,-1197,-1197, + + -1197,-1197,-1197,-1197,-1197,-1197,-1197,-1197,-1197,-1197, + -1197,-1197,-1197,-1197,-1197,-1197,-1197,-1197,-1197,-1197, + -1197,-1197,-1197,-1197,-1197,-1197,-1197,-1197,-1197,-1197, + -1197,-1197,-1197,-1197,-1197,-1197,-1197,-1197 + }, + + { + 69,-1198,-1198,-1198,-1198,-1198,-1198,-1198,-1198,-1198, + -1198,-1198,-1198,-1198,-1198,-1198,-1198,-1198,-1198,-1198, + -1198,-1198,-1198,-1198,-1198,-1198,-1198,-1198,-1198,-1198, + -1198,-1198,-1198,-1198,-1198,-1198,-1198,-1198,-1198,-1198, + -1198,-1198,-1198,-1198,-1198,-1198,-1198,-1198,-1198,-1198, + -1198,-1198,-1198,-1198,-1198,-1198,-1198,-1198,-1198,-1198, + + -1198,-1198,-1198,-1198,-1198,-1198,-1198,-1198,-1198,-1198, + -1198,-1198,-1198,-1198,-1198,-1198,-1198,-1198,-1198,-1198, + -1198, 1294,-1198,-1198,-1198,-1198,-1198,-1198,-1198,-1198, + -1198,-1198,-1198,-1198,-1198,-1198,-1198,-1198,-1198,-1198, + -1198,-1198,-1198,-1198,-1198,-1198,-1198,-1198,-1198,-1198, + -1198,-1198,-1198,-1198,-1198,-1198,-1198,-1198,-1198,-1198, + -1198,-1198,-1198,-1198,-1198,-1198,-1198,-1198 + }, + + { + 69,-1199,-1199,-1199,-1199,-1199,-1199,-1199,-1199,-1199, + -1199,-1199,-1199,-1199,-1199,-1199,-1199,-1199,-1199,-1199, + -1199,-1199,-1199,-1199,-1199,-1199,-1199,-1199,-1199,-1199, + + -1199,-1199,-1199,-1199,-1199,-1199,-1199,-1199,-1199,-1199, + -1199,-1199,-1199,-1199,-1199,-1199,-1199,-1199,-1199,-1199, + -1199,-1199,-1199,-1199,-1199,-1199,-1199,-1199,-1199,-1199, + -1199,-1199,-1199,-1199,-1199,-1199,-1199,-1199,-1199,-1199, + -1199,-1199,-1199,-1199,-1199,-1199,-1199,-1199,-1199,-1199, + -1199,-1199,-1199,-1199,-1199,-1199,-1199,-1199,-1199,-1199, + -1199,-1199,-1199,-1199,-1199,-1199,-1199,-1199,-1199,-1199, + -1199,-1199,-1199,-1199,-1199,-1199,-1199,-1199,-1199,-1199, + -1199,-1199,-1199,-1199,-1199,-1199,-1199,-1199,-1199,-1199, + -1199,-1199,-1199,-1199,-1199,-1199,-1199,-1199 + + }, + + { + 69,-1200,-1200,-1200,-1200,-1200,-1200,-1200,-1200,-1200, + -1200,-1200,-1200,-1200,-1200,-1200,-1200,-1200,-1200,-1200, + -1200,-1200,-1200,-1200,-1200,-1200,-1200,-1200,-1200,-1200, + -1200,-1200,-1200,-1200,-1200,-1200,-1200,-1200,-1200,-1200, + -1200,-1200,-1200,-1200,-1200,-1200,-1200,-1200,-1200,-1200, + -1200,-1200,-1200,-1200,-1200,-1200,-1200,-1200,-1200,-1200, + -1200,-1200,-1200,-1200,-1200,-1200,-1200,-1200,-1200,-1200, + -1200,-1200,-1200,-1200,-1200,-1200,-1200,-1200,-1200,-1200, + -1200,-1200,-1200,-1200,-1200,-1200,-1200,-1200,-1200,-1200, + -1200,-1200,-1200,-1200,-1200,-1200,-1200,-1200,-1200,-1200, + + -1200,-1200,-1200,-1200,-1200,-1200,-1200,-1200,-1200,-1200, + -1200,-1200,-1200,-1200,-1200,-1200,-1200,-1200,-1200,-1200, + -1200,-1200,-1200,-1200,-1200,-1200,-1200,-1200 + }, + + { + 69,-1201,-1201,-1201,-1201,-1201,-1201,-1201,-1201,-1201, + -1201,-1201,-1201,-1201,-1201,-1201,-1201,-1201,-1201,-1201, + -1201,-1201,-1201,-1201,-1201,-1201,-1201,-1201,-1201,-1201, + -1201,-1201,-1201,-1201,-1201,-1201,-1201,-1201,-1201,-1201, + -1201,-1201,-1201,-1201,-1201,-1201,-1201,-1201,-1201,-1201, + -1201,-1201,-1201,-1201,-1201,-1201,-1201,-1201,-1201,-1201, + -1201,-1201,-1201,-1201,-1201,-1201,-1201,-1201,-1201,-1201, + + 1295,-1201,-1201,-1201,-1201,-1201,-1201,-1201,-1201,-1201, + -1201,-1201,-1201,-1201,-1201,-1201,-1201,-1201,-1201,-1201, + -1201,-1201,-1201,-1201,-1201,-1201,-1201,-1201,-1201,-1201, + -1201,-1201,-1201,-1201,-1201,-1201,-1201,-1201,-1201,-1201, + -1201,-1201,-1201,-1201,-1201,-1201,-1201,-1201,-1201,-1201, + -1201,-1201,-1201,-1201,-1201,-1201,-1201,-1201 + }, + + { + 69,-1202,-1202,-1202,-1202,-1202,-1202,-1202,-1202,-1202, + -1202,-1202,-1202,-1202,-1202,-1202,-1202,-1202,-1202,-1202, + -1202,-1202,-1202,-1202,-1202,-1202,-1202,-1202,-1202,-1202, + -1202,-1202,-1202,-1202,-1202,-1202,-1202,-1202,-1202,-1202, + + -1202,-1202,-1202,-1202,-1202,-1202,-1202,-1202,-1202,-1202, + -1202,-1202,-1202,-1202,-1202,-1202,-1202,-1202,-1202,-1202, + -1202,-1202,-1202,-1202,-1202,-1202,-1202,-1202,-1202,-1202, + -1202,-1202,-1202,-1202,-1202,-1202,-1202,-1202,-1202,-1202, + -1202,-1202,-1202,-1202,-1202,-1202,-1202,-1202,-1202,-1202, + -1202,-1202,-1202,-1202,-1202,-1202,-1202,-1202,-1202,-1202, + -1202,-1202,-1202,-1202,-1202,-1202,-1202,-1202,-1202,-1202, + -1202,-1202,-1202,-1202,-1202,-1202,-1202,-1202,-1202,-1202, + -1202,-1202,-1202,-1202,-1202,-1202,-1202,-1202 + }, + + { + 69,-1203,-1203,-1203,-1203,-1203,-1203,-1203,-1203,-1203, + + -1203,-1203,-1203,-1203,-1203,-1203,-1203,-1203,-1203,-1203, + -1203,-1203,-1203,-1203,-1203,-1203,-1203,-1203,-1203,-1203, + -1203,-1203,-1203,-1203,-1203,-1203,-1203,-1203,-1203,-1203, + -1203,-1203,-1203,-1203,-1203,-1203,-1203,-1203,-1203,-1203, + -1203,-1203,-1203,-1203,-1203,-1203,-1203,-1203,-1203,-1203, + -1203,-1203,-1203,-1203,-1203,-1203,-1203,-1203,-1203,-1203, + -1203,-1203,-1203,-1203,-1203,-1203,-1203,-1203,-1203,-1203, + -1203,-1203,-1203,-1203,-1203,-1203,-1203,-1203,-1203,-1203, + -1203,-1203,-1203,-1203,-1203,-1203,-1203,-1203,-1203,-1203, + -1203,-1203,-1203,-1203,-1203,-1203,-1203,-1203,-1203,-1203, + + -1203,-1203,-1203,-1203,-1203,-1203,-1203,-1203,-1203,-1203, + -1203,-1203,-1203,-1203,-1203,-1203,-1203,-1203 + }, + + { + 69,-1204,-1204,-1204,-1204,-1204,-1204,-1204,-1204,-1204, + -1204,-1204,-1204,-1204,-1204,-1204,-1204,-1204,-1204,-1204, + -1204,-1204,-1204,-1204,-1204,-1204,-1204,-1204,-1204,-1204, + -1204,-1204,-1204,-1204,-1204,-1204,-1204,-1204,-1204,-1204, + -1204,-1204,-1204,-1204,-1204,-1204,-1204,-1204,-1204,-1204, + -1204,-1204,-1204,-1204,-1204,-1204,-1204,-1204,-1204,-1204, + -1204,-1204,-1204,-1204,-1204,-1204,-1204,-1204,-1204,-1204, + -1204,-1204,-1204,-1204,-1204,-1204,-1204,-1204,-1204,-1204, + + -1204,-1204,-1204,-1204,-1204,-1204,-1204,-1204,-1204,-1204, + -1204,-1204,-1204,-1204,-1204,-1204,-1204,-1204,-1204,-1204, + -1204,-1204,-1204,-1204,-1204,-1204,-1204,-1204,-1204,-1204, + -1204,-1204,-1204,-1204,-1204,-1204,-1204,-1204,-1204,-1204, + -1204,-1204,-1204,-1204,-1204,-1204,-1204,-1204 + }, + + { + 69,-1205,-1205,-1205,-1205,-1205,-1205,-1205,-1205,-1205, + -1205,-1205,-1205,-1205,-1205,-1205,-1205,-1205,-1205,-1205, + -1205,-1205,-1205,-1205,-1205,-1205,-1205,-1205,-1205,-1205, + -1205,-1205, 1296,-1205,-1205,-1205,-1205,-1205,-1205,-1205, + -1205,-1205,-1205,-1205,-1205,-1205,-1205,-1205,-1205,-1205, + + -1205,-1205,-1205,-1205,-1205,-1205,-1205,-1205,-1205,-1205, + -1205,-1205,-1205,-1205,-1205,-1205,-1205,-1205,-1205,-1205, + -1205,-1205,-1205,-1205,-1205,-1205,-1205,-1205,-1205,-1205, + -1205,-1205,-1205,-1205,-1205,-1205,-1205,-1205,-1205,-1205, + -1205,-1205,-1205,-1205,-1205,-1205,-1205,-1205,-1205,-1205, + -1205,-1205,-1205,-1205,-1205,-1205,-1205,-1205,-1205,-1205, + -1205,-1205,-1205,-1205,-1205,-1205,-1205,-1205,-1205,-1205, + -1205,-1205,-1205,-1205,-1205,-1205,-1205,-1205 + }, + + { + 69,-1206,-1206,-1206,-1206,-1206,-1206,-1206,-1206,-1206, + -1206,-1206,-1206,-1206,-1206,-1206,-1206,-1206,-1206,-1206, + + -1206,-1206,-1206,-1206,-1206,-1206,-1206,-1206,-1206,-1206, + -1206,-1206, 1297,-1206,-1206,-1206,-1206,-1206,-1206,-1206, + -1206,-1206,-1206,-1206,-1206,-1206,-1206,-1206,-1206,-1206, + -1206,-1206,-1206,-1206,-1206,-1206,-1206,-1206,-1206,-1206, + -1206,-1206,-1206,-1206,-1206,-1206,-1206,-1206,-1206,-1206, + -1206,-1206,-1206,-1206,-1206,-1206,-1206,-1206,-1206,-1206, + -1206,-1206,-1206,-1206,-1206,-1206,-1206,-1206,-1206,-1206, + -1206,-1206,-1206,-1206,-1206,-1206,-1206,-1206,-1206,-1206, + -1206,-1206,-1206,-1206,-1206,-1206,-1206,-1206,-1206,-1206, + -1206,-1206,-1206,-1206,-1206,-1206,-1206,-1206,-1206,-1206, + + -1206,-1206,-1206,-1206,-1206,-1206,-1206,-1206 + }, + + { + 69,-1207,-1207,-1207,-1207,-1207,-1207,-1207,-1207,-1207, + -1207,-1207,-1207,-1207,-1207,-1207,-1207,-1207,-1207,-1207, + -1207,-1207,-1207,-1207,-1207,-1207,-1207,-1207,-1207,-1207, + -1207,-1207, 1298,-1207,-1207,-1207,-1207,-1207,-1207,-1207, + -1207,-1207,-1207,-1207,-1207,-1207,-1207,-1207,-1207,-1207, + -1207,-1207,-1207,-1207,-1207,-1207,-1207,-1207,-1207,-1207, + -1207,-1207,-1207,-1207,-1207,-1207,-1207,-1207,-1207,-1207, + -1207,-1207,-1207,-1207,-1207,-1207,-1207,-1207,-1207,-1207, + -1207,-1207,-1207,-1207,-1207,-1207,-1207,-1207,-1207,-1207, + + -1207,-1207,-1207,-1207,-1207,-1207,-1207,-1207,-1207,-1207, + -1207,-1207,-1207,-1207,-1207,-1207,-1207,-1207,-1207,-1207, + -1207,-1207,-1207,-1207,-1207,-1207,-1207,-1207,-1207,-1207, + -1207,-1207,-1207,-1207,-1207,-1207,-1207,-1207 + }, + + { + 69,-1208,-1208,-1208,-1208,-1208,-1208,-1208,-1208,-1208, + -1208,-1208,-1208,-1208,-1208,-1208,-1208,-1208,-1208,-1208, + -1208,-1208,-1208,-1208,-1208,-1208,-1208,-1208,-1208,-1208, + -1208,-1208,-1208,-1208,-1208,-1208,-1208,-1208,-1208,-1208, + -1208,-1208,-1208,-1208,-1208,-1208,-1208,-1208,-1208,-1208, + -1208,-1208,-1208,-1208,-1208,-1208,-1208,-1208,-1208,-1208, + + -1208,-1208,-1208,-1208,-1208,-1208,-1208,-1208,-1208,-1208, + -1208,-1208,-1208,-1208,-1208,-1208,-1208,-1208,-1208,-1208, + -1208,-1208,-1208, 1299,-1208,-1208,-1208,-1208,-1208,-1208, + -1208,-1208,-1208,-1208,-1208,-1208,-1208,-1208,-1208,-1208, + -1208,-1208,-1208,-1208,-1208,-1208,-1208,-1208,-1208,-1208, + -1208,-1208,-1208,-1208,-1208,-1208,-1208,-1208,-1208,-1208, + -1208,-1208,-1208,-1208,-1208,-1208,-1208,-1208 + }, + + { + 69,-1209,-1209,-1209,-1209,-1209,-1209,-1209,-1209,-1209, + -1209,-1209,-1209,-1209,-1209,-1209,-1209,-1209,-1209,-1209, + -1209,-1209,-1209,-1209,-1209,-1209,-1209,-1209,-1209,-1209, + + -1209,-1209,-1209,-1209,-1209,-1209,-1209,-1209,-1209,-1209, + -1209,-1209,-1209,-1209,-1209,-1209,-1209,-1209,-1209,-1209, + -1209,-1209,-1209,-1209,-1209,-1209,-1209,-1209,-1209,-1209, + -1209,-1209,-1209,-1209,-1209,-1209,-1209,-1209,-1209,-1209, + -1209,-1209,-1209,-1209,-1209,-1209,-1209,-1209,-1209,-1209, + -1209,-1209, 1300,-1209,-1209,-1209,-1209,-1209,-1209,-1209, + -1209,-1209,-1209,-1209,-1209,-1209,-1209,-1209,-1209,-1209, + -1209,-1209,-1209,-1209,-1209,-1209,-1209,-1209,-1209,-1209, + -1209,-1209,-1209,-1209,-1209,-1209,-1209,-1209,-1209,-1209, + -1209,-1209,-1209,-1209,-1209,-1209,-1209,-1209 + + }, + + { + 69,-1210,-1210,-1210,-1210,-1210,-1210,-1210,-1210,-1210, + -1210,-1210,-1210,-1210,-1210,-1210,-1210,-1210,-1210,-1210, + -1210,-1210,-1210,-1210,-1210,-1210,-1210,-1210,-1210,-1210, + -1210,-1210, 1301,-1210,-1210,-1210,-1210,-1210,-1210,-1210, + -1210,-1210,-1210,-1210,-1210,-1210,-1210,-1210,-1210,-1210, + -1210,-1210,-1210,-1210,-1210,-1210,-1210,-1210,-1210,-1210, + -1210,-1210,-1210,-1210,-1210,-1210,-1210,-1210,-1210,-1210, + -1210,-1210,-1210,-1210,-1210,-1210,-1210,-1210,-1210,-1210, + -1210,-1210,-1210,-1210,-1210,-1210,-1210,-1210,-1210,-1210, + -1210,-1210,-1210,-1210,-1210,-1210,-1210,-1210,-1210,-1210, + + -1210,-1210,-1210,-1210,-1210,-1210,-1210,-1210,-1210,-1210, + -1210,-1210,-1210,-1210,-1210,-1210,-1210,-1210,-1210,-1210, + -1210,-1210,-1210,-1210,-1210,-1210,-1210,-1210 + }, + + { + 69,-1211,-1211,-1211,-1211,-1211,-1211,-1211,-1211,-1211, + -1211,-1211,-1211,-1211,-1211,-1211,-1211,-1211,-1211,-1211, + -1211,-1211,-1211,-1211,-1211,-1211,-1211,-1211,-1211,-1211, + -1211,-1211,-1211,-1211,-1211,-1211,-1211,-1211,-1211,-1211, + -1211,-1211,-1211,-1211,-1211,-1211,-1211,-1211,-1211,-1211, + -1211,-1211,-1211,-1211,-1211,-1211,-1211,-1211,-1211,-1211, + -1211,-1211,-1211,-1211,-1211,-1211,-1211,-1211,-1211,-1211, + + -1211,-1211,-1211,-1211,-1211,-1211,-1211,-1211,-1211,-1211, + -1211,-1211,-1211,-1211, 1302,-1211,-1211,-1211,-1211,-1211, + -1211,-1211,-1211,-1211,-1211,-1211,-1211,-1211,-1211,-1211, + -1211,-1211,-1211,-1211,-1211,-1211,-1211,-1211,-1211,-1211, + -1211,-1211,-1211,-1211,-1211,-1211,-1211,-1211,-1211,-1211, + -1211,-1211,-1211,-1211,-1211,-1211,-1211,-1211 + }, + + { + 69,-1212,-1212,-1212,-1212,-1212,-1212,-1212,-1212,-1212, + -1212,-1212,-1212,-1212,-1212,-1212,-1212,-1212,-1212,-1212, + -1212,-1212,-1212,-1212,-1212,-1212,-1212,-1212,-1212,-1212, + -1212,-1212, 1303,-1212,-1212,-1212,-1212,-1212,-1212,-1212, + + -1212,-1212,-1212,-1212,-1212,-1212,-1212,-1212,-1212,-1212, + -1212,-1212,-1212,-1212,-1212,-1212,-1212,-1212,-1212,-1212, + -1212,-1212,-1212,-1212,-1212,-1212,-1212,-1212,-1212,-1212, + -1212,-1212,-1212,-1212,-1212,-1212,-1212,-1212,-1212,-1212, + -1212,-1212,-1212,-1212,-1212,-1212,-1212,-1212,-1212,-1212, + -1212,-1212,-1212,-1212,-1212,-1212,-1212,-1212,-1212,-1212, + -1212,-1212,-1212,-1212,-1212,-1212,-1212,-1212,-1212,-1212, + -1212,-1212,-1212,-1212,-1212,-1212,-1212,-1212,-1212,-1212, + -1212,-1212,-1212,-1212,-1212,-1212,-1212,-1212 + }, + + { + 69,-1213,-1213,-1213,-1213,-1213,-1213,-1213,-1213,-1213, + + -1213,-1213,-1213,-1213,-1213,-1213,-1213,-1213,-1213,-1213, + -1213,-1213,-1213,-1213,-1213,-1213,-1213,-1213,-1213,-1213, + -1213,-1213, 1304,-1213,-1213,-1213,-1213,-1213,-1213,-1213, + -1213,-1213,-1213,-1213,-1213,-1213,-1213,-1213,-1213,-1213, + -1213,-1213,-1213,-1213,-1213,-1213,-1213,-1213,-1213,-1213, + -1213,-1213,-1213,-1213,-1213,-1213,-1213,-1213,-1213,-1213, + -1213,-1213,-1213,-1213,-1213,-1213,-1213,-1213,-1213,-1213, + -1213,-1213,-1213,-1213,-1213,-1213,-1213,-1213,-1213,-1213, + -1213,-1213,-1213,-1213,-1213,-1213,-1213,-1213,-1213,-1213, + -1213,-1213,-1213,-1213,-1213,-1213,-1213,-1213,-1213,-1213, + + -1213,-1213,-1213,-1213,-1213,-1213,-1213,-1213,-1213,-1213, + -1213,-1213,-1213,-1213,-1213,-1213,-1213,-1213 + }, + + { + 69,-1214,-1214,-1214,-1214,-1214,-1214,-1214,-1214,-1214, + -1214,-1214,-1214,-1214,-1214,-1214,-1214,-1214,-1214,-1214, + -1214,-1214,-1214,-1214,-1214,-1214,-1214,-1214,-1214,-1214, + -1214,-1214, 1305,-1214,-1214,-1214,-1214,-1214,-1214,-1214, + -1214,-1214,-1214,-1214,-1214,-1214,-1214,-1214,-1214,-1214, + -1214,-1214,-1214,-1214,-1214,-1214,-1214,-1214,-1214,-1214, + -1214,-1214,-1214,-1214,-1214,-1214,-1214,-1214,-1214,-1214, + -1214,-1214,-1214,-1214,-1214,-1214,-1214,-1214,-1214,-1214, + + -1214,-1214,-1214,-1214,-1214,-1214,-1214,-1214,-1214,-1214, + -1214,-1214,-1214,-1214,-1214,-1214,-1214,-1214,-1214,-1214, + -1214,-1214,-1214,-1214,-1214,-1214,-1214,-1214,-1214,-1214, + -1214,-1214,-1214,-1214,-1214,-1214,-1214,-1214,-1214,-1214, + -1214,-1214,-1214,-1214,-1214,-1214,-1214,-1214 + }, + + { + 69,-1215,-1215,-1215,-1215,-1215,-1215,-1215,-1215,-1215, + -1215,-1215,-1215,-1215,-1215,-1215,-1215,-1215,-1215,-1215, + -1215,-1215,-1215,-1215,-1215,-1215,-1215,-1215,-1215,-1215, + -1215,-1215, 1306,-1215,-1215,-1215,-1215,-1215,-1215,-1215, + -1215,-1215,-1215,-1215,-1215,-1215,-1215,-1215,-1215,-1215, + + -1215,-1215,-1215,-1215,-1215,-1215,-1215,-1215,-1215,-1215, + -1215,-1215,-1215,-1215,-1215,-1215,-1215,-1215,-1215,-1215, + -1215,-1215,-1215,-1215,-1215,-1215,-1215,-1215,-1215,-1215, + -1215,-1215,-1215,-1215,-1215,-1215,-1215,-1215,-1215,-1215, + -1215,-1215,-1215,-1215,-1215,-1215,-1215,-1215,-1215,-1215, + -1215,-1215,-1215,-1215,-1215,-1215,-1215,-1215,-1215,-1215, + -1215,-1215,-1215,-1215,-1215,-1215,-1215,-1215,-1215,-1215, + -1215,-1215,-1215,-1215,-1215,-1215,-1215,-1215 + }, + + { + 69,-1216,-1216,-1216,-1216,-1216,-1216,-1216,-1216,-1216, + -1216,-1216,-1216,-1216,-1216,-1216,-1216,-1216,-1216,-1216, + + -1216,-1216,-1216,-1216,-1216,-1216,-1216,-1216,-1216,-1216, + -1216,-1216, 1307,-1216,-1216,-1216,-1216,-1216,-1216,-1216, + -1216,-1216,-1216,-1216,-1216,-1216,-1216,-1216,-1216,-1216, + -1216,-1216,-1216,-1216,-1216,-1216,-1216,-1216,-1216,-1216, + -1216,-1216,-1216,-1216,-1216,-1216,-1216,-1216,-1216,-1216, + -1216,-1216,-1216,-1216,-1216,-1216,-1216,-1216,-1216,-1216, + -1216,-1216,-1216,-1216,-1216,-1216,-1216,-1216,-1216,-1216, + -1216,-1216,-1216,-1216,-1216,-1216,-1216,-1216,-1216,-1216, + -1216,-1216,-1216,-1216,-1216,-1216,-1216,-1216,-1216,-1216, + -1216,-1216,-1216,-1216,-1216,-1216,-1216,-1216,-1216,-1216, + + -1216,-1216,-1216,-1216,-1216,-1216,-1216,-1216 + }, + + { + 69,-1217,-1217,-1217,-1217,-1217,-1217,-1217,-1217,-1217, + -1217,-1217,-1217,-1217,-1217,-1217,-1217,-1217,-1217,-1217, + -1217,-1217,-1217,-1217,-1217,-1217,-1217,-1217,-1217,-1217, + -1217,-1217, 1308,-1217,-1217,-1217,-1217,-1217,-1217,-1217, + -1217,-1217,-1217,-1217,-1217,-1217,-1217,-1217,-1217,-1217, + -1217,-1217,-1217,-1217,-1217,-1217,-1217,-1217,-1217,-1217, + -1217,-1217,-1217,-1217,-1217,-1217,-1217,-1217,-1217,-1217, + -1217,-1217,-1217,-1217,-1217,-1217,-1217,-1217,-1217,-1217, + -1217,-1217,-1217,-1217,-1217,-1217,-1217,-1217,-1217,-1217, + + -1217,-1217,-1217,-1217,-1217,-1217,-1217,-1217,-1217,-1217, + -1217,-1217,-1217,-1217,-1217,-1217,-1217,-1217,-1217,-1217, + -1217,-1217,-1217,-1217,-1217,-1217,-1217,-1217,-1217,-1217, + -1217,-1217,-1217,-1217,-1217,-1217,-1217,-1217 + }, + + { + 69,-1218,-1218,-1218,-1218,-1218,-1218,-1218,-1218,-1218, + -1218,-1218,-1218,-1218,-1218,-1218,-1218,-1218,-1218,-1218, + -1218,-1218,-1218,-1218,-1218,-1218,-1218,-1218,-1218,-1218, + -1218,-1218,-1218,-1218,-1218,-1218,-1218,-1218,-1218,-1218, + -1218,-1218,-1218,-1218,-1218,-1218,-1218,-1218,-1218,-1218, + -1218,-1218,-1218,-1218,-1218,-1218,-1218,-1218,-1218,-1218, + + -1218,-1218,-1218,-1218,-1218,-1218,-1218,-1218,-1218,-1218, + -1218,-1218,-1218,-1218,-1218,-1218,-1218,-1218,-1218,-1218, + -1218,-1218,-1218,-1218,-1218,-1218,-1218,-1218,-1218,-1218, + -1218,-1218,-1218,-1218,-1218,-1218,-1218,-1218,-1218,-1218, + -1218,-1218,-1218,-1218,-1218,-1218,-1218,-1218,-1218,-1218, + -1218,-1218,-1218,-1218,-1218,-1218,-1218,-1218,-1218,-1218, + -1218,-1218,-1218,-1218,-1218,-1218,-1218,-1218 + }, + + { + 69,-1219,-1219,-1219,-1219,-1219,-1219,-1219,-1219,-1219, + -1219,-1219,-1219,-1219,-1219,-1219,-1219,-1219,-1219,-1219, + -1219,-1219,-1219,-1219,-1219,-1219,-1219,-1219,-1219,-1219, + + -1219,-1219,-1219,-1219,-1219,-1219,-1219,-1219,-1219,-1219, + -1219,-1219,-1219,-1219,-1219,-1219,-1219,-1219,-1219,-1219, + -1219,-1219,-1219,-1219,-1219,-1219,-1219,-1219,-1219,-1219, + -1219,-1219,-1219,-1219,-1219,-1219,-1219,-1219,-1219,-1219, + -1219,-1219,-1219,-1219,-1219,-1219,-1219,-1219,-1219,-1219, + -1219,-1219,-1219,-1219,-1219,-1219,-1219,-1219,-1219,-1219, + -1219,-1219,-1219,-1219,-1219,-1219,-1219,-1219,-1219,-1219, + -1219,-1219,-1219,-1219,-1219,-1219,-1219,-1219,-1219,-1219, + -1219,-1219,-1219,-1219,-1219,-1219,-1219,-1219,-1219,-1219, + -1219,-1219,-1219,-1219,-1219,-1219,-1219,-1219 + + }, + + { + 69,-1220,-1220,-1220,-1220,-1220,-1220,-1220,-1220,-1220, + -1220,-1220,-1220,-1220,-1220,-1220,-1220,-1220,-1220,-1220, + -1220,-1220,-1220,-1220,-1220,-1220,-1220,-1220,-1220,-1220, + -1220,-1220, 1309,-1220,-1220,-1220,-1220,-1220,-1220,-1220, + -1220,-1220,-1220,-1220,-1220,-1220,-1220,-1220,-1220,-1220, + -1220,-1220,-1220,-1220,-1220,-1220,-1220,-1220,-1220,-1220, + -1220,-1220,-1220,-1220,-1220,-1220,-1220,-1220,-1220,-1220, + -1220,-1220,-1220,-1220,-1220,-1220,-1220,-1220,-1220,-1220, + -1220,-1220,-1220,-1220,-1220,-1220,-1220,-1220,-1220,-1220, + -1220,-1220,-1220,-1220,-1220,-1220,-1220,-1220,-1220,-1220, + + -1220,-1220,-1220,-1220,-1220,-1220,-1220,-1220,-1220,-1220, + -1220,-1220,-1220,-1220,-1220,-1220,-1220,-1220,-1220,-1220, + -1220,-1220,-1220,-1220,-1220,-1220,-1220,-1220 + }, + + { + 69,-1221,-1221,-1221,-1221,-1221,-1221,-1221,-1221,-1221, + -1221,-1221,-1221,-1221,-1221,-1221,-1221,-1221,-1221,-1221, + -1221,-1221,-1221,-1221,-1221,-1221,-1221,-1221,-1221,-1221, + -1221,-1221, 1310,-1221,-1221,-1221,-1221,-1221,-1221,-1221, + -1221,-1221,-1221,-1221,-1221,-1221,-1221,-1221,-1221,-1221, + -1221,-1221,-1221,-1221,-1221,-1221,-1221,-1221,-1221,-1221, + -1221,-1221,-1221,-1221,-1221,-1221,-1221,-1221,-1221,-1221, + + -1221,-1221,-1221,-1221,-1221,-1221,-1221,-1221,-1221,-1221, + -1221,-1221,-1221,-1221,-1221,-1221,-1221,-1221,-1221,-1221, + -1221,-1221,-1221,-1221,-1221,-1221,-1221,-1221,-1221,-1221, + -1221,-1221,-1221,-1221,-1221,-1221,-1221,-1221,-1221,-1221, + -1221,-1221,-1221,-1221,-1221,-1221,-1221,-1221,-1221,-1221, + -1221,-1221,-1221,-1221,-1221,-1221,-1221,-1221 + }, + + { + 69,-1222,-1222,-1222,-1222,-1222,-1222,-1222,-1222,-1222, + -1222,-1222,-1222,-1222,-1222,-1222,-1222,-1222,-1222,-1222, + -1222,-1222,-1222,-1222,-1222,-1222,-1222,-1222,-1222,-1222, + -1222,-1222, 1311,-1222,-1222,-1222,-1222,-1222,-1222,-1222, + + -1222,-1222,-1222,-1222,-1222,-1222,-1222,-1222,-1222,-1222, + -1222,-1222,-1222,-1222,-1222,-1222,-1222,-1222,-1222,-1222, + -1222,-1222,-1222,-1222,-1222,-1222,-1222,-1222,-1222,-1222, + -1222,-1222,-1222,-1222,-1222,-1222,-1222,-1222,-1222,-1222, + -1222,-1222,-1222,-1222,-1222,-1222,-1222,-1222,-1222,-1222, + -1222,-1222,-1222,-1222,-1222,-1222,-1222,-1222,-1222,-1222, + -1222,-1222,-1222,-1222,-1222,-1222,-1222,-1222,-1222,-1222, + -1222,-1222,-1222,-1222,-1222,-1222,-1222,-1222,-1222,-1222, + -1222,-1222,-1222,-1222,-1222,-1222,-1222,-1222 + }, + + { + 69,-1223,-1223,-1223,-1223,-1223,-1223,-1223,-1223,-1223, + + -1223,-1223,-1223,-1223,-1223,-1223,-1223,-1223,-1223,-1223, + -1223,-1223,-1223,-1223,-1223,-1223,-1223,-1223,-1223,-1223, + -1223,-1223, 1312,-1223,-1223,-1223,-1223,-1223,-1223,-1223, + -1223,-1223,-1223,-1223,-1223,-1223,-1223,-1223,-1223,-1223, + -1223,-1223,-1223,-1223,-1223,-1223,-1223,-1223,-1223,-1223, + -1223,-1223,-1223,-1223,-1223, 1312, 1312, 1312, 1312, 1312, + 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, + 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, + 1312,-1223,-1223,-1223,-1223,-1223,-1223,-1223,-1223,-1223, + -1223,-1223,-1223,-1223,-1223,-1223,-1223,-1223,-1223,-1223, + + -1223,-1223,-1223,-1223,-1223,-1223,-1223,-1223,-1223,-1223, + -1223,-1223,-1223,-1223,-1223,-1223,-1223,-1223 + }, + + { + 69,-1224,-1224,-1224,-1224,-1224,-1224,-1224,-1224,-1224, + -1224,-1224,-1224,-1224,-1224,-1224,-1224,-1224,-1224,-1224, + -1224,-1224,-1224,-1224,-1224,-1224,-1224,-1224,-1224,-1224, + -1224,-1224, 1313,-1224,-1224,-1224,-1224,-1224,-1224,-1224, + -1224,-1224,-1224,-1224,-1224,-1224,-1224,-1224,-1224,-1224, + -1224,-1224,-1224,-1224,-1224,-1224,-1224,-1224,-1224,-1224, + -1224,-1224,-1224,-1224,-1224, 1313, 1313, 1313, 1313, 1313, + 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, + + 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, + 1313,-1224,-1224,-1224,-1224,-1224,-1224,-1224,-1224,-1224, + -1224,-1224,-1224,-1224,-1224,-1224,-1224,-1224,-1224,-1224, + -1224,-1224,-1224,-1224,-1224,-1224,-1224,-1224,-1224,-1224, + -1224,-1224,-1224,-1224,-1224,-1224,-1224,-1224 + }, + + { + 69,-1225,-1225,-1225,-1225,-1225,-1225,-1225,-1225,-1225, + -1225,-1225,-1225,-1225,-1225,-1225,-1225,-1225,-1225,-1225, + -1225,-1225,-1225,-1225,-1225,-1225,-1225,-1225,-1225,-1225, + -1225,-1225, 1314,-1225,-1225,-1225,-1225,-1225,-1225,-1225, + -1225,-1225,-1225,-1225,-1225,-1225,-1225,-1225,-1225,-1225, + + -1225,-1225,-1225,-1225,-1225,-1225,-1225,-1225,-1225,-1225, + -1225,-1225,-1225,-1225,-1225,-1225,-1225,-1225,-1225,-1225, + -1225,-1225,-1225,-1225,-1225,-1225,-1225,-1225,-1225,-1225, + -1225,-1225,-1225,-1225,-1225,-1225,-1225,-1225,-1225,-1225, + -1225,-1225,-1225,-1225,-1225,-1225,-1225,-1225,-1225,-1225, + -1225,-1225,-1225,-1225,-1225,-1225,-1225,-1225,-1225,-1225, + -1225,-1225,-1225,-1225,-1225,-1225,-1225,-1225,-1225,-1225, + -1225,-1225,-1225,-1225,-1225,-1225,-1225,-1225 + }, + + { + 69,-1226,-1226,-1226,-1226,-1226,-1226,-1226,-1226,-1226, + -1226,-1226,-1226,-1226,-1226,-1226,-1226,-1226,-1226,-1226, + + -1226,-1226,-1226,-1226,-1226,-1226,-1226,-1226,-1226,-1226, + -1226,-1226, 1315,-1226,-1226,-1226,-1226,-1226,-1226,-1226, + -1226,-1226,-1226,-1226,-1226,-1226,-1226,-1226,-1226,-1226, + -1226,-1226,-1226,-1226,-1226,-1226,-1226,-1226,-1226,-1226, + -1226,-1226,-1226,-1226,-1226,-1226,-1226,-1226,-1226,-1226, + -1226,-1226,-1226,-1226,-1226,-1226,-1226,-1226,-1226,-1226, + -1226,-1226,-1226,-1226,-1226,-1226,-1226,-1226,-1226,-1226, + -1226,-1226,-1226,-1226,-1226,-1226,-1226,-1226,-1226,-1226, + -1226,-1226,-1226,-1226,-1226,-1226,-1226,-1226,-1226,-1226, + -1226,-1226,-1226,-1226,-1226,-1226,-1226,-1226,-1226,-1226, + + -1226,-1226,-1226,-1226,-1226,-1226,-1226,-1226 + }, + + { + 69,-1227,-1227,-1227,-1227,-1227,-1227,-1227,-1227,-1227, + -1227,-1227,-1227,-1227,-1227,-1227,-1227,-1227,-1227,-1227, + -1227,-1227,-1227,-1227,-1227,-1227,-1227,-1227,-1227,-1227, + -1227,-1227, 1316,-1227,-1227,-1227,-1227,-1227,-1227,-1227, + -1227,-1227,-1227,-1227,-1227,-1227,-1227,-1227,-1227,-1227, + -1227,-1227,-1227,-1227,-1227,-1227,-1227,-1227,-1227,-1227, + -1227,-1227,-1227,-1227,-1227, 1316, 1316, 1316, 1316, 1316, + 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, + 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, + + 1316,-1227,-1227,-1227,-1227,-1227,-1227,-1227,-1227,-1227, + -1227,-1227,-1227,-1227,-1227,-1227,-1227,-1227,-1227,-1227, + -1227,-1227,-1227,-1227,-1227,-1227,-1227,-1227,-1227,-1227, + -1227,-1227,-1227,-1227,-1227,-1227,-1227,-1227 + }, + + { + 69,-1228,-1228,-1228,-1228,-1228,-1228,-1228,-1228,-1228, + -1228,-1228,-1228,-1228,-1228,-1228,-1228,-1228,-1228,-1228, + -1228,-1228,-1228,-1228,-1228,-1228,-1228,-1228,-1228,-1228, + -1228,-1228, 1317,-1228,-1228,-1228,-1228,-1228,-1228,-1228, + -1228,-1228,-1228,-1228,-1228,-1228,-1228,-1228,-1228,-1228, + -1228,-1228,-1228,-1228,-1228,-1228,-1228,-1228,-1228,-1228, + + -1228,-1228,-1228,-1228,-1228, 1317, 1317, 1317, 1317, 1317, + 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, + 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, + 1317,-1228,-1228,-1228,-1228,-1228,-1228,-1228,-1228,-1228, + -1228,-1228,-1228,-1228,-1228,-1228,-1228,-1228,-1228,-1228, + -1228,-1228,-1228,-1228,-1228,-1228,-1228,-1228,-1228,-1228, + -1228,-1228,-1228,-1228,-1228,-1228,-1228,-1228 + }, + + { + 69,-1229,-1229,-1229,-1229,-1229,-1229,-1229,-1229,-1229, + -1229,-1229,-1229,-1229,-1229,-1229,-1229,-1229,-1229,-1229, + -1229,-1229,-1229,-1229,-1229,-1229,-1229,-1229,-1229,-1229, + + -1229,-1229,-1229,-1229,-1229,-1229,-1229,-1229,-1229,-1229, + -1229,-1229,-1229,-1229,-1229,-1229,-1229,-1229,-1229,-1229, + -1229,-1229,-1229,-1229,-1229,-1229,-1229,-1229,-1229,-1229, + -1229,-1229,-1229,-1229,-1229,-1229,-1229,-1229,-1229,-1229, + -1229,-1229,-1229,-1229,-1229,-1229,-1229,-1229,-1229,-1229, + -1229,-1229,-1229,-1229,-1229,-1229,-1229,-1229,-1229,-1229, + -1229,-1229,-1229,-1229,-1229,-1229,-1229,-1229,-1229,-1229, + -1229,-1229,-1229,-1229,-1229,-1229,-1229,-1229,-1229,-1229, + -1229,-1229,-1229,-1229,-1229,-1229,-1229,-1229,-1229,-1229, + -1229,-1229,-1229,-1229,-1229,-1229,-1229,-1229 + + }, + + { + 69,-1230,-1230,-1230,-1230,-1230,-1230,-1230,-1230,-1230, + -1230,-1230,-1230,-1230,-1230,-1230,-1230,-1230,-1230,-1230, + -1230,-1230,-1230,-1230,-1230,-1230,-1230,-1230,-1230,-1230, + -1230,-1230, 1318,-1230,-1230,-1230,-1230,-1230,-1230,-1230, + -1230,-1230,-1230,-1230,-1230,-1230,-1230,-1230,-1230,-1230, + -1230,-1230,-1230,-1230,-1230,-1230,-1230,-1230,-1230,-1230, + -1230,-1230,-1230,-1230,-1230,-1230,-1230,-1230,-1230,-1230, + -1230,-1230,-1230,-1230,-1230,-1230,-1230,-1230,-1230,-1230, + -1230,-1230,-1230,-1230,-1230,-1230,-1230,-1230,-1230,-1230, + -1230,-1230,-1230,-1230,-1230,-1230,-1230,-1230,-1230,-1230, + + -1230,-1230,-1230,-1230,-1230,-1230,-1230,-1230,-1230,-1230, + -1230,-1230,-1230,-1230,-1230,-1230,-1230,-1230,-1230,-1230, + -1230,-1230,-1230,-1230,-1230,-1230,-1230,-1230 + }, + + { + 69,-1231,-1231,-1231,-1231,-1231,-1231,-1231,-1231,-1231, + -1231,-1231,-1231,-1231,-1231,-1231,-1231,-1231,-1231,-1231, + -1231,-1231,-1231,-1231,-1231,-1231,-1231,-1231,-1231,-1231, + -1231,-1231, 1319,-1231,-1231,-1231,-1231,-1231,-1231,-1231, + -1231,-1231,-1231,-1231,-1231,-1231,-1231,-1231,-1231,-1231, + -1231,-1231,-1231,-1231,-1231,-1231,-1231,-1231,-1231,-1231, + -1231,-1231,-1231,-1231,-1231,-1231,-1231,-1231,-1231,-1231, + + -1231,-1231,-1231,-1231,-1231,-1231,-1231,-1231,-1231,-1231, + -1231,-1231,-1231,-1231,-1231,-1231,-1231,-1231,-1231,-1231, + -1231,-1231,-1231,-1231,-1231,-1231,-1231,-1231,-1231,-1231, + -1231,-1231,-1231,-1231,-1231,-1231,-1231,-1231,-1231,-1231, + -1231,-1231,-1231,-1231,-1231,-1231,-1231,-1231,-1231,-1231, + -1231,-1231,-1231,-1231,-1231,-1231,-1231,-1231 + }, + + { + 69,-1232,-1232,-1232,-1232,-1232,-1232,-1232,-1232,-1232, + -1232,-1232,-1232,-1232,-1232,-1232,-1232,-1232,-1232,-1232, + -1232,-1232,-1232,-1232,-1232,-1232,-1232,-1232,-1232,-1232, + -1232,-1232, 1320,-1232,-1232,-1232,-1232,-1232,-1232,-1232, + + -1232,-1232,-1232,-1232,-1232,-1232,-1232,-1232,-1232,-1232, + -1232,-1232,-1232,-1232,-1232,-1232,-1232,-1232,-1232,-1232, + -1232,-1232,-1232,-1232,-1232, 1320, 1320, 1320, 1320, 1320, + 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, + 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, + 1320,-1232,-1232,-1232,-1232,-1232,-1232,-1232,-1232,-1232, + -1232,-1232,-1232,-1232,-1232,-1232,-1232,-1232,-1232,-1232, + -1232,-1232,-1232,-1232,-1232,-1232,-1232,-1232,-1232,-1232, + -1232,-1232,-1232,-1232,-1232,-1232,-1232,-1232 + }, + + { + 69,-1233,-1233,-1233,-1233,-1233,-1233,-1233,-1233,-1233, + + -1233,-1233,-1233,-1233,-1233,-1233,-1233,-1233,-1233,-1233, + -1233,-1233,-1233,-1233,-1233,-1233,-1233,-1233,-1233,-1233, + -1233,-1233, 1321,-1233,-1233,-1233,-1233,-1233,-1233,-1233, + -1233,-1233,-1233,-1233,-1233,-1233,-1233,-1233,-1233,-1233, + -1233,-1233,-1233,-1233,-1233,-1233,-1233,-1233,-1233,-1233, + -1233,-1233,-1233,-1233,-1233,-1233,-1233,-1233,-1233,-1233, + -1233,-1233,-1233,-1233,-1233,-1233,-1233,-1233,-1233,-1233, + -1233,-1233,-1233,-1233,-1233,-1233,-1233,-1233,-1233,-1233, + -1233,-1233,-1233,-1233,-1233,-1233,-1233,-1233,-1233,-1233, + -1233,-1233,-1233,-1233,-1233,-1233,-1233,-1233,-1233,-1233, + + -1233,-1233,-1233,-1233,-1233,-1233,-1233,-1233,-1233,-1233, + -1233,-1233,-1233,-1233,-1233,-1233,-1233,-1233 + }, + + { + 69,-1234,-1234,-1234,-1234,-1234,-1234,-1234,-1234,-1234, + -1234,-1234,-1234,-1234,-1234,-1234,-1234,-1234,-1234,-1234, + -1234,-1234,-1234,-1234,-1234,-1234,-1234,-1234,-1234,-1234, + -1234,-1234, 1322,-1234,-1234,-1234,-1234,-1234,-1234,-1234, + -1234,-1234,-1234,-1234,-1234,-1234,-1234,-1234,-1234,-1234, + -1234,-1234,-1234,-1234,-1234,-1234,-1234,-1234,-1234,-1234, + -1234,-1234,-1234,-1234,-1234,-1234,-1234,-1234,-1234,-1234, + -1234,-1234,-1234,-1234,-1234,-1234,-1234,-1234,-1234,-1234, + + -1234,-1234,-1234,-1234,-1234,-1234,-1234,-1234,-1234,-1234, + -1234,-1234,-1234,-1234,-1234,-1234,-1234,-1234,-1234,-1234, + -1234,-1234,-1234,-1234,-1234,-1234,-1234,-1234,-1234,-1234, + -1234,-1234,-1234,-1234,-1234,-1234,-1234,-1234,-1234,-1234, + -1234,-1234,-1234,-1234,-1234,-1234,-1234,-1234 + }, + + { + 69,-1235,-1235,-1235,-1235,-1235,-1235,-1235,-1235,-1235, + -1235,-1235,-1235,-1235,-1235,-1235,-1235,-1235,-1235,-1235, + -1235,-1235,-1235,-1235,-1235,-1235,-1235,-1235,-1235,-1235, + -1235,-1235, 1323,-1235,-1235,-1235,-1235,-1235,-1235,-1235, + -1235,-1235,-1235,-1235,-1235,-1235,-1235,-1235,-1235,-1235, + + -1235,-1235,-1235,-1235,-1235,-1235,-1235,-1235,-1235,-1235, + -1235,-1235,-1235,-1235,-1235, 1323, 1323, 1323, 1323, 1323, + 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, + 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, + 1323,-1235,-1235,-1235,-1235,-1235,-1235,-1235,-1235,-1235, + -1235,-1235,-1235,-1235,-1235,-1235,-1235,-1235,-1235,-1235, + -1235,-1235,-1235,-1235,-1235,-1235,-1235,-1235,-1235,-1235, + -1235,-1235,-1235,-1235,-1235,-1235,-1235,-1235 + }, + + { + 69,-1236,-1236,-1236,-1236,-1236,-1236,-1236,-1236,-1236, + -1236,-1236,-1236,-1236,-1236,-1236,-1236,-1236,-1236,-1236, + + -1236,-1236,-1236,-1236,-1236,-1236,-1236,-1236,-1236,-1236, + -1236,-1236, 1324,-1236,-1236,-1236,-1236,-1236,-1236,-1236, + -1236,-1236,-1236,-1236,-1236,-1236,-1236,-1236,-1236,-1236, + -1236,-1236,-1236,-1236,-1236,-1236,-1236,-1236,-1236,-1236, + -1236,-1236,-1236,-1236,-1236,-1236,-1236,-1236,-1236,-1236, + -1236,-1236,-1236,-1236,-1236,-1236,-1236,-1236,-1236,-1236, + -1236,-1236,-1236,-1236,-1236,-1236,-1236,-1236,-1236,-1236, + -1236,-1236,-1236,-1236,-1236,-1236,-1236,-1236,-1236,-1236, + -1236,-1236,-1236,-1236,-1236,-1236,-1236,-1236,-1236,-1236, + -1236,-1236,-1236,-1236,-1236,-1236,-1236,-1236,-1236,-1236, + + -1236,-1236,-1236,-1236,-1236,-1236,-1236,-1236 + }, + + { + 69,-1237,-1237,-1237,-1237,-1237,-1237,-1237,-1237,-1237, + -1237,-1237,-1237,-1237,-1237,-1237,-1237,-1237,-1237,-1237, + -1237,-1237,-1237,-1237,-1237,-1237,-1237,-1237,-1237,-1237, + -1237,-1237,-1237,-1237,-1237,-1237,-1237,-1237,-1237,-1237, + -1237,-1237,-1237,-1237,-1237,-1237,-1237,-1237,-1237,-1237, + -1237,-1237,-1237,-1237,-1237,-1237,-1237,-1237,-1237,-1237, + -1237,-1237,-1237,-1237,-1237,-1237,-1237,-1237,-1237,-1237, + -1237,-1237,-1237,-1237,-1237,-1237,-1237,-1237,-1237,-1237, + -1237,-1237,-1237,-1237,-1237,-1237,-1237,-1237,-1237,-1237, + + -1237,-1237,-1237,-1237,-1237,-1237,-1237,-1237,-1237,-1237, + -1237,-1237,-1237,-1237,-1237,-1237,-1237,-1237,-1237,-1237, + -1237,-1237,-1237,-1237,-1237,-1237,-1237,-1237,-1237,-1237, + -1237,-1237,-1237,-1237,-1237,-1237,-1237,-1237 + }, + + { + 69,-1238,-1238,-1238,-1238,-1238,-1238,-1238,-1238,-1238, + -1238,-1238,-1238,-1238,-1238,-1238,-1238,-1238,-1238,-1238, + -1238,-1238,-1238,-1238,-1238,-1238,-1238,-1238,-1238,-1238, + -1238,-1238,-1238,-1238,-1238,-1238,-1238,-1238,-1238,-1238, + -1238,-1238,-1238,-1238,-1238,-1238,-1238,-1238,-1238,-1238, + -1238,-1238,-1238,-1238,-1238,-1238,-1238,-1238,-1238,-1238, + + -1238,-1238,-1238,-1238,-1238,-1238,-1238,-1238,-1238,-1238, + -1238,-1238,-1238,-1238,-1238,-1238,-1238,-1238,-1238,-1238, + -1238,-1238,-1238,-1238,-1238,-1238,-1238,-1238,-1238,-1238, + -1238,-1238,-1238,-1238,-1238,-1238,-1238,-1238,-1238,-1238, + -1238,-1238,-1238,-1238,-1238,-1238,-1238,-1238,-1238,-1238, + -1238,-1238,-1238,-1238,-1238,-1238,-1238,-1238,-1238,-1238, + -1238,-1238,-1238,-1238,-1238,-1238,-1238,-1238 + }, + + { + 69,-1239,-1239,-1239,-1239,-1239,-1239,-1239,-1239,-1239, + -1239,-1239,-1239,-1239,-1239,-1239,-1239,-1239,-1239,-1239, + -1239,-1239,-1239,-1239,-1239,-1239,-1239,-1239,-1239,-1239, + + -1239,-1239,-1239,-1239,-1239,-1239,-1239,-1239,-1239,-1239, + -1239,-1239,-1239,-1239,-1239,-1239,-1239,-1239,-1239,-1239, + -1239,-1239,-1239,-1239,-1239,-1239,-1239,-1239,-1239,-1239, + -1239,-1239,-1239,-1239,-1239,-1239,-1239,-1239,-1239,-1239, + -1239,-1239,-1239,-1239,-1239,-1239,-1239,-1239,-1239,-1239, + -1239,-1239,-1239,-1239,-1239,-1239,-1239,-1239,-1239,-1239, + -1239,-1239,-1239,-1239,-1239,-1239,-1239,-1239,-1239,-1239, + -1239,-1239,-1239,-1239,-1239,-1239,-1239,-1239,-1239,-1239, + -1239,-1239,-1239,-1239,-1239,-1239,-1239,-1239,-1239,-1239, + -1239,-1239,-1239,-1239,-1239,-1239,-1239,-1239 + + }, + + { + 69,-1240,-1240,-1240,-1240,-1240,-1240,-1240,-1240,-1240, + -1240,-1240,-1240,-1240,-1240,-1240,-1240,-1240,-1240,-1240, + -1240,-1240,-1240,-1240,-1240,-1240,-1240,-1240,-1240,-1240, + -1240,-1240,-1240,-1240,-1240,-1240,-1240,-1240,-1240,-1240, + -1240,-1240,-1240,-1240,-1240,-1240,-1240,-1240,-1240,-1240, + -1240,-1240,-1240,-1240,-1240,-1240,-1240,-1240,-1240,-1240, + -1240,-1240,-1240,-1240,-1240,-1240,-1240,-1240,-1240,-1240, + -1240,-1240,-1240,-1240,-1240,-1240,-1240,-1240,-1240,-1240, + -1240,-1240,-1240,-1240,-1240,-1240,-1240,-1240,-1240,-1240, + -1240,-1240,-1240,-1240,-1240,-1240,-1240,-1240,-1240,-1240, + + -1240,-1240,-1240,-1240,-1240,-1240,-1240,-1240,-1240,-1240, + -1240,-1240,-1240,-1240,-1240,-1240,-1240,-1240,-1240,-1240, + -1240,-1240,-1240,-1240,-1240,-1240,-1240,-1240 + }, + + { + 69,-1241,-1241,-1241,-1241,-1241,-1241,-1241,-1241,-1241, + -1241,-1241,-1241,-1241,-1241,-1241,-1241,-1241,-1241,-1241, + -1241,-1241,-1241,-1241,-1241,-1241,-1241,-1241,-1241,-1241, + -1241,-1241,-1241,-1241,-1241,-1241,-1241,-1241,-1241,-1241, + -1241,-1241,-1241,-1241,-1241,-1241,-1241,-1241,-1241,-1241, + -1241,-1241,-1241,-1241,-1241,-1241,-1241,-1241,-1241,-1241, + -1241,-1241,-1241,-1241,-1241,-1241,-1241,-1241,-1241,-1241, + + -1241,-1241,-1241,-1241,-1241,-1241,-1241,-1241,-1241,-1241, + -1241,-1241,-1241,-1241,-1241,-1241,-1241,-1241,-1241,-1241, + -1241,-1241,-1241,-1241,-1241,-1241,-1241,-1241,-1241,-1241, + -1241,-1241,-1241,-1241,-1241,-1241,-1241,-1241,-1241,-1241, + -1241,-1241,-1241,-1241,-1241,-1241,-1241,-1241,-1241,-1241, + -1241,-1241,-1241,-1241,-1241,-1241,-1241,-1241 + }, + + { + 69,-1242,-1242,-1242,-1242,-1242,-1242,-1242,-1242,-1242, + -1242,-1242,-1242,-1242,-1242,-1242,-1242,-1242,-1242,-1242, + -1242,-1242,-1242,-1242,-1242,-1242,-1242,-1242,-1242,-1242, + -1242,-1242,-1242,-1242,-1242,-1242,-1242,-1242,-1242,-1242, + + -1242,-1242,-1242,-1242,-1242,-1242,-1242,-1242,-1242,-1242, + -1242,-1242,-1242,-1242,-1242,-1242,-1242,-1242,-1242,-1242, + -1242,-1242,-1242,-1242,-1242,-1242,-1242,-1242,-1242,-1242, + -1242,-1242,-1242,-1242,-1242,-1242,-1242,-1242,-1242,-1242, + -1242,-1242,-1242,-1242,-1242,-1242,-1242,-1242,-1242,-1242, + -1242,-1242,-1242,-1242,-1242,-1242,-1242,-1242,-1242,-1242, + -1242,-1242,-1242,-1242,-1242,-1242,-1242,-1242,-1242,-1242, + -1242,-1242,-1242,-1242,-1242,-1242,-1242,-1242,-1242,-1242, + -1242,-1242,-1242,-1242,-1242,-1242,-1242,-1242 + }, + + { + 69,-1243,-1243,-1243,-1243,-1243,-1243,-1243,-1243,-1243, + + -1243,-1243,-1243,-1243,-1243,-1243,-1243,-1243,-1243,-1243, + -1243,-1243,-1243,-1243,-1243,-1243,-1243,-1243,-1243,-1243, + -1243,-1243,-1243,-1243,-1243,-1243,-1243,-1243,-1243,-1243, + -1243,-1243,-1243,-1243,-1243,-1243,-1243,-1243,-1243,-1243, + -1243,-1243,-1243,-1243,-1243,-1243,-1243,-1243,-1243,-1243, + -1243,-1243,-1243,-1243,-1243,-1243,-1243,-1243,-1243,-1243, + -1243,-1243,-1243,-1243,-1243,-1243,-1243,-1243,-1243,-1243, + -1243,-1243,-1243,-1243,-1243,-1243,-1243,-1243,-1243,-1243, + -1243,-1243,-1243,-1243,-1243,-1243,-1243,-1243,-1243,-1243, + -1243,-1243,-1243,-1243,-1243,-1243,-1243,-1243,-1243,-1243, + + -1243,-1243,-1243,-1243,-1243,-1243,-1243,-1243,-1243,-1243, + -1243,-1243,-1243,-1243,-1243,-1243,-1243,-1243 + }, + + { + 69,-1244,-1244,-1244,-1244,-1244,-1244,-1244,-1244,-1244, + -1244,-1244,-1244,-1244,-1244,-1244,-1244,-1244,-1244,-1244, + -1244,-1244,-1244,-1244,-1244,-1244,-1244,-1244,-1244,-1244, + -1244,-1244,-1244,-1244,-1244,-1244,-1244,-1244,-1244,-1244, + -1244,-1244,-1244,-1244,-1244,-1244,-1244,-1244,-1244,-1244, + -1244,-1244,-1244,-1244,-1244,-1244,-1244,-1244,-1244,-1244, + -1244,-1244,-1244,-1244,-1244,-1244,-1244,-1244,-1244,-1244, + -1244,-1244,-1244,-1244,-1244,-1244,-1244,-1244,-1244,-1244, + + -1244,-1244,-1244,-1244,-1244,-1244,-1244,-1244,-1244,-1244, + -1244,-1244,-1244,-1244,-1244,-1244,-1244,-1244,-1244,-1244, + -1244,-1244,-1244,-1244,-1244,-1244,-1244,-1244,-1244,-1244, + -1244,-1244,-1244,-1244,-1244,-1244,-1244,-1244,-1244,-1244, + -1244,-1244,-1244,-1244,-1244,-1244,-1244,-1244 + }, + + { + 69,-1245,-1245,-1245,-1245,-1245,-1245,-1245,-1245,-1245, + -1245,-1245,-1245,-1245,-1245,-1245,-1245,-1245,-1245,-1245, + -1245,-1245,-1245,-1245,-1245,-1245,-1245,-1245,-1245,-1245, + -1245,-1245,-1245,-1245,-1245,-1245,-1245,-1245,-1245,-1245, + -1245,-1245,-1245,-1245,-1245,-1245,-1245,-1245,-1245,-1245, + + -1245,-1245,-1245,-1245,-1245,-1245,-1245,-1245,-1245,-1245, + -1245,-1245,-1245,-1245,-1245,-1245,-1245,-1245,-1245,-1245, + -1245,-1245,-1245,-1245,-1245,-1245,-1245,-1245,-1245,-1245, + -1245,-1245,-1245,-1245,-1245,-1245,-1245,-1245,-1245,-1245, + -1245,-1245,-1245,-1245,-1245,-1245,-1245,-1245,-1245,-1245, + -1245,-1245,-1245,-1245,-1245,-1245,-1245,-1245,-1245,-1245, + -1245,-1245,-1245,-1245,-1245,-1245,-1245,-1245,-1245,-1245, + -1245,-1245,-1245,-1245,-1245,-1245,-1245,-1245 + }, + + { + 69,-1246,-1246,-1246,-1246,-1246,-1246,-1246,-1246,-1246, + -1246,-1246,-1246,-1246,-1246,-1246,-1246,-1246,-1246,-1246, + + -1246,-1246,-1246,-1246,-1246,-1246,-1246,-1246,-1246,-1246, + -1246,-1246,-1246,-1246,-1246,-1246,-1246,-1246,-1246,-1246, + -1246,-1246,-1246,-1246,-1246,-1246,-1246,-1246,-1246,-1246, + -1246,-1246,-1246,-1246,-1246,-1246,-1246,-1246,-1246,-1246, + -1246,-1246,-1246,-1246,-1246,-1246,-1246,-1246,-1246,-1246, + -1246,-1246,-1246,-1246,-1246,-1246,-1246,-1246,-1246,-1246, + -1246,-1246,-1246,-1246,-1246,-1246,-1246,-1246,-1246,-1246, + -1246,-1246,-1246,-1246,-1246,-1246,-1246,-1246,-1246,-1246, + -1246,-1246,-1246,-1246,-1246,-1246,-1246,-1246,-1246,-1246, + -1246,-1246,-1246,-1246,-1246,-1246,-1246,-1246,-1246,-1246, + + -1246,-1246,-1246,-1246,-1246,-1246,-1246,-1246 + }, + + { + 69,-1247,-1247,-1247,-1247,-1247,-1247,-1247,-1247,-1247, + -1247,-1247,-1247,-1247,-1247,-1247,-1247,-1247,-1247,-1247, + -1247,-1247,-1247,-1247,-1247,-1247,-1247,-1247,-1247,-1247, + -1247,-1247,-1247,-1247,-1247,-1247,-1247,-1247,-1247,-1247, + -1247,-1247,-1247,-1247,-1247,-1247,-1247,-1247,-1247,-1247, + -1247,-1247,-1247,-1247,-1247,-1247,-1247,-1247,-1247,-1247, + -1247,-1247,-1247,-1247,-1247,-1247,-1247,-1247,-1247,-1247, + -1247,-1247,-1247,-1247,-1247,-1247,-1247,-1247,-1247,-1247, + -1247,-1247,-1247,-1247,-1247,-1247,-1247,-1247,-1247,-1247, + + -1247,-1247,-1247,-1247,-1247,-1247,-1247,-1247,-1247,-1247, + -1247,-1247,-1247,-1247,-1247,-1247,-1247,-1247,-1247,-1247, + -1247,-1247,-1247,-1247,-1247,-1247,-1247,-1247,-1247,-1247, + -1247,-1247,-1247,-1247,-1247,-1247,-1247,-1247 + }, + + { + 69,-1248,-1248,-1248,-1248,-1248,-1248,-1248,-1248,-1248, + -1248,-1248,-1248,-1248,-1248,-1248,-1248,-1248,-1248,-1248, + -1248,-1248,-1248,-1248,-1248,-1248,-1248,-1248,-1248,-1248, + -1248,-1248,-1248,-1248,-1248,-1248,-1248,-1248,-1248,-1248, + -1248,-1248,-1248,-1248,-1248,-1248,-1248,-1248,-1248,-1248, + -1248,-1248,-1248,-1248,-1248,-1248,-1248,-1248,-1248,-1248, + + -1248,-1248,-1248,-1248,-1248,-1248,-1248,-1248,-1248,-1248, + -1248,-1248,-1248,-1248,-1248,-1248,-1248,-1248,-1248,-1248, + -1248,-1248,-1248,-1248,-1248,-1248,-1248,-1248,-1248,-1248, + -1248,-1248,-1248,-1248,-1248,-1248,-1248,-1248,-1248,-1248, + -1248,-1248,-1248,-1248,-1248,-1248,-1248,-1248,-1248,-1248, + -1248,-1248,-1248,-1248,-1248,-1248,-1248,-1248,-1248,-1248, + -1248,-1248,-1248,-1248,-1248,-1248,-1248,-1248 + }, + + { + 69,-1249,-1249,-1249,-1249,-1249,-1249,-1249,-1249,-1249, + -1249,-1249,-1249,-1249,-1249,-1249,-1249,-1249,-1249,-1249, + -1249,-1249,-1249,-1249,-1249,-1249,-1249,-1249,-1249,-1249, + + -1249,-1249,-1249,-1249,-1249,-1249,-1249,-1249,-1249,-1249, + -1249,-1249,-1249,-1249,-1249,-1249,-1249,-1249,-1249,-1249, + -1249,-1249,-1249,-1249,-1249,-1249,-1249,-1249,-1249,-1249, + -1249,-1249,-1249,-1249,-1249,-1249,-1249,-1249,-1249,-1249, + -1249,-1249,-1249,-1249,-1249,-1249,-1249,-1249,-1249,-1249, + -1249,-1249,-1249,-1249,-1249,-1249,-1249,-1249,-1249,-1249, + -1249,-1249,-1249,-1249,-1249,-1249,-1249,-1249,-1249,-1249, + -1249,-1249,-1249,-1249,-1249,-1249,-1249,-1249,-1249,-1249, + -1249,-1249,-1249,-1249,-1249,-1249,-1249,-1249,-1249,-1249, + -1249,-1249,-1249,-1249,-1249,-1249,-1249,-1249 + + }, + + { + 69,-1250,-1250,-1250,-1250,-1250,-1250,-1250,-1250,-1250, + -1250,-1250,-1250,-1250,-1250,-1250,-1250,-1250,-1250,-1250, + -1250,-1250,-1250,-1250,-1250,-1250,-1250,-1250,-1250,-1250, + -1250,-1250,-1250,-1250,-1250,-1250,-1250,-1250,-1250,-1250, + -1250,-1250,-1250,-1250,-1250,-1250,-1250,-1250,-1250,-1250, + -1250,-1250,-1250,-1250,-1250,-1250,-1250,-1250,-1250,-1250, + -1250,-1250,-1250,-1250,-1250,-1250,-1250,-1250,-1250,-1250, + -1250,-1250,-1250,-1250,-1250,-1250,-1250,-1250,-1250,-1250, + -1250,-1250,-1250,-1250,-1250,-1250,-1250,-1250,-1250,-1250, + -1250,-1250,-1250,-1250,-1250,-1250,-1250,-1250,-1250,-1250, + + -1250,-1250,-1250,-1250,-1250,-1250,-1250,-1250,-1250,-1250, + -1250,-1250,-1250,-1250,-1250,-1250,-1250,-1250,-1250,-1250, + -1250,-1250,-1250,-1250,-1250,-1250,-1250,-1250 + }, + + { + 69,-1251,-1251,-1251,-1251,-1251,-1251,-1251,-1251,-1251, + -1251,-1251,-1251,-1251,-1251,-1251,-1251,-1251,-1251,-1251, + -1251,-1251,-1251,-1251,-1251,-1251,-1251,-1251,-1251,-1251, + -1251,-1251,-1251,-1251,-1251,-1251,-1251,-1251,-1251,-1251, + -1251,-1251,-1251,-1251,-1251,-1251,-1251,-1251,-1251,-1251, + -1251,-1251,-1251,-1251,-1251,-1251,-1251,-1251,-1251,-1251, + -1251,-1251,-1251,-1251,-1251,-1251,-1251,-1251,-1251,-1251, + + -1251,-1251,-1251,-1251,-1251,-1251,-1251,-1251,-1251,-1251, + -1251,-1251,-1251,-1251,-1251,-1251,-1251,-1251,-1251,-1251, + -1251,-1251,-1251,-1251,-1251,-1251,-1251,-1251,-1251,-1251, + -1251,-1251,-1251,-1251,-1251,-1251,-1251,-1251,-1251,-1251, + -1251,-1251,-1251,-1251,-1251,-1251,-1251,-1251,-1251,-1251, + -1251,-1251,-1251,-1251,-1251,-1251,-1251,-1251 + }, + + { + 69,-1252,-1252,-1252,-1252,-1252,-1252,-1252,-1252,-1252, + -1252,-1252,-1252,-1252,-1252,-1252,-1252,-1252,-1252,-1252, + -1252,-1252,-1252,-1252,-1252,-1252,-1252,-1252,-1252,-1252, + -1252,-1252,-1252,-1252,-1252,-1252,-1252,-1252,-1252,-1252, + + -1252,-1252,-1252,-1252,-1252,-1252,-1252,-1252,-1252,-1252, + -1252,-1252,-1252,-1252,-1252,-1252,-1252,-1252,-1252,-1252, + -1252,-1252,-1252,-1252,-1252,-1252,-1252,-1252,-1252,-1252, + -1252,-1252,-1252,-1252,-1252,-1252,-1252,-1252,-1252,-1252, + -1252,-1252,-1252,-1252,-1252,-1252,-1252,-1252,-1252,-1252, + -1252,-1252,-1252,-1252,-1252,-1252,-1252,-1252,-1252,-1252, + -1252,-1252,-1252,-1252,-1252,-1252,-1252,-1252,-1252,-1252, + -1252,-1252,-1252,-1252,-1252,-1252,-1252,-1252,-1252,-1252, + -1252,-1252,-1252,-1252,-1252,-1252,-1252,-1252 + }, + + { + 69,-1253,-1253,-1253,-1253,-1253,-1253,-1253,-1253,-1253, + + -1253,-1253,-1253,-1253,-1253,-1253,-1253,-1253,-1253,-1253, + -1253,-1253,-1253,-1253,-1253,-1253,-1253,-1253,-1253,-1253, + -1253,-1253,-1253,-1253,-1253,-1253,-1253,-1253,-1253,-1253, + -1253,-1253,-1253,-1253,-1253,-1253,-1253,-1253,-1253,-1253, + -1253,-1253,-1253,-1253,-1253,-1253,-1253,-1253,-1253,-1253, + -1253,-1253,-1253,-1253,-1253,-1253,-1253,-1253,-1253,-1253, + -1253,-1253,-1253,-1253,-1253,-1253,-1253,-1253,-1253,-1253, + -1253,-1253,-1253,-1253,-1253,-1253,-1253,-1253,-1253,-1253, + -1253,-1253,-1253,-1253,-1253,-1253,-1253,-1253,-1253,-1253, + -1253,-1253,-1253,-1253,-1253,-1253,-1253,-1253,-1253,-1253, + + -1253,-1253,-1253,-1253,-1253,-1253,-1253,-1253,-1253,-1253, + -1253,-1253,-1253,-1253,-1253,-1253,-1253,-1253 + }, + + { + 69,-1254,-1254,-1254,-1254,-1254,-1254,-1254,-1254,-1254, + -1254,-1254,-1254,-1254,-1254,-1254,-1254,-1254,-1254,-1254, + -1254,-1254,-1254,-1254,-1254,-1254,-1254,-1254,-1254,-1254, + -1254,-1254,-1254,-1254,-1254,-1254,-1254,-1254,-1254,-1254, + -1254,-1254,-1254,-1254,-1254,-1254,-1254,-1254,-1254,-1254, + -1254,-1254,-1254,-1254,-1254,-1254,-1254,-1254,-1254,-1254, + -1254,-1254,-1254,-1254,-1254,-1254,-1254,-1254,-1254,-1254, + -1254,-1254,-1254,-1254,-1254,-1254,-1254,-1254,-1254,-1254, + + -1254,-1254,-1254,-1254,-1254,-1254,-1254,-1254,-1254,-1254, + -1254,-1254,-1254,-1254,-1254,-1254,-1254,-1254,-1254,-1254, + -1254,-1254,-1254,-1254,-1254,-1254,-1254,-1254,-1254,-1254, + -1254,-1254,-1254,-1254,-1254,-1254,-1254,-1254,-1254,-1254, + -1254,-1254,-1254,-1254,-1254,-1254,-1254,-1254 + }, + + { + 69,-1255,-1255,-1255,-1255,-1255,-1255,-1255,-1255,-1255, + -1255,-1255,-1255,-1255,-1255,-1255,-1255,-1255,-1255,-1255, + -1255,-1255,-1255,-1255,-1255,-1255,-1255,-1255,-1255,-1255, + -1255,-1255,-1255,-1255,-1255,-1255,-1255,-1255,-1255,-1255, + -1255,-1255,-1255,-1255,-1255,-1255,-1255,-1255,-1255,-1255, + + -1255,-1255,-1255,-1255,-1255,-1255,-1255,-1255,-1255,-1255, + -1255,-1255,-1255,-1255,-1255,-1255,-1255,-1255,-1255,-1255, + -1255,-1255,-1255,-1255,-1255,-1255,-1255,-1255,-1255,-1255, + -1255,-1255,-1255,-1255,-1255,-1255,-1255,-1255,-1255,-1255, + -1255,-1255,-1255,-1255,-1255,-1255,-1255,-1255,-1255,-1255, + -1255,-1255,-1255,-1255,-1255,-1255,-1255,-1255,-1255,-1255, + -1255,-1255,-1255,-1255,-1255,-1255,-1255,-1255,-1255,-1255, + -1255,-1255,-1255,-1255,-1255,-1255,-1255,-1255 + }, + + { + 69,-1256,-1256,-1256,-1256,-1256,-1256,-1256,-1256,-1256, + -1256,-1256,-1256,-1256,-1256,-1256,-1256,-1256,-1256,-1256, + + -1256,-1256,-1256,-1256,-1256,-1256,-1256,-1256,-1256,-1256, + -1256,-1256,-1256,-1256,-1256,-1256,-1256,-1256,-1256,-1256, + -1256,-1256,-1256,-1256,-1256,-1256,-1256,-1256,-1256,-1256, + -1256,-1256,-1256,-1256,-1256,-1256,-1256,-1256,-1256,-1256, + -1256,-1256,-1256,-1256,-1256,-1256,-1256,-1256,-1256,-1256, + -1256,-1256,-1256,-1256,-1256,-1256,-1256,-1256,-1256,-1256, + -1256,-1256,-1256,-1256,-1256,-1256,-1256,-1256,-1256,-1256, + -1256,-1256,-1256,-1256,-1256,-1256,-1256,-1256,-1256,-1256, + -1256,-1256,-1256,-1256,-1256,-1256,-1256,-1256,-1256,-1256, + -1256,-1256,-1256,-1256,-1256,-1256,-1256,-1256,-1256,-1256, + + -1256,-1256,-1256,-1256,-1256,-1256,-1256,-1256 + }, + + { + 69,-1257,-1257,-1257,-1257,-1257,-1257,-1257,-1257,-1257, + -1257,-1257,-1257,-1257,-1257,-1257,-1257,-1257,-1257,-1257, + -1257,-1257,-1257,-1257,-1257,-1257,-1257,-1257,-1257,-1257, + -1257,-1257, 1325,-1257,-1257,-1257,-1257,-1257,-1257,-1257, + -1257,-1257,-1257,-1257,-1257,-1257,-1257,-1257,-1257,-1257, + -1257,-1257,-1257,-1257,-1257,-1257,-1257,-1257,-1257,-1257, + -1257,-1257,-1257,-1257,-1257,-1257,-1257,-1257,-1257,-1257, + -1257,-1257,-1257,-1257,-1257,-1257,-1257,-1257,-1257,-1257, + -1257,-1257,-1257,-1257,-1257,-1257,-1257,-1257,-1257,-1257, + + -1257,-1257,-1257,-1257,-1257,-1257,-1257,-1257,-1257,-1257, + -1257,-1257,-1257,-1257,-1257,-1257,-1257,-1257,-1257,-1257, + -1257,-1257,-1257,-1257,-1257,-1257,-1257,-1257,-1257,-1257, + -1257,-1257,-1257,-1257,-1257,-1257,-1257,-1257 + }, + + { + 69,-1258,-1258,-1258,-1258,-1258,-1258,-1258,-1258,-1258, + -1258,-1258,-1258,-1258,-1258,-1258,-1258,-1258,-1258,-1258, + -1258,-1258,-1258,-1258,-1258,-1258,-1258,-1258,-1258,-1258, + -1258,-1258,-1258,-1258,-1258,-1258,-1258,-1258,-1258,-1258, + -1258,-1258,-1258,-1258,-1258,-1258,-1258,-1258,-1258,-1258, + -1258,-1258,-1258,-1258,-1258,-1258,-1258,-1258,-1258,-1258, + + -1258,-1258,-1258,-1258,-1258,-1258,-1258,-1258,-1258,-1258, + -1258,-1258,-1258,-1258,-1258,-1258,-1258,-1258,-1258,-1258, + -1258,-1258,-1258,-1258,-1258,-1258,-1258,-1258,-1258,-1258, + -1258,-1258,-1258,-1258,-1258,-1258,-1258,-1258,-1258,-1258, + -1258,-1258,-1258,-1258,-1258,-1258,-1258,-1258,-1258,-1258, + -1258,-1258,-1258,-1258,-1258,-1258,-1258,-1258,-1258,-1258, + -1258,-1258,-1258,-1258,-1258,-1258,-1258,-1258 + }, + + { + 69,-1259,-1259,-1259,-1259,-1259,-1259,-1259,-1259,-1259, + -1259,-1259,-1259,-1259,-1259,-1259,-1259,-1259,-1259,-1259, + -1259,-1259,-1259,-1259,-1259,-1259,-1259,-1259,-1259,-1259, + + -1259,-1259,-1259,-1259,-1259,-1259,-1259,-1259,-1259,-1259, + -1259,-1259,-1259,-1259,-1259,-1259,-1259,-1259,-1259,-1259, + -1259,-1259,-1259,-1259,-1259,-1259,-1259,-1259,-1259,-1259, + -1259,-1259,-1259,-1259,-1259,-1259,-1259,-1259,-1259,-1259, + -1259,-1259,-1259,-1259,-1259,-1259,-1259,-1259,-1259,-1259, + -1259,-1259,-1259,-1259,-1259,-1259,-1259,-1259,-1259,-1259, + -1259,-1259,-1259,-1259,-1259,-1259,-1259,-1259,-1259,-1259, + -1259,-1259,-1259,-1259,-1259,-1259,-1259,-1259,-1259,-1259, + -1259,-1259,-1259,-1259,-1259,-1259,-1259,-1259,-1259,-1259, + -1259,-1259,-1259,-1259,-1259,-1259,-1259,-1259 + + }, + + { + 69,-1260,-1260,-1260,-1260,-1260,-1260,-1260,-1260,-1260, + -1260,-1260,-1260,-1260,-1260,-1260,-1260,-1260,-1260,-1260, + -1260,-1260,-1260,-1260,-1260,-1260,-1260,-1260,-1260,-1260, + -1260,-1260,-1260,-1260,-1260,-1260,-1260,-1260,-1260,-1260, + -1260,-1260,-1260,-1260,-1260,-1260,-1260,-1260,-1260,-1260, + -1260,-1260,-1260,-1260,-1260,-1260,-1260,-1260,-1260,-1260, + -1260,-1260,-1260,-1260,-1260,-1260,-1260,-1260,-1260,-1260, + -1260,-1260,-1260,-1260,-1260,-1260,-1260,-1260,-1260,-1260, + -1260,-1260,-1260,-1260,-1260,-1260,-1260,-1260,-1260,-1260, + -1260,-1260,-1260,-1260,-1260,-1260,-1260,-1260,-1260,-1260, + + -1260,-1260,-1260,-1260,-1260,-1260,-1260,-1260,-1260,-1260, + -1260,-1260,-1260,-1260,-1260,-1260,-1260,-1260,-1260,-1260, + -1260,-1260,-1260,-1260,-1260,-1260,-1260,-1260 + }, + + { + 69,-1261,-1261,-1261,-1261,-1261,-1261,-1261,-1261,-1261, + -1261,-1261,-1261,-1261,-1261,-1261,-1261,-1261,-1261,-1261, + -1261,-1261,-1261,-1261,-1261,-1261,-1261,-1261,-1261,-1261, + -1261,-1261,-1261,-1261,-1261,-1261,-1261,-1261,-1261,-1261, + -1261,-1261,-1261,-1261,-1261,-1261,-1261,-1261,-1261,-1261, + -1261,-1261,-1261,-1261,-1261,-1261,-1261,-1261,-1261,-1261, + -1261,-1261,-1261,-1261,-1261,-1261,-1261,-1261,-1261,-1261, + + -1261,-1261,-1261,-1261,-1261,-1261,-1261,-1261,-1261,-1261, + -1261,-1261,-1261,-1261,-1261,-1261,-1261,-1261,-1261,-1261, + -1261,-1261,-1261,-1261,-1261,-1261,-1261,-1261,-1261,-1261, + -1261,-1261,-1261,-1261,-1261,-1261,-1261,-1261,-1261,-1261, + -1261,-1261,-1261,-1261,-1261,-1261,-1261,-1261,-1261,-1261, + -1261,-1261,-1261,-1261,-1261,-1261,-1261,-1261 + }, + + { + 69,-1262,-1262,-1262,-1262,-1262,-1262,-1262,-1262,-1262, + -1262,-1262,-1262,-1262,-1262,-1262,-1262,-1262,-1262,-1262, + -1262,-1262,-1262,-1262,-1262,-1262,-1262,-1262,-1262,-1262, + -1262,-1262,-1262,-1262,-1262,-1262,-1262,-1262,-1262,-1262, + + -1262,-1262,-1262,-1262,-1262,-1262,-1262,-1262,-1262,-1262, + -1262,-1262,-1262,-1262,-1262,-1262,-1262,-1262,-1262,-1262, + -1262,-1262,-1262,-1262,-1262,-1262,-1262,-1262,-1262,-1262, + -1262,-1262,-1262,-1262,-1262,-1262,-1262,-1262,-1262,-1262, + -1262,-1262,-1262,-1262,-1262,-1262,-1262,-1262,-1262,-1262, + -1262,-1262,-1262,-1262,-1262,-1262,-1262,-1262,-1262,-1262, + -1262,-1262,-1262,-1262,-1262,-1262,-1262,-1262,-1262,-1262, + -1262,-1262,-1262,-1262,-1262,-1262,-1262,-1262,-1262,-1262, + -1262,-1262,-1262,-1262,-1262,-1262,-1262,-1262 + }, + + { + 69,-1263,-1263,-1263,-1263,-1263,-1263,-1263,-1263,-1263, + + -1263,-1263,-1263,-1263,-1263,-1263,-1263,-1263,-1263,-1263, + -1263,-1263,-1263,-1263,-1263,-1263,-1263,-1263,-1263,-1263, + -1263,-1263,-1263,-1263,-1263,-1263,-1263,-1263,-1263,-1263, + -1263,-1263,-1263,-1263,-1263,-1263,-1263,-1263,-1263,-1263, + -1263,-1263,-1263,-1263,-1263,-1263,-1263,-1263,-1263,-1263, + -1263,-1263,-1263,-1263,-1263,-1263,-1263,-1263,-1263,-1263, + -1263,-1263,-1263,-1263,-1263,-1263,-1263,-1263,-1263,-1263, + -1263,-1263,-1263,-1263,-1263,-1263,-1263,-1263,-1263,-1263, + -1263,-1263,-1263,-1263,-1263,-1263,-1263,-1263,-1263,-1263, + -1263,-1263,-1263,-1263,-1263,-1263,-1263,-1263,-1263,-1263, + + -1263,-1263,-1263,-1263,-1263,-1263,-1263,-1263,-1263,-1263, + -1263,-1263,-1263,-1263,-1263,-1263,-1263,-1263 + }, + + { + 69,-1264,-1264,-1264,-1264,-1264,-1264,-1264,-1264,-1264, + -1264,-1264,-1264,-1264,-1264,-1264,-1264,-1264,-1264,-1264, + -1264,-1264,-1264,-1264,-1264,-1264,-1264,-1264,-1264,-1264, + -1264,-1264,-1264,-1264,-1264,-1264,-1264,-1264,-1264,-1264, + -1264,-1264,-1264,-1264,-1264,-1264,-1264,-1264,-1264,-1264, + -1264,-1264,-1264,-1264,-1264,-1264,-1264,-1264,-1264,-1264, + -1264,-1264,-1264,-1264,-1264,-1264,-1264,-1264,-1264,-1264, + -1264,-1264,-1264,-1264,-1264,-1264,-1264,-1264,-1264,-1264, + + -1264,-1264,-1264,-1264,-1264,-1264,-1264,-1264,-1264,-1264, + -1264,-1264,-1264,-1264,-1264,-1264,-1264,-1264,-1264,-1264, + -1264,-1264,-1264,-1264,-1264,-1264,-1264,-1264,-1264,-1264, + -1264,-1264,-1264,-1264,-1264,-1264,-1264,-1264,-1264,-1264, + -1264,-1264,-1264,-1264,-1264,-1264,-1264,-1264 + }, + + { + 69,-1265,-1265,-1265,-1265,-1265,-1265,-1265,-1265,-1265, + -1265,-1265,-1265,-1265,-1265,-1265,-1265,-1265,-1265,-1265, + -1265,-1265,-1265,-1265,-1265,-1265,-1265,-1265,-1265,-1265, + -1265,-1265,-1265,-1265,-1265,-1265,-1265,-1265,-1265,-1265, + -1265,-1265,-1265,-1265,-1265,-1265,-1265,-1265,-1265,-1265, + + -1265,-1265,-1265,-1265,-1265,-1265,-1265,-1265,-1265,-1265, + -1265,-1265,-1265,-1265,-1265,-1265,-1265,-1265,-1265,-1265, + -1265,-1265,-1265,-1265,-1265,-1265,-1265,-1265,-1265,-1265, + -1265,-1265,-1265,-1265,-1265,-1265,-1265,-1265,-1265,-1265, + -1265,-1265,-1265,-1265,-1265,-1265,-1265,-1265,-1265,-1265, + -1265,-1265,-1265,-1265,-1265,-1265,-1265,-1265,-1265,-1265, + -1265,-1265,-1265,-1265,-1265,-1265,-1265,-1265,-1265,-1265, + -1265,-1265,-1265,-1265,-1265,-1265,-1265,-1265 + }, + + { + 69,-1266,-1266,-1266,-1266,-1266,-1266,-1266,-1266,-1266, + -1266,-1266,-1266,-1266,-1266,-1266,-1266,-1266,-1266,-1266, + + -1266,-1266,-1266,-1266,-1266,-1266,-1266,-1266,-1266,-1266, + -1266,-1266,-1266,-1266,-1266,-1266,-1266,-1266,-1266,-1266, + -1266,-1266,-1266,-1266,-1266,-1266,-1266,-1266,-1266,-1266, + -1266,-1266,-1266,-1266,-1266,-1266,-1266,-1266,-1266,-1266, + -1266,-1266,-1266,-1266,-1266,-1266,-1266,-1266,-1266,-1266, + -1266,-1266,-1266,-1266,-1266,-1266,-1266,-1266,-1266,-1266, + -1266,-1266,-1266,-1266,-1266,-1266,-1266,-1266,-1266,-1266, + -1266,-1266,-1266,-1266,-1266,-1266,-1266,-1266,-1266,-1266, + -1266,-1266,-1266,-1266,-1266,-1266,-1266,-1266,-1266,-1266, + -1266,-1266,-1266,-1266,-1266,-1266,-1266,-1266,-1266,-1266, + + -1266,-1266,-1266,-1266,-1266,-1266,-1266,-1266 + }, + + { + 69,-1267,-1267,-1267,-1267,-1267,-1267,-1267,-1267,-1267, + -1267,-1267,-1267,-1267,-1267,-1267,-1267,-1267,-1267,-1267, + -1267,-1267,-1267,-1267,-1267,-1267,-1267,-1267,-1267,-1267, + -1267,-1267,-1267,-1267,-1267,-1267,-1267,-1267,-1267,-1267, + -1267,-1267,-1267,-1267,-1267,-1267,-1267,-1267,-1267,-1267, + -1267,-1267,-1267,-1267,-1267,-1267,-1267,-1267,-1267,-1267, + -1267,-1267,-1267,-1267,-1267,-1267,-1267,-1267,-1267,-1267, + -1267,-1267,-1267,-1267,-1267,-1267,-1267,-1267,-1267,-1267, + -1267,-1267,-1267,-1267,-1267,-1267,-1267,-1267,-1267,-1267, + + -1267,-1267,-1267,-1267,-1267,-1267,-1267,-1267,-1267,-1267, + -1267,-1267,-1267,-1267,-1267,-1267,-1267,-1267,-1267,-1267, + -1267,-1267,-1267,-1267,-1267,-1267,-1267,-1267,-1267,-1267, + -1267,-1267,-1267,-1267,-1267,-1267,-1267,-1267 + }, + + { + 69,-1268,-1268,-1268,-1268,-1268,-1268,-1268,-1268,-1268, + -1268,-1268,-1268,-1268,-1268,-1268,-1268,-1268,-1268,-1268, + -1268,-1268,-1268,-1268,-1268,-1268,-1268,-1268,-1268,-1268, + -1268,-1268,-1268,-1268,-1268,-1268,-1268,-1268,-1268,-1268, + -1268,-1268,-1268,-1268,-1268,-1268,-1268,-1268,-1268,-1268, + -1268,-1268,-1268,-1268,-1268,-1268,-1268,-1268,-1268,-1268, + + -1268,-1268,-1268,-1268,-1268,-1268,-1268,-1268,-1268,-1268, + -1268,-1268,-1268,-1268,-1268,-1268,-1268,-1268,-1268,-1268, + -1268,-1268,-1268,-1268,-1268,-1268,-1268,-1268,-1268,-1268, + -1268,-1268,-1268,-1268,-1268,-1268,-1268,-1268,-1268,-1268, + -1268,-1268,-1268,-1268,-1268,-1268,-1268,-1268,-1268,-1268, + -1268,-1268,-1268,-1268,-1268,-1268,-1268,-1268,-1268,-1268, + -1268,-1268,-1268,-1268,-1268,-1268,-1268,-1268 + }, + + { + 69,-1269,-1269,-1269,-1269,-1269,-1269,-1269,-1269,-1269, + -1269,-1269,-1269,-1269,-1269,-1269,-1269,-1269,-1269,-1269, + -1269,-1269,-1269,-1269,-1269,-1269,-1269,-1269,-1269,-1269, + + -1269,-1269,-1269,-1269,-1269,-1269,-1269,-1269,-1269,-1269, + -1269,-1269,-1269,-1269,-1269,-1269,-1269,-1269,-1269,-1269, + -1269,-1269,-1269,-1269,-1269,-1269,-1269,-1269,-1269,-1269, + -1269,-1269,-1269,-1269,-1269,-1269,-1269,-1269,-1269,-1269, + -1269,-1269,-1269,-1269,-1269,-1269,-1269,-1269,-1269,-1269, + -1269,-1269,-1269,-1269,-1269,-1269,-1269,-1269,-1269,-1269, + -1269,-1269,-1269,-1269,-1269,-1269,-1269,-1269,-1269,-1269, + -1269,-1269,-1269,-1269,-1269,-1269,-1269,-1269,-1269,-1269, + -1269,-1269,-1269,-1269,-1269,-1269,-1269,-1269,-1269,-1269, + -1269,-1269,-1269,-1269,-1269,-1269,-1269,-1269 + + }, + + { + 69,-1270,-1270,-1270,-1270,-1270,-1270,-1270,-1270,-1270, + -1270,-1270,-1270,-1270,-1270,-1270,-1270,-1270,-1270,-1270, + -1270,-1270,-1270,-1270,-1270,-1270,-1270,-1270,-1270,-1270, + -1270,-1270,-1270,-1270,-1270,-1270,-1270,-1270,-1270,-1270, + -1270,-1270,-1270,-1270,-1270,-1270,-1270,-1270,-1270,-1270, + -1270,-1270,-1270,-1270,-1270,-1270,-1270,-1270,-1270,-1270, + -1270,-1270,-1270,-1270,-1270,-1270,-1270,-1270,-1270,-1270, + -1270,-1270,-1270,-1270,-1270,-1270,-1270,-1270,-1270,-1270, + -1270,-1270,-1270,-1270,-1270,-1270,-1270,-1270,-1270,-1270, + -1270,-1270,-1270,-1270,-1270,-1270,-1270,-1270,-1270,-1270, + + -1270,-1270,-1270,-1270,-1270,-1270,-1270,-1270,-1270,-1270, + -1270,-1270,-1270,-1270,-1270,-1270,-1270,-1270,-1270,-1270, + -1270,-1270,-1270,-1270,-1270,-1270,-1270,-1270 + }, + + { + 69,-1271,-1271,-1271,-1271,-1271,-1271,-1271,-1271,-1271, + -1271,-1271,-1271,-1271,-1271,-1271,-1271,-1271,-1271,-1271, + -1271,-1271,-1271,-1271,-1271,-1271,-1271,-1271,-1271,-1271, + -1271,-1271,-1271,-1271,-1271,-1271,-1271,-1271,-1271,-1271, + -1271,-1271,-1271,-1271,-1271,-1271,-1271,-1271,-1271,-1271, + -1271,-1271,-1271,-1271,-1271,-1271,-1271,-1271,-1271,-1271, + -1271,-1271,-1271,-1271,-1271,-1271,-1271,-1271,-1271,-1271, + + -1271,-1271,-1271,-1271,-1271,-1271,-1271,-1271,-1271,-1271, + -1271,-1271,-1271,-1271,-1271,-1271,-1271,-1271,-1271,-1271, + -1271,-1271,-1271,-1271,-1271,-1271,-1271,-1271,-1271,-1271, + -1271,-1271,-1271,-1271,-1271,-1271,-1271,-1271,-1271,-1271, + -1271,-1271,-1271,-1271,-1271,-1271,-1271,-1271,-1271,-1271, + -1271,-1271,-1271,-1271,-1271,-1271,-1271,-1271 + }, + + { + 69,-1272,-1272,-1272,-1272,-1272,-1272,-1272,-1272,-1272, + -1272,-1272,-1272,-1272,-1272,-1272,-1272,-1272,-1272,-1272, + -1272,-1272,-1272,-1272,-1272,-1272,-1272,-1272,-1272,-1272, + -1272,-1272,-1272,-1272,-1272,-1272,-1272,-1272,-1272,-1272, + + -1272,-1272,-1272,-1272,-1272,-1272,-1272,-1272,-1272,-1272, + -1272,-1272,-1272,-1272,-1272,-1272,-1272,-1272,-1272,-1272, + -1272,-1272,-1272,-1272,-1272,-1272,-1272,-1272,-1272,-1272, + -1272,-1272,-1272,-1272,-1272,-1272,-1272,-1272,-1272,-1272, + -1272,-1272,-1272,-1272,-1272,-1272,-1272,-1272,-1272,-1272, + -1272,-1272,-1272,-1272,-1272,-1272,-1272,-1272,-1272,-1272, + -1272,-1272,-1272,-1272,-1272,-1272,-1272,-1272,-1272,-1272, + -1272,-1272,-1272,-1272,-1272,-1272,-1272,-1272,-1272,-1272, + -1272,-1272,-1272,-1272,-1272,-1272,-1272,-1272 + }, + + { + 69,-1273,-1273,-1273,-1273,-1273,-1273,-1273,-1273,-1273, + + -1273,-1273,-1273,-1273,-1273,-1273,-1273,-1273,-1273,-1273, + -1273,-1273,-1273,-1273,-1273,-1273,-1273,-1273,-1273,-1273, + -1273,-1273,-1273,-1273,-1273,-1273,-1273,-1273,-1273,-1273, + -1273,-1273,-1273,-1273,-1273,-1273,-1273,-1273,-1273,-1273, + -1273,-1273,-1273,-1273,-1273,-1273,-1273,-1273,-1273,-1273, + -1273,-1273,-1273,-1273,-1273,-1273,-1273,-1273,-1273,-1273, + -1273,-1273,-1273,-1273,-1273,-1273,-1273,-1273,-1273,-1273, + -1273,-1273,-1273,-1273,-1273,-1273,-1273,-1273,-1273,-1273, + -1273,-1273,-1273,-1273,-1273,-1273,-1273,-1273,-1273,-1273, + -1273,-1273,-1273,-1273,-1273,-1273,-1273,-1273,-1273,-1273, + + -1273,-1273,-1273,-1273,-1273,-1273,-1273,-1273,-1273,-1273, + -1273,-1273,-1273,-1273,-1273,-1273,-1273,-1273 + }, + + { + 69,-1274,-1274,-1274,-1274,-1274,-1274,-1274,-1274,-1274, + -1274,-1274,-1274,-1274,-1274,-1274,-1274,-1274,-1274,-1274, + -1274,-1274,-1274,-1274,-1274,-1274,-1274,-1274,-1274,-1274, + -1274,-1274,-1274,-1274,-1274,-1274,-1274,-1274,-1274,-1274, + -1274,-1274,-1274,-1274,-1274,-1274,-1274,-1274,-1274,-1274, + -1274,-1274,-1274,-1274,-1274,-1274,-1274,-1274,-1274,-1274, + -1274,-1274,-1274,-1274,-1274,-1274,-1274,-1274,-1274,-1274, + -1274,-1274,-1274,-1274,-1274,-1274,-1274,-1274,-1274,-1274, + + -1274,-1274,-1274,-1274,-1274,-1274,-1274,-1274,-1274,-1274, + -1274,-1274,-1274,-1274,-1274,-1274,-1274,-1274,-1274,-1274, + -1274,-1274,-1274,-1274,-1274,-1274,-1274,-1274,-1274,-1274, + -1274,-1274,-1274,-1274,-1274,-1274,-1274,-1274,-1274,-1274, + -1274,-1274,-1274,-1274,-1274,-1274,-1274,-1274 + }, + + { + 69,-1275,-1275,-1275,-1275,-1275,-1275,-1275,-1275,-1275, + -1275,-1275,-1275,-1275,-1275,-1275,-1275,-1275,-1275,-1275, + -1275,-1275,-1275,-1275,-1275,-1275,-1275,-1275,-1275,-1275, + -1275,-1275,-1275,-1275,-1275,-1275,-1275,-1275,-1275,-1275, + -1275,-1275,-1275,-1275,-1275,-1275,-1275,-1275,-1275,-1275, + + -1275,-1275,-1275,-1275,-1275,-1275,-1275,-1275,-1275,-1275, + -1275,-1275,-1275,-1275,-1275,-1275,-1275,-1275,-1275,-1275, + -1275,-1275,-1275,-1275,-1275,-1275,-1275,-1275,-1275,-1275, + -1275,-1275,-1275,-1275,-1275,-1275,-1275,-1275,-1275,-1275, + -1275,-1275,-1275,-1275,-1275,-1275,-1275,-1275,-1275,-1275, + -1275,-1275,-1275,-1275,-1275,-1275,-1275,-1275,-1275,-1275, + -1275,-1275,-1275,-1275,-1275,-1275,-1275,-1275,-1275,-1275, + -1275,-1275,-1275,-1275,-1275,-1275,-1275,-1275 + }, + + { + 69,-1276,-1276,-1276,-1276,-1276,-1276,-1276,-1276,-1276, + -1276,-1276,-1276,-1276,-1276,-1276,-1276,-1276,-1276,-1276, + + -1276,-1276,-1276,-1276,-1276,-1276,-1276,-1276,-1276,-1276, + -1276,-1276,-1276,-1276,-1276,-1276,-1276,-1276,-1276,-1276, + -1276,-1276,-1276,-1276,-1276,-1276,-1276,-1276,-1276,-1276, + -1276,-1276,-1276,-1276,-1276,-1276,-1276,-1276,-1276,-1276, + -1276,-1276,-1276,-1276,-1276,-1276,-1276,-1276,-1276,-1276, + -1276,-1276,-1276,-1276,-1276,-1276,-1276,-1276,-1276,-1276, + -1276,-1276,-1276,-1276,-1276,-1276,-1276,-1276,-1276,-1276, + -1276,-1276,-1276,-1276,-1276,-1276,-1276,-1276,-1276,-1276, + -1276,-1276,-1276,-1276,-1276,-1276,-1276,-1276,-1276,-1276, + -1276,-1276,-1276,-1276,-1276,-1276,-1276,-1276,-1276,-1276, + + -1276,-1276,-1276,-1276,-1276,-1276,-1276,-1276 + }, + + { + 69,-1277,-1277,-1277,-1277,-1277,-1277,-1277,-1277,-1277, + -1277,-1277,-1277,-1277,-1277,-1277,-1277,-1277,-1277,-1277, + -1277,-1277,-1277,-1277,-1277,-1277,-1277,-1277,-1277,-1277, + -1277,-1277,-1277,-1277,-1277,-1277,-1277,-1277,-1277,-1277, + -1277,-1277,-1277,-1277,-1277,-1277,-1277,-1277,-1277,-1277, + -1277,-1277,-1277,-1277,-1277,-1277,-1277,-1277,-1277,-1277, + -1277,-1277,-1277,-1277,-1277,-1277,-1277,-1277,-1277,-1277, + -1277,-1277,-1277,-1277,-1277,-1277,-1277,-1277,-1277,-1277, + -1277,-1277,-1277,-1277,-1277,-1277,-1277,-1277,-1277,-1277, + + -1277,-1277,-1277,-1277,-1277,-1277,-1277,-1277,-1277,-1277, + -1277,-1277,-1277,-1277,-1277,-1277,-1277,-1277,-1277,-1277, + -1277,-1277,-1277,-1277,-1277,-1277,-1277,-1277,-1277,-1277, + -1277,-1277,-1277,-1277,-1277,-1277,-1277,-1277 + }, + + { + 69,-1278,-1278,-1278,-1278,-1278,-1278,-1278,-1278,-1278, + -1278,-1278,-1278,-1278,-1278,-1278,-1278,-1278,-1278,-1278, + -1278,-1278,-1278,-1278,-1278,-1278,-1278,-1278,-1278,-1278, + -1278,-1278,-1278,-1278,-1278,-1278,-1278,-1278,-1278,-1278, + -1278,-1278,-1278,-1278,-1278,-1278,-1278,-1278,-1278,-1278, + -1278,-1278,-1278,-1278,-1278,-1278,-1278,-1278,-1278,-1278, + + -1278,-1278,-1278,-1278,-1278,-1278,-1278,-1278,-1278,-1278, + -1278,-1278,-1278,-1278,-1278,-1278,-1278,-1278,-1278,-1278, + -1278,-1278,-1278,-1278,-1278,-1278,-1278,-1278,-1278,-1278, + -1278,-1278,-1278,-1278,-1278,-1278,-1278,-1278,-1278,-1278, + -1278,-1278,-1278,-1278,-1278,-1278,-1278,-1278,-1278,-1278, + -1278,-1278,-1278,-1278,-1278,-1278,-1278,-1278,-1278,-1278, + -1278,-1278,-1278,-1278,-1278,-1278,-1278,-1278 + }, + + { + 69,-1279,-1279,-1279,-1279,-1279,-1279,-1279,-1279,-1279, + -1279,-1279,-1279,-1279,-1279,-1279,-1279,-1279,-1279,-1279, + -1279,-1279,-1279,-1279,-1279,-1279,-1279,-1279,-1279,-1279, + + -1279,-1279,-1279,-1279,-1279,-1279,-1279,-1279,-1279,-1279, + -1279,-1279,-1279,-1279,-1279,-1279,-1279,-1279,-1279,-1279, + -1279,-1279,-1279,-1279,-1279,-1279,-1279,-1279,-1279,-1279, + -1279,-1279,-1279,-1279,-1279,-1279,-1279,-1279,-1279,-1279, + -1279,-1279,-1279,-1279,-1279,-1279,-1279,-1279,-1279,-1279, + -1279,-1279,-1279,-1279,-1279,-1279,-1279,-1279,-1279,-1279, + -1279,-1279,-1279,-1279,-1279,-1279,-1279,-1279,-1279,-1279, + -1279,-1279,-1279,-1279,-1279,-1279,-1279,-1279,-1279,-1279, + -1279,-1279,-1279,-1279,-1279,-1279,-1279,-1279,-1279,-1279, + -1279,-1279,-1279,-1279,-1279,-1279,-1279,-1279 + + }, + + { + 69,-1280,-1280,-1280,-1280,-1280,-1280,-1280,-1280,-1280, + -1280,-1280,-1280,-1280,-1280,-1280,-1280,-1280,-1280,-1280, + -1280,-1280,-1280,-1280,-1280,-1280,-1280,-1280,-1280,-1280, + -1280,-1280,-1280,-1280,-1280,-1280,-1280,-1280,-1280,-1280, + -1280,-1280,-1280,-1280,-1280,-1280,-1280,-1280,-1280,-1280, + -1280,-1280,-1280,-1280,-1280,-1280,-1280,-1280,-1280,-1280, + -1280,-1280,-1280,-1280,-1280,-1280,-1280,-1280,-1280,-1280, + -1280,-1280,-1280,-1280,-1280,-1280,-1280,-1280,-1280,-1280, + -1280,-1280,-1280,-1280,-1280,-1280,-1280,-1280,-1280,-1280, + -1280,-1280,-1280,-1280,-1280,-1280,-1280,-1280,-1280,-1280, + + -1280,-1280,-1280,-1280,-1280,-1280,-1280,-1280,-1280,-1280, + -1280,-1280,-1280,-1280,-1280,-1280,-1280,-1280,-1280,-1280, + -1280,-1280,-1280,-1280,-1280,-1280,-1280,-1280 + }, + + { + 69,-1281,-1281,-1281,-1281,-1281,-1281,-1281,-1281,-1281, + -1281,-1281,-1281,-1281,-1281,-1281,-1281,-1281,-1281,-1281, + -1281,-1281,-1281,-1281,-1281,-1281,-1281,-1281,-1281,-1281, + -1281,-1281,-1281,-1281,-1281,-1281,-1281,-1281,-1281,-1281, + -1281,-1281,-1281,-1281,-1281,-1281,-1281,-1281,-1281,-1281, + -1281,-1281,-1281,-1281,-1281,-1281,-1281,-1281,-1281,-1281, + -1281,-1281,-1281,-1281,-1281,-1281,-1281,-1281,-1281,-1281, + + -1281,-1281,-1281,-1281,-1281,-1281,-1281,-1281,-1281,-1281, + -1281,-1281,-1281,-1281,-1281,-1281,-1281,-1281,-1281,-1281, + -1281,-1281,-1281,-1281,-1281,-1281,-1281,-1281,-1281,-1281, + -1281,-1281,-1281,-1281,-1281,-1281,-1281,-1281,-1281,-1281, + -1281,-1281,-1281,-1281,-1281,-1281,-1281,-1281,-1281,-1281, + -1281,-1281,-1281,-1281,-1281,-1281,-1281,-1281 + }, + + { + 69,-1282,-1282,-1282,-1282,-1282,-1282,-1282,-1282,-1282, + -1282,-1282,-1282,-1282,-1282,-1282,-1282,-1282,-1282,-1282, + -1282,-1282,-1282,-1282,-1282,-1282,-1282,-1282,-1282,-1282, + -1282,-1282,-1282,-1282,-1282,-1282,-1282,-1282,-1282,-1282, + + -1282,-1282,-1282,-1282,-1282,-1282,-1282,-1282,-1282,-1282, + -1282,-1282,-1282,-1282,-1282,-1282,-1282,-1282,-1282,-1282, + -1282,-1282,-1282,-1282,-1282,-1282,-1282,-1282,-1282,-1282, + -1282,-1282,-1282,-1282,-1282,-1282,-1282,-1282,-1282,-1282, + -1282,-1282,-1282,-1282,-1282,-1282,-1282,-1282,-1282,-1282, + -1282,-1282,-1282,-1282,-1282,-1282,-1282,-1282,-1282,-1282, + -1282,-1282,-1282,-1282,-1282,-1282,-1282,-1282,-1282,-1282, + -1282,-1282,-1282,-1282,-1282,-1282,-1282,-1282,-1282,-1282, + -1282,-1282,-1282,-1282,-1282,-1282,-1282,-1282 + }, + + { + 69,-1283,-1283,-1283,-1283,-1283,-1283,-1283,-1283,-1283, + + -1283,-1283,-1283,-1283,-1283,-1283,-1283,-1283,-1283,-1283, + -1283,-1283,-1283,-1283,-1283,-1283,-1283,-1283,-1283,-1283, + -1283,-1283,-1283,-1283,-1283,-1283,-1283,-1283,-1283,-1283, + -1283,-1283,-1283,-1283,-1283,-1283,-1283,-1283,-1283,-1283, + -1283,-1283,-1283,-1283,-1283,-1283,-1283,-1283,-1283,-1283, + -1283,-1283,-1283,-1283,-1283,-1283,-1283,-1283,-1283,-1283, + -1283,-1283,-1283,-1283,-1283,-1283,-1283,-1283,-1283,-1283, + -1283,-1283,-1283,-1283,-1283,-1283,-1283,-1283,-1283,-1283, + -1283,-1283,-1283,-1283,-1283,-1283,-1283,-1283,-1283,-1283, + -1283,-1283,-1283,-1283,-1283,-1283,-1283,-1283,-1283,-1283, + + -1283,-1283,-1283,-1283,-1283,-1283,-1283,-1283,-1283,-1283, + -1283,-1283,-1283,-1283,-1283,-1283,-1283,-1283 + }, + + { + 69,-1284,-1284,-1284,-1284,-1284,-1284,-1284,-1284,-1284, + -1284,-1284,-1284,-1284,-1284,-1284,-1284,-1284,-1284,-1284, + -1284,-1284,-1284,-1284,-1284,-1284,-1284,-1284,-1284,-1284, + -1284,-1284,-1284,-1284,-1284,-1284,-1284,-1284,-1284,-1284, + -1284,-1284,-1284,-1284,-1284,-1284,-1284,-1284,-1284,-1284, + -1284,-1284,-1284,-1284,-1284,-1284,-1284,-1284,-1284,-1284, + -1284,-1284,-1284,-1284,-1284,-1284,-1284,-1284,-1284,-1284, + -1284,-1284,-1284,-1284,-1284,-1284,-1284,-1284,-1284,-1284, + + -1284,-1284,-1284,-1284,-1284,-1284,-1284,-1284,-1284,-1284, + -1284,-1284,-1284,-1284,-1284,-1284,-1284,-1284,-1284,-1284, + -1284,-1284,-1284,-1284,-1284,-1284,-1284,-1284,-1284,-1284, + -1284,-1284,-1284,-1284,-1284,-1284,-1284,-1284,-1284,-1284, + -1284,-1284,-1284,-1284,-1284,-1284,-1284,-1284 + }, + + { + 69,-1285,-1285,-1285,-1285,-1285,-1285,-1285,-1285,-1285, + -1285,-1285,-1285,-1285,-1285,-1285,-1285,-1285,-1285,-1285, + -1285,-1285,-1285,-1285,-1285,-1285,-1285,-1285,-1285,-1285, + -1285,-1285,-1285,-1285,-1285,-1285,-1285,-1285,-1285,-1285, + -1285,-1285,-1285,-1285,-1285,-1285,-1285,-1285,-1285,-1285, + + -1285,-1285,-1285,-1285,-1285,-1285,-1285,-1285,-1285,-1285, + -1285,-1285,-1285,-1285,-1285,-1285,-1285,-1285,-1285,-1285, + -1285,-1285,-1285,-1285,-1285,-1285,-1285,-1285,-1285,-1285, + -1285,-1285,-1285,-1285,-1285,-1285,-1285,-1285,-1285,-1285, + -1285,-1285,-1285,-1285,-1285,-1285,-1285,-1285,-1285,-1285, + -1285,-1285,-1285,-1285,-1285,-1285,-1285,-1285,-1285,-1285, + -1285,-1285,-1285,-1285,-1285,-1285,-1285,-1285,-1285,-1285, + -1285,-1285,-1285,-1285,-1285,-1285,-1285,-1285 + }, + + { + 69,-1286,-1286,-1286,-1286,-1286,-1286,-1286,-1286,-1286, + -1286,-1286,-1286,-1286,-1286,-1286,-1286,-1286,-1286,-1286, + + -1286,-1286,-1286,-1286,-1286,-1286,-1286,-1286,-1286,-1286, + -1286,-1286,-1286,-1286,-1286,-1286,-1286,-1286,-1286,-1286, + -1286,-1286,-1286,-1286,-1286,-1286,-1286,-1286,-1286,-1286, + -1286,-1286,-1286,-1286,-1286,-1286,-1286,-1286,-1286,-1286, + -1286,-1286,-1286,-1286,-1286,-1286,-1286,-1286,-1286,-1286, + -1286,-1286,-1286,-1286,-1286,-1286,-1286,-1286,-1286,-1286, + -1286,-1286,-1286,-1286,-1286,-1286,-1286,-1286,-1286,-1286, + -1286,-1286,-1286,-1286,-1286,-1286,-1286,-1286,-1286,-1286, + -1286,-1286,-1286,-1286,-1286,-1286,-1286,-1286,-1286,-1286, + -1286,-1286,-1286,-1286,-1286,-1286,-1286,-1286,-1286,-1286, + + -1286,-1286,-1286,-1286,-1286,-1286,-1286,-1286 + }, + + { + 69,-1287,-1287,-1287,-1287,-1287,-1287,-1287,-1287,-1287, + -1287,-1287,-1287,-1287,-1287,-1287,-1287,-1287,-1287,-1287, + -1287,-1287,-1287,-1287,-1287,-1287,-1287,-1287,-1287,-1287, + -1287,-1287,-1287,-1287,-1287,-1287,-1287,-1287,-1287,-1287, + -1287,-1287,-1287,-1287,-1287,-1287,-1287,-1287,-1287,-1287, + -1287,-1287,-1287,-1287,-1287,-1287,-1287,-1287,-1287,-1287, + -1287,-1287,-1287,-1287,-1287,-1287,-1287,-1287,-1287,-1287, + -1287,-1287,-1287,-1287,-1287,-1287,-1287,-1287,-1287,-1287, + -1287,-1287,-1287,-1287,-1287,-1287,-1287,-1287,-1287,-1287, + + -1287,-1287,-1287,-1287,-1287,-1287,-1287,-1287,-1287,-1287, + -1287,-1287,-1287,-1287,-1287,-1287,-1287,-1287,-1287,-1287, + -1287,-1287,-1287,-1287,-1287,-1287,-1287,-1287,-1287,-1287, + -1287,-1287,-1287,-1287,-1287,-1287,-1287,-1287 + }, + + { + 69,-1288,-1288,-1288,-1288,-1288,-1288,-1288,-1288,-1288, + -1288,-1288,-1288,-1288,-1288,-1288,-1288,-1288,-1288,-1288, + -1288,-1288,-1288,-1288,-1288,-1288,-1288,-1288,-1288,-1288, + -1288,-1288,-1288,-1288,-1288,-1288,-1288,-1288,-1288,-1288, + -1288,-1288,-1288,-1288,-1288,-1288,-1288,-1288,-1288,-1288, + -1288,-1288,-1288,-1288,-1288,-1288,-1288,-1288,-1288,-1288, + + -1288,-1288,-1288,-1288,-1288,-1288,-1288,-1288,-1288,-1288, + -1288,-1288,-1288,-1288,-1288,-1288,-1288,-1288,-1288,-1288, + -1288,-1288,-1288,-1288,-1288,-1288,-1288,-1288,-1288,-1288, + -1288,-1288,-1288,-1288,-1288,-1288,-1288,-1288,-1288,-1288, + -1288,-1288,-1288,-1288,-1288,-1288,-1288,-1288,-1288,-1288, + -1288,-1288,-1288,-1288,-1288,-1288,-1288,-1288,-1288,-1288, + -1288,-1288,-1288,-1288,-1288,-1288,-1288,-1288 + }, + + { + 69,-1289,-1289,-1289,-1289,-1289,-1289,-1289,-1289,-1289, + -1289,-1289,-1289,-1289,-1289,-1289,-1289,-1289,-1289,-1289, + -1289,-1289,-1289,-1289,-1289,-1289,-1289,-1289,-1289,-1289, + + -1289,-1289,-1289,-1289,-1289,-1289,-1289,-1289,-1289,-1289, + -1289,-1289,-1289,-1289,-1289,-1289,-1289,-1289,-1289,-1289, + -1289,-1289,-1289,-1289,-1289,-1289,-1289,-1289,-1289,-1289, + -1289,-1289,-1289,-1289,-1289,-1289,-1289,-1289,-1289,-1289, + -1289,-1289,-1289,-1289,-1289,-1289,-1289,-1289,-1289,-1289, + -1289,-1289,-1289,-1289,-1289,-1289,-1289,-1289,-1289,-1289, + -1289,-1289,-1289,-1289,-1289,-1289,-1289,-1289,-1289,-1289, + -1289,-1289,-1289,-1289,-1289,-1289,-1289,-1289,-1289,-1289, + -1289,-1289,-1289,-1289,-1289,-1289,-1289,-1289,-1289,-1289, + -1289,-1289,-1289,-1289,-1289,-1289,-1289,-1289 + + }, + + { + 69,-1290,-1290,-1290,-1290,-1290,-1290,-1290,-1290,-1290, + -1290,-1290,-1290,-1290,-1290,-1290,-1290,-1290,-1290,-1290, + -1290,-1290,-1290,-1290,-1290,-1290,-1290,-1290,-1290,-1290, + -1290,-1290,-1290,-1290,-1290,-1290,-1290,-1290,-1290,-1290, + -1290,-1290,-1290,-1290,-1290,-1290,-1290,-1290,-1290,-1290, + -1290,-1290,-1290,-1290,-1290,-1290,-1290,-1290,-1290,-1290, + -1290,-1290,-1290,-1290,-1290,-1290,-1290,-1290,-1290,-1290, + -1290,-1290,-1290,-1290,-1290,-1290,-1290,-1290,-1290,-1290, + -1290,-1290,-1290,-1290,-1290,-1290,-1290,-1290,-1290,-1290, + -1290,-1290,-1290,-1290,-1290,-1290,-1290,-1290,-1290,-1290, + + -1290,-1290,-1290,-1290,-1290,-1290,-1290,-1290,-1290,-1290, + -1290,-1290,-1290,-1290,-1290,-1290,-1290,-1290,-1290,-1290, + -1290,-1290,-1290,-1290,-1290,-1290,-1290,-1290 + }, + + { + 69,-1291,-1291,-1291,-1291,-1291,-1291,-1291,-1291,-1291, + -1291,-1291,-1291,-1291,-1291,-1291,-1291,-1291,-1291,-1291, + -1291,-1291,-1291,-1291,-1291,-1291,-1291,-1291,-1291,-1291, + -1291,-1291,-1291,-1291,-1291,-1291,-1291,-1291,-1291,-1291, + -1291,-1291,-1291,-1291,-1291,-1291,-1291,-1291,-1291,-1291, + -1291,-1291,-1291,-1291,-1291,-1291,-1291,-1291,-1291,-1291, + -1291,-1291,-1291,-1291,-1291,-1291,-1291,-1291,-1291,-1291, + + -1291,-1291,-1291,-1291,-1291,-1291,-1291,-1291,-1291,-1291, + -1291,-1291,-1291,-1291,-1291,-1291,-1291,-1291,-1291,-1291, + -1291,-1291,-1291,-1291,-1291,-1291,-1291,-1291,-1291,-1291, + -1291,-1291,-1291,-1291,-1291,-1291,-1291,-1291,-1291,-1291, + -1291,-1291,-1291,-1291,-1291,-1291,-1291,-1291,-1291,-1291, + -1291,-1291,-1291,-1291,-1291,-1291,-1291,-1291 + }, + + { + 69,-1292,-1292,-1292,-1292,-1292,-1292,-1292,-1292,-1292, + -1292,-1292,-1292,-1292,-1292,-1292,-1292,-1292,-1292,-1292, + -1292,-1292,-1292,-1292,-1292,-1292,-1292,-1292,-1292,-1292, + -1292,-1292,-1292,-1292,-1292,-1292,-1292,-1292,-1292,-1292, + + -1292,-1292,-1292,-1292,-1292,-1292,-1292,-1292,-1292,-1292, + -1292,-1292,-1292,-1292,-1292,-1292,-1292,-1292,-1292,-1292, + -1292,-1292,-1292,-1292,-1292,-1292,-1292,-1292,-1292,-1292, + -1292,-1292,-1292,-1292,-1292,-1292,-1292,-1292,-1292,-1292, + -1292,-1292,-1292,-1292,-1292,-1292,-1292,-1292,-1292,-1292, + -1292,-1292,-1292,-1292,-1292,-1292,-1292,-1292,-1292,-1292, + -1292,-1292,-1292,-1292,-1292,-1292,-1292,-1292,-1292,-1292, + -1292,-1292,-1292,-1292,-1292,-1292,-1292,-1292,-1292,-1292, + -1292,-1292,-1292,-1292,-1292,-1292,-1292,-1292 + }, + + { + 69,-1293,-1293,-1293,-1293,-1293,-1293,-1293,-1293,-1293, + + -1293,-1293,-1293,-1293,-1293,-1293,-1293,-1293,-1293,-1293, + -1293,-1293,-1293,-1293,-1293,-1293,-1293,-1293,-1293,-1293, + -1293,-1293,-1293,-1293,-1293,-1293,-1293,-1293,-1293,-1293, + -1293,-1293,-1293,-1293,-1293,-1293,-1293,-1293,-1293,-1293, + -1293,-1293,-1293,-1293,-1293,-1293,-1293,-1293,-1293,-1293, + -1293,-1293,-1293,-1293,-1293,-1293,-1293,-1293,-1293,-1293, + -1293,-1293,-1293,-1293,-1293,-1293,-1293,-1293,-1293,-1293, + -1293,-1293,-1293,-1293,-1293,-1293,-1293,-1293,-1293,-1293, + -1293,-1293,-1293,-1293,-1293,-1293,-1293,-1293,-1293,-1293, + -1293,-1293,-1293,-1293,-1293,-1293,-1293,-1293,-1293,-1293, + + -1293,-1293,-1293,-1293,-1293,-1293,-1293,-1293,-1293,-1293, + -1293,-1293,-1293,-1293,-1293,-1293,-1293,-1293 + }, + + { + 69,-1294,-1294,-1294,-1294,-1294,-1294,-1294,-1294,-1294, + -1294,-1294,-1294,-1294,-1294,-1294,-1294,-1294,-1294,-1294, + -1294,-1294,-1294,-1294,-1294,-1294,-1294,-1294,-1294,-1294, + -1294,-1294,-1294,-1294,-1294,-1294,-1294,-1294,-1294,-1294, + -1294,-1294,-1294,-1294,-1294,-1294,-1294,-1294,-1294,-1294, + -1294,-1294,-1294,-1294,-1294,-1294,-1294,-1294,-1294,-1294, + -1294,-1294,-1294,-1294,-1294,-1294,-1294,-1294,-1294,-1294, + -1294,-1294,-1294,-1294,-1294,-1294,-1294,-1294,-1294,-1294, + + -1294,-1294,-1294,-1294,-1294,-1294,-1294,-1294,-1294,-1294, + -1294,-1294,-1294,-1294,-1294,-1294,-1294,-1294,-1294,-1294, + -1294,-1294,-1294,-1294,-1294,-1294,-1294,-1294,-1294,-1294, + -1294,-1294,-1294,-1294,-1294,-1294,-1294,-1294,-1294,-1294, + -1294,-1294,-1294,-1294,-1294,-1294,-1294,-1294 + }, + + { + 69,-1295,-1295,-1295,-1295,-1295,-1295,-1295,-1295,-1295, + -1295,-1295,-1295,-1295,-1295,-1295,-1295,-1295,-1295,-1295, + -1295,-1295,-1295,-1295,-1295,-1295,-1295,-1295,-1295,-1295, + -1295,-1295,-1295,-1295,-1295,-1295,-1295,-1295,-1295,-1295, + -1295,-1295,-1295,-1295,-1295,-1295,-1295,-1295,-1295,-1295, + + -1295,-1295,-1295,-1295,-1295,-1295,-1295,-1295,-1295,-1295, + -1295,-1295,-1295,-1295,-1295,-1295,-1295,-1295,-1295,-1295, + -1295,-1295,-1295,-1295,-1295,-1295,-1295,-1295,-1295,-1295, + -1295,-1295,-1295,-1295,-1295,-1295,-1295,-1295,-1295,-1295, + -1295,-1295,-1295,-1295,-1295,-1295,-1295,-1295,-1295,-1295, + -1295,-1295,-1295,-1295,-1295,-1295,-1295,-1295,-1295,-1295, + -1295,-1295,-1295,-1295,-1295,-1295,-1295,-1295,-1295,-1295, + -1295,-1295,-1295,-1295,-1295,-1295,-1295,-1295 + }, + + { + 69,-1296,-1296,-1296,-1296,-1296,-1296,-1296,-1296,-1296, + -1296,-1296,-1296,-1296,-1296,-1296,-1296,-1296,-1296,-1296, + + -1296,-1296,-1296,-1296,-1296,-1296,-1296,-1296,-1296,-1296, + -1296,-1296,-1296,-1296,-1296,-1296,-1296,-1296,-1296,-1296, + -1296,-1296,-1296,-1296,-1296,-1296,-1296,-1296,-1296,-1296, + -1296,-1296,-1296,-1296,-1296,-1296,-1296,-1296,-1296,-1296, + -1296,-1296,-1296,-1296,-1296,-1296,-1296,-1296,-1296,-1296, + -1296,-1296,-1296,-1296,-1296,-1296,-1296,-1296,-1296,-1296, + -1296,-1296,-1296,-1296,-1296,-1296,-1296,-1296,-1296,-1296, + -1296,-1296,-1296,-1296,-1296,-1296,-1296,-1296,-1296,-1296, + -1296,-1296,-1296,-1296,-1296,-1296,-1296,-1296,-1296,-1296, + -1296,-1296,-1296,-1296,-1296,-1296,-1296,-1296,-1296,-1296, + + -1296,-1296,-1296,-1296,-1296,-1296,-1296,-1296 + }, + + { + 69,-1297,-1297,-1297,-1297,-1297,-1297,-1297,-1297,-1297, + -1297,-1297,-1297,-1297,-1297,-1297,-1297,-1297,-1297,-1297, + -1297,-1297,-1297,-1297,-1297,-1297,-1297,-1297,-1297,-1297, + -1297,-1297,-1297,-1297,-1297,-1297,-1297,-1297,-1297,-1297, + -1297,-1297,-1297,-1297,-1297,-1297,-1297,-1297,-1297,-1297, + -1297,-1297,-1297,-1297,-1297,-1297,-1297,-1297,-1297,-1297, + -1297, 1326,-1297,-1297,-1297,-1297,-1297,-1297,-1297,-1297, + -1297,-1297,-1297,-1297,-1297,-1297,-1297,-1297,-1297,-1297, + -1297,-1297,-1297,-1297,-1297,-1297,-1297,-1297,-1297,-1297, + + -1297,-1297,-1297,-1297,-1297,-1297,-1297,-1297,-1297,-1297, + -1297,-1297,-1297,-1297,-1297,-1297,-1297,-1297,-1297,-1297, + -1297,-1297,-1297,-1297,-1297,-1297,-1297,-1297,-1297,-1297, + -1297,-1297,-1297,-1297,-1297,-1297,-1297,-1297 + }, + + { + 69,-1298,-1298,-1298,-1298,-1298,-1298,-1298,-1298,-1298, + -1298,-1298,-1298,-1298,-1298,-1298,-1298,-1298,-1298,-1298, + -1298,-1298,-1298,-1298,-1298,-1298,-1298,-1298,-1298,-1298, + -1298,-1298,-1298,-1298,-1298,-1298,-1298,-1298,-1298,-1298, + -1298,-1298,-1298,-1298,-1298,-1298,-1298,-1298,-1298,-1298, + -1298,-1298,-1298,-1298,-1298,-1298,-1298,-1298,-1298,-1298, + + -1298,-1298,-1298,-1298,-1298,-1298,-1298,-1298,-1298,-1298, + -1298,-1298,-1298,-1298,-1298,-1298,-1298,-1298,-1298,-1298, + -1298,-1298,-1298,-1298,-1298,-1298,-1298,-1298,-1298,-1298, + -1298,-1298,-1298,-1298,-1298,-1298,-1298,-1298,-1298,-1298, + -1298,-1298,-1298,-1298,-1298,-1298,-1298,-1298,-1298,-1298, + -1298,-1298,-1298,-1298,-1298,-1298,-1298,-1298,-1298,-1298, + -1298,-1298,-1298,-1298,-1298,-1298,-1298,-1298 + }, + + { + 69,-1299,-1299,-1299,-1299,-1299,-1299,-1299,-1299,-1299, + -1299,-1299,-1299,-1299,-1299,-1299,-1299,-1299,-1299,-1299, + -1299,-1299,-1299,-1299,-1299,-1299,-1299,-1299,-1299,-1299, + + -1299,-1299,-1299,-1299,-1299,-1299,-1299,-1299,-1299,-1299, + -1299,-1299,-1299,-1299,-1299,-1299,-1299,-1299,-1299,-1299, + -1299,-1299,-1299,-1299,-1299,-1299,-1299,-1299,-1299,-1299, + -1299,-1299,-1299,-1299,-1299,-1299,-1299,-1299,-1299,-1299, + -1299,-1299,-1299,-1299,-1299,-1299,-1299,-1299,-1299,-1299, + -1299,-1299,-1299,-1299,-1299,-1299,-1299,-1299,-1299,-1299, + -1299,-1299,-1299,-1299,-1299,-1299,-1299,-1299,-1299,-1299, + -1299,-1299,-1299,-1299,-1299,-1299,-1299,-1299,-1299,-1299, + -1299,-1299,-1299,-1299,-1299,-1299,-1299,-1299,-1299,-1299, + -1299,-1299,-1299,-1299,-1299,-1299,-1299,-1299 + + }, + + { + 69,-1300,-1300,-1300,-1300,-1300,-1300,-1300,-1300,-1300, + -1300,-1300,-1300,-1300,-1300,-1300,-1300,-1300,-1300,-1300, + -1300,-1300,-1300,-1300,-1300,-1300,-1300,-1300,-1300,-1300, + -1300,-1300,-1300,-1300,-1300,-1300,-1300,-1300,-1300,-1300, + -1300,-1300,-1300,-1300,-1300,-1300,-1300,-1300,-1300,-1300, + -1300,-1300,-1300,-1300,-1300,-1300,-1300,-1300,-1300,-1300, + -1300,-1300,-1300,-1300,-1300,-1300,-1300,-1300,-1300,-1300, + -1300,-1300,-1300,-1300,-1300,-1300,-1300,-1300,-1300,-1300, + -1300,-1300,-1300,-1300,-1300,-1300,-1300,-1300,-1300,-1300, + -1300,-1300,-1300,-1300,-1300,-1300,-1300,-1300,-1300,-1300, + + -1300,-1300,-1300,-1300,-1300,-1300,-1300,-1300,-1300,-1300, + -1300,-1300,-1300,-1300,-1300,-1300,-1300,-1300,-1300,-1300, + -1300,-1300,-1300,-1300,-1300,-1300,-1300,-1300 + }, + + { + 69,-1301,-1301,-1301,-1301,-1301,-1301,-1301,-1301,-1301, + -1301,-1301,-1301,-1301,-1301,-1301,-1301,-1301,-1301,-1301, + -1301,-1301,-1301,-1301,-1301,-1301,-1301,-1301,-1301,-1301, + -1301,-1301,-1301,-1301,-1301,-1301,-1301,-1301,-1301,-1301, + -1301,-1301,-1301,-1301,-1301,-1301,-1301,-1301,-1301,-1301, + -1301,-1301,-1301,-1301,-1301,-1301,-1301,-1301,-1301,-1301, + -1301,-1301,-1301,-1301,-1301,-1301,-1301,-1301,-1301,-1301, + + -1301,-1301,-1301,-1301,-1301,-1301,-1301,-1301,-1301,-1301, + -1301,-1301,-1301,-1301,-1301,-1301,-1301,-1301,-1301,-1301, + -1301,-1301,-1301,-1301,-1301,-1301,-1301,-1301,-1301,-1301, + -1301,-1301,-1301,-1301,-1301,-1301,-1301,-1301,-1301,-1301, + -1301,-1301,-1301,-1301,-1301,-1301,-1301,-1301,-1301,-1301, + -1301,-1301,-1301,-1301,-1301,-1301,-1301,-1301 + }, + + { + 69,-1302,-1302,-1302,-1302,-1302,-1302,-1302,-1302,-1302, + -1302,-1302,-1302,-1302,-1302,-1302,-1302,-1302,-1302,-1302, + -1302,-1302,-1302,-1302,-1302,-1302,-1302,-1302,-1302,-1302, + -1302,-1302,-1302,-1302,-1302,-1302,-1302,-1302,-1302,-1302, + + -1302,-1302,-1302,-1302,-1302,-1302,-1302,-1302,-1302,-1302, + -1302,-1302,-1302,-1302,-1302,-1302,-1302,-1302,-1302,-1302, + -1302,-1302,-1302,-1302,-1302,-1302,-1302,-1302,-1302,-1302, + -1302,-1302,-1302,-1302,-1302,-1302,-1302,-1302,-1302,-1302, + -1302,-1302,-1302,-1302,-1302,-1302,-1302,-1302,-1302,-1302, + -1302,-1302,-1302,-1302,-1302,-1302,-1302,-1302,-1302,-1302, + -1302,-1302,-1302,-1302,-1302,-1302,-1302,-1302,-1302,-1302, + -1302,-1302,-1302,-1302,-1302,-1302,-1302,-1302,-1302,-1302, + -1302,-1302,-1302,-1302,-1302,-1302,-1302,-1302 + }, + + { + 69,-1303,-1303,-1303,-1303,-1303,-1303,-1303,-1303,-1303, + + -1303,-1303,-1303,-1303,-1303,-1303,-1303,-1303,-1303,-1303, + -1303,-1303,-1303,-1303,-1303,-1303,-1303,-1303,-1303,-1303, + -1303,-1303,-1303,-1303,-1303,-1303,-1303,-1303,-1303,-1303, + -1303,-1303,-1303,-1303,-1303,-1303,-1303,-1303,-1303,-1303, + -1303,-1303,-1303,-1303,-1303,-1303,-1303,-1303,-1303,-1303, + -1303,-1303,-1303,-1303,-1303,-1303,-1303,-1303,-1303,-1303, + -1303,-1303,-1303,-1303,-1303,-1303,-1303,-1303,-1303,-1303, + -1303,-1303,-1303,-1303,-1303,-1303,-1303,-1303,-1303,-1303, + -1303,-1303,-1303,-1303,-1303,-1303,-1303,-1303,-1303,-1303, + -1303,-1303,-1303,-1303,-1303,-1303,-1303,-1303,-1303,-1303, + + -1303,-1303,-1303,-1303,-1303,-1303,-1303,-1303,-1303,-1303, + -1303,-1303,-1303,-1303,-1303,-1303,-1303,-1303 + }, + + { + 69,-1304,-1304,-1304,-1304,-1304,-1304,-1304,-1304,-1304, + -1304,-1304,-1304,-1304,-1304,-1304,-1304,-1304,-1304,-1304, + -1304,-1304,-1304,-1304,-1304,-1304,-1304,-1304,-1304,-1304, + -1304,-1304,-1304,-1304,-1304,-1304,-1304,-1304,-1304,-1304, + -1304,-1304,-1304,-1304,-1304,-1304,-1304,-1304,-1304,-1304, + -1304,-1304,-1304,-1304,-1304,-1304,-1304,-1304,-1304,-1304, + -1304,-1304,-1304,-1304,-1304,-1304,-1304,-1304,-1304,-1304, + -1304,-1304,-1304,-1304,-1304,-1304,-1304,-1304,-1304,-1304, + + -1304,-1304,-1304,-1304,-1304,-1304,-1304,-1304,-1304,-1304, + -1304,-1304,-1304,-1304,-1304,-1304,-1304,-1304,-1304,-1304, + -1304,-1304,-1304,-1304,-1304,-1304,-1304,-1304,-1304,-1304, + -1304,-1304,-1304,-1304,-1304,-1304,-1304,-1304,-1304,-1304, + -1304,-1304,-1304,-1304,-1304,-1304,-1304,-1304 + }, + + { + 69,-1305,-1305,-1305,-1305,-1305,-1305,-1305,-1305,-1305, + -1305,-1305,-1305,-1305,-1305,-1305,-1305,-1305,-1305,-1305, + -1305,-1305,-1305,-1305,-1305,-1305,-1305,-1305,-1305,-1305, + -1305,-1305,-1305,-1305,-1305,-1305,-1305,-1305,-1305,-1305, + -1305,-1305,-1305,-1305,-1305,-1305,-1305,-1305,-1305,-1305, + + -1305,-1305,-1305,-1305,-1305,-1305,-1305,-1305,-1305,-1305, + -1305,-1305,-1305,-1305,-1305,-1305,-1305,-1305,-1305,-1305, + -1305,-1305,-1305,-1305,-1305,-1305,-1305,-1305,-1305,-1305, + -1305,-1305,-1305,-1305,-1305,-1305,-1305,-1305,-1305,-1305, + -1305,-1305,-1305,-1305,-1305,-1305,-1305,-1305,-1305,-1305, + -1305,-1305,-1305,-1305,-1305,-1305,-1305,-1305,-1305,-1305, + -1305,-1305,-1305,-1305,-1305,-1305,-1305,-1305,-1305,-1305, + -1305,-1305,-1305,-1305,-1305,-1305,-1305,-1305 + }, + + { + 69,-1306,-1306,-1306,-1306,-1306,-1306,-1306,-1306,-1306, + -1306,-1306,-1306,-1306,-1306,-1306,-1306,-1306,-1306,-1306, + + -1306,-1306,-1306,-1306,-1306,-1306,-1306,-1306,-1306,-1306, + -1306,-1306,-1306,-1306,-1306,-1306,-1306,-1306,-1306,-1306, + -1306,-1306,-1306,-1306,-1306,-1306,-1306,-1306,-1306,-1306, + -1306,-1306,-1306,-1306,-1306,-1306,-1306,-1306,-1306,-1306, + -1306,-1306,-1306,-1306,-1306,-1306,-1306,-1306,-1306,-1306, + -1306,-1306,-1306,-1306,-1306,-1306,-1306,-1306,-1306,-1306, + -1306,-1306,-1306,-1306,-1306,-1306,-1306,-1306,-1306,-1306, + -1306,-1306,-1306,-1306,-1306,-1306,-1306,-1306,-1306,-1306, + -1306,-1306,-1306,-1306,-1306,-1306,-1306,-1306,-1306,-1306, + -1306,-1306,-1306,-1306,-1306,-1306,-1306,-1306,-1306,-1306, + + -1306,-1306,-1306,-1306,-1306,-1306,-1306,-1306 + }, + + { + 69,-1307,-1307,-1307,-1307,-1307,-1307,-1307,-1307,-1307, + -1307,-1307,-1307,-1307,-1307,-1307,-1307,-1307,-1307,-1307, + -1307,-1307,-1307,-1307,-1307,-1307,-1307,-1307,-1307,-1307, + -1307,-1307,-1307,-1307,-1307,-1307,-1307,-1307,-1307,-1307, + -1307,-1307,-1307,-1307,-1307,-1307,-1307,-1307,-1307,-1307, + -1307,-1307,-1307,-1307,-1307,-1307,-1307,-1307,-1307,-1307, + -1307,-1307,-1307,-1307,-1307,-1307,-1307,-1307,-1307,-1307, + -1307,-1307,-1307,-1307,-1307,-1307,-1307,-1307,-1307,-1307, + -1307,-1307,-1307,-1307,-1307,-1307,-1307,-1307,-1307,-1307, + + -1307,-1307,-1307,-1307,-1307,-1307,-1307,-1307,-1307,-1307, + -1307,-1307,-1307,-1307,-1307,-1307,-1307,-1307,-1307,-1307, + -1307,-1307,-1307,-1307,-1307,-1307,-1307,-1307,-1307,-1307, + -1307,-1307,-1307,-1307,-1307,-1307,-1307,-1307 + }, + + { + 69,-1308,-1308,-1308,-1308,-1308,-1308,-1308,-1308,-1308, + -1308,-1308,-1308,-1308,-1308,-1308,-1308,-1308,-1308,-1308, + -1308,-1308,-1308,-1308,-1308,-1308,-1308,-1308,-1308,-1308, + -1308,-1308,-1308,-1308,-1308,-1308,-1308,-1308,-1308,-1308, + -1308,-1308,-1308,-1308,-1308,-1308,-1308,-1308,-1308,-1308, + -1308,-1308,-1308,-1308,-1308,-1308,-1308,-1308,-1308,-1308, + + -1308,-1308,-1308,-1308,-1308,-1308,-1308,-1308,-1308,-1308, + -1308,-1308,-1308,-1308,-1308,-1308,-1308,-1308,-1308,-1308, + -1308,-1308,-1308,-1308,-1308,-1308,-1308,-1308,-1308,-1308, + -1308,-1308,-1308,-1308,-1308,-1308,-1308,-1308,-1308,-1308, + -1308,-1308,-1308,-1308,-1308,-1308,-1308,-1308,-1308,-1308, + -1308,-1308,-1308,-1308,-1308,-1308,-1308,-1308,-1308,-1308, + -1308,-1308,-1308,-1308,-1308,-1308,-1308,-1308 + }, + + { + 69,-1309,-1309,-1309,-1309,-1309,-1309,-1309,-1309,-1309, + -1309,-1309,-1309,-1309,-1309,-1309,-1309,-1309,-1309,-1309, + -1309,-1309,-1309,-1309,-1309,-1309,-1309,-1309,-1309,-1309, + + -1309,-1309,-1309,-1309,-1309,-1309,-1309,-1309,-1309,-1309, + -1309,-1309,-1309,-1309,-1309,-1309,-1309,-1309,-1309,-1309, + -1309,-1309,-1309,-1309,-1309,-1309,-1309,-1309,-1309,-1309, + -1309,-1309,-1309,-1309,-1309,-1309,-1309,-1309,-1309,-1309, + -1309,-1309,-1309,-1309,-1309,-1309,-1309,-1309,-1309,-1309, + -1309,-1309,-1309,-1309,-1309,-1309,-1309,-1309,-1309,-1309, + -1309,-1309,-1309,-1309,-1309,-1309,-1309,-1309,-1309,-1309, + -1309,-1309,-1309,-1309,-1309,-1309,-1309,-1309,-1309,-1309, + -1309,-1309,-1309,-1309,-1309,-1309,-1309,-1309,-1309,-1309, + -1309,-1309,-1309,-1309,-1309,-1309,-1309,-1309 + + }, + + { + 69,-1310,-1310,-1310,-1310,-1310,-1310,-1310,-1310,-1310, + -1310,-1310,-1310,-1310,-1310,-1310,-1310,-1310,-1310,-1310, + -1310,-1310,-1310,-1310,-1310,-1310,-1310,-1310,-1310,-1310, + -1310,-1310,-1310,-1310,-1310,-1310,-1310,-1310,-1310,-1310, + -1310,-1310,-1310,-1310,-1310,-1310,-1310,-1310,-1310,-1310, + -1310,-1310,-1310,-1310,-1310,-1310,-1310,-1310,-1310,-1310, + -1310,-1310,-1310,-1310,-1310,-1310,-1310,-1310,-1310,-1310, + -1310,-1310,-1310,-1310,-1310,-1310,-1310,-1310,-1310,-1310, + -1310,-1310,-1310,-1310,-1310,-1310,-1310,-1310,-1310,-1310, + -1310,-1310,-1310,-1310,-1310,-1310,-1310,-1310,-1310,-1310, + + -1310,-1310,-1310,-1310,-1310,-1310,-1310,-1310,-1310,-1310, + -1310,-1310,-1310,-1310,-1310,-1310,-1310,-1310,-1310,-1310, + -1310,-1310,-1310,-1310,-1310,-1310,-1310,-1310 + }, + + { + 69,-1311,-1311,-1311,-1311,-1311,-1311,-1311,-1311,-1311, + -1311,-1311,-1311,-1311,-1311,-1311,-1311,-1311,-1311,-1311, + -1311,-1311,-1311,-1311,-1311,-1311,-1311,-1311,-1311,-1311, + -1311,-1311,-1311,-1311,-1311,-1311,-1311,-1311,-1311,-1311, + -1311,-1311,-1311,-1311,-1311,-1311,-1311,-1311,-1311,-1311, + -1311,-1311,-1311,-1311,-1311,-1311,-1311,-1311,-1311,-1311, + -1311,-1311,-1311,-1311,-1311,-1311,-1311,-1311,-1311,-1311, + + -1311,-1311,-1311,-1311,-1311,-1311,-1311,-1311,-1311,-1311, + -1311,-1311,-1311,-1311,-1311,-1311,-1311,-1311,-1311,-1311, + -1311,-1311,-1311,-1311,-1311,-1311,-1311,-1311,-1311,-1311, + -1311,-1311,-1311,-1311,-1311,-1311,-1311,-1311,-1311,-1311, + -1311,-1311,-1311,-1311,-1311,-1311,-1311,-1311,-1311,-1311, + -1311,-1311,-1311,-1311,-1311,-1311,-1311,-1311 + }, + + { + 69,-1312,-1312,-1312,-1312,-1312,-1312,-1312,-1312,-1312, + -1312,-1312,-1312,-1312,-1312,-1312,-1312,-1312,-1312,-1312, + -1312,-1312,-1312,-1312,-1312,-1312,-1312,-1312,-1312,-1312, + -1312,-1312,-1312,-1312,-1312,-1312,-1312,-1312,-1312,-1312, + + -1312,-1312,-1312,-1312,-1312,-1312,-1312,-1312,-1312,-1312, + -1312,-1312,-1312,-1312,-1312,-1312,-1312,-1312,-1312,-1312, + -1312,-1312,-1312,-1312,-1312,-1312,-1312,-1312,-1312,-1312, + -1312,-1312,-1312,-1312,-1312,-1312,-1312,-1312,-1312,-1312, + -1312,-1312,-1312,-1312,-1312,-1312,-1312,-1312,-1312,-1312, + -1312,-1312,-1312,-1312,-1312,-1312,-1312,-1312,-1312,-1312, + -1312,-1312,-1312,-1312,-1312,-1312,-1312,-1312,-1312,-1312, + -1312,-1312,-1312,-1312,-1312,-1312,-1312,-1312,-1312,-1312, + -1312,-1312,-1312,-1312,-1312,-1312,-1312,-1312 + }, + + { + 69,-1313,-1313,-1313,-1313,-1313,-1313,-1313,-1313,-1313, + + -1313,-1313,-1313,-1313,-1313,-1313,-1313,-1313,-1313,-1313, + -1313,-1313,-1313,-1313,-1313,-1313,-1313,-1313,-1313,-1313, + -1313,-1313,-1313,-1313,-1313,-1313,-1313,-1313,-1313,-1313, + -1313,-1313,-1313,-1313,-1313,-1313,-1313,-1313,-1313,-1313, + -1313,-1313,-1313,-1313,-1313,-1313,-1313,-1313,-1313,-1313, + -1313,-1313,-1313,-1313,-1313,-1313,-1313,-1313,-1313,-1313, + -1313,-1313,-1313,-1313,-1313,-1313,-1313,-1313,-1313,-1313, + -1313,-1313,-1313,-1313,-1313,-1313,-1313,-1313,-1313,-1313, + -1313,-1313,-1313,-1313,-1313,-1313,-1313,-1313,-1313,-1313, + -1313,-1313,-1313,-1313,-1313,-1313,-1313,-1313,-1313,-1313, + + -1313,-1313,-1313,-1313,-1313,-1313,-1313,-1313,-1313,-1313, + -1313,-1313,-1313,-1313,-1313,-1313,-1313,-1313 + }, + + { + 69,-1314,-1314,-1314,-1314,-1314,-1314,-1314,-1314,-1314, + -1314,-1314,-1314,-1314,-1314,-1314,-1314,-1314,-1314,-1314, + -1314,-1314,-1314,-1314,-1314,-1314,-1314,-1314,-1314,-1314, + -1314,-1314,-1314,-1314,-1314,-1314,-1314,-1314,-1314,-1314, + -1314,-1314,-1314,-1314,-1314,-1314,-1314,-1314,-1314,-1314, + -1314,-1314,-1314,-1314,-1314,-1314,-1314,-1314,-1314,-1314, + -1314, 1327,-1314,-1314,-1314,-1314,-1314,-1314,-1314,-1314, + -1314,-1314,-1314,-1314,-1314,-1314,-1314,-1314,-1314,-1314, + + -1314,-1314,-1314,-1314,-1314,-1314,-1314,-1314,-1314,-1314, + -1314,-1314,-1314,-1314,-1314,-1314,-1314,-1314,-1314,-1314, + -1314,-1314,-1314,-1314,-1314,-1314,-1314,-1314,-1314,-1314, + -1314,-1314,-1314,-1314,-1314,-1314,-1314,-1314,-1314,-1314, + -1314,-1314,-1314,-1314,-1314,-1314,-1314,-1314 + }, + + { + 69,-1315,-1315,-1315,-1315,-1315,-1315,-1315,-1315,-1315, + -1315,-1315,-1315,-1315,-1315,-1315,-1315,-1315,-1315,-1315, + -1315,-1315,-1315,-1315,-1315,-1315,-1315,-1315,-1315,-1315, + -1315,-1315,-1315,-1315,-1315,-1315,-1315,-1315,-1315,-1315, + -1315,-1315,-1315,-1315,-1315,-1315,-1315,-1315,-1315,-1315, + + -1315,-1315,-1315,-1315,-1315,-1315,-1315,-1315,-1315,-1315, + -1315, 1328,-1315,-1315,-1315,-1315,-1315,-1315,-1315,-1315, + -1315,-1315,-1315,-1315,-1315,-1315,-1315,-1315,-1315,-1315, + -1315,-1315,-1315,-1315,-1315,-1315,-1315,-1315,-1315,-1315, + -1315,-1315,-1315,-1315,-1315,-1315,-1315,-1315,-1315,-1315, + -1315,-1315,-1315,-1315,-1315,-1315,-1315,-1315,-1315,-1315, + -1315,-1315,-1315,-1315,-1315,-1315,-1315,-1315,-1315,-1315, + -1315,-1315,-1315,-1315,-1315,-1315,-1315,-1315 + }, + + { + 69,-1316,-1316,-1316,-1316,-1316,-1316,-1316,-1316,-1316, + -1316,-1316,-1316,-1316,-1316,-1316,-1316,-1316,-1316,-1316, + + -1316,-1316,-1316,-1316,-1316,-1316,-1316,-1316,-1316,-1316, + -1316,-1316,-1316,-1316,-1316,-1316,-1316,-1316,-1316,-1316, + -1316,-1316,-1316,-1316,-1316,-1316,-1316,-1316,-1316,-1316, + -1316,-1316,-1316,-1316,-1316,-1316,-1316,-1316,-1316,-1316, + -1316, 1329,-1316,-1316,-1316,-1316,-1316,-1316,-1316,-1316, + -1316,-1316,-1316,-1316,-1316,-1316,-1316,-1316,-1316,-1316, + -1316,-1316,-1316,-1316,-1316,-1316,-1316,-1316,-1316,-1316, + -1316,-1316,-1316,-1316,-1316,-1316,-1316,-1316,-1316,-1316, + -1316,-1316,-1316,-1316,-1316,-1316,-1316,-1316,-1316,-1316, + -1316,-1316,-1316,-1316,-1316,-1316,-1316,-1316,-1316,-1316, + + -1316,-1316,-1316,-1316,-1316,-1316,-1316,-1316 + }, + + { + 69,-1317,-1317,-1317,-1317,-1317,-1317,-1317,-1317,-1317, + -1317,-1317,-1317,-1317,-1317,-1317,-1317,-1317,-1317,-1317, + -1317,-1317,-1317,-1317,-1317,-1317,-1317,-1317,-1317,-1317, + -1317,-1317,-1317,-1317,-1317,-1317,-1317,-1317,-1317,-1317, + -1317,-1317,-1317,-1317,-1317,-1317,-1317,-1317,-1317,-1317, + -1317,-1317,-1317,-1317,-1317,-1317,-1317,-1317,-1317,-1317, + -1317, 1330,-1317,-1317,-1317,-1317,-1317,-1317,-1317,-1317, + -1317,-1317,-1317,-1317,-1317,-1317,-1317,-1317,-1317,-1317, + -1317,-1317,-1317,-1317,-1317,-1317,-1317,-1317,-1317,-1317, + + -1317,-1317,-1317,-1317,-1317,-1317,-1317,-1317,-1317,-1317, + -1317,-1317,-1317,-1317,-1317,-1317,-1317,-1317,-1317,-1317, + -1317,-1317,-1317,-1317,-1317,-1317,-1317,-1317,-1317,-1317, + -1317,-1317,-1317,-1317,-1317,-1317,-1317,-1317 + }, + + { + 69,-1318,-1318,-1318,-1318,-1318,-1318,-1318,-1318,-1318, + -1318,-1318,-1318,-1318,-1318,-1318,-1318,-1318,-1318,-1318, + -1318,-1318,-1318,-1318,-1318,-1318,-1318,-1318,-1318,-1318, + -1318,-1318,-1318,-1318,-1318,-1318,-1318,-1318,-1318,-1318, + -1318,-1318,-1318,-1318,-1318,-1318,-1318,-1318,-1318,-1318, + -1318,-1318,-1318,-1318,-1318,-1318,-1318,-1318,-1318,-1318, + + -1318, 1331,-1318,-1318,-1318,-1318,-1318,-1318,-1318,-1318, + -1318,-1318,-1318,-1318,-1318,-1318,-1318,-1318,-1318,-1318, + -1318,-1318,-1318,-1318,-1318,-1318,-1318,-1318,-1318,-1318, + -1318,-1318,-1318,-1318,-1318,-1318,-1318,-1318,-1318,-1318, + -1318,-1318,-1318,-1318,-1318,-1318,-1318,-1318,-1318,-1318, + -1318,-1318,-1318,-1318,-1318,-1318,-1318,-1318,-1318,-1318, + -1318,-1318,-1318,-1318,-1318,-1318,-1318,-1318 + }, + + { + 69,-1319,-1319,-1319,-1319,-1319,-1319,-1319,-1319,-1319, + -1319,-1319,-1319,-1319,-1319,-1319,-1319,-1319,-1319,-1319, + -1319,-1319,-1319,-1319,-1319,-1319,-1319,-1319,-1319,-1319, + + -1319,-1319,-1319,-1319,-1319,-1319,-1319,-1319,-1319,-1319, + -1319,-1319,-1319,-1319,-1319,-1319,-1319,-1319,-1319,-1319, + -1319,-1319,-1319,-1319,-1319,-1319,-1319,-1319,-1319,-1319, + -1319, 1332,-1319,-1319,-1319,-1319,-1319,-1319,-1319,-1319, + -1319,-1319,-1319,-1319,-1319,-1319,-1319,-1319,-1319,-1319, + -1319,-1319,-1319,-1319,-1319,-1319,-1319,-1319,-1319,-1319, + -1319,-1319,-1319,-1319,-1319,-1319,-1319,-1319,-1319,-1319, + -1319,-1319,-1319,-1319,-1319,-1319,-1319,-1319,-1319,-1319, + -1319,-1319,-1319,-1319,-1319,-1319,-1319,-1319,-1319,-1319, + -1319,-1319,-1319,-1319,-1319,-1319,-1319,-1319 + + }, + + { + 69,-1320,-1320,-1320,-1320,-1320,-1320,-1320,-1320,-1320, + -1320,-1320,-1320,-1320,-1320,-1320,-1320,-1320,-1320,-1320, + -1320,-1320,-1320,-1320,-1320,-1320,-1320,-1320,-1320,-1320, + -1320,-1320,-1320,-1320,-1320,-1320,-1320,-1320,-1320,-1320, + -1320,-1320,-1320,-1320,-1320,-1320,-1320,-1320,-1320,-1320, + -1320,-1320,-1320,-1320,-1320,-1320,-1320,-1320,-1320,-1320, + -1320, 1333,-1320,-1320,-1320,-1320,-1320,-1320,-1320,-1320, + -1320,-1320,-1320,-1320,-1320,-1320,-1320,-1320,-1320,-1320, + -1320,-1320,-1320,-1320,-1320,-1320,-1320,-1320,-1320,-1320, + -1320,-1320,-1320,-1320,-1320,-1320,-1320,-1320,-1320,-1320, + + -1320,-1320,-1320,-1320,-1320,-1320,-1320,-1320,-1320,-1320, + -1320,-1320,-1320,-1320,-1320,-1320,-1320,-1320,-1320,-1320, + -1320,-1320,-1320,-1320,-1320,-1320,-1320,-1320 + }, + + { + 69,-1321,-1321,-1321,-1321,-1321,-1321,-1321,-1321,-1321, + -1321,-1321,-1321,-1321,-1321,-1321,-1321,-1321,-1321,-1321, + -1321,-1321,-1321,-1321,-1321,-1321,-1321,-1321,-1321,-1321, + -1321,-1321,-1321,-1321,-1321,-1321,-1321,-1321,-1321,-1321, + -1321,-1321,-1321,-1321,-1321,-1321,-1321,-1321,-1321,-1321, + -1321,-1321,-1321,-1321,-1321,-1321,-1321,-1321,-1321,-1321, + -1321, 1334,-1321,-1321,-1321,-1321,-1321,-1321,-1321,-1321, + + -1321,-1321,-1321,-1321,-1321,-1321,-1321,-1321,-1321,-1321, + -1321,-1321,-1321,-1321,-1321,-1321,-1321,-1321,-1321,-1321, + -1321,-1321,-1321,-1321,-1321,-1321,-1321,-1321,-1321,-1321, + -1321,-1321,-1321,-1321,-1321,-1321,-1321,-1321,-1321,-1321, + -1321,-1321,-1321,-1321,-1321,-1321,-1321,-1321,-1321,-1321, + -1321,-1321,-1321,-1321,-1321,-1321,-1321,-1321 + }, + + { + 69,-1322,-1322,-1322,-1322,-1322,-1322,-1322,-1322,-1322, + -1322,-1322,-1322,-1322,-1322,-1322,-1322,-1322,-1322,-1322, + -1322,-1322,-1322,-1322,-1322,-1322,-1322,-1322,-1322,-1322, + -1322,-1322,-1322,-1322,-1322,-1322,-1322,-1322,-1322,-1322, + + -1322,-1322,-1322,-1322,-1322,-1322,-1322,-1322,-1322,-1322, + -1322,-1322,-1322,-1322,-1322,-1322,-1322,-1322,-1322,-1322, + -1322, 1335,-1322,-1322,-1322,-1322,-1322,-1322,-1322,-1322, + -1322,-1322,-1322,-1322,-1322,-1322,-1322,-1322,-1322,-1322, + -1322,-1322,-1322,-1322,-1322,-1322,-1322,-1322,-1322,-1322, + -1322,-1322,-1322,-1322,-1322,-1322,-1322,-1322,-1322,-1322, + -1322,-1322,-1322,-1322,-1322,-1322,-1322,-1322,-1322,-1322, + -1322,-1322,-1322,-1322,-1322,-1322,-1322,-1322,-1322,-1322, + -1322,-1322,-1322,-1322,-1322,-1322,-1322,-1322 + }, + + { + 69,-1323,-1323,-1323,-1323,-1323,-1323,-1323,-1323,-1323, + + -1323,-1323,-1323,-1323,-1323,-1323,-1323,-1323,-1323,-1323, + -1323,-1323,-1323,-1323,-1323,-1323,-1323,-1323,-1323,-1323, + -1323,-1323,-1323,-1323,-1323,-1323,-1323,-1323,-1323,-1323, + -1323,-1323,-1323,-1323,-1323,-1323,-1323,-1323,-1323,-1323, + -1323,-1323,-1323,-1323,-1323,-1323,-1323,-1323,-1323,-1323, + -1323, 1336,-1323,-1323,-1323,-1323,-1323,-1323,-1323,-1323, + -1323,-1323,-1323,-1323,-1323,-1323,-1323,-1323,-1323,-1323, + -1323,-1323,-1323,-1323,-1323,-1323,-1323,-1323,-1323,-1323, + -1323,-1323,-1323,-1323,-1323,-1323,-1323,-1323,-1323,-1323, + -1323,-1323,-1323,-1323,-1323,-1323,-1323,-1323,-1323,-1323, + + -1323,-1323,-1323,-1323,-1323,-1323,-1323,-1323,-1323,-1323, + -1323,-1323,-1323,-1323,-1323,-1323,-1323,-1323 + }, + + { + 69,-1324,-1324,-1324,-1324,-1324,-1324,-1324,-1324,-1324, + -1324,-1324,-1324,-1324,-1324,-1324,-1324,-1324,-1324,-1324, + -1324,-1324,-1324,-1324,-1324,-1324,-1324,-1324,-1324,-1324, + -1324,-1324,-1324,-1324,-1324,-1324,-1324,-1324,-1324,-1324, + -1324,-1324,-1324,-1324,-1324,-1324,-1324,-1324,-1324,-1324, + -1324,-1324,-1324,-1324,-1324,-1324,-1324,-1324,-1324,-1324, + -1324,-1324,-1324,-1324,-1324,-1324,-1324,-1324,-1324,-1324, + -1324,-1324,-1324,-1324,-1324,-1324,-1324,-1324,-1324,-1324, + + -1324,-1324,-1324,-1324,-1324,-1324,-1324,-1324,-1324,-1324, + -1324,-1324,-1324,-1324,-1324,-1324,-1324,-1324,-1324,-1324, + -1324,-1324,-1324,-1324,-1324,-1324,-1324,-1324,-1324,-1324, + -1324,-1324,-1324,-1324,-1324,-1324,-1324,-1324,-1324,-1324, + -1324,-1324,-1324,-1324,-1324,-1324,-1324,-1324 + }, + + { + 69,-1325,-1325,-1325,-1325,-1325,-1325,-1325,-1325,-1325, + -1325,-1325,-1325,-1325,-1325,-1325,-1325,-1325,-1325,-1325, + -1325,-1325,-1325,-1325,-1325,-1325,-1325,-1325,-1325,-1325, + -1325,-1325, 1337,-1325,-1325,-1325,-1325,-1325,-1325,-1325, + -1325,-1325,-1325,-1325,-1325,-1325,-1325,-1325,-1325,-1325, + + -1325,-1325,-1325,-1325,-1325,-1325,-1325,-1325,-1325,-1325, + -1325,-1325,-1325,-1325,-1325,-1325,-1325,-1325,-1325,-1325, + -1325,-1325,-1325,-1325,-1325,-1325,-1325,-1325,-1325,-1325, + -1325,-1325,-1325,-1325,-1325,-1325,-1325,-1325,-1325,-1325, + -1325,-1325,-1325,-1325,-1325,-1325,-1325,-1325,-1325,-1325, + -1325,-1325,-1325,-1325,-1325,-1325,-1325,-1325,-1325,-1325, + -1325,-1325,-1325,-1325,-1325,-1325,-1325,-1325,-1325,-1325, + -1325,-1325,-1325,-1325,-1325,-1325,-1325,-1325 + }, + + { + 69,-1326,-1326,-1326,-1326,-1326,-1326,-1326,-1326,-1326, + -1326,-1326,-1326,-1326,-1326,-1326,-1326,-1326,-1326,-1326, + + -1326,-1326,-1326,-1326,-1326,-1326,-1326,-1326,-1326,-1326, + -1326,-1326, 1338,-1326,-1326,-1326,-1326,-1326,-1326,-1326, + -1326,-1326,-1326,-1326,-1326,-1326,-1326,-1326,-1326,-1326, + -1326,-1326,-1326,-1326,-1326,-1326,-1326,-1326,-1326,-1326, + -1326,-1326,-1326,-1326,-1326,-1326,-1326,-1326,-1326,-1326, + -1326,-1326,-1326,-1326,-1326,-1326,-1326,-1326,-1326,-1326, + -1326,-1326,-1326,-1326,-1326,-1326,-1326,-1326,-1326,-1326, + -1326,-1326,-1326,-1326,-1326,-1326,-1326,-1326,-1326,-1326, + -1326,-1326,-1326,-1326,-1326,-1326,-1326,-1326,-1326,-1326, + -1326,-1326,-1326,-1326,-1326,-1326,-1326,-1326,-1326,-1326, + + -1326,-1326,-1326,-1326,-1326,-1326,-1326,-1326 + }, + + { + 69,-1327,-1327,-1327,-1327,-1327,-1327,-1327,-1327,-1327, + -1327,-1327,-1327,-1327,-1327,-1327,-1327,-1327,-1327,-1327, + -1327,-1327,-1327,-1327,-1327,-1327,-1327,-1327,-1327,-1327, + -1327,-1327, 1339,-1327,-1327,-1327,-1327,-1327,-1327,-1327, + -1327,-1327,-1327,-1327,-1327,-1327,-1327,-1327,-1327,-1327, + -1327,-1327,-1327,-1327,-1327,-1327,-1327,-1327,-1327,-1327, + -1327,-1327,-1327,-1327,-1327,-1327,-1327,-1327,-1327,-1327, + -1327,-1327,-1327,-1327,-1327,-1327,-1327,-1327,-1327,-1327, + -1327,-1327,-1327,-1327,-1327,-1327,-1327,-1327,-1327,-1327, + + -1327,-1327,-1327,-1327,-1327,-1327,-1327,-1327,-1327,-1327, + -1327,-1327,-1327,-1327,-1327,-1327,-1327,-1327,-1327,-1327, + -1327,-1327,-1327,-1327,-1327,-1327,-1327,-1327,-1327,-1327, + -1327,-1327,-1327,-1327,-1327,-1327,-1327,-1327 + }, + + { + 69,-1328,-1328,-1328,-1328,-1328,-1328,-1328,-1328,-1328, + -1328,-1328,-1328,-1328,-1328,-1328,-1328,-1328,-1328,-1328, + -1328,-1328,-1328,-1328,-1328,-1328,-1328,-1328,-1328,-1328, + -1328,-1328, 1340,-1328,-1328,-1328,-1328,-1328,-1328,-1328, + -1328,-1328,-1328,-1328,-1328,-1328,-1328,-1328,-1328,-1328, + -1328,-1328,-1328,-1328,-1328,-1328,-1328,-1328,-1328,-1328, + + -1328,-1328,-1328,-1328,-1328,-1328,-1328,-1328,-1328,-1328, + -1328,-1328,-1328,-1328,-1328,-1328,-1328,-1328,-1328,-1328, + -1328,-1328,-1328,-1328,-1328,-1328,-1328,-1328,-1328,-1328, + -1328,-1328,-1328,-1328,-1328,-1328,-1328,-1328,-1328,-1328, + -1328,-1328,-1328,-1328,-1328,-1328,-1328,-1328,-1328,-1328, + -1328,-1328,-1328,-1328,-1328,-1328,-1328,-1328,-1328,-1328, + -1328,-1328,-1328,-1328,-1328,-1328,-1328,-1328 + }, + + { + 69,-1329,-1329,-1329,-1329,-1329,-1329,-1329,-1329,-1329, + -1329,-1329,-1329,-1329,-1329,-1329,-1329,-1329,-1329,-1329, + -1329,-1329,-1329,-1329,-1329,-1329,-1329,-1329,-1329,-1329, + + -1329,-1329, 1341,-1329,-1329,-1329,-1329,-1329,-1329,-1329, + -1329,-1329,-1329,-1329,-1329,-1329,-1329,-1329,-1329,-1329, + -1329,-1329,-1329,-1329,-1329,-1329,-1329,-1329,-1329,-1329, + -1329,-1329,-1329,-1329,-1329,-1329,-1329,-1329,-1329,-1329, + -1329,-1329,-1329,-1329,-1329,-1329,-1329,-1329,-1329,-1329, + -1329,-1329,-1329,-1329,-1329,-1329,-1329,-1329,-1329,-1329, + -1329,-1329,-1329,-1329,-1329,-1329,-1329,-1329,-1329,-1329, + -1329,-1329,-1329,-1329,-1329,-1329,-1329,-1329,-1329,-1329, + -1329,-1329,-1329,-1329,-1329,-1329,-1329,-1329,-1329,-1329, + -1329,-1329,-1329,-1329,-1329,-1329,-1329,-1329 + + }, + + { + 69,-1330,-1330,-1330,-1330,-1330,-1330,-1330,-1330,-1330, + -1330,-1330,-1330,-1330,-1330,-1330,-1330,-1330,-1330,-1330, + -1330,-1330,-1330,-1330,-1330,-1330,-1330,-1330,-1330,-1330, + -1330,-1330, 1342,-1330,-1330,-1330,-1330,-1330,-1330,-1330, + -1330,-1330,-1330,-1330,-1330,-1330,-1330,-1330,-1330,-1330, + -1330,-1330,-1330,-1330,-1330,-1330,-1330,-1330,-1330,-1330, + -1330,-1330,-1330,-1330,-1330,-1330,-1330,-1330,-1330,-1330, + -1330,-1330,-1330,-1330,-1330,-1330,-1330,-1330,-1330,-1330, + -1330,-1330,-1330,-1330,-1330,-1330,-1330,-1330,-1330,-1330, + -1330,-1330,-1330,-1330,-1330,-1330,-1330,-1330,-1330,-1330, + + -1330,-1330,-1330,-1330,-1330,-1330,-1330,-1330,-1330,-1330, + -1330,-1330,-1330,-1330,-1330,-1330,-1330,-1330,-1330,-1330, + -1330,-1330,-1330,-1330,-1330,-1330,-1330,-1330 + }, + + { + 69,-1331,-1331,-1331,-1331,-1331,-1331,-1331,-1331,-1331, + -1331,-1331,-1331,-1331,-1331,-1331,-1331,-1331,-1331,-1331, + -1331,-1331,-1331,-1331,-1331,-1331,-1331,-1331,-1331,-1331, + -1331,-1331, 1343,-1331,-1331,-1331,-1331,-1331,-1331,-1331, + -1331,-1331,-1331,-1331,-1331,-1331,-1331,-1331,-1331,-1331, + -1331,-1331,-1331,-1331,-1331,-1331,-1331,-1331,-1331,-1331, + -1331,-1331,-1331,-1331,-1331,-1331,-1331,-1331,-1331,-1331, + + -1331,-1331,-1331,-1331,-1331,-1331,-1331,-1331,-1331,-1331, + -1331,-1331,-1331,-1331,-1331,-1331,-1331,-1331,-1331,-1331, + -1331,-1331,-1331,-1331,-1331,-1331,-1331,-1331,-1331,-1331, + -1331,-1331,-1331,-1331,-1331,-1331,-1331,-1331,-1331,-1331, + -1331,-1331,-1331,-1331,-1331,-1331,-1331,-1331,-1331,-1331, + -1331,-1331,-1331,-1331,-1331,-1331,-1331,-1331 + }, + + { + 69,-1332,-1332,-1332,-1332,-1332,-1332,-1332,-1332,-1332, + -1332,-1332,-1332,-1332,-1332,-1332,-1332,-1332,-1332,-1332, + -1332,-1332,-1332,-1332,-1332,-1332,-1332,-1332,-1332,-1332, + -1332,-1332, 1344,-1332,-1332,-1332,-1332,-1332,-1332,-1332, + + -1332,-1332,-1332,-1332,-1332,-1332,-1332,-1332,-1332,-1332, + -1332,-1332,-1332,-1332,-1332,-1332,-1332,-1332,-1332,-1332, + -1332,-1332,-1332,-1332,-1332,-1332,-1332,-1332,-1332,-1332, + -1332,-1332,-1332,-1332,-1332,-1332,-1332,-1332,-1332,-1332, + -1332,-1332,-1332,-1332,-1332,-1332,-1332,-1332,-1332,-1332, + -1332,-1332,-1332,-1332,-1332,-1332,-1332,-1332,-1332,-1332, + -1332,-1332,-1332,-1332,-1332,-1332,-1332,-1332,-1332,-1332, + -1332,-1332,-1332,-1332,-1332,-1332,-1332,-1332,-1332,-1332, + -1332,-1332,-1332,-1332,-1332,-1332,-1332,-1332 + }, + + { + 69,-1333,-1333,-1333,-1333,-1333,-1333,-1333,-1333,-1333, + + -1333,-1333,-1333,-1333,-1333,-1333,-1333,-1333,-1333,-1333, + -1333,-1333,-1333,-1333,-1333,-1333,-1333,-1333,-1333,-1333, + -1333,-1333, 1345,-1333,-1333,-1333,-1333,-1333,-1333,-1333, + -1333,-1333,-1333,-1333,-1333,-1333,-1333,-1333,-1333,-1333, + -1333,-1333,-1333,-1333,-1333,-1333,-1333,-1333,-1333,-1333, + -1333,-1333,-1333,-1333,-1333,-1333,-1333,-1333,-1333,-1333, + -1333,-1333,-1333,-1333,-1333,-1333,-1333,-1333,-1333,-1333, + -1333,-1333,-1333,-1333,-1333,-1333,-1333,-1333,-1333,-1333, + -1333,-1333,-1333,-1333,-1333,-1333,-1333,-1333,-1333,-1333, + -1333,-1333,-1333,-1333,-1333,-1333,-1333,-1333,-1333,-1333, + + -1333,-1333,-1333,-1333,-1333,-1333,-1333,-1333,-1333,-1333, + -1333,-1333,-1333,-1333,-1333,-1333,-1333,-1333 + }, + + { + 69,-1334,-1334,-1334,-1334,-1334,-1334,-1334,-1334,-1334, + -1334,-1334,-1334,-1334,-1334,-1334,-1334,-1334,-1334,-1334, + -1334,-1334,-1334,-1334,-1334,-1334,-1334,-1334,-1334,-1334, + -1334,-1334, 1346,-1334,-1334,-1334,-1334,-1334,-1334,-1334, + -1334,-1334,-1334,-1334,-1334,-1334,-1334,-1334,-1334,-1334, + -1334,-1334,-1334,-1334,-1334,-1334,-1334,-1334,-1334,-1334, + -1334,-1334,-1334,-1334,-1334,-1334,-1334,-1334,-1334,-1334, + -1334,-1334,-1334,-1334,-1334,-1334,-1334,-1334,-1334,-1334, + + -1334,-1334,-1334,-1334,-1334,-1334,-1334,-1334,-1334,-1334, + -1334,-1334,-1334,-1334,-1334,-1334,-1334,-1334,-1334,-1334, + -1334,-1334,-1334,-1334,-1334,-1334,-1334,-1334,-1334,-1334, + -1334,-1334,-1334,-1334,-1334,-1334,-1334,-1334,-1334,-1334, + -1334,-1334,-1334,-1334,-1334,-1334,-1334,-1334 + }, + + { + 69,-1335,-1335,-1335,-1335,-1335,-1335,-1335,-1335,-1335, + -1335,-1335,-1335,-1335,-1335,-1335,-1335,-1335,-1335,-1335, + -1335,-1335,-1335,-1335,-1335,-1335,-1335,-1335,-1335,-1335, + -1335,-1335, 1347,-1335,-1335,-1335,-1335,-1335,-1335,-1335, + -1335,-1335,-1335,-1335,-1335,-1335,-1335,-1335,-1335,-1335, + + -1335,-1335,-1335,-1335,-1335,-1335,-1335,-1335,-1335,-1335, + -1335,-1335,-1335,-1335,-1335,-1335,-1335,-1335,-1335,-1335, + -1335,-1335,-1335,-1335,-1335,-1335,-1335,-1335,-1335,-1335, + -1335,-1335,-1335,-1335,-1335,-1335,-1335,-1335,-1335,-1335, + -1335,-1335,-1335,-1335,-1335,-1335,-1335,-1335,-1335,-1335, + -1335,-1335,-1335,-1335,-1335,-1335,-1335,-1335,-1335,-1335, + -1335,-1335,-1335,-1335,-1335,-1335,-1335,-1335,-1335,-1335, + -1335,-1335,-1335,-1335,-1335,-1335,-1335,-1335 + }, + + { + 69,-1336,-1336,-1336,-1336,-1336,-1336,-1336,-1336,-1336, + -1336,-1336,-1336,-1336,-1336,-1336,-1336,-1336,-1336,-1336, + + -1336,-1336,-1336,-1336,-1336,-1336,-1336,-1336,-1336,-1336, + -1336,-1336, 1348,-1336,-1336,-1336,-1336,-1336,-1336,-1336, + -1336,-1336,-1336,-1336,-1336,-1336,-1336,-1336,-1336,-1336, + -1336,-1336,-1336,-1336,-1336,-1336,-1336,-1336,-1336,-1336, + -1336,-1336,-1336,-1336,-1336,-1336,-1336,-1336,-1336,-1336, + -1336,-1336,-1336,-1336,-1336,-1336,-1336,-1336,-1336,-1336, + -1336,-1336,-1336,-1336,-1336,-1336,-1336,-1336,-1336,-1336, + -1336,-1336,-1336,-1336,-1336,-1336,-1336,-1336,-1336,-1336, + -1336,-1336,-1336,-1336,-1336,-1336,-1336,-1336,-1336,-1336, + -1336,-1336,-1336,-1336,-1336,-1336,-1336,-1336,-1336,-1336, + + -1336,-1336,-1336,-1336,-1336,-1336,-1336,-1336 + }, + + { + 69,-1337,-1337,-1337,-1337,-1337,-1337,-1337,-1337,-1337, + -1337,-1337,-1337,-1337,-1337,-1337,-1337,-1337,-1337,-1337, + -1337,-1337,-1337,-1337,-1337,-1337,-1337,-1337,-1337,-1337, + -1337,-1337, 1349,-1337,-1337,-1337,-1337,-1337,-1337,-1337, + -1337,-1337,-1337,-1337,-1337,-1337,-1337,-1337,-1337,-1337, + -1337,-1337,-1337,-1337,-1337,-1337,-1337,-1337,-1337,-1337, + -1337,-1337,-1337,-1337,-1337,-1337,-1337,-1337,-1337,-1337, + -1337,-1337,-1337,-1337,-1337,-1337,-1337,-1337,-1337,-1337, + -1337,-1337,-1337,-1337,-1337,-1337,-1337,-1337,-1337,-1337, + + -1337,-1337,-1337,-1337,-1337,-1337,-1337,-1337,-1337,-1337, + -1337,-1337,-1337,-1337,-1337,-1337,-1337,-1337,-1337,-1337, + -1337,-1337,-1337,-1337,-1337,-1337,-1337,-1337,-1337,-1337, + -1337,-1337,-1337,-1337,-1337,-1337,-1337,-1337 + }, + + { + 69,-1338,-1338,-1338,-1338,-1338,-1338,-1338,-1338,-1338, + -1338,-1338,-1338,-1338,-1338,-1338,-1338,-1338,-1338,-1338, + -1338,-1338,-1338,-1338,-1338,-1338,-1338,-1338,-1338,-1338, + -1338,-1338, 1350,-1338,-1338,-1338,-1338,-1338,-1338,-1338, + -1338,-1338,-1338, 1351,-1338, 1351,-1338,-1338, 1352, 1352, + 1352, 1352, 1352, 1352, 1352, 1352, 1352, 1352,-1338,-1338, + + -1338,-1338,-1338,-1338,-1338,-1338,-1338,-1338,-1338,-1338, + -1338,-1338,-1338,-1338,-1338,-1338,-1338,-1338,-1338,-1338, + -1338,-1338,-1338,-1338,-1338,-1338,-1338,-1338,-1338,-1338, + -1338,-1338,-1338,-1338,-1338,-1338,-1338,-1338,-1338,-1338, + -1338,-1338,-1338,-1338,-1338,-1338,-1338,-1338,-1338,-1338, + -1338,-1338,-1338,-1338,-1338,-1338,-1338,-1338,-1338,-1338, + -1338,-1338,-1338,-1338,-1338,-1338,-1338,-1338 + }, + + { + 69,-1339,-1339,-1339,-1339,-1339,-1339,-1339,-1339,-1339, + -1339,-1339,-1339,-1339,-1339,-1339,-1339,-1339,-1339,-1339, + -1339,-1339,-1339,-1339,-1339,-1339,-1339,-1339,-1339,-1339, + + -1339,-1339, 1353,-1339,-1339,-1339,-1339,-1339,-1339,-1339, + -1339,-1339,-1339, 1354,-1339, 1354,-1339,-1339, 1355, 1355, + 1355, 1355, 1355, 1355, 1355, 1355, 1355, 1355,-1339,-1339, + -1339,-1339,-1339,-1339,-1339,-1339,-1339,-1339,-1339,-1339, + -1339,-1339,-1339,-1339,-1339,-1339,-1339,-1339,-1339,-1339, + -1339,-1339,-1339,-1339,-1339,-1339,-1339,-1339,-1339,-1339, + -1339,-1339,-1339,-1339,-1339,-1339,-1339,-1339,-1339,-1339, + -1339,-1339,-1339,-1339,-1339,-1339,-1339,-1339,-1339,-1339, + -1339,-1339,-1339,-1339,-1339,-1339,-1339,-1339,-1339,-1339, + -1339,-1339,-1339,-1339,-1339,-1339,-1339,-1339 + + }, + + { + 69,-1340,-1340,-1340,-1340,-1340,-1340,-1340,-1340,-1340, + -1340,-1340,-1340,-1340,-1340,-1340,-1340,-1340,-1340,-1340, + -1340,-1340,-1340,-1340,-1340,-1340,-1340,-1340,-1340,-1340, + -1340,-1340, 1356,-1340,-1340,-1340,-1340,-1340,-1340,-1340, + -1340,-1340,-1340, 1357,-1340, 1357,-1340,-1340, 1358, 1358, + 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358,-1340,-1340, + -1340,-1340,-1340,-1340,-1340,-1340,-1340,-1340,-1340,-1340, + -1340,-1340,-1340,-1340,-1340,-1340,-1340,-1340,-1340,-1340, + -1340,-1340,-1340,-1340,-1340,-1340,-1340,-1340,-1340,-1340, + -1340,-1340,-1340,-1340,-1340,-1340,-1340,-1340,-1340,-1340, + + -1340,-1340,-1340,-1340,-1340,-1340,-1340,-1340,-1340,-1340, + -1340,-1340,-1340,-1340,-1340,-1340,-1340,-1340,-1340,-1340, + -1340,-1340,-1340,-1340,-1340,-1340,-1340,-1340 + }, + + { + 69,-1341,-1341,-1341,-1341,-1341,-1341,-1341,-1341,-1341, + -1341,-1341,-1341,-1341,-1341,-1341,-1341,-1341,-1341,-1341, + -1341,-1341,-1341,-1341,-1341,-1341,-1341,-1341,-1341,-1341, + -1341,-1341, 1359,-1341,-1341,-1341,-1341,-1341,-1341,-1341, + -1341,-1341,-1341, 1360,-1341, 1360,-1341,-1341, 1361, 1361, + 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361,-1341,-1341, + -1341,-1341,-1341,-1341,-1341,-1341,-1341,-1341,-1341,-1341, + + -1341,-1341,-1341,-1341,-1341,-1341,-1341,-1341,-1341,-1341, + -1341,-1341,-1341,-1341,-1341,-1341,-1341,-1341,-1341,-1341, + -1341,-1341,-1341,-1341,-1341,-1341,-1341,-1341,-1341,-1341, + -1341,-1341,-1341,-1341,-1341,-1341,-1341,-1341,-1341,-1341, + -1341,-1341,-1341,-1341,-1341,-1341,-1341,-1341,-1341,-1341, + -1341,-1341,-1341,-1341,-1341,-1341,-1341,-1341 + }, + + { + 69,-1342,-1342,-1342,-1342,-1342,-1342,-1342,-1342,-1342, + -1342,-1342,-1342,-1342,-1342,-1342,-1342,-1342,-1342,-1342, + -1342,-1342,-1342,-1342,-1342,-1342,-1342,-1342,-1342,-1342, + -1342,-1342, 1362,-1342,-1342,-1342,-1342,-1342,-1342,-1342, + + -1342,-1342,-1342, 1363,-1342, 1363,-1342,-1342, 1364, 1364, + 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364,-1342,-1342, + -1342,-1342,-1342,-1342,-1342,-1342,-1342,-1342,-1342,-1342, + -1342,-1342,-1342,-1342,-1342,-1342,-1342,-1342,-1342,-1342, + -1342,-1342,-1342,-1342,-1342,-1342,-1342,-1342,-1342,-1342, + -1342,-1342,-1342,-1342,-1342,-1342,-1342,-1342,-1342,-1342, + -1342,-1342,-1342,-1342,-1342,-1342,-1342,-1342,-1342,-1342, + -1342,-1342,-1342,-1342,-1342,-1342,-1342,-1342,-1342,-1342, + -1342,-1342,-1342,-1342,-1342,-1342,-1342,-1342 + }, + + { + 69,-1343,-1343,-1343,-1343,-1343,-1343,-1343,-1343,-1343, + + -1343,-1343,-1343,-1343,-1343,-1343,-1343,-1343,-1343,-1343, + -1343,-1343,-1343,-1343,-1343,-1343,-1343,-1343,-1343,-1343, + -1343,-1343, 1365,-1343,-1343,-1343,-1343,-1343,-1343, 1366, + -1343,-1343,-1343,-1343,-1343,-1343,-1343,-1343,-1343,-1343, + -1343,-1343,-1343,-1343,-1343,-1343,-1343,-1343,-1343,-1343, + -1343,-1343,-1343,-1343,-1343,-1343,-1343,-1343,-1343,-1343, + -1343,-1343,-1343,-1343,-1343,-1343,-1343,-1343,-1343,-1343, + -1343,-1343,-1343,-1343,-1343,-1343,-1343,-1343,-1343,-1343, + -1343,-1343,-1343,-1343,-1343,-1343,-1343,-1343,-1343,-1343, + -1343,-1343,-1343,-1343,-1343,-1343,-1343,-1343,-1343,-1343, + + -1343,-1343,-1343,-1343,-1343,-1343,-1343,-1343,-1343,-1343, + -1343,-1343,-1343,-1343,-1343,-1343,-1343,-1343 + }, + + { + 69,-1344,-1344,-1344,-1344,-1344,-1344,-1344,-1344,-1344, + -1344,-1344,-1344,-1344,-1344,-1344,-1344,-1344,-1344,-1344, + -1344,-1344,-1344,-1344,-1344,-1344,-1344,-1344,-1344,-1344, + -1344,-1344, 1367,-1344,-1344,-1344,-1344,-1344,-1344, 1368, + -1344,-1344,-1344,-1344,-1344,-1344,-1344,-1344,-1344,-1344, + -1344,-1344,-1344,-1344,-1344,-1344,-1344,-1344,-1344,-1344, + -1344,-1344,-1344,-1344,-1344,-1344,-1344,-1344,-1344,-1344, + -1344,-1344,-1344,-1344,-1344,-1344,-1344,-1344,-1344,-1344, + + -1344,-1344,-1344,-1344,-1344,-1344,-1344,-1344,-1344,-1344, + -1344,-1344,-1344,-1344,-1344,-1344,-1344,-1344,-1344,-1344, + -1344,-1344,-1344,-1344,-1344,-1344,-1344,-1344,-1344,-1344, + -1344,-1344,-1344,-1344,-1344,-1344,-1344,-1344,-1344,-1344, + -1344,-1344,-1344,-1344,-1344,-1344,-1344,-1344 + }, + + { + 69,-1345,-1345,-1345,-1345,-1345,-1345,-1345,-1345,-1345, + -1345,-1345,-1345,-1345,-1345,-1345,-1345,-1345,-1345,-1345, + -1345,-1345,-1345,-1345,-1345,-1345,-1345,-1345,-1345,-1345, + -1345,-1345, 1369,-1345,-1345,-1345,-1345,-1345,-1345, 1370, + -1345,-1345,-1345,-1345,-1345,-1345,-1345,-1345,-1345,-1345, + + -1345,-1345,-1345,-1345,-1345,-1345,-1345,-1345,-1345,-1345, + -1345,-1345,-1345,-1345,-1345,-1345,-1345,-1345,-1345,-1345, + -1345,-1345,-1345,-1345,-1345,-1345,-1345,-1345,-1345,-1345, + -1345,-1345,-1345,-1345,-1345,-1345,-1345,-1345,-1345,-1345, + -1345,-1345,-1345,-1345,-1345,-1345,-1345,-1345,-1345,-1345, + -1345,-1345,-1345,-1345,-1345,-1345,-1345,-1345,-1345,-1345, + -1345,-1345,-1345,-1345,-1345,-1345,-1345,-1345,-1345,-1345, + -1345,-1345,-1345,-1345,-1345,-1345,-1345,-1345 + }, + + { + 69,-1346,-1346,-1346,-1346,-1346,-1346,-1346,-1346,-1346, + -1346,-1346,-1346,-1346,-1346,-1346,-1346,-1346,-1346,-1346, + + -1346,-1346,-1346,-1346,-1346,-1346,-1346,-1346,-1346,-1346, + -1346,-1346, 1371,-1346,-1346,-1346,-1346,-1346,-1346, 1372, + -1346,-1346,-1346,-1346,-1346,-1346,-1346,-1346,-1346,-1346, + -1346,-1346,-1346,-1346,-1346,-1346,-1346,-1346,-1346,-1346, + -1346,-1346,-1346,-1346,-1346,-1346,-1346,-1346,-1346,-1346, + -1346,-1346,-1346,-1346,-1346,-1346,-1346,-1346,-1346,-1346, + -1346,-1346,-1346,-1346,-1346,-1346,-1346,-1346,-1346,-1346, + -1346,-1346,-1346,-1346,-1346,-1346,-1346,-1346,-1346,-1346, + -1346,-1346,-1346,-1346,-1346,-1346,-1346,-1346,-1346,-1346, + -1346,-1346,-1346,-1346,-1346,-1346,-1346,-1346,-1346,-1346, + + -1346,-1346,-1346,-1346,-1346,-1346,-1346,-1346 + }, + + { + 69,-1347,-1347,-1347,-1347,-1347,-1347,-1347,-1347,-1347, + -1347,-1347,-1347,-1347,-1347,-1347,-1347,-1347,-1347,-1347, + -1347,-1347,-1347,-1347,-1347,-1347,-1347,-1347,-1347,-1347, + -1347,-1347, 1373,-1347,-1347,-1347,-1347,-1347,-1347, 1374, + -1347,-1347,-1347,-1347,-1347,-1347,-1347,-1347,-1347,-1347, + -1347,-1347,-1347,-1347,-1347,-1347,-1347,-1347,-1347,-1347, + -1347,-1347,-1347,-1347,-1347,-1347,-1347,-1347,-1347,-1347, + -1347,-1347,-1347,-1347,-1347,-1347,-1347,-1347,-1347,-1347, + -1347,-1347,-1347,-1347,-1347,-1347,-1347,-1347,-1347,-1347, + + -1347,-1347,-1347,-1347,-1347,-1347,-1347,-1347,-1347,-1347, + -1347,-1347,-1347,-1347,-1347,-1347,-1347,-1347,-1347,-1347, + -1347,-1347,-1347,-1347,-1347,-1347,-1347,-1347,-1347,-1347, + -1347,-1347,-1347,-1347,-1347,-1347,-1347,-1347 + }, + + { + 69,-1348,-1348,-1348,-1348,-1348,-1348,-1348,-1348,-1348, + -1348,-1348,-1348,-1348,-1348,-1348,-1348,-1348,-1348,-1348, + -1348,-1348,-1348,-1348,-1348,-1348,-1348,-1348,-1348,-1348, + -1348,-1348, 1375,-1348,-1348,-1348,-1348,-1348,-1348, 1376, + -1348,-1348,-1348,-1348,-1348,-1348,-1348,-1348,-1348,-1348, + -1348,-1348,-1348,-1348,-1348,-1348,-1348,-1348,-1348,-1348, + + -1348,-1348,-1348,-1348,-1348,-1348,-1348,-1348,-1348,-1348, + -1348,-1348,-1348,-1348,-1348,-1348,-1348,-1348,-1348,-1348, + -1348,-1348,-1348,-1348,-1348,-1348,-1348,-1348,-1348,-1348, + -1348,-1348,-1348,-1348,-1348,-1348,-1348,-1348,-1348,-1348, + -1348,-1348,-1348,-1348,-1348,-1348,-1348,-1348,-1348,-1348, + -1348,-1348,-1348,-1348,-1348,-1348,-1348,-1348,-1348,-1348, + -1348,-1348,-1348,-1348,-1348,-1348,-1348,-1348 + }, + + { + 69,-1349,-1349,-1349,-1349,-1349,-1349,-1349,-1349,-1349, + -1349,-1349,-1349,-1349,-1349,-1349,-1349,-1349,-1349,-1349, + -1349,-1349,-1349,-1349,-1349,-1349,-1349,-1349,-1349,-1349, + + -1349,-1349, 1377,-1349,-1349,-1349,-1349,-1349,-1349,-1349, + -1349,-1349,-1349,-1349,-1349,-1349,-1349,-1349,-1349,-1349, + -1349,-1349,-1349,-1349,-1349,-1349,-1349,-1349,-1349,-1349, + -1349,-1349,-1349,-1349,-1349,-1349,-1349,-1349,-1349,-1349, + -1349,-1349,-1349,-1349,-1349,-1349,-1349,-1349,-1349,-1349, + -1349,-1349,-1349,-1349,-1349,-1349,-1349,-1349,-1349,-1349, + -1349,-1349,-1349,-1349,-1349,-1349,-1349,-1349,-1349,-1349, + -1349,-1349,-1349,-1349,-1349,-1349,-1349,-1349,-1349,-1349, + -1349,-1349,-1349,-1349,-1349,-1349,-1349,-1349,-1349,-1349, + -1349,-1349,-1349,-1349,-1349,-1349,-1349,-1349 + + }, + + { + 69,-1350,-1350,-1350,-1350,-1350,-1350,-1350,-1350,-1350, + -1350,-1350,-1350,-1350,-1350,-1350,-1350,-1350,-1350,-1350, + -1350,-1350,-1350,-1350,-1350,-1350,-1350,-1350,-1350,-1350, + -1350,-1350, 1350,-1350,-1350,-1350,-1350,-1350,-1350,-1350, + -1350,-1350,-1350, 1351,-1350, 1351,-1350,-1350, 1352, 1352, + 1352, 1352, 1352, 1352, 1352, 1352, 1352, 1352,-1350,-1350, + -1350,-1350,-1350,-1350,-1350,-1350,-1350,-1350,-1350,-1350, + -1350,-1350,-1350,-1350,-1350,-1350,-1350,-1350,-1350,-1350, + -1350,-1350,-1350,-1350,-1350,-1350,-1350,-1350,-1350,-1350, + -1350,-1350,-1350,-1350,-1350,-1350,-1350,-1350,-1350,-1350, + + -1350,-1350,-1350,-1350,-1350,-1350,-1350,-1350,-1350,-1350, + -1350,-1350,-1350,-1350,-1350,-1350,-1350,-1350,-1350,-1350, + -1350,-1350,-1350,-1350,-1350,-1350,-1350,-1350 + }, + + { + 69,-1351,-1351,-1351,-1351,-1351,-1351,-1351,-1351,-1351, + -1351,-1351,-1351,-1351,-1351,-1351,-1351,-1351,-1351,-1351, + -1351,-1351,-1351,-1351,-1351,-1351,-1351,-1351,-1351,-1351, + -1351,-1351,-1351,-1351,-1351,-1351,-1351,-1351,-1351,-1351, + -1351,-1351,-1351,-1351,-1351,-1351,-1351,-1351, 1352, 1352, + 1352, 1352, 1352, 1352, 1352, 1352, 1352, 1352,-1351,-1351, + -1351,-1351,-1351,-1351,-1351,-1351,-1351,-1351,-1351,-1351, + + -1351,-1351,-1351,-1351,-1351,-1351,-1351,-1351,-1351,-1351, + -1351,-1351,-1351,-1351,-1351,-1351,-1351,-1351,-1351,-1351, + -1351,-1351,-1351,-1351,-1351,-1351,-1351,-1351,-1351,-1351, + -1351,-1351,-1351,-1351,-1351,-1351,-1351,-1351,-1351,-1351, + -1351,-1351,-1351,-1351,-1351,-1351,-1351,-1351,-1351,-1351, + -1351,-1351,-1351,-1351,-1351,-1351,-1351,-1351 + }, + + { + 69,-1352,-1352,-1352,-1352,-1352,-1352,-1352,-1352,-1352, + -1352,-1352,-1352,-1352,-1352,-1352,-1352,-1352,-1352,-1352, + -1352,-1352,-1352,-1352,-1352,-1352,-1352,-1352,-1352,-1352, + -1352,-1352,-1352,-1352,-1352,-1352,-1352,-1352,-1352,-1352, + + -1352,-1352,-1352,-1352,-1352,-1352,-1352,-1352, 1352, 1352, + 1352, 1352, 1352, 1352, 1352, 1352, 1352, 1352,-1352,-1352, + -1352,-1352,-1352,-1352,-1352,-1352,-1352,-1352,-1352,-1352, + -1352,-1352,-1352,-1352,-1352,-1352,-1352,-1352,-1352,-1352, + -1352,-1352,-1352,-1352,-1352,-1352,-1352,-1352,-1352,-1352, + -1352,-1352,-1352,-1352,-1352,-1352,-1352,-1352,-1352,-1352, + -1352,-1352,-1352,-1352,-1352,-1352,-1352,-1352,-1352,-1352, + -1352,-1352,-1352,-1352,-1352,-1352,-1352,-1352,-1352,-1352, + -1352,-1352,-1352,-1352,-1352,-1352,-1352,-1352 + }, + + { + 69,-1353,-1353,-1353,-1353,-1353,-1353,-1353,-1353,-1353, + + -1353,-1353,-1353,-1353,-1353,-1353,-1353,-1353,-1353,-1353, + -1353,-1353,-1353,-1353,-1353,-1353,-1353,-1353,-1353,-1353, + -1353,-1353, 1353,-1353,-1353,-1353,-1353,-1353,-1353,-1353, + -1353,-1353,-1353, 1354,-1353, 1354,-1353,-1353, 1355, 1355, + 1355, 1355, 1355, 1355, 1355, 1355, 1355, 1355,-1353,-1353, + -1353,-1353,-1353,-1353,-1353,-1353,-1353,-1353,-1353,-1353, + -1353,-1353,-1353,-1353,-1353,-1353,-1353,-1353,-1353,-1353, + -1353,-1353,-1353,-1353,-1353,-1353,-1353,-1353,-1353,-1353, + -1353,-1353,-1353,-1353,-1353,-1353,-1353,-1353,-1353,-1353, + -1353,-1353,-1353,-1353,-1353,-1353,-1353,-1353,-1353,-1353, + + -1353,-1353,-1353,-1353,-1353,-1353,-1353,-1353,-1353,-1353, + -1353,-1353,-1353,-1353,-1353,-1353,-1353,-1353 + }, + + { + 69,-1354,-1354,-1354,-1354,-1354,-1354,-1354,-1354,-1354, + -1354,-1354,-1354,-1354,-1354,-1354,-1354,-1354,-1354,-1354, + -1354,-1354,-1354,-1354,-1354,-1354,-1354,-1354,-1354,-1354, + -1354,-1354,-1354,-1354,-1354,-1354,-1354,-1354,-1354,-1354, + -1354,-1354,-1354,-1354,-1354,-1354,-1354,-1354, 1355, 1355, + 1355, 1355, 1355, 1355, 1355, 1355, 1355, 1355,-1354,-1354, + -1354,-1354,-1354,-1354,-1354,-1354,-1354,-1354,-1354,-1354, + -1354,-1354,-1354,-1354,-1354,-1354,-1354,-1354,-1354,-1354, + + -1354,-1354,-1354,-1354,-1354,-1354,-1354,-1354,-1354,-1354, + -1354,-1354,-1354,-1354,-1354,-1354,-1354,-1354,-1354,-1354, + -1354,-1354,-1354,-1354,-1354,-1354,-1354,-1354,-1354,-1354, + -1354,-1354,-1354,-1354,-1354,-1354,-1354,-1354,-1354,-1354, + -1354,-1354,-1354,-1354,-1354,-1354,-1354,-1354 + }, + + { + 69,-1355,-1355,-1355,-1355,-1355,-1355,-1355,-1355,-1355, + -1355,-1355,-1355,-1355,-1355,-1355,-1355,-1355,-1355,-1355, + -1355,-1355,-1355,-1355,-1355,-1355,-1355,-1355,-1355,-1355, + -1355,-1355,-1355,-1355,-1355,-1355,-1355,-1355,-1355,-1355, + -1355,-1355,-1355,-1355,-1355,-1355,-1355,-1355, 1355, 1355, + + 1355, 1355, 1355, 1355, 1355, 1355, 1355, 1355,-1355,-1355, + -1355,-1355,-1355,-1355,-1355,-1355,-1355,-1355,-1355,-1355, + -1355,-1355,-1355,-1355,-1355,-1355,-1355,-1355,-1355,-1355, + -1355,-1355,-1355,-1355,-1355,-1355,-1355,-1355,-1355,-1355, + -1355,-1355,-1355,-1355,-1355,-1355,-1355,-1355,-1355,-1355, + -1355,-1355,-1355,-1355,-1355,-1355,-1355,-1355,-1355,-1355, + -1355,-1355,-1355,-1355,-1355,-1355,-1355,-1355,-1355,-1355, + -1355,-1355,-1355,-1355,-1355,-1355,-1355,-1355 + }, + + { + 69,-1356,-1356,-1356,-1356,-1356,-1356,-1356,-1356,-1356, + -1356,-1356,-1356,-1356,-1356,-1356,-1356,-1356,-1356,-1356, + + -1356,-1356,-1356,-1356,-1356,-1356,-1356,-1356,-1356,-1356, + -1356,-1356, 1356,-1356,-1356,-1356,-1356,-1356,-1356,-1356, + -1356,-1356,-1356, 1357,-1356, 1357,-1356,-1356, 1358, 1358, + 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358,-1356,-1356, + -1356,-1356,-1356,-1356,-1356,-1356,-1356,-1356,-1356,-1356, + -1356,-1356,-1356,-1356,-1356,-1356,-1356,-1356,-1356,-1356, + -1356,-1356,-1356,-1356,-1356,-1356,-1356,-1356,-1356,-1356, + -1356,-1356,-1356,-1356,-1356,-1356,-1356,-1356,-1356,-1356, + -1356,-1356,-1356,-1356,-1356,-1356,-1356,-1356,-1356,-1356, + -1356,-1356,-1356,-1356,-1356,-1356,-1356,-1356,-1356,-1356, + + -1356,-1356,-1356,-1356,-1356,-1356,-1356,-1356 + }, + + { + 69,-1357,-1357,-1357,-1357,-1357,-1357,-1357,-1357,-1357, + -1357,-1357,-1357,-1357,-1357,-1357,-1357,-1357,-1357,-1357, + -1357,-1357,-1357,-1357,-1357,-1357,-1357,-1357,-1357,-1357, + -1357,-1357,-1357,-1357,-1357,-1357,-1357,-1357,-1357,-1357, + -1357,-1357,-1357,-1357,-1357,-1357,-1357,-1357, 1358, 1358, + 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358,-1357,-1357, + -1357,-1357,-1357,-1357,-1357,-1357,-1357,-1357,-1357,-1357, + -1357,-1357,-1357,-1357,-1357,-1357,-1357,-1357,-1357,-1357, + -1357,-1357,-1357,-1357,-1357,-1357,-1357,-1357,-1357,-1357, + + -1357,-1357,-1357,-1357,-1357,-1357,-1357,-1357,-1357,-1357, + -1357,-1357,-1357,-1357,-1357,-1357,-1357,-1357,-1357,-1357, + -1357,-1357,-1357,-1357,-1357,-1357,-1357,-1357,-1357,-1357, + -1357,-1357,-1357,-1357,-1357,-1357,-1357,-1357 + }, + + { + 69,-1358,-1358,-1358,-1358,-1358,-1358,-1358,-1358,-1358, + -1358,-1358,-1358,-1358,-1358,-1358,-1358,-1358,-1358,-1358, + -1358,-1358,-1358,-1358,-1358,-1358,-1358,-1358,-1358,-1358, + -1358,-1358,-1358,-1358,-1358,-1358,-1358,-1358,-1358,-1358, + -1358,-1358,-1358,-1358,-1358,-1358,-1358,-1358, 1358, 1358, + 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358,-1358,-1358, + + -1358,-1358,-1358,-1358,-1358,-1358,-1358,-1358,-1358,-1358, + -1358,-1358,-1358,-1358,-1358,-1358,-1358,-1358,-1358,-1358, + -1358,-1358,-1358,-1358,-1358,-1358,-1358,-1358,-1358,-1358, + -1358,-1358,-1358,-1358,-1358,-1358,-1358,-1358,-1358,-1358, + -1358,-1358,-1358,-1358,-1358,-1358,-1358,-1358,-1358,-1358, + -1358,-1358,-1358,-1358,-1358,-1358,-1358,-1358,-1358,-1358, + -1358,-1358,-1358,-1358,-1358,-1358,-1358,-1358 + }, + + { + 69,-1359,-1359,-1359,-1359,-1359,-1359,-1359,-1359,-1359, + -1359,-1359,-1359,-1359,-1359,-1359,-1359,-1359,-1359,-1359, + -1359,-1359,-1359,-1359,-1359,-1359,-1359,-1359,-1359,-1359, + + -1359,-1359, 1359,-1359,-1359,-1359,-1359,-1359,-1359,-1359, + -1359,-1359,-1359, 1360,-1359, 1360,-1359,-1359, 1361, 1361, + 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361,-1359,-1359, + -1359,-1359,-1359,-1359,-1359,-1359,-1359,-1359,-1359,-1359, + -1359,-1359,-1359,-1359,-1359,-1359,-1359,-1359,-1359,-1359, + -1359,-1359,-1359,-1359,-1359,-1359,-1359,-1359,-1359,-1359, + -1359,-1359,-1359,-1359,-1359,-1359,-1359,-1359,-1359,-1359, + -1359,-1359,-1359,-1359,-1359,-1359,-1359,-1359,-1359,-1359, + -1359,-1359,-1359,-1359,-1359,-1359,-1359,-1359,-1359,-1359, + -1359,-1359,-1359,-1359,-1359,-1359,-1359,-1359 + + }, + + { + 69,-1360,-1360,-1360,-1360,-1360,-1360,-1360,-1360,-1360, + -1360,-1360,-1360,-1360,-1360,-1360,-1360,-1360,-1360,-1360, + -1360,-1360,-1360,-1360,-1360,-1360,-1360,-1360,-1360,-1360, + -1360,-1360,-1360,-1360,-1360,-1360,-1360,-1360,-1360,-1360, + -1360,-1360,-1360,-1360,-1360,-1360,-1360,-1360, 1361, 1361, + 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361,-1360,-1360, + -1360,-1360,-1360,-1360,-1360,-1360,-1360,-1360,-1360,-1360, + -1360,-1360,-1360,-1360,-1360,-1360,-1360,-1360,-1360,-1360, + -1360,-1360,-1360,-1360,-1360,-1360,-1360,-1360,-1360,-1360, + -1360,-1360,-1360,-1360,-1360,-1360,-1360,-1360,-1360,-1360, + + -1360,-1360,-1360,-1360,-1360,-1360,-1360,-1360,-1360,-1360, + -1360,-1360,-1360,-1360,-1360,-1360,-1360,-1360,-1360,-1360, + -1360,-1360,-1360,-1360,-1360,-1360,-1360,-1360 + }, + + { + 69,-1361,-1361,-1361,-1361,-1361,-1361,-1361,-1361,-1361, + -1361,-1361,-1361,-1361,-1361,-1361,-1361,-1361,-1361,-1361, + -1361,-1361,-1361,-1361,-1361,-1361,-1361,-1361,-1361,-1361, + -1361,-1361,-1361,-1361,-1361,-1361,-1361,-1361,-1361,-1361, + -1361,-1361,-1361,-1361,-1361,-1361,-1361,-1361, 1361, 1361, + 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361,-1361,-1361, + -1361,-1361,-1361,-1361,-1361,-1361,-1361,-1361,-1361,-1361, + + -1361,-1361,-1361,-1361,-1361,-1361,-1361,-1361,-1361,-1361, + -1361,-1361,-1361,-1361,-1361,-1361,-1361,-1361,-1361,-1361, + -1361,-1361,-1361,-1361,-1361,-1361,-1361,-1361,-1361,-1361, + -1361,-1361,-1361,-1361,-1361,-1361,-1361,-1361,-1361,-1361, + -1361,-1361,-1361,-1361,-1361,-1361,-1361,-1361,-1361,-1361, + -1361,-1361,-1361,-1361,-1361,-1361,-1361,-1361 + }, + + { + 69,-1362,-1362,-1362,-1362,-1362,-1362,-1362,-1362,-1362, + -1362,-1362,-1362,-1362,-1362,-1362,-1362,-1362,-1362,-1362, + -1362,-1362,-1362,-1362,-1362,-1362,-1362,-1362,-1362,-1362, + -1362,-1362, 1362,-1362,-1362,-1362,-1362,-1362,-1362,-1362, + + -1362,-1362,-1362, 1363,-1362, 1363,-1362,-1362, 1364, 1364, + 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364,-1362,-1362, + -1362,-1362,-1362,-1362,-1362,-1362,-1362,-1362,-1362,-1362, + -1362,-1362,-1362,-1362,-1362,-1362,-1362,-1362,-1362,-1362, + -1362,-1362,-1362,-1362,-1362,-1362,-1362,-1362,-1362,-1362, + -1362,-1362,-1362,-1362,-1362,-1362,-1362,-1362,-1362,-1362, + -1362,-1362,-1362,-1362,-1362,-1362,-1362,-1362,-1362,-1362, + -1362,-1362,-1362,-1362,-1362,-1362,-1362,-1362,-1362,-1362, + -1362,-1362,-1362,-1362,-1362,-1362,-1362,-1362 + }, + + { + 69,-1363,-1363,-1363,-1363,-1363,-1363,-1363,-1363,-1363, + + -1363,-1363,-1363,-1363,-1363,-1363,-1363,-1363,-1363,-1363, + -1363,-1363,-1363,-1363,-1363,-1363,-1363,-1363,-1363,-1363, + -1363,-1363,-1363,-1363,-1363,-1363,-1363,-1363,-1363,-1363, + -1363,-1363,-1363,-1363,-1363,-1363,-1363,-1363, 1364, 1364, + 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364,-1363,-1363, + -1363,-1363,-1363,-1363,-1363,-1363,-1363,-1363,-1363,-1363, + -1363,-1363,-1363,-1363,-1363,-1363,-1363,-1363,-1363,-1363, + -1363,-1363,-1363,-1363,-1363,-1363,-1363,-1363,-1363,-1363, + -1363,-1363,-1363,-1363,-1363,-1363,-1363,-1363,-1363,-1363, + -1363,-1363,-1363,-1363,-1363,-1363,-1363,-1363,-1363,-1363, + + -1363,-1363,-1363,-1363,-1363,-1363,-1363,-1363,-1363,-1363, + -1363,-1363,-1363,-1363,-1363,-1363,-1363,-1363 + }, + + { + 69,-1364,-1364,-1364,-1364,-1364,-1364,-1364,-1364,-1364, + -1364,-1364,-1364,-1364,-1364,-1364,-1364,-1364,-1364,-1364, + -1364,-1364,-1364,-1364,-1364,-1364,-1364,-1364,-1364,-1364, + -1364,-1364,-1364,-1364,-1364,-1364,-1364,-1364,-1364,-1364, + -1364,-1364,-1364,-1364,-1364,-1364,-1364,-1364, 1364, 1364, + 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364,-1364,-1364, + -1364,-1364,-1364,-1364,-1364,-1364,-1364,-1364,-1364,-1364, + -1364,-1364,-1364,-1364,-1364,-1364,-1364,-1364,-1364,-1364, + + -1364,-1364,-1364,-1364,-1364,-1364,-1364,-1364,-1364,-1364, + -1364,-1364,-1364,-1364,-1364,-1364,-1364,-1364,-1364,-1364, + -1364,-1364,-1364,-1364,-1364,-1364,-1364,-1364,-1364,-1364, + -1364,-1364,-1364,-1364,-1364,-1364,-1364,-1364,-1364,-1364, + -1364,-1364,-1364,-1364,-1364,-1364,-1364,-1364 + }, + + { + 69,-1365,-1365,-1365,-1365,-1365,-1365,-1365,-1365,-1365, + -1365,-1365,-1365,-1365,-1365,-1365,-1365,-1365,-1365,-1365, + -1365,-1365,-1365,-1365,-1365,-1365,-1365,-1365,-1365,-1365, + -1365,-1365, 1365,-1365,-1365,-1365,-1365,-1365,-1365, 1366, + -1365,-1365,-1365,-1365,-1365,-1365,-1365,-1365,-1365,-1365, + + -1365,-1365,-1365,-1365,-1365,-1365,-1365,-1365,-1365,-1365, + -1365,-1365,-1365,-1365,-1365,-1365,-1365,-1365,-1365,-1365, + -1365,-1365,-1365,-1365,-1365,-1365,-1365,-1365,-1365,-1365, + -1365,-1365,-1365,-1365,-1365,-1365,-1365,-1365,-1365,-1365, + -1365,-1365,-1365,-1365,-1365,-1365,-1365,-1365,-1365,-1365, + -1365,-1365,-1365,-1365,-1365,-1365,-1365,-1365,-1365,-1365, + -1365,-1365,-1365,-1365,-1365,-1365,-1365,-1365,-1365,-1365, + -1365,-1365,-1365,-1365,-1365,-1365,-1365,-1365 + }, + + { + 69, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, + 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, + + 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, + 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1379, + 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, + 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, + 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, + 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, + 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, + 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, + 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, + 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, + + 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378 + }, + + { + 69,-1367,-1367,-1367,-1367,-1367,-1367,-1367,-1367,-1367, + -1367,-1367,-1367,-1367,-1367,-1367,-1367,-1367,-1367,-1367, + -1367,-1367,-1367,-1367,-1367,-1367,-1367,-1367,-1367,-1367, + -1367,-1367, 1367,-1367,-1367,-1367,-1367,-1367,-1367, 1368, + -1367,-1367,-1367,-1367,-1367,-1367,-1367,-1367,-1367,-1367, + -1367,-1367,-1367,-1367,-1367,-1367,-1367,-1367,-1367,-1367, + -1367,-1367,-1367,-1367,-1367,-1367,-1367,-1367,-1367,-1367, + -1367,-1367,-1367,-1367,-1367,-1367,-1367,-1367,-1367,-1367, + -1367,-1367,-1367,-1367,-1367,-1367,-1367,-1367,-1367,-1367, + + -1367,-1367,-1367,-1367,-1367,-1367,-1367,-1367,-1367,-1367, + -1367,-1367,-1367,-1367,-1367,-1367,-1367,-1367,-1367,-1367, + -1367,-1367,-1367,-1367,-1367,-1367,-1367,-1367,-1367,-1367, + -1367,-1367,-1367,-1367,-1367,-1367,-1367,-1367 + }, + + { + 69, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, + 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, + 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, + 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1381, + 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, + 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, + + 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, + 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, + 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, + 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, + 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, + 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, + 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380 + }, + + { + 69,-1369,-1369,-1369,-1369,-1369,-1369,-1369,-1369,-1369, + -1369,-1369,-1369,-1369,-1369,-1369,-1369,-1369,-1369,-1369, + -1369,-1369,-1369,-1369,-1369,-1369,-1369,-1369,-1369,-1369, + + -1369,-1369, 1369,-1369,-1369,-1369,-1369,-1369,-1369, 1370, + -1369,-1369,-1369,-1369,-1369,-1369,-1369,-1369,-1369,-1369, + -1369,-1369,-1369,-1369,-1369,-1369,-1369,-1369,-1369,-1369, + -1369,-1369,-1369,-1369,-1369,-1369,-1369,-1369,-1369,-1369, + -1369,-1369,-1369,-1369,-1369,-1369,-1369,-1369,-1369,-1369, + -1369,-1369,-1369,-1369,-1369,-1369,-1369,-1369,-1369,-1369, + -1369,-1369,-1369,-1369,-1369,-1369,-1369,-1369,-1369,-1369, + -1369,-1369,-1369,-1369,-1369,-1369,-1369,-1369,-1369,-1369, + -1369,-1369,-1369,-1369,-1369,-1369,-1369,-1369,-1369,-1369, + -1369,-1369,-1369,-1369,-1369,-1369,-1369,-1369 + + }, + + { + 69, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, + 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, + 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, + 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1383, + 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, + 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, + 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, + 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, + 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, + 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, + + 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, + 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, + 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382 + }, + + { + 69,-1371,-1371,-1371,-1371,-1371,-1371,-1371,-1371,-1371, + -1371,-1371,-1371,-1371,-1371,-1371,-1371,-1371,-1371,-1371, + -1371,-1371,-1371,-1371,-1371,-1371,-1371,-1371,-1371,-1371, + -1371,-1371, 1371,-1371,-1371,-1371,-1371,-1371,-1371, 1372, + -1371,-1371,-1371,-1371,-1371,-1371,-1371,-1371,-1371,-1371, + -1371,-1371,-1371,-1371,-1371,-1371,-1371,-1371,-1371,-1371, + -1371,-1371,-1371,-1371,-1371,-1371,-1371,-1371,-1371,-1371, + + -1371,-1371,-1371,-1371,-1371,-1371,-1371,-1371,-1371,-1371, + -1371,-1371,-1371,-1371,-1371,-1371,-1371,-1371,-1371,-1371, + -1371,-1371,-1371,-1371,-1371,-1371,-1371,-1371,-1371,-1371, + -1371,-1371,-1371,-1371,-1371,-1371,-1371,-1371,-1371,-1371, + -1371,-1371,-1371,-1371,-1371,-1371,-1371,-1371,-1371,-1371, + -1371,-1371,-1371,-1371,-1371,-1371,-1371,-1371 + }, + + { + 69, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, + 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, + 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, + 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1385, + + 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, + 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, + 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, + 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, + 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, + 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, + 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, + 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, + 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384 + }, + + { + 69,-1373,-1373,-1373,-1373,-1373,-1373,-1373,-1373,-1373, + + -1373,-1373,-1373,-1373,-1373,-1373,-1373,-1373,-1373,-1373, + -1373,-1373,-1373,-1373,-1373,-1373,-1373,-1373,-1373,-1373, + -1373,-1373, 1373,-1373,-1373,-1373,-1373,-1373,-1373, 1374, + -1373,-1373,-1373,-1373,-1373,-1373,-1373,-1373,-1373,-1373, + -1373,-1373,-1373,-1373,-1373,-1373,-1373,-1373,-1373,-1373, + -1373,-1373,-1373,-1373,-1373,-1373,-1373,-1373,-1373,-1373, + -1373,-1373,-1373,-1373,-1373,-1373,-1373,-1373,-1373,-1373, + -1373,-1373,-1373,-1373,-1373,-1373,-1373,-1373,-1373,-1373, + -1373,-1373,-1373,-1373,-1373,-1373,-1373,-1373,-1373,-1373, + -1373,-1373,-1373,-1373,-1373,-1373,-1373,-1373,-1373,-1373, + + -1373,-1373,-1373,-1373,-1373,-1373,-1373,-1373,-1373,-1373, + -1373,-1373,-1373,-1373,-1373,-1373,-1373,-1373 + }, + + { + 69, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, + 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, + 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, + 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1387, + 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, + 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, + 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, + 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, + + 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, + 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, + 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, + 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, + 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386 + }, + + { + 69,-1375,-1375,-1375,-1375,-1375,-1375,-1375,-1375,-1375, + -1375,-1375,-1375,-1375,-1375,-1375,-1375,-1375,-1375,-1375, + -1375,-1375,-1375,-1375,-1375,-1375,-1375,-1375,-1375,-1375, + -1375,-1375, 1375,-1375,-1375,-1375,-1375,-1375,-1375, 1376, + -1375,-1375,-1375,-1375,-1375,-1375,-1375,-1375,-1375,-1375, + + -1375,-1375,-1375,-1375,-1375,-1375,-1375,-1375,-1375,-1375, + -1375,-1375,-1375,-1375,-1375,-1375,-1375,-1375,-1375,-1375, + -1375,-1375,-1375,-1375,-1375,-1375,-1375,-1375,-1375,-1375, + -1375,-1375,-1375,-1375,-1375,-1375,-1375,-1375,-1375,-1375, + -1375,-1375,-1375,-1375,-1375,-1375,-1375,-1375,-1375,-1375, + -1375,-1375,-1375,-1375,-1375,-1375,-1375,-1375,-1375,-1375, + -1375,-1375,-1375,-1375,-1375,-1375,-1375,-1375,-1375,-1375, + -1375,-1375,-1375,-1375,-1375,-1375,-1375,-1375 + }, + + { + 69, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, + 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, + + 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, + 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1389, + 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, + 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, + 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, + 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, + 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, + 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, + 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, + 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, + + 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388 + }, + + { + 69,-1377,-1377,-1377,-1377,-1377,-1377,-1377,-1377,-1377, + -1377,-1377,-1377,-1377,-1377,-1377,-1377,-1377,-1377,-1377, + -1377,-1377,-1377,-1377,-1377,-1377,-1377,-1377,-1377,-1377, + -1377,-1377, 1390,-1377,-1377,-1377,-1377,-1377,-1377,-1377, + -1377,-1377,-1377,-1377,-1377,-1377,-1377,-1377,-1377,-1377, + -1377,-1377,-1377,-1377,-1377,-1377,-1377,-1377,-1377,-1377, + -1377,-1377,-1377,-1377,-1377,-1377,-1377,-1377,-1377,-1377, + -1377,-1377,-1377,-1377,-1377,-1377,-1377,-1377,-1377,-1377, + -1377,-1377,-1377,-1377,-1377,-1377,-1377,-1377,-1377,-1377, + + -1377,-1377,-1377,-1377,-1377,-1377,-1377,-1377,-1377,-1377, + -1377,-1377,-1377,-1377,-1377,-1377,-1377,-1377,-1377,-1377, + -1377,-1377,-1377,-1377,-1377,-1377,-1377,-1377,-1377,-1377, + -1377,-1377,-1377,-1377,-1377,-1377,-1377,-1377 + }, + + { + 69, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, + 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, + 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, + 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1379, + 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, + 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, + + 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, + 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, + 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, + 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, + 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, + 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, + 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378 + }, + + { + 69,-1379,-1379,-1379,-1379,-1379,-1379,-1379,-1379,-1379, + -1379,-1379,-1379,-1379,-1379,-1379,-1379,-1379,-1379,-1379, + -1379,-1379,-1379,-1379,-1379,-1379,-1379,-1379,-1379,-1379, + + -1379,-1379,-1379,-1379,-1379,-1379,-1379,-1379,-1379, 1378, + -1379,-1379,-1379,-1379,-1379,-1379,-1379,-1379,-1379,-1379, + -1379,-1379,-1379,-1379,-1379,-1379,-1379,-1379,-1379,-1379, + -1379,-1379,-1379,-1379,-1379,-1379,-1379,-1379,-1379,-1379, + -1379,-1379,-1379,-1379,-1379,-1379,-1379,-1379,-1379,-1379, + -1379,-1379,-1379,-1379,-1379,-1379,-1379,-1379,-1379,-1379, + -1379,-1379,-1379,-1379,-1379,-1379,-1379,-1379,-1379,-1379, + -1379,-1379,-1379,-1379,-1379,-1379,-1379,-1379,-1379,-1379, + -1379,-1379,-1379,-1379,-1379,-1379,-1379,-1379,-1379,-1379, + -1379,-1379,-1379,-1379,-1379,-1379,-1379,-1379 + + }, + + { + 69, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, + 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, + 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, + 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1381, + 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, + 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, + 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, + 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, + 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, + 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, + + 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, + 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, + 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380 + }, + + { + 69,-1381,-1381,-1381,-1381,-1381,-1381,-1381,-1381,-1381, + -1381,-1381,-1381,-1381,-1381,-1381,-1381,-1381,-1381,-1381, + -1381,-1381,-1381,-1381,-1381,-1381,-1381,-1381,-1381,-1381, + -1381,-1381,-1381,-1381,-1381,-1381,-1381,-1381,-1381, 1380, + -1381,-1381,-1381,-1381,-1381,-1381,-1381,-1381,-1381,-1381, + -1381,-1381,-1381,-1381,-1381,-1381,-1381,-1381,-1381,-1381, + -1381,-1381,-1381,-1381,-1381,-1381,-1381,-1381,-1381,-1381, + + -1381,-1381,-1381,-1381,-1381,-1381,-1381,-1381,-1381,-1381, + -1381,-1381,-1381,-1381,-1381,-1381,-1381,-1381,-1381,-1381, + -1381,-1381,-1381,-1381,-1381,-1381,-1381,-1381,-1381,-1381, + -1381,-1381,-1381,-1381,-1381,-1381,-1381,-1381,-1381,-1381, + -1381,-1381,-1381,-1381,-1381,-1381,-1381,-1381,-1381,-1381, + -1381,-1381,-1381,-1381,-1381,-1381,-1381,-1381 + }, + + { + 69, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, + 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, + 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, + 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1383, + + 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, + 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, + 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, + 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, + 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, + 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, + 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, + 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, + 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382 + }, + + { + 69,-1383,-1383,-1383,-1383,-1383,-1383,-1383,-1383,-1383, + + -1383,-1383,-1383,-1383,-1383,-1383,-1383,-1383,-1383,-1383, + -1383,-1383,-1383,-1383,-1383,-1383,-1383,-1383,-1383,-1383, + -1383,-1383,-1383,-1383,-1383,-1383,-1383,-1383,-1383, 1382, + -1383,-1383,-1383,-1383,-1383,-1383,-1383,-1383,-1383,-1383, + -1383,-1383,-1383,-1383,-1383,-1383,-1383,-1383,-1383,-1383, + -1383,-1383,-1383,-1383,-1383,-1383,-1383,-1383,-1383,-1383, + -1383,-1383,-1383,-1383,-1383,-1383,-1383,-1383,-1383,-1383, + -1383,-1383,-1383,-1383,-1383,-1383,-1383,-1383,-1383,-1383, + -1383,-1383,-1383,-1383,-1383,-1383,-1383,-1383,-1383,-1383, + -1383,-1383,-1383,-1383,-1383,-1383,-1383,-1383,-1383,-1383, + + -1383,-1383,-1383,-1383,-1383,-1383,-1383,-1383,-1383,-1383, + -1383,-1383,-1383,-1383,-1383,-1383,-1383,-1383 + }, + + { + 69, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, + 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, + 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, + 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1385, + 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, + 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, + 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, + 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, + + 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, + 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, + 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, + 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, + 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384 + }, + + { + 69,-1385,-1385,-1385,-1385,-1385,-1385,-1385,-1385,-1385, + -1385,-1385,-1385,-1385,-1385,-1385,-1385,-1385,-1385,-1385, + -1385,-1385,-1385,-1385,-1385,-1385,-1385,-1385,-1385,-1385, + -1385,-1385,-1385,-1385,-1385,-1385,-1385,-1385,-1385, 1384, + -1385,-1385,-1385,-1385,-1385,-1385,-1385,-1385,-1385,-1385, + + -1385,-1385,-1385,-1385,-1385,-1385,-1385,-1385,-1385,-1385, + -1385,-1385,-1385,-1385,-1385,-1385,-1385,-1385,-1385,-1385, + -1385,-1385,-1385,-1385,-1385,-1385,-1385,-1385,-1385,-1385, + -1385,-1385,-1385,-1385,-1385,-1385,-1385,-1385,-1385,-1385, + -1385,-1385,-1385,-1385,-1385,-1385,-1385,-1385,-1385,-1385, + -1385,-1385,-1385,-1385,-1385,-1385,-1385,-1385,-1385,-1385, + -1385,-1385,-1385,-1385,-1385,-1385,-1385,-1385,-1385,-1385, + -1385,-1385,-1385,-1385,-1385,-1385,-1385,-1385 + }, + + { + 69, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, + 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, + + 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, + 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1387, + 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, + 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, + 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, + 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, + 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, + 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, + 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, + 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, + + 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386 + }, + + { + 69,-1387,-1387,-1387,-1387,-1387,-1387,-1387,-1387,-1387, + -1387,-1387,-1387,-1387,-1387,-1387,-1387,-1387,-1387,-1387, + -1387,-1387,-1387,-1387,-1387,-1387,-1387,-1387,-1387,-1387, + -1387,-1387,-1387,-1387,-1387,-1387,-1387,-1387,-1387, 1386, + -1387,-1387,-1387,-1387,-1387,-1387,-1387,-1387,-1387,-1387, + -1387,-1387,-1387,-1387,-1387,-1387,-1387,-1387,-1387,-1387, + -1387,-1387,-1387,-1387,-1387,-1387,-1387,-1387,-1387,-1387, + -1387,-1387,-1387,-1387,-1387,-1387,-1387,-1387,-1387,-1387, + -1387,-1387,-1387,-1387,-1387,-1387,-1387,-1387,-1387,-1387, + + -1387,-1387,-1387,-1387,-1387,-1387,-1387,-1387,-1387,-1387, + -1387,-1387,-1387,-1387,-1387,-1387,-1387,-1387,-1387,-1387, + -1387,-1387,-1387,-1387,-1387,-1387,-1387,-1387,-1387,-1387, + -1387,-1387,-1387,-1387,-1387,-1387,-1387,-1387 + }, + + { + 69, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, + 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, + 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, + 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1389, + 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, + 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, + + 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, + 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, + 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, + 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, + 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, + 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, + 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388 + }, + + { + 69,-1389,-1389,-1389,-1389,-1389,-1389,-1389,-1389,-1389, + -1389,-1389,-1389,-1389,-1389,-1389,-1389,-1389,-1389,-1389, + -1389,-1389,-1389,-1389,-1389,-1389,-1389,-1389,-1389,-1389, + + -1389,-1389,-1389,-1389,-1389,-1389,-1389,-1389,-1389, 1388, + -1389,-1389,-1389,-1389,-1389,-1389,-1389,-1389,-1389,-1389, + -1389,-1389,-1389,-1389,-1389,-1389,-1389,-1389,-1389,-1389, + -1389,-1389,-1389,-1389,-1389,-1389,-1389,-1389,-1389,-1389, + -1389,-1389,-1389,-1389,-1389,-1389,-1389,-1389,-1389,-1389, + -1389,-1389,-1389,-1389,-1389,-1389,-1389,-1389,-1389,-1389, + -1389,-1389,-1389,-1389,-1389,-1389,-1389,-1389,-1389,-1389, + -1389,-1389,-1389,-1389,-1389,-1389,-1389,-1389,-1389,-1389, + -1389,-1389,-1389,-1389,-1389,-1389,-1389,-1389,-1389,-1389, + -1389,-1389,-1389,-1389,-1389,-1389,-1389,-1389 + + }, + + { + 69,-1390,-1390,-1390,-1390,-1390,-1390,-1390,-1390,-1390, + -1390,-1390,-1390,-1390,-1390,-1390,-1390,-1390,-1390,-1390, + -1390,-1390,-1390,-1390,-1390,-1390,-1390,-1390,-1390,-1390, + -1390,-1390, 1391,-1390,-1390,-1390,-1390,-1390,-1390,-1390, + -1390,-1390,-1390,-1390,-1390,-1390,-1390,-1390,-1390,-1390, + -1390,-1390,-1390,-1390,-1390,-1390,-1390,-1390,-1390,-1390, + -1390,-1390,-1390,-1390,-1390,-1390,-1390,-1390,-1390,-1390, + -1390,-1390,-1390,-1390,-1390,-1390,-1390,-1390,-1390,-1390, + -1390,-1390,-1390,-1390,-1390,-1390,-1390,-1390,-1390,-1390, + -1390,-1390,-1390,-1390,-1390,-1390,-1390,-1390,-1390,-1390, + + -1390,-1390,-1390,-1390,-1390,-1390,-1390,-1390,-1390,-1390, + -1390,-1390,-1390,-1390,-1390,-1390,-1390,-1390,-1390,-1390, + -1390,-1390,-1390,-1390,-1390,-1390,-1390,-1390 + }, + + { + 69,-1391,-1391,-1391,-1391,-1391,-1391,-1391,-1391,-1391, + -1391,-1391,-1391,-1391,-1391,-1391,-1391,-1391,-1391,-1391, + -1391,-1391,-1391,-1391,-1391,-1391,-1391,-1391,-1391,-1391, + -1391,-1391, 1392,-1391,-1391,-1391,-1391,-1391,-1391,-1391, + -1391,-1391,-1391,-1391,-1391,-1391,-1391,-1391,-1391,-1391, + -1391,-1391,-1391,-1391,-1391,-1391,-1391,-1391,-1391,-1391, + -1391,-1391,-1391,-1391,-1391,-1391,-1391,-1391,-1391,-1391, + + -1391,-1391,-1391,-1391,-1391,-1391,-1391,-1391,-1391,-1391, + -1391,-1391,-1391,-1391,-1391,-1391,-1391,-1391,-1391,-1391, + -1391,-1391,-1391,-1391,-1391,-1391,-1391,-1391,-1391,-1391, + -1391,-1391,-1391,-1391,-1391,-1391,-1391,-1391,-1391,-1391, + -1391,-1391,-1391,-1391,-1391,-1391,-1391,-1391,-1391,-1391, + -1391,-1391,-1391,-1391,-1391,-1391,-1391,-1391 + }, + + { + 69,-1392,-1392,-1392,-1392,-1392,-1392,-1392,-1392,-1392, + -1392,-1392,-1392,-1392,-1392,-1392,-1392,-1392,-1392,-1392, + -1392,-1392,-1392,-1392,-1392,-1392,-1392,-1392,-1392,-1392, + -1392,-1392, 1393,-1392,-1392,-1392,-1392,-1392,-1392,-1392, + + -1392,-1392,-1392,-1392,-1392,-1392,-1392,-1392,-1392,-1392, + -1392,-1392,-1392,-1392,-1392,-1392,-1392,-1392,-1392,-1392, + -1392,-1392,-1392,-1392,-1392,-1392,-1392,-1392,-1392,-1392, + -1392,-1392,-1392,-1392,-1392,-1392,-1392,-1392,-1392,-1392, + -1392,-1392,-1392,-1392,-1392,-1392,-1392,-1392,-1392,-1392, + -1392,-1392,-1392,-1392,-1392,-1392,-1392,-1392,-1392,-1392, + -1392,-1392,-1392,-1392,-1392,-1392,-1392,-1392,-1392,-1392, + -1392,-1392,-1392,-1392,-1392,-1392,-1392,-1392,-1392,-1392, + -1392,-1392,-1392,-1392,-1392,-1392,-1392,-1392 + }, + + { + 69,-1393,-1393,-1393,-1393,-1393,-1393,-1393,-1393,-1393, + + -1393,-1393,-1393,-1393,-1393,-1393,-1393,-1393,-1393,-1393, + -1393,-1393,-1393,-1393,-1393,-1393,-1393,-1393,-1393,-1393, + -1393,-1393, 1394,-1393,-1393,-1393,-1393,-1393,-1393,-1393, + -1393,-1393,-1393,-1393,-1393,-1393,-1393,-1393,-1393,-1393, + -1393,-1393,-1393,-1393,-1393,-1393,-1393,-1393,-1393,-1393, + -1393,-1393,-1393,-1393,-1393,-1393,-1393,-1393,-1393,-1393, + -1393,-1393,-1393,-1393,-1393,-1393,-1393,-1393,-1393,-1393, + -1393,-1393,-1393,-1393,-1393,-1393,-1393,-1393,-1393,-1393, + -1393,-1393,-1393,-1393,-1393,-1393,-1393,-1393,-1393,-1393, + -1393,-1393,-1393,-1393,-1393,-1393,-1393,-1393,-1393,-1393, + + -1393,-1393,-1393,-1393,-1393,-1393,-1393,-1393,-1393,-1393, + -1393,-1393,-1393,-1393,-1393,-1393,-1393,-1393 + }, + + { + 69,-1394,-1394,-1394,-1394,-1394,-1394,-1394,-1394,-1394, + -1394,-1394,-1394,-1394,-1394,-1394,-1394,-1394,-1394,-1394, + -1394,-1394,-1394,-1394,-1394,-1394,-1394,-1394,-1394,-1394, + -1394,-1394, 1395,-1394,-1394,-1394,-1394,-1394,-1394,-1394, + -1394,-1394,-1394,-1394,-1394,-1394,-1394,-1394,-1394,-1394, + -1394,-1394,-1394,-1394,-1394,-1394,-1394,-1394,-1394,-1394, + -1394,-1394,-1394,-1394,-1394,-1394,-1394,-1394,-1394,-1394, + -1394,-1394,-1394,-1394,-1394,-1394,-1394,-1394,-1394,-1394, + + -1394,-1394,-1394,-1394,-1394,-1394,-1394,-1394,-1394,-1394, + -1394,-1394,-1394,-1394,-1394,-1394,-1394,-1394,-1394,-1394, + -1394,-1394,-1394,-1394,-1394,-1394,-1394,-1394,-1394,-1394, + -1394,-1394,-1394,-1394,-1394,-1394,-1394,-1394,-1394,-1394, + -1394,-1394,-1394,-1394,-1394,-1394,-1394,-1394 + }, + + { + 69,-1395,-1395,-1395,-1395,-1395,-1395,-1395,-1395,-1395, + -1395,-1395,-1395,-1395,-1395,-1395,-1395,-1395,-1395,-1395, + -1395,-1395,-1395,-1395,-1395,-1395,-1395,-1395,-1395,-1395, + -1395,-1395, 1396,-1395,-1395,-1395,-1395,-1395,-1395,-1395, + -1395,-1395,-1395,-1395,-1395,-1395,-1395,-1395,-1395,-1395, + + -1395,-1395,-1395,-1395,-1395,-1395,-1395,-1395,-1395,-1395, + -1395,-1395,-1395,-1395,-1395,-1395,-1395,-1395,-1395,-1395, + -1395,-1395,-1395,-1395,-1395,-1395,-1395,-1395,-1395,-1395, + -1395,-1395,-1395,-1395,-1395,-1395,-1395,-1395,-1395,-1395, + -1395,-1395,-1395,-1395,-1395,-1395,-1395,-1395,-1395,-1395, + -1395,-1395,-1395,-1395,-1395,-1395,-1395,-1395,-1395,-1395, + -1395,-1395,-1395,-1395,-1395,-1395,-1395,-1395,-1395,-1395, + -1395,-1395,-1395,-1395,-1395,-1395,-1395,-1395 + }, + + { + 69,-1396,-1396,-1396,-1396,-1396,-1396,-1396,-1396,-1396, + -1396,-1396,-1396,-1396,-1396,-1396,-1396,-1396,-1396,-1396, + + -1396,-1396,-1396,-1396,-1396,-1396,-1396,-1396,-1396,-1396, + -1396,-1396, 1397,-1396,-1396,-1396,-1396,-1396,-1396,-1396, + -1396,-1396,-1396,-1396,-1396,-1396,-1396,-1396,-1396,-1396, + -1396,-1396,-1396,-1396,-1396,-1396,-1396,-1396,-1396,-1396, + -1396,-1396,-1396,-1396,-1396,-1396,-1396,-1396,-1396,-1396, + -1396,-1396,-1396,-1396,-1396,-1396,-1396,-1396,-1396,-1396, + -1396,-1396,-1396,-1396,-1396,-1396,-1396,-1396,-1396,-1396, + -1396,-1396,-1396,-1396,-1396,-1396,-1396,-1396,-1396,-1396, + -1396,-1396,-1396,-1396,-1396,-1396,-1396,-1396,-1396,-1396, + -1396,-1396,-1396,-1396,-1396,-1396,-1396,-1396,-1396,-1396, + + -1396,-1396,-1396,-1396,-1396,-1396,-1396,-1396 + }, + + { + 69,-1397,-1397,-1397,-1397,-1397,-1397,-1397,-1397,-1397, + -1397,-1397,-1397,-1397,-1397,-1397,-1397,-1397,-1397,-1397, + -1397,-1397,-1397,-1397,-1397,-1397,-1397,-1397,-1397,-1397, + -1397,-1397, 1398,-1397,-1397,-1397,-1397,-1397,-1397,-1397, + -1397,-1397,-1397,-1397,-1397,-1397,-1397,-1397,-1397,-1397, + -1397,-1397,-1397,-1397,-1397,-1397,-1397,-1397,-1397,-1397, + -1397,-1397,-1397,-1397,-1397,-1397,-1397,-1397,-1397,-1397, + -1397,-1397,-1397,-1397,-1397,-1397,-1397,-1397,-1397,-1397, + -1397,-1397,-1397,-1397,-1397,-1397,-1397,-1397,-1397,-1397, + + -1397,-1397,-1397,-1397,-1397,-1397,-1397,-1397,-1397,-1397, + -1397,-1397,-1397,-1397,-1397,-1397,-1397,-1397,-1397,-1397, + -1397,-1397,-1397,-1397,-1397,-1397,-1397,-1397,-1397,-1397, + -1397,-1397,-1397,-1397,-1397,-1397,-1397,-1397 + }, + + { + 69,-1398,-1398,-1398,-1398,-1398,-1398,-1398,-1398,-1398, + -1398,-1398,-1398,-1398,-1398,-1398,-1398,-1398,-1398,-1398, + -1398,-1398,-1398,-1398,-1398,-1398,-1398,-1398,-1398,-1398, + -1398,-1398, 1399,-1398,-1398,-1398,-1398,-1398,-1398,-1398, + -1398,-1398,-1398,-1398,-1398,-1398,-1398,-1398,-1398,-1398, + -1398,-1398,-1398,-1398,-1398,-1398,-1398,-1398,-1398,-1398, + + -1398,-1398,-1398,-1398,-1398,-1398,-1398,-1398,-1398,-1398, + -1398,-1398,-1398,-1398,-1398,-1398,-1398,-1398,-1398,-1398, + -1398,-1398,-1398,-1398,-1398,-1398,-1398,-1398,-1398,-1398, + -1398,-1398,-1398,-1398,-1398,-1398,-1398,-1398,-1398,-1398, + -1398,-1398,-1398,-1398,-1398,-1398,-1398,-1398,-1398,-1398, + -1398,-1398,-1398,-1398,-1398,-1398,-1398,-1398,-1398,-1398, + -1398,-1398,-1398,-1398,-1398,-1398,-1398,-1398 + }, + + { + 69,-1399,-1399,-1399,-1399,-1399,-1399,-1399,-1399,-1399, + -1399,-1399,-1399,-1399,-1399,-1399,-1399,-1399,-1399,-1399, + -1399,-1399,-1399,-1399,-1399,-1399,-1399,-1399,-1399,-1399, + + -1399,-1399, 1400,-1399,-1399,-1399,-1399,-1399,-1399,-1399, + -1399,-1399,-1399,-1399,-1399,-1399,-1399,-1399,-1399,-1399, + -1399,-1399,-1399,-1399,-1399,-1399,-1399,-1399,-1399,-1399, + -1399,-1399,-1399,-1399,-1399,-1399,-1399,-1399,-1399,-1399, + -1399,-1399,-1399,-1399,-1399,-1399,-1399,-1399,-1399,-1399, + -1399,-1399,-1399,-1399,-1399,-1399,-1399,-1399,-1399,-1399, + -1399,-1399,-1399,-1399,-1399,-1399,-1399,-1399,-1399,-1399, + -1399,-1399,-1399,-1399,-1399,-1399,-1399,-1399,-1399,-1399, + -1399,-1399,-1399,-1399,-1399,-1399,-1399,-1399,-1399,-1399, + -1399,-1399,-1399,-1399,-1399,-1399,-1399,-1399 + + }, + + { + 69,-1400,-1400,-1400,-1400,-1400,-1400,-1400,-1400,-1400, + -1400,-1400,-1400,-1400,-1400,-1400,-1400,-1400,-1400,-1400, + -1400,-1400,-1400,-1400,-1400,-1400,-1400,-1400,-1400,-1400, + -1400,-1400, 1401,-1400,-1400,-1400,-1400,-1400,-1400,-1400, + -1400,-1400,-1400,-1400,-1400,-1400,-1400,-1400,-1400,-1400, + -1400,-1400,-1400,-1400,-1400,-1400,-1400,-1400,-1400,-1400, + -1400,-1400,-1400,-1400,-1400,-1400,-1400,-1400,-1400,-1400, + -1400,-1400,-1400,-1400,-1400,-1400,-1400,-1400,-1400,-1400, + -1400,-1400,-1400,-1400,-1400,-1400,-1400,-1400,-1400,-1400, + -1400,-1400,-1400,-1400,-1400,-1400,-1400,-1400,-1400,-1400, + + -1400,-1400,-1400,-1400,-1400,-1400,-1400,-1400,-1400,-1400, + -1400,-1400,-1400,-1400,-1400,-1400,-1400,-1400,-1400,-1400, + -1400,-1400,-1400,-1400,-1400,-1400,-1400,-1400 + }, + + { + 69,-1401,-1401,-1401,-1401,-1401,-1401,-1401,-1401,-1401, + -1401,-1401,-1401,-1401,-1401,-1401,-1401,-1401,-1401,-1401, + -1401,-1401,-1401,-1401,-1401,-1401,-1401,-1401,-1401,-1401, + -1401,-1401, 1402,-1401,-1401,-1401,-1401,-1401,-1401,-1401, + -1401,-1401,-1401,-1401,-1401,-1401,-1401,-1401,-1401,-1401, + -1401,-1401,-1401,-1401,-1401,-1401,-1401,-1401,-1401,-1401, + -1401,-1401,-1401,-1401,-1401,-1401,-1401,-1401,-1401,-1401, + + -1401,-1401,-1401,-1401,-1401,-1401,-1401,-1401,-1401,-1401, + -1401,-1401,-1401,-1401,-1401,-1401,-1401,-1401,-1401,-1401, + -1401,-1401,-1401,-1401,-1401,-1401,-1401,-1401,-1401,-1401, + -1401,-1401,-1401,-1401,-1401,-1401,-1401,-1401,-1401,-1401, + -1401,-1401,-1401,-1401,-1401,-1401,-1401,-1401,-1401,-1401, + -1401,-1401,-1401,-1401,-1401,-1401,-1401,-1401 + }, + + { + 69,-1402,-1402,-1402,-1402,-1402,-1402,-1402,-1402,-1402, + -1402,-1402,-1402,-1402,-1402,-1402,-1402,-1402,-1402,-1402, + -1402,-1402,-1402,-1402,-1402,-1402,-1402,-1402,-1402,-1402, + -1402,-1402, 1403,-1402,-1402,-1402,-1402,-1402,-1402,-1402, + + -1402,-1402,-1402,-1402,-1402,-1402,-1402,-1402,-1402,-1402, + -1402,-1402,-1402,-1402,-1402,-1402,-1402,-1402,-1402,-1402, + -1402,-1402,-1402,-1402,-1402,-1402,-1402,-1402,-1402,-1402, + -1402,-1402,-1402,-1402,-1402,-1402,-1402,-1402,-1402,-1402, + -1402,-1402,-1402,-1402,-1402,-1402,-1402,-1402,-1402,-1402, + -1402,-1402,-1402,-1402,-1402,-1402,-1402,-1402,-1402,-1402, + -1402,-1402,-1402,-1402,-1402,-1402,-1402,-1402,-1402,-1402, + -1402,-1402,-1402,-1402,-1402,-1402,-1402,-1402,-1402,-1402, + -1402,-1402,-1402,-1402,-1402,-1402,-1402,-1402 + }, + + { + 69,-1403,-1403,-1403,-1403,-1403,-1403,-1403,-1403,-1403, + + -1403,-1403,-1403,-1403,-1403,-1403,-1403,-1403,-1403,-1403, + -1403,-1403,-1403,-1403,-1403,-1403,-1403,-1403,-1403,-1403, + -1403,-1403, 1404,-1403,-1403,-1403,-1403,-1403,-1403,-1403, + -1403,-1403,-1403,-1403,-1403,-1403,-1403,-1403,-1403,-1403, + -1403,-1403,-1403,-1403,-1403,-1403,-1403,-1403,-1403,-1403, + -1403,-1403,-1403,-1403,-1403,-1403,-1403,-1403,-1403,-1403, + -1403,-1403,-1403,-1403,-1403,-1403,-1403,-1403,-1403,-1403, + -1403,-1403,-1403,-1403,-1403,-1403,-1403,-1403,-1403,-1403, + -1403,-1403,-1403,-1403,-1403,-1403,-1403,-1403,-1403,-1403, + -1403,-1403,-1403,-1403,-1403,-1403,-1403,-1403,-1403,-1403, + + -1403,-1403,-1403,-1403,-1403,-1403,-1403,-1403,-1403,-1403, + -1403,-1403,-1403,-1403,-1403,-1403,-1403,-1403 + }, + + { + 69,-1404,-1404,-1404,-1404,-1404,-1404,-1404,-1404,-1404, + -1404,-1404,-1404,-1404,-1404,-1404,-1404,-1404,-1404,-1404, + -1404,-1404,-1404,-1404,-1404,-1404,-1404,-1404,-1404,-1404, + -1404,-1404, 1405,-1404,-1404,-1404,-1404,-1404,-1404,-1404, + -1404,-1404,-1404,-1404,-1404,-1404,-1404,-1404,-1404,-1404, + -1404,-1404,-1404,-1404,-1404,-1404,-1404,-1404,-1404,-1404, + -1404,-1404,-1404,-1404,-1404,-1404,-1404,-1404,-1404,-1404, + -1404,-1404,-1404,-1404,-1404,-1404,-1404,-1404,-1404,-1404, + + -1404,-1404,-1404,-1404,-1404,-1404,-1404,-1404,-1404,-1404, + -1404,-1404,-1404,-1404,-1404,-1404,-1404,-1404,-1404,-1404, + -1404,-1404,-1404,-1404,-1404,-1404,-1404,-1404,-1404,-1404, + -1404,-1404,-1404,-1404,-1404,-1404,-1404,-1404,-1404,-1404, + -1404,-1404,-1404,-1404,-1404,-1404,-1404,-1404 + }, + + { + 69,-1405,-1405,-1405,-1405,-1405,-1405,-1405,-1405,-1405, + -1405,-1405,-1405,-1405,-1405,-1405,-1405,-1405,-1405,-1405, + -1405,-1405,-1405,-1405,-1405,-1405,-1405,-1405,-1405,-1405, + -1405,-1405, 1406,-1405,-1405,-1405,-1405,-1405,-1405,-1405, + -1405,-1405,-1405,-1405,-1405,-1405,-1405,-1405,-1405,-1405, + + -1405,-1405,-1405,-1405,-1405,-1405,-1405,-1405,-1405,-1405, + -1405,-1405,-1405,-1405,-1405,-1405,-1405,-1405,-1405,-1405, + -1405,-1405,-1405,-1405,-1405,-1405,-1405,-1405,-1405,-1405, + -1405,-1405,-1405,-1405,-1405,-1405,-1405,-1405,-1405,-1405, + -1405,-1405,-1405,-1405,-1405,-1405,-1405,-1405,-1405,-1405, + -1405,-1405,-1405,-1405,-1405,-1405,-1405,-1405,-1405,-1405, + -1405,-1405,-1405,-1405,-1405,-1405,-1405,-1405,-1405,-1405, + -1405,-1405,-1405,-1405,-1405,-1405,-1405,-1405 + }, + + { + 69,-1406,-1406,-1406,-1406,-1406,-1406,-1406,-1406,-1406, + -1406,-1406,-1406,-1406,-1406,-1406,-1406,-1406,-1406,-1406, + + -1406,-1406,-1406,-1406,-1406,-1406,-1406,-1406,-1406,-1406, + -1406,-1406, 1407,-1406,-1406,-1406,-1406,-1406,-1406,-1406, + -1406,-1406,-1406,-1406,-1406,-1406,-1406,-1406,-1406,-1406, + -1406,-1406,-1406,-1406,-1406,-1406,-1406,-1406,-1406,-1406, + -1406,-1406,-1406,-1406,-1406,-1406,-1406,-1406,-1406,-1406, + -1406,-1406,-1406,-1406,-1406,-1406,-1406,-1406,-1406,-1406, + -1406,-1406,-1406,-1406,-1406,-1406,-1406,-1406,-1406,-1406, + -1406,-1406,-1406,-1406,-1406,-1406,-1406,-1406,-1406,-1406, + -1406,-1406,-1406,-1406,-1406,-1406,-1406,-1406,-1406,-1406, + -1406,-1406,-1406,-1406,-1406,-1406,-1406,-1406,-1406,-1406, + + -1406,-1406,-1406,-1406,-1406,-1406,-1406,-1406 + }, + + { + 69,-1407,-1407,-1407,-1407,-1407,-1407,-1407,-1407,-1407, + -1407,-1407,-1407,-1407,-1407,-1407,-1407,-1407,-1407,-1407, + -1407,-1407,-1407,-1407,-1407,-1407,-1407,-1407,-1407,-1407, + -1407,-1407, 1408,-1407,-1407,-1407,-1407,-1407,-1407,-1407, + -1407,-1407,-1407,-1407,-1407,-1407,-1407,-1407,-1407,-1407, + -1407,-1407,-1407,-1407,-1407,-1407,-1407,-1407,-1407,-1407, + -1407,-1407,-1407,-1407,-1407,-1407,-1407,-1407,-1407,-1407, + -1407,-1407,-1407,-1407,-1407,-1407,-1407,-1407,-1407,-1407, + -1407,-1407,-1407,-1407,-1407,-1407,-1407,-1407,-1407,-1407, + + -1407,-1407,-1407,-1407,-1407,-1407,-1407,-1407,-1407,-1407, + -1407,-1407,-1407,-1407,-1407,-1407,-1407,-1407,-1407,-1407, + -1407,-1407,-1407,-1407,-1407,-1407,-1407,-1407,-1407,-1407, + -1407,-1407,-1407,-1407,-1407,-1407,-1407,-1407 + }, + + { + 69,-1408,-1408,-1408,-1408,-1408,-1408,-1408,-1408,-1408, + -1408,-1408,-1408,-1408,-1408,-1408,-1408,-1408,-1408,-1408, + -1408,-1408,-1408,-1408,-1408,-1408,-1408,-1408,-1408,-1408, + -1408,-1408, 1409,-1408,-1408,-1408,-1408,-1408,-1408,-1408, + -1408,-1408,-1408,-1408,-1408,-1408,-1408,-1408,-1408,-1408, + -1408,-1408,-1408,-1408,-1408,-1408,-1408,-1408,-1408,-1408, + + -1408,-1408,-1408,-1408,-1408,-1408,-1408,-1408,-1408,-1408, + -1408,-1408,-1408,-1408,-1408,-1408,-1408,-1408,-1408,-1408, + -1408,-1408,-1408,-1408,-1408,-1408,-1408,-1408,-1408,-1408, + -1408,-1408,-1408,-1408,-1408,-1408,-1408,-1408,-1408,-1408, + -1408,-1408,-1408,-1408,-1408,-1408,-1408,-1408,-1408,-1408, + -1408,-1408,-1408,-1408,-1408,-1408,-1408,-1408,-1408,-1408, + -1408,-1408,-1408,-1408,-1408,-1408,-1408,-1408 + }, + + { + 69,-1409,-1409,-1409,-1409,-1409,-1409,-1409,-1409,-1409, + -1409,-1409,-1409,-1409,-1409,-1409,-1409,-1409,-1409,-1409, + -1409,-1409,-1409,-1409,-1409,-1409,-1409,-1409,-1409,-1409, + + -1409,-1409, 1410,-1409,-1409,-1409,-1409,-1409,-1409,-1409, + -1409,-1409,-1409,-1409,-1409,-1409,-1409,-1409,-1409,-1409, + -1409,-1409,-1409,-1409,-1409,-1409,-1409,-1409,-1409,-1409, + -1409,-1409,-1409,-1409,-1409,-1409,-1409,-1409,-1409,-1409, + -1409,-1409,-1409,-1409,-1409,-1409,-1409,-1409,-1409,-1409, + -1409,-1409,-1409,-1409,-1409,-1409,-1409,-1409,-1409,-1409, + -1409,-1409,-1409,-1409,-1409,-1409,-1409,-1409,-1409,-1409, + -1409,-1409,-1409,-1409,-1409,-1409,-1409,-1409,-1409,-1409, + -1409,-1409,-1409,-1409,-1409,-1409,-1409,-1409,-1409,-1409, + -1409,-1409,-1409,-1409,-1409,-1409,-1409,-1409 + + }, + + { + 69,-1410,-1410,-1410,-1410,-1410,-1410,-1410,-1410,-1410, + -1410,-1410,-1410,-1410,-1410,-1410,-1410,-1410,-1410,-1410, + -1410,-1410,-1410,-1410,-1410,-1410,-1410,-1410,-1410,-1410, + -1410,-1410, 1411,-1410,-1410,-1410,-1410,-1410,-1410,-1410, + -1410,-1410,-1410,-1410,-1410,-1410,-1410,-1410,-1410,-1410, + -1410,-1410,-1410,-1410,-1410,-1410,-1410,-1410,-1410,-1410, + -1410,-1410,-1410,-1410,-1410,-1410,-1410,-1410,-1410,-1410, + -1410,-1410,-1410,-1410,-1410,-1410,-1410,-1410,-1410,-1410, + -1410,-1410,-1410,-1410,-1410,-1410,-1410,-1410,-1410,-1410, + -1410,-1410,-1410,-1410,-1410,-1410,-1410,-1410,-1410,-1410, + + -1410,-1410,-1410,-1410,-1410,-1410,-1410,-1410,-1410,-1410, + -1410,-1410,-1410,-1410,-1410,-1410,-1410,-1410,-1410,-1410, + -1410,-1410,-1410,-1410,-1410,-1410,-1410,-1410 + }, + + { + 69,-1411,-1411,-1411,-1411,-1411,-1411,-1411,-1411,-1411, + -1411,-1411,-1411,-1411,-1411,-1411,-1411,-1411,-1411,-1411, + -1411,-1411,-1411,-1411,-1411,-1411,-1411,-1411,-1411,-1411, + -1411,-1411, 1412,-1411,-1411,-1411,-1411,-1411,-1411,-1411, + -1411,-1411,-1411,-1411,-1411,-1411,-1411,-1411,-1411,-1411, + -1411,-1411,-1411,-1411,-1411,-1411,-1411,-1411,-1411,-1411, + -1411,-1411,-1411,-1411,-1411,-1411,-1411,-1411,-1411,-1411, + + -1411,-1411,-1411,-1411,-1411,-1411,-1411,-1411,-1411,-1411, + -1411,-1411,-1411,-1411,-1411,-1411,-1411,-1411,-1411,-1411, + -1411,-1411,-1411,-1411,-1411,-1411,-1411,-1411,-1411,-1411, + -1411,-1411,-1411,-1411,-1411,-1411,-1411,-1411,-1411,-1411, + -1411,-1411,-1411,-1411,-1411,-1411,-1411,-1411,-1411,-1411, + -1411,-1411,-1411,-1411,-1411,-1411,-1411,-1411 + }, + + { + 69,-1412,-1412,-1412,-1412,-1412,-1412,-1412,-1412,-1412, + -1412,-1412,-1412,-1412,-1412,-1412,-1412,-1412,-1412,-1412, + -1412,-1412,-1412,-1412,-1412,-1412,-1412,-1412,-1412,-1412, + -1412,-1412, 1413,-1412,-1412,-1412,-1412,-1412,-1412,-1412, + + -1412,-1412,-1412,-1412,-1412,-1412,-1412,-1412,-1412,-1412, + -1412,-1412,-1412,-1412,-1412,-1412,-1412,-1412,-1412,-1412, + -1412,-1412,-1412,-1412,-1412,-1412,-1412,-1412,-1412,-1412, + -1412,-1412,-1412,-1412,-1412,-1412,-1412,-1412,-1412,-1412, + -1412,-1412,-1412,-1412,-1412,-1412,-1412,-1412,-1412,-1412, + -1412,-1412,-1412,-1412,-1412,-1412,-1412,-1412,-1412,-1412, + -1412,-1412,-1412,-1412,-1412,-1412,-1412,-1412,-1412,-1412, + -1412,-1412,-1412,-1412,-1412,-1412,-1412,-1412,-1412,-1412, + -1412,-1412,-1412,-1412,-1412,-1412,-1412,-1412 + }, + + { + 69,-1413,-1413,-1413,-1413,-1413,-1413,-1413,-1413,-1413, + + -1413,-1413,-1413,-1413,-1413,-1413,-1413,-1413,-1413,-1413, + -1413,-1413,-1413,-1413,-1413,-1413,-1413,-1413,-1413,-1413, + -1413,-1413, 1414,-1413,-1413,-1413,-1413,-1413,-1413,-1413, + -1413,-1413,-1413,-1413,-1413,-1413,-1413,-1413,-1413,-1413, + -1413,-1413,-1413,-1413,-1413,-1413,-1413,-1413,-1413,-1413, + -1413,-1413,-1413,-1413,-1413,-1413,-1413,-1413,-1413,-1413, + -1413,-1413,-1413,-1413,-1413,-1413,-1413,-1413,-1413,-1413, + -1413,-1413,-1413,-1413,-1413,-1413,-1413,-1413,-1413,-1413, + -1413,-1413,-1413,-1413,-1413,-1413,-1413,-1413,-1413,-1413, + -1413,-1413,-1413,-1413,-1413,-1413,-1413,-1413,-1413,-1413, + + -1413,-1413,-1413,-1413,-1413,-1413,-1413,-1413,-1413,-1413, + -1413,-1413,-1413,-1413,-1413,-1413,-1413,-1413 + }, + + { + 69,-1414,-1414,-1414,-1414,-1414,-1414,-1414,-1414,-1414, + -1414,-1414,-1414,-1414,-1414,-1414,-1414,-1414,-1414,-1414, + -1414,-1414,-1414,-1414,-1414,-1414,-1414,-1414,-1414,-1414, + -1414,-1414, 1415,-1414,-1414,-1414,-1414,-1414,-1414,-1414, + -1414,-1414,-1414,-1414,-1414,-1414,-1414,-1414,-1414,-1414, + -1414,-1414,-1414,-1414,-1414,-1414,-1414,-1414,-1414,-1414, + -1414,-1414,-1414,-1414,-1414,-1414,-1414,-1414,-1414,-1414, + -1414,-1414,-1414,-1414,-1414,-1414,-1414,-1414,-1414,-1414, + + -1414,-1414,-1414,-1414,-1414,-1414,-1414,-1414,-1414,-1414, + -1414,-1414,-1414,-1414,-1414,-1414,-1414,-1414,-1414,-1414, + -1414,-1414,-1414,-1414,-1414,-1414,-1414,-1414,-1414,-1414, + -1414,-1414,-1414,-1414,-1414,-1414,-1414,-1414,-1414,-1414, + -1414,-1414,-1414,-1414,-1414,-1414,-1414,-1414 + }, + + { + 69,-1415,-1415,-1415,-1415,-1415,-1415,-1415,-1415,-1415, + -1415,-1415,-1415,-1415,-1415,-1415,-1415,-1415,-1415,-1415, + -1415,-1415,-1415,-1415,-1415,-1415,-1415,-1415,-1415,-1415, + -1415,-1415, 1416,-1415,-1415,-1415,-1415,-1415,-1415,-1415, + -1415,-1415,-1415,-1415,-1415,-1415,-1415,-1415,-1415,-1415, + + -1415,-1415,-1415,-1415,-1415,-1415,-1415,-1415,-1415,-1415, + -1415,-1415,-1415,-1415,-1415,-1415,-1415,-1415,-1415,-1415, + -1415,-1415,-1415,-1415,-1415,-1415,-1415,-1415,-1415,-1415, + -1415,-1415,-1415,-1415,-1415,-1415,-1415,-1415,-1415,-1415, + -1415,-1415,-1415,-1415,-1415,-1415,-1415,-1415,-1415,-1415, + -1415,-1415,-1415,-1415,-1415,-1415,-1415,-1415,-1415,-1415, + -1415,-1415,-1415,-1415,-1415,-1415,-1415,-1415,-1415,-1415, + -1415,-1415,-1415,-1415,-1415,-1415,-1415,-1415 + }, + + { + 69,-1416,-1416,-1416,-1416,-1416,-1416,-1416,-1416,-1416, + -1416,-1416,-1416,-1416,-1416,-1416,-1416,-1416,-1416,-1416, + + -1416,-1416,-1416,-1416,-1416,-1416,-1416,-1416,-1416,-1416, + -1416,-1416, 1417,-1416,-1416,-1416,-1416,-1416,-1416,-1416, + -1416,-1416,-1416,-1416,-1416,-1416,-1416,-1416,-1416,-1416, + -1416,-1416,-1416,-1416,-1416,-1416,-1416,-1416,-1416,-1416, + -1416,-1416,-1416,-1416,-1416,-1416,-1416,-1416,-1416,-1416, + -1416,-1416,-1416,-1416,-1416,-1416,-1416,-1416,-1416,-1416, + -1416,-1416,-1416,-1416,-1416,-1416,-1416,-1416,-1416,-1416, + -1416,-1416,-1416,-1416,-1416,-1416,-1416,-1416,-1416,-1416, + -1416,-1416,-1416,-1416,-1416,-1416,-1416,-1416,-1416,-1416, + -1416,-1416,-1416,-1416,-1416,-1416,-1416,-1416,-1416,-1416, + + -1416,-1416,-1416,-1416,-1416,-1416,-1416,-1416 + }, + + { + 69,-1417,-1417,-1417,-1417,-1417,-1417,-1417,-1417,-1417, + -1417,-1417,-1417,-1417,-1417,-1417,-1417,-1417,-1417,-1417, + -1417,-1417,-1417,-1417,-1417,-1417,-1417,-1417,-1417,-1417, + -1417,-1417, 1418,-1417,-1417,-1417,-1417,-1417,-1417,-1417, + -1417,-1417,-1417,-1417,-1417,-1417,-1417,-1417,-1417,-1417, + -1417,-1417,-1417,-1417,-1417,-1417,-1417,-1417,-1417,-1417, + -1417,-1417,-1417,-1417,-1417,-1417,-1417,-1417,-1417,-1417, + -1417,-1417,-1417,-1417,-1417,-1417,-1417,-1417,-1417,-1417, + -1417,-1417,-1417,-1417,-1417,-1417,-1417,-1417,-1417,-1417, + + -1417,-1417,-1417,-1417,-1417,-1417,-1417,-1417,-1417,-1417, + -1417,-1417,-1417,-1417,-1417,-1417,-1417,-1417,-1417,-1417, + -1417,-1417,-1417,-1417,-1417,-1417,-1417,-1417,-1417,-1417, + -1417,-1417,-1417,-1417,-1417,-1417,-1417,-1417 + }, + + { + 69,-1418,-1418,-1418,-1418,-1418,-1418,-1418,-1418,-1418, + -1418,-1418,-1418,-1418,-1418,-1418,-1418,-1418,-1418,-1418, + -1418,-1418,-1418,-1418,-1418,-1418,-1418,-1418,-1418,-1418, + -1418,-1418, 1419,-1418,-1418,-1418,-1418,-1418,-1418,-1418, + -1418,-1418,-1418,-1418,-1418,-1418,-1418,-1418,-1418,-1418, + -1418,-1418,-1418,-1418,-1418,-1418,-1418,-1418,-1418,-1418, + + -1418,-1418,-1418,-1418,-1418,-1418,-1418,-1418,-1418,-1418, + -1418,-1418,-1418,-1418,-1418,-1418,-1418,-1418,-1418,-1418, + -1418,-1418,-1418,-1418,-1418,-1418,-1418,-1418,-1418,-1418, + -1418,-1418,-1418,-1418,-1418,-1418,-1418,-1418,-1418,-1418, + -1418,-1418,-1418,-1418,-1418,-1418,-1418,-1418,-1418,-1418, + -1418,-1418,-1418,-1418,-1418,-1418,-1418,-1418,-1418,-1418, + -1418,-1418,-1418,-1418,-1418,-1418,-1418,-1418 + }, + + { + 69,-1419,-1419,-1419,-1419,-1419,-1419,-1419,-1419,-1419, + -1419,-1419,-1419,-1419,-1419,-1419,-1419,-1419,-1419,-1419, + -1419,-1419,-1419,-1419,-1419,-1419,-1419,-1419,-1419,-1419, + + -1419,-1419, 1420,-1419,-1419,-1419,-1419,-1419,-1419,-1419, + -1419,-1419,-1419,-1419,-1419,-1419,-1419,-1419,-1419,-1419, + -1419,-1419,-1419,-1419,-1419,-1419,-1419,-1419,-1419,-1419, + -1419,-1419,-1419,-1419,-1419,-1419,-1419,-1419,-1419,-1419, + -1419,-1419,-1419,-1419,-1419,-1419,-1419,-1419,-1419,-1419, + -1419,-1419,-1419,-1419,-1419,-1419,-1419,-1419,-1419,-1419, + -1419,-1419,-1419,-1419,-1419,-1419,-1419,-1419,-1419,-1419, + -1419,-1419,-1419,-1419,-1419,-1419,-1419,-1419,-1419,-1419, + -1419,-1419,-1419,-1419,-1419,-1419,-1419,-1419,-1419,-1419, + -1419,-1419,-1419,-1419,-1419,-1419,-1419,-1419 + + }, + + { + 69,-1420,-1420,-1420,-1420,-1420,-1420,-1420,-1420,-1420, + -1420,-1420,-1420,-1420,-1420,-1420,-1420,-1420,-1420,-1420, + -1420,-1420,-1420,-1420,-1420,-1420,-1420,-1420,-1420,-1420, + -1420,-1420, 1421,-1420,-1420,-1420,-1420,-1420,-1420,-1420, + -1420,-1420,-1420,-1420,-1420,-1420,-1420,-1420,-1420,-1420, + -1420,-1420,-1420,-1420,-1420,-1420,-1420,-1420,-1420,-1420, + -1420,-1420,-1420,-1420,-1420,-1420,-1420,-1420,-1420,-1420, + -1420,-1420,-1420,-1420,-1420,-1420,-1420,-1420,-1420,-1420, + -1420,-1420,-1420,-1420,-1420,-1420,-1420,-1420,-1420,-1420, + -1420,-1420,-1420,-1420,-1420,-1420,-1420,-1420,-1420,-1420, + + -1420,-1420,-1420,-1420,-1420,-1420,-1420,-1420,-1420,-1420, + -1420,-1420,-1420,-1420,-1420,-1420,-1420,-1420,-1420,-1420, + -1420,-1420,-1420,-1420,-1420,-1420,-1420,-1420 + }, + + { + 69,-1421,-1421,-1421,-1421,-1421,-1421,-1421,-1421,-1421, + -1421,-1421,-1421,-1421,-1421,-1421,-1421,-1421,-1421,-1421, + -1421,-1421,-1421,-1421,-1421,-1421,-1421,-1421,-1421,-1421, + -1421,-1421, 1422,-1421,-1421,-1421,-1421,-1421,-1421,-1421, + -1421,-1421,-1421,-1421,-1421,-1421,-1421,-1421,-1421,-1421, + -1421,-1421,-1421,-1421,-1421,-1421,-1421,-1421,-1421,-1421, + -1421,-1421,-1421,-1421,-1421,-1421,-1421,-1421,-1421,-1421, + + -1421,-1421,-1421,-1421,-1421,-1421,-1421,-1421,-1421,-1421, + -1421,-1421,-1421,-1421,-1421,-1421,-1421,-1421,-1421,-1421, + -1421,-1421,-1421,-1421,-1421,-1421,-1421,-1421,-1421,-1421, + -1421,-1421,-1421,-1421,-1421,-1421,-1421,-1421,-1421,-1421, + -1421,-1421,-1421,-1421,-1421,-1421,-1421,-1421,-1421,-1421, + -1421,-1421,-1421,-1421,-1421,-1421,-1421,-1421 + }, + + { + 69,-1422,-1422,-1422,-1422,-1422,-1422,-1422,-1422,-1422, + -1422,-1422,-1422,-1422,-1422,-1422,-1422,-1422,-1422,-1422, + -1422,-1422,-1422,-1422,-1422,-1422,-1422,-1422,-1422,-1422, + -1422,-1422, 1423,-1422,-1422,-1422,-1422,-1422,-1422,-1422, + + -1422,-1422,-1422,-1422,-1422,-1422,-1422,-1422,-1422,-1422, + -1422,-1422,-1422,-1422,-1422,-1422,-1422,-1422,-1422,-1422, + -1422,-1422,-1422,-1422,-1422,-1422,-1422,-1422,-1422,-1422, + -1422,-1422,-1422,-1422,-1422,-1422,-1422,-1422,-1422,-1422, + -1422,-1422,-1422,-1422,-1422,-1422,-1422,-1422,-1422,-1422, + -1422,-1422,-1422,-1422,-1422,-1422,-1422,-1422,-1422,-1422, + -1422,-1422,-1422,-1422,-1422,-1422,-1422,-1422,-1422,-1422, + -1422,-1422,-1422,-1422,-1422,-1422,-1422,-1422,-1422,-1422, + -1422,-1422,-1422,-1422,-1422,-1422,-1422,-1422 + }, + + { + 69,-1423,-1423,-1423,-1423,-1423,-1423,-1423,-1423,-1423, + + -1423,-1423,-1423,-1423,-1423,-1423,-1423,-1423,-1423,-1423, + -1423,-1423,-1423,-1423,-1423,-1423,-1423,-1423,-1423,-1423, + -1423,-1423, 1424,-1423,-1423,-1423,-1423,-1423,-1423,-1423, + -1423,-1423,-1423,-1423,-1423,-1423,-1423,-1423,-1423,-1423, + -1423,-1423,-1423,-1423,-1423,-1423,-1423,-1423,-1423,-1423, + -1423,-1423,-1423,-1423,-1423,-1423,-1423,-1423,-1423,-1423, + -1423,-1423,-1423,-1423,-1423,-1423,-1423,-1423,-1423,-1423, + -1423,-1423,-1423,-1423,-1423,-1423,-1423,-1423,-1423,-1423, + -1423,-1423,-1423,-1423,-1423,-1423,-1423,-1423,-1423,-1423, + -1423,-1423,-1423,-1423,-1423,-1423,-1423,-1423,-1423,-1423, + + -1423,-1423,-1423,-1423,-1423,-1423,-1423,-1423,-1423,-1423, + -1423,-1423,-1423,-1423,-1423,-1423,-1423,-1423 + }, + + { + 69,-1424,-1424,-1424,-1424,-1424,-1424,-1424,-1424,-1424, + -1424,-1424,-1424,-1424,-1424,-1424,-1424,-1424,-1424,-1424, + -1424,-1424,-1424,-1424,-1424,-1424,-1424,-1424,-1424,-1424, + -1424,-1424, 1425,-1424,-1424,-1424,-1424,-1424,-1424,-1424, + -1424,-1424,-1424,-1424,-1424,-1424,-1424,-1424,-1424,-1424, + -1424,-1424,-1424,-1424,-1424,-1424,-1424,-1424,-1424,-1424, + -1424,-1424,-1424,-1424,-1424,-1424,-1424,-1424,-1424,-1424, + -1424,-1424,-1424,-1424,-1424,-1424,-1424,-1424,-1424,-1424, + + -1424,-1424,-1424,-1424,-1424,-1424,-1424,-1424,-1424,-1424, + -1424,-1424,-1424,-1424,-1424,-1424,-1424,-1424,-1424,-1424, + -1424,-1424,-1424,-1424,-1424,-1424,-1424,-1424,-1424,-1424, + -1424,-1424,-1424,-1424,-1424,-1424,-1424,-1424,-1424,-1424, + -1424,-1424,-1424,-1424,-1424,-1424,-1424,-1424 + }, + + { + 69,-1425,-1425,-1425,-1425,-1425,-1425,-1425,-1425,-1425, + -1425,-1425,-1425,-1425,-1425,-1425,-1425,-1425,-1425,-1425, + -1425,-1425,-1425,-1425,-1425,-1425,-1425,-1425,-1425,-1425, + -1425,-1425, 1426,-1425,-1425,-1425,-1425,-1425,-1425,-1425, + -1425,-1425,-1425,-1425,-1425,-1425,-1425,-1425,-1425,-1425, + + -1425,-1425,-1425,-1425,-1425,-1425,-1425,-1425,-1425,-1425, + -1425,-1425,-1425,-1425,-1425,-1425,-1425,-1425,-1425,-1425, + -1425,-1425,-1425,-1425,-1425,-1425,-1425,-1425,-1425,-1425, + -1425,-1425,-1425,-1425,-1425,-1425,-1425,-1425,-1425,-1425, + -1425,-1425,-1425,-1425,-1425,-1425,-1425,-1425,-1425,-1425, + -1425,-1425,-1425,-1425,-1425,-1425,-1425,-1425,-1425,-1425, + -1425,-1425,-1425,-1425,-1425,-1425,-1425,-1425,-1425,-1425, + -1425,-1425,-1425,-1425,-1425,-1425,-1425,-1425 + }, + + { + 69,-1426,-1426,-1426,-1426,-1426,-1426,-1426,-1426,-1426, + -1426,-1426,-1426,-1426,-1426,-1426,-1426,-1426,-1426,-1426, + + -1426,-1426,-1426,-1426,-1426,-1426,-1426,-1426,-1426,-1426, + -1426,-1426, 1427,-1426,-1426,-1426,-1426,-1426,-1426,-1426, + -1426,-1426,-1426,-1426,-1426,-1426,-1426,-1426,-1426,-1426, + -1426,-1426,-1426,-1426,-1426,-1426,-1426,-1426,-1426,-1426, + -1426,-1426,-1426,-1426,-1426,-1426,-1426,-1426,-1426,-1426, + -1426,-1426,-1426,-1426,-1426,-1426,-1426,-1426,-1426,-1426, + -1426,-1426,-1426,-1426,-1426,-1426,-1426,-1426,-1426,-1426, + -1426,-1426,-1426,-1426,-1426,-1426,-1426,-1426,-1426,-1426, + -1426,-1426,-1426,-1426,-1426,-1426,-1426,-1426,-1426,-1426, + -1426,-1426,-1426,-1426,-1426,-1426,-1426,-1426,-1426,-1426, + + -1426,-1426,-1426,-1426,-1426,-1426,-1426,-1426 + }, + + { + 69,-1427,-1427,-1427,-1427,-1427,-1427,-1427,-1427,-1427, + -1427,-1427,-1427,-1427,-1427,-1427,-1427,-1427,-1427,-1427, + -1427,-1427,-1427,-1427,-1427,-1427,-1427,-1427,-1427,-1427, + -1427,-1427, 1428,-1427,-1427,-1427,-1427,-1427,-1427,-1427, + -1427,-1427,-1427,-1427,-1427,-1427,-1427,-1427,-1427,-1427, + -1427,-1427,-1427,-1427,-1427,-1427,-1427,-1427,-1427,-1427, + -1427,-1427,-1427,-1427,-1427,-1427,-1427,-1427,-1427,-1427, + -1427,-1427,-1427,-1427,-1427,-1427,-1427,-1427,-1427,-1427, + -1427,-1427,-1427,-1427,-1427,-1427,-1427,-1427,-1427,-1427, + + -1427,-1427,-1427,-1427,-1427,-1427,-1427,-1427,-1427,-1427, + -1427,-1427,-1427,-1427,-1427,-1427,-1427,-1427,-1427,-1427, + -1427,-1427,-1427,-1427,-1427,-1427,-1427,-1427,-1427,-1427, + -1427,-1427,-1427,-1427,-1427,-1427,-1427,-1427 + }, + + { + 69,-1428,-1428,-1428,-1428,-1428,-1428,-1428,-1428,-1428, + -1428,-1428,-1428,-1428,-1428,-1428,-1428,-1428,-1428,-1428, + -1428,-1428,-1428,-1428,-1428,-1428,-1428,-1428,-1428,-1428, + -1428,-1428, 1429,-1428,-1428,-1428,-1428,-1428,-1428,-1428, + -1428,-1428,-1428,-1428,-1428,-1428,-1428,-1428,-1428,-1428, + -1428,-1428,-1428,-1428,-1428,-1428,-1428,-1428,-1428,-1428, + + -1428,-1428,-1428,-1428,-1428,-1428,-1428,-1428,-1428,-1428, + -1428,-1428,-1428,-1428,-1428,-1428,-1428,-1428,-1428,-1428, + -1428,-1428,-1428,-1428,-1428,-1428,-1428,-1428,-1428,-1428, + -1428,-1428,-1428,-1428,-1428,-1428,-1428,-1428,-1428,-1428, + -1428,-1428,-1428,-1428,-1428,-1428,-1428,-1428,-1428,-1428, + -1428,-1428,-1428,-1428,-1428,-1428,-1428,-1428,-1428,-1428, + -1428,-1428,-1428,-1428,-1428,-1428,-1428,-1428 + }, + + { + 69,-1429,-1429,-1429,-1429,-1429,-1429,-1429,-1429,-1429, + -1429,-1429,-1429,-1429,-1429,-1429,-1429,-1429,-1429,-1429, + -1429,-1429,-1429,-1429,-1429,-1429,-1429,-1429,-1429,-1429, + + -1429,-1429, 1430,-1429,-1429,-1429,-1429,-1429,-1429,-1429, + -1429,-1429,-1429,-1429,-1429,-1429,-1429,-1429,-1429,-1429, + -1429,-1429,-1429,-1429,-1429,-1429,-1429,-1429,-1429,-1429, + -1429,-1429,-1429,-1429,-1429,-1429,-1429,-1429,-1429,-1429, + -1429,-1429,-1429,-1429,-1429,-1429,-1429,-1429,-1429,-1429, + -1429,-1429,-1429,-1429,-1429,-1429,-1429,-1429,-1429,-1429, + -1429,-1429,-1429,-1429,-1429,-1429,-1429,-1429,-1429,-1429, + -1429,-1429,-1429,-1429,-1429,-1429,-1429,-1429,-1429,-1429, + -1429,-1429,-1429,-1429,-1429,-1429,-1429,-1429,-1429,-1429, + -1429,-1429,-1429,-1429,-1429,-1429,-1429,-1429 + + }, + + { + 69,-1430,-1430,-1430,-1430,-1430,-1430,-1430,-1430,-1430, + -1430,-1430,-1430,-1430,-1430,-1430,-1430,-1430,-1430,-1430, + -1430,-1430,-1430,-1430,-1430,-1430,-1430,-1430,-1430,-1430, + -1430,-1430, 1431,-1430,-1430,-1430,-1430,-1430,-1430,-1430, + -1430,-1430,-1430,-1430,-1430,-1430,-1430,-1430,-1430,-1430, + -1430,-1430,-1430,-1430,-1430,-1430,-1430,-1430,-1430,-1430, + -1430,-1430,-1430,-1430,-1430,-1430,-1430,-1430,-1430,-1430, + -1430,-1430,-1430,-1430,-1430,-1430,-1430,-1430,-1430,-1430, + -1430,-1430,-1430,-1430,-1430,-1430,-1430,-1430,-1430,-1430, + -1430,-1430,-1430,-1430,-1430,-1430,-1430,-1430,-1430,-1430, + + -1430,-1430,-1430,-1430,-1430,-1430,-1430,-1430,-1430,-1430, + -1430,-1430,-1430,-1430,-1430,-1430,-1430,-1430,-1430,-1430, + -1430,-1430,-1430,-1430,-1430,-1430,-1430,-1430 + }, + + { + 69,-1431,-1431,-1431,-1431,-1431,-1431,-1431,-1431,-1431, + -1431,-1431,-1431,-1431,-1431,-1431,-1431,-1431,-1431,-1431, + -1431,-1431,-1431,-1431,-1431,-1431,-1431,-1431,-1431,-1431, + -1431,-1431, 1432,-1431,-1431,-1431,-1431,-1431,-1431,-1431, + -1431,-1431,-1431,-1431,-1431,-1431,-1431,-1431,-1431,-1431, + -1431,-1431,-1431,-1431,-1431,-1431,-1431,-1431,-1431,-1431, + -1431,-1431,-1431,-1431,-1431,-1431,-1431,-1431,-1431,-1431, + + -1431,-1431,-1431,-1431,-1431,-1431,-1431,-1431,-1431,-1431, + -1431,-1431,-1431,-1431,-1431,-1431,-1431,-1431,-1431,-1431, + -1431,-1431,-1431,-1431,-1431,-1431,-1431,-1431,-1431,-1431, + -1431,-1431,-1431,-1431,-1431,-1431,-1431,-1431,-1431,-1431, + -1431,-1431,-1431,-1431,-1431,-1431,-1431,-1431,-1431,-1431, + -1431,-1431,-1431,-1431,-1431,-1431,-1431,-1431 + }, + + { + 69,-1432,-1432,-1432,-1432,-1432,-1432,-1432,-1432,-1432, + -1432,-1432,-1432,-1432,-1432,-1432,-1432,-1432,-1432,-1432, + -1432,-1432,-1432,-1432,-1432,-1432,-1432,-1432,-1432,-1432, + -1432,-1432, 1433,-1432,-1432,-1432,-1432,-1432,-1432,-1432, + + -1432,-1432,-1432,-1432,-1432,-1432,-1432,-1432,-1432,-1432, + -1432,-1432,-1432,-1432,-1432,-1432,-1432,-1432,-1432,-1432, + -1432,-1432,-1432,-1432,-1432,-1432,-1432,-1432,-1432,-1432, + -1432,-1432,-1432,-1432,-1432,-1432,-1432,-1432,-1432,-1432, + -1432,-1432,-1432,-1432,-1432,-1432,-1432,-1432,-1432,-1432, + -1432,-1432,-1432,-1432,-1432,-1432,-1432,-1432,-1432,-1432, + -1432,-1432,-1432,-1432,-1432,-1432,-1432,-1432,-1432,-1432, + -1432,-1432,-1432,-1432,-1432,-1432,-1432,-1432,-1432,-1432, + -1432,-1432,-1432,-1432,-1432,-1432,-1432,-1432 + }, + + { + 69,-1433,-1433,-1433,-1433,-1433,-1433,-1433,-1433,-1433, + + -1433,-1433,-1433,-1433,-1433,-1433,-1433,-1433,-1433,-1433, + -1433,-1433,-1433,-1433,-1433,-1433,-1433,-1433,-1433,-1433, + -1433,-1433, 1434,-1433,-1433,-1433,-1433,-1433,-1433,-1433, + -1433,-1433,-1433,-1433,-1433,-1433,-1433,-1433,-1433,-1433, + -1433,-1433,-1433,-1433,-1433,-1433,-1433,-1433,-1433,-1433, + -1433,-1433,-1433,-1433,-1433,-1433,-1433,-1433,-1433,-1433, + -1433,-1433,-1433,-1433,-1433,-1433,-1433,-1433,-1433,-1433, + -1433,-1433,-1433,-1433,-1433,-1433,-1433,-1433,-1433,-1433, + -1433,-1433,-1433,-1433,-1433,-1433,-1433,-1433,-1433,-1433, + -1433,-1433,-1433,-1433,-1433,-1433,-1433,-1433,-1433,-1433, + + -1433,-1433,-1433,-1433,-1433,-1433,-1433,-1433,-1433,-1433, + -1433,-1433,-1433,-1433,-1433,-1433,-1433,-1433 + }, + + { + 69,-1434,-1434,-1434,-1434,-1434,-1434,-1434,-1434,-1434, + -1434,-1434,-1434,-1434,-1434,-1434,-1434,-1434,-1434,-1434, + -1434,-1434,-1434,-1434,-1434,-1434,-1434,-1434,-1434,-1434, + -1434,-1434, 1435,-1434,-1434,-1434,-1434,-1434,-1434,-1434, + -1434,-1434,-1434,-1434,-1434,-1434,-1434,-1434,-1434,-1434, + -1434,-1434,-1434,-1434,-1434,-1434,-1434,-1434,-1434,-1434, + -1434,-1434,-1434,-1434,-1434,-1434,-1434,-1434,-1434,-1434, + -1434,-1434,-1434,-1434,-1434,-1434,-1434,-1434,-1434,-1434, + + -1434,-1434,-1434,-1434,-1434,-1434,-1434,-1434,-1434,-1434, + -1434,-1434,-1434,-1434,-1434,-1434,-1434,-1434,-1434,-1434, + -1434,-1434,-1434,-1434,-1434,-1434,-1434,-1434,-1434,-1434, + -1434,-1434,-1434,-1434,-1434,-1434,-1434,-1434,-1434,-1434, + -1434,-1434,-1434,-1434,-1434,-1434,-1434,-1434 + }, + + { + 69,-1435,-1435,-1435,-1435,-1435,-1435,-1435,-1435,-1435, + -1435,-1435,-1435,-1435,-1435,-1435,-1435,-1435,-1435,-1435, + -1435,-1435,-1435,-1435,-1435,-1435,-1435,-1435,-1435,-1435, + -1435,-1435, 1436,-1435,-1435,-1435,-1435,-1435,-1435,-1435, + -1435,-1435,-1435,-1435,-1435,-1435,-1435,-1435,-1435,-1435, + + -1435,-1435,-1435,-1435,-1435,-1435,-1435,-1435,-1435,-1435, + -1435,-1435,-1435,-1435,-1435,-1435,-1435,-1435,-1435,-1435, + -1435,-1435,-1435,-1435,-1435,-1435,-1435,-1435,-1435,-1435, + -1435,-1435,-1435,-1435,-1435,-1435,-1435,-1435,-1435,-1435, + -1435,-1435,-1435,-1435,-1435,-1435,-1435,-1435,-1435,-1435, + -1435,-1435,-1435,-1435,-1435,-1435,-1435,-1435,-1435,-1435, + -1435,-1435,-1435,-1435,-1435,-1435,-1435,-1435,-1435,-1435, + -1435,-1435,-1435,-1435,-1435,-1435,-1435,-1435 + }, + + { + 69,-1436,-1436,-1436,-1436,-1436,-1436,-1436,-1436,-1436, + -1436,-1436,-1436,-1436,-1436,-1436,-1436,-1436,-1436,-1436, + + -1436,-1436,-1436,-1436,-1436,-1436,-1436,-1436,-1436,-1436, + -1436,-1436, 1437,-1436,-1436,-1436,-1436,-1436,-1436,-1436, + -1436,-1436,-1436,-1436,-1436,-1436,-1436,-1436,-1436,-1436, + -1436,-1436,-1436,-1436,-1436,-1436,-1436,-1436,-1436,-1436, + -1436,-1436,-1436,-1436,-1436,-1436,-1436,-1436,-1436,-1436, + -1436,-1436,-1436,-1436,-1436,-1436,-1436,-1436,-1436,-1436, + -1436,-1436,-1436,-1436,-1436,-1436,-1436,-1436,-1436,-1436, + -1436,-1436,-1436,-1436,-1436,-1436,-1436,-1436,-1436,-1436, + -1436,-1436,-1436,-1436,-1436,-1436,-1436,-1436,-1436,-1436, + -1436,-1436,-1436,-1436,-1436,-1436,-1436,-1436,-1436,-1436, + + -1436,-1436,-1436,-1436,-1436,-1436,-1436,-1436 + }, + + { + 69,-1437,-1437,-1437,-1437,-1437,-1437,-1437,-1437,-1437, + -1437,-1437,-1437,-1437,-1437,-1437,-1437,-1437,-1437,-1437, + -1437,-1437,-1437,-1437,-1437,-1437,-1437,-1437,-1437,-1437, + -1437,-1437, 1438,-1437,-1437,-1437,-1437,-1437,-1437,-1437, + -1437,-1437,-1437,-1437,-1437,-1437,-1437,-1437,-1437,-1437, + -1437,-1437,-1437,-1437,-1437,-1437,-1437,-1437,-1437,-1437, + -1437,-1437,-1437,-1437,-1437,-1437,-1437,-1437,-1437,-1437, + -1437,-1437,-1437,-1437,-1437,-1437,-1437,-1437,-1437,-1437, + -1437,-1437,-1437,-1437,-1437,-1437,-1437,-1437,-1437,-1437, + + -1437,-1437,-1437,-1437,-1437,-1437,-1437,-1437,-1437,-1437, + -1437,-1437,-1437,-1437,-1437,-1437,-1437,-1437,-1437,-1437, + -1437,-1437,-1437,-1437,-1437,-1437,-1437,-1437,-1437,-1437, + -1437,-1437,-1437,-1437,-1437,-1437,-1437,-1437 + }, + + { + 69,-1438,-1438,-1438,-1438,-1438,-1438,-1438,-1438,-1438, + -1438,-1438,-1438,-1438,-1438,-1438,-1438,-1438,-1438,-1438, + -1438,-1438,-1438,-1438,-1438,-1438,-1438,-1438,-1438,-1438, + -1438,-1438, 1439,-1438,-1438,-1438,-1438,-1438,-1438,-1438, + -1438,-1438,-1438,-1438,-1438,-1438,-1438,-1438,-1438,-1438, + -1438,-1438,-1438,-1438,-1438,-1438,-1438,-1438,-1438,-1438, + + -1438,-1438,-1438,-1438,-1438,-1438,-1438,-1438,-1438,-1438, + -1438,-1438,-1438,-1438,-1438,-1438,-1438,-1438,-1438,-1438, + -1438,-1438,-1438,-1438,-1438,-1438,-1438,-1438,-1438,-1438, + -1438,-1438,-1438,-1438,-1438,-1438,-1438,-1438,-1438,-1438, + -1438,-1438,-1438,-1438,-1438,-1438,-1438,-1438,-1438,-1438, + -1438,-1438,-1438,-1438,-1438,-1438,-1438,-1438,-1438,-1438, + -1438,-1438,-1438,-1438,-1438,-1438,-1438,-1438 + }, + + { + 69,-1439,-1439,-1439,-1439,-1439,-1439,-1439,-1439,-1439, + -1439,-1439,-1439,-1439,-1439,-1439,-1439,-1439,-1439,-1439, + -1439,-1439,-1439,-1439,-1439,-1439,-1439,-1439,-1439,-1439, + + -1439,-1439, 1440,-1439,-1439,-1439,-1439,-1439,-1439,-1439, + -1439,-1439,-1439,-1439,-1439,-1439,-1439,-1439,-1439,-1439, + -1439,-1439,-1439,-1439,-1439,-1439,-1439,-1439,-1439,-1439, + -1439,-1439,-1439,-1439,-1439,-1439,-1439,-1439,-1439,-1439, + -1439,-1439,-1439,-1439,-1439,-1439,-1439,-1439,-1439,-1439, + -1439,-1439,-1439,-1439,-1439,-1439,-1439,-1439,-1439,-1439, + -1439,-1439,-1439,-1439,-1439,-1439,-1439,-1439,-1439,-1439, + -1439,-1439,-1439,-1439,-1439,-1439,-1439,-1439,-1439,-1439, + -1439,-1439,-1439,-1439,-1439,-1439,-1439,-1439,-1439,-1439, + -1439,-1439,-1439,-1439,-1439,-1439,-1439,-1439 + + }, + + { + 69,-1440,-1440,-1440,-1440,-1440,-1440,-1440,-1440,-1440, + -1440,-1440,-1440,-1440,-1440,-1440,-1440,-1440,-1440,-1440, + -1440,-1440,-1440,-1440,-1440,-1440,-1440,-1440,-1440,-1440, + -1440,-1440, 1441,-1440,-1440,-1440,-1440,-1440,-1440,-1440, + -1440,-1440,-1440,-1440,-1440,-1440,-1440,-1440,-1440,-1440, + -1440,-1440,-1440,-1440,-1440,-1440,-1440,-1440,-1440,-1440, + -1440,-1440,-1440,-1440,-1440,-1440,-1440,-1440,-1440,-1440, + -1440,-1440,-1440,-1440,-1440,-1440,-1440,-1440,-1440,-1440, + -1440,-1440,-1440,-1440,-1440,-1440,-1440,-1440,-1440,-1440, + -1440,-1440,-1440,-1440,-1440,-1440,-1440,-1440,-1440,-1440, + + -1440,-1440,-1440,-1440,-1440,-1440,-1440,-1440,-1440,-1440, + -1440,-1440,-1440,-1440,-1440,-1440,-1440,-1440,-1440,-1440, + -1440,-1440,-1440,-1440,-1440,-1440,-1440,-1440 + }, + + { + 69,-1441,-1441,-1441,-1441,-1441,-1441,-1441,-1441,-1441, + -1441,-1441,-1441,-1441,-1441,-1441,-1441,-1441,-1441,-1441, + -1441,-1441,-1441,-1441,-1441,-1441,-1441,-1441,-1441,-1441, + -1441,-1441, 1442,-1441,-1441,-1441,-1441,-1441,-1441,-1441, + -1441,-1441,-1441,-1441,-1441,-1441,-1441,-1441,-1441,-1441, + -1441,-1441,-1441,-1441,-1441,-1441,-1441,-1441,-1441,-1441, + -1441,-1441,-1441,-1441,-1441,-1441,-1441,-1441,-1441,-1441, + + -1441,-1441,-1441,-1441,-1441,-1441,-1441,-1441,-1441,-1441, + -1441,-1441,-1441,-1441,-1441,-1441,-1441,-1441,-1441,-1441, + -1441,-1441,-1441,-1441,-1441,-1441,-1441,-1441,-1441,-1441, + -1441,-1441,-1441,-1441,-1441,-1441,-1441,-1441,-1441,-1441, + -1441,-1441,-1441,-1441,-1441,-1441,-1441,-1441,-1441,-1441, + -1441,-1441,-1441,-1441,-1441,-1441,-1441,-1441 + }, + + { + 69,-1442,-1442,-1442,-1442,-1442,-1442,-1442,-1442,-1442, + -1442,-1442,-1442,-1442,-1442,-1442,-1442,-1442,-1442,-1442, + -1442,-1442,-1442,-1442,-1442,-1442,-1442,-1442,-1442,-1442, + -1442,-1442, 1443,-1442,-1442,-1442,-1442,-1442,-1442,-1442, + + -1442,-1442,-1442,-1442,-1442,-1442,-1442,-1442,-1442,-1442, + -1442,-1442,-1442,-1442,-1442,-1442,-1442,-1442,-1442,-1442, + -1442,-1442,-1442,-1442,-1442,-1442,-1442,-1442,-1442,-1442, + -1442,-1442,-1442,-1442,-1442,-1442,-1442,-1442,-1442,-1442, + -1442,-1442,-1442,-1442,-1442,-1442,-1442,-1442,-1442,-1442, + -1442,-1442,-1442,-1442,-1442,-1442,-1442,-1442,-1442,-1442, + -1442,-1442,-1442,-1442,-1442,-1442,-1442,-1442,-1442,-1442, + -1442,-1442,-1442,-1442,-1442,-1442,-1442,-1442,-1442,-1442, + -1442,-1442,-1442,-1442,-1442,-1442,-1442,-1442 + }, + + { + 69,-1443,-1443,-1443,-1443,-1443,-1443,-1443,-1443,-1443, + + -1443,-1443,-1443,-1443,-1443,-1443,-1443,-1443,-1443,-1443, + -1443,-1443,-1443,-1443,-1443,-1443,-1443,-1443,-1443,-1443, + -1443,-1443, 1444,-1443,-1443,-1443,-1443,-1443,-1443,-1443, + -1443,-1443,-1443,-1443,-1443,-1443,-1443,-1443,-1443,-1443, + -1443,-1443,-1443,-1443,-1443,-1443,-1443,-1443,-1443,-1443, + -1443,-1443,-1443,-1443,-1443,-1443,-1443,-1443,-1443,-1443, + -1443,-1443,-1443,-1443,-1443,-1443,-1443,-1443,-1443,-1443, + -1443,-1443,-1443,-1443,-1443,-1443,-1443,-1443,-1443,-1443, + -1443,-1443,-1443,-1443,-1443,-1443,-1443,-1443,-1443,-1443, + -1443,-1443,-1443,-1443,-1443,-1443,-1443,-1443,-1443,-1443, + + -1443,-1443,-1443,-1443,-1443,-1443,-1443,-1443,-1443,-1443, + -1443,-1443,-1443,-1443,-1443,-1443,-1443,-1443 + }, + + { + 69,-1444,-1444,-1444,-1444,-1444,-1444,-1444,-1444,-1444, + -1444,-1444,-1444,-1444,-1444,-1444,-1444,-1444,-1444,-1444, + -1444,-1444,-1444,-1444,-1444,-1444,-1444,-1444,-1444,-1444, + -1444,-1444, 1445,-1444,-1444,-1444,-1444,-1444,-1444,-1444, + -1444,-1444,-1444,-1444,-1444,-1444,-1444,-1444,-1444,-1444, + -1444,-1444,-1444,-1444,-1444,-1444,-1444,-1444,-1444,-1444, + -1444,-1444,-1444,-1444,-1444,-1444,-1444,-1444,-1444,-1444, + -1444,-1444,-1444,-1444,-1444,-1444,-1444,-1444,-1444,-1444, + + -1444,-1444,-1444,-1444,-1444,-1444,-1444,-1444,-1444,-1444, + -1444,-1444,-1444,-1444,-1444,-1444,-1444,-1444,-1444,-1444, + -1444,-1444,-1444,-1444,-1444,-1444,-1444,-1444,-1444,-1444, + -1444,-1444,-1444,-1444,-1444,-1444,-1444,-1444,-1444,-1444, + -1444,-1444,-1444,-1444,-1444,-1444,-1444,-1444 + }, + + { + 69,-1445,-1445,-1445,-1445,-1445,-1445,-1445,-1445,-1445, + -1445,-1445,-1445,-1445,-1445,-1445,-1445,-1445,-1445,-1445, + -1445,-1445,-1445,-1445,-1445,-1445,-1445,-1445,-1445,-1445, + -1445,-1445, 1446,-1445,-1445,-1445,-1445,-1445,-1445,-1445, + -1445,-1445,-1445,-1445,-1445,-1445,-1445,-1445,-1445,-1445, + + -1445,-1445,-1445,-1445,-1445,-1445,-1445,-1445,-1445,-1445, + -1445,-1445,-1445,-1445,-1445,-1445,-1445,-1445,-1445,-1445, + -1445,-1445,-1445,-1445,-1445,-1445,-1445,-1445,-1445,-1445, + -1445,-1445,-1445,-1445,-1445,-1445,-1445,-1445,-1445,-1445, + -1445,-1445,-1445,-1445,-1445,-1445,-1445,-1445,-1445,-1445, + -1445,-1445,-1445,-1445,-1445,-1445,-1445,-1445,-1445,-1445, + -1445,-1445,-1445,-1445,-1445,-1445,-1445,-1445,-1445,-1445, + -1445,-1445,-1445,-1445,-1445,-1445,-1445,-1445 + }, + + { + 69,-1446,-1446,-1446,-1446,-1446,-1446,-1446,-1446,-1446, + -1446,-1446,-1446,-1446,-1446,-1446,-1446,-1446,-1446,-1446, + + -1446,-1446,-1446,-1446,-1446,-1446,-1446,-1446,-1446,-1446, + -1446,-1446, 1447,-1446,-1446,-1446,-1446,-1446,-1446,-1446, + -1446,-1446,-1446,-1446,-1446,-1446,-1446,-1446,-1446,-1446, + -1446,-1446,-1446,-1446,-1446,-1446,-1446,-1446,-1446,-1446, + -1446,-1446,-1446,-1446,-1446,-1446,-1446,-1446,-1446,-1446, + -1446,-1446,-1446,-1446,-1446,-1446,-1446,-1446,-1446,-1446, + -1446,-1446,-1446,-1446,-1446,-1446,-1446,-1446,-1446,-1446, + -1446,-1446,-1446,-1446,-1446,-1446,-1446,-1446,-1446,-1446, + -1446,-1446,-1446,-1446,-1446,-1446,-1446,-1446,-1446,-1446, + -1446,-1446,-1446,-1446,-1446,-1446,-1446,-1446,-1446,-1446, + + -1446,-1446,-1446,-1446,-1446,-1446,-1446,-1446 + }, + + { + 69,-1447,-1447,-1447,-1447,-1447,-1447,-1447,-1447,-1447, + -1447,-1447,-1447,-1447,-1447,-1447,-1447,-1447,-1447,-1447, + -1447,-1447,-1447,-1447,-1447,-1447,-1447,-1447,-1447,-1447, + -1447,-1447, 1448,-1447,-1447,-1447,-1447,-1447,-1447,-1447, + -1447,-1447,-1447,-1447,-1447,-1447,-1447,-1447,-1447,-1447, + -1447,-1447,-1447,-1447,-1447,-1447,-1447,-1447,-1447,-1447, + -1447,-1447,-1447,-1447,-1447,-1447,-1447,-1447,-1447,-1447, + -1447,-1447,-1447,-1447,-1447,-1447,-1447,-1447,-1447,-1447, + -1447,-1447,-1447,-1447,-1447,-1447,-1447,-1447,-1447,-1447, + + -1447,-1447,-1447,-1447,-1447,-1447,-1447,-1447,-1447,-1447, + -1447,-1447,-1447,-1447,-1447,-1447,-1447,-1447,-1447,-1447, + -1447,-1447,-1447,-1447,-1447,-1447,-1447,-1447,-1447,-1447, + -1447,-1447,-1447,-1447,-1447,-1447,-1447,-1447 + }, + + { + 69,-1448,-1448,-1448,-1448,-1448,-1448,-1448,-1448,-1448, + -1448,-1448,-1448,-1448,-1448,-1448,-1448,-1448,-1448,-1448, + -1448,-1448,-1448,-1448,-1448,-1448,-1448,-1448,-1448,-1448, + -1448,-1448, 1449,-1448,-1448,-1448,-1448,-1448,-1448,-1448, + -1448,-1448,-1448,-1448,-1448,-1448,-1448,-1448,-1448,-1448, + -1448,-1448,-1448,-1448,-1448,-1448,-1448,-1448,-1448,-1448, + + -1448,-1448,-1448,-1448,-1448,-1448,-1448,-1448,-1448,-1448, + -1448,-1448,-1448,-1448,-1448,-1448,-1448,-1448,-1448,-1448, + -1448,-1448,-1448,-1448,-1448,-1448,-1448,-1448,-1448,-1448, + -1448,-1448,-1448,-1448,-1448,-1448,-1448,-1448,-1448,-1448, + -1448,-1448,-1448,-1448,-1448,-1448,-1448,-1448,-1448,-1448, + -1448,-1448,-1448,-1448,-1448,-1448,-1448,-1448,-1448,-1448, + -1448,-1448,-1448,-1448,-1448,-1448,-1448,-1448 + }, + + { + 69,-1449,-1449,-1449,-1449,-1449,-1449,-1449,-1449,-1449, + -1449,-1449,-1449,-1449,-1449,-1449,-1449,-1449,-1449,-1449, + -1449,-1449,-1449,-1449,-1449,-1449,-1449,-1449,-1449,-1449, + + -1449,-1449, 1450,-1449,-1449,-1449,-1449,-1449,-1449,-1449, + -1449,-1449,-1449,-1449,-1449,-1449,-1449,-1449,-1449,-1449, + -1449,-1449,-1449,-1449,-1449,-1449,-1449,-1449,-1449,-1449, + -1449,-1449,-1449,-1449,-1449,-1449,-1449,-1449,-1449,-1449, + -1449,-1449,-1449,-1449,-1449,-1449,-1449,-1449,-1449,-1449, + -1449,-1449,-1449,-1449,-1449,-1449,-1449,-1449,-1449,-1449, + -1449,-1449,-1449,-1449,-1449,-1449,-1449,-1449,-1449,-1449, + -1449,-1449,-1449,-1449,-1449,-1449,-1449,-1449,-1449,-1449, + -1449,-1449,-1449,-1449,-1449,-1449,-1449,-1449,-1449,-1449, + -1449,-1449,-1449,-1449,-1449,-1449,-1449,-1449 + + }, + + { + 69,-1450,-1450,-1450,-1450,-1450,-1450,-1450,-1450,-1450, + -1450,-1450,-1450,-1450,-1450,-1450,-1450,-1450,-1450,-1450, + -1450,-1450,-1450,-1450,-1450,-1450,-1450,-1450,-1450,-1450, + -1450,-1450, 1451,-1450,-1450,-1450,-1450,-1450,-1450,-1450, + -1450,-1450,-1450,-1450,-1450,-1450,-1450,-1450,-1450,-1450, + -1450,-1450,-1450,-1450,-1450,-1450,-1450,-1450,-1450,-1450, + -1450,-1450,-1450,-1450,-1450,-1450,-1450,-1450,-1450,-1450, + -1450,-1450,-1450,-1450,-1450,-1450,-1450,-1450,-1450,-1450, + -1450,-1450,-1450,-1450,-1450,-1450,-1450,-1450,-1450,-1450, + -1450,-1450,-1450,-1450,-1450,-1450,-1450,-1450,-1450,-1450, + + -1450,-1450,-1450,-1450,-1450,-1450,-1450,-1450,-1450,-1450, + -1450,-1450,-1450,-1450,-1450,-1450,-1450,-1450,-1450,-1450, + -1450,-1450,-1450,-1450,-1450,-1450,-1450,-1450 + }, + + { + 69,-1451,-1451,-1451,-1451,-1451,-1451,-1451,-1451,-1451, + -1451,-1451,-1451,-1451,-1451,-1451,-1451,-1451,-1451,-1451, + -1451,-1451,-1451,-1451,-1451,-1451,-1451,-1451,-1451,-1451, + -1451,-1451, 1452,-1451,-1451,-1451,-1451,-1451,-1451,-1451, + -1451,-1451,-1451,-1451,-1451,-1451,-1451,-1451,-1451,-1451, + -1451,-1451,-1451,-1451,-1451,-1451,-1451,-1451,-1451,-1451, + -1451,-1451,-1451,-1451,-1451,-1451,-1451,-1451,-1451,-1451, + + -1451,-1451,-1451,-1451,-1451,-1451,-1451,-1451,-1451,-1451, + -1451,-1451,-1451,-1451,-1451,-1451,-1451,-1451,-1451,-1451, + -1451,-1451,-1451,-1451,-1451,-1451,-1451,-1451,-1451,-1451, + -1451,-1451,-1451,-1451,-1451,-1451,-1451,-1451,-1451,-1451, + -1451,-1451,-1451,-1451,-1451,-1451,-1451,-1451,-1451,-1451, + -1451,-1451,-1451,-1451,-1451,-1451,-1451,-1451 + }, + + { + 69,-1452,-1452,-1452,-1452,-1452,-1452,-1452,-1452,-1452, + -1452,-1452,-1452,-1452,-1452,-1452,-1452,-1452,-1452,-1452, + -1452,-1452,-1452,-1452,-1452,-1452,-1452,-1452,-1452,-1452, + -1452,-1452, 1453,-1452,-1452,-1452,-1452,-1452,-1452,-1452, + + -1452,-1452,-1452,-1452,-1452,-1452,-1452,-1452,-1452,-1452, + -1452,-1452,-1452,-1452,-1452,-1452,-1452,-1452,-1452,-1452, + -1452,-1452,-1452,-1452,-1452,-1452,-1452,-1452,-1452,-1452, + -1452,-1452,-1452,-1452,-1452,-1452,-1452,-1452,-1452,-1452, + -1452,-1452,-1452,-1452,-1452,-1452,-1452,-1452,-1452,-1452, + -1452,-1452,-1452,-1452,-1452,-1452,-1452,-1452,-1452,-1452, + -1452,-1452,-1452,-1452,-1452,-1452,-1452,-1452,-1452,-1452, + -1452,-1452,-1452,-1452,-1452,-1452,-1452,-1452,-1452,-1452, + -1452,-1452,-1452,-1452,-1452,-1452,-1452,-1452 + }, + + { + 69,-1453,-1453,-1453,-1453,-1453,-1453,-1453,-1453,-1453, + + -1453,-1453,-1453,-1453,-1453,-1453,-1453,-1453,-1453,-1453, + -1453,-1453,-1453,-1453,-1453,-1453,-1453,-1453,-1453,-1453, + -1453,-1453, 1454,-1453,-1453,-1453,-1453,-1453,-1453,-1453, + -1453,-1453,-1453,-1453,-1453,-1453,-1453,-1453,-1453,-1453, + -1453,-1453,-1453,-1453,-1453,-1453,-1453,-1453,-1453,-1453, + -1453,-1453,-1453,-1453,-1453,-1453,-1453,-1453,-1453,-1453, + -1453,-1453,-1453,-1453,-1453,-1453,-1453,-1453,-1453,-1453, + -1453,-1453,-1453,-1453,-1453,-1453,-1453,-1453,-1453,-1453, + -1453,-1453,-1453,-1453,-1453,-1453,-1453,-1453,-1453,-1453, + -1453,-1453,-1453,-1453,-1453,-1453,-1453,-1453,-1453,-1453, + + -1453,-1453,-1453,-1453,-1453,-1453,-1453,-1453,-1453,-1453, + -1453,-1453,-1453,-1453,-1453,-1453,-1453,-1453 + }, + + { + 69,-1454,-1454,-1454,-1454,-1454,-1454,-1454,-1454,-1454, + -1454,-1454,-1454,-1454,-1454,-1454,-1454,-1454,-1454,-1454, + -1454,-1454,-1454,-1454,-1454,-1454,-1454,-1454,-1454,-1454, + -1454,-1454, 1455,-1454,-1454,-1454,-1454,-1454,-1454,-1454, + -1454,-1454,-1454,-1454,-1454,-1454,-1454,-1454,-1454,-1454, + -1454,-1454,-1454,-1454,-1454,-1454,-1454,-1454,-1454,-1454, + -1454,-1454,-1454,-1454,-1454,-1454,-1454,-1454,-1454,-1454, + -1454,-1454,-1454,-1454,-1454,-1454,-1454,-1454,-1454,-1454, + + -1454,-1454,-1454,-1454,-1454,-1454,-1454,-1454,-1454,-1454, + -1454,-1454,-1454,-1454,-1454,-1454,-1454,-1454,-1454,-1454, + -1454,-1454,-1454,-1454,-1454,-1454,-1454,-1454,-1454,-1454, + -1454,-1454,-1454,-1454,-1454,-1454,-1454,-1454,-1454,-1454, + -1454,-1454,-1454,-1454,-1454,-1454,-1454,-1454 + }, + + { + 69,-1455,-1455,-1455,-1455,-1455,-1455,-1455,-1455,-1455, + -1455,-1455,-1455,-1455,-1455,-1455,-1455,-1455,-1455,-1455, + -1455,-1455,-1455,-1455,-1455,-1455,-1455,-1455,-1455,-1455, + -1455,-1455, 1456,-1455,-1455,-1455,-1455,-1455,-1455,-1455, + -1455,-1455,-1455,-1455,-1455,-1455,-1455,-1455,-1455,-1455, + + -1455,-1455,-1455,-1455,-1455,-1455,-1455,-1455,-1455,-1455, + -1455,-1455,-1455,-1455,-1455,-1455,-1455,-1455,-1455,-1455, + -1455,-1455,-1455,-1455,-1455,-1455,-1455,-1455,-1455,-1455, + -1455,-1455,-1455,-1455,-1455,-1455,-1455,-1455,-1455,-1455, + -1455,-1455,-1455,-1455,-1455,-1455,-1455,-1455,-1455,-1455, + -1455,-1455,-1455,-1455,-1455,-1455,-1455,-1455,-1455,-1455, + -1455,-1455,-1455,-1455,-1455,-1455,-1455,-1455,-1455,-1455, + -1455,-1455,-1455,-1455,-1455,-1455,-1455,-1455 + }, + + { + 69,-1456,-1456,-1456,-1456,-1456,-1456,-1456,-1456,-1456, + -1456,-1456,-1456,-1456,-1456,-1456,-1456,-1456,-1456,-1456, + + -1456,-1456,-1456,-1456,-1456,-1456,-1456,-1456,-1456,-1456, + -1456,-1456, 1457,-1456,-1456,-1456,-1456,-1456,-1456,-1456, + -1456,-1456,-1456,-1456,-1456,-1456,-1456,-1456,-1456,-1456, + -1456,-1456,-1456,-1456,-1456,-1456,-1456,-1456,-1456,-1456, + -1456,-1456,-1456,-1456,-1456,-1456,-1456,-1456,-1456,-1456, + -1456,-1456,-1456,-1456,-1456,-1456,-1456,-1456,-1456,-1456, + -1456,-1456,-1456,-1456,-1456,-1456,-1456,-1456,-1456,-1456, + -1456,-1456,-1456,-1456,-1456,-1456,-1456,-1456,-1456,-1456, + -1456,-1456,-1456,-1456,-1456,-1456,-1456,-1456,-1456,-1456, + -1456,-1456,-1456,-1456,-1456,-1456,-1456,-1456,-1456,-1456, + + -1456,-1456,-1456,-1456,-1456,-1456,-1456,-1456 + }, + + { + 69,-1457,-1457,-1457,-1457,-1457,-1457,-1457,-1457,-1457, + -1457,-1457,-1457,-1457,-1457,-1457,-1457,-1457,-1457,-1457, + -1457,-1457,-1457,-1457,-1457,-1457,-1457,-1457,-1457,-1457, + -1457,-1457,-1457,-1457,-1457,-1457,-1457,-1457,-1457,-1457, + -1457,-1457,-1457,-1457,-1457,-1457,-1457,-1457,-1457,-1457, + -1457,-1457,-1457,-1457,-1457,-1457,-1457,-1457,-1457,-1457, + -1457,-1457,-1457,-1457,-1457,-1457,-1457,-1457,-1457,-1457, + -1457,-1457,-1457,-1457,-1457,-1457,-1457,-1457,-1457,-1457, + -1457,-1457,-1457,-1457,-1457,-1457,-1457,-1457,-1457,-1457, + + -1457,-1457,-1457,-1457,-1457,-1457,-1457,-1457,-1457,-1457, + -1457,-1457,-1457,-1457,-1457,-1457,-1457,-1457,-1457,-1457, + -1457,-1457,-1457,-1457,-1457,-1457,-1457,-1457,-1457,-1457, + -1457,-1457,-1457,-1457,-1457,-1457,-1457,-1457 + }, + + } ; + +static yy_state_type yy_get_previous_state ( yyscan_t yyscanner ); +static yy_state_type yy_try_NUL_trans ( yy_state_type current_state , yyscan_t yyscanner); +static int yy_get_next_buffer ( yyscan_t yyscanner ); +static void yynoreturn yy_fatal_error ( const char* msg , yyscan_t yyscanner ); + +/* Done after the current pattern has been matched and before the + * corresponding action - sets up yytext. + */ +#define YY_DO_BEFORE_ACTION \ + yyg->yytext_ptr = yy_bp; \ + yyleng = (int) (yy_cp - yy_bp); \ + yyg->yy_hold_char = *yy_cp; \ + *yy_cp = '\0'; \ + yyg->yy_c_buf_p = yy_cp; +#define YY_NUM_RULES 434 +#define YY_END_OF_BUFFER 435 +/* This struct is not used in this scanner, + but its presence is necessary. */ +struct yy_trans_info + { + flex_int32_t yy_verify; + flex_int32_t yy_nxt; + }; +static const flex_int16_t yy_accept[1458] = + { 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, 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, 435, 434, + 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, + 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, + 205, 205, 205, 224, 224, 216, 216, 225, 225, 217, + + 217, 272, 272, 272, 434, 293, 293, 282, 282, 297, + 297, 308, 308, 309, 309, 375, 375, 375, 410, 410, + 388, 388, 411, 411, 389, 389, 415, 415, 311, 312, + 310, 319, 319, 320, 320, 328, 328, 329, 329, 417, + 417, 419, 419, 418, 421, 421, 421, 420, 423, 423, + 423, 422, 425, 425, 434, 426, 434, 434, 434, 431, + 434, 432, 434, 433, 0, 0, 0, 64, 58, 0, + 0, 0, 0, 0, 21, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 17, 0, 0, 63, + + 57, 0, 0, 0, 0, 0, 0, 0, 0, 23, + 0, 0, 0, 19, 0, 66, 60, 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, 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, 416, 418, 0, 420, 420, 420, 420, + 0, 0, 422, 422, 422, 422, 0, 0, 424, 0, + + 428, 0, 0, 0, 426, 0, 0, 0, 426, 0, + 0, 0, 431, 0, 432, 0, 433, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 65, 59, 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, 0, + 0, 0, 0, 0, 24, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 20, 67, 61, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + + 0, 202, 203, 204, 201, 200, 198, 199, 0, 0, + 0, 206, 207, 208, 213, 212, 0, 0, 0, 209, + 210, 211, 215, 214, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 294, 295, 296, 298, 299, + 300, 305, 304, 301, 302, 303, 307, 306, 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, 412, 413, 414, 0, + 0, 0, 316, 317, 318, 0, 0, 0, 325, 326, + + 327, 420, 0, 420, 422, 0, 422, 0, 0, 427, + 0, 0, 0, 429, 0, 0, 0, 426, 0, 0, + 22, 18, 26, 0, 70, 0, 90, 75, 0, 13, + 44, 80, 39, 34, 85, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 125, 0, 0, 0, + 0, 167, 0, 0, 0, 0, 0, 51, 49, 0, + 131, 0, 0, 0, 0, 0, 0, 163, 0, 54, + 0, 56, 171, 169, 181, 0, 28, 0, 72, 0, + 92, 77, 0, 15, 46, 82, 41, 36, 87, 0, + + 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, + 183, 0, 0, 0, 0, 173, 0, 0, 95, 0, + 0, 0, 179, 218, 219, 220, 221, 222, 223, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 273, 274, 275, 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, 0, 0, 0, 0, 0, 0, 0, + + 0, 0, 0, 0, 0, 313, 314, 315, 321, 322, + 323, 324, 430, 0, 0, 0, 427, 0, 0, 0, + 426, 0, 0, 27, 71, 91, 76, 31, 14, 45, + 81, 40, 35, 86, 0, 0, 0, 0, 0, 0, + 25, 69, 89, 74, 0, 0, 30, 12, 43, 79, + 38, 33, 84, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 128, 0, 0, 0, 0, 0, 144, + 146, 148, 0, 0, 62, 0, 0, 0, 0, 0, + 0, 0, 0, 29, 73, 93, 78, 32, 16, 47, + + 83, 42, 37, 88, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 101, 0, 0, 99, 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, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 281, 278, 280, 276, 277, 279, 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, 0, + 0, 381, 379, 380, 376, 377, 378, 0, 0, 0, + 0, 0, 0, 0, 0, 387, 385, 386, 382, 383, + 384, 0, 427, 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, 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, 0, 261, 263, 266, 270, 262, 265, + 269, 264, 268, 267, 271, 257, 235, 254, 258, 246, + 236, 232, 241, 252, 255, 259, 247, 244, 249, 237, + 233, 242, 230, 239, 251, 253, 256, 260, 248, 245, + 250, 228, 229, 238, 234, 243, 231, 240, 226, 227, + 292, 288, 291, 285, 287, 290, 283, 284, 286, 289, + 365, 367, 370, 374, 366, 369, 373, 368, 372, 371, + 361, 339, 358, 362, 350, 336, 340, 345, 356, 359, + + 363, 348, 351, 353, 334, 337, 341, 346, 343, 355, + 357, 360, 364, 332, 349, 352, 354, 333, 330, 335, + 338, 342, 347, 344, 331, 405, 395, 404, 393, 394, + 403, 390, 391, 392, 402, 409, 401, 408, 399, 400, + 407, 396, 397, 398, 406, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 166, 0, 0, 0, + 117, 115, 0, 0, 0, 0, 50, 48, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 162, 0, 53, 55, + + 0, 168, 170, 180, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 182, 172, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 94, 0, + 0, 0, 0, 0, 0, 0, 178, 190, 195, 134, + 194, 193, 191, 186, 188, 192, 124, 123, 126, 119, + 105, 104, 120, 121, 122, 185, 0, 165, 187, 189, + 113, 112, 116, 114, 133, 130, 129, 132, 127, 107, + 111, 109, 106, 110, 108, 154, 155, 156, 153, 157, + 149, 143, 145, 147, 158, 159, 160, 150, 151, 152, + 161, 102, 164, 52, 184, 138, 0, 141, 118, 142, + + 97, 103, 140, 139, 100, 98, 135, 136, 68, 175, + 176, 177, 174, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 137, 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, 1, 0, 0, 3, 0, 0, 4, 0, 0, + 5, 0, 0, 2, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, + 7, 0, 8, 0, 9, 0, 10, 0, 11, 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, 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, 196 + } ; + +static const yy_state_type yy_NUL_trans[1458] = + { 0, + 70, 71, 91, 91, 94, 94, 96, 96, 98, 98, + 100, 100, 102, 102, 70, 70, 106, 106, 108, 108, + 110, 110, 112, 112, 114, 114, 116, 116, 119, 119, + 121, 121, 123, 123, 125, 125, 127, 127, 129, 129, + 130, 130, 132, 132, 134, 134, 136, 136, 138, 138, + 140, 140, 142, 142, 145, 145, 149, 149, 153, 153, + 155, 155, 159, 159, 161, 161, 163, 163, 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, 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, 0, 0, + 0, 0, 0, 298, 300, 0, 304, 308, 312, 0, + 314, 0, 316, 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, 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, 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, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 298, 0, 300, + + 0, 508, 509, 513, 0, 517, 308, 308, 0, 308, + 308, 312, 0, 314, 0, 316, 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, 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, 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, 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, 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, 508, 509, 0, + 509, 509, 513, 0, 513, 716, 513, 0, 517, 720, + 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, + 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, + + 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, + 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, + 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, 508, 942, 716, 0, 716, 716, 720, + 0, 720, 720, 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, 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, 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, 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, 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, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 942, 0, 942, 942, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1378, 0, 1380, 0, 1382, + 0, 1384, 0, 1386, 0, 1388, 0, 1378, 0, 1380, + 0, 1382, 0, 1384, 0, 1386, 0, 1388, 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, 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 + } ; + +/* The intent behind this definition is that it'll catch + * any uses of REJECT which flex missed. + */ +#define REJECT reject_used_but_not_detected +#define yymore() yymore_used_but_not_detected +#define YY_MORE_ADJ 0 +#define YY_RESTORE_YY_MORE_OFFSET +#line 1 "wcsbth.l" +/*============================================================================ + WCSLIB 8.6 - an implementation of the FITS WCS standard. + Copyright (C) 1995-2025, Mark Calabretta + + This file is part of WCSLIB. + + WCSLIB is free software: you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + WCSLIB is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for + more details. + + You should have received a copy of the GNU Lesser General Public License + along with WCSLIB. If not, see http://www.gnu.org/licenses. + + Author: Mark Calabretta, Australia Telescope National Facility, CSIRO. + http://www.atnf.csiro.au/computing/software/wcs + $Id: wcsbth.c,v 8.6 2026/03/29 13:53:56 mcalabre Exp $ +*============================================================================= +* +* wcsbth.l is a Flex description file containing the definition of a lexical +* scanner for parsing the WCS keyrecords for one or more image arrays and/or +* pixel lists in a FITS binary table header. It can also handle primary image +* and image extension headers. +* +* wcsbth.l requires Flex v2.5.4 or later. Refer to wcshdr.h for a description +* of the user interface and operating notes. +* +* Implementation notes +* -------------------- +* wcsbth() may be invoked with an option that causes it to recognize the +* image-header form of WCS keywords as defaults for each alternate coordinate +* representation (up to 27). By design, with this option enabled wcsbth() can +* also handle primary image and image extension headers, effectively treating +* them as a single-column binary table though with WCS keywords of a different +* form. +* +* NAXIS is always 2 for binary tables, it refers to the two-dimensional nature +* of the table. Thus NAXIS does not count the number of image axes in either +* image arrays or pixels lists and for the latter there is not even a formal +* equivalent of WCSAXESa. Hence NAXIS is always ignored and a first pass +* through the header is required to determine the number of images, the number +* of alternate coordinate representations for each image (up to 27), and the +* number of coordinate axes in each representation; this pass also counts the +* number of iPVn_ma and iPSn_ma or TVk_ma and TSk_ma keywords in each +* representation. +* +* On completion of the first pass, the association between column number and +* axis number is defined for each representation of a pixel list. Memory is +* allocated for an array of the required number of wcsprm structs and each of +* these is initialized appropriately. These structs are filled in the second +* pass. +* +* It is permissible for a scalar table column to contain degenerate (single- +* point) image arrays and simultaneously form one axis of a pixel list. +* +* The parser does not check for duplicated keywords, for most keywords it +* accepts the last encountered. +* +* wcsbth() does not currently handle the Green Bank convention. +* +*===========================================================================*/ +/* Options. */ +#define YY_NO_INPUT 1 +/* Indices for parameterized keywords. */ +/* Alternate coordinate system identifier. */ +/* Keyvalue data types. */ +/* Inline comment syntax. */ +/* Exclusive start states. */ + + + + + + + + + + +#line 113 "wcsbth.l" +#include +#include +#include +#include +#include +#include + +#include "wcs.h" +#include "wcshdr.h" +#include "wcsmath.h" +#include "wcsprintf.h" +#include "wcsutil.h" + + // Codes used for keyvalue data types. +#define INTEGER 0 +#define FLOAT 1 +#define FLOAT2 2 +#define STRING 3 + + // Bit masks used for keyword types: +#define IMGAUX 0x1 // Auxiliary image header, e.g. LONPOLEa or + // DATE-OBS. +#define IMGAXIS 0x2 // Image header with axis number, e.g. + // CTYPEia. +#define IMGHEAD 0x3 // IMGAUX | IMGAXIS, i.e. image header of + // either type. +#define BIMGARR 0x4 // Binary table image array, e.g. iCTYna. +#define PIXLIST 0x8 // Pixel list, e.g. TCTYna. +#define BINTAB 0xC // BIMGARR | PIXLIST, i.e. binary table + // image array (without axis number) or + // pixel list, e.g. LONPna or OBSGXn. + +// User data associated with yyscanner. +struct wcsbth_extra { + // Values passed to YY_INPUT. + char *hdr; + int nkeyrec; + + // Used in preempting the call to exit() by yy_fatal_error(). + jmp_buf abort_jmp_env; +}; + +#define YY_DECL int wcsbth_scanner(char *header, int nkeyrec, int relax, \ + int ctrl, int keysel, int *colsel, int *nreject, int *nwcs, \ + struct wcsprm **wcs, yyscan_t yyscanner) + +#define YY_INPUT(inbuff, count, bufsize) \ + { \ + if (yyextra->nkeyrec) { \ + strncpy(inbuff, yyextra->hdr, 80); \ + inbuff[80] = '\n'; \ + yyextra->hdr += 80; \ + yyextra->nkeyrec--; \ + count = 81; \ + } else { \ + count = YY_NULL; \ + } \ + } + +// Preempt the call to exit() by yy_fatal_error(). +#define exit(status) longjmp(yyextra->abort_jmp_env, status); + +// A convenience macro to get around incompatibilities between unput() and +// yyless(): put yytext followed by a blank back onto the input stream. +#define WCSBTH_PUTBACK \ + sprintf(strtmp, "%s ", yytext); \ + size_t iz = strlen(strtmp); \ + while (iz) unput(strtmp[--iz]); + +// Struct used internally for header bookkeeping. +struct wcsbth_alts { + int ncol, ialt, icol, imgherit; + short int (*arridx)[27]; + short int pixidx[27]; + short int pad1; + unsigned int *pixlist; + + unsigned char (*npv)[27]; + unsigned char (*nps)[27]; + unsigned char pixnpv[27]; + unsigned char pixnps[27]; + unsigned char pad2[2]; +}; + +// Internal helper functions. +static YY_DECL; +static int wcsbth_colax(struct wcsprm *wcs, struct wcsbth_alts *alts, int k, + char a); +static int wcsbth_final(struct wcsbth_alts *alts, int *nwcs, + struct wcsprm **wcs); +static struct wcsprm *wcsbth_idx(struct wcsprm *wcs, struct wcsbth_alts *alts, + int keytype, int n, char a); +static int wcsbth_init1(struct wcsbth_alts *alts, int auxprm, int *nwcs, + struct wcsprm **wcs); +static int wcsbth_pass1(int keytype, int i, int j, int n, int k, char a, + char ptype, struct wcsbth_alts *alts); + +// Helper functions for keywords that require special handling. +static int wcsbth_jdref(double *wptr, const double *jdref); +static int wcsbth_jdrefi(double *wptr, const double *jdrefi); +static int wcsbth_jdreff(double *wptr, const double *jdreff); +static int wcsbth_epoch(double *wptr, const double *epoch); +static int wcsbth_vsource(double *wptr, const double *vsource); + +// Helper functions for keyvalue validity checking. +static int wcsbth_timepixr(double timepixr); + +#line 26317 "wcsbth.c" +#line 26318 "wcsbth.c" + +#define INITIAL 0 +#define CCCCCia 1 +#define iCCCna 2 +#define iCCCCn 3 +#define TCCCna 4 +#define TCCCCn 5 +#define CCi_ja 6 +#define ijCCna 7 +#define TCn_ka 8 +#define TCCn_ka 9 +#define CROTAi 10 +#define iCROTn 11 +#define TCROTn 12 +#define CCi_ma 13 +#define iCn_ma 14 +#define iCCn_ma 15 +#define TCn_ma 16 +#define TCCn_ma 17 +#define PROJPm 18 +#define CCCCCCCC 19 +#define CCCCCCCa 20 +#define CCCCna 21 +#define CCCCCna 22 +#define CCCCn 23 +#define CCCCCn 24 +#define VALUE 25 +#define INTEGER_VAL 26 +#define FLOAT_VAL 27 +#define FLOAT2_VAL 28 +#define STRING_VAL 29 +#define COMMENT 30 +#define DISCARD 31 +#define ERROR 32 +#define FLUSH 33 + +#ifndef YY_NO_UNISTD_H +/* Special case for "unistd.h", since it is non-ANSI. We include it way + * down here because we want the user's section 1 to have been scanned first. + * The user has a chance to override it with an option. + */ +#include +#endif + +#define YY_EXTRA_TYPE struct wcsbth_extra * + +/* Holds the entire state of the reentrant scanner. */ +struct yyguts_t + { + + /* User-defined. Not touched by flex. */ + YY_EXTRA_TYPE yyextra_r; + + /* The rest are the same as the globals declared in the non-reentrant scanner. */ + FILE *yyin_r, *yyout_r; + size_t yy_buffer_stack_top; /**< index of top of stack. */ + size_t yy_buffer_stack_max; /**< capacity of stack. */ + YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */ + char yy_hold_char; + int yy_n_chars; + int yyleng_r; + char *yy_c_buf_p; + int yy_init; + int yy_start; + int yy_did_buffer_switch_on_eof; + int yy_start_stack_ptr; + int yy_start_stack_depth; + int *yy_start_stack; + yy_state_type yy_last_accepting_state; + char* yy_last_accepting_cpos; + + int yylineno_r; + int yy_flex_debug_r; + + char *yytext_r; + int yy_more_flag; + int yy_more_len; + + }; /* end struct yyguts_t */ + +static int yy_init_globals ( yyscan_t yyscanner ); + +int yylex_init (yyscan_t* scanner); + +int yylex_init_extra ( YY_EXTRA_TYPE user_defined, yyscan_t* scanner); + +/* Accessor methods to globals. + These are made visible to non-reentrant scanners for convenience. */ + +int yylex_destroy ( yyscan_t yyscanner ); + +int yyget_debug ( yyscan_t yyscanner ); + +void yyset_debug ( int debug_flag , yyscan_t yyscanner ); + +YY_EXTRA_TYPE yyget_extra ( yyscan_t yyscanner ); + +void yyset_extra ( YY_EXTRA_TYPE user_defined , yyscan_t yyscanner ); + +FILE *yyget_in ( yyscan_t yyscanner ); + +void yyset_in ( FILE * _in_str , yyscan_t yyscanner ); + +FILE *yyget_out ( yyscan_t yyscanner ); + +void yyset_out ( FILE * _out_str , yyscan_t yyscanner ); + + int yyget_leng ( yyscan_t yyscanner ); + +char *yyget_text ( yyscan_t yyscanner ); + +int yyget_lineno ( yyscan_t yyscanner ); + +void yyset_lineno ( int _line_number , yyscan_t yyscanner ); + +int yyget_column ( yyscan_t yyscanner ); + +void yyset_column ( int _column_no , yyscan_t yyscanner ); + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int yywrap ( yyscan_t yyscanner ); +#else +extern int yywrap ( yyscan_t yyscanner ); +#endif +#endif + +#ifndef YY_NO_UNPUT + + static void yyunput ( int c, char *buf_ptr , yyscan_t yyscanner); + +#endif + +#ifndef yytext_ptr +static void yy_flex_strncpy ( char *, const char *, int , yyscan_t yyscanner); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen ( const char * , yyscan_t yyscanner); +#endif + +#ifndef YY_NO_INPUT +#ifdef __cplusplus +static int yyinput ( yyscan_t yyscanner ); +#else +static int input ( yyscan_t yyscanner ); +#endif + +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k */ +#define YY_READ_BUF_SIZE 16384 +#else +#define YY_READ_BUF_SIZE 8192 +#endif /* __ia64__ */ +#endif + +/* Copy whatever the last rule matched to the standard output. */ +#ifndef ECHO +/* This used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite(). + */ +#define ECHO do { if (fwrite( yytext, (size_t) yyleng, 1, yyout )) {} } while (0) +#endif + +/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#ifndef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + errno=0; \ + while ( (result = (int) read( fileno(yyin), buf, (yy_size_t) max_size )) < 0 ) \ + { \ + if( errno != EINTR) \ + { \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + break; \ + } \ + errno=0; \ + clearerr(yyin); \ + }\ +\ + +#endif + +/* No semi-colon after return; correct usage is to write "yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#ifndef yyterminate +#define yyterminate() return YY_NULL +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Report a fatal error. */ +#ifndef YY_FATAL_ERROR +#define YY_FATAL_ERROR(msg) yy_fatal_error( msg , yyscanner) +#endif + +/* end tables serialization structures and prototypes */ + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL_IS_OURS 1 + +extern int yylex (yyscan_t yyscanner); + +#define YY_DECL int yylex (yyscan_t yyscanner) +#endif /* !YY_DECL */ + +/* Code executed at the beginning of each rule, after yytext and yyleng + * have been set up. + */ +#ifndef YY_USER_ACTION +#define YY_USER_ACTION +#endif + +/* Code executed at the end of each rule. */ +#ifndef YY_BREAK +#define YY_BREAK /*LINTED*/break; +#endif + +#define YY_RULE_SETUP \ + if ( yyleng > 0 ) \ + YY_CURRENT_BUFFER_LVALUE->yy_at_bol = \ + (yytext[yyleng - 1] == '\n'); \ + YY_USER_ACTION + +/** The main scanner function which does all the work. + */ +YY_DECL +{ + yy_state_type yy_current_state; + char *yy_cp, *yy_bp; + int yy_act; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if ( !yyg->yy_init ) + { + yyg->yy_init = 1; + +#ifdef YY_USER_INIT + YY_USER_INIT; +#endif + + if ( ! yyg->yy_start ) + yyg->yy_start = 1; /* first start state */ + + if ( ! yyin ) + yyin = stdin; + + if ( ! yyout ) + yyout = stdout; + + if ( ! YY_CURRENT_BUFFER ) { + yyensure_buffer_stack (yyscanner); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); + } + + yy_load_buffer_state( yyscanner ); + } + + { +#line 222 "wcsbth.l" + +#line 224 "wcsbth.l" + char *errmsg, errtxt[80], *keyname, strtmp[80]; + int inttmp; + double dbltmp, dbl2tmp[2]; + struct auxprm auxtem; + struct wcsprm wcstem; + + // Initialize returned values. + *nreject = 0; + *nwcs = 0; + *wcs = 0x0; + + // Our handle on the input stream. + char *keyrec = header; + char *hptr = header; + char *keep = 0x0; + + // For keeping tallies of keywords found. + int nvalid = 0; + int nother = 0; + + // Used to flag image header keywords that are always inherited. + int imherit = 1; + + // If strict, then also reject. + if (relax & WCSHDR_strict) relax |= WCSHDR_reject; + + // Keyword indices, as used in the WCS papers, e.g. iVn_ma, TPn_ka. + int i = 0; + int j = 0; + int k = 0; + int n = 0; + int m = 0; + char a = ' '; + + // Header bookkeeping. + struct wcsbth_alts alts; + alts.ncol = 0; + alts.arridx = 0x0; + alts.pixlist = 0x0; + alts.npv = 0x0; + alts.nps = 0x0; + + for (int ialt = 0; ialt < 27; ialt++) { + alts.pixidx[ialt] = 0; + alts.pixnpv[ialt] = 0; + alts.pixnps[ialt] = 0; + } + + // For decoding the keyvalue. + int keytype = 0; + int valtype = -1; + void *vptr = 0x0; + + // For keywords that require special handling. + int altlin = 0; + char ptype = ' '; + int (*chekval)(double) = 0x0; + int (*special)(double *, const double *) = 0x0; + struct auxprm *auxp = 0x0; + int auxprm = 0; + int naux = 0; + + // Selection by column number. + int nsel = colsel ? colsel[0] : 0; + int incl = (nsel > 0); + char exclude[1000]; + for (int icol = 0; icol < 1000; icol++) { + exclude[icol] = incl; + } + for (int icol = 1; icol <= abs(nsel); icol++) { + int itmp = colsel[icol]; + if (0 < itmp && itmp < 1000) { + exclude[itmp] = !incl; + } + } + exclude[0] = 0; + + // Selection by keyword type. + if (keysel) { + int itmp = keysel; + keysel = 0; + if (itmp & WCSHDR_IMGHEAD) keysel |= IMGHEAD; + if (itmp & WCSHDR_BIMGARR) keysel |= BIMGARR; + if (itmp & WCSHDR_PIXLIST) keysel |= PIXLIST; + } + if (keysel == 0) { + keysel = IMGHEAD | BINTAB; + } + + // Control variables. + int ipass = 1; + int npass = 2; + + // User data associated with yyscanner. + yyextra->hdr = header; + yyextra->nkeyrec = nkeyrec; + + // Return here via longjmp() invoked by yy_fatal_error(). + if (setjmp(yyextra->abort_jmp_env)) { + return WCSHDRERR_PARSER; + } + + BEGIN(INITIAL); + + +#line 26704 "wcsbth.c" + + while ( /*CONSTCOND*/1 ) /* loops until end-of-file is reached */ + { + yy_cp = yyg->yy_c_buf_p; + + /* Support of yytext. */ + *yy_cp = yyg->yy_hold_char; + + /* yy_bp points to the position in yy_ch_buf of the start of + * the current run. + */ + yy_bp = yy_cp; + + yy_current_state = yyg->yy_start; + yy_current_state += YY_AT_BOL(); +yy_match: + while ( (yy_current_state = yy_nxt[yy_current_state][ YY_SC_TO_UI(*yy_cp) ]) > 0 ) + { + if ( yy_accept[yy_current_state] ) + { + yyg->yy_last_accepting_state = yy_current_state; + yyg->yy_last_accepting_cpos = yy_cp; + } + + ++yy_cp; + } + + yy_current_state = -yy_current_state; + +yy_find_action: + yy_act = yy_accept[yy_current_state]; + + YY_DO_BEFORE_ACTION; + +do_action: /* This label is used only to access EOF actions. */ + + switch ( yy_act ) + { /* beginning of action switch */ + case 0: /* must back up */ + /* undo the effects of YY_DO_BEFORE_ACTION */ + *yy_cp = yyg->yy_hold_char; + yy_cp = yyg->yy_last_accepting_cpos + 1; + yy_current_state = yyg->yy_last_accepting_state; + goto yy_find_action; + +case 1: +YY_RULE_SETUP +#line 329 "wcsbth.l" +{ + if (ipass == 1) { + if (alts.ncol == 0) { + sscanf(yytext, "TFIELDS = %d", &(alts.ncol)); + BEGIN(FLUSH); + } else { + errmsg = "duplicate or out-of-sequence TFIELDS keyword"; + BEGIN(ERROR); + } + + } else { + BEGIN(FLUSH); + } + } + YY_BREAK +case 2: +YY_RULE_SETUP +#line 344 "wcsbth.l" +{ + if (!(keysel & IMGAXIS)) { + // Ignore this key type. + BEGIN(DISCARD); + + } else { + if (relax & WCSHDR_ALLIMG) { + sscanf(yytext, "WCSAXES%c= %d", &a, &i); + + if (i < 0) { + errmsg = "negative value of WCSAXESa ignored"; + BEGIN(ERROR); + + } else { + valtype = INTEGER; + vptr = 0x0; + + keyname = "WCSAXESa"; + keytype = IMGAXIS; + BEGIN(COMMENT); + } + + } else if (relax & WCSHDR_reject) { + errmsg = "image-header keyword WCSAXESa in binary table"; + BEGIN(ERROR); + + } else { + // Pretend we don't recognize it. + BEGIN(DISCARD); + } + } + } + YY_BREAK +case 3: +#line 378 "wcsbth.l" +case 4: +#line 379 "wcsbth.l" +case 5: +YY_RULE_SETUP +#line 379 "wcsbth.l" +{ + keyname = "WCAXna"; + + // Note that a blank in the sscanf() format string matches zero or + // more of them in the input. + sscanf(yytext, "WCAX%d%c = %d", &n, &a, &i); + + if (!(keysel & BIMGARR) || exclude[n]) { + // Ignore this key type or column. + BEGIN(DISCARD); + + } else if (i < 0) { + errmsg = "negative value of WCSAXESa ignored"; + BEGIN(ERROR); + + } else { + valtype = INTEGER; + vptr = 0x0; + + keyname = "WCAXna"; + keytype = IMGAXIS; + BEGIN(COMMENT); + } + } + YY_BREAK +case 6: +/* rule 6 can match eol */ +#line 405 "wcsbth.l" +case 7: +/* rule 7 can match eol */ +#line 406 "wcsbth.l" +case 8: +/* rule 8 can match eol */ +YY_RULE_SETUP +#line 406 "wcsbth.l" +{ + // Cross-reference supplier. + keyname = "WCSTna"; + errmsg = "cross-references are not implemented"; + BEGIN(ERROR); + } + YY_BREAK +case 9: +/* rule 9 can match eol */ +#line 414 "wcsbth.l" +case 10: +/* rule 10 can match eol */ +#line 415 "wcsbth.l" +case 11: +/* rule 11 can match eol */ +YY_RULE_SETUP +#line 415 "wcsbth.l" +{ + // Cross-reference consumer. + keyname = "WCSXna"; + errmsg = "cross-references are not implemented"; + BEGIN(ERROR); + } + YY_BREAK +case 12: +YY_RULE_SETUP +#line 422 "wcsbth.l" +{ + valtype = FLOAT; + vptr = &(wcstem.crpix); + + keyname = "CRPIXja"; + BEGIN(CCCCCia); + } + YY_BREAK +case 13: +#line 431 "wcsbth.l" +case 14: +YY_RULE_SETUP +#line 431 "wcsbth.l" +{ + valtype = FLOAT; + vptr = &(wcstem.crpix); + + sscanf(yytext, "%d", &i); + + if (yyleng == 4) { + keyname = "jCRPna"; + BEGIN(iCCCna); + } else { + keyname = "jCRPXn"; + BEGIN(iCCCCn); + } + } + YY_BREAK +case 15: +#line 447 "wcsbth.l" +case 16: +YY_RULE_SETUP +#line 447 "wcsbth.l" +{ + valtype = FLOAT; + vptr = &(wcstem.crpix); + + if (yyleng == 4) { + keyname = "TCRPna"; + BEGIN(TCCCna); + } else { + keyname = "TCRPXn"; + BEGIN(TCCCCn); + } + } + YY_BREAK +case 17: +YY_RULE_SETUP +#line 460 "wcsbth.l" +{ + valtype = FLOAT; + vptr = &(wcstem.pc); + altlin = 1; + + keyname = "PCi_ja"; + BEGIN(CCi_ja); + } + YY_BREAK +case 18: +YY_RULE_SETUP +#line 469 "wcsbth.l" +{ + valtype = FLOAT; + vptr = &(wcstem.pc); + altlin = 1; + + sscanf(yytext, "%1d%1d", &i, &j); + + keyname = "ijPCna"; + BEGIN(ijCCna); + } + YY_BREAK +case 19: +#line 481 "wcsbth.l" +case 20: +YY_RULE_SETUP +#line 481 "wcsbth.l" +{ + valtype = FLOAT; + vptr = &(wcstem.pc); + altlin = 1; + + if (yyleng == 2) { + keyname = "TPn_ka"; + BEGIN(TCn_ka); + } else { + keyname = "TPCn_ka"; + BEGIN(TCCn_ka); + } + } + YY_BREAK +case 21: +YY_RULE_SETUP +#line 495 "wcsbth.l" +{ + valtype = FLOAT; + vptr = &(wcstem.cd); + altlin = 2; + + keyname = "CDi_ja"; + BEGIN(CCi_ja); + } + YY_BREAK +case 22: +YY_RULE_SETUP +#line 504 "wcsbth.l" +{ + valtype = FLOAT; + vptr = &(wcstem.cd); + altlin = 2; + + sscanf(yytext, "%1d%1d", &i, &j); + + keyname = "ijCDna"; + BEGIN(ijCCna); + } + YY_BREAK +case 23: +#line 516 "wcsbth.l" +case 24: +YY_RULE_SETUP +#line 516 "wcsbth.l" +{ + valtype = FLOAT; + vptr = &(wcstem.cd); + altlin = 2; + + if (yyleng == 2) { + keyname = "TCn_ka"; + BEGIN(TCn_ka); + } else { + keyname = "TCDn_ka"; + BEGIN(TCCn_ka); + } + } + YY_BREAK +case 25: +YY_RULE_SETUP +#line 530 "wcsbth.l" +{ + valtype = FLOAT; + vptr = &(wcstem.cdelt); + + keyname = "CDELTia"; + BEGIN(CCCCCia); + } + YY_BREAK +case 26: +#line 539 "wcsbth.l" +case 27: +YY_RULE_SETUP +#line 539 "wcsbth.l" +{ + valtype = FLOAT; + vptr = &(wcstem.cdelt); + + sscanf(yytext, "%d", &i); + + if (yyleng == 4) { + keyname = "iCDEna"; + BEGIN(iCCCna); + } else { + keyname = "iCDLTn"; + BEGIN(iCCCCn); + } + } + YY_BREAK +case 28: +#line 555 "wcsbth.l" +case 29: +YY_RULE_SETUP +#line 555 "wcsbth.l" +{ + valtype = FLOAT; + vptr = &(wcstem.cdelt); + + if (yyleng == 4) { + keyname = "TCDEna"; + BEGIN(TCCCna); + } else { + keyname = "TCDLTn"; + BEGIN(TCCCCn); + } + } + YY_BREAK +case 30: +YY_RULE_SETUP +#line 568 "wcsbth.l" +{ + valtype = FLOAT; + vptr = &(wcstem.crota); + altlin = 4; + + keyname = "CROTAi"; + BEGIN(CROTAi); + } + YY_BREAK +case 31: +YY_RULE_SETUP +#line 577 "wcsbth.l" +{ + valtype = FLOAT; + vptr = &(wcstem.crota); + altlin = 4; + + sscanf(yytext, "%d", &i); + + keyname = "iCROTn"; + BEGIN(iCROTn); + } + YY_BREAK +case 32: +YY_RULE_SETUP +#line 588 "wcsbth.l" +{ + valtype = FLOAT; + vptr = &(wcstem.crota); + altlin = 4; + + keyname = "TCROTn"; + BEGIN(TCROTn); + } + YY_BREAK +case 33: +YY_RULE_SETUP +#line 597 "wcsbth.l" +{ + valtype = STRING; + vptr = &(wcstem.cunit); + + keyname = "CUNITia"; + BEGIN(CCCCCia); + } + YY_BREAK +case 34: +#line 606 "wcsbth.l" +case 35: +YY_RULE_SETUP +#line 606 "wcsbth.l" +{ + valtype = STRING; + vptr = &(wcstem.cunit); + + sscanf(yytext, "%d", &i); + + if (yyleng == 4) { + keyname = "iCUNna"; + BEGIN(iCCCna); + } else { + keyname = "iCUNIn"; + BEGIN(iCCCCn); + } + } + YY_BREAK +case 36: +#line 622 "wcsbth.l" +case 37: +YY_RULE_SETUP +#line 622 "wcsbth.l" +{ + valtype = STRING; + vptr = &(wcstem.cunit); + + if (yyleng == 4) { + keyname = "TCUNna"; + BEGIN(TCCCna); + } else { + keyname = "TCUNIn"; + BEGIN(TCCCCn); + } + } + YY_BREAK +case 38: +YY_RULE_SETUP +#line 635 "wcsbth.l" +{ + valtype = STRING; + vptr = &(wcstem.ctype); + + keyname = "CTYPEia"; + BEGIN(CCCCCia); + } + YY_BREAK +case 39: +#line 644 "wcsbth.l" +case 40: +YY_RULE_SETUP +#line 644 "wcsbth.l" +{ + valtype = STRING; + vptr = &(wcstem.ctype); + + sscanf(yytext, "%d", &i); + + if (yyleng == 4) { + keyname = "iCTYna"; + BEGIN(iCCCna); + } else { + keyname = "iCTYPn"; + BEGIN(iCCCCn); + } + } + YY_BREAK +case 41: +#line 660 "wcsbth.l" +case 42: +YY_RULE_SETUP +#line 660 "wcsbth.l" +{ + valtype = STRING; + vptr = &(wcstem.ctype); + + if (yyleng == 4) { + keyname = "TCTYna"; + BEGIN(TCCCna); + } else { + keyname = "TCTYPn"; + BEGIN(TCCCCn); + } + } + YY_BREAK +case 43: +YY_RULE_SETUP +#line 673 "wcsbth.l" +{ + valtype = FLOAT; + vptr = &(wcstem.crval); + + keyname = "CRVALia"; + BEGIN(CCCCCia); + } + YY_BREAK +case 44: +#line 682 "wcsbth.l" +case 45: +YY_RULE_SETUP +#line 682 "wcsbth.l" +{ + valtype = FLOAT; + vptr = &(wcstem.crval); + + sscanf(yytext, "%d", &i); + + if (yyleng == 4) { + keyname = "iCRVna"; + BEGIN(iCCCna); + } else { + keyname = "iCRVLn"; + BEGIN(iCCCCn); + } + } + YY_BREAK +case 46: +#line 698 "wcsbth.l" +case 47: +YY_RULE_SETUP +#line 698 "wcsbth.l" +{ + valtype = FLOAT; + vptr = &(wcstem.crval); + + if (yyleng == 4) { + keyname = "TCRVna"; + BEGIN(TCCCna); + } else { + keyname = "TCRVLn"; + BEGIN(TCCCCn); + } + } + YY_BREAK +case 48: +#line 712 "wcsbth.l" +case 49: +YY_RULE_SETUP +#line 712 "wcsbth.l" +{ + valtype = FLOAT; + vptr = &(wcstem.lonpole); + + if (yyleng == 7) { + keyname = "LONPOLEa"; + imherit = 0; + BEGIN(CCCCCCCa); + } else { + keyname = "LONPna"; + BEGIN(CCCCna); + } + } + YY_BREAK +case 50: +#line 727 "wcsbth.l" +case 51: +YY_RULE_SETUP +#line 727 "wcsbth.l" +{ + valtype = FLOAT; + vptr = &(wcstem.latpole); + + if (yyleng == 7) { + keyname = "LATPOLEa"; + imherit = 0; + BEGIN(CCCCCCCa); + } else { + keyname = "LATPna"; + BEGIN(CCCCna); + } + } + YY_BREAK +case 52: +#line 742 "wcsbth.l" +case 53: +#line 743 "wcsbth.l" +case 54: +YY_RULE_SETUP +#line 743 "wcsbth.l" +{ + valtype = FLOAT; + vptr = &(wcstem.restfrq); + + if (yyleng == 8) { + if (relax & WCSHDR_strict) { + errmsg = "the RESTFREQ keyword is deprecated, use RESTFRQa"; + BEGIN(ERROR); + + } else { + unput(' '); + + keyname = "RESTFREQ"; + BEGIN(CCCCCCCa); + } + + } else if (yyleng == 7) { + keyname = "RESTFRQa"; + BEGIN(CCCCCCCa); + + } else { + keyname = "RFRQna"; + BEGIN(CCCCna); + } + } + YY_BREAK +case 55: +#line 770 "wcsbth.l" +case 56: +YY_RULE_SETUP +#line 770 "wcsbth.l" +{ + valtype = FLOAT; + vptr = &(wcstem.restwav); + + if (yyleng == 7) { + keyname = "RESTWAVa"; + BEGIN(CCCCCCCa); + } else { + keyname = "RWAVna"; + BEGIN(CCCCna); + } + } + YY_BREAK +case 57: +YY_RULE_SETUP +#line 783 "wcsbth.l" +{ + valtype = FLOAT; + vptr = &(wcstem.pv); + ptype = 'v'; + + keyname = "PVi_ma"; + BEGIN(CCi_ma); + } + YY_BREAK +case 58: +#line 793 "wcsbth.l" +case 59: +YY_RULE_SETUP +#line 793 "wcsbth.l" +{ + valtype = FLOAT; + vptr = &(wcstem.pv); + ptype = 'v'; + + sscanf(yytext, "%d", &i); + + if (yyleng == 2) { + keyname = "iVn_ma"; + BEGIN(iCn_ma); + } else { + keyname = "iPVn_ma"; + BEGIN(iCCn_ma); + } + } + YY_BREAK +case 60: +#line 810 "wcsbth.l" +case 61: +YY_RULE_SETUP +#line 810 "wcsbth.l" +{ + valtype = FLOAT; + vptr = &(wcstem.pv); + ptype = 'v'; + + if (yyleng == 2) { + keyname = "TVn_ma"; + BEGIN(TCn_ma); + } else { + keyname = "TPVn_ma"; + BEGIN(TCCn_ma); + } + } + YY_BREAK +case 62: +YY_RULE_SETUP +#line 824 "wcsbth.l" +{ + valtype = FLOAT; + vptr = &(wcstem.pv); + ptype = 'v'; + + keyname = "PROJPm"; + BEGIN(PROJPm); + } + YY_BREAK +case 63: +YY_RULE_SETUP +#line 833 "wcsbth.l" +{ + valtype = STRING; + vptr = &(wcstem.ps); + ptype = 's'; + + keyname = "PSi_ma"; + BEGIN(CCi_ma); + } + YY_BREAK +case 64: +#line 843 "wcsbth.l" +case 65: +YY_RULE_SETUP +#line 843 "wcsbth.l" +{ + valtype = STRING; + vptr = &(wcstem.ps); + ptype = 's'; + + sscanf(yytext, "%d", &i); + + if (yyleng == 2) { + keyname = "iSn_ma"; + BEGIN(iCn_ma); + } else { + keyname = "iPSn_ma"; + BEGIN(iCCn_ma); + } + } + YY_BREAK +case 66: +#line 860 "wcsbth.l" +case 67: +YY_RULE_SETUP +#line 860 "wcsbth.l" +{ + valtype = STRING; + vptr = &(wcstem.ps); + ptype = 's'; + + if (yyleng == 2) { + keyname = "TSn_ma"; + BEGIN(TCn_ma); + } else { + keyname = "TPSn_ma"; + BEGIN(TCCn_ma); + } + } + YY_BREAK +case 68: +YY_RULE_SETUP +#line 874 "wcsbth.l" +{ + sscanf(yytext, "VELREF%c", &a); + + if (relax & WCSHDR_strict) { + errmsg = "the VELREF keyword is deprecated, use SPECSYSa"; + BEGIN(ERROR); + + } else if (a == ' ' || (relax & WCSHDR_VELREFa)) { + valtype = INTEGER; + vptr = &(wcstem.velref); + + unput(a); + + keyname = "VELREF"; + imherit = 0; + BEGIN(CCCCCCCa); + + } else if (relax & WCSHDR_reject) { + errmsg = "VELREF keyword may not have an alternate version code"; + BEGIN(ERROR); + + } else { + BEGIN(DISCARD); + } + } + YY_BREAK +case 69: +YY_RULE_SETUP +#line 900 "wcsbth.l" +{ + valtype = STRING; + vptr = &(wcstem.cname); + + keyname = "CNAMEia"; + BEGIN(CCCCCia); + } + YY_BREAK +case 70: +#line 909 "wcsbth.l" +case 71: +YY_RULE_SETUP +#line 909 "wcsbth.l" +{ + valtype = STRING; + vptr = &(wcstem.cname); + + sscanf(yytext, "%d", &i); + + if (yyleng == 4) { + keyname = "iCNAna"; + BEGIN(iCCCna); + } else { + if (!(relax & WCSHDR_CNAMn)) vptr = 0x0; + keyname = "iCNAMn"; + BEGIN(iCCCCn); + } + } + YY_BREAK +case 72: +#line 926 "wcsbth.l" +case 73: +YY_RULE_SETUP +#line 926 "wcsbth.l" +{ + valtype = STRING; + vptr = &(wcstem.cname); + + if (yyleng == 4) { + keyname = "TCNAna"; + BEGIN(TCCCna); + } else { + if (!(relax & WCSHDR_CNAMn)) vptr = 0x0; + keyname = "TCNAMn"; + BEGIN(TCCCCn); + } + } + YY_BREAK +case 74: +YY_RULE_SETUP +#line 940 "wcsbth.l" +{ + valtype = FLOAT; + vptr = &(wcstem.crder); + + keyname = "CRDERia"; + BEGIN(CCCCCia); + } + YY_BREAK +case 75: +#line 949 "wcsbth.l" +case 76: +YY_RULE_SETUP +#line 949 "wcsbth.l" +{ + valtype = FLOAT; + vptr = &(wcstem.crder); + sscanf(yytext, "%d", &i); + + if (yyleng == 4) { + keyname = "iCRDna"; + BEGIN(iCCCna); } else { - BEGIN(FLUSH); + if (!(relax & WCSHDR_CNAMn)) vptr = 0x0; + keyname = "iCRDEn"; + BEGIN(iCCCCn); } } YY_BREAK -case 2: +case 77: +#line 966 "wcsbth.l" +case 78: YY_RULE_SETUP -#line 306 "wcsbth.l" +#line 966 "wcsbth.l" { - keytype = IMGAXIS; - - if (!(keytype & keysel)) { - /* Ignore this key type. */ - BEGIN(DISCARD); + valtype = FLOAT; + vptr = &(wcstem.crder); + if (yyleng == 4) { + keyname = "TCRDna"; + BEGIN(TCCCna); } else { - if (relax & WCSHDR_ALLIMG) { - if (ipass == 1) { - sscanf(wcsbthtext, "WCSAXES%c= %d", &a, &i); - wcsbth_pass1(IMGAXIS, i, 0, 0, 0, a, ' ', &alts); - } + if (!(relax & WCSHDR_CNAMn)) vptr = 0x0; + keyname = "TCRDEn"; + BEGIN(TCCCCn); + } + } + YY_BREAK +case 79: +YY_RULE_SETUP +#line 980 "wcsbth.l" +{ + valtype = FLOAT; + vptr = &(wcstem.csyer); - BEGIN(FLUSH); + keyname = "CSYERia"; + BEGIN(CCCCCia); + } + YY_BREAK +case 80: +#line 989 "wcsbth.l" +case 81: +YY_RULE_SETUP +#line 989 "wcsbth.l" +{ + valtype = FLOAT; + vptr = &(wcstem.csyer); - } else if (relax & WCSHDR_reject) { - errmsg = "Image-header keyword WCSAXESa in binary table"; - BEGIN(ERROR); + sscanf(yytext, "%d", &i); - } else { - /* Pretend we don't recognize it. */ - BEGIN(DISCARD); - } + if (yyleng == 4) { + keyname = "iCSYna"; + BEGIN(iCCCna); + } else { + if (!(relax & WCSHDR_CNAMn)) vptr = 0x0; + keyname = "iCSYEn"; + BEGIN(iCCCCn); } } YY_BREAK -case 3: -#line 334 "wcsbth.l" -case 4: -#line 335 "wcsbth.l" -case 5: +case 82: +#line 1006 "wcsbth.l" +case 83: YY_RULE_SETUP -#line 335 "wcsbth.l" +#line 1006 "wcsbth.l" { - keytype = BIMGARR; - - /* Note that a blank in the sscanf() format string matches zero or - more of them in the input. */ - sscanf(wcsbthtext, "WCAX%d%c = %d", &n, &a, &i); + valtype = FLOAT; + vptr = &(wcstem.csyer); - if (!(keytype & keysel) || exclude[n]) { - /* Ignore this key type or column. */ - BEGIN(DISCARD); + if (yyleng == 4) { + keyname = "TCSYna"; + BEGIN(TCCCna); } else { - if (ipass == 1) { - wcsbth_pass1(BIMGARR, i, 0, n, 0, a, ' ', &alts); - } - BEGIN(FLUSH); + if (!(relax & WCSHDR_CNAMn)) vptr = 0x0; + keyname = "TCSYEn"; + BEGIN(TCCCCn); } } YY_BREAK -case 6: -/* rule 6 can match eol */ -#line 354 "wcsbth.l" -case 7: -/* rule 7 can match eol */ -#line 355 "wcsbth.l" -case 8: -/* rule 8 can match eol */ +case 84: YY_RULE_SETUP -#line 355 "wcsbth.l" +#line 1020 "wcsbth.l" { - /* Cross-reference supplier. */ - keytype = BIMGARR; - errmsg = "Cross-references are not currently implemented"; - BEGIN(ERROR); + valtype = FLOAT; + vptr = &(wcstem.czphs); + + keyname = "CZPHSia"; + BEGIN(CCCCCia); } YY_BREAK -case 9: -/* rule 9 can match eol */ -#line 363 "wcsbth.l" -case 10: -/* rule 10 can match eol */ -#line 364 "wcsbth.l" -case 11: -/* rule 11 can match eol */ +case 85: +#line 1029 "wcsbth.l" +case 86: YY_RULE_SETUP -#line 364 "wcsbth.l" +#line 1029 "wcsbth.l" { - /* Cross-reference consumer. */ - keytype = BIMGARR; - errmsg = "Cross-references are not currently implemented"; - BEGIN(ERROR); + valtype = FLOAT; + vptr = &(wcstem.czphs); + + sscanf(yytext, "%d", &i); + + if (yyleng == 4) { + keyname = "iCZPna"; + BEGIN(iCCCna); + } else { + if (!(relax & WCSHDR_CNAMn)) vptr = 0x0; + keyname = "iCZPHn"; + BEGIN(iCCCCn); + } } YY_BREAK -case 12: +case 87: +#line 1046 "wcsbth.l" +case 88: +YY_RULE_SETUP +#line 1046 "wcsbth.l" +{ + valtype = FLOAT; + vptr = &(wcstem.czphs); + + if (yyleng == 4) { + keyname = "TCZPna"; + BEGIN(TCCCna); + } else { + if (!(relax & WCSHDR_CNAMn)) vptr = 0x0; + keyname = "TCZPHn"; + BEGIN(TCCCCn); + } + } + YY_BREAK +case 89: YY_RULE_SETUP -#line 371 "wcsbth.l" +#line 1060 "wcsbth.l" { valtype = FLOAT; - vptr = &(wcstem.crpix); + vptr = &(wcstem.cperi); - extkey = "CRPIXja"; + keyname = "CPERIia"; BEGIN(CCCCCia); } YY_BREAK -case 13: -#line 380 "wcsbth.l" -case 14: +case 90: +#line 1069 "wcsbth.l" +case 91: YY_RULE_SETUP -#line 380 "wcsbth.l" +#line 1069 "wcsbth.l" { valtype = FLOAT; - vptr = &(wcstem.crpix); + vptr = &(wcstem.cperi); - sscanf(wcsbthtext, "%d", &i); + sscanf(yytext, "%d", &i); - if (wcsbthleng == 4) { + if (yyleng == 4) { + keyname = "iCPRna"; BEGIN(iCCCna); } else { - extkey = "jCRPXn"; + if (!(relax & WCSHDR_CNAMn)) vptr = 0x0; + keyname = "iCPERn"; BEGIN(iCCCCn); } } YY_BREAK -case 15: -#line 395 "wcsbth.l" -case 16: +case 92: +#line 1086 "wcsbth.l" +case 93: YY_RULE_SETUP -#line 395 "wcsbth.l" +#line 1086 "wcsbth.l" { valtype = FLOAT; - vptr = &(wcstem.crpix); + vptr = &(wcstem.cperi); - if (wcsbthleng == 4) { + if (yyleng == 4) { + keyname = "TCPRna"; BEGIN(TCCCna); } else { - extkey = "TCRPXn"; + if (!(relax & WCSHDR_CNAMn)) vptr = 0x0; + keyname = "TCPERn"; BEGIN(TCCCCn); } } YY_BREAK -case 17: +case 94: +#line 1101 "wcsbth.l" +case 95: +#line 1102 "wcsbth.l" +case 96: YY_RULE_SETUP -#line 407 "wcsbth.l" +#line 1102 "wcsbth.l" { - valtype = FLOAT; - vptr = &(wcstem.pc); - altlin = 1; + valtype = STRING; + vptr = wcstem.wcsname; - extkey = "PCi_ja"; - BEGIN(CCi_ja); + if (yyleng == 7) { + keyname = "WCSNAMEa"; + imherit = 0; + BEGIN(CCCCCCCa); + + } else { + if (*yytext == 'W') { + keyname = "WCSNna"; + } else { + keyname = "TWCSna"; + } + BEGIN(CCCCna); + } } YY_BREAK -case 18: +case 97: YY_RULE_SETUP -#line 416 "wcsbth.l" +#line 1121 "wcsbth.l" { - valtype = FLOAT; - vptr = &(wcstem.pc); - altlin = 1; + valtype = STRING; + vptr = wcstem.timesys; - sscanf(wcsbthtext, "%1d%1d", &i, &j); + keyname = "TIMESYS"; + BEGIN(CCCCCCCC); + } + YY_BREAK +case 98: +#line 1130 "wcsbth.l" +case 99: +YY_RULE_SETUP +#line 1130 "wcsbth.l" +{ + valtype = STRING; + vptr = wcstem.trefpos; - BEGIN(ijCCna); + if (yyleng == 8) { + if (ctrl < -10) keep = keyrec; + keyname = "TREFPOS"; + BEGIN(CCCCCCCC); + } else { + keyname = "TRPOSn"; + BEGIN(CCCCCn); + } } YY_BREAK -case 19: -#line 427 "wcsbth.l" -case 20: +case 100: +#line 1145 "wcsbth.l" +case 101: YY_RULE_SETUP -#line 427 "wcsbth.l" +#line 1145 "wcsbth.l" { - valtype = FLOAT; - vptr = &(wcstem.pc); - altlin = 1; + valtype = STRING; + vptr = wcstem.trefdir; - if (wcsbthleng == 2) { - BEGIN(TCn_ka); + if (yyleng == 8) { + if (ctrl < -10) keep = keyrec; + keyname = "TREFDIR"; + BEGIN(CCCCCCCC); } else { - extkey = "TPCn_ka"; - BEGIN(TCCn_ka); + keyname = "TRDIRn"; + BEGIN(CCCCCn); } } YY_BREAK -case 21: +case 102: YY_RULE_SETUP -#line 440 "wcsbth.l" +#line 1159 "wcsbth.l" { - valtype = FLOAT; - vptr = &(wcstem.cd); - altlin = 2; + valtype = STRING; + vptr = wcstem.plephem; - extkey = "CDi_ja"; - BEGIN(CCi_ja); + keyname = "PLEPHEM"; + BEGIN(CCCCCCCC); } YY_BREAK -case 22: +case 103: YY_RULE_SETUP -#line 449 "wcsbth.l" +#line 1167 "wcsbth.l" { - valtype = FLOAT; - vptr = &(wcstem.cd); - altlin = 2; + valtype = STRING; + vptr = wcstem.timeunit; + + keyname = "TIMEUNIT"; + BEGIN(CCCCCCCC); + } + YY_BREAK +case 104: +#line 1176 "wcsbth.l" +case 105: +YY_RULE_SETUP +#line 1176 "wcsbth.l" +{ + if ((yytext[4] == 'R') || (relax & WCSHDR_DATEREF)) { + valtype = STRING; + vptr = wcstem.dateref; - sscanf(wcsbthtext, "%1d%1d", &i, &j); + keyname = "DATEREF"; + BEGIN(CCCCCCCC); - BEGIN(ijCCna); + } else if (relax & WCSHDR_reject) { + errmsg = "the DATE-REF keyword is non-standard"; + BEGIN(ERROR); + + } else { + BEGIN(DISCARD); + } } YY_BREAK -case 23: -#line 460 "wcsbth.l" -case 24: +case 106: +#line 1194 "wcsbth.l" +case 107: YY_RULE_SETUP -#line 460 "wcsbth.l" +#line 1194 "wcsbth.l" { - valtype = FLOAT; - vptr = &(wcstem.cd); - altlin = 2; + if ((yytext[3] == 'R') || (relax & WCSHDR_DATEREF)) { + valtype = FLOAT2; + vptr = wcstem.mjdref; + + keyname = "MJDREF"; + BEGIN(CCCCCCCC); + + } else if (relax & WCSHDR_reject) { + errmsg = "the MJD-REF keyword is non-standard"; + BEGIN(ERROR); - if (wcsbthleng == 2) { - BEGIN(TCn_ka); } else { - extkey = "TCDn_ka"; - BEGIN(TCCn_ka); + BEGIN(DISCARD); } } YY_BREAK -case 25: +case 108: +#line 1212 "wcsbth.l" +case 109: YY_RULE_SETUP -#line 473 "wcsbth.l" +#line 1212 "wcsbth.l" { - valtype = FLOAT; - vptr = &(wcstem.cdelt); + if ((yytext[3] == 'R') || (relax & WCSHDR_DATEREF)) { + // Actually integer, but treated as float. + valtype = FLOAT; + vptr = wcstem.mjdref; - extkey = "CDELTia"; - BEGIN(CCCCCia); + keyname = "MJDREFI"; + BEGIN(CCCCCCCC); + + } else if (relax & WCSHDR_reject) { + errmsg = "the MJD-REFI keyword is non-standard"; + BEGIN(ERROR); + + } else { + BEGIN(DISCARD); + } } YY_BREAK -case 26: -#line 482 "wcsbth.l" -case 27: +case 110: +#line 1231 "wcsbth.l" +case 111: YY_RULE_SETUP -#line 482 "wcsbth.l" +#line 1231 "wcsbth.l" { - valtype = FLOAT; - vptr = &(wcstem.cdelt); + if ((yytext[3] == 'R') || (relax & WCSHDR_DATEREF)) { + valtype = FLOAT; + vptr = wcstem.mjdref + 1; + + keyname = "MJDREFF"; + BEGIN(CCCCCCCC); - sscanf(wcsbthtext, "%d", &i); + } else if (relax & WCSHDR_reject) { + errmsg = "the MJD-REFF keyword is non-standard"; + BEGIN(ERROR); - if (wcsbthleng == 4) { - BEGIN(iCCCna); } else { - extkey = "iCDLTn"; - BEGIN(iCCCCn); + BEGIN(DISCARD); } } YY_BREAK -case 28: -#line 497 "wcsbth.l" -case 29: +case 112: +#line 1249 "wcsbth.l" +case 113: YY_RULE_SETUP -#line 497 "wcsbth.l" +#line 1249 "wcsbth.l" { - valtype = FLOAT; - vptr = &(wcstem.cdelt); + if ((yytext[2] == 'R') || (relax & WCSHDR_DATEREF)) { + valtype = FLOAT2; + vptr = wcstem.mjdref; + special = wcsbth_jdref; + + keyname = "JDREF"; + BEGIN(CCCCCCCC); + + } else if (relax & WCSHDR_reject) { + errmsg = "the JD-REF keyword is non-standard"; + BEGIN(ERROR); - if (wcsbthleng == 4) { - BEGIN(TCCCna); } else { - extkey = "TCDLTn"; - BEGIN(TCCCCn); + BEGIN(DISCARD); } } YY_BREAK -case 30: +case 114: +#line 1268 "wcsbth.l" +case 115: YY_RULE_SETUP -#line 509 "wcsbth.l" +#line 1268 "wcsbth.l" { - valtype = FLOAT; - vptr = &(wcstem.crota); - altlin = 4; + if ((yytext[2] == 'R') || (relax & WCSHDR_DATEREF)) { + // Actually integer, but treated as float. + valtype = FLOAT; + vptr = wcstem.mjdref; + special = wcsbth_jdrefi; - extkey = "CROTAi"; - BEGIN(CROTAi); + keyname = "JDREFI"; + BEGIN(CCCCCCCC); + + } else if (relax & WCSHDR_reject) { + errmsg = "the JD-REFI keyword is non-standard"; + BEGIN(ERROR); + + } else { + BEGIN(DISCARD); + } } YY_BREAK -case 31: +case 116: +#line 1288 "wcsbth.l" +case 117: YY_RULE_SETUP -#line 518 "wcsbth.l" +#line 1288 "wcsbth.l" { - valtype = FLOAT; - vptr = &(wcstem.crota); - altlin = 4; + if ((yytext[2] == 'R') || (relax & WCSHDR_DATEREF)) { + valtype = FLOAT; + vptr = wcstem.mjdref; + special = wcsbth_jdreff; - sscanf(wcsbthtext, "%d", &i); + keyname = "JDREFF"; + BEGIN(CCCCCCCC); - extkey = "iCROTn"; - BEGIN(iCROTn); + } else if (relax & WCSHDR_reject) { + errmsg = "the JD-REFF keyword is non-standard"; + BEGIN(ERROR); + + } else { + BEGIN(DISCARD); + } } YY_BREAK -case 32: +case 118: YY_RULE_SETUP -#line 529 "wcsbth.l" +#line 1306 "wcsbth.l" { valtype = FLOAT; - vptr = &(wcstem.crota); - altlin = 4; + vptr = &(wcstem.timeoffs); - extkey = "TCROTn"; - BEGIN(TCROTn); + keyname = "TIMEOFFS"; + BEGIN(CCCCCCCC); } YY_BREAK -case 33: +case 119: YY_RULE_SETUP -#line 538 "wcsbth.l" +#line 1314 "wcsbth.l" { valtype = STRING; - vptr = &(wcstem.cunit); + vptr = wcstem.dateobs; + if (ctrl < -10) keep = keyrec; - extkey = "CUNITia"; - BEGIN(CCCCCia); + keyname = "DATE-OBS"; + imherit = 0; + BEGIN(CCCCCCCC); } YY_BREAK -case 34: -#line 547 "wcsbth.l" -case 35: +case 120: +#line 1325 "wcsbth.l" +case 121: +#line 1326 "wcsbth.l" +case 122: YY_RULE_SETUP -#line 547 "wcsbth.l" +#line 1326 "wcsbth.l" { valtype = STRING; - vptr = &(wcstem.cunit); + vptr = wcstem.dateobs; - sscanf(wcsbthtext, "%d", &i); + if (relax & WCSHDR_DOBSn) { + yyless(4); + + keyname = "DOBSn"; + BEGIN(CCCCn); + + } else if (relax & WCSHDR_reject) { + errmsg = "DOBSn keyword is non-standard"; + BEGIN(ERROR); - if (wcsbthleng == 4) { - BEGIN(iCCCna); } else { - extkey = "iCUNIn"; - BEGIN(iCCCCn); + BEGIN(DISCARD); } } YY_BREAK -case 36: -#line 562 "wcsbth.l" -case 37: +case 123: YY_RULE_SETUP -#line 562 "wcsbth.l" +#line 1345 "wcsbth.l" { valtype = STRING; - vptr = &(wcstem.cunit); + vptr = wcstem.datebeg; + if (ctrl < -10) keep = keyrec; - if (wcsbthleng == 4) { - BEGIN(TCCCna); - } else { - extkey = "TCUNIn"; - BEGIN(TCCCCn); - } + keyname = "DATE-BEG"; + BEGIN(CCCCCCCC); } YY_BREAK -case 38: +case 124: +#line 1355 "wcsbth.l" +case 125: YY_RULE_SETUP -#line 574 "wcsbth.l" +#line 1355 "wcsbth.l" { valtype = STRING; - vptr = &(wcstem.ctype); + vptr = wcstem.dateavg; - extkey = "CTYPEia"; - BEGIN(CCCCCia); + if (yyleng == 8) { + if (ctrl < -10) keep = keyrec; + keyname = "DATE-AVG"; + BEGIN(CCCCCCCC); + } else { + keyname = "DAVGn"; + BEGIN(CCCCn); + } } YY_BREAK -case 39: -#line 583 "wcsbth.l" -case 40: +case 126: YY_RULE_SETUP -#line 583 "wcsbth.l" +#line 1369 "wcsbth.l" { valtype = STRING; - vptr = &(wcstem.ctype); - - sscanf(wcsbthtext, "%d", &i); + vptr = wcstem.dateend; + if (ctrl < -10) keep = keyrec; - if (wcsbthleng == 4) { - BEGIN(iCCCna); - } else { - extkey = "iCTYPn"; - BEGIN(iCCCCn); - } + keyname = "DATE-END"; + BEGIN(CCCCCCCC); } YY_BREAK -case 41: -#line 598 "wcsbth.l" -case 42: +case 127: +#line 1379 "wcsbth.l" +case 128: YY_RULE_SETUP -#line 598 "wcsbth.l" +#line 1379 "wcsbth.l" { - valtype = STRING; - vptr = &(wcstem.ctype); + valtype = FLOAT; + vptr = &(wcstem.mjdobs); - if (wcsbthleng == 4) { - BEGIN(TCCCna); + if (yyleng == 8) { + if (ctrl < -10) keep = keyrec; + keyname = "MJD-OBS"; + imherit = 0; + BEGIN(CCCCCCCC); } else { - extkey = "TCTYPn"; - BEGIN(TCCCCn); + keyname = "MJDOBn"; + BEGIN(CCCCCn); } } YY_BREAK -case 43: +case 129: YY_RULE_SETUP -#line 610 "wcsbth.l" +#line 1394 "wcsbth.l" { valtype = FLOAT; - vptr = &(wcstem.crval); + vptr = &(wcstem.mjdbeg); + if (ctrl < -10) keep = keyrec; - extkey = "CRVALia"; - BEGIN(CCCCCia); + keyname = "MJD-BEG"; + BEGIN(CCCCCCCC); } YY_BREAK -case 44: -#line 619 "wcsbth.l" -case 45: +case 130: +#line 1404 "wcsbth.l" +case 131: YY_RULE_SETUP -#line 619 "wcsbth.l" +#line 1404 "wcsbth.l" { valtype = FLOAT; - vptr = &(wcstem.crval); - - sscanf(wcsbthtext, "%d", &i); + vptr = &(wcstem.mjdavg); - if (wcsbthleng == 4) { - BEGIN(iCCCna); + if (yyleng == 8) { + if (ctrl < -10) keep = keyrec; + keyname = "MJD-AVG"; + BEGIN(CCCCCCCC); } else { - extkey = "iCRVLn"; - BEGIN(iCCCCn); + keyname = "MJDAn"; + BEGIN(CCCCn); } } YY_BREAK -case 46: -#line 634 "wcsbth.l" -case 47: +case 132: YY_RULE_SETUP -#line 634 "wcsbth.l" +#line 1418 "wcsbth.l" { valtype = FLOAT; - vptr = &(wcstem.crval); + vptr = &(wcstem.mjdend); + if (ctrl < -10) keep = keyrec; - if (wcsbthleng == 4) { - BEGIN(TCCCna); - } else { - extkey = "TCRVLn"; - BEGIN(TCCCCn); - } + keyname = "MJD-END"; + BEGIN(CCCCCCCC); } YY_BREAK -case 48: -#line 647 "wcsbth.l" -case 49: +case 133: YY_RULE_SETUP -#line 647 "wcsbth.l" +#line 1427 "wcsbth.l" { valtype = FLOAT; - vptr = &(wcstem.lonpole); + vptr = &(wcstem.jepoch); + if (ctrl < -10) keep = keyrec; - if (wcsbthleng == 7) { - extkey = "LONPOLEa"; - BEGIN(CCCCCCCa); - } else { - BEGIN(CCCCna); - } + keyname = "JEPOCH"; + BEGIN(CCCCCCCC); } YY_BREAK -case 50: -#line 660 "wcsbth.l" -case 51: +case 134: YY_RULE_SETUP -#line 660 "wcsbth.l" +#line 1436 "wcsbth.l" { valtype = FLOAT; - vptr = &(wcstem.latpole); + vptr = &(wcstem.bepoch); + if (ctrl < -10) keep = keyrec; - if (wcsbthleng == 7) { - extkey = "LATPOLEa"; - BEGIN(CCCCCCCa); - } else { - BEGIN(CCCCna); - } + keyname = "BEPOCH"; + BEGIN(CCCCCCCC); } YY_BREAK -case 52: -#line 673 "wcsbth.l" -case 53: -#line 674 "wcsbth.l" -case 54: +case 135: YY_RULE_SETUP -#line 674 "wcsbth.l" +#line 1445 "wcsbth.l" { valtype = FLOAT; - vptr = &(wcstem.restfrq); + vptr = &(wcstem.tstart); + if (ctrl < -10) keep = keyrec; - if (wcsbthleng == 8) { - unput(' '); - extkey = "RESTFREQ"; - BEGIN(CCCCCCCa); - } else if (wcsbthleng == 7) { - extkey = "RESTFRQa"; - BEGIN(CCCCCCCa); - } else { - BEGIN(CCCCna); - } + keyname = "TSTART"; + BEGIN(CCCCCCCC); } YY_BREAK -case 55: -#line 691 "wcsbth.l" -case 56: +case 136: YY_RULE_SETUP -#line 691 "wcsbth.l" +#line 1454 "wcsbth.l" { valtype = FLOAT; - vptr = &(wcstem.restwav); + vptr = &(wcstem.tstop); + if (ctrl < -10) keep = keyrec; - if (wcsbthleng == 7) { - extkey = "RESTWAVa"; - BEGIN(CCCCCCCa); - } else { - BEGIN(CCCCna); - } + keyname = "TSTOP"; + BEGIN(CCCCCCCC); } YY_BREAK -case 57: +case 137: YY_RULE_SETUP -#line 703 "wcsbth.l" +#line 1463 "wcsbth.l" { valtype = FLOAT; - vptr = &(wcstem.pv); - ptype = 'v'; + vptr = &(wcstem.xposure); + if (ctrl < -10) keep = keyrec; - extkey = "PVi_ma"; - BEGIN(CCi_ma); + keyname = "XPOSURE"; + BEGIN(CCCCCCCC); } YY_BREAK -case 58: -#line 713 "wcsbth.l" -case 59: +case 138: YY_RULE_SETUP -#line 713 "wcsbth.l" +#line 1472 "wcsbth.l" { valtype = FLOAT; - vptr = &(wcstem.pv); - ptype = 'v'; - - sscanf(wcsbthtext, "%d", &i); + vptr = &(wcstem.telapse); + if (ctrl < -10) keep = keyrec; - if (wcsbthleng == 2) { - BEGIN(iCn_ma); - } else { - extkey = "iPVn_ma"; - BEGIN(iCCn_ma); - } + keyname = "TELAPSE"; + BEGIN(CCCCCCCC); } YY_BREAK -case 60: -#line 729 "wcsbth.l" -case 61: +case 139: YY_RULE_SETUP -#line 729 "wcsbth.l" +#line 1481 "wcsbth.l" { valtype = FLOAT; - vptr = &(wcstem.pv); - ptype = 'v'; + vptr = &(wcstem.timsyer); + if (ctrl < -10) keep = keyrec; - if (wcsbthleng == 2) { - BEGIN(TCn_ma); - } else { - extkey = "TPVn_ma"; - BEGIN(TCCn_ma); - } + keyname = "TIMSYER"; + BEGIN(CCCCCCCC); } YY_BREAK -case 62: +case 140: YY_RULE_SETUP -#line 742 "wcsbth.l" +#line 1490 "wcsbth.l" { valtype = FLOAT; - vptr = &(wcstem.pv); - ptype = 'v'; + vptr = &(wcstem.timrder); + if (ctrl < -10) keep = keyrec; - BEGIN(PROJPm); + keyname = "TIMRDER"; + BEGIN(CCCCCCCC); } YY_BREAK -case 63: +case 141: YY_RULE_SETUP -#line 750 "wcsbth.l" +#line 1499 "wcsbth.l" { - valtype = STRING; - vptr = &(wcstem.ps); - ptype = 's'; + valtype = FLOAT; + vptr = &(wcstem.timedel); + if (ctrl < -10) keep = keyrec; - extkey = "PSi_ma"; - BEGIN(CCi_ma); + keyname = "TIMEDEL"; + BEGIN(CCCCCCCC); } YY_BREAK -case 64: -#line 760 "wcsbth.l" -case 65: +case 142: YY_RULE_SETUP -#line 760 "wcsbth.l" +#line 1508 "wcsbth.l" { - valtype = STRING; - vptr = &(wcstem.ps); - ptype = 's'; - - sscanf(wcsbthtext, "%d", &i); + valtype = FLOAT; + vptr = &(wcstem.timepixr); + chekval = wcsbth_timepixr; + if (ctrl < -10) keep = keyrec; - if (wcsbthleng == 2) { - BEGIN(iCn_ma); - } else { - extkey = "iPSn_ma"; - BEGIN(iCCn_ma); - } + keyname = "TIMEPIXR"; + BEGIN(CCCCCCCC); } YY_BREAK -case 66: -#line 776 "wcsbth.l" -case 67: +case 143: +#line 1519 "wcsbth.l" +case 144: YY_RULE_SETUP -#line 776 "wcsbth.l" +#line 1519 "wcsbth.l" { - valtype = STRING; - vptr = &(wcstem.ps); - ptype = 's'; + valtype = FLOAT; + vptr = wcstem.obsgeo; - if (wcsbthleng == 2) { - BEGIN(TCn_ma); + if (yyleng == 8) { + if (ctrl < -10) keep = keyrec; + keyname = "OBSGEO-X"; + BEGIN(CCCCCCCC); } else { - extkey = "TPSn_ma"; - BEGIN(TCCn_ma); + keyname = "OBSGXn"; + BEGIN(CCCCCn); } } YY_BREAK -case 68: -YY_RULE_SETUP -#line 789 "wcsbth.l" -{ - valtype = STRING; - vptr = &(wcstem.cname); - - extkey = "CNAMEia"; - BEGIN(CCCCCia); - } - YY_BREAK -case 69: -#line 798 "wcsbth.l" -case 70: +case 145: +#line 1534 "wcsbth.l" +case 146: YY_RULE_SETUP -#line 798 "wcsbth.l" +#line 1534 "wcsbth.l" { - valtype = STRING; - vptr = &(wcstem.cname); - - sscanf(wcsbthtext, "%d", &i); + valtype = FLOAT; + vptr = wcstem.obsgeo + 1; - if (wcsbthleng == 4) { - BEGIN(iCCCna); + if (yyleng == 8) { + if (ctrl < -10) keep = keyrec; + keyname = "OBSGEO-Y"; + BEGIN(CCCCCCCC); } else { - if (!(relax & WCSHDR_CNAMn)) vptr = 0x0; - extkey = "iCNAMn"; - BEGIN(iCCCCn); + keyname = "OBSGYn"; + BEGIN(CCCCCn); } } YY_BREAK -case 71: -#line 814 "wcsbth.l" -case 72: +case 147: +#line 1549 "wcsbth.l" +case 148: YY_RULE_SETUP -#line 814 "wcsbth.l" +#line 1549 "wcsbth.l" { - valtype = STRING; - vptr = &(wcstem.cname); + valtype = FLOAT; + vptr = wcstem.obsgeo + 2; - if (wcsbthleng == 4) { - BEGIN(TCCCna); + if (yyleng == 8) { + if (ctrl < -10) keep = keyrec; + keyname = "OBSGEO-Z"; + BEGIN(CCCCCCCC); } else { - if (!(relax & WCSHDR_CNAMn)) vptr = 0x0; - extkey = "TCNAMn"; - BEGIN(TCCCCn); + keyname = "OBSGZn"; + BEGIN(CCCCCn); } } YY_BREAK -case 73: +case 149: YY_RULE_SETUP -#line 827 "wcsbth.l" +#line 1563 "wcsbth.l" { valtype = FLOAT; - vptr = &(wcstem.crder); + vptr = wcstem.obsgeo + 3; + if (ctrl < -10) keep = keyrec; - extkey = "CRDERia"; - BEGIN(CCCCCia); + keyname = "OBSGEO-L"; + BEGIN(CCCCCCCC); } YY_BREAK -case 74: -#line 836 "wcsbth.l" -case 75: +case 150: +#line 1573 "wcsbth.l" +case 151: +#line 1574 "wcsbth.l" +case 152: YY_RULE_SETUP -#line 836 "wcsbth.l" +#line 1574 "wcsbth.l" { - valtype = FLOAT; - vptr = &(wcstem.crder); + valtype = STRING; + vptr = wcstem.obsgeo + 3; - sscanf(wcsbthtext, "%d", &i); + if (relax & WCSHDR_OBSGLBHn) { + yyless(5); + + keyname = "OBSGLn"; + BEGIN(CCCCCn); + + } else if (relax & WCSHDR_reject) { + errmsg = "OBSGLn keyword is non-standard"; + BEGIN(ERROR); - if (wcsbthleng == 4) { - BEGIN(iCCCna); } else { - if (!(relax & WCSHDR_CNAMn)) vptr = 0x0; - extkey = "iCRDEn"; - BEGIN(iCCCCn); + BEGIN(DISCARD); } } YY_BREAK -case 76: -#line 852 "wcsbth.l" -case 77: +case 153: YY_RULE_SETUP -#line 852 "wcsbth.l" +#line 1593 "wcsbth.l" { valtype = FLOAT; - vptr = &(wcstem.crder); + vptr = wcstem.obsgeo + 4; + if (ctrl < -10) keep = keyrec; + + keyname = "OBSGEO-B"; + BEGIN(CCCCCCCC); + } + YY_BREAK +case 154: +#line 1603 "wcsbth.l" +case 155: +#line 1604 "wcsbth.l" +case 156: +YY_RULE_SETUP +#line 1604 "wcsbth.l" +{ + valtype = STRING; + vptr = wcstem.obsgeo + 3; + + if (relax & WCSHDR_OBSGLBHn) { + yyless(5); + + keyname = "OBSGBn"; + BEGIN(CCCCCn); + + } else if (relax & WCSHDR_reject) { + errmsg = "OBSGBn keyword is non-standard"; + BEGIN(ERROR); - if (wcsbthleng == 4) { - BEGIN(TCCCna); } else { - if (!(relax & WCSHDR_CNAMn)) vptr = 0x0; - extkey = "TCRDEn"; - BEGIN(TCCCCn); + BEGIN(DISCARD); } } YY_BREAK -case 78: +case 157: YY_RULE_SETUP -#line 865 "wcsbth.l" +#line 1623 "wcsbth.l" { valtype = FLOAT; - vptr = &(wcstem.csyer); + vptr = wcstem.obsgeo + 5; + if (ctrl < -10) keep = keyrec; - extkey = "CSYERia"; - BEGIN(CCCCCia); + keyname = "OBSGEO-H"; + BEGIN(CCCCCCCC); } YY_BREAK -case 79: -#line 874 "wcsbth.l" -case 80: +case 158: +#line 1633 "wcsbth.l" +case 159: +#line 1634 "wcsbth.l" +case 160: YY_RULE_SETUP -#line 874 "wcsbth.l" +#line 1634 "wcsbth.l" { - valtype = FLOAT; - vptr = &(wcstem.csyer); + valtype = STRING; + vptr = wcstem.obsgeo + 3; - sscanf(wcsbthtext, "%d", &i); + if (relax & WCSHDR_OBSGLBHn) { + yyless(5); - if (wcsbthleng == 4) { - BEGIN(iCCCna); - } else { - if (!(relax & WCSHDR_CNAMn)) vptr = 0x0; - extkey = "iCSYEn"; - BEGIN(iCCCCn); - } - } - YY_BREAK -case 81: -#line 890 "wcsbth.l" -case 82: -YY_RULE_SETUP -#line 890 "wcsbth.l" -{ - valtype = FLOAT; - vptr = &(wcstem.csyer); + keyname = "OBSGHn"; + BEGIN(CCCCCn); + + } else if (relax & WCSHDR_reject) { + errmsg = "OBSGHn keyword is non-standard"; + BEGIN(ERROR); - if (wcsbthleng == 4) { - BEGIN(TCCCna); } else { - if (!(relax & WCSHDR_CNAMn)) vptr = 0x0; - extkey = "TCSYEn"; - BEGIN(TCCCCn); + BEGIN(DISCARD); } } YY_BREAK -case 83: -#line 904 "wcsbth.l" -case 84: +case 161: YY_RULE_SETUP -#line 904 "wcsbth.l" +#line 1653 "wcsbth.l" { valtype = STRING; - vptr = wcstem.dateavg; + vptr = wcstem.obsorbit; - if (wcsbthleng == 8) { - extkey = "DATE-AVG"; - BEGIN(CCCCCCCC); - } else { - BEGIN(CCCCn); - } + keyname = "OBSORBIT"; + BEGIN(CCCCCCCC); } YY_BREAK -case 85: +case 162: +#line 1662 "wcsbth.l" +case 163: YY_RULE_SETUP -#line 916 "wcsbth.l" +#line 1662 "wcsbth.l" { valtype = STRING; - vptr = wcstem.dateobs; + vptr = wcstem.radesys; - extkey = "DATE-OBS"; - BEGIN(CCCCCCCC); + if (yyleng == 7) { + keyname = "RADESYSa"; + imherit = 0; + BEGIN(CCCCCCCa); + } else { + keyname = "RADEna"; + BEGIN(CCCCna); + } } YY_BREAK -case 86: -#line 925 "wcsbth.l" -case 87: -#line 926 "wcsbth.l" -case 88: +case 164: YY_RULE_SETUP -#line 926 "wcsbth.l" +#line 1676 "wcsbth.l" { - if (relax & WCSHDR_DOBSn) { + if (relax & WCSHDR_RADECSYS) { valtype = STRING; - vptr = wcstem.dateobs; + vptr = wcstem.radesys; - yyless(4); - BEGIN(CCCCn); + unput(' '); + + keyname = "RADECSYS"; + imherit = 0; + BEGIN(CCCCCCCa); + + } else if (relax & WCSHDR_reject) { + errmsg = "the RADECSYS keyword is deprecated, use RADESYSa"; + BEGIN(ERROR); } else { - keytype = BINTAB; - if (relax & WCSHDR_reject) { - errmsg = "DOBSna keyword is non-standard"; - BEGIN(ERROR); - } else { - BEGIN(DISCARD); - } + BEGIN(DISCARD); } } YY_BREAK -case 89: +case 165: YY_RULE_SETUP -#line 945 "wcsbth.l" +#line 1696 "wcsbth.l" { - sscanf(wcsbthtext, "EPOCH%c", &a); + sscanf(yytext, "EPOCH%c", &a); + + if (relax & WCSHDR_strict) { + errmsg = "the EPOCH keyword is deprecated, use EQUINOXa"; + BEGIN(ERROR); - if (a == ' ' || (relax & WCSHDR_EPOCHa)) { + } else if (a == ' ' || (relax & WCSHDR_EPOCHa)) { valtype = FLOAT; - vptr = &(wcstem.equinox); + vptr = &(wcstem.equinox); special = wcsbth_epoch; unput(a); - extkey = "EPOCH"; + + keyname = "EPOCH"; + imherit = 0; BEGIN(CCCCCCCa); + } else if (relax & WCSHDR_reject) { + errmsg = "EPOCH keyword may not have an alternate version code"; + BEGIN(ERROR); + } else { - keytype = IMGAUX; - if (relax & WCSHDR_reject) { - errmsg = "EPOCH keyword may not have an alternate version code"; - BEGIN(ERROR); - } else { - BEGIN(DISCARD); - } + BEGIN(DISCARD); } } YY_BREAK -case 90: -#line 969 "wcsbth.l" -case 91: +case 166: +#line 1724 "wcsbth.l" +case 167: YY_RULE_SETUP -#line 969 "wcsbth.l" +#line 1724 "wcsbth.l" { valtype = FLOAT; - vptr = &(wcstem.equinox); + vptr = &(wcstem.equinox); - if (wcsbthleng == 7) { - extkey = "EQUINOXa"; + if (yyleng == 7) { + keyname = "EQUINOXa"; + imherit = 0; BEGIN(CCCCCCCa); } else { + keyname = "EQUIna"; BEGIN(CCCCna); } } YY_BREAK -case 92: -#line 982 "wcsbth.l" -case 93: +case 168: +#line 1739 "wcsbth.l" +case 169: YY_RULE_SETUP -#line 982 "wcsbth.l" +#line 1739 "wcsbth.l" { - valtype = FLOAT; - vptr = &(wcstem.mjdavg); + valtype = STRING; + vptr = wcstem.specsys; - if (wcsbthleng == 8) { - extkey = "MJD-AVG"; - BEGIN(CCCCCCCC); + if (yyleng == 7) { + keyname = "SPECSYSa"; + BEGIN(CCCCCCCa); } else { - BEGIN(CCCCn); + keyname = "SPECna"; + BEGIN(CCCCna); } } YY_BREAK -case 94: -#line 995 "wcsbth.l" -case 95: +case 170: +#line 1753 "wcsbth.l" +case 171: YY_RULE_SETUP -#line 995 "wcsbth.l" +#line 1753 "wcsbth.l" { - valtype = FLOAT; - vptr = &(wcstem.mjdobs); + valtype = STRING; + vptr = wcstem.ssysobs; - if (wcsbthleng == 8) { - extkey = "MJD-OBS"; - BEGIN(CCCCCCCC); + if (yyleng == 7) { + keyname = "SSYSOBSa"; + BEGIN(CCCCCCCa); } else { - BEGIN(CCCCCn); + keyname = "SOBSna"; + BEGIN(CCCCna); } } YY_BREAK -case 96: -#line 1008 "wcsbth.l" -case 97: +case 172: +#line 1767 "wcsbth.l" +case 173: YY_RULE_SETUP -#line 1008 "wcsbth.l" +#line 1767 "wcsbth.l" { valtype = FLOAT; - vptr = wcstem.obsgeo; + vptr = &(wcstem.velosys); - if (wcsbthleng == 8) { - extkey = "OBSGEO-X"; - BEGIN(CCCCCCCC); + if (yyleng == 7) { + keyname = "VELOSYSa"; + BEGIN(CCCCCCCa); } else { - BEGIN(CCCCCn); + keyname = "VSYSna"; + BEGIN(CCCCna); } } YY_BREAK -case 98: -#line 1021 "wcsbth.l" -case 99: +case 174: YY_RULE_SETUP -#line 1021 "wcsbth.l" +#line 1780 "wcsbth.l" { - valtype = FLOAT; - vptr = wcstem.obsgeo + 1; + if (relax & WCSHDR_VSOURCE) { + valtype = FLOAT; + vptr = &(wcstem.zsource); + special = wcsbth_vsource; + + yyless(7); + + keyname = "VSOURCEa"; + BEGIN(CCCCCCCa); + + } else if (relax & WCSHDR_reject) { + errmsg = "the VSOURCEa keyword is deprecated, use ZSOURCEa"; + BEGIN(ERROR); - if (wcsbthleng == 8) { - extkey = "OBSGEO-Y"; - BEGIN(CCCCCCCC); } else { - BEGIN(CCCCCn); + BEGIN(DISCARD); } } YY_BREAK -case 100: -#line 1034 "wcsbth.l" -case 101: +case 175: +#line 1801 "wcsbth.l" +case 176: +#line 1802 "wcsbth.l" +case 177: YY_RULE_SETUP -#line 1034 "wcsbth.l" +#line 1802 "wcsbth.l" { - valtype = FLOAT; - vptr = wcstem.obsgeo + 2; + if (relax & WCSHDR_VSOURCE) { + valtype = FLOAT; + vptr = &(wcstem.zsource); + special = wcsbth_vsource; + + yyless(4); + keyname = "VSOUna"; + BEGIN(CCCCna); + + } else if (relax & WCSHDR_reject) { + errmsg = "VSOUna keyword is deprecated, use ZSOUna"; + BEGIN(ERROR); - if (wcsbthleng == 8) { - extkey = "OBSGEO-Z"; - BEGIN(CCCCCCCC); } else { - BEGIN(CCCCCn); + // Pretend we don't recognize it. + BEGIN(DISCARD); } } YY_BREAK -case 102: -#line 1047 "wcsbth.l" -case 103: +case 178: +#line 1823 "wcsbth.l" +case 179: YY_RULE_SETUP -#line 1047 "wcsbth.l" +#line 1823 "wcsbth.l" { - valtype = STRING; - vptr = wcstem.radesys; + valtype = FLOAT; + vptr = &(wcstem.zsource); - if (wcsbthleng == 7) { - extkey = "RADESYSa"; + if (yyleng == 7) { + keyname = "ZSOURCEa"; BEGIN(CCCCCCCa); } else { + keyname = "ZSOUna"; BEGIN(CCCCna); } } YY_BREAK -case 104: +case 180: +#line 1837 "wcsbth.l" +case 181: YY_RULE_SETUP -#line 1059 "wcsbth.l" +#line 1837 "wcsbth.l" { - if (relax & WCSHDR_RADECSYS) { - valtype = STRING; - vptr = wcstem.radesys; + valtype = STRING; + vptr = wcstem.ssyssrc; - unput(' '); - extkey = "RADECSYS"; + if (yyleng == 7) { + keyname = "SSYSSRCa"; BEGIN(CCCCCCCa); - } else { - keytype = IMGAUX; - if (relax & WCSHDR_reject) { - errmsg = "RADECSYS keyword is non-standard"; - BEGIN(ERROR); - } else { - BEGIN(DISCARD); - } + keyname = "SSRCna"; + BEGIN(CCCCna); } } YY_BREAK -case 105: -#line 1080 "wcsbth.l" -case 106: +case 182: +#line 1851 "wcsbth.l" +case 183: YY_RULE_SETUP -#line 1080 "wcsbth.l" +#line 1851 "wcsbth.l" { - valtype = STRING; - vptr = wcstem.specsys; + valtype = FLOAT; + vptr = &(wcstem.velangl); - if (wcsbthleng == 7) { - extkey = "SPECSYSa"; + if (yyleng == 7) { + keyname = "VELANGLa"; BEGIN(CCCCCCCa); } else { + keyname = "VANGna"; BEGIN(CCCCna); } } YY_BREAK -case 107: -#line 1093 "wcsbth.l" -case 108: +case 184: YY_RULE_SETUP -#line 1093 "wcsbth.l" +#line 1864 "wcsbth.l" { - valtype = STRING; - vptr = wcstem.ssysobs; + valtype = FLOAT; + auxprm = 1; + vptr = &(auxtem.rsun_ref); - if (wcsbthleng == 7) { - extkey = "SSYSOBSa"; - BEGIN(CCCCCCCa); - } else { - BEGIN(CCCCna); - } + keyname = "RSUN_REF"; + BEGIN(CCCCCCCC); } YY_BREAK -case 109: -#line 1106 "wcsbth.l" -case 110: +case 185: YY_RULE_SETUP -#line 1106 "wcsbth.l" +#line 1873 "wcsbth.l" { - valtype = STRING; - vptr = wcstem.ssyssrc; + valtype = FLOAT; + auxprm = 1; + vptr = &(auxtem.dsun_obs); - if (wcsbthleng == 7) { - extkey = "SSYSSRCa"; - BEGIN(CCCCCCCa); - } else { - BEGIN(CCCCna); - } + keyname = "DSUN_OBS"; + BEGIN(CCCCCCCC); } YY_BREAK -case 111: -#line 1119 "wcsbth.l" -case 112: +case 186: YY_RULE_SETUP -#line 1119 "wcsbth.l" +#line 1882 "wcsbth.l" { valtype = FLOAT; - vptr = &(wcstem.velosys); + auxprm = 1; + vptr = &(auxtem.crln_obs); - if (wcsbthleng == 7) { - extkey = "VELOSYSa"; - BEGIN(CCCCCCCa); - } else { - BEGIN(CCCCna); - } + keyname = "CRLN_OBS"; + BEGIN(CCCCCCCC); } YY_BREAK -case 113: -#line 1132 "wcsbth.l" -case 114: +case 187: YY_RULE_SETUP -#line 1132 "wcsbth.l" +#line 1891 "wcsbth.l" { valtype = FLOAT; - vptr = &(wcstem.velangl); + auxprm = 1; + vptr = &(auxtem.hgln_obs); - if (wcsbthleng == 7) { - extkey = "VELANGLa"; - BEGIN(CCCCCCCa); - } else { - BEGIN(CCCCna); - } + keyname = "HGLN_OBS"; + BEGIN(CCCCCCCC); } YY_BREAK -case 115: +case 188: +#line 1901 "wcsbth.l" +case 189: YY_RULE_SETUP -#line 1144 "wcsbth.l" +#line 1901 "wcsbth.l" { - sscanf(wcsbthtext, "VELREF%c", &a); - - if (a == ' ' || (relax & WCSHDR_VELREFa)) { - valtype = INTEGER; - vptr = &(wcstem.velref); + valtype = FLOAT; + auxprm = 1; + vptr = &(auxtem.hglt_obs); - unput(a); - extkey = "VELREF"; - BEGIN(CCCCCCCa); + keyname = "HGLT_OBS"; + BEGIN(CCCCCCCC); + } + YY_BREAK +case 190: +YY_RULE_SETUP +#line 1910 "wcsbth.l" +{ + valtype = FLOAT; + auxprm = 1; + vptr = &(auxtem.a_radius); - } else { - keytype = IMGAUX; - if (relax & WCSHDR_reject) { - errmsg = "VELREF keyword may not have an alternate version code"; - BEGIN(ERROR); - } else { - BEGIN(DISCARD); - } - } + keyname = "A_RADIUS"; + BEGIN(CCCCCCCC); + } + YY_BREAK +case 191: +YY_RULE_SETUP +#line 1919 "wcsbth.l" +{ + valtype = FLOAT; + auxprm = 1; + vptr = &(auxtem.b_radius); + + keyname = "B_RADIUS"; + BEGIN(CCCCCCCC); } YY_BREAK -case 116: +case 192: YY_RULE_SETUP -#line 1166 "wcsbth.l" +#line 1928 "wcsbth.l" { - if (relax & WCSHDR_VSOURCE) { - valtype = FLOAT; - vptr = &(wcstem.zsource); - special = wcsbth_vsource; - - yyless(7); - extkey = "VSOURCEa"; - BEGIN(CCCCCCCa); + valtype = FLOAT; + auxprm = 1; + vptr = &(auxtem.c_radius); - } else { - keytype = IMGAUX; - if (relax & WCSHDR_reject) { - errmsg = "VSOURCEa keyword is deprecated"; - BEGIN(ERROR); - } else { - BEGIN(DISCARD); - } - } + keyname = "C_RADIUS"; + BEGIN(CCCCCCCC); } YY_BREAK -case 117: -#line 1188 "wcsbth.l" -case 118: -#line 1189 "wcsbth.l" -case 119: +case 193: YY_RULE_SETUP -#line 1189 "wcsbth.l" +#line 1937 "wcsbth.l" { - if (relax & WCSHDR_VSOURCE) { - valtype = FLOAT; - vptr = &(wcstem.zsource); - special = wcsbth_vsource; - - yyless(4); - BEGIN(CCCCna); + valtype = FLOAT; + auxprm = 1; + vptr = &(auxtem.blon_obs); - } else { - keytype = BINTAB; - if (relax & WCSHDR_reject) { - errmsg = "VSOUna keyword is deprecated"; - BEGIN(ERROR); - } else { - /* Pretend we don't recognize it. */ - BEGIN(DISCARD); - } - } + keyname = "BLON_OBS"; + BEGIN(CCCCCCCC); } YY_BREAK -case 120: -#line 1211 "wcsbth.l" -case 121: -#line 1212 "wcsbth.l" -case 122: +case 194: YY_RULE_SETUP -#line 1212 "wcsbth.l" +#line 1946 "wcsbth.l" { - valtype = STRING; - vptr = wcstem.wcsname; + valtype = FLOAT; + auxprm = 1; + vptr = &(auxtem.blat_obs); - if (wcsbthleng == 7) { - extkey = "WCSNAMEa"; - BEGIN(CCCCCCCa); - } else { - BEGIN(CCCCna); - } + keyname = "BLAT_OBS"; + BEGIN(CCCCCCCC); } YY_BREAK -case 123: -#line 1225 "wcsbth.l" -case 124: +case 195: YY_RULE_SETUP -#line 1225 "wcsbth.l" +#line 1955 "wcsbth.l" { valtype = FLOAT; - vptr = &(wcstem.zsource); + auxprm = 1; + vptr = &(auxtem.bdis_obs); - if (wcsbthleng == 7) { - extkey = "ZSOURCEa"; - BEGIN(CCCCCCCa); - } else { - BEGIN(CCCCna); - } + keyname = "BDIS_OBS"; + BEGIN(CCCCCCCC); } YY_BREAK -case 125: +case 196: YY_RULE_SETUP -#line 1237 "wcsbth.l" +#line 1964 "wcsbth.l" { - yyless(0); - if (wcsbth_nkeyrec) { - wcsbth_nkeyrec = 0; - errmsg = "Keyrecords following the END keyrecord were ignored"; + if (yyextra->nkeyrec) { + yyextra->nkeyrec = 0; + errmsg = "keyrecords following the END keyrecord were ignored"; BEGIN(ERROR); } else { BEGIN(DISCARD); } } YY_BREAK -case 126: +case 197: YY_RULE_SETUP -#line 1248 "wcsbth.l" +#line 1974 "wcsbth.l" { - yyless(0); BEGIN(DISCARD); } YY_BREAK -case 127: -#line 1254 "wcsbth.l" -case 128: +case 198: +#line 1979 "wcsbth.l" +case 199: YY_RULE_SETUP -#line 1254 "wcsbth.l" +#line 1979 "wcsbth.l" { - /* Image-header keyword. */ - keytype = IMGAXIS; if (relax & WCSHDR_ALLIMG) { - sscanf(wcsbthtext, "%d%c", &i, &a); + sscanf(yytext, "%d%c", &i, &a); + keytype = IMGAXIS; BEGIN(VALUE); } else if (relax & WCSHDR_reject) { errmsg = errtxt; sprintf(errmsg, - "Image-header keyword %s in binary table", extkey); + "image-header keyword %s in binary table", keyname); BEGIN(ERROR); } else { - /* Pretend we don't recognize it. */ + // Pretend we don't recognize it. BEGIN(DISCARD); } } YY_BREAK -case 129: +case 200: +#line 1998 "wcsbth.l" +case 201: YY_RULE_SETUP -#line 1273 "wcsbth.l" +#line 1998 "wcsbth.l" { - /* Invalid axis number in image-header keyword. */ - keytype = IMGAXIS; if (relax & WCSHDR_ALLIMG) { - /* Will also be flagged by as invalid. */ - sscanf(wcsbthtext, "%3d", &i); - BEGIN(VALUE); + if (relax & WCSHDR_reject) { + // Violates the basic FITS standard. + errmsg = "indices in parameterized keywords must not have " + "leading zeroes"; + BEGIN(ERROR); + + } else { + // Pretend we don't recognize it. + BEGIN(DISCARD); + } + + } else if (relax & WCSHDR_reject) { + errmsg = errtxt; + sprintf(errmsg, + "invalid image-header keyword %s in binary table", keyname); + BEGIN(ERROR); } else { - /* Pretend we don't recognize it. */ + // Pretend we don't recognize it. BEGIN(DISCARD); } } YY_BREAK -case 130: -#line 1288 "wcsbth.l" -case 131: -#line 1289 "wcsbth.l" -case 132: -#line 1290 "wcsbth.l" -case 133: -#line 1291 "wcsbth.l" -case 134: -#line 1292 "wcsbth.l" -case 135: +case 202: +#line 2024 "wcsbth.l" +case 203: +#line 2025 "wcsbth.l" +case 204: +YY_RULE_SETUP +#line 2025 "wcsbth.l" +{ + // Anything that has fallen through to this point must contain + // an invalid axis number. + if (relax & WCSHDR_ALLIMG) { + errmsg = "axis number must exceed 0"; + BEGIN(ERROR); + + } else if (relax & WCSHDR_reject) { + errmsg = errtxt; + sprintf(errmsg, + "invalid image-header keyword %s in binary table", keyname); + BEGIN(ERROR); + + } else { + // Pretend we don't recognize it. + BEGIN(DISCARD); + } + } + YY_BREAK +case 205: +YY_RULE_SETUP +#line 2044 "wcsbth.l" +{ + if (relax & WCSHDR_reject) { + // Looks too much like a FITS WCS keyword not to flag it. + errmsg = errtxt; + sprintf(errmsg, "keyword looks very much like %s but isn't", + keyname); + BEGIN(ERROR); + + } else { + // Pretend we don't recognize it. + BEGIN(DISCARD); + } + } + YY_BREAK +case 206: +#line 2059 "wcsbth.l" +case 207: +#line 2060 "wcsbth.l" +case 208: +#line 2061 "wcsbth.l" +case 209: +#line 2062 "wcsbth.l" +case 210: +#line 2063 "wcsbth.l" +case 211: YY_RULE_SETUP -#line 1292 "wcsbth.l" +#line 2063 "wcsbth.l" { if (vptr) { WCSBTH_PUTBACK; BEGIN((YY_START == iCCCCn) ? iCCCna : TCCCna); + + } else if (relax & WCSHDR_reject) { + errmsg = errtxt; + sprintf(errmsg, "%s keyword is non-standard", keyname); + BEGIN(ERROR); + } else { - keytype = (YY_START == iCCCCn) ? BIMGARR : PIXLIST; - if (relax & WCSHDR_reject) { - errmsg = errtxt; - sprintf(errmsg, "%s keyword is non-standard", extkey); - BEGIN(ERROR); - } else { - BEGIN(DISCARD); - } + BEGIN(DISCARD); } } YY_BREAK -case 136: -#line 1309 "wcsbth.l" -case 137: -#line 1310 "wcsbth.l" -case 138: -#line 1311 "wcsbth.l" -case 139: +case 212: +#line 2079 "wcsbth.l" +case 213: +#line 2080 "wcsbth.l" +case 214: +#line 2081 "wcsbth.l" +case 215: YY_RULE_SETUP -#line 1311 "wcsbth.l" +#line 2081 "wcsbth.l" { if (vptr && (relax & WCSHDR_LONGKEY)) { WCSBTH_PUTBACK; BEGIN((YY_START == iCCCCn) ? iCCCna : TCCCna); - } else { - keytype = (YY_START == iCCCna) ? BIMGARR : PIXLIST; - if (relax & WCSHDR_reject) { - errmsg = errtxt; - if (!vptr) { - sprintf(errmsg, "%s keyword is non-standard", extkey); - } else { - sprintf(errmsg, - "%s keyword may not have an alternate version code", extkey); - } - BEGIN(ERROR); - + } else if (relax & WCSHDR_reject) { + errmsg = errtxt; + if (!vptr) { + sprintf(errmsg, "%s keyword is non-standard", keyname); } else { - /* Pretend we don't recognize it. */ - BEGIN(DISCARD); + sprintf(errmsg, + "%s keyword may not have an alternate version code", keyname); } + BEGIN(ERROR); + + } else { + // Pretend we don't recognize it. + BEGIN(DISCARD); } } YY_BREAK -case 140: -#line 1336 "wcsbth.l" -case 141: +case 216: +#line 2103 "wcsbth.l" +case 217: YY_RULE_SETUP -#line 1336 "wcsbth.l" +#line 2103 "wcsbth.l" { BEGIN(DISCARD); } YY_BREAK -case 142: -#line 1341 "wcsbth.l" -case 143: -#line 1342 "wcsbth.l" -case 144: -#line 1343 "wcsbth.l" -case 145: -#line 1344 "wcsbth.l" -case 146: -#line 1345 "wcsbth.l" -case 147: +case 218: +#line 2108 "wcsbth.l" +case 219: +#line 2109 "wcsbth.l" +case 220: +#line 2110 "wcsbth.l" +case 221: +#line 2111 "wcsbth.l" +case 222: +#line 2112 "wcsbth.l" +case 223: YY_RULE_SETUP -#line 1345 "wcsbth.l" +#line 2112 "wcsbth.l" { - sscanf(wcsbthtext, "%d%c", &n, &a); + sscanf(yytext, "%d%c", &n, &a); if (YY_START == TCCCna) i = wcsbth_colax(*wcs, &alts, n, a); keytype = (YY_START == iCCCna) ? BIMGARR : PIXLIST; BEGIN(VALUE); } YY_BREAK -case 148: -#line 1353 "wcsbth.l" -case 149: +case 224: +#line 2120 "wcsbth.l" +case 225: YY_RULE_SETUP -#line 1353 "wcsbth.l" +#line 2120 "wcsbth.l" { BEGIN(DISCARD); } YY_BREAK -case 150: -#line 1358 "wcsbth.l" -case 151: -#line 1359 "wcsbth.l" -case 152: -#line 1360 "wcsbth.l" -case 153: +case 226: +#line 2125 "wcsbth.l" +case 227: +#line 2126 "wcsbth.l" +case 228: +#line 2127 "wcsbth.l" +case 229: +YY_RULE_SETUP +#line 2127 "wcsbth.l" +{ + if (relax & WCSHDR_ALLIMG) { + sscanf(yytext, "%d_%d%c", &i, &j, &a); + keytype = IMGAXIS; + BEGIN(VALUE); + + } else if (relax & WCSHDR_reject) { + errmsg = errtxt; + sprintf(errmsg, + "image-header keyword %s in binary table", keyname); + BEGIN(ERROR); + + } else { + // Pretend we don't recognize it. + BEGIN(DISCARD); + } + } + YY_BREAK +case 230: +#line 2146 "wcsbth.l" +case 231: +#line 2147 "wcsbth.l" +case 232: +#line 2148 "wcsbth.l" +case 233: +#line 2149 "wcsbth.l" +case 234: +#line 2150 "wcsbth.l" +case 235: +#line 2151 "wcsbth.l" +case 236: +#line 2152 "wcsbth.l" +case 237: +#line 2153 "wcsbth.l" +case 238: +#line 2154 "wcsbth.l" +case 239: +#line 2155 "wcsbth.l" +case 240: +#line 2156 "wcsbth.l" +case 241: +#line 2157 "wcsbth.l" +case 242: +#line 2158 "wcsbth.l" +case 243: +#line 2159 "wcsbth.l" +case 244: +#line 2160 "wcsbth.l" +case 245: +#line 2161 "wcsbth.l" +case 246: +#line 2162 "wcsbth.l" +case 247: +#line 2163 "wcsbth.l" +case 248: +#line 2164 "wcsbth.l" +case 249: +#line 2165 "wcsbth.l" +case 250: +YY_RULE_SETUP +#line 2165 "wcsbth.l" +{ + if (relax & WCSHDR_ALLIMG) { + if (((altlin == 1) && (relax & WCSHDR_PC0i_0ja)) || + ((altlin == 2) && (relax & WCSHDR_CD0i_0ja))) { + sscanf(yytext, "%d_%d%c", &i, &j, &a); + keytype = IMGAXIS; + BEGIN(VALUE); + + } else if (relax & WCSHDR_reject) { + errmsg = "indices in parameterized keywords must not have " + "leading zeroes"; + BEGIN(ERROR); + + } else { + // Pretend we don't recognize it. + BEGIN(DISCARD); + } + + } else if (relax & WCSHDR_reject) { + errmsg = errtxt; + sprintf(errmsg, + "invalid image-header keyword %s in binary table", keyname); + BEGIN(ERROR); + + } else { + // Pretend we don't recognize it. + BEGIN(DISCARD); + } + } + YY_BREAK +case 251: +#line 2196 "wcsbth.l" +case 252: +#line 2197 "wcsbth.l" +case 253: +#line 2198 "wcsbth.l" +case 254: +#line 2199 "wcsbth.l" +case 255: +#line 2200 "wcsbth.l" +case 256: +#line 2201 "wcsbth.l" +case 257: +#line 2202 "wcsbth.l" +case 258: +#line 2203 "wcsbth.l" +case 259: +#line 2204 "wcsbth.l" +case 260: YY_RULE_SETUP -#line 1360 "wcsbth.l" +#line 2204 "wcsbth.l" { - /* Image-header keyword. */ + // Anything that has fallen through to this point must contain + // an invalid axis number. if (relax & WCSHDR_ALLIMG) { - sscanf(wcsbthtext, "%d_%d%c", &i, &j, &a); - keytype = IMGAXIS; - BEGIN(VALUE); + errmsg = "axis number must exceed 0"; + BEGIN(ERROR); } else if (relax & WCSHDR_reject) { errmsg = errtxt; sprintf(errmsg, - "Image-header keyword %s in binary table", extkey); + "invalid image-header keyword %s in binary table", keyname); BEGIN(ERROR); } else { - /* Pretend we don't recognize it. */ + // Pretend we don't recognize it. BEGIN(DISCARD); } } YY_BREAK -case 154: -#line 1380 "wcsbth.l" -case 155: -#line 1381 "wcsbth.l" -case 156: -#line 1382 "wcsbth.l" -case 157: -#line 1383 "wcsbth.l" -case 158: -#line 1384 "wcsbth.l" -case 159: +case 261: +#line 2224 "wcsbth.l" +case 262: +#line 2225 "wcsbth.l" +case 263: +#line 2226 "wcsbth.l" +case 264: +#line 2227 "wcsbth.l" +case 265: +#line 2228 "wcsbth.l" +case 266: +#line 2229 "wcsbth.l" +case 267: +#line 2230 "wcsbth.l" +case 268: +#line 2231 "wcsbth.l" +case 269: +#line 2232 "wcsbth.l" +case 270: YY_RULE_SETUP -#line 1384 "wcsbth.l" +#line 2232 "wcsbth.l" { - /* Invalid axis number in image-header keyword. */ if (relax & WCSHDR_ALLIMG) { - /* Will be flagged by as invalid. */ - sscanf(wcsbthtext, "%d_%d", &i, &j); - keytype = IMGAXIS; - BEGIN(VALUE); + errmsg = errtxt; + sprintf(errmsg, "%s keyword must use an underscore, not a dash", + keyname); + BEGIN(ERROR); + + } else if (relax & WCSHDR_reject) { + errmsg = errtxt; + sprintf(errmsg, + "invalid image-header keyword %s in binary table", keyname); + BEGIN(ERROR); } else { - /* Pretend we don't recognize it. */ + // Pretend we don't recognize it. BEGIN(DISCARD); } } YY_BREAK -case 160: +case 271: YY_RULE_SETUP -#line 1398 "wcsbth.l" +#line 2251 "wcsbth.l" { - /* This covers the defunct forms CD00i00j and PC00i00j. */ - if (((relax & WCSHDR_PC00i00j) && (altlin == 1)) || - ((relax & WCSHDR_CD00i00j) && (altlin == 2))) { - sscanf(wcsbthtext, "%3d%3d", &i, &j); - a = ' '; - keytype = IMGAXIS; - BEGIN(VALUE); + // This covers the defunct forms CD00i00j and PC00i00j. + if (relax & WCSHDR_ALLIMG) { + if (((altlin == 1) && (relax & WCSHDR_PC00i00j)) || + ((altlin == 2) && (relax & WCSHDR_CD00i00j))) { + sscanf(yytext, "%3d%3d", &i, &j); + a = ' '; + keytype = IMGAXIS; + BEGIN(VALUE); + + } else if (relax & WCSHDR_reject) { + errmsg = errtxt; + sprintf(errmsg, + "this form of the %s keyword is deprecated, use %s", + keyname, keyname); + BEGIN(ERROR); + + } else { + // Pretend we don't recognize it. + BEGIN(DISCARD); + } } else if (relax & WCSHDR_reject) { errmsg = errtxt; - sprintf(errmsg, "Defunct form of %si_ja keyword", - (altlin==1) ? "PC" : "CD"); + sprintf(errmsg, + "deprecated image-header keyword %s in binary table", keyname); BEGIN(ERROR); } else { - /* Pretend we don't recognize it. */ + // Pretend we don't recognize it. BEGIN(DISCARD); } } YY_BREAK -case 161: +case 272: YY_RULE_SETUP -#line 1419 "wcsbth.l" +#line 2285 "wcsbth.l" { BEGIN(DISCARD); } YY_BREAK -case 162: -#line 1424 "wcsbth.l" -case 163: -#line 1425 "wcsbth.l" -case 164: +case 273: +#line 2290 "wcsbth.l" +case 274: +#line 2291 "wcsbth.l" +case 275: YY_RULE_SETUP -#line 1425 "wcsbth.l" +#line 2291 "wcsbth.l" { - sscanf(wcsbthtext, "%d%c", &n, &a); + sscanf(yytext, "%d%c", &n, &a); keytype = BIMGARR; BEGIN(VALUE); } YY_BREAK -case 165: -#line 1432 "wcsbth.l" -case 166: -#line 1433 "wcsbth.l" -case 167: -#line 1434 "wcsbth.l" -case 168: -#line 1435 "wcsbth.l" -case 169: -#line 1436 "wcsbth.l" -case 170: +case 276: +#line 2298 "wcsbth.l" +case 277: +#line 2299 "wcsbth.l" +case 278: +#line 2300 "wcsbth.l" +case 279: +#line 2301 "wcsbth.l" +case 280: +#line 2302 "wcsbth.l" +case 281: YY_RULE_SETUP -#line 1436 "wcsbth.l" +#line 2302 "wcsbth.l" { if (relax & WCSHDR_LONGKEY) { WCSBTH_PUTBACK; @@ -18714,54 +29295,54 @@ YY_RULE_SETUP } else if (relax & WCSHDR_reject) { errmsg = errtxt; - sprintf(errmsg, "%s keyword is non-standard", extkey); + sprintf(errmsg, "%s keyword is non-standard", keyname); BEGIN(ERROR); } else { - /* Pretend we don't recognize it. */ + // Pretend we don't recognize it. BEGIN(DISCARD); } } YY_BREAK -case 171: +case 282: YY_RULE_SETUP -#line 1452 "wcsbth.l" +#line 2318 "wcsbth.l" { BEGIN(DISCARD); } YY_BREAK -case 172: -#line 1457 "wcsbth.l" -case 173: -#line 1458 "wcsbth.l" -case 174: -#line 1459 "wcsbth.l" -case 175: -#line 1460 "wcsbth.l" -case 176: -#line 1461 "wcsbth.l" -case 177: +case 283: +#line 2323 "wcsbth.l" +case 284: +#line 2324 "wcsbth.l" +case 285: +#line 2325 "wcsbth.l" +case 286: +#line 2326 "wcsbth.l" +case 287: +#line 2327 "wcsbth.l" +case 288: YY_RULE_SETUP -#line 1461 "wcsbth.l" +#line 2327 "wcsbth.l" { - sscanf(wcsbthtext, "%d_%d%c", &n, &k, &a); + sscanf(yytext, "%d_%d%c", &n, &k, &a); i = wcsbth_colax(*wcs, &alts, n, a); j = wcsbth_colax(*wcs, &alts, k, a); keytype = PIXLIST; BEGIN(VALUE); } YY_BREAK -case 178: -#line 1470 "wcsbth.l" -case 179: -#line 1471 "wcsbth.l" -case 180: -#line 1472 "wcsbth.l" -case 181: +case 289: +#line 2336 "wcsbth.l" +case 290: +#line 2337 "wcsbth.l" +case 291: +#line 2338 "wcsbth.l" +case 292: YY_RULE_SETUP -#line 1472 "wcsbth.l" +#line 2338 "wcsbth.l" { - sscanf(wcsbthtext, "%d_%d", &n, &k); + sscanf(yytext, "%d_%d", &n, &k); a = ' '; i = wcsbth_colax(*wcs, &alts, n, a); j = wcsbth_colax(*wcs, &alts, k, a); @@ -18769,77 +29350,94 @@ YY_RULE_SETUP BEGIN(VALUE); } YY_BREAK -case 182: +case 293: YY_RULE_SETUP -#line 1481 "wcsbth.l" +#line 2347 "wcsbth.l" { BEGIN(DISCARD); } YY_BREAK -case 183: -#line 1486 "wcsbth.l" -case 184: -YY_RULE_SETUP -#line 1486 "wcsbth.l" -{ - yyless(0); - BEGIN(CCCCCia); - } - YY_BREAK -case 185: -#line 1492 "wcsbth.l" -case 186: +case 294: +#line 2352 "wcsbth.l" +case 295: +#line 2353 "wcsbth.l" +case 296: YY_RULE_SETUP -#line 1492 "wcsbth.l" +#line 2353 "wcsbth.l" { - if (relax & WCSHDR_CROTAia) { - yyless(0); - BEGIN(CCCCCia); + if (relax & WCSHDR_ALLIMG) { + a = ' '; + sscanf(yytext, "%d%c", &i, &a); + + if (relax & WCSHDR_strict) { + errmsg = "the CROTAn keyword is deprecated, use PCi_ja"; + BEGIN(ERROR); + + } else if (a == ' ' || relax & WCSHDR_CROTAia) { + yyless(0); + BEGIN(CCCCCia); + + } else if (relax & WCSHDR_reject) { + errmsg = "CROTAn keyword may not have an alternate version code"; + BEGIN(ERROR); + + } else { + // Pretend we don't recognize it. + BEGIN(DISCARD); + } } else if (relax & WCSHDR_reject) { - errmsg = "CROTAn keyword may not have an alternate version code"; + errmsg = errtxt; + sprintf(errmsg, + "deprecated image-header keyword %s in binary table", keyname); BEGIN(ERROR); } else { - /* Pretend we don't recognize it. */ + // Pretend we don't recognize it. BEGIN(DISCARD); } } YY_BREAK -case 187: +case 297: YY_RULE_SETUP -#line 1507 "wcsbth.l" +#line 2387 "wcsbth.l" { - BEGIN(DISCARD); + if (relax & WCSHDR_ALLIMG) { + yyless(0); + BEGIN(CCCCCia); + } else { + // Let it go. + BEGIN(DISCARD); + } } YY_BREAK -case 188: -#line 1512 "wcsbth.l" -case 189: -#line 1513 "wcsbth.l" -case 190: -#line 1514 "wcsbth.l" -case 191: -#line 1515 "wcsbth.l" -case 192: -#line 1516 "wcsbth.l" -case 193: +case 298: +#line 2398 "wcsbth.l" +case 299: +#line 2399 "wcsbth.l" +case 300: +#line 2400 "wcsbth.l" +case 301: +#line 2401 "wcsbth.l" +case 302: +#line 2402 "wcsbth.l" +case 303: YY_RULE_SETUP -#line 1516 "wcsbth.l" +#line 2402 "wcsbth.l" { WCSBTH_PUTBACK; BEGIN((YY_START == iCROTn) ? iCCCna : TCCCna); } YY_BREAK -case 194: -#line 1522 "wcsbth.l" -case 195: -#line 1523 "wcsbth.l" -case 196: -#line 1524 "wcsbth.l" -case 197: +case 304: +#line 2408 "wcsbth.l" +case 305: +#line 2409 "wcsbth.l" +case 306: +#line 2410 "wcsbth.l" +case 307: YY_RULE_SETUP -#line 1524 "wcsbth.l" +#line 2410 "wcsbth.l" { if (relax & WCSHDR_CROTAia) { WCSBTH_PUTBACK; @@ -18848,37 +29446,37 @@ YY_RULE_SETUP } else if (relax & WCSHDR_reject) { errmsg = errtxt; sprintf(errmsg, - "%s keyword may not have an alternate version code", extkey); + "%s keyword may not have an alternate version code", keyname); BEGIN(ERROR); } else { - /* Pretend we don't recognize it. */ + // Pretend we don't recognize it. BEGIN(DISCARD); } } YY_BREAK -case 198: -#line 1542 "wcsbth.l" -case 199: +case 308: +#line 2428 "wcsbth.l" +case 309: YY_RULE_SETUP -#line 1542 "wcsbth.l" +#line 2428 "wcsbth.l" { BEGIN(DISCARD); } YY_BREAK -case 200: -#line 1547 "wcsbth.l" -case 201: +case 310: +#line 2433 "wcsbth.l" +case 311: YY_RULE_SETUP -#line 1547 "wcsbth.l" +#line 2433 "wcsbth.l" { - /* Image-header keyword. */ - if (relax & (WCSHDR_AUXIMG | WCSHDR_ALLIMG)) { + // Image-header keyword. + if (imherit || (relax & (WCSHDR_AUXIMG | WCSHDR_ALLIMG))) { if (YY_START == CCCCCCCa) { - sscanf(wcsbthtext, "%c", &a); + sscanf(yytext, "%c", &a); } else { a = 0; - unput(wcsbthtext[0]); + unput(yytext[0]); } keytype = IMGAUX; BEGIN(VALUE); @@ -18886,176 +29484,300 @@ YY_RULE_SETUP } else if (relax & WCSHDR_reject) { errmsg = errtxt; sprintf(errmsg, - "Image-header keyword %s in binary table", extkey); + "image-header keyword %s in binary table", keyname); BEGIN(ERROR); } else { - /* Pretend we don't recognize it. */ + // Pretend we don't recognize it. BEGIN(DISCARD); } } YY_BREAK -case 202: +case 312: YY_RULE_SETUP -#line 1571 "wcsbth.l" +#line 2457 "wcsbth.l" { - BEGIN(DISCARD); + if (relax & WCSHDR_reject) { + // Looks too much like a FITS WCS keyword not to flag it. + errmsg = errtxt; + sprintf(errmsg, "invalid alternate code, keyword resembles %s " + "but isn't", keyname); + BEGIN(ERROR); + + } else { + // Pretend we don't recognize it. + BEGIN(DISCARD); + } } YY_BREAK -case 203: -#line 1576 "wcsbth.l" -case 204: -#line 1577 "wcsbth.l" -case 205: -#line 1578 "wcsbth.l" -case 206: -#line 1579 "wcsbth.l" -case 207: +case 313: +#line 2472 "wcsbth.l" +case 314: +#line 2473 "wcsbth.l" +case 315: +#line 2474 "wcsbth.l" +case 316: +#line 2475 "wcsbth.l" +case 317: YY_RULE_SETUP -#line 1579 "wcsbth.l" +#line 2475 "wcsbth.l" { - sscanf(wcsbthtext, "%d%c", &n, &a); + sscanf(yytext, "%d%c", &n, &a); keytype = BINTAB; BEGIN(VALUE); } YY_BREAK -case 208: +case 318: YY_RULE_SETUP -#line 1585 "wcsbth.l" +#line 2481 "wcsbth.l" { - sscanf(wcsbthtext, "%d", &n); + sscanf(yytext, "%d", &n); a = ' '; keytype = BINTAB; BEGIN(VALUE); } YY_BREAK -case 209: -#line 1593 "wcsbth.l" -case 210: +case 319: +#line 2489 "wcsbth.l" +case 320: YY_RULE_SETUP -#line 1593 "wcsbth.l" +#line 2489 "wcsbth.l" { BEGIN(DISCARD); } YY_BREAK -case 211: -#line 1598 "wcsbth.l" -case 212: -#line 1599 "wcsbth.l" -case 213: -#line 1600 "wcsbth.l" -case 214: -#line 1601 "wcsbth.l" -case 215: -#line 1602 "wcsbth.l" -case 216: -#line 1603 "wcsbth.l" -case 217: +case 321: +#line 2494 "wcsbth.l" +case 322: +#line 2495 "wcsbth.l" +case 323: +#line 2496 "wcsbth.l" +case 324: +#line 2497 "wcsbth.l" +case 325: +#line 2498 "wcsbth.l" +case 326: +#line 2499 "wcsbth.l" +case 327: YY_RULE_SETUP -#line 1603 "wcsbth.l" +#line 2499 "wcsbth.l" { - sscanf(wcsbthtext, "%d", &n); + sscanf(yytext, "%d", &n); a = 0; keytype = BINTAB; BEGIN(VALUE); } YY_BREAK -case 218: -#line 1611 "wcsbth.l" -case 219: +case 328: +#line 2507 "wcsbth.l" +case 329: YY_RULE_SETUP -#line 1611 "wcsbth.l" +#line 2507 "wcsbth.l" { BEGIN(DISCARD); } YY_BREAK -case 220: -#line 1616 "wcsbth.l" -case 221: -#line 1617 "wcsbth.l" -case 222: -#line 1618 "wcsbth.l" -case 223: +case 330: +#line 2512 "wcsbth.l" +case 331: +#line 2513 "wcsbth.l" +case 332: +#line 2514 "wcsbth.l" +case 333: YY_RULE_SETUP -#line 1618 "wcsbth.l" +#line 2514 "wcsbth.l" { - /* Image-header keyword. */ + // Image-header keyword. if (relax & WCSHDR_ALLIMG) { - sscanf(wcsbthtext, "%d_%d%c", &i, &m, &a); + sscanf(yytext, "%d_%d%c", &i, &m, &a); keytype = IMGAXIS; BEGIN(VALUE); } else if (relax & WCSHDR_reject) { errmsg = errtxt; sprintf(errmsg, - "Image-header keyword %s in binary table", extkey); + "image-header keyword %s in binary table", keyname); BEGIN(ERROR); } else { - /* Pretend we don't recognize it. */ + // Pretend we don't recognize it. BEGIN(DISCARD); } } YY_BREAK -case 224: -#line 1638 "wcsbth.l" -case 225: -#line 1639 "wcsbth.l" -case 226: -#line 1640 "wcsbth.l" -case 227: -#line 1641 "wcsbth.l" -case 228: -#line 1642 "wcsbth.l" -case 229: +case 334: +#line 2534 "wcsbth.l" +case 335: +#line 2535 "wcsbth.l" +case 336: +#line 2536 "wcsbth.l" +case 337: +#line 2537 "wcsbth.l" +case 338: +#line 2538 "wcsbth.l" +case 339: +#line 2539 "wcsbth.l" +case 340: +#line 2540 "wcsbth.l" +case 341: +#line 2541 "wcsbth.l" +case 342: +#line 2542 "wcsbth.l" +case 343: +#line 2543 "wcsbth.l" +case 344: +#line 2544 "wcsbth.l" +case 345: +#line 2545 "wcsbth.l" +case 346: +#line 2546 "wcsbth.l" +case 347: +#line 2547 "wcsbth.l" +case 348: +#line 2548 "wcsbth.l" +case 349: +#line 2549 "wcsbth.l" +case 350: +#line 2550 "wcsbth.l" +case 351: +#line 2551 "wcsbth.l" +case 352: +#line 2552 "wcsbth.l" +case 353: +#line 2553 "wcsbth.l" +case 354: YY_RULE_SETUP -#line 1642 "wcsbth.l" +#line 2553 "wcsbth.l" { - /* Invalid parameter in image-header keyword. */ if (relax & WCSHDR_ALLIMG) { - /* Will be flagged by as invalid. */ - sscanf(wcsbthtext, "%d_%d", &i, &m); - keytype = IMGAXIS; - BEGIN(VALUE); + if (((valtype == FLOAT) && (relax & WCSHDR_PV0i_0ma)) || + ((valtype == STRING) && (relax & WCSHDR_PS0i_0ma))) { + sscanf(yytext, "%d_%d%c", &i, &m, &a); + keytype = IMGAXIS; + BEGIN(VALUE); + + } else if (relax & WCSHDR_reject) { + errmsg = "indices in parameterized keywords must not have " + "leading zeroes"; + BEGIN(ERROR); + + } else { + // Pretend we don't recognize it. + BEGIN(DISCARD); + } + + } else if (relax & WCSHDR_reject) { + errmsg = errtxt; + sprintf(errmsg, + "invalid image-header keyword %s in binary table", keyname); + BEGIN(ERROR); } else { - /* Pretend we don't recognize it. */ + // Pretend we don't recognize it. BEGIN(DISCARD); } } YY_BREAK -case 230: +case 355: +#line 2584 "wcsbth.l" +case 356: +#line 2585 "wcsbth.l" +case 357: +#line 2586 "wcsbth.l" +case 358: +#line 2587 "wcsbth.l" +case 359: +#line 2588 "wcsbth.l" +case 360: +#line 2589 "wcsbth.l" +case 361: +#line 2590 "wcsbth.l" +case 362: +#line 2591 "wcsbth.l" +case 363: +#line 2592 "wcsbth.l" +case 364: +YY_RULE_SETUP +#line 2592 "wcsbth.l" +{ + if (relax & WCSHDR_ALLIMG) { + // Anything that has fallen through to this point must contain + // an invalid parameter. + errmsg = "axis number must exceed 0"; + BEGIN(ERROR); + + } else if (relax & WCSHDR_reject) { + errmsg = errtxt; + sprintf(errmsg, + "invalid image-header keyword %s in binary table", keyname); + BEGIN(ERROR); + + } else { + // Pretend we don't recognize it. + BEGIN(DISCARD); + } + } + YY_BREAK +case 365: +#line 2612 "wcsbth.l" +case 366: +#line 2613 "wcsbth.l" +case 367: +#line 2614 "wcsbth.l" +case 368: +#line 2615 "wcsbth.l" +case 369: +#line 2616 "wcsbth.l" +case 370: +#line 2617 "wcsbth.l" +case 371: +#line 2618 "wcsbth.l" +case 372: +#line 2619 "wcsbth.l" +case 373: +#line 2620 "wcsbth.l" +case 374: +YY_RULE_SETUP +#line 2620 "wcsbth.l" +{ + errmsg = errtxt; + sprintf(errmsg, "%s keyword must use an underscore, not a dash", + keyname); + BEGIN(ERROR); + } + YY_BREAK +case 375: YY_RULE_SETUP -#line 1656 "wcsbth.l" +#line 2627 "wcsbth.l" { BEGIN(DISCARD); } YY_BREAK -case 231: -#line 1661 "wcsbth.l" -case 232: -#line 1662 "wcsbth.l" -case 233: -#line 1663 "wcsbth.l" -case 234: -#line 1664 "wcsbth.l" -case 235: -#line 1665 "wcsbth.l" -case 236: -#line 1666 "wcsbth.l" -case 237: -#line 1667 "wcsbth.l" -case 238: -#line 1668 "wcsbth.l" -case 239: -#line 1669 "wcsbth.l" -case 240: -#line 1670 "wcsbth.l" -case 241: -#line 1671 "wcsbth.l" -case 242: +case 376: +#line 2632 "wcsbth.l" +case 377: +#line 2633 "wcsbth.l" +case 378: +#line 2634 "wcsbth.l" +case 379: +#line 2635 "wcsbth.l" +case 380: +#line 2636 "wcsbth.l" +case 381: +#line 2637 "wcsbth.l" +case 382: +#line 2638 "wcsbth.l" +case 383: +#line 2639 "wcsbth.l" +case 384: +#line 2640 "wcsbth.l" +case 385: +#line 2641 "wcsbth.l" +case 386: +#line 2642 "wcsbth.l" +case 387: YY_RULE_SETUP -#line 1671 "wcsbth.l" +#line 2642 "wcsbth.l" { if (relax & WCSHDR_LONGKEY) { WCSBTH_PUTBACK; @@ -19063,135 +29785,149 @@ YY_RULE_SETUP } else if (relax & WCSHDR_reject) { errmsg = errtxt; - sprintf(errmsg, "%s keyword is non-standard", extkey); + sprintf(errmsg, "the %s keyword is non-standard", keyname); BEGIN(ERROR); } else { - /* Pretend we don't recognize it. */ + // Pretend we don't recognize it. BEGIN(DISCARD); } } YY_BREAK -case 243: -#line 1688 "wcsbth.l" -case 244: +case 388: +#line 2659 "wcsbth.l" +case 389: YY_RULE_SETUP -#line 1688 "wcsbth.l" +#line 2659 "wcsbth.l" { BEGIN(DISCARD); } YY_BREAK -case 245: -#line 1693 "wcsbth.l" -case 246: -#line 1694 "wcsbth.l" -case 247: -#line 1695 "wcsbth.l" -case 248: -#line 1696 "wcsbth.l" -case 249: -#line 1697 "wcsbth.l" -case 250: -#line 1698 "wcsbth.l" -case 251: -#line 1699 "wcsbth.l" -case 252: -#line 1700 "wcsbth.l" -case 253: -#line 1701 "wcsbth.l" -case 254: -#line 1702 "wcsbth.l" -case 255: -#line 1703 "wcsbth.l" -case 256: +case 390: +#line 2664 "wcsbth.l" +case 391: +#line 2665 "wcsbth.l" +case 392: +#line 2666 "wcsbth.l" +case 393: +#line 2667 "wcsbth.l" +case 394: +#line 2668 "wcsbth.l" +case 395: +#line 2669 "wcsbth.l" +case 396: +#line 2670 "wcsbth.l" +case 397: +#line 2671 "wcsbth.l" +case 398: +#line 2672 "wcsbth.l" +case 399: +#line 2673 "wcsbth.l" +case 400: +#line 2674 "wcsbth.l" +case 401: YY_RULE_SETUP -#line 1703 "wcsbth.l" +#line 2674 "wcsbth.l" { - sscanf(wcsbthtext, "%d_%d%c", &n, &m, &a); + sscanf(yytext, "%d_%d%c", &n, &m, &a); if (YY_START == TCn_ma) i = wcsbth_colax(*wcs, &alts, n, a); keytype = (YY_START == iCn_ma) ? BIMGARR : PIXLIST; BEGIN(VALUE); } YY_BREAK -case 257: -#line 1711 "wcsbth.l" -case 258: -#line 1712 "wcsbth.l" -case 259: -#line 1713 "wcsbth.l" -case 260: -#line 1714 "wcsbth.l" -case 261: -#line 1715 "wcsbth.l" -case 262: -#line 1716 "wcsbth.l" -case 263: -#line 1717 "wcsbth.l" -case 264: +case 402: +#line 2682 "wcsbth.l" +case 403: +#line 2683 "wcsbth.l" +case 404: +#line 2684 "wcsbth.l" +case 405: +#line 2685 "wcsbth.l" +case 406: +#line 2686 "wcsbth.l" +case 407: +#line 2687 "wcsbth.l" +case 408: +#line 2688 "wcsbth.l" +case 409: YY_RULE_SETUP -#line 1717 "wcsbth.l" +#line 2688 "wcsbth.l" { - /* Invalid combinations will be flagged by . */ - sscanf(wcsbthtext, "%d_%d", &n, &m); + // Invalid combinations will be flagged by . + sscanf(yytext, "%d_%d", &n, &m); a = ' '; if (YY_START == TCn_ma) i = wcsbth_colax(*wcs, &alts, n, a); keytype = (YY_START == iCn_ma) ? BIMGARR : PIXLIST; BEGIN(VALUE); } YY_BREAK -case 265: -#line 1727 "wcsbth.l" -case 266: +case 410: +#line 2698 "wcsbth.l" +case 411: YY_RULE_SETUP -#line 1727 "wcsbth.l" +#line 2698 "wcsbth.l" { BEGIN(DISCARD); } YY_BREAK -case 267: +case 412: YY_RULE_SETUP -#line 1731 "wcsbth.l" +#line 2702 "wcsbth.l" { if (relax & WCSHDR_PROJPn) { - sscanf(wcsbthtext, "%d", &m); + sscanf(yytext, "%d", &m); i = 0; a = ' '; keytype = IMGAXIS; BEGIN(VALUE); } else if (relax & WCSHDR_reject) { - errmsg = "PROJPn keyword is defunct"; + errmsg = "the PROJPn keyword is deprecated, use PVi_ma"; + BEGIN(ERROR); + + } else { + // Pretend we don't recognize it. + BEGIN(DISCARD); + } + } + YY_BREAK +case 413: +#line 2721 "wcsbth.l" +case 414: +YY_RULE_SETUP +#line 2721 "wcsbth.l" +{ + if (relax & (WCSHDR_PROJPn | WCSHDR_reject)) { + errmsg = "invalid PROJPn keyword"; BEGIN(ERROR); } else { - /* Pretend we don't recognize it. */ BEGIN(DISCARD); } } YY_BREAK -case 268: +case 415: YY_RULE_SETUP -#line 1749 "wcsbth.l" +#line 2731 "wcsbth.l" { BEGIN(DISCARD); } YY_BREAK -case 269: +case 416: YY_RULE_SETUP -#line 1753 "wcsbth.l" +#line 2735 "wcsbth.l" { - /* Do checks on i, j, m, n, k. */ + // Do checks on i, j, m, n, k. if (!(keytype & keysel)) { - /* Selection by keyword type. */ + // Selection by keyword type. BEGIN(DISCARD); } else if (exclude[n] || exclude[k]) { - /* One or other column is not selected. */ + // One or other column is not selected. if (k && (exclude[n] != exclude[k])) { - /* For keywords such as TCn_ka, both columns must be excluded. - User error, so return immediately. */ - wcsbthlex_destroy(); - return 3; + // For keywords such as TCn_ka, both columns must be excluded. + // User error, so return immediately. + return WCSHDRERR_BAD_COLUMN; } else { BEGIN(DISCARD); @@ -19201,290 +29937,425 @@ YY_RULE_SETUP if (relax & WCSHDR_reject) { errmsg = errtxt; if (i > 99 || j > 99) { - sprintf(errmsg, "Axis number exceeds 99"); + sprintf(errmsg, "axis number exceeds 99"); } else if (m > 99) { - sprintf(errmsg, "Parameter number exceeds 99"); + sprintf(errmsg, "parameter number exceeds 99"); } else if (n > 999 || k > 999) { - sprintf(errmsg, "Column number exceeds 999"); + sprintf(errmsg, "column number exceeds 999"); } BEGIN(ERROR); } else { - /* Pretend we don't recognize it. */ + // Pretend we don't recognize it. BEGIN(DISCARD); } } else if (ipass == 2 && npass == 3 && (keytype & BINTAB)) { - /* Skip keyvalues that won't be inherited. */ - BEGIN(FLUSH); - - } else if (ipass == 3 && (keytype & IMGHEAD)) { - /* IMGHEAD keytypes are always dealt with on the second pass. */ + // Skip keyvalues that won't be inherited. BEGIN(FLUSH); - } else if (vptr) { - alts.icol = 0; - alts.ialt = 0; - voff = (char *)vptr - (char *)(&wcstem); + } else { + if (ipass == 3 && (keytype & IMGHEAD)) { + // IMGHEAD keytypes are always dealt with on the second pass. + // However, they must be re-parsed in order to report errors. + vptr = 0x0; + } if (valtype == INTEGER) { BEGIN(INTEGER_VAL); } else if (valtype == FLOAT) { BEGIN(FLOAT_VAL); + } else if (valtype == FLOAT2) { + BEGIN(FLOAT2_VAL); } else if (valtype == STRING) { BEGIN(STRING_VAL); } else { errmsg = errtxt; - sprintf(errmsg, "Internal parser ERROR, bad data type: %d", + sprintf(errmsg, "internal parser ERROR, bad data type: %d", valtype); BEGIN(ERROR); } + } + } + YY_BREAK +case 417: +YY_RULE_SETUP +#line 2797 "wcsbth.l" +{ + errmsg = "invalid KEYWORD = VALUE syntax"; + BEGIN(ERROR); + } + YY_BREAK +case 418: +YY_RULE_SETUP +#line 2802 "wcsbth.l" +{ + if (ipass == 1) { + BEGIN(COMMENT); } else { - errmsg = "Internal parser ERROR, null pointer"; - BEGIN(ERROR); + // Read the keyvalue. + sscanf(yytext, "%d", &inttmp); + + BEGIN(COMMENT); } } YY_BREAK -case 270: +case 419: YY_RULE_SETUP -#line 1820 "wcsbth.l" +#line 2814 "wcsbth.l" { - errmsg = "Invalid KEYWORD = VALUE syntax"; + errmsg = "an integer value was expected"; BEGIN(ERROR); } YY_BREAK -case 271: +case 420: YY_RULE_SETUP -#line 1825 "wcsbth.l" +#line 2819 "wcsbth.l" { if (ipass == 1) { - /* Do first-pass bookkeeping. */ - wcsbth_pass1(keytype, i, j, n, k, a, ptype, &alts); - BEGIN(FLUSH); + BEGIN(COMMENT); } else { - /* Update each coordinate representation. */ - while ((wcsp = wcsbth_idx(*wcs, &alts, keytype, n, a))) { - wptr = (void *)((char *)wcsp + voff); + // Read the keyvalue. + wcsutil_str2double(yytext, &dbltmp); - /* Read the keyvalue. */ - if (special) { - special(wptr); - } else { - sscanf(wcsbthtext, "%d", (int *)wptr); - } + if (chekval && chekval(dbltmp)) { + errmsg = "invalid keyvalue"; + BEGIN(ERROR); + } else { + BEGIN(COMMENT); } - - BEGIN(COMMENT); } } YY_BREAK -case 272: +case 421: YY_RULE_SETUP -#line 1848 "wcsbth.l" +#line 2836 "wcsbth.l" { - errmsg = "An integer value was expected"; + errmsg = "a floating-point value was expected"; BEGIN(ERROR); } YY_BREAK -case 273: +case 422: YY_RULE_SETUP -#line 1853 "wcsbth.l" +#line 2841 "wcsbth.l" { if (ipass == 1) { - /* Do first-pass bookkeeping. */ - wcsbth_pass1(keytype, i, j, n, k, a, ptype, &alts); - BEGIN(FLUSH); + BEGIN(COMMENT); } else { - /* Update each coordinate representation. */ - while ((wcsp = wcsbth_idx(*wcs, &alts, keytype, n, a))) { - wptr = (void *)((char *)wcsp + voff); + // Read the keyvalue as integer and fractional parts. + wcsutil_str2double2(yytext, dbl2tmp); - /* Apply keyword parameterization. */ - if (ptype == 'v') { - ipx = wcsp->npv++; - wcsp->pv[ipx].i = i; - wcsp->pv[ipx].m = m; - wptr = &(wcsp->pv[ipx].value); + BEGIN(COMMENT); + } + } + YY_BREAK +case 423: +YY_RULE_SETUP +#line 2853 "wcsbth.l" +{ + errmsg = "a floating-point value was expected"; + BEGIN(ERROR); + } + YY_BREAK +case 424: +/* rule 424 can match eol */ +YY_RULE_SETUP +#line 2858 "wcsbth.l" +{ + if (ipass == 1) { + BEGIN(COMMENT); - } else if (j) { - /* Is the de-reference necessary? */ - wptr = *((double **)wptr) + (i - 1)*(wcsp->naxis) + (j - 1); + } else { + strtmp[0] = '\0'; + int nch = yyleng - 2; + if (0 < nch && nch < 80) { + // Copy the keyvalue minus the quotes. + strncpy(strtmp, yytext+1, nch); + strtmp[nch] = '\0'; - } else if (i) { - wptr = *((double **)wptr) + (i - 1); + // Strip off trailing blanks. + for (int jx = nch-1; jx >= 0; jx--) { + if (strtmp[jx] != ' ') { + break; + } + strtmp[jx] = '\0'; } + } - /* Read the keyvalue. */ - if (special) { - special(wptr); - } else { - wcsutil_str2double(wcsbthtext, "%lf", (double *)wptr); + // Squeeze out repeated quotes. + int ix = 0; + for (int jx = 0; jx < 72; jx++) { + if (ix < jx) { + strtmp[ix] = strtmp[jx]; } - /* Flag the presence of PC, or CD and/or CROTA. */ - if (altlin) { - wcsp->altlin |= altlin; - altlin = 0; + if (strtmp[jx] == '\0') { + break; + } else if (strtmp[jx] == '\'' && strtmp[jx+1] == '\'') { + jx++; } + + ix++; } BEGIN(COMMENT); } } YY_BREAK -case 274: +case 425: YY_RULE_SETUP -#line 1897 "wcsbth.l" +#line 2899 "wcsbth.l" { - errmsg = "A floating-point value was expected"; + errmsg = "a string value was expected"; BEGIN(ERROR); } YY_BREAK -case 275: -/* rule 275 can match eol */ +case 426: +*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ +yyg->yy_c_buf_p = yy_cp -= 1; +YY_DO_BEFORE_ACTION; /* set up yytext again */ YY_RULE_SETUP -#line 1902 "wcsbth.l" +#line 2904 "wcsbth.l" { if (ipass == 1) { - /* Do first-pass bookkeeping. */ + // Do first-pass bookkeeping. wcsbth_pass1(keytype, i, j, n, k, a, ptype, &alts); BEGIN(FLUSH); - } else { - /* Update each coordinate representation. */ + } else if (*wcs) { + // Store the value now that the keyrecord has been validated. + alts.icol = 0; + alts.ialt = 0; + + // Update each coordinate representation. + int gotone = 0; + struct wcsprm *wcsp; while ((wcsp = wcsbth_idx(*wcs, &alts, keytype, n, a))) { - wptr = (void *)((char *)wcsp + voff); + gotone = 1; + + if (vptr) { + void *wptr; + if (auxprm) { + // Additional auxiliary parameter. + auxp = wcsp->aux; + ptrdiff_t voff = (char *)vptr - (char *)(&auxtem); + wptr = (void *)((char *)auxp + voff); + } else { + // A parameter that lives directly in wcsprm. + ptrdiff_t voff = (char *)vptr - (char *)(&wcstem); + wptr = (void *)((char *)wcsp + voff); + } - /* Apply keyword parameterization. */ - if (ptype == 's') { - ipx = wcsp->nps++; - wcsp->ps[ipx].i = i; - wcsp->ps[ipx].m = m; - wptr = wcsp->ps[ipx].value; + if (valtype == INTEGER) { + *((int *)wptr) = inttmp; - } else if (j) { - wptr = *((char (**)[72])wptr) + - (i - 1)*(wcsp->naxis) + (j - 1); + } else if (valtype == FLOAT) { + // Apply keyword parameterization. + if (ptype == 'v') { + int ipx = (wcsp->npv)++; + wcsp->pv[ipx].i = i; + wcsp->pv[ipx].m = m; + wptr = &(wcsp->pv[ipx].value); - } else if (i) { - wptr = *((char (**)[72])wptr) + (i - 1); - } + } else if (j) { + wptr = *((double **)wptr) + (i - 1)*(wcsp->naxis) + + (j - 1); + + } else if (i) { + wptr = *((double **)wptr) + (i - 1); + } + + if (special) { + special(wptr, &dbltmp); + } else { + *((double *)wptr) = dbltmp; + } - /* Read the keyvalue. */ - cptr = (char *)wptr; - strcpy(cptr, wcsbthtext+1); + // Flag the presence of PCi_ja, or CDi_ja and/or CROTAia. + if (altlin) { + wcsp->altlin |= altlin; + altlin = 0; + } - /* Squeeze out repeated quotes. */ - ix = 0; - for (jx = 0; jx < 72; jx++) { - if (ix < jx) { - cptr[ix] = cptr[jx]; + } else if (valtype == FLOAT2) { + // Split MJDREF and JDREF into integer and fraction. + if (special) { + special(wptr, dbl2tmp); + } else { + *((double *)wptr) = dbl2tmp[0]; + *((double *)wptr + 1) = dbl2tmp[1]; + } + + } else if (valtype == STRING) { + // Apply keyword parameterization. + if (ptype == 's') { + int ipx = wcsp->nps++; + wcsp->ps[ipx].i = i; + wcsp->ps[ipx].m = m; + wptr = wcsp->ps[ipx].value; + + } else if (j) { + wptr = *((char (**)[72])wptr) + + (i - 1)*(wcsp->naxis) + (j - 1); + + } else if (i) { + wptr = *((char (**)[72])wptr) + (i - 1); + } + + char *cptr = (char *)wptr; + strcpy(cptr, strtmp); } + } + } - if (cptr[jx] == '\0') { - if (ix) cptr[ix-1] = '\0'; - break; - } else if (cptr[jx] == '\'' && cptr[jx+1] == '\'') { - jx++; + if (ipass == npass) { + if (gotone) { + nvalid++; + if (ctrl == 4) { + wcsfprintf(stderr, + "%.80s\n Accepted (%d) as a valid WCS keyrecord.\n", + keyrec, nvalid); } - ix++; + BEGIN(FLUSH); + + } else { + errmsg = "syntactically valid WCS keyrecord has no effect"; + BEGIN(ERROR); } + + } else { + BEGIN(FLUSH); } - BEGIN(COMMENT); + } else { + BEGIN(FLUSH); } } YY_BREAK -case 276: +case 427: +*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ +yyg->yy_c_buf_p = yy_cp -= 1; +YY_DO_BEFORE_ACTION; /* set up yytext again */ YY_RULE_SETUP -#line 1954 "wcsbth.l" +#line 3021 "wcsbth.l" { - errmsg = "A string value was expected"; + errmsg = "invalid keyvalue"; BEGIN(ERROR); } YY_BREAK -case 277: -#line 1960 "wcsbth.l" -case 278: +case 428: +*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ +yyg->yy_c_buf_p = yy_cp -= 1; +YY_DO_BEFORE_ACTION; /* set up yytext again */ YY_RULE_SETUP -#line 1960 "wcsbth.l" +#line 3026 "wcsbth.l" { - BEGIN(FLUSH); + errmsg = "invalid keyvalue"; + BEGIN(ERROR); } YY_BREAK -case 279: +case 429: +*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ +yyg->yy_c_buf_p = yy_cp -= 1; +YY_DO_BEFORE_ACTION; /* set up yytext again */ YY_RULE_SETUP -#line 1964 "wcsbth.l" +#line 3031 "wcsbth.l" { - errmsg = "Malformed keycomment"; + errmsg = "invalid keyvalue or malformed keycomment"; BEGIN(ERROR); } YY_BREAK -case 280: +case 430: +*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ +yyg->yy_c_buf_p = yy_cp -= 1; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +YY_RULE_SETUP +#line 3036 "wcsbth.l" +{ + errmsg = "malformed keycomment"; + BEGIN(ERROR); + } + YY_BREAK +case 431: +*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ +yyg->yy_c_buf_p = yy_cp -= 1; +YY_DO_BEFORE_ACTION; /* set up yytext again */ YY_RULE_SETUP -#line 1969 "wcsbth.l" +#line 3041 "wcsbth.l" { if (ipass == npass) { if (ctrl < 0) { - /* Preserve discards. */ - if (hptr < wcsbth_hdr-80) { - strncpy(hptr, wcsbth_hdr-80, 80); - } - hptr += 80; + // Preserve discards. + keep = keyrec; - } else if (ctrl > 2) { - fprintf(stderr, "%.80s\n Discarded.\n", wcsbth_hdr-80); + } else if (2 < ctrl) { + nother++; + wcsfprintf(stderr, "%.80s\n Not a recognized WCS keyword.\n", + keyrec); } } - BEGIN(FLUSH); } YY_BREAK -case 281: +case 432: +*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ +yyg->yy_c_buf_p = yy_cp -= 1; +YY_DO_BEFORE_ACTION; /* set up yytext again */ YY_RULE_SETUP -#line 1986 "wcsbth.l" +#line 3056 "wcsbth.l" { - (*nreject)++; if (ipass == npass) { - if (ctrl == -1) { - if (hptr < wcsbth_hdr-80) { - /* Preserve rejects. */ - strncpy(hptr, wcsbth_hdr-80, 80); - } - hptr += 80; + (*nreject)++; + + if (ctrl%10 == -1) { + keep = keyrec; } - if (abs(ctrl) > 1) { - fprintf(stderr, "%.80s\n%4d: %s.\n", wcsbth_hdr-80, *nreject, - errmsg); + if (1 < abs(ctrl%10)) { + wcsfprintf(stderr, "%.80s\n Rejected (%d), %s.\n", + keyrec, *nreject, errmsg); } } - BEGIN(FLUSH); } YY_BREAK -case 282: -/* rule 282 can match eol */ +case 433: +/* rule 433 can match eol */ YY_RULE_SETUP -#line 2006 "wcsbth.l" +#line 3072 "wcsbth.l" { - /* Throw away the rest of the line and reset for the next one. */ + if (ipass == npass && keep) { + if (hptr < keep) { + strncpy(hptr, keep, 80); + } + hptr += 80; + } + + naux += auxprm; + auxprm = 0; + + // Throw away the rest of the line and reset for the next one. i = j = 0; n = k = 0; m = 0; a = ' '; + keyrec += 80; + keytype = 0; valtype = -1; vptr = 0x0; + keep = 0x0; - altlin = 0; - ptype = ' '; + altlin = 0; + ptype = ' '; + chekval = 0x0; special = 0x0; + BEGIN(INITIAL); } YY_BREAK @@ -19516,37 +30387,44 @@ case YY_STATE_EOF(CCCCCn): case YY_STATE_EOF(VALUE): case YY_STATE_EOF(INTEGER_VAL): case YY_STATE_EOF(FLOAT_VAL): +case YY_STATE_EOF(FLOAT2_VAL): case YY_STATE_EOF(STRING_VAL): case YY_STATE_EOF(COMMENT): case YY_STATE_EOF(DISCARD): case YY_STATE_EOF(ERROR): case YY_STATE_EOF(FLUSH): -#line 2023 "wcsbth.l" +#line 3104 "wcsbth.l" { - /* End-of-input. */ + // End-of-input. if (ipass == 1) { - if ((status = wcsbth_init1(&alts, nwcs, wcs)) || *nwcs == 0) { - wcsbthlex_destroy(); + int status; + if ((status = wcsbth_init1(&alts, naux, nwcs, wcs)) || + (*nwcs == 0 && ctrl == 0)) { return status; } - - if (alts.imgherit) npass = 3; - if (abs(ctrl) > 2) { + if (2 < abs(ctrl%10)) { if (*nwcs == 1) { - fprintf(stderr, "Found one coordinate representation.\n"); + if (strcmp(wcs[0]->wcsname, "DEFAULTS") != 0) { + wcsfprintf(stderr, "Found one coordinate representation.\n"); + } } else { - fprintf(stderr, "Found %d coordinate representations.\n", + wcsfprintf(stderr, "Found %d coordinate representations.\n", *nwcs); } } + + if (alts.imgherit) npass = 3; } if (ipass++ < npass) { - wcsbth_hdr = header; - wcsbth_nkeyrec = nkeyrec; + yyextra->hdr = header; + yyextra->nkeyrec = nkeyrec; + keyrec = header; *nreject = 0; + imherit = 1; + i = j = 0; k = n = 0; m = 0; @@ -19558,52 +30436,61 @@ case YY_STATE_EOF(FLUSH): altlin = 0; ptype = ' '; + chekval = 0x0; special = 0x0; - wcsbthrestart(wcsbthin); + yyrestart(yyin, yyscanner); } else { - wcsbthlex_destroy(); if (ctrl < 0) { *hptr = '\0'; } else if (ctrl == 1) { - fprintf(stderr, "%d WCS keyrecords were rejected.\n", *nreject); + wcsfprintf(stderr, "%d WCS keyrecord%s rejected.\n", + *nreject, (*nreject==1)?" was":"s were"); + } else if (ctrl == 4) { + wcsfprintf(stderr, "\n"); + wcsfprintf(stderr, "%5d keyrecord%s rejected for syntax or " + "other errors,\n", *nreject, (*nreject==1)?" was":"s were"); + wcsfprintf(stderr, "%5d %s recognized as syntactically valid, " + "and\n", nvalid, (nvalid==1)?"was":"were"); + wcsfprintf(stderr, "%5d other%s were not recognized as WCS " + "keyrecords.\n", nother, (nother==1)?"":"s"); } return wcsbth_final(&alts, nwcs, wcs); } } YY_BREAK -case 283: +case 434: YY_RULE_SETUP -#line 2076 "wcsbth.l" +#line 3172 "wcsbth.l" ECHO; YY_BREAK -#line 19584 "wcsbth.c" +#line 30471 "wcsbth.c" case YY_END_OF_BUFFER: { /* Amount of text matched not including the EOB char. */ - int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1; + int yy_amount_of_matched_text = (int) (yy_cp - yyg->yytext_ptr) - 1; /* Undo the effects of YY_DO_BEFORE_ACTION. */ - *yy_cp = (yy_hold_char); + *yy_cp = yyg->yy_hold_char; YY_RESTORE_YY_MORE_OFFSET if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) { /* We're scanning a new file or input source. It's * possible that this happened because the user - * just pointed wcsbthin at a new source and called - * wcsbthlex(). If so, then we have to assure + * just pointed yyin at a new source and called + * yylex(). If so, then we have to assure * consistency between YY_CURRENT_BUFFER and our * globals. Here is the right place to do so, because * this is the first action (other than possibly a * back-up) that will match for the new input source. */ - (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; - YY_CURRENT_BUFFER_LVALUE->yy_input_file = wcsbthin; + yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin; YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; } @@ -19614,13 +30501,13 @@ ECHO; * end-of-buffer state). Contrast this with the test * in input(). */ - if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) + if ( yyg->yy_c_buf_p <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) { /* This was really a NUL. */ yy_state_type yy_next_state; - (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text; + yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text; - yy_current_state = yy_get_previous_state( ); + yy_current_state = yy_get_previous_state( yyscanner ); /* Okay, we're now positioned to make the NUL * transition. We couldn't have @@ -19631,43 +30518,43 @@ ECHO; * will run more slowly). */ - yy_next_state = yy_try_NUL_trans( yy_current_state ); + yy_next_state = yy_try_NUL_trans( yy_current_state , yyscanner); - yy_bp = (yytext_ptr) + YY_MORE_ADJ; + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; if ( yy_next_state ) { /* Consume the NUL. */ - yy_cp = ++(yy_c_buf_p); + yy_cp = ++yyg->yy_c_buf_p; yy_current_state = yy_next_state; goto yy_match; } else { - yy_cp = (yy_c_buf_p); + yy_cp = yyg->yy_c_buf_p; goto yy_find_action; } } - else switch ( yy_get_next_buffer( ) ) + else switch ( yy_get_next_buffer( yyscanner ) ) { case EOB_ACT_END_OF_FILE: { - (yy_did_buffer_switch_on_eof) = 0; + yyg->yy_did_buffer_switch_on_eof = 0; - if ( wcsbthwrap( ) ) + if ( yywrap( yyscanner ) ) { /* Note: because we've taken care in * yy_get_next_buffer() to have set up - * wcsbthtext, we can now set up + * yytext, we can now set up * yy_c_buf_p so that if some total * hoser (like flex itself) wants to * call the scanner after we return the * YY_NULL, it'll still work - another * YY_NULL will get returned. */ - (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ; + yyg->yy_c_buf_p = yyg->yytext_ptr + YY_MORE_ADJ; yy_act = YY_STATE_EOF(YY_START); goto do_action; @@ -19675,30 +30562,30 @@ ECHO; else { - if ( ! (yy_did_buffer_switch_on_eof) ) + if ( ! yyg->yy_did_buffer_switch_on_eof ) YY_NEW_FILE; } break; } case EOB_ACT_CONTINUE_SCAN: - (yy_c_buf_p) = - (yytext_ptr) + yy_amount_of_matched_text; + yyg->yy_c_buf_p = + yyg->yytext_ptr + yy_amount_of_matched_text; - yy_current_state = yy_get_previous_state( ); + yy_current_state = yy_get_previous_state( yyscanner ); - yy_cp = (yy_c_buf_p); - yy_bp = (yytext_ptr) + YY_MORE_ADJ; + yy_cp = yyg->yy_c_buf_p; + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; goto yy_match; case EOB_ACT_LAST_MATCH: - (yy_c_buf_p) = - &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)]; + yyg->yy_c_buf_p = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars]; - yy_current_state = yy_get_previous_state( ); + yy_current_state = yy_get_previous_state( yyscanner ); - yy_cp = (yy_c_buf_p); - yy_bp = (yytext_ptr) + YY_MORE_ADJ; + yy_cp = yyg->yy_c_buf_p; + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; goto yy_find_action; } break; @@ -19709,7 +30596,8 @@ ECHO; "fatal flex scanner internal error--no action found" ); } /* end of action switch */ } /* end of scanning one token */ -} /* end of wcsbthlex */ + } /* end of user's declarations */ +} /* end of yylex */ /* yy_get_next_buffer - try to read in a new buffer * @@ -19718,20 +30606,21 @@ ECHO; * EOB_ACT_CONTINUE_SCAN - continue scanning from current position * EOB_ACT_END_OF_FILE - end of file */ -static int yy_get_next_buffer (void) +static int yy_get_next_buffer (yyscan_t yyscanner) { - register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; - register char *source = (yytext_ptr); - register int number_to_move, i; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; + char *source = yyg->yytext_ptr; + int number_to_move, i; int ret_val; - if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] ) + if ( yyg->yy_c_buf_p > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] ) YY_FATAL_ERROR( "fatal flex scanner internal error--end of buffer missed" ); if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) { /* Don't try to fill the buffer, so this is an EOF. */ - if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 ) + if ( yyg->yy_c_buf_p - yyg->yytext_ptr - YY_MORE_ADJ == 1 ) { /* We matched a single character, the EOB, so * treat this as a final EOF. @@ -19751,7 +30640,7 @@ static int yy_get_next_buffer (void) /* Try to read more data. */ /* First move last chars to start of buffer. */ - number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr)) - 1; + number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr - 1); for ( i = 0; i < number_to_move; ++i ) *(dest++) = *(source++); @@ -19760,7 +30649,7 @@ static int yy_get_next_buffer (void) /* don't do the read, it's not guaranteed to return an EOF, * just force an EOF */ - YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0; + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars = 0; else { @@ -19771,10 +30660,10 @@ static int yy_get_next_buffer (void) { /* Not enough room in the buffer - grow it. */ /* just a shorter name for the current buffer */ - YY_BUFFER_STATE b = YY_CURRENT_BUFFER; + YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE; int yy_c_buf_p_offset = - (int) ((yy_c_buf_p) - b->yy_ch_buf); + (int) (yyg->yy_c_buf_p - b->yy_ch_buf); if ( b->yy_is_our_buffer ) { @@ -19787,17 +30676,18 @@ static int yy_get_next_buffer (void) b->yy_ch_buf = (char *) /* Include room in for 2 EOB chars. */ - wcsbthrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 ); + yyrealloc( (void *) b->yy_ch_buf, + (yy_size_t) (b->yy_buf_size + 2) , yyscanner ); } else /* Can't grow it, we don't own it. */ - b->yy_ch_buf = 0; + b->yy_ch_buf = NULL; if ( ! b->yy_ch_buf ) YY_FATAL_ERROR( "fatal error - scanner input buffer overflow" ); - (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset]; + yyg->yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset]; num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; @@ -19809,17 +30699,17 @@ static int yy_get_next_buffer (void) /* Read in more data. */ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), - (yy_n_chars), (size_t) num_to_read ); + yyg->yy_n_chars, num_to_read ); - YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; } - if ( (yy_n_chars) == 0 ) + if ( yyg->yy_n_chars == 0 ) { if ( number_to_move == YY_MORE_ADJ ) { ret_val = EOB_ACT_END_OF_FILE; - wcsbthrestart(wcsbthin ); + yyrestart( yyin , yyscanner); } else @@ -19833,34 +30723,38 @@ static int yy_get_next_buffer (void) else ret_val = EOB_ACT_CONTINUE_SCAN; - if ((yy_size_t) ((yy_n_chars) + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { + if ((yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { /* Extend the array by 50%, plus the number we really need. */ - yy_size_t new_size = (yy_n_chars) + number_to_move + ((yy_n_chars) >> 1); - YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) wcsbthrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size ); + int new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1); + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc( + (void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf, (yy_size_t) new_size , yyscanner ); if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); + /* "- 2" to take care of EOB's */ + YY_CURRENT_BUFFER_LVALUE->yy_buf_size = (int) (new_size - 2); } - (yy_n_chars) += number_to_move; - YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR; - YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR; + yyg->yy_n_chars += number_to_move; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] = YY_END_OF_BUFFER_CHAR; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR; - (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; + yyg->yytext_ptr = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; return ret_val; } /* yy_get_previous_state - get the state just before the EOB char was reached */ - static yy_state_type yy_get_previous_state (void) + static yy_state_type yy_get_previous_state (yyscan_t yyscanner) { - register yy_state_type yy_current_state; - register char *yy_cp; - - yy_current_state = (yy_start); + yy_state_type yy_current_state; + char *yy_cp; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + yy_current_state = yyg->yy_start; yy_current_state += YY_AT_BOL(); - for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp ) + for ( yy_cp = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp ) { if ( *yy_cp ) { @@ -19870,8 +30764,8 @@ static int yy_get_next_buffer (void) yy_current_state = yy_NUL_trans[yy_current_state]; if ( yy_accept[yy_current_state] ) { - (yy_last_accepting_state) = yy_current_state; - (yy_last_accepting_cpos) = yy_cp; + yyg->yy_last_accepting_state = yy_current_state; + yyg->yy_last_accepting_cpos = yy_cp; } } @@ -19883,10 +30777,11 @@ static int yy_get_next_buffer (void) * synopsis * next_state = yy_try_NUL_trans( current_state ); */ - static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state ) + static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state , yyscan_t yyscanner) { - register int yy_is_jam; - register char *yy_cp = (yy_c_buf_p); + int yy_is_jam; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */ + char *yy_cp = yyg->yy_c_buf_p; yy_current_state = yy_NUL_trans[yy_current_state]; yy_is_jam = (yy_current_state == 0); @@ -19895,30 +30790,34 @@ static int yy_get_next_buffer (void) { if ( yy_accept[yy_current_state] ) { - (yy_last_accepting_state) = yy_current_state; - (yy_last_accepting_cpos) = yy_cp; + yyg->yy_last_accepting_state = yy_current_state; + yyg->yy_last_accepting_cpos = yy_cp; } } + (void)yyg; return yy_is_jam ? 0 : yy_current_state; } - static void yyunput (int c, register char * yy_bp ) +#ifndef YY_NO_UNPUT + + static void yyunput (int c, char * yy_bp , yyscan_t yyscanner) { - register char *yy_cp; - - yy_cp = (yy_c_buf_p); + char *yy_cp; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; - /* undo effects of setting up wcsbthtext */ - *yy_cp = (yy_hold_char); + yy_cp = yyg->yy_c_buf_p; + + /* undo effects of setting up yytext */ + *yy_cp = yyg->yy_hold_char; if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) { /* need to shift things up to make room */ /* +2 for EOB chars. */ - register int number_to_move = (yy_n_chars) + 2; - register char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[ + int number_to_move = yyg->yy_n_chars + 2; + char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[ YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2]; - register char *source = + char *source = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]; while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) @@ -19927,7 +30826,7 @@ static int yy_get_next_buffer (void) yy_cp += (int) (dest - source); yy_bp += (int) (dest - source); YY_CURRENT_BUFFER_LVALUE->yy_n_chars = - (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_buf_size; + yyg->yy_n_chars = (int) YY_CURRENT_BUFFER_LVALUE->yy_buf_size; if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) YY_FATAL_ERROR( "flex scanner push-back overflow" ); @@ -19935,39 +30834,42 @@ static int yy_get_next_buffer (void) *--yy_cp = (char) c; - (yytext_ptr) = yy_bp; - (yy_hold_char) = *yy_cp; - (yy_c_buf_p) = yy_cp; + yyg->yytext_ptr = yy_bp; + yyg->yy_hold_char = *yy_cp; + yyg->yy_c_buf_p = yy_cp; } +#endif + #ifndef YY_NO_INPUT #ifdef __cplusplus - static int yyinput (void) + static int yyinput (yyscan_t yyscanner) #else - static int input (void) + static int input (yyscan_t yyscanner) #endif { int c; - - *(yy_c_buf_p) = (yy_hold_char); + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; - if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR ) + *yyg->yy_c_buf_p = yyg->yy_hold_char; + + if ( *yyg->yy_c_buf_p == YY_END_OF_BUFFER_CHAR ) { /* yy_c_buf_p now points to the character we want to return. * If this occurs *before* the EOB characters, then it's a * valid NUL; if not, then we've hit the end of the buffer. */ - if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) + if ( yyg->yy_c_buf_p < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) /* This was really a NUL. */ - *(yy_c_buf_p) = '\0'; + *yyg->yy_c_buf_p = '\0'; else { /* need more input */ - int offset = (yy_c_buf_p) - (yytext_ptr); - ++(yy_c_buf_p); + int offset = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr); + ++yyg->yy_c_buf_p; - switch ( yy_get_next_buffer( ) ) + switch ( yy_get_next_buffer( yyscanner ) ) { case EOB_ACT_LAST_MATCH: /* This happens because yy_g_n_b() @@ -19981,34 +30883,34 @@ static int yy_get_next_buffer (void) */ /* Reset buffer status. */ - wcsbthrestart(wcsbthin ); + yyrestart( yyin , yyscanner); /*FALLTHROUGH*/ case EOB_ACT_END_OF_FILE: { - if ( wcsbthwrap( ) ) - return EOF; + if ( yywrap( yyscanner ) ) + return 0; - if ( ! (yy_did_buffer_switch_on_eof) ) + if ( ! yyg->yy_did_buffer_switch_on_eof ) YY_NEW_FILE; #ifdef __cplusplus - return yyinput(); + return yyinput(yyscanner); #else - return input(); + return input(yyscanner); #endif } case EOB_ACT_CONTINUE_SCAN: - (yy_c_buf_p) = (yytext_ptr) + offset; + yyg->yy_c_buf_p = yyg->yytext_ptr + offset; break; } } } - c = *(unsigned char *) (yy_c_buf_p); /* cast for 8-bit char's */ - *(yy_c_buf_p) = '\0'; /* preserve wcsbthtext */ - (yy_hold_char) = *++(yy_c_buf_p); + c = *(unsigned char *) yyg->yy_c_buf_p; /* cast for 8-bit char's */ + *yyg->yy_c_buf_p = '\0'; /* preserve yytext */ + yyg->yy_hold_char = *++yyg->yy_c_buf_p; YY_CURRENT_BUFFER_LVALUE->yy_at_bol = (c == '\n'); @@ -20018,102 +30920,106 @@ static int yy_get_next_buffer (void) /** Immediately switch to a different input stream. * @param input_file A readable stream. - * + * @param yyscanner The scanner object. * @note This function does not reset the start condition to @c INITIAL . */ - void wcsbthrestart (FILE * input_file ) + void yyrestart (FILE * input_file , yyscan_t yyscanner) { - + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if ( ! YY_CURRENT_BUFFER ){ - wcsbthensure_buffer_stack (); + yyensure_buffer_stack (yyscanner); YY_CURRENT_BUFFER_LVALUE = - wcsbth_create_buffer(wcsbthin,YY_BUF_SIZE ); + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); } - wcsbth_init_buffer(YY_CURRENT_BUFFER,input_file ); - wcsbth_load_buffer_state( ); + yy_init_buffer( YY_CURRENT_BUFFER, input_file , yyscanner); + yy_load_buffer_state( yyscanner ); } /** Switch to a different input buffer. * @param new_buffer The new input buffer. - * + * @param yyscanner The scanner object. */ - void wcsbth_switch_to_buffer (YY_BUFFER_STATE new_buffer ) + void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) { - + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + /* TODO. We should be able to replace this entire function body * with - * wcsbthpop_buffer_state(); - * wcsbthpush_buffer_state(new_buffer); + * yypop_buffer_state(); + * yypush_buffer_state(new_buffer); */ - wcsbthensure_buffer_stack (); + yyensure_buffer_stack (yyscanner); if ( YY_CURRENT_BUFFER == new_buffer ) return; if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ - *(yy_c_buf_p) = (yy_hold_char); - YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); - YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + *yyg->yy_c_buf_p = yyg->yy_hold_char; + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; } YY_CURRENT_BUFFER_LVALUE = new_buffer; - wcsbth_load_buffer_state( ); + yy_load_buffer_state( yyscanner ); /* We don't actually know whether we did this switch during - * EOF (wcsbthwrap()) processing, but the only time this flag - * is looked at is after wcsbthwrap() is called, so it's safe + * EOF (yywrap()) processing, but the only time this flag + * is looked at is after yywrap() is called, so it's safe * to go ahead and always set it. */ - (yy_did_buffer_switch_on_eof) = 1; + yyg->yy_did_buffer_switch_on_eof = 1; } -static void wcsbth_load_buffer_state (void) +static void yy_load_buffer_state (yyscan_t yyscanner) { - (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; - (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; - wcsbthin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; - (yy_hold_char) = *(yy_c_buf_p); + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + yyg->yytext_ptr = yyg->yy_c_buf_p = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; + yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; + yyg->yy_hold_char = *yyg->yy_c_buf_p; } /** Allocate and initialize an input buffer state. * @param file A readable stream. * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. - * + * @param yyscanner The scanner object. * @return the allocated buffer state. */ - YY_BUFFER_STATE wcsbth_create_buffer (FILE * file, int size ) + YY_BUFFER_STATE yy_create_buffer (FILE * file, int size , yyscan_t yyscanner) { YY_BUFFER_STATE b; - b = (YY_BUFFER_STATE) wcsbthalloc(sizeof( struct yy_buffer_state ) ); + b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner ); if ( ! b ) - YY_FATAL_ERROR( "out of dynamic memory in wcsbth_create_buffer()" ); + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); b->yy_buf_size = size; /* yy_ch_buf has to be 2 characters longer than the size given because * we need to put in 2 end-of-buffer characters. */ - b->yy_ch_buf = (char *) wcsbthalloc(b->yy_buf_size + 2 ); + b->yy_ch_buf = (char *) yyalloc( (yy_size_t) (b->yy_buf_size + 2) , yyscanner ); if ( ! b->yy_ch_buf ) - YY_FATAL_ERROR( "out of dynamic memory in wcsbth_create_buffer()" ); + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); b->yy_is_our_buffer = 1; - wcsbth_init_buffer(b,file ); + yy_init_buffer( b, file , yyscanner); return b; } /** Destroy the buffer. - * @param b a buffer created with wcsbth_create_buffer() - * + * @param b a buffer created with yy_create_buffer() + * @param yyscanner The scanner object. */ - void wcsbth_delete_buffer (YY_BUFFER_STATE b ) + void yy_delete_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) { - + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if ( ! b ) return; @@ -20121,27 +31027,28 @@ static void wcsbth_load_buffer_state (void) YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; if ( b->yy_is_our_buffer ) - wcsbthfree((void *) b->yy_ch_buf ); + yyfree( (void *) b->yy_ch_buf , yyscanner ); - wcsbthfree((void *) b ); + yyfree( (void *) b , yyscanner ); } /* Initializes or reinitializes a buffer. * This function is sometimes called more than once on the same buffer, - * such as during a wcsbthrestart() or at EOF. + * such as during a yyrestart() or at EOF. */ - static void wcsbth_init_buffer (YY_BUFFER_STATE b, FILE * file ) + static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file , yyscan_t yyscanner) { int oerrno = errno; - - wcsbth_flush_buffer(b ); + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + yy_flush_buffer( b , yyscanner); b->yy_input_file = file; b->yy_fill_buffer = 1; - /* If b is the current buffer, then wcsbth_init_buffer was _probably_ - * called from wcsbthrestart() or through yy_get_next_buffer. + /* If b is the current buffer, then yy_init_buffer was _probably_ + * called from yyrestart() or through yy_get_next_buffer. * In that case, we don't want to reset the lineno or column. */ if (b != YY_CURRENT_BUFFER){ @@ -20156,11 +31063,12 @@ static void wcsbth_load_buffer_state (void) /** Discard all buffered characters. On the next scan, YY_INPUT will be called. * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. - * + * @param yyscanner The scanner object. */ - void wcsbth_flush_buffer (YY_BUFFER_STATE b ) + void yy_flush_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) { - if ( ! b ) + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if ( ! b ) return; b->yy_n_chars = 0; @@ -20178,114 +31086,117 @@ static void wcsbth_load_buffer_state (void) b->yy_buffer_status = YY_BUFFER_NEW; if ( b == YY_CURRENT_BUFFER ) - wcsbth_load_buffer_state( ); + yy_load_buffer_state( yyscanner ); } /** Pushes the new state onto the stack. The new state becomes * the current state. This function will allocate the stack * if necessary. * @param new_buffer The new state. - * + * @param yyscanner The scanner object. */ -void wcsbthpush_buffer_state (YY_BUFFER_STATE new_buffer ) +void yypush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) { - if (new_buffer == NULL) + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if (new_buffer == NULL) return; - wcsbthensure_buffer_stack(); + yyensure_buffer_stack(yyscanner); - /* This block is copied from wcsbth_switch_to_buffer. */ + /* This block is copied from yy_switch_to_buffer. */ if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ - *(yy_c_buf_p) = (yy_hold_char); - YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); - YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + *yyg->yy_c_buf_p = yyg->yy_hold_char; + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; } /* Only push if top exists. Otherwise, replace top. */ if (YY_CURRENT_BUFFER) - (yy_buffer_stack_top)++; + yyg->yy_buffer_stack_top++; YY_CURRENT_BUFFER_LVALUE = new_buffer; - /* copied from wcsbth_switch_to_buffer. */ - wcsbth_load_buffer_state( ); - (yy_did_buffer_switch_on_eof) = 1; + /* copied from yy_switch_to_buffer. */ + yy_load_buffer_state( yyscanner ); + yyg->yy_did_buffer_switch_on_eof = 1; } /** Removes and deletes the top of the stack, if present. * The next element becomes the new top. - * + * @param yyscanner The scanner object. */ -void wcsbthpop_buffer_state (void) +void yypop_buffer_state (yyscan_t yyscanner) { - if (!YY_CURRENT_BUFFER) + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if (!YY_CURRENT_BUFFER) return; - wcsbth_delete_buffer(YY_CURRENT_BUFFER ); + yy_delete_buffer(YY_CURRENT_BUFFER , yyscanner); YY_CURRENT_BUFFER_LVALUE = NULL; - if ((yy_buffer_stack_top) > 0) - --(yy_buffer_stack_top); + if (yyg->yy_buffer_stack_top > 0) + --yyg->yy_buffer_stack_top; if (YY_CURRENT_BUFFER) { - wcsbth_load_buffer_state( ); - (yy_did_buffer_switch_on_eof) = 1; + yy_load_buffer_state( yyscanner ); + yyg->yy_did_buffer_switch_on_eof = 1; } } /* Allocates the stack if it does not exist. * Guarantees space for at least one push. */ -static void wcsbthensure_buffer_stack (void) +static void yyensure_buffer_stack (yyscan_t yyscanner) { - int num_to_alloc; - - if (!(yy_buffer_stack)) { + yy_size_t num_to_alloc; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (!yyg->yy_buffer_stack) { /* First allocation is just for 2 elements, since we don't know if this * scanner will even need a stack. We use 2 instead of 1 to avoid an * immediate realloc on the next call. */ - num_to_alloc = 1; - (yy_buffer_stack) = (struct yy_buffer_state**)wcsbthalloc + num_to_alloc = 1; /* After all that talk, this was set to 1 anyways... */ + yyg->yy_buffer_stack = (struct yy_buffer_state**)yyalloc (num_to_alloc * sizeof(struct yy_buffer_state*) - ); - if ( ! (yy_buffer_stack) ) - YY_FATAL_ERROR( "out of dynamic memory in wcsbthensure_buffer_stack()" ); - - memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*)); - - (yy_buffer_stack_max) = num_to_alloc; - (yy_buffer_stack_top) = 0; + , yyscanner); + if ( ! yyg->yy_buffer_stack ) + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); + + memset(yyg->yy_buffer_stack, 0, num_to_alloc * sizeof(struct yy_buffer_state*)); + + yyg->yy_buffer_stack_max = num_to_alloc; + yyg->yy_buffer_stack_top = 0; return; } - if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){ + if (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){ /* Increase the buffer to prepare for a possible push. */ - int grow_size = 8 /* arbitrary grow size */; + yy_size_t grow_size = 8 /* arbitrary grow size */; - num_to_alloc = (yy_buffer_stack_max) + grow_size; - (yy_buffer_stack) = (struct yy_buffer_state**)wcsbthrealloc - ((yy_buffer_stack), + num_to_alloc = yyg->yy_buffer_stack_max + grow_size; + yyg->yy_buffer_stack = (struct yy_buffer_state**)yyrealloc + (yyg->yy_buffer_stack, num_to_alloc * sizeof(struct yy_buffer_state*) - ); - if ( ! (yy_buffer_stack) ) - YY_FATAL_ERROR( "out of dynamic memory in wcsbthensure_buffer_stack()" ); + , yyscanner); + if ( ! yyg->yy_buffer_stack ) + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); /* zero only the new slots.*/ - memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*)); - (yy_buffer_stack_max) = num_to_alloc; + memset(yyg->yy_buffer_stack + yyg->yy_buffer_stack_max, 0, grow_size * sizeof(struct yy_buffer_state*)); + yyg->yy_buffer_stack_max = num_to_alloc; } } /** Setup the input buffer state to scan directly from a user-specified character buffer. * @param base the character buffer * @param size the size in bytes of the character buffer - * - * @return the newly allocated buffer state object. + * @param yyscanner The scanner object. + * @return the newly allocated buffer state object. */ -YY_BUFFER_STATE wcsbth_scan_buffer (char * base, yy_size_t size ) +YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size , yyscan_t yyscanner) { YY_BUFFER_STATE b; @@ -20293,49 +31204,49 @@ YY_BUFFER_STATE wcsbth_scan_buffer (char * base, yy_size_t size ) base[size-2] != YY_END_OF_BUFFER_CHAR || base[size-1] != YY_END_OF_BUFFER_CHAR ) /* They forgot to leave room for the EOB's. */ - return 0; + return NULL; - b = (YY_BUFFER_STATE) wcsbthalloc(sizeof( struct yy_buffer_state ) ); + b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner ); if ( ! b ) - YY_FATAL_ERROR( "out of dynamic memory in wcsbth_scan_buffer()" ); + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); - b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */ + b->yy_buf_size = (int) (size - 2); /* "- 2" to take care of EOB's */ b->yy_buf_pos = b->yy_ch_buf = base; b->yy_is_our_buffer = 0; - b->yy_input_file = 0; + b->yy_input_file = NULL; b->yy_n_chars = b->yy_buf_size; b->yy_is_interactive = 0; b->yy_at_bol = 1; b->yy_fill_buffer = 0; b->yy_buffer_status = YY_BUFFER_NEW; - wcsbth_switch_to_buffer(b ); + yy_switch_to_buffer( b , yyscanner ); return b; } -/** Setup the input buffer state to scan a string. The next call to wcsbthlex() will +/** Setup the input buffer state to scan a string. The next call to yylex() will * scan from a @e copy of @a str. * @param yystr a NUL-terminated string to scan - * + * @param yyscanner The scanner object. * @return the newly allocated buffer state object. * @note If you want to scan bytes that may contain NUL values, then use - * wcsbth_scan_bytes() instead. + * yy_scan_bytes() instead. */ -YY_BUFFER_STATE wcsbth_scan_string (yyconst char * yystr ) +YY_BUFFER_STATE yy_scan_string (const char * yystr , yyscan_t yyscanner) { - return wcsbth_scan_bytes(yystr,strlen(yystr) ); + return yy_scan_bytes( yystr, (int) strlen(yystr) , yyscanner); } -/** Setup the input buffer state to scan the given bytes. The next call to wcsbthlex() will +/** Setup the input buffer state to scan the given bytes. The next call to yylex() will * scan from a @e copy of @a bytes. * @param yybytes the byte buffer to scan * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes. - * + * @param yyscanner The scanner object. * @return the newly allocated buffer state object. */ -YY_BUFFER_STATE wcsbth_scan_bytes (yyconst char * yybytes, int _yybytes_len ) +YY_BUFFER_STATE yy_scan_bytes (const char * yybytes, int _yybytes_len , yyscan_t yyscanner) { YY_BUFFER_STATE b; char *buf; @@ -20343,19 +31254,19 @@ YY_BUFFER_STATE wcsbth_scan_bytes (yyconst char * yybytes, int _yybytes_len ) int i; /* Get memory for full buffer, including space for trailing EOB's. */ - n = _yybytes_len + 2; - buf = (char *) wcsbthalloc(n ); + n = (yy_size_t) (_yybytes_len + 2); + buf = (char *) yyalloc( n , yyscanner ); if ( ! buf ) - YY_FATAL_ERROR( "out of dynamic memory in wcsbth_scan_bytes()" ); + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" ); for ( i = 0; i < _yybytes_len; ++i ) buf[i] = yybytes[i]; buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; - b = wcsbth_scan_buffer(buf,n ); + b = yy_scan_buffer( buf, n , yyscanner); if ( ! b ) - YY_FATAL_ERROR( "bad buffer in wcsbth_scan_bytes()" ); + YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" ); /* It's okay to grow etc. this buffer, and we should throw it * away when we're done. @@ -20369,9 +31280,11 @@ YY_BUFFER_STATE wcsbth_scan_bytes (yyconst char * yybytes, int _yybytes_len ) #define YY_EXIT_FAILURE 2 #endif -static void yy_fatal_error (yyconst char* msg ) +static void yynoreturn yy_fatal_error (const char* msg , yyscan_t yyscanner) { - (void) fprintf( stderr, "%s\n", msg ); + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + fprintf( stderr, "%s\n", msg ); exit( YY_EXIT_FAILURE ); } @@ -20381,144 +31294,283 @@ static void yy_fatal_error (yyconst char* msg ) #define yyless(n) \ do \ { \ - /* Undo effects of setting up wcsbthtext. */ \ + /* Undo effects of setting up yytext. */ \ int yyless_macro_arg = (n); \ YY_LESS_LINENO(yyless_macro_arg);\ - wcsbthtext[wcsbthleng] = (yy_hold_char); \ - (yy_c_buf_p) = wcsbthtext + yyless_macro_arg; \ - (yy_hold_char) = *(yy_c_buf_p); \ - *(yy_c_buf_p) = '\0'; \ - wcsbthleng = yyless_macro_arg; \ + yytext[yyleng] = yyg->yy_hold_char; \ + yyg->yy_c_buf_p = yytext + yyless_macro_arg; \ + yyg->yy_hold_char = *yyg->yy_c_buf_p; \ + *yyg->yy_c_buf_p = '\0'; \ + yyleng = yyless_macro_arg; \ } \ while ( 0 ) /* Accessor methods (get/set functions) to struct members. */ +/** Get the user-defined data for this scanner. + * @param yyscanner The scanner object. + */ +YY_EXTRA_TYPE yyget_extra (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyextra; +} + /** Get the current line number. - * + * @param yyscanner The scanner object. + */ +int yyget_lineno (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (! YY_CURRENT_BUFFER) + return 0; + + return yylineno; +} + +/** Get the current column number. + * @param yyscanner The scanner object. */ -int wcsbthget_lineno (void) +int yyget_column (yyscan_t yyscanner) { - - return wcsbthlineno; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (! YY_CURRENT_BUFFER) + return 0; + + return yycolumn; } /** Get the input stream. - * + * @param yyscanner The scanner object. */ -FILE *wcsbthget_in (void) +FILE *yyget_in (yyscan_t yyscanner) { - return wcsbthin; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyin; } /** Get the output stream. - * + * @param yyscanner The scanner object. */ -FILE *wcsbthget_out (void) +FILE *yyget_out (yyscan_t yyscanner) { - return wcsbthout; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyout; } /** Get the length of the current token. - * + * @param yyscanner The scanner object. */ -int wcsbthget_leng (void) +int yyget_leng (yyscan_t yyscanner) { - return wcsbthleng; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyleng; } /** Get the current token. - * + * @param yyscanner The scanner object. */ -char *wcsbthget_text (void) +char *yyget_text (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yytext; +} + +/** Set the user-defined data. This data is never touched by the scanner. + * @param user_defined The data to be associated with this scanner. + * @param yyscanner The scanner object. + */ +void yyset_extra (YY_EXTRA_TYPE user_defined , yyscan_t yyscanner) { - return wcsbthtext; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyextra = user_defined ; } /** Set the current line number. - * @param line_number - * + * @param _line_number line number + * @param yyscanner The scanner object. + */ +void yyset_lineno (int _line_number , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* lineno is only valid if an input buffer exists. */ + if (! YY_CURRENT_BUFFER ) + YY_FATAL_ERROR( "yyset_lineno called with no buffer" ); + + yylineno = _line_number; +} + +/** Set the current column. + * @param _column_no column number + * @param yyscanner The scanner object. */ -void wcsbthset_lineno (int line_number ) +void yyset_column (int _column_no , yyscan_t yyscanner) { + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* column is only valid if an input buffer exists. */ + if (! YY_CURRENT_BUFFER ) + YY_FATAL_ERROR( "yyset_column called with no buffer" ); - wcsbthlineno = line_number; + yycolumn = _column_no; } /** Set the input stream. This does not discard the current * input buffer. - * @param in_str A readable stream. - * - * @see wcsbth_switch_to_buffer + * @param _in_str A readable stream. + * @param yyscanner The scanner object. + * @see yy_switch_to_buffer */ -void wcsbthset_in (FILE * in_str ) +void yyset_in (FILE * _in_str , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyin = _in_str ; +} + +void yyset_out (FILE * _out_str , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyout = _out_str ; +} + +int yyget_debug (yyscan_t yyscanner) { - wcsbthin = in_str ; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yy_flex_debug; } -void wcsbthset_out (FILE * out_str ) +void yyset_debug (int _bdebug , yyscan_t yyscanner) { - wcsbthout = out_str ; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yy_flex_debug = _bdebug ; } -int wcsbthget_debug (void) +/* Accessor methods for yylval and yylloc */ + +/* User-visible API */ + +/* yylex_init is special because it creates the scanner itself, so it is + * the ONLY reentrant function that doesn't take the scanner as the last argument. + * That's why we explicitly handle the declaration, instead of using our macros. + */ +int yylex_init(yyscan_t* ptr_yy_globals) { - return wcsbth_flex_debug; + if (ptr_yy_globals == NULL){ + errno = EINVAL; + return 1; + } + + *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), NULL ); + + if (*ptr_yy_globals == NULL){ + errno = ENOMEM; + return 1; + } + + /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */ + memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); + + return yy_init_globals ( *ptr_yy_globals ); } -void wcsbthset_debug (int bdebug ) +/* yylex_init_extra has the same functionality as yylex_init, but follows the + * convention of taking the scanner as the last argument. Note however, that + * this is a *pointer* to a scanner, as it will be allocated by this call (and + * is the reason, too, why this function also must handle its own declaration). + * The user defined value in the first argument will be available to yyalloc in + * the yyextra field. + */ +int yylex_init_extra( YY_EXTRA_TYPE yy_user_defined, yyscan_t* ptr_yy_globals ) { - wcsbth_flex_debug = bdebug ; + struct yyguts_t dummy_yyguts; + + yyset_extra (yy_user_defined, &dummy_yyguts); + + if (ptr_yy_globals == NULL){ + errno = EINVAL; + return 1; + } + + *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), &dummy_yyguts ); + + if (*ptr_yy_globals == NULL){ + errno = ENOMEM; + return 1; + } + + /* By setting to 0xAA, we expose bugs in + yy_init_globals. Leave at 0x00 for releases. */ + memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); + + yyset_extra (yy_user_defined, *ptr_yy_globals); + + return yy_init_globals ( *ptr_yy_globals ); } -static int yy_init_globals (void) +static int yy_init_globals (yyscan_t yyscanner) { - /* Initialization is the same as for the non-reentrant scanner. - * This function is called from wcsbthlex_destroy(), so don't allocate here. + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + /* Initialization is the same as for the non-reentrant scanner. + * This function is called from yylex_destroy(), so don't allocate here. */ - (yy_buffer_stack) = 0; - (yy_buffer_stack_top) = 0; - (yy_buffer_stack_max) = 0; - (yy_c_buf_p) = (char *) 0; - (yy_init) = 0; - (yy_start) = 0; + yyg->yy_buffer_stack = NULL; + yyg->yy_buffer_stack_top = 0; + yyg->yy_buffer_stack_max = 0; + yyg->yy_c_buf_p = NULL; + yyg->yy_init = 0; + yyg->yy_start = 0; + + yyg->yy_start_stack_ptr = 0; + yyg->yy_start_stack_depth = 0; + yyg->yy_start_stack = NULL; /* Defined in main.c */ #ifdef YY_STDINIT - wcsbthin = stdin; - wcsbthout = stdout; + yyin = stdin; + yyout = stdout; #else - wcsbthin = (FILE *) 0; - wcsbthout = (FILE *) 0; + yyin = NULL; + yyout = NULL; #endif /* For future reference: Set errno on error, since we are called by - * wcsbthlex_init() + * yylex_init() */ return 0; } -/* wcsbthlex_destroy is for both reentrant and non-reentrant scanners. */ -int wcsbthlex_destroy (void) +/* yylex_destroy is for both reentrant and non-reentrant scanners. */ +int yylex_destroy (yyscan_t yyscanner) { - + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + /* Pop the buffer stack, destroying each element. */ while(YY_CURRENT_BUFFER){ - wcsbth_delete_buffer(YY_CURRENT_BUFFER ); + yy_delete_buffer( YY_CURRENT_BUFFER , yyscanner ); YY_CURRENT_BUFFER_LVALUE = NULL; - wcsbthpop_buffer_state(); + yypop_buffer_state(yyscanner); } /* Destroy the stack itself. */ - wcsbthfree((yy_buffer_stack) ); - (yy_buffer_stack) = NULL; + yyfree(yyg->yy_buffer_stack , yyscanner); + yyg->yy_buffer_stack = NULL; + + /* Destroy the start condition stack. */ + yyfree( yyg->yy_start_stack , yyscanner ); + yyg->yy_start_stack = NULL; /* Reset the globals. This is important in a non-reentrant scanner so the next time - * wcsbthlex() is called, initialization will occur. */ - yy_init_globals( ); + * yylex() is called, initialization will occur. */ + yy_init_globals( yyscanner); + /* Destroy the main struct (reentrant only). */ + yyfree ( yyscanner , yyscanner ); + yyscanner = NULL; return 0; } @@ -20527,18 +31579,21 @@ int wcsbthlex_destroy (void) */ #ifndef yytext_ptr -static void yy_flex_strncpy (char* s1, yyconst char * s2, int n ) +static void yy_flex_strncpy (char* s1, const char * s2, int n , yyscan_t yyscanner) { - register int i; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + + int i; for ( i = 0; i < n; ++i ) s1[i] = s2[i]; } #endif #ifdef YY_NEED_STRLEN -static int yy_flex_strlen (yyconst char * s ) +static int yy_flex_strlen (const char * s , yyscan_t yyscanner) { - register int n; + int n; for ( n = 0; s[n]; ++n ) ; @@ -20546,13 +31601,18 @@ static int yy_flex_strlen (yyconst char * s ) } #endif -void *wcsbthalloc (yy_size_t size ) +void *yyalloc (yy_size_t size , yyscan_t yyscanner) { - return (void *) malloc( size ); + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + return malloc(size); } -void *wcsbthrealloc (void * ptr, yy_size_t size ) +void *yyrealloc (void * ptr, yy_size_t size , yyscan_t yyscanner) { + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + /* The cast to (char *) in the following accommodates both * implementations that use char* generic pointers, and those * that use void* generic pointers. It works with the latter @@ -20560,19 +31620,50 @@ void *wcsbthrealloc (void * ptr, yy_size_t size ) * any pointer type to void*, and deal with argument conversions * as though doing an assignment. */ - return (void *) realloc( (char *) ptr, size ); + return realloc(ptr, size); } -void wcsbthfree (void * ptr ) +void yyfree (void * ptr , yyscan_t yyscanner) { - free( (char *) ptr ); /* see wcsbthrealloc() for (char *) cast */ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + free( (char *) ptr ); /* see yyrealloc() for (char *) cast */ } #define YYTABLES_NAME "yytables" -#line 2076 "wcsbth.l" +#line 3172 "wcsbth.l" + + +/*---------------------------------------------------------------------------- +* External interface to the scanner. +*---------------------------------------------------------------------------*/ + +int wcsbth( + char *header, + int nkeyrec, + int relax, + int ctrl, + int keysel, + int *colsel, + int *nreject, + int *nwcs, + struct wcsprm **wcs) + +{ + // Function prototypes. + int yylex_init_extra(YY_EXTRA_TYPE extra, yyscan_t *yyscanner); + int yylex_destroy(yyscan_t yyscanner); + struct wcsbth_extra extra; + yyscan_t yyscanner; + yylex_init_extra(&extra, &yyscanner); + int status = wcsbth_scanner(header, nkeyrec, relax, ctrl, keysel, colsel, + nreject, nwcs, wcs, yyscanner); + yylex_destroy(yyscanner); + return status; +} /*---------------------------------------------------------------------------- * Perform first-pass tasks: @@ -20611,23 +31702,21 @@ int wcsbth_pass1( struct wcsbth_alts *alts) { - int ialt, icol, mask, ncol; - if (a == 0) { - /* Keywords such as DATE-OBS go along for the ride. */ + // Keywords such as DATE-OBS go along for the ride. return 0; } - ncol = alts->ncol; + int ncol = alts->ncol; - /* Do we need to allocate memory for alts? */ + // Do we need to allocate memory for alts? if (alts->arridx == 0x0) { if (ncol == 0) { - /* Can only happen if TFIELDS is missing or out-of-sequence. If n and - k are both zero then we may be processing an image header so leave - ncol alone - the array will be realloc'd later if required. */ + // Can only happen if TFIELDS is missing or out-of-sequence. If n and + // k are both zero then we may be processing an image header so leave + // ncol alone - the array will be realloc'd later if required. if (n || k) { - /* The header is mangled, assume the worst. */ + // The header is mangled, assume the worst. ncol = 999; } } @@ -20640,13 +31729,13 @@ int wcsbth_pass1( if (alts->npv) free(alts->npv); if (alts->nps) free(alts->nps); if (alts->pixlist) free(alts->pixlist); - return 2; + return WCSHDRERR_MEMORY; } alts->ncol = ncol; } else if (n > ncol || k > ncol) { - /* Can only happen if TFIELDS or the WCS keyword is wrong; carry on. */ + // Can only happen if TFIELDS or the WCS keyword is wrong; carry on. ncol = 999; if (!(alts->arridx = realloc(alts->arridx, 27*(1 + ncol)*sizeof(short int))) || @@ -20660,12 +31749,12 @@ int wcsbth_pass1( if (alts->npv) free(alts->npv); if (alts->nps) free(alts->nps); if (alts->pixlist) free(alts->pixlist); - return 2; + return WCSHDRERR_MEMORY; } - /* Since realloc() doesn't initialize the extra memory. */ - for (icol = (1 + alts->ncol); icol < (1 + ncol); icol++) { - for (ialt = 0; ialt < 27; ialt++) { + // Since realloc() doesn't initialize the extra memory. + for (int icol = (1 + alts->ncol); icol < (1 + ncol); icol++) { + for (int ialt = 0; ialt < 27; ialt++) { alts->arridx[icol][ialt] = 0; alts->npv[icol][ialt] = 0; alts->nps[icol][ialt] = 0; @@ -20676,23 +31765,23 @@ int wcsbth_pass1( alts->ncol = ncol; } - ialt = 0; + int ialt = 0; if (a != ' ') { ialt = a - 'A' + 1; } - /* A BINTAB keytype such as LONPna, in conjunction with an IMGAXIS keytype - causes a table column to be recognized as an image array. */ + // A BINTAB keytype such as LONPna, in conjunction with an IMGAXIS keytype + // causes a table column to be recognized as an image array. if (keytype & IMGHEAD || keytype & BIMGARR) { - /* n == 0 is expected for IMGHEAD keywords. */ + // n == 0 is expected for IMGHEAD keywords. if (i == 0 && j == 0) { if (alts->arridx[n][ialt] == 0) { - /* Flag that an auxiliary keyword was seen. */ + // Flag that an auxiliary keyword was seen. alts->arridx[n][ialt] = -1; } } else { - /* Record the maximum axis number found. */ + // Record the maximum axis number found. if (alts->arridx[n][ialt] < i) { alts->arridx[n][ialt] = i; } @@ -20709,17 +31798,17 @@ int wcsbth_pass1( } } - /* BINTAB keytypes, which apply both to pixel lists as well as binary table - image arrays, never contribute to recognizing a table column as a pixel - list axis. A PIXLIST keytype is required for that. */ + // BINTAB keytypes, which apply both to pixel lists as well as binary table + // image arrays, never contribute to recognizing a table column as a pixel + // list axis. A PIXLIST keytype is required for that. if (keytype == PIXLIST) { - mask = 1 << ialt; + int mask = 1 << ialt; - /* n > 0 for PIXLIST keytypes. */ + // n > 0 for PIXLIST keytypes. alts->pixlist[n] |= mask; if (k) alts->pixlist[k] |= mask; - /* Used as a flag over all columns. */ + // Used as a flag over all columns. alts->pixlist[0] |= mask; if (ptype == 'v') { @@ -20742,26 +31831,25 @@ int wcsbth_pass1( int wcsbth_init1( struct wcsbth_alts *alts, + int naux, int *nwcs, struct wcsprm **wcs) { - int ialt, icol, inherit[27], ix, mask, ncol, npsmax, npvmax, status = 0; - struct wcsprm *wcsp; - + int status = 0; if (alts->arridx == 0x0) { *nwcs = 0; return 0; } - /* Determine the number of axes in each pixel list representation. */ - ncol = alts->ncol; + // Determine the number of axes in each pixel list representation. + int ialt, mask, ncol = alts->ncol; for (ialt = 0, mask = 1; ialt < 27; ialt++, mask <<= 1) { alts->pixidx[ialt] = 0; if (alts->pixlist[0] | mask) { - for (icol = 1; icol <= ncol; icol++) { + for (int icol = 1; icol <= ncol; icol++) { if (alts->pixlist[icol] & mask) { alts->pixidx[ialt]++; } @@ -20769,18 +31857,19 @@ int wcsbth_init1( } } - /* Find the total number of coordinate representations. */ + // Find the total number of coordinate representations. *nwcs = 0; alts->imgherit = 0; - for (ialt = 0; ialt < 27; ialt++) { + int inherit[27]; + for (int ialt = 0; ialt < 27; ialt++) { inherit[ialt] = 0; - for (icol = 1; icol <= ncol; icol++) { + for (int icol = 1; icol <= ncol; icol++) { if (alts->arridx[icol][ialt] < 0) { - /* No BIMGARR keytype but there's at least one BINTAB. */ + // No BIMGARR keytype but there's at least one BINTAB. if (alts->arridx[0][ialt] > 0) { - /* There is an IMGAXIS keytype that we will inherit, so count this - representation. */ + // There is an IMGAXIS keytype that we will inherit, so count this + // representation. alts->arridx[icol][ialt] = alts->arridx[0][ialt]; } else { alts->arridx[icol][ialt] = 0; @@ -20789,11 +31878,11 @@ int wcsbth_init1( if (alts->arridx[icol][ialt]) { if (alts->arridx[0][ialt]) { - /* All IMGHEAD keywords are inherited for this ialt. */ + // All IMGHEAD keywords are inherited for this ialt. inherit[ialt] = 1; if (alts->arridx[icol][ialt] < alts->arridx[0][ialt]) { - /* The extra axes are also inherited. */ + // The extra axes are also inherited. alts->arridx[icol][ialt] = alts->arridx[0][ialt]; } } @@ -20802,18 +31891,18 @@ int wcsbth_init1( } } - /* Count every "a" found in any IMGHEAD keyword... */ + // Count every "a" found in any IMGHEAD keyword... if (alts->arridx[0][ialt]) { if (inherit[ialt]) { - /* ...but not if the IMGHEAD keywords will be inherited. */ + // ...but not if the IMGHEAD keywords will be inherited. alts->arridx[0][ialt] = 0; alts->imgherit = 1; - } else { + } else if (alts->arridx[0][ialt] > 0) { (*nwcs)++; } } - /* We need a struct for every "a" found in a PIXLIST keyword. */ + // We need a struct for every "a" found in a PIXLIST keyword. if (alts->pixidx[ialt]) { (*nwcs)++; } @@ -20821,90 +31910,98 @@ int wcsbth_init1( if (*nwcs) { - /* Allocate memory for the required number of wcsprm structs. */ + // Allocate memory for the required number of wcsprm structs. if (!(*wcs = calloc(*nwcs, sizeof(struct wcsprm)))) { - return 2; + return WCSHDRERR_MEMORY; } - /* Record the current values of NPVMAX and NPSMAX. */ - npvmax = wcsnpv(-1); - npsmax = wcsnps(-1); - - /* Initialize each wcsprm struct. */ - wcsp = *wcs; + // Initialize each wcsprm struct. + struct wcsprm *wcsp = *wcs; *nwcs = 0; - for (icol = 0; icol <= ncol; icol++) { - for (ialt = 0; ialt < 27; ialt++) { - if (alts->arridx[icol][ialt]) { - /* Image-header representations that are not for inheritance - (icol == 0) or binary table image array representations. */ + for (int icol = 0; icol <= ncol; icol++) { + for (int ialt = 0; ialt < 27; ialt++) { + if (alts->arridx[icol][ialt] > 0) { + // Image-header representations that are not for inheritance + // (icol == 0) or binary table image array representations. wcsp->flag = -1; - wcsnpv(alts->npv[icol][ialt]); - wcsnps(alts->nps[icol][ialt]); - if ((status = wcsini(1, (int)(alts->arridx[icol][ialt]), wcsp))) { + int npvmax = alts->npv[icol][ialt]; + int npsmax = alts->nps[icol][ialt]; + if ((status = wcsinit(1, (int)(alts->arridx[icol][ialt]), wcsp, + npvmax, npsmax, -1))) { wcsvfree(nwcs, wcs); break; } - /* Record the alternate version code. */ + // Record the alternate version code. if (ialt) { wcsp->alt[0] = 'A' + ialt - 1; } - /* Record the table column number. */ + // Any additional auxiliary keywords present? + if (naux) { + if (wcsauxi(1, wcsp)) { + return WCSHDRERR_MEMORY; + } + } + + // Record the table column number. wcsp->colnum = icol; - /* On the second pass alts->arridx[icol][27] indexes the array of - wcsprm structs. */ + // On the second pass alts->arridx[icol][27] indexes the array of + // wcsprm structs. alts->arridx[icol][ialt] = (*nwcs)++; wcsp++; } else { - /* Signal that this column has no WCS for this "a". */ + // Signal that this column has no WCS for this "a". alts->arridx[icol][ialt] = -1; } } } - for (ialt = 0; ialt < 27; ialt++) { + for (int ialt = 0; ialt < 27; ialt++) { if (alts->pixidx[ialt]) { - /* Pixel lists representations. */ + // Pixel lists representations. wcsp->flag = -1; - wcsnpv(alts->pixnpv[ialt]); - wcsnps(alts->pixnps[ialt]); - if ((status = wcsini(1, (int)(alts->pixidx[ialt]), wcsp))) { + int npvmax = alts->pixnpv[ialt]; + int npsmax = alts->pixnps[ialt]; + if ((status = wcsinit(1, (int)(alts->pixidx[ialt]), wcsp, npvmax, + npsmax, -1))) { wcsvfree(nwcs, wcs); break; } - /* Record the alternate version code. */ + // Record the alternate version code. if (ialt) { wcsp->alt[0] = 'A' + ialt - 1; } - /* Record the pixel list column numbers. */ - mask = (1 << ialt); + // Any additional auxiliary keywords present? + if (naux) { + if (wcsauxi(1, wcsp)) { + return WCSHDRERR_MEMORY; + } + } + + // Record the pixel list column numbers. + int icol, ix, mask = (1 << ialt); for (icol = 1, ix = 0; icol <= ncol; icol++) { if (alts->pixlist[icol] & mask) { wcsp->colax[ix++] = icol; } } - /* alts->pixidx[] indexes the array of wcsprm structs. */ + // alts->pixidx[] indexes the array of wcsprm structs. alts->pixidx[ialt] = (*nwcs)++; wcsp++; } else { - /* Signal that this column is not a pixel list axis for this "a". */ + // Signal that this column is not a pixel list axis for this "a". alts->pixidx[ialt] = -1; } } - - /* Restore the original values of NPVMAX and NPSMAX. */ - wcsnpv(npvmax); - wcsnps(npsmax); } return status; @@ -20925,28 +32022,27 @@ struct wcsprm *wcsbth_idx( { const char as[] = " ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - int iwcs; if (!wcs) return 0x0; - iwcs = -1; + int iwcs = -1; for (; iwcs < 0 && alts->ialt < 27; alts->ialt++) { - /* Note that a == 0 applies to every alternate, otherwise this - loop simply determines the appropriate value of alts->ialt. */ + // Note that a == 0 applies to every alternate, otherwise this + // loop simply determines the appropriate value of alts->ialt. if (a && a != as[alts->ialt]) continue; if (keytype & (IMGHEAD | BIMGARR)) { for (; iwcs < 0 && alts->icol <= alts->ncol; alts->icol++) { - /* Image header keywords, n == 0, apply to all columns, otherwise this - loop simply determines the appropriate value of alts->icol. */ + // Image header keywords, n == 0, apply to all columns, otherwise this + // loop simply determines the appropriate value of alts->icol. if (n && n != alts->icol) continue; iwcs = alts->arridx[alts->icol][alts->ialt]; } - /* Break out of the loop to stop alts->ialt from being incremented. */ + // Break out of the loop to stop alts->ialt from being incremented. if (iwcs >= 0) break; - /* Start from scratch for the next alts->ialt. */ + // Start from scratch for the next alts->ialt. alts->icol = 0; } @@ -20971,17 +32067,14 @@ int wcsbth_colax( char a) { - int ix; - struct wcsprm *wcsp; - if (!wcs) return 0; - wcsp = wcs; + struct wcsprm *wcsp = wcs; if (a != ' ') { wcsp += alts->pixidx[a-'A'+1]; } - for (ix = 0; ix < wcsp->naxis; ix++) { + for (int ix = 0; ix < wcsp->naxis; ix++) { if (wcsp->colax[ix] == n) { return ++ix; } @@ -20992,18 +32085,60 @@ int wcsbth_colax( /*---------------------------------------------------------------------------- -* Interpret EPOCH keywords. +* Interpret the JDREF, JDREFI, and JDREFF keywords. *---------------------------------------------------------------------------*/ -int wcsbth_epoch(void *wptr) +int wcsbth_jdref(double *mjdref, const double *jdref) + +{ + // Set MJDREF from JDREF. + if (undefined(mjdref[0] && undefined(mjdref[1]))) { + mjdref[0] = jdref[0] - 2400000.0; + mjdref[1] = jdref[1] - 0.5; + + if (mjdref[1] < 0.0) { + mjdref[0] -= 1.0; + mjdref[1] += 1.0; + } + } + + return 0; +} + +int wcsbth_jdrefi(double *mjdref, const double *jdrefi) + +{ + // Set the integer part of MJDREF from JDREFI. + if (undefined(mjdref[0])) { + mjdref[0] = *jdrefi - 2400000.5; + } + + return 0; +} + + +int wcsbth_jdreff(double *mjdref, const double *jdreff) { - double *equinox; + // Set the fractional part of MJDREF from JDREFF. + if (undefined(mjdref[1])) { + mjdref[1] = *jdreff; + } + + return 0; +} - /* If EQUINOXa is currently undefined then set it from EPOCHa. */ - equinox = (double *)wptr; + +/*---------------------------------------------------------------------------- +* Interpret EPOCHa keywords. +*---------------------------------------------------------------------------*/ + +int wcsbth_epoch(double *equinox, const double *epoch) + +{ + // If EQUINOXa is currently undefined then set it from EPOCHa. if (undefined(*equinox)) { - wcsutil_str2double(wcsbthtext, "%lf", equinox); + *equinox = *epoch; } return 0; @@ -21011,21 +32146,18 @@ int wcsbth_epoch(void *wptr) /*---------------------------------------------------------------------------- -* Interpret VSOURCE keywords. +* Interpret VSOURCEa keywords. *---------------------------------------------------------------------------*/ -int wcsbth_vsource(void *wptr) +int wcsbth_vsource(double *zsource, const double *vsource) { - double beta, c = 299792458.0, vsource, *zsource; + const double c = 299792458.0; - /* If ZSOURCEa is currently undefined then set it from VSOURCEa. */ - zsource = (double *)wptr; + // If ZSOURCEa is currently undefined then set it from VSOURCEa. if (undefined(*zsource)) { - wcsutil_str2double(wcsbthtext, "%lf", &vsource); - - /* Convert relativistic Doppler velocity to redshift. */ - beta = vsource/c; + // Convert relativistic Doppler velocity to redshift. + double beta = *vsource/c; *zsource = (1.0 + beta)/sqrt(1.0 - beta*beta) - 1.0; } @@ -21033,6 +32165,17 @@ int wcsbth_vsource(void *wptr) } +/*---------------------------------------------------------------------------- +* Check validity of a TIMEPIXR keyvalue. +*---------------------------------------------------------------------------*/ + +int wcsbth_timepixr(double timepixr) + +{ + return (timepixr < 0.0 || 1.0 < timepixr); +} + + /*---------------------------------------------------------------------------- * Tie up loose ends. *---------------------------------------------------------------------------*/ @@ -21043,15 +32186,14 @@ int wcsbth_final( struct wcsprm **wcs) { - int ialt, status; - if (alts->arridx) free(alts->arridx); if (alts->npv) free(alts->npv); if (alts->nps) free(alts->nps); if (alts->pixlist) free(alts->pixlist); - for (ialt = 0; ialt < *nwcs; ialt++) { - /* Interpret -TAB header keywords. */ + for (int ialt = 0; ialt < *nwcs; ialt++) { + // Interpret -TAB header keywords. + int status; if ((status = wcstab(*wcs+ialt))) { wcsvfree(nwcs, wcs); return status; diff --git a/cextern/wcslib/C/flexed/wcspih.c b/cextern/wcslib/C/flexed/wcspih.c index b9bfb0217a7e..a3ba24617478 100644 --- a/cextern/wcslib/C/flexed/wcspih.c +++ b/cextern/wcslib/C/flexed/wcspih.c @@ -2,35 +2,227 @@ #line 4 "wcspih.c" +#define _POSIX_C_SOURCE 1 #define YY_INT_ALIGNED short int /* A lexical scanner generated by flex */ +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 6 +#define YY_FLEX_SUBMINOR_VERSION 4 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +#ifdef yy_create_buffer +#define wcspih_create_buffer_ALREADY_DEFINED +#else #define yy_create_buffer wcspih_create_buffer +#endif + +#ifdef yy_delete_buffer +#define wcspih_delete_buffer_ALREADY_DEFINED +#else #define yy_delete_buffer wcspih_delete_buffer -#define yy_flex_debug wcspih_flex_debug +#endif + +#ifdef yy_scan_buffer +#define wcspih_scan_buffer_ALREADY_DEFINED +#else +#define yy_scan_buffer wcspih_scan_buffer +#endif + +#ifdef yy_scan_string +#define wcspih_scan_string_ALREADY_DEFINED +#else +#define yy_scan_string wcspih_scan_string +#endif + +#ifdef yy_scan_bytes +#define wcspih_scan_bytes_ALREADY_DEFINED +#else +#define yy_scan_bytes wcspih_scan_bytes +#endif + +#ifdef yy_init_buffer +#define wcspih_init_buffer_ALREADY_DEFINED +#else #define yy_init_buffer wcspih_init_buffer +#endif + +#ifdef yy_flush_buffer +#define wcspih_flush_buffer_ALREADY_DEFINED +#else #define yy_flush_buffer wcspih_flush_buffer +#endif + +#ifdef yy_load_buffer_state +#define wcspih_load_buffer_state_ALREADY_DEFINED +#else #define yy_load_buffer_state wcspih_load_buffer_state +#endif + +#ifdef yy_switch_to_buffer +#define wcspih_switch_to_buffer_ALREADY_DEFINED +#else #define yy_switch_to_buffer wcspih_switch_to_buffer -#define yyin wcspihin -#define yyleng wcspihleng +#endif + +#ifdef yypush_buffer_state +#define wcspihpush_buffer_state_ALREADY_DEFINED +#else +#define yypush_buffer_state wcspihpush_buffer_state +#endif + +#ifdef yypop_buffer_state +#define wcspihpop_buffer_state_ALREADY_DEFINED +#else +#define yypop_buffer_state wcspihpop_buffer_state +#endif + +#ifdef yyensure_buffer_stack +#define wcspihensure_buffer_stack_ALREADY_DEFINED +#else +#define yyensure_buffer_stack wcspihensure_buffer_stack +#endif + +#ifdef yylex +#define wcspihlex_ALREADY_DEFINED +#else #define yylex wcspihlex -#define yylineno wcspihlineno -#define yyout wcspihout +#endif + +#ifdef yyrestart +#define wcspihrestart_ALREADY_DEFINED +#else #define yyrestart wcspihrestart -#define yytext wcspihtext +#endif + +#ifdef yylex_init +#define wcspihlex_init_ALREADY_DEFINED +#else +#define yylex_init wcspihlex_init +#endif + +#ifdef yylex_init_extra +#define wcspihlex_init_extra_ALREADY_DEFINED +#else +#define yylex_init_extra wcspihlex_init_extra +#endif + +#ifdef yylex_destroy +#define wcspihlex_destroy_ALREADY_DEFINED +#else +#define yylex_destroy wcspihlex_destroy +#endif + +#ifdef yyget_debug +#define wcspihget_debug_ALREADY_DEFINED +#else +#define yyget_debug wcspihget_debug +#endif + +#ifdef yyset_debug +#define wcspihset_debug_ALREADY_DEFINED +#else +#define yyset_debug wcspihset_debug +#endif + +#ifdef yyget_extra +#define wcspihget_extra_ALREADY_DEFINED +#else +#define yyget_extra wcspihget_extra +#endif + +#ifdef yyset_extra +#define wcspihset_extra_ALREADY_DEFINED +#else +#define yyset_extra wcspihset_extra +#endif + +#ifdef yyget_in +#define wcspihget_in_ALREADY_DEFINED +#else +#define yyget_in wcspihget_in +#endif + +#ifdef yyset_in +#define wcspihset_in_ALREADY_DEFINED +#else +#define yyset_in wcspihset_in +#endif + +#ifdef yyget_out +#define wcspihget_out_ALREADY_DEFINED +#else +#define yyget_out wcspihget_out +#endif + +#ifdef yyset_out +#define wcspihset_out_ALREADY_DEFINED +#else +#define yyset_out wcspihset_out +#endif + +#ifdef yyget_leng +#define wcspihget_leng_ALREADY_DEFINED +#else +#define yyget_leng wcspihget_leng +#endif + +#ifdef yyget_text +#define wcspihget_text_ALREADY_DEFINED +#else +#define yyget_text wcspihget_text +#endif + +#ifdef yyget_lineno +#define wcspihget_lineno_ALREADY_DEFINED +#else +#define yyget_lineno wcspihget_lineno +#endif + +#ifdef yyset_lineno +#define wcspihset_lineno_ALREADY_DEFINED +#else +#define yyset_lineno wcspihset_lineno +#endif + +#ifdef yyget_column +#define wcspihget_column_ALREADY_DEFINED +#else +#define yyget_column wcspihget_column +#endif + +#ifdef yyset_column +#define wcspihset_column_ALREADY_DEFINED +#else +#define yyset_column wcspihset_column +#endif + +#ifdef yywrap +#define wcspihwrap_ALREADY_DEFINED +#else #define yywrap wcspihwrap +#endif + +#ifdef yyalloc +#define wcspihalloc_ALREADY_DEFINED +#else #define yyalloc wcspihalloc +#endif + +#ifdef yyrealloc +#define wcspihrealloc_ALREADY_DEFINED +#else #define yyrealloc wcspihrealloc -#define yyfree wcspihfree +#endif -#define FLEX_SCANNER -#define YY_FLEX_MAJOR_VERSION 2 -#define YY_FLEX_MINOR_VERSION 5 -#define YY_FLEX_SUBMINOR_VERSION 35 -#if YY_FLEX_SUBMINOR_VERSION > 0 -#define FLEX_BETA +#ifdef yyfree +#define wcspihfree_ALREADY_DEFINED +#else +#define yyfree wcspihfree #endif /* First, we deal with platform-specific or compiler-specific issues. */ @@ -103,60 +295,65 @@ typedef unsigned int flex_uint32_t; #define UINT32_MAX (4294967295U) #endif +#ifndef SIZE_MAX +#define SIZE_MAX (~(size_t)0) +#endif + #endif /* ! C99 */ #endif /* ! FLEXINT_H */ -#ifdef __cplusplus - -/* The "const" storage-class-modifier is valid. */ -#define YY_USE_CONST - -#else /* ! __cplusplus */ - -/* C99 requires __STDC__ to be defined as 1. */ -#if defined (__STDC__) +/* begin standard C++ headers. */ -#define YY_USE_CONST - -#endif /* defined (__STDC__) */ -#endif /* ! __cplusplus */ - -#ifdef YY_USE_CONST +/* TODO: this is always defined, so inline it */ #define yyconst const + +#if defined(__GNUC__) && __GNUC__ >= 3 +#define yynoreturn __attribute__((__noreturn__)) #else -#define yyconst +#define yynoreturn #endif /* Returned upon end-of-file. */ #define YY_NULL 0 -/* Promotes a possibly negative, possibly signed char to an unsigned - * integer for use as an array index. If the signed char is negative, - * we want to instead treat it as an 8-bit unsigned char, hence the - * double cast. +/* Promotes a possibly negative, possibly signed char to an + * integer in range [0..255] for use as an array index. */ -#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) +#define YY_SC_TO_UI(c) ((YY_CHAR) (c)) + +/* An opaque pointer. */ +#ifndef YY_TYPEDEF_YY_SCANNER_T +#define YY_TYPEDEF_YY_SCANNER_T +typedef void* yyscan_t; +#endif + +/* For convenience, these vars (plus the bison vars far below) + are macros in the reentrant scanner. */ +#define yyin yyg->yyin_r +#define yyout yyg->yyout_r +#define yyextra yyg->yyextra_r +#define yyleng yyg->yyleng_r +#define yytext yyg->yytext_r +#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno) +#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column) +#define yy_flex_debug yyg->yy_flex_debug_r /* Enter a start condition. This macro really ought to take a parameter, * but we do it the disgusting crufty way forced on us by the ()-less * definition of BEGIN. */ -#define BEGIN (yy_start) = 1 + 2 * - +#define BEGIN yyg->yy_start = 1 + 2 * /* Translate the current start state into a value that can be later handed * to BEGIN to return to the state. The YYSTATE alias is for lex * compatibility. */ -#define YY_START (((yy_start) - 1) / 2) +#define YY_START ((yyg->yy_start - 1) / 2) #define YYSTATE YY_START - /* Action number for EOF rule of a given start state. */ #define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) - /* Special action meaning "start processing a new file". */ -#define YY_NEW_FILE wcspihrestart(wcspihin ) - +#define YY_NEW_FILE yyrestart( yyin , yyscanner ) #define YY_END_OF_BUFFER_CHAR 0 /* Size of default input buffer. */ @@ -181,36 +378,32 @@ typedef unsigned int flex_uint32_t; typedef struct yy_buffer_state *YY_BUFFER_STATE; #endif -extern int wcspihleng; - -extern FILE *wcspihin, *wcspihout; +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef size_t yy_size_t; +#endif #define EOB_ACT_CONTINUE_SCAN 0 #define EOB_ACT_END_OF_FILE 1 #define EOB_ACT_LAST_MATCH 2 - + #define YY_LESS_LINENO(n) +#define YY_LINENO_REWIND_TO(ptr) /* Return all but the first "n" matched characters back to the input stream. */ #define yyless(n) \ do \ { \ - /* Undo effects of setting up wcspihtext. */ \ + /* Undo effects of setting up yytext. */ \ int yyless_macro_arg = (n); \ YY_LESS_LINENO(yyless_macro_arg);\ - *yy_cp = (yy_hold_char); \ + *yy_cp = yyg->yy_hold_char; \ YY_RESTORE_YY_MORE_OFFSET \ - (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ - YY_DO_BEFORE_ACTION; /* set up wcspihtext again */ \ + yyg->yy_c_buf_p = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ + YY_DO_BEFORE_ACTION; /* set up yytext again */ \ } \ while ( 0 ) - -#define unput(c) yyunput( c, (yytext_ptr) ) - -#ifndef YY_TYPEDEF_YY_SIZE_T -#define YY_TYPEDEF_YY_SIZE_T -typedef size_t yy_size_t; -#endif +#define unput(c) yyunput( c, yyg->yytext_ptr , yyscanner ) #ifndef YY_STRUCT_YY_BUFFER_STATE #define YY_STRUCT_YY_BUFFER_STATE @@ -224,7 +417,7 @@ struct yy_buffer_state /* Size of input buffer in bytes, not including room for EOB * characters. */ - yy_size_t yy_buf_size; + int yy_buf_size; /* Number of characters read into yy_ch_buf, not including EOB * characters. @@ -252,7 +445,7 @@ struct yy_buffer_state int yy_bs_lineno; /**< The line count. */ int yy_bs_column; /**< The column count. */ - + /* Whether to try to fill the input buffer when we reach the * end of it. */ @@ -269,113 +462,81 @@ struct yy_buffer_state * possible backing-up. * * When we actually see the EOF, we change the status to "new" - * (via wcspihrestart()), so that the user can continue scanning by - * just pointing wcspihin at a new input file. + * (via yyrestart()), so that the user can continue scanning by + * just pointing yyin at a new input file. */ #define YY_BUFFER_EOF_PENDING 2 }; #endif /* !YY_STRUCT_YY_BUFFER_STATE */ -/* Stack of input buffers. */ -static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */ -static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */ -static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */ - /* We provide macros for accessing buffer states in case in the * future we want to put the buffer states in a more general * "scanner state". * * Returns the top of the stack, or NULL. */ -#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \ - ? (yy_buffer_stack)[(yy_buffer_stack_top)] \ +#define YY_CURRENT_BUFFER ( yyg->yy_buffer_stack \ + ? yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] \ : NULL) - /* Same as previous macro, but useful when we know that the buffer stack is not * NULL or when we need an lvalue. For internal use only. */ -#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)] - -/* yy_hold_char holds the character lost when wcspihtext is formed. */ -static char yy_hold_char; -static int yy_n_chars; /* number of characters read into yy_ch_buf */ -int wcspihleng; - -/* Points to current character in buffer. */ -static char *yy_c_buf_p = (char *) 0; -static int yy_init = 0; /* whether we need to initialize */ -static int yy_start = 0; /* start state number */ - -/* Flag which is used to allow wcspihwrap()'s to do buffer switches - * instead of setting up a fresh wcspihin. A bit of a hack ... - */ -static int yy_did_buffer_switch_on_eof; - -void wcspihrestart (FILE *input_file ); -void wcspih_switch_to_buffer (YY_BUFFER_STATE new_buffer ); -YY_BUFFER_STATE wcspih_create_buffer (FILE *file,int size ); -void wcspih_delete_buffer (YY_BUFFER_STATE b ); -void wcspih_flush_buffer (YY_BUFFER_STATE b ); -void wcspihpush_buffer_state (YY_BUFFER_STATE new_buffer ); -void wcspihpop_buffer_state (void ); - -static void wcspihensure_buffer_stack (void ); -static void wcspih_load_buffer_state (void ); -static void wcspih_init_buffer (YY_BUFFER_STATE b,FILE *file ); - -#define YY_FLUSH_BUFFER wcspih_flush_buffer(YY_CURRENT_BUFFER ) - -YY_BUFFER_STATE wcspih_scan_buffer (char *base,yy_size_t size ); -YY_BUFFER_STATE wcspih_scan_string (yyconst char *yy_str ); -YY_BUFFER_STATE wcspih_scan_bytes (yyconst char *bytes,int len ); - -void *wcspihalloc (yy_size_t ); -void *wcspihrealloc (void *,yy_size_t ); -void wcspihfree (void * ); - -#define yy_new_buffer wcspih_create_buffer - +#define YY_CURRENT_BUFFER_LVALUE yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] + +void yyrestart ( FILE *input_file , yyscan_t yyscanner ); +void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size , yyscan_t yyscanner ); +void yy_delete_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yy_flush_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yypush_buffer_state ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +void yypop_buffer_state ( yyscan_t yyscanner ); + +static void yyensure_buffer_stack ( yyscan_t yyscanner ); +static void yy_load_buffer_state ( yyscan_t yyscanner ); +static void yy_init_buffer ( YY_BUFFER_STATE b, FILE *file , yyscan_t yyscanner ); +#define YY_FLUSH_BUFFER yy_flush_buffer( YY_CURRENT_BUFFER , yyscanner) + +YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_string ( const char *yy_str , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len , yyscan_t yyscanner ); + +void *yyalloc ( yy_size_t , yyscan_t yyscanner ); +void *yyrealloc ( void *, yy_size_t , yyscan_t yyscanner ); +void yyfree ( void * , yyscan_t yyscanner ); + +#define yy_new_buffer yy_create_buffer #define yy_set_interactive(is_interactive) \ { \ if ( ! YY_CURRENT_BUFFER ){ \ - wcspihensure_buffer_stack (); \ + yyensure_buffer_stack (yyscanner); \ YY_CURRENT_BUFFER_LVALUE = \ - wcspih_create_buffer(wcspihin,YY_BUF_SIZE ); \ + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); \ } \ YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ } - #define yy_set_bol(at_bol) \ { \ if ( ! YY_CURRENT_BUFFER ){\ - wcspihensure_buffer_stack (); \ + yyensure_buffer_stack (yyscanner); \ YY_CURRENT_BUFFER_LVALUE = \ - wcspih_create_buffer(wcspihin,YY_BUF_SIZE ); \ + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); \ } \ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ } - #define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) /* Begin user sect3 */ -#define wcspihwrap(n) 1 +#define wcspihwrap(yyscanner) (/*CONSTCOND*/1) #define YY_SKIP_YYWRAP - -typedef char YY_CHAR; - -FILE *wcspihin = (FILE *) 0, *wcspihout = (FILE *) 0; +typedef flex_uint8_t YY_CHAR; typedef int yy_state_type; -extern int wcspihlineno; +#define yytext_ptr yytext_r -int wcspihlineno = 1; - -extern char *wcspihtext; -#define yytext_ptr wcspihtext -static yyconst flex_int16_t yy_nxt[][128] = +static const flex_int16_t yy_nxt[][128] = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -395,965 +556,965 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, 34, 34, 34, 34, 34, 34, 34, 34, 34, - 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, - 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, - 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, - 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, - 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, - 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, - - 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, - 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, - 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, - 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, - 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, - 34, 34, 34, 34, 34, 34, 34, 34 - }, - - { - 33, 35, 35, 35, 35, 35, 35, 35, 35, 35, - 34, 35, 35, 35, 35, 35, 35, 35, 35, 35, - 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, - 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, - - 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, - 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, - 35, 35, 35, 35, 35, 35, 35, 36, 37, 38, - 35, 35, 35, 35, 35, 35, 39, 40, 41, 42, - 43, 35, 44, 45, 35, 35, 46, 47, 35, 35, - 48, 35, 35, 35, 35, 35, 35, 35, 35, 35, - 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, - 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, - 35, 35, 35, 35, 35, 35, 35, 35 - }, - - { - 33, 49, 49, 49, 49, 49, 49, 49, 49, 49, - - 34, 49, 49, 49, 49, 49, 49, 49, 49, 49, - 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, - 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, - 49, 49, 49, 49, 49, 49, 49, 49, 49, 50, - 50, 50, 50, 50, 50, 50, 50, 50, 49, 49, - 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, - 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, - 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, - 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, - 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, - - 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, - 49, 49, 49, 49, 49, 49, 49, 49 - }, - - { - 33, 49, 49, 49, 49, 49, 49, 49, 49, 49, - 34, 49, 49, 49, 49, 49, 49, 49, 49, 49, - 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, - 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, - 49, 49, 49, 49, 49, 49, 49, 49, 49, 50, - 50, 50, 50, 50, 50, 50, 50, 50, 49, 49, - 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, - 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, - - 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, - 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, - 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, - 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, - 49, 49, 49, 49, 49, 49, 49, 49 - }, - - { - 33, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 34, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 52, 52, - - 52, 52, 52, 52, 52, 52, 52, 52, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51 - }, - - { - 33, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 34, 51, 51, 51, 51, 51, 51, 51, 51, 51, - - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 52, 52, - 52, 52, 52, 52, 52, 52, 52, 52, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - - 51, 51, 51, 51, 51, 51, 51, 51 - }, - - { - 33, 53, 53, 53, 53, 53, 53, 53, 53, 53, - 34, 53, 53, 53, 53, 53, 53, 53, 53, 53, - 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, - 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, - 53, 53, 53, 53, 53, 53, 53, 53, 53, 54, - 54, 54, 54, 54, 54, 54, 54, 54, 53, 53, - 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, - 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, - 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, - - 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, - 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, - 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, - 53, 53, 53, 53, 53, 53, 53, 53 - }, - - { - 33, 53, 53, 53, 53, 53, 53, 53, 53, 53, - 34, 53, 53, 53, 53, 53, 53, 53, 53, 53, - 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, - 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, - 53, 53, 53, 53, 53, 53, 53, 53, 53, 54, - 54, 54, 54, 54, 54, 54, 54, 54, 53, 53, - - 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, - 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, - 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, - 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, - 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, - 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, - 53, 53, 53, 53, 53, 53, 53, 53 - }, - - { - 33, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 34, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 56, 57, - 57, 57, 57, 57, 57, 57, 57, 57, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55 - - }, - - { - 33, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 34, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 56, 57, - 57, 57, 57, 57, 57, 57, 57, 57, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55 - }, - - { - 33, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 34, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 59, - 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58 - }, - - { - 33, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 34, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - - 58, 58, 58, 58, 58, 58, 58, 58, 58, 59, - 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58 - }, - - { - 33, 60, 60, 60, 60, 60, 60, 60, 60, 60, - - 34, 60, 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 61, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 61, 61, 61, 61, 61, - 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, - 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, - 61, 60, 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, - - 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60 - }, - - { - 33, 60, 60, 60, 60, 60, 60, 60, 60, 60, - 34, 60, 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 61, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 61, 61, 61, 61, 61, - 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, - - 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, - 61, 60, 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60 - }, - - { - 33, 62, 62, 62, 62, 62, 62, 62, 62, 62, - 34, 62, 62, 62, 62, 62, 62, 62, 62, 62, - 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, - 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, - 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, - - 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, - 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, - 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, - 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, - 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, - 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, - 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, - 62, 62, 62, 62, 62, 62, 62, 62 - }, - - { - 33, 62, 62, 62, 62, 62, 62, 62, 62, 62, - 34, 62, 62, 62, 62, 62, 62, 62, 62, 62, - - 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, - 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, - 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, - 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, - 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, - 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, - 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, - 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, - 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, - 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, - - 62, 62, 62, 62, 62, 62, 62, 62 - }, - - { - 33, 63, 63, 63, 63, 63, 63, 63, 63, 63, - 34, 63, 63, 63, 63, 63, 63, 63, 63, 63, - 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, - 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, - 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, - 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, - 63, 64, 63, 63, 63, 63, 63, 63, 63, 63, - 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, - 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, - - 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, - 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, - 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, - 63, 63, 63, 63, 63, 63, 63, 63 - }, - - { - 33, 63, 63, 63, 63, 63, 63, 63, 63, 63, - 34, 63, 63, 63, 63, 63, 63, 63, 63, 63, - 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, - 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, - 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, - 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, - - 63, 64, 63, 63, 63, 63, 63, 63, 63, 63, - 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, - 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, - 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, - 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, - 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, - 63, 63, 63, 63, 63, 63, 63, 63 - }, - - { - 33, 65, 65, 65, 65, 65, 65, 65, 65, 65, - 34, 65, 65, 65, 65, 65, 65, 65, 65, 65, - 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, - - 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, - 65, 65, 65, 66, 65, 66, 65, 65, 67, 67, - 67, 67, 67, 67, 67, 67, 67, 67, 65, 65, - 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, - 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, - 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, - 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, - 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, - 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, - 65, 65, 65, 65, 65, 65, 65, 65 - - }, - - { - 33, 65, 65, 65, 65, 65, 65, 65, 65, 65, - 34, 65, 65, 65, 65, 65, 65, 65, 65, 65, - 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, - 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, - 65, 65, 65, 66, 65, 66, 65, 65, 67, 67, - 67, 67, 67, 67, 67, 67, 67, 67, 65, 65, - 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, - 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, - 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, - 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, - - 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, - 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, - 65, 65, 65, 65, 65, 65, 65, 65 - }, - - { - 33, 68, 68, 68, 68, 68, 68, 68, 68, 68, - 34, 68, 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 69, 68, 69, 70, 68, 71, 71, - 71, 71, 71, 71, 71, 71, 71, 71, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, - - 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68 - }, - - { - 33, 68, 68, 68, 68, 68, 68, 68, 68, 68, - 34, 68, 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, - - 68, 68, 68, 69, 68, 69, 70, 68, 71, 71, - 71, 71, 71, 71, 71, 71, 71, 71, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68 - }, - - { - 33, 72, 72, 72, 72, 72, 72, 72, 72, 72, - - 34, 72, 72, 72, 72, 72, 72, 72, 72, 72, - 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, - 72, 72, 72, 72, 72, 72, 72, 72, 72, 73, - 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, - 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, - 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, - 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, - 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, - 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, - 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, - - 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, - 72, 72, 72, 72, 72, 72, 72, 72 - }, - - { - 33, 72, 72, 72, 72, 72, 72, 72, 72, 72, - 34, 72, 72, 72, 72, 72, 72, 72, 72, 72, - 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, - 72, 72, 72, 72, 72, 72, 72, 72, 72, 73, - 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, - 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, - 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, - 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, - - 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, - 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, - 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, - 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, - 72, 72, 72, 72, 72, 72, 72, 72 - }, - - { - 33, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 34, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 75, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 76, 74, 74, - - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74 - }, - - { - 33, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 34, 74, 74, 74, 74, 74, 74, 74, 74, 74, - - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 75, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 76, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 55, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, - 74, 74, 74, 74, 74, 74, 74, 74 + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56 }, { - 33, 77, 77, 77, 77, 77, 77, 77, 77, 77, - 34, 77, 77, 77, 77, 77, 77, 77, 77, 77, - 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, - 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, - 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, - 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, - 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, - 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, - 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 55, 57, 57, 57, 57, 57, 57, 57, 57, 57, + 56, 57, 57, 57, 57, 57, 57, 57, 57, 57, + 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, + 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, - 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, - 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, - 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, - 77, 77, 77, 77, 77, 77, 77, 77 + 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, + 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, + 57, 57, 57, 57, 57, 58, 59, 60, 61, 62, + 57, 57, 63, 57, 64, 57, 65, 66, 67, 68, + 69, 57, 70, 71, 72, 57, 73, 74, 75, 76, + 77, 57, 57, 57, 57, 57, 57, 57, 57, 57, + 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, + 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, + 57, 57, 57, 57, 57, 57, 57, 57 }, { - 33, 77, 77, 77, 77, 77, 77, 77, 77, 77, - 34, 77, 77, 77, 77, 77, 77, 77, 77, 77, - 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, - 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, - 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, - 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, - - 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, - 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, - 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, - 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, - 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, - 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, - 77, 77, 77, 77, 77, 77, 77, 77 - }, - - { - 33, 78, 78, 78, 78, 78, 78, 78, 78, 78, - 34, 78, 78, 78, 78, 78, 78, 78, 78, 78, - 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, + 55, 78, 78, 78, 78, 78, 78, 78, 78, 78, + 56, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, + 78, 78, 78, 78, 78, 78, 78, 78, 79, 80, + 80, 80, 80, 80, 80, 80, 80, 80, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, - 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, + 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78 - }, { - 33, 78, 78, 78, 78, 78, 78, 78, 78, 78, - 34, 78, 78, 78, 78, 78, 78, 78, 78, 78, - 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, - 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, + 55, 78, 78, 78, 78, 78, 78, 78, 78, 78, + 56, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, + 78, 78, 78, 78, 78, 78, 78, 78, 79, 80, + 80, 80, 80, 80, 80, 80, 80, 80, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, + 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, - 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78 }, { - 33, 79, 79, 79, 79, 79, 79, 79, 79, 79, - 80, 79, 79, 79, 79, 79, 79, 79, 79, 79, - 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, - 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, - 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, - 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, - 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, + 55, 81, 81, 81, 81, 81, 81, 81, 81, 81, + 56, 81, 81, 81, 81, 81, 81, 81, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 81, 82, 83, - 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, - 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, - 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, - 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, - 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, - 79, 79, 79, 79, 79, 79, 79, 79 + 83, 83, 83, 83, 83, 83, 83, 83, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 81 }, { - 33, 79, 79, 79, 79, 79, 79, 79, 79, 79, - 80, 79, 79, 79, 79, 79, 79, 79, 79, 79, - 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, - 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, + 55, 81, 81, 81, 81, 81, 81, 81, 81, 81, + 56, 81, 81, 81, 81, 81, 81, 81, 81, 81, + + 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 81, 82, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, - 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, - 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, - 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, - 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, - 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, - 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, - 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, - 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, - 79, 79, 79, 79, 79, 79, 79, 79 + 81, 81, 81, 81, 81, 81, 81, 81 }, { - -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, + 55, 84, 84, 84, 84, 84, 84, 84, 84, 84, + 56, 84, 84, 84, 84, 84, 84, 84, 84, 84, + 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, + 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, + 84, 84, 84, 84, 84, 84, 84, 84, 85, 86, + 86, 86, 86, 86, 86, 86, 86, 86, 84, 84, + 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, + 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, + 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, - -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, - -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, - -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, - -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, - -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, - -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, - -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, - -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, - -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, - -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, - - -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, - -33, -33, -33, -33, -33, -33, -33, -33 + 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, + 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, + 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, + 84, 84, 84, 84, 84, 84, 84, 84 }, { - 33, -34, -34, -34, -34, -34, -34, -34, -34, -34, - -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, - -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, - -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, - -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, - -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, - -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, - -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, + 55, 84, 84, 84, 84, 84, 84, 84, 84, 84, + 56, 84, 84, 84, 84, 84, 84, 84, 84, 84, + 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, + 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, + 84, 84, 84, 84, 84, 84, 84, 84, 85, 86, + 86, 86, 86, 86, 86, 86, 86, 86, 84, 84, - -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, - -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, - -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, - -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, - -34, -34, -34, -34, -34, -34, -34, -34 + 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, + 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, + 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, + 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, + 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, + 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, + 84, 84, 84, 84, 84, 84, 84, 84 }, { - 33, -35, -35, -35, -35, -35, -35, -35, -35, -35, - -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, - -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, - -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, - -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, + 55, 87, 87, 87, 87, 87, 87, 87, 87, 87, + 56, 87, 87, 87, 87, 87, 87, 87, 87, 87, + 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, + + 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, + 87, 87, 87, 87, 87, 87, 87, 87, 88, 89, + 89, 89, 89, 89, 89, 89, 89, 89, 87, 87, + 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, + 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, + 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, + 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, + 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, + 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, + 87, 87, 87, 87, 87, 87, 87, 87 - -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, - -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, - -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, - -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, - -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, - -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, - -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, - -35, -35, -35, -35, -35, -35, -35, -35 }, { - 33, -36, -36, -36, -36, -36, -36, -36, -36, -36, - -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, + 55, 87, 87, 87, 87, 87, 87, 87, 87, 87, + 56, 87, 87, 87, 87, 87, 87, 87, 87, 87, + 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, + 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, + 87, 87, 87, 87, 87, 87, 87, 87, 88, 89, + 89, 89, 89, 89, 89, 89, 89, 89, 87, 87, + 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, + 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, + 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, + 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, - -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, - -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, - -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, - -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, - -36, -36, -36, -36, -36, -36, -36, -36, 81, -36, - -36, -36, -36, -36, -36, -36, -36, -36, 82, -36, - -36, -36, 83, 84, 85, 86, -36, -36, -36, -36, - -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, - -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, - -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, - - -36, -36, -36, -36, -36, -36, -36, -36 - }, - - { - 33, -37, -37, -37, -37, -37, -37, -37, -37, -37, - -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, - -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, - -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, - -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, - -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, - -37, -37, -37, -37, -37, 87, -37, -37, -37, -37, - -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, - -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, + 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, + 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, + 87, 87, 87, 87, 87, 87, 87, 87 + }, + + { + 55, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 56, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 91, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 91, 91, 91, 91, 91, - -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, - -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, - -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, - -37, -37, -37, -37, -37, -37, -37, -37 - }, - - { - 33, -38, -38, -38, -38, -38, -38, -38, -38, -38, - -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, - -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, - -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, - -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, - -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, + 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + 91, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90 + }, + + { + 55, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 56, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 91, 90, 90, 90, 90, 90, 90, 90, - -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, - -38, -38, -38, -38, -38, -38, -38, -38, 88, -38, - 89, 90, -38, -38, -38, -38, -38, -38, -38, -38, - -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, - -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, - -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, - -38, -38, -38, -38, -38, -38, -38, -38 - }, - - { - 33, -39, -39, -39, -39, -39, -39, -39, -39, -39, - -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, - -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 91, 91, 91, 91, 91, + 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + 91, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90 + }, + + { + 55, 92, 92, 92, 92, 92, 92, 92, 92, 92, + + 56, 92, 92, 92, 92, 92, 92, 92, 92, 92, + 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, + 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, + 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, + 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, + 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, + 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, + 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, + 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, + 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, - -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, - -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, - -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, - -39, -39, -39, -39, -39, 91, -39, -39, -39, -39, - -39, -39, -39, -39, -39, -39, -39, -39, -39, 92, - -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, - -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, - -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, - -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, - -39, -39, -39, -39, -39, -39, -39, -39 - - }, - - { - 33, -40, -40, -40, -40, -40, -40, -40, -40, -40, - -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, - -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, - -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, - -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, - -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, - -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, - -40, -40, -40, -40, 93, -40, -40, -40, -40, -40, - -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, - -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, + 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, + 92, 92, 92, 92, 92, 92, 92, 92 + }, + + { + 55, 92, 92, 92, 92, 92, 92, 92, 92, 92, + 56, 92, 92, 92, 92, 92, 92, 92, 92, 92, + 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, + 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, + 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, + 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, + 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, + 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, - -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, - -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, - -40, -40, -40, -40, -40, -40, -40, -40 - }, - - { - 33, -41, -41, -41, -41, -41, -41, -41, -41, -41, - -41, -41, -41, -41, -41, -41, -41, -41, -41, -41, - -41, -41, -41, -41, -41, -41, -41, -41, -41, -41, - -41, -41, -41, -41, -41, -41, -41, -41, -41, -41, - -41, -41, -41, -41, -41, -41, -41, -41, -41, -41, - -41, -41, -41, -41, -41, -41, -41, -41, -41, -41, - -41, -41, -41, -41, -41, 94, -41, -41, -41, -41, + 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, + 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, + 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, + 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, + 92, 92, 92, 92, 92, 92, 92, 92 + }, + + { + 55, 93, 93, 93, 93, 93, 93, 93, 93, 93, + 56, 93, 93, 93, 93, 93, 93, 93, 93, 93, + 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, + 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, + 93, 93, 93, 93, 93, 93, 93, 93, 94, 94, - -41, -41, -41, -41, -41, -41, -41, -41, -41, -41, - -41, -41, -41, -41, -41, -41, -41, -41, -41, -41, - -41, -41, -41, -41, -41, -41, -41, -41, -41, -41, - -41, -41, -41, -41, -41, -41, -41, -41, -41, -41, - -41, -41, -41, -41, -41, -41, -41, -41, -41, -41, - -41, -41, -41, -41, -41, -41, -41, -41 - }, - - { - 33, -42, -42, -42, -42, -42, -42, -42, -42, -42, - -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, - -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, - -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, + 94, 94, 94, 94, 94, 94, 94, 94, 93, 93, + 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, + 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, + 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, + 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, + 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, + 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, + 93, 93, 93, 93, 93, 93, 93, 93 + }, + + { + 55, 93, 93, 93, 93, 93, 93, 93, 93, 93, + 56, 93, 93, 93, 93, 93, 93, 93, 93, 93, - -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, - -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, - -42, -42, -42, -42, -42, -42, 95, -42, -42, -42, - -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, - -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, - -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, - -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, - -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, - -42, -42, -42, -42, -42, -42, -42, -42 - }, - - { - 33, -43, -43, -43, -43, -43, -43, -43, -43, -43, - - -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, - -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, - -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, - -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, - -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, - -43, -43, -43, -43, -43, -43, -43, 96, -43, -43, - -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, - -43, -43, 97, 98, -43, -43, 99, -43, -43, -43, - -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, - -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, + 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, + 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, + 93, 93, 93, 93, 93, 93, 93, 93, 94, 94, + 94, 94, 94, 94, 94, 94, 94, 94, 93, 93, + 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, + 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, + 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, + 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, + 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, + 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, + + 93, 93, 93, 93, 93, 93, 93, 93 + }, + + { + 55, 95, 95, 95, 95, 95, 95, 95, 95, 95, + 56, 95, 95, 95, 95, 95, 95, 95, 95, 95, + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, + 95, 95, 95, 95, 95, 95, 95, 95, 96, 96, + 96, 96, 96, 96, 96, 96, 96, 96, 95, 95, + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, - -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, - -43, -43, -43, -43, -43, -43, -43, -43 - }, - - { - 33, -44, -44, -44, -44, -44, -44, -44, -44, -44, - -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, - -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, - -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, - -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, - -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, - -44, -44, -44, -44, -44, 100, -44, -44, -44, 101, - -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, + 95, 95, 95, 95, 95, 95, 95, 95 + }, + + { + 55, 95, 95, 95, 95, 95, 95, 95, 95, 95, + 56, 95, 95, 95, 95, 95, 95, 95, 95, 95, + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, + 95, 95, 95, 95, 95, 95, 95, 95, 96, 96, + 96, 96, 96, 96, 96, 96, 96, 96, 95, 95, - -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, - -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, - -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, - -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, - -44, -44, -44, -44, -44, -44, -44, -44 - }, - - { - 33, -45, -45, -45, -45, -45, -45, -45, -45, -45, - -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, - -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, - -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, - -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, + 95, 95, 95, 95, 95, 95, 95, 95 + }, + + { + 55, 97, 97, 97, 97, 97, 97, 97, 97, 97, + 56, 97, 97, 97, 97, 97, 97, 97, 97, 97, + 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, - -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, - -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, - -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, - 102, -45, -45, 103, -45, -45, -45, -45, -45, -45, - -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, - -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, - -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, - -45, -45, -45, -45, -45, -45, -45, -45 - }, - - { - 33, -46, -46, -46, -46, -46, -46, -46, -46, -46, - -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, + 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, + 97, 97, 97, 97, 97, 97, 97, 97, 98, 98, + 98, 98, 98, 98, 98, 98, 98, 98, 97, 97, + 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, + 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, + 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, + 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, + 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, + 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, + 97, 97, 97, 97, 97, 97, 97, 97 + + }, + + { + 55, 97, 97, 97, 97, 97, 97, 97, 97, 97, + 56, 97, 97, 97, 97, 97, 97, 97, 97, 97, + 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, + 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, + 97, 97, 97, 97, 97, 97, 97, 97, 98, 98, + 98, 98, 98, 98, 98, 98, 98, 98, 97, 97, + 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, + 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, + 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, + 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, - -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, - -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, - -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, - -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, - -46, -46, -46, -46, -46, -46, -46, -46, -46, 104, - -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, - -46, -46, -46, 105, -46, -46, -46, -46, -46, -46, - -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, - -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, - -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, - - -46, -46, -46, -46, -46, -46, -46, -46 - }, - - { - 33, -47, -47, -47, -47, -47, -47, -47, -47, -47, - -47, -47, -47, -47, -47, -47, -47, -47, -47, -47, - -47, -47, -47, -47, -47, -47, -47, -47, -47, -47, - -47, -47, -47, -47, -47, -47, -47, -47, -47, -47, - -47, -47, -47, -47, -47, -47, -47, -47, -47, -47, - -47, -47, -47, -47, -47, -47, -47, -47, -47, -47, - -47, -47, -47, -47, -47, -47, -47, 106, -47, -47, - -47, -47, -47, -47, -47, -47, -47, -47, -47, -47, - -47, -47, -47, -47, -47, -47, -47, -47, -47, -47, + 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, + 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, + 97, 97, 97, 97, 97, 97, 97, 97 + }, + + { + 55, 99, 99, 99, 99, 99, 99, 99, 99, 99, + 56, 99, 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, 100, 100, + 100, 100, 100, 100, 100, 100, 100, 100, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, - -47, -47, -47, -47, -47, -47, -47, -47, -47, -47, - -47, -47, -47, -47, -47, -47, -47, -47, -47, -47, - -47, -47, -47, -47, -47, -47, -47, -47, -47, -47, - -47, -47, -47, -47, -47, -47, -47, -47 - }, - - { - 33, -48, -48, -48, -48, -48, -48, -48, -48, -48, - -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, - -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, - -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, - -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, - -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, + 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99 + }, + + { + 55, 99, 99, 99, 99, 99, 99, 99, 99, 99, + 56, 99, 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, - -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, - -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, - -48, -48, -48, 107, -48, -48, -48, -48, -48, -48, - -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, - -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, - -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, - -48, -48, -48, -48, -48, -48, -48, -48 - }, - - { - 33, -49, -49, -49, -49, -49, -49, -49, -49, -49, - -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, - -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, + 99, 99, 99, 99, 99, 99, 99, 99, 100, 100, + 100, 100, 100, 100, 100, 100, 100, 100, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99 + }, + + { + 55, 101, 101, 101, 101, 101, 101, 101, 101, 101, + + 56, 101, 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, 101, 102, + 102, 102, 102, 102, 102, 102, 102, 102, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, - -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, - -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, - -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, - -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, - -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, - -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, - -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, - -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, - -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, - -49, -49, -49, -49, -49, -49, -49, -49 + 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101 + }, + + { + 55, 101, 101, 101, 101, 101, 101, 101, 101, 101, + 56, 101, 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, 101, 102, + 102, 102, 102, 102, 102, 102, 102, 102, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, - }, + 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101 + }, + + { + 55, 103, 103, 103, 103, 103, 103, 103, 103, 103, + 56, 103, 103, 103, 103, 103, 103, 103, 103, 103, + 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, + 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, + 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, - { - 33, -50, -50, -50, -50, -50, -50, -50, -50, -50, - -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, - -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, - -50, -50, 108, -50, -50, -50, -50, -50, -50, -50, - -50, -50, -50, -50, -50, -50, -50, -50, 109, 109, - 109, 109, 109, 109, 109, 109, 109, 109, -50, -50, - -50, -50, -50, -50, -50, 108, 108, 108, 108, 108, - 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, - 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, - 108, -50, -50, -50, -50, -50, -50, -50, -50, -50, + 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, + 103, 104, 103, 103, 103, 103, 103, 103, 103, 103, + 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, + 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, + 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, + 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, + 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, + 103, 103, 103, 103, 103, 103, 103, 103 + }, + + { + 55, 103, 103, 103, 103, 103, 103, 103, 103, 103, + 56, 103, 103, 103, 103, 103, 103, 103, 103, 103, + + 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, + 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, + 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, + 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, + 103, 104, 103, 103, 103, 103, 103, 103, 103, 103, + 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, + 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, + 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, + 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, + 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, + + 103, 103, 103, 103, 103, 103, 103, 103 + }, + + { + 55, 105, 105, 105, 105, 105, 105, 105, 105, 105, + 56, 105, 105, 105, 105, 105, 105, 105, 105, 105, + 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, + 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, + 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, + 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, + 105, 106, 105, 105, 105, 105, 105, 105, 105, 105, + 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, + 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, + + 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, + 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, + 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, + 105, 105, 105, 105, 105, 105, 105, 105 + }, + + { + 55, 105, 105, 105, 105, 105, 105, 105, 105, 105, + 56, 105, 105, 105, 105, 105, 105, 105, 105, 105, + 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, + 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, + 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, + 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, + + 105, 106, 105, 105, 105, 105, 105, 105, 105, 105, + 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, + 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, + 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, + 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, + 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, + 105, 105, 105, 105, 105, 105, 105, 105 + }, + + { + 55, 107, 107, 107, 107, 107, 107, 107, 107, 107, + 56, 107, 107, 107, 107, 107, 107, 107, 107, 107, + 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, + + 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, + 107, 107, 107, 108, 107, 108, 107, 107, 109, 109, + 109, 109, 109, 109, 109, 109, 109, 109, 107, 107, + 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, + 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, + 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, + 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, + 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, + 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, + 107, 107, 107, 107, 107, 107, 107, 107 + + }, + + { + 55, 107, 107, 107, 107, 107, 107, 107, 107, 107, + 56, 107, 107, 107, 107, 107, 107, 107, 107, 107, + 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, + 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, + 107, 107, 107, 108, 107, 108, 107, 107, 109, 109, + 109, 109, 109, 109, 109, 109, 109, 109, 107, 107, + 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, + 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, + 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, + 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, - -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, - -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, - -50, -50, -50, -50, -50, -50, -50, -50 + 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, + 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, + 107, 107, 107, 107, 107, 107, 107, 107 + }, + + { + 55, 110, 110, 110, 110, 110, 110, 110, 110, 110, + 56, 110, 110, 110, 110, 110, 110, 110, 110, 110, + 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, + 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, + 110, 110, 110, 111, 110, 111, 112, 110, 113, 113, + 113, 113, 113, 113, 113, 113, 113, 113, 110, 110, + 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, + + 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, + 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, + 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, + 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, + 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, + 110, 110, 110, 110, 110, 110, 110, 110 + }, + + { + 55, 110, 110, 110, 110, 110, 110, 110, 110, 110, + 56, 110, 110, 110, 110, 110, 110, 110, 110, 110, + 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, + 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, + + 110, 110, 110, 111, 110, 111, 112, 110, 113, 113, + 113, 113, 113, 113, 113, 113, 113, 113, 110, 110, + 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, + 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, + 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, + 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, + 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, + 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, + 110, 110, 110, 110, 110, 110, 110, 110 + }, + + { + 55, 114, 114, 114, 114, 114, 114, 114, 114, 114, + + 56, 114, 114, 114, 114, 114, 114, 114, 114, 114, + 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, + 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, + 114, 114, 114, 115, 114, 115, 116, 114, 117, 117, + 117, 117, 117, 117, 117, 117, 117, 117, 114, 114, + 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, + 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, + 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, + 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, + 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, + + 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, + 114, 114, 114, 114, 114, 114, 114, 114 + }, + + { + 55, 114, 114, 114, 114, 114, 114, 114, 114, 114, + 56, 114, 114, 114, 114, 114, 114, 114, 114, 114, + 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, + 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, + 114, 114, 114, 115, 114, 115, 116, 114, 117, 117, + 117, 117, 117, 117, 117, 117, 117, 117, 114, 114, + 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, + 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, + + 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, + 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, + 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, + 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, + 114, 114, 114, 114, 114, 114, 114, 114 + }, + + { + 55, 118, 118, 118, 118, 118, 118, 118, 118, 118, + 56, 118, 118, 118, 118, 118, 118, 118, 118, 118, + 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, + 118, 118, 118, 118, 118, 118, 118, 118, 118, 119, + 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, + + 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, + 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, + 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, + 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, + 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, + 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, + 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, + 118, 118, 118, 118, 118, 118, 118, 118 + }, + + { + 55, 118, 118, 118, 118, 118, 118, 118, 118, 118, + 56, 118, 118, 118, 118, 118, 118, 118, 118, 118, + + 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, + 118, 118, 118, 118, 118, 118, 118, 118, 118, 119, + 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, + 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, + 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, + 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, + 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, + 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, + 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, + 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, + + 118, 118, 118, 118, 118, 118, 118, 118 + }, + + { + 55, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 56, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 121, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120 }, { - 33, -51, -51, -51, -51, -51, -51, -51, -51, -51, - -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, - -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, - -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, - -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, - -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, - -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, + 55, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 56, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 121, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, - -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, - -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, - -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, - -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, - -51, -51, -51, -51, -51, -51, -51, -51 + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120 }, { - 33, -52, -52, -52, -52, -52, -52, -52, -52, -52, - -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, - -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, - -52, -52, 110, -52, -52, -52, -52, -52, -52, -52, + 55, 122, 122, 122, 122, 122, 122, 122, 122, 122, + 56, 122, 122, 122, 122, 122, 122, 122, 122, 122, + 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, + + 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, + 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, + 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, + 122, 122, 122, 122, 122, 123, 123, 123, 123, 123, + 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, + 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, + 123, 122, 122, 122, 122, 123, 122, 123, 123, 123, + 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, + 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, + 123, 123, 123, 122, 122, 122, 122, 122 - -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, - -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, - -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, - -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, - -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, - -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, - -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, - -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, - -52, -52, -52, -52, -52, -52, -52, -52 }, { - 33, -53, -53, -53, -53, -53, -53, -53, -53, -53, + 55, 122, 122, 122, 122, 122, 122, 122, 122, 122, + 56, 122, 122, 122, 122, 122, 122, 122, 122, 122, + 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, + 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, + 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, + 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, + 122, 122, 122, 122, 122, 123, 123, 123, 123, 123, + 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, + 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, + 123, 122, 122, 122, 122, 123, 122, 123, 123, 123, - -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, - -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, - -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, - -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, - -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, - -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, - -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, - -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, - -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, - -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, - - -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, - -53, -53, -53, -53, -53, -53, -53, -53 + 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, + 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, + 123, 123, 123, 122, 122, 122, 122, 122 }, { - 33, -54, -54, -54, -54, -54, -54, -54, -54, -54, - -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, - -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, - -54, -54, 111, -54, -54, -54, -54, -54, -54, -54, - -54, -54, -54, -54, -54, -54, -54, -54, 112, 112, - 112, 112, 112, 112, 112, 112, 112, 112, -54, -54, - -54, -54, -54, -54, -54, 111, 111, 111, 111, 111, - 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, + 55, 124, 124, 124, 124, 124, 124, 124, 124, 124, + 56, 124, 124, 124, 124, 124, 124, 124, 124, 124, + 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, + 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, + 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, + 124, 124, 124, 124, 124, 124, 124, 124, 125, 124, + 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, - 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, - 111, -54, -54, -54, -54, -54, -54, -54, -54, -54, - -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, - -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, - -54, -54, -54, -54, -54, -54, -54, -54 + 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, + 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, + 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, + 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, + 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, + 124, 124, 124, 124, 124, 124, 124, 124 }, { - 33, -55, -55, -55, -55, -55, -55, -55, -55, -55, - -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, - -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, - -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, - -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, + 55, 124, 124, 124, 124, 124, 124, 124, 124, 124, + 56, 124, 124, 124, 124, 124, 124, 124, 124, 124, + 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, + 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, - -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, - -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, - -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, - -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, - -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, - -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, - -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, - -55, -55, -55, -55, -55, -55, -55, -55 + 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, + 124, 124, 124, 124, 124, 124, 124, 124, 125, 124, + 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, + 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, + 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, + 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, + 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, + 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, + 124, 124, 124, 124, 124, 124, 124, 124 }, { - 33, -56, -56, -56, -56, -56, -56, -56, -56, -56, - -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, + 55, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 56, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 127, 126, 127, 128, 126, 129, 129, + 129, 129, 129, 129, 129, 129, 129, 129, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126 + }, + + { + 55, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 56, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 127, 126, 127, 128, 126, 129, 129, + 129, 129, 129, 129, 129, 129, 129, 129, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126 + }, + + { + 55, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 130, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56 + }, + + { + 55, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 130, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + + 56, 56, 56, 56, 56, 56, 56, 56 + }, + + { + 55, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 132, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 133, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 134, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131 + }, + + { + 55, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 132, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 133, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 134, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131 + }, + + { + 55, 135, 135, 135, 135, 135, 135, 135, 135, 135, + 136, 135, 135, 135, 135, 135, 135, 135, 135, 135, + 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, + + 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, + 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, + 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, + 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, + 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, + 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, + 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, + 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, + 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, + 135, 135, 135, 135, 135, 135, 135, 135 + + }, + + { + 55, 135, 135, 135, 135, 135, 135, 135, 135, 135, + 136, 135, 135, 135, 135, 135, 135, 135, 135, 135, + 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, + 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, + 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, + 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, + 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, + 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, + 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, + 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, + + 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, + 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, + 135, 135, 135, 135, 135, 135, 135, 135 + }, + + { + 55, 137, 137, 137, 137, 137, 137, 137, 137, 137, + 138, 137, 137, 137, 137, 137, 137, 137, 137, 137, + 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, + 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, + 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, + 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, + 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, + + 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, + 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, + 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, + 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, + 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, + 137, 137, 137, 137, 137, 137, 137, 137 + }, + + { + 55, 137, 137, 137, 137, 137, 137, 137, 137, 137, + 138, 137, 137, 137, 137, 137, 137, 137, 137, 137, + 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, + 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, + + 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, + 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, + 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, + 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, + 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, + 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, + 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, + 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, + 137, 137, 137, 137, 137, 137, 137, 137 + }, + + { + 55, 139, 139, 139, 139, 139, 139, 139, 139, 139, + + 140, 139, 139, 139, 139, 139, 139, 139, 139, 139, + 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, + 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, + 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, + 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, + 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, + 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, + 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, + 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, + 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, + + 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, + 139, 139, 139, 139, 139, 139, 139, 139 + }, + + { + 55, 139, 139, 139, 139, 139, 139, 139, 139, 139, + 140, 139, 139, 139, 139, 139, 139, 139, 139, 139, + 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, + 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, + 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, + 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, + 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, + 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, + + 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, + 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, + 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, + 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, + 139, 139, 139, 139, 139, 139, 139, 139 + }, + + { + -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, + -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, + -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, + -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, + -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, + + -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, + -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, + -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, + -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, + -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, + -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, + -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, + -55, -55, -55, -55, -55, -55, -55, -55 + }, + + { + 55, -56, -56, -56, -56, -56, -56, -56, -56, -56, + -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, + + -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, + -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, + -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, + -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, - -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, - -56, -56, -56, -56, -56, -56, -56, -56, 113, 113, - 113, 113, 113, 113, 113, 113, 113, 113, -56, -56, - -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, @@ -1364,24 +1525,24 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -57, -57, -57, -57, -57, -57, -57, -57, -57, + 55, -57, -57, -57, -57, -57, -57, -57, -57, -57, + -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, + -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, - -57, -57, -57, -57, -57, -57, -57, -57, 114, 114, - 114, 114, 114, 114, 114, 114, 114, 114, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, - -57, -57, -57, -57, -57, 115, -57, -57, -57, -57, + -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57 }, { - 33, -58, -58, -58, -58, -58, -58, -58, -58, -58, + 55, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, @@ -1389,26 +1550,26 @@ static yyconst flex_int16_t yy_nxt[][128] = -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, - -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, - -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, - -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, + -58, -58, -58, -58, -58, -58, -58, 141, -58, -58, + 142, -58, -58, -58, -58, -58, -58, -58, -58, -58, + -58, -58, -58, -58, -58, 143, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58 }, { - 33, -59, -59, -59, -59, -59, -59, -59, -59, -59, + 55, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, - -59, -59, -59, -59, -59, -59, -59, -59, 116, 116, - 116, 116, 116, 116, 116, 116, 116, 116, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, - -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, - -59, -59, -59, -59, -59, 117, -59, -59, -59, -59, + -59, -59, -59, -59, -59, -59, -59, -59, 144, 145, + -59, -59, -59, -59, -59, -59, 146, -59, -59, -59, + 147, -59, -59, -59, -59, -59, -59, -59, -59, -59, + -59, -59, -59, -59, -59, 148, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59 @@ -1416,16 +1577,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -60, -60, -60, -60, -60, -60, -60, -60, -60, - -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, - -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, - -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, - -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, + 55, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, + -60, -60, -60, -60, -60, -60, -60, -60, 149, -60, + -60, -60, -60, -60, -60, -60, -60, -60, 150, -60, + 151, 152, 153, 154, 155, 156, -60, -60, -60, -60, + 157, -60, -60, -60, -60, 158, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, @@ -1433,16 +1594,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -61, -61, -61, -61, -61, -61, -61, -61, -61, - -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, + 55, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, + -61, -61, -61, -61, -61, 159, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, - -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, + 160, 161, -61, 162, -61, -61, 163, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, @@ -1450,7 +1611,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -62, -62, -62, -62, -62, -62, -62, -62, -62, + 55, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, @@ -1458,8 +1619,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, - -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, - -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, + -62, -62, -62, -62, -62, -62, -62, -62, 164, -62, + 165, 166, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, @@ -1467,7 +1628,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -63, -63, -63, -63, -63, -63, -63, -63, -63, + 55, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, @@ -1475,7 +1636,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, - -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, + -63, 167, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, @@ -1485,13 +1646,13 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -64, -64, -64, -64, -64, -64, -64, -64, -64, + 55, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, - -64, -64, 118, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, + -64, -64, -64, -64, -64, -64, -64, -64, 168, 169, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, @@ -1502,15 +1663,15 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -65, -65, -65, -65, -65, -65, -65, -65, -65, + 55, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, - -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, - -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, + -65, -65, -65, -65, -65, 170, -65, -65, -65, -65, + -65, -65, -65, -65, -65, -65, -65, -65, -65, 171, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, @@ -1519,16 +1680,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -66, -66, -66, -66, -66, -66, -66, -66, -66, + 55, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, - -66, -66, -66, -66, -66, -66, -66, -66, 119, 119, - 119, 119, 119, 119, 119, 119, 119, 119, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, + -66, -66, -66, -66, 172, -66, -66, -66, -66, -66, + -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, @@ -1537,13 +1698,13 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -67, -67, -67, -67, -67, -67, -67, -67, -67, + 55, -67, -67, -67, -67, -67, -67, -67, -67, -67, + -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, - -67, -67, -67, -67, -67, -67, -67, -67, 119, 119, - 119, 119, 119, 119, 119, 119, 119, 119, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, + -67, -67, -67, -67, -67, 173, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, @@ -1554,14 +1715,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -68, -68, -68, -68, -68, -68, -68, -68, -68, + 55, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, - -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, + -68, -68, -68, -68, -68, -68, 174, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, @@ -1571,16 +1732,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -69, -69, -69, -69, -69, -69, -69, -69, -69, + 55, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, - -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, - -69, -69, -69, -69, -69, -69, 120, -69, 121, 121, - 121, 121, 121, 121, 121, 121, 121, 121, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, + -69, -69, -69, -69, -69, -69, -69, 175, -69, -69, + -69, -69, -69, -69, -69, -69, 176, -69, -69, -69, + 177, -69, 178, 179, -69, -69, 180, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, @@ -1589,15 +1750,15 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -70, -70, -70, -70, -70, -70, -70, -70, -70, + 55, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, - -70, -70, -70, -70, -70, -70, -70, -70, 122, 122, - 122, 122, 122, 122, 122, 122, 122, 122, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, + -70, -70, -70, -70, -70, 181, -70, -70, -70, 182, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, + -70, -70, -70, 183, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, @@ -1606,33 +1767,33 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -71, -71, -71, -71, -71, -71, -71, -71, -71, + 55, -71, -71, -71, -71, -71, -71, -71, -71, -71, + -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, + -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, + -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, - -71, -71, -71, -71, -71, -71, 123, -71, 124, 124, - 124, 124, 124, 124, 124, 124, 124, 124, -71, -71, - -71, -71, -71, -71, -71, -71, -71, -71, -71, 125, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, + 184, -71, -71, 185, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, - -71, 125, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71 }, { - 33, -72, -72, -72, -72, -72, -72, -72, -72, -72, + 55, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, - -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, - -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, - -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, + -72, -72, -72, -72, -72, -72, -72, -72, -72, 186, + -72, -72, -72, 187, -72, -72, -72, -72, -72, -72, + -72, -72, 188, 189, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, @@ -1640,31 +1801,31 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 55, -73, -73, -73, -73, -73, -73, -73, -73, -73, - 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, - 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, - 126, 126, 126, 126, 126, 126, 126, 126, 126, 127, - 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, - 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, - 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, - 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, - 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, - 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, - 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, + -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, + -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, + -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, + -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, + -73, -73, -73, -73, -73, -73, -73, -73, -73, 190, + -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, + -73, -73, -73, 191, -73, -73, -73, -73, -73, -73, + -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, + -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, - 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, - 126, 126, 126, 126, 126, 126, 126, 126 + -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, + -73, -73, -73, -73, -73, -73, -73, -73 }, { - 33, -74, -74, -74, -74, -74, -74, -74, -74, -74, - -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, + 55, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, + -74, -74, -74, -74, -74, 192, -74, 193, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, @@ -1675,16 +1836,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -75, -75, -75, -75, -75, -75, -75, -75, -75, + 55, -75, -75, -75, -75, -75, -75, -75, -75, -75, + -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, - -75, -75, 128, -75, -75, -75, -75, -75, -75, -75, - -75, -75, -75, -75, -75, -75, -75, 129, -75, -75, - -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, + -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, + 194, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, @@ -1692,86 +1853,86 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, 130, 130, 130, 130, 130, 130, 130, 130, 130, - -76, 130, 130, 130, 130, 130, 130, 130, 130, 130, + 55, -76, -76, -76, -76, -76, -76, -76, -76, -76, + -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, - 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, - 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, - 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, - 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, - 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, - 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, - 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, - 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, - 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, - 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, + -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, + -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, + -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, + -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, + -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, + -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, + 195, -76, -76, -76, -76, -76, -76, -76, -76, -76, + -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, + -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, + -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, - 130, 130, 130, 130, 130, 130, 130, 130 + -76, -76, -76, -76, -76, -76, -76, -76 }, { - 33, 131, 131, 131, 131, 131, 131, 131, 131, 131, - -77, 131, 131, 131, 131, 131, 131, 131, 131, 131, - 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, - 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, - 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, - 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, - 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, - 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, - 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 55, -77, -77, -77, -77, -77, -77, -77, -77, -77, + -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, + -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, + -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, + -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, + -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, + -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, + -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, + -77, -77, -77, 196, -77, -77, -77, -77, -77, -77, - 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, - 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, - 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, - 131, 131, 131, 131, 131, 131, 131, 131 + -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, + -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, + -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, + -77, -77, -77, -77, -77, -77, -77, -77 }, { - 33, 132, 132, 132, 132, 132, 132, 132, 132, 132, - -78, 132, 132, 132, 132, 132, 132, 132, 132, 132, - 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, - 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, - 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, - 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, + 55, -78, -78, -78, -78, -78, -78, -78, -78, -78, + -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, + -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, + -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, + -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, + -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, - 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, - 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, - 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, - 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, - 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, - 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, - 132, 132, 132, 132, 132, 132, 132, 132 + -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, + -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, + -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, + -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, + -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, + -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, + -78, -78, -78, -78, -78, -78, -78, -78 }, { - 33, 133, 133, 133, 133, 133, 133, 133, 133, 133, - 134, 133, 133, 133, 133, 133, 133, 133, 133, 133, - 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, + 55, -79, -79, -79, -79, -79, -79, -79, -79, -79, + -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, + -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, - 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, - 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, - 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, - 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, - 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, - 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, - 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, - 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, - 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, - 133, 133, 133, 133, 133, 133, 133, 133 + -79, -79, 197, -79, -79, -79, -79, -79, -79, -79, + -79, -79, -79, -79, -79, -79, -79, -79, 198, 199, + 199, 199, 199, 199, 199, 199, 199, 199, -79, -79, + -79, -79, -79, -79, -79, 197, 197, 197, 197, 197, + 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, + 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, + 197, -79, -79, -79, -79, -79, -79, -79, -79, -79, + -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, + -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, + -79, -79, -79, -79, -79, -79, -79, -79 }, { - 33, -80, -80, -80, -80, -80, -80, -80, -80, -80, - -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, - -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, - -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, - -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, - -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, - -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, - -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, + 55, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, + -80, -80, 200, -80, -80, -80, -80, -80, -80, -80, + -80, -80, -80, -80, -80, -80, -80, -80, 201, 201, + 201, 201, 201, 201, 201, 201, 201, 201, -80, -80, + -80, -80, -80, -80, -80, 200, 200, 200, 200, 200, + 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, + 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, + 200, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, @@ -1779,13 +1940,13 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -81, -81, -81, -81, -81, -81, -81, -81, -81, + 55, -81, -81, -81, -81, -81, -81, -81, -81, -81, + -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, - -81, -81, -81, -81, -81, -81, -81, -81, -81, 135, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, @@ -1796,34 +1957,34 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -82, -82, -82, -82, -82, -82, -82, -82, -82, + 55, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, - -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, - -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, - -82, -82, -82, -82, -82, 136, -82, -82, -82, -82, + -82, -82, -82, -82, -82, 202, -82, -82, 203, 204, + 204, 204, 204, 204, 204, 204, 204, 204, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, + -82, -82, -82, -82, -82, 205, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82 }, { - 33, -83, -83, -83, -83, -83, -83, -83, -83, -83, + 55, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, + -83, -83, -83, -83, -83, 202, -83, -83, 206, 206, + 206, 206, 206, 206, 206, 206, 206, 206, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, - -83, -83, -83, -83, -83, -83, -83, -83, 137, -83, - -83, -83, -83, -83, -83, -83, -83, -83, -83, 138, - 139, -83, -83, -83, -83, -83, 140, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, + -83, -83, -83, -83, -83, 207, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, @@ -1831,7 +1992,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -84, -84, -84, -84, -84, -84, -84, -84, -84, + 55, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, @@ -1840,7 +2001,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, - -84, -84, -84, -84, -84, -84, -84, -84, -84, 141, + -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, @@ -1848,34 +2009,34 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -85, -85, -85, -85, -85, -85, -85, -85, -85, - -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, + 55, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, - -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, - - -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, - -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, - -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, - -85, -85, -85, -85, -85, -85, -85, -85, -85, 142, - -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, + -85, -85, 208, -85, -85, -85, -85, -85, -85, -85, + -85, -85, -85, -85, -85, -85, -85, -85, 209, 210, + + 210, 210, 210, 210, 210, 210, 210, 210, -85, -85, + -85, -85, -85, -85, -85, 208, 208, 208, 208, 208, + 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, + 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, + 208, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85 }, { - 33, -86, -86, -86, -86, -86, -86, -86, -86, -86, + 55, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, - -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, - -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, - -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, - -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, - -86, -86, -86, -86, -86, -86, -86, -86, 143, -86, - -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, - -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, + -86, -86, 211, -86, -86, -86, -86, -86, -86, -86, + -86, -86, -86, -86, -86, -86, -86, -86, 212, 212, + 212, 212, 212, 212, 212, 212, 212, 212, -86, -86, + -86, -86, -86, -86, -86, 211, 211, 211, 211, 211, + 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, + 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, + 211, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, @@ -1883,7 +2044,8 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -87, -87, -87, -87, -87, -87, -87, -87, -87, + 55, -87, -87, -87, -87, -87, -87, -87, -87, -87, + -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, @@ -1891,7 +2053,6 @@ static yyconst flex_int16_t yy_nxt[][128] = -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, - -87, -87, -87, -87, 144, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, @@ -1900,34 +2061,34 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -88, -88, -88, -88, -88, -88, -88, -88, -88, - -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, - -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, + 55, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, + -88, -88, -88, -88, -88, 213, -88, -88, 214, 215, + 215, 215, 215, 215, 215, 215, 215, 215, -88, -88, - -88, -88, -88, -88, -88, -88, -88, -88, 145, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, + -88, -88, -88, -88, -88, 216, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88 }, { - 33, -89, -89, -89, -89, -89, -89, -89, -89, -89, + 55, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, + -89, -89, -89, -89, -89, 213, -89, -89, 217, 217, + 217, 217, 217, 217, 217, 217, 217, 217, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, - -89, -89, -89, -89, -89, -89, -89, -89, -89, 146, - -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, - -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, + -89, -89, -89, -89, -89, 218, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89 @@ -1935,7 +2096,8 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -90, -90, -90, -90, -90, -90, -90, -90, -90, + 55, -90, -90, -90, -90, -90, -90, -90, -90, -90, + -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, @@ -1943,7 +2105,6 @@ static yyconst flex_int16_t yy_nxt[][128] = -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, - -90, -90, -90, -90, -90, 147, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, @@ -1952,7 +2113,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -91, -91, -91, -91, -91, -91, -91, -91, -91, + 55, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, @@ -1961,7 +2122,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, - -91, -91, -91, -91, 148, -91, -91, -91, -91, -91, + -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, @@ -1969,7 +2130,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -92, -92, -92, -92, -92, -92, -92, -92, -92, + 55, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, @@ -1977,7 +2138,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, - -92, -92, -92, -92, -92, -92, -92, -92, 149, -92, + -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, @@ -1986,14 +2147,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -93, -93, -93, -93, -93, -93, -93, -93, -93, + 55, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, - -93, -93, -93, -93, -93, -93, -93, -93, 150, -93, + -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, @@ -2004,24 +2165,24 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -94, -94, -94, -94, -94, -94, -94, -94, -94, - -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, - -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, - -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, + 55, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, - -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, - -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, - - -94, -94, -94, -94, -94, -94, -94, -94, 151, -94, - -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, + -94, -94, 219, -94, -94, -94, -94, -94, -94, -94, + -94, -94, -94, -94, -94, -94, -94, -94, 220, 220, + 220, 220, 220, 220, 220, 220, 220, 220, -94, -94, + -94, -94, -94, -94, -94, 219, 219, 219, 219, 219, + 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, + + 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, + 219, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94 }, { - 33, -95, -95, -95, -95, -95, -95, -95, -95, -95, + 55, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, @@ -2030,7 +2191,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, - -95, -95, -95, 152, -95, -95, -95, -95, -95, -95, + -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, @@ -2038,13 +2199,13 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -96, -96, -96, -96, -96, -96, -96, -96, -96, + 55, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, - -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, - -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, - -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, + -96, -96, 221, -96, -96, -96, -96, -96, -96, -96, + -96, -96, -96, -96, -96, -96, -96, -96, 222, 222, + 222, 222, 222, 222, 222, 222, 222, 222, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, @@ -2056,14 +2217,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -97, -97, -97, -97, -97, -97, -97, -97, -97, + 55, -97, -97, -97, -97, -97, -97, -97, -97, -97, + -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, - -97, -97, -97, -97, -97, -97, -97, -97, -97, 153, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, @@ -2073,7 +2234,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -98, -98, -98, -98, -98, -98, -98, -98, -98, + 55, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, @@ -2083,14 +2244,14 @@ static yyconst flex_int16_t yy_nxt[][128] = -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, - -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, + -98, -98, -98, -98, -98, 223, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98 }, { - 33, -99, -99, -99, -99, -99, -99, -99, -99, -99, + 55, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, @@ -2108,16 +2269,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -100, -100, -100, -100, -100, -100, -100, -100, -100, + 55, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, - -100, -100, -100, -100, -100, -100, -100, -100, 154, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, + -100, -100, -100, -100, -100, 224, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, @@ -2125,7 +2286,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -101, -101, -101, -101, -101, -101, -101, -101, -101, + 55, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, @@ -2134,7 +2295,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, - -101, -101, -101, 155, -101, -101, -101, -101, -101, -101, + -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, @@ -2142,15 +2303,15 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -102, -102, -102, -102, -102, -102, -102, -102, -102, - -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, + 55, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, + -102, -102, 225, -102, -102, -102, -102, -102, -102, -102, + -102, -102, -102, -102, -102, -102, -102, -102, 226, 226, + 226, 226, 226, 226, 226, 226, 226, 226, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, - -102, -102, -102, -102, -102, -102, -102, -102, -102, 156, - -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, @@ -2159,7 +2320,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -103, -103, -103, -103, -103, -103, -103, -103, -103, + 55, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, @@ -2168,7 +2329,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, - -103, -103, -103, -103, -103, -103, -103, -103, -103, 157, + -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, @@ -2177,14 +2338,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -104, -104, -104, -104, -104, -104, -104, -104, -104, + 55, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, + -104, -104, 227, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, - -104, -104, -104, -104, -104, -104, 158, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, @@ -2194,7 +2355,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -105, -105, -105, -105, -105, -105, -105, -105, -105, + 55, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, @@ -2202,7 +2363,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, - -105, -105, -105, -105, -105, -105, -105, -105, -105, 159, + -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, @@ -2211,16 +2372,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -106, -106, -106, -106, -106, -106, -106, -106, -106, + 55, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, + -106, -106, 228, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, - -106, -106, -106, 160, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, @@ -2229,14 +2390,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -107, -107, -107, -107, -107, -107, -107, -107, -107, + 55, -107, -107, -107, -107, -107, -107, -107, -107, -107, + -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, - -107, -107, -107, -107, -107, -107, -107, -107, -107, 161, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, @@ -2246,12 +2407,12 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -108, -108, -108, -108, -108, -108, -108, -108, -108, + 55, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, - -108, -108, 162, -108, -108, -108, -108, -108, -108, -108, - -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, + -108, -108, -108, -108, -108, -108, -108, -108, 229, 229, + 229, 229, 229, 229, 229, 229, 229, 229, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, @@ -2263,17 +2424,17 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -109, -109, -109, -109, -109, -109, -109, -109, -109, + 55, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, - -109, -109, 163, -109, -109, -109, -109, -109, -109, -109, - -109, -109, -109, -109, -109, -109, -109, -109, 164, 164, - 164, 164, 164, 164, 164, 164, 164, 164, -109, -109, - -109, -109, -109, -109, -109, 163, 163, 163, 163, 163, - 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, - 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, - 163, -109, -109, -109, -109, -109, -109, -109, -109, -109, + -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, + -109, -109, -109, -109, -109, -109, -109, -109, 229, 229, + 229, 229, 229, 229, 229, 229, 229, 229, -109, -109, + -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, + -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, + -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, + -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109 @@ -2281,10 +2442,10 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -110, -110, -110, -110, -110, -110, -110, -110, -110, + 55, -110, -110, -110, -110, -110, -110, -110, -110, -110, + -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, - -110, -110, 165, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, @@ -2298,12 +2459,12 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -111, -111, -111, -111, -111, -111, -111, -111, -111, - -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, + 55, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, - -111, -111, 166, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, + -111, -111, -111, -111, -111, -111, 230, -111, 231, 231, + 231, 231, 231, 231, 231, 231, 231, 231, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, @@ -2315,65 +2476,65 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -112, -112, -112, -112, -112, -112, -112, -112, -112, + 55, -112, -112, -112, -112, -112, -112, -112, -112, -112, + -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, + -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, + -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, + + -112, -112, -112, -112, -112, -112, -112, -112, 232, 232, + 232, 232, 232, 232, 232, 232, 232, 232, -112, -112, + -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, + -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, - -112, -112, 167, -112, -112, -112, -112, -112, -112, -112, - - -112, -112, -112, -112, -112, -112, -112, -112, 168, 168, - 168, 168, 168, 168, 168, 168, 168, 168, -112, -112, - -112, -112, -112, -112, -112, 167, 167, 167, 167, 167, - 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, - 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, - 167, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112 }, { - 33, -113, -113, -113, -113, -113, -113, -113, -113, -113, + 55, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, - -113, -113, -113, -113, -113, -113, -113, -113, 169, 169, - 169, 169, 169, 169, 169, 169, 169, 169, -113, -113, - -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, - -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, + -113, -113, -113, -113, -113, -113, 233, -113, 234, 234, + 234, 234, 234, 234, 234, 234, 234, 234, -113, -113, + -113, -113, -113, -113, -113, -113, -113, -113, 235, 235, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, + 235, 235, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113 }, { - 33, -114, -114, -114, -114, -114, -114, -114, -114, -114, + 55, -114, -114, -114, -114, -114, -114, -114, -114, -114, + -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, + -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, - -114, -114, -114, -114, -114, -114, -114, -114, 170, 170, - 170, 170, 170, 170, 170, 170, 170, 170, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, - -114, -114, -114, -114, -114, 171, -114, -114, -114, -114, + -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114 }, { - 33, -115, -115, -115, -115, -115, -115, -115, -115, -115, + 55, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, - -115, -115, -115, -115, -115, -115, -115, -115, -115, 172, + -115, -115, -115, -115, -115, -115, 236, -115, 237, 237, - 172, 172, 172, 172, 172, 172, 172, 172, -115, -115, + 237, 237, 237, 237, 237, 237, 237, 237, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, @@ -2384,17 +2545,17 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -116, -116, -116, -116, -116, -116, -116, -116, -116, + 55, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, - -116, -116, -116, -116, -116, -116, -116, -116, 173, 173, - 173, 173, 173, 173, 173, 173, 173, 173, -116, -116, + -116, -116, -116, -116, -116, -116, -116, -116, 238, 238, + 238, 238, 238, 238, 238, 238, 238, 238, -116, -116, + -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, - -116, -116, -116, -116, -116, 174, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, @@ -2402,27 +2563,27 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -117, -117, -117, -117, -117, -117, -117, -117, -117, + 55, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, - -117, -117, -117, -117, -117, -117, -117, -117, 175, 176, - 176, 176, 176, 176, 176, 176, 176, 176, -117, -117, - -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, + -117, -117, -117, -117, -117, -117, 239, -117, 240, 240, + 240, 240, 240, 240, 240, 240, 240, 240, -117, -117, + -117, -117, -117, -117, -117, -117, -117, -117, 241, 241, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, - -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, + 241, 241, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117 }, { - 33, -118, -118, -118, -118, -118, -118, -118, -118, -118, + 55, -118, -118, -118, -118, -118, -118, -118, -118, -118, + -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, - -118, -118, 118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, @@ -2436,30 +2597,30 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -119, -119, -119, -119, -119, -119, -119, -119, -119, - -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, - -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, + 55, 242, 242, 242, 242, 242, 242, 242, 242, 242, + 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, + 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, - -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, - -119, -119, -119, -119, -119, -119, -119, -119, 119, 119, - 119, 119, 119, 119, 119, 119, 119, 119, -119, -119, - -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, - -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, - -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, - -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, - -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, - -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, - -119, -119, -119, -119, -119, -119, -119, -119 + 242, 242, 242, 242, 242, 242, 242, 242, 242, 243, + 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, + 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, + 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, + 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, + 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, + 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, + 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, + 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, + 242, 242, 242, 242, 242, 242, 242, 242 }, { - 33, -120, -120, -120, -120, -120, -120, -120, -120, -120, + 55, -120, -120, -120, -120, -120, -120, -120, -120, -120, + -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, + -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, - -120, -120, -120, -120, -120, -120, -120, -120, 122, 122, - 122, 122, 122, 122, 122, 122, 122, 122, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, @@ -2471,82 +2632,82 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -121, -121, -121, -121, -121, -121, -121, -121, -121, - -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, - -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, - -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, - -121, -121, -121, -121, -121, -121, 123, -121, 124, 124, - 124, 124, 124, 124, 124, 124, 124, 124, -121, -121, - -121, -121, -121, -121, -121, -121, -121, -121, -121, 125, + 55, 244, 244, 244, 244, 244, 244, 244, 244, 244, + 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, + 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, + 244, 244, 244, 244, 244, 244, 244, 244, 244, 245, + 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, + 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, + 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, - -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, - -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, - -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, - -121, 125, -121, -121, -121, -121, -121, -121, -121, -121, - -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, - -121, -121, -121, -121, -121, -121, -121, -121 + 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, + 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, + 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, + 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, + 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, + 244, 244, 244, 244, 244, 244, 244, 244 }, { - 33, -122, -122, -122, -122, -122, -122, -122, -122, -122, + 55, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, - -122, -122, -122, -122, -122, -122, -122, -122, 122, 122, - 122, 122, 122, 122, 122, 122, 122, 122, -122, -122, - -122, -122, -122, -122, -122, -122, -122, -122, -122, 125, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, - -122, 125, -122, -122, -122, -122, -122, -122, -122, -122, + -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, + -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, + -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, + -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122 }, { - 33, -123, -123, -123, -123, -123, -123, -123, -123, -123, + 55, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, - -123, -123, -123, -123, -123, -123, -123, -123, 177, 177, - 177, 177, 177, 177, 177, 177, 177, 177, -123, -123, - -123, -123, -123, -123, -123, -123, -123, -123, -123, 125, - -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, - -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, - -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, - -123, 125, -123, -123, -123, -123, -123, -123, -123, -123, + -123, -123, -123, -123, -123, -123, 246, -123, 246, 246, + 246, 246, 246, 246, 246, 246, 246, 246, -123, -123, + -123, -123, -123, -123, -123, 246, 246, 246, 246, 246, + 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, + 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, + 246, -123, -123, -123, -123, 246, -123, 246, 246, 246, + 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, - -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, - -123, -123, -123, -123, -123, -123, -123, -123 + 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, + 246, 246, 246, -123, -123, -123, -123, -123 }, { - 33, -124, -124, -124, -124, -124, -124, -124, -124, -124, + 55, -124, -124, -124, -124, -124, -124, -124, -124, -124, + -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, + -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, + -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, - -124, -124, -124, -124, -124, -124, 123, -124, 124, 124, - 124, 124, 124, 124, 124, 124, 124, 124, -124, -124, - -124, -124, -124, -124, -124, -124, -124, -124, -124, 125, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, - -124, 125, -124, -124, -124, -124, -124, -124, -124, -124, + -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124 }, { - 33, -125, -125, -125, -125, -125, -125, -125, -125, -125, + 55, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, + -125, -125, 247, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, - -125, -125, -125, 178, -125, 178, -125, -125, 179, 179, - 179, 179, 179, 179, 179, 179, 179, 179, -125, -125, + -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, @@ -2557,30 +2718,30 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, 126, 126, 126, 126, 126, 126, 126, 126, 126, - 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 55, -126, -126, -126, -126, -126, -126, -126, -126, -126, + -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, - 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, - 126, 126, 126, 126, 126, 126, 126, 126, 126, 127, - 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, - 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, - 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, - 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, - 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, - 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, - 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, - 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, + -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, + -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, + -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, + -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, + -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, + -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, + -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, + -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, + -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, - 126, 126, 126, 126, 126, 126, 126, 126 + -126, -126, -126, -126, -126, -126, -126, -126 }, { - 33, -127, -127, -127, -127, -127, -127, -127, -127, -127, - -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, + 55, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, - -127, -127, -127, -127, -127, -127, -127, -127, -127, 126, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, + -127, -127, -127, -127, -127, -127, 248, -127, 249, 249, + 249, 249, 249, 249, 249, 249, 249, 249, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, @@ -2592,12 +2753,12 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -128, -128, -128, -128, -128, -128, -128, -128, -128, + 55, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, - -128, -128, 128, -128, -128, -128, -128, -128, -128, -128, - -128, -128, -128, -128, -128, -128, -128, 129, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, + -128, -128, -128, -128, -128, -128, -128, -128, 250, 250, + 250, 250, 250, 250, 250, 250, 250, 250, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, @@ -2609,128 +2770,128 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, 130, 130, 130, 130, 130, 130, 130, 130, 130, - -129, 130, 130, 130, 130, 130, 130, 130, 130, 130, - 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, + 55, -129, -129, -129, -129, -129, -129, -129, -129, -129, + -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, + -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, - 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, - 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, - 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, - 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, - 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, - 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, - 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, - 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, - 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, - 130, 130, 130, 130, 130, 130, 130, 130 + -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, + -129, -129, -129, -129, -129, -129, 251, -129, 252, 252, + 252, 252, 252, 252, 252, 252, 252, 252, -129, -129, + -129, -129, -129, -129, -129, -129, -129, -129, 253, 253, + -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, + -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, + -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, + 253, 253, -129, -129, -129, -129, -129, -129, -129, -129, + -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, + -129, -129, -129, -129, -129, -129, -129, -129 }, { - 33, 130, 130, 130, 130, 130, 130, 130, 130, 130, - -130, 130, 130, 130, 130, 130, 130, 130, 130, 130, - 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, - 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, - 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, - 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, - 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, - 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, - 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, - 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, + 55, -130, -130, -130, -130, -130, -130, -130, -130, -130, + -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, + -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, + -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, + -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, + -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, + -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, + -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, + -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, + -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, - 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, - 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, - 130, 130, 130, 130, 130, 130, 130, 130 + -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, + -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, + -130, -130, -130, -130, -130, -130, -130, -130 }, { - 33, 131, 131, 131, 131, 131, 131, 131, 131, 131, - -131, 131, 131, 131, 131, 131, 131, 131, 131, 131, - 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, - 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, - 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, - 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, - 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 55, 254, 254, 254, 254, 254, 254, 254, 254, 254, + 255, 254, 254, 254, 254, 254, 254, 254, 254, 254, + 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, + 254, 254, 256, 254, 254, 254, 254, 254, 254, 254, + 254, 254, 254, 254, 254, 254, 254, 257, 254, 254, + 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, + 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, - 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, - 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, - 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, - 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, - 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, - 131, 131, 131, 131, 131, 131, 131, 131 + 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, + 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, + 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, + 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, + 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, + 254, 254, 254, 254, 254, 254, 254, 254 }, { - 33, 132, 132, 132, 132, 132, 132, 132, 132, 132, - -132, 132, 132, 132, 132, 132, 132, 132, 132, 132, - 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, - 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, + 55, -132, -132, -132, -132, -132, -132, -132, -132, -132, + -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, + -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, + -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, - 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, - 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, - 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, - 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, - 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, - 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, - 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, - 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, - 132, 132, 132, 132, 132, 132, 132, 132 + -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, + -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, + -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, + -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, + -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, + -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, + -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, + -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, + -132, -132, -132, -132, -132, -132, -132, -132 }, { - 33, 133, 133, 133, 133, 133, 133, 133, 133, 133, + 55, 258, 258, 258, 258, 258, 258, 258, 258, 258, - 134, 133, 133, 133, 133, 133, 133, 133, 133, 133, - 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, - 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, - 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, - 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, - 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, - 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, - 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, - 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, - 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, + 259, 258, 258, 258, 258, 258, 258, 258, 258, 258, + 258, 258, 258, 258, 258, 258, 258, 258, 258, 258, + 258, 258, 260, 258, 258, 258, 258, 258, 258, 258, + 258, 258, 258, 258, 258, 258, 258, 261, 258, 258, + 258, 258, 258, 258, 258, 258, 258, 258, 258, 258, + 258, 258, 258, 258, 258, 258, 258, 258, 258, 258, + 258, 258, 258, 258, 258, 258, 258, 258, 258, 258, + 258, 258, 258, 258, 258, 258, 258, 258, 258, 258, + 258, 258, 258, 258, 258, 258, 258, 258, 258, 258, + 258, 258, 258, 258, 258, 258, 258, 258, 258, 258, - 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, - 133, 133, 133, 133, 133, 133, 133, 133 + 258, 258, 258, 258, 258, 258, 258, 258, 258, 258, + 258, 258, 258, 258, 258, 258, 258, 258 }, { - 33, -134, -134, -134, -134, -134, -134, -134, -134, -134, - -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, - -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, - -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, - -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, - -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, - -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, - -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, + 55, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 263, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 264, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 265, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, - -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, - -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, - -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, - -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, - -134, -134, -134, -134, -134, -134, -134, -134 + 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 262 }, { - 33, -135, -135, -135, -135, -135, -135, -135, -135, -135, - -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, - -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, - -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, - -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, + 55, 266, 266, 266, 266, 266, 266, 266, 266, 266, + 267, 266, 266, 266, 266, 266, 266, 266, 266, 266, + 266, 266, 266, 266, 266, 266, 266, 266, 266, 266, + 266, 266, 266, 266, 266, 266, 266, 266, 266, 266, + 266, 266, 266, 266, 266, 266, 266, 266, 266, 266, - -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, - -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, - -135, -135, -135, -135, -135, -135, 180, -135, -135, -135, - -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, - -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, - -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, - -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, - -135, -135, -135, -135, -135, -135, -135, -135 + 266, 266, 266, 266, 266, 266, 266, 266, 266, 266, + 266, 266, 266, 266, 266, 266, 266, 266, 266, 266, + 266, 266, 266, 266, 266, 266, 266, 266, 266, 266, + 266, 266, 266, 266, 266, 266, 266, 266, 266, 266, + 266, 266, 266, 266, 266, 266, 266, 266, 266, 266, + 266, 266, 266, 266, 266, 266, 266, 266, 266, 266, + 266, 266, 266, 266, 266, 266, 266, 266, 266, 266, + 266, 266, 266, 266, 266, 266, 266, 266 }, { - 33, -136, -136, -136, -136, -136, -136, -136, -136, -136, + 55, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, @@ -2738,7 +2899,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, - -136, -136, -136, -136, -136, -136, -136, 181, -136, -136, + -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, @@ -2748,24 +2909,24 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -137, -137, -137, -137, -137, -137, -137, -137, -137, - -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, - -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, - -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, - -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, - -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, - -137, -137, -137, -137, -137, -137, -137, -137, -137, 182, - -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, - -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, + 55, 268, 268, 268, 268, 268, 268, 268, 268, 268, + 269, 268, 268, 268, 268, 268, 268, 268, 268, 268, + 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, + 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, + 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, + 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, + 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, + 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, + 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, - -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, - -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, - -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, - -137, -137, -137, -137, -137, -137, -137, -137 + 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, + 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, + 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, + 268, 268, 268, 268, 268, 268, 268, 268 }, { - 33, -138, -138, -138, -138, -138, -138, -138, -138, -138, + 55, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, @@ -2774,7 +2935,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, - -138, -138, -138, -138, 183, -138, -138, -138, -138, -138, + -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, @@ -2782,31 +2943,31 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -139, -139, -139, -139, -139, -139, -139, -139, -139, - -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, - -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, + 55, 270, 270, 270, 270, 270, 270, 270, 270, 270, + 271, 270, 270, 270, 270, 270, 270, 270, 270, 270, + 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, - -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, - -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, - -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, - -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, - -139, -139, -139, 184, -139, -139, -139, -139, -139, -139, - -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, - -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, - -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, - -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, - -139, -139, -139, -139, -139, -139, -139, -139 + 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, + 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, + 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, + 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, + 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, + 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, + 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, + 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, + 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, + 270, 270, 270, 270, 270, 270, 270, 270 }, { - 33, -140, -140, -140, -140, -140, -140, -140, -140, -140, + 55, -140, -140, -140, -140, -140, -140, -140, -140, -140, + -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, - -140, -140, -140, -140, -140, 185, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, @@ -2817,13 +2978,13 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -141, -141, -141, -141, -141, -141, -141, -141, -141, + 55, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, - -141, -141, -141, -141, -141, -141, -141, -141, -141, 186, + -141, -141, -141, -141, -141, -141, -141, -141, 272, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, @@ -2834,7 +2995,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -142, -142, -142, -142, -142, -142, -142, -142, -142, + 55, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, @@ -2843,24 +3004,24 @@ static yyconst flex_int16_t yy_nxt[][128] = -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, - 187, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, + -142, -142, -142, -142, -142, 273, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142 }, { - 33, -143, -143, -143, -143, -143, -143, -143, -143, -143, + 55, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, - -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, - -143, -143, -143, 188, -143, -143, -143, -143, -143, -143, - -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, + -143, -143, -143, -143, -143, -143, -143, -143, 274, -143, + -143, -143, -143, -143, -143, -143, -143, -143, -143, 275, + -143, -143, 276, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, @@ -2869,14 +3030,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -144, -144, -144, -144, -144, -144, -144, -144, -144, + 55, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, - -144, -144, -144, -144, -144, -144, -144, -144, -144, 189, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, + -144, -144, -144, 277, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, @@ -2886,16 +3047,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -145, -145, -145, -145, -145, -145, -145, -145, -145, + 55, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, - -145, -145, 190, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, - -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, + -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, + 278, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, @@ -2903,15 +3064,15 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -146, -146, -146, -146, -146, -146, -146, -146, -146, + 55, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, - -146, -146, -146, -146, -146, -146, -146, 191, -146, -146, - -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, + -146, -146, -146, -146, -146, 279, -146, -146, -146, -146, + -146, -146, -146, -146, -146, -146, -146, -146, -146, 280, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, @@ -2921,33 +3082,33 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -147, -147, -147, -147, -147, -147, -147, -147, -147, + 55, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, - -147, -147, -147, 192, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, - -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, + + -147, -147, -147, -147, -147, 281, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147 }, { - 33, -148, -148, -148, -148, -148, -148, -148, -148, -148, + 55, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, - -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, - -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, - 193, -148, -148, -148, -148, -148, -148, -148, -148, -148, + -148, -148, -148, -148, -148, -148, -148, -148, 282, -148, + -148, -148, -148, -148, -148, -148, -148, -148, -148, 283, + -148, -148, 284, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, @@ -2955,16 +3116,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -149, -149, -149, -149, -149, -149, -149, -149, -149, + 55, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, + -149, -149, -149, -149, -149, -149, -149, -149, -149, 285, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, - 194, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, @@ -2973,15 +3134,15 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -150, -150, -150, -150, -150, -150, -150, -150, -150, - -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, + 55, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, - -150, -150, -150, -150, -150, 195, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, + -150, -150, -150, -150, -150, 286, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, + 287, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, @@ -2990,15 +3151,15 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -151, -151, -151, -151, -151, -151, -151, -151, -151, - -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, + 55, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, + -151, -151, -151, -151, -151, -151, -151, -151, 288, 289, - -151, -151, -151, 196, -151, -151, -151, -151, -151, -151, + -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, @@ -3007,15 +3168,15 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -152, -152, -152, -152, -152, -152, -152, -152, -152, + 55, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, + -152, -152, -152, -152, -152, -152, -152, -152, 290, 291, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, - -152, 197, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, @@ -3024,16 +3185,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -153, -153, -153, -153, -153, -153, -153, -153, -153, + 55, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, - -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, - -153, -153, -153, -153, 198, -153, -153, -153, -153, -153, - -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, + -153, -153, -153, -153, -153, -153, -153, -153, 292, -153, + -153, -153, -153, -153, -153, -153, 293, -153, -153, 294, + 295, -153, -153, -153, -153, -153, 296, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, @@ -3042,16 +3203,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -154, -154, -154, -154, -154, -154, -154, -154, -154, + 55, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, - -154, -154, -154, -154, -154, -154, -154, -154, -154, 199, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, - -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, + + -154, -154, -154, -154, -154, -154, -154, -154, -154, 297, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, @@ -3059,7 +3220,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -155, -155, -155, -155, -155, -155, -155, -155, -155, + 55, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, @@ -3068,7 +3229,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, - -155, -155, -155, -155, 200, -155, -155, -155, -155, -155, + -155, -155, -155, -155, -155, -155, -155, -155, -155, 298, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, @@ -3076,15 +3237,15 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -156, -156, -156, -156, -156, -156, -156, -156, -156, + 55, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, - -156, -156, -156, -156, -156, -156, -156, 201, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, + -156, -156, -156, -156, -156, -156, -156, -156, 299, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, @@ -3094,7 +3255,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -157, -157, -157, -157, -157, -157, -157, -157, -157, + 55, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, @@ -3102,7 +3263,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, - -157, -157, -157, 202, -157, -157, -157, -157, -157, -157, + 300, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, @@ -3111,16 +3272,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -158, -158, -158, -158, -158, -158, -158, -158, -158, + 55, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, - -158, -158, -158, -158, -158, 203, -158, -158, -158, -158, - -158, -158, -158, -158, -158, -158, -158, -158, -158, 204, - -158, -158, 205, -158, -158, -158, -158, -158, -158, -158, + -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, + -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, + -158, -158, 301, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, @@ -3128,7 +3289,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -159, -159, -159, -159, -159, -159, -159, -159, -159, + 55, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, @@ -3137,7 +3298,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, - -159, -159, -159, -159, -159, 206, -159, -159, -159, -159, + -159, -159, -159, -159, 302, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, @@ -3146,14 +3307,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -160, -160, -160, -160, -160, -160, -160, -160, -160, + 55, -160, -160, -160, -160, -160, -160, -160, -160, -160, + -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, + -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, - -160, -160, -160, -160, -160, 207, -160, -160, -160, -160, - -160, -160, -160, -160, -160, -160, -160, -160, 208, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, @@ -3163,7 +3324,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -161, -161, -161, -161, -161, -161, -161, -161, -161, + 55, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, @@ -3172,7 +3333,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, - -161, -161, -161, -161, -161, 209, -161, -161, -161, -161, + -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, @@ -3180,7 +3341,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -162, -162, -162, -162, -162, -162, -162, -162, -162, + 55, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, @@ -3189,7 +3350,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, - -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, + -162, -162, -162, -162, -162, 303, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, @@ -3197,14 +3358,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -163, -163, -163, -163, -163, -163, -163, -163, -163, + 55, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, - -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, + -163, -163, -163, -163, -163, -163, -163, -163, -163, 304, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, @@ -3215,13 +3376,13 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -164, -164, -164, -164, -164, -164, -164, -164, -164, - -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, + 55, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, + -164, -164, -164, -164, -164, -164, -164, -164, 305, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, @@ -3232,7 +3393,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -165, -165, -165, -165, -165, -165, -165, -165, -165, + 55, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, @@ -3240,7 +3401,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, - -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, + -165, -165, -165, -165, -165, -165, -165, -165, -165, 306, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, @@ -3249,7 +3410,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -166, -166, -166, -166, -166, -166, -166, -166, -166, + 55, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, @@ -3258,7 +3419,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, - -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, + -166, -166, -166, -166, -166, 307, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, @@ -3267,14 +3428,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -167, -167, -167, -167, -167, -167, -167, -167, -167, - -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, + 55, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, + -167, -167, -167, -167, -167, -167, 308, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, @@ -3284,16 +3445,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -168, -168, -168, -168, -168, -168, -168, -168, -168, - -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, + 55, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, + -168, -168, -168, -168, -168, 309, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, - -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, + -168, -168, 310, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, @@ -3301,16 +3462,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -169, -169, -169, -169, -169, -169, -169, -169, -169, + 55, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, - -169, -169, -169, -169, -169, -169, -169, -169, 210, 210, - 210, 210, 210, 210, 210, 210, 210, 210, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, + -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, + 311, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, @@ -3319,16 +3480,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -170, -170, -170, -170, -170, -170, -170, -170, -170, + 55, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, - -170, -170, -170, -170, -170, -170, -170, -170, 211, 211, - 211, 211, 211, 211, 211, 211, 211, 211, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, - -170, -170, -170, -170, -170, 212, -170, -170, -170, -170, + -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, + -170, -170, -170, -170, 312, -170, -170, -170, -170, -170, + -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, @@ -3336,15 +3497,15 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -171, -171, -171, -171, -171, -171, -171, -171, -171, + 55, -171, -171, -171, -171, -171, -171, -171, -171, -171, + -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, - -171, -171, -171, -171, -171, -171, -171, -171, -171, 213, - 213, 213, 213, 213, 213, 213, 213, 213, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, - -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, + + -171, -171, -171, -171, -171, -171, -171, -171, 313, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, @@ -3353,34 +3514,34 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -172, -172, -172, -172, -172, -172, -172, -172, -172, + 55, -172, -172, -172, -172, -172, -172, -172, -172, -172, + -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, + -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, + -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, + + -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, + -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, + -172, -172, -172, -172, -172, -172, -172, -172, 314, -172, + -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, - -172, -172, 214, -172, -172, -172, -172, -172, -172, -172, - - -172, -172, -172, -172, -172, -172, -172, -172, 215, 215, - 215, 215, 215, 215, 215, 215, 215, 215, -172, -172, - -172, -172, -172, -172, -172, 214, 214, 214, 214, 214, - 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, - 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, - 214, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172 }, { - 33, -173, -173, -173, -173, -173, -173, -173, -173, -173, + 55, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, - -173, -173, -173, -173, -173, -173, -173, -173, 216, 216, - 216, 216, 216, 216, 216, 216, 216, 216, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, - -173, -173, -173, -173, -173, 217, -173, -173, -173, -173, + -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, + -173, -173, -173, -173, -173, -173, -173, -173, 315, -173, + -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, @@ -3388,16 +3549,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -174, -174, -174, -174, -174, -174, -174, -174, -174, + 55, -174, -174, -174, -174, -174, -174, -174, -174, -174, + -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, - -174, -174, -174, -174, -174, -174, -174, -174, 218, 219, - 219, 219, 219, 219, 219, 219, 219, 219, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, - -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, + + -174, -174, -174, 316, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, @@ -3405,34 +3566,34 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -175, -175, -175, -175, -175, -175, -175, -175, -175, + 55, -175, -175, -175, -175, -175, -175, -175, -175, -175, + -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, - -175, -175, 220, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, - -175, -175, -175, -175, -175, 220, 220, 220, 220, 220, - 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, - 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, - 220, -175, -175, -175, -175, -175, -175, -175, -175, -175, + -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, + -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, + -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, + -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175 }, { - 33, -176, -176, -176, -176, -176, -176, -176, -176, -176, + 55, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, - -176, -176, 220, -176, -176, -176, -176, -176, -176, -176, - -176, -176, -176, -176, -176, -176, -176, -176, 221, 221, - 221, 221, 221, 221, 221, 221, 221, 221, -176, -176, - -176, -176, -176, -176, -176, 220, 220, 220, 220, 220, - 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, - 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, - 220, -176, -176, -176, -176, -176, -176, -176, -176, -176, + -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, + -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, + -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, + -176, -176, -176, -176, -176, 317, -176, -176, -176, 318, + -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, + -176, -176, -176, -176, 319, -176, -176, -176, -176, -176, + -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, @@ -3440,32 +3601,32 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -177, -177, -177, -177, -177, -177, -177, -177, -177, + 55, -177, -177, -177, -177, -177, -177, -177, -177, -177, + -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, - -177, -177, -177, -177, -177, -177, -177, -177, 177, 177, - 177, 177, 177, 177, 177, 177, 177, 177, -177, -177, - -177, -177, -177, -177, -177, -177, -177, -177, -177, 125, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, + -177, -177, -177, -177, -177, -177, -177, -177, -177, 320, + -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, - -177, 125, -177, -177, -177, -177, -177, -177, -177, -177, + -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177 }, { - 33, -178, -178, -178, -178, -178, -178, -178, -178, -178, + 55, -178, -178, -178, -178, -178, -178, -178, -178, -178, + -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, - -178, -178, -178, -178, -178, -178, -178, -178, 179, 179, - 179, 179, 179, 179, 179, 179, 179, 179, -178, -178, - -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, + -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, + -178, -178, -178, -178, -178, -178, -178, -178, -178, 321, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, @@ -3474,13 +3635,13 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -179, -179, -179, -179, -179, -179, -179, -179, -179, + 55, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, - -179, -179, -179, -179, -179, -179, -179, -179, 179, 179, - 179, 179, 179, 179, 179, 179, 179, 179, -179, -179, + -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, + -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, @@ -3492,7 +3653,8 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -180, -180, -180, -180, -180, -180, -180, -180, -180, + 55, -180, -180, -180, -180, -180, -180, -180, -180, -180, + -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, @@ -3500,7 +3662,6 @@ static yyconst flex_int16_t yy_nxt[][128] = -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, - -180, -180, -180, -180, 222, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, @@ -3509,13 +3670,13 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -181, -181, -181, -181, -181, -181, -181, -181, -181, + 55, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, - -181, -181, -181, -181, -181, -181, -181, -181, -181, 223, + -181, -181, -181, -181, -181, -181, -181, -181, 322, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, @@ -3526,7 +3687,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -182, -182, -182, -182, -182, -182, -182, -182, -182, + 55, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, @@ -3535,7 +3696,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, - -182, -182, 224, -182, -182, -182, -182, -182, -182, -182, + -182, -182, -182, 323, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, @@ -3543,16 +3704,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -183, -183, -183, -183, -183, -183, -183, -183, -183, + 55, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, - -183, -183, -183, -183, -183, 225, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, + -183, -183, -183, -183, -183, 324, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, @@ -3561,16 +3722,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -184, -184, -184, -184, -184, -184, -184, -184, -184, - -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, + 55, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, + -184, -184, -184, -184, -184, -184, -184, -184, -184, 325, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, - -184, -184, -184, -184, -184, -184, -184, -184, 226, -184, + -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, @@ -3578,7 +3739,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -185, -185, -185, -185, -185, -185, -185, -185, -185, + 55, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, @@ -3586,8 +3747,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, - -185, -185, -185, -185, -185, -185, 227, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, + -185, -185, -185, -185, -185, -185, -185, -185, -185, 326, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, @@ -3595,7 +3756,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -186, -186, -186, -186, -186, -186, -186, -186, -186, + 55, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, @@ -3603,8 +3764,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, + -186, -186, -186, -186, -186, -186, 327, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, - -186, -186, 228, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, @@ -3613,14 +3774,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -187, -187, -187, -187, -187, -187, -187, -187, -187, + 55, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, - -187, -187, -187, -187, -187, -187, -187, -187, -187, 229, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, + -187, -187, -187, -187, -187, -187, -187, 328, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, @@ -3630,16 +3791,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -188, -188, -188, -188, -188, -188, -188, -188, -188, + 55, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, + -188, -188, -188, -188, -188, -188, -188, -188, -188, 329, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, - -188, -188, -188, -188, 230, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, @@ -3647,16 +3808,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -189, -189, -189, -189, -189, -189, -189, -189, -189, + 55, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, - -189, -189, -189, -189, -189, 231, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, + -189, -189, -189, -189, 330, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, @@ -3665,14 +3826,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -190, -190, -190, -190, -190, -190, -190, -190, -190, + 55, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, - -190, -190, 232, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, + -190, -190, -190, -190, -190, -190, 331, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, @@ -3682,7 +3843,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -191, -191, -191, -191, -191, -191, -191, -191, -191, + 55, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, @@ -3690,7 +3851,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, - -191, -191, 233, -191, -191, -191, -191, -191, -191, -191, + -191, -191, -191, -191, -191, -191, -191, -191, -191, 332, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, @@ -3699,7 +3860,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -192, -192, -192, -192, -192, -192, -192, -192, -192, + 55, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, @@ -3707,8 +3868,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, - -192, -192, -192, -192, -192, -192, -192, -192, 234, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, + -192, -192, -192, -192, 333, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, @@ -3716,7 +3877,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -193, -193, -193, -193, -193, -193, -193, -193, -193, + 55, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, @@ -3724,8 +3885,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, - -193, -193, -193, -193, -193, -193, -193, -193, -193, 235, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, + -193, -193, -193, 334, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, -193, @@ -3734,14 +3895,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -194, -194, -194, -194, -194, -194, -194, -194, -194, + 55, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, - -194, -194, -194, -194, -194, -194, -194, -194, -194, 236, + -194, -194, -194, 335, -194, -194, -194, -194, -194, 336, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, -194, @@ -3751,15 +3912,15 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -195, -195, -195, -195, -195, -195, -195, -195, -195, + 55, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, - -195, -195, -195, -195, -195, 237, -195, -195, -195, -195, - -195, -195, -195, -195, -195, -195, -195, -195, -195, 238, + -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, + -195, -195, -195, 337, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, @@ -3768,7 +3929,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -196, -196, -196, -196, -196, -196, -196, -196, -196, + 55, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, @@ -3776,8 +3937,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, + -196, -196, -196, -196, -196, -196, -196, -196, -196, 338, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, - -196, -196, -196, 239, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, -196, @@ -3786,13 +3947,13 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -197, -197, -197, -197, -197, -197, -197, -197, -197, + 55, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, + -197, -197, 339, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, - -197, -197, -197, -197, -197, -197, -197, -197, -197, 240, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, @@ -3803,34 +3964,34 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -198, -198, -198, -198, -198, -198, -198, -198, -198, - -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, + 55, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, - -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, - -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, - - -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, - -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, - 241, -198, -198, -198, -198, -198, -198, -198, -198, -198, - -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, + -198, -198, 340, -198, -198, -198, -198, -198, -198, -198, + -198, -198, -198, -198, -198, -198, -198, -198, 341, 342, + 342, 342, 342, 342, 342, 342, 342, 342, -198, -198, + + -198, -198, -198, -198, -198, 340, 340, 340, 340, 340, + 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, + 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, + 340, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198 }, { - 33, -199, -199, -199, -199, -199, -199, -199, -199, -199, + 55, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, - -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, - -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, - -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, - -199, -199, -199, -199, -199, -199, -199, 242, -199, -199, - -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, - -199, -199, -199, 243, -199, -199, -199, -199, -199, -199, - -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, + -199, -199, 343, -199, -199, -199, -199, -199, -199, -199, + -199, -199, -199, -199, -199, -199, -199, -199, 341, 342, + 342, 342, 342, 342, 342, 342, 342, 342, -199, -199, + -199, -199, -199, -199, -199, 343, 343, 343, 343, 343, + 343, 343, 343, 343, 343, 343, 343, 343, 343, 343, + 343, 343, 343, 343, 343, 343, 343, 343, 343, 343, + 343, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199 @@ -3838,15 +3999,15 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -200, -200, -200, -200, -200, -200, -200, -200, -200, + 55, -200, -200, -200, -200, -200, -200, -200, -200, -200, + -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, + -200, -200, 344, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, - 244, -200, -200, -200, -200, -200, -200, -200, -200, -200, - -200, -200, -200, -200, -200, -200, -200, 245, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, @@ -3855,33 +4016,33 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -201, -201, -201, -201, -201, -201, -201, -201, -201, - -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, - -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, - -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, - -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, + 55, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, - - -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, - -201, -201, -201, 246, -201, -201, -201, -201, -201, -201, - -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, + -201, -201, 345, -201, -201, -201, -201, -201, -201, -201, + -201, -201, -201, -201, -201, -201, -201, -201, 346, 346, + 346, 346, 346, 346, 346, 346, 346, 346, -201, -201, + -201, -201, -201, -201, -201, 345, 345, 345, 345, 345, + + 345, 345, 345, 345, 345, 345, 345, 345, 345, 345, + 345, 345, 345, 345, 345, 345, 345, 345, 345, 345, + 345, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201 }, { - 33, -202, -202, -202, -202, -202, -202, -202, -202, -202, + 55, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, + -202, -202, -202, -202, -202, -202, -202, -202, 347, 347, + 347, 347, 347, 347, 347, 347, 347, 347, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, - -202, -202, -202, -202, -202, -202, -202, -202, -202, 247, - -202, -202, -202, 248, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, @@ -3889,17 +4050,17 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -203, -203, -203, -203, -203, -203, -203, -203, -203, + 55, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, + -203, -203, -203, -203, -203, 348, -203, -203, 349, 350, + 350, 350, 350, 350, 350, 350, 350, 350, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, - -203, -203, -203, -203, -203, -203, -203, -203, 249, -203, - -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, - -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, + -203, -203, -203, -203, -203, 351, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, @@ -3907,31 +4068,31 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -204, -204, -204, -204, -204, -204, -204, -204, -204, - -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, - -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, + 55, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, + -204, -204, -204, -204, -204, 348, -204, -204, 352, 353, + 353, 353, 353, 353, 353, 353, 353, 353, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, - -204, -204, -204, 250, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, + -204, -204, -204, -204, -204, 354, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204 }, { - 33, -205, -205, -205, -205, -205, -205, -205, -205, -205, - -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, + 55, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, + -205, -205, -205, -205, -205, -205, -205, -205, 355, 355, + 355, 355, 355, 355, 355, 355, 355, 355, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, - -205, -205, -205, -205, -205, -205, -205, -205, -205, 251, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, @@ -3941,17 +4102,17 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -206, -206, -206, -206, -206, -206, -206, -206, -206, + 55, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, + -206, -206, -206, -206, -206, 348, -206, -206, 356, 357, + 357, 357, 357, 357, 357, 357, 357, 357, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, - -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, - -206, -206, 252, -206, -206, -206, -206, -206, -206, -206, - -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, + -206, -206, -206, -206, -206, 358, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, @@ -3959,15 +4120,15 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -207, -207, -207, -207, -207, -207, -207, -207, -207, + 55, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, + -207, -207, -207, -207, -207, -207, -207, -207, 359, 360, + 360, 360, 360, 360, 360, 360, 360, 360, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, - -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, - -207, -207, -207, -207, -207, -207, -207, -207, 253, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, @@ -3976,14 +4137,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -208, -208, -208, -208, -208, -208, -208, -208, -208, - -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, + 55, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, + -208, -208, 361, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, - -208, -208, -208, -208, -208, 254, -208, -208, -208, -208, + -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, @@ -3993,17 +4154,17 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -209, -209, -209, -209, -209, -209, -209, -209, -209, + 55, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, - -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, - -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, - -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, - -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, - -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, - -209, -209, 255, -209, -209, -209, -209, -209, -209, -209, - -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, + -209, -209, 362, -209, -209, -209, -209, -209, -209, -209, + -209, -209, -209, -209, -209, -209, -209, -209, 363, 364, + 364, 364, 364, 364, 364, 364, 364, 364, -209, -209, + -209, -209, -209, -209, -209, 362, 362, 362, 362, 362, + 362, 362, 362, 362, 362, 362, 362, 362, 362, 362, + 362, 362, 362, 362, 362, 362, 362, 362, 362, 362, + 362, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, -209 @@ -4011,16 +4172,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -210, -210, -210, -210, -210, -210, -210, -210, -210, - -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, - -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, - -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, - -210, -210, -210, -210, -210, -210, -210, -210, 256, 256, - 256, 256, 256, 256, 256, 256, 256, 256, -210, -210, - -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, - -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, + 55, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, + -210, -210, 365, -210, -210, -210, -210, -210, -210, -210, + -210, -210, -210, -210, -210, -210, -210, -210, 363, 364, + 364, 364, 364, 364, 364, 364, 364, 364, -210, -210, + -210, -210, -210, -210, -210, 365, 365, 365, 365, 365, + 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, + 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, + 365, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, @@ -4028,51 +4189,51 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -211, -211, -211, -211, -211, -211, -211, -211, -211, + 55, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, + -211, -211, 366, -211, -211, -211, -211, -211, -211, -211, + -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, - -211, -211, -211, -211, -211, -211, -211, -211, 256, 256, - 256, 256, 256, 256, 256, 256, 256, 256, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, - -211, -211, -211, -211, -211, 257, -211, -211, -211, -211, + -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211 }, { - 33, -212, -212, -212, -212, -212, -212, -212, -212, -212, - -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, - -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, - -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, - - -212, -212, -212, -212, -212, -212, -212, -212, -212, 258, - 258, 258, 258, 258, 258, 258, 258, 258, -212, -212, - -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, - -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, + 55, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, + -212, -212, 367, -212, -212, -212, -212, -212, -212, -212, + + -212, -212, -212, -212, -212, -212, -212, -212, 363, 363, + 363, 363, 363, 363, 363, 363, 363, 363, -212, -212, + -212, -212, -212, -212, -212, 367, 367, 367, 367, 367, + 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, + 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, + 367, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212 }, { - 33, -213, -213, -213, -213, -213, -213, -213, -213, -213, + 55, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, - -213, -213, 259, -213, -213, -213, -213, -213, -213, -213, - -213, -213, -213, -213, -213, -213, -213, -213, 260, 260, - 260, 260, 260, 260, 260, 260, 260, 260, -213, -213, - -213, -213, -213, -213, -213, 259, 259, 259, 259, 259, - 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, - 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, - 259, -213, -213, -213, -213, -213, -213, -213, -213, -213, + -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, + -213, -213, -213, -213, -213, -213, -213, -213, 368, 368, + 368, 368, 368, 368, 368, 368, 368, 368, -213, -213, + -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, + -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, + -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, + -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, @@ -4080,103 +4241,103 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -214, -214, -214, -214, -214, -214, -214, -214, -214, - -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, + 55, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, - -214, -214, 261, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, + -214, -214, -214, -214, -214, 369, -214, -214, 370, 371, + 371, 371, 371, 371, 371, 371, 371, 371, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, - -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, + -214, -214, -214, -214, -214, 372, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214 }, { - 33, -215, -215, -215, -215, -215, -215, -215, -215, -215, + 55, -215, -215, -215, -215, -215, -215, -215, -215, -215, + -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, - -215, -215, 262, -215, -215, -215, -215, -215, -215, -215, - -215, -215, -215, -215, -215, -215, -215, -215, 263, 263, + -215, -215, -215, -215, -215, 369, -215, -215, 373, 373, - 263, 263, 263, 263, 263, 263, 263, 263, -215, -215, - -215, -215, -215, -215, -215, 262, 262, 262, 262, 262, - 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, - 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, - 262, -215, -215, -215, -215, -215, -215, -215, -215, -215, + 373, 373, 373, 373, 373, 373, 373, 373, -215, -215, + -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, + -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, + -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, + -215, -215, -215, -215, -215, 374, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215 }, { - 33, -216, -216, -216, -216, -216, -216, -216, -216, -216, + 55, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, + -216, -216, -216, -216, -216, -216, -216, -216, 375, 375, + 375, 375, 375, 375, 375, 375, 375, 375, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, - -216, -216, -216, -216, -216, 264, -216, -216, -216, -216, - -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216 }, { - 33, -217, -217, -217, -217, -217, -217, -217, -217, -217, + 55, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, - -217, -217, -217, -217, -217, -217, -217, -217, 265, 266, - 266, 266, 266, 266, 266, 266, 266, 266, -217, -217, + -217, -217, -217, -217, -217, 369, -217, -217, 376, 376, + 376, 376, 376, 376, 376, 376, 376, 376, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, - -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, + -217, -217, -217, -217, -217, 377, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217 }, { - 33, -218, -218, -218, -218, -218, -218, -218, -218, -218, - -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, + 55, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, - -218, -218, 267, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, + -218, -218, -218, -218, -218, -218, -218, -218, 378, 379, + 379, 379, 379, 379, 379, 379, 379, 379, -218, -218, - -218, -218, -218, -218, -218, 267, 267, 267, 267, 267, - 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, - 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, - 267, -218, -218, -218, -218, -218, -218, -218, -218, -218, + -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, + -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, + -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, + -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218 }, { - 33, -219, -219, -219, -219, -219, -219, -219, -219, -219, + 55, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, - -219, -219, 267, -219, -219, -219, -219, -219, -219, -219, - -219, -219, -219, -219, -219, -219, -219, -219, 268, 268, - 268, 268, 268, 268, 268, 268, 268, 268, -219, -219, - -219, -219, -219, -219, -219, 267, 267, 267, 267, 267, - 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, - 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, - 267, -219, -219, -219, -219, -219, -219, -219, -219, -219, + -219, -219, 380, -219, -219, -219, -219, -219, -219, -219, + -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, + -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, + -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, + -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, + -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, + -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219 @@ -4184,16 +4345,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -220, -220, -220, -220, -220, -220, -220, -220, -220, - -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, - -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, - -220, -220, 269, -220, -220, -220, -220, -220, -220, -220, - -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, - -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, - -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, - -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, + 55, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, + -220, -220, 381, -220, -220, -220, -220, -220, -220, -220, + -220, -220, -220, -220, -220, -220, -220, -220, 382, 382, + 382, 382, 382, 382, 382, 382, 382, 382, -220, -220, + -220, -220, -220, -220, -220, 381, 381, 381, 381, 381, + 381, 381, 381, 381, 381, 381, 381, 381, 381, 381, + 381, 381, 381, 381, 381, 381, 381, 381, 381, 381, + 381, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, @@ -4201,30 +4362,30 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -221, -221, -221, -221, -221, -221, -221, -221, -221, + 55, -221, -221, -221, -221, -221, -221, -221, -221, -221, + -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, + -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, + -221, -221, 383, -221, -221, -221, -221, -221, -221, -221, + -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, - -221, -221, 270, -221, -221, -221, -221, -221, -221, -221, - -221, -221, -221, -221, -221, -221, -221, -221, 271, 271, - 271, 271, 271, 271, 271, 271, 271, 271, -221, -221, - -221, -221, -221, -221, -221, 270, 270, 270, 270, 270, - 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, - 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, - 270, -221, -221, -221, -221, -221, -221, -221, -221, -221, + -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, + -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, + -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221 }, { - 33, -222, -222, -222, -222, -222, -222, -222, -222, -222, - -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, + 55, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, + -222, -222, 384, -222, -222, -222, -222, -222, -222, -222, - -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, - -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, + -222, -222, -222, -222, -222, -222, -222, -222, 385, 385, + 385, 385, 385, 385, 385, 385, 385, 385, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, @@ -4235,13 +4396,13 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -223, -223, -223, -223, -223, -223, -223, -223, -223, + 55, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, - -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, - -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, + -223, -223, -223, -223, -223, -223, -223, -223, 386, 386, + 386, 386, 386, 386, 386, 386, 386, 386, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, @@ -4253,12 +4414,12 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -224, -224, -224, -224, -224, -224, -224, -224, -224, - -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, - -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, + 55, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, + -224, -224, -224, -224, -224, -224, -224, -224, 387, 387, + 387, 387, 387, 387, 387, 387, 387, 387, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, @@ -4270,10 +4431,10 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -225, -225, -225, -225, -225, -225, -225, -225, -225, - -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, + 55, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, + -225, -225, 388, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, @@ -4287,11 +4448,11 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -226, -226, -226, -226, -226, -226, -226, -226, -226, + 55, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, - -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, + -226, -226, 389, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, @@ -4305,10 +4466,10 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -227, -227, -227, -227, -227, -227, -227, -227, -227, - -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, + 55, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, + -227, -227, 227, -227, -227, -227, -227, -227, -227, 390, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, @@ -4322,10 +4483,10 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -228, -228, -228, -228, -228, -228, -228, -228, -228, - -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, + 55, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, + -228, -228, 228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, @@ -4339,13 +4500,13 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -229, -229, -229, -229, -229, -229, -229, -229, -229, + 55, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, - -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, - -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, + -229, -229, -229, -229, -229, -229, -229, -229, 229, 229, + 229, 229, 229, 229, 229, 229, 229, 229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, @@ -4357,12 +4518,12 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -230, -230, -230, -230, -230, -230, -230, -230, -230, - -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, - -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, + 55, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, + -230, -230, -230, -230, -230, -230, -230, -230, 232, 232, + 232, 232, 232, 232, 232, 232, 232, 232, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, @@ -4374,84 +4535,84 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -231, -231, -231, -231, -231, -231, -231, -231, -231, - -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, + 55, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, - -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, - -231, -231, -231, -231, -231, 272, -231, -231, -231, -231, + -231, -231, -231, -231, -231, -231, 233, -231, 234, 234, + 234, 234, 234, 234, 234, 234, 234, 234, -231, -231, + -231, -231, -231, -231, -231, -231, -231, -231, 235, 235, - -231, -231, -231, -231, -231, -231, -231, -231, -231, 273, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, + 235, 235, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231 }, { - 33, -232, -232, -232, -232, -232, -232, -232, -232, -232, - -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, - -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, - -232, -232, 274, -232, -232, -232, -232, -232, -232, -232, - - -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, + 55, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, + + -232, -232, -232, -232, -232, -232, -232, -232, 232, 232, + 232, 232, 232, 232, 232, 232, 232, 232, -232, -232, + -232, -232, -232, -232, -232, -232, -232, -232, 235, 235, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, + 235, 235, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232 }, { - 33, -233, -233, -233, -233, -233, -233, -233, -233, -233, + 55, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, - -233, -233, 275, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, + -233, -233, -233, -233, -233, -233, -233, -233, 391, 391, + 391, 391, 391, 391, 391, 391, 391, 391, -233, -233, + -233, -233, -233, -233, -233, -233, -233, -233, 235, 235, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, - -233, -233, -233, -233, -233, 275, 275, 275, 275, 275, - 275, 275, 275, 275, 275, 275, 275, 275, 275, 275, - 275, 275, 275, 275, 275, 275, 275, 275, 275, 275, - 275, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, + -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, + 235, 235, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233 }, { - 33, -234, -234, -234, -234, -234, -234, -234, -234, -234, - -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, + 55, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, + -234, -234, -234, -234, -234, -234, 233, -234, 234, 234, + 234, 234, 234, 234, 234, 234, 234, 234, -234, -234, + -234, -234, -234, -234, -234, -234, -234, -234, 235, 235, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, - -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, - -234, -234, -234, -234, -234, -234, -234, -234, -234, 276, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, - -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, + 235, 235, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234 }, { - 33, -235, -235, -235, -235, -235, -235, -235, -235, -235, - -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, + 55, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, + -235, -235, -235, 392, -235, 392, -235, -235, 393, 393, + 393, 393, 393, 393, 393, 393, 393, 393, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, - -235, -235, -235, -235, -235, -235, 277, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, @@ -4460,16 +4621,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -236, -236, -236, -236, -236, -236, -236, -236, -236, + 55, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, + -236, -236, -236, -236, -236, -236, -236, -236, 238, 238, + 238, 238, 238, 238, 238, 238, 238, 238, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, - -236, -236, -236, -236, -236, -236, 278, -236, -236, -236, - -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, @@ -4478,81 +4639,81 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -237, -237, -237, -237, -237, -237, -237, -237, -237, + 55, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, + -237, -237, -237, -237, -237, -237, 239, -237, 240, 240, + 240, 240, 240, 240, 240, 240, 240, 240, -237, -237, + -237, -237, -237, -237, -237, -237, -237, -237, 241, 241, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, - -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, - -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, - -237, -237, -237, -237, -237, -237, 279, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, - -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, + 241, 241, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237 }, { - 33, -238, -238, -238, -238, -238, -238, -238, -238, -238, - -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, - -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, + 55, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, + -238, -238, -238, -238, -238, -238, -238, -238, 238, 238, + 238, 238, 238, 238, 238, 238, 238, 238, -238, -238, - -238, -238, -238, -238, -238, -238, 280, -238, -238, -238, - -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, + -238, -238, -238, -238, -238, -238, -238, -238, 241, 241, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, + 241, 241, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238 }, { - 33, -239, -239, -239, -239, -239, -239, -239, -239, -239, + 55, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, - -239, -239, 281, -239, -239, -239, -239, -239, -239, -239, - -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, - -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, - -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, + -239, -239, -239, -239, -239, -239, -239, -239, 394, 394, + 394, 394, 394, 394, 394, 394, 394, 394, -239, -239, + -239, -239, -239, -239, -239, -239, -239, -239, 241, 241, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, + 241, 241, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239 }, { - 33, -240, -240, -240, -240, -240, -240, -240, -240, -240, - -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, + 55, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, + -240, -240, -240, -240, -240, -240, 239, -240, 240, 240, + 240, 240, 240, 240, 240, 240, 240, 240, -240, -240, + -240, -240, -240, -240, -240, -240, -240, -240, 241, 241, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, - -240, -240, -240, -240, -240, -240, -240, -240, -240, 282, - -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, - -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, + 241, 241, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, -240 }, { - 33, -241, -241, -241, -241, -241, -241, -241, -241, -241, - -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, - -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, + 55, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, + -241, -241, -241, 395, -241, 395, -241, -241, 396, 396, + 396, 396, 396, 396, 396, 396, 396, 396, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, @@ -4564,33 +4725,33 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -242, -242, -242, -242, -242, -242, -242, -242, -242, - -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, - -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, - -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, + 55, 242, 242, 242, 242, 242, 242, 242, 242, 242, + 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, + 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, + 242, 242, 242, 242, 242, 242, 242, 242, 242, 243, - -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, - -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, - -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, - -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, - -242, -242, -242, 283, -242, -242, -242, -242, -242, -242, - -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, - -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, - -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, - -242, -242, -242, -242, -242, -242, -242, -242 + 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, + 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, + 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, + 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, + 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, + 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, + 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, + 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, + 242, 242, 242, 242, 242, 242, 242, 242 }, { - 33, -243, -243, -243, -243, -243, -243, -243, -243, -243, + 55, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, + -243, -243, -243, -243, -243, -243, -243, -243, -243, 242, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, - -243, -243, -243, -243, -243, -243, -243, -243, -243, 284, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, @@ -4599,31 +4760,31 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -244, -244, -244, -244, -244, -244, -244, -244, -244, - -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, - -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, - -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, - -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, - -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, - -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, - -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, + 55, 244, 244, 244, 244, 244, 244, 244, 244, 244, + 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, + 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, + 244, 244, 244, 244, 244, 244, 244, 244, 244, 245, + 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, + 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, + 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, + 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, - -244, -244, 285, -244, -244, -244, -244, -244, -244, -244, - -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, - -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, - -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, - -244, -244, -244, -244, -244, -244, -244, -244 + 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, + 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, + 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, + 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, + 244, 244, 244, 244, 244, 244, 244, 244 }, { - 33, -245, -245, -245, -245, -245, -245, -245, -245, -245, + 55, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, - -245, -245, -245, -245, -245, 286, -245, -245, -245, -245, + -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, @@ -4633,31 +4794,31 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -246, -246, -246, -246, -246, -246, -246, -246, -246, + 55, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, - -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, - -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, - -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, - -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, - -246, -246, -246, -246, -246, -246, -246, -246, -246, 287, - -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, - -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, - -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, + -246, -246, -246, -246, -246, -246, 246, -246, 246, 246, + 246, 246, 246, 246, 246, 246, 246, 246, -246, -246, + -246, -246, -246, -246, -246, 246, 246, 246, 246, 246, + 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, + 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, + 246, -246, -246, -246, -246, 246, -246, 246, 246, 246, + 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, + 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, - -246, -246, -246, -246, -246, -246, -246, -246 + 246, 246, 246, -246, -246, -246, -246, -246 }, { - 33, -247, -247, -247, -247, -247, -247, -247, -247, -247, + 55, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, + -247, -247, 247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, - -247, -247, -247, -247, -247, -247, 288, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, @@ -4668,16 +4829,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -248, -248, -248, -248, -248, -248, -248, -248, -248, - -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, - -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, + 55, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, + -248, -248, -248, -248, -248, -248, -248, -248, 250, 250, + 250, 250, 250, 250, 250, 250, 250, 250, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, - -248, -248, 289, -248, -248, -248, -248, -248, -248, -248, + -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, @@ -4685,84 +4846,84 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -249, -249, -249, -249, -249, -249, -249, -249, -249, + 55, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, + -249, -249, -249, -249, -249, -249, 251, -249, 252, 252, + 252, 252, 252, 252, 252, 252, 252, 252, -249, -249, + -249, -249, -249, -249, -249, -249, -249, -249, 253, 253, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, - -249, 290, -249, -249, -249, -249, -249, -249, -249, -249, - -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, - -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, - -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, + 253, 253, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249 }, { - 33, -250, -250, -250, -250, -250, -250, -250, -250, -250, + 55, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, + -250, -250, -250, -250, -250, -250, -250, -250, 250, 250, + 250, 250, 250, 250, 250, 250, 250, 250, -250, -250, + -250, -250, -250, -250, -250, -250, -250, -250, 253, 253, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, - -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, - -250, -250, -250, -250, -250, -250, -250, -250, -250, 291, - -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, - -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, + 253, 253, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250 }, { - 33, -251, -251, -251, -251, -251, -251, -251, -251, -251, - -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, - -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, - -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, + 55, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, + -251, -251, -251, -251, -251, -251, -251, -251, 397, 397, + 397, 397, 397, 397, 397, 397, 397, 397, -251, -251, + -251, -251, -251, -251, -251, -251, -251, -251, 253, 253, - 292, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, + 253, 253, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251 }, { - 33, -252, -252, -252, -252, -252, -252, -252, -252, -252, + 55, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, - -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, - -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, - -252, -252, -252, -252, -252, -252, -252, 293, -252, -252, - -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, + -252, -252, -252, -252, -252, -252, 251, -252, 252, 252, + 252, 252, 252, 252, 252, 252, 252, 252, -252, -252, + -252, -252, -252, -252, -252, -252, -252, -252, 253, 253, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, + 253, 253, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252 }, { - 33, -253, -253, -253, -253, -253, -253, -253, -253, -253, + 55, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, + -253, -253, -253, 398, -253, 398, -253, -253, 399, 399, + 399, 399, 399, 399, 399, 399, 399, 399, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, - -253, -253, -253, -253, -253, -253, -253, -253, -253, 294, - -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, @@ -4772,31 +4933,31 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -254, -254, -254, -254, -254, -254, -254, -254, -254, - -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, - -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, - -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, - -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, - -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, - -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, - -254, -254, -254, -254, -254, -254, -254, 295, -254, -254, + 55, 254, 254, 254, 254, 254, 254, 254, 254, 254, + 255, 254, 254, 254, 254, 254, 254, 254, 254, 254, + 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, + 254, 254, 256, 254, 254, 254, 254, 254, 254, 254, + 254, 254, 254, 254, 254, 254, 254, 257, 254, 254, + 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, + 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, + 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, - -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, - -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, - -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, - -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, - -254, -254, -254, -254, -254, -254, -254, -254 + 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, + 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, + 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, + 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, + 254, 254, 254, 254, 254, 254, 254, 254 }, { - 33, -255, -255, -255, -255, -255, -255, -255, -255, -255, + 55, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, - -255, -255, -255, -255, -255, -255, -255, 296, -255, -255, + -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, @@ -4806,63 +4967,63 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -256, -256, -256, -256, -256, -256, -256, -256, -256, - -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, + 55, 400, 400, 400, 400, 400, 400, 400, 400, 400, + 255, 400, 400, 400, 400, 400, 400, 400, 400, 400, - -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, - -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, - -256, -256, -256, -256, -256, -256, -256, -256, 297, 297, - 297, 297, 297, 297, 297, 297, 297, 297, -256, -256, - -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, - -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, - -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, - -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, - -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, - -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, + 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, + 400, 400, 256, 400, 400, 400, 400, 400, 400, 400, + 400, 400, 400, 400, 400, 400, 400, 257, 400, 400, + 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, + 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, + 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, + 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, + 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, + 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, + 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, - -256, -256, -256, -256, -256, -256, -256, -256 + 400, 400, 400, 400, 400, 400, 400, 400 }, { - 33, -257, -257, -257, -257, -257, -257, -257, -257, -257, - -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, - -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, - -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, - -257, -257, -257, -257, -257, -257, -257, -257, -257, 298, - 298, 298, 298, 298, 298, 298, 298, 298, -257, -257, - -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, - -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, - -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, + 55, 401, 401, 401, 401, 401, 401, 401, 401, 401, + 402, 401, 401, 401, 401, 401, 401, 401, 401, 401, + 401, 401, 401, 401, 401, 401, 401, 401, 401, 401, + 401, 401, 403, 401, 401, 401, 401, 401, 401, 401, + 401, 401, 401, 401, 401, 401, 401, 404, 401, 401, + 401, 401, 401, 401, 401, 401, 401, 401, 401, 401, + 401, 401, 401, 401, 401, 401, 401, 401, 401, 401, + 401, 401, 401, 401, 401, 401, 401, 401, 401, 401, + 401, 401, 401, 401, 401, 401, 401, 401, 401, 401, - -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, - -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, - -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, - -257, -257, -257, -257, -257, -257, -257, -257 + 401, 401, 401, 401, 401, 401, 401, 401, 401, 401, + 401, 401, 401, 401, 401, 401, 401, 401, 401, 401, + 401, 401, 401, 401, 401, 401, 401, 401, 401, 401, + 401, 401, 401, 401, 401, 401, 401, 401 }, { - 33, -258, -258, -258, -258, -258, -258, -258, -258, -258, - -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, - -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, - -258, -258, 299, -258, -258, -258, -258, -258, -258, -258, - -258, -258, -258, -258, -258, -258, -258, -258, 300, 300, - 300, 300, 300, 300, 300, 300, 300, 300, -258, -258, + 55, 405, 405, 405, 405, 405, 405, 405, 405, 405, + 406, 405, 405, 405, 405, 405, 405, 405, 405, 405, + 405, 405, 405, 405, 405, 405, 405, 405, 405, 405, + 405, 405, 407, 405, 405, 405, 405, 405, 405, 405, + 405, 405, 405, 405, 405, 405, 405, 408, 405, 405, + 405, 405, 405, 405, 405, 405, 405, 405, 405, 405, - -258, -258, -258, -258, -258, 299, 299, 299, 299, 299, - 299, 299, 299, 299, 299, 299, 299, 299, 299, 299, - 299, 299, 299, 299, 299, 299, 299, 299, 299, 299, - 299, -258, -258, -258, -258, -258, -258, -258, -258, -258, - -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, - -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, - -258, -258, -258, -258, -258, -258, -258, -258 + 405, 405, 405, 405, 405, 405, 405, 405, 405, 405, + 405, 405, 405, 405, 405, 405, 405, 405, 405, 405, + 405, 405, 405, 405, 405, 405, 405, 405, 405, 405, + 405, 405, 405, 405, 405, 405, 405, 405, 405, 405, + 405, 405, 405, 405, 405, 405, 405, 405, 405, 405, + 405, 405, 405, 405, 405, 405, 405, 405, 405, 405, + 405, 405, 405, 405, 405, 405, 405, 405 }, { - 33, -259, -259, -259, -259, -259, -259, -259, -259, -259, + 55, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, - -259, -259, 301, -259, -259, -259, -259, -259, -259, -259, + -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, @@ -4876,68 +5037,68 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -260, -260, -260, -260, -260, -260, -260, -260, -260, - -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, - -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, - -260, -260, 302, -260, -260, -260, -260, -260, -260, -260, - -260, -260, -260, -260, -260, -260, -260, -260, 303, 303, - 303, 303, 303, 303, 303, 303, 303, 303, -260, -260, - -260, -260, -260, -260, -260, 302, 302, 302, 302, 302, - 302, 302, 302, 302, 302, 302, 302, 302, 302, 302, - 302, 302, 302, 302, 302, 302, 302, 302, 302, 302, - 302, -260, -260, -260, -260, -260, -260, -260, -260, -260, + 55, 409, 409, 409, 409, 409, 409, 409, 409, 409, + 410, 409, 409, 409, 409, 409, 409, 409, 409, 409, + 409, 409, 409, 409, 409, 409, 409, 409, 409, 409, + 409, 409, 411, 409, 409, 409, 409, 409, 409, 409, + 409, 409, 409, 409, 409, 409, 409, 412, 409, 409, + 409, 409, 409, 409, 409, 409, 409, 409, 409, 409, + 409, 409, 409, 409, 409, 409, 409, 409, 409, 409, + 409, 409, 409, 409, 409, 409, 409, 409, 409, 409, + 409, 409, 409, 409, 409, 409, 409, 409, 409, 409, + 409, 409, 409, 409, 409, 409, 409, 409, 409, 409, - -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, - -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, - -260, -260, -260, -260, -260, -260, -260, -260 + 409, 409, 409, 409, 409, 409, 409, 409, 409, 409, + 409, 409, 409, 409, 409, 409, 409, 409, 409, 409, + 409, 409, 409, 409, 409, 409, 409, 409 }, { - 33, -261, -261, -261, -261, -261, -261, -261, -261, -261, - -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, - -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, - -261, -261, 304, -261, -261, -261, -261, -261, -261, -261, - -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, - -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, - -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, + 55, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 263, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 264, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 265, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, - -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, - -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, - -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, - -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, - -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, - -261, -261, -261, -261, -261, -261, -261, -261 + 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 262 }, { - 33, -262, -262, -262, -262, -262, -262, -262, -262, -262, - -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, - -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, - -262, -262, 305, -262, -262, -262, -262, -262, -262, -262, + 55, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 263, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 264, 262, 262, 262, 262, 262, 262, 262, - -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, - -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, - -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, - -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, - -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, - -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, - -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, - -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, - -262, -262, -262, -262, -262, -262, -262, -262 + 262, 262, 262, 262, 262, 262, 262, 265, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 262 }, { - 33, -263, -263, -263, -263, -263, -263, -263, -263, -263, + 55, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, - -263, -263, 306, -263, -263, -263, -263, -263, -263, -263, - -263, -263, -263, -263, -263, -263, -263, -263, 307, 307, - 307, 307, 307, 307, 307, 307, 307, 307, -263, -263, - -263, -263, -263, -263, -263, 306, 306, 306, 306, 306, - 306, 306, 306, 306, 306, 306, 306, 306, 306, 306, - 306, 306, 306, 306, 306, 306, 306, 306, 306, 306, - 306, -263, -263, -263, -263, -263, -263, -263, -263, -263, + -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, + -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, + -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, + -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, + -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, + -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, + -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, @@ -4945,62 +5106,62 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -264, -264, -264, -264, -264, -264, -264, -264, -264, - -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, - -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, - -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, - -264, -264, -264, -264, -264, -264, -264, -264, 308, 308, - 308, 308, 308, 308, 308, 308, 308, 308, -264, -264, - -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, - -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, + 55, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 263, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 264, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 265, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, - -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, - -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, - -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, - -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, - -264, -264, -264, -264, -264, -264, -264, -264 + 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 262 }, { - 33, -265, -265, -265, -265, -265, -265, -265, -265, -265, - -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, - -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, - -265, -265, 309, -265, -265, -265, -265, -265, -265, -265, - -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, + 55, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 263, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 264, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 265, 262, 262, - -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, - -265, -265, -265, -265, -265, 309, 309, 309, 309, 309, - 309, 309, 309, 309, 309, 309, 309, 309, 309, 309, - 309, 309, 309, 309, 309, 309, 309, 309, 309, 309, - 309, -265, -265, -265, -265, -265, -265, -265, -265, -265, - -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, - -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, - -265, -265, -265, -265, -265, -265, -265, -265 + 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 262 }, { - 33, -266, -266, -266, -266, -266, -266, -266, -266, -266, - -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, + 55, 266, 266, 266, 266, 266, 266, 266, 266, 266, + 267, 266, 266, 266, 266, 266, 266, 266, 266, 266, - -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, - -266, -266, 309, -266, -266, -266, -266, -266, -266, -266, - -266, -266, -266, -266, -266, -266, -266, -266, 310, 310, - 310, 310, 310, 310, 310, 310, 310, 310, -266, -266, - -266, -266, -266, -266, -266, 309, 309, 309, 309, 309, - 309, 309, 309, 309, 309, 309, 309, 309, 309, 309, - 309, 309, 309, 309, 309, 309, 309, 309, 309, 309, - 309, -266, -266, -266, -266, -266, -266, -266, -266, -266, - -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, - -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, + 266, 266, 266, 266, 266, 266, 266, 266, 266, 266, + 266, 266, 266, 266, 266, 266, 266, 266, 266, 266, + 266, 266, 266, 266, 266, 266, 266, 266, 266, 266, + 266, 266, 266, 266, 266, 266, 266, 266, 266, 266, + 266, 266, 266, 266, 266, 266, 266, 266, 266, 266, + 266, 266, 266, 266, 266, 266, 266, 266, 266, 266, + 266, 266, 266, 266, 266, 266, 266, 266, 266, 266, + 266, 266, 266, 266, 266, 266, 266, 266, 266, 266, + 266, 266, 266, 266, 266, 266, 266, 266, 266, 266, + 266, 266, 266, 266, 266, 266, 266, 266, 266, 266, - -266, -266, -266, -266, -266, -266, -266, -266 + 266, 266, 266, 266, 266, 266, 266, 266 }, { - 33, -267, -267, -267, -267, -267, -267, -267, -267, -267, + 55, -267, -267, -267, -267, -267, -267, -267, -267, -267, + -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, - -267, -267, 311, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, @@ -5014,28 +5175,28 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -268, -268, -268, -268, -268, -268, -268, -268, -268, - -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, - -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, - -268, -268, 312, -268, -268, -268, -268, -268, -268, -268, - -268, -268, -268, -268, -268, -268, -268, -268, 313, 313, - 313, 313, 313, 313, 313, 313, 313, 313, -268, -268, + 55, 268, 268, 268, 268, 268, 268, 268, 268, 268, + 269, 268, 268, 268, 268, 268, 268, 268, 268, 268, + 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, + 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, + 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, + 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, - -268, -268, -268, -268, -268, 312, 312, 312, 312, 312, - 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, - 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, - 312, -268, -268, -268, -268, -268, -268, -268, -268, -268, - -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, - -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, - -268, -268, -268, -268, -268, -268, -268, -268 + 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, + 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, + 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, + 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, + 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, + 268, 268, 268, 268, 268, 268, 268, 268, 268, 268, + 268, 268, 268, 268, 268, 268, 268, 268 }, { - 33, -269, -269, -269, -269, -269, -269, -269, -269, -269, + 55, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, - -269, -269, 314, -269, -269, -269, -269, -269, -269, -269, + -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, @@ -5049,41 +5210,41 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -270, -270, -270, -270, -270, -270, -270, -270, -270, - -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, - -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, - -270, -270, 315, -270, -270, -270, -270, -270, -270, -270, - -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, - -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, - -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, - -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, - -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, - -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, + 55, 270, 270, 270, 270, 270, 270, 270, 270, 270, + 271, 270, 270, 270, 270, 270, 270, 270, 270, 270, + 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, + 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, + 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, + 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, + 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, + 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, + 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, + 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, - -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, - -270, -270, -270, -270, -270, -270, -270, -270, -270, -270, - -270, -270, -270, -270, -270, -270, -270, -270 + 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, + 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, + 270, 270, 270, 270, 270, 270, 270, 270 }, { - 33, -271, -271, -271, -271, -271, -271, -271, -271, -271, + 55, -271, -271, -271, -271, -271, -271, -271, -271, -271, + -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, + -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, + -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, + -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, + -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, + -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, + + -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, - -271, -271, 316, -271, -271, -271, -271, -271, -271, -271, - -271, -271, -271, -271, -271, -271, -271, -271, 317, 317, - 317, 317, 317, 317, 317, 317, 317, 317, -271, -271, - -271, -271, -271, -271, -271, 316, 316, 316, 316, 316, - - 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, - 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, - 316, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271 }, { - 33, -272, -272, -272, -272, -272, -272, -272, -272, -272, + 55, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, @@ -5092,7 +5253,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, - -272, -272, -272, -272, -272, -272, 318, -272, -272, -272, + -272, -272, -272, -272, -272, -272, -272, -272, 413, 414, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, @@ -5100,15 +5261,15 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -273, -273, -273, -273, -273, -273, -273, -273, -273, + 55, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, - -273, -273, -273, -273, -273, -273, 319, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, + -273, -273, -273, -273, -273, -273, -273, -273, -273, 415, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, @@ -5118,14 +5279,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -274, -274, -274, -274, -274, -274, -274, -274, -274, + 55, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, - -274, -274, 320, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, + -274, -274, -274, -274, -274, -274, -274, 416, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, -274, @@ -5135,16 +5296,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -275, -275, -275, -275, -275, -275, -275, -275, -275, + 55, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, - -275, -275, 321, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, - -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, + -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, + -275, -275, 417, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, -275, @@ -5152,16 +5313,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -276, -276, -276, -276, -276, -276, -276, -276, -276, + 55, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, + -276, -276, -276, -276, -276, 418, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, - -276, -276, -276, -276, -276, -276, -276, -276, 322, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, @@ -5170,15 +5331,15 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -277, -277, -277, -277, -277, -277, -277, -277, -277, + 55, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, - -277, -277, -277, -277, -277, -277, -277, -277, -277, 323, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, + -277, -277, -277, 419, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, @@ -5187,15 +5348,15 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -278, -278, -278, -278, -278, -278, -278, -278, -278, + 55, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, - -278, -278, -278, -278, -278, -278, -278, -278, -278, 324, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, + -278, -278, -278, -278, -278, -278, -278, -278, -278, 420, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, @@ -5204,7 +5365,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -279, -279, -279, -279, -279, -279, -279, -279, -279, + 55, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, @@ -5212,8 +5373,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, - -279, 325, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, + -279, -279, -279, -279, 421, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, @@ -5222,15 +5383,15 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -280, -280, -280, -280, -280, -280, -280, -280, -280, + 55, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, + -280, -280, -280, -280, -280, -280, -280, -280, 422, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, - -280, -280, -280, 326, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, -280, @@ -5239,15 +5400,15 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -281, -281, -281, -281, -281, -281, -281, -281, -281, + 55, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, - -281, -281, 327, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, - -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, + + -281, -281, -281, -281, -281, -281, -281, -281, -281, 423, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, -281, @@ -5256,15 +5417,15 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -282, -282, -282, -282, -282, -282, -282, -282, -282, + 55, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, - -282, -282, -282, -282, -282, 328, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, + -282, -282, -282, -282, -282, -282, -282, 424, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, @@ -5273,7 +5434,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -283, -283, -283, -283, -283, -283, -283, -283, -283, + 55, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, @@ -5282,7 +5443,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, - -283, -283, -283, -283, -283, -283, -283, -283, -283, 329, + -283, -283, 425, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, @@ -5291,16 +5452,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -284, -284, -284, -284, -284, -284, -284, -284, -284, - -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, + 55, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, + -284, -284, -284, -284, -284, 426, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, - -284, -284, -284, 330, -284, -284, -284, -284, -284, -284, + -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, -284, @@ -5308,16 +5469,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -285, -285, -285, -285, -285, -285, -285, -285, -285, + 55, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, - -285, -285, -285, -285, -285, -285, -285, -285, -285, 331, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, - -285, 332, -285, -285, -285, -285, -285, -285, -285, -285, + -285, -285, -285, -285, -285, -285, 427, -285, -285, -285, + -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, @@ -5325,7 +5486,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -286, -286, -286, -286, -286, -286, -286, -286, -286, + 55, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, @@ -5333,8 +5494,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, + -286, -286, -286, -286, -286, -286, -286, 428, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, - -286, -286, -286, -286, -286, -286, 333, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, @@ -5343,15 +5504,15 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -287, -287, -287, -287, -287, -287, -287, -287, -287, + 55, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, + -287, -287, -287, 429, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, - -287, -287, -287, 334, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, -287, @@ -5360,7 +5521,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -288, -288, -288, -288, -288, -288, -288, -288, -288, + 55, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, @@ -5368,8 +5529,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, + -288, -288, -288, 430, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, - -288, -288, -288, 335, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, @@ -5377,16 +5538,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -289, -289, -289, -289, -289, -289, -289, -289, -289, + 55, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, - -289, -289, -289, -289, -289, -289, -289, 336, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, + -289, -289, 431, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, @@ -5395,14 +5556,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -290, -290, -290, -290, -290, -290, -290, -290, -290, + 55, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, - -290, -290, -290, -290, -290, -290, 337, -290, -290, -290, + -290, -290, -290, 432, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, @@ -5412,7 +5573,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -291, -291, -291, -291, -291, -291, -291, -291, -291, + 55, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, @@ -5421,7 +5582,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, - -291, -291, -291, 338, -291, -291, -291, -291, -291, -291, + -291, -291, 433, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, @@ -5429,33 +5590,33 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -292, -292, -292, -292, -292, -292, -292, -292, -292, + 55, -292, -292, -292, -292, -292, -292, -292, -292, -292, + -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, - -292, -292, 339, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, - -292, -292, -292, -292, -292, 339, 339, 339, 339, 339, - 339, 339, 339, 339, 339, 339, 339, 339, 339, 339, - 339, 339, 339, 339, 339, 339, 339, 339, 339, 339, - 339, -292, -292, -292, -292, -292, -292, -292, -292, -292, + -292, -292, -292, -292, -292, -292, -292, -292, -292, 434, + -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, + -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, + -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292 }, { - 33, -293, -293, -293, -293, -293, -293, -293, -293, -293, + 55, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, - -293, -293, -293, -293, -293, -293, -293, -293, -293, 340, - -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, + -293, -293, -293, -293, -293, -293, -293, -293, 435, -293, + -293, -293, -293, -293, 436, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, @@ -5464,7 +5625,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -294, -294, -294, -294, -294, -294, -294, -294, -294, + 55, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, @@ -5473,7 +5634,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, - -294, -294, -294, 341, -294, -294, -294, -294, -294, -294, + -294, -294, -294, -294, 437, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, @@ -5481,15 +5642,15 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -295, -295, -295, -295, -295, -295, -295, -295, -295, + 55, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, - -295, -295, -295, -295, -295, -295, -295, -295, -295, 342, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, + -295, -295, -295, 438, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, @@ -5498,14 +5659,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -296, -296, -296, -296, -296, -296, -296, -296, -296, + 55, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, - -296, -296, -296, -296, -296, -296, -296, -296, -296, 343, + -296, -296, -296, -296, -296, 439, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, @@ -5516,13 +5677,13 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -297, -297, -297, -297, -297, -297, -297, -297, -297, - -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, + 55, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, + -297, -297, -297, -297, -297, -297, -297, -297, -297, 440, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, @@ -5533,7 +5694,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -298, -298, -298, -298, -298, -298, -298, -298, -298, + 55, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, @@ -5542,7 +5703,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, - -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, + 441, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, @@ -5550,7 +5711,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -299, -299, -299, -299, -299, -299, -299, -299, -299, + 55, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, @@ -5558,7 +5719,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, - -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, + -299, -299, -299, 442, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, @@ -5568,14 +5729,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -300, -300, -300, -300, -300, -300, -300, -300, -300, - -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, + 55, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, + -300, -300, 443, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, @@ -5585,13 +5746,13 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -301, -301, -301, -301, -301, -301, -301, -301, -301, - -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, + 55, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, + -301, -301, -301, -301, -301, 444, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, @@ -5602,14 +5763,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -302, -302, -302, -302, -302, -302, -302, -302, -302, + 55, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, - -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, + -302, -302, -302, -302, -302, -302, -302, -302, -302, 445, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, @@ -5619,7 +5780,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -303, -303, -303, -303, -303, -303, -303, -303, -303, + 55, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, @@ -5627,7 +5788,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, - -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, + -303, -303, -303, -303, -303, -303, -303, -303, 446, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, @@ -5637,7 +5798,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -304, -304, -304, -304, -304, -304, -304, -304, -304, + 55, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, @@ -5646,7 +5807,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, - -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, + -304, -304, 447, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, @@ -5654,10 +5815,10 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -305, -305, -305, -305, -305, -305, -305, -305, -305, - -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, + 55, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, + -305, -305, 448, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, @@ -5671,14 +5832,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -306, -306, -306, -306, -306, -306, -306, -306, -306, + 55, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, - -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, + -306, -306, -306, -306, -306, -306, -306, 449, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, @@ -5689,14 +5850,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -307, -307, -307, -307, -307, -307, -307, -307, -307, - -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, + 55, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, + -307, -307, -307, 450, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, @@ -5706,7 +5867,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -308, -308, -308, -308, -308, -308, -308, -308, -308, + 55, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, @@ -5714,8 +5875,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, - -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, - -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, + -308, -308, -308, -308, -308, -308, -308, -308, 451, -308, + -308, -308, -308, -308, 452, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, @@ -5723,7 +5884,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -309, -309, -309, -309, -309, -309, -309, -309, -309, + 55, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, @@ -5732,7 +5893,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, - -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, + -309, -309, 453, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, @@ -5741,13 +5902,13 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -310, -310, -310, -310, -310, -310, -310, -310, -310, - -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, + 55, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, + -310, -310, -310, -310, -310, -310, -310, -310, -310, 454, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, @@ -5758,7 +5919,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -311, -311, -311, -311, -311, -311, -311, -311, -311, + 55, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, @@ -5766,7 +5927,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, - -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, + -311, -311, -311, -311, -311, -311, -311, -311, -311, 455, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, @@ -5775,7 +5936,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -312, -312, -312, -312, -312, -312, -312, -312, -312, + 55, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, @@ -5784,7 +5945,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, - -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, + 456, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, @@ -5792,7 +5953,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -313, -313, -313, -313, -313, -313, -313, -313, -313, + 55, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, @@ -5801,7 +5962,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, - -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, + 457, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, @@ -5810,16 +5971,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -314, -314, -314, -314, -314, -314, -314, -314, -314, - -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, + 55, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, + -314, -314, -314, -314, -314, 458, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, - -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, + -314, -314, 459, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, @@ -5827,7 +5988,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -315, -315, -315, -315, -315, -315, -315, -315, -315, + 55, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, @@ -5835,7 +5996,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, - -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, + -315, -315, -315, 460, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, @@ -5844,7 +6005,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -316, -316, -316, -316, -316, -316, -316, -316, -316, + 55, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, @@ -5852,7 +6013,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, - -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, + -316, 461, -316, -316, -316, -316, -316, -316, -316, 462, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, @@ -5862,8 +6023,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -317, -317, -317, -317, -317, -317, -317, -317, -317, - -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, + 55, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, @@ -5871,6 +6031,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, + -317, -317, -317, -317, 463, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, @@ -5879,7 +6040,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -318, -318, -318, -318, -318, -318, -318, -318, -318, + 55, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, @@ -5887,8 +6048,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, - -318, 344, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, + 464, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, @@ -5896,16 +6057,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -319, -319, -319, -319, -319, -319, -319, -319, -319, + 55, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, + -319, -319, -319, -319, -319, -319, -319, -319, 465, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, - -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, - -319, -319, -319, 345, -319, -319, -319, -319, -319, -319, + -319, -319, 466, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, @@ -5914,12 +6075,12 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -320, -320, -320, -320, -320, -320, -320, -320, -320, + 55, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, - -320, -320, 346, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, + -320, 467, -320, -320, 468, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, @@ -5931,15 +6092,15 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -321, -321, -321, -321, -321, -321, -321, -321, -321, + 55, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, - -321, -321, 347, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, - -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, + + -321, -321, -321, -321, 469, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, -321, @@ -5948,14 +6109,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -322, -322, -322, -322, -322, -322, -322, -322, -322, + 55, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, - -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, + -322, -322, -322, -322, -322, -322, -322, -322, -322, 470, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, @@ -5965,7 +6126,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -323, -323, -323, -323, -323, -323, -323, -323, -323, + 55, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, @@ -5974,7 +6135,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, - -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, + -323, -323, -323, -323, 471, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, -323, @@ -5983,14 +6144,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -324, -324, -324, -324, -324, -324, -324, -324, -324, - -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, + 55, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, + -324, -324, -324, -324, -324, -324, -324, -324, 472, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, @@ -6000,14 +6161,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -325, -325, -325, -325, -325, -325, -325, -325, -325, + 55, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, - -325, -325, 348, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, - -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, + -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, + -325, -325, -325, -325, -325, -325, -325, 473, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, @@ -6017,16 +6178,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -326, -326, -326, -326, -326, -326, -326, -326, -326, + 55, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, - -326, -326, 349, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, + -326, -326, -326, 474, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, @@ -6035,13 +6196,13 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -327, -327, -327, -327, -327, -327, -327, -327, -327, + 55, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, - -327, -327, 350, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, + -327, -327, -327, -327, -327, 475, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, @@ -6052,24 +6213,24 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -328, -328, -328, -328, -328, -328, -328, -328, -328, + 55, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, + -328, -328, -328, -328, -328, -328, -328, -328, -328, 476, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, + -328, -328, 477, 478, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, - -328, -328, -328, -328, -328, -328, -328, -328, 351, 352, - 353, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328 }, { - 33, -329, -329, -329, -329, -329, -329, -329, -329, -329, + 55, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, @@ -6077,8 +6238,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, + 479, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, - -329, -329, -329, 354, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, @@ -6087,14 +6248,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -330, -330, -330, -330, -330, -330, -330, -330, -330, - -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, - -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, + 55, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, + -330, -330, -330, -330, -330, 480, -330, -330, -330, -330, + -330, -330, -330, -330, -330, -330, -330, -330, -330, 481, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, @@ -6104,16 +6265,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -331, -331, -331, -331, -331, -331, -331, -331, -331, - -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, + 55, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, + -331, -331, -331, -331, -331, 482, -331, -331, -331, -331, - -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, - -331, 355, -331, -331, -331, -331, -331, -331, -331, -331, + -331, -331, -331, -331, -331, -331, -331, -331, -331, 483, + -331, -331, 484, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, @@ -6121,7 +6282,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -332, -332, -332, -332, -332, -332, -332, -332, -332, + 55, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, @@ -6130,7 +6291,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, - -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, + -332, -332, -332, -332, -332, 485, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, @@ -6138,13 +6299,13 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -333, -333, -333, -333, -333, -333, -333, -333, -333, + 55, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, - -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, - -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, + -333, -333, -333, -333, -333, -333, -333, -333, -333, 486, + 486, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, @@ -6156,14 +6317,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -334, -334, -334, -334, -334, -334, -334, -334, -334, - -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, - -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, + 55, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, + -334, -334, -334, -334, -334, 487, -334, -334, -334, -334, + -334, -334, -334, -334, -334, -334, -334, -334, 488, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, @@ -6173,7 +6334,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -335, -335, -335, -335, -335, -335, -335, -335, -335, + 55, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, @@ -6182,7 +6343,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, - -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, + -335, -335, -335, -335, -335, -335, -335, -335, 489, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, @@ -6190,7 +6351,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -336, -336, -336, -336, -336, -336, -336, -336, -336, + 55, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, @@ -6199,7 +6360,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, - -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, + -336, -336, -336, 490, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, @@ -6208,8 +6369,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -337, -337, -337, -337, -337, -337, -337, -337, -337, - -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, + 55, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, @@ -6217,6 +6377,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, + -337, -337, -337, -337, -337, -337, -337, -337, 491, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, @@ -6225,7 +6386,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -338, -338, -338, -338, -338, -338, -338, -338, -338, + 55, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, @@ -6234,7 +6395,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, - -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, + -338, -338, -338, -338, -338, 492, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, @@ -6242,11 +6403,11 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -339, -339, -339, -339, -339, -339, -339, -339, -339, + 55, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, - -339, -339, 356, -339, -339, -339, -339, -339, -339, -339, + -339, -339, 493, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, @@ -6260,16 +6421,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -340, -340, -340, -340, -340, -340, -340, -340, -340, + 55, -340, -340, -340, -340, -340, -340, -340, -340, -340, + -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, + -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, + -340, -340, 494, -340, -340, -340, -340, -340, -340, -340, + -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, + -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, - -340, -340, 357, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, - -340, -340, -340, -340, -340, 357, 357, 357, 357, 357, - 357, 357, 357, 357, 357, 357, 357, 357, 357, 357, - 357, 357, 357, 357, 357, 357, 357, 357, 357, 357, - 357, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, @@ -6277,45 +6438,45 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -341, -341, -341, -341, -341, -341, -341, -341, -341, - -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, + 55, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, - -341, -341, 358, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, - -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, - -341, -341, -341, -341, -341, 358, 358, 358, 358, 358, - - 358, 358, 358, 358, 358, 358, 358, 358, 358, 358, - 358, 358, 358, 358, 358, 358, 358, 358, 358, 358, - 358, -341, -341, -341, -341, -341, -341, -341, -341, -341, + -341, -341, 495, -341, -341, -341, -341, -341, -341, -341, + -341, -341, -341, -341, -341, -341, -341, -341, 496, 497, + 497, 497, 497, 497, 497, 497, 497, 497, -341, -341, + -341, -341, -341, -341, -341, 495, 495, 495, 495, 495, + + 495, 495, 495, 495, 495, 495, 495, 495, 495, 495, + 495, 495, 495, 495, 495, 495, 495, 495, 495, 495, + 495, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341 }, { - 33, -342, -342, -342, -342, -342, -342, -342, -342, -342, - -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, - -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, - -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, - - -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, - -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, - -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, - -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, + 55, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, + -342, -342, 498, -342, -342, -342, -342, -342, -342, -342, + + -342, -342, -342, -342, -342, -342, -342, -342, 496, 497, + 497, 497, 497, 497, 497, 497, 497, 497, -342, -342, + -342, -342, -342, -342, -342, 498, 498, 498, 498, 498, + 498, 498, 498, 498, 498, 498, 498, 498, 498, 498, + 498, 498, 498, 498, 498, 498, 498, 498, 498, 498, + 498, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342 }, { - 33, -343, -343, -343, -343, -343, -343, -343, -343, -343, + 55, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, - -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, + -343, -343, 499, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, @@ -6329,10 +6490,10 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -344, -344, -344, -344, -344, -344, -344, -344, -344, - -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, + 55, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, + -344, -344, 500, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, @@ -6346,10 +6507,10 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -345, -345, -345, -345, -345, -345, -345, -345, -345, - -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, + 55, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, + -345, -345, 501, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, @@ -6363,17 +6524,17 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -346, -346, -346, -346, -346, -346, -346, -346, -346, + 55, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, - -346, -346, 359, -346, -346, -346, -346, -346, -346, -346, - -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, - -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, - -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, - -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, - -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, - -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, + -346, -346, 495, -346, -346, -346, -346, -346, -346, -346, + -346, -346, -346, -346, -346, -346, -346, -346, 502, 502, + 502, 502, 502, 502, 502, 502, 502, 502, -346, -346, + -346, -346, -346, -346, -346, 495, 495, 495, 495, 495, + 495, 495, 495, 495, 495, 495, 495, 495, 495, 495, + 495, 495, 495, 495, 495, 495, 495, 495, 495, 495, + 495, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, @@ -6381,29 +6542,29 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -347, -347, -347, -347, -347, -347, -347, -347, -347, - -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, + 55, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, - -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, - -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, - -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, - -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, - -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, - - -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, + -347, -347, 503, -347, -347, -347, -347, -347, -347, -347, + -347, -347, -347, -347, -347, -347, -347, -347, 504, 504, + 504, 504, 504, 504, 504, 504, 504, 504, -347, -347, + -347, -347, -347, -347, -347, 503, 503, 503, 503, 503, + 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, + 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, + + 503, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347 }, { - 33, -348, -348, -348, -348, -348, -348, -348, -348, -348, - -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, - -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, + 55, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, + -348, -348, -348, -348, -348, -348, -348, -348, 505, 505, + 505, 505, 505, 505, 505, 505, 505, 505, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, @@ -6415,17 +6576,17 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -349, -349, -349, -349, -349, -349, -349, -349, -349, + 55, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, + -349, -349, -349, -349, -349, 506, -349, -349, 507, 508, + 508, 508, 508, 508, 508, 508, 508, 508, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, - -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, - -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, - -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, + -349, -349, -349, -349, -349, 509, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349 @@ -6433,16 +6594,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -350, -350, -350, -350, -350, -350, -350, -350, -350, + 55, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, - -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, - -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, - -350, 360, -350, -350, -350, -350, -350, -350, -350, -350, + -350, -350, -350, -350, -350, 506, -350, -350, 510, 510, + 510, 510, 510, 510, 510, 510, 510, 510, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, + -350, -350, -350, -350, -350, 511, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, @@ -6450,12 +6611,12 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -351, -351, -351, -351, -351, -351, -351, -351, -351, - -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, - -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, + 55, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, + -351, -351, -351, -351, -351, -351, -351, -351, 512, 512, + 512, 512, 512, 512, 512, 512, 512, 512, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, @@ -6467,34 +6628,34 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -352, -352, -352, -352, -352, -352, -352, -352, -352, + 55, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, + -352, -352, -352, -352, -352, 506, -352, -352, 507, 507, + 507, 507, 507, 507, 507, 507, 507, 507, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, - -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, - -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, - -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, + -352, -352, -352, -352, -352, 513, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352 }, { - 33, -353, -353, -353, -353, -353, -353, -353, -353, -353, + 55, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, + -353, -353, -353, -353, -353, 506, -353, -353, 514, 514, + 514, 514, 514, 514, 514, 514, 514, 514, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, - -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, - -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, - -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, + -353, -353, -353, -353, -353, 513, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, @@ -6502,12 +6663,12 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -354, -354, -354, -354, -354, -354, -354, -354, -354, - -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, - -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, + 55, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, + -354, -354, -354, -354, -354, -354, -354, -354, 515, 516, + 516, 516, 516, 516, 516, 516, 516, 516, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, @@ -6519,34 +6680,34 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -355, -355, -355, -355, -355, -355, -355, -355, -355, - -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, - -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, - -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, - -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, - - -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, - -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, - -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, + 55, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, + -355, -355, 517, -355, -355, -355, -355, -355, -355, -355, + -355, -355, -355, -355, -355, -355, -355, -355, 518, 518, + + 518, 518, 518, 518, 518, 518, 518, 518, -355, -355, + -355, -355, -355, -355, -355, 517, 517, 517, 517, 517, + 517, 517, 517, 517, 517, 517, 517, 517, 517, 517, + 517, 517, 517, 517, 517, 517, 517, 517, 517, 517, + 517, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355 }, { - 33, -356, -356, -356, -356, -356, -356, -356, -356, -356, + 55, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, + -356, -356, -356, -356, -356, 506, -356, -356, 507, 507, + 507, 507, 507, 507, 507, 507, 507, 507, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, - -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, - -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, - -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, + -356, -356, -356, -356, -356, 509, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, @@ -6554,31 +6715,31 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -357, -357, -357, -357, -357, -357, -357, -357, -357, - -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, - -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, + 55, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, + -357, -357, -357, -357, -357, 506, -357, -357, 514, 514, + 514, 514, 514, 514, 514, 514, 514, 514, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, - -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, + -357, -357, -357, -357, -357, 509, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357 }, { - 33, -358, -358, -358, -358, -358, -358, -358, -358, -358, - -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, - -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, + 55, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, + -358, -358, -358, -358, -358, -358, -358, -358, 519, 520, + 520, 520, 520, 520, 520, 520, 520, 520, -358, -358, - -358, 361, -358, -358, -358, -358, -358, -358, -358, -358, + -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, -358, @@ -6588,17 +6749,17 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -359, -359, -359, -359, -359, -359, -359, -359, -359, + 55, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, - -359, -359, 362, -359, -359, -359, -359, -359, -359, -359, - -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, - -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, - -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, - -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, - -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, - -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, + -359, -359, 517, -359, -359, -359, -359, -359, -359, -359, + -359, -359, -359, -359, -359, -359, -359, -359, 521, 522, + 522, 522, 522, 522, 522, 522, 522, 522, -359, -359, + -359, -359, -359, -359, -359, 517, 517, 517, 517, 517, + 517, 517, 517, 517, 517, 517, 517, 517, 517, 517, + 517, 517, 517, 517, 517, 517, 517, 517, 517, 517, + 517, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359 @@ -6606,16 +6767,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -360, -360, -360, -360, -360, -360, -360, -360, -360, - -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, - -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, - -360, -360, 363, -360, -360, -360, -360, -360, -360, -360, - -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, - -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, - -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, - -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, + 55, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, + -360, -360, 523, -360, -360, -360, -360, -360, -360, -360, + -360, -360, -360, -360, -360, -360, -360, -360, 524, 524, + 524, 524, 524, 524, 524, 524, 524, 524, -360, -360, + -360, -360, -360, -360, -360, 523, 523, 523, 523, 523, + 523, 523, 523, 523, 523, 523, 523, 523, 523, 523, + 523, 523, 523, 523, 523, 523, 523, 523, 523, 523, + 523, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, -360, @@ -6623,10 +6784,10 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -361, -361, -361, -361, -361, -361, -361, -361, -361, + 55, -361, -361, -361, -361, -361, -361, -361, -361, -361, + -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, - -361, -361, 364, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, @@ -6640,10 +6801,10 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -362, -362, -362, -362, -362, -362, -362, -362, -362, + 55, -362, -362, -362, -362, -362, -362, -362, -362, -362, + -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, - -362, -362, 365, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, @@ -6657,13 +6818,13 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -363, -363, -363, -363, -363, -363, -363, -363, -363, + 55, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, - -363, -363, 366, -363, -363, -363, -363, -363, -363, -363, - -363, -363, -363, 367, -363, 367, -363, -363, 368, 368, - 368, 368, 368, 368, 368, 368, 368, 368, -363, -363, + -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, + -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, + -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, @@ -6675,12 +6836,12 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -364, -364, -364, -364, -364, -364, -364, -364, -364, + 55, -364, -364, -364, -364, -364, -364, -364, -364, -364, + -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, + -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, + -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, - -364, -364, 369, -364, -364, -364, -364, -364, -364, -364, - -364, -364, -364, 370, -364, 370, -364, -364, 371, 371, - 371, 371, 371, 371, 371, 371, 371, 371, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, @@ -6692,10 +6853,10 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -365, -365, -365, -365, -365, -365, -365, -365, -365, + 55, -365, -365, -365, -365, -365, -365, -365, -365, -365, + -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, - -365, -365, 372, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, -365, @@ -6709,13 +6870,13 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -366, -366, -366, -366, -366, -366, -366, -366, -366, + 55, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, - -366, -366, 366, -366, -366, -366, -366, -366, -366, -366, - -366, -366, -366, 367, -366, 367, -366, -366, 368, 368, - 368, 368, 368, 368, 368, 368, 368, 368, -366, -366, + -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, + -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, + -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, @@ -6727,12 +6888,12 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -367, -367, -367, -367, -367, -367, -367, -367, -367, + 55, -367, -367, -367, -367, -367, -367, -367, -367, -367, + -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, + -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, - -367, -367, -367, -367, -367, -367, -367, -367, 368, 368, - 368, 368, 368, 368, 368, 368, 368, 368, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, @@ -6744,30 +6905,30 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -368, -368, -368, -368, -368, -368, -368, -368, -368, - -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, - -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, - -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, - -368, -368, -368, -368, -368, -368, -368, -368, 368, 368, - 368, 368, 368, 368, 368, 368, 368, 368, -368, -368, - - -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, - -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, + 55, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, + -368, -368, 525, -368, -368, -368, -368, -368, -368, -368, + -368, -368, -368, -368, -368, -368, -368, -368, 526, 526, + 526, 526, 526, 526, 526, 526, 526, 526, -368, -368, + + -368, -368, -368, -368, -368, 525, 525, 525, 525, 525, + 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, + 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, + 525, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, -368 }, { - 33, -369, -369, -369, -369, -369, -369, -369, -369, -369, + 55, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, - -369, -369, 369, -369, -369, -369, -369, -369, -369, -369, - -369, -369, -369, 370, -369, 370, -369, -369, 371, 371, - 371, 371, 371, 371, 371, 371, 371, 371, -369, -369, + -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, + -369, -369, -369, -369, -369, -369, -369, -369, 527, 527, + 527, 527, 527, 527, 527, 527, 527, 527, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, @@ -6779,16 +6940,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -370, -370, -370, -370, -370, -370, -370, -370, -370, + 55, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, - -370, -370, -370, -370, -370, -370, -370, -370, 371, 371, - 371, 371, 371, 371, 371, 371, 371, 371, -370, -370, - -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, + -370, -370, -370, -370, -370, 528, -370, -370, 529, 530, + 530, 530, 530, 530, 530, 530, 530, 530, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, + -370, -370, -370, -370, -370, 531, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, @@ -6796,30 +6957,30 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -371, -371, -371, -371, -371, -371, -371, -371, -371, + 55, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, - -371, -371, -371, -371, -371, -371, -371, -371, 371, 371, - 371, 371, 371, 371, 371, 371, 371, 371, -371, -371, + -371, -371, -371, -371, -371, 528, -371, -371, 532, 532, + 532, 532, 532, 532, 532, 532, 532, 532, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, - -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, + -371, -371, -371, -371, -371, 533, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, -371 }, { - 33, -372, -372, -372, -372, -372, -372, -372, -372, -372, + 55, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, - -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, - -372, -372, 373, -372, -372, -372, -372, -372, -372, -372, - -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, + + -372, -372, -372, -372, -372, -372, -372, -372, 534, 534, + 534, 534, 534, 534, 534, 534, 534, 534, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, @@ -6830,17 +6991,17 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -373, -373, -373, -373, -373, -373, -373, -373, -373, + 55, -373, -373, -373, -373, -373, -373, -373, -373, -373, - -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, - -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, - -373, -373, 374, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, + -373, -373, -373, -373, -373, 528, -373, -373, 529, 529, + 529, 529, 529, 529, 529, 529, 529, 529, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, + -373, -373, -373, -373, -373, 535, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, @@ -6848,12 +7009,12 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -374, -374, -374, -374, -374, -374, -374, -374, -374, - -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, + 55, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, - -374, -374, 375, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, + -374, -374, -374, -374, -374, -374, -374, -374, 536, 537, + 537, 537, 537, 537, 537, 537, 537, 537, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, @@ -6865,34 +7026,34 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -375, -375, -375, -375, -375, -375, -375, -375, -375, - -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, - -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, - -375, -375, 376, -375, -375, -375, -375, -375, -375, -375, - -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, - - -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, - -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, - -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, + 55, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, + -375, -375, 538, -375, -375, -375, -375, -375, -375, -375, + -375, -375, -375, -375, -375, -375, -375, -375, 539, 539, + + 539, 539, 539, 539, 539, 539, 539, 539, -375, -375, + -375, -375, -375, -375, -375, 538, 538, 538, 538, 538, + 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, + 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, + 538, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375, -375 }, { - 33, -376, -376, -376, -376, -376, -376, -376, -376, -376, + 55, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, - -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, - -376, -376, 377, -376, -376, -376, -376, -376, -376, -376, - -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, + -376, -376, -376, -376, -376, 528, -376, -376, 529, 529, + 529, 529, 529, 529, 529, 529, 529, 529, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, + -376, -376, -376, -376, -376, 531, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, -376, @@ -6900,12 +7061,12 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -377, -377, -377, -377, -377, -377, -377, -377, -377, - -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, + 55, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, - -377, -377, 378, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, + -377, -377, -377, -377, -377, -377, -377, -377, 540, 541, + 541, 541, 541, 541, 541, 541, 541, 541, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, -377, @@ -6917,34 +7078,34 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -378, -378, -378, -378, -378, -378, -378, -378, -378, - -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, - -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, - -378, -378, 379, -378, -378, -378, -378, -378, -378, -378, - -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, - -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, - - -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, - -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, + 55, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, + -378, -378, 542, -378, -378, -378, -378, -378, -378, -378, + -378, -378, -378, -378, -378, -378, -378, -378, 543, 544, + 544, 544, 544, 544, 544, 544, 544, 544, -378, -378, + + -378, -378, -378, -378, -378, 542, 542, 542, 542, 542, + 542, 542, 542, 542, 542, 542, 542, 542, 542, 542, + 542, 542, 542, 542, 542, 542, 542, 542, 542, 542, + 542, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378, -378 }, { - 33, -379, -379, -379, -379, -379, -379, -379, -379, -379, + 55, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, - -379, -379, 380, -379, -379, -379, -379, -379, -379, -379, - -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, - -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, - -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, - -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, - -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, - -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, + -379, -379, 542, -379, -379, -379, -379, -379, -379, -379, + -379, -379, -379, -379, -379, -379, -379, -379, 545, 545, + 545, 545, 545, 545, 545, 545, 545, 545, -379, -379, + -379, -379, -379, -379, -379, 542, 542, 542, 542, 542, + 542, 542, 542, 542, 542, 542, 542, 542, 542, 542, + 542, 542, 542, 542, 542, 542, 542, 542, 542, 542, + 542, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379, -379 @@ -6952,10 +7113,10 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -380, -380, -380, -380, -380, -380, -380, -380, -380, + 55, -380, -380, -380, -380, -380, -380, -380, -380, -380, + -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, - -380, -380, 381, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, -380, @@ -6969,10 +7130,10 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -381, -381, -381, -381, -381, -381, -381, -381, -381, + 55, -381, -381, -381, -381, -381, -381, -381, -381, -381, + -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, - -381, -381, 382, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, -381, @@ -6986,10 +7147,10 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -382, -382, -382, -382, -382, -382, -382, -382, -382, + 55, -382, -382, -382, -382, -382, -382, -382, -382, -382, + -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, - -382, -382, 383, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, -382, @@ -7003,11 +7164,11 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -383, -383, -383, -383, -383, -383, -383, -383, -383, + 55, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, - -383, -383, 384, -383, -383, -383, -383, -383, -383, -383, + -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, -383, @@ -7021,10 +7182,10 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -384, -384, -384, -384, -384, -384, -384, -384, -384, + 55, -384, -384, -384, -384, -384, -384, -384, -384, -384, + -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, - -384, -384, 385, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, -384, @@ -7038,10 +7199,10 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -385, -385, -385, -385, -385, -385, -385, -385, -385, + 55, -385, -385, -385, -385, -385, -385, -385, -385, -385, + -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, - -385, -385, 386, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, -385, @@ -7055,11 +7216,11 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -386, -386, -386, -386, -386, -386, -386, -386, -386, + 55, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, - -386, -386, 387, -386, -386, -386, -386, -386, -386, -386, + -386, -386, 546, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, -386, @@ -7073,10 +7234,10 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -387, -387, -387, -387, -387, -387, -387, -387, -387, + 55, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, - -387, -387, 388, -387, -387, -387, -387, -387, -387, -387, + -387, -387, 547, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, -387, @@ -7090,10 +7251,10 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -388, -388, -388, -388, -388, -388, -388, -388, -388, + 55, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, - -388, -388, 389, -388, -388, -388, -388, -388, -388, -388, + -388, -388, 548, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, @@ -7107,11 +7268,11 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -389, -389, -389, -389, -389, -389, -389, -389, -389, + 55, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, - -389, -389, 390, -389, -389, -389, -389, -389, -389, -389, + -389, -389, 549, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, -389, @@ -7125,47 +7286,47 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -390, -390, -390, -390, -390, -390, -390, -390, -390, - -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, - -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, - -390, -390, 391, -390, -390, -390, -390, -390, -390, -390, - -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, - -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, - -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, - -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, - -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, - -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, + 55, 550, 550, 550, 550, 550, 550, 550, 550, 550, + 550, 550, 550, 550, 550, 550, 550, 550, 550, 550, + 550, 550, 550, 550, 550, 550, 550, 550, 550, 550, + 550, 550, 550, 550, 550, 550, 550, 550, 550, 551, + 550, 550, 550, 550, 550, 550, 550, 550, 550, 550, + 550, 550, 550, 550, 550, 550, 550, 550, 550, 550, + 550, 550, 550, 550, 550, 550, 550, 550, 550, 550, + 550, 550, 550, 550, 550, 550, 550, 550, 550, 550, + 550, 550, 550, 550, 550, 550, 550, 550, 550, 550, + 550, 550, 550, 550, 550, 550, 550, 550, 550, 550, - -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, - -390, -390, -390, -390, -390, -390, -390, -390, -390, -390, - -390, -390, -390, -390, -390, -390, -390, -390 + 550, 550, 550, 550, 550, 550, 550, 550, 550, 550, + 550, 550, 550, 550, 550, 550, 550, 550, 550, 550, + 550, 550, 550, 550, 550, 550, 550, 550 }, { - 33, -391, -391, -391, -391, -391, -391, -391, -391, -391, - -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, - -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, - -391, -391, 392, -391, -391, -391, -391, -391, -391, -391, + 55, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, + -391, -391, -391, -391, -391, -391, -391, -391, 391, 391, + 391, 391, 391, 391, 391, 391, 391, 391, -391, -391, + -391, -391, -391, -391, -391, -391, -391, -391, 235, 235, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, - -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, + 235, 235, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391, -391 }, { - 33, -392, -392, -392, -392, -392, -392, -392, -392, -392, + 55, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, - -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, - -392, -392, 393, -392, -392, -392, -392, -392, -392, -392, - -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, + + -392, -392, -392, -392, -392, -392, -392, -392, 393, 393, + 393, 393, 393, 393, 393, 393, 393, 393, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, -392, @@ -7176,13 +7337,13 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -393, -393, -393, -393, -393, -393, -393, -393, -393, + 55, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, - -393, -393, 394, -393, -393, -393, -393, -393, -393, -393, - -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, + -393, -393, -393, -393, -393, -393, -393, -393, 393, 393, + 393, 393, 393, 393, 393, 393, 393, 393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, -393, @@ -7194,30 +7355,30 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -394, -394, -394, -394, -394, -394, -394, -394, -394, - -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, - -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, - -394, -394, 395, -394, -394, -394, -394, -394, -394, -394, + 55, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, + -394, -394, -394, -394, -394, -394, -394, -394, 394, 394, + 394, 394, 394, 394, 394, 394, 394, 394, -394, -394, + -394, -394, -394, -394, -394, -394, -394, -394, 241, 241, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, - -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, + 241, 241, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394, -394 }, { - 33, -395, -395, -395, -395, -395, -395, -395, -395, -395, + 55, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, - -395, -395, 396, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, + -395, -395, -395, -395, -395, -395, -395, -395, 396, 396, - -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, + 396, 396, 396, 396, 396, 396, 396, 396, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, -395, @@ -7228,13 +7389,13 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -396, -396, -396, -396, -396, -396, -396, -396, -396, + 55, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, - -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, - -396, -396, 397, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, + -396, -396, -396, -396, -396, -396, -396, -396, 396, 396, + 396, 396, 396, 396, 396, 396, 396, 396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, -396, @@ -7246,29 +7407,29 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -397, -397, -397, -397, -397, -397, -397, -397, -397, - -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, - -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, - -397, -397, 398, -397, -397, -397, -397, -397, -397, -397, + 55, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, + -397, -397, -397, -397, -397, -397, -397, -397, 397, 397, + 397, 397, 397, 397, 397, 397, 397, 397, -397, -397, + -397, -397, -397, -397, -397, -397, -397, -397, 253, 253, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, - -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, + 253, 253, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397, -397 }, { - 33, -398, -398, -398, -398, -398, -398, -398, -398, -398, + 55, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, - -398, -398, 399, -398, -398, -398, -398, -398, -398, -398, - -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, + -398, -398, -398, -398, -398, -398, -398, -398, 399, 399, + 399, 399, 399, 399, 399, 399, 399, 399, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, -398, @@ -7280,13 +7441,13 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -399, -399, -399, -399, -399, -399, -399, -399, -399, + 55, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, - -399, -399, 400, -399, -399, -399, -399, -399, -399, -399, - -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, + -399, -399, -399, -399, -399, -399, -399, -399, 399, 399, + 399, 399, 399, 399, 399, 399, 399, 399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, -399, @@ -7298,44 +7459,44 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -400, -400, -400, -400, -400, -400, -400, -400, -400, - -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, - -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, - -400, -400, 401, -400, -400, -400, -400, -400, -400, -400, - -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, - -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, - -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, - -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, - -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, - -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, + 55, 400, 400, 400, 400, 400, 400, 400, 400, 400, + 552, 400, 400, 400, 400, 400, 400, 400, 400, 400, + 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, + 400, 400, 553, 400, 400, 400, 400, 400, 400, 400, + 400, 400, 400, 400, 400, 400, 400, 554, 400, 400, + 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, + 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, + 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, + 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, + 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, - -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, - -400, -400, -400, -400, -400, -400, -400, -400, -400, -400, - -400, -400, -400, -400, -400, -400, -400, -400 + 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, + 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, + 400, 400, 400, 400, 400, 400, 400, 400 }, { - 33, -401, -401, -401, -401, -401, -401, -401, -401, -401, - -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, - -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, - -401, -401, 402, -401, -401, -401, -401, -401, -401, -401, - -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, - -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, - -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, + 55, 401, 401, 401, 401, 401, 401, 401, 401, 401, + 402, 401, 401, 401, 401, 401, 401, 401, 401, 401, + 401, 401, 401, 401, 401, 401, 401, 401, 401, 401, + 401, 401, 403, 401, 401, 401, 401, 401, 401, 401, + 401, 401, 401, 401, 401, 401, 401, 404, 401, 401, + 401, 401, 401, 401, 401, 401, 401, 401, 401, 401, + 401, 401, 401, 401, 401, 401, 401, 401, 401, 401, - -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, - -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, - -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, - -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, - -401, -401, -401, -401, -401, -401, -401, -401, -401, -401, - -401, -401, -401, -401, -401, -401, -401, -401 + 401, 401, 401, 401, 401, 401, 401, 401, 401, 401, + 401, 401, 401, 401, 401, 401, 401, 401, 401, 401, + 401, 401, 401, 401, 401, 401, 401, 401, 401, 401, + 401, 401, 401, 401, 401, 401, 401, 401, 401, 401, + 401, 401, 401, 401, 401, 401, 401, 401, 401, 401, + 401, 401, 401, 401, 401, 401, 401, 401 }, { - 33, -402, -402, -402, -402, -402, -402, -402, -402, -402, + 55, -402, -402, -402, -402, -402, -402, -402, -402, -402, + -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, - -402, -402, 403, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, @@ -7349,63 +7510,63 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -403, -403, -403, -403, -403, -403, -403, -403, -403, + 55, 401, 401, 401, 401, 401, 401, 401, 401, 401, - -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, - -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, - -403, -403, 404, -403, -403, -403, -403, -403, -403, -403, - -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, - -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, - -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, - -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, - -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, - -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, - -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, + 402, 401, 401, 401, 401, 401, 401, 401, 401, 401, + 401, 401, 401, 401, 401, 401, 401, 401, 401, 401, + 401, 401, 403, 401, 401, 401, 401, 401, 401, 401, + 401, 401, 401, 401, 401, 401, 401, 404, 401, 401, + 401, 401, 401, 401, 401, 401, 401, 401, 401, 401, + 401, 401, 401, 401, 401, 401, 401, 401, 401, 401, + 401, 401, 401, 401, 401, 401, 401, 401, 401, 401, + 401, 401, 401, 401, 401, 401, 401, 401, 401, 401, + 401, 401, 401, 401, 401, 401, 401, 401, 401, 401, + 401, 401, 401, 401, 401, 401, 401, 401, 401, 401, - -403, -403, -403, -403, -403, -403, -403, -403, -403, -403, - -403, -403, -403, -403, -403, -403, -403, -403 + 401, 401, 401, 401, 401, 401, 401, 401, 401, 401, + 401, 401, 401, 401, 401, 401, 401, 401 }, { - 33, -404, -404, -404, -404, -404, -404, -404, -404, -404, - -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, - -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, - -404, -404, 405, -404, -404, -404, -404, -404, -404, -404, - -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, - -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, - -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, - -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, + 55, 401, 401, 401, 401, 401, 401, 401, 401, 401, + 402, 401, 401, 401, 401, 401, 401, 401, 401, 401, + 401, 401, 401, 401, 401, 401, 401, 401, 401, 401, + 401, 401, 403, 401, 401, 401, 401, 401, 401, 401, + 401, 401, 401, 401, 401, 401, 401, 404, 401, 401, + 401, 401, 401, 401, 401, 401, 401, 401, 401, 401, + 401, 401, 401, 401, 401, 401, 401, 401, 401, 401, + 401, 401, 401, 401, 401, 401, 401, 401, 401, 401, - -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, - -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, - -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, - -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, - -404, -404, -404, -404, -404, -404, -404, -404 + 401, 401, 401, 401, 401, 401, 401, 401, 401, 401, + 401, 401, 401, 401, 401, 401, 401, 401, 401, 401, + 401, 401, 401, 401, 401, 401, 401, 401, 401, 401, + 401, 401, 401, 401, 401, 401, 401, 401, 401, 401, + 401, 401, 401, 401, 401, 401, 401, 401 }, { - 33, -405, -405, -405, -405, -405, -405, -405, -405, -405, - -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, - -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, - -405, -405, 406, -405, -405, -405, -405, -405, -405, -405, - -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, + 55, 405, 405, 405, 405, 405, 405, 405, 405, 405, + 406, 405, 405, 405, 405, 405, 405, 405, 405, 405, + 405, 405, 405, 405, 405, 405, 405, 405, 405, 405, + 405, 405, 407, 405, 405, 405, 405, 405, 405, 405, + 405, 405, 405, 405, 405, 405, 405, 408, 405, 405, - -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, - -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, - -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, - -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, - -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, - -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, - -405, -405, -405, -405, -405, -405, -405, -405, -405, -405, - -405, -405, -405, -405, -405, -405, -405, -405 + 405, 405, 405, 405, 405, 405, 405, 405, 405, 405, + 405, 405, 405, 405, 405, 405, 405, 405, 405, 405, + 405, 405, 405, 405, 405, 405, 405, 405, 405, 405, + 405, 405, 405, 405, 405, 405, 405, 405, 405, 405, + 405, 405, 405, 405, 405, 405, 405, 405, 405, 405, + 405, 405, 405, 405, 405, 405, 405, 405, 405, 405, + 405, 405, 405, 405, 405, 405, 405, 405, 405, 405, + 405, 405, 405, 405, 405, 405, 405, 405 }, { - 33, -406, -406, -406, -406, -406, -406, -406, -406, -406, + 55, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, - -406, -406, 407, -406, -406, -406, -406, -406, -406, -406, + -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, -406, @@ -7419,62 +7580,62 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -407, -407, -407, -407, -407, -407, -407, -407, -407, - -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, - -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, - -407, -407, 408, -407, -407, -407, -407, -407, -407, -407, - -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, - -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, - -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, - -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, - -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, + 55, 405, 405, 405, 405, 405, 405, 405, 405, 405, + 406, 405, 405, 405, 405, 405, 405, 405, 405, 405, + 405, 405, 405, 405, 405, 405, 405, 405, 405, 405, + 405, 405, 407, 405, 405, 405, 405, 405, 405, 405, + 405, 405, 405, 405, 405, 405, 405, 408, 405, 405, + 405, 405, 405, 405, 405, 405, 405, 405, 405, 405, + 405, 405, 405, 405, 405, 405, 405, 405, 405, 405, + 405, 405, 405, 405, 405, 405, 405, 405, 405, 405, + 405, 405, 405, 405, 405, 405, 405, 405, 405, 405, - -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, - -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, - -407, -407, -407, -407, -407, -407, -407, -407, -407, -407, - -407, -407, -407, -407, -407, -407, -407, -407 + 405, 405, 405, 405, 405, 405, 405, 405, 405, 405, + 405, 405, 405, 405, 405, 405, 405, 405, 405, 405, + 405, 405, 405, 405, 405, 405, 405, 405, 405, 405, + 405, 405, 405, 405, 405, 405, 405, 405 }, { - 33, -408, -408, -408, -408, -408, -408, -408, -408, -408, - -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, - -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, - -408, -408, 409, -408, -408, -408, -408, -408, -408, -408, - -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, - -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, + 55, 555, 555, 555, 555, 555, 555, 555, 555, 555, + 556, 555, 555, 555, 555, 555, 555, 555, 555, 555, + 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, + 555, 555, 557, 555, 555, 555, 555, 555, 555, 555, + 555, 555, 555, 555, 555, 555, 555, 558, 555, 555, + 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, - -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, - -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, - -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, - -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, - -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, - -408, -408, -408, -408, -408, -408, -408, -408, -408, -408, - -408, -408, -408, -408, -408, -408, -408, -408 + 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, + 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, + 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, + 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, + 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, + 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, + 555, 555, 555, 555, 555, 555, 555, 555 }, { - 33, -409, -409, -409, -409, -409, -409, -409, -409, -409, - -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, - -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, + 55, 405, 405, 405, 405, 405, 405, 405, 405, 405, + 406, 405, 405, 405, 405, 405, 405, 405, 405, 405, + 405, 405, 405, 405, 405, 405, 405, 405, 405, 405, - -409, -409, 410, -409, -409, -409, -409, -409, -409, -409, - -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, - -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, - -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, - -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, - -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, - -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, - -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, - -409, -409, -409, -409, -409, -409, -409, -409, -409, -409, - -409, -409, -409, -409, -409, -409, -409, -409 + 405, 405, 407, 405, 405, 405, 405, 405, 405, 405, + 405, 405, 405, 405, 405, 405, 405, 408, 405, 405, + 405, 405, 405, 405, 405, 405, 405, 405, 405, 405, + 405, 405, 405, 405, 405, 405, 405, 405, 405, 405, + 405, 405, 405, 405, 405, 405, 405, 405, 405, 405, + 405, 405, 405, 405, 405, 405, 405, 405, 405, 405, + 405, 405, 405, 405, 405, 405, 405, 405, 405, 405, + 405, 405, 405, 405, 405, 405, 405, 405, 405, 405, + 405, 405, 405, 405, 405, 405, 405, 405, 405, 405, + 405, 405, 405, 405, 405, 405, 405, 405 }, { - 33, -410, -410, -410, -410, -410, -410, -410, -410, -410, + 55, -410, -410, -410, -410, -410, -410, -410, -410, -410, + -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, - -410, -410, 411, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, -410, @@ -7488,45 +7649,45 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -411, -411, -411, -411, -411, -411, -411, -411, -411, - -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, - -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, - -411, -411, 412, -411, -411, -411, -411, -411, -411, -411, - -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, - -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, - -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, + 55, 409, 409, 409, 409, 409, 409, 409, 409, 409, + 410, 409, 409, 409, 409, 409, 409, 409, 409, 409, + 409, 409, 409, 409, 409, 409, 409, 409, 409, 409, + 409, 409, 411, 409, 409, 409, 409, 409, 409, 409, + 409, 409, 409, 409, 409, 409, 409, 412, 409, 409, + 409, 409, 409, 409, 409, 409, 409, 409, 409, 409, + 409, 409, 409, 409, 409, 409, 409, 409, 409, 409, - -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, - -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, - -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, - -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, - -411, -411, -411, -411, -411, -411, -411, -411, -411, -411, - -411, -411, -411, -411, -411, -411, -411, -411 + 409, 409, 409, 409, 409, 409, 409, 409, 409, 409, + 409, 409, 409, 409, 409, 409, 409, 409, 409, 409, + 409, 409, 409, 409, 409, 409, 409, 409, 409, 409, + 409, 409, 409, 409, 409, 409, 409, 409, 409, 409, + 409, 409, 409, 409, 409, 409, 409, 409, 409, 409, + 409, 409, 409, 409, 409, 409, 409, 409 }, { - 33, -412, -412, -412, -412, -412, -412, -412, -412, -412, - -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, - -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, - -412, -412, 413, -412, -412, -412, -412, -412, -412, -412, + 55, 559, 559, 559, 559, 559, 559, 559, 559, 559, + 560, 559, 559, 559, 559, 559, 559, 559, 559, 559, + 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, + 559, 559, 561, 559, 559, 559, 559, 559, 559, 559, - -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, - -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, - -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, - -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, - -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, - -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, - -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, - -412, -412, -412, -412, -412, -412, -412, -412, -412, -412, - -412, -412, -412, -412, -412, -412, -412, -412 + 559, 559, 559, 559, 559, 559, 559, 562, 559, 559, + 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, + 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, + 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, + 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, + 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, + 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, + 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, + 559, 559, 559, 559, 559, 559, 559, 559 }, { - 33, -413, -413, -413, -413, -413, -413, -413, -413, -413, + 55, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, - -413, -413, 414, -413, -413, -413, -413, -413, -413, -413, + -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, -413, @@ -7540,10 +7701,10 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -414, -414, -414, -414, -414, -414, -414, -414, -414, + 55, -414, -414, -414, -414, -414, -414, -414, -414, -414, + -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, - -414, -414, 415, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, -414, @@ -7557,16 +7718,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -415, -415, -415, -415, -415, -415, -415, -415, -415, + 55, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, - -415, -415, 416, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, - -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, + -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, + -415, -415, 563, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, @@ -7574,14 +7735,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -416, -416, -416, -416, -416, -416, -416, -416, -416, + 55, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, - -416, -416, 417, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, + -416, -416, -416, -416, -416, 564, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, -416, @@ -7592,13 +7753,13 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -417, -417, -417, -417, -417, -417, -417, -417, -417, + 55, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, - -417, -417, 418, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, + -417, -417, -417, -417, -417, -417, -417, -417, 565, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, -417, @@ -7609,14 +7770,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -418, -418, -418, -418, -418, -418, -418, -418, -418, + 55, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, - -418, -418, 419, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, - -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, + + -418, -418, -418, -418, -418, -418, -418, -418, 566, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, -418, @@ -7626,17 +7787,17 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -419, -419, -419, -419, -419, -419, -419, -419, -419, + 55, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, - -419, -419, 420, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, + -419, -419, -419, -419, -419, 567, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419, -419 @@ -7644,13 +7805,13 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -420, -420, -420, -420, -420, -420, -420, -420, -420, + 55, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, - -420, -420, 421, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, + -420, -420, -420, -420, -420, -420, -420, 568, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, -420, @@ -7661,50 +7822,50 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -421, -421, -421, -421, -421, -421, -421, -421, -421, + 55, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, - -421, -421, 422, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, - -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, + -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, + -421, -421, -421, -421, -421, 569, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421, -421 }, { - 33, -422, -422, -422, -422, -422, -422, -422, -422, -422, + 55, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, - -422, -422, 423, -422, -422, -422, -422, -422, -422, -422, - -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, + -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, + -422, -422, -422, -422, -422, 570, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422, -422 }, { - 33, -423, -423, -423, -423, -423, -423, -423, -423, -423, + 55, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, - -423, -423, 424, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, + -423, -423, 571, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, -423, @@ -7713,13 +7874,13 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -424, -424, -424, -424, -424, -424, -424, -424, -424, + 55, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, - -424, -424, 425, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, + -424, -424, -424, -424, -424, 572, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, -424, @@ -7730,14 +7891,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -425, -425, -425, -425, -425, -425, -425, -425, -425, + 55, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, - -425, -425, 426, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, - -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, + -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, + -425, -425, -425, -425, -425, -425, -425, -425, 573, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, @@ -7747,14 +7908,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -426, -426, -426, -426, -426, -426, -426, -426, -426, + 55, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, - -426, -426, 427, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, + -426, -426, -426, -426, -426, -426, -426, -426, 574, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, -426, @@ -7765,15 +7926,15 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -427, -427, -427, -427, -427, -427, -427, -427, -427, + 55, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, - -427, -427, 428, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, + -427, -427, -427, -427, 575, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, -427, @@ -7782,14 +7943,14 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -428, -428, -428, -428, -428, -428, -428, -428, -428, + 55, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, - -428, -428, 429, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, - -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, + + -428, -428, -428, -428, -428, -428, -428, -428, -428, 576, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, -428, @@ -7799,16 +7960,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -429, -429, -429, -429, -429, -429, -429, -429, -429, + 55, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, - -429, -429, 430, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, + -429, -429, -429, -429, -429, -429, -429, -429, 577, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, -429, @@ -7817,15 +7978,15 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -430, -430, -430, -430, -430, -430, -430, -430, -430, + 55, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, - -430, -430, 431, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, + -430, -430, -430, 578, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, -430, @@ -7834,16 +7995,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -431, -431, -431, -431, -431, -431, -431, -431, -431, - -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, + 55, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, - -431, -431, 432, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, - -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, + + -431, -431, -431, 579, -431, -431, -431, -431, -431, -431, + -431, -431, 580, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, -431, @@ -7851,16 +8012,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -432, -432, -432, -432, -432, -432, -432, -432, -432, + 55, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, - -432, -432, 433, -432, -432, -432, -432, -432, -432, -432, - -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, + -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, + -432, -432, -432, 581, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, -432, @@ -7868,16 +8029,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -433, -433, -433, -433, -433, -433, -433, -433, -433, + 55, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, - -433, -433, 434, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, + -433, -433, 582, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, -433, @@ -7886,16 +8047,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -434, -434, -434, -434, -434, -434, -434, -434, -434, + 55, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, - -434, -434, 435, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, - -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, + + -434, -434, 583, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, -434, @@ -7903,34 +8064,34 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -435, -435, -435, -435, -435, -435, -435, -435, -435, + 55, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, - -435, -435, 436, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, - -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, + -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, + -435, -435, -435, -435, -435, 584, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435, -435 }, { - 33, -436, -436, -436, -436, -436, -436, -436, -436, -436, + 55, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, - -436, -436, 437, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, + -436, -436, -436, -436, -436, 585, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, -436, @@ -7938,13 +8099,13 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -437, -437, -437, -437, -437, -437, -437, -437, -437, + 55, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, - -437, -437, 438, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, + -437, -437, -437, -437, -437, 586, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, -437, @@ -7955,16 +8116,16 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -438, -438, -438, -438, -438, -438, -438, -438, -438, + 55, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, - -438, -438, 439, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, - -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, + -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, + -438, -438, -438, -438, -438, -438, -438, -438, 587, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, -438, @@ -7972,15 +8133,15 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -439, -439, -439, -439, -439, -439, -439, -439, -439, + 55, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, - -439, -439, 440, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, + -439, -439, -439, -439, -439, -439, 588, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, -439, @@ -7990,8 +8151,7 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 33, -440, -440, -440, -440, -440, -440, -440, -440, -440, - -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, + 55, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, @@ -7999,6 +8159,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, + -440, -440, 589, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, -440, @@ -8006,1181 +8167,15955 @@ static yyconst flex_int16_t yy_nxt[][128] = -440, -440, -440, -440, -440, -440, -440, -440 }, - } ; + { + 55, -441, -441, -441, -441, -441, -441, -441, -441, -441, + -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, + -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, + -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, + -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, + -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, + -441, -441, -441, -441, -441, -441, -441, -441, -441, 590, -static yy_state_type yy_get_previous_state (void ); -static yy_state_type yy_try_NUL_trans (yy_state_type current_state ); -static int yy_get_next_buffer (void ); -static void yy_fatal_error (yyconst char msg[] ); + -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, + -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, + -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, + -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, + -441, -441, -441, -441, -441, -441, -441, -441, -441, -441, + -441, -441, -441, -441, -441, -441, -441, -441 + }, -/* Done after the current pattern has been matched and before the - * corresponding action - sets up wcspihtext. - */ -#define YY_DO_BEFORE_ACTION \ - (yytext_ptr) = yy_bp; \ - wcspihleng = (size_t) (yy_cp - yy_bp); \ - (yy_hold_char) = *yy_cp; \ - *yy_cp = '\0'; \ - (yy_c_buf_p) = yy_cp; + { + 55, -442, -442, -442, -442, -442, -442, -442, -442, -442, + -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, + -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, + -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, -#define YY_NUM_RULES 94 -#define YY_END_OF_BUFFER 95 -/* This struct is not used in this scanner, - but its presence is necessary. */ -struct yy_trans_info - { - flex_int32_t yy_verify; - flex_int32_t yy_nxt; - }; -static yyconst flex_int16_t yy_accept[441] = - { 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, 89, 89, 91, 91, 92, 92, - 0, 0, 95, 94, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 63, 63, - 79, 79, 47, 47, 59, 59, 59, 77, 77, 66, - 64, 65, 81, 81, 83, 83, 82, 85, 85, 85, - 84, 87, 87, 90, 89, 88, 91, 92, 94, 93, - 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 4, 0, 18, 16, 0, + -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, + -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, + -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, + -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, + -442, -442, -442, -442, 591, -442, -442, -442, -442, -442, + -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, + -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, + -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, + -442, -442, -442, -442, -442, -442, -442, -442 + }, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 80, 82, 0, - 84, 84, 84, 84, 0, 0, 86, 89, 88, 88, - 91, 92, 0, 93, 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, 60, 61, 62, 78, 44, 45, 46, 0, 0, - 0, 0, 0, 0, 0, 0, 84, 0, 84, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + { + 55, -443, -443, -443, -443, -443, -443, -443, -443, -443, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 6, 19, 20, 7, 3, 10, 21, 9, 8, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 17, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 58, 57, 53, 56, + -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, + -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, + -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, + -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, + -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, + -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, + -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, + -443, -443, -443, 592, -443, -443, -443, -443, -443, -443, + -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, + -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, - 50, 51, 55, 48, 49, 52, 54, 76, 72, 75, - 69, 70, 74, 67, 68, 71, 73, 0, 0, 0, - 0, 25, 12, 11, 0, 0, 0, 0, 0, 31, - 0, 13, 15, 33, 34, 35, 36, 37, 0, 0, - 0, 40, 41, 22, 23, 0, 24, 26, 27, 0, - 28, 29, 30, 32, 14, 38, 39, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, - 2, 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, + -443, -443, -443, -443, -443, -443, -443, -443, -443, -443, + -443, -443, -443, -443, -443, -443, -443, -443 + }, - 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, 42 - } ; + { + 55, -444, -444, -444, -444, -444, -444, -444, -444, -444, + -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, + -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, + -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, + -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, + -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, + -444, -444, -444, -444, -444, -444, -444, -444, 593, -444, + -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, -static yy_state_type yy_last_accepting_state; -static char *yy_last_accepting_cpos; + -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, + -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, + -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, + -444, -444, -444, -444, -444, -444, -444, -444, -444, -444, + -444, -444, -444, -444, -444, -444, -444, -444 + }, -static yyconst yy_state_type yy_NUL_trans[441] = - { 0, - 34, 35, 49, 49, 51, 51, 53, 53, 55, 55, - 58, 58, 60, 60, 62, 62, 63, 63, 65, 65, - 68, 68, 72, 72, 74, 74, 77, 77, 78, 78, - 79, 79, 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, 126, 0, 0, 130, 131, 132, 133, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + { + 55, -445, -445, -445, -445, -445, -445, -445, -445, -445, + -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, + -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, + -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, + -445, -445, -445, -445, -445, 594, -445, -445, -445, -445, - 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, 126, 0, 0, 130, 130, - 131, 132, 133, 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, 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, + -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, + -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, + -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, + -445, -445, 595, -445, -445, -445, -445, -445, -445, -445, + -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, + -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, + -445, -445, -445, -445, -445, -445, -445, -445, -445, -445, + -445, -445, -445, -445, -445, -445, -445, -445 + }, - 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, - 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, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + { + 55, -446, -446, -446, -446, -446, -446, -446, -446, -446, + -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, - 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, - 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, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, + -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, + -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, + -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, + -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, + -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, + -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, + -446, -446, -446, -446, -446, 596, -446, -446, -446, -446, + -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, + -446, -446, -446, -446, -446, -446, -446, -446, -446, -446, + + -446, -446, -446, -446, -446, -446, -446, -446 + }, + + { + 55, -447, -447, -447, -447, -447, -447, -447, -447, -447, + -447, -447, -447, -447, -447, -447, -447, -447, -447, -447, + -447, -447, -447, -447, -447, -447, -447, -447, -447, -447, + -447, -447, -447, -447, -447, -447, -447, -447, -447, -447, + -447, -447, -447, -447, -447, -447, -447, -447, -447, -447, + -447, -447, -447, -447, -447, -447, -447, -447, -447, -447, + -447, -447, -447, -447, -447, -447, -447, -447, -447, -447, + -447, -447, -447, -447, -447, -447, -447, -447, -447, -447, + -447, -447, 597, -447, -447, -447, -447, -447, -447, -447, - 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 - } ; + -447, -447, -447, -447, -447, -447, -447, -447, -447, -447, + -447, -447, -447, -447, -447, -447, -447, -447, -447, -447, + -447, -447, -447, -447, -447, -447, -447, -447, -447, -447, + -447, -447, -447, -447, -447, -447, -447, -447 + }, + + { + 55, -448, -448, -448, -448, -448, -448, -448, -448, -448, + -448, -448, -448, -448, -448, -448, -448, -448, -448, -448, + -448, -448, -448, -448, -448, -448, -448, -448, -448, -448, + -448, -448, 598, -448, -448, -448, -448, -448, -448, -448, + -448, -448, -448, -448, -448, -448, -448, -448, -448, -448, + -448, -448, -448, -448, -448, -448, -448, -448, -448, -448, -extern int wcspih_flex_debug; -int wcspih_flex_debug = 0; + -448, -448, -448, -448, -448, -448, -448, -448, -448, -448, + -448, -448, -448, -448, -448, -448, -448, -448, -448, -448, + -448, -448, -448, -448, -448, -448, -448, -448, -448, -448, + -448, -448, -448, -448, -448, -448, -448, -448, -448, -448, + -448, -448, -448, -448, -448, -448, -448, -448, -448, -448, + -448, -448, -448, -448, -448, -448, -448, -448, -448, -448, + -448, -448, -448, -448, -448, -448, -448, -448 + }, + + { + 55, -449, -449, -449, -449, -449, -449, -449, -449, -449, + -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, + -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -/* The intent behind this definition is that it'll catch - * any uses of REJECT which flex missed. - */ -#define REJECT reject_used_but_not_detected -#define yymore() yymore_used_but_not_detected -#define YY_MORE_ADJ 0 -#define YY_RESTORE_YY_MORE_OFFSET -char *wcspihtext; -#line 1 "wcspih.l" -/*============================================================================ + -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, + -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, + -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, + -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, + -449, -449, 599, -449, -449, -449, -449, -449, -449, -449, + -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, + -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, + -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, + -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, + -449, -449, -449, -449, -449, -449, -449, -449 + + }, + + { + 55, -450, -450, -450, -450, -450, -450, -450, -450, -450, + -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, + -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, + -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, + -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, + -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, + -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, + -450, -450, -450, -450, -450, -450, -450, -450, 600, -450, + -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, + -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, - WCSLIB 4.14 - an implementation of the FITS WCS standard. - Copyright (C) 1995-2012, Mark Calabretta + -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, + -450, -450, -450, -450, -450, -450, -450, -450, -450, -450, + -450, -450, -450, -450, -450, -450, -450, -450 + }, + + { + 55, -451, -451, -451, -451, -451, -451, -451, -451, -451, + -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, + -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, + -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, + -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, + -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, + -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, - This file is part of WCSLIB. + -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, + -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, + -451, -451, -451, -451, -451, 601, -451, -451, -451, -451, + -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, + -451, -451, -451, -451, -451, -451, -451, -451, -451, -451, + -451, -451, -451, -451, -451, -451, -451, -451 + }, + + { + 55, -452, -452, -452, -452, -452, -452, -452, -452, -452, + -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, + -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, + -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, - WCSLIB is free software: you can redistribute it and/or modify it under the - terms of the GNU Lesser General Public License as published by the Free - Software Foundation, either version 3 of the License, or (at your option) - any later version. + -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, + -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, + -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, + -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, + -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, + -452, -452, -452, -452, -452, 602, -452, -452, -452, -452, + -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, + -452, -452, -452, -452, -452, -452, -452, -452, -452, -452, + -452, -452, -452, -452, -452, -452, -452, -452 + }, + + { + 55, -453, -453, -453, -453, -453, -453, -453, -453, -453, + + -453, -453, -453, -453, -453, -453, -453, -453, -453, -453, + -453, -453, -453, -453, -453, -453, -453, -453, -453, -453, + -453, -453, -453, -453, -453, -453, -453, -453, -453, -453, + -453, -453, -453, -453, -453, -453, -453, -453, -453, -453, + -453, -453, -453, -453, -453, -453, -453, -453, -453, -453, + -453, -453, -453, -453, -453, -453, -453, -453, -453, 603, + -453, -453, -453, -453, -453, -453, -453, -453, -453, -453, + -453, -453, -453, -453, -453, -453, -453, -453, -453, -453, + -453, -453, -453, -453, -453, -453, -453, -453, -453, -453, + -453, -453, -453, -453, -453, -453, -453, -453, -453, -453, - WCSLIB is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for - more details. + -453, -453, -453, -453, -453, -453, -453, -453, -453, -453, + -453, -453, -453, -453, -453, -453, -453, -453 + }, + + { + 55, -454, -454, -454, -454, -454, -454, -454, -454, -454, + -454, -454, -454, -454, -454, -454, -454, -454, -454, -454, + -454, -454, -454, -454, -454, -454, -454, -454, -454, -454, + -454, -454, -454, -454, -454, -454, -454, -454, -454, -454, + -454, -454, -454, -454, -454, -454, -454, -454, -454, -454, + -454, -454, -454, -454, -454, -454, -454, -454, -454, -454, + -454, -454, -454, -454, -454, -454, -454, -454, -454, -454, + 604, -454, -454, -454, -454, -454, -454, -454, -454, -454, - You should have received a copy of the GNU Lesser General Public License - along with WCSLIB. If not, see http://www.gnu.org/licenses. + -454, -454, -454, -454, -454, -454, -454, -454, -454, -454, + -454, -454, -454, -454, -454, -454, -454, -454, -454, -454, + -454, -454, -454, -454, -454, -454, -454, -454, -454, -454, + -454, -454, -454, -454, -454, -454, -454, -454, -454, -454, + -454, -454, -454, -454, -454, -454, -454, -454 + }, + + { + 55, -455, -455, -455, -455, -455, -455, -455, -455, -455, + -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, + -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, + -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, + -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, - Direct correspondence concerning WCSLIB to mark@calabretta.id.au + -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, + -455, -455, -455, -455, -455, -455, -455, 605, -455, -455, + -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, + -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, + -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, + -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, + -455, -455, -455, -455, -455, -455, -455, -455, -455, -455, + -455, -455, -455, -455, -455, -455, -455, -455 + }, + + { + 55, -456, -456, -456, -456, -456, -456, -456, -456, -456, + -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, - Author: Mark Calabretta, Australia Telescope National Facility, CSIRO. - http://www.atnf.csiro.au/people/Mark.Calabretta - $Id: wcspih.c,v 4.14 2012/07/13 10:02:31 cal103 Exp $ -*============================================================================= -* -* wcspih.l is a Flex description file containing the definition of a lexical -* scanner for parsing the WCS keyrecords from a FITS primary image or image -* extension header. -* -* wcspih.l requires Flex v2.5.4 or later. Refer to wcshdr.h for a description -* of the user interface and operating notes. -* -* Implementation notes -* -------------------- -* Use of the WCSAXESa keyword is not mandatory. Its default value is "the -* larger of NAXIS and the largest index of these keywords [i.e. CRPIXj, PCi_j -* or CDi_j, CDELTi, CTYPEi, CRVALi, and CUNITi] found in the FITS header". -* Consequently the definition of WCSAXESa effectively invalidates the use of -* NAXIS for determining the number of coordinate axes and forces a preliminary -* pass through the header to determine the "largest index" in headers where -* WCSAXESa was omitted. -* -* Furthermore, since the use of WCSAXESa is optional, there is no way to -* determine the number of coordinate representations (the "a" value) other -* than by parsing all of the WCS keywords in the header; even if WCSAXESa was -* specified for some representations it cannot be known in advance whether it -* was specified for all of those present in the header. -* -* Hence the definition of WCSAXESa forces the scanner to be implemented in two -* passes. The first pass is used to determine the number of coordinate -* representations (up to 27) and the number of coordinate axes in each. -* Effectively WCSAXESa is ignored unless it exceeds the "largest index" in -* which case the keywords for the extra axes assume their default values. The -* number of PVi_ma and PSi_ma keywords in each representation is also counted -* in the first pass. -* -* On completion of the first pass, memory is allocated for an array of the -* required number of wcsprm structs and each of these is initialized -* appropriately. These structs are filled in the second pass. -* -* The parser does not check for duplicated keywords, it accepts the last -* encountered. -* -*===========================================================================*/ -/* Options. */ -/* Indices for parameterized keywords. */ -/* Alternate coordinate system identifier. */ -/* Keyvalue data types. */ -/* Exclusive start states. */ + -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, + -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, + -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, + -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, + -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, + -456, -456, -456, -456, -456, -456, -456, -456, -456, 606, + -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, + -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, + -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, + -456, -456, -456, -456, -456, -456, -456, -456, -456, -456, + + -456, -456, -456, -456, -456, -456, -456, -456 + }, + + { + 55, -457, -457, -457, -457, -457, -457, -457, -457, -457, + -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, + -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, + -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, + -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, + -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, + -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, + -457, -457, -457, -457, -457, -457, -457, -457, -457, 607, + -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, + -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, + -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, + -457, -457, -457, -457, -457, -457, -457, -457, -457, -457, + -457, -457, -457, -457, -457, -457, -457, -457 + }, + + { + 55, -458, -458, -458, -458, -458, -458, -458, -458, -458, + -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, + -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, + -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, + -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, + -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, + -458, -458, -458, -458, -458, 608, 609, -458, -458, 610, + -458, -458, -458, -458, -458, -458, -458, -458, -458, 611, + -458, -458, 612, -458, -458, -458, -458, -458, -458, -458, + -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, + -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, + -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, + -458, -458, -458, -458, -458, -458, -458, -458 + }, + + { + 55, -459, -459, -459, -459, -459, -459, -459, -459, -459, + -459, -459, -459, -459, -459, -459, -459, -459, -459, -459, + -459, -459, -459, -459, -459, -459, -459, -459, -459, -459, + -459, -459, -459, -459, -459, -459, -459, -459, -459, -459, + -459, -459, -459, -459, -459, -459, -459, -459, -459, -459, + -459, -459, -459, -459, -459, -459, -459, -459, -459, -459, + -459, -459, -459, -459, -459, -459, -459, -459, -459, 613, + -459, -459, -459, -459, -459, -459, -459, -459, -459, -459, + -459, -459, -459, -459, -459, -459, -459, -459, -459, -459, + -459, -459, -459, -459, -459, -459, -459, -459, -459, -459, + -459, -459, -459, -459, -459, -459, -459, -459, -459, -459, + -459, -459, -459, -459, -459, -459, -459, -459, -459, -459, + -459, -459, -459, -459, -459, -459, -459, -459 + + }, + + { + 55, -460, -460, -460, -460, -460, -460, -460, -460, -460, + -460, -460, -460, -460, -460, -460, -460, -460, -460, -460, + -460, -460, -460, -460, -460, -460, -460, -460, -460, -460, + -460, -460, -460, -460, -460, -460, -460, -460, -460, -460, + -460, -460, -460, -460, -460, -460, -460, -460, -460, -460, + -460, -460, -460, -460, -460, -460, -460, -460, -460, -460, + -460, -460, -460, -460, -460, -460, -460, -460, -460, -460, + -460, -460, -460, -460, -460, -460, -460, -460, -460, -460, + -460, -460, -460, 614, -460, -460, -460, -460, -460, -460, + -460, -460, -460, -460, -460, -460, -460, -460, -460, -460, + -460, -460, -460, -460, -460, -460, -460, -460, -460, -460, + -460, -460, -460, -460, -460, -460, -460, -460, -460, -460, + -460, -460, -460, -460, -460, -460, -460, -460 + }, + + { + 55, -461, -461, -461, -461, -461, -461, -461, -461, -461, + -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, + -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, + -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, + -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, + -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, + -461, -461, -461, -461, -461, -461, -461, -461, -461, 615, + -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, + -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, + -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, + -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, + -461, -461, -461, -461, -461, -461, -461, -461, -461, -461, + -461, -461, -461, -461, -461, -461, -461, -461 + }, + + { + 55, -462, -462, -462, -462, -462, -462, -462, -462, -462, + -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, + -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, + -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, -#line 99 "wcspih.l" -#include -#include -#include -#include -#include + -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, + -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, + -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, + -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, + -462, -462, 616, -462, -462, -462, -462, -462, -462, -462, + -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, + -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, + -462, -462, -462, -462, -462, -462, -462, -462, -462, -462, + -462, -462, -462, -462, -462, -462, -462, -462 + }, + + { + 55, -463, -463, -463, -463, -463, -463, -463, -463, -463, + + -463, -463, -463, -463, -463, -463, -463, -463, -463, -463, + -463, -463, -463, -463, -463, -463, -463, -463, -463, -463, + -463, -463, -463, -463, -463, -463, -463, -463, -463, -463, + -463, -463, -463, -463, -463, -463, -463, -463, -463, -463, + -463, -463, -463, -463, -463, -463, -463, -463, -463, -463, + -463, -463, -463, -463, -463, -463, -463, -463, -463, 617, + -463, -463, -463, -463, -463, -463, -463, -463, -463, -463, + -463, -463, -463, -463, -463, -463, -463, -463, -463, -463, + -463, -463, -463, -463, -463, -463, -463, -463, -463, -463, + -463, -463, -463, -463, -463, -463, -463, -463, -463, -463, -#include "wcs.h" -#include "wcshdr.h" -#include "wcsmath.h" -#include "wcsutil.h" + -463, -463, -463, -463, -463, -463, -463, -463, -463, -463, + -463, -463, -463, -463, -463, -463, -463, -463 + }, + + { + 55, -464, -464, -464, -464, -464, -464, -464, -464, -464, + -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, + -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, + -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, + -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, + -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, + -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, + -464, -464, 618, -464, -464, -464, -464, -464, -464, -464, -#define INTEGER 0 -#define FLOAT 1 -#define STRING 2 + -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, + -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, + -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, + -464, -464, -464, -464, -464, -464, -464, -464, -464, -464, + -464, -464, -464, -464, -464, -464, -464, -464 + }, + + { + 55, -465, -465, -465, -465, -465, -465, -465, -465, -465, + -465, -465, -465, -465, -465, -465, -465, -465, -465, -465, + -465, -465, -465, -465, -465, -465, -465, -465, -465, -465, + -465, -465, -465, -465, -465, -465, -465, -465, -465, -465, + -465, -465, -465, -465, -465, -465, -465, -465, -465, -465, -#define YY_DECL int wcspih(char *header, int nkeyrec, int relax, int ctrl, \ - int *nreject, int *nwcs, struct wcsprm **wcs) + -465, -465, -465, -465, -465, -465, -465, -465, -465, -465, + -465, -465, -465, -465, -465, -465, -465, -465, -465, 619, + -465, -465, -465, -465, -465, -465, -465, -465, -465, -465, + -465, -465, -465, -465, -465, -465, -465, -465, -465, -465, + -465, -465, -465, -465, -465, -465, -465, -465, -465, -465, + -465, -465, -465, -465, -465, -465, -465, -465, -465, -465, + -465, -465, -465, -465, -465, -465, -465, -465, -465, -465, + -465, -465, -465, -465, -465, -465, -465, -465 + }, + + { + 55, -466, -466, -466, -466, -466, -466, -466, -466, -466, + -466, -466, -466, -466, -466, -466, -466, -466, -466, -466, -#define YY_INPUT(inbuff, count, bufsize) \ - { \ - if (wcspih_nkeyrec) { \ - strncpy(inbuff, wcspih_hdr, 80); \ - inbuff[80] = '\n'; \ - wcspih_hdr += 80; \ - wcspih_nkeyrec--; \ - count = 81; \ - } else { \ - count = YY_NULL; \ - } \ - } + -466, -466, -466, -466, -466, -466, -466, -466, -466, -466, + -466, -466, -466, -466, -466, -466, -466, -466, -466, -466, + -466, -466, -466, -466, -466, -466, -466, -466, -466, -466, + -466, -466, -466, -466, -466, -466, -466, -466, -466, -466, + -466, -466, -466, -466, -466, 620, -466, -466, -466, -466, + -466, -466, -466, -466, -466, -466, -466, -466, -466, -466, + -466, -466, -466, -466, -466, -466, -466, -466, -466, -466, + -466, -466, -466, -466, -466, -466, -466, -466, -466, -466, + -466, -466, -466, -466, -466, -466, -466, -466, -466, -466, + -466, -466, -466, -466, -466, -466, -466, -466, -466, -466, + + -466, -466, -466, -466, -466, -466, -466, -466 + }, + + { + 55, -467, -467, -467, -467, -467, -467, -467, -467, -467, + -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, + -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, + -467, -467, 621, -467, -467, -467, -467, -467, -467, -467, + -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, + -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, + -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, + -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, + -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, -/* These global variables are required by YY_INPUT. */ -char *wcspih_hdr; -int wcspih_nkeyrec; + -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, + -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, + -467, -467, -467, -467, -467, -467, -467, -467, -467, -467, + -467, -467, -467, -467, -467, -467, -467, -467 + }, + + { + 55, -468, -468, -468, -468, -468, -468, -468, -468, -468, + -468, -468, -468, -468, -468, -468, -468, -468, -468, -468, + -468, -468, -468, -468, -468, -468, -468, -468, -468, -468, + -468, -468, 622, -468, -468, -468, -468, -468, -468, -468, + -468, -468, -468, -468, -468, -468, -468, -468, -468, -468, + -468, -468, -468, -468, -468, -468, -468, -468, -468, -468, -int wcspih_final(int alts[], double epoch[], double vsource[], int *nwcs, - struct wcsprm **wcs); -int wcspih_inits(int naxis, int alts[], int npv[], int nps[], int *nwcs, - struct wcsprm **wcs); -void wcspih_naxes(int naxis, int i, int j, char a, int alts[], int *npptr); + -468, -468, -468, -468, -468, -468, -468, -468, -468, -468, + -468, -468, -468, -468, -468, -468, -468, -468, -468, -468, + -468, -468, -468, -468, -468, -468, -468, -468, -468, -468, + -468, -468, -468, -468, -468, -468, -468, -468, -468, -468, + -468, -468, -468, -468, -468, -468, -468, -468, -468, -468, + -468, -468, -468, -468, -468, -468, -468, -468, -468, -468, + -468, -468, -468, -468, -468, -468, -468, -468 + }, + + { + 55, -469, -469, -469, -469, -469, -469, -469, -469, -469, + -469, -469, -469, -469, -469, -469, -469, -469, -469, -469, + -469, -469, -469, -469, -469, -469, -469, -469, -469, -469, -/* Used in preempting the call to exit() by yy_fatal_error(). */ -jmp_buf wcspih_abort_jmp_env; -#define exit(status) longjmp(wcspih_abort_jmp_env, status) + -469, -469, -469, -469, -469, -469, -469, -469, -469, -469, + -469, -469, -469, -469, -469, -469, -469, -469, -469, -469, + -469, -469, -469, -469, -469, -469, -469, -469, -469, -469, + -469, -469, -469, -469, -469, -469, -469, -469, -469, -469, + -469, -469, -469, -469, -469, -469, -469, -469, -469, -469, + 623, -469, -469, -469, -469, -469, -469, -469, -469, -469, + -469, -469, -469, -469, -469, -469, -469, -469, -469, -469, + -469, -469, -469, -469, -469, -469, -469, -469, -469, -469, + -469, -469, -469, -469, -469, -469, -469, -469, -469, -469, + -469, -469, -469, -469, -469, -469, -469, -469 + + }, + + { + 55, -470, -470, -470, -470, -470, -470, -470, -470, -470, + -470, -470, -470, -470, -470, -470, -470, -470, -470, -470, + -470, -470, -470, -470, -470, -470, -470, -470, -470, -470, + -470, -470, -470, -470, -470, -470, -470, -470, -470, -470, + -470, -470, -470, -470, -470, -470, -470, -470, -470, -470, + -470, -470, -470, -470, -470, -470, -470, -470, -470, -470, + -470, -470, -470, -470, -470, -470, -470, 624, -470, -470, + -470, -470, -470, -470, -470, -470, -470, -470, -470, -470, + -470, -470, -470, 625, -470, -470, -470, -470, -470, -470, + -470, -470, -470, -470, -470, -470, -470, -470, -470, -470, -#line 8278 "wcspih.c" + -470, -470, -470, -470, -470, -470, -470, -470, -470, -470, + -470, -470, -470, -470, -470, -470, -470, -470, -470, -470, + -470, -470, -470, -470, -470, -470, -470, -470 + }, + + { + 55, -471, -471, -471, -471, -471, -471, -471, -471, -471, + -471, -471, -471, -471, -471, -471, -471, -471, -471, -471, + -471, -471, -471, -471, -471, -471, -471, -471, -471, -471, + -471, -471, -471, -471, -471, -471, -471, -471, -471, -471, + -471, -471, -471, -471, -471, -471, -471, -471, -471, -471, + -471, -471, -471, -471, -471, -471, -471, -471, -471, -471, + -471, -471, -471, -471, -471, -471, -471, -471, -471, -471, -#define INITIAL 0 -#define CROTAi 1 -#define PROJPn 2 -#define CCCCCia 3 -#define CCi_ja 4 -#define CCi_ma 5 -#define CCCCCCCa 6 -#define CCCCCCCC 7 -#define VALUE 8 -#define INTEGER_VAL 9 -#define FLOAT_VAL 10 -#define STRING_VAL 11 -#define COMMENT 12 -#define DISCARD 13 -#define ERROR 14 -#define FLUSH 15 + 626, -471, -471, -471, -471, -471, -471, -471, -471, -471, + -471, -471, -471, -471, -471, -471, -471, 627, -471, -471, + -471, -471, -471, -471, -471, -471, -471, -471, -471, -471, + -471, -471, -471, -471, -471, -471, -471, -471, -471, -471, + -471, -471, -471, -471, -471, -471, -471, -471, -471, -471, + -471, -471, -471, -471, -471, -471, -471, -471 + }, + + { + 55, -472, -472, -472, -472, -472, -472, -472, -472, -472, + -472, -472, -472, -472, -472, -472, -472, -472, -472, -472, + -472, -472, -472, -472, -472, -472, -472, -472, -472, -472, + -472, -472, -472, -472, -472, -472, -472, -472, -472, -472, -#ifndef YY_NO_UNISTD_H -/* Special case for "unistd.h", since it is non-ANSI. We include it way - * down here because we want the user's section 1 to have been scanned first. - * The user has a chance to override it with an option. - */ -#include -#endif + -472, -472, -472, -472, -472, -472, -472, -472, -472, -472, + -472, -472, -472, -472, -472, -472, -472, -472, -472, -472, + -472, -472, -472, -472, -472, -472, -472, -472, -472, -472, + -472, -472, -472, -472, -472, -472, -472, -472, -472, -472, + -472, -472, -472, -472, -472, -472, -472, -472, -472, -472, + -472, -472, -472, -472, -472, 628, -472, -472, -472, -472, + -472, -472, -472, -472, -472, -472, -472, -472, -472, -472, + -472, -472, -472, -472, -472, -472, -472, -472, -472, -472, + -472, -472, -472, -472, -472, -472, -472, -472 + }, + + { + 55, -473, -473, -473, -473, -473, -473, -473, -473, -473, + + -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, + -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, + -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, + -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, + -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, + -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, + -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, + -473, -473, -473, 629, -473, -473, -473, -473, -473, -473, + -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, + -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, -#ifndef YY_EXTRA_TYPE -#define YY_EXTRA_TYPE void * -#endif + -473, -473, -473, -473, -473, -473, -473, -473, -473, -473, + -473, -473, -473, -473, -473, -473, -473, -473 + }, + + { + 55, -474, -474, -474, -474, -474, -474, -474, -474, -474, + -474, -474, -474, -474, -474, -474, -474, -474, -474, -474, + -474, -474, -474, -474, -474, -474, -474, -474, -474, -474, + -474, -474, -474, -474, -474, -474, -474, -474, -474, -474, + -474, -474, -474, -474, -474, -474, -474, -474, -474, -474, + -474, -474, -474, -474, -474, -474, -474, -474, -474, -474, + -474, -474, -474, -474, -474, -474, -474, -474, -474, -474, + -474, -474, -474, -474, -474, -474, -474, -474, -474, 630, -static int yy_init_globals (void ); + -474, -474, -474, 631, -474, -474, -474, -474, -474, -474, + -474, -474, -474, -474, -474, -474, -474, -474, -474, -474, + -474, -474, -474, -474, -474, -474, -474, -474, -474, -474, + -474, -474, -474, -474, -474, -474, -474, -474, -474, -474, + -474, -474, -474, -474, -474, -474, -474, -474 + }, + + { + 55, -475, -475, -475, -475, -475, -475, -475, -475, -475, + -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, + -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, + -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, + -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, -/* Accessor methods to globals. - These are made visible to non-reentrant scanners for convenience. */ + -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, + -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, + -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, + 632, -475, -475, -475, -475, -475, -475, -475, -475, -475, + -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, + -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, + -475, -475, -475, -475, -475, -475, -475, -475, -475, -475, + -475, -475, -475, -475, -475, -475, -475, -475 + }, + + { + 55, -476, -476, -476, -476, -476, -476, -476, -476, -476, + -476, -476, -476, -476, -476, -476, -476, -476, -476, -476, -int wcspihlex_destroy (void ); + -476, -476, -476, -476, -476, -476, -476, -476, -476, -476, + -476, -476, -476, -476, -476, -476, -476, -476, -476, -476, + -476, -476, -476, -476, -476, -476, -476, -476, -476, -476, + -476, -476, -476, -476, -476, -476, -476, -476, -476, -476, + -476, -476, -476, -476, -476, -476, -476, -476, 633, -476, + -476, -476, -476, -476, -476, -476, -476, -476, -476, 634, + 635, -476, -476, 636, -476, 637, -476, -476, -476, -476, + -476, -476, -476, -476, -476, -476, -476, -476, -476, -476, + -476, -476, -476, -476, -476, -476, -476, -476, -476, -476, + -476, -476, -476, -476, -476, -476, -476, -476, -476, -476, + + -476, -476, -476, -476, -476, -476, -476, -476 + }, + + { + 55, -477, -477, -477, -477, -477, -477, -477, -477, -477, + -477, -477, -477, -477, -477, -477, -477, -477, -477, -477, + -477, -477, -477, -477, -477, -477, -477, -477, -477, -477, + -477, -477, -477, -477, -477, -477, -477, -477, -477, -477, + -477, -477, -477, -477, -477, -477, -477, -477, -477, -477, + -477, -477, -477, -477, -477, -477, -477, -477, -477, -477, + -477, -477, -477, -477, -477, -477, -477, -477, 638, -477, + -477, -477, -477, -477, -477, -477, -477, -477, -477, -477, + -477, -477, -477, -477, -477, -477, -477, -477, -477, -477, -int wcspihget_debug (void ); + -477, -477, -477, -477, -477, -477, -477, -477, -477, -477, + -477, -477, -477, -477, -477, -477, -477, -477, -477, -477, + -477, -477, -477, -477, -477, -477, -477, -477, -477, -477, + -477, -477, -477, -477, -477, -477, -477, -477 + }, + + { + 55, -478, -478, -478, -478, -478, -478, -478, -478, -478, + -478, -478, -478, -478, -478, -478, -478, -478, -478, -478, + -478, -478, -478, -478, -478, -478, -478, -478, -478, -478, + -478, -478, -478, -478, -478, -478, -478, -478, -478, -478, + -478, -478, -478, -478, -478, -478, -478, -478, -478, -478, + -478, -478, -478, -478, -478, -478, -478, -478, -478, -478, -void wcspihset_debug (int debug_flag ); + -478, -478, -478, -478, -478, -478, -478, -478, -478, -478, + -478, -478, -478, -478, -478, -478, -478, -478, -478, -478, + -478, -478, -478, -478, -478, -478, -478, -478, -478, 639, + -478, -478, -478, -478, -478, -478, -478, -478, -478, -478, + -478, -478, -478, -478, -478, -478, -478, -478, -478, -478, + -478, -478, -478, -478, -478, -478, -478, -478, -478, -478, + -478, -478, -478, -478, -478, -478, -478, -478 + }, + + { + 55, -479, -479, -479, -479, -479, -479, -479, -479, -479, + -479, -479, -479, -479, -479, -479, -479, -479, -479, -479, + -479, -479, -479, -479, -479, -479, -479, -479, -479, -479, -YY_EXTRA_TYPE wcspihget_extra (void ); + -479, -479, -479, -479, -479, -479, -479, -479, -479, -479, + -479, -479, -479, -479, -479, -479, -479, -479, -479, -479, + -479, -479, -479, -479, -479, -479, -479, -479, -479, -479, + -479, -479, -479, -479, -479, -479, -479, -479, 640, -479, + -479, -479, -479, -479, -479, -479, -479, -479, -479, -479, + 641, -479, -479, -479, -479, -479, -479, -479, -479, -479, + -479, -479, -479, -479, -479, -479, -479, -479, -479, -479, + -479, -479, -479, -479, -479, -479, -479, -479, -479, -479, + -479, -479, -479, -479, -479, -479, -479, -479, -479, -479, + -479, -479, -479, -479, -479, -479, -479, -479 + + }, + + { + 55, -480, -480, -480, -480, -480, -480, -480, -480, -480, + -480, -480, -480, -480, -480, -480, -480, -480, -480, -480, + -480, -480, -480, -480, -480, -480, -480, -480, -480, -480, + -480, -480, -480, -480, -480, -480, -480, -480, -480, -480, + -480, -480, -480, -480, -480, -480, -480, -480, -480, -480, + -480, -480, -480, -480, -480, -480, -480, -480, -480, -480, + -480, -480, -480, -480, -480, -480, -480, -480, -480, -480, + -480, -480, -480, -480, -480, -480, -480, -480, -480, -480, + -480, -480, 642, -480, -480, -480, -480, -480, -480, -480, + -480, -480, -480, -480, -480, -480, -480, -480, -480, -480, -void wcspihset_extra (YY_EXTRA_TYPE user_defined ); + -480, -480, -480, -480, -480, -480, -480, -480, -480, -480, + -480, -480, -480, -480, -480, -480, -480, -480, -480, -480, + -480, -480, -480, -480, -480, -480, -480, -480 + }, + + { + 55, -481, -481, -481, -481, -481, -481, -481, -481, -481, + -481, -481, -481, -481, -481, -481, -481, -481, -481, -481, + -481, -481, -481, -481, -481, -481, -481, -481, -481, -481, + -481, -481, -481, -481, -481, -481, -481, -481, -481, -481, + -481, -481, -481, -481, -481, -481, -481, -481, -481, -481, + -481, -481, -481, -481, -481, -481, -481, -481, -481, -481, + -481, -481, -481, -481, -481, -481, -481, -481, -481, -481, -FILE *wcspihget_in (void ); + -481, -481, -481, -481, -481, -481, -481, -481, -481, -481, + 643, -481, -481, -481, -481, -481, -481, -481, -481, -481, + -481, -481, -481, -481, -481, -481, -481, -481, -481, -481, + -481, -481, -481, -481, -481, -481, -481, -481, -481, -481, + -481, -481, -481, -481, -481, -481, -481, -481, -481, -481, + -481, -481, -481, -481, -481, -481, -481, -481 + }, + + { + 55, -482, -482, -482, -482, -482, -482, -482, -482, -482, + -482, -482, -482, -482, -482, -482, -482, -482, -482, -482, + -482, -482, -482, -482, -482, -482, -482, -482, -482, -482, + -482, -482, -482, -482, -482, -482, -482, -482, -482, -482, -void wcspihset_in (FILE * in_str ); + -482, -482, -482, -482, -482, -482, -482, -482, -482, -482, + -482, -482, -482, -482, -482, -482, -482, -482, -482, -482, + -482, -482, -482, -482, -482, -482, -482, -482, -482, -482, + -482, -482, -482, -482, -482, -482, -482, -482, 644, -482, + -482, -482, -482, -482, -482, -482, -482, -482, -482, -482, + -482, -482, -482, -482, -482, -482, -482, -482, -482, -482, + -482, -482, -482, -482, -482, -482, -482, -482, -482, -482, + -482, -482, -482, -482, -482, -482, -482, -482, -482, -482, + -482, -482, -482, -482, -482, -482, -482, -482 + }, + + { + 55, -483, -483, -483, -483, -483, -483, -483, -483, -483, + + -483, -483, -483, -483, -483, -483, -483, -483, -483, -483, + -483, -483, -483, -483, -483, -483, -483, -483, -483, -483, + -483, -483, -483, -483, -483, -483, -483, -483, -483, -483, + -483, -483, -483, -483, -483, -483, -483, -483, -483, -483, + -483, -483, -483, -483, -483, -483, -483, -483, -483, -483, + -483, -483, -483, -483, -483, -483, -483, -483, -483, -483, + -483, -483, -483, -483, -483, -483, -483, -483, -483, -483, + -483, -483, -483, 645, -483, -483, -483, -483, -483, -483, + -483, -483, -483, -483, -483, -483, -483, -483, -483, -483, + -483, -483, -483, -483, -483, -483, -483, -483, -483, -483, -FILE *wcspihget_out (void ); + -483, -483, -483, -483, -483, -483, -483, -483, -483, -483, + -483, -483, -483, -483, -483, -483, -483, -483 + }, + + { + 55, -484, -484, -484, -484, -484, -484, -484, -484, -484, + -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, + -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, + -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, + -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, + -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, + -484, -484, -484, -484, -484, -484, -484, -484, -484, 646, + -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, -void wcspihset_out (FILE * out_str ); + -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, + -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, + -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, + -484, -484, -484, -484, -484, -484, -484, -484, -484, -484, + -484, -484, -484, -484, -484, -484, -484, -484 + }, + + { + 55, -485, -485, -485, -485, -485, -485, -485, -485, -485, + -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, + -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, + -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, + -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, -int wcspihget_leng (void ); + -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, + -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, + -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, + -485, -485, 647, -485, -485, -485, -485, -485, -485, -485, + -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, + -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, + -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, + -485, -485, -485, -485, -485, -485, -485, -485 + }, + + { + 55, -486, -486, -486, -486, -486, -486, -486, -486, -486, + -486, -486, -486, -486, -486, -486, -486, -486, -486, -486, -char *wcspihget_text (void ); + -486, -486, -486, -486, -486, -486, -486, -486, -486, -486, + -486, -486, -486, -486, -486, -486, -486, -486, -486, -486, + -486, -486, -486, -486, -486, -486, -486, -486, -486, -486, + -486, -486, -486, -486, -486, -486, -486, -486, -486, -486, + -486, -486, -486, -486, -486, -486, -486, -486, -486, -486, + -486, -486, -486, -486, -486, -486, -486, -486, -486, -486, + -486, -486, -486, -486, -486, -486, -486, -486, -486, -486, + -486, -486, -486, -486, -486, 648, -486, -486, -486, -486, + -486, -486, -486, -486, -486, -486, -486, -486, -486, -486, + -486, -486, -486, -486, -486, -486, -486, -486, -486, -486, + + -486, -486, -486, -486, -486, -486, -486, -486 + }, + + { + 55, -487, -487, -487, -487, -487, -487, -487, -487, -487, + -487, -487, -487, -487, -487, -487, -487, -487, -487, -487, + -487, -487, -487, -487, -487, -487, -487, -487, -487, -487, + -487, -487, -487, -487, -487, -487, -487, -487, -487, -487, + -487, -487, -487, -487, -487, -487, -487, -487, -487, -487, + -487, -487, -487, -487, -487, -487, -487, -487, -487, -487, + -487, -487, -487, -487, -487, -487, -487, -487, -487, -487, + -487, -487, -487, -487, -487, -487, -487, -487, -487, -487, + -487, -487, -487, -487, -487, -487, -487, -487, 649, -487, -int wcspihget_lineno (void ); + -487, -487, -487, -487, -487, -487, -487, -487, -487, -487, + -487, -487, -487, -487, -487, -487, -487, -487, -487, -487, + -487, -487, -487, -487, -487, -487, -487, -487, -487, -487, + -487, -487, -487, -487, -487, -487, -487, -487 + }, + + { + 55, -488, -488, -488, -488, -488, -488, -488, -488, -488, + -488, -488, -488, -488, -488, -488, -488, -488, -488, -488, + -488, -488, -488, -488, -488, -488, -488, -488, -488, -488, + -488, -488, -488, -488, -488, -488, -488, -488, -488, -488, + -488, -488, -488, -488, -488, -488, -488, -488, -488, -488, + -488, -488, -488, -488, -488, -488, -488, -488, -488, -488, -void wcspihset_lineno (int line_number ); + -488, -488, -488, -488, -488, 650, -488, -488, -488, -488, + -488, -488, -488, -488, -488, -488, -488, -488, -488, -488, + -488, -488, -488, -488, -488, -488, -488, -488, -488, -488, + -488, -488, -488, -488, -488, -488, -488, -488, -488, -488, + -488, -488, -488, -488, -488, -488, -488, -488, -488, -488, + -488, -488, -488, -488, -488, -488, -488, -488, -488, -488, + -488, -488, -488, -488, -488, -488, -488, -488 + }, + + { + 55, -489, -489, -489, -489, -489, -489, -489, -489, -489, + -489, -489, -489, -489, -489, -489, -489, -489, -489, -489, + -489, -489, -489, -489, -489, -489, -489, -489, -489, -489, -/* Macros after this point can all be overridden by user definitions in - * section 1. - */ + -489, -489, -489, -489, -489, -489, -489, -489, -489, -489, + -489, -489, -489, -489, -489, -489, -489, -489, -489, -489, + -489, -489, -489, -489, -489, -489, -489, -489, -489, -489, + -489, -489, -489, -489, -489, -489, -489, -489, -489, 651, + -489, -489, -489, -489, -489, -489, -489, -489, -489, -489, + -489, -489, -489, -489, -489, -489, -489, -489, -489, -489, + -489, -489, -489, -489, -489, -489, -489, -489, -489, -489, + -489, -489, -489, -489, -489, -489, -489, -489, -489, -489, + -489, -489, -489, -489, -489, -489, -489, -489, -489, -489, + -489, -489, -489, -489, -489, -489, -489, -489 + + }, + + { + 55, -490, -490, -490, -490, -490, -490, -490, -490, -490, + -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, + -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, + -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, + -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, + -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, + -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, + -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, + -490, -490, -490, -490, -490, 652, -490, -490, -490, -490, + -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, -#ifndef YY_SKIP_YYWRAP -#ifdef __cplusplus -extern "C" int wcspihwrap (void ); -#else -extern int wcspihwrap (void ); -#endif -#endif + -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, + -490, -490, -490, -490, -490, -490, -490, -490, -490, -490, + -490, -490, -490, -490, -490, -490, -490, -490 + }, + + { + 55, -491, -491, -491, -491, -491, -491, -491, -491, -491, + -491, -491, -491, -491, -491, -491, -491, -491, -491, -491, + -491, -491, -491, -491, -491, -491, -491, -491, -491, -491, + -491, -491, -491, -491, -491, -491, -491, -491, -491, -491, + -491, -491, -491, -491, -491, -491, -491, -491, -491, -491, + -491, -491, -491, -491, -491, -491, -491, -491, -491, -491, + -491, -491, -491, -491, -491, -491, -491, -491, -491, 653, - static void yyunput (int c,char *buf_ptr ); - -#ifndef yytext_ptr -static void yy_flex_strncpy (char *,yyconst char *,int ); -#endif + -491, -491, -491, -491, -491, -491, -491, -491, -491, -491, + -491, -491, -491, -491, -491, -491, -491, -491, -491, -491, + -491, -491, -491, -491, -491, -491, -491, -491, -491, -491, + -491, -491, -491, -491, -491, -491, -491, -491, -491, -491, + -491, -491, -491, -491, -491, -491, -491, -491, -491, -491, + -491, -491, -491, -491, -491, -491, -491, -491 + }, + + { + 55, -492, -492, -492, -492, -492, -492, -492, -492, -492, + -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, + -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, + -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, -#ifdef YY_NEED_STRLEN -static int yy_flex_strlen (yyconst char * ); -#endif + -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, + -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, + -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, + -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, + -492, -492, 654, -492, -492, -492, -492, -492, -492, -492, + -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, + -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, + -492, -492, -492, -492, -492, -492, -492, -492, -492, -492, + -492, -492, -492, -492, -492, -492, -492, -492 + }, + + { + 55, -493, -493, -493, -493, -493, -493, -493, -493, -493, + + -493, -493, -493, -493, -493, -493, -493, -493, -493, -493, + -493, -493, -493, -493, -493, -493, -493, -493, -493, -493, + -493, -493, 655, -493, -493, -493, -493, -493, -493, -493, + -493, -493, -493, -493, -493, -493, -493, -493, -493, -493, + -493, -493, -493, -493, -493, -493, -493, -493, -493, -493, + -493, -493, -493, -493, -493, -493, -493, -493, -493, -493, + -493, -493, -493, -493, -493, -493, -493, -493, -493, -493, + -493, -493, -493, -493, -493, -493, -493, -493, -493, -493, + -493, -493, -493, -493, -493, -493, -493, -493, -493, -493, + -493, -493, -493, -493, -493, -493, -493, -493, -493, -493, -#ifndef YY_NO_INPUT + -493, -493, -493, -493, -493, -493, -493, -493, -493, -493, + -493, -493, -493, -493, -493, -493, -493, -493 + }, + + { + 55, -494, -494, -494, -494, -494, -494, -494, -494, -494, + -494, -494, -494, -494, -494, -494, -494, -494, -494, -494, + -494, -494, -494, -494, -494, -494, -494, -494, -494, -494, + -494, -494, 656, -494, -494, -494, -494, -494, -494, -494, + -494, -494, -494, -494, -494, -494, -494, -494, -494, -494, + -494, -494, -494, -494, -494, -494, -494, -494, -494, -494, + -494, -494, -494, -494, -494, -494, -494, -494, -494, -494, + -494, -494, -494, -494, -494, -494, -494, -494, -494, -494, -#ifdef __cplusplus -static int yyinput (void ); -#else -static int input (void ); -#endif + -494, -494, -494, -494, -494, -494, -494, -494, -494, -494, + -494, -494, -494, -494, -494, -494, -494, -494, -494, -494, + -494, -494, -494, -494, -494, -494, -494, -494, -494, -494, + -494, -494, -494, -494, -494, -494, -494, -494, -494, -494, + -494, -494, -494, -494, -494, -494, -494, -494 + }, + + { + 55, -495, -495, -495, -495, -495, -495, -495, -495, -495, + -495, -495, -495, -495, -495, -495, -495, -495, -495, -495, + -495, -495, -495, -495, -495, -495, -495, -495, -495, -495, + -495, -495, 657, -495, -495, -495, -495, -495, -495, -495, + -495, -495, -495, -495, -495, -495, -495, -495, -495, -495, -#endif + -495, -495, -495, -495, -495, -495, -495, -495, -495, -495, + -495, -495, -495, -495, -495, -495, -495, -495, -495, -495, + -495, -495, -495, -495, -495, -495, -495, -495, -495, -495, + -495, -495, -495, -495, -495, -495, -495, -495, -495, -495, + -495, -495, -495, -495, -495, -495, -495, -495, -495, -495, + -495, -495, -495, -495, -495, -495, -495, -495, -495, -495, + -495, -495, -495, -495, -495, -495, -495, -495, -495, -495, + -495, -495, -495, -495, -495, -495, -495, -495 + }, + + { + 55, -496, -496, -496, -496, -496, -496, -496, -496, -496, + -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, -/* Amount of stuff to slurp up with each read. */ -#ifndef YY_READ_BUF_SIZE -#ifdef __ia64__ -/* On IA-64, the buffer size is 16k, not 8k */ -#define YY_READ_BUF_SIZE 16384 -#else -#define YY_READ_BUF_SIZE 8192 -#endif /* __ia64__ */ -#endif + -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, + -496, -496, 658, -496, -496, -496, -496, -496, -496, -496, + -496, -496, -496, -496, -496, -496, -496, -496, 659, 660, + 660, 660, 660, 660, 660, 660, 660, 660, -496, -496, + -496, -496, -496, -496, -496, 658, 658, 658, 658, 658, + 658, 658, 658, 658, 658, 658, 658, 658, 658, 658, + 658, 658, 658, 658, 658, 658, 658, 658, 658, 658, + 658, -496, -496, -496, -496, -496, -496, -496, -496, -496, + -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, + -496, -496, -496, -496, -496, -496, -496, -496, -496, -496, + + -496, -496, -496, -496, -496, -496, -496, -496 + }, + + { + 55, -497, -497, -497, -497, -497, -497, -497, -497, -497, + -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, + -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, + -497, -497, 661, -497, -497, -497, -497, -497, -497, -497, + -497, -497, -497, -497, -497, -497, -497, -497, 659, 660, + 660, 660, 660, 660, 660, 660, 660, 660, -497, -497, + -497, -497, -497, -497, -497, 661, 661, 661, 661, 661, + 661, 661, 661, 661, 661, 661, 661, 661, 661, 661, + 661, 661, 661, 661, 661, 661, 661, 661, 661, 661, -/* Copy whatever the last rule matched to the standard output. */ -#ifndef ECHO -/* This used to be an fputs(), but since the string might contain NUL's, - * we now use fwrite(). - */ -#define ECHO do { if (fwrite( wcspihtext, wcspihleng, 1, wcspihout )) {} } while (0) -#endif + 661, -497, -497, -497, -497, -497, -497, -497, -497, -497, + -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, + -497, -497, -497, -497, -497, -497, -497, -497, -497, -497, + -497, -497, -497, -497, -497, -497, -497, -497 + }, + + { + 55, -498, -498, -498, -498, -498, -498, -498, -498, -498, + -498, -498, -498, -498, -498, -498, -498, -498, -498, -498, + -498, -498, -498, -498, -498, -498, -498, -498, -498, -498, + -498, -498, 662, -498, -498, -498, -498, -498, -498, -498, + -498, -498, -498, -498, -498, -498, -498, -498, -498, -498, + -498, -498, -498, -498, -498, -498, -498, -498, -498, -498, -/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, - * is returned in "result". - */ -#ifndef YY_INPUT -#define YY_INPUT(buf,result,max_size) \ - errno=0; \ - while ( (result = read( fileno(wcspihin), (char *) buf, max_size )) < 0 ) \ - { \ - if( errno != EINTR) \ - { \ - YY_FATAL_ERROR( "input in flex scanner failed" ); \ - break; \ - } \ - errno=0; \ - clearerr(wcspihin); \ - }\ -\ + -498, -498, -498, -498, -498, -498, -498, -498, -498, -498, + -498, -498, -498, -498, -498, -498, -498, -498, -498, -498, + -498, -498, -498, -498, -498, -498, -498, -498, -498, -498, + -498, -498, -498, -498, -498, -498, -498, -498, -498, -498, + -498, -498, -498, -498, -498, -498, -498, -498, -498, -498, + -498, -498, -498, -498, -498, -498, -498, -498, -498, -498, + -498, -498, -498, -498, -498, -498, -498, -498 + }, + + { + 55, -499, -499, -499, -499, -499, -499, -499, -499, -499, + -499, -499, -499, -499, -499, -499, -499, -499, -499, -499, + -499, -499, -499, -499, -499, -499, -499, -499, -499, -499, -#endif + -499, -499, 663, -499, -499, -499, -499, -499, -499, -499, + -499, -499, -499, -499, -499, -499, -499, -499, -499, -499, + -499, -499, -499, -499, -499, -499, -499, -499, -499, -499, + -499, -499, -499, -499, -499, -499, -499, -499, -499, -499, + -499, -499, -499, -499, -499, -499, -499, -499, -499, -499, + -499, -499, -499, -499, -499, -499, -499, -499, -499, -499, + -499, -499, -499, -499, -499, -499, -499, -499, -499, -499, + -499, -499, -499, -499, -499, -499, -499, -499, -499, -499, + -499, -499, -499, -499, -499, -499, -499, -499, -499, -499, + -499, -499, -499, -499, -499, -499, -499, -499 + + }, + + { + 55, -500, -500, -500, -500, -500, -500, -500, -500, -500, + -500, -500, -500, -500, -500, -500, -500, -500, -500, -500, + -500, -500, -500, -500, -500, -500, -500, -500, -500, -500, + -500, -500, 664, -500, -500, -500, -500, -500, -500, -500, + -500, -500, -500, -500, -500, -500, -500, -500, -500, -500, + -500, -500, -500, -500, -500, -500, -500, -500, -500, -500, + -500, -500, -500, -500, -500, -500, -500, -500, -500, -500, + -500, -500, -500, -500, -500, -500, -500, -500, -500, -500, + -500, -500, -500, -500, -500, -500, -500, -500, -500, -500, + -500, -500, -500, -500, -500, -500, -500, -500, -500, -500, -/* No semi-colon after return; correct usage is to write "yyterminate();" - - * we don't want an extra ';' after the "return" because that will cause - * some compilers to complain about unreachable statements. - */ -#ifndef yyterminate -#define yyterminate() return YY_NULL -#endif + -500, -500, -500, -500, -500, -500, -500, -500, -500, -500, + -500, -500, -500, -500, -500, -500, -500, -500, -500, -500, + -500, -500, -500, -500, -500, -500, -500, -500 + }, + + { + 55, -501, -501, -501, -501, -501, -501, -501, -501, -501, + -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, + -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, + -501, -501, 665, -501, -501, -501, -501, -501, -501, -501, + -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, + -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, + -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, -/* Number of entries by which start-condition stack grows. */ -#ifndef YY_START_STACK_INCR -#define YY_START_STACK_INCR 25 -#endif + -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, + -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, + -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, + -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, + -501, -501, -501, -501, -501, -501, -501, -501, -501, -501, + -501, -501, -501, -501, -501, -501, -501, -501 + }, + + { + 55, -502, -502, -502, -502, -502, -502, -502, -502, -502, + -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, + -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, + -502, -502, 658, -502, -502, -502, -502, -502, -502, -502, -/* Report a fatal error. */ -#ifndef YY_FATAL_ERROR -#define YY_FATAL_ERROR(msg) yy_fatal_error( msg ) -#endif + -502, -502, -502, -502, -502, -502, -502, -502, 666, 666, + 666, 666, 666, 666, 666, 666, 666, 666, -502, -502, + -502, -502, -502, -502, -502, 658, 658, 658, 658, 658, + 658, 658, 658, 658, 658, 658, 658, 658, 658, 658, + 658, 658, 658, 658, 658, 658, 658, 658, 658, 658, + 658, -502, -502, -502, -502, -502, -502, -502, -502, -502, + -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, + -502, -502, -502, -502, -502, -502, -502, -502, -502, -502, + -502, -502, -502, -502, -502, -502, -502, -502 + }, + + { + 55, -503, -503, -503, -503, -503, -503, -503, -503, -503, + + -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, + -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, + -503, -503, 667, -503, -503, -503, -503, -503, -503, -503, + -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, + -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, + -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, + -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, + -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, + -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, + -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, -/* end tables serialization structures and prototypes */ + -503, -503, -503, -503, -503, -503, -503, -503, -503, -503, + -503, -503, -503, -503, -503, -503, -503, -503 + }, + + { + 55, -504, -504, -504, -504, -504, -504, -504, -504, -504, + -504, -504, -504, -504, -504, -504, -504, -504, -504, -504, + -504, -504, -504, -504, -504, -504, -504, -504, -504, -504, + -504, -504, 668, -504, -504, -504, -504, -504, -504, -504, + -504, -504, -504, -504, -504, -504, -504, -504, 669, 669, + 669, 669, 669, 669, 669, 669, 669, 669, -504, -504, + -504, -504, -504, -504, -504, 668, 668, 668, 668, 668, + 668, 668, 668, 668, 668, 668, 668, 668, 668, 668, -/* Default declaration of generated scanner - a define so the user can - * easily add parameters. - */ -#ifndef YY_DECL -#define YY_DECL_IS_OURS 1 + 668, 668, 668, 668, 668, 668, 668, 668, 668, 668, + 668, -504, -504, -504, -504, -504, -504, -504, -504, -504, + -504, -504, -504, -504, -504, -504, -504, -504, -504, -504, + -504, -504, -504, -504, -504, -504, -504, -504, -504, -504, + -504, -504, -504, -504, -504, -504, -504, -504 + }, + + { + 55, -505, -505, -505, -505, -505, -505, -505, -505, -505, + -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, + -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, + -505, -505, 670, -505, -505, -505, -505, -505, -505, -505, + -505, -505, -505, -505, -505, -505, -505, -505, 671, 671, -extern int wcspihlex (void); + 671, 671, 671, 671, 671, 671, 671, 671, -505, -505, + -505, -505, -505, -505, -505, 670, 670, 670, 670, 670, + 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, + 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, + 670, -505, -505, -505, -505, -505, -505, -505, -505, -505, + -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, + -505, -505, -505, -505, -505, -505, -505, -505, -505, -505, + -505, -505, -505, -505, -505, -505, -505, -505 + }, + + { + 55, -506, -506, -506, -506, -506, -506, -506, -506, -506, + -506, -506, -506, -506, -506, -506, -506, -506, -506, -506, -#define YY_DECL int wcspihlex (void) -#endif /* !YY_DECL */ + -506, -506, -506, -506, -506, -506, -506, -506, -506, -506, + -506, -506, -506, -506, -506, -506, -506, -506, -506, -506, + -506, -506, -506, -506, -506, -506, -506, -506, 672, 672, + 672, 672, 672, 672, 672, 672, 672, 672, -506, -506, + -506, -506, -506, -506, -506, -506, -506, -506, -506, -506, + -506, -506, -506, -506, -506, -506, -506, -506, -506, -506, + -506, -506, -506, -506, -506, -506, -506, -506, -506, -506, + -506, -506, -506, -506, -506, -506, -506, -506, -506, -506, + -506, -506, -506, -506, -506, -506, -506, -506, -506, -506, + -506, -506, -506, -506, -506, -506, -506, -506, -506, -506, + + -506, -506, -506, -506, -506, -506, -506, -506 + }, + + { + 55, -507, -507, -507, -507, -507, -507, -507, -507, -507, + -507, -507, -507, -507, -507, -507, -507, -507, -507, -507, + -507, -507, -507, -507, -507, -507, -507, -507, -507, -507, + -507, -507, -507, -507, -507, -507, -507, -507, -507, -507, + -507, -507, -507, -507, -507, 673, -507, -507, -507, -507, + -507, -507, -507, -507, -507, -507, -507, -507, -507, -507, + -507, -507, -507, -507, -507, -507, -507, -507, -507, -507, + -507, -507, -507, -507, -507, -507, -507, -507, -507, -507, + -507, -507, -507, -507, -507, -507, -507, -507, -507, -507, -/* Code executed at the beginning of each rule, after wcspihtext and wcspihleng - * have been set up. - */ -#ifndef YY_USER_ACTION -#define YY_USER_ACTION -#endif + -507, -507, -507, -507, -507, 674, -507, -507, -507, -507, + -507, -507, -507, -507, -507, -507, -507, -507, -507, -507, + -507, -507, -507, -507, -507, -507, -507, -507, -507, -507, + -507, -507, -507, -507, -507, -507, -507, -507 + }, + + { + 55, -508, -508, -508, -508, -508, -508, -508, -508, -508, + -508, -508, -508, -508, -508, -508, -508, -508, -508, -508, + -508, -508, -508, -508, -508, -508, -508, -508, -508, -508, + -508, -508, -508, -508, -508, -508, -508, -508, -508, -508, + -508, -508, -508, -508, -508, 673, -508, -508, -508, -508, + -508, -508, -508, -508, -508, -508, -508, -508, -508, -508, -/* Code executed at the end of each rule. */ -#ifndef YY_BREAK -#define YY_BREAK break; -#endif + -508, -508, -508, -508, -508, -508, -508, -508, -508, -508, + -508, -508, -508, -508, -508, -508, -508, -508, -508, -508, + -508, -508, -508, -508, -508, -508, -508, -508, -508, -508, + -508, -508, -508, -508, -508, 675, -508, -508, -508, -508, + -508, -508, -508, -508, -508, -508, -508, -508, -508, -508, + -508, -508, -508, -508, -508, -508, -508, -508, -508, -508, + -508, -508, -508, -508, -508, -508, -508, -508 + }, + + { + 55, -509, -509, -509, -509, -509, -509, -509, -509, -509, + -509, -509, -509, -509, -509, -509, -509, -509, -509, -509, + -509, -509, -509, -509, -509, -509, -509, -509, -509, -509, -#define YY_RULE_SETUP \ - if ( wcspihleng > 0 ) \ - YY_CURRENT_BUFFER_LVALUE->yy_at_bol = \ - (wcspihtext[wcspihleng - 1] == '\n'); \ - YY_USER_ACTION + -509, -509, -509, -509, -509, -509, -509, -509, -509, -509, + -509, -509, -509, -509, -509, -509, -509, -509, 676, 676, + 676, 676, 676, 676, 676, 676, 676, 676, -509, -509, + -509, -509, -509, -509, -509, -509, -509, -509, -509, -509, + -509, -509, -509, -509, -509, -509, -509, -509, -509, -509, + -509, -509, -509, -509, -509, -509, -509, -509, -509, -509, + -509, -509, -509, -509, -509, -509, -509, -509, -509, -509, + -509, -509, -509, -509, -509, -509, -509, -509, -509, -509, + -509, -509, -509, -509, -509, -509, -509, -509, -509, -509, + -509, -509, -509, -509, -509, -509, -509, -509 + + }, + + { + 55, -510, -510, -510, -510, -510, -510, -510, -510, -510, + -510, -510, -510, -510, -510, -510, -510, -510, -510, -510, + -510, -510, -510, -510, -510, -510, -510, -510, -510, -510, + -510, -510, -510, -510, -510, -510, -510, -510, -510, -510, + -510, -510, -510, -510, -510, 673, -510, -510, 677, 677, + 677, 677, 677, 677, 677, 677, 677, 677, -510, -510, + -510, -510, -510, -510, -510, -510, -510, -510, -510, -510, + -510, -510, -510, -510, -510, -510, -510, -510, -510, -510, + -510, -510, -510, -510, -510, -510, -510, -510, -510, -510, + -510, -510, -510, -510, -510, 678, -510, -510, -510, -510, -/** The main scanner function which does all the work. - */ -YY_DECL -{ - register yy_state_type yy_current_state; - register char *yy_cp, *yy_bp; - register int yy_act; - -#line 146 "wcspih.l" - - /* Keyword indices, as used in the WCS papers, e.g. PCi_ja, PVi_ma. */ - char a; - int i, j, m; - - char *cptr, *errmsg, errtxt[80], *hptr, *keep; - int altlin, alts[27], ialt, idx, ipx, ix, jx, naxis, *npptr, - nps[27], npv[27], pass, status, valtype, voff; - double epoch[27], vsource[27]; - void *vptr, *wptr; - struct wcsprm *wcsp; - int wcspihlex_destroy(void); - - naxis = 0; - for (ialt = 0; ialt < 27; ialt++) { - alts[ialt] = 0; - npv[ialt] = 0; - nps[ialt] = 0; - epoch[ialt] = UNDEFINED; - vsource[ialt] = UNDEFINED; - } - - /* Parameters used to implement YY_INPUT. */ - wcspih_hdr = header; - wcspih_nkeyrec = nkeyrec; - - /* Our handle on the input stream. */ - hptr = header; - keep = 0x0; - *nreject = 0; - - /* Keyword parameters. */ - i = j = m = 0; - a = ' '; - - /* For decoding the keyvalue. */ - valtype = -1; - idx = -1; - vptr = 0x0; - - /* For keywords that require special handling. */ - altlin = 0; - npptr = 0x0; - - /* The data structures produced. */ - *nwcs = 0; - *wcs = 0x0; - - pass = 1; - - /* Return here via longjmp() invoked by yy_fatal_error(). */ - if (setjmp(wcspih_abort_jmp_env)) { - return 3; - } - - BEGIN(INITIAL); + -510, -510, -510, -510, -510, -510, -510, -510, -510, -510, + -510, -510, -510, -510, -510, -510, -510, -510, -510, -510, + -510, -510, -510, -510, -510, -510, -510, -510 + }, + + { + 55, -511, -511, -511, -511, -511, -511, -511, -511, -511, + -511, -511, -511, -511, -511, -511, -511, -511, -511, -511, + -511, -511, -511, -511, -511, -511, -511, -511, -511, -511, + -511, -511, -511, -511, -511, -511, -511, -511, -511, -511, + -511, -511, -511, -511, -511, -511, -511, -511, 679, 680, + 680, 680, 680, 680, 680, 680, 680, 680, -511, -511, + -511, -511, -511, -511, -511, -511, -511, -511, -511, -511, + -511, -511, -511, -511, -511, -511, -511, -511, -511, -511, + -511, -511, -511, -511, -511, -511, -511, -511, -511, -511, + -511, -511, -511, -511, -511, -511, -511, -511, -511, -511, + -511, -511, -511, -511, -511, -511, -511, -511, -511, -511, + -511, -511, -511, -511, -511, -511, -511, -511, -511, -511, + -511, -511, -511, -511, -511, -511, -511, -511 + }, + + { + 55, -512, -512, -512, -512, -512, -512, -512, -512, -512, + -512, -512, -512, -512, -512, -512, -512, -512, -512, -512, + -512, -512, -512, -512, -512, -512, -512, -512, -512, -512, + -512, -512, 681, -512, -512, -512, -512, -512, -512, -512, -#line 8526 "wcspih.c" + -512, -512, -512, -512, -512, -512, -512, -512, 682, 682, + 682, 682, 682, 682, 682, 682, 682, 682, -512, -512, + -512, -512, -512, -512, -512, 681, 681, 681, 681, 681, + 681, 681, 681, 681, 681, 681, 681, 681, 681, 681, + 681, 681, 681, 681, 681, 681, 681, 681, 681, 681, + 681, -512, -512, -512, -512, -512, -512, -512, -512, -512, + -512, -512, -512, -512, -512, -512, -512, -512, -512, -512, + -512, -512, -512, -512, -512, -512, -512, -512, -512, -512, + -512, -512, -512, -512, -512, -512, -512, -512 + }, + + { + 55, -513, -513, -513, -513, -513, -513, -513, -513, -513, + + -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, + -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, + -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, + -513, -513, -513, -513, -513, -513, -513, -513, 683, 684, + 684, 684, 684, 684, 684, 684, 684, 684, -513, -513, + -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, + -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, + -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, + -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, + -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, - if ( !(yy_init) ) - { - (yy_init) = 1; + -513, -513, -513, -513, -513, -513, -513, -513, -513, -513, + -513, -513, -513, -513, -513, -513, -513, -513 + }, + + { + 55, -514, -514, -514, -514, -514, -514, -514, -514, -514, + -514, -514, -514, -514, -514, -514, -514, -514, -514, -514, + -514, -514, -514, -514, -514, -514, -514, -514, -514, -514, + -514, -514, -514, -514, -514, -514, -514, -514, -514, -514, + -514, -514, -514, -514, -514, 673, -514, -514, 677, 677, + 677, 677, 677, 677, 677, 677, 677, 677, -514, -514, + -514, -514, -514, -514, -514, -514, -514, -514, -514, -514, + -514, -514, -514, -514, -514, -514, -514, -514, -514, -514, -#ifdef YY_USER_INIT - YY_USER_INIT; -#endif + -514, -514, -514, -514, -514, -514, -514, -514, -514, -514, + -514, -514, -514, -514, -514, 674, -514, -514, -514, -514, + -514, -514, -514, -514, -514, -514, -514, -514, -514, -514, + -514, -514, -514, -514, -514, -514, -514, -514, -514, -514, + -514, -514, -514, -514, -514, -514, -514, -514 + }, + + { + 55, -515, -515, -515, -515, -515, -515, -515, -515, -515, + -515, -515, -515, -515, -515, -515, -515, -515, -515, -515, + -515, -515, -515, -515, -515, -515, -515, -515, -515, -515, + -515, -515, 681, -515, -515, -515, -515, -515, -515, -515, + -515, -515, -515, -515, -515, -515, -515, -515, 685, 686, - if ( ! (yy_start) ) - (yy_start) = 1; /* first start state */ + 686, 686, 686, 686, 686, 686, 686, 686, -515, -515, + -515, -515, -515, -515, -515, 681, 681, 681, 681, 681, + 681, 681, 681, 681, 681, 681, 681, 681, 681, 681, + 681, 681, 681, 681, 681, 681, 681, 681, 681, 681, + 681, -515, -515, -515, -515, -515, -515, -515, -515, -515, + -515, -515, -515, -515, -515, -515, -515, -515, -515, -515, + -515, -515, -515, -515, -515, -515, -515, -515, -515, -515, + -515, -515, -515, -515, -515, -515, -515, -515 + }, + + { + 55, -516, -516, -516, -516, -516, -516, -516, -516, -516, + -516, -516, -516, -516, -516, -516, -516, -516, -516, -516, - if ( ! wcspihin ) - wcspihin = stdin; + -516, -516, -516, -516, -516, -516, -516, -516, -516, -516, + -516, -516, 687, -516, -516, -516, -516, -516, -516, -516, + -516, -516, -516, -516, -516, -516, -516, -516, 688, 688, + 688, 688, 688, 688, 688, 688, 688, 688, -516, -516, + -516, -516, -516, -516, -516, 687, 687, 687, 687, 687, + 687, 687, 687, 687, 687, 687, 687, 687, 687, 687, + 687, 687, 687, 687, 687, 687, 687, 687, 687, 687, + 687, -516, -516, -516, -516, -516, -516, -516, -516, -516, + -516, -516, -516, -516, -516, -516, -516, -516, -516, -516, + -516, -516, -516, -516, -516, -516, -516, -516, -516, -516, + + -516, -516, -516, -516, -516, -516, -516, -516 + }, + + { + 55, -517, -517, -517, -517, -517, -517, -517, -517, -517, + -517, -517, -517, -517, -517, -517, -517, -517, -517, -517, + -517, -517, -517, -517, -517, -517, -517, -517, -517, -517, + -517, -517, 689, -517, -517, -517, -517, -517, -517, -517, + -517, -517, -517, -517, -517, -517, -517, -517, -517, -517, + -517, -517, -517, -517, -517, -517, -517, -517, -517, -517, + -517, -517, -517, -517, -517, -517, -517, -517, -517, -517, + -517, -517, -517, -517, -517, -517, -517, -517, -517, -517, + -517, -517, -517, -517, -517, -517, -517, -517, -517, -517, - if ( ! wcspihout ) - wcspihout = stdout; + -517, -517, -517, -517, -517, -517, -517, -517, -517, -517, + -517, -517, -517, -517, -517, -517, -517, -517, -517, -517, + -517, -517, -517, -517, -517, -517, -517, -517, -517, -517, + -517, -517, -517, -517, -517, -517, -517, -517 + }, + + { + 55, -518, -518, -518, -518, -518, -518, -518, -518, -518, + -518, -518, -518, -518, -518, -518, -518, -518, -518, -518, + -518, -518, -518, -518, -518, -518, -518, -518, -518, -518, + -518, -518, 690, -518, -518, -518, -518, -518, -518, -518, + -518, -518, -518, -518, -518, -518, -518, -518, 691, 691, + 691, 691, 691, 691, 691, 691, 691, 691, -518, -518, - if ( ! YY_CURRENT_BUFFER ) { - wcspihensure_buffer_stack (); - YY_CURRENT_BUFFER_LVALUE = - wcspih_create_buffer(wcspihin,YY_BUF_SIZE ); - } + -518, -518, -518, -518, -518, 690, 690, 690, 690, 690, + 690, 690, 690, 690, 690, 690, 690, 690, 690, 690, + 690, 690, 690, 690, 690, 690, 690, 690, 690, 690, + 690, -518, -518, -518, -518, -518, -518, -518, -518, -518, + -518, -518, -518, -518, -518, -518, -518, -518, -518, -518, + -518, -518, -518, -518, -518, -518, -518, -518, -518, -518, + -518, -518, -518, -518, -518, -518, -518, -518 + }, + + { + 55, -519, -519, -519, -519, -519, -519, -519, -519, -519, + -519, -519, -519, -519, -519, -519, -519, -519, -519, -519, + -519, -519, -519, -519, -519, -519, -519, -519, -519, -519, - wcspih_load_buffer_state( ); - } + -519, -519, 681, -519, -519, -519, -519, -519, -519, -519, + -519, -519, -519, -519, -519, -519, -519, -519, 692, 693, + 693, 693, 693, 693, 693, 693, 693, 693, -519, -519, + -519, -519, -519, -519, -519, 681, 681, 681, 681, 681, + 681, 681, 681, 681, 681, 681, 681, 681, 681, 681, + 681, 681, 681, 681, 681, 681, 681, 681, 681, 681, + 681, -519, -519, -519, -519, -519, -519, -519, -519, -519, + -519, -519, -519, -519, -519, -519, -519, -519, -519, -519, + -519, -519, -519, -519, -519, -519, -519, -519, -519, -519, + -519, -519, -519, -519, -519, -519, -519, -519 + + }, + + { + 55, -520, -520, -520, -520, -520, -520, -520, -520, -520, + -520, -520, -520, -520, -520, -520, -520, -520, -520, -520, + -520, -520, -520, -520, -520, -520, -520, -520, -520, -520, + -520, -520, 694, -520, -520, -520, -520, -520, -520, -520, + -520, -520, -520, -520, -520, -520, -520, -520, 695, 695, + 695, 695, 695, 695, 695, 695, 695, 695, -520, -520, + -520, -520, -520, -520, -520, 694, 694, 694, 694, 694, + 694, 694, 694, 694, 694, 694, 694, 694, 694, 694, + 694, 694, 694, 694, 694, 694, 694, 694, 694, 694, + 694, -520, -520, -520, -520, -520, -520, -520, -520, -520, - while ( 1 ) /* loops until end-of-file is reached */ - { - yy_cp = (yy_c_buf_p); + -520, -520, -520, -520, -520, -520, -520, -520, -520, -520, + -520, -520, -520, -520, -520, -520, -520, -520, -520, -520, + -520, -520, -520, -520, -520, -520, -520, -520 + }, + + { + 55, -521, -521, -521, -521, -521, -521, -521, -521, -521, + -521, -521, -521, -521, -521, -521, -521, -521, -521, -521, + -521, -521, -521, -521, -521, -521, -521, -521, -521, -521, + -521, -521, 690, -521, -521, -521, -521, -521, -521, -521, + -521, -521, -521, -521, -521, -521, -521, -521, 696, 697, + 697, 697, 697, 697, 697, 697, 697, 697, -521, -521, + -521, -521, -521, -521, -521, 690, 690, 690, 690, 690, - /* Support of wcspihtext. */ - *yy_cp = (yy_hold_char); + 690, 690, 690, 690, 690, 690, 690, 690, 690, 690, + 690, 690, 690, 690, 690, 690, 690, 690, 690, 690, + 690, -521, -521, -521, -521, -521, -521, -521, -521, -521, + -521, -521, -521, -521, -521, -521, -521, -521, -521, -521, + -521, -521, -521, -521, -521, -521, -521, -521, -521, -521, + -521, -521, -521, -521, -521, -521, -521, -521 + }, + + { + 55, -522, -522, -522, -522, -522, -522, -522, -522, -522, + -522, -522, -522, -522, -522, -522, -522, -522, -522, -522, + -522, -522, -522, -522, -522, -522, -522, -522, -522, -522, + -522, -522, 698, -522, -522, -522, -522, -522, -522, -522, - /* yy_bp points to the position in yy_ch_buf of the start of - * the current run. - */ - yy_bp = yy_cp; + -522, -522, -522, -522, -522, -522, -522, -522, 699, 699, + 699, 699, 699, 699, 699, 699, 699, 699, -522, -522, + -522, -522, -522, -522, -522, 698, 698, 698, 698, 698, + 698, 698, 698, 698, 698, 698, 698, 698, 698, 698, + 698, 698, 698, 698, 698, 698, 698, 698, 698, 698, + 698, -522, -522, -522, -522, -522, -522, -522, -522, -522, + -522, -522, -522, -522, -522, -522, -522, -522, -522, -522, + -522, -522, -522, -522, -522, -522, -522, -522, -522, -522, + -522, -522, -522, -522, -522, -522, -522, -522 + }, + + { + 55, -523, -523, -523, -523, -523, -523, -523, -523, -523, + + -523, -523, -523, -523, -523, -523, -523, -523, -523, -523, + -523, -523, -523, -523, -523, -523, -523, -523, -523, -523, + -523, -523, 700, -523, -523, -523, -523, -523, -523, -523, + -523, -523, -523, -523, -523, -523, -523, -523, -523, -523, + -523, -523, -523, -523, -523, -523, -523, -523, -523, -523, + -523, -523, -523, -523, -523, -523, -523, -523, -523, -523, + -523, -523, -523, -523, -523, -523, -523, -523, -523, -523, + -523, -523, -523, -523, -523, -523, -523, -523, -523, -523, + -523, -523, -523, -523, -523, -523, -523, -523, -523, -523, + -523, -523, -523, -523, -523, -523, -523, -523, -523, -523, - yy_current_state = (yy_start); - yy_current_state += YY_AT_BOL(); -yy_match: - while ( (yy_current_state = yy_nxt[yy_current_state][ YY_SC_TO_UI(*yy_cp) ]) > 0 ) - { - if ( yy_accept[yy_current_state] ) - { - (yy_last_accepting_state) = yy_current_state; - (yy_last_accepting_cpos) = yy_cp; - } + -523, -523, -523, -523, -523, -523, -523, -523, -523, -523, + -523, -523, -523, -523, -523, -523, -523, -523 + }, + + { + 55, -524, -524, -524, -524, -524, -524, -524, -524, -524, + -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, + -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, + -524, -524, 701, -524, -524, -524, -524, -524, -524, -524, + -524, -524, -524, -524, -524, -524, -524, -524, 691, 691, + 691, 691, 691, 691, 691, 691, 691, 691, -524, -524, + -524, -524, -524, -524, -524, 701, 701, 701, 701, 701, + 701, 701, 701, 701, 701, 701, 701, 701, 701, 701, - ++yy_cp; - } + 701, 701, 701, 701, 701, 701, 701, 701, 701, 701, + 701, -524, -524, -524, -524, -524, -524, -524, -524, -524, + -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, + -524, -524, -524, -524, -524, -524, -524, -524, -524, -524, + -524, -524, -524, -524, -524, -524, -524, -524 + }, + + { + 55, -525, -525, -525, -525, -525, -525, -525, -525, -525, + -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, + -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, + -525, -525, 702, -525, -525, -525, -525, -525, -525, -525, + -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, - yy_current_state = -yy_current_state; + -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, + -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, + -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, + -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, + -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, + -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, + -525, -525, -525, -525, -525, -525, -525, -525, -525, -525, + -525, -525, -525, -525, -525, -525, -525, -525 + }, + + { + 55, -526, -526, -526, -526, -526, -526, -526, -526, -526, + -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, -yy_find_action: - yy_act = yy_accept[yy_current_state]; + -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, + -526, -526, 703, -526, -526, -526, -526, -526, -526, -526, + -526, -526, -526, -526, -526, -526, -526, -526, 704, 704, + 704, 704, 704, 704, 704, 704, 704, 704, -526, -526, + -526, -526, -526, -526, -526, 703, 703, 703, 703, 703, + 703, 703, 703, 703, 703, 703, 703, 703, 703, 703, + 703, 703, 703, 703, 703, 703, 703, 703, 703, 703, + 703, -526, -526, -526, -526, -526, -526, -526, -526, -526, + -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, + -526, -526, -526, -526, -526, -526, -526, -526, -526, -526, + + -526, -526, -526, -526, -526, -526, -526, -526 + }, + + { + 55, -527, -527, -527, -527, -527, -527, -527, -527, -527, + -527, -527, -527, -527, -527, -527, -527, -527, -527, -527, + -527, -527, -527, -527, -527, -527, -527, -527, -527, -527, + -527, -527, 705, -527, -527, -527, -527, -527, -527, -527, + -527, -527, -527, -527, -527, -527, -527, -527, 706, 706, + 706, 706, 706, 706, 706, 706, 706, 706, -527, -527, + -527, -527, -527, -527, -527, 705, 705, 705, 705, 705, + 705, 705, 705, 705, 705, 705, 705, 705, 705, 705, + 705, 705, 705, 705, 705, 705, 705, 705, 705, 705, - YY_DO_BEFORE_ACTION; + 705, -527, -527, -527, -527, -527, -527, -527, -527, -527, + -527, -527, -527, -527, -527, -527, -527, -527, -527, -527, + -527, -527, -527, -527, -527, -527, -527, -527, -527, -527, + -527, -527, -527, -527, -527, -527, -527, -527 + }, + + { + 55, -528, -528, -528, -528, -528, -528, -528, -528, -528, + -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, + -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, + -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, + -528, -528, -528, -528, -528, -528, -528, -528, 707, 707, + 707, 707, 707, 707, 707, 707, 707, 707, -528, -528, -do_action: /* This label is used only to access EOF actions. */ + -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, + -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, + -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, + -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, + -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, + -528, -528, -528, -528, -528, -528, -528, -528, -528, -528, + -528, -528, -528, -528, -528, -528, -528, -528 + }, + + { + 55, -529, -529, -529, -529, -529, -529, -529, -529, -529, + -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, + -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, - switch ( yy_act ) - { /* beginning of action switch */ - case 0: /* must back up */ - /* undo the effects of YY_DO_BEFORE_ACTION */ - *yy_cp = (yy_hold_char); - yy_cp = (yy_last_accepting_cpos) + 1; - yy_current_state = (yy_last_accepting_state); - goto yy_find_action; + -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, + -529, -529, -529, -529, -529, 708, -529, -529, -529, -529, + -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, + -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, + -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, + -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, + -529, -529, -529, -529, -529, 709, -529, -529, -529, -529, + -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, + -529, -529, -529, -529, -529, -529, -529, -529, -529, -529, + -529, -529, -529, -529, -529, -529, -529, -529 + + }, + + { + 55, -530, -530, -530, -530, -530, -530, -530, -530, -530, + -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, + -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, + -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, + -530, -530, -530, -530, -530, 708, -530, -530, -530, -530, + -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, + -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, + -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, + -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, + -530, -530, -530, -530, -530, 710, -530, -530, -530, -530, -case 1: -YY_RULE_SETUP -#line 204 "wcspih.l" -{ - if (pass == 1) { - sscanf(wcspihtext, "NAXIS = %d", &naxis); - } - - if (naxis < 0) { - errmsg = errtxt; - sprintf(errmsg, "Negative value of NAXIS ignored: %d", naxis); - naxis = 0; - BEGIN(ERROR); - } else { - BEGIN(DISCARD); - } + -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, + -530, -530, -530, -530, -530, -530, -530, -530, -530, -530, + -530, -530, -530, -530, -530, -530, -530, -530 + }, + + { + 55, -531, -531, -531, -531, -531, -531, -531, -531, -531, + -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, + -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, + -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, + -531, -531, -531, -531, -531, -531, -531, -531, 711, 711, + 711, 711, 711, 711, 711, 711, 711, 711, -531, -531, + -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, + + -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, + -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, + -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, + -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, + -531, -531, -531, -531, -531, -531, -531, -531, -531, -531, + -531, -531, -531, -531, -531, -531, -531, -531 + }, + + { + 55, -532, -532, -532, -532, -532, -532, -532, -532, -532, + -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, + -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, + -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, + + -532, -532, -532, -532, -532, 708, -532, -532, -532, -532, + -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, + -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, + -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, + -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, + -532, -532, -532, -532, -532, 712, -532, -532, -532, -532, + -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, + -532, -532, -532, -532, -532, -532, -532, -532, -532, -532, + -532, -532, -532, -532, -532, -532, -532, -532 + }, + + { + 55, -533, -533, -533, -533, -533, -533, -533, -533, -533, + + -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, + -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, + -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, + -533, -533, -533, -533, -533, -533, -533, -533, 713, 714, + 714, 714, 714, 714, 714, 714, 714, 714, -533, -533, + -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, + -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, + -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, + -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, + -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, + + -533, -533, -533, -533, -533, -533, -533, -533, -533, -533, + -533, -533, -533, -533, -533, -533, -533, -533 + }, + + { + 55, -534, -534, -534, -534, -534, -534, -534, -534, -534, + -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, + -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, + -534, -534, 715, -534, -534, -534, -534, -534, -534, -534, + -534, -534, -534, -534, -534, -534, -534, -534, 716, 716, + 716, 716, 716, 716, 716, 716, 716, 716, -534, -534, + -534, -534, -534, -534, -534, 715, 715, 715, 715, 715, + 715, 715, 715, 715, 715, 715, 715, 715, 715, 715, + + 715, 715, 715, 715, 715, 715, 715, 715, 715, 715, + 715, -534, -534, -534, -534, -534, -534, -534, -534, -534, + -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, + -534, -534, -534, -534, -534, -534, -534, -534, -534, -534, + -534, -534, -534, -534, -534, -534, -534, -534 + }, + + { + 55, -535, -535, -535, -535, -535, -535, -535, -535, -535, + -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, + -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, + -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, + -535, -535, -535, -535, -535, -535, -535, -535, 717, 718, + + 718, 718, 718, 718, 718, 718, 718, 718, -535, -535, + -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, + -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, + -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, + -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, + -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, + -535, -535, -535, -535, -535, -535, -535, -535, -535, -535, + -535, -535, -535, -535, -535, -535, -535, -535 + }, + + { + 55, -536, -536, -536, -536, -536, -536, -536, -536, -536, + -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, + + -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, + -536, -536, 719, -536, -536, -536, -536, -536, -536, -536, + -536, -536, -536, -536, -536, -536, -536, -536, 720, 721, + 721, 721, 721, 721, 721, 721, 721, 721, -536, -536, + -536, -536, -536, -536, -536, 719, 719, 719, 719, 719, + 719, 719, 719, 719, 719, 719, 719, 719, 719, 719, + 719, 719, 719, 719, 719, 719, 719, 719, 719, 719, + 719, -536, -536, -536, -536, -536, -536, -536, -536, -536, + -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, + -536, -536, -536, -536, -536, -536, -536, -536, -536, -536, + + -536, -536, -536, -536, -536, -536, -536, -536 + }, + + { + 55, -537, -537, -537, -537, -537, -537, -537, -537, -537, + -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, + -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, + -537, -537, 719, -537, -537, -537, -537, -537, -537, -537, + -537, -537, -537, -537, -537, -537, -537, -537, 722, 722, + 722, 722, 722, 722, 722, 722, 722, 722, -537, -537, + -537, -537, -537, -537, -537, 719, 719, 719, 719, 719, + 719, 719, 719, 719, 719, 719, 719, 719, 719, 719, + 719, 719, 719, 719, 719, 719, 719, 719, 719, 719, + + 719, -537, -537, -537, -537, -537, -537, -537, -537, -537, + -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, + -537, -537, -537, -537, -537, -537, -537, -537, -537, -537, + -537, -537, -537, -537, -537, -537, -537, -537 + }, + + { + 55, -538, -538, -538, -538, -538, -538, -538, -538, -538, + -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, + -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, + -538, -538, 723, -538, -538, -538, -538, -538, -538, -538, + -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, + -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, + + -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, + -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, + -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, + -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, + -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, + -538, -538, -538, -538, -538, -538, -538, -538, -538, -538, + -538, -538, -538, -538, -538, -538, -538, -538 + }, + + { + 55, -539, -539, -539, -539, -539, -539, -539, -539, -539, + -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, + -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, + + -539, -539, 724, -539, -539, -539, -539, -539, -539, -539, + -539, -539, -539, -539, -539, -539, -539, -539, 725, 725, + 725, 725, 725, 725, 725, 725, 725, 725, -539, -539, + -539, -539, -539, -539, -539, 724, 724, 724, 724, 724, + 724, 724, 724, 724, 724, 724, 724, 724, 724, 724, + 724, 724, 724, 724, 724, 724, 724, 724, 724, 724, + 724, -539, -539, -539, -539, -539, -539, -539, -539, -539, + -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, + -539, -539, -539, -539, -539, -539, -539, -539, -539, -539, + -539, -539, -539, -539, -539, -539, -539, -539 + + }, + + { + 55, -540, -540, -540, -540, -540, -540, -540, -540, -540, + -540, -540, -540, -540, -540, -540, -540, -540, -540, -540, + -540, -540, -540, -540, -540, -540, -540, -540, -540, -540, + -540, -540, 726, -540, -540, -540, -540, -540, -540, -540, + -540, -540, -540, -540, -540, -540, -540, -540, 727, 728, + 728, 728, 728, 728, 728, 728, 728, 728, -540, -540, + -540, -540, -540, -540, -540, 726, 726, 726, 726, 726, + 726, 726, 726, 726, 726, 726, 726, 726, 726, 726, + 726, 726, 726, 726, 726, 726, 726, 726, 726, 726, + 726, -540, -540, -540, -540, -540, -540, -540, -540, -540, + + -540, -540, -540, -540, -540, -540, -540, -540, -540, -540, + -540, -540, -540, -540, -540, -540, -540, -540, -540, -540, + -540, -540, -540, -540, -540, -540, -540, -540 + }, + + { + 55, -541, -541, -541, -541, -541, -541, -541, -541, -541, + -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, + -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, + -541, -541, 726, -541, -541, -541, -541, -541, -541, -541, + -541, -541, -541, -541, -541, -541, -541, -541, 729, 729, + 729, 729, 729, 729, 729, 729, 729, 729, -541, -541, + -541, -541, -541, -541, -541, 726, 726, 726, 726, 726, + + 726, 726, 726, 726, 726, 726, 726, 726, 726, 726, + 726, 726, 726, 726, 726, 726, 726, 726, 726, 726, + 726, -541, -541, -541, -541, -541, -541, -541, -541, -541, + -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, + -541, -541, -541, -541, -541, -541, -541, -541, -541, -541, + -541, -541, -541, -541, -541, -541, -541, -541 + }, + + { + 55, -542, -542, -542, -542, -542, -542, -542, -542, -542, + -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, + -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, + -542, -542, 730, -542, -542, -542, -542, -542, -542, -542, + + -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, + -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, + -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, + -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, + -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, + -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, + -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, + -542, -542, -542, -542, -542, -542, -542, -542, -542, -542, + -542, -542, -542, -542, -542, -542, -542, -542 + }, + + { + 55, -543, -543, -543, -543, -543, -543, -543, -543, -543, + + -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, + -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, + -543, -543, 731, -543, -543, -543, -543, -543, -543, -543, + -543, -543, -543, -543, -543, -543, -543, -543, 732, 733, + 733, 733, 733, 733, 733, 733, 733, 733, -543, -543, + -543, -543, -543, -543, -543, 731, 731, 731, 731, 731, + 731, 731, 731, 731, 731, 731, 731, 731, 731, 731, + 731, 731, 731, 731, 731, 731, 731, 731, 731, 731, + 731, -543, -543, -543, -543, -543, -543, -543, -543, -543, + -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, + + -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, + -543, -543, -543, -543, -543, -543, -543, -543 + }, + + { + 55, -544, -544, -544, -544, -544, -544, -544, -544, -544, + -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, + -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, + -544, -544, 731, -544, -544, -544, -544, -544, -544, -544, + -544, -544, -544, -544, -544, -544, -544, -544, 734, 734, + 734, 734, 734, 734, 734, 734, 734, 734, -544, -544, + -544, -544, -544, -544, -544, 731, 731, 731, 731, 731, + 731, 731, 731, 731, 731, 731, 731, 731, 731, 731, + + 731, 731, 731, 731, 731, 731, 731, 731, 731, 731, + 731, -544, -544, -544, -544, -544, -544, -544, -544, -544, + -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, + -544, -544, -544, -544, -544, -544, -544, -544, -544, -544, + -544, -544, -544, -544, -544, -544, -544, -544 + }, + + { + 55, -545, -545, -545, -545, -545, -545, -545, -545, -545, + -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, + -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, + -545, -545, 735, -545, -545, -545, -545, -545, -545, -545, + -545, -545, -545, -545, -545, -545, -545, -545, 725, 725, + + 725, 725, 725, 725, 725, 725, 725, 725, -545, -545, + -545, -545, -545, -545, -545, 735, 735, 735, 735, 735, + 735, 735, 735, 735, 735, 735, 735, 735, 735, 735, + 735, 735, 735, 735, 735, 735, 735, 735, 735, 735, + 735, -545, -545, -545, -545, -545, -545, -545, -545, -545, + -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, + -545, -545, -545, -545, -545, -545, -545, -545, -545, -545, + -545, -545, -545, -545, -545, -545, -545, -545 + }, + + { + 55, -546, -546, -546, -546, -546, -546, -546, -546, -546, + -546, -546, -546, -546, -546, -546, -546, -546, -546, -546, + + -546, -546, -546, -546, -546, -546, -546, -546, -546, -546, + -546, -546, 736, -546, -546, -546, -546, -546, -546, -546, + -546, -546, -546, -546, -546, -546, -546, -546, -546, -546, + -546, -546, -546, -546, -546, -546, -546, -546, -546, -546, + -546, -546, -546, -546, -546, -546, -546, -546, -546, -546, + -546, -546, -546, -546, -546, -546, -546, -546, -546, -546, + -546, -546, -546, -546, -546, -546, -546, -546, -546, -546, + -546, -546, -546, -546, -546, -546, -546, -546, -546, -546, + -546, -546, -546, -546, -546, -546, -546, -546, -546, -546, + -546, -546, -546, -546, -546, -546, -546, -546, -546, -546, + + -546, -546, -546, -546, -546, -546, -546, -546 + }, + + { + 55, -547, -547, -547, -547, -547, -547, -547, -547, -547, + -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, + -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, + -547, -547, 737, -547, -547, -547, -547, -547, -547, -547, + -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, + -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, + -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, + -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, + -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, + + -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, + -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, + -547, -547, -547, -547, -547, -547, -547, -547, -547, -547, + -547, -547, -547, -547, -547, -547, -547, -547 + }, + + { + 55, -548, -548, -548, -548, -548, -548, -548, -548, -548, + -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, + -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, + -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, + -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, + -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, + + -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, + -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, + -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, + -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, + -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, + -548, -548, -548, -548, -548, -548, -548, -548, -548, -548, + -548, -548, -548, -548, -548, -548, -548, -548 + }, + + { + 55, -549, -549, -549, -549, -549, -549, -549, -549, -549, + -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, + -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, + + -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, + -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, + -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, + -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, + -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, + -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, + -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, + -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, + -549, -549, -549, -549, -549, -549, -549, -549, -549, -549, + -549, -549, -549, -549, -549, -549, -549, -549 + + }, + + { + 55, 550, 550, 550, 550, 550, 550, 550, 550, 550, + 550, 550, 550, 550, 550, 550, 550, 550, 550, 550, + 550, 550, 550, 550, 550, 550, 550, 550, 550, 550, + 550, 550, 550, 550, 550, 550, 550, 550, 550, 551, + 550, 550, 550, 550, 550, 550, 550, 550, 550, 550, + 550, 550, 550, 550, 550, 550, 550, 550, 550, 550, + 550, 550, 550, 550, 550, 550, 550, 550, 550, 550, + 550, 550, 550, 550, 550, 550, 550, 550, 550, 550, + 550, 550, 550, 550, 550, 550, 550, 550, 550, 550, + 550, 550, 550, 550, 550, 550, 550, 550, 550, 550, + + 550, 550, 550, 550, 550, 550, 550, 550, 550, 550, + 550, 550, 550, 550, 550, 550, 550, 550, 550, 550, + 550, 550, 550, 550, 550, 550, 550, 550 + }, + + { + 55, -551, -551, -551, -551, -551, -551, -551, -551, -551, + -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, + -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, + -551, -551, -551, -551, -551, -551, -551, -551, -551, 550, + -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, + -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, + -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, + + -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, + -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, + -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, + -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, + -551, -551, -551, -551, -551, -551, -551, -551, -551, -551, + -551, -551, -551, -551, -551, -551, -551, -551 + }, + + { + 55, -552, -552, -552, -552, -552, -552, -552, -552, -552, + -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, + -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, + -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, + + -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, + -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, + -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, + -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, + -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, + -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, + -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, + -552, -552, -552, -552, -552, -552, -552, -552, -552, -552, + -552, -552, -552, -552, -552, -552, -552, -552 + }, + + { + 55, 400, 400, 400, 400, 400, 400, 400, 400, 400, + + 552, 400, 400, 400, 400, 400, 400, 400, 400, 400, + 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, + 400, 400, 553, 400, 400, 400, 400, 400, 400, 400, + 400, 400, 400, 400, 400, 400, 400, 554, 400, 400, + 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, + 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, + 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, + 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, + 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, + 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, + + 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, + 400, 400, 400, 400, 400, 400, 400, 400 + }, + + { + 55, 738, 738, 738, 738, 738, 738, 738, 738, 738, + 739, 738, 738, 738, 738, 738, 738, 738, 738, 738, + 738, 738, 738, 738, 738, 738, 738, 738, 738, 738, + 738, 738, 740, 738, 738, 738, 738, 738, 738, 738, + 738, 738, 738, 738, 738, 738, 738, 741, 738, 738, + 738, 738, 738, 738, 738, 738, 738, 738, 738, 738, + 738, 738, 738, 738, 738, 738, 738, 738, 738, 738, + 738, 738, 738, 738, 738, 738, 738, 738, 738, 738, + + 738, 738, 738, 738, 738, 738, 738, 738, 738, 738, + 738, 738, 738, 738, 738, 738, 738, 738, 738, 738, + 738, 738, 738, 738, 738, 738, 738, 738, 738, 738, + 738, 738, 738, 738, 738, 738, 738, 738, 738, 738, + 738, 738, 738, 738, 738, 738, 738, 738 + }, + + { + 55, 555, 555, 555, 555, 555, 555, 555, 555, 555, + 556, 555, 555, 555, 555, 555, 555, 555, 555, 555, + 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, + 555, 555, 557, 555, 555, 555, 555, 555, 555, 555, + 555, 555, 555, 555, 555, 555, 555, 558, 555, 555, + + 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, + 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, + 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, + 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, + 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, + 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, + 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, + 555, 555, 555, 555, 555, 555, 555, 555 + }, + + { + 55, -556, -556, -556, -556, -556, -556, -556, -556, -556, + -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, + + -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, + -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, + -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, + -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, + -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, + -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, + -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, + -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, + -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, + -556, -556, -556, -556, -556, -556, -556, -556, -556, -556, + + -556, -556, -556, -556, -556, -556, -556, -556 + }, + + { + 55, 555, 555, 555, 555, 555, 555, 555, 555, 555, + 556, 555, 555, 555, 555, 555, 555, 555, 555, 555, + 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, + 555, 555, 557, 555, 555, 555, 555, 555, 555, 555, + 555, 555, 555, 555, 555, 555, 555, 558, 555, 555, + 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, + 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, + 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, + 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, + + 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, + 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, + 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, + 555, 555, 555, 555, 555, 555, 555, 555 + }, + + { + 55, 555, 555, 555, 555, 555, 555, 555, 555, 555, + 556, 555, 555, 555, 555, 555, 555, 555, 555, 555, + 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, + 555, 555, 557, 555, 555, 555, 555, 555, 555, 555, + 555, 555, 555, 555, 555, 555, 555, 558, 555, 555, + 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, + + 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, + 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, + 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, + 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, + 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, + 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, + 555, 555, 555, 555, 555, 555, 555, 555 + }, + + { + 55, 559, 559, 559, 559, 559, 559, 559, 559, 559, + 560, 559, 559, 559, 559, 559, 559, 559, 559, 559, + 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, + + 559, 559, 561, 559, 559, 559, 559, 559, 559, 559, + 559, 559, 559, 559, 559, 559, 559, 562, 559, 559, + 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, + 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, + 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, + 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, + 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, + 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, + 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, + 559, 559, 559, 559, 559, 559, 559, 559 + + }, + + { + 55, -560, -560, -560, -560, -560, -560, -560, -560, -560, + -560, -560, -560, -560, -560, -560, -560, -560, -560, -560, + -560, -560, -560, -560, -560, -560, -560, -560, -560, -560, + -560, -560, -560, -560, -560, -560, -560, -560, -560, -560, + -560, -560, -560, -560, -560, -560, -560, -560, -560, -560, + -560, -560, -560, -560, -560, -560, -560, -560, -560, -560, + -560, -560, -560, -560, -560, -560, -560, -560, -560, -560, + -560, -560, -560, -560, -560, -560, -560, -560, -560, -560, + -560, -560, -560, -560, -560, -560, -560, -560, -560, -560, + -560, -560, -560, -560, -560, -560, -560, -560, -560, -560, + + -560, -560, -560, -560, -560, -560, -560, -560, -560, -560, + -560, -560, -560, -560, -560, -560, -560, -560, -560, -560, + -560, -560, -560, -560, -560, -560, -560, -560 + }, + + { + 55, 559, 559, 559, 559, 559, 559, 559, 559, 559, + 560, 559, 559, 559, 559, 559, 559, 559, 559, 559, + 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, + 559, 559, 561, 559, 559, 559, 559, 559, 559, 559, + 559, 559, 559, 559, 559, 559, 559, 562, 559, 559, + 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, + 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, + + 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, + 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, + 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, + 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, + 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, + 559, 559, 559, 559, 559, 559, 559, 559 + }, + + { + 55, 559, 559, 559, 559, 559, 559, 559, 559, 559, + 560, 559, 559, 559, 559, 559, 559, 559, 559, 559, + 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, + 559, 559, 561, 559, 559, 559, 559, 559, 559, 559, + + 559, 559, 559, 559, 559, 559, 559, 562, 559, 559, + 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, + 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, + 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, + 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, + 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, + 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, + 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, + 559, 559, 559, 559, 559, 559, 559, 559 + }, + + { + 55, -563, -563, -563, -563, -563, -563, -563, -563, -563, + + -563, -563, -563, -563, -563, -563, -563, -563, -563, -563, + -563, -563, -563, -563, -563, -563, -563, -563, -563, -563, + -563, -563, -563, -563, -563, -563, -563, -563, -563, -563, + -563, -563, -563, -563, -563, -563, -563, -563, -563, -563, + -563, -563, -563, -563, -563, -563, -563, -563, -563, -563, + -563, -563, -563, -563, -563, -563, -563, -563, 742, -563, + -563, -563, -563, -563, -563, -563, -563, -563, -563, -563, + -563, -563, -563, -563, -563, -563, -563, -563, -563, -563, + -563, -563, -563, -563, -563, -563, -563, -563, -563, -563, + -563, -563, -563, -563, -563, -563, -563, -563, -563, -563, + + -563, -563, -563, -563, -563, -563, -563, -563, -563, -563, + -563, -563, -563, -563, -563, -563, -563, -563 + }, + + { + 55, -564, -564, -564, -564, -564, -564, -564, -564, -564, + -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, + -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, + -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, + -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, + -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, + -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, + -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, + + -564, -564, -564, -564, -564, -564, -564, -564, 743, -564, + -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, + -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, + -564, -564, -564, -564, -564, -564, -564, -564, -564, -564, + -564, -564, -564, -564, -564, -564, -564, -564 + }, + + { + 55, -565, -565, -565, -565, -565, -565, -565, -565, -565, + -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, + -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, + -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, + -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, + + -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, + -565, -565, -565, -565, -565, -565, -565, -565, -565, 744, + -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, + -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, + -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, + -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, + -565, -565, -565, -565, -565, -565, -565, -565, -565, -565, + -565, -565, -565, -565, -565, -565, -565, -565 + }, + + { + 55, -566, -566, -566, -566, -566, -566, -566, -566, -566, + -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, + + -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, + -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, + -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, + -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, + -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, + -566, -566, -566, 745, -566, -566, -566, -566, -566, -566, + -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, + -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, + -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, + -566, -566, -566, -566, -566, -566, -566, -566, -566, -566, + + -566, -566, -566, -566, -566, -566, -566, -566 + }, + + { + 55, -567, -567, -567, -567, -567, -567, -567, -567, -567, + -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, + -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, + -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, + -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, + -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, + -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, + -567, -567, -567, -567, -567, -567, -567, -567, -567, 746, + -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, + + -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, + -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, + -567, -567, -567, -567, -567, -567, -567, -567, -567, -567, + -567, -567, -567, -567, -567, -567, -567, -567 + }, + + { + 55, -568, -568, -568, -568, -568, -568, -568, -568, -568, + -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, + -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, + -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, + -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, + -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, + + -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, + -568, -568, 747, -568, -568, -568, -568, -568, -568, -568, + -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, + -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, + -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, + -568, -568, -568, -568, -568, -568, -568, -568, -568, -568, + -568, -568, -568, -568, -568, -568, -568, -568 + }, + + { + 55, -569, -569, -569, -569, -569, -569, -569, -569, -569, + -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, + -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, + + -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, + -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, + -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, + -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, + -569, -569, -569, -569, -569, -569, -569, -569, -569, 748, + -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, + -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, + -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, + -569, -569, -569, -569, -569, -569, -569, -569, -569, -569, + -569, -569, -569, -569, -569, -569, -569, -569 + + }, + + { + 55, -570, -570, -570, -570, -570, -570, -570, -570, -570, + -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, + -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, + -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, + -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, + -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, + -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, + -570, -570, -570, -570, -570, -570, -570, -570, -570, 749, + -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, + -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, + + -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, + -570, -570, -570, -570, -570, -570, -570, -570, -570, -570, + -570, -570, -570, -570, -570, -570, -570, -570 + }, + + { + 55, -571, -571, -571, -571, -571, -571, -571, -571, -571, + -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, + -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, + -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, + -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, + -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, + -571, -571, -571, -571, -571, -571, -571, -571, 750, -571, + + -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, + -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, + -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, + -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, + -571, -571, -571, -571, -571, -571, -571, -571, -571, -571, + -571, -571, -571, -571, -571, -571, -571, -571 + }, + + { + 55, -572, -572, -572, -572, -572, -572, -572, -572, -572, + -572, -572, -572, -572, -572, -572, -572, -572, -572, -572, + -572, -572, -572, -572, -572, -572, -572, -572, -572, -572, + -572, -572, -572, -572, -572, -572, -572, -572, -572, -572, + + -572, -572, -572, -572, -572, -572, -572, -572, -572, -572, + -572, -572, -572, -572, -572, -572, -572, -572, -572, -572, + -572, -572, -572, -572, -572, -572, -572, -572, -572, -572, + -572, -572, -572, -572, -572, -572, -572, -572, -572, -572, + -572, -572, -572, -572, -572, -572, -572, -572, 751, -572, + -572, -572, -572, -572, -572, -572, -572, -572, -572, -572, + -572, -572, -572, -572, -572, -572, -572, -572, -572, -572, + -572, -572, -572, -572, -572, -572, -572, -572, -572, -572, + -572, -572, -572, -572, -572, -572, -572, -572 + }, + + { + 55, -573, -573, -573, -573, -573, -573, -573, -573, -573, + + -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, + -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, + -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, + -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, + -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, + -573, -573, -573, -573, -573, -573, -573, -573, -573, 752, + -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, + -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, + -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, + -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, + + -573, -573, -573, -573, -573, -573, -573, -573, -573, -573, + -573, -573, -573, -573, -573, -573, -573, -573 + }, + + { + 55, -574, -574, -574, -574, -574, -574, -574, -574, -574, + -574, -574, -574, -574, -574, -574, -574, -574, -574, -574, + -574, -574, -574, -574, -574, -574, -574, -574, -574, -574, + -574, -574, -574, -574, -574, -574, -574, -574, -574, -574, + -574, -574, -574, -574, -574, -574, -574, -574, -574, -574, + -574, -574, -574, -574, -574, -574, -574, -574, -574, -574, + -574, -574, -574, -574, -574, -574, -574, -574, -574, -574, + -574, -574, -574, 753, -574, -574, -574, -574, -574, -574, + + -574, -574, -574, -574, -574, -574, -574, -574, -574, -574, + -574, -574, -574, -574, -574, -574, -574, -574, -574, -574, + -574, -574, -574, -574, -574, -574, -574, -574, -574, -574, + -574, -574, -574, -574, -574, -574, -574, -574, -574, -574, + -574, -574, -574, -574, -574, -574, -574, -574 + }, + + { + 55, -575, -575, -575, -575, -575, -575, -575, -575, -575, + -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, + -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, + -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, + -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, + + -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, + -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, + -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, + -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, + -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, + -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, + -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, + -575, -575, -575, -575, -575, -575, -575, -575 + }, + + { + 55, -576, -576, -576, -576, -576, -576, -576, -576, -576, + -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, + + -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, + -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, + -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, + -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, + -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, + -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, + -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, + -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, + -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, + -576, -576, -576, -576, -576, -576, -576, -576, -576, -576, + + -576, -576, -576, -576, -576, -576, -576, -576 + }, + + { + 55, -577, -577, -577, -577, -577, -577, -577, -577, -577, + -577, -577, -577, -577, -577, -577, -577, -577, -577, -577, + -577, -577, -577, -577, -577, -577, -577, -577, -577, -577, + -577, -577, -577, -577, -577, -577, -577, -577, -577, -577, + -577, -577, -577, -577, -577, -577, -577, -577, -577, 754, + 755, -577, -577, -577, -577, -577, -577, -577, -577, -577, + -577, -577, -577, -577, -577, -577, -577, -577, -577, -577, + -577, -577, -577, -577, -577, -577, -577, -577, -577, -577, + -577, -577, -577, -577, -577, -577, -577, -577, -577, -577, + + -577, -577, -577, -577, -577, -577, -577, -577, -577, -577, + -577, -577, -577, -577, -577, -577, -577, -577, -577, -577, + -577, -577, -577, -577, -577, -577, -577, -577, -577, -577, + -577, -577, -577, -577, -577, -577, -577, -577 + }, + + { + 55, -578, -578, -578, -578, -578, -578, -578, -578, -578, + -578, -578, -578, -578, -578, -578, -578, -578, -578, -578, + -578, -578, -578, -578, -578, -578, -578, -578, -578, -578, + -578, -578, -578, -578, -578, -578, -578, -578, -578, -578, + -578, -578, -578, -578, -578, -578, -578, -578, -578, -578, + -578, -578, -578, -578, -578, -578, -578, -578, -578, -578, + + -578, -578, -578, -578, -578, -578, -578, -578, -578, -578, + -578, -578, -578, -578, -578, -578, -578, -578, -578, -578, + -578, -578, -578, -578, -578, -578, -578, -578, -578, -578, + -578, -578, -578, -578, -578, -578, -578, -578, -578, -578, + -578, -578, -578, -578, -578, -578, -578, -578, -578, -578, + -578, -578, -578, -578, -578, -578, -578, -578, -578, -578, + -578, -578, -578, -578, -578, -578, -578, -578 + }, + + { + 55, -579, -579, -579, -579, -579, -579, -579, -579, -579, + -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, + -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, + + -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, + -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, + -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, + -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, + -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, + -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, + -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, + -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, + -579, -579, -579, -579, -579, -579, -579, -579, -579, -579, + -579, -579, -579, -579, -579, -579, -579, -579 + + }, + + { + 55, -580, -580, -580, -580, -580, -580, -580, -580, -580, + -580, -580, -580, -580, -580, -580, -580, -580, -580, -580, + -580, -580, -580, -580, -580, -580, -580, -580, -580, -580, + -580, -580, -580, -580, -580, -580, -580, -580, -580, -580, + -580, -580, -580, -580, -580, -580, -580, -580, -580, -580, + -580, -580, -580, -580, -580, -580, -580, -580, -580, -580, + -580, -580, -580, -580, -580, -580, -580, -580, -580, -580, + -580, -580, -580, -580, -580, -580, -580, -580, -580, -580, + -580, -580, -580, -580, -580, -580, -580, -580, -580, -580, + -580, -580, -580, -580, -580, -580, -580, -580, -580, -580, + + -580, -580, -580, -580, -580, -580, -580, -580, -580, -580, + -580, -580, -580, -580, -580, -580, -580, -580, -580, -580, + -580, -580, -580, -580, -580, -580, -580, -580 + }, + + { + 55, -581, -581, -581, -581, -581, -581, -581, -581, -581, + -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, + -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, + -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, + -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, + -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, + -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, + + -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, + -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, + -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, + -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, + -581, -581, -581, -581, -581, -581, -581, -581, -581, -581, + -581, -581, -581, -581, -581, -581, -581, -581 + }, + + { + 55, -582, -582, -582, -582, -582, -582, -582, -582, -582, + -582, -582, -582, -582, -582, -582, -582, -582, -582, -582, + -582, -582, -582, -582, -582, -582, -582, -582, -582, -582, + -582, -582, -582, -582, -582, -582, -582, -582, -582, -582, + + -582, -582, -582, -582, -582, -582, -582, -582, -582, -582, + -582, -582, -582, -582, -582, -582, -582, -582, -582, -582, + -582, -582, -582, -582, -582, -582, -582, -582, -582, -582, + -582, -582, -582, -582, -582, -582, -582, -582, -582, -582, + -582, -582, -582, -582, -582, -582, -582, -582, -582, -582, + -582, -582, -582, -582, -582, -582, -582, -582, -582, -582, + -582, -582, -582, -582, -582, -582, -582, -582, -582, -582, + -582, -582, -582, -582, -582, -582, -582, -582, -582, -582, + -582, -582, -582, -582, -582, -582, -582, -582 + }, + + { + 55, -583, -583, -583, -583, -583, -583, -583, -583, -583, + + -583, -583, -583, -583, -583, -583, -583, -583, -583, -583, + -583, -583, -583, -583, -583, -583, -583, -583, -583, -583, + -583, -583, -583, -583, -583, -583, -583, -583, -583, -583, + -583, -583, -583, -583, -583, -583, -583, -583, -583, -583, + -583, -583, -583, -583, -583, -583, -583, -583, -583, -583, + -583, -583, -583, -583, -583, -583, -583, -583, -583, -583, + -583, -583, -583, -583, -583, -583, -583, -583, -583, -583, + -583, -583, -583, -583, -583, -583, -583, -583, -583, -583, + -583, -583, -583, -583, -583, -583, -583, -583, -583, -583, + -583, -583, -583, -583, -583, -583, -583, -583, -583, -583, + + -583, -583, -583, -583, -583, -583, -583, -583, -583, -583, + -583, -583, -583, -583, -583, -583, -583, -583 + }, + + { + 55, -584, -584, -584, -584, -584, -584, -584, -584, -584, + -584, -584, -584, -584, -584, -584, -584, -584, -584, -584, + -584, -584, -584, -584, -584, -584, -584, -584, -584, -584, + -584, -584, -584, -584, -584, -584, -584, -584, -584, -584, + -584, -584, -584, -584, -584, -584, -584, -584, -584, -584, + -584, -584, -584, -584, -584, -584, -584, -584, -584, -584, + -584, -584, -584, -584, -584, -584, -584, -584, -584, -584, + -584, -584, -584, -584, -584, -584, -584, -584, -584, 756, + + -584, -584, -584, -584, -584, -584, -584, -584, -584, -584, + -584, -584, -584, -584, -584, -584, -584, -584, -584, -584, + -584, -584, -584, -584, -584, -584, -584, -584, -584, -584, + -584, -584, -584, -584, -584, -584, -584, -584, -584, -584, + -584, -584, -584, -584, -584, -584, -584, -584 + }, + + { + 55, -585, -585, -585, -585, -585, -585, -585, -585, -585, + -585, -585, -585, -585, -585, -585, -585, -585, -585, -585, + -585, -585, -585, -585, -585, -585, -585, -585, -585, -585, + -585, -585, -585, -585, -585, -585, -585, -585, -585, -585, + -585, -585, -585, -585, -585, -585, -585, -585, -585, -585, + + -585, -585, -585, -585, -585, -585, -585, -585, -585, -585, + -585, -585, -585, -585, -585, -585, -585, -585, -585, -585, + -585, -585, -585, -585, -585, -585, -585, -585, -585, 757, + -585, -585, -585, -585, -585, -585, -585, -585, -585, -585, + -585, -585, -585, -585, -585, -585, -585, -585, -585, -585, + -585, -585, -585, -585, -585, -585, -585, -585, -585, -585, + -585, -585, -585, -585, -585, -585, -585, -585, -585, -585, + -585, -585, -585, -585, -585, -585, -585, -585 + }, + + { + 55, -586, -586, -586, -586, -586, -586, -586, -586, -586, + -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, + + -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, + -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, + -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, + -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, + -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, + -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, + -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, + -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, + -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, + -586, -586, -586, -586, -586, -586, -586, -586, -586, -586, + + -586, -586, -586, -586, -586, -586, -586, -586 + }, + + { + 55, -587, -587, -587, -587, -587, -587, -587, -587, -587, + -587, -587, -587, -587, -587, -587, -587, -587, -587, -587, + -587, -587, -587, -587, -587, -587, -587, -587, -587, -587, + -587, -587, -587, -587, -587, -587, -587, -587, -587, -587, + -587, -587, -587, -587, -587, -587, -587, -587, -587, -587, + -587, -587, -587, -587, -587, -587, -587, -587, -587, -587, + -587, -587, -587, -587, -587, -587, -587, -587, -587, -587, + -587, -587, -587, -587, -587, -587, -587, -587, -587, -587, + -587, -587, -587, -587, -587, -587, -587, -587, -587, -587, + + -587, -587, -587, -587, -587, -587, -587, -587, -587, -587, + -587, -587, -587, -587, -587, -587, -587, -587, -587, -587, + -587, -587, -587, -587, -587, -587, -587, -587, -587, -587, + -587, -587, -587, -587, -587, -587, -587, -587 + }, + + { + 55, -588, -588, -588, -588, -588, -588, -588, -588, -588, + -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, + -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, + -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, + -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, + -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, + + -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, + -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, + -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, + -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, + -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, + -588, -588, -588, -588, -588, -588, -588, -588, -588, -588, + -588, -588, -588, -588, -588, -588, -588, -588 + }, + + { + 55, -589, -589, -589, -589, -589, -589, -589, -589, -589, + -589, -589, -589, -589, -589, -589, -589, -589, -589, -589, + -589, -589, -589, -589, -589, -589, -589, -589, -589, -589, + + -589, -589, -589, -589, -589, -589, -589, -589, -589, -589, + -589, -589, -589, -589, -589, -589, -589, -589, -589, -589, + -589, -589, -589, -589, -589, -589, -589, -589, -589, -589, + -589, -589, -589, -589, -589, -589, -589, -589, -589, -589, + -589, -589, -589, -589, -589, -589, -589, -589, -589, -589, + -589, -589, -589, -589, -589, -589, -589, -589, -589, -589, + -589, -589, -589, -589, -589, -589, -589, -589, -589, -589, + -589, -589, -589, -589, -589, -589, -589, -589, -589, -589, + -589, -589, -589, -589, -589, -589, -589, -589, -589, -589, + -589, -589, -589, -589, -589, -589, -589, -589 + + }, + + { + 55, -590, -590, -590, -590, -590, -590, -590, -590, -590, + -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, + -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, + -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, + -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, + -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, + -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, + -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, + -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, + -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, + + -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, + -590, -590, -590, -590, -590, -590, -590, -590, -590, -590, + -590, -590, -590, -590, -590, -590, -590, -590 + }, + + { + 55, -591, -591, -591, -591, -591, -591, -591, -591, -591, + -591, -591, -591, -591, -591, -591, -591, -591, -591, -591, + -591, -591, -591, -591, -591, -591, -591, -591, -591, -591, + -591, -591, -591, -591, -591, -591, -591, -591, -591, -591, + -591, -591, -591, -591, -591, -591, -591, -591, -591, -591, + -591, -591, -591, -591, -591, -591, -591, -591, -591, -591, + -591, -591, -591, -591, -591, -591, -591, -591, -591, -591, + + -591, -591, -591, -591, -591, -591, -591, -591, -591, -591, + -591, -591, -591, -591, -591, -591, -591, -591, -591, -591, + -591, -591, -591, -591, -591, -591, -591, -591, -591, -591, + -591, -591, -591, -591, -591, -591, -591, -591, -591, -591, + -591, -591, -591, -591, -591, -591, -591, -591, -591, -591, + -591, -591, -591, -591, -591, -591, -591, -591 + }, + + { + 55, -592, -592, -592, -592, -592, -592, -592, -592, -592, + -592, -592, -592, -592, -592, -592, -592, -592, -592, -592, + -592, -592, -592, -592, -592, -592, -592, -592, -592, -592, + -592, -592, -592, -592, -592, -592, -592, -592, -592, -592, + + -592, -592, -592, -592, -592, -592, -592, -592, -592, -592, + -592, -592, -592, -592, -592, -592, -592, -592, -592, -592, + -592, -592, -592, -592, -592, -592, -592, -592, -592, -592, + -592, -592, -592, -592, -592, -592, -592, -592, -592, -592, + -592, -592, -592, -592, -592, -592, -592, -592, -592, -592, + -592, -592, -592, -592, -592, -592, -592, -592, -592, -592, + -592, -592, -592, -592, -592, -592, -592, -592, -592, -592, + -592, -592, -592, -592, -592, -592, -592, -592, -592, -592, + -592, -592, -592, -592, -592, -592, -592, -592 + }, + + { + 55, -593, -593, -593, -593, -593, -593, -593, -593, -593, + + -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, + -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, + -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, + -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, + -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, + -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, + -593, -593, -593, 758, -593, -593, -593, -593, -593, -593, + -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, + -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, + -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, + + -593, -593, -593, -593, -593, -593, -593, -593, -593, -593, + -593, -593, -593, -593, -593, -593, -593, -593 + }, + + { + 55, -594, -594, -594, -594, -594, -594, -594, -594, -594, + -594, -594, -594, -594, -594, -594, -594, -594, -594, -594, + -594, -594, -594, -594, -594, -594, -594, -594, -594, -594, + -594, -594, -594, -594, -594, -594, -594, -594, -594, -594, + -594, -594, -594, -594, -594, -594, -594, -594, -594, -594, + -594, -594, -594, -594, -594, -594, -594, -594, -594, -594, + -594, -594, -594, -594, -594, 759, 760, -594, -594, 761, + -594, -594, -594, -594, -594, -594, -594, -594, -594, 762, + + -594, -594, 763, -594, -594, -594, -594, -594, -594, -594, + -594, -594, -594, -594, -594, -594, -594, -594, -594, -594, + -594, -594, -594, -594, -594, -594, -594, -594, -594, -594, + -594, -594, -594, -594, -594, -594, -594, -594, -594, -594, + -594, -594, -594, -594, -594, -594, -594, -594 + }, + + { + 55, -595, -595, -595, -595, -595, -595, -595, -595, -595, + -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, + -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, + -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, + -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, + + -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, + -595, -595, -595, -595, -595, -595, -595, -595, -595, 764, + -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, + -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, + -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, + -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, + -595, -595, -595, -595, -595, -595, -595, -595, -595, -595, + -595, -595, -595, -595, -595, -595, -595, -595 + }, + + { + 55, -596, -596, -596, -596, -596, -596, -596, -596, -596, + -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, + + -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, + -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, + -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, + -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, + -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, + -596, -596, -596, -596, -596, -596, -596, -596, -596, 765, + -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, + -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, + -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, + -596, -596, -596, -596, -596, -596, -596, -596, -596, -596, + + -596, -596, -596, -596, -596, -596, -596, -596 + }, + + { + 55, -597, -597, -597, -597, -597, -597, -597, -597, -597, + -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, + -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, + -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, + -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, + -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, + -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, + -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, + -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, + + -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, + -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, + -597, -597, -597, -597, -597, -597, -597, -597, -597, -597, + -597, -597, -597, -597, -597, -597, -597, -597 + }, + + { + 55, -598, -598, -598, -598, -598, -598, -598, -598, -598, + -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, + -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, + -598, -598, 766, -598, -598, -598, -598, -598, -598, -598, + -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, + -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, + + -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, + -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, + -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, + -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, + -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, + -598, -598, -598, -598, -598, -598, -598, -598, -598, -598, + -598, -598, -598, -598, -598, -598, -598, -598 + }, + + { + 55, -599, -599, -599, -599, -599, -599, -599, -599, -599, + -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, + -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, + + -599, -599, 767, -599, -599, -599, -599, -599, -599, -599, + -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, + -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, + -599, -599, -599, -599, -599, 767, 767, 767, 767, 767, + 767, 767, 767, 767, 767, 767, 767, 767, 767, 767, + 767, 767, 767, 767, 767, 767, 767, 767, 767, 767, + 767, -599, -599, -599, -599, -599, -599, -599, -599, -599, + -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, + -599, -599, -599, -599, -599, -599, -599, -599, -599, -599, + -599, -599, -599, -599, -599, -599, -599, -599 + + }, + + { + 55, -600, -600, -600, -600, -600, -600, -600, -600, -600, + -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, + -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, + -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, + -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, + -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, + -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, + -600, -600, -600, -600, -600, -600, -600, -600, -600, 768, + -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, + -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, + + -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, + -600, -600, -600, -600, -600, -600, -600, -600, -600, -600, + -600, -600, -600, -600, -600, -600, -600, -600 + }, + + { + 55, -601, -601, -601, -601, -601, -601, -601, -601, -601, + -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, + -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, + -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, + -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, + -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, + -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, + + -601, -601, -601, -601, -601, -601, -601, -601, -601, 769, + -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, + -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, + -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, + -601, -601, -601, -601, -601, -601, -601, -601, -601, -601, + -601, -601, -601, -601, -601, -601, -601, -601 + }, + + { + 55, -602, -602, -602, -602, -602, -602, -602, -602, -602, + -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, + -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, + -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, + + -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, + -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, + -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, + -602, -602, -602, -602, -602, -602, -602, -602, -602, 770, + -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, + -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, + -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, + -602, -602, -602, -602, -602, -602, -602, -602, -602, -602, + -602, -602, -602, -602, -602, -602, -602, -602 + }, + + { + 55, -603, -603, -603, -603, -603, -603, -603, -603, -603, + + -603, -603, -603, -603, -603, -603, -603, -603, -603, -603, + -603, -603, -603, -603, -603, -603, -603, -603, -603, -603, + -603, -603, -603, -603, -603, -603, -603, -603, -603, -603, + -603, -603, -603, -603, -603, -603, -603, -603, -603, -603, + -603, -603, -603, -603, -603, -603, -603, -603, -603, -603, + -603, -603, -603, -603, -603, -603, -603, -603, -603, -603, + 771, -603, -603, -603, -603, -603, -603, -603, -603, -603, + -603, -603, -603, -603, -603, -603, -603, -603, -603, -603, + -603, -603, -603, -603, -603, -603, -603, -603, -603, -603, + -603, -603, -603, -603, -603, -603, -603, -603, -603, -603, + + -603, -603, -603, -603, -603, -603, -603, -603, -603, -603, + -603, -603, -603, -603, -603, -603, -603, -603 + }, + + { + 55, -604, -604, -604, -604, -604, -604, -604, -604, -604, + -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, + -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, + -604, -604, 772, -604, -604, -604, -604, -604, -604, -604, + -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, + -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, + -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, + 773, -604, -604, 774, -604, -604, -604, -604, -604, -604, + + -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, + -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, + -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, + -604, -604, -604, -604, -604, -604, -604, -604, -604, -604, + -604, -604, -604, -604, -604, -604, -604, -604 + }, + + { + 55, -605, -605, -605, -605, -605, -605, -605, -605, -605, + -605, -605, -605, -605, -605, -605, -605, -605, -605, -605, + -605, -605, -605, -605, -605, -605, -605, -605, -605, -605, + -605, -605, -605, -605, -605, -605, -605, -605, -605, -605, + -605, -605, -605, -605, -605, -605, -605, -605, -605, -605, + + -605, -605, -605, -605, -605, -605, -605, -605, -605, -605, + -605, -605, -605, -605, -605, -605, -605, -605, -605, -605, + -605, -605, 775, -605, -605, -605, -605, -605, -605, -605, + -605, -605, -605, -605, -605, -605, -605, -605, -605, -605, + -605, -605, -605, -605, -605, -605, -605, -605, -605, -605, + -605, -605, -605, -605, -605, -605, -605, -605, -605, -605, + -605, -605, -605, -605, -605, -605, -605, -605, -605, -605, + -605, -605, -605, -605, -605, -605, -605, -605 + }, + + { + 55, -606, -606, -606, -606, -606, -606, -606, -606, -606, + -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, + + -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, + -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, + -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, + -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, + -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, + -606, -606, -606, -606, -606, -606, 776, -606, -606, -606, + -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, + -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, + -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, + -606, -606, -606, -606, -606, -606, -606, -606, -606, -606, + + -606, -606, -606, -606, -606, -606, -606, -606 + }, + + { + 55, -607, -607, -607, -607, -607, -607, -607, -607, -607, + -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, + -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, + -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, + -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, + -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, + -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, + -607, -607, -607, -607, -607, -607, 777, -607, -607, -607, + -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, + + -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, + -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, + -607, -607, -607, -607, -607, -607, -607, -607, -607, -607, + -607, -607, -607, -607, -607, -607, -607, -607 + }, + + { + 55, -608, -608, -608, -608, -608, -608, -608, -608, -608, + -608, -608, -608, -608, -608, -608, -608, -608, -608, -608, + -608, -608, -608, -608, -608, -608, -608, -608, -608, -608, + -608, -608, -608, -608, -608, -608, -608, -608, -608, -608, + -608, -608, -608, -608, -608, -608, -608, -608, -608, -608, + -608, -608, -608, -608, -608, -608, -608, -608, -608, -608, + + -608, -608, -608, -608, -608, -608, -608, -608, -608, -608, + -608, -608, -608, -608, -608, -608, -608, -608, -608, -608, + -608, -608, -608, -608, -608, -608, 778, -608, -608, -608, + -608, -608, -608, -608, -608, -608, -608, -608, -608, -608, + -608, -608, -608, -608, -608, -608, -608, -608, -608, -608, + -608, -608, -608, -608, -608, -608, -608, -608, -608, -608, + -608, -608, -608, -608, -608, -608, -608, -608 + }, + + { + 55, -609, -609, -609, -609, -609, -609, -609, -609, -609, + -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, + -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, + + -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, + -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, + -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, + -609, -609, -609, -609, -609, -609, -609, -609, -609, 779, + -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, + -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, + -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, + -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, + -609, -609, -609, -609, -609, -609, -609, -609, -609, -609, + -609, -609, -609, -609, -609, -609, -609, -609 + + }, + + { + 55, -610, -610, -610, -610, -610, -610, -610, -610, -610, + -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, + -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, + -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, + -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, + -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, + -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, + -610, -610, -610, -610, -610, -610, -610, -610, 780, -610, + -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, + -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, + + -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, + -610, -610, -610, -610, -610, -610, -610, -610, -610, -610, + -610, -610, -610, -610, -610, -610, -610, -610 + }, + + { + 55, -611, -611, -611, -611, -611, -611, -611, -611, -611, + -611, -611, -611, -611, -611, -611, -611, -611, -611, -611, + -611, -611, -611, -611, -611, -611, -611, -611, -611, -611, + -611, -611, -611, -611, -611, -611, -611, -611, -611, -611, + -611, -611, -611, -611, -611, -611, -611, -611, -611, -611, + -611, -611, -611, -611, -611, -611, -611, -611, -611, -611, + -611, -611, -611, -611, -611, -611, 781, -611, -611, -611, + + -611, -611, -611, -611, -611, -611, -611, -611, -611, -611, + -611, -611, -611, -611, -611, -611, -611, -611, -611, -611, + -611, -611, -611, -611, -611, -611, -611, -611, -611, -611, + -611, -611, -611, -611, -611, -611, -611, -611, -611, -611, + -611, -611, -611, -611, -611, -611, -611, -611, -611, -611, + -611, -611, -611, -611, -611, -611, -611, -611 + }, + + { + 55, -612, -612, -612, -612, -612, -612, -612, -612, -612, + -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, + -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, + -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, + + -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, + -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, + -612, -612, -612, -612, -612, -612, -612, -612, -612, 782, + -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, + -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, + -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, + -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, + -612, -612, -612, -612, -612, -612, -612, -612, -612, -612, + -612, -612, -612, -612, -612, -612, -612, -612 + }, + + { + 55, -613, -613, -613, -613, -613, -613, -613, -613, -613, + + -613, -613, -613, -613, -613, -613, -613, -613, -613, -613, + -613, -613, -613, -613, -613, -613, -613, -613, -613, -613, + -613, -613, -613, -613, -613, -613, -613, -613, -613, -613, + -613, -613, -613, -613, -613, -613, -613, -613, -613, -613, + -613, -613, -613, -613, -613, -613, -613, -613, -613, -613, + -613, -613, -613, -613, -613, -613, -613, -613, -613, -613, + 783, -613, -613, -613, -613, -613, -613, -613, -613, -613, + -613, -613, -613, -613, -613, -613, -613, -613, -613, -613, + -613, -613, -613, -613, -613, -613, -613, -613, -613, -613, + -613, -613, -613, -613, -613, -613, -613, -613, -613, -613, + + -613, -613, -613, -613, -613, -613, -613, -613, -613, -613, + -613, -613, -613, -613, -613, -613, -613, -613 + }, + + { + 55, -614, -614, -614, -614, -614, -614, -614, -614, -614, + -614, -614, -614, -614, -614, -614, -614, -614, -614, -614, + -614, -614, -614, -614, -614, -614, -614, -614, -614, -614, + -614, -614, 784, -614, -614, -614, -614, -614, -614, -614, + -614, -614, -614, -614, -614, -614, -614, -614, -614, -614, + -614, -614, -614, -614, -614, -614, -614, -614, -614, -614, + -614, -614, -614, -614, -614, -614, -614, -614, -614, -614, + -614, -614, -614, -614, -614, -614, -614, -614, -614, -614, + + -614, -614, -614, -614, -614, -614, -614, -614, -614, -614, + -614, -614, -614, -614, -614, -614, -614, -614, -614, -614, + -614, -614, -614, -614, -614, -614, -614, -614, -614, -614, + -614, -614, -614, -614, -614, -614, -614, -614, -614, -614, + -614, -614, -614, -614, -614, -614, -614, -614 + }, + + { + 55, -615, -615, -615, -615, -615, -615, -615, -615, -615, + -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, + -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, + -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, + -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, + + -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, + -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, + -615, -615, -615, -615, -615, -615, -615, -615, -615, 785, + -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, + -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, + -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, + -615, -615, -615, -615, -615, -615, -615, -615, -615, -615, + -615, -615, -615, -615, -615, -615, -615, -615 + }, + + { + 55, -616, -616, -616, -616, -616, -616, -616, -616, -616, + -616, -616, -616, -616, -616, -616, -616, -616, -616, -616, + + -616, -616, -616, -616, -616, -616, -616, -616, -616, -616, + -616, -616, -616, -616, -616, -616, -616, -616, -616, -616, + -616, -616, -616, -616, -616, -616, -616, -616, -616, -616, + -616, -616, -616, -616, -616, -616, -616, -616, -616, -616, + -616, -616, -616, -616, -616, -616, 786, -616, -616, -616, + -616, -616, -616, -616, -616, -616, -616, -616, -616, -616, + -616, -616, -616, -616, -616, -616, -616, -616, -616, -616, + -616, -616, -616, -616, -616, -616, -616, -616, -616, -616, + -616, -616, -616, -616, -616, -616, -616, -616, -616, -616, + -616, -616, -616, -616, -616, -616, -616, -616, -616, -616, + + -616, -616, -616, -616, -616, -616, -616, -616 + }, + + { + 55, -617, -617, -617, -617, -617, -617, -617, -617, -617, + -617, -617, -617, -617, -617, -617, -617, -617, -617, -617, + -617, -617, -617, -617, -617, -617, -617, -617, -617, -617, + -617, -617, -617, -617, -617, -617, -617, -617, -617, -617, + -617, -617, -617, -617, -617, -617, -617, -617, -617, -617, + -617, -617, -617, -617, -617, -617, -617, -617, -617, -617, + -617, -617, -617, -617, -617, -617, -617, -617, -617, -617, + -617, -617, -617, 787, -617, -617, -617, -617, -617, -617, + -617, -617, -617, -617, -617, -617, -617, -617, -617, -617, + + -617, -617, -617, -617, -617, -617, -617, -617, -617, -617, + -617, -617, -617, -617, -617, -617, -617, -617, -617, -617, + -617, -617, -617, -617, -617, -617, -617, -617, -617, -617, + -617, -617, -617, -617, -617, -617, -617, -617 + }, + + { + 55, -618, -618, -618, -618, -618, -618, -618, -618, -618, + -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, + -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, + -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, + -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, + -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, + + -618, -618, -618, -618, -618, -618, -618, -618, -618, 788, + -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, + -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, + -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, + -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, + -618, -618, -618, -618, -618, -618, -618, -618, -618, -618, + -618, -618, -618, -618, -618, -618, -618, -618 + }, + + { + 55, -619, -619, -619, -619, -619, -619, -619, -619, -619, + -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, + -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, + + -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, + -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, + -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, + -619, -619, -619, -619, -619, -619, -619, 789, -619, -619, + -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, + -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, + -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, + -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, + -619, -619, -619, -619, -619, -619, -619, -619, -619, -619, + -619, -619, -619, -619, -619, -619, -619, -619 + + }, + + { + 55, -620, -620, -620, -620, -620, -620, -620, -620, -620, + -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, + -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, + -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, + -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, + -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, + -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, + -620, -620, 790, -620, -620, -620, -620, 791, -620, -620, + -620, -620, -620, 792, -620, -620, -620, -620, -620, -620, + -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, + + -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, + -620, -620, -620, -620, -620, -620, -620, -620, -620, -620, + -620, -620, -620, -620, -620, -620, -620, -620 + }, + + { + 55, -621, -621, -621, -621, -621, -621, -621, -621, -621, + -621, -621, -621, -621, -621, -621, -621, -621, -621, -621, + -621, -621, -621, -621, -621, -621, -621, -621, -621, -621, + -621, -621, 793, -621, -621, -621, -621, -621, -621, -621, + -621, -621, -621, -621, -621, -621, -621, -621, -621, -621, + -621, -621, -621, -621, -621, -621, -621, -621, -621, -621, + -621, -621, -621, -621, -621, -621, -621, -621, -621, -621, + + -621, -621, -621, -621, -621, -621, -621, -621, -621, -621, + -621, -621, -621, -621, -621, -621, -621, -621, -621, -621, + -621, -621, -621, -621, -621, -621, -621, -621, -621, -621, + -621, -621, -621, -621, -621, -621, -621, -621, -621, -621, + -621, -621, -621, -621, -621, -621, -621, -621, -621, -621, + -621, -621, -621, -621, -621, -621, -621, -621 + }, + + { + 55, -622, -622, -622, -622, -622, -622, -622, -622, -622, + -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, + -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, + -622, -622, 794, -622, -622, -622, -622, -622, -622, -622, + + -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, + -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, + -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, + -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, + -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, + -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, + -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, + -622, -622, -622, -622, -622, -622, -622, -622, -622, -622, + -622, -622, -622, -622, -622, -622, -622, -622 + }, + + { + 55, -623, -623, -623, -623, -623, -623, -623, -623, -623, + + -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, + -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, + -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, + -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, + -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, + -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, + -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, + -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, + -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, + -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, + + -623, -623, -623, -623, -623, -623, -623, -623, -623, -623, + -623, -623, -623, -623, -623, -623, -623, -623 + }, + + { + 55, -624, -624, -624, -624, -624, -624, -624, -624, -624, + -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, + -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, + -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, + -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, + -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, + -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, + -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, + + -624, -624, -624, 795, -624, -624, -624, -624, -624, -624, + -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, + -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, + -624, -624, -624, -624, -624, -624, -624, -624, -624, -624, + -624, -624, -624, -624, -624, -624, -624, -624 + }, + + { + 55, -625, -625, -625, -625, -625, -625, -625, -625, -625, + -625, -625, -625, -625, -625, -625, -625, -625, -625, -625, + -625, -625, -625, -625, -625, -625, -625, -625, -625, -625, + -625, -625, -625, -625, -625, -625, -625, -625, -625, -625, + -625, -625, -625, -625, -625, -625, -625, -625, -625, -625, + + -625, -625, -625, -625, -625, -625, -625, -625, -625, -625, + -625, -625, -625, -625, -625, -625, -625, -625, -625, -625, + -625, -625, -625, -625, -625, -625, -625, -625, -625, -625, + -625, -625, -625, -625, -625, -625, -625, -625, -625, 796, + -625, -625, -625, -625, -625, -625, -625, -625, -625, -625, + -625, -625, -625, -625, -625, -625, -625, -625, -625, -625, + -625, -625, -625, -625, -625, -625, -625, -625, -625, -625, + -625, -625, -625, -625, -625, -625, -625, -625 + }, + + { + 55, -626, -626, -626, -626, -626, -626, -626, -626, -626, + -626, -626, -626, -626, -626, -626, -626, -626, -626, -626, + + -626, -626, -626, -626, -626, -626, -626, -626, -626, -626, + -626, -626, -626, -626, -626, -626, -626, -626, -626, -626, + -626, -626, -626, -626, -626, -626, -626, -626, -626, -626, + -626, -626, -626, -626, -626, -626, -626, -626, -626, -626, + -626, -626, -626, -626, -626, -626, -626, -626, -626, -626, + -626, -626, -626, -626, -626, -626, -626, -626, -626, -626, + -626, -626, 797, -626, -626, -626, -626, -626, -626, -626, + -626, -626, -626, -626, -626, -626, -626, -626, -626, -626, + -626, -626, -626, -626, -626, -626, -626, -626, -626, -626, + -626, -626, -626, -626, -626, -626, -626, -626, -626, -626, + + -626, -626, -626, -626, -626, -626, -626, -626 + }, + + { + 55, -627, -627, -627, -627, -627, -627, -627, -627, -627, + -627, -627, -627, -627, -627, -627, -627, -627, -627, -627, + -627, -627, -627, -627, -627, -627, -627, -627, -627, -627, + -627, -627, -627, -627, -627, -627, -627, -627, -627, -627, + -627, -627, -627, -627, -627, -627, -627, -627, -627, -627, + -627, -627, -627, -627, -627, -627, -627, -627, -627, -627, + -627, -627, -627, -627, -627, 798, -627, -627, -627, -627, + -627, -627, -627, -627, -627, -627, -627, -627, -627, -627, + -627, -627, -627, -627, -627, -627, -627, -627, -627, -627, + + -627, -627, -627, -627, -627, -627, -627, -627, -627, -627, + -627, -627, -627, -627, -627, -627, -627, -627, -627, -627, + -627, -627, -627, -627, -627, -627, -627, -627, -627, -627, + -627, -627, -627, -627, -627, -627, -627, -627 + }, + + { + 55, -628, -628, -628, -628, -628, -628, -628, -628, -628, + -628, -628, -628, -628, -628, -628, -628, -628, -628, -628, + -628, -628, -628, -628, -628, -628, -628, -628, -628, -628, + -628, -628, -628, -628, -628, -628, -628, -628, -628, -628, + -628, -628, -628, -628, -628, -628, -628, -628, -628, -628, + -628, -628, -628, -628, -628, -628, -628, -628, -628, -628, + + -628, -628, -628, -628, -628, -628, -628, -628, -628, -628, + -628, -628, -628, -628, -628, -628, -628, -628, -628, -628, + -628, -628, 799, -628, -628, -628, -628, -628, -628, -628, + -628, -628, -628, -628, -628, -628, -628, -628, -628, -628, + -628, -628, -628, -628, -628, -628, -628, -628, -628, -628, + -628, -628, -628, -628, -628, -628, -628, -628, -628, -628, + -628, -628, -628, -628, -628, -628, -628, -628 + }, + + { + 55, -629, -629, -629, -629, -629, -629, -629, -629, -629, + -629, -629, -629, -629, -629, -629, -629, -629, -629, -629, + -629, -629, -629, -629, -629, -629, -629, -629, -629, -629, + + -629, -629, -629, -629, -629, -629, -629, -629, -629, -629, + -629, -629, -629, -629, -629, -629, -629, -629, -629, -629, + -629, -629, -629, -629, -629, -629, -629, -629, -629, -629, + -629, -629, -629, -629, -629, -629, -629, -629, -629, -629, + -629, -629, -629, -629, -629, -629, -629, -629, -629, -629, + -629, -629, -629, -629, -629, -629, -629, -629, -629, 800, + -629, -629, -629, -629, -629, -629, -629, -629, -629, -629, + -629, -629, -629, -629, -629, -629, -629, -629, -629, -629, + -629, -629, -629, -629, -629, -629, -629, -629, -629, -629, + -629, -629, -629, -629, -629, -629, -629, -629 + + }, + + { + 55, -630, -630, -630, -630, -630, -630, -630, -630, -630, + -630, -630, -630, -630, -630, -630, -630, -630, -630, -630, + -630, -630, -630, -630, -630, -630, -630, -630, -630, -630, + -630, -630, -630, -630, -630, -630, -630, -630, -630, -630, + -630, -630, -630, -630, -630, -630, -630, -630, -630, -630, + -630, -630, -630, -630, -630, -630, -630, -630, -630, -630, + -630, -630, -630, -630, -630, -630, 801, -630, -630, -630, + -630, -630, -630, -630, -630, -630, -630, -630, -630, -630, + -630, -630, -630, -630, -630, -630, -630, -630, -630, -630, + -630, -630, -630, -630, -630, -630, -630, -630, -630, -630, + + -630, -630, -630, -630, -630, -630, -630, -630, -630, -630, + -630, -630, -630, -630, -630, -630, -630, -630, -630, -630, + -630, -630, -630, -630, -630, -630, -630, -630 + }, + + { + 55, -631, -631, -631, -631, -631, -631, -631, -631, -631, + -631, -631, -631, -631, -631, -631, -631, -631, -631, -631, + -631, -631, -631, -631, -631, -631, -631, -631, -631, -631, + -631, -631, -631, -631, -631, -631, -631, -631, -631, -631, + -631, -631, -631, -631, -631, -631, -631, -631, -631, -631, + -631, -631, -631, -631, -631, -631, -631, -631, -631, -631, + -631, -631, -631, -631, -631, -631, -631, -631, -631, -631, + + -631, -631, -631, -631, -631, -631, -631, -631, -631, -631, + -631, -631, 802, -631, -631, -631, -631, -631, -631, -631, + -631, -631, -631, -631, -631, -631, -631, -631, -631, -631, + -631, -631, -631, -631, -631, -631, -631, -631, -631, -631, + -631, -631, -631, -631, -631, -631, -631, -631, -631, -631, + -631, -631, -631, -631, -631, -631, -631, -631 + }, + + { + 55, -632, -632, -632, -632, -632, -632, -632, -632, -632, + -632, -632, -632, -632, -632, -632, -632, -632, -632, -632, + -632, -632, -632, -632, -632, -632, -632, -632, -632, -632, + -632, -632, -632, -632, -632, -632, -632, -632, -632, -632, + + -632, -632, -632, -632, -632, -632, -632, -632, -632, -632, + -632, -632, -632, -632, -632, -632, -632, -632, -632, -632, + -632, -632, -632, -632, -632, -632, -632, -632, -632, -632, + -632, -632, -632, -632, -632, -632, -632, -632, -632, -632, + -632, -632, -632, 803, -632, -632, -632, -632, -632, -632, + -632, -632, -632, -632, -632, -632, -632, -632, -632, -632, + -632, -632, -632, -632, -632, -632, -632, -632, -632, -632, + -632, -632, -632, -632, -632, -632, -632, -632, -632, -632, + -632, -632, -632, -632, -632, -632, -632, -632 + }, + + { + 55, -633, -633, -633, -633, -633, -633, -633, -633, -633, + + -633, -633, -633, -633, -633, -633, -633, -633, -633, -633, + -633, -633, -633, -633, -633, -633, -633, -633, -633, -633, + -633, -633, -633, -633, -633, -633, -633, -633, -633, -633, + -633, -633, -633, -633, -633, -633, -633, -633, -633, -633, + -633, -633, -633, -633, -633, -633, -633, -633, -633, -633, + -633, -633, -633, -633, -633, -633, -633, -633, -633, 804, + -633, -633, -633, -633, -633, -633, -633, -633, -633, -633, + -633, -633, -633, -633, -633, -633, -633, -633, -633, -633, + -633, -633, -633, -633, -633, -633, -633, -633, -633, -633, + -633, -633, -633, -633, -633, -633, -633, -633, -633, -633, + + -633, -633, -633, -633, -633, -633, -633, -633, -633, -633, + -633, -633, -633, -633, -633, -633, -633, -633 + }, + + { + 55, -634, -634, -634, -634, -634, -634, -634, -634, -634, + -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, + -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, + -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, + -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, + -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, + -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, + 805, -634, -634, -634, -634, -634, -634, -634, -634, -634, + + -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, + -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, + -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, + -634, -634, -634, -634, -634, -634, -634, -634, -634, -634, + -634, -634, -634, -634, -634, -634, -634, -634 + }, + + { + 55, -635, -635, -635, -635, -635, -635, -635, -635, -635, + -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, + -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, + -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, + -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, + + -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, + -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, + -635, -635, -635, 806, -635, -635, -635, -635, -635, -635, + -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, + -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, + -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, + -635, -635, -635, -635, -635, -635, -635, -635, -635, -635, + -635, -635, -635, -635, -635, -635, -635, -635 + }, + + { + 55, -636, -636, -636, -636, -636, -636, -636, -636, -636, + -636, -636, -636, -636, -636, -636, -636, -636, -636, -636, + + -636, -636, -636, -636, -636, -636, -636, -636, -636, -636, + -636, -636, -636, -636, -636, -636, -636, -636, -636, -636, + -636, -636, -636, -636, -636, -636, -636, -636, -636, -636, + -636, -636, -636, -636, -636, -636, -636, -636, -636, -636, + -636, -636, -636, -636, -636, -636, -636, -636, -636, -636, + -636, -636, -636, -636, -636, -636, -636, -636, -636, -636, + -636, -636, -636, -636, -636, -636, -636, -636, -636, 807, + -636, -636, -636, -636, -636, -636, -636, -636, -636, -636, + -636, -636, -636, -636, -636, -636, -636, -636, -636, -636, + -636, -636, -636, -636, -636, -636, -636, -636, -636, -636, + + -636, -636, -636, -636, -636, -636, -636, -636 + }, + + { + 55, -637, -637, -637, -637, -637, -637, -637, -637, -637, + -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, + -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, + -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, + -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, + -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, + -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, + -637, -637, -637, -637, -637, -637, -637, -637, 808, -637, + -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, + + -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, + -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, + -637, -637, -637, -637, -637, -637, -637, -637, -637, -637, + -637, -637, -637, -637, -637, -637, -637, -637 + }, + + { + 55, -638, -638, -638, -638, -638, -638, -638, -638, -638, + -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, + -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, + -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, + -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, + -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, + + -638, -638, -638, -638, -638, -638, -638, -638, -638, 809, + -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, + -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, + -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, + -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, + -638, -638, -638, -638, -638, -638, -638, -638, -638, -638, + -638, -638, -638, -638, -638, -638, -638, -638 + }, + + { + 55, -639, -639, -639, -639, -639, -639, -639, -639, -639, + -639, -639, -639, -639, -639, -639, -639, -639, -639, -639, + -639, -639, -639, -639, -639, -639, -639, -639, -639, -639, + + -639, -639, -639, -639, -639, -639, -639, -639, -639, -639, + -639, -639, -639, -639, -639, -639, -639, -639, -639, -639, + -639, -639, -639, -639, -639, -639, -639, -639, -639, -639, + -639, -639, -639, -639, -639, -639, -639, -639, -639, 810, + -639, -639, -639, -639, -639, -639, -639, -639, -639, -639, + -639, -639, -639, -639, -639, -639, -639, -639, -639, -639, + -639, -639, -639, -639, -639, -639, -639, -639, -639, -639, + -639, -639, -639, -639, -639, -639, -639, -639, -639, -639, + -639, -639, -639, -639, -639, -639, -639, -639, -639, -639, + -639, -639, -639, -639, -639, -639, -639, -639 + + }, + + { + 55, -640, -640, -640, -640, -640, -640, -640, -640, -640, + -640, -640, -640, -640, -640, -640, -640, -640, -640, -640, + -640, -640, -640, -640, -640, -640, -640, -640, -640, -640, + -640, -640, -640, -640, -640, -640, -640, -640, -640, -640, + -640, -640, -640, -640, -640, -640, -640, -640, -640, -640, + -640, -640, -640, -640, -640, -640, -640, -640, -640, -640, + -640, -640, -640, -640, -640, -640, -640, -640, -640, -640, + -640, -640, -640, 811, -640, -640, -640, -640, -640, -640, + -640, -640, -640, -640, -640, -640, -640, -640, -640, -640, + -640, -640, -640, -640, -640, -640, -640, -640, -640, -640, + + -640, -640, -640, -640, -640, -640, -640, -640, -640, -640, + -640, -640, -640, -640, -640, -640, -640, -640, -640, -640, + -640, -640, -640, -640, -640, -640, -640, -640 + }, + + { + 55, -641, -641, -641, -641, -641, -641, -641, -641, -641, + -641, -641, -641, -641, -641, -641, -641, -641, -641, -641, + -641, -641, -641, -641, -641, -641, -641, -641, -641, -641, + -641, -641, -641, -641, -641, -641, -641, -641, -641, -641, + -641, -641, -641, -641, -641, -641, -641, -641, -641, -641, + -641, -641, -641, -641, -641, -641, -641, -641, -641, -641, + -641, -641, -641, -641, -641, -641, -641, -641, -641, -641, + + -641, -641, -641, -641, -641, -641, -641, -641, -641, 812, + -641, -641, -641, -641, -641, -641, -641, -641, -641, -641, + -641, -641, -641, -641, -641, -641, -641, -641, -641, -641, + -641, -641, -641, -641, -641, -641, -641, -641, -641, -641, + -641, -641, -641, -641, -641, -641, -641, -641, -641, -641, + -641, -641, -641, -641, -641, -641, -641, -641 + }, + + { + 55, -642, -642, -642, -642, -642, -642, -642, -642, -642, + -642, -642, -642, -642, -642, -642, -642, -642, -642, -642, + -642, -642, -642, -642, -642, -642, -642, -642, -642, -642, + -642, -642, -642, -642, -642, -642, -642, -642, -642, -642, + + -642, -642, -642, -642, -642, -642, -642, -642, -642, -642, + -642, -642, -642, -642, -642, -642, -642, -642, -642, -642, + -642, -642, -642, -642, -642, -642, -642, -642, -642, -642, + -642, -642, -642, -642, -642, -642, -642, -642, -642, -642, + -642, -642, -642, -642, 813, -642, -642, -642, -642, -642, + -642, -642, -642, -642, -642, -642, -642, -642, -642, -642, + -642, -642, -642, -642, -642, -642, -642, -642, -642, -642, + -642, -642, -642, -642, -642, -642, -642, -642, -642, -642, + -642, -642, -642, -642, -642, -642, -642, -642 + }, + + { + 55, -643, -643, -643, -643, -643, -643, -643, -643, -643, + + -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, + -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, + -643, -643, 814, -643, -643, -643, -643, -643, -643, -643, + -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, + -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, + -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, + -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, + -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, + -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, + -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, + + -643, -643, -643, -643, -643, -643, -643, -643, -643, -643, + -643, -643, -643, -643, -643, -643, -643, -643 + }, + + { + 55, -644, -644, -644, -644, -644, -644, -644, -644, -644, + -644, -644, -644, -644, -644, -644, -644, -644, -644, -644, + -644, -644, -644, -644, -644, -644, -644, -644, -644, -644, + -644, -644, -644, -644, -644, -644, -644, -644, -644, -644, + -644, -644, -644, -644, -644, -644, -644, -644, -644, -644, + -644, -644, -644, -644, -644, -644, -644, -644, -644, -644, + -644, -644, -644, -644, -644, -644, -644, -644, -644, -644, + -644, 815, -644, -644, -644, -644, -644, -644, -644, -644, + + -644, -644, -644, -644, -644, -644, -644, -644, -644, -644, + -644, -644, -644, -644, -644, -644, -644, -644, -644, -644, + -644, -644, -644, -644, -644, -644, -644, -644, -644, -644, + -644, -644, -644, -644, -644, -644, -644, -644, -644, -644, + -644, -644, -644, -644, -644, -644, -644, -644 + }, + + { + 55, -645, -645, -645, -645, -645, -645, -645, -645, -645, + -645, -645, -645, -645, -645, -645, -645, -645, -645, -645, + -645, -645, -645, -645, -645, -645, -645, -645, -645, -645, + -645, -645, -645, -645, -645, -645, -645, -645, -645, -645, + -645, -645, -645, -645, -645, -645, -645, -645, -645, -645, + + -645, -645, -645, -645, -645, -645, -645, -645, -645, -645, + -645, -645, -645, -645, -645, -645, -645, -645, -645, -645, + -645, -645, -645, -645, -645, -645, -645, -645, -645, -645, + -645, -645, -645, -645, -645, -645, -645, -645, -645, 816, + -645, -645, -645, -645, -645, -645, -645, -645, -645, -645, + -645, -645, -645, -645, -645, -645, -645, -645, -645, -645, + -645, -645, -645, -645, -645, -645, -645, -645, -645, -645, + -645, -645, -645, -645, -645, -645, -645, -645 + }, + + { + 55, -646, -646, -646, -646, -646, -646, -646, -646, -646, + -646, -646, -646, -646, -646, -646, -646, -646, -646, -646, + + -646, -646, -646, -646, -646, -646, -646, -646, -646, -646, + -646, -646, -646, -646, -646, -646, -646, -646, -646, -646, + -646, -646, -646, -646, -646, -646, -646, -646, -646, -646, + -646, -646, -646, -646, -646, -646, -646, -646, -646, -646, + -646, -646, -646, -646, -646, -646, -646, -646, -646, -646, + 817, -646, -646, -646, -646, -646, -646, -646, -646, -646, + -646, -646, -646, -646, -646, -646, -646, -646, -646, -646, + -646, -646, -646, -646, -646, -646, -646, -646, -646, -646, + -646, -646, -646, -646, -646, -646, -646, -646, -646, -646, + -646, -646, -646, -646, -646, -646, -646, -646, -646, -646, + + -646, -646, -646, -646, -646, -646, -646, -646 + }, + + { + 55, -647, -647, -647, -647, -647, -647, -647, -647, -647, + -647, -647, -647, -647, -647, -647, -647, -647, -647, -647, + -647, -647, -647, -647, -647, -647, -647, -647, -647, -647, + -647, -647, -647, -647, -647, -647, -647, -647, -647, -647, + -647, -647, -647, -647, -647, -647, -647, -647, -647, -647, + -647, -647, -647, -647, -647, -647, -647, -647, -647, -647, + -647, -647, -647, -647, -647, -647, -647, 818, -647, -647, + -647, -647, -647, -647, -647, -647, -647, -647, -647, -647, + -647, -647, -647, -647, -647, -647, -647, -647, -647, -647, + + -647, -647, -647, -647, -647, -647, -647, -647, -647, -647, + -647, -647, -647, -647, -647, -647, -647, -647, -647, -647, + -647, -647, -647, -647, -647, -647, -647, -647, -647, -647, + -647, -647, -647, -647, -647, -647, -647, -647 + }, + + { + 55, -648, -648, -648, -648, -648, -648, -648, -648, -648, + -648, -648, -648, -648, -648, -648, -648, -648, -648, -648, + -648, -648, -648, -648, -648, -648, -648, -648, -648, -648, + -648, -648, -648, -648, -648, -648, -648, -648, -648, -648, + -648, -648, -648, -648, -648, -648, -648, -648, 819, 819, + 819, 819, 819, 819, 819, 819, 819, 819, -648, -648, + + -648, -648, -648, -648, -648, -648, -648, -648, -648, -648, + -648, -648, -648, -648, -648, -648, -648, -648, -648, -648, + -648, -648, -648, -648, -648, -648, -648, -648, -648, -648, + -648, -648, -648, -648, -648, -648, -648, -648, -648, -648, + -648, -648, -648, -648, -648, -648, -648, -648, -648, -648, + -648, -648, -648, -648, -648, -648, -648, -648, -648, -648, + -648, -648, -648, -648, -648, -648, -648, -648 + }, + + { + 55, -649, -649, -649, -649, -649, -649, -649, -649, -649, + -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, + -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, + + -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, + -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, + -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, + -649, -649, -649, -649, -649, -649, -649, -649, -649, 820, + -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, + -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, + -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, + -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, + -649, -649, -649, -649, -649, -649, -649, -649, -649, -649, + -649, -649, -649, -649, -649, -649, -649, -649 + + }, + + { + 55, -650, -650, -650, -650, -650, -650, -650, -650, -650, + -650, -650, -650, -650, -650, -650, -650, -650, -650, -650, + -650, -650, -650, -650, -650, -650, -650, -650, -650, -650, + -650, -650, -650, -650, -650, -650, -650, -650, -650, -650, + -650, -650, -650, -650, -650, -650, -650, -650, -650, -650, + -650, -650, -650, -650, -650, -650, -650, -650, -650, -650, + -650, -650, -650, -650, -650, -650, -650, -650, -650, -650, + -650, -650, -650, -650, -650, -650, -650, 821, -650, -650, + -650, -650, -650, -650, -650, -650, -650, -650, -650, -650, + -650, -650, -650, -650, -650, -650, -650, -650, -650, -650, + + -650, -650, -650, -650, -650, -650, -650, -650, -650, -650, + -650, -650, -650, -650, -650, -650, -650, -650, -650, -650, + -650, -650, -650, -650, -650, -650, -650, -650 + }, + + { + 55, -651, -651, -651, -651, -651, -651, -651, -651, -651, + -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, + -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, + -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, + -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, + -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, + -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, + + -651, -651, -651, -651, -651, -651, 822, -651, -651, -651, + -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, + -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, + -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, + -651, -651, -651, -651, -651, -651, -651, -651, -651, -651, + -651, -651, -651, -651, -651, -651, -651, -651 + }, + + { + 55, -652, -652, -652, -652, -652, -652, -652, -652, -652, + -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, + -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, + -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, + + -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, + -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, + -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, + -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, + -652, -652, 823, -652, -652, -652, -652, -652, -652, -652, + -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, + -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, + -652, -652, -652, -652, -652, -652, -652, -652, -652, -652, + -652, -652, -652, -652, -652, -652, -652, -652 + }, + + { + 55, -653, -653, -653, -653, -653, -653, -653, -653, -653, + + -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, + -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, + -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, + -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, + -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, + -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, + -653, -653, -653, -653, -653, -653, 824, -653, -653, -653, + -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, + -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, + -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, + + -653, -653, -653, -653, -653, -653, -653, -653, -653, -653, + -653, -653, -653, -653, -653, -653, -653, -653 + }, + + { + 55, -654, -654, -654, -654, -654, -654, -654, -654, -654, + -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, + -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, + -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, + -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, + -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, + -654, -654, -654, -654, -654, -654, -654, 825, -654, -654, + -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, + + -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, + -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, + -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, + -654, -654, -654, -654, -654, -654, -654, -654, -654, -654, + -654, -654, -654, -654, -654, -654, -654, -654 + }, + + { + 55, -655, -655, -655, -655, -655, -655, -655, -655, -655, + -655, -655, -655, -655, -655, -655, -655, -655, -655, -655, + -655, -655, -655, -655, -655, -655, -655, -655, -655, -655, + -655, -655, 826, -655, -655, -655, -655, -655, -655, -655, + -655, -655, -655, -655, -655, -655, -655, -655, -655, -655, + + -655, -655, -655, -655, -655, -655, -655, -655, -655, -655, + -655, -655, -655, -655, -655, -655, -655, -655, -655, -655, + -655, -655, -655, -655, -655, -655, -655, -655, -655, -655, + -655, -655, -655, -655, -655, -655, -655, -655, -655, -655, + -655, -655, -655, -655, -655, -655, -655, -655, -655, -655, + -655, -655, -655, -655, -655, -655, -655, -655, -655, -655, + -655, -655, -655, -655, -655, -655, -655, -655, -655, -655, + -655, -655, -655, -655, -655, -655, -655, -655 + }, + + { + 55, -656, -656, -656, -656, -656, -656, -656, -656, -656, + -656, -656, -656, -656, -656, -656, -656, -656, -656, -656, + + -656, -656, -656, -656, -656, -656, -656, -656, -656, -656, + -656, -656, 827, -656, -656, -656, -656, -656, -656, -656, + -656, -656, -656, -656, -656, -656, -656, -656, -656, -656, + -656, -656, -656, -656, -656, -656, -656, -656, -656, -656, + -656, -656, -656, -656, -656, -656, -656, -656, -656, -656, + -656, -656, -656, -656, -656, -656, -656, -656, -656, -656, + -656, -656, -656, -656, -656, -656, -656, -656, -656, -656, + -656, -656, -656, -656, -656, -656, -656, -656, -656, -656, + -656, -656, -656, -656, -656, -656, -656, -656, -656, -656, + -656, -656, -656, -656, -656, -656, -656, -656, -656, -656, + + -656, -656, -656, -656, -656, -656, -656, -656 + }, + + { + 55, -657, -657, -657, -657, -657, -657, -657, -657, -657, + -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, + -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, + -657, -657, 828, -657, -657, -657, -657, -657, -657, -657, + -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, + -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, + -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, + -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, + -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, + + -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, + -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, + -657, -657, -657, -657, -657, -657, -657, -657, -657, -657, + -657, -657, -657, -657, -657, -657, -657, -657 + }, + + { + 55, -658, -658, -658, -658, -658, -658, -658, -658, -658, + -658, -658, -658, -658, -658, -658, -658, -658, -658, -658, + -658, -658, -658, -658, -658, -658, -658, -658, -658, -658, + -658, -658, 829, -658, -658, -658, -658, -658, -658, -658, + -658, -658, -658, -658, -658, -658, -658, -658, -658, -658, + -658, -658, -658, -658, -658, -658, -658, -658, -658, -658, + + -658, -658, -658, -658, -658, -658, -658, -658, -658, -658, + -658, -658, -658, -658, -658, -658, -658, -658, -658, -658, + -658, -658, -658, -658, -658, -658, -658, -658, -658, -658, + -658, -658, -658, -658, -658, -658, -658, -658, -658, -658, + -658, -658, -658, -658, -658, -658, -658, -658, -658, -658, + -658, -658, -658, -658, -658, -658, -658, -658, -658, -658, + -658, -658, -658, -658, -658, -658, -658, -658 + }, + + { + 55, -659, -659, -659, -659, -659, -659, -659, -659, -659, + -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, + -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, + + -659, -659, 830, -659, -659, -659, -659, -659, -659, -659, + -659, -659, -659, -659, -659, -659, -659, -659, 831, 832, + 832, 832, 832, 832, 832, 832, 832, 832, -659, -659, + -659, -659, -659, -659, -659, 830, 830, 830, 830, 830, + 830, 830, 830, 830, 830, 830, 830, 830, 830, 830, + 830, 830, 830, 830, 830, 830, 830, 830, 830, 830, + 830, -659, -659, -659, -659, -659, -659, -659, -659, -659, + -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, + -659, -659, -659, -659, -659, -659, -659, -659, -659, -659, + -659, -659, -659, -659, -659, -659, -659, -659 + + }, + + { + 55, -660, -660, -660, -660, -660, -660, -660, -660, -660, + -660, -660, -660, -660, -660, -660, -660, -660, -660, -660, + -660, -660, -660, -660, -660, -660, -660, -660, -660, -660, + -660, -660, 833, -660, -660, -660, -660, -660, -660, -660, + -660, -660, -660, -660, -660, -660, -660, -660, 831, 832, + 832, 832, 832, 832, 832, 832, 832, 832, -660, -660, + -660, -660, -660, -660, -660, 833, 833, 833, 833, 833, + 833, 833, 833, 833, 833, 833, 833, 833, 833, 833, + 833, 833, 833, 833, 833, 833, 833, 833, 833, 833, + 833, -660, -660, -660, -660, -660, -660, -660, -660, -660, + + -660, -660, -660, -660, -660, -660, -660, -660, -660, -660, + -660, -660, -660, -660, -660, -660, -660, -660, -660, -660, + -660, -660, -660, -660, -660, -660, -660, -660 + }, + + { + 55, -661, -661, -661, -661, -661, -661, -661, -661, -661, + -661, -661, -661, -661, -661, -661, -661, -661, -661, -661, + -661, -661, -661, -661, -661, -661, -661, -661, -661, -661, + -661, -661, 834, -661, -661, -661, -661, -661, -661, -661, + -661, -661, -661, -661, -661, -661, -661, -661, -661, -661, + -661, -661, -661, -661, -661, -661, -661, -661, -661, -661, + -661, -661, -661, -661, -661, -661, -661, -661, -661, -661, + + -661, -661, -661, -661, -661, -661, -661, -661, -661, -661, + -661, -661, -661, -661, -661, -661, -661, -661, -661, -661, + -661, -661, -661, -661, -661, -661, -661, -661, -661, -661, + -661, -661, -661, -661, -661, -661, -661, -661, -661, -661, + -661, -661, -661, -661, -661, -661, -661, -661, -661, -661, + -661, -661, -661, -661, -661, -661, -661, -661 + }, + + { + 55, -662, -662, -662, -662, -662, -662, -662, -662, -662, + -662, -662, -662, -662, -662, -662, -662, -662, -662, -662, + -662, -662, -662, -662, -662, -662, -662, -662, -662, -662, + -662, -662, 835, -662, -662, -662, -662, -662, -662, -662, + + -662, -662, -662, -662, -662, -662, -662, -662, -662, -662, + -662, -662, -662, -662, -662, -662, -662, -662, -662, -662, + -662, -662, -662, -662, -662, -662, -662, -662, -662, -662, + -662, -662, -662, -662, -662, -662, -662, -662, -662, -662, + -662, -662, -662, -662, -662, -662, -662, -662, -662, -662, + -662, -662, -662, -662, -662, -662, -662, -662, -662, -662, + -662, -662, -662, -662, -662, -662, -662, -662, -662, -662, + -662, -662, -662, -662, -662, -662, -662, -662, -662, -662, + -662, -662, -662, -662, -662, -662, -662, -662 + }, + + { + 55, -663, -663, -663, -663, -663, -663, -663, -663, -663, + + -663, -663, -663, -663, -663, -663, -663, -663, -663, -663, + -663, -663, -663, -663, -663, -663, -663, -663, -663, -663, + -663, -663, 836, -663, -663, -663, -663, -663, -663, -663, + -663, -663, -663, -663, -663, -663, -663, -663, -663, -663, + -663, -663, -663, -663, -663, -663, -663, -663, -663, -663, + -663, -663, -663, -663, -663, -663, -663, -663, -663, -663, + -663, -663, -663, -663, -663, -663, -663, -663, -663, -663, + -663, -663, -663, -663, -663, -663, -663, -663, -663, -663, + -663, -663, -663, -663, -663, -663, -663, -663, -663, -663, + -663, -663, -663, -663, -663, -663, -663, -663, -663, -663, + + -663, -663, -663, -663, -663, -663, -663, -663, -663, -663, + -663, -663, -663, -663, -663, -663, -663, -663 + }, + + { + 55, -664, -664, -664, -664, -664, -664, -664, -664, -664, + -664, -664, -664, -664, -664, -664, -664, -664, -664, -664, + -664, -664, -664, -664, -664, -664, -664, -664, -664, -664, + -664, -664, 837, -664, -664, -664, -664, -664, -664, -664, + -664, -664, -664, -664, -664, -664, -664, -664, -664, -664, + -664, -664, -664, -664, -664, -664, -664, -664, -664, -664, + -664, -664, -664, -664, -664, -664, -664, -664, -664, -664, + -664, -664, -664, -664, -664, -664, -664, -664, -664, -664, + + -664, -664, -664, -664, -664, -664, -664, -664, -664, -664, + -664, -664, -664, -664, -664, -664, -664, -664, -664, -664, + -664, -664, -664, -664, -664, -664, -664, -664, -664, -664, + -664, -664, -664, -664, -664, -664, -664, -664, -664, -664, + -664, -664, -664, -664, -664, -664, -664, -664 + }, + + { + 55, -665, -665, -665, -665, -665, -665, -665, -665, -665, + -665, -665, -665, -665, -665, -665, -665, -665, -665, -665, + -665, -665, -665, -665, -665, -665, -665, -665, -665, -665, + -665, -665, 838, -665, -665, -665, -665, -665, -665, -665, + -665, -665, -665, -665, -665, -665, -665, -665, -665, -665, + + -665, -665, -665, -665, -665, -665, -665, -665, -665, -665, + -665, -665, -665, -665, -665, -665, -665, -665, -665, -665, + -665, -665, -665, -665, -665, -665, -665, -665, -665, -665, + -665, -665, -665, -665, -665, -665, -665, -665, -665, -665, + -665, -665, -665, -665, -665, -665, -665, -665, -665, -665, + -665, -665, -665, -665, -665, -665, -665, -665, -665, -665, + -665, -665, -665, -665, -665, -665, -665, -665, -665, -665, + -665, -665, -665, -665, -665, -665, -665, -665 + }, + + { + 55, -666, -666, -666, -666, -666, -666, -666, -666, -666, + -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, + + -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, + -666, -666, 830, -666, -666, -666, -666, -666, -666, -666, + -666, -666, -666, -666, -666, -666, -666, -666, 831, 831, + 831, 831, 831, 831, 831, 831, 831, 831, -666, -666, + -666, -666, -666, -666, -666, 830, 830, 830, 830, 830, + 830, 830, 830, 830, 830, 830, 830, 830, 830, 830, + 830, 830, 830, 830, 830, 830, 830, 830, 830, 830, + 830, -666, -666, -666, -666, -666, -666, -666, -666, -666, + -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, + -666, -666, -666, -666, -666, -666, -666, -666, -666, -666, + + -666, -666, -666, -666, -666, -666, -666, -666 + }, + + { + 55, -667, -667, -667, -667, -667, -667, -667, -667, -667, + -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, + -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, + -667, -667, 839, -667, -667, -667, -667, -667, -667, -667, + -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, + -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, + -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, + -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, + -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, + + -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, + -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, + -667, -667, -667, -667, -667, -667, -667, -667, -667, -667, + -667, -667, -667, -667, -667, -667, -667, -667 + }, + + { + 55, -668, -668, -668, -668, -668, -668, -668, -668, -668, + -668, -668, -668, -668, -668, -668, -668, -668, -668, -668, + -668, -668, -668, -668, -668, -668, -668, -668, -668, -668, + -668, -668, 840, -668, -668, -668, -668, -668, -668, -668, + -668, -668, -668, -668, -668, -668, -668, -668, -668, -668, + -668, -668, -668, -668, -668, -668, -668, -668, -668, -668, + + -668, -668, -668, -668, -668, -668, -668, -668, -668, -668, + -668, -668, -668, -668, -668, -668, -668, -668, -668, -668, + -668, -668, -668, -668, -668, -668, -668, -668, -668, -668, + -668, -668, -668, -668, -668, -668, -668, -668, -668, -668, + -668, -668, -668, -668, -668, -668, -668, -668, -668, -668, + -668, -668, -668, -668, -668, -668, -668, -668, -668, -668, + -668, -668, -668, -668, -668, -668, -668, -668 + }, + + { + 55, -669, -669, -669, -669, -669, -669, -669, -669, -669, + -669, -669, -669, -669, -669, -669, -669, -669, -669, -669, + -669, -669, -669, -669, -669, -669, -669, -669, -669, -669, + + -669, -669, 841, -669, -669, -669, -669, -669, -669, -669, + -669, -669, -669, -669, -669, -669, -669, -669, 842, 842, + 842, 842, 842, 842, 842, 842, 842, 842, -669, -669, + -669, -669, -669, -669, -669, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, 841, 841, + 841, -669, -669, -669, -669, -669, -669, -669, -669, -669, + -669, -669, -669, -669, -669, -669, -669, -669, -669, -669, + -669, -669, -669, -669, -669, -669, -669, -669, -669, -669, + -669, -669, -669, -669, -669, -669, -669, -669 + + }, + + { + 55, -670, -670, -670, -670, -670, -670, -670, -670, -670, + -670, -670, -670, -670, -670, -670, -670, -670, -670, -670, + -670, -670, -670, -670, -670, -670, -670, -670, -670, -670, + -670, -670, 843, -670, -670, -670, -670, -670, -670, -670, + -670, -670, -670, -670, -670, -670, -670, -670, -670, -670, + -670, -670, -670, -670, -670, -670, -670, -670, -670, -670, + -670, -670, -670, -670, -670, -670, -670, -670, -670, -670, + -670, -670, -670, -670, -670, -670, -670, -670, -670, -670, + -670, -670, -670, -670, -670, -670, -670, -670, -670, -670, + -670, -670, -670, -670, -670, -670, -670, -670, -670, -670, + + -670, -670, -670, -670, -670, -670, -670, -670, -670, -670, + -670, -670, -670, -670, -670, -670, -670, -670, -670, -670, + -670, -670, -670, -670, -670, -670, -670, -670 + }, + + { + 55, -671, -671, -671, -671, -671, -671, -671, -671, -671, + -671, -671, -671, -671, -671, -671, -671, -671, -671, -671, + -671, -671, -671, -671, -671, -671, -671, -671, -671, -671, + -671, -671, 844, -671, -671, -671, -671, -671, -671, -671, + -671, -671, -671, -671, -671, -671, -671, -671, 845, 845, + 845, 845, 845, 845, 845, 845, 845, 845, -671, -671, + -671, -671, -671, -671, -671, 844, 844, 844, 844, 844, + + 844, 844, 844, 844, 844, 844, 844, 844, 844, 844, + 844, 844, 844, 844, 844, 844, 844, 844, 844, 844, + 844, -671, -671, -671, -671, -671, -671, -671, -671, -671, + -671, -671, -671, -671, -671, -671, -671, -671, -671, -671, + -671, -671, -671, -671, -671, -671, -671, -671, -671, -671, + -671, -671, -671, -671, -671, -671, -671, -671 + }, + + { + 55, -672, -672, -672, -672, -672, -672, -672, -672, -672, + -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, + -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, + -672, -672, 846, -672, -672, -672, -672, -672, -672, -672, + + -672, -672, -672, -672, -672, -672, -672, -672, 847, 847, + 847, 847, 847, 847, 847, 847, 847, 847, -672, -672, + -672, -672, -672, -672, -672, 846, 846, 846, 846, 846, + 846, 846, 846, 846, 846, 846, 846, 846, 846, 846, + 846, 846, 846, 846, 846, 846, 846, 846, 846, 846, + 846, -672, -672, -672, -672, -672, -672, -672, -672, -672, + -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, + -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, + -672, -672, -672, -672, -672, -672, -672, -672 + }, + + { + 55, -673, -673, -673, -673, -673, -673, -673, -673, -673, + + -673, -673, -673, -673, -673, -673, -673, -673, -673, -673, + -673, -673, -673, -673, -673, -673, -673, -673, -673, -673, + -673, -673, -673, -673, -673, -673, -673, -673, -673, -673, + -673, -673, -673, -673, -673, -673, -673, -673, 848, 848, + 848, 848, 848, 848, 848, 848, 848, 848, -673, -673, + -673, -673, -673, -673, -673, -673, -673, -673, -673, -673, + -673, -673, -673, -673, -673, -673, -673, -673, -673, -673, + -673, -673, -673, -673, -673, -673, -673, -673, -673, -673, + -673, -673, -673, -673, -673, -673, -673, -673, -673, -673, + -673, -673, -673, -673, -673, -673, -673, -673, -673, -673, + + -673, -673, -673, -673, -673, -673, -673, -673, -673, -673, + -673, -673, -673, -673, -673, -673, -673, -673 + }, + + { + 55, -674, -674, -674, -674, -674, -674, -674, -674, -674, + -674, -674, -674, -674, -674, -674, -674, -674, -674, -674, + -674, -674, -674, -674, -674, -674, -674, -674, -674, -674, + -674, -674, -674, -674, -674, -674, -674, -674, -674, -674, + -674, -674, -674, -674, -674, -674, -674, -674, 849, 849, + 849, 849, 849, 849, 849, 849, 849, 849, -674, -674, + -674, -674, -674, -674, -674, -674, -674, -674, -674, -674, + -674, -674, -674, -674, -674, -674, -674, -674, -674, -674, + + -674, -674, -674, -674, -674, -674, -674, -674, -674, -674, + -674, -674, -674, -674, -674, -674, -674, -674, -674, -674, + -674, -674, -674, -674, -674, -674, -674, -674, -674, -674, + -674, -674, -674, -674, -674, -674, -674, -674, -674, -674, + -674, -674, -674, -674, -674, -674, -674, -674 + }, + + { + 55, -675, -675, -675, -675, -675, -675, -675, -675, -675, + -675, -675, -675, -675, -675, -675, -675, -675, -675, -675, + -675, -675, -675, -675, -675, -675, -675, -675, -675, -675, + -675, -675, -675, -675, -675, -675, -675, -675, -675, -675, + -675, -675, -675, -675, -675, -675, -675, -675, 849, 850, + + 850, 850, 850, 850, 850, 850, 850, 850, -675, -675, + -675, -675, -675, -675, -675, -675, -675, -675, -675, -675, + -675, -675, -675, -675, -675, -675, -675, -675, -675, -675, + -675, -675, -675, -675, -675, -675, -675, -675, -675, -675, + -675, -675, -675, -675, -675, -675, -675, -675, -675, -675, + -675, -675, -675, -675, -675, -675, -675, -675, -675, -675, + -675, -675, -675, -675, -675, -675, -675, -675, -675, -675, + -675, -675, -675, -675, -675, -675, -675, -675 + }, + + { + 55, -676, -676, -676, -676, -676, -676, -676, -676, -676, + -676, -676, -676, -676, -676, -676, -676, -676, -676, -676, + + -676, -676, -676, -676, -676, -676, -676, -676, -676, -676, + -676, -676, 851, -676, -676, -676, -676, -676, -676, -676, + -676, -676, -676, -676, -676, -676, -676, -676, 852, 852, + 852, 852, 852, 852, 852, 852, 852, 852, -676, -676, + -676, -676, -676, -676, -676, 851, 851, 851, 851, 851, + 851, 851, 851, 851, 851, 851, 851, 851, 851, 851, + 851, 851, 851, 851, 851, 851, 851, 851, 851, 851, + 851, -676, -676, -676, -676, -676, -676, -676, -676, -676, + -676, -676, -676, -676, -676, -676, -676, -676, -676, -676, + -676, -676, -676, -676, -676, -676, -676, -676, -676, -676, + + -676, -676, -676, -676, -676, -676, -676, -676 + }, + + { + 55, -677, -677, -677, -677, -677, -677, -677, -677, -677, + -677, -677, -677, -677, -677, -677, -677, -677, -677, -677, + -677, -677, -677, -677, -677, -677, -677, -677, -677, -677, + -677, -677, -677, -677, -677, -677, -677, -677, -677, -677, + -677, -677, -677, -677, -677, -677, -677, -677, -677, 853, + 853, 853, 853, 853, 853, 853, 853, 853, -677, -677, + -677, -677, -677, -677, -677, -677, -677, -677, -677, -677, + -677, -677, -677, -677, -677, -677, -677, -677, -677, -677, + -677, -677, -677, -677, -677, -677, -677, -677, -677, -677, + + -677, -677, -677, -677, -677, -677, -677, -677, -677, -677, + -677, -677, -677, -677, -677, -677, -677, -677, -677, -677, + -677, -677, -677, -677, -677, -677, -677, -677, -677, -677, + -677, -677, -677, -677, -677, -677, -677, -677 + }, + + { + 55, -678, -678, -678, -678, -678, -678, -678, -678, -678, + -678, -678, -678, -678, -678, -678, -678, -678, -678, -678, + -678, -678, -678, -678, -678, -678, -678, -678, -678, -678, + -678, -678, -678, -678, -678, -678, -678, -678, -678, -678, + -678, -678, -678, -678, -678, -678, -678, -678, 849, 854, + 854, 854, 854, 854, 854, 854, 854, 854, -678, -678, + + -678, -678, -678, -678, -678, -678, -678, -678, -678, -678, + -678, -678, -678, -678, -678, -678, -678, -678, -678, -678, + -678, -678, -678, -678, -678, -678, -678, -678, -678, -678, + -678, -678, -678, -678, -678, -678, -678, -678, -678, -678, + -678, -678, -678, -678, -678, -678, -678, -678, -678, -678, + -678, -678, -678, -678, -678, -678, -678, -678, -678, -678, + -678, -678, -678, -678, -678, -678, -678, -678 + }, + + { + 55, -679, -679, -679, -679, -679, -679, -679, -679, -679, + -679, -679, -679, -679, -679, -679, -679, -679, -679, -679, + -679, -679, -679, -679, -679, -679, -679, -679, -679, -679, + + -679, -679, 851, -679, -679, -679, -679, -679, -679, -679, + -679, -679, -679, -679, -679, -679, -679, -679, 852, 855, + 855, 855, 855, 855, 855, 855, 855, 855, -679, -679, + -679, -679, -679, -679, -679, 851, 851, 851, 851, 851, + 851, 851, 851, 851, 851, 851, 851, 851, 851, 851, + 851, 851, 851, 851, 851, 851, 851, 851, 851, 851, + 851, -679, -679, -679, -679, -679, -679, -679, -679, -679, + -679, -679, -679, -679, -679, -679, -679, -679, -679, -679, + -679, -679, -679, -679, -679, -679, -679, -679, -679, -679, + -679, -679, -679, -679, -679, -679, -679, -679 + + }, + + { + 55, -680, -680, -680, -680, -680, -680, -680, -680, -680, + -680, -680, -680, -680, -680, -680, -680, -680, -680, -680, + -680, -680, -680, -680, -680, -680, -680, -680, -680, -680, + -680, -680, 856, -680, -680, -680, -680, -680, -680, -680, + -680, -680, -680, -680, -680, -680, -680, -680, 857, 857, + 857, 857, 857, 857, 857, 857, 857, 857, -680, -680, + -680, -680, -680, -680, -680, 856, 856, 856, 856, 856, + 856, 856, 856, 856, 856, 856, 856, 856, 856, 856, + 856, 856, 856, 856, 856, 856, 856, 856, 856, 856, + 856, -680, -680, -680, -680, -680, -680, -680, -680, -680, + + -680, -680, -680, -680, -680, -680, -680, -680, -680, -680, + -680, -680, -680, -680, -680, -680, -680, -680, -680, -680, + -680, -680, -680, -680, -680, -680, -680, -680 + }, + + { + 55, -681, -681, -681, -681, -681, -681, -681, -681, -681, + -681, -681, -681, -681, -681, -681, -681, -681, -681, -681, + -681, -681, -681, -681, -681, -681, -681, -681, -681, -681, + -681, -681, 858, -681, -681, -681, -681, -681, -681, -681, + -681, -681, -681, -681, -681, -681, -681, -681, -681, -681, + -681, -681, -681, -681, -681, -681, -681, -681, -681, -681, + -681, -681, -681, -681, -681, -681, -681, -681, -681, -681, + + -681, -681, -681, -681, -681, -681, -681, -681, -681, -681, + -681, -681, -681, -681, -681, -681, -681, -681, -681, -681, + -681, -681, -681, -681, -681, -681, -681, -681, -681, -681, + -681, -681, -681, -681, -681, -681, -681, -681, -681, -681, + -681, -681, -681, -681, -681, -681, -681, -681, -681, -681, + -681, -681, -681, -681, -681, -681, -681, -681 + }, + + { + 55, -682, -682, -682, -682, -682, -682, -682, -682, -682, + -682, -682, -682, -682, -682, -682, -682, -682, -682, -682, + -682, -682, -682, -682, -682, -682, -682, -682, -682, -682, + -682, -682, 859, -682, -682, -682, -682, -682, -682, -682, + + -682, -682, -682, -682, -682, -682, -682, -682, 860, 860, + 860, 860, 860, 860, 860, 860, 860, 860, -682, -682, + -682, -682, -682, -682, -682, 859, 859, 859, 859, 859, + 859, 859, 859, 859, 859, 859, 859, 859, 859, 859, + 859, 859, 859, 859, 859, 859, 859, 859, 859, 859, + 859, -682, -682, -682, -682, -682, -682, -682, -682, -682, + -682, -682, -682, -682, -682, -682, -682, -682, -682, -682, + -682, -682, -682, -682, -682, -682, -682, -682, -682, -682, + -682, -682, -682, -682, -682, -682, -682, -682 + }, + + { + 55, -683, -683, -683, -683, -683, -683, -683, -683, -683, + + -683, -683, -683, -683, -683, -683, -683, -683, -683, -683, + -683, -683, -683, -683, -683, -683, -683, -683, -683, -683, + -683, -683, 851, -683, -683, -683, -683, -683, -683, -683, + -683, -683, -683, -683, -683, -683, -683, -683, 852, 861, + 861, 861, 861, 861, 861, 861, 861, 861, -683, -683, + -683, -683, -683, -683, -683, 851, 851, 851, 851, 851, + 851, 851, 851, 851, 851, 851, 851, 851, 851, 851, + 851, 851, 851, 851, 851, 851, 851, 851, 851, 851, + 851, -683, -683, -683, -683, -683, -683, -683, -683, -683, + -683, -683, -683, -683, -683, -683, -683, -683, -683, -683, + + -683, -683, -683, -683, -683, -683, -683, -683, -683, -683, + -683, -683, -683, -683, -683, -683, -683, -683 + }, + + { + 55, -684, -684, -684, -684, -684, -684, -684, -684, -684, + -684, -684, -684, -684, -684, -684, -684, -684, -684, -684, + -684, -684, -684, -684, -684, -684, -684, -684, -684, -684, + -684, -684, 862, -684, -684, -684, -684, -684, -684, -684, + -684, -684, -684, -684, -684, -684, -684, -684, 863, 863, + 863, 863, 863, 863, 863, 863, 863, 863, -684, -684, + -684, -684, -684, -684, -684, 862, 862, 862, 862, 862, + 862, 862, 862, 862, 862, 862, 862, 862, 862, 862, + + 862, 862, 862, 862, 862, 862, 862, 862, 862, 862, + 862, -684, -684, -684, -684, -684, -684, -684, -684, -684, + -684, -684, -684, -684, -684, -684, -684, -684, -684, -684, + -684, -684, -684, -684, -684, -684, -684, -684, -684, -684, + -684, -684, -684, -684, -684, -684, -684, -684 + }, + + { + 55, -685, -685, -685, -685, -685, -685, -685, -685, -685, + -685, -685, -685, -685, -685, -685, -685, -685, -685, -685, + -685, -685, -685, -685, -685, -685, -685, -685, -685, -685, + -685, -685, 859, -685, -685, -685, -685, -685, -685, -685, + -685, -685, -685, -685, -685, -685, -685, -685, 860, 864, + + 864, 864, 864, 864, 864, 864, 864, 864, -685, -685, + -685, -685, -685, -685, -685, 859, 859, 859, 859, 859, + 859, 859, 859, 859, 859, 859, 859, 859, 859, 859, + 859, 859, 859, 859, 859, 859, 859, 859, 859, 859, + 859, -685, -685, -685, -685, -685, -685, -685, -685, -685, + -685, -685, -685, -685, -685, -685, -685, -685, -685, -685, + -685, -685, -685, -685, -685, -685, -685, -685, -685, -685, + -685, -685, -685, -685, -685, -685, -685, -685 + }, + + { + 55, -686, -686, -686, -686, -686, -686, -686, -686, -686, + -686, -686, -686, -686, -686, -686, -686, -686, -686, -686, + + -686, -686, -686, -686, -686, -686, -686, -686, -686, -686, + -686, -686, 865, -686, -686, -686, -686, -686, -686, -686, + -686, -686, -686, -686, -686, -686, -686, -686, 866, 866, + 866, 866, 866, 866, 866, 866, 866, 866, -686, -686, + -686, -686, -686, -686, -686, 865, 865, 865, 865, 865, + 865, 865, 865, 865, 865, 865, 865, 865, 865, 865, + 865, 865, 865, 865, 865, 865, 865, 865, 865, 865, + 865, -686, -686, -686, -686, -686, -686, -686, -686, -686, + -686, -686, -686, -686, -686, -686, -686, -686, -686, -686, + -686, -686, -686, -686, -686, -686, -686, -686, -686, -686, + + -686, -686, -686, -686, -686, -686, -686, -686 + }, + + { + 55, -687, -687, -687, -687, -687, -687, -687, -687, -687, + -687, -687, -687, -687, -687, -687, -687, -687, -687, -687, + -687, -687, -687, -687, -687, -687, -687, -687, -687, -687, + -687, -687, 867, -687, -687, -687, -687, -687, -687, -687, + -687, -687, -687, -687, -687, -687, -687, -687, -687, -687, + -687, -687, -687, -687, -687, -687, -687, -687, -687, -687, + -687, -687, -687, -687, -687, -687, -687, -687, -687, -687, + -687, -687, -687, -687, -687, -687, -687, -687, -687, -687, + -687, -687, -687, -687, -687, -687, -687, -687, -687, -687, + + -687, -687, -687, -687, -687, -687, -687, -687, -687, -687, + -687, -687, -687, -687, -687, -687, -687, -687, -687, -687, + -687, -687, -687, -687, -687, -687, -687, -687, -687, -687, + -687, -687, -687, -687, -687, -687, -687, -687 + }, + + { + 55, -688, -688, -688, -688, -688, -688, -688, -688, -688, + -688, -688, -688, -688, -688, -688, -688, -688, -688, -688, + -688, -688, -688, -688, -688, -688, -688, -688, -688, -688, + -688, -688, 868, -688, -688, -688, -688, -688, -688, -688, + -688, -688, -688, -688, -688, -688, -688, -688, 860, 860, + 860, 860, 860, 860, 860, 860, 860, 860, -688, -688, + + -688, -688, -688, -688, -688, 868, 868, 868, 868, 868, + 868, 868, 868, 868, 868, 868, 868, 868, 868, 868, + 868, 868, 868, 868, 868, 868, 868, 868, 868, 868, + 868, -688, -688, -688, -688, -688, -688, -688, -688, -688, + -688, -688, -688, -688, -688, -688, -688, -688, -688, -688, + -688, -688, -688, -688, -688, -688, -688, -688, -688, -688, + -688, -688, -688, -688, -688, -688, -688, -688 + }, + + { + 55, -689, -689, -689, -689, -689, -689, -689, -689, -689, + -689, -689, -689, -689, -689, -689, -689, -689, -689, -689, + -689, -689, -689, -689, -689, -689, -689, -689, -689, -689, + + -689, -689, 869, -689, -689, -689, -689, -689, -689, -689, + -689, -689, -689, -689, -689, -689, -689, -689, -689, -689, + -689, -689, -689, -689, -689, -689, -689, -689, -689, -689, + -689, -689, -689, -689, -689, -689, -689, -689, -689, -689, + -689, -689, -689, -689, -689, -689, -689, -689, -689, -689, + -689, -689, -689, -689, -689, -689, -689, -689, -689, -689, + -689, -689, -689, -689, -689, -689, -689, -689, -689, -689, + -689, -689, -689, -689, -689, -689, -689, -689, -689, -689, + -689, -689, -689, -689, -689, -689, -689, -689, -689, -689, + -689, -689, -689, -689, -689, -689, -689, -689 + + }, + + { + 55, -690, -690, -690, -690, -690, -690, -690, -690, -690, + -690, -690, -690, -690, -690, -690, -690, -690, -690, -690, + -690, -690, -690, -690, -690, -690, -690, -690, -690, -690, + -690, -690, 870, -690, -690, -690, -690, -690, -690, -690, + -690, -690, -690, -690, -690, -690, -690, -690, -690, -690, + -690, -690, -690, -690, -690, -690, -690, -690, -690, -690, + -690, -690, -690, -690, -690, -690, -690, -690, -690, -690, + -690, -690, -690, -690, -690, -690, -690, -690, -690, -690, + -690, -690, -690, -690, -690, -690, -690, -690, -690, -690, + -690, -690, -690, -690, -690, -690, -690, -690, -690, -690, + + -690, -690, -690, -690, -690, -690, -690, -690, -690, -690, + -690, -690, -690, -690, -690, -690, -690, -690, -690, -690, + -690, -690, -690, -690, -690, -690, -690, -690 + }, + + { + 55, -691, -691, -691, -691, -691, -691, -691, -691, -691, + -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, + -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, + -691, -691, 871, -691, -691, -691, -691, -691, -691, -691, + -691, -691, -691, -691, -691, -691, -691, -691, 872, 872, + 872, 872, 872, 872, 872, 872, 872, 872, -691, -691, + -691, -691, -691, -691, -691, 871, 871, 871, 871, 871, + + 871, 871, 871, 871, 871, 871, 871, 871, 871, 871, + 871, 871, 871, 871, 871, 871, 871, 871, 871, 871, + 871, -691, -691, -691, -691, -691, -691, -691, -691, -691, + -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, + -691, -691, -691, -691, -691, -691, -691, -691, -691, -691, + -691, -691, -691, -691, -691, -691, -691, -691 + }, + + { + 55, -692, -692, -692, -692, -692, -692, -692, -692, -692, + -692, -692, -692, -692, -692, -692, -692, -692, -692, -692, + -692, -692, -692, -692, -692, -692, -692, -692, -692, -692, + -692, -692, 859, -692, -692, -692, -692, -692, -692, -692, + + -692, -692, -692, -692, -692, -692, -692, -692, 860, 873, + 873, 873, 873, 873, 873, 873, 873, 873, -692, -692, + -692, -692, -692, -692, -692, 859, 859, 859, 859, 859, + 859, 859, 859, 859, 859, 859, 859, 859, 859, 859, + 859, 859, 859, 859, 859, 859, 859, 859, 859, 859, + 859, -692, -692, -692, -692, -692, -692, -692, -692, -692, + -692, -692, -692, -692, -692, -692, -692, -692, -692, -692, + -692, -692, -692, -692, -692, -692, -692, -692, -692, -692, + -692, -692, -692, -692, -692, -692, -692, -692 + }, + + { + 55, -693, -693, -693, -693, -693, -693, -693, -693, -693, + + -693, -693, -693, -693, -693, -693, -693, -693, -693, -693, + -693, -693, -693, -693, -693, -693, -693, -693, -693, -693, + -693, -693, 874, -693, -693, -693, -693, -693, -693, -693, + -693, -693, -693, -693, -693, -693, -693, -693, 875, 875, + 875, 875, 875, 875, 875, 875, 875, 875, -693, -693, + -693, -693, -693, -693, -693, 874, 874, 874, 874, 874, + 874, 874, 874, 874, 874, 874, 874, 874, 874, 874, + 874, 874, 874, 874, 874, 874, 874, 874, 874, 874, + 874, -693, -693, -693, -693, -693, -693, -693, -693, -693, + -693, -693, -693, -693, -693, -693, -693, -693, -693, -693, + + -693, -693, -693, -693, -693, -693, -693, -693, -693, -693, + -693, -693, -693, -693, -693, -693, -693, -693 + }, + + { + 55, -694, -694, -694, -694, -694, -694, -694, -694, -694, + -694, -694, -694, -694, -694, -694, -694, -694, -694, -694, + -694, -694, -694, -694, -694, -694, -694, -694, -694, -694, + -694, -694, 876, -694, -694, -694, -694, -694, -694, -694, + -694, -694, -694, -694, -694, -694, -694, -694, -694, -694, + -694, -694, -694, -694, -694, -694, -694, -694, -694, -694, + -694, -694, -694, -694, -694, -694, -694, -694, -694, -694, + -694, -694, -694, -694, -694, -694, -694, -694, -694, -694, + + -694, -694, -694, -694, -694, -694, -694, -694, -694, -694, + -694, -694, -694, -694, -694, -694, -694, -694, -694, -694, + -694, -694, -694, -694, -694, -694, -694, -694, -694, -694, + -694, -694, -694, -694, -694, -694, -694, -694, -694, -694, + -694, -694, -694, -694, -694, -694, -694, -694 + }, + + { + 55, -695, -695, -695, -695, -695, -695, -695, -695, -695, + -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, + -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, + -695, -695, 877, -695, -695, -695, -695, -695, -695, -695, + -695, -695, -695, -695, -695, -695, -695, -695, 860, 860, + + 860, 860, 860, 860, 860, 860, 860, 860, -695, -695, + -695, -695, -695, -695, -695, 877, 877, 877, 877, 877, + 877, 877, 877, 877, 877, 877, 877, 877, 877, 877, + 877, 877, 877, 877, 877, 877, 877, 877, 877, 877, + 877, -695, -695, -695, -695, -695, -695, -695, -695, -695, + -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, + -695, -695, -695, -695, -695, -695, -695, -695, -695, -695, + -695, -695, -695, -695, -695, -695, -695, -695 + }, + + { + 55, -696, -696, -696, -696, -696, -696, -696, -696, -696, + -696, -696, -696, -696, -696, -696, -696, -696, -696, -696, + + -696, -696, -696, -696, -696, -696, -696, -696, -696, -696, + -696, -696, 871, -696, -696, -696, -696, -696, -696, -696, + -696, -696, -696, -696, -696, -696, -696, -696, 872, 878, + 878, 878, 878, 878, 878, 878, 878, 878, -696, -696, + -696, -696, -696, -696, -696, 871, 871, 871, 871, 871, + 871, 871, 871, 871, 871, 871, 871, 871, 871, 871, + 871, 871, 871, 871, 871, 871, 871, 871, 871, 871, + 871, -696, -696, -696, -696, -696, -696, -696, -696, -696, + -696, -696, -696, -696, -696, -696, -696, -696, -696, -696, + -696, -696, -696, -696, -696, -696, -696, -696, -696, -696, + + -696, -696, -696, -696, -696, -696, -696, -696 + }, + + { + 55, -697, -697, -697, -697, -697, -697, -697, -697, -697, + -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, + -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, + -697, -697, 879, -697, -697, -697, -697, -697, -697, -697, + -697, -697, -697, -697, -697, -697, -697, -697, 880, 880, + 880, 880, 880, 880, 880, 880, 880, 880, -697, -697, + -697, -697, -697, -697, -697, 879, 879, 879, 879, 879, + 879, 879, 879, 879, 879, 879, 879, 879, 879, 879, + 879, 879, 879, 879, 879, 879, 879, 879, 879, 879, + + 879, -697, -697, -697, -697, -697, -697, -697, -697, -697, + -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, + -697, -697, -697, -697, -697, -697, -697, -697, -697, -697, + -697, -697, -697, -697, -697, -697, -697, -697 + }, + + { + 55, -698, -698, -698, -698, -698, -698, -698, -698, -698, + -698, -698, -698, -698, -698, -698, -698, -698, -698, -698, + -698, -698, -698, -698, -698, -698, -698, -698, -698, -698, + -698, -698, 881, -698, -698, -698, -698, -698, -698, -698, + -698, -698, -698, -698, -698, -698, -698, -698, -698, -698, + -698, -698, -698, -698, -698, -698, -698, -698, -698, -698, + + -698, -698, -698, -698, -698, -698, -698, -698, -698, -698, + -698, -698, -698, -698, -698, -698, -698, -698, -698, -698, + -698, -698, -698, -698, -698, -698, -698, -698, -698, -698, + -698, -698, -698, -698, -698, -698, -698, -698, -698, -698, + -698, -698, -698, -698, -698, -698, -698, -698, -698, -698, + -698, -698, -698, -698, -698, -698, -698, -698, -698, -698, + -698, -698, -698, -698, -698, -698, -698, -698 + }, + + { + 55, -699, -699, -699, -699, -699, -699, -699, -699, -699, + -699, -699, -699, -699, -699, -699, -699, -699, -699, -699, + -699, -699, -699, -699, -699, -699, -699, -699, -699, -699, + + -699, -699, 882, -699, -699, -699, -699, -699, -699, -699, + -699, -699, -699, -699, -699, -699, -699, -699, 872, 872, + 872, 872, 872, 872, 872, 872, 872, 872, -699, -699, + -699, -699, -699, -699, -699, 882, 882, 882, 882, 882, + 882, 882, 882, 882, 882, 882, 882, 882, 882, 882, + 882, 882, 882, 882, 882, 882, 882, 882, 882, 882, + 882, -699, -699, -699, -699, -699, -699, -699, -699, -699, + -699, -699, -699, -699, -699, -699, -699, -699, -699, -699, + -699, -699, -699, -699, -699, -699, -699, -699, -699, -699, + -699, -699, -699, -699, -699, -699, -699, -699 + + }, + + { + 55, -700, -700, -700, -700, -700, -700, -700, -700, -700, + -700, -700, -700, -700, -700, -700, -700, -700, -700, -700, + -700, -700, -700, -700, -700, -700, -700, -700, -700, -700, + -700, -700, 883, -700, -700, -700, -700, -700, -700, -700, + -700, -700, -700, -700, -700, -700, -700, -700, -700, -700, + -700, -700, -700, -700, -700, -700, -700, -700, -700, -700, + -700, -700, -700, -700, -700, -700, -700, -700, -700, -700, + -700, -700, -700, -700, -700, -700, -700, -700, -700, -700, + -700, -700, -700, -700, -700, -700, -700, -700, -700, -700, + -700, -700, -700, -700, -700, -700, -700, -700, -700, -700, + + -700, -700, -700, -700, -700, -700, -700, -700, -700, -700, + -700, -700, -700, -700, -700, -700, -700, -700, -700, -700, + -700, -700, -700, -700, -700, -700, -700, -700 + }, + + { + 55, -701, -701, -701, -701, -701, -701, -701, -701, -701, + -701, -701, -701, -701, -701, -701, -701, -701, -701, -701, + -701, -701, -701, -701, -701, -701, -701, -701, -701, -701, + -701, -701, 884, -701, -701, -701, -701, -701, -701, -701, + -701, -701, -701, -701, -701, -701, -701, -701, -701, -701, + -701, -701, -701, -701, -701, -701, -701, -701, -701, -701, + -701, -701, -701, -701, -701, -701, -701, -701, -701, -701, + + -701, -701, -701, -701, -701, -701, -701, -701, -701, -701, + -701, -701, -701, -701, -701, -701, -701, -701, -701, -701, + -701, -701, -701, -701, -701, -701, -701, -701, -701, -701, + -701, -701, -701, -701, -701, -701, -701, -701, -701, -701, + -701, -701, -701, -701, -701, -701, -701, -701, -701, -701, + -701, -701, -701, -701, -701, -701, -701, -701 + }, + + { + 55, -702, -702, -702, -702, -702, -702, -702, -702, -702, + -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, + -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, + -702, -702, 885, -702, -702, -702, -702, -702, -702, -702, + + -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, + -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, + -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, + -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, + -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, + -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, + -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, + -702, -702, -702, -702, -702, -702, -702, -702, -702, -702, + -702, -702, -702, -702, -702, -702, -702, -702 + }, + + { + 55, -703, -703, -703, -703, -703, -703, -703, -703, -703, + + -703, -703, -703, -703, -703, -703, -703, -703, -703, -703, + -703, -703, -703, -703, -703, -703, -703, -703, -703, -703, + -703, -703, 886, -703, -703, -703, -703, -703, -703, -703, + -703, -703, -703, -703, -703, -703, -703, -703, -703, -703, + -703, -703, -703, -703, -703, -703, -703, -703, -703, -703, + -703, -703, -703, -703, -703, -703, -703, -703, -703, -703, + -703, -703, -703, -703, -703, -703, -703, -703, -703, -703, + -703, -703, -703, -703, -703, -703, -703, -703, -703, -703, + -703, -703, -703, -703, -703, -703, -703, -703, -703, -703, + -703, -703, -703, -703, -703, -703, -703, -703, -703, -703, + + -703, -703, -703, -703, -703, -703, -703, -703, -703, -703, + -703, -703, -703, -703, -703, -703, -703, -703 + }, + + { + 55, -704, -704, -704, -704, -704, -704, -704, -704, -704, + -704, -704, -704, -704, -704, -704, -704, -704, -704, -704, + -704, -704, -704, -704, -704, -704, -704, -704, -704, -704, + -704, -704, 887, -704, -704, -704, -704, -704, -704, -704, + -704, -704, -704, -704, -704, -704, -704, -704, 888, 888, + 888, 888, 888, 888, 888, 888, 888, 888, -704, -704, + -704, -704, -704, -704, -704, 887, 887, 887, 887, 887, + 887, 887, 887, 887, 887, 887, 887, 887, 887, 887, + + 887, 887, 887, 887, 887, 887, 887, 887, 887, 887, + 887, -704, -704, -704, -704, -704, -704, -704, -704, -704, + -704, -704, -704, -704, -704, -704, -704, -704, -704, -704, + -704, -704, -704, -704, -704, -704, -704, -704, -704, -704, + -704, -704, -704, -704, -704, -704, -704, -704 + }, + + { + 55, -705, -705, -705, -705, -705, -705, -705, -705, -705, + -705, -705, -705, -705, -705, -705, -705, -705, -705, -705, + -705, -705, -705, -705, -705, -705, -705, -705, -705, -705, + -705, -705, 889, -705, -705, -705, -705, -705, -705, -705, + -705, -705, -705, -705, -705, -705, -705, -705, -705, -705, + + -705, -705, -705, -705, -705, -705, -705, -705, -705, -705, + -705, -705, -705, -705, -705, -705, -705, -705, -705, -705, + -705, -705, -705, -705, -705, -705, -705, -705, -705, -705, + -705, -705, -705, -705, -705, -705, -705, -705, -705, -705, + -705, -705, -705, -705, -705, -705, -705, -705, -705, -705, + -705, -705, -705, -705, -705, -705, -705, -705, -705, -705, + -705, -705, -705, -705, -705, -705, -705, -705, -705, -705, + -705, -705, -705, -705, -705, -705, -705, -705 + }, + + { + 55, -706, -706, -706, -706, -706, -706, -706, -706, -706, + -706, -706, -706, -706, -706, -706, -706, -706, -706, -706, + + -706, -706, -706, -706, -706, -706, -706, -706, -706, -706, + -706, -706, 890, -706, -706, -706, -706, -706, -706, -706, + -706, -706, -706, -706, -706, -706, -706, -706, 891, 891, + 891, 891, 891, 891, 891, 891, 891, 891, -706, -706, + -706, -706, -706, -706, -706, 890, 890, 890, 890, 890, + 890, 890, 890, 890, 890, 890, 890, 890, 890, 890, + 890, 890, 890, 890, 890, 890, 890, 890, 890, 890, + 890, -706, -706, -706, -706, -706, -706, -706, -706, -706, + -706, -706, -706, -706, -706, -706, -706, -706, -706, -706, + -706, -706, -706, -706, -706, -706, -706, -706, -706, -706, + + -706, -706, -706, -706, -706, -706, -706, -706 + }, + + { + 55, -707, -707, -707, -707, -707, -707, -707, -707, -707, + -707, -707, -707, -707, -707, -707, -707, -707, -707, -707, + -707, -707, -707, -707, -707, -707, -707, -707, -707, -707, + -707, -707, 892, -707, -707, -707, -707, -707, -707, -707, + -707, -707, -707, -707, -707, -707, -707, -707, 893, 893, + 893, 893, 893, 893, 893, 893, 893, 893, -707, -707, + -707, -707, -707, -707, -707, 892, 892, 892, 892, 892, + 892, 892, 892, 892, 892, 892, 892, 892, 892, 892, + 892, 892, 892, 892, 892, 892, 892, 892, 892, 892, + + 892, -707, -707, -707, -707, -707, -707, -707, -707, -707, + -707, -707, -707, -707, -707, -707, -707, -707, -707, -707, + -707, -707, -707, -707, -707, -707, -707, -707, -707, -707, + -707, -707, -707, -707, -707, -707, -707, -707 + }, + + { + 55, -708, -708, -708, -708, -708, -708, -708, -708, -708, + -708, -708, -708, -708, -708, -708, -708, -708, -708, -708, + -708, -708, -708, -708, -708, -708, -708, -708, -708, -708, + -708, -708, -708, -708, -708, -708, -708, -708, -708, -708, + -708, -708, -708, -708, -708, -708, -708, -708, 894, 894, + 894, 894, 894, 894, 894, 894, 894, 894, -708, -708, + + -708, -708, -708, -708, -708, -708, -708, -708, -708, -708, + -708, -708, -708, -708, -708, -708, -708, -708, -708, -708, + -708, -708, -708, -708, -708, -708, -708, -708, -708, -708, + -708, -708, -708, -708, -708, -708, -708, -708, -708, -708, + -708, -708, -708, -708, -708, -708, -708, -708, -708, -708, + -708, -708, -708, -708, -708, -708, -708, -708, -708, -708, + -708, -708, -708, -708, -708, -708, -708, -708 + }, + + { + 55, -709, -709, -709, -709, -709, -709, -709, -709, -709, + -709, -709, -709, -709, -709, -709, -709, -709, -709, -709, + -709, -709, -709, -709, -709, -709, -709, -709, -709, -709, + + -709, -709, -709, -709, -709, -709, -709, -709, -709, -709, + -709, -709, -709, -709, -709, -709, -709, -709, 895, 895, + 895, 895, 895, 895, 895, 895, 895, 895, -709, -709, + -709, -709, -709, -709, -709, -709, -709, -709, -709, -709, + -709, -709, -709, -709, -709, -709, -709, -709, -709, -709, + -709, -709, -709, -709, -709, -709, -709, -709, -709, -709, + -709, -709, -709, -709, -709, -709, -709, -709, -709, -709, + -709, -709, -709, -709, -709, -709, -709, -709, -709, -709, + -709, -709, -709, -709, -709, -709, -709, -709, -709, -709, + -709, -709, -709, -709, -709, -709, -709, -709 + + }, + + { + 55, -710, -710, -710, -710, -710, -710, -710, -710, -710, + -710, -710, -710, -710, -710, -710, -710, -710, -710, -710, + -710, -710, -710, -710, -710, -710, -710, -710, -710, -710, + -710, -710, -710, -710, -710, -710, -710, -710, -710, -710, + -710, -710, -710, -710, -710, -710, -710, -710, 896, 896, + 896, 896, 896, 896, 896, 896, 896, 896, -710, -710, + -710, -710, -710, -710, -710, -710, -710, -710, -710, -710, + -710, -710, -710, -710, -710, -710, -710, -710, -710, -710, + -710, -710, -710, -710, -710, -710, -710, -710, -710, -710, + -710, -710, -710, -710, -710, -710, -710, -710, -710, -710, + + -710, -710, -710, -710, -710, -710, -710, -710, -710, -710, + -710, -710, -710, -710, -710, -710, -710, -710, -710, -710, + -710, -710, -710, -710, -710, -710, -710, -710 + }, + + { + 55, -711, -711, -711, -711, -711, -711, -711, -711, -711, + -711, -711, -711, -711, -711, -711, -711, -711, -711, -711, + -711, -711, -711, -711, -711, -711, -711, -711, -711, -711, + -711, -711, 897, -711, -711, -711, -711, -711, -711, -711, + -711, -711, -711, -711, -711, -711, -711, -711, 898, 898, + 898, 898, 898, 898, 898, 898, 898, 898, -711, -711, + -711, -711, -711, -711, -711, 897, 897, 897, 897, 897, + + 897, 897, 897, 897, 897, 897, 897, 897, 897, 897, + 897, 897, 897, 897, 897, 897, 897, 897, 897, 897, + 897, -711, -711, -711, -711, -711, -711, -711, -711, -711, + -711, -711, -711, -711, -711, -711, -711, -711, -711, -711, + -711, -711, -711, -711, -711, -711, -711, -711, -711, -711, + -711, -711, -711, -711, -711, -711, -711, -711 + }, + + { + 55, -712, -712, -712, -712, -712, -712, -712, -712, -712, + -712, -712, -712, -712, -712, -712, -712, -712, -712, -712, + -712, -712, -712, -712, -712, -712, -712, -712, -712, -712, + -712, -712, -712, -712, -712, -712, -712, -712, -712, -712, + + -712, -712, -712, -712, -712, -712, -712, -712, 899, 899, + 899, 899, 899, 899, 899, 899, 899, 899, -712, -712, + -712, -712, -712, -712, -712, -712, -712, -712, -712, -712, + -712, -712, -712, -712, -712, -712, -712, -712, -712, -712, + -712, -712, -712, -712, -712, -712, -712, -712, -712, -712, + -712, -712, -712, -712, -712, -712, -712, -712, -712, -712, + -712, -712, -712, -712, -712, -712, -712, -712, -712, -712, + -712, -712, -712, -712, -712, -712, -712, -712, -712, -712, + -712, -712, -712, -712, -712, -712, -712, -712 + }, + + { + 55, -713, -713, -713, -713, -713, -713, -713, -713, -713, + + -713, -713, -713, -713, -713, -713, -713, -713, -713, -713, + -713, -713, -713, -713, -713, -713, -713, -713, -713, -713, + -713, -713, 900, -713, -713, -713, -713, -713, -713, -713, + -713, -713, -713, -713, -713, -713, -713, -713, 901, 901, + 901, 901, 901, 901, 901, 901, 901, 901, -713, -713, + -713, -713, -713, -713, -713, 900, 900, 900, 900, 900, + 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, + 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, + 900, -713, -713, -713, -713, -713, -713, -713, -713, -713, + -713, -713, -713, -713, -713, -713, -713, -713, -713, -713, + + -713, -713, -713, -713, -713, -713, -713, -713, -713, -713, + -713, -713, -713, -713, -713, -713, -713, -713 + }, + + { + 55, -714, -714, -714, -714, -714, -714, -714, -714, -714, + -714, -714, -714, -714, -714, -714, -714, -714, -714, -714, + -714, -714, -714, -714, -714, -714, -714, -714, -714, -714, + -714, -714, 900, -714, -714, -714, -714, -714, -714, -714, + -714, -714, -714, -714, -714, -714, -714, -714, 902, 902, + 902, 902, 902, 902, 902, 902, 902, 902, -714, -714, + -714, -714, -714, -714, -714, 900, 900, 900, 900, 900, + 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, + + 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, + 900, -714, -714, -714, -714, -714, -714, -714, -714, -714, + -714, -714, -714, -714, -714, -714, -714, -714, -714, -714, + -714, -714, -714, -714, -714, -714, -714, -714, -714, -714, + -714, -714, -714, -714, -714, -714, -714, -714 + }, + + { + 55, -715, -715, -715, -715, -715, -715, -715, -715, -715, + -715, -715, -715, -715, -715, -715, -715, -715, -715, -715, + -715, -715, -715, -715, -715, -715, -715, -715, -715, -715, + -715, -715, 903, -715, -715, -715, -715, -715, -715, -715, + -715, -715, -715, -715, -715, -715, -715, -715, -715, -715, + + -715, -715, -715, -715, -715, -715, -715, -715, -715, -715, + -715, -715, -715, -715, -715, -715, -715, -715, -715, -715, + -715, -715, -715, -715, -715, -715, -715, -715, -715, -715, + -715, -715, -715, -715, -715, -715, -715, -715, -715, -715, + -715, -715, -715, -715, -715, -715, -715, -715, -715, -715, + -715, -715, -715, -715, -715, -715, -715, -715, -715, -715, + -715, -715, -715, -715, -715, -715, -715, -715, -715, -715, + -715, -715, -715, -715, -715, -715, -715, -715 + }, + + { + 55, -716, -716, -716, -716, -716, -716, -716, -716, -716, + -716, -716, -716, -716, -716, -716, -716, -716, -716, -716, + + -716, -716, -716, -716, -716, -716, -716, -716, -716, -716, + -716, -716, 904, -716, -716, -716, -716, -716, -716, -716, + -716, -716, -716, -716, -716, -716, -716, -716, 905, 905, + 905, 905, 905, 905, 905, 905, 905, 905, -716, -716, + -716, -716, -716, -716, -716, 904, 904, 904, 904, 904, + 904, 904, 904, 904, 904, 904, 904, 904, 904, 904, + 904, 904, 904, 904, 904, 904, 904, 904, 904, 904, + 904, -716, -716, -716, -716, -716, -716, -716, -716, -716, + -716, -716, -716, -716, -716, -716, -716, -716, -716, -716, + -716, -716, -716, -716, -716, -716, -716, -716, -716, -716, + + -716, -716, -716, -716, -716, -716, -716, -716 + }, + + { + 55, -717, -717, -717, -717, -717, -717, -717, -717, -717, + -717, -717, -717, -717, -717, -717, -717, -717, -717, -717, + -717, -717, -717, -717, -717, -717, -717, -717, -717, -717, + -717, -717, 906, -717, -717, -717, -717, -717, -717, -717, + -717, -717, -717, -717, -717, -717, -717, -717, 907, 907, + 907, 907, 907, 907, 907, 907, 907, 907, -717, -717, + -717, -717, -717, -717, -717, 906, 906, 906, 906, 906, + 906, 906, 906, 906, 906, 906, 906, 906, 906, 906, + 906, 906, 906, 906, 906, 906, 906, 906, 906, 906, + + 906, -717, -717, -717, -717, -717, -717, -717, -717, -717, + -717, -717, -717, -717, -717, -717, -717, -717, -717, -717, + -717, -717, -717, -717, -717, -717, -717, -717, -717, -717, + -717, -717, -717, -717, -717, -717, -717, -717 + }, + + { + 55, -718, -718, -718, -718, -718, -718, -718, -718, -718, + -718, -718, -718, -718, -718, -718, -718, -718, -718, -718, + -718, -718, -718, -718, -718, -718, -718, -718, -718, -718, + -718, -718, 906, -718, -718, -718, -718, -718, -718, -718, + -718, -718, -718, -718, -718, -718, -718, -718, 908, 908, + 908, 908, 908, 908, 908, 908, 908, 908, -718, -718, + + -718, -718, -718, -718, -718, 906, 906, 906, 906, 906, + 906, 906, 906, 906, 906, 906, 906, 906, 906, 906, + 906, 906, 906, 906, 906, 906, 906, 906, 906, 906, + 906, -718, -718, -718, -718, -718, -718, -718, -718, -718, + -718, -718, -718, -718, -718, -718, -718, -718, -718, -718, + -718, -718, -718, -718, -718, -718, -718, -718, -718, -718, + -718, -718, -718, -718, -718, -718, -718, -718 + }, + + { + 55, -719, -719, -719, -719, -719, -719, -719, -719, -719, + -719, -719, -719, -719, -719, -719, -719, -719, -719, -719, + -719, -719, -719, -719, -719, -719, -719, -719, -719, -719, + + -719, -719, 909, -719, -719, -719, -719, -719, -719, -719, + -719, -719, -719, -719, -719, -719, -719, -719, -719, -719, + -719, -719, -719, -719, -719, -719, -719, -719, -719, -719, + -719, -719, -719, -719, -719, -719, -719, -719, -719, -719, + -719, -719, -719, -719, -719, -719, -719, -719, -719, -719, + -719, -719, -719, -719, -719, -719, -719, -719, -719, -719, + -719, -719, -719, -719, -719, -719, -719, -719, -719, -719, + -719, -719, -719, -719, -719, -719, -719, -719, -719, -719, + -719, -719, -719, -719, -719, -719, -719, -719, -719, -719, + -719, -719, -719, -719, -719, -719, -719, -719 + + }, + + { + 55, -720, -720, -720, -720, -720, -720, -720, -720, -720, + -720, -720, -720, -720, -720, -720, -720, -720, -720, -720, + -720, -720, -720, -720, -720, -720, -720, -720, -720, -720, + -720, -720, 910, -720, -720, -720, -720, -720, -720, -720, + -720, -720, -720, -720, -720, -720, -720, -720, 911, 911, + 911, 911, 911, 911, 911, 911, 911, 911, -720, -720, + -720, -720, -720, -720, -720, 910, 910, 910, 910, 910, + 910, 910, 910, 910, 910, 910, 910, 910, 910, 910, + 910, 910, 910, 910, 910, 910, 910, 910, 910, 910, + 910, -720, -720, -720, -720, -720, -720, -720, -720, -720, + + -720, -720, -720, -720, -720, -720, -720, -720, -720, -720, + -720, -720, -720, -720, -720, -720, -720, -720, -720, -720, + -720, -720, -720, -720, -720, -720, -720, -720 + }, + + { + 55, -721, -721, -721, -721, -721, -721, -721, -721, -721, + -721, -721, -721, -721, -721, -721, -721, -721, -721, -721, + -721, -721, -721, -721, -721, -721, -721, -721, -721, -721, + -721, -721, 910, -721, -721, -721, -721, -721, -721, -721, + -721, -721, -721, -721, -721, -721, -721, -721, 912, 912, + 912, 912, 912, 912, 912, 912, 912, 912, -721, -721, + -721, -721, -721, -721, -721, 910, 910, 910, 910, 910, + + 910, 910, 910, 910, 910, 910, 910, 910, 910, 910, + 910, 910, 910, 910, 910, 910, 910, 910, 910, 910, + 910, -721, -721, -721, -721, -721, -721, -721, -721, -721, + -721, -721, -721, -721, -721, -721, -721, -721, -721, -721, + -721, -721, -721, -721, -721, -721, -721, -721, -721, -721, + -721, -721, -721, -721, -721, -721, -721, -721 + }, + + { + 55, -722, -722, -722, -722, -722, -722, -722, -722, -722, + -722, -722, -722, -722, -722, -722, -722, -722, -722, -722, + -722, -722, -722, -722, -722, -722, -722, -722, -722, -722, + -722, -722, 913, -722, -722, -722, -722, -722, -722, -722, + + -722, -722, -722, -722, -722, -722, -722, -722, 905, 905, + 905, 905, 905, 905, 905, 905, 905, 905, -722, -722, + -722, -722, -722, -722, -722, 913, 913, 913, 913, 913, + 913, 913, 913, 913, 913, 913, 913, 913, 913, 913, + 913, 913, 913, 913, 913, 913, 913, 913, 913, 913, + 913, -722, -722, -722, -722, -722, -722, -722, -722, -722, + -722, -722, -722, -722, -722, -722, -722, -722, -722, -722, + -722, -722, -722, -722, -722, -722, -722, -722, -722, -722, + -722, -722, -722, -722, -722, -722, -722, -722 + }, + + { + 55, -723, -723, -723, -723, -723, -723, -723, -723, -723, + + -723, -723, -723, -723, -723, -723, -723, -723, -723, -723, + -723, -723, -723, -723, -723, -723, -723, -723, -723, -723, + -723, -723, 914, -723, -723, -723, -723, -723, -723, -723, + -723, -723, -723, -723, -723, -723, -723, -723, -723, -723, + -723, -723, -723, -723, -723, -723, -723, -723, -723, -723, + -723, -723, -723, -723, -723, -723, -723, -723, -723, -723, + -723, -723, -723, -723, -723, -723, -723, -723, -723, -723, + -723, -723, -723, -723, -723, -723, -723, -723, -723, -723, + -723, -723, -723, -723, -723, -723, -723, -723, -723, -723, + -723, -723, -723, -723, -723, -723, -723, -723, -723, -723, + + -723, -723, -723, -723, -723, -723, -723, -723, -723, -723, + -723, -723, -723, -723, -723, -723, -723, -723 + }, + + { + 55, -724, -724, -724, -724, -724, -724, -724, -724, -724, + -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, + -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, + -724, -724, 915, -724, -724, -724, -724, -724, -724, -724, + -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, + -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, + -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, + -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, + + -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, + -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, + -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, + -724, -724, -724, -724, -724, -724, -724, -724, -724, -724, + -724, -724, -724, -724, -724, -724, -724, -724 + }, + + { + 55, -725, -725, -725, -725, -725, -725, -725, -725, -725, + -725, -725, -725, -725, -725, -725, -725, -725, -725, -725, + -725, -725, -725, -725, -725, -725, -725, -725, -725, -725, + -725, -725, 916, -725, -725, -725, -725, -725, -725, -725, + -725, -725, -725, -725, -725, -725, -725, -725, 917, 917, + + 917, 917, 917, 917, 917, 917, 917, 917, -725, -725, + -725, -725, -725, -725, -725, 916, 916, 916, 916, 916, + 916, 916, 916, 916, 916, 916, 916, 916, 916, 916, + 916, 916, 916, 916, 916, 916, 916, 916, 916, 916, + 916, -725, -725, -725, -725, -725, -725, -725, -725, -725, + -725, -725, -725, -725, -725, -725, -725, -725, -725, -725, + -725, -725, -725, -725, -725, -725, -725, -725, -725, -725, + -725, -725, -725, -725, -725, -725, -725, -725 + }, + + { + 55, -726, -726, -726, -726, -726, -726, -726, -726, -726, + -726, -726, -726, -726, -726, -726, -726, -726, -726, -726, + + -726, -726, -726, -726, -726, -726, -726, -726, -726, -726, + -726, -726, 918, -726, -726, -726, -726, -726, -726, -726, + -726, -726, -726, -726, -726, -726, -726, -726, -726, -726, + -726, -726, -726, -726, -726, -726, -726, -726, -726, -726, + -726, -726, -726, -726, -726, -726, -726, -726, -726, -726, + -726, -726, -726, -726, -726, -726, -726, -726, -726, -726, + -726, -726, -726, -726, -726, -726, -726, -726, -726, -726, + -726, -726, -726, -726, -726, -726, -726, -726, -726, -726, + -726, -726, -726, -726, -726, -726, -726, -726, -726, -726, + -726, -726, -726, -726, -726, -726, -726, -726, -726, -726, + + -726, -726, -726, -726, -726, -726, -726, -726 + }, + + { + 55, -727, -727, -727, -727, -727, -727, -727, -727, -727, + -727, -727, -727, -727, -727, -727, -727, -727, -727, -727, + -727, -727, -727, -727, -727, -727, -727, -727, -727, -727, + -727, -727, 919, -727, -727, -727, -727, -727, -727, -727, + -727, -727, -727, -727, -727, -727, -727, -727, 920, 920, + 920, 920, 920, 920, 920, 920, 920, 920, -727, -727, + -727, -727, -727, -727, -727, 919, 919, 919, 919, 919, + 919, 919, 919, 919, 919, 919, 919, 919, 919, 919, + 919, 919, 919, 919, 919, 919, 919, 919, 919, 919, + + 919, -727, -727, -727, -727, -727, -727, -727, -727, -727, + -727, -727, -727, -727, -727, -727, -727, -727, -727, -727, + -727, -727, -727, -727, -727, -727, -727, -727, -727, -727, + -727, -727, -727, -727, -727, -727, -727, -727 + }, + + { + 55, -728, -728, -728, -728, -728, -728, -728, -728, -728, + -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, + -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, + -728, -728, 919, -728, -728, -728, -728, -728, -728, -728, + -728, -728, -728, -728, -728, -728, -728, -728, 921, 921, + 921, 921, 921, 921, 921, 921, 921, 921, -728, -728, + + -728, -728, -728, -728, -728, 919, 919, 919, 919, 919, + 919, 919, 919, 919, 919, 919, 919, 919, 919, 919, + 919, 919, 919, 919, 919, 919, 919, 919, 919, 919, + 919, -728, -728, -728, -728, -728, -728, -728, -728, -728, + -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, + -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, + -728, -728, -728, -728, -728, -728, -728, -728 + }, + + { + 55, -729, -729, -729, -729, -729, -729, -729, -729, -729, + -729, -729, -729, -729, -729, -729, -729, -729, -729, -729, + -729, -729, -729, -729, -729, -729, -729, -729, -729, -729, + + -729, -729, 922, -729, -729, -729, -729, -729, -729, -729, + -729, -729, -729, -729, -729, -729, -729, -729, 905, 905, + 905, 905, 905, 905, 905, 905, 905, 905, -729, -729, + -729, -729, -729, -729, -729, 922, 922, 922, 922, 922, + 922, 922, 922, 922, 922, 922, 922, 922, 922, 922, + 922, 922, 922, 922, 922, 922, 922, 922, 922, 922, + 922, -729, -729, -729, -729, -729, -729, -729, -729, -729, + -729, -729, -729, -729, -729, -729, -729, -729, -729, -729, + -729, -729, -729, -729, -729, -729, -729, -729, -729, -729, + -729, -729, -729, -729, -729, -729, -729, -729 + + }, + + { + 55, -730, -730, -730, -730, -730, -730, -730, -730, -730, + -730, -730, -730, -730, -730, -730, -730, -730, -730, -730, + -730, -730, -730, -730, -730, -730, -730, -730, -730, -730, + -730, -730, 923, -730, -730, -730, -730, -730, -730, -730, + -730, -730, -730, -730, -730, -730, -730, -730, -730, -730, + -730, -730, -730, -730, -730, -730, -730, -730, -730, -730, + -730, -730, -730, -730, -730, -730, -730, -730, -730, -730, + -730, -730, -730, -730, -730, -730, -730, -730, -730, -730, + -730, -730, -730, -730, -730, -730, -730, -730, -730, -730, + -730, -730, -730, -730, -730, -730, -730, -730, -730, -730, + + -730, -730, -730, -730, -730, -730, -730, -730, -730, -730, + -730, -730, -730, -730, -730, -730, -730, -730, -730, -730, + -730, -730, -730, -730, -730, -730, -730, -730 + }, + + { + 55, -731, -731, -731, -731, -731, -731, -731, -731, -731, + -731, -731, -731, -731, -731, -731, -731, -731, -731, -731, + -731, -731, -731, -731, -731, -731, -731, -731, -731, -731, + -731, -731, 924, -731, -731, -731, -731, -731, -731, -731, + -731, -731, -731, -731, -731, -731, -731, -731, -731, -731, + -731, -731, -731, -731, -731, -731, -731, -731, -731, -731, + -731, -731, -731, -731, -731, -731, -731, -731, -731, -731, + + -731, -731, -731, -731, -731, -731, -731, -731, -731, -731, + -731, -731, -731, -731, -731, -731, -731, -731, -731, -731, + -731, -731, -731, -731, -731, -731, -731, -731, -731, -731, + -731, -731, -731, -731, -731, -731, -731, -731, -731, -731, + -731, -731, -731, -731, -731, -731, -731, -731, -731, -731, + -731, -731, -731, -731, -731, -731, -731, -731 + }, + + { + 55, -732, -732, -732, -732, -732, -732, -732, -732, -732, + -732, -732, -732, -732, -732, -732, -732, -732, -732, -732, + -732, -732, -732, -732, -732, -732, -732, -732, -732, -732, + -732, -732, 925, -732, -732, -732, -732, -732, -732, -732, + + -732, -732, -732, -732, -732, -732, -732, -732, 926, 926, + 926, 926, 926, 926, 926, 926, 926, 926, -732, -732, + -732, -732, -732, -732, -732, 925, 925, 925, 925, 925, + 925, 925, 925, 925, 925, 925, 925, 925, 925, 925, + 925, 925, 925, 925, 925, 925, 925, 925, 925, 925, + 925, -732, -732, -732, -732, -732, -732, -732, -732, -732, + -732, -732, -732, -732, -732, -732, -732, -732, -732, -732, + -732, -732, -732, -732, -732, -732, -732, -732, -732, -732, + -732, -732, -732, -732, -732, -732, -732, -732 + }, + + { + 55, -733, -733, -733, -733, -733, -733, -733, -733, -733, + + -733, -733, -733, -733, -733, -733, -733, -733, -733, -733, + -733, -733, -733, -733, -733, -733, -733, -733, -733, -733, + -733, -733, 925, -733, -733, -733, -733, -733, -733, -733, + -733, -733, -733, -733, -733, -733, -733, -733, 927, 927, + 927, 927, 927, 927, 927, 927, 927, 927, -733, -733, + -733, -733, -733, -733, -733, 925, 925, 925, 925, 925, + 925, 925, 925, 925, 925, 925, 925, 925, 925, 925, + 925, 925, 925, 925, 925, 925, 925, 925, 925, 925, + 925, -733, -733, -733, -733, -733, -733, -733, -733, -733, + -733, -733, -733, -733, -733, -733, -733, -733, -733, -733, + + -733, -733, -733, -733, -733, -733, -733, -733, -733, -733, + -733, -733, -733, -733, -733, -733, -733, -733 + }, + + { + 55, -734, -734, -734, -734, -734, -734, -734, -734, -734, + -734, -734, -734, -734, -734, -734, -734, -734, -734, -734, + -734, -734, -734, -734, -734, -734, -734, -734, -734, -734, + -734, -734, 928, -734, -734, -734, -734, -734, -734, -734, + -734, -734, -734, -734, -734, -734, -734, -734, 917, 917, + 917, 917, 917, 917, 917, 917, 917, 917, -734, -734, + -734, -734, -734, -734, -734, 928, 928, 928, 928, 928, + 928, 928, 928, 928, 928, 928, 928, 928, 928, 928, + + 928, 928, 928, 928, 928, 928, 928, 928, 928, 928, + 928, -734, -734, -734, -734, -734, -734, -734, -734, -734, + -734, -734, -734, -734, -734, -734, -734, -734, -734, -734, + -734, -734, -734, -734, -734, -734, -734, -734, -734, -734, + -734, -734, -734, -734, -734, -734, -734, -734 + }, + + { + 55, -735, -735, -735, -735, -735, -735, -735, -735, -735, + -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, + -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, + -735, -735, 929, -735, -735, -735, -735, -735, -735, -735, + -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, + + -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, + -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, + -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, + -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, + -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, + -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, + -735, -735, -735, -735, -735, -735, -735, -735, -735, -735, + -735, -735, -735, -735, -735, -735, -735, -735 + }, + + { + 55, -736, -736, -736, -736, -736, -736, -736, -736, -736, + -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, + + -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, + -736, -736, 930, -736, -736, -736, -736, -736, -736, -736, + -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, + -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, + -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, + -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, + -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, + -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, + -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, + -736, -736, -736, -736, -736, -736, -736, -736, -736, -736, + + -736, -736, -736, -736, -736, -736, -736, -736 + }, + + { + 55, -737, -737, -737, -737, -737, -737, -737, -737, -737, + -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, + -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, + -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, + -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, + -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, + -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, + -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, + -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, + + -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, + -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, + -737, -737, -737, -737, -737, -737, -737, -737, -737, -737, + -737, -737, -737, -737, -737, -737, -737, -737 + }, + + { + 55, 738, 738, 738, 738, 738, 738, 738, 738, 738, + 739, 738, 738, 738, 738, 738, 738, 738, 738, 738, + 738, 738, 738, 738, 738, 738, 738, 738, 738, 738, + 738, 738, 740, 738, 738, 738, 738, 738, 738, 738, + 738, 738, 738, 738, 738, 738, 738, 741, 738, 738, + 738, 738, 738, 738, 738, 738, 738, 738, 738, 738, + + 738, 738, 738, 738, 738, 738, 738, 738, 738, 738, + 738, 738, 738, 738, 738, 738, 738, 738, 738, 738, + 738, 738, 738, 738, 738, 738, 738, 738, 738, 738, + 738, 738, 738, 738, 738, 738, 738, 738, 738, 738, + 738, 738, 738, 738, 738, 738, 738, 738, 738, 738, + 738, 738, 738, 738, 738, 738, 738, 738, 738, 738, + 738, 738, 738, 738, 738, 738, 738, 738 + }, + + { + 55, -739, -739, -739, -739, -739, -739, -739, -739, -739, + -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, + -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, + + -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, + -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, + -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, + -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, + -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, + -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, + -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, + -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, + -739, -739, -739, -739, -739, -739, -739, -739, -739, -739, + -739, -739, -739, -739, -739, -739, -739, -739 + + }, + + { + 55, 738, 738, 738, 738, 738, 738, 738, 738, 738, + 739, 738, 738, 738, 738, 738, 738, 738, 738, 738, + 738, 738, 738, 738, 738, 738, 738, 738, 738, 738, + 738, 738, 740, 738, 738, 738, 738, 738, 738, 738, + 738, 738, 738, 738, 738, 738, 738, 741, 738, 738, + 738, 738, 738, 738, 738, 738, 738, 738, 738, 738, + 738, 738, 738, 738, 738, 738, 738, 738, 738, 738, + 738, 738, 738, 738, 738, 738, 738, 738, 738, 738, + 738, 738, 738, 738, 738, 738, 738, 738, 738, 738, + 738, 738, 738, 738, 738, 738, 738, 738, 738, 738, + + 738, 738, 738, 738, 738, 738, 738, 738, 738, 738, + 738, 738, 738, 738, 738, 738, 738, 738, 738, 738, + 738, 738, 738, 738, 738, 738, 738, 738 + }, + + { + 55, 738, 738, 738, 738, 738, 738, 738, 738, 738, + 739, 738, 738, 738, 738, 738, 738, 738, 738, 738, + 738, 738, 738, 738, 738, 738, 738, 738, 738, 738, + 738, 738, 740, 738, 738, 738, 738, 738, 738, 738, + 738, 738, 738, 738, 738, 738, 738, 741, 738, 738, + 738, 738, 738, 738, 738, 738, 738, 738, 738, 738, + 738, 738, 738, 738, 738, 738, 738, 738, 738, 738, + + 738, 738, 738, 738, 738, 738, 738, 738, 738, 738, + 738, 738, 738, 738, 738, 738, 738, 738, 738, 738, + 738, 738, 738, 738, 738, 738, 738, 738, 738, 738, + 738, 738, 738, 738, 738, 738, 738, 738, 738, 738, + 738, 738, 738, 738, 738, 738, 738, 738, 738, 738, + 738, 738, 738, 738, 738, 738, 738, 738 + }, + + { + 55, -742, -742, -742, -742, -742, -742, -742, -742, -742, + -742, -742, -742, -742, -742, -742, -742, -742, -742, -742, + -742, -742, -742, -742, -742, -742, -742, -742, -742, -742, + -742, -742, -742, -742, -742, -742, -742, -742, -742, -742, + + -742, -742, -742, -742, -742, -742, -742, -742, -742, -742, + -742, -742, -742, -742, -742, -742, -742, -742, -742, -742, + -742, -742, -742, -742, -742, -742, -742, -742, -742, 931, + -742, -742, -742, -742, -742, -742, -742, -742, -742, -742, + -742, -742, -742, -742, -742, -742, -742, -742, -742, -742, + -742, -742, -742, -742, -742, -742, -742, -742, -742, -742, + -742, -742, -742, -742, -742, -742, -742, -742, -742, -742, + -742, -742, -742, -742, -742, -742, -742, -742, -742, -742, + -742, -742, -742, -742, -742, -742, -742, -742 + }, + + { + 55, -743, -743, -743, -743, -743, -743, -743, -743, -743, + + -743, -743, -743, -743, -743, -743, -743, -743, -743, -743, + -743, -743, -743, -743, -743, -743, -743, -743, -743, -743, + -743, -743, 932, -743, -743, -743, -743, -743, -743, -743, + -743, -743, -743, -743, -743, -743, -743, -743, -743, -743, + -743, -743, -743, -743, -743, -743, -743, -743, -743, -743, + -743, -743, -743, -743, -743, -743, -743, -743, -743, -743, + -743, -743, -743, -743, -743, -743, -743, -743, -743, -743, + -743, -743, -743, -743, -743, -743, -743, -743, -743, -743, + -743, -743, -743, -743, -743, -743, -743, -743, -743, -743, + -743, -743, -743, -743, -743, -743, -743, -743, -743, -743, + + -743, -743, -743, -743, -743, -743, -743, -743, -743, -743, + -743, -743, -743, -743, -743, -743, -743, -743 + }, + + { + 55, -744, -744, -744, -744, -744, -744, -744, -744, -744, + -744, -744, -744, -744, -744, -744, -744, -744, -744, -744, + -744, -744, -744, -744, -744, -744, -744, -744, -744, -744, + -744, -744, -744, -744, -744, -744, -744, -744, -744, -744, + -744, -744, -744, -744, -744, -744, -744, -744, -744, -744, + -744, -744, -744, -744, -744, -744, -744, -744, -744, -744, + -744, -744, -744, -744, -744, -744, -744, -744, -744, -744, + -744, -744, -744, -744, -744, -744, -744, -744, -744, -744, + + -744, -744, 933, -744, -744, -744, -744, -744, -744, -744, + -744, -744, -744, -744, -744, -744, -744, -744, -744, -744, + -744, -744, -744, -744, -744, -744, -744, -744, -744, -744, + -744, -744, -744, -744, -744, -744, -744, -744, -744, -744, + -744, -744, -744, -744, -744, -744, -744, -744 + }, + + { + 55, -745, -745, -745, -745, -745, -745, -745, -745, -745, + -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, + -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, + -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, + -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, + + -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, + -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, + -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, + -745, -745, -745, -745, -745, 934, -745, -745, -745, -745, + -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, + -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, + -745, -745, -745, -745, -745, -745, -745, -745, -745, -745, + -745, -745, -745, -745, -745, -745, -745, -745 + }, + + { + 55, -746, -746, -746, -746, -746, -746, -746, -746, -746, + -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, + + -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, + -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, + -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, + -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, + -746, -746, -746, -746, -746, -746, 935, -746, -746, -746, + -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, + -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, + -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, + -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, + -746, -746, -746, -746, -746, -746, -746, -746, -746, -746, + + -746, -746, -746, -746, -746, -746, -746, -746 + }, + + { + 55, -747, -747, -747, -747, -747, -747, -747, -747, -747, + -747, -747, -747, -747, -747, -747, -747, -747, -747, -747, + -747, -747, -747, -747, -747, -747, -747, -747, -747, -747, + -747, -747, 936, -747, -747, -747, -747, -747, -747, -747, + -747, -747, -747, -747, -747, -747, -747, -747, -747, -747, + -747, -747, -747, -747, -747, -747, -747, -747, -747, -747, + -747, -747, -747, -747, -747, -747, -747, -747, -747, -747, + -747, -747, -747, -747, -747, -747, -747, -747, -747, -747, + -747, -747, -747, -747, -747, -747, -747, -747, -747, -747, + + -747, -747, -747, -747, -747, -747, -747, -747, -747, -747, + -747, -747, -747, -747, -747, -747, -747, -747, -747, -747, + -747, -747, -747, -747, -747, -747, -747, -747, -747, -747, + -747, -747, -747, -747, -747, -747, -747, -747 + }, + + { + 55, -748, -748, -748, -748, -748, -748, -748, -748, -748, + -748, -748, -748, -748, -748, -748, -748, -748, -748, -748, + -748, -748, -748, -748, -748, -748, -748, -748, -748, -748, + -748, -748, -748, -748, -748, -748, -748, -748, -748, -748, + -748, -748, -748, -748, -748, -748, -748, -748, -748, -748, + -748, -748, -748, -748, -748, -748, -748, -748, -748, -748, + + -748, -748, -748, -748, -748, -748, 937, -748, -748, -748, + -748, -748, -748, -748, -748, -748, -748, -748, -748, -748, + -748, -748, -748, -748, -748, -748, -748, -748, -748, -748, + -748, -748, -748, -748, -748, -748, -748, -748, -748, -748, + -748, -748, -748, -748, -748, -748, -748, -748, -748, -748, + -748, -748, -748, -748, -748, -748, -748, -748, -748, -748, + -748, -748, -748, -748, -748, -748, -748, -748 + }, + + { + 55, -749, -749, -749, -749, -749, -749, -749, -749, -749, + -749, -749, -749, -749, -749, -749, -749, -749, -749, -749, + -749, -749, -749, -749, -749, -749, -749, -749, -749, -749, + + -749, -749, -749, -749, -749, -749, -749, -749, -749, -749, + -749, -749, -749, -749, -749, -749, -749, -749, -749, -749, + -749, -749, -749, -749, -749, -749, -749, -749, -749, -749, + -749, -749, -749, -749, -749, -749, 938, -749, -749, -749, + -749, -749, -749, -749, -749, -749, -749, -749, -749, -749, + -749, -749, -749, -749, -749, -749, -749, -749, -749, -749, + -749, -749, -749, -749, -749, -749, -749, -749, -749, -749, + -749, -749, -749, -749, -749, -749, -749, -749, -749, -749, + -749, -749, -749, -749, -749, -749, -749, -749, -749, -749, + -749, -749, -749, -749, -749, -749, -749, -749 + + }, + + { + 55, -750, -750, -750, -750, -750, -750, -750, -750, -750, + -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, + -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, + -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, + -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, + -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, + -750, -750, -750, -750, -750, -750, -750, -750, -750, 939, + -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, + -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, + -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, + + -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, + -750, -750, -750, -750, -750, -750, -750, -750, -750, -750, + -750, -750, -750, -750, -750, -750, -750, -750 + }, + + { + 55, -751, -751, -751, -751, -751, -751, -751, -751, -751, + -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, + -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, + -751, -751, 940, -751, -751, -751, -751, -751, -751, -751, + -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, + -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, + -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, + + -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, + -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, + -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, + -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, + -751, -751, -751, -751, -751, -751, -751, -751, -751, -751, + -751, -751, -751, -751, -751, -751, -751, -751 + }, + + { + 55, -752, -752, -752, -752, -752, -752, -752, -752, -752, + -752, -752, -752, -752, -752, -752, -752, -752, -752, -752, + -752, -752, -752, -752, -752, -752, -752, -752, -752, -752, + -752, -752, -752, -752, -752, -752, -752, -752, -752, -752, + + -752, -752, -752, -752, -752, -752, -752, -752, -752, -752, + -752, -752, -752, -752, -752, -752, -752, -752, -752, -752, + -752, -752, -752, -752, -752, -752, -752, -752, -752, -752, + -752, -752, -752, -752, -752, -752, -752, -752, -752, -752, + -752, -752, 941, -752, -752, -752, -752, -752, -752, -752, + -752, -752, -752, -752, -752, -752, -752, -752, -752, -752, + -752, -752, -752, -752, -752, -752, -752, -752, -752, -752, + -752, -752, -752, -752, -752, -752, -752, -752, -752, -752, + -752, -752, -752, -752, -752, -752, -752, -752 + }, + + { + 55, -753, -753, -753, -753, -753, -753, -753, -753, -753, + + -753, -753, -753, -753, -753, -753, -753, -753, -753, -753, + -753, -753, -753, -753, -753, -753, -753, -753, -753, -753, + -753, -753, -753, -753, -753, -753, -753, -753, -753, -753, + -753, -753, -753, -753, -753, -753, -753, -753, -753, -753, + -753, -753, -753, -753, -753, -753, -753, -753, -753, -753, + -753, -753, -753, -753, -753, -753, -753, -753, -753, -753, + -753, -753, -753, -753, -753, -753, -753, -753, -753, -753, + -753, -753, -753, -753, -753, 942, -753, -753, -753, -753, + -753, -753, -753, -753, -753, -753, -753, -753, -753, -753, + -753, -753, -753, -753, -753, -753, -753, -753, -753, -753, + + -753, -753, -753, -753, -753, -753, -753, -753, -753, -753, + -753, -753, -753, -753, -753, -753, -753, -753 + }, + + { + 55, -754, -754, -754, -754, -754, -754, -754, -754, -754, + -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, + -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, + -754, -754, 943, -754, -754, -754, -754, -754, -754, -754, + -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, + -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, + -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, + -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, + + -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, + -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, + -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, + -754, -754, -754, -754, -754, -754, -754, -754, -754, -754, + -754, -754, -754, -754, -754, -754, -754, -754 + }, + + { + 55, -755, -755, -755, -755, -755, -755, -755, -755, -755, + -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, + -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, + -755, -755, 944, -755, -755, -755, -755, -755, -755, -755, + -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, + + -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, + -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, + -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, + -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, + -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, + -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, + -755, -755, -755, -755, -755, -755, -755, -755, -755, -755, + -755, -755, -755, -755, -755, -755, -755, -755 + }, + + { + 55, -756, -756, -756, -756, -756, -756, -756, -756, -756, + -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, + + -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, + -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, + -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, + -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, + -756, -756, -756, -756, -756, -756, 945, -756, -756, -756, + -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, + -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, + -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, + -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, + -756, -756, -756, -756, -756, -756, -756, -756, -756, -756, + + -756, -756, -756, -756, -756, -756, -756, -756 + }, + + { + 55, -757, -757, -757, -757, -757, -757, -757, -757, -757, + -757, -757, -757, -757, -757, -757, -757, -757, -757, -757, + -757, -757, -757, -757, -757, -757, -757, -757, -757, -757, + -757, -757, -757, -757, -757, -757, -757, -757, -757, -757, + -757, -757, -757, -757, -757, -757, -757, -757, -757, -757, + -757, -757, -757, -757, -757, -757, -757, -757, -757, -757, + -757, -757, -757, -757, -757, -757, 946, -757, -757, -757, + -757, -757, -757, -757, -757, -757, -757, -757, -757, -757, + -757, -757, -757, -757, -757, -757, -757, -757, -757, -757, + + -757, -757, -757, -757, -757, -757, -757, -757, -757, -757, + -757, -757, -757, -757, -757, -757, -757, -757, -757, -757, + -757, -757, -757, -757, -757, -757, -757, -757, -757, -757, + -757, -757, -757, -757, -757, -757, -757, -757 + }, + + { + 55, -758, -758, -758, -758, -758, -758, -758, -758, -758, + -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, + -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, + -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, + -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, + -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, + + -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, + -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, + -758, -758, -758, -758, -758, 947, -758, -758, -758, -758, + -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, + -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, + -758, -758, -758, -758, -758, -758, -758, -758, -758, -758, + -758, -758, -758, -758, -758, -758, -758, -758 + }, + + { + 55, -759, -759, -759, -759, -759, -759, -759, -759, -759, + -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, + -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, + + -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, + -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, + -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, + -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, + -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, + -759, -759, -759, -759, -759, -759, 948, -759, -759, -759, + -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, + -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, + -759, -759, -759, -759, -759, -759, -759, -759, -759, -759, + -759, -759, -759, -759, -759, -759, -759, -759 + + }, + + { + 55, -760, -760, -760, -760, -760, -760, -760, -760, -760, + -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, + -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, + -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, + -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, + -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, + -760, -760, -760, -760, -760, -760, -760, -760, -760, 949, + -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, + -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, + -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, + + -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, + -760, -760, -760, -760, -760, -760, -760, -760, -760, -760, + -760, -760, -760, -760, -760, -760, -760, -760 + }, + + { + 55, -761, -761, -761, -761, -761, -761, -761, -761, -761, + -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, + -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, + -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, + -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, + -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, + -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, + + -761, -761, -761, -761, -761, -761, -761, -761, 950, -761, + -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, + -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, + -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, + -761, -761, -761, -761, -761, -761, -761, -761, -761, -761, + -761, -761, -761, -761, -761, -761, -761, -761 + }, + + { + 55, -762, -762, -762, -762, -762, -762, -762, -762, -762, + -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, + -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, + -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, + + -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, + -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, + -762, -762, -762, -762, -762, -762, 951, -762, -762, -762, + -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, + -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, + -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, + -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, + -762, -762, -762, -762, -762, -762, -762, -762, -762, -762, + -762, -762, -762, -762, -762, -762, -762, -762 + }, + + { + 55, -763, -763, -763, -763, -763, -763, -763, -763, -763, + + -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, + -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, + -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, + -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, + -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, + -763, -763, -763, -763, -763, -763, -763, -763, -763, 952, + -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, + -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, + -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, + -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, + + -763, -763, -763, -763, -763, -763, -763, -763, -763, -763, + -763, -763, -763, -763, -763, -763, -763, -763 + }, + + { + 55, -764, -764, -764, -764, -764, -764, -764, -764, -764, + -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, + -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, + -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, + -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, + -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, + -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, + 953, -764, -764, -764, -764, -764, -764, -764, -764, -764, + + -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, + -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, + -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, + -764, -764, -764, -764, -764, -764, -764, -764, -764, -764, + -764, -764, -764, -764, -764, -764, -764, -764 + }, + + { + 55, -765, -765, -765, -765, -765, -765, -765, -765, -765, + -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, + -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, + -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, + -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, + + -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, + -765, -765, -765, -765, -765, -765, 954, -765, -765, -765, + -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, + -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, + -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, + -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, + -765, -765, -765, -765, -765, -765, -765, -765, -765, -765, + -765, -765, -765, -765, -765, -765, -765, -765 + }, + + { + 55, -766, -766, -766, -766, -766, -766, -766, -766, -766, + -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, + + -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, + -766, -766, 955, -766, -766, -766, -766, -766, -766, -766, + -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, + -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, + -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, + -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, + -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, + -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, + -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, + -766, -766, -766, -766, -766, -766, -766, -766, -766, -766, + + -766, -766, -766, -766, -766, -766, -766, -766 + }, + + { + 55, -767, -767, -767, -767, -767, -767, -767, -767, -767, + -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, + -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, + -767, -767, 956, -767, -767, -767, -767, -767, -767, -767, + -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, + -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, + -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, + -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, + -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, + + -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, + -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, + -767, -767, -767, -767, -767, -767, -767, -767, -767, -767, + -767, -767, -767, -767, -767, -767, -767, -767 + }, + + { + 55, -768, -768, -768, -768, -768, -768, -768, -768, -768, + -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, + -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, + -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, + -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, + -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, + + -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, + -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, + -768, -768, -768, -768, -768, -768, -768, -768, 957, -768, + -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, + -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, + -768, -768, -768, -768, -768, -768, -768, -768, -768, -768, + -768, -768, -768, -768, -768, -768, -768, -768 + }, + + { + 55, -769, -769, -769, -769, -769, -769, -769, -769, -769, + -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, + -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, + + -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, + -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, + -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, + -769, -769, -769, -769, -769, -769, 958, -769, -769, -769, + -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, + -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, + -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, + -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, + -769, -769, -769, -769, -769, -769, -769, -769, -769, -769, + -769, -769, -769, -769, -769, -769, -769, -769 + + }, + + { + 55, -770, -770, -770, -770, -770, -770, -770, -770, -770, + -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, + -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, + -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, + -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, + -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, + -770, -770, -770, -770, -770, -770, 959, -770, -770, -770, + -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, + -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, + -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, + + -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, + -770, -770, -770, -770, -770, -770, -770, -770, -770, -770, + -770, -770, -770, -770, -770, -770, -770, -770 + }, + + { + 55, -771, -771, -771, -771, -771, -771, -771, -771, -771, + -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, + -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, + -771, -771, 960, -771, -771, -771, -771, -771, -771, -771, + -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, + -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, + -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, + + 961, -771, -771, 962, -771, -771, -771, -771, -771, -771, + -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, + -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, + -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, + -771, -771, -771, -771, -771, -771, -771, -771, -771, -771, + -771, -771, -771, -771, -771, -771, -771, -771 + }, + + { + 55, -772, -772, -772, -772, -772, -772, -772, -772, -772, + -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, + -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, + -772, -772, 963, -772, -772, -772, -772, -772, -772, -772, + + -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, + -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, + -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, + -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, + -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, + -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, + -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, + -772, -772, -772, -772, -772, -772, -772, -772, -772, -772, + -772, -772, -772, -772, -772, -772, -772, -772 + }, + + { + 55, -773, -773, -773, -773, -773, -773, -773, -773, -773, + + -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, + -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, + -773, -773, 964, -773, -773, -773, -773, -773, -773, -773, + -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, + -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, + -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, + -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, + -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, + -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, + -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, + + -773, -773, -773, -773, -773, -773, -773, -773, -773, -773, + -773, -773, -773, -773, -773, -773, -773, -773 + }, + + { + 55, -774, -774, -774, -774, -774, -774, -774, -774, -774, + -774, -774, -774, -774, -774, -774, -774, -774, -774, -774, + -774, -774, -774, -774, -774, -774, -774, -774, -774, -774, + -774, -774, 965, -774, -774, -774, -774, -774, -774, -774, + -774, -774, -774, -774, -774, -774, -774, -774, -774, -774, + -774, -774, -774, -774, -774, -774, -774, -774, -774, -774, + -774, -774, -774, -774, -774, -774, -774, -774, -774, -774, + -774, -774, -774, -774, -774, -774, -774, -774, -774, -774, + + -774, -774, -774, -774, -774, -774, -774, -774, -774, -774, + -774, -774, -774, -774, -774, -774, -774, -774, -774, -774, + -774, -774, -774, -774, -774, -774, -774, -774, -774, -774, + -774, -774, -774, -774, -774, -774, -774, -774, -774, -774, + -774, -774, -774, -774, -774, -774, -774, -774 + }, + + { + 55, -775, -775, -775, -775, -775, -775, -775, -775, -775, + -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, + -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, + -775, -775, 966, -775, -775, -775, -775, -775, -775, -775, + -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, + + -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, + -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, + -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, + -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, + -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, + -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, + -775, -775, -775, -775, -775, -775, -775, -775, -775, -775, + -775, -775, -775, -775, -775, -775, -775, -775 + }, + + { + 55, -776, -776, -776, -776, -776, -776, -776, -776, -776, + -776, -776, -776, -776, -776, -776, -776, -776, -776, -776, + + -776, -776, -776, -776, -776, -776, -776, -776, -776, -776, + -776, -776, -776, -776, -776, -776, -776, -776, -776, -776, + -776, -776, -776, -776, -776, -776, -776, -776, -776, -776, + -776, -776, -776, -776, -776, -776, -776, -776, -776, -776, + -776, -776, -776, -776, -776, -776, -776, -776, -776, 967, + -776, -776, -776, -776, -776, -776, -776, -776, -776, -776, + -776, -776, -776, -776, -776, -776, -776, -776, -776, -776, + -776, -776, -776, -776, -776, -776, -776, -776, -776, -776, + -776, -776, -776, -776, -776, -776, -776, -776, -776, -776, + -776, -776, -776, -776, -776, -776, -776, -776, -776, -776, + + -776, -776, -776, -776, -776, -776, -776, -776 + }, + + { + 55, -777, -777, -777, -777, -777, -777, -777, -777, -777, + -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, + -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, + -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, + -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, + -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, + -777, -777, -777, -777, -777, -777, -777, -777, -777, 968, + -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, + -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, + + -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, + -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, + -777, -777, -777, -777, -777, -777, -777, -777, -777, -777, + -777, -777, -777, -777, -777, -777, -777, -777 + }, + + { + 55, -778, -778, -778, -778, -778, -778, -778, -778, -778, + -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, + -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, + -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, + -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, + -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, + + -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, + -778, 969, -778, -778, -778, -778, -778, -778, -778, -778, + -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, + -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, + -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, + -778, -778, -778, -778, -778, -778, -778, -778, -778, -778, + -778, -778, -778, -778, -778, -778, -778, -778 + }, + + { + 55, -779, -779, -779, -779, -779, -779, -779, -779, -779, + -779, -779, -779, -779, -779, -779, -779, -779, -779, -779, + -779, -779, -779, -779, -779, -779, -779, -779, -779, -779, + + -779, -779, -779, -779, -779, -779, -779, -779, -779, -779, + -779, -779, -779, -779, -779, -779, -779, -779, -779, -779, + -779, -779, -779, -779, -779, -779, -779, -779, -779, -779, + -779, -779, -779, -779, -779, -779, -779, -779, -779, -779, + -779, 970, -779, -779, -779, -779, -779, -779, -779, -779, + -779, -779, -779, -779, -779, -779, -779, -779, -779, -779, + -779, -779, -779, -779, -779, -779, -779, -779, -779, -779, + -779, -779, -779, -779, -779, -779, -779, -779, -779, -779, + -779, -779, -779, -779, -779, -779, -779, -779, -779, -779, + -779, -779, -779, -779, -779, -779, -779, -779 + + }, + + { + 55, -780, -780, -780, -780, -780, -780, -780, -780, -780, + -780, -780, -780, -780, -780, -780, -780, -780, -780, -780, + -780, -780, -780, -780, -780, -780, -780, -780, -780, -780, + -780, -780, -780, -780, -780, -780, -780, -780, -780, -780, + -780, -780, -780, -780, -780, -780, -780, -780, -780, -780, + -780, -780, -780, -780, -780, -780, -780, -780, -780, -780, + -780, -780, -780, -780, -780, -780, -780, -780, 971, -780, + -780, -780, -780, -780, -780, -780, -780, -780, -780, -780, + -780, -780, -780, -780, -780, -780, -780, -780, -780, -780, + -780, -780, -780, -780, -780, -780, -780, -780, -780, -780, + + -780, -780, -780, -780, -780, -780, -780, -780, -780, -780, + -780, -780, -780, -780, -780, -780, -780, -780, -780, -780, + -780, -780, -780, -780, -780, -780, -780, -780 + }, + + { + 55, -781, -781, -781, -781, -781, -781, -781, -781, -781, + -781, -781, -781, -781, -781, -781, -781, -781, -781, -781, + -781, -781, -781, -781, -781, -781, -781, -781, -781, -781, + -781, -781, -781, -781, -781, -781, -781, -781, -781, -781, + -781, -781, -781, -781, -781, -781, -781, -781, -781, -781, + -781, -781, -781, -781, -781, -781, -781, -781, -781, -781, + -781, -781, -781, -781, -781, -781, -781, -781, -781, -781, + + -781, -781, -781, -781, -781, -781, -781, -781, -781, -781, + -781, -781, -781, 972, -781, -781, -781, -781, -781, -781, + -781, -781, -781, -781, -781, -781, -781, -781, -781, -781, + -781, -781, -781, -781, -781, -781, -781, -781, -781, -781, + -781, -781, -781, -781, -781, -781, -781, -781, -781, -781, + -781, -781, -781, -781, -781, -781, -781, -781 + }, + + { + 55, -782, -782, -782, -782, -782, -782, -782, -782, -782, + -782, -782, -782, -782, -782, -782, -782, -782, -782, -782, + -782, -782, -782, -782, -782, -782, -782, -782, -782, -782, + -782, -782, -782, -782, -782, -782, -782, -782, -782, -782, + + -782, -782, -782, -782, -782, -782, -782, -782, -782, -782, + -782, -782, -782, -782, -782, -782, -782, -782, -782, -782, + -782, -782, -782, -782, -782, -782, -782, -782, -782, -782, + 973, -782, -782, -782, -782, -782, -782, -782, -782, -782, + -782, -782, -782, -782, -782, -782, -782, -782, -782, -782, + -782, -782, -782, -782, -782, -782, -782, -782, -782, -782, + -782, -782, -782, -782, -782, -782, -782, -782, -782, -782, + -782, -782, -782, -782, -782, -782, -782, -782, -782, -782, + -782, -782, -782, -782, -782, -782, -782, -782 + }, + + { + 55, -783, -783, -783, -783, -783, -783, -783, -783, -783, + + -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, + -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, + -783, -783, 974, -783, -783, -783, -783, -783, -783, -783, + -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, + -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, + -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, + 975, -783, -783, 976, -783, -783, -783, -783, -783, -783, + -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, + -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, + -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, + + -783, -783, -783, -783, -783, -783, -783, -783, -783, -783, + -783, -783, -783, -783, -783, -783, -783, -783 + }, + + { + 55, -784, -784, -784, -784, -784, -784, -784, -784, -784, + -784, -784, -784, -784, -784, -784, -784, -784, -784, -784, + -784, -784, -784, -784, -784, -784, -784, -784, -784, -784, + -784, -784, 977, -784, -784, -784, -784, -784, -784, -784, + -784, -784, -784, -784, -784, -784, -784, -784, -784, -784, + -784, -784, -784, -784, -784, -784, -784, -784, -784, -784, + -784, -784, -784, -784, -784, -784, -784, -784, -784, -784, + -784, -784, -784, -784, -784, -784, -784, -784, -784, -784, + + -784, -784, -784, -784, -784, -784, -784, -784, -784, -784, + -784, -784, -784, -784, -784, -784, -784, -784, -784, -784, + -784, -784, -784, -784, -784, -784, -784, -784, -784, -784, + -784, -784, -784, -784, -784, -784, -784, -784, -784, -784, + -784, -784, -784, -784, -784, -784, -784, -784 + }, + + { + 55, -785, -785, -785, -785, -785, -785, -785, -785, -785, + -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, + -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, + -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, + -785, -785, -785, -785, -785, 978, -785, -785, -785, -785, + + -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, + -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, + -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, + -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, + -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, + -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, + -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, + -785, -785, -785, -785, -785, -785, -785, -785 + }, + + { + 55, -786, -786, -786, -786, -786, -786, -786, -786, -786, + -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, + + -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, + -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, + -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, + -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, + -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, + -786, -786, -786, 979, -786, -786, -786, -786, -786, -786, + -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, + -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, + -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, + -786, -786, -786, -786, -786, -786, -786, -786, -786, -786, + + -786, -786, -786, -786, -786, -786, -786, -786 + }, + + { + 55, -787, -787, -787, -787, -787, -787, -787, -787, -787, + -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, + -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, + -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, + -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, + -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, + -787, -787, -787, -787, -787, -787, -787, -787, 980, -787, + -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, + -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, + + -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, + -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, + -787, -787, -787, -787, -787, -787, -787, -787, -787, -787, + -787, -787, -787, -787, -787, -787, -787, -787 + }, + + { + 55, -788, -788, -788, -788, -788, -788, -788, -788, -788, + -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, + -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, + -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, + -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, + -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, + + -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, + -788, -788, -788, -788, -788, -788, -788, 981, -788, -788, + -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, + -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, + -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, + -788, -788, -788, -788, -788, -788, -788, -788, -788, -788, + -788, -788, -788, -788, -788, -788, -788, -788 + }, + + { + 55, -789, -789, -789, -789, -789, -789, -789, -789, -789, + -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, + -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, + + -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, + -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, + -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, + -789, -789, -789, -789, -789, -789, -789, -789, 982, -789, + -789, -789, -789, -789, -789, -789, -789, 983, -789, -789, + -789, -789, -789, 984, -789, -789, -789, -789, -789, -789, + -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, + -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, + -789, -789, -789, -789, -789, -789, -789, -789, -789, -789, + -789, -789, -789, -789, -789, -789, -789, -789 + + }, + + { + 55, -790, -790, -790, -790, -790, -790, -790, -790, -790, + -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, + -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, + -790, -790, 985, -790, -790, -790, -790, -790, -790, -790, + -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, + -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, + -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, + -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, + -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, + -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, + + -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, + -790, -790, -790, -790, -790, -790, -790, -790, -790, -790, + -790, -790, -790, -790, -790, -790, -790, -790 + }, + + { + 55, -791, -791, -791, -791, -791, -791, -791, -791, -791, + -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, + -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, + -791, -791, 986, -791, -791, -791, -791, -791, -791, -791, + -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, + -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, + -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, + + -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, + -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, + -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, + -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, + -791, -791, -791, -791, -791, -791, -791, -791, -791, -791, + -791, -791, -791, -791, -791, -791, -791, -791 + }, + + { + 55, -792, -792, -792, -792, -792, -792, -792, -792, -792, + -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, + -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, + -792, -792, 987, -792, -792, -792, -792, -792, -792, -792, + + -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, + -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, + -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, + -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, + -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, + -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, + -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, + -792, -792, -792, -792, -792, -792, -792, -792, -792, -792, + -792, -792, -792, -792, -792, -792, -792, -792 + }, + + { + 55, -793, -793, -793, -793, -793, -793, -793, -793, -793, + + -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, + -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, + -793, -793, 988, -793, -793, -793, -793, -793, -793, -793, + -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, + -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, + -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, + -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, + -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, + -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, + -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, + + -793, -793, -793, -793, -793, -793, -793, -793, -793, -793, + -793, -793, -793, -793, -793, -793, -793, -793 + }, + + { + 55, -794, -794, -794, -794, -794, -794, -794, -794, -794, + -794, -794, -794, -794, -794, -794, -794, -794, -794, -794, + -794, -794, -794, -794, -794, -794, -794, -794, -794, -794, + -794, -794, 989, -794, -794, -794, -794, -794, -794, -794, + -794, -794, -794, -794, -794, -794, -794, -794, -794, -794, + -794, -794, -794, -794, -794, -794, -794, -794, -794, -794, + -794, -794, -794, -794, -794, -794, -794, -794, -794, -794, + -794, -794, -794, -794, -794, -794, -794, -794, -794, -794, + + -794, -794, -794, -794, -794, -794, -794, -794, -794, -794, + -794, -794, -794, -794, -794, -794, -794, -794, -794, -794, + -794, -794, -794, -794, -794, -794, -794, -794, -794, -794, + -794, -794, -794, -794, -794, -794, -794, -794, -794, -794, + -794, -794, -794, -794, -794, -794, -794, -794 + }, + + { + 55, -795, -795, -795, -795, -795, -795, -795, -795, -795, + -795, -795, -795, -795, -795, -795, -795, -795, -795, -795, + -795, -795, -795, -795, -795, -795, -795, -795, -795, -795, + -795, -795, -795, -795, -795, -795, -795, -795, -795, -795, + -795, -795, -795, -795, -795, -795, -795, -795, -795, -795, + + -795, -795, -795, -795, -795, -795, -795, -795, -795, -795, + -795, -795, -795, -795, -795, -795, -795, -795, -795, -795, + -795, -795, -795, -795, -795, -795, -795, -795, -795, -795, + -795, -795, -795, -795, -795, -795, -795, -795, -795, 990, + -795, -795, -795, -795, -795, -795, -795, -795, -795, -795, + -795, -795, -795, -795, -795, -795, -795, -795, -795, -795, + -795, -795, -795, -795, -795, -795, -795, -795, -795, -795, + -795, -795, -795, -795, -795, -795, -795, -795 + }, + + { + 55, -796, -796, -796, -796, -796, -796, -796, -796, -796, + -796, -796, -796, -796, -796, -796, -796, -796, -796, -796, + + -796, -796, -796, -796, -796, -796, -796, -796, -796, -796, + -796, -796, -796, -796, -796, -796, -796, -796, -796, -796, + -796, -796, -796, -796, -796, -796, -796, -796, -796, -796, + -796, -796, -796, -796, -796, -796, -796, -796, -796, -796, + -796, -796, -796, -796, -796, -796, -796, -796, -796, -796, + -796, -796, -796, -796, -796, -796, -796, -796, -796, -796, + -796, -796, -796, 991, -796, -796, -796, -796, -796, -796, + -796, -796, -796, -796, -796, -796, -796, -796, -796, -796, + -796, -796, -796, -796, -796, -796, -796, -796, -796, -796, + -796, -796, -796, -796, -796, -796, -796, -796, -796, -796, + + -796, -796, -796, -796, -796, -796, -796, -796 + }, + + { + 55, -797, -797, -797, -797, -797, -797, -797, -797, -797, + -797, -797, -797, -797, -797, -797, -797, -797, -797, -797, + -797, -797, -797, -797, -797, -797, -797, -797, -797, -797, + -797, -797, -797, -797, -797, -797, -797, -797, -797, -797, + -797, -797, -797, -797, -797, -797, -797, -797, -797, -797, + -797, -797, -797, -797, -797, -797, -797, -797, -797, -797, + -797, -797, -797, -797, -797, -797, -797, -797, -797, 992, + -797, -797, -797, -797, -797, -797, -797, -797, -797, -797, + -797, 993, -797, -797, -797, -797, -797, -797, -797, -797, + + -797, -797, -797, -797, -797, -797, -797, -797, -797, -797, + -797, -797, -797, -797, -797, -797, -797, -797, -797, -797, + -797, -797, -797, -797, -797, -797, -797, -797, -797, -797, + -797, -797, -797, -797, -797, -797, -797, -797 + }, + + { + 55, -798, -798, -798, -798, -798, -798, -798, -798, -798, + -798, -798, -798, -798, -798, -798, -798, -798, -798, -798, + -798, -798, -798, -798, -798, -798, -798, -798, -798, -798, + -798, -798, -798, -798, -798, -798, -798, -798, -798, -798, + -798, -798, -798, -798, -798, -798, -798, -798, -798, -798, + -798, -798, -798, -798, -798, -798, -798, -798, -798, -798, + + -798, -798, -798, -798, -798, -798, -798, -798, -798, -798, + -798, -798, -798, -798, -798, -798, -798, -798, -798, -798, + -798, -798, -798, -798, -798, -798, 994, -798, -798, -798, + -798, -798, -798, -798, -798, -798, -798, -798, -798, -798, + -798, -798, -798, -798, -798, -798, -798, -798, -798, -798, + -798, -798, -798, -798, -798, -798, -798, -798, -798, -798, + -798, -798, -798, -798, -798, -798, -798, -798 + }, + + { + 55, -799, -799, -799, -799, -799, -799, -799, -799, -799, + -799, -799, -799, -799, -799, -799, -799, -799, -799, -799, + -799, -799, -799, -799, -799, -799, -799, -799, -799, -799, + + -799, -799, -799, -799, -799, -799, -799, -799, -799, -799, + -799, -799, -799, -799, -799, -799, -799, -799, -799, -799, + -799, -799, -799, -799, -799, -799, -799, -799, -799, -799, + -799, -799, -799, -799, -799, -799, -799, -799, -799, 995, + -799, -799, -799, -799, -799, -799, -799, -799, -799, -799, + -799, -799, -799, -799, -799, -799, -799, -799, -799, -799, + -799, -799, -799, -799, -799, -799, -799, -799, -799, -799, + -799, -799, -799, -799, -799, -799, -799, -799, -799, -799, + -799, -799, -799, -799, -799, -799, -799, -799, -799, -799, + -799, -799, -799, -799, -799, -799, -799, -799 + + }, + + { + 55, -800, -800, -800, -800, -800, -800, -800, -800, -800, + -800, -800, -800, -800, -800, -800, -800, -800, -800, -800, + -800, -800, -800, -800, -800, -800, -800, -800, -800, -800, + -800, -800, -800, -800, -800, -800, -800, -800, -800, -800, + -800, -800, -800, -800, -800, -800, -800, -800, -800, -800, + -800, -800, -800, -800, -800, -800, -800, -800, -800, -800, + -800, -800, -800, -800, -800, -800, -800, -800, -800, -800, + -800, -800, -800, -800, -800, -800, -800, -800, -800, -800, + -800, -800, -800, 996, -800, -800, -800, -800, -800, -800, + -800, -800, -800, -800, -800, -800, -800, -800, -800, -800, + + -800, -800, -800, -800, -800, -800, -800, -800, -800, -800, + -800, -800, -800, -800, -800, -800, -800, -800, -800, -800, + -800, -800, -800, -800, -800, -800, -800, -800 + }, + + { + 55, -801, -801, -801, -801, -801, -801, -801, -801, -801, + -801, -801, -801, -801, -801, -801, -801, -801, -801, -801, + -801, -801, -801, -801, -801, -801, -801, -801, -801, -801, + -801, -801, -801, -801, -801, -801, -801, -801, -801, -801, + -801, -801, -801, -801, -801, -801, -801, -801, -801, -801, + -801, -801, -801, -801, -801, -801, -801, -801, -801, -801, + -801, -801, -801, -801, -801, -801, -801, -801, -801, -801, + + -801, -801, -801, -801, -801, -801, -801, -801, -801, -801, + -801, -801, -801, 997, -801, -801, -801, -801, -801, -801, + -801, -801, -801, -801, -801, -801, -801, -801, -801, -801, + -801, -801, -801, -801, -801, -801, -801, -801, -801, -801, + -801, -801, -801, -801, -801, -801, -801, -801, -801, -801, + -801, -801, -801, -801, -801, -801, -801, -801 + }, + + { + 55, -802, -802, -802, -802, -802, -802, -802, -802, -802, + -802, -802, -802, -802, -802, -802, -802, -802, -802, -802, + -802, -802, -802, -802, -802, -802, -802, -802, -802, -802, + -802, -802, -802, -802, -802, -802, -802, -802, -802, -802, + + -802, -802, -802, -802, -802, -802, -802, -802, -802, -802, + -802, -802, -802, -802, -802, -802, -802, -802, -802, -802, + -802, -802, -802, -802, -802, -802, -802, 998, -802, -802, + -802, -802, -802, -802, -802, -802, -802, -802, -802, -802, + -802, -802, -802, -802, -802, -802, -802, -802, -802, -802, + -802, -802, -802, -802, -802, -802, -802, -802, -802, -802, + -802, -802, -802, -802, -802, -802, -802, -802, -802, -802, + -802, -802, -802, -802, -802, -802, -802, -802, -802, -802, + -802, -802, -802, -802, -802, -802, -802, -802 + }, + + { + 55, -803, -803, -803, -803, -803, -803, -803, -803, -803, + + -803, -803, -803, -803, -803, -803, -803, -803, -803, -803, + -803, -803, -803, -803, -803, -803, -803, -803, -803, -803, + -803, -803, -803, -803, -803, -803, -803, -803, -803, -803, + -803, -803, -803, -803, -803, -803, -803, -803, -803, -803, + -803, -803, -803, -803, -803, -803, -803, -803, -803, -803, + -803, -803, -803, -803, -803, -803, -803, -803, -803, 999, + -803, -803, -803, -803, -803, -803, -803, -803, -803, -803, + -803, -803, -803, -803, -803, -803, -803, -803, -803, -803, + -803, -803, -803, -803, -803, -803, -803, -803, -803, -803, + -803, -803, -803, -803, -803, -803, -803, -803, -803, -803, + + -803, -803, -803, -803, -803, -803, -803, -803, -803, -803, + -803, -803, -803, -803, -803, -803, -803, -803 + }, + + { + 55, -804, -804, -804, -804, -804, -804, -804, -804, -804, + -804, -804, -804, -804, -804, -804, -804, -804, -804, -804, + -804, -804, -804, -804, -804, -804, -804, -804, -804, -804, + -804, -804, -804, -804, -804, -804, -804, -804, -804, -804, + -804, -804, -804, -804, -804, -804, -804, -804, -804, -804, + -804, -804, -804, -804, -804, -804, -804, -804, -804, -804, + -804, -804, -804, -804, -804, -804, -804, -804, -804, -804, + -804, -804, -804, -804, -804, -804, 1000, -804, -804, -804, + + -804, -804, -804, -804, -804, -804, -804, -804, -804, -804, + -804, -804, -804, -804, -804, -804, -804, -804, -804, -804, + -804, -804, -804, -804, -804, -804, -804, -804, -804, -804, + -804, -804, -804, -804, -804, -804, -804, -804, -804, -804, + -804, -804, -804, -804, -804, -804, -804, -804 + }, + + { + 55, -805, -805, -805, -805, -805, -805, -805, -805, -805, + -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, + -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, + -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, + -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, + + -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, + -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, + 1001, -805, -805, -805, -805, -805, -805, -805, -805, -805, + -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, + -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, + -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, + -805, -805, -805, -805, -805, -805, -805, -805, -805, -805, + -805, -805, -805, -805, -805, -805, -805, -805 + }, + + { + 55, -806, -806, -806, -806, -806, -806, -806, -806, -806, + -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, + + -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, + -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, + -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, + -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, + -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, + -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, + -806, -806, -806, -806, -806, -806, -806, -806, 1002, -806, + -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, + -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, + -806, -806, -806, -806, -806, -806, -806, -806, -806, -806, + + -806, -806, -806, -806, -806, -806, -806, -806 + }, + + { + 55, -807, -807, -807, -807, -807, -807, -807, -807, -807, + -807, -807, -807, -807, -807, -807, -807, -807, -807, -807, + -807, -807, -807, -807, -807, -807, -807, -807, -807, -807, + -807, -807, -807, -807, -807, -807, -807, -807, -807, -807, + -807, -807, -807, -807, -807, -807, -807, -807, -807, -807, + -807, -807, -807, -807, -807, -807, -807, -807, -807, -807, + -807, -807, -807, -807, -807, -807, -807, -807, -807, -807, + -807, -807, -807, -807, -807, -807, -807, -807, -807, -807, + -807, -807, -807, 1003, -807, -807, -807, -807, -807, -807, + + -807, -807, -807, -807, -807, -807, -807, -807, -807, -807, + -807, -807, -807, -807, -807, -807, -807, -807, -807, -807, + -807, -807, -807, -807, -807, -807, -807, -807, -807, -807, + -807, -807, -807, -807, -807, -807, -807, -807 + }, + + { + 55, -808, -808, -808, -808, -808, -808, -808, -808, -808, + -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, + -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, + -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, + -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, + -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, + + -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, + -808, -808, -808, 1004, -808, -808, -808, -808, -808, -808, + -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, + -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, + -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, + -808, -808, -808, -808, -808, -808, -808, -808, -808, -808, + -808, -808, -808, -808, -808, -808, -808, -808 + }, + + { + 55, -809, -809, -809, -809, -809, -809, -809, -809, -809, + -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, + -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, + + -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, + -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, + -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, + -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, + -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, + -809, -809, 1005, -809, -809, -809, -809, -809, -809, -809, + -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, + -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, + -809, -809, -809, -809, -809, -809, -809, -809, -809, -809, + -809, -809, -809, -809, -809, -809, -809, -809 + + }, + + { + 55, -810, -810, -810, -810, -810, -810, -810, -810, -810, + -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, + -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, + -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, + -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, + -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, + -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, + -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, + -810, -810, 1006, -810, -810, -810, -810, -810, -810, -810, + -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, + + -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, + -810, -810, -810, -810, -810, -810, -810, -810, -810, -810, + -810, -810, -810, -810, -810, -810, -810, -810 + }, + + { + 55, -811, -811, -811, -811, -811, -811, -811, -811, -811, + -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, + -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, + -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, + -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, + -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, + -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, + + -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, + -811, -811, 1007, -811, -811, -811, -811, -811, -811, -811, + -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, + -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, + -811, -811, -811, -811, -811, -811, -811, -811, -811, -811, + -811, -811, -811, -811, -811, -811, -811, -811 + }, + + { + 55, -812, -812, -812, -812, -812, -812, -812, -812, -812, + -812, -812, -812, -812, -812, -812, -812, -812, -812, -812, + -812, -812, -812, -812, -812, -812, -812, -812, -812, -812, + -812, -812, -812, -812, -812, -812, -812, -812, -812, -812, + + -812, -812, -812, -812, -812, -812, -812, -812, -812, -812, + -812, -812, -812, -812, -812, -812, -812, -812, -812, -812, + -812, -812, -812, -812, -812, -812, -812, -812, -812, -812, + -812, -812, -812, -812, -812, -812, -812, -812, -812, -812, + -812, -812, -812, 1008, -812, -812, -812, -812, -812, -812, + -812, -812, -812, -812, -812, -812, -812, -812, -812, -812, + -812, -812, -812, -812, -812, -812, -812, -812, -812, -812, + -812, -812, -812, -812, -812, -812, -812, -812, -812, -812, + -812, -812, -812, -812, -812, -812, -812, -812 + }, + + { + 55, -813, -813, -813, -813, -813, -813, -813, -813, -813, + + -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, + -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, + -813, -813, 1009, -813, -813, -813, -813, -813, -813, -813, + -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, + -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, + -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, + -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, + -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, + -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, + -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, + + -813, -813, -813, -813, -813, -813, -813, -813, -813, -813, + -813, -813, -813, -813, -813, -813, -813, -813 + }, + + { + 55, -814, -814, -814, -814, -814, -814, -814, -814, -814, + -814, -814, -814, -814, -814, -814, -814, -814, -814, -814, + -814, -814, -814, -814, -814, -814, -814, -814, -814, -814, + -814, -814, 1010, -814, -814, -814, -814, -814, -814, -814, + -814, -814, -814, -814, -814, -814, -814, -814, -814, -814, + -814, -814, -814, -814, -814, -814, -814, -814, -814, -814, + -814, -814, -814, -814, -814, -814, -814, -814, -814, -814, + -814, -814, -814, -814, -814, -814, -814, -814, -814, -814, + + -814, -814, -814, -814, -814, -814, -814, -814, -814, -814, + -814, -814, -814, -814, -814, -814, -814, -814, -814, -814, + -814, -814, -814, -814, -814, -814, -814, -814, -814, -814, + -814, -814, -814, -814, -814, -814, -814, -814, -814, -814, + -814, -814, -814, -814, -814, -814, -814, -814 + }, + + { + 55, -815, -815, -815, -815, -815, -815, -815, -815, -815, + -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, + -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, + -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, + -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, + + -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, + -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, + -815, -815, -815, -815, -815, -815, 1011, -815, -815, -815, + -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, + -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, + -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, + -815, -815, -815, -815, -815, -815, -815, -815, -815, -815, + -815, -815, -815, -815, -815, -815, -815, -815 + }, + + { + 55, -816, -816, -816, -816, -816, -816, -816, -816, -816, + -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, + + -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, + -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, + -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, + -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, + -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, + -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, + -816, -816, -816, 1012, -816, -816, -816, -816, -816, -816, + -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, + -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, + -816, -816, -816, -816, -816, -816, -816, -816, -816, -816, + + -816, -816, -816, -816, -816, -816, -816, -816 + }, + + { + 55, -817, -817, -817, -817, -817, -817, -817, -817, -817, + -817, -817, -817, -817, -817, -817, -817, -817, -817, -817, + -817, -817, -817, -817, -817, -817, -817, -817, -817, -817, + -817, -817, 1013, -817, -817, -817, -817, -817, -817, -817, + -817, -817, -817, -817, -817, -817, -817, -817, -817, -817, + -817, -817, -817, -817, -817, -817, -817, -817, -817, -817, + -817, -817, -817, -817, -817, 1013, 1013, 1013, 1013, 1013, + 1013, 1013, 1013, 1013, 1013, 1013, 1013, 1013, 1013, 1013, + 1013, 1013, 1013, 1013, 1013, 1013, 1013, 1013, 1013, 1013, + + 1013, -817, -817, -817, -817, -817, -817, -817, -817, -817, + -817, -817, -817, -817, -817, -817, -817, -817, -817, -817, + -817, -817, -817, -817, -817, -817, -817, -817, -817, -817, + -817, -817, -817, -817, -817, -817, -817, -817 + }, + + { + 55, -818, -818, -818, -818, -818, -818, -818, -818, -818, + -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, + -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, + -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, + -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, + -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, + + -818, -818, -818, -818, -818, -818, -818, -818, -818, 1014, + -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, + -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, + -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, + -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, + -818, -818, -818, -818, -818, -818, -818, -818, -818, -818, + -818, -818, -818, -818, -818, -818, -818, -818 + }, + + { + 55, -819, -819, -819, -819, -819, -819, -819, -819, -819, + -819, -819, -819, -819, -819, -819, -819, -819, -819, -819, + -819, -819, -819, -819, -819, -819, -819, -819, -819, -819, + + -819, -819, -819, -819, -819, -819, -819, -819, -819, -819, + -819, -819, -819, -819, -819, -819, -819, -819, 1015, 1015, + 1015, 1015, 1015, 1015, 1015, 1015, 1015, 1015, -819, -819, + -819, -819, -819, -819, -819, -819, -819, -819, -819, -819, + -819, -819, -819, -819, -819, -819, -819, -819, -819, -819, + -819, -819, -819, -819, -819, -819, -819, -819, -819, -819, + -819, -819, -819, -819, -819, -819, -819, -819, -819, -819, + -819, -819, -819, -819, -819, -819, -819, -819, -819, -819, + -819, -819, -819, -819, -819, -819, -819, -819, -819, -819, + -819, -819, -819, -819, -819, -819, -819, -819 + + }, + + { + 55, -820, -820, -820, -820, -820, -820, -820, -820, -820, + -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, + -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, + -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, + -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, + -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, + -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, + -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, + -820, -820, -820, 1016, -820, -820, -820, -820, -820, -820, + -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, + + -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, + -820, -820, -820, -820, -820, -820, -820, -820, -820, -820, + -820, -820, -820, -820, -820, -820, -820, -820 + }, + + { + 55, -821, -821, -821, -821, -821, -821, -821, -821, -821, + -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, + -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, + -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, + -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, + -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, + -821, -821, -821, -821, -821, -821, -821, -821, -821, 1017, + + -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, + -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, + -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, + -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, + -821, -821, -821, -821, -821, -821, -821, -821, -821, -821, + -821, -821, -821, -821, -821, -821, -821, -821 + }, + + { + 55, -822, -822, -822, -822, -822, -822, -822, -822, -822, + -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, + -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, + -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, + + -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, + -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, + -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, + -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, + -822, -822, -822, 1018, -822, -822, -822, -822, -822, -822, + -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, + -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, + -822, -822, -822, -822, -822, -822, -822, -822, -822, -822, + -822, -822, -822, -822, -822, -822, -822, -822 + }, + + { + 55, -823, -823, -823, -823, -823, -823, -823, -823, -823, + + -823, -823, -823, -823, -823, -823, -823, -823, -823, -823, + -823, -823, -823, -823, -823, -823, -823, -823, -823, -823, + -823, -823, -823, -823, -823, -823, -823, -823, -823, -823, + -823, -823, -823, -823, -823, -823, -823, -823, -823, -823, + -823, -823, -823, -823, -823, -823, -823, -823, -823, -823, + -823, -823, -823, -823, -823, -823, -823, -823, -823, 1019, + -823, -823, -823, -823, -823, -823, -823, -823, -823, -823, + -823, -823, -823, -823, -823, -823, -823, -823, -823, -823, + -823, -823, -823, -823, -823, -823, -823, -823, -823, -823, + -823, -823, -823, -823, -823, -823, -823, -823, -823, -823, + + -823, -823, -823, -823, -823, -823, -823, -823, -823, -823, + -823, -823, -823, -823, -823, -823, -823, -823 + }, + + { + 55, -824, -824, -824, -824, -824, -824, -824, -824, -824, + -824, -824, -824, -824, -824, -824, -824, -824, -824, -824, + -824, -824, -824, -824, -824, -824, -824, -824, -824, -824, + -824, -824, -824, -824, -824, -824, -824, -824, -824, -824, + -824, -824, -824, -824, -824, -824, -824, -824, -824, -824, + -824, -824, -824, -824, -824, -824, -824, -824, -824, -824, + -824, -824, -824, -824, -824, -824, -824, -824, -824, -824, + -824, -824, -824, -824, -824, -824, -824, -824, -824, -824, + + -824, -824, -824, 1020, -824, -824, -824, -824, -824, -824, + -824, -824, -824, -824, -824, -824, -824, -824, -824, -824, + -824, -824, -824, -824, -824, -824, -824, -824, -824, -824, + -824, -824, -824, -824, -824, -824, -824, -824, -824, -824, + -824, -824, -824, -824, -824, -824, -824, -824 + }, + + { + 55, -825, -825, -825, -825, -825, -825, -825, -825, -825, + -825, -825, -825, -825, -825, -825, -825, -825, -825, -825, + -825, -825, -825, -825, -825, -825, -825, -825, -825, -825, + -825, -825, -825, -825, -825, -825, -825, -825, -825, -825, + -825, -825, -825, -825, -825, -825, -825, -825, -825, -825, + + -825, -825, -825, -825, -825, -825, -825, -825, -825, -825, + -825, -825, -825, -825, -825, -825, -825, -825, -825, 1021, + -825, -825, -825, -825, -825, -825, -825, -825, -825, -825, + -825, -825, -825, -825, -825, -825, -825, -825, -825, -825, + -825, -825, -825, -825, -825, -825, -825, -825, -825, -825, + -825, -825, -825, -825, -825, -825, -825, -825, -825, -825, + -825, -825, -825, -825, -825, -825, -825, -825, -825, -825, + -825, -825, -825, -825, -825, -825, -825, -825 + }, + + { + 55, -826, -826, -826, -826, -826, -826, -826, -826, -826, + -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, + + -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, + -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, + -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, + -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, + -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, + -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, + -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, + -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, + -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, + -826, -826, -826, -826, -826, -826, -826, -826, -826, -826, + + -826, -826, -826, -826, -826, -826, -826, -826 + }, + + { + 55, -827, -827, -827, -827, -827, -827, -827, -827, -827, + -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, + -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, + -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, + -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, + -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, + -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, + -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, + -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, + + -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, + -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, + -827, -827, -827, -827, -827, -827, -827, -827, -827, -827, + -827, -827, -827, -827, -827, -827, -827, -827 + }, + + { + 55, -828, -828, -828, -828, -828, -828, -828, -828, -828, + -828, -828, -828, -828, -828, -828, -828, -828, -828, -828, + -828, -828, -828, -828, -828, -828, -828, -828, -828, -828, + -828, -828, -828, -828, -828, -828, -828, -828, -828, -828, + -828, -828, -828, -828, -828, -828, -828, -828, -828, -828, + -828, -828, -828, -828, -828, -828, -828, -828, -828, -828, + + -828, -828, -828, -828, -828, -828, -828, -828, -828, -828, + -828, -828, -828, -828, -828, -828, -828, -828, -828, -828, + -828, -828, -828, -828, -828, -828, -828, -828, -828, -828, + -828, -828, -828, -828, -828, -828, -828, -828, -828, -828, + -828, -828, -828, -828, -828, -828, -828, -828, -828, -828, + -828, -828, -828, -828, -828, -828, -828, -828, -828, -828, + -828, -828, -828, -828, -828, -828, -828, -828 + }, + + { + 55, -829, -829, -829, -829, -829, -829, -829, -829, -829, + -829, -829, -829, -829, -829, -829, -829, -829, -829, -829, + -829, -829, -829, -829, -829, -829, -829, -829, -829, -829, + + -829, -829, -829, -829, -829, -829, -829, -829, -829, -829, + -829, -829, -829, -829, -829, -829, -829, -829, -829, -829, + -829, -829, -829, -829, -829, -829, -829, -829, -829, -829, + -829, -829, -829, -829, -829, -829, -829, -829, -829, -829, + -829, -829, -829, -829, -829, -829, -829, -829, -829, -829, + -829, -829, -829, -829, -829, -829, -829, -829, -829, -829, + -829, -829, -829, -829, -829, -829, -829, -829, -829, -829, + -829, -829, -829, -829, -829, -829, -829, -829, -829, -829, + -829, -829, -829, -829, -829, -829, -829, -829, -829, -829, + -829, -829, -829, -829, -829, -829, -829, -829 + + }, + + { + 55, -830, -830, -830, -830, -830, -830, -830, -830, -830, + -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, + -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, + -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, + -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, + -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, + -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, + -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, + -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, + -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, + + -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, + -830, -830, -830, -830, -830, -830, -830, -830, -830, -830, + -830, -830, -830, -830, -830, -830, -830, -830 + }, + + { + 55, -831, -831, -831, -831, -831, -831, -831, -831, -831, + -831, -831, -831, -831, -831, -831, -831, -831, -831, -831, + -831, -831, -831, -831, -831, -831, -831, -831, -831, -831, + -831, -831, -831, -831, -831, -831, -831, -831, -831, -831, + -831, -831, -831, -831, -831, -831, -831, -831, -831, -831, + -831, -831, -831, -831, -831, -831, -831, -831, -831, -831, + -831, -831, -831, -831, -831, -831, -831, -831, -831, -831, + + -831, -831, -831, -831, -831, -831, -831, -831, -831, -831, + -831, -831, -831, -831, -831, -831, -831, -831, -831, -831, + -831, -831, -831, -831, -831, -831, -831, -831, -831, -831, + -831, -831, -831, -831, -831, -831, -831, -831, -831, -831, + -831, -831, -831, -831, -831, -831, -831, -831, -831, -831, + -831, -831, -831, -831, -831, -831, -831, -831 + }, + + { + 55, -832, -832, -832, -832, -832, -832, -832, -832, -832, + -832, -832, -832, -832, -832, -832, -832, -832, -832, -832, + -832, -832, -832, -832, -832, -832, -832, -832, -832, -832, + -832, -832, -832, -832, -832, -832, -832, -832, -832, -832, + + -832, -832, -832, -832, -832, -832, -832, -832, -832, -832, + -832, -832, -832, -832, -832, -832, -832, -832, -832, -832, + -832, -832, -832, -832, -832, -832, -832, -832, -832, -832, + -832, -832, -832, -832, -832, -832, -832, -832, -832, -832, + -832, -832, -832, -832, -832, -832, -832, -832, -832, -832, + -832, -832, -832, -832, -832, -832, -832, -832, -832, -832, + -832, -832, -832, -832, -832, -832, -832, -832, -832, -832, + -832, -832, -832, -832, -832, -832, -832, -832, -832, -832, + -832, -832, -832, -832, -832, -832, -832, -832 + }, + + { + 55, -833, -833, -833, -833, -833, -833, -833, -833, -833, + + -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, + -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, + -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, + -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, + -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, + -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, + -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, + -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, + -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, + -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, + + -833, -833, -833, -833, -833, -833, -833, -833, -833, -833, + -833, -833, -833, -833, -833, -833, -833, -833 + }, + + { + 55, -834, -834, -834, -834, -834, -834, -834, -834, -834, + -834, -834, -834, -834, -834, -834, -834, -834, -834, -834, + -834, -834, -834, -834, -834, -834, -834, -834, -834, -834, + -834, -834, -834, -834, -834, -834, -834, -834, -834, -834, + -834, -834, -834, -834, -834, -834, -834, -834, -834, -834, + -834, -834, -834, -834, -834, -834, -834, -834, -834, -834, + -834, -834, -834, -834, -834, -834, -834, -834, -834, -834, + -834, -834, -834, -834, -834, -834, -834, -834, -834, -834, + + -834, -834, -834, -834, -834, -834, -834, -834, -834, -834, + -834, -834, -834, -834, -834, -834, -834, -834, -834, -834, + -834, -834, -834, -834, -834, -834, -834, -834, -834, -834, + -834, -834, -834, -834, -834, -834, -834, -834, -834, -834, + -834, -834, -834, -834, -834, -834, -834, -834 + }, + + { + 55, -835, -835, -835, -835, -835, -835, -835, -835, -835, + -835, -835, -835, -835, -835, -835, -835, -835, -835, -835, + -835, -835, -835, -835, -835, -835, -835, -835, -835, -835, + -835, -835, -835, -835, -835, -835, -835, -835, -835, -835, + -835, -835, -835, -835, -835, -835, -835, -835, -835, -835, + + -835, -835, -835, -835, -835, -835, -835, -835, -835, -835, + -835, -835, -835, -835, -835, -835, -835, -835, -835, -835, + -835, -835, -835, -835, -835, -835, -835, -835, -835, -835, + -835, -835, -835, -835, -835, -835, -835, -835, -835, -835, + -835, -835, -835, -835, -835, -835, -835, -835, -835, -835, + -835, -835, -835, -835, -835, -835, -835, -835, -835, -835, + -835, -835, -835, -835, -835, -835, -835, -835, -835, -835, + -835, -835, -835, -835, -835, -835, -835, -835 + }, + + { + 55, -836, -836, -836, -836, -836, -836, -836, -836, -836, + -836, -836, -836, -836, -836, -836, -836, -836, -836, -836, + + -836, -836, -836, -836, -836, -836, -836, -836, -836, -836, + -836, -836, -836, -836, -836, -836, -836, -836, -836, -836, + -836, -836, -836, -836, -836, -836, -836, -836, -836, -836, + -836, -836, -836, -836, -836, -836, -836, -836, -836, -836, + -836, -836, -836, -836, -836, -836, -836, -836, -836, -836, + -836, -836, -836, -836, -836, -836, -836, -836, -836, -836, + -836, -836, -836, -836, -836, -836, -836, -836, -836, -836, + -836, -836, -836, -836, -836, -836, -836, -836, -836, -836, + -836, -836, -836, -836, -836, -836, -836, -836, -836, -836, + -836, -836, -836, -836, -836, -836, -836, -836, -836, -836, + + -836, -836, -836, -836, -836, -836, -836, -836 + }, + + { + 55, -837, -837, -837, -837, -837, -837, -837, -837, -837, + -837, -837, -837, -837, -837, -837, -837, -837, -837, -837, + -837, -837, -837, -837, -837, -837, -837, -837, -837, -837, + -837, -837, -837, -837, -837, -837, -837, -837, -837, -837, + -837, -837, -837, -837, -837, -837, -837, -837, -837, -837, + -837, -837, -837, -837, -837, -837, -837, -837, -837, -837, + -837, -837, -837, -837, -837, -837, -837, -837, -837, -837, + -837, -837, -837, -837, -837, -837, -837, -837, -837, -837, + -837, -837, -837, -837, -837, -837, -837, -837, -837, -837, + + -837, -837, -837, -837, -837, -837, -837, -837, -837, -837, + -837, -837, -837, -837, -837, -837, -837, -837, -837, -837, + -837, -837, -837, -837, -837, -837, -837, -837, -837, -837, + -837, -837, -837, -837, -837, -837, -837, -837 + }, + + { + 55, -838, -838, -838, -838, -838, -838, -838, -838, -838, + -838, -838, -838, -838, -838, -838, -838, -838, -838, -838, + -838, -838, -838, -838, -838, -838, -838, -838, -838, -838, + -838, -838, -838, -838, -838, -838, -838, -838, -838, -838, + -838, -838, -838, -838, -838, -838, -838, -838, -838, -838, + -838, -838, -838, -838, -838, -838, -838, -838, -838, -838, + + -838, -838, -838, -838, -838, -838, -838, -838, -838, -838, + -838, -838, -838, -838, -838, -838, -838, -838, -838, -838, + -838, -838, -838, -838, -838, -838, -838, -838, -838, -838, + -838, -838, -838, -838, -838, -838, -838, -838, -838, -838, + -838, -838, -838, -838, -838, -838, -838, -838, -838, -838, + -838, -838, -838, -838, -838, -838, -838, -838, -838, -838, + -838, -838, -838, -838, -838, -838, -838, -838 + }, + + { + 55, -839, -839, -839, -839, -839, -839, -839, -839, -839, + -839, -839, -839, -839, -839, -839, -839, -839, -839, -839, + -839, -839, -839, -839, -839, -839, -839, -839, -839, -839, + + -839, -839, -839, -839, -839, -839, -839, -839, -839, -839, + -839, -839, -839, -839, -839, -839, -839, -839, -839, -839, + -839, -839, -839, -839, -839, -839, -839, -839, -839, -839, + -839, -839, -839, -839, -839, -839, -839, -839, -839, -839, + -839, -839, -839, -839, -839, -839, -839, -839, -839, -839, + -839, -839, -839, -839, -839, -839, -839, -839, -839, -839, + -839, -839, -839, -839, -839, -839, -839, -839, -839, -839, + -839, -839, -839, -839, -839, -839, -839, -839, -839, -839, + -839, -839, -839, -839, -839, -839, -839, -839, -839, -839, + -839, -839, -839, -839, -839, -839, -839, -839 + + }, + + { + 55, -840, -840, -840, -840, -840, -840, -840, -840, -840, + -840, -840, -840, -840, -840, -840, -840, -840, -840, -840, + -840, -840, -840, -840, -840, -840, -840, -840, -840, -840, + -840, -840, -840, -840, -840, -840, -840, -840, -840, -840, + -840, -840, -840, -840, -840, -840, -840, -840, -840, -840, + -840, -840, -840, -840, -840, -840, -840, -840, -840, -840, + -840, -840, -840, -840, -840, -840, -840, -840, -840, -840, + -840, -840, -840, -840, -840, -840, -840, -840, -840, -840, + -840, -840, -840, -840, -840, -840, -840, -840, -840, -840, + -840, -840, -840, -840, -840, -840, -840, -840, -840, -840, + + -840, -840, -840, -840, -840, -840, -840, -840, -840, -840, + -840, -840, -840, -840, -840, -840, -840, -840, -840, -840, + -840, -840, -840, -840, -840, -840, -840, -840 + }, + + { + 55, -841, -841, -841, -841, -841, -841, -841, -841, -841, + -841, -841, -841, -841, -841, -841, -841, -841, -841, -841, + -841, -841, -841, -841, -841, -841, -841, -841, -841, -841, + -841, -841, -841, -841, -841, -841, -841, -841, -841, -841, + -841, -841, -841, -841, -841, -841, -841, -841, -841, -841, + -841, -841, -841, -841, -841, -841, -841, -841, -841, -841, + -841, -841, -841, -841, -841, -841, -841, -841, -841, -841, + + -841, -841, -841, -841, -841, -841, -841, -841, -841, -841, + -841, -841, -841, -841, -841, -841, -841, -841, -841, -841, + -841, -841, -841, -841, -841, -841, -841, -841, -841, -841, + -841, -841, -841, -841, -841, -841, -841, -841, -841, -841, + -841, -841, -841, -841, -841, -841, -841, -841, -841, -841, + -841, -841, -841, -841, -841, -841, -841, -841 + }, + + { + 55, -842, -842, -842, -842, -842, -842, -842, -842, -842, + -842, -842, -842, -842, -842, -842, -842, -842, -842, -842, + -842, -842, -842, -842, -842, -842, -842, -842, -842, -842, + -842, -842, -842, -842, -842, -842, -842, -842, -842, -842, + + -842, -842, -842, -842, -842, -842, -842, -842, -842, -842, + -842, -842, -842, -842, -842, -842, -842, -842, -842, -842, + -842, -842, -842, -842, -842, -842, -842, -842, -842, -842, + -842, -842, -842, -842, -842, -842, -842, -842, -842, -842, + -842, -842, -842, -842, -842, -842, -842, -842, -842, -842, + -842, -842, -842, -842, -842, -842, -842, -842, -842, -842, + -842, -842, -842, -842, -842, -842, -842, -842, -842, -842, + -842, -842, -842, -842, -842, -842, -842, -842, -842, -842, + -842, -842, -842, -842, -842, -842, -842, -842 + }, + + { + 55, -843, -843, -843, -843, -843, -843, -843, -843, -843, + + -843, -843, -843, -843, -843, -843, -843, -843, -843, -843, + -843, -843, -843, -843, -843, -843, -843, -843, -843, -843, + -843, -843, -843, -843, -843, -843, -843, -843, -843, -843, + -843, -843, -843, -843, -843, -843, -843, -843, -843, -843, + -843, -843, -843, -843, -843, -843, -843, -843, -843, -843, + -843, -843, -843, -843, -843, -843, -843, -843, -843, -843, + -843, -843, -843, -843, -843, -843, -843, -843, -843, -843, + -843, -843, -843, -843, -843, -843, -843, -843, -843, -843, + -843, -843, -843, -843, -843, -843, -843, -843, -843, -843, + -843, -843, -843, -843, -843, -843, -843, -843, -843, -843, + + -843, -843, -843, -843, -843, -843, -843, -843, -843, -843, + -843, -843, -843, -843, -843, -843, -843, -843 + }, + + { + 55, -844, -844, -844, -844, -844, -844, -844, -844, -844, + -844, -844, -844, -844, -844, -844, -844, -844, -844, -844, + -844, -844, -844, -844, -844, -844, -844, -844, -844, -844, + -844, -844, -844, -844, -844, -844, -844, -844, -844, -844, + -844, -844, -844, -844, -844, -844, -844, -844, -844, -844, + -844, -844, -844, -844, -844, -844, -844, -844, -844, -844, + -844, -844, -844, -844, -844, -844, -844, -844, -844, -844, + -844, -844, -844, -844, -844, -844, -844, -844, -844, -844, + + -844, -844, -844, -844, -844, -844, -844, -844, -844, -844, + -844, -844, -844, -844, -844, -844, -844, -844, -844, -844, + -844, -844, -844, -844, -844, -844, -844, -844, -844, -844, + -844, -844, -844, -844, -844, -844, -844, -844, -844, -844, + -844, -844, -844, -844, -844, -844, -844, -844 + }, + + { + 55, -845, -845, -845, -845, -845, -845, -845, -845, -845, + -845, -845, -845, -845, -845, -845, -845, -845, -845, -845, + -845, -845, -845, -845, -845, -845, -845, -845, -845, -845, + -845, -845, -845, -845, -845, -845, -845, -845, -845, -845, + -845, -845, -845, -845, -845, -845, -845, -845, -845, -845, + + -845, -845, -845, -845, -845, -845, -845, -845, -845, -845, + -845, -845, -845, -845, -845, -845, -845, -845, -845, -845, + -845, -845, -845, -845, -845, -845, -845, -845, -845, -845, + -845, -845, -845, -845, -845, -845, -845, -845, -845, -845, + -845, -845, -845, -845, -845, -845, -845, -845, -845, -845, + -845, -845, -845, -845, -845, -845, -845, -845, -845, -845, + -845, -845, -845, -845, -845, -845, -845, -845, -845, -845, + -845, -845, -845, -845, -845, -845, -845, -845 + }, + + { + 55, -846, -846, -846, -846, -846, -846, -846, -846, -846, + -846, -846, -846, -846, -846, -846, -846, -846, -846, -846, + + -846, -846, -846, -846, -846, -846, -846, -846, -846, -846, + -846, -846, -846, -846, -846, -846, -846, -846, -846, -846, + -846, -846, -846, -846, -846, -846, -846, -846, -846, -846, + -846, -846, -846, -846, -846, -846, -846, -846, -846, -846, + -846, -846, -846, -846, -846, -846, -846, -846, -846, -846, + -846, -846, -846, -846, -846, -846, -846, -846, -846, -846, + -846, -846, -846, -846, -846, -846, -846, -846, -846, -846, + -846, -846, -846, -846, -846, -846, -846, -846, -846, -846, + -846, -846, -846, -846, -846, -846, -846, -846, -846, -846, + -846, -846, -846, -846, -846, -846, -846, -846, -846, -846, + + -846, -846, -846, -846, -846, -846, -846, -846 + }, + + { + 55, -847, -847, -847, -847, -847, -847, -847, -847, -847, + -847, -847, -847, -847, -847, -847, -847, -847, -847, -847, + -847, -847, -847, -847, -847, -847, -847, -847, -847, -847, + -847, -847, -847, -847, -847, -847, -847, -847, -847, -847, + -847, -847, -847, -847, -847, -847, -847, -847, -847, -847, + -847, -847, -847, -847, -847, -847, -847, -847, -847, -847, + -847, -847, -847, -847, -847, -847, -847, -847, -847, -847, + -847, -847, -847, -847, -847, -847, -847, -847, -847, -847, + -847, -847, -847, -847, -847, -847, -847, -847, -847, -847, + + -847, -847, -847, -847, -847, -847, -847, -847, -847, -847, + -847, -847, -847, -847, -847, -847, -847, -847, -847, -847, + -847, -847, -847, -847, -847, -847, -847, -847, -847, -847, + -847, -847, -847, -847, -847, -847, -847, -847 + }, + + { + 55, -848, -848, -848, -848, -848, -848, -848, -848, -848, + -848, -848, -848, -848, -848, -848, -848, -848, -848, -848, + -848, -848, -848, -848, -848, -848, -848, -848, -848, -848, + -848, -848, -848, -848, -848, -848, -848, -848, -848, -848, + -848, -848, -848, -848, -848, -848, -848, -848, -848, -848, + -848, -848, -848, -848, -848, -848, -848, -848, -848, -848, + + -848, -848, -848, -848, -848, -848, -848, -848, -848, -848, + -848, -848, -848, -848, -848, -848, -848, -848, -848, -848, + -848, -848, -848, -848, -848, -848, -848, -848, -848, -848, + -848, -848, -848, -848, -848, -848, -848, -848, -848, -848, + -848, -848, -848, -848, -848, -848, -848, -848, -848, -848, + -848, -848, -848, -848, -848, -848, -848, -848, -848, -848, + -848, -848, -848, -848, -848, -848, -848, -848 + }, + + { + 55, -849, -849, -849, -849, -849, -849, -849, -849, -849, + -849, -849, -849, -849, -849, -849, -849, -849, -849, -849, + -849, -849, -849, -849, -849, -849, -849, -849, -849, -849, + + -849, -849, -849, -849, -849, -849, -849, -849, -849, -849, + -849, -849, -849, -849, -849, -849, -849, -849, -849, -849, + -849, -849, -849, -849, -849, -849, -849, -849, -849, -849, + -849, -849, -849, -849, -849, -849, -849, -849, -849, -849, + -849, -849, -849, -849, -849, -849, -849, -849, -849, -849, + -849, -849, -849, -849, -849, -849, -849, -849, -849, -849, + -849, -849, -849, -849, -849, -849, -849, -849, -849, -849, + -849, -849, -849, -849, -849, -849, -849, -849, -849, -849, + -849, -849, -849, -849, -849, -849, -849, -849, -849, -849, + -849, -849, -849, -849, -849, -849, -849, -849 + + }, + + { + 55, -850, -850, -850, -850, -850, -850, -850, -850, -850, + -850, -850, -850, -850, -850, -850, -850, -850, -850, -850, + -850, -850, -850, -850, -850, -850, -850, -850, -850, -850, + -850, -850, -850, -850, -850, -850, -850, -850, -850, -850, + -850, -850, -850, -850, -850, -850, -850, -850, -850, -850, + -850, -850, -850, -850, -850, -850, -850, -850, -850, -850, + -850, -850, -850, -850, -850, -850, -850, -850, -850, -850, + -850, -850, -850, -850, -850, -850, -850, -850, -850, -850, + -850, -850, -850, -850, -850, -850, -850, -850, -850, -850, + -850, -850, -850, -850, -850, -850, -850, -850, -850, -850, + + -850, -850, -850, -850, -850, -850, -850, -850, -850, -850, + -850, -850, -850, -850, -850, -850, -850, -850, -850, -850, + -850, -850, -850, -850, -850, -850, -850, -850 + }, + + { + 55, -851, -851, -851, -851, -851, -851, -851, -851, -851, + -851, -851, -851, -851, -851, -851, -851, -851, -851, -851, + -851, -851, -851, -851, -851, -851, -851, -851, -851, -851, + -851, -851, -851, -851, -851, -851, -851, -851, -851, -851, + -851, -851, -851, -851, -851, -851, -851, -851, -851, -851, + -851, -851, -851, -851, -851, -851, -851, -851, -851, -851, + -851, -851, -851, -851, -851, -851, -851, -851, -851, -851, + + -851, -851, -851, -851, -851, -851, -851, -851, -851, -851, + -851, -851, -851, -851, -851, -851, -851, -851, -851, -851, + -851, -851, -851, -851, -851, -851, -851, -851, -851, -851, + -851, -851, -851, -851, -851, -851, -851, -851, -851, -851, + -851, -851, -851, -851, -851, -851, -851, -851, -851, -851, + -851, -851, -851, -851, -851, -851, -851, -851 + }, + + { + 55, -852, -852, -852, -852, -852, -852, -852, -852, -852, + -852, -852, -852, -852, -852, -852, -852, -852, -852, -852, + -852, -852, -852, -852, -852, -852, -852, -852, -852, -852, + -852, -852, -852, -852, -852, -852, -852, -852, -852, -852, + + -852, -852, -852, -852, -852, -852, -852, -852, -852, -852, + -852, -852, -852, -852, -852, -852, -852, -852, -852, -852, + -852, -852, -852, -852, -852, -852, -852, -852, -852, -852, + -852, -852, -852, -852, -852, -852, -852, -852, -852, -852, + -852, -852, -852, -852, -852, -852, -852, -852, -852, -852, + -852, -852, -852, -852, -852, -852, -852, -852, -852, -852, + -852, -852, -852, -852, -852, -852, -852, -852, -852, -852, + -852, -852, -852, -852, -852, -852, -852, -852, -852, -852, + -852, -852, -852, -852, -852, -852, -852, -852 + }, + + { + 55, -853, -853, -853, -853, -853, -853, -853, -853, -853, + + -853, -853, -853, -853, -853, -853, -853, -853, -853, -853, + -853, -853, -853, -853, -853, -853, -853, -853, -853, -853, + -853, -853, -853, -853, -853, -853, -853, -853, -853, -853, + -853, -853, -853, -853, -853, -853, -853, -853, -853, -853, + -853, -853, -853, -853, -853, -853, -853, -853, -853, -853, + -853, -853, -853, -853, -853, -853, -853, -853, -853, -853, + -853, -853, -853, -853, -853, -853, -853, -853, -853, -853, + -853, -853, -853, -853, -853, -853, -853, -853, -853, -853, + -853, -853, -853, -853, -853, -853, -853, -853, -853, -853, + -853, -853, -853, -853, -853, -853, -853, -853, -853, -853, + + -853, -853, -853, -853, -853, -853, -853, -853, -853, -853, + -853, -853, -853, -853, -853, -853, -853, -853 + }, + + { + 55, -854, -854, -854, -854, -854, -854, -854, -854, -854, + -854, -854, -854, -854, -854, -854, -854, -854, -854, -854, + -854, -854, -854, -854, -854, -854, -854, -854, -854, -854, + -854, -854, -854, -854, -854, -854, -854, -854, -854, -854, + -854, -854, -854, -854, -854, -854, -854, -854, -854, -854, + -854, -854, -854, -854, -854, -854, -854, -854, -854, -854, + -854, -854, -854, -854, -854, -854, -854, -854, -854, -854, + -854, -854, -854, -854, -854, -854, -854, -854, -854, -854, + + -854, -854, -854, -854, -854, -854, -854, -854, -854, -854, + -854, -854, -854, -854, -854, -854, -854, -854, -854, -854, + -854, -854, -854, -854, -854, -854, -854, -854, -854, -854, + -854, -854, -854, -854, -854, -854, -854, -854, -854, -854, + -854, -854, -854, -854, -854, -854, -854, -854 + }, + + { + 55, -855, -855, -855, -855, -855, -855, -855, -855, -855, + -855, -855, -855, -855, -855, -855, -855, -855, -855, -855, + -855, -855, -855, -855, -855, -855, -855, -855, -855, -855, + -855, -855, -855, -855, -855, -855, -855, -855, -855, -855, + -855, -855, -855, -855, -855, -855, -855, -855, -855, -855, + + -855, -855, -855, -855, -855, -855, -855, -855, -855, -855, + -855, -855, -855, -855, -855, -855, -855, -855, -855, -855, + -855, -855, -855, -855, -855, -855, -855, -855, -855, -855, + -855, -855, -855, -855, -855, -855, -855, -855, -855, -855, + -855, -855, -855, -855, -855, -855, -855, -855, -855, -855, + -855, -855, -855, -855, -855, -855, -855, -855, -855, -855, + -855, -855, -855, -855, -855, -855, -855, -855, -855, -855, + -855, -855, -855, -855, -855, -855, -855, -855 + }, + + { + 55, -856, -856, -856, -856, -856, -856, -856, -856, -856, + -856, -856, -856, -856, -856, -856, -856, -856, -856, -856, + + -856, -856, -856, -856, -856, -856, -856, -856, -856, -856, + -856, -856, -856, -856, -856, -856, -856, -856, -856, -856, + -856, -856, -856, -856, -856, -856, -856, -856, -856, -856, + -856, -856, -856, -856, -856, -856, -856, -856, -856, -856, + -856, -856, -856, -856, -856, -856, -856, -856, -856, -856, + -856, -856, -856, -856, -856, -856, -856, -856, -856, -856, + -856, -856, -856, -856, -856, -856, -856, -856, -856, -856, + -856, -856, -856, -856, -856, -856, -856, -856, -856, -856, + -856, -856, -856, -856, -856, -856, -856, -856, -856, -856, + -856, -856, -856, -856, -856, -856, -856, -856, -856, -856, + + -856, -856, -856, -856, -856, -856, -856, -856 + }, + + { + 55, -857, -857, -857, -857, -857, -857, -857, -857, -857, + -857, -857, -857, -857, -857, -857, -857, -857, -857, -857, + -857, -857, -857, -857, -857, -857, -857, -857, -857, -857, + -857, -857, -857, -857, -857, -857, -857, -857, -857, -857, + -857, -857, -857, -857, -857, -857, -857, -857, -857, -857, + -857, -857, -857, -857, -857, -857, -857, -857, -857, -857, + -857, -857, -857, -857, -857, -857, -857, -857, -857, -857, + -857, -857, -857, -857, -857, -857, -857, -857, -857, -857, + -857, -857, -857, -857, -857, -857, -857, -857, -857, -857, + + -857, -857, -857, -857, -857, -857, -857, -857, -857, -857, + -857, -857, -857, -857, -857, -857, -857, -857, -857, -857, + -857, -857, -857, -857, -857, -857, -857, -857, -857, -857, + -857, -857, -857, -857, -857, -857, -857, -857 + }, + + { + 55, -858, -858, -858, -858, -858, -858, -858, -858, -858, + -858, -858, -858, -858, -858, -858, -858, -858, -858, -858, + -858, -858, -858, -858, -858, -858, -858, -858, -858, -858, + -858, -858, -858, -858, -858, -858, -858, -858, -858, -858, + -858, -858, -858, -858, -858, -858, -858, -858, -858, -858, + -858, -858, -858, -858, -858, -858, -858, -858, -858, -858, + + -858, -858, -858, -858, -858, -858, -858, -858, -858, -858, + -858, -858, -858, -858, -858, -858, -858, -858, -858, -858, + -858, -858, -858, -858, -858, -858, -858, -858, -858, -858, + -858, -858, -858, -858, -858, -858, -858, -858, -858, -858, + -858, -858, -858, -858, -858, -858, -858, -858, -858, -858, + -858, -858, -858, -858, -858, -858, -858, -858, -858, -858, + -858, -858, -858, -858, -858, -858, -858, -858 + }, + + { + 55, -859, -859, -859, -859, -859, -859, -859, -859, -859, + -859, -859, -859, -859, -859, -859, -859, -859, -859, -859, + -859, -859, -859, -859, -859, -859, -859, -859, -859, -859, + + -859, -859, -859, -859, -859, -859, -859, -859, -859, -859, + -859, -859, -859, -859, -859, -859, -859, -859, -859, -859, + -859, -859, -859, -859, -859, -859, -859, -859, -859, -859, + -859, -859, -859, -859, -859, -859, -859, -859, -859, -859, + -859, -859, -859, -859, -859, -859, -859, -859, -859, -859, + -859, -859, -859, -859, -859, -859, -859, -859, -859, -859, + -859, -859, -859, -859, -859, -859, -859, -859, -859, -859, + -859, -859, -859, -859, -859, -859, -859, -859, -859, -859, + -859, -859, -859, -859, -859, -859, -859, -859, -859, -859, + -859, -859, -859, -859, -859, -859, -859, -859 + + }, + + { + 55, -860, -860, -860, -860, -860, -860, -860, -860, -860, + -860, -860, -860, -860, -860, -860, -860, -860, -860, -860, + -860, -860, -860, -860, -860, -860, -860, -860, -860, -860, + -860, -860, -860, -860, -860, -860, -860, -860, -860, -860, + -860, -860, -860, -860, -860, -860, -860, -860, -860, -860, + -860, -860, -860, -860, -860, -860, -860, -860, -860, -860, + -860, -860, -860, -860, -860, -860, -860, -860, -860, -860, + -860, -860, -860, -860, -860, -860, -860, -860, -860, -860, + -860, -860, -860, -860, -860, -860, -860, -860, -860, -860, + -860, -860, -860, -860, -860, -860, -860, -860, -860, -860, + + -860, -860, -860, -860, -860, -860, -860, -860, -860, -860, + -860, -860, -860, -860, -860, -860, -860, -860, -860, -860, + -860, -860, -860, -860, -860, -860, -860, -860 + }, + + { + 55, -861, -861, -861, -861, -861, -861, -861, -861, -861, + -861, -861, -861, -861, -861, -861, -861, -861, -861, -861, + -861, -861, -861, -861, -861, -861, -861, -861, -861, -861, + -861, -861, -861, -861, -861, -861, -861, -861, -861, -861, + -861, -861, -861, -861, -861, -861, -861, -861, -861, -861, + -861, -861, -861, -861, -861, -861, -861, -861, -861, -861, + -861, -861, -861, -861, -861, -861, -861, -861, -861, -861, + + -861, -861, -861, -861, -861, -861, -861, -861, -861, -861, + -861, -861, -861, -861, -861, -861, -861, -861, -861, -861, + -861, -861, -861, -861, -861, -861, -861, -861, -861, -861, + -861, -861, -861, -861, -861, -861, -861, -861, -861, -861, + -861, -861, -861, -861, -861, -861, -861, -861, -861, -861, + -861, -861, -861, -861, -861, -861, -861, -861 + }, + + { + 55, -862, -862, -862, -862, -862, -862, -862, -862, -862, + -862, -862, -862, -862, -862, -862, -862, -862, -862, -862, + -862, -862, -862, -862, -862, -862, -862, -862, -862, -862, + -862, -862, -862, -862, -862, -862, -862, -862, -862, -862, + + -862, -862, -862, -862, -862, -862, -862, -862, -862, -862, + -862, -862, -862, -862, -862, -862, -862, -862, -862, -862, + -862, -862, -862, -862, -862, -862, -862, -862, -862, -862, + -862, -862, -862, -862, -862, -862, -862, -862, -862, -862, + -862, -862, -862, -862, -862, -862, -862, -862, -862, -862, + -862, -862, -862, -862, -862, -862, -862, -862, -862, -862, + -862, -862, -862, -862, -862, -862, -862, -862, -862, -862, + -862, -862, -862, -862, -862, -862, -862, -862, -862, -862, + -862, -862, -862, -862, -862, -862, -862, -862 + }, + + { + 55, -863, -863, -863, -863, -863, -863, -863, -863, -863, + + -863, -863, -863, -863, -863, -863, -863, -863, -863, -863, + -863, -863, -863, -863, -863, -863, -863, -863, -863, -863, + -863, -863, -863, -863, -863, -863, -863, -863, -863, -863, + -863, -863, -863, -863, -863, -863, -863, -863, -863, -863, + -863, -863, -863, -863, -863, -863, -863, -863, -863, -863, + -863, -863, -863, -863, -863, -863, -863, -863, -863, -863, + -863, -863, -863, -863, -863, -863, -863, -863, -863, -863, + -863, -863, -863, -863, -863, -863, -863, -863, -863, -863, + -863, -863, -863, -863, -863, -863, -863, -863, -863, -863, + -863, -863, -863, -863, -863, -863, -863, -863, -863, -863, + + -863, -863, -863, -863, -863, -863, -863, -863, -863, -863, + -863, -863, -863, -863, -863, -863, -863, -863 + }, + + { + 55, -864, -864, -864, -864, -864, -864, -864, -864, -864, + -864, -864, -864, -864, -864, -864, -864, -864, -864, -864, + -864, -864, -864, -864, -864, -864, -864, -864, -864, -864, + -864, -864, -864, -864, -864, -864, -864, -864, -864, -864, + -864, -864, -864, -864, -864, -864, -864, -864, -864, -864, + -864, -864, -864, -864, -864, -864, -864, -864, -864, -864, + -864, -864, -864, -864, -864, -864, -864, -864, -864, -864, + -864, -864, -864, -864, -864, -864, -864, -864, -864, -864, + + -864, -864, -864, -864, -864, -864, -864, -864, -864, -864, + -864, -864, -864, -864, -864, -864, -864, -864, -864, -864, + -864, -864, -864, -864, -864, -864, -864, -864, -864, -864, + -864, -864, -864, -864, -864, -864, -864, -864, -864, -864, + -864, -864, -864, -864, -864, -864, -864, -864 + }, + + { + 55, -865, -865, -865, -865, -865, -865, -865, -865, -865, + -865, -865, -865, -865, -865, -865, -865, -865, -865, -865, + -865, -865, -865, -865, -865, -865, -865, -865, -865, -865, + -865, -865, -865, -865, -865, -865, -865, -865, -865, -865, + -865, -865, -865, -865, -865, -865, -865, -865, -865, -865, + + -865, -865, -865, -865, -865, -865, -865, -865, -865, -865, + -865, -865, -865, -865, -865, -865, -865, -865, -865, -865, + -865, -865, -865, -865, -865, -865, -865, -865, -865, -865, + -865, -865, -865, -865, -865, -865, -865, -865, -865, -865, + -865, -865, -865, -865, -865, -865, -865, -865, -865, -865, + -865, -865, -865, -865, -865, -865, -865, -865, -865, -865, + -865, -865, -865, -865, -865, -865, -865, -865, -865, -865, + -865, -865, -865, -865, -865, -865, -865, -865 + }, + + { + 55, -866, -866, -866, -866, -866, -866, -866, -866, -866, + -866, -866, -866, -866, -866, -866, -866, -866, -866, -866, + + -866, -866, -866, -866, -866, -866, -866, -866, -866, -866, + -866, -866, -866, -866, -866, -866, -866, -866, -866, -866, + -866, -866, -866, -866, -866, -866, -866, -866, -866, -866, + -866, -866, -866, -866, -866, -866, -866, -866, -866, -866, + -866, -866, -866, -866, -866, -866, -866, -866, -866, -866, + -866, -866, -866, -866, -866, -866, -866, -866, -866, -866, + -866, -866, -866, -866, -866, -866, -866, -866, -866, -866, + -866, -866, -866, -866, -866, -866, -866, -866, -866, -866, + -866, -866, -866, -866, -866, -866, -866, -866, -866, -866, + -866, -866, -866, -866, -866, -866, -866, -866, -866, -866, + + -866, -866, -866, -866, -866, -866, -866, -866 + }, + + { + 55, -867, -867, -867, -867, -867, -867, -867, -867, -867, + -867, -867, -867, -867, -867, -867, -867, -867, -867, -867, + -867, -867, -867, -867, -867, -867, -867, -867, -867, -867, + -867, -867, -867, -867, -867, -867, -867, -867, -867, -867, + -867, -867, -867, -867, -867, -867, -867, -867, -867, -867, + -867, -867, -867, -867, -867, -867, -867, -867, -867, -867, + -867, -867, -867, -867, -867, -867, -867, -867, -867, -867, + -867, -867, -867, -867, -867, -867, -867, -867, -867, -867, + -867, -867, -867, -867, -867, -867, -867, -867, -867, -867, + + -867, -867, -867, -867, -867, -867, -867, -867, -867, -867, + -867, -867, -867, -867, -867, -867, -867, -867, -867, -867, + -867, -867, -867, -867, -867, -867, -867, -867, -867, -867, + -867, -867, -867, -867, -867, -867, -867, -867 + }, + + { + 55, -868, -868, -868, -868, -868, -868, -868, -868, -868, + -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, + -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, + -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, + -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, + -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, + + -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, + -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, + -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, + -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, + -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, + -868, -868, -868, -868, -868, -868, -868, -868, -868, -868, + -868, -868, -868, -868, -868, -868, -868, -868 + }, + + { + 55, -869, -869, -869, -869, -869, -869, -869, -869, -869, + -869, -869, -869, -869, -869, -869, -869, -869, -869, -869, + -869, -869, -869, -869, -869, -869, -869, -869, -869, -869, + + -869, -869, -869, -869, -869, -869, -869, -869, -869, -869, + -869, -869, -869, -869, -869, -869, -869, -869, -869, -869, + -869, -869, -869, -869, -869, -869, -869, -869, -869, -869, + -869, -869, -869, -869, -869, -869, -869, -869, -869, -869, + -869, -869, -869, -869, -869, -869, -869, -869, -869, -869, + -869, -869, -869, -869, -869, -869, -869, -869, -869, -869, + -869, -869, -869, -869, -869, -869, -869, -869, -869, -869, + -869, -869, -869, -869, -869, -869, -869, -869, -869, -869, + -869, -869, -869, -869, -869, -869, -869, -869, -869, -869, + -869, -869, -869, -869, -869, -869, -869, -869 + + }, + + { + 55, -870, -870, -870, -870, -870, -870, -870, -870, -870, + -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, + -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, + -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, + -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, + -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, + -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, + -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, + -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, + -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, + + -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, + -870, -870, -870, -870, -870, -870, -870, -870, -870, -870, + -870, -870, -870, -870, -870, -870, -870, -870 + }, + + { + 55, -871, -871, -871, -871, -871, -871, -871, -871, -871, + -871, -871, -871, -871, -871, -871, -871, -871, -871, -871, + -871, -871, -871, -871, -871, -871, -871, -871, -871, -871, + -871, -871, -871, -871, -871, -871, -871, -871, -871, -871, + -871, -871, -871, -871, -871, -871, -871, -871, -871, -871, + -871, -871, -871, -871, -871, -871, -871, -871, -871, -871, + -871, -871, -871, -871, -871, -871, -871, -871, -871, -871, + + -871, -871, -871, -871, -871, -871, -871, -871, -871, -871, + -871, -871, -871, -871, -871, -871, -871, -871, -871, -871, + -871, -871, -871, -871, -871, -871, -871, -871, -871, -871, + -871, -871, -871, -871, -871, -871, -871, -871, -871, -871, + -871, -871, -871, -871, -871, -871, -871, -871, -871, -871, + -871, -871, -871, -871, -871, -871, -871, -871 + }, + + { + 55, -872, -872, -872, -872, -872, -872, -872, -872, -872, + -872, -872, -872, -872, -872, -872, -872, -872, -872, -872, + -872, -872, -872, -872, -872, -872, -872, -872, -872, -872, + -872, -872, -872, -872, -872, -872, -872, -872, -872, -872, + + -872, -872, -872, -872, -872, -872, -872, -872, -872, -872, + -872, -872, -872, -872, -872, -872, -872, -872, -872, -872, + -872, -872, -872, -872, -872, -872, -872, -872, -872, -872, + -872, -872, -872, -872, -872, -872, -872, -872, -872, -872, + -872, -872, -872, -872, -872, -872, -872, -872, -872, -872, + -872, -872, -872, -872, -872, -872, -872, -872, -872, -872, + -872, -872, -872, -872, -872, -872, -872, -872, -872, -872, + -872, -872, -872, -872, -872, -872, -872, -872, -872, -872, + -872, -872, -872, -872, -872, -872, -872, -872 + }, + + { + 55, -873, -873, -873, -873, -873, -873, -873, -873, -873, + + -873, -873, -873, -873, -873, -873, -873, -873, -873, -873, + -873, -873, -873, -873, -873, -873, -873, -873, -873, -873, + -873, -873, -873, -873, -873, -873, -873, -873, -873, -873, + -873, -873, -873, -873, -873, -873, -873, -873, -873, -873, + -873, -873, -873, -873, -873, -873, -873, -873, -873, -873, + -873, -873, -873, -873, -873, -873, -873, -873, -873, -873, + -873, -873, -873, -873, -873, -873, -873, -873, -873, -873, + -873, -873, -873, -873, -873, -873, -873, -873, -873, -873, + -873, -873, -873, -873, -873, -873, -873, -873, -873, -873, + -873, -873, -873, -873, -873, -873, -873, -873, -873, -873, + + -873, -873, -873, -873, -873, -873, -873, -873, -873, -873, + -873, -873, -873, -873, -873, -873, -873, -873 + }, + + { + 55, -874, -874, -874, -874, -874, -874, -874, -874, -874, + -874, -874, -874, -874, -874, -874, -874, -874, -874, -874, + -874, -874, -874, -874, -874, -874, -874, -874, -874, -874, + -874, -874, -874, -874, -874, -874, -874, -874, -874, -874, + -874, -874, -874, -874, -874, -874, -874, -874, -874, -874, + -874, -874, -874, -874, -874, -874, -874, -874, -874, -874, + -874, -874, -874, -874, -874, -874, -874, -874, -874, -874, + -874, -874, -874, -874, -874, -874, -874, -874, -874, -874, + + -874, -874, -874, -874, -874, -874, -874, -874, -874, -874, + -874, -874, -874, -874, -874, -874, -874, -874, -874, -874, + -874, -874, -874, -874, -874, -874, -874, -874, -874, -874, + -874, -874, -874, -874, -874, -874, -874, -874, -874, -874, + -874, -874, -874, -874, -874, -874, -874, -874 + }, + + { + 55, -875, -875, -875, -875, -875, -875, -875, -875, -875, + -875, -875, -875, -875, -875, -875, -875, -875, -875, -875, + -875, -875, -875, -875, -875, -875, -875, -875, -875, -875, + -875, -875, -875, -875, -875, -875, -875, -875, -875, -875, + -875, -875, -875, -875, -875, -875, -875, -875, -875, -875, + + -875, -875, -875, -875, -875, -875, -875, -875, -875, -875, + -875, -875, -875, -875, -875, -875, -875, -875, -875, -875, + -875, -875, -875, -875, -875, -875, -875, -875, -875, -875, + -875, -875, -875, -875, -875, -875, -875, -875, -875, -875, + -875, -875, -875, -875, -875, -875, -875, -875, -875, -875, + -875, -875, -875, -875, -875, -875, -875, -875, -875, -875, + -875, -875, -875, -875, -875, -875, -875, -875, -875, -875, + -875, -875, -875, -875, -875, -875, -875, -875 + }, + + { + 55, -876, -876, -876, -876, -876, -876, -876, -876, -876, + -876, -876, -876, -876, -876, -876, -876, -876, -876, -876, + + -876, -876, -876, -876, -876, -876, -876, -876, -876, -876, + -876, -876, -876, -876, -876, -876, -876, -876, -876, -876, + -876, -876, -876, -876, -876, -876, -876, -876, -876, -876, + -876, -876, -876, -876, -876, -876, -876, -876, -876, -876, + -876, -876, -876, -876, -876, -876, -876, -876, -876, -876, + -876, -876, -876, -876, -876, -876, -876, -876, -876, -876, + -876, -876, -876, -876, -876, -876, -876, -876, -876, -876, + -876, -876, -876, -876, -876, -876, -876, -876, -876, -876, + -876, -876, -876, -876, -876, -876, -876, -876, -876, -876, + -876, -876, -876, -876, -876, -876, -876, -876, -876, -876, + + -876, -876, -876, -876, -876, -876, -876, -876 + }, + + { + 55, -877, -877, -877, -877, -877, -877, -877, -877, -877, + -877, -877, -877, -877, -877, -877, -877, -877, -877, -877, + -877, -877, -877, -877, -877, -877, -877, -877, -877, -877, + -877, -877, -877, -877, -877, -877, -877, -877, -877, -877, + -877, -877, -877, -877, -877, -877, -877, -877, -877, -877, + -877, -877, -877, -877, -877, -877, -877, -877, -877, -877, + -877, -877, -877, -877, -877, -877, -877, -877, -877, -877, + -877, -877, -877, -877, -877, -877, -877, -877, -877, -877, + -877, -877, -877, -877, -877, -877, -877, -877, -877, -877, + + -877, -877, -877, -877, -877, -877, -877, -877, -877, -877, + -877, -877, -877, -877, -877, -877, -877, -877, -877, -877, + -877, -877, -877, -877, -877, -877, -877, -877, -877, -877, + -877, -877, -877, -877, -877, -877, -877, -877 + }, + + { + 55, -878, -878, -878, -878, -878, -878, -878, -878, -878, + -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, + -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, + -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, + -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, + -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, + + -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, + -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, + -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, + -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, + -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, + -878, -878, -878, -878, -878, -878, -878, -878, -878, -878, + -878, -878, -878, -878, -878, -878, -878, -878 + }, + + { + 55, -879, -879, -879, -879, -879, -879, -879, -879, -879, + -879, -879, -879, -879, -879, -879, -879, -879, -879, -879, + -879, -879, -879, -879, -879, -879, -879, -879, -879, -879, + + -879, -879, -879, -879, -879, -879, -879, -879, -879, -879, + -879, -879, -879, -879, -879, -879, -879, -879, -879, -879, + -879, -879, -879, -879, -879, -879, -879, -879, -879, -879, + -879, -879, -879, -879, -879, -879, -879, -879, -879, -879, + -879, -879, -879, -879, -879, -879, -879, -879, -879, -879, + -879, -879, -879, -879, -879, -879, -879, -879, -879, -879, + -879, -879, -879, -879, -879, -879, -879, -879, -879, -879, + -879, -879, -879, -879, -879, -879, -879, -879, -879, -879, + -879, -879, -879, -879, -879, -879, -879, -879, -879, -879, + -879, -879, -879, -879, -879, -879, -879, -879 + + }, + + { + 55, -880, -880, -880, -880, -880, -880, -880, -880, -880, + -880, -880, -880, -880, -880, -880, -880, -880, -880, -880, + -880, -880, -880, -880, -880, -880, -880, -880, -880, -880, + -880, -880, -880, -880, -880, -880, -880, -880, -880, -880, + -880, -880, -880, -880, -880, -880, -880, -880, -880, -880, + -880, -880, -880, -880, -880, -880, -880, -880, -880, -880, + -880, -880, -880, -880, -880, -880, -880, -880, -880, -880, + -880, -880, -880, -880, -880, -880, -880, -880, -880, -880, + -880, -880, -880, -880, -880, -880, -880, -880, -880, -880, + -880, -880, -880, -880, -880, -880, -880, -880, -880, -880, + + -880, -880, -880, -880, -880, -880, -880, -880, -880, -880, + -880, -880, -880, -880, -880, -880, -880, -880, -880, -880, + -880, -880, -880, -880, -880, -880, -880, -880 + }, + + { + 55, -881, -881, -881, -881, -881, -881, -881, -881, -881, + -881, -881, -881, -881, -881, -881, -881, -881, -881, -881, + -881, -881, -881, -881, -881, -881, -881, -881, -881, -881, + -881, -881, -881, -881, -881, -881, -881, -881, -881, -881, + -881, -881, -881, -881, -881, -881, -881, -881, -881, -881, + -881, -881, -881, -881, -881, -881, -881, -881, -881, -881, + -881, -881, -881, -881, -881, -881, -881, -881, -881, -881, + + -881, -881, -881, -881, -881, -881, -881, -881, -881, -881, + -881, -881, -881, -881, -881, -881, -881, -881, -881, -881, + -881, -881, -881, -881, -881, -881, -881, -881, -881, -881, + -881, -881, -881, -881, -881, -881, -881, -881, -881, -881, + -881, -881, -881, -881, -881, -881, -881, -881, -881, -881, + -881, -881, -881, -881, -881, -881, -881, -881 + }, + + { + 55, -882, -882, -882, -882, -882, -882, -882, -882, -882, + -882, -882, -882, -882, -882, -882, -882, -882, -882, -882, + -882, -882, -882, -882, -882, -882, -882, -882, -882, -882, + -882, -882, -882, -882, -882, -882, -882, -882, -882, -882, + + -882, -882, -882, -882, -882, -882, -882, -882, -882, -882, + -882, -882, -882, -882, -882, -882, -882, -882, -882, -882, + -882, -882, -882, -882, -882, -882, -882, -882, -882, -882, + -882, -882, -882, -882, -882, -882, -882, -882, -882, -882, + -882, -882, -882, -882, -882, -882, -882, -882, -882, -882, + -882, -882, -882, -882, -882, -882, -882, -882, -882, -882, + -882, -882, -882, -882, -882, -882, -882, -882, -882, -882, + -882, -882, -882, -882, -882, -882, -882, -882, -882, -882, + -882, -882, -882, -882, -882, -882, -882, -882 + }, + + { + 55, -883, -883, -883, -883, -883, -883, -883, -883, -883, + + -883, -883, -883, -883, -883, -883, -883, -883, -883, -883, + -883, -883, -883, -883, -883, -883, -883, -883, -883, -883, + -883, -883, -883, -883, -883, -883, -883, -883, -883, -883, + -883, -883, -883, -883, -883, -883, -883, -883, -883, -883, + -883, -883, -883, -883, -883, -883, -883, -883, -883, -883, + -883, -883, -883, -883, -883, -883, -883, -883, -883, -883, + -883, -883, -883, -883, -883, -883, -883, -883, -883, -883, + -883, -883, -883, -883, -883, -883, -883, -883, -883, -883, + -883, -883, -883, -883, -883, -883, -883, -883, -883, -883, + -883, -883, -883, -883, -883, -883, -883, -883, -883, -883, + + -883, -883, -883, -883, -883, -883, -883, -883, -883, -883, + -883, -883, -883, -883, -883, -883, -883, -883 + }, + + { + 55, -884, -884, -884, -884, -884, -884, -884, -884, -884, + -884, -884, -884, -884, -884, -884, -884, -884, -884, -884, + -884, -884, -884, -884, -884, -884, -884, -884, -884, -884, + -884, -884, -884, -884, -884, -884, -884, -884, -884, -884, + -884, -884, -884, -884, -884, -884, -884, -884, -884, -884, + -884, -884, -884, -884, -884, -884, -884, -884, -884, -884, + -884, -884, -884, -884, -884, -884, -884, -884, -884, -884, + -884, -884, -884, -884, -884, -884, -884, -884, -884, -884, + + -884, -884, -884, -884, -884, -884, -884, -884, -884, -884, + -884, -884, -884, -884, -884, -884, -884, -884, -884, -884, + -884, -884, -884, -884, -884, -884, -884, -884, -884, -884, + -884, -884, -884, -884, -884, -884, -884, -884, -884, -884, + -884, -884, -884, -884, -884, -884, -884, -884 + }, + + { + 55, -885, -885, -885, -885, -885, -885, -885, -885, -885, + -885, -885, -885, -885, -885, -885, -885, -885, -885, -885, + -885, -885, -885, -885, -885, -885, -885, -885, -885, -885, + -885, -885, -885, -885, -885, -885, -885, -885, -885, -885, + -885, -885, -885, -885, -885, -885, -885, -885, -885, -885, + + -885, -885, -885, -885, -885, -885, -885, -885, -885, -885, + -885, -885, -885, -885, -885, -885, -885, -885, -885, -885, + -885, -885, -885, -885, -885, -885, -885, -885, -885, -885, + -885, -885, -885, -885, -885, -885, -885, -885, -885, -885, + -885, -885, -885, -885, -885, -885, -885, -885, -885, -885, + -885, -885, -885, -885, -885, -885, -885, -885, -885, -885, + -885, -885, -885, -885, -885, -885, -885, -885, -885, -885, + -885, -885, -885, -885, -885, -885, -885, -885 + }, + + { + 55, -886, -886, -886, -886, -886, -886, -886, -886, -886, + -886, -886, -886, -886, -886, -886, -886, -886, -886, -886, + + -886, -886, -886, -886, -886, -886, -886, -886, -886, -886, + -886, -886, -886, -886, -886, -886, -886, -886, -886, -886, + -886, -886, -886, -886, -886, -886, -886, -886, -886, -886, + -886, -886, -886, -886, -886, -886, -886, -886, -886, -886, + -886, -886, -886, -886, -886, -886, -886, -886, -886, -886, + -886, -886, -886, -886, -886, -886, -886, -886, -886, -886, + -886, -886, -886, -886, -886, -886, -886, -886, -886, -886, + -886, -886, -886, -886, -886, -886, -886, -886, -886, -886, + -886, -886, -886, -886, -886, -886, -886, -886, -886, -886, + -886, -886, -886, -886, -886, -886, -886, -886, -886, -886, + + -886, -886, -886, -886, -886, -886, -886, -886 + }, + + { + 55, -887, -887, -887, -887, -887, -887, -887, -887, -887, + -887, -887, -887, -887, -887, -887, -887, -887, -887, -887, + -887, -887, -887, -887, -887, -887, -887, -887, -887, -887, + -887, -887, -887, -887, -887, -887, -887, -887, -887, -887, + -887, -887, -887, -887, -887, -887, -887, -887, -887, -887, + -887, -887, -887, -887, -887, -887, -887, -887, -887, -887, + -887, -887, -887, -887, -887, -887, -887, -887, -887, -887, + -887, -887, -887, -887, -887, -887, -887, -887, -887, -887, + -887, -887, -887, -887, -887, -887, -887, -887, -887, -887, + + -887, -887, -887, -887, -887, -887, -887, -887, -887, -887, + -887, -887, -887, -887, -887, -887, -887, -887, -887, -887, + -887, -887, -887, -887, -887, -887, -887, -887, -887, -887, + -887, -887, -887, -887, -887, -887, -887, -887 + }, + + { + 55, -888, -888, -888, -888, -888, -888, -888, -888, -888, + -888, -888, -888, -888, -888, -888, -888, -888, -888, -888, + -888, -888, -888, -888, -888, -888, -888, -888, -888, -888, + -888, -888, -888, -888, -888, -888, -888, -888, -888, -888, + -888, -888, -888, -888, -888, -888, -888, -888, -888, -888, + -888, -888, -888, -888, -888, -888, -888, -888, -888, -888, + + -888, -888, -888, -888, -888, -888, -888, -888, -888, -888, + -888, -888, -888, -888, -888, -888, -888, -888, -888, -888, + -888, -888, -888, -888, -888, -888, -888, -888, -888, -888, + -888, -888, -888, -888, -888, -888, -888, -888, -888, -888, + -888, -888, -888, -888, -888, -888, -888, -888, -888, -888, + -888, -888, -888, -888, -888, -888, -888, -888, -888, -888, + -888, -888, -888, -888, -888, -888, -888, -888 + }, + + { + 55, -889, -889, -889, -889, -889, -889, -889, -889, -889, + -889, -889, -889, -889, -889, -889, -889, -889, -889, -889, + -889, -889, -889, -889, -889, -889, -889, -889, -889, -889, + + -889, -889, -889, -889, -889, -889, -889, -889, -889, -889, + -889, -889, -889, -889, -889, -889, -889, -889, -889, -889, + -889, -889, -889, -889, -889, -889, -889, -889, -889, -889, + -889, -889, -889, -889, -889, -889, -889, -889, -889, -889, + -889, -889, -889, -889, -889, -889, -889, -889, -889, -889, + -889, -889, -889, -889, -889, -889, -889, -889, -889, -889, + -889, -889, -889, -889, -889, -889, -889, -889, -889, -889, + -889, -889, -889, -889, -889, -889, -889, -889, -889, -889, + -889, -889, -889, -889, -889, -889, -889, -889, -889, -889, + -889, -889, -889, -889, -889, -889, -889, -889 + + }, + + { + 55, -890, -890, -890, -890, -890, -890, -890, -890, -890, + -890, -890, -890, -890, -890, -890, -890, -890, -890, -890, + -890, -890, -890, -890, -890, -890, -890, -890, -890, -890, + -890, -890, -890, -890, -890, -890, -890, -890, -890, -890, + -890, -890, -890, -890, -890, -890, -890, -890, -890, -890, + -890, -890, -890, -890, -890, -890, -890, -890, -890, -890, + -890, -890, -890, -890, -890, -890, -890, -890, -890, -890, + -890, -890, -890, -890, -890, -890, -890, -890, -890, -890, + -890, -890, -890, -890, -890, -890, -890, -890, -890, -890, + -890, -890, -890, -890, -890, -890, -890, -890, -890, -890, + + -890, -890, -890, -890, -890, -890, -890, -890, -890, -890, + -890, -890, -890, -890, -890, -890, -890, -890, -890, -890, + -890, -890, -890, -890, -890, -890, -890, -890 + }, + + { + 55, -891, -891, -891, -891, -891, -891, -891, -891, -891, + -891, -891, -891, -891, -891, -891, -891, -891, -891, -891, + -891, -891, -891, -891, -891, -891, -891, -891, -891, -891, + -891, -891, -891, -891, -891, -891, -891, -891, -891, -891, + -891, -891, -891, -891, -891, -891, -891, -891, -891, -891, + -891, -891, -891, -891, -891, -891, -891, -891, -891, -891, + -891, -891, -891, -891, -891, -891, -891, -891, -891, -891, + + -891, -891, -891, -891, -891, -891, -891, -891, -891, -891, + -891, -891, -891, -891, -891, -891, -891, -891, -891, -891, + -891, -891, -891, -891, -891, -891, -891, -891, -891, -891, + -891, -891, -891, -891, -891, -891, -891, -891, -891, -891, + -891, -891, -891, -891, -891, -891, -891, -891, -891, -891, + -891, -891, -891, -891, -891, -891, -891, -891 + }, + + { + 55, -892, -892, -892, -892, -892, -892, -892, -892, -892, + -892, -892, -892, -892, -892, -892, -892, -892, -892, -892, + -892, -892, -892, -892, -892, -892, -892, -892, -892, -892, + -892, -892, -892, -892, -892, -892, -892, -892, -892, -892, + + -892, -892, -892, -892, -892, -892, -892, -892, -892, -892, + -892, -892, -892, -892, -892, -892, -892, -892, -892, -892, + -892, -892, -892, -892, -892, -892, -892, -892, -892, -892, + -892, -892, -892, -892, -892, -892, -892, -892, -892, -892, + -892, -892, -892, -892, -892, -892, -892, -892, -892, -892, + -892, -892, -892, -892, -892, -892, -892, -892, -892, -892, + -892, -892, -892, -892, -892, -892, -892, -892, -892, -892, + -892, -892, -892, -892, -892, -892, -892, -892, -892, -892, + -892, -892, -892, -892, -892, -892, -892, -892 + }, + + { + 55, -893, -893, -893, -893, -893, -893, -893, -893, -893, + + -893, -893, -893, -893, -893, -893, -893, -893, -893, -893, + -893, -893, -893, -893, -893, -893, -893, -893, -893, -893, + -893, -893, -893, -893, -893, -893, -893, -893, -893, -893, + -893, -893, -893, -893, -893, -893, -893, -893, -893, -893, + -893, -893, -893, -893, -893, -893, -893, -893, -893, -893, + -893, -893, -893, -893, -893, -893, -893, -893, -893, -893, + -893, -893, -893, -893, -893, -893, -893, -893, -893, -893, + -893, -893, -893, -893, -893, -893, -893, -893, -893, -893, + -893, -893, -893, -893, -893, -893, -893, -893, -893, -893, + -893, -893, -893, -893, -893, -893, -893, -893, -893, -893, + + -893, -893, -893, -893, -893, -893, -893, -893, -893, -893, + -893, -893, -893, -893, -893, -893, -893, -893 + }, + + { + 55, -894, -894, -894, -894, -894, -894, -894, -894, -894, + -894, -894, -894, -894, -894, -894, -894, -894, -894, -894, + -894, -894, -894, -894, -894, -894, -894, -894, -894, -894, + -894, -894, -894, -894, -894, -894, -894, -894, -894, -894, + -894, -894, -894, -894, -894, -894, -894, -894, -894, -894, + -894, -894, -894, -894, -894, -894, -894, -894, -894, -894, + -894, -894, -894, -894, -894, -894, -894, -894, -894, -894, + -894, -894, -894, -894, -894, -894, -894, -894, -894, -894, + + -894, -894, -894, -894, -894, -894, -894, -894, -894, -894, + -894, -894, -894, -894, -894, -894, -894, -894, -894, -894, + -894, -894, -894, -894, -894, -894, -894, -894, -894, -894, + -894, -894, -894, -894, -894, -894, -894, -894, -894, -894, + -894, -894, -894, -894, -894, -894, -894, -894 + }, + + { + 55, -895, -895, -895, -895, -895, -895, -895, -895, -895, + -895, -895, -895, -895, -895, -895, -895, -895, -895, -895, + -895, -895, -895, -895, -895, -895, -895, -895, -895, -895, + -895, -895, -895, -895, -895, -895, -895, -895, -895, -895, + -895, -895, -895, -895, -895, -895, -895, -895, -895, -895, + + -895, -895, -895, -895, -895, -895, -895, -895, -895, -895, + -895, -895, -895, -895, -895, -895, -895, -895, -895, -895, + -895, -895, -895, -895, -895, -895, -895, -895, -895, -895, + -895, -895, -895, -895, -895, -895, -895, -895, -895, -895, + -895, -895, -895, -895, -895, -895, -895, -895, -895, -895, + -895, -895, -895, -895, -895, -895, -895, -895, -895, -895, + -895, -895, -895, -895, -895, -895, -895, -895, -895, -895, + -895, -895, -895, -895, -895, -895, -895, -895 + }, + + { + 55, -896, -896, -896, -896, -896, -896, -896, -896, -896, + -896, -896, -896, -896, -896, -896, -896, -896, -896, -896, + + -896, -896, -896, -896, -896, -896, -896, -896, -896, -896, + -896, -896, -896, -896, -896, -896, -896, -896, -896, -896, + -896, -896, -896, -896, -896, -896, -896, -896, -896, -896, + -896, -896, -896, -896, -896, -896, -896, -896, -896, -896, + -896, -896, -896, -896, -896, -896, -896, -896, -896, -896, + -896, -896, -896, -896, -896, -896, -896, -896, -896, -896, + -896, -896, -896, -896, -896, -896, -896, -896, -896, -896, + -896, -896, -896, -896, -896, -896, -896, -896, -896, -896, + -896, -896, -896, -896, -896, -896, -896, -896, -896, -896, + -896, -896, -896, -896, -896, -896, -896, -896, -896, -896, + + -896, -896, -896, -896, -896, -896, -896, -896 + }, + + { + 55, -897, -897, -897, -897, -897, -897, -897, -897, -897, + -897, -897, -897, -897, -897, -897, -897, -897, -897, -897, + -897, -897, -897, -897, -897, -897, -897, -897, -897, -897, + -897, -897, -897, -897, -897, -897, -897, -897, -897, -897, + -897, -897, -897, -897, -897, -897, -897, -897, -897, -897, + -897, -897, -897, -897, -897, -897, -897, -897, -897, -897, + -897, -897, -897, -897, -897, -897, -897, -897, -897, -897, + -897, -897, -897, -897, -897, -897, -897, -897, -897, -897, + -897, -897, -897, -897, -897, -897, -897, -897, -897, -897, + + -897, -897, -897, -897, -897, -897, -897, -897, -897, -897, + -897, -897, -897, -897, -897, -897, -897, -897, -897, -897, + -897, -897, -897, -897, -897, -897, -897, -897, -897, -897, + -897, -897, -897, -897, -897, -897, -897, -897 + }, + + { + 55, -898, -898, -898, -898, -898, -898, -898, -898, -898, + -898, -898, -898, -898, -898, -898, -898, -898, -898, -898, + -898, -898, -898, -898, -898, -898, -898, -898, -898, -898, + -898, -898, -898, -898, -898, -898, -898, -898, -898, -898, + -898, -898, -898, -898, -898, -898, -898, -898, -898, -898, + -898, -898, -898, -898, -898, -898, -898, -898, -898, -898, + + -898, -898, -898, -898, -898, -898, -898, -898, -898, -898, + -898, -898, -898, -898, -898, -898, -898, -898, -898, -898, + -898, -898, -898, -898, -898, -898, -898, -898, -898, -898, + -898, -898, -898, -898, -898, -898, -898, -898, -898, -898, + -898, -898, -898, -898, -898, -898, -898, -898, -898, -898, + -898, -898, -898, -898, -898, -898, -898, -898, -898, -898, + -898, -898, -898, -898, -898, -898, -898, -898 + }, + + { + 55, -899, -899, -899, -899, -899, -899, -899, -899, -899, + -899, -899, -899, -899, -899, -899, -899, -899, -899, -899, + -899, -899, -899, -899, -899, -899, -899, -899, -899, -899, + + -899, -899, -899, -899, -899, -899, -899, -899, -899, -899, + -899, -899, -899, -899, -899, -899, -899, -899, -899, -899, + -899, -899, -899, -899, -899, -899, -899, -899, -899, -899, + -899, -899, -899, -899, -899, -899, -899, -899, -899, -899, + -899, -899, -899, -899, -899, -899, -899, -899, -899, -899, + -899, -899, -899, -899, -899, -899, -899, -899, -899, -899, + -899, -899, -899, -899, -899, -899, -899, -899, -899, -899, + -899, -899, -899, -899, -899, -899, -899, -899, -899, -899, + -899, -899, -899, -899, -899, -899, -899, -899, -899, -899, + -899, -899, -899, -899, -899, -899, -899, -899 + + }, + + { + 55, -900, -900, -900, -900, -900, -900, -900, -900, -900, + -900, -900, -900, -900, -900, -900, -900, -900, -900, -900, + -900, -900, -900, -900, -900, -900, -900, -900, -900, -900, + -900, -900, -900, -900, -900, -900, -900, -900, -900, -900, + -900, -900, -900, -900, -900, -900, -900, -900, -900, -900, + -900, -900, -900, -900, -900, -900, -900, -900, -900, -900, + -900, -900, -900, -900, -900, -900, -900, -900, -900, -900, + -900, -900, -900, -900, -900, -900, -900, -900, -900, -900, + -900, -900, -900, -900, -900, -900, -900, -900, -900, -900, + -900, -900, -900, -900, -900, -900, -900, -900, -900, -900, + + -900, -900, -900, -900, -900, -900, -900, -900, -900, -900, + -900, -900, -900, -900, -900, -900, -900, -900, -900, -900, + -900, -900, -900, -900, -900, -900, -900, -900 + }, + + { + 55, -901, -901, -901, -901, -901, -901, -901, -901, -901, + -901, -901, -901, -901, -901, -901, -901, -901, -901, -901, + -901, -901, -901, -901, -901, -901, -901, -901, -901, -901, + -901, -901, -901, -901, -901, -901, -901, -901, -901, -901, + -901, -901, -901, -901, -901, -901, -901, -901, -901, -901, + -901, -901, -901, -901, -901, -901, -901, -901, -901, -901, + -901, -901, -901, -901, -901, -901, -901, -901, -901, -901, + + -901, -901, -901, -901, -901, -901, -901, -901, -901, -901, + -901, -901, -901, -901, -901, -901, -901, -901, -901, -901, + -901, -901, -901, -901, -901, -901, -901, -901, -901, -901, + -901, -901, -901, -901, -901, -901, -901, -901, -901, -901, + -901, -901, -901, -901, -901, -901, -901, -901, -901, -901, + -901, -901, -901, -901, -901, -901, -901, -901 + }, + + { + 55, -902, -902, -902, -902, -902, -902, -902, -902, -902, + -902, -902, -902, -902, -902, -902, -902, -902, -902, -902, + -902, -902, -902, -902, -902, -902, -902, -902, -902, -902, + -902, -902, -902, -902, -902, -902, -902, -902, -902, -902, + + -902, -902, -902, -902, -902, -902, -902, -902, -902, -902, + -902, -902, -902, -902, -902, -902, -902, -902, -902, -902, + -902, -902, -902, -902, -902, -902, -902, -902, -902, -902, + -902, -902, -902, -902, -902, -902, -902, -902, -902, -902, + -902, -902, -902, -902, -902, -902, -902, -902, -902, -902, + -902, -902, -902, -902, -902, -902, -902, -902, -902, -902, + -902, -902, -902, -902, -902, -902, -902, -902, -902, -902, + -902, -902, -902, -902, -902, -902, -902, -902, -902, -902, + -902, -902, -902, -902, -902, -902, -902, -902 + }, + + { + 55, -903, -903, -903, -903, -903, -903, -903, -903, -903, + + -903, -903, -903, -903, -903, -903, -903, -903, -903, -903, + -903, -903, -903, -903, -903, -903, -903, -903, -903, -903, + -903, -903, -903, -903, -903, -903, -903, -903, -903, -903, + -903, -903, -903, -903, -903, -903, -903, -903, -903, -903, + -903, -903, -903, -903, -903, -903, -903, -903, -903, -903, + -903, -903, -903, -903, -903, -903, -903, -903, -903, -903, + -903, -903, -903, -903, -903, -903, -903, -903, -903, -903, + -903, -903, -903, -903, -903, -903, -903, -903, -903, -903, + -903, -903, -903, -903, -903, -903, -903, -903, -903, -903, + -903, -903, -903, -903, -903, -903, -903, -903, -903, -903, + + -903, -903, -903, -903, -903, -903, -903, -903, -903, -903, + -903, -903, -903, -903, -903, -903, -903, -903 + }, + + { + 55, -904, -904, -904, -904, -904, -904, -904, -904, -904, + -904, -904, -904, -904, -904, -904, -904, -904, -904, -904, + -904, -904, -904, -904, -904, -904, -904, -904, -904, -904, + -904, -904, -904, -904, -904, -904, -904, -904, -904, -904, + -904, -904, -904, -904, -904, -904, -904, -904, -904, -904, + -904, -904, -904, -904, -904, -904, -904, -904, -904, -904, + -904, -904, -904, -904, -904, -904, -904, -904, -904, -904, + -904, -904, -904, -904, -904, -904, -904, -904, -904, -904, + + -904, -904, -904, -904, -904, -904, -904, -904, -904, -904, + -904, -904, -904, -904, -904, -904, -904, -904, -904, -904, + -904, -904, -904, -904, -904, -904, -904, -904, -904, -904, + -904, -904, -904, -904, -904, -904, -904, -904, -904, -904, + -904, -904, -904, -904, -904, -904, -904, -904 + }, + + { + 55, -905, -905, -905, -905, -905, -905, -905, -905, -905, + -905, -905, -905, -905, -905, -905, -905, -905, -905, -905, + -905, -905, -905, -905, -905, -905, -905, -905, -905, -905, + -905, -905, -905, -905, -905, -905, -905, -905, -905, -905, + -905, -905, -905, -905, -905, -905, -905, -905, -905, -905, + + -905, -905, -905, -905, -905, -905, -905, -905, -905, -905, + -905, -905, -905, -905, -905, -905, -905, -905, -905, -905, + -905, -905, -905, -905, -905, -905, -905, -905, -905, -905, + -905, -905, -905, -905, -905, -905, -905, -905, -905, -905, + -905, -905, -905, -905, -905, -905, -905, -905, -905, -905, + -905, -905, -905, -905, -905, -905, -905, -905, -905, -905, + -905, -905, -905, -905, -905, -905, -905, -905, -905, -905, + -905, -905, -905, -905, -905, -905, -905, -905 + }, + + { + 55, -906, -906, -906, -906, -906, -906, -906, -906, -906, + -906, -906, -906, -906, -906, -906, -906, -906, -906, -906, + + -906, -906, -906, -906, -906, -906, -906, -906, -906, -906, + -906, -906, -906, -906, -906, -906, -906, -906, -906, -906, + -906, -906, -906, -906, -906, -906, -906, -906, -906, -906, + -906, -906, -906, -906, -906, -906, -906, -906, -906, -906, + -906, -906, -906, -906, -906, -906, -906, -906, -906, -906, + -906, -906, -906, -906, -906, -906, -906, -906, -906, -906, + -906, -906, -906, -906, -906, -906, -906, -906, -906, -906, + -906, -906, -906, -906, -906, -906, -906, -906, -906, -906, + -906, -906, -906, -906, -906, -906, -906, -906, -906, -906, + -906, -906, -906, -906, -906, -906, -906, -906, -906, -906, + + -906, -906, -906, -906, -906, -906, -906, -906 + }, + + { + 55, -907, -907, -907, -907, -907, -907, -907, -907, -907, + -907, -907, -907, -907, -907, -907, -907, -907, -907, -907, + -907, -907, -907, -907, -907, -907, -907, -907, -907, -907, + -907, -907, -907, -907, -907, -907, -907, -907, -907, -907, + -907, -907, -907, -907, -907, -907, -907, -907, -907, -907, + -907, -907, -907, -907, -907, -907, -907, -907, -907, -907, + -907, -907, -907, -907, -907, -907, -907, -907, -907, -907, + -907, -907, -907, -907, -907, -907, -907, -907, -907, -907, + -907, -907, -907, -907, -907, -907, -907, -907, -907, -907, + + -907, -907, -907, -907, -907, -907, -907, -907, -907, -907, + -907, -907, -907, -907, -907, -907, -907, -907, -907, -907, + -907, -907, -907, -907, -907, -907, -907, -907, -907, -907, + -907, -907, -907, -907, -907, -907, -907, -907 + }, + + { + 55, -908, -908, -908, -908, -908, -908, -908, -908, -908, + -908, -908, -908, -908, -908, -908, -908, -908, -908, -908, + -908, -908, -908, -908, -908, -908, -908, -908, -908, -908, + -908, -908, -908, -908, -908, -908, -908, -908, -908, -908, + -908, -908, -908, -908, -908, -908, -908, -908, -908, -908, + -908, -908, -908, -908, -908, -908, -908, -908, -908, -908, + + -908, -908, -908, -908, -908, -908, -908, -908, -908, -908, + -908, -908, -908, -908, -908, -908, -908, -908, -908, -908, + -908, -908, -908, -908, -908, -908, -908, -908, -908, -908, + -908, -908, -908, -908, -908, -908, -908, -908, -908, -908, + -908, -908, -908, -908, -908, -908, -908, -908, -908, -908, + -908, -908, -908, -908, -908, -908, -908, -908, -908, -908, + -908, -908, -908, -908, -908, -908, -908, -908 + }, + + { + 55, -909, -909, -909, -909, -909, -909, -909, -909, -909, + -909, -909, -909, -909, -909, -909, -909, -909, -909, -909, + -909, -909, -909, -909, -909, -909, -909, -909, -909, -909, + + -909, -909, -909, -909, -909, -909, -909, -909, -909, -909, + -909, -909, -909, -909, -909, -909, -909, -909, -909, -909, + -909, -909, -909, -909, -909, -909, -909, -909, -909, -909, + -909, -909, -909, -909, -909, -909, -909, -909, -909, -909, + -909, -909, -909, -909, -909, -909, -909, -909, -909, -909, + -909, -909, -909, -909, -909, -909, -909, -909, -909, -909, + -909, -909, -909, -909, -909, -909, -909, -909, -909, -909, + -909, -909, -909, -909, -909, -909, -909, -909, -909, -909, + -909, -909, -909, -909, -909, -909, -909, -909, -909, -909, + -909, -909, -909, -909, -909, -909, -909, -909 + + }, + + { + 55, -910, -910, -910, -910, -910, -910, -910, -910, -910, + -910, -910, -910, -910, -910, -910, -910, -910, -910, -910, + -910, -910, -910, -910, -910, -910, -910, -910, -910, -910, + -910, -910, -910, -910, -910, -910, -910, -910, -910, -910, + -910, -910, -910, -910, -910, -910, -910, -910, -910, -910, + -910, -910, -910, -910, -910, -910, -910, -910, -910, -910, + -910, -910, -910, -910, -910, -910, -910, -910, -910, -910, + -910, -910, -910, -910, -910, -910, -910, -910, -910, -910, + -910, -910, -910, -910, -910, -910, -910, -910, -910, -910, + -910, -910, -910, -910, -910, -910, -910, -910, -910, -910, + + -910, -910, -910, -910, -910, -910, -910, -910, -910, -910, + -910, -910, -910, -910, -910, -910, -910, -910, -910, -910, + -910, -910, -910, -910, -910, -910, -910, -910 + }, + + { + 55, -911, -911, -911, -911, -911, -911, -911, -911, -911, + -911, -911, -911, -911, -911, -911, -911, -911, -911, -911, + -911, -911, -911, -911, -911, -911, -911, -911, -911, -911, + -911, -911, -911, -911, -911, -911, -911, -911, -911, -911, + -911, -911, -911, -911, -911, -911, -911, -911, -911, -911, + -911, -911, -911, -911, -911, -911, -911, -911, -911, -911, + -911, -911, -911, -911, -911, -911, -911, -911, -911, -911, + + -911, -911, -911, -911, -911, -911, -911, -911, -911, -911, + -911, -911, -911, -911, -911, -911, -911, -911, -911, -911, + -911, -911, -911, -911, -911, -911, -911, -911, -911, -911, + -911, -911, -911, -911, -911, -911, -911, -911, -911, -911, + -911, -911, -911, -911, -911, -911, -911, -911, -911, -911, + -911, -911, -911, -911, -911, -911, -911, -911 + }, + + { + 55, -912, -912, -912, -912, -912, -912, -912, -912, -912, + -912, -912, -912, -912, -912, -912, -912, -912, -912, -912, + -912, -912, -912, -912, -912, -912, -912, -912, -912, -912, + -912, -912, -912, -912, -912, -912, -912, -912, -912, -912, + + -912, -912, -912, -912, -912, -912, -912, -912, -912, -912, + -912, -912, -912, -912, -912, -912, -912, -912, -912, -912, + -912, -912, -912, -912, -912, -912, -912, -912, -912, -912, + -912, -912, -912, -912, -912, -912, -912, -912, -912, -912, + -912, -912, -912, -912, -912, -912, -912, -912, -912, -912, + -912, -912, -912, -912, -912, -912, -912, -912, -912, -912, + -912, -912, -912, -912, -912, -912, -912, -912, -912, -912, + -912, -912, -912, -912, -912, -912, -912, -912, -912, -912, + -912, -912, -912, -912, -912, -912, -912, -912 + }, + + { + 55, -913, -913, -913, -913, -913, -913, -913, -913, -913, + + -913, -913, -913, -913, -913, -913, -913, -913, -913, -913, + -913, -913, -913, -913, -913, -913, -913, -913, -913, -913, + -913, -913, -913, -913, -913, -913, -913, -913, -913, -913, + -913, -913, -913, -913, -913, -913, -913, -913, -913, -913, + -913, -913, -913, -913, -913, -913, -913, -913, -913, -913, + -913, -913, -913, -913, -913, -913, -913, -913, -913, -913, + -913, -913, -913, -913, -913, -913, -913, -913, -913, -913, + -913, -913, -913, -913, -913, -913, -913, -913, -913, -913, + -913, -913, -913, -913, -913, -913, -913, -913, -913, -913, + -913, -913, -913, -913, -913, -913, -913, -913, -913, -913, + + -913, -913, -913, -913, -913, -913, -913, -913, -913, -913, + -913, -913, -913, -913, -913, -913, -913, -913 + }, + + { + 55, -914, -914, -914, -914, -914, -914, -914, -914, -914, + -914, -914, -914, -914, -914, -914, -914, -914, -914, -914, + -914, -914, -914, -914, -914, -914, -914, -914, -914, -914, + -914, -914, -914, -914, -914, -914, -914, -914, -914, -914, + -914, -914, -914, -914, -914, -914, -914, -914, -914, -914, + -914, -914, -914, -914, -914, -914, -914, -914, -914, -914, + -914, -914, -914, -914, -914, -914, -914, -914, -914, -914, + -914, -914, -914, -914, -914, -914, -914, -914, -914, -914, + + -914, -914, -914, -914, -914, -914, -914, -914, -914, -914, + -914, -914, -914, -914, -914, -914, -914, -914, -914, -914, + -914, -914, -914, -914, -914, -914, -914, -914, -914, -914, + -914, -914, -914, -914, -914, -914, -914, -914, -914, -914, + -914, -914, -914, -914, -914, -914, -914, -914 + }, + + { + 55, -915, -915, -915, -915, -915, -915, -915, -915, -915, + -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, + -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, + -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, + -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, + + -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, + -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, + -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, + -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, + -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, + -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, + -915, -915, -915, -915, -915, -915, -915, -915, -915, -915, + -915, -915, -915, -915, -915, -915, -915, -915 + }, + + { + 55, -916, -916, -916, -916, -916, -916, -916, -916, -916, + -916, -916, -916, -916, -916, -916, -916, -916, -916, -916, + + -916, -916, -916, -916, -916, -916, -916, -916, -916, -916, + -916, -916, -916, -916, -916, -916, -916, -916, -916, -916, + -916, -916, -916, -916, -916, -916, -916, -916, -916, -916, + -916, -916, -916, -916, -916, -916, -916, -916, -916, -916, + -916, -916, -916, -916, -916, -916, -916, -916, -916, -916, + -916, -916, -916, -916, -916, -916, -916, -916, -916, -916, + -916, -916, -916, -916, -916, -916, -916, -916, -916, -916, + -916, -916, -916, -916, -916, -916, -916, -916, -916, -916, + -916, -916, -916, -916, -916, -916, -916, -916, -916, -916, + -916, -916, -916, -916, -916, -916, -916, -916, -916, -916, + + -916, -916, -916, -916, -916, -916, -916, -916 + }, + + { + 55, -917, -917, -917, -917, -917, -917, -917, -917, -917, + -917, -917, -917, -917, -917, -917, -917, -917, -917, -917, + -917, -917, -917, -917, -917, -917, -917, -917, -917, -917, + -917, -917, -917, -917, -917, -917, -917, -917, -917, -917, + -917, -917, -917, -917, -917, -917, -917, -917, -917, -917, + -917, -917, -917, -917, -917, -917, -917, -917, -917, -917, + -917, -917, -917, -917, -917, -917, -917, -917, -917, -917, + -917, -917, -917, -917, -917, -917, -917, -917, -917, -917, + -917, -917, -917, -917, -917, -917, -917, -917, -917, -917, + + -917, -917, -917, -917, -917, -917, -917, -917, -917, -917, + -917, -917, -917, -917, -917, -917, -917, -917, -917, -917, + -917, -917, -917, -917, -917, -917, -917, -917, -917, -917, + -917, -917, -917, -917, -917, -917, -917, -917 + }, + + { + 55, -918, -918, -918, -918, -918, -918, -918, -918, -918, + -918, -918, -918, -918, -918, -918, -918, -918, -918, -918, + -918, -918, -918, -918, -918, -918, -918, -918, -918, -918, + -918, -918, -918, -918, -918, -918, -918, -918, -918, -918, + -918, -918, -918, -918, -918, -918, -918, -918, -918, -918, + -918, -918, -918, -918, -918, -918, -918, -918, -918, -918, + + -918, -918, -918, -918, -918, -918, -918, -918, -918, -918, + -918, -918, -918, -918, -918, -918, -918, -918, -918, -918, + -918, -918, -918, -918, -918, -918, -918, -918, -918, -918, + -918, -918, -918, -918, -918, -918, -918, -918, -918, -918, + -918, -918, -918, -918, -918, -918, -918, -918, -918, -918, + -918, -918, -918, -918, -918, -918, -918, -918, -918, -918, + -918, -918, -918, -918, -918, -918, -918, -918 + }, + + { + 55, -919, -919, -919, -919, -919, -919, -919, -919, -919, + -919, -919, -919, -919, -919, -919, -919, -919, -919, -919, + -919, -919, -919, -919, -919, -919, -919, -919, -919, -919, + + -919, -919, -919, -919, -919, -919, -919, -919, -919, -919, + -919, -919, -919, -919, -919, -919, -919, -919, -919, -919, + -919, -919, -919, -919, -919, -919, -919, -919, -919, -919, + -919, -919, -919, -919, -919, -919, -919, -919, -919, -919, + -919, -919, -919, -919, -919, -919, -919, -919, -919, -919, + -919, -919, -919, -919, -919, -919, -919, -919, -919, -919, + -919, -919, -919, -919, -919, -919, -919, -919, -919, -919, + -919, -919, -919, -919, -919, -919, -919, -919, -919, -919, + -919, -919, -919, -919, -919, -919, -919, -919, -919, -919, + -919, -919, -919, -919, -919, -919, -919, -919 + + }, + + { + 55, -920, -920, -920, -920, -920, -920, -920, -920, -920, + -920, -920, -920, -920, -920, -920, -920, -920, -920, -920, + -920, -920, -920, -920, -920, -920, -920, -920, -920, -920, + -920, -920, -920, -920, -920, -920, -920, -920, -920, -920, + -920, -920, -920, -920, -920, -920, -920, -920, -920, -920, + -920, -920, -920, -920, -920, -920, -920, -920, -920, -920, + -920, -920, -920, -920, -920, -920, -920, -920, -920, -920, + -920, -920, -920, -920, -920, -920, -920, -920, -920, -920, + -920, -920, -920, -920, -920, -920, -920, -920, -920, -920, + -920, -920, -920, -920, -920, -920, -920, -920, -920, -920, + + -920, -920, -920, -920, -920, -920, -920, -920, -920, -920, + -920, -920, -920, -920, -920, -920, -920, -920, -920, -920, + -920, -920, -920, -920, -920, -920, -920, -920 + }, + + { + 55, -921, -921, -921, -921, -921, -921, -921, -921, -921, + -921, -921, -921, -921, -921, -921, -921, -921, -921, -921, + -921, -921, -921, -921, -921, -921, -921, -921, -921, -921, + -921, -921, -921, -921, -921, -921, -921, -921, -921, -921, + -921, -921, -921, -921, -921, -921, -921, -921, -921, -921, + -921, -921, -921, -921, -921, -921, -921, -921, -921, -921, + -921, -921, -921, -921, -921, -921, -921, -921, -921, -921, + + -921, -921, -921, -921, -921, -921, -921, -921, -921, -921, + -921, -921, -921, -921, -921, -921, -921, -921, -921, -921, + -921, -921, -921, -921, -921, -921, -921, -921, -921, -921, + -921, -921, -921, -921, -921, -921, -921, -921, -921, -921, + -921, -921, -921, -921, -921, -921, -921, -921, -921, -921, + -921, -921, -921, -921, -921, -921, -921, -921 + }, + + { + 55, -922, -922, -922, -922, -922, -922, -922, -922, -922, + -922, -922, -922, -922, -922, -922, -922, -922, -922, -922, + -922, -922, -922, -922, -922, -922, -922, -922, -922, -922, + -922, -922, -922, -922, -922, -922, -922, -922, -922, -922, + + -922, -922, -922, -922, -922, -922, -922, -922, -922, -922, + -922, -922, -922, -922, -922, -922, -922, -922, -922, -922, + -922, -922, -922, -922, -922, -922, -922, -922, -922, -922, + -922, -922, -922, -922, -922, -922, -922, -922, -922, -922, + -922, -922, -922, -922, -922, -922, -922, -922, -922, -922, + -922, -922, -922, -922, -922, -922, -922, -922, -922, -922, + -922, -922, -922, -922, -922, -922, -922, -922, -922, -922, + -922, -922, -922, -922, -922, -922, -922, -922, -922, -922, + -922, -922, -922, -922, -922, -922, -922, -922 + }, + + { + 55, -923, -923, -923, -923, -923, -923, -923, -923, -923, + + -923, -923, -923, -923, -923, -923, -923, -923, -923, -923, + -923, -923, -923, -923, -923, -923, -923, -923, -923, -923, + -923, -923, -923, -923, -923, -923, -923, -923, -923, -923, + -923, -923, -923, -923, -923, -923, -923, -923, -923, -923, + -923, -923, -923, -923, -923, -923, -923, -923, -923, -923, + -923, -923, -923, -923, -923, -923, -923, -923, -923, -923, + -923, -923, -923, -923, -923, -923, -923, -923, -923, -923, + -923, -923, -923, -923, -923, -923, -923, -923, -923, -923, + -923, -923, -923, -923, -923, -923, -923, -923, -923, -923, + -923, -923, -923, -923, -923, -923, -923, -923, -923, -923, + + -923, -923, -923, -923, -923, -923, -923, -923, -923, -923, + -923, -923, -923, -923, -923, -923, -923, -923 + }, + + { + 55, -924, -924, -924, -924, -924, -924, -924, -924, -924, + -924, -924, -924, -924, -924, -924, -924, -924, -924, -924, + -924, -924, -924, -924, -924, -924, -924, -924, -924, -924, + -924, -924, -924, -924, -924, -924, -924, -924, -924, -924, + -924, -924, -924, -924, -924, -924, -924, -924, -924, -924, + -924, -924, -924, -924, -924, -924, -924, -924, -924, -924, + -924, -924, -924, -924, -924, -924, -924, -924, -924, -924, + -924, -924, -924, -924, -924, -924, -924, -924, -924, -924, + + -924, -924, -924, -924, -924, -924, -924, -924, -924, -924, + -924, -924, -924, -924, -924, -924, -924, -924, -924, -924, + -924, -924, -924, -924, -924, -924, -924, -924, -924, -924, + -924, -924, -924, -924, -924, -924, -924, -924, -924, -924, + -924, -924, -924, -924, -924, -924, -924, -924 + }, + + { + 55, -925, -925, -925, -925, -925, -925, -925, -925, -925, + -925, -925, -925, -925, -925, -925, -925, -925, -925, -925, + -925, -925, -925, -925, -925, -925, -925, -925, -925, -925, + -925, -925, -925, -925, -925, -925, -925, -925, -925, -925, + -925, -925, -925, -925, -925, -925, -925, -925, -925, -925, + + -925, -925, -925, -925, -925, -925, -925, -925, -925, -925, + -925, -925, -925, -925, -925, -925, -925, -925, -925, -925, + -925, -925, -925, -925, -925, -925, -925, -925, -925, -925, + -925, -925, -925, -925, -925, -925, -925, -925, -925, -925, + -925, -925, -925, -925, -925, -925, -925, -925, -925, -925, + -925, -925, -925, -925, -925, -925, -925, -925, -925, -925, + -925, -925, -925, -925, -925, -925, -925, -925, -925, -925, + -925, -925, -925, -925, -925, -925, -925, -925 + }, + + { + 55, -926, -926, -926, -926, -926, -926, -926, -926, -926, + -926, -926, -926, -926, -926, -926, -926, -926, -926, -926, + + -926, -926, -926, -926, -926, -926, -926, -926, -926, -926, + -926, -926, -926, -926, -926, -926, -926, -926, -926, -926, + -926, -926, -926, -926, -926, -926, -926, -926, -926, -926, + -926, -926, -926, -926, -926, -926, -926, -926, -926, -926, + -926, -926, -926, -926, -926, -926, -926, -926, -926, -926, + -926, -926, -926, -926, -926, -926, -926, -926, -926, -926, + -926, -926, -926, -926, -926, -926, -926, -926, -926, -926, + -926, -926, -926, -926, -926, -926, -926, -926, -926, -926, + -926, -926, -926, -926, -926, -926, -926, -926, -926, -926, + -926, -926, -926, -926, -926, -926, -926, -926, -926, -926, + + -926, -926, -926, -926, -926, -926, -926, -926 + }, + + { + 55, -927, -927, -927, -927, -927, -927, -927, -927, -927, + -927, -927, -927, -927, -927, -927, -927, -927, -927, -927, + -927, -927, -927, -927, -927, -927, -927, -927, -927, -927, + -927, -927, -927, -927, -927, -927, -927, -927, -927, -927, + -927, -927, -927, -927, -927, -927, -927, -927, -927, -927, + -927, -927, -927, -927, -927, -927, -927, -927, -927, -927, + -927, -927, -927, -927, -927, -927, -927, -927, -927, -927, + -927, -927, -927, -927, -927, -927, -927, -927, -927, -927, + -927, -927, -927, -927, -927, -927, -927, -927, -927, -927, + + -927, -927, -927, -927, -927, -927, -927, -927, -927, -927, + -927, -927, -927, -927, -927, -927, -927, -927, -927, -927, + -927, -927, -927, -927, -927, -927, -927, -927, -927, -927, + -927, -927, -927, -927, -927, -927, -927, -927 + }, + + { + 55, -928, -928, -928, -928, -928, -928, -928, -928, -928, + -928, -928, -928, -928, -928, -928, -928, -928, -928, -928, + -928, -928, -928, -928, -928, -928, -928, -928, -928, -928, + -928, -928, -928, -928, -928, -928, -928, -928, -928, -928, + -928, -928, -928, -928, -928, -928, -928, -928, -928, -928, + -928, -928, -928, -928, -928, -928, -928, -928, -928, -928, + + -928, -928, -928, -928, -928, -928, -928, -928, -928, -928, + -928, -928, -928, -928, -928, -928, -928, -928, -928, -928, + -928, -928, -928, -928, -928, -928, -928, -928, -928, -928, + -928, -928, -928, -928, -928, -928, -928, -928, -928, -928, + -928, -928, -928, -928, -928, -928, -928, -928, -928, -928, + -928, -928, -928, -928, -928, -928, -928, -928, -928, -928, + -928, -928, -928, -928, -928, -928, -928, -928 + }, + + { + 55, -929, -929, -929, -929, -929, -929, -929, -929, -929, + -929, -929, -929, -929, -929, -929, -929, -929, -929, -929, + -929, -929, -929, -929, -929, -929, -929, -929, -929, -929, + + -929, -929, -929, -929, -929, -929, -929, -929, -929, -929, + -929, -929, -929, -929, -929, -929, -929, -929, -929, -929, + -929, -929, -929, -929, -929, -929, -929, -929, -929, -929, + -929, -929, -929, -929, -929, -929, -929, -929, -929, -929, + -929, -929, -929, -929, -929, -929, -929, -929, -929, -929, + -929, -929, -929, -929, -929, -929, -929, -929, -929, -929, + -929, -929, -929, -929, -929, -929, -929, -929, -929, -929, + -929, -929, -929, -929, -929, -929, -929, -929, -929, -929, + -929, -929, -929, -929, -929, -929, -929, -929, -929, -929, + -929, -929, -929, -929, -929, -929, -929, -929 + + }, + + { + 55, -930, -930, -930, -930, -930, -930, -930, -930, -930, + -930, -930, -930, -930, -930, -930, -930, -930, -930, -930, + -930, -930, -930, -930, -930, -930, -930, -930, -930, -930, + -930, -930, -930, -930, -930, -930, -930, -930, -930, -930, + -930, -930, -930, -930, -930, -930, -930, -930, -930, -930, + -930, -930, -930, -930, -930, -930, -930, -930, -930, -930, + -930, -930, -930, -930, -930, -930, -930, -930, -930, -930, + -930, -930, -930, -930, -930, -930, -930, -930, -930, -930, + -930, -930, -930, -930, -930, -930, -930, -930, -930, -930, + -930, -930, -930, -930, -930, -930, -930, -930, -930, -930, + + -930, -930, -930, -930, -930, -930, -930, -930, -930, -930, + -930, -930, -930, -930, -930, -930, -930, -930, -930, -930, + -930, -930, -930, -930, -930, -930, -930, -930 + }, + + { + 55, -931, -931, -931, -931, -931, -931, -931, -931, -931, + -931, -931, -931, -931, -931, -931, -931, -931, -931, -931, + -931, -931, -931, -931, -931, -931, -931, -931, -931, -931, + -931, -931, -931, -931, -931, -931, -931, -931, -931, -931, + -931, -931, -931, -931, -931, -931, -931, -931, -931, -931, + -931, -931, -931, -931, -931, -931, -931, -931, -931, -931, + -931, -931, -931, -931, -931, -931, -931, -931, -931, -931, + + -931, -931, -931, -931, -931, -931, -931, -931, -931, -931, + -931, -931, 1022, -931, -931, -931, -931, -931, -931, -931, + -931, -931, -931, -931, -931, -931, -931, -931, -931, -931, + -931, -931, -931, -931, -931, -931, -931, -931, -931, -931, + -931, -931, -931, -931, -931, -931, -931, -931, -931, -931, + -931, -931, -931, -931, -931, -931, -931, -931 + }, + + { + 55, -932, -932, -932, -932, -932, -932, -932, -932, -932, + -932, -932, -932, -932, -932, -932, -932, -932, -932, -932, + -932, -932, -932, -932, -932, -932, -932, -932, -932, -932, + -932, -932, 1023, -932, -932, -932, -932, -932, -932, -932, + + -932, -932, -932, -932, -932, -932, -932, -932, -932, -932, + -932, -932, -932, -932, -932, -932, -932, -932, -932, -932, + -932, -932, -932, -932, -932, -932, -932, -932, -932, -932, + -932, -932, -932, -932, -932, -932, -932, -932, -932, -932, + -932, -932, -932, -932, -932, -932, -932, -932, -932, -932, + -932, -932, -932, -932, -932, -932, -932, -932, -932, -932, + -932, -932, -932, -932, -932, -932, -932, -932, -932, -932, + -932, -932, -932, -932, -932, -932, -932, -932, -932, -932, + -932, -932, -932, -932, -932, -932, -932, -932 + }, + + { + 55, -933, -933, -933, -933, -933, -933, -933, -933, -933, + + -933, -933, -933, -933, -933, -933, -933, -933, -933, -933, + -933, -933, -933, -933, -933, -933, -933, -933, -933, -933, + -933, -933, 1024, -933, -933, -933, -933, -933, -933, -933, + -933, -933, -933, -933, -933, -933, -933, -933, -933, -933, + -933, -933, -933, -933, -933, -933, -933, -933, -933, -933, + -933, -933, -933, -933, -933, -933, -933, -933, -933, -933, + -933, -933, -933, -933, -933, -933, -933, -933, -933, -933, + -933, -933, -933, -933, -933, -933, -933, -933, -933, -933, + -933, -933, -933, -933, -933, -933, -933, -933, -933, -933, + -933, -933, -933, -933, -933, -933, -933, -933, -933, -933, + + -933, -933, -933, -933, -933, -933, -933, -933, -933, -933, + -933, -933, -933, -933, -933, -933, -933, -933 + }, + + { + 55, -934, -934, -934, -934, -934, -934, -934, -934, -934, + -934, -934, -934, -934, -934, -934, -934, -934, -934, -934, + -934, -934, -934, -934, -934, -934, -934, -934, -934, -934, + -934, -934, -934, -934, -934, -934, -934, -934, -934, -934, + -934, -934, -934, -934, -934, -934, -934, -934, -934, -934, + -934, -934, -934, -934, -934, -934, -934, -934, -934, -934, + -934, -934, -934, -934, -934, -934, -934, -934, -934, -934, + -934, -934, -934, -934, -934, -934, -934, -934, -934, -934, + + -934, -934, -934, 1025, -934, -934, -934, -934, -934, -934, + -934, -934, -934, -934, -934, -934, -934, -934, -934, -934, + -934, -934, -934, -934, -934, -934, -934, -934, -934, -934, + -934, -934, -934, -934, -934, -934, -934, -934, -934, -934, + -934, -934, -934, -934, -934, -934, -934, -934 + }, + + { + 55, -935, -935, -935, -935, -935, -935, -935, -935, -935, + -935, -935, -935, -935, -935, -935, -935, -935, -935, -935, + -935, -935, -935, -935, -935, -935, -935, -935, -935, -935, + -935, -935, -935, -935, -935, -935, -935, -935, -935, -935, + -935, -935, -935, -935, -935, -935, -935, -935, -935, -935, + + -935, -935, -935, -935, -935, -935, -935, -935, -935, -935, + -935, -935, -935, -935, -935, -935, -935, -935, -935, -935, + -935, -935, -935, -935, -935, -935, -935, -935, -935, -935, + -935, -935, -935, 1026, -935, -935, -935, -935, -935, -935, + -935, -935, -935, -935, -935, -935, -935, -935, -935, -935, + -935, -935, -935, -935, -935, -935, -935, -935, -935, -935, + -935, -935, -935, -935, -935, -935, -935, -935, -935, -935, + -935, -935, -935, -935, -935, -935, -935, -935 + }, + + { + 55, -936, -936, -936, -936, -936, -936, -936, -936, -936, + -936, -936, -936, -936, -936, -936, -936, -936, -936, -936, + + -936, -936, -936, -936, -936, -936, -936, -936, -936, -936, + -936, -936, 1027, -936, -936, -936, -936, -936, -936, -936, + -936, -936, -936, -936, -936, -936, -936, -936, -936, -936, + -936, -936, -936, -936, -936, -936, -936, -936, -936, -936, + -936, -936, -936, -936, -936, -936, -936, -936, -936, -936, + -936, -936, -936, -936, -936, -936, -936, -936, -936, -936, + -936, -936, -936, -936, -936, -936, -936, -936, -936, -936, + -936, -936, -936, -936, -936, -936, -936, -936, -936, -936, + -936, -936, -936, -936, -936, -936, -936, -936, -936, -936, + -936, -936, -936, -936, -936, -936, -936, -936, -936, -936, + + -936, -936, -936, -936, -936, -936, -936, -936 + }, + + { + 55, -937, -937, -937, -937, -937, -937, -937, -937, -937, + -937, -937, -937, -937, -937, -937, -937, -937, -937, -937, + -937, -937, -937, -937, -937, -937, -937, -937, -937, -937, + -937, -937, -937, -937, -937, -937, -937, -937, -937, -937, + -937, -937, -937, -937, -937, -937, -937, -937, -937, -937, + -937, -937, -937, -937, -937, -937, -937, -937, -937, -937, + -937, -937, -937, -937, -937, -937, -937, -937, -937, -937, + -937, -937, -937, -937, -937, -937, -937, -937, -937, -937, + -937, -937, -937, 1028, -937, -937, -937, -937, -937, -937, + + -937, -937, -937, -937, -937, -937, -937, -937, -937, -937, + -937, -937, -937, -937, -937, -937, -937, -937, -937, -937, + -937, -937, -937, -937, -937, -937, -937, -937, -937, -937, + -937, -937, -937, -937, -937, -937, -937, -937 + }, + + { + 55, -938, -938, -938, -938, -938, -938, -938, -938, -938, + -938, -938, -938, -938, -938, -938, -938, -938, -938, -938, + -938, -938, -938, -938, -938, -938, -938, -938, -938, -938, + -938, -938, -938, -938, -938, -938, -938, -938, -938, -938, + -938, -938, -938, -938, -938, -938, -938, -938, -938, -938, + -938, -938, -938, -938, -938, -938, -938, -938, -938, -938, + + -938, -938, -938, -938, -938, -938, -938, -938, -938, -938, + -938, -938, -938, -938, -938, -938, -938, -938, -938, -938, + -938, -938, -938, 1029, -938, -938, -938, -938, -938, -938, + -938, -938, -938, -938, -938, -938, -938, -938, -938, -938, + -938, -938, -938, -938, -938, -938, -938, -938, -938, -938, + -938, -938, -938, -938, -938, -938, -938, -938, -938, -938, + -938, -938, -938, -938, -938, -938, -938, -938 + }, + + { + 55, -939, -939, -939, -939, -939, -939, -939, -939, -939, + -939, -939, -939, -939, -939, -939, -939, -939, -939, -939, + -939, -939, -939, -939, -939, -939, -939, -939, -939, -939, + + -939, -939, -939, -939, -939, -939, -939, -939, -939, -939, + -939, -939, -939, -939, -939, -939, -939, -939, -939, -939, + -939, -939, -939, -939, -939, -939, -939, -939, -939, -939, + -939, -939, -939, -939, -939, -939, -939, -939, -939, -939, + -939, -939, -939, -939, -939, -939, -939, -939, -939, -939, + -939, -939, 1030, -939, -939, -939, -939, -939, -939, -939, + -939, -939, -939, -939, -939, -939, -939, -939, -939, -939, + -939, -939, -939, -939, -939, -939, -939, -939, -939, -939, + -939, -939, -939, -939, -939, -939, -939, -939, -939, -939, + -939, -939, -939, -939, -939, -939, -939, -939 + + }, + + { + 55, -940, -940, -940, -940, -940, -940, -940, -940, -940, + -940, -940, -940, -940, -940, -940, -940, -940, -940, -940, + -940, -940, -940, -940, -940, -940, -940, -940, -940, -940, + -940, -940, 1031, -940, -940, -940, -940, -940, -940, -940, + -940, -940, -940, -940, -940, -940, -940, -940, -940, -940, + -940, -940, -940, -940, -940, -940, -940, -940, -940, -940, + -940, -940, -940, -940, -940, -940, -940, -940, -940, -940, + -940, -940, -940, -940, -940, -940, -940, -940, -940, -940, + -940, -940, -940, -940, -940, -940, -940, -940, -940, -940, + -940, -940, -940, -940, -940, -940, -940, -940, -940, -940, + + -940, -940, -940, -940, -940, -940, -940, -940, -940, -940, + -940, -940, -940, -940, -940, -940, -940, -940, -940, -940, + -940, -940, -940, -940, -940, -940, -940, -940 + }, + + { + 55, -941, -941, -941, -941, -941, -941, -941, -941, -941, + -941, -941, -941, -941, -941, -941, -941, -941, -941, -941, + -941, -941, -941, -941, -941, -941, -941, -941, -941, -941, + -941, -941, 1032, -941, -941, -941, -941, -941, -941, -941, + -941, -941, -941, -941, -941, -941, -941, -941, -941, -941, + -941, -941, -941, -941, -941, -941, -941, -941, -941, -941, + -941, -941, -941, -941, -941, -941, -941, -941, -941, -941, + + -941, -941, -941, -941, -941, -941, -941, -941, -941, -941, + -941, -941, -941, -941, -941, -941, -941, -941, -941, -941, + -941, -941, -941, -941, -941, -941, -941, -941, -941, -941, + -941, -941, -941, -941, -941, -941, -941, -941, -941, -941, + -941, -941, -941, -941, -941, -941, -941, -941, -941, -941, + -941, -941, -941, -941, -941, -941, -941, -941 + }, + + { + 55, -942, -942, -942, -942, -942, -942, -942, -942, -942, + -942, -942, -942, -942, -942, -942, -942, -942, -942, -942, + -942, -942, -942, -942, -942, -942, -942, -942, -942, -942, + -942, -942, -942, -942, -942, -942, -942, -942, -942, -942, + + -942, -942, -942, -942, -942, -942, -942, -942, -942, -942, + -942, -942, -942, -942, -942, -942, -942, -942, -942, -942, + -942, -942, -942, -942, -942, -942, -942, -942, -942, -942, + -942, -942, -942, -942, -942, -942, -942, -942, -942, -942, + -942, -942, -942, 1033, -942, -942, -942, -942, -942, -942, + -942, -942, -942, -942, -942, -942, -942, -942, -942, -942, + -942, -942, -942, -942, -942, -942, -942, -942, -942, -942, + -942, -942, -942, -942, -942, -942, -942, -942, -942, -942, + -942, -942, -942, -942, -942, -942, -942, -942 + }, + + { + 55, -943, -943, -943, -943, -943, -943, -943, -943, -943, + + -943, -943, -943, -943, -943, -943, -943, -943, -943, -943, + -943, -943, -943, -943, -943, -943, -943, -943, -943, -943, + -943, -943, 1034, -943, -943, -943, -943, -943, -943, -943, + -943, -943, -943, -943, -943, -943, -943, -943, -943, -943, + -943, -943, -943, -943, -943, -943, -943, -943, -943, -943, + -943, -943, -943, -943, -943, -943, -943, -943, -943, -943, + -943, -943, -943, -943, -943, -943, -943, -943, -943, -943, + -943, -943, -943, -943, -943, -943, -943, -943, -943, -943, + -943, -943, -943, -943, -943, -943, -943, -943, -943, -943, + -943, -943, -943, -943, -943, -943, -943, -943, -943, -943, + + -943, -943, -943, -943, -943, -943, -943, -943, -943, -943, + -943, -943, -943, -943, -943, -943, -943, -943 + }, + + { + 55, -944, -944, -944, -944, -944, -944, -944, -944, -944, + -944, -944, -944, -944, -944, -944, -944, -944, -944, -944, + -944, -944, -944, -944, -944, -944, -944, -944, -944, -944, + -944, -944, 1035, -944, -944, -944, -944, -944, -944, -944, + -944, -944, -944, -944, -944, -944, -944, -944, -944, -944, + -944, -944, -944, -944, -944, -944, -944, -944, -944, -944, + -944, -944, -944, -944, -944, -944, -944, -944, -944, -944, + -944, -944, -944, -944, -944, -944, -944, -944, -944, -944, + + -944, -944, -944, -944, -944, -944, -944, -944, -944, -944, + -944, -944, -944, -944, -944, -944, -944, -944, -944, -944, + -944, -944, -944, -944, -944, -944, -944, -944, -944, -944, + -944, -944, -944, -944, -944, -944, -944, -944, -944, -944, + -944, -944, -944, -944, -944, -944, -944, -944 + }, + + { + 55, -945, -945, -945, -945, -945, -945, -945, -945, -945, + -945, -945, -945, -945, -945, -945, -945, -945, -945, -945, + -945, -945, -945, -945, -945, -945, -945, -945, -945, -945, + -945, -945, -945, -945, -945, -945, -945, -945, -945, -945, + -945, -945, -945, -945, -945, -945, -945, -945, -945, -945, + + -945, -945, -945, -945, -945, -945, -945, -945, -945, -945, + -945, -945, -945, -945, -945, -945, -945, -945, -945, -945, + -945, -945, -945, -945, -945, -945, -945, -945, -945, -945, + -945, -945, -945, 1036, -945, -945, -945, -945, -945, -945, + -945, -945, -945, -945, -945, -945, -945, -945, -945, -945, + -945, -945, -945, -945, -945, -945, -945, -945, -945, -945, + -945, -945, -945, -945, -945, -945, -945, -945, -945, -945, + -945, -945, -945, -945, -945, -945, -945, -945 + }, + + { + 55, -946, -946, -946, -946, -946, -946, -946, -946, -946, + -946, -946, -946, -946, -946, -946, -946, -946, -946, -946, + + -946, -946, -946, -946, -946, -946, -946, -946, -946, -946, + -946, -946, -946, -946, -946, -946, -946, -946, -946, -946, + -946, -946, -946, -946, -946, -946, -946, -946, -946, -946, + -946, -946, -946, -946, -946, -946, -946, -946, -946, -946, + -946, -946, -946, -946, -946, -946, -946, -946, -946, -946, + -946, -946, -946, -946, -946, -946, -946, -946, -946, -946, + -946, -946, -946, 1037, -946, -946, -946, -946, -946, -946, + -946, -946, -946, -946, -946, -946, -946, -946, -946, -946, + -946, -946, -946, -946, -946, -946, -946, -946, -946, -946, + -946, -946, -946, -946, -946, -946, -946, -946, -946, -946, + + -946, -946, -946, -946, -946, -946, -946, -946 + }, + + { + 55, -947, -947, -947, -947, -947, -947, -947, -947, -947, + -947, -947, -947, -947, -947, -947, -947, -947, -947, -947, + -947, -947, -947, -947, -947, -947, -947, -947, -947, -947, + -947, -947, -947, -947, -947, -947, -947, -947, -947, -947, + -947, -947, -947, -947, -947, -947, -947, -947, -947, -947, + -947, -947, -947, -947, -947, -947, -947, -947, -947, -947, + -947, -947, -947, -947, -947, -947, -947, -947, -947, -947, + -947, -947, -947, -947, -947, -947, -947, -947, -947, -947, + -947, -947, -947, 1038, -947, -947, -947, -947, -947, -947, + + -947, -947, -947, -947, -947, -947, -947, -947, -947, -947, + -947, -947, -947, -947, -947, -947, -947, -947, -947, -947, + -947, -947, -947, -947, -947, -947, -947, -947, -947, -947, + -947, -947, -947, -947, -947, -947, -947, -947 + }, + + { + 55, -948, -948, -948, -948, -948, -948, -948, -948, -948, + -948, -948, -948, -948, -948, -948, -948, -948, -948, -948, + -948, -948, -948, -948, -948, -948, -948, -948, -948, -948, + -948, -948, -948, -948, -948, -948, -948, -948, -948, -948, + -948, -948, -948, -948, -948, -948, -948, -948, -948, -948, + -948, -948, -948, -948, -948, -948, -948, -948, -948, -948, + + -948, -948, -948, -948, -948, -948, -948, -948, -948, -948, + -948, 1039, -948, -948, -948, -948, -948, -948, -948, -948, + -948, -948, -948, -948, -948, -948, -948, -948, -948, -948, + -948, -948, -948, -948, -948, -948, -948, -948, -948, -948, + -948, -948, -948, -948, -948, -948, -948, -948, -948, -948, + -948, -948, -948, -948, -948, -948, -948, -948, -948, -948, + -948, -948, -948, -948, -948, -948, -948, -948 + }, + + { + 55, -949, -949, -949, -949, -949, -949, -949, -949, -949, + -949, -949, -949, -949, -949, -949, -949, -949, -949, -949, + -949, -949, -949, -949, -949, -949, -949, -949, -949, -949, + + -949, -949, -949, -949, -949, -949, -949, -949, -949, -949, + -949, -949, -949, -949, -949, -949, -949, -949, -949, -949, + -949, -949, -949, -949, -949, -949, -949, -949, -949, -949, + -949, -949, -949, -949, -949, -949, -949, -949, -949, -949, + -949, 1040, -949, -949, -949, -949, -949, -949, -949, -949, + -949, -949, -949, -949, -949, -949, -949, -949, -949, -949, + -949, -949, -949, -949, -949, -949, -949, -949, -949, -949, + -949, -949, -949, -949, -949, -949, -949, -949, -949, -949, + -949, -949, -949, -949, -949, -949, -949, -949, -949, -949, + -949, -949, -949, -949, -949, -949, -949, -949 + + }, + + { + 55, -950, -950, -950, -950, -950, -950, -950, -950, -950, + -950, -950, -950, -950, -950, -950, -950, -950, -950, -950, + -950, -950, -950, -950, -950, -950, -950, -950, -950, -950, + -950, -950, -950, -950, -950, -950, -950, -950, -950, -950, + -950, -950, -950, -950, -950, -950, -950, -950, -950, -950, + -950, -950, -950, -950, -950, -950, -950, -950, -950, -950, + -950, -950, -950, -950, -950, -950, -950, -950, 1041, -950, + -950, -950, -950, -950, -950, -950, -950, -950, -950, -950, + -950, -950, -950, -950, -950, -950, -950, -950, -950, -950, + -950, -950, -950, -950, -950, -950, -950, -950, -950, -950, + + -950, -950, -950, -950, -950, -950, -950, -950, -950, -950, + -950, -950, -950, -950, -950, -950, -950, -950, -950, -950, + -950, -950, -950, -950, -950, -950, -950, -950 + }, + + { + 55, -951, -951, -951, -951, -951, -951, -951, -951, -951, + -951, -951, -951, -951, -951, -951, -951, -951, -951, -951, + -951, -951, -951, -951, -951, -951, -951, -951, -951, -951, + -951, -951, -951, -951, -951, -951, -951, -951, -951, -951, + -951, -951, -951, -951, -951, -951, -951, -951, -951, -951, + -951, -951, -951, -951, -951, -951, -951, -951, -951, -951, + -951, -951, -951, -951, -951, -951, -951, -951, -951, -951, + + -951, -951, -951, -951, -951, -951, -951, -951, -951, -951, + -951, -951, -951, 1042, -951, -951, -951, -951, -951, -951, + -951, -951, -951, -951, -951, -951, -951, -951, -951, -951, + -951, -951, -951, -951, -951, -951, -951, -951, -951, -951, + -951, -951, -951, -951, -951, -951, -951, -951, -951, -951, + -951, -951, -951, -951, -951, -951, -951, -951 + }, + + { + 55, -952, -952, -952, -952, -952, -952, -952, -952, -952, + -952, -952, -952, -952, -952, -952, -952, -952, -952, -952, + -952, -952, -952, -952, -952, -952, -952, -952, -952, -952, + -952, -952, -952, -952, -952, -952, -952, -952, -952, -952, + + -952, -952, -952, -952, -952, -952, -952, -952, -952, -952, + -952, -952, -952, -952, -952, -952, -952, -952, -952, -952, + -952, -952, -952, -952, -952, -952, -952, -952, -952, -952, + 1043, -952, -952, -952, -952, -952, -952, -952, -952, -952, + -952, -952, -952, -952, -952, -952, -952, -952, -952, -952, + -952, -952, -952, -952, -952, -952, -952, -952, -952, -952, + -952, -952, -952, -952, -952, -952, -952, -952, -952, -952, + -952, -952, -952, -952, -952, -952, -952, -952, -952, -952, + -952, -952, -952, -952, -952, -952, -952, -952 + }, + + { + 55, -953, -953, -953, -953, -953, -953, -953, -953, -953, + + -953, -953, -953, -953, -953, -953, -953, -953, -953, -953, + -953, -953, -953, -953, -953, -953, -953, -953, -953, -953, + -953, -953, 1044, -953, -953, -953, -953, -953, -953, -953, + -953, -953, -953, -953, -953, -953, -953, -953, -953, -953, + -953, -953, -953, -953, -953, -953, -953, -953, -953, -953, + -953, -953, -953, -953, -953, -953, -953, -953, -953, -953, + -953, -953, -953, -953, -953, -953, -953, -953, -953, -953, + -953, -953, -953, -953, -953, -953, -953, -953, -953, -953, + -953, -953, -953, -953, -953, -953, -953, -953, -953, -953, + -953, -953, -953, -953, -953, -953, -953, -953, -953, -953, + + -953, -953, -953, -953, -953, -953, -953, -953, -953, -953, + -953, -953, -953, -953, -953, -953, -953, -953 + }, + + { + 55, -954, -954, -954, -954, -954, -954, -954, -954, -954, + -954, -954, -954, -954, -954, -954, -954, -954, -954, -954, + -954, -954, -954, -954, -954, -954, -954, -954, -954, -954, + -954, -954, -954, -954, -954, -954, -954, -954, -954, -954, + -954, -954, -954, -954, -954, -954, -954, -954, -954, -954, + -954, -954, -954, -954, -954, -954, -954, -954, -954, -954, + -954, -954, -954, -954, -954, -954, -954, -954, -954, -954, + -954, -954, -954, -954, -954, -954, -954, -954, -954, -954, + + -954, -954, -954, 1045, -954, -954, -954, -954, -954, -954, + -954, -954, -954, -954, -954, -954, -954, -954, -954, -954, + -954, -954, -954, -954, -954, -954, -954, -954, -954, -954, + -954, -954, -954, -954, -954, -954, -954, -954, -954, -954, + -954, -954, -954, -954, -954, -954, -954, -954 + }, + + { + 55, -955, -955, -955, -955, -955, -955, -955, -955, -955, + -955, -955, -955, -955, -955, -955, -955, -955, -955, -955, + -955, -955, -955, -955, -955, -955, -955, -955, -955, -955, + -955, -955, 1046, -955, -955, -955, -955, -955, -955, -955, + -955, -955, -955, -955, -955, -955, -955, -955, -955, -955, + + -955, -955, -955, -955, -955, -955, -955, -955, -955, -955, + -955, -955, -955, -955, -955, -955, -955, -955, -955, -955, + -955, -955, -955, -955, -955, -955, -955, -955, -955, -955, + -955, -955, -955, -955, -955, -955, -955, -955, -955, -955, + -955, -955, -955, -955, -955, -955, -955, -955, -955, -955, + -955, -955, -955, -955, -955, -955, -955, -955, -955, -955, + -955, -955, -955, -955, -955, -955, -955, -955, -955, -955, + -955, -955, -955, -955, -955, -955, -955, -955 + }, + + { + 55, -956, -956, -956, -956, -956, -956, -956, -956, -956, + -956, -956, -956, -956, -956, -956, -956, -956, -956, -956, + + -956, -956, -956, -956, -956, -956, -956, -956, -956, -956, + -956, -956, 1047, -956, -956, -956, -956, -956, -956, -956, + -956, -956, -956, -956, -956, -956, -956, -956, -956, -956, + -956, -956, -956, -956, -956, -956, -956, -956, -956, -956, + -956, -956, -956, -956, -956, -956, -956, -956, -956, -956, + -956, -956, -956, -956, -956, -956, -956, -956, -956, -956, + -956, -956, -956, -956, -956, -956, -956, -956, -956, -956, + -956, -956, -956, -956, -956, -956, -956, -956, -956, -956, + -956, -956, -956, -956, -956, -956, -956, -956, -956, -956, + -956, -956, -956, -956, -956, -956, -956, -956, -956, -956, + + -956, -956, -956, -956, -956, -956, -956, -956 + }, + + { + 55, -957, -957, -957, -957, -957, -957, -957, -957, -957, + -957, -957, -957, -957, -957, -957, -957, -957, -957, -957, + -957, -957, -957, -957, -957, -957, -957, -957, -957, -957, + -957, -957, -957, -957, -957, -957, -957, -957, -957, -957, + -957, -957, -957, -957, -957, -957, -957, -957, -957, -957, + -957, -957, -957, -957, -957, -957, -957, -957, -957, -957, + -957, -957, -957, -957, -957, -957, -957, -957, -957, -957, + -957, -957, -957, -957, -957, -957, -957, -957, -957, -957, + -957, -957, -957, -957, -957, -957, -957, -957, -957, -957, + + -957, -957, -957, -957, -957, -957, -957, -957, -957, -957, + -957, -957, -957, -957, -957, -957, -957, -957, -957, -957, + -957, -957, -957, -957, -957, -957, -957, -957, -957, -957, + -957, -957, -957, -957, -957, -957, -957, -957 + }, + + { + 55, -958, -958, -958, -958, -958, -958, -958, -958, -958, + -958, -958, -958, -958, -958, -958, -958, -958, -958, -958, + -958, -958, -958, -958, -958, -958, -958, -958, -958, -958, + -958, -958, -958, -958, -958, -958, -958, -958, -958, -958, + -958, -958, -958, -958, -958, -958, -958, -958, -958, -958, + -958, -958, -958, -958, -958, -958, -958, -958, -958, -958, + + -958, -958, -958, -958, -958, -958, -958, -958, -958, -958, + -958, -958, -958, -958, -958, -958, -958, -958, -958, -958, + -958, -958, -958, 1048, -958, -958, -958, -958, -958, -958, + -958, -958, -958, -958, -958, -958, -958, -958, -958, -958, + -958, -958, -958, -958, -958, -958, -958, -958, -958, -958, + -958, -958, -958, -958, -958, -958, -958, -958, -958, -958, + -958, -958, -958, -958, -958, -958, -958, -958 + }, + + { + 55, -959, -959, -959, -959, -959, -959, -959, -959, -959, + -959, -959, -959, -959, -959, -959, -959, -959, -959, -959, + -959, -959, -959, -959, -959, -959, -959, -959, -959, -959, + + -959, -959, -959, -959, -959, -959, -959, -959, -959, -959, + -959, -959, -959, -959, -959, -959, -959, -959, -959, -959, + -959, -959, -959, -959, -959, -959, -959, -959, -959, -959, + -959, -959, -959, -959, -959, -959, -959, -959, -959, -959, + -959, -959, -959, -959, -959, -959, -959, -959, -959, -959, + -959, -959, -959, 1049, -959, -959, -959, -959, -959, -959, + -959, -959, -959, -959, -959, -959, -959, -959, -959, -959, + -959, -959, -959, -959, -959, -959, -959, -959, -959, -959, + -959, -959, -959, -959, -959, -959, -959, -959, -959, -959, + -959, -959, -959, -959, -959, -959, -959, -959 + + }, + + { + 55, -960, -960, -960, -960, -960, -960, -960, -960, -960, + -960, -960, -960, -960, -960, -960, -960, -960, -960, -960, + -960, -960, -960, -960, -960, -960, -960, -960, -960, -960, + -960, -960, 1050, -960, -960, -960, -960, -960, -960, -960, + -960, -960, -960, -960, -960, -960, -960, -960, -960, -960, + -960, -960, -960, -960, -960, -960, -960, -960, -960, -960, + -960, -960, -960, -960, -960, -960, -960, -960, -960, -960, + -960, -960, -960, -960, -960, -960, -960, -960, -960, -960, + -960, -960, -960, -960, -960, -960, -960, -960, -960, -960, + -960, -960, -960, -960, -960, -960, -960, -960, -960, -960, + + -960, -960, -960, -960, -960, -960, -960, -960, -960, -960, + -960, -960, -960, -960, -960, -960, -960, -960, -960, -960, + -960, -960, -960, -960, -960, -960, -960, -960 + }, + + { + 55, -961, -961, -961, -961, -961, -961, -961, -961, -961, + -961, -961, -961, -961, -961, -961, -961, -961, -961, -961, + -961, -961, -961, -961, -961, -961, -961, -961, -961, -961, + -961, -961, -961, -961, -961, -961, -961, -961, -961, -961, + -961, -961, -961, -961, -961, -961, -961, -961, -961, -961, + -961, -961, -961, -961, -961, -961, -961, -961, -961, -961, + -961, -961, -961, -961, -961, -961, -961, -961, -961, -961, + + -961, -961, -961, -961, -961, -961, -961, -961, -961, -961, + -961, -961, -961, -961, -961, -961, -961, -961, -961, -961, + -961, -961, -961, -961, -961, -961, -961, -961, -961, -961, + -961, -961, -961, -961, -961, -961, -961, -961, -961, -961, + -961, -961, -961, -961, -961, -961, -961, -961, -961, -961, + -961, -961, -961, -961, -961, -961, -961, -961 + }, + + { + 55, -962, -962, -962, -962, -962, -962, -962, -962, -962, + -962, -962, -962, -962, -962, -962, -962, -962, -962, -962, + -962, -962, -962, -962, -962, -962, -962, -962, -962, -962, + -962, -962, -962, -962, -962, -962, -962, -962, -962, -962, + + -962, -962, -962, -962, -962, -962, -962, -962, -962, -962, + -962, -962, -962, -962, -962, -962, -962, -962, -962, -962, + -962, -962, -962, -962, -962, -962, -962, -962, -962, -962, + -962, -962, -962, -962, -962, -962, -962, -962, -962, -962, + -962, -962, -962, -962, -962, -962, -962, -962, -962, -962, + -962, -962, -962, -962, -962, -962, -962, -962, -962, -962, + -962, -962, -962, -962, -962, -962, -962, -962, -962, -962, + -962, -962, -962, -962, -962, -962, -962, -962, -962, -962, + -962, -962, -962, -962, -962, -962, -962, -962 + }, + + { + 55, -963, -963, -963, -963, -963, -963, -963, -963, -963, + + -963, -963, -963, -963, -963, -963, -963, -963, -963, -963, + -963, -963, -963, -963, -963, -963, -963, -963, -963, -963, + -963, -963, 1051, -963, -963, -963, -963, -963, -963, -963, + -963, -963, -963, -963, -963, -963, -963, -963, -963, -963, + -963, -963, -963, -963, -963, -963, -963, -963, -963, -963, + -963, -963, -963, -963, -963, -963, -963, -963, -963, -963, + -963, -963, -963, -963, -963, -963, -963, -963, -963, -963, + -963, -963, -963, -963, -963, -963, -963, -963, -963, -963, + -963, -963, -963, -963, -963, -963, -963, -963, -963, -963, + -963, -963, -963, -963, -963, -963, -963, -963, -963, -963, + + -963, -963, -963, -963, -963, -963, -963, -963, -963, -963, + -963, -963, -963, -963, -963, -963, -963, -963 + }, + + { + 55, -964, -964, -964, -964, -964, -964, -964, -964, -964, + -964, -964, -964, -964, -964, -964, -964, -964, -964, -964, + -964, -964, -964, -964, -964, -964, -964, -964, -964, -964, + -964, -964, 1052, -964, -964, -964, -964, -964, -964, -964, + -964, -964, -964, -964, -964, -964, -964, -964, -964, -964, + -964, -964, -964, -964, -964, -964, -964, -964, -964, -964, + -964, -964, -964, -964, -964, -964, -964, -964, -964, -964, + -964, -964, -964, -964, -964, -964, -964, -964, -964, -964, + + -964, -964, -964, -964, -964, -964, -964, -964, -964, -964, + -964, -964, -964, -964, -964, -964, -964, -964, -964, -964, + -964, -964, -964, -964, -964, -964, -964, -964, -964, -964, + -964, -964, -964, -964, -964, -964, -964, -964, -964, -964, + -964, -964, -964, -964, -964, -964, -964, -964 + }, + + { + 55, -965, -965, -965, -965, -965, -965, -965, -965, -965, + -965, -965, -965, -965, -965, -965, -965, -965, -965, -965, + -965, -965, -965, -965, -965, -965, -965, -965, -965, -965, + -965, -965, 1053, -965, -965, -965, -965, -965, -965, -965, + -965, -965, -965, -965, -965, -965, -965, -965, -965, -965, + + -965, -965, -965, -965, -965, -965, -965, -965, -965, -965, + -965, -965, -965, -965, -965, -965, -965, -965, -965, -965, + -965, -965, -965, -965, -965, -965, -965, -965, -965, -965, + -965, -965, -965, -965, -965, -965, -965, -965, -965, -965, + -965, -965, -965, -965, -965, -965, -965, -965, -965, -965, + -965, -965, -965, -965, -965, -965, -965, -965, -965, -965, + -965, -965, -965, -965, -965, -965, -965, -965, -965, -965, + -965, -965, -965, -965, -965, -965, -965, -965 + }, + + { + 55, -966, -966, -966, -966, -966, -966, -966, -966, -966, + -966, -966, -966, -966, -966, -966, -966, -966, -966, -966, + + -966, -966, -966, -966, -966, -966, -966, -966, -966, -966, + -966, -966, 1054, -966, -966, -966, -966, -966, -966, -966, + -966, -966, -966, -966, -966, -966, -966, -966, -966, -966, + -966, -966, -966, -966, -966, -966, -966, -966, -966, -966, + -966, -966, -966, -966, -966, -966, -966, -966, -966, -966, + -966, -966, -966, -966, -966, -966, -966, -966, -966, -966, + -966, -966, -966, -966, -966, -966, -966, -966, -966, -966, + -966, -966, -966, -966, -966, -966, -966, -966, -966, -966, + -966, -966, -966, -966, -966, -966, -966, -966, -966, -966, + -966, -966, -966, -966, -966, -966, -966, -966, -966, -966, + + -966, -966, -966, -966, -966, -966, -966, -966 + }, + + { + 55, -967, -967, -967, -967, -967, -967, -967, -967, -967, + -967, -967, -967, -967, -967, -967, -967, -967, -967, -967, + -967, -967, -967, -967, -967, -967, -967, -967, -967, -967, + -967, -967, -967, -967, -967, -967, -967, -967, -967, -967, + -967, -967, -967, -967, -967, -967, -967, -967, -967, -967, + -967, -967, -967, -967, -967, -967, -967, -967, -967, -967, + -967, -967, -967, -967, -967, -967, -967, -967, -967, -967, + -967, -967, -967, -967, -967, -967, -967, -967, -967, -967, + -967, -967, -967, -967, -967, -967, -967, -967, -967, -967, + + -967, -967, -967, -967, -967, -967, -967, -967, -967, -967, + -967, -967, -967, -967, -967, -967, -967, -967, -967, -967, + -967, -967, -967, -967, -967, -967, -967, -967, -967, -967, + -967, -967, -967, -967, -967, -967, -967, -967 + }, + + { + 55, -968, -968, -968, -968, -968, -968, -968, -968, -968, + -968, -968, -968, -968, -968, -968, -968, -968, -968, -968, + -968, -968, -968, -968, -968, -968, -968, -968, -968, -968, + -968, -968, -968, -968, -968, -968, -968, -968, -968, -968, + -968, -968, -968, -968, -968, -968, -968, -968, -968, -968, + -968, -968, -968, -968, -968, -968, -968, -968, -968, -968, + + -968, -968, -968, -968, -968, -968, -968, -968, -968, -968, + -968, -968, -968, -968, -968, -968, -968, -968, -968, -968, + -968, -968, -968, -968, -968, -968, -968, -968, -968, -968, + -968, -968, -968, -968, -968, -968, -968, -968, -968, -968, + -968, -968, -968, -968, -968, -968, -968, -968, -968, -968, + -968, -968, -968, -968, -968, -968, -968, -968, -968, -968, + -968, -968, -968, -968, -968, -968, -968, -968 + }, + + { + 55, -969, -969, -969, -969, -969, -969, -969, -969, -969, + -969, -969, -969, -969, -969, -969, -969, -969, -969, -969, + -969, -969, -969, -969, -969, -969, -969, -969, -969, -969, + + -969, -969, 1055, -969, -969, -969, -969, -969, -969, -969, + -969, -969, -969, -969, -969, -969, -969, -969, -969, -969, + -969, -969, -969, -969, -969, -969, -969, -969, -969, -969, + -969, -969, -969, -969, -969, -969, -969, -969, -969, -969, + -969, -969, -969, -969, -969, -969, -969, -969, -969, -969, + -969, -969, -969, -969, -969, -969, -969, -969, -969, -969, + -969, -969, -969, -969, -969, -969, -969, -969, -969, -969, + -969, -969, -969, -969, -969, -969, -969, -969, -969, -969, + -969, -969, -969, -969, -969, -969, -969, -969, -969, -969, + -969, -969, -969, -969, -969, -969, -969, -969 + + }, + + { + 55, -970, -970, -970, -970, -970, -970, -970, -970, -970, + -970, -970, -970, -970, -970, -970, -970, -970, -970, -970, + -970, -970, -970, -970, -970, -970, -970, -970, -970, -970, + -970, -970, 1056, -970, -970, -970, -970, -970, -970, -970, + -970, -970, -970, -970, -970, -970, -970, -970, -970, -970, + -970, -970, -970, -970, -970, -970, -970, -970, -970, -970, + -970, -970, -970, -970, -970, -970, -970, -970, -970, -970, + -970, -970, -970, -970, -970, -970, -970, -970, -970, -970, + -970, -970, -970, -970, -970, -970, -970, -970, -970, -970, + -970, -970, -970, -970, -970, -970, -970, -970, -970, -970, + + -970, -970, -970, -970, -970, -970, -970, -970, -970, -970, + -970, -970, -970, -970, -970, -970, -970, -970, -970, -970, + -970, -970, -970, -970, -970, -970, -970, -970 + }, + + { + 55, -971, -971, -971, -971, -971, -971, -971, -971, -971, + -971, -971, -971, -971, -971, -971, -971, -971, -971, -971, + -971, -971, -971, -971, -971, -971, -971, -971, -971, -971, + -971, -971, 1057, -971, -971, -971, -971, -971, -971, -971, + -971, -971, -971, -971, -971, -971, -971, -971, -971, -971, + -971, -971, -971, -971, -971, -971, -971, -971, -971, -971, + -971, -971, -971, -971, -971, -971, -971, -971, -971, -971, + + -971, -971, -971, -971, -971, -971, -971, -971, -971, -971, + -971, -971, -971, -971, -971, -971, -971, -971, -971, -971, + -971, -971, -971, -971, -971, -971, -971, -971, -971, -971, + -971, -971, -971, -971, -971, -971, -971, -971, -971, -971, + -971, -971, -971, -971, -971, -971, -971, -971, -971, -971, + -971, -971, -971, -971, -971, -971, -971, -971 + }, + + { + 55, -972, -972, -972, -972, -972, -972, -972, -972, -972, + -972, -972, -972, -972, -972, -972, -972, -972, -972, -972, + -972, -972, -972, -972, -972, -972, -972, -972, -972, -972, + -972, -972, 1058, -972, -972, -972, -972, -972, -972, -972, + + -972, -972, -972, -972, -972, -972, -972, -972, -972, -972, + -972, -972, -972, -972, -972, -972, -972, -972, -972, -972, + -972, -972, -972, -972, -972, -972, -972, -972, -972, -972, + -972, -972, -972, -972, -972, -972, -972, -972, -972, -972, + -972, -972, -972, -972, -972, -972, -972, -972, -972, -972, + -972, -972, -972, -972, -972, -972, -972, -972, -972, -972, + -972, -972, -972, -972, -972, -972, -972, -972, -972, -972, + -972, -972, -972, -972, -972, -972, -972, -972, -972, -972, + -972, -972, -972, -972, -972, -972, -972, -972 + }, + + { + 55, -973, -973, -973, -973, -973, -973, -973, -973, -973, + + -973, -973, -973, -973, -973, -973, -973, -973, -973, -973, + -973, -973, -973, -973, -973, -973, -973, -973, -973, -973, + -973, -973, 1059, -973, -973, -973, -973, -973, -973, -973, + -973, -973, -973, -973, -973, -973, -973, -973, -973, -973, + -973, -973, -973, -973, -973, -973, -973, -973, -973, -973, + -973, -973, -973, -973, -973, -973, -973, -973, -973, -973, + 1060, -973, -973, 1061, -973, -973, -973, -973, -973, -973, + -973, -973, -973, -973, -973, -973, -973, -973, -973, -973, + -973, -973, -973, -973, -973, -973, -973, -973, -973, -973, + -973, -973, -973, -973, -973, -973, -973, -973, -973, -973, + + -973, -973, -973, -973, -973, -973, -973, -973, -973, -973, + -973, -973, -973, -973, -973, -973, -973, -973 + }, + + { + 55, -974, -974, -974, -974, -974, -974, -974, -974, -974, + -974, -974, -974, -974, -974, -974, -974, -974, -974, -974, + -974, -974, -974, -974, -974, -974, -974, -974, -974, -974, + -974, -974, 1062, -974, -974, -974, -974, -974, -974, -974, + -974, -974, -974, -974, -974, -974, -974, -974, -974, -974, + -974, -974, -974, -974, -974, -974, -974, -974, -974, -974, + -974, -974, -974, -974, -974, -974, -974, -974, -974, -974, + -974, -974, -974, -974, -974, -974, -974, -974, -974, -974, + + -974, -974, -974, -974, -974, -974, -974, -974, -974, -974, + -974, -974, -974, -974, -974, -974, -974, -974, -974, -974, + -974, -974, -974, -974, -974, -974, -974, -974, -974, -974, + -974, -974, -974, -974, -974, -974, -974, -974, -974, -974, + -974, -974, -974, -974, -974, -974, -974, -974 + }, + + { + 55, -975, -975, -975, -975, -975, -975, -975, -975, -975, + -975, -975, -975, -975, -975, -975, -975, -975, -975, -975, + -975, -975, -975, -975, -975, -975, -975, -975, -975, -975, + -975, -975, 1063, -975, -975, -975, -975, -975, -975, -975, + -975, -975, -975, -975, -975, -975, -975, -975, -975, -975, + + -975, -975, -975, -975, -975, -975, -975, -975, -975, -975, + -975, -975, -975, -975, -975, -975, -975, -975, -975, -975, + -975, -975, -975, -975, -975, -975, -975, -975, -975, -975, + -975, -975, -975, -975, -975, -975, -975, -975, -975, -975, + -975, -975, -975, -975, -975, -975, -975, -975, -975, -975, + -975, -975, -975, -975, -975, -975, -975, -975, -975, -975, + -975, -975, -975, -975, -975, -975, -975, -975, -975, -975, + -975, -975, -975, -975, -975, -975, -975, -975 + }, + + { + 55, -976, -976, -976, -976, -976, -976, -976, -976, -976, + -976, -976, -976, -976, -976, -976, -976, -976, -976, -976, + + -976, -976, -976, -976, -976, -976, -976, -976, -976, -976, + -976, -976, 1064, -976, -976, -976, -976, -976, -976, -976, + -976, -976, -976, -976, -976, -976, -976, -976, -976, -976, + -976, -976, -976, -976, -976, -976, -976, -976, -976, -976, + -976, -976, -976, -976, -976, -976, -976, -976, -976, -976, + -976, -976, -976, -976, -976, -976, -976, -976, -976, -976, + -976, -976, -976, -976, -976, -976, -976, -976, -976, -976, + -976, -976, -976, -976, -976, -976, -976, -976, -976, -976, + -976, -976, -976, -976, -976, -976, -976, -976, -976, -976, + -976, -976, -976, -976, -976, -976, -976, -976, -976, -976, + + -976, -976, -976, -976, -976, -976, -976, -976 + }, + + { + 55, -977, -977, -977, -977, -977, -977, -977, -977, -977, + -977, -977, -977, -977, -977, -977, -977, -977, -977, -977, + -977, -977, -977, -977, -977, -977, -977, -977, -977, -977, + -977, -977, 1065, -977, -977, -977, -977, -977, -977, -977, + -977, -977, -977, -977, -977, -977, -977, -977, -977, -977, + -977, -977, -977, -977, -977, -977, -977, -977, -977, -977, + -977, -977, -977, -977, -977, -977, -977, -977, -977, -977, + -977, -977, -977, -977, -977, -977, -977, -977, -977, -977, + -977, -977, -977, -977, -977, -977, -977, -977, -977, -977, + + -977, -977, -977, -977, -977, -977, -977, -977, -977, -977, + -977, -977, -977, -977, -977, -977, -977, -977, -977, -977, + -977, -977, -977, -977, -977, -977, -977, -977, -977, -977, + -977, -977, -977, -977, -977, -977, -977, -977 + }, + + { + 55, -978, -978, -978, -978, -978, -978, -978, -978, -978, + -978, -978, -978, -978, -978, -978, -978, -978, -978, -978, + -978, -978, -978, -978, -978, -978, -978, -978, -978, -978, + -978, -978, -978, -978, -978, -978, -978, -978, -978, -978, + -978, -978, -978, -978, -978, -978, -978, -978, -978, -978, + -978, -978, -978, -978, -978, -978, -978, -978, -978, -978, + + -978, -978, -978, -978, -978, -978, 1066, -978, -978, -978, + -978, -978, 1067, -978, -978, -978, 1068, -978, -978, -978, + -978, -978, -978, -978, -978, -978, -978, -978, 1069, 1070, + 1071, -978, -978, -978, -978, -978, -978, -978, -978, -978, + -978, -978, -978, -978, -978, -978, -978, -978, -978, -978, + -978, -978, -978, -978, -978, -978, -978, -978, -978, -978, + -978, -978, -978, -978, -978, -978, -978, -978 + }, + + { + 55, -979, -979, -979, -979, -979, -979, -979, -979, -979, + -979, -979, -979, -979, -979, -979, -979, -979, -979, -979, + -979, -979, -979, -979, -979, -979, -979, -979, -979, -979, + + -979, -979, -979, -979, -979, -979, -979, -979, -979, -979, + -979, -979, -979, -979, -979, -979, -979, -979, -979, -979, + -979, -979, -979, -979, -979, -979, -979, -979, -979, -979, + -979, -979, -979, -979, -979, -979, -979, -979, -979, -979, + -979, -979, -979, -979, -979, -979, -979, -979, -979, -979, + -979, -979, -979, -979, 1072, -979, -979, -979, -979, -979, + -979, -979, -979, -979, -979, -979, -979, -979, -979, -979, + -979, -979, -979, -979, -979, -979, -979, -979, -979, -979, + -979, -979, -979, -979, -979, -979, -979, -979, -979, -979, + -979, -979, -979, -979, -979, -979, -979, -979 + + }, + + { + 55, -980, -980, -980, -980, -980, -980, -980, -980, -980, + -980, -980, -980, -980, -980, -980, -980, -980, -980, -980, + -980, -980, -980, -980, -980, -980, -980, -980, -980, -980, + -980, -980, 1073, -980, -980, -980, -980, -980, -980, -980, + -980, -980, -980, -980, -980, -980, -980, -980, -980, -980, + -980, -980, -980, -980, -980, -980, -980, -980, -980, -980, + -980, -980, -980, -980, -980, -980, -980, -980, -980, -980, + -980, -980, -980, -980, -980, -980, -980, -980, -980, -980, + -980, -980, -980, -980, -980, -980, -980, -980, -980, -980, + -980, -980, -980, -980, -980, -980, -980, -980, -980, -980, + + -980, -980, -980, -980, -980, -980, -980, -980, -980, -980, + -980, -980, -980, -980, -980, -980, -980, -980, -980, -980, + -980, -980, -980, -980, -980, -980, -980, -980 + }, + + { + 55, -981, -981, -981, -981, -981, -981, -981, -981, -981, + -981, -981, -981, -981, -981, -981, -981, -981, -981, -981, + -981, -981, -981, -981, -981, -981, -981, -981, -981, -981, + -981, -981, 1074, -981, -981, -981, -981, -981, -981, -981, + -981, -981, -981, -981, -981, -981, -981, -981, -981, -981, + -981, -981, -981, -981, -981, -981, -981, -981, -981, -981, + -981, -981, -981, -981, -981, -981, -981, -981, -981, -981, + + -981, -981, -981, -981, -981, -981, -981, -981, -981, -981, + -981, -981, -981, -981, -981, -981, -981, -981, -981, -981, + -981, -981, -981, -981, -981, -981, -981, -981, -981, -981, + -981, -981, -981, -981, -981, -981, -981, -981, -981, -981, + -981, -981, -981, -981, -981, -981, -981, -981, -981, -981, + -981, -981, -981, -981, -981, -981, -981, -981 + }, + + { + 55, -982, -982, -982, -982, -982, -982, -982, -982, -982, + -982, -982, -982, -982, -982, -982, -982, -982, -982, -982, + -982, -982, -982, -982, -982, -982, -982, -982, -982, -982, + -982, -982, 1075, -982, -982, -982, -982, -982, -982, -982, + + -982, -982, -982, -982, -982, -982, -982, -982, -982, -982, + -982, -982, -982, -982, -982, -982, -982, -982, -982, -982, + -982, -982, -982, -982, -982, -982, -982, -982, -982, -982, + -982, -982, -982, -982, -982, -982, -982, -982, -982, -982, + -982, -982, -982, -982, -982, -982, -982, -982, -982, -982, + -982, -982, -982, -982, -982, -982, -982, -982, -982, -982, + -982, -982, -982, -982, -982, -982, -982, -982, -982, -982, + -982, -982, -982, -982, -982, -982, -982, -982, -982, -982, + -982, -982, -982, -982, -982, -982, -982, -982 + }, + + { + 55, -983, -983, -983, -983, -983, -983, -983, -983, -983, + + -983, -983, -983, -983, -983, -983, -983, -983, -983, -983, + -983, -983, -983, -983, -983, -983, -983, -983, -983, -983, + -983, -983, 1076, -983, -983, -983, -983, -983, -983, -983, + -983, -983, -983, -983, -983, -983, -983, -983, -983, -983, + -983, -983, -983, -983, -983, -983, -983, -983, -983, -983, + -983, -983, -983, -983, -983, -983, -983, -983, -983, -983, + -983, -983, -983, -983, -983, -983, -983, -983, -983, -983, + -983, -983, -983, -983, -983, -983, -983, -983, -983, -983, + -983, -983, -983, -983, -983, -983, -983, -983, -983, -983, + -983, -983, -983, -983, -983, -983, -983, -983, -983, -983, + + -983, -983, -983, -983, -983, -983, -983, -983, -983, -983, + -983, -983, -983, -983, -983, -983, -983, -983 + }, + + { + 55, -984, -984, -984, -984, -984, -984, -984, -984, -984, + -984, -984, -984, -984, -984, -984, -984, -984, -984, -984, + -984, -984, -984, -984, -984, -984, -984, -984, -984, -984, + -984, -984, 1077, -984, -984, -984, -984, -984, -984, -984, + -984, -984, -984, -984, -984, -984, -984, -984, -984, -984, + -984, -984, -984, -984, -984, -984, -984, -984, -984, -984, + -984, -984, -984, -984, -984, -984, -984, -984, -984, -984, + -984, -984, -984, -984, -984, -984, -984, -984, 1078, -984, + + -984, -984, -984, -984, -984, -984, -984, -984, -984, -984, + -984, -984, -984, -984, -984, -984, -984, -984, -984, -984, + -984, -984, -984, -984, -984, -984, -984, -984, -984, -984, + -984, -984, -984, -984, -984, -984, -984, -984, -984, -984, + -984, -984, -984, -984, -984, -984, -984, -984 + }, + + { + 55, -985, -985, -985, -985, -985, -985, -985, -985, -985, + -985, -985, -985, -985, -985, -985, -985, -985, -985, -985, + -985, -985, -985, -985, -985, -985, -985, -985, -985, -985, + -985, -985, 1079, -985, -985, -985, -985, -985, -985, -985, + -985, -985, -985, -985, -985, -985, -985, -985, -985, -985, + + -985, -985, -985, -985, -985, -985, -985, -985, -985, -985, + -985, -985, -985, -985, -985, -985, -985, -985, -985, -985, + -985, -985, -985, -985, -985, -985, -985, -985, -985, -985, + -985, -985, -985, -985, -985, -985, -985, -985, -985, -985, + -985, -985, -985, -985, -985, -985, -985, -985, -985, -985, + -985, -985, -985, -985, -985, -985, -985, -985, -985, -985, + -985, -985, -985, -985, -985, -985, -985, -985, -985, -985, + -985, -985, -985, -985, -985, -985, -985, -985 + }, + + { + 55, -986, -986, -986, -986, -986, -986, -986, -986, -986, + -986, -986, -986, -986, -986, -986, -986, -986, -986, -986, + + -986, -986, -986, -986, -986, -986, -986, -986, -986, -986, + -986, -986, 1080, -986, -986, -986, -986, -986, -986, -986, + -986, -986, -986, -986, -986, -986, -986, -986, -986, -986, + -986, -986, -986, -986, -986, -986, -986, -986, -986, -986, + -986, -986, -986, -986, -986, -986, -986, -986, -986, -986, + -986, -986, -986, -986, -986, -986, -986, -986, -986, -986, + -986, -986, -986, -986, -986, -986, -986, -986, -986, -986, + -986, -986, -986, -986, -986, -986, -986, -986, -986, -986, + -986, -986, -986, -986, -986, -986, -986, -986, -986, -986, + -986, -986, -986, -986, -986, -986, -986, -986, -986, -986, + + -986, -986, -986, -986, -986, -986, -986, -986 + }, + + { + 55, -987, -987, -987, -987, -987, -987, -987, -987, -987, + -987, -987, -987, -987, -987, -987, -987, -987, -987, -987, + -987, -987, -987, -987, -987, -987, -987, -987, -987, -987, + -987, -987, 1081, -987, -987, -987, -987, -987, -987, -987, + -987, -987, -987, -987, -987, -987, -987, -987, -987, -987, + -987, -987, -987, -987, -987, -987, -987, -987, -987, -987, + -987, -987, -987, -987, -987, -987, -987, -987, -987, -987, + -987, -987, -987, -987, -987, -987, -987, -987, -987, -987, + -987, -987, -987, -987, -987, -987, -987, -987, -987, -987, + + -987, -987, -987, -987, -987, -987, -987, -987, -987, -987, + -987, -987, -987, -987, -987, -987, -987, -987, -987, -987, + -987, -987, -987, -987, -987, -987, -987, -987, -987, -987, + -987, -987, -987, -987, -987, -987, -987, -987 + }, + + { + 55, -988, -988, -988, -988, -988, -988, -988, -988, -988, + -988, -988, -988, -988, -988, -988, -988, -988, -988, -988, + -988, -988, -988, -988, -988, -988, -988, -988, -988, -988, + -988, -988, 1082, -988, -988, -988, -988, -988, -988, -988, + -988, -988, -988, -988, -988, -988, -988, -988, -988, -988, + -988, -988, -988, -988, -988, -988, -988, -988, -988, -988, + + -988, -988, -988, -988, -988, -988, -988, -988, -988, -988, + -988, -988, -988, -988, -988, -988, -988, -988, -988, -988, + -988, -988, -988, -988, -988, -988, -988, -988, -988, -988, + -988, -988, -988, -988, -988, -988, -988, -988, -988, -988, + -988, -988, -988, -988, -988, -988, -988, -988, -988, -988, + -988, -988, -988, -988, -988, -988, -988, -988, -988, -988, + -988, -988, -988, -988, -988, -988, -988, -988 + }, + + { + 55, -989, -989, -989, -989, -989, -989, -989, -989, -989, + -989, -989, -989, -989, -989, -989, -989, -989, -989, -989, + -989, -989, -989, -989, -989, -989, -989, -989, -989, -989, + + -989, -989, 1083, -989, -989, -989, -989, -989, -989, -989, + -989, -989, -989, -989, -989, -989, -989, -989, -989, -989, + -989, -989, -989, -989, -989, -989, -989, -989, -989, -989, + -989, -989, -989, -989, -989, -989, -989, -989, -989, -989, + -989, -989, -989, -989, -989, -989, -989, -989, -989, -989, + -989, -989, -989, -989, -989, -989, -989, -989, -989, -989, + -989, -989, -989, -989, -989, -989, -989, -989, -989, -989, + -989, -989, -989, -989, -989, -989, -989, -989, -989, -989, + -989, -989, -989, -989, -989, -989, -989, -989, -989, -989, + -989, -989, -989, -989, -989, -989, -989, -989 + + }, + + { + 55, -990, -990, -990, -990, -990, -990, -990, -990, -990, + -990, -990, -990, -990, -990, -990, -990, -990, -990, -990, + -990, -990, -990, -990, -990, -990, -990, -990, -990, -990, + -990, -990, -990, -990, -990, -990, -990, -990, -990, -990, + -990, -990, -990, -990, -990, -990, -990, -990, -990, -990, + -990, -990, -990, -990, -990, -990, -990, -990, -990, -990, + -990, -990, -990, -990, -990, -990, -990, -990, -990, -990, + -990, -990, -990, -990, -990, -990, -990, -990, -990, -990, + -990, -990, -990, 1084, -990, -990, -990, -990, -990, -990, + -990, -990, -990, -990, -990, -990, -990, -990, -990, -990, + + -990, -990, -990, -990, -990, -990, -990, -990, -990, -990, + -990, -990, -990, -990, -990, -990, -990, -990, -990, -990, + -990, -990, -990, -990, -990, -990, -990, -990 + }, + + { + 55, -991, -991, -991, -991, -991, -991, -991, -991, -991, + -991, -991, -991, -991, -991, -991, -991, -991, -991, -991, + -991, -991, -991, -991, -991, -991, -991, -991, -991, -991, + -991, -991, -991, -991, -991, -991, -991, -991, -991, -991, + -991, -991, -991, -991, -991, -991, -991, -991, -991, -991, + -991, -991, -991, -991, -991, -991, -991, -991, -991, -991, + -991, -991, -991, -991, -991, -991, -991, -991, -991, -991, + + -991, -991, -991, -991, -991, -991, -991, -991, -991, -991, + -991, -991, -991, -991, -991, -991, -991, -991, -991, -991, + -991, -991, -991, -991, -991, -991, -991, -991, -991, -991, + -991, -991, -991, -991, -991, -991, -991, -991, -991, -991, + -991, -991, -991, -991, -991, -991, -991, -991, -991, -991, + -991, -991, -991, -991, -991, -991, -991, -991 + }, + + { + 55, -992, -992, -992, -992, -992, -992, -992, -992, -992, + -992, -992, -992, -992, -992, -992, -992, -992, -992, -992, + -992, -992, -992, -992, -992, -992, -992, -992, -992, -992, + -992, -992, -992, -992, -992, -992, -992, -992, -992, -992, + + -992, -992, -992, -992, -992, -992, -992, -992, -992, -992, + -992, -992, -992, -992, -992, -992, -992, -992, -992, -992, + -992, -992, -992, -992, -992, -992, -992, -992, -992, -992, + -992, -992, -992, -992, -992, -992, -992, -992, -992, -992, + -992, 1085, -992, -992, -992, -992, -992, -992, -992, -992, + -992, -992, -992, -992, -992, -992, -992, -992, -992, -992, + -992, -992, -992, -992, -992, -992, -992, -992, -992, -992, + -992, -992, -992, -992, -992, -992, -992, -992, -992, -992, + -992, -992, -992, -992, -992, -992, -992, -992 + }, + + { + 55, -993, -993, -993, -993, -993, -993, -993, -993, -993, + + -993, -993, -993, -993, -993, -993, -993, -993, -993, -993, + -993, -993, -993, -993, -993, -993, -993, -993, -993, -993, + -993, -993, -993, -993, -993, -993, -993, -993, -993, -993, + -993, -993, -993, -993, -993, -993, -993, -993, -993, -993, + -993, -993, -993, -993, -993, -993, -993, -993, -993, -993, + -993, -993, -993, -993, -993, -993, -993, -993, -993, -993, + -993, -993, -993, -993, -993, -993, -993, -993, -993, -993, + -993, -993, -993, -993, -993, -993, -993, -993, -993, -993, + -993, -993, -993, -993, -993, -993, -993, -993, -993, -993, + -993, -993, -993, -993, -993, -993, -993, -993, -993, -993, + + -993, -993, -993, -993, -993, -993, -993, -993, -993, -993, + -993, -993, -993, -993, -993, -993, -993, -993 + }, + + { + 55, -994, -994, -994, -994, -994, -994, -994, -994, -994, + -994, -994, -994, -994, -994, -994, -994, -994, -994, -994, + -994, -994, -994, -994, -994, -994, -994, -994, -994, -994, + -994, -994, -994, -994, -994, -994, -994, -994, -994, -994, + -994, -994, -994, -994, -994, -994, -994, -994, -994, -994, + -994, -994, -994, -994, -994, -994, -994, -994, -994, -994, + -994, -994, -994, -994, -994, -994, -994, -994, -994, -994, + -994, -994, -994, -994, -994, -994, -994, -994, -994, -994, + + -994, -994, -994, -994, -994, -994, -994, -994, -994, -994, + -994, -994, -994, -994, -994, -994, -994, -994, -994, -994, + -994, -994, -994, -994, -994, -994, -994, -994, -994, -994, + -994, -994, -994, -994, -994, -994, -994, -994, -994, -994, + -994, -994, -994, -994, -994, -994, -994, -994 + }, + + { + 55, -995, -995, -995, -995, -995, -995, -995, -995, -995, + -995, -995, -995, -995, -995, -995, -995, -995, -995, -995, + -995, -995, -995, -995, -995, -995, -995, -995, -995, -995, + -995, -995, -995, -995, -995, -995, -995, -995, -995, -995, + -995, -995, -995, -995, -995, -995, -995, -995, -995, -995, + + -995, -995, -995, -995, -995, -995, -995, -995, -995, -995, + -995, -995, -995, -995, -995, -995, -995, -995, -995, -995, + 1086, -995, -995, -995, -995, -995, -995, -995, -995, -995, + -995, -995, -995, -995, -995, -995, -995, -995, -995, -995, + -995, -995, -995, -995, -995, -995, -995, -995, -995, -995, + -995, -995, -995, -995, -995, -995, -995, -995, -995, -995, + -995, -995, -995, -995, -995, -995, -995, -995, -995, -995, + -995, -995, -995, -995, -995, -995, -995, -995 + }, + + { + 55, -996, -996, -996, -996, -996, -996, -996, -996, -996, + -996, -996, -996, -996, -996, -996, -996, -996, -996, -996, + + -996, -996, -996, -996, -996, -996, -996, -996, -996, -996, + -996, -996, -996, -996, -996, -996, -996, -996, -996, -996, + -996, -996, -996, -996, -996, -996, -996, -996, -996, -996, + -996, -996, -996, -996, -996, -996, -996, -996, -996, -996, + -996, -996, -996, -996, -996, -996, -996, -996, -996, -996, + -996, -996, -996, -996, -996, -996, -996, -996, -996, -996, + -996, -996, -996, -996, -996, -996, -996, -996, -996, -996, + -996, -996, -996, -996, -996, -996, -996, -996, -996, -996, + -996, -996, -996, -996, -996, -996, -996, -996, -996, -996, + -996, -996, -996, -996, -996, -996, -996, -996, -996, -996, + + -996, -996, -996, -996, -996, -996, -996, -996 + }, + + { + 55, -997, -997, -997, -997, -997, -997, -997, -997, -997, + -997, -997, -997, -997, -997, -997, -997, -997, -997, -997, + -997, -997, -997, -997, -997, -997, -997, -997, -997, -997, + -997, -997, -997, -997, -997, -997, -997, -997, -997, -997, + -997, -997, -997, -997, -997, -997, -997, -997, -997, -997, + -997, -997, -997, -997, -997, -997, -997, -997, -997, -997, + -997, -997, -997, -997, -997, -997, -997, -997, -997, -997, + -997, -997, -997, -997, -997, -997, -997, -997, -997, -997, + -997, -997, -997, -997, -997, -997, -997, -997, -997, -997, + + -997, -997, -997, -997, -997, -997, -997, -997, -997, -997, + -997, -997, -997, -997, -997, -997, -997, -997, -997, -997, + -997, -997, -997, -997, -997, -997, -997, -997, -997, -997, + -997, -997, -997, -997, -997, -997, -997, -997 + }, + + { + 55, -998, -998, -998, -998, -998, -998, -998, -998, -998, + -998, -998, -998, -998, -998, -998, -998, -998, -998, -998, + -998, -998, -998, -998, -998, -998, -998, -998, -998, -998, + -998, -998, -998, -998, -998, -998, -998, -998, -998, -998, + -998, -998, -998, -998, -998, -998, -998, -998, -998, -998, + -998, -998, -998, -998, -998, -998, -998, -998, -998, -998, + + -998, -998, -998, -998, -998, -998, -998, -998, -998, -998, + -998, -998, -998, -998, -998, -998, -998, -998, -998, -998, + -998, -998, -998, -998, -998, -998, -998, -998, -998, -998, + -998, -998, -998, -998, -998, -998, -998, -998, -998, -998, + -998, -998, -998, -998, -998, -998, -998, -998, -998, -998, + -998, -998, -998, -998, -998, -998, -998, -998, -998, -998, + -998, -998, -998, -998, -998, -998, -998, -998 + }, + + { + 55, -999, -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, + + -999, -999, 1087, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999 + + }, + + { + 55,-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000, + -1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000, + -1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000, + -1000,-1000, 1088,-1000,-1000,-1000,-1000,-1000,-1000,-1000, + -1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000, + -1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000, + -1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000, + -1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000, + -1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000, + -1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000, + + -1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000, + -1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000, + -1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000 + }, + + { + 55,-1001,-1001,-1001,-1001,-1001,-1001,-1001,-1001,-1001, + -1001,-1001,-1001,-1001,-1001,-1001,-1001,-1001,-1001,-1001, + -1001,-1001,-1001,-1001,-1001,-1001,-1001,-1001,-1001,-1001, + -1001,-1001,-1001,-1001,-1001,-1001,-1001,-1001,-1001,-1001, + -1001,-1001,-1001,-1001,-1001,-1001,-1001,-1001,-1001,-1001, + -1001,-1001,-1001,-1001,-1001,-1001,-1001,-1001,-1001,-1001, + -1001,-1001,-1001,-1001,-1001,-1001,-1001,-1001,-1001,-1001, + + -1001,-1001,-1001,-1001,-1001,-1001,-1001,-1001,-1001,-1001, + -1001,-1001,-1001, 1089,-1001,-1001,-1001,-1001,-1001,-1001, + -1001,-1001,-1001,-1001,-1001,-1001,-1001,-1001,-1001,-1001, + -1001,-1001,-1001,-1001,-1001,-1001,-1001,-1001,-1001,-1001, + -1001,-1001,-1001,-1001,-1001,-1001,-1001,-1001,-1001,-1001, + -1001,-1001,-1001,-1001,-1001,-1001,-1001,-1001 + }, + + { + 55,-1002,-1002,-1002,-1002,-1002,-1002,-1002,-1002,-1002, + -1002,-1002,-1002,-1002,-1002,-1002,-1002,-1002,-1002,-1002, + -1002,-1002,-1002,-1002,-1002,-1002,-1002,-1002,-1002,-1002, + -1002,-1002,-1002,-1002,-1002,-1002,-1002,-1002,-1002,-1002, + + -1002,-1002,-1002,-1002,-1002,-1002,-1002,-1002,-1002,-1002, + -1002,-1002,-1002,-1002,-1002,-1002,-1002,-1002,-1002,-1002, + -1002,-1002,-1002,-1002,-1002,-1002,-1002,-1002,-1002,-1002, + -1002,-1002,-1002,-1002,-1002,-1002,-1002,-1002,-1002,-1002, + -1002,-1002, 1090,-1002,-1002,-1002,-1002,-1002,-1002,-1002, + -1002,-1002,-1002,-1002,-1002,-1002,-1002,-1002,-1002,-1002, + -1002,-1002,-1002,-1002,-1002,-1002,-1002,-1002,-1002,-1002, + -1002,-1002,-1002,-1002,-1002,-1002,-1002,-1002,-1002,-1002, + -1002,-1002,-1002,-1002,-1002,-1002,-1002,-1002 + }, + + { + 55,-1003,-1003,-1003,-1003,-1003,-1003,-1003,-1003,-1003, + + -1003,-1003,-1003,-1003,-1003,-1003,-1003,-1003,-1003,-1003, + -1003,-1003,-1003,-1003,-1003,-1003,-1003,-1003,-1003,-1003, + -1003,-1003, 1091,-1003,-1003,-1003,-1003,-1003,-1003,-1003, + -1003,-1003,-1003,-1003,-1003,-1003,-1003,-1003,-1003,-1003, + -1003,-1003,-1003,-1003,-1003,-1003,-1003,-1003,-1003,-1003, + -1003,-1003,-1003,-1003,-1003,-1003,-1003,-1003,-1003,-1003, + -1003,-1003,-1003,-1003,-1003,-1003,-1003,-1003,-1003,-1003, + -1003,-1003,-1003,-1003,-1003,-1003,-1003,-1003,-1003,-1003, + -1003,-1003,-1003,-1003,-1003,-1003,-1003,-1003,-1003,-1003, + -1003,-1003,-1003,-1003,-1003,-1003,-1003,-1003,-1003,-1003, + + -1003,-1003,-1003,-1003,-1003,-1003,-1003,-1003,-1003,-1003, + -1003,-1003,-1003,-1003,-1003,-1003,-1003,-1003 + }, + + { + 55,-1004,-1004,-1004,-1004,-1004,-1004,-1004,-1004,-1004, + -1004,-1004,-1004,-1004,-1004,-1004,-1004,-1004,-1004,-1004, + -1004,-1004,-1004,-1004,-1004,-1004,-1004,-1004,-1004,-1004, + -1004,-1004,-1004,-1004,-1004,-1004,-1004,-1004,-1004,-1004, + -1004,-1004,-1004,-1004,-1004,-1004,-1004,-1004,-1004,-1004, + -1004,-1004,-1004,-1004,-1004,-1004,-1004,-1004,-1004,-1004, + -1004,-1004,-1004,-1004,-1004,-1004,-1004,-1004,-1004,-1004, + -1004,-1004,-1004,-1004,-1004,-1004,-1004,-1004,-1004,-1004, + + -1004,-1004,-1004,-1004, 1092,-1004,-1004,-1004,-1004,-1004, + -1004,-1004,-1004,-1004,-1004,-1004,-1004,-1004,-1004,-1004, + -1004,-1004,-1004,-1004,-1004,-1004,-1004,-1004,-1004,-1004, + -1004,-1004,-1004,-1004,-1004,-1004,-1004,-1004,-1004,-1004, + -1004,-1004,-1004,-1004,-1004,-1004,-1004,-1004 + }, + + { + 55,-1005,-1005,-1005,-1005,-1005,-1005,-1005,-1005,-1005, + -1005,-1005,-1005,-1005,-1005,-1005,-1005,-1005,-1005,-1005, + -1005,-1005,-1005,-1005,-1005,-1005,-1005,-1005,-1005,-1005, + -1005,-1005, 1093,-1005,-1005,-1005,-1005,-1005,-1005,-1005, + -1005,-1005,-1005,-1005,-1005,-1005,-1005,-1005,-1005,-1005, + + -1005,-1005,-1005,-1005,-1005,-1005,-1005,-1005,-1005,-1005, + -1005,-1005,-1005,-1005,-1005,-1005,-1005,-1005,-1005,-1005, + -1005,-1005,-1005,-1005,-1005,-1005,-1005,-1005,-1005,-1005, + -1005,-1005,-1005,-1005,-1005,-1005,-1005,-1005,-1005,-1005, + -1005,-1005,-1005,-1005,-1005,-1005,-1005,-1005,-1005,-1005, + -1005,-1005,-1005,-1005,-1005,-1005,-1005,-1005,-1005,-1005, + -1005,-1005,-1005,-1005,-1005,-1005,-1005,-1005,-1005,-1005, + -1005,-1005,-1005,-1005,-1005,-1005,-1005,-1005 + }, + + { + 55,-1006,-1006,-1006,-1006,-1006,-1006,-1006,-1006,-1006, + -1006,-1006,-1006,-1006,-1006,-1006,-1006,-1006,-1006,-1006, + + -1006,-1006,-1006,-1006,-1006,-1006,-1006,-1006,-1006,-1006, + -1006,-1006, 1094,-1006,-1006,-1006,-1006,-1006,-1006,-1006, + -1006,-1006,-1006,-1006,-1006,-1006,-1006,-1006,-1006,-1006, + -1006,-1006,-1006,-1006,-1006,-1006,-1006,-1006,-1006,-1006, + -1006,-1006,-1006,-1006,-1006,-1006,-1006,-1006,-1006,-1006, + -1006,-1006,-1006,-1006,-1006,-1006,-1006,-1006,-1006,-1006, + -1006,-1006,-1006,-1006,-1006,-1006,-1006,-1006,-1006,-1006, + -1006,-1006,-1006,-1006,-1006,-1006,-1006,-1006,-1006,-1006, + -1006,-1006,-1006,-1006,-1006,-1006,-1006,-1006,-1006,-1006, + -1006,-1006,-1006,-1006,-1006,-1006,-1006,-1006,-1006,-1006, + + -1006,-1006,-1006,-1006,-1006,-1006,-1006,-1006 + }, + + { + 55,-1007,-1007,-1007,-1007,-1007,-1007,-1007,-1007,-1007, + -1007,-1007,-1007,-1007,-1007,-1007,-1007,-1007,-1007,-1007, + -1007,-1007,-1007,-1007,-1007,-1007,-1007,-1007,-1007,-1007, + -1007,-1007, 1095,-1007,-1007,-1007,-1007,-1007,-1007,-1007, + -1007,-1007,-1007,-1007,-1007,-1007,-1007,-1007,-1007,-1007, + -1007,-1007,-1007,-1007,-1007,-1007,-1007,-1007,-1007,-1007, + -1007,-1007,-1007,-1007,-1007,-1007,-1007,-1007,-1007,-1007, + -1007,-1007,-1007,-1007,-1007,-1007,-1007,-1007,-1007,-1007, + -1007,-1007,-1007,-1007,-1007,-1007,-1007,-1007,-1007,-1007, + + -1007,-1007,-1007,-1007,-1007,-1007,-1007,-1007,-1007,-1007, + -1007,-1007,-1007,-1007,-1007,-1007,-1007,-1007,-1007,-1007, + -1007,-1007,-1007,-1007,-1007,-1007,-1007,-1007,-1007,-1007, + -1007,-1007,-1007,-1007,-1007,-1007,-1007,-1007 + }, + + { + 55,-1008,-1008,-1008,-1008,-1008,-1008,-1008,-1008,-1008, + -1008,-1008,-1008,-1008,-1008,-1008,-1008,-1008,-1008,-1008, + -1008,-1008,-1008,-1008,-1008,-1008,-1008,-1008,-1008,-1008, + -1008,-1008, 1096,-1008,-1008,-1008,-1008,-1008,-1008,-1008, + -1008,-1008,-1008,-1008,-1008,-1008,-1008,-1008,-1008,-1008, + -1008,-1008,-1008,-1008,-1008,-1008,-1008,-1008,-1008,-1008, + + -1008,-1008,-1008,-1008,-1008,-1008,-1008,-1008,-1008,-1008, + -1008,-1008,-1008,-1008,-1008,-1008,-1008,-1008,-1008,-1008, + -1008,-1008,-1008,-1008,-1008,-1008,-1008,-1008,-1008,-1008, + -1008,-1008,-1008,-1008,-1008,-1008,-1008,-1008,-1008,-1008, + -1008,-1008,-1008,-1008,-1008,-1008,-1008,-1008,-1008,-1008, + -1008,-1008,-1008,-1008,-1008,-1008,-1008,-1008,-1008,-1008, + -1008,-1008,-1008,-1008,-1008,-1008,-1008,-1008 + }, + + { + 55,-1009,-1009,-1009,-1009,-1009,-1009,-1009,-1009,-1009, + -1009,-1009,-1009,-1009,-1009,-1009,-1009,-1009,-1009,-1009, + -1009,-1009,-1009,-1009,-1009,-1009,-1009,-1009,-1009,-1009, + + -1009,-1009, 1097,-1009,-1009,-1009,-1009,-1009,-1009,-1009, + -1009,-1009,-1009,-1009,-1009,-1009,-1009,-1009,-1009,-1009, + -1009,-1009,-1009,-1009,-1009,-1009,-1009,-1009,-1009,-1009, + -1009,-1009,-1009,-1009,-1009,-1009,-1009,-1009,-1009,-1009, + -1009,-1009,-1009,-1009,-1009,-1009,-1009,-1009,-1009,-1009, + -1009,-1009,-1009,-1009,-1009,-1009,-1009,-1009,-1009,-1009, + -1009,-1009,-1009,-1009,-1009,-1009,-1009,-1009,-1009,-1009, + -1009,-1009,-1009,-1009,-1009,-1009,-1009,-1009,-1009,-1009, + -1009,-1009,-1009,-1009,-1009,-1009,-1009,-1009,-1009,-1009, + -1009,-1009,-1009,-1009,-1009,-1009,-1009,-1009 + + }, + + { + 55,-1010,-1010,-1010,-1010,-1010,-1010,-1010,-1010,-1010, + -1010,-1010,-1010,-1010,-1010,-1010,-1010,-1010,-1010,-1010, + -1010,-1010,-1010,-1010,-1010,-1010,-1010,-1010,-1010,-1010, + -1010,-1010, 1098,-1010,-1010,-1010,-1010,-1010,-1010,-1010, + -1010,-1010,-1010,-1010,-1010,-1010,-1010,-1010,-1010,-1010, + -1010,-1010,-1010,-1010,-1010,-1010,-1010,-1010,-1010,-1010, + -1010,-1010,-1010,-1010,-1010,-1010,-1010,-1010,-1010,-1010, + -1010,-1010,-1010,-1010,-1010,-1010,-1010,-1010,-1010,-1010, + -1010,-1010,-1010,-1010,-1010,-1010,-1010,-1010,-1010,-1010, + -1010,-1010,-1010,-1010,-1010,-1010,-1010,-1010,-1010,-1010, + + -1010,-1010,-1010,-1010,-1010,-1010,-1010,-1010,-1010,-1010, + -1010,-1010,-1010,-1010,-1010,-1010,-1010,-1010,-1010,-1010, + -1010,-1010,-1010,-1010,-1010,-1010,-1010,-1010 + }, + + { + 55,-1011,-1011,-1011,-1011,-1011,-1011,-1011,-1011,-1011, + -1011,-1011,-1011,-1011,-1011,-1011,-1011,-1011,-1011,-1011, + -1011,-1011,-1011,-1011,-1011,-1011,-1011,-1011,-1011,-1011, + -1011,-1011,-1011,-1011,-1011,-1011,-1011,-1011,-1011,-1011, + -1011,-1011,-1011,-1011,-1011,-1011,-1011,-1011,-1011,-1011, + -1011,-1011,-1011,-1011,-1011,-1011,-1011,-1011,-1011,-1011, + -1011,-1011,-1011,-1011,-1011,-1011,-1011,-1011,-1011,-1011, + + -1011,-1011,-1011,-1011,-1011,-1011,-1011,-1011,-1011,-1011, + -1011,-1011,-1011,-1011,-1011,-1011,-1011,-1011,-1011,-1011, + -1011,-1011,-1011,-1011,-1011,-1011,-1011,-1011,-1011,-1011, + -1011,-1011,-1011,-1011,-1011,-1011,-1011,-1011,-1011,-1011, + -1011,-1011,-1011,-1011,-1011,-1011,-1011,-1011,-1011,-1011, + -1011,-1011,-1011,-1011,-1011,-1011,-1011,-1011 + }, + + { + 55,-1012,-1012,-1012,-1012,-1012,-1012,-1012,-1012,-1012, + -1012,-1012,-1012,-1012,-1012,-1012,-1012,-1012,-1012,-1012, + -1012,-1012,-1012,-1012,-1012,-1012,-1012,-1012,-1012,-1012, + -1012,-1012,-1012,-1012,-1012,-1012,-1012,-1012,-1012,-1012, + + -1012,-1012,-1012,-1012,-1012,-1012,-1012,-1012,-1012,-1012, + -1012,-1012,-1012,-1012,-1012,-1012,-1012,-1012,-1012,-1012, + -1012,-1012,-1012,-1012,-1012,-1012,-1012,-1012,-1012,-1012, + -1012,-1012,-1012,-1012,-1012,-1012,-1012,-1012,-1012,-1012, + -1012,-1012,-1012,-1012,-1012,-1012,-1012,-1012,-1012,-1012, + -1012,-1012,-1012,-1012,-1012,-1012,-1012,-1012,-1012,-1012, + -1012,-1012,-1012,-1012,-1012,-1012,-1012,-1012,-1012,-1012, + -1012,-1012,-1012,-1012,-1012,-1012,-1012,-1012,-1012,-1012, + -1012,-1012,-1012,-1012,-1012,-1012,-1012,-1012 + }, + + { + 55,-1013,-1013,-1013,-1013,-1013,-1013,-1013,-1013,-1013, + + -1013,-1013,-1013,-1013,-1013,-1013,-1013,-1013,-1013,-1013, + -1013,-1013,-1013,-1013,-1013,-1013,-1013,-1013,-1013,-1013, + -1013,-1013, 1099,-1013,-1013,-1013,-1013,-1013,-1013,-1013, + -1013,-1013,-1013,-1013,-1013,-1013,-1013,-1013,-1013,-1013, + -1013,-1013,-1013,-1013,-1013,-1013,-1013,-1013,-1013,-1013, + -1013,-1013,-1013,-1013,-1013,-1013,-1013,-1013,-1013,-1013, + -1013,-1013,-1013,-1013,-1013,-1013,-1013,-1013,-1013,-1013, + -1013,-1013,-1013,-1013,-1013,-1013,-1013,-1013,-1013,-1013, + -1013,-1013,-1013,-1013,-1013,-1013,-1013,-1013,-1013,-1013, + -1013,-1013,-1013,-1013,-1013,-1013,-1013,-1013,-1013,-1013, + + -1013,-1013,-1013,-1013,-1013,-1013,-1013,-1013,-1013,-1013, + -1013,-1013,-1013,-1013,-1013,-1013,-1013,-1013 + }, + + { + 55,-1014,-1014,-1014,-1014,-1014,-1014,-1014,-1014,-1014, + -1014,-1014,-1014,-1014,-1014,-1014,-1014,-1014,-1014,-1014, + -1014,-1014,-1014,-1014,-1014,-1014,-1014,-1014,-1014,-1014, + -1014,-1014, 1100,-1014,-1014,-1014,-1014,-1014,-1014,-1014, + -1014,-1014,-1014,-1014,-1014,-1014,-1014,-1014,-1014,-1014, + -1014,-1014,-1014,-1014,-1014,-1014,-1014,-1014,-1014,-1014, + -1014,-1014,-1014,-1014,-1014, 1100, 1100, 1100, 1100, 1100, + 1100, 1100, 1100, 1100, 1100, 1100, 1100, 1100, 1100, 1100, + + 1100, 1100, 1100, 1100, 1100, 1100, 1100, 1100, 1100, 1100, + 1100,-1014,-1014,-1014,-1014,-1014,-1014,-1014,-1014,-1014, + -1014,-1014,-1014,-1014,-1014,-1014,-1014,-1014,-1014,-1014, + -1014,-1014,-1014,-1014,-1014,-1014,-1014,-1014,-1014,-1014, + -1014,-1014,-1014,-1014,-1014,-1014,-1014,-1014 + }, + + { + 55,-1015,-1015,-1015,-1015,-1015,-1015,-1015,-1015,-1015, + -1015,-1015,-1015,-1015,-1015,-1015,-1015,-1015,-1015,-1015, + -1015,-1015,-1015,-1015,-1015,-1015,-1015,-1015,-1015,-1015, + -1015,-1015,-1015,-1015,-1015,-1015,-1015,-1015,-1015,-1015, + -1015,-1015,-1015,-1015,-1015,-1015,-1015,-1015, 1101, 1101, + + 1101, 1101, 1101, 1101, 1101, 1101, 1101, 1101,-1015,-1015, + -1015,-1015,-1015,-1015,-1015,-1015,-1015,-1015,-1015,-1015, + -1015,-1015,-1015,-1015,-1015,-1015,-1015,-1015,-1015,-1015, + -1015,-1015,-1015,-1015,-1015,-1015,-1015,-1015,-1015,-1015, + -1015,-1015,-1015,-1015,-1015,-1015,-1015,-1015,-1015,-1015, + -1015,-1015,-1015,-1015,-1015,-1015,-1015,-1015,-1015,-1015, + -1015,-1015,-1015,-1015,-1015,-1015,-1015,-1015,-1015,-1015, + -1015,-1015,-1015,-1015,-1015,-1015,-1015,-1015 + }, + + { + 55,-1016,-1016,-1016,-1016,-1016,-1016,-1016,-1016,-1016, + -1016,-1016,-1016,-1016,-1016,-1016,-1016,-1016,-1016,-1016, + + -1016,-1016,-1016,-1016,-1016,-1016,-1016,-1016,-1016,-1016, + -1016,-1016, 1102,-1016,-1016,-1016,-1016,-1016,-1016,-1016, + -1016,-1016,-1016,-1016,-1016,-1016,-1016,-1016,-1016,-1016, + -1016,-1016,-1016,-1016,-1016,-1016,-1016,-1016,-1016,-1016, + -1016,-1016,-1016,-1016,-1016, 1102, 1102, 1102, 1102, 1102, + 1102, 1102, 1102, 1102, 1102, 1102, 1102, 1102, 1102, 1102, + 1102, 1102, 1102, 1102, 1102, 1102, 1102, 1102, 1102, 1102, + 1102,-1016,-1016,-1016,-1016,-1016,-1016,-1016,-1016,-1016, + -1016,-1016,-1016,-1016,-1016,-1016,-1016,-1016,-1016,-1016, + -1016,-1016,-1016,-1016,-1016,-1016,-1016,-1016,-1016,-1016, + + -1016,-1016,-1016,-1016,-1016,-1016,-1016,-1016 + }, + + { + 55,-1017,-1017,-1017,-1017,-1017,-1017,-1017,-1017,-1017, + -1017,-1017,-1017,-1017,-1017,-1017,-1017,-1017,-1017,-1017, + -1017,-1017,-1017,-1017,-1017,-1017,-1017,-1017,-1017,-1017, + -1017,-1017,-1017,-1017,-1017,-1017,-1017,-1017,-1017,-1017, + -1017,-1017,-1017,-1017,-1017,-1017,-1017,-1017,-1017,-1017, + -1017,-1017,-1017,-1017,-1017,-1017,-1017,-1017,-1017,-1017, + -1017,-1017,-1017,-1017,-1017,-1017,-1017,-1017,-1017,-1017, + -1017,-1017,-1017,-1017,-1017,-1017,-1017,-1017,-1017,-1017, + -1017,-1017,-1017,-1017,-1017,-1017,-1017,-1017,-1017,-1017, + + -1017,-1017,-1017,-1017,-1017,-1017,-1017,-1017,-1017,-1017, + -1017,-1017,-1017,-1017,-1017,-1017,-1017,-1017,-1017,-1017, + -1017,-1017,-1017,-1017,-1017,-1017,-1017,-1017,-1017,-1017, + -1017,-1017,-1017,-1017,-1017,-1017,-1017,-1017 + }, + + { + 55,-1018,-1018,-1018,-1018,-1018,-1018,-1018,-1018,-1018, + -1018,-1018,-1018,-1018,-1018,-1018,-1018,-1018,-1018,-1018, + -1018,-1018,-1018,-1018,-1018,-1018,-1018,-1018,-1018,-1018, + -1018,-1018,-1018,-1018,-1018,-1018,-1018,-1018,-1018,-1018, + -1018,-1018,-1018,-1018,-1018,-1018,-1018,-1018,-1018,-1018, + -1018,-1018,-1018,-1018,-1018,-1018,-1018,-1018,-1018,-1018, + + -1018,-1018,-1018,-1018,-1018,-1018,-1018,-1018,-1018,-1018, + -1018,-1018,-1018,-1018,-1018,-1018,-1018,-1018,-1018,-1018, + -1018,-1018,-1018,-1018,-1018,-1018,-1018,-1018,-1018,-1018, + 1103,-1018,-1018,-1018,-1018,-1018,-1018,-1018,-1018,-1018, + -1018,-1018,-1018,-1018,-1018,-1018,-1018,-1018,-1018,-1018, + -1018,-1018,-1018,-1018,-1018,-1018,-1018,-1018,-1018,-1018, + -1018,-1018,-1018,-1018,-1018,-1018,-1018,-1018 + }, + + { + 55,-1019,-1019,-1019,-1019,-1019,-1019,-1019,-1019,-1019, + -1019,-1019,-1019,-1019,-1019,-1019,-1019,-1019,-1019,-1019, + -1019,-1019,-1019,-1019,-1019,-1019,-1019,-1019,-1019,-1019, + + -1019,-1019, 1104,-1019,-1019,-1019,-1019,-1019,-1019,-1019, + -1019,-1019,-1019,-1019,-1019,-1019,-1019,-1019,-1019,-1019, + -1019,-1019,-1019,-1019,-1019,-1019,-1019,-1019,-1019,-1019, + -1019,-1019,-1019,-1019,-1019,-1019,-1019,-1019,-1019,-1019, + -1019,-1019,-1019,-1019,-1019,-1019,-1019,-1019,-1019,-1019, + -1019,-1019,-1019,-1019,-1019,-1019,-1019,-1019,-1019,-1019, + -1019,-1019,-1019,-1019,-1019,-1019,-1019,-1019,-1019,-1019, + -1019,-1019,-1019,-1019,-1019,-1019,-1019,-1019,-1019,-1019, + -1019,-1019,-1019,-1019,-1019,-1019,-1019,-1019,-1019,-1019, + -1019,-1019,-1019,-1019,-1019,-1019,-1019,-1019 + + }, + + { + 55,-1020,-1020,-1020,-1020,-1020,-1020,-1020,-1020,-1020, + -1020,-1020,-1020,-1020,-1020,-1020,-1020,-1020,-1020,-1020, + -1020,-1020,-1020,-1020,-1020,-1020,-1020,-1020,-1020,-1020, + -1020,-1020,-1020,-1020,-1020,-1020,-1020,-1020,-1020,-1020, + -1020,-1020,-1020,-1020,-1020,-1020,-1020,-1020,-1020,-1020, + -1020,-1020,-1020,-1020,-1020,-1020,-1020,-1020,-1020,-1020, + -1020,-1020,-1020,-1020,-1020,-1020,-1020,-1020,-1020,-1020, + -1020,-1020,-1020,-1020,-1020,-1020,-1020,-1020,-1020,-1020, + -1020,-1020,-1020,-1020,-1020,-1020,-1020,-1020,-1020,-1020, + 1105,-1020,-1020,-1020,-1020,-1020,-1020,-1020,-1020,-1020, + + -1020,-1020,-1020,-1020,-1020,-1020,-1020,-1020,-1020,-1020, + -1020,-1020,-1020,-1020,-1020,-1020,-1020,-1020,-1020,-1020, + -1020,-1020,-1020,-1020,-1020,-1020,-1020,-1020 + }, + + { + 55,-1021,-1021,-1021,-1021,-1021,-1021,-1021,-1021,-1021, + -1021,-1021,-1021,-1021,-1021,-1021,-1021,-1021,-1021,-1021, + -1021,-1021,-1021,-1021,-1021,-1021,-1021,-1021,-1021,-1021, + -1021,-1021,-1021,-1021,-1021,-1021,-1021,-1021,-1021,-1021, + -1021,-1021,-1021,-1021,-1021,-1021,-1021,-1021,-1021,-1021, + -1021,-1021,-1021,-1021,-1021,-1021,-1021,-1021,-1021,-1021, + -1021,-1021,-1021,-1021,-1021,-1021,-1021,-1021,-1021,-1021, + + -1021,-1021,-1021,-1021,-1021,-1021,-1021,-1021,-1021,-1021, + -1021,-1021,-1021,-1021,-1021,-1021,-1021,-1021,-1021,-1021, + -1021,-1021,-1021,-1021,-1021,-1021,-1021,-1021,-1021,-1021, + -1021,-1021,-1021,-1021,-1021,-1021,-1021,-1021,-1021,-1021, + -1021,-1021,-1021,-1021,-1021,-1021,-1021,-1021,-1021,-1021, + -1021,-1021,-1021,-1021,-1021,-1021,-1021,-1021 + }, + + { + 55,-1022,-1022,-1022,-1022,-1022,-1022,-1022,-1022,-1022, + -1022,-1022,-1022,-1022,-1022,-1022,-1022,-1022,-1022,-1022, + -1022,-1022,-1022,-1022,-1022,-1022,-1022,-1022,-1022,-1022, + -1022,-1022,-1022,-1022,-1022,-1022,-1022,-1022,-1022,-1022, + + -1022,-1022,-1022,-1022,-1022,-1022,-1022,-1022,-1022,-1022, + -1022,-1022,-1022,-1022,-1022,-1022,-1022,-1022,-1022,-1022, + -1022,-1022,-1022,-1022,-1022,-1022,-1022,-1022,-1022,-1022, + -1022,-1022,-1022,-1022,-1022,-1022,-1022,-1022,-1022,-1022, + -1022,-1022,-1022,-1022,-1022,-1022,-1022,-1022,-1022,-1022, + -1022,-1022,-1022,-1022,-1022,-1022,-1022,-1022,-1022,-1022, + -1022,-1022,-1022,-1022,-1022,-1022,-1022,-1022,-1022,-1022, + -1022,-1022,-1022,-1022,-1022,-1022,-1022,-1022,-1022,-1022, + -1022,-1022,-1022,-1022,-1022,-1022,-1022,-1022 + }, + + { + 55,-1023,-1023,-1023,-1023,-1023,-1023,-1023,-1023,-1023, + + -1023,-1023,-1023,-1023,-1023,-1023,-1023,-1023,-1023,-1023, + -1023,-1023,-1023,-1023,-1023,-1023,-1023,-1023,-1023,-1023, + -1023,-1023,-1023,-1023,-1023,-1023,-1023,-1023,-1023,-1023, + -1023,-1023,-1023,-1023,-1023,-1023,-1023,-1023,-1023,-1023, + -1023,-1023,-1023,-1023,-1023,-1023,-1023,-1023,-1023,-1023, + -1023,-1023,-1023,-1023,-1023,-1023,-1023,-1023,-1023,-1023, + -1023,-1023,-1023,-1023,-1023,-1023,-1023,-1023,-1023,-1023, + -1023,-1023,-1023,-1023,-1023,-1023,-1023,-1023,-1023,-1023, + -1023,-1023,-1023,-1023,-1023,-1023,-1023,-1023,-1023,-1023, + -1023,-1023,-1023,-1023,-1023,-1023,-1023,-1023,-1023,-1023, + + -1023,-1023,-1023,-1023,-1023,-1023,-1023,-1023,-1023,-1023, + -1023,-1023,-1023,-1023,-1023,-1023,-1023,-1023 + }, + + { + 55,-1024,-1024,-1024,-1024,-1024,-1024,-1024,-1024,-1024, + -1024,-1024,-1024,-1024,-1024,-1024,-1024,-1024,-1024,-1024, + -1024,-1024,-1024,-1024,-1024,-1024,-1024,-1024,-1024,-1024, + -1024,-1024,-1024,-1024,-1024,-1024,-1024,-1024,-1024,-1024, + -1024,-1024,-1024,-1024,-1024,-1024,-1024,-1024,-1024,-1024, + -1024,-1024,-1024,-1024,-1024,-1024,-1024,-1024,-1024,-1024, + -1024,-1024,-1024,-1024,-1024,-1024,-1024,-1024,-1024,-1024, + -1024,-1024,-1024,-1024,-1024,-1024,-1024,-1024,-1024,-1024, + + -1024,-1024,-1024,-1024,-1024,-1024,-1024,-1024,-1024,-1024, + -1024,-1024,-1024,-1024,-1024,-1024,-1024,-1024,-1024,-1024, + -1024,-1024,-1024,-1024,-1024,-1024,-1024,-1024,-1024,-1024, + -1024,-1024,-1024,-1024,-1024,-1024,-1024,-1024,-1024,-1024, + -1024,-1024,-1024,-1024,-1024,-1024,-1024,-1024 + }, + + { + 55,-1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025, + -1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025, + -1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025, + -1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025, + -1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025, + + -1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025, + -1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025, + -1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025, + -1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025, + -1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025, + -1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025, + -1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025, + -1025,-1025,-1025,-1025,-1025,-1025,-1025,-1025 + }, + + { + 55,-1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026, + -1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026, + + -1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026, + -1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026, + -1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026, + -1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026, + -1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026, + -1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026, + -1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026, + -1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026, + -1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026, + -1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026, + + -1026,-1026,-1026,-1026,-1026,-1026,-1026,-1026 + }, + + { + 55,-1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027, + -1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027, + -1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027, + -1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027, + -1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027, + -1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027, + -1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027, + -1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027, + -1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027, + + -1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027, + -1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027, + -1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027, + -1027,-1027,-1027,-1027,-1027,-1027,-1027,-1027 + }, + + { + 55,-1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028, + -1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028, + -1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028, + -1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028, + -1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028, + -1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028, + + -1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028, + -1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028, + -1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028, + -1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028, + -1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028, + -1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028, + -1028,-1028,-1028,-1028,-1028,-1028,-1028,-1028 + }, + + { + 55,-1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029, + -1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029, + -1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029, + + -1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029, + -1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029, + -1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029, + -1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029, + -1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029, + -1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029, + -1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029, + -1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029, + -1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029, + -1029,-1029,-1029,-1029,-1029,-1029,-1029,-1029 + + }, + + { + 55,-1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030, + -1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030, + -1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030, + -1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030, + -1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030, + -1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030, + -1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030, + -1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030, + -1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030, + -1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030, + + -1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030, + -1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030, + -1030,-1030,-1030,-1030,-1030,-1030,-1030,-1030 + }, + + { + 55,-1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031, + -1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031, + -1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031, + -1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031, + -1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031, + -1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031, + -1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031, + + -1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031, + -1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031, + -1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031, + -1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031, + -1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031, + -1031,-1031,-1031,-1031,-1031,-1031,-1031,-1031 + }, + + { + 55,-1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032, + -1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032, + -1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032, + -1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032, + + -1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032, + -1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032, + -1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032, + -1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032, + -1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032, + -1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032, + -1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032, + -1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032, + -1032,-1032,-1032,-1032,-1032,-1032,-1032,-1032 + }, + + { + 55,-1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033, + + -1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033, + -1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033, + -1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033, + -1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033, + -1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033, + -1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033, + -1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033, + -1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033, + -1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033, + -1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033, + + -1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033, + -1033,-1033,-1033,-1033,-1033,-1033,-1033,-1033 + }, + + { + 55,-1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034, + -1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034, + -1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034, + -1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034, + -1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034, + -1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034, + -1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034, + -1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034, + + -1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034, + -1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034, + -1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034, + -1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034, + -1034,-1034,-1034,-1034,-1034,-1034,-1034,-1034 + }, + + { + 55,-1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035, + -1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035, + -1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035, + -1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035, + -1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035, + + -1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035, + -1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035, + -1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035, + -1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035, + -1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035, + -1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035, + -1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035, + -1035,-1035,-1035,-1035,-1035,-1035,-1035,-1035 + }, + + { + 55,-1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036, + -1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036, + + -1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036, + -1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036, + -1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036, + -1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036, + -1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036, + -1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036, + -1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036, + -1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036, + -1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036, + -1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036, + + -1036,-1036,-1036,-1036,-1036,-1036,-1036,-1036 + }, + + { + 55,-1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037, + -1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037, + -1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037, + -1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037, + -1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037, + -1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037, + -1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037, + -1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037, + -1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037, + + -1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037, + -1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037, + -1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037, + -1037,-1037,-1037,-1037,-1037,-1037,-1037,-1037 + }, + + { + 55,-1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038, + -1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038, + -1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038, + -1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038, + -1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038, + -1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038, + + -1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038, + -1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038, + -1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038, + -1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038, + -1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038, + -1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038, + -1038,-1038,-1038,-1038,-1038,-1038,-1038,-1038 + }, + + { + 55,-1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039, + -1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039, + -1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039, + + -1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039, + -1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039, + -1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039, + -1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039, + -1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039, + -1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039, + -1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039, + -1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039, + -1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039, + -1039,-1039,-1039,-1039,-1039,-1039,-1039,-1039 + + }, + + { + 55,-1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040, + -1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040, + -1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040, + -1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040, + -1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040, + -1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040, + -1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040, + -1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040, + -1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040, + -1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040, + + -1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040, + -1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040, + -1040,-1040,-1040,-1040,-1040,-1040,-1040,-1040 + }, + + { + 55,-1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041, + -1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041, + -1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041, + -1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041, + -1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041, + -1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041, + -1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041, + + -1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041, + -1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041, + -1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041, + -1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041, + -1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041, + -1041,-1041,-1041,-1041,-1041,-1041,-1041,-1041 + }, + + { + 55,-1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042, + -1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042, + -1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042, + -1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042, + + -1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042, + -1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042, + -1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042, + -1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042, + -1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042, + -1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042, + -1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042, + -1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042, + -1042,-1042,-1042,-1042,-1042,-1042,-1042,-1042 + }, + + { + 55,-1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043, + + -1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043, + -1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043, + -1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043, + -1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043, + -1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043, + -1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043, + -1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043, + -1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043, + -1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043, + -1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043, + + -1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043, + -1043,-1043,-1043,-1043,-1043,-1043,-1043,-1043 + }, + + { + 55,-1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044, + -1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044, + -1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044, + -1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044, + -1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044, + -1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044, + -1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044, + -1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044, + + -1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044, + -1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044, + -1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044, + -1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044, + -1044,-1044,-1044,-1044,-1044,-1044,-1044,-1044 + }, + + { + 55,-1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045, + -1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045, + -1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045, + -1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045, + -1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045, + + -1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045, + -1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045, + -1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045, + -1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045, + -1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045, + -1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045, + -1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045, + -1045,-1045,-1045,-1045,-1045,-1045,-1045,-1045 + }, + + { + 55,-1046,-1046,-1046,-1046,-1046,-1046,-1046,-1046,-1046, + -1046,-1046,-1046,-1046,-1046,-1046,-1046,-1046,-1046,-1046, + + -1046,-1046,-1046,-1046,-1046,-1046,-1046,-1046,-1046,-1046, + -1046,-1046, 1106,-1046,-1046,-1046,-1046,-1046,-1046,-1046, + -1046,-1046,-1046,-1046,-1046,-1046,-1046,-1046,-1046,-1046, + -1046,-1046,-1046,-1046,-1046,-1046,-1046,-1046,-1046,-1046, + -1046,-1046,-1046,-1046,-1046,-1046,-1046,-1046,-1046,-1046, + -1046,-1046,-1046,-1046,-1046,-1046,-1046,-1046,-1046,-1046, + -1046,-1046,-1046,-1046,-1046,-1046,-1046,-1046,-1046,-1046, + -1046,-1046,-1046,-1046,-1046,-1046,-1046,-1046,-1046,-1046, + -1046,-1046,-1046,-1046,-1046,-1046,-1046,-1046,-1046,-1046, + -1046,-1046,-1046,-1046,-1046,-1046,-1046,-1046,-1046,-1046, + + -1046,-1046,-1046,-1046,-1046,-1046,-1046,-1046 + }, + + { + 55,-1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047, + -1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047, + -1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047, + -1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047, + -1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047, + -1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047, + -1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047, + -1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047, + -1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047, + + -1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047, + -1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047, + -1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047, + -1047,-1047,-1047,-1047,-1047,-1047,-1047,-1047 + }, + + { + 55,-1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048, + -1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048, + -1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048, + -1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048, + -1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048, + -1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048, + + -1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048, + -1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048, + -1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048, + -1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048, + -1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048, + -1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048, + -1048,-1048,-1048,-1048,-1048,-1048,-1048,-1048 + }, + + { + 55,-1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049, + -1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049, + -1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049, + + -1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049, + -1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049, + -1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049, + -1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049, + -1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049, + -1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049, + -1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049, + -1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049, + -1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049, + -1049,-1049,-1049,-1049,-1049,-1049,-1049,-1049 + + }, + + { + 55,-1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050, + -1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050, + -1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050, + -1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050, + -1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050, + -1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050, + -1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050, + -1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050, + -1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050, + -1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050, + + -1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050, + -1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050, + -1050,-1050,-1050,-1050,-1050,-1050,-1050,-1050 + }, + + { + 55,-1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051, + -1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051, + -1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051, + -1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051, + -1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051, + -1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051, + -1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051, + + -1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051, + -1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051, + -1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051, + -1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051, + -1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051, + -1051,-1051,-1051,-1051,-1051,-1051,-1051,-1051 + }, + + { + 55,-1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052, + -1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052, + -1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052, + -1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052, + + -1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052, + -1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052, + -1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052, + -1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052, + -1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052, + -1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052, + -1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052, + -1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052, + -1052,-1052,-1052,-1052,-1052,-1052,-1052,-1052 + }, + + { + 55,-1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053, + + -1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053, + -1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053, + -1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053, + -1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053, + -1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053, + -1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053, + -1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053, + -1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053, + -1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053, + -1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053, + + -1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053, + -1053,-1053,-1053,-1053,-1053,-1053,-1053,-1053 + }, + + { + 55,-1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054, + -1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054, + -1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054, + -1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054, + -1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054, + -1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054, + -1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054, + -1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054, + + -1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054, + -1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054, + -1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054, + -1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054, + -1054,-1054,-1054,-1054,-1054,-1054,-1054,-1054 + }, + + { + 55,-1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055, + -1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055, + -1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055, + -1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055, + -1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055, + + -1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055, + -1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055, + -1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055, + -1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055, + -1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055, + -1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055, + -1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055, + -1055,-1055,-1055,-1055,-1055,-1055,-1055,-1055 + }, + + { + 55,-1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056, + -1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056, + + -1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056, + -1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056, + -1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056, + -1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056, + -1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056, + -1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056, + -1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056, + -1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056, + -1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056, + -1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056, + + -1056,-1056,-1056,-1056,-1056,-1056,-1056,-1056 + }, + + { + 55,-1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057, + -1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057, + -1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057, + -1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057, + -1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057, + -1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057, + -1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057, + -1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057, + -1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057, + + -1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057, + -1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057, + -1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057, + -1057,-1057,-1057,-1057,-1057,-1057,-1057,-1057 + }, + + { + 55,-1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058, + -1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058, + -1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058, + -1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058, + -1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058, + -1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058, + + -1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058, + -1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058, + -1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058, + -1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058, + -1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058, + -1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058, + -1058,-1058,-1058,-1058,-1058,-1058,-1058,-1058 + }, + + { + 55,-1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059, + -1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059, + -1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059, + + -1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059, + -1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059, + -1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059, + -1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059, + -1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059, + -1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059, + -1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059, + -1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059, + -1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059, + -1059,-1059,-1059,-1059,-1059,-1059,-1059,-1059 + + }, + + { + 55,-1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060, + -1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060, + -1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060, + -1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060, + -1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060, + -1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060, + -1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060, + -1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060, + -1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060, + -1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060, + + -1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060, + -1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060, + -1060,-1060,-1060,-1060,-1060,-1060,-1060,-1060 + }, + + { + 55,-1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061, + -1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061, + -1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061, + -1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061, + -1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061, + -1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061, + -1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061, + + -1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061, + -1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061, + -1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061, + -1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061, + -1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061, + -1061,-1061,-1061,-1061,-1061,-1061,-1061,-1061 + }, + + { + 55,-1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062, + -1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062, + -1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062, + -1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062, + + -1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062, + -1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062, + -1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062, + -1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062, + -1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062, + -1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062, + -1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062, + -1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062, + -1062,-1062,-1062,-1062,-1062,-1062,-1062,-1062 + }, + + { + 55,-1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063, + + -1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063, + -1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063, + -1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063, + -1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063, + -1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063, + -1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063, + -1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063, + -1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063, + -1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063, + -1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063, + + -1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063, + -1063,-1063,-1063,-1063,-1063,-1063,-1063,-1063 + }, + + { + 55,-1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064, + -1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064, + -1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064, + -1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064, + -1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064, + -1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064, + -1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064, + -1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064, + + -1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064, + -1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064, + -1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064, + -1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064, + -1064,-1064,-1064,-1064,-1064,-1064,-1064,-1064 + }, + + { + 55,-1065,-1065,-1065,-1065,-1065,-1065,-1065,-1065,-1065, + -1065,-1065,-1065,-1065,-1065,-1065,-1065,-1065,-1065,-1065, + -1065,-1065,-1065,-1065,-1065,-1065,-1065,-1065,-1065,-1065, + -1065,-1065,-1065,-1065,-1065,-1065,-1065,-1065,-1065,-1065, + -1065,-1065,-1065,-1065,-1065,-1065,-1065,-1065,-1065,-1065, + + -1065,-1065,-1065,-1065,-1065,-1065,-1065,-1065,-1065,-1065, + -1065, 1107,-1065,-1065,-1065,-1065,-1065,-1065,-1065,-1065, + -1065,-1065,-1065,-1065,-1065,-1065,-1065,-1065,-1065,-1065, + -1065,-1065,-1065,-1065,-1065,-1065,-1065,-1065,-1065,-1065, + -1065,-1065,-1065,-1065,-1065,-1065,-1065,-1065,-1065,-1065, + -1065,-1065,-1065,-1065,-1065,-1065,-1065,-1065,-1065,-1065, + -1065,-1065,-1065,-1065,-1065,-1065,-1065,-1065,-1065,-1065, + -1065,-1065,-1065,-1065,-1065,-1065,-1065,-1065 + }, + + { + 55,-1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066, + -1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066, + + -1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066, + -1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066, + -1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066, + -1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066, + -1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066, + -1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066, + -1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066, + -1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066, + -1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066, + -1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066, + + -1066,-1066,-1066,-1066,-1066,-1066,-1066,-1066 + }, + + { + 55,-1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067, + -1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067, + -1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067, + -1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067, + -1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067, + -1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067, + -1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067, + -1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067, + -1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067, + + -1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067, + -1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067, + -1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067, + -1067,-1067,-1067,-1067,-1067,-1067,-1067,-1067 + }, + + { + 55,-1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068, + -1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068, + -1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068, + -1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068, + -1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068, + -1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068, + + -1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068, + -1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068, + -1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068, + -1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068, + -1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068, + -1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068, + -1068,-1068,-1068,-1068,-1068,-1068,-1068,-1068 + }, + + { + 55,-1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069, + -1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069, + -1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069, + + -1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069, + -1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069, + -1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069, + -1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069, + -1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069, + -1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069, + -1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069, + -1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069, + -1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069, + -1069,-1069,-1069,-1069,-1069,-1069,-1069,-1069 + + }, + + { + 55,-1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070, + -1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070, + -1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070, + -1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070, + -1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070, + -1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070, + -1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070, + -1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070, + -1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070, + -1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070, + + -1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070, + -1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070, + -1070,-1070,-1070,-1070,-1070,-1070,-1070,-1070 + }, + + { + 55,-1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071, + -1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071, + -1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071, + -1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071, + -1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071, + -1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071, + -1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071, + + -1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071, + -1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071, + -1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071, + -1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071, + -1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071, + -1071,-1071,-1071,-1071,-1071,-1071,-1071,-1071 + }, + + { + 55,-1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072, + -1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072, + -1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072, + -1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072, + + -1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072, + -1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072, + -1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072, + -1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072, + -1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072, + -1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072, + -1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072, + -1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072, + -1072,-1072,-1072,-1072,-1072,-1072,-1072,-1072 + }, + + { + 55,-1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073, + + -1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073, + -1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073, + -1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073, + -1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073, + -1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073, + -1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073, + -1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073, + -1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073, + -1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073, + -1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073, + + -1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073, + -1073,-1073,-1073,-1073,-1073,-1073,-1073,-1073 + }, + + { + 55,-1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074, + -1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074, + -1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074, + -1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074, + -1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074, + -1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074, + -1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074, + -1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074, + + -1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074, + -1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074, + -1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074, + -1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074, + -1074,-1074,-1074,-1074,-1074,-1074,-1074,-1074 + }, + + { + 55,-1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075, + -1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075, + -1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075, + -1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075, + -1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075, + + -1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075, + -1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075, + -1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075, + -1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075, + -1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075, + -1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075, + -1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075, + -1075,-1075,-1075,-1075,-1075,-1075,-1075,-1075 + }, + + { + 55,-1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076, + -1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076, + + -1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076, + -1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076, + -1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076, + -1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076, + -1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076, + -1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076, + -1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076, + -1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076, + -1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076, + -1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076, + + -1076,-1076,-1076,-1076,-1076,-1076,-1076,-1076 + }, + + { + 55,-1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077, + -1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077, + -1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077, + -1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077, + -1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077, + -1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077, + -1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077, + -1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077, + -1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077, + + -1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077, + -1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077, + -1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077, + -1077,-1077,-1077,-1077,-1077,-1077,-1077,-1077 + }, + + { + 55,-1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078, + -1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078, + -1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078, + -1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078, + -1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078, + -1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078, + + -1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078, + -1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078, + -1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078, + -1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078, + -1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078, + -1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078, + -1078,-1078,-1078,-1078,-1078,-1078,-1078,-1078 + }, + + { + 55,-1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079, + -1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079, + -1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079, + + -1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079, + -1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079, + -1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079, + -1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079, + -1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079, + -1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079, + -1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079, + -1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079, + -1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079, + -1079,-1079,-1079,-1079,-1079,-1079,-1079,-1079 + + }, + + { + 55,-1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080, + -1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080, + -1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080, + -1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080, + -1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080, + -1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080, + -1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080, + -1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080, + -1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080, + -1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080, + + -1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080, + -1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080, + -1080,-1080,-1080,-1080,-1080,-1080,-1080,-1080 + }, + + { + 55,-1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081, + -1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081, + -1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081, + -1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081, + -1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081, + -1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081, + -1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081, + + -1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081, + -1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081, + -1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081, + -1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081, + -1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081, + -1081,-1081,-1081,-1081,-1081,-1081,-1081,-1081 + }, + + { + 55,-1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082, + -1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082, + -1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082, + -1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082, + + -1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082, + -1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082, + -1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082, + -1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082, + -1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082, + -1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082, + -1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082, + -1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082, + -1082,-1082,-1082,-1082,-1082,-1082,-1082,-1082 + }, + + { + 55,-1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083, + + -1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083, + -1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083, + -1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083, + -1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083, + -1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083, + -1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083, + -1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083, + -1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083, + -1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083, + -1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083, + + -1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083, + -1083,-1083,-1083,-1083,-1083,-1083,-1083,-1083 + }, + + { + 55,-1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084, + -1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084, + -1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084, + -1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084, + -1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084, + -1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084, + -1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084, + -1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084, + + -1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084, + -1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084, + -1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084, + -1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084, + -1084,-1084,-1084,-1084,-1084,-1084,-1084,-1084 + }, + + { + 55,-1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085, + -1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085, + -1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085, + -1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085, + -1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085, + + -1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085, + -1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085, + -1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085, + -1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085, + -1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085, + -1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085, + -1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085, + -1085,-1085,-1085,-1085,-1085,-1085,-1085,-1085 + }, + + { + 55,-1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086, + -1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086, + + -1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086, + -1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086, + -1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086, + -1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086, + -1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086, + -1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086, + -1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086, + -1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086, + -1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086, + -1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086, + + -1086,-1086,-1086,-1086,-1086,-1086,-1086,-1086 + }, + + { + 55,-1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087, + -1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087, + -1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087, + -1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087, + -1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087, + -1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087, + -1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087, + -1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087, + -1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087, + + -1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087, + -1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087, + -1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087, + -1087,-1087,-1087,-1087,-1087,-1087,-1087,-1087 + }, + + { + 55,-1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088, + -1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088, + -1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088, + -1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088, + -1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088, + -1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088, + + -1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088, + -1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088, + -1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088, + -1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088, + -1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088, + -1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088, + -1088,-1088,-1088,-1088,-1088,-1088,-1088,-1088 + }, + + { + 55,-1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089, + -1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089, + -1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089, + + -1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089, + -1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089, + -1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089, + -1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089, + -1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089, + -1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089, + -1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089, + -1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089, + -1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089, + -1089,-1089,-1089,-1089,-1089,-1089,-1089,-1089 + + }, + + { + 55,-1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090, + -1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090, + -1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090, + -1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090, + -1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090, + -1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090, + -1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090, + -1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090, + -1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090, + -1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090, + + -1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090, + -1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090, + -1090,-1090,-1090,-1090,-1090,-1090,-1090,-1090 + }, + + { + 55,-1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091, + -1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091, + -1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091, + -1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091, + -1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091, + -1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091, + -1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091, + + -1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091, + -1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091, + -1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091, + -1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091, + -1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091, + -1091,-1091,-1091,-1091,-1091,-1091,-1091,-1091 + }, + + { + 55,-1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092, + -1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092, + -1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092, + -1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092, + + -1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092, + -1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092, + -1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092, + -1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092, + -1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092, + -1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092, + -1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092, + -1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092, + -1092,-1092,-1092,-1092,-1092,-1092,-1092,-1092 + }, + + { + 55,-1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093, + + -1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093, + -1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093, + -1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093, + -1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093, + -1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093, + -1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093, + -1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093, + -1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093, + -1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093, + -1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093, + + -1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093, + -1093,-1093,-1093,-1093,-1093,-1093,-1093,-1093 + }, + + { + 55,-1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094, + -1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094, + -1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094, + -1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094, + -1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094, + -1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094, + -1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094, + -1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094, + + -1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094, + -1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094, + -1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094, + -1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094, + -1094,-1094,-1094,-1094,-1094,-1094,-1094,-1094 + }, + + { + 55,-1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095, + -1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095, + -1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095, + -1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095, + -1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095, + + -1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095, + -1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095, + -1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095, + -1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095, + -1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095, + -1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095, + -1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095, + -1095,-1095,-1095,-1095,-1095,-1095,-1095,-1095 + }, + + { + 55,-1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096, + -1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096, + + -1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096, + -1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096, + -1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096, + -1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096, + -1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096, + -1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096, + -1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096, + -1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096, + -1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096, + -1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096, + + -1096,-1096,-1096,-1096,-1096,-1096,-1096,-1096 + }, + + { + 55,-1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097, + -1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097, + -1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097, + -1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097, + -1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097, + -1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097, + -1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097, + -1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097, + -1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097, + + -1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097, + -1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097, + -1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097, + -1097,-1097,-1097,-1097,-1097,-1097,-1097,-1097 + }, + + { + 55,-1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098, + -1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098, + -1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098, + -1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098, + -1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098, + -1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098, + + -1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098, + -1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098, + -1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098, + -1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098, + -1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098, + -1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098, + -1098,-1098,-1098,-1098,-1098,-1098,-1098,-1098 + }, + + { + 55,-1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099, + -1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099, + -1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099, + + -1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099, + -1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099, + -1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099, + -1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099, + -1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099, + -1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099, + -1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099, + -1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099, + -1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099, + -1099,-1099,-1099,-1099,-1099,-1099,-1099,-1099 + + }, + + { + 55,-1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100, + -1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100, + -1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100, + -1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100, + -1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100, + -1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100, + -1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100, + -1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100, + -1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100, + -1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100, + + -1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100, + -1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100, + -1100,-1100,-1100,-1100,-1100,-1100,-1100,-1100 + }, + + { + 55,-1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101, + -1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101, + -1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101, + -1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101, + -1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101, + -1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101, + -1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101, + + -1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101, + -1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101, + -1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101, + -1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101, + -1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101, + -1101,-1101,-1101,-1101,-1101,-1101,-1101,-1101 + }, + + { + 55,-1102,-1102,-1102,-1102,-1102,-1102,-1102,-1102,-1102, + -1102,-1102,-1102,-1102,-1102,-1102,-1102,-1102,-1102,-1102, + -1102,-1102,-1102,-1102,-1102,-1102,-1102,-1102,-1102,-1102, + -1102,-1102,-1102,-1102,-1102,-1102,-1102,-1102,-1102,-1102, + + -1102,-1102,-1102,-1102,-1102,-1102,-1102,-1102,-1102,-1102, + -1102,-1102,-1102,-1102,-1102,-1102,-1102,-1102,-1102,-1102, + -1102, 1108,-1102,-1102,-1102,-1102,-1102,-1102,-1102,-1102, + -1102,-1102,-1102,-1102,-1102,-1102,-1102,-1102,-1102,-1102, + -1102,-1102,-1102,-1102,-1102,-1102,-1102,-1102,-1102,-1102, + -1102,-1102,-1102,-1102,-1102,-1102,-1102,-1102,-1102,-1102, + -1102,-1102,-1102,-1102,-1102,-1102,-1102,-1102,-1102,-1102, + -1102,-1102,-1102,-1102,-1102,-1102,-1102,-1102,-1102,-1102, + -1102,-1102,-1102,-1102,-1102,-1102,-1102,-1102 + }, + + { + 55,-1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103, + + -1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103, + -1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103, + -1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103, + -1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103, + -1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103, + -1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103, + -1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103, + -1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103, + -1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103, + -1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103, + + -1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103, + -1103,-1103,-1103,-1103,-1103,-1103,-1103,-1103 + }, + + { + 55,-1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104, + -1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104, + -1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104, + -1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104, + -1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104, + -1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104, + -1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104, + -1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104, + + -1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104, + -1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104, + -1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104, + -1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104, + -1104,-1104,-1104,-1104,-1104,-1104,-1104,-1104 + }, + + { + 55,-1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105, + -1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105, + -1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105, + -1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105, + -1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105, + + -1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105, + -1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105, + -1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105, + -1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105, + -1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105, + -1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105, + -1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105, + -1105,-1105,-1105,-1105,-1105,-1105,-1105,-1105 + }, + + { + 55,-1106,-1106,-1106,-1106,-1106,-1106,-1106,-1106,-1106, + -1106,-1106,-1106,-1106,-1106,-1106,-1106,-1106,-1106,-1106, + + -1106,-1106,-1106,-1106,-1106,-1106,-1106,-1106,-1106,-1106, + -1106,-1106, 1109,-1106,-1106,-1106,-1106,-1106,-1106,-1106, + -1106,-1106,-1106,-1106,-1106,-1106,-1106,-1106,-1106,-1106, + -1106,-1106,-1106,-1106,-1106,-1106,-1106,-1106,-1106,-1106, + -1106,-1106,-1106,-1106,-1106,-1106,-1106,-1106,-1106,-1106, + -1106,-1106,-1106,-1106,-1106,-1106,-1106,-1106,-1106,-1106, + -1106,-1106,-1106,-1106,-1106,-1106,-1106,-1106,-1106,-1106, + -1106,-1106,-1106,-1106,-1106,-1106,-1106,-1106,-1106,-1106, + -1106,-1106,-1106,-1106,-1106,-1106,-1106,-1106,-1106,-1106, + -1106,-1106,-1106,-1106,-1106,-1106,-1106,-1106,-1106,-1106, + + -1106,-1106,-1106,-1106,-1106,-1106,-1106,-1106 + }, + + { + 55,-1107,-1107,-1107,-1107,-1107,-1107,-1107,-1107,-1107, + -1107,-1107,-1107,-1107,-1107,-1107,-1107,-1107,-1107,-1107, + -1107,-1107,-1107,-1107,-1107,-1107,-1107,-1107,-1107,-1107, + -1107,-1107, 1110,-1107,-1107,-1107,-1107,-1107,-1107,-1107, + -1107,-1107,-1107,-1107,-1107,-1107,-1107,-1107,-1107,-1107, + -1107,-1107,-1107,-1107,-1107,-1107,-1107,-1107,-1107,-1107, + -1107,-1107,-1107,-1107,-1107,-1107,-1107,-1107,-1107,-1107, + -1107,-1107,-1107,-1107,-1107,-1107,-1107,-1107,-1107,-1107, + -1107,-1107,-1107,-1107,-1107,-1107,-1107,-1107,-1107,-1107, + + -1107,-1107,-1107,-1107,-1107,-1107,-1107,-1107,-1107,-1107, + -1107,-1107,-1107,-1107,-1107,-1107,-1107,-1107,-1107,-1107, + -1107,-1107,-1107,-1107,-1107,-1107,-1107,-1107,-1107,-1107, + -1107,-1107,-1107,-1107,-1107,-1107,-1107,-1107 + }, + + { + 55,-1108,-1108,-1108,-1108,-1108,-1108,-1108,-1108,-1108, + -1108,-1108,-1108,-1108,-1108,-1108,-1108,-1108,-1108,-1108, + -1108,-1108,-1108,-1108,-1108,-1108,-1108,-1108,-1108,-1108, + -1108,-1108, 1111,-1108,-1108,-1108,-1108,-1108,-1108,-1108, + -1108,-1108,-1108,-1108,-1108,-1108,-1108,-1108,-1108,-1108, + -1108,-1108,-1108,-1108,-1108,-1108,-1108,-1108,-1108,-1108, + + -1108,-1108,-1108,-1108,-1108,-1108,-1108,-1108,-1108,-1108, + -1108,-1108,-1108,-1108,-1108,-1108,-1108,-1108,-1108,-1108, + -1108,-1108,-1108,-1108,-1108,-1108,-1108,-1108,-1108,-1108, + -1108,-1108,-1108,-1108,-1108,-1108,-1108,-1108,-1108,-1108, + -1108,-1108,-1108,-1108,-1108,-1108,-1108,-1108,-1108,-1108, + -1108,-1108,-1108,-1108,-1108,-1108,-1108,-1108,-1108,-1108, + -1108,-1108,-1108,-1108,-1108,-1108,-1108,-1108 + }, + + { + 55,-1109,-1109,-1109,-1109,-1109,-1109,-1109,-1109,-1109, + -1109,-1109,-1109,-1109,-1109,-1109,-1109,-1109,-1109,-1109, + -1109,-1109,-1109,-1109,-1109,-1109,-1109,-1109,-1109,-1109, + + -1109,-1109, 1112,-1109,-1109,-1109,-1109,-1109,-1109,-1109, + -1109,-1109,-1109,-1109,-1109,-1109,-1109,-1109,-1109,-1109, + -1109,-1109,-1109,-1109,-1109,-1109,-1109,-1109,-1109,-1109, + -1109,-1109,-1109,-1109,-1109,-1109,-1109,-1109,-1109,-1109, + -1109,-1109,-1109,-1109,-1109,-1109,-1109,-1109,-1109,-1109, + -1109,-1109,-1109,-1109,-1109,-1109,-1109,-1109,-1109,-1109, + -1109,-1109,-1109,-1109,-1109,-1109,-1109,-1109,-1109,-1109, + -1109,-1109,-1109,-1109,-1109,-1109,-1109,-1109,-1109,-1109, + -1109,-1109,-1109,-1109,-1109,-1109,-1109,-1109,-1109,-1109, + -1109,-1109,-1109,-1109,-1109,-1109,-1109,-1109 + + }, + + { + 55,-1110,-1110,-1110,-1110,-1110,-1110,-1110,-1110,-1110, + -1110,-1110,-1110,-1110,-1110,-1110,-1110,-1110,-1110,-1110, + -1110,-1110,-1110,-1110,-1110,-1110,-1110,-1110,-1110,-1110, + -1110,-1110, 1113,-1110,-1110,-1110,-1110,-1110,-1110,-1110, + -1110,-1110,-1110, 1114,-1110, 1114,-1110,-1110, 1115, 1115, + 1115, 1115, 1115, 1115, 1115, 1115, 1115, 1115,-1110,-1110, + -1110,-1110,-1110,-1110,-1110,-1110,-1110,-1110,-1110,-1110, + -1110,-1110,-1110,-1110,-1110,-1110,-1110,-1110,-1110,-1110, + -1110,-1110,-1110,-1110,-1110,-1110,-1110,-1110,-1110,-1110, + -1110,-1110,-1110,-1110,-1110,-1110,-1110,-1110,-1110,-1110, + + -1110,-1110,-1110,-1110,-1110,-1110,-1110,-1110,-1110,-1110, + -1110,-1110,-1110,-1110,-1110,-1110,-1110,-1110,-1110,-1110, + -1110,-1110,-1110,-1110,-1110,-1110,-1110,-1110 + }, + + { + 55,-1111,-1111,-1111,-1111,-1111,-1111,-1111,-1111,-1111, + -1111,-1111,-1111,-1111,-1111,-1111,-1111,-1111,-1111,-1111, + -1111,-1111,-1111,-1111,-1111,-1111,-1111,-1111,-1111,-1111, + -1111,-1111, 1116,-1111,-1111,-1111,-1111,-1111,-1111,-1111, + -1111,-1111,-1111, 1117,-1111, 1117,-1111,-1111, 1118, 1118, + 1118, 1118, 1118, 1118, 1118, 1118, 1118, 1118,-1111,-1111, + -1111,-1111,-1111,-1111,-1111,-1111,-1111,-1111,-1111,-1111, + + -1111,-1111,-1111,-1111,-1111,-1111,-1111,-1111,-1111,-1111, + -1111,-1111,-1111,-1111,-1111,-1111,-1111,-1111,-1111,-1111, + -1111,-1111,-1111,-1111,-1111,-1111,-1111,-1111,-1111,-1111, + -1111,-1111,-1111,-1111,-1111,-1111,-1111,-1111,-1111,-1111, + -1111,-1111,-1111,-1111,-1111,-1111,-1111,-1111,-1111,-1111, + -1111,-1111,-1111,-1111,-1111,-1111,-1111,-1111 + }, + + { + 55,-1112,-1112,-1112,-1112,-1112,-1112,-1112,-1112,-1112, + -1112,-1112,-1112,-1112,-1112,-1112,-1112,-1112,-1112,-1112, + -1112,-1112,-1112,-1112,-1112,-1112,-1112,-1112,-1112,-1112, + -1112,-1112, 1119,-1112,-1112,-1112,-1112,-1112,-1112,-1112, + + -1112,-1112,-1112,-1112,-1112,-1112,-1112,-1112,-1112,-1112, + -1112,-1112,-1112,-1112,-1112,-1112,-1112,-1112,-1112,-1112, + -1112,-1112,-1112,-1112,-1112,-1112,-1112,-1112,-1112,-1112, + -1112,-1112,-1112,-1112,-1112,-1112,-1112,-1112,-1112,-1112, + -1112,-1112,-1112,-1112,-1112,-1112,-1112,-1112,-1112,-1112, + -1112,-1112,-1112,-1112,-1112,-1112,-1112,-1112,-1112,-1112, + -1112,-1112,-1112,-1112,-1112,-1112,-1112,-1112,-1112,-1112, + -1112,-1112,-1112,-1112,-1112,-1112,-1112,-1112,-1112,-1112, + -1112,-1112,-1112,-1112,-1112,-1112,-1112,-1112 + }, + + { + 55,-1113,-1113,-1113,-1113,-1113,-1113,-1113,-1113,-1113, + + -1113,-1113,-1113,-1113,-1113,-1113,-1113,-1113,-1113,-1113, + -1113,-1113,-1113,-1113,-1113,-1113,-1113,-1113,-1113,-1113, + -1113,-1113, 1113,-1113,-1113,-1113,-1113,-1113,-1113,-1113, + -1113,-1113,-1113, 1114,-1113, 1114,-1113,-1113, 1115, 1115, + 1115, 1115, 1115, 1115, 1115, 1115, 1115, 1115,-1113,-1113, + -1113,-1113,-1113,-1113,-1113,-1113,-1113,-1113,-1113,-1113, + -1113,-1113,-1113,-1113,-1113,-1113,-1113,-1113,-1113,-1113, + -1113,-1113,-1113,-1113,-1113,-1113,-1113,-1113,-1113,-1113, + -1113,-1113,-1113,-1113,-1113,-1113,-1113,-1113,-1113,-1113, + -1113,-1113,-1113,-1113,-1113,-1113,-1113,-1113,-1113,-1113, + + -1113,-1113,-1113,-1113,-1113,-1113,-1113,-1113,-1113,-1113, + -1113,-1113,-1113,-1113,-1113,-1113,-1113,-1113 + }, + + { + 55,-1114,-1114,-1114,-1114,-1114,-1114,-1114,-1114,-1114, + -1114,-1114,-1114,-1114,-1114,-1114,-1114,-1114,-1114,-1114, + -1114,-1114,-1114,-1114,-1114,-1114,-1114,-1114,-1114,-1114, + -1114,-1114,-1114,-1114,-1114,-1114,-1114,-1114,-1114,-1114, + -1114,-1114,-1114,-1114,-1114,-1114,-1114,-1114, 1115, 1115, + 1115, 1115, 1115, 1115, 1115, 1115, 1115, 1115,-1114,-1114, + -1114,-1114,-1114,-1114,-1114,-1114,-1114,-1114,-1114,-1114, + -1114,-1114,-1114,-1114,-1114,-1114,-1114,-1114,-1114,-1114, + + -1114,-1114,-1114,-1114,-1114,-1114,-1114,-1114,-1114,-1114, + -1114,-1114,-1114,-1114,-1114,-1114,-1114,-1114,-1114,-1114, + -1114,-1114,-1114,-1114,-1114,-1114,-1114,-1114,-1114,-1114, + -1114,-1114,-1114,-1114,-1114,-1114,-1114,-1114,-1114,-1114, + -1114,-1114,-1114,-1114,-1114,-1114,-1114,-1114 + }, + + { + 55,-1115,-1115,-1115,-1115,-1115,-1115,-1115,-1115,-1115, + -1115,-1115,-1115,-1115,-1115,-1115,-1115,-1115,-1115,-1115, + -1115,-1115,-1115,-1115,-1115,-1115,-1115,-1115,-1115,-1115, + -1115,-1115, 1120,-1115,-1115,-1115,-1115,-1115,-1115,-1115, + -1115,-1115,-1115,-1115,-1115,-1115,-1115, 1121, 1115, 1115, + + 1115, 1115, 1115, 1115, 1115, 1115, 1115, 1115,-1115,-1115, + -1115,-1115,-1115,-1115,-1115,-1115,-1115,-1115,-1115,-1115, + -1115,-1115,-1115,-1115,-1115,-1115,-1115,-1115,-1115,-1115, + -1115,-1115,-1115,-1115,-1115,-1115,-1115,-1115,-1115,-1115, + -1115,-1115,-1115,-1115,-1115,-1115,-1115,-1115,-1115,-1115, + -1115,-1115,-1115,-1115,-1115,-1115,-1115,-1115,-1115,-1115, + -1115,-1115,-1115,-1115,-1115,-1115,-1115,-1115,-1115,-1115, + -1115,-1115,-1115,-1115,-1115,-1115,-1115,-1115 + }, + + { + 55,-1116,-1116,-1116,-1116,-1116,-1116,-1116,-1116,-1116, + -1116,-1116,-1116,-1116,-1116,-1116,-1116,-1116,-1116,-1116, + + -1116,-1116,-1116,-1116,-1116,-1116,-1116,-1116,-1116,-1116, + -1116,-1116, 1116,-1116,-1116,-1116,-1116,-1116,-1116,-1116, + -1116,-1116,-1116, 1117,-1116, 1117,-1116,-1116, 1118, 1118, + 1118, 1118, 1118, 1118, 1118, 1118, 1118, 1118,-1116,-1116, + -1116,-1116,-1116,-1116,-1116,-1116,-1116,-1116,-1116,-1116, + -1116,-1116,-1116,-1116,-1116,-1116,-1116,-1116,-1116,-1116, + -1116,-1116,-1116,-1116,-1116,-1116,-1116,-1116,-1116,-1116, + -1116,-1116,-1116,-1116,-1116,-1116,-1116,-1116,-1116,-1116, + -1116,-1116,-1116,-1116,-1116,-1116,-1116,-1116,-1116,-1116, + -1116,-1116,-1116,-1116,-1116,-1116,-1116,-1116,-1116,-1116, + + -1116,-1116,-1116,-1116,-1116,-1116,-1116,-1116 + }, + + { + 55,-1117,-1117,-1117,-1117,-1117,-1117,-1117,-1117,-1117, + -1117,-1117,-1117,-1117,-1117,-1117,-1117,-1117,-1117,-1117, + -1117,-1117,-1117,-1117,-1117,-1117,-1117,-1117,-1117,-1117, + -1117,-1117,-1117,-1117,-1117,-1117,-1117,-1117,-1117,-1117, + -1117,-1117,-1117,-1117,-1117,-1117,-1117,-1117, 1118, 1118, + 1118, 1118, 1118, 1118, 1118, 1118, 1118, 1118,-1117,-1117, + -1117,-1117,-1117,-1117,-1117,-1117,-1117,-1117,-1117,-1117, + -1117,-1117,-1117,-1117,-1117,-1117,-1117,-1117,-1117,-1117, + -1117,-1117,-1117,-1117,-1117,-1117,-1117,-1117,-1117,-1117, + + -1117,-1117,-1117,-1117,-1117,-1117,-1117,-1117,-1117,-1117, + -1117,-1117,-1117,-1117,-1117,-1117,-1117,-1117,-1117,-1117, + -1117,-1117,-1117,-1117,-1117,-1117,-1117,-1117,-1117,-1117, + -1117,-1117,-1117,-1117,-1117,-1117,-1117,-1117 + }, + + { + 55,-1118,-1118,-1118,-1118,-1118,-1118,-1118,-1118,-1118, + -1118,-1118,-1118,-1118,-1118,-1118,-1118,-1118,-1118,-1118, + -1118,-1118,-1118,-1118,-1118,-1118,-1118,-1118,-1118,-1118, + -1118,-1118,-1118,-1118,-1118,-1118,-1118,-1118,-1118,-1118, + -1118,-1118,-1118,-1118,-1118,-1118,-1118,-1118, 1118, 1118, + 1118, 1118, 1118, 1118, 1118, 1118, 1118, 1118,-1118,-1118, + + -1118,-1118,-1118,-1118,-1118,-1118,-1118,-1118,-1118,-1118, + -1118,-1118,-1118,-1118,-1118,-1118,-1118,-1118,-1118,-1118, + -1118,-1118,-1118,-1118,-1118,-1118,-1118,-1118,-1118,-1118, + -1118,-1118,-1118,-1118,-1118,-1118,-1118,-1118,-1118,-1118, + -1118,-1118,-1118,-1118,-1118,-1118,-1118,-1118,-1118,-1118, + -1118,-1118,-1118,-1118,-1118,-1118,-1118,-1118,-1118,-1118, + -1118,-1118,-1118,-1118,-1118,-1118,-1118,-1118 + }, + + { + 55,-1119,-1119,-1119,-1119,-1119,-1119,-1119,-1119,-1119, + -1119,-1119,-1119,-1119,-1119,-1119,-1119,-1119,-1119,-1119, + -1119,-1119,-1119,-1119,-1119,-1119,-1119,-1119,-1119,-1119, + + -1119,-1119, 1122,-1119,-1119,-1119,-1119,-1119,-1119,-1119, + -1119,-1119,-1119,-1119,-1119,-1119,-1119,-1119,-1119,-1119, + -1119,-1119,-1119,-1119,-1119,-1119,-1119,-1119,-1119,-1119, + -1119,-1119,-1119,-1119,-1119,-1119,-1119,-1119,-1119,-1119, + -1119,-1119,-1119,-1119,-1119,-1119,-1119,-1119,-1119,-1119, + -1119,-1119,-1119,-1119,-1119,-1119,-1119,-1119,-1119,-1119, + -1119,-1119,-1119,-1119,-1119,-1119,-1119,-1119,-1119,-1119, + -1119,-1119,-1119,-1119,-1119,-1119,-1119,-1119,-1119,-1119, + -1119,-1119,-1119,-1119,-1119,-1119,-1119,-1119,-1119,-1119, + -1119,-1119,-1119,-1119,-1119,-1119,-1119,-1119 + + }, + + { + 55,-1120,-1120,-1120,-1120,-1120,-1120,-1120,-1120,-1120, + -1120,-1120,-1120,-1120,-1120,-1120,-1120,-1120,-1120,-1120, + -1120,-1120,-1120,-1120,-1120,-1120,-1120,-1120,-1120,-1120, + -1120,-1120, 1120,-1120,-1120,-1120,-1120,-1120,-1120,-1120, + -1120,-1120,-1120,-1120,-1120,-1120,-1120, 1121,-1120,-1120, + -1120,-1120,-1120,-1120,-1120,-1120,-1120,-1120,-1120,-1120, + -1120,-1120,-1120,-1120,-1120,-1120,-1120,-1120,-1120,-1120, + -1120,-1120,-1120,-1120,-1120,-1120,-1120,-1120,-1120,-1120, + -1120,-1120,-1120,-1120,-1120,-1120,-1120,-1120,-1120,-1120, + -1120,-1120,-1120,-1120,-1120,-1120,-1120,-1120,-1120,-1120, + + -1120,-1120,-1120,-1120,-1120,-1120,-1120,-1120,-1120,-1120, + -1120,-1120,-1120,-1120,-1120,-1120,-1120,-1120,-1120,-1120, + -1120,-1120,-1120,-1120,-1120,-1120,-1120,-1120 + }, + + { + 55, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, + -1121, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, + 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, + 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, + 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, + 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, + 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, + + 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, + 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, + 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, + 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, + 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, + 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123 + }, + + { + 55,-1122,-1122,-1122,-1122,-1122,-1122,-1122,-1122,-1122, + -1122,-1122,-1122,-1122,-1122,-1122,-1122,-1122,-1122,-1122, + -1122,-1122,-1122,-1122,-1122,-1122,-1122,-1122,-1122,-1122, + -1122,-1122, 1124,-1122,-1122,-1122,-1122,-1122,-1122,-1122, + + -1122,-1122,-1122,-1122,-1122,-1122,-1122,-1122,-1122,-1122, + -1122,-1122,-1122,-1122,-1122,-1122,-1122,-1122,-1122,-1122, + -1122,-1122,-1122,-1122,-1122,-1122,-1122,-1122,-1122,-1122, + -1122,-1122,-1122,-1122,-1122,-1122,-1122,-1122,-1122,-1122, + -1122,-1122,-1122,-1122,-1122,-1122,-1122,-1122,-1122,-1122, + -1122,-1122,-1122,-1122,-1122,-1122,-1122,-1122,-1122,-1122, + -1122,-1122,-1122,-1122,-1122,-1122,-1122,-1122,-1122,-1122, + -1122,-1122,-1122,-1122,-1122,-1122,-1122,-1122,-1122,-1122, + -1122,-1122,-1122,-1122,-1122,-1122,-1122,-1122 + }, + + { + 55, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, + + -1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, + 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, + 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, + 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, + 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, + 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, + 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, + 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, + 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, + 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, + + 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, + 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123 + }, + + { + 55,-1124,-1124,-1124,-1124,-1124,-1124,-1124,-1124,-1124, + -1124,-1124,-1124,-1124,-1124,-1124,-1124,-1124,-1124,-1124, + -1124,-1124,-1124,-1124,-1124,-1124,-1124,-1124,-1124,-1124, + -1124,-1124, 1125,-1124,-1124,-1124,-1124,-1124,-1124,-1124, + -1124,-1124,-1124,-1124,-1124,-1124,-1124,-1124,-1124,-1124, + -1124,-1124,-1124,-1124,-1124,-1124,-1124,-1124,-1124,-1124, + -1124,-1124,-1124,-1124,-1124,-1124,-1124,-1124,-1124,-1124, + -1124,-1124,-1124,-1124,-1124,-1124,-1124,-1124,-1124,-1124, + + -1124,-1124,-1124,-1124,-1124,-1124,-1124,-1124,-1124,-1124, + -1124,-1124,-1124,-1124,-1124,-1124,-1124,-1124,-1124,-1124, + -1124,-1124,-1124,-1124,-1124,-1124,-1124,-1124,-1124,-1124, + -1124,-1124,-1124,-1124,-1124,-1124,-1124,-1124,-1124,-1124, + -1124,-1124,-1124,-1124,-1124,-1124,-1124,-1124 + }, + + { + 55,-1125,-1125,-1125,-1125,-1125,-1125,-1125,-1125,-1125, + -1125,-1125,-1125,-1125,-1125,-1125,-1125,-1125,-1125,-1125, + -1125,-1125,-1125,-1125,-1125,-1125,-1125,-1125,-1125,-1125, + -1125,-1125, 1126,-1125,-1125,-1125,-1125,-1125,-1125,-1125, + -1125,-1125,-1125,-1125,-1125,-1125,-1125,-1125,-1125,-1125, + + -1125,-1125,-1125,-1125,-1125,-1125,-1125,-1125,-1125,-1125, + -1125,-1125,-1125,-1125,-1125,-1125,-1125,-1125,-1125,-1125, + -1125,-1125,-1125,-1125,-1125,-1125,-1125,-1125,-1125,-1125, + -1125,-1125,-1125,-1125,-1125,-1125,-1125,-1125,-1125,-1125, + -1125,-1125,-1125,-1125,-1125,-1125,-1125,-1125,-1125,-1125, + -1125,-1125,-1125,-1125,-1125,-1125,-1125,-1125,-1125,-1125, + -1125,-1125,-1125,-1125,-1125,-1125,-1125,-1125,-1125,-1125, + -1125,-1125,-1125,-1125,-1125,-1125,-1125,-1125 + }, + + { + 55,-1126,-1126,-1126,-1126,-1126,-1126,-1126,-1126,-1126, + -1126,-1126,-1126,-1126,-1126,-1126,-1126,-1126,-1126,-1126, + + -1126,-1126,-1126,-1126,-1126,-1126,-1126,-1126,-1126,-1126, + -1126,-1126, 1127,-1126,-1126,-1126,-1126,-1126,-1126,-1126, + -1126,-1126,-1126,-1126,-1126,-1126,-1126,-1126,-1126,-1126, + -1126,-1126,-1126,-1126,-1126,-1126,-1126,-1126,-1126,-1126, + -1126,-1126,-1126,-1126,-1126,-1126,-1126,-1126,-1126,-1126, + -1126,-1126,-1126,-1126,-1126,-1126,-1126,-1126,-1126,-1126, + -1126,-1126,-1126,-1126,-1126,-1126,-1126,-1126,-1126,-1126, + -1126,-1126,-1126,-1126,-1126,-1126,-1126,-1126,-1126,-1126, + -1126,-1126,-1126,-1126,-1126,-1126,-1126,-1126,-1126,-1126, + -1126,-1126,-1126,-1126,-1126,-1126,-1126,-1126,-1126,-1126, + + -1126,-1126,-1126,-1126,-1126,-1126,-1126,-1126 + }, + + { + 55,-1127,-1127,-1127,-1127,-1127,-1127,-1127,-1127,-1127, + -1127,-1127,-1127,-1127,-1127,-1127,-1127,-1127,-1127,-1127, + -1127,-1127,-1127,-1127,-1127,-1127,-1127,-1127,-1127,-1127, + -1127,-1127, 1128,-1127,-1127,-1127,-1127,-1127,-1127,-1127, + -1127,-1127,-1127,-1127,-1127,-1127,-1127,-1127,-1127,-1127, + -1127,-1127,-1127,-1127,-1127,-1127,-1127,-1127,-1127,-1127, + -1127,-1127,-1127,-1127,-1127,-1127,-1127,-1127,-1127,-1127, + -1127,-1127,-1127,-1127,-1127,-1127,-1127,-1127,-1127,-1127, + -1127,-1127,-1127,-1127,-1127,-1127,-1127,-1127,-1127,-1127, + + -1127,-1127,-1127,-1127,-1127,-1127,-1127,-1127,-1127,-1127, + -1127,-1127,-1127,-1127,-1127,-1127,-1127,-1127,-1127,-1127, + -1127,-1127,-1127,-1127,-1127,-1127,-1127,-1127,-1127,-1127, + -1127,-1127,-1127,-1127,-1127,-1127,-1127,-1127 + }, + + { + 55,-1128,-1128,-1128,-1128,-1128,-1128,-1128,-1128,-1128, + -1128,-1128,-1128,-1128,-1128,-1128,-1128,-1128,-1128,-1128, + -1128,-1128,-1128,-1128,-1128,-1128,-1128,-1128,-1128,-1128, + -1128,-1128, 1129,-1128,-1128,-1128,-1128,-1128,-1128,-1128, + -1128,-1128,-1128,-1128,-1128,-1128,-1128,-1128,-1128,-1128, + -1128,-1128,-1128,-1128,-1128,-1128,-1128,-1128,-1128,-1128, + + -1128,-1128,-1128,-1128,-1128,-1128,-1128,-1128,-1128,-1128, + -1128,-1128,-1128,-1128,-1128,-1128,-1128,-1128,-1128,-1128, + -1128,-1128,-1128,-1128,-1128,-1128,-1128,-1128,-1128,-1128, + -1128,-1128,-1128,-1128,-1128,-1128,-1128,-1128,-1128,-1128, + -1128,-1128,-1128,-1128,-1128,-1128,-1128,-1128,-1128,-1128, + -1128,-1128,-1128,-1128,-1128,-1128,-1128,-1128,-1128,-1128, + -1128,-1128,-1128,-1128,-1128,-1128,-1128,-1128 + }, + + { + 55,-1129,-1129,-1129,-1129,-1129,-1129,-1129,-1129,-1129, + -1129,-1129,-1129,-1129,-1129,-1129,-1129,-1129,-1129,-1129, + -1129,-1129,-1129,-1129,-1129,-1129,-1129,-1129,-1129,-1129, + + -1129,-1129, 1130,-1129,-1129,-1129,-1129,-1129,-1129,-1129, + -1129,-1129,-1129,-1129,-1129,-1129,-1129,-1129,-1129,-1129, + -1129,-1129,-1129,-1129,-1129,-1129,-1129,-1129,-1129,-1129, + -1129,-1129,-1129,-1129,-1129,-1129,-1129,-1129,-1129,-1129, + -1129,-1129,-1129,-1129,-1129,-1129,-1129,-1129,-1129,-1129, + -1129,-1129,-1129,-1129,-1129,-1129,-1129,-1129,-1129,-1129, + -1129,-1129,-1129,-1129,-1129,-1129,-1129,-1129,-1129,-1129, + -1129,-1129,-1129,-1129,-1129,-1129,-1129,-1129,-1129,-1129, + -1129,-1129,-1129,-1129,-1129,-1129,-1129,-1129,-1129,-1129, + -1129,-1129,-1129,-1129,-1129,-1129,-1129,-1129 + + }, + + { + 55,-1130,-1130,-1130,-1130,-1130,-1130,-1130,-1130,-1130, + -1130,-1130,-1130,-1130,-1130,-1130,-1130,-1130,-1130,-1130, + -1130,-1130,-1130,-1130,-1130,-1130,-1130,-1130,-1130,-1130, + -1130,-1130, 1131,-1130,-1130,-1130,-1130,-1130,-1130,-1130, + -1130,-1130,-1130,-1130,-1130,-1130,-1130,-1130,-1130,-1130, + -1130,-1130,-1130,-1130,-1130,-1130,-1130,-1130,-1130,-1130, + -1130,-1130,-1130,-1130,-1130,-1130,-1130,-1130,-1130,-1130, + -1130,-1130,-1130,-1130,-1130,-1130,-1130,-1130,-1130,-1130, + -1130,-1130,-1130,-1130,-1130,-1130,-1130,-1130,-1130,-1130, + -1130,-1130,-1130,-1130,-1130,-1130,-1130,-1130,-1130,-1130, + + -1130,-1130,-1130,-1130,-1130,-1130,-1130,-1130,-1130,-1130, + -1130,-1130,-1130,-1130,-1130,-1130,-1130,-1130,-1130,-1130, + -1130,-1130,-1130,-1130,-1130,-1130,-1130,-1130 + }, + + { + 55,-1131,-1131,-1131,-1131,-1131,-1131,-1131,-1131,-1131, + -1131,-1131,-1131,-1131,-1131,-1131,-1131,-1131,-1131,-1131, + -1131,-1131,-1131,-1131,-1131,-1131,-1131,-1131,-1131,-1131, + -1131,-1131, 1132,-1131,-1131,-1131,-1131,-1131,-1131,-1131, + -1131,-1131,-1131,-1131,-1131,-1131,-1131,-1131,-1131,-1131, + -1131,-1131,-1131,-1131,-1131,-1131,-1131,-1131,-1131,-1131, + -1131,-1131,-1131,-1131,-1131,-1131,-1131,-1131,-1131,-1131, + + -1131,-1131,-1131,-1131,-1131,-1131,-1131,-1131,-1131,-1131, + -1131,-1131,-1131,-1131,-1131,-1131,-1131,-1131,-1131,-1131, + -1131,-1131,-1131,-1131,-1131,-1131,-1131,-1131,-1131,-1131, + -1131,-1131,-1131,-1131,-1131,-1131,-1131,-1131,-1131,-1131, + -1131,-1131,-1131,-1131,-1131,-1131,-1131,-1131,-1131,-1131, + -1131,-1131,-1131,-1131,-1131,-1131,-1131,-1131 + }, + + { + 55,-1132,-1132,-1132,-1132,-1132,-1132,-1132,-1132,-1132, + -1132,-1132,-1132,-1132,-1132,-1132,-1132,-1132,-1132,-1132, + -1132,-1132,-1132,-1132,-1132,-1132,-1132,-1132,-1132,-1132, + -1132,-1132, 1133,-1132,-1132,-1132,-1132,-1132,-1132,-1132, + + -1132,-1132,-1132,-1132,-1132,-1132,-1132,-1132,-1132,-1132, + -1132,-1132,-1132,-1132,-1132,-1132,-1132,-1132,-1132,-1132, + -1132,-1132,-1132,-1132,-1132,-1132,-1132,-1132,-1132,-1132, + -1132,-1132,-1132,-1132,-1132,-1132,-1132,-1132,-1132,-1132, + -1132,-1132,-1132,-1132,-1132,-1132,-1132,-1132,-1132,-1132, + -1132,-1132,-1132,-1132,-1132,-1132,-1132,-1132,-1132,-1132, + -1132,-1132,-1132,-1132,-1132,-1132,-1132,-1132,-1132,-1132, + -1132,-1132,-1132,-1132,-1132,-1132,-1132,-1132,-1132,-1132, + -1132,-1132,-1132,-1132,-1132,-1132,-1132,-1132 + }, + + { + 55,-1133,-1133,-1133,-1133,-1133,-1133,-1133,-1133,-1133, + + -1133,-1133,-1133,-1133,-1133,-1133,-1133,-1133,-1133,-1133, + -1133,-1133,-1133,-1133,-1133,-1133,-1133,-1133,-1133,-1133, + -1133,-1133, 1134,-1133,-1133,-1133,-1133,-1133,-1133,-1133, + -1133,-1133,-1133,-1133,-1133,-1133,-1133,-1133,-1133,-1133, + -1133,-1133,-1133,-1133,-1133,-1133,-1133,-1133,-1133,-1133, + -1133,-1133,-1133,-1133,-1133,-1133,-1133,-1133,-1133,-1133, + -1133,-1133,-1133,-1133,-1133,-1133,-1133,-1133,-1133,-1133, + -1133,-1133,-1133,-1133,-1133,-1133,-1133,-1133,-1133,-1133, + -1133,-1133,-1133,-1133,-1133,-1133,-1133,-1133,-1133,-1133, + -1133,-1133,-1133,-1133,-1133,-1133,-1133,-1133,-1133,-1133, + + -1133,-1133,-1133,-1133,-1133,-1133,-1133,-1133,-1133,-1133, + -1133,-1133,-1133,-1133,-1133,-1133,-1133,-1133 + }, + + { + 55,-1134,-1134,-1134,-1134,-1134,-1134,-1134,-1134,-1134, + -1134,-1134,-1134,-1134,-1134,-1134,-1134,-1134,-1134,-1134, + -1134,-1134,-1134,-1134,-1134,-1134,-1134,-1134,-1134,-1134, + -1134,-1134, 1135,-1134,-1134,-1134,-1134,-1134,-1134,-1134, + -1134,-1134,-1134,-1134,-1134,-1134,-1134,-1134,-1134,-1134, + -1134,-1134,-1134,-1134,-1134,-1134,-1134,-1134,-1134,-1134, + -1134,-1134,-1134,-1134,-1134,-1134,-1134,-1134,-1134,-1134, + -1134,-1134,-1134,-1134,-1134,-1134,-1134,-1134,-1134,-1134, + + -1134,-1134,-1134,-1134,-1134,-1134,-1134,-1134,-1134,-1134, + -1134,-1134,-1134,-1134,-1134,-1134,-1134,-1134,-1134,-1134, + -1134,-1134,-1134,-1134,-1134,-1134,-1134,-1134,-1134,-1134, + -1134,-1134,-1134,-1134,-1134,-1134,-1134,-1134,-1134,-1134, + -1134,-1134,-1134,-1134,-1134,-1134,-1134,-1134 + }, + + { + 55,-1135,-1135,-1135,-1135,-1135,-1135,-1135,-1135,-1135, + -1135,-1135,-1135,-1135,-1135,-1135,-1135,-1135,-1135,-1135, + -1135,-1135,-1135,-1135,-1135,-1135,-1135,-1135,-1135,-1135, + -1135,-1135, 1136,-1135,-1135,-1135,-1135,-1135,-1135,-1135, + -1135,-1135,-1135,-1135,-1135,-1135,-1135,-1135,-1135,-1135, + + -1135,-1135,-1135,-1135,-1135,-1135,-1135,-1135,-1135,-1135, + -1135,-1135,-1135,-1135,-1135,-1135,-1135,-1135,-1135,-1135, + -1135,-1135,-1135,-1135,-1135,-1135,-1135,-1135,-1135,-1135, + -1135,-1135,-1135,-1135,-1135,-1135,-1135,-1135,-1135,-1135, + -1135,-1135,-1135,-1135,-1135,-1135,-1135,-1135,-1135,-1135, + -1135,-1135,-1135,-1135,-1135,-1135,-1135,-1135,-1135,-1135, + -1135,-1135,-1135,-1135,-1135,-1135,-1135,-1135,-1135,-1135, + -1135,-1135,-1135,-1135,-1135,-1135,-1135,-1135 + }, + + { + 55,-1136,-1136,-1136,-1136,-1136,-1136,-1136,-1136,-1136, + -1136,-1136,-1136,-1136,-1136,-1136,-1136,-1136,-1136,-1136, + + -1136,-1136,-1136,-1136,-1136,-1136,-1136,-1136,-1136,-1136, + -1136,-1136, 1137,-1136,-1136,-1136,-1136,-1136,-1136,-1136, + -1136,-1136,-1136,-1136,-1136,-1136,-1136,-1136,-1136,-1136, + -1136,-1136,-1136,-1136,-1136,-1136,-1136,-1136,-1136,-1136, + -1136,-1136,-1136,-1136,-1136,-1136,-1136,-1136,-1136,-1136, + -1136,-1136,-1136,-1136,-1136,-1136,-1136,-1136,-1136,-1136, + -1136,-1136,-1136,-1136,-1136,-1136,-1136,-1136,-1136,-1136, + -1136,-1136,-1136,-1136,-1136,-1136,-1136,-1136,-1136,-1136, + -1136,-1136,-1136,-1136,-1136,-1136,-1136,-1136,-1136,-1136, + -1136,-1136,-1136,-1136,-1136,-1136,-1136,-1136,-1136,-1136, + + -1136,-1136,-1136,-1136,-1136,-1136,-1136,-1136 + }, + + { + 55,-1137,-1137,-1137,-1137,-1137,-1137,-1137,-1137,-1137, + -1137,-1137,-1137,-1137,-1137,-1137,-1137,-1137,-1137,-1137, + -1137,-1137,-1137,-1137,-1137,-1137,-1137,-1137,-1137,-1137, + -1137,-1137, 1138,-1137,-1137,-1137,-1137,-1137,-1137,-1137, + -1137,-1137,-1137,-1137,-1137,-1137,-1137,-1137,-1137,-1137, + -1137,-1137,-1137,-1137,-1137,-1137,-1137,-1137,-1137,-1137, + -1137,-1137,-1137,-1137,-1137,-1137,-1137,-1137,-1137,-1137, + -1137,-1137,-1137,-1137,-1137,-1137,-1137,-1137,-1137,-1137, + -1137,-1137,-1137,-1137,-1137,-1137,-1137,-1137,-1137,-1137, + + -1137,-1137,-1137,-1137,-1137,-1137,-1137,-1137,-1137,-1137, + -1137,-1137,-1137,-1137,-1137,-1137,-1137,-1137,-1137,-1137, + -1137,-1137,-1137,-1137,-1137,-1137,-1137,-1137,-1137,-1137, + -1137,-1137,-1137,-1137,-1137,-1137,-1137,-1137 + }, + + { + 55,-1138,-1138,-1138,-1138,-1138,-1138,-1138,-1138,-1138, + -1138,-1138,-1138,-1138,-1138,-1138,-1138,-1138,-1138,-1138, + -1138,-1138,-1138,-1138,-1138,-1138,-1138,-1138,-1138,-1138, + -1138,-1138, 1139,-1138,-1138,-1138,-1138,-1138,-1138,-1138, + -1138,-1138,-1138,-1138,-1138,-1138,-1138,-1138,-1138,-1138, + -1138,-1138,-1138,-1138,-1138,-1138,-1138,-1138,-1138,-1138, + + -1138,-1138,-1138,-1138,-1138,-1138,-1138,-1138,-1138,-1138, + -1138,-1138,-1138,-1138,-1138,-1138,-1138,-1138,-1138,-1138, + -1138,-1138,-1138,-1138,-1138,-1138,-1138,-1138,-1138,-1138, + -1138,-1138,-1138,-1138,-1138,-1138,-1138,-1138,-1138,-1138, + -1138,-1138,-1138,-1138,-1138,-1138,-1138,-1138,-1138,-1138, + -1138,-1138,-1138,-1138,-1138,-1138,-1138,-1138,-1138,-1138, + -1138,-1138,-1138,-1138,-1138,-1138,-1138,-1138 + }, + + { + 55,-1139,-1139,-1139,-1139,-1139,-1139,-1139,-1139,-1139, + -1139,-1139,-1139,-1139,-1139,-1139,-1139,-1139,-1139,-1139, + -1139,-1139,-1139,-1139,-1139,-1139,-1139,-1139,-1139,-1139, + + -1139,-1139, 1140,-1139,-1139,-1139,-1139,-1139,-1139,-1139, + -1139,-1139,-1139,-1139,-1139,-1139,-1139,-1139,-1139,-1139, + -1139,-1139,-1139,-1139,-1139,-1139,-1139,-1139,-1139,-1139, + -1139,-1139,-1139,-1139,-1139,-1139,-1139,-1139,-1139,-1139, + -1139,-1139,-1139,-1139,-1139,-1139,-1139,-1139,-1139,-1139, + -1139,-1139,-1139,-1139,-1139,-1139,-1139,-1139,-1139,-1139, + -1139,-1139,-1139,-1139,-1139,-1139,-1139,-1139,-1139,-1139, + -1139,-1139,-1139,-1139,-1139,-1139,-1139,-1139,-1139,-1139, + -1139,-1139,-1139,-1139,-1139,-1139,-1139,-1139,-1139,-1139, + -1139,-1139,-1139,-1139,-1139,-1139,-1139,-1139 + + }, + + { + 55,-1140,-1140,-1140,-1140,-1140,-1140,-1140,-1140,-1140, + -1140,-1140,-1140,-1140,-1140,-1140,-1140,-1140,-1140,-1140, + -1140,-1140,-1140,-1140,-1140,-1140,-1140,-1140,-1140,-1140, + -1140,-1140, 1141,-1140,-1140,-1140,-1140,-1140,-1140,-1140, + -1140,-1140,-1140,-1140,-1140,-1140,-1140,-1140,-1140,-1140, + -1140,-1140,-1140,-1140,-1140,-1140,-1140,-1140,-1140,-1140, + -1140,-1140,-1140,-1140,-1140,-1140,-1140,-1140,-1140,-1140, + -1140,-1140,-1140,-1140,-1140,-1140,-1140,-1140,-1140,-1140, + -1140,-1140,-1140,-1140,-1140,-1140,-1140,-1140,-1140,-1140, + -1140,-1140,-1140,-1140,-1140,-1140,-1140,-1140,-1140,-1140, + + -1140,-1140,-1140,-1140,-1140,-1140,-1140,-1140,-1140,-1140, + -1140,-1140,-1140,-1140,-1140,-1140,-1140,-1140,-1140,-1140, + -1140,-1140,-1140,-1140,-1140,-1140,-1140,-1140 + }, + + { + 55,-1141,-1141,-1141,-1141,-1141,-1141,-1141,-1141,-1141, + -1141,-1141,-1141,-1141,-1141,-1141,-1141,-1141,-1141,-1141, + -1141,-1141,-1141,-1141,-1141,-1141,-1141,-1141,-1141,-1141, + -1141,-1141, 1142,-1141,-1141,-1141,-1141,-1141,-1141,-1141, + -1141,-1141,-1141,-1141,-1141,-1141,-1141,-1141,-1141,-1141, + -1141,-1141,-1141,-1141,-1141,-1141,-1141,-1141,-1141,-1141, + -1141,-1141,-1141,-1141,-1141,-1141,-1141,-1141,-1141,-1141, + + -1141,-1141,-1141,-1141,-1141,-1141,-1141,-1141,-1141,-1141, + -1141,-1141,-1141,-1141,-1141,-1141,-1141,-1141,-1141,-1141, + -1141,-1141,-1141,-1141,-1141,-1141,-1141,-1141,-1141,-1141, + -1141,-1141,-1141,-1141,-1141,-1141,-1141,-1141,-1141,-1141, + -1141,-1141,-1141,-1141,-1141,-1141,-1141,-1141,-1141,-1141, + -1141,-1141,-1141,-1141,-1141,-1141,-1141,-1141 + }, + + { + 55,-1142,-1142,-1142,-1142,-1142,-1142,-1142,-1142,-1142, + -1142,-1142,-1142,-1142,-1142,-1142,-1142,-1142,-1142,-1142, + -1142,-1142,-1142,-1142,-1142,-1142,-1142,-1142,-1142,-1142, + -1142,-1142, 1143,-1142,-1142,-1142,-1142,-1142,-1142,-1142, + + -1142,-1142,-1142,-1142,-1142,-1142,-1142,-1142,-1142,-1142, + -1142,-1142,-1142,-1142,-1142,-1142,-1142,-1142,-1142,-1142, + -1142,-1142,-1142,-1142,-1142,-1142,-1142,-1142,-1142,-1142, + -1142,-1142,-1142,-1142,-1142,-1142,-1142,-1142,-1142,-1142, + -1142,-1142,-1142,-1142,-1142,-1142,-1142,-1142,-1142,-1142, + -1142,-1142,-1142,-1142,-1142,-1142,-1142,-1142,-1142,-1142, + -1142,-1142,-1142,-1142,-1142,-1142,-1142,-1142,-1142,-1142, + -1142,-1142,-1142,-1142,-1142,-1142,-1142,-1142,-1142,-1142, + -1142,-1142,-1142,-1142,-1142,-1142,-1142,-1142 + }, + + { + 55,-1143,-1143,-1143,-1143,-1143,-1143,-1143,-1143,-1143, + + -1143,-1143,-1143,-1143,-1143,-1143,-1143,-1143,-1143,-1143, + -1143,-1143,-1143,-1143,-1143,-1143,-1143,-1143,-1143,-1143, + -1143,-1143, 1144,-1143,-1143,-1143,-1143,-1143,-1143,-1143, + -1143,-1143,-1143,-1143,-1143,-1143,-1143,-1143,-1143,-1143, + -1143,-1143,-1143,-1143,-1143,-1143,-1143,-1143,-1143,-1143, + -1143,-1143,-1143,-1143,-1143,-1143,-1143,-1143,-1143,-1143, + -1143,-1143,-1143,-1143,-1143,-1143,-1143,-1143,-1143,-1143, + -1143,-1143,-1143,-1143,-1143,-1143,-1143,-1143,-1143,-1143, + -1143,-1143,-1143,-1143,-1143,-1143,-1143,-1143,-1143,-1143, + -1143,-1143,-1143,-1143,-1143,-1143,-1143,-1143,-1143,-1143, + + -1143,-1143,-1143,-1143,-1143,-1143,-1143,-1143,-1143,-1143, + -1143,-1143,-1143,-1143,-1143,-1143,-1143,-1143 + }, + + { + 55,-1144,-1144,-1144,-1144,-1144,-1144,-1144,-1144,-1144, + -1144,-1144,-1144,-1144,-1144,-1144,-1144,-1144,-1144,-1144, + -1144,-1144,-1144,-1144,-1144,-1144,-1144,-1144,-1144,-1144, + -1144,-1144, 1145,-1144,-1144,-1144,-1144,-1144,-1144,-1144, + -1144,-1144,-1144,-1144,-1144,-1144,-1144,-1144,-1144,-1144, + -1144,-1144,-1144,-1144,-1144,-1144,-1144,-1144,-1144,-1144, + -1144,-1144,-1144,-1144,-1144,-1144,-1144,-1144,-1144,-1144, + -1144,-1144,-1144,-1144,-1144,-1144,-1144,-1144,-1144,-1144, + + -1144,-1144,-1144,-1144,-1144,-1144,-1144,-1144,-1144,-1144, + -1144,-1144,-1144,-1144,-1144,-1144,-1144,-1144,-1144,-1144, + -1144,-1144,-1144,-1144,-1144,-1144,-1144,-1144,-1144,-1144, + -1144,-1144,-1144,-1144,-1144,-1144,-1144,-1144,-1144,-1144, + -1144,-1144,-1144,-1144,-1144,-1144,-1144,-1144 + }, + + { + 55,-1145,-1145,-1145,-1145,-1145,-1145,-1145,-1145,-1145, + -1145,-1145,-1145,-1145,-1145,-1145,-1145,-1145,-1145,-1145, + -1145,-1145,-1145,-1145,-1145,-1145,-1145,-1145,-1145,-1145, + -1145,-1145, 1146,-1145,-1145,-1145,-1145,-1145,-1145,-1145, + -1145,-1145,-1145,-1145,-1145,-1145,-1145,-1145,-1145,-1145, + + -1145,-1145,-1145,-1145,-1145,-1145,-1145,-1145,-1145,-1145, + -1145,-1145,-1145,-1145,-1145,-1145,-1145,-1145,-1145,-1145, + -1145,-1145,-1145,-1145,-1145,-1145,-1145,-1145,-1145,-1145, + -1145,-1145,-1145,-1145,-1145,-1145,-1145,-1145,-1145,-1145, + -1145,-1145,-1145,-1145,-1145,-1145,-1145,-1145,-1145,-1145, + -1145,-1145,-1145,-1145,-1145,-1145,-1145,-1145,-1145,-1145, + -1145,-1145,-1145,-1145,-1145,-1145,-1145,-1145,-1145,-1145, + -1145,-1145,-1145,-1145,-1145,-1145,-1145,-1145 + }, + + { + 55,-1146,-1146,-1146,-1146,-1146,-1146,-1146,-1146,-1146, + -1146,-1146,-1146,-1146,-1146,-1146,-1146,-1146,-1146,-1146, + + -1146,-1146,-1146,-1146,-1146,-1146,-1146,-1146,-1146,-1146, + -1146,-1146, 1147,-1146,-1146,-1146,-1146,-1146,-1146,-1146, + -1146,-1146,-1146,-1146,-1146,-1146,-1146,-1146,-1146,-1146, + -1146,-1146,-1146,-1146,-1146,-1146,-1146,-1146,-1146,-1146, + -1146,-1146,-1146,-1146,-1146,-1146,-1146,-1146,-1146,-1146, + -1146,-1146,-1146,-1146,-1146,-1146,-1146,-1146,-1146,-1146, + -1146,-1146,-1146,-1146,-1146,-1146,-1146,-1146,-1146,-1146, + -1146,-1146,-1146,-1146,-1146,-1146,-1146,-1146,-1146,-1146, + -1146,-1146,-1146,-1146,-1146,-1146,-1146,-1146,-1146,-1146, + -1146,-1146,-1146,-1146,-1146,-1146,-1146,-1146,-1146,-1146, + + -1146,-1146,-1146,-1146,-1146,-1146,-1146,-1146 + }, + + { + 55,-1147,-1147,-1147,-1147,-1147,-1147,-1147,-1147,-1147, + -1147,-1147,-1147,-1147,-1147,-1147,-1147,-1147,-1147,-1147, + -1147,-1147,-1147,-1147,-1147,-1147,-1147,-1147,-1147,-1147, + -1147,-1147, 1148,-1147,-1147,-1147,-1147,-1147,-1147,-1147, + -1147,-1147,-1147,-1147,-1147,-1147,-1147,-1147,-1147,-1147, + -1147,-1147,-1147,-1147,-1147,-1147,-1147,-1147,-1147,-1147, + -1147,-1147,-1147,-1147,-1147,-1147,-1147,-1147,-1147,-1147, + -1147,-1147,-1147,-1147,-1147,-1147,-1147,-1147,-1147,-1147, + -1147,-1147,-1147,-1147,-1147,-1147,-1147,-1147,-1147,-1147, + + -1147,-1147,-1147,-1147,-1147,-1147,-1147,-1147,-1147,-1147, + -1147,-1147,-1147,-1147,-1147,-1147,-1147,-1147,-1147,-1147, + -1147,-1147,-1147,-1147,-1147,-1147,-1147,-1147,-1147,-1147, + -1147,-1147,-1147,-1147,-1147,-1147,-1147,-1147 + }, + + { + 55,-1148,-1148,-1148,-1148,-1148,-1148,-1148,-1148,-1148, + -1148,-1148,-1148,-1148,-1148,-1148,-1148,-1148,-1148,-1148, + -1148,-1148,-1148,-1148,-1148,-1148,-1148,-1148,-1148,-1148, + -1148,-1148, 1149,-1148,-1148,-1148,-1148,-1148,-1148,-1148, + -1148,-1148,-1148,-1148,-1148,-1148,-1148,-1148,-1148,-1148, + -1148,-1148,-1148,-1148,-1148,-1148,-1148,-1148,-1148,-1148, + + -1148,-1148,-1148,-1148,-1148,-1148,-1148,-1148,-1148,-1148, + -1148,-1148,-1148,-1148,-1148,-1148,-1148,-1148,-1148,-1148, + -1148,-1148,-1148,-1148,-1148,-1148,-1148,-1148,-1148,-1148, + -1148,-1148,-1148,-1148,-1148,-1148,-1148,-1148,-1148,-1148, + -1148,-1148,-1148,-1148,-1148,-1148,-1148,-1148,-1148,-1148, + -1148,-1148,-1148,-1148,-1148,-1148,-1148,-1148,-1148,-1148, + -1148,-1148,-1148,-1148,-1148,-1148,-1148,-1148 + }, + + { + 55,-1149,-1149,-1149,-1149,-1149,-1149,-1149,-1149,-1149, + -1149,-1149,-1149,-1149,-1149,-1149,-1149,-1149,-1149,-1149, + -1149,-1149,-1149,-1149,-1149,-1149,-1149,-1149,-1149,-1149, + + -1149,-1149, 1150,-1149,-1149,-1149,-1149,-1149,-1149,-1149, + -1149,-1149,-1149,-1149,-1149,-1149,-1149,-1149,-1149,-1149, + -1149,-1149,-1149,-1149,-1149,-1149,-1149,-1149,-1149,-1149, + -1149,-1149,-1149,-1149,-1149,-1149,-1149,-1149,-1149,-1149, + -1149,-1149,-1149,-1149,-1149,-1149,-1149,-1149,-1149,-1149, + -1149,-1149,-1149,-1149,-1149,-1149,-1149,-1149,-1149,-1149, + -1149,-1149,-1149,-1149,-1149,-1149,-1149,-1149,-1149,-1149, + -1149,-1149,-1149,-1149,-1149,-1149,-1149,-1149,-1149,-1149, + -1149,-1149,-1149,-1149,-1149,-1149,-1149,-1149,-1149,-1149, + -1149,-1149,-1149,-1149,-1149,-1149,-1149,-1149 + + }, + + { + 55,-1150,-1150,-1150,-1150,-1150,-1150,-1150,-1150,-1150, + -1150,-1150,-1150,-1150,-1150,-1150,-1150,-1150,-1150,-1150, + -1150,-1150,-1150,-1150,-1150,-1150,-1150,-1150,-1150,-1150, + -1150,-1150, 1151,-1150,-1150,-1150,-1150,-1150,-1150,-1150, + -1150,-1150,-1150,-1150,-1150,-1150,-1150,-1150,-1150,-1150, + -1150,-1150,-1150,-1150,-1150,-1150,-1150,-1150,-1150,-1150, + -1150,-1150,-1150,-1150,-1150,-1150,-1150,-1150,-1150,-1150, + -1150,-1150,-1150,-1150,-1150,-1150,-1150,-1150,-1150,-1150, + -1150,-1150,-1150,-1150,-1150,-1150,-1150,-1150,-1150,-1150, + -1150,-1150,-1150,-1150,-1150,-1150,-1150,-1150,-1150,-1150, + + -1150,-1150,-1150,-1150,-1150,-1150,-1150,-1150,-1150,-1150, + -1150,-1150,-1150,-1150,-1150,-1150,-1150,-1150,-1150,-1150, + -1150,-1150,-1150,-1150,-1150,-1150,-1150,-1150 + }, + + { + 55,-1151,-1151,-1151,-1151,-1151,-1151,-1151,-1151,-1151, + -1151,-1151,-1151,-1151,-1151,-1151,-1151,-1151,-1151,-1151, + -1151,-1151,-1151,-1151,-1151,-1151,-1151,-1151,-1151,-1151, + -1151,-1151, 1152,-1151,-1151,-1151,-1151,-1151,-1151,-1151, + -1151,-1151,-1151,-1151,-1151,-1151,-1151,-1151,-1151,-1151, + -1151,-1151,-1151,-1151,-1151,-1151,-1151,-1151,-1151,-1151, + -1151,-1151,-1151,-1151,-1151,-1151,-1151,-1151,-1151,-1151, + + -1151,-1151,-1151,-1151,-1151,-1151,-1151,-1151,-1151,-1151, + -1151,-1151,-1151,-1151,-1151,-1151,-1151,-1151,-1151,-1151, + -1151,-1151,-1151,-1151,-1151,-1151,-1151,-1151,-1151,-1151, + -1151,-1151,-1151,-1151,-1151,-1151,-1151,-1151,-1151,-1151, + -1151,-1151,-1151,-1151,-1151,-1151,-1151,-1151,-1151,-1151, + -1151,-1151,-1151,-1151,-1151,-1151,-1151,-1151 + }, + + { + 55,-1152,-1152,-1152,-1152,-1152,-1152,-1152,-1152,-1152, + -1152,-1152,-1152,-1152,-1152,-1152,-1152,-1152,-1152,-1152, + -1152,-1152,-1152,-1152,-1152,-1152,-1152,-1152,-1152,-1152, + -1152,-1152, 1153,-1152,-1152,-1152,-1152,-1152,-1152,-1152, + + -1152,-1152,-1152,-1152,-1152,-1152,-1152,-1152,-1152,-1152, + -1152,-1152,-1152,-1152,-1152,-1152,-1152,-1152,-1152,-1152, + -1152,-1152,-1152,-1152,-1152,-1152,-1152,-1152,-1152,-1152, + -1152,-1152,-1152,-1152,-1152,-1152,-1152,-1152,-1152,-1152, + -1152,-1152,-1152,-1152,-1152,-1152,-1152,-1152,-1152,-1152, + -1152,-1152,-1152,-1152,-1152,-1152,-1152,-1152,-1152,-1152, + -1152,-1152,-1152,-1152,-1152,-1152,-1152,-1152,-1152,-1152, + -1152,-1152,-1152,-1152,-1152,-1152,-1152,-1152,-1152,-1152, + -1152,-1152,-1152,-1152,-1152,-1152,-1152,-1152 + }, + + { + 55,-1153,-1153,-1153,-1153,-1153,-1153,-1153,-1153,-1153, + + -1153,-1153,-1153,-1153,-1153,-1153,-1153,-1153,-1153,-1153, + -1153,-1153,-1153,-1153,-1153,-1153,-1153,-1153,-1153,-1153, + -1153,-1153, 1154,-1153,-1153,-1153,-1153,-1153,-1153,-1153, + -1153,-1153,-1153,-1153,-1153,-1153,-1153,-1153,-1153,-1153, + -1153,-1153,-1153,-1153,-1153,-1153,-1153,-1153,-1153,-1153, + -1153,-1153,-1153,-1153,-1153,-1153,-1153,-1153,-1153,-1153, + -1153,-1153,-1153,-1153,-1153,-1153,-1153,-1153,-1153,-1153, + -1153,-1153,-1153,-1153,-1153,-1153,-1153,-1153,-1153,-1153, + -1153,-1153,-1153,-1153,-1153,-1153,-1153,-1153,-1153,-1153, + -1153,-1153,-1153,-1153,-1153,-1153,-1153,-1153,-1153,-1153, + + -1153,-1153,-1153,-1153,-1153,-1153,-1153,-1153,-1153,-1153, + -1153,-1153,-1153,-1153,-1153,-1153,-1153,-1153 + }, + + { + 55,-1154,-1154,-1154,-1154,-1154,-1154,-1154,-1154,-1154, + -1154,-1154,-1154,-1154,-1154,-1154,-1154,-1154,-1154,-1154, + -1154,-1154,-1154,-1154,-1154,-1154,-1154,-1154,-1154,-1154, + -1154,-1154, 1155,-1154,-1154,-1154,-1154,-1154,-1154,-1154, + -1154,-1154,-1154,-1154,-1154,-1154,-1154,-1154,-1154,-1154, + -1154,-1154,-1154,-1154,-1154,-1154,-1154,-1154,-1154,-1154, + -1154,-1154,-1154,-1154,-1154,-1154,-1154,-1154,-1154,-1154, + -1154,-1154,-1154,-1154,-1154,-1154,-1154,-1154,-1154,-1154, + + -1154,-1154,-1154,-1154,-1154,-1154,-1154,-1154,-1154,-1154, + -1154,-1154,-1154,-1154,-1154,-1154,-1154,-1154,-1154,-1154, + -1154,-1154,-1154,-1154,-1154,-1154,-1154,-1154,-1154,-1154, + -1154,-1154,-1154,-1154,-1154,-1154,-1154,-1154,-1154,-1154, + -1154,-1154,-1154,-1154,-1154,-1154,-1154,-1154 + }, + + { + 55,-1155,-1155,-1155,-1155,-1155,-1155,-1155,-1155,-1155, + -1155,-1155,-1155,-1155,-1155,-1155,-1155,-1155,-1155,-1155, + -1155,-1155,-1155,-1155,-1155,-1155,-1155,-1155,-1155,-1155, + -1155,-1155, 1156,-1155,-1155,-1155,-1155,-1155,-1155,-1155, + -1155,-1155,-1155,-1155,-1155,-1155,-1155,-1155,-1155,-1155, + + -1155,-1155,-1155,-1155,-1155,-1155,-1155,-1155,-1155,-1155, + -1155,-1155,-1155,-1155,-1155,-1155,-1155,-1155,-1155,-1155, + -1155,-1155,-1155,-1155,-1155,-1155,-1155,-1155,-1155,-1155, + -1155,-1155,-1155,-1155,-1155,-1155,-1155,-1155,-1155,-1155, + -1155,-1155,-1155,-1155,-1155,-1155,-1155,-1155,-1155,-1155, + -1155,-1155,-1155,-1155,-1155,-1155,-1155,-1155,-1155,-1155, + -1155,-1155,-1155,-1155,-1155,-1155,-1155,-1155,-1155,-1155, + -1155,-1155,-1155,-1155,-1155,-1155,-1155,-1155 + }, + + { + 55,-1156,-1156,-1156,-1156,-1156,-1156,-1156,-1156,-1156, + -1156,-1156,-1156,-1156,-1156,-1156,-1156,-1156,-1156,-1156, + + -1156,-1156,-1156,-1156,-1156,-1156,-1156,-1156,-1156,-1156, + -1156,-1156, 1157,-1156,-1156,-1156,-1156,-1156,-1156,-1156, + -1156,-1156,-1156,-1156,-1156,-1156,-1156,-1156,-1156,-1156, + -1156,-1156,-1156,-1156,-1156,-1156,-1156,-1156,-1156,-1156, + -1156,-1156,-1156,-1156,-1156,-1156,-1156,-1156,-1156,-1156, + -1156,-1156,-1156,-1156,-1156,-1156,-1156,-1156,-1156,-1156, + -1156,-1156,-1156,-1156,-1156,-1156,-1156,-1156,-1156,-1156, + -1156,-1156,-1156,-1156,-1156,-1156,-1156,-1156,-1156,-1156, + -1156,-1156,-1156,-1156,-1156,-1156,-1156,-1156,-1156,-1156, + -1156,-1156,-1156,-1156,-1156,-1156,-1156,-1156,-1156,-1156, + + -1156,-1156,-1156,-1156,-1156,-1156,-1156,-1156 + }, + + { + 55,-1157,-1157,-1157,-1157,-1157,-1157,-1157,-1157,-1157, + -1157,-1157,-1157,-1157,-1157,-1157,-1157,-1157,-1157,-1157, + -1157,-1157,-1157,-1157,-1157,-1157,-1157,-1157,-1157,-1157, + -1157,-1157, 1158,-1157,-1157,-1157,-1157,-1157,-1157,-1157, + -1157,-1157,-1157,-1157,-1157,-1157,-1157,-1157,-1157,-1157, + -1157,-1157,-1157,-1157,-1157,-1157,-1157,-1157,-1157,-1157, + -1157,-1157,-1157,-1157,-1157,-1157,-1157,-1157,-1157,-1157, + -1157,-1157,-1157,-1157,-1157,-1157,-1157,-1157,-1157,-1157, + -1157,-1157,-1157,-1157,-1157,-1157,-1157,-1157,-1157,-1157, + + -1157,-1157,-1157,-1157,-1157,-1157,-1157,-1157,-1157,-1157, + -1157,-1157,-1157,-1157,-1157,-1157,-1157,-1157,-1157,-1157, + -1157,-1157,-1157,-1157,-1157,-1157,-1157,-1157,-1157,-1157, + -1157,-1157,-1157,-1157,-1157,-1157,-1157,-1157 + }, + + { + 55,-1158,-1158,-1158,-1158,-1158,-1158,-1158,-1158,-1158, + -1158,-1158,-1158,-1158,-1158,-1158,-1158,-1158,-1158,-1158, + -1158,-1158,-1158,-1158,-1158,-1158,-1158,-1158,-1158,-1158, + -1158,-1158, 1159,-1158,-1158,-1158,-1158,-1158,-1158,-1158, + -1158,-1158,-1158,-1158,-1158,-1158,-1158,-1158,-1158,-1158, + -1158,-1158,-1158,-1158,-1158,-1158,-1158,-1158,-1158,-1158, + + -1158,-1158,-1158,-1158,-1158,-1158,-1158,-1158,-1158,-1158, + -1158,-1158,-1158,-1158,-1158,-1158,-1158,-1158,-1158,-1158, + -1158,-1158,-1158,-1158,-1158,-1158,-1158,-1158,-1158,-1158, + -1158,-1158,-1158,-1158,-1158,-1158,-1158,-1158,-1158,-1158, + -1158,-1158,-1158,-1158,-1158,-1158,-1158,-1158,-1158,-1158, + -1158,-1158,-1158,-1158,-1158,-1158,-1158,-1158,-1158,-1158, + -1158,-1158,-1158,-1158,-1158,-1158,-1158,-1158 + }, + + { + 55,-1159,-1159,-1159,-1159,-1159,-1159,-1159,-1159,-1159, + -1159,-1159,-1159,-1159,-1159,-1159,-1159,-1159,-1159,-1159, + -1159,-1159,-1159,-1159,-1159,-1159,-1159,-1159,-1159,-1159, + + -1159,-1159, 1160,-1159,-1159,-1159,-1159,-1159,-1159,-1159, + -1159,-1159,-1159,-1159,-1159,-1159,-1159,-1159,-1159,-1159, + -1159,-1159,-1159,-1159,-1159,-1159,-1159,-1159,-1159,-1159, + -1159,-1159,-1159,-1159,-1159,-1159,-1159,-1159,-1159,-1159, + -1159,-1159,-1159,-1159,-1159,-1159,-1159,-1159,-1159,-1159, + -1159,-1159,-1159,-1159,-1159,-1159,-1159,-1159,-1159,-1159, + -1159,-1159,-1159,-1159,-1159,-1159,-1159,-1159,-1159,-1159, + -1159,-1159,-1159,-1159,-1159,-1159,-1159,-1159,-1159,-1159, + -1159,-1159,-1159,-1159,-1159,-1159,-1159,-1159,-1159,-1159, + -1159,-1159,-1159,-1159,-1159,-1159,-1159,-1159 + + }, + + { + 55,-1160,-1160,-1160,-1160,-1160,-1160,-1160,-1160,-1160, + -1160,-1160,-1160,-1160,-1160,-1160,-1160,-1160,-1160,-1160, + -1160,-1160,-1160,-1160,-1160,-1160,-1160,-1160,-1160,-1160, + -1160,-1160, 1161,-1160,-1160,-1160,-1160,-1160,-1160,-1160, + -1160,-1160,-1160,-1160,-1160,-1160,-1160,-1160,-1160,-1160, + -1160,-1160,-1160,-1160,-1160,-1160,-1160,-1160,-1160,-1160, + -1160,-1160,-1160,-1160,-1160,-1160,-1160,-1160,-1160,-1160, + -1160,-1160,-1160,-1160,-1160,-1160,-1160,-1160,-1160,-1160, + -1160,-1160,-1160,-1160,-1160,-1160,-1160,-1160,-1160,-1160, + -1160,-1160,-1160,-1160,-1160,-1160,-1160,-1160,-1160,-1160, + + -1160,-1160,-1160,-1160,-1160,-1160,-1160,-1160,-1160,-1160, + -1160,-1160,-1160,-1160,-1160,-1160,-1160,-1160,-1160,-1160, + -1160,-1160,-1160,-1160,-1160,-1160,-1160,-1160 + }, + + { + 55,-1161,-1161,-1161,-1161,-1161,-1161,-1161,-1161,-1161, + -1161,-1161,-1161,-1161,-1161,-1161,-1161,-1161,-1161,-1161, + -1161,-1161,-1161,-1161,-1161,-1161,-1161,-1161,-1161,-1161, + -1161,-1161, 1162,-1161,-1161,-1161,-1161,-1161,-1161,-1161, + -1161,-1161,-1161,-1161,-1161,-1161,-1161,-1161,-1161,-1161, + -1161,-1161,-1161,-1161,-1161,-1161,-1161,-1161,-1161,-1161, + -1161,-1161,-1161,-1161,-1161,-1161,-1161,-1161,-1161,-1161, + + -1161,-1161,-1161,-1161,-1161,-1161,-1161,-1161,-1161,-1161, + -1161,-1161,-1161,-1161,-1161,-1161,-1161,-1161,-1161,-1161, + -1161,-1161,-1161,-1161,-1161,-1161,-1161,-1161,-1161,-1161, + -1161,-1161,-1161,-1161,-1161,-1161,-1161,-1161,-1161,-1161, + -1161,-1161,-1161,-1161,-1161,-1161,-1161,-1161,-1161,-1161, + -1161,-1161,-1161,-1161,-1161,-1161,-1161,-1161 + }, + + { + 55,-1162,-1162,-1162,-1162,-1162,-1162,-1162,-1162,-1162, + -1162,-1162,-1162,-1162,-1162,-1162,-1162,-1162,-1162,-1162, + -1162,-1162,-1162,-1162,-1162,-1162,-1162,-1162,-1162,-1162, + -1162,-1162, 1163,-1162,-1162,-1162,-1162,-1162,-1162,-1162, + + -1162,-1162,-1162,-1162,-1162,-1162,-1162,-1162,-1162,-1162, + -1162,-1162,-1162,-1162,-1162,-1162,-1162,-1162,-1162,-1162, + -1162,-1162,-1162,-1162,-1162,-1162,-1162,-1162,-1162,-1162, + -1162,-1162,-1162,-1162,-1162,-1162,-1162,-1162,-1162,-1162, + -1162,-1162,-1162,-1162,-1162,-1162,-1162,-1162,-1162,-1162, + -1162,-1162,-1162,-1162,-1162,-1162,-1162,-1162,-1162,-1162, + -1162,-1162,-1162,-1162,-1162,-1162,-1162,-1162,-1162,-1162, + -1162,-1162,-1162,-1162,-1162,-1162,-1162,-1162,-1162,-1162, + -1162,-1162,-1162,-1162,-1162,-1162,-1162,-1162 + }, + + { + 55,-1163,-1163,-1163,-1163,-1163,-1163,-1163,-1163,-1163, + + -1163,-1163,-1163,-1163,-1163,-1163,-1163,-1163,-1163,-1163, + -1163,-1163,-1163,-1163,-1163,-1163,-1163,-1163,-1163,-1163, + -1163,-1163, 1164,-1163,-1163,-1163,-1163,-1163,-1163,-1163, + -1163,-1163,-1163,-1163,-1163,-1163,-1163,-1163,-1163,-1163, + -1163,-1163,-1163,-1163,-1163,-1163,-1163,-1163,-1163,-1163, + -1163,-1163,-1163,-1163,-1163,-1163,-1163,-1163,-1163,-1163, + -1163,-1163,-1163,-1163,-1163,-1163,-1163,-1163,-1163,-1163, + -1163,-1163,-1163,-1163,-1163,-1163,-1163,-1163,-1163,-1163, + -1163,-1163,-1163,-1163,-1163,-1163,-1163,-1163,-1163,-1163, + -1163,-1163,-1163,-1163,-1163,-1163,-1163,-1163,-1163,-1163, + + -1163,-1163,-1163,-1163,-1163,-1163,-1163,-1163,-1163,-1163, + -1163,-1163,-1163,-1163,-1163,-1163,-1163,-1163 + }, + + { + 55,-1164,-1164,-1164,-1164,-1164,-1164,-1164,-1164,-1164, + -1164,-1164,-1164,-1164,-1164,-1164,-1164,-1164,-1164,-1164, + -1164,-1164,-1164,-1164,-1164,-1164,-1164,-1164,-1164,-1164, + -1164,-1164, 1165,-1164,-1164,-1164,-1164,-1164,-1164,-1164, + -1164,-1164,-1164,-1164,-1164,-1164,-1164,-1164,-1164,-1164, + -1164,-1164,-1164,-1164,-1164,-1164,-1164,-1164,-1164,-1164, + -1164,-1164,-1164,-1164,-1164,-1164,-1164,-1164,-1164,-1164, + -1164,-1164,-1164,-1164,-1164,-1164,-1164,-1164,-1164,-1164, + + -1164,-1164,-1164,-1164,-1164,-1164,-1164,-1164,-1164,-1164, + -1164,-1164,-1164,-1164,-1164,-1164,-1164,-1164,-1164,-1164, + -1164,-1164,-1164,-1164,-1164,-1164,-1164,-1164,-1164,-1164, + -1164,-1164,-1164,-1164,-1164,-1164,-1164,-1164,-1164,-1164, + -1164,-1164,-1164,-1164,-1164,-1164,-1164,-1164 + }, + + { + 55,-1165,-1165,-1165,-1165,-1165,-1165,-1165,-1165,-1165, + -1165,-1165,-1165,-1165,-1165,-1165,-1165,-1165,-1165,-1165, + -1165,-1165,-1165,-1165,-1165,-1165,-1165,-1165,-1165,-1165, + -1165,-1165, 1166,-1165,-1165,-1165,-1165,-1165,-1165,-1165, + -1165,-1165,-1165,-1165,-1165,-1165,-1165,-1165,-1165,-1165, + + -1165,-1165,-1165,-1165,-1165,-1165,-1165,-1165,-1165,-1165, + -1165,-1165,-1165,-1165,-1165,-1165,-1165,-1165,-1165,-1165, + -1165,-1165,-1165,-1165,-1165,-1165,-1165,-1165,-1165,-1165, + -1165,-1165,-1165,-1165,-1165,-1165,-1165,-1165,-1165,-1165, + -1165,-1165,-1165,-1165,-1165,-1165,-1165,-1165,-1165,-1165, + -1165,-1165,-1165,-1165,-1165,-1165,-1165,-1165,-1165,-1165, + -1165,-1165,-1165,-1165,-1165,-1165,-1165,-1165,-1165,-1165, + -1165,-1165,-1165,-1165,-1165,-1165,-1165,-1165 + }, + + { + 55,-1166,-1166,-1166,-1166,-1166,-1166,-1166,-1166,-1166, + -1166,-1166,-1166,-1166,-1166,-1166,-1166,-1166,-1166,-1166, + + -1166,-1166,-1166,-1166,-1166,-1166,-1166,-1166,-1166,-1166, + -1166,-1166, 1167,-1166,-1166,-1166,-1166,-1166,-1166,-1166, + -1166,-1166,-1166,-1166,-1166,-1166,-1166,-1166,-1166,-1166, + -1166,-1166,-1166,-1166,-1166,-1166,-1166,-1166,-1166,-1166, + -1166,-1166,-1166,-1166,-1166,-1166,-1166,-1166,-1166,-1166, + -1166,-1166,-1166,-1166,-1166,-1166,-1166,-1166,-1166,-1166, + -1166,-1166,-1166,-1166,-1166,-1166,-1166,-1166,-1166,-1166, + -1166,-1166,-1166,-1166,-1166,-1166,-1166,-1166,-1166,-1166, + -1166,-1166,-1166,-1166,-1166,-1166,-1166,-1166,-1166,-1166, + -1166,-1166,-1166,-1166,-1166,-1166,-1166,-1166,-1166,-1166, + + -1166,-1166,-1166,-1166,-1166,-1166,-1166,-1166 + }, + + { + 55,-1167,-1167,-1167,-1167,-1167,-1167,-1167,-1167,-1167, + -1167,-1167,-1167,-1167,-1167,-1167,-1167,-1167,-1167,-1167, + -1167,-1167,-1167,-1167,-1167,-1167,-1167,-1167,-1167,-1167, + -1167,-1167, 1168,-1167,-1167,-1167,-1167,-1167,-1167,-1167, + -1167,-1167,-1167,-1167,-1167,-1167,-1167,-1167,-1167,-1167, + -1167,-1167,-1167,-1167,-1167,-1167,-1167,-1167,-1167,-1167, + -1167,-1167,-1167,-1167,-1167,-1167,-1167,-1167,-1167,-1167, + -1167,-1167,-1167,-1167,-1167,-1167,-1167,-1167,-1167,-1167, + -1167,-1167,-1167,-1167,-1167,-1167,-1167,-1167,-1167,-1167, + + -1167,-1167,-1167,-1167,-1167,-1167,-1167,-1167,-1167,-1167, + -1167,-1167,-1167,-1167,-1167,-1167,-1167,-1167,-1167,-1167, + -1167,-1167,-1167,-1167,-1167,-1167,-1167,-1167,-1167,-1167, + -1167,-1167,-1167,-1167,-1167,-1167,-1167,-1167 + }, + + { + 55,-1168,-1168,-1168,-1168,-1168,-1168,-1168,-1168,-1168, + -1168,-1168,-1168,-1168,-1168,-1168,-1168,-1168,-1168,-1168, + -1168,-1168,-1168,-1168,-1168,-1168,-1168,-1168,-1168,-1168, + -1168,-1168, 1169,-1168,-1168,-1168,-1168,-1168,-1168,-1168, + -1168,-1168,-1168,-1168,-1168,-1168,-1168,-1168,-1168,-1168, + -1168,-1168,-1168,-1168,-1168,-1168,-1168,-1168,-1168,-1168, + + -1168,-1168,-1168,-1168,-1168,-1168,-1168,-1168,-1168,-1168, + -1168,-1168,-1168,-1168,-1168,-1168,-1168,-1168,-1168,-1168, + -1168,-1168,-1168,-1168,-1168,-1168,-1168,-1168,-1168,-1168, + -1168,-1168,-1168,-1168,-1168,-1168,-1168,-1168,-1168,-1168, + -1168,-1168,-1168,-1168,-1168,-1168,-1168,-1168,-1168,-1168, + -1168,-1168,-1168,-1168,-1168,-1168,-1168,-1168,-1168,-1168, + -1168,-1168,-1168,-1168,-1168,-1168,-1168,-1168 + }, + + { + 55,-1169,-1169,-1169,-1169,-1169,-1169,-1169,-1169,-1169, + -1169,-1169,-1169,-1169,-1169,-1169,-1169,-1169,-1169,-1169, + -1169,-1169,-1169,-1169,-1169,-1169,-1169,-1169,-1169,-1169, + + -1169,-1169, 1170,-1169,-1169,-1169,-1169,-1169,-1169,-1169, + -1169,-1169,-1169,-1169,-1169,-1169,-1169,-1169,-1169,-1169, + -1169,-1169,-1169,-1169,-1169,-1169,-1169,-1169,-1169,-1169, + -1169,-1169,-1169,-1169,-1169,-1169,-1169,-1169,-1169,-1169, + -1169,-1169,-1169,-1169,-1169,-1169,-1169,-1169,-1169,-1169, + -1169,-1169,-1169,-1169,-1169,-1169,-1169,-1169,-1169,-1169, + -1169,-1169,-1169,-1169,-1169,-1169,-1169,-1169,-1169,-1169, + -1169,-1169,-1169,-1169,-1169,-1169,-1169,-1169,-1169,-1169, + -1169,-1169,-1169,-1169,-1169,-1169,-1169,-1169,-1169,-1169, + -1169,-1169,-1169,-1169,-1169,-1169,-1169,-1169 + + }, + + { + 55,-1170,-1170,-1170,-1170,-1170,-1170,-1170,-1170,-1170, + -1170,-1170,-1170,-1170,-1170,-1170,-1170,-1170,-1170,-1170, + -1170,-1170,-1170,-1170,-1170,-1170,-1170,-1170,-1170,-1170, + -1170,-1170, 1171,-1170,-1170,-1170,-1170,-1170,-1170,-1170, + -1170,-1170,-1170,-1170,-1170,-1170,-1170,-1170,-1170,-1170, + -1170,-1170,-1170,-1170,-1170,-1170,-1170,-1170,-1170,-1170, + -1170,-1170,-1170,-1170,-1170,-1170,-1170,-1170,-1170,-1170, + -1170,-1170,-1170,-1170,-1170,-1170,-1170,-1170,-1170,-1170, + -1170,-1170,-1170,-1170,-1170,-1170,-1170,-1170,-1170,-1170, + -1170,-1170,-1170,-1170,-1170,-1170,-1170,-1170,-1170,-1170, + + -1170,-1170,-1170,-1170,-1170,-1170,-1170,-1170,-1170,-1170, + -1170,-1170,-1170,-1170,-1170,-1170,-1170,-1170,-1170,-1170, + -1170,-1170,-1170,-1170,-1170,-1170,-1170,-1170 + }, + + { + 55,-1171,-1171,-1171,-1171,-1171,-1171,-1171,-1171,-1171, + -1171,-1171,-1171,-1171,-1171,-1171,-1171,-1171,-1171,-1171, + -1171,-1171,-1171,-1171,-1171,-1171,-1171,-1171,-1171,-1171, + -1171,-1171, 1172,-1171,-1171,-1171,-1171,-1171,-1171,-1171, + -1171,-1171,-1171,-1171,-1171,-1171,-1171,-1171,-1171,-1171, + -1171,-1171,-1171,-1171,-1171,-1171,-1171,-1171,-1171,-1171, + -1171,-1171,-1171,-1171,-1171,-1171,-1171,-1171,-1171,-1171, + + -1171,-1171,-1171,-1171,-1171,-1171,-1171,-1171,-1171,-1171, + -1171,-1171,-1171,-1171,-1171,-1171,-1171,-1171,-1171,-1171, + -1171,-1171,-1171,-1171,-1171,-1171,-1171,-1171,-1171,-1171, + -1171,-1171,-1171,-1171,-1171,-1171,-1171,-1171,-1171,-1171, + -1171,-1171,-1171,-1171,-1171,-1171,-1171,-1171,-1171,-1171, + -1171,-1171,-1171,-1171,-1171,-1171,-1171,-1171 + }, + + { + 55,-1172,-1172,-1172,-1172,-1172,-1172,-1172,-1172,-1172, + -1172,-1172,-1172,-1172,-1172,-1172,-1172,-1172,-1172,-1172, + -1172,-1172,-1172,-1172,-1172,-1172,-1172,-1172,-1172,-1172, + -1172,-1172, 1173,-1172,-1172,-1172,-1172,-1172,-1172,-1172, + + -1172,-1172,-1172,-1172,-1172,-1172,-1172,-1172,-1172,-1172, + -1172,-1172,-1172,-1172,-1172,-1172,-1172,-1172,-1172,-1172, + -1172,-1172,-1172,-1172,-1172,-1172,-1172,-1172,-1172,-1172, + -1172,-1172,-1172,-1172,-1172,-1172,-1172,-1172,-1172,-1172, + -1172,-1172,-1172,-1172,-1172,-1172,-1172,-1172,-1172,-1172, + -1172,-1172,-1172,-1172,-1172,-1172,-1172,-1172,-1172,-1172, + -1172,-1172,-1172,-1172,-1172,-1172,-1172,-1172,-1172,-1172, + -1172,-1172,-1172,-1172,-1172,-1172,-1172,-1172,-1172,-1172, + -1172,-1172,-1172,-1172,-1172,-1172,-1172,-1172 + }, + + { + 55,-1173,-1173,-1173,-1173,-1173,-1173,-1173,-1173,-1173, + + -1173,-1173,-1173,-1173,-1173,-1173,-1173,-1173,-1173,-1173, + -1173,-1173,-1173,-1173,-1173,-1173,-1173,-1173,-1173,-1173, + -1173,-1173, 1174,-1173,-1173,-1173,-1173,-1173,-1173,-1173, + -1173,-1173,-1173,-1173,-1173,-1173,-1173,-1173,-1173,-1173, + -1173,-1173,-1173,-1173,-1173,-1173,-1173,-1173,-1173,-1173, + -1173,-1173,-1173,-1173,-1173,-1173,-1173,-1173,-1173,-1173, + -1173,-1173,-1173,-1173,-1173,-1173,-1173,-1173,-1173,-1173, + -1173,-1173,-1173,-1173,-1173,-1173,-1173,-1173,-1173,-1173, + -1173,-1173,-1173,-1173,-1173,-1173,-1173,-1173,-1173,-1173, + -1173,-1173,-1173,-1173,-1173,-1173,-1173,-1173,-1173,-1173, + + -1173,-1173,-1173,-1173,-1173,-1173,-1173,-1173,-1173,-1173, + -1173,-1173,-1173,-1173,-1173,-1173,-1173,-1173 + }, + + { + 55,-1174,-1174,-1174,-1174,-1174,-1174,-1174,-1174,-1174, + -1174,-1174,-1174,-1174,-1174,-1174,-1174,-1174,-1174,-1174, + -1174,-1174,-1174,-1174,-1174,-1174,-1174,-1174,-1174,-1174, + -1174,-1174, 1175,-1174,-1174,-1174,-1174,-1174,-1174,-1174, + -1174,-1174,-1174,-1174,-1174,-1174,-1174,-1174,-1174,-1174, + -1174,-1174,-1174,-1174,-1174,-1174,-1174,-1174,-1174,-1174, + -1174,-1174,-1174,-1174,-1174,-1174,-1174,-1174,-1174,-1174, + -1174,-1174,-1174,-1174,-1174,-1174,-1174,-1174,-1174,-1174, + + -1174,-1174,-1174,-1174,-1174,-1174,-1174,-1174,-1174,-1174, + -1174,-1174,-1174,-1174,-1174,-1174,-1174,-1174,-1174,-1174, + -1174,-1174,-1174,-1174,-1174,-1174,-1174,-1174,-1174,-1174, + -1174,-1174,-1174,-1174,-1174,-1174,-1174,-1174,-1174,-1174, + -1174,-1174,-1174,-1174,-1174,-1174,-1174,-1174 + }, + + { + 55,-1175,-1175,-1175,-1175,-1175,-1175,-1175,-1175,-1175, + -1175,-1175,-1175,-1175,-1175,-1175,-1175,-1175,-1175,-1175, + -1175,-1175,-1175,-1175,-1175,-1175,-1175,-1175,-1175,-1175, + -1175,-1175, 1176,-1175,-1175,-1175,-1175,-1175,-1175,-1175, + -1175,-1175,-1175,-1175,-1175,-1175,-1175,-1175,-1175,-1175, + + -1175,-1175,-1175,-1175,-1175,-1175,-1175,-1175,-1175,-1175, + -1175,-1175,-1175,-1175,-1175,-1175,-1175,-1175,-1175,-1175, + -1175,-1175,-1175,-1175,-1175,-1175,-1175,-1175,-1175,-1175, + -1175,-1175,-1175,-1175,-1175,-1175,-1175,-1175,-1175,-1175, + -1175,-1175,-1175,-1175,-1175,-1175,-1175,-1175,-1175,-1175, + -1175,-1175,-1175,-1175,-1175,-1175,-1175,-1175,-1175,-1175, + -1175,-1175,-1175,-1175,-1175,-1175,-1175,-1175,-1175,-1175, + -1175,-1175,-1175,-1175,-1175,-1175,-1175,-1175 + }, + + { + 55,-1176,-1176,-1176,-1176,-1176,-1176,-1176,-1176,-1176, + -1176,-1176,-1176,-1176,-1176,-1176,-1176,-1176,-1176,-1176, + + -1176,-1176,-1176,-1176,-1176,-1176,-1176,-1176,-1176,-1176, + -1176,-1176, 1177,-1176,-1176,-1176,-1176,-1176,-1176,-1176, + -1176,-1176,-1176,-1176,-1176,-1176,-1176,-1176,-1176,-1176, + -1176,-1176,-1176,-1176,-1176,-1176,-1176,-1176,-1176,-1176, + -1176,-1176,-1176,-1176,-1176,-1176,-1176,-1176,-1176,-1176, + -1176,-1176,-1176,-1176,-1176,-1176,-1176,-1176,-1176,-1176, + -1176,-1176,-1176,-1176,-1176,-1176,-1176,-1176,-1176,-1176, + -1176,-1176,-1176,-1176,-1176,-1176,-1176,-1176,-1176,-1176, + -1176,-1176,-1176,-1176,-1176,-1176,-1176,-1176,-1176,-1176, + -1176,-1176,-1176,-1176,-1176,-1176,-1176,-1176,-1176,-1176, + + -1176,-1176,-1176,-1176,-1176,-1176,-1176,-1176 + }, + + { + 55,-1177,-1177,-1177,-1177,-1177,-1177,-1177,-1177,-1177, + -1177,-1177,-1177,-1177,-1177,-1177,-1177,-1177,-1177,-1177, + -1177,-1177,-1177,-1177,-1177,-1177,-1177,-1177,-1177,-1177, + -1177,-1177, 1178,-1177,-1177,-1177,-1177,-1177,-1177,-1177, + -1177,-1177,-1177,-1177,-1177,-1177,-1177,-1177,-1177,-1177, + -1177,-1177,-1177,-1177,-1177,-1177,-1177,-1177,-1177,-1177, + -1177,-1177,-1177,-1177,-1177,-1177,-1177,-1177,-1177,-1177, + -1177,-1177,-1177,-1177,-1177,-1177,-1177,-1177,-1177,-1177, + -1177,-1177,-1177,-1177,-1177,-1177,-1177,-1177,-1177,-1177, + + -1177,-1177,-1177,-1177,-1177,-1177,-1177,-1177,-1177,-1177, + -1177,-1177,-1177,-1177,-1177,-1177,-1177,-1177,-1177,-1177, + -1177,-1177,-1177,-1177,-1177,-1177,-1177,-1177,-1177,-1177, + -1177,-1177,-1177,-1177,-1177,-1177,-1177,-1177 + }, + + { + 55,-1178,-1178,-1178,-1178,-1178,-1178,-1178,-1178,-1178, + -1178,-1178,-1178,-1178,-1178,-1178,-1178,-1178,-1178,-1178, + -1178,-1178,-1178,-1178,-1178,-1178,-1178,-1178,-1178,-1178, + -1178,-1178, 1179,-1178,-1178,-1178,-1178,-1178,-1178,-1178, + -1178,-1178,-1178,-1178,-1178,-1178,-1178,-1178,-1178,-1178, + -1178,-1178,-1178,-1178,-1178,-1178,-1178,-1178,-1178,-1178, + + -1178,-1178,-1178,-1178,-1178,-1178,-1178,-1178,-1178,-1178, + -1178,-1178,-1178,-1178,-1178,-1178,-1178,-1178,-1178,-1178, + -1178,-1178,-1178,-1178,-1178,-1178,-1178,-1178,-1178,-1178, + -1178,-1178,-1178,-1178,-1178,-1178,-1178,-1178,-1178,-1178, + -1178,-1178,-1178,-1178,-1178,-1178,-1178,-1178,-1178,-1178, + -1178,-1178,-1178,-1178,-1178,-1178,-1178,-1178,-1178,-1178, + -1178,-1178,-1178,-1178,-1178,-1178,-1178,-1178 + }, + + { + 55,-1179,-1179,-1179,-1179,-1179,-1179,-1179,-1179,-1179, + -1179,-1179,-1179,-1179,-1179,-1179,-1179,-1179,-1179,-1179, + -1179,-1179,-1179,-1179,-1179,-1179,-1179,-1179,-1179,-1179, + + -1179,-1179, 1180,-1179,-1179,-1179,-1179,-1179,-1179,-1179, + -1179,-1179,-1179,-1179,-1179,-1179,-1179,-1179,-1179,-1179, + -1179,-1179,-1179,-1179,-1179,-1179,-1179,-1179,-1179,-1179, + -1179,-1179,-1179,-1179,-1179,-1179,-1179,-1179,-1179,-1179, + -1179,-1179,-1179,-1179,-1179,-1179,-1179,-1179,-1179,-1179, + -1179,-1179,-1179,-1179,-1179,-1179,-1179,-1179,-1179,-1179, + -1179,-1179,-1179,-1179,-1179,-1179,-1179,-1179,-1179,-1179, + -1179,-1179,-1179,-1179,-1179,-1179,-1179,-1179,-1179,-1179, + -1179,-1179,-1179,-1179,-1179,-1179,-1179,-1179,-1179,-1179, + -1179,-1179,-1179,-1179,-1179,-1179,-1179,-1179 + + }, + + { + 55,-1180,-1180,-1180,-1180,-1180,-1180,-1180,-1180,-1180, + -1180,-1180,-1180,-1180,-1180,-1180,-1180,-1180,-1180,-1180, + -1180,-1180,-1180,-1180,-1180,-1180,-1180,-1180,-1180,-1180, + -1180,-1180, 1181,-1180,-1180,-1180,-1180,-1180,-1180,-1180, + -1180,-1180,-1180,-1180,-1180,-1180,-1180,-1180,-1180,-1180, + -1180,-1180,-1180,-1180,-1180,-1180,-1180,-1180,-1180,-1180, + -1180,-1180,-1180,-1180,-1180,-1180,-1180,-1180,-1180,-1180, + -1180,-1180,-1180,-1180,-1180,-1180,-1180,-1180,-1180,-1180, + -1180,-1180,-1180,-1180,-1180,-1180,-1180,-1180,-1180,-1180, + -1180,-1180,-1180,-1180,-1180,-1180,-1180,-1180,-1180,-1180, + + -1180,-1180,-1180,-1180,-1180,-1180,-1180,-1180,-1180,-1180, + -1180,-1180,-1180,-1180,-1180,-1180,-1180,-1180,-1180,-1180, + -1180,-1180,-1180,-1180,-1180,-1180,-1180,-1180 + }, + + { + 55,-1181,-1181,-1181,-1181,-1181,-1181,-1181,-1181,-1181, + -1181,-1181,-1181,-1181,-1181,-1181,-1181,-1181,-1181,-1181, + -1181,-1181,-1181,-1181,-1181,-1181,-1181,-1181,-1181,-1181, + -1181,-1181, 1182,-1181,-1181,-1181,-1181,-1181,-1181,-1181, + -1181,-1181,-1181,-1181,-1181,-1181,-1181,-1181,-1181,-1181, + -1181,-1181,-1181,-1181,-1181,-1181,-1181,-1181,-1181,-1181, + -1181,-1181,-1181,-1181,-1181,-1181,-1181,-1181,-1181,-1181, + + -1181,-1181,-1181,-1181,-1181,-1181,-1181,-1181,-1181,-1181, + -1181,-1181,-1181,-1181,-1181,-1181,-1181,-1181,-1181,-1181, + -1181,-1181,-1181,-1181,-1181,-1181,-1181,-1181,-1181,-1181, + -1181,-1181,-1181,-1181,-1181,-1181,-1181,-1181,-1181,-1181, + -1181,-1181,-1181,-1181,-1181,-1181,-1181,-1181,-1181,-1181, + -1181,-1181,-1181,-1181,-1181,-1181,-1181,-1181 + }, + + { + 55,-1182,-1182,-1182,-1182,-1182,-1182,-1182,-1182,-1182, + -1182,-1182,-1182,-1182,-1182,-1182,-1182,-1182,-1182,-1182, + -1182,-1182,-1182,-1182,-1182,-1182,-1182,-1182,-1182,-1182, + -1182,-1182, 1183,-1182,-1182,-1182,-1182,-1182,-1182,-1182, + + -1182,-1182,-1182,-1182,-1182,-1182,-1182,-1182,-1182,-1182, + -1182,-1182,-1182,-1182,-1182,-1182,-1182,-1182,-1182,-1182, + -1182,-1182,-1182,-1182,-1182,-1182,-1182,-1182,-1182,-1182, + -1182,-1182,-1182,-1182,-1182,-1182,-1182,-1182,-1182,-1182, + -1182,-1182,-1182,-1182,-1182,-1182,-1182,-1182,-1182,-1182, + -1182,-1182,-1182,-1182,-1182,-1182,-1182,-1182,-1182,-1182, + -1182,-1182,-1182,-1182,-1182,-1182,-1182,-1182,-1182,-1182, + -1182,-1182,-1182,-1182,-1182,-1182,-1182,-1182,-1182,-1182, + -1182,-1182,-1182,-1182,-1182,-1182,-1182,-1182 + }, + + { + 55,-1183,-1183,-1183,-1183,-1183,-1183,-1183,-1183,-1183, + + -1183,-1183,-1183,-1183,-1183,-1183,-1183,-1183,-1183,-1183, + -1183,-1183,-1183,-1183,-1183,-1183,-1183,-1183,-1183,-1183, + -1183,-1183, 1184,-1183,-1183,-1183,-1183,-1183,-1183,-1183, + -1183,-1183,-1183,-1183,-1183,-1183,-1183,-1183,-1183,-1183, + -1183,-1183,-1183,-1183,-1183,-1183,-1183,-1183,-1183,-1183, + -1183,-1183,-1183,-1183,-1183,-1183,-1183,-1183,-1183,-1183, + -1183,-1183,-1183,-1183,-1183,-1183,-1183,-1183,-1183,-1183, + -1183,-1183,-1183,-1183,-1183,-1183,-1183,-1183,-1183,-1183, + -1183,-1183,-1183,-1183,-1183,-1183,-1183,-1183,-1183,-1183, + -1183,-1183,-1183,-1183,-1183,-1183,-1183,-1183,-1183,-1183, + + -1183,-1183,-1183,-1183,-1183,-1183,-1183,-1183,-1183,-1183, + -1183,-1183,-1183,-1183,-1183,-1183,-1183,-1183 + }, + + { + 55,-1184,-1184,-1184,-1184,-1184,-1184,-1184,-1184,-1184, + -1184,-1184,-1184,-1184,-1184,-1184,-1184,-1184,-1184,-1184, + -1184,-1184,-1184,-1184,-1184,-1184,-1184,-1184,-1184,-1184, + -1184,-1184, 1185,-1184,-1184,-1184,-1184,-1184,-1184,-1184, + -1184,-1184,-1184,-1184,-1184,-1184,-1184,-1184,-1184,-1184, + -1184,-1184,-1184,-1184,-1184,-1184,-1184,-1184,-1184,-1184, + -1184,-1184,-1184,-1184,-1184,-1184,-1184,-1184,-1184,-1184, + -1184,-1184,-1184,-1184,-1184,-1184,-1184,-1184,-1184,-1184, + + -1184,-1184,-1184,-1184,-1184,-1184,-1184,-1184,-1184,-1184, + -1184,-1184,-1184,-1184,-1184,-1184,-1184,-1184,-1184,-1184, + -1184,-1184,-1184,-1184,-1184,-1184,-1184,-1184,-1184,-1184, + -1184,-1184,-1184,-1184,-1184,-1184,-1184,-1184,-1184,-1184, + -1184,-1184,-1184,-1184,-1184,-1184,-1184,-1184 + }, + + { + 55,-1185,-1185,-1185,-1185,-1185,-1185,-1185,-1185,-1185, + -1185,-1185,-1185,-1185,-1185,-1185,-1185,-1185,-1185,-1185, + -1185,-1185,-1185,-1185,-1185,-1185,-1185,-1185,-1185,-1185, + -1185,-1185, 1186,-1185,-1185,-1185,-1185,-1185,-1185,-1185, + -1185,-1185,-1185,-1185,-1185,-1185,-1185,-1185,-1185,-1185, + + -1185,-1185,-1185,-1185,-1185,-1185,-1185,-1185,-1185,-1185, + -1185,-1185,-1185,-1185,-1185,-1185,-1185,-1185,-1185,-1185, + -1185,-1185,-1185,-1185,-1185,-1185,-1185,-1185,-1185,-1185, + -1185,-1185,-1185,-1185,-1185,-1185,-1185,-1185,-1185,-1185, + -1185,-1185,-1185,-1185,-1185,-1185,-1185,-1185,-1185,-1185, + -1185,-1185,-1185,-1185,-1185,-1185,-1185,-1185,-1185,-1185, + -1185,-1185,-1185,-1185,-1185,-1185,-1185,-1185,-1185,-1185, + -1185,-1185,-1185,-1185,-1185,-1185,-1185,-1185 + }, + + { + 55,-1186,-1186,-1186,-1186,-1186,-1186,-1186,-1186,-1186, + -1186,-1186,-1186,-1186,-1186,-1186,-1186,-1186,-1186,-1186, + + -1186,-1186,-1186,-1186,-1186,-1186,-1186,-1186,-1186,-1186, + -1186,-1186, 1187,-1186,-1186,-1186,-1186,-1186,-1186,-1186, + -1186,-1186,-1186,-1186,-1186,-1186,-1186,-1186,-1186,-1186, + -1186,-1186,-1186,-1186,-1186,-1186,-1186,-1186,-1186,-1186, + -1186,-1186,-1186,-1186,-1186,-1186,-1186,-1186,-1186,-1186, + -1186,-1186,-1186,-1186,-1186,-1186,-1186,-1186,-1186,-1186, + -1186,-1186,-1186,-1186,-1186,-1186,-1186,-1186,-1186,-1186, + -1186,-1186,-1186,-1186,-1186,-1186,-1186,-1186,-1186,-1186, + -1186,-1186,-1186,-1186,-1186,-1186,-1186,-1186,-1186,-1186, + -1186,-1186,-1186,-1186,-1186,-1186,-1186,-1186,-1186,-1186, + + -1186,-1186,-1186,-1186,-1186,-1186,-1186,-1186 + }, + + { + 55,-1187,-1187,-1187,-1187,-1187,-1187,-1187,-1187,-1187, + -1187,-1187,-1187,-1187,-1187,-1187,-1187,-1187,-1187,-1187, + -1187,-1187,-1187,-1187,-1187,-1187,-1187,-1187,-1187,-1187, + -1187,-1187, 1188,-1187,-1187,-1187,-1187,-1187,-1187,-1187, + -1187,-1187,-1187,-1187,-1187,-1187,-1187,-1187,-1187,-1187, + -1187,-1187,-1187,-1187,-1187,-1187,-1187,-1187,-1187,-1187, + -1187,-1187,-1187,-1187,-1187,-1187,-1187,-1187,-1187,-1187, + -1187,-1187,-1187,-1187,-1187,-1187,-1187,-1187,-1187,-1187, + -1187,-1187,-1187,-1187,-1187,-1187,-1187,-1187,-1187,-1187, + + -1187,-1187,-1187,-1187,-1187,-1187,-1187,-1187,-1187,-1187, + -1187,-1187,-1187,-1187,-1187,-1187,-1187,-1187,-1187,-1187, + -1187,-1187,-1187,-1187,-1187,-1187,-1187,-1187,-1187,-1187, + -1187,-1187,-1187,-1187,-1187,-1187,-1187,-1187 + }, + + { + 55,-1188,-1188,-1188,-1188,-1188,-1188,-1188,-1188,-1188, + -1188,-1188,-1188,-1188,-1188,-1188,-1188,-1188,-1188,-1188, + -1188,-1188,-1188,-1188,-1188,-1188,-1188,-1188,-1188,-1188, + -1188,-1188, 1189,-1188,-1188,-1188,-1188,-1188,-1188,-1188, + -1188,-1188,-1188,-1188,-1188,-1188,-1188,-1188,-1188,-1188, + -1188,-1188,-1188,-1188,-1188,-1188,-1188,-1188,-1188,-1188, + + -1188,-1188,-1188,-1188,-1188,-1188,-1188,-1188,-1188,-1188, + -1188,-1188,-1188,-1188,-1188,-1188,-1188,-1188,-1188,-1188, + -1188,-1188,-1188,-1188,-1188,-1188,-1188,-1188,-1188,-1188, + -1188,-1188,-1188,-1188,-1188,-1188,-1188,-1188,-1188,-1188, + -1188,-1188,-1188,-1188,-1188,-1188,-1188,-1188,-1188,-1188, + -1188,-1188,-1188,-1188,-1188,-1188,-1188,-1188,-1188,-1188, + -1188,-1188,-1188,-1188,-1188,-1188,-1188,-1188 + }, + + { + 55,-1189,-1189,-1189,-1189,-1189,-1189,-1189,-1189,-1189, + -1189,-1189,-1189,-1189,-1189,-1189,-1189,-1189,-1189,-1189, + -1189,-1189,-1189,-1189,-1189,-1189,-1189,-1189,-1189,-1189, + + -1189,-1189, 1190,-1189,-1189,-1189,-1189,-1189,-1189,-1189, + -1189,-1189,-1189,-1189,-1189,-1189,-1189,-1189,-1189,-1189, + -1189,-1189,-1189,-1189,-1189,-1189,-1189,-1189,-1189,-1189, + -1189,-1189,-1189,-1189,-1189,-1189,-1189,-1189,-1189,-1189, + -1189,-1189,-1189,-1189,-1189,-1189,-1189,-1189,-1189,-1189, + -1189,-1189,-1189,-1189,-1189,-1189,-1189,-1189,-1189,-1189, + -1189,-1189,-1189,-1189,-1189,-1189,-1189,-1189,-1189,-1189, + -1189,-1189,-1189,-1189,-1189,-1189,-1189,-1189,-1189,-1189, + -1189,-1189,-1189,-1189,-1189,-1189,-1189,-1189,-1189,-1189, + -1189,-1189,-1189,-1189,-1189,-1189,-1189,-1189 + + }, + + { + 55,-1190,-1190,-1190,-1190,-1190,-1190,-1190,-1190,-1190, + -1190,-1190,-1190,-1190,-1190,-1190,-1190,-1190,-1190,-1190, + -1190,-1190,-1190,-1190,-1190,-1190,-1190,-1190,-1190,-1190, + -1190,-1190,-1190,-1190,-1190,-1190,-1190,-1190,-1190,-1190, + -1190,-1190,-1190,-1190,-1190,-1190,-1190,-1190,-1190,-1190, + -1190,-1190,-1190,-1190,-1190,-1190,-1190,-1190,-1190,-1190, + -1190,-1190,-1190,-1190,-1190,-1190,-1190,-1190,-1190,-1190, + -1190,-1190,-1190,-1190,-1190,-1190,-1190,-1190,-1190,-1190, + -1190,-1190,-1190,-1190,-1190,-1190,-1190,-1190,-1190,-1190, + -1190,-1190,-1190,-1190,-1190,-1190,-1190,-1190,-1190,-1190, + + -1190,-1190,-1190,-1190,-1190,-1190,-1190,-1190,-1190,-1190, + -1190,-1190,-1190,-1190,-1190,-1190,-1190,-1190,-1190,-1190, + -1190,-1190,-1190,-1190,-1190,-1190,-1190,-1190 + }, + + } ; + +static yy_state_type yy_get_previous_state ( yyscan_t yyscanner ); +static yy_state_type yy_try_NUL_trans ( yy_state_type current_state , yyscan_t yyscanner); +static int yy_get_next_buffer ( yyscan_t yyscanner ); +static void yynoreturn yy_fatal_error ( const char* msg , yyscan_t yyscanner ); + +/* Done after the current pattern has been matched and before the + * corresponding action - sets up yytext. + */ +#define YY_DO_BEFORE_ACTION \ + yyg->yytext_ptr = yy_bp; \ + yyleng = (int) (yy_cp - yy_bp); \ + yyg->yy_hold_char = *yy_cp; \ + *yy_cp = '\0'; \ + yyg->yy_c_buf_p = yy_cp; +#define YY_NUM_RULES 293 +#define YY_END_OF_BUFFER 294 +/* This struct is not used in this scanner, + but its presence is necessary. */ +struct yy_trans_info + { + flex_int32_t yy_verify; + flex_int32_t yy_nxt; + }; +static const flex_int16_t yy_accept[1191] = + { 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, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 294, 293, 129, 129, 129, 129, + 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, + 129, 129, 129, 129, 129, 129, 129, 150, 150, 150, + 198, 198, 198, 151, 151, 151, 247, 247, 247, 201, + 199, 200, 251, 251, 255, 255, 258, 258, 259, 259, + + 262, 262, 264, 264, 266, 266, 268, 268, 267, 270, + 270, 270, 269, 272, 272, 272, 271, 274, 274, 276, + 276, 278, 277, 280, 280, 283, 283, 283, 281, 284, + 293, 285, 293, 293, 293, 290, 293, 291, 293, 292, + 0, 0, 107, 0, 0, 0, 0, 108, 5, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, + 97, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 4, 0, 0, 0, 18, 16, + 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, + 0, 0, 0, 0, 0, 0, 0, 265, 267, 0, + 269, 269, 269, 269, 0, 0, 271, 271, 271, 271, + 0, 0, 273, 0, 275, 277, 279, 0, 281, 282, + 282, 281, 0, 0, 287, 0, 0, 0, 285, 0, + 0, 0, 285, 0, 0, 0, 290, 0, 291, 0, + 292, 0, 109, 0, 0, 0, 0, 0, 0, 0, + 110, 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, 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, + 147, 148, 149, 140, 139, 132, 133, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 248, + 249, 250, 252, 253, 254, 0, 0, 0, 0, 0, + 269, 0, 269, 271, 0, 271, 282, 0, 282, 0, + + 0, 286, 0, 0, 0, 288, 0, 0, 0, 285, + 0, 0, 125, 126, 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, 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, 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, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 260, 261, 0, + 263, 289, 0, 0, 0, 286, 0, 0, 0, 285, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 6, 20, 0, 94, 24, 98, + 95, 99, 21, 0, 0, 7, 3, 10, 22, 9, + 8, 23, 0, 0, 0, 0, 100, 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, 17, 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, 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, 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, 257, 0, 286, 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, 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, 0, + 0, 0, 0, 0, 0, 141, 142, 143, 144, 145, + 146, 138, 137, 136, 135, 134, 130, 131, 187, 189, + 192, 196, 188, 191, 195, 190, 194, 193, 183, 161, + 180, 184, 197, 172, 162, 158, 167, 178, 181, 185, + 173, 170, 175, 163, 159, 168, 156, 165, 177, 179, + 182, 186, 174, 171, 176, 154, 155, 164, 160, 169, + 157, 166, 152, 153, 237, 239, 242, 246, 238, 241, + 245, 240, 244, 243, 233, 211, 230, 234, 222, 208, + + 212, 217, 228, 231, 235, 220, 223, 225, 206, 209, + 213, 218, 215, 227, 229, 232, 236, 204, 221, 224, + 226, 205, 202, 207, 210, 214, 219, 216, 203, 256, + 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, 74, 0, 0, 0, + 44, 42, 0, 0, 0, 0, 12, 11, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 71, 0, 13, 15, 0, 75, 76, 80, 0, 0, + + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 81, 77, 0, 0, 0, 0, 25, 0, 0, 0, + 79, 103, 105, 101, 88, 93, 55, 92, 91, 104, + 106, 102, 89, 111, 112, 84, 86, 90, 48, 47, + 49, 46, 32, 31, 83, 0, 73, 85, 87, 40, + 39, 43, 41, 54, 52, 51, 53, 50, 34, 38, + 36, 33, 37, 35, 0, 68, 69, 67, 64, 65, + 66, 70, 124, 29, 121, 122, 123, 120, 117, 118, + 119, 113, 114, 72, 14, 82, 59, 62, 45, 63, + 26, 30, 61, 60, 28, 27, 56, 57, 19, 78, + + 127, 0, 115, 58, 116, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, 0, 2, 0, 1, + 1, 0, 1, 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, 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, 128 + } ; + +static const yy_state_type yy_NUL_trans[1191] = + { 0, + 56, 57, 78, 78, 81, 81, 84, 84, 87, 87, + 90, 90, 92, 92, 93, 93, 95, 95, 97, 97, + 99, 99, 101, 101, 103, 103, 105, 105, 107, 107, + 110, 110, 114, 114, 118, 118, 120, 120, 122, 122, + 124, 124, 126, 126, 56, 56, 131, 131, 135, 135, + 137, 137, 139, 139, 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, 0, 0, 0, 0, 0, 0, + + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 242, 0, + 244, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 254, 0, 258, 262, 266, 0, 268, 0, 270, 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, 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, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 242, 0, 244, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 254, 0, 400, 401, 405, 0, 409, + 262, 262, 0, 262, 262, 266, 0, 268, 0, 270, + 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, + 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, + 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, 550, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 400, + + 401, 0, 401, 401, 405, 0, 405, 555, 405, 0, + 409, 559, 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, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 550, + 0, 0, 400, 738, 555, 0, 555, 555, 559, 0, + 559, 559, 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, 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, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 738, 0, 738, + 738, 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, 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, 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, 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, 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, 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, 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, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1123, 0, 1123, 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, 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 + } ; + +/* The intent behind this definition is that it'll catch + * any uses of REJECT which flex missed. + */ +#define REJECT reject_used_but_not_detected +#define yymore() yymore_used_but_not_detected +#define YY_MORE_ADJ 0 +#define YY_RESTORE_YY_MORE_OFFSET +#line 1 "wcspih.l" +/*============================================================================ + WCSLIB 8.6 - an implementation of the FITS WCS standard. + Copyright (C) 1995-2025, Mark Calabretta + + This file is part of WCSLIB. + + WCSLIB is free software: you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + WCSLIB is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for + more details. + + You should have received a copy of the GNU Lesser General Public License + along with WCSLIB. If not, see http://www.gnu.org/licenses. + + Author: Mark Calabretta, Australia Telescope National Facility, CSIRO. + http://www.atnf.csiro.au/computing/software/wcs + $Id: wcspih.c,v 8.6 2026/03/29 13:53:56 mcalabre Exp $ +*============================================================================= +* +* wcspih.l is a Flex description file containing the definition of a lexical +* scanner for parsing the WCS keyrecords from a FITS primary image or image +* extension header. +* +* wcspih.l requires Flex v2.5.4 or later. Refer to wcshdr.h for a description +* of the user interface and operating notes. +* +* Implementation notes +* -------------------- +* Use of the WCSAXESa keyword is not mandatory. Its default value is "the +* larger of NAXIS and the largest index of these keywords [i.e. CRPIXj, PCi_j +* or CDi_j, CDELTi, CTYPEi, CRVALi, and CUNITi] found in the FITS header". +* Consequently the definition of WCSAXESa effectively invalidates the use of +* NAXIS for determining the number of coordinate axes and forces a preliminary +* pass through the header to determine the "largest index" in headers where +* WCSAXESa was omitted. +* +* Furthermore, since the use of WCSAXESa is optional, there is no way to +* determine the number of coordinate representations (the "a" value) other +* than by parsing all of the WCS keywords in the header; even if WCSAXESa was +* specified for some representations it cannot be known in advance whether it +* was specified for all of those present in the header. +* +* Hence the definition of WCSAXESa forces the scanner to be implemented in two +* passes. The first pass is used to determine the number of coordinate +* representations (up to 27) and the number of coordinate axes in each. +* Effectively WCSAXESa is ignored unless it exceeds the "largest index" in +* which case the keywords for the extra axes assume their default values. The +* number of PVi_ma and PSi_ma keywords in each representation is also counted +* in the first pass. +* +* On completion of the first pass, memory is allocated for an array of the +* required number of wcsprm structs and each of these is initialized +* appropriately. These structs are filled in the second pass. +* +* The parser does not check for duplicated keywords, it accepts the last +* encountered. +* +*===========================================================================*/ +/* Options. */ +#define YY_NO_INPUT 1 +/* Indices for parameterized keywords. */ +/* Alternate coordinate system identifier. */ +/* Keyvalue data types. */ +/* Inline comment syntax. */ +/* Exclusive start states. */ + + + + + + +#line 110 "wcspih.l" +#include +#include +#include +#include +#include +#include + +#include "wcsmath.h" +#include "wcsprintf.h" +#include "wcsutil.h" + +#include "dis.h" +#include "wcs.h" +#include "wcshdr.h" + +#define INTEGER 0 +#define FLOAT 1 +#define FLOAT2 2 +#define STRING 3 +#define RECORD 4 + +#define PRIOR 1 +#define SEQUENT 2 + +#define SIP 1 +#define DSS 2 +#define WAT 3 + +// User data associated with yyscanner. +struct wcspih_extra { + // Values passed to YY_INPUT. + char *hdr; + int nkeyrec; + + // Used in preempting the call to exit() by yy_fatal_error(). + jmp_buf abort_jmp_env; +}; + +#define YY_DECL int wcspih_scanner(char *header, int nkeyrec, int relax, \ + int ctrl, int *nreject, int *nwcs, struct wcsprm **wcs, yyscan_t yyscanner) + +#define YY_INPUT(inbuff, count, bufsize) \ + { \ + if (yyextra->nkeyrec) { \ + strncpy(inbuff, yyextra->hdr, 80); \ + inbuff[80] = '\n'; \ + yyextra->hdr += 80; \ + yyextra->nkeyrec--; \ + count = 81; \ + } else { \ + count = YY_NULL; \ + } \ + } + +// Preempt the call to exit() by yy_fatal_error(). +#define exit(status) longjmp(yyextra->abort_jmp_env, status); + +// Internal helper functions. +static YY_DECL; +static int wcspih_final(int ndp[], int ndq[], int distran, double dsstmp[], + char *wat[], int *nwcs, struct wcsprm **wcs); +static int wcspih_init1(int naxis, int alts[], int dpq[], int npv[], + int nps[], int ndp[], int ndq[], int auxprm, int distran, + int *nwcs, struct wcsprm **wcs); +static void wcspih_pass1(int naxis, int i, int j, char a, int distype, + int alts[], int dpq[], int *npptr); + +static int wcspih_jdref(double *wptr, const double *jdref); +static int wcspih_jdrefi(double *wptr, const double *jdrefi); +static int wcspih_jdreff(double *wptr, const double *jdreff); +static int wcspih_epoch(double *wptr, const double *epoch); +static int wcspih_vsource(double *wptr, const double *vsource); + +static int wcspih_timepixr(double timepixr); + +#line 21599 "wcspih.c" +#line 21600 "wcspih.c" + +#define INITIAL 0 +#define CCia 1 +#define CCi_ja 2 +#define CCCCCia 3 +#define CCi_ma 4 +#define CCCCCCCa 5 +#define CCCCCCCC 6 +#define CROTAi 7 +#define PROJPn 8 +#define SIP2 9 +#define SIP3 10 +#define DSSAMDXY 11 +#define PLTDECSN 12 +#define VALUE 13 +#define INTEGER_VAL 14 +#define FLOAT_VAL 15 +#define FLOAT2_VAL 16 +#define STRING_VAL 17 +#define RECORD_VAL 18 +#define RECFIELD 19 +#define RECCOLON 20 +#define RECVALUE 21 +#define RECEND 22 +#define COMMENT 23 +#define DISCARD 24 +#define ERROR 25 +#define FLUSH 26 + +#ifndef YY_NO_UNISTD_H +/* Special case for "unistd.h", since it is non-ANSI. We include it way + * down here because we want the user's section 1 to have been scanned first. + * The user has a chance to override it with an option. + */ +#include +#endif + +#define YY_EXTRA_TYPE struct wcspih_extra * + +/* Holds the entire state of the reentrant scanner. */ +struct yyguts_t + { + + /* User-defined. Not touched by flex. */ + YY_EXTRA_TYPE yyextra_r; + + /* The rest are the same as the globals declared in the non-reentrant scanner. */ + FILE *yyin_r, *yyout_r; + size_t yy_buffer_stack_top; /**< index of top of stack. */ + size_t yy_buffer_stack_max; /**< capacity of stack. */ + YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */ + char yy_hold_char; + int yy_n_chars; + int yyleng_r; + char *yy_c_buf_p; + int yy_init; + int yy_start; + int yy_did_buffer_switch_on_eof; + int yy_start_stack_ptr; + int yy_start_stack_depth; + int *yy_start_stack; + yy_state_type yy_last_accepting_state; + char* yy_last_accepting_cpos; + + int yylineno_r; + int yy_flex_debug_r; + + char *yytext_r; + int yy_more_flag; + int yy_more_len; + + }; /* end struct yyguts_t */ + +static int yy_init_globals ( yyscan_t yyscanner ); + +int yylex_init (yyscan_t* scanner); + +int yylex_init_extra ( YY_EXTRA_TYPE user_defined, yyscan_t* scanner); + +/* Accessor methods to globals. + These are made visible to non-reentrant scanners for convenience. */ + +int yylex_destroy ( yyscan_t yyscanner ); + +int yyget_debug ( yyscan_t yyscanner ); + +void yyset_debug ( int debug_flag , yyscan_t yyscanner ); + +YY_EXTRA_TYPE yyget_extra ( yyscan_t yyscanner ); + +void yyset_extra ( YY_EXTRA_TYPE user_defined , yyscan_t yyscanner ); + +FILE *yyget_in ( yyscan_t yyscanner ); + +void yyset_in ( FILE * _in_str , yyscan_t yyscanner ); + +FILE *yyget_out ( yyscan_t yyscanner ); + +void yyset_out ( FILE * _out_str , yyscan_t yyscanner ); + + int yyget_leng ( yyscan_t yyscanner ); + +char *yyget_text ( yyscan_t yyscanner ); + +int yyget_lineno ( yyscan_t yyscanner ); + +void yyset_lineno ( int _line_number , yyscan_t yyscanner ); + +int yyget_column ( yyscan_t yyscanner ); + +void yyset_column ( int _column_no , yyscan_t yyscanner ); + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int yywrap ( yyscan_t yyscanner ); +#else +extern int yywrap ( yyscan_t yyscanner ); +#endif +#endif + +#ifndef YY_NO_UNPUT + + static void yyunput ( int c, char *buf_ptr , yyscan_t yyscanner); + +#endif + +#ifndef yytext_ptr +static void yy_flex_strncpy ( char *, const char *, int , yyscan_t yyscanner); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen ( const char * , yyscan_t yyscanner); +#endif + +#ifndef YY_NO_INPUT +#ifdef __cplusplus +static int yyinput ( yyscan_t yyscanner ); +#else +static int input ( yyscan_t yyscanner ); +#endif + +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k */ +#define YY_READ_BUF_SIZE 16384 +#else +#define YY_READ_BUF_SIZE 8192 +#endif /* __ia64__ */ +#endif + +/* Copy whatever the last rule matched to the standard output. */ +#ifndef ECHO +/* This used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite(). + */ +#define ECHO do { if (fwrite( yytext, (size_t) yyleng, 1, yyout )) {} } while (0) +#endif + +/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#ifndef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + errno=0; \ + while ( (result = (int) read( fileno(yyin), buf, (yy_size_t) max_size )) < 0 ) \ + { \ + if( errno != EINTR) \ + { \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + break; \ + } \ + errno=0; \ + clearerr(yyin); \ + }\ +\ + +#endif + +/* No semi-colon after return; correct usage is to write "yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#ifndef yyterminate +#define yyterminate() return YY_NULL +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Report a fatal error. */ +#ifndef YY_FATAL_ERROR +#define YY_FATAL_ERROR(msg) yy_fatal_error( msg , yyscanner) +#endif + +/* end tables serialization structures and prototypes */ + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL_IS_OURS 1 + +extern int yylex (yyscan_t yyscanner); + +#define YY_DECL int yylex (yyscan_t yyscanner) +#endif /* !YY_DECL */ + +/* Code executed at the beginning of each rule, after yytext and yyleng + * have been set up. + */ +#ifndef YY_USER_ACTION +#define YY_USER_ACTION +#endif + +/* Code executed at the end of each rule. */ +#ifndef YY_BREAK +#define YY_BREAK /*LINTED*/break; +#endif + +#define YY_RULE_SETUP \ + if ( yyleng > 0 ) \ + YY_CURRENT_BUFFER_LVALUE->yy_at_bol = \ + (yytext[yyleng - 1] == '\n'); \ + YY_USER_ACTION + +/** The main scanner function which does all the work. + */ +YY_DECL +{ + yy_state_type yy_current_state; + char *yy_cp, *yy_bp; + int yy_act; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if ( !yyg->yy_init ) + { + yyg->yy_init = 1; + +#ifdef YY_USER_INIT + YY_USER_INIT; +#endif + + if ( ! yyg->yy_start ) + yyg->yy_start = 1; /* first start state */ + + if ( ! yyin ) + yyin = stdin; + + if ( ! yyout ) + yyout = stdout; + + if ( ! YY_CURRENT_BUFFER ) { + yyensure_buffer_stack (yyscanner); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); + } + + yy_load_buffer_state( yyscanner ); + } + + { +#line 187 "wcspih.l" + +#line 189 "wcspih.l" + int p, q; + char *errmsg, errtxt[80], *keyname, strtmp[80], *wat[2], *watstr; + int alts[27], dpq[27], inttmp, ndp[27], ndq[27], nps[27], npv[27], + rectype; + double dbltmp, dbl2tmp[2], dsstmp[20]; + struct auxprm auxtem; + struct disprm distem; + struct wcsprm wcstem; + + int naxis = 0; + for (int ialt = 0; ialt < 27; ialt++) { + alts[ialt] = 0; + dpq[ialt] = 0; + npv[ialt] = 0; + nps[ialt] = 0; + ndp[ialt] = 0; + ndq[ialt] = 0; + } + + // Our handle on the input stream. + char *keyrec = header; + char *hptr = header; + char *keep = 0x0; + + // For keeping tallies of keywords found. + *nreject = 0; + int nvalid = 0; + int nother = 0; + + // If strict, then also reject. + if (relax & WCSHDR_strict) relax |= WCSHDR_reject; + + // Keyword indices, as used in the WCS papers, e.g. PCi_ja, PVi_ma. + int i = 0; + int j = 0; + int m = 0; + char a = ' '; + + // For decoding the keyvalue. + int valtype = -1; + int distype = 0; + void *vptr = 0x0; + + // For keywords that require special handling. + int altlin = 0; + int *npptr = 0x0; + int (*chekval)(double) = 0x0; + int (*special)(double *, const double *) = 0x0; + int auxprm = 0; + int naux = 0; + int distran = 0; + int sipflag = 0; + int dssflag = 0; + int watflag = 0; + int watn = 0; + + // The data structures produced. + *nwcs = 0; + *wcs = 0x0; + + // Control variables. + int ipass = 1; + int npass = 2; + + // User data associated with yyscanner. + yyextra->hdr = header; + yyextra->nkeyrec = nkeyrec; + + // Return here via longjmp() invoked by yy_fatal_error(). + if (setjmp(yyextra->abort_jmp_env)) { + return WCSHDRERR_PARSER; + } + + BEGIN(INITIAL); + + +#line 21950 "wcspih.c" + + while ( /*CONSTCOND*/1 ) /* loops until end-of-file is reached */ + { + yy_cp = yyg->yy_c_buf_p; + + /* Support of yytext. */ + *yy_cp = yyg->yy_hold_char; + + /* yy_bp points to the position in yy_ch_buf of the start of + * the current run. + */ + yy_bp = yy_cp; + + yy_current_state = yyg->yy_start; + yy_current_state += YY_AT_BOL(); +yy_match: + while ( (yy_current_state = yy_nxt[yy_current_state][ YY_SC_TO_UI(*yy_cp) ]) > 0 ) + { + if ( yy_accept[yy_current_state] ) + { + yyg->yy_last_accepting_state = yy_current_state; + yyg->yy_last_accepting_cpos = yy_cp; + } + + ++yy_cp; + } + + yy_current_state = -yy_current_state; + +yy_find_action: + yy_act = yy_accept[yy_current_state]; + + YY_DO_BEFORE_ACTION; + +do_action: /* This label is used only to access EOF actions. */ + + switch ( yy_act ) + { /* beginning of action switch */ + case 0: /* must back up */ + /* undo the effects of YY_DO_BEFORE_ACTION */ + *yy_cp = yyg->yy_hold_char; + yy_cp = yyg->yy_last_accepting_cpos + 1; + yy_current_state = yyg->yy_last_accepting_state; + goto yy_find_action; + +case 1: +YY_RULE_SETUP +#line 265 "wcspih.l" +{ + keyname = "NAXISn"; + + if (ipass == 1) { + sscanf(yytext, "NAXIS = %d", &naxis); + if (naxis < 0) naxis = 0; + BEGIN(FLUSH); + + } else { + sscanf(yytext, "NAXIS = %d", &i); + + if (i < 0) { + errmsg = "negative value of NAXIS ignored"; + BEGIN(ERROR); + } else { + BEGIN(DISCARD); + } + } + } + YY_BREAK +case 2: +YY_RULE_SETUP +#line 285 "wcspih.l" +{ + sscanf(yytext, "WCSAXES%c= %d", &a, &i); + + if (i < 0) { + errmsg = "negative value of WCSAXESa ignored"; + BEGIN(ERROR); + + } else { + valtype = INTEGER; + vptr = 0x0; + + keyname = "WCSAXESa"; + BEGIN(COMMENT); + } + } + YY_BREAK +case 3: +YY_RULE_SETUP +#line 301 "wcspih.l" +{ + valtype = FLOAT; + vptr = &(wcstem.crpix); + + keyname = "CRPIXja"; + BEGIN(CCCCCia); + } + YY_BREAK +case 4: +YY_RULE_SETUP +#line 309 "wcspih.l" +{ + valtype = FLOAT; + vptr = &(wcstem.pc); + altlin = 1; + + keyname = "PCi_ja"; + BEGIN(CCi_ja); + } + YY_BREAK +case 5: +YY_RULE_SETUP +#line 318 "wcspih.l" +{ + valtype = FLOAT; + vptr = &(wcstem.cd); + altlin = 2; + + keyname = "CDi_ja"; + BEGIN(CCi_ja); + } + YY_BREAK +case 6: +YY_RULE_SETUP +#line 327 "wcspih.l" +{ + valtype = FLOAT; + vptr = &(wcstem.cdelt); + + keyname = "CDELTia"; + BEGIN(CCCCCia); + } + YY_BREAK +case 7: +YY_RULE_SETUP +#line 335 "wcspih.l" +{ + valtype = FLOAT; + vptr = &(wcstem.crota); + altlin = 4; + + keyname = "CROTAn"; + BEGIN(CROTAi); + } + YY_BREAK +case 8: +YY_RULE_SETUP +#line 344 "wcspih.l" +{ + valtype = STRING; + vptr = &(wcstem.cunit); + + keyname = "CUNITia"; + BEGIN(CCCCCia); + } + YY_BREAK +case 9: +YY_RULE_SETUP +#line 352 "wcspih.l" +{ + valtype = STRING; + vptr = &(wcstem.ctype); + + keyname = "CTYPEia"; + BEGIN(CCCCCia); + } + YY_BREAK +case 10: +YY_RULE_SETUP +#line 360 "wcspih.l" +{ + valtype = FLOAT; + vptr = &(wcstem.crval); + + keyname = "CRVALia"; + BEGIN(CCCCCia); + } + YY_BREAK +case 11: +YY_RULE_SETUP +#line 368 "wcspih.l" +{ + valtype = FLOAT; + vptr = &(wcstem.lonpole); + + keyname = "LONPOLEa"; + BEGIN(CCCCCCCa); + } + YY_BREAK +case 12: +YY_RULE_SETUP +#line 376 "wcspih.l" +{ + valtype = FLOAT; + vptr = &(wcstem.latpole); + + keyname = "LATPOLEa"; + BEGIN(CCCCCCCa); + } + YY_BREAK +case 13: +YY_RULE_SETUP +#line 384 "wcspih.l" +{ + valtype = FLOAT; + vptr = &(wcstem.restfrq); + + keyname = "RESTFRQa"; + BEGIN(CCCCCCCa); + } + YY_BREAK +case 14: +YY_RULE_SETUP +#line 392 "wcspih.l" +{ + if (relax & WCSHDR_strict) { + errmsg = "the RESTFREQ keyword is deprecated, use RESTFRQa"; + BEGIN(ERROR); + + } else { + valtype = FLOAT; + vptr = &(wcstem.restfrq); + + unput(' '); + + keyname = "RESTFREQ"; + BEGIN(CCCCCCCa); + } + } + YY_BREAK +case 15: +YY_RULE_SETUP +#line 408 "wcspih.l" +{ + valtype = FLOAT; + vptr = &(wcstem.restwav); + + keyname = "RESTWAVa"; + BEGIN(CCCCCCCa); + } + YY_BREAK +case 16: +YY_RULE_SETUP +#line 416 "wcspih.l" +{ + valtype = FLOAT; + vptr = &(wcstem.pv); + npptr = npv; + + keyname = "PVi_ma"; + BEGIN(CCi_ma); + } + YY_BREAK +case 17: +YY_RULE_SETUP +#line 425 "wcspih.l" +{ + valtype = FLOAT; + vptr = &(wcstem.pv); + npptr = npv; + + keyname = "PROJPn"; + BEGIN(PROJPn); + } + YY_BREAK +case 18: +YY_RULE_SETUP +#line 434 "wcspih.l" +{ + valtype = STRING; + vptr = &(wcstem.ps); + npptr = nps; + + keyname = "PSi_ma"; + BEGIN(CCi_ma); + } + YY_BREAK +case 19: +YY_RULE_SETUP +#line 443 "wcspih.l" +{ + sscanf(yytext, "VELREF%c", &a); + + if (relax & WCSHDR_strict) { + errmsg = "the VELREF keyword is deprecated, use SPECSYSa"; + BEGIN(ERROR); + + } else if ((a == ' ') || (relax & WCSHDR_VELREFa)) { + valtype = INTEGER; + vptr = &(wcstem.velref); + + unput(a); + + keyname = "VELREF"; + BEGIN(CCCCCCCa); + + } else if (relax & WCSHDR_reject) { + errmsg = "VELREF keyword may not have an alternate version code"; + BEGIN(ERROR); + + } else { + BEGIN(DISCARD); + } + } + YY_BREAK +case 20: +YY_RULE_SETUP +#line 468 "wcspih.l" +{ + valtype = STRING; + vptr = &(wcstem.cname); + + keyname = "CNAMEia"; + BEGIN(CCCCCia); + } + YY_BREAK +case 21: +YY_RULE_SETUP +#line 476 "wcspih.l" +{ + valtype = FLOAT; + vptr = &(wcstem.crder); + + keyname = "CRDERia"; + BEGIN(CCCCCia); + } + YY_BREAK +case 22: +YY_RULE_SETUP +#line 484 "wcspih.l" +{ + valtype = FLOAT; + vptr = &(wcstem.csyer); + + keyname = "CSYERia"; + BEGIN(CCCCCia); + } + YY_BREAK +case 23: +YY_RULE_SETUP +#line 492 "wcspih.l" +{ + valtype = FLOAT; + vptr = &(wcstem.czphs); + + keyname = "CZPHSia"; + BEGIN(CCCCCia); + } + YY_BREAK +case 24: +YY_RULE_SETUP +#line 500 "wcspih.l" +{ + valtype = FLOAT; + vptr = &(wcstem.cperi); + + keyname = "CPERIia"; + BEGIN(CCCCCia); + } + YY_BREAK +case 25: +YY_RULE_SETUP +#line 508 "wcspih.l" +{ + valtype = STRING; + vptr = wcstem.wcsname; + + keyname = "WCSNAMEa"; + BEGIN(CCCCCCCa); + } + YY_BREAK +case 26: +YY_RULE_SETUP +#line 516 "wcspih.l" +{ + valtype = STRING; + vptr = wcstem.timesys; + + keyname = "TIMESYS"; + BEGIN(CCCCCCCC); + } + YY_BREAK +case 27: +YY_RULE_SETUP +#line 524 "wcspih.l" +{ + valtype = STRING; + vptr = wcstem.trefpos; + + keyname = "TREFPOS"; + BEGIN(CCCCCCCC); + } + YY_BREAK +case 28: +YY_RULE_SETUP +#line 532 "wcspih.l" +{ + valtype = STRING; + vptr = wcstem.trefdir; + + keyname = "TREFDIR"; + BEGIN(CCCCCCCC); + } + YY_BREAK +case 29: +YY_RULE_SETUP +#line 540 "wcspih.l" +{ + valtype = STRING; + vptr = wcstem.plephem; + + keyname = "PLEPHEM"; + BEGIN(CCCCCCCC); + } + YY_BREAK +case 30: +YY_RULE_SETUP +#line 548 "wcspih.l" +{ + valtype = STRING; + vptr = wcstem.timeunit; + + keyname = "TIMEUNIT"; + BEGIN(CCCCCCCC); + } + YY_BREAK +case 31: +#line 557 "wcspih.l" +case 32: +YY_RULE_SETUP +#line 557 "wcspih.l" +{ + if ((yytext[4] == 'R') || (relax & WCSHDR_DATEREF)) { + valtype = STRING; + vptr = wcstem.dateref; + + keyname = "DATEREF"; + BEGIN(CCCCCCCC); + + } else if (relax & WCSHDR_reject) { + errmsg = "the DATE-REF keyword is non-standard"; + BEGIN(ERROR); + + } else { + BEGIN(DISCARD); + } + } + YY_BREAK +case 33: +#line 575 "wcspih.l" +case 34: +YY_RULE_SETUP +#line 575 "wcspih.l" +{ + if ((yytext[3] == 'R') || (relax & WCSHDR_DATEREF)) { + valtype = FLOAT2; + vptr = wcstem.mjdref; + + keyname = "MJDREF"; + BEGIN(CCCCCCCC); + + } else if (relax & WCSHDR_reject) { + errmsg = "the MJD-REF keyword is non-standard"; + BEGIN(ERROR); + + } else { + BEGIN(DISCARD); + } + } + YY_BREAK +case 35: +#line 593 "wcspih.l" +case 36: +YY_RULE_SETUP +#line 593 "wcspih.l" +{ + if ((yytext[3] == 'R') || (relax & WCSHDR_DATEREF)) { + // Actually integer, but treated as float. + valtype = FLOAT; + vptr = wcstem.mjdref; + + keyname = "MJDREFI"; + BEGIN(CCCCCCCC); + + } else if (relax & WCSHDR_reject) { + errmsg = "the MJD-REFI keyword is non-standard"; + BEGIN(ERROR); + + } else { + BEGIN(DISCARD); + } + } + YY_BREAK +case 37: +#line 612 "wcspih.l" +case 38: +YY_RULE_SETUP +#line 612 "wcspih.l" +{ + if ((yytext[3] == 'R') || (relax & WCSHDR_DATEREF)) { + valtype = FLOAT; + vptr = wcstem.mjdref + 1; + + keyname = "MJDREFF"; + BEGIN(CCCCCCCC); + + } else if (relax & WCSHDR_reject) { + errmsg = "the MJD-REFF keyword is non-standard"; + BEGIN(ERROR); + + } else { + BEGIN(DISCARD); + } + } + YY_BREAK +case 39: +#line 630 "wcspih.l" +case 40: +YY_RULE_SETUP +#line 630 "wcspih.l" +{ + if ((yytext[2] == 'R') || (relax & WCSHDR_DATEREF)) { + valtype = FLOAT2; + vptr = wcstem.mjdref; + special = wcspih_jdref; + + keyname = "JDREF"; + BEGIN(CCCCCCCC); + + } else if (relax & WCSHDR_reject) { + errmsg = "the JD-REF keyword is non-standard"; + BEGIN(ERROR); + + } else { + BEGIN(DISCARD); + } + } + YY_BREAK +case 41: +#line 649 "wcspih.l" +case 42: +YY_RULE_SETUP +#line 649 "wcspih.l" +{ + if ((yytext[2] == 'R') || (relax & WCSHDR_DATEREF)) { + // Actually integer, but treated as float. + valtype = FLOAT; + vptr = wcstem.mjdref; + special = wcspih_jdrefi; + + keyname = "JDREFI"; + BEGIN(CCCCCCCC); + + } else if (relax & WCSHDR_reject) { + errmsg = "the JD-REFI keyword is non-standard"; + BEGIN(ERROR); + + } else { + BEGIN(DISCARD); + } + } + YY_BREAK +case 43: +#line 669 "wcspih.l" +case 44: +YY_RULE_SETUP +#line 669 "wcspih.l" +{ + if ((yytext[2] == 'R') || (relax & WCSHDR_DATEREF)) { + valtype = FLOAT; + vptr = wcstem.mjdref; + special = wcspih_jdreff; + + keyname = "JDREFF"; + BEGIN(CCCCCCCC); + + } else if (relax & WCSHDR_reject) { + errmsg = "the JD-REFF keyword is non-standard"; + BEGIN(ERROR); + + } else { + BEGIN(DISCARD); + } + } + YY_BREAK +case 45: +YY_RULE_SETUP +#line 687 "wcspih.l" +{ + valtype = FLOAT; + vptr = &(wcstem.timeoffs); + + keyname = "TIMEOFFS"; + BEGIN(CCCCCCCC); + } + YY_BREAK +case 46: +YY_RULE_SETUP +#line 695 "wcspih.l" +{ + valtype = STRING; + vptr = wcstem.dateobs; + if (ctrl < -10) keep = keyrec; + + keyname = "DATE-OBS"; + BEGIN(CCCCCCCC); + } + YY_BREAK +case 47: +YY_RULE_SETUP +#line 704 "wcspih.l" +{ + valtype = STRING; + vptr = wcstem.datebeg; + if (ctrl < -10) keep = keyrec; + + keyname = "DATE-BEG"; + BEGIN(CCCCCCCC); + } + YY_BREAK +case 48: +YY_RULE_SETUP +#line 713 "wcspih.l" +{ + valtype = STRING; + vptr = wcstem.dateavg; + if (ctrl < -10) keep = keyrec; + + keyname = "DATE-AVG"; + BEGIN(CCCCCCCC); + } + YY_BREAK +case 49: +YY_RULE_SETUP +#line 722 "wcspih.l" +{ + valtype = STRING; + vptr = wcstem.dateend; + if (ctrl < -10) keep = keyrec; + + keyname = "DATE-END"; + BEGIN(CCCCCCCC); + } + YY_BREAK +case 50: +YY_RULE_SETUP +#line 731 "wcspih.l" +{ + valtype = FLOAT; + vptr = &(wcstem.mjdobs); + if (ctrl < -10) keep = keyrec; + + keyname = "MJD-OBS"; + BEGIN(CCCCCCCC); + } + YY_BREAK +case 51: +YY_RULE_SETUP +#line 740 "wcspih.l" +{ + valtype = FLOAT; + vptr = &(wcstem.mjdbeg); + if (ctrl < -10) keep = keyrec; + + keyname = "MJD-BEG"; + BEGIN(CCCCCCCC); + } + YY_BREAK +case 52: +YY_RULE_SETUP +#line 749 "wcspih.l" +{ + valtype = FLOAT; + vptr = &(wcstem.mjdavg); + if (ctrl < -10) keep = keyrec; + + keyname = "MJD-AVG"; + BEGIN(CCCCCCCC); + } + YY_BREAK +case 53: +YY_RULE_SETUP +#line 758 "wcspih.l" +{ + valtype = FLOAT; + vptr = &(wcstem.mjdend); + if (ctrl < -10) keep = keyrec; + + keyname = "MJD-END"; + BEGIN(CCCCCCCC); + } + YY_BREAK +case 54: +YY_RULE_SETUP +#line 767 "wcspih.l" +{ + valtype = FLOAT; + vptr = &(wcstem.jepoch); + if (ctrl < -10) keep = keyrec; + + keyname = "JEPOCH"; + BEGIN(CCCCCCCC); + } + YY_BREAK +case 55: +YY_RULE_SETUP +#line 776 "wcspih.l" +{ + valtype = FLOAT; + vptr = &(wcstem.bepoch); + if (ctrl < -10) keep = keyrec; + + keyname = "BEPOCH"; + BEGIN(CCCCCCCC); + } + YY_BREAK +case 56: +YY_RULE_SETUP +#line 785 "wcspih.l" +{ + valtype = FLOAT; + vptr = &(wcstem.tstart); + if (ctrl < -10) keep = keyrec; + + keyname = "TSTART"; + BEGIN(CCCCCCCC); + } + YY_BREAK +case 57: +YY_RULE_SETUP +#line 794 "wcspih.l" +{ + valtype = FLOAT; + vptr = &(wcstem.tstop); + if (ctrl < -10) keep = keyrec; + + keyname = "TSTOP"; + BEGIN(CCCCCCCC); + } + YY_BREAK +case 58: +YY_RULE_SETUP +#line 803 "wcspih.l" +{ + valtype = FLOAT; + vptr = &(wcstem.xposure); + if (ctrl < -10) keep = keyrec; + + keyname = "XPOSURE"; + BEGIN(CCCCCCCC); + } + YY_BREAK +case 59: +YY_RULE_SETUP +#line 812 "wcspih.l" +{ + valtype = FLOAT; + vptr = &(wcstem.telapse); + if (ctrl < -10) keep = keyrec; + + keyname = "TELAPSE"; + BEGIN(CCCCCCCC); + } + YY_BREAK +case 60: +YY_RULE_SETUP +#line 821 "wcspih.l" +{ + valtype = FLOAT; + vptr = &(wcstem.timsyer); + if (ctrl < -10) keep = keyrec; + + keyname = "TIMSYER"; + BEGIN(CCCCCCCC); + } + YY_BREAK +case 61: +YY_RULE_SETUP +#line 830 "wcspih.l" +{ + valtype = FLOAT; + vptr = &(wcstem.timrder); + if (ctrl < -10) keep = keyrec; + + keyname = "TIMRDER"; + BEGIN(CCCCCCCC); + } + YY_BREAK +case 62: +YY_RULE_SETUP +#line 839 "wcspih.l" +{ + valtype = FLOAT; + vptr = &(wcstem.timedel); + if (ctrl < -10) keep = keyrec; + + keyname = "TIMEDEL"; + BEGIN(CCCCCCCC); + } + YY_BREAK +case 63: +YY_RULE_SETUP +#line 848 "wcspih.l" +{ + valtype = FLOAT; + vptr = &(wcstem.timepixr); + chekval = wcspih_timepixr; + if (ctrl < -10) keep = keyrec; + + keyname = "TIMEPIXR"; + BEGIN(CCCCCCCC); + } + YY_BREAK +case 64: +YY_RULE_SETUP +#line 858 "wcspih.l" +{ + valtype = FLOAT; + vptr = wcstem.obsgeo; + if (ctrl < -10) keep = keyrec; + + keyname = "OBSGEO-X"; + BEGIN(CCCCCCCC); + } + YY_BREAK +case 65: +YY_RULE_SETUP +#line 867 "wcspih.l" +{ + valtype = FLOAT; + vptr = wcstem.obsgeo + 1; + if (ctrl < -10) keep = keyrec; + + keyname = "OBSGEO-Y"; + BEGIN(CCCCCCCC); + } + YY_BREAK +case 66: +YY_RULE_SETUP +#line 876 "wcspih.l" +{ + valtype = FLOAT; + vptr = wcstem.obsgeo + 2; + if (ctrl < -10) keep = keyrec; + + keyname = "OBSGEO-Z"; + BEGIN(CCCCCCCC); + } + YY_BREAK +case 67: +YY_RULE_SETUP +#line 885 "wcspih.l" +{ + valtype = FLOAT; + vptr = wcstem.obsgeo + 3; + if (ctrl < -10) keep = keyrec; + + keyname = "OBSGEO-L"; + BEGIN(CCCCCCCC); + } + YY_BREAK +case 68: +YY_RULE_SETUP +#line 894 "wcspih.l" +{ + valtype = FLOAT; + vptr = wcstem.obsgeo + 4; + if (ctrl < -10) keep = keyrec; + + keyname = "OBSGEO-B"; + BEGIN(CCCCCCCC); + } + YY_BREAK +case 69: +YY_RULE_SETUP +#line 903 "wcspih.l" +{ + valtype = FLOAT; + vptr = wcstem.obsgeo + 5; + if (ctrl < -10) keep = keyrec; + + keyname = "OBSGEO-H"; + BEGIN(CCCCCCCC); + } + YY_BREAK +case 70: +YY_RULE_SETUP +#line 912 "wcspih.l" +{ + valtype = STRING; + vptr = wcstem.obsorbit; + + keyname = "OBSORBIT"; + BEGIN(CCCCCCCC); + } + YY_BREAK +case 71: +YY_RULE_SETUP +#line 920 "wcspih.l" +{ + valtype = STRING; + vptr = wcstem.radesys; + + keyname = "RADESYSa"; + BEGIN(CCCCCCCa); + } + YY_BREAK +case 72: +YY_RULE_SETUP +#line 928 "wcspih.l" +{ + if (relax & WCSHDR_RADECSYS) { + valtype = STRING; + vptr = wcstem.radesys; + + unput(' '); + + keyname = "RADECSYS"; + BEGIN(CCCCCCCa); + + } else if (relax & WCSHDR_reject) { + errmsg = "the RADECSYS keyword is deprecated, use RADESYSa"; + BEGIN(ERROR); + + } else { + BEGIN(DISCARD); + } + } + YY_BREAK +case 73: +YY_RULE_SETUP +#line 947 "wcspih.l" +{ + sscanf(yytext, "EPOCH%c", &a); + + if (relax & WCSHDR_strict) { + errmsg = "the EPOCH keyword is deprecated, use EQUINOXa"; + BEGIN(ERROR); + + } else if (a == ' ' || relax & WCSHDR_EPOCHa) { + valtype = FLOAT; + vptr = &(wcstem.equinox); + special = wcspih_epoch; + + unput(a); + + keyname = "EPOCH"; + BEGIN(CCCCCCCa); + + } else if (relax & WCSHDR_reject) { + errmsg = "EPOCH keyword may not have an alternate version code"; + BEGIN(ERROR); + + } else { + BEGIN(DISCARD); + } + } + YY_BREAK +case 74: +YY_RULE_SETUP +#line 973 "wcspih.l" +{ + valtype = FLOAT; + vptr = &(wcstem.equinox); + + keyname = "EQUINOXa"; + BEGIN(CCCCCCCa); + } + YY_BREAK +case 75: +YY_RULE_SETUP +#line 981 "wcspih.l" +{ + valtype = STRING; + vptr = wcstem.specsys; + + keyname = "SPECSYSa"; + BEGIN(CCCCCCCa); + } + YY_BREAK +case 76: +YY_RULE_SETUP +#line 989 "wcspih.l" +{ + valtype = STRING; + vptr = wcstem.ssysobs; + + keyname = "SSYSOBSa"; + BEGIN(CCCCCCCa); + } + YY_BREAK +case 77: +YY_RULE_SETUP +#line 997 "wcspih.l" +{ + valtype = FLOAT; + vptr = &(wcstem.velosys); + + keyname = "VELOSYSa"; + BEGIN(CCCCCCCa); + } + YY_BREAK +case 78: +YY_RULE_SETUP +#line 1005 "wcspih.l" +{ + if (relax & WCSHDR_VSOURCE) { + valtype = FLOAT; + vptr = &(wcstem.zsource); + special = wcspih_vsource; + + yyless(7); + + keyname = "VSOURCEa"; + BEGIN(CCCCCCCa); + + } else if (relax & WCSHDR_reject) { + errmsg = "the VSOURCEa keyword is deprecated, use ZSOURCEa"; + BEGIN(ERROR); + + } else { + BEGIN(DISCARD); + } + } + YY_BREAK +case 79: +YY_RULE_SETUP +#line 1025 "wcspih.l" +{ + valtype = FLOAT; + vptr = &(wcstem.zsource); + + keyname = "ZSOURCEa"; + BEGIN(CCCCCCCa); + } + YY_BREAK +case 80: +YY_RULE_SETUP +#line 1033 "wcspih.l" +{ + valtype = STRING; + vptr = wcstem.ssyssrc; + + keyname = "SSYSSRCa"; + BEGIN(CCCCCCCa); + } + YY_BREAK +case 81: +YY_RULE_SETUP +#line 1041 "wcspih.l" +{ + valtype = FLOAT; + vptr = &(wcstem.velangl); + + keyname = "VELANGLa"; + BEGIN(CCCCCCCa); + } + YY_BREAK +case 82: +YY_RULE_SETUP +#line 1049 "wcspih.l" +{ + valtype = FLOAT; + auxprm = 1; + vptr = &(auxtem.rsun_ref); + + keyname = "RSUN_REF"; + BEGIN(CCCCCCCC); + } + YY_BREAK +case 83: +YY_RULE_SETUP +#line 1058 "wcspih.l" +{ + valtype = FLOAT; + auxprm = 1; + vptr = &(auxtem.dsun_obs); + + keyname = "DSUN_OBS"; + BEGIN(CCCCCCCC); + } + YY_BREAK +case 84: +YY_RULE_SETUP +#line 1067 "wcspih.l" +{ + valtype = FLOAT; + auxprm = 1; + vptr = &(auxtem.crln_obs); + + keyname = "CRLN_OBS"; + BEGIN(CCCCCCCC); + } + YY_BREAK +case 85: +YY_RULE_SETUP +#line 1076 "wcspih.l" +{ + valtype = FLOAT; + auxprm = 1; + vptr = &(auxtem.hgln_obs); + + keyname = "HGLN_OBS"; + BEGIN(CCCCCCCC); + } + YY_BREAK +case 86: +#line 1086 "wcspih.l" +case 87: +YY_RULE_SETUP +#line 1086 "wcspih.l" +{ + valtype = FLOAT; + auxprm = 1; + vptr = &(auxtem.hglt_obs); + + keyname = "HGLT_OBS"; + BEGIN(CCCCCCCC); + } + YY_BREAK +case 88: +YY_RULE_SETUP +#line 1095 "wcspih.l" +{ + valtype = FLOAT; + auxprm = 1; + vptr = &(auxtem.a_radius); + + keyname = "A_RADIUS"; + BEGIN(CCCCCCCC); + } + YY_BREAK +case 89: +YY_RULE_SETUP +#line 1104 "wcspih.l" +{ + valtype = FLOAT; + auxprm = 1; + vptr = &(auxtem.b_radius); + + keyname = "B_RADIUS"; + BEGIN(CCCCCCCC); + } + YY_BREAK +case 90: +YY_RULE_SETUP +#line 1113 "wcspih.l" +{ + valtype = FLOAT; + auxprm = 1; + vptr = &(auxtem.c_radius); + + keyname = "C_RADIUS"; + BEGIN(CCCCCCCC); + } + YY_BREAK +case 91: +YY_RULE_SETUP +#line 1122 "wcspih.l" +{ + valtype = FLOAT; + auxprm = 1; + vptr = &(auxtem.blon_obs); + + keyname = "BLON_OBS"; + BEGIN(CCCCCCCC); + } + YY_BREAK +case 92: +YY_RULE_SETUP +#line 1131 "wcspih.l" +{ + valtype = FLOAT; + auxprm = 1; + vptr = &(auxtem.blat_obs); + + keyname = "BLAT_OBS"; + BEGIN(CCCCCCCC); + } + YY_BREAK +case 93: +YY_RULE_SETUP +#line 1140 "wcspih.l" +{ + valtype = FLOAT; + auxprm = 1; + vptr = &(auxtem.bdis_obs); + + keyname = "BDIS_OBS"; + BEGIN(CCCCCCCC); + } + YY_BREAK +case 94: +YY_RULE_SETUP +#line 1149 "wcspih.l" +{ + valtype = STRING; + distype = PRIOR; + vptr = &(distem.dtype); + + keyname = "CPDISja"; + BEGIN(CCCCCia); + } + YY_BREAK +case 95: +YY_RULE_SETUP +#line 1158 "wcspih.l" +{ + valtype = STRING; + distype = SEQUENT; + vptr = &(distem.dtype); + + keyname = "CQDISia"; + BEGIN(CCCCCia); + } + YY_BREAK +case 96: +YY_RULE_SETUP +#line 1167 "wcspih.l" +{ + valtype = RECORD; + distype = PRIOR; + vptr = &(distem.dp); + npptr = ndp; + + keyname = "DPja"; + BEGIN(CCia); } YY_BREAK -case 2: +case 97: YY_RULE_SETUP -#line 219 "wcspih.l" +#line 1177 "wcspih.l" { - if (pass == 1) { - sscanf(wcspihtext, "WCSAXES%c= %d", &a, &i); - wcspih_naxes(naxis, i, 0, a, alts, 0); - } - BEGIN(FLUSH); + valtype = RECORD; + distype = SEQUENT; + vptr = &(distem.dp); + npptr = ndq; + + keyname = "DQia"; + BEGIN(CCia); } YY_BREAK -case 3: +case 98: YY_RULE_SETUP -#line 227 "wcspih.l" +#line 1187 "wcspih.l" { valtype = FLOAT; - if (pass == 2) vptr = &((*wcs)->crpix); + distype = PRIOR; + vptr = &(distem.maxdis); + + keyname = "CPERRja"; BEGIN(CCCCCia); } YY_BREAK -case 4: +case 99: YY_RULE_SETUP -#line 233 "wcspih.l" +#line 1196 "wcspih.l" { valtype = FLOAT; - if (pass == 2) vptr = &((*wcs)->pc); - altlin = 1; - BEGIN(CCi_ja); + distype = SEQUENT; + vptr = &(distem.maxdis); + + keyname = "CQERRia"; + BEGIN(CCCCCia); } YY_BREAK -case 5: +case 100: YY_RULE_SETUP -#line 240 "wcspih.l" +#line 1205 "wcspih.l" { valtype = FLOAT; - if (pass == 2) vptr = &((*wcs)->cd); - altlin = 2; - BEGIN(CCi_ja); + distype = PRIOR; + vptr = &(distem.totdis); + + keyname = "DVERRa"; + BEGIN(CCCCCCCa); } YY_BREAK -case 6: +case 101: YY_RULE_SETUP -#line 247 "wcspih.l" +#line 1214 "wcspih.l" { - valtype = FLOAT; - if (pass == 2) vptr = &((*wcs)->cdelt); - BEGIN(CCCCCia); + // SIP: axis 1 polynomial degree (not stored). + valtype = INTEGER; + distype = PRIOR; + vptr = 0x0; + + i = 1; + a = ' '; + + keyname = "A_ORDER"; + BEGIN(VALUE); } YY_BREAK -case 7: +case 102: YY_RULE_SETUP -#line 253 "wcspih.l" +#line 1227 "wcspih.l" { - valtype = FLOAT; - if (pass == 2) vptr = &((*wcs)->crota); - altlin = 4; - BEGIN(CROTAi); + // SIP: axis 2 polynomial degree (not stored). + valtype = INTEGER; + distype = PRIOR; + vptr = 0x0; + + i = 2; + a = ' '; + + keyname = "B_ORDER"; + BEGIN(VALUE); } YY_BREAK -case 8: +case 103: YY_RULE_SETUP -#line 260 "wcspih.l" +#line 1240 "wcspih.l" { - valtype = STRING; - if (pass == 2) vptr = &((*wcs)->cunit); - BEGIN(CCCCCia); + // SIP: axis 1 inverse polynomial degree (not stored). + valtype = INTEGER; + distype = PRIOR; + vptr = 0x0; + + i = 1; + a = ' '; + + keyname = "AP_ORDER"; + BEGIN(VALUE); } YY_BREAK -case 9: +case 104: YY_RULE_SETUP -#line 266 "wcspih.l" +#line 1253 "wcspih.l" { - valtype = STRING; - if (pass == 2) vptr = &((*wcs)->ctype); - BEGIN(CCCCCia); + // SIP: axis 2 inverse polynomial degree (not stored). + valtype = INTEGER; + distype = PRIOR; + vptr = 0x0; + + i = 2; + a = ' '; + + keyname = "BP_ORDER"; + BEGIN(VALUE); } YY_BREAK -case 10: +case 105: YY_RULE_SETUP -#line 272 "wcspih.l" +#line 1266 "wcspih.l" { + // SIP: axis 1 maximum distortion. valtype = FLOAT; - if (pass == 2) vptr = &((*wcs)->crval); - BEGIN(CCCCCia); + distype = PRIOR; + vptr = &(distem.maxdis); + + i = 1; + a = ' '; + + keyname = "A_DMAX"; + BEGIN(VALUE); } YY_BREAK -case 11: +case 106: YY_RULE_SETUP -#line 278 "wcspih.l" +#line 1279 "wcspih.l" { + // SIP: axis 2 maximum distortion. valtype = FLOAT; - if (pass == 2) vptr = &((*wcs)->lonpole); - BEGIN(CCCCCCCa); + distype = PRIOR; + vptr = &(distem.maxdis); + + i = 2; + a = ' '; + + keyname = "B_DMAX"; + BEGIN(VALUE); } YY_BREAK -case 12: +case 107: YY_RULE_SETUP -#line 284 "wcspih.l" +#line 1292 "wcspih.l" { - valtype = FLOAT; - if (pass == 2) vptr = &((*wcs)->latpole); - BEGIN(CCCCCCCa); + // SIP: axis 1 polynomial coefficient. + i = 1; + sipflag = 2; + + keyname = "A_p_q"; + BEGIN(SIP2); } YY_BREAK -case 13: +case 108: YY_RULE_SETUP -#line 290 "wcspih.l" +#line 1301 "wcspih.l" { - valtype = FLOAT; - if (pass == 2) vptr = &((*wcs)->restfrq); - BEGIN(CCCCCCCa); + // SIP: axis 2 polynomial coefficient. + i = 2; + sipflag = 2; + + keyname = "B_p_q"; + BEGIN(SIP2); } YY_BREAK -case 14: +case 109: YY_RULE_SETUP -#line 296 "wcspih.l" +#line 1310 "wcspih.l" { - valtype = FLOAT; - if (pass == 2) vptr = &((*wcs)->restfrq); - unput(' '); - BEGIN(CCCCCCCa); + // SIP: axis 1 inverse polynomial coefficient. + i = 1; + sipflag = 3; + + keyname = "AP_p_q"; + BEGIN(SIP3); } YY_BREAK -case 15: +case 110: YY_RULE_SETUP -#line 303 "wcspih.l" +#line 1319 "wcspih.l" { - valtype = FLOAT; - if (pass == 2) vptr = &((*wcs)->restwav); - BEGIN(CCCCCCCa); + // SIP: axis 2 inverse polynomial coefficient. + i = 2; + sipflag = 3; + + keyname = "BP_p_q"; + BEGIN(SIP3); } YY_BREAK -case 16: +case 111: YY_RULE_SETUP -#line 309 "wcspih.l" +#line 1328 "wcspih.l" { + // DSS: LLH corner pixel coordinate 1. valtype = FLOAT; - if (pass == 2) vptr = &((*wcs)->pv); - npptr = npv; - BEGIN(CCi_ma); + distype = SEQUENT; + vptr = dsstmp; + dssflag = 1; + distran = DSS; + + keyname = "CNPIX1"; + BEGIN(VALUE); } YY_BREAK -case 17: +case 112: YY_RULE_SETUP -#line 316 "wcspih.l" +#line 1340 "wcspih.l" { + // DSS: LLH corner pixel coordinate 2. valtype = FLOAT; - if (pass == 2) vptr = &((*wcs)->pv); - npptr = npv; - BEGIN(PROJPn); + distype = SEQUENT; + vptr = dsstmp+1; + dssflag = 1; + distran = DSS; + + keyname = "CNPIX1"; + BEGIN(VALUE); } YY_BREAK -case 18: +case 113: YY_RULE_SETUP -#line 323 "wcspih.l" +#line 1352 "wcspih.l" { - valtype = STRING; - if (pass == 2) vptr = &((*wcs)->ps); - npptr = nps; - BEGIN(CCi_ma); + // DSS: plate centre x-coordinate in micron. + valtype = FLOAT; + distype = SEQUENT; + vptr = dsstmp+2; + dssflag = 1; + distran = DSS; + + keyname = "PPO3"; + BEGIN(VALUE); } YY_BREAK -case 19: +case 114: YY_RULE_SETUP -#line 330 "wcspih.l" +#line 1364 "wcspih.l" { - valtype = STRING; - if (pass == 2) vptr = &((*wcs)->cname); - BEGIN(CCCCCia); + // DSS: plate centre y-coordinate in micron. + valtype = FLOAT; + distype = SEQUENT; + vptr = dsstmp+3; + dssflag = 1; + distran = DSS; + + keyname = "PPO6"; + BEGIN(VALUE); } YY_BREAK -case 20: +case 115: YY_RULE_SETUP -#line 336 "wcspih.l" +#line 1376 "wcspih.l" { + // DSS: pixel x-dimension in micron. valtype = FLOAT; - if (pass == 2) vptr = &((*wcs)->crder); - BEGIN(CCCCCia); + distype = SEQUENT; + vptr = dsstmp+4; + dssflag = 1; + distran = DSS; + + keyname = "XPIXELSZ"; + BEGIN(VALUE); } YY_BREAK -case 21: +case 116: YY_RULE_SETUP -#line 342 "wcspih.l" +#line 1388 "wcspih.l" { + // DSS: pixel y-dimension in micron. valtype = FLOAT; - if (pass == 2) vptr = &((*wcs)->csyer); - BEGIN(CCCCCia); + distype = SEQUENT; + vptr = dsstmp+5; + dssflag = 1; + distran = DSS; + + keyname = "YPIXELSZ"; + BEGIN(VALUE); } YY_BREAK -case 22: +case 117: YY_RULE_SETUP -#line 348 "wcspih.l" +#line 1400 "wcspih.l" { - valtype = STRING; - if (pass == 2) vptr = (*wcs)->dateavg; - if (ctrl < -10) keep = wcspih_hdr - 80; - BEGIN(CCCCCCCC); + // DSS: plate centre, right ascension - hours. + valtype = FLOAT; + distype = SEQUENT; + vptr = dsstmp+6; + dssflag = 1; + distran = DSS; + + keyname = "PLTRAH"; + BEGIN(VALUE); } YY_BREAK -case 23: +case 118: YY_RULE_SETUP -#line 355 "wcspih.l" +#line 1412 "wcspih.l" { - valtype = STRING; - if (pass == 2) vptr = (*wcs)->dateobs; - if (ctrl < -10) keep = wcspih_hdr - 80; - BEGIN(CCCCCCCC); + // DSS: plate centre, right ascension - minutes. + valtype = FLOAT; + distype = SEQUENT; + vptr = dsstmp+7; + dssflag = 1; + distran = DSS; + + keyname = "PLTRAM"; + BEGIN(VALUE); } YY_BREAK -case 24: +case 119: YY_RULE_SETUP -#line 362 "wcspih.l" +#line 1424 "wcspih.l" { - sscanf(wcspihtext, "EPOCH%c", &a); - - if (a == ' ' || relax & WCSHDR_EPOCHa) { - valtype = FLOAT; - if (pass == 2) { - vptr = epoch; - if (a >= 'A') { - vptr = (void *)((double *)vptr + alts[a-'A'+1]); - } - } - - unput(' '); - BEGIN(CCCCCCCa); + // DSS: plate centre, right ascension - seconds. + valtype = FLOAT; + distype = SEQUENT; + vptr = dsstmp+8; + dssflag = 1; + distran = DSS; - } else if (relax & WCSHDR_reject) { - errmsg = "EPOCH keyword may not have an alternate version code"; - BEGIN(ERROR); + keyname = "PLTRAS"; + BEGIN(VALUE); + } + YY_BREAK +case 120: +YY_RULE_SETUP +#line 1436 "wcspih.l" +{ + // DSS: plate centre, declination - sign. + valtype = STRING; + distype = SEQUENT; + vptr = dsstmp+9; + dssflag = 1; + distran = DSS; - } else { - BEGIN(DISCARD); - } + keyname = "PLTDECSN"; + BEGIN(PLTDECSN); } YY_BREAK -case 25: +case 121: YY_RULE_SETUP -#line 386 "wcspih.l" +#line 1448 "wcspih.l" { + // DSS: plate centre, declination - degrees. valtype = FLOAT; - if (pass == 2) vptr = &((*wcs)->equinox); - BEGIN(CCCCCCCa); + distype = SEQUENT; + vptr = dsstmp+10; + dssflag = 1; + distran = DSS; + + keyname = "PLTDECD"; + BEGIN(VALUE); } YY_BREAK -case 26: +case 122: YY_RULE_SETUP -#line 392 "wcspih.l" +#line 1460 "wcspih.l" { + // DSS: plate centre, declination - arcmin. valtype = FLOAT; - if (pass == 2) vptr = &((*wcs)->mjdavg); - if (ctrl < -10) keep = wcspih_hdr - 80; - BEGIN(CCCCCCCC); + distype = SEQUENT; + vptr = dsstmp+11; + dssflag = 1; + distran = DSS; + + keyname = "PLTDECM"; + BEGIN(VALUE); } YY_BREAK -case 27: +case 123: YY_RULE_SETUP -#line 399 "wcspih.l" +#line 1472 "wcspih.l" { + // DSS: plate centre, declination - arcsec. valtype = FLOAT; - if (pass == 2) vptr = &((*wcs)->mjdobs); - if (ctrl < -10) keep = wcspih_hdr - 80; - BEGIN(CCCCCCCC); + distype = SEQUENT; + vptr = dsstmp+12; + dssflag = 1; + distran = DSS; + + keyname = "PLTDECS"; + BEGIN(VALUE); } YY_BREAK -case 28: +case 124: YY_RULE_SETUP -#line 406 "wcspih.l" +#line 1484 "wcspih.l" { - valtype = FLOAT; - if (pass == 2) vptr = (*wcs)->obsgeo; - if (ctrl < -10) keep = wcspih_hdr - 80; - BEGIN(CCCCCCCC); + // DSS: plate identification (insufficient to trigger DSS). + valtype = STRING; + distype = SEQUENT; + vptr = dsstmp+13; + dssflag = 2; + distran = 0; + + keyname = "PLATEID"; + BEGIN(VALUE); } YY_BREAK -case 29: +case 125: YY_RULE_SETUP -#line 413 "wcspih.l" +#line 1496 "wcspih.l" { - valtype = FLOAT; - if (pass == 2) vptr = (*wcs)->obsgeo + 1; - if (ctrl < -10) keep = wcspih_hdr - 80; - BEGIN(CCCCCCCC); + // DSS: axis 1 polynomial coefficient. + i = 1; + dssflag = 3; + + keyname = "AMDXm"; + BEGIN(DSSAMDXY); } YY_BREAK -case 30: +case 126: YY_RULE_SETUP -#line 420 "wcspih.l" +#line 1505 "wcspih.l" { - valtype = FLOAT; - if (pass == 2) vptr = (*wcs)->obsgeo + 2; - if (ctrl < -10) keep = wcspih_hdr - 80; - BEGIN(CCCCCCCC); + // DSS: axis 2 polynomial coefficient. + i = 2; + dssflag = 3; + + keyname = "AMDYm"; + BEGIN(DSSAMDXY); } YY_BREAK -case 31: +case 127: YY_RULE_SETUP -#line 427 "wcspih.l" +#line 1514 "wcspih.l" { + // TNX or ZPX: string-encoded data array. + sscanf(yytext, "WAT%d_%d", &i, &m); + if (watn < m) watn = m; + watflag = 1; + valtype = STRING; - if (pass == 2) vptr = (*wcs)->radesys; - BEGIN(CCCCCCCa); + distype = SEQUENT; + vptr = wat[i-1] + 68*(m-1); + + a = ' '; + distran = WAT; + + keyname = "WATi_m"; + BEGIN(VALUE); } YY_BREAK -case 32: +case 128: YY_RULE_SETUP -#line 433 "wcspih.l" +#line 1531 "wcspih.l" { - if (relax & WCSHDR_RADECSYS) { - valtype = STRING; - if (pass == 2) vptr = (*wcs)->radesys; - unput(' '); - BEGIN(CCCCCCCa); - - } else if (relax & WCSHDR_reject) { - errmsg = "RADECSYS is non-standard, use RADESYSa"; + if (yyextra->nkeyrec) { + yyextra->nkeyrec = 0; + errmsg = "keyrecords following the END keyrecord were ignored"; BEGIN(ERROR); - } else { BEGIN(DISCARD); } } YY_BREAK -case 33: +case 129: YY_RULE_SETUP -#line 449 "wcspih.l" +#line 1541 "wcspih.l" { - valtype = STRING; - if (pass == 2) vptr = (*wcs)->specsys; - BEGIN(CCCCCCCa); + BEGIN(DISCARD); } YY_BREAK -case 34: +case 130: +#line 1546 "wcspih.l" +case 131: +#line 1547 "wcspih.l" +case 132: +#line 1548 "wcspih.l" +case 133: YY_RULE_SETUP -#line 455 "wcspih.l" +#line 1548 "wcspih.l" { - valtype = STRING; - if (pass == 2) vptr = (*wcs)->ssysobs; - BEGIN(CCCCCCCa); + sscanf(yytext, "%d%c", &i, &a); + BEGIN(VALUE); } YY_BREAK -case 35: +case 134: +#line 1554 "wcspih.l" +case 135: +#line 1555 "wcspih.l" +case 136: +#line 1556 "wcspih.l" +case 137: +#line 1557 "wcspih.l" +case 138: +#line 1558 "wcspih.l" +case 139: +#line 1559 "wcspih.l" +case 140: YY_RULE_SETUP -#line 461 "wcspih.l" +#line 1559 "wcspih.l" { - valtype = STRING; - if (pass == 2) vptr = (*wcs)->ssyssrc; - BEGIN(CCCCCCCa); + if (relax & WCSHDR_reject) { + // Violates the basic FITS standard. + errmsg = "indices in parameterized keywords must not have " + "leading zeroes"; + BEGIN(ERROR); + + } else { + // Pretend we don't recognize it. + BEGIN(DISCARD); + } } YY_BREAK -case 36: +case 141: +#line 1573 "wcspih.l" +case 142: +#line 1574 "wcspih.l" +case 143: +#line 1575 "wcspih.l" +case 144: +#line 1576 "wcspih.l" +case 145: +#line 1577 "wcspih.l" +case 146: +#line 1578 "wcspih.l" +case 147: +#line 1579 "wcspih.l" +case 148: +#line 1580 "wcspih.l" +case 149: YY_RULE_SETUP -#line 467 "wcspih.l" +#line 1580 "wcspih.l" { - valtype = FLOAT; - if (pass == 2) vptr = &((*wcs)->velangl); - BEGIN(CCCCCCCa); + // Anything that has fallen through to this point must contain + // an invalid axis number. + errmsg = "axis number must exceed 0"; + BEGIN(ERROR); } YY_BREAK -case 37: +case 150: YY_RULE_SETUP -#line 473 "wcspih.l" +#line 1587 "wcspih.l" { - valtype = FLOAT; - if (pass == 2) vptr = &((*wcs)->velosys); - BEGIN(CCCCCCCa); + // Let it go. + BEGIN(DISCARD); } YY_BREAK -case 38: +case 151: YY_RULE_SETUP -#line 479 "wcspih.l" +#line 1592 "wcspih.l" { - sscanf(wcspihtext, "VELREF%c", &a); - - if (a == ' ' || relax & WCSHDR_VELREFa) { - valtype = INTEGER; - if (pass == 2) vptr = &((*wcs)->velref); + if (relax & WCSHDR_reject) { + // Looks too much like a FITS WCS keyword not to flag it. + errmsg = errtxt; + sprintf(errmsg, "keyword looks very much like %s but isn't", + keyname); + BEGIN(ERROR); - unput(a); - BEGIN(CCCCCCCa); + } else { + // Let it go. + BEGIN(DISCARD); + } + } + YY_BREAK +case 152: +#line 1607 "wcspih.l" +case 153: +#line 1608 "wcspih.l" +case 154: +#line 1609 "wcspih.l" +case 155: +YY_RULE_SETUP +#line 1609 "wcspih.l" +{ + sscanf(yytext, "%d_%d%c", &i, &j, &a); + BEGIN(VALUE); + } + YY_BREAK +case 156: +#line 1616 "wcspih.l" +case 157: +#line 1617 "wcspih.l" +case 158: +#line 1618 "wcspih.l" +case 159: +#line 1619 "wcspih.l" +case 160: +#line 1620 "wcspih.l" +case 161: +#line 1621 "wcspih.l" +case 162: +#line 1622 "wcspih.l" +case 163: +#line 1623 "wcspih.l" +case 164: +#line 1624 "wcspih.l" +case 165: +#line 1625 "wcspih.l" +case 166: +#line 1626 "wcspih.l" +case 167: +#line 1627 "wcspih.l" +case 168: +#line 1628 "wcspih.l" +case 169: +#line 1629 "wcspih.l" +case 170: +#line 1630 "wcspih.l" +case 171: +#line 1631 "wcspih.l" +case 172: +#line 1632 "wcspih.l" +case 173: +#line 1633 "wcspih.l" +case 174: +#line 1634 "wcspih.l" +case 175: +#line 1635 "wcspih.l" +case 176: +YY_RULE_SETUP +#line 1635 "wcspih.l" +{ + if (((altlin == 1) && (relax & WCSHDR_PC0i_0ja)) || + ((altlin == 2) && (relax & WCSHDR_CD0i_0ja))) { + sscanf(yytext, "%d_%d%c", &i, &j, &a); + BEGIN(VALUE); } else if (relax & WCSHDR_reject) { - errmsg = "VELREF keyword may not have an alternate version code"; + errmsg = "indices in parameterized keywords must not have " + "leading zeroes"; BEGIN(ERROR); } else { + // Pretend we don't recognize it. BEGIN(DISCARD); } } YY_BREAK -case 39: +case 177: +#line 1653 "wcspih.l" +case 178: +#line 1654 "wcspih.l" +case 179: +#line 1655 "wcspih.l" +case 180: +#line 1656 "wcspih.l" +case 181: +#line 1657 "wcspih.l" +case 182: +#line 1658 "wcspih.l" +case 183: +#line 1659 "wcspih.l" +case 184: +#line 1660 "wcspih.l" +case 185: +#line 1661 "wcspih.l" +case 186: YY_RULE_SETUP -#line 498 "wcspih.l" +#line 1661 "wcspih.l" { - sscanf(wcspihtext, "VSOURCE%c", &a); - - if (relax & WCSHDR_VSOURCE) { - valtype = FLOAT; - if (pass == 2) { - vptr = vsource; - if (a >= 'A') { - vptr = (void *)((double *)vptr + alts[a-'A'+1]); - } - } - - unput(' '); - BEGIN(CCCCCCCa); + // Anything that has fallen through to this point must contain + // an invalid axis number. + errmsg = "axis number must exceed 0"; + BEGIN(ERROR); + } + YY_BREAK +case 187: +#line 1669 "wcspih.l" +case 188: +#line 1670 "wcspih.l" +case 189: +#line 1671 "wcspih.l" +case 190: +#line 1672 "wcspih.l" +case 191: +#line 1673 "wcspih.l" +case 192: +#line 1674 "wcspih.l" +case 193: +#line 1675 "wcspih.l" +case 194: +#line 1676 "wcspih.l" +case 195: +#line 1677 "wcspih.l" +case 196: +YY_RULE_SETUP +#line 1677 "wcspih.l" +{ + errmsg = errtxt; + sprintf(errmsg, "%s keyword must use an underscore, not a dash", + keyname); + BEGIN(ERROR); + } + YY_BREAK +case 197: +YY_RULE_SETUP +#line 1684 "wcspih.l" +{ + // This covers the defunct forms CD00i00j and PC00i00j. + if (((altlin == 1) && (relax & WCSHDR_PC00i00j)) || + ((altlin == 2) && (relax & WCSHDR_CD00i00j))) { + sscanf(yytext, "%3d%3d", &i, &j); + a = ' '; + BEGIN(VALUE); } else if (relax & WCSHDR_reject) { - errmsg = "Deprecated VSOURCEa keyword rejected"; + errmsg = errtxt; + sprintf(errmsg, + "this form of the %s keyword is deprecated, use %s", + keyname, keyname); BEGIN(ERROR); } else { + // Pretend we don't recognize it. BEGIN(DISCARD); } } YY_BREAK -case 40: +case 198: YY_RULE_SETUP -#line 522 "wcspih.l" +#line 1705 "wcspih.l" { - valtype = STRING; - if (pass == 2) vptr = (*wcs)->wcsname; - BEGIN(CCCCCCCa); + BEGIN(DISCARD); } YY_BREAK -case 41: +case 199: +#line 1710 "wcspih.l" +case 200: YY_RULE_SETUP -#line 528 "wcspih.l" +#line 1710 "wcspih.l" { - valtype = FLOAT; - if (pass == 2) vptr = &((*wcs)->zsource); - BEGIN(CCCCCCCa); + if (YY_START == CCCCCCCa) { + sscanf(yytext, "%c", &a); + } else { + unput(yytext[0]); + a = 0; + } + + BEGIN(VALUE); } YY_BREAK -case 42: +case 201: YY_RULE_SETUP -#line 534 "wcspih.l" +#line 1721 "wcspih.l" { - yyless(0); - if (wcspih_nkeyrec) { - wcspih_nkeyrec = 0; - errmsg = "Keyrecords following the END keyrecord were ignored"; + if (relax & WCSHDR_reject) { + // Looks too much like a FITS WCS keyword not to flag it. + errmsg = errtxt; + sprintf(errmsg, "invalid alternate code, keyword resembles %s " + "but isn't", keyname); BEGIN(ERROR); + } else { + // Pretend we don't recognize it. BEGIN(DISCARD); } } YY_BREAK -case 43: -YY_RULE_SETUP -#line 545 "wcspih.l" -{ - BEGIN(DISCARD); - } - YY_BREAK -case 44: -#line 550 "wcspih.l" -case 45: +case 202: +#line 1736 "wcspih.l" +case 203: +#line 1737 "wcspih.l" +case 204: +#line 1738 "wcspih.l" +case 205: YY_RULE_SETUP -#line 550 "wcspih.l" +#line 1738 "wcspih.l" { - sscanf(wcspihtext, "%d%c", &i, &a); - idx = i - 1; + sscanf(yytext, "%d_%d%c", &i, &m, &a); BEGIN(VALUE); } YY_BREAK -case 46: +case 206: +#line 1744 "wcspih.l" +case 207: +#line 1745 "wcspih.l" +case 208: +#line 1746 "wcspih.l" +case 209: +#line 1747 "wcspih.l" +case 210: +#line 1748 "wcspih.l" +case 211: +#line 1749 "wcspih.l" +case 212: +#line 1750 "wcspih.l" +case 213: +#line 1751 "wcspih.l" +case 214: +#line 1752 "wcspih.l" +case 215: +#line 1753 "wcspih.l" +case 216: +#line 1754 "wcspih.l" +case 217: +#line 1755 "wcspih.l" +case 218: +#line 1756 "wcspih.l" +case 219: +#line 1757 "wcspih.l" +case 220: +#line 1758 "wcspih.l" +case 221: +#line 1759 "wcspih.l" +case 222: +#line 1760 "wcspih.l" +case 223: +#line 1761 "wcspih.l" +case 224: +#line 1762 "wcspih.l" +case 225: +#line 1763 "wcspih.l" +case 226: YY_RULE_SETUP -#line 556 "wcspih.l" +#line 1763 "wcspih.l" { - /* Invalid axis number will be caught by . */ - sscanf(wcspihtext, "%3d", &i); - BEGIN(VALUE); + if (((valtype == FLOAT) && (relax & WCSHDR_PV0i_0ma)) || + ((valtype == STRING) && (relax & WCSHDR_PS0i_0ma))) { + sscanf(yytext, "%d_%d%c", &i, &m, &a); + BEGIN(VALUE); + + } else if (relax & WCSHDR_reject) { + errmsg = "indices in parameterized keywords must not have " + "leading zeroes"; + BEGIN(ERROR); + + } else { + // Pretend we don't recognize it. + BEGIN(DISCARD); + } } YY_BREAK -case 47: +case 227: +#line 1781 "wcspih.l" +case 228: +#line 1782 "wcspih.l" +case 229: +#line 1783 "wcspih.l" +case 230: +#line 1784 "wcspih.l" +case 231: +#line 1785 "wcspih.l" +case 232: +#line 1786 "wcspih.l" +case 233: +#line 1787 "wcspih.l" +case 234: +#line 1788 "wcspih.l" +case 235: +#line 1789 "wcspih.l" +case 236: YY_RULE_SETUP -#line 562 "wcspih.l" +#line 1789 "wcspih.l" { - BEGIN(DISCARD); + // Anything that has fallen through to this point must contain + // an invalid axis number. + errmsg = "axis number must exceed 0"; + BEGIN(ERROR); } YY_BREAK -case 48: -#line 567 "wcspih.l" -case 49: -#line 568 "wcspih.l" -case 50: -#line 569 "wcspih.l" -case 51: +case 237: +#line 1797 "wcspih.l" +case 238: +#line 1798 "wcspih.l" +case 239: +#line 1799 "wcspih.l" +case 240: +#line 1800 "wcspih.l" +case 241: +#line 1801 "wcspih.l" +case 242: +#line 1802 "wcspih.l" +case 243: +#line 1803 "wcspih.l" +case 244: +#line 1804 "wcspih.l" +case 245: +#line 1805 "wcspih.l" +case 246: YY_RULE_SETUP -#line 569 "wcspih.l" +#line 1805 "wcspih.l" { - sscanf(wcspihtext, "%d_%d%c", &i, &j, &a); - if (pass == 2) { - wcsp = *wcs; - if (a != ' ') { - wcsp += alts[a-'A'+1]; - } - - idx = (i-1)*(wcsp->naxis) + j - 1; - } - BEGIN(VALUE); + errmsg = errtxt; + sprintf(errmsg, "%s keyword must use an underscore, not a dash", + keyname); + BEGIN(ERROR); } YY_BREAK -case 52: -#line 583 "wcspih.l" -case 53: -#line 584 "wcspih.l" -case 54: -#line 585 "wcspih.l" -case 55: -#line 586 "wcspih.l" -case 56: -#line 587 "wcspih.l" -case 57: +case 247: YY_RULE_SETUP -#line 587 "wcspih.l" +#line 1812 "wcspih.l" { - /* Invalid axis numbers will be caught by . */ - sscanf(wcspihtext, "%d_%d", &i, &j); - BEGIN(VALUE); + BEGIN(DISCARD); } YY_BREAK -case 58: +case 248: +#line 1817 "wcspih.l" +case 249: +#line 1818 "wcspih.l" +case 250: YY_RULE_SETUP -#line 593 "wcspih.l" +#line 1818 "wcspih.l" { - /* This covers the defunct forms CD00i00j and PC00i00j. */ - if (((relax & WCSHDR_PC00i00j) && (altlin == 1)) || - ((relax & WCSHDR_CD00i00j) && (altlin == 2))) { - sscanf(wcspihtext, "%3d%3d", &i, &j); - a = ' '; - if (pass == 2) { - idx = (i-1)*((*wcs)->naxis) + j - 1; - } - BEGIN(VALUE); + a = ' '; + sscanf(yytext, "%d%c", &i, &a); + + if (relax & WCSHDR_strict) { + errmsg = "the CROTAn keyword is deprecated, use PCi_ja"; + BEGIN(ERROR); + + } else if ((a == ' ') || (relax & WCSHDR_CROTAia)) { + yyless(0); + BEGIN(CCCCCia); } else if (relax & WCSHDR_reject) { - errmsg = errtxt; - sprintf(errmsg, "Defunct form of %si_ja keyword", - (altlin==1) ? "PC" : "CD"); + errmsg = "CROTAn keyword may not have an alternate version code"; BEGIN(ERROR); } else { + // Pretend we don't recognize it. BEGIN(DISCARD); } } YY_BREAK -case 59: +case 251: YY_RULE_SETUP -#line 615 "wcspih.l" +#line 1840 "wcspih.l" { - BEGIN(DISCARD); + yyless(0); + BEGIN(CCCCCia); } YY_BREAK -case 60: -#line 620 "wcspih.l" -case 61: +case 252: YY_RULE_SETUP -#line 620 "wcspih.l" +#line 1845 "wcspih.l" { - sscanf(wcspihtext, "%d%c", &i, &a); - if (a == ' ' || relax & WCSHDR_CROTAia) { - idx = i - 1; + if (relax & WCSHDR_PROJPn) { + sscanf(yytext, "%d", &m); + i = 0; + a = ' '; BEGIN(VALUE); } else if (relax & WCSHDR_reject) { - errmsg = "CROTAn keyword may not have an alternate version code"; + errmsg = "the PROJPn keyword is deprecated, use PVi_ma"; BEGIN(ERROR); } else { @@ -9188,132 +24123,120 @@ YY_RULE_SETUP } } YY_BREAK -case 62: +case 253: +#line 1862 "wcspih.l" +case 254: YY_RULE_SETUP -#line 635 "wcspih.l" +#line 1862 "wcspih.l" { - sscanf(wcspihtext, "%d", &i); - a = ' '; - idx = i - 1; - BEGIN(VALUE); + if (relax & (WCSHDR_PROJPn | WCSHDR_reject)) { + errmsg = "invalid PROJPn keyword"; + BEGIN(ERROR); + + } else { + BEGIN(DISCARD); + } } YY_BREAK -case 63: +case 255: YY_RULE_SETUP -#line 642 "wcspih.l" +#line 1872 "wcspih.l" { BEGIN(DISCARD); } YY_BREAK -case 64: -#line 647 "wcspih.l" -case 65: +case 256: +#line 1877 "wcspih.l" +case 257: YY_RULE_SETUP -#line 647 "wcspih.l" +#line 1877 "wcspih.l" { - idx = -1; + // SIP keywords. + valtype = FLOAT; + distype = PRIOR; + vptr = &(distem.dp); + npptr = ndp; - if (YY_START == CCCCCCCa) { - sscanf(wcspihtext, "%c", &a); - } else { - unput(wcspihtext[0]); - a = 0; - } + a = ' '; + distran = SIP; + + sscanf(yytext, "%d_%d", &p, &q); BEGIN(VALUE); } YY_BREAK -case 66: +case 258: +#line 1892 "wcspih.l" +case 259: YY_RULE_SETUP -#line 659 "wcspih.l" +#line 1892 "wcspih.l" { BEGIN(DISCARD); } YY_BREAK -case 67: -#line 664 "wcspih.l" -case 68: -#line 665 "wcspih.l" -case 69: -#line 666 "wcspih.l" -case 70: -YY_RULE_SETUP -#line 666 "wcspih.l" -{ - sscanf(wcspihtext, "%d_%d%c", &i, &m, &a); - idx = -1; - BEGIN(VALUE); - } - YY_BREAK -case 71: -#line 673 "wcspih.l" -case 72: -#line 674 "wcspih.l" -case 73: -#line 675 "wcspih.l" -case 74: -#line 676 "wcspih.l" -case 75: -#line 677 "wcspih.l" -case 76: +case 260: +#line 1897 "wcspih.l" +case 261: YY_RULE_SETUP -#line 677 "wcspih.l" +#line 1897 "wcspih.l" { - /* Invalid parameters will be caught by . */ - sscanf(wcspihtext, "%d_%d", &i, &m); + // DSS keywords. + valtype = FLOAT; + distype = SEQUENT; + vptr = &(distem.dp); + npptr = ndq; + + a = ' '; + distran = DSS; + + sscanf(yytext, "%d", &m); BEGIN(VALUE); } YY_BREAK -case 77: +case 262: YY_RULE_SETUP -#line 683 "wcspih.l" +#line 1911 "wcspih.l" { BEGIN(DISCARD); } YY_BREAK -case 78: +case 263: +/* rule 263 can match eol */ YY_RULE_SETUP -#line 687 "wcspih.l" +#line 1915 "wcspih.l" { - if (relax & WCSHDR_PROJPn) { - sscanf(wcspihtext, "%d", &m); - i = 0; - a = ' '; - idx = -1; - BEGIN(VALUE); - - } else if (relax & WCSHDR_reject) { - errmsg = "Defunct PROJPn keyword rejected"; - BEGIN(ERROR); - - } else { - BEGIN(DISCARD); + // Special handling for this iconic DSS keyword. + if (1 < ipass) { + // Look for a minus sign. + sscanf(yytext, "= '%s", strtmp); + dbltmp = strcmp(strtmp, "-") ? 1.0 : -1.0; } + + BEGIN(COMMENT); } YY_BREAK -case 79: +case 264: YY_RULE_SETUP -#line 704 "wcspih.l" +#line 1926 "wcspih.l" { BEGIN(DISCARD); } YY_BREAK -case 80: +case 265: YY_RULE_SETUP -#line 708 "wcspih.l" +#line 1930 "wcspih.l" { - /* Do checks on i, j & m. */ - if (i > 99 || j > 99 || m > 99) { + // Do checks on i, j & m. + if (99 < i || 99 < j || 99 < m) { if (relax & WCSHDR_reject) { - errmsg = errtxt; - if (i > 99 || j > 99) { - sprintf(errmsg, "Axis number exceeds 99"); + if (99 < i || 99 < j) { + errmsg = "axis number exceeds 99"; } else if (m > 99) { - sprintf(errmsg, "Parameter number exceeds 99"); + errmsg = "parameter number exceeds 99"; } BEGIN(ERROR); } else { - /* Pretend we don't recognize it. */ + // Pretend we don't recognize it. BEGIN(DISCARD); } @@ -9322,380 +24245,707 @@ YY_RULE_SETUP BEGIN(INTEGER_VAL); } else if (valtype == FLOAT) { BEGIN(FLOAT_VAL); + } else if (valtype == FLOAT2) { + BEGIN(FLOAT2_VAL); } else if (valtype == STRING) { BEGIN(STRING_VAL); + } else if (valtype == RECORD) { + BEGIN(RECORD_VAL); } else { errmsg = errtxt; - sprintf(errmsg, "Internal parser ERROR, bad data type: %d", + sprintf(errmsg, "internal parser ERROR, bad data type: %d", valtype); BEGIN(ERROR); } } } YY_BREAK -case 81: +case 266: YY_RULE_SETUP -#line 741 "wcspih.l" +#line 1966 "wcspih.l" { - errmsg = "Invalid KEYWORD = VALUE syntax"; + errmsg = "invalid KEYWORD = VALUE syntax"; BEGIN(ERROR); } YY_BREAK -case 82: +case 267: YY_RULE_SETUP -#line 746 "wcspih.l" +#line 1971 "wcspih.l" { - if (pass == 1) { - wcspih_naxes(naxis, i, j, a, alts, npptr); - BEGIN(FLUSH); + if (ipass == 1) { + BEGIN(COMMENT); } else { - if (vptr) { - /* Determine the coordinate representation. */ - for (ialt = 0; ialt < *nwcs; ialt++) { - /* The loop here is for keywords that apply */ - /* to every alternate; these have a == 0. */ - if (a >= 'A') { - ialt = alts[a-'A'+1]; - } - - wptr = vptr; - if (ialt) { - voff = (char *)(*wcs+ialt) - (char *)(*wcs); - wptr = (void *)((char *)vptr + voff); - } - - /* Apply keyword parameterization. */ - if (idx >= 0) { - wptr = *((int **)wptr) + idx; - } - - /* Read the keyvalue. */ - sscanf(wcspihtext, "%d", (int *)wptr); + // Read the keyvalue. + sscanf(yytext, "%d", &inttmp); - if (a) break; - } - - BEGIN(COMMENT); + BEGIN(COMMENT); + } + } + YY_BREAK +case 268: +YY_RULE_SETUP +#line 1983 "wcspih.l" +{ + errmsg = "an integer value was expected"; + BEGIN(ERROR); + } + YY_BREAK +case 269: +YY_RULE_SETUP +#line 1988 "wcspih.l" +{ + if (ipass == 1) { + BEGIN(COMMENT); - } else { - errmsg = "Internal parser ERROR, null int pointer"; + } else { + // Read the keyvalue. + wcsutil_str2double(yytext, &dbltmp); + + if (chekval && chekval(dbltmp)) { + errmsg = "invalid keyvalue"; BEGIN(ERROR); + } else { + BEGIN(COMMENT); } } } YY_BREAK -case 83: +case 270: YY_RULE_SETUP -#line 787 "wcspih.l" +#line 2005 "wcspih.l" { - errmsg = "An integer value was expected"; + errmsg = "a floating-point value was expected"; BEGIN(ERROR); } YY_BREAK -case 84: +case 271: YY_RULE_SETUP -#line 792 "wcspih.l" +#line 2010 "wcspih.l" { - if (pass == 1) { - wcspih_naxes(naxis, i, j, a, alts, npptr); - BEGIN(FLUSH); + if (ipass == 1) { + BEGIN(COMMENT); } else { - if (vptr) { - /* Determine the coordinate representation. */ - for (ialt = 0; ialt < *nwcs; ialt++) { - /* The loop here is for keywords like MJD-OBS that */ - /* apply to every alternate; these have a == 0. */ - if (a >= 'A') { - ialt = alts[a-'A'+1]; - } - - wptr = vptr; - if (ialt) { - voff = (char *)(*wcs+ialt) - (char *)(*wcs); - wptr = (void *)((char *)vptr + voff); - } + // Read the keyvalue as integer and fractional parts. + wcsutil_str2double2(yytext, dbl2tmp); - /* Apply keyword parameterization. */ - if (idx >= 0) { - wptr = *((double **)wptr) + idx; - - } else if (npptr == npv) { - ipx = (*wcs+ialt)->npv++; - (*wcs+ialt)->pv[ipx].i = i; - (*wcs+ialt)->pv[ipx].m = m; - wptr = &((*wcs+ialt)->pv[ipx].value); - } + BEGIN(COMMENT); + } + } + YY_BREAK +case 272: +YY_RULE_SETUP +#line 2022 "wcspih.l" +{ + errmsg = "a floating-point value was expected"; + BEGIN(ERROR); + } + YY_BREAK +case 273: +/* rule 273 can match eol */ +YY_RULE_SETUP +#line 2027 "wcspih.l" +{ + if (ipass == 1) { + BEGIN(COMMENT); - /* Read the keyvalue. */ - wcsutil_str2double(wcspihtext, "%lf", (double *)wptr); + } else { + strtmp[0] = '\0'; + int nch = yyleng - 2; + if (0 < nch && nch < 80) { + // Copy the keyvalue minus the quotes. + strncpy(strtmp, yytext+1, nch); + strtmp[nch] = '\0'; - /* Flag the presence of PCi_ja, or CDi_ja and/or CROTAia. */ - if (altlin) { - (*wcs+ialt)->altlin |= altlin; - altlin = 0; + // Strip off trailing blanks. + for (int jx = nch-1; jx >= 0; jx--) { + if (strtmp[jx] != ' ') { + break; } + strtmp[jx] = '\0'; + } + } - if (a) break; + // Squeeze out repeated quotes. + int ix = 0; + for (int jx = 0; jx < 72; jx++) { + if (ix < jx) { + strtmp[ix] = strtmp[jx]; } - BEGIN(COMMENT); + if (strtmp[jx] == '\0') { + break; + } else if (strtmp[jx] == '\'' && strtmp[jx+1] == '\'') { + jx++; + } - } else { - errmsg = "Internal parser ERROR, null float pointer"; - BEGIN(ERROR); + ix++; } + + BEGIN(COMMENT); } } YY_BREAK -case 85: +case 274: YY_RULE_SETUP -#line 845 "wcspih.l" +#line 2068 "wcspih.l" { - errmsg = "A floating-point value was expected"; + errmsg = "a string value was expected"; BEGIN(ERROR); } YY_BREAK -case 86: -/* rule 86 can match eol */ +case 275: +/* rule 275 can match eol */ YY_RULE_SETUP -#line 850 "wcspih.l" +#line 2073 "wcspih.l" { - if (pass == 1) { - wcspih_naxes(naxis, i, j, a, alts, npptr); - BEGIN(FLUSH); + if (ipass == 1) { + BEGIN(COMMENT); } else { - if (vptr) { - /* Determine the coordinate representation. */ - for (ialt = 0; ialt < *nwcs; ialt++) { - /* The loop here is for keywords like DATE-OBS that */ - /* apply to every alternate; these have a == 0. */ - if (a >= 'A') { - ialt = alts[a-'A'+1]; - } + yyless(1); - wptr = vptr; - if (ialt) { - voff = (char *)(*wcs+ialt) - (char *)(*wcs); - wptr = (void *)((char *)vptr + voff); - } + BEGIN(RECFIELD); + } + } + YY_BREAK +case 276: +YY_RULE_SETUP +#line 2084 "wcspih.l" +{ + errmsg = "a record was expected"; + BEGIN(ERROR); + } + YY_BREAK +case 277: +YY_RULE_SETUP +#line 2089 "wcspih.l" +{ + strncpy(strtmp, yytext, 72); + strtmp[72] = '\0'; + BEGIN(RECCOLON); + } + YY_BREAK +case 278: +YY_RULE_SETUP +#line 2095 "wcspih.l" +{ + errmsg = "invalid record field"; + BEGIN(ERROR); + } + YY_BREAK +case 279: +YY_RULE_SETUP +#line 2100 "wcspih.l" +{ + BEGIN(RECVALUE); + } + YY_BREAK +case 280: +YY_RULE_SETUP +#line 2104 "wcspih.l" +{ + errmsg = "invalid record syntax"; + BEGIN(ERROR); + } + YY_BREAK +case 281: +YY_RULE_SETUP +#line 2109 "wcspih.l" +{ + rectype = 0; + sscanf(yytext, "%d", &inttmp); + BEGIN(RECEND); + } + YY_BREAK +case 282: +YY_RULE_SETUP +#line 2115 "wcspih.l" +{ + rectype = 1; + wcsutil_str2double(yytext, &dbltmp); + BEGIN(RECEND); + } + YY_BREAK +case 283: +YY_RULE_SETUP +#line 2121 "wcspih.l" +{ + errmsg = "invalid record value"; + BEGIN(ERROR); + } + YY_BREAK +case 284: +YY_RULE_SETUP +#line 2126 "wcspih.l" +{ + BEGIN(COMMENT); + } + YY_BREAK +case 285: +*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ +yyg->yy_c_buf_p = yy_cp -= 1; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +YY_RULE_SETUP +#line 2130 "wcspih.l" +{ + if (ipass == 1) { + // Do first-pass bookkeeping. + wcspih_pass1(naxis, i, j, a, distype, alts, dpq, npptr); + BEGIN(FLUSH); + + } else if (*wcs) { + // Store the value now that the keyrecord has been validated. + int gotone = 0; + for (int ialt = 0; ialt < *nwcs; ialt++) { + // The loop here is for keywords that apply + // to every alternate; these have a == 0. + if (a >= 'A') { + ialt = alts[a-'A'+1]; + if (ialt < 0) break; + } + gotone = 1; - /* Apply keyword parameterization. */ - if (idx >= 0) { - wptr = *((char (**)[72])wptr) + idx; + if (vptr) { + if (sipflag) { + // Translate a SIP keyword into DPja. + struct disprm *disp = (*wcs)->lin.dispre; + int ipx = (disp->ndp)++; - } else if (npptr == nps) { - ipx = (*wcs+ialt)->nps++; - (*wcs+ialt)->ps[ipx].i = i; - (*wcs+ialt)->ps[ipx].m = m; - wptr = (*wcs+ialt)->ps[ipx].value; - } + // SIP doesn't have alternates. + char keyword[16]; + sprintf(keyword, "DP%d", i); + sprintf(strtmp, "SIP.%s.%d_%d", (sipflag==2)?"FWD":"REV", + p, q); + if (valtype == INTEGER) { + dpfill(disp->dp+ipx, keyword, strtmp, i, 0, inttmp, 0.0); + } else { + dpfill(disp->dp+ipx, keyword, strtmp, i, 1, 0, dbltmp); + } - /* Read the keyvalue. */ - cptr = (char *)wptr; - strcpy(cptr, wcspihtext+1); + } else if (dssflag) { + // All DSS keywords require special handling. + if (dssflag == 1) { + // Temporary parameter for DSS used by wcspih_final(). + *((double *)vptr) = dbltmp; - /* Squeeze out repeated quotes. */ - ix = 0; - for (jx = 0; jx < 72; jx++) { - if (ix < jx) { - cptr[ix] = cptr[jx]; + } else if (dssflag == 2) { + // Temporary parameter for DSS used by wcspih_final(). + strcpy((char *)vptr, strtmp); + + } else { + // Translate a DSS keyword into DQia. + if (m <= 13 || dbltmp != 0.0) { + struct disprm *disp = (*wcs)->lin.disseq; + int ipx = (disp->ndp)++; + + // DSS doesn't have alternates. + char keyword[16]; + sprintf(keyword, "DQ%d", i); + sprintf(strtmp, "DSS.AMD.%d", m); + dpfill(disp->dp+ipx, keyword, strtmp, i, 1, 0, dbltmp); + + // Also required by wcspih_final(). + if (m <= 3) { + dsstmp[13+(i-1)*3+m] = dbltmp; + } + } } - if (cptr[jx] == '\0') { - if (ix) cptr[ix-1] = '\0'; - break; - } else if (cptr[jx] == '\'' && cptr[jx+1] == '\'') { - jx++; + } else if (watflag) { + // String array for TNX and ZPX used by wcspih_final(). + strcpy((char *)vptr, strtmp); + + } else { + // An "ordinary" keyword. + struct wcsprm *wcsp = *wcs + ialt; + struct disprm *disp; + void *wptr; + ptrdiff_t voff; + if (auxprm) { + // Additional auxiliary parameter. + struct auxprm *auxp = wcsp->aux; + voff = (char *)vptr - (char *)(&auxtem); + wptr = (void *)((char *)auxp + voff); + + } else if (distype) { + // Distortion parameter of some kind. + if (distype == PRIOR) { + // Prior distortion. + disp = wcsp->lin.dispre; + } else { + // Sequent distortion. + disp = wcsp->lin.disseq; + } + voff = (char *)vptr - (char *)(&distem); + wptr = (void *)((char *)disp + voff); + + } else { + // A parameter that lives directly in wcsprm. + voff = (char *)vptr - (char *)(&wcstem); + wptr = (void *)((char *)wcsp + voff); } - ix++; + if (valtype == INTEGER) { + *((int *)wptr) = inttmp; + + } else if (valtype == FLOAT) { + // Apply keyword parameterization. + if (npptr == npv) { + int ipx = (wcsp->npv)++; + wcsp->pv[ipx].i = i; + wcsp->pv[ipx].m = m; + wptr = &(wcsp->pv[ipx].value); + + } else if (j) { + wptr = *((double **)wptr) + (i - 1)*(wcsp->naxis) + + (j - 1); + + } else if (i) { + wptr = *((double **)wptr) + (i - 1); + } + + if (special) { + special(wptr, &dbltmp); + } else { + *((double *)wptr) = dbltmp; + } + + // Flag presence of PCi_ja, or CDi_ja and/or CROTAia. + if (altlin) { + wcsp->altlin |= altlin; + altlin = 0; + } + + } else if (valtype == FLOAT2) { + // Split MJDREF and JDREF into integer and fraction. + if (special) { + special(wptr, dbl2tmp); + } else { + *((double *)wptr) = dbl2tmp[0]; + *((double *)wptr + 1) = dbl2tmp[1]; + } + + } else if (valtype == STRING) { + // Apply keyword parameterization. + if (npptr == nps) { + int ipx = (wcsp->nps)++; + wcsp->ps[ipx].i = i; + wcsp->ps[ipx].m = m; + wptr = wcsp->ps[ipx].value; + + } else if (j) { + wptr = *((char (**)[72])wptr) + + (i - 1)*(wcsp->naxis) + (j - 1); + + } else if (i) { + wptr = *((char (**)[72])wptr) + (i - 1); + } + + char *cptr = (char *)wptr; + strcpy(cptr, strtmp); + + } else if (valtype == RECORD) { + int ipx = (disp->ndp)++; + + char keyword[16]; + if (a == ' ') { + sprintf(keyword, "%.2s%d", keyname, i); + } else { + sprintf(keyword, "%.2s%d%c", keyname, i, a); + } + + dpfill(disp->dp+ipx, keyword, strtmp, i, rectype, inttmp, + dbltmp); + } } + } + + if (a) break; + } - if (a) break; + if (gotone) { + nvalid++; + if (ctrl == 4) { + if (distran || dssflag) { + wcsfprintf(stderr, "%.80s\n Accepted (%d) as a " + "recognized WCS convention.\n", keyrec, nvalid); + } else { + wcsfprintf(stderr, "%.80s\n Accepted (%d) as a " + "valid WCS keyrecord.\n", keyrec, nvalid); + } } - BEGIN(COMMENT); + BEGIN(FLUSH); } else { - errmsg = "Internal parser ERROR, null string pointer"; + errmsg = "syntactically valid WCS keyrecord has no effect"; BEGIN(ERROR); } + + } else { + BEGIN(FLUSH); } } YY_BREAK -case 87: +case 286: +*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ +yyg->yy_c_buf_p = yy_cp -= 1; +YY_DO_BEFORE_ACTION; /* set up yytext again */ YY_RULE_SETUP -#line 915 "wcspih.l" +#line 2330 "wcspih.l" { - errmsg = "A string value was expected"; + errmsg = "invalid keyvalue"; BEGIN(ERROR); } YY_BREAK -case 88: -#line 921 "wcspih.l" -case 89: +case 287: +*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ +yyg->yy_c_buf_p = yy_cp -= 1; +YY_DO_BEFORE_ACTION; /* set up yytext again */ YY_RULE_SETUP -#line 921 "wcspih.l" +#line 2335 "wcspih.l" { - BEGIN(FLUSH); + errmsg = "invalid keyvalue"; + BEGIN(ERROR); } YY_BREAK -case 90: +case 288: +*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ +yyg->yy_c_buf_p = yy_cp -= 1; +YY_DO_BEFORE_ACTION; /* set up yytext again */ YY_RULE_SETUP -#line 925 "wcspih.l" +#line 2340 "wcspih.l" { - errmsg = "Malformed keycomment"; + errmsg = "invalid keyvalue or malformed keycomment"; BEGIN(ERROR); } YY_BREAK -case 91: +case 289: +*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ +yyg->yy_c_buf_p = yy_cp -= 1; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +YY_RULE_SETUP +#line 2345 "wcspih.l" +{ + errmsg = "malformed keycomment"; + BEGIN(ERROR); + } + YY_BREAK +case 290: +*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ +yyg->yy_c_buf_p = yy_cp -= 1; +YY_DO_BEFORE_ACTION; /* set up yytext again */ YY_RULE_SETUP -#line 930 "wcspih.l" +#line 2350 "wcspih.l" { - if (pass == 2) { + if (ipass == npass) { if (ctrl < 0) { - /* Preserve discards. */ - keep = wcspih_hdr - 80; + // Preserve discards. + keep = keyrec; - } else if (ctrl > 2) { - fprintf(stderr, "%.80s\n Discarded.\n", wcspih_hdr-80); + } else if (2 < ctrl) { + nother++; + wcsfprintf(stderr, "%.80s\n Not a recognized WCS keyword.\n", + keyrec); } } BEGIN(FLUSH); } YY_BREAK -case 92: +case 291: +*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ +yyg->yy_c_buf_p = yy_cp -= 1; +YY_DO_BEFORE_ACTION; /* set up yytext again */ YY_RULE_SETUP -#line 943 "wcspih.l" +#line 2365 "wcspih.l" { - (*nreject)++; - if (pass == 2) { + if (ipass == npass) { + (*nreject)++; + if (ctrl%10 == -1) { - /* Preserve rejects. */ - keep = wcspih_hdr - 80; + // Preserve rejects. + keep = keyrec; } - if (abs(ctrl%10) > 1) { - fprintf(stderr, "%.80s\n%4d: %s.\n", wcspih_hdr-80, *nreject, - errmsg); + if (1 < abs(ctrl%10)) { + wcsfprintf(stderr, "%.80s\n Rejected (%d), %s.\n", + keyrec, *nreject, errmsg); } } BEGIN(FLUSH); } YY_BREAK -case 93: -/* rule 93 can match eol */ +case 292: +/* rule 292 can match eol */ YY_RULE_SETUP -#line 959 "wcspih.l" +#line 2382 "wcspih.l" { - if (pass == 2 && keep) { + if (ipass == npass && keep) { if (hptr < keep) { strncpy(hptr, keep, 80); } hptr += 80; } - i = j = m = 0; + naux += auxprm; + + // Throw away the rest of the line and reset for the next one. + i = j = 0; + m = 0; a = ' '; + + keyrec += 80; + valtype = -1; - keep = 0x0; - altlin = 0; - npptr = 0x0; + distype = 0; + vptr = 0x0; + keep = 0x0; + + altlin = 0; + npptr = 0x0; + chekval = 0x0; + special = 0x0; + auxprm = 0; + sipflag = 0; + dssflag = 0; + watflag = 0; + BEGIN(INITIAL); } YY_BREAK case YY_STATE_EOF(INITIAL): -case YY_STATE_EOF(CROTAi): -case YY_STATE_EOF(PROJPn): -case YY_STATE_EOF(CCCCCia): +case YY_STATE_EOF(CCia): case YY_STATE_EOF(CCi_ja): +case YY_STATE_EOF(CCCCCia): case YY_STATE_EOF(CCi_ma): case YY_STATE_EOF(CCCCCCCa): case YY_STATE_EOF(CCCCCCCC): +case YY_STATE_EOF(CROTAi): +case YY_STATE_EOF(PROJPn): +case YY_STATE_EOF(SIP2): +case YY_STATE_EOF(SIP3): +case YY_STATE_EOF(DSSAMDXY): +case YY_STATE_EOF(PLTDECSN): case YY_STATE_EOF(VALUE): case YY_STATE_EOF(INTEGER_VAL): case YY_STATE_EOF(FLOAT_VAL): +case YY_STATE_EOF(FLOAT2_VAL): case YY_STATE_EOF(STRING_VAL): +case YY_STATE_EOF(RECORD_VAL): +case YY_STATE_EOF(RECFIELD): +case YY_STATE_EOF(RECCOLON): +case YY_STATE_EOF(RECVALUE): +case YY_STATE_EOF(RECEND): case YY_STATE_EOF(COMMENT): case YY_STATE_EOF(DISCARD): case YY_STATE_EOF(ERROR): case YY_STATE_EOF(FLUSH): -#line 976 "wcspih.l" +#line 2416 "wcspih.l" { - /* End-of-input. */ - if (pass == 1) { - if ((status = wcspih_inits(naxis, alts, npv, nps, nwcs, wcs)) || - *nwcs == 0) { - wcspihlex_destroy(); + // End-of-input. + int status; + if (ipass == 1) { + if ((status = wcspih_init1(naxis, alts, dpq, npv, nps, ndp, ndq, + naux, distran, nwcs, wcs)) || + (*nwcs == 0 && ctrl == 0)) { return status; } - if (abs(ctrl%10) > 2) { + if (2 < abs(ctrl%10)) { if (*nwcs == 1) { - fprintf(stderr, "Found one coordinate representation.\n"); + if (strcmp(wcs[0]->wcsname, "DEFAULTS") != 0) { + wcsfprintf(stderr, "Found one coordinate representation.\n"); + } } else { - fprintf(stderr, "Found %d coordinate representations.\n", + wcsfprintf(stderr, "Found %d coordinate representations.\n", *nwcs); } } - wcspih_hdr = header; - wcspih_nkeyrec = nkeyrec; + watstr = calloc(2*(watn*68 + 1), sizeof(char)); + wat[0] = watstr; + wat[1] = watstr + watn*68 + 1; + } + + if (ipass++ < npass) { + yyextra->hdr = header; + yyextra->nkeyrec = nkeyrec; + keyrec = header; *nreject = 0; - pass = 2; - i = j = m = 0; + i = j = 0; + m = 0; a = ' '; + valtype = -1; + distype = 0; + vptr = 0x0; - wcspihrestart(wcspihin); + altlin = 0; + npptr = 0x0; + chekval = 0x0; + special = 0x0; + auxprm = 0; + sipflag = 0; + dssflag = 0; + watflag = 0; + + yyrestart(yyin, yyscanner); } else { - wcspihlex_destroy(); if (ctrl < 0) { *hptr = '\0'; } else if (ctrl == 1) { - fprintf(stderr, "%d WCS keyrecords were rejected.\n", - *nreject); + wcsfprintf(stderr, "%d WCS keyrecord%s rejected.\n", + *nreject, (*nreject==1)?" was":"s were"); + } else if (ctrl == 4) { + wcsfprintf(stderr, "\n"); + wcsfprintf(stderr, "%5d keyrecord%s rejected for syntax or " + "other errors,\n", *nreject, (*nreject==1)?" was":"s were"); + wcsfprintf(stderr, "%5d %s recognized as syntactically valid, " + "and\n", nvalid, (nvalid==1)?"was":"were"); + wcsfprintf(stderr, "%5d other%s were not recognized as WCS " + "keyrecords.\n", nother, (nother==1)?"":"s"); } - return wcspih_final(alts, epoch, vsource, nwcs, wcs); + status = wcspih_final(ndp, ndq, distran, dsstmp, wat, nwcs, wcs); + free(watstr); + return status; } } YY_BREAK -case 94: +case 293: YY_RULE_SETUP -#line 1019 "wcspih.l" +#line 2490 "wcspih.l" ECHO; YY_BREAK -#line 9676 "wcspih.c" +#line 24926 "wcspih.c" case YY_END_OF_BUFFER: { /* Amount of text matched not including the EOB char. */ - int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1; + int yy_amount_of_matched_text = (int) (yy_cp - yyg->yytext_ptr) - 1; /* Undo the effects of YY_DO_BEFORE_ACTION. */ - *yy_cp = (yy_hold_char); + *yy_cp = yyg->yy_hold_char; YY_RESTORE_YY_MORE_OFFSET if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) { /* We're scanning a new file or input source. It's * possible that this happened because the user - * just pointed wcspihin at a new source and called - * wcspihlex(). If so, then we have to assure + * just pointed yyin at a new source and called + * yylex(). If so, then we have to assure * consistency between YY_CURRENT_BUFFER and our * globals. Here is the right place to do so, because * this is the first action (other than possibly a * back-up) that will match for the new input source. */ - (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; - YY_CURRENT_BUFFER_LVALUE->yy_input_file = wcspihin; + yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin; YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; } @@ -9706,13 +24956,13 @@ ECHO; * end-of-buffer state). Contrast this with the test * in input(). */ - if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) + if ( yyg->yy_c_buf_p <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) { /* This was really a NUL. */ yy_state_type yy_next_state; - (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text; + yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text; - yy_current_state = yy_get_previous_state( ); + yy_current_state = yy_get_previous_state( yyscanner ); /* Okay, we're now positioned to make the NUL * transition. We couldn't have @@ -9723,43 +24973,43 @@ ECHO; * will run more slowly). */ - yy_next_state = yy_try_NUL_trans( yy_current_state ); + yy_next_state = yy_try_NUL_trans( yy_current_state , yyscanner); - yy_bp = (yytext_ptr) + YY_MORE_ADJ; + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; if ( yy_next_state ) { /* Consume the NUL. */ - yy_cp = ++(yy_c_buf_p); + yy_cp = ++yyg->yy_c_buf_p; yy_current_state = yy_next_state; goto yy_match; } else { - yy_cp = (yy_c_buf_p); + yy_cp = yyg->yy_c_buf_p; goto yy_find_action; } } - else switch ( yy_get_next_buffer( ) ) + else switch ( yy_get_next_buffer( yyscanner ) ) { case EOB_ACT_END_OF_FILE: { - (yy_did_buffer_switch_on_eof) = 0; + yyg->yy_did_buffer_switch_on_eof = 0; - if ( wcspihwrap( ) ) + if ( yywrap( yyscanner ) ) { /* Note: because we've taken care in * yy_get_next_buffer() to have set up - * wcspihtext, we can now set up + * yytext, we can now set up * yy_c_buf_p so that if some total * hoser (like flex itself) wants to * call the scanner after we return the * YY_NULL, it'll still work - another * YY_NULL will get returned. */ - (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ; + yyg->yy_c_buf_p = yyg->yytext_ptr + YY_MORE_ADJ; yy_act = YY_STATE_EOF(YY_START); goto do_action; @@ -9767,30 +25017,30 @@ ECHO; else { - if ( ! (yy_did_buffer_switch_on_eof) ) + if ( ! yyg->yy_did_buffer_switch_on_eof ) YY_NEW_FILE; } break; } case EOB_ACT_CONTINUE_SCAN: - (yy_c_buf_p) = - (yytext_ptr) + yy_amount_of_matched_text; + yyg->yy_c_buf_p = + yyg->yytext_ptr + yy_amount_of_matched_text; - yy_current_state = yy_get_previous_state( ); + yy_current_state = yy_get_previous_state( yyscanner ); - yy_cp = (yy_c_buf_p); - yy_bp = (yytext_ptr) + YY_MORE_ADJ; + yy_cp = yyg->yy_c_buf_p; + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; goto yy_match; case EOB_ACT_LAST_MATCH: - (yy_c_buf_p) = - &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)]; + yyg->yy_c_buf_p = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars]; - yy_current_state = yy_get_previous_state( ); + yy_current_state = yy_get_previous_state( yyscanner ); - yy_cp = (yy_c_buf_p); - yy_bp = (yytext_ptr) + YY_MORE_ADJ; + yy_cp = yyg->yy_c_buf_p; + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; goto yy_find_action; } break; @@ -9801,7 +25051,8 @@ ECHO; "fatal flex scanner internal error--no action found" ); } /* end of action switch */ } /* end of scanning one token */ -} /* end of wcspihlex */ + } /* end of user's declarations */ +} /* end of yylex */ /* yy_get_next_buffer - try to read in a new buffer * @@ -9810,20 +25061,21 @@ ECHO; * EOB_ACT_CONTINUE_SCAN - continue scanning from current position * EOB_ACT_END_OF_FILE - end of file */ -static int yy_get_next_buffer (void) +static int yy_get_next_buffer (yyscan_t yyscanner) { - register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; - register char *source = (yytext_ptr); - register int number_to_move, i; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; + char *source = yyg->yytext_ptr; + int number_to_move, i; int ret_val; - if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] ) + if ( yyg->yy_c_buf_p > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] ) YY_FATAL_ERROR( "fatal flex scanner internal error--end of buffer missed" ); if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) { /* Don't try to fill the buffer, so this is an EOF. */ - if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 ) + if ( yyg->yy_c_buf_p - yyg->yytext_ptr - YY_MORE_ADJ == 1 ) { /* We matched a single character, the EOB, so * treat this as a final EOF. @@ -9843,7 +25095,7 @@ static int yy_get_next_buffer (void) /* Try to read more data. */ /* First move last chars to start of buffer. */ - number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr)) - 1; + number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr - 1); for ( i = 0; i < number_to_move; ++i ) *(dest++) = *(source++); @@ -9852,7 +25104,7 @@ static int yy_get_next_buffer (void) /* don't do the read, it's not guaranteed to return an EOF, * just force an EOF */ - YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0; + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars = 0; else { @@ -9863,10 +25115,10 @@ static int yy_get_next_buffer (void) { /* Not enough room in the buffer - grow it. */ /* just a shorter name for the current buffer */ - YY_BUFFER_STATE b = YY_CURRENT_BUFFER; + YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE; int yy_c_buf_p_offset = - (int) ((yy_c_buf_p) - b->yy_ch_buf); + (int) (yyg->yy_c_buf_p - b->yy_ch_buf); if ( b->yy_is_our_buffer ) { @@ -9879,17 +25131,18 @@ static int yy_get_next_buffer (void) b->yy_ch_buf = (char *) /* Include room in for 2 EOB chars. */ - wcspihrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 ); + yyrealloc( (void *) b->yy_ch_buf, + (yy_size_t) (b->yy_buf_size + 2) , yyscanner ); } else /* Can't grow it, we don't own it. */ - b->yy_ch_buf = 0; + b->yy_ch_buf = NULL; if ( ! b->yy_ch_buf ) YY_FATAL_ERROR( "fatal error - scanner input buffer overflow" ); - (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset]; + yyg->yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset]; num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; @@ -9901,17 +25154,17 @@ static int yy_get_next_buffer (void) /* Read in more data. */ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), - (yy_n_chars), (size_t) num_to_read ); + yyg->yy_n_chars, num_to_read ); - YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; } - if ( (yy_n_chars) == 0 ) + if ( yyg->yy_n_chars == 0 ) { if ( number_to_move == YY_MORE_ADJ ) { ret_val = EOB_ACT_END_OF_FILE; - wcspihrestart(wcspihin ); + yyrestart( yyin , yyscanner); } else @@ -9925,34 +25178,38 @@ static int yy_get_next_buffer (void) else ret_val = EOB_ACT_CONTINUE_SCAN; - if ((yy_size_t) ((yy_n_chars) + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { + if ((yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { /* Extend the array by 50%, plus the number we really need. */ - yy_size_t new_size = (yy_n_chars) + number_to_move + ((yy_n_chars) >> 1); - YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) wcspihrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size ); + int new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1); + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc( + (void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf, (yy_size_t) new_size , yyscanner ); if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); + /* "- 2" to take care of EOB's */ + YY_CURRENT_BUFFER_LVALUE->yy_buf_size = (int) (new_size - 2); } - (yy_n_chars) += number_to_move; - YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR; - YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR; + yyg->yy_n_chars += number_to_move; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] = YY_END_OF_BUFFER_CHAR; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR; - (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; + yyg->yytext_ptr = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; return ret_val; } /* yy_get_previous_state - get the state just before the EOB char was reached */ - static yy_state_type yy_get_previous_state (void) + static yy_state_type yy_get_previous_state (yyscan_t yyscanner) { - register yy_state_type yy_current_state; - register char *yy_cp; - - yy_current_state = (yy_start); + yy_state_type yy_current_state; + char *yy_cp; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + yy_current_state = yyg->yy_start; yy_current_state += YY_AT_BOL(); - for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp ) + for ( yy_cp = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp ) { if ( *yy_cp ) { @@ -9962,8 +25219,8 @@ static int yy_get_next_buffer (void) yy_current_state = yy_NUL_trans[yy_current_state]; if ( yy_accept[yy_current_state] ) { - (yy_last_accepting_state) = yy_current_state; - (yy_last_accepting_cpos) = yy_cp; + yyg->yy_last_accepting_state = yy_current_state; + yyg->yy_last_accepting_cpos = yy_cp; } } @@ -9975,10 +25232,11 @@ static int yy_get_next_buffer (void) * synopsis * next_state = yy_try_NUL_trans( current_state ); */ - static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state ) + static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state , yyscan_t yyscanner) { - register int yy_is_jam; - register char *yy_cp = (yy_c_buf_p); + int yy_is_jam; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */ + char *yy_cp = yyg->yy_c_buf_p; yy_current_state = yy_NUL_trans[yy_current_state]; yy_is_jam = (yy_current_state == 0); @@ -9987,30 +25245,34 @@ static int yy_get_next_buffer (void) { if ( yy_accept[yy_current_state] ) { - (yy_last_accepting_state) = yy_current_state; - (yy_last_accepting_cpos) = yy_cp; + yyg->yy_last_accepting_state = yy_current_state; + yyg->yy_last_accepting_cpos = yy_cp; } } + (void)yyg; return yy_is_jam ? 0 : yy_current_state; } - static void yyunput (int c, register char * yy_bp ) +#ifndef YY_NO_UNPUT + + static void yyunput (int c, char * yy_bp , yyscan_t yyscanner) { - register char *yy_cp; - - yy_cp = (yy_c_buf_p); + char *yy_cp; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; - /* undo effects of setting up wcspihtext */ - *yy_cp = (yy_hold_char); + yy_cp = yyg->yy_c_buf_p; + + /* undo effects of setting up yytext */ + *yy_cp = yyg->yy_hold_char; if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) { /* need to shift things up to make room */ /* +2 for EOB chars. */ - register int number_to_move = (yy_n_chars) + 2; - register char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[ + int number_to_move = yyg->yy_n_chars + 2; + char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[ YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2]; - register char *source = + char *source = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]; while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) @@ -10019,7 +25281,7 @@ static int yy_get_next_buffer (void) yy_cp += (int) (dest - source); yy_bp += (int) (dest - source); YY_CURRENT_BUFFER_LVALUE->yy_n_chars = - (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_buf_size; + yyg->yy_n_chars = (int) YY_CURRENT_BUFFER_LVALUE->yy_buf_size; if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) YY_FATAL_ERROR( "flex scanner push-back overflow" ); @@ -10027,39 +25289,42 @@ static int yy_get_next_buffer (void) *--yy_cp = (char) c; - (yytext_ptr) = yy_bp; - (yy_hold_char) = *yy_cp; - (yy_c_buf_p) = yy_cp; + yyg->yytext_ptr = yy_bp; + yyg->yy_hold_char = *yy_cp; + yyg->yy_c_buf_p = yy_cp; } +#endif + #ifndef YY_NO_INPUT #ifdef __cplusplus - static int yyinput (void) + static int yyinput (yyscan_t yyscanner) #else - static int input (void) + static int input (yyscan_t yyscanner) #endif { int c; - - *(yy_c_buf_p) = (yy_hold_char); + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; - if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR ) + *yyg->yy_c_buf_p = yyg->yy_hold_char; + + if ( *yyg->yy_c_buf_p == YY_END_OF_BUFFER_CHAR ) { /* yy_c_buf_p now points to the character we want to return. * If this occurs *before* the EOB characters, then it's a * valid NUL; if not, then we've hit the end of the buffer. */ - if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) + if ( yyg->yy_c_buf_p < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) /* This was really a NUL. */ - *(yy_c_buf_p) = '\0'; + *yyg->yy_c_buf_p = '\0'; else { /* need more input */ - int offset = (yy_c_buf_p) - (yytext_ptr); - ++(yy_c_buf_p); + int offset = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr); + ++yyg->yy_c_buf_p; - switch ( yy_get_next_buffer( ) ) + switch ( yy_get_next_buffer( yyscanner ) ) { case EOB_ACT_LAST_MATCH: /* This happens because yy_g_n_b() @@ -10073,34 +25338,34 @@ static int yy_get_next_buffer (void) */ /* Reset buffer status. */ - wcspihrestart(wcspihin ); + yyrestart( yyin , yyscanner); /*FALLTHROUGH*/ case EOB_ACT_END_OF_FILE: { - if ( wcspihwrap( ) ) - return EOF; + if ( yywrap( yyscanner ) ) + return 0; - if ( ! (yy_did_buffer_switch_on_eof) ) + if ( ! yyg->yy_did_buffer_switch_on_eof ) YY_NEW_FILE; #ifdef __cplusplus - return yyinput(); + return yyinput(yyscanner); #else - return input(); + return input(yyscanner); #endif } case EOB_ACT_CONTINUE_SCAN: - (yy_c_buf_p) = (yytext_ptr) + offset; + yyg->yy_c_buf_p = yyg->yytext_ptr + offset; break; } } } - c = *(unsigned char *) (yy_c_buf_p); /* cast for 8-bit char's */ - *(yy_c_buf_p) = '\0'; /* preserve wcspihtext */ - (yy_hold_char) = *++(yy_c_buf_p); + c = *(unsigned char *) yyg->yy_c_buf_p; /* cast for 8-bit char's */ + *yyg->yy_c_buf_p = '\0'; /* preserve yytext */ + yyg->yy_hold_char = *++yyg->yy_c_buf_p; YY_CURRENT_BUFFER_LVALUE->yy_at_bol = (c == '\n'); @@ -10110,102 +25375,106 @@ static int yy_get_next_buffer (void) /** Immediately switch to a different input stream. * @param input_file A readable stream. - * + * @param yyscanner The scanner object. * @note This function does not reset the start condition to @c INITIAL . */ - void wcspihrestart (FILE * input_file ) + void yyrestart (FILE * input_file , yyscan_t yyscanner) { - + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if ( ! YY_CURRENT_BUFFER ){ - wcspihensure_buffer_stack (); + yyensure_buffer_stack (yyscanner); YY_CURRENT_BUFFER_LVALUE = - wcspih_create_buffer(wcspihin,YY_BUF_SIZE ); + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); } - wcspih_init_buffer(YY_CURRENT_BUFFER,input_file ); - wcspih_load_buffer_state( ); + yy_init_buffer( YY_CURRENT_BUFFER, input_file , yyscanner); + yy_load_buffer_state( yyscanner ); } /** Switch to a different input buffer. * @param new_buffer The new input buffer. - * + * @param yyscanner The scanner object. */ - void wcspih_switch_to_buffer (YY_BUFFER_STATE new_buffer ) + void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) { - + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + /* TODO. We should be able to replace this entire function body * with - * wcspihpop_buffer_state(); - * wcspihpush_buffer_state(new_buffer); + * yypop_buffer_state(); + * yypush_buffer_state(new_buffer); */ - wcspihensure_buffer_stack (); + yyensure_buffer_stack (yyscanner); if ( YY_CURRENT_BUFFER == new_buffer ) return; if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ - *(yy_c_buf_p) = (yy_hold_char); - YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); - YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + *yyg->yy_c_buf_p = yyg->yy_hold_char; + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; } YY_CURRENT_BUFFER_LVALUE = new_buffer; - wcspih_load_buffer_state( ); + yy_load_buffer_state( yyscanner ); /* We don't actually know whether we did this switch during - * EOF (wcspihwrap()) processing, but the only time this flag - * is looked at is after wcspihwrap() is called, so it's safe + * EOF (yywrap()) processing, but the only time this flag + * is looked at is after yywrap() is called, so it's safe * to go ahead and always set it. */ - (yy_did_buffer_switch_on_eof) = 1; + yyg->yy_did_buffer_switch_on_eof = 1; } -static void wcspih_load_buffer_state (void) +static void yy_load_buffer_state (yyscan_t yyscanner) { - (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; - (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; - wcspihin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; - (yy_hold_char) = *(yy_c_buf_p); + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + yyg->yytext_ptr = yyg->yy_c_buf_p = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; + yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; + yyg->yy_hold_char = *yyg->yy_c_buf_p; } /** Allocate and initialize an input buffer state. * @param file A readable stream. * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. - * + * @param yyscanner The scanner object. * @return the allocated buffer state. */ - YY_BUFFER_STATE wcspih_create_buffer (FILE * file, int size ) + YY_BUFFER_STATE yy_create_buffer (FILE * file, int size , yyscan_t yyscanner) { YY_BUFFER_STATE b; - b = (YY_BUFFER_STATE) wcspihalloc(sizeof( struct yy_buffer_state ) ); + b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner ); if ( ! b ) - YY_FATAL_ERROR( "out of dynamic memory in wcspih_create_buffer()" ); + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); b->yy_buf_size = size; /* yy_ch_buf has to be 2 characters longer than the size given because * we need to put in 2 end-of-buffer characters. */ - b->yy_ch_buf = (char *) wcspihalloc(b->yy_buf_size + 2 ); + b->yy_ch_buf = (char *) yyalloc( (yy_size_t) (b->yy_buf_size + 2) , yyscanner ); if ( ! b->yy_ch_buf ) - YY_FATAL_ERROR( "out of dynamic memory in wcspih_create_buffer()" ); + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); b->yy_is_our_buffer = 1; - wcspih_init_buffer(b,file ); + yy_init_buffer( b, file , yyscanner); return b; } /** Destroy the buffer. - * @param b a buffer created with wcspih_create_buffer() - * + * @param b a buffer created with yy_create_buffer() + * @param yyscanner The scanner object. */ - void wcspih_delete_buffer (YY_BUFFER_STATE b ) + void yy_delete_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) { - + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if ( ! b ) return; @@ -10213,27 +25482,28 @@ static void wcspih_load_buffer_state (void) YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; if ( b->yy_is_our_buffer ) - wcspihfree((void *) b->yy_ch_buf ); + yyfree( (void *) b->yy_ch_buf , yyscanner ); - wcspihfree((void *) b ); + yyfree( (void *) b , yyscanner ); } /* Initializes or reinitializes a buffer. * This function is sometimes called more than once on the same buffer, - * such as during a wcspihrestart() or at EOF. + * such as during a yyrestart() or at EOF. */ - static void wcspih_init_buffer (YY_BUFFER_STATE b, FILE * file ) + static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file , yyscan_t yyscanner) { int oerrno = errno; - - wcspih_flush_buffer(b ); + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + yy_flush_buffer( b , yyscanner); b->yy_input_file = file; b->yy_fill_buffer = 1; - /* If b is the current buffer, then wcspih_init_buffer was _probably_ - * called from wcspihrestart() or through yy_get_next_buffer. + /* If b is the current buffer, then yy_init_buffer was _probably_ + * called from yyrestart() or through yy_get_next_buffer. * In that case, we don't want to reset the lineno or column. */ if (b != YY_CURRENT_BUFFER){ @@ -10248,11 +25518,12 @@ static void wcspih_load_buffer_state (void) /** Discard all buffered characters. On the next scan, YY_INPUT will be called. * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. - * + * @param yyscanner The scanner object. */ - void wcspih_flush_buffer (YY_BUFFER_STATE b ) + void yy_flush_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) { - if ( ! b ) + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if ( ! b ) return; b->yy_n_chars = 0; @@ -10270,114 +25541,117 @@ static void wcspih_load_buffer_state (void) b->yy_buffer_status = YY_BUFFER_NEW; if ( b == YY_CURRENT_BUFFER ) - wcspih_load_buffer_state( ); + yy_load_buffer_state( yyscanner ); } /** Pushes the new state onto the stack. The new state becomes * the current state. This function will allocate the stack * if necessary. * @param new_buffer The new state. - * + * @param yyscanner The scanner object. */ -void wcspihpush_buffer_state (YY_BUFFER_STATE new_buffer ) +void yypush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) { - if (new_buffer == NULL) + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if (new_buffer == NULL) return; - wcspihensure_buffer_stack(); + yyensure_buffer_stack(yyscanner); - /* This block is copied from wcspih_switch_to_buffer. */ + /* This block is copied from yy_switch_to_buffer. */ if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ - *(yy_c_buf_p) = (yy_hold_char); - YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); - YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + *yyg->yy_c_buf_p = yyg->yy_hold_char; + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; } /* Only push if top exists. Otherwise, replace top. */ if (YY_CURRENT_BUFFER) - (yy_buffer_stack_top)++; + yyg->yy_buffer_stack_top++; YY_CURRENT_BUFFER_LVALUE = new_buffer; - /* copied from wcspih_switch_to_buffer. */ - wcspih_load_buffer_state( ); - (yy_did_buffer_switch_on_eof) = 1; + /* copied from yy_switch_to_buffer. */ + yy_load_buffer_state( yyscanner ); + yyg->yy_did_buffer_switch_on_eof = 1; } /** Removes and deletes the top of the stack, if present. * The next element becomes the new top. - * + * @param yyscanner The scanner object. */ -void wcspihpop_buffer_state (void) +void yypop_buffer_state (yyscan_t yyscanner) { - if (!YY_CURRENT_BUFFER) + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if (!YY_CURRENT_BUFFER) return; - wcspih_delete_buffer(YY_CURRENT_BUFFER ); + yy_delete_buffer(YY_CURRENT_BUFFER , yyscanner); YY_CURRENT_BUFFER_LVALUE = NULL; - if ((yy_buffer_stack_top) > 0) - --(yy_buffer_stack_top); + if (yyg->yy_buffer_stack_top > 0) + --yyg->yy_buffer_stack_top; if (YY_CURRENT_BUFFER) { - wcspih_load_buffer_state( ); - (yy_did_buffer_switch_on_eof) = 1; + yy_load_buffer_state( yyscanner ); + yyg->yy_did_buffer_switch_on_eof = 1; } } /* Allocates the stack if it does not exist. * Guarantees space for at least one push. */ -static void wcspihensure_buffer_stack (void) +static void yyensure_buffer_stack (yyscan_t yyscanner) { - int num_to_alloc; - - if (!(yy_buffer_stack)) { + yy_size_t num_to_alloc; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (!yyg->yy_buffer_stack) { /* First allocation is just for 2 elements, since we don't know if this * scanner will even need a stack. We use 2 instead of 1 to avoid an * immediate realloc on the next call. */ - num_to_alloc = 1; - (yy_buffer_stack) = (struct yy_buffer_state**)wcspihalloc + num_to_alloc = 1; /* After all that talk, this was set to 1 anyways... */ + yyg->yy_buffer_stack = (struct yy_buffer_state**)yyalloc (num_to_alloc * sizeof(struct yy_buffer_state*) - ); - if ( ! (yy_buffer_stack) ) - YY_FATAL_ERROR( "out of dynamic memory in wcspihensure_buffer_stack()" ); - - memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*)); - - (yy_buffer_stack_max) = num_to_alloc; - (yy_buffer_stack_top) = 0; + , yyscanner); + if ( ! yyg->yy_buffer_stack ) + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); + + memset(yyg->yy_buffer_stack, 0, num_to_alloc * sizeof(struct yy_buffer_state*)); + + yyg->yy_buffer_stack_max = num_to_alloc; + yyg->yy_buffer_stack_top = 0; return; } - if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){ + if (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){ /* Increase the buffer to prepare for a possible push. */ - int grow_size = 8 /* arbitrary grow size */; + yy_size_t grow_size = 8 /* arbitrary grow size */; - num_to_alloc = (yy_buffer_stack_max) + grow_size; - (yy_buffer_stack) = (struct yy_buffer_state**)wcspihrealloc - ((yy_buffer_stack), + num_to_alloc = yyg->yy_buffer_stack_max + grow_size; + yyg->yy_buffer_stack = (struct yy_buffer_state**)yyrealloc + (yyg->yy_buffer_stack, num_to_alloc * sizeof(struct yy_buffer_state*) - ); - if ( ! (yy_buffer_stack) ) - YY_FATAL_ERROR( "out of dynamic memory in wcspihensure_buffer_stack()" ); + , yyscanner); + if ( ! yyg->yy_buffer_stack ) + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); /* zero only the new slots.*/ - memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*)); - (yy_buffer_stack_max) = num_to_alloc; + memset(yyg->yy_buffer_stack + yyg->yy_buffer_stack_max, 0, grow_size * sizeof(struct yy_buffer_state*)); + yyg->yy_buffer_stack_max = num_to_alloc; } } /** Setup the input buffer state to scan directly from a user-specified character buffer. * @param base the character buffer * @param size the size in bytes of the character buffer - * - * @return the newly allocated buffer state object. + * @param yyscanner The scanner object. + * @return the newly allocated buffer state object. */ -YY_BUFFER_STATE wcspih_scan_buffer (char * base, yy_size_t size ) +YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size , yyscan_t yyscanner) { YY_BUFFER_STATE b; @@ -10385,49 +25659,49 @@ YY_BUFFER_STATE wcspih_scan_buffer (char * base, yy_size_t size ) base[size-2] != YY_END_OF_BUFFER_CHAR || base[size-1] != YY_END_OF_BUFFER_CHAR ) /* They forgot to leave room for the EOB's. */ - return 0; + return NULL; - b = (YY_BUFFER_STATE) wcspihalloc(sizeof( struct yy_buffer_state ) ); + b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner ); if ( ! b ) - YY_FATAL_ERROR( "out of dynamic memory in wcspih_scan_buffer()" ); + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); - b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */ + b->yy_buf_size = (int) (size - 2); /* "- 2" to take care of EOB's */ b->yy_buf_pos = b->yy_ch_buf = base; b->yy_is_our_buffer = 0; - b->yy_input_file = 0; + b->yy_input_file = NULL; b->yy_n_chars = b->yy_buf_size; b->yy_is_interactive = 0; b->yy_at_bol = 1; b->yy_fill_buffer = 0; b->yy_buffer_status = YY_BUFFER_NEW; - wcspih_switch_to_buffer(b ); + yy_switch_to_buffer( b , yyscanner ); return b; } -/** Setup the input buffer state to scan a string. The next call to wcspihlex() will +/** Setup the input buffer state to scan a string. The next call to yylex() will * scan from a @e copy of @a str. * @param yystr a NUL-terminated string to scan - * + * @param yyscanner The scanner object. * @return the newly allocated buffer state object. * @note If you want to scan bytes that may contain NUL values, then use - * wcspih_scan_bytes() instead. + * yy_scan_bytes() instead. */ -YY_BUFFER_STATE wcspih_scan_string (yyconst char * yystr ) +YY_BUFFER_STATE yy_scan_string (const char * yystr , yyscan_t yyscanner) { - return wcspih_scan_bytes(yystr,strlen(yystr) ); + return yy_scan_bytes( yystr, (int) strlen(yystr) , yyscanner); } -/** Setup the input buffer state to scan the given bytes. The next call to wcspihlex() will +/** Setup the input buffer state to scan the given bytes. The next call to yylex() will * scan from a @e copy of @a bytes. * @param yybytes the byte buffer to scan * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes. - * + * @param yyscanner The scanner object. * @return the newly allocated buffer state object. */ -YY_BUFFER_STATE wcspih_scan_bytes (yyconst char * yybytes, int _yybytes_len ) +YY_BUFFER_STATE yy_scan_bytes (const char * yybytes, int _yybytes_len , yyscan_t yyscanner) { YY_BUFFER_STATE b; char *buf; @@ -10435,19 +25709,19 @@ YY_BUFFER_STATE wcspih_scan_bytes (yyconst char * yybytes, int _yybytes_len ) int i; /* Get memory for full buffer, including space for trailing EOB's. */ - n = _yybytes_len + 2; - buf = (char *) wcspihalloc(n ); + n = (yy_size_t) (_yybytes_len + 2); + buf = (char *) yyalloc( n , yyscanner ); if ( ! buf ) - YY_FATAL_ERROR( "out of dynamic memory in wcspih_scan_bytes()" ); + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" ); for ( i = 0; i < _yybytes_len; ++i ) buf[i] = yybytes[i]; buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; - b = wcspih_scan_buffer(buf,n ); + b = yy_scan_buffer( buf, n , yyscanner); if ( ! b ) - YY_FATAL_ERROR( "bad buffer in wcspih_scan_bytes()" ); + YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" ); /* It's okay to grow etc. this buffer, and we should throw it * away when we're done. @@ -10461,9 +25735,11 @@ YY_BUFFER_STATE wcspih_scan_bytes (yyconst char * yybytes, int _yybytes_len ) #define YY_EXIT_FAILURE 2 #endif -static void yy_fatal_error (yyconst char* msg ) +static void yynoreturn yy_fatal_error (const char* msg , yyscan_t yyscanner) { - (void) fprintf( stderr, "%s\n", msg ); + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + fprintf( stderr, "%s\n", msg ); exit( YY_EXIT_FAILURE ); } @@ -10473,144 +25749,283 @@ static void yy_fatal_error (yyconst char* msg ) #define yyless(n) \ do \ { \ - /* Undo effects of setting up wcspihtext. */ \ + /* Undo effects of setting up yytext. */ \ int yyless_macro_arg = (n); \ YY_LESS_LINENO(yyless_macro_arg);\ - wcspihtext[wcspihleng] = (yy_hold_char); \ - (yy_c_buf_p) = wcspihtext + yyless_macro_arg; \ - (yy_hold_char) = *(yy_c_buf_p); \ - *(yy_c_buf_p) = '\0'; \ - wcspihleng = yyless_macro_arg; \ + yytext[yyleng] = yyg->yy_hold_char; \ + yyg->yy_c_buf_p = yytext + yyless_macro_arg; \ + yyg->yy_hold_char = *yyg->yy_c_buf_p; \ + *yyg->yy_c_buf_p = '\0'; \ + yyleng = yyless_macro_arg; \ } \ while ( 0 ) /* Accessor methods (get/set functions) to struct members. */ +/** Get the user-defined data for this scanner. + * @param yyscanner The scanner object. + */ +YY_EXTRA_TYPE yyget_extra (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyextra; +} + /** Get the current line number. - * + * @param yyscanner The scanner object. + */ +int yyget_lineno (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (! YY_CURRENT_BUFFER) + return 0; + + return yylineno; +} + +/** Get the current column number. + * @param yyscanner The scanner object. */ -int wcspihget_lineno (void) +int yyget_column (yyscan_t yyscanner) { - - return wcspihlineno; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (! YY_CURRENT_BUFFER) + return 0; + + return yycolumn; } /** Get the input stream. - * + * @param yyscanner The scanner object. */ -FILE *wcspihget_in (void) +FILE *yyget_in (yyscan_t yyscanner) { - return wcspihin; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyin; } /** Get the output stream. - * + * @param yyscanner The scanner object. */ -FILE *wcspihget_out (void) +FILE *yyget_out (yyscan_t yyscanner) { - return wcspihout; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyout; } /** Get the length of the current token. - * + * @param yyscanner The scanner object. */ -int wcspihget_leng (void) +int yyget_leng (yyscan_t yyscanner) { - return wcspihleng; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyleng; } /** Get the current token. - * + * @param yyscanner The scanner object. */ -char *wcspihget_text (void) +char *yyget_text (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yytext; +} + +/** Set the user-defined data. This data is never touched by the scanner. + * @param user_defined The data to be associated with this scanner. + * @param yyscanner The scanner object. + */ +void yyset_extra (YY_EXTRA_TYPE user_defined , yyscan_t yyscanner) { - return wcspihtext; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyextra = user_defined ; } /** Set the current line number. - * @param line_number - * + * @param _line_number line number + * @param yyscanner The scanner object. + */ +void yyset_lineno (int _line_number , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* lineno is only valid if an input buffer exists. */ + if (! YY_CURRENT_BUFFER ) + YY_FATAL_ERROR( "yyset_lineno called with no buffer" ); + + yylineno = _line_number; +} + +/** Set the current column. + * @param _column_no column number + * @param yyscanner The scanner object. */ -void wcspihset_lineno (int line_number ) +void yyset_column (int _column_no , yyscan_t yyscanner) { + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* column is only valid if an input buffer exists. */ + if (! YY_CURRENT_BUFFER ) + YY_FATAL_ERROR( "yyset_column called with no buffer" ); - wcspihlineno = line_number; + yycolumn = _column_no; } /** Set the input stream. This does not discard the current * input buffer. - * @param in_str A readable stream. - * - * @see wcspih_switch_to_buffer + * @param _in_str A readable stream. + * @param yyscanner The scanner object. + * @see yy_switch_to_buffer */ -void wcspihset_in (FILE * in_str ) +void yyset_in (FILE * _in_str , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyin = _in_str ; +} + +void yyset_out (FILE * _out_str , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyout = _out_str ; +} + +int yyget_debug (yyscan_t yyscanner) { - wcspihin = in_str ; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yy_flex_debug; } -void wcspihset_out (FILE * out_str ) +void yyset_debug (int _bdebug , yyscan_t yyscanner) { - wcspihout = out_str ; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yy_flex_debug = _bdebug ; } -int wcspihget_debug (void) +/* Accessor methods for yylval and yylloc */ + +/* User-visible API */ + +/* yylex_init is special because it creates the scanner itself, so it is + * the ONLY reentrant function that doesn't take the scanner as the last argument. + * That's why we explicitly handle the declaration, instead of using our macros. + */ +int yylex_init(yyscan_t* ptr_yy_globals) { - return wcspih_flex_debug; + if (ptr_yy_globals == NULL){ + errno = EINVAL; + return 1; + } + + *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), NULL ); + + if (*ptr_yy_globals == NULL){ + errno = ENOMEM; + return 1; + } + + /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */ + memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); + + return yy_init_globals ( *ptr_yy_globals ); } -void wcspihset_debug (int bdebug ) +/* yylex_init_extra has the same functionality as yylex_init, but follows the + * convention of taking the scanner as the last argument. Note however, that + * this is a *pointer* to a scanner, as it will be allocated by this call (and + * is the reason, too, why this function also must handle its own declaration). + * The user defined value in the first argument will be available to yyalloc in + * the yyextra field. + */ +int yylex_init_extra( YY_EXTRA_TYPE yy_user_defined, yyscan_t* ptr_yy_globals ) { - wcspih_flex_debug = bdebug ; + struct yyguts_t dummy_yyguts; + + yyset_extra (yy_user_defined, &dummy_yyguts); + + if (ptr_yy_globals == NULL){ + errno = EINVAL; + return 1; + } + + *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), &dummy_yyguts ); + + if (*ptr_yy_globals == NULL){ + errno = ENOMEM; + return 1; + } + + /* By setting to 0xAA, we expose bugs in + yy_init_globals. Leave at 0x00 for releases. */ + memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); + + yyset_extra (yy_user_defined, *ptr_yy_globals); + + return yy_init_globals ( *ptr_yy_globals ); } -static int yy_init_globals (void) +static int yy_init_globals (yyscan_t yyscanner) { - /* Initialization is the same as for the non-reentrant scanner. - * This function is called from wcspihlex_destroy(), so don't allocate here. + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + /* Initialization is the same as for the non-reentrant scanner. + * This function is called from yylex_destroy(), so don't allocate here. */ - (yy_buffer_stack) = 0; - (yy_buffer_stack_top) = 0; - (yy_buffer_stack_max) = 0; - (yy_c_buf_p) = (char *) 0; - (yy_init) = 0; - (yy_start) = 0; + yyg->yy_buffer_stack = NULL; + yyg->yy_buffer_stack_top = 0; + yyg->yy_buffer_stack_max = 0; + yyg->yy_c_buf_p = NULL; + yyg->yy_init = 0; + yyg->yy_start = 0; + + yyg->yy_start_stack_ptr = 0; + yyg->yy_start_stack_depth = 0; + yyg->yy_start_stack = NULL; /* Defined in main.c */ #ifdef YY_STDINIT - wcspihin = stdin; - wcspihout = stdout; + yyin = stdin; + yyout = stdout; #else - wcspihin = (FILE *) 0; - wcspihout = (FILE *) 0; + yyin = NULL; + yyout = NULL; #endif /* For future reference: Set errno on error, since we are called by - * wcspihlex_init() + * yylex_init() */ return 0; } -/* wcspihlex_destroy is for both reentrant and non-reentrant scanners. */ -int wcspihlex_destroy (void) +/* yylex_destroy is for both reentrant and non-reentrant scanners. */ +int yylex_destroy (yyscan_t yyscanner) { - + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + /* Pop the buffer stack, destroying each element. */ while(YY_CURRENT_BUFFER){ - wcspih_delete_buffer(YY_CURRENT_BUFFER ); + yy_delete_buffer( YY_CURRENT_BUFFER , yyscanner ); YY_CURRENT_BUFFER_LVALUE = NULL; - wcspihpop_buffer_state(); + yypop_buffer_state(yyscanner); } /* Destroy the stack itself. */ - wcspihfree((yy_buffer_stack) ); - (yy_buffer_stack) = NULL; + yyfree(yyg->yy_buffer_stack , yyscanner); + yyg->yy_buffer_stack = NULL; + + /* Destroy the start condition stack. */ + yyfree( yyg->yy_start_stack , yyscanner ); + yyg->yy_start_stack = NULL; /* Reset the globals. This is important in a non-reentrant scanner so the next time - * wcspihlex() is called, initialization will occur. */ - yy_init_globals( ); + * yylex() is called, initialization will occur. */ + yy_init_globals( yyscanner); + /* Destroy the main struct (reentrant only). */ + yyfree ( yyscanner , yyscanner ); + yyscanner = NULL; return 0; } @@ -10619,18 +26034,21 @@ int wcspihlex_destroy (void) */ #ifndef yytext_ptr -static void yy_flex_strncpy (char* s1, yyconst char * s2, int n ) +static void yy_flex_strncpy (char* s1, const char * s2, int n , yyscan_t yyscanner) { - register int i; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + + int i; for ( i = 0; i < n; ++i ) s1[i] = s2[i]; } #endif #ifdef YY_NEED_STRLEN -static int yy_flex_strlen (yyconst char * s ) +static int yy_flex_strlen (const char * s , yyscan_t yyscanner) { - register int n; + int n; for ( n = 0; s[n]; ++n ) ; @@ -10638,13 +26056,18 @@ static int yy_flex_strlen (yyconst char * s ) } #endif -void *wcspihalloc (yy_size_t size ) +void *yyalloc (yy_size_t size , yyscan_t yyscanner) { - return (void *) malloc( size ); + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + return malloc(size); } -void *wcspihrealloc (void * ptr, yy_size_t size ) +void *yyrealloc (void * ptr, yy_size_t size , yyscan_t yyscanner) { + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + /* The cast to (char *) in the following accommodates both * implementations that use char* generic pointers, and those * that use void* generic pointers. It works with the latter @@ -10652,49 +26075,85 @@ void *wcspihrealloc (void * ptr, yy_size_t size ) * any pointer type to void*, and deal with argument conversions * as though doing an assignment. */ - return (void *) realloc( (char *) ptr, size ); + return realloc(ptr, size); } -void wcspihfree (void * ptr ) +void yyfree (void * ptr , yyscan_t yyscanner) { - free( (char *) ptr ); /* see wcspihrealloc() for (char *) cast */ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + free( (char *) ptr ); /* see yyrealloc() for (char *) cast */ } #define YYTABLES_NAME "yytables" -#line 1019 "wcspih.l" +#line 2490 "wcspih.l" + + +/*---------------------------------------------------------------------------- +* External interface to the scanner. +*---------------------------------------------------------------------------*/ + +int wcspih( + char *header, + int nkeyrec, + int relax, + int ctrl, + int *nreject, + int *nwcs, + struct wcsprm **wcs) + +{ + // Function prototypes. + int yylex_init_extra(YY_EXTRA_TYPE extra, yyscan_t *yyscanner); + int yylex_destroy(yyscan_t yyscanner); + + struct wcspih_extra extra; + yyscan_t yyscanner; + yylex_init_extra(&extra, &yyscanner); + int status = wcspih_scanner(header, nkeyrec, relax, ctrl, nreject, nwcs, + wcs, yyscanner); + yylex_destroy(yyscanner); + return status; +} /*---------------------------------------------------------------------------- * Determine the number of coordinate representations (up to 27) and the -* number of coordinate axes in each, and count the number of PVi_ma and -* PSi_ma keywords in each representation. +* number of coordinate axes in each, which distortions are present, and the +* number of PVi_ma, PSi_ma, DPja, and DQia keywords in each representation. *---------------------------------------------------------------------------*/ -void wcspih_naxes(int naxis, int i, int j, char a, int alts[], int *npptr) +void wcspih_pass1( + int naxis, + int i, + int j, + char a, + int distype, + int alts[], + int dpq[], + int *npptr) { - /* On the first pass alts[] is used to determine the number of axes */ - /* for each of the 27 possible alternate coordinate descriptions. */ - int ialt, *ip; - + // On the first pass alts[] is used to determine the number of axes + // for each of the 27 possible alternate coordinate descriptions. if (a == 0) { return; } - ialt = 0; + int ialt = 0; if (a != ' ') { ialt = a - 'A' + 1; } - ip = alts + ialt; + int *ip = alts + ialt; if (*ip < naxis) { *ip = naxis; } - /* i or j can be greater than naxis. */ + // i or j can be greater than naxis. if (*ip < i) { *ip = i; } @@ -10703,6 +26162,10 @@ void wcspih_naxes(int naxis, int i, int j, char a, int alts[], int *npptr) *ip = j; } + // Type of distortions present. + dpq[ialt] |= distype; + + // Count PVi_ma, PSi_ma, DPja, or DQia keywords. if (npptr) { npptr[ialt]++; } @@ -10714,109 +26177,507 @@ void wcspih_naxes(int naxis, int i, int j, char a, int alts[], int *npptr) * initialize each of them. *---------------------------------------------------------------------------*/ -int wcspih_inits( +int wcspih_init1( int naxis, int alts[], + int dpq[], int npv[], int nps[], + int ndp[], + int ndq[], + int naux, + int distran, int *nwcs, struct wcsprm **wcs) { - int ialt, npsmax, npvmax, status = 0; - struct wcsprm *wcsp; + int status = 0; - /* Find the number of coordinate descriptions. */ + // Find the number of coordinate descriptions. *nwcs = 0; - for (ialt = 0; ialt < 27; ialt++) { + for (int ialt = 0; ialt < 27; ialt++) { if (alts[ialt]) (*nwcs)++; } - if (!(*nwcs) && naxis) { - /* NAXIS is non-zero but there were no WCS keywords with an alternate - version code; create a default WCS with blank alternate version. */ - wcspih_naxes(naxis, 0, 0, ' ', alts, 0x0); + int defaults; + if ((defaults = !(*nwcs) && naxis)) { + // NAXIS is non-zero but there were no WCS keywords with an alternate + // version code; create a default WCS with blank alternate version. + wcspih_pass1(naxis, 0, 0, ' ', 0, alts, dpq, 0x0); *nwcs = 1; } if (*nwcs) { - /* Allocate memory for the required number of wcsprm structs. */ - if (!(*wcs = calloc(*nwcs, sizeof(struct wcsprm)))) { - return 2; + // Allocate memory for the required number of wcsprm structs. + if ((*wcs = calloc(*nwcs, sizeof(struct wcsprm))) == 0x0) { + return WCSHDRERR_MEMORY; } - /* Record the current values of NPVMAX and NPSMAX. */ - npvmax = wcsnpv(-1); - npsmax = wcsnps(-1); + int ndis = 0; + if (distran == SIP) { + // DPja.NAXES and DPja.OFFSET.j to be added for SIP (see below and + // wcspih_final()). + ndp[0] += 6; + + } else if (distran == DSS) { + // DPja.NAXES to be added for DSS (see below and wcspih_final()). + ndq[0] += 2; + } - /* Initialize each wcsprm struct. */ - wcsp = *wcs; + // Initialize each wcsprm struct. + struct wcsprm *wcsp = *wcs; *nwcs = 0; - for (ialt = 0; ialt < 27; ialt++) { + for (int ialt = 0; ialt < 27; ialt++) { if (alts[ialt]) { wcsp->flag = -1; - wcsnpv(npv[ialt]); - wcsnps(nps[ialt]); - if ((status = wcsini(1, alts[ialt], wcsp))) { + int npvmax = npv[ialt]; + int npsmax = nps[ialt]; + if ((status = wcsinit(1, alts[ialt], wcsp, npvmax, npsmax, -1))) { wcsvfree(nwcs, wcs); break; } - /* Record the alternate version code. */ + // Record the alternate version code. if (ialt) { wcsp->alt[0] = 'A' + ialt - 1; } - /* On the second pass alts[] indexes the array of wcsprm structs. */ + // Record in wcsname whether this is a default description. + if (defaults) { + strncpy(wcsp->wcsname, "DEFAULTS", 72); + } + + // Any additional auxiliary keywords present? + if (naux) { + if (wcsauxi(1, wcsp)) { + return WCSHDRERR_MEMORY; + } + } + + // Any distortions present? + struct disprm *disp; + if (dpq[ialt] & 1) { + if ((disp = calloc(1, sizeof(struct disprm))) == 0x0) { + return WCSHDRERR_MEMORY; + } + + // Attach it to linprm. Also inits it. + ndis++; + int ndpmax = ndp[ialt]; + disp->flag = -1; + lindist(1, &(wcsp->lin), disp, ndpmax); + } + + if (dpq[ialt] & 2) { + if ((disp = calloc(1, sizeof(struct disprm))) == 0x0) { + return WCSHDRERR_MEMORY; + } + + // Attach it to linprm. Also inits it. + ndis++; + int ndpmax = ndq[ialt]; + disp->flag = -1; + lindist(2, &(wcsp->lin), disp, ndpmax); + } + + // On the second pass alts[] indexes the array of wcsprm structs. alts[ialt] = (*nwcs)++; wcsp++; + + } else { + // Signal that there is no wcsprm for this alt. + alts[ialt] = -1; } } - /* Restore the original values of NPVMAX and NPSMAX. */ - wcsnpv(npvmax); - wcsnps(npsmax); + + // Translated distortion? Neither SIP nor DSS have alternates, so the + // presence of keywords for either (not both together), as flagged by + // distran, necessarily refers to the primary representation. + if (distran == SIP) { + strncpy((*wcs)->lin.dispre->dtype[0], "SIP", 72); + strncpy((*wcs)->lin.dispre->dtype[1], "SIP", 72); + + // SIP doesn't have axis mapping. + (*wcs)->lin.dispre->ndp = 6; + dpfill((*wcs)->lin.dispre->dp, "DP1", "NAXES", 0, 0, 2, 0.0); + dpfill((*wcs)->lin.dispre->dp+3, "DP2", "NAXES", 0, 0, 2, 0.0); + + } else if (distran == DSS) { + strncpy((*wcs)->lin.disseq->dtype[0], "DSS", 72); + strncpy((*wcs)->lin.disseq->dtype[1], "DSS", 72); + + // The Paper IV translation of DSS doesn't require an axis mapping. + (*wcs)->lin.disseq->ndp = 2; + dpfill((*wcs)->lin.disseq->dp, "DQ1", "NAXES", 0, 0, 2, 0.0); + dpfill((*wcs)->lin.disseq->dp+1, "DQ2", "NAXES", 0, 0, 2, 0.0); + } } return status; } +/*---------------------------------------------------------------------------- +* Interpret the JDREF, JDREFI, and JDREFF keywords. +*---------------------------------------------------------------------------*/ + +int wcspih_jdref(double *mjdref, const double *jdref) + +{ + // Set MJDREF from JDREF. + if (undefined(mjdref[0] && undefined(mjdref[1]))) { + mjdref[0] = jdref[0] - 2400000.0; + mjdref[1] = jdref[1] - 0.5; + + if (mjdref[1] < 0.0) { + mjdref[0] -= 1.0; + mjdref[1] += 1.0; + } + } + + return 0; +} + +int wcspih_jdrefi(double *mjdref, const double *jdrefi) + +{ + // Set the integer part of MJDREF from JDREFI. + if (undefined(mjdref[0])) { + mjdref[0] = *jdrefi - 2400000.5; + } + + return 0; +} + + +int wcspih_jdreff(double *mjdref, const double *jdreff) + +{ + // Set the fractional part of MJDREF from JDREFF. + if (undefined(mjdref[1])) { + mjdref[1] = *jdreff; + } + + return 0; +} + + +/*---------------------------------------------------------------------------- +* Interpret EPOCHa keywords. +*---------------------------------------------------------------------------*/ + +int wcspih_epoch(double *equinox, const double *epoch) + +{ + // If EQUINOXa is currently undefined then set it from EPOCHa. + if (undefined(*equinox)) { + *equinox = *epoch; + } + + return 0; +} + + +/*---------------------------------------------------------------------------- +* Interpret VSOURCEa keywords. +*---------------------------------------------------------------------------*/ + +int wcspih_vsource(double *zsource, const double *vsource) + +{ + const double c = 299792458.0; + + // If ZSOURCEa is currently undefined then set it from VSOURCEa. + if (undefined(*zsource)) { + // Convert relativistic Doppler velocity to redshift. + double beta = *vsource/c; + *zsource = (1.0 + beta)/sqrt(1.0 - beta*beta) - 1.0; + } + + return 0; +} + + +/*---------------------------------------------------------------------------- +* Check validity of a TIMEPIXR keyvalue. +*---------------------------------------------------------------------------*/ + +int wcspih_timepixr(double timepixr) + +{ + return (timepixr < 0.0 || 1.0 < timepixr); +} + + /*---------------------------------------------------------------------------- * Interpret special keywords encountered for each coordinate representation. *---------------------------------------------------------------------------*/ int wcspih_final( - int alts[], - double epoch[], - double vsource[], - int *nwcs, + int ndp[], + int ndq[], + int distran, + double dsstmp[], + char *wat[], + int *nwcs, struct wcsprm **wcs) { - int ialt, status; - double beta, c = 299792458.0; + for (int ialt = 0; ialt < *nwcs; ialt++) { + // Interpret -TAB header keywords. + int status; + if ((status = wcstab(*wcs+ialt))) { + wcsvfree(nwcs, wcs); + return status; + } + + if (ndp[ialt] && ndq[ialt]) { + // Prior and sequent distortions co-exist in this representation; + // ensure the latter gets DVERRa. + (*wcs+ialt)->lin.disseq->totdis = (*wcs+ialt)->lin.dispre->totdis; + } + } + + // Translated distortion functions; apply only to the primary WCS. + struct wcsprm *wcsp = *wcs; + if (distran == SIP) { + // SIP doesn't have alternates, nor axis mapping. + struct disprm *disp = wcsp->lin.dispre; + dpfill(disp->dp+1, "DP1", "OFFSET.1", 0, 1, 0, wcsp->crpix[0]); + dpfill(disp->dp+2, "DP1", "OFFSET.2", 0, 1, 0, wcsp->crpix[1]); + dpfill(disp->dp+4, "DP2", "OFFSET.1", 0, 1, 0, wcsp->crpix[0]); + dpfill(disp->dp+5, "DP2", "OFFSET.2", 0, 1, 0, wcsp->crpix[1]); + + } else if (distran == DSS) { + // DSS doesn't have alternates, nor axis mapping. This translation + // follows Paper IV, Sect. 5.2 using the same variable names. + double CNPIX1 = dsstmp[0]; + double CNPIX2 = dsstmp[1]; + + double Xc = dsstmp[2]/1000.0; + double Yc = dsstmp[3]/1000.0; + double Rx = dsstmp[4]/1000.0; + double Ry = dsstmp[5]/1000.0; + + double A1 = dsstmp[14]; + double A2 = dsstmp[15]; + double A3 = dsstmp[16]; + double B1 = dsstmp[17]; + double B2 = dsstmp[18]; + double B3 = dsstmp[19]; + double S = sqrt(fabs(A1*B1 - A2*B2)); + + double X0 = (A2*B3 - A3*B1) / (A1*B1 - A2*B2); + double Y0 = (A3*B2 - A1*B3) / (A1*B1 - A2*B2); + + wcsp->crpix[0] = (Xc - X0)/Rx - (CNPIX1 - 0.5); + wcsp->crpix[1] = (Yc + Y0)/Ry - (CNPIX2 - 0.5); + + wcsp->pc[0] = A1*Rx/S; + wcsp->pc[1] = -A2*Ry/S; + wcsp->pc[2] = -B2*Rx/S; + wcsp->pc[3] = B1*Ry/S; + wcsp->altlin = 1; + + wcsp->cdelt[0] = -S/3600.0; + wcsp->cdelt[1] = S/3600.0; + + double *crval = wcsp->crval; + crval[0] = (dsstmp[6] + (dsstmp[7] + dsstmp[8] /60.0)/60.0)*15.0; + crval[1] = dsstmp[10] + (dsstmp[11] + dsstmp[12]/60.0)/60.0; + if (dsstmp[9] == -1.0) crval[1] *= -1.0; + + strncpy(wcsp->ctype[0], "RA---TAN", 72); + strncpy(wcsp->ctype[1], "DEC--TAN", 72); + + sprintf(wcsp->wcsname, "DSS PLATEID %.4s", (char *)(dsstmp+13)); + + // Erase the approximate WCS provided in modern DSS headers. + wcsp->cd[0] = 0.0; + wcsp->cd[1] = 0.0; + wcsp->cd[2] = 0.0; + wcsp->cd[3] = 0.0; + + } else if (distran == WAT) { + // TNX and ZPX don't have alternates, nor axis mapping. + char *wp; + int omax, omin, wctrl[4]; + double wval; + struct disprm *disp = wcsp->lin.disseq; + + // Disassemble the core dump stored in the WATi_m strings. + int i, nterms = 0; + for (i = 0; i < 2; i++) { + char wtype[8]; + sscanf(wat[i], "wtype=%s", wtype); + + if (strcmp(wtype, "tnx") == 0) { + strncpy(disp->dtype[i], "WAT-TNX", 72); + } else if (strcmp(wtype, "zpx") == 0) { + strncpy(disp->dtype[i], "WAT-ZPX", 72); + } else { + // Could contain "tan" or something else to be ignored. + lindist(2, &(wcsp->lin), 0x0, 0); + return 0; + } + + // The PROJPn parameters are duplicated on each ZPX axis. + if (i == 1 && strcmp(wtype, "zpx") == 0) { + // Take those on the second (latitude) axis ignoring the other. + // First we have to count them and allocate space in wcsprm. + wp = wat[i]; + int npv; + for (npv = 0; npv < 30; npv++) { + if ((wp = strstr(wp, "projp")) == 0x0) break; + wp += 5; + } + + // Allocate space. + if (npv) { + wcsp->npvmax += npv; + wcsp->pv = realloc(wcsp->pv, wcsp->npvmax*sizeof(struct pvcard)); + if (wcsp->pv == 0x0) { + return WCSHDRERR_MEMORY; + } + + wcsp->m_pv = wcsp->pv; + } + + // Copy the values. + wp = wat[i]; + for (int ipv = wcsp->npv; ipv < wcsp->npvmax; ipv++) { + if ((wp = strstr(wp, "projp")) == 0x0) break; + + int m; + sscanf(wp, "projp%d=%lf", &m, &wval); + wcsp->pv[ipv].i = 2; + wcsp->pv[ipv].m = m; + wcsp->pv[ipv].value = wval; + + wp += 5; + } + + wcsp->npv += npv; + } + + // Read the control parameters. + if ((wp = strchr(wat[i], '"')) == 0x0) { + return WCSHDRERR_PARSER; + } + wp++; + + for (int m = 0; m < 4; m++) { + sscanf(wp, "%d", wctrl+m); + if ((wp = strchr(wp, ' ')) == 0x0) { + return WCSHDRERR_PARSER; + } + wp++; + } - for (ialt = 0; ialt < *nwcs; ialt++) { - /* Check for EPOCH overriding EQUINOXa. */ - if (undefined((*wcs+ialt)->equinox) && !undefined(epoch[ialt])) { - /* Set EQUINOXa. */ - (*wcs+ialt)->equinox = epoch[ialt]; + // How many coefficients are we expecting? + omin = (wctrl[1] < wctrl[2]) ? wctrl[1] : wctrl[2]; + omax = (wctrl[1] < wctrl[2]) ? wctrl[2] : wctrl[1]; + if (wctrl[3] == 0) { + // No cross terms. + nterms += omin + omax; + + } else if (wctrl[3] == 1) { + // Full cross terms. + nterms += omin*omax; + + } else if (wctrl[3] == 2) { + // Half cross terms. + nterms += omin*omax - omin*(omin-1)/2; + } } - /* Check for VSOURCEa overriding ZSOURCEa. */ - if (undefined((*wcs+ialt)->zsource) && !undefined(vsource[ialt])) { - /* Convert relativistic Doppler velocity to redshift. */ - beta = vsource[ialt]/c; - (*wcs+ialt)->zsource = (1.0+beta)/sqrt(1.0 - beta*beta) - 1.0; + // Allocate memory for dpkeys. + ndq[0] += 2*(1 + 1 + 4) + nterms; + + disp->ndpmax += ndq[0]; + disp->dp = realloc(disp->dp, disp->ndpmax*sizeof(struct dpkey)); + if (disp->dp == 0x0) { + return WCSHDRERR_MEMORY; } - /* Interpret -TAB header keywords. */ - if ((status = wcstab(*wcs+ialt))) { - wcsvfree(nwcs, wcs); - return status; + disp->m_dp = disp->dp; + + + // Populate dpkeys. + int idp = disp->ndp; + for (i = 0; i < 2; i++) { + dpfill(disp->dp+(idp++), "DQ", "NAXES", i+1, 0, 2, 0.0); + + // Read the control parameters. + if ((wp = strchr(wat[i], '"')) == 0x0) { + return WCSHDRERR_PARSER; + } + wp++; + + for (int m = 0; m < 4; m++) { + sscanf(wp, "%d", wctrl+m); + if ((wp = strchr(wp, ' ')) == 0x0) { + return WCSHDRERR_PARSER; + } + wp++; + } + + // Polynomial type. + char wpoly[12]; + dpfill(disp->dp+(idp++), "DQ", "WAT.POLY", i+1, 0, wctrl[0], 0.0); + if (wctrl[0] == 1) { + // Chebyshev polynomial. + strncpy(wpoly, "CHBY", 12); + } else if (wctrl[0] == 2) { + // Legendre polynomial. + strncpy(wpoly, "LEGR", 12); + } else if (wctrl[0] == 3) { + // Polynomial is the sum of monomials. + strncpy(wpoly, "MONO", 12); + } else { + // Unknown code. + strncpy(wpoly, "UNKN", 12); + } + + // Read the scaling parameters. + char field[40]; + for (int m = 0; m < 4; m++) { + sscanf(wp, "%lf", &wval); + sprintf(field, "WAT.%c%s", (m<2)?'X':'Y', (m%2)?"MAX":"MIN"); + dpfill(disp->dp+(idp++), "DQ", field, i+1, 1, 0, wval); + + if ((wp = strchr(wp, ' ')) == 0x0) { + return WCSHDRERR_PARSER; + } + wp++; + } + + // Read the coefficients. + for (int n = 0; n < wctrl[2]; n++) { + for (int m = 0; m < wctrl[1]; m++) { + if (wctrl[3] == 0) { + if (m && n) continue; + } else if (wctrl[3] == 2) { + if (m+n > omax-1) continue; + } + + sscanf(wp, "%lf", &wval); + if (wval == 0.0) continue; + + sprintf(field, "WAT.%s.%d_%d", wpoly, m, n); + dpfill(disp->dp+(idp++), "DQ", field, i+1, 1, 0, wval); + + if ((wp = strchr(wp, ' ')) == 0x0) { + return WCSHDRERR_PARSER; + } + wp++; + } + } } + + disp->ndp = idp; } return 0; diff --git a/cextern/wcslib/C/flexed/wcsulex.c b/cextern/wcslib/C/flexed/wcsulex.c index a69f330e04e5..58a78d5a4984 100644 --- a/cextern/wcslib/C/flexed/wcsulex.c +++ b/cextern/wcslib/C/flexed/wcsulex.c @@ -2,35 +2,227 @@ #line 4 "wcsulex.c" +#define _POSIX_C_SOURCE 1 #define YY_INT_ALIGNED short int /* A lexical scanner generated by flex */ +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 6 +#define YY_FLEX_SUBMINOR_VERSION 4 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +#ifdef yy_create_buffer +#define wcsulex_create_buffer_ALREADY_DEFINED +#else #define yy_create_buffer wcsulex_create_buffer +#endif + +#ifdef yy_delete_buffer +#define wcsulex_delete_buffer_ALREADY_DEFINED +#else #define yy_delete_buffer wcsulex_delete_buffer -#define yy_flex_debug wcsulex_flex_debug +#endif + +#ifdef yy_scan_buffer +#define wcsulex_scan_buffer_ALREADY_DEFINED +#else +#define yy_scan_buffer wcsulex_scan_buffer +#endif + +#ifdef yy_scan_string +#define wcsulex_scan_string_ALREADY_DEFINED +#else +#define yy_scan_string wcsulex_scan_string +#endif + +#ifdef yy_scan_bytes +#define wcsulex_scan_bytes_ALREADY_DEFINED +#else +#define yy_scan_bytes wcsulex_scan_bytes +#endif + +#ifdef yy_init_buffer +#define wcsulex_init_buffer_ALREADY_DEFINED +#else #define yy_init_buffer wcsulex_init_buffer +#endif + +#ifdef yy_flush_buffer +#define wcsulex_flush_buffer_ALREADY_DEFINED +#else #define yy_flush_buffer wcsulex_flush_buffer +#endif + +#ifdef yy_load_buffer_state +#define wcsulex_load_buffer_state_ALREADY_DEFINED +#else #define yy_load_buffer_state wcsulex_load_buffer_state +#endif + +#ifdef yy_switch_to_buffer +#define wcsulex_switch_to_buffer_ALREADY_DEFINED +#else #define yy_switch_to_buffer wcsulex_switch_to_buffer -#define yyin wcsulexin -#define yyleng wcsulexleng +#endif + +#ifdef yypush_buffer_state +#define wcsulexpush_buffer_state_ALREADY_DEFINED +#else +#define yypush_buffer_state wcsulexpush_buffer_state +#endif + +#ifdef yypop_buffer_state +#define wcsulexpop_buffer_state_ALREADY_DEFINED +#else +#define yypop_buffer_state wcsulexpop_buffer_state +#endif + +#ifdef yyensure_buffer_stack +#define wcsulexensure_buffer_stack_ALREADY_DEFINED +#else +#define yyensure_buffer_stack wcsulexensure_buffer_stack +#endif + +#ifdef yylex +#define wcsulexlex_ALREADY_DEFINED +#else #define yylex wcsulexlex -#define yylineno wcsulexlineno -#define yyout wcsulexout +#endif + +#ifdef yyrestart +#define wcsulexrestart_ALREADY_DEFINED +#else #define yyrestart wcsulexrestart -#define yytext wcsulextext +#endif + +#ifdef yylex_init +#define wcsulexlex_init_ALREADY_DEFINED +#else +#define yylex_init wcsulexlex_init +#endif + +#ifdef yylex_init_extra +#define wcsulexlex_init_extra_ALREADY_DEFINED +#else +#define yylex_init_extra wcsulexlex_init_extra +#endif + +#ifdef yylex_destroy +#define wcsulexlex_destroy_ALREADY_DEFINED +#else +#define yylex_destroy wcsulexlex_destroy +#endif + +#ifdef yyget_debug +#define wcsulexget_debug_ALREADY_DEFINED +#else +#define yyget_debug wcsulexget_debug +#endif + +#ifdef yyset_debug +#define wcsulexset_debug_ALREADY_DEFINED +#else +#define yyset_debug wcsulexset_debug +#endif + +#ifdef yyget_extra +#define wcsulexget_extra_ALREADY_DEFINED +#else +#define yyget_extra wcsulexget_extra +#endif + +#ifdef yyset_extra +#define wcsulexset_extra_ALREADY_DEFINED +#else +#define yyset_extra wcsulexset_extra +#endif + +#ifdef yyget_in +#define wcsulexget_in_ALREADY_DEFINED +#else +#define yyget_in wcsulexget_in +#endif + +#ifdef yyset_in +#define wcsulexset_in_ALREADY_DEFINED +#else +#define yyset_in wcsulexset_in +#endif + +#ifdef yyget_out +#define wcsulexget_out_ALREADY_DEFINED +#else +#define yyget_out wcsulexget_out +#endif + +#ifdef yyset_out +#define wcsulexset_out_ALREADY_DEFINED +#else +#define yyset_out wcsulexset_out +#endif + +#ifdef yyget_leng +#define wcsulexget_leng_ALREADY_DEFINED +#else +#define yyget_leng wcsulexget_leng +#endif + +#ifdef yyget_text +#define wcsulexget_text_ALREADY_DEFINED +#else +#define yyget_text wcsulexget_text +#endif + +#ifdef yyget_lineno +#define wcsulexget_lineno_ALREADY_DEFINED +#else +#define yyget_lineno wcsulexget_lineno +#endif + +#ifdef yyset_lineno +#define wcsulexset_lineno_ALREADY_DEFINED +#else +#define yyset_lineno wcsulexset_lineno +#endif + +#ifdef yyget_column +#define wcsulexget_column_ALREADY_DEFINED +#else +#define yyget_column wcsulexget_column +#endif + +#ifdef yyset_column +#define wcsulexset_column_ALREADY_DEFINED +#else +#define yyset_column wcsulexset_column +#endif + +#ifdef yywrap +#define wcsulexwrap_ALREADY_DEFINED +#else #define yywrap wcsulexwrap +#endif + +#ifdef yyalloc +#define wcsulexalloc_ALREADY_DEFINED +#else #define yyalloc wcsulexalloc +#endif + +#ifdef yyrealloc +#define wcsulexrealloc_ALREADY_DEFINED +#else #define yyrealloc wcsulexrealloc -#define yyfree wcsulexfree +#endif -#define FLEX_SCANNER -#define YY_FLEX_MAJOR_VERSION 2 -#define YY_FLEX_MINOR_VERSION 5 -#define YY_FLEX_SUBMINOR_VERSION 35 -#if YY_FLEX_SUBMINOR_VERSION > 0 -#define FLEX_BETA +#ifdef yyfree +#define wcsulexfree_ALREADY_DEFINED +#else +#define yyfree wcsulexfree #endif /* First, we deal with platform-specific or compiler-specific issues. */ @@ -103,60 +295,65 @@ typedef unsigned int flex_uint32_t; #define UINT32_MAX (4294967295U) #endif +#ifndef SIZE_MAX +#define SIZE_MAX (~(size_t)0) +#endif + #endif /* ! C99 */ #endif /* ! FLEXINT_H */ -#ifdef __cplusplus - -/* The "const" storage-class-modifier is valid. */ -#define YY_USE_CONST - -#else /* ! __cplusplus */ - -/* C99 requires __STDC__ to be defined as 1. */ -#if defined (__STDC__) +/* begin standard C++ headers. */ -#define YY_USE_CONST - -#endif /* defined (__STDC__) */ -#endif /* ! __cplusplus */ - -#ifdef YY_USE_CONST +/* TODO: this is always defined, so inline it */ #define yyconst const + +#if defined(__GNUC__) && __GNUC__ >= 3 +#define yynoreturn __attribute__((__noreturn__)) #else -#define yyconst +#define yynoreturn #endif /* Returned upon end-of-file. */ #define YY_NULL 0 -/* Promotes a possibly negative, possibly signed char to an unsigned - * integer for use as an array index. If the signed char is negative, - * we want to instead treat it as an 8-bit unsigned char, hence the - * double cast. +/* Promotes a possibly negative, possibly signed char to an + * integer in range [0..255] for use as an array index. */ -#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) +#define YY_SC_TO_UI(c) ((YY_CHAR) (c)) + +/* An opaque pointer. */ +#ifndef YY_TYPEDEF_YY_SCANNER_T +#define YY_TYPEDEF_YY_SCANNER_T +typedef void* yyscan_t; +#endif + +/* For convenience, these vars (plus the bison vars far below) + are macros in the reentrant scanner. */ +#define yyin yyg->yyin_r +#define yyout yyg->yyout_r +#define yyextra yyg->yyextra_r +#define yyleng yyg->yyleng_r +#define yytext yyg->yytext_r +#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno) +#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column) +#define yy_flex_debug yyg->yy_flex_debug_r /* Enter a start condition. This macro really ought to take a parameter, * but we do it the disgusting crufty way forced on us by the ()-less * definition of BEGIN. */ -#define BEGIN (yy_start) = 1 + 2 * - +#define BEGIN yyg->yy_start = 1 + 2 * /* Translate the current start state into a value that can be later handed * to BEGIN to return to the state. The YYSTATE alias is for lex * compatibility. */ -#define YY_START (((yy_start) - 1) / 2) +#define YY_START ((yyg->yy_start - 1) / 2) #define YYSTATE YY_START - /* Action number for EOF rule of a given start state. */ #define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) - /* Special action meaning "start processing a new file". */ -#define YY_NEW_FILE wcsulexrestart(wcsulexin ) - +#define YY_NEW_FILE yyrestart( yyin , yyscanner ) #define YY_END_OF_BUFFER_CHAR 0 /* Size of default input buffer. */ @@ -181,36 +378,32 @@ typedef unsigned int flex_uint32_t; typedef struct yy_buffer_state *YY_BUFFER_STATE; #endif -extern int wcsulexleng; - -extern FILE *wcsulexin, *wcsulexout; +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef size_t yy_size_t; +#endif #define EOB_ACT_CONTINUE_SCAN 0 #define EOB_ACT_END_OF_FILE 1 #define EOB_ACT_LAST_MATCH 2 - + #define YY_LESS_LINENO(n) +#define YY_LINENO_REWIND_TO(ptr) /* Return all but the first "n" matched characters back to the input stream. */ #define yyless(n) \ do \ { \ - /* Undo effects of setting up wcsulextext. */ \ + /* Undo effects of setting up yytext. */ \ int yyless_macro_arg = (n); \ YY_LESS_LINENO(yyless_macro_arg);\ - *yy_cp = (yy_hold_char); \ + *yy_cp = yyg->yy_hold_char; \ YY_RESTORE_YY_MORE_OFFSET \ - (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ - YY_DO_BEFORE_ACTION; /* set up wcsulextext again */ \ + yyg->yy_c_buf_p = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ + YY_DO_BEFORE_ACTION; /* set up yytext again */ \ } \ while ( 0 ) - -#define unput(c) yyunput( c, (yytext_ptr) ) - -#ifndef YY_TYPEDEF_YY_SIZE_T -#define YY_TYPEDEF_YY_SIZE_T -typedef size_t yy_size_t; -#endif +#define unput(c) yyunput( c, yyg->yytext_ptr , yyscanner ) #ifndef YY_STRUCT_YY_BUFFER_STATE #define YY_STRUCT_YY_BUFFER_STATE @@ -224,7 +417,7 @@ struct yy_buffer_state /* Size of input buffer in bytes, not including room for EOB * characters. */ - yy_size_t yy_buf_size; + int yy_buf_size; /* Number of characters read into yy_ch_buf, not including EOB * characters. @@ -252,7 +445,7 @@ struct yy_buffer_state int yy_bs_lineno; /**< The line count. */ int yy_bs_column; /**< The column count. */ - + /* Whether to try to fill the input buffer when we reach the * end of it. */ @@ -269,113 +462,81 @@ struct yy_buffer_state * possible backing-up. * * When we actually see the EOF, we change the status to "new" - * (via wcsulexrestart()), so that the user can continue scanning by - * just pointing wcsulexin at a new input file. + * (via yyrestart()), so that the user can continue scanning by + * just pointing yyin at a new input file. */ #define YY_BUFFER_EOF_PENDING 2 }; #endif /* !YY_STRUCT_YY_BUFFER_STATE */ -/* Stack of input buffers. */ -static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */ -static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */ -static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */ - /* We provide macros for accessing buffer states in case in the * future we want to put the buffer states in a more general * "scanner state". * * Returns the top of the stack, or NULL. */ -#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \ - ? (yy_buffer_stack)[(yy_buffer_stack_top)] \ +#define YY_CURRENT_BUFFER ( yyg->yy_buffer_stack \ + ? yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] \ : NULL) - /* Same as previous macro, but useful when we know that the buffer stack is not * NULL or when we need an lvalue. For internal use only. */ -#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)] - -/* yy_hold_char holds the character lost when wcsulextext is formed. */ -static char yy_hold_char; -static int yy_n_chars; /* number of characters read into yy_ch_buf */ -int wcsulexleng; - -/* Points to current character in buffer. */ -static char *yy_c_buf_p = (char *) 0; -static int yy_init = 0; /* whether we need to initialize */ -static int yy_start = 0; /* start state number */ - -/* Flag which is used to allow wcsulexwrap()'s to do buffer switches - * instead of setting up a fresh wcsulexin. A bit of a hack ... - */ -static int yy_did_buffer_switch_on_eof; - -void wcsulexrestart (FILE *input_file ); -void wcsulex_switch_to_buffer (YY_BUFFER_STATE new_buffer ); -YY_BUFFER_STATE wcsulex_create_buffer (FILE *file,int size ); -void wcsulex_delete_buffer (YY_BUFFER_STATE b ); -void wcsulex_flush_buffer (YY_BUFFER_STATE b ); -void wcsulexpush_buffer_state (YY_BUFFER_STATE new_buffer ); -void wcsulexpop_buffer_state (void ); - -static void wcsulexensure_buffer_stack (void ); -static void wcsulex_load_buffer_state (void ); -static void wcsulex_init_buffer (YY_BUFFER_STATE b,FILE *file ); - -#define YY_FLUSH_BUFFER wcsulex_flush_buffer(YY_CURRENT_BUFFER ) - -YY_BUFFER_STATE wcsulex_scan_buffer (char *base,yy_size_t size ); -YY_BUFFER_STATE wcsulex_scan_string (yyconst char *yy_str ); -YY_BUFFER_STATE wcsulex_scan_bytes (yyconst char *bytes,int len ); - -void *wcsulexalloc (yy_size_t ); -void *wcsulexrealloc (void *,yy_size_t ); -void wcsulexfree (void * ); - -#define yy_new_buffer wcsulex_create_buffer - +#define YY_CURRENT_BUFFER_LVALUE yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] + +void yyrestart ( FILE *input_file , yyscan_t yyscanner ); +void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size , yyscan_t yyscanner ); +void yy_delete_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yy_flush_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yypush_buffer_state ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +void yypop_buffer_state ( yyscan_t yyscanner ); + +static void yyensure_buffer_stack ( yyscan_t yyscanner ); +static void yy_load_buffer_state ( yyscan_t yyscanner ); +static void yy_init_buffer ( YY_BUFFER_STATE b, FILE *file , yyscan_t yyscanner ); +#define YY_FLUSH_BUFFER yy_flush_buffer( YY_CURRENT_BUFFER , yyscanner) + +YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_string ( const char *yy_str , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len , yyscan_t yyscanner ); + +void *yyalloc ( yy_size_t , yyscan_t yyscanner ); +void *yyrealloc ( void *, yy_size_t , yyscan_t yyscanner ); +void yyfree ( void * , yyscan_t yyscanner ); + +#define yy_new_buffer yy_create_buffer #define yy_set_interactive(is_interactive) \ { \ if ( ! YY_CURRENT_BUFFER ){ \ - wcsulexensure_buffer_stack (); \ + yyensure_buffer_stack (yyscanner); \ YY_CURRENT_BUFFER_LVALUE = \ - wcsulex_create_buffer(wcsulexin,YY_BUF_SIZE ); \ + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); \ } \ YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ } - #define yy_set_bol(at_bol) \ { \ if ( ! YY_CURRENT_BUFFER ){\ - wcsulexensure_buffer_stack (); \ + yyensure_buffer_stack (yyscanner); \ YY_CURRENT_BUFFER_LVALUE = \ - wcsulex_create_buffer(wcsulexin,YY_BUF_SIZE ); \ + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); \ } \ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ } - #define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) /* Begin user sect3 */ -#define wcsulexwrap(n) 1 +#define wcsulexwrap(yyscanner) (/*CONSTCOND*/1) #define YY_SKIP_YYWRAP - -typedef char YY_CHAR; - -FILE *wcsulexin = (FILE *) 0, *wcsulexout = (FILE *) 0; +typedef flex_uint8_t YY_CHAR; typedef int yy_state_type; -extern int wcsulexlineno; +#define yytext_ptr yytext_r -int wcsulexlineno = 1; - -extern char *wcsulextext; -#define yytext_ptr wcsulextext -static yyconst flex_int16_t yy_nxt[][128] = +static const flex_int16_t yy_nxt[][128] = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -407,198 +568,198 @@ static yyconst flex_int16_t yy_nxt[][128] = 30, 14, 31, 32, 33, 14, 22, 34, 14, 24, 24, 14, 14, 35, 14, 14, 14, 36, 37, 38, 39, 40, 41, 28, 42, 14, 14, 24, 43, 44, - 41, 29, 45, 14, 46, 47, 14, 48, 49, 14, - 14, 50, 41, 14, 14, 14, 14, 14 + 41, 29, 45, 14, 46, 47, 48, 49, 50, 14, + 14, 51, 41, 14, 14, 14, 14, 14 }, { 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, - 14, 14, 51, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 52, 14, 14, 14, 14, 14, 14, 14, - 17, 14, 52, 14, 14, 14, 52, 19, 14, 53, + 17, 14, 53, 14, 14, 14, 53, 19, 14, 54, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 20, 21, 22, 23, 24, 22, 25, 26, 14, 27, 28, 14, 24, 22, 29, 30, 14, 31, 32, 33, 14, 22, 34, 14, 24, - 24, 54, 14, 35, 14, 14, 14, 36, 37, 38, - 39, 55, 41, 28, 42, 14, 14, 24, 56, 44, - 41, 29, 45, 14, 46, 47, 14, 48, 49, 14, - 14, 50, 41, 14, 14, 14, 14, 14 + 24, 55, 14, 35, 14, 14, 14, 36, 37, 38, + 39, 56, 41, 28, 42, 14, 14, 24, 57, 44, + 41, 29, 45, 14, 46, 47, 48, 49, 50, 14, + 14, 51, 41, 14, 14, 14, 14, 14 }, { - 13, 57, 57, 57, 57, 57, 57, 57, 57, 57, + 13, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, - 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, - 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, - 58, 59, 57, 57, 57, 57, 57, 57, 57, 57, - 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, - 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, - 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, - 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, - 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, - 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 59, 60, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, - 57, 57, 57, 57, 57, 57, 57, 57 + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58 }, - { - 13, 57, 57, 57, 57, 57, 57, 57, 57, 57, - 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, - 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, - 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, - 58, 59, 57, 57, 57, 57, 57, 57, 57, 57, - 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, - 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, - 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, - - 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, - 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, - 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, - 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, - 57, 57, 57, 57, 57, 57, 57, 57 + { + 13, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 59, 60, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58 }, { - 13, 60, 60, 60, 60, 60, 60, 60, 60, 60, - 15, 60, 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, + 13, 61, 61, 61, 61, 61, 61, 61, 61, 61, + 15, 61, 61, 61, 61, 61, 61, 61, 61, 61, + 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, + 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, + 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, - 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, 60, 61, - 60, 62, 60, 60, 60, 60, 60, 63, 60, 60, - 64, 60, 60, 60, 65, 60, 60, 60, 60, 66, - 67, 60, 60, 60, 60, 60, 60, 68, 60, 69, - 70, 60, 71, 60, 72, 60, 60, 73, 60, 74, - 75, 60, 76, 60, 60, 60, 60, 77, 60, 60, - 60, 78, 79, 60, 60, 60, 60, 60 + 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, + 61, 61, 61, 61, 61, 61, 61, 61, 61, 62, + 61, 63, 61, 61, 61, 61, 61, 64, 61, 61, + 65, 61, 61, 61, 66, 61, 61, 61, 61, 67, + 68, 61, 61, 61, 61, 61, 61, 69, 61, 70, + 71, 61, 72, 61, 73, 61, 61, 74, 61, 75, + 76, 61, 77, 61, 61, 61, 61, 78, 61, 61, + 61, 79, 80, 61, 61, 61, 61, 61 }, { - 13, 60, 60, 60, 60, 60, 60, 60, 60, 60, - 15, 60, 60, 60, 60, 60, 60, 60, 60, 60, + 13, 61, 61, 61, 61, 61, 61, 61, 61, 61, + 15, 61, 61, 61, 61, 61, 61, 61, 61, 61, - 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, 60, 61, - 60, 62, 60, 60, 60, 60, 60, 63, 60, 60, - 64, 60, 60, 60, 65, 60, 60, 60, 60, 66, - 67, 60, 60, 60, 60, 60, 60, 68, 60, 69, - 70, 60, 71, 60, 72, 60, 60, 73, 60, 74, - 75, 60, 76, 60, 60, 60, 60, 77, 60, 60, + 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, + 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, + 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, + 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, + 61, 61, 61, 61, 61, 61, 61, 61, 61, 62, + 61, 63, 61, 61, 61, 61, 61, 64, 61, 61, + 65, 61, 61, 61, 66, 61, 61, 61, 61, 67, + 68, 61, 61, 61, 61, 61, 61, 69, 61, 70, + 71, 61, 72, 61, 73, 61, 61, 74, 61, 75, + 76, 61, 77, 61, 61, 61, 61, 78, 61, 61, - 60, 78, 79, 60, 60, 60, 60, 60 + 61, 79, 80, 61, 61, 61, 61, 61 }, - { - 13, 80, 80, 80, 80, 80, 80, 80, 80, 80, - 15, 80, 80, 80, 80, 80, 80, 80, 80, 80, - 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, - 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, - 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, - 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, - 80, 80, 80, 80, 80, 81, 82, 83, 84, 80, - 85, 86, 87, 80, 88, 89, 80, 80, 90, 91, - 92, 80, 93, 94, 95, 80, 96, 97, 80, 80, - - 80, 80, 80, 80, 80, 80, 80, 98, 99, 100, - 101, 102, 80, 103, 104, 80, 80, 80, 105, 106, - 80, 91, 107, 80, 108, 109, 80, 110, 111, 80, - 80, 112, 80, 80, 80, 80, 80, 80 + { + 13, 81, 81, 81, 81, 81, 81, 81, 81, 81, + 15, 81, 81, 81, 81, 81, 81, 81, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, + 81, 81, 81, 81, 81, 82, 83, 84, 85, 81, + 86, 87, 88, 81, 89, 90, 81, 81, 91, 92, + 93, 81, 94, 95, 96, 81, 97, 98, 81, 81, + + 81, 81, 81, 81, 81, 81, 81, 99, 100, 101, + 102, 103, 81, 104, 105, 81, 81, 81, 106, 107, + 81, 92, 108, 81, 109, 110, 111, 112, 113, 81, + 81, 114, 81, 81, 81, 81, 81, 81 }, { - 13, 80, 80, 80, 80, 80, 80, 80, 80, 80, - 15, 80, 80, 80, 80, 80, 80, 80, 80, 80, - 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, - 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, - 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, - 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, + 13, 81, 81, 81, 81, 81, 81, 81, 81, 81, + 15, 81, 81, 81, 81, 81, 81, 81, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, - 80, 80, 80, 80, 80, 81, 82, 83, 84, 80, - 85, 86, 87, 80, 88, 89, 80, 80, 90, 91, - 92, 80, 93, 94, 95, 80, 96, 97, 80, 80, - 80, 80, 80, 80, 80, 80, 80, 98, 99, 100, - 101, 102, 80, 103, 104, 80, 80, 80, 105, 106, - 80, 91, 107, 80, 108, 109, 80, 110, 111, 80, - 80, 112, 80, 80, 80, 80, 80, 80 + 81, 81, 81, 81, 81, 82, 83, 84, 85, 81, + 86, 87, 88, 81, 89, 90, 81, 81, 91, 92, + 93, 81, 94, 95, 96, 81, 97, 98, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 99, 100, 101, + 102, 103, 81, 104, 105, 81, 81, 81, 106, 107, + 81, 92, 108, 81, 109, 110, 111, 112, 113, 81, + 81, 114, 81, 81, 81, 81, 81, 81 }, { - 13, 113, 113, 113, 113, 113, 113, 113, 113, 113, - 15, 113, 113, 113, 113, 113, 113, 113, 113, 113, - 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, + 13, 115, 115, 115, 115, 115, 115, 115, 115, 115, + 15, 115, 115, 115, 115, 115, 115, 115, 115, 115, + 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, - 113, 113, 114, 113, 113, 113, 113, 113, 113, 113, - 115, 113, 116, 117, 113, 117, 118, 119, 113, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 113, 113, - 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, - 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, - 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, - 113, 113, 113, 121, 122, 113, 113, 113, 113, 113, - 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, - 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, - 113, 113, 113, 113, 113, 113, 113, 113 + 115, 115, 116, 115, 115, 115, 115, 115, 115, 115, + 117, 115, 118, 119, 115, 119, 120, 121, 115, 122, + 122, 122, 122, 122, 122, 122, 122, 122, 115, 115, + 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, + 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, + 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, + 115, 115, 115, 123, 124, 115, 115, 115, 115, 115, + 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, + 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, + 115, 115, 115, 115, 115, 115, 115, 115 }, { - 13, 113, 113, 113, 113, 113, 113, 113, 113, 113, - 15, 113, 113, 113, 113, 113, 113, 113, 113, 113, - 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, - 113, 113, 114, 113, 113, 113, 113, 113, 113, 113, - 115, 113, 116, 117, 113, 117, 118, 119, 113, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 113, 113, - 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, - 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, - 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, - 113, 113, 113, 121, 122, 113, 113, 113, 113, 113, + 13, 115, 115, 115, 115, 115, 115, 115, 115, 115, + 15, 115, 115, 115, 115, 115, 115, 115, 115, 115, + 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, + 115, 115, 116, 115, 115, 115, 115, 115, 115, 115, + 117, 115, 118, 119, 115, 119, 120, 121, 115, 122, + 122, 122, 122, 122, 122, 122, 122, 122, 115, 115, + 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, + 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, + 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, + 115, 115, 115, 123, 124, 115, 115, 115, 115, 115, - 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, - 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, - 113, 113, 113, 113, 113, 113, 113, 113 + 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, + 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, + 115, 115, 115, 115, 115, 115, 115, 115 }, { - 13, 123, 123, 123, 123, 123, 123, 123, 123, 123, - 15, 123, 123, 123, 123, 123, 123, 123, 123, 123, - 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, - 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, - 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, - 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, - 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, + 13, 125, 125, 125, 125, 125, 125, 125, 125, 125, + 15, 125, 125, 125, 125, 125, 125, 125, 125, 125, + 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, + 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, + 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, + 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, + 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, - 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, - 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, - 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, - 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, - 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, - 123, 123, 123, 123, 123, 123, 123, 123 + 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, + 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, + 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, + 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, + 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, + 125, 125, 125, 125, 125, 125, 125, 125 }, { - 13, 123, 123, 123, 123, 123, 123, 123, 123, 123, - 15, 123, 123, 123, 123, 123, 123, 123, 123, 123, - 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, - 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, + 13, 125, 125, 125, 125, 125, 125, 125, 125, 125, + 15, 125, 125, 125, 125, 125, 125, 125, 125, 125, + 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, + 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, - 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, - 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, - 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, - 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, - 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, - 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, - 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, - 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, - 123, 123, 123, 123, 123, 123, 123, 123 + 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, + 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, + 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, + 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, + 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, + 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, + 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, + 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, + 125, 125, 125, 125, 125, 125, 125, 125 }, { @@ -658,7 +819,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, - -16, -16, 124, -16, -16, -16, -16, -16, -16, -16, + -16, -16, 126, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, @@ -732,11 +893,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, - -20, -20, -20, -20, -20, 125, -20, -20, -20, -20, + -20, -20, -20, -20, -20, 127, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, - 126, -20, -20, -20, -20, -20, -20, -20, -20, -20, + 128, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20 }, @@ -754,7 +915,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -21, -21, -21, -21, -21, -21, -21, -21, -21, -21, -21, -21, -21, -21, -21, -21, -21, -21, -21, -21, -21, -21, -21, -21, -21, -21, -21, -21, -21, -21, - -21, 127, -21, -21, -21, -21, -21, -21 + -21, 129, -21, -21, -21, -21, -21, -21 }, { @@ -799,14 +960,14 @@ static yyconst flex_int16_t yy_nxt[][128] = -24, -24, -24, -24, -24, -24, -24, -24, -24, -24, -24, -24, -24, -24, -24, -24, -24, -24, -24, -24, -24, -24, -24, -24, -24, -24, -24, -24, -24, -24, - -24, -24, -24, -24, -24, 128, 129, 130, -24, -24, - 130, 131, 132, -24, 133, 128, -24, -24, 130, 134, + -24, -24, -24, -24, -24, 130, 131, 132, -24, -24, + 132, 133, 134, -24, 135, 130, -24, -24, 132, 136, - 135, -24, 131, 130, 130, -24, 130, 136, -24, -24, - -24, -24, -24, -24, -24, -24, -24, 137, 138, 139, - -24, 140, -24, 128, -24, -24, -24, -24, 141, 142, - -24, 134, 143, -24, 144, 145, -24, -24, -24, -24, - -24, 146, -24, -24, -24, -24, -24, -24 + 137, -24, 133, 132, 132, -24, 132, 138, -24, -24, + -24, -24, -24, -24, -24, -24, -24, 139, 140, 141, + -24, 142, -24, 130, -24, -24, -24, -24, 143, 144, + -24, 136, 145, -24, 146, 147, -24, -24, -24, -24, + -24, 148, -24, -24, -24, -24, -24, -24 }, { @@ -817,13 +978,13 @@ static yyconst flex_int16_t yy_nxt[][128] = -25, -25, -25, -25, -25, -25, -25, -25, -25, -25, -25, -25, -25, -25, -25, -25, -25, -25, -25, -25, - -25, -25, -25, -25, -25, 128, 129, 130, -25, -25, - 130, 131, 132, -25, 133, 128, -25, -25, 130, 134, - 135, -25, 131, 130, 130, -25, 130, 136, -25, -25, - -25, -25, -25, -25, -25, -25, -25, 137, 138, 139, - -25, 140, -25, 128, -25, -25, -25, -25, 141, 142, - -25, 134, 143, -25, 144, 145, -25, -25, -25, -25, - -25, 146, -25, -25, -25, -25, -25, -25 + -25, -25, -25, -25, -25, 130, 131, 132, -25, -25, + 132, 133, 134, -25, 135, 130, -25, -25, 132, 136, + 137, -25, 133, 132, 132, -25, 132, 138, -25, -25, + -25, -25, -25, -25, -25, -25, -25, 139, 140, 141, + -25, 142, -25, 130, -25, -25, -25, -25, 143, 144, + -25, 136, 145, -25, 146, 147, -25, -25, -25, -25, + -25, 148, -25, -25, -25, -25, -25, -25 }, { @@ -841,7 +1002,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, - -26, -26, 147, -26, -26, -26, -26, -26 + -26, -26, 149, -26, -26, -26, -26, -26 }, { @@ -858,7 +1019,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, - -27, 148, -27, -27, -27, -27, -27, -27 + -27, 150, -27, -27, -27, -27, -27, -27 }, { @@ -890,7 +1051,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, - -29, -29, -29, -29, 149, -29, -29, -29, -29, -29, + -29, -29, -29, -29, 151, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29 @@ -903,14 +1064,14 @@ static yyconst flex_int16_t yy_nxt[][128] = -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, - -30, -30, -30, -30, -30, 128, 129, 130, -30, -30, - 130, 131, 132, -30, 133, 128, -30, -30, 130, 134, - 135, -30, 131, 130, 130, -30, 130, 136, -30, -30, - -30, -30, -30, -30, -30, -30, -30, 150, 138, 139, + -30, -30, -30, -30, -30, 130, 131, 132, -30, -30, + 132, 133, 134, -30, 135, 130, -30, -30, 132, 136, + 137, -30, 133, 132, 132, -30, 132, 138, -30, -30, + -30, -30, -30, -30, -30, -30, -30, 152, 140, 141, - -30, 140, -30, 128, -30, -30, -30, -30, 141, 142, - -30, 134, 143, -30, 144, 145, -30, -30, -30, -30, - -30, 146, -30, -30, -30, -30, -30, -30 + -30, 142, -30, 130, -30, -30, -30, -30, 143, 144, + -30, 136, 145, -30, 146, 147, -30, -30, -30, -30, + -30, 148, -30, -30, -30, -30, -30, -30 }, { @@ -927,7 +1088,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, - -31, 151, -31, -31, -31, -31, -31, -31 + -31, 153, -31, -31, -31, -31, -31, -31 }, { @@ -943,7 +1104,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, - -32, -32, -32, -32, -32, -32, -32, 152, -32, -32, + -32, -32, -32, -32, -32, -32, -32, 154, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32 }, @@ -955,14 +1116,14 @@ static yyconst flex_int16_t yy_nxt[][128] = -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, - -33, -33, -33, -33, -33, 128, 129, 130, -33, -33, - 130, 131, 132, -33, 133, 128, -33, -33, 130, 134, - 135, -33, 131, 130, 130, -33, 130, 136, -33, -33, - -33, -33, -33, -33, -33, -33, -33, 137, 138, 139, - -33, 140, -33, 128, -33, -33, -33, -33, 141, 142, + -33, -33, -33, -33, -33, 130, 131, 132, -33, -33, + 132, 133, 134, -33, 135, 130, -33, -33, 132, 136, + 137, -33, 133, 132, 132, -33, 132, 138, -33, -33, + -33, -33, -33, -33, -33, -33, -33, 139, 140, 141, + -33, 142, -33, 130, -33, -33, -33, -33, 143, 144, - -33, 134, 143, -33, 144, 145, -33, -33, -33, -33, - -33, 146, -33, -33, -33, -33, -33, -33 + -33, 136, 145, -33, 146, 147, -33, -33, -33, -33, + -33, 148, -33, -33, -33, -33, -33, -33 }, { @@ -976,7 +1137,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, - -34, -34, -34, -34, -34, -34, -34, -34, 147, -34, + -34, -34, -34, -34, -34, -34, -34, -34, 149, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34 @@ -1007,12 +1168,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, - -36, -36, -36, -36, -36, 128, -36, 130, -36, -36, - 130, 131, 132, -36, 133, 128, -36, -36, 130, 134, - 135, -36, 131, 130, 130, -36, 130, 136, -36, -36, - -36, -36, -36, -36, -36, -36, -36, -36, 153, 139, - 154, 140, -36, 128, -36, -36, -36, -36, 141, 155, - 126, 134, -36, -36, 156, 145, -36, -36, -36, -36, + -36, -36, -36, -36, -36, 130, -36, 132, -36, -36, + 132, 133, 134, -36, 135, 130, -36, -36, 132, 136, + 137, -36, 133, 132, 132, -36, 132, 138, -36, -36, + -36, -36, -36, -36, -36, -36, -36, -36, 155, 141, + 156, 142, -36, 130, -36, -36, -36, -36, 143, 157, + 128, 136, -36, -36, 158, 147, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36 }, @@ -1028,10 +1189,10 @@ static yyconst flex_int16_t yy_nxt[][128] = -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, - -37, -37, -37, -37, -37, -37, -37, 157, -37, -37, - -37, 158, -37, -37, -37, 159, -37, -37, -37, -37, + -37, -37, -37, -37, -37, -37, -37, 159, -37, -37, + -37, 160, -37, -37, -37, 161, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, - -37, 127, -37, -37, -37, -37, -37, -37 + -37, 129, -37, -37, -37, -37, -37, -37 }, { @@ -1042,13 +1203,13 @@ static yyconst flex_int16_t yy_nxt[][128] = -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, - -38, -38, -38, -38, -38, 128, -38, 130, -38, -38, - 130, 131, 132, -38, 133, 128, -38, -38, 130, 134, - 135, -38, 131, 130, 130, -38, 130, 136, -38, -38, - -38, -38, -38, -38, -38, -38, -38, -38, 153, 139, - 160, 140, -38, 128, 161, -38, -38, -38, 141, 155, - -38, 162, -38, -38, 144, 145, 163, -38, -38, -38, - -38, -38, -38, -38, -38, -38, -38, -38 + -38, -38, -38, -38, -38, 130, -38, 132, -38, -38, + 132, 133, 134, -38, 135, 130, -38, -38, 132, 136, + 137, -38, 133, 132, 132, -38, 132, 138, -38, -38, + -38, -38, -38, -38, -38, -38, -38, -38, 155, 141, + 162, 142, -38, 130, 163, -38, -38, -38, 143, 157, + -38, 164, -38, -38, 146, 147, 165, -38, -38, -38, + -38, 153, -38, -38, -38, -38, -38, -38 }, { @@ -1059,12 +1220,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, - -39, -39, -39, -39, -39, 128, -39, 130, -39, -39, - 130, 131, 132, -39, 133, 128, -39, -39, 130, 134, - 135, -39, 131, 130, 130, -39, 130, 136, -39, -39, - -39, -39, -39, -39, -39, -39, -39, 164, 153, 139, - -39, 165, -39, 128, -39, -39, -39, -39, 141, 155, - -39, 134, -39, -39, 144, 145, -39, -39, -39, -39, + -39, -39, -39, -39, -39, 130, -39, 132, -39, -39, + 132, 133, 134, -39, 135, 130, -39, -39, 132, 136, + 137, -39, 133, 132, 132, -39, 132, 138, -39, -39, + -39, -39, -39, -39, -39, -39, -39, 166, 155, 141, + -39, 167, -39, 130, -39, -39, -39, -39, 143, 157, + -39, 136, -39, -39, 146, 147, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39 }, @@ -1078,11 +1239,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, - -40, -40, -40, -40, -40, -40, 148, -40, -40, -40, + -40, -40, -40, -40, -40, -40, 150, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, - -40, -40, -40, -40, 166, -40, -40, -40, -40, -40, + -40, -40, -40, -40, 168, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40 }, @@ -1093,13 +1254,13 @@ static yyconst flex_int16_t yy_nxt[][128] = -41, -41, -41, -41, -41, -41, -41, -41, -41, -41, -41, -41, -41, -41, -41, -41, -41, -41, -41, -41, -41, -41, -41, -41, -41, -41, -41, -41, -41, -41, - -41, -41, -41, -41, -41, 128, -41, 130, -41, -41, + -41, -41, -41, -41, -41, 130, -41, 132, -41, -41, - 130, 131, 132, -41, 133, 128, -41, -41, 130, 134, - 135, -41, 131, 130, 130, -41, 130, 136, -41, -41, - -41, -41, -41, -41, -41, -41, -41, -41, 153, 139, - -41, 140, -41, 128, -41, -41, -41, -41, 141, 155, - -41, 134, -41, -41, 144, 145, -41, -41, -41, -41, + 132, 133, 134, -41, 135, 130, -41, -41, 132, 136, + 137, -41, 133, 132, 132, -41, 132, 138, -41, -41, + -41, -41, -41, -41, -41, -41, -41, -41, 155, 141, + -41, 142, -41, 130, -41, -41, -41, -41, 143, 157, + -41, 136, -41, -41, 146, 147, -41, -41, -41, -41, -41, -41, -41, -41, -41, -41, -41, -41 }, @@ -1111,13 +1272,13 @@ static yyconst flex_int16_t yy_nxt[][128] = -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, - -42, -42, -42, -42, -42, 128, 129, 130, -42, -42, - 130, 131, 132, -42, 133, 128, -42, -42, 130, 134, - 135, -42, 131, 130, 130, -42, 130, 136, -42, -42, - -42, -42, -42, -42, -42, -42, -42, 137, 138, 139, - -42, 140, -42, 128, -42, -42, -42, -42, 141, 142, - -42, 134, 143, -42, 144, 145, -42, -42, -42, -42, - -42, 146, -42, -42, -42, -42, -42, -42 + -42, -42, -42, -42, -42, 130, 131, 132, -42, -42, + 132, 133, 134, -42, 135, 130, -42, -42, 132, 136, + 137, -42, 133, 132, 132, -42, 132, 138, -42, -42, + -42, -42, -42, -42, -42, -42, -42, 139, 140, 141, + -42, 142, -42, 130, -42, -42, -42, -42, 143, 144, + -42, 136, 145, -42, 146, 147, -42, -42, -42, -42, + -42, 148, -42, -42, -42, -42, -42, -42 }, { @@ -1132,10 +1293,10 @@ static yyconst flex_int16_t yy_nxt[][128] = -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, - -43, -43, -43, -43, -43, -43, -43, -43, -43, 147, + -43, -43, -43, -43, -43, -43, -43, -43, -43, 149, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, - 147, 167, -43, -43, -43, -43, -43, -43 + 149, 169, -43, -43, -43, -43, -43, -43 }, { @@ -1145,13 +1306,13 @@ static yyconst flex_int16_t yy_nxt[][128] = -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, - -44, -44, -44, -44, -44, 128, -44, 130, -44, -44, - 130, 131, 132, -44, 133, 128, -44, -44, 130, 134, + -44, -44, -44, -44, -44, 130, -44, 132, -44, -44, + 132, 133, 134, -44, 135, 130, -44, -44, 132, 136, - 135, -44, 131, 130, 130, -44, 130, 136, -44, -44, - -44, -44, -44, -44, -44, -44, -44, 168, 153, 139, - -44, 140, -44, 128, -44, 169, -44, -44, 141, 155, - -44, 170, -44, -44, 144, 145, -44, -44, -44, -44, + 137, -44, 133, 132, 132, -44, 132, 138, -44, -44, + -44, -44, -44, -44, -44, -44, -44, 170, 155, 141, + -44, 142, -44, 130, -44, 171, -44, -44, 143, 157, + -44, 172, -44, -44, 146, 147, -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, -44 }, @@ -1163,12 +1324,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, - -45, -45, -45, -45, -45, 128, -45, 130, -45, -45, - 130, 131, 132, -45, 133, 128, -45, -45, 130, 134, - 135, -45, 131, 130, 130, -45, 130, 136, -45, -45, - -45, -45, -45, -45, -45, -45, -45, -45, 153, 171, - -45, 140, -45, 128, 172, 173, -45, -45, 141, 155, - -45, 134, -45, -45, 144, 145, -45, -45, -45, -45, + -45, -45, -45, -45, -45, 130, -45, 132, -45, -45, + 132, 133, 134, -45, 135, 130, -45, -45, 132, 136, + 137, -45, 133, 132, 132, -45, 132, 138, -45, -45, + -45, -45, -45, -45, -45, -45, -45, -45, 155, 173, + -45, 142, -45, 130, 174, 175, -45, -45, 143, 157, + -45, 136, -45, -45, 146, 147, -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, -45 }, @@ -1183,7 +1344,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, - -46, -46, -46, -46, -46, -46, -46, 174, -46, -46, + -46, -46, -46, -46, -46, -46, -46, 176, -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, @@ -1203,7 +1364,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -47, -47, -47, -47, -47, -47, -47, -47, -47, -47, -47, -47, -47, -47, -47, -47, -47, -47, -47, -47, - -47, 175, -47, 176, 160, -47, -47, -47, -47, -47, + -47, 177, -47, 178, 162, -47, -47, -47, -47, -47, -47, -47, -47, -47, -47, -47, -47, -47 }, @@ -1215,12 +1376,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, - -48, -48, -48, -48, -48, 128, -48, 130, -48, -48, - 130, 131, 132, -48, 133, 128, -48, -48, 130, 134, - 135, -48, 131, 130, 130, -48, 130, 136, -48, -48, - -48, -48, -48, -48, -48, -48, -48, -48, 153, 139, - -48, 140, -48, 128, -48, -48, -48, -48, 141, 155, - -48, 134, -48, -48, 144, 145, -48, -48, -48, -48, + -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, + -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, + -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, + -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, + -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, + -48, -48, -48, -48, -48, -48, -48, 179, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48 }, @@ -1232,12 +1393,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, - -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, - -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, - -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, - -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, - -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, - -49, 177, -49, -49, -49, -49, -49, -49, -49, -49, + -49, -49, -49, -49, -49, 130, -49, 132, -49, -49, + 132, 133, 134, -49, 135, 130, -49, -49, 132, 136, + 137, -49, 133, 132, 132, -49, 132, 138, -49, -49, + -49, -49, -49, -49, -49, -49, -49, -49, 155, 141, + -49, 142, -49, 130, -49, -49, -49, -49, 143, 157, + -49, 136, -49, -49, 146, 147, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49 }, @@ -1249,13 +1410,13 @@ static yyconst flex_int16_t yy_nxt[][128] = -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, - -50, -50, -50, -50, -50, 128, -50, 130, -50, -50, - 130, 131, 132, -50, 133, 128, -50, -50, 130, 134, - 135, -50, 131, 130, 130, -50, 130, 136, -50, -50, - -50, -50, -50, -50, -50, -50, -50, -50, 153, 139, + -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, + -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, + -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, + -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, - -50, 140, -50, 128, -50, -50, -50, -50, 141, 155, - -50, 134, -50, -50, 178, 145, -50, -50, -50, -50, + -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, + -50, 180, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50 }, @@ -1263,16 +1424,16 @@ static yyconst flex_int16_t yy_nxt[][128] = 13, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, - -51, -51, 179, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, + -51, -51, -51, -51, -51, 130, -51, 132, -51, -51, - -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, - -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, - -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, - -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, - -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, + 132, 133, 134, -51, 135, 130, -51, -51, 132, 136, + 137, -51, 133, 132, 132, -51, 132, 138, -51, -51, + -51, -51, -51, -51, -51, -51, -51, -51, 155, 141, + -51, 142, -51, 130, -51, -51, -51, -51, 143, 157, + -51, 136, -51, -51, 181, 147, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51 }, @@ -1280,7 +1441,7 @@ static yyconst flex_int16_t yy_nxt[][128] = 13, -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, - -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, + -52, -52, 182, -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, @@ -1299,7 +1460,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, - -53, -53, -53, -53, -53, -53, -53, 180, 181, -53, + -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, @@ -1316,7 +1477,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, - -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, + -54, -54, -54, -54, -54, -54, -54, 183, 184, -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, @@ -1338,11 +1499,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, - -55, -55, -55, -55, -55, -55, 148, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, - -55, -55, -55, -55, 166, -55, -55, -55, -55, -55, - 182, -55, -55, -55, -55, -55, -55, -55 + -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, + -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, + -55, -55, -55, -55, -55, -55, -55, -55 }, { @@ -1355,46 +1516,46 @@ static yyconst flex_int16_t yy_nxt[][128] = -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, + -56, -56, -56, -56, -56, -56, 150, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, - -56, -56, -56, -56, -56, -56, -56, -56, -56, 147, - 183, 184, -56, -56, -56, -56, -56, -56, -56, -56, + -56, -56, -56, -56, 168, -56, -56, -56, -56, -56, - 147, 167, -56, -56, -56, -56, -56, -56 + 185, -56, -56, -56, -56, -56, -56, -56 }, { - 13, 185, 185, 185, 185, 185, 185, 185, 185, 185, - 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, - 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, - 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, - -57, -57, 185, 185, 185, 185, 185, 185, 185, 185, - 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, - 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, - 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, - 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, + 13, -57, -57, -57, -57, -57, -57, -57, -57, -57, + -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, + -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, + -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, + -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, + -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, + -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, + -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, + -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, - 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, - 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, - 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, - 185, 185, 185, 185, 185, 185, 185, 185 + -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, + -57, -57, -57, -57, -57, -57, -57, -57, -57, 149, + 186, 187, -57, -57, -57, -57, -57, -57, -57, -57, + 149, 169, -57, -57, -57, -57, -57, -57 }, { - 13, -58, -58, -58, -58, -58, -58, -58, -58, -58, - -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, - -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, - -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, - -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, - -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, + 13, 188, 188, 188, 188, 188, 188, 188, 188, 188, + 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, + 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, + 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, + -58, -58, 188, 188, 188, 188, 188, 188, 188, 188, + 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, - -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, - -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, - -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, - -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, - -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, - -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, - -58, -58, -58, -58, -58, -58, -58, -58 + 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, + 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, + 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, + 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, + 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, + 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, + 188, 188, 188, 188, 188, 188, 188, 188 }, { @@ -1598,7 +1759,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, - -70, -70, -70, -70, -70, -70, -70, 186, -70, -70, + -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, @@ -1616,7 +1777,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, - -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, + -71, -71, -71, -71, -71, -71, -71, 189, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71 @@ -1788,10 +1949,10 @@ static yyconst flex_int16_t yy_nxt[][128] = -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, - -81, -81, -81, -81, -81, 187, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, - 188, -81, -81, -81, -81, -81, -81, -81, -81, -81, + -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, + -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81 }, @@ -1805,11 +1966,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, + -82, -82, -82, -82, -82, 190, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, - -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, - -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, - -82, 189, -82, -82, -82, -82, -82, -82 + 191, -82, -82, -82, -82, -82, -82, -82, -82, -82, + -82, -82, -82, -82, -82, -82, -82, -82 }, { @@ -1827,7 +1988,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, - -83, -83, -83, -83, -83, -83, -83, -83 + -83, 192, -83, -83, -83, -83, -83, -83 }, { @@ -1896,7 +2057,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, - -87, -87, 190, -87, -87, -87, -87, -87 + -87, -87, -87, -87, -87, -87, -87, -87 }, { @@ -1913,7 +2074,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, - -88, 191, -88, -88, -88, -88, -88, -88 + -88, -88, 193, -88, -88, -88, -88, -88 }, { @@ -1930,7 +2091,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, - -89, -89, -89, -89, -89, -89, -89, -89 + -89, 194, -89, -89, -89, -89, -89, -89 }, @@ -1963,7 +2124,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, - -91, -91, -91, -91, 192, -91, -91, -91, -91, -91, + -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91 }, @@ -1979,8 +2140,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, - -92, -92, -92, -92, -92, -92, -92, 193, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, + -92, -92, -92, -92, 195, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92 }, @@ -1996,11 +2157,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, - -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, + -93, -93, -93, -93, -93, -93, -93, 196, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, - -93, 194, -93, -93, -93, -93, -93, -93 + -93, -93, -93, -93, -93, -93, -93, -93 }, { @@ -2016,8 +2177,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, - -94, -94, -94, -94, -94, -94, -94, 195, -94, -94, - -94, -94, -94, -94, -94, -94, -94, -94 + -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, + -94, 197, -94, -94, -94, -94, -94, -94 }, { @@ -2033,7 +2194,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, - -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, + -95, -95, -95, -95, -95, -95, -95, 198, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95 }, @@ -2066,7 +2227,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, - -97, -97, -97, -97, -97, -97, -97, -97, 196, -97, + -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97 @@ -2083,9 +2244,9 @@ static yyconst flex_int16_t yy_nxt[][128] = -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, + -98, -98, -98, -98, -98, -98, -98, -98, 199, -98, + -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, - 197, -98, -98, -98, -98, -98, -98, -98, -98, -98, - 188, -98, -98, -98, 198, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98 }, @@ -2100,10 +2261,10 @@ static yyconst flex_int16_t yy_nxt[][128] = -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, - -99, -99, -99, -99, -99, -99, -99, 199, -99, -99, - -99, 200, -99, -99, -99, 201, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, - -99, 189, -99, -99, -99, -99, -99, -99 + 200, -99, -99, -99, -99, -99, -99, -99, -99, -99, + 191, -99, -99, -99, 201, -99, -99, -99, -99, -99, + -99, -99, -99, -99, -99, -99, -99, -99 }, @@ -2117,11 +2278,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, - -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, + -100, -100, -100, -100, -100, -100, -100, 202, -100, -100, - 202, -100, -100, -100, 203, -100, -100, -100, -100, -100, - -100, 204, -100, -100, -100, -100, 205, -100, -100, -100, - -100, -100, -100, -100, -100, -100, -100, -100 + -100, 203, -100, -100, -100, 204, -100, -100, -100, -100, + -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, + -100, 192, -100, -100, -100, -100, -100, -100 }, { @@ -2136,9 +2297,9 @@ static yyconst flex_int16_t yy_nxt[][128] = -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, - -101, 206, -101, -101, -101, -101, -101, -101, -101, -101, - -101, -101, -101, -101, -101, -101, -101, -101, -101, -101, - -101, -101, -101, -101, -101, -101, -101, -101 + 205, -101, -101, -101, 206, -101, -101, -101, -101, -101, + -101, 207, -101, -101, -101, -101, 208, -101, -101, -101, + -101, 209, -101, -101, -101, -101, -101, -101 }, { @@ -2151,10 +2312,10 @@ static yyconst flex_int16_t yy_nxt[][128] = -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, - -102, -102, -102, -102, -102, -102, 207, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, - -102, -102, -102, -102, 208, -102, -102, -102, -102, -102, + -102, 210, -102, -102, -102, -102, -102, -102, -102, -102, + -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102 }, @@ -2168,11 +2329,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, - -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, + -103, -103, -103, -103, -103, -103, 211, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, - -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, + -103, -103, -103, -103, 212, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103 }, @@ -2205,9 +2366,9 @@ static yyconst flex_int16_t yy_nxt[][128] = -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, - -105, -105, -105, -105, -105, -105, -105, -105, -105, 209, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, - 210, 211, -105, -105, -105, -105, -105, -105 + -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, + -105, -105, -105, -105, -105, -105, -105, -105 }, { @@ -2221,11 +2382,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, - -106, -106, -106, -106, -106, -106, -106, 212, -106, -106, - -106, -106, -106, -106, -106, 213, -106, -106, -106, -106, - -106, 214, -106, -106, -106, -106, -106, -106, -106, -106, + -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, + -106, -106, -106, -106, -106, -106, -106, -106, -106, 213, + -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, - -106, -106, -106, -106, -106, -106, -106, -106 + 214, 215, -106, -106, -106, -106, -106, -106 }, { @@ -2239,9 +2400,9 @@ static yyconst flex_int16_t yy_nxt[][128] = -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, - -107, -107, -107, -107, -107, -107, -107, -107, -107, 215, - -107, -107, -107, -107, 216, 217, -107, -107, -107, -107, - -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, + -107, -107, -107, -107, -107, -107, -107, 216, -107, -107, + -107, -107, -107, -107, -107, 217, -107, -107, -107, -107, + -107, 218, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107 }, @@ -2256,8 +2417,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, - -108, -108, -108, -108, -108, -108, -108, 218, -108, -108, - -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, + -108, -108, -108, -108, -108, -108, -108, -108, -108, 219, + -108, -108, -108, -108, 220, 221, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108 }, @@ -2273,9 +2434,9 @@ static yyconst flex_int16_t yy_nxt[][128] = -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, + -109, -109, -109, -109, -109, -109, -109, 222, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, - -109, 219, -109, -109, 220, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109 }, @@ -2293,7 +2454,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, - -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, + -110, 223, -110, -110, 224, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110 }, @@ -2310,7 +2471,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, - -111, 221, -111, -111, -111, -111, -111, -111, -111, -111, + -111, -111, -111, -111, -111, -111, -111, 225, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111 }, @@ -2327,7 +2488,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, - -112, -112, -112, -112, 222, -112, -112, -112, -112, -112, + -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112 }, @@ -2345,7 +2506,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, - -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, + -113, 226, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113 }, @@ -2353,16 +2514,16 @@ static yyconst flex_int16_t yy_nxt[][128] = 13, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, - -114, -114, 223, -114, -114, -114, -114, -114, -114, -114, - 224, -114, 225, 226, -114, 226, 227, 228, -114, 229, - 229, 229, 229, 229, 229, 229, 229, 229, -114, -114, + -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, + -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, + -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, - -114, -114, -114, 230, 231, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, + -114, -114, -114, -114, 227, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114 }, @@ -2370,10 +2531,10 @@ static yyconst flex_int16_t yy_nxt[][128] = 13, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, - -115, -115, 232, -115, -115, -115, -115, -115, -115, -115, - -115, -115, -115, 233, -115, 233, 234, -115, 235, 236, + -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, + -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, - 236, 236, 236, 236, 236, 236, 236, 236, -115, -115, + -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, @@ -2388,13 +2549,13 @@ static yyconst flex_int16_t yy_nxt[][128] = -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, - -116, -116, 237, -116, -116, -116, -116, -116, -116, -116, - -116, -116, 231, -116, -116, -116, -116, -116, -116, -116, - -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, - -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, + -116, -116, 228, -116, -116, -116, -116, -116, -116, -116, + 229, -116, 230, 231, -116, 231, 232, 233, -116, 234, + 234, 234, 234, 234, 234, 234, 234, 234, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, + -116, -116, -116, 235, 236, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, @@ -2405,9 +2566,9 @@ static yyconst flex_int16_t yy_nxt[][128] = 13, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, - -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, - -117, -117, -117, -117, -117, -117, -117, -117, -117, 229, - 229, 229, 229, 229, 229, 229, 229, 229, -117, -117, + -117, -117, 237, -117, -117, -117, -117, -117, -117, -117, + -117, -117, -117, 238, -117, 238, 239, -117, 240, 241, + 241, 241, 241, 241, 241, 241, 241, 241, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, @@ -2422,8 +2583,8 @@ static yyconst flex_int16_t yy_nxt[][128] = 13, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, - -118, -118, 237, -118, -118, -118, -118, -118, -118, -118, - -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, + -118, -118, 242, -118, -118, -118, -118, -118, -118, -118, + -118, -118, 236, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, -118, @@ -2440,9 +2601,9 @@ static yyconst flex_int16_t yy_nxt[][128] = -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, - -119, -119, 238, -119, -119, -119, -119, -119, -119, -119, - -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, + -119, -119, -119, -119, -119, -119, -119, -119, -119, 234, + 234, 234, 234, 234, 234, 234, 234, 234, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, @@ -2457,9 +2618,9 @@ static yyconst flex_int16_t yy_nxt[][128] = 13, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, + -120, -120, 242, -120, -120, -120, -120, -120, -120, -120, + -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, - -120, -120, -120, -120, -120, -120, -120, -120, 239, 239, - 239, 239, 239, 239, 239, 239, 239, 239, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, @@ -2474,7 +2635,7 @@ static yyconst flex_int16_t yy_nxt[][128] = 13, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, - -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, + -121, -121, 243, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, @@ -2493,8 +2654,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, - -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, - -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, + -122, -122, -122, -122, -122, -122, -122, -122, 244, 244, + 244, 244, 244, 244, 244, 244, 244, 244, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, @@ -2505,28 +2666,28 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 13, 240, 240, 240, 240, 240, 240, 240, 240, 240, + 13, -123, -123, -123, -123, -123, -123, -123, -123, -123, - -123, 240, 240, 240, 240, 240, 240, 240, 240, 240, - 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, - 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, - 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, - 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, - 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, - 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, - 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, - 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, - 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, + -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, + -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, + -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, + -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, + -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, + -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, + -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, + -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, + -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, + -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, - 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, - 240, 240, 240, 240, 240, 240, 240, 240 + -123, -123, -123, -123, -123, -123, -123, -123, -123, -123, + -123, -123, -123, -123, -123, -123, -123, -123 }, { 13, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, - -124, -124, 124, -124, -124, -124, -124, -124, -124, -124, + -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, @@ -2540,20 +2701,20 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 13, -125, -125, -125, -125, -125, -125, -125, -125, -125, - -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, - -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, - -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, - -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, + 13, 245, 245, 245, 245, 245, 245, 245, 245, 245, + -125, 245, 245, 245, 245, 245, 245, 245, 245, 245, + 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, + 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, + 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, - -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, - -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, - -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, - -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, - -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, - -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, - -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, - -125, -125, -125, -125, -125, -125, -125, -125 + 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, + 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, + 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, + 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, + 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, + 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, + 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, + 245, 245, 245, 245, 245, 245, 245, 245 }, { @@ -2561,6 +2722,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, + -126, -126, 126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, @@ -2568,7 +2730,6 @@ static yyconst flex_int16_t yy_nxt[][128] = -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, - -126, -126, -126, 241, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126 @@ -2587,7 +2748,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, - -127, -127, -127, -127, -127, -127, 242, -127, -127, -127, + -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127 }, @@ -2603,7 +2764,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, - -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, + -128, -128, -128, 246, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128 }, @@ -2621,8 +2782,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, - -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, - -129, 243, -129, -129, -129, -129, -129, -129 + -129, -129, -129, -129, -129, -129, 247, -129, -129, -129, + -129, -129, -129, -129, -129, -129, -129, -129 }, @@ -2657,7 +2818,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, - -131, -131, -131, -131, -131, -131, -131, -131 + -131, 248, -131, -131, -131, -131, -131, -131 }, { @@ -2674,7 +2835,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, - -132, -132, 130, -132, -132, -132, -132, -132 + -132, -132, -132, -132, -132, -132, -132, -132 }, { @@ -2692,7 +2853,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, - -133, 131, -133, -133, -133, -133, -133, -133 + -133, -133, -133, -133, -133, -133, -133, -133 }, { @@ -2707,9 +2868,9 @@ static yyconst flex_int16_t yy_nxt[][128] = -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, - -134, -134, -134, -134, 244, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, - -134, -134, -134, -134, -134, -134, -134, -134 + -134, -134, -134, -134, -134, -134, -134, -134, -134, -134, + -134, -134, 132, -134, -134, -134, -134, -134 }, { @@ -2723,10 +2884,10 @@ static yyconst flex_int16_t yy_nxt[][128] = -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, - -135, -135, -135, -135, -135, -135, -135, 130, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, - -135, -135, -135, -135, -135, -135, -135, -135 + -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, + -135, 133, -135, -135, -135, -135, -135, -135 }, { @@ -2740,8 +2901,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, - -136, -136, -136, -136, -136, -136, -136, -136, 130, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, + -136, -136, -136, -136, 249, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136 @@ -2758,7 +2919,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, - -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, + -137, -137, -137, -137, -137, -137, -137, 132, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137 @@ -2775,10 +2936,10 @@ static yyconst flex_int16_t yy_nxt[][128] = -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, - -138, -138, -138, -138, -138, -138, -138, 245, -138, -138, - -138, -138, -138, -138, -138, 246, -138, -138, -138, -138, + -138, -138, -138, -138, -138, -138, -138, -138, 132, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, - -138, 243, -138, -138, -138, -138, -138, -138 + -138, -138, -138, -138, -138, -138, -138, -138, -138, -138, + -138, -138, -138, -138, -138, -138, -138, -138 }, { @@ -2793,7 +2954,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, - 128, -139, -139, -139, -139, -139, -139, -139, -139, -139, + -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139 @@ -2808,12 +2969,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, - -140, -140, -140, -140, -140, -140, 131, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, + -140, -140, -140, -140, -140, -140, -140, 250, -140, -140, + -140, -140, -140, -140, -140, 251, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, - -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, - -140, -140, -140, -140, -140, -140, -140, -140 + -140, 248, -140, -140, -140, -140, -140, -140 }, { @@ -2828,9 +2989,9 @@ static yyconst flex_int16_t yy_nxt[][128] = -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, - -141, -141, -141, -141, -141, -141, -141, -141, -141, 130, + 130, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, -141, - 130, -141, -141, -141, -141, -141, -141, -141 + -141, -141, -141, -141, -141, -141, -141, -141 }, { @@ -2843,10 +3004,10 @@ static yyconst flex_int16_t yy_nxt[][128] = -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, + -142, -142, -142, -142, -142, -142, 133, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, - -142, 247, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142, -142 }, @@ -2861,11 +3022,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, - -143, -143, -143, -143, -143, -143, -143, -143, -143, 137, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, + -143, -143, -143, -143, -143, -143, -143, -143, -143, 132, -143, -143, -143, -143, -143, -143, -143, -143, -143, -143, - -143, -143, -143, -143, -143, -143, -143, -143 + 132, -143, -143, -143, -143, -143, -143, -143 }, { @@ -2879,9 +3040,9 @@ static yyconst flex_int16_t yy_nxt[][128] = -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, - -144, -144, -144, -144, -144, -144, -144, 248, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, + -144, 252, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144, -144 }, @@ -2896,9 +3057,9 @@ static yyconst flex_int16_t yy_nxt[][128] = -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, + -145, -145, -145, -145, -145, -145, -145, -145, -145, 139, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, - -145, -145, -145, -145, 128, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145 }, @@ -2913,9 +3074,9 @@ static yyconst flex_int16_t yy_nxt[][128] = -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, + -146, -146, -146, -146, -146, -146, -146, 253, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, - -146, -146, -146, -146, 137, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146 }, @@ -2933,7 +3094,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, - -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, + -147, -147, -147, -147, 130, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147 }, @@ -2950,7 +3111,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, - -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, + -148, -148, -148, -148, 139, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148 }, @@ -2966,7 +3127,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, - -149, -149, -149, -149, -149, -149, -149, -149, -149, 147, + -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149, -149 @@ -3001,7 +3162,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, - -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, + -151, -151, -151, -151, -151, -151, -151, -151, -151, 149, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151 }, @@ -3019,7 +3180,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, - 125, -152, -152, -152, -152, -152, -152, -152, -152, -152, + -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152 }, @@ -3034,7 +3195,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, - -153, -153, -153, -153, -153, -153, -153, 245, -153, -153, + -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, @@ -3054,7 +3215,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, - -154, -154, -154, -154, -154, -154, -154, 163, -154, -154, + 127, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154 }, @@ -3069,9 +3230,9 @@ static yyconst flex_int16_t yy_nxt[][128] = -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, - -155, -155, -155, -155, -155, -155, -155, 249, -155, -155, + -155, -155, -155, -155, -155, -155, -155, 250, -155, -155, + -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, - -155, 247, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155 }, @@ -3086,9 +3247,9 @@ static yyconst flex_int16_t yy_nxt[][128] = -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, - -156, -156, -156, -156, -156, -156, -156, 248, -156, 250, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, + -156, -156, -156, -156, -156, -156, -156, 165, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156 }, @@ -3104,9 +3265,9 @@ static yyconst flex_int16_t yy_nxt[][128] = -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, + -157, -157, -157, -157, -157, -157, -157, 254, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, - -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, - -157, -157, -157, -157, 251, -157, -157, -157, -157, -157, + -157, 252, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157, -157 }, @@ -3121,7 +3282,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, - -158, -158, -158, -158, -158, -158, -158, 252, -158, -158, + -158, -158, -158, -158, -158, -158, -158, 253, -158, 255, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158 @@ -3140,7 +3301,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, - 163, -159, -159, -159, -159, -159, 253, -159, -159, -159, + -159, -159, -159, -159, 256, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159 }, @@ -3155,7 +3316,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, - -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, + -160, -160, -160, -160, -160, -160, -160, 257, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, @@ -3173,9 +3334,9 @@ static yyconst flex_int16_t yy_nxt[][128] = -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, - -161, -161, -161, -161, -161, -161, -161, 254, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, + 165, -161, -161, -161, -161, -161, 258, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161, -161 }, @@ -3191,8 +3352,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, - -162, -162, -162, -162, 244, -162, -162, -162, -162, -162, - -162, -162, -162, -162, -162, -162, -162, 255, -162, -162, + -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, + -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162, -162 }, @@ -3207,7 +3368,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, - -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, + -163, -163, -163, -163, -163, -163, -163, 259, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, @@ -3221,14 +3382,14 @@ static yyconst flex_int16_t yy_nxt[][128] = -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, - -164, -164, -164, -164, -164, 128, 129, 130, -164, -164, - 130, 131, 132, -164, 133, 128, -164, -164, 130, 134, + -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, + -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, - 135, -164, 131, 130, 130, -164, 130, 136, -164, -164, - -164, -164, -164, -164, -164, -164, -164, 137, 138, 139, - -164, 140, -164, 128, -164, -164, -164, -164, 141, 142, - -164, 134, 143, -164, 144, 145, -164, -164, -164, -164, - -164, 146, -164, -164, -164, -164, -164, -164 + -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, + -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, + -164, -164, -164, -164, 249, -164, -164, -164, -164, -164, + -164, -164, -164, -164, -164, -164, -164, 260, -164, -164, + -164, -164, -164, -164, -164, -164, -164, -164 }, { @@ -3241,11 +3402,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, - -165, -165, -165, -165, -165, -165, 131, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, - -165, -165, -165, 151, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, - -165, -165, -165, -165, -165, -165, -165, -165 + -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, + -165, -165, -165, -165, -165, -165, -165, -165, -165, -165, + -165, -165, -165, -165, -165, -165, -165, -165 }, { @@ -3256,14 +3417,14 @@ static yyconst flex_int16_t yy_nxt[][128] = -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, - -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, - -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, - -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, - -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, - -166, -166, -166, 151, -166, -166, -166, -166, -166, -166, - -166, -166, -166, -166, -166, -166, -166, -166, -166, -166, + -166, -166, -166, -166, -166, 130, 131, 132, -166, -166, + 132, 133, 134, -166, 135, 130, -166, -166, 132, 136, + 137, -166, 133, 132, 132, -166, 132, 138, -166, -166, + -166, -166, -166, -166, -166, -166, -166, 139, 140, 141, + -166, 142, -166, 130, -166, -166, -166, -166, 143, 144, + -166, 136, 145, -166, 146, 147, -166, -166, -166, -166, - -166, -166, -166, -166, -166, -166, -166, -166 + -166, 148, -166, -166, -166, -166, -166, -166 }, { @@ -3275,11 +3436,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, - -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, + -167, -167, -167, -167, -167, -167, 133, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, + -167, -167, -167, 153, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, - -167, -167, -167, -167, 125, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167, -167 }, @@ -3295,8 +3456,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, - -168, -168, -168, 148, -168, -168, -168, -168, -168, -168, - -168, -168, -168, -168, -168, 151, -168, -168, -168, -168, + -168, -168, -168, 153, -168, -168, -168, -168, -168, -168, + -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168, -168 }, @@ -3313,7 +3474,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, - 151, -169, -169, -169, -169, -169, -169, -169, -169, -169, + -169, -169, -169, -169, 127, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169 }, @@ -3330,8 +3491,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, - -170, -170, -170, -170, 244, -170, -170, -170, 160, -170, - -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, + -170, -170, -170, 150, -170, -170, -170, -170, -170, -170, + -170, -170, -170, -170, -170, 153, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170 }, @@ -3347,8 +3508,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, - 128, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, + 153, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171, -171 }, @@ -3364,8 +3525,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, + -172, -172, -172, -172, 249, -172, -172, -172, 162, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, - -172, 256, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172 }, @@ -3381,10 +3542,10 @@ static yyconst flex_int16_t yy_nxt[][128] = -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, - -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, + 130, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, -173, - 257, -173, -173, -173, -173, -173, -173, -173 + -173, -173, -173, -173, -173, -173, -173, -173 }, { @@ -3399,8 +3560,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, - 160, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, + -174, 261, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174, -174 }, @@ -3416,9 +3577,9 @@ static yyconst flex_int16_t yy_nxt[][128] = -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, - -175, -175, -175, -175, -175, -175, -175, -175, 258, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, - -175, -175, -175, -175, -175, -175, -175, -175 + -175, -175, -175, -175, -175, -175, -175, -175, -175, -175, + 262, -175, -175, -175, -175, -175, -175, -175 }, { @@ -3433,8 +3594,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, + 162, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, - -176, -176, -176, -176, 259, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176 }, @@ -3451,9 +3612,9 @@ static yyconst flex_int16_t yy_nxt[][128] = -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, + -177, -177, -177, -177, -177, -177, -177, -177, 263, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, - -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, - 260, -177, -177, -177, -177, -177, -177, -177 + -177, -177, -177, -177, -177, -177, -177, -177 }, { @@ -3467,9 +3628,9 @@ static yyconst flex_int16_t yy_nxt[][128] = -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, - -178, -178, -178, -178, -178, -178, -178, 248, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, + -178, -178, -178, -178, 264, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178 }, @@ -3478,7 +3639,6 @@ static yyconst flex_int16_t yy_nxt[][128] = -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, - -179, -179, 179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, @@ -3487,6 +3647,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, + -179, -179, -179, -179, 265, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179 }, @@ -3505,7 +3666,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, - -180, -180, -180, -180, -180, -180, -180, -180 + 266, -180, -180, -180, -180, -180, -180, -180 }, { @@ -3513,13 +3674,13 @@ static yyconst flex_int16_t yy_nxt[][128] = -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, - -181, -181, -181, -181, -181, -181, -181, -181, 261, 261, - 261, 261, 261, 261, 261, 261, 261, 261, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, - -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, + -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, + -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, + -181, -181, -181, -181, -181, -181, -181, 253, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181, -181 @@ -3529,7 +3690,7 @@ static yyconst flex_int16_t yy_nxt[][128] = 13, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, - -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, + -182, -182, 182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, @@ -3538,7 +3699,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, - -182, -182, 262, -182, -182, -182, -182, -182, -182, -182, + -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182 }, @@ -3547,8 +3708,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, - -183, -183, 263, -183, -183, -183, -183, -183, -183, -183, - 264, -183, -183, -183, -183, -183, -183, -183, -183, -183, + -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, + -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, -183, @@ -3565,33 +3726,33 @@ static yyconst flex_int16_t yy_nxt[][128] = -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, - -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, - -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, + -184, -184, -184, -184, -184, -184, -184, -184, 267, 267, + 267, 267, 267, 267, 267, 267, 267, 267, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, - -184, -184, -184, 265, -184, -184, -184, -184, -184, -184, + -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184, -184 }, { - 13, 185, 185, 185, 185, 185, 185, 185, 185, 185, - 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, - 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, - 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, - -185, -185, 185, 185, 185, 185, 185, 185, 185, 185, + 13, -185, -185, -185, -185, -185, -185, -185, -185, -185, + -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, + -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, + -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, + -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, - 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, - 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, - 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, - 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, - 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, - 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, - 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, - 185, 185, 185, 185, 185, 185, 185, 185 + -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, + -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, + -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, + -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, + -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, + -185, -185, -185, -185, -185, -185, -185, -185, -185, -185, + -185, -185, 268, -185, -185, -185, -185, -185, -185, -185, + -185, -185, -185, -185, -185, -185, -185, -185 }, { @@ -3599,8 +3760,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, - -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, - -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, + -186, -186, 269, -186, -186, -186, -186, -186, -186, -186, + 270, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, @@ -3624,26 +3785,26 @@ static yyconst flex_int16_t yy_nxt[][128] = -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, - -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, + -187, -187, -187, 271, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187, -187 }, { - 13, -188, -188, -188, -188, -188, -188, -188, -188, -188, - -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, - -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, - -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, - -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, - -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, + 13, 188, 188, 188, 188, 188, 188, 188, 188, 188, + 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, + 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, + 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, + -188, -188, 188, 188, 188, 188, 188, 188, 188, 188, + 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, - -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, - -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, - -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, - -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, - -188, -188, -188, 266, -188, -188, -188, -188, -188, -188, - -188, -188, -188, -188, -188, -188, -188, -188, -188, -188, - -188, -188, -188, -188, -188, -188, -188, -188 + 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, + 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, + 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, + 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, + 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, + 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, + 188, 188, 188, 188, 188, 188, 188, 188 }, { @@ -3659,7 +3820,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, - -189, -189, -189, -189, -189, -189, 267, -189, -189, -189, + -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189 }, @@ -3693,7 +3854,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, - -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, + -191, -191, -191, 272, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191, -191 }, @@ -3710,8 +3871,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, - -192, -192, -192, -192, -192, -192, -192, -192, -192, 268, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, + -192, -192, -192, -192, -192, -192, 273, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192, -192 }, @@ -3762,8 +3923,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, + -195, -195, -195, -195, -195, -195, -195, -195, -195, 274, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, - 269, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195, -195 }, @@ -3798,7 +3959,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, - -197, -197, -197, -197, -197, -197, -197, 270, -197, -197, + -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197, -197 }, @@ -3813,9 +3974,9 @@ static yyconst flex_int16_t yy_nxt[][128] = -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, - -198, -198, -198, -198, -198, -198, -198, -198, -198, 271, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, + 275, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198, -198 }, @@ -3832,7 +3993,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, - -199, -199, -199, -199, 272, -199, -199, -199, -199, -199, + -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199, -199 }, @@ -3847,10 +4008,10 @@ static yyconst flex_int16_t yy_nxt[][128] = -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, - -200, -200, -200, -200, -200, -200, -200, 273, -200, -200, - -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, + -200, -200, -200, -200, -200, -200, -200, -200, -200, -200, + -200, -200, -200, -200, -200, -200, -200, 276, -200, -200, -200, -200, -200, -200, -200, -200, -200, -200 }, @@ -3865,9 +4026,9 @@ static yyconst flex_int16_t yy_nxt[][128] = -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, + -201, -201, -201, -201, -201, -201, -201, -201, -201, 277, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, - 274, -201, -201, -201, -201, -201, 275, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201, -201 }, @@ -3884,7 +4045,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, - -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, + -202, -202, -202, -202, 278, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202, -202 }, @@ -3899,7 +4060,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, - -203, -203, -203, -203, -203, -203, -203, 276, -203, -203, + -203, -203, -203, -203, -203, -203, -203, 279, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, @@ -3919,7 +4080,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, - -204, -204, -204, -204, -204, -204, -204, 277, -204, -204, + 280, -204, -204, -204, -204, -204, 281, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, -204 }, @@ -3951,8 +4112,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, + -206, -206, -206, -206, -206, -206, -206, 282, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, - -206, -206, -206, 278, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, -206 @@ -3971,7 +4132,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, - -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, + -207, -207, -207, -207, -207, -207, -207, 283, -207, -207, -207, -207, -207, -207, -207, -207, -207, -207 }, @@ -3987,7 +4148,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, - -208, -208, -208, 279, -208, -208, -208, -208, -208, -208, + -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, -208 }, @@ -4022,7 +4183,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, - -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, + -210, -210, -210, 284, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, -210 }, @@ -4040,7 +4201,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, - -211, -211, -211, -211, 280, -211, -211, -211, -211, -211, + -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, -211 }, @@ -4056,8 +4217,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, - -212, -212, -212, 281, -212, -212, -212, -212, -212, -212, - -212, -212, -212, -212, -212, 282, -212, -212, -212, -212, + -212, -212, -212, 285, -212, -212, -212, -212, -212, -212, + -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, -212 }, @@ -4075,7 +4236,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, - 283, -213, -213, -213, -213, -213, -213, -213, -213, -213, + -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, -213 }, @@ -4091,7 +4252,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, - -214, -214, -214, -214, -214, -214, -214, -214, 284, -214, + -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, -214 }, @@ -4109,7 +4270,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, - -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, + -215, -215, -215, -215, 286, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, -215 }, @@ -4125,8 +4286,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, - -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, - -216, 285, -216, -216, -216, -216, -216, -216, -216, -216, + -216, -216, -216, 287, -216, -216, -216, -216, -216, -216, + -216, -216, -216, -216, -216, 288, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, -216 }, @@ -4144,8 +4305,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, - -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, - 286, -217, -217, -217, -217, -217, -217, -217 + 289, -217, -217, -217, -217, -217, -217, -217, -217, -217, + -217, -217, -217, -217, -217, -217, -217, -217 }, { @@ -4160,7 +4321,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, - 287, -218, -218, -218, -218, -218, -218, -218, -218, -218, + -218, -218, -218, -218, -218, -218, -218, -218, 290, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218, -218 }, @@ -4177,7 +4338,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, - -219, -219, -219, -219, -219, -219, -219, -219, 288, -219, + -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219, -219 @@ -4196,7 +4357,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, - -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, + -220, 291, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220, -220 }, @@ -4214,7 +4375,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, -221, - 289, -221, -221, -221, -221, -221, -221, -221 + 292, -221, -221, -221, -221, -221, -221, -221 }, { @@ -4229,7 +4390,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, - -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, + 293, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222, -222 }, @@ -4239,14 +4400,14 @@ static yyconst flex_int16_t yy_nxt[][128] = -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, - -223, -223, 223, -223, -223, -223, -223, -223, -223, -223, - 224, -223, 225, 226, -223, 226, 227, 228, -223, 229, - 229, 229, 229, 229, 229, 229, 229, 229, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, - -223, -223, -223, 230, 231, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, + -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, + -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, + -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, + -223, -223, -223, -223, -223, -223, -223, -223, 294, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223, -223 @@ -4256,9 +4417,9 @@ static yyconst flex_int16_t yy_nxt[][128] = 13, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, - -224, -224, 232, -224, -224, -224, -224, -224, -224, -224, - -224, -224, -224, 233, -224, 233, 234, -224, 235, 236, - 236, 236, 236, 236, 236, 236, 236, 236, -224, -224, + -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, + -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, + -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, -224, @@ -4273,16 +4434,16 @@ static yyconst flex_int16_t yy_nxt[][128] = 13, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, - -225, -225, 237, -225, -225, -225, -225, -225, -225, -225, - -225, -225, 231, -225, -225, -225, -225, -225, -225, -225, - -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, + -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, + -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, + -225, -225, -225, -225, 295, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, -225 }, @@ -4292,8 +4453,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, - -226, -226, -226, -226, -226, -226, -226, -226, -226, 229, - 229, 229, 229, 229, 229, 229, 229, 229, -226, -226, + -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, + -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, @@ -4301,14 +4462,14 @@ static yyconst flex_int16_t yy_nxt[][128] = -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, -226, - -226, -226, -226, -226, -226, -226, -226, -226 + 296, -226, -226, -226, -226, -226, -226, -226 }, { 13, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, - -227, -227, 237, -227, -227, -227, -227, -227, -227, -227, + -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, -227, @@ -4325,14 +4486,14 @@ static yyconst flex_int16_t yy_nxt[][128] = 13, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, - -228, -228, 238, -228, -228, -228, -228, -228, -228, -228, - -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, - -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, + -228, -228, 228, -228, -228, -228, -228, -228, -228, -228, + 229, -228, 230, 231, -228, 231, 232, 233, -228, 234, + 234, 234, 234, 234, 234, 234, 234, 234, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, - -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, + -228, -228, -228, 235, 236, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228, -228 @@ -4343,9 +4504,9 @@ static yyconst flex_int16_t yy_nxt[][128] = -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, - -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, - -229, -229, -229, -229, -229, -229, -229, -229, 239, 239, - 239, 239, 239, 239, 239, 239, 239, 239, -229, -229, + -229, -229, 237, -229, -229, -229, -229, -229, -229, -229, + -229, -229, -229, 238, -229, 238, 239, -229, 240, 241, + 241, 241, 241, 241, 241, 241, 241, 241, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, -229, @@ -4360,8 +4521,8 @@ static yyconst flex_int16_t yy_nxt[][128] = 13, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, - -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, - -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, + -230, -230, 242, -230, -230, -230, -230, -230, -230, -230, + -230, -230, 236, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, -230, @@ -4378,8 +4539,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, - -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, - -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, + -231, -231, -231, -231, -231, -231, -231, -231, -231, 234, + 234, 234, 234, 234, 234, 234, 234, 234, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, -231, @@ -4394,10 +4555,10 @@ static yyconst flex_int16_t yy_nxt[][128] = 13, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, - -232, -232, 232, -232, -232, -232, -232, -232, -232, -232, + -232, -232, 242, -232, -232, -232, -232, -232, -232, -232, - -232, -232, -232, 233, -232, 233, 234, -232, 235, 236, - 236, 236, 236, 236, 236, 236, 236, 236, -232, -232, + -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, + -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, -232, @@ -4412,9 +4573,9 @@ static yyconst flex_int16_t yy_nxt[][128] = -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, + -233, -233, 243, -233, -233, -233, -233, -233, -233, -233, + -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, - -233, -233, -233, -233, -233, -233, 234, -233, 235, 236, - 236, 236, 236, 236, 236, 236, 236, 236, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, -233, @@ -4430,8 +4591,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, - -234, -234, -234, -234, -234, -234, -234, -234, 290, 290, - 290, 290, 290, 290, 290, 290, 290, 290, -234, -234, + -234, -234, -234, -234, -234, -234, -234, -234, 244, 244, + 244, 244, 244, 244, 244, 244, 244, 244, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, @@ -4446,10 +4607,10 @@ static yyconst flex_int16_t yy_nxt[][128] = 13, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, - -235, -235, 291, -235, -235, -235, -235, -235, -235, -235, - -235, 292, -235, -235, -235, -235, 293, -235, 294, 294, + -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, + -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, - 294, 294, 294, 294, 294, 294, 294, 294, -235, -235, + -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, -235, @@ -4464,9 +4625,9 @@ static yyconst flex_int16_t yy_nxt[][128] = -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, - -236, -236, 295, -236, -236, -236, -236, -236, -236, -236, - -236, 296, -236, -236, -236, -236, 293, 297, 298, 298, - 298, 298, 298, 298, 298, 298, 298, 298, -236, -236, + -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, + -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, + -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, -236, @@ -4482,8 +4643,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, 237, -237, -237, -237, -237, -237, -237, -237, - -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, - -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, + -237, -237, -237, 238, -237, 238, 239, -237, 240, 241, + 241, 241, 241, 241, 241, 241, 241, 241, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, -237, @@ -4498,9 +4659,9 @@ static yyconst flex_int16_t yy_nxt[][128] = 13, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, - -238, -238, 238, -238, -238, -238, -238, -238, -238, -238, - -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, + -238, -238, -238, -238, -238, -238, 239, -238, 240, 241, + 241, 241, 241, 241, 241, 241, 241, 241, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, @@ -4517,8 +4678,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, - -239, -239, -239, -239, -239, -239, -239, -239, 239, 239, - 239, 239, 239, 239, 239, 239, 239, 239, -239, -239, + -239, -239, -239, -239, -239, -239, -239, -239, 297, 297, + 297, 297, 297, 297, 297, 297, 297, 297, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, -239, @@ -4530,36 +4691,36 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 13, 240, 240, 240, 240, 240, 240, 240, 240, 240, - -240, 240, 240, 240, 240, 240, 240, 240, 240, 240, - 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, - 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, - 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, - 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, - 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, - 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, - 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, - 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, + 13, -240, -240, -240, -240, -240, -240, -240, -240, -240, + -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, + -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, + -240, -240, 298, -240, -240, -240, -240, -240, -240, -240, + -240, 299, -240, -240, -240, -240, 300, -240, 301, 301, + 301, 301, 301, 301, 301, 301, 301, 301, -240, -240, + -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, + -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, + -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, + -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, - 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, - 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, - 240, 240, 240, 240, 240, 240, 240, 240 + -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, + -240, -240, -240, -240, -240, -240, -240, -240, -240, -240, + -240, -240, -240, -240, -240, -240, -240, -240 }, { 13, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, - -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, - -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, - -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, + -241, -241, 302, -241, -241, -241, -241, -241, -241, -241, + -241, 303, -241, -241, -241, -241, 300, 304, 305, 305, + 305, 305, 305, 305, 305, 305, 305, 305, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, - -241, -241, -241, -241, -241, 299, -241, -241, -241, -241, + -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241 }, @@ -4567,7 +4728,7 @@ static yyconst flex_int16_t yy_nxt[][128] = 13, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, - -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, + -242, -242, 242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, @@ -4575,7 +4736,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, - -242, 253, -242, -242, -242, -242, -242, -242, -242, -242, + -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242, -242 }, @@ -4585,7 +4746,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, - -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, + -243, -243, 243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, @@ -4594,7 +4755,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, - -243, -243, -243, -243, -243, -243, 300, -243, -243, -243, + -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243, -243 }, @@ -4603,33 +4764,33 @@ static yyconst flex_int16_t yy_nxt[][128] = -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, - -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, - -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, + -244, -244, -244, -244, -244, -244, -244, -244, 244, 244, + 244, 244, 244, 244, 244, 244, 244, 244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, - -244, -244, -244, -244, -244, -244, -244, -244, -244, 130, + -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244, -244 }, { - 13, -245, -245, -245, -245, -245, -245, -245, -245, -245, - -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, - -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, - -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, - -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, + 13, 245, 245, 245, 245, 245, 245, 245, 245, 245, + -245, 245, 245, 245, 245, 245, 245, 245, 245, 245, + 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, + 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, + 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, - -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, - -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, - -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, - -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, - -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, - -245, -245, -245, -245, -245, -245, -245, -245, -245, -245, - -245, -245, -245, -245, 301, -245, -245, -245, -245, -245, - -245, -245, -245, -245, -245, -245, -245, -245 + 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, + 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, + 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, + 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, + 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, + 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, + 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, + 245, 245, 245, 245, 245, 245, 245, 245 }, { @@ -4645,7 +4806,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, - -246, -246, -246, -246, -246, -246, 137, -246, -246, -246, + -246, -246, -246, -246, -246, 306, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246, -246 }, @@ -4662,7 +4823,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, - -247, -247, -247, -247, -247, -247, -247, -247, 128, -247, + -247, 258, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247, -247 }, @@ -4679,8 +4840,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, - 128, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, + -248, -248, -248, -248, -248, -248, 307, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248, -248 }, @@ -4696,7 +4857,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, - -249, -249, -249, 302, -249, -249, -249, -249, -249, -249, + -249, -249, -249, -249, -249, -249, -249, -249, -249, 132, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249, -249 @@ -4714,8 +4875,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, - -250, -250, -250, -250, -250, -250, -250, -250, -250, 303, - -250, -250, -250, -250, -250, 304, -250, -250, -250, -250, + -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, + -250, -250, -250, -250, 308, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250, -250 }, @@ -4732,7 +4893,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, - 148, -251, -251, -251, -251, -251, -251, -251, -251, -251, + -251, -251, -251, -251, -251, -251, 139, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251, -251 }, @@ -4748,7 +4909,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, - -252, -252, -252, -252, -252, -252, -252, -252, -252, 125, + -252, -252, -252, -252, -252, -252, -252, -252, 130, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252, -252 }, @@ -4765,7 +4926,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, - -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, + 130, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253, -253 @@ -4783,8 +4944,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, + -254, -254, -254, 309, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, - 163, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254, -254 }, @@ -4800,8 +4961,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, - -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, - 305, -255, -255, -255, -255, -255, -255, -255, -255, -255, + -255, -255, -255, -255, -255, -255, -255, -255, -255, 310, + -255, -255, -255, -255, -255, 311, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255, -255 }, @@ -4818,7 +4979,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, - -256, -256, -256, -256, -256, -256, 306, -256, -256, -256, + 150, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, -256 }, @@ -4835,7 +4996,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, - -257, 307, -257, -257, -257, -257, -257, -257, -257, -257, + -257, -257, -257, -257, -257, -257, -257, -257, -257, 127, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257, -257 }, @@ -4849,8 +5010,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, - -258, -258, -258, -258, -258, -258, 308, 309, -258, -258, - -258, -258, 310, -258, -258, -258, -258, -258, -258, -258, + -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, + -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, -258, @@ -4870,7 +5031,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, - -259, -259, -259, -259, -259, -259, 311, -259, -259, -259, + 165, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259, -259 }, @@ -4887,8 +5048,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, - -260, 312, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, + 312, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260, -260 }, @@ -4905,7 +5066,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, - -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, + -261, -261, -261, -261, -261, -261, 313, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, -261 }, @@ -4913,15 +5074,15 @@ static yyconst flex_int16_t yy_nxt[][128] = 13, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, - -262, -262, 313, -262, -262, -262, -262, -262, -262, -262, + -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, - 314, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, + -262, 314, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262, -262 }, @@ -4931,12 +5092,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, - -263, -263, 263, -263, -263, -263, -263, -263, -263, -263, - 264, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, + -263, -263, -263, -263, -263, -263, 315, 316, -263, -263, + -263, -263, 317, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, -263, @@ -4957,7 +5118,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, - -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, + -264, -264, -264, -264, -264, -264, 318, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264, -264 }, @@ -4965,16 +5126,16 @@ static yyconst flex_int16_t yy_nxt[][128] = 13, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, - -265, -265, 315, -265, -265, -265, -265, -265, -265, -265, - 316, -265, -265, -265, -265, -265, -265, -265, -265, -265, - -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, + -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, + -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, + 153, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265, -265 }, @@ -4990,8 +5151,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, + -266, 319, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, - -266, -266, -266, -266, -266, 317, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266, -266 }, @@ -5008,7 +5169,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, - -267, 318, -267, -267, -267, -267, -267, -267, -267, -267, + -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267, -267 }, @@ -5017,8 +5178,8 @@ static yyconst flex_int16_t yy_nxt[][128] = 13, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, - -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, - -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, + -268, -268, 320, -268, -268, -268, -268, -268, -268, -268, + 321, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, -268, @@ -5035,8 +5196,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, - -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, - -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, + -269, -269, 269, -269, -269, -269, -269, -269, -269, -269, + 270, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, -269, @@ -5069,16 +5230,16 @@ static yyconst flex_int16_t yy_nxt[][128] = 13, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, + -271, -271, 322, -271, -271, -271, -271, -271, -271, -271, + 323, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, + -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, - -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, - -271, -271, -271, -271, -271, -271, -271, -271, -271, 319, - -271, -271, -271, -271, -271, 320, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, -271 }, @@ -5095,7 +5256,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, - 321, -272, -272, -272, -272, -272, -272, -272, -272, -272, + -272, -272, -272, -272, -272, 324, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272, -272 }, @@ -5111,7 +5272,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, - -273, -273, -273, -273, -273, -273, -273, -273, -273, 322, + -273, 325, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273, -273 @@ -5164,7 +5325,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, - 323, -276, -276, -276, -276, -276, -276, -276, -276, -276, + -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276, -276 }, @@ -5181,8 +5342,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, - -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, - 324, -277, -277, -277, -277, -277, -277, -277, -277, -277, + -277, -277, -277, -277, -277, -277, -277, -277, -277, 326, + -277, -277, -277, -277, -277, 327, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277, -277 }, @@ -5199,7 +5360,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, - -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, + 328, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278, -278 }, @@ -5215,7 +5376,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, - -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, + -279, -279, -279, -279, -279, -279, -279, -279, -279, 329, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279, -279 @@ -5268,7 +5429,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, - -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, + 330, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282, -282 }, @@ -5286,7 +5447,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, - -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, + 331, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283, -283 }, @@ -5320,7 +5481,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, - -285, -285, -285, -285, -285, -285, 325, -285, -285, -285, + -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285, -285 }, @@ -5336,7 +5497,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, - -286, 326, -286, -286, -286, -286, -286, -286, -286, -286, + -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286, -286 @@ -5368,8 +5529,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, - -288, -288, -288, -288, -288, -288, 327, 328, -288, -288, - -288, -288, 329, -288, -288, -288, -288, -288, -288, -288, + -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, + -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, @@ -5388,7 +5549,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, - -289, 330, -289, -289, -289, -289, -289, -289, -289, -289, + -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289, -289 @@ -5398,9 +5559,9 @@ static yyconst flex_int16_t yy_nxt[][128] = 13, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, - -290, -290, 291, -290, -290, -290, -290, -290, -290, -290, - -290, 292, -290, -290, -290, -290, -290, -290, 290, 290, - 290, 290, 290, 290, 290, 290, 290, 290, -290, -290, + -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, + -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, + -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, -290, @@ -5415,16 +5576,16 @@ static yyconst flex_int16_t yy_nxt[][128] = 13, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, - -291, -291, 291, -291, -291, -291, -291, -291, -291, -291, - -291, 292, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, - -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, + -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, + -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, + -291, -291, -291, -291, -291, -291, 332, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291, -291 }, @@ -5440,7 +5601,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, - -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, + -292, 333, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292, -292 }, @@ -5450,9 +5611,9 @@ static yyconst flex_int16_t yy_nxt[][128] = -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, - -293, -293, 291, -293, -293, -293, -293, -293, -293, -293, - -293, 292, -293, -293, -293, -293, -293, -293, 331, 331, - 331, 331, 331, 331, 331, 331, 331, 331, -293, -293, + -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, + -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, + -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, -293, @@ -5467,14 +5628,14 @@ static yyconst flex_int16_t yy_nxt[][128] = 13, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, - -294, -294, 291, -294, -294, -294, -294, -294, -294, -294, - -294, 292, -294, -294, -294, -294, 293, -294, 294, 294, - 294, 294, 294, 294, 294, 294, 294, 294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, - -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, + -294, -294, -294, -294, -294, -294, 334, 335, -294, -294, + + -294, -294, 336, -294, -294, -294, -294, -294, -294, -294, + -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294, -294 @@ -5484,16 +5645,16 @@ static yyconst flex_int16_t yy_nxt[][128] = 13, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, - -295, -295, 295, -295, -295, -295, -295, -295, -295, -295, - -295, 296, -295, -295, -295, -295, -295, -295, -295, -295, - -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, + -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, + -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, + 337, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295, -295 }, @@ -5509,7 +5670,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, - -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, + -296, 338, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296, -296 @@ -5519,9 +5680,9 @@ static yyconst flex_int16_t yy_nxt[][128] = 13, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, - -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, - -297, -297, -297, -297, -297, -297, -297, -297, -297, 332, - 332, 332, 332, 332, 332, 332, 332, 332, -297, -297, + -297, -297, 298, -297, -297, -297, -297, -297, -297, -297, + -297, 299, -297, -297, -297, -297, -297, -297, 297, 297, + 297, 297, 297, 297, 297, 297, 297, 297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, -297, @@ -5536,9 +5697,9 @@ static yyconst flex_int16_t yy_nxt[][128] = 13, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, - -298, -298, 295, -298, -298, -298, -298, -298, -298, -298, - -298, 296, -298, -298, -298, -298, 293, 297, 298, 298, - 298, 298, 298, 298, 298, 298, 298, 298, -298, -298, + -298, -298, 298, -298, -298, -298, -298, -298, -298, -298, + -298, 299, -298, -298, -298, -298, -298, -298, -298, -298, + -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, -298, @@ -5562,7 +5723,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, - -299, -299, -299, -299, -299, -299, 333, -299, -299, -299, + -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299, -299 }, @@ -5571,15 +5732,15 @@ static yyconst flex_int16_t yy_nxt[][128] = 13, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, - -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, - -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, - -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, + -300, -300, 298, -300, -300, -300, -300, -300, -300, -300, + -300, 299, -300, -300, -300, -300, -300, -300, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, - -300, 137, -300, -300, -300, -300, -300, -300, -300, -300, + -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300, -300 }, @@ -5588,16 +5749,16 @@ static yyconst flex_int16_t yy_nxt[][128] = 13, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, - -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, - -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, - -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, + -301, -301, 298, -301, -301, -301, -301, -301, -301, -301, + -301, 299, -301, -301, -301, -301, 300, -301, 301, 301, + 301, 301, 301, 301, 301, 301, 301, 301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, - 131, -301, -301, -301, -301, -301, -301, -301, -301, -301, + -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301 }, @@ -5605,9 +5766,9 @@ static yyconst flex_int16_t yy_nxt[][128] = 13, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, - -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, + -302, -302, 302, -302, -302, -302, -302, -302, -302, -302, - -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, + -302, 303, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, -302, @@ -5630,7 +5791,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, - -303, -303, -303, -303, -303, 334, -303, -303, -303, -303, + -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303, -303 @@ -5641,14 +5802,14 @@ static yyconst flex_int16_t yy_nxt[][128] = -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, - -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, - -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, + -304, -304, -304, -304, -304, -304, -304, -304, -304, 340, + 340, 340, 340, 340, 340, 340, 340, 340, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, - -304, 335, -304, -304, -304, -304, -304, -304, -304, -304, + -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304, -304 }, @@ -5657,16 +5818,16 @@ static yyconst flex_int16_t yy_nxt[][128] = 13, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, - -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, - -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, + -305, -305, 302, -305, -305, -305, -305, -305, -305, -305, + -305, 303, -305, -305, -305, -305, 300, 304, 305, 305, + 305, 305, 305, 305, 305, 305, 305, 305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, - -305, -305, -305, -305, -305, -305, 163, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305, -305 }, @@ -5683,7 +5844,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, - -306, 336, -306, -306, -306, -306, -306, -306, -306, -306, + -306, -306, -306, -306, -306, -306, 341, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306, -306 }, @@ -5700,7 +5861,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, - -307, -307, -307, -307, -307, -307, -307, -307, 163, -307, + -307, 139, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307, -307 }, @@ -5718,7 +5879,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, - -308, -308, -308, -308, -308, -308, -308, 337, -308, -308, + 133, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308, -308 }, @@ -5733,7 +5894,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, - -309, -309, -309, -309, -309, -309, -309, 338, -309, -309, + -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309, -309 @@ -5750,9 +5911,9 @@ static yyconst flex_int16_t yy_nxt[][128] = -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, - -310, -310, -310, -310, -310, -310, -310, 339, -310, -310, - -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, + + -310, -310, -310, -310, -310, 342, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310, -310 }, @@ -5761,15 +5922,15 @@ static yyconst flex_int16_t yy_nxt[][128] = 13, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, - -311, -311, 340, -311, -311, -311, -311, -311, -311, -311, - 341, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, - -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, + -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, + -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, + -311, 343, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311, -311 }, @@ -5786,8 +5947,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, - -312, -312, -312, -312, -312, -312, -312, -312, 163, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, + -312, -312, -312, -312, -312, -312, 165, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312, -312 }, @@ -5796,16 +5957,16 @@ static yyconst flex_int16_t yy_nxt[][128] = -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, - -313, -313, 313, -313, -313, -313, -313, -313, -313, -313, - 314, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, - -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, + -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, + + -313, 344, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313, -313 }, @@ -5821,7 +5982,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, - -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, + -314, -314, -314, -314, -314, -314, -314, -314, 165, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314, -314 }, @@ -5830,16 +5991,16 @@ static yyconst flex_int16_t yy_nxt[][128] = 13, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, - -315, -315, 315, -315, -315, -315, -315, -315, -315, -315, - 316, -315, -315, -315, -315, -315, -315, -315, -315, -315, - -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, + -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, + -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315, + -315, -315, -315, -315, -315, -315, -315, 345, -315, -315, -315, -315, -315, -315, -315, -315, -315, -315 }, @@ -5854,7 +6015,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, - -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, + -316, -316, -316, -316, -316, -316, -316, 346, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, -316, @@ -5872,9 +6033,9 @@ static yyconst flex_int16_t yy_nxt[][128] = -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, + -317, -317, -317, -317, -317, -317, -317, 347, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, - -317, -317, -317, -317, -317, -317, 342, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317, -317 }, @@ -5882,8 +6043,8 @@ static yyconst flex_int16_t yy_nxt[][128] = 13, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, - -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, - -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, + -318, -318, 348, -318, -318, -318, -318, -318, -318, -318, + 349, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, -318, @@ -5907,7 +6068,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, - -319, -319, -319, -319, -319, 343, -319, -319, -319, -319, + -319, -319, -319, -319, -319, -319, -319, -319, 165, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319, -319 @@ -5917,15 +6078,15 @@ static yyconst flex_int16_t yy_nxt[][128] = 13, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, - -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, - -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, + -320, -320, 320, -320, -320, -320, -320, -320, -320, -320, + 321, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, - -320, 344, -320, -320, -320, -320, -320, -320, -320, -320, + -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320, -320 }, @@ -5951,9 +6112,9 @@ static yyconst flex_int16_t yy_nxt[][128] = 13, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, - -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, + -322, -322, 322, -322, -322, -322, -322, -322, -322, -322, - -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, + 323, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, -322, @@ -5995,7 +6156,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, - -324, -324, -324, -324, -324, -324, 205, -324, -324, -324, + -324, -324, -324, -324, -324, -324, 350, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324, -324 }, @@ -6012,7 +6173,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, - -325, 345, -325, -325, -325, -325, -325, -325, -325, -325, + -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325, -325 }, @@ -6028,7 +6189,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, - -326, -326, -326, -326, -326, -326, -326, -326, 346, -326, + -326, -326, -326, -326, -326, 351, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326, -326 @@ -6046,8 +6207,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, + -327, 352, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327, - -327, -327, -327, -327, -327, -327, -327, 347, -327, -327, -327, -327, -327, -327, -327, -327, -327, -327 }, @@ -6062,7 +6223,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, - -328, -328, -328, -328, -328, -328, -328, 348, -328, -328, + -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328, -328 @@ -6079,7 +6240,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, - -329, -329, -329, -329, -329, -329, -329, 349, -329, -329, + -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329, -329 @@ -6098,7 +6259,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, - -330, -330, -330, -330, -330, -330, -330, -330, 350, -330, + -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330, -330 }, @@ -6107,16 +6268,16 @@ static yyconst flex_int16_t yy_nxt[][128] = 13, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, - -331, -331, 291, -331, -331, -331, -331, -331, -331, -331, - -331, 292, -331, -331, -331, -331, -331, -331, 331, 331, - 331, 331, 331, 331, 331, 331, 331, 331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, - -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, + -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, + -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, + -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, + -331, -331, -331, -331, -331, -331, 208, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, -331 }, @@ -6124,16 +6285,16 @@ static yyconst flex_int16_t yy_nxt[][128] = 13, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, - -332, -332, 351, -332, -332, -332, -332, -332, -332, -332, + -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, - -332, 352, -332, -332, -332, -332, -332, -332, 353, 353, - 353, 353, 353, 353, 353, 353, 353, 353, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, + -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, + -332, 353, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332, -332 }, @@ -6149,9 +6310,9 @@ static yyconst flex_int16_t yy_nxt[][128] = -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, - -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, + -333, -333, -333, -333, -333, -333, -333, -333, 354, -333, - -333, -333, -333, -333, 354, -333, -333, -333, -333, -333, + -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333, -333 }, @@ -6168,7 +6329,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334, - 151, -334, -334, -334, -334, -334, -334, -334, -334, -334, + -334, -334, -334, -334, -334, -334, -334, 355, -334, -334, -334, -334, -334, -334, -334, -334, -334, -334 }, @@ -6183,7 +6344,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, - -335, -335, -335, -335, -335, -335, -335, -335, -335, 151, + -335, -335, -335, -335, -335, -335, -335, 356, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335, -335 @@ -6200,9 +6361,9 @@ static yyconst flex_int16_t yy_nxt[][128] = -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, + -336, -336, -336, -336, -336, -336, -336, 357, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, - 163, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336, -336 }, @@ -6219,7 +6380,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, - -337, -337, -337, -337, -337, -337, -337, -337, -337, 125, + -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337, -337 }, @@ -6236,8 +6397,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, + -338, -338, -338, -338, -338, -338, -338, -338, 358, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, - -338, -338, -338, -338, -338, 355, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338, -338 }, @@ -6246,15 +6407,15 @@ static yyconst flex_int16_t yy_nxt[][128] = -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, + -339, -339, 298, -339, -339, -339, -339, -339, -339, -339, + -339, 299, -339, -339, -339, -339, -339, -339, 339, 339, + 339, 339, 339, 339, 339, 339, 339, 339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, - -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, - 125, -339, -339, -339, -339, -339, -339, -339, -339, -339, - -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339, -339 }, @@ -6263,9 +6424,9 @@ static yyconst flex_int16_t yy_nxt[][128] = 13, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, - -340, -340, 340, -340, -340, -340, -340, -340, -340, -340, - 341, -340, -340, -340, -340, -340, -340, -340, -340, -340, - -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, + -340, -340, 359, -340, -340, -340, -340, -340, -340, -340, + -340, 360, -340, -340, -340, -340, -340, -340, 361, 361, + 361, 361, 361, 361, 361, 361, 361, 361, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, -340, @@ -6289,7 +6450,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, - -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, + -341, -341, -341, -341, 362, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341, -341 }, @@ -6306,7 +6467,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, - -342, -342, -342, -342, 356, -342, -342, -342, -342, -342, + 153, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342, -342 }, @@ -6321,10 +6482,10 @@ static yyconst flex_int16_t yy_nxt[][128] = -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, - -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, + -343, -343, -343, -343, -343, -343, -343, -343, -343, 153, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, - 357, -343, -343, -343, -343, -343, -343, -343, -343, -343, + -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343, -343 }, @@ -6339,9 +6500,9 @@ static yyconst flex_int16_t yy_nxt[][128] = -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, - -344, -344, -344, -344, -344, -344, -344, -344, -344, 358, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, + 165, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344, -344 }, @@ -6357,8 +6518,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, + -345, -345, -345, -345, -345, -345, -345, -345, -345, 127, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, - 359, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345, -345 }, @@ -6375,7 +6536,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, - -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, + -346, -346, -346, -346, -346, 363, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346, -346 }, @@ -6392,7 +6553,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, - -347, -347, -347, -347, -347, -347, -347, -347, -347, 360, + 127, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347, -347 }, @@ -6401,8 +6562,8 @@ static yyconst flex_int16_t yy_nxt[][128] = 13, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, - -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, - -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, + -348, -348, 348, -348, -348, -348, -348, -348, -348, -348, + 349, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, @@ -6410,7 +6571,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, - -348, -348, -348, -348, -348, 361, -348, -348, -348, -348, + -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, -348 }, @@ -6426,7 +6587,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, - 362, -349, -349, -349, -349, -349, -349, -349, -349, -349, + -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349, -349 @@ -6445,7 +6606,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, - -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, + -350, -350, -350, -350, 364, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350, -350 }, @@ -6453,16 +6614,16 @@ static yyconst flex_int16_t yy_nxt[][128] = 13, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, - -351, -351, 351, -351, -351, -351, -351, -351, -351, -351, - -351, 352, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, - -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, + -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, + -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, + 365, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351, -351 }, @@ -6477,7 +6638,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, - -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, + -352, -352, -352, -352, -352, -352, -352, -352, -352, 366, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352, -352 @@ -6488,16 +6649,16 @@ static yyconst flex_int16_t yy_nxt[][128] = -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, - -353, -353, 351, -353, -353, -353, -353, -353, -353, -353, - -353, 352, -353, -353, -353, -353, -353, -353, 353, 353, - 353, 353, 353, 353, 353, 353, 353, 353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, - -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, + -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, + -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, + + 367, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353, -353 }, @@ -6514,7 +6675,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, - -354, 363, -354, -354, -354, -354, -354, -354, -354, -354, + -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354, -354 }, @@ -6530,8 +6691,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, + -355, -355, -355, -355, -355, -355, -355, -355, -355, 368, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, - -355, -355, -355, -355, -355, 125, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355, -355 }, @@ -6548,7 +6709,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, - -356, 364, -356, -356, -356, -356, -356, -356, -356, -356, + -356, -356, -356, -356, -356, 369, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356, -356 }, @@ -6565,7 +6726,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, - -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, + 370, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357, -357 }, @@ -6592,8 +6753,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, - -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, - -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, + -359, -359, 359, -359, -359, -359, -359, -359, -359, -359, + -359, 360, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, -359, @@ -6626,16 +6787,16 @@ static yyconst flex_int16_t yy_nxt[][128] = 13, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, - -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, - -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, - -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, + -361, -361, 359, -361, -361, -361, -361, -361, -361, -361, + -361, 360, -361, -361, -361, -361, -361, -361, 361, 361, + 361, 361, 361, 361, 361, 361, 361, 361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, - -361, -361, -361, -361, -361, 365, -361, -361, -361, -361, + -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361, -361 }, @@ -6652,7 +6813,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, - -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, + -362, 371, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362, -362 }, @@ -6668,9 +6829,9 @@ static yyconst flex_int16_t yy_nxt[][128] = -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, - -363, -363, -363, -363, -363, -363, -363, -363, -363, 125, - -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, + + -363, -363, -363, -363, -363, 127, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363, -363 }, @@ -6686,8 +6847,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, - -364, -364, -364, -364, -364, -364, -364, -364, -364, 366, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, + -364, 372, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364, -364 }, @@ -6726,26 +6887,163 @@ static yyconst flex_int16_t yy_nxt[][128] = -366, -366, -366, -366, -366, -366, -366, -366 }, + { + 13, -367, -367, -367, -367, -367, -367, -367, -367, -367, + -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, + -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, + -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, + -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, + -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, + -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, + -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, + -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, + + -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, + -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, + -367, -367, -367, -367, -367, -367, -367, -367, -367, -367, + -367, -367, -367, -367, -367, -367, -367, -367 + }, + + { + 13, -368, -368, -368, -368, -368, -368, -368, -368, -368, + -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, + -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, + -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, + -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, + -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, + + -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, + -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, + -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, + -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, + -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, + -368, -368, -368, -368, -368, -368, -368, -368, -368, -368, + -368, -368, -368, -368, -368, -368, -368, -368 + }, + + { + 13, -369, -369, -369, -369, -369, -369, -369, -369, -369, + -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, + -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, + + -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, + -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, + -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, + -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, + -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, + -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, + -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, + -369, -369, -369, -369, -369, -369, -369, -369, -369, -369, + -369, -369, -369, -369, -369, 373, -369, -369, -369, -369, + -369, -369, -369, -369, -369, -369, -369, -369 + + }, + + { + 13, -370, -370, -370, -370, -370, -370, -370, -370, -370, + -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, + -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, + -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, + -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, + -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, + -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, + -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, + -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, + -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, + + -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, + -370, -370, -370, -370, -370, -370, -370, -370, -370, -370, + -370, -370, -370, -370, -370, -370, -370, -370 + }, + + { + 13, -371, -371, -371, -371, -371, -371, -371, -371, -371, + -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, + -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, + -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, + -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, + -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, + -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, + + -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, + -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, + -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, + -371, -371, -371, -371, -371, -371, -371, -371, -371, 127, + -371, -371, -371, -371, -371, -371, -371, -371, -371, -371, + -371, -371, -371, -371, -371, -371, -371, -371 + }, + + { + 13, -372, -372, -372, -372, -372, -372, -372, -372, -372, + -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, + -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, + -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, + + -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, + -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, + -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, + -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, + -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, + -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, + -372, -372, -372, -372, -372, -372, -372, -372, -372, 374, + -372, -372, -372, -372, -372, -372, -372, -372, -372, -372, + -372, -372, -372, -372, -372, -372, -372, -372 + }, + + { + 13, -373, -373, -373, -373, -373, -373, -373, -373, -373, + + -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, + -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, + -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, + -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, + -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, + -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, + -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, + -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, + -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, + -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, + + -373, -373, -373, -373, -373, -373, -373, -373, -373, -373, + -373, -373, -373, -373, -373, -373, -373, -373 + }, + + { + 13, -374, -374, -374, -374, -374, -374, -374, -374, -374, + -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, + -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, + -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, + -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, + -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, + -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, + -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, + + -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, + -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, + -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, + -374, -374, -374, -374, -374, -374, -374, -374, -374, -374, + -374, -374, -374, -374, -374, -374, -374, -374 + }, + } ; -static yy_state_type yy_get_previous_state (void ); -static yy_state_type yy_try_NUL_trans (yy_state_type current_state ); -static int yy_get_next_buffer (void ); -static void yy_fatal_error (yyconst char msg[] ); +static yy_state_type yy_get_previous_state ( yyscan_t yyscanner ); +static yy_state_type yy_try_NUL_trans ( yy_state_type current_state , yyscan_t yyscanner); +static int yy_get_next_buffer ( yyscan_t yyscanner ); +static void yynoreturn yy_fatal_error ( const char* msg , yyscan_t yyscanner ); /* Done after the current pattern has been matched and before the - * corresponding action - sets up wcsulextext. + * corresponding action - sets up yytext. */ #define YY_DO_BEFORE_ACTION \ - (yytext_ptr) = yy_bp; \ - (yytext_ptr) -= (yy_more_len); \ - wcsulexleng = (size_t) (yy_cp - (yytext_ptr)); \ - (yy_hold_char) = *yy_cp; \ + yyg->yytext_ptr = yy_bp; \ + yyg->yytext_ptr -= yyg->yy_more_len; \ + yyleng = (int) (yy_cp - yyg->yytext_ptr); \ + yyg->yy_hold_char = *yy_cp; \ *yy_cp = '\0'; \ - (yy_c_buf_p) = yy_cp; - -#define YY_NUM_RULES 118 -#define YY_END_OF_BUFFER 119 + yyg->yy_c_buf_p = yy_cp; +#define YY_NUM_RULES 120 +#define YY_END_OF_BUFFER 121 /* This struct is not used in this scanner, but its presence is necessary. */ struct yy_trans_info @@ -6753,61 +7051,59 @@ struct yy_trans_info flex_int32_t yy_verify; flex_int32_t yy_nxt; }; -static yyconst flex_int16_t yy_accept[367] = +static const flex_int16_t yy_accept[375] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 117, 117, 119, 20, 118, 9, 11, 12, 14, 15, + 119, 119, 121, 20, 120, 9, 11, 12, 14, 15, 20, 15, 15, 20, 15, 15, 15, 15, 20, 20, 15, 15, 15, 15, 19, 15, 20, 20, 15, 20, - 20, 15, 20, 15, 20, 20, 15, 15, 20, 20, - 1, 8, 20, 2, 20, 20, 23, 21, 22, 44, - 41, 38, 37, 40, 39, 43, 42, 31, 25, 24, - 30, 35, 36, 26, 28, 29, 27, 33, 32, 105, - 45, 105, 57, 61, 66, 67, 69, 72, 74, 83, - 105, 105, 89, 92, 99, 101, 103, 46, 105, 105, - - 62, 105, 68, 70, 105, 78, 105, 105, 93, 100, - 105, 105, 116, 113, 112, 111, 116, 111, 114, 107, - 115, 106, 117, 9, 15, 0, 0, 16, 0, 16, - 16, 16, 16, 0, 0, 16, 17, 0, 0, 0, - 0, 16, 0, 0, 16, 0, 15, 15, 0, 15, - 15, 0, 0, 0, 16, 0, 0, 0, 0, 15, - 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, - 15, 15, 0, 0, 0, 0, 0, 15, 1, 13, - 4, 0, 0, 0, 23, 34, 51, 0, 0, 71, - 73, 0, 85, 91, 0, 104, 0, 0, 0, 0, - - 0, 58, 0, 0, 60, 0, 65, 0, 75, 76, - 0, 0, 0, 0, 86, 87, 0, 0, 0, 97, - 0, 46, 113, 112, 111, 0, 111, 114, 107, 115, - 106, 0, 0, 0, 0, 0, 111, 114, 107, 117, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 15, 0, 0, 0, 15, 0, 0, 0, - 3, 0, 0, 6, 0, 0, 0, 84, 98, 47, - 0, 0, 0, 54, 55, 0, 0, 63, 64, 77, - 79, 80, 81, 82, 0, 88, 90, 0, 0, 0, - 0, 110, 0, 0, 0, 108, 0, 0, 0, 0, - - 0, 18, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 7, 0, 5, 0, 56, 0, 0, - 52, 53, 59, 0, 0, 0, 0, 0, 0, 0, + 20, 15, 20, 15, 20, 20, 15, 20, 15, 20, + 20, 1, 8, 20, 2, 20, 20, 23, 21, 22, + 44, 41, 38, 37, 40, 39, 43, 42, 31, 25, + 24, 30, 35, 36, 26, 28, 29, 27, 33, 32, + 107, 45, 107, 57, 62, 67, 68, 70, 73, 75, + 84, 107, 107, 90, 93, 100, 103, 105, 46, 107, + + 107, 63, 107, 69, 71, 107, 79, 107, 107, 94, + 107, 102, 107, 107, 118, 115, 114, 113, 118, 113, + 116, 109, 117, 108, 119, 9, 15, 0, 0, 16, + 0, 16, 16, 16, 16, 0, 0, 16, 17, 0, + 0, 0, 0, 16, 0, 0, 16, 0, 15, 15, + 0, 15, 15, 0, 0, 0, 16, 0, 0, 0, + 0, 15, 0, 0, 15, 0, 0, 0, 0, 0, + 0, 0, 15, 15, 0, 0, 0, 0, 0, 0, + 15, 1, 13, 4, 0, 0, 0, 23, 34, 51, + 0, 0, 72, 74, 0, 86, 92, 0, 106, 0, + + 0, 0, 0, 0, 58, 0, 0, 60, 61, 0, + 66, 0, 76, 77, 0, 0, 0, 0, 87, 88, + 0, 0, 0, 98, 0, 0, 46, 115, 114, 113, + 0, 113, 116, 109, 117, 108, 0, 0, 0, 0, + 0, 113, 116, 109, 119, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, + 0, 15, 0, 0, 0, 0, 3, 0, 0, 6, + 0, 0, 0, 85, 99, 47, 0, 0, 0, 54, + 55, 0, 0, 64, 65, 78, 80, 81, 82, 83, + 0, 89, 91, 0, 0, 0, 0, 0, 112, 0, + + 0, 0, 110, 0, 0, 0, 0, 0, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 10, 0, 0, 0, 0, 88, 0, 0, 0, 102, - 0, 109, 0, 0, 0, 0, 49, 50, 87, 94, - 0, 96, 0, 0, 95, 48 + 7, 0, 5, 0, 56, 0, 0, 52, 53, 59, + 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, + 0, 0, 0, 89, 0, 0, 0, 104, 0, 111, + 0, 0, 0, 0, 49, 50, 88, 95, 0, 97, + 0, 0, 96, 48 } ; -static yy_state_type yy_last_accepting_state; -static char *yy_last_accepting_cpos; - -static yyconst yy_state_type yy_NUL_trans[367] = +static const yy_state_type yy_NUL_trans[375] = { 0, - 14, 14, 57, 57, 60, 60, 80, 80, 113, 113, - 123, 123, 0, 0, 0, 0, 0, 0, 0, 0, + 14, 14, 58, 58, 61, 61, 81, 81, 115, 115, + 125, 125, 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, 0, 0, 0, 0, 185, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 188, 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, @@ -6815,20 +7111,20 @@ static yyconst yy_state_type yy_NUL_trans[367] = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 240, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 245, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 185, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 188, 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, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 245, 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, @@ -6841,27 +7137,21 @@ static yyconst yy_state_type yy_NUL_trans[367] = 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, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0 } ; -extern int wcsulex_flex_debug; -int wcsulex_flex_debug = 0; - /* The intent behind this definition is that it'll catch * any uses of REJECT which flex missed. */ #define REJECT reject_used_but_not_detected -static int yy_more_flag = 0; -static int yy_more_len = 0; -#define yymore() ((yy_more_flag) = 1) -#define YY_MORE_ADJ (yy_more_len) +#define yymore() (yyg->yy_more_flag = 1) +#define YY_MORE_ADJ yyg->yy_more_len #define YY_RESTORE_YY_MORE_OFFSET -char *wcsulextext; #line 1 "wcsulex.l" /*============================================================================ - - WCSLIB 4.14 - an implementation of the FITS WCS standard. - Copyright (C) 1995-2012, Mark Calabretta + WCSLIB 8.6 - an implementation of the FITS WCS standard. + Copyright (C) 1995-2025, Mark Calabretta This file is part of WCSLIB. @@ -6878,15 +7168,13 @@ char *wcsulextext; You should have received a copy of the GNU Lesser General Public License along with WCSLIB. If not, see http://www.gnu.org/licenses. - Direct correspondence concerning WCSLIB to mark@calabretta.id.au - Author: Mark Calabretta, Australia Telescope National Facility, CSIRO. - http://www.atnf.csiro.au/people/Mark.Calabretta - $Id: wcsulex.c,v 4.14 2012/07/13 10:02:31 cal103 Exp $ + http://www.atnf.csiro.au/computing/software/wcs + $Id: wcsulex.c,v 8.6 2026/03/29 13:53:56 mcalabre Exp $ *============================================================================= * * wcsulex.l is a Flex description file containing the definition of a -* recursive, multi-buffered lexical scanner that parses FITS units +* recursive, multi-buffered lexical scanner and parser for FITS units * specifications. * * It requires Flex v2.5.4 or later. @@ -6896,6 +7184,7 @@ char *wcsulextext; * *===========================================================================*/ /* Options. */ +#define YY_NO_INPUT 1 /* Exponents. */ /* Metric prefixes. */ /* Basic and derived SI units. */ @@ -6907,10 +7196,6 @@ char *wcsulextext; /* Exclusive start states. */ #line 85 "wcsulex.l" -/* To get the prototype for fileno() from stdio.h when gcc is invoked with - * -std=c89 (same as -ansi) or -std=c99 since we do not define YY_INPUT. */ -#define _POSIX_SOURCE 1 - #include #include #include @@ -6921,14 +7206,27 @@ char *wcsulextext; #include "wcsunits.h" #include "wcsutil.h" -#define YY_DECL int wcsulexe(const char unitstr[], int *func, double *scale, \ - double units[], struct wcserr **err) +// User data associated with yyscanner. +struct wcsulex_extra { + // Used in preempting the call to exit() by yy_fatal_error(). + jmp_buf abort_jmp_env; +}; + +#define YY_DECL int wcsulexe_scanner(const char unitstr[], int *func, \ + double *scale, double units[WCSUNITS_NTYPE], struct wcserr **err, \ + yyscan_t yyscanner) + +// Dummy definition to circumvent compiler warnings. +#define YY_INPUT(inbuff, count, bufsize) { count = YY_NULL; } -/* Used in preempting the call to exit() by yy_fatal_error(). */ -jmp_buf wcsulex_abort_jmp_env; -#define exit(status) longjmp(wcsulex_abort_jmp_env, status) +// Preempt the call to exit() by yy_fatal_error(). +#define exit(status) longjmp(yyextra->abort_jmp_env, status); -#line 6932 "wcsulex.c" +// Internal helper functions. +static YY_DECL; + +#line 7229 "wcsulex.c" +#line 7230 "wcsulex.c" #define INITIAL 0 #define PAREN 1 @@ -6945,40 +7243,80 @@ jmp_buf wcsulex_abort_jmp_env; #include #endif -#ifndef YY_EXTRA_TYPE -#define YY_EXTRA_TYPE void * -#endif +#define YY_EXTRA_TYPE struct wcsulex_extra * + +/* Holds the entire state of the reentrant scanner. */ +struct yyguts_t + { + + /* User-defined. Not touched by flex. */ + YY_EXTRA_TYPE yyextra_r; -static int yy_init_globals (void ); + /* The rest are the same as the globals declared in the non-reentrant scanner. */ + FILE *yyin_r, *yyout_r; + size_t yy_buffer_stack_top; /**< index of top of stack. */ + size_t yy_buffer_stack_max; /**< capacity of stack. */ + YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */ + char yy_hold_char; + int yy_n_chars; + int yyleng_r; + char *yy_c_buf_p; + int yy_init; + int yy_start; + int yy_did_buffer_switch_on_eof; + int yy_start_stack_ptr; + int yy_start_stack_depth; + int *yy_start_stack; + yy_state_type yy_last_accepting_state; + char* yy_last_accepting_cpos; + + int yylineno_r; + int yy_flex_debug_r; + + char *yytext_r; + int yy_more_flag; + int yy_more_len; + + }; /* end struct yyguts_t */ + +static int yy_init_globals ( yyscan_t yyscanner ); + +int yylex_init (yyscan_t* scanner); + +int yylex_init_extra ( YY_EXTRA_TYPE user_defined, yyscan_t* scanner); /* Accessor methods to globals. These are made visible to non-reentrant scanners for convenience. */ -int wcsulexlex_destroy (void ); +int yylex_destroy ( yyscan_t yyscanner ); + +int yyget_debug ( yyscan_t yyscanner ); + +void yyset_debug ( int debug_flag , yyscan_t yyscanner ); -int wcsulexget_debug (void ); +YY_EXTRA_TYPE yyget_extra ( yyscan_t yyscanner ); -void wcsulexset_debug (int debug_flag ); +void yyset_extra ( YY_EXTRA_TYPE user_defined , yyscan_t yyscanner ); -YY_EXTRA_TYPE wcsulexget_extra (void ); +FILE *yyget_in ( yyscan_t yyscanner ); -void wcsulexset_extra (YY_EXTRA_TYPE user_defined ); +void yyset_in ( FILE * _in_str , yyscan_t yyscanner ); -FILE *wcsulexget_in (void ); +FILE *yyget_out ( yyscan_t yyscanner ); -void wcsulexset_in (FILE * in_str ); +void yyset_out ( FILE * _out_str , yyscan_t yyscanner ); -FILE *wcsulexget_out (void ); + int yyget_leng ( yyscan_t yyscanner ); -void wcsulexset_out (FILE * out_str ); +char *yyget_text ( yyscan_t yyscanner ); -int wcsulexget_leng (void ); +int yyget_lineno ( yyscan_t yyscanner ); -char *wcsulexget_text (void ); +void yyset_lineno ( int _line_number , yyscan_t yyscanner ); -int wcsulexget_lineno (void ); +int yyget_column ( yyscan_t yyscanner ); -void wcsulexset_lineno (int line_number ); +void yyset_column ( int _column_no , yyscan_t yyscanner ); /* Macros after this point can all be overridden by user definitions in * section 1. @@ -6986,28 +7324,31 @@ void wcsulexset_lineno (int line_number ); #ifndef YY_SKIP_YYWRAP #ifdef __cplusplus -extern "C" int wcsulexwrap (void ); +extern "C" int yywrap ( yyscan_t yyscanner ); #else -extern int wcsulexwrap (void ); +extern int yywrap ( yyscan_t yyscanner ); #endif #endif - static void yyunput (int c,char *buf_ptr ); +#ifndef YY_NO_UNPUT + static void yyunput ( int c, char *buf_ptr , yyscan_t yyscanner); + +#endif + #ifndef yytext_ptr -static void yy_flex_strncpy (char *,yyconst char *,int ); +static void yy_flex_strncpy ( char *, const char *, int , yyscan_t yyscanner); #endif #ifdef YY_NEED_STRLEN -static int yy_flex_strlen (yyconst char * ); +static int yy_flex_strlen ( const char * , yyscan_t yyscanner); #endif #ifndef YY_NO_INPUT - #ifdef __cplusplus -static int yyinput (void ); +static int yyinput ( yyscan_t yyscanner ); #else -static int input (void ); +static int input ( yyscan_t yyscanner ); #endif #endif @@ -7027,7 +7368,7 @@ static int input (void ); /* This used to be an fputs(), but since the string might contain NUL's, * we now use fwrite(). */ -#define ECHO do { if (fwrite( wcsulextext, wcsulexleng, 1, wcsulexout )) {} } while (0) +#define ECHO do { if (fwrite( yytext, (size_t) yyleng, 1, yyout )) {} } while (0) #endif /* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, @@ -7036,7 +7377,7 @@ static int input (void ); #ifndef YY_INPUT #define YY_INPUT(buf,result,max_size) \ errno=0; \ - while ( (result = read( fileno(wcsulexin), (char *) buf, max_size )) < 0 ) \ + while ( (result = (int) read( fileno(yyin), buf, (yy_size_t) max_size )) < 0 ) \ { \ if( errno != EINTR) \ { \ @@ -7044,7 +7385,7 @@ static int input (void ); break; \ } \ errno=0; \ - clearerr(wcsulexin); \ + clearerr(yyin); \ }\ \ @@ -7065,7 +7406,7 @@ static int input (void ); /* Report a fatal error. */ #ifndef YY_FATAL_ERROR -#define YY_FATAL_ERROR(msg) yy_fatal_error( msg ) +#define YY_FATAL_ERROR(msg) yy_fatal_error( msg , yyscanner) #endif /* end tables serialization structures and prototypes */ @@ -7076,12 +7417,12 @@ static int input (void ); #ifndef YY_DECL #define YY_DECL_IS_OURS 1 -extern int wcsulexlex (void); +extern int yylex (yyscan_t yyscanner); -#define YY_DECL int wcsulexlex (void) +#define YY_DECL int yylex (yyscan_t yyscanner) #endif /* !YY_DECL */ -/* Code executed at the beginning of each rule, after wcsulextext and wcsulexleng +/* Code executed at the beginning of each rule, after yytext and yyleng * have been set up. */ #ifndef YY_USER_ACTION @@ -7090,52 +7431,90 @@ extern int wcsulexlex (void); /* Code executed at the end of each rule. */ #ifndef YY_BREAK -#define YY_BREAK break; +#define YY_BREAK /*LINTED*/break; #endif #define YY_RULE_SETUP \ - if ( wcsulexleng > 0 ) \ + if ( yyleng > 0 ) \ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = \ - (wcsulextext[wcsulexleng - 1] == '\n'); \ + (yytext[yyleng - 1] == '\n'); \ YY_USER_ACTION /** The main scanner function which does all the work. */ YY_DECL { - register yy_state_type yy_current_state; - register char *yy_cp, *yy_bp; - register int yy_act; - -#line 108 "wcsulex.l" + yy_state_type yy_current_state; + char *yy_cp, *yy_bp; + int yy_act; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; - static const char *function = "wcsulexe"; + if ( !yyg->yy_init ) + { + yyg->yy_init = 1; - char ctmp[72]; - int bracket = 0; - int operator = 0; - int paren = 0; - int status = 0; - int func_r, i, j; - double dexp, expon, factor, factor_r, types[WCSUNITS_NTYPE]; - YY_BUFFER_STATE buf; +#ifdef YY_USER_INIT + YY_USER_INIT; +#endif + + if ( ! yyg->yy_start ) + yyg->yy_start = 1; /* first start state */ + + if ( ! yyin ) + yyin = stdin; + + if ( ! yyout ) + yyout = stdout; + + if ( ! YY_CURRENT_BUFFER ) { + yyensure_buffer_stack (yyscanner); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); + } + + yy_load_buffer_state( yyscanner ); + } + + { +#line 116 "wcsulex.l" + +#line 118 "wcsulex.l" + static const char *function = "wcsulexe_scanner"; + void add(double *factor, double types[], double *expon, double *scale, double units[]); - int wcsulexlex_destroy(void); - *func = 0; - for (i = 0; i < WCSUNITS_NTYPE; i++) { + // Initialise returned values. + *func = 0; + *scale = 1.0; + + for (int i = 0; i < WCSUNITS_NTYPE; i++) { units[i] = 0.0; + } + + if (err) *err = 0x0; + + double types[WCSUNITS_NTYPE]; + for (int i = 0; i < WCSUNITS_NTYPE; i++) { types[i] = 0.0; } - expon = 1.0; - factor = 1.0; - *scale = 1.0; + double expon = 1.0; + double factor = 1.0; - wcsulex_scan_string(unitstr); + int bracket = 0; + int operator = 0; + int paren = 0; + int status = 0; + + // Avert a flex-induced memory leak. + if (YY_CURRENT_BUFFER && YY_CURRENT_BUFFER->yy_input_file == stdin) { + yy_delete_buffer(YY_CURRENT_BUFFER, yyscanner); + } + + yy_scan_string(unitstr, yyscanner); - /* Return here via longjmp() invoked by yy_fatal_error(). */ - if (setjmp(wcsulex_abort_jmp_env)) { + // Return here via longjmp() invoked by yy_fatal_error(). + if (setjmp(yyextra->abort_jmp_env)) { return wcserr_set(WCSERR_SET(UNITSERR_PARSER_ERROR), "Internal units parser error parsing '%s'", unitstr); } @@ -7146,61 +7525,35 @@ YY_DECL fprintf(stderr, "\n%s ->\n", unitstr); #endif -#line 7150 "wcsulex.c" - - if ( !(yy_init) ) - { - (yy_init) = 1; - -#ifdef YY_USER_INIT - YY_USER_INIT; -#endif - - if ( ! (yy_start) ) - (yy_start) = 1; /* first start state */ - - if ( ! wcsulexin ) - wcsulexin = stdin; - - if ( ! wcsulexout ) - wcsulexout = stdout; - - if ( ! YY_CURRENT_BUFFER ) { - wcsulexensure_buffer_stack (); - YY_CURRENT_BUFFER_LVALUE = - wcsulex_create_buffer(wcsulexin,YY_BUF_SIZE ); - } - - wcsulex_load_buffer_state( ); - } +#line 7529 "wcsulex.c" - while ( 1 ) /* loops until end-of-file is reached */ + while ( /*CONSTCOND*/1 ) /* loops until end-of-file is reached */ { - (yy_more_len) = 0; - if ( (yy_more_flag) ) + yyg->yy_more_len = 0; + if ( yyg->yy_more_flag ) { - (yy_more_len) = (yy_c_buf_p) - (yytext_ptr); - (yy_more_flag) = 0; + yyg->yy_more_len = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr); + yyg->yy_more_flag = 0; } - yy_cp = (yy_c_buf_p); + yy_cp = yyg->yy_c_buf_p; - /* Support of wcsulextext. */ - *yy_cp = (yy_hold_char); + /* Support of yytext. */ + *yy_cp = yyg->yy_hold_char; /* yy_bp points to the position in yy_ch_buf of the start of * the current run. */ yy_bp = yy_cp; - yy_current_state = (yy_start); + yy_current_state = yyg->yy_start; yy_current_state += YY_AT_BOL(); yy_match: while ( (yy_current_state = yy_nxt[yy_current_state][ YY_SC_TO_UI(*yy_cp) ]) > 0 ) { if ( yy_accept[yy_current_state] ) { - (yy_last_accepting_state) = yy_current_state; - (yy_last_accepting_cpos) = yy_cp; + yyg->yy_last_accepting_state = yy_current_state; + yyg->yy_last_accepting_cpos = yy_cp; } ++yy_cp; @@ -7219,22 +7572,22 @@ YY_DECL { /* beginning of action switch */ case 0: /* must back up */ /* undo the effects of YY_DO_BEFORE_ACTION */ - *yy_cp = (yy_hold_char); - yy_cp = (yy_last_accepting_cpos) + 1; - yy_current_state = (yy_last_accepting_state); + *yy_cp = yyg->yy_hold_char; + yy_cp = yyg->yy_last_accepting_cpos + 1; + yy_current_state = yyg->yy_last_accepting_state; goto yy_find_action; case 1: YY_RULE_SETUP -#line 146 "wcsulex.l" +#line 164 "wcsulex.l" { - /* Pretend initial whitespace doesn't exist. */ + // Pretend initial whitespace doesn't exist. yy_set_bol(1); } YY_BREAK case 2: YY_RULE_SETUP -#line 151 "wcsulex.l" +#line 169 "wcsulex.l" { if (bracket++) { BEGIN(FLUSH); @@ -7245,7 +7598,7 @@ YY_RULE_SETUP YY_BREAK case 3: YY_RULE_SETUP -#line 159 "wcsulex.l" +#line 177 "wcsulex.l" { status = wcserr_set(WCSERR_SET(UNITSERR_BAD_NUM_MULTIPLIER), "Invalid exponent in '%s'", unitstr); @@ -7254,7 +7607,7 @@ YY_RULE_SETUP YY_BREAK case 4: YY_RULE_SETUP -#line 165 "wcsulex.l" +#line 183 "wcsulex.l" { factor = 10.0; BEGIN(EXPON); @@ -7262,7 +7615,7 @@ YY_RULE_SETUP YY_BREAK case 5: YY_RULE_SETUP -#line 170 "wcsulex.l" +#line 188 "wcsulex.l" { *func = 1; unput('('); @@ -7271,7 +7624,7 @@ YY_RULE_SETUP YY_BREAK case 6: YY_RULE_SETUP -#line 176 "wcsulex.l" +#line 194 "wcsulex.l" { *func = 2; unput('('); @@ -7280,7 +7633,7 @@ YY_RULE_SETUP YY_BREAK case 7: YY_RULE_SETUP -#line 182 "wcsulex.l" +#line 200 "wcsulex.l" { *func = 3; unput('('); @@ -7289,9 +7642,9 @@ YY_RULE_SETUP YY_BREAK case 8: YY_RULE_SETUP -#line 188 "wcsulex.l" +#line 206 "wcsulex.l" { - /* Leading binary multiply. */ + // Leading binary multiply. status = wcserr_set(WCSERR_SET(UNITSERR_DANGLING_BINOP), "Dangling binary operator in '%s'", unitstr); BEGIN(FLUSH); @@ -7299,12 +7652,12 @@ YY_RULE_SETUP YY_BREAK case 9: YY_RULE_SETUP -#line 195 "wcsulex.l" -/* Discard whitespace in INITIAL context. */ +#line 213 "wcsulex.l" +// Discard whitespace in INITIAL context. YY_BREAK case 10: YY_RULE_SETUP -#line 197 "wcsulex.l" +#line 215 "wcsulex.l" { expon /= 2.0; unput('('); @@ -7313,16 +7666,16 @@ YY_RULE_SETUP YY_BREAK case 11: YY_RULE_SETUP -#line 203 "wcsulex.l" +#line 221 "wcsulex.l" { - /* Gather terms in parentheses. */ + // Gather terms in parentheses. yyless(0); BEGIN(PAREN); } YY_BREAK case 12: YY_RULE_SETUP -#line 209 "wcsulex.l" +#line 227 "wcsulex.l" { if (operator++) { BEGIN(FLUSH); @@ -7330,10 +7683,10 @@ YY_RULE_SETUP } YY_BREAK case 13: -#line 216 "wcsulex.l" +#line 234 "wcsulex.l" case 14: YY_RULE_SETUP -#line 216 "wcsulex.l" +#line 234 "wcsulex.l" { if (operator++) { BEGIN(FLUSH); @@ -7344,7 +7697,7 @@ YY_RULE_SETUP YY_BREAK case 15: YY_RULE_SETUP -#line 224 "wcsulex.l" +#line 242 "wcsulex.l" { operator = 0; yyless(0); @@ -7352,12 +7705,12 @@ YY_RULE_SETUP } YY_BREAK case 16: -#line 231 "wcsulex.l" +#line 249 "wcsulex.l" case 17: -#line 232 "wcsulex.l" +#line 250 "wcsulex.l" case 18: YY_RULE_SETUP -#line 232 "wcsulex.l" +#line 250 "wcsulex.l" { operator = 0; yyless(0); @@ -7366,7 +7719,7 @@ YY_RULE_SETUP YY_BREAK case 19: YY_RULE_SETUP -#line 238 "wcsulex.l" +#line 256 "wcsulex.l" { bracket = !bracket; BEGIN(FLUSH); @@ -7374,7 +7727,7 @@ YY_RULE_SETUP YY_BREAK case 20: YY_RULE_SETUP -#line 243 "wcsulex.l" +#line 261 "wcsulex.l" { status = wcserr_set(WCSERR_SET(UNITSERR_BAD_INITIAL_SYMBOL), "Invalid symbol in INITIAL context in '%s'", unitstr); @@ -7383,7 +7736,7 @@ YY_RULE_SETUP YY_BREAK case 21: YY_RULE_SETUP -#line 249 "wcsulex.l" +#line 267 "wcsulex.l" { paren++; operator = 0; @@ -7392,20 +7745,23 @@ YY_RULE_SETUP YY_BREAK case 22: YY_RULE_SETUP -#line 255 "wcsulex.l" +#line 273 "wcsulex.l" { paren--; if (paren) { - /* Not balanced yet. */ + // Not balanced yet. yymore(); } else { - /* Balanced; strip off the outer parentheses and recurse. */ - wcsulextext[wcsulexleng-1] = '\0'; + // Balanced; strip off the outer parentheses and recurse. + yytext[yyleng-1] = '\0'; + + int func_r; + double factor_r; + status = wcsulexe(yytext+1, &func_r, &factor_r, types, err); - buf = YY_CURRENT_BUFFER; - status = wcsulexe(wcsulextext+1, &func_r, &factor_r, types, err); - wcsulex_switch_to_buffer(buf); + YY_BUFFER_STATE buf = YY_CURRENT_BUFFER; + yy_switch_to_buffer(buf, yyscanner); if (func_r) { status = wcserr_set(WCSERR_SET(UNITSERR_FUNCTION_CONTEXT), @@ -7424,14 +7780,14 @@ YY_RULE_SETUP case 23: /* rule 23 can match eol */ YY_RULE_SETUP -#line 283 "wcsulex.l" +#line 304 "wcsulex.l" { yymore(); } YY_BREAK case 24: YY_RULE_SETUP -#line 287 "wcsulex.l" +#line 308 "wcsulex.l" { factor = 1e-1; BEGIN(UNITS); @@ -7439,7 +7795,7 @@ YY_RULE_SETUP YY_BREAK case 25: YY_RULE_SETUP -#line 292 "wcsulex.l" +#line 313 "wcsulex.l" { factor = 1e-2; BEGIN(UNITS); @@ -7447,7 +7803,7 @@ YY_RULE_SETUP YY_BREAK case 26: YY_RULE_SETUP -#line 297 "wcsulex.l" +#line 318 "wcsulex.l" { factor = 1e-3; BEGIN(UNITS); @@ -7455,7 +7811,7 @@ YY_RULE_SETUP YY_BREAK case 27: YY_RULE_SETUP -#line 302 "wcsulex.l" +#line 323 "wcsulex.l" { factor = 1e-6; BEGIN(UNITS); @@ -7463,7 +7819,7 @@ YY_RULE_SETUP YY_BREAK case 28: YY_RULE_SETUP -#line 307 "wcsulex.l" +#line 328 "wcsulex.l" { factor = 1e-9; BEGIN(UNITS); @@ -7471,7 +7827,7 @@ YY_RULE_SETUP YY_BREAK case 29: YY_RULE_SETUP -#line 312 "wcsulex.l" +#line 333 "wcsulex.l" { factor = 1e-12; BEGIN(UNITS); @@ -7479,7 +7835,7 @@ YY_RULE_SETUP YY_BREAK case 30: YY_RULE_SETUP -#line 317 "wcsulex.l" +#line 338 "wcsulex.l" { factor = 1e-15; BEGIN(UNITS); @@ -7487,7 +7843,7 @@ YY_RULE_SETUP YY_BREAK case 31: YY_RULE_SETUP -#line 322 "wcsulex.l" +#line 343 "wcsulex.l" { factor = 1e-18; BEGIN(UNITS); @@ -7495,7 +7851,7 @@ YY_RULE_SETUP YY_BREAK case 32: YY_RULE_SETUP -#line 327 "wcsulex.l" +#line 348 "wcsulex.l" { factor = 1e-21; BEGIN(UNITS); @@ -7503,7 +7859,7 @@ YY_RULE_SETUP YY_BREAK case 33: YY_RULE_SETUP -#line 332 "wcsulex.l" +#line 353 "wcsulex.l" { factor = 1e-24; BEGIN(UNITS); @@ -7511,7 +7867,7 @@ YY_RULE_SETUP YY_BREAK case 34: YY_RULE_SETUP -#line 337 "wcsulex.l" +#line 358 "wcsulex.l" { factor = 1e+1; BEGIN(UNITS); @@ -7519,7 +7875,7 @@ YY_RULE_SETUP YY_BREAK case 35: YY_RULE_SETUP -#line 342 "wcsulex.l" +#line 363 "wcsulex.l" { factor = 1e+2; BEGIN(UNITS); @@ -7527,7 +7883,7 @@ YY_RULE_SETUP YY_BREAK case 36: YY_RULE_SETUP -#line 347 "wcsulex.l" +#line 368 "wcsulex.l" { factor = 1e+3; BEGIN(UNITS); @@ -7535,7 +7891,7 @@ YY_RULE_SETUP YY_BREAK case 37: YY_RULE_SETUP -#line 352 "wcsulex.l" +#line 373 "wcsulex.l" { factor = 1e+6; BEGIN(UNITS); @@ -7543,7 +7899,7 @@ YY_RULE_SETUP YY_BREAK case 38: YY_RULE_SETUP -#line 357 "wcsulex.l" +#line 378 "wcsulex.l" { factor = 1e+9; BEGIN(UNITS); @@ -7551,7 +7907,7 @@ YY_RULE_SETUP YY_BREAK case 39: YY_RULE_SETUP -#line 362 "wcsulex.l" +#line 383 "wcsulex.l" { factor = 1e+12; BEGIN(UNITS); @@ -7559,7 +7915,7 @@ YY_RULE_SETUP YY_BREAK case 40: YY_RULE_SETUP -#line 367 "wcsulex.l" +#line 388 "wcsulex.l" { factor = 1e+15; BEGIN(UNITS); @@ -7567,7 +7923,7 @@ YY_RULE_SETUP YY_BREAK case 41: YY_RULE_SETUP -#line 372 "wcsulex.l" +#line 393 "wcsulex.l" { factor = 1e+18; BEGIN(UNITS); @@ -7575,7 +7931,7 @@ YY_RULE_SETUP YY_BREAK case 42: YY_RULE_SETUP -#line 377 "wcsulex.l" +#line 398 "wcsulex.l" { factor = 1e+21; BEGIN(UNITS); @@ -7583,7 +7939,7 @@ YY_RULE_SETUP YY_BREAK case 43: YY_RULE_SETUP -#line 382 "wcsulex.l" +#line 403 "wcsulex.l" { factor = 1e+24; BEGIN(UNITS); @@ -7591,9 +7947,9 @@ YY_RULE_SETUP YY_BREAK case 44: YY_RULE_SETUP -#line 387 "wcsulex.l" +#line 408 "wcsulex.l" { - /* Internal parser error. */ + // Internal parser error. status = wcserr_set(WCSERR_SET(UNITSERR_PARSER_ERROR), "Internal units parser error parsing '%s'", unitstr); BEGIN(FLUSH); @@ -7601,9 +7957,9 @@ YY_RULE_SETUP YY_BREAK case 45: YY_RULE_SETUP -#line 394 "wcsulex.l" +#line 415 "wcsulex.l" { - /* Ampere. */ + // Ampere. types[WCSUNITS_CHARGE] += 1.0; types[WCSUNITS_TIME] -= 1.0; BEGIN(EXPON); @@ -7611,9 +7967,9 @@ YY_RULE_SETUP YY_BREAK case 46: YY_RULE_SETUP -#line 401 "wcsulex.l" +#line 422 "wcsulex.l" { - /* Year (annum). */ + // Julian year (annum). factor *= 31557600.0; types[WCSUNITS_TIME] += 1.0; BEGIN(EXPON); @@ -7621,18 +7977,18 @@ YY_RULE_SETUP YY_BREAK case 47: YY_RULE_SETUP -#line 408 "wcsulex.l" +#line 429 "wcsulex.l" { - /* Analogue-to-digital converter units. */ + // Analogue-to-digital converter units. types[WCSUNITS_COUNT] += 1.0; BEGIN(EXPON); } YY_BREAK case 48: YY_RULE_SETUP -#line 414 "wcsulex.l" +#line 435 "wcsulex.l" { - /* Angstrom. */ + // Angstrom. factor *= 1e-10; types[WCSUNITS_LENGTH] += 1.0; BEGIN(EXPON); @@ -7640,9 +7996,9 @@ YY_RULE_SETUP YY_BREAK case 49: YY_RULE_SETUP -#line 421 "wcsulex.l" +#line 442 "wcsulex.l" { - /* Minute of arc. */ + // Minute of arc. factor /= 60.0; types[WCSUNITS_PLANE_ANGLE] += 1.0; BEGIN(EXPON); @@ -7650,9 +8006,9 @@ YY_RULE_SETUP YY_BREAK case 50: YY_RULE_SETUP -#line 428 "wcsulex.l" +#line 449 "wcsulex.l" { - /* Second of arc. */ + // Second of arc. factor /= 3600.0; types[WCSUNITS_PLANE_ANGLE] += 1.0; BEGIN(EXPON); @@ -7660,9 +8016,9 @@ YY_RULE_SETUP YY_BREAK case 51: YY_RULE_SETUP -#line 435 "wcsulex.l" +#line 456 "wcsulex.l" { - /* Astronomical unit. */ + // Astronomical unit. factor *= 1.49598e+11; types[WCSUNITS_LENGTH] += 1.0; BEGIN(EXPON); @@ -7670,9 +8026,9 @@ YY_RULE_SETUP YY_BREAK case 52: YY_RULE_SETUP -#line 442 "wcsulex.l" +#line 463 "wcsulex.l" { - /* Barn. */ + // Barn. factor *= 1e-28; types[WCSUNITS_LENGTH] += 2.0; BEGIN(EXPON); @@ -7680,36 +8036,36 @@ YY_RULE_SETUP YY_BREAK case 53: YY_RULE_SETUP -#line 449 "wcsulex.l" +#line 470 "wcsulex.l" { - /* Beam, as in Jy/beam. */ + // Beam, as in Jy/beam. types[WCSUNITS_BEAM] += 1.0; BEGIN(EXPON); } YY_BREAK case 54: YY_RULE_SETUP -#line 455 "wcsulex.l" +#line 476 "wcsulex.l" { - /* Bin (e.g. histogram). */ + // Bin (e.g. histogram). types[WCSUNITS_BIN] += 1.0; BEGIN(EXPON); } YY_BREAK case 55: YY_RULE_SETUP -#line 461 "wcsulex.l" +#line 482 "wcsulex.l" { - /* Bit. */ + // Bit. types[WCSUNITS_BIT] += 1.0; BEGIN(EXPON); } YY_BREAK case 56: YY_RULE_SETUP -#line 467 "wcsulex.l" +#line 488 "wcsulex.l" { - /* Byte. */ + // Byte. factor *= 8.0; types[WCSUNITS_BIT] += 1.0; BEGIN(EXPON); @@ -7717,75 +8073,85 @@ YY_RULE_SETUP YY_BREAK case 57: YY_RULE_SETUP -#line 474 "wcsulex.l" +#line 495 "wcsulex.l" { - /* Coulomb. */ + // Coulomb. types[WCSUNITS_CHARGE] += 1.0; BEGIN(EXPON); } YY_BREAK case 58: YY_RULE_SETUP -#line 480 "wcsulex.l" +#line 501 "wcsulex.l" { - /* Candela. */ + // Candela. types[WCSUNITS_LUMINTEN] += 1.0; BEGIN(EXPON); } YY_BREAK case 59: YY_RULE_SETUP -#line 486 "wcsulex.l" +#line 507 "wcsulex.l" { - /* Channel. */ + // Channel. types[WCSUNITS_BIN] += 1.0; BEGIN(EXPON); } YY_BREAK case 60: YY_RULE_SETUP -#line 492 "wcsulex.l" +#line 513 "wcsulex.l" { - /* Count. */ + // Count. types[WCSUNITS_COUNT] += 1.0; BEGIN(EXPON); } YY_BREAK case 61: YY_RULE_SETUP -#line 498 "wcsulex.l" +#line 519 "wcsulex.l" +{ + // Julian century. + factor *= 3155760000.0; + types[WCSUNITS_TIME] += 1.0; + BEGIN(EXPON); + } + YY_BREAK +case 62: +YY_RULE_SETUP +#line 526 "wcsulex.l" { - /* Debye. */ + // Debye. factor *= 1e-29 / 3.0; types[WCSUNITS_CHARGE] += 1.0; types[WCSUNITS_LENGTH] += 1.0; BEGIN(EXPON); } YY_BREAK -case 62: +case 63: YY_RULE_SETUP -#line 506 "wcsulex.l" +#line 534 "wcsulex.l" { - /* Day. */ + // Day. factor *= 86400.0; types[WCSUNITS_TIME] += 1.0; BEGIN(EXPON); } YY_BREAK -case 63: +case 64: YY_RULE_SETUP -#line 513 "wcsulex.l" +#line 541 "wcsulex.l" { - /* Degree. */ + // Degree. types[WCSUNITS_PLANE_ANGLE] += 1.0; BEGIN(EXPON); } YY_BREAK -case 64: +case 65: YY_RULE_SETUP -#line 519 "wcsulex.l" +#line 547 "wcsulex.l" { - /* Erg. */ + // Erg. factor *= 1e-7; types[WCSUNITS_MASS] += 1.0; types[WCSUNITS_LENGTH] += 2.0; @@ -7793,11 +8159,11 @@ YY_RULE_SETUP BEGIN(EXPON); } YY_BREAK -case 65: +case 66: YY_RULE_SETUP -#line 528 "wcsulex.l" +#line 556 "wcsulex.l" { - /* Electron volt. */ + // Electron volt. factor *= 1.6021765e-19; types[WCSUNITS_MASS] += 1.0; types[WCSUNITS_LENGTH] += 2.0; @@ -7805,11 +8171,11 @@ YY_RULE_SETUP BEGIN(EXPON); } YY_BREAK -case 66: +case 67: YY_RULE_SETUP -#line 537 "wcsulex.l" +#line 565 "wcsulex.l" { - /* Farad. */ + // Farad. types[WCSUNITS_MASS] -= 1.0; types[WCSUNITS_LENGTH] -= 2.0; types[WCSUNITS_TIME] += 3.0; @@ -7817,11 +8183,11 @@ YY_RULE_SETUP BEGIN(EXPON); } YY_BREAK -case 67: +case 68: YY_RULE_SETUP -#line 546 "wcsulex.l" +#line 574 "wcsulex.l" { - /* Gauss. */ + // Gauss. factor *= 1e-4; types[WCSUNITS_MASS] += 1.0; types[WCSUNITS_TIME] += 1.0; @@ -7829,21 +8195,21 @@ YY_RULE_SETUP BEGIN(EXPON); } YY_BREAK -case 68: +case 69: YY_RULE_SETUP -#line 555 "wcsulex.l" +#line 583 "wcsulex.l" { - /* Gram. */ + // Gram. factor *= 1e-3; types[WCSUNITS_MASS] += 1.0; BEGIN(EXPON); } YY_BREAK -case 69: +case 70: YY_RULE_SETUP -#line 562 "wcsulex.l" +#line 590 "wcsulex.l" { - /* Henry. */ + // Henry. types[WCSUNITS_MASS] += 1.0; types[WCSUNITS_LENGTH] += 2.0; types[WCSUNITS_TIME] += 2.0; @@ -7851,150 +8217,150 @@ YY_RULE_SETUP BEGIN(EXPON); } YY_BREAK -case 70: +case 71: YY_RULE_SETUP -#line 571 "wcsulex.l" +#line 599 "wcsulex.l" { - /* Hour. */ + // Hour. factor *= 3600.0; types[WCSUNITS_TIME] += 1.0; BEGIN(EXPON); } YY_BREAK -case 71: +case 72: YY_RULE_SETUP -#line 578 "wcsulex.l" +#line 606 "wcsulex.l" { - /* Hertz. */ + // Hertz. types[WCSUNITS_TIME] -= 1.0; BEGIN(EXPON); } YY_BREAK -case 72: +case 73: YY_RULE_SETUP -#line 584 "wcsulex.l" +#line 612 "wcsulex.l" { - /* Joule. */ + // Joule. types[WCSUNITS_MASS] += 1.0; types[WCSUNITS_LENGTH] += 2.0; types[WCSUNITS_TIME] -= 2.0; BEGIN(EXPON); } YY_BREAK -case 73: +case 74: YY_RULE_SETUP -#line 592 "wcsulex.l" +#line 620 "wcsulex.l" { - /* Jansky. */ + // Jansky. factor *= 1e-26; types[WCSUNITS_MASS] += 1.0; types[WCSUNITS_TIME] -= 2.0; BEGIN(EXPON); } YY_BREAK -case 74: +case 75: YY_RULE_SETUP -#line 600 "wcsulex.l" +#line 628 "wcsulex.l" { - /* Kelvin. */ + // Kelvin. types[WCSUNITS_TEMPERATURE] += 1.0; BEGIN(EXPON); } YY_BREAK -case 75: +case 76: YY_RULE_SETUP -#line 606 "wcsulex.l" +#line 634 "wcsulex.l" { - /* Lumen. */ + // Lumen. types[WCSUNITS_LUMINTEN] += 1.0; types[WCSUNITS_SOLID_ANGLE] += 1.0; BEGIN(EXPON); } YY_BREAK -case 76: +case 77: YY_RULE_SETUP -#line 613 "wcsulex.l" +#line 641 "wcsulex.l" { - /* Lux. */ + // Lux. types[WCSUNITS_LUMINTEN] += 1.0; types[WCSUNITS_SOLID_ANGLE] += 1.0; types[WCSUNITS_LENGTH] -= 2.0; BEGIN(EXPON); } YY_BREAK -case 77: +case 78: YY_RULE_SETUP -#line 621 "wcsulex.l" +#line 649 "wcsulex.l" { - /* Light year. */ + // Light year. factor *= 2.99792458e8 * 31557600.0; types[WCSUNITS_LENGTH] += 1.0; BEGIN(EXPON); } YY_BREAK -case 78: +case 79: YY_RULE_SETUP -#line 628 "wcsulex.l" +#line 656 "wcsulex.l" { - /* Metre. */ + // Metre. types[WCSUNITS_LENGTH] += 1.0; BEGIN(EXPON); } YY_BREAK -case 79: +case 80: YY_RULE_SETUP -#line 634 "wcsulex.l" +#line 662 "wcsulex.l" { - /* Stellar magnitude. */ + // Stellar magnitude. types[WCSUNITS_MAGNITUDE] += 1.0; BEGIN(EXPON); } YY_BREAK -case 80: +case 81: YY_RULE_SETUP -#line 640 "wcsulex.l" +#line 668 "wcsulex.l" { - /* Milli-arcsec. */ + // Milli-arcsec. factor /= 3600e+3; types[WCSUNITS_PLANE_ANGLE] += 1.0; BEGIN(EXPON); } YY_BREAK -case 81: +case 82: YY_RULE_SETUP -#line 647 "wcsulex.l" +#line 675 "wcsulex.l" { - /* Minute. */ + // Minute. factor *= 60.0; types[WCSUNITS_TIME] += 1.0; BEGIN(EXPON); } YY_BREAK -case 82: +case 83: YY_RULE_SETUP -#line 654 "wcsulex.l" +#line 682 "wcsulex.l" { - /* Mole. */ + // Mole. types[WCSUNITS_MOLE] += 1.0; BEGIN(EXPON); } YY_BREAK -case 83: +case 84: YY_RULE_SETUP -#line 660 "wcsulex.l" +#line 688 "wcsulex.l" { - /* Newton. */ + // Newton. types[WCSUNITS_MASS] += 1.0; types[WCSUNITS_LENGTH] += 1.0; types[WCSUNITS_TIME] -= 2.0; BEGIN(EXPON); } YY_BREAK -case 84: +case 85: YY_RULE_SETUP -#line 668 "wcsulex.l" +#line 696 "wcsulex.l" { - /* Ohm. */ + // Ohm. types[WCSUNITS_MASS] += 1.0; types[WCSUNITS_LENGTH] += 2.0; types[WCSUNITS_TIME] -= 1.0; @@ -8002,50 +8368,50 @@ YY_RULE_SETUP BEGIN(EXPON); } YY_BREAK -case 85: +case 86: YY_RULE_SETUP -#line 677 "wcsulex.l" +#line 705 "wcsulex.l" { - /* Pascal. */ + // Pascal. types[WCSUNITS_MASS] += 1.0; types[WCSUNITS_LENGTH] -= 1.0; types[WCSUNITS_TIME] -= 2.0; BEGIN(EXPON); } YY_BREAK -case 86: +case 87: YY_RULE_SETUP -#line 685 "wcsulex.l" +#line 713 "wcsulex.l" { - /* Parsec. */ + // Parsec. factor *= 3.0857e16; types[WCSUNITS_LENGTH] += 1.0; BEGIN(EXPON); } YY_BREAK -case 87: +case 88: YY_RULE_SETUP -#line 692 "wcsulex.l" +#line 720 "wcsulex.l" { - /* Photon. */ + // Photon. types[WCSUNITS_COUNT] += 1.0; BEGIN(EXPON); } YY_BREAK -case 88: +case 89: YY_RULE_SETUP -#line 698 "wcsulex.l" +#line 726 "wcsulex.l" { - /* Pixel. */ + // Pixel. types[WCSUNITS_PIXEL] += 1.0; BEGIN(EXPON); } YY_BREAK -case 89: +case 90: YY_RULE_SETUP -#line 704 "wcsulex.l" +#line 732 "wcsulex.l" { - /* Rayleigh. */ + // Rayleigh. factor *= 1e10 / (4.0 * PI); types[WCSUNITS_LENGTH] -= 2.0; types[WCSUNITS_TIME] -= 1.0; @@ -8053,21 +8419,21 @@ YY_RULE_SETUP BEGIN(EXPON); } YY_BREAK -case 90: +case 91: YY_RULE_SETUP -#line 713 "wcsulex.l" +#line 741 "wcsulex.l" { - /* Radian. */ + // Radian. factor *= 180.0 / PI; types[WCSUNITS_PLANE_ANGLE] += 1.0; BEGIN(EXPON); } YY_BREAK -case 91: +case 92: YY_RULE_SETUP -#line 720 "wcsulex.l" +#line 748 "wcsulex.l" { - /* Rydberg. */ + // Rydberg. factor *= 13.605692 * 1.6021765e-19; types[WCSUNITS_MASS] += 1.0; types[WCSUNITS_LENGTH] += 2.0; @@ -8075,11 +8441,11 @@ YY_RULE_SETUP BEGIN(EXPON); } YY_BREAK -case 92: +case 93: YY_RULE_SETUP -#line 729 "wcsulex.l" +#line 757 "wcsulex.l" { - /* Siemen. */ + // Siemen. types[WCSUNITS_MASS] -= 1.0; types[WCSUNITS_LENGTH] -= 2.0; types[WCSUNITS_TIME] += 1.0; @@ -8087,20 +8453,20 @@ YY_RULE_SETUP BEGIN(EXPON); } YY_BREAK -case 93: +case 94: YY_RULE_SETUP -#line 738 "wcsulex.l" +#line 766 "wcsulex.l" { - /* Second. */ + // Second. types[WCSUNITS_TIME] += 1.0; BEGIN(EXPON); } YY_BREAK -case 94: +case 95: YY_RULE_SETUP -#line 744 "wcsulex.l" +#line 772 "wcsulex.l" { - /* Solar luminosity. */ + // Solar luminosity. factor *= 3.8268e26; types[WCSUNITS_MASS] += 1.0; types[WCSUNITS_LENGTH] += 2.0; @@ -8108,70 +8474,80 @@ YY_RULE_SETUP BEGIN(EXPON); } YY_BREAK -case 95: +case 96: YY_RULE_SETUP -#line 753 "wcsulex.l" +#line 781 "wcsulex.l" { - /* Solar mass. */ + // Solar mass. factor *= 1.9891e30; types[WCSUNITS_MASS] += 1.0; BEGIN(EXPON); } YY_BREAK -case 96: +case 97: YY_RULE_SETUP -#line 760 "wcsulex.l" +#line 788 "wcsulex.l" { - /* Solar radius. */ + // Solar radius. factor *= 6.9599e8; types[WCSUNITS_LENGTH] += 1.0; BEGIN(EXPON); } YY_BREAK -case 97: +case 98: YY_RULE_SETUP -#line 767 "wcsulex.l" +#line 795 "wcsulex.l" { - /* Steradian. */ + // Steradian. types[WCSUNITS_SOLID_ANGLE] += 1.0; BEGIN(EXPON); } YY_BREAK -case 98: +case 99: YY_RULE_SETUP -#line 773 "wcsulex.l" +#line 801 "wcsulex.l" { - /* Sun (with respect to). */ + // Sun (with respect to). types[WCSUNITS_SOLRATIO] += 1.0; BEGIN(EXPON); } YY_BREAK -case 99: +case 100: YY_RULE_SETUP -#line 779 "wcsulex.l" +#line 807 "wcsulex.l" { - /* Tesla. */ + // Tesla. types[WCSUNITS_MASS] += 1.0; types[WCSUNITS_TIME] += 1.0; types[WCSUNITS_CHARGE] -= 1.0; BEGIN(EXPON); } YY_BREAK -case 100: +case 101: +YY_RULE_SETUP +#line 815 "wcsulex.l" +{ + // Turn. + factor *= 360.0; + types[WCSUNITS_PLANE_ANGLE] += 1.0; + BEGIN(EXPON); + } + YY_BREAK +case 102: YY_RULE_SETUP -#line 787 "wcsulex.l" +#line 822 "wcsulex.l" { - /* Unified atomic mass unit. */ + // Unified atomic mass unit. factor *= 1.6605387e-27; types[WCSUNITS_MASS] += 1.0; BEGIN(EXPON); } YY_BREAK -case 101: +case 103: YY_RULE_SETUP -#line 794 "wcsulex.l" +#line 829 "wcsulex.l" { - /* Volt. */ + // Volt. types[WCSUNITS_MASS] += 1.0; types[WCSUNITS_LENGTH] += 1.0; types[WCSUNITS_TIME] -= 2.0; @@ -8179,31 +8555,31 @@ YY_RULE_SETUP BEGIN(EXPON); } YY_BREAK -case 102: +case 104: YY_RULE_SETUP -#line 803 "wcsulex.l" +#line 838 "wcsulex.l" { - /* Voxel. */ + // Voxel. types[WCSUNITS_VOXEL] += 1.0; BEGIN(EXPON); } YY_BREAK -case 103: +case 105: YY_RULE_SETUP -#line 809 "wcsulex.l" +#line 844 "wcsulex.l" { - /* Watt. */ + // Watt. types[WCSUNITS_MASS] += 1.0; types[WCSUNITS_LENGTH] += 2.0; types[WCSUNITS_TIME] -= 3.0; BEGIN(EXPON); } YY_BREAK -case 104: +case 106: YY_RULE_SETUP -#line 817 "wcsulex.l" +#line 852 "wcsulex.l" { - /* Weber. */ + // Weber. types[WCSUNITS_MASS] += 1.0; types[WCSUNITS_LENGTH] += 2.0; types[WCSUNITS_TIME] += 1.0; @@ -8211,76 +8587,81 @@ YY_RULE_SETUP BEGIN(EXPON); } YY_BREAK -case 105: +case 107: YY_RULE_SETUP -#line 826 "wcsulex.l" +#line 861 "wcsulex.l" { - /* Internal parser error. */ + // Internal parser error. status = wcserr_set(WCSERR_SET(UNITSERR_PARSER_ERROR), "Internal units parser error parsing '%s'", unitstr); BEGIN(FLUSH); } YY_BREAK -case 106: +case 108: YY_RULE_SETUP -#line 833 "wcsulex.l" +#line 868 "wcsulex.l" { - /* Exponentiation. */ + // Exponentiation. if (operator++) { BEGIN(FLUSH); } } YY_BREAK -case 107: +case 109: YY_RULE_SETUP -#line 840 "wcsulex.l" +#line 875 "wcsulex.l" { - sscanf(wcsulextext, " %d", &i); + int i; + sscanf(yytext, " %d", &i); expon *= (double)i; add(&factor, types, &expon, scale, units); operator = 0; BEGIN(INITIAL); } YY_BREAK -case 108: +case 110: YY_RULE_SETUP -#line 848 "wcsulex.l" +#line 884 "wcsulex.l" { - sscanf(wcsulextext, " (%d)", &i); + int i; + sscanf(yytext, " (%d)", &i); expon *= (double)i; add(&factor, types, &expon, scale, units); operator = 0; BEGIN(INITIAL); } YY_BREAK -case 109: +case 111: YY_RULE_SETUP -#line 856 "wcsulex.l" +#line 893 "wcsulex.l" { - sscanf(wcsulextext, " (%d/%d)", &i, &j); + int i, j; + sscanf(yytext, " (%d/%d)", &i, &j); expon *= (double)i / (double)j; add(&factor, types, &expon, scale, units); operator = 0; BEGIN(INITIAL); } YY_BREAK -case 110: +case 112: YY_RULE_SETUP -#line 864 "wcsulex.l" +#line 902 "wcsulex.l" { - sscanf(wcsulextext, " (%s)", ctmp); - wcsutil_str2double(ctmp, "%lf", &dexp); + char ctmp[72]; + sscanf(yytext, " (%s)", ctmp); + double dexp; + wcsutil_str2double(ctmp, &dexp); expon *= dexp; add(&factor, types, &expon, scale, units); operator = 0; BEGIN(INITIAL); } YY_BREAK -case 111: +case 113: YY_RULE_SETUP -#line 873 "wcsulex.l" +#line 913 "wcsulex.l" { - /* Multiply. */ + // Multiply. if (operator++) { BEGIN(FLUSH); } else { @@ -8289,11 +8670,11 @@ YY_RULE_SETUP } } YY_BREAK -case 112: +case 114: YY_RULE_SETUP -#line 883 "wcsulex.l" +#line 923 "wcsulex.l" { - /* Multiply. */ + // Multiply. if (operator) { BEGIN(FLUSH); } else { @@ -8303,11 +8684,11 @@ YY_RULE_SETUP } } YY_BREAK -case 113: +case 115: YY_RULE_SETUP -#line 894 "wcsulex.l" +#line 934 "wcsulex.l" { - /* Multiply. */ + // Multiply. if (operator) { BEGIN(FLUSH); } else { @@ -8316,11 +8697,11 @@ YY_RULE_SETUP } } YY_BREAK -case 114: +case 116: YY_RULE_SETUP -#line 904 "wcsulex.l" +#line 944 "wcsulex.l" { - /* Divide. */ + // Divide. if (operator++) { BEGIN(FLUSH); } else { @@ -8330,29 +8711,29 @@ YY_RULE_SETUP } } YY_BREAK -case 115: +case 117: YY_RULE_SETUP -#line 915 "wcsulex.l" +#line 955 "wcsulex.l" { add(&factor, types, &expon, scale, units); bracket = !bracket; BEGIN(FLUSH); } YY_BREAK -case 116: +case 118: YY_RULE_SETUP -#line 921 "wcsulex.l" +#line 961 "wcsulex.l" { status = wcserr_set(WCSERR_SET(UNITSERR_BAD_EXPON_SYMBOL), "Invalid symbol in EXPON context in '%s'", unitstr); BEGIN(FLUSH); } YY_BREAK -case 117: +case 119: YY_RULE_SETUP -#line 927 "wcsulex.l" +#line 967 "wcsulex.l" { - /* Discard any remaining input. */ + // Discard any remaining input. } YY_BREAK case YY_STATE_EOF(INITIAL): @@ -8361,15 +8742,13 @@ case YY_STATE_EOF(PREFIX): case YY_STATE_EOF(UNITS): case YY_STATE_EOF(EXPON): case YY_STATE_EOF(FLUSH): -#line 931 "wcsulex.l" +#line 971 "wcsulex.l" { - /* End-of-string. */ + // End-of-string. if (YY_START == EXPON) { add(&factor, types, &expon, scale, units); } - wcsulexlex_destroy(); - if (bracket) { status = wcserr_set(WCSERR_SET(UNITSERR_UNBAL_BRACKET), "Unbalanced bracket in '%s'", unitstr); @@ -8389,7 +8768,7 @@ case YY_STATE_EOF(FLUSH): } if (status) { - for (i = 0; i < WCSUNITS_NTYPE; i++) { + for (int i = 0; i < WCSUNITS_NTYPE; i++) { units[i] = 0.0; *scale = 0.0; } @@ -8398,35 +8777,35 @@ case YY_STATE_EOF(FLUSH): return status; } YY_BREAK -case 118: +case 120: YY_RULE_SETUP -#line 967 "wcsulex.l" +#line 1005 "wcsulex.l" ECHO; YY_BREAK -#line 8407 "wcsulex.c" +#line 8786 "wcsulex.c" case YY_END_OF_BUFFER: { /* Amount of text matched not including the EOB char. */ - int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1; + int yy_amount_of_matched_text = (int) (yy_cp - yyg->yytext_ptr) - 1; /* Undo the effects of YY_DO_BEFORE_ACTION. */ - *yy_cp = (yy_hold_char); + *yy_cp = yyg->yy_hold_char; YY_RESTORE_YY_MORE_OFFSET if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) { /* We're scanning a new file or input source. It's * possible that this happened because the user - * just pointed wcsulexin at a new source and called - * wcsulexlex(). If so, then we have to assure + * just pointed yyin at a new source and called + * yylex(). If so, then we have to assure * consistency between YY_CURRENT_BUFFER and our * globals. Here is the right place to do so, because * this is the first action (other than possibly a * back-up) that will match for the new input source. */ - (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; - YY_CURRENT_BUFFER_LVALUE->yy_input_file = wcsulexin; + yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin; YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; } @@ -8437,13 +8816,13 @@ ECHO; * end-of-buffer state). Contrast this with the test * in input(). */ - if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) + if ( yyg->yy_c_buf_p <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) { /* This was really a NUL. */ yy_state_type yy_next_state; - (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text; + yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text; - yy_current_state = yy_get_previous_state( ); + yy_current_state = yy_get_previous_state( yyscanner ); /* Okay, we're now positioned to make the NUL * transition. We couldn't have @@ -8454,43 +8833,43 @@ ECHO; * will run more slowly). */ - yy_next_state = yy_try_NUL_trans( yy_current_state ); + yy_next_state = yy_try_NUL_trans( yy_current_state , yyscanner); - yy_bp = (yytext_ptr) + YY_MORE_ADJ; + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; if ( yy_next_state ) { /* Consume the NUL. */ - yy_cp = ++(yy_c_buf_p); + yy_cp = ++yyg->yy_c_buf_p; yy_current_state = yy_next_state; goto yy_match; } else { - yy_cp = (yy_c_buf_p); + yy_cp = yyg->yy_c_buf_p; goto yy_find_action; } } - else switch ( yy_get_next_buffer( ) ) + else switch ( yy_get_next_buffer( yyscanner ) ) { case EOB_ACT_END_OF_FILE: { - (yy_did_buffer_switch_on_eof) = 0; + yyg->yy_did_buffer_switch_on_eof = 0; - if ( wcsulexwrap( ) ) + if ( yywrap( yyscanner ) ) { /* Note: because we've taken care in * yy_get_next_buffer() to have set up - * wcsulextext, we can now set up + * yytext, we can now set up * yy_c_buf_p so that if some total * hoser (like flex itself) wants to * call the scanner after we return the * YY_NULL, it'll still work - another * YY_NULL will get returned. */ - (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ; + yyg->yy_c_buf_p = yyg->yytext_ptr + YY_MORE_ADJ; yy_act = YY_STATE_EOF(YY_START); goto do_action; @@ -8498,30 +8877,30 @@ ECHO; else { - if ( ! (yy_did_buffer_switch_on_eof) ) + if ( ! yyg->yy_did_buffer_switch_on_eof ) YY_NEW_FILE; } break; } case EOB_ACT_CONTINUE_SCAN: - (yy_c_buf_p) = - (yytext_ptr) + yy_amount_of_matched_text; + yyg->yy_c_buf_p = + yyg->yytext_ptr + yy_amount_of_matched_text; - yy_current_state = yy_get_previous_state( ); + yy_current_state = yy_get_previous_state( yyscanner ); - yy_cp = (yy_c_buf_p); - yy_bp = (yytext_ptr) + YY_MORE_ADJ; + yy_cp = yyg->yy_c_buf_p; + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; goto yy_match; case EOB_ACT_LAST_MATCH: - (yy_c_buf_p) = - &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)]; + yyg->yy_c_buf_p = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars]; - yy_current_state = yy_get_previous_state( ); + yy_current_state = yy_get_previous_state( yyscanner ); - yy_cp = (yy_c_buf_p); - yy_bp = (yytext_ptr) + YY_MORE_ADJ; + yy_cp = yyg->yy_c_buf_p; + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; goto yy_find_action; } break; @@ -8532,7 +8911,8 @@ ECHO; "fatal flex scanner internal error--no action found" ); } /* end of action switch */ } /* end of scanning one token */ -} /* end of wcsulexlex */ + } /* end of user's declarations */ +} /* end of yylex */ /* yy_get_next_buffer - try to read in a new buffer * @@ -8541,20 +8921,21 @@ ECHO; * EOB_ACT_CONTINUE_SCAN - continue scanning from current position * EOB_ACT_END_OF_FILE - end of file */ -static int yy_get_next_buffer (void) +static int yy_get_next_buffer (yyscan_t yyscanner) { - register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; - register char *source = (yytext_ptr); - register int number_to_move, i; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; + char *source = yyg->yytext_ptr; + int number_to_move, i; int ret_val; - if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] ) + if ( yyg->yy_c_buf_p > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] ) YY_FATAL_ERROR( "fatal flex scanner internal error--end of buffer missed" ); if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) { /* Don't try to fill the buffer, so this is an EOF. */ - if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 ) + if ( yyg->yy_c_buf_p - yyg->yytext_ptr - YY_MORE_ADJ == 1 ) { /* We matched a single character, the EOB, so * treat this as a final EOF. @@ -8574,7 +8955,7 @@ static int yy_get_next_buffer (void) /* Try to read more data. */ /* First move last chars to start of buffer. */ - number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr)) - 1; + number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr - 1); for ( i = 0; i < number_to_move; ++i ) *(dest++) = *(source++); @@ -8583,7 +8964,7 @@ static int yy_get_next_buffer (void) /* don't do the read, it's not guaranteed to return an EOF, * just force an EOF */ - YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0; + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars = 0; else { @@ -8594,10 +8975,10 @@ static int yy_get_next_buffer (void) { /* Not enough room in the buffer - grow it. */ /* just a shorter name for the current buffer */ - YY_BUFFER_STATE b = YY_CURRENT_BUFFER; + YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE; int yy_c_buf_p_offset = - (int) ((yy_c_buf_p) - b->yy_ch_buf); + (int) (yyg->yy_c_buf_p - b->yy_ch_buf); if ( b->yy_is_our_buffer ) { @@ -8610,17 +8991,18 @@ static int yy_get_next_buffer (void) b->yy_ch_buf = (char *) /* Include room in for 2 EOB chars. */ - wcsulexrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 ); + yyrealloc( (void *) b->yy_ch_buf, + (yy_size_t) (b->yy_buf_size + 2) , yyscanner ); } else /* Can't grow it, we don't own it. */ - b->yy_ch_buf = 0; + b->yy_ch_buf = NULL; if ( ! b->yy_ch_buf ) YY_FATAL_ERROR( "fatal error - scanner input buffer overflow" ); - (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset]; + yyg->yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset]; num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; @@ -8632,17 +9014,17 @@ static int yy_get_next_buffer (void) /* Read in more data. */ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), - (yy_n_chars), (size_t) num_to_read ); + yyg->yy_n_chars, num_to_read ); - YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; } - if ( (yy_n_chars) == 0 ) + if ( yyg->yy_n_chars == 0 ) { if ( number_to_move == YY_MORE_ADJ ) { ret_val = EOB_ACT_END_OF_FILE; - wcsulexrestart(wcsulexin ); + yyrestart( yyin , yyscanner); } else @@ -8656,34 +9038,38 @@ static int yy_get_next_buffer (void) else ret_val = EOB_ACT_CONTINUE_SCAN; - if ((yy_size_t) ((yy_n_chars) + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { + if ((yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { /* Extend the array by 50%, plus the number we really need. */ - yy_size_t new_size = (yy_n_chars) + number_to_move + ((yy_n_chars) >> 1); - YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) wcsulexrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size ); + int new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1); + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc( + (void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf, (yy_size_t) new_size , yyscanner ); if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); + /* "- 2" to take care of EOB's */ + YY_CURRENT_BUFFER_LVALUE->yy_buf_size = (int) (new_size - 2); } - (yy_n_chars) += number_to_move; - YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR; - YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR; + yyg->yy_n_chars += number_to_move; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] = YY_END_OF_BUFFER_CHAR; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR; - (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; + yyg->yytext_ptr = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; return ret_val; } /* yy_get_previous_state - get the state just before the EOB char was reached */ - static yy_state_type yy_get_previous_state (void) + static yy_state_type yy_get_previous_state (yyscan_t yyscanner) { - register yy_state_type yy_current_state; - register char *yy_cp; - - yy_current_state = (yy_start); + yy_state_type yy_current_state; + char *yy_cp; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + yy_current_state = yyg->yy_start; yy_current_state += YY_AT_BOL(); - for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp ) + for ( yy_cp = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp ) { if ( *yy_cp ) { @@ -8693,8 +9079,8 @@ static int yy_get_next_buffer (void) yy_current_state = yy_NUL_trans[yy_current_state]; if ( yy_accept[yy_current_state] ) { - (yy_last_accepting_state) = yy_current_state; - (yy_last_accepting_cpos) = yy_cp; + yyg->yy_last_accepting_state = yy_current_state; + yyg->yy_last_accepting_cpos = yy_cp; } } @@ -8706,10 +9092,11 @@ static int yy_get_next_buffer (void) * synopsis * next_state = yy_try_NUL_trans( current_state ); */ - static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state ) + static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state , yyscan_t yyscanner) { - register int yy_is_jam; - register char *yy_cp = (yy_c_buf_p); + int yy_is_jam; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */ + char *yy_cp = yyg->yy_c_buf_p; yy_current_state = yy_NUL_trans[yy_current_state]; yy_is_jam = (yy_current_state == 0); @@ -8718,30 +9105,34 @@ static int yy_get_next_buffer (void) { if ( yy_accept[yy_current_state] ) { - (yy_last_accepting_state) = yy_current_state; - (yy_last_accepting_cpos) = yy_cp; + yyg->yy_last_accepting_state = yy_current_state; + yyg->yy_last_accepting_cpos = yy_cp; } } + (void)yyg; return yy_is_jam ? 0 : yy_current_state; } - static void yyunput (int c, register char * yy_bp ) +#ifndef YY_NO_UNPUT + + static void yyunput (int c, char * yy_bp , yyscan_t yyscanner) { - register char *yy_cp; - - yy_cp = (yy_c_buf_p); + char *yy_cp; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + yy_cp = yyg->yy_c_buf_p; - /* undo effects of setting up wcsulextext */ - *yy_cp = (yy_hold_char); + /* undo effects of setting up yytext */ + *yy_cp = yyg->yy_hold_char; if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) { /* need to shift things up to make room */ /* +2 for EOB chars. */ - register int number_to_move = (yy_n_chars) + 2; - register char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[ + int number_to_move = yyg->yy_n_chars + 2; + char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[ YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2]; - register char *source = + char *source = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]; while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) @@ -8750,7 +9141,7 @@ static int yy_get_next_buffer (void) yy_cp += (int) (dest - source); yy_bp += (int) (dest - source); YY_CURRENT_BUFFER_LVALUE->yy_n_chars = - (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_buf_size; + yyg->yy_n_chars = (int) YY_CURRENT_BUFFER_LVALUE->yy_buf_size; if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) YY_FATAL_ERROR( "flex scanner push-back overflow" ); @@ -8758,39 +9149,42 @@ static int yy_get_next_buffer (void) *--yy_cp = (char) c; - (yytext_ptr) = yy_bp; - (yy_hold_char) = *yy_cp; - (yy_c_buf_p) = yy_cp; + yyg->yytext_ptr = yy_bp; + yyg->yy_hold_char = *yy_cp; + yyg->yy_c_buf_p = yy_cp; } +#endif + #ifndef YY_NO_INPUT #ifdef __cplusplus - static int yyinput (void) + static int yyinput (yyscan_t yyscanner) #else - static int input (void) + static int input (yyscan_t yyscanner) #endif { int c; - - *(yy_c_buf_p) = (yy_hold_char); + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; - if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR ) + *yyg->yy_c_buf_p = yyg->yy_hold_char; + + if ( *yyg->yy_c_buf_p == YY_END_OF_BUFFER_CHAR ) { /* yy_c_buf_p now points to the character we want to return. * If this occurs *before* the EOB characters, then it's a * valid NUL; if not, then we've hit the end of the buffer. */ - if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) + if ( yyg->yy_c_buf_p < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) /* This was really a NUL. */ - *(yy_c_buf_p) = '\0'; + *yyg->yy_c_buf_p = '\0'; else { /* need more input */ - int offset = (yy_c_buf_p) - (yytext_ptr); - ++(yy_c_buf_p); + int offset = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr); + ++yyg->yy_c_buf_p; - switch ( yy_get_next_buffer( ) ) + switch ( yy_get_next_buffer( yyscanner ) ) { case EOB_ACT_LAST_MATCH: /* This happens because yy_g_n_b() @@ -8804,34 +9198,34 @@ static int yy_get_next_buffer (void) */ /* Reset buffer status. */ - wcsulexrestart(wcsulexin ); + yyrestart( yyin , yyscanner); /*FALLTHROUGH*/ case EOB_ACT_END_OF_FILE: { - if ( wcsulexwrap( ) ) - return EOF; + if ( yywrap( yyscanner ) ) + return 0; - if ( ! (yy_did_buffer_switch_on_eof) ) + if ( ! yyg->yy_did_buffer_switch_on_eof ) YY_NEW_FILE; #ifdef __cplusplus - return yyinput(); + return yyinput(yyscanner); #else - return input(); + return input(yyscanner); #endif } case EOB_ACT_CONTINUE_SCAN: - (yy_c_buf_p) = (yytext_ptr) + offset; + yyg->yy_c_buf_p = yyg->yytext_ptr + offset; break; } } } - c = *(unsigned char *) (yy_c_buf_p); /* cast for 8-bit char's */ - *(yy_c_buf_p) = '\0'; /* preserve wcsulextext */ - (yy_hold_char) = *++(yy_c_buf_p); + c = *(unsigned char *) yyg->yy_c_buf_p; /* cast for 8-bit char's */ + *yyg->yy_c_buf_p = '\0'; /* preserve yytext */ + yyg->yy_hold_char = *++yyg->yy_c_buf_p; YY_CURRENT_BUFFER_LVALUE->yy_at_bol = (c == '\n'); @@ -8841,102 +9235,106 @@ static int yy_get_next_buffer (void) /** Immediately switch to a different input stream. * @param input_file A readable stream. - * + * @param yyscanner The scanner object. * @note This function does not reset the start condition to @c INITIAL . */ - void wcsulexrestart (FILE * input_file ) + void yyrestart (FILE * input_file , yyscan_t yyscanner) { - + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if ( ! YY_CURRENT_BUFFER ){ - wcsulexensure_buffer_stack (); + yyensure_buffer_stack (yyscanner); YY_CURRENT_BUFFER_LVALUE = - wcsulex_create_buffer(wcsulexin,YY_BUF_SIZE ); + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); } - wcsulex_init_buffer(YY_CURRENT_BUFFER,input_file ); - wcsulex_load_buffer_state( ); + yy_init_buffer( YY_CURRENT_BUFFER, input_file , yyscanner); + yy_load_buffer_state( yyscanner ); } /** Switch to a different input buffer. * @param new_buffer The new input buffer. - * + * @param yyscanner The scanner object. */ - void wcsulex_switch_to_buffer (YY_BUFFER_STATE new_buffer ) + void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) { - + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + /* TODO. We should be able to replace this entire function body * with - * wcsulexpop_buffer_state(); - * wcsulexpush_buffer_state(new_buffer); + * yypop_buffer_state(); + * yypush_buffer_state(new_buffer); */ - wcsulexensure_buffer_stack (); + yyensure_buffer_stack (yyscanner); if ( YY_CURRENT_BUFFER == new_buffer ) return; if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ - *(yy_c_buf_p) = (yy_hold_char); - YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); - YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + *yyg->yy_c_buf_p = yyg->yy_hold_char; + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; } YY_CURRENT_BUFFER_LVALUE = new_buffer; - wcsulex_load_buffer_state( ); + yy_load_buffer_state( yyscanner ); /* We don't actually know whether we did this switch during - * EOF (wcsulexwrap()) processing, but the only time this flag - * is looked at is after wcsulexwrap() is called, so it's safe + * EOF (yywrap()) processing, but the only time this flag + * is looked at is after yywrap() is called, so it's safe * to go ahead and always set it. */ - (yy_did_buffer_switch_on_eof) = 1; + yyg->yy_did_buffer_switch_on_eof = 1; } -static void wcsulex_load_buffer_state (void) +static void yy_load_buffer_state (yyscan_t yyscanner) { - (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; - (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; - wcsulexin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; - (yy_hold_char) = *(yy_c_buf_p); + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + yyg->yytext_ptr = yyg->yy_c_buf_p = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; + yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; + yyg->yy_hold_char = *yyg->yy_c_buf_p; } /** Allocate and initialize an input buffer state. * @param file A readable stream. * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. - * + * @param yyscanner The scanner object. * @return the allocated buffer state. */ - YY_BUFFER_STATE wcsulex_create_buffer (FILE * file, int size ) + YY_BUFFER_STATE yy_create_buffer (FILE * file, int size , yyscan_t yyscanner) { YY_BUFFER_STATE b; - b = (YY_BUFFER_STATE) wcsulexalloc(sizeof( struct yy_buffer_state ) ); + b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner ); if ( ! b ) - YY_FATAL_ERROR( "out of dynamic memory in wcsulex_create_buffer()" ); + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); b->yy_buf_size = size; /* yy_ch_buf has to be 2 characters longer than the size given because * we need to put in 2 end-of-buffer characters. */ - b->yy_ch_buf = (char *) wcsulexalloc(b->yy_buf_size + 2 ); + b->yy_ch_buf = (char *) yyalloc( (yy_size_t) (b->yy_buf_size + 2) , yyscanner ); if ( ! b->yy_ch_buf ) - YY_FATAL_ERROR( "out of dynamic memory in wcsulex_create_buffer()" ); + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); b->yy_is_our_buffer = 1; - wcsulex_init_buffer(b,file ); + yy_init_buffer( b, file , yyscanner); return b; } /** Destroy the buffer. - * @param b a buffer created with wcsulex_create_buffer() - * + * @param b a buffer created with yy_create_buffer() + * @param yyscanner The scanner object. */ - void wcsulex_delete_buffer (YY_BUFFER_STATE b ) + void yy_delete_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) { - + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if ( ! b ) return; @@ -8944,27 +9342,28 @@ static void wcsulex_load_buffer_state (void) YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; if ( b->yy_is_our_buffer ) - wcsulexfree((void *) b->yy_ch_buf ); + yyfree( (void *) b->yy_ch_buf , yyscanner ); - wcsulexfree((void *) b ); + yyfree( (void *) b , yyscanner ); } /* Initializes or reinitializes a buffer. * This function is sometimes called more than once on the same buffer, - * such as during a wcsulexrestart() or at EOF. + * such as during a yyrestart() or at EOF. */ - static void wcsulex_init_buffer (YY_BUFFER_STATE b, FILE * file ) + static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file , yyscan_t yyscanner) { int oerrno = errno; - - wcsulex_flush_buffer(b ); + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + yy_flush_buffer( b , yyscanner); b->yy_input_file = file; b->yy_fill_buffer = 1; - /* If b is the current buffer, then wcsulex_init_buffer was _probably_ - * called from wcsulexrestart() or through yy_get_next_buffer. + /* If b is the current buffer, then yy_init_buffer was _probably_ + * called from yyrestart() or through yy_get_next_buffer. * In that case, we don't want to reset the lineno or column. */ if (b != YY_CURRENT_BUFFER){ @@ -8979,11 +9378,12 @@ static void wcsulex_load_buffer_state (void) /** Discard all buffered characters. On the next scan, YY_INPUT will be called. * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. - * + * @param yyscanner The scanner object. */ - void wcsulex_flush_buffer (YY_BUFFER_STATE b ) + void yy_flush_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) { - if ( ! b ) + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if ( ! b ) return; b->yy_n_chars = 0; @@ -9001,114 +9401,117 @@ static void wcsulex_load_buffer_state (void) b->yy_buffer_status = YY_BUFFER_NEW; if ( b == YY_CURRENT_BUFFER ) - wcsulex_load_buffer_state( ); + yy_load_buffer_state( yyscanner ); } /** Pushes the new state onto the stack. The new state becomes * the current state. This function will allocate the stack * if necessary. * @param new_buffer The new state. - * + * @param yyscanner The scanner object. */ -void wcsulexpush_buffer_state (YY_BUFFER_STATE new_buffer ) +void yypush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) { - if (new_buffer == NULL) + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if (new_buffer == NULL) return; - wcsulexensure_buffer_stack(); + yyensure_buffer_stack(yyscanner); - /* This block is copied from wcsulex_switch_to_buffer. */ + /* This block is copied from yy_switch_to_buffer. */ if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ - *(yy_c_buf_p) = (yy_hold_char); - YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); - YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + *yyg->yy_c_buf_p = yyg->yy_hold_char; + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; } /* Only push if top exists. Otherwise, replace top. */ if (YY_CURRENT_BUFFER) - (yy_buffer_stack_top)++; + yyg->yy_buffer_stack_top++; YY_CURRENT_BUFFER_LVALUE = new_buffer; - /* copied from wcsulex_switch_to_buffer. */ - wcsulex_load_buffer_state( ); - (yy_did_buffer_switch_on_eof) = 1; + /* copied from yy_switch_to_buffer. */ + yy_load_buffer_state( yyscanner ); + yyg->yy_did_buffer_switch_on_eof = 1; } /** Removes and deletes the top of the stack, if present. * The next element becomes the new top. - * + * @param yyscanner The scanner object. */ -void wcsulexpop_buffer_state (void) +void yypop_buffer_state (yyscan_t yyscanner) { - if (!YY_CURRENT_BUFFER) + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if (!YY_CURRENT_BUFFER) return; - wcsulex_delete_buffer(YY_CURRENT_BUFFER ); + yy_delete_buffer(YY_CURRENT_BUFFER , yyscanner); YY_CURRENT_BUFFER_LVALUE = NULL; - if ((yy_buffer_stack_top) > 0) - --(yy_buffer_stack_top); + if (yyg->yy_buffer_stack_top > 0) + --yyg->yy_buffer_stack_top; if (YY_CURRENT_BUFFER) { - wcsulex_load_buffer_state( ); - (yy_did_buffer_switch_on_eof) = 1; + yy_load_buffer_state( yyscanner ); + yyg->yy_did_buffer_switch_on_eof = 1; } } /* Allocates the stack if it does not exist. * Guarantees space for at least one push. */ -static void wcsulexensure_buffer_stack (void) +static void yyensure_buffer_stack (yyscan_t yyscanner) { - int num_to_alloc; - - if (!(yy_buffer_stack)) { + yy_size_t num_to_alloc; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (!yyg->yy_buffer_stack) { /* First allocation is just for 2 elements, since we don't know if this * scanner will even need a stack. We use 2 instead of 1 to avoid an * immediate realloc on the next call. */ - num_to_alloc = 1; - (yy_buffer_stack) = (struct yy_buffer_state**)wcsulexalloc + num_to_alloc = 1; /* After all that talk, this was set to 1 anyways... */ + yyg->yy_buffer_stack = (struct yy_buffer_state**)yyalloc (num_to_alloc * sizeof(struct yy_buffer_state*) - ); - if ( ! (yy_buffer_stack) ) - YY_FATAL_ERROR( "out of dynamic memory in wcsulexensure_buffer_stack()" ); - - memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*)); - - (yy_buffer_stack_max) = num_to_alloc; - (yy_buffer_stack_top) = 0; + , yyscanner); + if ( ! yyg->yy_buffer_stack ) + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); + + memset(yyg->yy_buffer_stack, 0, num_to_alloc * sizeof(struct yy_buffer_state*)); + + yyg->yy_buffer_stack_max = num_to_alloc; + yyg->yy_buffer_stack_top = 0; return; } - if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){ + if (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){ /* Increase the buffer to prepare for a possible push. */ - int grow_size = 8 /* arbitrary grow size */; + yy_size_t grow_size = 8 /* arbitrary grow size */; - num_to_alloc = (yy_buffer_stack_max) + grow_size; - (yy_buffer_stack) = (struct yy_buffer_state**)wcsulexrealloc - ((yy_buffer_stack), + num_to_alloc = yyg->yy_buffer_stack_max + grow_size; + yyg->yy_buffer_stack = (struct yy_buffer_state**)yyrealloc + (yyg->yy_buffer_stack, num_to_alloc * sizeof(struct yy_buffer_state*) - ); - if ( ! (yy_buffer_stack) ) - YY_FATAL_ERROR( "out of dynamic memory in wcsulexensure_buffer_stack()" ); + , yyscanner); + if ( ! yyg->yy_buffer_stack ) + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); /* zero only the new slots.*/ - memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*)); - (yy_buffer_stack_max) = num_to_alloc; + memset(yyg->yy_buffer_stack + yyg->yy_buffer_stack_max, 0, grow_size * sizeof(struct yy_buffer_state*)); + yyg->yy_buffer_stack_max = num_to_alloc; } } /** Setup the input buffer state to scan directly from a user-specified character buffer. * @param base the character buffer * @param size the size in bytes of the character buffer - * - * @return the newly allocated buffer state object. + * @param yyscanner The scanner object. + * @return the newly allocated buffer state object. */ -YY_BUFFER_STATE wcsulex_scan_buffer (char * base, yy_size_t size ) +YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size , yyscan_t yyscanner) { YY_BUFFER_STATE b; @@ -9116,49 +9519,49 @@ YY_BUFFER_STATE wcsulex_scan_buffer (char * base, yy_size_t size ) base[size-2] != YY_END_OF_BUFFER_CHAR || base[size-1] != YY_END_OF_BUFFER_CHAR ) /* They forgot to leave room for the EOB's. */ - return 0; + return NULL; - b = (YY_BUFFER_STATE) wcsulexalloc(sizeof( struct yy_buffer_state ) ); + b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner ); if ( ! b ) - YY_FATAL_ERROR( "out of dynamic memory in wcsulex_scan_buffer()" ); + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); - b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */ + b->yy_buf_size = (int) (size - 2); /* "- 2" to take care of EOB's */ b->yy_buf_pos = b->yy_ch_buf = base; b->yy_is_our_buffer = 0; - b->yy_input_file = 0; + b->yy_input_file = NULL; b->yy_n_chars = b->yy_buf_size; b->yy_is_interactive = 0; b->yy_at_bol = 1; b->yy_fill_buffer = 0; b->yy_buffer_status = YY_BUFFER_NEW; - wcsulex_switch_to_buffer(b ); + yy_switch_to_buffer( b , yyscanner ); return b; } -/** Setup the input buffer state to scan a string. The next call to wcsulexlex() will +/** Setup the input buffer state to scan a string. The next call to yylex() will * scan from a @e copy of @a str. * @param yystr a NUL-terminated string to scan - * + * @param yyscanner The scanner object. * @return the newly allocated buffer state object. * @note If you want to scan bytes that may contain NUL values, then use - * wcsulex_scan_bytes() instead. + * yy_scan_bytes() instead. */ -YY_BUFFER_STATE wcsulex_scan_string (yyconst char * yystr ) +YY_BUFFER_STATE yy_scan_string (const char * yystr , yyscan_t yyscanner) { - return wcsulex_scan_bytes(yystr,strlen(yystr) ); + return yy_scan_bytes( yystr, (int) strlen(yystr) , yyscanner); } -/** Setup the input buffer state to scan the given bytes. The next call to wcsulexlex() will +/** Setup the input buffer state to scan the given bytes. The next call to yylex() will * scan from a @e copy of @a bytes. * @param yybytes the byte buffer to scan * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes. - * + * @param yyscanner The scanner object. * @return the newly allocated buffer state object. */ -YY_BUFFER_STATE wcsulex_scan_bytes (yyconst char * yybytes, int _yybytes_len ) +YY_BUFFER_STATE yy_scan_bytes (const char * yybytes, int _yybytes_len , yyscan_t yyscanner) { YY_BUFFER_STATE b; char *buf; @@ -9166,19 +9569,19 @@ YY_BUFFER_STATE wcsulex_scan_bytes (yyconst char * yybytes, int _yybytes_len ) int i; /* Get memory for full buffer, including space for trailing EOB's. */ - n = _yybytes_len + 2; - buf = (char *) wcsulexalloc(n ); + n = (yy_size_t) (_yybytes_len + 2); + buf = (char *) yyalloc( n , yyscanner ); if ( ! buf ) - YY_FATAL_ERROR( "out of dynamic memory in wcsulex_scan_bytes()" ); + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" ); for ( i = 0; i < _yybytes_len; ++i ) buf[i] = yybytes[i]; buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; - b = wcsulex_scan_buffer(buf,n ); + b = yy_scan_buffer( buf, n , yyscanner); if ( ! b ) - YY_FATAL_ERROR( "bad buffer in wcsulex_scan_bytes()" ); + YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" ); /* It's okay to grow etc. this buffer, and we should throw it * away when we're done. @@ -9192,9 +9595,11 @@ YY_BUFFER_STATE wcsulex_scan_bytes (yyconst char * yybytes, int _yybytes_len ) #define YY_EXIT_FAILURE 2 #endif -static void yy_fatal_error (yyconst char* msg ) +static void yynoreturn yy_fatal_error (const char* msg , yyscan_t yyscanner) { - (void) fprintf( stderr, "%s\n", msg ); + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + fprintf( stderr, "%s\n", msg ); exit( YY_EXIT_FAILURE ); } @@ -9204,144 +9609,283 @@ static void yy_fatal_error (yyconst char* msg ) #define yyless(n) \ do \ { \ - /* Undo effects of setting up wcsulextext. */ \ + /* Undo effects of setting up yytext. */ \ int yyless_macro_arg = (n); \ YY_LESS_LINENO(yyless_macro_arg);\ - wcsulextext[wcsulexleng] = (yy_hold_char); \ - (yy_c_buf_p) = wcsulextext + yyless_macro_arg; \ - (yy_hold_char) = *(yy_c_buf_p); \ - *(yy_c_buf_p) = '\0'; \ - wcsulexleng = yyless_macro_arg; \ + yytext[yyleng] = yyg->yy_hold_char; \ + yyg->yy_c_buf_p = yytext + yyless_macro_arg; \ + yyg->yy_hold_char = *yyg->yy_c_buf_p; \ + *yyg->yy_c_buf_p = '\0'; \ + yyleng = yyless_macro_arg; \ } \ while ( 0 ) /* Accessor methods (get/set functions) to struct members. */ +/** Get the user-defined data for this scanner. + * @param yyscanner The scanner object. + */ +YY_EXTRA_TYPE yyget_extra (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyextra; +} + /** Get the current line number. - * + * @param yyscanner The scanner object. + */ +int yyget_lineno (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (! YY_CURRENT_BUFFER) + return 0; + + return yylineno; +} + +/** Get the current column number. + * @param yyscanner The scanner object. */ -int wcsulexget_lineno (void) +int yyget_column (yyscan_t yyscanner) { - - return wcsulexlineno; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (! YY_CURRENT_BUFFER) + return 0; + + return yycolumn; } /** Get the input stream. - * + * @param yyscanner The scanner object. */ -FILE *wcsulexget_in (void) +FILE *yyget_in (yyscan_t yyscanner) { - return wcsulexin; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyin; } /** Get the output stream. - * + * @param yyscanner The scanner object. */ -FILE *wcsulexget_out (void) +FILE *yyget_out (yyscan_t yyscanner) { - return wcsulexout; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyout; } /** Get the length of the current token. - * + * @param yyscanner The scanner object. */ -int wcsulexget_leng (void) +int yyget_leng (yyscan_t yyscanner) { - return wcsulexleng; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyleng; } /** Get the current token. - * + * @param yyscanner The scanner object. */ -char *wcsulexget_text (void) +char *yyget_text (yyscan_t yyscanner) { - return wcsulextext; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yytext; +} + +/** Set the user-defined data. This data is never touched by the scanner. + * @param user_defined The data to be associated with this scanner. + * @param yyscanner The scanner object. + */ +void yyset_extra (YY_EXTRA_TYPE user_defined , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyextra = user_defined ; } /** Set the current line number. - * @param line_number - * + * @param _line_number line number + * @param yyscanner The scanner object. + */ +void yyset_lineno (int _line_number , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* lineno is only valid if an input buffer exists. */ + if (! YY_CURRENT_BUFFER ) + YY_FATAL_ERROR( "yyset_lineno called with no buffer" ); + + yylineno = _line_number; +} + +/** Set the current column. + * @param _column_no column number + * @param yyscanner The scanner object. */ -void wcsulexset_lineno (int line_number ) +void yyset_column (int _column_no , yyscan_t yyscanner) { + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* column is only valid if an input buffer exists. */ + if (! YY_CURRENT_BUFFER ) + YY_FATAL_ERROR( "yyset_column called with no buffer" ); - wcsulexlineno = line_number; + yycolumn = _column_no; } /** Set the input stream. This does not discard the current * input buffer. - * @param in_str A readable stream. - * - * @see wcsulex_switch_to_buffer + * @param _in_str A readable stream. + * @param yyscanner The scanner object. + * @see yy_switch_to_buffer */ -void wcsulexset_in (FILE * in_str ) +void yyset_in (FILE * _in_str , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyin = _in_str ; +} + +void yyset_out (FILE * _out_str , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyout = _out_str ; +} + +int yyget_debug (yyscan_t yyscanner) { - wcsulexin = in_str ; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yy_flex_debug; } -void wcsulexset_out (FILE * out_str ) +void yyset_debug (int _bdebug , yyscan_t yyscanner) { - wcsulexout = out_str ; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yy_flex_debug = _bdebug ; } -int wcsulexget_debug (void) +/* Accessor methods for yylval and yylloc */ + +/* User-visible API */ + +/* yylex_init is special because it creates the scanner itself, so it is + * the ONLY reentrant function that doesn't take the scanner as the last argument. + * That's why we explicitly handle the declaration, instead of using our macros. + */ +int yylex_init(yyscan_t* ptr_yy_globals) { - return wcsulex_flex_debug; + if (ptr_yy_globals == NULL){ + errno = EINVAL; + return 1; + } + + *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), NULL ); + + if (*ptr_yy_globals == NULL){ + errno = ENOMEM; + return 1; + } + + /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */ + memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); + + return yy_init_globals ( *ptr_yy_globals ); } -void wcsulexset_debug (int bdebug ) +/* yylex_init_extra has the same functionality as yylex_init, but follows the + * convention of taking the scanner as the last argument. Note however, that + * this is a *pointer* to a scanner, as it will be allocated by this call (and + * is the reason, too, why this function also must handle its own declaration). + * The user defined value in the first argument will be available to yyalloc in + * the yyextra field. + */ +int yylex_init_extra( YY_EXTRA_TYPE yy_user_defined, yyscan_t* ptr_yy_globals ) { - wcsulex_flex_debug = bdebug ; + struct yyguts_t dummy_yyguts; + + yyset_extra (yy_user_defined, &dummy_yyguts); + + if (ptr_yy_globals == NULL){ + errno = EINVAL; + return 1; + } + + *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), &dummy_yyguts ); + + if (*ptr_yy_globals == NULL){ + errno = ENOMEM; + return 1; + } + + /* By setting to 0xAA, we expose bugs in + yy_init_globals. Leave at 0x00 for releases. */ + memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); + + yyset_extra (yy_user_defined, *ptr_yy_globals); + + return yy_init_globals ( *ptr_yy_globals ); } -static int yy_init_globals (void) +static int yy_init_globals (yyscan_t yyscanner) { - /* Initialization is the same as for the non-reentrant scanner. - * This function is called from wcsulexlex_destroy(), so don't allocate here. + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + /* Initialization is the same as for the non-reentrant scanner. + * This function is called from yylex_destroy(), so don't allocate here. */ - (yy_buffer_stack) = 0; - (yy_buffer_stack_top) = 0; - (yy_buffer_stack_max) = 0; - (yy_c_buf_p) = (char *) 0; - (yy_init) = 0; - (yy_start) = 0; + yyg->yy_buffer_stack = NULL; + yyg->yy_buffer_stack_top = 0; + yyg->yy_buffer_stack_max = 0; + yyg->yy_c_buf_p = NULL; + yyg->yy_init = 0; + yyg->yy_start = 0; + + yyg->yy_start_stack_ptr = 0; + yyg->yy_start_stack_depth = 0; + yyg->yy_start_stack = NULL; /* Defined in main.c */ #ifdef YY_STDINIT - wcsulexin = stdin; - wcsulexout = stdout; + yyin = stdin; + yyout = stdout; #else - wcsulexin = (FILE *) 0; - wcsulexout = (FILE *) 0; + yyin = NULL; + yyout = NULL; #endif /* For future reference: Set errno on error, since we are called by - * wcsulexlex_init() + * yylex_init() */ return 0; } -/* wcsulexlex_destroy is for both reentrant and non-reentrant scanners. */ -int wcsulexlex_destroy (void) +/* yylex_destroy is for both reentrant and non-reentrant scanners. */ +int yylex_destroy (yyscan_t yyscanner) { - + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + /* Pop the buffer stack, destroying each element. */ while(YY_CURRENT_BUFFER){ - wcsulex_delete_buffer(YY_CURRENT_BUFFER ); + yy_delete_buffer( YY_CURRENT_BUFFER , yyscanner ); YY_CURRENT_BUFFER_LVALUE = NULL; - wcsulexpop_buffer_state(); + yypop_buffer_state(yyscanner); } /* Destroy the stack itself. */ - wcsulexfree((yy_buffer_stack) ); - (yy_buffer_stack) = NULL; + yyfree(yyg->yy_buffer_stack , yyscanner); + yyg->yy_buffer_stack = NULL; + + /* Destroy the start condition stack. */ + yyfree( yyg->yy_start_stack , yyscanner ); + yyg->yy_start_stack = NULL; /* Reset the globals. This is important in a non-reentrant scanner so the next time - * wcsulexlex() is called, initialization will occur. */ - yy_init_globals( ); + * yylex() is called, initialization will occur. */ + yy_init_globals( yyscanner); + /* Destroy the main struct (reentrant only). */ + yyfree ( yyscanner , yyscanner ); + yyscanner = NULL; return 0; } @@ -9350,18 +9894,21 @@ int wcsulexlex_destroy (void) */ #ifndef yytext_ptr -static void yy_flex_strncpy (char* s1, yyconst char * s2, int n ) +static void yy_flex_strncpy (char* s1, const char * s2, int n , yyscan_t yyscanner) { - register int i; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + + int i; for ( i = 0; i < n; ++i ) s1[i] = s2[i]; } #endif #ifdef YY_NEED_STRLEN -static int yy_flex_strlen (yyconst char * s ) +static int yy_flex_strlen (const char * s , yyscan_t yyscanner) { - register int n; + int n; for ( n = 0; s[n]; ++n ) ; @@ -9369,13 +9916,18 @@ static int yy_flex_strlen (yyconst char * s ) } #endif -void *wcsulexalloc (yy_size_t size ) +void *yyalloc (yy_size_t size , yyscan_t yyscanner) { - return (void *) malloc( size ); + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + return malloc(size); } -void *wcsulexrealloc (void * ptr, yy_size_t size ) +void *yyrealloc (void * ptr, yy_size_t size , yyscan_t yyscanner) { + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + /* The cast to (char *) in the following accommodates both * implementations that use char* generic pointers, and those * that use void* generic pointers. It works with the latter @@ -9383,18 +9935,45 @@ void *wcsulexrealloc (void * ptr, yy_size_t size ) * any pointer type to void*, and deal with argument conversions * as though doing an assignment. */ - return (void *) realloc( (char *) ptr, size ); + return realloc(ptr, size); } -void wcsulexfree (void * ptr ) +void yyfree (void * ptr , yyscan_t yyscanner) { - free( (char *) ptr ); /* see wcsulexrealloc() for (char *) cast */ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + free( (char *) ptr ); /* see yyrealloc() for (char *) cast */ } #define YYTABLES_NAME "yytables" -#line 967 "wcsulex.l" +#line 1005 "wcsulex.l" + +/*---------------------------------------------------------------------------- +* External interface to the scanner. +*---------------------------------------------------------------------------*/ + +int wcsulexe( + const char unitstr[], + int *func, + double *scale, + double units[WCSUNITS_NTYPE], + struct wcserr **err) + +{ + // Function prototypes. + int yylex_init_extra(YY_EXTRA_TYPE extra, yyscan_t *yyscanner); + int yylex_destroy(yyscan_t yyscanner); + + struct wcsulex_extra extra; + yyscan_t yyscanner; + yylex_init_extra(&extra, &yyscanner); + int status = wcsulexe_scanner(unitstr, func, scale, units, err, yyscanner); + yylex_destroy(yyscanner); + + return status; +} /*---------------------------------------------------------------------------- @@ -9409,11 +9988,9 @@ void add( double units[]) { - int i; - *scale *= pow(*factor, *expon); - for (i = 0; i < WCSUNITS_NTYPE; i++) { + for (int i = 0; i < WCSUNITS_NTYPE; i++) { units[i] += *expon * types[i]; types[i] = 0.0; } diff --git a/cextern/wcslib/C/flexed/wcsutrn.c b/cextern/wcslib/C/flexed/wcsutrn.c index 39afef1f8313..90e4a2c3c0ff 100644 --- a/cextern/wcslib/C/flexed/wcsutrn.c +++ b/cextern/wcslib/C/flexed/wcsutrn.c @@ -2,35 +2,227 @@ #line 4 "wcsutrn.c" +#define _POSIX_C_SOURCE 1 #define YY_INT_ALIGNED short int /* A lexical scanner generated by flex */ +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 6 +#define YY_FLEX_SUBMINOR_VERSION 4 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +#ifdef yy_create_buffer +#define wcsutrn_create_buffer_ALREADY_DEFINED +#else #define yy_create_buffer wcsutrn_create_buffer +#endif + +#ifdef yy_delete_buffer +#define wcsutrn_delete_buffer_ALREADY_DEFINED +#else #define yy_delete_buffer wcsutrn_delete_buffer -#define yy_flex_debug wcsutrn_flex_debug +#endif + +#ifdef yy_scan_buffer +#define wcsutrn_scan_buffer_ALREADY_DEFINED +#else +#define yy_scan_buffer wcsutrn_scan_buffer +#endif + +#ifdef yy_scan_string +#define wcsutrn_scan_string_ALREADY_DEFINED +#else +#define yy_scan_string wcsutrn_scan_string +#endif + +#ifdef yy_scan_bytes +#define wcsutrn_scan_bytes_ALREADY_DEFINED +#else +#define yy_scan_bytes wcsutrn_scan_bytes +#endif + +#ifdef yy_init_buffer +#define wcsutrn_init_buffer_ALREADY_DEFINED +#else #define yy_init_buffer wcsutrn_init_buffer +#endif + +#ifdef yy_flush_buffer +#define wcsutrn_flush_buffer_ALREADY_DEFINED +#else #define yy_flush_buffer wcsutrn_flush_buffer +#endif + +#ifdef yy_load_buffer_state +#define wcsutrn_load_buffer_state_ALREADY_DEFINED +#else #define yy_load_buffer_state wcsutrn_load_buffer_state +#endif + +#ifdef yy_switch_to_buffer +#define wcsutrn_switch_to_buffer_ALREADY_DEFINED +#else #define yy_switch_to_buffer wcsutrn_switch_to_buffer -#define yyin wcsutrnin -#define yyleng wcsutrnleng +#endif + +#ifdef yypush_buffer_state +#define wcsutrnpush_buffer_state_ALREADY_DEFINED +#else +#define yypush_buffer_state wcsutrnpush_buffer_state +#endif + +#ifdef yypop_buffer_state +#define wcsutrnpop_buffer_state_ALREADY_DEFINED +#else +#define yypop_buffer_state wcsutrnpop_buffer_state +#endif + +#ifdef yyensure_buffer_stack +#define wcsutrnensure_buffer_stack_ALREADY_DEFINED +#else +#define yyensure_buffer_stack wcsutrnensure_buffer_stack +#endif + +#ifdef yylex +#define wcsutrnlex_ALREADY_DEFINED +#else #define yylex wcsutrnlex -#define yylineno wcsutrnlineno -#define yyout wcsutrnout +#endif + +#ifdef yyrestart +#define wcsutrnrestart_ALREADY_DEFINED +#else #define yyrestart wcsutrnrestart -#define yytext wcsutrntext +#endif + +#ifdef yylex_init +#define wcsutrnlex_init_ALREADY_DEFINED +#else +#define yylex_init wcsutrnlex_init +#endif + +#ifdef yylex_init_extra +#define wcsutrnlex_init_extra_ALREADY_DEFINED +#else +#define yylex_init_extra wcsutrnlex_init_extra +#endif + +#ifdef yylex_destroy +#define wcsutrnlex_destroy_ALREADY_DEFINED +#else +#define yylex_destroy wcsutrnlex_destroy +#endif + +#ifdef yyget_debug +#define wcsutrnget_debug_ALREADY_DEFINED +#else +#define yyget_debug wcsutrnget_debug +#endif + +#ifdef yyset_debug +#define wcsutrnset_debug_ALREADY_DEFINED +#else +#define yyset_debug wcsutrnset_debug +#endif + +#ifdef yyget_extra +#define wcsutrnget_extra_ALREADY_DEFINED +#else +#define yyget_extra wcsutrnget_extra +#endif + +#ifdef yyset_extra +#define wcsutrnset_extra_ALREADY_DEFINED +#else +#define yyset_extra wcsutrnset_extra +#endif + +#ifdef yyget_in +#define wcsutrnget_in_ALREADY_DEFINED +#else +#define yyget_in wcsutrnget_in +#endif + +#ifdef yyset_in +#define wcsutrnset_in_ALREADY_DEFINED +#else +#define yyset_in wcsutrnset_in +#endif + +#ifdef yyget_out +#define wcsutrnget_out_ALREADY_DEFINED +#else +#define yyget_out wcsutrnget_out +#endif + +#ifdef yyset_out +#define wcsutrnset_out_ALREADY_DEFINED +#else +#define yyset_out wcsutrnset_out +#endif + +#ifdef yyget_leng +#define wcsutrnget_leng_ALREADY_DEFINED +#else +#define yyget_leng wcsutrnget_leng +#endif + +#ifdef yyget_text +#define wcsutrnget_text_ALREADY_DEFINED +#else +#define yyget_text wcsutrnget_text +#endif + +#ifdef yyget_lineno +#define wcsutrnget_lineno_ALREADY_DEFINED +#else +#define yyget_lineno wcsutrnget_lineno +#endif + +#ifdef yyset_lineno +#define wcsutrnset_lineno_ALREADY_DEFINED +#else +#define yyset_lineno wcsutrnset_lineno +#endif + +#ifdef yyget_column +#define wcsutrnget_column_ALREADY_DEFINED +#else +#define yyget_column wcsutrnget_column +#endif + +#ifdef yyset_column +#define wcsutrnset_column_ALREADY_DEFINED +#else +#define yyset_column wcsutrnset_column +#endif + +#ifdef yywrap +#define wcsutrnwrap_ALREADY_DEFINED +#else #define yywrap wcsutrnwrap +#endif + +#ifdef yyalloc +#define wcsutrnalloc_ALREADY_DEFINED +#else #define yyalloc wcsutrnalloc +#endif + +#ifdef yyrealloc +#define wcsutrnrealloc_ALREADY_DEFINED +#else #define yyrealloc wcsutrnrealloc -#define yyfree wcsutrnfree +#endif -#define FLEX_SCANNER -#define YY_FLEX_MAJOR_VERSION 2 -#define YY_FLEX_MINOR_VERSION 5 -#define YY_FLEX_SUBMINOR_VERSION 35 -#if YY_FLEX_SUBMINOR_VERSION > 0 -#define FLEX_BETA +#ifdef yyfree +#define wcsutrnfree_ALREADY_DEFINED +#else +#define yyfree wcsutrnfree #endif /* First, we deal with platform-specific or compiler-specific issues. */ @@ -103,60 +295,65 @@ typedef unsigned int flex_uint32_t; #define UINT32_MAX (4294967295U) #endif +#ifndef SIZE_MAX +#define SIZE_MAX (~(size_t)0) +#endif + #endif /* ! C99 */ #endif /* ! FLEXINT_H */ -#ifdef __cplusplus - -/* The "const" storage-class-modifier is valid. */ -#define YY_USE_CONST - -#else /* ! __cplusplus */ - -/* C99 requires __STDC__ to be defined as 1. */ -#if defined (__STDC__) +/* begin standard C++ headers. */ -#define YY_USE_CONST - -#endif /* defined (__STDC__) */ -#endif /* ! __cplusplus */ - -#ifdef YY_USE_CONST +/* TODO: this is always defined, so inline it */ #define yyconst const + +#if defined(__GNUC__) && __GNUC__ >= 3 +#define yynoreturn __attribute__((__noreturn__)) #else -#define yyconst +#define yynoreturn #endif /* Returned upon end-of-file. */ #define YY_NULL 0 -/* Promotes a possibly negative, possibly signed char to an unsigned - * integer for use as an array index. If the signed char is negative, - * we want to instead treat it as an 8-bit unsigned char, hence the - * double cast. +/* Promotes a possibly negative, possibly signed char to an + * integer in range [0..255] for use as an array index. */ -#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) +#define YY_SC_TO_UI(c) ((YY_CHAR) (c)) + +/* An opaque pointer. */ +#ifndef YY_TYPEDEF_YY_SCANNER_T +#define YY_TYPEDEF_YY_SCANNER_T +typedef void* yyscan_t; +#endif + +/* For convenience, these vars (plus the bison vars far below) + are macros in the reentrant scanner. */ +#define yyin yyg->yyin_r +#define yyout yyg->yyout_r +#define yyextra yyg->yyextra_r +#define yyleng yyg->yyleng_r +#define yytext yyg->yytext_r +#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno) +#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column) +#define yy_flex_debug yyg->yy_flex_debug_r /* Enter a start condition. This macro really ought to take a parameter, * but we do it the disgusting crufty way forced on us by the ()-less * definition of BEGIN. */ -#define BEGIN (yy_start) = 1 + 2 * - +#define BEGIN yyg->yy_start = 1 + 2 * /* Translate the current start state into a value that can be later handed * to BEGIN to return to the state. The YYSTATE alias is for lex * compatibility. */ -#define YY_START (((yy_start) - 1) / 2) +#define YY_START ((yyg->yy_start - 1) / 2) #define YYSTATE YY_START - /* Action number for EOF rule of a given start state. */ #define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) - /* Special action meaning "start processing a new file". */ -#define YY_NEW_FILE wcsutrnrestart(wcsutrnin ) - +#define YY_NEW_FILE yyrestart( yyin , yyscanner ) #define YY_END_OF_BUFFER_CHAR 0 /* Size of default input buffer. */ @@ -181,36 +378,32 @@ typedef unsigned int flex_uint32_t; typedef struct yy_buffer_state *YY_BUFFER_STATE; #endif -extern int wcsutrnleng; - -extern FILE *wcsutrnin, *wcsutrnout; +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef size_t yy_size_t; +#endif #define EOB_ACT_CONTINUE_SCAN 0 #define EOB_ACT_END_OF_FILE 1 #define EOB_ACT_LAST_MATCH 2 - + #define YY_LESS_LINENO(n) +#define YY_LINENO_REWIND_TO(ptr) /* Return all but the first "n" matched characters back to the input stream. */ #define yyless(n) \ do \ { \ - /* Undo effects of setting up wcsutrntext. */ \ + /* Undo effects of setting up yytext. */ \ int yyless_macro_arg = (n); \ YY_LESS_LINENO(yyless_macro_arg);\ - *yy_cp = (yy_hold_char); \ + *yy_cp = yyg->yy_hold_char; \ YY_RESTORE_YY_MORE_OFFSET \ - (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ - YY_DO_BEFORE_ACTION; /* set up wcsutrntext again */ \ + yyg->yy_c_buf_p = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ + YY_DO_BEFORE_ACTION; /* set up yytext again */ \ } \ while ( 0 ) - -#define unput(c) yyunput( c, (yytext_ptr) ) - -#ifndef YY_TYPEDEF_YY_SIZE_T -#define YY_TYPEDEF_YY_SIZE_T -typedef size_t yy_size_t; -#endif +#define unput(c) yyunput( c, yyg->yytext_ptr , yyscanner ) #ifndef YY_STRUCT_YY_BUFFER_STATE #define YY_STRUCT_YY_BUFFER_STATE @@ -224,7 +417,7 @@ struct yy_buffer_state /* Size of input buffer in bytes, not including room for EOB * characters. */ - yy_size_t yy_buf_size; + int yy_buf_size; /* Number of characters read into yy_ch_buf, not including EOB * characters. @@ -252,7 +445,7 @@ struct yy_buffer_state int yy_bs_lineno; /**< The line count. */ int yy_bs_column; /**< The column count. */ - + /* Whether to try to fill the input buffer when we reach the * end of it. */ @@ -269,113 +462,81 @@ struct yy_buffer_state * possible backing-up. * * When we actually see the EOF, we change the status to "new" - * (via wcsutrnrestart()), so that the user can continue scanning by - * just pointing wcsutrnin at a new input file. + * (via yyrestart()), so that the user can continue scanning by + * just pointing yyin at a new input file. */ #define YY_BUFFER_EOF_PENDING 2 }; #endif /* !YY_STRUCT_YY_BUFFER_STATE */ -/* Stack of input buffers. */ -static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */ -static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */ -static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */ - /* We provide macros for accessing buffer states in case in the * future we want to put the buffer states in a more general * "scanner state". * * Returns the top of the stack, or NULL. */ -#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \ - ? (yy_buffer_stack)[(yy_buffer_stack_top)] \ +#define YY_CURRENT_BUFFER ( yyg->yy_buffer_stack \ + ? yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] \ : NULL) - /* Same as previous macro, but useful when we know that the buffer stack is not * NULL or when we need an lvalue. For internal use only. */ -#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)] - -/* yy_hold_char holds the character lost when wcsutrntext is formed. */ -static char yy_hold_char; -static int yy_n_chars; /* number of characters read into yy_ch_buf */ -int wcsutrnleng; - -/* Points to current character in buffer. */ -static char *yy_c_buf_p = (char *) 0; -static int yy_init = 0; /* whether we need to initialize */ -static int yy_start = 0; /* start state number */ - -/* Flag which is used to allow wcsutrnwrap()'s to do buffer switches - * instead of setting up a fresh wcsutrnin. A bit of a hack ... - */ -static int yy_did_buffer_switch_on_eof; - -void wcsutrnrestart (FILE *input_file ); -void wcsutrn_switch_to_buffer (YY_BUFFER_STATE new_buffer ); -YY_BUFFER_STATE wcsutrn_create_buffer (FILE *file,int size ); -void wcsutrn_delete_buffer (YY_BUFFER_STATE b ); -void wcsutrn_flush_buffer (YY_BUFFER_STATE b ); -void wcsutrnpush_buffer_state (YY_BUFFER_STATE new_buffer ); -void wcsutrnpop_buffer_state (void ); - -static void wcsutrnensure_buffer_stack (void ); -static void wcsutrn_load_buffer_state (void ); -static void wcsutrn_init_buffer (YY_BUFFER_STATE b,FILE *file ); - -#define YY_FLUSH_BUFFER wcsutrn_flush_buffer(YY_CURRENT_BUFFER ) - -YY_BUFFER_STATE wcsutrn_scan_buffer (char *base,yy_size_t size ); -YY_BUFFER_STATE wcsutrn_scan_string (yyconst char *yy_str ); -YY_BUFFER_STATE wcsutrn_scan_bytes (yyconst char *bytes,int len ); - -void *wcsutrnalloc (yy_size_t ); -void *wcsutrnrealloc (void *,yy_size_t ); -void wcsutrnfree (void * ); - -#define yy_new_buffer wcsutrn_create_buffer - +#define YY_CURRENT_BUFFER_LVALUE yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] + +void yyrestart ( FILE *input_file , yyscan_t yyscanner ); +void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size , yyscan_t yyscanner ); +void yy_delete_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yy_flush_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yypush_buffer_state ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +void yypop_buffer_state ( yyscan_t yyscanner ); + +static void yyensure_buffer_stack ( yyscan_t yyscanner ); +static void yy_load_buffer_state ( yyscan_t yyscanner ); +static void yy_init_buffer ( YY_BUFFER_STATE b, FILE *file , yyscan_t yyscanner ); +#define YY_FLUSH_BUFFER yy_flush_buffer( YY_CURRENT_BUFFER , yyscanner) + +YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_string ( const char *yy_str , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len , yyscan_t yyscanner ); + +void *yyalloc ( yy_size_t , yyscan_t yyscanner ); +void *yyrealloc ( void *, yy_size_t , yyscan_t yyscanner ); +void yyfree ( void * , yyscan_t yyscanner ); + +#define yy_new_buffer yy_create_buffer #define yy_set_interactive(is_interactive) \ { \ if ( ! YY_CURRENT_BUFFER ){ \ - wcsutrnensure_buffer_stack (); \ + yyensure_buffer_stack (yyscanner); \ YY_CURRENT_BUFFER_LVALUE = \ - wcsutrn_create_buffer(wcsutrnin,YY_BUF_SIZE ); \ + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); \ } \ YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ } - #define yy_set_bol(at_bol) \ { \ if ( ! YY_CURRENT_BUFFER ){\ - wcsutrnensure_buffer_stack (); \ + yyensure_buffer_stack (yyscanner); \ YY_CURRENT_BUFFER_LVALUE = \ - wcsutrn_create_buffer(wcsutrnin,YY_BUF_SIZE ); \ + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); \ } \ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ } - #define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) /* Begin user sect3 */ -#define wcsutrnwrap(n) 1 +#define wcsutrnwrap(yyscanner) (/*CONSTCOND*/1) #define YY_SKIP_YYWRAP - -typedef char YY_CHAR; - -FILE *wcsutrnin = (FILE *) 0, *wcsutrnout = (FILE *) 0; +typedef flex_uint8_t YY_CHAR; typedef int yy_state_type; -extern int wcsutrnlineno; +#define yytext_ptr yytext_r -int wcsutrnlineno = 1; - -extern char *wcsutrntext; -#define yytext_ptr wcsutrntext -static yyconst flex_int16_t yy_nxt[][128] = +static const flex_int16_t yy_nxt[][128] = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -563,7 +724,7 @@ static yyconst flex_int16_t yy_nxt[][128] = 43, -10, -10, -10, -10, -10, -10, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 45, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -10, -10, -10, -10, -10 }, @@ -574,14 +735,14 @@ static yyconst flex_int16_t yy_nxt[][128] = -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, - -11, -11, -11, -11, -11, 43, 43, 43, 43, 45, + -11, -11, -11, -11, -11, 43, 43, 43, 43, 46, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -11, -11, -11, -11, -11, -11, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 46, 43, -11, -11, -11, -11, -11 + 43, 47, 43, -11, -11, -11, -11, -11 }, { @@ -609,11 +770,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, - -13, -13, -13, -13, -13, 47, 43, 43, 43, 48, + -13, -13, -13, -13, -13, 48, 43, 43, 43, 49, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -13, -13, -13, -13, -13, -13, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 50, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -13, -13, -13, -13, -13 @@ -627,7 +788,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -14, -14, -14, -14, -14, -14, -14, -14, -14, -14, -14, -14, -14, -14, -14, -14, -14, -14, -14, -14, -14, -14, -14, -14, -14, 43, 43, 43, 43, 43, - 43, 43, 49, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 51, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -14, -14, -14, -14, -14, -14, 43, 43, 43, @@ -646,8 +807,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 50, 43, 43, 43, 43, 43, 43, 43, - 51, -15, -15, -15, -15, -15, -15, 43, 43, 43, + 43, 43, 52, 43, 43, 43, 43, 43, 43, 43, + 53, -15, -15, -15, -15, -15, -15, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -15, -15, -15, -15, -15 @@ -663,7 +824,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 52, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 54, 43, -16, -16, -16, -16, -16, -16, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, @@ -678,12 +839,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, - -17, -17, -17, -17, -17, 43, 43, 43, 43, 53, - 43, 43, 54, 43, 43, 43, 43, 55, 43, 43, + -17, -17, -17, -17, -17, 43, 43, 43, 43, 55, + 43, 43, 56, 43, 43, 43, 43, 57, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -17, -17, -17, -17, -17, -17, 43, 43, 43, - 43, 56, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 58, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -17, -17, -17, -17, -17 }, @@ -696,8 +857,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, - -18, -18, -18, -18, -18, 43, 43, 43, 43, 57, - 43, 43, 58, 59, 43, 43, 43, 43, 43, 43, + -18, -18, -18, -18, -18, 43, 43, 43, 43, 59, + 43, 43, 60, 61, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -18, -18, -18, -18, -18, -18, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, @@ -717,7 +878,7 @@ static yyconst flex_int16_t yy_nxt[][128] = 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -19, -19, -19, -19, -19, -19, 43, 43, 43, - 43, 43, 43, 43, 60, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 62, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -19, -19, -19, -19, -19 @@ -730,10 +891,10 @@ static yyconst flex_int16_t yy_nxt[][128] = -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, - -20, -20, -20, -20, -20, 61, 43, 43, 43, 43, - 43, 43, 43, 62, 43, 43, 43, 43, 43, 43, + -20, -20, -20, -20, -20, 63, 43, 43, 43, 43, + 43, 43, 43, 64, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, -20, -20, -20, -20, -20, -20, 63, 43, 43, + 43, -20, -20, -20, -20, -20, -20, 65, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, @@ -747,7 +908,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -21, -21, -21, -21, -21, -21, -21, -21, -21, -21, -21, -21, -21, -21, -21, -21, -21, -21, -21, -21, -21, -21, -21, -21, -21, -21, -21, -21, -21, -21, - -21, -21, -21, -21, -21, 64, 43, 43, 43, 43, + -21, -21, -21, -21, -21, 66, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, @@ -765,7 +926,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -22, -22, -22, -22, -22, -22, -22, -22, -22, -22, -22, -22, -22, -22, -22, -22, -22, -22, -22, -22, - -22, -22, -22, -22, -22, 43, 43, 43, 43, 65, + -22, -22, -22, -22, -22, 43, 43, 43, 43, 67, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -22, -22, -22, -22, -22, -22, 43, 43, 43, @@ -783,12 +944,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -23, -23, -23, -23, -23, -23, -23, -23, -23, -23, -23, -23, -23, -23, -23, -23, -23, -23, -23, -23, -23, -23, -23, -23, -23, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 66, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 68, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -23, -23, -23, -23, -23, -23, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 67, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 69, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -23, -23, -23, -23, -23 }, @@ -799,10 +960,10 @@ static yyconst flex_int16_t yy_nxt[][128] = -24, -24, -24, -24, -24, -24, -24, -24, -24, -24, -24, -24, -24, -24, -24, -24, -24, -24, -24, -24, -24, -24, -24, -24, -24, -24, -24, -24, -24, -24, - -24, -24, -24, -24, -24, 43, 43, 43, 43, 68, + -24, -24, -24, -24, -24, 43, 43, 43, 43, 70, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 69, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 71, 43, 43, 43, 43, 43, 43, 43, 43, -24, -24, -24, -24, -24, -24, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, @@ -822,7 +983,7 @@ static yyconst flex_int16_t yy_nxt[][128] = 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -25, -25, -25, -25, -25, -25, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 70, 43, 43, 43, 71, 43, 43, 43, 43, 43, + 72, 43, 43, 43, 73, 43, 43, 43, 43, 43, 43, 43, 43, -25, -25, -25, -25, -25 }, @@ -837,8 +998,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -26, -26, -26, -26, -26, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, -26, -26, -26, -26, -26, -26, 72, 43, 43, - 43, 73, 43, 43, 43, 43, 43, 43, 43, 43, + 43, -26, -26, -26, -26, -26, -26, 74, 43, 43, + 43, 75, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -26, -26, -26, -26, -26 @@ -857,8 +1018,8 @@ static yyconst flex_int16_t yy_nxt[][128] = 43, -27, -27, -27, -27, -27, -27, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 50, 43, 43, 43, 43, 43, - 43, 43, 51, -27, -27, -27, -27, -27 + 43, 43, 43, 43, 52, 43, 43, 43, 43, 43, + 43, 43, 53, -27, -27, -27, -27, -27 }, { @@ -873,7 +1034,7 @@ static yyconst flex_int16_t yy_nxt[][128] = 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -28, -28, -28, -28, -28, -28, 43, 43, 43, - 43, 56, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 58, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -28, -28, -28, -28, -28 }, @@ -890,7 +1051,7 @@ static yyconst flex_int16_t yy_nxt[][128] = 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -29, -29, -29, -29, -29, -29, 43, 43, 43, - 43, 74, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 76, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -29, -29, -29, -29, -29 @@ -906,9 +1067,9 @@ static yyconst flex_int16_t yy_nxt[][128] = -30, -30, -30, -30, -30, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, -30, -30, -30, -30, -30, -30, 63, 43, 43, + 43, -30, -30, -30, -30, -30, -30, 65, 43, 43, - 43, 43, 43, 43, 43, 75, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 77, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -30, -30, -30, -30, -30 }, @@ -924,7 +1085,7 @@ static yyconst flex_int16_t yy_nxt[][128] = 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, -31, -31, -31, -31, -31, -31, 76, 43, 43, + 43, -31, -31, -31, -31, -31, -31, 78, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -31, -31, -31, -31, -31 @@ -942,7 +1103,7 @@ static yyconst flex_int16_t yy_nxt[][128] = 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -32, -32, -32, -32, -32, -32, 43, 43, 43, - 43, 77, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 79, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -32, -32, -32, -32, -32 }, @@ -961,7 +1122,7 @@ static yyconst flex_int16_t yy_nxt[][128] = 43, -33, -33, -33, -33, -33, -33, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 67, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 69, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -33, -33, -33, -33, -33 }, @@ -977,7 +1138,7 @@ static yyconst flex_int16_t yy_nxt[][128] = 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -34, -34, -34, -34, -34, -34, 43, 43, 43, - 43, 78, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 80, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -34, -34, -34, -34, -34 }, @@ -986,14 +1147,14 @@ static yyconst flex_int16_t yy_nxt[][128] = 7, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, - -35, -35, 79, -35, -35, -35, -35, -35, -35, -35, + -35, -35, 81, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, - -35, 80, -35, -35, -35, -35, -35, -35, -35, -35, + -35, 82, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35 @@ -1052,20 +1213,20 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 7, 81, 81, 81, 81, 81, 81, 81, 81, 81, - 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, - 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, - - 81, 81, 82, 81, 81, 81, 81, 81, 81, 81, - 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, - 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, - 81, 81, 81, 81, 81, 83, 83, 83, 83, 83, + 7, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, - 83, 81, 81, 81, 81, 81, 81, 83, 83, 83, + + 83, 83, 84, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, - 83, 83, 83, 81, 81, 81, 81, 81 + 83, 83, 83, 83, 83, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 83, 83, 83, 83, 83, 83, 85, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 83, 83, 83, 83, 83 }, @@ -1076,31 +1237,31 @@ static yyconst flex_int16_t yy_nxt[][128] = -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, - -40, -40, -40, -40, -40, 84, 84, 84, 84, 84, - 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, - 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, - 84, -40, -40, -40, -40, -40, -40, 84, 84, 84, + -40, -40, -40, -40, -40, 86, 86, 86, 86, 86, + 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, + 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, + 86, -40, -40, -40, -40, -40, -40, 86, 86, 86, - 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, - 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, - 84, 84, 84, -40, -40, -40, -40, -40 + 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, + 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, + 86, 86, 86, -40, -40, -40, -40, -40 }, { - 7, 85, 85, 85, 85, 85, 85, 85, 85, 85, - -41, 85, 85, 85, 85, 85, 85, 85, 85, 85, - 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, - 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, - 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, - 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, - 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 7, 87, 87, 87, 87, 87, 87, 87, 87, 87, + -41, 87, 87, 87, 87, 87, 87, 87, 87, 87, + 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, + 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, + 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, + 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, + 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, - 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, - 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, - 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, - 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, - 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, - 85, 85, 85, 85, 85, 85, 85, 85 + 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, + 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, + 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, + 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, + 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, + 87, 87, 87, 87, 87, 87, 87, 87 }, { @@ -1145,7 +1306,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, - -44, -44, -44, -44, -44, 43, 43, 86, 43, 43, + -44, -44, -44, -44, -44, 43, 43, 88, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, @@ -1163,11 +1324,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, - -45, -45, -45, -45, -45, 87, 43, 43, 43, 43, + -45, -45, -45, -45, -45, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -45, -45, -45, -45, -45, -45, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 89, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -45, -45, -45, -45, -45 }, @@ -1180,12 +1341,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, - -46, -46, -46, -46, -46, 43, 43, 43, 43, 43, + -46, -46, -46, -46, -46, 90, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -46, -46, -46, -46, -46, -46, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 88, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -46, -46, -46, -46, -46 }, @@ -1199,11 +1360,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -47, -47, -47, -47, -47, -47, -47, -47, -47, -47, -47, -47, -47, -47, -47, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 89, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -47, -47, -47, -47, -47, -47, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 91, 43, 43, 43, 43, 43, 43, -47, -47, -47, -47, -47 }, @@ -1216,8 +1377,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, 43, 43, 43, 43, 43, - 43, 90, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 92, 43, -48, -48, -48, -48, -48, -48, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, @@ -1233,9 +1394,9 @@ static yyconst flex_int16_t yy_nxt[][128] = -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, 43, 43, 43, 43, 43, + 43, 93, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 91, -49, -49, -49, -49, -49, -49, 43, 43, 43, + 43, -49, -49, -49, -49, -49, -49, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -49, -49, -49, -49, -49 @@ -1254,7 +1415,7 @@ static yyconst flex_int16_t yy_nxt[][128] = 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -50, -50, -50, -50, -50, -50, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 94, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -50, -50, -50, -50, -50 }, @@ -1270,7 +1431,7 @@ static yyconst flex_int16_t yy_nxt[][128] = 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, -51, -51, -51, -51, -51, -51, 43, 43, 43, + 95, -51, -51, -51, -51, -51, -51, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -51, -51, -51, -51, -51 @@ -1302,7 +1463,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 92, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -53, -53, -53, -53, -53, -53, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, @@ -1322,7 +1483,7 @@ static yyconst flex_int16_t yy_nxt[][128] = 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 93, -54, -54, -54, -54, -54, -54, 43, 43, 43, + 43, -54, -54, -54, -54, -54, -54, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -54, -54, -54, -54, -54 @@ -1337,7 +1498,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 96, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -55, -55, -55, -55, -55, -55, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, @@ -1356,8 +1517,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -56, -56, -56, -56, -56, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, -56, -56, -56, -56, -56, -56, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 94, 43, + 97, -56, -56, -56, -56, -56, -56, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -56, -56, -56, -56, -56 @@ -1372,7 +1533,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 95, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -57, -57, -57, -57, -57, -57, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, @@ -1391,8 +1552,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -58, -58, -58, -58, -58, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 96, -58, -58, -58, -58, -58, -58, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, -58, -58, -58, -58, -58, -58, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 98, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -58, -58, -58, -58, -58 }, @@ -1406,8 +1567,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 97, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 99, 43, 43, 43, 43, 43, 43, -59, -59, -59, -59, -59, -59, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, @@ -1425,9 +1586,9 @@ static yyconst flex_int16_t yy_nxt[][128] = -60, -60, -60, -60, -60, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, -60, -60, -60, -60, -60, -60, 43, 43, 43, + 100, -60, -60, -60, -60, -60, -60, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 98, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -60, -60, -60, -60, -60 }, @@ -1441,8 +1602,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 101, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 99, 43, 43, 43, 43, 43, 43, 43, -61, -61, -61, -61, -61, -61, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, @@ -1459,9 +1620,9 @@ static yyconst flex_int16_t yy_nxt[][128] = -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 100, 43, - 43, -62, -62, -62, -62, -62, -62, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, -62, -62, -62, -62, -62, -62, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 102, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -62, -62, -62, -62, -62 }, @@ -1476,11 +1637,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 103, 43, 43, 43, 43, 43, 43, 43, -63, -63, -63, -63, -63, -63, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 101, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -63, -63, -63, -63, -63 }, @@ -1491,10 +1652,10 @@ static yyconst flex_int16_t yy_nxt[][128] = -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, - -64, -64, -64, -64, -64, 43, 43, 43, 102, 43, + -64, -64, -64, -64, -64, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 104, 43, 43, -64, -64, -64, -64, -64, -64, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, @@ -1509,12 +1670,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, - -65, -65, -65, -65, -65, 43, 43, 103, 43, 43, + -65, -65, -65, -65, -65, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -65, -65, -65, -65, -65, -65, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 105, 43, 43, 43, 43, 43, 43, 43, -65, -65, -65, -65, -65 }, @@ -1526,8 +1687,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, - -66, -66, -66, -66, -66, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 104, 43, 43, 43, + -66, -66, -66, -66, -66, 43, 43, 43, 106, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -66, -66, -66, -66, -66, -66, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, @@ -1543,12 +1704,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, - -67, -67, -67, -67, -67, 43, 43, 43, 43, 43, + -67, -67, -67, -67, -67, 43, 43, 107, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -67, -67, -67, -67, -67, -67, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 105, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -67, -67, -67, -67, -67 }, @@ -1561,8 +1722,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, - -68, -68, -68, -68, -68, 106, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + -68, -68, -68, -68, -68, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 108, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -68, -68, -68, -68, -68, -68, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, @@ -1582,7 +1743,7 @@ static yyconst flex_int16_t yy_nxt[][128] = 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -69, -69, -69, -69, -69, -69, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 109, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -69, -69, -69, -69, -69 @@ -1595,12 +1756,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, - -70, -70, -70, -70, -70, 43, 43, 43, 43, 43, + -70, -70, -70, -70, -70, 110, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -70, -70, -70, -70, -70, -70, 43, 43, 43, - 43, 43, 43, 107, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -70, -70, -70, -70, -70 }, @@ -1616,7 +1777,7 @@ static yyconst flex_int16_t yy_nxt[][128] = 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, -71, -71, -71, -71, -71, -71, 43, 43, 108, + 43, -71, -71, -71, -71, -71, -71, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -71, -71, -71, -71, -71 @@ -1634,9 +1795,9 @@ static yyconst flex_int16_t yy_nxt[][128] = 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -72, -72, -72, -72, -72, -72, 43, 43, 43, + 43, 43, 43, 111, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 109, 43, -72, -72, -72, -72, -72 + 43, 43, 43, -72, -72, -72, -72, -72 }, { @@ -1650,8 +1811,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -73, -73, -73, -73, -73, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, -73, -73, -73, -73, -73, -73, 43, 43, 43, - 43, 43, 43, 110, 43, 43, 43, 43, 43, 43, + 43, -73, -73, -73, -73, -73, -73, 43, 43, 112, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -73, -73, -73, -73, -73 @@ -1670,8 +1831,8 @@ static yyconst flex_int16_t yy_nxt[][128] = 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -74, -74, -74, -74, -74, -74, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 111, 43, 43, 43, - 43, 43, 43, -74, -74, -74, -74, -74 + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 113, 43, -74, -74, -74, -74, -74 }, { @@ -1686,9 +1847,9 @@ static yyconst flex_int16_t yy_nxt[][128] = 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -75, -75, -75, -75, -75, -75, 43, 43, 43, + 43, 43, 43, 114, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 112, 43, 43, -75, -75, -75, -75, -75 + 43, 43, 43, -75, -75, -75, -75, -75 }, { @@ -1703,8 +1864,8 @@ static yyconst flex_int16_t yy_nxt[][128] = 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -76, -76, -76, -76, -76, -76, 43, 43, 43, - 113, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 115, 43, 43, 43, 43, 43, 43, -76, -76, -76, -76, -76 }, @@ -1720,10 +1881,10 @@ static yyconst flex_int16_t yy_nxt[][128] = 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, -77, -77, -77, -77, -77, -77, 43, 43, 114, + 43, -77, -77, -77, -77, -77, -77, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, -77, -77, -77, -77, -77 + 116, 43, 43, -77, -77, -77, -77, -77 }, { @@ -1737,8 +1898,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -78, -78, -78, -78, -78, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, -78, -78, -78, -78, -78, -78, 115, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, -78, -78, -78, -78, -78, -78, 43, 43, 43, + 117, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -78, -78, -78, -78, -78 }, @@ -1748,16 +1909,16 @@ static yyconst flex_int16_t yy_nxt[][128] = -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, - -79, -79, 79, -79, -79, -79, -79, -79, -79, -79, - -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, - -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, - -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, - -79, 80, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, - -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, - -79, -79, -79, -79, -79, -79, -79, -79 + -79, -79, -79, -79, -79, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, -79, -79, -79, -79, -79, -79, 43, 43, 118, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, -79, -79, -79, -79, -79 }, @@ -1768,48 +1929,48 @@ static yyconst flex_int16_t yy_nxt[][128] = -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, - -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, - -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, - -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, - -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, + -80, -80, -80, -80, -80, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, -80, -80, -80, -80, -80, -80, 119, 43, 43, - -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, - -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, - -80, -80, -80, -80, -80, -80, -80, -80 + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, -80, -80, -80, -80, -80 }, { 7, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, - -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, + -81, -81, 81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, - -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, + -81, 82, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81 }, { - 7, 81, 81, 81, 81, 81, 81, 81, 81, 81, - 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, - 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, - 81, 81, 82, 81, 81, 81, 81, 81, 81, 81, + 7, -82, -82, -82, -82, -82, -82, -82, -82, -82, + -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, + -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, + -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, - 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, - 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, - 81, 81, 81, 81, 81, 83, 83, 83, 83, 83, - 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, - 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, - 83, 81, 81, 81, 81, 81, 81, 83, 83, 83, - 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, - 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, - 83, 83, 83, 81, 81, 81, 81, 81 + -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, + -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, + -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, + -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, + -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, + -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, + -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, + -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, + -82, -82, -82, -82, -82, -82, -82, -82 }, { @@ -1831,37 +1992,37 @@ static yyconst flex_int16_t yy_nxt[][128] = }, { - 7, -84, -84, -84, -84, -84, -84, -84, -84, -84, - -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, - -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, - -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, - -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, - -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, - -84, -84, -84, -84, -84, 84, 84, 84, 84, 84, - 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, - - 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, - 84, -84, -84, -84, -84, -84, -84, 84, 84, 84, - 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, - 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, - 84, 84, 84, -84, -84, -84, -84, -84 - }, - - { - 7, 85, 85, 85, 85, 85, 85, 85, 85, 85, - -85, 85, 85, 85, 85, 85, 85, 85, 85, 85, - 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, - 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 7, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 84, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 83, 83, 83, 83, 83, 83, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, - 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, - 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, - 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, - 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, - 85, 85, 85, 85, 85, 85, 85, 85 + 85, 85, 85, 83, 83, 83, 83, 83 + }, + + { + 7, -85, -85, -85, -85, -85, -85, -85, -85, -85, + -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, + -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, + -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, + -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, + + -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, + -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, + -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, + -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, + -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, + -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, + -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, + -85, -85, -85, -85, -85, -85, -85, -85 }, { @@ -1872,31 +2033,31 @@ static yyconst flex_int16_t yy_nxt[][128] = -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, - -86, -86, -86, -86, -86, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 116, 43, 43, - 43, 43, 43, 117, 43, 43, 43, 43, 43, 43, - 43, -86, -86, -86, -86, -86, -86, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + -86, -86, -86, -86, -86, 86, 86, 86, 86, 86, + 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, + 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, + 86, -86, -86, -86, -86, -86, -86, 86, 86, 86, + 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, + 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, - 43, 43, 43, -86, -86, -86, -86, -86 + 86, 86, 86, -86, -86, -86, -86, -86 }, { - 7, -87, -87, -87, -87, -87, -87, -87, -87, -87, - -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, - -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, - -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, - -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, - -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, - -87, -87, -87, -87, -87, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 118, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 7, 87, 87, 87, 87, 87, 87, 87, 87, 87, + -87, 87, 87, 87, 87, 87, 87, 87, 87, 87, + 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, + 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, + 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, + 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, + 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, + 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, + 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, - 43, -87, -87, -87, -87, -87, -87, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, -87, -87, -87, -87, -87 + 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, + 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, + 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, + 87, 87, 87, 87, 87, 87, 87, 87 }, { @@ -1908,10 +2069,10 @@ static yyconst flex_int16_t yy_nxt[][128] = -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 120, 43, 43, + 43, 43, 43, 121, 43, 43, 43, 43, 43, 43, 43, -88, -88, -88, -88, -88, -88, 43, 43, 43, - 43, 119, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -88, -88, -88, -88, -88 }, @@ -1926,10 +2087,10 @@ static yyconst flex_int16_t yy_nxt[][128] = -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 120, 43, 43, 43, 43, 43, 43, - 43, -89, -89, -89, -89, -89, -89, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, -89, -89, -89, -89, -89, -89, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 122, 43, 43, 43, 43, 43, 43, 43, -89, -89, -89, -89, -89 }, @@ -1942,8 +2103,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 123, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 121, 43, 43, 43, 43, 43, 43, 43, 43, -90, -90, -90, -90, -90, -90, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, @@ -1963,7 +2124,7 @@ static yyconst flex_int16_t yy_nxt[][128] = 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -91, -91, -91, -91, -91, -91, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 124, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -91, -91, -91, -91, -91 }, @@ -1978,7 +2139,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 122, 43, 43, 43, + 43, 43, 43, 125, 43, 43, 43, 43, 43, 43, 43, -92, -92, -92, -92, -92, -92, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, @@ -1995,7 +2156,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 126, 43, 43, 43, 43, 43, 43, 43, 43, -93, -93, -93, -93, -93, -93, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, @@ -2016,7 +2177,7 @@ static yyconst flex_int16_t yy_nxt[][128] = 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -94, -94, -94, -94, -94, -94, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 123, 43, + 43, 43, 43, 43, 127, 43, 43, 43, 43, 43, 43, 43, 43, -94, -94, -94, -94, -94 }, @@ -2028,9 +2189,9 @@ static yyconst flex_int16_t yy_nxt[][128] = -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, - -95, -95, -95, -95, -95, 43, 43, 43, 43, 124, + -95, -95, -95, -95, -95, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 125, 43, 43, 43, 43, 43, 43, 43, 43, -95, -95, -95, -95, -95, -95, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, @@ -2047,7 +2208,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 128, 43, 43, 43, 43, -96, -96, -96, -96, -96, -96, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, @@ -2085,7 +2246,7 @@ static yyconst flex_int16_t yy_nxt[][128] = 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -98, -98, -98, -98, -98, -98, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 129, 43, 43, 43, 43, -98, -98, -98, -98, -98 }, @@ -2097,9 +2258,9 @@ static yyconst flex_int16_t yy_nxt[][128] = -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, - -99, -99, -99, -99, -99, 43, 43, 126, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + -99, -99, -99, -99, -99, 43, 43, 43, 43, 130, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 131, 43, 43, 43, 43, 43, 43, 43, 43, -99, -99, -99, -99, -99, -99, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, @@ -2114,7 +2275,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, - -100, -100, -100, -100, -100, 43, 43, 43, 43, 127, + -100, -100, -100, -100, -100, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -100, -100, -100, -100, -100, -100, 43, 43, 43, @@ -2135,7 +2296,7 @@ static yyconst flex_int16_t yy_nxt[][128] = 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, -101, -101, -101, -101, -101, -101, 43, 43, 128, + 43, -101, -101, -101, -101, -101, -101, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -101, -101, -101, -101, -101 @@ -2150,7 +2311,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, 43, 43, 43, 43, 43, - 43, 43, 43, 129, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -102, -102, -102, -102, -102, -102, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, @@ -2166,8 +2327,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, - -103, -103, -103, -103, -103, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 130, + -103, -103, -103, -103, -103, 43, 43, 132, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -103, -103, -103, -103, -103, -103, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, @@ -2183,10 +2344,10 @@ static yyconst flex_int16_t yy_nxt[][128] = -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, - -104, -104, -104, -104, -104, 43, 43, 43, 43, 43, + -104, -104, -104, -104, -104, 43, 43, 43, 43, 133, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 131, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -104, -104, -104, -104, -104, -104, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, @@ -2204,9 +2365,9 @@ static yyconst flex_int16_t yy_nxt[][128] = -105, -105, -105, -105, -105, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, -105, -105, -105, -105, -105, -105, 43, 43, 43, + 43, -105, -105, -105, -105, -105, -105, 43, 43, 134, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 132, 43, 43, 43, 43, 43, 43, -105, -105, -105, -105, -105 }, @@ -2219,8 +2380,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, 43, 43, 43, 43, 43, + 43, 43, 43, 135, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 133, 43, 43, 43, 43, 43, 43, 43, 43, -106, -106, -106, -106, -106, -106, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, @@ -2236,12 +2397,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 136, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -107, -107, -107, -107, -107, -107, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 134, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -107, -107, -107, -107, -107 }, @@ -2255,10 +2416,10 @@ static yyconst flex_int16_t yy_nxt[][128] = -108, -108, -108, -108, -108, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 137, 43, 43, 43, 43, 43, 43, -108, -108, -108, -108, -108, -108, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 135, - 43, 43, 43, 43, 43, 136, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -108, -108, -108, -108, -108 }, @@ -2275,7 +2436,7 @@ static yyconst flex_int16_t yy_nxt[][128] = 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -109, -109, -109, -109, -109, -109, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 137, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 138, 43, 43, 43, 43, 43, 43, -109, -109, -109, -109, -109 }, @@ -2289,11 +2450,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 139, 43, 43, 43, 43, 43, 43, 43, 43, -110, -110, -110, -110, -110, -110, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 138, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -110, -110, -110, -110, -110 }, @@ -2309,8 +2470,8 @@ static yyconst flex_int16_t yy_nxt[][128] = 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -111, -111, -111, -111, -111, -111, 43, 43, 43, - 43, 139, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 140, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 140, 43, 43, 43, 43, 43, 43, 43, -111, -111, -111, -111, -111 }, @@ -2326,8 +2487,8 @@ static yyconst flex_int16_t yy_nxt[][128] = 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -112, -112, -112, -112, -112, -112, 43, 43, 43, - 43, 141, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 141, + 43, 43, 43, 43, 43, 142, 43, 43, 43, 43, 43, 43, 43, -112, -112, -112, -112, -112 }, @@ -2343,9 +2504,9 @@ static yyconst flex_int16_t yy_nxt[][128] = 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -113, -113, -113, -113, -113, -113, 43, 43, 43, - 43, 43, 43, 43, 43, 142, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + + 43, 43, 43, 43, 43, 143, 43, 43, 43, 43, 43, 43, 43, -113, -113, -113, -113, -113 }, @@ -2362,7 +2523,7 @@ static yyconst flex_int16_t yy_nxt[][128] = 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -114, -114, -114, -114, -114, -114, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 143, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 144, 43, 43, 43, 43, 43, 43, 43, 43, -114, -114, -114, -114, -114 }, @@ -2378,8 +2539,8 @@ static yyconst flex_int16_t yy_nxt[][128] = 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -115, -115, -115, -115, -115, -115, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 144, 43, 43, 43, 43, 43, + 43, 145, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 146, 43, 43, 43, 43, 43, 43, 43, 43, -115, -115, -115, -115, -115 }, @@ -2392,10 +2553,10 @@ static yyconst flex_int16_t yy_nxt[][128] = -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, -116, 43, 43, 43, 43, 43, - 43, 43, 43, 145, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, -116, -116, -116, -116, -116, -116, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, -116, -116, -116, -116, -116, -116, 43, 43, 43, + 43, 147, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -116, -116, -116, -116, -116 @@ -2408,12 +2569,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, - -117, -117, -117, -117, -117, 43, 43, 43, 43, 146, + -117, -117, -117, -117, -117, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -117, -117, -117, -117, -117, -117, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 148, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -117, -117, -117, -117, -117 }, @@ -2431,7 +2592,7 @@ static yyconst flex_int16_t yy_nxt[][128] = 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -118, -118, -118, -118, -118, -118, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 149, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -118, -118, -118, -118, -118 }, @@ -2448,7 +2609,7 @@ static yyconst flex_int16_t yy_nxt[][128] = 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -119, -119, -119, -119, -119, -119, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 150, 43, 43, 43, 43, 43, 43, 43, 43, -119, -119, -119, -119, -119 }, @@ -2461,7 +2622,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 151, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -120, -120, -120, -120, -120, -120, 43, 43, 43, @@ -2477,7 +2638,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, - -121, -121, -121, -121, -121, 43, 43, 43, 43, 147, + -121, -121, -121, -121, -121, 43, 43, 43, 43, 152, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, @@ -2496,11 +2657,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, -122, 43, 43, 43, 43, 43, - 43, 43, 43, 148, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, -122, -122, -122, -122, -122, -122, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, -122, -122, -122, -122, -122, -122, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 153, 43, 43, 43, 43, 43, 43, -122, -122, -122, -122, -122 }, @@ -2516,7 +2677,7 @@ static yyconst flex_int16_t yy_nxt[][128] = 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -123, -123, -123, -123, -123, -123, 43, 43, 43, - 43, 43, 43, 43, 43, 149, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -123, -123, -123, -123, -123 @@ -2532,7 +2693,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -124, -124, -124, -124, -124, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 150, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -124, -124, -124, -124, -124, -124, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, @@ -2547,7 +2708,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, -125, - -125, -125, -125, -125, -125, 43, 43, 43, 43, 151, + -125, -125, -125, -125, -125, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -125, -125, -125, -125, -125, -125, 43, 43, 43, @@ -2564,7 +2725,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, - -126, -126, -126, -126, -126, 152, 43, 43, 43, 43, + -126, -126, -126, -126, -126, 43, 43, 43, 43, 154, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -126, -126, -126, -126, -126, -126, 43, 43, 43, @@ -2582,11 +2743,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 153, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -127, -127, -127, -127, -127, -127, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 155, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -127, -127, -127, -127, -127 }, @@ -2600,9 +2761,9 @@ static yyconst flex_int16_t yy_nxt[][128] = -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, 43, 43, 43, 43, 43, + 43, 43, 43, 156, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, -128, -128, -128, -128, -128, -128, 154, 43, 43, + 43, -128, -128, -128, -128, -128, -128, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -128, -128, -128, -128, -128 @@ -2616,11 +2777,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, -129, - -129, -129, -129, -129, -129, 155, 43, 43, 43, 43, + -129, -129, -129, -129, -129, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -129, -129, -129, -129, -129, -129, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 157, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -129, -129, -129, -129, -129 @@ -2634,8 +2795,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, -130, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 156, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 158, 43, 43, 43, 43, 43, 43, 43, 43, -130, -130, -130, -130, -130, -130, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, @@ -2650,10 +2811,10 @@ static yyconst flex_int16_t yy_nxt[][128] = -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, -131, - -131, -131, -131, -131, -131, 43, 43, 43, 43, 43, + -131, -131, -131, -131, -131, 43, 43, 43, 43, 159, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 157, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -131, -131, -131, -131, -131, -131, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, @@ -2668,12 +2829,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, -132, - -132, -132, -132, -132, -132, 43, 43, 43, 43, 43, + -132, -132, -132, -132, -132, 160, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -132, -132, -132, -132, -132, -132, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 158, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -132, -132, -132, -132, -132 }, @@ -2686,8 +2847,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, -133, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 161, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 159, 43, 43, 43, 43, 43, 43, 43, -133, -133, -133, -133, -133, -133, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, @@ -2706,9 +2867,9 @@ static yyconst flex_int16_t yy_nxt[][128] = 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, -134, -134, -134, -134, -134, -134, 43, 43, 43, + 43, -134, -134, -134, -134, -134, -134, 162, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 160, 43, 43, 43, 43, 43, 43, -134, -134, -134, -134, -134 }, @@ -2720,11 +2881,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, - -135, -135, -135, -135, -135, 43, 43, 43, 43, 43, + -135, -135, -135, -135, -135, 163, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -135, -135, -135, -135, -135, -135, 43, 43, 43, - 43, 43, 43, 43, 43, 161, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -135, -135, -135, -135, -135 }, @@ -2738,10 +2899,10 @@ static yyconst flex_int16_t yy_nxt[][128] = -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, -136, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 164, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -136, -136, -136, -136, -136, -136, 43, 43, 43, - 43, 162, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -136, -136, -136, -136, -136 @@ -2756,7 +2917,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, -137, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 165, 43, 43, 43, 43, 43, 43, 43, -137, -137, -137, -137, -137, -137, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, @@ -2776,8 +2937,8 @@ static yyconst flex_int16_t yy_nxt[][128] = 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -138, -138, -138, -138, -138, -138, 43, 43, 43, - 43, 163, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 166, 43, 43, 43, 43, 43, 43, 43, -138, -138, -138, -138, -138 }, @@ -2791,10 +2952,10 @@ static yyconst flex_int16_t yy_nxt[][128] = -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, -139, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 167, 43, 43, 43, 43, 43, 43, 43, -139, -139, -139, -139, -139, -139, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 164, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -139, -139, -139, -139, -139 }, @@ -2811,8 +2972,8 @@ static yyconst flex_int16_t yy_nxt[][128] = 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -140, -140, -140, -140, -140, -140, 43, 43, 43, - 43, 165, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 168, 43, 43, 43, 43, 43, 43, -140, -140, -140, -140, -140 }, @@ -2828,7 +2989,7 @@ static yyconst flex_int16_t yy_nxt[][128] = 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -141, -141, -141, -141, -141, -141, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 166, 43, + 43, 43, 43, 43, 43, 169, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -141, -141, -141, -141, -141 }, @@ -2844,8 +3005,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -142, -142, -142, -142, -142, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, -142, -142, -142, -142, -142, -142, 167, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, -142, -142, -142, -142, -142, -142, 43, 43, 43, + 43, 170, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -142, -142, -142, -142, -142 }, @@ -2864,7 +3025,7 @@ static yyconst flex_int16_t yy_nxt[][128] = 43, -143, -143, -143, -143, -143, -143, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 168, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -143, -143, -143, -143, -143 }, @@ -2880,8 +3041,8 @@ static yyconst flex_int16_t yy_nxt[][128] = 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -144, -144, -144, -144, -144, -144, 43, 43, 43, + 43, 171, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 69, 43, 43, 43, 43, 43, 43, 43, -144, -144, -144, -144, -144 }, @@ -2894,11 +3055,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, -145, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 169, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, -145, -145, -145, -145, -145, -145, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, -145, -145, -145, -145, -145, -145, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 172, 43, 43, 43, 43, 43, 43, 43, 43, -145, -145, -145, -145, -145 }, @@ -2910,11 +3071,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, -146, - -146, -146, -146, -146, -146, 43, 43, 170, 43, 43, + -146, -146, -146, -146, -146, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -146, -146, -146, -146, -146, -146, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 173, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -146, -146, -146, -146, -146 @@ -2927,12 +3088,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, -147, - -147, -147, -147, -147, -147, 43, 43, 43, 43, 171, + -147, -147, -147, -147, -147, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -147, -147, -147, -147, -147, -147, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 174, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -147, -147, -147, -147, -147 }, @@ -2946,9 +3107,9 @@ static yyconst flex_int16_t yy_nxt[][128] = -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, -148, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 172, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, -148, -148, -148, -148, -148, -148, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, -148, -148, -148, -148, -148, -148, 175, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -148, -148, -148, -148, -148 @@ -2967,7 +3128,7 @@ static yyconst flex_int16_t yy_nxt[][128] = 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -149, -149, -149, -149, -149, -149, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 173, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 176, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -149, -149, -149, -149, -149 }, @@ -2981,11 +3142,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, -150, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 174, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -150, -150, -150, -150, -150, -150, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 71, 43, 43, 43, 43, 43, 43, 43, -150, -150, -150, -150, -150 }, @@ -2998,8 +3159,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, -151, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 177, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 175, 43, 43, 43, 43, 43, 43, 43, -151, -151, -151, -151, -151, -151, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, @@ -3014,8 +3175,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, -152, - -152, -152, -152, -152, -152, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 176, 43, 43, 43, + -152, -152, -152, -152, -152, 43, 43, 178, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -152, -152, -152, -152, -152, -152, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, @@ -3033,11 +3194,11 @@ static yyconst flex_int16_t yy_nxt[][128] = -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, -153, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 177, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -153, -153, -153, -153, -153, -153, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 179, 43, 43, 43, 43, 43, 43, 43, 43, -153, -153, -153, -153, -153 }, @@ -3048,12 +3209,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, -154, - -154, -154, -154, -154, -154, 43, 43, 43, 43, 43, + -154, -154, -154, -154, -154, 43, 43, 43, 43, 180, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -154, -154, -154, -154, -154, -154, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 178, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -154, -154, -154, -154, -154 }, @@ -3067,10 +3228,10 @@ static yyconst flex_int16_t yy_nxt[][128] = -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, -155, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 179, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, -155, -155, -155, -155, -155, -155, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, -155, -155, -155, -155, -155, -155, 43, 43, 43, + 43, 181, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -155, -155, -155, -155, -155 }, @@ -3083,8 +3244,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, -156, - -156, -156, -156, -156, -156, 43, 43, 43, 180, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + -156, -156, -156, -156, -156, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 182, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -156, -156, -156, -156, -156, -156, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, @@ -3106,7 +3267,7 @@ static yyconst flex_int16_t yy_nxt[][128] = 43, -157, -157, -157, -157, -157, -157, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 183, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -157, -157, -157, -157, -157 }, @@ -3120,7 +3281,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -158, -158, -158, -158, -158, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 184, 43, 43, 43, 43, 43, 43, 43, -158, -158, -158, -158, -158, -158, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, @@ -3137,7 +3298,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, -159, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 185, 43, 43, 43, 43, 43, 43, 43, -159, -159, -159, -159, -159, -159, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, @@ -3153,12 +3314,12 @@ static yyconst flex_int16_t yy_nxt[][128] = -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, -160, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 186, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -160, -160, -160, -160, -160, -160, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 181, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -160, -160, -160, -160, -160 }, @@ -3172,10 +3333,10 @@ static yyconst flex_int16_t yy_nxt[][128] = -161, -161, -161, -161, -161, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 187, 43, 43, 43, 43, 43, 43, 43, -161, -161, -161, -161, -161, -161, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 182, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -161, -161, -161, -161, -161 }, @@ -3190,8 +3351,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -162, -162, -162, -162, -162, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, -162, -162, -162, -162, -162, -162, 43, 43, 183, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, -162, -162, -162, -162, -162, -162, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 188, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -162, -162, -162, -162, -162 }, @@ -3205,10 +3366,10 @@ static yyconst flex_int16_t yy_nxt[][128] = -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, -163, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 189, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -163, -163, -163, -163, -163, -163, 43, 43, 43, - 43, 184, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -163, -163, -163, -163, -163 @@ -3221,13 +3382,13 @@ static yyconst flex_int16_t yy_nxt[][128] = -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, -164, - -164, -164, -164, -164, -164, 43, 43, 43, 43, 43, + -164, -164, -164, -164, -164, 43, 43, 43, 190, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -164, -164, -164, -164, -164, -164, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 185, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -164, -164, -164, -164, -164 }, @@ -3244,7 +3405,7 @@ static yyconst flex_int16_t yy_nxt[][128] = 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -165, -165, -165, -165, -165, -165, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 186, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -165, -165, -165, -165, -165 }, @@ -3261,7 +3422,7 @@ static yyconst flex_int16_t yy_nxt[][128] = 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -166, -166, -166, -166, -166, -166, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 177, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -166, -166, -166, -166, -166 }, @@ -3279,7 +3440,7 @@ static yyconst flex_int16_t yy_nxt[][128] = 43, -167, -167, -167, -167, -167, -167, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 187, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -167, -167, -167, -167, -167 }, @@ -3295,8 +3456,8 @@ static yyconst flex_int16_t yy_nxt[][128] = 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -168, -168, -168, -168, -168, -168, 43, 43, 43, - 188, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 191, 43, 43, 43, 43, 43, 43, 43, 43, -168, -168, -168, -168, -168 }, @@ -3310,10 +3471,10 @@ static yyconst flex_int16_t yy_nxt[][128] = -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, -169, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 189, 43, 43, 43, 43, 43, 43, - 43, -169, -169, -169, -169, -169, -169, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, -169, -169, -169, -169, -169, -169, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 192, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -169, -169, -169, -169, -169 }, @@ -3327,8 +3488,8 @@ static yyconst flex_int16_t yy_nxt[][128] = -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, -170, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 190, 43, 43, 43, 43, 43, 43, - 43, -170, -170, -170, -170, -170, -170, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, -170, -170, -170, -170, -170, -170, 43, 43, 193, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, @@ -3345,9 +3506,9 @@ static yyconst flex_int16_t yy_nxt[][128] = -171, -171, -171, -171, -171, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 191, 43, 43, 43, 43, 43, 43, - 43, -171, -171, -171, -171, -171, -171, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, -171, -171, -171, -171, -171, -171, 43, 43, 43, + 43, 194, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -171, -171, -171, -171, -171 }, @@ -3362,10 +3523,10 @@ static yyconst flex_int16_t yy_nxt[][128] = -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, -172, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 192, 43, 43, 43, 43, 43, 43, - 43, -172, -172, -172, -172, -172, -172, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, -172, -172, -172, -172, -172, -172, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 195, 43, 43, 43, 43, 43, 43, 43, -172, -172, -172, -172, -172 }, @@ -3383,7 +3544,7 @@ static yyconst flex_int16_t yy_nxt[][128] = 43, -173, -173, -173, -173, -173, -173, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 193, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 196, 43, 43, 43, 43, 43, 43, 43, -173, -173, -173, -173, -173 }, @@ -3400,7 +3561,7 @@ static yyconst flex_int16_t yy_nxt[][128] = 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -174, -174, -174, -174, -174, -174, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 187, 43, 43, 43, 43, 43, 43, 43, -174, -174, -174, -174, -174 }, @@ -3417,7 +3578,7 @@ static yyconst flex_int16_t yy_nxt[][128] = 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -175, -175, -175, -175, -175, -175, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 197, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -175, -175, -175, -175, -175 }, @@ -3431,9 +3592,9 @@ static yyconst flex_int16_t yy_nxt[][128] = -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, -176, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 194, 43, 43, 43, 43, 43, 43, - 43, -176, -176, -176, -176, -176, -176, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, -176, -176, -176, -176, -176, -176, 43, 43, 43, + 198, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -176, -176, -176, -176, -176 @@ -3448,7 +3609,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 199, 43, 43, 43, 43, 43, 43, 43, -177, -177, -177, -177, -177, -177, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, @@ -3466,10 +3627,10 @@ static yyconst flex_int16_t yy_nxt[][128] = -178, -178, -178, -178, -178, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 200, 43, 43, 43, 43, 43, 43, 43, -178, -178, -178, -178, -178, -178, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 195, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -178, -178, -178, -178, -178 }, @@ -3483,10 +3644,10 @@ static yyconst flex_int16_t yy_nxt[][128] = -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, -179, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 196, 43, 43, 43, 43, 43, 43, - 43, -179, -179, -179, -179, -179, -179, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, -179, -179, -179, -179, -179, -179, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 201, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -179, -179, -179, -179, -179 }, @@ -3500,7 +3661,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, -180, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 197, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 202, 43, 43, 43, 43, 43, 43, 43, -180, -180, -180, -180, -180, -180, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, @@ -3521,7 +3682,7 @@ static yyconst flex_int16_t yy_nxt[][128] = 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -181, -181, -181, -181, -181, -181, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 198, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 203, 43, 43, 43, 43, 43, 43, 43, -181, -181, -181, -181, -181 }, @@ -3535,10 +3696,10 @@ static yyconst flex_int16_t yy_nxt[][128] = -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, -182, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 204, 43, 43, 43, 43, 43, 43, 43, -182, -182, -182, -182, -182, -182, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 189, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -182, -182, -182, -182, -182 }, @@ -3556,7 +3717,7 @@ static yyconst flex_int16_t yy_nxt[][128] = 43, -183, -183, -183, -183, -183, -183, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 190, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 205, 43, 43, 43, 43, 43, 43, 43, -183, -183, -183, -183, -183 }, @@ -3573,7 +3734,7 @@ static yyconst flex_int16_t yy_nxt[][128] = 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -184, -184, -184, -184, -184, -184, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 199, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -184, -184, -184, -184, -184 }, @@ -3604,7 +3765,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, -186, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 206, 43, 43, 43, 43, 43, 43, 43, -186, -186, -186, -186, -186, -186, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, @@ -3625,7 +3786,7 @@ static yyconst flex_int16_t yy_nxt[][128] = 43, -187, -187, -187, -187, -187, -187, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 200, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -187, -187, -187, -187, -187 }, @@ -3642,7 +3803,7 @@ static yyconst flex_int16_t yy_nxt[][128] = 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -188, -188, -188, -188, -188, -188, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 201, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 207, 43, 43, 43, 43, 43, 43, 43, -188, -188, -188, -188, -188 }, @@ -3656,7 +3817,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, -189, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 208, 43, 43, 43, 43, 43, 43, 43, -189, -189, -189, -189, -189, -189, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, @@ -3673,7 +3834,7 @@ static yyconst flex_int16_t yy_nxt[][128] = -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, -190, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 209, 43, 43, 43, 43, 43, 43, 43, -190, -190, -190, -190, -190, -190, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, @@ -3694,7 +3855,7 @@ static yyconst flex_int16_t yy_nxt[][128] = 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -191, -191, -191, -191, -191, -191, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 210, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -191, -191, -191, -191, -191 }, @@ -3711,7 +3872,7 @@ static yyconst flex_int16_t yy_nxt[][128] = 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -192, -192, -192, -192, -192, -192, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 199, 43, 43, 43, 43, 43, 43, 43, -192, -192, -192, -192, -192 }, @@ -3729,7 +3890,7 @@ static yyconst flex_int16_t yy_nxt[][128] = 43, -193, -193, -193, -193, -193, -193, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 200, 43, 43, 43, 43, 43, 43, 43, -193, -193, -193, -193, -193 }, @@ -3746,7 +3907,7 @@ static yyconst flex_int16_t yy_nxt[][128] = 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -194, -194, -194, -194, -194, -194, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 211, 43, 43, 43, 43, 43, 43, 43, -194, -194, -194, -194, -194 }, @@ -3798,7 +3959,7 @@ static yyconst flex_int16_t yy_nxt[][128] = 43, -197, -197, -197, -197, -197, -197, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 212, 43, 43, 43, 43, 43, 43, 43, -197, -197, -197, -197, -197 }, @@ -3814,8 +3975,8 @@ static yyconst flex_int16_t yy_nxt[][128] = 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -198, -198, -198, -198, -198, -198, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 202, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 213, 43, 43, 43, 43, 43, 43, 43, -198, -198, -198, -198, -198 }, @@ -3866,7 +4027,7 @@ static yyconst flex_int16_t yy_nxt[][128] = 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -201, -201, -201, -201, -201, -201, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 214, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -201, -201, -201, -201, -201 }, @@ -3888,23 +4049,265 @@ static yyconst flex_int16_t yy_nxt[][128] = 43, 43, 43, -202, -202, -202, -202, -202 }, + { + 7, -203, -203, -203, -203, -203, -203, -203, -203, -203, + + -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, + -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, + -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, + -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, + -203, -203, -203, -203, -203, -203, -203, -203, -203, -203, + -203, -203, -203, -203, -203, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, -203, -203, -203, -203, -203, -203, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, -203, -203, -203, -203, -203 + }, + + { + 7, -204, -204, -204, -204, -204, -204, -204, -204, -204, + -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, + -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, + -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, + -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, + -204, -204, -204, -204, -204, -204, -204, -204, -204, -204, + -204, -204, -204, -204, -204, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, -204, -204, -204, -204, -204, -204, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, -204, -204, -204, -204, -204 + }, + + { + 7, -205, -205, -205, -205, -205, -205, -205, -205, -205, + -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, + -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, + -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, + -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, + + -205, -205, -205, -205, -205, -205, -205, -205, -205, -205, + -205, -205, -205, -205, -205, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, -205, -205, -205, -205, -205, -205, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, -205, -205, -205, -205, -205 + }, + + { + 7, -206, -206, -206, -206, -206, -206, -206, -206, -206, + -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, + + -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, + -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, + -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, + -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, + -206, -206, -206, -206, -206, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, -206, -206, -206, -206, -206, -206, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + + 43, 43, 43, -206, -206, -206, -206, -206 + }, + + { + 7, -207, -207, -207, -207, -207, -207, -207, -207, -207, + -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, + -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, + -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, + -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, + -207, -207, -207, -207, -207, -207, -207, -207, -207, -207, + -207, -207, -207, -207, -207, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + + 43, -207, -207, -207, -207, -207, -207, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, -207, -207, -207, -207, -207 + }, + + { + 7, -208, -208, -208, -208, -208, -208, -208, -208, -208, + -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, + -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, + -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, + -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, + -208, -208, -208, -208, -208, -208, -208, -208, -208, -208, + + -208, -208, -208, -208, -208, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, -208, -208, -208, -208, -208, -208, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, -208, -208, -208, -208, -208 + }, + + { + 7, -209, -209, -209, -209, -209, -209, -209, -209, -209, + -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, + -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, + + -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, + -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, + -209, -209, -209, -209, -209, -209, -209, -209, -209, -209, + -209, -209, -209, -209, -209, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, -209, -209, -209, -209, -209, -209, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, -209, -209, -209, -209, -209 + + }, + + { + 7, -210, -210, -210, -210, -210, -210, -210, -210, -210, + -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, + -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, + -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, + -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, + -210, -210, -210, -210, -210, -210, -210, -210, -210, -210, + -210, -210, -210, -210, -210, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, -210, -210, -210, -210, -210, -210, 43, 43, 43, + + 43, 43, 43, 43, 43, 43, 43, 43, 43, 215, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, -210, -210, -210, -210, -210 + }, + + { + 7, -211, -211, -211, -211, -211, -211, -211, -211, -211, + -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, + -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, + -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, + -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, + -211, -211, -211, -211, -211, -211, -211, -211, -211, -211, + -211, -211, -211, -211, -211, 43, 43, 43, 43, 43, + + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, -211, -211, -211, -211, -211, -211, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, -211, -211, -211, -211, -211 + }, + + { + 7, -212, -212, -212, -212, -212, -212, -212, -212, -212, + -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, + -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, + -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, + + -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, + -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, + -212, -212, -212, -212, -212, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, -212, -212, -212, -212, -212, -212, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, -212, -212, -212, -212, -212 + }, + + { + 7, -213, -213, -213, -213, -213, -213, -213, -213, -213, + + -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, + -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, + -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, + -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, + -213, -213, -213, -213, -213, -213, -213, -213, -213, -213, + -213, -213, -213, -213, -213, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, -213, -213, -213, -213, -213, -213, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, -213, -213, -213, -213, -213 + }, + + { + 7, -214, -214, -214, -214, -214, -214, -214, -214, -214, + -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, + -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, + -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, + -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, + -214, -214, -214, -214, -214, -214, -214, -214, -214, -214, + -214, -214, -214, -214, -214, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, -214, -214, -214, -214, -214, -214, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 216, 43, 43, 43, 43, + 43, 43, 43, -214, -214, -214, -214, -214 + }, + + { + 7, -215, -215, -215, -215, -215, -215, -215, -215, -215, + -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, + -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, + -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, + -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, + + -215, -215, -215, -215, -215, -215, -215, -215, -215, -215, + -215, -215, -215, -215, -215, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, -215, -215, -215, -215, -215, -215, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 216, 43, 43, 43, 43, + 43, 43, 43, -215, -215, -215, -215, -215 + }, + + { + 7, -216, -216, -216, -216, -216, -216, -216, -216, -216, + -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, + + -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, + -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, + -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, + -216, -216, -216, -216, -216, -216, -216, -216, -216, -216, + -216, -216, -216, -216, -216, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, -216, -216, -216, -216, -216, -216, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + + 43, 43, 43, -216, -216, -216, -216, -216 + }, + } ; -static yy_state_type yy_get_previous_state (void ); -static yy_state_type yy_try_NUL_trans (yy_state_type current_state ); -static int yy_get_next_buffer (void ); -static void yy_fatal_error (yyconst char msg[] ); +static yy_state_type yy_get_previous_state ( yyscan_t yyscanner ); +static yy_state_type yy_try_NUL_trans ( yy_state_type current_state , yyscan_t yyscanner); +static int yy_get_next_buffer ( yyscan_t yyscanner ); +static void yynoreturn yy_fatal_error ( const char* msg , yyscan_t yyscanner ); /* Done after the current pattern has been matched and before the - * corresponding action - sets up wcsutrntext. + * corresponding action - sets up yytext. */ #define YY_DO_BEFORE_ACTION \ - (yytext_ptr) = yy_bp; \ - wcsutrnleng = (size_t) (yy_cp - yy_bp); \ - (yy_hold_char) = *yy_cp; \ + yyg->yytext_ptr = yy_bp; \ + yyleng = (int) (yy_cp - yy_bp); \ + yyg->yy_hold_char = *yy_cp; \ *yy_cp = '\0'; \ - (yy_c_buf_p) = yy_cp; - + yyg->yy_c_buf_p = yy_cp; #define YY_NUM_RULES 37 #define YY_END_OF_BUFFER 38 /* This struct is not used in this scanner, @@ -3914,44 +4317,45 @@ struct yy_trans_info flex_int32_t yy_verify; flex_int32_t yy_nxt; }; -static yyconst flex_int16_t yy_accept[203] = +static const flex_int16_t yy_accept[217] = { 0, 0, 0, 0, 0, 36, 36, 38, 3, 2, 31, 31, 31, 10, 31, 14, 31, 31, 20, 31, 31, 31, 28, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 2, 1, 35, 37, 35, 32, - 36, 2, 31, 31, 31, 31, 31, 31, 31, 13, - 15, 17, 31, 31, 19, 31, 31, 31, 31, 31, - 31, 31, 31, 31, 31, 31, 31, 31, 30, 31, - 31, 31, 31, 31, 31, 31, 31, 31, 2, 1, - 33, 33, 34, 32, 36, 31, 31, 31, 9, 11, - 12, 31, 16, 31, 31, 22, 21, 23, 31, 31, - - 31, 26, 27, 31, 31, 31, 31, 31, 9, 31, - 31, 31, 31, 27, 31, 31, 31, 7, 8, 9, + 36, 2, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 13, 15, 17, 31, 31, 19, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, - 29, 29, 30, 31, 31, 31, 9, 31, 31, 31, - 31, 31, 31, 30, 31, 31, 31, 31, 31, 20, - 20, 31, 25, 31, 31, 31, 29, 29, 30, 31, - 31, 31, 31, 20, 20, 31, 31, 31, 5, 6, + 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 2, 1, 33, 33, 34, 32, 36, 31, 31, 31, + 31, 9, 11, 11, 12, 31, 16, 31, 31, 22, + + 21, 23, 31, 31, 31, 26, 27, 31, 31, 31, + 31, 31, 9, 31, 31, 31, 31, 27, 31, 31, + 31, 31, 7, 8, 9, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 29, 29, 30, 31, + 31, 31, 9, 31, 31, 31, 31, 31, 31, 30, + 31, 31, 31, 31, 31, 31, 31, 20, 20, 31, + 25, 31, 31, 31, 29, 29, 30, 31, 31, 31, + 31, 20, 20, 31, 31, 31, 5, 6, 31, 11, 11, 18, 18, 20, 20, 24, 25, 24, 26, 27, 31, 31, 31, 11, 20, 20, 26, 27, 5, 6, - 11, 18, 18, 24, 24, 26, 27, 31, 11, 26, - 27, 4 + 31, 11, 11, 18, 18, 24, 24, 26, 27, 31, + 11, 26, 27, 31, 4, 4 } ; -static yyconst yy_state_type yy_NUL_trans[203] = +static const yy_state_type yy_NUL_trans[217] = { 0, 8, 8, 37, 37, 41, 41, 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, 81, 0, - 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 83, 0, + 87, 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, 81, 0, 0, 85, 0, 0, 0, 0, 0, + 0, 0, 0, 83, 0, 0, 87, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -3965,12 +4369,10 @@ static yyconst yy_state_type yy_NUL_trans[203] = 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 } ; -extern int wcsutrn_flex_debug; -int wcsutrn_flex_debug = 0; - /* The intent behind this definition is that it'll catch * any uses of REJECT which flex missed. */ @@ -3978,12 +4380,10 @@ int wcsutrn_flex_debug = 0; #define yymore() yymore_used_but_not_detected #define YY_MORE_ADJ 0 #define YY_RESTORE_YY_MORE_OFFSET -char *wcsutrntext; #line 1 "wcsutrn.l" /*============================================================================ - - WCSLIB 4.14 - an implementation of the FITS WCS standard. - Copyright (C) 1995-2012, Mark Calabretta + WCSLIB 8.6 - an implementation of the FITS WCS standard. + Copyright (C) 1995-2025, Mark Calabretta This file is part of WCSLIB. @@ -4000,11 +4400,9 @@ char *wcsutrntext; You should have received a copy of the GNU Lesser General Public License along with WCSLIB. If not, see http://www.gnu.org/licenses. - Direct correspondence concerning WCSLIB to mark@calabretta.id.au - Author: Mark Calabretta, Australia Telescope National Facility, CSIRO. - http://www.atnf.csiro.au/people/Mark.Calabretta - $Id: wcsutrn.c,v 4.14 2012/07/13 10:02:31 cal103 Exp $ + http://www.atnf.csiro.au/computing/software/wcs + $Id: wcsutrn.c,v 8.6 2026/03/29 13:53:56 mcalabre Exp $ *============================================================================= * * wcsutrn.l is a Flex description file containing the definition of a lexical @@ -4017,13 +4415,10 @@ char *wcsutrntext; * *===========================================================================*/ /* Options. */ +#define YY_NO_INPUT 1 /* Exclusive start states. */ #line 49 "wcsutrn.l" -/* To get the prototype for fileno() from stdio.h when gcc is invoked with - * -std=c89 (same as -ansi) or -std=c99 since we do not define YY_INPUT. */ -#define _POSIX_SOURCE 1 - #include #include #include @@ -4032,13 +4427,26 @@ char *wcsutrntext; #include "wcserr.h" #include "wcsunits.h" -#define YY_DECL int wcsutrne(int ctrl, char unitstr[], struct wcserr **err) +// User data associated with yyscanner. +struct wcsutrn_extra { + // Used in preempting the call to exit() by yy_fatal_error(). + jmp_buf abort_jmp_env; +}; + +#define YY_DECL int wcsutrne_scanner(int ctrl, char unitstr[], \ + struct wcserr **err, yyscan_t yyscanner) + +// Dummy definition to circumvent compiler warnings. +#define YY_INPUT(inbuff, count, bufsize) { count = YY_NULL; } + +// Preempt the call to exit() by yy_fatal_error(). +#define exit(status) longjmp(yyextra->abort_jmp_env, status); -/* Used in preempting the call to exit() by yy_fatal_error(). */ -jmp_buf wcsutrn_abort_jmp_env; -#define exit(status) longjmp(wcsutrn_abort_jmp_env, status) +// Internal helper functions. +static YY_DECL; -#line 4042 "wcsutrn.c" +#line 4449 "wcsutrn.c" +#line 4450 "wcsutrn.c" #define INITIAL 0 #define NEXT 1 @@ -4052,40 +4460,80 @@ jmp_buf wcsutrn_abort_jmp_env; #include #endif -#ifndef YY_EXTRA_TYPE -#define YY_EXTRA_TYPE void * -#endif +#define YY_EXTRA_TYPE struct wcsutrn_extra * + +/* Holds the entire state of the reentrant scanner. */ +struct yyguts_t + { + + /* User-defined. Not touched by flex. */ + YY_EXTRA_TYPE yyextra_r; + + /* The rest are the same as the globals declared in the non-reentrant scanner. */ + FILE *yyin_r, *yyout_r; + size_t yy_buffer_stack_top; /**< index of top of stack. */ + size_t yy_buffer_stack_max; /**< capacity of stack. */ + YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */ + char yy_hold_char; + int yy_n_chars; + int yyleng_r; + char *yy_c_buf_p; + int yy_init; + int yy_start; + int yy_did_buffer_switch_on_eof; + int yy_start_stack_ptr; + int yy_start_stack_depth; + int *yy_start_stack; + yy_state_type yy_last_accepting_state; + char* yy_last_accepting_cpos; + + int yylineno_r; + int yy_flex_debug_r; + + char *yytext_r; + int yy_more_flag; + int yy_more_len; -static int yy_init_globals (void ); + }; /* end struct yyguts_t */ + +static int yy_init_globals ( yyscan_t yyscanner ); + +int yylex_init (yyscan_t* scanner); + +int yylex_init_extra ( YY_EXTRA_TYPE user_defined, yyscan_t* scanner); /* Accessor methods to globals. These are made visible to non-reentrant scanners for convenience. */ -int wcsutrnlex_destroy (void ); +int yylex_destroy ( yyscan_t yyscanner ); -int wcsutrnget_debug (void ); +int yyget_debug ( yyscan_t yyscanner ); -void wcsutrnset_debug (int debug_flag ); +void yyset_debug ( int debug_flag , yyscan_t yyscanner ); -YY_EXTRA_TYPE wcsutrnget_extra (void ); +YY_EXTRA_TYPE yyget_extra ( yyscan_t yyscanner ); -void wcsutrnset_extra (YY_EXTRA_TYPE user_defined ); +void yyset_extra ( YY_EXTRA_TYPE user_defined , yyscan_t yyscanner ); -FILE *wcsutrnget_in (void ); +FILE *yyget_in ( yyscan_t yyscanner ); -void wcsutrnset_in (FILE * in_str ); +void yyset_in ( FILE * _in_str , yyscan_t yyscanner ); -FILE *wcsutrnget_out (void ); +FILE *yyget_out ( yyscan_t yyscanner ); -void wcsutrnset_out (FILE * out_str ); +void yyset_out ( FILE * _out_str , yyscan_t yyscanner ); -int wcsutrnget_leng (void ); + int yyget_leng ( yyscan_t yyscanner ); -char *wcsutrnget_text (void ); +char *yyget_text ( yyscan_t yyscanner ); -int wcsutrnget_lineno (void ); +int yyget_lineno ( yyscan_t yyscanner ); -void wcsutrnset_lineno (int line_number ); +void yyset_lineno ( int _line_number , yyscan_t yyscanner ); + +int yyget_column ( yyscan_t yyscanner ); + +void yyset_column ( int _column_no , yyscan_t yyscanner ); /* Macros after this point can all be overridden by user definitions in * section 1. @@ -4093,28 +4541,31 @@ void wcsutrnset_lineno (int line_number ); #ifndef YY_SKIP_YYWRAP #ifdef __cplusplus -extern "C" int wcsutrnwrap (void ); +extern "C" int yywrap ( yyscan_t yyscanner ); #else -extern int wcsutrnwrap (void ); +extern int yywrap ( yyscan_t yyscanner ); #endif #endif - static void yyunput (int c,char *buf_ptr ); +#ifndef YY_NO_UNPUT + + static void yyunput ( int c, char *buf_ptr , yyscan_t yyscanner); +#endif + #ifndef yytext_ptr -static void yy_flex_strncpy (char *,yyconst char *,int ); +static void yy_flex_strncpy ( char *, const char *, int , yyscan_t yyscanner); #endif #ifdef YY_NEED_STRLEN -static int yy_flex_strlen (yyconst char * ); +static int yy_flex_strlen ( const char * , yyscan_t yyscanner); #endif #ifndef YY_NO_INPUT - #ifdef __cplusplus -static int yyinput (void ); +static int yyinput ( yyscan_t yyscanner ); #else -static int input (void ); +static int input ( yyscan_t yyscanner ); #endif #endif @@ -4134,7 +4585,7 @@ static int input (void ); /* This used to be an fputs(), but since the string might contain NUL's, * we now use fwrite(). */ -#define ECHO do { if (fwrite( wcsutrntext, wcsutrnleng, 1, wcsutrnout )) {} } while (0) +#define ECHO do { if (fwrite( yytext, (size_t) yyleng, 1, yyout )) {} } while (0) #endif /* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, @@ -4143,7 +4594,7 @@ static int input (void ); #ifndef YY_INPUT #define YY_INPUT(buf,result,max_size) \ errno=0; \ - while ( (result = read( fileno(wcsutrnin), (char *) buf, max_size )) < 0 ) \ + while ( (result = (int) read( fileno(yyin), buf, (yy_size_t) max_size )) < 0 ) \ { \ if( errno != EINTR) \ { \ @@ -4151,7 +4602,7 @@ static int input (void ); break; \ } \ errno=0; \ - clearerr(wcsutrnin); \ + clearerr(yyin); \ }\ \ @@ -4172,7 +4623,7 @@ static int input (void ); /* Report a fatal error. */ #ifndef YY_FATAL_ERROR -#define YY_FATAL_ERROR(msg) yy_fatal_error( msg ) +#define YY_FATAL_ERROR(msg) yy_fatal_error( msg , yyscanner) #endif /* end tables serialization structures and prototypes */ @@ -4183,12 +4634,12 @@ static int input (void ); #ifndef YY_DECL #define YY_DECL_IS_OURS 1 -extern int wcsutrnlex (void); +extern int yylex (yyscan_t yyscanner); -#define YY_DECL int wcsutrnlex (void) +#define YY_DECL int yylex (yyscan_t yyscanner) #endif /* !YY_DECL */ -/* Code executed at the beginning of each rule, after wcsutrntext and wcsutrnleng +/* Code executed at the beginning of each rule, after yytext and yyleng * have been set up. */ #ifndef YY_USER_ACTION @@ -4197,44 +4648,74 @@ extern int wcsutrnlex (void); /* Code executed at the end of each rule. */ #ifndef YY_BREAK -#define YY_BREAK break; +#define YY_BREAK /*LINTED*/break; #endif #define YY_RULE_SETUP \ - if ( wcsutrnleng > 0 ) \ + if ( yyleng > 0 ) \ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = \ - (wcsutrntext[wcsutrnleng - 1] == '\n'); \ + (yytext[yyleng - 1] == '\n'); \ YY_USER_ACTION /** The main scanner function which does all the work. */ YY_DECL { - register yy_state_type yy_current_state; - register char *yy_cp, *yy_bp; - register int yy_act; - -#line 69 "wcsutrn.l" + yy_state_type yy_current_state; + char *yy_cp, *yy_bp; + int yy_act; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if ( !yyg->yy_init ) + { + yyg->yy_init = 1; + +#ifdef YY_USER_INIT + YY_USER_INIT; +#endif + + if ( ! yyg->yy_start ) + yyg->yy_start = 1; /* first start state */ + + if ( ! yyin ) + yyin = stdin; - static const char *function = "wcsutrne"; + if ( ! yyout ) + yyout = stdout; + if ( ! YY_CURRENT_BUFFER ) { + yyensure_buffer_stack (yyscanner); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); + } + + yy_load_buffer_state( yyscanner ); + } + + { +#line 77 "wcsutrn.l" + +#line 79 "wcsutrn.l" + static const char *function = "wcsutrne_scanner"; + + if (err) *err = 0x0; + char orig[80], subs[80]; + *orig = '\0'; + *subs = '\0'; + int bracket = 0; int unsafe = 0; int status = -1; - YY_BUFFER_STATE inbuff; - int wcsutrnlex_destroy(void); - - *orig = '\0'; - *subs = '\0'; - inbuff = wcsutrn_scan_string(unitstr); + yy_delete_buffer(YY_CURRENT_BUFFER, yyscanner); + yy_scan_string(unitstr, yyscanner); *unitstr = '\0'; - /* Return here via longjmp() invoked by yy_fatal_error(). */ - if (setjmp(wcsutrn_abort_jmp_env)) { + // Return here via longjmp() invoked by yy_fatal_error(). + if (setjmp(yyextra->abort_jmp_env)) { return wcserr_set(WCSERR_SET(UNITSERR_PARSER_ERROR), - "Internal units translator error parsing '%s'", unitstr); + "Internal units translator error"); } BEGIN(INITIAL); @@ -4243,47 +4724,21 @@ YY_DECL fprintf(stderr, "\n%s ->\n", unitstr); #endif -#line 4247 "wcsutrn.c" +#line 4728 "wcsutrn.c" - if ( !(yy_init) ) + while ( /*CONSTCOND*/1 ) /* loops until end-of-file is reached */ { - (yy_init) = 1; - -#ifdef YY_USER_INIT - YY_USER_INIT; -#endif - - if ( ! (yy_start) ) - (yy_start) = 1; /* first start state */ + yy_cp = yyg->yy_c_buf_p; - if ( ! wcsutrnin ) - wcsutrnin = stdin; - - if ( ! wcsutrnout ) - wcsutrnout = stdout; - - if ( ! YY_CURRENT_BUFFER ) { - wcsutrnensure_buffer_stack (); - YY_CURRENT_BUFFER_LVALUE = - wcsutrn_create_buffer(wcsutrnin,YY_BUF_SIZE ); - } - - wcsutrn_load_buffer_state( ); - } - - while ( 1 ) /* loops until end-of-file is reached */ - { - yy_cp = (yy_c_buf_p); - - /* Support of wcsutrntext. */ - *yy_cp = (yy_hold_char); + /* Support of yytext. */ + *yy_cp = yyg->yy_hold_char; /* yy_bp points to the position in yy_ch_buf of the start of * the current run. */ yy_bp = yy_cp; - yy_current_state = (yy_start); + yy_current_state = yyg->yy_start; yy_current_state += YY_AT_BOL(); yy_match: while ( (yy_current_state = yy_nxt[yy_current_state][ YY_SC_TO_UI(*yy_cp) ]) > 0 ) @@ -4302,349 +4757,348 @@ YY_DECL { /* beginning of action switch */ case 1: YY_RULE_SETUP -#line 97 "wcsutrn.l" +#line 107 "wcsutrn.l" { - /* Looks like a keycomment. */ + // Looks like a keycomment. strcat(unitstr, "["); bracket = 1; } YY_BREAK case 2: YY_RULE_SETUP -#line 103 "wcsutrn.l" -/* Discard leading whitespace. */ +#line 113 "wcsutrn.l" +// Discard leading whitespace. YY_BREAK case 3: /* rule 3 can match eol */ YY_RULE_SETUP -#line 105 "wcsutrn.l" +#line 115 "wcsutrn.l" { - /* Non-alphabetic character. */ - strcat(unitstr, wcsutrntext); - if (bracket && *wcsutrntext == ']') { + // Non-alphabetic character. + strcat(unitstr, yytext); + if (bracket && *yytext == ']') { BEGIN(FLUSH); } } YY_BREAK case 4: YY_RULE_SETUP -#line 113 "wcsutrn.l" +#line 123 "wcsutrn.l" { - strcpy(orig, wcsutrntext); + strcpy(orig, yytext); strcpy(subs, "Angstrom"); BEGIN(NEXT); } YY_BREAK case 5: YY_RULE_SETUP -#line 119 "wcsutrn.l" +#line 129 "wcsutrn.l" { - strcpy(orig, wcsutrntext); + strcpy(orig, yytext); strcpy(subs, "arcmin"); BEGIN(NEXT); } YY_BREAK case 6: YY_RULE_SETUP -#line 125 "wcsutrn.l" +#line 135 "wcsutrn.l" { - strcpy(orig, wcsutrntext); + strcpy(orig, yytext); strcpy(subs, "arcsec"); BEGIN(NEXT); } YY_BREAK case 7: YY_RULE_SETUP -#line 131 "wcsutrn.l" +#line 141 "wcsutrn.l" { - strcpy(orig, wcsutrntext); + strcpy(orig, yytext); strcpy(subs, "beam"); BEGIN(NEXT); } YY_BREAK case 8: YY_RULE_SETUP -#line 137 "wcsutrn.l" +#line 147 "wcsutrn.l" { - strcpy(orig, wcsutrntext); + strcpy(orig, yytext); strcpy(subs, "byte"); BEGIN(NEXT); } YY_BREAK case 9: YY_RULE_SETUP -#line 143 "wcsutrn.l" +#line 153 "wcsutrn.l" { - strcpy(orig, wcsutrntext); + strcpy(orig, yytext); strcpy(subs, "d"); BEGIN(NEXT); } YY_BREAK case 10: YY_RULE_SETUP -#line 149 "wcsutrn.l" +#line 159 "wcsutrn.l" { unsafe = 1; - strcpy(orig, wcsutrntext); + strcpy(orig, yytext); strcpy(subs, (ctrl & 4) ? "d" : "D"); BEGIN(NEXT); } YY_BREAK case 11: YY_RULE_SETUP -#line 156 "wcsutrn.l" +#line 166 "wcsutrn.l" { - strcpy(orig, wcsutrntext); + strcpy(orig, yytext); strcpy(subs, "deg"); BEGIN(NEXT); } YY_BREAK case 12: YY_RULE_SETUP -#line 162 "wcsutrn.l" +#line 172 "wcsutrn.l" { - strcpy(orig, wcsutrntext); + strcpy(orig, yytext); strcpy(subs, "GHz"); BEGIN(NEXT); } YY_BREAK case 13: YY_RULE_SETUP -#line 168 "wcsutrn.l" +#line 178 "wcsutrn.l" { - strcpy(orig, wcsutrntext); + strcpy(orig, yytext); strcpy(subs, "h"); BEGIN(NEXT); } YY_BREAK case 14: YY_RULE_SETUP -#line 174 "wcsutrn.l" +#line 184 "wcsutrn.l" { unsafe = 1; - strcpy(orig, wcsutrntext); + strcpy(orig, yytext); strcpy(subs, (ctrl & 2) ? "h" : "H"); BEGIN(NEXT); } YY_BREAK case 15: YY_RULE_SETUP -#line 181 "wcsutrn.l" +#line 191 "wcsutrn.l" { - strcpy(orig, wcsutrntext); + strcpy(orig, yytext); strcpy(subs, "Hz"); BEGIN(NEXT); } YY_BREAK case 16: YY_RULE_SETUP -#line 187 "wcsutrn.l" +#line 197 "wcsutrn.l" { - strcpy(orig, wcsutrntext); + strcpy(orig, yytext); strcpy(subs, "kHz"); BEGIN(NEXT); } YY_BREAK case 17: YY_RULE_SETUP -#line 193 "wcsutrn.l" +#line 203 "wcsutrn.l" { - strcpy(orig, wcsutrntext); + strcpy(orig, yytext); strcpy(subs, "Jy"); BEGIN(NEXT); } YY_BREAK case 18: YY_RULE_SETUP -#line 199 "wcsutrn.l" +#line 209 "wcsutrn.l" { - strcpy(orig, wcsutrntext); + strcpy(orig, yytext); strcpy(subs, "K"); BEGIN(NEXT); } YY_BREAK case 19: YY_RULE_SETUP -#line 205 "wcsutrn.l" +#line 215 "wcsutrn.l" { - strcpy(orig, wcsutrntext); + strcpy(orig, yytext); strcpy(subs, "km"); BEGIN(NEXT); } YY_BREAK case 20: YY_RULE_SETUP -#line 211 "wcsutrn.l" +#line 221 "wcsutrn.l" { - strcpy(orig, wcsutrntext); + strcpy(orig, yytext); strcpy(subs, "m"); BEGIN(NEXT); } YY_BREAK case 21: YY_RULE_SETUP -#line 217 "wcsutrn.l" +#line 227 "wcsutrn.l" { - strcpy(orig, wcsutrntext); + strcpy(orig, yytext); strcpy(subs, "min"); BEGIN(NEXT); } YY_BREAK case 22: YY_RULE_SETUP -#line 223 "wcsutrn.l" +#line 233 "wcsutrn.l" { - strcpy(orig, wcsutrntext); + strcpy(orig, yytext); strcpy(subs, "MHz"); BEGIN(NEXT); } YY_BREAK case 23: YY_RULE_SETUP -#line 229 "wcsutrn.l" +#line 239 "wcsutrn.l" { - strcpy(orig, wcsutrntext); + strcpy(orig, yytext); strcpy(subs, "ohm"); BEGIN(NEXT); } YY_BREAK case 24: YY_RULE_SETUP -#line 235 "wcsutrn.l" +#line 245 "wcsutrn.l" { - strcpy(orig, wcsutrntext); + strcpy(orig, yytext); strcpy(subs, "Pa"); BEGIN(NEXT); } YY_BREAK case 25: YY_RULE_SETUP -#line 241 "wcsutrn.l" +#line 251 "wcsutrn.l" { - strcpy(orig, wcsutrntext); + strcpy(orig, yytext); strcpy(subs, "pixel"); BEGIN(NEXT); } YY_BREAK case 26: YY_RULE_SETUP -#line 247 "wcsutrn.l" +#line 257 "wcsutrn.l" { - strcpy(orig, wcsutrntext); + strcpy(orig, yytext); strcpy(subs, "rad"); BEGIN(NEXT); } YY_BREAK case 27: YY_RULE_SETUP -#line 253 "wcsutrn.l" +#line 263 "wcsutrn.l" { - strcpy(orig, wcsutrntext); + strcpy(orig, yytext); strcpy(subs, "s"); BEGIN(NEXT); } YY_BREAK case 28: YY_RULE_SETUP -#line 259 "wcsutrn.l" +#line 269 "wcsutrn.l" { unsafe = 1; - strcpy(orig, wcsutrntext); + strcpy(orig, yytext); strcpy(subs, (ctrl & 1) ? "s" : "S"); BEGIN(NEXT); } YY_BREAK case 29: YY_RULE_SETUP -#line 266 "wcsutrn.l" +#line 276 "wcsutrn.l" { - strcpy(orig, wcsutrntext); + strcpy(orig, yytext); strcpy(subs, "V"); BEGIN(NEXT); } YY_BREAK case 30: YY_RULE_SETUP -#line 272 "wcsutrn.l" +#line 282 "wcsutrn.l" { - strcpy(orig, wcsutrntext); + strcpy(orig, yytext); strcpy(subs, "yr"); BEGIN(NEXT); } YY_BREAK case 31: YY_RULE_SETUP -#line 278 "wcsutrn.l" +#line 288 "wcsutrn.l" { - /* Not a recognized alias. */ - strcpy(orig, wcsutrntext); + // Not a recognized alias. + strcpy(orig, yytext); strcpy(subs, orig); BEGIN(NEXT); } YY_BREAK case 32: YY_RULE_SETUP -#line 285 "wcsutrn.l" +#line 295 "wcsutrn.l" { - /* Reject the alias match. */ - strcat(orig, wcsutrntext); + // Reject the alias match. + strcat(orig, yytext); strcpy(subs, orig); } YY_BREAK case 33: /* rule 33 can match eol */ YY_RULE_SETUP -#line 291 "wcsutrn.l" +#line 301 "wcsutrn.l" { - /* Discard separating whitespace. */ - unput(wcsutrntext[wcsutrnleng-1]); + // Discard separating whitespace. + unput(yytext[yyleng-1]); } YY_BREAK case 34: YY_RULE_SETUP -#line 296 "wcsutrn.l" +#line 306 "wcsutrn.l" { - /* Compress separating whitespace. */ + // Compress separating whitespace. strcat(unitstr, subs); strcat(unitstr, " "); if (strcmp(orig, subs)) status = 0; - unput(wcsutrntext[wcsutrnleng-1]); + unput(yytext[yyleng-1]); *subs = '\0'; BEGIN(INITIAL); } YY_BREAK case 35: YY_RULE_SETUP -#line 306 "wcsutrn.l" +#line 316 "wcsutrn.l" { - /* Copy anything else unchanged. */ + // Copy anything else unchanged. strcat(unitstr, subs); if (strcmp(orig, subs)) status = 0; - unput(*wcsutrntext); + unput(*yytext); *subs = '\0'; BEGIN(INITIAL); } YY_BREAK case 36: YY_RULE_SETUP -#line 315 "wcsutrn.l" +#line 325 "wcsutrn.l" { - /* Copy out remaining input. */ - strcat(unitstr, wcsutrntext); + // Copy out remaining input. + strcat(unitstr, yytext); } YY_BREAK case YY_STATE_EOF(INITIAL): case YY_STATE_EOF(NEXT): case YY_STATE_EOF(FLUSH): -#line 320 "wcsutrn.l" +#line 330 "wcsutrn.l" { - /* End-of-string. */ + // End-of-string. if (*subs) { strcat(unitstr, subs); if (strcmp(orig, subs)) status = 0; } - wcsutrnlex_destroy(); if (unsafe) { return wcserr_set(WCSERR_SET(UNITSERR_UNSAFE_TRANS), "Unsafe unit translation in '%s'", unitstr); @@ -4654,33 +5108,33 @@ case YY_STATE_EOF(FLUSH): YY_BREAK case 37: YY_RULE_SETUP -#line 335 "wcsutrn.l" +#line 344 "wcsutrn.l" ECHO; YY_BREAK -#line 4661 "wcsutrn.c" +#line 5115 "wcsutrn.c" case YY_END_OF_BUFFER: { /* Amount of text matched not including the EOB char. */ - int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1; + int yy_amount_of_matched_text = (int) (yy_cp - yyg->yytext_ptr) - 1; /* Undo the effects of YY_DO_BEFORE_ACTION. */ - *yy_cp = (yy_hold_char); + *yy_cp = yyg->yy_hold_char; YY_RESTORE_YY_MORE_OFFSET if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) { /* We're scanning a new file or input source. It's * possible that this happened because the user - * just pointed wcsutrnin at a new source and called - * wcsutrnlex(). If so, then we have to assure + * just pointed yyin at a new source and called + * yylex(). If so, then we have to assure * consistency between YY_CURRENT_BUFFER and our * globals. Here is the right place to do so, because * this is the first action (other than possibly a * back-up) that will match for the new input source. */ - (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; - YY_CURRENT_BUFFER_LVALUE->yy_input_file = wcsutrnin; + yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin; YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; } @@ -4691,13 +5145,13 @@ ECHO; * end-of-buffer state). Contrast this with the test * in input(). */ - if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) + if ( yyg->yy_c_buf_p <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) { /* This was really a NUL. */ yy_state_type yy_next_state; - (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text; + yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text; - yy_current_state = yy_get_previous_state( ); + yy_current_state = yy_get_previous_state( yyscanner ); /* Okay, we're now positioned to make the NUL * transition. We couldn't have @@ -4708,43 +5162,43 @@ ECHO; * will run more slowly). */ - yy_next_state = yy_try_NUL_trans( yy_current_state ); + yy_next_state = yy_try_NUL_trans( yy_current_state , yyscanner); - yy_bp = (yytext_ptr) + YY_MORE_ADJ; + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; if ( yy_next_state ) { /* Consume the NUL. */ - yy_cp = ++(yy_c_buf_p); + yy_cp = ++yyg->yy_c_buf_p; yy_current_state = yy_next_state; goto yy_match; } else { - yy_cp = (yy_c_buf_p); + yy_cp = yyg->yy_c_buf_p; goto yy_find_action; } } - else switch ( yy_get_next_buffer( ) ) + else switch ( yy_get_next_buffer( yyscanner ) ) { case EOB_ACT_END_OF_FILE: { - (yy_did_buffer_switch_on_eof) = 0; + yyg->yy_did_buffer_switch_on_eof = 0; - if ( wcsutrnwrap( ) ) + if ( yywrap( yyscanner ) ) { /* Note: because we've taken care in * yy_get_next_buffer() to have set up - * wcsutrntext, we can now set up + * yytext, we can now set up * yy_c_buf_p so that if some total * hoser (like flex itself) wants to * call the scanner after we return the * YY_NULL, it'll still work - another * YY_NULL will get returned. */ - (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ; + yyg->yy_c_buf_p = yyg->yytext_ptr + YY_MORE_ADJ; yy_act = YY_STATE_EOF(YY_START); goto do_action; @@ -4752,30 +5206,30 @@ ECHO; else { - if ( ! (yy_did_buffer_switch_on_eof) ) + if ( ! yyg->yy_did_buffer_switch_on_eof ) YY_NEW_FILE; } break; } case EOB_ACT_CONTINUE_SCAN: - (yy_c_buf_p) = - (yytext_ptr) + yy_amount_of_matched_text; + yyg->yy_c_buf_p = + yyg->yytext_ptr + yy_amount_of_matched_text; - yy_current_state = yy_get_previous_state( ); + yy_current_state = yy_get_previous_state( yyscanner ); - yy_cp = (yy_c_buf_p); - yy_bp = (yytext_ptr) + YY_MORE_ADJ; + yy_cp = yyg->yy_c_buf_p; + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; goto yy_match; case EOB_ACT_LAST_MATCH: - (yy_c_buf_p) = - &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)]; + yyg->yy_c_buf_p = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars]; - yy_current_state = yy_get_previous_state( ); + yy_current_state = yy_get_previous_state( yyscanner ); - yy_cp = (yy_c_buf_p); - yy_bp = (yytext_ptr) + YY_MORE_ADJ; + yy_cp = yyg->yy_c_buf_p; + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; goto yy_find_action; } break; @@ -4786,7 +5240,8 @@ ECHO; "fatal flex scanner internal error--no action found" ); } /* end of action switch */ } /* end of scanning one token */ -} /* end of wcsutrnlex */ + } /* end of user's declarations */ +} /* end of yylex */ /* yy_get_next_buffer - try to read in a new buffer * @@ -4795,20 +5250,21 @@ ECHO; * EOB_ACT_CONTINUE_SCAN - continue scanning from current position * EOB_ACT_END_OF_FILE - end of file */ -static int yy_get_next_buffer (void) +static int yy_get_next_buffer (yyscan_t yyscanner) { - register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; - register char *source = (yytext_ptr); - register int number_to_move, i; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; + char *source = yyg->yytext_ptr; + int number_to_move, i; int ret_val; - if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] ) + if ( yyg->yy_c_buf_p > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] ) YY_FATAL_ERROR( "fatal flex scanner internal error--end of buffer missed" ); if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) { /* Don't try to fill the buffer, so this is an EOF. */ - if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 ) + if ( yyg->yy_c_buf_p - yyg->yytext_ptr - YY_MORE_ADJ == 1 ) { /* We matched a single character, the EOB, so * treat this as a final EOF. @@ -4828,7 +5284,7 @@ static int yy_get_next_buffer (void) /* Try to read more data. */ /* First move last chars to start of buffer. */ - number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr)) - 1; + number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr - 1); for ( i = 0; i < number_to_move; ++i ) *(dest++) = *(source++); @@ -4837,7 +5293,7 @@ static int yy_get_next_buffer (void) /* don't do the read, it's not guaranteed to return an EOF, * just force an EOF */ - YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0; + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars = 0; else { @@ -4848,10 +5304,10 @@ static int yy_get_next_buffer (void) { /* Not enough room in the buffer - grow it. */ /* just a shorter name for the current buffer */ - YY_BUFFER_STATE b = YY_CURRENT_BUFFER; + YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE; int yy_c_buf_p_offset = - (int) ((yy_c_buf_p) - b->yy_ch_buf); + (int) (yyg->yy_c_buf_p - b->yy_ch_buf); if ( b->yy_is_our_buffer ) { @@ -4864,17 +5320,18 @@ static int yy_get_next_buffer (void) b->yy_ch_buf = (char *) /* Include room in for 2 EOB chars. */ - wcsutrnrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 ); + yyrealloc( (void *) b->yy_ch_buf, + (yy_size_t) (b->yy_buf_size + 2) , yyscanner ); } else /* Can't grow it, we don't own it. */ - b->yy_ch_buf = 0; + b->yy_ch_buf = NULL; if ( ! b->yy_ch_buf ) YY_FATAL_ERROR( "fatal error - scanner input buffer overflow" ); - (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset]; + yyg->yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset]; num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; @@ -4886,17 +5343,17 @@ static int yy_get_next_buffer (void) /* Read in more data. */ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), - (yy_n_chars), (size_t) num_to_read ); + yyg->yy_n_chars, num_to_read ); - YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; } - if ( (yy_n_chars) == 0 ) + if ( yyg->yy_n_chars == 0 ) { if ( number_to_move == YY_MORE_ADJ ) { ret_val = EOB_ACT_END_OF_FILE; - wcsutrnrestart(wcsutrnin ); + yyrestart( yyin , yyscanner); } else @@ -4910,34 +5367,38 @@ static int yy_get_next_buffer (void) else ret_val = EOB_ACT_CONTINUE_SCAN; - if ((yy_size_t) ((yy_n_chars) + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { + if ((yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { /* Extend the array by 50%, plus the number we really need. */ - yy_size_t new_size = (yy_n_chars) + number_to_move + ((yy_n_chars) >> 1); - YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) wcsutrnrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size ); + int new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1); + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc( + (void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf, (yy_size_t) new_size , yyscanner ); if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); + /* "- 2" to take care of EOB's */ + YY_CURRENT_BUFFER_LVALUE->yy_buf_size = (int) (new_size - 2); } - (yy_n_chars) += number_to_move; - YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR; - YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR; + yyg->yy_n_chars += number_to_move; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] = YY_END_OF_BUFFER_CHAR; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR; - (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; + yyg->yytext_ptr = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; return ret_val; } /* yy_get_previous_state - get the state just before the EOB char was reached */ - static yy_state_type yy_get_previous_state (void) + static yy_state_type yy_get_previous_state (yyscan_t yyscanner) { - register yy_state_type yy_current_state; - register char *yy_cp; - - yy_current_state = (yy_start); + yy_state_type yy_current_state; + char *yy_cp; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + yy_current_state = yyg->yy_start; yy_current_state += YY_AT_BOL(); - for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp ) + for ( yy_cp = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp ) { if ( *yy_cp ) { @@ -4955,32 +5416,37 @@ static int yy_get_next_buffer (void) * synopsis * next_state = yy_try_NUL_trans( current_state ); */ - static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state ) + static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state , yyscan_t yyscanner) { - register int yy_is_jam; - + int yy_is_jam; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */ + yy_current_state = yy_NUL_trans[yy_current_state]; yy_is_jam = (yy_current_state == 0); + (void)yyg; return yy_is_jam ? 0 : yy_current_state; } - static void yyunput (int c, register char * yy_bp ) +#ifndef YY_NO_UNPUT + + static void yyunput (int c, char * yy_bp , yyscan_t yyscanner) { - register char *yy_cp; - - yy_cp = (yy_c_buf_p); + char *yy_cp; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + yy_cp = yyg->yy_c_buf_p; - /* undo effects of setting up wcsutrntext */ - *yy_cp = (yy_hold_char); + /* undo effects of setting up yytext */ + *yy_cp = yyg->yy_hold_char; if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) { /* need to shift things up to make room */ /* +2 for EOB chars. */ - register int number_to_move = (yy_n_chars) + 2; - register char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[ + int number_to_move = yyg->yy_n_chars + 2; + char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[ YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2]; - register char *source = + char *source = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]; while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) @@ -4989,7 +5455,7 @@ static int yy_get_next_buffer (void) yy_cp += (int) (dest - source); yy_bp += (int) (dest - source); YY_CURRENT_BUFFER_LVALUE->yy_n_chars = - (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_buf_size; + yyg->yy_n_chars = (int) YY_CURRENT_BUFFER_LVALUE->yy_buf_size; if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) YY_FATAL_ERROR( "flex scanner push-back overflow" ); @@ -4997,39 +5463,42 @@ static int yy_get_next_buffer (void) *--yy_cp = (char) c; - (yytext_ptr) = yy_bp; - (yy_hold_char) = *yy_cp; - (yy_c_buf_p) = yy_cp; + yyg->yytext_ptr = yy_bp; + yyg->yy_hold_char = *yy_cp; + yyg->yy_c_buf_p = yy_cp; } +#endif + #ifndef YY_NO_INPUT #ifdef __cplusplus - static int yyinput (void) + static int yyinput (yyscan_t yyscanner) #else - static int input (void) + static int input (yyscan_t yyscanner) #endif { int c; - - *(yy_c_buf_p) = (yy_hold_char); + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + *yyg->yy_c_buf_p = yyg->yy_hold_char; - if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR ) + if ( *yyg->yy_c_buf_p == YY_END_OF_BUFFER_CHAR ) { /* yy_c_buf_p now points to the character we want to return. * If this occurs *before* the EOB characters, then it's a * valid NUL; if not, then we've hit the end of the buffer. */ - if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) + if ( yyg->yy_c_buf_p < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) /* This was really a NUL. */ - *(yy_c_buf_p) = '\0'; + *yyg->yy_c_buf_p = '\0'; else { /* need more input */ - int offset = (yy_c_buf_p) - (yytext_ptr); - ++(yy_c_buf_p); + int offset = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr); + ++yyg->yy_c_buf_p; - switch ( yy_get_next_buffer( ) ) + switch ( yy_get_next_buffer( yyscanner ) ) { case EOB_ACT_LAST_MATCH: /* This happens because yy_g_n_b() @@ -5043,34 +5512,34 @@ static int yy_get_next_buffer (void) */ /* Reset buffer status. */ - wcsutrnrestart(wcsutrnin ); + yyrestart( yyin , yyscanner); /*FALLTHROUGH*/ case EOB_ACT_END_OF_FILE: { - if ( wcsutrnwrap( ) ) - return EOF; + if ( yywrap( yyscanner ) ) + return 0; - if ( ! (yy_did_buffer_switch_on_eof) ) + if ( ! yyg->yy_did_buffer_switch_on_eof ) YY_NEW_FILE; #ifdef __cplusplus - return yyinput(); + return yyinput(yyscanner); #else - return input(); + return input(yyscanner); #endif } case EOB_ACT_CONTINUE_SCAN: - (yy_c_buf_p) = (yytext_ptr) + offset; + yyg->yy_c_buf_p = yyg->yytext_ptr + offset; break; } } } - c = *(unsigned char *) (yy_c_buf_p); /* cast for 8-bit char's */ - *(yy_c_buf_p) = '\0'; /* preserve wcsutrntext */ - (yy_hold_char) = *++(yy_c_buf_p); + c = *(unsigned char *) yyg->yy_c_buf_p; /* cast for 8-bit char's */ + *yyg->yy_c_buf_p = '\0'; /* preserve yytext */ + yyg->yy_hold_char = *++yyg->yy_c_buf_p; YY_CURRENT_BUFFER_LVALUE->yy_at_bol = (c == '\n'); @@ -5080,102 +5549,106 @@ static int yy_get_next_buffer (void) /** Immediately switch to a different input stream. * @param input_file A readable stream. - * + * @param yyscanner The scanner object. * @note This function does not reset the start condition to @c INITIAL . */ - void wcsutrnrestart (FILE * input_file ) + void yyrestart (FILE * input_file , yyscan_t yyscanner) { - + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if ( ! YY_CURRENT_BUFFER ){ - wcsutrnensure_buffer_stack (); + yyensure_buffer_stack (yyscanner); YY_CURRENT_BUFFER_LVALUE = - wcsutrn_create_buffer(wcsutrnin,YY_BUF_SIZE ); + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); } - wcsutrn_init_buffer(YY_CURRENT_BUFFER,input_file ); - wcsutrn_load_buffer_state( ); + yy_init_buffer( YY_CURRENT_BUFFER, input_file , yyscanner); + yy_load_buffer_state( yyscanner ); } /** Switch to a different input buffer. * @param new_buffer The new input buffer. - * + * @param yyscanner The scanner object. */ - void wcsutrn_switch_to_buffer (YY_BUFFER_STATE new_buffer ) + void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) { - + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + /* TODO. We should be able to replace this entire function body * with - * wcsutrnpop_buffer_state(); - * wcsutrnpush_buffer_state(new_buffer); + * yypop_buffer_state(); + * yypush_buffer_state(new_buffer); */ - wcsutrnensure_buffer_stack (); + yyensure_buffer_stack (yyscanner); if ( YY_CURRENT_BUFFER == new_buffer ) return; if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ - *(yy_c_buf_p) = (yy_hold_char); - YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); - YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + *yyg->yy_c_buf_p = yyg->yy_hold_char; + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; } YY_CURRENT_BUFFER_LVALUE = new_buffer; - wcsutrn_load_buffer_state( ); + yy_load_buffer_state( yyscanner ); /* We don't actually know whether we did this switch during - * EOF (wcsutrnwrap()) processing, but the only time this flag - * is looked at is after wcsutrnwrap() is called, so it's safe + * EOF (yywrap()) processing, but the only time this flag + * is looked at is after yywrap() is called, so it's safe * to go ahead and always set it. */ - (yy_did_buffer_switch_on_eof) = 1; + yyg->yy_did_buffer_switch_on_eof = 1; } -static void wcsutrn_load_buffer_state (void) +static void yy_load_buffer_state (yyscan_t yyscanner) { - (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; - (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; - wcsutrnin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; - (yy_hold_char) = *(yy_c_buf_p); + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + yyg->yytext_ptr = yyg->yy_c_buf_p = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; + yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; + yyg->yy_hold_char = *yyg->yy_c_buf_p; } /** Allocate and initialize an input buffer state. * @param file A readable stream. * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. - * + * @param yyscanner The scanner object. * @return the allocated buffer state. */ - YY_BUFFER_STATE wcsutrn_create_buffer (FILE * file, int size ) + YY_BUFFER_STATE yy_create_buffer (FILE * file, int size , yyscan_t yyscanner) { YY_BUFFER_STATE b; - b = (YY_BUFFER_STATE) wcsutrnalloc(sizeof( struct yy_buffer_state ) ); + b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner ); if ( ! b ) - YY_FATAL_ERROR( "out of dynamic memory in wcsutrn_create_buffer()" ); + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); b->yy_buf_size = size; /* yy_ch_buf has to be 2 characters longer than the size given because * we need to put in 2 end-of-buffer characters. */ - b->yy_ch_buf = (char *) wcsutrnalloc(b->yy_buf_size + 2 ); + b->yy_ch_buf = (char *) yyalloc( (yy_size_t) (b->yy_buf_size + 2) , yyscanner ); if ( ! b->yy_ch_buf ) - YY_FATAL_ERROR( "out of dynamic memory in wcsutrn_create_buffer()" ); + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); b->yy_is_our_buffer = 1; - wcsutrn_init_buffer(b,file ); + yy_init_buffer( b, file , yyscanner); return b; } /** Destroy the buffer. - * @param b a buffer created with wcsutrn_create_buffer() - * + * @param b a buffer created with yy_create_buffer() + * @param yyscanner The scanner object. */ - void wcsutrn_delete_buffer (YY_BUFFER_STATE b ) + void yy_delete_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) { - + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if ( ! b ) return; @@ -5183,27 +5656,28 @@ static void wcsutrn_load_buffer_state (void) YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; if ( b->yy_is_our_buffer ) - wcsutrnfree((void *) b->yy_ch_buf ); + yyfree( (void *) b->yy_ch_buf , yyscanner ); - wcsutrnfree((void *) b ); + yyfree( (void *) b , yyscanner ); } /* Initializes or reinitializes a buffer. * This function is sometimes called more than once on the same buffer, - * such as during a wcsutrnrestart() or at EOF. + * such as during a yyrestart() or at EOF. */ - static void wcsutrn_init_buffer (YY_BUFFER_STATE b, FILE * file ) + static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file , yyscan_t yyscanner) { int oerrno = errno; - - wcsutrn_flush_buffer(b ); + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + yy_flush_buffer( b , yyscanner); b->yy_input_file = file; b->yy_fill_buffer = 1; - /* If b is the current buffer, then wcsutrn_init_buffer was _probably_ - * called from wcsutrnrestart() or through yy_get_next_buffer. + /* If b is the current buffer, then yy_init_buffer was _probably_ + * called from yyrestart() or through yy_get_next_buffer. * In that case, we don't want to reset the lineno or column. */ if (b != YY_CURRENT_BUFFER){ @@ -5218,11 +5692,12 @@ static void wcsutrn_load_buffer_state (void) /** Discard all buffered characters. On the next scan, YY_INPUT will be called. * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. - * + * @param yyscanner The scanner object. */ - void wcsutrn_flush_buffer (YY_BUFFER_STATE b ) + void yy_flush_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) { - if ( ! b ) + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if ( ! b ) return; b->yy_n_chars = 0; @@ -5240,114 +5715,117 @@ static void wcsutrn_load_buffer_state (void) b->yy_buffer_status = YY_BUFFER_NEW; if ( b == YY_CURRENT_BUFFER ) - wcsutrn_load_buffer_state( ); + yy_load_buffer_state( yyscanner ); } /** Pushes the new state onto the stack. The new state becomes * the current state. This function will allocate the stack * if necessary. * @param new_buffer The new state. - * + * @param yyscanner The scanner object. */ -void wcsutrnpush_buffer_state (YY_BUFFER_STATE new_buffer ) +void yypush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) { - if (new_buffer == NULL) + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if (new_buffer == NULL) return; - wcsutrnensure_buffer_stack(); + yyensure_buffer_stack(yyscanner); - /* This block is copied from wcsutrn_switch_to_buffer. */ + /* This block is copied from yy_switch_to_buffer. */ if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ - *(yy_c_buf_p) = (yy_hold_char); - YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); - YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + *yyg->yy_c_buf_p = yyg->yy_hold_char; + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; } /* Only push if top exists. Otherwise, replace top. */ if (YY_CURRENT_BUFFER) - (yy_buffer_stack_top)++; + yyg->yy_buffer_stack_top++; YY_CURRENT_BUFFER_LVALUE = new_buffer; - /* copied from wcsutrn_switch_to_buffer. */ - wcsutrn_load_buffer_state( ); - (yy_did_buffer_switch_on_eof) = 1; + /* copied from yy_switch_to_buffer. */ + yy_load_buffer_state( yyscanner ); + yyg->yy_did_buffer_switch_on_eof = 1; } /** Removes and deletes the top of the stack, if present. * The next element becomes the new top. - * + * @param yyscanner The scanner object. */ -void wcsutrnpop_buffer_state (void) +void yypop_buffer_state (yyscan_t yyscanner) { - if (!YY_CURRENT_BUFFER) + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if (!YY_CURRENT_BUFFER) return; - wcsutrn_delete_buffer(YY_CURRENT_BUFFER ); + yy_delete_buffer(YY_CURRENT_BUFFER , yyscanner); YY_CURRENT_BUFFER_LVALUE = NULL; - if ((yy_buffer_stack_top) > 0) - --(yy_buffer_stack_top); + if (yyg->yy_buffer_stack_top > 0) + --yyg->yy_buffer_stack_top; if (YY_CURRENT_BUFFER) { - wcsutrn_load_buffer_state( ); - (yy_did_buffer_switch_on_eof) = 1; + yy_load_buffer_state( yyscanner ); + yyg->yy_did_buffer_switch_on_eof = 1; } } /* Allocates the stack if it does not exist. * Guarantees space for at least one push. */ -static void wcsutrnensure_buffer_stack (void) +static void yyensure_buffer_stack (yyscan_t yyscanner) { - int num_to_alloc; - - if (!(yy_buffer_stack)) { + yy_size_t num_to_alloc; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (!yyg->yy_buffer_stack) { /* First allocation is just for 2 elements, since we don't know if this * scanner will even need a stack. We use 2 instead of 1 to avoid an * immediate realloc on the next call. */ - num_to_alloc = 1; - (yy_buffer_stack) = (struct yy_buffer_state**)wcsutrnalloc + num_to_alloc = 1; /* After all that talk, this was set to 1 anyways... */ + yyg->yy_buffer_stack = (struct yy_buffer_state**)yyalloc (num_to_alloc * sizeof(struct yy_buffer_state*) - ); - if ( ! (yy_buffer_stack) ) - YY_FATAL_ERROR( "out of dynamic memory in wcsutrnensure_buffer_stack()" ); - - memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*)); - - (yy_buffer_stack_max) = num_to_alloc; - (yy_buffer_stack_top) = 0; + , yyscanner); + if ( ! yyg->yy_buffer_stack ) + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); + + memset(yyg->yy_buffer_stack, 0, num_to_alloc * sizeof(struct yy_buffer_state*)); + + yyg->yy_buffer_stack_max = num_to_alloc; + yyg->yy_buffer_stack_top = 0; return; } - if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){ + if (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){ /* Increase the buffer to prepare for a possible push. */ - int grow_size = 8 /* arbitrary grow size */; + yy_size_t grow_size = 8 /* arbitrary grow size */; - num_to_alloc = (yy_buffer_stack_max) + grow_size; - (yy_buffer_stack) = (struct yy_buffer_state**)wcsutrnrealloc - ((yy_buffer_stack), + num_to_alloc = yyg->yy_buffer_stack_max + grow_size; + yyg->yy_buffer_stack = (struct yy_buffer_state**)yyrealloc + (yyg->yy_buffer_stack, num_to_alloc * sizeof(struct yy_buffer_state*) - ); - if ( ! (yy_buffer_stack) ) - YY_FATAL_ERROR( "out of dynamic memory in wcsutrnensure_buffer_stack()" ); + , yyscanner); + if ( ! yyg->yy_buffer_stack ) + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); /* zero only the new slots.*/ - memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*)); - (yy_buffer_stack_max) = num_to_alloc; + memset(yyg->yy_buffer_stack + yyg->yy_buffer_stack_max, 0, grow_size * sizeof(struct yy_buffer_state*)); + yyg->yy_buffer_stack_max = num_to_alloc; } } /** Setup the input buffer state to scan directly from a user-specified character buffer. * @param base the character buffer * @param size the size in bytes of the character buffer - * - * @return the newly allocated buffer state object. + * @param yyscanner The scanner object. + * @return the newly allocated buffer state object. */ -YY_BUFFER_STATE wcsutrn_scan_buffer (char * base, yy_size_t size ) +YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size , yyscan_t yyscanner) { YY_BUFFER_STATE b; @@ -5355,49 +5833,49 @@ YY_BUFFER_STATE wcsutrn_scan_buffer (char * base, yy_size_t size ) base[size-2] != YY_END_OF_BUFFER_CHAR || base[size-1] != YY_END_OF_BUFFER_CHAR ) /* They forgot to leave room for the EOB's. */ - return 0; + return NULL; - b = (YY_BUFFER_STATE) wcsutrnalloc(sizeof( struct yy_buffer_state ) ); + b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner ); if ( ! b ) - YY_FATAL_ERROR( "out of dynamic memory in wcsutrn_scan_buffer()" ); + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); - b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */ + b->yy_buf_size = (int) (size - 2); /* "- 2" to take care of EOB's */ b->yy_buf_pos = b->yy_ch_buf = base; b->yy_is_our_buffer = 0; - b->yy_input_file = 0; + b->yy_input_file = NULL; b->yy_n_chars = b->yy_buf_size; b->yy_is_interactive = 0; b->yy_at_bol = 1; b->yy_fill_buffer = 0; b->yy_buffer_status = YY_BUFFER_NEW; - wcsutrn_switch_to_buffer(b ); + yy_switch_to_buffer( b , yyscanner ); return b; } -/** Setup the input buffer state to scan a string. The next call to wcsutrnlex() will +/** Setup the input buffer state to scan a string. The next call to yylex() will * scan from a @e copy of @a str. * @param yystr a NUL-terminated string to scan - * + * @param yyscanner The scanner object. * @return the newly allocated buffer state object. * @note If you want to scan bytes that may contain NUL values, then use - * wcsutrn_scan_bytes() instead. + * yy_scan_bytes() instead. */ -YY_BUFFER_STATE wcsutrn_scan_string (yyconst char * yystr ) +YY_BUFFER_STATE yy_scan_string (const char * yystr , yyscan_t yyscanner) { - return wcsutrn_scan_bytes(yystr,strlen(yystr) ); + return yy_scan_bytes( yystr, (int) strlen(yystr) , yyscanner); } -/** Setup the input buffer state to scan the given bytes. The next call to wcsutrnlex() will +/** Setup the input buffer state to scan the given bytes. The next call to yylex() will * scan from a @e copy of @a bytes. * @param yybytes the byte buffer to scan * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes. - * + * @param yyscanner The scanner object. * @return the newly allocated buffer state object. */ -YY_BUFFER_STATE wcsutrn_scan_bytes (yyconst char * yybytes, int _yybytes_len ) +YY_BUFFER_STATE yy_scan_bytes (const char * yybytes, int _yybytes_len , yyscan_t yyscanner) { YY_BUFFER_STATE b; char *buf; @@ -5405,19 +5883,19 @@ YY_BUFFER_STATE wcsutrn_scan_bytes (yyconst char * yybytes, int _yybytes_len ) int i; /* Get memory for full buffer, including space for trailing EOB's. */ - n = _yybytes_len + 2; - buf = (char *) wcsutrnalloc(n ); + n = (yy_size_t) (_yybytes_len + 2); + buf = (char *) yyalloc( n , yyscanner ); if ( ! buf ) - YY_FATAL_ERROR( "out of dynamic memory in wcsutrn_scan_bytes()" ); + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" ); for ( i = 0; i < _yybytes_len; ++i ) buf[i] = yybytes[i]; buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; - b = wcsutrn_scan_buffer(buf,n ); + b = yy_scan_buffer( buf, n , yyscanner); if ( ! b ) - YY_FATAL_ERROR( "bad buffer in wcsutrn_scan_bytes()" ); + YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" ); /* It's okay to grow etc. this buffer, and we should throw it * away when we're done. @@ -5431,9 +5909,11 @@ YY_BUFFER_STATE wcsutrn_scan_bytes (yyconst char * yybytes, int _yybytes_len ) #define YY_EXIT_FAILURE 2 #endif -static void yy_fatal_error (yyconst char* msg ) +static void yynoreturn yy_fatal_error (const char* msg , yyscan_t yyscanner) { - (void) fprintf( stderr, "%s\n", msg ); + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + fprintf( stderr, "%s\n", msg ); exit( YY_EXIT_FAILURE ); } @@ -5443,144 +5923,283 @@ static void yy_fatal_error (yyconst char* msg ) #define yyless(n) \ do \ { \ - /* Undo effects of setting up wcsutrntext. */ \ + /* Undo effects of setting up yytext. */ \ int yyless_macro_arg = (n); \ YY_LESS_LINENO(yyless_macro_arg);\ - wcsutrntext[wcsutrnleng] = (yy_hold_char); \ - (yy_c_buf_p) = wcsutrntext + yyless_macro_arg; \ - (yy_hold_char) = *(yy_c_buf_p); \ - *(yy_c_buf_p) = '\0'; \ - wcsutrnleng = yyless_macro_arg; \ + yytext[yyleng] = yyg->yy_hold_char; \ + yyg->yy_c_buf_p = yytext + yyless_macro_arg; \ + yyg->yy_hold_char = *yyg->yy_c_buf_p; \ + *yyg->yy_c_buf_p = '\0'; \ + yyleng = yyless_macro_arg; \ } \ while ( 0 ) /* Accessor methods (get/set functions) to struct members. */ +/** Get the user-defined data for this scanner. + * @param yyscanner The scanner object. + */ +YY_EXTRA_TYPE yyget_extra (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyextra; +} + /** Get the current line number. - * + * @param yyscanner The scanner object. + */ +int yyget_lineno (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (! YY_CURRENT_BUFFER) + return 0; + + return yylineno; +} + +/** Get the current column number. + * @param yyscanner The scanner object. */ -int wcsutrnget_lineno (void) +int yyget_column (yyscan_t yyscanner) { - - return wcsutrnlineno; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (! YY_CURRENT_BUFFER) + return 0; + + return yycolumn; } /** Get the input stream. - * + * @param yyscanner The scanner object. */ -FILE *wcsutrnget_in (void) +FILE *yyget_in (yyscan_t yyscanner) { - return wcsutrnin; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyin; } /** Get the output stream. - * + * @param yyscanner The scanner object. */ -FILE *wcsutrnget_out (void) +FILE *yyget_out (yyscan_t yyscanner) { - return wcsutrnout; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyout; } /** Get the length of the current token. - * + * @param yyscanner The scanner object. */ -int wcsutrnget_leng (void) +int yyget_leng (yyscan_t yyscanner) { - return wcsutrnleng; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyleng; } /** Get the current token. - * + * @param yyscanner The scanner object. */ -char *wcsutrnget_text (void) +char *yyget_text (yyscan_t yyscanner) { - return wcsutrntext; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yytext; +} + +/** Set the user-defined data. This data is never touched by the scanner. + * @param user_defined The data to be associated with this scanner. + * @param yyscanner The scanner object. + */ +void yyset_extra (YY_EXTRA_TYPE user_defined , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyextra = user_defined ; } /** Set the current line number. - * @param line_number - * + * @param _line_number line number + * @param yyscanner The scanner object. + */ +void yyset_lineno (int _line_number , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* lineno is only valid if an input buffer exists. */ + if (! YY_CURRENT_BUFFER ) + YY_FATAL_ERROR( "yyset_lineno called with no buffer" ); + + yylineno = _line_number; +} + +/** Set the current column. + * @param _column_no column number + * @param yyscanner The scanner object. */ -void wcsutrnset_lineno (int line_number ) +void yyset_column (int _column_no , yyscan_t yyscanner) { + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* column is only valid if an input buffer exists. */ + if (! YY_CURRENT_BUFFER ) + YY_FATAL_ERROR( "yyset_column called with no buffer" ); - wcsutrnlineno = line_number; + yycolumn = _column_no; } /** Set the input stream. This does not discard the current * input buffer. - * @param in_str A readable stream. - * - * @see wcsutrn_switch_to_buffer + * @param _in_str A readable stream. + * @param yyscanner The scanner object. + * @see yy_switch_to_buffer */ -void wcsutrnset_in (FILE * in_str ) +void yyset_in (FILE * _in_str , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyin = _in_str ; +} + +void yyset_out (FILE * _out_str , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyout = _out_str ; +} + +int yyget_debug (yyscan_t yyscanner) { - wcsutrnin = in_str ; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yy_flex_debug; } -void wcsutrnset_out (FILE * out_str ) +void yyset_debug (int _bdebug , yyscan_t yyscanner) { - wcsutrnout = out_str ; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yy_flex_debug = _bdebug ; } -int wcsutrnget_debug (void) +/* Accessor methods for yylval and yylloc */ + +/* User-visible API */ + +/* yylex_init is special because it creates the scanner itself, so it is + * the ONLY reentrant function that doesn't take the scanner as the last argument. + * That's why we explicitly handle the declaration, instead of using our macros. + */ +int yylex_init(yyscan_t* ptr_yy_globals) { - return wcsutrn_flex_debug; + if (ptr_yy_globals == NULL){ + errno = EINVAL; + return 1; + } + + *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), NULL ); + + if (*ptr_yy_globals == NULL){ + errno = ENOMEM; + return 1; + } + + /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */ + memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); + + return yy_init_globals ( *ptr_yy_globals ); } -void wcsutrnset_debug (int bdebug ) +/* yylex_init_extra has the same functionality as yylex_init, but follows the + * convention of taking the scanner as the last argument. Note however, that + * this is a *pointer* to a scanner, as it will be allocated by this call (and + * is the reason, too, why this function also must handle its own declaration). + * The user defined value in the first argument will be available to yyalloc in + * the yyextra field. + */ +int yylex_init_extra( YY_EXTRA_TYPE yy_user_defined, yyscan_t* ptr_yy_globals ) { - wcsutrn_flex_debug = bdebug ; + struct yyguts_t dummy_yyguts; + + yyset_extra (yy_user_defined, &dummy_yyguts); + + if (ptr_yy_globals == NULL){ + errno = EINVAL; + return 1; + } + + *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), &dummy_yyguts ); + + if (*ptr_yy_globals == NULL){ + errno = ENOMEM; + return 1; + } + + /* By setting to 0xAA, we expose bugs in + yy_init_globals. Leave at 0x00 for releases. */ + memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); + + yyset_extra (yy_user_defined, *ptr_yy_globals); + + return yy_init_globals ( *ptr_yy_globals ); } -static int yy_init_globals (void) +static int yy_init_globals (yyscan_t yyscanner) { - /* Initialization is the same as for the non-reentrant scanner. - * This function is called from wcsutrnlex_destroy(), so don't allocate here. + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + /* Initialization is the same as for the non-reentrant scanner. + * This function is called from yylex_destroy(), so don't allocate here. */ - (yy_buffer_stack) = 0; - (yy_buffer_stack_top) = 0; - (yy_buffer_stack_max) = 0; - (yy_c_buf_p) = (char *) 0; - (yy_init) = 0; - (yy_start) = 0; + yyg->yy_buffer_stack = NULL; + yyg->yy_buffer_stack_top = 0; + yyg->yy_buffer_stack_max = 0; + yyg->yy_c_buf_p = NULL; + yyg->yy_init = 0; + yyg->yy_start = 0; + + yyg->yy_start_stack_ptr = 0; + yyg->yy_start_stack_depth = 0; + yyg->yy_start_stack = NULL; /* Defined in main.c */ #ifdef YY_STDINIT - wcsutrnin = stdin; - wcsutrnout = stdout; + yyin = stdin; + yyout = stdout; #else - wcsutrnin = (FILE *) 0; - wcsutrnout = (FILE *) 0; + yyin = NULL; + yyout = NULL; #endif /* For future reference: Set errno on error, since we are called by - * wcsutrnlex_init() + * yylex_init() */ return 0; } -/* wcsutrnlex_destroy is for both reentrant and non-reentrant scanners. */ -int wcsutrnlex_destroy (void) +/* yylex_destroy is for both reentrant and non-reentrant scanners. */ +int yylex_destroy (yyscan_t yyscanner) { - + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + /* Pop the buffer stack, destroying each element. */ while(YY_CURRENT_BUFFER){ - wcsutrn_delete_buffer(YY_CURRENT_BUFFER ); + yy_delete_buffer( YY_CURRENT_BUFFER , yyscanner ); YY_CURRENT_BUFFER_LVALUE = NULL; - wcsutrnpop_buffer_state(); + yypop_buffer_state(yyscanner); } /* Destroy the stack itself. */ - wcsutrnfree((yy_buffer_stack) ); - (yy_buffer_stack) = NULL; + yyfree(yyg->yy_buffer_stack , yyscanner); + yyg->yy_buffer_stack = NULL; + + /* Destroy the start condition stack. */ + yyfree( yyg->yy_start_stack , yyscanner ); + yyg->yy_start_stack = NULL; /* Reset the globals. This is important in a non-reentrant scanner so the next time - * wcsutrnlex() is called, initialization will occur. */ - yy_init_globals( ); + * yylex() is called, initialization will occur. */ + yy_init_globals( yyscanner); + /* Destroy the main struct (reentrant only). */ + yyfree ( yyscanner , yyscanner ); + yyscanner = NULL; return 0; } @@ -5589,18 +6208,21 @@ int wcsutrnlex_destroy (void) */ #ifndef yytext_ptr -static void yy_flex_strncpy (char* s1, yyconst char * s2, int n ) +static void yy_flex_strncpy (char* s1, const char * s2, int n , yyscan_t yyscanner) { - register int i; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + + int i; for ( i = 0; i < n; ++i ) s1[i] = s2[i]; } #endif #ifdef YY_NEED_STRLEN -static int yy_flex_strlen (yyconst char * s ) +static int yy_flex_strlen (const char * s , yyscan_t yyscanner) { - register int n; + int n; for ( n = 0; s[n]; ++n ) ; @@ -5608,13 +6230,18 @@ static int yy_flex_strlen (yyconst char * s ) } #endif -void *wcsutrnalloc (yy_size_t size ) +void *yyalloc (yy_size_t size , yyscan_t yyscanner) { - return (void *) malloc( size ); + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + return malloc(size); } -void *wcsutrnrealloc (void * ptr, yy_size_t size ) +void *yyrealloc (void * ptr, yy_size_t size , yyscan_t yyscanner) { + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + /* The cast to (char *) in the following accommodates both * implementations that use char* generic pointers, and those * that use void* generic pointers. It works with the latter @@ -5622,17 +6249,41 @@ void *wcsutrnrealloc (void * ptr, yy_size_t size ) * any pointer type to void*, and deal with argument conversions * as though doing an assignment. */ - return (void *) realloc( (char *) ptr, size ); + return realloc(ptr, size); } -void wcsutrnfree (void * ptr ) +void yyfree (void * ptr , yyscan_t yyscanner) { - free( (char *) ptr ); /* see wcsutrnrealloc() for (char *) cast */ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + free( (char *) ptr ); /* see yyrealloc() for (char *) cast */ } #define YYTABLES_NAME "yytables" -#line 335 "wcsutrn.l" +#line 344 "wcsutrn.l" +/*---------------------------------------------------------------------------- +* External interface to the scanner. +*---------------------------------------------------------------------------*/ + +int wcsutrne( + int ctrl, + char unitstr[], + struct wcserr **err) + +{ + // Function prototypes. + int yylex_init_extra(YY_EXTRA_TYPE extra, yyscan_t *yyscanner); + int yylex_destroy(yyscan_t yyscanner); + + struct wcsutrn_extra extra; + yyscan_t yyscanner; + yylex_init_extra(&extra, &yyscanner); + int status = wcsutrne_scanner(ctrl, unitstr, err, yyscanner); + yylex_destroy(yyscanner); + + return status; +} diff --git a/cextern/wcslib/C/getwcstab.c b/cextern/wcslib/C/getwcstab.c index 2690b6d4375a..9ba9c8ef2c94 100644 --- a/cextern/wcslib/C/getwcstab.c +++ b/cextern/wcslib/C/getwcstab.c @@ -1,7 +1,6 @@ /*============================================================================ - - WCSLIB 4.14 - an implementation of the FITS WCS standard. - Copyright (C) 1995-2012, Mark Calabretta + WCSLIB 8.6 - an implementation of the FITS WCS standard. + Copyright (C) 1995-2026, Mark Calabretta This file is part of WCSLIB. @@ -18,11 +17,9 @@ You should have received a copy of the GNU Lesser General Public License along with WCSLIB. If not, see http://www.gnu.org/licenses. - Direct correspondence concerning WCSLIB to mark@calabretta.id.au - Author: Mark Calabretta, Australia Telescope National Facility, CSIRO. - http://www.atnf.csiro.au/people/Mark.Calabretta - $Id: getwcstab.c,v 4.14 2012/07/13 10:02:27 cal103 Exp $ + http://www.atnf.csiro.au/computing/software/wcs + $Id: getwcstab.c,v 8.6 2026/03/29 13:53:56 mcalabre Exp $ *===========================================================================*/ #include @@ -32,7 +29,7 @@ #include "getwcstab.h" -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- int fits_read_wcstab( fitsfile *fptr, @@ -54,30 +51,30 @@ int fits_read_wcstab( if (nwtb == 0) return 0; - /* Zero the array pointers. */ + // Zero the array pointers. wtbp = wtb; for (iwtb = 0; iwtb < nwtb; iwtb++, wtbp++) { *wtbp->arrayp = 0x0; } - /* Save HDU number so that we can move back to it later. */ + // Save HDU number so that we can move back to it later. fits_get_hdu_num(fptr, &hdunum); wtbp = wtb; for (iwtb = 0; iwtb < nwtb; iwtb++, wtbp++) { - /* Move to the required binary table extension. */ + // Move to the required binary table extension. if (fits_movnam_hdu(fptr, BINARY_TBL, (char *)(wtbp->extnam), wtbp->extver, status)) { goto cleanup; } - /* Locate the table column. */ + // Locate the table column. if (fits_get_colnum(fptr, CASEINSEN, (char *)(wtbp->ttype), &colnum, status)) { goto cleanup; } - /* Get the array dimensions and check for consistency. */ + // Get the array dimensions and check for consistency. if (wtbp->ndim < 1) { *status = NEG_AXIS; goto cleanup; @@ -94,7 +91,7 @@ int fits_read_wcstab( if (naxis != wtbp->ndim) { if (wtbp->kind == 'c' && wtbp->ndim == 2) { - /* Allow TDIMn to be omitted for degenerate coordinate arrays. */ + // Allow TDIMn to be omitted for degenerate coordinate arrays. naxis = 2; naxes[1] = naxes[0]; naxes[0] = 1; @@ -105,16 +102,16 @@ int fits_read_wcstab( } if (wtbp->kind == 'c') { - /* Coordinate array; calculate the array size. */ + // Coordinate array; calculate the array size. nelem = naxes[0]; for (m = 0; m < naxis-1; m++) { *(wtbp->dimlen + m) = naxes[m+1]; nelem *= naxes[m+1]; } } else { - /* Index vector; check length. */ + // Index vector; check length. if ((nelem = naxes[0]) != *(wtbp->dimlen)) { - /* N.B. coordinate array precedes the index vectors. */ + // N.B. coordinate array precedes the index vectors. *status = BAD_TDIM; goto cleanup; } @@ -123,13 +120,13 @@ int fits_read_wcstab( free(naxes); naxes = 0; - /* Allocate memory for the array. */ + // Allocate memory for the array. if (!(*wtbp->arrayp = calloc((size_t)nelem, sizeof(double)))) { *status = MEMORY_ALLOCATION; goto cleanup; } - /* Read the array from the table. */ + // Read the array from the table. if (fits_read_col_dbl(fptr, colnum, wtbp->row, 1L, nelem, 0.0, *wtbp->arrayp, &anynul, status)) { goto cleanup; @@ -137,11 +134,11 @@ int fits_read_wcstab( } cleanup: - /* Move back to the starting HDU. */ + // Move back to the starting HDU. nostat = 0; fits_movabs_hdu(fptr, hdunum, 0, &nostat); - /* Release allocated memory. */ + // Release allocated memory. if (naxes) free(naxes); if (*status) { wtbp = wtb; diff --git a/cextern/wcslib/C/getwcstab.h b/cextern/wcslib/C/getwcstab.h index 8ddecdb7f1fb..a1a47382dca2 100644 --- a/cextern/wcslib/C/getwcstab.h +++ b/cextern/wcslib/C/getwcstab.h @@ -1,7 +1,6 @@ /*============================================================================ - - WCSLIB 4.14 - an implementation of the FITS WCS standard. - Copyright (C) 1995-2012, Mark Calabretta + WCSLIB 8.6 - an implementation of the FITS WCS standard. + Copyright (C) 1995-2026, Mark Calabretta This file is part of WCSLIB. @@ -18,13 +17,15 @@ You should have received a copy of the GNU Lesser General Public License along with WCSLIB. If not, see http://www.gnu.org/licenses. - Direct correspondence concerning WCSLIB to mark@calabretta.id.au - Author: Mark Calabretta, Australia Telescope National Facility, CSIRO. - http://www.atnf.csiro.au/people/Mark.Calabretta - $Id: getwcstab.h,v 4.14 2012/07/13 10:02:27 cal103 Exp $ + http://www.atnf.csiro.au/computing/software/wcs + $Id: getwcstab.h,v 8.6 2026/03/29 13:53:56 mcalabre Exp $ *============================================================================= * +* WCSLIB 8.6 - C routines that implement the FITS World Coordinate System +* (WCS) standard. Refer to the README file provided with WCSLIB for an +* overview of the library. +* * Summary of the getwcstab routines * --------------------------------- * fits_read_wcstab(), an implementation of a FITS table reading routine for @@ -64,50 +65,50 @@ * int CFITSIO status value. * * Notes: -* In order to maintain WCSLIB and CFITSIO as independent libraries it is not -* permissible for any CFITSIO library code to include WCSLIB header files, -* or vice versa. However, the CFITSIO function fits_read_wcstab() accepts -* an array of wtbarr structs defined in wcs.h within WCSLIB. +* 1: In order to maintain WCSLIB and CFITSIO as independent libraries it is +* not permissible for any CFITSIO library code to include WCSLIB header +* files, or vice versa. However, the CFITSIO function fits_read_wcstab() +* accepts an array of wtbarr structs defined in wcs.h within WCSLIB. * -* The problem therefore is to define the wtbarr struct within fitsio.h -* without including wcs.h, especially noting that wcs.h will often (but not -* always) be included together with fitsio.h in an applications program that -* uses fits_read_wcstab(). +* The problem therefore is to define the wtbarr struct within fitsio.h +* without including wcs.h, especially noting that wcs.h will often (but +* not always) be included together with fitsio.h in an applications +* program that uses fits_read_wcstab(). * -* The solution adopted is for WCSLIB to define "struct wtbarr" while -* fitsio.h defines "typedef wtbarr" as an untagged struct with identical -* members. This allows both wcs.h and fitsio.h to define a wtbarr data type -* without conflict by virtue of the fact that structure tags and typedef -* names share different name spaces in C; Appendix A, Sect. A11.1 (p227) of -* the K&R ANSI edition states that: +* The solution adopted is for WCSLIB to define "struct wtbarr" while +* fitsio.h defines "typedef wtbarr" as an untagged struct with identical +* members. This allows both wcs.h and fitsio.h to define a wtbarr data +* type without conflict by virtue of the fact that structure tags and +* typedef names share different name spaces in C; Appendix A, Sect. A11.1 +* (p227) of the K&R ANSI edition states that: * -* Identifiers fall into several name spaces that do not interfere with one -* another; the same identifier may be used for different purposes, even in -* the same scope, if the uses are in different name spaces. These classes -* are: objects, functions, typedef names, and enum constants; labels; tags -* of structures, unions, and enumerations; and members of each structure -* or union individually. += Identifiers fall into several name spaces that do not interfere with += one another; the same identifier may be used for different purposes, += even in the same scope, if the uses are in different name spaces. += These classes are: objects, functions, typedef names, and enum += constants; labels; tags of structures, unions, and enumerations; and += members of each structure or union individually. * -* Therefore, declarations within WCSLIB look like +* Therefore, declarations within WCSLIB look like * -= struct wtbarr *w; += struct wtbarr *w; * -* while within CFITSIO they are simply +* while within CFITSIO they are simply * -= wtbarr *w; += wtbarr *w; * -* As suggested by the commonality of the names, these are really the same -* aggregate data type. However, in passing a (struct wtbarr *) to -* fits_read_wcstab() a cast to (wtbarr *) is formally required. +* As suggested by the commonality of the names, these are really the same +* aggregate data type. However, in passing a (struct wtbarr *) to +* fits_read_wcstab() a cast to (wtbarr *) is formally required. * -* When using WCSLIB and CFITSIO together in C++ the situation is complicated -* by the fact that typedefs and structs share the same namespace; C++ -* Annotated Reference Manual, Sect. 7.1.3 (p105). In that case the wtbarr -* struct in wcs.h is renamed by preprocessor macro substitution to wtbarr_s -* to distinguish it from the typedef defined in fitsio.h. However, the -* scope of this macro substitution is limited to wcs.h itself and CFITSIO -* programmer code, whether in C++ or C, should always use the wtbarr -* typedef. +* When using WCSLIB and CFITSIO together in C++ the situation is +* complicated by the fact that typedefs and structs share the same +* namespace; C++ Annotated Reference Manual, Sect. 7.1.3 (p105). In that +* case the wtbarr struct in wcs.h is renamed by preprocessor macro +* substitution to wtbarr_s to distinguish it from the typedef defined in +* fitsio.h. However, the scope of this macro substitution is limited to +* wcs.h itself and CFITSIO programmer code, whether in C++ or C, should +* always use the wtbarr typedef. * * * wtbarr typedef @@ -164,18 +165,18 @@ extern "C" { #include typedef struct { - int i; /* Image axis number. */ - int m; /* Array axis number for index vectors. */ - int kind; /* Array type, 'c' (coord) or 'i' (index). */ - char extnam[72]; /* EXTNAME of binary table extension. */ - int extver; /* EXTVER of binary table extension. */ - int extlev; /* EXTLEV of binary table extension. */ - char ttype[72]; /* TTYPEn of column containing the array. */ - long row; /* Table row number. */ - int ndim; /* Expected array dimensionality. */ - int *dimlen; /* Where to write the array axis lengths. */ - double **arrayp; /* Where to write the address of the array */ - /* allocated to store the array. */ + int i; // Image axis number. + int m; // Array axis number for index vectors. + int kind; // Array type, 'c' (coord) or 'i' (index). + char extnam[72]; // EXTNAME of binary table extension. + int extver; // EXTVER of binary table extension. + int extlev; // EXTLEV of binary table extension. + char ttype[72]; // TTYPEn of column containing the array. + long row; // Table row number. + int ndim; // Expected array dimensionality. + int *dimlen; // Where to write the array axis lengths. + double **arrayp; // Where to write the address of the array + // allocated to store the array. } wtbarr; @@ -186,4 +187,4 @@ int fits_read_wcstab(fitsfile *fptr, int nwtb, wtbarr *wtb, int *status); } #endif -#endif /* WCSLIB_GETWCSTAB */ +#endif // WCSLIB_GETWCSTAB diff --git a/cextern/wcslib/C/lin.c b/cextern/wcslib/C/lin.c index ecde304f2c1d..280bcb77e39c 100644 --- a/cextern/wcslib/C/lin.c +++ b/cextern/wcslib/C/lin.c @@ -1,7 +1,6 @@ /*============================================================================ - - WCSLIB 4.14 - an implementation of the FITS WCS standard. - Copyright (C) 1995-2012, Mark Calabretta + WCSLIB 8.6 - an implementation of the FITS WCS standard. + Copyright (C) 1995-2026, Mark Calabretta This file is part of WCSLIB. @@ -18,66 +17,87 @@ You should have received a copy of the GNU Lesser General Public License along with WCSLIB. If not, see http://www.gnu.org/licenses. - Direct correspondence concerning WCSLIB to mark@calabretta.id.au - Author: Mark Calabretta, Australia Telescope National Facility, CSIRO. - http://www.atnf.csiro.au/people/Mark.Calabretta - $Id: lin.c,v 4.14 2012/07/13 10:02:27 cal103 Exp $ + http://www.atnf.csiro.au/computing/software/wcs + $Id: lin.c,v 8.6 2026/03/29 13:53:56 mcalabre Exp $ *===========================================================================*/ +#include #include #include -#include +#include #include "wcserr.h" #include "wcsprintf.h" #include "lin.h" +#include "dis.h" -const int LINSET = 137; - -/* Map status return value to message. */ +// Map status return value to message. const char *lin_errmsg[] = { "Success", "Null linprm pointer passed", "Memory allocation failed", - "PCi_ja matrix is singular"}; - -/* Convenience macro for invoking wcserr_set(). */ + "PCi_ja matrix is singular", + "Failed to initialize distortion functions", + "Distort error", + "De-distort error"}; + +// Map error returns for lower-level routines. +const int lin_diserr[] = { + LINERR_SUCCESS, // 0: DISERR_SUCCESS + LINERR_NULL_POINTER, // 1: DISERR_NULL_POINTER + LINERR_MEMORY, // 2: DISERR_MEMORY + LINERR_DISTORT_INIT, // 3: DISERR_BAD_PARAM + LINERR_DISTORT, // 4: DISERR_DISTORT + LINERR_DEDISTORT // 5: DISERR_DEDISTORT +}; + +static const int LINSET = 137; + +// Convenience macro for invoking wcserr_set(). #define LIN_ERRMSG(status) WCSERR_SET(status), lin_errmsg[status] -/*--------------------------------------------------------------------------*/ - -int linini(alloc, naxis, lin) +//---------------------------------------------------------------------------- -int alloc, naxis; -struct linprm *lin; +int linini(int alloc, int naxis, struct linprm *lin) { - static const char *function = "linini"; + return lininit(alloc, naxis, lin, -1); +} - int i, j; - double *pc; - struct wcserr **err; +//---------------------------------------------------------------------------- + +int lininit(int alloc, int naxis, struct linprm *lin, int ndpmax) + +{ + static const char *function = "lininit"; if (lin == 0x0) return LINERR_NULL_POINTER; - /* Initialize error message handling. */ - err = &(lin->err); - if (lin->flag != -1) { - if (lin->err) free(lin->err); + // Initialize error message handling. + if (lin->flag == -1) { + lin->err = 0x0; } - lin->err = 0x0; + struct wcserr **err = &(lin->err); + wcserr_clear(err); - /* Initialize memory management. */ + // Initialize memory management. if (lin->flag == -1 || lin->m_flag != LINSET) { - lin->m_flag = 0; - lin->m_naxis = 0x0; - lin->m_crpix = 0x0; - lin->m_pc = 0x0; - lin->m_cdelt = 0x0; - } + if (lin->flag == -1) { + lin->dispre = 0x0; + lin->disseq = 0x0; + lin->dummy = 0x0; + } + lin->m_flag = 0; + lin->m_naxis = 0; + lin->m_crpix = 0x0; + lin->m_pc = 0x0; + lin->m_cdelt = 0x0; + lin->m_dispre = 0x0; + lin->m_disseq = 0x0; + } if (naxis < 0) { return wcserr_set(WCSERR_SET(LINERR_MEMORY), @@ -85,25 +105,25 @@ struct linprm *lin; } - /* Allocate memory for arrays if required. */ + // Allocate memory for arrays if required. if (alloc || - lin->crpix == 0x0 || - lin->pc == 0x0 || - lin->cdelt == 0x0) { + lin->crpix == 0x0 || + lin->pc == 0x0 || + lin->cdelt == 0x0) { - /* Was sufficient allocated previously? */ + // Was sufficient allocated previously? if (lin->m_flag == LINSET && lin->m_naxis < naxis) { - /* No, free it. */ + // No, free it. linfree(lin); } if (alloc || lin->crpix == 0x0) { if (lin->m_crpix) { - /* In case the caller fiddled with it. */ + // In case the caller fiddled with it. lin->crpix = lin->m_crpix; } else { - if (!(lin->crpix = calloc(naxis, sizeof(double)))) { + if ((lin->crpix = calloc(naxis, sizeof(double))) == 0x0) { return wcserr_set(LIN_ERRMSG(LINERR_MEMORY)); } @@ -115,11 +135,11 @@ struct linprm *lin; if (alloc || lin->pc == 0x0) { if (lin->m_pc) { - /* In case the caller fiddled with it. */ + // In case the caller fiddled with it. lin->pc = lin->m_pc; } else { - if (!(lin->pc = calloc(naxis*naxis, sizeof(double)))) { + if ((lin->pc = calloc(naxis*naxis, sizeof(double))) == 0x0) { linfree(lin); return wcserr_set(LIN_ERRMSG(LINERR_MEMORY)); } @@ -132,11 +152,11 @@ struct linprm *lin; if (alloc || lin->cdelt == 0x0) { if (lin->m_cdelt) { - /* In case the caller fiddled with it. */ + // In case the caller fiddled with it. lin->cdelt = lin->m_cdelt; } else { - if (!(lin->cdelt = calloc(naxis, sizeof(double)))) { + if ((lin->cdelt = calloc(naxis, sizeof(double))) == 0x0) { linfree(lin); return wcserr_set(LIN_ERRMSG(LINERR_MEMORY)); } @@ -148,30 +168,42 @@ struct linprm *lin; } } - /* Free memory allocated by linset(). */ - if (lin->flag == LINSET) { + + // Reinitialize disprm structs if we are managing them. + if (lin->m_dispre) { + disinit(1, naxis, lin->dispre, ndpmax); + } + + if (lin->m_disseq) { + disinit(1, naxis, lin->disseq, ndpmax); + } + + + // Free memory allocated by linset(). + if (abs(lin->flag) == LINSET) { if (lin->piximg) free(lin->piximg); if (lin->imgpix) free(lin->imgpix); } - lin->piximg = 0x0; - lin->imgpix = 0x0; - lin->i_naxis = 0x0; + lin->piximg = 0x0; + lin->imgpix = 0x0; + lin->i_naxis = 0; + lin->unity = 0; + lin->affine = 0; + lin->simple = 0; - lin->flag = 0; - lin->naxis = naxis; + lin->naxis = naxis; - /* CRPIXja defaults to 0.0. */ - for (j = 0; j < naxis; j++) { + // CRPIXja defaults to 0.0. + for (int j = 0; j < naxis; j++) { lin->crpix[j] = 0.0; } - - /* PCi_ja defaults to the unit matrix. */ - pc = lin->pc; - for (i = 0; i < naxis; i++) { - for (j = 0; j < naxis; j++) { + // PCi_ja defaults to the unit matrix. + double *pc = lin->pc; + for (int i = 0; i < naxis; i++) { + for (int j = 0; j < naxis; j++) { if (j == i) { *pc = 1.0; } else { @@ -181,175 +213,355 @@ struct linprm *lin; } } - - /* CDELTia defaults to 1.0. */ - for (i = 0; i < naxis; i++) { + // CDELTia defaults to 1.0. + for (int i = 0; i < naxis; i++) { lin->cdelt[i] = 1.0; } + lin->flag = 0; return 0; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- -int lincpy(alloc, linsrc, lindst) +int lindis(int sequence, struct linprm *lin, struct disprm *dis) -int alloc; -const struct linprm *linsrc; -struct linprm *lindst; +{ + return lindist(sequence, lin, dis, -1); +} + +//---------------------------------------------------------------------------- + +int lindist(int sequence, struct linprm *lin, struct disprm *dis, int ndpmax) { - static const char *function = "lincpy"; + static const char *function = "lindist"; - int i, j, naxis, status; - const double *srcp; - double *dstp; - struct wcserr **err; + if (lin == 0x0) return LINERR_NULL_POINTER; + struct wcserr **err = &(lin->err); + + if (sequence == 1) { + if (lin->m_dispre) { + disfree(lin->m_dispre); + free(lin->m_dispre); + } + + lin->dispre = dis; + lin->m_flag = LINSET; + lin->m_dispre = dis; + + } else if (sequence == 2) { + if (lin->m_disseq) { + disfree(lin->m_disseq); + free(lin->m_disseq); + } + + lin->disseq = dis; + lin->m_flag = LINSET; + lin->m_disseq = dis; + + } else { + return wcserr_set(WCSERR_SET(LINERR_DISTORT_INIT), + "Invalid sequence (%d)", sequence); + } + + if (dis) { + int status = disinit(1, lin->naxis, dis, ndpmax); + if (status) { + return wcserr_set(LIN_ERRMSG(lin_diserr[status])); + } + } + + return 0; +} + +//---------------------------------------------------------------------------- + +int lincpy(int alloc, const struct linprm *linsrc, struct linprm *lindst) + +{ + static const char *function = "lincpy"; if (linsrc == 0x0) return LINERR_NULL_POINTER; if (lindst == 0x0) return LINERR_NULL_POINTER; - err = &(lindst->err); + struct wcserr **err = &(lindst->err); - naxis = linsrc->naxis; + int naxis = linsrc->naxis; if (naxis < 1) { return wcserr_set(WCSERR_SET(LINERR_MEMORY), "naxis must be positive (got %d)", naxis); } - if ((status = linini(alloc, naxis, lindst))) { + int status = lininit(alloc, naxis, lindst, 0); + if (status) { return status; } - srcp = linsrc->crpix; - dstp = lindst->crpix; - for (j = 0; j < naxis; j++) { + const double *srcp = linsrc->crpix; + double *dstp = lindst->crpix; + for (int j = 0; j < naxis; j++) { *(dstp++) = *(srcp++); } srcp = linsrc->pc; dstp = lindst->pc; - for (i = 0; i < naxis; i++) { - for (j = 0; j < naxis; j++) { + for (int i = 0; i < naxis; i++) { + for (int j = 0; j < naxis; j++) { *(dstp++) = *(srcp++); } } srcp = linsrc->cdelt; dstp = lindst->cdelt; - for (i = 0; i < naxis; i++) { + for (int i = 0; i < naxis; i++) { *(dstp++) = *(srcp++); } - return 0; -} + if (linsrc->dispre) { + if (!lindst->dispre) { + if ((lindst->dispre = calloc(1, sizeof(struct disprm))) == 0x0) { + return wcserr_set(LIN_ERRMSG(LINERR_MEMORY)); + } + + lindst->m_dispre = lindst->dispre; + } + + if ((status = discpy(alloc, linsrc->dispre, lindst->dispre))) { + status = wcserr_set(LIN_ERRMSG(lin_diserr[status])); + goto cleanup; + } + } + + if (linsrc->disseq) { + if (!lindst->disseq) { + if ((lindst->disseq = calloc(1, sizeof(struct disprm))) == 0x0) { + return wcserr_set(LIN_ERRMSG(LINERR_MEMORY)); + } -/*--------------------------------------------------------------------------*/ + lindst->m_disseq = lindst->disseq; + } -int linfree(lin) + if ((status = discpy(alloc, linsrc->disseq, lindst->disseq))) { + status = wcserr_set(LIN_ERRMSG(lin_diserr[status])); + goto cleanup; + } + } + +cleanup: + if (status) { + if (lindst->m_dispre) { + disfree(lindst->m_dispre); + free(lindst->m_dispre); + lindst->m_dispre = 0x0; + lindst->dispre = 0x0; + } -struct linprm *lin; + if (lindst->m_disseq) { + disfree(lindst->m_disseq); + free(lindst->m_disseq); + lindst->m_disseq = 0x0; + lindst->disseq = 0x0; + } + } + + return status; +} + +//---------------------------------------------------------------------------- + +int linfree(struct linprm *lin) { if (lin == 0x0) return LINERR_NULL_POINTER; if (lin->flag != -1) { - /* Free memory allocated by linini(). */ + // Optionally allocated by lininit() for given parameters. if (lin->m_flag == LINSET) { - if (lin->crpix == lin->m_crpix) lin->crpix = 0x0; - if (lin->pc == lin->m_pc) lin->pc = 0x0; - if (lin->cdelt == lin->m_cdelt) lin->cdelt = 0x0; + if (lin->crpix == lin->m_crpix) lin->crpix = 0x0; + if (lin->pc == lin->m_pc) lin->pc = 0x0; + if (lin->cdelt == lin->m_cdelt) lin->cdelt = 0x0; + if (lin->dispre == lin->m_dispre) lin->dispre = 0x0; + if (lin->disseq == lin->m_disseq) lin->disseq = 0x0; + + if (lin->m_crpix) free(lin->m_crpix); + if (lin->m_pc) free(lin->m_pc); + if (lin->m_cdelt) free(lin->m_cdelt); + + if (lin->m_dispre) { + disfree(lin->m_dispre); + free(lin->m_dispre); + } - if (lin->m_crpix) free(lin->m_crpix); - if (lin->m_pc) free(lin->m_pc); - if (lin->m_cdelt) free(lin->m_cdelt); + if (lin->m_disseq) { + disfree(lin->m_disseq); + free(lin->m_disseq); + } } - } - lin->m_flag = 0; - lin->m_naxis = 0; - lin->m_crpix = 0x0; - lin->m_pc = 0x0; - lin->m_cdelt = 0x0; - - - /* Free memory allocated by linset(). */ - if (lin->flag == LINSET) { + // Allocated unconditionally by linset(). if (lin->piximg) free(lin->piximg); if (lin->imgpix) free(lin->imgpix); } - lin->piximg = 0x0; - lin->imgpix = 0x0; - lin->i_naxis = 0; - if (lin->err) { - free(lin->err); - lin->err = 0x0; - } + lin->m_flag = 0; + lin->m_naxis = 0; + lin->m_crpix = 0x0; + lin->m_pc = 0x0; + lin->m_cdelt = 0x0; + lin->m_dispre = 0x0; + lin->m_disseq = 0x0; + + lin->piximg = 0x0; + lin->imgpix = 0x0; + lin->i_naxis = 0; + + wcserr_clear(&(lin->err)); lin->flag = 0; return 0; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- + +int linsize(const struct linprm *lin, int sizes[2]) + +{ + if (lin == 0x0) { + sizes[0] = sizes[1] = 0; + return 0; + } + + // Base size, in bytes. + sizes[0] = sizeof(struct linprm); -int linprt(lin) + // Total size of allocated memory, in bytes. + sizes[1] = 0; -const struct linprm *lin; + int naxis = lin->naxis; + + // linprm::crpix[]. + sizes[1] += naxis * sizeof(double); + + // linprm::pc[]. + sizes[1] += naxis*naxis * sizeof(double); + + // linprm::cdelt[]. + sizes[1] += naxis * sizeof(double); + + // linprm::dispre[]. + int exsizes[2]; + dissize(lin->dispre, exsizes); + sizes[1] += exsizes[0] + exsizes[1]; + + // linprm::disseq[]. + dissize(lin->disseq, exsizes); + sizes[1] += exsizes[0] + exsizes[1]; + + // linprm::err[]. + wcserr_size(lin->err, exsizes); + sizes[1] += exsizes[0] + exsizes[1]; + + // The remaining arrays are allocated unconditionally by linset(). + if (abs(lin->flag) != LINSET) { + return 0; + } + + // linprm::piximg[]. + sizes[1] += naxis*naxis * sizeof(double); + + // linprm::imgpix[]. + sizes[1] += naxis*naxis * sizeof(double); + + return 0; +} + +//---------------------------------------------------------------------------- + +int linenq(const struct linprm *lin, int enquiry) { - int i, j, k; + // Initialize. + if (lin == 0x0) return LINERR_NULL_POINTER; + + int answer = 0; + if (enquiry & LINENQ_MEM) { + if (lin->m_flag != LINSET) return 0; + answer = 1; + } + + if (enquiry & LINENQ_SET) { + if (abs(lin->flag) != LINSET) return 0; + answer = 1; + } + + if (enquiry & LINENQ_BYP) { + if (lin->flag != 1 && lin->flag != -LINSET) return 0; + answer = 1; + } + + return answer; +} + +//---------------------------------------------------------------------------- + +int linprt(const struct linprm *lin) + +{ if (lin == 0x0) return LINERR_NULL_POINTER; - if (lin->flag != LINSET) { + if (abs(lin->flag) != LINSET) { wcsprintf("The linprm struct is UNINITIALIZED.\n"); return 0; } + // Parameters supplied. wcsprintf(" flag: %d\n", lin->flag); wcsprintf(" naxis: %d\n", lin->naxis); + WCSPRINTF_PTR(" crpix: ", lin->crpix, "\n"); wcsprintf(" "); - for (i = 0; i < lin->naxis; i++) { - wcsprintf(" %- 11.5g", lin->crpix[i]); + for (int j = 0; j < lin->naxis; j++) { + wcsprintf(" %#- 11.5g", lin->crpix[j]); } wcsprintf("\n"); - k = 0; + int k = 0; WCSPRINTF_PTR(" pc: ", lin->pc, "\n"); - for (i = 0; i < lin->naxis; i++) { + for (int i = 0; i < lin->naxis; i++) { wcsprintf(" pc[%d][]:", i); - for (j = 0; j < lin->naxis; j++) { - wcsprintf(" %- 11.5g", lin->pc[k++]); + for (int j = 0; j < lin->naxis; j++) { + wcsprintf(" %#- 11.5g", lin->pc[k++]); } wcsprintf("\n"); } WCSPRINTF_PTR(" cdelt: ", lin->cdelt, "\n"); wcsprintf(" "); - for (i = 0; i < lin->naxis; i++) { - wcsprintf(" %- 11.5g", lin->cdelt[i]); + for (int i = 0; i < lin->naxis; i++) { + wcsprintf(" %#- 11.5g", lin->cdelt[i]); } wcsprintf("\n"); - wcsprintf(" unity: %d\n", lin->unity); - - WCSPRINTF_PTR(" err: ", lin->err, "\n"); - if (lin->err) { - wcserr_prt(lin->err, " "); - } + WCSPRINTF_PTR(" dispre: ", lin->dispre, ""); + if (lin->dispre != 0x0) wcsprintf(" (see below)"); + wcsprintf("\n"); + WCSPRINTF_PTR(" disseq: ", lin->disseq, ""); + if (lin->disseq != 0x0) wcsprintf(" (see below)"); + wcsprintf("\n"); + // Derived values. if (lin->piximg == 0x0) { wcsprintf(" piximg: (nil)\n"); } else { - k = 0; - for (i = 0; i < lin->naxis; i++) { + int k = 0; + for (int i = 0; i < lin->naxis; i++) { wcsprintf("piximg[%d][]:", i); - for (j = 0; j < lin->naxis; j++) { - wcsprintf(" %- 11.5g", lin->piximg[k++]); + for (int j = 0; j < lin->naxis; j++) { + wcsprintf(" %#- 11.5g", lin->piximg[k++]); } wcsprintf("\n"); } @@ -358,16 +570,28 @@ const struct linprm *lin; if (lin->imgpix == 0x0) { wcsprintf(" imgpix: (nil)\n"); } else { - k = 0; - for (i = 0; i < lin->naxis; i++) { + int k = 0; + for (int i = 0; i < lin->naxis; i++) { wcsprintf("imgpix[%d][]:", i); - for (j = 0; j < lin->naxis; j++) { - wcsprintf(" %- 11.5g", lin->imgpix[k++]); + for (int j = 0; j < lin->naxis; j++) { + wcsprintf(" %#- 11.5g", lin->imgpix[k++]); } wcsprintf("\n"); } } + wcsprintf(" i_naxis: %d\n", lin->i_naxis); + wcsprintf(" unity: %d\n", lin->unity); + wcsprintf(" affine: %d\n", lin->affine); + wcsprintf(" simple: %d\n", lin->simple); + + // Error handling. + WCSPRINTF_PTR(" err: ", lin->err, "\n"); + if (lin->err) { + wcserr_prt(lin->err, " "); + } + + // Memory management. wcsprintf(" m_flag: %d\n", lin->m_flag); wcsprintf(" m_naxis: %d\n", lin->m_naxis); WCSPRINTF_PTR(" m_crpix: ", lin->m_crpix, ""); @@ -379,33 +603,62 @@ const struct linprm *lin; WCSPRINTF_PTR(" m_cdelt: ", lin->m_cdelt, ""); if (lin->m_cdelt == lin->cdelt) wcsprintf(" (= cdelt)"); wcsprintf("\n"); + WCSPRINTF_PTR(" m_dispre: ", lin->m_dispre, ""); + if (lin->dispre && lin->m_dispre == lin->dispre) wcsprintf(" (= dispre)"); + wcsprintf("\n"); + WCSPRINTF_PTR(" m_disseq: ", lin->m_disseq, ""); + if (lin->disseq && lin->m_disseq == lin->disseq) wcsprintf(" (= disseq)"); + wcsprintf("\n"); + + // Distortion parameters (from above). + if (lin->dispre) { + wcsprintf("\n"); + wcsprintf("dispre.*\n"); + disprt(lin->dispre); + } + + if (lin->disseq) { + wcsprintf("\n"); + wcsprintf("disseq.*\n"); + disprt(lin->disseq); + } return 0; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- + +int linperr(const struct linprm *lin, const char *prefix) + +{ + if (lin == 0x0) return LINERR_NULL_POINTER; + + if (lin->err && wcserr_prt(lin->err, prefix) == 0) { + if (lin->dispre) wcserr_prt(lin->dispre->err, prefix); + if (lin->disseq) wcserr_prt(lin->disseq->err, prefix); + } + + return 0; +} -int linset(lin) +//---------------------------------------------------------------------------- -struct linprm *lin; +int linset(struct linprm *lin) { static const char *function = "linset"; - int i, j, n, status; - double *pc, *piximg; - struct wcserr **err; - if (lin == 0x0) return LINERR_NULL_POINTER; - err = &(lin->err); + if (lin->flag == -LINSET) return 0; + struct wcserr **err = &(lin->err); - n = lin->naxis; + int naxis = lin->naxis; - /* Check for a unit matrix. */ + // Check for a unit matrix. lin->unity = 1; - pc = lin->pc; - for (i = 0; i < n; i++) { - for (j = 0; j < n; j++) { + double *pc = lin->pc; + for (int i = 0; i < naxis; i++) { + for (int j = 0; j < naxis; j++) { if (j == i) { if (*(pc++) != 1.0) { lin->unity = 0; @@ -422,207 +675,610 @@ struct linprm *lin; if (lin->unity) { - if (lin->flag == LINSET) { - /* Free memory that may have been allocated previously. */ + if (abs(lin->flag) == LINSET) { + // Free memory that may have been allocated previously. if (lin->piximg) free(lin->piximg); if (lin->imgpix) free(lin->imgpix); } - lin->piximg = 0x0; - lin->imgpix = 0x0; + lin->piximg = 0x0; + lin->imgpix = 0x0; lin->i_naxis = 0; + // Check cdelt. + for (int i = 0; i < naxis; i++) { + if (lin->cdelt[i] == 0.0) { + return wcserr_set(LIN_ERRMSG(LINERR_SINGULAR_MTX)); + } + } + } else { - if (lin->flag != LINSET || lin->i_naxis < n) { - if (lin->flag == LINSET) { - /* Free memory that may have been allocated previously. */ + if (abs(lin->flag) != LINSET || lin->i_naxis < naxis) { + if (abs(lin->flag) == LINSET) { + // Free memory that may have been allocated previously. if (lin->piximg) free(lin->piximg); if (lin->imgpix) free(lin->imgpix); } - /* Allocate memory for internal arrays. */ - if (!(lin->piximg = calloc(n*n, sizeof(double)))) { + // Allocate memory for internal arrays. + if ((lin->piximg = calloc(naxis*naxis, sizeof(double))) == 0x0) { return wcserr_set(LIN_ERRMSG(LINERR_MEMORY)); } - if (!(lin->imgpix = calloc(n*n, sizeof(double)))) { + if ((lin->imgpix = calloc(naxis*naxis, sizeof(double))) == 0x0) { free(lin->piximg); return wcserr_set(LIN_ERRMSG(LINERR_MEMORY)); } - lin->i_naxis = n; + lin->i_naxis = naxis; } - /* Compute the pixel-to-image transformation matrix. */ - pc = lin->pc; - piximg = lin->piximg; - for (i = 0; i < n; i++) { - for (j = 0; j < n; j++) { - *(piximg++) = lin->cdelt[i] * (*(pc++)); + // Compute the pixel-to-image transformation matrix. + pc = lin->pc; + double *piximg = lin->piximg; + for (int i = 0; i < naxis; i++) { + if (lin->disseq == 0x0) { + // No sequent distortions. Incorporate cdelt into piximg. + for (int j = 0; j < naxis; j++) { + *(piximg++) = lin->cdelt[i] * (*(pc++)); + } + } else { + for (int j = 0; j < naxis; j++) { + *(piximg++) = *(pc++); + } } } - /* Compute the image-to-pixel transformation matrix. */ - if ((status = matinv(n, lin->piximg, lin->imgpix))) { + // Compute the image-to-pixel transformation matrix. + int status = matinv(naxis, lin->piximg, lin->imgpix); + if (status) { return wcserr_set(LIN_ERRMSG(status)); } } - lin->flag = LINSET; + // Set up the distortion functions. + lin->affine = 1; + if (lin->dispre) { + (lin->dispre)->flag = 0; + int status = disset(lin->dispre); + if (status) { + return wcserr_set(LIN_ERRMSG(lin_diserr[status])); + } + + lin->affine = 0; + } + + if (lin->disseq) { + (lin->disseq)->flag = 0; + int status = disset(lin->disseq); + if (status) { + return wcserr_set(LIN_ERRMSG(lin_diserr[status])); + } + + lin->affine = 0; + } + + lin->simple = lin->unity && lin->affine; + + lin->flag = (lin->flag == 1) ? -LINSET : LINSET; return 0; } -/*--------------------------------------------------------------------------*/ - -int linp2x(lin, ncoord, nelem, pixcrd, imgcrd) +//---------------------------------------------------------------------------- -struct linprm *lin; -int ncoord, nelem; -const double pixcrd[]; -double imgcrd[]; +int linp2x( + struct linprm *lin, + int ncoord, + int nelem, + const double pixcrd[], + double imgcrd[]) { - int i, j, k, n, status; - double temp; - register const double *pix; - register double *img, *piximg; + static const char *function = "linp2x"; - - /* Initialize. */ + // Initialize. if (lin == 0x0) return LINERR_NULL_POINTER; - if (lin->flag != LINSET) { - if ((status = linset(lin))) return status; + struct wcserr **err = &(lin->err); + + if (abs(lin->flag) != LINSET) { + int status = linset(lin); + if (status) { + return status; + } } - n = lin->naxis; + int naxis = lin->naxis; - /* Convert pixel coordinates to intermediate world coordinates. */ - pix = pixcrd; - img = imgcrd; + // Convert pixel coordinates to intermediate world coordinates. + const double *pix = pixcrd; + double *img = imgcrd; - if (lin->unity) { - for (k = 0; k < ncoord; k++) { - for (i = 0; i < n; i++) { + if (lin->simple) { + // Handle the simplest and most common case with maximum efficiency. + int nelemn = nelem - naxis; + for (int k = 0; k < ncoord; k++) { + for (int i = 0; i < naxis; i++) { *(img++) = lin->cdelt[i] * (*(pix++) - lin->crpix[i]); } - pix += (nelem - n); - img += (nelem - n); + pix += nelemn; + img += nelemn; + } + + } else if (lin->affine) { + // No distortions. + int ndbl = naxis * sizeof(double); + int nelemn = nelem - naxis; + for (int k = 0; k < ncoord; k++) { + memset(img, 0, ndbl); + + for (int j = 0; j < naxis; j++) { + // cdelt will have been incorporated into piximg. + double *piximg = lin->piximg + j; + + // Column-wise multiplication allows this to be cached. + double temp = *(pix++) - lin->crpix[j]; + for (int i = 0; i < naxis; i++, piximg += naxis) { + if (*piximg != 0.0) { + // Skipped if *piximg == 0.0 in case temp is NaN, noting that + // zero * NaN = NaN. Thus, if the PCij matrix is diagonal, this + // quarantines NaN coordinate elements of pixcrd[] from infecting + // non-NaN elements. + img[i] += *piximg * temp; + } + } + } + + pix += nelemn; + img += nelem; } } else { - for (k = 0; k < ncoord; k++) { - for (i = 0; i < n; i++) { - img[i] = 0.0; + // Distortions are present. + int ndbl = naxis * sizeof(double); + double *tmp = calloc(naxis, sizeof(double)); + if (tmp == 0x0) { + return wcserr_set(LIN_ERRMSG(LINERR_MEMORY)); + } + + for (int k = 0; k < ncoord; k++) { + if (lin->dispre) { + int status = disp2x(lin->dispre, pix, tmp); + if (status) { + return wcserr_set(LIN_ERRMSG(lin_diserr[status])); + } + } else { + memcpy(tmp, pix, ndbl); } - for (j = 0; j < n; j++) { - /* Column-wise multiplication allows this to be cached. */ - temp = *(pix++) - lin->crpix[j]; + if (lin->unity) { + for (int i = 0; i < naxis; i++) { + img[i] = tmp[i] - lin->crpix[i]; + } - piximg = lin->piximg + j; - for (i = 0; i < n; i++, piximg += n) { - img[i] += *piximg * temp; + } else { + for (int j = 0; j < naxis; j++) { + tmp[j] -= lin->crpix[j]; + } + + double *piximg = lin->piximg; + for (int i = 0; i < naxis; i++) { + img[i] = 0.0; + for (int j = 0; j < naxis; j++) { + if (*piximg != 0.0) { + // See comment above. + img[i] += *piximg * tmp[j]; + } + piximg++; + } } } - pix += (nelem - n); + if (lin->disseq) { + int status = disp2x(lin->disseq, img, tmp); + if (status) { + free(tmp); + return wcserr_set(LIN_ERRMSG(lin_diserr[status])); + } + + // With sequent distortions, cdelt is not incorporated into piximg... + for (int i = 0; i < naxis; i++) { + img[i] = lin->cdelt[i] * tmp[i]; + } + + } else if (lin->unity) { + // ...nor if the matrix is unity. + for (int i = 0; i < naxis; i++) { + img[i] *= lin->cdelt[i]; + } + } + + pix += nelem; img += nelem; } + + free(tmp); } return 0; } -/*--------------------------------------------------------------------------*/ - -int linx2p(lin, ncoord, nelem, imgcrd, pixcrd) +//---------------------------------------------------------------------------- -struct linprm *lin; -int ncoord, nelem; -const double imgcrd[]; -double pixcrd[]; +int linx2p( + struct linprm *lin, + int ncoord, + int nelem, + const double imgcrd[], + double pixcrd[]) { - int i, j, k, n, status; - register const double *img; - register double *imgpix, *pix; + static const char *function = "linx2p"; + int status = 0; - /* Initialize. */ + // Initialize. if (lin == 0x0) return LINERR_NULL_POINTER; - if (lin->flag != LINSET) { - if ((status = linset(lin))) return status; + struct wcserr **err = &(lin->err); + + if (abs(lin->flag) != LINSET) { + if ((status = linset(lin))) { + return status; + } } - n = lin->naxis; + int naxis = lin->naxis; - /* Convert intermediate world coordinates to pixel coordinates. */ - img = imgcrd; - pix = pixcrd; + // Convert intermediate world coordinates to pixel coordinates. + const double *img = imgcrd; + double *pix = pixcrd; - if (lin->unity) { - for (k = 0; k < ncoord; k++) { - for (j = 0; j < n; j++) { + if (lin->simple) { + // Handle the simplest and most common case with maximum efficiency. + int nelemn = nelem - naxis; + for (int k = 0; k < ncoord; k++) { + for (int j = 0; j < naxis; j++) { *(pix++) = (*(img++) / lin->cdelt[j]) + lin->crpix[j]; } - pix += (nelem - n); - img += (nelem - n); + img += nelemn; + pix += nelemn; } - } else { - for (k = 0; k < ncoord; k++) { - imgpix = lin->imgpix; + } else if (lin->affine) { + // No distortions. + int nelemn = nelem - naxis; + for (int k = 0; k < ncoord; k++) { + // cdelt will have been incorporated into imgpix. + double *imgpix = lin->imgpix; - for (j = 0; j < n; j++) { + for (int j = 0; j < naxis; j++) { *pix = 0.0; - for (i = 0; i < n; i++) { - *pix += *imgpix * img[i]; + for (int i = 0; i < naxis; i++) { + if (*imgpix != 0.0) { + // Skipped if *imgpix == 0.0 in case img[i] is NaN, noting that + // zero * NaN = NaN. Thus, if the PCij matrix is diagonal, this + // quarantines NaN coordinate elements of imgcrd[] from infecting + // non-NaN elements. + *pix += *imgpix * img[i]; + } imgpix++; } *(pix++) += lin->crpix[j]; } - pix += (nelem - n); img += nelem; + pix += nelemn; } + + } else { + // Distortions are present. + int ndbl = naxis * sizeof(double); + double *tmp = calloc(naxis, sizeof(double)); + if (tmp == 0x0) { + return wcserr_set(LIN_ERRMSG(LINERR_MEMORY)); + } + + for (int k = 0; k < ncoord; k++) { + if (lin->disseq) { + // With sequent distortions, cdelt is not incorporated into imgpix... + for (int i = 0; i < naxis; i++) { + tmp[i] = img[i] / lin->cdelt[i]; + } + + if ((status = disx2p(lin->disseq, tmp, pix))) { + free(tmp); + return wcserr_set(LIN_ERRMSG(lin_diserr[status])); + } + + memcpy(tmp, pix, ndbl); + + } else if (lin->unity) { + // ...nor if the matrix is unity. + for (int i = 0; i < naxis; i++) { + tmp[i] = img[i] / lin->cdelt[i]; + } + + } else { + // cdelt will have been incorporated into imgpix. + memcpy(tmp, img, ndbl); + } + + if (lin->unity) { + for (int j = 0; j < naxis; j++) { + pix[j] = tmp[j] + lin->crpix[j]; + } + + } else { + double *imgpix = lin->imgpix; + for (int j = 0; j < naxis; j++) { + pix[j] = lin->crpix[j]; + for (int i = 0; i < naxis; i++) { + if (*imgpix != 0.0) { + // See comment above. + pix[j] += *imgpix * tmp[i]; + } + imgpix++; + } + } + } + + if (lin->dispre) { + memcpy(tmp, pix, ndbl); + + if ((status = disx2p(lin->dispre, tmp, pix))) { + free(tmp); + return wcserr_set(LIN_ERRMSG(lin_diserr[status])); + } + } + + img += nelem; + pix += nelem; + } + + free(tmp); } return 0; } -/*--------------------------------------------------------------------------*/ - -int matinv(int n, const double mat[], double inv[]) +//---------------------------------------------------------------------------- + +int linwarp( + struct linprm *lin, + const double pixblc[], + const double pixtrc[], + const double pixsamp[], + int *nsamp, + double maxdis[], + double *maxtot, + double avgdis[], + double *avgtot, + double rmsdis[], + double *rmstot) { - register int i, ij, ik, j, k, kj, pj; - int itemp, *mxl, *lxm, pivot; - double colmax, *lu, *rowmax, dtemp; + static const char *function = "linwarp"; + + // Initialize. + if (lin == 0x0) return LINERR_NULL_POINTER; + struct wcserr **err = &(lin->err); + + int naxis = lin->naxis; + + if (nsamp) *nsamp = 0; + for (int j = 0; j < naxis; j++) { + if (maxdis) maxdis[j] = 0.0; + if (avgdis) avgdis[j] = 0.0; + if (rmsdis) rmsdis[j] = 0.0; + } + if (maxtot) *maxtot = 0.0; + if (avgtot) *avgtot = 0.0; + if (rmstot) *rmstot = 0.0; + + // Quick return if no distortions. + if (lin->affine) return 0; + + int status = 0; + + // It's easier if there are no sequent distortions! + if (lin->disseq == 0x0) { + status = diswarp(lin->dispre, pixblc, pixtrc, pixsamp, nsamp, maxdis, + maxtot, avgdis, avgtot, rmsdis, rmstot); + return wcserr_set(LIN_ERRMSG(lin_diserr[status])); + } + + // Make a reference copy of lin without distortions. + double *pixinc = 0x0; + double *pix0 = 0x0; + struct linprm affine; + affine.flag = -1; + + status = lincpy(1, lin, &affine) || + lindist(1, &affine, 0x0, 0) || + lindist(2, &affine, 0x0, 0) || + linset(&affine); + if (status) { + wcserr_set(LIN_ERRMSG(status)); + goto cleanup; + } + + // Work out increments on each axis. + int ncoord = 0; + if ((pixinc = calloc(naxis, sizeof(double))) == 0x0) { + wcserr_set(LIN_ERRMSG(LINERR_MEMORY)); + goto cleanup; + } + + for (int j = 0; j < naxis; j++) { + double pixspan = pixtrc[j] - (pixblc ? pixblc[j] : 1.0); + + if (pixsamp == 0x0) { + pixinc[j] = 1.0; + } else if (pixsamp[j] == 0.0) { + pixinc[j] = 1.0; + } else if (pixsamp[j] > 0.0) { + pixinc[j] = pixsamp[j]; + } else if (pixsamp[j] > -1.5) { + pixinc[j] = 2.0*pixspan; + } else { + pixinc[j] = pixspan / ((int)(-pixsamp[j] - 0.5)); + } + + if (j == 0) { + // Number of samples on axis 1. + ncoord = 1 + (int)((pixspan/pixinc[0]) + 0.5); + } + } + + // Allocate memory in bulk for processing the image row by row. + if ((pix0 = calloc((3*ncoord+3)*naxis, sizeof(double))) == 0x0) { + status = wcserr_set(LIN_ERRMSG(LINERR_MEMORY)); + goto cleanup; + } + + // Carve up the allocated memory. + double *img = pix0 + naxis*ncoord; + double *pix1 = img + naxis*ncoord; + double *pixend = pix1 + naxis*ncoord; + double *sumdis = pixend + naxis; + double *ssqdis = sumdis + naxis; + + + // Set up the array of pixel coordinates. + for (int j = 0; j < naxis; j++) { + pix0[j] = pixblc ? pixblc[j] : 1.0; + pixend[j] = pixtrc[j] + 0.5*pixinc[j]; + } + + double *pix0p = pix0 + naxis; + for (int i = 1; i < ncoord; i++) { + *(pix0p++) = pix0[0] + i*pixinc[0]; + + for (int j = 1; j < naxis; j++) { + *(pix0p++) = pix0[j]; + } + } + // Initialize accumulators. + for (int j = 0; j < naxis; j++) { + sumdis[j] = 0.0; + ssqdis[j] = 0.0; + } + double sumtot = 0.0; + double ssqtot = 0.0; + + + // Loop over N dimensions. + int carry = 0; + while (carry == 0) { + if ((status = linp2x(lin, ncoord, naxis, pix0, img))) { + // (Preserve the error message set by linp2x().) + goto cleanup; + } + + if ((status = linx2p(&affine, ncoord, naxis, img, pix1))) { + // (Preserve the error message set by linx2p().) + goto cleanup; + } + + // Accumulate statistics. + double *pix0p = pix0; + double *pix1p = pix1; + for (int i = 0; i < ncoord; i++) { + (*nsamp)++; + + double dssq = 0.0; + for (int j = 0; j < naxis; j++) { + double dpix = *(pix1p++) - *(pix0p++); + double dpx2 = dpix*dpix; + + sumdis[j] += dpix; + ssqdis[j] += dpx2; + + if (maxdis && (dpix = fabs(dpix)) > maxdis[j]) maxdis[j] = dpix; + + dssq += dpx2; + } + + double totdis = sqrt(dssq); + sumtot += totdis; + ssqtot += totdis*totdis; + + if (maxtot && *maxtot < totdis) *maxtot = totdis; + } + + // Next array of pixel coordinates. + for (int j = 1; j < naxis; j++) { + pix0[j] += pixinc[j]; + if ((carry = (pix0[j] > pixend[j]))) { + pix0[j] = pixblc ? pixblc[j] : 1.0; + } + + pix0p = pix0 + naxis + j; + for (int i = 1; i < ncoord; i++) { + *pix0p = pix0[j]; + pix0p += naxis; + } - /* Allocate memory for internal arrays. */ - if (!(mxl = calloc(n, sizeof(int)))) { + if (carry == 0) break; + } + } + + + // Compute the means and RMSs. + for (int j = 0; j < naxis; j++) { + ssqdis[j] /= *nsamp; + sumdis[j] /= *nsamp; + if (avgdis) avgdis[j] = sumdis[j]; + if (rmsdis) rmsdis[j] = sqrt(ssqdis[j] - sumdis[j]*sumdis[j]); + } + + ssqtot /= *nsamp; + sumtot /= *nsamp; + if (avgtot) *avgtot = sumtot; + if (rmstot) *rmstot = sqrt(ssqtot - sumtot*sumtot); + + +cleanup: + linfree(&affine); + if (pixinc) free(pixinc); + if (pix0) free(pix0); + + return status; +} + +//---------------------------------------------------------------------------- + +int matinv(int n, const double mat[], double inv[]) + +{ + // Allocate memory for internal arrays. + int *mxl = calloc(n, sizeof(int)); + if (mxl == 0x0) { return LINERR_MEMORY; } - if (!(lxm = calloc(n, sizeof(int)))) { + + int *lxm = calloc(n, sizeof(int)); + if (lxm == 0x0) { free(mxl); return LINERR_MEMORY; } - if (!(rowmax = calloc(n, sizeof(double)))) { + double *rowmax = calloc(n, sizeof(double)); + if (rowmax == 0x0) { free(mxl); free(lxm); return LINERR_MEMORY; } - if (!(lu = calloc(n*n, sizeof(double)))) { + double *lu = calloc(n*n, sizeof(double)); + if (lu == 0x0) { free(mxl); free(lxm); free(rowmax); @@ -630,21 +1286,22 @@ int matinv(int n, const double mat[], double inv[]) } - /* Initialize arrays. */ - for (i = 0, ij = 0; i < n; i++) { - /* Vector that records row interchanges. */ + // Initialize arrays. + int ij = 0; + for (int i = 0; i < n; i++) { + // Vector that records row interchanges. mxl[i] = i; rowmax[i] = 0.0; - for (j = 0; j < n; j++, ij++) { - dtemp = fabs(mat[ij]); + for (int j = 0; j < n; j++, ij++) { + double dtemp = fabs(mat[ij]); if (dtemp > rowmax[i]) rowmax[i] = dtemp; lu[ij] = mat[ij]; } - /* A row of zeroes indicates a singular matrix. */ + // A row of zeroes indicates a singular matrix. if (rowmax[i] == 0.0) { free(mxl); free(lxm); @@ -655,15 +1312,15 @@ int matinv(int n, const double mat[], double inv[]) } - /* Form the LU triangular factorization using scaled partial pivoting. */ - for (k = 0; k < n; k++) { - /* Decide whether to pivot. */ - colmax = fabs(lu[k*n+k]) / rowmax[k]; - pivot = k; + // Form the LU triangular factorization using scaled partial pivoting. + for (int k = 0; k < n; k++) { + // Decide whether to pivot. + int pivot = k; + double colmax = fabs(lu[k*n+k]) / rowmax[k]; - for (i = k+1; i < n; i++) { - ik = i*n + k; - dtemp = fabs(lu[ik]) / rowmax[i]; + for (int i = k+1; i < n; i++) { + int ik = i*n + k; + double dtemp = fabs(lu[ik]) / rowmax[i]; if (dtemp > colmax) { colmax = dtemp; pivot = i; @@ -671,35 +1328,37 @@ int matinv(int n, const double mat[], double inv[]) } if (pivot > k) { - /* We must pivot, interchange the rows of the design matrix. */ - for (j = 0, pj = pivot*n, kj = k*n; j < n; j++, pj++, kj++) { - dtemp = lu[pj]; + // We must pivot, interchange the rows of the design matrix. + int kj = k*n; + int pj = pivot*n; + for (int j = 0; j < n; j++, pj++, kj++) { + double dtemp = lu[pj]; lu[pj] = lu[kj]; lu[kj] = dtemp; } - /* Amend the vector of row maxima. */ - dtemp = rowmax[pivot]; + // Amend the vector of row maxima. + double dtemp = rowmax[pivot]; rowmax[pivot] = rowmax[k]; rowmax[k] = dtemp; - /* Record the interchange for later use. */ - itemp = mxl[pivot]; + // Record the interchange for later use. + int itemp = mxl[pivot]; mxl[pivot] = mxl[k]; mxl[k] = itemp; } - /* Gaussian elimination. */ - for (i = k+1; i < n; i++) { - ik = i*n + k; + // Gaussian elimination. + for (int i = k+1; i < n; i++) { + int ik = i*n + k; - /* Nothing to do if lu[ik] is zero. */ + // Nothing to do if lu[ik] is zero. if (lu[ik] != 0.0) { - /* Save the scaling factor. */ + // Save the scaling factor. lu[ik] /= lu[k*n+k]; - /* Subtract rows. */ - for (j = k+1; j < n; j++) { + // Subtract rows. + for (int j = k+1; j < n; j++) { lu[i*n+j] -= lu[ik]*lu[k*n+j]; } } @@ -707,43 +1366,44 @@ int matinv(int n, const double mat[], double inv[]) } - /* mxl[i] records which row of mat corresponds to row i of lu. */ - /* lxm[i] records which row of lu corresponds to row i of mat. */ - for (i = 0; i < n; i++) { + // mxl[i] records which row of mat corresponds to row i of lu. + // lxm[i] records which row of lu corresponds to row i of mat. + for (int i = 0; i < n; i++) { lxm[mxl[i]] = i; } - /* Determine the inverse matrix. */ - for (i = 0, ij = 0; i < n; i++) { - for (j = 0; j < n; j++, ij++) { + // Determine the inverse matrix. + ij = 0; + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++, ij++) { inv[ij] = 0.0; } } - for (k = 0; k < n; k++) { + for (int k = 0; k < n; k++) { inv[lxm[k]*n+k] = 1.0; - /* Forward substitution. */ - for (i = lxm[k]+1; i < n; i++) { - for (j = lxm[k]; j < i; j++) { + // Forward substitution. + for (int i = lxm[k]+1; i < n; i++) { + for (int j = lxm[k]; j < i; j++) { inv[i*n+k] -= lu[i*n+j]*inv[j*n+k]; } } - /* Backward substitution. */ - for (i = n-1; i >= 0; i--) { - for (j = i+1; j < n; j++) { + // Backward substitution. + for (int i = n-1; i >= 0; i--) { + for (int j = i+1; j < n; j++) { inv[i*n+k] -= lu[i*n+j]*inv[j*n+k]; } inv[i*n+k] /= lu[i*n+i]; } - } + } - free(mxl); - free(lxm); - free(rowmax); - free(lu); + free(mxl); + free(lxm); + free(rowmax); + free(lu); - return 0; + return 0; } diff --git a/cextern/wcslib/C/lin.h b/cextern/wcslib/C/lin.h index 0c41b4b716ca..9372fb2e7296 100644 --- a/cextern/wcslib/C/lin.h +++ b/cextern/wcslib/C/lin.h @@ -1,7 +1,6 @@ /*============================================================================ - - WCSLIB 4.14 - an implementation of the FITS WCS standard. - Copyright (C) 1995-2012, Mark Calabretta + WCSLIB 8.6 - an implementation of the FITS WCS standard. + Copyright (C) 1995-2026, Mark Calabretta This file is part of WCSLIB. @@ -18,33 +17,36 @@ You should have received a copy of the GNU Lesser General Public License along with WCSLIB. If not, see http://www.gnu.org/licenses. - Direct correspondence concerning WCSLIB to mark@calabretta.id.au - Author: Mark Calabretta, Australia Telescope National Facility, CSIRO. - http://www.atnf.csiro.au/people/Mark.Calabretta - $Id: lin.h,v 4.14 2012/07/13 10:02:27 cal103 Exp $ + http://www.atnf.csiro.au/computing/software/wcs + $Id: lin.h,v 8.6 2026/03/29 13:53:56 mcalabre Exp $ *============================================================================= * -* WCSLIB 4.14 - C routines that implement the FITS World Coordinate System -* (WCS) standard. Refer to -* -* "Representations of world coordinates in FITS", -* Greisen, E.W., & Calabretta, M.R. 2002, A&A, 395, 1061 (Paper I) -* -* Refer to the README file provided with WCSLIB for an overview of the -* library. +* WCSLIB 8.6 - C routines that implement the FITS World Coordinate System +* (WCS) standard. Refer to the README file provided with WCSLIB for an +* overview of the library. * * * Summary of the lin routines * --------------------------- -* These routines apply the linear transformation defined by the FITS WCS -* standard. They are based on the linprm struct which contains all -* information needed for the computations. The struct contains some members -* that must be set by the user, and others that are maintained by these -* routines, somewhat like a C++ class but with no encapsulation. +* Routines in this suite apply the linear transformation defined by the FITS +* World Coordinate System (WCS) standard, as described in * -* Three routines, linini(), lincpy(), and linfree() are provided to manage the -* linprm struct, and another, linprt(), prints its contents. += "Representations of world coordinates in FITS", += Greisen, E.W., & Calabretta, M.R. 2002, A&A, 395, 1061 (WCS Paper I) +* +* These routines are based on the linprm struct which contains all information +* needed for the computations. The struct contains some members that must be +* set by the user, and others that are maintained by these routines, somewhat +* like a C++ class but with no encapsulation. +* +* Six routines, linini(), lininit(), lindis(), lindist() lincpy(), and +* linfree() are provided to manage the linprm struct, linsize() computes its +* total size including allocated memory, linenq() returns information about +* the state of the struct, and linprt() prints its contents. +* +* linperr() prints the error message(s) (if any) stored in a linprm struct, +* and the disprm structs that it may contain. * * A setup routine, linset(), computes intermediate values in the linprm struct * from parameters in it that were supplied by the user. The struct always @@ -53,19 +55,30 @@ * * linp2x() and linx2p() implement the WCS linear transformations. * +* An auxiliary routine, linwarp(), computes various measures of the distortion +* over a specified range of pixel coordinates. +* * An auxiliary matrix inversion routine, matinv(), is included. It uses * LU-triangular factorization with scaled partial pivoting. * * * linini() - Default constructor for the linprm struct * ---------------------------------------------------- -* linini() allocates memory for arrays in a linprm struct and sets all members -* of the struct to default values. +* linini() is a thin wrapper on lininit(). It invokes it with ndpmax set +* to -1 which causes it to use the value of the global variable NDPMAX. It +* is thereby potentially thread-unsafe if NDPMAX is altered dynamically via +* disndp(). Use lininit() for a thread-safe alternative in this case. * -* PLEASE NOTE: every linprm struct should be initialized by linini(), possibly +* +* lininit() - Default constructor for the linprm struct +* ----------------------------------------------------- +* lininit() allocates memory for arrays in a linprm struct and sets all +* members of the struct to default values. +* +* PLEASE NOTE: every linprm struct must be initialized by lininit(), possibly * repeatedly. On the first invokation, and only the first invokation, * linprm::flag must be set to -1 to initialize memory management, regardless -* of whether linini() will actually be used to allocate memory. +* of whether lininit() will actually be used to allocate memory. * * Given: * alloc int If true, allocate memory unconditionally for arrays in @@ -88,6 +101,13 @@ * (memory leaks may result if it had already been * initialized). * +* Given: +* ndpmax int The number of DPja or DQia keywords to allocate space +* for. If set to -1, the value of the global variable +* NDPMAX will be used. This is potentially +* thread-unsafe if disndp() is being used dynamically to +* alter its value. +* * Function return value: * int Status return value: * 0: Success. @@ -98,10 +118,63 @@ * linprm::err if enabled, see wcserr_enable(). * * +* lindis() - Assign a distortion to a linprm struct +* ------------------------------------------------- +* lindis() is a thin wrapper on lindist(). It invokes it with ndpmax set +* to -1 which causes the value of the global variable NDPMAX to be used (by +* disinit()). It is thereby potentially thread-unsafe if NDPMAX is altered +* dynamically via disndp(). Use lindist() for a thread-safe alternative in +* this case. +* +* +* lindist() - Assign a distortion to a linprm struct +* -------------------------------------------------- +* lindist() may be used to assign the address of a disprm struct to +* linprm::dispre or linprm::disseq. The linprm struct must already have been +* initialized by lininit(). +* +* The disprm struct must have been allocated from the heap (e.g. using +* malloc(), calloc(), etc.). lindist() will immediately initialize it via a +* call to disini() using the value of linprm::naxis. Subsequently, it will be +* reinitialized by calls to lininit(), and freed by linfree(), neither of +* which would happen if the disprm struct was assigned directly. +* +* If the disprm struct had previously been assigned via lindist(), it will be +* freed before reassignment. It is also permissable for a null disprm pointer +* to be assigned to disable the distortion correction. +* +* Given: +* sequence int Is it a prior or sequent distortion? +* 1: Prior, the assignment is to linprm::dispre. +* 2: Sequent, the assignment is to linprm::disseq. +* +* Anything else is an error. +* +* Given and returned: +* lin struct linprm* +* Linear transformation parameters. +* +* dis struct disprm* +* Distortion function parameters. +* +* Given: +* ndpmax int The number of DPja or DQia keywords to allocate space +* for. If set to -1, the value of the global variable +* NDPMAX will be used. This is potentially +* thread-unsafe if disndp() is being used dynamically to +* alter its value. +* +* Function return value: +* int Status return value: +* 0: Success. +* 1: Null linprm pointer passed. +* 4: Invalid sequence. +* +* * lincpy() - Copy routine for the linprm struct * --------------------------------------------- -* lincpy() does a deep copy of one linprm struct to another, using linini() to -* allocate memory for its arrays if required. Only the "information to be +* lincpy() does a deep copy of one linprm struct to another, using lininit() +* to allocate memory for its arrays if required. Only the "information to be * provided" part of the struct is copied; a call to linset() is required to * initialize the remainder. * @@ -133,14 +206,14 @@ * * linfree() - Destructor for the linprm struct * -------------------------------------------- -* linfree() frees memory allocated for the linprm arrays by linini() and/or -* linset(). linini() keeps a record of the memory it allocates and linfree() +* linfree() frees memory allocated for the linprm arrays by lininit() and/or +* linset(). lininit() keeps a record of the memory it allocates and linfree() * will only attempt to free this. * * PLEASE NOTE: linfree() must not be invoked on a linprm struct that was not -* initialized by linini(). +* initialized by lininit(). * -* Given: +* Given and returned: * lin struct linprm* * Linear transformation parameters. * @@ -150,6 +223,62 @@ * 1: Null linprm pointer passed. * * +* linsize() - Compute the size of a linprm struct +* ----------------------------------------------- +* linsize() computes the full size of a linprm struct, including allocated +* memory. +* +* Given: +* lin const struct linprm* +* Linear transformation parameters. +* +* If NULL, the base size of the struct and the allocated +* size are both set to zero. +* +* Returned: +* sizes int[2] The first element is the base size of the struct as +* returned by sizeof(struct linprm). +* +* The second element is the total size of memory +* allocated in the struct, in bytes, assuming that the +* allocation was done by lininit(). This figure +* includes memory allocated for members of constituent +* structs, * such as linprm::dispre. +* +* It is not an error for the struct not to have been set +* up via linset(), which normally results in additional +* memory allocation. +* +* Function return value: +* int Status return value: +* 0: Success. +* +* +* linenq() - enquire about the state of a linprm struct +* ----------------------------------------------------- +* linenq() may be used to obtain information about the state of a linprm +* struct. The function returns a true/false answer for the enquiry asked. +* +* Given: +* lin const struct linprm* +* Linear transformation parameters. +* +* enquiry int Enquiry according to the following parameters: +* LINENQ_MEM: memory in the struct is being managed by +* WCSLIB (see lininit()). +* LINENQ_SET: the struct has been set up by linset(). +* LINENQ_BYP: the struct is in bypass mode (see +* linset()). +* These may be combined by logical OR, e.g. +* LINENQ_MEM | LINENQ_SET. The enquiry result will be +* the logical AND of the individual results. +* +* Function return value: +* int Enquiry result: +* 0: No. +* 1: Yes. +* +* * linprt() - Print routine for the linprm struct * ---------------------------------------------- * linprt() prints the contents of a linprm struct using wcsprintf(). Mainly @@ -165,6 +294,26 @@ * 1: Null linprm pointer passed. * * +* linperr() - Print error messages from a linprm struct +* ----------------------------------------------------- +* linperr() prints the error message(s) (if any) stored in a linprm struct, +* and the disprm structs that it may contain. If there are no errors then +* nothing is printed. It uses wcserr_prt(), q.v. +* +* Given: +* lin const struct linprm* +* Coordinate transformation parameters. +* +* prefix const char * +* If non-NULL, each output line will be prefixed with +* this string. +* +* Function return value: +* int Status return value: +* 0: Success. +* 1: Null linprm pointer passed. +* +* * linset() - Setup routine for the linprm struct * ---------------------------------------------- * linset(), if necessary, allocates memory for the linprm::piximg and @@ -175,6 +324,13 @@ * linp2x() and linx2p() if the linprm::flag is anything other than a * predefined magic value. * +* linset() normally operates regardless of the value of linprm::flag; i.e. +* even if a struct was previously set up it will be reset unconditionally. +* However, a linprm struct may be put into "bypass" mode by invoking linset() +* initially with linprm::flag == 1 (rather than 0). linset() will return +* immediately if invoked on a struct in that state. To take a struct out of +* bypass mode, simply reset linprm::flag to zero. See also linenq(). +* * Given and returned: * lin struct linprm* * Linear transformation parameters. @@ -185,6 +341,7 @@ * 1: Null linprm pointer passed. * 2: Memory allocation failed. * 3: PCi_ja matrix is singular. +* 4: Failed to initialise distortions. * * For returns > 1, a detailed error message is set in * linprm::err if enabled, see wcserr_enable(). @@ -216,10 +373,20 @@ * 1: Null linprm pointer passed. * 2: Memory allocation failed. * 3: PCi_ja matrix is singular. +* 4: Failed to initialise distortions. +* 5: Distort error. * * For returns > 1, a detailed error message is set in * linprm::err if enabled, see wcserr_enable(). * +* Notes: +* 1. Historically, the API to linp2x() did not have a stat[] vector because +* a valid linear transformation should always succeed. However, now that +* it invokes disp2x() if distortions are present, it does have the +* potential to fail. Consequently, when distortions are present and a +* status return (stat[]) is required for each coordinate, then linp2x() +* should be invoked separately for each of them. +* * * linx2p() - World-to-pixel linear transformation * ----------------------------------------------- @@ -246,10 +413,111 @@ * 1: Null linprm pointer passed. * 2: Memory allocation failed. * 3: PCi_ja matrix is singular. +* 4: Failed to initialise distortions. +* 6: De-distort error. * * For returns > 1, a detailed error message is set in * linprm::err if enabled, see wcserr_enable(). * +* Notes: +* 1. Historically, the API to linx2p() did not have a stat[] vector because +* a valid linear transformation should always succeed. However, now that +* it invokes disx2p() if distortions are present, it does have the +* potential to fail. Consequently, when distortions are present and a +* status return (stat[]) is required for each coordinate, then linx2p() +* should be invoked separately for each of them. +* +* +* linwarp() - Compute measures of distortion +* ------------------------------------------ +* linwarp() computes various measures of the distortion over a specified range +* of pixel coordinates. +* +* All distortion measures are specified as an offset in pixel coordinates, +* as given directly by prior distortions. The offset in intermediate pixel +* coordinates given by sequent distortions is translated back to pixel +* coordinates by applying the inverse of the linear transformation matrix +* (PCi_ja or CDi_ja). The difference may be significant if the matrix +* introduced a scaling. +* +* If all distortions are prior, then linwarp() uses diswarp(), q.v. +* +* Given and returned: +* lin struct linprm* +* Linear transformation parameters plus distortions. +* +* Given: +* pixblc const double[naxis] +* Start of the range of pixel coordinates (i.e. "bottom +* left-hand corner" in the conventional FITS image +* display orientation). May be specified as a NULL +* pointer which is interpreted as (1,1,...). +* +* pixtrc const double[naxis] +* End of the range of pixel coordinates (i.e. "top +* right-hand corner" in the conventional FITS image +* display orientation). +* +* pixsamp const double[naxis] +* If positive or zero, the increment on the particular +* axis, starting at pixblc[]. Zero is interpreted as a +* unit increment. pixsamp may also be specified as a +* NULL pointer which is interpreted as all zeroes, i.e. +* unit increments on all axes. +* +* If negative, the grid size on the particular axis (the +* absolute value being rounded to the nearest integer). +* For example, if pixsamp is (-128.0,-128.0,...) then +* each axis will be sampled at 128 points between +* pixblc[] and pixtrc[] inclusive. Use caution when +* using this option on non-square images. +* +* Returned: +* nsamp int* The number of pixel coordinates sampled. +* +* Can be specified as a NULL pointer if not required. +* +* maxdis double[naxis] +* For each individual distortion function, the +* maximum absolute value of the distortion. +* +* Can be specified as a NULL pointer if not required. +* +* maxtot double* For the combination of all distortion functions, the +* maximum absolute value of the distortion. +* +* Can be specified as a NULL pointer if not required. +* +* avgdis double[naxis] +* For each individual distortion function, the +* mean value of the distortion. +* +* Can be specified as a NULL pointer if not required. +* +* avgtot double* For the combination of all distortion functions, the +* mean value of the distortion. +* +* Can be specified as a NULL pointer if not required. +* +* rmsdis double[naxis] +* For each individual distortion function, the +* root mean square deviation of the distortion. +* +* Can be specified as a NULL pointer if not required. +* +* rmstot double* For the combination of all distortion functions, the +* root mean square deviation of the distortion. +* +* Can be specified as a NULL pointer if not required. +* +* Function return value: +* int Status return value: +* 0: Success. +* 1: Null linprm pointer passed. +* 2: Memory allocation failed. +* 3: Invalid parameter. +* 4: Distort error. +* * * linprm struct - Linear transformation parameters * ------------------------------------------------ @@ -259,18 +527,20 @@ * ("returned"). * * int flag -* (Given and returned) This flag must be set to zero whenever any of the -* following members of the linprm struct are set or modified: +* (Given and returned) This flag must be set to zero (or 1, see linset()) +* whenever any of the following linprm members are set or changed: * * - linprm::naxis (q.v., not normally set by the user), * - linprm::pc, -* - linprm::cdelt. +* - linprm::cdelt, +* - linprm::dispre. +* - linprm::disseq. * * This signals the initialization routine, linset(), to recompute the * returned members of the linprm struct. linset() will reset flag to * indicate that this has been done. * -* PLEASE NOTE: flag should be set to -1 when linini() is called for the +* PLEASE NOTE: flag should be set to -1 when lininit() is called for the * first time for a particular linprm struct in order to initialize memory * management. It must ONLY be used on the first initialization otherwise * memory leaks may result. @@ -278,7 +548,7 @@ * int naxis * (Given or returned) Number of pixel and world coordinate elements. * -* If linini() is used to initialize the linprm struct (as would normally +* If lininit() is used to initialize the linprm struct (as would normally * be the case) then it will set naxis from the value passed to it as a * function argument. The user should not subsequently modify it. * @@ -286,6 +556,9 @@ * (Given) Pointer to the first element of an array of double containing * the coordinate reference pixel, CRPIXja. * +* It is not necessary to reset the linprm struct (via linset()) when +* linprm::crpix is changed. +* * double *pc * (Given) Pointer to the first element of the PCi_ja (pixel coordinate) * transformation matrix. The expected order is @@ -317,11 +590,56 @@ * (Given) Pointer to the first element of an array of double containing * the coordinate increments, CDELTia. * -* int unity -* (Returned) True if the linear transformation matrix is unity. -* -* int padding -* (An unused variable inserted for alignment purposes only.) +* struct disprm *dispre +* (Given) Pointer to a disprm struct holding parameters for prior +* distortion functions, or a null (0x0) pointer if there are none. +* +* Function lindist() may be used to assign a disprm pointer to a linprm +* struct, allowing it to take control of any memory allocated for it, as +* in the following example: +* += void add_distortion(struct linprm *lin) += { += struct disprm *dispre; += += dispre = malloc(sizeof(struct disprm)); += dispre->flag = -1; += lindist(1, lin, dispre, ndpmax); += : += (Set up dispre.) += : += += return; += } +* +* Here, after the distortion function parameters etc. are copied into +* dispre, dispre is assigned using lindist() which takes control of the +* allocated memory. It will be freed later when linfree() is invoked on +* the linprm struct. +* +* Consider also the following erroneous code: +* += void bad_code(struct linprm *lin) += { += struct disprm dispre; += += dispre.flag = -1; += lindist(1, lin, &dispre, ndpmax); // WRONG. += : += += return; += } +* +* Here, dispre is declared as a struct, rather than a pointer. When the +* function returns, dispre will go out of scope and its memory will most +* likely be reused, thereby trashing its contents. Later, a segfault will +* occur when linfree() tries to free dispre's stale address. +* +* struct disprm *disseq +* (Given) Pointer to a disprm struct holding parameters for sequent +* distortion functions, or a null (0x0) pointer if there are none. +* +* Refer to the comments and examples given for disprm::dispre. * * double *piximg * (Returned) Pointer to the first element of the matrix containing the @@ -331,25 +649,38 @@ * (Returned) Pointer to the first element of the inverse of the * linprm::piximg matrix. * +* int i_naxis +* (Returned) The dimension of linprm::piximg and linprm::imgpix (normally +* equal to naxis). +* +* int unity +* (Returned) True if the linear transformation matrix is unity. +* +* int affine +* (Returned) True if there are no distortions. +* +* int simple +* (Returned) True if unity and no distortions. +* * struct wcserr *err -* (Returned) If enabled, when an error status is returned this struct +* (Returned) If enabled, when an error status is returned, this struct * contains detailed information about the error, see wcserr_enable(). * -* int i_naxis +* double *dummy * (For internal use only.) * int m_flag * (For internal use only.) * int m_naxis * (For internal use only.) -* int m_padding -* (For internal use only.) * double *m_crpix * (For internal use only.) * double *m_pc * (For internal use only.) * double *m_cdelt * (For internal use only.) -* void *padding2 +* struct disprm *m_dispre +* (For internal use only.) +* struct disprm *m_disseq * (For internal use only.) * * @@ -362,65 +693,90 @@ #ifndef WCSLIB_LIN #define WCSLIB_LIN -#include "wcserr.h" - #ifdef __cplusplus extern "C" { #endif +enum linenq_enum { + LINENQ_MEM = 1, // linprm struct memory is managed by WCSLIB. + LINENQ_SET = 2, // linprm struct has been set up. + LINENQ_BYP = 4, // linprm struct is in bypass mode. +}; extern const char *lin_errmsg[]; enum lin_errmsg_enum { - LINERR_SUCCESS = 0, /* Success. */ - LINERR_NULL_POINTER = 1, /* Null linprm pointer passed. */ - LINERR_MEMORY = 2, /* Memory allocation failed. */ - LINERR_SINGULAR_MTX = 3 /* PCi_ja matrix is singular. */ + LINERR_SUCCESS = 0, // Success. + LINERR_NULL_POINTER = 1, // Null linprm pointer passed. + LINERR_MEMORY = 2, // Memory allocation failed. + LINERR_SINGULAR_MTX = 3, // PCi_ja matrix is singular. + LINERR_DISTORT_INIT = 4, // Failed to initialise distortions. + LINERR_DISTORT = 5, // Distort error. + LINERR_DEDISTORT = 6 // De-distort error. }; struct linprm { - /* Initialization flag (see the prologue above). */ - /*------------------------------------------------------------------------*/ - int flag; /* Set to zero to force initialization. */ - - /* Parameters to be provided (see the prologue above). */ - /*------------------------------------------------------------------------*/ - int naxis; /* The number of axes, given by NAXIS. */ - double *crpix; /* CRPIXja keywords for each pixel axis. */ - double *pc; /* PCi_ja linear transformation matrix. */ - double *cdelt; /* CDELTia keywords for each coord axis. */ - - /* Information derived from the parameters supplied. */ - /*------------------------------------------------------------------------*/ - double *piximg; /* Product of CDELTia and PCi_ja matrices. */ - double *imgpix; /* Inverse of the piximg matrix. */ - int unity; /* True if the PCi_ja matrix is unity. */ - - /* Error handling */ - /*------------------------------------------------------------------------*/ - int padding; /* (Dummy inserted for alignment purposes.) */ + // Initialization flag (see the prologue above). + //-------------------------------------------------------------------------- + int flag; // Set to zero to force initialization. + + // Parameters to be provided (see the prologue above). + //-------------------------------------------------------------------------- + int naxis; // The number of axes, given by NAXIS. + double *crpix; // CRPIXja keywords for each pixel axis. + double *pc; // PCi_ja linear transformation matrix. + double *cdelt; // CDELTia keywords for each coord axis. + struct disprm *dispre; // Prior distortion parameters, if any. + struct disprm *disseq; // Sequent distortion parameters, if any. + + // Information derived from the parameters supplied. + //-------------------------------------------------------------------------- + double *piximg; // Product of CDELTia and PCi_ja matrices. + double *imgpix; // Inverse of the piximg matrix. + int i_naxis; // Dimension of piximg and imgpix. + int unity; // True if the PCi_ja matrix is unity. + int affine; // True if there are no distortions. + int simple; // True if unity and no distortions. + + // Error messaging, if enabled. + //-------------------------------------------------------------------------- struct wcserr *err; - /* Private - the remainder are for memory management. */ - /*------------------------------------------------------------------------*/ - int i_naxis; - int m_flag, m_naxis, m_padding; + //-------------------------------------------------------------------------- + // Private - the remainder are for internal use. + //-------------------------------------------------------------------------- + double *dummy; + + // The remainder are for memory management. + int m_flag, m_naxis; double *m_crpix, *m_pc, *m_cdelt; - void *padding2; + struct disprm *m_dispre, *m_disseq; }; -/* Size of the linprm struct in int units, used by the Fortran wrappers. */ +// Size of the linprm struct in int units, used by the Fortran wrappers. #define LINLEN (sizeof(struct linprm)/sizeof(int)) int linini(int alloc, int naxis, struct linprm *lin); +int lininit(int alloc, int naxis, struct linprm *lin, int ndpmax); + +int lindis(int sequence, struct linprm *lin, struct disprm *dis); + +int lindist(int sequence, struct linprm *lin, struct disprm *dis, int ndpmax); + int lincpy(int alloc, const struct linprm *linsrc, struct linprm *lindst); int linfree(struct linprm *lin); +int linsize(const struct linprm *lin, int sizes[2]); + +int linenq(const struct linprm *lin, int enquiry); + int linprt(const struct linprm *lin); +int linperr(const struct linprm *lin, const char *prefix); + int linset(struct linprm *lin); int linp2x(struct linprm *lin, int ncoord, int nelem, const double pixcrd[], @@ -429,10 +785,16 @@ int linp2x(struct linprm *lin, int ncoord, int nelem, const double pixcrd[], int linx2p(struct linprm *lin, int ncoord, int nelem, const double imgcrd[], double pixcrd[]); +int linwarp(struct linprm *lin, const double pixblc[], const double pixtrc[], + const double pixsamp[], int *nsamp, + double maxdis[], double *maxtot, + double avgdis[], double *avgtot, + double rmsdis[], double *rmstot); + int matinv(int n, const double mat[], double inv[]); -/* Deprecated. */ +// Deprecated. #define linini_errmsg lin_errmsg #define lincpy_errmsg lin_errmsg #define linfree_errmsg lin_errmsg @@ -445,4 +807,4 @@ int matinv(int n, const double mat[], double inv[]); } #endif -#endif /* WCSLIB_LIN */ +#endif // WCSLIB_LIN diff --git a/cextern/wcslib/C/log.c b/cextern/wcslib/C/log.c index abd6b1a3a431..92f725d62924 100644 --- a/cextern/wcslib/C/log.c +++ b/cextern/wcslib/C/log.c @@ -1,7 +1,6 @@ /*============================================================================ - - WCSLIB 4.14 - an implementation of the FITS WCS standard. - Copyright (C) 1995-2012, Mark Calabretta + WCSLIB 8.6 - an implementation of the FITS WCS standard. + Copyright (C) 1995-2026, Mark Calabretta This file is part of WCSLIB. @@ -18,18 +17,16 @@ You should have received a copy of the GNU Lesser General Public License along with WCSLIB. If not, see http://www.gnu.org/licenses. - Direct correspondence concerning WCSLIB to mark@calabretta.id.au - Author: Mark Calabretta, Australia Telescope National Facility, CSIRO. - http://www.atnf.csiro.au/people/Mark.Calabretta - $Id: log.c,v 4.14 2012/07/13 10:02:27 cal103 Exp $ + http://www.atnf.csiro.au/computing/software/wcs + $Id: log.c,v 8.6 2026/03/29 13:53:56 mcalabre Exp $ *===========================================================================*/ #include #include "log.h" -/* Map status return value to message. */ +// Map status return value to message. const char *log_errmsg[] = { "Success", "", @@ -38,7 +35,7 @@ const char *log_errmsg[] = { "One or more of the world coordinates were invalid"}; -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- int logx2s( double crval, @@ -50,28 +47,22 @@ int logx2s( int stat[]) { - register int ix; - register int *statp; - register const double *xp; - register double *logcp; - - if (crval <= 0.0) { return LOGERR_BAD_LOG_REF_VAL; } - xp = x; - logcp = logc; - statp = stat; - for (ix = 0; ix < nx; ix++, xp += sx, logcp += slogc) { + const double *xp = x; + double *logcp = logc; + int *statp = stat; + for (int ix = 0; ix < nx; ix++, xp += sx, logcp += slogc, statp++) { *logcp = crval * exp((*xp) / crval); - *(statp++) = 0; + *statp = 0; } return 0; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- int logs2x( double crval, @@ -83,28 +74,22 @@ int logs2x( int stat[]) { - int status; - register int ilogc; - register int *statp; - register const double *logcp; - register double *xp; - - if (crval <= 0.0) { return LOGERR_BAD_LOG_REF_VAL; } - xp = x; - logcp = logc; - statp = stat; - status = 0; - for (ilogc = 0; ilogc < nlogc; ilogc++, logcp += slogc, xp += sx) { + double *xp = x; + const double *logcp = logc; + int *statp = stat; + int status = 0; + for (int ilogc = 0; ilogc < nlogc; ilogc++, logcp += slogc, xp += sx, + statp++) { if (*logcp > 0.0) { *xp = crval * log(*logcp / crval); - *(statp++) = 0; + *statp = 0; } else { - *(statp++) = 1; status = LOGERR_BAD_WORLD; + *statp = 1; } } diff --git a/cextern/wcslib/C/log.h b/cextern/wcslib/C/log.h index 19ba2d1f8bfc..6b3b642743cf 100644 --- a/cextern/wcslib/C/log.h +++ b/cextern/wcslib/C/log.h @@ -1,7 +1,6 @@ /*============================================================================ - - WCSLIB 4.14 - an implementation of the FITS WCS standard. - Copyright (C) 1995-2012, Mark Calabretta + WCSLIB 8.6 - an implementation of the FITS WCS standard. + Copyright (C) 1995-2026, Mark Calabretta This file is part of WCSLIB. @@ -18,33 +17,32 @@ You should have received a copy of the GNU Lesser General Public License along with WCSLIB. If not, see http://www.gnu.org/licenses. - Direct correspondence concerning WCSLIB to mark@calabretta.id.au - Author: Mark Calabretta, Australia Telescope National Facility, CSIRO. - http://www.atnf.csiro.au/people/Mark.Calabretta - $Id: log.h,v 4.14 2012/07/13 10:02:27 cal103 Exp $ + http://www.atnf.csiro.au/computing/software/wcs + $Id: log.h,v 8.6 2026/03/29 13:53:56 mcalabre Exp $ *============================================================================= * -* WCSLIB 4.14 - C routines that implement logarithmic coordinate systems as -* defined by the FITS World Coordinate System (WCS) standard. Refer to +* WCSLIB 8.6 - C routines that implement the FITS World Coordinate System +* (WCS) standard. Refer to the README file provided with WCSLIB for an +* overview of the library. +* +* +* Summary of the log routines +* --------------------------- +* Routines in this suite implement the part of the FITS World Coordinate +* System (WCS) standard that deals with logarithmic coordinates, as described +* in * * "Representations of world coordinates in FITS", -* Greisen, E.W., & Calabretta, M.R. 2002, A&A, 395, 1061 (Paper I) +* Greisen, E.W., & Calabretta, M.R. 2002, A&A, 395, 1061 (WCS Paper I) * * "Representations of spectral coordinates in FITS", * Greisen, E.W., Calabretta, M.R., Valdes, F.G., & Allen, S.L. -* 2006, A&A, 446, 747 (Paper III) -* -* Refer to the README file provided with WCSLIB for an overview of the -* library. -* +* 2006, A&A, 446, 747 (WCS Paper III) * -* Summary of the log routines -* --------------------------- -* These routines implement the part of the FITS WCS standard that deals with -* logarithmic coordinates. They define methods to be used for computing -* logarithmic world coordinates from intermediate world coordinates (a linear -* transformation of image pixel coordinates), and vice versa. +* These routines define methods to be used for computing logarithmic world +* coordinates from intermediate world coordinates (a linear transformation of +* image pixel coordinates), and vice versa. * * logx2s() and logs2x() implement the WCS logarithmic coordinate * transformations. @@ -143,13 +141,13 @@ extern "C" { extern const char *log_errmsg[]; enum log_errmsg_enum { - LOGERR_SUCCESS = 0, /* Success. */ - LOGERR_NULL_POINTER = 1, /* Null pointer passed. */ - LOGERR_BAD_LOG_REF_VAL = 2, /* Invalid log-coordinate reference value. */ - LOGERR_BAD_X = 3, /* One or more of the x coordinates were - invalid. */ - LOGERR_BAD_WORLD = 4 /* One or more of the world coordinates were - invalid. */ + LOGERR_SUCCESS = 0, // Success. + LOGERR_NULL_POINTER = 1, // Null pointer passed. + LOGERR_BAD_LOG_REF_VAL = 2, // Invalid log-coordinate reference value. + LOGERR_BAD_X = 3, // One or more of the x coordinates were + // invalid. + LOGERR_BAD_WORLD = 4 // One or more of the world coordinates were + // invalid. }; int logx2s(double crval, int nx, int sx, int slogc, const double x[], @@ -163,4 +161,4 @@ int logs2x(double crval, int nlogc, int slogc, int sx, const double logc[], } #endif -#endif /* WCSLIB_LOG */ +#endif // WCSLIB_LOG diff --git a/cextern/wcslib/C/prj.c b/cextern/wcslib/C/prj.c index 47372b812b8d..6ad8a824515f 100644 --- a/cextern/wcslib/C/prj.c +++ b/cextern/wcslib/C/prj.c @@ -1,7 +1,6 @@ /*============================================================================ - - WCSLIB 4.14 - an implementation of the FITS WCS standard. - Copyright (C) 1995-2012, Mark Calabretta + WCSLIB 8.6 - an implementation of the FITS WCS standard. + Copyright (C) 1995-2026, Mark Calabretta This file is part of WCSLIB. @@ -18,11 +17,9 @@ You should have received a copy of the GNU Lesser General Public License along with WCSLIB. If not, see http://www.gnu.org/licenses. - Direct correspondence concerning WCSLIB to mark@calabretta.id.au - Author: Mark Calabretta, Australia Telescope National Facility, CSIRO. - http://www.atnf.csiro.au/people/Mark.Calabretta - $Id: prj.c,v 4.14 2012/07/13 10:02:27 cal103 Exp $ + http://www.atnf.csiro.au/computing/software/wcs + $Id: prj.c,v 8.6 2026/03/29 13:53:56 mcalabre Exp $ *===========================================================================*/ #include @@ -38,7 +35,7 @@ #include "prj.h" -/* Projection categories. */ +// Projection categories. const int ZENITHAL = 1; const int CYLINDRICAL = 2; const int PSEUDOCYLINDRICAL = 3; @@ -53,43 +50,14 @@ const char prj_categories[9][32] = "conventional", "conic", "polyconic", "quadcube", "HEALPix"}; -/* Projection codes. */ -const int prj_ncode = 27; -const char prj_codes[27][4] = +// Projection codes. +const int prj_ncode = 28; +const char prj_codes[28][4] = {"AZP", "SZP", "TAN", "STG", "SIN", "ARC", "ZPN", "ZEA", "AIR", "CYP", "CEA", "CAR", "MER", "COP", "COE", "COD", "COO", "SFL", "PAR", "MOL", - "AIT", "BON", "PCO", "TSC", "CSC", "QSC", "HPX"}; - -const int AZP = 101; -const int SZP = 102; -const int TAN = 103; -const int STG = 104; -const int SIN = 105; -const int ARC = 106; -const int ZPN = 107; -const int ZEA = 108; -const int AIR = 109; -const int CYP = 201; -const int CEA = 202; -const int CAR = 203; -const int MER = 204; -const int SFL = 301; -const int PAR = 302; -const int MOL = 303; -const int AIT = 401; -const int COP = 501; -const int COE = 502; -const int COD = 503; -const int COO = 504; -const int BON = 601; -const int PCO = 602; -const int TSC = 701; -const int CSC = 702; -const int QSC = 703; -const int HPX = 801; - - -/* Map status return value to message. */ + "AIT", "BON", "PCO", "TSC", "CSC", "QSC", "HPX", "XPH"}; + +// Map status return value to message. const char *prj_errmsg[] = { "Success", "Null prjprm pointer passed", @@ -97,7 +65,36 @@ const char *prj_errmsg[] = { "One or more of the (x,y) coordinates were invalid", "One or more of the (phi,theta) coordinates were invalid"}; -/* Convenience macros for generating common error messages. */ +static const int AZP = 101; +static const int SZP = 102; +static const int TAN = 103; +static const int STG = 104; +static const int SIN = 105; +static const int ARC = 106; +static const int ZPN = 107; +static const int ZEA = 108; +static const int AIR = 109; +static const int CYP = 201; +static const int CEA = 202; +static const int CAR = 203; +static const int MER = 204; +static const int SFL = 301; +static const int PAR = 302; +static const int MOL = 303; +static const int AIT = 401; +static const int COP = 501; +static const int COE = 502; +static const int COD = 503; +static const int COO = 504; +static const int BON = 601; +static const int PCO = 602; +static const int TSC = 701; +static const int CSC = 702; +static const int QSC = 703; +static const int HPX = 801; +static const int XPH = 802; + +// Convenience macros for generating common error messages. #define PRJERR_BAD_PARAM_SET(function) \ wcserr_set(&(prj->err), PRJERR_BAD_PARAM, function, __FILE__, __LINE__, \ "Invalid parameters for %s projection", prj->name); @@ -116,12 +113,20 @@ const char *prj_errmsg[] = { /*============================================================================ -* Generic routines. +* Generic routines: * * prjini initializes a prjprm struct to default values. * +* prjfree frees any memory that may have been allocated to store an error +* message in the prjprm struct. +* +* prjsize computes the size of a prjprm struct. +* * prjprt prints the contents of a prjprm struct. * +* prjbchk performs bounds checking on the native coordinates returned by the +* *x2s() routines. +* * prjset invokes the specific initialization routine based on the projection * code in the prjprm struct. * @@ -133,30 +138,24 @@ const char *prj_errmsg[] = { * *---------------------------------------------------------------------------*/ -int prjini(prj) - -struct prjprm *prj; +int prjini(struct prjprm *prj) { - register int k; - if (prj == 0x0) return PRJERR_NULL_POINTER; - prj->flag = 0; - strcpy(prj->code, " "); prj->pv[0] = 0.0; prj->pv[1] = UNDEFINED; prj->pv[2] = UNDEFINED; prj->pv[3] = UNDEFINED; - for (k = 4; k < PVN; prj->pv[k++] = 0.0); + for (int k = 4; k < PVN; prj->pv[k++] = 0.0); prj->r0 = 0.0; prj->phi0 = UNDEFINED; prj->theta0 = UNDEFINED; - prj->bounds = 1; + prj->bounds = 7; strcpy(prj->name, "undefined"); - for (k = 9; k < 40; prj->name[k++] = '\0'); + for (int k = 9; k < 40; prj->name[k++] = '\0'); prj->category = 0; prj->pvrange = 0; prj->simplezen = 0; @@ -170,59 +169,100 @@ struct prjprm *prj; prj->err = 0x0; prj->padding = 0x0; - for (k = 0; k < 10; prj->w[k++] = 0.0); + for (int k = 0; k < 10; prj->w[k++] = 0.0); prj->m = 0; prj->n = 0; prj->prjx2s = 0x0; prj->prjs2x = 0x0; + prj->flag = 0; + return 0; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- -int prjfree(prj) - -struct prjprm *prj; +int prjfree(struct prjprm *prj) { if (prj == 0x0) return PRJERR_NULL_POINTER; - if (prj->err) { - free(prj->err); - prj->err = 0x0; - } + wcserr_clear(&(prj->err)); return 0; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- + +int prjsize(const struct prjprm *prj, int sizes[2]) -int prjprt(prj) +{ + if (prj == 0x0) { + sizes[0] = sizes[1] = 0; + return 0; + } + + // Base size, in bytes. + sizes[0] = sizeof(struct prjprm); + + // Total size of allocated memory, in bytes. + sizes[1] = 0; + + // prjprm::err. + int exsizes[2]; + wcserr_size(prj->err, exsizes); + sizes[1] += exsizes[0] + exsizes[1]; + + return 0; +} -const struct prjprm *prj; +//---------------------------------------------------------------------------- + +int prjenq(const struct prjprm *prj, int enquiry) { - char hext[32]; - int i, n; + // Initialize. + if (prj == 0x0) return PRJERR_NULL_POINTER; + + int answer = 0; + int absflag = abs(prj->flag); + + if (enquiry & PRJENQ_SET) { + if (absflag < 100 || 1000 < absflag) return 0; + answer = 1; + } + + if (enquiry & PRJENQ_BYP) { + if (prj->flag != 1 && !(-1000 < prj->flag && prj->flag < -100)) return 0; + answer = 1; + } + return answer; +} + +//---------------------------------------------------------------------------- + +int prjprt(const struct prjprm *prj) + +{ if (prj == 0x0) return PRJERR_NULL_POINTER; + // Parameters supplied. wcsprintf(" flag: %d\n", prj->flag); - wcsprintf(" code: \"%s\"\n", prj->code); + wcsprintf(" code: \"%s\"\n", prj->code); wcsprintf(" r0: %9f\n", prj->r0); wcsprintf(" pv:"); if (prj->pvrange) { - n = (prj->pvrange)%100; + int n = (prj->pvrange)%100; if (prj->pvrange/100) { wcsprintf(" (0)"); } else { - wcsprintf(" %- 11.5g", prj->pv[0]); + wcsprintf(" %#- 11.5g", prj->pv[0]); n--; } - for (i = 1; i <= n; i++) { + for (int i = 1; i <= n; i++) { if (i%5 == 1) { wcsprintf("\n "); } @@ -230,7 +270,7 @@ const struct prjprm *prj; if (undefined(prj->pv[i])) { wcsprintf(" UNDEFINED "); } else { - wcsprintf(" %- 11.5g", prj->pv[i]); + wcsprintf(" %#- 11.5g", prj->pv[i]); } } wcsprintf("\n"); @@ -249,6 +289,7 @@ const struct prjprm *prj; } wcsprintf(" bounds: %d\n", prj->bounds); + // Derived values. wcsprintf("\n"); wcsprintf(" name: \"%s\"\n", prj->name); wcsprintf(" category: %d (%s)\n", prj->category, @@ -262,47 +303,123 @@ const struct prjprm *prj; wcsprintf(" x0: %f\n", prj->x0); wcsprintf(" y0: %f\n", prj->y0); + // Error handling. WCSPRINTF_PTR(" err: ", prj->err, "\n"); if (prj->err) { wcserr_prt(prj->err, " "); } + // Intermediate values set by prjset(). wcsprintf(" w[]:"); - for (i = 0; i < 5; i++) { - wcsprintf(" %- 11.5g", prj->w[i]); + for (int i = 0; i < 5; i++) { + wcsprintf(" %#- 11.5g", prj->w[i]); } wcsprintf("\n "); - for (i = 5; i < 10; i++) { - wcsprintf(" %- 11.5g", prj->w[i]); + for (int i = 5; i < 10; i++) { + wcsprintf(" %#- 11.5g", prj->w[i]); } wcsprintf("\n"); wcsprintf(" m: %d\n", prj->m); wcsprintf(" n: %d\n", prj->n); + + // Pointers to projection and deprojection functions. + char hext[32]; wcsprintf(" prjx2s: %s\n", - wcsutil_fptr2str((int (*)(void))prj->prjx2s, hext)); + wcsutil_fptr2str((void (*)(void))prj->prjx2s, hext)); wcsprintf(" prjs2x: %s\n", - wcsutil_fptr2str((int (*)(void))prj->prjs2x, hext)); + wcsutil_fptr2str((void (*)(void))prj->prjs2x, hext)); return 0; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- + +int prjperr(const struct prjprm *prj, const char *prefix) + +{ + if (prj == 0x0) return PRJERR_NULL_POINTER; + + if (prj->err) { + wcserr_prt(prj->err, prefix); + } + + return 0; +} -int prjset(prj) +//---------------------------------------------------------------------------- -struct prjprm *prj; +int prjbchk( + double tol, + int nphi, + int ntheta, + int spt, + double phi[], + double theta[], + int stat[]) { - static const char *function = "prjset"; + double *phip = phi; + double *thetap = theta; + int *statp = stat; + int status = 0; + for (int itheta = 0; itheta < ntheta; itheta++) { + for (int iphi = 0; iphi < nphi; iphi++, phip += spt, thetap += spt, + statp++) { + // Skip values already marked as illegal. + if (*statp == 0) { + if (*phip < -180.0) { + if (*phip < -180.0-tol) { + *statp = 1; + status = 1; + } else { + *phip = -180.0; + } + } else if (180.0 < *phip) { + if (180.0+tol < *phip) { + *statp = 1; + status = 1; + } else { + *phip = 180.0; + } + } - int status; - struct wcserr **err; + if (*thetap < -90.0) { + if (*thetap < -90.0-tol) { + *statp = 1; + status = 1; + } else { + *thetap = -90.0; + } + } else if (90.0 < *thetap) { + if (90.0+tol < *thetap) { + *statp = 1; + status = 1; + } else { + *thetap = 90.0; + } + } + } + } + } + + return status; +} + +//---------------------------------------------------------------------------- + +int prjset(struct prjprm *prj) + +{ + static const char *function = "prjset"; if (prj == 0x0) return PRJERR_NULL_POINTER; - err = &(prj->err); + if (prj->flag < 0) return 0; + struct wcserr **err = &(prj->err); - /* Invoke the relevant initialization routine. */ + // Invoke the relevant initialization routine. prj->code[3] = '\0'; + + int status; if (strcmp(prj->code, "AZP") == 0) { status = azpset(prj); } else if (strcmp(prj->code, "SZP") == 0) { @@ -357,8 +474,10 @@ struct prjprm *prj; status = qscset(prj); } else if (strcmp(prj->code, "HPX") == 0) { status = hpxset(prj); + } else if (strcmp(prj->code, "XPH") == 0) { + status = xphset(prj); } else { - /* Unrecognized projection code. */ + // Unrecognized projection code. status = wcserr_set(WCSERR_SET(PRJERR_BAD_PARAM), "Unrecognized projection code '%s'", prj->code); } @@ -366,44 +485,52 @@ struct prjprm *prj; return status; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- -int prjx2s(prj, nx, ny, sxy, spt, x, y, phi, theta, stat) - -struct prjprm *prj; -int nx, ny, sxy, spt; -const double x[], y[]; -double phi[], theta[]; -int stat[]; +int prjx2s( + struct prjprm *prj, + int nx, + int ny, + int sxy, + int spt, + const double x[], + const double y[], + double phi[], + double theta[], + int stat[]) { - int status; - - /* Initialize. */ + // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; - if (prj->flag == 0) { + + int status; + if (abs(prj->flag) < 100) { if ((status = prjset(prj))) return status; } return prj->prjx2s(prj, nx, ny, sxy, spt, x, y, phi, theta, stat); } -/*--------------------------------------------------------------------------*/ - -int prjs2x(prj, nphi, ntheta, spt, sxy, phi, theta, x, y, stat) +//---------------------------------------------------------------------------- -struct prjprm *prj; -int nphi, ntheta, spt, sxy; -const double phi[], theta[]; -double x[], y[]; -int stat[]; +int prjs2x( + struct prjprm *prj, + int nphi, + int ntheta, + int spt, + int sxy, + const double phi[], + const double theta[], + double x[], + double y[], + int stat[]) { - int status; - - /* Initialize. */ + // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; - if (prj->flag == 0) { + + int status; + if (abs(prj->flag) < 100) { if ((status = prjset(prj))) return status; } @@ -411,30 +538,29 @@ int stat[]; } /*============================================================================ -* Internal helper routine used by the *set() routines that forces -* (x,y) = (0,0) at (phi0,theta0). +* Internal helper routine used by the *set() routines - not intended for +* outside use. It forces (x,y) = (0,0) at (phi0,theta0). *---------------------------------------------------------------------------*/ -int prjoff(prj, phi0, theta0) - -struct prjprm *prj; -const double phi0, theta0; +static int prjoff( + struct prjprm *prj, + const double phi0, + const double theta0) { - int stat; - double x0, y0; - if (prj == 0x0) return PRJERR_NULL_POINTER; prj->x0 = 0.0; prj->y0 = 0.0; if (undefined(prj->phi0) || undefined(prj->theta0)) { - /* Set both to the projection-specific default if either undefined. */ + // Set both to the projection-specific default if either undefined. prj->phi0 = phi0; prj->theta0 = theta0; } else { + double x0, y0; + int stat; if (prj->prjs2x(prj, 1, 1, 1, 1, &(prj->phi0), &(prj->theta0), &x0, &y0, &stat)) { return PRJERR_BAD_PARAM_SET("prjoff"); @@ -476,14 +602,12 @@ const double phi0, theta0; * prj->prjs2x Pointer to azps2x(). *===========================================================================*/ -int azpset(prj) - -struct prjprm *prj; +int azpset(struct prjprm *prj) { if (prj == 0x0) return PRJERR_NULL_POINTER; + if (prj->flag == -AZP) return 0; - prj->flag = AZP; strcpy(prj->code, "AZP"); if (undefined(prj->pv[1])) prj->pv[1] = 0.0; @@ -525,34 +649,37 @@ struct prjprm *prj; prj->prjx2s = azpx2s; prj->prjs2x = azps2x; + prj->flag = (prj->flag == 1) ? -AZP : AZP; + return prjoff(prj, 0.0, 90.0); } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- -int azpx2s(prj, nx, ny, sxy, spt, x, y, phi, theta, stat) - -struct prjprm *prj; -int nx, ny, sxy, spt; -const double x[], y[]; -double phi[], theta[]; -int stat[]; +int azpx2s( + struct prjprm *prj, + int nx, + int ny, + int sxy, + int spt, + const double x[], + const double y[], + double phi[], + double theta[], + int stat[]) { - int mx, my, rowlen, rowoff, status; - double a, b, q, r, s, t, xj, yj, yc, yc2; const double tol = 1.0e-13; - register int ix, iy, *statp; - register const double *xp, *yp; - register double *phip, *thetap; - - /* Initialize. */ + // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; - if (prj->flag != AZP) { + + int status; + if (abs(prj->flag) != AZP) { if ((status = azpset(prj))) return status; } + int mx, my; if (ny > 0) { mx = nx; my = ny; @@ -565,55 +692,57 @@ int stat[]; status = 0; - /* Do x dependence. */ - xp = x; - rowoff = 0; - rowlen = nx*spt; - for (ix = 0; ix < nx; ix++, rowoff += spt, xp += sxy) { - xj = *xp + prj->x0; + // Do x dependence. + const double *xp = x; + double *phip, *thetap; + int rowoff = 0; + int rowlen = nx*spt; + for (int ix = 0; ix < nx; ix++, rowoff += spt, xp += sxy) { + double xj = *xp + prj->x0; phip = phi + rowoff; - for (iy = 0; iy < my; iy++) { + for (int iy = 0; iy < my; iy++) { *phip = xj; phip += rowlen; } } - /* Do y dependence. */ - yp = y; + // Do y dependence. + const double *yp = y; + int *statp = stat; + phip = phi; thetap = theta; - statp = stat; - for (iy = 0; iy < ny; iy++, yp += sxy) { - yj = *yp + prj->y0; + for (int iy = 0; iy < ny; iy++, yp += sxy) { + double yj = *yp + prj->y0; - yc = yj*prj->w[3]; - yc2 = yc*yc; + double yc = yj*prj->w[3]; + double yc2 = yc*yc; - q = prj->w[0] + yj*prj->w[4]; + double q = prj->w[0] + yj*prj->w[4]; - for (ix = 0; ix < mx; ix++, phip += spt, thetap += spt) { - xj = *phip; + for (int ix = 0; ix < mx; ix++, phip += spt, thetap += spt, statp++) { + double xj = *phip; - r = sqrt(xj*xj + yc2); + double r = sqrt(xj*xj + yc2); if (r == 0.0) { *phip = 0.0; *thetap = 90.0; - *(statp++) = 0; + *statp = 0; } else { *phip = atan2d(xj, -yc); - s = r / q; - t = s*prj->pv[1]/sqrt(s*s + 1.0); + double s = r / q; + double t = s*prj->pv[1]/sqrt(s*s + 1.0); s = atan2d(1.0, s); if (fabs(t) > 1.0) { if (fabs(t) > 1.0+tol) { *thetap = 0.0; - *(statp++) = 1; + *statp = 1; if (!status) status = PRJERR_BAD_PIX_SET("azpx2s"); continue; } @@ -622,45 +751,51 @@ int stat[]; t = asind(t); } - a = s - t; - b = s + t + 180.0; + double a = s - t; + double b = s + t + 180.0; if (a > 90.0) a -= 360.0; if (b > 90.0) b -= 360.0; *thetap = (a > b) ? a : b; - *(statp++) = 0; + *statp = 0; } } } + + // Do bounds checking on the native coordinates. + if (prj->bounds&4 && prjbchk(1.0e-13, nx, my, spt, phi, theta, stat)) { + if (!status) status = PRJERR_BAD_PIX_SET("azpx2s"); + } + return status; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- -int azps2x(prj, nphi, ntheta, spt, sxy, phi, theta, x, y, stat) - -struct prjprm *prj; -int nphi, ntheta, spt, sxy; -const double phi[], theta[]; -double x[], y[]; -int stat[]; +int azps2x( + struct prjprm *prj, + int nphi, + int ntheta, + int spt, + int sxy, + const double phi[], + const double theta[], + double x[], + double y[], + int stat[]) { - int mphi, mtheta, rowlen, rowoff, status; - double a, b, cosphi, costhe, r, s, sinphi, sinthe, t; - register int iphi, itheta, istat, *statp; - register const double *phip, *thetap; - register double *xp, *yp; - - - /* Initialize. */ + // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; - if (prj->flag != AZP) { + + int status; + if (abs(prj->flag) != AZP) { if ((status = azpset(prj))) return status; } + int mphi, mtheta; if (ntheta > 0) { mphi = nphi; mtheta = ntheta; @@ -673,16 +808,17 @@ int stat[]; status = 0; - /* Do phi dependence. */ - phip = phi; - rowoff = 0; - rowlen = nphi*sxy; - for (iphi = 0; iphi < nphi; iphi++, rowoff += sxy, phip += spt) { + // Do phi dependence. + const double *phip = phi; + int rowoff = 0; + int rowlen = nphi*sxy; + for (int iphi = 0; iphi < nphi; iphi++, rowoff += sxy, phip += spt) { + double sinphi, cosphi; sincosd(*phip, &sinphi, &cosphi); - xp = x + rowoff; - yp = y + rowoff; - for (itheta = 0; itheta < mtheta; itheta++) { + double *xp = x + rowoff; + double *yp = y + rowoff; + for (int itheta = 0; itheta < mtheta; itheta++) { *xp = sinphi; *yp = cosphi; xp += rowlen; @@ -691,44 +827,46 @@ int stat[]; } - /* Do theta dependence. */ - thetap = theta; - xp = x; - yp = y; - statp = stat; - for (itheta = 0; itheta < ntheta; itheta++, thetap += spt) { + // Do theta dependence. + const double *thetap = theta; + double *xp = x; + double *yp = y; + int *statp = stat; + for (int itheta = 0; itheta < ntheta; itheta++, thetap += spt) { + double sinthe, costhe; sincosd(*thetap, &sinthe, &costhe); - for (iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy) { - s = prj->w[1]*(*yp); - t = (prj->pv[1] + sinthe) + costhe*s; + for (int iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy, statp++) { + double s = prj->w[1]*(*yp); + double t = (prj->pv[1] + sinthe) + costhe*s; if (t == 0.0) { *xp = 0.0; *yp = 0.0; - *(statp++) = 1; + *statp = 1; if (!status) status = PRJERR_BAD_WORLD_SET("azps2x"); } else { - r = prj->w[0]*costhe/t; + double r = prj->w[0]*costhe/t; - /* Bounds checking. */ - istat = 0; - if (prj->bounds) { + // Bounds checking. + int istat = 0; + if (prj->bounds&1) { if (*thetap < prj->w[5]) { - /* Overlap. */ + // Overlap. istat = 1; if (!status) status = PRJERR_BAD_WORLD_SET("azps2x"); } else if (prj->w[7] > 0.0) { - /* Divergence. */ + // Divergence. t = prj->pv[1] / sqrt(1.0 + s*s); if (fabs(t) <= 1.0) { s = atand(-s); t = asind(t); - a = s - t; - b = s + t + 180.0; + + double a = s - t; + double b = s + t + 180.0; if (a > 90.0) a -= 360.0; if (b > 90.0) b -= 360.0; @@ -743,7 +881,7 @@ int stat[]; *xp = r*(*xp) - prj->x0; *yp = -r*(*yp)*prj->w[2] - prj->y0; - *(statp++) = istat; + *statp = istat; } } } @@ -785,14 +923,12 @@ int stat[]; * prj->prjs2x Pointer to szps2x(). *===========================================================================*/ -int szpset(prj) - -struct prjprm *prj; +int szpset(struct prjprm *prj) { if (prj == 0x0) return PRJERR_NULL_POINTER; + if (prj->flag == -SZP) return 0; - prj->flag = SZP; strcpy(prj->code, "SZP"); if (undefined(prj->pv[1])) prj->pv[1] = 0.0; @@ -832,34 +968,37 @@ struct prjprm *prj; prj->prjx2s = szpx2s; prj->prjs2x = szps2x; + prj->flag = (prj->flag == 1) ? -SZP : SZP; + return prjoff(prj, 0.0, 90.0); } -/*--------------------------------------------------------------------------*/ - -int szpx2s(prj, nx, ny, sxy, spt, x, y, phi, theta, stat) +//---------------------------------------------------------------------------- -struct prjprm *prj; -int nx, ny, sxy, spt; -const double x[], y[]; -double phi[], theta[]; -int stat[]; +int szpx2s( + struct prjprm *prj, + int nx, + int ny, + int sxy, + int spt, + const double x[], + const double y[], + double phi[], + double theta[], + int stat[]) { - int mx, my, rowlen, rowoff, status; - double a, b, c, d, r2, sinth1, sinth2, sinthe, t, x1, xr, xy, y1, yr, z; const double tol = 1.0e-13; - register int ix, iy, *statp; - register const double *xp, *yp; - register double *phip, *thetap; - - /* Initialize. */ + // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; - if (prj->flag != SZP) { + + int status; + if (abs(prj->flag) != SZP) { if ((status = szpset(prj))) return status; } + int mx, my; if (ny > 0) { mx = nx; my = ny; @@ -872,63 +1011,64 @@ int stat[]; status = 0; - /* Do x dependence. */ - xp = x; - rowoff = 0; - rowlen = nx*spt; - for (ix = 0; ix < nx; ix++, rowoff += spt, xp += sxy) { - xr = (*xp + prj->x0)*prj->w[0]; + // Do x dependence. + const double *xp = x; + int rowoff = 0; + int rowlen = nx*spt; + for (int ix = 0; ix < nx; ix++, rowoff += spt, xp += sxy) { + double xr = (*xp + prj->x0)*prj->w[0]; - phip = phi + rowoff; - for (iy = 0; iy < my; iy++) { + double *phip = phi + rowoff; + for (int iy = 0; iy < my; iy++) { *phip = xr; phip += rowlen; } } - /* Do y dependence. */ - yp = y; - phip = phi; - thetap = theta; - statp = stat; - for (iy = 0; iy < ny; iy++, yp += sxy) { - yr = (*yp + prj->y0)*prj->w[0]; + // Do y dependence. + const double *yp = y; + double *phip = phi; + double *thetap = theta; + int *statp = stat; + for (int iy = 0; iy < ny; iy++, yp += sxy) { + double yr = (*yp + prj->y0)*prj->w[0]; - for (ix = 0; ix < mx; ix++, phip += spt, thetap += spt) { - xr = *phip; - r2 = xr*xr + yr*yr; + for (int ix = 0; ix < mx; ix++, phip += spt, thetap += spt, statp++) { + double xr = *phip; + double r2 = xr*xr + yr*yr; - x1 = (xr - prj->w[1])/prj->w[3]; - y1 = (yr - prj->w[2])/prj->w[3]; - xy = xr*x1 + yr*y1; + double x1 = (xr - prj->w[1])/prj->w[3]; + double y1 = (yr - prj->w[2])/prj->w[3]; + double xy = xr*x1 + yr*y1; + double z; if (r2 < 1.0e-10) { - /* Use small angle formula. */ + // Use small angle formula. z = r2/2.0; *thetap = 90.0 - R2D*sqrt(r2/(1.0 + xy)); } else { - t = x1*x1 + y1*y1; - a = t + 1.0; - b = xy - t; - c = r2 - xy - xy + t - 1.0; - d = b*b - a*c; + double t = x1*x1 + y1*y1; + double a = t + 1.0; + double b = xy - t; + double c = r2 - xy - xy + t - 1.0; + double d = b*b - a*c; - /* Check for a solution. */ + // Check for a solution. if (d < 0.0) { *phip = 0.0; *thetap = 0.0; - *(statp++) = 1; + *statp = 1; if (!status) status = PRJERR_BAD_PIX_SET("szpx2s"); continue; } d = sqrt(d); - /* Choose solution closest to pole. */ - sinth1 = (-b + d)/a; - sinth2 = (-b - d)/a; - sinthe = (sinth1 > sinth2) ? sinth1 : sinth2; + // Choose solution closest to pole. + double sinth1 = (-b + d)/a; + double sinth2 = (-b - d)/a; + double sinthe = (sinth1 > sinth2) ? sinth1 : sinth2; if (sinthe > 1.0) { if (sinthe-1.0 < tol) { sinthe = 1.0; @@ -946,7 +1086,7 @@ int stat[]; if (sinthe > 1.0 || sinthe < -1.0) { *phip = 0.0; *thetap = 0.0; - *(statp++) = 1; + *statp = 1; if (!status) status = PRJERR_BAD_PIX_SET("szpx2s"); continue; } @@ -957,37 +1097,43 @@ int stat[]; } *phip = atan2d(xr - x1*z, -(yr - y1*z)); - *(statp++) = 0; + *statp = 0; } } + + // Do bounds checking on the native coordinates. + if (prj->bounds&4 && prjbchk(1.0e-13, nx, my, spt, phi, theta, stat)) { + if (!status) status = PRJERR_BAD_PIX_SET("szpx2s"); + } + return status; } -/*--------------------------------------------------------------------------*/ - -int szps2x(prj, nphi, ntheta, spt, sxy, phi, theta, x, y, stat) +//---------------------------------------------------------------------------- -struct prjprm *prj; -int nphi, ntheta, spt, sxy; -const double phi[], theta[]; -double x[], y[]; -int stat[]; +int szps2x( + struct prjprm *prj, + int nphi, + int ntheta, + int spt, + int sxy, + const double phi[], + const double theta[], + double x[], + double y[], + int stat[]) { - int mphi, mtheta, rowlen, rowoff, status; - double a, b, cosphi, r, s, sinphi, t, u, v; - register int iphi, itheta, istat, *statp; - register const double *phip, *thetap; - register double *xp, *yp; - - - /* Initialize. */ + // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; - if (prj->flag != SZP) { + + int status; + if (abs(prj->flag) != SZP) { if ((status = szpset(prj))) return status; } + int mphi, mtheta; if (ntheta > 0) { mphi = nphi; mtheta = ntheta; @@ -1000,16 +1146,17 @@ int stat[]; status = 0; - /* Do phi dependence. */ - phip = phi; - rowoff = 0; - rowlen = nphi*sxy; - for (iphi = 0; iphi < nphi; iphi++, rowoff += sxy, phip += spt) { + // Do phi dependence. + const double *phip = phi; + int rowoff = 0; + int rowlen = nphi*sxy; + for (int iphi = 0; iphi < nphi; iphi++, rowoff += sxy, phip += spt) { + double sinphi, cosphi; sincosd(*phip, &sinphi, &cosphi); - xp = x + rowoff; - yp = y + rowoff; - for (itheta = 0; itheta < mtheta; itheta++) { + double *xp = x + rowoff; + double *yp = y + rowoff; + for (int itheta = 0; itheta < mtheta; itheta++) { *xp = sinphi; *yp = cosphi; xp += rowlen; @@ -1018,48 +1165,49 @@ int stat[]; } - /* Do theta dependence. */ - thetap = theta; - xp = x; - yp = y; - statp = stat; - for (itheta = 0; itheta < ntheta; itheta++, thetap += spt) { - s = 1.0 - sind(*thetap); - t = prj->w[3] - s; + // Do theta dependence. + const double *thetap = theta; + double *xp = x; + double *yp = y; + int *statp = stat; + for (int itheta = 0; itheta < ntheta; itheta++, thetap += spt) { + double s = 1.0 - sind(*thetap); + double t = prj->w[3] - s; if (t == 0.0) { - for (iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy) { + for (int iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy, statp++) { *xp = 0.0; *yp = 0.0; - *(statp++) = 1; + *statp = 1; } if (!status) status = PRJERR_BAD_WORLD_SET("szps2x"); } else { - r = prj->w[6]*cosd(*thetap)/t; - u = prj->w[4]*s/t + prj->x0; - v = prj->w[5]*s/t + prj->y0; - - for (iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy) { - /* Bounds checking. */ - istat = 0; - if (prj->bounds) { + double r = prj->w[6]*cosd(*thetap)/t; + double u = prj->w[4]*s/t + prj->x0; + double v = prj->w[5]*s/t + prj->y0; + + for (int iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy, statp++) { + // Bounds checking. + int istat = 0; + if (prj->bounds&1) { if (*thetap < prj->w[8]) { - /* Divergence. */ + // Divergence. istat = 1; if (!status) status = PRJERR_BAD_WORLD_SET("szps2x"); } else if (fabs(prj->pv[1]) > 1.0) { - /* Overlap. */ + // Overlap. s = prj->w[1]*(*xp) - prj->w[2]*(*yp); t = 1.0/sqrt(prj->w[7] + s*s); if (fabs(t) <= 1.0) { s = atan2d(s, prj->w[3] - 1.0); t = asind(t); - a = s - t; - b = s + t + 180.0; + + double a = s - t; + double b = s + t + 180.0; if (a > 90.0) a -= 360.0; if (b > 90.0) b -= 360.0; @@ -1074,7 +1222,7 @@ int stat[]; *xp = r*(*xp) - u; *yp = -r*(*yp) - v; - *(statp++) = istat; + *statp = istat; } } } @@ -1100,14 +1248,12 @@ int stat[]; * prj->prjs2x Pointer to tans2x(). *===========================================================================*/ -int tanset(prj) - -struct prjprm *prj; +int tanset(struct prjprm *prj) { if (prj == 0x0) return PRJERR_NULL_POINTER; + if (prj->flag == -TAN) return 0; - prj->flag = TAN; strcpy(prj->code, "TAN"); if (prj->r0 == 0.0) prj->r0 = R2D; @@ -1124,33 +1270,35 @@ struct prjprm *prj; prj->prjx2s = tanx2s; prj->prjs2x = tans2x; + prj->flag = (prj->flag == 1) ? -TAN : TAN; + return prjoff(prj, 0.0, 90.0); } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- -int tanx2s(prj, nx, ny, sxy, spt, x, y, phi, theta, stat) - -struct prjprm *prj; -int nx, ny, sxy, spt; -const double x[], y[]; -double phi[], theta[]; -int stat[]; +int tanx2s( + struct prjprm *prj, + int nx, + int ny, + int sxy, + int spt, + const double x[], + const double y[], + double phi[], + double theta[], + int stat[]) { - int mx, my, rowlen, rowoff, status; - double r, xj, yj, yj2; - register int ix, iy, *statp; - register const double *xp, *yp; - register double *phip, *thetap; - - - /* Initialize. */ + // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; - if (prj->flag != TAN) { + + int status; + if (abs(prj->flag) != TAN) { if ((status = tanset(prj))) return status; } + int mx, my; if (ny > 0) { mx = nx; my = ny; @@ -1160,35 +1308,37 @@ int stat[]; ny = nx; } + status = 0; + - /* Do x dependence. */ - xp = x; - rowoff = 0; - rowlen = nx*spt; - for (ix = 0; ix < nx; ix++, rowoff += spt, xp += sxy) { - xj = *xp + prj->x0; + // Do x dependence. + const double *xp = x; + int rowoff = 0; + int rowlen = nx*spt; + for (int ix = 0; ix < nx; ix++, rowoff += spt, xp += sxy) { + double xj = *xp + prj->x0; - phip = phi + rowoff; - for (iy = 0; iy < my; iy++) { + double *phip = phi + rowoff; + for (int iy = 0; iy < my; iy++) { *phip = xj; phip += rowlen; } } - /* Do y dependence. */ - yp = y; - phip = phi; - thetap = theta; - statp = stat; - for (iy = 0; iy < ny; iy++, yp += sxy) { - yj = *yp + prj->y0; - yj2 = yj*yj; + // Do y dependence. + const double *yp = y; + double *phip = phi; + double *thetap = theta; + int *statp = stat; + for (int iy = 0; iy < ny; iy++, yp += sxy) { + double yj = *yp + prj->y0; + double yj2 = yj*yj; - for (ix = 0; ix < mx; ix++, phip += spt, thetap += spt) { - xj = *phip; + for (int ix = 0; ix < mx; ix++, phip += spt, thetap += spt, statp++) { + double xj = *phip; - r = sqrt(xj*xj + yj2); + double r = sqrt(xj*xj + yj2); if (r == 0.0) { *phip = 0.0; } else { @@ -1196,37 +1346,43 @@ int stat[]; } *thetap = atan2d(prj->r0, r); - *(statp++) = 0; + *statp = 0; } } - return 0; -} - -/*--------------------------------------------------------------------------*/ -int tans2x(prj, nphi, ntheta, spt, sxy, phi, theta, x, y, stat) + // Do bounds checking on the native coordinates. + if (prj->bounds&4 && prjbchk(1.0e-13, nx, my, spt, phi, theta, stat)) { + if (!status) status = PRJERR_BAD_PIX_SET("tanx2s"); + } -struct prjprm *prj; -int nphi, ntheta, spt, sxy; -const double phi[], theta[]; -double x[], y[]; -int stat[]; + return status; +} -{ - int mphi, mtheta, rowlen, rowoff, status; - double cosphi, r, s, sinphi; - register int iphi, itheta, istat, *statp; - register const double *phip, *thetap; - register double *xp, *yp; +//---------------------------------------------------------------------------- +int tans2x( + struct prjprm *prj, + int nphi, + int ntheta, + int spt, + int sxy, + const double phi[], + const double theta[], + double x[], + double y[], + int stat[]) - /* Initialize. */ +{ + // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; - if (prj->flag != TAN) { + + int status; + if (abs(prj->flag) != TAN) { if ((status = tanset(prj))) return status; } + int mphi, mtheta; if (ntheta > 0) { mphi = nphi; mtheta = ntheta; @@ -1239,16 +1395,17 @@ int stat[]; status = 0; - /* Do phi dependence. */ - phip = phi; - rowoff = 0; - rowlen = nphi*sxy; - for (iphi = 0; iphi < nphi; iphi++, rowoff += sxy, phip += spt) { + // Do phi dependence. + const double *phip = phi; + int rowoff = 0; + int rowlen = nphi*sxy; + for (int iphi = 0; iphi < nphi; iphi++, rowoff += sxy, phip += spt) { + double sinphi, cosphi; sincosd(*phip, &sinphi, &cosphi); - xp = x + rowoff; - yp = y + rowoff; - for (itheta = 0; itheta < mtheta; itheta++) { + double *xp = x + rowoff; + double *yp = y + rowoff; + for (int itheta = 0; itheta < mtheta; itheta++) { *xp = sinphi; *yp = cosphi; xp += rowlen; @@ -1257,34 +1414,37 @@ int stat[]; } - /* Do theta dependence. */ - thetap = theta; - xp = x; - yp = y; - statp = stat; - for (itheta = 0; itheta < ntheta; itheta++, thetap += spt) { - s = sind(*thetap); + // Do theta dependence. + const double *thetap = theta; + double *xp = x; + double *yp = y; + int *statp = stat; + for (int itheta = 0; itheta < ntheta; itheta++, thetap += spt) { + double s = sind(*thetap); if (s == 0.0) { - for (iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy) { + for (int iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy, statp++) { *xp = 0.0; *yp = 0.0; - *(statp++) = 1; + *statp = 1; } if (!status) status = PRJERR_BAD_WORLD_SET("tans2x"); } else { - r = prj->r0*cosd(*thetap)/s; + double r = prj->r0*cosd(*thetap)/s; - istat = 0; - if (prj->bounds && s < 0.0) { - istat = 1; - if (!status) status = PRJERR_BAD_WORLD_SET("tans2x"); + // Bounds checking. + int istat = 0; + if (prj->bounds&1) { + if (s < 0.0) { + istat = 1; + if (!status) status = PRJERR_BAD_WORLD_SET("tans2x"); + } } - for (iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy) { + for (int iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy, statp++) { *xp = r*(*xp) - prj->x0; *yp = -r*(*yp) - prj->y0; - *(statp++) = istat; + *statp = istat; } } } @@ -1311,14 +1471,12 @@ int stat[]; * prj->prjs2x Pointer to stgs2x(). *===========================================================================*/ -int stgset(prj) - -struct prjprm *prj; +int stgset(struct prjprm *prj) { if (prj == 0x0) return PRJERR_NULL_POINTER; + if (prj->flag == -STG) return 0; - prj->flag = STG; strcpy(prj->code, "STG"); strcpy(prj->name, "stereographic"); @@ -1342,33 +1500,35 @@ struct prjprm *prj; prj->prjx2s = stgx2s; prj->prjs2x = stgs2x; + prj->flag = (prj->flag == 1) ? -STG : STG; + return prjoff(prj, 0.0, 90.0); } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- -int stgx2s(prj, nx, ny, sxy, spt, x, y, phi, theta, stat) - -struct prjprm *prj; -int nx, ny, sxy, spt; -const double x[], y[]; -double phi[], theta[]; -int stat[]; +int stgx2s( + struct prjprm *prj, + int nx, + int ny, + int sxy, + int spt, + const double x[], + const double y[], + double phi[], + double theta[], + int stat[]) { - int mx, my, rowlen, rowoff, status; - double r, xj, yj, yj2; - register int ix, iy, *statp; - register const double *xp, *yp; - register double *phip, *thetap; - - - /* Initialize. */ + // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; - if (prj->flag != STG) { + + int status; + if (abs(prj->flag) != STG) { if ((status = stgset(prj))) return status; } + int mx, my; if (ny > 0) { mx = nx; my = ny; @@ -1379,34 +1539,34 @@ int stat[]; } - /* Do x dependence. */ - xp = x; - rowoff = 0; - rowlen = nx*spt; - for (ix = 0; ix < nx; ix++, rowoff += spt, xp += sxy) { - xj = *xp + prj->x0; + // Do x dependence. + const double *xp = x; + int rowoff = 0; + int rowlen = nx*spt; + for (int ix = 0; ix < nx; ix++, rowoff += spt, xp += sxy) { + double xj = *xp + prj->x0; - phip = phi + rowoff; - for (iy = 0; iy < my; iy++) { + double *phip = phi + rowoff; + for (int iy = 0; iy < my; iy++) { *phip = xj; phip += rowlen; } } - /* Do y dependence. */ - yp = y; - phip = phi; - thetap = theta; - statp = stat; - for (iy = 0; iy < ny; iy++, yp += sxy) { - yj = *yp + prj->y0; - yj2 = yj*yj; + // Do y dependence. + const double *yp = y; + double *phip = phi; + double *thetap = theta; + int *statp = stat; + for (int iy = 0; iy < ny; iy++, yp += sxy) { + double yj = *yp + prj->y0; + double yj2 = yj*yj; - for (ix = 0; ix < mx; ix++, phip += spt, thetap += spt) { - xj = *phip; + for (int ix = 0; ix < mx; ix++, phip += spt, thetap += spt, statp++) { + double xj = *phip; - r = sqrt(xj*xj + yj2); + double r = sqrt(xj*xj + yj2); if (r == 0.0) { *phip = 0.0; } else { @@ -1414,37 +1574,37 @@ int stat[]; } *thetap = 90.0 - 2.0*atand(r*prj->w[1]); - *(statp++) = 0; + *statp = 0; } } return 0; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- -int stgs2x(prj, nphi, ntheta, spt, sxy, phi, theta, x, y, stat) - -struct prjprm *prj; -int nphi, ntheta, spt, sxy; -const double phi[], theta[]; -double x[], y[]; -int stat[]; +int stgs2x( + struct prjprm *prj, + int nphi, + int ntheta, + int spt, + int sxy, + const double phi[], + const double theta[], + double x[], + double y[], + int stat[]) { - int mphi, mtheta, rowlen, rowoff, status; - double cosphi, r, s, sinphi; - register int iphi, itheta, *statp; - register const double *phip, *thetap; - register double *xp, *yp; - - - /* Initialize. */ + // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; - if (prj->flag != STG) { + + int status; + if (abs(prj->flag) != STG) { if ((status = stgset(prj))) return status; } + int mphi, mtheta; if (ntheta > 0) { mphi = nphi; mtheta = ntheta; @@ -1457,16 +1617,17 @@ int stat[]; status = 0; - /* Do phi dependence. */ - phip = phi; - rowoff = 0; - rowlen = nphi*sxy; - for (iphi = 0; iphi < nphi; iphi++, rowoff += sxy, phip += spt) { + // Do phi dependence. + const double *phip = phi; + int rowoff = 0; + int rowlen = nphi*sxy; + for (int iphi = 0; iphi < nphi; iphi++, rowoff += sxy, phip += spt) { + double sinphi, cosphi; sincosd(*phip, &sinphi, &cosphi); - xp = x + rowoff; - yp = y + rowoff; - for (itheta = 0; itheta < mtheta; itheta++) { + double *xp = x + rowoff; + double *yp = y + rowoff; + for (int itheta = 0; itheta < mtheta; itheta++) { *xp = sinphi; *yp = cosphi; xp += rowlen; @@ -1475,28 +1636,28 @@ int stat[]; } - /* Do theta dependence. */ - thetap = theta; - xp = x; - yp = y; - statp = stat; - for (itheta = 0; itheta < ntheta; itheta++, thetap += spt) { - s = 1.0 + sind(*thetap); + // Do theta dependence. + const double *thetap = theta; + double *xp = x; + double *yp = y; + int *statp = stat; + for (int itheta = 0; itheta < ntheta; itheta++, thetap += spt) { + double s = 1.0 + sind(*thetap); if (s == 0.0) { - for (iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy) { + for (int iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy, statp++) { *xp = 0.0; *yp = 0.0; - *(statp++) = 1; + *statp = 1; } if (!status) status = PRJERR_BAD_WORLD_SET("stgs2x"); } else { - r = prj->w[0]*cosd(*thetap)/s; + double r = prj->w[0]*cosd(*thetap)/s; - for (iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy) { + for (int iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy, statp++) { *xp = r*(*xp) - prj->x0; *yp = -r*(*yp) - prj->y0; - *(statp++) = 0; + *statp = 0; } } } @@ -1528,14 +1689,12 @@ int stat[]; * prj->prjs2x Pointer to sins2x(). *===========================================================================*/ -int sinset(prj) - -struct prjprm *prj; +int sinset(struct prjprm *prj) { if (prj == 0x0) return PRJERR_NULL_POINTER; + if (prj->flag == -SIN) return 0; - prj->flag = SIN; strcpy(prj->code, "SIN"); if (undefined(prj->pv[1])) prj->pv[1] = 0.0; @@ -1559,38 +1718,40 @@ struct prjprm *prj; prj->prjx2s = sinx2s; prj->prjs2x = sins2x; + prj->flag = (prj->flag == 1) ? -SIN : SIN; + return prjoff(prj, 0.0, 90.0); } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- -int sinx2s(prj, nx, ny, sxy, spt, x, y, phi, theta, stat) - -struct prjprm *prj; -int nx, ny, sxy, spt; -const double x[], y[]; -double phi[], theta[]; -int stat[]; +int sinx2s( + struct prjprm *prj, + int nx, + int ny, + int sxy, + int spt, + const double x[], + const double y[], + double phi[], + double theta[], + int stat[]) { - int mx, my, rowlen, rowoff, status; const double tol = 1.0e-13; - double a, b, c, d, eta, r2, sinth1, sinth2, sinthe, x0, xi, x1, xy, y0, y02, - y1, z; - register int ix, iy, *statp; - register const double *xp, *yp; - register double *phip, *thetap; - - /* Initialize. */ + // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; - if (prj->flag != SIN) { + + int status; + if (abs(prj->flag) != SIN) { if ((status = sinset(prj))) return status; } - xi = prj->pv[1]; - eta = prj->pv[2]; + double xi = prj->pv[1]; + double eta = prj->pv[2]; + int mx, my; if (ny > 0) { mx = nx; my = ny; @@ -1603,37 +1764,37 @@ int stat[]; status = 0; - /* Do x dependence. */ - xp = x; - rowoff = 0; - rowlen = nx*spt; - for (ix = 0; ix < nx; ix++, rowoff += spt, xp += sxy) { - x0 = (*xp + prj->x0)*prj->w[0]; + // Do x dependence. + const double *xp = x; + int rowoff = 0; + int rowlen = nx*spt; + for (int ix = 0; ix < nx; ix++, rowoff += spt, xp += sxy) { + double x0 = (*xp + prj->x0)*prj->w[0]; - phip = phi + rowoff; - for (iy = 0; iy < my; iy++) { + double *phip = phi + rowoff; + for (int iy = 0; iy < my; iy++) { *phip = x0; phip += rowlen; } } - /* Do y dependence. */ - yp = y; - phip = phi; - thetap = theta; - statp = stat; - for (iy = 0; iy < ny; iy++, yp += sxy) { - y0 = (*yp + prj->y0)*prj->w[0]; - y02 = y0*y0; + // Do y dependence. + const double *yp = y; + double *phip = phi; + double *thetap = theta; + int *statp = stat; + for (int iy = 0; iy < ny; iy++, yp += sxy) { + double y0 = (*yp + prj->y0)*prj->w[0]; + double y02 = y0*y0; - for (ix = 0; ix < mx; ix++, phip += spt, thetap += spt) { - /* Compute intermediaries. */ - x0 = *phip; - r2 = x0*x0 + y02; + for (int ix = 0; ix < mx; ix++, phip += spt, thetap += spt, statp++) { + // Compute intermediaries. + double x0 = *phip; + double r2 = x0*x0 + y02; if (prj->w[1] == 0.0) { - /* Orthographic projection. */ + // Orthographic projection. if (r2 != 0.0) { *phip = atan2d(x0, -y0); } else { @@ -1645,40 +1806,41 @@ int stat[]; } else if (r2 <= 1.0) { *thetap = asind(sqrt(1.0 - r2)); } else { - *(statp++) = 1; + *statp = 1; if (!status) status = PRJERR_BAD_PIX_SET("sinx2s") continue; } } else { - /* "Synthesis" projection. */ - xy = x0*xi + y0*eta; + // "Synthesis" projection. + double xy = x0*xi + y0*eta; + double z; if (r2 < 1.0e-10) { - /* Use small angle formula. */ + // Use small angle formula. z = r2/2.0; *thetap = 90.0 - R2D*sqrt(r2/(1.0 + xy)); } else { - a = prj->w[2]; - b = xy - prj->w[1]; - c = r2 - xy - xy + prj->w[3]; - d = b*b - a*c; + double a = prj->w[2]; + double b = xy - prj->w[1]; + double c = r2 - xy - xy + prj->w[3]; + double d = b*b - a*c; - /* Check for a solution. */ + // Check for a solution. if (d < 0.0) { *phip = 0.0; *thetap = 0.0; - *(statp++) = 1; + *statp = 1; if (!status) status = PRJERR_BAD_PIX_SET("sinx2s") continue; } d = sqrt(d); - /* Choose solution closest to pole. */ - sinth1 = (-b + d)/a; - sinth2 = (-b - d)/a; - sinthe = (sinth1 > sinth2) ? sinth1 : sinth2; + // Choose solution closest to pole. + double sinth1 = (-b + d)/a; + double sinth2 = (-b - d)/a; + double sinthe = (sinth1 > sinth2) ? sinth1 : sinth2; if (sinthe > 1.0) { if (sinthe-1.0 < tol) { sinthe = 1.0; @@ -1696,7 +1858,7 @@ int stat[]; if (sinthe > 1.0 || sinthe < -1.0) { *phip = 0.0; *thetap = 0.0; - *(statp++) = 1; + *statp = 1; if (!status) status = PRJERR_BAD_PIX_SET("sinx2s") continue; } @@ -1705,8 +1867,8 @@ int stat[]; z = 1.0 - sinthe; } - x1 = -y0 + eta*z; - y1 = x0 - xi*z; + double x1 = -y0 + eta*z; + double y1 = x0 - xi*z; if (x1 == 0.0 && y1 == 0.0) { *phip = 0.0; } else { @@ -1714,37 +1876,43 @@ int stat[]; } } - *(statp++) = 0; + *statp = 0; } } + + // Do bounds checking on the native coordinates. + if (prj->bounds&4 && prjbchk(1.0e-13, nx, my, spt, phi, theta, stat)) { + if (!status) status = PRJERR_BAD_PIX_SET("sinx2s"); + } + return status; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- -int sins2x(prj, nphi, ntheta, spt, sxy, phi, theta, x, y, stat) - -struct prjprm *prj; -int nphi, ntheta, spt, sxy; -const double phi[], theta[]; -double x[], y[]; -int stat[]; +int sins2x( + struct prjprm *prj, + int nphi, + int ntheta, + int spt, + int sxy, + const double phi[], + const double theta[], + double x[], + double y[], + int stat[]) { - int mphi, mtheta, rowlen, rowoff, status; - double cosphi, costhe, sinphi, r, t, z, z1, z2; - register int iphi, itheta, istat, *statp; - register const double *phip, *thetap; - register double *xp, *yp; - - - /* Initialize. */ + // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; - if (prj->flag != SIN) { + + int status; + if (abs(prj->flag) != SIN) { if ((status = sinset(prj))) return status; } + int mphi, mtheta; if (ntheta > 0) { mphi = nphi; mtheta = ntheta; @@ -1757,16 +1925,17 @@ int stat[]; status = 0; - /* Do phi dependence. */ - phip = phi; - rowoff = 0; - rowlen = nphi*sxy; - for (iphi = 0; iphi < nphi; iphi++, rowoff += sxy, phip += spt) { + // Do phi dependence. + const double *phip = phi; + int rowoff = 0; + int rowlen = nphi*sxy; + for (int iphi = 0; iphi < nphi; iphi++, rowoff += sxy, phip += spt) { + double sinphi, cosphi; sincosd(*phip, &sinphi, &cosphi); - xp = x + rowoff; - yp = y + rowoff; - for (itheta = 0; itheta < mtheta; itheta++) { + double *xp = x + rowoff; + double *yp = y + rowoff; + for (int itheta = 0; itheta < mtheta; itheta++) { *xp = sinphi; *yp = cosphi; xp += rowlen; @@ -1775,13 +1944,15 @@ int stat[]; } - /* Do theta dependence. */ - thetap = theta; - xp = x; - yp = y; - statp = stat; - for (itheta = 0; itheta < ntheta; itheta++, thetap += spt) { - t = (90.0 - fabs(*thetap))*D2R; + // Do theta dependence. + const double *thetap = theta; + double *xp = x; + double *yp = y; + int *statp = stat; + for (int itheta = 0; itheta < ntheta; itheta++, thetap += spt) { + double costhe, z; + + double t = (90.0 - fabs(*thetap))*D2R; if (t < 1.0e-5) { if (*thetap > 0.0) { z = t*t/2.0; @@ -1793,31 +1964,33 @@ int stat[]; z = 1.0 - sind(*thetap); costhe = cosd(*thetap); } - r = prj->r0*costhe; + double r = prj->r0*costhe; if (prj->w[1] == 0.0) { - /* Orthographic projection. */ - istat = 0; - if (prj->bounds && *thetap < 0.0) { - istat = 1; - if (!status) status = PRJERR_BAD_WORLD_SET("sins2x"); + // Orthographic projection. + int istat = 0; + if (prj->bounds&1) { + if (*thetap < 0.0) { + istat = 1; + if (!status) status = PRJERR_BAD_WORLD_SET("sins2x"); + } } - for (iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy) { + for (int iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy, statp++) { *xp = r*(*xp) - prj->x0; *yp = -r*(*yp) - prj->y0; - *(statp++) = istat; + *statp = istat; } } else { - /* "Synthesis" projection. */ + // "Synthesis" projection. z *= prj->r0; - z1 = prj->pv[1]*z - prj->x0; - z2 = prj->pv[2]*z - prj->y0; + double z1 = prj->pv[1]*z - prj->x0; + double z2 = prj->pv[2]*z - prj->y0; - for (iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy) { - istat = 0; - if (prj->bounds) { + for (int iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy, statp++) { + int istat = 0; + if (prj->bounds&1) { t = -atand(prj->pv[1]*(*xp) - prj->pv[2]*(*yp)); if (*thetap < t) { istat = 1; @@ -1827,7 +2000,7 @@ int stat[]; *xp = r*(*xp) + z1; *yp = -r*(*yp) + z2; - *(statp++) = istat; + *statp = istat; } } } @@ -1854,14 +2027,12 @@ int stat[]; * prj->prjs2x Pointer to arcs2x(). *===========================================================================*/ -int arcset(prj) - -struct prjprm *prj; +int arcset(struct prjprm *prj) { if (prj == 0x0) return PRJERR_NULL_POINTER; + if (prj->flag == -ARC) return 0; - prj->flag = ARC; strcpy(prj->code, "ARC"); strcpy(prj->name, "zenithal/azimuthal equidistant"); @@ -1885,33 +2056,35 @@ struct prjprm *prj; prj->prjx2s = arcx2s; prj->prjs2x = arcs2x; + prj->flag = (prj->flag == 1) ? -ARC : ARC; + return prjoff(prj, 0.0, 90.0); } -/*--------------------------------------------------------------------------*/ - -int arcx2s(prj, nx, ny, sxy, spt, x, y, phi, theta, stat) +//---------------------------------------------------------------------------- -struct prjprm *prj; -int nx, ny, sxy, spt; -const double x[], y[]; -double phi[], theta[]; -int stat[]; +int arcx2s( + struct prjprm *prj, + int nx, + int ny, + int sxy, + int spt, + const double x[], + const double y[], + double phi[], + double theta[], + int stat[]) { - int mx, my, rowlen, rowoff, status; - double r, xj, yj, yj2; - register int ix, iy, *statp; - register const double *xp, *yp; - register double *phip, *thetap; - - - /* Initialize. */ + // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; - if (prj->flag != ARC) { + + int status; + if (abs(prj->flag) != ARC) { if ((status = arcset(prj))) return status; } + int mx, my; if (ny > 0) { mx = nx; my = ny; @@ -1921,35 +2094,37 @@ int stat[]; ny = nx; } + status = 0; - /* Do x dependence. */ - xp = x; - rowoff = 0; - rowlen = nx*spt; - for (ix = 0; ix < nx; ix++, rowoff += spt, xp += sxy) { - xj = *xp + prj->x0; - phip = phi + rowoff; - for (iy = 0; iy < my; iy++) { + // Do x dependence. + const double *xp = x; + int rowoff = 0; + int rowlen = nx*spt; + for (int ix = 0; ix < nx; ix++, rowoff += spt, xp += sxy) { + double xj = *xp + prj->x0; + + double *phip = phi + rowoff; + for (int iy = 0; iy < my; iy++) { *phip = xj; phip += rowlen; } } - /* Do y dependence. */ - yp = y; - phip = phi; - thetap = theta; - statp = stat; - for (iy = 0; iy < ny; iy++, yp += sxy) { - yj = *yp + prj->y0; - yj2 = yj*yj; + // Do y dependence. + const double *yp = y; + double *phip = phi; + double *thetap = theta; + int *statp = stat; + for (int iy = 0; iy < ny; iy++, yp += sxy) { + double yj = *yp + prj->y0; + double yj2 = yj*yj; - for (ix = 0; ix < mx; ix++, phip += spt, thetap += spt) { - xj = *phip; + for (int ix = 0; ix < mx; ix++, phip += spt, thetap += spt, statp++) { + double xj = *phip; - r = sqrt(xj*xj + yj2); + double r = sqrt(xj*xj + yj2); if (r == 0.0) { *phip = 0.0; *thetap = 90.0; @@ -1958,37 +2133,43 @@ int stat[]; *thetap = 90.0 - r*prj->w[1]; } - *(statp++) = 0; + *statp = 0; } } - return 0; -} - -/*--------------------------------------------------------------------------*/ -int arcs2x(prj, nphi, ntheta, spt, sxy, phi, theta, x, y, stat) + // Do bounds checking on the native coordinates. + if (prj->bounds&4 && prjbchk(1.0e-13, nx, my, spt, phi, theta, stat)) { + if (!status) status = PRJERR_BAD_PIX_SET("arcx2s"); + } -struct prjprm *prj; -int nphi, ntheta, spt, sxy; -const double phi[], theta[]; -double x[], y[]; -int stat[]; + return status; +} -{ - int mphi, mtheta, rowlen, rowoff, status; - double cosphi, r, sinphi; - register int iphi, itheta, *statp; - register const double *phip, *thetap; - register double *xp, *yp; +//---------------------------------------------------------------------------- +int arcs2x( + struct prjprm *prj, + int nphi, + int ntheta, + int spt, + int sxy, + const double phi[], + const double theta[], + double x[], + double y[], + int stat[]) - /* Initialize. */ +{ + // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; - if (prj->flag != ARC) { + + int status; + if (abs(prj->flag) != ARC) { if ((status = arcset(prj))) return status; } + int mphi, mtheta; if (ntheta > 0) { mphi = nphi; mtheta = ntheta; @@ -1999,16 +2180,17 @@ int stat[]; } - /* Do phi dependence. */ - phip = phi; - rowoff = 0; - rowlen = nphi*sxy; - for (iphi = 0; iphi < nphi; iphi++, rowoff += sxy, phip += spt) { + // Do phi dependence. + const double *phip = phi; + int rowoff = 0; + int rowlen = nphi*sxy; + for (int iphi = 0; iphi < nphi; iphi++, rowoff += sxy, phip += spt) { + double sinphi, cosphi; sincosd(*phip, &sinphi, &cosphi); - xp = x + rowoff; - yp = y + rowoff; - for (itheta = 0; itheta < mtheta; itheta++) { + double *xp = x + rowoff; + double *yp = y + rowoff; + for (int itheta = 0; itheta < mtheta; itheta++) { *xp = sinphi; *yp = cosphi; xp += rowlen; @@ -2017,18 +2199,18 @@ int stat[]; } - /* Do theta dependence. */ - thetap = theta; - xp = x; - yp = y; - statp = stat; - for (itheta = 0; itheta < ntheta; itheta++, thetap += spt) { - r = prj->w[0]*(90.0 - *thetap); + // Do theta dependence. + const double *thetap = theta; + double *xp = x; + double *yp = y; + int *statp = stat; + for (int itheta = 0; itheta < ntheta; itheta++, thetap += spt) { + double r = prj->w[0]*(90.0 - *thetap); - for (iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy) { + for (int iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy, statp++) { *xp = r*(*xp) - prj->x0; *yp = -r*(*yp) - prj->y0; - *(statp++) = 0; + *statp = 0; } } @@ -2058,19 +2240,15 @@ int stat[]; * prj->prjs2x Pointer to zpns2x(). *===========================================================================*/ -int zpnset(prj) - -struct prjprm *prj; +int zpnset(struct prjprm *prj) { - int j, k, m; - double d, d1, d2, r, zd, zd1, zd2; const double tol = 1.0e-13; if (prj == 0x0) return PRJERR_NULL_POINTER; + if (prj->flag == -ZPN) return 0; strcpy(prj->code, "ZPN"); - prj->flag = ZPN; if (undefined(prj->pv[1])) prj->pv[1] = 0.0; if (undefined(prj->pv[2])) prj->pv[2] = 0.0; @@ -2086,7 +2264,8 @@ struct prjprm *prj; prj->global = 0; prj->divergent = 0; - /* Find the highest non-zero coefficient. */ + // Find the highest non-zero coefficient. + int k; for (k = PVN-1; k >= 0 && prj->pv[k] == 0.0; k--); if (k < 0) { return PRJERR_BAD_PARAM_SET("zpnset"); @@ -2095,22 +2274,27 @@ struct prjprm *prj; prj->n = k; if (k < 2) { - /* No point of inflection. */ + // No point of inflection. prj->w[0] = PI; } else { - /* Find the point of inflection closest to the pole. */ - zd1 = 0.0; - d1 = prj->pv[1]; + // Find the point of inflection closest to the pole. + double d, d1, d2; + + d1 = prj->pv[1]; if (d1 <= 0.0) { return PRJERR_BAD_PARAM_SET("zpnset"); } - /* Find the point where the derivative first goes negative. */ + // Find the point where the derivative first goes negative. + int j; + double zd, zd1, zd2; + + zd1 = 0.0; for (j = 0; j < 180; j++) { zd2 = j*D2R; d2 = 0.0; - for (m = k; m > 0; m--) { + for (int m = k; m > 0; m--) { d2 = d2*zd2 + m*prj->pv[m]; } @@ -2120,16 +2304,16 @@ struct prjprm *prj; } if (j == 180) { - /* No negative derivative -> no point of inflection. */ + // No negative derivative -> no point of inflection. zd = PI; prj->global = 1; } else { - /* Find where the derivative is zero. */ + // Find where the derivative is zero. for (j = 1; j <= 10; j++) { zd = zd1 - d1*(zd2-zd1)/(d2-d1); d = 0.0; - for (m = k; m > 0; m--) { + for (int m = k; m > 0; m--) { d = d*zd + m*prj->pv[m]; } @@ -2145,8 +2329,8 @@ struct prjprm *prj; } } - r = 0.0; - for (m = k; m >= 0; m--) { + double r = 0.0; + for (int m = k; m >= 0; m--) { r = r*zd + prj->pv[m]; } prj->w[0] = zd; @@ -2156,36 +2340,39 @@ struct prjprm *prj; prj->prjx2s = zpnx2s; prj->prjs2x = zpns2x; + prj->flag = (prj->flag == 1) ? -ZPN : ZPN; + return prjoff(prj, 0.0, 90.0); } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- -int zpnx2s(prj, nx, ny, sxy, spt, x, y, phi, theta, stat) - -struct prjprm *prj; -int nx, ny, sxy, spt; -const double x[], y[]; -double phi[], theta[]; -int stat[]; +int zpnx2s( + struct prjprm *prj, + int nx, + int ny, + int sxy, + int spt, + const double x[], + const double y[], + double phi[], + double theta[], + int stat[]) { - int j, k, m, mx, my, rowlen, rowoff, status; - double a, b, c, d, lambda, r, r1, r2, rt, xj, yj, yj2, zd, zd1, zd2; const double tol = 1.0e-13; - register int ix, iy, *statp; - register const double *xp, *yp; - register double *phip, *thetap; - - /* Initialize. */ + // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; - if (prj->flag != ZPN) { + + int status; + if (abs(prj->flag) != ZPN) { if ((status = zpnset(prj))) return status; } - k = prj->n; + int k = prj->n; + int mx, my; if (ny > 0) { mx = nx; my = ny; @@ -2198,70 +2385,74 @@ int stat[]; status = 0; - /* Do x dependence. */ - xp = x; - rowoff = 0; - rowlen = nx*spt; - for (ix = 0; ix < nx; ix++, rowoff += spt, xp += sxy) { - xj = *xp + prj->x0; + // Do x dependence. + const double *xp = x; + int rowoff = 0; + int rowlen = nx*spt; + for (int ix = 0; ix < nx; ix++, rowoff += spt, xp += sxy) { + double xj = *xp + prj->x0; - phip = phi + rowoff; - for (iy = 0; iy < my; iy++) { + double *phip = phi + rowoff; + for (int iy = 0; iy < my; iy++) { *phip = xj; phip += rowlen; } } - /* Do y dependence. */ - yp = y; - phip = phi; - thetap = theta; - statp = stat; - for (iy = 0; iy < ny; iy++, yp += sxy) { - yj = *yp + prj->y0; - yj2 = yj*yj; + // Do y dependence. + const double *yp = y; + double *phip = phi; + double *thetap = theta; + int *statp = stat; + for (int iy = 0; iy < ny; iy++, yp += sxy) { + double yj = *yp + prj->y0; + double yj2 = yj*yj; - for (ix = 0; ix < mx; ix++, phip += spt, thetap += spt) { - xj = *phip; + for (int ix = 0; ix < mx; ix++, phip += spt, thetap += spt, statp++) { + double xj = *phip; - r = sqrt(xj*xj + yj2)/prj->r0; + double r = sqrt(xj*xj + yj2)/prj->r0; if (r == 0.0) { *phip = 0.0; } else { *phip = atan2d(xj, -yj); } + double zd; if (k < 1) { - /* Constant - no solution. */ + // Constant - no solution. return PRJERR_BAD_PARAM_SET("zpnx2s"); + } else if (k == 1) { - /* Linear. */ + // Linear. zd = (r - prj->pv[0])/prj->pv[1]; + } else if (k == 2) { - /* Quadratic. */ - a = prj->pv[2]; - b = prj->pv[1]; - c = prj->pv[0] - r; + // Quadratic. + double a = prj->pv[2]; + double b = prj->pv[1]; + double c = prj->pv[0] - r; - d = b*b - 4.0*a*c; + double d = b*b - 4.0*a*c; if (d < 0.0) { *thetap = 0.0; - *(statp++) = 1; + *statp = 1; if (!status) status = PRJERR_BAD_PIX_SET("zpnx2s"); continue; } d = sqrt(d); - /* Choose solution closest to pole. */ - zd1 = (-b + d)/(2.0*a); - zd2 = (-b - d)/(2.0*a); + // Choose solution closest to pole. + double zd1 = (-b + d)/(2.0*a); + double zd2 = (-b - d)/(2.0*a); + zd = (zd1zd2) ? zd1 : zd2; if (zd < 0.0) { if (zd < -tol) { *thetap = 0.0; - *(statp++) = 1; + *statp = 1; if (!status) status = PRJERR_BAD_PIX_SET("zpnx2s"); continue; } @@ -2269,23 +2460,23 @@ int stat[]; } else if (zd > PI) { if (zd > PI+tol) { *thetap = 0.0; - *(statp++) = 1; + *statp = 1; if (!status) status = PRJERR_BAD_PIX_SET("zpnx2s"); continue; } zd = PI; } } else { - /* Higher order - solve iteratively. */ - zd1 = 0.0; - r1 = prj->pv[0]; - zd2 = prj->w[0]; - r2 = prj->w[1]; + // Higher order - solve iteratively. + double zd1 = 0.0; + double r1 = prj->pv[0]; + double zd2 = prj->w[0]; + double r2 = prj->w[1]; if (r < r1) { if (r < r1-tol) { *thetap = 0.0; - *(statp++) = 1; + *statp = 1; if (!status) status = PRJERR_BAD_PIX_SET("zpnx2s"); continue; } @@ -2293,15 +2484,15 @@ int stat[]; } else if (r > r2) { if (r > r2+tol) { *thetap = 0.0; - *(statp++) = 1; + *statp = 1; if (!status) status = PRJERR_BAD_PIX_SET("zpnx2s"); continue; } zd = zd2; } else { - /* Disect the interval. */ - for (j = 0; j < 100; j++) { - lambda = (r2 - r)/(r2 - r1); + // Dissect the interval. + for (int j = 0; j < 100; j++) { + double lambda = (r2 - r)/(r2 - r1); if (lambda < 0.1) { lambda = 0.1; } else if (lambda > 0.9) { @@ -2310,8 +2501,8 @@ int stat[]; zd = zd2 - lambda*(zd2 - zd1); - rt = 0.0; - for (m = k; m >= 0; m--) { + double rt = 0.0; + for (int m = k; m >= 0; m--) { rt = (rt * zd) + prj->pv[m]; } @@ -2331,37 +2522,43 @@ int stat[]; } *thetap = 90.0 - zd*R2D; - *(statp++) = 0; + *statp = 0; } } + + // Do bounds checking on the native coordinates. + if (prj->bounds&4 && prjbchk(1.0e-13, nx, my, spt, phi, theta, stat)) { + if (!status) status = PRJERR_BAD_PIX_SET("zpnx2s"); + } + return status; } -/*--------------------------------------------------------------------------*/ - -int zpns2x(prj, nphi, ntheta, spt, sxy, phi, theta, x, y, stat) +//---------------------------------------------------------------------------- -struct prjprm *prj; -int nphi, ntheta, spt, sxy; -const double phi[], theta[]; -double x[], y[]; -int stat[]; +int zpns2x( + struct prjprm *prj, + int nphi, + int ntheta, + int spt, + int sxy, + const double phi[], + const double theta[], + double x[], + double y[], + int stat[]) { - int m, mphi, mtheta, rowlen, rowoff, status; - double cosphi, r, s, sinphi; - register int iphi, itheta, istat, *statp; - register const double *phip, *thetap; - register double *xp, *yp; - - - /* Initialize. */ + // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; - if (prj->flag != ZPN) { + + int status; + if (abs(prj->flag) != ZPN) { if ((status = zpnset(prj))) return status; } + int mphi, mtheta; if (ntheta > 0) { mphi = nphi; mtheta = ntheta; @@ -2374,16 +2571,17 @@ int stat[]; status = 0; - /* Do phi dependence. */ - phip = phi; - rowoff = 0; - rowlen = nphi*sxy; - for (iphi = 0; iphi < nphi; iphi++, rowoff += sxy, phip += spt) { + // Do phi dependence. + const double *phip = phi; + int rowoff = 0; + int rowlen = nphi*sxy; + for (int iphi = 0; iphi < nphi; iphi++, rowoff += sxy, phip += spt) { + double sinphi, cosphi; sincosd(*phip, &sinphi, &cosphi); - xp = x + rowoff; - yp = y + rowoff; - for (itheta = 0; itheta < mtheta; itheta++) { + double *xp = x + rowoff; + double *yp = y + rowoff; + for (int itheta = 0; itheta < mtheta; itheta++) { *xp = sinphi; *yp = cosphi; xp += rowlen; @@ -2392,30 +2590,33 @@ int stat[]; } - /* Do theta dependence. */ - thetap = theta; - xp = x; - yp = y; - statp = stat; - for (itheta = 0; itheta < ntheta; itheta++, thetap += spt) { - s = (90.0 - *thetap)*D2R; - - r = 0.0; - for (m = prj->n; m >= 0; m--) { + // Do theta dependence. + const double *thetap = theta; + double *xp = x; + double *yp = y; + int *statp = stat; + for (int itheta = 0; itheta < ntheta; itheta++, thetap += spt) { + double s = (90.0 - *thetap)*D2R; + + double r = 0.0; + for (int m = prj->n; m >= 0; m--) { r = r*s + prj->pv[m]; } r *= prj->r0; - istat = 0; - if (prj->bounds && s > prj->w[0]) { - istat = 1; - if (!status) status = PRJERR_BAD_WORLD_SET("zpns2x"); + // Bounds checking. + int istat = 0; + if (prj->bounds&1) { + if (s > prj->w[0]) { + istat = 1; + if (!status) status = PRJERR_BAD_WORLD_SET("zpns2x"); + } } - for (iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy) { + for (int iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy, statp++) { *xp = r*(*xp) - prj->x0; *yp = -r*(*yp) - prj->y0; - *(statp++) = istat; + *statp = istat; } } @@ -2441,14 +2642,12 @@ int stat[]; * prj->prjs2x Pointer to zeas2x(). *===========================================================================*/ -int zeaset(prj) - -struct prjprm *prj; +int zeaset(struct prjprm *prj) { if (prj == 0x0) return PRJERR_NULL_POINTER; + if (prj->flag == -ZEA) return 0; - prj->flag = ZEA; strcpy(prj->code, "ZEA"); strcpy(prj->name, "zenithal/azimuthal equal area"); @@ -2472,34 +2671,37 @@ struct prjprm *prj; prj->prjx2s = zeax2s; prj->prjs2x = zeas2x; + prj->flag = (prj->flag == 1) ? -ZEA : ZEA; + return prjoff(prj, 0.0, 90.0); } -/*--------------------------------------------------------------------------*/ - -int zeax2s(prj, nx, ny, sxy, spt, x, y, phi, theta, stat) +//---------------------------------------------------------------------------- -struct prjprm *prj; -int nx, ny, sxy, spt; -const double x[], y[]; -double phi[], theta[]; -int stat[]; +int zeax2s( + struct prjprm *prj, + int nx, + int ny, + int sxy, + int spt, + const double x[], + const double y[], + double phi[], + double theta[], + int stat[]) { - int mx, my, rowlen, rowoff, status; - double r, s, xj, yj, yj2; const double tol = 1.0e-12; - register int ix, iy, *statp; - register const double *xp, *yp; - register double *phip, *thetap; - - /* Initialize. */ + // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; - if (prj->flag != ZEA) { + + int status; + if (abs(prj->flag) != ZEA) { if ((status = zeaset(prj))) return status; } + int mx, my; if (ny > 0) { mx = nx; my = ny; @@ -2512,47 +2714,47 @@ int stat[]; status = 0; - /* Do x dependence. */ - xp = x; - rowoff = 0; - rowlen = nx*spt; - for (ix = 0; ix < nx; ix++, rowoff += spt, xp += sxy) { - xj = *xp + prj->x0; + // Do x dependence. + const double *xp = x; + int rowoff = 0; + int rowlen = nx*spt; + for (int ix = 0; ix < nx; ix++, rowoff += spt, xp += sxy) { + double xj = *xp + prj->x0; - phip = phi + rowoff; - for (iy = 0; iy < my; iy++) { + double *phip = phi + rowoff; + for (int iy = 0; iy < my; iy++) { *phip = xj; phip += rowlen; } } - /* Do y dependence. */ - yp = y; - phip = phi; - thetap = theta; - statp = stat; - for (iy = 0; iy < ny; iy++, yp += sxy) { - yj = *yp + prj->y0; - yj2 = yj*yj; + // Do y dependence. + const double *yp = y; + double *phip = phi; + double *thetap = theta; + int *statp = stat; + for (int iy = 0; iy < ny; iy++, yp += sxy) { + double yj = *yp + prj->y0; + double yj2 = yj*yj; - for (ix = 0; ix < mx; ix++, phip += spt, thetap += spt) { - xj = *phip; + for (int ix = 0; ix < mx; ix++, phip += spt, thetap += spt, statp++) { + double xj = *phip; - r = sqrt(xj*xj + yj2); + double r = sqrt(xj*xj + yj2); if (r == 0.0) { *phip = 0.0; } else { *phip = atan2d(xj, -yj); } - s = r*prj->w[1]; + double s = r*prj->w[1]; if (fabs(s) > 1.0) { if (fabs(r - prj->w[0]) < tol) { *thetap = -90.0; } else { *thetap = 0.0; - *(statp++) = 1; + *statp = 1; if (!status) status = PRJERR_BAD_PIX_SET("zeax2s"); continue; } @@ -2560,37 +2762,43 @@ int stat[]; *thetap = 90.0 - 2.0*asind(s); } - *(statp++) = 0; + *statp = 0; } } + + // Do bounds checking on the native coordinates. + if (prj->bounds&4 && prjbchk(1.0e-13, nx, my, spt, phi, theta, stat)) { + if (!status) status = PRJERR_BAD_PIX_SET("zeax2s"); + } + return status; } -/*--------------------------------------------------------------------------*/ - -int zeas2x(prj, nphi, ntheta, spt, sxy, phi, theta, x, y, stat) +//---------------------------------------------------------------------------- -struct prjprm *prj; -int nphi, ntheta, spt, sxy; -const double phi[], theta[]; -double x[], y[]; -int stat[]; +int zeas2x( + struct prjprm *prj, + int nphi, + int ntheta, + int spt, + int sxy, + const double phi[], + const double theta[], + double x[], + double y[], + int stat[]) { - int mphi, mtheta, rowlen, rowoff, status; - double cosphi, r, sinphi; - register int iphi, itheta, *statp; - register const double *phip, *thetap; - register double *xp, *yp; - - - /* Initialize. */ + // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; - if (prj->flag != ZEA) { + + int status; + if (abs(prj->flag) != ZEA) { if ((status = zeaset(prj))) return status; } + int mphi, mtheta; if (ntheta > 0) { mphi = nphi; mtheta = ntheta; @@ -2601,16 +2809,17 @@ int stat[]; } - /* Do phi dependence. */ - phip = phi; - rowoff = 0; - rowlen = nphi*sxy; - for (iphi = 0; iphi < nphi; iphi++, rowoff += sxy, phip += spt) { + // Do phi dependence. + const double *phip = phi; + int rowoff = 0; + int rowlen = nphi*sxy; + for (int iphi = 0; iphi < nphi; iphi++, rowoff += sxy, phip += spt) { + double sinphi, cosphi; sincosd(*phip, &sinphi, &cosphi); - xp = x + rowoff; - yp = y + rowoff; - for (itheta = 0; itheta < mtheta; itheta++) { + double *xp = x + rowoff; + double *yp = y + rowoff; + for (int itheta = 0; itheta < mtheta; itheta++) { *xp = sinphi; *yp = cosphi; xp += rowlen; @@ -2619,18 +2828,18 @@ int stat[]; } - /* Do theta dependence. */ - thetap = theta; - xp = x; - yp = y; - statp = stat; - for (itheta = 0; itheta < ntheta; itheta++, thetap += spt) { - r = prj->w[0]*sind((90.0 - *thetap)/2.0); + // Do theta dependence. + const double *thetap = theta; + double *xp = x; + double *yp = y; + int *statp = stat; + for (int itheta = 0; itheta < ntheta; itheta++, thetap += spt) { + double r = prj->w[0]*sind((90.0 - *thetap)/2.0); - for (iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy) { + for (int iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy, statp++) { *xp = r*(*xp) - prj->x0; *yp = -r*(*yp) - prj->y0; - *(statp++) = 0; + *statp = 0; } } @@ -2666,17 +2875,14 @@ int stat[]; * prj->prjs2x Pointer to airs2x(). *===========================================================================*/ -int airset(prj) - -struct prjprm *prj; +int airset(struct prjprm *prj) { const double tol = 1.0e-4; - double cosxi; if (prj == 0x0) return PRJERR_NULL_POINTER; + if (prj->flag == -AIR) return 0; - prj->flag = AIR; strcpy(prj->code, "AIR"); if (undefined(prj->pv[1])) prj->pv[1] = 90.0; @@ -2696,7 +2902,7 @@ struct prjprm *prj; prj->w[1] = -0.5; prj->w[2] = 1.0; } else if (prj->pv[1] > -90.0) { - cosxi = cosd((90.0 - prj->pv[1])/2.0); + double cosxi = cosd((90.0 - prj->pv[1])/2.0); prj->w[1] = log(cosxi)*(cosxi*cosxi)/(1.0-cosxi*cosxi); prj->w[2] = 0.5 - prj->w[1]; } else { @@ -2711,34 +2917,37 @@ struct prjprm *prj; prj->prjx2s = airx2s; prj->prjs2x = airs2x; + prj->flag = (prj->flag == 1) ? -AIR : AIR; + return prjoff(prj, 0.0, 90.0); } -/*--------------------------------------------------------------------------*/ - -int airx2s(prj, nx, ny, sxy, spt, x, y, phi, theta, stat) +//---------------------------------------------------------------------------- -struct prjprm *prj; -int nx, ny, sxy, spt; -const double x[], y[]; -double phi[], theta[]; -int stat[]; +int airx2s( + struct prjprm *prj, + int nx, + int ny, + int sxy, + int spt, + const double x[], + const double y[], + double phi[], + double theta[], + int stat[]) { - int k, mx, my, rowlen, rowoff, status; - double cosxi, lambda, r, r1, r2, rt, tanxi, x1, x2, xi, xj, yj, yj2; const double tol = 1.0e-12; - register int ix, iy, *statp; - register const double *xp, *yp; - register double *phip, *thetap; - - /* Initialize. */ + // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; - if (prj->flag != AIR) { + + int status; + if (abs(prj->flag) != AIR) { if ((status = airset(prj))) return status; } + int mx, my; if (ny > 0) { mx = nx; my = ny; @@ -2751,52 +2960,56 @@ int stat[]; status = 0; - /* Do x dependence. */ - xp = x; - rowoff = 0; - rowlen = nx*spt; - for (ix = 0; ix < nx; ix++, rowoff += spt, xp += sxy) { - xj = *xp + prj->x0; + // Do x dependence. + const double *xp = x; + int rowoff = 0; + int rowlen = nx*spt; + for (int ix = 0; ix < nx; ix++, rowoff += spt, xp += sxy) { + double xj = *xp + prj->x0; - phip = phi + rowoff; - for (iy = 0; iy < my; iy++) { + double *phip = phi + rowoff; + for (int iy = 0; iy < my; iy++) { *phip = xj; phip += rowlen; } } - /* Do y dependence. */ - yp = y; - phip = phi; - thetap = theta; - statp = stat; - for (iy = 0; iy < ny; iy++, yp += sxy) { - yj = *yp + prj->y0; - yj2 = yj*yj; + // Do y dependence. + const double *yp = y; + double *phip = phi; + double *thetap = theta; + int *statp = stat; + for (int iy = 0; iy < ny; iy++, yp += sxy) { + double yj = *yp + prj->y0; + double yj2 = yj*yj; - for (ix = 0; ix < mx; ix++, phip += spt, thetap += spt) { - xj = *phip; + for (int ix = 0; ix < mx; ix++, phip += spt, thetap += spt, statp++) { + double xj = *phip; - r = sqrt(xj*xj + yj2)/prj->w[0]; + double r = sqrt(xj*xj + yj2)/prj->w[0]; if (r == 0.0) { *phip = 0.0; } else { *phip = atan2d(xj, -yj); } - + double xi; if (r == 0.0) { xi = 0.0; } else if (r < prj->w[5]) { xi = r*prj->w[6]; } else { - /* Find a solution interval. */ - x1 = x2 = 1.0; - r1 = r2 = 0.0; + // Find a solution interval. + double x1 = 1.0; + double x2 = 1.0; + double r1 = 0.0; + double r2 = 0.0; + + int k; for (k = 0; k < 30; k++) { x2 = x1/2.0; - tanxi = sqrt(1.0-x2*x2)/x2; + double tanxi = sqrt(1.0-x2*x2)/x2; r2 = -(log(x2)/tanxi + prj->w[1]*tanxi); if (r2 >= r) break; @@ -2805,14 +3018,15 @@ int stat[]; } if (k == 30) { *thetap = 0.0; - *(statp++) = 1; + *statp = 1; if (!status) status = PRJERR_BAD_PIX_SET("airx2s"); continue; } + double cosxi; for (k = 0; k < 100; k++) { - /* Weighted division of the interval. */ - lambda = (r2-r)/(r2-r1); + // Weighted division of the interval. + double lambda = (r2-r)/(r2-r1); if (lambda < 0.1) { lambda = 0.1; } else if (lambda > 0.9) { @@ -2820,8 +3034,8 @@ int stat[]; } cosxi = x2 - lambda*(x2-x1); - tanxi = sqrt(1.0-cosxi*cosxi)/cosxi; - rt = -(log(cosxi)/tanxi + prj->w[1]*tanxi); + double tanxi = sqrt(1.0-cosxi*cosxi)/cosxi; + double rt = -(log(cosxi)/tanxi + prj->w[1]*tanxi); if (rt < r) { if (r-rt < tol) break; @@ -2835,7 +3049,7 @@ int stat[]; } if (k == 100) { *thetap = 0.0; - *(statp++) = 1; + *statp = 1; if (!status) status = PRJERR_BAD_PIX_SET("airx2s"); continue; } @@ -2844,37 +3058,43 @@ int stat[]; } *thetap = 90.0 - 2.0*xi; - *(statp++) = 0; + *statp = 0; } } + + // Do bounds checking on the native coordinates. + if (prj->bounds&4 && prjbchk(1.0e-13, nx, my, spt, phi, theta, stat)) { + if (!status) status = PRJERR_BAD_PIX_SET("airx2s"); + } + return status; } -/*--------------------------------------------------------------------------*/ - -int airs2x(prj, nphi, ntheta, spt, sxy, phi, theta, x, y, stat) +//---------------------------------------------------------------------------- -struct prjprm *prj; -int nphi, ntheta, spt, sxy; -const double phi[], theta[]; -double x[], y[]; -int stat[]; +int airs2x( + struct prjprm *prj, + int nphi, + int ntheta, + int spt, + int sxy, + const double phi[], + const double theta[], + double x[], + double y[], + int stat[]) { - int mphi, mtheta, rowlen, rowoff, status; - double cosphi, cosxi, r, tanxi, xi, sinphi; - register int iphi, itheta, istat, *statp; - register const double *phip, *thetap; - register double *xp, *yp; - - - /* Initialize. */ + // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; - if (prj->flag != AIR) { + + int status; + if (abs(prj->flag) != AIR) { if ((status = airset(prj))) return status; } + int mphi, mtheta; if (ntheta > 0) { mphi = nphi; mtheta = ntheta; @@ -2887,16 +3107,17 @@ int stat[]; status = 0; - /* Do phi dependence. */ - phip = phi; - rowoff = 0; - rowlen = nphi*sxy; - for (iphi = 0; iphi < nphi; iphi++, rowoff += sxy, phip += spt) { + // Do phi dependence. + const double *phip = phi; + int rowoff = 0; + int rowlen = nphi*sxy; + for (int iphi = 0; iphi < nphi; iphi++, rowoff += sxy, phip += spt) { + double sinphi, cosphi; sincosd(*phip, &sinphi, &cosphi); - xp = x + rowoff; - yp = y + rowoff; - for (itheta = 0; itheta < mtheta; itheta++) { + double *xp = x + rowoff; + double *yp = y + rowoff; + for (int itheta = 0; itheta < mtheta; itheta++) { *xp = sinphi; *yp = cosphi; xp += rowlen; @@ -2905,23 +3126,24 @@ int stat[]; } - /* Do theta dependence. */ - thetap = theta; - xp = x; - yp = y; - statp = stat; - for (itheta = 0; itheta < ntheta; itheta++, thetap += spt) { - istat = 0; + // Do theta dependence. + const double *thetap = theta; + double *xp = x; + double *yp = y; + int *statp = stat; + for (int itheta = 0; itheta < ntheta; itheta++, thetap += spt) { + int istat = 0; + double r; if (*thetap == 90.0) { r = 0.0; } else if (*thetap > -90.0) { - xi = D2R*(90.0 - *thetap)/2.0; + double xi = D2R*(90.0 - *thetap)/2.0; if (xi < prj->w[4]) { r = xi*prj->w[3]; } else { - cosxi = cosd((90.0 - *thetap)/2.0); - tanxi = sqrt(1.0-cosxi*cosxi)/cosxi; + double cosxi = cosd((90.0 - *thetap)/2.0); + double tanxi = sqrt(1.0 - cosxi*cosxi)/cosxi; r = -prj->w[0]*(log(cosxi)/tanxi + prj->w[1]*tanxi); } } else { @@ -2930,10 +3152,10 @@ int stat[]; if (!status) status = PRJERR_BAD_WORLD_SET("airs2x"); } - for (iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy) { + for (int iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy, statp++) { *xp = r*(*xp) - prj->x0; *yp = -r*(*yp) - prj->y0; - *(statp++) = istat; + *statp = istat; } } @@ -2967,14 +3189,12 @@ int stat[]; * prj->prjs2x Pointer to cyps2x(). *===========================================================================*/ -int cypset(prj) - -struct prjprm *prj; +int cypset(struct prjprm *prj) { if (prj == 0x0) return PRJERR_NULL_POINTER; + if (prj->flag == -CYP) return 0; - prj->flag = CYP; strcpy(prj->code, "CYP"); if (undefined(prj->pv[1])) prj->pv[1] = 1.0; @@ -3024,33 +3244,35 @@ struct prjprm *prj; prj->prjx2s = cypx2s; prj->prjs2x = cyps2x; + prj->flag = (prj->flag == 1) ? -CYP : CYP; + return prjoff(prj, 0.0, 0.0); } -/*--------------------------------------------------------------------------*/ - -int cypx2s(prj, nx, ny, sxy, spt, x, y, phi, theta, stat) +//---------------------------------------------------------------------------- -struct prjprm *prj; -int nx, ny, sxy, spt; -const double x[], y[]; -double phi[], theta[]; -int stat[]; +int cypx2s( + struct prjprm *prj, + int nx, + int ny, + int sxy, + int spt, + const double x[], + const double y[], + double phi[], + double theta[], + int stat[]) { - int mx, my, rowlen, rowoff, status; - double eta, s, t; - register int ix, iy, *statp; - register const double *xp, *yp; - register double *phip, *thetap; - - - /* Initialize. */ + // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; - if (prj->flag != CYP) { + + int status; + if (abs(prj->flag) != CYP) { if ((status = cypset(prj))) return status; } + int mx, my; if (ny > 0) { mx = nx; my = ny; @@ -3060,63 +3282,71 @@ int stat[]; ny = nx; } + status = 0; - /* Do x dependence. */ - xp = x; - rowoff = 0; - rowlen = nx*spt; - for (ix = 0; ix < nx; ix++, rowoff += spt, xp += sxy) { - s = prj->w[1]*(*xp + prj->x0); - phip = phi + rowoff; - for (iy = 0; iy < my; iy++) { + // Do x dependence. + const double *xp = x; + int rowoff = 0; + int rowlen = nx*spt; + for (int ix = 0; ix < nx; ix++, rowoff += spt, xp += sxy) { + double s = prj->w[1]*(*xp + prj->x0); + + double *phip = phi + rowoff; + for (int iy = 0; iy < my; iy++) { *phip = s; phip += rowlen; } } - /* Do y dependence. */ - yp = y; - thetap = theta; - statp = stat; - for (iy = 0; iy < ny; iy++, yp += sxy) { - eta = prj->w[3]*(*yp + prj->y0); - t = atan2d(eta,1.0) + asind(eta*prj->pv[1]/sqrt(eta*eta+1.0)); + // Do y dependence. + const double *yp = y; + double *thetap = theta; + int *statp = stat; + for (int iy = 0; iy < ny; iy++, yp += sxy) { + double eta = prj->w[3]*(*yp + prj->y0); + double t = atan2d(eta,1.0) + asind(eta*prj->pv[1]/sqrt(eta*eta+1.0)); - for (ix = 0; ix < mx; ix++, thetap += spt) { + for (int ix = 0; ix < mx; ix++, thetap += spt, statp++) { *thetap = t; - *(statp++) = 0; + *statp = 0; } } - return 0; -} -/*--------------------------------------------------------------------------*/ - -int cyps2x(prj, nphi, ntheta, spt, sxy, phi, theta, x, y, stat) + // Do bounds checking on the native coordinates. + if (prj->bounds&4 && prjbchk(1.0e-13, nx, my, spt, phi, theta, stat)) { + if (!status) status = PRJERR_BAD_PIX_SET("cypx2s"); + } -struct prjprm *prj; -int nphi, ntheta, spt, sxy; -const double phi[], theta[]; -double x[], y[]; -int stat[]; + return status; +} -{ - int mphi, mtheta, rowlen, rowoff, status; - double eta, xi; - register int iphi, itheta, istat, *statp; - register const double *phip, *thetap; - register double *xp, *yp; +//---------------------------------------------------------------------------- +int cyps2x( + struct prjprm *prj, + int nphi, + int ntheta, + int spt, + int sxy, + const double phi[], + const double theta[], + double x[], + double y[], + int stat[]) - /* Initialize. */ +{ + // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; - if (prj->flag != CYP) { + + int status; + if (abs(prj->flag) != CYP) { if ((status = cypset(prj))) return status; } + int mphi, mtheta; if (ntheta > 0) { mphi = nphi; mtheta = ntheta; @@ -3129,29 +3359,29 @@ int stat[]; status = 0; - /* Do phi dependence. */ - phip = phi; - rowoff = 0; - rowlen = nphi*sxy; - for (iphi = 0; iphi < nphi; iphi++, rowoff += sxy, phip += spt) { - xi = prj->w[0]*(*phip) - prj->x0; + // Do phi dependence. + const double *phip = phi; + int rowoff = 0; + int rowlen = nphi*sxy; + for (int iphi = 0; iphi < nphi; iphi++, rowoff += sxy, phip += spt) { + double xi = prj->w[0]*(*phip) - prj->x0; - xp = x + rowoff; - for (itheta = 0; itheta < mtheta; itheta++) { + double *xp = x + rowoff; + for (int itheta = 0; itheta < mtheta; itheta++) { *xp = xi; xp += rowlen; } } - /* Do theta dependence. */ - thetap = theta; - yp = y; - statp = stat; - for (itheta = 0; itheta < ntheta; itheta++, thetap += spt) { - eta = prj->pv[1] + cosd(*thetap); + // Do theta dependence. + const double *thetap = theta; + double *yp = y; + int *statp = stat; + for (int itheta = 0; itheta < ntheta; itheta++, thetap += spt) { + double eta = prj->pv[1] + cosd(*thetap); - istat = 0; + int istat = 0; if (eta == 0.0) { istat = 1; if (!status) status = PRJERR_BAD_WORLD_SET("cyps2x"); @@ -3161,9 +3391,9 @@ int stat[]; } eta -= prj->y0; - for (iphi = 0; iphi < mphi; iphi++, yp += sxy) { + for (int iphi = 0; iphi < mphi; iphi++, yp += sxy, statp++) { *yp = eta; - *(statp++) = istat; + *statp = istat; } } @@ -3195,14 +3425,12 @@ int stat[]; * prj->prjs2x Pointer to ceas2x(). *===========================================================================*/ -int ceaset(prj) - -struct prjprm *prj; +int ceaset(struct prjprm *prj) { if (prj == 0x0) return PRJERR_NULL_POINTER; + if (prj->flag == -CEA) return 0; - prj->flag = CEA; strcpy(prj->code, "CEA"); if (undefined(prj->pv[1])) prj->pv[1] = 1.0; @@ -3238,34 +3466,37 @@ struct prjprm *prj; prj->prjx2s = ceax2s; prj->prjs2x = ceas2x; + prj->flag = (prj->flag == 1) ? -CEA : CEA; + return prjoff(prj, 0.0, 0.0); } -/*--------------------------------------------------------------------------*/ - -int ceax2s(prj, nx, ny, sxy, spt, x, y, phi, theta, stat) +//---------------------------------------------------------------------------- -struct prjprm *prj; -int nx, ny, sxy, spt; -const double x[], y[]; -double phi[], theta[]; -int stat[]; +int ceax2s( + struct prjprm *prj, + int nx, + int ny, + int sxy, + int spt, + const double x[], + const double y[], + double phi[], + double theta[], + int stat[]) { - int mx, my, rowlen, rowoff, status; - double s; const double tol = 1.0e-13; - register int istat, ix, iy, *statp; - register const double *xp, *yp; - register double *phip, *thetap; - - /* Initialize. */ + // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; - if (prj->flag != CEA) { + + int status; + if (abs(prj->flag) != CEA) { if ((status = ceaset(prj))) return status; } + int mx, my; if (ny > 0) { mx = nx; my = ny; @@ -3278,29 +3509,29 @@ int stat[]; status = 0; - /* Do x dependence. */ - xp = x; - rowoff = 0; - rowlen = nx*spt; - for (ix = 0; ix < nx; ix++, rowoff += spt, xp += sxy) { - s = prj->w[1]*(*xp + prj->x0); + // Do x dependence. + const double *xp = x; + int rowoff = 0; + int rowlen = nx*spt; + for (int ix = 0; ix < nx; ix++, rowoff += spt, xp += sxy) { + double s = prj->w[1]*(*xp + prj->x0); - phip = phi + rowoff; - for (iy = 0; iy < my; iy++) { + double *phip = phi + rowoff; + for (int iy = 0; iy < my; iy++) { *phip = s; phip += rowlen; } } - /* Do y dependence. */ - yp = y; - thetap = theta; - statp = stat; - for (iy = 0; iy < ny; iy++, yp += sxy) { - s = prj->w[3]*(*yp + prj->y0); + // Do y dependence. + const double *yp = y; + double *thetap = theta; + int *statp = stat; + for (int iy = 0; iy < ny; iy++, yp += sxy) { + double s = prj->w[3]*(*yp + prj->y0); - istat = 0; + int istat = 0; if (fabs(s) > 1.0) { if (fabs(s) > 1.0+tol) { s = 0.0; @@ -3313,39 +3544,45 @@ int stat[]; s = asind(s); } - for (ix = 0; ix < mx; ix++, thetap += spt) { + for (int ix = 0; ix < mx; ix++, thetap += spt, statp++) { *thetap = s; - *(statp++) = istat; + *statp = istat; } } + + // Do bounds checking on the native coordinates. + if (prj->bounds&4 && prjbchk(1.0e-13, nx, my, spt, phi, theta, stat)) { + if (!status) status = PRJERR_BAD_PIX_SET("ceax2s"); + } + return status; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- -int ceas2x(prj, nphi, ntheta, spt, sxy, phi, theta, x, y, stat) - -struct prjprm *prj; -int nphi, ntheta, spt, sxy; -const double phi[], theta[]; -double x[], y[]; -int stat[]; +int ceas2x( + struct prjprm *prj, + int nphi, + int ntheta, + int spt, + int sxy, + const double phi[], + const double theta[], + double x[], + double y[], + int stat[]) { - int mphi, mtheta, rowlen, rowoff, status; - double eta, xi; - register int iphi, itheta, *statp; - register const double *phip, *thetap; - register double *xp, *yp; - - - /* Initialize. */ + // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; - if (prj->flag != CEA) { + + int status; + if (abs(prj->flag) != CEA) { if ((status = ceaset(prj))) return status; } + int mphi, mtheta; if (ntheta > 0) { mphi = nphi; mtheta = ntheta; @@ -3356,31 +3593,31 @@ int stat[]; } - /* Do phi dependence. */ - phip = phi; - rowoff = 0; - rowlen = nphi*sxy; - for (iphi = 0; iphi < nphi; iphi++, rowoff += sxy, phip += spt) { - xi = prj->w[0]*(*phip) - prj->x0; + // Do phi dependence. + const double *phip = phi; + int rowoff = 0; + int rowlen = nphi*sxy; + for (int iphi = 0; iphi < nphi; iphi++, rowoff += sxy, phip += spt) { + double xi = prj->w[0]*(*phip) - prj->x0; - xp = x + rowoff; - for (itheta = 0; itheta < mtheta; itheta++) { + double *xp = x + rowoff; + for (int itheta = 0; itheta < mtheta; itheta++) { *xp = xi; xp += rowlen; } } - /* Do theta dependence. */ - thetap = theta; - yp = y; - statp = stat; - for (itheta = 0; itheta < ntheta; itheta++, thetap += spt) { - eta = prj->w[2]*sind(*thetap) - prj->y0; + // Do theta dependence. + const double *thetap = theta; + double *yp = y; + int *statp = stat; + for (int itheta = 0; itheta < ntheta; itheta++, thetap += spt) { + double eta = prj->w[2]*sind(*thetap) - prj->y0; - for (iphi = 0; iphi < mphi; iphi++, yp += sxy) { + for (int iphi = 0; iphi < mphi; iphi++, yp += sxy, statp++) { *yp = eta; - *(statp++) = 0; + *statp = 0; } } @@ -3406,14 +3643,12 @@ int stat[]; * prj->prjs2x Pointer to cars2x(). *===========================================================================*/ -int carset(prj) - -struct prjprm *prj; +int carset(struct prjprm *prj) { if (prj == 0x0) return PRJERR_NULL_POINTER; + if (prj->flag == -CAR) return 0; - prj->flag = CAR; strcpy(prj->code, "CAR"); strcpy(prj->name, "plate caree"); @@ -3437,33 +3672,35 @@ struct prjprm *prj; prj->prjx2s = carx2s; prj->prjs2x = cars2x; + prj->flag = (prj->flag == 1) ? -CAR : CAR; + return prjoff(prj, 0.0, 0.0); } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- -int carx2s(prj, nx, ny, sxy, spt, x, y, phi, theta, stat) - -struct prjprm *prj; -int nx, ny, sxy, spt; -const double x[], y[]; -double phi[], theta[]; -int stat[]; +int carx2s( + struct prjprm *prj, + int nx, + int ny, + int sxy, + int spt, + const double x[], + const double y[], + double phi[], + double theta[], + int stat[]) { - int mx, my, rowlen, rowoff, status; - double s, t; - register int ix, iy, *statp; - register const double *xp, *yp; - register double *phip, *thetap; - - - /* Initialize. */ + // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; - if (prj->flag != CAR) { + + int status; + if (abs(prj->flag) != CAR) { if ((status = carset(prj))) return status; } + int mx, my; if (ny > 0) { mx = nx; my = ny; @@ -3473,62 +3710,70 @@ int stat[]; ny = nx; } + status = 0; + - /* Do x dependence. */ - xp = x; - rowoff = 0; - rowlen = nx*spt; - for (ix = 0; ix < nx; ix++, rowoff += spt, xp += sxy) { - s = prj->w[1]*(*xp + prj->x0); + // Do x dependence. + const double *xp = x; + int rowoff = 0; + int rowlen = nx*spt; + for (int ix = 0; ix < nx; ix++, rowoff += spt, xp += sxy) { + double s = prj->w[1]*(*xp + prj->x0); - phip = phi + rowoff; - for (iy = 0; iy < my; iy++) { + double *phip = phi + rowoff; + for (int iy = 0; iy < my; iy++) { *phip = s; phip += rowlen; } } - /* Do y dependence. */ - yp = y; - thetap = theta; - statp = stat; - for (iy = 0; iy < ny; iy++, yp += sxy) { - t = prj->w[1]*(*yp + prj->y0); + // Do y dependence. + const double *yp = y; + double *thetap = theta; + int *statp = stat; + for (int iy = 0; iy < ny; iy++, yp += sxy) { + double t = prj->w[1]*(*yp + prj->y0); - for (ix = 0; ix < mx; ix++, thetap += spt) { + for (int ix = 0; ix < mx; ix++, thetap += spt, statp++) { *thetap = t; - *(statp++) = 0; + *statp = 0; } } - return 0; -} - -/*--------------------------------------------------------------------------*/ -int cars2x(prj, nphi, ntheta, spt, sxy, phi, theta, x, y, stat) + // Do bounds checking on the native coordinates. + if (prj->bounds&4 && prjbchk(1.0e-13, nx, my, spt, phi, theta, stat)) { + if (!status) status = PRJERR_BAD_PIX_SET("carx2s"); + } -struct prjprm *prj; -int nphi, ntheta, spt, sxy; -const double phi[], theta[]; -double x[], y[]; -int stat[]; + return status; +} -{ - int mphi, mtheta, rowlen, rowoff, status; - double eta, xi; - register int iphi, itheta, *statp; - register const double *phip, *thetap; - register double *xp, *yp; +//---------------------------------------------------------------------------- +int cars2x( + struct prjprm *prj, + int nphi, + int ntheta, + int spt, + int sxy, + const double phi[], + const double theta[], + double x[], + double y[], + int stat[]) - /* Initialize. */ +{ + // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; - if (prj->flag != CAR) { + + int status; + if (abs(prj->flag) != CAR) { if ((status = carset(prj))) return status; } + int mphi, mtheta; if (ntheta > 0) { mphi = nphi; mtheta = ntheta; @@ -3539,31 +3784,31 @@ int stat[]; } - /* Do phi dependence. */ - phip = phi; - rowoff = 0; - rowlen = nphi*sxy; - for (iphi = 0; iphi < nphi; iphi++, rowoff += sxy, phip += spt) { - xi = prj->w[0]*(*phip) - prj->x0; + // Do phi dependence. + const double *phip = phi; + int rowoff = 0; + int rowlen = nphi*sxy; + for (int iphi = 0; iphi < nphi; iphi++, rowoff += sxy, phip += spt) { + double xi = prj->w[0]*(*phip) - prj->x0; - xp = x + rowoff; - for (itheta = 0; itheta < mtheta; itheta++) { + double *xp = x + rowoff; + for (int itheta = 0; itheta < mtheta; itheta++) { *xp = xi; xp += rowlen; } } - /* Do theta dependence. */ - thetap = theta; - yp = y; - statp = stat; - for (itheta = 0; itheta < ntheta; itheta++, thetap += spt) { - eta = prj->w[0]*(*thetap) - prj->y0; + // Do theta dependence. + const double *thetap = theta; + double *yp = y; + int *statp = stat; + for (int itheta = 0; itheta < ntheta; itheta++, thetap += spt) { + double eta = prj->w[0]*(*thetap) - prj->y0; - for (iphi = 0; iphi < mphi; iphi++, yp += sxy) { + for (int iphi = 0; iphi < mphi; iphi++, yp += sxy, statp++) { *yp = eta; - *(statp++) = 0; + *statp = 0; } } @@ -3589,14 +3834,12 @@ int stat[]; * prj->prjs2x Pointer to mers2x(). *===========================================================================*/ -int merset(prj) - -struct prjprm *prj; +int merset(struct prjprm *prj) { if (prj == 0x0) return PRJERR_NULL_POINTER; + if (prj->flag == -MER) return 0; - prj->flag = MER; strcpy(prj->code, "MER"); strcpy(prj->name, "Mercator's"); @@ -3620,33 +3863,35 @@ struct prjprm *prj; prj->prjx2s = merx2s; prj->prjs2x = mers2x; + prj->flag = (prj->flag == 1) ? -MER : MER; + return prjoff(prj, 0.0, 0.0); } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- -int merx2s(prj, nx, ny, sxy, spt, x, y, phi, theta, stat) - -struct prjprm *prj; -int nx, ny, sxy, spt; -const double x[], y[]; -double phi[], theta[]; -int stat[]; +int merx2s( + struct prjprm *prj, + int nx, + int ny, + int sxy, + int spt, + const double x[], + const double y[], + double phi[], + double theta[], + int stat[]) { - int mx, my, rowlen, rowoff, status; - double s, t; - register int ix, iy, *statp; - register const double *xp, *yp; - register double *phip, *thetap; - - - /* Initialize. */ + // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; - if (prj->flag != MER) { + + int status; + if (abs(prj->flag) != MER) { if ((status = merset(prj))) return status; } + int mx, my; if (ny > 0) { mx = nx; my = ny; @@ -3656,62 +3901,70 @@ int stat[]; ny = nx; } + status = 0; + - /* Do x dependence. */ - xp = x; - rowoff = 0; - rowlen = nx*spt; - for (ix = 0; ix < nx; ix++, rowoff += spt, xp += sxy) { - s = prj->w[1]*(*xp + prj->x0); + // Do x dependence. + const double *xp = x; + int rowoff = 0; + int rowlen = nx*spt; + for (int ix = 0; ix < nx; ix++, rowoff += spt, xp += sxy) { + double s = prj->w[1]*(*xp + prj->x0); - phip = phi + rowoff; - for (iy = 0; iy < my; iy++) { + double *phip = phi + rowoff; + for (int iy = 0; iy < my; iy++) { *phip = s; phip += rowlen; } } - /* Do y dependence. */ - yp = y; - thetap = theta; - statp = stat; - for (iy = 0; iy < ny; iy++, yp += sxy) { - t = 2.0*atand(exp((*yp + prj->y0)/prj->r0)) - 90.0; + // Do y dependence. + const double *yp = y; + double *thetap = theta; + int *statp = stat; + for (int iy = 0; iy < ny; iy++, yp += sxy) { + double t = 2.0*atand(exp((*yp + prj->y0)/prj->r0)) - 90.0; - for (ix = 0; ix < mx; ix++, thetap += spt) { + for (int ix = 0; ix < mx; ix++, thetap += spt, statp++) { *thetap = t; - *(statp++) = 0; + *statp = 0; } } - return 0; -} - -/*--------------------------------------------------------------------------*/ -int mers2x(prj, nphi, ntheta, spt, sxy, phi, theta, x, y, stat) + // Do bounds checking on the native coordinates. + if (prj->bounds&4 && prjbchk(1.0e-13, nx, my, spt, phi, theta, stat)) { + if (!status) status = PRJERR_BAD_PIX_SET("merx2s"); + } -struct prjprm *prj; -int nphi, ntheta, spt, sxy; -const double phi[], theta[]; -double x[], y[]; -int stat[]; + return status; +} -{ - int mphi, mtheta, rowlen, rowoff, status; - double eta, xi; - register int iphi, itheta, istat, *statp; - register const double *phip, *thetap; - register double *xp, *yp; +//---------------------------------------------------------------------------- +int mers2x( + struct prjprm *prj, + int nphi, + int ntheta, + int spt, + int sxy, + const double phi[], + const double theta[], + double x[], + double y[], + int stat[]) - /* Initialize. */ +{ + // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; - if (prj->flag != MER) { + + int status; + if (abs(prj->flag) != MER) { if ((status = merset(prj))) return status; } + int mphi, mtheta; if (ntheta > 0) { mphi = nphi; mtheta = ntheta; @@ -3724,28 +3977,29 @@ int stat[]; status = 0; - /* Do phi dependence. */ - phip = phi; - rowoff = 0; - rowlen = nphi*sxy; - for (iphi = 0; iphi < nphi; iphi++, rowoff += sxy, phip += spt) { - xi = prj->w[0]*(*phip) - prj->x0; + // Do phi dependence. + const double *phip = phi; + int rowoff = 0; + int rowlen = nphi*sxy; + for (int iphi = 0; iphi < nphi; iphi++, rowoff += sxy, phip += spt) { + double xi = prj->w[0]*(*phip) - prj->x0; - xp = x + rowoff; - for (itheta = 0; itheta < mtheta; itheta++) { + double *xp = x + rowoff; + for (int itheta = 0; itheta < mtheta; itheta++) { *xp = xi; xp += rowlen; } } - /* Do theta dependence. */ - thetap = theta; - yp = y; - statp = stat; - for (itheta = 0; itheta < ntheta; itheta++, thetap += spt) { - istat = 0; + // Do theta dependence. + const double *thetap = theta; + double *yp = y; + int *statp = stat; + for (int itheta = 0; itheta < ntheta; itheta++, thetap += spt) { + int istat = 0; + double eta; if (*thetap <= -90.0 || *thetap >= 90.0) { eta = 0.0; istat = 1; @@ -3754,9 +4008,9 @@ int stat[]; eta = prj->r0*log(tand((*thetap+90.0)/2.0)) - prj->y0; } - for (iphi = 0; iphi < mphi; iphi++, yp += sxy) { + for (int iphi = 0; iphi < mphi; iphi++, yp += sxy, statp++) { *yp = eta; - *(statp++) = istat; + *statp = istat; } } @@ -3782,14 +4036,12 @@ int stat[]; * prj->prjs2x Pointer to sfls2x(). *===========================================================================*/ -int sflset(prj) - -struct prjprm *prj; +int sflset(struct prjprm *prj) { if (prj == 0x0) return PRJERR_NULL_POINTER; + if (prj->flag == -SFL) return 0; - prj->flag = SFL; strcpy(prj->code, "SFL"); strcpy(prj->name, "Sanson-Flamsteed"); @@ -3813,33 +4065,35 @@ struct prjprm *prj; prj->prjx2s = sflx2s; prj->prjs2x = sfls2x; + prj->flag = (prj->flag == 1) ? -SFL : SFL; + return prjoff(prj, 0.0, 0.0); } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- -int sflx2s(prj, nx, ny, sxy, spt, x, y, phi, theta, stat) - -struct prjprm *prj; -int nx, ny, sxy, spt; -const double x[], y[]; -double phi[], theta[]; -int stat[]; +int sflx2s( + struct prjprm *prj, + int nx, + int ny, + int sxy, + int spt, + const double x[], + const double y[], + double phi[], + double theta[], + int stat[]) { - int mx, my, rowlen, rowoff, status; - double s, t, yj; - register int istat, ix, iy, *statp; - register const double *xp, *yp; - register double *phip, *thetap; - - - /* Initialize. */ + // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; - if (prj->flag != SFL) { + + int status; + if (abs(prj->flag) != SFL) { if ((status = sflset(prj))) return status; } + int mx, my; if (ny > 0) { mx = nx; my = ny; @@ -3852,31 +4106,31 @@ int stat[]; status = 0; - /* Do x dependence. */ - xp = x; - rowoff = 0; - rowlen = nx*spt; - for (ix = 0; ix < nx; ix++, rowoff += spt, xp += sxy) { - s = prj->w[1]*(*xp + prj->x0); + // Do x dependence. + const double *xp = x; + int rowoff = 0; + int rowlen = nx*spt; + for (int ix = 0; ix < nx; ix++, rowoff += spt, xp += sxy) { + double s = prj->w[1]*(*xp + prj->x0); - phip = phi + rowoff; - for (iy = 0; iy < my; iy++) { + double *phip = phi + rowoff; + for (int iy = 0; iy < my; iy++) { *phip = s; phip += rowlen; } } - /* Do y dependence. */ - yp = y; - phip = phi; - thetap = theta; - statp = stat; - for (iy = 0; iy < ny; iy++, yp += sxy) { - yj = *yp + prj->y0; - s = cos(yj/prj->r0); + // Do y dependence. + const double *yp = y; + double *phip = phi; + double *thetap = theta; + int *statp = stat; + for (int iy = 0; iy < ny; iy++, yp += sxy) { + double yj = *yp + prj->y0; + double s = cos(yj/prj->r0); - istat = 0; + int istat = 0; if (s == 0.0) { istat = 1; if (!status) status = PRJERR_BAD_PIX_SET("sflx2s"); @@ -3884,42 +4138,48 @@ int stat[]; s = 1.0/s; } - t = prj->w[1]*yj; + double t = prj->w[1]*yj; - for (ix = 0; ix < mx; ix++, phip += spt, thetap += spt) { + for (int ix = 0; ix < mx; ix++, phip += spt, thetap += spt, statp++) { *phip *= s; *thetap = t; - *(statp++) = istat; + *statp = istat; } } + + // Do bounds checking on the native coordinates. + if (prj->bounds&4 && prjbchk(1.0e-12, nx, my, spt, phi, theta, stat)) { + if (!status) status = PRJERR_BAD_PIX_SET("sflx2s"); + } + return status; } -/*--------------------------------------------------------------------------*/ - -int sfls2x(prj, nphi, ntheta, spt, sxy, phi, theta, x, y, stat) +//---------------------------------------------------------------------------- -struct prjprm *prj; -int nphi, ntheta, spt, sxy; -const double phi[], theta[]; -double x[], y[]; -int stat[]; +int sfls2x( + struct prjprm *prj, + int nphi, + int ntheta, + int spt, + int sxy, + const double phi[], + const double theta[], + double x[], + double y[], + int stat[]) { - int mphi, mtheta, rowlen, rowoff, status; - double eta, xi; - register int iphi, itheta, *statp; - register const double *phip, *thetap; - register double *xp, *yp; - - - /* Initialize. */ + // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; - if (prj->flag != SFL) { + + int status; + if (abs(prj->flag) != SFL) { if ((status = sflset(prj))) return status; } + int mphi, mtheta; if (ntheta > 0) { mphi = nphi; mtheta = ntheta; @@ -3930,34 +4190,34 @@ int stat[]; } - /* Do phi dependence. */ - phip = phi; - rowoff = 0; - rowlen = nphi*sxy; - for (iphi = 0; iphi < nphi; iphi++, rowoff += sxy, phip += spt) { - xi = prj->w[0]*(*phip); + // Do phi dependence. + const double *phip = phi; + int rowoff = 0; + int rowlen = nphi*sxy; + for (int iphi = 0; iphi < nphi; iphi++, rowoff += sxy, phip += spt) { + double xi = prj->w[0]*(*phip); - xp = x + rowoff; - for (itheta = 0; itheta < mtheta; itheta++) { + double *xp = x + rowoff; + for (int itheta = 0; itheta < mtheta; itheta++) { *xp = xi; xp += rowlen; } } - /* Do theta dependence. */ - thetap = theta; - xp = x; - yp = y; - statp = stat; - for (itheta = 0; itheta < ntheta; itheta++, thetap += spt) { - xi = cosd(*thetap); - eta = prj->w[0]*(*thetap) - prj->y0; - - for (iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy) { + // Do theta dependence. + const double *thetap = theta; + double *xp = x; + double *yp = y; + int *statp = stat; + for (int itheta = 0; itheta < ntheta; itheta++, thetap += spt) { + double xi = cosd(*thetap); + double eta = prj->w[0]*(*thetap) - prj->y0; + + for (int iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy, statp++) { *xp = xi*(*xp) - prj->x0; *yp = eta; - *(statp++) = 0; + *statp = 0; } } @@ -3985,14 +4245,12 @@ int stat[]; * prj->prjs2x Pointer to pars2x(). *===========================================================================*/ -int parset(prj) - -struct prjprm *prj; +int parset(struct prjprm *prj) { if (prj == 0x0) return PRJERR_NULL_POINTER; + if (prj->flag == -PAR) return 0; - prj->flag = PAR; strcpy(prj->code, "PAR"); strcpy(prj->name, "parabolic"); @@ -4020,34 +4278,37 @@ struct prjprm *prj; prj->prjx2s = parx2s; prj->prjs2x = pars2x; + prj->flag = (prj->flag == 1) ? -PAR : PAR; + return prjoff(prj, 0.0, 0.0); } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- -int parx2s(prj, nx, ny, sxy, spt, x, y, phi, theta, stat) - -struct prjprm *prj; -int nx, ny, sxy, spt; -const double x[], y[]; -double phi[], theta[]; -int stat[]; +int parx2s( + struct prjprm *prj, + int nx, + int ny, + int sxy, + int spt, + const double x[], + const double y[], + double phi[], + double theta[], + int stat[]) { - int mx, my, rowlen, rowoff, status; - double r, s, t, xj; const double tol = 1.0e-13; - register int istat, ix, iy, *statp; - register const double *xp, *yp; - register double *phip, *thetap; - - /* Initialize. */ + // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; - if (prj->flag != PAR) { + + int status; + if (abs(prj->flag) != PAR) { if ((status = parset(prj))) return status; } + int mx, my; if (ny > 0) { mx = nx; my = ny; @@ -4060,18 +4321,18 @@ int stat[]; status = 0; - /* Do x dependence. */ - xp = x; - rowoff = 0; - rowlen = nx*spt; - for (ix = 0; ix < nx; ix++, rowoff += spt, xp += sxy) { - xj = *xp + prj->x0; - s = prj->w[1]*xj; - t = fabs(xj) - tol; + // Do x dependence. + const double *xp = x; + int rowoff = 0; + int rowlen = nx*spt; + for (int ix = 0; ix < nx; ix++, rowoff += spt, xp += sxy) { + double xj = *xp + prj->x0; + double s = prj->w[1]*xj; + double t = fabs(xj) - tol; - phip = phi + rowoff; - thetap = theta + rowoff; - for (iy = 0; iy < my; iy++) { + double *phip = phi + rowoff; + double *thetap = theta + rowoff; + for (int iy = 0; iy < my; iy++) { *phip = s; *thetap = t; phip += rowlen; @@ -4080,15 +4341,16 @@ int stat[]; } - /* Do y dependence. */ - yp = y; - phip = phi; - thetap = theta; - statp = stat; - for (iy = 0; iy < ny; iy++, yp += sxy) { - r = prj->w[3]*(*yp + prj->y0); + // Do y dependence. + const double *yp = y; + double *phip = phi; + double *thetap = theta; + int *statp = stat; + for (int iy = 0; iy < ny; iy++, yp += sxy) { + double r = prj->w[3]*(*yp + prj->y0); - istat = 0; + int istat = 0; + double s, t; if (r > 1.0 || r < -1.0) { s = 0.0; t = 0.0; @@ -4098,7 +4360,7 @@ int stat[]; } else { s = 1.0 - 4.0*r*r; if (s == 0.0) { - /* Deferred test. */ + // Deferred test. istat = -1; } else { s = 1.0/s; @@ -4107,14 +4369,16 @@ int stat[]; t = 3.0*asind(r); } - for (ix = 0; ix < mx; ix++, phip += spt, thetap += spt) { + for (int ix = 0; ix < mx; ix++, phip += spt, thetap += spt, statp++) { if (istat < 0) { if (*thetap < 0.0) { - *(statp++) = 0; + *statp = 0; } else { - *(statp++) = 1; + *statp = 1; if (!status) status = PRJERR_BAD_PIX_SET("parx2s"); } + } else { + *statp = istat; } *phip *= s; @@ -4122,33 +4386,39 @@ int stat[]; } } + + // Do bounds checking on the native coordinates. + if (prj->bounds&4 && prjbchk(1.0e-12, nx, my, spt, phi, theta, stat)) { + if (!status) status = PRJERR_BAD_PIX_SET("parx2s"); + } + return status; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- -int pars2x(prj, nphi, ntheta, spt, sxy, phi, theta, x, y, stat) - -struct prjprm *prj; -int nphi, ntheta, spt, sxy; -const double phi[], theta[]; -double x[], y[]; -int stat[]; +int pars2x( + struct prjprm *prj, + int nphi, + int ntheta, + int spt, + int sxy, + const double phi[], + const double theta[], + double x[], + double y[], + int stat[]) { - int mphi, mtheta, rowlen, rowoff, status; - double eta, s, xi; - register int iphi, itheta, *statp; - register const double *phip, *thetap; - register double *xp, *yp; - - - /* Initialize. */ + // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; - if (prj->flag != PAR) { + + int status; + if (abs(prj->flag) != PAR) { if ((status = parset(prj))) return status; } + int mphi, mtheta; if (ntheta > 0) { mphi = nphi; mtheta = ntheta; @@ -4159,35 +4429,35 @@ int stat[]; } - /* Do phi dependence. */ - phip = phi; - rowoff = 0; - rowlen = nphi*sxy; - for (iphi = 0; iphi < nphi; iphi++, rowoff += sxy, phip += spt) { - xi = prj->w[0]*(*phip); + // Do phi dependence. + const double *phip = phi; + int rowoff = 0; + int rowlen = nphi*sxy; + for (int iphi = 0; iphi < nphi; iphi++, rowoff += sxy, phip += spt) { + double xi = prj->w[0]*(*phip); - xp = x + rowoff; - for (itheta = 0; itheta < mtheta; itheta++) { + double *xp = x + rowoff; + for (int itheta = 0; itheta < mtheta; itheta++) { *xp = xi; xp += rowlen; } } - /* Do theta dependence. */ - thetap = theta; - xp = x; - yp = y; - statp = stat; - for (itheta = 0; itheta < ntheta; itheta++, thetap += spt) { - s = sind((*thetap)/3.0); - xi = (1.0 - 4.0*s*s); - eta = prj->w[2]*s - prj->y0; - - for (iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy) { + // Do theta dependence. + const double *thetap = theta; + double *xp = x; + double *yp = y; + int *statp = stat; + for (int itheta = 0; itheta < ntheta; itheta++, thetap += spt) { + double s = sind((*thetap)/3.0); + double xi = (1.0 - 4.0*s*s); + double eta = prj->w[2]*s - prj->y0; + + for (int iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy, statp++) { *xp = xi*(*xp) - prj->x0; *yp = eta; - *(statp++) = 0; + *statp = 0; } } @@ -4215,14 +4485,12 @@ int stat[]; * prj->prjs2x Pointer to mols2x(). *===========================================================================*/ -int molset(prj) - -struct prjprm *prj; +int molset(struct prjprm *prj) { if (prj == 0x0) return PRJERR_NULL_POINTER; + if (prj->flag == -MOL) return 0; - prj->flag = MOL; strcpy(prj->code, "MOL"); if (prj->r0 == 0.0) prj->r0 = R2D; @@ -4245,34 +4513,37 @@ struct prjprm *prj; prj->prjx2s = molx2s; prj->prjs2x = mols2x; + prj->flag = (prj->flag == 1) ? -MOL : MOL; + return prjoff(prj, 0.0, 0.0); } -/*--------------------------------------------------------------------------*/ - -int molx2s(prj, nx, ny, sxy, spt, x, y, phi, theta, stat) +//---------------------------------------------------------------------------- -struct prjprm *prj; -int nx, ny, sxy, spt; -const double x[], y[]; -double phi[], theta[]; -int stat[]; +int molx2s( + struct prjprm *prj, + int nx, + int ny, + int sxy, + int spt, + const double x[], + const double y[], + double phi[], + double theta[], + int stat[]) { - int mx, my, rowlen, rowoff, status; - double r, s, t, xj, y0, yj, z; const double tol = 1.0e-12; - register int istat, ix, iy, *statp; - register const double *xp, *yp; - register double *phip, *thetap; - - /* Initialize. */ + // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; - if (prj->flag != MOL) { + + int status; + if (abs(prj->flag) != MOL) { if ((status = molset(prj))) return status; } + int mx, my; if (ny > 0) { mx = nx; my = ny; @@ -4285,18 +4556,18 @@ int stat[]; status = 0; - /* Do x dependence. */ - xp = x; - rowoff = 0; - rowlen = nx*spt; - for (ix = 0; ix < nx; ix++, rowoff += spt, xp += sxy) { - xj = *xp + prj->x0; - s = prj->w[3]*xj; - t = fabs(xj) - tol; + // Do x dependence. + const double *xp = x; + int rowoff = 0; + int rowlen = nx*spt; + for (int ix = 0; ix < nx; ix++, rowoff += spt, xp += sxy) { + double xj = *xp + prj->x0; + double s = prj->w[3]*xj; + double t = fabs(xj) - tol; - phip = phi + rowoff; - thetap = theta + rowoff; - for (iy = 0; iy < my; iy++) { + double *phip = phi + rowoff; + double *thetap = theta + rowoff; + for (int iy = 0; iy < my; iy++) { *phip = s; *thetap = t; phip += rowlen; @@ -4305,23 +4576,24 @@ int stat[]; } - /* Do y dependence. */ - yp = y; - phip = phi; - thetap = theta; - statp = stat; - for (iy = 0; iy < ny; iy++, yp += sxy) { - yj = *yp + prj->y0; - y0 = yj/prj->r0; - r = 2.0 - y0*y0; + // Do y dependence. + const double *yp = y; + double *phip = phi; + double *thetap = theta; + int *statp = stat; + for (int iy = 0; iy < ny; iy++, yp += sxy) { + double yj = *yp + prj->y0; + double y0 = yj/prj->r0; + double r = 2.0 - y0*y0; - istat = 0; + int istat = 0; + double s; if (r <= tol) { if (r < -tol) { istat = 1; if (!status) status = PRJERR_BAD_PIX_SET("molx2s"); } else { - /* OK if fabs(x) < tol whence phi = 0.0. */ + // OK if fabs(x) < tol whence phi = 0.0. istat = -1; } @@ -4333,7 +4605,7 @@ int stat[]; s = 1.0/r; } - z = yj*prj->w[2]; + double z = yj*prj->w[2]; if (fabs(z) > 1.0) { if (fabs(z) > 1.0+tol) { z = 0.0; @@ -4356,16 +4628,18 @@ int stat[]; } } - t = asind(z); + double t = asind(z); - for (ix = 0; ix < mx; ix++, phip += spt, thetap += spt) { + for (int ix = 0; ix < mx; ix++, phip += spt, thetap += spt, statp++) { if (istat < 0) { if (*thetap < 0.0) { - *(statp++) = 0; + *statp = 0; } else { - *(statp++) = 1; + *statp = 1; if (!status) status = PRJERR_BAD_PIX_SET("molx2s"); } + } else { + *statp = istat; } *phip *= s; @@ -4373,34 +4647,41 @@ int stat[]; } } + + // Do bounds checking on the native coordinates. + if (prj->bounds&4 && prjbchk(1.0e-11, nx, my, spt, phi, theta, stat)) { + if (!status) status = PRJERR_BAD_PIX_SET("molx2s"); + } + return status; } -/*--------------------------------------------------------------------------*/ - -int mols2x(prj, nphi, ntheta, spt, sxy, phi, theta, x, y, stat) +//---------------------------------------------------------------------------- -struct prjprm *prj; -int nphi, ntheta, spt, sxy; -const double phi[], theta[]; -double x[], y[]; -int stat[]; +int mols2x( + struct prjprm *prj, + int nphi, + int ntheta, + int spt, + int sxy, + const double phi[], + const double theta[], + double x[], + double y[], + int stat[]) { - int k, mphi, mtheta, rowlen, rowoff, status; - double eta, gamma, resid, u, v, v0, v1, xi; const double tol = 1.0e-13; - register int iphi, itheta, *statp; - register const double *phip, *thetap; - register double *xp, *yp; - - /* Initialize. */ + // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; - if (prj->flag != MOL) { + + int status; + if (abs(prj->flag) != MOL) { if ((status = molset(prj))) return status; } + int mphi, mtheta; if (ntheta > 0) { mphi = nphi; mtheta = ntheta; @@ -4411,40 +4692,44 @@ int stat[]; } - /* Do phi dependence. */ - phip = phi; - rowoff = 0; - rowlen = nphi*sxy; - for (iphi = 0; iphi < nphi; iphi++, rowoff += sxy, phip += spt) { - xi = prj->w[1]*(*phip); + // Do phi dependence. + const double *phip = phi; + int rowoff = 0; + int rowlen = nphi*sxy; + for (int iphi = 0; iphi < nphi; iphi++, rowoff += sxy, phip += spt) { + double xi = prj->w[1]*(*phip); - xp = x + rowoff; - for (itheta = 0; itheta < mtheta; itheta++) { + double *xp = x + rowoff; + for (int itheta = 0; itheta < mtheta; itheta++) { *xp = xi; xp += rowlen; } } - /* Do theta dependence. */ - thetap = theta; - xp = x; - yp = y; - statp = stat; - for (itheta = 0; itheta < ntheta; itheta++, thetap += spt) { + // Do theta dependence. + const double *thetap = theta; + double *xp = x; + double *yp = y; + int *statp = stat; + for (int itheta = 0; itheta < ntheta; itheta++, thetap += spt) { + double xi, eta; + if (fabs(*thetap) == 90.0) { xi = 0.0; eta = copysign(prj->w[0], *thetap); + } else if (*thetap == 0.0) { xi = 1.0; eta = 0.0; + } else { - u = PI*sind(*thetap); - v0 = -PI; - v1 = PI; - v = u; - for (k = 0; k < 100; k++) { - resid = (v - u) + sin(v); + double u = PI*sind(*thetap); + double v0 = -PI; + double v1 = PI; + double v = u; + for (int k = 0; k < 100; k++) { + double resid = (v - u) + sin(v); if (resid < 0.0) { if (resid > -tol) break; v0 = v; @@ -4455,16 +4740,16 @@ int stat[]; v = (v0 + v1)/2.0; } - gamma = v/2.0; + double gamma = v/2.0; xi = cos(gamma); eta = prj->w[0]*sin(gamma); } eta -= prj->y0; - for (iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy) { + for (int iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy, statp++) { *xp = xi*(*xp) - prj->x0; *yp = eta; - *(statp++) = 0; + *statp = 0; } } @@ -4492,14 +4777,12 @@ int stat[]; * prj->prjs2x Pointer to aits2x(). *===========================================================================*/ -int aitset(prj) - -struct prjprm *prj; +int aitset(struct prjprm *prj) { if (prj == 0x0) return PRJERR_NULL_POINTER; + if (prj->flag == -AIT) return 0; - prj->flag = AIT; strcpy(prj->code, "AIT"); if (prj->r0 == 0.0) prj->r0 = R2D; @@ -4521,34 +4804,37 @@ struct prjprm *prj; prj->prjx2s = aitx2s; prj->prjs2x = aits2x; + prj->flag = (prj->flag == 1) ? -AIT : AIT; + return prjoff(prj, 0.0, 0.0); } -/*--------------------------------------------------------------------------*/ - -int aitx2s(prj, nx, ny, sxy, spt, x, y, phi, theta, stat) +//---------------------------------------------------------------------------- -struct prjprm *prj; -int nx, ny, sxy, spt; -const double x[], y[]; -double phi[], theta[]; -int stat[]; +int aitx2s( + struct prjprm *prj, + int nx, + int ny, + int sxy, + int spt, + const double x[], + const double y[], + double phi[], + double theta[], + int stat[]) { - int mx, my, rowlen, rowoff, status; - double s, t, x0, xj, y0, yj, yj2, z; const double tol = 1.0e-13; - register int ix, iy, istat, *statp; - register const double *xp, *yp; - register double *phip, *thetap; - - /* Initialize. */ + // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; - if (prj->flag != AIT) { + + int status; + if (abs(prj->flag) != AIT) { if ((status = aitset(prj))) return status; } + int mx, my; if (ny > 0) { mx = nx; my = ny; @@ -4561,18 +4847,18 @@ int stat[]; status = 0; - /* Do x dependence. */ - xp = x; - rowoff = 0; - rowlen = nx*spt; - for (ix = 0; ix < nx; ix++, rowoff += spt, xp += sxy) { - xj = *xp + prj->x0; - s = 1.0 - xj*xj*prj->w[2]; - t = xj*prj->w[3]; + // Do x dependence. + const double *xp = x; + int rowoff = 0; + int rowlen = nx*spt; + for (int ix = 0; ix < nx; ix++, rowoff += spt, xp += sxy) { + double xj = *xp + prj->x0; + double s = 1.0 - xj*xj*prj->w[2]; + double t = xj*prj->w[3]; - phip = phi + rowoff; - thetap = theta + rowoff; - for (iy = 0; iy < my; iy++) { + double *phip = phi + rowoff; + double *thetap = theta + rowoff; + for (int iy = 0; iy < my; iy++) { *phip = s; *thetap = t; phip += rowlen; @@ -4581,19 +4867,19 @@ int stat[]; } - /* Do y dependence. */ - yp = y; - phip = phi; - thetap = theta; - statp = stat; - for (iy = 0; iy < ny; iy++, yp += sxy) { - yj = *yp + prj->y0; - yj2 = yj*yj*prj->w[1]; + // Do y dependence. + const double *yp = y; + double *phip = phi; + double *thetap = theta; + int *statp = stat; + for (int iy = 0; iy < ny; iy++, yp += sxy) { + double yj = *yp + prj->y0; + double yj2 = yj*yj*prj->w[1]; - for (ix = 0; ix < mx; ix++, phip += spt, thetap += spt) { - s = *phip - yj2; + for (int ix = 0; ix < mx; ix++, phip += spt, thetap += spt, statp++) { + double s = *phip - yj2; - istat = 0; + int istat = 0; if (s < 0.5) { if (s < 0.5-tol) { istat = 1; @@ -4603,16 +4889,16 @@ int stat[]; s = 0.5; } - z = sqrt(s); - x0 = 2.0*z*z - 1.0; - y0 = z*(*thetap); + double z = sqrt(s); + double x0 = 2.0*z*z - 1.0; + double y0 = z*(*thetap); if (x0 == 0.0 && y0 == 0.0) { *phip = 0.0; } else { *phip = 2.0*atan2d(y0, x0); } - t = z*yj/prj->r0; + double t = z*yj/prj->r0; if (fabs(t) > 1.0) { if (fabs(t) > 1.0+tol) { istat = 1; @@ -4625,37 +4911,43 @@ int stat[]; } *thetap = t; - *(statp++) = istat; + *statp = istat; } } + + // Do bounds checking on the native coordinates. + if (prj->bounds&4 && prjbchk(1.0e-13, nx, my, spt, phi, theta, stat)) { + if (!status) status = PRJERR_BAD_PIX_SET("aitx2s"); + } + return status; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- -int aits2x(prj, nphi, ntheta, spt, sxy, phi, theta, x, y, stat) - -struct prjprm *prj; -int nphi, ntheta, spt, sxy; -const double phi[], theta[]; -double x[], y[]; -int stat[]; +int aits2x( + struct prjprm *prj, + int nphi, + int ntheta, + int spt, + int sxy, + const double phi[], + const double theta[], + double x[], + double y[], + int stat[]) { - int mphi, mtheta, rowlen, rowoff, status; - double cosphi, costhe, sinphi, sinthe, w; - register int iphi, itheta, *statp; - register const double *phip, *thetap; - register double *xp, *yp; - - - /* Initialize. */ + // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; - if (prj->flag != AIT) { + + int status; + if (abs(prj->flag) != AIT) { if ((status = aitset(prj))) return status; } + int mphi, mtheta; if (ntheta > 0) { mphi = nphi; mtheta = ntheta; @@ -4666,17 +4958,18 @@ int stat[]; } - /* Do phi dependence. */ - phip = phi; - rowoff = 0; - rowlen = nphi*sxy; - for (iphi = 0; iphi < nphi; iphi++, rowoff += sxy, phip += spt) { - w = (*phip)/2.0; + // Do phi dependence. + const double *phip = phi; + int rowoff = 0; + int rowlen = nphi*sxy; + for (int iphi = 0; iphi < nphi; iphi++, rowoff += sxy, phip += spt) { + double w = (*phip)/2.0; + double sinphi, cosphi; sincosd(w, &sinphi, &cosphi); - xp = x + rowoff; - yp = y + rowoff; - for (itheta = 0; itheta < mtheta; itheta++) { + double *xp = x + rowoff; + double *yp = y + rowoff; + for (int itheta = 0; itheta < mtheta; itheta++) { *xp = sinphi; *yp = cosphi; xp += rowlen; @@ -4685,19 +4978,20 @@ int stat[]; } - /* Do theta dependence. */ - thetap = theta; - xp = x; - yp = y; - statp = stat; - for (itheta = 0; itheta < ntheta; itheta++, thetap += spt) { + // Do theta dependence. + const double *thetap = theta; + double *xp = x; + double *yp = y; + int *statp = stat; + for (int itheta = 0; itheta < ntheta; itheta++, thetap += spt) { + double sinthe, costhe; sincosd(*thetap, &sinthe, &costhe); - for (iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy) { - w = sqrt(prj->w[0]/(1.0 + costhe*(*yp))); + for (int iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy, statp++) { + double w = sqrt(prj->w[0]/(1.0 + costhe*(*yp))); *xp = 2.0*w*costhe*(*xp) - prj->x0; *yp = w*sinthe - prj->y0; - *(statp++) = 0; + *statp = 0; } } @@ -4732,16 +5026,13 @@ int stat[]; * prj->prjs2x Pointer to cops2x(). *===========================================================================*/ -int copset(prj) - -struct prjprm *prj; +int copset(struct prjprm *prj) { if (prj == 0x0) return PRJERR_NULL_POINTER; + if (prj->flag == -COP) return 0; - prj->flag = COP; strcpy(prj->code, "COP"); - strcpy(prj->name, "conic perspective"); if (undefined(prj->pv[1])) { return PRJERR_BAD_PARAM_SET("copset"); @@ -4749,6 +5040,7 @@ struct prjprm *prj; if (undefined(prj->pv[2])) prj->pv[2] = 0.0; if (prj->r0 == 0.0) prj->r0 = R2D; + strcpy(prj->name, "conic perspective"); prj->category = CONIC; prj->pvrange = 102; prj->simplezen = 0; @@ -4777,32 +5069,35 @@ struct prjprm *prj; prj->prjx2s = copx2s; prj->prjs2x = cops2x; + prj->flag = (prj->flag == 1) ? -COP : COP; + return prjoff(prj, 0.0, prj->pv[1]); } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- -int copx2s(prj, nx, ny, sxy, spt, x, y, phi, theta, stat) - -struct prjprm *prj; -int nx, ny, sxy, spt; -const double x[], y[]; -double phi[], theta[]; -int stat[]; +int copx2s( + struct prjprm *prj, + int nx, + int ny, + int sxy, + int spt, + const double x[], + const double y[], + double phi[], + double theta[], + int stat[]) { - int mx, my, rowlen, rowoff, status; - double alpha, dy, dy2, r, xj; - register int ix, iy, *statp; - register const double *xp, *yp; - register double *phip, *thetap; - - /* Initialize. */ + // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; - if (prj->flag != COP) { + + int status; + if (abs(prj->flag) != COP) { if ((status = copset(prj))) return status; } + int mx, my; if (ny > 0) { mx = nx; my = ny; @@ -4812,37 +5107,40 @@ int stat[]; ny = nx; } + status = 0; - /* Do x dependence. */ - xp = x; - rowoff = 0; - rowlen = nx*spt; - for (ix = 0; ix < nx; ix++, rowoff += spt, xp += sxy) { - xj = *xp + prj->x0; - phip = phi + rowoff; - for (iy = 0; iy < my; iy++) { + // Do x dependence. + const double *xp = x; + int rowoff = 0; + int rowlen = nx*spt; + for (int ix = 0; ix < nx; ix++, rowoff += spt, xp += sxy) { + double xj = *xp + prj->x0; + + double *phip = phi + rowoff; + for (int iy = 0; iy < my; iy++) { *phip = xj; phip += rowlen; } } - /* Do y dependence. */ - yp = y; - phip = phi; - thetap = theta; - statp = stat; - for (iy = 0; iy < ny; iy++, yp += sxy) { - dy = prj->w[2] - (*yp + prj->y0); - dy2 = dy*dy; + // Do y dependence. + const double *yp = y; + double *phip = phi; + double *thetap = theta; + int *statp = stat; + for (int iy = 0; iy < ny; iy++, yp += sxy) { + double dy = prj->w[2] - (*yp + prj->y0); + double dy2 = dy*dy; - for (ix = 0; ix < mx; ix++, phip += spt, thetap += spt) { - xj = *phip; + for (int ix = 0; ix < mx; ix++, phip += spt, thetap += spt, statp++) { + double xj = *phip; - r = sqrt(xj*xj + dy2); + double r = sqrt(xj*xj + dy2); if (prj->pv[1] < 0.0) r = -r; + double alpha; if (r == 0.0) { alpha = 0.0; } else { @@ -4851,36 +5149,43 @@ int stat[]; *phip = alpha*prj->w[1]; *thetap = prj->pv[1] + atand(prj->w[5] - r*prj->w[4]); - *(statp++) = 0; + *statp = 0; } } - return 0; -} -/*--------------------------------------------------------------------------*/ + // Do bounds checking on the native coordinates. + if (prj->bounds&4 && prjbchk(1.0e-13, nx, my, spt, phi, theta, stat)) { + if (!status) status = PRJERR_BAD_PIX_SET("copx2s"); + } + + return status; +} -int cops2x(prj, nphi, ntheta, spt, sxy, phi, theta, x, y, stat) +//---------------------------------------------------------------------------- -struct prjprm *prj; -int nphi, ntheta, spt, sxy; -const double phi[], theta[]; -double x[], y[]; -int stat[]; +int cops2x( + struct prjprm *prj, + int nphi, + int ntheta, + int spt, + int sxy, + const double phi[], + const double theta[], + double x[], + double y[], + int stat[]) { - int mphi, mtheta, rowlen, rowoff, status; - double alpha, cosalpha, r, s, t, sinalpha, y0; - register int iphi, itheta, istat, *statp; - register const double *phip, *thetap; - register double *xp, *yp; - - /* Initialize. */ + // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; - if (prj->flag != COP) { + + int status; + if (abs(prj->flag) != COP) { if ((status = copset(prj))) return status; } + int mphi, mtheta; if (ntheta > 0) { mphi = nphi; mtheta = ntheta; @@ -4893,17 +5198,18 @@ int stat[]; status = 0; - /* Do phi dependence. */ - phip = phi; - rowoff = 0; - rowlen = nphi*sxy; - for (iphi = 0; iphi < nphi; iphi++, rowoff += sxy, phip += spt) { - alpha = prj->w[0]*(*phip); + // Do phi dependence. + const double *phip = phi; + int rowoff = 0; + int rowlen = nphi*sxy; + for (int iphi = 0; iphi < nphi; iphi++, rowoff += sxy, phip += spt) { + double alpha = prj->w[0]*(*phip); + double sinalpha, cosalpha; sincosd(alpha, &sinalpha, &cosalpha); - xp = x + rowoff; - yp = y + rowoff; - for (itheta = 0; itheta < mtheta; itheta++) { + double *xp = x + rowoff; + double *yp = y + rowoff; + for (int itheta = 0; itheta < mtheta; itheta++) { *xp = sinalpha; *yp = cosalpha; xp += rowlen; @@ -4912,35 +5218,52 @@ int stat[]; } - /* Do theta dependence. */ - thetap = theta; - xp = x; - yp = y; - statp = stat; - y0 = prj->y0 - prj->w[2]; - for (itheta = 0; itheta < ntheta; itheta++, thetap += spt) { - t = *thetap - prj->pv[1]; - s = cosd(t); - - istat = 0; + // Do theta dependence. + const double *thetap = theta; + double *xp = x; + double *yp = y; + int *statp = stat; + double y0 = prj->y0 - prj->w[2]; + for (int itheta = 0; itheta < ntheta; itheta++, thetap += spt) { + double t = *thetap - prj->pv[1]; + double s = cosd(t); + + int istat = 0; + double r; if (s == 0.0) { + // Latitude of divergence. r = 0.0; istat = 1; if (!status) status = PRJERR_BAD_WORLD_SET("cops2x"); + } else if (fabs(*thetap) == 90.0) { + // Return an exact value at the poles. + r = 0.0; + + // Bounds checking. + if (prj->bounds&1) { + if ((*thetap < 0.0) != (prj->pv[1] < 0.0)) { + istat = 1; + if (!status) status = PRJERR_BAD_WORLD_SET("cops2x"); + } + } + } else { r = prj->w[2] - prj->w[3]*sind(t)/s; - if (prj->bounds && r*prj->w[0] < 0.0) { - istat = 1; - if (!status) status = PRJERR_BAD_WORLD_SET("cops2x"); + // Bounds checking. + if (prj->bounds&1) { + if (r*prj->w[0] < 0.0) { + istat = 1; + if (!status) status = PRJERR_BAD_WORLD_SET("cops2x"); + } } } - for (iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy) { + for (int iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy, statp++) { *xp = r*(*xp) - prj->x0; *yp = -r*(*yp) - y0; - *(statp++) = istat; + *statp = istat; } } @@ -4978,18 +5301,13 @@ int stat[]; * prj->prjs2x Pointer to coes2x(). *===========================================================================*/ -int coeset(prj) - -struct prjprm *prj; +int coeset(struct prjprm *prj) { - double theta1, theta2; - if (prj == 0x0) return PRJERR_NULL_POINTER; + if (prj->flag == -COE) return 0; - prj->flag = COE; strcpy(prj->code, "COE"); - strcpy(prj->name, "conic equal area"); if (undefined(prj->pv[1])) { return PRJERR_BAD_PARAM_SET("coeset"); @@ -4997,6 +5315,7 @@ struct prjprm *prj; if (undefined(prj->pv[2])) prj->pv[2] = 0.0; if (prj->r0 == 0.0) prj->r0 = R2D; + strcpy(prj->name, "conic equal area"); prj->category = CONIC; prj->pvrange = 102; prj->simplezen = 0; @@ -5005,8 +5324,8 @@ struct prjprm *prj; prj->global = 1; prj->divergent = 0; - theta1 = prj->pv[1] - prj->pv[2]; - theta2 = prj->pv[1] + prj->pv[2]; + double theta1 = prj->pv[1] - prj->pv[2]; + double theta2 = prj->pv[1] + prj->pv[2]; prj->w[0] = (sind(theta1) + sind(theta2))/2.0; if (prj->w[0] == 0.0) { @@ -5027,33 +5346,37 @@ struct prjprm *prj; prj->prjx2s = coex2s; prj->prjs2x = coes2x; + prj->flag = (prj->flag == 1) ? -COE : COE; + return prjoff(prj, 0.0, prj->pv[1]); } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- -int coex2s(prj, nx, ny, sxy, spt, x, y, phi, theta, stat) - -struct prjprm *prj; -int nx, ny, sxy, spt; -const double x[], y[]; -double phi[], theta[]; -int stat[]; +int coex2s( + struct prjprm *prj, + int nx, + int ny, + int sxy, + int spt, + const double x[], + const double y[], + double phi[], + double theta[], + int stat[]) { - int mx, my, rowlen, rowoff, status; - double alpha, dy, dy2, r, t, w, xj; const double tol = 1.0e-12; - register int ix, iy, istat, *statp; - register const double *xp, *yp; - register double *phip, *thetap; - /* Initialize. */ + // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; - if (prj->flag != COE) { + + int status; + if (abs(prj->flag) != COE) { if ((status = coeset(prj))) return status; } + int mx, my; if (ny > 0) { mx = nx; my = ny; @@ -5066,47 +5389,49 @@ int stat[]; status = 0; - /* Do x dependence. */ - xp = x; - rowoff = 0; - rowlen = nx*spt; - for (ix = 0; ix < nx; ix++, rowoff += spt, xp += sxy) { - xj = *xp + prj->x0; + // Do x dependence. + const double *xp = x; + int rowoff = 0; + int rowlen = nx*spt; + for (int ix = 0; ix < nx; ix++, rowoff += spt, xp += sxy) { + double xj = *xp + prj->x0; - phip = phi + rowoff; - for (iy = 0; iy < my; iy++) { + double *phip = phi + rowoff; + for (int iy = 0; iy < my; iy++) { *phip = xj; phip += rowlen; } } - /* Do y dependence. */ - yp = y; - phip = phi; - thetap = theta; - statp = stat; - for (iy = 0; iy < ny; iy++, yp += sxy) { - dy = prj->w[2] - (*yp + prj->y0); - dy2 = dy*dy; + // Do y dependence. + const double *yp = y; + double *phip = phi; + double *thetap = theta; + int *statp = stat; + for (int iy = 0; iy < ny; iy++, yp += sxy) { + double dy = prj->w[2] - (*yp + prj->y0); + double dy2 = dy*dy; - for (ix = 0; ix < mx; ix++, phip += spt, thetap += spt) { - xj = *phip; + for (int ix = 0; ix < mx; ix++, phip += spt, thetap += spt, statp++) { + double xj = *phip; - r = sqrt(xj*xj + dy2); + double r = sqrt(xj*xj + dy2); if (prj->pv[1] < 0.0) r = -r; + double alpha; if (r == 0.0) { alpha = 0.0; } else { alpha = atan2d(xj/r, dy/r); } - istat = 0; + int istat = 0; + double t; if (fabs(r - prj->w[8]) < tol) { t = -90.0; } else { - w = (prj->w[6] - r*r)*prj->w[7]; + double w = (prj->w[6] - r*r)*prj->w[7]; if (fabs(w) > 1.0) { if (fabs(w-1.0) < tol) { t = 90.0; @@ -5124,36 +5449,43 @@ int stat[]; *phip = alpha*prj->w[1]; *thetap = t; - *(statp++) = istat; + *statp = istat; } } + + // Do bounds checking on the native coordinates. + if (prj->bounds&4 && prjbchk(1.0e-13, nx, my, spt, phi, theta, stat)) { + if (!status) status = PRJERR_BAD_PIX_SET("coex2s"); + } + return status; } -/*--------------------------------------------------------------------------*/ - -int coes2x(prj, nphi, ntheta, spt, sxy, phi, theta, x, y, stat) +//---------------------------------------------------------------------------- -struct prjprm *prj; -int nphi, ntheta, spt, sxy; -const double phi[], theta[]; -double x[], y[]; -int stat[]; +int coes2x( + struct prjprm *prj, + int nphi, + int ntheta, + int spt, + int sxy, + const double phi[], + const double theta[], + double x[], + double y[], + int stat[]) { - int mphi, mtheta, rowlen, rowoff, status; - double alpha, cosalpha, r, sinalpha, y0; - register int iphi, itheta, *statp; - register const double *phip, *thetap; - register double *xp, *yp; - - /* Initialize. */ + // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; - if (prj->flag != COE) { + + int status; + if (abs(prj->flag) != COE) { if ((status = coeset(prj))) return status; } + int mphi, mtheta; if (ntheta > 0) { mphi = nphi; mtheta = ntheta; @@ -5164,17 +5496,18 @@ int stat[]; } - /* Do phi dependence. */ - phip = phi; - rowoff = 0; - rowlen = nphi*sxy; - for (iphi = 0; iphi < nphi; iphi++, rowoff += sxy, phip += spt) { - alpha = prj->w[0]*(*phip); + // Do phi dependence. + const double *phip = phi; + int rowoff = 0; + int rowlen = nphi*sxy; + for (int iphi = 0; iphi < nphi; iphi++, rowoff += sxy, phip += spt) { + double alpha = prj->w[0]*(*phip); + double sinalpha, cosalpha; sincosd(alpha, &sinalpha, &cosalpha); - xp = x + rowoff; - yp = y + rowoff; - for (itheta = 0; itheta < mtheta; itheta++) { + double *xp = x + rowoff; + double *yp = y + rowoff; + for (int itheta = 0; itheta < mtheta; itheta++) { *xp = sinalpha; *yp = cosalpha; xp += rowlen; @@ -5183,23 +5516,24 @@ int stat[]; } - /* Do theta dependence. */ - thetap = theta; - xp = x; - yp = y; - statp = stat; - y0 = prj->y0 - prj->w[2]; - for (itheta = 0; itheta < ntheta; itheta++, thetap += spt) { + // Do theta dependence. + const double *thetap = theta; + double *xp = x; + double *yp = y; + int *statp = stat; + double y0 = prj->y0 - prj->w[2]; + for (int itheta = 0; itheta < ntheta; itheta++, thetap += spt) { + double r; if (*thetap == -90.0) { r = prj->w[8]; } else { r = prj->w[3]*sqrt(prj->w[4] - prj->w[5]*sind(*thetap)); } - for (iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy) { + for (int iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy, statp++) { *xp = r*(*xp) - prj->x0; *yp = -r*(*yp) - y0; - *(statp++) = 0; + *statp = 0; } } @@ -5232,16 +5566,13 @@ int stat[]; * prj->prjs2x Pointer to cods2x(). *===========================================================================*/ -int codset(prj) - -struct prjprm *prj; +int codset(struct prjprm *prj) { if (prj == 0x0) return PRJERR_NULL_POINTER; + if (prj->flag == -COD) return 0; - prj->flag = COD; strcpy(prj->code, "COD"); - strcpy(prj->name, "conic equidistant"); if (undefined(prj->pv[1])) { return PRJERR_BAD_PARAM_SET("codset"); @@ -5249,6 +5580,7 @@ struct prjprm *prj; if (undefined(prj->pv[2])) prj->pv[2] = 0.0; if (prj->r0 == 0.0) prj->r0 = R2D; + strcpy(prj->name, "conic equidistant"); prj->category = CONIC; prj->pvrange = 102; prj->simplezen = 0; @@ -5274,32 +5606,35 @@ struct prjprm *prj; prj->prjx2s = codx2s; prj->prjs2x = cods2x; + prj->flag = (prj->flag == 1) ? -COD : COD; + return prjoff(prj, 0.0, prj->pv[1]); } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- -int codx2s(prj, nx, ny, sxy, spt, x, y, phi, theta, stat) - -struct prjprm *prj; -int nx, ny, sxy, spt; -const double x[], y[]; -double phi[], theta[]; -int stat[]; +int codx2s( + struct prjprm *prj, + int nx, + int ny, + int sxy, + int spt, + const double x[], + const double y[], + double phi[], + double theta[], + int stat[]) { - int mx, my, rowlen, rowoff, status; - double alpha, dy, dy2, r, xj; - register int ix, iy, *statp; - register const double *xp, *yp; - register double *phip, *thetap; - - /* Initialize. */ + // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; - if (prj->flag != COD) { + + int status; + if (abs(prj->flag) != COD) { if ((status = codset(prj))) return status; } + int mx, my; if (ny > 0) { mx = nx; my = ny; @@ -5309,37 +5644,40 @@ int stat[]; ny = nx; } + status = 0; - /* Do x dependence. */ - xp = x; - rowoff = 0; - rowlen = nx*spt; - for (ix = 0; ix < nx; ix++, rowoff += spt, xp += sxy) { - xj = *xp + prj->x0; - phip = phi + rowoff; - for (iy = 0; iy < my; iy++) { + // Do x dependence. + const double *xp = x; + int rowoff = 0; + int rowlen = nx*spt; + for (int ix = 0; ix < nx; ix++, rowoff += spt, xp += sxy) { + double xj = *xp + prj->x0; + + double *phip = phi + rowoff; + for (int iy = 0; iy < my; iy++) { *phip = xj; phip += rowlen; } } - /* Do y dependence. */ - yp = y; - phip = phi; - thetap = theta; - statp = stat; - for (iy = 0; iy < ny; iy++, yp += sxy) { - dy = prj->w[2] - (*yp + prj->y0); - dy2 = dy*dy; + // Do y dependence. + const double *yp = y; + double *phip = phi; + double *thetap = theta; + int *statp = stat; + for (int iy = 0; iy < ny; iy++, yp += sxy) { + double dy = prj->w[2] - (*yp + prj->y0); + double dy2 = dy*dy; - for (ix = 0; ix < mx; ix++, phip += spt, thetap += spt) { - xj = *phip; + for (int ix = 0; ix < mx; ix++, phip += spt, thetap += spt, statp++) { + double xj = *phip; - r = sqrt(xj*xj + dy2); + double r = sqrt(xj*xj + dy2); if (prj->pv[1] < 0.0) r = -r; + double alpha; if (r == 0.0) { alpha = 0.0; } else { @@ -5348,36 +5686,43 @@ int stat[]; *phip = alpha*prj->w[1]; *thetap = prj->w[3] - r; - *(statp++) = 0; + *statp = 0; } } - return 0; -} -/*--------------------------------------------------------------------------*/ + // Do bounds checking on the native coordinates. + if (prj->bounds&4 && prjbchk(1.0e-13, nx, my, spt, phi, theta, stat)) { + if (!status) status = PRJERR_BAD_PIX_SET("codx2s"); + } + + return status; +} -int cods2x(prj, nphi, ntheta, spt, sxy, phi, theta, x, y, stat) +//---------------------------------------------------------------------------- -struct prjprm *prj; -int nphi, ntheta, spt, sxy; -const double phi[], theta[]; -double x[], y[]; -int stat[]; +int cods2x( + struct prjprm *prj, + int nphi, + int ntheta, + int spt, + int sxy, + const double phi[], + const double theta[], + double x[], + double y[], + int stat[]) { - int mphi, mtheta, rowlen, rowoff, status; - double alpha, cosalpha, r, sinalpha, y0; - register int iphi, itheta, *statp; - register const double *phip, *thetap; - register double *xp, *yp; - - /* Initialize. */ + // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; - if (prj->flag != COD) { + + int status; + if (abs(prj->flag) != COD) { if ((status = codset(prj))) return status; } + int mphi, mtheta; if (ntheta > 0) { mphi = nphi; mtheta = ntheta; @@ -5388,17 +5733,18 @@ int stat[]; } - /* Do phi dependence. */ - phip = phi; - rowoff = 0; - rowlen = nphi*sxy; - for (iphi = 0; iphi < nphi; iphi++, rowoff += sxy, phip += spt) { - alpha = prj->w[0]*(*phip); + // Do phi dependence. + const double *phip = phi; + int rowoff = 0; + int rowlen = nphi*sxy; + for (int iphi = 0; iphi < nphi; iphi++, rowoff += sxy, phip += spt) { + double alpha = prj->w[0]*(*phip); + double sinalpha, cosalpha; sincosd(alpha, &sinalpha, &cosalpha); - xp = x + rowoff; - yp = y + rowoff; - for (itheta = 0; itheta < mtheta; itheta++) { + double *xp = x + rowoff; + double *yp = y + rowoff; + for (int itheta = 0; itheta < mtheta; itheta++) { *xp = sinalpha; *yp = cosalpha; xp += rowlen; @@ -5407,19 +5753,19 @@ int stat[]; } - /* Do theta dependence. */ - thetap = theta; - xp = x; - yp = y; - statp = stat; - y0 = prj->y0 - prj->w[2]; - for (itheta = 0; itheta < ntheta; itheta++, thetap += spt) { - r = prj->w[3] - *thetap; - - for (iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy) { + // Do theta dependence. + const double *thetap = theta; + double *xp = x; + double *yp = y; + int *statp = stat; + double y0 = prj->y0 - prj->w[2]; + for (int itheta = 0; itheta < ntheta; itheta++, thetap += spt) { + double r = prj->w[3] - *thetap; + + for (int iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy, statp++) { *xp = r*(*xp) - prj->x0; *yp = -r*(*yp) - y0; - *(statp++) = 0; + *statp = 0; } } @@ -5455,18 +5801,13 @@ int stat[]; * prj->prjs2x Pointer to coos2x(). *===========================================================================*/ -int cooset(prj) - -struct prjprm *prj; +int cooset(struct prjprm *prj) { - double cos1, cos2, tan1, tan2, theta1, theta2; - if (prj == 0x0) return PRJERR_NULL_POINTER; + if (prj->flag == -COO) return 0; - prj->flag = COO; strcpy(prj->code, "COO"); - strcpy(prj->name, "conic orthomorphic"); if (undefined(prj->pv[1])) { return PRJERR_BAD_PARAM_SET("cooset"); @@ -5474,6 +5815,7 @@ struct prjprm *prj; if (undefined(prj->pv[2])) prj->pv[2] = 0.0; if (prj->r0 == 0.0) prj->r0 = R2D; + strcpy(prj->name, "conic orthomorphic"); prj->category = CONIC; prj->pvrange = 102; prj->simplezen = 0; @@ -5482,17 +5824,17 @@ struct prjprm *prj; prj->global = 0; prj->divergent = 1; - theta1 = prj->pv[1] - prj->pv[2]; - theta2 = prj->pv[1] + prj->pv[2]; + double theta1 = prj->pv[1] - prj->pv[2]; + double theta2 = prj->pv[1] + prj->pv[2]; - tan1 = tand((90.0 - theta1)/2.0); - cos1 = cosd(theta1); + double tan1 = tand((90.0 - theta1)/2.0); + double cos1 = cosd(theta1); if (theta1 == theta2) { prj->w[0] = sind(theta1); } else { - tan2 = tand((90.0 - theta2)/2.0); - cos2 = cosd(theta2); + double tan2 = tand((90.0 - theta2)/2.0); + double cos2 = cosd(theta2); prj->w[0] = log(cos2/cos1)/log(tan2/tan1); } if (prj->w[0] == 0.0) { @@ -5511,32 +5853,35 @@ struct prjprm *prj; prj->prjx2s = coox2s; prj->prjs2x = coos2x; + prj->flag = (prj->flag == 1) ? -COO : COO; + return prjoff(prj, 0.0, prj->pv[1]); } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- -int coox2s(prj, nx, ny, sxy, spt, x, y, phi, theta, stat) - -struct prjprm *prj; -int nx, ny, sxy, spt; -const double x[], y[]; -double phi[], theta[]; -int stat[]; +int coox2s( + struct prjprm *prj, + int nx, + int ny, + int sxy, + int spt, + const double x[], + const double y[], + double phi[], + double theta[], + int stat[]) { - int mx, my, rowlen, rowoff, status; - double alpha, dy, dy2, r, t, xj; - register int ix, iy, istat, *statp; - register const double *xp, *yp; - register double *phip, *thetap; - - /* Initialize. */ + // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; - if (prj->flag != COO) { + + int status; + if (abs(prj->flag) != COO) { if ((status = cooset(prj))) return status; } + int mx, my; if (ny > 0) { mx = nx; my = ny; @@ -5549,43 +5894,45 @@ int stat[]; status = 0; - /* Do x dependence. */ - xp = x; - rowoff = 0; - rowlen = nx*spt; - for (ix = 0; ix < nx; ix++, rowoff += spt, xp += sxy) { - xj = *xp + prj->x0; + // Do x dependence. + const double *xp = x; + int rowoff = 0; + int rowlen = nx*spt; + for (int ix = 0; ix < nx; ix++, rowoff += spt, xp += sxy) { + double xj = *xp + prj->x0; - phip = phi + rowoff; - for (iy = 0; iy < my; iy++) { + double *phip = phi + rowoff; + for (int iy = 0; iy < my; iy++) { *phip = xj; phip += rowlen; } } - /* Do y dependence. */ - yp = y; - phip = phi; - thetap = theta; - statp = stat; - for (iy = 0; iy < ny; iy++, yp += sxy) { - dy = prj->w[2] - (*yp + prj->y0); - dy2 = dy*dy; + // Do y dependence. + const double *yp = y; + double *phip = phi; + double *thetap = theta; + int *statp = stat; + for (int iy = 0; iy < ny; iy++, yp += sxy) { + double dy = prj->w[2] - (*yp + prj->y0); + double dy2 = dy*dy; - for (ix = 0; ix < mx; ix++, phip += spt, thetap += spt) { - xj = *phip; + for (int ix = 0; ix < mx; ix++, phip += spt, thetap += spt, statp++) { + double xj = *phip; - r = sqrt(xj*xj + dy2); + double r = sqrt(xj*xj + dy2); if (prj->pv[1] < 0.0) r = -r; + double alpha; if (r == 0.0) { alpha = 0.0; } else { alpha = atan2d(xj/r, dy/r); } - istat = 0; + int istat = 0; + double t; if (r == 0.0) { if (prj->w[0] < 0.0) { t = -90.0; @@ -5600,36 +5947,43 @@ int stat[]; *phip = alpha*prj->w[1]; *thetap = t; - *(statp++) = istat; + *statp = istat; } } + + // Do bounds checking on the native coordinates. + if (prj->bounds&4 && prjbchk(1.0e-13, nx, my, spt, phi, theta, stat)) { + if (!status) status = PRJERR_BAD_PIX_SET("coox2s"); + } + return status; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- -int coos2x(prj, nphi, ntheta, spt, sxy, phi, theta, x, y, stat) - -struct prjprm *prj; -int nphi, ntheta, spt, sxy; -const double phi[], theta[]; -double x[], y[]; -int stat[]; +int coos2x( + struct prjprm *prj, + int nphi, + int ntheta, + int spt, + int sxy, + const double phi[], + const double theta[], + double x[], + double y[], + int stat[]) { - int mphi, mtheta, rowlen, rowoff, status; - double alpha, cosalpha, r, sinalpha, y0; - register int iphi, itheta, istat, *statp; - register const double *phip, *thetap; - register double *xp, *yp; - - /* Initialize. */ + // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; - if (prj->flag != COO) { + + int status; + if (abs(prj->flag) != COO) { if ((status = cooset(prj))) return status; } + int mphi, mtheta; if (ntheta > 0) { mphi = nphi; mtheta = ntheta; @@ -5642,17 +5996,18 @@ int stat[]; status = 0; - /* Do phi dependence. */ - phip = phi; - rowoff = 0; - rowlen = nphi*sxy; - for (iphi = 0; iphi < nphi; iphi++, rowoff += sxy, phip += spt) { - alpha = prj->w[0]*(*phip); + // Do phi dependence. + const double *phip = phi; + int rowoff = 0; + int rowlen = nphi*sxy; + for (int iphi = 0; iphi < nphi; iphi++, rowoff += sxy, phip += spt) { + double alpha = prj->w[0]*(*phip); + double sinalpha, cosalpha; sincosd(alpha, &sinalpha, &cosalpha); - xp = x + rowoff; - yp = y + rowoff; - for (itheta = 0; itheta < mtheta; itheta++) { + double *xp = x + rowoff; + double *yp = y + rowoff; + for (int itheta = 0; itheta < mtheta; itheta++) { *xp = sinalpha; *yp = cosalpha; xp += rowlen; @@ -5661,15 +6016,16 @@ int stat[]; } - /* Do theta dependence. */ - thetap = theta; - xp = x; - yp = y; - statp = stat; - y0 = prj->y0 - prj->w[2]; - for (itheta = 0; itheta < ntheta; itheta++, thetap += spt) { - istat = 0; + // Do theta dependence. + const double *thetap = theta; + double *xp = x; + double *yp = y; + int *statp = stat; + double y0 = prj->y0 - prj->w[2]; + for (int itheta = 0; itheta < ntheta; itheta++, thetap += spt) { + int istat = 0; + double r; if (*thetap == -90.0) { r = 0.0; if (prj->w[0] >= 0.0) { @@ -5680,10 +6036,10 @@ int stat[]; r = prj->w[3]*pow(tand((90.0 - *thetap)/2.0),prj->w[0]); } - for (iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy) { + for (int iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy, statp++) { *xp = r*(*xp) - prj->x0; *yp = -r*(*yp) - y0; - *(statp++) = istat; + *statp = istat; } } @@ -5712,26 +6068,24 @@ int stat[]; * prj->prjs2x Pointer to bons2x(). *===========================================================================*/ -int bonset(prj) - -struct prjprm *prj; +int bonset(struct prjprm *prj) { if (prj == 0x0) return PRJERR_NULL_POINTER; + if (prj->flag == -BON) return 0; - prj->flag = BON; strcpy(prj->code, "BON"); - strcpy(prj->name, "Bonne's"); if (undefined(prj->pv[1])) { return PRJERR_BAD_PARAM_SET("bonset"); } if (prj->pv[1] == 0.0) { - /* Sanson-Flamsteed. */ + // Sanson-Flamsteed. return sflset(prj); } + strcpy(prj->name, "Bonne's"); prj->category = POLYCONIC; prj->pvrange = 101; prj->simplezen = 0; @@ -5752,38 +6106,39 @@ struct prjprm *prj; prj->prjx2s = bonx2s; prj->prjs2x = bons2x; + prj->flag = (prj->flag == 1) ? -BON : BON; + return prjoff(prj, 0.0, 0.0); } -/*--------------------------------------------------------------------------*/ - -int bonx2s(prj, nx, ny, sxy, spt, x, y, phi, theta, stat) +//---------------------------------------------------------------------------- -struct prjprm *prj; -int nx, ny, sxy, spt; -const double x[], y[]; -double phi[], theta[]; -int stat[]; +int bonx2s( + struct prjprm *prj, + int nx, + int ny, + int sxy, + int spt, + const double x[], + const double y[], + double phi[], + double theta[], + int stat[]) { - int mx, my, rowlen, rowoff, status; - double alpha, dy, dy2, costhe, r, s, t, xj; - register int ix, iy, *statp; - register const double *xp, *yp; - register double *phip, *thetap; - - - /* Initialize. */ + // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; if (prj->pv[1] == 0.0) { - /* Sanson-Flamsteed. */ + // Sanson-Flamsteed. return sflx2s(prj, nx, ny, sxy, spt, x, y, phi, theta, stat); } - if (prj->flag != BON) { + int status; + if (abs(prj->flag) != BON) { if ((status = bonset(prj))) return status; } + int mx, my; if (ny > 0) { mx = nx; my = ny; @@ -5793,45 +6148,49 @@ int stat[]; ny = nx; } + status = 0; - /* Do x dependence. */ - xp = x; - rowoff = 0; - rowlen = nx*spt; - for (ix = 0; ix < nx; ix++, rowoff += spt, xp += sxy) { - xj = *xp + prj->x0; - phip = phi + rowoff; - for (iy = 0; iy < my; iy++) { + // Do x dependence. + const double *xp = x; + int rowoff = 0; + int rowlen = nx*spt; + for (int ix = 0; ix < nx; ix++, rowoff += spt, xp += sxy) { + double xj = *xp + prj->x0; + + double *phip = phi + rowoff; + for (int iy = 0; iy < my; iy++) { *phip = xj; phip += rowlen; } } - /* Do y dependence. */ - yp = y; - phip = phi; - thetap = theta; - statp = stat; - for (iy = 0; iy < ny; iy++, yp += sxy) { - dy = prj->w[2] - (*yp + prj->y0); - dy2 = dy*dy; + // Do y dependence. + const double *yp = y; + double *phip = phi; + double *thetap = theta; + int *statp = stat; + for (int iy = 0; iy < ny; iy++, yp += sxy) { + double dy = prj->w[2] - (*yp + prj->y0); + double dy2 = dy*dy; - for (ix = 0; ix < mx; ix++, phip += spt, thetap += spt) { - xj = *phip; + for (int ix = 0; ix < mx; ix++, phip += spt, thetap += spt, statp++) { + double xj = *phip; - r = sqrt(xj*xj + dy2); + double r = sqrt(xj*xj + dy2); if (prj->pv[1] < 0.0) r = -r; + double alpha; if (r == 0.0) { alpha = 0.0; } else { alpha = atan2d(xj/r, dy/r); } - t = (prj->w[2] - r)/prj->w[1]; - costhe = cosd(t); + double s; + double t = (prj->w[2] - r)/prj->w[1]; + double costhe = cosd(t); if (costhe == 0.0) { s = 0.0; } else { @@ -5840,41 +6199,47 @@ int stat[]; *phip = s; *thetap = t; - *(statp++) = 0; + *statp = 0; } } - return 0; -} -/*--------------------------------------------------------------------------*/ + // Do bounds checking on the native coordinates. + if (prj->bounds&4 && prjbchk(1.0e-11, nx, my, spt, phi, theta, stat)) { + if (!status) status = PRJERR_BAD_PIX_SET("bonx2s"); + } -int bons2x(prj, nphi, ntheta, spt, sxy, phi, theta, x, y, stat) + return status; +} -struct prjprm *prj; -int nphi, ntheta, spt, sxy; -const double phi[], theta[]; -double x[], y[]; -int stat[]; +//---------------------------------------------------------------------------- -{ - int mphi, mtheta, rowlen, rowoff, status; - double alpha, cosalpha, r, s, sinalpha, y0; - register int iphi, itheta, *statp; - register const double *phip, *thetap; - register double *xp, *yp; +int bons2x( + struct prjprm *prj, + int nphi, + int ntheta, + int spt, + int sxy, + const double phi[], + const double theta[], + double x[], + double y[], + int stat[]) - /* Initialize. */ +{ + // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; if (prj->pv[1] == 0.0) { - /* Sanson-Flamsteed. */ + // Sanson-Flamsteed. return sfls2x(prj, nphi, ntheta, spt, sxy, phi, theta, x, y, stat); } - if (prj->flag != BON) { + int status; + if (abs(prj->flag) != BON) { if ((status = bonset(prj))) return status; } + int mphi, mtheta; if (ntheta > 0) { mphi = nphi; mtheta = ntheta; @@ -5884,39 +6249,39 @@ int stat[]; ntheta = nphi; } - y0 = prj->y0 - prj->w[2]; - - /* Do phi dependence. */ - phip = phi; - rowoff = 0; - rowlen = nphi*sxy; - for (iphi = 0; iphi < nphi; iphi++, rowoff += sxy, phip += spt) { - s = prj->r0*(*phip); + // Do phi dependence. + const double *phip = phi; + int rowoff = 0; + int rowlen = nphi*sxy; + for (int iphi = 0; iphi < nphi; iphi++, rowoff += sxy, phip += spt) { + double s = prj->r0*(*phip); - xp = x + rowoff; - for (itheta = 0; itheta < mtheta; itheta++) { + double *xp = x + rowoff; + for (int itheta = 0; itheta < mtheta; itheta++) { *xp = s; xp += rowlen; } } - /* Do theta dependence. */ - thetap = theta; - xp = x; - yp = y; - statp = stat; - for (itheta = 0; itheta < ntheta; itheta++, thetap += spt) { - r = prj->w[2] - prj->w[1]*(*thetap); - s = cosd(*thetap)/r; - - for (iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy) { - alpha = s*(*xp); + // Do theta dependence. + const double *thetap = theta; + double *xp = x; + double *yp = y; + int *statp = stat; + double y0 = prj->y0 - prj->w[2]; + for (int itheta = 0; itheta < ntheta; itheta++, thetap += spt) { + double r = prj->w[2] - prj->w[1]*(*thetap); + double s = cosd(*thetap)/r; + + for (int iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy, statp++) { + double alpha = s*(*xp); + double sinalpha, cosalpha; sincosd(alpha, &sinalpha, &cosalpha); *xp = r*sinalpha - prj->x0; *yp = -r*cosalpha - y0; - *(statp++) = 0; + *statp = 0; } } @@ -5937,20 +6302,19 @@ int stat[]; * prj->x0 Fiducial offset in x. * prj->y0 Fiducial offset in y. * prj->w[0] r0*(pi/180) -* prj->w[1] 1/r0 +* prj->w[1] (180/pi)/r0 * prj->w[2] 2*r0 +* prj->w[3] (pi/180)/(2*r0) * prj->prjx2s Pointer to pcox2s(). * prj->prjs2x Pointer to pcos2x(). *===========================================================================*/ -int pcoset(prj) - -struct prjprm *prj; +int pcoset(struct prjprm *prj) { if (prj == 0x0) return PRJERR_NULL_POINTER; + if (prj->flag == -PCO) return 0; - prj->flag = PCO; strcpy(prj->code, "PCO"); strcpy(prj->name, "polyconic"); @@ -5972,39 +6336,42 @@ struct prjprm *prj; prj->w[1] = 1.0/prj->w[0]; prj->w[2] = 2.0*prj->r0; } + prj->w[3] = D2R/prj->w[2]; prj->prjx2s = pcox2s; prj->prjs2x = pcos2x; + prj->flag = (prj->flag == 1) ? -PCO : PCO; + return prjoff(prj, 0.0, 0.0); } -/*--------------------------------------------------------------------------*/ - -int pcox2s(prj, nx, ny, sxy, spt, x, y, phi, theta, stat) +//---------------------------------------------------------------------------- -struct prjprm *prj; -int nx, ny, sxy, spt; -const double x[], y[]; -double phi[], theta[]; -int stat[]; +int pcox2s( + struct prjprm *prj, + int nx, + int ny, + int sxy, + int spt, + const double x[], + const double y[], + double phi[], + double theta[], + int stat[]) { - int mx, my, rowlen, rowoff, status; - double f, fneg, fpos, lambda, tanthe, the, theneg, thepos, w, x1, xj, xx, - yj, ymthe, y1; const double tol = 1.0e-12; - register int ix, iy, k, *statp; - register const double *xp, *yp; - register double *phip, *thetap; - - /* Initialize. */ + // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; - if (prj->flag != PCO) { + + int status; + if (abs(prj->flag) != PCO) { if ((status = pcoset(prj))) return status; } + int mx, my; if (ny > 0) { mx = nx; my = ny; @@ -6014,33 +6381,35 @@ int stat[]; ny = nx; } + status = 0; - /* Do x dependence. */ - xp = x; - rowoff = 0; - rowlen = nx*spt; - for (ix = 0; ix < nx; ix++, rowoff += spt, xp += sxy) { - xj = *xp + prj->x0; - phip = phi + rowoff; - for (iy = 0; iy < my; iy++) { + // Do x dependence. + const double *xp = x; + int rowoff = 0; + int rowlen = nx*spt; + for (int ix = 0; ix < nx; ix++, rowoff += spt, xp += sxy) { + double xj = *xp + prj->x0; + + double *phip = phi + rowoff; + for (int iy = 0; iy < my; iy++) { *phip = xj; phip += rowlen; } } - /* Do y dependence. */ - yp = y; - phip = phi; - thetap = theta; - statp = stat; - for (iy = 0; iy < ny; iy++, yp += sxy) { - yj = *yp + prj->y0; - w = fabs(yj*prj->w[1]); + // Do y dependence. + const double *yp = y; + double *phip = phi; + double *thetap = theta; + int *statp = stat; + for (int iy = 0; iy < ny; iy++, yp += sxy) { + double yj = *yp + prj->y0; + double w = fabs(yj*prj->w[1]); - for (ix = 0; ix < mx; ix++, phip += spt, thetap += spt) { - xj = *phip; + for (int ix = 0; ix < mx; ix++, phip += spt, thetap += spt, statp++) { + double xj = *phip; if (w < tol) { *phip = xj*prj->w[1]; @@ -6051,55 +6420,55 @@ int stat[]; *thetap = copysign(90.0, yj); } else { - /* Iterative solution using weighted division of the interval. */ - if (yj > 0.0) { - thepos = 90.0; - } else { - thepos = -90.0; - } - theneg = 0.0; - - xx = xj*xj; - ymthe = yj - prj->w[0]*thepos; - fpos = xx + ymthe*ymthe; - fneg = -999.0; + double the, ymthe, tanthe; + if (w < 1.0e-4) { + // To avoid cot(theta) blowing up near theta == 0. + the = yj / (prj->w[0] + prj->w[3]*xj*xj); + ymthe = yj - prj->w[0]*the; + tanthe = tand(the); - for (k = 0; k < 64; k++) { - if (fneg < -100.0) { - /* Equal division of the interval. */ - the = (thepos+theneg)/2.0; - } else { - /* Weighted division of the interval. */ - lambda = fpos/(fpos-fneg); + } else { + // Iterative solution using weighted division of the interval. + double thepos = yj / prj->w[0]; + double theneg = 0.0; + + // Setting fneg = -fpos halves the interval in the first iter. + double xx = xj*xj; + double fpos = xx; + double fneg = -xx; + + for (int k = 0; k < 64; k++) { + // Weighted division of the interval. + double lambda = fpos/(fpos-fneg); if (lambda < 0.1) { lambda = 0.1; } else if (lambda > 0.9) { lambda = 0.9; } the = thepos - lambda*(thepos-theneg); - } - /* Compute the residue. */ - ymthe = yj - prj->w[0]*(the); - tanthe = tand(the); - f = xx + ymthe*(ymthe - prj->w[2]/tanthe); + // Compute the residue. + ymthe = yj - prj->w[0]*the; + tanthe = tand(the); + double f = xx + ymthe*(ymthe - prj->w[2]/tanthe); - /* Check for convergence. */ - if (fabs(f) < tol) break; - if (fabs(thepos-theneg) < tol) break; + // Check for convergence. + if (fabs(f) < tol) break; + if (fabs(thepos-theneg) < tol) break; - /* Redefine the interval. */ - if (f > 0.0) { - thepos = the; - fpos = f; - } else { - theneg = the; - fneg = f; + // Redefine the interval. + if (f > 0.0) { + thepos = the; + fpos = f; + } else { + theneg = the; + fneg = f; + } } } - x1 = prj->r0 - ymthe*tanthe; - y1 = xj*tanthe; + double x1 = prj->r0 - ymthe*tanthe; + double y1 = xj*tanthe; if (x1 == 0.0 && y1 == 0.0) { *phip = 0.0; } else { @@ -6109,36 +6478,43 @@ int stat[]; *thetap = the; } - *(statp++) = 0; + *statp = 0; } } - return 0; -} -/*--------------------------------------------------------------------------*/ + // Do bounds checking on the native coordinates. + if (prj->bounds&4 && prjbchk(1.0e-12, nx, my, spt, phi, theta, stat)) { + if (!status) status = PRJERR_BAD_PIX_SET("pcox2s"); + } -int pcos2x(prj, nphi, ntheta, spt, sxy, phi, theta, x, y, stat) + return status; +} -struct prjprm *prj; -int nphi, ntheta, spt, sxy; -const double phi[], theta[]; -double x[], y[]; -int stat[]; +//---------------------------------------------------------------------------- -{ - int mphi, mtheta, rowlen, rowoff, status; - double alpha, costhe, cotthe, sinthe, therad; - register int iphi, itheta, *statp; - register const double *phip, *thetap; - register double *xp, *yp; +int pcos2x( + struct prjprm *prj, + int nphi, + int ntheta, + int spt, + int sxy, + const double phi[], + const double theta[], + double x[], + double y[], + int stat[]) - /* Initialize. */ +{ + // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; - if (prj->flag != PCO) { + + int status; + if (abs(prj->flag) != PCO) { if ((status = pcoset(prj))) return status; } + int mphi, mtheta; if (ntheta > 0) { mphi = nphi; mtheta = ntheta; @@ -6149,40 +6525,53 @@ int stat[]; } - /* Do phi dependence. */ - phip = phi; - rowoff = 0; - rowlen = nphi*sxy; - for (iphi = 0; iphi < nphi; iphi++, rowoff += sxy, phip += spt) { - xp = x + rowoff; - for (itheta = 0; itheta < mtheta; itheta++) { + // Do phi dependence. + const double *phip = phi; + int rowoff = 0; + int rowlen = nphi*sxy; + for (int iphi = 0; iphi < nphi; iphi++, rowoff += sxy, phip += spt) { + double *xp = x + rowoff; + for (int itheta = 0; itheta < mtheta; itheta++) { *xp = *phip; xp += rowlen; } } - /* Do theta dependence. */ - thetap = theta; - xp = x; - yp = y; - statp = stat; - for (itheta = 0; itheta < ntheta; itheta++, thetap += spt) { - therad = (*thetap)*D2R; - sincosd(*thetap, &sinthe, &costhe); - - for (iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy) { - if (sinthe == 0.0) { + // Do theta dependence. + const double *thetap = theta; + double *xp = x; + double *yp = y; + int *statp = stat; + for (int itheta = 0; itheta < ntheta; itheta++, thetap += spt) { + if (*thetap == 0.0) { + for (int iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy, statp++) { *xp = prj->w[0]*(*xp) - prj->x0; *yp = -prj->y0; - } else { - alpha = (*xp)*sinthe; - cotthe = costhe/sinthe; - *xp = prj->r0*cotthe*sind(alpha) - prj->x0; - *yp = prj->r0*(cotthe*(1.0 - cosd(alpha)) + therad) - prj->y0; + *statp = 0; + } + + } else if (fabs(*thetap) < 1.0e-4) { + // To avoid cot(theta) blowing up near theta == 0. + for (int iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy, statp++) { + *xp = prj->w[0]*(*xp)*cosd(*thetap) - prj->x0; + *yp = (prj->w[0] + prj->w[3]*(*xp)*(*xp))*(*thetap) - prj->y0; + *statp = 0; } - *(statp++) = 0; + } else { + double therad = (*thetap)*D2R; + double sinthe, costhe; + sincosd(*thetap, &sinthe, &costhe); + + for (int iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy, statp++) { + double sinpsi, cospsi; + sincosd((*xp)*sinthe, &sinpsi, &cospsi); + double cotthe = costhe/sinthe; + *xp = prj->r0*cotthe*sinpsi - prj->x0; + *yp = prj->r0*(cotthe*(1.0 - cospsi) + therad) - prj->y0; + *statp = 0; + } } } @@ -6208,14 +6597,12 @@ int stat[]; * prj->prjs2x Pointer to tscs2x(). *===========================================================================*/ -int tscset(prj) - -struct prjprm *prj; +int tscset(struct prjprm *prj) { if (prj == 0x0) return PRJERR_NULL_POINTER; + if (prj->flag == -TSC) return 0; - prj->flag = TSC; strcpy(prj->code, "TSC"); strcpy(prj->name, "tangential spherical cube"); @@ -6239,33 +6626,35 @@ struct prjprm *prj; prj->prjx2s = tscx2s; prj->prjs2x = tscs2x; + prj->flag = (prj->flag == 1) ? -TSC : TSC; + return prjoff(prj, 0.0, 0.0); } -/*--------------------------------------------------------------------------*/ - -int tscx2s(prj, nx, ny, sxy, spt, x, y, phi, theta, stat) +//---------------------------------------------------------------------------- -struct prjprm *prj; -int nx, ny, sxy, spt; -const double x[], y[]; -double phi[], theta[]; -int stat[]; +int tscx2s( + struct prjprm *prj, + int nx, + int ny, + int sxy, + int spt, + const double x[], + const double y[], + double phi[], + double theta[], + int stat[]) { - int mx, my, rowlen, rowoff, status; - double l, m, n, xf, yf; - register int ix, iy, *statp; - register const double *xp, *yp; - register double *phip, *thetap; - - - /* Initialize. */ + // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; - if (prj->flag != TSC) { + + int status; + if (abs(prj->flag) != TSC) { if ((status = tscset(prj))) return status; } + int mx, my; if (ny > 0) { mx = nx; my = ny; @@ -6278,38 +6667,38 @@ int stat[]; status = 0; - /* Do x dependence. */ - xp = x; - rowoff = 0; - rowlen = nx*spt; - for (ix = 0; ix < nx; ix++, rowoff += spt, xp += sxy) { - xf = (*xp + prj->x0)*prj->w[1]; + // Do x dependence. + const double *xp = x; + int rowoff = 0; + int rowlen = nx*spt; + for (int ix = 0; ix < nx; ix++, rowoff += spt, xp += sxy) { + double xf = (*xp + prj->x0)*prj->w[1]; - phip = phi + rowoff; - for (iy = 0; iy < my; iy++) { + double *phip = phi + rowoff; + for (int iy = 0; iy < my; iy++) { *phip = xf; phip += rowlen; } } - /* Do y dependence. */ - yp = y; - phip = phi; - thetap = theta; - statp = stat; - for (iy = 0; iy < ny; iy++, yp += sxy) { - yf = (*yp + prj->y0)*prj->w[1]; + // Do y dependence. + const double *yp = y; + double *phip = phi; + double *thetap = theta; + int *statp = stat; + for (int iy = 0; iy < ny; iy++, yp += sxy) { + double yf = (*yp + prj->y0)*prj->w[1]; - for (ix = 0; ix < mx; ix++, phip += spt, thetap += spt) { - xf = *phip; + for (int ix = 0; ix < mx; ix++, phip += spt, thetap += spt, statp++) { + double xf = *phip; - /* Check bounds. */ + // Bounds checking. if (fabs(xf) <= 1.0) { if (fabs(yf) > 3.0) { *phip = 0.0; *thetap = 0.0; - *(statp++) = 1; + *statp = 1; if (!status) status = PRJERR_BAD_PIX_SET("tscx2s"); continue; } @@ -6317,48 +6706,49 @@ int stat[]; if (fabs(xf) > 7.0 || fabs(yf) > 1.0) { *phip = 0.0; *thetap = 0.0; - *(statp++) = 1; + *statp = 1; if (!status) status = PRJERR_BAD_PIX_SET("tscx2s"); continue; } } - /* Map negative faces to the other side. */ + // Map negative faces to the other side. if (xf < -1.0) xf += 8.0; - /* Determine the face. */ + // Determine the face. + double l, m, n; if (xf > 5.0) { - /* face = 4 */ + // face = 4 xf = xf - 6.0; m = -1.0/sqrt(1.0 + xf*xf + yf*yf); l = -m*xf; n = -m*yf; } else if (xf > 3.0) { - /* face = 3 */ + // face = 3 xf = xf - 4.0; l = -1.0/sqrt(1.0 + xf*xf + yf*yf); m = l*xf; n = -l*yf; } else if (xf > 1.0) { - /* face = 2 */ + // face = 2 xf = xf - 2.0; m = 1.0/sqrt(1.0 + xf*xf + yf*yf); l = -m*xf; n = m*yf; } else if (yf > 1.0) { - /* face = 0 */ + // face = 0 yf = yf - 2.0; n = 1.0/sqrt(1.0 + xf*xf + yf*yf); l = -n*yf; m = n*xf; } else if (yf < -1.0) { - /* face = 5 */ + // face = 5 yf = yf + 2.0; n = -1.0/sqrt(1.0 + xf*xf + yf*yf); l = -n*yf; m = -n*xf; } else { - /* face = 1 */ + // face = 1 l = 1.0/sqrt(1.0 + xf*xf + yf*yf); m = l*xf; n = l*yf; @@ -6371,37 +6761,45 @@ int stat[]; } *thetap = asind(n); - *(statp++) = 0; + *statp = 0; } } + + // Do bounds checking on the native coordinates. + if (prj->bounds&4 && prjbchk(1.0e-13, nx, my, spt, phi, theta, stat)) { + if (!status) status = PRJERR_BAD_PIX_SET("tscx2s"); + } + return status; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- -int tscs2x(prj, nphi, ntheta, spt, sxy, phi, theta, x, y, stat) - -struct prjprm *prj; -int nphi, ntheta, spt, sxy; -const double phi[], theta[]; -double x[], y[]; -int stat[]; +int tscs2x( + struct prjprm *prj, + int nphi, + int ntheta, + int spt, + int sxy, + const double phi[], + const double theta[], + double x[], + double y[], + int stat[]) { - int face, mphi, mtheta, rowlen, rowoff, status; - double cosphi, costhe, l, m, n, sinphi, sinthe, x0, xf, y0, yf, zeta; const double tol = 1.0e-12; - register int iphi, istat, itheta, *statp; - register const double *phip, *thetap; - register double *xp, *yp; - /* Initialize. */ + // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; - if (prj->flag != TSC) { + + int status; + if (abs(prj->flag) != TSC) { if ((status = tscset(prj))) return status; } + int mphi, mtheta; if (ntheta > 0) { mphi = nphi; mtheta = ntheta; @@ -6414,16 +6812,17 @@ int stat[]; status = 0; - /* Do phi dependence. */ - phip = phi; - rowoff = 0; - rowlen = nphi*sxy; - for (iphi = 0; iphi < nphi; iphi++, rowoff += sxy, phip += spt) { + // Do phi dependence. + const double *phip = phi; + int rowoff = 0; + int rowlen = nphi*sxy; + for (int iphi = 0; iphi < nphi; iphi++, rowoff += sxy, phip += spt) { + double sinphi, cosphi; sincosd(*phip, &sinphi, &cosphi); - xp = x + rowoff; - yp = y + rowoff; - for (itheta = 0; itheta < mtheta; itheta++) { + double *xp = x + rowoff; + double *yp = y + rowoff; + for (int itheta = 0; itheta < mtheta; itheta++) { *xp = cosphi; *yp = sinphi; xp += rowlen; @@ -6432,21 +6831,22 @@ int stat[]; } - /* Do theta dependence. */ - thetap = theta; - xp = x; - yp = y; - statp = stat; - for (itheta = 0; itheta < ntheta; itheta++, thetap += spt) { + // Do theta dependence. + const double *thetap = theta; + double *xp = x; + double *yp = y; + int *statp = stat; + for (int itheta = 0; itheta < ntheta; itheta++, thetap += spt) { + double sinthe, costhe; sincosd(*thetap, &sinthe, &costhe); - for (iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy) { - l = costhe*(*xp); - m = costhe*(*yp); - n = sinthe; + for (int iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy, statp++) { + double l = costhe*(*xp); + double m = costhe*(*yp); + double n = sinthe; - face = 0; - zeta = n; + int face = 0; + double zeta = n; if (l > zeta) { face = 1; zeta = l; @@ -6468,6 +6868,7 @@ int stat[]; zeta = -n; } + double xf, yf, x0, y0; switch (face) { case 1: xf = m/zeta; @@ -6500,7 +6901,7 @@ int stat[]; y0 = -2.0; break; default: - /* face == 0 */ + // face == 0 xf = m/zeta; yf = -l/zeta; x0 = 0.0; @@ -6508,7 +6909,7 @@ int stat[]; break; } - istat = 0; + int istat = 0; if (fabs(xf) > 1.0) { if (fabs(xf) > 1.0+tol) { istat = 1; @@ -6526,7 +6927,7 @@ int stat[]; *xp = prj->w[0]*(xf + x0) - prj->x0; *yp = prj->w[0]*(yf + y0) - prj->y0; - *(statp++) = istat; + *statp = istat; } } @@ -6552,14 +6953,12 @@ int stat[]; * prj->prjs2x Pointer to cscs2x(). *===========================================================================*/ -int cscset(prj) - -struct prjprm *prj; +int cscset(struct prjprm *prj) { if (prj == 0x0) return PRJERR_NULL_POINTER; + if (prj->flag == -CSC) return 0; - prj->flag = CSC; strcpy(prj->code, "CSC"); strcpy(prj->name, "COBE quadrilateralized spherical cube"); @@ -6583,27 +6982,26 @@ struct prjprm *prj; prj->prjx2s = cscx2s; prj->prjs2x = cscs2x; + prj->flag = (prj->flag == 1) ? -CSC : CSC; + return prjoff(prj, 0.0, 0.0); } -/*--------------------------------------------------------------------------*/ - -int cscx2s(prj, nx, ny, sxy, spt, x, y, phi, theta, stat) +//---------------------------------------------------------------------------- -struct prjprm *prj; -int nx, ny, sxy, spt; -const double x[], y[]; -double phi[], theta[]; -int stat[]; +int cscx2s( + struct prjprm *prj, + int nx, + int ny, + int sxy, + int spt, + const double x[], + const double y[], + double phi[], + double theta[], + int stat[]) { - int face, mx, my, rowlen, rowoff, status; - double l, m, n, t; - register int ix, iy, *statp; - register const double *xp, *yp; - register double *phip, *thetap; - - float chi, psi, xf, xx, yf, yy, z0, z1, z2, z3, z4, z5, z6; const float p00 = -0.27292696f; const float p10 = -0.07629969f; const float p20 = -0.22797056f; @@ -6633,12 +7031,15 @@ int stat[]; const float p15 = 0.52032238f; const float p06 = 0.14381585f; - /* Initialize. */ + // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; - if (prj->flag != CSC) { + + int status; + if (abs(prj->flag) != CSC) { if ((status = cscset(prj))) return status; } + int mx, my; if (ny > 0) { mx = nx; my = ny; @@ -6651,38 +7052,38 @@ int stat[]; status = 0; - /* Do x dependence. */ - xp = x; - rowoff = 0; - rowlen = nx*spt; - for (ix = 0; ix < nx; ix++, rowoff += spt, xp += sxy) { - xf = (float)((*xp + prj->x0)*prj->w[1]); + // Do x dependence. + const double *xp = x; + int rowoff = 0; + int rowlen = nx*spt; + for (int ix = 0; ix < nx; ix++, rowoff += spt, xp += sxy) { + float xf = (float)((*xp + prj->x0)*prj->w[1]); - phip = phi + rowoff; - for (iy = 0; iy < my; iy++) { + double *phip = phi + rowoff; + for (int iy = 0; iy < my; iy++) { *phip = xf; phip += rowlen; } } - /* Do y dependence. */ - yp = y; - phip = phi; - thetap = theta; - statp = stat; - for (iy = 0; iy < ny; iy++, yp += sxy) { - yf = (float)((*yp + prj->y0)*prj->w[1]); + // Do y dependence. + const double *yp = y; + double *phip = phi; + double *thetap = theta; + int *statp = stat; + for (int iy = 0; iy < ny; iy++, yp += sxy) { + float yf = (float)((*yp + prj->y0)*prj->w[1]); - for (ix = 0; ix < mx; ix++, phip += spt, thetap += spt) { - xf = (float)(*phip); + for (int ix = 0; ix < mx; ix++, phip += spt, thetap += spt, statp++) { + float xf = (float)(*phip); - /* Check bounds. */ + // Bounds checking. if (fabs((double)xf) <= 1.0) { if (fabs((double)yf) > 3.0) { *phip = 0.0; *thetap = 0.0; - *(statp++) = 1; + *statp = 1; if (!status) status = PRJERR_BAD_PIX_SET("cscx2s"); continue; } @@ -6690,16 +7091,17 @@ int stat[]; if (fabs((double)xf) > 7.0 || fabs((double)yf) > 1.0) { *phip = 0.0; *thetap = 0.0; - *(statp++) = 1; + *statp = 1; if (!status) status = PRJERR_BAD_PIX_SET("cscx2s"); continue; } } - /* Map negative faces to the other side. */ + // Map negative faces to the other side. if (xf < -1.0f) xf += 8.0f; - /* Determine the face. */ + // Determine the face. + int face = 0; if (xf > 5.0f) { face = 4; xf = xf - 6.0f; @@ -6719,9 +7121,10 @@ int stat[]; face = 1; } - xx = xf*xf; - yy = yf*yf; + float xx = xf*xf; + float yy = yf*yf; + float z0, z1, z2, z3, z4, z5, z6; z0 = p00 + xx*(p10 + xx*(p20 + xx*(p30 + xx*(p40 + xx*(p50 + xx*(p60)))))); z1 = p01 + xx*(p11 + xx*(p21 + xx*(p31 + xx*(p41 + xx*(p51))))); @@ -6731,6 +7134,7 @@ int stat[]; z5 = p05 + xx*(p15); z6 = p06; + float chi; chi = z0 + yy*(z1 + yy*(z2 + yy*(z3 + yy*(z4 + yy*(z5 + yy*z6))))); chi = xf + xf*(1.0f - xx)*chi; @@ -6743,10 +7147,12 @@ int stat[]; z5 = p05 + yy*(p15); z6 = p06; + float psi; psi = z0 + xx*(z1 + xx*(z2 + xx*(z3 + xx*(z4 + xx*(z5 + xx*z6))))); psi = yf + yf*(1.0f - yy)*psi; - t = 1.0/sqrt((double)(chi*chi + psi*psi) + 1.0); + double l, m, n; + double t = 1.0/sqrt((double)(chi*chi + psi*psi) + 1.0); switch (face) { case 1: l = t; @@ -6774,7 +7180,7 @@ int stat[]; m = -chi*n; break; default: - /* face == 0 */ + // face == 0 n = t; l = -psi*n; m = chi*n; @@ -6788,33 +7194,36 @@ int stat[]; } *thetap = asind(n); - *(statp++) = 0; + *statp = 0; } } + + // Do bounds checking on the native coordinates. + if (prj->bounds&4 && prjbchk(1.0e-13, nx, my, spt, phi, theta, stat)) { + if (!status) status = PRJERR_BAD_PIX_SET("cscx2s"); + } + return status; } -/*--------------------------------------------------------------------------*/ - -int cscs2x(prj, nphi, ntheta, spt, sxy, phi, theta, x, y, stat) +//---------------------------------------------------------------------------- -struct prjprm *prj; -int nphi, ntheta, spt, sxy; -const double phi[], theta[]; -double x[], y[]; -int stat[]; +int cscs2x( + struct prjprm *prj, + int nphi, + int ntheta, + int spt, + int sxy, + const double phi[], + const double theta[], + double x[], + double y[], + int stat[]) { - int face, mphi, mtheta, rowlen, rowoff, status; - double cosphi, costhe, eta, l, m, n, sinphi, sinthe, xi, zeta; - const float tol = 1.0e-7; - register int iphi, istat, itheta, *statp; - register const double *phip, *thetap; - register double *xp, *yp; - - float chi, chi2, chi2psi2, chi4, chipsi, psi, psi2, psi4, chi2co, psi2co, - x0, xf, y0, yf; + const double tol = 1.0e-7; + const float gstar = 1.37484847732f; const float mm = 0.004869491981f; const float gamma = -0.13161671474f; @@ -6829,12 +7238,15 @@ int stat[]; const float c02 = 0.106959469314f; - /* Initialize. */ + // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; - if (prj->flag != CSC) { + + int status; + if (abs(prj->flag) != CSC) { if ((status = cscset(prj))) return status; } + int mphi, mtheta; if (ntheta > 0) { mphi = nphi; mtheta = ntheta; @@ -6847,16 +7259,17 @@ int stat[]; status = 0; - /* Do phi dependence. */ - phip = phi; - rowoff = 0; - rowlen = nphi*sxy; - for (iphi = 0; iphi < nphi; iphi++, rowoff += sxy, phip += spt) { + // Do phi dependence. + const double *phip = phi; + int rowoff = 0; + int rowlen = nphi*sxy; + for (int iphi = 0; iphi < nphi; iphi++, rowoff += sxy, phip += spt) { + double sinphi, cosphi; sincosd(*phip, &sinphi, &cosphi); - xp = x + rowoff; - yp = y + rowoff; - for (itheta = 0; itheta < mtheta; itheta++) { + double *xp = x + rowoff; + double *yp = y + rowoff; + for (int itheta = 0; itheta < mtheta; itheta++) { *xp = cosphi; *yp = sinphi; xp += rowlen; @@ -6865,21 +7278,22 @@ int stat[]; } - /* Do theta dependence. */ - thetap = theta; - xp = x; - yp = y; - statp = stat; - for (itheta = 0; itheta < ntheta; itheta++, thetap += spt) { + // Do theta dependence. + const double *thetap = theta; + double *xp = x; + double *yp = y; + int *statp = stat; + for (int itheta = 0; itheta < ntheta; itheta++, thetap += spt) { + double sinthe, costhe; sincosd(*thetap, &sinthe, &costhe); - for (iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy) { - l = costhe*(*xp); - m = costhe*(*yp); - n = sinthe; + for (int iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy, statp++) { + double l = costhe*(*xp); + double m = costhe*(*yp); + double n = sinthe; - face = 0; - zeta = n; + int face = 0; + double zeta = n; if (l > zeta) { face = 1; zeta = l; @@ -6901,6 +7315,8 @@ int stat[]; zeta = -n; } + double eta, xi; + float x0, y0; switch (face) { case 1: xi = m; @@ -6933,7 +7349,7 @@ int stat[]; y0 = -2.0; break; default: - /* face == 0 */ + // face == 0 xi = m; eta = -l; x0 = 0.0; @@ -6941,20 +7357,21 @@ int stat[]; break; } - chi = (float)( xi/zeta); - psi = (float)(eta/zeta); + float chi = (float)( xi/zeta); + float psi = (float)(eta/zeta); - chi2 = chi*chi; - psi2 = psi*psi; - chi2co = 1.0f - chi2; - psi2co = 1.0f - psi2; + float chi2 = chi*chi; + float psi2 = psi*psi; + float chi2co = 1.0f - chi2; + float psi2co = 1.0f - psi2; - /* Avoid floating underflows. */ - chipsi = (float)fabs((double)(chi*psi)); - chi4 = (chi2 > 1.0e-16f) ? chi2*chi2 : 0.0f; - psi4 = (psi2 > 1.0e-16f) ? psi2*psi2 : 0.0f; - chi2psi2 = (chipsi > 1.0e-16f) ? chi2*psi2 : 0.0f; + // Avoid floating underflows. + float chipsi = (float)fabs((double)(chi*psi)); + float chi4 = (chi2 > 1.0e-16f) ? chi2*chi2 : 0.0f; + float psi4 = (psi2 > 1.0e-16f) ? psi2*psi2 : 0.0f; + float chi2psi2 = (chipsi > 1.0e-16f) ? chi2*psi2 : 0.0f; + float xf, yf; xf = chi*(chi2 + chi2co*(gstar + psi2*(gamma*chi2co + mm*chi2 + psi2co*(c00 + c10*chi2 + c01*psi2 + c11*chi2psi2 + c20*chi4 + c02*psi4)) + chi2*(omega1 - chi2co*(d0 + d1*chi2)))); @@ -6962,7 +7379,7 @@ int stat[]; chi2co*(c00 + c10*psi2 + c01*chi2 + c11*chi2psi2 + c20*psi4 + c02*chi4)) + psi2*(omega1 - psi2co*(d0 + d1*psi2)))); - istat = 0; + int istat = 0; if (fabs((double)xf) > 1.0) { if (fabs((double)xf) > 1.0+tol) { istat = 1; @@ -6980,7 +7397,7 @@ int stat[]; *xp = prj->w[0]*(xf + x0) - prj->x0; *yp = prj->w[0]*(yf + y0) - prj->y0; - *(statp++) = istat; + *statp = istat; } } @@ -7006,14 +7423,12 @@ int stat[]; * prj->prjs2x Pointer to qscs2x(). *===========================================================================*/ -int qscset(prj) - -struct prjprm *prj; +int qscset(struct prjprm *prj) { if (prj == 0x0) return PRJERR_NULL_POINTER; + if (prj->flag == -QSC) return 0; - prj->flag = QSC; strcpy(prj->code, "QSC"); strcpy(prj->name, "quadrilateralized spherical cube"); @@ -7037,34 +7452,37 @@ struct prjprm *prj; prj->prjx2s = qscx2s; prj->prjs2x = qscs2x; + prj->flag = (prj->flag == 1) ? -QSC : QSC; + return prjoff(prj, 0.0, 0.0); } -/*--------------------------------------------------------------------------*/ - -int qscx2s(prj, nx, ny, sxy, spt, x, y, phi, theta, stat) +//---------------------------------------------------------------------------- -struct prjprm *prj; -int nx, ny, sxy, spt; -const double x[], y[]; -double phi[], theta[]; -int stat[]; +int qscx2s( + struct prjprm *prj, + int nx, + int ny, + int sxy, + int spt, + const double x[], + const double y[], + double phi[], + double theta[], + int stat[]) { - int direct, face, mx, my, rowlen, rowoff, status; - double cosw, l, m, n, omega, sinw, tau, xf, yf, w, zeco, zeta; const double tol = 1.0e-12; - register int ix, iy, *statp; - register const double *xp, *yp; - register double *phip, *thetap; - - /* Initialize. */ + // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; - if (prj->flag != QSC) { + + int status; + if (abs(prj->flag) != QSC) { if ((status = qscset(prj))) return status; } + int mx, my; if (ny > 0) { mx = nx; my = ny; @@ -7077,38 +7495,38 @@ int stat[]; status = 0; - /* Do x dependence. */ - xp = x; - rowoff = 0; - rowlen = nx*spt; - for (ix = 0; ix < nx; ix++, rowoff += spt, xp += sxy) { - xf = (*xp + prj->x0)*prj->w[1]; + // Do x dependence. + const double *xp = x; + int rowoff = 0; + int rowlen = nx*spt; + for (int ix = 0; ix < nx; ix++, rowoff += spt, xp += sxy) { + double xf = (*xp + prj->x0)*prj->w[1]; - phip = phi + rowoff; - for (iy = 0; iy < my; iy++) { + double *phip = phi + rowoff; + for (int iy = 0; iy < my; iy++) { *phip = xf; phip += rowlen; } } - /* Do y dependence. */ - yp = y; - phip = phi; - thetap = theta; - statp = stat; - for (iy = 0; iy < ny; iy++, yp += sxy) { - yf = (*yp + prj->y0)*prj->w[1]; + // Do y dependence. + const double *yp = y; + double *phip = phi; + double *thetap = theta; + int *statp = stat; + for (int iy = 0; iy < ny; iy++, yp += sxy) { + double yf = (*yp + prj->y0)*prj->w[1]; - for (ix = 0; ix < mx; ix++, phip += spt, thetap += spt) { - xf = *phip; + for (int ix = 0; ix < mx; ix++, phip += spt, thetap += spt, statp++) { + double xf = *phip; - /* Check bounds. */ + // Bounds checking. if (fabs(xf) <= 1.0) { if (fabs(yf) > 3.0) { *phip = 0.0; *thetap = 0.0; - *(statp++) = 1; + *statp = 1; if (!status) status = PRJERR_BAD_PIX_SET("qscx2s"); continue; } @@ -7116,16 +7534,17 @@ int stat[]; if (fabs(xf) > 7.0 || fabs(yf) > 1.0) { *phip = 0.0; *thetap = 0.0; - *(statp++) = 1; + *statp = 1; if (!status) status = PRJERR_BAD_PIX_SET("qscx2s"); continue; } } - /* Map negative faces to the other side. */ + // Map negative faces to the other side. if (xf < -1.0) xf += 8.0; - /* Determine the face. */ + int face = 0; + // Determine the face. if (xf > 5.0) { face = 4; xf -= 6.0; @@ -7145,7 +7564,8 @@ int stat[]; face = 1; } - direct = (fabs(xf) > fabs(yf)); + double omega, tau, w, zeta, zeco; + int direct = (fabs(xf) > fabs(yf)); if (direct) { if (xf == 0.0) { omega = 0.0; @@ -7167,6 +7587,7 @@ int stat[]; zeco = 0.0; } else { w = 15.0*xf/yf; + double sinw, cosw; sincosd(w, &sinw, &cosw); omega = sinw/(cosw - SQRT2INV); tau = 1.0 + omega*omega; @@ -7179,7 +7600,7 @@ int stat[]; if (zeta < -1.0-tol) { *phip = 0.0; *thetap = 0.0; - *(statp++) = 1; + *statp = 1; if (!status) status = PRJERR_BAD_PIX_SET("qscx2s"); continue; } @@ -7191,6 +7612,7 @@ int stat[]; w = sqrt(zeco*(2.0-zeco)/tau); } + double l, m, n; switch (face) { case 1: l = zeta; @@ -7253,7 +7675,7 @@ int stat[]; } break; default: - /* face == 0 */ + // face == 0 n = zeta; if (direct) { m = w; @@ -7274,39 +7696,45 @@ int stat[]; } *thetap = asind(n); - *(statp++) = 0; + *statp = 0; } } + + // Do bounds checking on the native coordinates. + if (prj->bounds&4 && prjbchk(1.0e-13, nx, my, spt, phi, theta, stat)) { + if (!status) status = PRJERR_BAD_PIX_SET("qscx2s"); + } + return status; } -/*--------------------------------------------------------------------------*/ - -int qscs2x(prj, nphi, ntheta, spt, sxy, phi, theta, x, y, stat) +//---------------------------------------------------------------------------- -struct prjprm *prj; -int nphi, ntheta, spt, sxy; -const double phi[], theta[]; -double x[], y[]; -int stat[]; +int qscs2x( + struct prjprm *prj, + int nphi, + int ntheta, + int spt, + int sxy, + const double phi[], + const double theta[], + double x[], + double y[], + int stat[]) { - int face, mphi, mtheta, rowlen, rowoff, status; - double cosphi, costhe, eta, l, m, n, omega, p, sinphi, sinthe, t, tau, x0, - xf, xi, y0, yf, zeco, zeta; const double tol = 1.0e-12; - register int iphi, istat, itheta, *statp; - register const double *phip, *thetap; - register double *xp, *yp; - - /* Initialize. */ + // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; - if (prj->flag != QSC) { + + int status; + if (abs(prj->flag) != QSC) { if ((status = qscset(prj))) return status; } + int mphi, mtheta; if (ntheta > 0) { mphi = nphi; mtheta = ntheta; @@ -7319,16 +7747,17 @@ int stat[]; status = 0; - /* Do phi dependence. */ - phip = phi; - rowoff = 0; - rowlen = nphi*sxy; - for (iphi = 0; iphi < nphi; iphi++, rowoff += sxy, phip += spt) { + // Do phi dependence. + const double *phip = phi; + int rowoff = 0; + int rowlen = nphi*sxy; + for (int iphi = 0; iphi < nphi; iphi++, rowoff += sxy, phip += spt) { + double sinphi, cosphi; sincosd(*phip, &sinphi, &cosphi); - xp = x + rowoff; - yp = y + rowoff; - for (itheta = 0; itheta < mtheta; itheta++) { + double *xp = x + rowoff; + double *yp = y + rowoff; + for (int itheta = 0; itheta < mtheta; itheta++) { *xp = cosphi; *yp = sinphi; xp += rowlen; @@ -7337,28 +7766,29 @@ int stat[]; } - /* Do theta dependence. */ - thetap = theta; - xp = x; - yp = y; - statp = stat; - for (itheta = 0; itheta < ntheta; itheta++, thetap += spt) { + // Do theta dependence. + const double *thetap = theta; + double *xp = x; + double *yp = y; + int *statp = stat; + for (int itheta = 0; itheta < ntheta; itheta++, thetap += spt) { + double sinthe, costhe; sincosd(*thetap, &sinthe, &costhe); - for (iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy) { + for (int iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy, statp++) { if (fabs(*thetap) == 90.0) { *xp = -prj->x0; *yp = copysign(2.0*prj->w[0], *thetap) - prj->y0; - *(statp++) = 0; + *statp = 0; continue; } - l = costhe*(*xp); - m = costhe*(*yp); - n = sinthe; + double l = costhe*(*xp); + double m = costhe*(*yp); + double n = sinthe; - face = 0; - zeta = n; + int face = 0; + double zeta = n; if (l > zeta) { face = 1; zeta = l; @@ -7380,16 +7810,17 @@ int stat[]; zeta = -n; } - zeco = 1.0 - zeta; + double zeco = 1.0 - zeta; + double xi, eta, x0, y0; switch (face) { case 1: xi = m; eta = n; if (zeco < 1.0e-8) { - /* Small angle formula. */ - t = (*thetap)*D2R; - p = atan2(*yp, *xp); + // Small angle formula. + double t = (*thetap)*D2R; + double p = atan2(*yp, *xp); zeco = (p*p + t*t)/2.0; } x0 = 0.0; @@ -7399,9 +7830,9 @@ int stat[]; xi = -l; eta = n; if (zeco < 1.0e-8) { - /* Small angle formula. */ - t = (*thetap)*D2R; - p = atan2(*yp, *xp) - PI/2.0; + // Small angle formula. + double t = (*thetap)*D2R; + double p = atan2(*yp, *xp) - PI/2.0; zeco = (p*p + t*t)/2.0; } x0 = 2.0; @@ -7411,9 +7842,9 @@ int stat[]; xi = -m; eta = n; if (zeco < 1.0e-8) { - /* Small angle formula. */ - t = (*thetap)*D2R; - p = atan2(*yp, *xp); + // Small angle formula. + double t = (*thetap)*D2R; + double p = atan2(*yp, *xp); p -= copysign(PI, p); zeco = (p*p + t*t)/2.0; } @@ -7424,9 +7855,9 @@ int stat[]; xi = l; eta = n; if (zeco < 1.0e-8) { - /* Small angle formula. */ - t = (*thetap)*D2R; - p = atan2(*yp, *xp) + PI/2.0; + // Small angle formula. + double t = (*thetap)*D2R; + double p = atan2(*yp, *xp) + PI/2.0; zeco = (p*p + t*t)/2.0; } x0 = 6; @@ -7436,20 +7867,20 @@ int stat[]; xi = m; eta = l; if (zeco < 1.0e-8) { - /* Small angle formula. */ - t = (*thetap + 90.0)*D2R; + // Small angle formula. + double t = (*thetap + 90.0)*D2R; zeco = t*t/2.0; } x0 = 0.0; y0 = -2; break; default: - /* face == 0 */ + // face == 0 xi = m; eta = -l; if (zeco < 1.0e-8) { - /* Small angle formula. */ - t = (90.0 - *thetap)*D2R; + // Small angle formula. + double t = (90.0 - *thetap)*D2R; zeco = t*t/2.0; } x0 = 0.0; @@ -7457,8 +7888,9 @@ int stat[]; break; } - xf = 0.0; - yf = 0.0; + double omega, tau; + double xf = 0.0; + double yf = 0.0; if (xi != 0.0 || eta != 0.0) { if (-xi > fabs(eta)) { omega = eta/xi; @@ -7483,7 +7915,7 @@ int stat[]; } } - istat = 0; + int istat = 0; if (fabs(xf) > 1.0) { if (fabs(xf) > 1.0+tol) { istat = 1; @@ -7501,7 +7933,7 @@ int stat[]; *xp = prj->w[0]*(xf + x0) - prj->x0; *yp = prj->w[0]*(yf + y0) - prj->y0; - *(statp++) = istat; + *statp = istat; } } @@ -7541,14 +7973,12 @@ int stat[]; * prj->prjs2x Pointer to hpxs2x(). *===========================================================================*/ -int hpxset(prj) - -struct prjprm *prj; +int hpxset(struct prjprm *prj) { if (prj == 0x0) return PRJERR_NULL_POINTER; + if (prj->flag == -HPX) return 0; - prj->flag = HPX; strcpy(prj->code, "HPX"); if (undefined(prj->pv[1])) prj->pv[1] = 4.0; @@ -7591,35 +8021,35 @@ struct prjprm *prj; prj->prjx2s = hpxx2s; prj->prjs2x = hpxs2x; + prj->flag = (prj->flag == 1) ? -HPX : HPX; + return prjoff(prj, 0.0, 0.0); } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- -int hpxx2s(prj, nx, ny, sxy, spt, x, y, phi, theta, stat) - -struct prjprm *prj; -int nx, ny, sxy, spt; -const double x[], y[]; -double phi[], theta[]; -int stat[]; +int hpxx2s( + struct prjprm *prj, + int nx, + int ny, + int sxy, + int spt, + const double x[], + const double y[], + double phi[], + double theta[], + int stat[]) { - int h, mx, my, offset, rowlen, rowoff, status; - double absy, s, sigma, t, yr; - const double slim = prj->w[6] + 1e-12; - const double ylim = prj->w[9] * prj->w[4]; - register int istat, ix, iy, *statp; - register const double *xp, *yp; - register double *phip, *thetap; - - - /* Initialize. */ + // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; - if (prj->flag != HPX) { + + int status; + if (abs(prj->flag) != HPX) { if ((status = hpxset(prj))) return status; } + int mx, my; if (ny > 0) { mx = nx; my = ny; @@ -7632,20 +8062,21 @@ int stat[]; status = 0; - /* Do x dependence. */ - xp = x; - rowoff = 0; - rowlen = nx*spt; - for (ix = 0; ix < nx; ix++, rowoff += spt, xp += sxy) { - s = prj->w[1] * (*xp + prj->x0); - /* x_c for K odd or theta > 0. */ + // Do x dependence. + const double *xp = x; + int rowoff = 0; + int rowlen = nx*spt; + for (int ix = 0; ix < nx; ix++, rowoff += spt, xp += sxy) { + double s = prj->w[1] * (*xp + prj->x0); + // x_c for K odd or theta > 0. + double t; t = -180.0 + (2.0 * floor((*xp + 180.0) * prj->w[7]) + 1.0) * prj->w[6]; t = prj->w[1] * (*xp - t); - phip = phi + rowoff; - thetap = theta + rowoff; - for (iy = 0; iy < my; iy++) { - /* theta[] is used to hold (x - x_c). */ + double *phip = phi + rowoff; + double *thetap = theta + rowoff; + for (int iy = 0; iy < my; iy++) { + // theta[] is used to hold (x - x_c). *phip = s; *thetap = t; phip += rowlen; @@ -7654,30 +8085,35 @@ int stat[]; } - /* Do y dependence. */ - yp = y; - phip = phi; - thetap = theta; - statp = stat; - for (iy = 0; iy < ny; iy++, yp += sxy) { - yr = prj->w[1]*(*yp + prj->y0); - absy = fabs(yr); + // Do y dependence. + const double *yp = y; + double *phip = phi; + double *thetap = theta; + int *statp = stat; - istat = 0; + double slim = prj->w[6] + 1e-12; + double ylim = prj->w[9] * prj->w[4]; + + for (int iy = 0; iy < ny; iy++, yp += sxy) { + double yr = prj->w[1]*(*yp + prj->y0); + double absy = fabs(yr); + + int istat = 0; if (absy <= prj->w[5]) { - /* Equatorial regime. */ - t = asind(yr/prj->w[3]); - for (ix = 0; ix < mx; ix++, phip += spt, thetap += spt) { + // Equatorial regime. + double t = asind(yr/prj->w[3]); + for (int ix = 0; ix < mx; ix++, phip += spt, thetap += spt, statp++) { *thetap = t; - *(statp++) = 0; + *statp = 0; } } else if (absy <= ylim) { - /* Polar regime. */ - offset = (prj->n || *yp > 0.0) ? 0 : 1; + // Polar regime. + int offset = (prj->n || *yp > 0.0) ? 0 : 1; - sigma = prj->w[4] - absy / prj->w[6]; + double sigma = prj->w[4] - absy / prj->w[6]; + double s, t; if (sigma == 0.0) { s = 1e9; t = 90.0; @@ -7696,10 +8132,10 @@ int stat[]; } if (*yp < 0.0) t = -t; - for (ix = 0; ix < mx; ix++, phip += spt, thetap += spt) { + for (int ix = 0; ix < mx; ix++, phip += spt, thetap += spt, statp++) { if (offset) { - /* Offset the southern polar half-facets for even K. */ - h = (int)floor(*phip / prj->w[6]) + prj->m; + // Offset the southern polar half-facets for even K. + int h = (int)floor(*phip / prj->w[6]) + prj->m; if (h%2) { *thetap -= prj->w[6]; } else { @@ -7707,60 +8143,67 @@ int stat[]; } } - /* Recall that theta[] holds (x - x_c). */ - s *= *thetap; - if (fabs(s) < slim) { - if (s != 0.0) s -= *thetap; - *phip += s; - *thetap = t; - *(statp++) = istat; - } else { - /* Out-of-bounds. */ - *phip = 0.0; - *thetap = 0.0; - *(statp++) = 1; - if (!status) status = PRJERR_BAD_PIX_SET("hpxx2s"); + // Recall that theta[] holds (x - x_c). + double r = s * *thetap; + + // Bounds checking. + if (prj->bounds&2) { + if (slim <= fabs(r)) { + istat = 1; + if (!status) status = PRJERR_BAD_PIX_SET("hpxx2s"); + } } + + if (r != 0.0) r -= *thetap; + *phip += r; + *thetap = t; + *statp = istat; } } else { - /* Beyond latitude range. */ - for (ix = 0; ix < mx; ix++, phip += spt, thetap += spt) { + // Beyond latitude range. + for (int ix = 0; ix < mx; ix++, phip += spt, thetap += spt, statp++) { *phip = 0.0; *thetap = 0.0; - *(statp++) = 1; + *statp = 1; } if (!status) status = PRJERR_BAD_PIX_SET("hpxx2s"); } } + + // Do bounds checking on the native coordinates. + if (prj->bounds&4 && prjbchk(1.0e-12, nx, my, spt, phi, theta, stat)) { + if (!status) status = PRJERR_BAD_PIX_SET("hpxx2s"); + } + return status; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- -int hpxs2x(prj, nphi, ntheta, spt, sxy, phi, theta, x, y, stat) - -struct prjprm *prj; -int nphi, ntheta, spt, sxy; -const double phi[], theta[]; -double x[], y[]; -int stat[]; +int hpxs2x( + struct prjprm *prj, + int nphi, + int ntheta, + int spt, + int sxy, + const double phi[], + const double theta[], + double x[], + double y[], + int stat[]) { - int h, mphi, mtheta, offset, rowlen, rowoff, status; - double abssin, eta, sigma, sinthe, t, xi; - register int iphi, itheta, *statp; - register const double *phip, *thetap; - register double *xp, *yp; - - - /* Initialize. */ + // Initialize. if (prj == 0x0) return PRJERR_NULL_POINTER; - if (prj->flag != HPX) { + + int status; + if (abs(prj->flag) != HPX) { if ((status = hpxset(prj))) return status; } + int mphi, mtheta; if (ntheta > 0) { mphi = nphi; mtheta = ntheta; @@ -7771,21 +8214,22 @@ int stat[]; } - /* Do phi dependence. */ - phip = phi; - rowoff = 0; - rowlen = nphi*sxy; - for (iphi = 0; iphi < nphi; iphi++, rowoff += sxy, phip += spt) { - xi = prj->w[0] * (*phip) - prj->x0; + // Do phi dependence. + const double *phip = phi; + int rowoff = 0; + int rowlen = nphi*sxy; + for (int iphi = 0; iphi < nphi; iphi++, rowoff += sxy, phip += spt) { + double xi = prj->w[0] * (*phip) - prj->x0; - /* phi_c for K odd or theta > 0. */ + // phi_c for K odd or theta > 0. + double t; t = -180.0 + (2.0*floor((*phip+180.0) * prj->w[7]) + 1.0) * prj->w[6]; t = prj->w[0] * (*phip - t); - xp = x + rowoff; - yp = y + rowoff; - for (itheta = 0; itheta < mtheta; itheta++) { - /* y[] is used to hold (phi - phi_c). */ + double *xp = x + rowoff; + double *yp = y + rowoff; + for (int itheta = 0; itheta < mtheta; itheta++) { + // y[] is used to hold (phi - phi_c). *xp = xi; *yp = t; xp += rowlen; @@ -7794,38 +8238,39 @@ int stat[]; } - /* Do theta dependence. */ - thetap = theta; - xp = x; - yp = y; - statp = stat; - for (itheta = 0; itheta < ntheta; itheta++, thetap += spt) { - sinthe = sind(*thetap); - abssin = fabs(sinthe); + // Do theta dependence. + const double *thetap = theta; + double *xp = x; + double *yp = y; + int *statp = stat; + for (int itheta = 0; itheta < ntheta; itheta++, thetap += spt) { + double sinthe = sind(*thetap); + double abssin = fabs(sinthe); + double eta; if (abssin <= prj->w[2]) { - /* Equatorial regime. */ + // Equatorial regime. eta = prj->w[8] * sinthe - prj->y0; - for (iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy) { + for (int iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy, statp++) { *yp = eta; - *(statp++) = 0; + *statp = 0; } } else { - /* Polar regime. */ - offset = (prj->n || *thetap > 0.0) ? 0 : 1; + // Polar regime. + int offset = (prj->n || *thetap > 0.0) ? 0 : 1; - sigma = sqrt(prj->pv[2]*(1.0 - abssin)); - xi = sigma - 1.0; + double sigma = sqrt(prj->pv[2]*(1.0 - abssin)); + double xi = sigma - 1.0; eta = prj->w[9] * (prj->w[4] - sigma); if (*thetap < 0) eta = -eta; eta -= prj->y0; - for (iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy) { + for (int iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy, statp++) { if (offset) { - /* Offset the southern polar half-facets for even K. */ - h = (int)floor((*xp + prj->x0) / prj->w[9]) + prj->m; + // Offset the southern polar half-facets for even K. + int h = (int)floor((*xp + prj->x0) / prj->w[9]) + prj->m; if (h%2) { *yp -= prj->w[9]; } else { @@ -7833,12 +8278,12 @@ int stat[]; } } - /* Recall that y[] holds (phi - phi_c). */ + // Recall that y[] holds (phi - phi_c). *xp += *yp * xi; *yp = eta; - *(statp++) = 0; + *statp = 0; - /* Put the phi = 180 meridian in the expected place. */ + // Put the phi = 180 meridian in the expected place. if (180.0 < *xp) *xp = 360.0 - *xp; } } @@ -7846,3 +8291,357 @@ int stat[]; return 0; } + +/*============================================================================ +* XPH: HEALPix polar, aka "butterfly" projection. +* +* Given and/or returned: +* prj->r0 Reset to 180/pi if 0. +* prj->phi0 Reset to 0.0 if undefined. +* prj->theta0 Reset to 0.0 if undefined. +* +* Returned: +* prj->flag XPH +* prj->code "XPH" +* prj->x0 Fiducial offset in x. +* prj->y0 Fiducial offset in y. +* prj->w[0] r0*(pi/180)/sqrt(2) +* prj->w[1] (180/pi)/r0/sqrt(2) +* prj->w[2] 2/3 +* prj->w[3] tol (= 1e-4) +* prj->w[4] sqrt(2/3)*(180/pi) +* prj->w[5] 90 - tol*sqrt(2/3)*(180/pi) +* prj->w[6] sqrt(3/2)*(pi/180) +* prj->prjx2s Pointer to xphx2s(). +* prj->prjs2x Pointer to xphs2x(). +*===========================================================================*/ + +int xphset(struct prjprm *prj) + +{ + if (prj == 0x0) return PRJERR_NULL_POINTER; + if (prj->flag == -XPH) return 0; + + strcpy(prj->code, "XPH"); + + strcpy(prj->name, "butterfly"); + prj->category = HEALPIX; + prj->pvrange = 0; + prj->simplezen = 0; + prj->equiareal = 1; + prj->conformal = 0; + prj->global = 1; + prj->divergent = 0; + + if (prj->r0 == 0.0) { + prj->r0 = R2D; + prj->w[0] = 1.0; + prj->w[1] = 1.0; + } else { + prj->w[0] = prj->r0*D2R; + prj->w[1] = R2D/prj->r0; + } + + prj->w[0] /= sqrt(2.0); + prj->w[1] /= sqrt(2.0); + prj->w[2] = 2.0/3.0; + prj->w[3] = 1e-4; + prj->w[4] = sqrt(prj->w[2])*R2D; + prj->w[5] = 90.0 - prj->w[3]*prj->w[4]; + prj->w[6] = sqrt(1.5)*D2R; + + prj->prjx2s = xphx2s; + prj->prjs2x = xphs2x; + + prj->flag = (prj->flag == 1) ? -XPH : XPH; + + return prjoff(prj, 0.0, 90.0); +} + +//---------------------------------------------------------------------------- + +int xphx2s( + struct prjprm *prj, + int nx, + int ny, + int sxy, + int spt, + const double x[], + const double y[], + double phi[], + double theta[], + int stat[]) + +{ + const double tol = 1.0e-12; + + // Initialize. + if (prj == 0x0) return PRJERR_NULL_POINTER; + + int status; + if (abs(prj->flag) != XPH) { + if ((status = xphset(prj))) return status; + } + + int mx, my; + if (ny > 0) { + mx = nx; + my = ny; + } else { + mx = 1; + my = 1; + ny = nx; + } + + status = 0; + + + // Do x dependence. + const double *xp = x; + int rowoff = 0; + int rowlen = nx*spt; + for (int ix = 0; ix < nx; ix++, rowoff += spt, xp += sxy) { + double xr = (*xp + prj->x0)*prj->w[1]; + + double *phip = phi + rowoff; + for (int iy = 0; iy < my; iy++) { + *phip = xr; + phip += rowlen; + } + } + + + // Do y dependence. + const double *yp = y; + double *phip = phi; + double *thetap = theta; + int *statp = stat; + for (int iy = 0; iy < ny; iy++, yp += sxy) { + double yr = (*yp + prj->y0)*prj->w[1]; + + for (int ix = 0; ix < mx; ix++, phip += spt, thetap += spt, statp++) { + double xr = *phip; + + double xi1, eta1; + if (xr <= 0.0 && 0.0 < yr) { + xi1 = -xr - yr; + eta1 = xr - yr; + *phip = -180.0; + } else if (xr < 0.0 && yr <= 0.0) { + xi1 = xr - yr; + eta1 = xr + yr; + *phip = -90.0; + } else if (0.0 <= xr && yr < 0.0) { + xi1 = xr + yr; + eta1 = -xr + yr; + *phip = 0.0; + } else { + xi1 = -xr + yr; + eta1 = -xr - yr; + *phip = 90.0; + } + + double xi = xi1 + 45.0; + double eta = eta1 + 90.0; + double abseta = fabs(eta); + + if (abseta <= 90.0) { + if (abseta <= 45.0) { + // Equatorial regime. + *phip += xi; + *thetap = asind(eta/67.5); + int istat = 0; + + // Bounds checking. + if (prj->bounds&2) { + if (45.0+tol < fabs(xi1)) { + istat = 1; + if (!status) status = PRJERR_BAD_PIX_SET("xphx2s"); + } + } + + *statp = istat; + + } else { + // Polar regime. + double sigma = (90.0 - abseta) / 45.0; + + // Ensure an exact result for points on the boundary. + if (xr == 0.0) { + if (yr <= 0.0) { + *phip = 0.0; + } else { + *phip = 180.0; + } + } else if (yr == 0.0) { + if (xr < 0.0) { + *phip = -90.0; + } else { + *phip = 90.0; + } + } else { + *phip += 45.0 + xi1/sigma; + } + + if (sigma < prj->w[3]) { + *thetap = 90.0 - sigma*prj->w[4]; + } else { + *thetap = asind(1.0 - sigma*sigma/3.0); + } + if (eta < 0.0) *thetap = -(*thetap); + + // Bounds checking. + int istat = 0; + if (prj->bounds&2) { + if (eta < -45.0 && eta+90.0+tol < fabs(xi1)) { + istat = 1; + if (!status) status = PRJERR_BAD_PIX_SET("xphx2s"); + } + } + + *statp = istat; + } + + } else { + // Beyond latitude range. + *phip = 0.0; + *thetap = 0.0; + *statp = 1; + if (!status) status = PRJERR_BAD_PIX_SET("xphx2s"); + } + } + } + + + // Do bounds checking on the native coordinates. + if (prj->bounds&4 && prjbchk(1.0e-12, nx, my, spt, phi, theta, stat)) { + if (!status) status = PRJERR_BAD_PIX_SET("xphx2s"); + } + + return status; +} + +//---------------------------------------------------------------------------- + +int xphs2x( + struct prjprm *prj, + int nphi, + int ntheta, + int spt, + int sxy, + const double phi[], + const double theta[], + double x[], + double y[], + int stat[]) + +{ + // Initialize. + if (prj == 0x0) return PRJERR_NULL_POINTER; + + int status; + if (abs(prj->flag) != XPH) { + if ((status = xphset(prj))) return status; + } + + int mphi, mtheta; + if (ntheta > 0) { + mphi = nphi; + mtheta = ntheta; + } else { + mphi = 1; + mtheta = 1; + ntheta = nphi; + } + + + // Do phi dependence. + const double *phip = phi; + int rowoff = 0; + int rowlen = nphi*sxy; + + double chi, psi; + for (int iphi = 0; iphi < nphi; iphi++, rowoff += sxy, phip += spt) { + chi = *phip; + if (180.0 <= fabs(chi)) { + chi = fmod(chi, 360.0); + if (chi < -180.0) { + chi += 360.0; + } else if (180.0 <= chi) { + chi -= 360.0; + } + } + + // phi is also recomputed from chi to avoid rounding problems. + chi += 180.0; + psi = fmod(chi, 90.0); + + double *xp = x + rowoff; + double *yp = y + rowoff; + for (int itheta = 0; itheta < mtheta; itheta++) { + // y[] is used to hold phi (rounded). + *xp = psi; + *yp = chi - 180.0; + xp += rowlen; + yp += rowlen; + } + } + + + // Do theta dependence. + const double *thetap = theta; + double *xp = x; + double *yp = y; + int *statp = stat; + for (int itheta = 0; itheta < ntheta; itheta++, thetap += spt) { + double sinthe = sind(*thetap); + double abssin = fabs(sinthe); + + for (int iphi = 0; iphi < mphi; iphi++, xp += sxy, yp += sxy, statp++) { + double xi, eta; + if (abssin <= prj->w[2]) { + // Equatorial regime. + xi = *xp; + eta = 67.5 * sinthe; + + } else { + // Polar regime. + double sigma; + if (*thetap < prj->w[5]) { + sigma = sqrt(3.0*(1.0 - abssin)); + } else { + sigma = (90.0 - *thetap)*prj->w[6]; + } + + xi = 45.0 + (*xp - 45.0)*sigma; + eta = 45.0 * (2.0 - sigma); + if (*thetap < 0.0) eta = -eta; + } + + xi -= 45.0; + eta -= 90.0; + + // Recall that y[] holds phi. + if (*yp < -90.0) { + *xp = prj->w[0]*(-xi + eta) - prj->x0; + *yp = prj->w[0]*(-xi - eta) - prj->y0; + + } else if (*yp < 0.0) { + *xp = prj->w[0]*(+xi + eta) - prj->x0; + *yp = prj->w[0]*(-xi + eta) - prj->y0; + + } else if (*yp < 90.0) { + *xp = prj->w[0]*( xi - eta) - prj->x0; + *yp = prj->w[0]*( xi + eta) - prj->y0; + + } else { + *xp = prj->w[0]*(-xi - eta) - prj->x0; + *yp = prj->w[0]*( xi - eta) - prj->y0; + } + + *statp = 0; + } + } + + return 0; +} diff --git a/cextern/wcslib/C/prj.h b/cextern/wcslib/C/prj.h index 861ff7349919..8ff5fb06a740 100644 --- a/cextern/wcslib/C/prj.h +++ b/cextern/wcslib/C/prj.h @@ -1,7 +1,6 @@ /*============================================================================ - - WCSLIB 4.14 - an implementation of the FITS WCS standard. - Copyright (C) 1995-2012, Mark Calabretta + WCSLIB 8.6 - an implementation of the FITS WCS standard. + Copyright (C) 1995-2026, Mark Calabretta This file is part of WCSLIB. @@ -18,37 +17,46 @@ You should have received a copy of the GNU Lesser General Public License along with WCSLIB. If not, see http://www.gnu.org/licenses. - Direct correspondence concerning WCSLIB to mark@calabretta.id.au - Author: Mark Calabretta, Australia Telescope National Facility, CSIRO. - http://www.atnf.csiro.au/people/Mark.Calabretta - $Id: prj.h,v 4.14 2012/07/13 10:02:27 cal103 Exp $ + http://www.atnf.csiro.au/computing/software/wcs + $Id: prj.h,v 8.6 2026/03/29 13:53:56 mcalabre Exp $ *============================================================================= * -* WCSLIB 4.14 - C routines that implement the spherical map projections -* recognized by the FITS World Coordinate System (WCS) standard. Refer to -* -* "Representations of world coordinates in FITS", -* Greisen, E.W., & Calabretta, M.R. 2002, A&A, 395, 1061 (Paper I) -* -* "Representations of celestial coordinates in FITS", -* Calabretta, M.R., & Greisen, E.W. 2002, A&A, 395, 1077 (Paper II) -* -* Refer to the README file provided with WCSLIB for an overview of the -* library. +* WCSLIB 8.6 - C routines that implement the FITS World Coordinate System +* (WCS) standard. Refer to the README file provided with WCSLIB for an +* overview of the library. * * * Summary of the prj routines * --------------------------- -* These routines implement the spherical map projections defined by the FITS -* WCS standard. They are based on the prjprm struct which contains all -* information needed for the computations. The struct contains some members -* that must be set by the user, and others that are maintained by these -* routines, somewhat like a C++ class but with no encapsulation. +* Routines in this suite implement the spherical map projections defined by +* the FITS World Coordinate System (WCS) standard, as described in +* += "Representations of world coordinates in FITS", += Greisen, E.W., & Calabretta, M.R. 2002, A&A, 395, 1061 (WCS Paper I) += += "Representations of celestial coordinates in FITS", += Calabretta, M.R., & Greisen, E.W. 2002, A&A, 395, 1077 (WCS Paper II) += += "Mapping on the HEALPix grid", += Calabretta, M.R., & Roukema, B.F. 2007, MNRAS, 381, 865 (WCS Paper V) += += "Representing the 'Butterfly' Projection in FITS -- Projection Code XPH", += Calabretta, M.R., & Lowe, S.R. 2013, PASA, 30, e050 (WCS Paper VI) +* +* These routines are based on the prjprm struct which contains all information +* needed for the computations. The struct contains some members that must be +* set by the user, and others that are maintained by these routines, somewhat +* like a C++ class but with no encapsulation. * * Routine prjini() is provided to initialize the prjprm struct with default * values, prjfree() reclaims any memory that may have been allocated to store -* an error message, and prjprt() prints its contents. +* an error message, prjsize() computes its total size including allocated +* memory, prjenq() returns information about the state of the struct, and +* prjprt() prints its contents. +* +* prjperr() prints the error message(s) (if any) stored in a prjprm struct. +* prjbchk() performs bounds checking on native spherical coordinates. * * Setup routines for each projection with names of the form ???set(), where * "???" is the down-cased three-letter projection code, compute intermediate @@ -66,7 +74,11 @@ * * In summary, the routines are: * - prjini() Initialization routine for the prjprm struct. -* - prjprt() Routine to print the prjprm struct. +* - prjfree() Reclaim memory allocated for error messages. +* - prjsize() Compute total size of a prjprm struct. +* - prjprt() Print a prjprm struct. +* - prjperr() Print error message (if any). +* - prjbchk() Bounds checking on native coordinates. * * - prjset(), prjx2s(), prjs2x(): Generic driver routines * @@ -97,6 +109,7 @@ * - cscset(), cscx2s(), cscs2x(): CSC (COBE spherical cube) * - qscset(), qscx2s(), qscs2x(): QSC (quadrilateralized spherical cube) * - hpxset(), hpxx2s(), hpxs2x(): HPX (HEALPix) +* - xphset(), xphx2s(), xphs2x(): XPH (HEALPix polar, aka "butterfly") * * Argument checking (projection routines): * ---------------------------------------- @@ -110,15 +123,19 @@ * zero). The projection routines for AZP, SZP, TAN, SIN, ZPN, and COP also * return error 2 if (phi,theta) corresponds to the overlapped (far) side of * the projection but also return the corresponding value of (x,y). This -* strict bounds checking may be relaxed at any time by setting prjprm::bounds -* to 0 (rather than 1); the projections need not be reinitialized. +* strict bounds checking may be relaxed at any time by setting +* prjprm::bounds%2 to 0 (rather than 1); the projections need not be +* reinitialized. * * Argument checking (deprojection routines): * ------------------------------------------ * Error checking on the projected coordinates (x,y) is limited to that * required to ascertain whether a solution exists. Where a solution does -* exist no check is made that the value of phi and theta obtained lie within -* the ranges [-180,180] for phi, and [-90,90] for theta. +* exist, an optional check is made that the value of phi and theta obtained +* lie within the ranges [-180,180] for phi, and [-90,90] for theta. This +* check, performed by prjbchk(), is enabled by default. It may be disabled by +* setting prjprm::bounds%4 to 0 (rather than 1); the projections need not be +* reinitialized. * * Accuracy: * --------- @@ -137,6 +154,11 @@ * prjini() sets all members of a prjprm struct to default values. It should * be used to initialize every prjprm struct. * +* PLEASE NOTE: If the prjprm struct has already been initialized, then before +* reinitializing, it prjfree() should be used to free any memory that may have +* been allocated to store an error message. A memory leak may otherwise +* result. +* * Returned: * prj struct prjprm* * Projection parameters. @@ -152,7 +174,7 @@ * prjfree() frees any memory that may have been allocated to store an error * message in the prjprm struct. * -* Given: +* Given and returned: * prj struct prjprm* * Projection parameters. * @@ -162,6 +184,53 @@ * 1: Null prjprm pointer passed. * * +* prjsize() - Compute the size of a prjprm struct +* ----------------------------------------------- +* prjsize() computes the full size of a prjprm struct, including allocated +* memory. +* +* Given: +* prj const struct prjprm* +* Projection parameters. +* +* If NULL, the base size of the struct and the allocated +* size are both set to zero. +* +* Returned: +* sizes int[2] The first element is the base size of the struct as +* returned by sizeof(struct prjprm). The second element +* is the total allocated size, in bytes. This figure +* includes memory allocated for the constituent struct, +* prjprm::err. +* +* It is not an error for the struct not to have been set +* up via prjset(). +* +* Function return value: +* int Status return value: +* 0: Success. +* +* +* prjenq() - enquire about the state of a prjprm struct +* ----------------------------------------------------- +* prjenq() may be used to obtain information about the state of a prjprm +* struct. The function returns a true/false answer for the enquiry asked. +* +* Given: +* prj const struct prjprm* +* Projection parameters. +* +* enquiry int Enquiry according to the following parameters: +* PRJENQ_SET: the struct has been set up by prjset(). +* PRJENQ_BYP: the struct is in bypass mode (see +* prjset()). +* +* Function return value: +* int Enquiry result: +* 0: No. +* 1: Yes. +* +* * prjprt() - Print routine for the prjprm struct * ---------------------------------------------- * prjprt() prints the contents of a prjprm struct using wcsprintf(). Mainly @@ -177,15 +246,67 @@ * 1: Null prjprm pointer passed. * * +* prjperr() - Print error messages from a prjprm struct +* ----------------------------------------------------- +* prjperr() prints the error message(s) (if any) stored in a prjprm struct. +* If there are no errors then nothing is printed. It uses wcserr_prt(), q.v. +* +* Given: +* prj const struct prjprm* +* Projection parameters. +* +* prefix const char * +* If non-NULL, each output line will be prefixed with +* this string. +* +* Function return value: +* int Status return value: +* 0: Success. +* 1: Null prjprm pointer passed. +* +* +* prjbchk() - Bounds checking on native coordinates +* ------------------------------------------------- +* prjbchk() performs bounds checking on native spherical coordinates. As +* returned by the deprojection (x2s) routines, native longitude is expected +* to lie in the closed interval [-180,180], with latitude in [-90,90]. +* +* A tolerance may be specified to provide a small allowance for numerical +* imprecision. Values that lie outside the allowed range by not more than +* the specified tolerance will be adjusted back into range. +* +* If prjprm::bounds&4 is set, as it is by prjini(), then prjbchk() will be +* invoked automatically by the Cartesian-to-spherical deprojection (x2s) +* routines with an appropriate tolerance set for each projection. +* +* Given: +* tol double Tolerance for the bounds check [deg]. +* +* nphi, +* ntheta int Vector lengths. +* +* spt int Vector stride. +* +* Given and returned: +* phi,theta double[] Native longitude and latitude (phi,theta) [deg]. +* +* Returned: +* stat int[] Status value for each vector element: +* 0: Valid value of (phi,theta). +* 1: Invalid value. +* +* Function return value: +* int Status return value: +* 0: Success. +* 1: One or more of the (phi,theta) coordinates +* were, invalid, as indicated by the stat vector. +* +* * prjset() - Generic setup routine for the prjprm struct * ------------------------------------------------------ * prjset() sets up a prjprm struct according to information supplied within * it. * -* Note that this routine need not be called directly; it will be invoked by -* prjx2s() and prjs2x() if prj.flag is anything other than a predefined magic -* value. -* * The one important distinction between prjset() and the setup routines for * the specific projections is that the projection code must be defined in the * prjprm struct in order for prjset() to identify the required projection. @@ -193,6 +314,17 @@ * the pointers to the specific projection and deprojection routines contained * therein. * +* Note that this routine need not be called directly; it will be invoked by +* prjx2s() and prjs2x() if prj.flag is anything other than a predefined magic +* value. +* +* prjset() normally operates regardless of the value of prjprm::flag; i.e. +* even if a struct was previously set up it will be reset unconditionally. +* However, a prjprm struct may be put into "bypass" mode by invoking prjset() +* initially with prjprm::flag == 1 (rather than 0). prjset() will return +* immediately if invoked on a struct in that state. To take a struct out of +* bypass mode, simply reset prjprm::flag to zero. See also prjenq(). +* * Given and returned: * prj struct prjprm* * Projection parameters. @@ -230,7 +362,7 @@ * phi,theta double[] Longitude and latitude (phi,theta) of the projected * point in native spherical coordinates [deg]. * -* stat int[] Status return value for each vector element: +* stat int[] Status value for each vector element: * 0: Success. * 1: Invalid value of (x,y). * @@ -270,7 +402,7 @@ * Returned: * x,y double[] Projected coordinates. * -* stat int[] Status return value for each vector element: +* stat int[] Status value for each vector element: * 0: Success. * 1: Invalid value of (phi,theta). * @@ -326,7 +458,7 @@ * phi,theta double[] Longitude and latitude of the projected point in * native spherical coordinates [deg]. * -* stat int[] Status return value for each vector element: +* stat int[] Status value for each vector element: * 0: Success. * 1: Invalid value of (x,y). * @@ -364,7 +496,7 @@ * Returned: * x,y double[] Projected coordinates. * -* stat int[] Status return value for each vector element: +* stat int[] Status value for each vector element: * 0: Success. * 1: Invalid value of (phi,theta). * @@ -389,8 +521,8 @@ * while others are for internal use only. * * int flag -* (Given and returned) This flag must be set to zero whenever any of the -* following prjprm struct members are set or changed: +* (Given and returned) This flag must be set to zero (or 1, see prjset()) +* whenever any of the following prjprm members are set or changed: * * - prjprm::code, * - prjprm::r0, @@ -431,8 +563,18 @@ * projection-specific default. * * int bounds -* (Given) Controls strict bounds checking for the AZP, SZP, TAN, SIN, ZPN, -* and COP projections; set to zero to disable checking. +* (Given) Controls bounds checking. If bounds&1 then enable strict bounds +* checking for the spherical-to-Cartesian (s2x) transformation for the +* AZP, SZP, TAN, SIN, ZPN, and COP projections. If bounds&2 then enable +* strict bounds checking for the Cartesian-to-spherical transformation +* (x2s) for the HPX and XPH projections. If bounds&4 then the Cartesian- +* to-spherical transformations (x2s) will invoke prjbchk() to perform +* bounds checking on the computed native coordinates, with a tolerance set +* to suit each projection. bounds is set to 7 by prjini() by default +* which enables all checks. Zero it to disable all checking. +* +* It is not necessary to reset the prjprm struct (via prjset() or +* ???set()) when prjprm::bounds is changed. * * The remaining members of the prjprm struct are maintained by the setup * routines and must not be modified elsewhere: @@ -505,27 +647,27 @@ * (phi_0,theta_0). * * struct wcserr *err -* (Returned) If enabled, when an error status is returned this struct +* (Returned) If enabled, when an error status is returned, this struct * contains detailed information about the error, see wcserr_enable(). * * void *padding * (An unused variable inserted for alignment purposes only.) * * double w[10] -* (Returned) Intermediate floating-point values derived from the -* projection parameters, cached here to save recomputation. +* (For internal use only.) Intermediate floating-point values derived from +* the projection parameters by prjset(), cached here to save recomputation. * * Usage of the w[] array as it applies to each projection is described in * the prologue to each trio of projection routines in prj.c. * -* int n -* (Returned) Intermediate integer value (used only for the ZPN and HPX -* projections). +* int m, n +* (For internal use only.) Intermediate integer values set by prjset() +* (used only for the ZPN and HPX projections). * * int (*prjx2s)(PRJX2S_ARGS) -* (Returned) Pointer to the projection ... +* (For internal use only.) Pointer to the spherical projection ... * int (*prjs2x)(PRJ_ARGS) -* (Returned) ... and deprojection routines. +* (For internal use only.) ... and deprojection routines. * * * Global variable: const char *prj_errmsg[] - Status return messages @@ -537,26 +679,28 @@ #ifndef WCSLIB_PROJ #define WCSLIB_PROJ -#include "wcserr.h" - #ifdef __cplusplus extern "C" { #endif +enum prjenq_enum { + PRJENQ_SET = 2, // prjprm struct has been set up. + PRJENQ_BYP = 4, // prjprm struct is in bypass mode. +}; -/* Total number of projection parameters; 0 to PVN-1. */ +// Total number of projection parameters; 0 to PVN-1. #define PVN 30 extern const char *prj_errmsg[]; enum prj_errmsg_enum { - PRJERR_SUCCESS = 0, /* Success. */ - PRJERR_NULL_POINTER = 1, /* Null prjprm pointer passed. */ - PRJERR_BAD_PARAM = 2, /* Invalid projection parameters. */ - PRJERR_BAD_PIX = 3, /* One or more of the (x, y) coordinates were - invalid. */ - PRJERR_BAD_WORLD = 4 /* One or more of the (phi, theta) coordinates - were invalid. */ + PRJERR_SUCCESS = 0, // Success. + PRJERR_NULL_POINTER = 1, // Null prjprm pointer passed. + PRJERR_BAD_PARAM = 2, // Invalid projection parameters. + PRJERR_BAD_PIX = 3, // One or more of the (x, y) coordinates were + // invalid. + PRJERR_BAD_WORLD = 4 // One or more of the (phi, theta) coordinates + // were invalid. }; extern const int CONIC, CONVENTIONAL, CYLINDRICAL, POLYCONIC, @@ -564,7 +708,7 @@ extern const int CONIC, CONVENTIONAL, CYLINDRICAL, POLYCONIC, extern const char prj_categories[9][32]; extern const int prj_ncode; -extern const char prj_codes[27][4]; +extern const char prj_codes[28][4]; #ifdef PRJX2S_ARGS #undef PRJX2S_ARGS @@ -574,63 +718,76 @@ extern const char prj_codes[27][4]; #undef PRJS2X_ARGS #endif -/* For use in declaring deprojection function prototypes. */ +// For use in declaring deprojection function prototypes. #define PRJX2S_ARGS struct prjprm *prj, int nx, int ny, int sxy, int spt, \ const double x[], const double y[], double phi[], double theta[], int stat[] -/* For use in declaring projection function prototypes. */ +// For use in declaring projection function prototypes. #define PRJS2X_ARGS struct prjprm *prj, int nx, int ny, int sxy, int spt, \ const double phi[], const double theta[], double x[], double y[], int stat[] struct prjprm { - /* Initialization flag (see the prologue above). */ - /*------------------------------------------------------------------------*/ - int flag; /* Set to zero to force initialization. */ - - /* Parameters to be provided (see the prologue above). */ - /*------------------------------------------------------------------------*/ - char code[4]; /* Three-letter projection code. */ - double r0; /* Radius of the generating sphere. */ - double pv[PVN]; /* Projection parameters. */ - double phi0, theta0; /* Fiducial native coordinates. */ - int bounds; /* Enable strict bounds checking. */ - - /* Information derived from the parameters supplied. */ - /*------------------------------------------------------------------------*/ - char name[40]; /* Projection name. */ - int category; /* Projection category. */ - int pvrange; /* Range of projection parameter indices. */ - int simplezen; /* Is it a simple zenithal projection? */ - int equiareal; /* Is it an equal area projection? */ - int conformal; /* Is it a conformal projection? */ - int global; /* Can it map the whole sphere? */ - int divergent; /* Does the projection diverge in latitude? */ - double x0, y0; /* Fiducial offsets. */ - - /* Error handling */ - /*------------------------------------------------------------------------*/ + // Initialization flag (see the prologue above). + //-------------------------------------------------------------------------- + int flag; // Set to zero to force initialization. + + // Parameters to be provided (see the prologue above). + //-------------------------------------------------------------------------- + char code[4]; // Three-letter projection code. + double r0; // Radius of the generating sphere. + double pv[PVN]; // Projection parameters. + double phi0, theta0; // Fiducial native coordinates. + int bounds; // Controls bounds checking. + + // Information derived from the parameters supplied. + //-------------------------------------------------------------------------- + char name[40]; // Projection name. + int category; // Projection category. + int pvrange; // Range of projection parameter indices. + int simplezen; // Is it a simple zenithal projection? + int equiareal; // Is it an equal area projection? + int conformal; // Is it a conformal projection? + int global; // Can it map the whole sphere? + int divergent; // Does the projection diverge in latitude? + double x0, y0; // Fiducial offsets. + + // Error messaging, if enabled. + //-------------------------------------------------------------------------- struct wcserr *err; - /* Private */ - /*------------------------------------------------------------------------*/ - void *padding; /* (Dummy inserted for alignment purposes.) */ - double w[10]; /* Intermediate values. */ - int m, n; /* Intermediate values. */ + //-------------------------------------------------------------------------- + // Private - the remainder are for internal use. + //-------------------------------------------------------------------------- + void *padding; // (Dummy inserted for alignment purposes.) + + double w[10]; // Intermediate values set by prjset(). + int m, n; // Intermediate values set by prjset(). - int (*prjx2s)(PRJX2S_ARGS); /* Pointers to the spherical projection and */ - int (*prjs2x)(PRJS2X_ARGS); /* deprojection functions. */ + int (*prjx2s)(PRJX2S_ARGS); // Pointers to the spherical projection and + int (*prjs2x)(PRJS2X_ARGS); // deprojection functions. }; -/* Size of the prjprm struct in int units, used by the Fortran wrappers. */ +// Size of the prjprm struct in int units, used by the Fortran wrappers. #define PRJLEN (sizeof(struct prjprm)/sizeof(int)) -/* Use the preprocessor to help declare function prototypes (see above). */ int prjini(struct prjprm *prj); + int prjfree(struct prjprm *prj); + +int prjsize(const struct prjprm *prj, int sizes[2]); + +int prjenq(const struct prjprm *prj, int enquiry); + int prjprt(const struct prjprm *prj); +int prjperr(const struct prjprm *prj, const char *prefix); + +int prjbchk(double tol, int nphi, int ntheta, int spt, double phi[], + double theta[], int stat[]); + +// Use the preprocessor to help declare function prototypes (see above). int prjset(struct prjprm *prj); int prjx2s(PRJX2S_ARGS); int prjs2x(PRJS2X_ARGS); @@ -743,8 +900,12 @@ int hpxset(struct prjprm *prj); int hpxx2s(PRJX2S_ARGS); int hpxs2x(PRJS2X_ARGS); +int xphset(struct prjprm *prj); +int xphx2s(PRJX2S_ARGS); +int xphs2x(PRJS2X_ARGS); + -/* Deprecated. */ +// Deprecated. #define prjini_errmsg prj_errmsg #define prjprt_errmsg prj_errmsg #define prjset_errmsg prj_errmsg @@ -755,4 +916,4 @@ int hpxs2x(PRJS2X_ARGS); } #endif -#endif /* WCSLIB_PROJ */ +#endif // WCSLIB_PROJ diff --git a/cextern/wcslib/C/spc.c b/cextern/wcslib/C/spc.c index 03decc09af56..8cae42f1d285 100644 --- a/cextern/wcslib/C/spc.c +++ b/cextern/wcslib/C/spc.c @@ -1,7 +1,6 @@ /*============================================================================ - - WCSLIB 4.14 - an implementation of the FITS WCS standard. - Copyright (C) 1995-2012, Mark Calabretta + WCSLIB 8.6 - an implementation of the FITS WCS standard. + Copyright (C) 1995-2026, Mark Calabretta This file is part of WCSLIB. @@ -18,11 +17,9 @@ You should have received a copy of the GNU Lesser General Public License along with WCSLIB. If not, see http://www.gnu.org/licenses. - Direct correspondence concerning WCSLIB to mark@calabretta.id.au - Author: Mark Calabretta, Australia Telescope National Facility, CSIRO. - http://www.atnf.csiro.au/people/Mark.Calabretta - $Id: spc.c,v 4.14 2012/07/13 10:02:27 cal103 Exp $ + http://www.atnf.csiro.au/computing/software/wcs + $Id: spc.c,v 8.6 2026/03/29 13:53:56 mcalabre Exp $ *===========================================================================*/ #include @@ -38,29 +35,29 @@ #include "spc.h" #include "spx.h" -/* Spectral algorithm codes. */ -#define F2S 100; /* Axis linear in frequency. */ -#define W2S 200; /* Axis linear in vacuum wavelengths. */ -#define A2S 300; /* Axis linear in air wavelengths. */ -#define V2S 400; /* Axis linear in velocity. */ -#define GRI 500; /* Grism in vacuum. */ -#define GRA 600; /* Grism in air. */ - -/* S-type spectral variables. */ -#define FREQ 0; /* Frequency-like. */ -#define AFRQ 1; /* Frequency-like. */ -#define ENER 2; /* Frequency-like. */ -#define WAVN 3; /* Frequency-like. */ -#define VRAD 4; /* Frequency-like. */ -#define WAVE 10; /* Vacuum wavelength-like. */ -#define VOPT 11; /* Vacuum wavelength-like. */ -#define ZOPT 12; /* Vacuum wavelength-like. */ -#define AWAV 20; /* Air wavelength-like. */ -#define VELO 30; /* Velocity-like. */ -#define BETA 31; /* Velocity-like. */ - - -/* Map status return value to message. */ +// Spectral algorithm codes. +#define F2S 100; // Axis linear in frequency. +#define W2S 200; // Axis linear in vacuum wavelengths. +#define A2S 300; // Axis linear in air wavelengths. +#define V2S 400; // Axis linear in velocity. +#define GRI 500; // Grism in vacuum. +#define GRA 600; // Grism in air. + +// S-type spectral variables. +#define FREQ 0; // Frequency-like. +#define AFRQ 1; // Frequency-like. +#define ENER 2; // Frequency-like. +#define WAVN 3; // Frequency-like. +#define VRAD 4; // Frequency-like. +#define WAVE 10; // Vacuum wavelength-like. +#define VOPT 11; // Vacuum wavelength-like. +#define ZOPT 12; // Vacuum wavelength-like. +#define AWAV 20; // Air wavelength-like. +#define VELO 30; // Velocity-like. +#define BETA 31; // Velocity-like. + + +// Map status return value to message. const char *spc_errmsg[] = { "Success", "Null spcprm pointer passed", @@ -68,23 +65,29 @@ const char *spc_errmsg[] = { "One or more of x coordinates were invalid", "One or more of the spec coordinates were invalid"}; -/* Convenience macro for invoking wcserr_set(). */ +// Map error returns for lower-level routines. SPXERR_BAD_INSPEC_COORD +// maps to either SPCERR_BAD_X or SPCERR_BAD_SPEC depending on context. +const int spc_spxerr[] = { + SPCERR_SUCCESS, // 0: SPXERR_SUCCESS + SPCERR_NULL_POINTER, // 1: SPXERR_NULL_POINTER + SPCERR_BAD_SPEC_PARAMS, // 2: SPXERR_BAD_SPEC_PARAMS + SPCERR_BAD_SPEC_PARAMS // 3: SPXERR_BAD_SPEC_VAR + // 4: SPXERR_BAD_INSPEC_COORD +}; + +// Convenience macro for invoking wcserr_set(). #define SPC_ERRMSG(status) WCSERR_SET(status), spc_errmsg[status] #define C 2.99792458e8 -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- int spcini(struct spcprm *spc) { - register int k; - if (spc == 0x0) return SPCERR_NULL_POINTER; - spc->flag = 0; - memset(spc->type, 0, 8); strcpy(spc->type, " "); strcpy(spc->code, " "); @@ -93,11 +96,11 @@ int spcini(struct spcprm *spc) spc->restfrq = 0.0; spc->restwav = 0.0; - for (k = 0; k < 7; k++) { + for (int k = 0; k < 7; k++) { spc->pv[k] = UNDEFINED; } - for (k = 0; k < 6; k++) { + for (int k = 0; k < 6; k++) { spc->w[k] = 0.0; } @@ -112,60 +115,105 @@ int spcini(struct spcprm *spc) spc->spxS2P = 0x0; spc->spxP2X = 0x0; - return SPCERR_SUCCESS; + spc->flag = 0; + + return 0; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- int spcfree(struct spcprm *spc) { if (spc == 0x0) return SPCERR_NULL_POINTER; - if (spc->err) { - free(spc->err); - spc->err = 0x0; + wcserr_clear(&(spc->err)); + + return 0; +} + +//---------------------------------------------------------------------------- + +int spcsize(const struct spcprm *spc, int sizes[2]) + +{ + if (spc == 0x0) { + sizes[0] = sizes[1] = 0; + return 0; } - return SPCERR_SUCCESS; + // Base size, in bytes. + sizes[0] = sizeof(struct spcprm); + + // Total size of allocated memory, in bytes. + sizes[1] = 0; + + // spcprm::err. + int exsizes[2]; + wcserr_size(spc->err, exsizes); + sizes[1] += exsizes[0] + exsizes[1]; + + return 0; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- -int spcprt(const struct spcprm *spc) +int spcenq(const struct spcprm *spc, int enquiry) { - char hext[32]; - int i; + // Initialize. + if (spc == 0x0) return SPCERR_NULL_POINTER; + + int absflag = abs(spc->flag); + int answer = 0; + + if (enquiry & SPCENQ_SET) { + if (absflag < 100 || 1000 < absflag) return 0; + answer = 1; + } + + if (enquiry & SPCENQ_BYP) { + if (spc->flag != 1 && !(-1000 < spc->flag && spc->flag < -100)) return 0; + answer = 1; + } + return answer; +} + +//---------------------------------------------------------------------------- + +int spcprt(const struct spcprm *spc) + +{ if (spc == 0x0) return SPCERR_NULL_POINTER; + // Parameters supplied. wcsprintf(" flag: %d\n", spc->flag); wcsprintf(" type: \"%s\"\n", spc->type); wcsprintf(" code: \"%s\"\n", spc->code); if (undefined(spc->crval)) { wcsprintf(" crval: UNDEFINED\n"); } else { - wcsprintf(" crval: %- 11.4g\n", spc->crval); + wcsprintf(" crval: %#- 11.5g\n", spc->crval); } wcsprintf(" restfrq: %f\n", spc->restfrq); wcsprintf(" restwav: %f\n", spc->restwav); wcsprintf(" pv:"); if (spc->isGrism) { - for (i = 0; i < 5; i++) { + for (int i = 0; i < 5; i++) { if (undefined(spc->pv[i])) { wcsprintf(" UNDEFINED "); } else { - wcsprintf(" %- 11.4g", spc->pv[i]); + wcsprintf(" %#- 11.5g", spc->pv[i]); } } wcsprintf("\n "); - for (i = 5; i < 7; i++) { + for (int i = 5; i < 7; i++) { if (undefined(spc->pv[i])) { wcsprintf(" UNDEFINED "); } else { - wcsprintf(" %- 11.4g", spc->pv[i]); + wcsprintf(" %#- 11.5g", spc->pv[i]); } } wcsprintf("\n"); @@ -174,14 +222,15 @@ int spcprt(const struct spcprm *spc) wcsprintf(" (not used)\n"); } + // Derived values. wcsprintf(" w:"); - for (i = 0; i < 3; i++) { - wcsprintf(" %- 11.4g", spc->w[i]); + for (int i = 0; i < 3; i++) { + wcsprintf(" %#- 11.5g", spc->w[i]); } if (spc->isGrism) { wcsprintf("\n "); - for (i = 3; i < 6; i++) { - wcsprintf(" %- 11.4g", spc->w[i]); + for (int i = 3; i < 6; i++) { + wcsprintf(" %#- 11.5g", spc->w[i]); } wcsprintf("\n"); } else { @@ -190,38 +239,50 @@ int spcprt(const struct spcprm *spc) wcsprintf(" isGrism: %d\n", spc->isGrism); + // Error handling. WCSPRINTF_PTR(" err: ", spc->err, "\n"); if (spc->err) { wcserr_prt(spc->err, " "); } + // Pointers to spectral functions. + char hext[32]; wcsprintf(" spxX2P: %s\n", - wcsutil_fptr2str((int (*)(void))spc->spxX2P, hext)); + wcsutil_fptr2str((void (*)(void))spc->spxX2P, hext)); wcsprintf(" spxP2S: %s\n", - wcsutil_fptr2str((int (*)(void))spc->spxP2S, hext)); + wcsutil_fptr2str((void (*)(void))spc->spxP2S, hext)); wcsprintf(" spxS2P: %s\n", - wcsutil_fptr2str((int (*)(void))spc->spxS2P, hext)); + wcsutil_fptr2str((void (*)(void))spc->spxS2P, hext)); wcsprintf(" spxP2X: %s\n", - wcsutil_fptr2str((int (*)(void))spc->spxP2X, hext)); + wcsutil_fptr2str((void (*)(void))spc->spxP2X, hext)); + + return 0; +} + +//---------------------------------------------------------------------------- + +int spcperr(const struct spcprm *spc, const char *prefix) + +{ + if (spc == 0x0) return SPCERR_NULL_POINTER; + + if (spc->err) { + wcserr_prt(spc->err, prefix); + } - return SPCERR_SUCCESS; + return 0; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- int spcset(struct spcprm *spc) { static const char *function = "spcset"; - char ctype[9], ptype, xtype; - int restreq, status; - double alpha, beta_r, crvalX, dn_r, dXdS, epsilon, G, m, lambda_r, n_r, - t, restfrq, restwav, theta; - struct wcserr **err; - if (spc == 0x0) return SPCERR_NULL_POINTER; - err = &(spc->err); + if (spc->flag < 0) return 0; + struct wcserr **err = &(spc->err); if (undefined(spc->crval)) { return wcserr_set(WCSERR_SET(SPCERR_BAD_SPEC_PARAMS), @@ -235,25 +296,30 @@ int spcset(struct spcprm *spc) spc->w[0] = 0.0; - /* Analyse the spectral axis type. */ + // Analyse the spectral axis type. + char ctype[9]; memset(ctype, 0, 9); - strncpy(ctype, spc->type, 4); + memcpy(ctype, spc->type, 4); if (*(spc->code) != ' ') { sprintf(ctype+4, "-%s", spc->code); } - restfrq = spc->restfrq; - restwav = spc->restwav; + + double restfrq = spc->restfrq; + double restwav = spc->restwav; + char ptype, xtype; + int restreq, status; + double crvalX, dXdS; if ((status = spcspxe(ctype, spc->crval, restfrq, restwav, &ptype, &xtype, &restreq, &crvalX, &dXdS, &(spc->err)))) { return status; } - /* Satisfy rest frequency/wavelength requirements. */ + // Satisfy rest frequency/wavelength requirements. if (restreq) { if (restreq == 3 && restfrq == 0.0 && restwav == 0.0) { - /* VRAD-V2F, VOPT-V2W, and ZOPT-V2W require the rest frequency or */ - /* wavelength for the S-P and P-X transformations but not for S-X */ - /* so supply a phoney value. */ + // VRAD-V2F, VOPT-V2W, and ZOPT-V2W require the rest frequency or + // wavelength for the S-P and P-X transformations but not for S-X + // so supply a phoney value. restwav = 1.0; } @@ -280,88 +346,89 @@ int spcset(struct spcprm *spc) spc->w[2] = dXdS; - /* Set pointers-to-functions for the linear part of the transformation. */ + // Set pointers-to-functions for the linear part of the transformation. + int flag = 0; if (ptype == 'F') { if (strcmp(spc->type, "FREQ") == 0) { - /* Frequency. */ - spc->flag = FREQ; + // Frequency. + flag = FREQ; spc->spxP2S = 0x0; spc->spxS2P = 0x0; } else if (strcmp(spc->type, "AFRQ") == 0) { - /* Angular frequency. */ - spc->flag = AFRQ; + // Angular frequency. + flag = AFRQ; spc->spxP2S = freqafrq; spc->spxS2P = afrqfreq; } else if (strcmp(spc->type, "ENER") == 0) { - /* Photon energy. */ - spc->flag = ENER; + // Photon energy. + flag = ENER; spc->spxP2S = freqener; spc->spxS2P = enerfreq; } else if (strcmp(spc->type, "WAVN") == 0) { - /* Wave number. */ - spc->flag = WAVN; + // Wave number. + flag = WAVN; spc->spxP2S = freqwavn; spc->spxS2P = wavnfreq; } else if (strcmp(spc->type, "VRAD") == 0) { - /* Radio velocity. */ - spc->flag = VRAD; + // Radio velocity. + flag = VRAD; spc->spxP2S = freqvrad; spc->spxS2P = vradfreq; } } else if (ptype == 'W') { if (strcmp(spc->type, "WAVE") == 0) { - /* Vacuum wavelengths. */ - spc->flag = WAVE; + // Vacuum wavelengths. + flag = WAVE; spc->spxP2S = 0x0; spc->spxS2P = 0x0; } else if (strcmp(spc->type, "VOPT") == 0) { - /* Optical velocity. */ - spc->flag = VOPT; + // Optical velocity. + flag = VOPT; spc->spxP2S = wavevopt; spc->spxS2P = voptwave; } else if (strcmp(spc->type, "ZOPT") == 0) { - /* Redshift. */ - spc->flag = ZOPT; + // Redshift. + flag = ZOPT; spc->spxP2S = wavezopt; spc->spxS2P = zoptwave; } } else if (ptype == 'A') { if (strcmp(spc->type, "AWAV") == 0) { - /* Air wavelengths. */ - spc->flag = AWAV; + // Air wavelengths. + flag = AWAV; spc->spxP2S = 0x0; spc->spxS2P = 0x0; } } else if (ptype == 'V') { if (strcmp(spc->type, "VELO") == 0) { - /* Relativistic velocity. */ - spc->flag = VELO; + // Relativistic velocity. + flag = VELO; spc->spxP2S = 0x0; spc->spxS2P = 0x0; } else if (strcmp(spc->type, "BETA") == 0) { - /* Velocity ratio (v/c). */ - spc->flag = BETA; + // Velocity ratio (v/c). + flag = BETA; spc->spxP2S = velobeta; spc->spxS2P = betavelo; } } - /* Set pointers-to-functions for the non-linear part of the spectral */ - /* transformation. */ + // Set pointers-to-functions for the non-linear part of the spectral + // transformation. spc->isGrism = 0; if (xtype == 'F') { - /* Axis is linear in frequency. */ + // Axis is linear in frequency. if (ptype == 'F') { spc->spxX2P = 0x0; spc->spxP2X = 0x0; @@ -379,10 +446,10 @@ int spcset(struct spcprm *spc) spc->spxP2X = velofreq; } - spc->flag += F2S; + flag += F2S; } else if (xtype == 'W' || xtype == 'w') { - /* Axis is linear in vacuum wavelengths. */ + // Axis is linear in vacuum wavelengths. if (ptype == 'F') { spc->spxX2P = wavefreq; spc->spxP2X = freqwave; @@ -401,15 +468,15 @@ int spcset(struct spcprm *spc) } if (xtype == 'W') { - spc->flag += W2S; + flag += W2S; } else { - /* Grism in vacuum. */ + // Grism in vacuum. spc->isGrism = 1; - spc->flag += GRI; + flag += GRI; } } else if (xtype == 'A' || xtype == 'a') { - /* Axis is linear in air wavelengths. */ + // Axis is linear in air wavelengths. if (ptype == 'F') { spc->spxX2P = awavfreq; spc->spxP2X = freqawav; @@ -428,15 +495,15 @@ int spcset(struct spcprm *spc) } if (xtype == 'A') { - spc->flag += A2S; + flag += A2S; } else { - /* Grism in air. */ + // Grism in air. spc->isGrism = 2; - spc->flag += GRA; + flag += GRA; } } else if (xtype == 'V') { - /* Axis is linear in relativistic velocity. */ + // Axis is linear in relativistic velocity. if (ptype == 'F') { spc->spxX2P = velofreq; spc->spxP2X = freqvelo; @@ -454,16 +521,16 @@ int spcset(struct spcprm *spc) spc->spxP2X = 0x0; } - spc->flag += V2S; + flag += V2S; } - /* Check for grism axes. */ + // Check for grism axes. if (spc->isGrism) { - /* Axis is linear in "grism parameter"; work in wavelength. */ - lambda_r = crvalX; + // Axis is linear in "grism parameter"; work in wavelength. + double lambda_r = crvalX; - /* Set defaults. */ + // Set defaults. if (undefined(spc->pv[0])) spc->pv[0] = 0.0; if (undefined(spc->pv[1])) spc->pv[1] = 0.0; if (undefined(spc->pv[2])) spc->pv[2] = 0.0; @@ -472,17 +539,17 @@ int spcset(struct spcprm *spc) if (undefined(spc->pv[5])) spc->pv[5] = 0.0; if (undefined(spc->pv[6])) spc->pv[6] = 0.0; - /* Compute intermediaries. */ - G = spc->pv[0]; - m = spc->pv[1]; - alpha = spc->pv[2]; - n_r = spc->pv[3]; - dn_r = spc->pv[4]; - epsilon = spc->pv[5]; - theta = spc->pv[6]; + // Compute intermediaries. + double G = spc->pv[0]; + double m = spc->pv[1]; + double alpha = spc->pv[2]; + double n_r = spc->pv[3]; + double dn_r = spc->pv[4]; + double epsilon = spc->pv[5]; + double theta = spc->pv[6]; - t = G*m/cosd(epsilon); - beta_r = asind(t*lambda_r - n_r*sind(alpha)); + double t = G*m/cosd(epsilon); + double beta_r = asind(t*lambda_r - n_r*sind(alpha)); t -= dn_r*sind(alpha); @@ -493,10 +560,12 @@ int spcset(struct spcprm *spc) spc->w[5] = 1.0 / t; } - return SPCERR_SUCCESS; + spc->flag = (spc->flag == 1) ? -flag : flag; + + return 0; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- int spcx2s( struct spcprm *spc, @@ -510,43 +579,37 @@ int spcx2s( { static const char *function = "spcx2s"; - int statP2S, status = SPCERR_SUCCESS, statX2P; - double beta; - register int ix; - register int *statp; - register const double *xp; - register double *specp; - struct wcserr **err; - - /* Initialize. */ + // Initialize. if (spc == 0x0) return SPCERR_NULL_POINTER; - err = &(spc->err); + struct wcserr **err = &(spc->err); - if (spc->flag == 0) { + int status = 0; + if (abs(spc->flag) < 100) { if ((status = spcset(spc))) return status; } - /* Convert intermediate world coordinate x to X. */ - xp = x; - specp = spec; - statp = stat; - for (ix = 0; ix < nx; ix++, xp += sx, specp += sspec) { + // Convert intermediate world coordinate x to X. + const double *xp = x; + double *specp = spec; + int *statp = stat; + for (int ix = 0; ix < nx; ix++, xp += sx, specp += sspec, statp++) { *specp = spc->w[1] + (*xp)*spc->w[2]; - *(statp++) = 0; + *statp = 0; } - /* If X is the grism parameter then convert it to wavelength. */ + // If X is the grism parameter then convert it to wavelength. if (spc->isGrism) { specp = spec; - for (ix = 0; ix < nx; ix++, specp += sspec) { - beta = atand(*specp) + spc->w[3]; + for (int ix = 0; ix < nx; ix++, specp += sspec) { + double beta = atand(*specp) + spc->w[3]; *specp = (sind(beta) + spc->w[4]) * spc->w[5]; } } - /* Apply the non-linear step of the algorithm chain to convert the */ - /* X-type spectral variable to P-type intermediate spectral variable. */ + // Apply the non-linear step of the algorithm chain to convert the + // X-type spectral variable to P-type intermediate spectral variable. if (spc->spxX2P) { + int statX2P; if ((statX2P = spc->spxX2P(spc->w[0], nx, sspec, sspec, spec, spec, stat))) { if (statX2P == SPXERR_BAD_INSPEC_COORD) { @@ -555,14 +618,15 @@ int spcx2s( return wcserr_set(WCSERR_SET(SPCERR_BAD_SPEC_PARAMS), "Invalid spectral parameters: Frequency or wavelength is 0"); } else { - return wcserr_set(SPC_ERRMSG(statX2P)); + return wcserr_set(SPC_ERRMSG(spc_spxerr[statX2P])); } } } - /* Apply the linear step of the algorithm chain to convert P-type */ - /* intermediate spectral variable to the required S-type variable. */ + // Apply the linear step of the algorithm chain to convert P-type + // intermediate spectral variable to the required S-type variable. if (spc->spxP2S) { + int statP2S; if ((statP2S = spc->spxP2S(spc->w[0], nx, sspec, sspec, spec, spec, stat))) { if (statP2S == SPXERR_BAD_INSPEC_COORD) { @@ -571,7 +635,7 @@ int spcx2s( return wcserr_set(WCSERR_SET(SPCERR_BAD_SPEC_PARAMS), "Invalid spectral parameters: Frequency or wavelength is 0"); } else { - return wcserr_set(SPC_ERRMSG(statP2S)); + return wcserr_set(SPC_ERRMSG(spc_spxerr[statP2S])); } } } @@ -582,7 +646,7 @@ int spcx2s( return status; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- int spcs2x( struct spcprm *spc, @@ -596,25 +660,19 @@ int spcs2x( { static const char *function = "spcs2x"; - int statP2X, status = SPCERR_SUCCESS, statS2P; - double beta, s; - register int ispec; - register int *statp; - register const double *specp; - register double *xp; - struct wcserr **err; - - /* Initialize. */ + // Initialize. if (spc == 0x0) return SPCERR_NULL_POINTER; - err = &(spc->err); + struct wcserr **err = &(spc->err); - if (spc->flag == 0) { + int status = 0; + if (abs(spc->flag) < 100) { if ((status = spcset(spc))) return status; } - /* Apply the linear step of the algorithm chain to convert the S-type */ - /* spectral variable to P-type intermediate spectral variable. */ + // Apply the linear step of the algorithm chain to convert the S-type + // spectral variable to P-type intermediate spectral variable. if (spc->spxS2P) { + int statS2P; if ((statS2P = spc->spxS2P(spc->w[0], nspec, sspec, sx, spec, x, stat))) { if (statS2P == SPXERR_BAD_INSPEC_COORD) { status = SPCERR_BAD_SPEC; @@ -622,25 +680,27 @@ int spcs2x( return wcserr_set(WCSERR_SET(SPCERR_BAD_SPEC_PARAMS), "Invalid spectral parameters: Frequency or wavelength is 0"); } else { - return wcserr_set(SPC_ERRMSG(statS2P)); + return wcserr_set(SPC_ERRMSG(spc_spxerr[statS2P])); } } } else { - /* Just a copy. */ - xp = x; - specp = spec; - statp = stat; - for (ispec = 0; ispec < nspec; ispec++, specp += sspec, xp += sx) { + // Just a copy. + double *xp = x; + const double *specp = spec; + int *statp = stat; + for (int ispec = 0; ispec < nspec; ispec++, specp += sspec, xp += sx, + statp++) { *xp = *specp; - *(statp++) = 0; + *statp = 0; } } - /* Apply the non-linear step of the algorithm chain to convert P-type */ - /* intermediate spectral variable to X-type spectral variable. */ + // Apply the non-linear step of the algorithm chain to convert P-type + // intermediate spectral variable to X-type spectral variable. if (spc->spxP2X) { + int statP2X; if ((statP2X = spc->spxP2X(spc->w[0], nspec, sx, sx, x, x, stat))) { if (statP2X == SPCERR_BAD_SPEC) { status = SPCERR_BAD_SPEC; @@ -648,21 +708,21 @@ int spcs2x( return wcserr_set(WCSERR_SET(SPCERR_BAD_SPEC_PARAMS), "Invalid spectral parameters: Frequency or wavelength is 0"); } else { - return wcserr_set(SPC_ERRMSG(statP2X)); + return wcserr_set(SPC_ERRMSG(spc_spxerr[statP2X])); } } } if (spc->isGrism) { - /* Convert X-type spectral variable (wavelength) to grism parameter. */ - xp = x; - statp = stat; - for (ispec = 0; ispec < nspec; ispec++, xp += sx, statp++) { + // Convert X-type spectral variable (wavelength) to grism parameter. + double *xp = x; + int *statp = stat; + for (int ispec = 0; ispec < nspec; ispec++, xp += sx, statp++) { if (*statp) continue; - s = *xp/spc->w[5] - spc->w[4]; + double s = *xp/spc->w[5] - spc->w[4]; if (fabs(s) <= 1.0) { - beta = asind(s); + double beta = asind(s); *xp = tand(beta - spc->w[3]); } else { *statp = 1; @@ -671,11 +731,11 @@ int spcs2x( } - /* Convert X-type spectral variable to intermediate world coordinate x. */ - xp = x; - statp = stat; - for (ispec = 0; ispec < nspec; ispec++, xp += sx) { - if (*(statp++)) continue; + // Convert X-type spectral variable to intermediate world coordinate x. + double *xp = x; + int *statp = stat; + for (int ispec = 0; ispec < nspec; ispec++, xp += sx, statp++) { + if (*statp) continue; *xp -= spc->w[1]; *xp /= spc->w[2]; @@ -687,7 +747,7 @@ int spcs2x( return status; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- int spctyp( const char ctypei[9], @@ -704,7 +764,7 @@ int spctyp( ctypei, stype, scode, sname, units, ptype, xtype, restreq, NULL); } -/* : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : */ +// : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : int spctype( const char ctypei[9], @@ -720,14 +780,21 @@ int spctype( { static const char *function = "spctype"; - char ctype[9], ptype_t, sname_t[32], units_t[8], xtype_t; - int restreq_t = 0; + // Compiler balm for premature return on error. + if (ptype) *ptype = ' '; + if (xtype) *xtype = ' '; + if (restreq) *restreq = 0; + + if (err) *err = 0x0; - /* Copy with blank padding. */ + // Copy with blank padding. + char ctype[9]; sprintf(ctype, "%-8.8s", ctypei); ctype[8] = '\0'; - /* Validate the S-type spectral variable. */ + char sname_t[32], units_t[8], ptype_t; + int restreq_t = 0; + // Validate the S-type spectral variable. if (strncmp(ctype, "FREQ", 4) == 0) { strcpy(sname_t, "Frequency"); strcpy(units_t, "Hz"); @@ -781,9 +848,10 @@ int spctype( } - /* Determine X-type and validate the spectral algorithm code. */ + // Determine X-type and validate the spectral algorithm code. + char xtype_t; if ((xtype_t = ctype[5]) == ' ') { - /* The algorithm code must be completely blank. */ + // The algorithm code must be completely blank. if (strcmp(ctype+4, " ") != 0) { return wcserr_set(WCSERR_SET(SPCERR_BAD_SPEC_PARAMS), "Invalid spectral algorithm '%s'", ctype+4); @@ -796,21 +864,21 @@ int spctype( "Invalid spectral type '%s'", ctype); } else if (strcmp(ctype+5, "LOG") == 0 || strcmp(ctype+5, "TAB") == 0) { - /* Logarithmic or tabular axis, not linear in any spectral type. */ + // Logarithmic or tabular axis, not linear in any spectral type. } else if (xtype_t == 'G') { - /* Validate the algorithm code. */ + // Validate the algorithm code. if (ctype[6] != 'R') { return wcserr_set(WCSERR_SET(SPCERR_BAD_SPEC_PARAMS), "Invalid spectral algorithm '%s'", xtype_t); } - /* Grism coordinates... */ + // Grism coordinates... if (ctype[7] == 'I') { - /* ...in vacuum. */ + // ...in vacuum. xtype_t = 'w'; } else if (ctype[7] == 'A') { - /* ...in air. */ + // ...in air. xtype_t = 'a'; } else { return wcserr_set(WCSERR_SET(SPCERR_BAD_SPEC_PARAMS), @@ -818,22 +886,22 @@ int spctype( } } else if (ctype[6] != '2') { - /* Algorithm code has invalid syntax. */ + // Algorithm code has invalid syntax. return wcserr_set(WCSERR_SET(SPCERR_BAD_SPEC_PARAMS), "Invalid spectral algorithm syntax '%s'", xtype_t); } else if (ctype[7] != ptype_t && ctype[7] != '?') { - /* The P-, and S-type variables are inconsistent. */ + // The P-, and S-type variables are inconsistent. return wcserr_set(WCSERR_SET(SPCERR_BAD_SPEC_PARAMS), "In spectral type '%s', P- and S-type variables are inconsistent", ctype); } else if (ctype[7] == ctype[5]) { - /* Degenerate algorithm code. */ + // Degenerate algorithm code. sprintf(ctype+4, " "); } - /* Rest freq/wavelength required for transformation between P and X? */ + // Rest freq/wavelength required for transformation between P and X? if (strchr("FWAwa", (int)xtype_t)) { if (ptype_t == 'V') { restreq_t += 2; @@ -843,15 +911,15 @@ int spctype( restreq_t += 2; } } else if (strchr("LT", (int)xtype_t) == 0) { - /* Invalid X-type variable code. */ + // Invalid X-type variable code. return wcserr_set(WCSERR_SET(SPCERR_BAD_SPEC_PARAMS), "In spectral type '%s', invalid X-type variable code", ctype); } - /* Copy results. */ + // Copy results. if (stype) { - strncpy(stype, ctype, 4); + memcpy(stype, ctype, 4); stype[4] = '\0'; } if (scode) strcpy(scode, ctype+5); @@ -861,11 +929,10 @@ int spctype( if (xtype) *xtype = xtype_t; if (restreq) *restreq = restreq_t; - - return SPCERR_SUCCESS; + return 0; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- int spcspx( const char ctypeS[9], @@ -883,7 +950,7 @@ int spcspx( crvalX, dXdS, 0x0); } -/* : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : */ +// : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : int spcspxe( const char ctypeS[9], @@ -900,47 +967,48 @@ int spcspxe( { static const char *function = "spcspxe"; - char scode[4], stype[5], type[8]; + // Analyse the spectral axis code. + char stype[5], scode[4]; int status; - double dPdS, dXdP; - struct spxprm spx; - - - /* Analyse the spectral axis code. */ if ((status = spctype(ctypeS, stype, scode, 0x0, 0x0, ptype, xtype, restreq, err))) { return status; } - if (strstr("LT", xtype)) { - /* Can't handle logarithmic or tabular coordinates. */ + if (strchr("LT", (int)(*xtype))) { + // Can't handle logarithmic or tabular coordinates. return wcserr_set(WCSERR_SET(SPCERR_BAD_SPEC_PARAMS), "Can't handle logarithmic or tabular coordinates"); } - /* Do we have rest frequency and/or wavelength as required? */ + // Do we have rest frequency and/or wavelength as required? if ((*restreq)%3 && restfrq == 0.0 && restwav == 0.0) { return wcserr_set(WCSERR_SET(SPCERR_BAD_SPEC_PARAMS), "Missing required rest frequency or wavelength"); } - /* Compute all spectral parameters and their derivatives. */ + // Compute all spectral parameters and their derivatives. + char type[8]; strcpy(type, stype); + + struct spxprm spx; spx.err = (err ? *err : 0x0); if ((status = specx(type, crvalS, restfrq, restwav, &spx))) { - status = SPCERR_BAD_SPEC_PARAMS; + status = spc_spxerr[status]; if (err) { - (*err)->status = status; + if ((*err = spx.err)) { + (*err)->status = status; + } } else { - free(spx.err); + wcserr_clear(&(spx.err)); } return status; } - /* Transform S-P (linear) and P-X (non-linear). */ - dPdS = 0.0; - dXdP = 0.0; + // Transform S-P (linear) and P-X (non-linear). + double dPdS = 0.0; + double dXdP = 0.0; if (*ptype == 'F') { if (strcmp(stype, "FREQ") == 0) { dPdS = 1.0; @@ -1034,10 +1102,10 @@ int spcspxe( *dXdS = dXdP * dPdS; - return SPCERR_SUCCESS; + return 0; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- int spcxps( const char ctypeS[9], @@ -1055,7 +1123,7 @@ int spcxps( crvalS, dSdX, NULL); } -/* : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : */ +// : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : int spcxpse( const char ctypeS[9], @@ -1072,30 +1140,32 @@ int spcxpse( { static const char *function = "spcxpse"; - char scode[4], stype[5], type[8]; - int status; - double dPdX, dSdP; - struct spxprm spx; + // Compiler balm for premature return on error. + *crvalS = 0.0; + *dSdX = 0.0; - /* Analyse the spectral axis type. */ + // Analyse the spectral axis type. + char stype[5], scode[4]; + int status; if ((status = spctype(ctypeS, stype, scode, 0x0, 0x0, ptype, xtype, restreq, err))) { return status; } - if (strstr("LT", xtype)) { - /* Can't handle logarithmic or tabular coordinates. */ + if (strchr("LT", (int)(*xtype))) { + // Can't handle logarithmic or tabular coordinates. return wcserr_set(WCSERR_SET(SPCERR_BAD_SPEC_PARAMS), "Can't handle logarithmic or tabular coordinates"); } - /* Do we have rest frequency and/or wavelength as required? */ + // Do we have rest frequency and/or wavelength as required? if ((*restreq)%3 && restfrq == 0.0 && restwav == 0.0) { return wcserr_set(WCSERR_SET(SPCERR_BAD_SPEC_PARAMS), "Missing required rest frequency or wavelength"); } - /* Compute all spectral parameters and their derivatives. */ + // Compute all spectral parameters and their derivatives. + char type[8]; if (*xtype == 'F') { strcpy(type, "FREQ"); } else if (*xtype == 'W' || *xtype == 'w') { @@ -1106,21 +1176,24 @@ int spcxpse( strcpy(type, "VELO"); } + struct spxprm spx; spx.err = (err ? *err : 0x0); if (specx(type, crvalX, restfrq, restwav, &spx)) { - status = SPCERR_BAD_SPEC_PARAMS; + status = spc_spxerr[status]; if (err) { - (*err)->status = status; + if ((*err = spx.err)) { + (*err)->status = status; + } } else { - free(spx.err); + wcserr_clear(&(spx.err)); } return status; } - /* Transform X-P (non-linear) and P-S (linear). */ - dPdX = 0.0; - dSdP = 0.0; + // Transform X-P (non-linear) and P-S (linear). + double dPdX = 0.0; + double dSdP = 0.0; if (*ptype == 'F') { if (*xtype == 'F') { dPdX = 1.0; @@ -1209,10 +1282,10 @@ int spcxpse( *dSdX = dSdP * dPdX; - return SPCERR_SUCCESS; + return 0; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- int spctrn( const char ctypeS1[9], @@ -1229,7 +1302,7 @@ int spctrn( ctypeS2, crvalS2, cdeltS2, NULL); } -/* : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : */ +// : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : int spctrne( const char ctypeS1[9], @@ -1245,14 +1318,15 @@ int spctrne( { static const char *function = "spctrne"; - char *cp, ptype1, ptype2, stype1[5], stype2[5], xtype1, xtype2; - int restreq; - double crvalX, dS2dX, dXdS1; + // Compiler balm for premature return on error. + *crvalS2 = 0.0; + *cdeltS2 = 0.0; if (restfrq == 0.0 && restwav == 0.0) { - /* If translating between two velocity-characteristic types, or between - two wave-characteristic types, then we may need to set a dummy rest - frequency or wavelength to perform the calculations. */ + // If translating between two velocity-characteristic types, or between + // two wave-characteristic types, then we may need to set a dummy rest + // frequency or wavelength to perform the calculations. + char stype1[5], stype2[5]; strncpy(stype1, ctypeS1, 4); strncpy(stype2, ctypeS2, 4); stype1[4] = stype2[4] = '\0'; @@ -1262,18 +1336,22 @@ int spctrne( } } - if (spcspxe(ctypeS1, crvalS1, restfrq, restwav, &ptype1, &xtype1, - &restreq, &crvalX, &dXdS1, err)) { - return (*err)->status; + char ptype1, xtype1; + int status, restreq; + double crvalX, dXdS1; + if ((status = spcspxe(ctypeS1, crvalS1, restfrq, restwav, &ptype1, &xtype1, + &restreq, &crvalX, &dXdS1, err))) { + return status; } - /* Pad with blanks. */ + // Pad with blanks. + char *cp; ctypeS2[8] = '\0'; for (cp = ctypeS2; *cp; cp++); while (cp < ctypeS2+8) *(cp++) = ' '; if (strncmp(ctypeS2+5, "???", 3) == 0) { - /* Set the algorithm code if required. */ + // Set the algorithm code if required. if (xtype1 == 'w') { strcpy(ctypeS2+5, "GRI"); } else if (xtype1 == 'a') { @@ -1284,12 +1362,14 @@ int spctrne( } } - if (spcxpse(ctypeS2, crvalX, restfrq, restwav, &ptype2, &xtype2, - &restreq, crvalS2, &dS2dX, err)) { - return (*err)->status; + char ptype2, xtype2; + double dS2dX; + if ((status = spcxpse(ctypeS2, crvalX, restfrq, restwav, &ptype2, &xtype2, + &restreq, crvalS2, &dS2dX, err))) { + return status; } - /* Are the X-types compatible? */ + // Are the X-types compatible? if (xtype2 != xtype1) { return wcserr_set(WCSERR_SET(SPCERR_BAD_SPEC_PARAMS), "Incompatible X-types '%c' and '%c'", xtype1, xtype2); @@ -1305,10 +1385,10 @@ int spctrne( *cdeltS2 = dS2dX * dXdS1 * cdeltS1; - return SPCERR_SUCCESS; + return 0; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- int spcaips( const char ctypeA[9], @@ -1319,21 +1399,20 @@ int spcaips( { const char *frames[] = {"LSRK", "BARYCENT", "TOPOCENT", "LSRD", "GEOCENTR", "SOURCE", "GALACTOC"}; - char *fcode; - int ivf, status; - /* Make a null-filled copy of ctypeA. */ + // Make a null-filled copy of ctypeA. if (ctype != ctypeA) strncpy(ctype, ctypeA, 8); ctype[8] = '\0'; wcsutil_null_fill(9, ctype); *specsys = '\0'; - /* Is it a recognized AIPS-convention type? */ - status = SPCERR_NO_CHANGE; + // Is it a recognized AIPS-convention type? + int status = SPCERR_NO_CHANGE; if (strncmp(ctype, "FREQ", 4) == 0 || strncmp(ctype, "VELO", 4) == 0 || strncmp(ctype, "FELO", 4) == 0) { - /* Look for the Doppler frame. */ + // Look for the Doppler frame. + char *fcode; if (*(fcode = ctype+4)) { if (strcmp(fcode, "-LSR") == 0) { strcpy(specsys, "LSRK"); @@ -1342,27 +1421,27 @@ int spcaips( } else if (strcmp(fcode, "-OBS") == 0) { strcpy(specsys, "TOPOCENT"); } else { - /* Not a recognized AIPS spectral type. */ + // Not a recognized AIPS spectral type. return SPCERR_NO_CHANGE; } *fcode = '\0'; - status = SPCERR_SUCCESS; + status = 0; } - /* VELREF takes precedence if present. */ - ivf = velref%256; + // VELREF takes precedence if present. + int ivf = velref%256; if (0 < ivf && ivf <= 7) { strcpy(specsys, frames[ivf-1]); - status = SPCERR_SUCCESS; + status = 0; } else if (ivf) { status = SPCERR_BAD_SPEC_PARAMS; } if (strcmp(ctype, "VELO") == 0) { - /* Check that we found an AIPS-convention Doppler frame. */ + // Check that we found an AIPS-convention Doppler frame. if (*specsys) { - /* 'VELO' in AIPS means radio or optical depending on VELREF. */ + // 'VELO' in AIPS means radio or optical depending on VELREF. ivf = velref/256; if (ivf == 0) { strcpy(ctype, "VOPT"); @@ -1373,10 +1452,10 @@ int spcaips( } } } else if (strcmp(ctype, "FELO") == 0) { - /* Uniform in frequency but expressed as an optical velocity (strictly - we should also have found an AIPS-convention Doppler frame). */ + // Uniform in frequency but expressed as an optical velocity (strictly + // we should also have found an AIPS-convention Doppler frame). strcpy(ctype, "VOPT-F2W"); - if (status < 0) status = SPCERR_SUCCESS; + if (status < 0) status = 0; } } diff --git a/cextern/wcslib/C/spc.h b/cextern/wcslib/C/spc.h index 4c9f97267ad9..4049b0945e20 100644 --- a/cextern/wcslib/C/spc.h +++ b/cextern/wcslib/C/spc.h @@ -1,7 +1,6 @@ /*============================================================================ - - WCSLIB 4.14 - an implementation of the FITS WCS standard. - Copyright (C) 1995-2012, Mark Calabretta + WCSLIB 8.6 - an implementation of the FITS WCS standard. + Copyright (C) 1995-2026, Mark Calabretta This file is part of WCSLIB. @@ -18,41 +17,43 @@ You should have received a copy of the GNU Lesser General Public License along with WCSLIB. If not, see http://www.gnu.org/licenses. - Direct correspondence concerning WCSLIB to mark@calabretta.id.au - Author: Mark Calabretta, Australia Telescope National Facility, CSIRO. - http://www.atnf.csiro.au/people/Mark.Calabretta - $Id: spc.h,v 4.14 2012/07/13 10:02:27 cal103 Exp $ + http://www.atnf.csiro.au/computing/software/wcs + $Id: spc.h,v 8.6 2026/03/29 13:53:56 mcalabre Exp $ *============================================================================= * -* WCSLIB 4.14 - C routines that implement the spectral coordinate systems -* recognized by the FITS World Coordinate System (WCS) standard. Refer to -* -* "Representations of world coordinates in FITS", -* Greisen, E.W., & Calabretta, M.R. 2002, A&A, 395, 1061 (Paper I) -* -* "Representations of spectral coordinates in FITS", -* Greisen, E.W., Calabretta, M.R., Valdes, F.G., & Allen, S.L. -* 2006, A&A, 446, 747 (Paper III) -* -* Refer to the README file provided with WCSLIB for an overview of the -* library. +* WCSLIB 8.6 - C routines that implement the FITS World Coordinate System +* (WCS) standard. Refer to the README file provided with WCSLIB for an +* overview of the library. * * * Summary of the spc routines * --------------------------- -* These routines implement the part of the FITS WCS standard that deals with -* spectral coordinates. They define methods to be used for computing spectral -* world coordinates from intermediate world coordinates (a linear -* transformation of image pixel coordinates), and vice versa. They are based -* on the spcprm struct which contains all information needed for the -* computations. The struct contains some members that must be set by the -* user, and others that are maintained by these routines, somewhat like a -* C++ class but with no encapsulation. +* Routines in this suite implement the part of the FITS World Coordinate +* System (WCS) standard that deals with spectral coordinates, as described in +* += "Representations of world coordinates in FITS", += Greisen, E.W., & Calabretta, M.R. 2002, A&A, 395, 1061 (WCS Paper I) += += "Representations of spectral coordinates in FITS", += Greisen, E.W., Calabretta, M.R., Valdes, F.G., & Allen, S.L. += 2006, A&A, 446, 747 (WCS Paper III) +* +* These routines define methods to be used for computing spectral world +* coordinates from intermediate world coordinates (a linear transformation +* of image pixel coordinates), and vice versa. They are based on the spcprm +* struct which contains all information needed for the computations. The +* struct contains some members that must be set by the user, and others that +* are maintained by these routines, somewhat like a C++ class but with no +* encapsulation. * * Routine spcini() is provided to initialize the spcprm struct with default * values, spcfree() reclaims any memory that may have been allocated to store -* an error message, and spcprt() prints its contents. +* an error message, spcsize() computes its total size including allocated +* memory, spcenq() returns information about the state of the struct, and +* spcprt() prints its contents. +* +* spcperr() prints the error message(s) (if any) stored in a spcprm struct. * * A setup routine, spcset(), computes intermediate values in the spcprm struct * from parameters in it that were supplied by the user. The struct always @@ -92,24 +93,24 @@ * expressed. Each S-type is encoded as four characters and is * linearly related to one of four basic types as follows: * -* F: frequency -* 'FREQ': frequency -* 'AFRQ': angular frequency -* 'ENER': photon energy -* 'WAVN': wave number -* 'VRAD': radio velocity +* F (Frequency): +* - 'FREQ': frequency +* - 'AFRQ': angular frequency +* - 'ENER': photon energy +* - 'WAVN': wave number +* - 'VRAD': radio velocity * -* W: wavelength in vacuo -* 'WAVE': wavelength -* 'VOPT': optical velocity -* 'ZOPT': redshift +* W (Wavelength in vacuo): +* - 'WAVE': wavelength +* - 'VOPT': optical velocity +* - 'ZOPT': redshift * -* A: wavelength in air -* 'AWAV': wavelength in air +* A (wavelength in Air): +* - 'AWAV': wavelength in air * -* V: velocity -* 'VELO': relativistic velocity -* 'BETA': relativistic beta factor +* V (Velocity): +* - 'VELO': relativistic velocity +* - 'BETA': relativistic beta factor * * The S-type forms the first four characters of the CTYPEia keyvalue, * and CRVALia and CDELTia are expressed as S-type quantities so that @@ -154,6 +155,11 @@ * (wavelength), and the X-type is 'F' (frequency) by the nature of the * instrument. * +* Air-to-vacuum wavelength conversion: +* ------------------------------------ +* Please refer to the prologue of spx.h for important comments relating to the +* air-to-vacuum wavelength conversion. +* * Argument checking: * ------------------ * The input spectral values are only checked for values that would result in @@ -176,6 +182,10 @@ * spcini() sets all members of a spcprm struct to default values. It should * be used to initialize every spcprm struct. * +* PLEASE NOTE: If the spcprm struct has already been initialized then, before +* reinitializing it, spcfree() should be used to free any memory that may have +* been allocated to store an error message. Else a memory leak may result. +* * Given and returned: * spc struct spcprm* * Spectral transformation parameters. @@ -191,7 +201,7 @@ * spcfree() frees any memory that may have been allocated to store an error * message in the spcprm struct. * -* Given: +* Given and returned: * spc struct spcprm* * Spectral transformation parameters. * @@ -201,6 +211,58 @@ * 1: Null spcprm pointer passed. * * +* spcsize() - Compute the size of a spcprm struct +* ----------------------------------------------- +* spcsize() computes the full size of a spcprm struct, including allocated +* memory. +* +* Given: +* spc const struct spcprm* +* Spectral transformation parameters. +* +* If NULL, the base size of the struct and the allocated +* size are both set to zero. +* +* Returned: +* sizes int[2] The first element is the base size of the struct as +* returned by sizeof(struct spcprm). The second element +* is the total allocated size, in bytes. This figure +* includes memory allocated for the constituent struct, +* spcprm::err. +* +* It is not an error for the struct not to have been set +* up via spcset(). +* +* Function return value: +* int Status return value: +* 0: Success. +* +* +* spcenq() - enquire about the state of a spcprm struct +* ----------------------------------------------------- +* spcenq() may be used to obtain information about the state of a spcprm +* struct. The function returns a true/false answer for the enquiry asked. +* +* Given: +* spc const struct spcprm* +* Spectral transformation parameters. +* +* enquiry int Enquiry according to the following parameters: +* SPCENQ_MEM: memory in the struct is being managed by +* WCSLIB (see spcini()). +* SPCENQ_SET: the struct has been set up by spcset(). +* SPCENQ_BYP: the struct is in bypass mode (see +* spcset()). +* These may be combined by logical OR, e.g. +* SPCENQ_MEM | SPCENQ_SET. The enquiry result will be +* the logical AND of the individual results. +* +* Function return value: +* int Enquiry result: +* 0: No. +* 1: Yes. +* +* * spcprt() - Print routine for the spcprm struct * ---------------------------------------------- * spcprt() prints the contents of a spcprm struct using wcsprintf(). Mainly @@ -216,6 +278,25 @@ * 1: Null spcprm pointer passed. * * +* spcperr() - Print error messages from a spcprm struct +* ----------------------------------------------------- +* spcperr() prints the error message(s) (if any) stored in a spcprm struct. +* If there are no errors then nothing is printed. It uses wcserr_prt(), q.v. +* +* Given: +* spc const struct spcprm* +* Spectral transformation parameters. +* +* prefix const char * +* If non-NULL, each output line will be prefixed with +* this string. +* +* Function return value: +* int Status return value: +* 0: Success. +* 1: Null spcprm pointer passed. +* +* * spcset() - Setup routine for the spcprm struct * ---------------------------------------------- * spcset() sets up a spcprm struct according to information supplied within @@ -225,6 +306,13 @@ * spcx2s() and spcs2x() if spcprm::flag is anything other than a predefined * magic value. * +* spcset() normally operates regardless of the value of spcprm::flag; i.e. +* even if a struct was previously set up it will be reset unconditionally. +* However, a spcprm struct may be put into "bypass" mode by invoking spcset() +* initially with spcprm::flag == 1 (rather than 0). spcset() will return +* immediately if invoked on a struct in that state. To take a struct out of +* bypass mode, simply reset spcprm::flag to zero. See also spcenq(). +* * Given and returned: * spc struct spcprm* * Spectral transformation parameters. @@ -377,9 +465,11 @@ * X-types) only if restreq%3 != 0. * * err struct wcserr ** -* For function return values > 1, this struct will -* contain a detailed error message. May be NULL if an -* error message is not desired. +* If enabled, for function return values > 1, this +* struct will contain a detailed error message, see +* wcserr_enable(). May be NULL if an error message is +* not desired. Otherwise, the user is responsible for +* deleting the memory allocated for the wcserr struct. * * Function return value: * int Status return value: @@ -435,9 +525,11 @@ * coordinate. * * err struct wcserr ** -* For function return values > 1, this struct will -* contain a detailed error message. May be NULL if an -* error message is not desired. +* If enabled, for function return values > 1, this +* struct will contain a detailed error message, see +* wcserr_enable(). May be NULL if an error message is +* not desired. Otherwise, the user is responsible for +* deleting the memory allocated for the wcserr struct. * * Function return value: * int Status return value: @@ -495,9 +587,11 @@ * keyvalue. * * err struct wcserr ** -* For function return values > 1, this struct will -* contain a detailed error message. May be NULL if an -* error message is not desired. +* If enabled, for function return values > 1, this +* struct will contain a detailed error message, see +* wcserr_enable(). May be NULL if an error message is +* not desired. Otherwise, the user is responsible for +* deleting the memory allocated for the wcserr struct. * * Function return value: * int Status return value: @@ -561,9 +655,11 @@ * units. * * err struct wcserr ** -* For function return values > 1, this struct will -* contain a detailed error message. May be NULL if an -* error message is not desired. +* If enabled, for function return values > 1, this +* struct will contain a detailed error message, see +* wcserr_enable(). May be NULL if an error message is +* not desired. Otherwise, the user is responsible for +* deleting the memory allocated for the wcserr struct. * * Function return value: * int Status return value: @@ -614,11 +710,14 @@ * VELREF takes precedence over CTYPEia in defining the * Doppler frame, e.g. * -= ctypeA = 'VELO-HEL' -= velref = 1 += ctypeA = 'VELO-HEL'; += velref = 1; * * returns ctype = 'VOPT' with specsys set to 'LSRK'. * +* If omitted from the header, the default value of +* VELREF is 0. +* * Returned: * ctype char[9] Translated CTYPEia keyvalue, or a copy of ctypeA if no * translation was performed (in which case any trailing @@ -646,8 +745,8 @@ * internal use only. * * int flag -* (Given and returned) This flag must be set to zero whenever any of the -* following spcprm structure members are set or changed: +* (Given and returned) This flag must be set to zero (or 1, see spcset()) +* whenever any of the following spcprm members are set or changed: * * - spcprm::type, * - spcprm::code, @@ -711,26 +810,26 @@ * (An unused variable inserted for alignment purposes only.) * * struct wcserr *err -* (Returned) If enabled, when an error status is returned this structure +* (Returned) If enabled, when an error status is returned, this struct * contains detailed information about the error, see wcserr_enable(). * * void *padding2 * (An unused variable inserted for alignment purposes only.) * int (*spxX2P)(SPX_ARGS) -* (Returned) The first and ... +* (For internal use only) The first and ... * int (*spxP2S)(SPX_ARGS) -* (Returned) ... the second of the pointers to the transformation -* functions in the two-step algorithm chain X -> P -> S in the -* pixel-to-spectral direction where the non-linear transformation is from -* X to P. The argument list, SPX_ARGS, is defined in spx.h. +* (For internal use only) ... the second of the pointers to the +* transformation functions in the two-step algorithm chain X -> P -> S in +* the pixel-to-spectral direction where the non-linear transformation is +* from X to P. The argument list, SPX_ARGS, is defined in spx.h. * * int (*spxS2P)(SPX_ARGS) -* (Returned) The first and ... +* (For internal use only) The first and ... * int (*spxP2X)(SPX_ARGS) -* (Returned) ... the second of the pointers to the transformation -* functions in the two-step algorithm chain S -> P -> X in the -* spectral-to-pixel direction where the non-linear transformation is from -* P to X. The argument list, SPX_ARGS, is defined in spx.h. +* (For internal use only) ... the second of the pointers to the +* transformation functions in the two-step algorithm chain S -> P -> X in +* the spectral-to-pixel direction where the non-linear transformation is +* from P to X. The argument list, SPX_ARGS, is defined in spx.h. * * * Global variable: const char *spc_errmsg[] - Status return messages @@ -743,77 +842,82 @@ #define WCSLIB_SPC #include "spx.h" -#include "wcserr.h" #ifdef __cplusplus extern "C" { #endif +enum spcenq_enum { + SPCENQ_SET = 2, // spcprm struct has been set up. + SPCENQ_BYP = 4, // spcprm struct is in bypass mode. +}; extern const char *spc_errmsg[]; enum spc_errmsg_enum { - SPCERR_NO_CHANGE = -1, /* No change. */ - SPCERR_SUCCESS = 0, /* Success. */ - SPCERR_NULL_POINTER = 1, /* Null spcprm pointer passed. */ - SPCERR_BAD_SPEC_PARAMS = 2, /* Invalid spectral parameters. */ - SPCERR_BAD_X = 3, /* One or more of x coordinates were - invalid. */ - SPCERR_BAD_SPEC = 4 /* One or more of the spec coordinates were - invalid. */ + SPCERR_NO_CHANGE = -1, // No change. + SPCERR_SUCCESS = 0, // Success. + SPCERR_NULL_POINTER = 1, // Null spcprm pointer passed. + SPCERR_BAD_SPEC_PARAMS = 2, // Invalid spectral parameters. + SPCERR_BAD_X = 3, // One or more of x coordinates were + // invalid. + SPCERR_BAD_SPEC = 4 // One or more of the spec coordinates were + // invalid. }; struct spcprm { - /* Initialization flag (see the prologue above). */ - /*------------------------------------------------------------------------*/ - int flag; /* Set to zero to force initialization. */ - - /* Parameters to be provided (see the prologue above). */ - /*------------------------------------------------------------------------*/ - char type[8]; /* Four-letter spectral variable type. */ - char code[4]; /* Three-letter spectral algorithm code. */ - - double crval; /* Reference value (CRVALia), SI units. */ - double restfrq; /* Rest frequency, Hz. */ - double restwav; /* Rest wavelength, m. */ - - double pv[7]; /* Grism parameters: */ - /* 0: G, grating ruling density. */ - /* 1: m, interference order. */ - /* 2: alpha, angle of incidence. */ - /* 3: n_r, refractive index at lambda_r. */ - /* 4: n'_r, dn/dlambda at lambda_r. */ - /* 5: epsilon, grating tilt angle. */ - /* 6: theta, detector tilt angle. */ - - /* Information derived from the parameters supplied. */ - /*------------------------------------------------------------------------*/ - double w[6]; /* Intermediate values. */ - /* 0: Rest frequency or wavelength (SI). */ - /* 1: CRVALX (SI units). */ - /* 2: CDELTX/CDELTia = dX/dS (SI units). */ - /* The remainder are grism intermediates. */ - - int isGrism; /* Grism coordinates? 1: vacuum, 2: air. */ - int padding1; /* (Dummy inserted for alignment purposes.) */ - - /* Error handling */ - /*------------------------------------------------------------------------*/ + // Initialization flag (see the prologue above). + //-------------------------------------------------------------------------- + int flag; // Set to zero to force initialization. + + // Parameters to be provided (see the prologue above). + //-------------------------------------------------------------------------- + char type[8]; // Four-letter spectral variable type. + char code[4]; // Three-letter spectral algorithm code. + + double crval; // Reference value (CRVALia), SI units. + double restfrq; // Rest frequency, Hz. + double restwav; // Rest wavelength, m. + + double pv[7]; // Grism parameters: + // 0: G, grating ruling density. + // 1: m, interference order. + // 2: alpha, angle of incidence. + // 3: n_r, refractive index at lambda_r. + // 4: n'_r, dn/dlambda at lambda_r. + // 5: epsilon, grating tilt angle. + // 6: theta, detector tilt angle. + + // Information derived from the parameters supplied. + //-------------------------------------------------------------------------- + double w[6]; // Intermediate values. + // 0: Rest frequency or wavelength (SI). + // 1: CRVALX (SI units). + // 2: CDELTX/CDELTia = dX/dS (SI units). + // The remainder are grism intermediates. + + int isGrism; // Grism coordinates? 1: vacuum, 2: air. + int padding1; // (Dummy inserted for alignment purposes.) + + // Error messaging, if enabled. + //-------------------------------------------------------------------------- struct wcserr *err; - /* Private */ - /*------------------------------------------------------------------------*/ - void *padding2; /* (Dummy inserted for alignment purposes.) */ - int (*spxX2P)(SPX_ARGS); /* Pointers to the transformation functions */ - int (*spxP2S)(SPX_ARGS); /* in the two-step algorithm chain in the */ - /* pixel-to-spectral direction. */ + //-------------------------------------------------------------------------- + // Private - the remainder are for internal use. + //-------------------------------------------------------------------------- + void *padding2; // (Dummy inserted for alignment purposes.) + + int (*spxX2P)(SPX_ARGS); // Pointers to the transformation functions + int (*spxP2S)(SPX_ARGS); // in the two-step algorithm chain in the + // pixel-to-spectral direction. - int (*spxS2P)(SPX_ARGS); /* Pointers to the transformation functions */ - int (*spxP2X)(SPX_ARGS); /* in the two-step algorithm chain in the */ - /* spectral-to-pixel direction. */ + int (*spxS2P)(SPX_ARGS); // Pointers to the transformation functions + int (*spxP2X)(SPX_ARGS); // in the two-step algorithm chain in the + // spectral-to-pixel direction. }; -/* Size of the spcprm struct in int units, used by the Fortran wrappers. */ +// Size of the spcprm struct in int units, used by the Fortran wrappers. #define SPCLEN (sizeof(struct spcprm)/sizeof(int)) @@ -821,8 +925,14 @@ int spcini(struct spcprm *spc); int spcfree(struct spcprm *spc); +int spcsize(const struct spcprm *spc, int sizes[2]); + +int spcenq(const struct spcprm *spc, int enquiry); + int spcprt(const struct spcprm *spc); +int spcperr(const struct spcprm *spc, const char *prefix); + int spcset(struct spcprm *spc); int spcx2s(struct spcprm *spc, int nx, int sx, int sspec, @@ -831,46 +941,46 @@ int spcx2s(struct spcprm *spc, int nx, int sx, int sspec, int spcs2x(struct spcprm *spc, int nspec, int sspec, int sx, const double spec[], double x[], int stat[]); -int spctype(const char ctype[], char stype[], char scode[], char sname[], +int spctype(const char ctype[9], char stype[], char scode[], char sname[], char units[], char *ptype, char *xtype, int *restreq, struct wcserr **err); -int spcspxe(const char ctypeS[], double crvalS, double restfrq, +int spcspxe(const char ctypeS[9], double crvalS, double restfrq, double restwav, char *ptype, char *xtype, int *restreq, double *crvalX, double *dXdS, struct wcserr **err); -int spcxpse(const char ctypeS[], double crvalX, double restfrq, +int spcxpse(const char ctypeS[9], double crvalX, double restfrq, double restwav, char *ptype, char *xtype, int *restreq, double *crvalS, double *dSdX, struct wcserr **err); -int spctrne(const char ctypeS1[], double crvalS1, double cdeltS1, - double restfrq, double restwav, char ctypeS2[], double *crvalS2, +int spctrne(const char ctypeS1[9], double crvalS1, double cdeltS1, + double restfrq, double restwav, char ctypeS2[9], double *crvalS2, double *cdeltS2, struct wcserr **err); -int spcaips(const char ctypeA[], int velref, char ctype[], char specsys[]); +int spcaips(const char ctypeA[9], int velref, char ctype[9], char specsys[9]); -/* Deprecated. */ +// Deprecated. #define spcini_errmsg spc_errmsg #define spcprt_errmsg spc_errmsg #define spcset_errmsg spc_errmsg #define spcx2s_errmsg spc_errmsg #define spcs2x_errmsg spc_errmsg -int spctyp(const char ctype[], char stype[], char scode[], char sname[], +int spctyp(const char ctype[9], char stype[], char scode[], char sname[], char units[], char *ptype, char *xtype, int *restreq); -int spcspx(const char ctypeS[], double crvalS, double restfrq, double restwav, - char *ptype, char *xtype, int *restreq, double *crvalX, - double *dXdS); -int spcxps(const char ctypeS[], double crvalX, double restfrq, double restwav, - char *ptype, char *xtype, int *restreq, double *crvalS, - double *dSdX); -int spctrn(const char ctypeS1[], double crvalS1, double cdeltS1, - double restfrq, double restwav, char ctypeS2[], double *crvalS2, +int spcspx(const char ctypeS[9], double crvalS, double restfrq, + double restwav, char *ptype, char *xtype, int *restreq, + double *crvalX, double *dXdS); +int spcxps(const char ctypeS[9], double crvalX, double restfrq, + double restwav, char *ptype, char *xtype, int *restreq, + double *crvalS, double *dSdX); +int spctrn(const char ctypeS1[9], double crvalS1, double cdeltS1, + double restfrq, double restwav, char ctypeS2[9], double *crvalS2, double *cdeltS2); #ifdef __cplusplus } #endif -#endif /* WCSLIB_SPC */ +#endif // WCSLIB_SPC diff --git a/cextern/wcslib/C/sph.c b/cextern/wcslib/C/sph.c index f0a44300f4de..f4b0743fb619 100644 --- a/cextern/wcslib/C/sph.c +++ b/cextern/wcslib/C/sph.c @@ -1,7 +1,6 @@ /*============================================================================ - - WCSLIB 4.14 - an implementation of the FITS WCS standard. - Copyright (C) 1995-2012, Mark Calabretta + WCSLIB 8.6 - an implementation of the FITS WCS standard. + Copyright (C) 1995-2026, Mark Calabretta This file is part of WCSLIB. @@ -18,11 +17,9 @@ You should have received a copy of the GNU Lesser General Public License along with WCSLIB. If not, see http://www.gnu.org/licenses. - Direct correspondence concerning WCSLIB to mark@calabretta.id.au - Author: Mark Calabretta, Australia Telescope National Facility, CSIRO. - http://www.atnf.csiro.au/people/Mark.Calabretta - $Id: sph.c,v 4.14 2012/07/13 10:02:27 cal103 Exp $ + http://www.atnf.csiro.au/computing/software/wcs + $Id: sph.c,v 8.6 2026/03/29 13:53:56 mcalabre Exp $ *===========================================================================*/ #include @@ -33,7 +30,7 @@ #define tol 1.0e-5 -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- int sphx2s( const double eul[5], @@ -47,13 +44,7 @@ int sphx2s( double lat[]) { - int mphi, mtheta, rowlen, rowoff; - double cosphi, costhe, costhe3, costhe4, dlng, dphi, sinphi, sinthe, - sinthe3, sinthe4, x, y, z; - register int iphi, itheta; - register const double *phip, *thetap; - register double *latp, *lngp; - + int mphi, mtheta; if (ntheta > 0) { mphi = nphi; mtheta = ntheta; @@ -64,21 +55,23 @@ int sphx2s( } - /* Check for a simple change in origin of longitude. */ + // Check for special-case rotations. if (eul[4] == 0.0) { if (eul[1] == 0.0) { - dlng = fmod(eul[0] + 180.0 - eul[2], 360.0); - - lngp = lng; - latp = lat; - phip = phi; - thetap = theta; - for (itheta = 0; itheta < ntheta; itheta++) { - for (iphi = 0; iphi < mphi; iphi++) { + // Simple change in origin of longitude. + double dlng = fmod(eul[0] + 180.0 - eul[2], 360.0); + + int jphi = 0; + const double *thetap = theta; + double *lngp = lng; + double *latp = lat; + for (int itheta = 0; itheta < ntheta; itheta++, thetap += spt) { + const double *phip = phi + (jphi%nphi)*spt; + for (int iphi = 0; iphi < mphi; iphi++, phip += spt, jphi++) { *lngp = *phip + dlng; *latp = *thetap; - /* Normalize the celestial longitude. */ + // Normalize the celestial longitude. if (eul[0] >= 0.0) { if (*lngp < 0.0) *lngp += 360.0; } else { @@ -91,26 +84,26 @@ int sphx2s( *lngp += 360.0; } - lngp += sll; - latp += sll; - phip += spt; - thetap += spt; + lngp += sll; + latp += sll; } } } else { - dlng = fmod(eul[0] + eul[2], 360.0); - - lngp = lng; - latp = lat; - phip = phi; - thetap = theta; - for (itheta = 0; itheta < ntheta; itheta++) { - for (iphi = 0; iphi < mphi; iphi++) { + // Pole-flip with change in origin of longitude. + double dlng = fmod(eul[0] + eul[2], 360.0); + + int jphi = 0; + const double *thetap = theta; + double *lngp = lng; + double *latp = lat; + for (int itheta = 0; itheta < ntheta; itheta++, thetap += spt) { + const double *phip = phi + (jphi%nphi)*spt; + for (int iphi = 0; iphi < mphi; iphi++, phip += spt, jphi++) { *lngp = dlng - *phip; *latp = -(*thetap); - /* Normalize the celestial longitude. */ + // Normalize the celestial longitude. if (eul[0] >= 0.0) { if (*lngp < 0.0) *lngp += 360.0; } else { @@ -123,10 +116,8 @@ int sphx2s( *lngp += 360.0; } - lngp += sll; - latp += sll; - phip += spt; - thetap += spt; + lngp += sll; + latp += sll; } } } @@ -135,48 +126,52 @@ int sphx2s( } - /* Do phi dependency. */ - phip = phi; - rowoff = 0; - rowlen = nphi*sll; - for (iphi = 0; iphi < nphi; iphi++, rowoff += sll, phip += spt) { - dphi = *phip - eul[2]; + // Do phi dependency. + const double *phip = phi; + int rowoff = 0; + int rowlen = nphi*sll; + for (int iphi = 0; iphi < nphi; iphi++, rowoff += sll, phip += spt) { + double dphi = *phip - eul[2]; - lngp = lng + rowoff; - for (itheta = 0; itheta < mtheta; itheta++) { + double *lngp = lng + rowoff; + for (int itheta = 0; itheta < mtheta; itheta++) { *lngp = dphi; lngp += rowlen; } } - /* Do theta dependency. */ - thetap = theta; - lngp = lng; - latp = lat; - for (itheta = 0; itheta < ntheta; itheta++, thetap += spt) { + // Do theta dependency. + const double *thetap = theta; + double *lngp = lng; + double *latp = lat; + for (int itheta = 0; itheta < ntheta; itheta++, thetap += spt) { + double sinthe, costhe; sincosd(*thetap, &sinthe, &costhe); - costhe3 = costhe*eul[3]; - costhe4 = costhe*eul[4]; - sinthe3 = sinthe*eul[3]; - sinthe4 = sinthe*eul[4]; - for (iphi = 0; iphi < mphi; iphi++, lngp += sll, latp += sll) { - dphi = *lngp; + double costhe3 = costhe*eul[3]; + double costhe4 = costhe*eul[4]; + double sinthe3 = sinthe*eul[3]; + double sinthe4 = sinthe*eul[4]; + + for (int iphi = 0; iphi < mphi; iphi++, lngp += sll, latp += sll) { + double dphi = *lngp; + double sinphi, cosphi; sincosd(dphi, &sinphi, &cosphi); - /* Compute the celestial longitude. */ - x = sinthe4 - costhe3*cosphi; + // Compute the celestial longitude. + double x = sinthe4 - costhe3*cosphi; if (fabs(x) < tol) { - /* Rearrange formula to reduce roundoff errors. */ + // Rearrange formula to reduce roundoff errors. x = -cosd(*thetap + eul[1]) + costhe3*(1.0 - cosphi); } - y = -costhe*sinphi; + double dlng; + double y = -costhe*sinphi; if (x != 0.0 || y != 0.0) { dlng = atan2d(y, x); } else { - /* Change of origin of longitude. */ + // Change of origin of longitude. if (eul[1] < 90.0) { dlng = dphi + 180.0; } else { @@ -185,7 +180,7 @@ int sphx2s( } *lngp = eul[0] + dlng; - /* Normalize the celestial longitude. */ + // Normalize the celestial longitude. if (eul[0] >= 0.0) { if (*lngp < 0.0) *lngp += 360.0; } else { @@ -198,15 +193,15 @@ int sphx2s( *lngp += 360.0; } - /* Compute the celestial latitude. */ + // Compute the celestial latitude. if (fmod(dphi,180.0) == 0.0) { *latp = *thetap + cosphi*eul[1]; if (*latp > 90.0) *latp = 180.0 - *latp; if (*latp < -90.0) *latp = -180.0 - *latp; } else { - z = sinthe3 + costhe4*cosphi; + double z = sinthe3 + costhe4*cosphi; if (fabs(z) > 0.99) { - /* Use an alternative formula for greater accuracy. */ + // Use an alternative formula for greater accuracy. *latp = copysign(acosd(sqrt(x*x+y*y)), z); } else { *latp = asind(z); @@ -218,7 +213,7 @@ int sphx2s( return 0; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- int sphs2x( const double eul[5], @@ -232,13 +227,7 @@ int sphs2x( double theta[]) { - int mlat, mlng, rowlen, rowoff; - double coslat, coslat3, coslat4, coslng, dlng, dphi, sinlat, sinlat3, - sinlat4, sinlng, x, y, z; - register int ilat, ilng; - register const double *latp, *lngp; - register double *phip, *thetap; - + int mlng, mlat; if (nlat > 0) { mlng = nlng; mlat = nlat; @@ -249,21 +238,23 @@ int sphs2x( } - /* Check for a simple change in origin of longitude. */ + // Check for special-case rotations. if (eul[4] == 0.0) { if (eul[1] == 0.0) { - dphi = fmod(eul[2] - 180.0 - eul[0], 360.0); - - lngp = lng; - latp = lat; - phip = phi; - thetap = theta; - for (ilat = 0; ilat < nlat; ilat++) { - for (ilng = 0; ilng < mlng; ilng++) { + // Simple change in origin of longitude. + double dphi = fmod(eul[2] - 180.0 - eul[0], 360.0); + + int jlng = 0; + const double *latp = lat; + double *phip = phi; + double *thetap = theta; + for (int ilat = 0; ilat < nlat; ilat++, latp += sll) { + const double *lngp = lng + (jlng%nlng)*sll; + for (int ilng = 0; ilng < mlng; ilng++, lngp += sll, jlng++) { *phip = fmod(*lngp + dphi, 360.0); *thetap = *latp; - /* Normalize the native longitude. */ + // Normalize the native longitude. if (*phip > 180.0) { *phip -= 360.0; } else if (*phip < -180.0) { @@ -272,24 +263,24 @@ int sphs2x( phip += spt; thetap += spt; - lngp += sll; - latp += sll; } } } else { - dphi = fmod(eul[2] + eul[0], 360.0); - - lngp = lng; - latp = lat; - phip = phi; - thetap = theta; - for (ilat = 0; ilat < nlat; ilat++) { - for (ilng = 0; ilng < mlng; ilng++) { + // Pole-flip with change in origin of longitude. + double dphi = fmod(eul[2] + eul[0], 360.0); + + int jlng = 0; + const double *latp = lat; + double *phip = phi; + double *thetap = theta; + for (int ilat = 0; ilat < nlat; ilat++, latp += sll) { + const double *lngp = lng + (jlng%nlng)*sll; + for (int ilng = 0; ilng < mlng; ilng++, lngp += sll, jlng++) { *phip = fmod(dphi - *lngp, 360.0); *thetap = -(*latp); - /* Normalize the native longitude. */ + // Normalize the native longitude. if (*phip > 180.0) { *phip -= 360.0; } else if (*phip < -180.0) { @@ -298,8 +289,6 @@ int sphs2x( phip += spt; thetap += spt; - lngp += sll; - latp += sll; } } } @@ -308,49 +297,52 @@ int sphs2x( } - /* Do lng dependency. */ - lngp = lng; - rowoff = 0; - rowlen = nlng*spt; - for (ilng = 0; ilng < nlng; ilng++, rowoff += spt, lngp += sll) { - dlng = *lngp - eul[0]; + // Do lng dependency. + const double *lngp = lng; + int rowoff = 0; + int rowlen = nlng*spt; + for (int ilng = 0; ilng < nlng; ilng++, rowoff += spt, lngp += sll) { + double dlng = *lngp - eul[0]; - phip = phi + rowoff; - thetap = theta; - for (ilat = 0; ilat < mlat; ilat++) { + double *phip = phi + rowoff; + for (int ilat = 0; ilat < mlat; ilat++) { *phip = dlng; phip += rowlen; } } - /* Do lat dependency. */ - latp = lat; - phip = phi; - thetap = theta; - for (ilat = 0; ilat < nlat; ilat++, latp += sll) { + // Do lat dependency. + const double *latp = lat; + double *phip = phi; + double *thetap = theta; + for (int ilat = 0; ilat < nlat; ilat++, latp += sll) { + double sinlat, coslat; sincosd(*latp, &sinlat, &coslat); - coslat3 = coslat*eul[3]; - coslat4 = coslat*eul[4]; - sinlat3 = sinlat*eul[3]; - sinlat4 = sinlat*eul[4]; - for (ilng = 0; ilng < mlng; ilng++, phip += spt, thetap += spt) { - dlng = *phip; + double coslat3 = coslat*eul[3]; + double coslat4 = coslat*eul[4]; + double sinlat3 = sinlat*eul[3]; + double sinlat4 = sinlat*eul[4]; + + for (int ilng = 0; ilng < mlng; ilng++, phip += spt, thetap += spt) { + double dlng = *phip; + double sinlng, coslng; sincosd(dlng, &sinlng, &coslng); - /* Compute the native longitude. */ - x = sinlat4 - coslat3*coslng; + // Compute the native longitude. + double x = sinlat4 - coslat3*coslng; if (fabs(x) < tol) { - /* Rearrange formula to reduce roundoff errors. */ + // Rearrange formula to reduce roundoff errors. x = -cosd(*latp+eul[1]) + coslat3*(1.0 - coslng); } - y = -coslat*sinlng; + double dphi; + double y = -coslat*sinlng; if (x != 0.0 || y != 0.0) { dphi = atan2d(y, x); } else { - /* Change of origin of longitude. */ + // Change of origin of longitude. if (eul[1] < 90.0) { dphi = dlng - 180.0; } else { @@ -359,22 +351,22 @@ int sphs2x( } *phip = fmod(eul[2] + dphi, 360.0); - /* Normalize the native longitude. */ + // Normalize the native longitude. if (*phip > 180.0) { *phip -= 360.0; } else if (*phip < -180.0) { *phip += 360.0; } - /* Compute the native latitude. */ + // Compute the native latitude. if (fmod(dlng,180.0) == 0.0) { *thetap = *latp + coslng*eul[1]; if (*thetap > 90.0) *thetap = 180.0 - *thetap; if (*thetap < -90.0) *thetap = -180.0 - *thetap; } else { - z = sinlat3 + coslat4*coslng; + double z = sinlat3 + coslat4*coslng; if (fabs(z) > 0.99) { - /* Use an alternative formula for greater accuracy. */ + // Use an alternative formula for greater accuracy. *thetap = copysign(acosd(sqrt(x*x+y*y)), z); } else { *thetap = asind(z); @@ -386,7 +378,7 @@ int sphs2x( return 0; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- int sphdpa( int nfield, @@ -398,24 +390,22 @@ int sphdpa( double pa[]) { - int i; + // Set the Euler angles for the coordinate transformation. double eul[5]; - - /* Set the Euler angles for the coordinate transformation. */ eul[0] = lng0; eul[1] = 90.0 - lat0; eul[2] = 0.0; eul[3] = cosd(eul[1]); eul[4] = sind(eul[1]); - /* Transform field points to the new system. */ + // Transform field points to the new system. sphs2x(eul, nfield, 0, 1, 1, lng, lat, pa, dist); - for (i = 0; i < nfield; i++) { - /* Angular distance is obtained from latitude in the new frame. */ + for (int i = 0; i < nfield; i++) { + // Angular distance is obtained from latitude in the new frame. dist[i] = 90.0 - dist[i]; - /* Position angle is obtained from longitude in the new frame. */ + // Position angle is obtained from longitude in the new frame. pa[i] = -pa[i]; if (pa[i] < -180.0) pa[i] += 360.0; } @@ -423,7 +413,7 @@ int sphdpa( return 0; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- int sphpad( int nfield, @@ -435,25 +425,23 @@ int sphpad( double lat[]) { - int i; + // Set the Euler angles for the coordinate transformation. double eul[5]; - - /* Set the Euler angles for the coordinate transformation. */ eul[0] = lng0; eul[1] = 90.0 - lat0; eul[2] = 0.0; eul[3] = cosd(eul[1]); eul[4] = sind(eul[1]); - for (i = 0; i < nfield; i++) { - /* Latitude in the new frame is obtained from angular distance. */ + for (int i = 0; i < nfield; i++) { + // Latitude in the new frame is obtained from angular distance. lat[i] = 90.0 - dist[i]; - /* Longitude in the new frame is obtained from position angle. */ + // Longitude in the new frame is obtained from position angle. lng[i] = -pa[i]; } - /* Transform field points to the old system. */ + // Transform field points to the old system. sphx2s(eul, nfield, 0, 1, 1, lng, lat, lng, lat); return 0; diff --git a/cextern/wcslib/C/sph.h b/cextern/wcslib/C/sph.h index 4db6ff439704..9c9b6e64e292 100644 --- a/cextern/wcslib/C/sph.h +++ b/cextern/wcslib/C/sph.h @@ -1,7 +1,6 @@ /*============================================================================ - - WCSLIB 4.14 - an implementation of the FITS WCS standard. - Copyright (C) 1995-2012, Mark Calabretta + WCSLIB 8.6 - an implementation of the FITS WCS standard. + Copyright (C) 1995-2026, Mark Calabretta This file is part of WCSLIB. @@ -18,31 +17,29 @@ You should have received a copy of the GNU Lesser General Public License along with WCSLIB. If not, see http://www.gnu.org/licenses. - Direct correspondence concerning WCSLIB to mark@calabretta.id.au - Author: Mark Calabretta, Australia Telescope National Facility, CSIRO. - http://www.atnf.csiro.au/people/Mark.Calabretta - $Id: sph.h,v 4.14 2012/07/13 10:02:27 cal103 Exp $ + http://www.atnf.csiro.au/computing/software/wcs + $Id: sph.h,v 8.6 2026/03/29 13:53:56 mcalabre Exp $ *============================================================================= * -* WCSLIB 4.14 - C routines that implement the spherical coordinate -* transformations used by the FITS World Coordinate System (WCS) standard. -* Refer to -* -* "Representations of world coordinates in FITS", -* Greisen, E.W., & Calabretta, M.R. 2002, A&A, 395, 1061 (Paper I) -* -* "Representations of celestial coordinates in FITS", -* Calabretta, M.R., & Greisen, E.W. 2002, A&A, 395, 1077 (Paper II) -* -* Refer to the README file provided with WCSLIB for an overview of the -* library. +* WCSLIB 8.6 - C routines that implement the FITS World Coordinate System +* (WCS) standard. Refer to the README file provided with WCSLIB for an +* overview of the library. * * * Summary of the sph routines * --------------------------- -* The WCS spherical coordinate transformations are implemented via separate -* functions, sphx2s() and sphs2x(), for the transformation in each direction. +* Routines in this suite implement the spherical coordinate transformations +* defined by the FITS World Coordinate System (WCS) standard +* += "Representations of world coordinates in FITS", += Greisen, E.W., & Calabretta, M.R. 2002, A&A, 395, 1061 (WCS Paper I) += += "Representations of celestial coordinates in FITS", += Calabretta, M.R., & Greisen, E.W. 2002, A&A, 395, 1077 (WCS Paper II) +* +* The transformations are implemented via separate functions, sphx2s() and +* sphs2x(), for the spherical rotation in each direction. * * A utility function, sphdpa(), computes the angular distances and position * angles from a given point on the sky to a number of other points. sphpad() @@ -144,41 +141,41 @@ * 0: Success. * * Notes: -* sphdpa() uses sphs2x() to rotate coordinates so that the reference point -* is at the north pole of the new system with the north pole of the old -* system at zero longitude in the new. The Euler angles required by -* sphs2x() for this rotation are -* -= eul[0] = lng0; -= eul[1] = 90.0 - lat0; -= eul[2] = 0.0; -* -* The angular distance and generalized position angle are readily obtained -* from the longitude and latitude of the field point in the new system. -* This applies even if the reference point is at one of the poles, in which -* case the "position angle" returned is as would be computed for a reference -* point at (lng0,+90-epsilon) or (lng0,-90+epsilon), in the limit as epsilon -* goes to zero. -* -* It is evident that the coordinate system in which the two points are -* expressed is irrelevant to the determination of the angular separation -* between the points. However, this is not true of the generalized position -* angle. -* -* The generalized position angle is here defined as the angle of -* intersection of the great circle containing the reference and field points -* with that containing the reference point and the pole. It has its normal -* meaning when the the reference and field points are specified in -* equatorial coordinates (right ascension and declination). -* -* Interchanging the reference and field points changes the position angle in -* a non-intuitive way (because the sum of the angles of a spherical triangle -* normally exceeds 180 degrees). -* -* The position angle is undefined if the reference and field points are -* coincident or antipodal. This may be detected by checking for a distance -* of 0 or 180 degrees (within rounding tolerance). sphdpa() will return an -* arbitrary position angle in such circumstances. +* 1. sphdpa() uses sphs2x() to rotate coordinates so that the reference +* point is at the north pole of the new system with the north pole of the +* old system at zero longitude in the new. The Euler angles required by +* sphs2x() for this rotation are +* += eul[0] = lng0; += eul[1] = 90.0 - lat0; += eul[2] = 0.0; +* +* The angular distance and generalized position angle are readily +* obtained from the longitude and latitude of the field point in the new +* system. This applies even if the reference point is at one of the +* poles, in which case the "position angle" returned is as would be +* computed for a reference point at (lng0,+90-epsilon) or +* (lng0,-90+epsilon), in the limit as epsilon goes to zero. +* +* It is evident that the coordinate system in which the two points are +* expressed is irrelevant to the determination of the angular separation +* between the points. However, this is not true of the generalized +* position angle. +* +* The generalized position angle is here defined as the angle of +* intersection of the great circle containing the reference and field +* points with that containing the reference point and the pole. It has +* its normal meaning when the the reference and field points are +* specified in equatorial coordinates (right ascension and declination). +* +* Interchanging the reference and field points changes the position angle +* in a non-intuitive way (because the sum of the angles of a spherical +* triangle normally exceeds 180 degrees). +* +* The position angle is undefined if the reference and field points are +* coincident or antipodal. This may be detected by checking for a +* distance of 0 or 180 degrees (within rounding tolerance). sphdpa() +* will return an arbitrary position angle in such circumstances. * * * sphpad() - Compute field points offset from a given point @@ -208,11 +205,11 @@ * 0: Success. * * Notes: -* sphpad() is implemented analogously to sphdpa() although using sphx2s() -* for the inverse transformation. In particular, when the reference point -* is at one of the poles, "position angle" is interpreted as though the -* reference point was at (lng0,+90-epsilon) or (lng0,-90+epsilon), in the -* limit as epsilon goes to zero. +* 1: sphpad() is implemented analogously to sphdpa() although using sphx2s() +* for the inverse transformation. In particular, when the reference +* point is at one of the poles, "position angle" is interpreted as though +* the reference point was at (lng0,+90-epsilon) or (lng0,-90+epsilon), in +* the limit as epsilon goes to zero. * * Applying sphpad() with the distances and position angles computed by * sphdpa() should return the original field points. @@ -248,4 +245,4 @@ int sphpad(int nfield, double lng0, double lat0, } #endif -#endif /* WCSLIB_SPH */ +#endif // WCSLIB_SPH diff --git a/cextern/wcslib/C/spx.c b/cextern/wcslib/C/spx.c index 631266c08ac8..3cfc204fa608 100644 --- a/cextern/wcslib/C/spx.c +++ b/cextern/wcslib/C/spx.c @@ -1,7 +1,6 @@ /*============================================================================ - - WCSLIB 4.14 - an implementation of the FITS WCS standard. - Copyright (C) 1995-2012, Mark Calabretta + WCSLIB 8.6 - an implementation of the FITS WCS standard. + Copyright (C) 1995-2026, Mark Calabretta This file is part of WCSLIB. @@ -18,11 +17,9 @@ You should have received a copy of the GNU Lesser General Public License along with WCSLIB. If not, see http://www.gnu.org/licenses. - Direct correspondence concerning WCSLIB to mark@calabretta.id.au - Author: Mark Calabretta, Australia Telescope National Facility, CSIRO. - http://www.atnf.csiro.au/people/Mark.Calabretta - $Id: spx.c,v 4.14 2012/07/13 10:02:27 cal103 Exp $ + http://www.atnf.csiro.au/computing/software/wcs + $Id: spx.c,v 8.6 2026/03/29 13:53:56 mcalabre Exp $ *===========================================================================*/ #include @@ -34,7 +31,7 @@ #include "spx.h" -/* Map status return value to message. */ +// Map status return value to message. const char *spx_errmsg[] = { "Success", "Null spxprm pointer passed", @@ -42,7 +39,7 @@ const char *spx_errmsg[] = { "Invalid spectral variable", "One or more of the inspec coordinates were invalid"}; -/* Convenience macro for invoking wcserr_set(). */ +// Convenience macro for invoking wcserr_set(). #define SPX_ERRMSG(status) WCSERR_SET(status), spx_errmsg[status] #define C 2.99792458e8 @@ -54,30 +51,26 @@ const char *spx_errmsg[] = { * others. *===========================================================================*/ -int specx(type, spec, restfrq, restwav, spx) - -const char *type; -double spec, restfrq, restwav; -struct spxprm *spx; +int specx( + const char *type, + double spec, + double restfrq, + double restwav, + struct spxprm *spx) { static const char *function = "specx"; - register int k; - int haverest; - double beta, dwaveawav, gamma, n, s, t, u; - struct wcserr **err; - if (spx == 0x0) return SPXERR_NULL_POINTER; - err = &(spx->err); + struct wcserr **err = &(spx->err); - haverest = 1; + int haverest = 1; if (restfrq == 0.0) { if (restwav == 0.0) { - /* No line rest frequency supplied. */ + // No line rest frequency supplied. haverest = 0; - /* Temporarily set a dummy value for conversions. */ + // Temporarily set a dummy value for conversions. spx->restwav = 1.0; } else { spx->restwav = restwav; @@ -91,7 +84,7 @@ struct spxprm *spx; spx->err = 0x0; - /* Convert to frequency. */ + // Convert to frequency. spx->wavetype = 0; spx->velotype = 0; if (strcmp(type, "FREQ") == 0) { @@ -139,7 +132,7 @@ struct spxprm *spx; spx->wavetype = 1; } else if (strcmp(type, "VOPT") == 0) { - s = 1.0 + spec/C; + double s = 1.0 + spec/C; if (s == 0.0) { return wcserr_set(WCSERR_SET(SPXERR_BAD_SPEC_VAR), "Invalid spectral variable"); @@ -148,7 +141,7 @@ struct spxprm *spx; spx->velotype = 1; } else if (strcmp(type, "ZOPT") == 0) { - s = 1.0 + spec; + double s = 1.0 + spec; if (s == 0.0) { return wcserr_set(WCSERR_SET(SPXERR_BAD_SPEC_VAR), "Invalid spectral variable"); @@ -161,6 +154,8 @@ struct spxprm *spx; return wcserr_set(WCSERR_SET(SPXERR_BAD_SPEC_VAR), "Invalid spectral variable"); } + + double n, s; s = 1.0/spec; s *= s; n = 2.554e8 / (0.41e14 - s); @@ -170,7 +165,7 @@ struct spxprm *spx; spx->wavetype = 1; } else if (strcmp(type, "VELO") == 0) { - beta = spec/C; + double beta = spec/C; if (fabs(beta) == 1.0) { return wcserr_set(WCSERR_SET(SPXERR_BAD_SPEC_VAR), "Invalid spectral variable"); @@ -187,15 +182,16 @@ struct spxprm *spx; spx->velotype = 1; } else { - /* Unrecognized type. */ + // Unrecognized type. return wcserr_set(WCSERR_SET(SPXERR_BAD_SPEC_PARAMS), "Unrecognized spectral type '%s'", type); } - /* Convert frequency to the other spectral types. */ + // Convert frequency to the other spectral types. + double n, s, t, u; n = 1.0; - for (k = 0; k < 4; k++) { + for (int k = 0; k < 4; k++) { s = n*spx->freq/C; s *= s; t = 0.41e14 - s; @@ -203,7 +199,7 @@ struct spxprm *spx; n = 1.000064328 + (2.554e8/t + 294.981e8/u); } - dwaveawav = n - 2.0*s*(2.554e8/(t*t) + 294.981e8/(u*u)); + double dwaveawav = n - 2.0*s*(2.554e8/(t*t) + 294.981e8/(u*u)); s = spx->freq/spx->restfrq; @@ -218,8 +214,8 @@ struct spxprm *spx; spx->velo = C*(1.0 - s*s)/(1.0 + s*s); spx->beta = spx->velo/C; - /* Compute the required derivatives. */ - gamma = 1.0/sqrt(1.0 - spx->beta*spx->beta); + // Compute the required derivatives. + double gamma = 1.0/sqrt(1.0 - spx->beta*spx->beta); spx->dfreqafrq = 1.0/(2.0*PI); spx->dafrqfreq = 1.0/spx->dfreqafrq; @@ -261,7 +257,7 @@ struct spxprm *spx; spx->dbetavelo = 1.0/spx->dvelobeta; - /* Reset values if no line rest frequency was supplied. */ + // Reset values if no line rest frequency was supplied. if (haverest) { spx->wavetype = 1; spx->velotype = 1; @@ -271,7 +267,7 @@ struct spxprm *spx; spx->restwav = 0.0; if (!spx->wavetype) { - /* Don't have wave characteristic types. */ + // Don't have wave characteristic types. spx->freq = 0.0; spx->afrq = 0.0; spx->ener = 0.0; @@ -289,7 +285,7 @@ struct spxprm *spx; spx->dawavwave = 0.0; } else { - /* Don't have velocity types. */ + // Don't have velocity types. spx->vrad = 0.0; spx->vopt = 0.0; spx->zopt = 0.0; @@ -319,29 +315,43 @@ struct spxprm *spx; return 0; } +//---------------------------------------------------------------------------- + +int spxperr(const struct spxprm *spx, const char *prefix) + +{ + if (spx == 0x0) return SPXERR_NULL_POINTER; + + if (spx->err) { + wcserr_prt(spx->err, prefix); + } + + return 0; +} + /*============================================================================ * Conversions between frequency and vacuum wavelength. *===========================================================================*/ -int freqwave(dummy, nfreq, sfreq, swave, freq, wave, stat) - -double dummy; -int nfreq, sfreq, swave; -const double freq[]; -double wave[]; -int stat[]; +int freqwave( + double dummy, + int nfreq, + int sfreq, + int swave, + const double freq[], + double wave[], + int stat[]) { + // Avert nuisance compiler warnings about unused parameters. + (void)dummy; + + const double *freqp = freq; + double *wavep = wave; + int *statp = stat; int status = 0; - register int ifreq, *statp; - register const double *freqp; - register double *wavep; - - freqp = freq; - wavep = wave; - statp = stat; - for (ifreq = 0; ifreq < nfreq; ifreq++) { + for (int ifreq = 0; ifreq < nfreq; ifreq++) { if (*freqp != 0.0) { *wavep = C/(*freqp); *(statp++) = 0; @@ -357,26 +367,26 @@ int stat[]; return status; } -/*--------------------------------------------------------------------------*/ - -int wavefreq(dummy, nwave, swave, sfreq, wave, freq, stat) +//---------------------------------------------------------------------------- -double dummy; -int nwave, swave, sfreq; -const double wave[]; -double freq[]; -int stat[]; +int wavefreq( + double dummy, + int nwave, + int swave, + int sfreq, + const double wave[], + double freq[], + int stat[]) { + // Avert nuisance compiler warnings about unused parameters. + (void)dummy; + + const double *wavep = wave; + double *freqp = freq; + int *statp = stat; int status = 0; - register int iwave, *statp; - register const double *wavep; - register double *freqp; - - wavep = wave; - freqp = freq; - statp = stat; - for (iwave = 0; iwave < nwave; iwave++) { + for (int iwave = 0; iwave < nwave; iwave++) { if (*wavep != 0.0) { *freqp = C/(*wavep); *(statp++) = 0; @@ -396,17 +406,17 @@ int stat[]; * Conversions between frequency and air wavelength. *===========================================================================*/ -int freqawav(dummy, nfreq, sfreq, sawav, freq, awav, stat) - -double dummy; -int nfreq, sfreq, sawav; -const double freq[]; -double awav[]; -int stat[]; +int freqawav( + double dummy, + int nfreq, + int sfreq, + int sawav, + const double freq[], + double awav[], + int stat[]) { int status; - if ((status = freqwave(dummy, nfreq, sfreq, sawav, freq, awav, stat))) { return status; } @@ -414,19 +424,19 @@ int stat[]; return waveawav(dummy, nfreq, sawav, sawav, awav, awav, stat); } -/*--------------------------------------------------------------------------*/ - -int awavfreq(dummy, nawav, sawav, sfreq, awav, freq, stat) +//---------------------------------------------------------------------------- -double dummy; -int nawav, sawav, sfreq; -const double awav[]; -double freq[]; -int stat[]; +int awavfreq( + double dummy, + int nawav, + int sawav, + int sfreq, + const double awav[], + double freq[], + int stat[]) { int status; - if ((status = awavwave(dummy, nawav, sawav, sfreq, awav, freq, stat))) { return status; } @@ -438,27 +448,23 @@ int stat[]; * Conversions between frequency and relativistic velocity. *===========================================================================*/ -int freqvelo(restfrq, nfreq, sfreq, svelo, freq, velo, stat) - -double restfrq; -int nfreq, sfreq, svelo; -const double freq[]; -double velo[]; -int stat[]; +int freqvelo( + double restfrq, + int nfreq, + int sfreq, + int svelo, + const double freq[], + double velo[], + int stat[]) { - double r, s; - register int ifreq, *statp; - register const double *freqp; - register double *velop; - - r = restfrq*restfrq; - - freqp = freq; - velop = velo; - statp = stat; - for (ifreq = 0; ifreq < nfreq; ifreq++) { - s = *freqp * *freqp; + double r = restfrq*restfrq; + + const double *freqp = freq; + double *velop = velo; + int *statp = stat; + for (int ifreq = 0; ifreq < nfreq; ifreq++) { + double s = *freqp * *freqp; *velop = C*(r - s)/(r + s); *(statp++) = 0; @@ -469,28 +475,24 @@ int stat[]; return 0; } -/*--------------------------------------------------------------------------*/ - -int velofreq(restfrq, nvelo, svelo, sfreq, velo, freq, stat) +//---------------------------------------------------------------------------- -double restfrq; -int nvelo, svelo, sfreq; -const double velo[]; -double freq[]; -int stat[]; +int velofreq( + double restfrq, + int nvelo, + int svelo, + int sfreq, + const double velo[], + double freq[], + int stat[]) { + const double *velop = velo; + double *freqp = freq; + int *statp = stat; int status = 0; - double s; - register int ivelo, *statp; - register const double *velop; - register double *freqp; - - velop = velo; - freqp = freq; - statp = stat; - for (ivelo = 0; ivelo < nvelo; ivelo++) { - s = C + *velop; + for (int ivelo = 0; ivelo < nvelo; ivelo++) { + double s = C + *velop; if (s != 0.0) { *freqp = restfrq*sqrt((C - *velop)/s); *(statp++) = 0; @@ -510,29 +512,28 @@ int stat[]; * Conversions between vacuum wavelength and air wavelength. *===========================================================================*/ -int waveawav(dummy, nwave, swave, sawav, wave, awav, stat) - -double dummy; -int nwave, swave, sawav; -const double wave[]; -double awav[]; -int stat[]; +int waveawav( + double dummy, + int nwave, + int swave, + int sawav, + const double wave[], + double awav[], + int stat[]) { + // Avert nuisance compiler warnings about unused parameters. + (void)dummy; + + const double *wavep = wave; + double *awavp = awav; + int *statp = stat; int status = 0; - double n, s; - register int iwave, k, *statp; - register const double *wavep; - register double *awavp; - - wavep = wave; - awavp = awav; - statp = stat; - for (iwave = 0; iwave < nwave; iwave++) { + for (int iwave = 0; iwave < nwave; iwave++) { if (*wavep != 0.0) { - n = 1.0; - for (k = 0; k < 4; k++) { - s = n/(*wavep); + double n = 1.0; + for (int k = 0; k < 4; k++) { + double s = n/(*wavep); s *= s; n = 2.554e8 / (0.41e14 - s); n += 294.981e8 / (1.46e14 - s); @@ -553,28 +554,28 @@ int stat[]; return status; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- -int awavwave(dummy, nawav, sawav, swave, awav, wave, stat) - -double dummy; -int nawav, sawav, swave; -const double awav[]; -double wave[]; -int stat[]; +int awavwave( + double dummy, + int nawav, + int sawav, + int swave, + const double awav[], + double wave[], + int stat[]) { + // Avert nuisance compiler warnings about unused parameters. + (void)dummy; + + const double *awavp = awav; + double *wavep = wave; + int *statp = stat; int status = 0; - double n, s; - register int iawav, *statp; - register const double *awavp; - register double *wavep; - - awavp = awav; - wavep = wave; - statp = stat; - for (iawav = 0; iawav < nawav; iawav++) { + for (int iawav = 0; iawav < nawav; iawav++) { if (*awavp != 0.0) { + double n, s; s = 1.0/(*awavp); s *= s; n = 2.554e8 / (0.41e14 - s); @@ -598,27 +599,23 @@ int stat[]; * Conversions between vacuum wavelength and relativistic velocity. *===========================================================================*/ -int wavevelo(restwav, nwave, swave, svelo, wave, velo, stat) - -double restwav; -int nwave, swave, svelo; -const double wave[]; -double velo[]; -int stat[]; +int wavevelo( + double restwav, + int nwave, + int swave, + int svelo, + const double wave[], + double velo[], + int stat[]) { - double r, s; - register int iwave, *statp; - register const double *wavep; - register double *velop; - - r = restwav*restwav; - - wavep = wave; - velop = velo; - statp = stat; - for (iwave = 0; iwave < nwave; iwave++) { - s = *wavep * *wavep; + double r = restwav*restwav; + + const double *wavep = wave; + double *velop = velo; + int *statp = stat; + for (int iwave = 0; iwave < nwave; iwave++) { + double s = *wavep * *wavep; *velop = C*(s - r)/(s + r); *(statp++) = 0; @@ -629,28 +626,24 @@ int stat[]; return 0; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- -int velowave(restwav, nvelo, svelo, swave, velo, wave, stat) - -double restwav; -int nvelo, svelo, swave; -const double velo[]; -double wave[]; -int stat[]; +int velowave( + double restwav, + int nvelo, + int svelo, + int swave, + const double velo[], + double wave[], + int stat[]) { + const double *velop = velo; + double *wavep = wave; + int *statp = stat; int status = 0; - double s; - register int ivelo, *statp; - register const double *velop; - register double *wavep; - - velop = velo; - wavep = wave; - statp = stat; - for (ivelo = 0; ivelo < nvelo; ivelo++) { - s = C - *velop; + for (int ivelo = 0; ivelo < nvelo; ivelo++) { + double s = C - *velop; if (s != 0.0) { *wavep = restwav*sqrt((C + *velop)/s); *(statp++) = 0; @@ -670,17 +663,17 @@ int stat[]; * Conversions between air wavelength and relativistic velocity. *===========================================================================*/ -int awavvelo(dummy, nawav, sawav, svelo, awav, velo, stat) - -double dummy; -int nawav, sawav, svelo; -const double awav[]; -double velo[]; -int stat[]; +int awavvelo( + double dummy, + int nawav, + int sawav, + int svelo, + const double awav[], + double velo[], + int stat[]) { int status; - if ((status = awavwave(dummy, nawav, sawav, svelo, awav, velo, stat))) { return status; } @@ -688,19 +681,19 @@ int stat[]; return wavevelo(dummy, nawav, svelo, svelo, velo, velo, stat); } -/*--------------------------------------------------------------------------*/ - -int veloawav(dummy, nvelo, svelo, sawav, velo, awav, stat) +//---------------------------------------------------------------------------- -double dummy; -int nvelo, svelo, sawav; -const double velo[]; -double awav[]; -int stat[]; +int veloawav( + double dummy, + int nvelo, + int svelo, + int sawav, + const double velo[], + double awav[], + int stat[]) { int status; - if ((status = velowave(dummy, nvelo, svelo, sawav, velo, awav, stat))) { return status; } @@ -712,23 +705,23 @@ int stat[]; * Conversions between frequency and angular frequency. *===========================================================================*/ -int freqafrq(dummy, nfreq, sfreq, safrq, freq, afrq, stat) - -double dummy; -int nfreq, sfreq, safrq; -const double freq[]; -double afrq[]; -int stat[]; +int freqafrq( + double dummy, + int nfreq, + int sfreq, + int safrq, + const double freq[], + double afrq[], + int stat[]) { - register int ifreq, *statp; - register const double *freqp; - register double *afrqp; - - freqp = freq; - afrqp = afrq; - statp = stat; - for (ifreq = 0; ifreq < nfreq; ifreq++) { + // Avert nuisance compiler warnings about unused parameters. + (void)dummy; + + const double *freqp = freq; + double *afrqp = afrq; + int *statp = stat; + for (int ifreq = 0; ifreq < nfreq; ifreq++) { *afrqp = (*freqp)*(2.0*PI); *(statp++) = 0; @@ -739,25 +732,25 @@ int stat[]; return 0; } -/*--------------------------------------------------------------------------*/ - -int afrqfreq(dummy, nafrq, safrq, sfreq, afrq, freq, stat) +//---------------------------------------------------------------------------- -double dummy; -int nafrq, safrq, sfreq; -const double afrq[]; -double freq[]; -int stat[]; +int afrqfreq( + double dummy, + int nafrq, + int safrq, + int sfreq, + const double afrq[], + double freq[], + int stat[]) { - register int iafrq, *statp; - register const double *afrqp; - register double *freqp; - - afrqp = afrq; - freqp = freq; - statp = stat; - for (iafrq = 0; iafrq < nafrq; iafrq++) { + // Avert nuisance compiler warnings about unused parameters. + (void)dummy; + + const double *afrqp = afrq; + double *freqp = freq; + int *statp = stat; + for (int iafrq = 0; iafrq < nafrq; iafrq++) { *freqp = (*afrqp)/(2.0*PI); *(statp++) = 0; @@ -772,23 +765,23 @@ int stat[]; * Conversions between frequency and energy. *===========================================================================*/ -int freqener(dummy, nfreq, sfreq, sener, freq, ener, stat) - -double dummy; -int nfreq, sfreq, sener; -const double freq[]; -double ener[]; -int stat[]; +int freqener( + double dummy, + int nfreq, + int sfreq, + int sener, + const double freq[], + double ener[], + int stat[]) { - register int ifreq, *statp; - register const double *freqp; - register double *enerp; - - freqp = freq; - enerp = ener; - statp = stat; - for (ifreq = 0; ifreq < nfreq; ifreq++) { + // Avert nuisance compiler warnings about unused parameters. + (void)dummy; + + const double *freqp = freq; + double *enerp = ener; + int *statp = stat; + for (int ifreq = 0; ifreq < nfreq; ifreq++) { *enerp = (*freqp)*h; *(statp++) = 0; @@ -799,25 +792,25 @@ int stat[]; return 0; } -/*--------------------------------------------------------------------------*/ - -int enerfreq(dummy, nener, sener, sfreq, ener, freq, stat) +//---------------------------------------------------------------------------- -double dummy; -int nener, sener, sfreq; -const double ener[]; -double freq[]; -int stat[]; +int enerfreq( + double dummy, + int nener, + int sener, + int sfreq, + const double ener[], + double freq[], + int stat[]) { - register int iener, *statp; - register const double *enerp; - register double *freqp; - - enerp = ener; - freqp = freq; - statp = stat; - for (iener = 0; iener < nener; iener++) { + // Avert nuisance compiler warnings about unused parameters. + (void)dummy; + + const double *enerp = ener; + double *freqp = freq; + int *statp = stat; + for (int iener = 0; iener < nener; iener++) { *freqp = (*enerp)/h; *(statp++) = 0; @@ -832,23 +825,23 @@ int stat[]; * Conversions between frequency and wave number. *===========================================================================*/ -int freqwavn(dummy, nfreq, sfreq, swavn, freq, wavn, stat) - -double dummy; -int nfreq, sfreq, swavn; -const double freq[]; -double wavn[]; -int stat[]; +int freqwavn( + double dummy, + int nfreq, + int sfreq, + int swavn, + const double freq[], + double wavn[], + int stat[]) { - register int ifreq, *statp; - register const double *freqp; - register double *wavnp; - - freqp = freq; - wavnp = wavn; - statp = stat; - for (ifreq = 0; ifreq < nfreq; ifreq++) { + // Avert nuisance compiler warnings about unused parameters. + (void)dummy; + + const double *freqp = freq; + double *wavnp = wavn; + int *statp = stat; + for (int ifreq = 0; ifreq < nfreq; ifreq++) { *wavnp = (*freqp)/C; *(statp++) = 0; @@ -859,25 +852,25 @@ int stat[]; return 0; } -/*--------------------------------------------------------------------------*/ - -int wavnfreq(dummy, nwavn, swavn, sfreq, wavn, freq, stat) +//---------------------------------------------------------------------------- -double dummy; -int nwavn, swavn, sfreq; -const double wavn[]; -double freq[]; -int stat[]; +int wavnfreq( + double dummy, + int nwavn, + int swavn, + int sfreq, + const double wavn[], + double freq[], + int stat[]) { - register int iwavn, *statp; - register const double *wavnp; - register double *freqp; - - wavnp = wavn; - freqp = freq; - statp = stat; - for (iwavn = 0; iwavn < nwavn; iwavn++) { + // Avert nuisance compiler warnings about unused parameters. + (void)dummy; + + const double *wavnp = wavn; + double *freqp = freq; + int *statp = stat; + for (int iwavn = 0; iwavn < nwavn; iwavn++) { *freqp = (*wavnp)*C; *(statp++) = 0; @@ -892,29 +885,25 @@ int stat[]; * Conversions between frequency and radio velocity. *===========================================================================*/ -int freqvrad(restfrq, nfreq, sfreq, svrad, freq, vrad, stat) - -double restfrq; -int nfreq, sfreq, svrad; -const double freq[]; -double vrad[]; -int stat[]; +int freqvrad( + double restfrq, + int nfreq, + int sfreq, + int svrad, + const double freq[], + double vrad[], + int stat[]) { - double r; - register int ifreq, *statp; - register const double *freqp; - register double *vradp; - if (restfrq == 0.0) { return SPXERR_BAD_SPEC_PARAMS; } - r = C/restfrq; + double r = C/restfrq; - freqp = freq; - vradp = vrad; - statp = stat; - for (ifreq = 0; ifreq < nfreq; ifreq++) { + const double *freqp = freq; + double *vradp = vrad; + int *statp = stat; + for (int ifreq = 0; ifreq < nfreq; ifreq++) { *vradp = r*(restfrq - *freqp); *(statp++) = 0; @@ -925,28 +914,24 @@ int stat[]; return 0; } -/*--------------------------------------------------------------------------*/ - -int vradfreq(restfrq, nvrad, svrad, sfreq, vrad, freq, stat) +//---------------------------------------------------------------------------- -double restfrq; -int nvrad, svrad, sfreq; -const double vrad[]; -double freq[]; -int stat[]; +int vradfreq( + double restfrq, + int nvrad, + int svrad, + int sfreq, + const double vrad[], + double freq[], + int stat[]) { - double r; - register int ivrad, *statp; - register const double *vradp; - register double *freqp; + double r = restfrq/C; - r = restfrq/C; - - vradp = vrad; - freqp = freq; - statp = stat; - for (ivrad = 0; ivrad < nvrad; ivrad++) { + const double *vradp = vrad; + double *freqp = freq; + int *statp = stat; + for (int ivrad = 0; ivrad < nvrad; ivrad++) { *freqp = r*(C - *vradp); *(statp++) = 0; vradp += svrad; @@ -960,29 +945,25 @@ int stat[]; * Conversions between vacuum wavelength and optical velocity. *===========================================================================*/ -int wavevopt(restwav, nwave, swave, svopt, wave, vopt, stat) - -double restwav; -int nwave, swave, svopt; -const double wave[]; -double vopt[]; -int stat[]; +int wavevopt( + double restwav, + int nwave, + int swave, + int svopt, + const double wave[], + double vopt[], + int stat[]) { - double r; - register int iwave, *statp; - register const double *wavep; - register double *voptp; - if (restwav == 0.0) { return SPXERR_BAD_SPEC_PARAMS; } - r = C/restwav; + double r = C/restwav; - wavep = wave; - voptp = vopt; - statp = stat; - for (iwave = 0; iwave < nwave; iwave++) { + const double *wavep = wave; + double *voptp = vopt; + int *statp = stat; + for (int iwave = 0; iwave < nwave; iwave++) { *voptp = r*(*wavep) - C; *(statp++) = 0; wavep += swave; @@ -992,28 +973,24 @@ int stat[]; return 0; } -/*--------------------------------------------------------------------------*/ - -int voptwave(restwav, nvopt, svopt, swave, vopt, wave, stat) +//---------------------------------------------------------------------------- -double restwav; -int nvopt, svopt, swave; -const double vopt[]; -double wave[]; -int stat[]; +int voptwave( + double restwav, + int nvopt, + int svopt, + int swave, + const double vopt[], + double wave[], + int stat[]) { - double r; - register int ivopt, *statp; - register const double *voptp; - register double *wavep; + double r = restwav/C; - r = restwav/C; - - voptp = vopt; - wavep = wave; - statp = stat; - for (ivopt = 0; ivopt < nvopt; ivopt++) { + const double *voptp = vopt; + double *wavep = wave; + int *statp = stat; + for (int ivopt = 0; ivopt < nvopt; ivopt++) { *wavep = r*(C + *voptp); *(statp++) = 0; voptp += svopt; @@ -1027,29 +1004,25 @@ int stat[]; * Conversions between vacuum wavelength and redshift. *===========================================================================*/ -int wavezopt(restwav, nwave, swave, szopt, wave, zopt, stat) - -double restwav; -int nwave, swave, szopt; -const double wave[]; -double zopt[]; -int stat[]; +int wavezopt( + double restwav, + int nwave, + int swave, + int szopt, + const double wave[], + double zopt[], + int stat[]) { - double r; - register int iwave, *statp; - register const double *wavep; - register double *zoptp; - if (restwav == 0.0) { return SPXERR_BAD_SPEC_PARAMS; } - r = 1.0/restwav; + double r = 1.0/restwav; - wavep = wave; - zoptp = zopt; - statp = stat; - for (iwave = 0; iwave < nwave; iwave++) { + const double *wavep = wave; + double *zoptp = zopt; + int *statp = stat; + for (int iwave = 0; iwave < nwave; iwave++) { *zoptp = r*(*wavep) - 1.0; *(statp++) = 0; wavep += swave; @@ -1059,25 +1032,22 @@ int stat[]; return 0; } -/*--------------------------------------------------------------------------*/ - -int zoptwave(restwav, nzopt, szopt, swave, zopt, wave, stat) +//---------------------------------------------------------------------------- -double restwav; -int nzopt, szopt, swave; -const double zopt[]; -double wave[]; -int stat[]; +int zoptwave( + double restwav, + int nzopt, + int szopt, + int swave, + const double zopt[], + double wave[], + int stat[]) { - register int izopt, *statp; - register const double *zoptp; - register double *wavep; - - zoptp = zopt; - wavep = wave; - statp = stat; - for (izopt = 0; izopt < nzopt; izopt++) { + const double *zoptp = zopt; + double *wavep = wave; + int *statp = stat; + for (int izopt = 0; izopt < nzopt; izopt++) { *wavep = restwav*(1.0 + *zoptp); *(statp++) = 0; zoptp += szopt; @@ -1091,23 +1061,23 @@ int stat[]; * Conversions between relativistic velocity and beta (= v/c). *===========================================================================*/ -int velobeta(dummy, nvelo, svelo, sbeta, velo, beta, stat) - -double dummy; -int nvelo, svelo, sbeta; -const double velo[]; -double beta[]; -int stat[]; +int velobeta( + double dummy, + int nvelo, + int svelo, + int sbeta, + const double velo[], + double beta[], + int stat[]) { - register int ivelo, *statp; - register const double *velop; - register double *betap; - - velop = velo; - betap = beta; - statp = stat; - for (ivelo = 0; ivelo < nvelo; ivelo++) { + // Avert nuisance compiler warnings about unused parameters. + (void)dummy; + + const double *velop = velo; + double *betap = beta; + int *statp = stat; + for (int ivelo = 0; ivelo < nvelo; ivelo++) { *betap = (*velop)/C; *(statp++) = 0; @@ -1118,25 +1088,25 @@ int stat[]; return 0; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- -int betavelo(dummy, nbeta, sbeta, svelo, beta, velo, stat) - -double dummy; -int nbeta, sbeta, svelo; -const double beta[]; -double velo[]; -int stat[]; +int betavelo( + double dummy, + int nbeta, + int sbeta, + int svelo, + const double beta[], + double velo[], + int stat[]) { - register int ibeta, *statp; - register const double *betap; - register double *velop; - - betap = beta; - velop = velo; - statp = stat; - for (ibeta = 0; ibeta < nbeta; ibeta++) { + // Avert nuisance compiler warnings about unused parameters. + (void)dummy; + + const double *betap = beta; + double *velop = velo; + int *statp = stat; + for (int ibeta = 0; ibeta < nbeta; ibeta++) { *velop = (*betap)*C; *(statp++) = 0; diff --git a/cextern/wcslib/C/spx.h b/cextern/wcslib/C/spx.h index 103cb790f76b..71977f63af46 100644 --- a/cextern/wcslib/C/spx.h +++ b/cextern/wcslib/C/spx.h @@ -1,7 +1,6 @@ /*============================================================================ - - WCSLIB 4.14 - an implementation of the FITS WCS standard. - Copyright (C) 1995-2012, Mark Calabretta + WCSLIB 8.6 - an implementation of the FITS WCS standard. + Copyright (C) 1995-2026, Mark Calabretta This file is part of WCSLIB. @@ -18,34 +17,35 @@ You should have received a copy of the GNU Lesser General Public License along with WCSLIB. If not, see http://www.gnu.org/licenses. - Direct correspondence concerning WCSLIB to mark@calabretta.id.au - Author: Mark Calabretta, Australia Telescope National Facility, CSIRO. - http://www.atnf.csiro.au/people/Mark.Calabretta - $Id: spx.h,v 4.14 2012/07/13 10:02:27 cal103 Exp $ + http://www.atnf.csiro.au/computing/software/wcs + $Id: spx.h,v 8.6 2026/03/29 13:53:56 mcalabre Exp $ *============================================================================= * -* WCSLIB 4.14 - C routines that implement the spectral coordinate systems -* recognized by the FITS World Coordinate System (WCS) standard. Refer to -* -* "Representations of world coordinates in FITS", -* Greisen, E.W., & Calabretta, M.R. 2002, A&A, 395, 1061 (Paper I) -* -* "Representations of spectral coordinates in FITS", -* Greisen, E.W., Calabretta, M.R., Valdes, F.G., & Allen, S.L. -* 2006, A&A, 446, 747 (Paper III) -* -* Refer to the README file provided with WCSLIB for an overview of the -* library. +* WCSLIB 8.6 - C routines that implement the FITS World Coordinate System +* (WCS) standard. Refer to the README file provided with WCSLIB for an +* overview of the library. * * * Summary of the spx routines * --------------------------- +* Routines in this suite implement the spectral coordinate systems recognized +* by the FITS World Coordinate System (WCS) standard, as described in +* += "Representations of world coordinates in FITS", += Greisen, E.W., & Calabretta, M.R. 2002, A&A, 395, 1061 (WCS Paper I) += += "Representations of spectral coordinates in FITS", += Greisen, E.W., Calabretta, M.R., Valdes, F.G., & Allen, S.L. += 2006, A&A, 446, 747 (WCS Paper III) +* * specx() is a scalar routine that, given one spectral variable (e.g. * frequency), computes all the others (e.g. wavelength, velocity, etc.) plus * the required derivatives of each with respect to the others. The results * are returned in the spxprm struct. * +* spxperr() prints the error message(s) (if any) stored in a spxprm struct. +* * The remaining routines are all vector conversions from one spectral * variable to another. The API of these functions only differ in whether the * rest frequency or wavelength need be supplied. @@ -95,6 +95,39 @@ * Conversions may be done "in place" by calling the routine with the output * vector set to the input. * +* Air-to-vacuum wavelength conversion: +* ------------------------------------ +* The air-to-vacuum wavelength conversion in early drafts of WCS Paper III +* cites Cox (ed., 2000, Allen’s Astrophysical Quantities, AIP Press, +* Springer-Verlag, New York), which itself derives from EdlÊn (1953, Journal +* of the Optical Society of America, 43, 339). This is the IAU standard, +* adopted in 1957 and again in 1991. No more recent IAU resolution replaces +* this relation, and it is the one used by WCSLIB. +* +* However, the Cox relation was replaced in later drafts of Paper III, and as +* eventually published, by the IUGG relation (1999, International Union of +* Geodesy and Geophysics, comptes rendus of the 22nd General Assembly, +* Birmingham UK, p111). There is a nearly constant ratio between the two, +* with IUGG/Cox = 1.000015 over most of the range between 200nm and 10,000nm. +* +* The IUGG relation itself is derived from the work of Ciddor (1996, Applied +* Optics, 35, 1566), which is used directly by the Sloan Digital Sky Survey. +* It agrees closely with Cox; longwards of 2500nm, the ratio Ciddor/Cox is +* fixed at 1.000000021, decreasing only slightly, to 1.000000018, at 1000nm. +* +* The Cox, IUGG, and Ciddor relations all accurately provide the wavelength +* dependence of the air-to-vacuum wavelength conversion. However, for full +* accuracy, the atmospheric temperature, pressure, and partial pressure of +* water vapour must be taken into account. These will determine a small, +* wavelength-independent scale factor and offset, which is not considered by +* WCS Paper III. +* +* WCS Paper III is also silent on the question of the range of validity of the +* air-to-vacuum wavelength conversion. Cox's relation would appear to be +* valid in the range 200nm to 10,000nm. Both the Cox and the Ciddor relations +* have singularities below 200nm, with Cox's at 156nm and 83nm. WCSLIB checks +* neither the range of validity, nor for these singularities. +* * Argument checking: * ------------------ * The input spectral values are only checked for values that would result @@ -157,6 +190,25 @@ * frequency or wavelength). They all have the same API. * * +* spxperr() - Print error messages from a spxprm struct +* ----------------------------------------------------- +* spxperr() prints the error message(s) (if any) stored in a spxprm struct. +* If there are no errors then nothing is printed. It uses wcserr_prt(), q.v. +* +* Given: +* spx const struct spxprm* +* Spectral variables and their derivatives. +* +* prefix const char * +* If non-NULL, each output line will be prefixed with +* this string. +* +* Function return value: +* int Status return value: +* 0: Success. +* 1: Null spxprm pointer passed. +* +* * freqafrq() - Convert frequency to angular frequency (vector) * ------------------------------------------------------------ * freqafrq() converts frequency to angular frequency. @@ -334,7 +386,7 @@ * * double dfreqwavn * (Returned) Derivative of frequency with respect to wave number [m/s] -* (constant, = c, the speed of light in vacuuo), and ... +* (constant, = c, the speed of light in vacuo), and ... * double dwavnfreq * (Returned) ... vice versa [s/m] (constant, = 1/c, always available). * @@ -394,18 +446,19 @@ * * double dvelobeta * (Returned) Derivative of relativistic velocity with respect to -* relativistic beta [m/s] (constant, = c, the speed of light in vacuu0), +* relativistic beta [m/s] (constant, = c, the speed of light in vacuo), * and ... * double dbetavelo * (Returned) ... vice versa [s/m] (constant, = 1/c, always available). * * struct wcserr *err -* (Returned) If enabled, when an error status is returned this struct +* (Returned) If enabled, when an error status is returned, this struct * contains detailed information about the error, see wcserr_enable(). * * void *padding * (An unused variable inserted for alignment purposes only.) * +* * Global variable: const char *spx_errmsg[] - Status return messages * ------------------------------------------------------------------ * Error messages to match the status value returned from each function. @@ -419,73 +472,72 @@ extern "C" { #endif -#include "wcserr.h" - extern const char *spx_errmsg[]; enum spx_errmsg { - SPXERR_SUCCESS = 0, /* Success. */ - SPXERR_NULL_POINTER = 1, /* Null spxprm pointer passed. */ - SPXERR_BAD_SPEC_PARAMS = 2, /* Invalid spectral parameters. */ - SPXERR_BAD_SPEC_VAR = 3, /* Invalid spectral variable. */ - SPXERR_BAD_INSPEC_COORD = 4 /* One or more of the inspec coordinates were - invalid. */ + SPXERR_SUCCESS = 0, // Success. + SPXERR_NULL_POINTER = 1, // Null spxprm pointer passed. + SPXERR_BAD_SPEC_PARAMS = 2, // Invalid spectral parameters. + SPXERR_BAD_SPEC_VAR = 3, // Invalid spectral variable. + SPXERR_BAD_INSPEC_COORD = 4 // One or more of the inspec coordinates were + // invalid. }; struct spxprm { - double restfrq, restwav; /* Rest frequency [Hz] and wavelength [m]. */ - - int wavetype, velotype; /* True if wave/velocity types have been */ - /* computed; types are defined below. */ - - /* Spectral variables computed by specx(). */ - /*------------------------------------------------------------------------*/ - double freq, /* wavetype: Frequency [Hz]. */ - afrq, /* wavetype: Angular frequency [rad/s]. */ - ener, /* wavetype: Photon energy [J]. */ - wavn, /* wavetype: Wave number [/m]. */ - vrad, /* velotype: Radio velocity [m/s]. */ - wave, /* wavetype: Vacuum wavelength [m]. */ - vopt, /* velotype: Optical velocity [m/s]. */ - zopt, /* velotype: Redshift. */ - awav, /* wavetype: Air wavelength [m]. */ - velo, /* velotype: Relativistic velocity [m/s]. */ - beta; /* velotype: Relativistic beta. */ - - /* Derivatives of spectral variables computed by specx(). */ - /*------------------------------------------------------------------------*/ - double dfreqafrq, dafrqfreq, /* Constant, always available. */ - dfreqener, denerfreq, /* Constant, always available. */ - dfreqwavn, dwavnfreq, /* Constant, always available. */ - dfreqvrad, dvradfreq, /* wavetype && velotype. */ - dfreqwave, dwavefreq, /* wavetype. */ - dfreqawav, dawavfreq, /* wavetype. */ - dfreqvelo, dvelofreq, /* wavetype && velotype. */ - dwavevopt, dvoptwave, /* wavetype && velotype. */ - dwavezopt, dzoptwave, /* wavetype && velotype. */ - dwaveawav, dawavwave, /* wavetype. */ - dwavevelo, dvelowave, /* wavetype && velotype. */ - dawavvelo, dveloawav, /* wavetype && velotype. */ - dvelobeta, dbetavelo; /* Constant, always available. */ - - /* Error handling */ - /*------------------------------------------------------------------------*/ + double restfrq, restwav; // Rest frequency [Hz] and wavelength [m]. + + int wavetype, velotype; // True if wave/velocity types have been + // computed; types are defined below. + + // Spectral variables computed by specx(). + //-------------------------------------------------------------------------- + double freq, // wavetype: Frequency [Hz]. + afrq, // wavetype: Angular frequency [rad/s]. + ener, // wavetype: Photon energy [J]. + wavn, // wavetype: Wave number [/m]. + vrad, // velotype: Radio velocity [m/s]. + wave, // wavetype: Vacuum wavelength [m]. + vopt, // velotype: Optical velocity [m/s]. + zopt, // velotype: Redshift. + awav, // wavetype: Air wavelength [m]. + velo, // velotype: Relativistic velocity [m/s]. + beta; // velotype: Relativistic beta. + + // Derivatives of spectral variables computed by specx(). + //-------------------------------------------------------------------------- + double dfreqafrq, dafrqfreq, // Constant, always available. + dfreqener, denerfreq, // Constant, always available. + dfreqwavn, dwavnfreq, // Constant, always available. + dfreqvrad, dvradfreq, // wavetype && velotype. + dfreqwave, dwavefreq, // wavetype. + dfreqawav, dawavfreq, // wavetype. + dfreqvelo, dvelofreq, // wavetype && velotype. + dwavevopt, dvoptwave, // wavetype && velotype. + dwavezopt, dzoptwave, // wavetype && velotype. + dwaveawav, dawavwave, // wavetype. + dwavevelo, dvelowave, // wavetype && velotype. + dawavvelo, dveloawav, // wavetype && velotype. + dvelobeta, dbetavelo; // Constant, always available. + + // Error handling + //-------------------------------------------------------------------------- struct wcserr *err; - /* Private */ - /*------------------------------------------------------------------------*/ - void *padding; /* (Dummy inserted for alignment purposes.) */ + // Private + //-------------------------------------------------------------------------- + void *padding; // (Dummy inserted for alignment purposes.) }; -/* Size of the spxprm struct in int units, used by the Fortran wrappers. */ +// Size of the spxprm struct in int units, used by the Fortran wrappers. #define SPXLEN (sizeof(struct spxprm)/sizeof(int)) int specx(const char *type, double spec, double restfrq, double restwav, struct spxprm *specs); +int spxperr(const struct spxprm *spx, const char *prefix); -/* For use in declaring function prototypes, e.g. in spcprm. */ +// For use in declaring function prototypes, e.g. in spcprm. #define SPX_ARGS double param, int nspec, int instep, int outstep, \ const double inspec[], double outspec[], int stat[] @@ -535,4 +587,4 @@ int zoptwave(SPX_ARGS); } #endif -#endif /* WCSLIB_SPEC */ +#endif // WCSLIB_SPEC diff --git a/cextern/wcslib/C/tab.c b/cextern/wcslib/C/tab.c index 560d2573797e..c488580c399c 100644 --- a/cextern/wcslib/C/tab.c +++ b/cextern/wcslib/C/tab.c @@ -1,7 +1,6 @@ /*============================================================================ - - WCSLIB 4.14 - an implementation of the FITS WCS standard. - Copyright (C) 1995-2012, Mark Calabretta + WCSLIB 8.6 - an implementation of the FITS WCS standard. + Copyright (C) 1995-2026, Mark Calabretta This file is part of WCSLIB. @@ -18,11 +17,9 @@ You should have received a copy of the GNU Lesser General Public License along with WCSLIB. If not, see http://www.gnu.org/licenses. - Direct correspondence concerning WCSLIB to mark@calabretta.id.au - Author: Mark Calabretta, Australia Telescope National Facility, CSIRO. - http://www.atnf.csiro.au/people/Mark.Calabretta - $Id: tab.c,v 4.14 2012/07/13 10:02:27 cal103 Exp $ + http://www.atnf.csiro.au/computing/software/wcs + $Id: tab.c,v 8.6 2026/03/29 13:53:56 mcalabre Exp $ *===========================================================================*/ #include @@ -33,11 +30,10 @@ #include "wcserr.h" #include "wcsmath.h" #include "wcsprintf.h" +#include "wcsutil.h" #include "tab.h" -const int TABSET = 137; - -/* Map status return value to message. */ +// Map status return value to message. const char *tab_errmsg[] = { "Success", "Null tabprm pointer passed", @@ -46,28 +42,26 @@ const char *tab_errmsg[] = { "One or more of the x coordinates were invalid", "One or more of the world coordinates were invalid"}; -/* Convenience macro for invoking wcserr_set(). */ +static const int TABSET = 137; + +// Convenience macro for invoking wcserr_set(). #define TAB_ERRMSG(status) WCSERR_SET(status), tab_errmsg[status] -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- int tabini(int alloc, int M, const int K[], struct tabprm *tab) { static const char *function = "tabini"; - int k, m, N; - double *dp; - struct wcserr **err; - if (tab == 0x0) return TABERR_NULL_POINTER; - /* Initialize error message handling. */ - err = &(tab->err); - if (tab->err && tab->flag != -1) { - free(tab->err); + // Initialize error message handling. + if (tab->flag == -1) { + tab->err = 0x0; } - tab->err = 0x0; + struct wcserr **err = &(tab->err); + wcserr_clear(err); if (M <= 0) { @@ -75,11 +69,12 @@ int tabini(int alloc, int M, const int K[], struct tabprm *tab) "M must be positive, got %d", M); } - /* Determine the total number of elements in the coordinate array. */ + // Determine the total number of elements in the coordinate array. + int N; if (K) { N = M; - for (m = 0; m < M; m++) { + for (int m = 0; m < M; m++) { if (K[m] < 0) { return wcserr_set(WCSERR_SET(TABERR_BAD_PARAMS), "Invalid tabular parameters: Each element of K must be " @@ -90,13 +85,21 @@ int tabini(int alloc, int M, const int K[], struct tabprm *tab) } } else { - /* Axis lengths as yet unknown. */ + // Axis lengths as yet unknown. N = 0; } - /* Initialize memory management. */ + // Initialize memory management. if (tab->flag == -1 || tab->m_flag != TABSET) { + if (tab->flag == -1) { + tab->sense = 0x0; + tab->p0 = 0x0; + tab->delta = 0x0; + tab->extrema = 0x0; + tab->set_M = 0; + } + tab->m_flag = 0; tab->m_M = 0; tab->m_N = 0; @@ -108,24 +111,16 @@ int tabini(int alloc, int M, const int K[], struct tabprm *tab) tab->m_coord = 0x0; } else { - /* Clear any outstanding signals set by wcstab(). */ - for (m = 0; m < tab->m_M; m++) { + // Clear any outstanding signals set by wcstab(). + for (int m = 0; m < tab->m_M; m++) { if (tab->m_indxs[m] == (double *)0x1) tab->m_indxs[m] = 0x0; } if (tab->m_coord == (double *)0x1) tab->m_coord = 0x0; } - if (tab->flag == -1) { - tab->sense = 0x0; - tab->p0 = 0x0; - tab->delta = 0x0; - tab->extrema = 0x0; - tab->set_M = 0; - } - - /* Allocate memory for arrays if required. */ + // Allocate memory for arrays if required. if (alloc || tab->K == 0x0 || tab->map == 0x0 || @@ -133,15 +128,15 @@ int tabini(int alloc, int M, const int K[], struct tabprm *tab) tab->index == 0x0 || tab->coord == 0x0) { - /* Was sufficient allocated previously? */ + // Was sufficient allocated previously? if (tab->m_flag == TABSET && (tab->m_M < M || tab->m_N < N)) { - /* No, free it. */ + // No, free it. tabfree(tab); } if (alloc || tab->K == 0x0) { if (tab->m_K) { - /* In case the caller fiddled with it. */ + // In case the caller fiddled with it. tab->K = tab->m_K; } else { @@ -157,7 +152,7 @@ int tabini(int alloc, int M, const int K[], struct tabprm *tab) if (alloc || tab->map == 0x0) { if (tab->m_map) { - /* In case the caller fiddled with it. */ + // In case the caller fiddled with it. tab->map = tab->m_map; } else { @@ -173,7 +168,7 @@ int tabini(int alloc, int M, const int K[], struct tabprm *tab) if (alloc || tab->crval == 0x0) { if (tab->m_crval) { - /* In case the caller fiddled with it. */ + // In case the caller fiddled with it. tab->crval = tab->m_crval; } else { @@ -189,7 +184,7 @@ int tabini(int alloc, int M, const int K[], struct tabprm *tab) if (alloc || tab->index == 0x0) { if (tab->m_index) { - /* In case the caller fiddled with it. */ + // In case the caller fiddled with it. tab->index = tab->m_index; } else { @@ -206,9 +201,9 @@ int tabini(int alloc, int M, const int K[], struct tabprm *tab) return wcserr_set(TAB_ERRMSG(TABERR_MEMORY)); } - /* Recall that calloc() initializes these pointers to zero. */ + // Recall that calloc() initializes these pointers to zero. if (K) { - for (m = 0; m < M; m++) { + for (int m = 0; m < M; m++) { if (K[m]) { if (!(tab->index[m] = calloc(K[m], sizeof(double)))) { return wcserr_set(TAB_ERRMSG(TABERR_MEMORY)); @@ -223,7 +218,7 @@ int tabini(int alloc, int M, const int K[], struct tabprm *tab) if (alloc || tab->coord == 0x0) { if (tab->m_coord) { - /* In case the caller fiddled with it. */ + // In case the caller fiddled with it. tab->coord = tab->m_coord; } else if (N) { @@ -239,18 +234,19 @@ int tabini(int alloc, int M, const int K[], struct tabprm *tab) } } - tab->flag = 0; tab->M = M; - /* Set defaults. */ - for (m = 0; m < M; m++) { + // Set defaults. + for (int m = 0; m < M; m++) { tab->map[m] = -1; tab->crval[m] = 0.0; if (K) { tab->K[m] = K[m]; + double *dp; if ((dp = tab->index[m])) { - for (k = 0; k < K[m]; k++) { + // Table indexes are 1-relative. + for (int k = 1; k <= K[m]; k++) { *(dp++) = k; } } @@ -259,36 +255,36 @@ int tabini(int alloc, int M, const int K[], struct tabprm *tab) } } - /* Initialize the coordinate array. */ - for (dp = tab->coord; dp < tab->coord + N; dp++) { + // Initialize the coordinate array. + for (double *dp = tab->coord; dp < tab->coord + N; dp++) { *dp = UNDEFINED; } + tab->flag = 0; + return 0; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- int tabmem(struct tabprm *tab) { static const char *function = "tabmem"; - int m, M, N; - struct wcserr **err; - if (tab == 0x0) return TABERR_NULL_POINTER; - err = &(tab->err); + struct wcserr **err = &(tab->err); if (tab->M == 0 || tab->K == 0x0) { - /* Should have been set by this time. */ + // Should have been set by this time. return wcserr_set(WCSERR_SET(TABERR_MEMORY), "Null pointers in tabprm struct"); } - N = M = tab->M; - for (m = 0; m < M; m++) { + int M = tab->M; + int N = tab->M; + for (int m = 0; m < M; m++) { if (tab->K[m] < 0) { return wcserr_set(WCSERR_SET(TABERR_BAD_PARAMS), "Invalid tabular parameters: Each element of K must be " @@ -302,7 +298,7 @@ int tabmem(struct tabprm *tab) if (tab->m_M == 0) { tab->m_M = M; } else if (tab->m_M < M) { - /* Only possible if the user changed M. */ + // Only possible if the user changed M. return wcserr_set(WCSERR_SET(TABERR_MEMORY), "tabprm struct inconsistent"); } @@ -310,7 +306,7 @@ int tabmem(struct tabprm *tab) if (tab->m_N == 0) { tab->m_N = N; } else if (tab->m_N < N) { - /* Only possible if the user changed K[]. */ + // Only possible if the user changed K[]. return wcserr_set(WCSERR_SET(TABERR_MEMORY), "tabprm struct inconsistent"); } @@ -339,7 +335,7 @@ int tabmem(struct tabprm *tab) } } - for (m = 0; m < tab->m_M; m++) { + for (int m = 0; m < tab->m_M; m++) { if (tab->m_indxs[m] == 0x0 || tab->m_indxs[m] == (double *)0x1) { if ((tab->m_indxs[m] = tab->index[m])) { tab->m_flag = TABSET; @@ -358,22 +354,20 @@ int tabmem(struct tabprm *tab) return 0; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- int tabcpy(int alloc, const struct tabprm *tabsrc, struct tabprm *tabdst) { static const char *function = "tabcpy"; - int k, m, M, n, N, status; - double *dstp, *srcp; - struct wcserr **err; + int status; if (tabsrc == 0x0) return TABERR_NULL_POINTER; if (tabdst == 0x0) return TABERR_NULL_POINTER; - err = &(tabdst->err); + struct wcserr **err = &(tabdst->err); - M = tabsrc->M; + int M = tabsrc->M; if (M <= 0) { return wcserr_set(WCSERR_SET(TABERR_BAD_PARAMS), "M must be positive, got %d", M); @@ -383,49 +377,104 @@ int tabcpy(int alloc, const struct tabprm *tabsrc, struct tabprm *tabdst) return status; } - N = M; - for (m = 0; m < M; m++) { + int N = M; + for (int m = 0; m < M; m++) { tabdst->map[m] = tabsrc->map[m]; tabdst->crval[m] = tabsrc->crval[m]; N *= tabsrc->K[m]; } - for (m = 0; m < M; m++) { + double *dstp, *srcp; + for (int m = 0; m < M; m++) { if ((srcp = tabsrc->index[m])) { dstp = tabdst->index[m]; - for (k = 0; k < tabsrc->K[m]; k++) { + for (int k = 0; k < tabsrc->K[m]; k++) { *(dstp++) = *(srcp++); } + } else { + if (tabdst->m_indxs && tabdst->m_indxs[m]) { + free(tabdst->m_indxs[m]); + tabdst->index[m] = 0x0; + tabdst->m_indxs[m] = 0x0; + } } } srcp = tabsrc->coord; dstp = tabdst->coord; - for (n = 0; n < N; n++) { + for (int n = 0; n < N; n++) { *(dstp++) = *(srcp++); } return 0; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- -int tabfree(struct tabprm *tab) +int tabcmp( + int dummy, + double tol, + const struct tabprm *tab1, + const struct tabprm *tab2, + int *equal) { - int m; + // Avert nuisance compiler warnings about unused parameters. + (void)dummy; + + if (tab1 == 0x0) return TABERR_NULL_POINTER; + if (tab2 == 0x0) return TABERR_NULL_POINTER; + if (equal == 0x0) return TABERR_NULL_POINTER; + + *equal = 0; + + if (tab1->M != tab2->M) { + return 0; + } + + int M = tab1->M; + + if (!wcsutil_intEq(M, tab1->K, tab2->K) || + !wcsutil_intEq(M, tab1->map, tab2->map) || + !wcsutil_dblEq(M, tol, tab1->crval, tab2->crval)) { + return 0; + } + + int N = M; + for (int m = 0; m < M; m++) { + if (!wcsutil_dblEq(tab1->K[m], tol, tab1->index[m], tab2->index[m])) { + return 0; + } + N *= tab1->K[m]; + } + + if (!wcsutil_dblEq(N, tol, tab1->coord, tab2->coord)) { + return 0; + } + + *equal = 1; + + return 0; +} + + +//---------------------------------------------------------------------------- + +int tabfree(struct tabprm *tab) + +{ if (tab == 0x0) return TABERR_NULL_POINTER; if (tab->flag != -1) { - /* Clear any outstanding signals set by wcstab(). */ - for (m = 0; m < tab->m_M; m++) { + // Clear any outstanding signals set by wcstab(). + for (int m = 0; m < tab->m_M; m++) { if (tab->m_indxs[m] == (double *)0x1) tab->m_indxs[m] = 0x0; } if (tab->m_coord == (double *)0x1) tab->m_coord = 0x0; - /* Free memory allocated by tabini(). */ + // Free memory allocated by tabini(). if (tab->m_flag == TABSET) { if (tab->K == tab->m_K) tab->K = 0x0; if (tab->map == tab->m_map) tab->map = 0x0; @@ -438,7 +487,7 @@ int tabfree(struct tabprm *tab) if (tab->m_crval) free(tab->m_crval); if (tab->m_index) { - for (m = 0; m < tab->m_M; m++) { + for (int m = 0; m < tab->m_M; m++) { if (tab->m_indxs[m]) free(tab->m_indxs[m]); } free(tab->m_index); @@ -448,7 +497,7 @@ int tabfree(struct tabprm *tab) if (tab->m_coord) free(tab->m_coord); } - /* Free memory allocated by tabset(). */ + // Free memory allocated by tabset(). if (tab->sense) free(tab->sense); if (tab->p0) free(tab->p0); if (tab->delta) free(tab->delta); @@ -471,102 +520,198 @@ int tabfree(struct tabprm *tab) tab->extrema = 0x0; tab->set_M = 0; - if (tab->err) { - free(tab->err); - tab->err = 0x0; - } + wcserr_clear(&(tab->err)); tab->flag = 0; return 0; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- + +int tabsize(const struct tabprm *tab, int sizes[2]) + +{ + if (tab == 0x0) { + sizes[0] = sizes[1] = 0; + return 0; + } + + // Base size, in bytes. + sizes[0] = sizeof(struct tabprm); + + // Total size of allocated memory, in bytes. + sizes[1] = 0; + + int exsizes[2]; + int M = tab->M; + + // tabprm::K[]; + sizes[1] += M * sizeof(int); + + // tabprm::map[]; + sizes[1] += M * sizeof(int); + + // tabprm::crval[]; + sizes[1] += M * sizeof(double); + + // tabprm::index[] and tabprm::m_indxs; + sizes[1] += 2*M * sizeof(double *); + for (int m = 0; m < M; m++) { + if (tab->index[m]) { + sizes[1] += tab->K[m] * sizeof(double); + } + } + + // tabprm::coord[]; + sizes[1] += M * tab->nc * sizeof(double); + + // tab::err[]. + wcserr_size(tab->err, exsizes); + sizes[1] += exsizes[0] + exsizes[1]; + + // The remaining arrays are allocated by tabset(). + if (abs(tab->flag) != TABSET) { + return 0; + } + + // tabprm::sense[]. + if (tab->sense) { + sizes[1] += M * sizeof(int); + } + + // tabprm::p0[]. + if (tab->p0) { + sizes[1] += M * sizeof(int); + } + + // tabprm::delta[]. + if (tab->delta) { + sizes[1] += M * sizeof(double); + } + + // tabprm::extrema[]. + int ne = (tab->nc / tab->K[0]) * 2 * M; + sizes[1] += ne * sizeof(double); + + return 0; +} + +//---------------------------------------------------------------------------- + +int tabenq(const struct tabprm *tab, int enquiry) + +{ + // Initialize. + if (tab == 0x0) return TABERR_NULL_POINTER; + + int answer = 0; + + if (enquiry & TABENQ_MEM) { + if (tab->m_flag != TABSET) return 0; + answer = 1; + } + + if (enquiry & TABENQ_SET) { + if (abs(tab->flag) != TABSET) return 0; + answer = 1; + } + + if (enquiry & TABENQ_BYP) { + if (tab->flag != 1 && tab->flag != -TABSET) return 0; + answer = 1; + } + + return answer; +} + +//---------------------------------------------------------------------------- int tabprt(const struct tabprm *tab) { char *cp, text[128]; - int j, k, m, n, nd; double *dp; if (tab == 0x0) return TABERR_NULL_POINTER; - if (tab->flag != TABSET) { + if (abs(tab->flag) != TABSET) { wcsprintf("The tabprm struct is UNINITIALIZED.\n"); return 0; } + // Parameters supplied... wcsprintf(" flag: %d\n", tab->flag); wcsprintf(" M: %d\n", tab->M); - /* Array dimensions. */ + // ...array dimensions. WCSPRINTF_PTR(" K: ", tab->K, "\n"); wcsprintf(" "); - for (m = 0; m < tab->M; m++) { + for (int m = 0; m < tab->M; m++) { wcsprintf("%6d", tab->K[m]); } wcsprintf("\n"); - /* Map vector. */ + // ...map vector. WCSPRINTF_PTR(" map: ", tab->map, "\n"); wcsprintf(" "); - for (m = 0; m < tab->M; m++) { + for (int m = 0; m < tab->M; m++) { wcsprintf("%6d", tab->map[m]); } wcsprintf("\n"); - /* Reference index value. */ + // ...reference index value. WCSPRINTF_PTR(" crval: ", tab->crval, "\n"); wcsprintf(" "); - for (m = 0; m < tab->M; m++) { - wcsprintf(" %- 11.5g", tab->crval[m]); + for (int m = 0; m < tab->M; m++) { + wcsprintf(" %#- 11.5g", tab->crval[m]); } wcsprintf("\n"); - /* Index vectors. */ + // ...index vectors. WCSPRINTF_PTR(" index: ", tab->index, "\n"); - for (m = 0; m < tab->M; m++) { + for (int m = 0; m < tab->M; m++) { wcsprintf(" index[%d]: ", m); WCSPRINTF_PTR("", tab->index[m], ""); if (tab->index[m]) { - for (k = 0; k < tab->K[m]; k++) { + for (int k = 0; k < tab->K[m]; k++) { if (k%5 == 0) { wcsprintf("\n "); } - wcsprintf(" %- 11.5g", tab->index[m][k]); + wcsprintf(" %#- 11.5g", tab->index[m][k]); } - wcsprintf("\n"); } + wcsprintf("\n"); } - /* Coordinate array. */ + // ...coordinate array. WCSPRINTF_PTR(" coord: ", tab->coord, "\n"); dp = tab->coord; - for (n = 0; n < tab->nc; n++) { - /* Array index. */ - j = n; + for (int n = 0; n < tab->nc; n++) { + // Array index. + int j = n; cp = text; - for (m = 0; m < tab->M; m++) { - nd = (tab->K[m] < 10) ? 1 : 2; + for (int m = 0; m < tab->M; m++) { + int nd = (tab->K[m] < 10) ? 1 : 2; sprintf(cp, ",%*d", nd, j % tab->K[m] + 1); j /= tab->K[m]; cp += strlen(cp); } wcsprintf(" (*%s)", text); - for (m = 0; m < tab->M; m++) { - wcsprintf(" %- 11.5g", *(dp++)); + for (int m = 0; m < tab->M; m++) { + wcsprintf(" %#- 11.5g", *(dp++)); } wcsprintf("\n"); } + // Derived values. wcsprintf(" nc: %d\n", tab->nc); WCSPRINTF_PTR(" sense: ", tab->sense, "\n"); if (tab->sense) { wcsprintf(" "); - for (m = 0; m < tab->M; m++) { + for (int m = 0; m < tab->M; m++) { wcsprintf("%6d", tab->sense[m]); } wcsprintf("\n"); @@ -575,7 +720,7 @@ int tabprt(const struct tabprm *tab) WCSPRINTF_PTR(" p0: ", tab->p0, "\n"); if (tab->p0) { wcsprintf(" "); - for (m = 0; m < tab->M; m++) { + for (int m = 0; m < tab->M; m++) { wcsprintf("%6d", tab->p0[m]); } wcsprintf("\n"); @@ -584,40 +729,41 @@ int tabprt(const struct tabprm *tab) WCSPRINTF_PTR(" delta: ", tab->delta, "\n"); if (tab->delta) { wcsprintf(" "); - for (m = 0; m < tab->M; m++) { - wcsprintf(" %- 11.5g", tab->delta[m]); + for (int m = 0; m < tab->M; m++) { + wcsprintf(" %#- 11.5g", tab->delta[m]); } wcsprintf("\n"); } WCSPRINTF_PTR(" extrema: ", tab->extrema, "\n"); dp = tab->extrema; - for (n = 0; n < tab->nc/tab->K[0]; n++) { - /* Array index. */ - j = n; + for (int n = 0; n < tab->nc/tab->K[0]; n++) { + // Array index. + int j = n; cp = text; *cp = '\0'; - for (m = 1; m < tab->M; m++) { - nd = (tab->K[m] < 10) ? 1 : 2; + for (int m = 1; m < tab->M; m++) { + int nd = (tab->K[m] < 10) ? 1 : 2; sprintf(cp, ",%*d", nd, j % tab->K[m] + 1); j /= tab->K[m]; cp += strlen(cp); } wcsprintf(" (*,*%s)", text); - for (m = 0; m < 2*tab->M; m++) { + for (int m = 0; m < 2*tab->M; m++) { if (m == tab->M) wcsprintf("-> "); - wcsprintf(" %- 11.5g", *(dp++)); + wcsprintf(" %#- 11.5g", *(dp++)); } wcsprintf("\n"); } + // Error handling. WCSPRINTF_PTR(" err: ", tab->err, "\n"); if (tab->err) { wcserr_prt(tab->err, " "); } - /* Memory management. */ + // Memory management. wcsprintf(" m_flag: %d\n", tab->m_flag); wcsprintf(" m_M: %d\n", tab->m_M); wcsprintf(" m_N: %d\n", tab->m_N); @@ -637,7 +783,7 @@ int tabprt(const struct tabprm *tab) WCSPRINTF_PTR(" m_index: ", tab->m_index, ""); if (tab->m_index == tab->index) wcsprintf(" (= index)"); wcsprintf("\n"); - for (m = 0; m < tab->M; m++) { + for (int m = 0; m < tab->M; m++) { wcsprintf(" m_indxs[%d]: ", m); WCSPRINTF_PTR("", tab->m_indxs[m], ""); if (tab->m_indxs[m] == tab->index[m]) wcsprintf(" (= index[%d])", m); @@ -651,52 +797,64 @@ int tabprt(const struct tabprm *tab) return 0; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- + +int tabperr(const struct tabprm *tab, const char *prefix) + +{ + if (tab == 0x0) return TABERR_NULL_POINTER; + + if (tab->err) { + wcserr_prt(tab->err, prefix); + } + + return 0; +} + +//---------------------------------------------------------------------------- int tabset(struct tabprm *tab) { static const char *function = "tabset"; - int i, ic, k, *Km, m, M, ne; - double *dcrd, *dmax, *dmin, dPsi, dval, *Psi; - struct wcserr **err; - if (tab == 0x0) return TABERR_NULL_POINTER; - err = &(tab->err); + if (tab->flag == -TABSET) return 0; + struct wcserr **err = &(tab->err); - /* Check the number of tabular coordinate axes. */ - if ((M = tab->M) < 1) { + // Check the number of tabular coordinate axes. + int M = tab->M; + if (M < 1) { return wcserr_set(WCSERR_SET(TABERR_BAD_PARAMS), "Invalid tabular parameters: M must be positive, got %d", M); } - /* Check the axis lengths. */ + // Check the axis lengths. if (!tab->K) { return wcserr_set(WCSERR_SET(TABERR_MEMORY), "Null pointers in tabprm struct"); } tab->nc = 1; - for (m = 0; m < M; m++) { + for (int m = 0; m < M; m++) { if (tab->K[m] < 1) { return wcserr_set(WCSERR_SET(TABERR_BAD_PARAMS), "Invalid tabular parameters: Each element of K must be positive, " "got %d", tab->K[m]); } - /* Number of coordinate vectors in the coordinate array. */ + // Number of coordinate vectors in the coordinate array. tab->nc *= tab->K[m]; } - /* Check that the map vector is sensible. */ + // Check that the map vector is sensible. if (!tab->map) { return wcserr_set(WCSERR_SET(TABERR_MEMORY), "Null pointers in tabprm struct"); } - for (m = 0; m < M; m++) { - i = tab->map[m]; + for (int m = 0; m < M; m++) { + int i = tab->map[m]; if (i < 0) { return wcserr_set(WCSERR_SET(TABERR_BAD_PARAMS), "Invalid tabular parameters: Each element of map must be " @@ -704,14 +862,14 @@ int tabset(struct tabprm *tab) } } - /* Check memory allocation for the remaining vectors. */ + // Check memory allocation for the remaining vectors. if (!tab->crval || !tab->index || !tab->coord) { return wcserr_set(WCSERR_SET(TABERR_MEMORY), "Null pointers in tabprm struct"); } - /* Take memory if signalled to by wcstab(). */ - for (m = 0; m < tab->m_M; m++) { + // Take memory if signalled to by wcstab(). + for (int m = 0; m < tab->m_M; m++) { if (tab->m_indxs[m] == (double *)0x1 && (tab->m_indxs[m] = tab->index[m])) { tab->m_flag = TABSET; @@ -724,15 +882,20 @@ int tabset(struct tabprm *tab) } - /* Allocate memory for work vectors. */ - if (tab->flag != TABSET || tab->set_M < M) { - /* Free memory that may have been allocated previously. */ + // Allocate memory for work vectors. + if (abs(tab->flag) != TABSET || tab->set_M < M) { + // Free memory that may have been allocated previously. if (tab->sense) free(tab->sense); if (tab->p0) free(tab->p0); if (tab->delta) free(tab->delta); if (tab->extrema) free(tab->extrema); - /* Allocate memory for internal arrays. */ + tab->sense = 0x0; + tab->p0 = 0x0; + tab->delta = 0x0; + tab->extrema = 0x0; + + // Allocate memory for internal arrays. if (!(tab->sense = calloc(M, sizeof(int)))) { return wcserr_set(TAB_ERRMSG(TABERR_MEMORY)); } @@ -748,7 +911,7 @@ int tabset(struct tabprm *tab) return wcserr_set(TAB_ERRMSG(TABERR_MEMORY)); } - ne = M * tab->nc * 2 / tab->K[0]; + int ne = (tab->nc / tab->K[0]) * 2 * M; if (!(tab->extrema = calloc(ne, sizeof(double)))) { free(tab->sense); free(tab->p0); @@ -759,32 +922,33 @@ int tabset(struct tabprm *tab) tab->set_M = M; } - /* Check that the index vectors are monotonic. */ - Km = tab->K; - for (m = 0; m < M; m++, Km++) { + // Check that the index vectors are monotonic. + int *Km = tab->K; + for (int m = 0; m < M; m++, Km++) { tab->sense[m] = 0; if (*Km > 1) { + double *Psi; if ((Psi = tab->index[m]) == 0x0) { - /* Default indexing. */ + // Default indexing. tab->sense[m] = 1; } else { - for (k = 0; k < *Km-1; k++) { + for (int k = 0; k < *Km-1; k++) { switch (tab->sense[m]) { case 0: if (Psi[k] < Psi[k+1]) { - /* Monotonic increasing. */ + // Monotonic increasing. tab->sense[m] = 1; } else if (Psi[k] > Psi[k+1]) { - /* Monotonic decreasing. */ + // Monotonic decreasing. tab->sense[m] = -1; } break; case 1: if (Psi[k] > Psi[k+1]) { - /* Should be monotonic increasing. */ + // Should be monotonic increasing. free(tab->sense); free(tab->p0); free(tab->delta); @@ -797,7 +961,7 @@ int tabset(struct tabprm *tab) case -1: if (Psi[k] < Psi[k+1]) { - /* Should be monotonic decreasing. */ + // Should be monotonic decreasing. free(tab->sense); free(tab->p0); free(tab->delta); @@ -822,22 +986,23 @@ int tabset(struct tabprm *tab) } } - /* Find the extremal values of the coordinate elements in each row. */ - dcrd = tab->coord; - dmin = tab->extrema; - dmax = tab->extrema + M; - for (ic = 0; ic < tab->nc; ic += tab->K[0]) { - for (m = 0; m < M; m++, dcrd++) { + // Find the extremal values of the coordinate elements in each row. + double *dcrd = tab->coord; + double *dmin = tab->extrema; + double *dmax = tab->extrema + M; + for (int ic = 0; ic < tab->nc; ic += tab->K[0]) { + for (int m = 0; m < M; m++, dcrd++) { if (tab->K[0] > 1) { - /* Extrapolate a little before the start of the row. */ - Psi = tab->index[0]; + // Extrapolate a little before the start of the row. + double dPsi; + double *Psi = tab->index[0]; if (Psi == 0x0) { dPsi = 1.0; } else { dPsi = Psi[1] - Psi[0]; } - dval = *dcrd; + double dval = *dcrd; if (dPsi != 0.0) { dval -= 0.5 * (*(dcrd+M) - *dcrd)/dPsi; } @@ -849,21 +1014,22 @@ int tabset(struct tabprm *tab) } dcrd -= M; - for (i = 0; i < tab->K[0]; i++) { - for (m = 0; m < M; m++, dcrd++) { + for (int i = 0; i < tab->K[0]; i++) { + for (int m = 0; m < M; m++, dcrd++) { if (*(dmax+m) < *dcrd) *(dmax+m) = *dcrd; if (*(dmin+m) > *dcrd) *(dmin+m) = *dcrd; if (tab->K[0] > 1 && i == tab->K[0]-1) { - /* Extrapolate a little beyond the end of the row. */ - Psi = tab->index[0]; + // Extrapolate a little beyond the end of the row. + double dPsi; + double *Psi = tab->index[0]; if (Psi == 0x0) { dPsi = 1.0; } else { dPsi = Psi[i] - Psi[i-1]; } - dval = *dcrd; + double dval = *dcrd; if (dPsi != 0.0) { dval += 0.5 * (*dcrd - *(dcrd-M))/dPsi; } @@ -878,12 +1044,12 @@ int tabset(struct tabprm *tab) dmax += 2*M; } - tab->flag = TABSET; + tab->flag = (tab->flag == 1) ? -TABSET : TABSET; return 0; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- int tabx2s( struct tabprm *tab, @@ -896,48 +1062,44 @@ int tabx2s( { static const char *function = "tabx2s"; - int i, iv, k, *Km, m, M, n, nv, offset, p1, status; - double *coord, *Psi, psi_m, upsilon, wgt; - register int *statp; - register const double *xp; - register double *wp; - struct wcserr **err; + int status; if (tab == 0x0) return TABERR_NULL_POINTER; - err = &(tab->err); + struct wcserr **err = &(tab->err); - /* Initialize if required. */ - if (tab->flag != TABSET) { + // Initialize if required. + if (abs(tab->flag) != TABSET) { if ((status = tabset(tab))) return status; } - /* This is used a lot. */ - M = tab->M; + // This is used a lot. + int M = tab->M; status = 0; - xp = x; - wp = world; - statp = stat; - for (n = 0; n < ncoord; n++) { - /* Determine the indexes. */ - Km = tab->K; - for (m = 0; m < M; m++, Km++) { - /* N.B. psi_m and Upsilon_m are 1-relative FITS indexes. */ - i = tab->map[m]; - psi_m = *(xp+i) + tab->crval[m]; - - Psi = tab->index[m]; + const double *xp = x; + double *wp = world; + int *statp = stat; + for (int n = 0; n < ncoord; n++) { + // Determine the indexes. + int *Km = tab->K; + for (int m = 0; m < M; m++, Km++) { + // N.B. psi_m and Upsilon_m are 1-relative FITS indexes. + int i = tab->map[m]; + double psi_m = *(xp+i) + tab->crval[m]; + + double *Psi = tab->index[m]; + double upsilon; if (Psi == 0x0) { - /* Default indexing is simple. */ + // Default indexing is simple. upsilon = psi_m; } else { - /* To ease confusion, decrement Psi so that we can use 1-relative - C array indexing to match the 1-relative FITS indexing. */ + // To ease confusion, decrement Psi so that we can use 1-relative + // C array indexing to match the 1-relative FITS indexing. Psi--; if (*Km == 1) { - /* Index vector is degenerate. */ + // Index vector is degenerate. if (Psi[1]-0.5 <= psi_m && psi_m <= Psi[1]+0.5) { upsilon = psi_m; } else { @@ -947,16 +1109,17 @@ int tabx2s( } } else { - /* Interpolate in the indexing vector. */ + // Interpolate in the indexing vector. + int k; if (tab->sense[m] == 1) { - /* Monotonic increasing index values. */ + // Monotonic increasing index values. if (psi_m < Psi[1]) { if (Psi[1] - 0.5*(Psi[2]-Psi[1]) <= psi_m) { - /* Allow minor extrapolation. */ + // Allow minor extrapolation. k = 1; } else { - /* Index is out of range. */ + // Index is out of range. *statp = 1; status = wcserr_set(TAB_ERRMSG(TABERR_BAD_X)); goto next; @@ -964,11 +1127,11 @@ int tabx2s( } else if (Psi[*Km] < psi_m) { if (psi_m <= Psi[*Km] + 0.5*(Psi[*Km]-Psi[*Km-1])) { - /* Allow minor extrapolation. */ + // Allow minor extrapolation. k = *Km - 1; } else { - /* Index is out of range. */ + // Index is out of range. *statp = 1; status = wcserr_set(TAB_ERRMSG(TABERR_BAD_X)); goto next; @@ -989,14 +1152,14 @@ int tabx2s( } } else { - /* Monotonic decreasing index values. */ + // Monotonic decreasing index values. if (psi_m > Psi[1]) { if (Psi[1] + 0.5*(Psi[1]-Psi[2]) >= psi_m) { - /* Allow minor extrapolation. */ + // Allow minor extrapolation. k = 1; } else { - /* Index is out of range. */ + // Index is out of range. *statp = 1; status = wcserr_set(TAB_ERRMSG(TABERR_BAD_X)); goto next; @@ -1004,11 +1167,11 @@ int tabx2s( } else if (psi_m < Psi[*Km]) { if (Psi[*Km] - 0.5*(Psi[*Km-1]-Psi[*Km]) <= psi_m) { - /* Allow minor extrapolation. */ + // Allow minor extrapolation. k = *Km - 1; } else { - /* Index is out of range. */ + // Index is out of range. *statp = 1; status = wcserr_set(TAB_ERRMSG(TABERR_BAD_X)); goto next; @@ -1034,42 +1197,44 @@ int tabx2s( } if (upsilon < 0.5 || upsilon > *Km + 0.5) { - /* Index out of range. */ + // Index out of range. *statp = 1; status = wcserr_set(TAB_ERRMSG(TABERR_BAD_X)); goto next; } - /* Fiducial array indices and fractional offset. - p1 is 1-relative while tab::p0 is 0-relative. */ - p1 = (int)floor(upsilon); + // Fiducial array indices and fractional offset. + // p1 is 1-relative while tab::p0 is 0-relative. + int p1 = (int)floor(upsilon); tab->p0[m] = p1 - 1; tab->delta[m] = upsilon - p1; if (p1 == 0) { + // Extrapolation below p1 == 1. tab->p0[m] += 1; tab->delta[m] -= 1.0; } else if (p1 == *Km && *Km > 1) { + // Extrapolation above p1 == K_m. tab->p0[m] -= 1; tab->delta[m] += 1.0; } } - /* Now interpolate in the coordinate array; the M-dimensional linear */ - /* interpolation algorithm is described in Sect. 3.4 of WCS Paper IV. */ - for (m = 0; m < M; m++) { - i = tab->map[m]; - *(wp+i) = 0.0; + // Now interpolate in the coordinate array; the M-dimensional linear + // interpolation algorithm is described in Sect. 3.4 of WCS Paper IV. + for (int m = 0; m < M; m++) { + int i = tab->map[m]; + *(wp+i) = 0.0; } - /* Loop over the 2^M vertices surrounding P. */ - nv = 1 << M; - for (iv = 0; iv < nv; iv++) { - /* Locate vertex in the coordinate array and compute its weight. */ - offset = 0; - wgt = 1.0; - for (m = M-1; m >= 0; m--) { + // Loop over the 2^M vertices surrounding P. + int nv = 1 << M; + for (int iv = 0; iv < nv; iv++) { + // Locate vertex in the coordinate array and compute its weight. + int offset = 0; + double wgt = 1.0; + for (int m = M-1; m >= 0; m--) { offset *= tab->K[m]; offset += tab->p0[m]; if (iv & (1 << m)) { @@ -1082,10 +1247,10 @@ int tabx2s( if (wgt == 0.0) continue; - /* Add the contribution from this vertex to each element. */ - coord = tab->coord + offset*M; - for (m = 0; m < M; m++) { - i = tab->map[m]; + // Add the contribution from this vertex to each element. + double *coord = tab->coord + offset*M; + for (int m = 0; m < M; m++) { + int i = tab->map[m]; *(wp+i) += *(coord++) * wgt; } @@ -1103,7 +1268,13 @@ int tabx2s( return status; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- + +// Helper functions used only by tabs2x(). +static int tabedge(struct tabprm *); +static int tabrow(struct tabprm *, const double *); +static int tabvox(struct tabprm *, const double *, int, double **, + unsigned int *); int tabs2x( struct tabprm* tab, @@ -1116,30 +1287,21 @@ int tabs2x( { static const char *function = "tabs2x"; - int tabedge(struct tabprm *); - int tabrow(struct tabprm *, const double *); - int tabvox(struct tabprm *, const double *, int, double **, unsigned int *); - - int edge, i, ic, iv, k, *Km, M, m, n, nv, offset, status; - double *dcrd, delta, *Psi, psi_m, **tabcoord, upsilon; - register int *statp; - register const double *wp; - register double *xp; - struct wcserr **err; + int status; if (tab == 0x0) return TABERR_NULL_POINTER; - err = &(tab->err); + struct wcserr **err = &(tab->err); - /* Initialize if required. */ - if (tab->flag != TABSET) { + // Initialize if required. + if (abs(tab->flag) != TABSET) { if ((status = tabset(tab))) return status; } - /* This is used a lot. */ - M = tab->M; + // This is used a lot. + int M = tab->M; - tabcoord = 0x0; - nv = 0; + double **tabcoord = 0x0; + int nv = 0; if (M > 1) { nv = 1 << M; tabcoord = calloc(nv, sizeof(double *)); @@ -1147,57 +1309,61 @@ int tabs2x( status = 0; - wp = world; - xp = x; - statp = stat; - for (n = 0; n < ncoord; n++) { - /* Locate this coordinate in the coordinate array. */ - edge = 0; - for (m = 0; m < M; m++) { + const double *wp = world; + double *xp = x; + int *statp = stat; + for (int n = 0; n < ncoord; n++) { + // Locate this coordinate in the coordinate array. + int edge = 0; + for (int m = 0; m < M; m++) { tab->p0[m] = 0; } + int ic; for (ic = 0; ic < tab->nc; ic++) { if (tab->p0[0] == 0) { - /* New row, could it contain a solution? */ + // New row, could it contain a solution? if (edge || tabrow(tab, wp)) { - /* No, skip it. */ + // No, skip it. ic += tab->K[0]; - tab->p0[1]++; - edge = tabedge(tab); + if (1 < M) { + tab->p0[1]++; + edge = tabedge(tab); + } - /* Because ic will be incremented when the loop is reentered. */ + // Because ic will be incremented when the loop is reentered. ic--; continue; } } if (M == 1) { - /* Deal with the one-dimensional case separately for efficiency. */ - if (*wp == tab->coord[0]) { + // Deal with the one-dimensional case separately for efficiency. + double w = wp[tab->map[0]]; + if (w == tab->coord[0]) { tab->p0[0] = 0; tab->delta[0] = 0.0; break; } else if (ic < tab->nc - 1) { - if (((tab->coord[ic] <= *wp && *wp <= tab->coord[ic+1]) || - (tab->coord[ic] >= *wp && *wp >= tab->coord[ic+1])) && + if (((tab->coord[ic] <= w && w <= tab->coord[ic+1]) || + (tab->coord[ic] >= w && w >= tab->coord[ic+1])) && (tab->index[0] == 0x0 || tab->index[0][ic] != tab->index[0][ic+1])) { tab->p0[0] = ic; - tab->delta[0] = (*wp - tab->coord[ic]) / + tab->delta[0] = (w - tab->coord[ic]) / (tab->coord[ic+1] - tab->coord[ic]); break; } } } else { - /* Multi-dimensional tables are harder. */ + // Multi-dimensional tables are harder. if (!edge) { - /* Addresses of the coordinates for each corner of the "voxel". */ - for (iv = 0; iv < nv; iv++) { - offset = 0; - for (m = M-1; m >= 0; m--) { + // Addresses of the coordinates for each corner of the "voxel". + for (int iv = 0; iv < nv; iv++) { + int offset = 0; + for (int m = M-1; m >= 0; m--) { offset *= tab->K[m]; offset += tab->p0[m]; if ((iv & (1 << m)) && (tab->K[m] > 1)) offset++; @@ -1206,12 +1372,12 @@ int tabs2x( } if (tabvox(tab, wp, 0, tabcoord, 0x0) == 0) { - /* Found a solution. */ + // Found a solution. break; } } - /* Next voxel. */ + // Next voxel. tab->p0[0]++; edge = tabedge(tab); } @@ -1219,15 +1385,16 @@ int tabs2x( if (ic == tab->nc) { - /* Coordinate not found; allow minor extrapolation. */ + // Coordinate not found; allow minor extrapolation. if (M == 1) { - /* Should there be a solution? */ - if (tab->extrema[0] <= *wp && *wp <= tab->extrema[1]) { - dcrd = tab->coord; - for (i = 0; i < 2; i++) { + // Should there be a solution? + double w = wp[tab->map[0]]; + if (tab->extrema[0] <= w && w <= tab->extrema[1]) { + double *dcrd = tab->coord; + for (int i = 0; i < 2; i++) { if (i) dcrd += tab->K[0] - 2; - delta = (*wp - *dcrd) / (*(dcrd+1) - *dcrd); + double delta = (w - *dcrd) / (*(dcrd+1) - *dcrd); if (i == 0) { if (-0.5 <= delta && delta <= 0.0) { @@ -1247,45 +1414,47 @@ int tabs2x( } } else { - /* Multi-dimensional tables. */ - /* >>> TBD <<< */ + // Multi-dimensional tables. + // >>> TBD <<< } } if (ic == tab->nc) { - /* Coordinate not found. */ + // Coordinate not found. *statp = 1; status = wcserr_set(TAB_ERRMSG(TABERR_BAD_WORLD)); + } else { - /* Determine the intermediate world coordinates. */ - Km = tab->K; - for (m = 0; m < M; m++, Km++) { - /* N.B. Upsilon_m and psi_m are 1-relative FITS indexes. */ - upsilon = (tab->p0[m] + 1) + tab->delta[m]; + // Determine the intermediate world coordinates. + int *Km = tab->K; + for (int m = 0; m < M; m++, Km++) { + // N.B. Upsilon_m and psi_m are 1-relative FITS indexes. + double upsilon = (tab->p0[m] + 1) + tab->delta[m]; if (upsilon < 0.5 || upsilon > *Km + 0.5) { - /* Index out of range. */ + // Index out of range. *statp = 1; status = wcserr_set(TAB_ERRMSG(TABERR_BAD_WORLD)); } else { - /* Do inverse lookup of the index vector. */ - Psi = tab->index[m]; + // Do inverse lookup of the index vector. + double *Psi = tab->index[m]; + double psi_m; if (Psi == 0x0) { - /* Default indexing. */ + // Default indexing. psi_m = upsilon; } else { - /* Decrement Psi and use 1-relative C array indexing to match the - 1-relative FITS indexing. */ + // Decrement Psi and use 1-relative C array indexing to match the + // 1-relative FITS indexing. Psi--; if (*Km == 1) { - /* Degenerate index vector. */ + // Degenerate index vector. psi_m = Psi[1]; } else { - k = (int)(upsilon); + int k = (int)(upsilon); psi_m = Psi[k]; if (k < *Km) { psi_m += (upsilon - k) * (Psi[k+1] - Psi[k]); @@ -1293,8 +1462,7 @@ int tabs2x( } } - i = tab->map[m]; - xp[i] = psi_m - tab->crval[m]; + xp[tab->map[m]] = psi_m - tab->crval[m]; } } *statp = 0; @@ -1311,24 +1479,27 @@ int tabs2x( } /*---------------------------------------------------------------------------- -* Convenience routine to deal with of edge effects in tabprm::p0. +* Convenience routine to check whether tabprm::p0 has been incremented beyond +* the end of an index vector and if so move it to the start of the next one. +* Returns 1 if tabprm::p0 is sitting at the end of any non-degenerate index +* vector. *---------------------------------------------------------------------------*/ int tabedge(struct tabprm* tab) { - int edge, *Km, m; - - edge = 0; - Km = tab->K; - for (m = 0; m < tab->M; m++, Km++) { - if (tab->p0[m] == *Km) { - /* p0 has been incremented beyond the end of the row, point it to the - next one. */ + int edge = 0; + + for (int m = 0; m < tab->M; m++) { + if (tab->p0[m] == tab->K[m]) { + // p0 has been incremented beyond the end of an index vector, point it + // to the next one. tab->p0[m] = 0; - tab->p0[m+1]++; - } else if (tab->p0[m] == *Km - 1 && *Km > 1) { - /* p0 is sitting at the end of a non-degenerate row. */ + if (m < tab->M-1) { + tab->p0[m+1]++; + } + } else if (tab->p0[m] == tab->K[m]-1 && tab->K[m] > 1) { + // p0 is sitting at the end of a non-degenerate index vector. edge = 1; } } @@ -1368,55 +1539,52 @@ int tabedge(struct tabprm* tab) int tabrow(struct tabprm* tab, const double *wp) { - int iv, M, m, nv, offset; - unsigned int eq, gt, lt; const double tol = 1e-10; - double *cp, w; - - M = tab->M; - - /* The number of corners in a "voxel". We need examine only half this - number of rows. The extra factor of two will be used to select between - the minimal and maximal values in each row. */ - nv = 1 << M; - - eq = 0; - lt = 0; - gt = 0; - for (iv = 0; iv < nv; iv++) { - /* Find the index into tabprm::extrema for this row. */ - offset = 0; - for (m = M-1; m > 0; m--) { + + int M = tab->M; + + // The number of corners in a "voxel". We need examine only half this + // number of rows. The extra factor of two will be used to select between + // the minimal and maximal values in each row. + unsigned int nv = 1 << M; + + unsigned int eq = 0; + unsigned int lt = 0; + unsigned int gt = 0; + for (unsigned int iv = 0; iv < nv; iv++) { + // Find the index into tabprm::extrema for this row. + int offset = 0; + for (int m = M-1; m > 0; m--) { offset *= tab->K[m]; offset += tab->p0[m]; - /* Select the row. */ + // Select the row. if (iv & (1 << m)) { if (tab->K[m] > 1) offset++; } } - /* The K_1 dimension has length 2 (see prologue). */ + // The K_1 dimension has length 2 (see prologue). offset *= 2; - /* Select the minimum on even numbered iterations, else the maximum. */ + // Select the minimum on even numbered iterations, else the maximum. if (iv & 1) offset++; - /* The last dimension has length M (see prologue). */ + // The last dimension has length M (see prologue). offset *= M; - /* Address of the extremal elements (min or max) for this row. */ - cp = tab->extrema + offset; + // Address of the extremal elements (min or max) for this row. + double *cp = tab->extrema + offset; - /* For each coordinate element, we only need to find one row where its - minimum value is less than that of wp, and one row where the maximum - value is greater. That doesn't mean that there is a solution, only - that there might be. */ - for (m = 0; m < M; m++, cp++) { - /* Apply the axis mapping. */ - w = wp[tab->map[m]]; + // For each coordinate element, we only need to find one row where its + // minimum value is less than that of wp, and one row where the maximum + // value is greater. That doesn't mean that there is a solution, only + // that there might be. + for (int m = 0; m < M; m++, cp++) { + // Apply the axis mapping. + double w = wp[tab->map[m]]; - /* Finally the test itself; set bits in the bitmask. */ + // Finally the test itself; set bits in the bitmask. if (fabs(*cp - w) < tol) { eq |= (1 << m); } else if (*cp < w) { @@ -1426,14 +1594,14 @@ int tabrow(struct tabprm* tab, const double *wp) } } - /* Have all bits been switched on? */ + // Have all bits been switched on? if ((lt | eq) == nv-1 && (gt | eq) == nv-1) { - /* A solution could lie within this row of voxels. */ + // A solution could lie within this row of voxels. return 0; } } - /* No solution in this row. */ + // No solution in this row. return 1; } @@ -1454,7 +1622,7 @@ int tabrow(struct tabprm* tab, const double *wp) * dissect the voxel. It is ignored when tabvox() is called from outside * (level == 0). * -* It is assumed that the image dimensions are no greater than 16. +* It is assumed that the image dimensions are no greater than 32. ----------------------------------------------------------------------------*/ int tabvox( @@ -1465,30 +1633,28 @@ int tabvox( unsigned int *vox) { - int i, iv, jv, M, m, nv; - unsigned int eq, et, gt, lt, vox2[16]; const double tol = 1e-10; - double coord[16], *cp, dv, w, wgt; - M = tab->M; + int M = tab->M; - /* The number of corners in a voxel. */ - nv = 1 << M; + // The number of corners in a voxel. + unsigned int nv = 1 << M; - dv = 1.0; - for (i = 0; i < level; i++) { + double dv = 1.0; + for (int i = 0; i < level; i++) { dv /= 2.0; } - /* Could the coordinate lie within this voxel (level == 0) or sub-voxel - (level > 0)? We use the fact that with linear interpolation the - coordinate elements are extremal in a corner and test each one. */ - lt = 0; - gt = 0; - eq = 0; - for (iv = 0; iv < nv; iv++) { - /* Select a corner of the sub-voxel. */ - for (m = 0; m < M; m++) { + // Could the coordinate lie within this voxel (level == 0) or sub-voxel + // (level > 0)? We use the fact that with linear interpolation the + // coordinate elements are extremal in a corner and test each one. + unsigned int lt = 0; + unsigned int gt = 0; + unsigned int eq = 0; + for (unsigned int iv = 0; iv < nv; iv++) { + // Select a corner of the sub-voxel. + double coord[32]; + for (int m = 0; m < M; m++) { coord[m] = 0.0; tab->delta[m] = level ? dv*vox[m] : 0.0; @@ -1497,13 +1663,13 @@ int tabvox( } } - /* Compute the coordinates of this corner of the sub-voxel by linear - interpolation using the weighting algorithm described in Sect. 3.4 of - WCS Paper IV. */ - for (jv = 0; jv < nv; jv++) { - /* Find the weight for this corner of the parent voxel. */ - wgt = 1.0; - for (m = 0; m < M; m++) { + // Compute the coordinates of this corner of the sub-voxel by linear + // interpolation using the weighting algorithm described in Sect. 3.4 of + // WCS Paper IV. + for (unsigned int jv = 0; jv < nv; jv++) { + // Find the weight for this corner of the parent voxel. + double wgt = 1.0; + for (int m = 0; m < M; m++) { if (jv & (1 << m)) { wgt *= tab->delta[m]; } else { @@ -1513,22 +1679,22 @@ int tabvox( if (wgt == 0.0) continue; - /* Add its contribution to each coordinate element. */ - cp = tabcoord[jv]; - for (m = 0; m < M; m++) { + // Add its contribution to each coordinate element. + double *cp = tabcoord[jv]; + for (int m = 0; m < M; m++) { coord[m] += *(cp++) * wgt; } if (wgt == 1.0) break; } - /* Coordinate elements are minimal or maximal in a corner. */ - et = 0; - for (m = 0; m < M; m++) { - /* Apply the axis mapping. */ - w = wp[tab->map[m]]; + // Coordinate elements are minimal or maximal in a corner. + unsigned int et = 0; + for (int m = 0; m < M; m++) { + // Apply the axis mapping. + double w = wp[tab->map[m]]; - /* Finally the test itself; set bits in the bitmask. */ + // Finally the test itself; set bits in the bitmask. if (fabs(coord[m] - w) < tol) { et |= (1 << m); } else if (coord[m] < w) { @@ -1539,45 +1705,46 @@ int tabvox( } if (et == nv-1) { - /* We've stumbled across a solution in this corner of the sub-voxel. */ + // We've stumbled across a solution in this corner of the sub-voxel. return 0; } eq |= et; } - /* Could the coordinate lie within this sub-voxel? */ + // Could the coordinate lie within this sub-voxel? if ((lt | eq) == nv-1 && (gt | eq) == nv-1) { - /* Yes it could, but does it? */ + // Yes it could, but does it? - /* Is it time to stop the recursion? */ + // Is it time to stop the recursion? if (level == 31) { - /* We have a solution, squeeze out the last bit of juice. */ + // We have a solution, squeeze out the last bit of juice. dv /= 2.0; - for (m = 0; m < M; m++) { + for (int m = 0; m < M; m++) { tab->delta[m] = dv * (2.0*vox[m] + 1.0); } return 0; } - /* Subdivide the sub-voxel and try again for each subdivision. */ - for (iv = 0; iv < nv; iv++) { - /* Select the subdivision. */ - for (m = 0; m < M; m++) { + // Subdivide the sub-voxel and try again for each subdivision. + for (unsigned int iv = 0; iv < nv; iv++) { + // Select the subdivision. + unsigned int vox2[32]; + for (int m = 0; m < M; m++) { vox2[m] = level ? 2*vox[m] : 0; if (iv & (1 << m)) { vox2[m]++; } } - /* Recurse. */ + // Recurse. if (tabvox(tab, wp, level+1, tabcoord, vox2) == 0) { return 0; } } } - /* No solution in this sub-voxel. */ + // No solution in this sub-voxel. return 1; } diff --git a/cextern/wcslib/C/tab.h b/cextern/wcslib/C/tab.h index 4219ac1b2837..a8775815833a 100644 --- a/cextern/wcslib/C/tab.h +++ b/cextern/wcslib/C/tab.h @@ -1,7 +1,6 @@ /*============================================================================ - - WCSLIB 4.14 - an implementation of the FITS WCS standard. - Copyright (C) 1995-2012, Mark Calabretta + WCSLIB 8.6 - an implementation of the FITS WCS standard. + Copyright (C) 1995-2026, Mark Calabretta This file is part of WCSLIB. @@ -18,40 +17,43 @@ You should have received a copy of the GNU Lesser General Public License along with WCSLIB. If not, see http://www.gnu.org/licenses. - Direct correspondence concerning WCSLIB to mark@calabretta.id.au - Author: Mark Calabretta, Australia Telescope National Facility, CSIRO. - http://www.atnf.csiro.au/people/Mark.Calabretta - $Id: tab.h,v 4.14 2012/07/13 10:02:27 cal103 Exp $ + http://www.atnf.csiro.au/computing/software/wcs + $Id: tab.h,v 8.6 2026/03/29 13:53:56 mcalabre Exp $ *============================================================================= * -* WCSLIB 4.14 - C routines that implement tabular coordinate systems as -* defined by the FITS World Coordinate System (WCS) standard. Refer to -* -* "Representations of world coordinates in FITS", -* Greisen, E.W., & Calabretta, M.R. 2002, A&A, 395, 1061 (paper I) -* -* "Representations of spectral coordinates in FITS", -* Greisen, E.W., Calabretta, M.R., Valdes, F.G., & Allen, S.L. -* 2006, A&A, 446, 747 (Paper III) -* -* Refer to the README file provided with WCSLIB for an overview of the -* library. +* WCSLIB 8.6 - C routines that implement the FITS World Coordinate System +* (WCS) standard. Refer to the README file provided with WCSLIB for an +* overview of the library. * * * Summary of the tab routines * --------------------------- -* These routines implement the part of the FITS WCS standard that deals with -* tabular coordinates, i.e. coordinates that are defined via a lookup table. -* They define methods to be used for computing tabular world coordinates from -* intermediate world coordinates (a linear transformation of image pixel -* coordinates), and vice versa. They are based on the tabprm struct which -* contains all information needed for the computations. The struct contains -* some members that must be set by the user, and others that are maintained -* by these routines, somewhat like a C++ class but with no encapsulation. +* Routines in this suite implement the part of the FITS World Coordinate +* System (WCS) standard that deals with tabular coordinates, i.e. coordinates +* that are defined via a lookup table, as described in +* += "Representations of world coordinates in FITS", += Greisen, E.W., & Calabretta, M.R. 2002, A&A, 395, 1061 (WCS Paper I) += += "Representations of spectral coordinates in FITS", += Greisen, E.W., Calabretta, M.R., Valdes, F.G., & Allen, S.L. += 2006, A&A, 446, 747 (WCS Paper III) +* +* These routines define methods to be used for computing tabular world +* coordinates from intermediate world coordinates (a linear transformation +* of image pixel coordinates), and vice versa. They are based on the tabprm +* struct which contains all information needed for the computations. The +* struct contains some members that must be set by the user, and others that +* are maintained by these routines, somewhat like a C++ class but with no +* encapsulation. * * tabini(), tabmem(), tabcpy(), and tabfree() are provided to manage the -* tabprm struct, and another, tabprt(), to print its contents. +* tabprm struct, tabsize() computes its total size including allocated memory, +* tabenq() returns information about the state of the struct, and tabprt() +* prints its contents. +* +* tabperr() prints the error message(s) (if any) stored in a tabprm struct. * * A setup routine, tabset(), computes intermediate values in the tabprm struct * from parameters in it that were supplied by the user. The struct always @@ -183,6 +185,36 @@ * wcserr_enable(). * * +* tabcmp() - Compare two tabprm structs for equality +* -------------------------------------------------- +* tabcmp() compares two tabprm structs for equality. +* +* Given: +* cmp int A bit field controlling the strictness of the +* comparison. At present, this value must always be 0, +* indicating a strict comparison. In the future, other +* options may be added. +* +* tol double Tolerance for comparison of floating-point values. +* For example, for tol == 1e-6, all floating-point +* values in the structs must be equal to the first 6 +* decimal places. A value of 0 implies exact equality. +* +* tab1 const struct tabprm* +* The first tabprm struct to compare. +* +* tab2 const struct tabprm* +* The second tabprm struct to compare. +* +* Returned: +* equal int* Non-zero when the given structs are equal. +* +* Function return value: +* int Status return value: +* 0: Success. +* 1: Null pointer passed. +* +* * tabfree() - Destructor for the tabprm struct * -------------------------------------------- * tabfree() frees memory allocated for the tabprm arrays by tabini(). @@ -192,7 +224,7 @@ * PLEASE NOTE: tabfree() must not be invoked on a tabprm struct that was not * initialized by tabini(). * -* Returned: +* Given and returned: * tab struct tabprm* * Coordinate transformation parameters. * @@ -202,6 +234,60 @@ * 1: Null tabprm pointer passed. * * +* tabsize() - Compute the size of a tabprm struct +* ----------------------------------------------- +* tabsize() computes the full size of a tabprm struct, including allocated +* memory. +* +* Given: +* tab const struct tabprm* +* Tabular transformation parameters. +* +* If NULL, the base size of the struct and the allocated +* size are both set to zero. +* +* Returned: +* sizes int[2] The first element is the base size of the struct as +* returned by sizeof(struct tabprm). The second element +* is the total allocated size, in bytes, assuming that +* the allocation was done by tabini(). This figure +* includes memory allocated for the constituent struct, +* tabprm::err. +* +* It is not an error for the struct not to have been set +* up via tabset(), which normally results in additional +* memory allocation. +* +* Function return value: +* int Status return value: +* 0: Success. +* +* +* tabenq() - enquire about the state of a tabprm struct +* ----------------------------------------------------- +* tabenq() may be used to obtain information about the state of a tabprm +* struct. The function returns a true/false answer for the enquiry asked. +* +* Given: +* tab const struct tabprm* +* Tabular transformation parameters. +* +* enquiry int Enquiry according to the following parameters: +* TABENQ_MEM: memory in the struct is being managed by +* WCSLIB (see tabini()). +* TABENQ_SET: the struct has been set up by tabset(). +* TABENQ_BYP: the struct is in bypass mode (see +* tabset()). +* These may be combined by logical OR, e.g. +* TABENQ_MEM | TABENQ_SET. The enquiry result will be +* the logical AND of the individual results. +* +* Function return value: +* int Enquiry result: +* 0: No. +* 1: Yes. +* +* * tabprt() - Print routine for the tabprm struct * ---------------------------------------------- * tabprt() prints the contents of a tabprm struct using wcsprintf(). Mainly @@ -217,6 +303,25 @@ * 1: Null tabprm pointer passed. * * +* tabperr() - Print error messages from a tabprm struct +* ----------------------------------------------------- +* tabperr() prints the error message(s) (if any) stored in a tabprm struct. +* If there are no errors then nothing is printed. It uses wcserr_prt(), q.v. +* +* Given: +* tab const struct tabprm* +* Tabular transformation parameters. +* +* prefix const char * +* If non-NULL, each output line will be prefixed with +* this string. +* +* Function return value: +* int Status return value: +* 0: Success. +* 1: Null tabprm pointer passed. +* +* * tabset() - Setup routine for the tabprm struct * ----------------------------------------------- * tabset() allocates memory for work arrays in the tabprm struct and sets up @@ -226,6 +331,13 @@ * tabx2s() and tabs2x() if tabprm::flag is anything other than a predefined * magic value. * +* tabset() normally operates regardless of the value of tabprm::flag; i.e. +* even if a struct was previously set up it will be reset unconditionally. +* However, a tabprm struct may be put into "bypass" mode by invoking tabset() +* initially with tabprm::flag == 1 (rather than 0). tabset() will return +* immediately if invoked on a struct in that state. To take a struct out of +* bypass mode, simply reset tabprm::flag to zero. See also tabenq(). +* * Given and returned: * tab struct tabprm* * Tabular transformation parameters. @@ -322,8 +434,8 @@ * internal use only. * * int flag -* (Given and returned) This flag must be set to zero whenever any of the -* following tabprm structure members are set or changed: +* (Given and returned) This flag must be set to zero (or 1, see tabset()) +* whenever any of the following tabprm members are set or changed: * * - tabprm::M (q.v., not normally set by the user), * - tabprm::K (q.v., not normally set by the user), @@ -344,7 +456,7 @@ * int M * (Given or returned) Number of tabular coordinate axes. * -* If tabini() is used to initialize the linprm struct (as would normally +* If tabini() is used to initialize the tabprm struct (as would normally * be the case) then it will set M from the value passed to it as a * function argument. The user should not subsequently modify it. * @@ -353,7 +465,7 @@ * tabprm::M whose elements (K_1, K_2,... K_M) record the lengths of the * axes of the coordinate array and of each indexing vector. * -* If tabini() is used to initialize the linprm struct (as would normally +* If tabini() is used to initialize the tabprm struct (as would normally * be the case) then it will set K from the array passed to it as a * function argument. The user should not subsequently modify it. * @@ -439,7 +551,7 @@ * inverse table lookup function, tabs2x(), to speed up table searches. * * struct wcserr *err -* (Returned) If enabled, when an error status is returned this struct +* (Returned) If enabled, when an error status is returned, this struct * contains detailed information about the error, see wcserr_enable(). * * int m_flag @@ -473,75 +585,80 @@ #ifndef WCSLIB_TAB #define WCSLIB_TAB -#include "wcserr.h" - #ifdef __cplusplus extern "C" { #endif +enum tabenq_enum { + TABENQ_MEM = 1, // tabprm struct memory is managed by WCSLIB. + TABENQ_SET = 2, // tabprm struct has been set up. + TABENQ_BYP = 4, // tabprm struct is in bypass mode. +}; extern const char *tab_errmsg[]; enum tab_errmsg_enum { - TABERR_SUCCESS = 0, /* Success. */ - TABERR_NULL_POINTER = 1, /* Null tabprm pointer passed. */ - TABERR_MEMORY = 2, /* Memory allocation failed. */ - TABERR_BAD_PARAMS = 3, /* Invalid tabular parameters. */ - TABERR_BAD_X = 4, /* One or more of the x coordinates were - invalid. */ - TABERR_BAD_WORLD = 5 /* One or more of the world coordinates were - invalid. */ + TABERR_SUCCESS = 0, // Success. + TABERR_NULL_POINTER = 1, // Null tabprm pointer passed. + TABERR_MEMORY = 2, // Memory allocation failed. + TABERR_BAD_PARAMS = 3, // Invalid tabular parameters. + TABERR_BAD_X = 4, // One or more of the x coordinates were + // invalid. + TABERR_BAD_WORLD = 5 // One or more of the world coordinates were + // invalid. }; struct tabprm { - /* Initialization flag (see the prologue above). */ - /*------------------------------------------------------------------------*/ - int flag; /* Set to zero to force initialization. */ - - /* Parameters to be provided (see the prologue above). */ - /*------------------------------------------------------------------------*/ - int M; /* Number of tabular coordinate axes. */ - int *K; /* Vector of length M whose elements */ - /* (K_1, K_2,... K_M) record the lengths of */ - /* the axes of the coordinate array and of */ - /* each indexing vector. */ - int *map; /* Vector of length M usually such that */ - /* map[m-1] == i-1 for coordinate array */ - /* axis m and image axis i (see above). */ - double *crval; /* Vector of length M containing the index */ - /* value for the reference pixel for each */ - /* of the tabular coordinate axes. */ - double **index; /* Vector of pointers to M indexing vectors */ - /* of lengths (K_1, K_2,... K_M). */ - double *coord; /* (1+M)-dimensional tabular coordinate */ - /* array (see above). */ - - /* Information derived from the parameters supplied. */ - /*------------------------------------------------------------------------*/ - int nc; /* Number of coordinate vectors (of length */ - /* M) in the coordinate array. */ - int padding; /* (Dummy inserted for alignment purposes.) */ - int *sense; /* Vector of M flags that indicate whether */ - /* the Mth indexing vector is monotonic */ - /* increasing, or else decreasing. */ - int *p0; /* Vector of M indices. */ - double *delta; /* Vector of M increments. */ - double *extrema; /* (1+M)-dimensional array of coordinate */ - /* extrema. */ - - /* Error handling */ - /*------------------------------------------------------------------------*/ + // Initialization flag (see the prologue above). + //-------------------------------------------------------------------------- + int flag; // Set to zero to force initialization. + + // Parameters to be provided (see the prologue above). + //-------------------------------------------------------------------------- + int M; // Number of tabular coordinate axes. + int *K; // Vector of length M whose elements + // (K_1, K_2,... K_M) record the lengths of + // the axes of the coordinate array and of + // each indexing vector. + int *map; // Vector of length M usually such that + // map[m-1] == i-1 for coordinate array + // axis m and image axis i (see above). + double *crval; // Vector of length M containing the index + // value for the reference pixel for each + // of the tabular coordinate axes. + double **index; // Vector of pointers to M indexing vectors + // of lengths (K_1, K_2,... K_M). + double *coord; // (1+M)-dimensional tabular coordinate + // array (see above). + + // Information derived from the parameters supplied. + //-------------------------------------------------------------------------- + int nc; // Number of coordinate vectors (of length + // M) in the coordinate array. + int padding; // (Dummy inserted for alignment purposes.) + int *sense; // Vector of M flags that indicate whether + // the Mth indexing vector is monotonic + // increasing, or else decreasing. + int *p0; // Vector of M indices. + double *delta; // Vector of M increments. + double *extrema; // (1+M)-dimensional array of coordinate + // extrema. + + // Error messaging, if enabled. + //-------------------------------------------------------------------------- struct wcserr *err; - /* Private - the remainder are for memory management. */ - /*------------------------------------------------------------------------*/ + //-------------------------------------------------------------------------- + // Private - the remainder are for internal use. + //-------------------------------------------------------------------------- + // Memory management. int m_flag, m_M, m_N; int set_M; int *m_K, *m_map; double *m_crval, **m_index, **m_indxs, *m_coord; }; -/* Size of the tabprm struct in int units, used by the Fortran wrappers. */ +// Size of the tabprm struct in int units, used by the Fortran wrappers. #define TABLEN (sizeof(struct tabprm)/sizeof(int)) @@ -551,10 +668,19 @@ int tabmem(struct tabprm *tab); int tabcpy(int alloc, const struct tabprm *tabsrc, struct tabprm *tabdst); +int tabcmp(int cmp, double tol, const struct tabprm *tab1, + const struct tabprm *tab2, int *equal); + int tabfree(struct tabprm *tab); +int tabsize(const struct tabprm *tab, int size[2]); + +int tabenq(const struct tabprm *tab, int enquiry); + int tabprt(const struct tabprm *tab); +int tabperr(const struct tabprm *tab, const char *prefix); + int tabset(struct tabprm *tab); int tabx2s(struct tabprm *tab, int ncoord, int nelem, const double x[], @@ -564,7 +690,7 @@ int tabs2x(struct tabprm *tab, int ncoord, int nelem, const double world[], double x[], int stat[]); -/* Deprecated. */ +// Deprecated. #define tabini_errmsg tab_errmsg #define tabcpy_errmsg tab_errmsg #define tabfree_errmsg tab_errmsg @@ -577,4 +703,4 @@ int tabs2x(struct tabprm *tab, int ncoord, int nelem, const double world[], } #endif -#endif /* WCSLIB_TAB */ +#endif // WCSLIB_TAB diff --git a/cextern/wcslib/C/wcs.c b/cextern/wcslib/C/wcs.c index 09a484c3818d..7ba66fe20331 100644 --- a/cextern/wcslib/C/wcs.c +++ b/cextern/wcslib/C/wcs.c @@ -1,7 +1,6 @@ /*============================================================================ - - WCSLIB 4.14 - an implementation of the FITS WCS standard. - Copyright (C) 1995-2012, Mark Calabretta + WCSLIB 8.6 - an implementation of the FITS WCS standard. + Copyright (C) 1995-2026, Mark Calabretta This file is part of WCSLIB. @@ -18,14 +17,13 @@ You should have received a copy of the GNU Lesser General Public License along with WCSLIB. If not, see http://www.gnu.org/licenses. - Direct correspondence concerning WCSLIB to mark@calabretta.id.au - Author: Mark Calabretta, Australia Telescope National Facility, CSIRO. - http://www.atnf.csiro.au/people/Mark.Calabretta - $Id: wcs.c,v 4.14 2012/07/13 10:02:27 cal103 Exp $ + http://www.atnf.csiro.au/computing/software/wcs + $Id: wcs.c,v 8.6 2026/03/29 13:53:56 mcalabre Exp $ *===========================================================================*/ #include +#include #include #include #include @@ -36,7 +34,9 @@ #include "wcstrig.h" #include "wcsunits.h" #include "wcsutil.h" +#include "wtbarr.h" #include "lin.h" +#include "dis.h" #include "log.h" #include "spc.h" #include "prj.h" @@ -45,83 +45,156 @@ #include "tab.h" #include "wcs.h" -const int WCSSET = 137; - -/* Maximum number of PVi_ma and PSi_ma keywords. */ +// Maximum number of PVi_ma and PSi_ma keywords. int NPVMAX = 64; int NPSMAX = 8; -/* Map status return value to message. */ +// Map status return value to message. const char *wcs_errmsg[] = { "Success", "Null wcsprm pointer passed", "Memory allocation failed", "Linear transformation matrix is singular", - "Inconsistent or unrecognized coordinate axis types", + "Inconsistent or unrecognized coordinate axis type", "Invalid parameter value", - "Invalid coordinate transformation parameters", - "Ill-conditioned coordinate transformation parameters", + "Unrecognized coordinate transformation parameter", + "Ill-conditioned coordinate transformation parameter", "One or more of the pixel coordinates were invalid", "One or more of the world coordinates were invalid", "Invalid world coordinate", "No solution found in the specified interval", "Invalid subimage specification", - "Non-separable subimage coordinate system"}; + "Non-separable subimage coordinate system", + "wcsprm struct is unset, use wcsset()"}; + +// Map error returns for lower-level routines. +const int wcs_linerr[] = { + WCSERR_SUCCESS, // 0: LINERR_SUCCESS + WCSERR_NULL_POINTER, // 1: LINERR_NULL_POINTER + WCSERR_MEMORY, // 2: LINERR_MEMORY + WCSERR_SINGULAR_MTX, // 3: LINERR_SINGULAR_MTX + WCSERR_BAD_PARAM, // 4: LINERR_DISTORT_INIT + WCSERR_BAD_PIX, // 5: LINERR_DISTORT + WCSERR_BAD_WORLD // 6: LINERR_DEDISTORT +}; + +const int wcs_logerr[] = { + WCSERR_SUCCESS, // 0: LOGERR_SUCCESS + WCSERR_NULL_POINTER, // 1: LOGERR_NULL_POINTER + WCSERR_BAD_PARAM, // 2: LOGERR_BAD_LOG_REF_VAL + WCSERR_BAD_PIX, // 3: LOGERR_BAD_X + WCSERR_BAD_WORLD // 4: LOGERR_BAD_WORLD +}; + +const int wcs_spcerr[] = { + // -1: SPCERR_NO_CHANGE + WCSERR_SUCCESS, // 0: SPCERR_SUCCESS + WCSERR_NULL_POINTER, // 1: SPCERR_NULL_POINTER + WCSERR_BAD_PARAM, // 2: SPCERR_BAD_SPEC_PARAMS + WCSERR_BAD_PIX, // 3: SPCERR_BAD_X + WCSERR_BAD_WORLD // 4: SPCERR_BAD_SPEC +}; + +const int wcs_celerr[] = { + WCSERR_SUCCESS, // 0: CELERR_SUCCESS + WCSERR_NULL_POINTER, // 1: CELERR_NULL_POINTER + WCSERR_BAD_PARAM, // 2: CELERR_BAD_PARAM + WCSERR_BAD_COORD_TRANS, // 3: CELERR_BAD_COORD_TRANS + WCSERR_ILL_COORD_TRANS, // 4: CELERR_ILL_COORD_TRANS + WCSERR_BAD_PIX, // 5: CELERR_BAD_PIX + WCSERR_BAD_WORLD // 6: CELERR_BAD_WORLD +}; + +const int wcs_taberr[] = { + WCSERR_SUCCESS, // 0: TABERR_SUCCESS + WCSERR_NULL_POINTER, // 1: TABERR_NULL_POINTER + WCSERR_MEMORY, // 2: TABERR_MEMORY + WCSERR_BAD_PARAM, // 3: TABERR_BAD_PARAMS + WCSERR_BAD_PIX, // 4: TABERR_BAD_X + WCSERR_BAD_WORLD // 5: TABERR_BAD_WORLD +}; + +static const int WCSSET = 137; + +// Internal helper functions, not for general use. +static int wcs_types(struct wcsprm *); +static int time_type(const char *); +static int time_code(const char *ctype, int nc); +static int wcs_units(struct wcsprm *); +static int wcs_chksum(const struct wcsprm *wcs); +static int wcs_fletcher32(int chksum, const void *data, size_t len); -/* Convenience macro for invoking wcserr_set(). */ +// Convenience macro for invoking wcserr_set(). #define WCS_ERRMSG(status) WCSERR_SET(status), wcs_errmsg[status] #ifndef signbit #define signbit(X) ((X) < 0.0 ? 1 : 0) #endif -/* Internal helper functions, not for general use. */ -static int wcs_types(struct wcsprm *); -static int wcs_units(struct wcsprm *); - -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- int wcsnpv(int npvmax) { if (npvmax >= 0) NPVMAX = npvmax; return NPVMAX; } int wcsnps(int npsmax) { if (npsmax >= 0) NPSMAX = npsmax; return NPSMAX; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- int wcsini(int alloc, int naxis, struct wcsprm *wcs) { - static const char *function = "wcsini"; + return wcsinit(alloc, naxis, wcs, -1, -1, -1); +} + +//---------------------------------------------------------------------------- + +int wcsinit( + int alloc, + int naxis, + struct wcsprm *wcs, + int npvmax, + int npsmax, + int ndpmax) + +{ + static const char *function = "wcsinit"; - int i, j, k, status; - double *cd; - struct wcserr **err; + int status; + // Check inputs. if (wcs == 0x0) return WCSERR_NULL_POINTER; - /* Initialize error message handling. */ - err = &(wcs->err); - if (wcs->flag != -1) { - if (wcs->err) free(wcs->err); - if (wcs->lin.err) free(wcs->lin.err); - if (wcs->cel.err) free(wcs->cel.err); - if (wcs->spc.err) free(wcs->spc.err); - if (wcs->cel.prj.err) free(wcs->cel.prj.err); + if (npvmax < 0) npvmax = wcsnpv(-1); + if (npsmax < 0) npsmax = wcsnps(-1); + + + // Initialize error message handling... + if (wcs->flag == -1) { + wcs->err = 0x0; + } + struct wcserr **err = &(wcs->err); + wcserr_clear(err); + + // ...and also in the contained structs in case we have to return due to + // an error before they can be initialized by their specialized routines, + // since wcsperr() assumes their wcserr pointers are valid. + if (wcs->flag == -1) { + wcs->lin.err = 0x0; + wcs->cel.err = 0x0; + wcs->spc.err = 0x0; } - wcs->err = 0x0; - wcs->lin.err = 0x0; - wcs->cel.err = 0x0; - wcs->spc.err = 0x0; - wcs->cel.prj.err = 0x0; + wcserr_clear(&(wcs->lin.err)); + wcserr_clear(&(wcs->cel.err)); + wcserr_clear(&(wcs->spc.err)); - /* Initialize pointers. */ + // Initialize pointers. if (wcs->flag == -1 || wcs->m_flag != WCSSET) { if (wcs->flag == -1) { + wcs->tab = 0x0; wcs->types = 0x0; wcs->lin.flag = -1; - wcs->tab = 0x0; } - /* Initialize memory management. */ + // Initialize memory management. wcs->m_flag = 0; wcs->m_naxis = 0; wcs->m_crpix = 0x0; @@ -138,6 +211,9 @@ int wcsini(int alloc, int naxis, struct wcsprm *wcs) wcs->m_cname = 0x0; wcs->m_crder = 0x0; wcs->m_csyer = 0x0; + wcs->m_czphs = 0x0; + wcs->m_cperi = 0x0; + wcs->m_aux = 0x0; wcs->m_tab = 0x0; wcs->m_wtb = 0x0; } @@ -148,7 +224,7 @@ int wcsini(int alloc, int naxis, struct wcsprm *wcs) } - /* Allocate memory for arrays if required. */ + // Allocate memory for arrays if required. if (alloc || wcs->crpix == 0x0 || wcs->pc == 0x0 || @@ -156,32 +232,33 @@ int wcsini(int alloc, int naxis, struct wcsprm *wcs) wcs->crval == 0x0 || wcs->cunit == 0x0 || wcs->ctype == 0x0 || - (NPVMAX && wcs->pv == 0x0) || - (NPSMAX && wcs->ps == 0x0) || + (npvmax && wcs->pv == 0x0) || + (npsmax && wcs->ps == 0x0) || wcs->cd == 0x0 || wcs->crota == 0x0 || wcs->colax == 0x0 || wcs->cname == 0x0 || wcs->crder == 0x0 || - wcs->csyer == 0x0) { + wcs->csyer == 0x0 || + wcs->czphs == 0x0 || + wcs->cperi == 0x0) { - /* Was sufficient allocated previously? */ + // Was sufficient allocated previously? if (wcs->m_flag == WCSSET && (wcs->m_naxis < naxis || - wcs->npvmax < NPVMAX || - wcs->npsmax < NPSMAX)) { - /* No, free it. */ + wcs->npvmax < npvmax || + wcs->npsmax < npsmax)) { + // No, free it. wcsfree(wcs); } - if (alloc || wcs->crpix == 0x0) { if (wcs->m_crpix) { - /* In case the caller fiddled with it. */ + // In case the caller fiddled with it. wcs->crpix = wcs->m_crpix; } else { - if (!(wcs->crpix = calloc(naxis, sizeof(double)))) { + if ((wcs->crpix = calloc(naxis, sizeof(double))) == 0x0) { return wcserr_set(WCS_ERRMSG(WCSERR_MEMORY)); } @@ -193,11 +270,11 @@ int wcsini(int alloc, int naxis, struct wcsprm *wcs) if (alloc || wcs->pc == 0x0) { if (wcs->m_pc) { - /* In case the caller fiddled with it. */ + // In case the caller fiddled with it. wcs->pc = wcs->m_pc; } else { - if (!(wcs->pc = calloc(naxis*naxis, sizeof(double)))) { + if ((wcs->pc = calloc(naxis*naxis, sizeof(double))) == 0x0) { wcsfree(wcs); return wcserr_set(WCS_ERRMSG(WCSERR_MEMORY)); } @@ -210,11 +287,11 @@ int wcsini(int alloc, int naxis, struct wcsprm *wcs) if (alloc || wcs->cdelt == 0x0) { if (wcs->m_cdelt) { - /* In case the caller fiddled with it. */ + // In case the caller fiddled with it. wcs->cdelt = wcs->m_cdelt; } else { - if (!(wcs->cdelt = calloc(naxis, sizeof(double)))) { + if ((wcs->cdelt = calloc(naxis, sizeof(double))) == 0x0) { wcsfree(wcs); return wcserr_set(WCS_ERRMSG(WCSERR_MEMORY)); } @@ -227,11 +304,11 @@ int wcsini(int alloc, int naxis, struct wcsprm *wcs) if (alloc || wcs->crval == 0x0) { if (wcs->m_crval) { - /* In case the caller fiddled with it. */ + // In case the caller fiddled with it. wcs->crval = wcs->m_crval; } else { - if (!(wcs->crval = calloc(naxis, sizeof(double)))) { + if ((wcs->crval = calloc(naxis, sizeof(double))) == 0x0) { wcsfree(wcs); return wcserr_set(WCS_ERRMSG(WCSERR_MEMORY)); } @@ -244,11 +321,11 @@ int wcsini(int alloc, int naxis, struct wcsprm *wcs) if (alloc || wcs->cunit == 0x0) { if (wcs->m_cunit) { - /* In case the caller fiddled with it. */ + // In case the caller fiddled with it. wcs->cunit = wcs->m_cunit; } else { - if (!(wcs->cunit = calloc(naxis, sizeof(char [72])))) { + if ((wcs->cunit = calloc(naxis, sizeof(char [72]))) == 0x0) { wcsfree(wcs); return wcserr_set(WCS_ERRMSG(WCSERR_MEMORY)); } @@ -261,11 +338,11 @@ int wcsini(int alloc, int naxis, struct wcsprm *wcs) if (alloc || wcs->ctype == 0x0) { if (wcs->m_ctype) { - /* In case the caller fiddled with it. */ + // In case the caller fiddled with it. wcs->ctype = wcs->m_ctype; } else { - if (!(wcs->ctype = calloc(naxis, sizeof(char [72])))) { + if ((wcs->ctype = calloc(naxis, sizeof(char [72]))) == 0x0) { wcsfree(wcs); return wcserr_set(WCS_ERRMSG(WCSERR_MEMORY)); } @@ -278,20 +355,20 @@ int wcsini(int alloc, int naxis, struct wcsprm *wcs) if (alloc || wcs->pv == 0x0) { if (wcs->m_pv) { - /* In case the caller fiddled with it. */ + // In case the caller fiddled with it. wcs->pv = wcs->m_pv; } else { - if (NPVMAX) { - if (!(wcs->pv = calloc(NPVMAX, sizeof(struct pvcard)))) { + if (npvmax) { + if ((wcs->pv = calloc(npvmax, sizeof(struct pvcard))) == 0x0) { wcsfree(wcs); return wcserr_set(WCS_ERRMSG(WCSERR_MEMORY)); } } else { - wcs->pv = (struct pvcard *)0; + wcs->pv = 0x0; } - wcs->npvmax = NPVMAX; + wcs->npvmax = npvmax; wcs->m_flag = WCSSET; wcs->m_naxis = naxis; @@ -301,20 +378,20 @@ int wcsini(int alloc, int naxis, struct wcsprm *wcs) if (alloc || wcs->ps == 0x0) { if (wcs->m_ps) { - /* In case the caller fiddled with it. */ + // In case the caller fiddled with it. wcs->ps = wcs->m_ps; } else { - if (NPSMAX) { - if (!(wcs->ps = calloc(NPSMAX, sizeof(struct pscard)))) { + if (npsmax) { + if ((wcs->ps = calloc(npsmax, sizeof(struct pscard))) == 0x0) { wcsfree(wcs); return wcserr_set(WCS_ERRMSG(WCSERR_MEMORY)); } } else { - wcs->ps = (struct pscard *)0; + wcs->ps = 0x0; } - wcs->npsmax = NPSMAX; + wcs->npsmax = npsmax; wcs->m_flag = WCSSET; wcs->m_naxis = naxis; @@ -324,11 +401,11 @@ int wcsini(int alloc, int naxis, struct wcsprm *wcs) if (alloc || wcs->cd == 0x0) { if (wcs->m_cd) { - /* In case the caller fiddled with it. */ + // In case the caller fiddled with it. wcs->cd = wcs->m_cd; } else { - if (!(wcs->cd = calloc(naxis*naxis, sizeof(double)))) { + if ((wcs->cd = calloc(naxis*naxis, sizeof(double))) == 0x0) { wcsfree(wcs); return wcserr_set(WCS_ERRMSG(WCSERR_MEMORY)); } @@ -341,11 +418,11 @@ int wcsini(int alloc, int naxis, struct wcsprm *wcs) if (alloc || wcs->crota == 0x0) { if (wcs->m_crota) { - /* In case the caller fiddled with it. */ + // In case the caller fiddled with it. wcs->crota = wcs->m_crota; } else { - if (!(wcs->crota = calloc(naxis, sizeof(double)))) { + if ((wcs->crota = calloc(naxis, sizeof(double))) == 0x0) { wcsfree(wcs); return wcserr_set(WCS_ERRMSG(WCSERR_MEMORY)); } @@ -358,11 +435,11 @@ int wcsini(int alloc, int naxis, struct wcsprm *wcs) if (alloc || wcs->colax == 0x0) { if (wcs->m_colax) { - /* In case the caller fiddled with it. */ + // In case the caller fiddled with it. wcs->colax = wcs->m_colax; } else { - if (!(wcs->colax = calloc(naxis, sizeof(int)))) { + if ((wcs->colax = calloc(naxis, sizeof(int))) == 0x0) { wcsfree(wcs); return wcserr_set(WCS_ERRMSG(WCSERR_MEMORY)); } @@ -375,11 +452,11 @@ int wcsini(int alloc, int naxis, struct wcsprm *wcs) if (alloc || wcs->cname == 0x0) { if (wcs->m_cname) { - /* In case the caller fiddled with it. */ + // In case the caller fiddled with it. wcs->cname = wcs->m_cname; } else { - if (!(wcs->cname = calloc(naxis, sizeof(char [72])))) { + if ((wcs->cname = calloc(naxis, sizeof(char [72]))) == 0x0) { wcsfree(wcs); return wcserr_set(WCS_ERRMSG(WCSERR_MEMORY)); } @@ -392,11 +469,11 @@ int wcsini(int alloc, int naxis, struct wcsprm *wcs) if (alloc || wcs->crder == 0x0) { if (wcs->m_crder) { - /* In case the caller fiddled with it. */ + // In case the caller fiddled with it. wcs->crder = wcs->m_crder; } else { - if (!(wcs->crder = calloc(naxis, sizeof(double)))) { + if ((wcs->crder = calloc(naxis, sizeof(double))) == 0x0) { wcsfree(wcs); return wcserr_set(WCS_ERRMSG(WCSERR_MEMORY)); } @@ -409,11 +486,11 @@ int wcsini(int alloc, int naxis, struct wcsprm *wcs) if (alloc || wcs->csyer == 0x0) { if (wcs->m_csyer) { - /* In case the caller fiddled with it. */ + // In case the caller fiddled with it. wcs->csyer = wcs->m_csyer; } else { - if (!(wcs->csyer = calloc(naxis, sizeof(double)))) { + if ((wcs->csyer = calloc(naxis, sizeof(double))) == 0x0) { wcsfree(wcs); return wcserr_set(WCS_ERRMSG(WCSERR_MEMORY)); } @@ -423,119 +500,239 @@ int wcsini(int alloc, int naxis, struct wcsprm *wcs) wcs->m_csyer = wcs->csyer; } } + + if (alloc || wcs->czphs == 0x0) { + if (wcs->m_czphs) { + // In case the caller fiddled with it. + wcs->czphs = wcs->m_czphs; + + } else { + if ((wcs->czphs = calloc(naxis, sizeof(double))) == 0x0) { + wcsfree(wcs); + return wcserr_set(WCS_ERRMSG(WCSERR_MEMORY)); + } + + wcs->m_flag = WCSSET; + wcs->m_naxis = naxis; + wcs->m_czphs = wcs->czphs; + } + } + + if (alloc || wcs->cperi == 0x0) { + if (wcs->m_cperi) { + // In case the caller fiddled with it. + wcs->cperi = wcs->m_cperi; + + } else { + if ((wcs->cperi = calloc(naxis, sizeof(double))) == 0x0) { + wcsfree(wcs); + return wcserr_set(WCS_ERRMSG(WCSERR_MEMORY)); + } + + wcs->m_flag = WCSSET; + wcs->m_naxis = naxis; + wcs->m_cperi = wcs->cperi; + } + } } - wcs->flag = 0; wcs->naxis = naxis; - - /* Set defaults for the linear transformation. */ - wcs->lin.crpix = wcs->crpix; - wcs->lin.pc = wcs->pc; - wcs->lin.cdelt = wcs->cdelt; - wcs->lin.m_flag = 0; - if ((status = linini(0, naxis, &(wcs->lin)))) { - return wcserr_set(WCS_ERRMSG(status)); + // Set defaults for the linear transformation. + wcs->lin.crpix = wcs->crpix; + wcs->lin.pc = wcs->pc; + wcs->lin.cdelt = wcs->cdelt; + if ((status = lininit(0, naxis, &(wcs->lin), ndpmax))) { + return wcserr_set(WCS_ERRMSG(wcs_linerr[status])); } - /* CRVALia defaults to 0.0. */ - for (i = 0; i < naxis; i++) { + // CRVALia defaults to 0.0. + for (int i = 0; i < naxis; i++) { wcs->crval[i] = 0.0; } - /* CUNITia and CTYPEia are blank by default. */ - for (i = 0; i < naxis; i++) { + // CUNITia and CTYPEia are blank by default. + for (int i = 0; i < naxis; i++) { memset(wcs->cunit[i], 0, 72); memset(wcs->ctype[i], 0, 72); } - /* Set defaults for the celestial transformation parameters. */ + // Set defaults for the celestial transformation parameters. wcs->lonpole = UNDEFINED; wcs->latpole = +90.0; - /* Set defaults for the spectral transformation parameters. */ + // Set defaults for the spectral transformation parameters. wcs->restfrq = 0.0; wcs->restwav = 0.0; - /* Default parameter values. */ + // Default parameter values. wcs->npv = 0; - for (k = 0; k < wcs->npvmax; k++) { + for (int k = 0; k < wcs->npvmax; k++) { wcs->pv[k].i = 0; wcs->pv[k].m = 0; wcs->pv[k].value = 0.0; } wcs->nps = 0; - for (k = 0; k < wcs->npsmax; k++) { + for (int k = 0; k < wcs->npsmax; k++) { wcs->ps[k].i = 0; wcs->ps[k].m = 0; memset(wcs->ps[k].value, 0, 72); } - /* Defaults for alternate linear transformations. */ - cd = wcs->cd; - for (i = 0; i < naxis; i++) { - for (j = 0; j < naxis; j++) { + // Defaults for alternate linear transformations. + double *cd = wcs->cd; + for (int i = 0; i < naxis; i++) { + for (int j = 0; j < naxis; j++) { *(cd++) = 0.0; } } - for (i = 0; i < naxis; i++) { + for (int i = 0; i < naxis; i++) { wcs->crota[i] = 0.0; } wcs->altlin = 0; wcs->velref = 0; - /* Defaults for auxiliary coordinate system information. */ + // Defaults for auxiliary coordinate system information. memset(wcs->alt, 0, 4); wcs->alt[0] = ' '; wcs->colnum = 0; - memset(wcs->wcsname, 0, 72); - for (i = 0; i < naxis; i++) { + for (int i = 0; i < naxis; i++) { wcs->colax[i] = 0; memset(wcs->cname[i], 0, 72); wcs->crder[i] = UNDEFINED; wcs->csyer[i] = UNDEFINED; + wcs->czphs[i] = UNDEFINED; + wcs->cperi[i] = UNDEFINED; } - memset(wcs->radesys, 0, 72); - wcs->equinox = UNDEFINED; - memset(wcs->specsys, 0, 72); - memset(wcs->ssysobs, 0, 72); - wcs->velosys = UNDEFINED; - memset(wcs->ssyssrc, 0, 72); - wcs->zsource = UNDEFINED; - wcs->obsgeo[0] = UNDEFINED; - wcs->obsgeo[1] = UNDEFINED; - wcs->obsgeo[2] = UNDEFINED; + + memset(wcs->wcsname, 0, 72); + + memset(wcs->timesys, 0, 72); + memset(wcs->trefpos, 0, 72); + memset(wcs->trefdir, 0, 72); + memset(wcs->plephem, 0, 72); + + memset(wcs->timeunit, 0, 72); + memset(wcs->dateref, 0, 72); + wcs->mjdref[0] = UNDEFINED; + wcs->mjdref[1] = UNDEFINED; + wcs->timeoffs = UNDEFINED; + memset(wcs->dateobs, 0, 72); + memset(wcs->datebeg, 0, 72); memset(wcs->dateavg, 0, 72); + memset(wcs->dateend, 0, 72); wcs->mjdobs = UNDEFINED; + wcs->mjdbeg = UNDEFINED; wcs->mjdavg = UNDEFINED; + wcs->mjdend = UNDEFINED; + wcs->jepoch = UNDEFINED; + wcs->bepoch = UNDEFINED; + wcs->tstart = UNDEFINED; + wcs->tstop = UNDEFINED; + wcs->xposure = UNDEFINED; + wcs->telapse = UNDEFINED; + + wcs->timsyer = UNDEFINED; + wcs->timrder = UNDEFINED; + wcs->timedel = UNDEFINED; + wcs->timepixr = UNDEFINED; + + wcs->obsgeo[0] = UNDEFINED; + wcs->obsgeo[1] = UNDEFINED; + wcs->obsgeo[2] = UNDEFINED; + wcs->obsgeo[3] = UNDEFINED; + wcs->obsgeo[4] = UNDEFINED; + wcs->obsgeo[5] = UNDEFINED; + memset(wcs->obsorbit, 0, 72); + memset(wcs->radesys, 0, 72); + wcs->equinox = UNDEFINED; + memset(wcs->specsys, 0, 72); + memset(wcs->ssysobs, 0, 72); + wcs->velosys = UNDEFINED; + wcs->zsource = UNDEFINED; + memset(wcs->ssyssrc, 0, 72); + wcs->velangl = UNDEFINED; + + // No additional auxiliary coordinate system information. + wcs->aux = 0x0; + // Tabular parameters. wcs->ntab = 0; wcs->tab = 0x0; wcs->nwtb = 0; wcs->wtb = 0x0; - /* Reset derived values. */ + // Reset derived values. strcpy(wcs->lngtyp, " "); strcpy(wcs->lattyp, " "); wcs->lng = -1; wcs->lat = -1; wcs->spec = -1; + wcs->time = -1; wcs->cubeface = -1; + wcs->chksum = 0; celini(&(wcs->cel)); spcini(&(wcs->spc)); + wcs->flag = 0; + + return 0; +} + +//---------------------------------------------------------------------------- + +int wcsauxi( + int alloc, + struct wcsprm *wcs) + +{ + static const char *function = "wcsauxi"; + + // Check inputs. + if (wcs == 0x0) return WCSERR_NULL_POINTER; + struct wcserr **err = &(wcs->err); + + // Allocate memory if required. + if (alloc || wcs->aux == 0x0) { + if (wcs->m_aux) { + // In case the caller fiddled with it. + wcs->aux = wcs->m_aux; + + } else { + if ((wcs->aux = malloc(sizeof(struct auxprm))) == 0x0) { + return wcserr_set(WCS_ERRMSG(WCSERR_MEMORY)); + } + + wcs->m_aux = wcs->aux; + } + } + + struct auxprm *aux = wcs->aux; + aux->rsun_ref = UNDEFINED; + aux->dsun_obs = UNDEFINED; + aux->crln_obs = UNDEFINED; + aux->hgln_obs = UNDEFINED; + aux->hglt_obs = UNDEFINED; + + aux->a_radius = UNDEFINED; + aux->b_radius = UNDEFINED; + aux->c_radius = UNDEFINED; + aux->blon_obs = UNDEFINED; + aux->blat_obs = UNDEFINED; + aux->bdis_obs = UNDEFINED; + return 0; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- int wcssub( int alloc, @@ -547,26 +744,21 @@ int wcssub( { static const char *function = "wcssub"; - char *c, ctypei[16]; - int axis, cubeface, dealloc, dummy, i, itab, j, k, latitude, longitude, m, - *map = 0x0, msub, naxis, npv, nps, other, spectral, status, stokes; - const double *srcp; - double *dstp; - struct tabprm *tabp; - struct wcserr **err; + const char *pq = "PQ"; + int status; if (wcssrc == 0x0) return WCSERR_NULL_POINTER; - err = &(wcsdst->err); + if (wcsdst == 0x0) return WCSERR_NULL_POINTER; + struct wcserr **err = &(wcsdst->err); + // N.B. we do not rely on the wcsprm struct having been set up. + int naxis; if ((naxis = wcssrc->naxis) <= 0) { return wcserr_set(WCSERR_SET(WCSERR_MEMORY), "naxis must be positive (got %d)", naxis); } - if (!(map = calloc(naxis, sizeof(int)))) { - return wcserr_set(WCS_ERRMSG(WCSERR_MEMORY)); - } - + int dummy; if (nsub == 0x0) { nsub = &dummy; *nsub = naxis; @@ -574,50 +766,63 @@ int wcssub( *nsub = naxis; } + // Allocate enough temporary storage to hold either axes[] xor map[]. + int *itmp; + int ntmp = (*nsub <= naxis) ? naxis : *nsub; + if ((itmp = calloc(ntmp, sizeof(int))) == 0x0) { + return wcserr_set(WCS_ERRMSG(WCSERR_MEMORY)); + } + + int dealloc; if ((dealloc = (axes == 0x0))) { - /* Construct an index array. */ - if (!(axes = calloc(naxis, sizeof(int)))) { - free(map); + // Construct an index array. + if ((axes = calloc(naxis, sizeof(int))) == 0x0) { + free(itmp); return wcserr_set(WCS_ERRMSG(WCSERR_MEMORY)); } - for (i = 0; i < naxis; i++) { + for (int i = 0; i < naxis; i++) { axes[i] = i+1; } } - /* So that we don't try to free an uninitialized pointer on cleanup. */ + // So that we don't try to free uninitialized pointers on cleanup. + wcsdst->m_aux = 0x0; wcsdst->m_tab = 0x0; - msub = 0; - for (j = 0; j < *nsub; j++) { - axis = axes[j]; + int msub = 0; + for (int j = 0; j < *nsub; j++) { + int axis = axes[j]; if (abs(axis) > 0x1000) { - /* Subimage extraction by type. */ - k = abs(axis) & 0xFF; + // Subimage extraction by type. + int k = abs(axis) & 0xFF; - longitude = k & WCSSUB_LONGITUDE; - latitude = k & WCSSUB_LATITUDE; - cubeface = k & WCSSUB_CUBEFACE; - spectral = k & WCSSUB_SPECTRAL; - stokes = k & WCSSUB_STOKES; + int longitude = k & WCSSUB_LONGITUDE; + int latitude = k & WCSSUB_LATITUDE; + int cubeface = k & WCSSUB_CUBEFACE; + int spectral = k & WCSSUB_SPECTRAL; + int stokes = k & WCSSUB_STOKES; + int time = k & WCSSUB_TIME; + int other; if ((other = (axis < 0))) { longitude = !longitude; latitude = !latitude; cubeface = !cubeface; spectral = !spectral; stokes = !stokes; + time = !time; } - for (i = 0; i < naxis; i++) { + for (int i = 0; i < naxis; i++) { + char ctypei[16]; strncpy (ctypei, (char *)(wcssrc->ctype + i), 8); ctypei[8] = '\0'; - /* Find the last non-blank character. */ - c = ctypei + 8; + // Find the last non-blank character. + char *c = ctypei + 8; while (c-- > ctypei) { if (*c == ' ') *c = '\0'; if (*c != '\0') break; @@ -645,11 +850,6 @@ int wcssub( continue; } - } else if (strcmp(ctypei, "CUBEFACE") == 0) { - if (!cubeface) { - continue; - } - } else if (( strncmp(ctypei, "FREQ", 4) == 0 || strncmp(ctypei, "ENER", 4) == 0 || @@ -666,36 +866,48 @@ int wcssub( continue; } + } else if (time_type(ctypei)) { + if (!time) { + continue; + } + } else if (strcmp(ctypei, "STOKES") == 0) { if (!stokes) { continue; } + } else if (strcmp(ctypei, "CUBEFACE") == 0) { + if (!cubeface) { + continue; + } + } else if (!other) { continue; } - /* This axis is wanted, but has it already been added? */ + // This axis is wanted, but has it already been added? + int k; for (k = 0; k < msub; k++) { - if (map[k] == i+1) { + if (itmp[k] == i+1) { break; } } - if (k == msub) map[msub++] = i+1; + if (k == msub) itmp[msub++] = i+1; } } else if (0 < axis && axis <= naxis) { - /* Check that the requested axis has not already been added. */ + // Check that the requested axis has not already been added. + int k; for (k = 0; k < msub; k++) { - if (map[k] == axis) { + if (itmp[k] == axis) { break; } } - if (k == msub) map[msub++] = axis; + if (k == msub) itmp[msub++] = axis; } else if (axis == 0) { - /* Graft on a new axis. */ - map[msub++] = 0; + // Graft on a new axis. + itmp[msub++] = 0; } else { status = wcserr_set(WCS_ERRMSG(WCSERR_BAD_SUBIMAGE)); @@ -704,225 +916,385 @@ int wcssub( } if ((*nsub = msub) == 0) { - status = wcsini(alloc, 0, wcsdst); + // Zero out this struct. + status = wcsinit(alloc, 0, wcsdst, 0, 0, 0); goto cleanup; } - for (i = 0; i < *nsub; i++) { - axes[i] = map[i]; + for (int i = 0; i < *nsub; i++) { + axes[i] = itmp[i]; } - /* Construct the inverse axis map: - axes[i] == j means that output axis i+1 comes from input axis j, - axes[i] == 0 means to create a new axis, - map[i] == j means that input axis i+1 goes to output axis j, - map[i] == 0 means that input axis i+1 is not used. */ - for (i = 0; i < naxis; i++) { + // Construct the inverse axis map (i is 0-relative, j is 1-relative): + // axes[i] == j means that output axis i+1 comes from input axis j, + // axes[i] == 0 means to create a new axis, + // map[i] == j means that input axis i+1 goes to output axis j, + // map[i] == 0 means that input axis i+1 is not used. + int *map = itmp; + for (int i = 0; i < naxis; i++) { map[i] = 0; } - for (i = 0; i < *nsub; i++) { + for (int i = 0; i < *nsub; i++) { if (axes[i] > 0) { map[axes[i]-1] = i+1; } } - /* Check that the subimage coordinate system is separable. */ - if (*nsub < naxis) { - srcp = wcssrc->pc; - for (i = 0; i < naxis; i++) { - for (j = 0; j < naxis; j++) { - if (*(srcp++) == 0.0 || j == i) continue; - if ((map[i] == 0) != (map[j] == 0)) { - status = wcserr_set(WCS_ERRMSG(WCSERR_NON_SEPARABLE)); + // Check that the subimage coordinate system is separable. First check + // non-zero, off-diagonal elements of the linear transformation matrix. + double *dstp; + const double *srcp = wcssrc->pc; + for (int i = 0; i < naxis; i++) { + for (int j = 0; j < naxis; j++) { + if (*(srcp++) == 0.0 || j == i) continue; + + if ((map[i] == 0) != (map[j] == 0)) { + status = wcserr_set(WCSERR_SET(WCSERR_NON_SEPARABLE), + "Non-zero off-diagonal matrix elements excluded from the subimage"); + goto cleanup; + } + } + } + + // Tabular coordinates, if any, will be checked below. + + // Now check for distortions that depend on other axes. As the disprm + // struct may not have been initialized, we must parse the dpkey entries. + int ndpmax = 0; + for (int m = 0; m < 2; m++) { + struct disprm *dissrc; + if (m == 0) { + dissrc = wcssrc->lin.dispre; + } else { + dissrc = wcssrc->lin.disseq; + } + + int ndp = 0; + if (dissrc != 0x0) { + for (int j = 0; j < naxis; j++) { + if (map[j] == 0) continue; + + // Axis numbers in axmap[] are 0-relative. + int axmap[32]; + for (int jhat = 0; jhat < 32; jhat++) { + axmap[jhat] = -1; + } + + int Nhat = 0; + struct dpkey *dpsrc = dissrc->dp; + for (int idp = 0; idp < dissrc->ndp; idp++, dpsrc++) { + // Thorough error checking will be done later by disset(). + if (dpsrc->j != j+1) continue; + if (dpsrc->field[1] != pq[m]) continue; + char *fp; + if ((fp = strchr(dpsrc->field, '.')) == 0x0) continue; + fp++; + + ndp++; + + if (strncmp(fp, "NAXES", 6) == 0) { + Nhat = dpkeyi(dpsrc); + } else if (strncmp(fp, "AXIS.", 5) == 0) { + int jhat; + sscanf(fp+5, "%d", &jhat); + axmap[jhat-1] = dpkeyi(dpsrc) - 1; + } + } + + if (Nhat < 0 || (Nhat == 0 && 1 < ndp) || naxis < Nhat || 32 < Nhat) { + status = wcserr_set(WCSERR_SET(WCSERR_BAD_PARAM), + "NAXES was not set (or bad) for %s distortion on axis %d", + dissrc->dtype[j], j+1); goto cleanup; } + + for (int jhat = 0; jhat < Nhat; jhat++) { + if (axmap[jhat] < 0) { + axmap[jhat] = jhat; + + // Make room for an additional DPja.AXIS.j record. + ndp++; + } + + if (map[axmap[jhat]] == 0) { + // Distortion depends on an axis excluded from the subimage. + status = wcserr_set(WCSERR_SET(WCSERR_NON_SEPARABLE), + "Distortion depends on an axis excluded from the subimage."); + goto cleanup; + } + } } } - } + if (ndpmax < ndp) ndpmax = ndp; + } - /* Initialize the destination. */ - npv = NPVMAX; - nps = NPSMAX; - NPVMAX = 0; - for (k = 0; k < wcssrc->npv; k++) { - i = wcssrc->pv[k].i; + // Number of PVi_ma records in the subimage. + int npvmax = 0; + for (int m = 0; m < wcssrc->npv; m++) { + int i = wcssrc->pv[m].i; if (i == 0 || (i > 0 && map[i-1])) { - NPVMAX++; + npvmax++; } } - NPSMAX = 0; - for (k = 0; k < wcssrc->nps; k++) { - i = wcssrc->ps[k].i; + // Number of PSi_ma records in the subimage. + int npsmax = 0; + for (int m = 0; m < wcssrc->nps; m++) { + int i = wcssrc->ps[m].i; if (i > 0 && map[i-1]) { - NPSMAX++; + npsmax++; } } - status = wcsini(alloc, *nsub, wcsdst); + // Initialize the destination. + status = wcsinit(alloc, *nsub, wcsdst, npvmax, npsmax, ndpmax); + + for (int m = 0; m < 2; m++) { + struct disprm *dissrc, *disdst; + if (m == 0) { + dissrc = wcssrc->lin.dispre; + disdst = wcsdst->lin.dispre; + } else { + dissrc = wcssrc->lin.disseq; + disdst = wcsdst->lin.disseq; + } + + if (dissrc && !disdst) { + if ((disdst = calloc(1, sizeof(struct disprm))) == 0x0) { + return wcserr_set(WCS_ERRMSG(WCSERR_MEMORY)); + } - NPVMAX = npv; - NPSMAX = nps; + // Also inits disdst. + disdst->flag = -1; + lindist(m+1, &(wcsdst->lin), disdst, ndpmax); + } + } if (status) { goto cleanup; } - /* Linear transformation. */ + // Linear transformation. srcp = wcssrc->crpix; dstp = wcsdst->crpix; - for (j = 0; j < *nsub; j++, dstp++) { + for (int j = 0; j < *nsub; j++, dstp++) { if (axes[j] > 0) { - k = axes[j] - 1; + int k = axes[j] - 1; *dstp = *(srcp+k); } } srcp = wcssrc->pc; dstp = wcsdst->pc; - for (i = 0; i < *nsub; i++) { - if (axes[i] > 0) { - for (j = 0; j < *nsub; j++, dstp++) { - if (axes[j] > 0) { - k = (axes[i]-1)*naxis + (axes[j]-1); - *dstp = *(srcp+k); - } + for (int i = 0; i < *nsub; i++) { + for (int j = 0; j < *nsub; j++, dstp++) { + if (axes[i] > 0 && axes[j] > 0) { + int k = (axes[i]-1)*naxis + (axes[j]-1); + *dstp = *(srcp+k); } } } srcp = wcssrc->cdelt; dstp = wcsdst->cdelt; - for (i = 0; i < *nsub; i++, dstp++) { + for (int i = 0; i < *nsub; i++, dstp++) { if (axes[i] > 0) { - k = axes[i] - 1; + int k = axes[i] - 1; *dstp = *(srcp+k); } } - /* Coordinate reference value. */ + // Coordinate reference value. srcp = wcssrc->crval; dstp = wcsdst->crval; - for (i = 0; i < *nsub; i++, dstp++) { + for (int i = 0; i < *nsub; i++, dstp++) { if (axes[i] > 0) { - k = axes[i] - 1; + int k = axes[i] - 1; *dstp = *(srcp+k); } } - /* Coordinate units and type. */ - for (i = 0; i < *nsub; i++) { + // Coordinate units and type. + for (int i = 0; i < *nsub; i++) { if (axes[i] > 0) { - k = axes[i] - 1; + int k = axes[i] - 1; strncpy(wcsdst->cunit[i], wcssrc->cunit[k], 72); strncpy(wcsdst->ctype[i], wcssrc->ctype[k], 72); } } - /* Celestial and spectral transformation parameters. */ + // Celestial and spectral transformation parameters. wcsdst->lonpole = wcssrc->lonpole; wcsdst->latpole = wcssrc->latpole; wcsdst->restfrq = wcssrc->restfrq; wcsdst->restwav = wcssrc->restwav; - /* Parameter values. */ - npv = 0; - for (k = 0; k < wcssrc->npv; k++) { - i = wcssrc->pv[k].i; - if (i == 0 || (i > 0 && map[i-1])) { - /* i == 0 is a special code for the latitude axis. */ - wcsdst->pv[npv] = wcssrc->pv[k]; + // Parameter values. + int npv = 0; + for (int m = 0; m < wcssrc->npv; m++) { + int i = wcssrc->pv[m].i; + if (i == 0) { + // i == 0 is a special code that means "the latitude axis". + wcsdst->pv[npv] = wcssrc->pv[m]; + wcsdst->pv[npv].i = 0; + npv++; + } else if (i > 0 && map[i-1]) { + wcsdst->pv[npv] = wcssrc->pv[m]; wcsdst->pv[npv].i = map[i-1]; npv++; } } wcsdst->npv = npv; - nps = 0; - for (k = 0; k < wcssrc->nps; k++) { - i = wcssrc->ps[k].i; + int nps = 0; + for (int m = 0; m < wcssrc->nps; m++) { + int i = wcssrc->ps[m].i; if (i > 0 && map[i-1]) { - wcsdst->ps[nps] = wcssrc->ps[k]; + wcsdst->ps[nps] = wcssrc->ps[m]; wcsdst->ps[nps].i = map[i-1]; nps++; } } wcsdst->nps = nps; - /* Alternate linear transformations. */ - srcp = wcssrc->cd; - dstp = wcsdst->cd; - for (i = 0; i < *nsub; i++) { - if (axes[i] > 0) { - for (j = 0; j < *nsub; j++, dstp++) { - if (axes[j] > 0) { - k = (axes[i]-1)*naxis + (axes[j]-1); + // Alternate linear transformations. + if (wcssrc->cd) { + srcp = wcssrc->cd; + dstp = wcsdst->cd; + for (int i = 0; i < *nsub; i++) { + for (int j = 0; j < *nsub; j++, dstp++) { + if (axes[i] > 0 && axes[j] > 0) { + int k = (axes[i]-1)*naxis + (axes[j]-1); *dstp = *(srcp+k); + } else if (i == j && wcssrc->altlin & 2) { + // A new axis is being created where CDi_ja was present in the input + // header, so override the default value of 0 set by wcsinit(). + *dstp = 1.0; } } } } - srcp = wcssrc->crota; - dstp = wcsdst->crota; - for (i = 0; i < *nsub; i++, dstp++) { - if (axes[i] > 0) { - k = axes[i] - 1; - *dstp = *(srcp+k); + if (wcssrc->crota) { + srcp = wcssrc->crota; + dstp = wcsdst->crota; + for (int i = 0; i < *nsub; i++, dstp++) { + if (axes[i] > 0) { + int k = axes[i] - 1; + *dstp = *(srcp+k); + } } } wcsdst->altlin = wcssrc->altlin; wcsdst->velref = wcssrc->velref; - /* Auxiliary coordinate system information. */ + // Auxiliary coordinate system information. strncpy(wcsdst->alt, wcssrc->alt, 4); wcsdst->colnum = wcssrc->colnum; - strncpy(wcsdst->wcsname, wcssrc->wcsname, 72); - for (i = 0; i < *nsub; i++) { + for (int i = 0; i < *nsub; i++) { if (axes[i] > 0) { - k = axes[i] - 1; - wcsdst->colax[i] = wcssrc->colax[k]; - strncpy(wcsdst->cname[i], wcssrc->cname[k], 72); - wcsdst->crder[i] = wcssrc->crder[k]; - wcsdst->csyer[i] = wcssrc->csyer[k]; + int k = axes[i] - 1; + if (wcssrc->colax) wcsdst->colax[i] = wcssrc->colax[k]; + if (wcssrc->cname) strncpy(wcsdst->cname[i], wcssrc->cname[k], 72); + if (wcssrc->crder) wcsdst->crder[i] = wcssrc->crder[k]; + if (wcssrc->csyer) wcsdst->csyer[i] = wcssrc->csyer[k]; + if (wcssrc->czphs) wcsdst->czphs[i] = wcssrc->czphs[k]; + if (wcssrc->cperi) wcsdst->cperi[i] = wcssrc->cperi[k]; } } - strncpy(wcsdst->radesys, wcssrc->radesys, 72); - wcsdst->equinox = wcssrc->equinox; + strncpy(wcsdst->wcsname, wcssrc->wcsname, 72); - strncpy(wcsdst->specsys, wcssrc->specsys, 72); - strncpy(wcsdst->ssysobs, wcssrc->ssysobs, 72); - wcsdst->velosys = wcssrc->velosys; - strncpy(wcsdst->ssyssrc, wcssrc->ssyssrc, 72); - wcsdst->zsource = wcssrc->zsource; + strncpy(wcsdst->timesys, wcssrc->timesys, 72); + strncpy(wcsdst->trefpos, wcssrc->trefpos, 72); + strncpy(wcsdst->trefdir, wcssrc->trefdir, 72); + strncpy(wcsdst->plephem, wcssrc->plephem, 72); + + strncpy(wcsdst->timeunit, wcssrc->timeunit, 72); + strncpy(wcsdst->dateref, wcssrc->dateref, 72); + wcsdst->mjdref[0] = wcssrc->mjdref[0]; + wcsdst->mjdref[1] = wcssrc->mjdref[1]; + wcsdst->timeoffs = wcssrc->timeoffs; + + strncpy(wcsdst->dateobs, wcssrc->dateobs, 72); + strncpy(wcsdst->datebeg, wcssrc->datebeg, 72); + strncpy(wcsdst->dateavg, wcssrc->dateavg, 72); + strncpy(wcsdst->dateend, wcssrc->dateend, 72); + + wcsdst->mjdobs = wcssrc->mjdobs; + wcsdst->mjdbeg = wcssrc->mjdbeg; + wcsdst->mjdavg = wcssrc->mjdavg; + wcsdst->mjdend = wcssrc->mjdend; + wcsdst->jepoch = wcssrc->jepoch; + wcsdst->bepoch = wcssrc->bepoch; + wcsdst->tstart = wcssrc->tstart; + wcsdst->tstop = wcssrc->tstop; + wcsdst->xposure = wcssrc->xposure; + wcsdst->telapse = wcssrc->telapse; + + wcsdst->timsyer = wcssrc->timsyer; + wcsdst->timrder = wcssrc->timrder; + wcsdst->timedel = wcssrc->timedel; + wcsdst->timepixr = wcssrc->timepixr; wcsdst->obsgeo[0] = wcssrc->obsgeo[0]; wcsdst->obsgeo[1] = wcssrc->obsgeo[1]; wcsdst->obsgeo[2] = wcssrc->obsgeo[2]; + wcsdst->obsgeo[3] = wcssrc->obsgeo[3]; + wcsdst->obsgeo[4] = wcssrc->obsgeo[4]; + wcsdst->obsgeo[5] = wcssrc->obsgeo[5]; - strncpy(wcsdst->dateobs, wcssrc->dateobs, 72); - strncpy(wcsdst->dateavg, wcssrc->dateavg, 72); - wcsdst->mjdobs = wcssrc->mjdobs; - wcsdst->mjdavg = wcssrc->mjdavg; + strncpy(wcsdst->obsorbit, wcssrc->obsorbit, 72); + strncpy(wcsdst->radesys, wcssrc->radesys, 72); + wcsdst->equinox = wcssrc->equinox; + strncpy(wcsdst->specsys, wcssrc->specsys, 72); + strncpy(wcsdst->ssysobs, wcssrc->ssysobs, 72); + wcsdst->velosys = wcssrc->velosys; + wcsdst->zsource = wcssrc->zsource; + strncpy(wcsdst->ssyssrc, wcssrc->ssyssrc, 72); + wcsdst->velangl = wcssrc->velangl; + + + // Additional auxiliary coordinate system information. + if (wcssrc->aux && !wcsdst->aux) { + if ((wcsdst->aux = calloc(1, sizeof(struct auxprm))) == 0x0) { + status = wcserr_set(WCS_ERRMSG(WCSERR_MEMORY)); + goto cleanup; + } + wcsdst->m_aux = wcsdst->aux; - /* Coordinate lookup tables; only copy what's needed. */ + wcsdst->aux->rsun_ref = wcssrc->aux->rsun_ref; + wcsdst->aux->dsun_obs = wcssrc->aux->dsun_obs; + wcsdst->aux->crln_obs = wcssrc->aux->crln_obs; + wcsdst->aux->hgln_obs = wcssrc->aux->hgln_obs; + wcsdst->aux->hglt_obs = wcssrc->aux->hglt_obs; + + wcsdst->aux->a_radius = wcssrc->aux->a_radius; + wcsdst->aux->b_radius = wcssrc->aux->b_radius; + wcsdst->aux->c_radius = wcssrc->aux->c_radius; + wcsdst->aux->blon_obs = wcssrc->aux->blon_obs; + wcsdst->aux->blat_obs = wcssrc->aux->blat_obs; + wcsdst->aux->bdis_obs = wcssrc->aux->bdis_obs; + } + + + // Coordinate lookup tables; only copy what's needed. wcsdst->ntab = 0; - for (itab = 0; itab < wcssrc->ntab; itab++) { - /* Is this table wanted? */ - for (m = 0; m < wcssrc->tab[itab].M; m++) { - i = wcssrc->tab[itab].map[m]; + for (int itab = 0; itab < wcssrc->ntab; itab++) { + // Is this table wanted? + for (int m = 0; m < wcssrc->tab[itab].M; m++) { + int i = wcssrc->tab[itab].map[m]; - if (map[i-1]) { + if (map[i]) { wcsdst->ntab++; break; } @@ -930,8 +1302,8 @@ int wcssub( } if (wcsdst->ntab) { - /* Allocate memory for tabprm structs. */ - if (!(wcsdst->tab = calloc(wcsdst->ntab, sizeof(struct tabprm)))) { + // Allocate memory for tabprm structs. + if ((wcsdst->tab = calloc(wcsdst->ntab, sizeof(struct tabprm))) == 0x0) { wcsdst->ntab = 0; status = wcserr_set(WCS_ERRMSG(WCSERR_MEMORY)); @@ -941,50 +1313,370 @@ int wcssub( wcsdst->m_tab = wcsdst->tab; } - tabp = wcsdst->tab; - for (itab = 0; itab < wcssrc->ntab; itab++) { - for (m = 0; m < wcssrc->tab[itab].M; m++) { - i = wcssrc->tab[itab].map[m]; + struct tabprm *tab = wcsdst->tab; + for (int itab = 0; itab < wcssrc->ntab; itab++) { + for (int m = 0; m < wcssrc->tab[itab].M; m++) { + int i = wcssrc->tab[itab].map[m]; - if (map[i-1]) { - if ((status = tabcpy(1, wcssrc->tab + itab, tabp))) { - wcserr_set(WCS_ERRMSG(status)); + if (map[i]) { + tab->flag = -1; + if ((status = tabcpy(1, wcssrc->tab + itab, tab))) { + wcserr_set(WCS_ERRMSG(wcs_taberr[status])); goto cleanup; } - tabp++; + // Translate axis numbers in tab->map[]. + for (int m = 0; m < wcssrc->tab[itab].M; m++) { + // Table axis mapping, followed by... + int i = wcssrc->tab[itab].map[m]; + + // ...subimaging axis mapping. + int j = map[i] - 1; + if (j < 0) { + // In general, multi-dimensional tables (i.e. with M > 1) are not + // separable, so if one axis is selected then all must be. + status = wcserr_set(WCSERR_SET(WCSERR_NON_SEPARABLE), + "Table with M>1 depends on axis excluded from the subimage"); + goto cleanup; + } + + tab->map[m] = j; + } + + tab++; break; } } } + // Distortion parameters (in linprm). + for (int m = 0; m < 2; m++) { + struct disprm *dissrc, *disdst; + if (m == 0) { + dissrc = wcssrc->lin.dispre; + disdst = wcsdst->lin.dispre; + } else { + dissrc = wcssrc->lin.disseq; + disdst = wcsdst->lin.disseq; + } + + if (dissrc) { + disdst->naxis = *nsub; + + // Distortion type and maximum distortion (but not total distortion). + for (int j = 0; j < *nsub; j++) { + if (axes[j] > 0) { + int k = axes[j] - 1; + strncpy(disdst->dtype[j], dissrc->dtype[k], 72); + disdst->maxdis[j] = dissrc->maxdis[k]; + } + } + + // DPja or DQia keyvalues. + int ndp = 0; + struct dpkey *dpdst = disdst->dp; + for (int j = 0; j < *nsub; j++) { + if (axes[j] == 0) continue; + + // Determine the axis mapping. + int axmap[32]; + for (int jhat = 0; jhat < 32; jhat++) { + axmap[jhat] = -1; + } + + int Nhat = 0; + struct dpkey *dpsrc = dissrc->dp; + for (int idp = 0; idp < dissrc->ndp; idp++, dpsrc++) { + if (dpsrc->j != axes[j]) continue; + if (dpsrc->field[1] != pq[m]) continue; + char *fp; + if ((fp = strchr(dpsrc->field, '.')) == 0x0) continue; + fp++; + + if (strncmp(fp, "NAXES", 6) == 0) { + Nhat = dpkeyi(dpsrc); + } else if (strncmp(fp, "AXIS.", 5) == 0) { + int jhat; + sscanf(fp+5, "%d", &jhat); + axmap[jhat-1] = dpkeyi(dpsrc) - 1; + } + } + + for (int jhat = 0; jhat < Nhat; jhat++) { + if (axmap[jhat] < 0) { + axmap[jhat] = jhat; + } + } + + // Copy the DPja or DQia keyvalues. + dpsrc = dissrc->dp; + for (int idp = 0; idp < dissrc->ndp; idp++, dpsrc++) { + if (dpsrc->j != axes[j]) continue; + if (dpsrc->field[1] != pq[m]) continue; + char *fp; + if ((fp = strchr(dpsrc->field, '.')) == 0x0) continue; + fp++; + + if (strncmp(fp, "AXIS.", 5) == 0) { + // Skip it, we will create our own later. + continue; + } + + *dpdst = *dpsrc; + char ctmp[16]; + sprintf(ctmp, "%d", j+1); + dpdst->field[2] = ctmp[0]; + dpdst->j = j+1; + + ndp++; + dpdst++; + + if (strncmp(fp, "NAXES", 6) == 0) { + for (int jhat = 0; jhat < Nhat; jhat++) { + strcpy(dpdst->field, dpsrc->field); + dpdst->field[2] = ctmp[0]; + fp = strchr(dpdst->field, '.') + 1; + sprintf(fp, "AXIS.%d", jhat+1); + dpdst->j = j+1; + dpdst->type = 0; + dpdst->value.i = map[axmap[jhat]]; + + ndp++; + dpdst++; + } + } + } + } + + disdst->ndp = ndp; + } + } + + cleanup: - if (map) free(map); + if (itmp) free(itmp); if (dealloc) { free(axes); } - if (status && wcsdst->m_tab) free(wcsdst->m_tab); + if (status && wcsdst->m_aux) { + free(wcsdst->m_aux); + wcsdst->aux = 0x0; + wcsdst->m_aux = 0x0; + } + + if (status && wcsdst->m_tab) { + tabfree(wcsdst->m_tab); + } return status; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- -int wcsfree(struct wcsprm *wcs) +int wcscompare( + int cmp, + double tol, + const struct wcsprm *wcs1, + const struct wcsprm *wcs2, + int *equal) { - int j; + int status; + + if (wcs1 == 0x0) return WCSERR_NULL_POINTER; + if (wcs2 == 0x0) return WCSERR_NULL_POINTER; + if (equal == 0x0) return WCSERR_NULL_POINTER; + + *equal = 0; + + if (wcs1->naxis != wcs2->naxis) { + return 0; + } + + int naxis = wcs1->naxis; + int naxis2 = wcs1->naxis*wcs1->naxis; + + if (cmp & WCSCOMPARE_CRPIX) { + // Don't compare crpix. + } else if (cmp & WCSCOMPARE_TILING) { + for (int i = 0; i < naxis; ++i) { + double diff = wcs1->crpix[i] - wcs2->crpix[i]; + if ((double)(int)(diff) != diff) { + return 0; + } + } + } else { + if (!wcsutil_dblEq(naxis, tol, wcs1->crpix, wcs2->crpix)) { + return 0; + } + } + if (!wcsutil_dblEq(naxis2, tol, wcs1->pc, wcs2->pc) || + !wcsutil_dblEq(naxis, tol, wcs1->cdelt, wcs2->cdelt) || + !wcsutil_dblEq(naxis, tol, wcs1->crval, wcs2->crval) || + !wcsutil_strEq(naxis, wcs1->cunit, wcs2->cunit) || + !wcsutil_strEq(naxis, wcs1->ctype, wcs2->ctype) || + !wcsutil_dblEq(1, tol, &wcs1->lonpole, &wcs2->lonpole) || + !wcsutil_dblEq(1, tol, &wcs1->latpole, &wcs2->latpole) || + !wcsutil_dblEq(1, tol, &wcs1->restfrq, &wcs2->restfrq) || + !wcsutil_dblEq(1, tol, &wcs1->restwav, &wcs2->restwav) || + wcs1->npv != wcs2->npv || + wcs1->nps != wcs2->nps) { + return 0; + } + + // Compare pv cards, which may not be in the same order + for (int i = 0; i < wcs1->npv; ++i) { + int j; + for (j = 0; j < wcs2->npv; ++j) { + if (wcs1->pv[i].i == wcs2->pv[j].i && + wcs1->pv[i].m == wcs2->pv[j].m) { + if (!wcsutil_dblEq(1, tol, &wcs1->pv[i].value, &wcs2->pv[j].value)) { + return 0; + } + break; + } + } + // We didn't find a match, so they are not equal + if (j == wcs2->npv) { + return 0; + } + } + + // Compare ps cards, which may not be in the same order + for (int i = 0; i < wcs1->nps; ++i) { + int j; + for (j = 0; j < wcs2->nps; ++j) { + if (wcs1->ps[i].i == wcs2->ps[j].i && + wcs1->ps[i].m == wcs2->ps[j].m) { + if (strncmp(wcs1->ps[i].value, wcs2->ps[j].value, 72)) { + return 0; + } + break; + } + } + // We didn't find a match, so they are not equal + if (j == wcs2->nps) { + return 0; + } + } + + if (abs(wcs1->flag) != WCSSET || abs(wcs2->flag) != WCSSET) { + if (!wcsutil_dblEq(naxis2, tol, wcs1->cd, wcs2->cd) || + !wcsutil_dblEq(naxis, tol, wcs1->crota, wcs2->crota) || + wcs1->altlin != wcs2->altlin || + wcs1->velref != wcs2->velref) { + return 0; + } + } + + if (!(cmp & WCSCOMPARE_ANCILLARY)) { + if (strncmp(wcs1->alt, wcs2->alt, 4) || + wcs1->colnum != wcs2->colnum || + !wcsutil_intEq(naxis, wcs1->colax, wcs2->colax) || + !wcsutil_strEq(naxis, wcs1->cname, wcs2->cname) || + !wcsutil_dblEq(naxis, tol, wcs1->crder, wcs2->crder) || + !wcsutil_dblEq(naxis, tol, wcs1->csyer, wcs2->csyer) || + !wcsutil_dblEq(naxis, tol, wcs1->czphs, wcs2->czphs) || + !wcsutil_dblEq(naxis, tol, wcs1->cperi, wcs2->cperi) || + strncmp(wcs1->wcsname, wcs2->wcsname, 72) || + strncmp(wcs1->timesys, wcs2->timesys, 72) || + strncmp(wcs1->trefpos, wcs2->trefpos, 72) || + strncmp(wcs1->trefdir, wcs2->trefdir, 72) || + strncmp(wcs1->plephem, wcs2->plephem, 72) || + strncmp(wcs1->timeunit, wcs2->timeunit, 72) || + strncmp(wcs1->dateref, wcs2->dateref, 72) || + !wcsutil_dblEq(2, tol, wcs1->mjdref, wcs2->mjdref) || + !wcsutil_dblEq(1, tol, &wcs1->timeoffs, &wcs2->timeoffs) || + strncmp(wcs1->dateobs, wcs2->dateobs, 72) || + strncmp(wcs1->datebeg, wcs2->datebeg, 72) || + strncmp(wcs1->dateavg, wcs2->dateavg, 72) || + strncmp(wcs1->dateend, wcs2->dateend, 72) || + !wcsutil_dblEq(1, tol, &wcs1->mjdobs, &wcs2->mjdobs) || + !wcsutil_dblEq(1, tol, &wcs1->mjdbeg, &wcs2->mjdbeg) || + !wcsutil_dblEq(1, tol, &wcs1->mjdavg, &wcs2->mjdavg) || + !wcsutil_dblEq(1, tol, &wcs1->mjdend, &wcs2->mjdend) || + !wcsutil_dblEq(1, tol, &wcs1->jepoch, &wcs2->jepoch) || + !wcsutil_dblEq(1, tol, &wcs1->bepoch, &wcs2->bepoch) || + !wcsutil_dblEq(1, tol, &wcs1->tstart, &wcs2->tstart) || + !wcsutil_dblEq(1, tol, &wcs1->tstop, &wcs2->tstop) || + !wcsutil_dblEq(1, tol, &wcs1->xposure, &wcs2->xposure) || + !wcsutil_dblEq(1, tol, &wcs1->telapse, &wcs2->telapse) || + !wcsutil_dblEq(1, tol, &wcs1->timsyer, &wcs2->timsyer) || + !wcsutil_dblEq(1, tol, &wcs1->timrder, &wcs2->timrder) || + !wcsutil_dblEq(1, tol, &wcs1->timedel, &wcs2->timedel) || + !wcsutil_dblEq(1, tol, &wcs1->timepixr, &wcs2->timepixr) || + !wcsutil_dblEq(6, tol, wcs1->obsgeo, wcs2->obsgeo) || + strncmp(wcs1->obsorbit, wcs2->obsorbit, 72) || + strncmp(wcs1->radesys, wcs2->radesys, 72) || + !wcsutil_dblEq(1, tol, &wcs1->equinox, &wcs2->equinox) || + strncmp(wcs1->specsys, wcs2->specsys, 72) || + strncmp(wcs1->ssysobs, wcs2->ssysobs, 72) || + !wcsutil_dblEq(1, tol, &wcs1->velosys, &wcs2->velosys) || + !wcsutil_dblEq(1, tol, &wcs1->zsource, &wcs2->zsource) || + strncmp(wcs1->ssyssrc, wcs2->ssyssrc, 72) || + !wcsutil_dblEq(1, tol, &wcs1->velangl, &wcs2->velangl)) { + return 0; + } + + // Compare additional auxiliary parameters. + if (wcs1->aux && wcs2->aux) { + if (!wcsutil_dblEq(1, tol, &wcs1->aux->rsun_ref, &wcs2->aux->rsun_ref) || + !wcsutil_dblEq(1, tol, &wcs1->aux->dsun_obs, &wcs2->aux->dsun_obs) || + !wcsutil_dblEq(1, tol, &wcs1->aux->crln_obs, &wcs2->aux->crln_obs) || + !wcsutil_dblEq(1, tol, &wcs1->aux->hgln_obs, &wcs2->aux->hgln_obs) || + !wcsutil_dblEq(1, tol, &wcs1->aux->hglt_obs, &wcs2->aux->hglt_obs)) { + return 0; + } + + if (!wcsutil_dblEq(1, tol, &wcs1->aux->a_radius, &wcs2->aux->a_radius) || + !wcsutil_dblEq(1, tol, &wcs1->aux->b_radius, &wcs2->aux->b_radius) || + !wcsutil_dblEq(1, tol, &wcs1->aux->c_radius, &wcs2->aux->c_radius) || + !wcsutil_dblEq(1, tol, &wcs1->aux->blon_obs, &wcs2->aux->blon_obs) || + !wcsutil_dblEq(1, tol, &wcs1->aux->blat_obs, &wcs2->aux->blat_obs) || + !wcsutil_dblEq(1, tol, &wcs1->aux->bdis_obs, &wcs2->aux->bdis_obs)) { + return 0; + } + } else if (wcs1->aux || wcs2->aux) { + return 0; + } + } + + // Compare tabular parameters + if (wcs1->ntab != wcs2->ntab) { + return 0; + } + + for (int i = 0; i < wcs1->ntab; ++i) { + int tab_equal; + if ((status = tabcmp(0, tol, &wcs1->tab[i], &wcs2->tab[i], &tab_equal))) { + return status; + } + if (!tab_equal) { + return 0; + } + } + + *equal = 1; + return 0; +} + +//---------------------------------------------------------------------------- + +int wcsfree(struct wcsprm *wcs) + +{ if (wcs == 0x0) return WCSERR_NULL_POINTER; if (wcs->flag == -1) { wcs->lin.flag = -1; } else { - /* Free memory allocated by wcsini(). */ + // Optionally allocated by wcsinit() for given parameters. if (wcs->m_flag == WCSSET) { + // Start by cleaning the slate. if (wcs->crpix == wcs->m_crpix) wcs->crpix = 0x0; if (wcs->pc == wcs->m_pc) wcs->pc = 0x0; if (wcs->cdelt == wcs->m_cdelt) wcs->cdelt = 0x0; @@ -999,9 +1691,14 @@ int wcsfree(struct wcsprm *wcs) if (wcs->cname == wcs->m_cname) wcs->cname = 0x0; if (wcs->crder == wcs->m_crder) wcs->crder = 0x0; if (wcs->csyer == wcs->m_csyer) wcs->csyer = 0x0; + if (wcs->czphs == wcs->m_czphs) wcs->czphs = 0x0; + if (wcs->cperi == wcs->m_cperi) wcs->cperi = 0x0; + + if (wcs->aux == wcs->m_aux) wcs->aux = 0x0; if (wcs->tab == wcs->m_tab) wcs->tab = 0x0; if (wcs->wtb == wcs->m_wtb) wcs->wtb = 0x0; + // Now release the memory. if (wcs->m_crpix) free(wcs->m_crpix); if (wcs->m_pc) free(wcs->m_pc); if (wcs->m_cdelt) free(wcs->m_cdelt); @@ -1016,11 +1713,16 @@ int wcsfree(struct wcsprm *wcs) if (wcs->m_cname) free(wcs->m_cname); if (wcs->m_crder) free(wcs->m_crder); if (wcs->m_csyer) free(wcs->m_csyer); + if (wcs->m_czphs) free(wcs->m_czphs); + if (wcs->m_cperi) free(wcs->m_cperi); - /* Free memory allocated by wcstab(). */ + // May have been allocated by wcspih() or wcssub(). + if (wcs->m_aux) free(wcs->m_aux); + + // Allocated unconditionally by wcstab(). if (wcs->m_tab) { - for (j = 0; j < wcs->ntab; j++) { - tabfree(wcs->m_tab + j); + for (int itab = 0; itab < wcs->ntab; itab++) { + tabfree(wcs->m_tab + itab); } free(wcs->m_tab); @@ -1028,7 +1730,7 @@ int wcsfree(struct wcsprm *wcs) if (wcs->m_wtb) free(wcs->m_wtb); } - /* Free memory allocated by wcsset(). */ + // Allocated unconditionally by wcsset(). if (wcs->types) free(wcs->types); if (wcs->lin.crpix == wcs->m_crpix) wcs->lin.crpix = 0x0; @@ -1052,6 +1754,10 @@ int wcsfree(struct wcsprm *wcs) wcs->m_cname = 0x0; wcs->m_crder = 0x0; wcs->m_csyer = 0x0; + wcs->m_czphs = 0x0; + wcs->m_cperi = 0x0; + + wcs->m_aux = 0x0; wcs->ntab = 0; wcs->m_tab = 0x0; @@ -1060,83 +1766,375 @@ int wcsfree(struct wcsprm *wcs) wcs->types = 0x0; + linfree(&(wcs->lin)); + celfree(&(wcs->cel)); + spcfree(&(wcs->spc)); + + wcserr_clear(&(wcs->err)); + wcs->flag = 0; - if (wcs->err) { - free(wcs->err); - wcs->err = 0x0; + return 0; +} + +//---------------------------------------------------------------------------- + +int wcstrim(struct wcsprm *wcs) + +{ + if (wcs == 0x0) return WCSERR_NULL_POINTER; + + if (wcs->m_flag != WCSSET) { + // Nothing to do. + return 0; } - linfree(&(wcs->lin)); - celfree(&(wcs->cel)); - spcfree(&(wcs->spc)); + if (abs(wcs->flag) != WCSSET) { + return WCSERR_UNSET; + } + + if (wcs->npv < wcs->npvmax) { + if (wcs->m_pv) { + if (wcs->npv == 0) { + free(wcs->m_pv); + wcs->pv = wcs->m_pv = 0x0; + } else { + size_t size = wcs->npv * sizeof(struct pvcard); + // No error if realloc() fails, it will leave the array untouched. + if ((wcs->pv = wcs->m_pv = realloc(wcs->m_pv, size))) { + wcs->npvmax = wcs->npv; + } + } + } + } + + if (wcs->nps < wcs->npsmax) { + if (wcs->m_ps) { + if (wcs->nps == 0) { + free(wcs->m_ps); + wcs->ps = wcs->m_ps = 0x0; + } else { + size_t size = wcs->nps * sizeof(struct pscard); + // No error if realloc() fails, it will leave the array untouched. + if ((wcs->ps = wcs->m_ps = realloc(wcs->m_ps, size))) { + wcs->npsmax = wcs->nps; + } + } + } + } + + if (!(wcs->altlin & 2)) { + if (wcs->m_cd) { + free(wcs->m_cd); + wcs->cd = wcs->m_cd = 0x0; + } + } + + if (!(wcs->altlin & 4)) { + if (wcs->m_crota) { + free(wcs->m_crota); + wcs->crota = wcs->m_crota = 0x0; + } + } + + if (wcs->colax) { + if (wcsutil_all_ival(wcs->naxis, 0, wcs->colax)) { + free(wcs->m_colax); + wcs->colax = wcs->m_colax = 0x0; + } + } + + if (wcs->cname) { + if (wcsutil_all_sval(wcs->naxis, "", (const char (*)[72])wcs->cname)) { + free(wcs->m_cname); + wcs->cname = wcs->m_cname = 0x0; + } + } + + if (wcs->crder) { + if (wcsutil_all_dval(wcs->naxis, UNDEFINED, wcs->crder)) { + free(wcs->m_crder); + wcs->crder = wcs->m_crder = 0x0; + } + } + + if (wcs->csyer) { + if (wcsutil_all_dval(wcs->naxis, UNDEFINED, wcs->csyer)) { + free(wcs->m_csyer); + wcs->csyer = wcs->m_csyer = 0x0; + } + } + + if (wcs->czphs) { + if (wcsutil_all_dval(wcs->naxis, UNDEFINED, wcs->czphs)) { + free(wcs->m_czphs); + wcs->czphs = wcs->m_czphs = 0x0; + } + } + + if (wcs->cperi) { + if (wcsutil_all_dval(wcs->naxis, UNDEFINED, wcs->cperi)) { + free(wcs->m_cperi); + wcs->cperi = wcs->m_cperi = 0x0; + } + } + + // Reset the struct (to store the new checksum). + int status; + wcs->flag = (wcs->flag == -WCSSET) ? 1 : 0; + if ((status = wcsset(wcs))) return status; return 0; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- -int wcsprt(const struct wcsprm *wcs) +int wcssize(const struct wcsprm *wcs, int sizes[2]) { - int i, j, k; - struct wtbarr *wtbp; + if (wcs == 0x0) { + sizes[0] = sizes[1] = 0; + return 0; + } + + // Base size, in bytes. + sizes[0] = sizeof(struct wcsprm); + + // Total size of allocated memory, in bytes. + sizes[1] = 0; + + int exsizes[2]; + int naxis = wcs->naxis; + + // wcsprm::crpix[]. + sizes[1] += naxis * sizeof(double); + + // wcsprm::pc[]. + sizes[1] += naxis*naxis * sizeof(double); + + // wcsprm::cdelt[]. + sizes[1] += naxis * sizeof(double); + + // wcsprm::crval[]. + sizes[1] += naxis * sizeof(double); + + // wcsprm::cunit[]. + if (wcs->cunit) { + sizes[1] += naxis * sizeof(char [72]); + } + + // wcsprm::ctype[]. + sizes[1] += naxis * sizeof(char [72]); + + // wcsprm::pv[]. + if (wcs->pv) { + sizes[1] += wcs->npvmax * sizeof(struct pvcard); + } + + // wcsprm::ps[]. + if (wcs->ps) { + sizes[1] += wcs->npsmax * sizeof(struct pscard); + } + + // wcsprm::cd[]. + if (wcs->cd) { + sizes[1] += naxis*naxis * sizeof(double); + } + + // wcsprm::crota[]. + if (wcs->crota) { + sizes[1] += naxis * sizeof(double); + } + + // wcsprm::colax[]. + if (wcs->colax) { + sizes[1] += naxis * sizeof(int); + } + + // wcsprm::cname[]. + if (wcs->cname) { + sizes[1] += naxis * sizeof(char [72]); + } + + // wcsprm::crder[]. + if (wcs->crder) { + sizes[1] += naxis * sizeof(double); + } + + // wcsprm::csyer[]. + if (wcs->csyer) { + sizes[1] += naxis * sizeof(double); + } + + // wcsprm::czphs[]. + if (wcs->czphs) { + sizes[1] += naxis * sizeof(double); + } + + // wcsprm::cperi[]. + if (wcs->cperi) { + sizes[1] += naxis * sizeof(double); + } + + // wcsprm::aux. + if (wcs->aux) { + sizes[1] += sizeof(struct auxprm); + } + + // wcsprm::tab. + for (int itab = 0; itab < wcs->ntab; itab++) { + tabsize(wcs->tab + itab, exsizes); + sizes[1] += exsizes[0] + exsizes[1]; + } + + // wcsprm::wtb. + if (wcs->wtb) { + sizes[1] += wcs->nwtb * sizeof(struct wtbarr); + } + // wcsprm::lin. + linsize(&(wcs->lin), exsizes); + sizes[1] += exsizes[1]; + + // wcsprm::err. + wcserr_size(wcs->err, exsizes); + sizes[1] += exsizes[0] + exsizes[1]; + + return 0; +} + +//---------------------------------------------------------------------------- + +int auxsize(const struct auxprm *aux, int sizes[2]) + +{ + if (aux == 0x0) { + sizes[0] = sizes[1] = 0; + return 0; + } + + // Base size, in bytes. + sizes[0] = sizeof(struct auxprm); + + // Total size of allocated memory, in bytes. + sizes[1] = 0; + + return 0; +} + +//---------------------------------------------------------------------------- + +int wcsenq(const struct wcsprm *wcs, int enquiry) + +{ + // Initialize. + if (wcs == 0x0) return WCSERR_NULL_POINTER; + + int answer = 0; + + if (enquiry & WCSENQ_MEM) { + if (wcs->m_flag != WCSSET) return 0; + answer = 1; + } + + if (enquiry & WCSENQ_SET) { + if (abs(wcs->flag) != WCSSET) return 0; + answer = 1; + } + + if (enquiry & WCSENQ_BYP) { + if (wcs->flag != 1 && wcs->flag != -WCSSET) return 0; + answer = 1; + } + + if (enquiry & WCSENQ_CHK) { + if (abs(wcs->flag) != WCSSET) return 0; + if (wcs->chksum != wcs_chksum(wcs)) return 0; + answer = 1; + } + + return answer; +} + +//---------------------------------------------------------------------------- + +static void wcsprt_auxc(const char *name, const char *value) +{ + if (value[0] == '\0') { + wcsprintf(" %s: UNDEFINED\n", name); + } else { + wcsprintf(" %s: \"%s\"\n", name, value); + } +} + +static void wcsprt_auxd(const char *name, double value) +{ + if (undefined(value)) { + wcsprintf(" %s: UNDEFINED\n", name); + } else { + wcsprintf(" %s: %15.9f\n", name, value); + } +} + +int wcsprt(const struct wcsprm *wcs) + +{ if (wcs == 0x0) return WCSERR_NULL_POINTER; - if (wcs->flag != WCSSET) { + if (abs(wcs->flag) != WCSSET) { wcsprintf("The wcsprm struct is UNINITIALIZED.\n"); return 0; } + // Parameters supplied... wcsprintf(" flag: %d\n", wcs->flag); wcsprintf(" naxis: %d\n", wcs->naxis); WCSPRINTF_PTR(" crpix: ", wcs->crpix, "\n"); wcsprintf(" "); - for (i = 0; i < wcs->naxis; i++) { - wcsprintf(" %- 11.5g", wcs->crpix[i]); + for (int i = 0; i < wcs->naxis; i++) { + wcsprintf(" %#- 11.5g", wcs->crpix[i]); } wcsprintf("\n"); - /* Linear transformation. */ - k = 0; + // ...linear transformation. + int k = 0; WCSPRINTF_PTR(" pc: ", wcs->pc, "\n"); - for (i = 0; i < wcs->naxis; i++) { + for (int i = 0; i < wcs->naxis; i++) { wcsprintf(" pc[%d][]:", i); - for (j = 0; j < wcs->naxis; j++) { - wcsprintf(" %- 11.5g", wcs->pc[k++]); + for (int j = 0; j < wcs->naxis; j++) { + wcsprintf(" %#- 11.5g", wcs->pc[k++]); } wcsprintf("\n"); } - /* Coordinate increment at reference point. */ + // ...coordinate increment at reference point. WCSPRINTF_PTR(" cdelt: ", wcs->cdelt, "\n"); wcsprintf(" "); - for (i = 0; i < wcs->naxis; i++) { - wcsprintf(" %- 11.5g", wcs->cdelt[i]); + for (int i = 0; i < wcs->naxis; i++) { + wcsprintf(" %#- 11.5g", wcs->cdelt[i]); } wcsprintf("\n"); - /* Coordinate value at reference point. */ + // ...coordinate value at reference point. WCSPRINTF_PTR(" crval: ", wcs->crval, "\n"); wcsprintf(" "); - for (i = 0; i < wcs->naxis; i++) { - wcsprintf(" %- 11.5g", wcs->crval[i]); + for (int i = 0; i < wcs->naxis; i++) { + wcsprintf(" %#- 11.5g", wcs->crval[i]); } wcsprintf("\n"); - /* Coordinate units and type. */ + // ...coordinate units and type. WCSPRINTF_PTR(" cunit: ", wcs->cunit, "\n"); - for (i = 0; i < wcs->naxis; i++) { + for (int i = 0; i < wcs->naxis; i++) { wcsprintf(" \"%s\"\n", wcs->cunit[i]); } WCSPRINTF_PTR(" ctype: ", wcs->ctype, "\n"); - for (i = 0; i < wcs->naxis; i++) { + for (int i = 0; i < wcs->naxis; i++) { wcsprintf(" \"%s\"\n", wcs->ctype[i]); } - /* Celestial and spectral transformation parameters. */ + // ...celestial and spectral transformation parameters. if (undefined(wcs->lonpole)) { wcsprintf(" lonpole: UNDEFINED\n"); } else { @@ -1146,30 +2144,30 @@ int wcsprt(const struct wcsprm *wcs) wcsprintf(" restfrq: %f\n", wcs->restfrq); wcsprintf(" restwav: %f\n", wcs->restwav); - /* Parameter values. */ + // ...parameter values. wcsprintf(" npv: %d\n", wcs->npv); wcsprintf(" npvmax: %d\n", wcs->npvmax); WCSPRINTF_PTR(" pv: ", wcs->pv, "\n"); - for (i = 0; i < wcs->npv; i++) { - wcsprintf(" %3d%4d %- 11.5g\n", (wcs->pv[i]).i, - (wcs->pv[i]).m, (wcs->pv[i]).value); + for (int k = 0; k < wcs->npv; k++) { + wcsprintf(" %3d%4d %#- 11.5g\n", (wcs->pv[k]).i, + (wcs->pv[k]).m, (wcs->pv[k]).value); } wcsprintf(" nps: %d\n", wcs->nps); wcsprintf(" npsmax: %d\n", wcs->npsmax); WCSPRINTF_PTR(" ps: ", wcs->ps, "\n"); - for (i = 0; i < wcs->nps; i++) { - wcsprintf(" %3d%4d %s\n", (wcs->ps[i]).i, - (wcs->ps[i]).m, (wcs->ps[i]).value); + for (int k = 0; k < wcs->nps; k++) { + wcsprintf(" %3d%4d %s\n", (wcs->ps[k]).i, + (wcs->ps[k]).m, (wcs->ps[k]).value); } - /* Alternate linear transformations. */ + // ...alternate linear transformations. k = 0; WCSPRINTF_PTR(" cd: ", wcs->cd, "\n"); if (wcs->cd) { - for (i = 0; i < wcs->naxis; i++) { + for (int i = 0; i < wcs->naxis; i++) { wcsprintf(" cd[%d][]:", i); - for (j = 0; j < wcs->naxis; j++) { - wcsprintf(" %- 11.5g", wcs->cd[k++]); + for (int j = 0; j < wcs->naxis; j++) { + wcsprintf(" %#- 11.5g", wcs->cd[k++]); } wcsprintf("\n"); } @@ -1178,8 +2176,8 @@ int wcsprt(const struct wcsprm *wcs) WCSPRINTF_PTR(" crota: ", wcs->crota, "\n"); if (wcs->crota) { wcsprintf(" "); - for (i = 0; i < wcs->naxis; i++) { - wcsprintf(" %- 11.5g", wcs->crota[i]); + for (int i = 0; i < wcs->naxis; i++) { + wcsprintf(" %#- 11.5g", wcs->crota[i]); } wcsprintf("\n"); } @@ -1189,28 +2187,22 @@ int wcsprt(const struct wcsprm *wcs) - /* Auxiliary coordinate system information. */ + // ...auxiliary coordinate system information. wcsprintf(" alt: '%c'\n", wcs->alt[0]); wcsprintf(" colnum: %d\n", wcs->colnum); WCSPRINTF_PTR(" colax: ", wcs->colax, "\n"); if (wcs->colax) { wcsprintf(" "); - for (i = 0; i < wcs->naxis; i++) { + for (int i = 0; i < wcs->naxis; i++) { wcsprintf(" %5d", wcs->colax[i]); } wcsprintf("\n"); } - if (wcs->wcsname[0] == '\0') { - wcsprintf(" wcsname: UNDEFINED\n"); - } else { - wcsprintf(" wcsname: \"%s\"\n", wcs->wcsname); - } - WCSPRINTF_PTR(" cname: ", wcs->cname, "\n"); if (wcs->cname) { - for (i = 0; i < wcs->naxis; i++) { + for (int i = 0; i < wcs->naxis; i++) { if (wcs->cname[i][0] == '\0') { wcsprintf(" UNDEFINED\n"); } else { @@ -1222,11 +2214,11 @@ int wcsprt(const struct wcsprm *wcs) WCSPRINTF_PTR(" crder: ", wcs->crder, "\n"); if (wcs->crder) { wcsprintf(" "); - for (i = 0; i < wcs->naxis; i++) { + for (int i = 0; i < wcs->naxis; i++) { if (undefined(wcs->crder[i])) { - wcsprintf(" UNDEFINED "); + wcsprintf(" UNDEFINED"); } else { - wcsprintf(" %- 11.5g", wcs->crder[i]); + wcsprintf(" %#- 11.5g", wcs->crder[i]); } } wcsprintf("\n"); @@ -1235,90 +2227,125 @@ int wcsprt(const struct wcsprm *wcs) WCSPRINTF_PTR(" csyer: ", wcs->csyer, "\n"); if (wcs->csyer) { wcsprintf(" "); - for (i = 0; i < wcs->naxis; i++) { + for (int i = 0; i < wcs->naxis; i++) { if (undefined(wcs->csyer[i])) { - wcsprintf(" UNDEFINED "); + wcsprintf(" UNDEFINED"); } else { - wcsprintf(" %- 11.5g", wcs->csyer[i]); + wcsprintf(" %#- 11.5g", wcs->csyer[i]); } } wcsprintf("\n"); } - if (wcs->radesys[0] == '\0') { - wcsprintf(" radesys: UNDEFINED\n"); - } else { - wcsprintf(" radesys: \"%s\"\n", wcs->radesys); - } - - if (undefined(wcs->equinox)) { - wcsprintf(" equinox: UNDEFINED\n"); - } else { - wcsprintf(" equinox: %9f\n", wcs->equinox); - } - - if (wcs->specsys[0] == '\0') { - wcsprintf(" specsys: UNDEFINED\n"); - } else { - wcsprintf(" specsys: \"%s\"\n", wcs->specsys); - } - - if (wcs->ssysobs[0] == '\0') { - wcsprintf(" ssysobs: UNDEFINED\n"); - } else { - wcsprintf(" ssysobs: \"%s\"\n", wcs->ssysobs); - } - - if (undefined(wcs->velosys)) { - wcsprintf(" velosys: UNDEFINED\n"); - } else { - wcsprintf(" velosys: %9f\n", wcs->velosys); - } - - if (wcs->ssyssrc[0] == '\0') { - wcsprintf(" ssyssrc: UNDEFINED\n"); - } else { - wcsprintf(" ssyssrc: \"%s\"\n", wcs->ssyssrc); + WCSPRINTF_PTR(" czphs: ", wcs->czphs, "\n"); + if (wcs->czphs) { + wcsprintf(" "); + for (int i = 0; i < wcs->naxis; i++) { + if (undefined(wcs->czphs[i])) { + wcsprintf(" UNDEFINED"); + } else { + wcsprintf(" %#- 11.5g", wcs->czphs[i]); + } + } + wcsprintf("\n"); } - if (undefined(wcs->zsource)) { - wcsprintf(" zsource: UNDEFINED\n"); - } else { - wcsprintf(" zsource: %9f\n", wcs->zsource); + WCSPRINTF_PTR(" cperi: ", wcs->cperi, "\n"); + if (wcs->cperi) { + wcsprintf(" "); + for (int i = 0; i < wcs->naxis; i++) { + if (undefined(wcs->cperi[i])) { + wcsprintf(" UNDEFINED"); + } else { + wcsprintf(" %#- 11.5g", wcs->cperi[i]); + } + } + wcsprintf("\n"); } - wcsprintf(" obsgeo: "); - for (i = 0; i < 3; i++) { - if (undefined(wcs->obsgeo[i])) { - wcsprintf("UNDEFINED "); + wcsprt_auxc(" wcsname", wcs->wcsname); + + wcsprt_auxc(" timesys", wcs->timesys); + wcsprt_auxc(" trefpos", wcs->trefpos); + wcsprt_auxc(" trefdir", wcs->trefdir); + wcsprt_auxc(" plephem", wcs->plephem); + wcsprt_auxc("timeunit", wcs->timeunit); + wcsprt_auxc(" dateref", wcs->dateref); + wcsprintf(" mjdref: "); + for (int k = 0; k < 2; k++) { + if (undefined(wcs->mjdref[k])) { + wcsprintf(" UNDEFINED"); } else { - wcsprintf("%- 11.5g ", wcs->obsgeo[i]); + wcsprintf(" %15.9f", wcs->mjdref[k]); } } wcsprintf("\n"); + wcsprt_auxd("timeoffs", wcs->timeoffs); + + wcsprt_auxc(" dateobs", wcs->dateobs); + wcsprt_auxc(" datebeg", wcs->datebeg); + wcsprt_auxc(" dateavg", wcs->dateavg); + wcsprt_auxc(" dateend", wcs->dateend); + wcsprt_auxd(" mjdobs", wcs->mjdobs); + wcsprt_auxd(" mjdbeg", wcs->mjdbeg); + wcsprt_auxd(" mjdavg", wcs->mjdavg); + wcsprt_auxd(" mjdend", wcs->mjdend); + wcsprt_auxd(" jepoch", wcs->jepoch); + wcsprt_auxd(" bepoch", wcs->bepoch); + wcsprt_auxd(" tstart", wcs->tstart); + wcsprt_auxd(" tstop", wcs->tstop); + wcsprt_auxd(" xposure", wcs->xposure); + wcsprt_auxd(" telapse", wcs->telapse); + + + wcsprt_auxd(" timsyer", wcs->timsyer); + wcsprt_auxd(" timrder", wcs->timrder); + wcsprt_auxd(" timedel", wcs->timedel); + wcsprt_auxd("timepixr", wcs->timepixr); - if (wcs->dateobs[0] == '\0') { - wcsprintf(" dateobs: UNDEFINED\n"); - } else { - wcsprintf(" dateobs: \"%s\"\n", wcs->dateobs); - } - - if (wcs->dateavg[0] == '\0') { - wcsprintf(" dateavg: UNDEFINED\n"); - } else { - wcsprintf(" dateavg: \"%s\"\n", wcs->dateavg); + wcsprintf(" obsgeo: "); + for (int k = 0; k < 3; k++) { + if (undefined(wcs->obsgeo[k])) { + wcsprintf(" UNDEFINED"); + } else { + wcsprintf(" %15.6f", wcs->obsgeo[k]); + } } - - if (undefined(wcs->mjdobs)) { - wcsprintf(" mjdobs: UNDEFINED\n"); - } else { - wcsprintf(" mjdobs: %9f\n", wcs->mjdobs); + wcsprintf("\n "); + for (int k = 3; k < 6; k++) { + if (undefined(wcs->obsgeo[k])) { + wcsprintf(" UNDEFINED"); + } else { + wcsprintf(" %15.6f", wcs->obsgeo[k]); + } } + wcsprintf("\n"); - if (undefined(wcs->mjdavg)) { - wcsprintf(" mjdavg: UNDEFINED\n"); - } else { - wcsprintf(" mjdavg: %9f\n", wcs->mjdavg); + wcsprt_auxc("obsorbit", wcs->obsorbit); + wcsprt_auxc(" radesys", wcs->radesys); + wcsprt_auxd(" equinox", wcs->equinox); + wcsprt_auxc(" specsys", wcs->specsys); + wcsprt_auxc(" ssysobs", wcs->ssysobs); + wcsprt_auxd(" velosys", wcs->velosys); + wcsprt_auxd(" zsource", wcs->zsource); + wcsprt_auxc(" ssyssrc", wcs->ssyssrc); + wcsprt_auxd(" velangl", wcs->velangl); + + // ...additional auxiliary coordinate system information. + WCSPRINTF_PTR(" aux: ", wcs->aux, "\n"); + if (wcs->aux) { + wcsprt_auxd("rsun_ref", wcs->aux->rsun_ref); + wcsprt_auxd("dsun_obs", wcs->aux->dsun_obs); + wcsprt_auxd("crln_obs", wcs->aux->crln_obs); + wcsprt_auxd("hgln_obs", wcs->aux->hgln_obs); + wcsprt_auxd("hglt_obs", wcs->aux->hglt_obs); + + wcsprt_auxd("a_radius", wcs->aux->a_radius); + wcsprt_auxd("b_radius", wcs->aux->b_radius); + wcsprt_auxd("c_radius", wcs->aux->c_radius); + wcsprt_auxd("blon_obs", wcs->aux->blon_obs); + wcsprt_auxd("blat_obs", wcs->aux->blat_obs); + wcsprt_auxd("bdis_obs", wcs->aux->bdis_obs); } wcsprintf(" ntab: %d\n", wcs->ntab); @@ -1330,30 +2357,34 @@ int wcsprt(const struct wcsprm *wcs) if (wcs->wtb != 0x0) wcsprintf(" (see below)"); wcsprintf("\n"); - /* Derived values. */ - WCSPRINTF_PTR(" types: ", wcs->types, "\n "); - for (i = 0; i < wcs->naxis; i++) { - wcsprintf("%5d", wcs->types[i]); - } - wcsprintf("\n"); - + // Derived values. wcsprintf(" lngtyp: \"%s\"\n", wcs->lngtyp); wcsprintf(" lattyp: \"%s\"\n", wcs->lattyp); wcsprintf(" lng: %d\n", wcs->lng); wcsprintf(" lat: %d\n", wcs->lat); wcsprintf(" spec: %d\n", wcs->spec); + wcsprintf(" time: %d\n", wcs->time); wcsprintf(" cubeface: %d\n", wcs->cubeface); + wcsprintf(" chksum:%12d\n", wcs->chksum); - WCSPRINTF_PTR(" err: ", wcs->err, "\n"); - if (wcs->err) { - wcserr_prt(wcs->err, " "); + WCSPRINTF_PTR(" types: ", wcs->types, "\n "); + for (int i = 0; i < wcs->naxis; i++) { + wcsprintf("%5d", wcs->types[i]); } + wcsprintf("\n"); + // Contained structs. wcsprintf(" lin: (see below)\n"); wcsprintf(" cel: (see below)\n"); wcsprintf(" spc: (see below)\n"); - /* Memory management. */ + // Error handling. + WCSPRINTF_PTR(" err: ", wcs->err, "\n"); + if (wcs->err) { + wcserr_prt(wcs->err, " "); + } + + // Memory management. wcsprintf(" m_flag: %d\n", wcs->m_flag); wcsprintf(" m_naxis: %d\n", wcs->m_naxis); WCSPRINTF_PTR(" m_crpix: ", wcs->m_crpix, ""); @@ -1399,6 +2430,15 @@ int wcsprt(const struct wcsprm *wcs) WCSPRINTF_PTR(" m_csyer: ", wcs->m_csyer, ""); if (wcs->m_csyer == wcs->csyer) wcsprintf(" (= csyer)"); wcsprintf("\n"); + WCSPRINTF_PTR(" m_czphs: ", wcs->m_czphs, ""); + if (wcs->m_czphs == wcs->czphs) wcsprintf(" (= czphs)"); + wcsprintf("\n"); + WCSPRINTF_PTR(" m_cperi: ", wcs->m_cperi, ""); + if (wcs->m_cperi == wcs->cperi) wcsprintf(" (= cperi)"); + wcsprintf("\n"); + WCSPRINTF_PTR(" m_aux: ", wcs->m_aux, ""); + if (wcs->m_aux == wcs->aux) wcsprintf(" (= aux)"); + wcsprintf("\n"); WCSPRINTF_PTR(" m_tab: ", wcs->m_tab, ""); if (wcs->m_tab == wcs->tab) wcsprintf(" (= tab)"); wcsprintf("\n"); @@ -1406,11 +2446,12 @@ int wcsprt(const struct wcsprm *wcs) if (wcs->m_wtb == wcs->wtb) wcsprintf(" (= wtb)"); wcsprintf("\n"); - /* Tabular transformation parameters. */ - if ((wtbp = wcs->wtb)) { - for (j = 0; j < wcs->nwtb; j++, wtbp++) { + // Tabular transformation parameters. + struct wtbarr *wtbp = wcs->wtb; + if (wtbp) { + for (int iwtb = 0; iwtb < wcs->nwtb; iwtb++, wtbp++) { wcsprintf("\n"); - wcsprintf("wtb[%d].*\n", j); + wcsprintf("wtb[%d].*\n", iwtb); wcsprintf(" i: %d\n", wtbp->i); wcsprintf(" m: %d\n", wtbp->m); wcsprintf(" kind: %c\n", wtbp->kind); @@ -1427,24 +2468,24 @@ int wcsprt(const struct wcsprm *wcs) } if (wcs->tab) { - for (j = 0; j < wcs->ntab; j++) { + for (int itab = 0; itab < wcs->ntab; itab++) { wcsprintf("\n"); - wcsprintf("tab[%d].*\n", j); - tabprt(wcs->tab + j); + wcsprintf("tab[%d].*\n", itab); + tabprt(wcs->tab + itab); } } - /* Linear transformation parameters. */ + // Linear transformation parameters. wcsprintf("\n"); wcsprintf(" lin.*\n"); linprt(&(wcs->lin)); - /* Celestial transformation parameters. */ + // Celestial transformation parameters. wcsprintf("\n"); wcsprintf(" cel.*\n"); celprt(&(wcs->cel)); - /* Spectral transformation parameters. */ + // Spectral transformation parameters. wcsprintf("\n"); wcsprintf(" spc.*\n"); spcprt(&(wcs->spc)); @@ -1452,23 +2493,20 @@ int wcsprt(const struct wcsprm *wcs) return 0; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- int wcsperr(const struct wcsprm *wcs, const char *prefix) { - int j; - if (wcs == 0x0) return WCSERR_NULL_POINTER; - if (!wcserr_prt(wcs->err, prefix)) { - wcserr_prt(wcs->lin.err, prefix); - wcserr_prt(wcs->cel.err, prefix); - wcserr_prt(wcs->cel.prj.err, prefix); + if (wcs->err && wcserr_prt(wcs->err, prefix) == 0) { + linperr(&(wcs->lin), prefix); + celperr(&(wcs->cel), prefix); wcserr_prt(wcs->spc.err, prefix); if (wcs->tab) { - for (j = 0; j < wcs->ntab; j++) { - wcserr_prt((wcs->tab + j)->err, prefix); + for (int itab = 0; itab < wcs->ntab; itab++) { + wcserr_prt((wcs->tab + itab)->err, prefix); } } } @@ -1476,65 +2514,179 @@ int wcsperr(const struct wcsprm *wcs, const char *prefix) return 0; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- -int wcsset(struct wcsprm *wcs) +int wcsbchk(struct wcsprm *wcs, int bounds) { - static const char *function = "wcsset"; + if (wcs == 0x0) return WCSERR_NULL_POINTER; - char scode[4], stype[5]; - int i, j, k, m, naxis, status; - double lambda, rho; - double *cd, *pc; - struct celprm *wcscel = &(wcs->cel); - struct prjprm *wcsprj = &(wcscel->prj); - struct spcprm *wcsspc = &(wcs->spc); - struct wcserr **err; + if (abs(wcs->flag) != WCSSET) { + int status; + if ((status = wcsset(wcs))) return status; + } + + wcs->cel.prj.bounds = bounds; + + return 0; +} + +//---------------------------------------------------------------------------- + +int wcsset(struct wcsprm *wcs) +{ + static const char *function = "wcsset"; if (wcs == 0x0) return WCSERR_NULL_POINTER; - err = &(wcs->err); + if (wcs->flag == -WCSSET) return 0; + struct wcserr **err = &(wcs->err); - /* Determine axis types from CTYPEia. */ + // Determine axis types from CTYPEia. + int status; if ((status = wcs_types(wcs))) { return status; } - /* Convert to canonical units. */ + // Convert to canonical units. if ((status = wcs_units(wcs))) { return status; } + int naxis = wcs->naxis; + if (32 < naxis) { + return wcserr_set(WCSERR_SET(WCSERR_BAD_PARAM), + "naxis must not exceed 32 (got %d)", naxis); + } + - /* Non-linear celestial axes present? */ + // Non-linear celestial axes present? if (wcs->lng >= 0 && wcs->types[wcs->lng] == 2200) { + struct celprm *wcscel = &(wcs->cel); celini(wcscel); - /* CRVALia, LONPOLEa, and LATPOLEa keyvalues. */ + // CRVALia, LONPOLEa, and LATPOLEa keyvalues. wcscel->ref[0] = wcs->crval[wcs->lng]; wcscel->ref[1] = wcs->crval[wcs->lat]; wcscel->ref[2] = wcs->lonpole; wcscel->ref[3] = wcs->latpole; - /* PVi_ma keyvalues. */ - for (k = 0; k < wcs->npv; k++) { - i = wcs->pv[k].i - 1; - m = wcs->pv[k].m; + // Do alias translation for TPU/TPV before dealing with PVi_ma. + struct prjprm *wcsprj = &(wcscel->prj); + strncpy(wcsprj->code, wcs->ctype[wcs->lng]+5, 3); + wcsprj->code[3] = '\0'; + if (strncmp(wcsprj->code, "TPU", 3) == 0 || + strncmp(wcsprj->code, "TPV", 3) == 0) { + // Translate the PV parameters. + struct disprm *dis; + if ((dis = calloc(1, sizeof(struct disprm))) == 0x0) { + return wcserr_set(WCS_ERRMSG(WCSERR_MEMORY)); + } + + int ndpmax = 6 + wcs->npv; + + // Attach it to linprm. Also inits it. + char dpq[16]; + struct linprm *wcslin = &(wcs->lin); + dis->flag = -1; + if (strncmp(wcsprj->code, "TPU", 3) == 0) { + // Prior distortion. + lindist(1, wcslin, dis, ndpmax); + strcpy(dpq, "DP"); + } else { + // Sequent distortion. + lindist(2, wcslin, dis, ndpmax); + strcpy(dpq, "DQ"); + } + + // Yes, the distortion type is "TPV" even for TPU. + strcpy(dis->dtype[wcs->lng], "TPV"); + strcpy(dis->dtype[wcs->lat], "TPV"); + + // Keep the keywords in axis-order to aid debugging. + struct dpkey *keyp = dis->dp; + dis->ndp = 0; + + sprintf(dpq+2, "%d", wcs->lng+1); + dpfill(keyp++, dpq, "NAXES", 0, 0, 2, 0.0); + dpfill(keyp++, dpq, "AXIS.1", 0, 0, 1, 0.0); + dpfill(keyp++, dpq, "AXIS.2", 0, 0, 2, 0.0); + dis->ndp += 3; + + // Copy distortion parameters for the longitude axis. + for (int k = 0; k < wcs->npv; k++) { + if (wcs->pv[k].i != wcs->lng+1) continue; + sprintf(keyp->field, "%s.TPV.%d", dpq, wcs->pv[k].m); + dpfill(keyp++, 0x0, 0x0, 0, 1, 0, wcs->pv[k].value); + dis->ndp++; + } + + // Now the latitude axis. + sprintf(dpq+2, "%d", wcs->lat+1); + dpfill(keyp++, dpq, "NAXES", 0, 0, 2, 0.0); + dpfill(keyp++, dpq, "AXIS.1", 0, 0, 2, 0.0); + dpfill(keyp++, dpq, "AXIS.2", 0, 0, 1, 0.0); + dis->ndp += 3; + + for (int k = 0; k < wcs->npv; k++) { + if (wcs->pv[k].i != wcs->lat+1) continue; + sprintf(keyp->field, "%s.TPV.%d", dpq, wcs->pv[k].m); + dpfill(keyp++, 0x0, 0x0, 0, 1, 0, wcs->pv[k].value); + dis->ndp++; + } + + // Erase PVi_ma associated with the celestial axes. + int n = 0; + for (int k = 0; k < wcs->npv; k++) { + int i = wcs->pv[k].i - 1; + if (i == wcs->lng || i == wcs->lat) continue; + + wcs->pv[n].i = wcs->pv[k].i; + wcs->pv[n].m = wcs->pv[k].m; + wcs->pv[n].value = wcs->pv[k].value; + + n++; + } + + wcs->npv = n; + strcpy(wcsprj->code, "TAN"); + + // As the PVi_ma have now been erased, ctype must be reset to prevent + // this translation from re-occurring if wcsset() is called again. + strcpy(wcs->ctype[wcs->lng]+5, "TAN"); + strcpy(wcs->ctype[wcs->lat]+5, "TAN"); + + } else if (strncmp(wcsprj->code, "TNX", 3) == 0) { + // The WAT distortion should already have been encoded in disseq. + strcpy(wcsprj->code, "TAN"); + strcpy(wcs->ctype[wcs->lng]+5, "TAN"); + strcpy(wcs->ctype[wcs->lat]+5, "TAN"); + + } else if (strncmp(wcsprj->code, "ZPX", 3) == 0) { + // The WAT distortion should already have been encoded in disseq. + strcpy(wcsprj->code, "ZPN"); + strcpy(wcs->ctype[wcs->lng]+5, "ZPN"); + strcpy(wcs->ctype[wcs->lat]+5, "ZPN"); + } - if (i == -1) { - /* From a PROJPn keyword. */ - i = wcs->lat; + // PVi_ma keyvalues. + for (int k = 0; k < wcs->npv; k++) { + if (wcs->pv[k].i == 0) { + // From a PROJPn keyword. + wcs->pv[k].i = wcs->lat + 1; } + int i = wcs->pv[k].i - 1; + int m = wcs->pv[k].m; + if (i == wcs->lat) { - /* PVi_ma associated with latitude axis. */ + // PVi_ma associated with latitude axis. if (m < 30) { wcsprj->pv[m] = wcs->pv[k].value; } } else if (i == wcs->lng) { - /* PVi_ma associated with longitude axis. */ + // PVi_ma associated with longitude axis. switch (m) { case 0: wcscel->offset = (wcs->pv[k].value != 0.0); @@ -1546,11 +2698,11 @@ int wcsset(struct wcsprm *wcs) wcscel->theta0 = wcs->pv[k].value; break; case 3: - /* If present, overrides LONPOLEa. */ + // If present, overrides LONPOLEa. wcscel->ref[2] = wcs->pv[k].value; break; case 4: - /* If present, overrides LATPOLEa. */ + // If present, overrides LATPOLEa. wcscel->ref[3] = wcs->pv[k].value; break; default: @@ -1562,7 +2714,7 @@ int wcsset(struct wcsprm *wcs) } } - /* Do simple alias translations. */ + // Do simple alias translations. if (strncmp(wcs->ctype[wcs->lng]+5, "GLS", 3) == 0) { wcscel->offset = 1; wcscel->phi0 = 0.0; @@ -1570,7 +2722,7 @@ int wcsset(struct wcsprm *wcs) strcpy(wcsprj->code, "SFL"); } else if (strncmp(wcs->ctype[wcs->lng]+5, "NCP", 3) == 0) { - /* Convert NCP to SIN. */ + // Convert NCP to SIN. if (wcscel->ref[1] == 0.0) { return wcserr_set(WCSERR_SET(WCSERR_BAD_PARAM), "Invalid projection: NCP blows up on the equator"); @@ -1579,25 +2731,22 @@ int wcsset(struct wcsprm *wcs) strcpy(wcsprj->code, "SIN"); wcsprj->pv[1] = 0.0; wcsprj->pv[2] = cosd(wcscel->ref[1])/sind(wcscel->ref[1]); - - } else { - strncpy(wcsprj->code, wcs->ctype[wcs->lng]+5, 3); - wcsprj->code[3] = '\0'; } - /* Initialize the celestial transformation routines. */ + // Initialize the celestial transformation routines. wcsprj->r0 = 0.0; + wcscel->flag = 0; if ((status = celset(wcscel))) { - return wcserr_set(WCS_ERRMSG(status+3)); + return wcserr_set(WCS_ERRMSG(wcs_celerr[status])); } - /* Update LONPOLE, LATPOLE, and PVi_ma keyvalues. */ + // Update LONPOLE, LATPOLE, and PVi_ma keyvalues. wcs->lonpole = wcscel->ref[2]; wcs->latpole = wcscel->ref[3]; - for (k = 0; k < wcs->npv; k++) { - i = wcs->pv[k].i - 1; - m = wcs->pv[k].m; + for (int k = 0; k < wcs->npv; k++) { + int i = wcs->pv[k].i - 1; + int m = wcs->pv[k].m; if (i == wcs->lng) { switch (m) { @@ -1619,8 +2768,10 @@ int wcsset(struct wcsprm *wcs) } - /* Non-linear spectral axis present? */ + // Non-linear spectral axis present? if (wcs->spec >= 0 && wcs->types[wcs->spec] == 3300) { + char scode[4], stype[5]; + struct spcprm *wcsspc = &(wcs->spc); spcini(wcsspc); if ((status = spctype(wcs->ctype[wcs->spec], stype, scode, 0x0, 0x0, 0x0, 0x0, 0x0, err))) { @@ -1629,65 +2780,77 @@ int wcsset(struct wcsprm *wcs) strcpy(wcsspc->type, stype); strcpy(wcsspc->code, scode); - /* CRVALia, RESTFRQa, and RESTWAVa keyvalues. */ + // CRVALia, RESTFRQa, and RESTWAVa keyvalues. wcsspc->crval = wcs->crval[wcs->spec]; wcsspc->restfrq = wcs->restfrq; wcsspc->restwav = wcs->restwav; - /* PVi_ma keyvalues. */ - for (k = 0; k < wcs->npv; k++) { - i = wcs->pv[k].i - 1; - m = wcs->pv[k].m; + // PVi_ma keyvalues. + for (int k = 0; k < wcs->npv; k++) { + int i = wcs->pv[k].i - 1; + int m = wcs->pv[k].m; if (i == wcs->spec) { - /* PVi_ma associated with grism axis. */ + // PVi_ma associated with grism axis. if (m < 7) { wcsspc->pv[m] = wcs->pv[k].value; } } } - /* Initialize the spectral transformation routines. */ + // Initialize the spectral transformation routines. + wcsspc->flag = 0; if ((status = spcset(wcsspc))) { - return wcserr_set(WCS_ERRMSG(status+3)); + return wcserr_set(WCS_ERRMSG(wcs_spcerr[status])); } } - /* Tabular axes present? */ - for (j = 0; j < wcs->ntab; j++) { - if ((status = tabset(wcs->tab + j))) { - return wcserr_set(WCS_ERRMSG(status+3)); + // Tabular axes present? + for (int itab = 0; itab < wcs->ntab; itab++) { + wcs->tab[itab].flag = 0; + if ((status = tabset(wcs->tab + itab))) { + return wcserr_set(WCS_ERRMSG(wcs_taberr[status])); } } - /* Initialize the linear transformation. */ - naxis = wcs->naxis; - wcs->altlin &= 7; + // Initialize the linear transformation. + wcs->altlin &= 15; if (wcs->altlin > 1 && !(wcs->altlin & 1)) { - pc = wcs->pc; + double *pc = wcs->pc; + + if ((wcs->altlin & 2) && !(wcs->altlin & 8)) { + // Copy CDi_ja to PCi_ja and reset CDELTia. + if (!wcs->cd) { + return wcserr_set(WCSERR_SET(WCSERR_BAD_PARAM), + "ALTLIN == %d but CDij absent", wcs->altlin); + } - if (wcs->altlin & 2) { - /* Copy CDi_ja to PCi_ja and reset CDELTia. */ - cd = wcs->cd; - for (i = 0; i < naxis; i++) { - for (j = 0; j < naxis; j++) { + double *cd = wcs->cd; + for (int i = 0; i < naxis; i++) { + for (int j = 0; j < naxis; j++) { *(pc++) = *(cd++); } wcs->cdelt[i] = 1.0; } } else if (wcs->altlin & 4) { - /* Construct PCi_ja from CROTAia. */ + // Construct PCi_ja from CROTAia. + if (!wcs->crota) { + return wcserr_set(WCSERR_SET(WCSERR_BAD_PARAM), + "ALTLIN == %d but CROTAj absent", wcs->altlin); + } + + int i, j; if ((i = wcs->lng) >= 0 && (j = wcs->lat) >= 0) { - rho = wcs->crota[j]; + double rho = wcs->crota[j]; if (wcs->cdelt[i] == 0.0) { return wcserr_set(WCSERR_SET(WCSERR_SINGULAR_MTX), "Singular transformation matrix, CDELT%d is zero", i+1); } - lambda = wcs->cdelt[j]/wcs->cdelt[i]; + double lambda = wcs->cdelt[j]/wcs->cdelt[i]; *(pc + i*naxis + i) = *(pc + j*naxis + j) = cosd(rho); *(pc + i*naxis + j) = *(pc + j*naxis + i) = sind(rho); @@ -1697,164 +2860,237 @@ int wcsset(struct wcsprm *wcs) } } - wcs->lin.crpix = wcs->crpix; - wcs->lin.pc = wcs->pc; - wcs->lin.cdelt = wcs->cdelt; + wcs->lin.crpix = wcs->crpix; + wcs->lin.pc = wcs->pc; + wcs->lin.cdelt = wcs->cdelt; + wcs->lin.flag = 0; if ((status = linset(&(wcs->lin)))) { - return wcserr_set(WCS_ERRMSG(status)); + return wcserr_set(WCS_ERRMSG(wcs_linerr[status])); } - /* Strip off trailing blanks and null-fill auxiliary string members. */ - wcsutil_null_fill(4, wcs->alt); - wcsutil_null_fill(72, wcs->wcsname); - for (i = 0; i < naxis; i++) { - wcsutil_null_fill(72, wcs->cname[i]); + // Set defaults for radesys and equinox for equatorial or ecliptic. + if (strcmp(wcs->lngtyp, "RA") == 0 || + strcmp(wcs->lngtyp, "ELON") == 0 || + strcmp(wcs->lngtyp, "HLON") == 0) { + if (wcs->radesys[0] == '\0') { + if (undefined(wcs->equinox)) { + strcpy(wcs->radesys, "ICRS"); + } else if (wcs->equinox < 1984.0) { + strcpy(wcs->radesys, "FK4"); + } else { + strcpy(wcs->radesys, "FK5"); + } + + } else if (strcmp(wcs->radesys, "ICRS") == 0 || + strcmp(wcs->radesys, "GAPPT") == 0) { + // Equinox is not applicable for these coordinate systems. + wcs->equinox = UNDEFINED; + + } else if (undefined(wcs->equinox)) { + if (strcmp(wcs->radesys, "FK5") == 0) { + wcs->equinox = 2000.0; + } else if (strcmp(wcs->radesys, "FK4") == 0 || + strcmp(wcs->radesys, "FK4-NO-E") == 0) { + wcs->equinox = 1950.0; + } + } + + } else { + // No celestial axes, ensure that radesys and equinox are unset. + memset(wcs->radesys, 0, 72); + wcs->equinox = UNDEFINED; + } + + + // Strip off trailing blanks and null-fill auxiliary string members. + if (wcs->alt[0] == '\0') wcs->alt[0] = ' '; + memset(wcs->alt+1, '\0', 3); + + if (wcs->cname) { + for (int i = 0; i < naxis; i++) { + wcsutil_null_fill(72, wcs->cname[i]); + } } + wcsutil_null_fill(72, wcs->wcsname); + wcsutil_null_fill(72, wcs->timesys); + wcsutil_null_fill(72, wcs->trefpos); + wcsutil_null_fill(72, wcs->trefdir); + wcsutil_null_fill(72, wcs->plephem); + wcsutil_null_fill(72, wcs->timeunit); + wcsutil_null_fill(72, wcs->dateref); + wcsutil_null_fill(72, wcs->dateobs); + wcsutil_null_fill(72, wcs->datebeg); + wcsutil_null_fill(72, wcs->dateavg); + wcsutil_null_fill(72, wcs->dateend); + wcsutil_null_fill(72, wcs->obsorbit); wcsutil_null_fill(72, wcs->radesys); wcsutil_null_fill(72, wcs->specsys); wcsutil_null_fill(72, wcs->ssysobs); wcsutil_null_fill(72, wcs->ssyssrc); - wcsutil_null_fill(72, wcs->dateobs); - wcsutil_null_fill(72, wcs->dateavg); - wcs->flag = WCSSET; + // MJDREF defaults to zero if no reference date keywords were defined. + if (wcs->dateref[0] == '\0') { + if (undefined(wcs->mjdref[0])) { + wcs->mjdref[0] = 0.0; + } + if (undefined(wcs->mjdref[1])) { + wcs->mjdref[1] = 0.0; + } + } + + // Compute and store the checksum. + wcs->chksum = wcs_chksum(wcs); + + wcs->flag = (wcs->flag == 1) ? -WCSSET : WCSSET; return 0; } -/* : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : */ +// : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : -int wcs_types(struct wcsprm *wcs) +static int wcs_types(struct wcsprm *wcs) { static const char *function = "wcs_types"; - const int nalias = 2; - const char aliases [2][4] = {"NCP", "GLS"}; - - const char *alt = ""; - char ctypei[16], pcode[4], requir[9], scode[4], specsys[9]; - int i, j, m, naxis, *ndx = 0x0, type; - struct wcserr **err; + const int nalias = 6; + const char aliases [6][4] = {"NCP", "GLS", "TPU", "TPV", "TNX", "ZPX"}; if (wcs == 0x0) return WCSERR_NULL_POINTER; - err = &(wcs->err); + struct wcserr **err = &(wcs->err); - /* Parse the CTYPEia keyvalues. */ + // Parse the CTYPEia keyvalues. + char pcode[4], requir[16]; pcode[0] = '\0'; requir[0] = '\0'; wcs->lng = -1; wcs->lat = -1; wcs->spec = -1; + wcs->time = -1; wcs->cubeface = -1; + const char *alt = ""; if (*(wcs->alt) != ' ') alt = wcs->alt; - naxis = wcs->naxis; + int naxis = wcs->naxis; if (wcs->types) free(wcs->types); - wcs->types = calloc(naxis, sizeof(int)); - if (wcs->types == NULL) { + if ((wcs->types = calloc(naxis, sizeof(int))) == 0x0) { return wcserr_set(WCS_ERRMSG(WCSERR_MEMORY)); } - for (i = 0; i < naxis; i++) { - /* Null fill. */ + int *ndx = 0x0; + for (int i = 0; i < naxis; i++) { + // Null fill. wcsutil_null_fill(72, wcs->ctype[i]); + char ctypei[16]; strncpy(ctypei, wcs->ctype[i], 15); ctypei[15] = '\0'; - /* Check for early Paper IV syntax (e.g. '-SIP' used by Spitzer). */ + // Check for early Paper IV syntax (e.g. '-SIP' used by Spitzer). if (strlen(ctypei) == 12 && ctypei[8] == '-') { - /* Excise the "4-3-3" or "8-3"-form distortion code. */ + // Excise the "4-3-3" or "8-3"-form distortion code. ctypei[8] = '\0'; - /* Remove trailing dashes from "8-3"-form codes. */ - for (j = 7; j > 0; j--) { + // Remove trailing dashes from "8-3"-form codes. + for (int j = 7; j > 0; j--) { if (ctypei[j] != '-') break; ctypei[j] = '\0'; } } - /* Logarithmic or tabular axis? */ + // Logarithmic or tabular axis? wcs->types[i] = 0; if (strcmp(ctypei+4, "-LOG") == 0) { - /* Logarithmic axis. */ + // Logarithmic axis. wcs->types[i] = 400; } else if (strcmp(ctypei+4, "-TAB") == 0) { - /* Tabular axis. */ + // Tabular axis. wcs->types[i] = 500; } if (wcs->types[i]) { - /* Could have -LOG or -TAB with celestial or spectral types. */ + // Could have -LOG or -TAB with celestial or spectral types. ctypei[4] = '\0'; - /* Take care of things like 'FREQ-LOG' or 'RA---TAB'. */ - for (j = 3; j >= 0; j--) { + // Take care of things like 'FREQ-LOG' or 'RA---TAB'. + for (int j = 3; j >= 0; j--) { if (ctypei[j] != '-') break; ctypei[j] = '\0'; } } - /* Translate AIPS spectral types for spctyp(). */ + // Translate AIPS spectral types for spctyp(). + char specsys[9]; if (spcaips(ctypei, wcs->velref, ctypei, specsys) == 0) { strcpy(wcs->ctype[i], ctypei); if (wcs->specsys[0] == '\0') strcpy(wcs->specsys, specsys); } - /* Process linear axes. */ + // Process linear axes. if (!(strlen(ctypei) == 8 && ctypei[4] == '-')) { - /* Identify Stokes, celestial and spectral types. */ + // Identify Stokes, celestial, spectral, and time types. if (strcmp(ctypei, "STOKES") == 0) { - /* STOKES axis. */ + // STOKES axis. wcs->types[i] = 1100; } else if (strcmp(ctypei, "RA") == 0 || strcmp(ctypei+1, "LON") == 0 || strcmp(ctypei+2, "LN") == 0) { - /* Longitude axis. */ - if (wcs->lng < 0) wcs->lng = i; + // Longitude axis. wcs->types[i] += 2000; + if (wcs->lng < 0) { + wcs->lng = i; + strcpy(wcs->lngtyp, ctypei); + } } else if (strcmp(ctypei, "DEC") == 0 || strcmp(ctypei+1, "LAT") == 0 || strcmp(ctypei+2, "LT") == 0) { - /* Latitude axis. */ - if (wcs->lat < 0) wcs->lat = i; + // Latitude axis. wcs->types[i] += 2001; + if (wcs->lat < 0) { + wcs->lat = i; + strcpy(wcs->lattyp, ctypei); + } } else if (strcmp(ctypei, "CUBEFACE") == 0) { - /* CUBEFACE axis. */ + // CUBEFACE axis. if (wcs->cubeface == -1) { wcs->types[i] = 2102; wcs->cubeface = i; } else { - /* Multiple CUBEFACE axes! */ + // Multiple CUBEFACE axes! return wcserr_set(WCSERR_SET(WCSERR_BAD_CTYPE), "Multiple CUBEFACE axes (in CTYPE%d%.1s and CTYPE%d%.1s)", wcs->cubeface+1, alt, i+1, alt); } } else if (spctyp(ctypei, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0) == 0) { - /* Spectral axis. */ + // Spectral axis. if (wcs->spec < 0) wcs->spec = i; wcs->types[i] += 3000; + + } else if (time_type(ctypei)) { + // Time axis. + if (wcs->time < 0) wcs->time = i; + wcs->types[i] += 4000; } continue; } - /* CTYPEia is in "4-3" form; is it a recognized spectral type? */ + // CTYPEia is in "4-3" form; is it a recognized spectral type? + char scode[4]; if (spctyp(ctypei, 0x0, scode, 0x0, 0x0, 0x0, 0x0, 0x0) == 0) { - /* Non-linear spectral axis found. */ + // Non-linear spectral axis found. wcs->types[i] = 3300; - /* Check uniqueness. */ + // Check uniqueness. if (wcs->spec >= 0) { return wcserr_set(WCSERR_SET(WCSERR_BAD_CTYPE), "Multiple spectral axes (in CTYPE%d%.1s and CTYPE%d%.1s)", @@ -1867,19 +3103,20 @@ int wcs_types(struct wcsprm *wcs) } - /* Is it a recognized celestial projection? */ + // Is it a recognized celestial projection? + int j; for (j = 0; j < prj_ncode; j++) { if (strncmp(ctypei+5, prj_codes[j], 3) == 0) break; } if (j == prj_ncode) { - /* Not a standard projection code, maybe it's an alias. */ + // Not a standard projection code, maybe it's an alias. for (j = 0; j < nalias; j++) { if (strncmp(ctypei+5, aliases[j], 3) == 0) break; } if (j == nalias) { - /* Not a recognized algorithm code of any type. */ + // Not a recognized algorithm code of any type. wcs->types[i] = -1; return wcserr_set(WCSERR_SET(WCSERR_BAD_CTYPE), "Unrecognized projection code (%s in CTYPE%d%.1s)", @@ -1887,10 +3124,10 @@ int wcs_types(struct wcsprm *wcs) } } - /* Parse the celestial axis type. */ + // Parse the celestial axis type. wcs->types[i] = 2200; if (*pcode == '\0') { - /* The first of the two celestial axes. */ + // The first of the two celestial axes. sprintf(pcode, "%.3s", ctypei+5); if (strncmp(ctypei, "RA--", 4) == 0) { @@ -1930,7 +3167,7 @@ int wcs_types(struct wcsprm *wcs) ndx = &wcs->lng; sprintf(requir, "%s-%s", wcs->lngtyp, pcode); } else { - /* Unrecognized celestial type. */ + // Unrecognized celestial type. wcs->types[i] = -1; wcs->lng = -1; @@ -1943,11 +3180,11 @@ int wcs_types(struct wcsprm *wcs) if (wcs->lat >= 0) wcs->types[i]++; } else { - /* Looking for the complementary celestial axis. */ + // Looking for the complementary celestial axis. if (wcs->lat < 0) wcs->types[i]++; if (strncmp(ctypei, requir, 8) != 0) { - /* Inconsistent projection types. */ + // Inconsistent projection types. wcs->lng = -1; wcs->lat = -1; return wcserr_set(WCSERR_SET(WCSERR_BAD_CTYPE), "Inconsistent " @@ -1960,22 +3197,22 @@ int wcs_types(struct wcsprm *wcs) } } - /* Do we have a complementary pair of celestial axes? */ + // Do we have a complementary pair of celestial axes? if (strcmp(requir, "")) { - /* Unmatched celestial axis. */ + // Unmatched celestial axis. wcs->lng = -1; wcs->lat = -1; return wcserr_set(WCSERR_SET(WCSERR_BAD_CTYPE), "Unmatched celestial axes"); } - /* Table group numbers. */ - for (j = 0; j < wcs->ntab; j++) { - for (m = 0; m < wcs->tab[j].M; m++) { - /* Get image axis number. */ - i = wcs->tab[j].map[m]; + // Table group numbers. + for (int j = 0; j < wcs->ntab; j++) { + for (int m = 0; m < wcs->tab[j].M; m++) { + // Get image axis number. + int i = wcs->tab[j].map[m]; - type = (wcs->types[i] / 100) % 10; + int type = (wcs->types[i] / 100) % 10; if (type != 5) { return wcserr_set(WCSERR_SET(WCSERR_BAD_CTYPE), "Table parameters set for non-table axis type"); @@ -1987,32 +3224,80 @@ int wcs_types(struct wcsprm *wcs) return 0; } -/* : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : */ +// : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : -int wcs_units(struct wcsprm *wcs) +static int time_type(const char *ctype) { - static const char *function = "wcs_units"; + // Is it a recognised time system as listed in Table 2 of WCS Paper VII? + if (strncmp(ctype, "TIME", 4) == 0) return time_code(ctype, 4); + if (strncmp(ctype, "UTC", 3) == 0) return time_code(ctype, 3); + if (strncmp(ctype, "TAI", 3) == 0) return time_code(ctype, 3); + if (strncmp(ctype, "IAT", 3) == 0) return time_code(ctype, 3); + if (strncmp(ctype, "TT", 2) == 0) return time_code(ctype, 2); + if (strncmp(ctype, "TDB", 3) == 0) return time_code(ctype, 3); + if (strncmp(ctype, "TDT", 3) == 0) return time_code(ctype, 3); + if (strncmp(ctype, "GPS", 3) == 0) return time_code(ctype, 3); + if (strncmp(ctype, "TCB", 3) == 0) return time_code(ctype, 3); + if (strncmp(ctype, "TCG", 3) == 0) return time_code(ctype, 3); + if (strncmp(ctype, "GMT", 3) == 0) return time_code(ctype, 3); + if (strncmp(ctype, "UT1", 3) == 0) return time_code(ctype, 3); + if (strncmp(ctype, "UT", 2) == 0) return time_code(ctype, 2); + if (strncmp(ctype, "ET", 2) == 0) return time_code(ctype, 2); + if (strncmp(ctype, "LOCAL",5) == 0) return 1; + + return 0; +} + +// : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : + +static int time_code(const char *ctype, int nc) - char ctype[9], units[16]; - int i, j, naxis; - double scale, offset, power; - struct wcserr *uniterr = 0x0, **err; +{ + // If no algorithm code then we're finished. + if (*(ctype+nc) == '\0') return 1; + + // Check the correct number of hyphens for things like "TT---TAB". + while (nc < 4) { + if (*(ctype+nc) != '-') return 0; + nc++; + } + + // Is it a code applicable to time-like axes? + const char *code = ctype + 4; + if (*code == '-') { + if (strncmp(code, "-LOG", 5) == 0) return 1; + if (strncmp(code, "-TAB", 5) == 0) return 1; + } + + return 0; +} + +// : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : + +static int wcs_units(struct wcsprm *wcs) + +{ + static const char *function = "wcs_units"; if (wcs == 0x0) return WCSERR_NULL_POINTER; - err = &(wcs->err); + struct wcserr **err = &(wcs->err); + + int naxis = wcs->naxis; + for (int i = 0; i < naxis; i++) { + // Squeeze out trailing blanks. + wcsutil_null_fill(72, wcs->cunit[i]); - naxis = wcs->naxis; - for (i = 0; i < naxis; i++) { - /* Use types set by wcs_types(). */ + // Use types set by wcs_types(). + char ctype[9], units[16]; switch (wcs->types[i]/1000) { case 2: - /* Celestial axis. */ + // Celestial axis. strcpy(units, "deg"); break; case 3: - /* Spectral axis. */ + // Spectral axis. strncpy(ctype, wcs->ctype[i], 8); ctype[8] = '\0'; spctyp(ctype, 0x0, 0x0, 0x0, units, 0x0, 0x0, 0x0); @@ -2022,18 +3307,22 @@ int wcs_units(struct wcsprm *wcs) continue; } - /* Tabular axis, CDELTia and CRVALia relate to indices. */ + // Tabular axis, CDELTia and CRVALia relate to indices. if ((wcs->types[i]/100)%10 == 5) { continue; } - wcsutil_null_fill(72, wcs->cunit[i]); if (wcs->cunit[i][0]) { + double scale, offset, power; + struct wcserr *uniterr; if (wcsunitse(wcs->cunit[i], units, &scale, &offset, &power, &uniterr)) { - wcserr_set(WCSERR_SET(WCSERR_BAD_COORD_TRANS), - "In CUNIT%d%.1s: %s", i, (*wcs->alt)?wcs->alt:"", uniterr->msg); - free(uniterr); + if (uniterr) { + // uniterr will not be set if wcserr is not enabled. + wcserr_set(WCSERR_SET(WCSERR_BAD_COORD_TRANS), + "In CUNIT%d%.1s: %s", i+1, (*wcs->alt)?wcs->alt:"", uniterr->msg); + free(uniterr); + } return WCSERR_BAD_COORD_TRANS; } @@ -2041,8 +3330,10 @@ int wcs_units(struct wcsprm *wcs) wcs->cdelt[i] *= scale; wcs->crval[i] *= scale; - for (j = 0; j < naxis; j++) { - *(wcs->cd + i*naxis + j) *= scale; + if (wcs->cd) { + for (int j = 0; j < naxis; j++) { + *(wcs->cd + i*naxis + j) *= scale; + } } strcpy(wcs->cunit[i], units); @@ -2056,7 +3347,105 @@ int wcs_units(struct wcsprm *wcs) return 0; } -/*--------------------------------------------------------------------------*/ +// : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : + +static int wcs_chksum(const struct wcsprm *wcs) + +{ + if (wcs == 0x0) return WCSERR_NULL_POINTER; + + size_t naxis = wcs->naxis; + size_t szi = sizeof(int); + size_t szd = sizeof(double); + size_t nszc72 = naxis * sizeof(char [72]); + size_t nszd = naxis * szd; + size_t nnszd = naxis * nszd; + + int chksum = 0; + + // The checksum is computed incrementally - the result from one invokation + // forms the starting value for the next one. + chksum = wcs_fletcher32(chksum, &wcs->naxis, szi); + chksum = wcs_fletcher32(chksum, wcs->crpix, nszd); + chksum = wcs_fletcher32(chksum, wcs->pc, nnszd); + chksum = wcs_fletcher32(chksum, wcs->cdelt, nszd); + chksum = wcs_fletcher32(chksum, wcs->crval, nszd); + chksum = wcs_fletcher32(chksum, wcs->cunit, nszc72); + chksum = wcs_fletcher32(chksum, wcs->ctype, nszc72); + chksum = wcs_fletcher32(chksum, &wcs->lonpole, szd); + chksum = wcs_fletcher32(chksum, &wcs->latpole, szd); + chksum = wcs_fletcher32(chksum, &wcs->restfrq, szd); + chksum = wcs_fletcher32(chksum, &wcs->restwav, szd); + chksum = wcs_fletcher32(chksum, &wcs->npv, szi); + + if (wcs->pv) { + size_t nszpv = wcs->npv * sizeof(struct pvcard); + chksum = wcs_fletcher32(chksum, wcs->pv, nszpv); + } + + chksum = wcs_fletcher32(chksum, &wcs->nps, szi); + + if (wcs->ps) { + size_t nszps = wcs->nps * sizeof(struct pscard); + chksum = wcs_fletcher32(chksum, wcs->ps, nszps); + } + + if (wcs->cd) { + chksum = wcs_fletcher32(chksum, wcs->cd, nnszd); + } + + if (wcs->crota) { + chksum = wcs_fletcher32(chksum, wcs->crota, nszd); + } + + chksum = wcs_fletcher32(chksum, &wcs->altlin, szi); + chksum = wcs_fletcher32(chksum, &wcs->ntab, szi); + chksum = wcs_fletcher32(chksum, &wcs->nwtb, szi); + chksum = wcs_fletcher32(chksum, &wcs->tab, sizeof(struct tabprm *)); + chksum = wcs_fletcher32(chksum, &wcs->wtb, sizeof(struct wtbarr *)); + + return chksum; +} + +// : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : + +// Compute the Fletcher-32 checksum for a sequence of bytes. The algorithm is +// described in https://en.wikipedia.org/wiki/Fletcher's_checksum. +// +// Given: +// chksum int Checksum from a previous invokation, forming the +// starting value for this one. +// +// data const void * +// Data array, treated as an array of uint16_t. +// +// len size_t Length of the data array in bytes. Must be even and +// between 0 and 259200 inclusive (no checks are made). +// +// Function return value: +// int Fletcher-32 checksum. + +static int wcs_fletcher32(int chksum, const void *data, size_t len) + +{ + const uint16_t *datap = (const uint16_t *)data; + + uint32_t c0 = ((uint32_t)chksum & 65535); + uint32_t c1 = ((uint32_t)chksum >> 16); + + while (len) { + c0 += *datap++; + c1 += c0; + len -= 2; + } + + c0 %= 65535; + c1 %= 65535; + + return (int)(c1 << 16 | c0); +} + +//---------------------------------------------------------------------------- int wcsp2s( struct wcsprm *wcs, @@ -2072,36 +3461,25 @@ int wcsp2s( { static const char *function = "wcsp2s"; - int bits, face, i, iso_x, iso_y, istat, *istatp, itab, k, m, nx, ny, - *statp, status, type; - double crvali, offset; - register double *img, *wrl; - struct celprm *wcscel = &(wcs->cel); - struct prjprm *wcsprj = &(wcscel->prj); - struct wcserr **err; - - /* Initialize if required. */ + // Initialize if required. if (wcs == 0x0) return WCSERR_NULL_POINTER; - err = &(wcs->err); + struct wcserr **err = &(wcs->err); - if (wcs->flag != WCSSET) { + int status = 0; + if (abs(wcs->flag) != WCSSET) { if ((status = wcsset(wcs))) return status; } - /* Sanity check. */ + // Sanity check. if (ncoord < 1 || (ncoord > 1 && nelem < wcs->naxis)) { return wcserr_set(WCSERR_SET(WCSERR_BAD_CTYPE), "ncoord and/or nelem inconsistent with the wcsprm"); } - /* Apply pixel-to-world linear transformation. */ - if ((status = linp2x(&(wcs->lin), ncoord, nelem, pixcrd, imgcrd))) { - return wcserr_set(WCS_ERRMSG(status)); - } - - /* Initialize status vectors. */ - if (!(istatp = calloc(ncoord, sizeof(int)))) { + // Initialize status vectors. + int *istatp; + if ((istatp = calloc(ncoord, sizeof(int))) == 0x0) { return wcserr_set(WCS_ERRMSG(WCSERR_MEMORY)); } @@ -2109,38 +3487,93 @@ int wcsp2s( wcsutil_setAli(ncoord, 1, stat); - /* Convert intermediate world coordinates to world coordinates. */ - for (i = 0; i < wcs->naxis; i++) { - /* Extract the second digit of the axis type code. */ - type = (wcs->types[i] / 100) % 10; + // Apply pixel-to-world linear transformation. + struct linprm *lin = &(wcs->lin); + if (!(lin->dispre || lin->disseq)) { + // No distortions present, do vector call. + int istat = linp2x(lin, ncoord, nelem, pixcrd, imgcrd); + if (istat) { + // If one fails then all fail. + status = wcserr_set(WCS_ERRMSG(wcs_linerr[istat])); + goto cleanup; + } + + } else { + // Distortions present, get the status return for each coordinate. + int disaxes = 0; + + const double *pix = pixcrd; + double *img = imgcrd; + int *statp = stat; + for (int k = 0 ; k < ncoord; k++, pix += nelem, img += nelem, statp++) { + int istat = linp2x(lin, 1, nelem, pix, img); + if (istat) { + status = wcserr_set(WCS_ERRMSG(wcs_linerr[istat])); + if (status != WCSERR_BAD_PIX) { + goto cleanup; + } + + if (disaxes == 0) { + // Which axes have distortions? + struct disprm *dispre = lin->dispre; + struct disprm *disseq = lin->disseq; + for (int i = 0; i < wcs->naxis; i++) { + if (dispre && dispre->disp2x[i]) { + disaxes |= (1 << i); + } else if (disseq && disseq->disp2x[i]) { + disaxes |= (1 << i); + } + } + + if (disaxes == 0) { + // Shouldn't happen. + disaxes = (2 << wcs->naxis) - 1; + } + } + + // WCSERR_BAD_PIX stat[] vector accounting. + *statp = disaxes; + } + } + } + + + // Convert intermediate world coordinates to world coordinates. + struct celprm *wcscel = &(wcs->cel); + struct prjprm *wcsprj = &(wcscel->prj); + for (int i = 0; i < wcs->naxis; i++) { + // Extract the second digit of the axis type code. + int type = (wcs->types[i] / 100) % 10; + double *img, *wrl; if (type <= 1) { - /* Linear or quantized coordinate axis. */ + // Linear or quantized coordinate axis. img = imgcrd + i; wrl = world + i; - crvali = wcs->crval[i]; - for (k = 0; k < ncoord; k++) { + double crvali = wcs->crval[i]; + for (int k = 0; k < ncoord; k++) { *wrl = *img + crvali; img += nelem; wrl += nelem; } } else if (wcs->types[i] == 2200) { - /* Convert celestial coordinates; do we have a CUBEFACE axis? */ + // Convert celestial coordinates; do we have a CUBEFACE axis? if (wcs->cubeface != -1) { - /* Separation between faces. */ + // Separation between faces. + double offset; if (wcsprj->r0 == 0.0) { offset = 90.0; } else { offset = wcsprj->r0*PI/2.0; } - /* Lay out faces in a plane. */ + // Lay out faces in a plane. img = imgcrd; - statp = stat; - bits = (1 << i) | (1 << wcs->lat); - for (k = 0; k < ncoord; k++, statp++) { - face = (int)(*(img+wcs->cubeface) + 0.5); + int *statp = stat; + int bits = (1 << i) | (1 << wcs->lat); + for (int k = 0; k < ncoord; k++, statp++) { + int face = (int)(*(img+wcs->cubeface) + 0.5); if (fabs(*(img+wcs->cubeface) - face) > 1e-10) { *statp |= bits; status = wcserr_set(WCS_ERRMSG(WCSERR_BAD_PIX)); @@ -2176,31 +3609,34 @@ int wcsp2s( } } - /* Check for constant x and/or y. */ - nx = ncoord; - ny = 0; + // Check for constant x and/or y. + int iso_x = 0; + int iso_y = 0; + int nx = ncoord; + int ny = 0; - if ((iso_x = wcsutil_allEq(ncoord, nelem, imgcrd+i))) { - nx = 1; - ny = ncoord; - } - if ((iso_y = wcsutil_allEq(ncoord, nelem, imgcrd+wcs->lat))) { - ny = 1; + if (ncoord > 1) { + if ((iso_x = wcsutil_allEq(ncoord, nelem, imgcrd+i))) { + nx = 1; + ny = ncoord; + } + if ((iso_y = wcsutil_allEq(ncoord, nelem, imgcrd+wcs->lat))) { + ny = 1; + } } - /* Transform projection plane coordinates to celestial coordinates. */ - if ((istat = celx2s(wcscel, nx, ny, nelem, nelem, imgcrd+i, - imgcrd+wcs->lat, phi, theta, world+i, - world+wcs->lat, istatp))) { - if (istat == CELERR_BAD_PIX) { - status = wcserr_set(WCS_ERRMSG(WCSERR_BAD_PIX)); - } else { - status = wcserr_set(WCS_ERRMSG(istat+3)); + // Transform projection plane coordinates to celestial coordinates. + int istat = celx2s(wcscel, nx, ny, nelem, nelem, imgcrd+i, + imgcrd+wcs->lat, phi, theta, world+i, + world+wcs->lat, istatp); + if (istat) { + status = wcserr_set(WCS_ERRMSG(wcs_celerr[istat])); + if (status != WCSERR_BAD_PIX) { goto cleanup; } } - /* If x and y were both constant, replicate values. */ + // If x and y were both constant, replicate values. if (iso_x && iso_y) { wcsutil_setAll(ncoord, nelem, world+i); wcsutil_setAll(ncoord, nelem, world+wcs->lat); @@ -2209,79 +3645,82 @@ int wcsp2s( wcsutil_setAli(ncoord, 1, istatp); } - if (istat == 5) { - bits = (1 << i) | (1 << wcs->lat); + // WCSERR_BAD_PIX stat[] vector accounting. + if (istat) { + int bits = (1 << i) | (1 << wcs->lat); wcsutil_setBit(ncoord, istatp, bits, stat); } } else if (type == 3 || type == 4) { - /* Check for constant x. */ - nx = ncoord; - if ((iso_x = wcsutil_allEq(ncoord, nelem, imgcrd+i))) { - nx = 1; + // Spectral and logarithmic coordinates; check for constant x. + int iso_x = 0; + int nx = ncoord; + + if (ncoord > 1) { + if ((iso_x = wcsutil_allEq(ncoord, nelem, imgcrd+i))) { + nx = 1; + } } - istat = 0; + int istat = 0; if (wcs->types[i] == 3300) { - /* Spectral coordinates. */ + // Spectral coordinates. istat = spcx2s(&(wcs->spc), nx, nelem, nelem, imgcrd+i, world+i, istatp); - if (istat == SPCERR_BAD_X) { - status = wcserr_set(WCS_ERRMSG(WCSERR_BAD_PIX)); - } else if (istat) { - status = wcserr_set(WCS_ERRMSG(istat+3)); - goto cleanup; + if (istat) { + status = wcserr_set(WCS_ERRMSG(wcs_spcerr[istat])); + if (status != WCSERR_BAD_PIX) { + goto cleanup; + } } } else if (type == 4) { - /* Logarithmic coordinates. */ + // Logarithmic coordinates. istat = logx2s(wcs->crval[i], nx, nelem, nelem, imgcrd+i, world+i, istatp); - if (istat == LOGERR_BAD_X) { - if (*err == 0x0) { - wcserr_set(WCS_ERRMSG(WCSERR_BAD_PIX)); + if (istat) { + status = wcserr_set(WCS_ERRMSG(wcs_logerr[istat])); + if (status != WCSERR_BAD_PIX) { + goto cleanup; } - } else if (istat == LOGERR_BAD_LOG_REF_VAL) { - wcserr_set(WCSERR_SET(WCSERR_BAD_PARAM), log_errmsg[istat]); - goto cleanup; } } - /* If x was constant, replicate values. */ + // If x was constant, replicate values. if (iso_x) { wcsutil_setAll(ncoord, nelem, world+i); wcsutil_setAli(ncoord, 1, istatp); } - if (istat == 3) { + // WCSERR_BAD_PIX stat[] vector accounting. + if (istat) { wcsutil_setBit(ncoord, istatp, 1 << i, stat); } } } - /* Do tabular coordinates. */ - for (itab = 0; itab < wcs->ntab; itab++) { - istat = tabx2s(wcs->tab + itab, ncoord, nelem, imgcrd, world, istatp); + // Do tabular coordinates. + for (int itab = 0; itab < wcs->ntab; itab++) { + int istat = tabx2s(wcs->tab + itab, ncoord, nelem, imgcrd, world, istatp); - if (istat == TABERR_BAD_X) { - status = wcserr_set(WCS_ERRMSG(WCSERR_BAD_PIX)); + if (istat) { + status = wcserr_set(WCS_ERRMSG(wcs_taberr[istat])); + if (status != WCSERR_BAD_PIX) { + goto cleanup; + } - bits = 0; - for (m = 0; m < wcs->tab[itab].M; m++) { + // WCSERR_BAD_PIX stat[] vector accounting. + int bits = 0; + for (int m = 0; m < wcs->tab[itab].M; m++) { bits |= 1 << wcs->tab[itab].map[m]; } wcsutil_setBit(ncoord, istatp, bits, stat); - - } else if (istat) { - if (istat == TABERR_BAD_PARAMS) istat = WCSERR_BAD_PARAM; - status = wcserr_set(WCS_ERRMSG(istat)); - goto cleanup; } } - /* Zero the unused world coordinate elements. */ - for (i = wcs->naxis; i < nelem; i++) { + // Zero the unused world coordinate elements. + for (int i = wcs->naxis; i < nelem; i++) { world[i] = 0.0; wcsutil_setAll(ncoord, nelem, world+i); } @@ -2291,7 +3730,7 @@ int wcsp2s( return status; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- int wcss2p( struct wcsprm* wcs, @@ -2307,82 +3746,78 @@ int wcss2p( { static const char *function = "wcss2p"; - int bits, i, isolat, isolng, isospec, istat, *istatp, itab, k, m, nlat, - nlng, nwrld, status, type; - double crvali, offset; - register const double *wrl; - register double *img; - struct celprm *wcscel = &(wcs->cel); - struct prjprm *wcsprj = &(wcscel->prj); - struct wcserr **err; - - - /* Initialize if required. */ + // Initialize if required. if (wcs == 0x0) return WCSERR_NULL_POINTER; - err = &(wcs->err); + struct wcserr **err = &(wcs->err); - if (wcs->flag != WCSSET) { + int status = 0; + if (abs(wcs->flag) != WCSSET) { if ((status = wcsset(wcs))) return status; } - /* Sanity check. */ + // Sanity check. if (ncoord < 1 || (ncoord > 1 && nelem < wcs->naxis)) { return wcserr_set(WCSERR_SET(WCSERR_BAD_CTYPE), "ncoord and/or nelem inconsistent with the wcsprm"); } - /* Initialize status vectors. */ - if (!(istatp = calloc(ncoord, sizeof(int)))) { + // Initialize status vectors. + int *istatp; + if ((istatp = calloc(ncoord, sizeof(int))) == 0x0) { return wcserr_set(WCS_ERRMSG(WCSERR_MEMORY)); } - status = 0; stat[0] = 0; wcsutil_setAli(ncoord, 1, stat); - /* Convert world coordinates to intermediate world coordinates. */ - for (i = 0; i < wcs->naxis; i++) { - /* Extract the second digit of the axis type code. */ - type = (wcs->types[i] / 100) % 10; + // Convert world coordinates to intermediate world coordinates. + struct celprm *wcscel = &(wcs->cel); + struct prjprm *wcsprj = &(wcscel->prj); + for (int i = 0; i < wcs->naxis; i++) { + // Extract the second digit of the axis type code. + int type = (wcs->types[i] / 100) % 10; if (type <= 1) { - /* Linear or quantized coordinate axis. */ - wrl = world + i; - img = imgcrd + i; - crvali = wcs->crval[i]; - for (k = 0; k < ncoord; k++) { + // Linear or quantized coordinate axis. + const double *wrl = world + i; + double *img = imgcrd + i; + double crvali = wcs->crval[i]; + for (int k = 0; k < ncoord; k++) { *img = *wrl - crvali; wrl += nelem; img += nelem; } } else if (wcs->types[i] == 2200) { - /* Celestial coordinates; check for constant lng and/or lat. */ - nlng = ncoord; - nlat = 0; - - if ((isolng = wcsutil_allEq(ncoord, nelem, world+i))) { - nlng = 1; - nlat = ncoord; - } - if ((isolat = wcsutil_allEq(ncoord, nelem, world+wcs->lat))) { - nlat = 1; + // Celestial coordinates; check for constant lng and/or lat. + int isolng = 0; + int isolat = 0; + int nlng = ncoord; + int nlat = 0; + + if (ncoord > 1) { + if ((isolng = wcsutil_allEq(ncoord, nelem, world+i))) { + nlng = 1; + nlat = ncoord; + } + if ((isolat = wcsutil_allEq(ncoord, nelem, world+wcs->lat))) { + nlat = 1; + } } - /* Transform celestial coordinates to projection plane coordinates. */ - if ((istat = cels2x(wcscel, nlng, nlat, nelem, nelem, world+i, - world+wcs->lat, phi, theta, imgcrd+i, - imgcrd+wcs->lat, istatp))) { - if (istat == CELERR_BAD_WORLD) { - status = wcserr_set(WCS_ERRMSG(WCSERR_BAD_WORLD)); - } else { - status = wcserr_set(WCS_ERRMSG(istat+3)); + // Transform celestial coordinates to projection plane coordinates. + int istat = cels2x(wcscel, nlng, nlat, nelem, nelem, world+i, + world+wcs->lat, phi, theta, imgcrd+i, + imgcrd+wcs->lat, istatp); + if (istat) { + status = wcserr_set(WCS_ERRMSG(wcs_celerr[istat])); + if (status != WCSERR_BAD_WORLD) { goto cleanup; } } - /* If lng and lat were both constant, replicate values. */ + // If lng and lat were both constant, replicate values. if (isolng && isolat) { wcsutil_setAll(ncoord, nelem, imgcrd+i); wcsutil_setAll(ncoord, nelem, imgcrd+wcs->lat); @@ -2391,23 +3826,25 @@ int wcss2p( wcsutil_setAli(ncoord, 1, istatp); } - if (istat == CELERR_BAD_WORLD) { - bits = (1 << i) | (1 << wcs->lat); + // WCSERR_BAD_WORLD stat[] vector accounting. + if (istat) { + int bits = (1 << i) | (1 << wcs->lat); wcsutil_setBit(ncoord, istatp, bits, stat); } - /* Do we have a CUBEFACE axis? */ + // Do we have a CUBEFACE axis? if (wcs->cubeface != -1) { - /* Separation between faces. */ + // Separation between faces. + double offset; if (wcsprj->r0 == 0.0) { offset = 90.0; } else { offset = wcsprj->r0*PI/2.0; } - /* Stack faces in a cube. */ - img = imgcrd; - for (k = 0; k < ncoord; k++) { + // Stack faces in a cube. + double *img = imgcrd; + for (int k = 0; k < ncoord; k++) { if (*(img+wcs->lat) < -0.5*offset) { *(img+wcs->lat) += offset; *(img+wcs->cubeface) = 5.0; @@ -2432,81 +3869,127 @@ int wcss2p( } } else if (type == 3 || type == 4) { - /* Check for constancy. */ - nwrld = ncoord; - if ((isospec = wcsutil_allEq(ncoord, nelem, world+i))) { - nwrld = 1; + // Spectral and logarithmic coordinates; check for constancy. + int isospec = 0; + int nwrld = ncoord; + + if (ncoord > 1) { + if ((isospec = wcsutil_allEq(ncoord, nelem, world+i))) { + nwrld = 1; + } } - istat = 0; + int istat = 0; if (wcs->types[i] == 3300) { - /* Spectral coordinates. */ + // Spectral coordinates. istat = spcs2x(&(wcs->spc), nwrld, nelem, nelem, world+i, imgcrd+i, istatp); - if (istat == SPCERR_BAD_SPEC) { - status = wcserr_set(WCS_ERRMSG(WCSERR_BAD_WORLD)); - } else if (istat) { - status = wcserr_set(WCS_ERRMSG(istat+3)); - goto cleanup; + if (istat) { + status = wcserr_set(WCS_ERRMSG(wcs_spcerr[istat])); + if (status != WCSERR_BAD_WORLD) { + goto cleanup; + } } } else if (type == 4) { - /* Logarithmic coordinates. */ + // Logarithmic coordinates. istat = logs2x(wcs->crval[i], nwrld, nelem, nelem, world+i, imgcrd+i, istatp); - if (istat == LOGERR_BAD_WORLD) { - status = wcserr_set(WCS_ERRMSG(WCSERR_BAD_WORLD)); - } else if (istat == LOGERR_BAD_LOG_REF_VAL) { - status = wcserr_set(WCSERR_SET(WCSERR_BAD_PARAM), - log_errmsg[istat]); - goto cleanup; + if (istat) { + status = wcserr_set(WCS_ERRMSG(wcs_logerr[istat])); + if (status != WCSERR_BAD_WORLD) { + goto cleanup; + } } } - /* If constant, replicate values. */ + // If constant, replicate values. if (isospec) { wcsutil_setAll(ncoord, nelem, imgcrd+i); wcsutil_setAli(ncoord, 1, istatp); } - if (istat == 4) { + // WCSERR_BAD_WORLD stat[] vector accounting. + if (istat) { wcsutil_setBit(ncoord, istatp, 1 << i, stat); } } } - /* Do tabular coordinates. */ - for (itab = 0; itab < wcs->ntab; itab++) { - istat = tabs2x(wcs->tab + itab, ncoord, nelem, world, imgcrd, istatp); + // Do tabular coordinates. + for (int itab = 0; itab < wcs->ntab; itab++) { + int istat = tabs2x(wcs->tab + itab, ncoord, nelem, world, imgcrd, istatp); - if (istat == TABERR_BAD_WORLD) { - status = wcserr_set(WCS_ERRMSG(WCSERR_BAD_WORLD)); + if (istat) { + status = wcserr_set(WCS_ERRMSG(wcs_taberr[istat])); - bits = 0; - for (m = 0; m < wcs->tab[itab].M; m++) { + if (status != WCSERR_BAD_WORLD) { + goto cleanup; + } + + int bits = 0; + for (int m = 0; m < wcs->tab[itab].M; m++) { bits |= 1 << wcs->tab[itab].map[m]; } wcsutil_setBit(ncoord, istatp, bits, stat); - - } else if (istat) { - if (istat == TABERR_BAD_PARAMS) istat = WCSERR_BAD_PARAM; - status = wcserr_set(WCS_ERRMSG(istat)); - goto cleanup; } } - /* Zero the unused intermediate world coordinate elements. */ - for (i = wcs->naxis; i < nelem; i++) { + // Zero the unused intermediate world coordinate elements. + for (int i = wcs->naxis; i < nelem; i++) { imgcrd[i] = 0.0; wcsutil_setAll(ncoord, nelem, imgcrd+i); } - /* Apply world-to-pixel linear transformation. */ - if ((istat = linx2p(&(wcs->lin), ncoord, nelem, imgcrd, pixcrd))) { - status = wcserr_set(WCS_ERRMSG(istat)); - goto cleanup; + // Apply world-to-pixel linear transformation. + struct linprm *lin = &(wcs->lin); + if (!(lin->dispre || lin->disseq)) { + // No distortions present, do vector call. + int istat = linx2p(lin, ncoord, nelem, imgcrd, pixcrd); + if (istat) { + status = wcserr_set(WCS_ERRMSG(wcs_linerr[istat])); + goto cleanup; + } + + } else { + // Distortions present, get the status return for each coordinate. + int disaxes = 0; + + const double *img = imgcrd; + double *pix = pixcrd; + int *statp = stat; + for (int k = 0 ; k < ncoord; k++, pix += nelem, img += nelem, statp++) { + int istat = linx2p(lin, 1, nelem, img, pix); + if (istat) { + status = wcserr_set(WCS_ERRMSG(wcs_linerr[istat])); + if (status != WCSERR_BAD_WORLD) { + goto cleanup; + } + + if (disaxes == 0) { + // Which axes have distortions? + struct disprm *dispre = lin->dispre; + struct disprm *disseq = lin->disseq; + for (int i = 0; i < wcs->naxis; i++) { + if (dispre && dispre->disp2x[i]) { + disaxes |= (1 << i); + } else if (disseq && disseq->disp2x[i]) { + disaxes |= (1 << i); + } + } + + if (disaxes == 0) { + // Shouldn't happen. + disaxes = (2 << wcs->naxis) - 1; + } + } + + // WCSERR_BAD_WORLD stat[] vector accounting. + *statp = disaxes; + } + } } cleanup: @@ -2514,7 +3997,7 @@ int wcss2p( return status; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- int wcsmix( struct wcsprm *wcs, @@ -2533,72 +4016,68 @@ int wcsmix( static const char *function = "wcsmix"; const int niter = 60; - int crossed, istep, iter, j, k, nstep, retry, stat[1], status; const double tol = 1.0e-10; const double tol2 = 100.0*tol; - double *worldlat, *worldlng; - double lambda, span[2], step; - double pixmix; - double dlng, lng, lng0, lng0m, lng1, lng1m; - double dlat, lat, lat0, lat0m, lat1, lat1m; - double d, d0, d0m, d1, d1m, dx = 0.0; - double dabs, dmin, lmin; - double dphi, phi0, phi1; - struct celprm *wcscel = &(wcs->cel); - struct wcsprm wcs0; - struct wcserr **err; - /* Initialize if required. */ + // Initialize if required. if (wcs == 0x0) return WCSERR_NULL_POINTER; - err = &(wcs->err); + struct wcserr **err = &(wcs->err); - if (wcs->flag != WCSSET) { + int status; + if (abs(wcs->flag) != WCSSET) { if ((status = wcsset(wcs))) return status; } - worldlng = world + wcs->lng; - worldlat = world + wcs->lat; + if (wcs->lng < 0 || wcs->lat < 0) { + return wcserr_set(WCSERR_SET(WCSERR_BAD_SUBIMAGE), + "Image does not have celestial axes"); + } + + double *worldlng = world + wcs->lng; + double *worldlat = world + wcs->lat; - /* Check vspan. */ + // Check vspan. + double span[2]; if (vspan[0] <= vspan[1]) { span[0] = vspan[0]; span[1] = vspan[1]; } else { - /* Swap them. */ + // Swap them. span[0] = vspan[1]; span[1] = vspan[0]; } - /* Check vstep. */ - step = fabs(vstep); + // Check vstep. + double step = fabs(vstep); if (step == 0.0) { step = (span[1] - span[0])/10.0; if (step > 1.0 || step == 0.0) step = 1.0; } - /* Check viter. */ - nstep = viter; + // Check viter. + int nstep = viter; if (nstep < 5) { nstep = 5; } else if (nstep > 10) { nstep = 10; } - /* Given pixel element. */ - pixmix = pixcrd[mixpix]; + // Given pixel element. + double pixmix = pixcrd[mixpix]; - /* Iterate on the step size. */ - for (istep = 0; istep <= nstep; istep++) { + // Iterate on the step size. + for (int istep = 0; istep <= nstep; istep++) { if (istep) step /= 2.0; - /* Iterate on the sky coordinate between the specified range. */ + // Iterate on the sky coordinate between the specified range. if (mixcel == 1) { - /* Celestial longitude is given. */ + // Celestial longitude is given. - /* Check whether the solution interval is a crossing interval. */ - lat0 = span[0]; + // Check whether the solution interval is a crossing interval. + double lat0 = span[0]; *worldlat = lat0; + int stat[1]; if ((status = wcss2p(wcs, 1, 0, world, phi, theta, imgcrd, pixcrd, stat))) { if (status == WCSERR_BAD_WORLD) { @@ -2606,12 +4085,12 @@ int wcsmix( } return status; } - d0 = pixcrd[mixpix] - pixmix; + double d0 = pixcrd[mixpix] - pixmix; - dabs = fabs(d0); + double dabs = fabs(d0); if (dabs < tol) return 0; - lat1 = span[1]; + double lat1 = span[1]; *worldlat = lat1; if ((status = wcss2p(wcs, 1, 0, world, phi, theta, imgcrd, pixcrd, stat))) { @@ -2620,15 +4099,17 @@ int wcsmix( } return status; } - d1 = pixcrd[mixpix] - pixmix; + double d1 = pixcrd[mixpix] - pixmix; dabs = fabs(d1); if (dabs < tol) return 0; - lmin = lat1; - dmin = dabs; + double lmin = lat1; + double dmin = dabs; - /* Check for a crossing point. */ + // Check for a crossing point. + int crossed; + double dx = 0.0; if (signbit(d0) != signbit(d1)) { crossed = 1; dx = d1; @@ -2637,8 +4118,8 @@ int wcsmix( lat0 = span[1]; } - for (retry = 0; retry < 4; retry++) { - /* Refine the solution interval. */ + for (int retry = 0; retry < 4; retry++) { + // Refine the solution interval. while (lat0 > span[0]) { lat0 -= step; if (lat0 < span[0]) lat0 = span[0]; @@ -2652,41 +4133,41 @@ int wcsmix( } d0 = pixcrd[mixpix] - pixmix; - /* Check for a solution. */ + // Check for a solution. dabs = fabs(d0); if (dabs < tol) return 0; - /* Record the point of closest approach. */ + // Record the point of closest approach. if (dabs < dmin) { lmin = lat0; dmin = dabs; } - /* Check for a crossing point. */ + // Check for a crossing point. if (signbit(d0) != signbit(d1)) { crossed = 2; dx = d0; break; } - /* Advance to the next subinterval. */ + // Advance to the next subinterval. lat1 = lat0; d1 = d0; } if (crossed) { - /* A crossing point was found. */ - for (iter = 0; iter < niter; iter++) { - /* Use regula falsi division of the interval. */ - lambda = d0/(d0-d1); + // A crossing point was found. + for (int iter = 0; iter < niter; iter++) { + // Use regula falsi division of the interval. + double lambda = d0/(d0-d1); if (lambda < 0.1) { lambda = 0.1; } else if (lambda > 0.9) { lambda = 0.9; } - dlat = lat1 - lat0; - lat = lat0 + lambda*dlat; + double dlat = lat1 - lat0; + double lat = lat0 + lambda*dlat; *worldlat = lat; if ((status = wcss2p(wcs, 1, 0, world, phi, theta, imgcrd, pixcrd, stat))) { @@ -2696,20 +4177,20 @@ int wcsmix( return status; } - /* Check for a solution. */ - d = pixcrd[mixpix] - pixmix; + // Check for a solution. + double d = pixcrd[mixpix] - pixmix; dabs = fabs(d); if (dabs < tol) return 0; if (dlat < tol) { - /* An artifact of numerical imprecision. */ + // An artifact of numerical imprecision. if (dabs < tol2) return 0; - /* Must be a discontinuity. */ + // Must be a discontinuity. break; } - /* Record the point of closest approach. */ + // Record the point of closest approach. if (dabs < dmin) { lmin = lat; dmin = dabs; @@ -2724,18 +4205,18 @@ int wcsmix( } } - /* No convergence, must have been a discontinuity. */ + // No convergence, must have been a discontinuity. if (crossed == 1) lat0 = span[1]; lat1 = lat0; d1 = dx; crossed = 0; } else { - /* No crossing point; look for a tangent point. */ + // No crossing point; look for a tangent point. if (lmin == span[0]) break; if (lmin == span[1]) break; - lat = lmin; + double lat = lmin; lat0 = lat - step; if (lat0 < span[0]) lat0 = span[0]; lat1 = lat + step; @@ -2751,7 +4232,7 @@ int wcsmix( } d0 = fabs(pixcrd[mixpix] - pixmix); - d = dmin; + double d = dmin; *worldlat = lat1; if ((status = wcss2p(wcs, 1, 0, world, phi, theta, imgcrd, pixcrd, @@ -2763,8 +4244,8 @@ int wcsmix( } d1 = fabs(pixcrd[mixpix] - pixmix); - for (iter = 0; iter < niter; iter++) { - lat0m = (lat0 + lat)/2.0; + for (int iter = 0; iter < niter; iter++) { + double lat0m = (lat0 + lat)/2.0; *worldlat = lat0m; if ((status = wcss2p(wcs, 1, 0, world, phi, theta, imgcrd, pixcrd, stat))) { @@ -2773,11 +4254,11 @@ int wcsmix( } return status; } - d0m = fabs(pixcrd[mixpix] - pixmix); + double d0m = fabs(pixcrd[mixpix] - pixmix); if (d0m < tol) return 0; - lat1m = (lat1 + lat)/2.0; + double lat1m = (lat1 + lat)/2.0; *worldlat = lat1m; if ((status = wcss2p(wcs, 1, 0, world, phi, theta, imgcrd, pixcrd, stat))) { @@ -2786,7 +4267,7 @@ int wcsmix( } return status; } - d1m = fabs(pixcrd[mixpix] - pixmix); + double d1m = fabs(pixcrd[mixpix] - pixmix); if (d1m < tol) return 0; @@ -2811,11 +4292,12 @@ int wcsmix( } } else { - /* Celestial latitude is given. */ + // Celestial latitude is given. - /* Check whether the solution interval is a crossing interval. */ - lng0 = span[0]; + // Check whether the solution interval is a crossing interval. + double lng0 = span[0]; *worldlng = lng0; + int stat[1]; if ((status = wcss2p(wcs, 1, 0, world, phi, theta, imgcrd, pixcrd, stat))) { if (status == WCSERR_BAD_WORLD) { @@ -2823,12 +4305,12 @@ int wcsmix( } return status; } - d0 = pixcrd[mixpix] - pixmix; + double d0 = pixcrd[mixpix] - pixmix; - dabs = fabs(d0); + double dabs = fabs(d0); if (dabs < tol) return 0; - lng1 = span[1]; + double lng1 = span[1]; *worldlng = lng1; if ((status = wcss2p(wcs, 1, 0, world, phi, theta, imgcrd, pixcrd, stat))) { @@ -2837,14 +4319,16 @@ int wcsmix( } return status; } - d1 = pixcrd[mixpix] - pixmix; + double d1 = pixcrd[mixpix] - pixmix; dabs = fabs(d1); if (dabs < tol) return 0; - lmin = lng1; - dmin = dabs; + double lmin = lng1; + double dmin = dabs; - /* Check for a crossing point. */ + // Check for a crossing point. + int crossed; + double dx = 0.0; if (signbit(d0) != signbit(d1)) { crossed = 1; dx = d1; @@ -2853,8 +4337,8 @@ int wcsmix( lng0 = span[1]; } - for (retry = 0; retry < 4; retry++) { - /* Refine the solution interval. */ + for (int retry = 0; retry < 4; retry++) { + // Refine the solution interval. while (lng0 > span[0]) { lng0 -= step; if (lng0 < span[0]) lng0 = span[0]; @@ -2868,41 +4352,41 @@ int wcsmix( } d0 = pixcrd[mixpix] - pixmix; - /* Check for a solution. */ + // Check for a solution. dabs = fabs(d0); if (dabs < tol) return 0; - /* Record the point of closest approach. */ + // Record the point of closest approach. if (dabs < dmin) { lmin = lng0; dmin = dabs; } - /* Check for a crossing point. */ + // Check for a crossing point. if (signbit(d0) != signbit(d1)) { crossed = 2; dx = d0; break; } - /* Advance to the next subinterval. */ + // Advance to the next subinterval. lng1 = lng0; d1 = d0; } if (crossed) { - /* A crossing point was found. */ - for (iter = 0; iter < niter; iter++) { - /* Use regula falsi division of the interval. */ - lambda = d0/(d0-d1); + // A crossing point was found. + for (int iter = 0; iter < niter; iter++) { + // Use regula falsi division of the interval. + double lambda = d0/(d0-d1); if (lambda < 0.1) { lambda = 0.1; } else if (lambda > 0.9) { lambda = 0.9; } - dlng = lng1 - lng0; - lng = lng0 + lambda*dlng; + double dlng = lng1 - lng0; + double lng = lng0 + lambda*dlng; *worldlng = lng; if ((status = wcss2p(wcs, 1, 0, world, phi, theta, imgcrd, pixcrd, stat))) { @@ -2912,20 +4396,20 @@ int wcsmix( return status; } - /* Check for a solution. */ - d = pixcrd[mixpix] - pixmix; + // Check for a solution. + double d = pixcrd[mixpix] - pixmix; dabs = fabs(d); if (dabs < tol) return 0; if (dlng < tol) { - /* An artifact of numerical imprecision. */ + // An artifact of numerical imprecision. if (dabs < tol2) return 0; - /* Must be a discontinuity. */ + // Must be a discontinuity. break; } - /* Record the point of closest approach. */ + // Record the point of closest approach. if (dabs < dmin) { lmin = lng; dmin = dabs; @@ -2940,18 +4424,18 @@ int wcsmix( } } - /* No convergence, must have been a discontinuity. */ + // No convergence, must have been a discontinuity. if (crossed == 1) lng0 = span[1]; lng1 = lng0; d1 = dx; crossed = 0; } else { - /* No crossing point; look for a tangent point. */ + // No crossing point; look for a tangent point. if (lmin == span[0]) break; if (lmin == span[1]) break; - lng = lmin; + double lng = lmin; lng0 = lng - step; if (lng0 < span[0]) lng0 = span[0]; lng1 = lng + step; @@ -2967,7 +4451,7 @@ int wcsmix( } d0 = fabs(pixcrd[mixpix] - pixmix); - d = dmin; + double d = dmin; *worldlng = lng1; if ((status = wcss2p(wcs, 1, 0, world, phi, theta, imgcrd, pixcrd, @@ -2979,8 +4463,8 @@ int wcsmix( } d1 = fabs(pixcrd[mixpix] - pixmix); - for (iter = 0; iter < niter; iter++) { - lng0m = (lng0 + lng)/2.0; + for (int iter = 0; iter < niter; iter++) { + double lng0m = (lng0 + lng)/2.0; *worldlng = lng0m; if ((status = wcss2p(wcs, 1, 0, world, phi, theta, imgcrd, pixcrd, stat))) { @@ -2989,11 +4473,11 @@ int wcsmix( } return status; } - d0m = fabs(pixcrd[mixpix] - pixmix); + double d0m = fabs(pixcrd[mixpix] - pixmix); if (d0m < tol) return 0; - lng1m = (lng1 + lng)/2.0; + double lng1m = (lng1 + lng)/2.0; *worldlng = lng1m; if ((status = wcss2p(wcs, 1, 0, world, phi, theta, imgcrd, pixcrd, stat))) { @@ -3002,7 +4486,7 @@ int wcsmix( } return status; } - d1m = fabs(pixcrd[mixpix] - pixmix); + double d1m = fabs(pixcrd[mixpix] - pixmix); if (d1m < tol) return 0; @@ -3029,20 +4513,22 @@ int wcsmix( } - /* Set cel0 to the unity transformation. */ - wcs0 = *wcs; + // Set cel0 to the unity transformation. + struct wcsprm wcs0 = *wcs; wcs0.cel.euler[0] = -90.0; wcs0.cel.euler[1] = 0.0; wcs0.cel.euler[2] = 90.0; wcs0.cel.euler[3] = 1.0; wcs0.cel.euler[4] = 0.0; - /* No convergence, check for aberrant behaviour at a native pole. */ + // No convergence, check for aberrant behaviour at a native pole. *theta = -90.0; - for (j = 1; j <= 2; j++) { - /* Could the celestial coordinate element map to a native pole? */ + for (int j = 1; j <= 2; j++) { + // Could the celestial coordinate element map to a native pole? *phi = 0.0; *theta = -*theta; + struct celprm *wcscel = &(wcs->cel); + double lng, lat; sphx2s(wcscel->euler, 1, 1, 1, 1, phi, theta, &lng, &lat); if (mixcel == 1) { @@ -3059,40 +4545,42 @@ int wcsmix( *worldlng = lng; } - /* Is there a solution for the given pixel coordinate element? */ + // Is there a solution for the given pixel coordinate element? lng = *worldlng; lat = *worldlat; - /* Feed native coordinates to wcss2p() with cel0 set to unity. */ + // Feed native coordinates to wcss2p() with cel0 set to unity. *worldlng = -180.0; *worldlat = *theta; + int stat[1]; if ((status = wcss2p(&wcs0, 1, 0, world, phi, theta, imgcrd, pixcrd, stat))) { - if (wcs->err) free(wcs->err); + wcserr_clear(err); wcs->err = wcs0.err; if (status == WCSERR_BAD_WORLD) { status = wcserr_set(WCS_ERRMSG(WCSERR_BAD_WORLD_COORD)); } return status; } - d0 = pixcrd[mixpix] - pixmix; + double d0 = pixcrd[mixpix] - pixmix; - /* Check for a solution. */ + // Check for a solution. if (fabs(d0) < tol) { - /* Recall saved world coordinates. */ + // Recall saved world coordinates. *worldlng = lng; *worldlat = lat; return 0; } - /* Search for a crossing interval. */ - phi0 = -180.0; - for (k = -179; k <= 180; k++) { - phi1 = (double) k; + // Search for a crossing interval. + double phi0 = -180.0, phi1; + double d1; + for (int k = -179; k <= 180; k++) { + phi1 = (double)k; *worldlng = phi1; if ((status = wcss2p(&wcs0, 1, 0, world, phi, theta, imgcrd, pixcrd, stat))) { - if (wcs->err) free(wcs->err); + wcserr_clear(err); wcs->err = wcs0.err; if (status == WCSERR_BAD_WORLD) { status = wcserr_set(WCS_ERRMSG(WCSERR_BAD_WORLD_COORD)); @@ -3101,36 +4589,36 @@ int wcsmix( } d1 = pixcrd[mixpix] - pixmix; - /* Check for a solution. */ - dabs = fabs(d1); + // Check for a solution. + double dabs = fabs(d1); if (dabs < tol) { - /* Recall saved world coordinates. */ + // Recall saved world coordinates. *worldlng = lng; *worldlat = lat; return 0; } - /* Is it a crossing interval? */ + // Is it a crossing interval? if (signbit(d0) != signbit(d1)) break; phi0 = phi1; d0 = d1; } - for (iter = 1; iter <= niter; iter++) { - /* Use regula falsi division of the interval. */ - lambda = d0/(d0-d1); + for (int iter = 1; iter <= niter; iter++) { + // Use regula falsi division of the interval. + double lambda = d0/(d0-d1); if (lambda < 0.1) { lambda = 0.1; } else if (lambda > 0.9) { lambda = 0.9; } - dphi = phi1 - phi0; + double dphi = phi1 - phi0; *worldlng = phi0 + lambda*dphi; if ((status = wcss2p(&wcs0, 1, 0, world, phi, theta, imgcrd, pixcrd, stat))) { - if (wcs->err) free(wcs->err); + wcserr_clear(err); wcs->err = wcs0.err; if (status == WCSERR_BAD_WORLD) { status = wcserr_set(WCS_ERRMSG(WCSERR_BAD_WORLD_COORD)); @@ -3138,11 +4626,11 @@ int wcsmix( return status; } - /* Check for a solution. */ - d = pixcrd[mixpix] - pixmix; - dabs = fabs(d); + // Check for a solution. + double d = pixcrd[mixpix] - pixmix; + double dabs = fabs(d); if (dabs < tol || (dphi < tol && dabs < tol2)) { - /* Recall saved world coordinates. */ + // Recall saved world coordinates. *worldlng = lng; *worldlat = lat; return 0; @@ -3159,11 +4647,199 @@ int wcsmix( } - /* No solution. */ + // No solution. return wcserr_set(WCS_ERRMSG(WCSERR_NO_SOLUTION)); } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- + +int wcsccs( + struct wcsprm *wcs, + double lng2P1, + double lat2P1, + double lng1P2, + const char *clng, + const char *clat, + const char *radesys, + double equinox, + const char *alt) + +{ + static const char *function = "wcsccs"; + + int status; + + // Initialize if required. + if (wcs == 0x0) return WCSERR_NULL_POINTER; + struct wcserr **err = &(wcs->err); + + if (abs(wcs->flag) != WCSSET) { + if ((status = wcsset(wcs))) return status; + } + + if (wcs->lng < 0 || wcs->lat < 0) { + return wcserr_set(WCSERR_SET(WCSERR_BAD_SUBIMAGE), + "Image does not have celestial axes"); + } + + // (lng1XX,lat1XX) ...longitude and latitude of XX in the old system. + // (lng2XX,lat2XX) ...longitude and latitude of XX in the new system. + // XX = NP ...natuve pole, + // P1 ...pole of the old system, + // P2 ...pole of the new system, + // FP ...fiducial point. + + // Set up the transformation from the old to the new system. + double euler12[5]; + euler12[0] = lng2P1; + euler12[1] = 90.0 - lat2P1; + euler12[2] = lng1P2; + euler12[3] = cosd(euler12[1]); + euler12[4] = sind(euler12[1]); + + // Transform coordinates of the fiducial point (FP) to the new system. + double lng1FP = wcs->crval[wcs->lng]; + double lat1FP = wcs->crval[wcs->lat]; + double lng2FP, lat2FP; + (void)sphx2s(euler12, 1, 1, 1, 1, &lng1FP, &lat1FP, &lng2FP, &lat2FP); + + // Compute native coordinates of the new pole (noting lat1P2 == lat2P1). + double phiP2, thetaP2; + (void)sphs2x(wcs->cel.euler, 1, 1, 1, 1, &lng1P2, &lat2P1, + &phiP2, &thetaP2); + + if (fabs(lat2FP) == 90.0 || fabs(thetaP2) == 90.0) { + // If one of the poles of the new system is at the fiducial point, then + // lng2FP is indeterminate, and if one of them is at the native pole, then + // phiP2 is indeterminate. We have to work harder to obtain these values. + + // Compute coordinates of the native pole (NP) in the old and new systems. + double phiNP = 0.0, thetaNP = 90.0; + double lng1NP, lat1NP; + (void)sphx2s(wcs->cel.euler, 1, 1, 1, 1, &phiNP, &thetaNP, + &lng1NP, &lat1NP); + + double lng2NP, lat2NP; + (void)sphx2s(euler12, 1, 1, 1, 1, &lng1NP, &lat1NP, &lng2NP, &lat2NP); + + // Native latitude and longitude of the fiducial point, (phi0,theta0). + double phiFP = wcs->cel.prj.phi0; + double thetaFP = wcs->cel.prj.theta0; + + if (fabs(lat2NP) == 90.0) { + // Following WCS Paper II equations (3) and (4), we are free to choose + // phiP2 and set lng2NP accordingly. So set phiP2 to its default value + // for the projection. + if (thetaFP < lat2FP) { + phiP2 = 0.0; + } else { + phiP2 = 180.0; + } + + // Compute coordinates in the old system of test point X. + double phiX = 0.0, thetaX = 0.0; + double lng1X, lat1X; + (void)sphx2s(wcs->cel.euler, 1, 1, 1, 1, &phiX, &thetaX, + &lng1X, &lat1X); + + // Ensure that lng1X is not indeterminate. + if (fabs(lat1X) == 90.0) { + phiX = 90.0; + (void)sphx2s(wcs->cel.euler, 1, 1, 1, 1, &phiX, &thetaX, + &lng1X, &lat1X); + } + + // Compute coordinates in the new system of test point X. + double lng2X, lat2X; + (void)sphx2s(euler12, 1, 1, 1, 1, &lng1X, &lat1X, &lng2X, &lat2X); + + // Apply WCS Paper II equations (3) and (4). + if (lat2NP == +90.0) { + lng2NP = lng2X + (phiP2 - phiX) + 180.0; + } else { + lng2NP = lng2X - (phiP2 - phiX); + } + + } else { + // For (lng2NP + 90, 0), WCS Paper II equation (5) reduces to + // phi = phiP2 - 90. + double lng2X = lng2NP + 90.0; + double lat2X = 0.0; + double lng1X, lat1X; + (void)sphs2x(euler12, 1, 1, 1, 1, &lng2X, &lat2X, &lng1X, &lat1X); + + double phiX, thetaX; + (void)sphs2x(wcs->cel.euler, 1, 1, 1, 1, &lng1X, &lat1X, + &phiX, &thetaX); + + phiP2 = phiX + 90.0; + } + + // Compute the longitude of the fiducial point in the new system. + double eulerN2[5]; + eulerN2[0] = lng2NP; + eulerN2[1] = 90.0 - lat2NP; + eulerN2[2] = phiP2; + eulerN2[3] = cosd(eulerN2[1]); + eulerN2[4] = sind(eulerN2[1]); + + (void)sphx2s(eulerN2, 1, 1, 1, 1, &phiFP, &thetaFP, &lng2FP, &lat2FP); + } + + // Update reference values in wcsprm. + wcs->crval[wcs->lng] = lng2FP; + wcs->crval[wcs->lat] = lat2FP; + wcs->lonpole = phiP2; + wcs->latpole = thetaP2; + + // Update wcsprm::ctype. + if (clng) { + strncpy(wcs->ctype[wcs->lng], clng, 4); + for (int i = 0; i < 4; i++) { + if (wcs->ctype[wcs->lng][i] == '\0') { + wcs->ctype[wcs->lng][i] = '-'; + } + } + } + + if (clat) { + strncpy(wcs->ctype[wcs->lat], clat, 4); + for (int i = 0; i < 4; i++) { + if (wcs->ctype[wcs->lat][i] == '\0') { + wcs->ctype[wcs->lat][i] = '-'; + } + } + } + + // Update auxiliary values. + if (strncmp(wcs->ctype[wcs->lng], "RA--", 4) == 0 && + strncmp(wcs->ctype[wcs->lat], "DEC-", 4) == 0) { + // Transforming to equatorial coordinates. + if (radesys) { + strncpy(wcs->radesys, radesys, 71); + } + + if (equinox != 0.0) { + wcs->equinox = equinox; + } + } else { + // Meaningless for other than equatorial coordinates. + memset(wcs->radesys, 0, 72); + wcs->equinox = UNDEFINED; + } + + if (alt && *alt) { + wcs->alt[0] = *alt; + } + + // Reset the struct. + wcs->flag = (wcs->flag == -WCSSET) ? 1 : 0; + if ((status = wcsset(wcs))) return status; + + return 0; +} + +//---------------------------------------------------------------------------- int wcssptr( struct wcsprm *wcs, @@ -3173,21 +4849,20 @@ int wcssptr( { static const char *function = "wcssptr"; - int j, status; - double cdelt, crval; - struct wcserr **err; + int status; - /* Initialize if required. */ + // Initialize if required. if (wcs == 0x0) return WCSERR_NULL_POINTER; - err = &(wcs->err); + struct wcserr **err = &(wcs->err); - if (wcs->flag != WCSSET) { + if (abs(wcs->flag) != WCSSET) { if ((status = wcsset(wcs))) return status; } + int j; if ((j = *i) < 0) { if ((j = wcs->spec) < 0) { - /* Look for a linear spectral axis. */ + // Look for a linear spectral axis. for (j = 0; j < wcs->naxis; j++) { if (wcs->types[j]/100 == 30) { break; @@ -3195,32 +4870,56 @@ int wcssptr( } if (j >= wcs->naxis) { - /* No spectral axis. */ + // No spectral axis. return wcserr_set(WCSERR_SET(WCSERR_BAD_SUBIMAGE), - "No spectral axis found."); + "No spectral axis found"); } } *i = j; } - /* Translate the spectral axis. */ - if (spctrne(wcs->ctype[j], wcs->crval[j], wcs->cdelt[j], wcs->restfrq, - wcs->restwav, ctype, &crval, &cdelt, &(wcs->spc.err))) { - return wcserr_set(WCS_ERRMSG(WCSERR_BAD_COORD_TRANS)); + // Translate the spectral axis. + double cdelt, crval; + if ((status = spctrne(wcs->ctype[j], wcs->crval[j], wcs->cdelt[j], + wcs->restfrq, wcs->restwav, ctype, &crval, &cdelt, + &(wcs->spc.err)))) { + return wcserr_set(WCS_ERRMSG(wcs_spcerr[status])); } - /* Translate keyvalues. */ - wcs->flag = 0; + // Translate keyvalues. wcs->cdelt[j] = cdelt; wcs->crval[j] = crval; spctyp(ctype, 0x0, 0x0, 0x0, wcs->cunit[j], 0x0, 0x0, 0x0); strcpy(wcs->ctype[j], ctype); - /* This keeps things tidy if the spectral axis is linear. */ + // This keeps things tidy if the spectral axis is linear. spcfree(&(wcs->spc)); spcini(&(wcs->spc)); + // Reset the struct. + wcs->flag = (wcs->flag == -WCSSET) ? 1 : 0; + if ((status = wcsset(wcs))) return status; + return 0; } + +//---------------------------------------------------------------------------- + +#define STRINGIZE(s) STRINGIFY(s) +#define STRINGIFY(s) #s + +const char *wcslib_version( + int vers[3]) + +{ + static const char *wcsver = STRINGIZE(WCSLIB_VERSION); + + if (vers != 0x0) { + vers[2] = 0; + sscanf(wcsver, "%d.%d.%d", vers, vers+1, vers+2); + } + + return wcsver; +} diff --git a/cextern/wcslib/C/wcs.h b/cextern/wcslib/C/wcs.h index 5eac8217f180..a724c223fb19 100644 --- a/cextern/wcslib/C/wcs.h +++ b/cextern/wcslib/C/wcs.h @@ -1,7 +1,6 @@ /*============================================================================ - - WCSLIB 4.14 - an implementation of the FITS WCS standard. - Copyright (C) 1995-2012, Mark Calabretta + WCSLIB 8.6 - an implementation of the FITS WCS standard. + Copyright (C) 1995-2026, Mark Calabretta This file is part of WCSLIB. @@ -18,46 +17,59 @@ You should have received a copy of the GNU Lesser General Public License along with WCSLIB. If not, see http://www.gnu.org/licenses. - Direct correspondence concerning WCSLIB to mark@calabretta.id.au - Author: Mark Calabretta, Australia Telescope National Facility, CSIRO. - http://www.atnf.csiro.au/people/Mark.Calabretta - $Id: wcs.h,v 4.14 2012/07/13 10:02:27 cal103 Exp $ + http://www.atnf.csiro.au/computing/software/wcs + $Id: wcs.h,v 8.6 2026/03/29 13:53:56 mcalabre Exp $ *============================================================================= * -* WCSLIB 4.14 - C routines that implement the FITS World Coordinate System -* (WCS) standard. Refer to -* -* "Representations of world coordinates in FITS", -* Greisen, E.W., & Calabretta, M.R. 2002, A&A, 395, 1061 (Paper I) -* -* "Representations of celestial coordinates in FITS", -* Calabretta, M.R., & Greisen, E.W. 2002, A&A, 395, 1077 (Paper II) -* -* "Representations of spectral coordinates in FITS", -* Greisen, E.W., Calabretta, M.R., Valdes, F.G., & Allen, S.L. -* 2006, A&A, 446, 747 (Paper III) -* -* Refer to the README file provided with WCSLIB for an overview of the -* library. +* WCSLIB 8.6 - C routines that implement the FITS World Coordinate System +* (WCS) standard. Refer to the README file provided with WCSLIB for an +* overview of the library. * * * Summary of the wcs routines * --------------------------- -* These routines implement the FITS World Coordinate System (WCS) standard -* which defines methods to be used for computing world coordinates from image -* pixel coordinates, and vice versa. They are based on the wcsprm struct -* which contains all information needed for the computations. The struct -* contains some members that must be set by the user, and others that are -* maintained by these routines, somewhat like a C++ class but with no -* encapsulation. -* -* Three routines, wcsini(), wcssub(), and wcsfree() are provided to manage the -* wcsprm struct and another, wcsprt(), to prints its contents. Refer to the -* description of the wcsprm struct for an explanation of the anticipated usage -* of these routines. wcscopy(), which does a deep copy of one wcsprm struct -* to another, is defined as a preprocessor macro function that invokes -* wcssub(). +* Routines in this suite implement the FITS World Coordinate System (WCS) +* standard which defines methods to be used for computing world coordinates +* from image pixel coordinates, and vice versa. The standard, and proposed +* extensions for handling distortions, are described in +* += "Representations of world coordinates in FITS", += Greisen, E.W., & Calabretta, M.R. 2002, A&A, 395, 1061 (WCS Paper I) += += "Representations of celestial coordinates in FITS", += Calabretta, M.R., & Greisen, E.W. 2002, A&A, 395, 1077 (WCS Paper II) += += "Representations of spectral coordinates in FITS", += Greisen, E.W., Calabretta, M.R., Valdes, F.G., & Allen, S.L. += 2006, A&A, 446, 747 (WCS Paper III) += += "Representations of distortions in FITS world coordinate systems", += Calabretta, M.R. et al. (WCS Paper IV, draft dated 2004/04/22), += available from http://www.atnf.csiro.au/computing/software/wcs += += "Mapping on the HEALPix grid", += Calabretta, M.R., & Roukema, B.F. 2007, MNRAS, 381, 865 (WCS Paper V) += += "Representing the 'Butterfly' Projection in FITS -- Projection Code XPH", += Calabretta, M.R., & Lowe, S.R. 2013, PASA, 30, e050 (WCS Paper VI) += += "Representations of time coordinates in FITS - += Time and relative dimension in space", += Rots, A.H., Bunclark, P.S., Calabretta, M.R., Allen, S.L., += Manchester, R.N., & Thompson, W.T. 2015, A&A, 574, A36 (WCS Paper VII) +* +* These routines are based on the wcsprm struct which contains all information +* needed for the computations. The struct contains some members that must be +* set by the user, and others that are maintained by these routines, somewhat +* like a C++ class but with no encapsulation. +* +* wcsnpv(), wcsnps(), wcsini(), wcsinit(), wcssub(), wcsfree(), and wcstrim(), +* are provided to manage the wcsprm struct, wcssize() computes its total size +* including allocated memory, wcsenq() returns information about the state of +* the struct, and wcsprt() prints its contents. wcscopy(), which does a deep +* copy of one wcsprm struct to another, is defined as a preprocessor macro +* function that invokes wcssub(). * * wcsperr() prints the error message(s) (if any) stored in a wcsprm struct, * and the linprm, celprm, prjprm, spcprm, and tabprm structs that it contains. @@ -76,8 +88,12 @@ * pixel coordinate a hybrid routine, wcsmix(), iteratively solves for the * unknown elements. * -* wcssptr() translates the spectral axis in a wcsprm struct. For example, a -* 'FREQ' axis may be translated into 'ZOPT-F2W' and vice versa. +* wcsccs() changes the celestial coordinate system of a wcsprm struct, for +* example, from equatorial to galactic, and wcssptr() translates the spectral +* axis. For example, a 'FREQ' axis may be translated into 'ZOPT-F2W' and vice +* versa. +* +* wcslib_version() returns the WCSLIB version number. * * Quadcube projections: * --------------------- @@ -107,21 +123,65 @@ * routines. * * +* wcsnpv() - Memory allocation for PVi_ma +* --------------------------------------- +* wcsnpv() sets or gets the value of NPVMAX (default 64). This global +* variable controls the number of pvcard structs, for holding PVi_ma +* keyvalues, that wcsini() should allocate space for. It is also used by +* wcsinit() as the default value of npvmax. +* +* PLEASE NOTE: This function is not thread-safe. +* +* Given: +* n int Value of NPVMAX; ignored if < 0. Use a value less +* than zero to get the current value. +* +* Function return value: +* int Current value of NPVMAX. +* +* +* wcsnps() - Memory allocation for PSi_ma +* --------------------------------------- +* wcsnps() sets or gets the value of NPSMAX (default 8). This global variable +* controls the number of pscard structs, for holding PSi_ma keyvalues, that +* wcsini() should allocate space for. It is also used by wcsinit() as the +* default value of npsmax. +* +* PLEASE NOTE: This function is not thread-safe. +* +* Given: +* n int Value of NPSMAX; ignored if < 0. Use a value less +* than zero to get the current value. +* +* Function return value: +* int Current value of NPSMAX. +* +* * wcsini() - Default constructor for the wcsprm struct * ---------------------------------------------------- -* wcsini() optionally allocates memory for arrays in a wcsprm struct and sets -* all members of the struct to default values. Memory is allocated for up to -* NPVMAX PVi_ma keywords or NPSMAX PSi_ma keywords per WCS representation. -* These may be changed via wcsnpv() and wcsnps() before wcsini() is called. +* wcsini() is a thin wrapper on wcsinit(). It invokes it with npvmax, +* npsmax, and ndpmax set to -1 which causes it to use the values of the +* global variables NDPMAX, NPSMAX, and NDPMAX. It is thereby potentially +* thread-unsafe if these variables are altered dynamically via wcsnpv(), +* wcsnps(), and disndp(). Use wcsinit() for a thread-safe alternative in +* this case. +* +* +* wcsinit() - Default constructor for the wcsprm struct +* ----------------------------------------------------- +* wcsinit() optionally allocates memory for arrays in a wcsprm struct and sets +* all members of the struct to default values. * -* PLEASE NOTE: every wcsprm struct should be initialized by wcsini(), possibly -* repeatedly. On the first invokation, and only the first invokation, -* wcsprm::flag must be set to -1 to initialize memory management, regardless -* of whether wcsini() will actually be used to allocate memory. +* PLEASE NOTE: every wcsprm struct should be initialized by wcsinit(), +* possibly repeatedly. On the first invokation, and only the first +* invokation, wcsprm::flag must be set to -1 to initialize memory management, +* regardless of whether wcsinit() will actually be used to allocate memory. * * Given: * alloc int If true, allocate memory unconditionally for the -* crpix, etc. arrays. +* crpix, etc. arrays. Please note that memory is never +* allocated by wcsinit() for the auxprm, tabprm, nor +* wtbarr structs. * * If false, it is assumed that pointers to these arrays * have been set by the user except if they are null @@ -143,6 +203,23 @@ * initialized for the first time (memory leaks may * result if it had already been initialized). * +* Given: +* npvmax int The number of PVi_ma keywords to allocate space for. +* If set to -1, the value of the global variable NPVMAX +* will be used. This is potentially thread-unsafe if +* wcsnpv() is being used dynamically to alter its value. +* +* npsmax int The number of PSi_ma keywords to allocate space for. +* If set to -1, the value of the global variable NPSMAX +* will be used. This is potentially thread-unsafe if +* wcsnps() is being used dynamically to alter its value. +* +* ndpmax int The number of DPja or DQia keywords to allocate space +* for. If set to -1, the value of the global variable +* NDPMAX will be used. This is potentially +* thread-unsafe if disndp() is being used dynamically to +* alter its value. +* * Function return value: * int Status return value: * 0: Success. @@ -153,58 +230,60 @@ * wcsprm::err if enabled, see wcserr_enable(). * * -* wcsnpv() - Memory allocation for PVi_ma -* --------------------------------------- -* wcsnpv() changes the value of NPVMAX (default 64). This global variable -* controls the number of PVi_ma keywords that wcsini() should allocate space -* for. -* -* PLEASE NOTE: This function is not thread-safe. +* wcsauxi() - Default constructor for the auxprm struct +* ----------------------------------------------------- +* wcsauxi() optionally allocates memory for an auxprm struct, attaches it to +* wcsprm, and sets all members of the struct to default values. * * Given: -* n int Value of NPVMAX; ignored if < 0. -* -* Function return value: -* int Current value of NPVMAX. -* -* -* wcsnps() - Memory allocation for PSi_ma -* --------------------------------------- -* wcsnps() changes the values of NPSMAX (default 8). This global variable -* controls the number of PSi_ma keywords that wcsini() should allocate space -* for. +* alloc int If true, allocate memory unconditionally for the +* auxprm struct. * -* PLEASE NOTE: This function is not thread-safe. +* If false, it is assumed that wcsprm::aux has already +* been set to point to an auxprm struct, in which case +* the user is responsible for managing that memory. +* However, if wcsprm::aux is a null pointer, memory will +* be allocated regardless. (In other words, setting +* alloc true saves having to initalize the pointer to +* zero.) * -* Given: -* n int Value of NPSMAX; ignored if < 0. +* Given and returned: +* wcs struct wcsprm* +* Coordinate transformation parameters. * * Function return value: -* int Current value of NPSMAX. +* int Status return value: +* 0: Success. +* 1: Null wcsprm pointer passed. +* 2: Memory allocation failed. * * * wcssub() - Subimage extraction routine for the wcsprm struct * ------------------------------------------------------------ * wcssub() extracts the coordinate description for a subimage from a wcsprm -* struct. It does a deep copy, using wcsini() to allocate memory for its +* struct. It does a deep copy, using wcsinit() to allocate memory for its * arrays if required. Only the "information to be provided" part of the -* struct is extracted; a call to wcsset() is required to set up the remainder. +* struct is extracted. Consequently, wcsset() need not have been, and won't +* be invoked on the struct from which the subimage is extracted. A call to +* wcsset() is required to set up the subimage struct. * * The world coordinate system of the subimage must be separable in the sense * that the world coordinates at any point in the subimage must depend only on * the pixel coordinates of the axes extracted. In practice, this means that -* the PCi_ja matrix of the original image must not contain non-zero -* off-diagonal terms that associate any of the subimage axes with any of the -* non-subimage axes. +* the linear transformation matrix of the original image must not contain +* non-zero off-diagonal terms that associate any of the subimage axes with any +* of the non-subimage axes. Likewise, if any distortions are associated with +* the subimage axes, they must not depend on any of the axes that are not +* being extracted. * * Note that while the required elements of the tabprm array are extracted, the * wtbarr array is not. (Thus it is not appropriate to call wcssub() after * wcstab() but before filling the tabprm structs - refer to wcshdr.h.) * * wcssub() can also add axes to a wcsprm struct. The new axes will be created -* using the defaults set by wcsini() which produce a simple, unnamed, linear +* using the defaults set by wcsinit() which produce a simple, unnamed, linear * axis with world coordinate equal to the pixel coordinate. These default -* values can be changed in before invoking wcsset(). +* values can be changed afterwards, before invoking wcsset(). * * Given: * alloc int If true, allocate memory for the crpix, etc. arrays in @@ -225,17 +304,18 @@ * subimage, etc. * * Use an axis number of 0 to create a new axis using -* the defaults set by wcsini(). +* the defaults set by wcsinit(). They can be changed +* later. * * nsub (the pointer) may be set to zero, and so also may -* nsub, to indicate the number of axes in the input -* image; the number of axes will be returned if +* *nsub, which is interpreted to mean all axes in the +* input image; the number of axes will be returned if * nsub != 0x0. axes itself (the pointer) may be set to * zero to indicate the first *nsub axes in their * original order. * -* Set both nsub and axes to zero to do a deep copy of -* one wcsprm struct to another. +* Set both nsub (or *nsub) and axes to zero to do a deep +* copy of one wcsprm struct to another. * * Subimage extraction by coordinate axis type may be * done by setting the elements of axes[] to the @@ -246,10 +326,11 @@ * WCSSUB_CUBEFACE: Quadcube CUBEFACE axis. * WCSSUB_SPECTRAL: Spectral axis. * WCSSUB_STOKES: Stokes axis. +* WCSSUB_TIME: Time axis. * * Refer to the notes (below) for further usage examples. * -* On return, *nsub will contain the number of axes in +* On return, *nsub will be set to the number of axes in * the subimage; this may be zero if there were no axes * of the required type(s) (in which case no memory will * be allocated). axes[] will contain the axis numbers @@ -277,38 +358,81 @@ * wcsprm::err if enabled, see wcserr_enable(). * * Notes: -* Combinations of subimage axes of particular types may be extracted in the -* same order as they occur in the input image by combining preprocessor -* codes, for example +* 1: Combinations of subimage axes of particular types may be extracted in +* the same order as they occur in the input image by combining +* preprocessor codes, for example +* += *nsub = 1; += axes[0] = WCSSUB_LONGITUDE | WCSSUB_LATITUDE | WCSSUB_SPECTRAL; +* +* would extract the longitude, latitude, and spectral axes in the same +* order as the input image. If one of each were present, *nsub = 3 would +* be returned. +* +* For convenience, WCSSUB_CELESTIAL is defined as the combination +* WCSSUB_LONGITUDE | WCSSUB_LATITUDE | WCSSUB_CUBEFACE. * -= *nsub = 1; -= axes[0] = WCSSUB_LONGITUDE | WCSSUB_LATITUDE | WCSSUB_SPECTRAL; +* The codes may also be negated to extract all but the types specified, +* for example * -* would extract the longitude, latitude, and spectral axes in the same order -* as the input image. If one of each were present, *nsub = 3 would be -* returned. += *nsub = 4; += axes[0] = WCSSUB_LONGITUDE; += axes[1] = WCSSUB_LATITUDE; += axes[2] = WCSSUB_CUBEFACE; += axes[3] = -(WCSSUB_SPECTRAL | WCSSUB_STOKES); * -* For convenience, WCSSUB_CELESTIAL is defined as the combination -* WCSSUB_LONGITUDE | WCSSUB_LATITUDE | WCSSUB_CUBEFACE. +* The last of these specifies all axis types other than spectral or +* Stokes. Extraction is done in the order specified by axes[] a +* longitude axis (if present) would be extracted first (via axes[0]) and +* not subsequently (via axes[3]). Likewise for the latitude and cubeface +* axes in this example. * -* The codes may also be negated to extract all but the types specified, for -* example +* From the foregoing, it is apparent that the value of *nsub returned may +* be less than or greater than that given. However, it will never exceed +* the number of axes in the input image (plus the number of newly-created +* axes if any were specified on input). * -= *nsub = 4; -= axes[0] = WCSSUB_LONGITUDE; -= axes[1] = WCSSUB_LATITUDE; -= axes[2] = WCSSUB_CUBEFACE; -= axes[3] = -(WCSSUB_SPECTRAL | WCSSUB_STOKES); * -* The last of these specifies all axis types other than spectral or Stokes. -* Extraction is done in the order specified by axes[] a longitude axis (if -* present) would be extracted first (via axes[0]) and not subsequently (via -* axes[3]). Likewise for the latitude and cubeface axes in this example. +* wcscompare() - Compare two wcsprm structs for equality +* ------------------------------------------------------ +* wcscompare() compares two wcsprm structs for equality. * -* From the foregoing, it is apparent that the value of *nsub returned may be -* less than or greater than that given. However, it will never exceed the -* number of axes in the input image (plus the number of newly-created axes -* if any were specified on input). +* Given: +* cmp int A bit field controlling the strictness of the +* comparison. When 0, all fields must be identical. +* +* The following constants may be or'ed together to +* relax the comparison: +* WCSCOMPARE_ANCILLARY: Ignore ancillary keywords +* that don't change the WCS transformation, such +* as DATE-OBS or EQUINOX. +* WCSCOMPARE_TILING: Ignore integral differences in +* CRPIXja. This is the 'tiling' condition, where +* two WCSes cover different regions of the same +* map projection and align on the same map grid. +* WCSCOMPARE_CRPIX: Ignore any differences at all in +* CRPIXja. The two WCSes cover different regions +* of the same map projection but may not align on +* the same map grid. Overrides WCSCOMPARE_TILING. +* +* tol double Tolerance for comparison of floating-point values. +* For example, for tol == 1e-6, all floating-point +* values in the structs must be equal to the first 6 +* decimal places. A value of 0 implies exact equality. +* +* wcs1 const struct wcsprm* +* The first wcsprm struct to compare. +* +* wcs2 const struct wcsprm* +* The second wcsprm struct to compare. +* +* Returned: +* equal int* Non-zero when the given structs are equal. +* +* Function return value: +* int Status return value: +* 0: Success. +* 1: Null pointer passed. * * * wcscopy() macro - Copy routine for the wcsprm struct @@ -320,14 +444,14 @@ * * wcsfree() - Destructor for the wcsprm struct * -------------------------------------------- -* wcsfree() frees memory allocated for the wcsprm arrays by wcsini() and/or -* wcsset(). wcsini() records the memory it allocates and wcsfree() will only +* wcsfree() frees memory allocated for the wcsprm arrays by wcsinit() and/or +* wcsset(). wcsinit() records the memory it allocates and wcsfree() will only * attempt to free this. * * PLEASE NOTE: wcsfree() must not be invoked on a wcsprm struct that was not -* initialized by wcsini(). +* initialized by wcsinit(). * -* Returned: +* Given and returned: * wcs struct wcsprm* * Coordinate transformation parameters. * @@ -337,6 +461,120 @@ * 1: Null wcsprm pointer passed. * * +* wcstrim() - Free unused arrays in the wcsprm struct +* --------------------------------------------------- +* wcstrim() frees memory allocated by wcsinit() for arrays in the wcsprm +* struct that remains unused after it has been set up by wcsset(). +* +* The free'd array members are associated with FITS WCS keyrecords that are +* rarely used and usually just bloat the struct: wcsprm::crota, wcsprm::colax, +* wcsprm::cname, wcsprm::crder, wcsprm::csyer, wcsprm::czphs, and +* wcsprm::cperi. If unused, wcsprm::pv, wcsprm::ps, and wcsprm::cd are also +* freed. +* +* Once these arrays have been freed, a test such as += += if (!undefined(wcs->cname[i])) {...} += +* must be protected as follows += += if (wcs->cname && !undefined(wcs->cname[i])) {...} += +* In addition, if wcsprm::npv is non-zero but less than wcsprm::npvmax, then +* the unused space in wcsprm::pv will be recovered (using realloc()). +* Likewise for wcsprm::ps. +* +* Given and returned: +* wcs struct wcsprm* +* Coordinate transformation parameters. +* +* Function return value: +* int Status return value: +* 0: Success. +* 1: Null wcsprm pointer passed. +* 14: wcsprm struct is unset. +* +* +* wcssize() - Compute the size of a wcsprm struct +* ----------------------------------------------- +* wcssize() computes the full size of a wcsprm struct, including allocated +* memory. +* +* Given: +* wcs const struct wcsprm* +* Coordinate transformation parameters. +* +* If NULL, the base size of the struct and the allocated +* size are both set to zero. +* +* Returned: +* sizes int[2] The first element is the base size of the struct as +* returned by sizeof(struct wcsprm). The second element +* is the total allocated size, in bytes, assuming that +* the allocation was done by wcsini(). This figure +* includes memory allocated for members of constituent +* structs, such as wcsprm::lin. +* +* It is not an error for the struct not to have been set +* up via wcsset(), which normally results in additional +* memory allocation. +* +* Function return value: +* int Status return value: +* 0: Success. +* +* +* auxsize() - Compute the size of a auxprm struct +* ----------------------------------------------- +* auxsize() computes the full size of an auxprm struct, including allocated +* memory. +* +* Given: +* aux const struct auxprm* +* Auxiliary coordinate information. +* +* If NULL, the base size of the struct and the allocated +* size are both set to zero. +* +* Returned: +* sizes int[2] The first element is the base size of the struct as +* returned by sizeof(struct auxprm). The second element +* is the total allocated size, in bytes, currently zero. +* +* Function return value: +* int Status return value: +* 0: Success. +* +* +* wcsenq() - enquire about the state of a wcsprm struct +* ----------------------------------------------------- +* wcsenq() may be used to obtain information about the state of a wcsprm +* struct. The function returns a true/false answer for the enquiry asked. +* +* Given: +* wcs const struct wcsprm* +* Coordinate transformation parameters. +* +* enquiry int Enquiry according to the following parameters: +* WCSENQ_MEM: memory in the struct is being managed by +* WCSLIB (see wcsini()). +* WCSENQ_SET: the struct has been set up by wcsset(). +* WCSENQ_BYP: the struct is in bypass mode (see +* wcsset()). +* WCSENQ_CHK: the struct is self-consistent in that +* no changes have been made to any of the +* "parameters to be given" since the last +* call to wcsset(). +* These may be combined by logical OR, e.g. +* WCSENQ_MEM | WCSENQ_SET. The enquiry result will be +* the logical AND of the individual results. +* +* Function return value: +* int Enquiry result: +* 0: False. +* 1: True. +* +* * wcsprt() - Print routine for the wcsprm struct * ---------------------------------------------- * wcsprt() prints the contents of a wcsprm struct using wcsprintf(). Mainly @@ -372,6 +610,37 @@ * 1: Null wcsprm pointer passed. * * +* wcsbchk() - Enable/disable bounds checking +* ------------------------------------------ +* wcsbchk() is used to control bounds checking in the projection routines. +* Note that wcsset() always enables bounds checking. wcsbchk() will invoke +* wcsset() on the wcsprm struct beforehand if necessary. +* +* Given and returned: +* wcs struct wcsprm* +* Coordinate transformation parameters. +* +* Given: +* bounds int If bounds&1 then enable strict bounds checking for the +* spherical-to-Cartesian (s2x) transformation for the +* AZP, SZP, TAN, SIN, ZPN, and COP projections. +* +* If bounds&2 then enable strict bounds checking for the +* Cartesian-to-spherical (x2s) transformation for the +* HPX and XPH projections. +* +* If bounds&4 then enable bounds checking on the native +* coordinates returned by the Cartesian-to-spherical +* (x2s) transformations using prjchk(). +* +* Zero it to disable all checking. +* +* Function return value: +* int Status return value: +* 0: Success. +* 1: Null wcsprm pointer passed. +* +* * wcsset() - Setup routine for the wcsprm struct * ---------------------------------------------- * wcsset() sets up a wcsprm struct according to information supplied within @@ -386,6 +655,13 @@ * wcsp2s() and wcss2p() if the wcsprm::flag is anything other than a * predefined magic value. * +* wcsset() normally operates regardless of the value of wcsprm::flag; i.e. +* even if a struct was previously set up it will be reset unconditionally. +* However, a wcsprm struct may be put into "bypass" mode by invoking wcsset() +* initially with wcsprm::flag == 1 (rather than 0). wcsset() will return +* immediately if invoked on a struct in that state. To take a struct out of +* bypass mode, simply reset wcsprm::flag to zero. See also wcsenq(). +* * Given and returned: * wcs struct wcsprm* * Coordinate transformation parameters. @@ -406,6 +682,11 @@ * For returns > 1, a detailed error message is set in * wcsprm::err if enabled, see wcserr_enable(). * +* Notes: +* 1: wcsset() always enables strict bounds checking in the projection +* routines (via a call to prjini()). Use wcsbchk() to modify +* bounds-checking after wcsset() is invoked. +* * * wcsp2s() - Pixel-to-world transformation * ---------------------------------------- @@ -433,7 +714,9 @@ * imgcrd[][wcs.lat] are the projected x-, and * y-coordinates in pseudo "degrees". For spectral * axes, imgcrd[][wcs.spec] is the intermediate spectral -* coordinate, in SI units. +* coordinate, in SI units. For time axes, +* imgcrd[][wcs.time] is the intermediate time +* coordinate. * * phi,theta double[ncoord] * Longitude and latitude in the native coordinate system @@ -442,14 +725,15 @@ * world double[ncoord][nelem] * Array of world coordinates. For celestial axes, * world[][wcs.lng] and world[][wcs.lat] are the -* celestial longitude and latitude [deg]. For -* spectral axes, imgcrd[][wcs.spec] is the intermediate -* spectral coordinate, in SI units. +* celestial longitude and latitude [deg]. For spectral +* axes, world[][wcs.spec] is the spectral coordinate, in +* SI units. For time axes, world[][wcs.time] is the +* time coordinate. * * stat int[ncoord] * Status return value for each coordinate: * 0: Success. -* 1+: A bit mask indicating invalid pixel coordinate +* 1+: A bit mask indicating invalid pixel coordinate * element(s). * * Function return value: @@ -492,7 +776,8 @@ * world[][wcs.lng] and world[][wcs.lat] are the * celestial longitude and latitude [deg]. For spectral * axes, world[][wcs.spec] is the spectral coordinate, in -* SI units. +* SI units. For time axes, world[][wcs.time] is the +* time coordinate. * * Returned: * phi,theta double[ncoord] @@ -507,7 +792,9 @@ * projections with a CUBEFACE axis the face number is * also returned in imgcrd[][wcs.cubeface]. For * spectral axes, imgcrd[][wcs.spec] is the intermediate -* spectral coordinate, in SI units. +* spectral coordinate, in SI units. For time axes, +* imgcrd[][wcs.time] is the intermediate time +* coordinate. * * pixcrd double[ncoord][nelem] * Array of pixel coordinates. @@ -515,7 +802,7 @@ * stat int[ncoord] * Status return value for each coordinate: * 0: Success. -* 1+: A bit mask indicating invalid world coordinate +* 1+: A bit mask indicating invalid world coordinate * element(s). * * Function return value: @@ -621,31 +908,167 @@ * wcsprm::err if enabled, see wcserr_enable(). * * Notes: -* Initially the specified solution interval is checked to see if it's a -* "crossing" interval. If it isn't, a search is made for a crossing -* solution by iterating on the unknown celestial coordinate starting at the -* upper limit of the solution interval and decrementing by the specified -* step size. A crossing is indicated if the trial value of the pixel -* coordinate steps through the value specified. If a crossing interval is -* found then the solution is determined by a modified form of "regula falsi" -* division of the crossing interval. If no crossing interval was found -* within the specified solution interval then a search is made for a -* "non-crossing" solution as may arise from a point of tangency. The -* process is complicated by having to make allowance for the discontinuities -* that occur in all map projections. -* -* Once one solution has been determined others may be found by subsequent -* invokations of wcsmix() with suitably restricted solution intervals. -* -* Note the circumstance that arises when the solution point lies at a native -* pole of a projection in which the pole is represented as a finite curve, -* for example the zenithals and conics. In such cases two or more valid -* solutions may exist but wcsmix() only ever returns one. -* -* Because of its generality wcsmix() is very compute-intensive. For -* compute-limited applications more efficient special-case solvers could be -* written for simple projections, for example non-oblique cylindrical -* projections. +* 1: Initially the specified solution interval is checked to see if it's a +* "crossing" interval. If it isn't, a search is made for a crossing +* solution by iterating on the unknown celestial coordinate starting at +* the upper limit of the solution interval and decrementing by the +* specified step size. A crossing is indicated if the trial value of the +* pixel coordinate steps through the value specified. If a crossing +* interval is found then the solution is determined by a modified form of +* "regula falsi" division of the crossing interval. If no crossing +* interval was found within the specified solution interval then a search +* is made for a "non-crossing" solution as may arise from a point of +* tangency. The process is complicated by having to make allowance for +* the discontinuities that occur in all map projections. +* +* Once one solution has been determined others may be found by subsequent +* invokations of wcsmix() with suitably restricted solution intervals. +* +* Note the circumstance that arises when the solution point lies at a +* native pole of a projection in which the pole is represented as a +* finite curve, for example the zenithals and conics. In such cases two +* or more valid solutions may exist but wcsmix() only ever returns one. +* +* Because of its generality wcsmix() is very compute-intensive. For +* compute-limited applications more efficient special-case solvers could +* be written for simple projections, for example non-oblique cylindrical +* projections. +* +* +* wcsccs() - Change celestial coordinate system +* --------------------------------------------- +* wcsccs() changes the celestial coordinate system of a wcsprm struct. For +* example, from equatorial to galactic coordinates. +* +* Parameters that define the spherical coordinate transformation, essentially +* being three Euler angles, must be provided. Thereby wcsccs() does not need +* prior knowledge of specific celestial coordinate systems. It also has the +* advantage of making it completely general. +* +* Auxiliary members of the wcsprm struct relating to equatorial celestial +* coordinate systems may also be changed. +* +* Only orthodox spherical coordinate systems are supported. That is, they +* must be right-handed, with latitude increasing from zero at the equator to +* +90 degrees at the pole. This precludes systems such as aziumuth and zenith +* distance, which, however, could be handled as negative azimuth and +* elevation. +* +* PLEASE NOTE: Information in the wcsprm struct relating to the original +* coordinate system will be overwritten and therefore lost. If this is +* undesirable, invoke wcsccs() on a copy of the struct made with wcssub(). +* The wcsprm struct is reset on return with an explicit call to wcsset(). +* +* Given and returned: +* wcs struct wcsprm* +* Coordinate transformation parameters. Particular +* "values to be given" elements of the wcsprm struct +* are modified. +* +* Given: +* lng2p1, +* lat2p1 double Longitude and latitude in the new celestial coordinate +* system of the pole (i.e. latitude +90) of the original +* system [deg]. See notes 1 and 2 below. +* +* lng1p2 double Longitude in the original celestial coordinate system +* of the pole (i.e. latitude +90) of the new system +* [deg]. See note 1 below. +* +* clng,clat const char* +* Longitude and latitude identifiers of the new CTYPEia +* celestial axis codes, without trailing dashes. For +* example, "RA" and "DEC" or "GLON" and "GLAT". Up to +* four characters are used, longer strings need not be +* null-terminated. +* +* radesys const char* +* Used when transforming to equatorial coordinates, +* identified by clng == "RA" and clat = "DEC". May be +* set to the null pointer to preserve the current value. +* Up to 71 characters are used, longer strings need not +* be null-terminated. +* +* If the new coordinate system is anything other than +* equatorial, then wcsprm::radesys will be cleared. +* +* equinox double Used when transforming to equatorial coordinates. May +* be set to zero to preserve the current value. +* +* If the new coordinate system is not equatorial, then +* wcsprm::equinox will be marked as undefined. +* +* alt const char* +* Character code for alternate coordinate descriptions +* (i.e. the 'a' in keyword names such as CTYPEia). This +* is blank for the primary coordinate description, or +* one of the 26 upper-case letters, A-Z. May be set to +* the null pointer, or null string if no change is +* required. +* +* Function return value: +* int Status return value: +* 0: Success. +* 1: Null wcsprm pointer passed. +* 12: Invalid subimage specification (no celestial +* axes). +* +* Notes: +* 1: Follows the prescription given in WCS Paper II, Sect. 2.7 for changing +* celestial coordinates. +* +* The implementation takes account of indeterminacies that arise in that +* prescription in the particular cases where one of the poles of the new +* system is at the fiducial point, or one of them is at the native pole. +* +* 2: If lat2p1 == +90, i.e. where the poles of the two coordinate systems +* coincide, then the spherical coordinate transformation becomes a simple +* change in origin of longitude given by +* lng2 = lng1 + (lng2p1 - lng1p2 - 180), and lat2 = lat1, where +* (lng2,lat2) are coordinates in the new system, and (lng1,lat1) are +* coordinates in the original system. +* +* Likewise, if lat2p1 == -90, then lng2 = -lng1 + (lng2p1 + lng1p2), and +* lat2 = -lat1. +* +* 3: For example, if the original coordinate system is B1950 equatorial and +* the desired new coordinate system is galactic, then +* +* - (lng2p1,lat2p1) are the galactic coordinates of the B1950 celestial +* pole, defined by the IAU to be (123.0,+27.4), and lng1p2 is the B1950 +* right ascension of the galactic pole, defined as 192.25. Clearly +* these coordinates are fixed for a particular coordinate +* transformation. +* +* - (clng,clat) would be 'GLON' and 'GLAT', these being the FITS standard +* identifiers for galactic coordinates. +* +* - Since the new coordinate system is not equatorial, wcsprm::radesys +* and wcsprm::equinox will be cleared. +* +* 4. The coordinates required for some common transformations (obtained from +* https://ned.ipac.caltech.edu/coordinate_calculator) are as follows: +* += (123.0000,+27.4000) galactic coordinates of B1950 celestial pole, += (192.2500,+27.4000) B1950 equatorial coordinates of galactic pole. +* += (122.9319,+27.1283) galactic coordinates of J2000 celestial pole, += (192.8595,+27.1283) J2000 equatorial coordinates of galactic pole. +* += (359.6774,+89.7217) B1950 equatorial coordinates of J2000 pole, += (180.3162,+89.7217) J2000 equatorial coordinates of B1950 pole. +* += (270.0000,+66.5542) B1950 equatorial coordinates of B1950 ecliptic pole, += ( 90.0000,+66.5542) B1950 ecliptic coordinates of B1950 celestial pole. +* += (270.0000,+66.5607) J2000 equatorial coordinates of J2000 ecliptic pole, += ( 90.0000,+66.5607) J2000 ecliptic coordinates of J2000 celestial pole. +* += ( 26.7315,+15.6441) supergalactic coordinates of B1950 celestial pole, += (283.1894,+15.6441) B1950 equatorial coordinates of supergalactic pole. +* += ( 26.4505,+15.7089) supergalactic coordinates of J2000 celestial pole, += (283.7542,+15.7089) J2000 equatorial coordinates of supergalactic pole. * * * wcssptr() - Spectral axis translation @@ -653,6 +1076,11 @@ * wcssptr() translates the spectral axis in a wcsprm struct. For example, a * 'FREQ' axis may be translated into 'ZOPT-F2W' and vice versa. * +* PLEASE NOTE: Information in the wcsprm struct relating to the original +* coordinate system will be overwritten and therefore lost. If this is +* undesirable, invoke wcssptr() on a copy of the struct made with wcssub(). +* The wcsprm struct is reset on return with an explicit call to wcsset(). +* * Given and returned: * wcs struct wcsprm* * Coordinate transformation parameters. @@ -687,19 +1115,52 @@ * wcsprm::err if enabled, see wcserr_enable(). * * +* wcslib_version() - WCSLIB version number +* ---------------------------------------- +* wcslib_version() returns the WCSLIB version number. +* +* The major version number changes when the ABI changes or when the license +* conditions change. ABI changes typically result from a change to the +* contents of one of the structs. The major version number is used to +* distinguish between incompatible versions of the sharable library. +* +* The minor version number changes with new functionality or bug fixes that do +* not involve a change in the ABI. +* +* The auxiliary version number (which is often absent) signals changes to the +* documentation, test suite, build procedures, or any other change that does +* not affect the compiled library. +* +* Returned: +* vers[3] int[3] The broken-down version number: +* 0: Major version number. +* 1: Minor version number. +* 2: Auxiliary version number (zero if absent). +* May be given as a null pointer if not required. +* +* Function return value: +* char* A null-terminated, statically allocated string +* containing the version number in the usual form, i.e. +* "..". +* +* * wcsprm struct - Coordinate transformation parameters * ---------------------------------------------------- * The wcsprm struct contains information required to transform world * coordinates. It consists of certain members that must be set by the user * ("given") and others that are set by the WCSLIB routines ("returned"). -* Some of the former are not actually required for transforming coordinates. -* These are described as "auxiliary"; the struct simply provides a place to -* store them, though they may be used by wcshdo() in constructing a FITS -* header from a wcsprm struct. Some of the returned values are supplied for -* informational purposes and others are for internal use only as indicated. +* While the addresses of the arrays themselves may be set by wcsinit() if it +* (optionally) allocates memory, their contents must be set by the user. +* +* Some parameters that are given are not actually required for transforming +* coordinates. These are described as "auxiliary"; the struct simply provides +* a place to store them, though they may be used by wcshdo() in constructing a +* FITS header from a wcsprm struct. Some of the returned values are supplied +* for informational purposes and others are for internal use only as +* indicated. * * In practice, it is expected that a WCS parser would scan the FITS header to -* determine the number of coordinate axes. It would then use wcsini() to +* determine the number of coordinate axes. It would then use wcsinit() to * allocate memory for arrays in the wcsprm struct and set default values. * Then as it reread the header and identified each WCS keyrecord it would load * the value into the relevant wcsprm array element. This is essentially what @@ -709,8 +1170,8 @@ * string members and null-fills the character array. * * int flag -* (Given and returned) This flag must be set to zero whenever any of the -* following wcsprm struct members are set or changed: +* (Given and returned) This flag must be set to zero (or 1, see wcsset()) +* whenever any of the following wcsprm members are set or changed: * * - wcsprm::naxis (q.v., not normally set by the user), * - wcsprm::crpix, @@ -729,13 +1190,17 @@ * - wcsprm::ps, * - wcsprm::cd, * - wcsprm::crota, -* - wcsprm::altlin. +* - wcsprm::altlin, +* - wcsprm::ntab, +* - wcsprm::nwtb, +* - wcsprm::tab, +* - wcsprm::wtb. * * This signals the initialization routine, wcsset(), to recompute the -* returned members of the celprm struct. celset() will reset flag to -* indicate that this has been done. +* returned members of the linprm, celprm, spcprm, and tabprm structs. +* wcsset() will reset flag to indicate that this has been done. * -* PLEASE NOTE: flag should be set to -1 when wcsini() is called for the +* PLEASE NOTE: flag should be set to -1 when wcsinit() is called for the * first time for a particular wcsprm struct in order to initialize memory * management. It must ONLY be used on the first initialization otherwise * memory leaks may result. @@ -743,7 +1208,7 @@ * int naxis * (Given or returned) Number of pixel and world coordinate elements. * -* If wcsini() is used to initialize the linprm struct (as would normally +* If wcsinit() is used to initialize the linprm struct (as would normally * be the case) then it will set naxis from the value passed to it as a * function argument. The user should not subsequently modify it. * @@ -836,16 +1301,16 @@ * theta_p, given by LATPOLEa [deg] or by PVi_3a [deg] attached to the * longitude axis which takes precedence if defined. * -* lonpole and latpole may be left to default to values set by wcsini() +* lonpole and latpole may be left to default to values set by wcsinit() * (see celprm::ref), but in any case they will be reset by wcsset() to * the values actually used. Note therefore that if the wcsprm struct is -* reused without resetting them, whether directly or via wcsini(), they +* reused without resetting them, whether directly or via wcsinit(), they * will no longer have their default values. * * double restfrq * (Given) The rest frequency [Hz], and/or ... * double restwav -* (Given) ... the rest wavelength in vacuuo [m], only one of which need be +* (Given) ... the rest wavelength in vacuo [m], only one of which need be * given, the other should be set to zero. * * int npv @@ -854,13 +1319,12 @@ * int npvmax * (Given or returned) The length of the wcsprm::pv[] array. * -* npvmax will be set by wcsini() if it allocates memory for wcsprm::pv[], +* npvmax will be set by wcsinit() if it allocates memory for wcsprm::pv[], * otherwise it must be set by the user. See also wcsnpv(). * * struct pvcard *pv -* (Given or returned) Address of the first element of an array of length -* npvmax of pvcard structs. Set by wcsini() if it allocates memory for -* pv[], otherwise it must be set by the user. See also wcsnpv(). +* (Given) Address of the first element of an array of length npvmax of +* pvcard structs. * * As a FITS header parser encounters each PVi_ma keyword it should load it * into a pvcard struct in the array and increment npv. wcsset() @@ -877,13 +1341,12 @@ * int npsmax * (Given or returned) The length of the wcsprm::ps[] array. * -* npsmax will be set by wcsini() if it allocates memory for wcsprm::ps[], +* npsmax will be set by wcsinit() if it allocates memory for wcsprm::ps[], * otherwise it must be set by the user. See also wcsnps(). * * struct pscard *ps -* (Given or returned) Address of the first element of an array of length -* npsmax of pscard structs. Set by wcsini() if it allocates memory for -* ps[], otherwise it must be set by the user. See also wcsnps(). +* (Given) Address of the first element of an array of length npsmax of +* pscard structs. * * As a FITS header parser encounters each PSi_ma keyword it should load it * into a pscard struct in the array and increment nps. wcsset() @@ -895,68 +1358,79 @@ * alternate specifications of the linear transformation matrix, those * associated with the CDi_ja keywords, and ... * double *crota -* (Given) ... those associated with the CROTAia keywords. Although these +* (Given) ... those associated with the CROTAi keywords. Although these * may not formally co-exist with PCi_ja, the approach taken here is simply * to ignore them if given in conjunction with PCi_ja. * * int altlin * (Given) altlin is a bit flag that denotes which of the PCi_ja, CDi_ja -* and CROTAia keywords are present in the header: +* and CROTAi keywords are present in the header: * * - Bit 0: PCi_ja is present. * * - Bit 1: CDi_ja is present. * -* Matrix elements in the IRAF convention are -* equivalent to the product CDi_ja = CDELTia * PCi_ja, but the -* defaults differ from that of the PCi_ja matrix. If one or more -* CDi_ja keywords are present then all unspecified CDi_ja default to -* zero. If no CDi_ja (or CROTAia) keywords are present, then the -* header is assumed to be in PCi_ja form whether or not any PCi_ja -* keywords are present since this results in an interpretation of -* CDELTia consistent with the original FITS specification. +* Matrix elements in the IRAF convention are equivalent to the product +* CDi_ja = CDELTia * PCi_ja, but the defaults differ from that of the +* PCi_ja matrix. If one or more CDi_ja keywords are present then all +* unspecified CDi_ja default to zero. If no CDi_ja (or CROTAi) keywords +* are present, then the header is assumed to be in PCi_ja form whether +* or not any PCi_ja keywords are present since this results in an +* interpretation of CDELTia consistent with the original FITS +* specification. * * While CDi_ja may not formally co-exist with PCi_ja, it may co-exist -* with CDELTia and CROTAia which are to be ignored. +* with CDELTia and CROTAi which are to be ignored. * -* - Bit 2: CROTAia is present. +* - Bit 2: CROTAi is present. * -* In the AIPS convention, CROTAia may only be -* associated with the latitude axis of a celestial axis pair. It -* specifies a rotation in the image plane that is applied AFTER the -* CDELTia; any other CROTAia keywords are ignored. +* In the AIPS convention, CROTAi may only be associated with the +* latitude axis of a celestial axis pair. It specifies a rotation in +* the image plane that is applied AFTER the CDELTia; any other CROTAi +* keywords are ignored. * -* CROTAia may not formally co-exist with PCi_ja. +* CROTAi may not formally co-exist with PCi_ja. * -* CROTAia and CDELTia may formally co-exist with CDi_ja but if so are to +* CROTAi and CDELTia may formally co-exist with CDi_ja but if so are to * be ignored. * -* CDi_ja and CROTAia keywords, if found, are to be stored in the -* wcsprm::cd and wcsprm::crota arrays which are dimensioned similarly to -* wcsprm::pc and wcsprm::cdelt. FITS -* header parsers should use the following procedure: +* - Bit 3: PCi_ja + CDELTia was derived from CDi_ja by wcspcx(). +* +* This bit is set by wcspcx() when it derives PCi_ja and CDELTia from +* CDi_ja via an orthonormal decomposition. In particular, it signals +* wcsset() not to replace PCi_ja by a copy of CDi_ja with CDELTia set +* to unity. +* +* CDi_ja and CROTAi keywords, if found, are to be stored in the wcsprm::cd +* and wcsprm::crota arrays which are dimensioned similarly to wcsprm::pc +* and wcsprm::cdelt. FITS header parsers should use the following +* procedure: * -* - Whenever a PCi_ja keyword is encountered: altlin |= 1; +* - Whenever a PCi_ja keyword is encountered: altlin |= 1; * -* - Whenever a CDi_ja keyword is encountered: altlin |= 2; +* - Whenever a CDi_ja keyword is encountered: altlin |= 2; * -* - Whenever a CROTAia keyword is encountered: altlin |= 4; +* - Whenever a CROTAi keyword is encountered: altlin |= 4; * * If none of these bits are set the PCi_ja representation results, i.e. * wcsprm::pc and wcsprm::cdelt will be used as given. * * These alternate specifications of the linear transformation matrix are * translated immediately to PCi_ja by wcsset() and are invisible to the -* lower-level WCSLIB routines. In particular, wcsset() resets -* wcsprm::cdelt to unity if CDi_ja is present (and no PCi_ja). +* lower-level WCSLIB routines. In particular, unless bit 3 is also set, +* wcsset() resets wcsprm::cdelt to unity if CDi_ja is present (and no +* PCi_ja). * -* If CROTAia are present but none is associated with the latitude axis +* If CROTAi are present but none is associated with the latitude axis * (and no PCi_ja or CDi_ja), then wcsset() reverts to a unity PCi_ja * matrix. * * int velref * (Given) AIPS velocity code VELREF, refer to spcaips(). * +* It is not necessary to reset the wcsprm struct (via wcsset()) when +* wcsprm::velref is changed. +* * char alt[4] * (Given, auxiliary) Character code for alternate coordinate descriptions * (i.e. the 'a' in keyword names such as CTYPEia). This is blank for the @@ -966,6 +1440,9 @@ * An array of four characters is provided for alignment purposes, only the * first is used. * +* It is not necessary to reset the wcsprm struct (via wcsset()) when +* wcsprm::alt is changed. +* * int colnum * (Given, auxiliary) Where the coordinate representation is associated * with an image-array column in a FITS binary table, this variable may be @@ -973,6 +1450,9 @@ * * It should be set to zero for an image header or pixel list. * +* It is not necessary to reset the wcsprm struct (via wcsset()) when +* wcsprm::colnum is changed. +* * int *colax * (Given, auxiliary) Address of the first element of an array of int * recording the column numbers for each axis in a pixel list. @@ -980,6 +1460,9 @@ * The array elements should be set to zero for an image header or image * array in a binary table. * +* It is not necessary to reset the wcsprm struct (via wcsset()) when +* wcsprm::colax is changed. +* * char (*cname)[72] * (Given, auxiliary) The address of the first element of an array of * char[72] containing the coordinate axis names, CNAMEia. @@ -988,68 +1471,318 @@ * keyword, being limited to 68 characters, plus the null-terminating * character. * +* It is not necessary to reset the wcsprm struct (via wcsset()) when +* wcsprm::cname is changed. +* * double *crder * (Given, auxiliary) Address of the first element of an array of double * recording the random error in the coordinate value, CRDERia. +* +* It is not necessary to reset the wcsprm struct (via wcsset()) when +* wcsprm::crder is changed. +* * double *csyer * (Given, auxiliary) Address of the first element of an array of double * recording the systematic error in the coordinate value, CSYERia. * -* char dateavg[72] -* (Given, auxiliary) The date of a representative mid-point of the -* observation in ISO format, yyyy-mm-ddThh:mm:ss. +* It is not necessary to reset the wcsprm struct (via wcsset()) when +* wcsprm::csyer is changed. +* +* double *czphs +* (Given, auxiliary) Address of the first element of an array of double +* recording the time at the zero point of a phase axis, CZPHSia. +* +* It is not necessary to reset the wcsprm struct (via wcsset()) when +* wcsprm::czphs is changed. +* +* double *cperi +* (Given, auxiliary) Address of the first element of an array of double +* recording the period of a phase axis, CPERIia. +* +* It is not necessary to reset the wcsprm struct (via wcsset()) when +* wcsprm::cperi is changed. +* +* char wcsname[72] +* (Given, auxiliary) The name given to the coordinate representation, +* WCSNAMEa. This variable accomodates the longest allowed string-valued +* FITS keyword, being limited to 68 characters, plus the null-terminating +* character. +* +* It is not necessary to reset the wcsprm struct (via wcsset()) when +* wcsprm::wcsname is changed. +* +* char timesys[72] +* (Given, auxiliary) TIMESYS keyvalue, being the time scale (UTC, TAI, +* etc.) in which all other time-related auxiliary header values are +* recorded. Also defines the time scale for an image axis with CTYPEia +* set to 'TIME'. +* +* It is not necessary to reset the wcsprm struct (via wcsset()) when +* wcsprm::timesys is changed. +* +* char trefpos[72] +* (Given, auxiliary) TREFPOS keyvalue, being the location in space where +* the recorded time is valid. +* +* It is not necessary to reset the wcsprm struct (via wcsset()) when +* wcsprm::trefpos is changed. +* +* char trefdir[72] +* (Given, auxiliary) TREFDIR keyvalue, being the reference direction used +* in calculating a pathlength delay. +* +* It is not necessary to reset the wcsprm struct (via wcsset()) when +* wcsprm::trefdir is changed. +* +* char plephem[72] +* (Given, auxiliary) PLEPHEM keyvalue, being the Solar System ephemeris +* used for calculating a pathlength delay. +* +* It is not necessary to reset the wcsprm struct (via wcsset()) when +* wcsprm::plephem is changed. +* +* char timeunit[72] +* (Given, auxiliary) TIMEUNIT keyvalue, being the time units in which +* the following header values are expressed: TSTART, TSTOP, TIMEOFFS, +* TIMSYER, TIMRDER, TIMEDEL. It also provides the default value for +* CUNITia for time axes. +* +* It is not necessary to reset the wcsprm struct (via wcsset()) when +* wcsprm::timeunit is changed. +* +* char dateref[72] +* (Given, auxiliary) DATEREF keyvalue, being the date of a reference epoch +* relative to which other time measurements refer. +* +* It is not necessary to reset the wcsprm struct (via wcsset()) when +* wcsprm::dateref is changed. +* +* double mjdref[2] +* (Given, auxiliary) MJDREF keyvalue, equivalent to DATEREF expressed as +* a Modified Julian Date (MJD = JD - 2400000.5). The value is given as +* the sum of the two-element vector, allowing increased precision. +* +* It is not necessary to reset the wcsprm struct (via wcsset()) when +* wcsprm::mjdref is changed. +* +* double timeoffs +* (Given, auxiliary) TIMEOFFS keyvalue, being a time offset, which may be +* used, for example, to provide a uniform clock correction for times +* referenced to DATEREF. +* +* It is not necessary to reset the wcsprm struct (via wcsset()) when +* wcsprm::timeoffs is changed. +* * char dateobs[72] -* (Given, auxiliary) The date of the start of the observation unless -* otherwise explained in the comment field of the DATE-OBS keyword, in +* (Given, auxiliary) DATE-OBS keyvalue, being the date at the start of the +* observation unless otherwise explained in the DATE-OBS keycomment, in * ISO format, yyyy-mm-ddThh:mm:ss. * -* double equinox -* (Given, auxiliary) The equinox associated with dynamical equatorial or -* ecliptic coordinate systems, EQUINOXa (or EPOCH in older headers). Not -* applicable to ICRS equatorial or ecliptic coordinates. +* It is not necessary to reset the wcsprm struct (via wcsset()) when +* wcsprm::dateobs is changed. +* +* char datebeg[72] +* (Given, auxiliary) DATE-BEG keyvalue, being the date at the start of the +* observation in ISO format, yyyy-mm-ddThh:mm:ss. +* +* It is not necessary to reset the wcsprm struct (via wcsset()) when +* wcsprm::datebeg is changed. +* +* char dateavg[72] +* (Given, auxiliary) DATE-AVG keyvalue, being the date at a representative +* mid-point of the observation in ISO format, yyyy-mm-ddThh:mm:ss. +* +* It is not necessary to reset the wcsprm struct (via wcsset()) when +* wcsprm::dateavg is changed. +* +* char dateend[72] +* (Given, auxiliary) DATE-END keyvalue, baing the date at the end of the +* observation in ISO format, yyyy-mm-ddThh:mm:ss. +* +* It is not necessary to reset the wcsprm struct (via wcsset()) when +* wcsprm::dateend is changed. * -* double mjdavg -* (Given, auxiliary) Modified Julian Date (MJD = JD - 2400000.5), MJD-AVG, -* corresponding to DATE-AVG. * double mjdobs -* (Given, auxiliary) Modified Julian Date (MJD = JD - 2400000.5), MJD-OBS, -* corresponding to DATE-OBS. +* (Given, auxiliary) MJD-OBS keyvalue, equivalent to DATE-OBS expressed +* as a Modified Julian Date (MJD = JD - 2400000.5). +* +* It is not necessary to reset the wcsprm struct (via wcsset()) when +* wcsprm::mjdobs is changed. +* +* double mjdbeg +* (Given, auxiliary) MJD-BEG keyvalue, equivalent to DATE-BEG expressed +* as a Modified Julian Date (MJD = JD - 2400000.5). +* +* It is not necessary to reset the wcsprm struct (via wcsset()) when +* wcsprm::mjdbeg is changed. +* +* double mjdavg +* (Given, auxiliary) MJD-AVG keyvalue, equivalent to DATE-AVG expressed +* as a Modified Julian Date (MJD = JD - 2400000.5). +* +* It is not necessary to reset the wcsprm struct (via wcsset()) when +* wcsprm::mjdavg is changed. +* +* double mjdend +* (Given, auxiliary) MJD-END keyvalue, equivalent to DATE-END expressed +* as a Modified Julian Date (MJD = JD - 2400000.5). * -* double obsgeo[3] +* It is not necessary to reset the wcsprm struct (via wcsset()) when +* wcsprm::mjdend is changed. +* +* double jepoch +* (Given, auxiliary) JEPOCH keyvalue, equivalent to DATE-OBS expressed +* as a Julian epoch. +* +* It is not necessary to reset the wcsprm struct (via wcsset()) when +* wcsprm::jepoch is changed. +* +* double bepoch +* (Given, auxiliary) BEPOCH keyvalue, equivalent to DATE-OBS expressed +* as a Besselian epoch +* +* It is not necessary to reset the wcsprm struct (via wcsset()) when +* wcsprm::bepoch is changed. +* +* double tstart +* (Given, auxiliary) TSTART keyvalue, equivalent to DATE-BEG expressed +* as a time in units of TIMEUNIT relative to DATEREF+TIMEOFFS. +* +* It is not necessary to reset the wcsprm struct (via wcsset()) when +* wcsprm::tstart is changed. +* +* double tstop +* (Given, auxiliary) TSTOP keyvalue, equivalent to DATE-END expressed +* as a time in units of TIMEUNIT relative to DATEREF+TIMEOFFS. +* +* It is not necessary to reset the wcsprm struct (via wcsset()) when +* wcsprm::tstop is changed. +* +* double xposure +* (Given, auxiliary) XPOSURE keyvalue, being the effective exposure time +* in units of TIMEUNIT. +* +* It is not necessary to reset the wcsprm struct (via wcsset()) when +* wcsprm::xposure is changed. +* +* double telapse +* (Given, auxiliary) TELAPSE keyvalue, equivalent to the elapsed time +* between DATE-BEG and DATE-END, in units of TIMEUNIT. +* +* It is not necessary to reset the wcsprm struct (via wcsset()) when +* wcsprm::telapse is changed. +* +* double timsyer +* (Given, auxiliary) TIMSYER keyvalue, being the absolute error of the +* time values, in units of TIMEUNIT. +* +* It is not necessary to reset the wcsprm struct (via wcsset()) when +* wcsprm::timsyer is changed. +* +* double timrder +* (Given, auxiliary) TIMRDER keyvalue, being the accuracy of time stamps +* relative to each other, in units of TIMEUNIT. +* +* It is not necessary to reset the wcsprm struct (via wcsset()) when +* wcsprm::timrder is changed. +* +* double timedel +* (Given, auxiliary) TIMEDEL keyvalue, being the resolution of the time +* stamps. +* +* It is not necessary to reset the wcsprm struct (via wcsset()) when +* wcsprm::timedel is changed. +* +* double timepixr +* (Given, auxiliary) TIMEPIXR keyvalue, being the relative position of the +* time stamps in binned time intervals, a value between 0.0 and 1.0. +* +* It is not necessary to reset the wcsprm struct (via wcsset()) when +* wcsprm::timepixr is changed. +* +* double obsgeo[6] * (Given, auxiliary) Location of the observer in a standard terrestrial -* reference frame, OBSGEO-X, OBSGEO-Y, OBSGEO-Z [m]. +* reference frame. The first three give ITRS Cartesian coordinates +* OBSGEO-X [m], OBSGEO-Y [m], OBSGEO-Z [m], and the second three give +* OBSGEO-L [deg], OBSGEO-B [deg], OBSGEO-H [m], which are related through +* a standard transformation. +* +* It is not necessary to reset the wcsprm struct (via wcsset()) when +* wcsprm::obsgeo is changed. +* +* char obsorbit[72] +* (Given, auxiliary) OBSORBIT keyvalue, being the URI, URL, or name of an +* orbit ephemeris file giving spacecraft coordinates relating to TREFPOS. +* +* It is not necessary to reset the wcsprm struct (via wcsset()) when +* wcsprm::obsorbit is changed. * * char radesys[72] * (Given, auxiliary) The equatorial or ecliptic coordinate system type, * RADESYSa. * +* It is not necessary to reset the wcsprm struct (via wcsset()) when +* wcsprm::radesys is changed. +* +* double equinox +* (Given, auxiliary) The equinox associated with dynamical equatorial or +* ecliptic coordinate systems, EQUINOXa (or EPOCH in older headers). Not +* applicable to ICRS equatorial or ecliptic coordinates. +* +* It is not necessary to reset the wcsprm struct (via wcsset()) when +* wcsprm::equinox is changed. +* * char specsys[72] * (Given, auxiliary) Spectral reference frame (standard of rest), -* SPECSYSa, and ... +* SPECSYSa. +* +* It is not necessary to reset the wcsprm struct (via wcsset()) when +* wcsprm::specsys is changed. +* * char ssysobs[72] -* (Given, auxiliary) ... the actual frame in which there is no +* (Given, auxiliary) The spectral reference frame in which there is no * differential variation in the spectral coordinate across the * field-of-view, SSYSOBSa. +* +* It is not necessary to reset the wcsprm struct (via wcsset()) when +* wcsprm::ssysobs is changed. +* * double velosys * (Given, auxiliary) The relative radial velocity [m/s] between the * observer and the selected standard of rest in the direction of the * celestial reference coordinate, VELOSYSa. * +* It is not necessary to reset the wcsprm struct (via wcsset()) when +* wcsprm::velosys is changed. +* * double zsource -* (Given, auxiliary) The redshift, ZSOURCEa, of the source, and ... +* (Given, auxiliary) The redshift, ZSOURCEa, of the source. +* +* It is not necessary to reset the wcsprm struct (via wcsset()) when +* wcsprm::zsource is changed. +* * char ssyssrc[72] -* (Given, auxiliary) ... the spectral reference frame (standard of rest) -* in which this was measured, SSYSSRCa. +* (Given, auxiliary) The spectral reference frame (standard of rest), +* SSYSSRCa, in which wcsprm::zsource was measured. +* +* It is not necessary to reset the wcsprm struct (via wcsset()) when +* wcsprm::ssyssrc is changed. * * double velangl * (Given, auxiliary) The angle [deg] that should be used to decompose an * observed velocity into radial and transverse components. * -* char wcsname[72] -* (Given, auxiliary) The name given to the coordinate representation, -* WCSNAMEa. This variable accomodates the longest allowed string-valued -* FITS keyword, being limited to 68 characters, plus the null-terminating -* character. +* It is not necessary to reset the wcsprm struct (via wcsset()) when +* wcsprm::velangl is changed. +* +* struct auxprm *aux +* (Given, auxiliary) This struct holds auxiliary coordinate system +* information of a specialist nature. While these parameters may be +* widely recognized within particular fields of astronomy, they differ +* from the above auxiliary parameters in not being defined by any of the +* FITS WCS standards. Collecting them together in a separate struct that +* is allocated only when required helps to control bloat in the size of +* the wcsprm struct. * * int ntab * (Given) See wcsprm::tab. @@ -1092,7 +1825,9 @@ * int lat * (Returned) ... index for the latitude coordinate, and ... * int spec -* (Returned) ... index for the spectral coordinate in the imgcrd[][] and +* (Returned) ... index for the spectral coordinate, and ... +* int time +* (Returned) ... index for the time coordinate in the imgcrd[][] and * world[][] arrays in the API of wcsp2s(), wcss2p() and wcsmix(). * * These may also serve as indices into the pixcrd[][] array provided that @@ -1103,6 +1838,12 @@ * is used for quadcube projections where the cube faces are stored on a * separate axis (see wcs.h). * +* int chksum +* (Returned) Checksum of keyvalues provided (see wcsprm::flag). Used by +* wcsenq() to validate the self-consistency of the struct. Note that +* the checksum incorporates addresses and is therefore highly specific to +* the instance of the wcsprm struct. +* * int *types * (Returned) Address of the first element of an array of int containing a * four-digit type code for each axis. @@ -1112,6 +1853,7 @@ * - 1: Stokes coordinate. * - 2: Celestial coordinate (including CUBEFACE). * - 3: Spectral coordinate. +* - 4: Time coordinate. * * - Second digit (i.e. 100s): * - 0: Linear axis. @@ -1137,9 +1879,6 @@ * CTYPEia in "4-3" form with unrecognized algorithm code will have its * type set to -1 and generate an error. * -* void *padding -* (An unused variable inserted for alignment purposes only.) -* * struct linprm lin * (Returned) Linear transformation parameters (usage is described in the * prologue to lin.h). @@ -1153,11 +1892,9 @@ * prologue to spc.h). * * struct wcserr *err -* (Returned) If enabled, when an error status is returned this struct +* (Returned) If enabled, when an error status is returned, this struct * contains detailed information about the error, see wcserr_enable(). * -* void *m_padding -* (For internal use only.) * int m_flag * (For internal use only.) * int m_naxis @@ -1190,96 +1927,101 @@ * (For internal use only.) * double *m_csyer * (For internal use only.) +* double *m_czphs +* (For internal use only.) +* double *m_cperi +* (For internal use only.) * struct tabprm *m_tab * (For internal use only.) * struct wtbarr *m_wtb * (For internal use only.) * * -* pscard struct - Store for PSi_ma keyrecords +* pvcard struct - Store for PVi_ma keyrecords * ------------------------------------------- -* The pscard struct is used to pass the parsed contents of PSi_ma keyrecords +* The pvcard struct is used to pass the parsed contents of PVi_ma keyrecords * to wcsset() via the wcsprm struct. * * All members of this struct are to be set by the user. * * int i -* (Given) Axis number (1-relative), as in the FITS PSi_ma keyword. +* (Given) Axis number (1-relative), as in the FITS PVi_ma keyword. If +* i == 0, wcsset() will replace it with the latitude axis number. * * int m -* (Given) Parameter number (non-negative), as in the FITS PSi_ma keyword. +* (Given) Parameter number (non-negative), as in the FITS PVi_ma keyword. * -* char value[72] +* double value * (Given) Parameter value. * * -* pvcard struct - Store for PVi_ma keyrecords +* pscard struct - Store for PSi_ma keyrecords * ------------------------------------------- -* The pvcard struct is used to pass the parsed contents of PVi_ma keyrecords +* The pscard struct is used to pass the parsed contents of PSi_ma keyrecords * to wcsset() via the wcsprm struct. * * All members of this struct are to be set by the user. * * int i -* (Given) Axis number (1-relative), as in the FITS PVi_ma keyword. If -* i == 0, wcsset() will replace it with the latitude axis number. +* (Given) Axis number (1-relative), as in the FITS PSi_ma keyword. * * int m -* (Given) Parameter number (non-negative), as in the FITS PVi_ma keyword. +* (Given) Parameter number (non-negative), as in the FITS PSi_ma keyword. * -* double value +* char value[72] * (Given) Parameter value. * * -* wtbarr struct - Extraction of coordinate lookup tables from BINTABLE -* -------------------------------------------------------------------- -* Function wcstab(), which is invoked automatically by wcspih(), sets up an -* array of wtbarr structs to assist in extracting coordinate lookup tables -* from a binary table extension (BINTABLE) and copying them into the tabprm -* structs stored in wcsprm. Refer to the usage notes for wcspih() and -* wcstab() in wcshdr.h, and also the prologue to tab.h. +* auxprm struct - Additional auxiliary parameters +* ----------------------------------------------- +* The auxprm struct holds auxiliary coordinate system information of a +* specialist nature. It is anticipated that this struct will expand in future +* to accomodate additional parameters. * -* For C++ usage, because of a name space conflict with the wtbarr typedef -* defined in CFITSIO header fitsio.h, the wtbarr struct is renamed to wtbarr_s -* by preprocessor macro substitution with scope limited to wcs.h itself. +* All members of this struct are to be set by the user. * -* int i -* (Given) Image axis number. +* double rsun_ref +* (Given, auxiliary) Reference radius of the Sun used in coordinate +* calculations (m). * -* int m -* (Given) wcstab array axis number for index vectors. +* double dsun_obs +* (Given, auxiliary) Distance between the centre of the Sun and the +* observer (m). * -* int kind -* (Given) Character identifying the wcstab array type: -* - c: coordinate array, -* - i: index vector. +* double crln_obs +* (Given, auxiliary) Carrington heliographic longitude of the observer +* (deg). * -* char extnam[72] -* (Given) EXTNAME identifying the binary table extension. +* double hgln_obs +* (Given, auxiliary) Stonyhurst heliographic longitude of the observer +* (deg). * -* int extver -* (Given) EXTVER identifying the binary table extension. +* double hglt_obs +* (Given, auxiliary) Heliographic latitude (Carrington or Stonyhurst) of +* the observer (deg). * -* int extlev -* (Given) EXTLEV identifying the binary table extension. +* double a_radius +* Length of the semi-major axis of a triaxial ellipsoid approximating the +* shape of a body (e.g. planet) in the solar system (m). * -* char ttype[72] -* (Given) TTYPEn identifying the column of the binary table that contains -* the wcstab array. +* double b_radius +* Length of the intermediate axis, normal to the semi-major and semi-minor +* axes, of a triaxial ellipsoid approximating the shape of a body (m). * -* long row -* (Given) Table row number. +* double c_radius +* Length of the semi-minor axis, normal to the semi-major axis, of a +* triaxial ellipsoid approximating the shape of a body (m). * -* int ndim -* (Given) Expected dimensionality of the wcstab array. +* double blon_obs +* Bodycentric longitude of the observer in the coordinate system fixed to +* the planet or other solar system body (deg, in range 0 to 360). * -* int *dimlen -* (Given) Address of the first element of an array of int of length ndim -* into which the wcstab array axis lengths are to be written. +* double blat_obs +* Bodycentric latitude of the observer in the coordinate system fixed to +* the planet or other solar system body (deg). * -* double **arrayp -* (Given) Pointer to an array of double which is to be allocated by the -* user and into which the wcstab array is to be written. +* double bdis_obs +* Bodycentric distance of the observer (m). * * * Global variable: const char *wcs_errmsg[] - Status return messages @@ -1294,175 +2036,215 @@ #include "lin.h" #include "cel.h" #include "spc.h" -#include "tab.h" -#include "wcserr.h" #ifdef __cplusplus extern "C" { +#define wtbarr wtbarr_s // See prologue of wtbarr.h. #endif +enum wcsenq_enum { + WCSENQ_MEM = 1, // wcsprm struct memory is managed by WCSLIB. + WCSENQ_SET = 2, // wcsprm struct has been set up. + WCSENQ_BYP = 4, // wcsprm struct is in bypass mode. + WCSENQ_CHK = 8, // wcsprm struct is self-consistent. +}; + #define WCSSUB_LONGITUDE 0x1001 #define WCSSUB_LATITUDE 0x1002 #define WCSSUB_CUBEFACE 0x1004 #define WCSSUB_CELESTIAL 0x1007 #define WCSSUB_SPECTRAL 0x1008 #define WCSSUB_STOKES 0x1010 +#define WCSSUB_TIME 0x1020 + + +#define WCSCOMPARE_ANCILLARY 0x0001 +#define WCSCOMPARE_TILING 0x0002 +#define WCSCOMPARE_CRPIX 0x0004 extern const char *wcs_errmsg[]; enum wcs_errmsg_enum { - WCSERR_SUCCESS = 0, /* Success. */ - WCSERR_NULL_POINTER = 1, /* Null wcsprm pointer passed. */ - WCSERR_MEMORY = 2, /* Memory allocation failed. */ - WCSERR_SINGULAR_MTX = 3, /* Linear transformation matrix is - singular. */ - WCSERR_BAD_CTYPE = 4, /* Inconsistent or unrecognized coordinate - axis types. */ - WCSERR_BAD_PARAM = 5, /* Invalid parameter value. */ - WCSERR_BAD_COORD_TRANS = 6, /* Invalid coordinate transformation - parameters. */ - WCSERR_ILL_COORD_TRANS = 7, /* Ill-conditioned coordinate transformation - parameters. */ - WCSERR_BAD_PIX = 8, /* One or more of the pixel coordinates were - invalid. */ - WCSERR_BAD_WORLD = 9, /* One or more of the world coordinates were - invalid. */ - WCSERR_BAD_WORLD_COORD = 10, /* Invalid world coordinate. */ - WCSERR_NO_SOLUTION = 11, /* No solution found in the specified - interval. */ - WCSERR_BAD_SUBIMAGE = 12, /* Invalid subimage specification. */ - WCSERR_NON_SEPARABLE = 13 /* Non-separable subimage coordinate - system. */ + WCSERR_SUCCESS = 0, // Success. + WCSERR_NULL_POINTER = 1, // Null wcsprm pointer passed. + WCSERR_MEMORY = 2, // Memory allocation failed. + WCSERR_SINGULAR_MTX = 3, // Linear transformation matrix is singular. + WCSERR_BAD_CTYPE = 4, // Inconsistent or unrecognized coordinate + // axis type. + WCSERR_BAD_PARAM = 5, // Invalid parameter value. + WCSERR_BAD_COORD_TRANS = 6, // Unrecognized coordinate transformation + // parameter. + WCSERR_ILL_COORD_TRANS = 7, // Ill-conditioned coordinate transformation + // parameter. + WCSERR_BAD_PIX = 8, // One or more of the pixel coordinates were + // invalid. + WCSERR_BAD_WORLD = 9, // One or more of the world coordinates were + // invalid. + WCSERR_BAD_WORLD_COORD = 10, // Invalid world coordinate. + WCSERR_NO_SOLUTION = 11, // No solution found in the specified + // interval. + WCSERR_BAD_SUBIMAGE = 12, // Invalid subimage specification. + WCSERR_NON_SEPARABLE = 13, // Non-separable subimage coordinate system. + WCSERR_UNSET = 14 // wcsprm struct is unset. }; -/* Struct used for storing PVi_ma keywords. */ +// Struct used for storing PVi_ma keywords. struct pvcard { - int i; /* Axis number, as in PVi_ma (1-relative). */ - int m; /* Parameter number, ditto (0-relative). */ - double value; /* Parameter value. */ + int i; // Axis number, as in PVi_ma (1-relative). + int m; // Parameter number, ditto (0-relative). + double value; // Parameter value. }; -/* Struct used for storing PSi_ma keywords. */ +// Size of the pvcard struct in int units, used by the Fortran wrappers. +#define PVLEN (sizeof(struct pvcard)/sizeof(int)) + +// Struct used for storing PSi_ma keywords. struct pscard { - int i; /* Axis number, as in PSi_ma (1-relative). */ - int m; /* Parameter number, ditto (0-relative). */ - char value[72]; /* Parameter value. */ + int i; // Axis number, as in PSi_ma (1-relative). + int m; // Parameter number, ditto (0-relative). + char value[72]; // Parameter value. }; - /* For extracting wcstab arrays. Matches */ - /* the wtbarr typedef defined in CFITSIO */ - /* header fitsio.h. */ -#ifdef __cplusplus -#define wtbarr wtbarr_s /* See prologue above. */ -#endif -struct wtbarr { - int i; /* Image axis number. */ - int m; /* Array axis number for index vectors. */ - int kind; /* wcstab array type. */ - char extnam[72]; /* EXTNAME of binary table extension. */ - int extver; /* EXTVER of binary table extension. */ - int extlev; /* EXTLEV of binary table extension. */ - char ttype[72]; /* TTYPEn of column containing the array. */ - long row; /* Table row number. */ - int ndim; /* Expected wcstab array dimensionality. */ - int *dimlen; /* Where to write the array axis lengths. */ - double **arrayp; /* Where to write the address of the array */ - /* allocated to store the wcstab array. */ +// Size of the pscard struct in int units, used by the Fortran wrappers. +#define PSLEN (sizeof(struct pscard)/sizeof(int)) + +// Struct used to hold additional auxiliary parameters. +struct auxprm { + double rsun_ref; // Solar radius. + double dsun_obs; // Distance from Sun centre to observer. + double crln_obs; // Carrington heliographic lng of observer. + double hgln_obs; // Stonyhurst heliographic lng of observer. + double hglt_obs; // Heliographic latitude of observer. + + double a_radius; // Semi-major axis of solar system body. + double b_radius; // Semi-intermediate axis of solar system body. + double c_radius; // Semi-minor axis of solar system body. + double blon_obs; // Bodycentric longitude of observer. + double blat_obs; // Bodycentric latitude of observer. + double bdis_obs; // Bodycentric distance of observer. + double dummy[2]; // Reserved for future use. }; +// Size of the auxprm struct in int units, used by the Fortran wrappers. +#define AUXLEN (sizeof(struct auxprm)/sizeof(int)) + struct wcsprm { - /* Initialization flag (see the prologue above). */ - /*------------------------------------------------------------------------*/ - int flag; /* Set to zero to force initialization. */ - - /* FITS header keyvalues to be provided (see the prologue above). */ - /*------------------------------------------------------------------------*/ - int naxis; /* Number of axes (pixel and coordinate). */ - double *crpix; /* CRPIXja keyvalues for each pixel axis. */ - double *pc; /* PCi_ja linear transformation matrix. */ - double *cdelt; /* CDELTia keyvalues for each coord axis. */ - double *crval; /* CRVALia keyvalues for each coord axis. */ - - char (*cunit)[72]; /* CUNITia keyvalues for each coord axis. */ - char (*ctype)[72]; /* CTYPEia keyvalues for each coord axis. */ - - double lonpole; /* LONPOLEa keyvalue. */ - double latpole; /* LATPOLEa keyvalue. */ - - double restfrq; /* RESTFRQa keyvalue. */ - double restwav; /* RESTWAVa keyvalue. */ - - int npv; /* Number of PVi_ma keywords, and the */ - int npvmax; /* number for which space was allocated. */ - struct pvcard *pv; /* PVi_ma keywords for each i and m. */ - - int nps; /* Number of PSi_ma keywords, and the */ - int npsmax; /* number for which space was allocated. */ - struct pscard *ps; /* PSi_ma keywords for each i and m. */ - - /* Alternative header keyvalues (see the prologue above). */ - /*------------------------------------------------------------------------*/ - double *cd; /* CDi_ja linear transformation matrix. */ - double *crota; /* CROTAia keyvalues for each coord axis. */ - int altlin; /* Alternative representations */ - /* Bit 0: PCi_ja is present, */ - /* Bit 1: CDi_ja is present, */ - /* Bit 2: CROTAia is present. */ - int velref; /* AIPS velocity code, VELREF. */ - - /* Auxiliary coordinate system information, not used by WCSLIB. */ + // Initialization flag (see the prologue above). + //-------------------------------------------------------------------------- + int flag; // Set to zero to force initialization. + + // FITS header keyvalues to be provided (see the prologue above). + //-------------------------------------------------------------------------- + int naxis; // Number of axes (pixel and coordinate). + double *crpix; // CRPIXja keyvalues for each pixel axis. + double *pc; // PCi_ja linear transformation matrix. + double *cdelt; // CDELTia keyvalues for each coord axis. + double *crval; // CRVALia keyvalues for each coord axis. + + char (*cunit)[72]; // CUNITia keyvalues for each coord axis. + char (*ctype)[72]; // CTYPEia keyvalues for each coord axis. + + double lonpole; // LONPOLEa keyvalue. + double latpole; // LATPOLEa keyvalue. + + double restfrq; // RESTFRQa keyvalue. + double restwav; // RESTWAVa keyvalue. + + int npv; // Number of PVi_ma keywords, and the + int npvmax; // number for which space was allocated. + struct pvcard *pv; // PVi_ma keywords for each i and m. + + int nps; // Number of PSi_ma keywords, and the + int npsmax; // number for which space was allocated. + struct pscard *ps; // PSi_ma keywords for each i and m. + + // Alternative header keyvalues (see the prologue above). + //-------------------------------------------------------------------------- + double *cd; // CDi_ja linear transformation matrix. + double *crota; // CROTAi keyvalues for each coord axis. + int altlin; // Alternative representations + // Bit 0: PCi_ja is present, + // Bit 1: CDi_ja is present, + // Bit 2: CROTAi is present. + int velref; // AIPS velocity code, VELREF. + + // Auxiliary coordinate system information of a general nature. Not + // used by WCSLIB. Refer to the prologue comments above for a brief + // explanation of these values. char alt[4]; int colnum; int *colax; - + // Auxiliary coordinate axis information. char (*cname)[72]; double *crder; double *csyer; - char dateavg[72]; - char dateobs[72]; - double equinox; - double mjdavg; - double mjdobs; - double obsgeo[3]; + double *czphs; + double *cperi; + + char wcsname[72]; + // Time reference system and measurement. + char timesys[72], trefpos[72], trefdir[72], plephem[72]; + char timeunit[72]; + char dateref[72]; + double mjdref[2]; + double timeoffs; + // Data timestamps and durations. + char dateobs[72], datebeg[72], dateavg[72], dateend[72]; + double mjdobs, mjdbeg, mjdavg, mjdend; + double jepoch, bepoch; + double tstart, tstop; + double xposure, telapse; + // Timing accuracy. + double timsyer, timrder; + double timedel, timepixr; + // Spatial & celestial reference frame. + double obsgeo[6]; + char obsorbit[72]; char radesys[72]; + double equinox; char specsys[72]; char ssysobs[72]; double velosys; double zsource; char ssyssrc[72]; double velangl; - char wcsname[72]; - /* Coordinate lookup tables (see the prologue above). */ - /*------------------------------------------------------------------------*/ - int ntab; /* Number of separate tables. */ - int nwtb; /* Number of wtbarr structs. */ - struct tabprm *tab; /* Tabular transformation parameters. */ - struct wtbarr *wtb; /* Array of wtbarr structs. */ - - /* Information derived from the FITS header keyvalues by wcsset(). */ - /*------------------------------------------------------------------------*/ - char lngtyp[8], lattyp[8]; /* Celestial axis types, e.g. RA, DEC. */ - int lng, lat, spec; /* Longitude, latitude and spectral axis */ - /* indices (0-relative). */ - int cubeface; /* True if there is a CUBEFACE axis. */ - int *types; /* Coordinate type codes for each axis. */ - void *padding; /* (Dummy inserted for alignment purposes.) */ - - struct linprm lin; /* Linear transformation parameters. */ - struct celprm cel; /* Celestial transformation parameters. */ - struct spcprm spc; /* Spectral transformation parameters. */ - - /* Error handling */ - /*------------------------------------------------------------------------*/ + // Additional auxiliary coordinate system information of a specialist + // nature. Not used by WCSLIB. Refer to the prologue comments above. + struct auxprm *aux; + + // Coordinate lookup tables (see the prologue above). + //-------------------------------------------------------------------------- + int ntab; // Number of separate tables. + int nwtb; // Number of wtbarr structs. + struct tabprm *tab; // Tabular transformation parameters. + struct wtbarr *wtb; // Array of wtbarr structs. + + //-------------------------------------------------------------------------- + // Information derived from the FITS header keyvalues by wcsset(). + //-------------------------------------------------------------------------- + char lngtyp[8], lattyp[8]; // Celestial axis types, e.g. RA, DEC. + int lng, lat, spec, time; // Longitude, latitude, spectral, and time + // axis indices (0-relative). + int cubeface; // True if there is a CUBEFACE axis. + int chksum; // Checksum of keyvalues provided. + int *types; // Coordinate type codes for each axis. + + struct linprm lin; // Linear transformation parameters. + struct celprm cel; // Celestial transformation parameters. + struct spcprm spc; // Spectral transformation parameters. + + // Error messaging, if enabled. + //-------------------------------------------------------------------------- struct wcserr *err; - /* Private - the remainder are for memory management. */ - /*------------------------------------------------------------------------*/ - void *m_padding; + //-------------------------------------------------------------------------- + // Private - the remainder are for internal use. + //-------------------------------------------------------------------------- + // Memory management. int m_flag, m_naxis; double *m_crpix, *m_pc, *m_cdelt, *m_crval; char (*m_cunit)[72], (*m_ctype)[72]; @@ -1471,12 +2253,13 @@ struct wcsprm { double *m_cd, *m_crota; int *m_colax; char (*m_cname)[72]; - double *m_crder, *m_csyer; + double *m_crder, *m_csyer, *m_czphs, *m_cperi; + struct auxprm *m_aux; struct tabprm *m_tab; struct wtbarr *m_wtb; }; -/* Size of the wcsprm struct in int units, used by the Fortran wrappers. */ +// Size of the wcsprm struct in int units, used by the Fortran wrappers. #define WCSLEN (sizeof(struct wcsprm)/sizeof(int)) @@ -1486,15 +2269,33 @@ int wcsnps(int n); int wcsini(int alloc, int naxis, struct wcsprm *wcs); +int wcsinit(int alloc, int naxis, struct wcsprm *wcs, int npvmax, int npsmax, + int ndpmax); + +int wcsauxi(int alloc, struct wcsprm *wcs); + int wcssub(int alloc, const struct wcsprm *wcssrc, int *nsub, int axes[], struct wcsprm *wcsdst); +int wcscompare(int cmp, double tol, const struct wcsprm *wcs1, + const struct wcsprm *wcs2, int *equal); + int wcsfree(struct wcsprm *wcs); +int wcstrim(struct wcsprm *wcs); + +int wcssize(const struct wcsprm *wcs, int sizes[2]); + +int auxsize(const struct auxprm *aux, int sizes[2]); + +int wcsenq(const struct wcsprm *wcs, int enquiry); + int wcsprt(const struct wcsprm *wcs); int wcsperr(const struct wcsprm *wcs, const char *prefix); +int wcsbchk(struct wcsprm *wcs, int bounds); + int wcsset(struct wcsprm *wcs); int wcsp2s(struct wcsprm *wcs, int ncoord, int nelem, const double pixcrd[], @@ -1505,17 +2306,23 @@ int wcss2p(struct wcsprm *wcs, int ncoord, int nelem, const double world[], double phi[], double theta[], double imgcrd[], double pixcrd[], int stat[]); -int wcsmix(struct wcsprm *wcs, int mixpix, int mixcel, const double vspan[], +int wcsmix(struct wcsprm *wcs, int mixpix, int mixcel, const double vspan[2], double vstep, int viter, double world[], double phi[], double theta[], double imgcrd[], double pixcrd[]); +int wcsccs(struct wcsprm *wcs, double lng2p1, double lat2p1, double lng1p2, + const char *clng, const char *clat, const char *radesys, + double equinox, const char *alt); + int wcssptr(struct wcsprm *wcs, int *i, char ctype[9]); -/* Defined mainly for backwards compatibility, use wcssub() instead. */ +const char* wcslib_version(int vers[3]); + +// Defined mainly for backwards compatibility, use wcssub() instead. #define wcscopy(alloc, wcssrc, wcsdst) wcssub(alloc, wcssrc, 0x0, 0x0, wcsdst) -/* Deprecated. */ +// Deprecated. #define wcsini_errmsg wcs_errmsg #define wcssub_errmsg wcs_errmsg #define wcscopy_errmsg wcs_errmsg @@ -1531,4 +2338,4 @@ int wcssptr(struct wcsprm *wcs, int *i, char ctype[9]); } #endif -#endif /* WCSLIB_WCS */ +#endif // WCSLIB_WCS diff --git a/cextern/wcslib/C/wcsbth.l b/cextern/wcslib/C/wcsbth.l index 9921da72f003..f24d23dc15d1 100644 --- a/cextern/wcslib/C/wcsbth.l +++ b/cextern/wcslib/C/wcsbth.l @@ -1,7 +1,6 @@ /*============================================================================ - - WCSLIB 4.14 - an implementation of the FITS WCS standard. - Copyright (C) 1995-2012, Mark Calabretta + WCSLIB 8.6 - an implementation of the FITS WCS standard. + Copyright (C) 1995-2026, Mark Calabretta This file is part of WCSLIB. @@ -18,11 +17,9 @@ You should have received a copy of the GNU Lesser General Public License along with WCSLIB. If not, see http://www.gnu.org/licenses. - Direct correspondence concerning WCSLIB to mark@calabretta.id.au - Author: Mark Calabretta, Australia Telescope National Facility, CSIRO. - http://www.atnf.csiro.au/people/Mark.Calabretta - $Id: wcsbth.l,v 4.14 2012/07/13 10:02:27 cal103 Exp $ + http://www.atnf.csiro.au/computing/software/wcs + $Id: wcsbth.l,v 8.6 2026/03/29 13:53:56 mcalabre Exp $ *============================================================================= * * wcsbth.l is a Flex description file containing the definition of a lexical @@ -35,7 +32,7 @@ * * Implementation notes * -------------------- -* wcsbth() may be invoked with an option that causes it to recognise the +* wcsbth() may be invoked with an option that causes it to recognize the * image-header form of WCS keywords as defaults for each alternate coordinate * representation (up to 27). By design, with this option enabled wcsbth() can * also handle primary image and image extension headers, effectively treating @@ -71,12 +68,19 @@ /* Options. */ %option full %option never-interactive +%option noinput %option noyywrap %option outfile="wcsbth.c" %option prefix="wcsbth" +%option reentrant +%option extra-type="struct wcsbth_extra *" /* Indices for parameterized keywords. */ -I0 [0-9] +Z1 [0-9] +Z2 [0-9]{2} +Z3 [0-9]{3} +Z4 [0-9]{4} + I1 [1-9] I2 [1-9][0-9] I3 [1-9][0-9]{2} @@ -87,9 +91,12 @@ ALT [ A-Z] /* Keyvalue data types. */ INTEGER [+-]?[0-9]+ -FLOAT [+-]?([0-9]+\.?[0-9]*|\.[0-9]+)([eE][+-]?[0-9]+)? +FLOAT [+-]?([0-9]+\.?[0-9]*|\.[0-9]+)([eEdD][+-]?[0-9]+)? STRING '([^']|'')*' +/* Inline comment syntax. */ +INLINE " "*(\/.*)? + /* Exclusive start states. */ %x CCCCCia iCCCna iCCCCn TCCCna TCCCCn %x CCi_ja ijCCna TCn_ka TCCn_ka @@ -99,12 +106,13 @@ STRING '([^']|'')*' %x CCCCCCCC CCCCCCCa %x CCCCna CCCCCna %x CCCCn CCCCCn -%x VALUE INTEGER_VAL FLOAT_VAL STRING_VAL +%x VALUE INTEGER_VAL FLOAT_VAL FLOAT2_VAL STRING_VAL %x COMMENT DISCARD ERROR FLUSH %{ #include #include +#include #include #include #include @@ -112,59 +120,66 @@ STRING '([^']|'')*' #include "wcs.h" #include "wcshdr.h" #include "wcsmath.h" +#include "wcsprintf.h" #include "wcsutil.h" - /* Codes used for keyvalue data types. */ + // Codes used for keyvalue data types. #define INTEGER 0 #define FLOAT 1 -#define STRING 2 - - /* Bit masks used for keyword types: */ -#define IMGAUX 0x1 /* Auxiliary image header, e.g. LONPOLEa or */ - /* DATE-OBS. */ -#define IMGAXIS 0x2 /* Image header with axis number, e.g. */ - /* CTYPEia. */ -#define IMGHEAD 0x3 /* Image header of either type. */ -#define BIMGARR 0x4 /* Binary table image array with axis */ - /* number, e.g. iCTYna. */ -#define PIXLIST 0x8 /* Pixel list, e.g. TCTYna. */ -#define BINTAB 0xC /* Shared binary table image array (without */ - /* axis number) or pixel list, e.g. LONPna */ - /* or OBSGXn. */ - -#define YY_DECL int wcsbth(char *header, int nkeyrec, int relax, int ctrl, \ - int keysel, int *colsel, int *nreject, int *nwcs, \ - struct wcsprm **wcs) +#define FLOAT2 2 +#define STRING 3 + + // Bit masks used for keyword types: +#define IMGAUX 0x1 // Auxiliary image header, e.g. LONPOLEa or + // DATE-OBS. +#define IMGAXIS 0x2 // Image header with axis number, e.g. + // CTYPEia. +#define IMGHEAD 0x3 // IMGAUX | IMGAXIS, i.e. image header of + // either type. +#define BIMGARR 0x4 // Binary table image array, e.g. iCTYna. +#define PIXLIST 0x8 // Pixel list, e.g. TCTYna. +#define BINTAB 0xC // BIMGARR | PIXLIST, i.e. binary table + // image array (without axis number) or + // pixel list, e.g. LONPna or OBSGXn. + +// User data associated with yyscanner. +struct wcsbth_extra { + // Values passed to YY_INPUT. + char *hdr; + int nkeyrec; + + // Used in preempting the call to exit() by yy_fatal_error(). + jmp_buf abort_jmp_env; +}; + +#define YY_DECL int wcsbth_scanner(char *header, int nkeyrec, int relax, \ + int ctrl, int keysel, int *colsel, int *nreject, int *nwcs, \ + struct wcsprm **wcs, yyscan_t yyscanner) #define YY_INPUT(inbuff, count, bufsize) \ { \ - if (wcsbth_nkeyrec) { \ - strncpy(inbuff, wcsbth_hdr, 80); \ + if (yyextra->nkeyrec) { \ + strncpy(inbuff, yyextra->hdr, 80); \ inbuff[80] = '\n'; \ - wcsbth_hdr += 80; \ - wcsbth_nkeyrec--; \ + yyextra->hdr += 80; \ + yyextra->nkeyrec--; \ count = 81; \ } else { \ count = YY_NULL; \ } \ } -/* A convenience macro to get around incompatibilities between unput() and - yyless(): put yytext followed by a blank back onto the input stream. */ -#define WCSBTH_PUTBACK \ - sprintf(stmp, "%s ", yytext); \ - itmp = strlen(stmp); \ - while (itmp) unput(stmp[--itmp]); - -/* These global variables are required by YY_INPUT. */ -char *wcsbth_hdr; -int wcsbth_nkeyrec; +// Preempt the call to exit() by yy_fatal_error(). +#define exit(status) longjmp(yyextra->abort_jmp_env, status); -/* Used in preempting the call to exit() by yy_fatal_error(). */ -jmp_buf wcsbth_abort_jmp_env; -#define exit(status) longjmp(wcsbth_abort_jmp_env, status) +// A convenience macro to get around incompatibilities between unput() and +// yyless(): put yytext followed by a blank back onto the input stream. +#define WCSBTH_PUTBACK \ + sprintf(strtmp, "%s ", yytext); \ + size_t iz = strlen(strtmp); \ + while (iz) unput(strtmp[--iz]); -/* Struct used internally for header bookkeeping. */ +// Struct used internally for header bookkeeping. struct wcsbth_alts { int ncol, ialt, icol, imgherit; short int (*arridx)[27]; @@ -179,95 +194,113 @@ struct wcsbth_alts { unsigned char pad2[2]; }; -int wcsbth_pass1(int keytype, int i, int j, int n, int k, char a, char ptype, - struct wcsbth_alts *alts); -int wcsbth_init1(struct wcsbth_alts *alts, int *nwcs, struct wcsprm **wcs); - -struct wcsprm *wcsbth_idx(struct wcsprm *wcs, struct wcsbth_alts *alts, +// Internal helper functions. +static YY_DECL; +static int wcsbth_colax(struct wcsprm *wcs, struct wcsbth_alts *alts, int k, + char a); +static int wcsbth_final(struct wcsbth_alts *alts, int *nwcs, + struct wcsprm **wcs); +static struct wcsprm *wcsbth_idx(struct wcsprm *wcs, struct wcsbth_alts *alts, int keytype, int n, char a); -int wcsbth_colax(struct wcsprm *wcs, struct wcsbth_alts *alts, int k, char a); +static int wcsbth_init1(struct wcsbth_alts *alts, int auxprm, int *nwcs, + struct wcsprm **wcs); +static int wcsbth_pass1(int keytype, int i, int j, int n, int k, char a, + char ptype, struct wcsbth_alts *alts); -int wcsbth_epoch(void *wptr); -int wcsbth_vsource(void *wptr); +// Helper functions for keywords that require special handling. +static int wcsbth_jdref(double *wptr, const double *jdref); +static int wcsbth_jdrefi(double *wptr, const double *jdrefi); +static int wcsbth_jdreff(double *wptr, const double *jdreff); +static int wcsbth_epoch(double *wptr, const double *epoch); +static int wcsbth_vsource(double *wptr, const double *vsource); -int wcsbth_final(struct wcsbth_alts *alts, int *nwcs, struct wcsprm **wcs); +// Helper functions for keyvalue validity checking. +static int wcsbth_timepixr(double timepixr); %} %% - /* Keyword indices, as used in the WCS papers, e.g. iVn_ma, TPn_ka. */ - char a; - int i, j, k, m, n; - - char *cptr, *errmsg, errtxt[80], exclude[1000], *extkey, *hptr, ptype, - stmp[16]; - int altlin, ialt, icol, incl, ipass, ipx, itmp, ix, jx, keytype, - nsel, npass, status, valtype, voff; - void *vptr, *wptr; - struct wcsbth_alts alts; - struct wcsprm *wcsp, wcstem; - int (*special)(void *); - int yylex_destroy(void); + char *errmsg, errtxt[80], *keyname, strtmp[80]; + int inttmp; + double dbltmp, dbl2tmp[2]; + struct auxprm auxtem; + struct wcsprm wcstem; - /* The data structures produced. */ + // Initialize returned values. + *nreject = 0; *nwcs = 0; *wcs = 0x0; - /* Parameters used to implement YY_INPUT. */ - wcsbth_hdr = header; - wcsbth_nkeyrec = nkeyrec; + // Our handle on the input stream. + char *keyrec = header; + char *hptr = header; + char *keep = 0x0; - /* Our handle on the input stream. */ - hptr = header; - *nreject = 0; + // For keeping tallies of keywords found. + int nvalid = 0; + int nother = 0; + + // Used to flag image header keywords that are always inherited. + int imherit = 1; - /* Keyword parameters. */ - i = j = 0; - n = k = 0; - m = 0; - a = ' '; + // If strict, then also reject. + if (relax & WCSHDR_strict) relax |= WCSHDR_reject; - /* Header bookkeeping. */ + // Keyword indices, as used in the WCS papers, e.g. iVn_ma, TPn_ka. + int i = 0; + int j = 0; + int k = 0; + int n = 0; + int m = 0; + char a = ' '; + + // Header bookkeeping. + struct wcsbth_alts alts; alts.ncol = 0; alts.arridx = 0x0; alts.pixlist = 0x0; alts.npv = 0x0; alts.nps = 0x0; - for (ialt = 0; ialt < 27; ialt++) { + for (int ialt = 0; ialt < 27; ialt++) { alts.pixidx[ialt] = 0; alts.pixnpv[ialt] = 0; alts.pixnps[ialt] = 0; } - /* For decoding the keyvalue. */ - keytype = 0; - valtype = -1; - vptr = 0x0; - - /* For keywords that require special handling. */ - altlin = 0; - ptype = ' '; - special = 0x0; - - /* Selection by column number. */ - nsel = colsel ? colsel[0] : 0; - incl = (nsel > 0); - for (icol = 0; icol < 1000; icol++) { + // For decoding the keyvalue. + int keytype = 0; + int valtype = -1; + void *vptr = 0x0; + + // For keywords that require special handling. + int altlin = 0; + char ptype = ' '; + int (*chekval)(double) = 0x0; + int (*special)(double *, const double *) = 0x0; + struct auxprm *auxp = 0x0; + int auxprm = 0; + int naux = 0; + + // Selection by column number. + int nsel = colsel ? colsel[0] : 0; + int incl = (nsel > 0); + char exclude[1000]; + for (int icol = 0; icol < 1000; icol++) { exclude[icol] = incl; } - for (icol = 1; icol <= abs(nsel); icol++) { - itmp = colsel[icol]; + for (int icol = 1; icol <= abs(nsel); icol++) { + int itmp = colsel[icol]; if (0 < itmp && itmp < 1000) { exclude[itmp] = !incl; } } exclude[0] = 0; - /* Selection by keyword type. */ - itmp = keysel; - keysel = 0; - if (itmp) { + // Selection by keyword type. + if (keysel) { + int itmp = keysel; + keysel = 0; if (itmp & WCSHDR_IMGHEAD) keysel |= IMGHEAD; if (itmp & WCSHDR_BIMGARR) keysel |= BIMGARR; if (itmp & WCSHDR_PIXLIST) keysel |= PIXLIST; @@ -276,13 +309,17 @@ int wcsbth_final(struct wcsbth_alts *alts, int *nwcs, struct wcsprm **wcs); keysel = IMGHEAD | BINTAB; } - /* Control variables. */ - ipass = 1; - npass = 2; + // Control variables. + int ipass = 1; + int npass = 2; - /* Return here via longjmp() invoked by yy_fatal_error(). */ - if (setjmp(wcsbth_abort_jmp_env)) { - return 4; + // User data associated with yyscanner. + yyextra->hdr = header; + yyextra->nkeyrec = nkeyrec; + + // Return here via longjmp() invoked by yy_fatal_error(). + if (setjmp(yyextra->abort_jmp_env)) { + return WCSHDRERR_PARSER; } BEGIN(INITIAL); @@ -294,7 +331,7 @@ int wcsbth_final(struct wcsbth_alts *alts, int *nwcs, struct wcsprm **wcs); sscanf(yytext, "TFIELDS = %d", &(alts.ncol)); BEGIN(FLUSH); } else { - errmsg = "Duplicate or out-of-sequence TFIELDS keyword"; + errmsg = "duplicate or out-of-sequence TFIELDS keyword"; BEGIN(ERROR); } @@ -304,27 +341,33 @@ int wcsbth_final(struct wcsbth_alts *alts, int *nwcs, struct wcsprm **wcs); } ^WCSAXES{ALT}=" "" "*{INTEGER} { - keytype = IMGAXIS; - - if (!(keytype & keysel)) { - /* Ignore this key type. */ + if (!(keysel & IMGAXIS)) { + // Ignore this key type. BEGIN(DISCARD); } else { if (relax & WCSHDR_ALLIMG) { - if (ipass == 1) { - sscanf(yytext, "WCSAXES%c= %d", &a, &i); - wcsbth_pass1(IMGAXIS, i, 0, 0, 0, a, ' ', &alts); - } + sscanf(yytext, "WCSAXES%c= %d", &a, &i); - BEGIN(FLUSH); + if (i < 0) { + errmsg = "negative value of WCSAXESa ignored"; + BEGIN(ERROR); + + } else { + valtype = INTEGER; + vptr = 0x0; + + keyname = "WCSAXESa"; + keytype = IMGAXIS; + BEGIN(COMMENT); + } } else if (relax & WCSHDR_reject) { - errmsg = "Image-header keyword WCSAXESa in binary table"; + errmsg = "image-header keyword WCSAXESa in binary table"; BEGIN(ERROR); } else { - /* Pretend we don't recognize it. */ + // Pretend we don't recognize it. BEGIN(DISCARD); } } @@ -333,60 +376,68 @@ int wcsbth_final(struct wcsbth_alts *alts, int *nwcs, struct wcsprm **wcs); ^WCAX{I1}{ALT}" = "" "*{INTEGER} | ^WCAX{I2}{ALT}" = "" "*{INTEGER} | ^WCAX{I3}{ALT}"= "" "*{INTEGER} { - keytype = BIMGARR; + keyname = "WCAXna"; - /* Note that a blank in the sscanf() format string matches zero or - more of them in the input. */ + // Note that a blank in the sscanf() format string matches zero or + // more of them in the input. sscanf(yytext, "WCAX%d%c = %d", &n, &a, &i); - if (!(keytype & keysel) || exclude[n]) { - /* Ignore this key type or column. */ + if (!(keysel & BIMGARR) || exclude[n]) { + // Ignore this key type or column. BEGIN(DISCARD); + + } else if (i < 0) { + errmsg = "negative value of WCSAXESa ignored"; + BEGIN(ERROR); + } else { - if (ipass == 1) { - wcsbth_pass1(BIMGARR, i, 0, n, 0, a, ' ', &alts); - } - BEGIN(FLUSH); + valtype = INTEGER; + vptr = 0x0; + + keyname = "WCAXna"; + keytype = IMGAXIS; + BEGIN(COMMENT); } } ^WCST{I1}{ALT}" = "" "*{STRING} | ^WCST{I2}{ALT}" = "" "*{STRING} | ^WCST{I3}{ALT}"= "" "*{STRING} { - /* Cross-reference supplier. */ - keytype = BIMGARR; - errmsg = "Cross-references are not currently implemented"; + // Cross-reference supplier. + keyname = "WCSTna"; + errmsg = "cross-references are not implemented"; BEGIN(ERROR); } ^WCSX{I1}{ALT}" = "" "*{STRING} | ^WCSX{I2}{ALT}" = "" "*{STRING} | ^WCSX{I3}{ALT}"= "" "*{STRING} { - /* Cross-reference consumer. */ - keytype = BIMGARR; - errmsg = "Cross-references are not currently implemented"; + // Cross-reference consumer. + keyname = "WCSXna"; + errmsg = "cross-references are not implemented"; BEGIN(ERROR); } ^CRPIX { valtype = FLOAT; - vptr = &(wcstem.crpix); + vptr = &(wcstem.crpix); - extkey = "CRPIXja"; + keyname = "CRPIXja"; BEGIN(CCCCCia); } ^{I1}CRP | ^{I1}CRPX { valtype = FLOAT; - vptr = &(wcstem.crpix); + vptr = &(wcstem.crpix); sscanf(yytext, "%d", &i); if (yyleng == 4) { + keyname = "jCRPna"; BEGIN(iCCCna); } else { - extkey = "jCRPXn"; + keyname = "jCRPXn"; BEGIN(iCCCCn); } } @@ -394,101 +445,107 @@ int wcsbth_final(struct wcsbth_alts *alts, int *nwcs, struct wcsprm **wcs); ^TCRP | ^TCRPX { valtype = FLOAT; - vptr = &(wcstem.crpix); + vptr = &(wcstem.crpix); if (yyleng == 4) { + keyname = "TCRPna"; BEGIN(TCCCna); } else { - extkey = "TCRPXn"; + keyname = "TCRPXn"; BEGIN(TCCCCn); } } ^PC { valtype = FLOAT; - vptr = &(wcstem.pc); + vptr = &(wcstem.pc); altlin = 1; - extkey = "PCi_ja"; + keyname = "PCi_ja"; BEGIN(CCi_ja); } ^{I2}PC { valtype = FLOAT; - vptr = &(wcstem.pc); - altlin = 1; + vptr = &(wcstem.pc); + altlin = 1; sscanf(yytext, "%1d%1d", &i, &j); + keyname = "ijPCna"; BEGIN(ijCCna); } ^TP | ^TPC { valtype = FLOAT; - vptr = &(wcstem.pc); - altlin = 1; + vptr = &(wcstem.pc); + altlin = 1; if (yyleng == 2) { + keyname = "TPn_ka"; BEGIN(TCn_ka); } else { - extkey = "TPCn_ka"; + keyname = "TPCn_ka"; BEGIN(TCCn_ka); } } ^CD { valtype = FLOAT; - vptr = &(wcstem.cd); - altlin = 2; + vptr = &(wcstem.cd); + altlin = 2; - extkey = "CDi_ja"; + keyname = "CDi_ja"; BEGIN(CCi_ja); } ^{I2}CD { valtype = FLOAT; - vptr = &(wcstem.cd); - altlin = 2; + vptr = &(wcstem.cd); + altlin = 2; sscanf(yytext, "%1d%1d", &i, &j); + keyname = "ijCDna"; BEGIN(ijCCna); } ^TC | ^TCD { valtype = FLOAT; - vptr = &(wcstem.cd); - altlin = 2; + vptr = &(wcstem.cd); + altlin = 2; if (yyleng == 2) { + keyname = "TCn_ka"; BEGIN(TCn_ka); } else { - extkey = "TCDn_ka"; + keyname = "TCDn_ka"; BEGIN(TCCn_ka); } } ^CDELT { valtype = FLOAT; - vptr = &(wcstem.cdelt); + vptr = &(wcstem.cdelt); - extkey = "CDELTia"; + keyname = "CDELTia"; BEGIN(CCCCCia); } ^{I1}CDE | ^{I1}CDLT { valtype = FLOAT; - vptr = &(wcstem.cdelt); + vptr = &(wcstem.cdelt); sscanf(yytext, "%d", &i); if (yyleng == 4) { + keyname = "iCDEna"; BEGIN(iCCCna); } else { - extkey = "iCDLTn"; + keyname = "iCDLTn"; BEGIN(iCCCCn); } } @@ -496,64 +553,66 @@ int wcsbth_final(struct wcsbth_alts *alts, int *nwcs, struct wcsprm **wcs); ^TCDE | ^TCDLT { valtype = FLOAT; - vptr = &(wcstem.cdelt); + vptr = &(wcstem.cdelt); if (yyleng == 4) { + keyname = "TCDEna"; BEGIN(TCCCna); } else { - extkey = "TCDLTn"; + keyname = "TCDLTn"; BEGIN(TCCCCn); } } ^CROTA { valtype = FLOAT; - vptr = &(wcstem.crota); - altlin = 4; + vptr = &(wcstem.crota); + altlin = 4; - extkey = "CROTAi"; + keyname = "CROTAi"; BEGIN(CROTAi); } ^{I1}CROT { valtype = FLOAT; - vptr = &(wcstem.crota); - altlin = 4; + vptr = &(wcstem.crota); + altlin = 4; sscanf(yytext, "%d", &i); - extkey = "iCROTn"; + keyname = "iCROTn"; BEGIN(iCROTn); } ^TCROT { valtype = FLOAT; - vptr = &(wcstem.crota); - altlin = 4; + vptr = &(wcstem.crota); + altlin = 4; - extkey = "TCROTn"; + keyname = "TCROTn"; BEGIN(TCROTn); } ^CUNIT { valtype = STRING; - vptr = &(wcstem.cunit); + vptr = &(wcstem.cunit); - extkey = "CUNITia"; + keyname = "CUNITia"; BEGIN(CCCCCia); } ^{I1}CUN | ^{I1}CUNI { valtype = STRING; - vptr = &(wcstem.cunit); + vptr = &(wcstem.cunit); sscanf(yytext, "%d", &i); if (yyleng == 4) { + keyname = "iCUNna"; BEGIN(iCCCna); } else { - extkey = "iCUNIn"; + keyname = "iCUNIn"; BEGIN(iCCCCn); } } @@ -561,35 +620,37 @@ int wcsbth_final(struct wcsbth_alts *alts, int *nwcs, struct wcsprm **wcs); ^TCUN | ^TCUNI { valtype = STRING; - vptr = &(wcstem.cunit); + vptr = &(wcstem.cunit); if (yyleng == 4) { + keyname = "TCUNna"; BEGIN(TCCCna); } else { - extkey = "TCUNIn"; + keyname = "TCUNIn"; BEGIN(TCCCCn); } } ^CTYPE { valtype = STRING; - vptr = &(wcstem.ctype); + vptr = &(wcstem.ctype); - extkey = "CTYPEia"; + keyname = "CTYPEia"; BEGIN(CCCCCia); } ^{I1}CTY | ^{I1}CTYP { valtype = STRING; - vptr = &(wcstem.ctype); + vptr = &(wcstem.ctype); sscanf(yytext, "%d", &i); if (yyleng == 4) { + keyname = "iCTYna"; BEGIN(iCCCna); } else { - extkey = "iCTYPn"; + keyname = "iCTYPn"; BEGIN(iCCCCn); } } @@ -597,35 +658,37 @@ int wcsbth_final(struct wcsbth_alts *alts, int *nwcs, struct wcsprm **wcs); ^TCTY | ^TCTYP { valtype = STRING; - vptr = &(wcstem.ctype); + vptr = &(wcstem.ctype); if (yyleng == 4) { + keyname = "TCTYna"; BEGIN(TCCCna); } else { - extkey = "TCTYPn"; + keyname = "TCTYPn"; BEGIN(TCCCCn); } } ^CRVAL { valtype = FLOAT; - vptr = &(wcstem.crval); + vptr = &(wcstem.crval); - extkey = "CRVALia"; + keyname = "CRVALia"; BEGIN(CCCCCia); } ^{I1}CRV | ^{I1}CRVL { valtype = FLOAT; - vptr = &(wcstem.crval); + vptr = &(wcstem.crval); sscanf(yytext, "%d", &i); if (yyleng == 4) { + keyname = "iCRVna"; BEGIN(iCCCna); } else { - extkey = "iCRVLn"; + keyname = "iCRVLn"; BEGIN(iCCCCn); } } @@ -633,12 +696,13 @@ int wcsbth_final(struct wcsbth_alts *alts, int *nwcs, struct wcsprm **wcs); ^TCRV | ^TCRVL { valtype = FLOAT; - vptr = &(wcstem.crval); + vptr = &(wcstem.crval); if (yyleng == 4) { + keyname = "TCRVna"; BEGIN(TCCCna); } else { - extkey = "TCRVLn"; + keyname = "TCRVLn"; BEGIN(TCCCCn); } } @@ -646,12 +710,14 @@ int wcsbth_final(struct wcsbth_alts *alts, int *nwcs, struct wcsprm **wcs); ^LONPOLE | ^LONP { valtype = FLOAT; - vptr = &(wcstem.lonpole); + vptr = &(wcstem.lonpole); if (yyleng == 7) { - extkey = "LONPOLEa"; + keyname = "LONPOLEa"; + imherit = 0; BEGIN(CCCCCCCa); } else { + keyname = "LONPna"; BEGIN(CCCCna); } } @@ -659,30 +725,42 @@ int wcsbth_final(struct wcsbth_alts *alts, int *nwcs, struct wcsprm **wcs); ^LATPOLE | ^LATP { valtype = FLOAT; - vptr = &(wcstem.latpole); + vptr = &(wcstem.latpole); if (yyleng == 7) { - extkey = "LATPOLEa"; + keyname = "LATPOLEa"; + imherit = 0; BEGIN(CCCCCCCa); } else { + keyname = "LATPna"; BEGIN(CCCCna); } } -^RESTFRQ | ^RESTFREQ | +^RESTFRQ | ^RFRQ { valtype = FLOAT; - vptr = &(wcstem.restfrq); + vptr = &(wcstem.restfrq); if (yyleng == 8) { - unput(' '); - extkey = "RESTFREQ"; - BEGIN(CCCCCCCa); + if (relax & WCSHDR_strict) { + errmsg = "the RESTFREQ keyword is deprecated, use RESTFRQa"; + BEGIN(ERROR); + + } else { + unput(' '); + + keyname = "RESTFREQ"; + BEGIN(CCCCCCCa); + } + } else if (yyleng == 7) { - extkey = "RESTFRQa"; + keyname = "RESTFRQa"; BEGIN(CCCCCCCa); + } else { + keyname = "RFRQna"; BEGIN(CCCCna); } } @@ -690,37 +768,39 @@ int wcsbth_final(struct wcsbth_alts *alts, int *nwcs, struct wcsprm **wcs); ^RESTWAV | ^RWAV { valtype = FLOAT; - vptr = &(wcstem.restwav); + vptr = &(wcstem.restwav); if (yyleng == 7) { - extkey = "RESTWAVa"; + keyname = "RESTWAVa"; BEGIN(CCCCCCCa); } else { + keyname = "RWAVna"; BEGIN(CCCCna); } } ^PV { valtype = FLOAT; - vptr = &(wcstem.pv); - ptype = 'v'; + vptr = &(wcstem.pv); + ptype = 'v'; - extkey = "PVi_ma"; + keyname = "PVi_ma"; BEGIN(CCi_ma); } ^{I1}V | ^{I1}PV { valtype = FLOAT; - vptr = &(wcstem.pv); - ptype = 'v'; + vptr = &(wcstem.pv); + ptype = 'v'; sscanf(yytext, "%d", &i); if (yyleng == 2) { + keyname = "iVn_ma"; BEGIN(iCn_ma); } else { - extkey = "iPVn_ma"; + keyname = "iPVn_ma"; BEGIN(iCCn_ma); } } @@ -728,46 +808,49 @@ int wcsbth_final(struct wcsbth_alts *alts, int *nwcs, struct wcsprm **wcs); ^TV | ^TPV { valtype = FLOAT; - vptr = &(wcstem.pv); - ptype = 'v'; + vptr = &(wcstem.pv); + ptype = 'v'; if (yyleng == 2) { + keyname = "TVn_ma"; BEGIN(TCn_ma); } else { - extkey = "TPVn_ma"; + keyname = "TPVn_ma"; BEGIN(TCCn_ma); } } ^PROJP { valtype = FLOAT; - vptr = &(wcstem.pv); - ptype = 'v'; + vptr = &(wcstem.pv); + ptype = 'v'; + keyname = "PROJPm"; BEGIN(PROJPm); } ^PS { valtype = STRING; - vptr = &(wcstem.ps); - ptype = 's'; + vptr = &(wcstem.ps); + ptype = 's'; - extkey = "PSi_ma"; + keyname = "PSi_ma"; BEGIN(CCi_ma); } ^{I1}S | ^{I1}PS { valtype = STRING; - vptr = &(wcstem.ps); - ptype = 's'; + vptr = &(wcstem.ps); + ptype = 's'; sscanf(yytext, "%d", &i); if (yyleng == 2) { + keyname = "iSn_ma"; BEGIN(iCn_ma); } else { - extkey = "iPSn_ma"; + keyname = "iPSn_ma"; BEGIN(iCCn_ma); } } @@ -775,37 +858,65 @@ int wcsbth_final(struct wcsbth_alts *alts, int *nwcs, struct wcsprm **wcs); ^TS | ^TPS { valtype = STRING; - vptr = &(wcstem.ps); - ptype = 's'; + vptr = &(wcstem.ps); + ptype = 's'; if (yyleng == 2) { + keyname = "TSn_ma"; BEGIN(TCn_ma); } else { - extkey = "TPSn_ma"; + keyname = "TPSn_ma"; BEGIN(TCCn_ma); } } +^VELREF{ALT}" " { + sscanf(yytext, "VELREF%c", &a); + + if (relax & WCSHDR_strict) { + errmsg = "the VELREF keyword is deprecated, use SPECSYSa"; + BEGIN(ERROR); + + } else if (a == ' ' || (relax & WCSHDR_VELREFa)) { + valtype = INTEGER; + vptr = &(wcstem.velref); + + unput(a); + + keyname = "VELREF"; + imherit = 0; + BEGIN(CCCCCCCa); + + } else if (relax & WCSHDR_reject) { + errmsg = "VELREF keyword may not have an alternate version code"; + BEGIN(ERROR); + + } else { + BEGIN(DISCARD); + } + } + ^CNAME { valtype = STRING; - vptr = &(wcstem.cname); + vptr = &(wcstem.cname); - extkey = "CNAMEia"; + keyname = "CNAMEia"; BEGIN(CCCCCia); } ^{I1}CNA | ^{I1}CNAM { valtype = STRING; - vptr = &(wcstem.cname); + vptr = &(wcstem.cname); sscanf(yytext, "%d", &i); if (yyleng == 4) { + keyname = "iCNAna"; BEGIN(iCCCna); } else { if (!(relax & WCSHDR_CNAMn)) vptr = 0x0; - extkey = "iCNAMn"; + keyname = "iCNAMn"; BEGIN(iCCCCn); } } @@ -813,37 +924,39 @@ int wcsbth_final(struct wcsbth_alts *alts, int *nwcs, struct wcsprm **wcs); ^TCNA | ^TCNAM { valtype = STRING; - vptr = &(wcstem.cname); + vptr = &(wcstem.cname); if (yyleng == 4) { + keyname = "TCNAna"; BEGIN(TCCCna); } else { if (!(relax & WCSHDR_CNAMn)) vptr = 0x0; - extkey = "TCNAMn"; + keyname = "TCNAMn"; BEGIN(TCCCCn); } } ^CRDER { valtype = FLOAT; - vptr = &(wcstem.crder); + vptr = &(wcstem.crder); - extkey = "CRDERia"; + keyname = "CRDERia"; BEGIN(CCCCCia); } ^{I1}CRD | ^{I1}CRDE { valtype = FLOAT; - vptr = &(wcstem.crder); + vptr = &(wcstem.crder); sscanf(yytext, "%d", &i); if (yyleng == 4) { + keyname = "iCRDna"; BEGIN(iCCCna); } else { if (!(relax & WCSHDR_CNAMn)) vptr = 0x0; - extkey = "iCRDEn"; + keyname = "iCRDEn"; BEGIN(iCCCCn); } } @@ -851,37 +964,39 @@ int wcsbth_final(struct wcsbth_alts *alts, int *nwcs, struct wcsprm **wcs); ^TCRD | ^TCRDE { valtype = FLOAT; - vptr = &(wcstem.crder); + vptr = &(wcstem.crder); if (yyleng == 4) { + keyname = "TCRDna"; BEGIN(TCCCna); } else { if (!(relax & WCSHDR_CNAMn)) vptr = 0x0; - extkey = "TCRDEn"; + keyname = "TCRDEn"; BEGIN(TCCCCn); } } ^CSYER { valtype = FLOAT; - vptr = &(wcstem.csyer); + vptr = &(wcstem.csyer); - extkey = "CSYERia"; + keyname = "CSYERia"; BEGIN(CCCCCia); } ^{I1}CSY | ^{I1}CSYE { valtype = FLOAT; - vptr = &(wcstem.csyer); + vptr = &(wcstem.csyer); sscanf(yytext, "%d", &i); if (yyleng == 4) { + keyname = "iCSYna"; BEGIN(iCCCna); } else { if (!(relax & WCSHDR_CNAMn)) vptr = 0x0; - extkey = "iCSYEn"; + keyname = "iCSYEn"; BEGIN(iCCCCn); } } @@ -889,298 +1004,795 @@ int wcsbth_final(struct wcsbth_alts *alts, int *nwcs, struct wcsprm **wcs); ^TCSY | ^TCSYE { valtype = FLOAT; - vptr = &(wcstem.csyer); + vptr = &(wcstem.csyer); if (yyleng == 4) { + keyname = "TCSYna"; BEGIN(TCCCna); } else { if (!(relax & WCSHDR_CNAMn)) vptr = 0x0; - extkey = "TCSYEn"; + keyname = "TCSYEn"; BEGIN(TCCCCn); } } -^DATE-AVG | -^DAVG { - valtype = STRING; - vptr = wcstem.dateavg; +^CZPHS { + valtype = FLOAT; + vptr = &(wcstem.czphs); - if (yyleng == 8) { - extkey = "DATE-AVG"; - BEGIN(CCCCCCCC); - } else { - BEGIN(CCCCn); - } + keyname = "CZPHSia"; + BEGIN(CCCCCia); } -^DATE-OBS { - valtype = STRING; - vptr = wcstem.dateobs; +^{I1}CZP | +^{I1}CZPH { + valtype = FLOAT; + vptr = &(wcstem.czphs); - extkey = "DATE-OBS"; - BEGIN(CCCCCCCC); + sscanf(yytext, "%d", &i); + + if (yyleng == 4) { + keyname = "iCZPna"; + BEGIN(iCCCna); + } else { + if (!(relax & WCSHDR_CNAMn)) vptr = 0x0; + keyname = "iCZPHn"; + BEGIN(iCCCCn); + } } -^DOBS{I1}" " | -^DOBS{I2}" " | -^DOBS{I3}" " { - if (relax & WCSHDR_DOBSn) { - valtype = STRING; - vptr = wcstem.dateobs; - - yyless(4); - BEGIN(CCCCn); +^TCZP | +^TCZPH { + valtype = FLOAT; + vptr = &(wcstem.czphs); + if (yyleng == 4) { + keyname = "TCZPna"; + BEGIN(TCCCna); } else { - keytype = BINTAB; - if (relax & WCSHDR_reject) { - errmsg = "DOBSna keyword is non-standard"; - BEGIN(ERROR); - } else { - BEGIN(DISCARD); - } + if (!(relax & WCSHDR_CNAMn)) vptr = 0x0; + keyname = "TCZPHn"; + BEGIN(TCCCCn); } } -^EPOCH{ALT}" " { - sscanf(yytext, "EPOCH%c", &a); +^CPERI { + valtype = FLOAT; + vptr = &(wcstem.cperi); - if (a == ' ' || (relax & WCSHDR_EPOCHa)) { - valtype = FLOAT; - vptr = &(wcstem.equinox); - special = wcsbth_epoch; + keyname = "CPERIia"; + BEGIN(CCCCCia); + } + +^{I1}CPR | +^{I1}CPER { + valtype = FLOAT; + vptr = &(wcstem.cperi); - unput(a); - extkey = "EPOCH"; - BEGIN(CCCCCCCa); + sscanf(yytext, "%d", &i); + if (yyleng == 4) { + keyname = "iCPRna"; + BEGIN(iCCCna); } else { - keytype = IMGAUX; - if (relax & WCSHDR_reject) { - errmsg = "EPOCH keyword may not have an alternate version code"; - BEGIN(ERROR); - } else { - BEGIN(DISCARD); - } + if (!(relax & WCSHDR_CNAMn)) vptr = 0x0; + keyname = "iCPERn"; + BEGIN(iCCCCn); } } -^EQUINOX | -^EQUI { +^TCPR | +^TCPER { valtype = FLOAT; - vptr = &(wcstem.equinox); + vptr = &(wcstem.cperi); + + if (yyleng == 4) { + keyname = "TCPRna"; + BEGIN(TCCCna); + } else { + if (!(relax & WCSHDR_CNAMn)) vptr = 0x0; + keyname = "TCPERn"; + BEGIN(TCCCCn); + } + } + +^WCSNAME | +^WCSN | +^TWCS { + valtype = STRING; + vptr = wcstem.wcsname; if (yyleng == 7) { - extkey = "EQUINOXa"; + keyname = "WCSNAMEa"; + imherit = 0; BEGIN(CCCCCCCa); + } else { + if (*yytext == 'W') { + keyname = "WCSNna"; + } else { + keyname = "TWCSna"; + } BEGIN(CCCCna); } } -^MJD-AVG" " | -^MJDA { - valtype = FLOAT; - vptr = &(wcstem.mjdavg); +^TIMESYS" " { + valtype = STRING; + vptr = wcstem.timesys; - if (yyleng == 8) { - extkey = "MJD-AVG"; - BEGIN(CCCCCCCC); - } else { - BEGIN(CCCCn); - } + keyname = "TIMESYS"; + BEGIN(CCCCCCCC); } -^MJD-OBS" " | -^MJDOB { - valtype = FLOAT; - vptr = &(wcstem.mjdobs); +^TREFPOS" " | +^TRPOS { + valtype = STRING; + vptr = wcstem.trefpos; if (yyleng == 8) { - extkey = "MJD-OBS"; + if (ctrl < -10) keep = keyrec; + keyname = "TREFPOS"; BEGIN(CCCCCCCC); } else { + keyname = "TRPOSn"; BEGIN(CCCCCn); } } -^OBSGEO-X | -^OBSGX { - valtype = FLOAT; - vptr = wcstem.obsgeo; +^TREFDIR" " | +^TRDIR { + valtype = STRING; + vptr = wcstem.trefdir; if (yyleng == 8) { - extkey = "OBSGEO-X"; + if (ctrl < -10) keep = keyrec; + keyname = "TREFDIR"; BEGIN(CCCCCCCC); } else { + keyname = "TRDIRn"; BEGIN(CCCCCn); } } -^OBSGEO-Y | -^OBSGY { - valtype = FLOAT; - vptr = wcstem.obsgeo + 1; +^PLEPHEM" " { + valtype = STRING; + vptr = wcstem.plephem; - if (yyleng == 8) { - extkey = "OBSGEO-Y"; + keyname = "PLEPHEM"; + BEGIN(CCCCCCCC); + } + +^TIMEUNIT { + valtype = STRING; + vptr = wcstem.timeunit; + + keyname = "TIMEUNIT"; + BEGIN(CCCCCCCC); + } + +^DATEREF" " | +^DATE-REF { + if ((yytext[4] == 'R') || (relax & WCSHDR_DATEREF)) { + valtype = STRING; + vptr = wcstem.dateref; + + keyname = "DATEREF"; BEGIN(CCCCCCCC); + + } else if (relax & WCSHDR_reject) { + errmsg = "the DATE-REF keyword is non-standard"; + BEGIN(ERROR); + } else { - BEGIN(CCCCCn); + BEGIN(DISCARD); } } -^OBSGEO-Z | -^OBSGZ { - valtype = FLOAT; - vptr = wcstem.obsgeo + 2; +^MJDREF" " | +^MJD-REF" " { + if ((yytext[3] == 'R') || (relax & WCSHDR_DATEREF)) { + valtype = FLOAT2; + vptr = wcstem.mjdref; - if (yyleng == 8) { - extkey = "OBSGEO-Z"; + keyname = "MJDREF"; BEGIN(CCCCCCCC); + + } else if (relax & WCSHDR_reject) { + errmsg = "the MJD-REF keyword is non-standard"; + BEGIN(ERROR); + } else { - BEGIN(CCCCCn); + BEGIN(DISCARD); } } -^RADESYS | -^RADE { - valtype = STRING; - vptr = wcstem.radesys; +^MJDREFI" " | +^MJD-REFI { + if ((yytext[3] == 'R') || (relax & WCSHDR_DATEREF)) { + // Actually integer, but treated as float. + valtype = FLOAT; + vptr = wcstem.mjdref; + + keyname = "MJDREFI"; + BEGIN(CCCCCCCC); + + } else if (relax & WCSHDR_reject) { + errmsg = "the MJD-REFI keyword is non-standard"; + BEGIN(ERROR); - if (yyleng == 7) { - extkey = "RADESYSa"; - BEGIN(CCCCCCCa); } else { - BEGIN(CCCCna); + BEGIN(DISCARD); } } -^RADECSYS { - if (relax & WCSHDR_RADECSYS) { - valtype = STRING; - vptr = wcstem.radesys; +^MJDREFF" " | +^MJD-REFF { + if ((yytext[3] == 'R') || (relax & WCSHDR_DATEREF)) { + valtype = FLOAT; + vptr = wcstem.mjdref + 1; - unput(' '); - extkey = "RADECSYS"; - BEGIN(CCCCCCCa); + keyname = "MJDREFF"; + BEGIN(CCCCCCCC); + + } else if (relax & WCSHDR_reject) { + errmsg = "the MJD-REFF keyword is non-standard"; + BEGIN(ERROR); } else { - keytype = IMGAUX; - if (relax & WCSHDR_reject) { - errmsg = "RADECSYS keyword is non-standard"; - BEGIN(ERROR); - } else { - BEGIN(DISCARD); - } + BEGIN(DISCARD); } } -^SPECSYS | -^SPEC { - valtype = STRING; - vptr = wcstem.specsys; +^JDREF" " | +^JD-REF" " { + if ((yytext[2] == 'R') || (relax & WCSHDR_DATEREF)) { + valtype = FLOAT2; + vptr = wcstem.mjdref; + special = wcsbth_jdref; + + keyname = "JDREF"; + BEGIN(CCCCCCCC); + + } else if (relax & WCSHDR_reject) { + errmsg = "the JD-REF keyword is non-standard"; + BEGIN(ERROR); - if (yyleng == 7) { - extkey = "SPECSYSa"; - BEGIN(CCCCCCCa); } else { - BEGIN(CCCCna); + BEGIN(DISCARD); } } -^SSYSOBS | -^SOBS { - valtype = STRING; - vptr = wcstem.ssysobs; +^JDREFI" " | +^JD-REFI { + if ((yytext[2] == 'R') || (relax & WCSHDR_DATEREF)) { + // Actually integer, but treated as float. + valtype = FLOAT; + vptr = wcstem.mjdref; + special = wcsbth_jdrefi; + + keyname = "JDREFI"; + BEGIN(CCCCCCCC); + + } else if (relax & WCSHDR_reject) { + errmsg = "the JD-REFI keyword is non-standard"; + BEGIN(ERROR); - if (yyleng == 7) { - extkey = "SSYSOBSa"; - BEGIN(CCCCCCCa); } else { - BEGIN(CCCCna); + BEGIN(DISCARD); } } -^SSYSSRC | -^SSRC { +^JDREFF" " | +^JD-REFF { + if ((yytext[2] == 'R') || (relax & WCSHDR_DATEREF)) { + valtype = FLOAT; + vptr = wcstem.mjdref; + special = wcsbth_jdreff; + + keyname = "JDREFF"; + BEGIN(CCCCCCCC); + + } else if (relax & WCSHDR_reject) { + errmsg = "the JD-REFF keyword is non-standard"; + BEGIN(ERROR); + + } else { + BEGIN(DISCARD); + } + } + +^TIMEOFFS { + valtype = FLOAT; + vptr = &(wcstem.timeoffs); + + keyname = "TIMEOFFS"; + BEGIN(CCCCCCCC); + } + +^DATE-OBS { + valtype = STRING; + vptr = wcstem.dateobs; + if (ctrl < -10) keep = keyrec; + + keyname = "DATE-OBS"; + imherit = 0; + BEGIN(CCCCCCCC); + } + +^DOBS{I1}" " | +^DOBS{I2}" " | +^DOBS{I3}" " { + valtype = STRING; + vptr = wcstem.dateobs; + + if (relax & WCSHDR_DOBSn) { + yyless(4); + + keyname = "DOBSn"; + BEGIN(CCCCn); + + } else if (relax & WCSHDR_reject) { + errmsg = "DOBSn keyword is non-standard"; + BEGIN(ERROR); + + } else { + BEGIN(DISCARD); + } + } + +^DATE-BEG { + valtype = STRING; + vptr = wcstem.datebeg; + if (ctrl < -10) keep = keyrec; + + keyname = "DATE-BEG"; + BEGIN(CCCCCCCC); + } + +^DATE-AVG | +^DAVG { + valtype = STRING; + vptr = wcstem.dateavg; + + if (yyleng == 8) { + if (ctrl < -10) keep = keyrec; + keyname = "DATE-AVG"; + BEGIN(CCCCCCCC); + } else { + keyname = "DAVGn"; + BEGIN(CCCCn); + } + } + +^DATE-END { + valtype = STRING; + vptr = wcstem.dateend; + if (ctrl < -10) keep = keyrec; + + keyname = "DATE-END"; + BEGIN(CCCCCCCC); + } + +^MJD-OBS" " | +^MJDOB { + valtype = FLOAT; + vptr = &(wcstem.mjdobs); + + if (yyleng == 8) { + if (ctrl < -10) keep = keyrec; + keyname = "MJD-OBS"; + imherit = 0; + BEGIN(CCCCCCCC); + } else { + keyname = "MJDOBn"; + BEGIN(CCCCCn); + } + } + +^MJD-BEG" " { + valtype = FLOAT; + vptr = &(wcstem.mjdbeg); + if (ctrl < -10) keep = keyrec; + + keyname = "MJD-BEG"; + BEGIN(CCCCCCCC); + } + +^MJD-AVG" " | +^MJDA { + valtype = FLOAT; + vptr = &(wcstem.mjdavg); + + if (yyleng == 8) { + if (ctrl < -10) keep = keyrec; + keyname = "MJD-AVG"; + BEGIN(CCCCCCCC); + } else { + keyname = "MJDAn"; + BEGIN(CCCCn); + } + } + +^MJD-END" " { + valtype = FLOAT; + vptr = &(wcstem.mjdend); + if (ctrl < -10) keep = keyrec; + + keyname = "MJD-END"; + BEGIN(CCCCCCCC); + } + +^JEPOCH" " { + valtype = FLOAT; + vptr = &(wcstem.jepoch); + if (ctrl < -10) keep = keyrec; + + keyname = "JEPOCH"; + BEGIN(CCCCCCCC); + } + +^BEPOCH" " { + valtype = FLOAT; + vptr = &(wcstem.bepoch); + if (ctrl < -10) keep = keyrec; + + keyname = "BEPOCH"; + BEGIN(CCCCCCCC); + } + +^TSTART" " { + valtype = FLOAT; + vptr = &(wcstem.tstart); + if (ctrl < -10) keep = keyrec; + + keyname = "TSTART"; + BEGIN(CCCCCCCC); + } + +^TSTOP" " { + valtype = FLOAT; + vptr = &(wcstem.tstop); + if (ctrl < -10) keep = keyrec; + + keyname = "TSTOP"; + BEGIN(CCCCCCCC); + } + +^XPOSURE" " { + valtype = FLOAT; + vptr = &(wcstem.xposure); + if (ctrl < -10) keep = keyrec; + + keyname = "XPOSURE"; + BEGIN(CCCCCCCC); + } + +^TELAPSE" " { + valtype = FLOAT; + vptr = &(wcstem.telapse); + if (ctrl < -10) keep = keyrec; + + keyname = "TELAPSE"; + BEGIN(CCCCCCCC); + } + +^TIMSYER" " { + valtype = FLOAT; + vptr = &(wcstem.timsyer); + if (ctrl < -10) keep = keyrec; + + keyname = "TIMSYER"; + BEGIN(CCCCCCCC); + } + +^TIMRDER" " { + valtype = FLOAT; + vptr = &(wcstem.timrder); + if (ctrl < -10) keep = keyrec; + + keyname = "TIMRDER"; + BEGIN(CCCCCCCC); + } + +^TIMEDEL" " { + valtype = FLOAT; + vptr = &(wcstem.timedel); + if (ctrl < -10) keep = keyrec; + + keyname = "TIMEDEL"; + BEGIN(CCCCCCCC); + } + +^TIMEPIXR { + valtype = FLOAT; + vptr = &(wcstem.timepixr); + chekval = wcsbth_timepixr; + if (ctrl < -10) keep = keyrec; + + keyname = "TIMEPIXR"; + BEGIN(CCCCCCCC); + } + +^OBSGEO-X | +^OBSGX { + valtype = FLOAT; + vptr = wcstem.obsgeo; + + if (yyleng == 8) { + if (ctrl < -10) keep = keyrec; + keyname = "OBSGEO-X"; + BEGIN(CCCCCCCC); + } else { + keyname = "OBSGXn"; + BEGIN(CCCCCn); + } + } + +^OBSGEO-Y | +^OBSGY { + valtype = FLOAT; + vptr = wcstem.obsgeo + 1; + + if (yyleng == 8) { + if (ctrl < -10) keep = keyrec; + keyname = "OBSGEO-Y"; + BEGIN(CCCCCCCC); + } else { + keyname = "OBSGYn"; + BEGIN(CCCCCn); + } + } + +^OBSGEO-Z | +^OBSGZ { + valtype = FLOAT; + vptr = wcstem.obsgeo + 2; + + if (yyleng == 8) { + if (ctrl < -10) keep = keyrec; + keyname = "OBSGEO-Z"; + BEGIN(CCCCCCCC); + } else { + keyname = "OBSGZn"; + BEGIN(CCCCCn); + } + } + +^OBSGEO-L { + valtype = FLOAT; + vptr = wcstem.obsgeo + 3; + if (ctrl < -10) keep = keyrec; + + keyname = "OBSGEO-L"; + BEGIN(CCCCCCCC); + } + +^OBSGL{I1}" " | +^OBSGL{I2}" " | +^OBSGL{I3} { valtype = STRING; - vptr = wcstem.ssyssrc; + vptr = wcstem.obsgeo + 3; + + if (relax & WCSHDR_OBSGLBHn) { + yyless(5); + + keyname = "OBSGLn"; + BEGIN(CCCCCn); + + } else if (relax & WCSHDR_reject) { + errmsg = "OBSGLn keyword is non-standard"; + BEGIN(ERROR); + + } else { + BEGIN(DISCARD); + } + } + +^OBSGEO-B { + valtype = FLOAT; + vptr = wcstem.obsgeo + 4; + if (ctrl < -10) keep = keyrec; + + keyname = "OBSGEO-B"; + BEGIN(CCCCCCCC); + } + +^OBSGB{I1}" " | +^OBSGB{I2}" " | +^OBSGB{I3} { + valtype = STRING; + vptr = wcstem.obsgeo + 3; + + if (relax & WCSHDR_OBSGLBHn) { + yyless(5); + + keyname = "OBSGBn"; + BEGIN(CCCCCn); + + } else if (relax & WCSHDR_reject) { + errmsg = "OBSGBn keyword is non-standard"; + BEGIN(ERROR); + + } else { + BEGIN(DISCARD); + } + } + +^OBSGEO-H { + valtype = FLOAT; + vptr = wcstem.obsgeo + 5; + if (ctrl < -10) keep = keyrec; + + keyname = "OBSGEO-H"; + BEGIN(CCCCCCCC); + } + +^OBSGH{I1}" " | +^OBSGH{I2}" " | +^OBSGH{I3} { + valtype = STRING; + vptr = wcstem.obsgeo + 3; + + if (relax & WCSHDR_OBSGLBHn) { + yyless(5); + + keyname = "OBSGHn"; + BEGIN(CCCCCn); + + } else if (relax & WCSHDR_reject) { + errmsg = "OBSGHn keyword is non-standard"; + BEGIN(ERROR); + + } else { + BEGIN(DISCARD); + } + } + +^OBSORBIT { + valtype = STRING; + vptr = wcstem.obsorbit; + + keyname = "OBSORBIT"; + BEGIN(CCCCCCCC); + } + +^RADESYS | +^RADE { + valtype = STRING; + vptr = wcstem.radesys; if (yyleng == 7) { - extkey = "SSYSSRCa"; + keyname = "RADESYSa"; + imherit = 0; BEGIN(CCCCCCCa); } else { + keyname = "RADEna"; BEGIN(CCCCna); } } -^VELOSYS | -^VSYS { +^RADECSYS { + if (relax & WCSHDR_RADECSYS) { + valtype = STRING; + vptr = wcstem.radesys; + + unput(' '); + + keyname = "RADECSYS"; + imherit = 0; + BEGIN(CCCCCCCa); + + } else if (relax & WCSHDR_reject) { + errmsg = "the RADECSYS keyword is deprecated, use RADESYSa"; + BEGIN(ERROR); + + } else { + BEGIN(DISCARD); + } + } + +^EPOCH{ALT}" " { + sscanf(yytext, "EPOCH%c", &a); + + if (relax & WCSHDR_strict) { + errmsg = "the EPOCH keyword is deprecated, use EQUINOXa"; + BEGIN(ERROR); + + } else if (a == ' ' || (relax & WCSHDR_EPOCHa)) { + valtype = FLOAT; + vptr = &(wcstem.equinox); + special = wcsbth_epoch; + + unput(a); + + keyname = "EPOCH"; + imherit = 0; + BEGIN(CCCCCCCa); + + } else if (relax & WCSHDR_reject) { + errmsg = "EPOCH keyword may not have an alternate version code"; + BEGIN(ERROR); + + } else { + BEGIN(DISCARD); + } + } + +^EQUINOX | +^EQUI { valtype = FLOAT; - vptr = &(wcstem.velosys); + vptr = &(wcstem.equinox); if (yyleng == 7) { - extkey = "VELOSYSa"; + keyname = "EQUINOXa"; + imherit = 0; BEGIN(CCCCCCCa); } else { + keyname = "EQUIna"; BEGIN(CCCCna); } } -^VELANGL | -^VANG { - valtype = FLOAT; - vptr = &(wcstem.velangl); +^SPECSYS | +^SPEC { + valtype = STRING; + vptr = wcstem.specsys; if (yyleng == 7) { - extkey = "VELANGLa"; + keyname = "SPECSYSa"; BEGIN(CCCCCCCa); } else { + keyname = "SPECna"; BEGIN(CCCCna); } } -^VELREF{ALT}" " { - sscanf(yytext, "VELREF%c", &a); - - if (a == ' ' || (relax & WCSHDR_VELREFa)) { - valtype = INTEGER; - vptr = &(wcstem.velref); +^SSYSOBS | +^SOBS { + valtype = STRING; + vptr = wcstem.ssysobs; - unput(a); - extkey = "VELREF"; + if (yyleng == 7) { + keyname = "SSYSOBSa"; BEGIN(CCCCCCCa); + } else { + keyname = "SOBSna"; + BEGIN(CCCCna); + } + } + +^VELOSYS | +^VSYS { + valtype = FLOAT; + vptr = &(wcstem.velosys); + if (yyleng == 7) { + keyname = "VELOSYSa"; + BEGIN(CCCCCCCa); } else { - keytype = IMGAUX; - if (relax & WCSHDR_reject) { - errmsg = "VELREF keyword may not have an alternate version code"; - BEGIN(ERROR); - } else { - BEGIN(DISCARD); - } + keyname = "VSYSna"; + BEGIN(CCCCna); } } ^VSOURCE{ALT} { if (relax & WCSHDR_VSOURCE) { valtype = FLOAT; - vptr = &(wcstem.zsource); + vptr = &(wcstem.zsource); special = wcsbth_vsource; yyless(7); - extkey = "VSOURCEa"; + + keyname = "VSOURCEa"; BEGIN(CCCCCCCa); + } else if (relax & WCSHDR_reject) { + errmsg = "the VSOURCEa keyword is deprecated, use ZSOURCEa"; + BEGIN(ERROR); + } else { - keytype = IMGAUX; - if (relax & WCSHDR_reject) { - errmsg = "VSOURCEa keyword is deprecated"; - BEGIN(ERROR); - } else { - BEGIN(DISCARD); - } + BEGIN(DISCARD); } } @@ -1189,56 +1801,169 @@ int wcsbth_final(struct wcsbth_alts *alts, int *nwcs, struct wcsprm **wcs); ^VSOU{I3}{ALT} { if (relax & WCSHDR_VSOURCE) { valtype = FLOAT; - vptr = &(wcstem.zsource); + vptr = &(wcstem.zsource); special = wcsbth_vsource; yyless(4); + keyname = "VSOUna"; BEGIN(CCCCna); + } else if (relax & WCSHDR_reject) { + errmsg = "VSOUna keyword is deprecated, use ZSOUna"; + BEGIN(ERROR); + } else { - keytype = BINTAB; - if (relax & WCSHDR_reject) { - errmsg = "VSOUna keyword is deprecated"; - BEGIN(ERROR); - } else { - /* Pretend we don't recognize it. */ - BEGIN(DISCARD); - } + // Pretend we don't recognize it. + BEGIN(DISCARD); } } -^WCSNAME | -^WCSN | -^TWCS { +^ZSOURCE | +^ZSOU { + valtype = FLOAT; + vptr = &(wcstem.zsource); + + if (yyleng == 7) { + keyname = "ZSOURCEa"; + BEGIN(CCCCCCCa); + } else { + keyname = "ZSOUna"; + BEGIN(CCCCna); + } + } + +^SSYSSRC | +^SSRC { valtype = STRING; - vptr = wcstem.wcsname; + vptr = wcstem.ssyssrc; if (yyleng == 7) { - extkey = "WCSNAMEa"; + keyname = "SSYSSRCa"; BEGIN(CCCCCCCa); } else { + keyname = "SSRCna"; BEGIN(CCCCna); } } -^ZSOURCE | -^ZSOU { +^VELANGL | +^VANG { valtype = FLOAT; - vptr = &(wcstem.zsource); + vptr = &(wcstem.velangl); if (yyleng == 7) { - extkey = "ZSOURCEa"; + keyname = "VELANGLa"; BEGIN(CCCCCCCa); } else { + keyname = "VANGna"; BEGIN(CCCCna); } } +^RSUN_REF { + valtype = FLOAT; + auxprm = 1; + vptr = &(auxtem.rsun_ref); + + keyname = "RSUN_REF"; + BEGIN(CCCCCCCC); + } + +^DSUN_OBS { + valtype = FLOAT; + auxprm = 1; + vptr = &(auxtem.dsun_obs); + + keyname = "DSUN_OBS"; + BEGIN(CCCCCCCC); + } + +^CRLN_OBS { + valtype = FLOAT; + auxprm = 1; + vptr = &(auxtem.crln_obs); + + keyname = "CRLN_OBS"; + BEGIN(CCCCCCCC); + } + +^HGLN_OBS { + valtype = FLOAT; + auxprm = 1; + vptr = &(auxtem.hgln_obs); + + keyname = "HGLN_OBS"; + BEGIN(CCCCCCCC); + } + +^CRLT_OBS | +^HGLT_OBS { + valtype = FLOAT; + auxprm = 1; + vptr = &(auxtem.hglt_obs); + + keyname = "HGLT_OBS"; + BEGIN(CCCCCCCC); + } + +^A_RADIUS { + valtype = FLOAT; + auxprm = 1; + vptr = &(auxtem.a_radius); + + keyname = "A_RADIUS"; + BEGIN(CCCCCCCC); + } + +^B_RADIUS { + valtype = FLOAT; + auxprm = 1; + vptr = &(auxtem.b_radius); + + keyname = "B_RADIUS"; + BEGIN(CCCCCCCC); + } + +^C_RADIUS { + valtype = FLOAT; + auxprm = 1; + vptr = &(auxtem.c_radius); + + keyname = "C_RADIUS"; + BEGIN(CCCCCCCC); + } + +^BLON_OBS { + valtype = FLOAT; + auxprm = 1; + vptr = &(auxtem.blon_obs); + + keyname = "BLON_OBS"; + BEGIN(CCCCCCCC); + } + +^BLAT_OBS { + valtype = FLOAT; + auxprm = 1; + vptr = &(auxtem.blat_obs); + + keyname = "BLAT_OBS"; + BEGIN(CCCCCCCC); + } + +^BDIS_OBS { + valtype = FLOAT; + auxprm = 1; + vptr = &(auxtem.bdis_obs); + + keyname = "BDIS_OBS"; + BEGIN(CCCCCCCC); + } + ^END" "{77} { - yyless(0); - if (wcsbth_nkeyrec) { - wcsbth_nkeyrec = 0; - errmsg = "Keyrecords following the END keyrecord were ignored"; + if (yyextra->nkeyrec) { + yyextra->nkeyrec = 0; + errmsg = "keyrecords following the END keyrecord were ignored"; BEGIN(ERROR); } else { BEGIN(DISCARD); @@ -1246,40 +1971,85 @@ int wcsbth_final(struct wcsbth_alts *alts, int *nwcs, struct wcsprm **wcs); } ^. { - yyless(0); BEGIN(DISCARD); } {I1}{ALT}" " | {I2}{ALT} { - /* Image-header keyword. */ - keytype = IMGAXIS; if (relax & WCSHDR_ALLIMG) { sscanf(yytext, "%d%c", &i, &a); + keytype = IMGAXIS; BEGIN(VALUE); } else if (relax & WCSHDR_reject) { errmsg = errtxt; sprintf(errmsg, - "Image-header keyword %s in binary table", extkey); + "image-header keyword %s in binary table", keyname); BEGIN(ERROR); } else { - /* Pretend we don't recognize it. */ + // Pretend we don't recognize it. BEGIN(DISCARD); } } -{I3} { - /* Invalid axis number in image-header keyword. */ - keytype = IMGAXIS; +0{I1}{ALT} | +00{I1} { if (relax & WCSHDR_ALLIMG) { - /* Will also be flagged by as invalid. */ - sscanf(yytext, "%3d", &i); - BEGIN(VALUE); + if (relax & WCSHDR_reject) { + // Violates the basic FITS standard. + errmsg = "indices in parameterized keywords must not have " + "leading zeroes"; + BEGIN(ERROR); + + } else { + // Pretend we don't recognize it. + BEGIN(DISCARD); + } + + } else if (relax & WCSHDR_reject) { + errmsg = errtxt; + sprintf(errmsg, + "invalid image-header keyword %s in binary table", keyname); + BEGIN(ERROR); + + } else { + // Pretend we don't recognize it. + BEGIN(DISCARD); + } + } + +0{ALT}" " | +00{ALT} | +{Z3} { + // Anything that has fallen through to this point must contain + // an invalid axis number. + if (relax & WCSHDR_ALLIMG) { + errmsg = "axis number must exceed 0"; + BEGIN(ERROR); + + } else if (relax & WCSHDR_reject) { + errmsg = errtxt; + sprintf(errmsg, + "invalid image-header keyword %s in binary table", keyname); + BEGIN(ERROR); + + } else { + // Pretend we don't recognize it. + BEGIN(DISCARD); + } + } + +. { + if (relax & WCSHDR_reject) { + // Looks too much like a FITS WCS keyword not to flag it. + errmsg = errtxt; + sprintf(errmsg, "keyword looks very much like %s but isn't", + keyname); + BEGIN(ERROR); } else { - /* Pretend we don't recognize it. */ + // Pretend we don't recognize it. BEGIN(DISCARD); } } @@ -1293,15 +2063,14 @@ int wcsbth_final(struct wcsbth_alts *alts, int *nwcs, struct wcsprm **wcs); if (vptr) { WCSBTH_PUTBACK; BEGIN((YY_START == iCCCCn) ? iCCCna : TCCCna); + + } else if (relax & WCSHDR_reject) { + errmsg = errtxt; + sprintf(errmsg, "%s keyword is non-standard", keyname); + BEGIN(ERROR); + } else { - keytype = (YY_START == iCCCCn) ? BIMGARR : PIXLIST; - if (relax & WCSHDR_reject) { - errmsg = errtxt; - sprintf(errmsg, "%s keyword is non-standard", extkey); - BEGIN(ERROR); - } else { - BEGIN(DISCARD); - } + BEGIN(DISCARD); } } @@ -1313,22 +2082,19 @@ int wcsbth_final(struct wcsbth_alts *alts, int *nwcs, struct wcsprm **wcs); WCSBTH_PUTBACK; BEGIN((YY_START == iCCCCn) ? iCCCna : TCCCna); - } else { - keytype = (YY_START == iCCCna) ? BIMGARR : PIXLIST; - if (relax & WCSHDR_reject) { - errmsg = errtxt; - if (!vptr) { - sprintf(errmsg, "%s keyword is non-standard", extkey); - } else { - sprintf(errmsg, - "%s keyword may not have an alternate version code", extkey); - } - BEGIN(ERROR); - + } else if (relax & WCSHDR_reject) { + errmsg = errtxt; + if (!vptr) { + sprintf(errmsg, "%s keyword is non-standard", keyname); } else { - /* Pretend we don't recognize it. */ - BEGIN(DISCARD); + sprintf(errmsg, + "%s keyword may not have an alternate version code", keyname); } + BEGIN(ERROR); + + } else { + // Pretend we don't recognize it. + BEGIN(DISCARD); } } @@ -1358,60 +2124,159 @@ int wcsbth_final(struct wcsbth_alts *alts, int *nwcs, struct wcsprm **wcs); {I1}_{I2}{ALT}" " | {I2}_{I1}{ALT}" " | {I2}_{I2}{ALT} { - /* Image-header keyword. */ if (relax & WCSHDR_ALLIMG) { - sscanf(yytext, "%d_%d%c", &i, &j, &a); - keytype = IMGAXIS; - BEGIN(VALUE); + sscanf(yytext, "%d_%d%c", &i, &j, &a); + keytype = IMGAXIS; + BEGIN(VALUE); + + } else if (relax & WCSHDR_reject) { + errmsg = errtxt; + sprintf(errmsg, + "image-header keyword %s in binary table", keyname); + BEGIN(ERROR); + + } else { + // Pretend we don't recognize it. + BEGIN(DISCARD); + } + } + +0{I1}_{I1}{ALT}" " | +{I1}_0{I1}{ALT}" " | +00{I1}_{I1}{ALT} | +0{I1}_0{I1}{ALT} | +{I1}_00{I1}{ALT} | +000{I1}_{I1} | +00{I1}_0{I1} | +0{I1}_00{I1} | +{I1}_000{I1} | +0{I1}_{I2}{ALT} | +{I1}_0{I2}{ALT} | +00{I1}_{I2} | +0{I1}_0{I2} | +{I1}_00{I2} | +0{I2}_{I1}{ALT} | +{I2}_0{I1}{ALT} | +00{I2}_{I1} | +0{I2}_0{I1} | +{I2}_00{I1} | +0{I2}_{I2} | +{I2}_0{I2} { + if (relax & WCSHDR_ALLIMG) { + if (((altlin == 1) && (relax & WCSHDR_PC0i_0ja)) || + ((altlin == 2) && (relax & WCSHDR_CD0i_0ja))) { + sscanf(yytext, "%d_%d%c", &i, &j, &a); + keytype = IMGAXIS; + BEGIN(VALUE); + + } else if (relax & WCSHDR_reject) { + errmsg = "indices in parameterized keywords must not have " + "leading zeroes"; + BEGIN(ERROR); + + } else { + // Pretend we don't recognize it. + BEGIN(DISCARD); + } + + } else if (relax & WCSHDR_reject) { + errmsg = errtxt; + sprintf(errmsg, + "invalid image-header keyword %s in binary table", keyname); + BEGIN(ERROR); + + } else { + // Pretend we don't recognize it. + BEGIN(DISCARD); + } + } + +{Z1}_{Z1}{ALT}" " | +{Z2}_{Z1}{ALT}" " | +{Z1}_{Z2}{ALT}" " | +{Z3}_{Z1}{ALT} | +{Z2}_{Z2}{ALT} | +{Z1}_{Z3}{ALT} | +{Z4}_{Z1} | +{Z3}_{Z2} | +{Z2}_{Z3} | +{Z1}_{Z4} { + // Anything that has fallen through to this point must contain + // an invalid axis number. + if (relax & WCSHDR_ALLIMG) { + errmsg = "axis number must exceed 0"; + BEGIN(ERROR); } else if (relax & WCSHDR_reject) { errmsg = errtxt; sprintf(errmsg, - "Image-header keyword %s in binary table", extkey); + "invalid image-header keyword %s in binary table", keyname); BEGIN(ERROR); } else { - /* Pretend we don't recognize it. */ + // Pretend we don't recognize it. BEGIN(DISCARD); } } -{I1}_{I3}{ALT} | -{I3}_{I1}{ALT} | -{I1}_{I4} | -{I2}_{I3} | -{I3}_{I2} | -{I4}_{I1} { - /* Invalid axis number in image-header keyword. */ +{Z1}-{Z1}{ALT}" " | +{Z2}-{Z1}{ALT}" " | +{Z1}-{Z2}{ALT}" " | +{Z3}-{Z1}{ALT} | +{Z2}-{Z2}{ALT} | +{Z1}-{Z3}{ALT} | +{Z4}-{Z1} | +{Z3}-{Z2} | +{Z2}-{Z3} | +{Z1}-{Z4} { if (relax & WCSHDR_ALLIMG) { - /* Will be flagged by as invalid. */ - sscanf(yytext, "%d_%d", &i, &j); - keytype = IMGAXIS; - BEGIN(VALUE); + errmsg = errtxt; + sprintf(errmsg, "%s keyword must use an underscore, not a dash", + keyname); + BEGIN(ERROR); + + } else if (relax & WCSHDR_reject) { + errmsg = errtxt; + sprintf(errmsg, + "invalid image-header keyword %s in binary table", keyname); + BEGIN(ERROR); } else { - /* Pretend we don't recognize it. */ + // Pretend we don't recognize it. BEGIN(DISCARD); } } -{I0}{6} { - /* This covers the defunct forms CD00i00j and PC00i00j. */ - if (((relax & WCSHDR_PC00i00j) && (altlin == 1)) || - ((relax & WCSHDR_CD00i00j) && (altlin == 2))) { - sscanf(yytext, "%3d%3d", &i, &j); - a = ' '; - keytype = IMGAXIS; - BEGIN(VALUE); +{Z1}{6} { + // This covers the defunct forms CD00i00j and PC00i00j. + if (relax & WCSHDR_ALLIMG) { + if (((altlin == 1) && (relax & WCSHDR_PC00i00j)) || + ((altlin == 2) && (relax & WCSHDR_CD00i00j))) { + sscanf(yytext, "%3d%3d", &i, &j); + a = ' '; + keytype = IMGAXIS; + BEGIN(VALUE); + + } else if (relax & WCSHDR_reject) { + errmsg = errtxt; + sprintf(errmsg, + "this form of the %s keyword is deprecated, use %s", + keyname, keyname); + BEGIN(ERROR); + + } else { + // Pretend we don't recognize it. + BEGIN(DISCARD); + } } else if (relax & WCSHDR_reject) { errmsg = errtxt; - sprintf(errmsg, "Defunct form of %si_ja keyword", - (altlin==1) ? "PC" : "CD"); + sprintf(errmsg, + "deprecated image-header keyword %s in binary table", keyname); BEGIN(ERROR); } else { - /* Pretend we don't recognize it. */ + // Pretend we don't recognize it. BEGIN(DISCARD); } } @@ -1440,11 +2305,11 @@ int wcsbth_final(struct wcsbth_alts *alts, int *nwcs, struct wcsprm **wcs); } else if (relax & WCSHDR_reject) { errmsg = errtxt; - sprintf(errmsg, "%s keyword is non-standard", extkey); + sprintf(errmsg, "%s keyword is non-standard", keyname); BEGIN(ERROR); } else { - /* Pretend we don't recognize it. */ + // Pretend we don't recognize it. BEGIN(DISCARD); } } @@ -1482,30 +2347,50 @@ int wcsbth_final(struct wcsbth_alts *alts, int *nwcs, struct wcsprm **wcs); BEGIN(DISCARD); } -{I1}" " | -{I2}" " { - yyless(0); - BEGIN(CCCCCia); - } - -{I1}[A-Z]" " | -{I2}[A-Z] { - if (relax & WCSHDR_CROTAia) { - yyless(0); - BEGIN(CCCCCia); +{Z1}{ALT}" " | +{Z2}{ALT} | +{Z3} { + if (relax & WCSHDR_ALLIMG) { + a = ' '; + sscanf(yytext, "%d%c", &i, &a); + + if (relax & WCSHDR_strict) { + errmsg = "the CROTAn keyword is deprecated, use PCi_ja"; + BEGIN(ERROR); + + } else if (a == ' ' || relax & WCSHDR_CROTAia) { + yyless(0); + BEGIN(CCCCCia); + + } else if (relax & WCSHDR_reject) { + errmsg = "CROTAn keyword may not have an alternate version code"; + BEGIN(ERROR); + + } else { + // Pretend we don't recognize it. + BEGIN(DISCARD); + } } else if (relax & WCSHDR_reject) { - errmsg = "CROTAn keyword may not have an alternate version code"; + errmsg = errtxt; + sprintf(errmsg, + "deprecated image-header keyword %s in binary table", keyname); BEGIN(ERROR); } else { - /* Pretend we don't recognize it. */ + // Pretend we don't recognize it. BEGIN(DISCARD); } } . { - BEGIN(DISCARD); + if (relax & WCSHDR_ALLIMG) { + yyless(0); + BEGIN(CCCCCia); + } else { + // Let it go. + BEGIN(DISCARD); + } } {I1}" " | @@ -1529,11 +2414,11 @@ int wcsbth_final(struct wcsbth_alts *alts, int *nwcs, struct wcsprm **wcs); } else if (relax & WCSHDR_reject) { errmsg = errtxt; sprintf(errmsg, - "%s keyword may not have an alternate version code", extkey); + "%s keyword may not have an alternate version code", keyname); BEGIN(ERROR); } else { - /* Pretend we don't recognize it. */ + // Pretend we don't recognize it. BEGIN(DISCARD); } } @@ -1545,8 +2430,8 @@ int wcsbth_final(struct wcsbth_alts *alts, int *nwcs, struct wcsprm **wcs); {ALT} | . { - /* Image-header keyword. */ - if (relax & (WCSHDR_AUXIMG | WCSHDR_ALLIMG)) { + // Image-header keyword. + if (imherit || (relax & (WCSHDR_AUXIMG | WCSHDR_ALLIMG))) { if (YY_START == CCCCCCCa) { sscanf(yytext, "%c", &a); } else { @@ -1559,17 +2444,27 @@ int wcsbth_final(struct wcsbth_alts *alts, int *nwcs, struct wcsprm **wcs); } else if (relax & WCSHDR_reject) { errmsg = errtxt; sprintf(errmsg, - "Image-header keyword %s in binary table", extkey); + "image-header keyword %s in binary table", keyname); BEGIN(ERROR); } else { - /* Pretend we don't recognize it. */ + // Pretend we don't recognize it. BEGIN(DISCARD); } } . { - BEGIN(DISCARD); + if (relax & WCSHDR_reject) { + // Looks too much like a FITS WCS keyword not to flag it. + errmsg = errtxt; + sprintf(errmsg, "invalid alternate code, keyword resembles %s " + "but isn't", keyname); + BEGIN(ERROR); + + } else { + // Pretend we don't recognize it. + BEGIN(DISCARD); + } } {I1}{ALT}" " | @@ -1612,11 +2507,11 @@ int wcsbth_final(struct wcsbth_alts *alts, int *nwcs, struct wcsprm **wcs); BEGIN(DISCARD); } -{I1}_{I0}{ALT}" " | +{I1}_{Z1}{ALT}" " | {I1}_{I2}{ALT}" " | -{I2}_{I0}{ALT}" " | +{I2}_{Z1}{ALT}" " | {I2}_{I2}{ALT} { - /* Image-header keyword. */ + // Image-header keyword. if (relax & WCSHDR_ALLIMG) { sscanf(yytext, "%d_%d%c", &i, &m, &a); keytype = IMGAXIS; @@ -1625,61 +2520,136 @@ int wcsbth_final(struct wcsbth_alts *alts, int *nwcs, struct wcsprm **wcs); } else if (relax & WCSHDR_reject) { errmsg = errtxt; sprintf(errmsg, - "Image-header keyword %s in binary table", extkey); + "image-header keyword %s in binary table", keyname); BEGIN(ERROR); } else { - /* Pretend we don't recognize it. */ + // Pretend we don't recognize it. BEGIN(DISCARD); } } -{I1}_{I3}{ALT} | -{I3}_{I0}{ALT} | -{I1}_{I4} | -{I2}_{I3} | -{I3}_{I2} | -{I4}_{I0} { - /* Invalid parameter in image-header keyword. */ +0{I1}_{Z1}{ALT}" " | +{I1}_0{Z1}{ALT}" " | +00{I1}_{Z1}{ALT} | +0{I1}_0{Z1}{ALT} | +{I1}_00{Z1}{ALT} | +000{I1}_{Z1} | +00{I1}_0{Z1} | +0{I1}_00{Z1} | +{I1}_000{Z1} | +0{I1}_{I2}{ALT} | +{I1}_0{I2}{ALT} | +00{I1}_{I2} | +0{I1}_0{I2} | +{I1}_00{I2} | +0{I2}_{Z1}{ALT} | +{I2}_0{Z1}{ALT} | +00{I2}_{Z1} | +0{I2}_0{Z1} | +{I2}_00{Z1} | +0{I2}_{I2} | +{I2}_0{I2} { if (relax & WCSHDR_ALLIMG) { - /* Will be flagged by as invalid. */ - sscanf(yytext, "%d_%d", &i, &m); - keytype = IMGAXIS; - BEGIN(VALUE); + if (((valtype == FLOAT) && (relax & WCSHDR_PV0i_0ma)) || + ((valtype == STRING) && (relax & WCSHDR_PS0i_0ma))) { + sscanf(yytext, "%d_%d%c", &i, &m, &a); + keytype = IMGAXIS; + BEGIN(VALUE); + + } else if (relax & WCSHDR_reject) { + errmsg = "indices in parameterized keywords must not have " + "leading zeroes"; + BEGIN(ERROR); + + } else { + // Pretend we don't recognize it. + BEGIN(DISCARD); + } + + } else if (relax & WCSHDR_reject) { + errmsg = errtxt; + sprintf(errmsg, + "invalid image-header keyword %s in binary table", keyname); + BEGIN(ERROR); + + } else { + // Pretend we don't recognize it. + BEGIN(DISCARD); + } + } + +{Z1}_{Z1}{ALT}" " | +{Z2}_{Z1}{ALT}" " | +{Z1}_{Z2}{ALT}" " | +{Z3}_{Z1}{ALT} | +{Z2}_{Z2}{ALT} | +{Z1}_{Z3}{ALT} | +{Z4}_{Z1} | +{Z3}_{Z2} | +{Z2}_{Z3} | +{Z1}_{Z4} { + if (relax & WCSHDR_ALLIMG) { + // Anything that has fallen through to this point must contain + // an invalid parameter. + errmsg = "axis number must exceed 0"; + BEGIN(ERROR); + + } else if (relax & WCSHDR_reject) { + errmsg = errtxt; + sprintf(errmsg, + "invalid image-header keyword %s in binary table", keyname); + BEGIN(ERROR); } else { - /* Pretend we don't recognize it. */ + // Pretend we don't recognize it. BEGIN(DISCARD); } } +{Z1}-{Z1}{ALT}" " | +{Z2}-{Z1}{ALT}" " | +{Z1}-{Z2}{ALT}" " | +{Z3}-{Z1}{ALT} | +{Z2}-{Z2}{ALT} | +{Z1}-{Z3}{ALT} | +{Z4}-{Z1} | +{Z3}-{Z2} | +{Z2}-{Z3} | +{Z1}-{Z4} { + errmsg = errtxt; + sprintf(errmsg, "%s keyword must use an underscore, not a dash", + keyname); + BEGIN(ERROR); + } + . { BEGIN(DISCARD); } -{I1}_{I0}{ALT}" " | +{I1}_{Z1}{ALT}" " | {I1}_{I2}{ALT} | {I1}_{I3} | -{I2}_{I0}{ALT} | +{I2}_{Z1}{ALT} | {I2}_{I2} | -{I3}_{I0} | -{I1}_{I0}{ALT}" " | +{I3}_{Z1} | +{I1}_{Z1}{ALT}" " | {I1}_{I2}{ALT} | {I1}_{I3} | -{I2}_{I0}{ALT} | +{I2}_{Z1}{ALT} | {I2}_{I2} | -{I3}_{I0} { +{I3}_{Z1} { if (relax & WCSHDR_LONGKEY) { WCSBTH_PUTBACK; BEGIN((YY_START == iCCn_ma) ? iCn_ma : TCn_ma); } else if (relax & WCSHDR_reject) { errmsg = errtxt; - sprintf(errmsg, "%s keyword is non-standard", extkey); + sprintf(errmsg, "the %s keyword is non-standard", keyname); BEGIN(ERROR); } else { - /* Pretend we don't recognize it. */ + // Pretend we don't recognize it. BEGIN(DISCARD); } } @@ -1689,18 +2659,18 @@ int wcsbth_final(struct wcsbth_alts *alts, int *nwcs, struct wcsprm **wcs); BEGIN(DISCARD); } -{I1}_{I0}{ALT}" " | +{I1}_{Z1}{ALT}" " | {I1}_{I2}{ALT}" " | {I1}_{I3}{ALT} | -{I2}_{I0}{ALT}" " | +{I2}_{Z1}{ALT}" " | {I2}_{I2}{ALT} | -{I3}_{I0}{ALT} | -{I1}_{I0}{ALT}" " | +{I3}_{Z1}{ALT} | +{I1}_{Z1}{ALT}" " | {I1}_{I2}{ALT}" " | {I1}_{I3}{ALT} | -{I2}_{I0}{ALT}" " | +{I2}_{Z1}{ALT}" " | {I2}_{I2}{ALT} | -{I3}_{I0}{ALT} { +{I3}_{Z1}{ALT} { sscanf(yytext, "%d_%d%c", &n, &m, &a); if (YY_START == TCn_ma) i = wcsbth_colax(*wcs, &alts, n, a); keytype = (YY_START == iCn_ma) ? BIMGARR : PIXLIST; @@ -1710,12 +2680,12 @@ int wcsbth_final(struct wcsbth_alts *alts, int *nwcs, struct wcsprm **wcs); {I1}_{I4} | {I2}_{I3} | {I3}_{I2} | -{I4}_{I0} | +{I4}_{Z1} | {I1}_{I4} | {I2}_{I3} | {I3}_{I2} | -{I4}_{I0} { - /* Invalid combinations will be flagged by . */ +{I4}_{Z1} { + // Invalid combinations will be flagged by . sscanf(yytext, "%d_%d", &n, &m); a = ' '; if (YY_START == TCn_ma) i = wcsbth_colax(*wcs, &alts, n, a); @@ -1728,7 +2698,7 @@ int wcsbth_final(struct wcsbth_alts *alts, int *nwcs, struct wcsprm **wcs); BEGIN(DISCARD); } -{I0}" " { +{Z1}" " { if (relax & WCSHDR_PROJPn) { sscanf(yytext, "%d", &m); i = 0; @@ -1737,11 +2707,22 @@ int wcsbth_final(struct wcsbth_alts *alts, int *nwcs, struct wcsprm **wcs); BEGIN(VALUE); } else if (relax & WCSHDR_reject) { - errmsg = "PROJPn keyword is defunct"; + errmsg = "the PROJPn keyword is deprecated, use PVi_ma"; + BEGIN(ERROR); + + } else { + // Pretend we don't recognize it. + BEGIN(DISCARD); + } + } + +{Z2}" " | +{Z3} { + if (relax & (WCSHDR_PROJPn | WCSHDR_reject)) { + errmsg = "invalid PROJPn keyword"; BEGIN(ERROR); } else { - /* Pretend we don't recognize it. */ BEGIN(DISCARD); } } @@ -1751,18 +2732,17 @@ int wcsbth_final(struct wcsbth_alts *alts, int *nwcs, struct wcsprm **wcs); } =" "+ { - /* Do checks on i, j, m, n, k. */ + // Do checks on i, j, m, n, k. if (!(keytype & keysel)) { - /* Selection by keyword type. */ + // Selection by keyword type. BEGIN(DISCARD); } else if (exclude[n] || exclude[k]) { - /* One or other column is not selected. */ + // One or other column is not selected. if (k && (exclude[n] != exclude[k])) { - /* For keywords such as TCn_ka, both columns must be excluded. - User error, so return immediately. */ - yylex_destroy(); - return 3; + // For keywords such as TCn_ka, both columns must be excluded. + // User error, so return immediately. + return WCSHDRERR_BAD_COLUMN; } else { BEGIN(DISCARD); @@ -1772,279 +2752,385 @@ int wcsbth_final(struct wcsbth_alts *alts, int *nwcs, struct wcsprm **wcs); if (relax & WCSHDR_reject) { errmsg = errtxt; if (i > 99 || j > 99) { - sprintf(errmsg, "Axis number exceeds 99"); + sprintf(errmsg, "axis number exceeds 99"); } else if (m > 99) { - sprintf(errmsg, "Parameter number exceeds 99"); + sprintf(errmsg, "parameter number exceeds 99"); } else if (n > 999 || k > 999) { - sprintf(errmsg, "Column number exceeds 999"); + sprintf(errmsg, "column number exceeds 999"); } BEGIN(ERROR); } else { - /* Pretend we don't recognize it. */ + // Pretend we don't recognize it. BEGIN(DISCARD); } } else if (ipass == 2 && npass == 3 && (keytype & BINTAB)) { - /* Skip keyvalues that won't be inherited. */ + // Skip keyvalues that won't be inherited. BEGIN(FLUSH); - } else if (ipass == 3 && (keytype & IMGHEAD)) { - /* IMGHEAD keytypes are always dealt with on the second pass. */ - BEGIN(FLUSH); - - } else if (vptr) { - alts.icol = 0; - alts.ialt = 0; - voff = (char *)vptr - (char *)(&wcstem); + } else { + if (ipass == 3 && (keytype & IMGHEAD)) { + // IMGHEAD keytypes are always dealt with on the second pass. + // However, they must be re-parsed in order to report errors. + vptr = 0x0; + } if (valtype == INTEGER) { BEGIN(INTEGER_VAL); } else if (valtype == FLOAT) { BEGIN(FLOAT_VAL); + } else if (valtype == FLOAT2) { + BEGIN(FLOAT2_VAL); } else if (valtype == STRING) { BEGIN(STRING_VAL); } else { errmsg = errtxt; - sprintf(errmsg, "Internal parser ERROR, bad data type: %d", + sprintf(errmsg, "internal parser ERROR, bad data type: %d", valtype); BEGIN(ERROR); } - - } else { - errmsg = "Internal parser ERROR, null pointer"; - BEGIN(ERROR); } } . { - errmsg = "Invalid KEYWORD = VALUE syntax"; + errmsg = "invalid KEYWORD = VALUE syntax"; BEGIN(ERROR); } {INTEGER} { if (ipass == 1) { - /* Do first-pass bookkeeping. */ - wcsbth_pass1(keytype, i, j, n, k, a, ptype, &alts); - BEGIN(FLUSH); + BEGIN(COMMENT); } else { - /* Update each coordinate representation. */ - while ((wcsp = wcsbth_idx(*wcs, &alts, keytype, n, a))) { - wptr = (void *)((char *)wcsp + voff); - - /* Read the keyvalue. */ - if (special) { - special(wptr); - } else { - sscanf(yytext, "%d", (int *)wptr); - } - } + // Read the keyvalue. + sscanf(yytext, "%d", &inttmp); BEGIN(COMMENT); } } . { - errmsg = "An integer value was expected"; + errmsg = "an integer value was expected"; BEGIN(ERROR); } {FLOAT} { if (ipass == 1) { - /* Do first-pass bookkeeping. */ - wcsbth_pass1(keytype, i, j, n, k, a, ptype, &alts); - BEGIN(FLUSH); + BEGIN(COMMENT); } else { - /* Update each coordinate representation. */ - while ((wcsp = wcsbth_idx(*wcs, &alts, keytype, n, a))) { - wptr = (void *)((char *)wcsp + voff); + // Read the keyvalue. + wcsutil_str2double(yytext, &dbltmp); + + if (chekval && chekval(dbltmp)) { + errmsg = "invalid keyvalue"; + BEGIN(ERROR); + } else { + BEGIN(COMMENT); + } + } + } + +. { + errmsg = "a floating-point value was expected"; + BEGIN(ERROR); + } + +{FLOAT} { + if (ipass == 1) { + BEGIN(COMMENT); + + } else { + // Read the keyvalue as integer and fractional parts. + wcsutil_str2double2(yytext, dbl2tmp); - /* Apply keyword parameterization. */ - if (ptype == 'v') { - ipx = wcsp->npv++; - wcsp->pv[ipx].i = i; - wcsp->pv[ipx].m = m; - wptr = &(wcsp->pv[ipx].value); + BEGIN(COMMENT); + } + } + +. { + errmsg = "a floating-point value was expected"; + BEGIN(ERROR); + } + +{STRING} { + if (ipass == 1) { + BEGIN(COMMENT); - } else if (j) { - /* Is the de-reference necessary? */ - wptr = *((double **)wptr) + (i - 1)*(wcsp->naxis) + (j - 1); + } else { + strtmp[0] = '\0'; + int nch = yyleng - 2; + if (0 < nch && nch < 80) { + // Copy the keyvalue minus the quotes. + strncpy(strtmp, yytext+1, nch); + strtmp[nch] = '\0'; - } else if (i) { - wptr = *((double **)wptr) + (i - 1); + // Strip off trailing blanks. + for (int jx = nch-1; jx >= 0; jx--) { + if (strtmp[jx] != ' ') { + break; + } + strtmp[jx] = '\0'; } + } - /* Read the keyvalue. */ - if (special) { - special(wptr); - } else { - wcsutil_str2double(yytext, "%lf", (double *)wptr); + // Squeeze out repeated quotes. + int ix = 0; + for (int jx = 0; jx < 72; jx++) { + if (ix < jx) { + strtmp[ix] = strtmp[jx]; } - /* Flag the presence of PC, or CD and/or CROTA. */ - if (altlin) { - wcsp->altlin |= altlin; - altlin = 0; + if (strtmp[jx] == '\0') { + break; + } else if (strtmp[jx] == '\'' && strtmp[jx+1] == '\'') { + jx++; } + + ix++; } BEGIN(COMMENT); } } -. { - errmsg = "A floating-point value was expected"; +. { + errmsg = "a string value was expected"; BEGIN(ERROR); } -{STRING} { +{INLINE}$ { if (ipass == 1) { - /* Do first-pass bookkeeping. */ + // Do first-pass bookkeeping. wcsbth_pass1(keytype, i, j, n, k, a, ptype, &alts); BEGIN(FLUSH); - } else { - /* Update each coordinate representation. */ - while ((wcsp = wcsbth_idx(*wcs, &alts, keytype, n, a))) { - wptr = (void *)((char *)wcsp + voff); - - /* Apply keyword parameterization. */ - if (ptype == 's') { - ipx = wcsp->nps++; - wcsp->ps[ipx].i = i; - wcsp->ps[ipx].m = m; - wptr = wcsp->ps[ipx].value; + } else if (*wcs) { + // Store the value now that the keyrecord has been validated. + alts.icol = 0; + alts.ialt = 0; - } else if (j) { - wptr = *((char (**)[72])wptr) + - (i - 1)*(wcsp->naxis) + (j - 1); + // Update each coordinate representation. + int gotone = 0; + struct wcsprm *wcsp; + while ((wcsp = wcsbth_idx(*wcs, &alts, keytype, n, a))) { + gotone = 1; + + if (vptr) { + void *wptr; + if (auxprm) { + // Additional auxiliary parameter. + auxp = wcsp->aux; + ptrdiff_t voff = (char *)vptr - (char *)(&auxtem); + wptr = (void *)((char *)auxp + voff); + } else { + // A parameter that lives directly in wcsprm. + ptrdiff_t voff = (char *)vptr - (char *)(&wcstem); + wptr = (void *)((char *)wcsp + voff); + } - } else if (i) { - wptr = *((char (**)[72])wptr) + (i - 1); + if (valtype == INTEGER) { + *((int *)wptr) = inttmp; + + } else if (valtype == FLOAT) { + // Apply keyword parameterization. + if (ptype == 'v') { + int ipx = (wcsp->npv)++; + wcsp->pv[ipx].i = i; + wcsp->pv[ipx].m = m; + wptr = &(wcsp->pv[ipx].value); + + } else if (j) { + wptr = *((double **)wptr) + (i - 1)*(wcsp->naxis) + + (j - 1); + + } else if (i) { + wptr = *((double **)wptr) + (i - 1); + } + + if (special) { + special(wptr, &dbltmp); + } else { + *((double *)wptr) = dbltmp; + } + + // Flag the presence of PCi_ja, or CDi_ja and/or CROTAia. + if (altlin) { + wcsp->altlin |= altlin; + altlin = 0; + } + + } else if (valtype == FLOAT2) { + // Split MJDREF and JDREF into integer and fraction. + if (special) { + special(wptr, dbl2tmp); + } else { + *((double *)wptr) = dbl2tmp[0]; + *((double *)wptr + 1) = dbl2tmp[1]; + } + + } else if (valtype == STRING) { + // Apply keyword parameterization. + if (ptype == 's') { + int ipx = wcsp->nps++; + wcsp->ps[ipx].i = i; + wcsp->ps[ipx].m = m; + wptr = wcsp->ps[ipx].value; + + } else if (j) { + wptr = *((char (**)[72])wptr) + + (i - 1)*(wcsp->naxis) + (j - 1); + + } else if (i) { + wptr = *((char (**)[72])wptr) + (i - 1); + } + + char *cptr = (char *)wptr; + strcpy(cptr, strtmp); + } } + } - /* Read the keyvalue. */ - cptr = (char *)wptr; - strcpy(cptr, yytext+1); - - /* Squeeze out repeated quotes. */ - ix = 0; - for (jx = 0; jx < 72; jx++) { - if (ix < jx) { - cptr[ix] = cptr[jx]; + if (ipass == npass) { + if (gotone) { + nvalid++; + if (ctrl == 4) { + wcsfprintf(stderr, + "%.80s\n Accepted (%d) as a valid WCS keyrecord.\n", + keyrec, nvalid); } - if (cptr[jx] == '\0') { - if (ix) cptr[ix-1] = '\0'; - break; - } else if (cptr[jx] == '\'' && cptr[jx+1] == '\'') { - jx++; - } + BEGIN(FLUSH); - ix++; + } else { + errmsg = "syntactically valid WCS keyrecord has no effect"; + BEGIN(ERROR); } + + } else { + BEGIN(FLUSH); } - BEGIN(COMMENT); + } else { + BEGIN(FLUSH); } } -. { - errmsg = "A string value was expected"; +.*" "*\/.*$ { + errmsg = "invalid keyvalue"; BEGIN(ERROR); } -" "*\/.* | -" "* { - BEGIN(FLUSH); +[^ \/\n]*{INLINE}$ { + errmsg = "invalid keyvalue"; + BEGIN(ERROR); + } + +" "+[^\/\n].*{INLINE}$ { + errmsg = "invalid keyvalue or malformed keycomment"; + BEGIN(ERROR); } -. { - errmsg = "Malformed keycomment"; +.*$ { + errmsg = "malformed keycomment"; BEGIN(ERROR); } -.* { +.*$ { if (ipass == npass) { if (ctrl < 0) { - /* Preserve discards. */ - if (hptr < wcsbth_hdr-80) { - strncpy(hptr, wcsbth_hdr-80, 80); - } - hptr += 80; + // Preserve discards. + keep = keyrec; - } else if (ctrl > 2) { - fprintf(stderr, "%.80s\n Discarded.\n", wcsbth_hdr-80); + } else if (2 < ctrl) { + nother++; + wcsfprintf(stderr, "%.80s\n Not a recognized WCS keyword.\n", + keyrec); } } - BEGIN(FLUSH); } -.* { - (*nreject)++; +.*$ { if (ipass == npass) { - if (ctrl == -1) { - if (hptr < wcsbth_hdr-80) { - /* Preserve rejects. */ - strncpy(hptr, wcsbth_hdr-80, 80); - } - hptr += 80; + (*nreject)++; + + if (ctrl%10 == -1) { + keep = keyrec; } - if (abs(ctrl) > 1) { - fprintf(stderr, "%.80s\n%4d: %s.\n", wcsbth_hdr-80, *nreject, - errmsg); + if (1 < abs(ctrl%10)) { + wcsfprintf(stderr, "%.80s\n Rejected (%d), %s.\n", + keyrec, *nreject, errmsg); } } - BEGIN(FLUSH); } .*\n { - /* Throw away the rest of the line and reset for the next one. */ + if (ipass == npass && keep) { + if (hptr < keep) { + strncpy(hptr, keep, 80); + } + hptr += 80; + } + + naux += auxprm; + auxprm = 0; + + // Throw away the rest of the line and reset for the next one. i = j = 0; n = k = 0; m = 0; a = ' '; + keyrec += 80; + keytype = 0; valtype = -1; vptr = 0x0; + keep = 0x0; - altlin = 0; - ptype = ' '; + altlin = 0; + ptype = ' '; + chekval = 0x0; special = 0x0; + BEGIN(INITIAL); } <> { - /* End-of-input. */ + // End-of-input. if (ipass == 1) { - if ((status = wcsbth_init1(&alts, nwcs, wcs)) || *nwcs == 0) { - yylex_destroy(); + int status; + if ((status = wcsbth_init1(&alts, naux, nwcs, wcs)) || + (*nwcs == 0 && ctrl == 0)) { return status; } - - if (alts.imgherit) npass = 3; - if (abs(ctrl) > 2) { + if (2 < abs(ctrl%10)) { if (*nwcs == 1) { - fprintf(stderr, "Found one coordinate representation.\n"); + if (strcmp(wcs[0]->wcsname, "DEFAULTS") != 0) { + wcsfprintf(stderr, "Found one coordinate representation.\n"); + } } else { - fprintf(stderr, "Found %d coordinate representations.\n", + wcsfprintf(stderr, "Found %d coordinate representations.\n", *nwcs); } } + + if (alts.imgherit) npass = 3; } if (ipass++ < npass) { - wcsbth_hdr = header; - wcsbth_nkeyrec = nkeyrec; + yyextra->hdr = header; + yyextra->nkeyrec = nkeyrec; + keyrec = header; *nreject = 0; + imherit = 1; + i = j = 0; k = n = 0; m = 0; @@ -2056,17 +3142,26 @@ int wcsbth_final(struct wcsbth_alts *alts, int *nwcs, struct wcsprm **wcs); altlin = 0; ptype = ' '; + chekval = 0x0; special = 0x0; - yyrestart(yyin); + yyrestart(yyin, yyscanner); } else { - yylex_destroy(); if (ctrl < 0) { *hptr = '\0'; } else if (ctrl == 1) { - fprintf(stderr, "%d WCS keyrecords were rejected.\n", *nreject); + wcsfprintf(stderr, "%d WCS keyrecord%s rejected.\n", + *nreject, (*nreject==1)?" was":"s were"); + } else if (ctrl == 4) { + wcsfprintf(stderr, "\n"); + wcsfprintf(stderr, "%5d keyrecord%s rejected for syntax or " + "other errors,\n", *nreject, (*nreject==1)?" was":"s were"); + wcsfprintf(stderr, "%5d %s recognized as syntactically valid, " + "and\n", nvalid, (nvalid==1)?"was":"were"); + wcsfprintf(stderr, "%5d other%s were not recognized as WCS " + "keyrecords.\n", nother, (nother==1)?"":"s"); } return wcsbth_final(&alts, nwcs, wcs); @@ -2075,6 +3170,36 @@ int wcsbth_final(struct wcsbth_alts *alts, int *nwcs, struct wcsprm **wcs); %% +/*---------------------------------------------------------------------------- +* External interface to the scanner. +*---------------------------------------------------------------------------*/ + +int wcsbth( + char *header, + int nkeyrec, + int relax, + int ctrl, + int keysel, + int *colsel, + int *nreject, + int *nwcs, + struct wcsprm **wcs) + +{ + // Function prototypes. + int yylex_init_extra(YY_EXTRA_TYPE extra, yyscan_t *yyscanner); + int yylex_destroy(yyscan_t yyscanner); + + struct wcsbth_extra extra; + yyscan_t yyscanner; + yylex_init_extra(&extra, &yyscanner); + int status = wcsbth_scanner(header, nkeyrec, relax, ctrl, keysel, colsel, + nreject, nwcs, wcs, yyscanner); + yylex_destroy(yyscanner); + + return status; +} + /*---------------------------------------------------------------------------- * Perform first-pass tasks: * @@ -2112,23 +3237,21 @@ int wcsbth_pass1( struct wcsbth_alts *alts) { - int ialt, icol, mask, ncol; - if (a == 0) { - /* Keywords such as DATE-OBS go along for the ride. */ + // Keywords such as DATE-OBS go along for the ride. return 0; } - ncol = alts->ncol; + int ncol = alts->ncol; - /* Do we need to allocate memory for alts? */ + // Do we need to allocate memory for alts? if (alts->arridx == 0x0) { if (ncol == 0) { - /* Can only happen if TFIELDS is missing or out-of-sequence. If n and - k are both zero then we may be processing an image header so leave - ncol alone - the array will be realloc'd later if required. */ + // Can only happen if TFIELDS is missing or out-of-sequence. If n and + // k are both zero then we may be processing an image header so leave + // ncol alone - the array will be realloc'd later if required. if (n || k) { - /* The header is mangled, assume the worst. */ + // The header is mangled, assume the worst. ncol = 999; } } @@ -2141,13 +3264,13 @@ int wcsbth_pass1( if (alts->npv) free(alts->npv); if (alts->nps) free(alts->nps); if (alts->pixlist) free(alts->pixlist); - return 2; + return WCSHDRERR_MEMORY; } alts->ncol = ncol; } else if (n > ncol || k > ncol) { - /* Can only happen if TFIELDS or the WCS keyword is wrong; carry on. */ + // Can only happen if TFIELDS or the WCS keyword is wrong; carry on. ncol = 999; if (!(alts->arridx = realloc(alts->arridx, 27*(1 + ncol)*sizeof(short int))) || @@ -2161,12 +3284,12 @@ int wcsbth_pass1( if (alts->npv) free(alts->npv); if (alts->nps) free(alts->nps); if (alts->pixlist) free(alts->pixlist); - return 2; + return WCSHDRERR_MEMORY; } - /* Since realloc() doesn't initialize the extra memory. */ - for (icol = (1 + alts->ncol); icol < (1 + ncol); icol++) { - for (ialt = 0; ialt < 27; ialt++) { + // Since realloc() doesn't initialize the extra memory. + for (int icol = (1 + alts->ncol); icol < (1 + ncol); icol++) { + for (int ialt = 0; ialt < 27; ialt++) { alts->arridx[icol][ialt] = 0; alts->npv[icol][ialt] = 0; alts->nps[icol][ialt] = 0; @@ -2177,23 +3300,23 @@ int wcsbth_pass1( alts->ncol = ncol; } - ialt = 0; + int ialt = 0; if (a != ' ') { ialt = a - 'A' + 1; } - /* A BINTAB keytype such as LONPna, in conjunction with an IMGAXIS keytype - causes a table column to be recognized as an image array. */ + // A BINTAB keytype such as LONPna, in conjunction with an IMGAXIS keytype + // causes a table column to be recognized as an image array. if (keytype & IMGHEAD || keytype & BIMGARR) { - /* n == 0 is expected for IMGHEAD keywords. */ + // n == 0 is expected for IMGHEAD keywords. if (i == 0 && j == 0) { if (alts->arridx[n][ialt] == 0) { - /* Flag that an auxiliary keyword was seen. */ + // Flag that an auxiliary keyword was seen. alts->arridx[n][ialt] = -1; } } else { - /* Record the maximum axis number found. */ + // Record the maximum axis number found. if (alts->arridx[n][ialt] < i) { alts->arridx[n][ialt] = i; } @@ -2210,17 +3333,17 @@ int wcsbth_pass1( } } - /* BINTAB keytypes, which apply both to pixel lists as well as binary table - image arrays, never contribute to recognizing a table column as a pixel - list axis. A PIXLIST keytype is required for that. */ + // BINTAB keytypes, which apply both to pixel lists as well as binary table + // image arrays, never contribute to recognizing a table column as a pixel + // list axis. A PIXLIST keytype is required for that. if (keytype == PIXLIST) { - mask = 1 << ialt; + int mask = 1 << ialt; - /* n > 0 for PIXLIST keytypes. */ + // n > 0 for PIXLIST keytypes. alts->pixlist[n] |= mask; if (k) alts->pixlist[k] |= mask; - /* Used as a flag over all columns. */ + // Used as a flag over all columns. alts->pixlist[0] |= mask; if (ptype == 'v') { @@ -2243,26 +3366,25 @@ int wcsbth_pass1( int wcsbth_init1( struct wcsbth_alts *alts, + int naux, int *nwcs, struct wcsprm **wcs) { - int ialt, icol, inherit[27], ix, mask, ncol, npsmax, npvmax, status = 0; - struct wcsprm *wcsp; - + int status = 0; if (alts->arridx == 0x0) { *nwcs = 0; return 0; } - /* Determine the number of axes in each pixel list representation. */ - ncol = alts->ncol; + // Determine the number of axes in each pixel list representation. + int ialt, mask, ncol = alts->ncol; for (ialt = 0, mask = 1; ialt < 27; ialt++, mask <<= 1) { alts->pixidx[ialt] = 0; if (alts->pixlist[0] | mask) { - for (icol = 1; icol <= ncol; icol++) { + for (int icol = 1; icol <= ncol; icol++) { if (alts->pixlist[icol] & mask) { alts->pixidx[ialt]++; } @@ -2270,18 +3392,19 @@ int wcsbth_init1( } } - /* Find the total number of coordinate representations. */ + // Find the total number of coordinate representations. *nwcs = 0; alts->imgherit = 0; - for (ialt = 0; ialt < 27; ialt++) { + int inherit[27]; + for (int ialt = 0; ialt < 27; ialt++) { inherit[ialt] = 0; - for (icol = 1; icol <= ncol; icol++) { + for (int icol = 1; icol <= ncol; icol++) { if (alts->arridx[icol][ialt] < 0) { - /* No BIMGARR keytype but there's at least one BINTAB. */ + // No BIMGARR keytype but there's at least one BINTAB. if (alts->arridx[0][ialt] > 0) { - /* There is an IMGAXIS keytype that we will inherit, so count this - representation. */ + // There is an IMGAXIS keytype that we will inherit, so count this + // representation. alts->arridx[icol][ialt] = alts->arridx[0][ialt]; } else { alts->arridx[icol][ialt] = 0; @@ -2290,11 +3413,11 @@ int wcsbth_init1( if (alts->arridx[icol][ialt]) { if (alts->arridx[0][ialt]) { - /* All IMGHEAD keywords are inherited for this ialt. */ + // All IMGHEAD keywords are inherited for this ialt. inherit[ialt] = 1; if (alts->arridx[icol][ialt] < alts->arridx[0][ialt]) { - /* The extra axes are also inherited. */ + // The extra axes are also inherited. alts->arridx[icol][ialt] = alts->arridx[0][ialt]; } } @@ -2303,18 +3426,18 @@ int wcsbth_init1( } } - /* Count every "a" found in any IMGHEAD keyword... */ + // Count every "a" found in any IMGHEAD keyword... if (alts->arridx[0][ialt]) { if (inherit[ialt]) { - /* ...but not if the IMGHEAD keywords will be inherited. */ + // ...but not if the IMGHEAD keywords will be inherited. alts->arridx[0][ialt] = 0; alts->imgherit = 1; - } else { + } else if (alts->arridx[0][ialt] > 0) { (*nwcs)++; } } - /* We need a struct for every "a" found in a PIXLIST keyword. */ + // We need a struct for every "a" found in a PIXLIST keyword. if (alts->pixidx[ialt]) { (*nwcs)++; } @@ -2322,90 +3445,98 @@ int wcsbth_init1( if (*nwcs) { - /* Allocate memory for the required number of wcsprm structs. */ + // Allocate memory for the required number of wcsprm structs. if (!(*wcs = calloc(*nwcs, sizeof(struct wcsprm)))) { - return 2; + return WCSHDRERR_MEMORY; } - /* Record the current values of NPVMAX and NPSMAX. */ - npvmax = wcsnpv(-1); - npsmax = wcsnps(-1); - - /* Initialize each wcsprm struct. */ - wcsp = *wcs; + // Initialize each wcsprm struct. + struct wcsprm *wcsp = *wcs; *nwcs = 0; - for (icol = 0; icol <= ncol; icol++) { - for (ialt = 0; ialt < 27; ialt++) { - if (alts->arridx[icol][ialt]) { - /* Image-header representations that are not for inheritance - (icol == 0) or binary table image array representations. */ + for (int icol = 0; icol <= ncol; icol++) { + for (int ialt = 0; ialt < 27; ialt++) { + if (alts->arridx[icol][ialt] > 0) { + // Image-header representations that are not for inheritance + // (icol == 0) or binary table image array representations. wcsp->flag = -1; - wcsnpv(alts->npv[icol][ialt]); - wcsnps(alts->nps[icol][ialt]); - if ((status = wcsini(1, (int)(alts->arridx[icol][ialt]), wcsp))) { + int npvmax = alts->npv[icol][ialt]; + int npsmax = alts->nps[icol][ialt]; + if ((status = wcsinit(1, (int)(alts->arridx[icol][ialt]), wcsp, + npvmax, npsmax, -1))) { wcsvfree(nwcs, wcs); break; } - /* Record the alternate version code. */ + // Record the alternate version code. if (ialt) { wcsp->alt[0] = 'A' + ialt - 1; } - /* Record the table column number. */ + // Any additional auxiliary keywords present? + if (naux) { + if (wcsauxi(1, wcsp)) { + return WCSHDRERR_MEMORY; + } + } + + // Record the table column number. wcsp->colnum = icol; - /* On the second pass alts->arridx[icol][27] indexes the array of - wcsprm structs. */ + // On the second pass alts->arridx[icol][27] indexes the array of + // wcsprm structs. alts->arridx[icol][ialt] = (*nwcs)++; wcsp++; } else { - /* Signal that this column has no WCS for this "a". */ + // Signal that this column has no WCS for this "a". alts->arridx[icol][ialt] = -1; } } } - for (ialt = 0; ialt < 27; ialt++) { + for (int ialt = 0; ialt < 27; ialt++) { if (alts->pixidx[ialt]) { - /* Pixel lists representations. */ + // Pixel lists representations. wcsp->flag = -1; - wcsnpv(alts->pixnpv[ialt]); - wcsnps(alts->pixnps[ialt]); - if ((status = wcsini(1, (int)(alts->pixidx[ialt]), wcsp))) { + int npvmax = alts->pixnpv[ialt]; + int npsmax = alts->pixnps[ialt]; + if ((status = wcsinit(1, (int)(alts->pixidx[ialt]), wcsp, npvmax, + npsmax, -1))) { wcsvfree(nwcs, wcs); break; } - /* Record the alternate version code. */ + // Record the alternate version code. if (ialt) { wcsp->alt[0] = 'A' + ialt - 1; } - /* Record the pixel list column numbers. */ - mask = (1 << ialt); + // Any additional auxiliary keywords present? + if (naux) { + if (wcsauxi(1, wcsp)) { + return WCSHDRERR_MEMORY; + } + } + + // Record the pixel list column numbers. + int icol, ix, mask = (1 << ialt); for (icol = 1, ix = 0; icol <= ncol; icol++) { if (alts->pixlist[icol] & mask) { wcsp->colax[ix++] = icol; } } - /* alts->pixidx[] indexes the array of wcsprm structs. */ + // alts->pixidx[] indexes the array of wcsprm structs. alts->pixidx[ialt] = (*nwcs)++; wcsp++; } else { - /* Signal that this column is not a pixel list axis for this "a". */ + // Signal that this column is not a pixel list axis for this "a". alts->pixidx[ialt] = -1; } } - - /* Restore the original values of NPVMAX and NPSMAX. */ - wcsnpv(npvmax); - wcsnps(npsmax); } return status; @@ -2426,28 +3557,27 @@ struct wcsprm *wcsbth_idx( { const char as[] = " ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - int iwcs; if (!wcs) return 0x0; - iwcs = -1; + int iwcs = -1; for (; iwcs < 0 && alts->ialt < 27; alts->ialt++) { - /* Note that a == 0 applies to every alternate, otherwise this - loop simply determines the appropriate value of alts->ialt. */ + // Note that a == 0 applies to every alternate, otherwise this + // loop simply determines the appropriate value of alts->ialt. if (a && a != as[alts->ialt]) continue; if (keytype & (IMGHEAD | BIMGARR)) { for (; iwcs < 0 && alts->icol <= alts->ncol; alts->icol++) { - /* Image header keywords, n == 0, apply to all columns, otherwise this - loop simply determines the appropriate value of alts->icol. */ + // Image header keywords, n == 0, apply to all columns, otherwise this + // loop simply determines the appropriate value of alts->icol. if (n && n != alts->icol) continue; iwcs = alts->arridx[alts->icol][alts->ialt]; } - /* Break out of the loop to stop alts->ialt from being incremented. */ + // Break out of the loop to stop alts->ialt from being incremented. if (iwcs >= 0) break; - /* Start from scratch for the next alts->ialt. */ + // Start from scratch for the next alts->ialt. alts->icol = 0; } @@ -2472,17 +3602,14 @@ int wcsbth_colax( char a) { - int ix; - struct wcsprm *wcsp; - if (!wcs) return 0; - wcsp = wcs; + struct wcsprm *wcsp = wcs; if (a != ' ') { wcsp += alts->pixidx[a-'A'+1]; } - for (ix = 0; ix < wcsp->naxis; ix++) { + for (int ix = 0; ix < wcsp->naxis; ix++) { if (wcsp->colax[ix] == n) { return ++ix; } @@ -2493,18 +3620,60 @@ int wcsbth_colax( /*---------------------------------------------------------------------------- -* Interpret EPOCH keywords. +* Interpret the JDREF, JDREFI, and JDREFF keywords. *---------------------------------------------------------------------------*/ -int wcsbth_epoch(void *wptr) +int wcsbth_jdref(double *mjdref, const double *jdref) + +{ + // Set MJDREF from JDREF. + if (undefined(mjdref[0] && undefined(mjdref[1]))) { + mjdref[0] = jdref[0] - 2400000.0; + mjdref[1] = jdref[1] - 0.5; + + if (mjdref[1] < 0.0) { + mjdref[0] -= 1.0; + mjdref[1] += 1.0; + } + } + + return 0; +} + +int wcsbth_jdrefi(double *mjdref, const double *jdrefi) + +{ + // Set the integer part of MJDREF from JDREFI. + if (undefined(mjdref[0])) { + mjdref[0] = *jdrefi - 2400000.5; + } + + return 0; +} + + +int wcsbth_jdreff(double *mjdref, const double *jdreff) { - double *equinox; + // Set the fractional part of MJDREF from JDREFF. + if (undefined(mjdref[1])) { + mjdref[1] = *jdreff; + } + + return 0; +} - /* If EQUINOXa is currently undefined then set it from EPOCHa. */ - equinox = (double *)wptr; + +/*---------------------------------------------------------------------------- +* Interpret EPOCHa keywords. +*---------------------------------------------------------------------------*/ + +int wcsbth_epoch(double *equinox, const double *epoch) + +{ + // If EQUINOXa is currently undefined then set it from EPOCHa. if (undefined(*equinox)) { - wcsutil_str2double(yytext, "%lf", equinox); + *equinox = *epoch; } return 0; @@ -2512,21 +3681,18 @@ int wcsbth_epoch(void *wptr) /*---------------------------------------------------------------------------- -* Interpret VSOURCE keywords. +* Interpret VSOURCEa keywords. *---------------------------------------------------------------------------*/ -int wcsbth_vsource(void *wptr) +int wcsbth_vsource(double *zsource, const double *vsource) { - double beta, c = 299792458.0, vsource, *zsource; + const double c = 299792458.0; - /* If ZSOURCEa is currently undefined then set it from VSOURCEa. */ - zsource = (double *)wptr; + // If ZSOURCEa is currently undefined then set it from VSOURCEa. if (undefined(*zsource)) { - wcsutil_str2double(yytext, "%lf", &vsource); - - /* Convert relativistic Doppler velocity to redshift. */ - beta = vsource/c; + // Convert relativistic Doppler velocity to redshift. + double beta = *vsource/c; *zsource = (1.0 + beta)/sqrt(1.0 - beta*beta) - 1.0; } @@ -2534,6 +3700,17 @@ int wcsbth_vsource(void *wptr) } +/*---------------------------------------------------------------------------- +* Check validity of a TIMEPIXR keyvalue. +*---------------------------------------------------------------------------*/ + +int wcsbth_timepixr(double timepixr) + +{ + return (timepixr < 0.0 || 1.0 < timepixr); +} + + /*---------------------------------------------------------------------------- * Tie up loose ends. *---------------------------------------------------------------------------*/ @@ -2544,15 +3721,14 @@ int wcsbth_final( struct wcsprm **wcs) { - int ialt, status; - if (alts->arridx) free(alts->arridx); if (alts->npv) free(alts->npv); if (alts->nps) free(alts->nps); if (alts->pixlist) free(alts->pixlist); - for (ialt = 0; ialt < *nwcs; ialt++) { - /* Interpret -TAB header keywords. */ + for (int ialt = 0; ialt < *nwcs; ialt++) { + // Interpret -TAB header keywords. + int status; if ((status = wcstab(*wcs+ialt))) { wcsvfree(nwcs, wcs); return status; diff --git a/cextern/wcslib/C/wcserr.c b/cextern/wcslib/C/wcserr.c index 834c41a35138..11c6471cfd19 100644 --- a/cextern/wcslib/C/wcserr.c +++ b/cextern/wcslib/C/wcserr.c @@ -1,7 +1,6 @@ /*============================================================================ - - WCSLIB 4.14 - an implementation of the FITS WCS standard. - Copyright (C) 1995-2012, Mark Calabretta + WCSLIB 8.6 - an implementation of the FITS WCS standard. + Copyright (C) 1995-2026, Mark Calabretta This file is part of WCSLIB. @@ -18,12 +17,10 @@ You should have received a copy of the GNU Lesser General Public License along with WCSLIB. If not, see http://www.gnu.org/licenses. - Direct correspondence concerning WCSLIB to mark@calabretta.id.au - Author: Mark Calabretta, Australia Telescope National Facility, CSIRO. Module author: Michael Droettboom - http://www.atnf.csiro.au/people/Mark.Calabretta - $Id: wcserr.c,v 4.14 2012/07/13 10:02:27 cal103 Exp $ + http://www.atnf.csiro.au/computing/software/wcs + $Id: wcserr.c,v 8.6 2026/03/29 13:53:56 mcalabre Exp $ *===========================================================================*/ #include @@ -36,7 +33,7 @@ static int wcserr_enabled = 0; -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- int wcserr_enable(int enable) @@ -44,11 +41,32 @@ int wcserr_enable(int enable) return wcserr_enabled = (enable ? 1 : 0); } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- + +int wcserr_size(const struct wcserr *err, int sizes[2]) + +{ + if (err == 0x0) { + sizes[0] = sizes[1] = 0; + return 0; + } + + // Base size, in bytes. + sizes[0] = sizeof(struct wcserr); + + // Total size of allocated memory, in bytes. + sizes[1] = 0; + + if (err->msg) { + sizes[1] += strlen(err->msg) + 1; + } + + return 0; +} + +//---------------------------------------------------------------------------- -int wcserr_prt( - const struct wcserr *err, - const char *prefix) +int wcserr_prt(const struct wcserr *err, const char *prefix) { if (!wcserr_enabled) { @@ -68,7 +86,7 @@ int wcserr_prt( prefix, err->status, err->function, err->line_no, err->file, prefix, err->msg); } else { - /* An informative message only. */ + // An informative message only. wcsprintf("%sINFORMATIVE message from %s() at line %d of file " "%s:\n%s%s.\n", prefix, err->function, err->line_no, err->file, prefix, err->msg); @@ -78,19 +96,23 @@ int wcserr_prt( return 0; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- -int wcserr_clear( - struct wcserr **errp) +int wcserr_clear(struct wcserr **errp) { - if (*errp) free(*errp); - *errp = 0x0; + if (*errp) { + if ((*errp)->msg) { + free((*errp)->msg); + } + free(*errp); + *errp = 0x0; + } return 0; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- int wcserr_set( struct wcserr **errp, @@ -102,7 +124,7 @@ int wcserr_set( ...) { - char fmt[128]; + int msglen; struct wcserr *err; va_list argp; @@ -118,33 +140,46 @@ int wcserr_set( *errp = err = calloc(1, sizeof(struct wcserr)); } + if (err == 0x0) { + return status; + } + err->status = status; err->function = function; err->file = file; err->line_no = line_no; + err->msg = 0x0; - /* Workaround for a compiler segv from gcc 4.2.1 in MacOSX 10.7. */ - strncpy(fmt, format, 128); + // Determine the required message buffer size. + va_start(argp, format); + msglen = vsnprintf(0x0, 0, format, argp) + 1; + va_end(argp); + if (msglen <= 0 || (err->msg = malloc(msglen)) == 0x0) { + wcserr_clear(errp); + return status; + } + + // Write the message. va_start(argp, format); - vsnprintf(err->msg, WCSERR_MSG_LENGTH, fmt, argp); + msglen = vsnprintf(err->msg, msglen, format, argp); va_end(argp); - } else if (err) { - free(err); - *errp = 0x0; + if (msglen < 0) { + wcserr_clear(errp); + } } return status; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- -int wcserr_copy( - const struct wcserr *src, - struct wcserr *dst) +int wcserr_copy(const struct wcserr *src, struct wcserr *dst) { + size_t msglen; + if (src == 0x0) { if (dst) { memset(dst, 0, sizeof(struct wcserr)); @@ -154,6 +189,13 @@ int wcserr_copy( if (dst) { memcpy(dst, src, sizeof(struct wcserr)); + + if (src->msg) { + msglen = strlen(src->msg) + 1; + if ((dst->msg = malloc(msglen))) { + strcpy(dst->msg, src->msg); + } + } } return src->status; diff --git a/cextern/wcslib/C/wcserr.h b/cextern/wcslib/C/wcserr.h index 1900f62d346e..5d47c18b9d72 100644 --- a/cextern/wcslib/C/wcserr.h +++ b/cextern/wcslib/C/wcserr.h @@ -1,7 +1,6 @@ /*============================================================================ - - WCSLIB 4.14 - an implementation of the FITS WCS standard. - Copyright (C) 1995-2012, Mark Calabretta + WCSLIB 8.6 - an implementation of the FITS WCS standard. + Copyright (C) 1995-2026, Mark Calabretta This file is part of WCSLIB. @@ -18,19 +17,21 @@ You should have received a copy of the GNU Lesser General Public License along with WCSLIB. If not, see http://www.gnu.org/licenses. - Direct correspondence concerning WCSLIB to mark@calabretta.id.au - Author: Mark Calabretta, Australia Telescope National Facility, CSIRO. Module author: Michael Droettboom - http://www.atnf.csiro.au/people/Mark.Calabretta - $Id: wcserr.h,v 4.14 2012/07/13 10:02:27 cal103 Exp $ + http://www.atnf.csiro.au/computing/software/wcs + $Id: wcserr.h,v 8.6 2026/03/29 13:53:56 mcalabre Exp $ *============================================================================= * +* WCSLIB 8.6 - C routines that implement the FITS World Coordinate System +* (WCS) standard. Refer to the README file provided with WCSLIB for an +* overview of the library. +* * Summary of the wcserr routines * ------------------------------ * Most of the structs in WCSLIB contain a pointer to a wcserr struct as a * member. Functions in WCSLIB that return an error status code can also -* allocate and set a detailed error message in this struct which also +* allocate and set a detailed error message in this struct, which also * identifies the function, source file, and line number where the error * occurred. * @@ -72,7 +73,7 @@ * Name of the source file where the error occurred as given by the * __FILE__ preprocessor macro. * -* char msg[WCSERR_MSG_LENGTH] +* char *msg * Informative error message. * * @@ -93,6 +94,29 @@ * 1: Error messaging is enabled. * * +* wcserr_size() - Compute the size of a wcserr struct +* --------------------------------------------------- +* wcserr_size() computes the full size of a wcserr struct, including allocated +* memory. +* +* Given: +* err const struct wcserr* +* The error object. +* +* If NULL, the base size of the struct and the allocated +* size are both set to zero. +* +* Returned: +* sizes int[2] The first element is the base size of the struct as +* returned by sizeof(struct wcserr). The second element +* is the total allocated size of the message buffer, in +* bytes. +* +* Function return value: +* int Status return value: +* 0: Success. +* +* * wcserr_prt() - Print a wcserr struct * ------------------------------------ * wcserr_prt() prints the error message (if any) contained in a wcserr struct. @@ -114,7 +138,7 @@ * * wcserr_clear() - Clear a wcserr struct * -------------------------------------- -* wcserr_clear() clears the error (if any) contained in a wcserr struct. +* wcserr_clear() clears (deletes) a wcserr struct. * * Given and returned: * err struct wcserr** @@ -212,34 +236,42 @@ #ifndef WCSLIB_WCSERR #define WCSLIB_WCSERR -#define WCSERR_MSG_LENGTH 160 +#ifdef __cplusplus +extern "C" { +#endif struct wcserr { - int status; /* Status code for the error. */ - int line_no; /* Line number where the error occurred. */ - const char *function; /* Function name. */ - const char *file; /* Source file name. */ - char msg[WCSERR_MSG_LENGTH]; /* Informative error message. */ + int status; // Status code for the error. + int line_no; // Line number where the error occurred. + const char *function; // Function name. + const char *file; // Source file name. + char *msg; // Informative error message. }; -/* Size of the wcserr struct in int units, used by the Fortran wrappers. */ +// Size of the wcserr struct in int units, used by the Fortran wrappers. #define ERRLEN (sizeof(struct wcserr)/sizeof(int)) int wcserr_enable(int enable); +int wcserr_size(const struct wcserr *err, int sizes[2]); + int wcserr_prt(const struct wcserr *err, const char *prefix); int wcserr_clear(struct wcserr **err); -/* INTERNAL USE ONLY -------------------------------------------------------*/ +// INTERNAL USE ONLY ------------------------------------------------------- int wcserr_set(struct wcserr **err, int status, const char *function, const char *file, int line_no, const char *format, ...); int wcserr_copy(const struct wcserr *src, struct wcserr *dst); -/* Convenience macro for invoking wcserr_set(). */ +// Convenience macro for invoking wcserr_set(). #define WCSERR_SET(status) err, status, function, __FILE__, __LINE__ -#endif /* WSCLIB_WCSERR */ +#ifdef __cplusplus +} +#endif + +#endif // WSCLIB_WCSERR diff --git a/cextern/wcslib/C/wcsfix.c b/cextern/wcslib/C/wcsfix.c index 3384b02b236b..52a03ce361a6 100644 --- a/cextern/wcslib/C/wcsfix.c +++ b/cextern/wcslib/C/wcsfix.c @@ -1,7 +1,6 @@ /*============================================================================ - - WCSLIB 4.14 - an implementation of the FITS WCS standard. - Copyright (C) 1995-2012, Mark Calabretta + WCSLIB 8.6 - an implementation of the FITS WCS standard. + Copyright (C) 1995-2026, Mark Calabretta This file is part of WCSLIB. @@ -18,11 +17,9 @@ You should have received a copy of the GNU Lesser General Public License along with WCSLIB. If not, see http://www.gnu.org/licenses. - Direct correspondence concerning WCSLIB to mark@calabretta.id.au - Author: Mark Calabretta, Australia Telescope National Facility, CSIRO. - http://www.atnf.csiro.au/people/Mark.Calabretta - $Id: wcsfix.c,v 4.14 2012/07/13 10:02:27 cal103 Exp $ + http://www.atnf.csiro.au/computing/software/wcs + $Id: wcsfix.c,v 8.6 2026/03/29 13:53:56 mcalabre Exp $ *===========================================================================*/ #include @@ -30,20 +27,22 @@ #include #include -#include "wcserr.h" -#include "wcsmath.h" -#include "wcsutil.h" +#include "lin.h" #include "sph.h" +#include "tab.h" #include "wcs.h" -#include "wcsunits.h" +#include "wcserr.h" #include "wcsfix.h" +#include "wcsmath.h" +#include "wcstrig.h" +#include "wcsunits.h" +#include "wcsutil.h" +#include "wtbarr.h" -extern const int WCSSET; - -/* Maximum number of coordinate axes that can be handled. */ +// Maximum number of coordinate axes that can be handled. #define NMAX 16 -/* Map status return value to message. */ +// Map status return value to message. const char *wcsfix_errmsg[] = { "Success", "Null wcsprm pointer passed", @@ -57,10 +56,38 @@ const char *wcsfix_errmsg[] = { "Could not determine reference pixel coordinate", "Could not determine reference pixel value"}; -/* Convenience macro for invoking wcserr_set(). */ +// Map error returns for lower-level routines. +const int fix_linerr[] = { + FIXERR_SUCCESS, // 0: LINERR_SUCCESS + FIXERR_NULL_POINTER, // 1: LINERR_NULL_POINTER + FIXERR_MEMORY, // 2: LINERR_MEMORY + FIXERR_SINGULAR_MTX, // 3: LINERR_SINGULAR_MTX + FIXERR_BAD_PARAM, // 4: LINERR_DISTORT_INIT + FIXERR_NO_REF_PIX_COORD, // 5: LINERR_DISTORT + FIXERR_NO_REF_PIX_VAL // 6: LINERR_DEDISTORT +}; + +const int fix_wcserr[] = { + FIXERR_SUCCESS, // 0: WCSERR_SUCCESS + FIXERR_NULL_POINTER, // 1: WCSERR_NULL_POINTER + FIXERR_MEMORY, // 2: WCSERR_MEMORY + FIXERR_SINGULAR_MTX, // 3: WCSERR_SINGULAR_MTX + FIXERR_BAD_CTYPE, // 4: WCSERR_BAD_CTYPE + FIXERR_BAD_PARAM, // 5: WCSERR_BAD_PARAM + FIXERR_BAD_COORD_TRANS, // 6: WCSERR_BAD_COORD_TRANS + FIXERR_ILL_COORD_TRANS, // 7: WCSERR_ILL_COORD_TRANS + FIXERR_BAD_CORNER_PIX, // 8: WCSERR_BAD_PIX + FIXERR_NO_REF_PIX_VAL, // 9: WCSERR_BAD_WORLD + FIXERR_NO_REF_PIX_VAL // 10: WCSERR_BAD_WORLD_COORD + // ...others not used +}; + +static const int WCSSET = 137; // Matching wcs.c + +// Convenience macro for invoking wcserr_set(). #define WCSFIX_ERRMSG(status) WCSERR_SET(status), wcsfix_errmsg[status] -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- int wcsfix(int ctrl, const int naxis[], struct wcsprm *wcs, int stat[]) @@ -75,6 +102,10 @@ int wcsfix(int ctrl, const int naxis[], struct wcsprm *wcs, int stat[]) status = 1; } + if ((stat[OBSFIX] = obsfix(0, wcs)) > 0) { + status = 1; + } + if ((stat[UNITFIX] = unitfix(ctrl, wcs)) > 0) { status = 1; } @@ -94,29 +125,33 @@ int wcsfix(int ctrl, const int naxis[], struct wcsprm *wcs, int stat[]) return status; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- -int wcsfixi(int ctrl, const int naxis[], struct wcsprm *wcs, int stat[], - struct wcserr info[]) +int wcsfixi( + int ctrl, + const int naxis[], + struct wcsprm *wcs, + int stat[], + struct wcserr info[]) { - int ifix, status = 0; - struct wcserr err; + int status = 0; - /* Handling the status values returned from the sub-fixers is trickier than - it might seem, especially considering that wcs->err may contain an error - status on input which should be preserved if no translation errors occur. - The simplest way seems to be to save a copy of wcs->err and clear it before - each sub-fixer. The last real error to occur, excluding informative - messages, is the one returned. + // Handling the status values returned from the sub-fixers is trickier than + // it might seem, especially considering that wcs->err may contain an error + // status on input which should be preserved if no translation errors occur. + // The simplest way seems to be to save a copy of wcs->err and clear it + // before each sub-fixer. The last real error to occur, excluding + // informative messages, is the one returned. - To get informative messages from spcfix() it must precede celfix() and - cylfix(). The latter call wcsset() which also translates AIPS-convention - spectral axes. */ + // To get informative messages from spcfix() it must precede celfix() and + // cylfix(). The latter call wcsset() which also translates AIPS-convention + // spectral axes. + struct wcserr err; wcserr_copy(wcs->err, &err); - for (ifix = CDFIX; ifix < NWCSFIX; ifix++) { - /* Clear (delete) wcs->err. */ + for (int ifix = CDFIX; ifix < NWCSFIX; ifix++) { + // Clear (delete) wcs->err. wcserr_clear(&(wcs->err)); switch (ifix) { @@ -126,6 +161,9 @@ int wcsfixi(int ctrl, const int naxis[], struct wcsprm *wcs, int stat[], case DATFIX: stat[ifix] = datfix(wcs); break; + case OBSFIX: + stat[ifix] = obsfix(0, wcs); + break; case UNITFIX: stat[ifix] = unitfix(ctrl, wcs); break; @@ -143,11 +181,11 @@ int wcsfixi(int ctrl, const int naxis[], struct wcsprm *wcs, int stat[], } if (stat[ifix] == FIXERR_NO_CHANGE) { - /* No change => no message. */ + // No change => no message. wcserr_copy(0x0, info+ifix); - } else if (stat[ifix] == FIXERR_SUCCESS) { - /* Successful translation, but there may be an informative message. */ + } else if (stat[ifix] == 0) { + // Successful translation, but there may be an informative message. if (wcs->err && wcs->err->status < 0) { wcserr_copy(wcs->err, info+ifix); } else { @@ -155,17 +193,17 @@ int wcsfixi(int ctrl, const int naxis[], struct wcsprm *wcs, int stat[], } } else { - /* An informative message or error message. */ + // An informative message or error message. wcserr_copy(wcs->err, info+ifix); if ((status = (stat[ifix] > 0))) { - /* It was an error, replace the previous one. */ + // It was an error, replace the previous one. wcserr_copy(wcs->err, &err); } } } - /* Restore the last error to occur. */ + // Restore the last error to occur. if (err.status) { wcserr_copy(&err, wcs->err); } else { @@ -175,33 +213,30 @@ int wcsfixi(int ctrl, const int naxis[], struct wcsprm *wcs, int stat[], return status; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- int cdfix(struct wcsprm *wcs) { - int i, k, naxis, status = FIXERR_NO_CHANGE; - double *cd; - if (wcs == 0x0) return FIXERR_NULL_POINTER; if ((wcs->altlin & 1) || !(wcs->altlin & 2)) { - /* Either we have PCi_ja or there are no CDi_ja. */ + // Either we have PCi_ja or there are no CDi_ja. return FIXERR_NO_CHANGE; } - naxis = wcs->naxis; - status = FIXERR_NO_CHANGE; - for (i = 0; i < naxis; i++) { - /* Row of zeros? */ - cd = wcs->cd + i * naxis; - for (k = 0; k < naxis; k++, cd++) { + int naxis = wcs->naxis; + int status = FIXERR_NO_CHANGE; + for (int i = 0; i < naxis; i++) { + // Row of zeros? + double *cd = wcs->cd + i*naxis; + for (int k = 0; k < naxis; k++, cd++) { if (*cd != 0.0) goto next; } - /* Column of zeros? */ + // Column of zeros? cd = wcs->cd + i; - for (k = 0; k < naxis; k++, cd += naxis) { + for (int k = 0; k < naxis; k++, cd += naxis) { if (*cd != 0.0) goto next; } @@ -215,226 +250,593 @@ next: ; return status; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- static int parse_date(const char *buf, int *hour, int *minute, double *sec) { char ctmp[72]; - if (sscanf(buf, "%2d:%2d:%s", hour, minute, ctmp) < 3 || - wcsutil_str2double(ctmp, "%lf", sec)) { + wcsutil_str2double(ctmp, sec)) { return 1; } return 0; } + static void write_date(char *buf, int hour, int minute, double sec) { - char ctmp[72]; - + char ctmp[32]; wcsutil_double2str(ctmp, "%04.1f", sec); sprintf(buf, "T%.2d:%.2d:%s", hour, minute, ctmp); } + +static char *newline(char **cp) + +{ + size_t k; + if ((k = strlen(*cp))) { + *cp += k; + strcat(*cp, ".\n"); + *cp += 2; + } + + return *cp; +} + + int datfix(struct wcsprm *wcs) { static const char *function = "datfix"; - char orig_dateobs[72]; - char ctmp[72], ctmp2[72]; - char *dateobs; - int day, dd, hour = 0, jd, minute = 0, month, msec, n4, year; - double mjdobs, sec = 0.0, t; - struct wcserr **err; + // MJD of J2000.0 and B1900.0. + const double mjd2000 = 51544.5; + const double mjd1900 = 15019.81352; + + // Days per Julian year and per tropical year. + const double djy = 365.25; + const double dty = 365.242198781; + + int day, hour = 0, minute = 0, month, year; + double sec = 0.0; if (wcs == 0x0) return FIXERR_NULL_POINTER; - err = &(wcs->err); + struct wcserr **err = &(wcs->err); + + char infomsg[512]; + char *cp = infomsg; + *cp = '\0'; + + int status = FIXERR_NO_CHANGE; + + for (int i = 0; i < 5; i++) { + // MJDREF is split into integer and fractional parts, wheres MJDOBS and + // the rest are a single value. + const char *dateid = 0x0; + char *date = 0x0; + double *wcsmjd = 0x0; + if (i == 0) { + // Note, DATEREF and MJDREF, not DATE-REF and MJD-REF (sigh). + dateid = "REF"; + date = wcs->dateref; + wcsmjd = wcs->mjdref; + } else if (i == 1) { + dateid = "-OBS"; + date = wcs->dateobs; + wcsmjd = &(wcs->mjdobs); + } else if (i == 2) { + dateid = "-BEG"; + date = wcs->datebeg; + wcsmjd = &(wcs->mjdbeg); + } else if (i == 3) { + dateid = "-AVG"; + date = wcs->dateavg; + wcsmjd = &(wcs->mjdavg); + } else if (i == 4) { + dateid = "-END"; + date = wcs->dateend; + wcsmjd = &(wcs->mjdend); + } - dateobs = wcs->dateobs; - strncpy(orig_dateobs, dateobs, 72); - if (dateobs[0] == '\0') { - if (undefined(wcs->mjdobs)) { - /* No date information was provided. */ - return FIXERR_NO_CHANGE; + char orig_date[72]; + strncpy(orig_date, date, 72); - } else { - /* Calendar date from MJD. */ - jd = 2400001 + (int)wcs->mjdobs; - - n4 = 4*(jd + ((2*((4*jd - 17918)/146097)*3)/4 + 1)/2 - 37); - dd = 10*(((n4-237)%1461)/4) + 5; - - year = n4/1461 - 4712; - month = (2 + dd/306)%12 + 1; - day = (dd%306)/10 + 1; - sprintf(dateobs, "%.4d-%.2d-%.2d", year, month, day); - - /* Write time part only if non-zero. */ - if ((t = wcs->mjdobs - (int)wcs->mjdobs) > 0.0) { - t *= 24.0; - hour = (int)t; - t = 60.0 * (t - hour); - minute = (int)t; - sec = 60.0 * (t - minute); - - /* Round to 1ms. */ - dd = 60000*(60*hour + minute) + (int)(1000*(sec+0.0005)); - hour = dd / 3600000; - dd -= 3600000 * hour; - minute = dd / 60000; - msec = dd - 60000 * minute; - sprintf(dateobs+10, "T%.2d:%.2d:%.2d", hour, minute, msec/1000); - - /* Write fractions of a second only if non-zero. */ - if (msec%1000) { - sprintf(dateobs+19, ".%.3d", msec%1000); + if (date[0] == '\0') { + // Fill in DATE from MJD if possible. + + if (i == 1 && undefined(*wcsmjd)) { + // See if we have jepoch or bepoch. + if (!undefined(wcs->jepoch)) { + *wcsmjd = mjd2000 + (wcs->jepoch - 2000.0)*djy; + sprintf(newline(&cp), "Set MJD-OBS to %.6f from JEPOCH", *wcsmjd); + if (status == FIXERR_NO_CHANGE) status = FIXERR_SUCCESS; + + } else if (!undefined(wcs->bepoch)) { + *wcsmjd = mjd1900 + (wcs->bepoch - 1900.0)*dty; + sprintf(newline(&cp), "Set MJD-OBS to %.6f from BEPOCH", *wcsmjd); + if (status == FIXERR_NO_CHANGE) status = FIXERR_SUCCESS; } } - } - } else { - if (strlen(dateobs) < 8) { - /* Can't be a valid date. */ - return wcserr_set(WCSERR_SET(FIXERR_BAD_PARAM), - "Invalid parameter value: date string too short '%s'", dateobs); - } + if (undefined(*wcsmjd)) { + // No date information was provided. + + } else { + // Calendar date from MJD, with allowance for MJD < 0. + double mjd[2], t; + if (i == 0) { + // MJDREF is already split into integer and fractional parts. + mjd[0] = wcsmjd[0]; + mjd[1] = wcsmjd[1]; + if (1.0 < mjd[1]) { + // Ensure the fractional part lies between 0 and +1. + t = floor(mjd[1]); + mjd[0] += t; + mjd[1] -= t; + } + } else { + // Split it into integer and fractional parts. + mjd[0] = floor(*wcsmjd); + mjd[1] = *wcsmjd - mjd[0]; + } - /* Identify the date format. */ - if (dateobs[4] == '-' && dateobs[7] == '-') { - /* Standard year-2000 form: CCYY-MM-DD[Thh:mm:ss[.sss...]] */ - if (sscanf(dateobs, "%4d-%2d-%2d", &year, &month, &day) < 3) { - return wcserr_set(WCSERR_SET(FIXERR_BAD_PARAM), - "Invalid parameter value: invalid date '%s'", dateobs); + int jd = 2400001 + (int)mjd[0]; + + int n4 = 4*(jd + ((2*((4*jd - 17918)/146097)*3)/4 + 1)/2 - 37); + int dd = 10*(((n4-237)%1461)/4) + 5; + + year = n4/1461 - 4712; + month = (2 + dd/306)%12 + 1; + day = (dd%306)/10 + 1; + sprintf(date, "%.4d-%.2d-%.2d", year, month, day); + + // Write time part only if non-zero. + if (0.0 < (t = mjd[1])) { + t *= 24.0; + hour = (int)t; + t = 60.0 * (t - hour); + minute = (int)t; + sec = 60.0 * (t - minute); + + // Round to 1ms. + dd = 60000*(60*hour + minute) + (int)(1000*(sec+0.0005)); + hour = dd / 3600000; + dd -= 3600000 * hour; + minute = dd / 60000; + int msec = dd - 60000 * minute; + sprintf(date+10, "T%.2d:%.2d:%.2d", hour, minute, msec/1000); + + // Write fractions of a second only if non-zero. + if (msec%1000) { + sprintf(date+19, ".%.3d", msec%1000); + } + } } - if (dateobs[10] == 'T') { - if (parse_date(dateobs+11, &hour, &minute, &sec)) { - return wcserr_set(WCSERR_SET(FIXERR_BAD_PARAM), - "Invalid parameter value: invalid time '%s'", dateobs+11); + } else { + if (strlen(date) < 8) { + // Can't be a valid date. + status = FIXERR_BAD_PARAM; + sprintf(newline(&cp), "Invalid DATE%s format '%s' is too short", + dateid, date); + continue; + } + + // Identify the date format. + if (date[4] == '-' && date[7] == '-') { + // Standard year-2000 form: CCYY-MM-DD[Thh:mm:ss[.sss...]] + if (sscanf(date, "%4d-%2d-%2d", &year, &month, &day) < 3) { + status = FIXERR_BAD_PARAM; + sprintf(newline(&cp), "Invalid DATE%s format '%s'", dateid, date); + continue; + } + + if (date[10] == 'T') { + if (parse_date(date+11, &hour, &minute, &sec)) { + status = FIXERR_BAD_PARAM; + sprintf(newline(&cp), "Invalid time in DATE%s '%s'", dateid, + date+11); + continue; + } + } else if (date[10] == ' ') { + hour = 0; + minute = 0; + sec = 0.0; + if (parse_date(date+11, &hour, &minute, &sec)) { + write_date(date+10, hour, minute, sec); + } else { + date[10] = 'T'; + } + } + + } else if (date[4] == '/' && date[7] == '/') { + // Also allow CCYY/MM/DD[Thh:mm:ss[.sss...]] + if (sscanf(date, "%4d/%2d/%2d", &year, &month, &day) < 3) { + status = FIXERR_BAD_PARAM; + sprintf(newline(&cp), "Invalid DATE%s format '%s'", dateid, date); + continue; + } + + if (date[10] == 'T') { + if (parse_date(date+11, &hour, &minute, &sec)) { + status = FIXERR_BAD_PARAM; + sprintf(newline(&cp), "Invalid time in DATE%s '%s'", dateid, + date+11); + continue; + } + } else if (date[10] == ' ') { + hour = 0; + minute = 0; + sec = 0.0; + if (parse_date(date+11, &hour, &minute, &sec)) { + write_date(date+10, hour, minute, sec); + } else { + date[10] = 'T'; + } } - } else if (dateobs[10] == ' ') { - hour = 0; - minute = 0; - sec = 0.0; - if (parse_date(dateobs+11, &hour, &minute, &sec)) { - write_date(dateobs+10, hour, minute, sec); + + // Looks ok, fix it up. + date[4] = '-'; + date[7] = '-'; + + } else { + if (i == 1 && date[2] == '/' && date[5] == '/') { + // Old format DATE-OBS date: DD/MM/YY, also allowing DD/MM/CCYY. + if (sscanf(date, "%2d/%2d/%4d", &day, &month, &year) < 3) { + status = FIXERR_BAD_PARAM; + sprintf(newline(&cp), "Invalid DATE%s format '%s'", dateid, + date); + continue; + } + + } else if (i == 1 && date[2] == '-' && date[5] == '-') { + // Also recognize DD-MM-YY and DD-MM-CCYY + if (sscanf(date, "%2d-%2d-%4d", &day, &month, &year) < 3) { + status = FIXERR_BAD_PARAM; + sprintf(newline(&cp), "Invalid DATE%s format '%s'", dateid, + date); + continue; + } + } else { - dateobs[10] = 'T'; + // Not a valid date format. + status = FIXERR_BAD_PARAM; + sprintf(newline(&cp), "Invalid DATE%s format '%s'", dateid, date); + continue; } - } - } else if (dateobs[4] == '/' && dateobs[7] == '/') { - /* Also allow CCYY/MM/DD[Thh:mm:ss[.sss...]] */ - if (sscanf(dateobs, "%4d/%2d/%2d", &year, &month, &day) < 3) { - return wcserr_set(WCSERR_SET(FIXERR_BAD_PARAM), - "Invalid parameter value: invalid date '%s'", dateobs); + if (year < 100) year += 1900; + + // Doesn't have a time. + sprintf(date, "%.4d-%.2d-%.2d", year, month, day); } - if (dateobs[10] == 'T') { - if (parse_date(dateobs+11, &hour, &minute, &sec)) { - return wcserr_set(WCSERR_SET(FIXERR_BAD_PARAM), - "Invalid parameter value: invalid time '%s'", dateobs+11); + // Compute MJD. + double mjd[2]; + mjd[0] = (double)((1461*(year - (12-month)/10 + 4712))/4 + + (306*((month+9)%12) + 5)/10 + - (3*((year - (12-month)/10 + 4900)/100))/4 + + day - 2399904); + mjd[1] = (hour + (minute + sec/60.0)/60.0)/24.0; + double mjdsum = mjd[0] + mjd[1]; + + if (undefined(*wcsmjd)) { + if (i == 0) { + wcsmjd[0] = mjd[0]; + wcsmjd[1] = mjd[1]; + } else { + *wcsmjd = mjdsum; } - } else if (dateobs[10] == ' ') { - hour = 0; - minute = 0; - sec = 0.0; - if (parse_date(dateobs+11, &hour, &minute, &sec)) { - write_date(dateobs+10, hour, minute, sec); + sprintf(newline(&cp), "Set MJD%s to %.6f from DATE%s", dateid, + mjdsum, dateid); + + if (status == FIXERR_NO_CHANGE) status = FIXERR_SUCCESS; + + } else { + // Check for consistency. + double mjdtmp; + if (i == 0) { + mjdtmp = wcsmjd[0] + wcsmjd[1]; } else { - dateobs[10] = 'T'; + mjdtmp = *wcsmjd; + } + + if (0.001 < fabs(mjdsum - mjdtmp)) { + status = FIXERR_BAD_PARAM; + sprintf(newline(&cp), + "Invalid parameter values: MJD%s and DATE%s are inconsistent", + dateid, dateid); } } - /* Looks ok, fix it up. */ - dateobs[4] = '-'; - dateobs[7] = '-'; + if (i == 1) { + if (!undefined(wcs->jepoch)) { + // Check consistency of JEPOCH. + double jepoch = 2000.0 + (*wcsmjd - mjd2000) / djy; - } else { - if (dateobs[2] == '/' && dateobs[5] == '/') { - /* Old format date: DD/MM/YY, also allowing DD/MM/CCYY. */ - if (sscanf(dateobs, "%2d/%2d/%4d", &day, &month, &year) < 3) { - return wcserr_set(WCSERR_SET(FIXERR_BAD_PARAM), - "Invalid parameter value: invalid date '%s'", dateobs); + if (0.000002 < fabs(jepoch - wcs->jepoch)) { + // Informational only, no error. + sprintf(newline(&cp), "JEPOCH is inconsistent with DATE-OBS"); + } } - } else if (dateobs[2] == '-' && dateobs[5] == '-') { - /* Also recognize DD-MM-YY and DD-MM-CCYY */ - if (sscanf(dateobs, "%2d-%2d-%4d", &day, &month, &year) < 3) { - return wcserr_set(WCSERR_SET(FIXERR_BAD_PARAM), - "Invalid parameter value: invalid date '%s'", dateobs); + if (!undefined(wcs->bepoch)) { + // Check consistency of BEPOCH. + double bepoch = 1900.0 + (*wcsmjd - mjd1900) / dty; + + if (0.000002 < fabs(bepoch - wcs->bepoch)) { + // Informational only, no error. + sprintf(newline(&cp), "BEPOCH is inconsistent with DATE-OBS"); + } } + } + } + if (strncmp(orig_date, date, 72)) { + if (orig_date[0] == '\0') { + sprintf(newline(&cp), "Set DATE%s to '%s' from MJD%s", dateid, date, + dateid); } else { - /* Not a valid date format. */ - return wcserr_set(WCSERR_SET(FIXERR_BAD_PARAM), - "Invalid parameter value: invalid date '%s'", dateobs); + sprintf(newline(&cp), "Changed DATE%s from '%s' to '%s'", dateid, + orig_date, date); } - if (year < 100) year += 1900; + if (status == FIXERR_NO_CHANGE) status = FIXERR_SUCCESS; + } + } + + if (*infomsg) { + wcserr_set(WCSERR_SET(FIXERR_DATE_FIX), infomsg); + } + + return status; +} + +//---------------------------------------------------------------------------- + +int obsfix(int ctrl, struct wcsprm *wcs) + +{ + static const char *function = "obsfix"; + + // IAU(1976) ellipsoid (as prescribed by WCS Paper VII). + const double a = 6378140.0; + const double f = 1.0 / 298.2577; + const double e2 = (2.0 - f)*f; + + if (wcs == 0x0) return FIXERR_NULL_POINTER; + struct wcserr **err = &(wcs->err); + + // Set masks for checking partially-defined coordinate triplets. + int havexyz = 7; + havexyz -= 1*undefined(wcs->obsgeo[0]); + havexyz -= 2*undefined(wcs->obsgeo[1]); + havexyz -= 4*undefined(wcs->obsgeo[2]); + int havelbh = 7; + havelbh -= 1*undefined(wcs->obsgeo[3]); + havelbh -= 2*undefined(wcs->obsgeo[4]); + havelbh -= 4*undefined(wcs->obsgeo[5]); + + if (ctrl == 2) { + // Make no changes. + if (0 < havexyz && havexyz < 7) { + return wcserr_set(WCSERR_SET(FIXERR_BAD_PARAM), + "Partially undefined Cartesian coordinate triplet"); + } + + if (0 < havelbh && havelbh < 7) { + return wcserr_set(WCSERR_SET(FIXERR_BAD_PARAM), + "Partially undefined Geodetic coordinate triplet"); + } - /* Doesn't have a time. */ - sprintf(dateobs, "%.4d-%.2d-%.2d", year, month, day); + if (havexyz == 0 || havelbh == 0) { + return FIXERR_NO_CHANGE; } + } - /* Compute MJD. */ - mjdobs = (double)((1461*(year - (12-month)/10 + 4712))/4 - + (306*((month+9)%12) + 5)/10 - - (3*((year - (12-month)/10 + 4900)/100))/4 - + day - 2399904) - + (hour + (minute + sec/60.0)/60.0)/24.0; + if (havexyz == 0 && havelbh == 0) { + return FIXERR_NO_CHANGE; + } - if (undefined(wcs->mjdobs)) { - wcs->mjdobs = mjdobs; - } else { - /* Check for consistency. */ - if (fabs(mjdobs - wcs->mjdobs) > 0.5) { - return wcserr_set(WCSERR_SET(FIXERR_BAD_PARAM), - "Invalid parameter value: inconsistent date '%s'", dateobs); + + char infomsg[256]; + infomsg[0] = '\0'; + + int status = FIXERR_NO_CHANGE; + + size_t k; + double x, y, z; + if (havelbh == 7) { + // Compute (x,y,z) from (lng,lat,hgt). + double coslat, coslng, sinlat, sinlng; + sincosd(wcs->obsgeo[3], &sinlng, &coslng); + sincosd(wcs->obsgeo[4], &sinlat, &coslat); + double n = a / sqrt(1.0 - e2*sinlat*sinlat); + double rho = n + wcs->obsgeo[5]; + + x = rho*coslng*coslat; + y = rho*sinlng*coslat; + z = (rho - n*e2)*sinlat; + + if (havexyz < 7) { + // One or more of the Cartesian elements was undefined. + status = FIXERR_SUCCESS; + char *cp = infomsg; + + if (ctrl == 1 || !(havexyz & 1)) { + wcs->obsgeo[0] = x; + sprintf(cp, "%s OBSGEO-X to %12.3f from OBSGEO-[LBH]", + (havexyz & 1) ? "Reset" : "Set", x); + } + + if (ctrl == 1 || !(havexyz & 2)) { + wcs->obsgeo[1] = y; + + if ((k = strlen(cp))) { + strcat(cp+k, ".\n"); + cp += k + 2; + } + + sprintf(cp, "%s OBSGEO-Y to %12.3f from OBSGEO-[LBH]", + (havexyz & 2) ? "Reset" : "Set", y); + } + + if (ctrl == 1 || !(havexyz & 4)) { + wcs->obsgeo[2] = z; + if ((k = strlen(cp))) { + strcat(cp+k, ".\n"); + cp += k + 2; + } + + sprintf(cp, "%s OBSGEO-Z to %12.3f from OBSGEO-[LBH]", + (havexyz & 4) ? "Reset" : "Set", z); + } + + wcserr_set(WCSERR_SET(FIXERR_OBSGEO_FIX), infomsg); + + if (havexyz == 0) { + // Skip the consistency check. + return status; + } + } + + } else if (havexyz == 7) { + // Compute (lng,lat,hgt) from (x,y,z). + x = wcs->obsgeo[0]; + y = wcs->obsgeo[1]; + z = wcs->obsgeo[2]; + double r2 = x*x + y*y; + + // Iterate over the value of zeta. + double coslat, coslng, sinlat, sinlng; + double n, rho, zeta = z; + for (int i = 0; i < 4; i++) { + rho = sqrt(r2 + zeta*zeta); + sinlat = zeta / rho; + n = a / sqrt(1.0 - e2*sinlat*sinlat); + + zeta = z / (1.0 - n*e2/rho); + } + + double lng = atan2d(y, x); + double lat = asind(sinlat); + double hgt = rho - n; + + if (havelbh < 7) { + // One or more of the Geodetic elements was undefined. + status = FIXERR_SUCCESS; + char *cp = infomsg; + + if (ctrl == 1 || !(havelbh & 1)) { + wcs->obsgeo[3] = lng; + sprintf(cp, "%s OBSGEO-L to %12.6f from OBSGEO-[XYZ]", + (havelbh & 1) ? "Reset" : "Set", lng); + } + + if (ctrl == 1 || !(havelbh & 2)) { + wcs->obsgeo[4] = lat; + if ((k = strlen(cp))) { + strcat(cp+k, ".\n"); + cp += k + 2; + } + + sprintf(cp, "%s OBSGEO-B to %12.6f from OBSGEO-[XYZ]", + (havelbh & 2) ? "Reset" : "Set", lat); + } + + if (ctrl == 1 || !(havelbh & 4)) { + wcs->obsgeo[5] = hgt; + if ((k = strlen(cp))) { + strcat(cp+k, ".\n"); + cp += k + 2; + } + + sprintf(cp, "%s OBSGEO-H to %12.3f from OBSGEO-[XYZ]", + (havelbh & 4) ? "Reset" : "Set", hgt); + } + + wcserr_set(WCSERR_SET(FIXERR_OBSGEO_FIX), infomsg); + + if (havelbh == 0) { + // Skip the consistency check. + return status; } } + + // Compute (x,y,z) from (lng,lat,hgt) for consistency checking. + sincosd(wcs->obsgeo[3], &sinlng, &coslng); + sincosd(wcs->obsgeo[4], &sinlat, &coslat); + n = a / sqrt(1.0 - e2*sinlat*sinlat); + rho = n + wcs->obsgeo[5]; + + x = rho*coslng*coslat; + y = rho*sinlng*coslat; + z = (rho - n*e2)*sinlat; + + } else { + return wcserr_set(WCSERR_SET(FIXERR_BAD_PARAM), + "Observatory coordinates incomplete"); } - if (strncmp(orig_dateobs, dateobs, 72)) { - wcserr_set(WCSERR_SET(FIXERR_DATE_FIX), - "Changed '%s' to '%s'", orig_dateobs, dateobs); - return FIXERR_SUCCESS; + // Check consistency. + double d, r2 = 0.0; + d = wcs->obsgeo[0] - x; + r2 += d*d; + d = wcs->obsgeo[1] - y; + r2 += d*d; + d = wcs->obsgeo[2] - z; + r2 += d*d; + + if (1.0 < r2) { + d = sqrt(r2); + return wcserr_set(WCSERR_SET(FIXERR_BAD_PARAM), + "Observatory coordinates inconsistent by %.1f metres", d); } - return FIXERR_NO_CHANGE; + return status; } -/*--------------------------------------------------------------------------*/ + +//---------------------------------------------------------------------------- int unitfix(int ctrl, struct wcsprm *wcs) { - int i, k, status = FIXERR_NO_CHANGE; - char orig_unit[80], msg[WCSERR_MSG_LENGTH]; const char *function = "unitfix"; - struct wcserr **err; if (wcs == 0x0) return FIXERR_NULL_POINTER; - err = &(wcs->err); - - strcpy(msg, "Changed units: "); - for (i = 0; i < wcs->naxis; i++) { - strncpy(orig_unit, wcs->cunit[i], 80); - if (wcsutrne(ctrl, wcs->cunit[i], &(wcs->err)) == 0) { - k = strlen(msg); - sprintf(msg+k, "'%s' -> '%s', ", orig_unit, wcs->cunit[i]); - status = FIXERR_UNITS_ALIAS; + struct wcserr **err = &(wcs->err); + + int status = FIXERR_NO_CHANGE; + + char msg[512]; + strncpy(msg, "Changed units:", 512); + + for (int i = 0; i < wcs->naxis; i++) { + char orig_unit[72]; + strncpy(orig_unit, wcs->cunit[i], 71); + int result = wcsutrne(ctrl, wcs->cunit[i], &(wcs->err)); + if (result == 0 || result == 12) { + size_t msglen = strlen(msg); + if (msglen < 511) { + wcsutil_null_fill(72, orig_unit); + char msgtmp[192]; + sprintf(msgtmp, "\n '%s' -> '%s',", orig_unit, wcs->cunit[i]); + strncpy(msg+msglen, msgtmp, 511-msglen); + status = FIXERR_UNITS_ALIAS; + } } } if (status == FIXERR_UNITS_ALIAS) { - k = strlen(msg) - 2; - msg[k] = '\0'; + // Chop off the trailing ", ". + size_t msglen = strlen(msg) - 2; + msg[msglen] = '\0'; wcserr_set(WCSERR_SET(FIXERR_UNITS_ALIAS), msg); status = FIXERR_SUCCESS; @@ -443,28 +845,25 @@ int unitfix(int ctrl, struct wcsprm *wcs) return status; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- int spcfix(struct wcsprm *wcs) { static const char *function = "spcfix"; - char ctype[9], specsys[9]; - int i, status; - struct wcserr **err; - if (wcs == 0x0) return FIXERR_NULL_POINTER; - err = &(wcs->err); - - for (i = 0; i < wcs->naxis; i++) { - /* Translate an AIPS-convention spectral type if present. */ - status = spcaips(wcs->ctype[i], wcs->velref, ctype, specsys); - if (status == 0) { - /* An AIPS type was found but it may match what we already have. */ + struct wcserr **err = &(wcs->err); + + for (int i = 0; i < wcs->naxis; i++) { + // Translate an AIPS-convention spectral type if present. + char ctype[9], specsys[9]; + int status = spcaips(wcs->ctype[i], wcs->velref, ctype, specsys); + if (status == FIXERR_SUCCESS) { + // An AIPS type was found but it may match what we already have. status = FIXERR_NO_CHANGE; - /* Was specsys translated? */ + // Was specsys translated? if (wcs->specsys[0] == '\0' && *specsys) { strncpy(wcs->specsys, specsys, 9); wcserr_set(WCSERR_SET(FIXERR_SPC_UPDATE), @@ -472,35 +871,37 @@ int spcfix(struct wcsprm *wcs) status = FIXERR_SUCCESS; } - /* Was ctype translated? Have to null-fill for comparing them. */ + // Was ctype translated? Have to null-fill for comparing them. wcsutil_null_fill(9, wcs->ctype[i]); if (strncmp(wcs->ctype[i], ctype, 9)) { - /* ctype was translated... */ + // ctype was translated... if (status == FIXERR_SUCCESS) { - /* ...and specsys was also. */ + // ...and specsys was also. wcserr_set(WCSERR_SET(FIXERR_SPC_UPDATE), - "Changed CTYPE%d from '%s' to '%s', and SPECSYS to '%s'", - i+1, wcs->ctype[i], ctype, wcs->specsys); + "Changed CTYPE%d from '%s' to '%s', and SPECSYS to '%s' " + "(VELREF=%d)", i+1, wcs->ctype[i], ctype, wcs->specsys, + wcs->velref); } else { wcserr_set(WCSERR_SET(FIXERR_SPC_UPDATE), - "Changed CTYPE%d from '%s' to '%s'", i+1, wcs->ctype[i], ctype); + "Changed CTYPE%d from '%s' to '%s' (VELREF=%d)", i+1, + wcs->ctype[i], ctype, wcs->velref); status = FIXERR_SUCCESS; } strncpy(wcs->ctype[i], ctype, 9); } - /* Tidy up. */ + // Tidy up. if (status == FIXERR_SUCCESS) { wcsutil_null_fill(72, wcs->ctype[i]); wcsutil_null_fill(72, wcs->specsys); } - /* No need to check for others, wcsset() will fail if so. */ + // No need to check for others, wcsset() will fail if so. return status; } else if (status == SPCERR_BAD_SPEC_PARAMS) { - /* An AIPS spectral type was found but with invalid velref. */ + // An AIPS spectral type was found but with invalid velref. return wcserr_set(WCSERR_SET(FIXERR_BAD_PARAM), "Invalid parameter value: velref = %d", wcs->velref); } @@ -509,35 +910,31 @@ int spcfix(struct wcsprm *wcs) return FIXERR_NO_CHANGE; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- int celfix(struct wcsprm *wcs) { static const char *function = "celfix"; - int k, status; - struct celprm *wcscel = &(wcs->cel); - struct prjprm *wcsprj = &(wcscel->prj); - struct wcserr **err; - if (wcs == 0x0) return FIXERR_NULL_POINTER; - err = &(wcs->err); + struct wcserr **err = &(wcs->err); - /* Initialize if required. */ - if (wcs->flag != WCSSET) { - if ((status = wcsset(wcs))) return status; + // Initialize if required. + int status; + if (abs(wcs->flag) != WCSSET) { + if ((status = wcsset(wcs))) return fix_wcserr[status]; } - /* Was an NCP or GLS projection code translated? */ + // Was an NCP or GLS projection code translated? if (wcs->lat >= 0) { - /* Check ctype. */ + // Check ctype. if (strcmp(wcs->ctype[wcs->lat]+5, "NCP") == 0) { strcpy(wcs->ctype[wcs->lng]+5, "SIN"); strcpy(wcs->ctype[wcs->lat]+5, "SIN"); if (wcs->npvmax < wcs->npv + 2) { - /* Allocate space for two more PVi_ja keyvalues. */ + // Allocate space for two more PVi_ma keyvalues. if (wcs->m_flag == WCSSET && wcs->pv == wcs->m_pv) { if (!(wcs->pv = calloc(wcs->npv+2, sizeof(struct pvcard)))) { wcs->pv = wcs->m_pv; @@ -547,7 +944,7 @@ int celfix(struct wcsprm *wcs) wcs->npvmax = wcs->npv + 2; wcs->m_flag = WCSSET; - for (k = 0; k < wcs->npv; k++) { + for (int k = 0; k < wcs->npv; k++) { wcs->pv[k] = wcs->m_pv[k]; } @@ -559,6 +956,8 @@ int celfix(struct wcsprm *wcs) } } + struct celprm *wcscel = &(wcs->cel); + struct prjprm *wcsprj = &(wcscel->prj); wcs->pv[wcs->npv].i = wcs->lat + 1; wcs->pv[wcs->npv].m = 1; wcs->pv[wcs->npv].value = wcsprj->pv[1]; @@ -576,16 +975,16 @@ int celfix(struct wcsprm *wcs) strcpy(wcs->ctype[wcs->lat]+5, "SFL"); if (wcs->crval[wcs->lng] != 0.0 || wcs->crval[wcs->lat] != 0.0) { - /* In the AIPS convention, setting the reference longitude and - * latitude for GLS does not create an oblique graticule. A non-zero - * reference longitude introduces an offset in longitude in the normal - * way, whereas a non-zero reference latitude simply translates the - * reference point (i.e. the map as a whole) to that latitude. This - * might be effected by adjusting CRPIXja but that is complicated by - * the linear transformation and instead is accomplished here by - * setting theta_0. */ - if (wcs->npvmax < wcs->npv + 2) { - /* Allocate space for three more PVi_ja keyvalues. */ + // In the AIPS convention, setting the reference longitude and + // latitude for GLS does not create an oblique graticule. A non-zero + // reference longitude introduces an offset in longitude in the normal + // way, whereas a non-zero reference latitude simply translates the + // reference point (i.e. the map as a whole) to that latitude. This + // might be effected by adjusting CRPIXja but that is complicated by + // the linear transformation and instead is accomplished here by + // setting theta_0. + if (wcs->npvmax < wcs->npv + 3) { + // Allocate space for three more PVi_ma keyvalues. if (wcs->m_flag == WCSSET && wcs->pv == wcs->m_pv) { if (!(wcs->pv = calloc(wcs->npv+3, sizeof(struct pvcard)))) { wcs->pv = wcs->m_pv; @@ -595,7 +994,7 @@ int celfix(struct wcsprm *wcs) wcs->npvmax = wcs->npv + 3; wcs->m_flag = WCSSET; - for (k = 0; k < wcs->npv; k++) { + for (int k = 0; k < wcs->npv; k++) { wcs->pv[k] = wcs->m_pv[k]; } @@ -612,7 +1011,7 @@ int celfix(struct wcsprm *wcs) wcs->pv[wcs->npv].value = 1.0; (wcs->npv)++; - /* Note that the reference longitude is still zero. */ + // Note that the reference longitude is still zero. wcs->pv[wcs->npv].i = wcs->lng + 1; wcs->pv[wcs->npv].m = 1; wcs->pv[wcs->npv].value = 0.0; @@ -631,48 +1030,47 @@ int celfix(struct wcsprm *wcs) return FIXERR_NO_CHANGE; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- int cylfix(const int naxis[], struct wcsprm *wcs) { static const char *function = "cylfix"; - unsigned short icnr, indx[NMAX], ncnr; - int j, k, stat[4], status; - double img[4][NMAX], lat, lng, phi[4], phi0, phimax, phimin, pix[4][NMAX], - *pixj, theta[4], theta0, world[4][NMAX], x, y; - struct wcserr **err; - if (naxis == 0x0) return FIXERR_NO_CHANGE; if (wcs == 0x0) return FIXERR_NULL_POINTER; - err = &(wcs->err); + struct wcserr **err = &(wcs->err); - /* Initialize if required. */ - if (wcs->flag != WCSSET) { - if ((status = wcsset(wcs))) return status; + // Initialize if required. + int status; + if (abs(wcs->flag) != WCSSET) { + if ((status = wcsset(wcs))) return fix_wcserr[status]; } - /* Check that we have a cylindrical projection. */ + // Check that we have a cylindrical projection. if (wcs->cel.prj.category != CYLINDRICAL) return FIXERR_NO_CHANGE; if (wcs->naxis < 2) return FIXERR_NO_CHANGE; - /* Compute the native longitude in each corner of the image. */ - ncnr = 1 << wcs->naxis; + // Compute the native longitude in each corner of the image. + unsigned short ncnr = 1 << wcs->naxis; - for (k = 0; k < NMAX; k++) { + unsigned short indx[NMAX]; + for (int k = 0; k < NMAX; k++) { indx[k] = 1 << k; } - phimin = 1.0e99; - phimax = -1.0e99; - for (icnr = 0; icnr < ncnr;) { - /* Do four corners at a time. */ - for (j = 0; j < 4; j++, icnr++) { - pixj = pix[j]; + int stat[4]; + double img[4][NMAX], phi[4], pix[4][NMAX], theta[4], world[4][NMAX]; - for (k = 0; k < wcs->naxis; k++) { + double phimin = 1.0e99; + double phimax = -1.0e99; + for (unsigned short icnr = 0; icnr < ncnr;) { + // Do four corners at a time. + for (int j = 0; j < 4; j++, icnr++) { + double *pixj = pix[j]; + + for (int k = 0; k < wcs->naxis; k++) { if (icnr & indx[k]) { *(pixj++) = naxis[k] + 0.5; } else { @@ -683,54 +1081,54 @@ int cylfix(const int naxis[], struct wcsprm *wcs) if (!(status = wcsp2s(wcs, 4, NMAX, pix[0], img[0], phi, theta, world[0], stat))) { - for (j = 0; j < 4; j++) { + for (int j = 0; j < 4; j++) { if (phi[j] < phimin) phimin = phi[j]; if (phi[j] > phimax) phimax = phi[j]; } } } - if (phimin > phimax) return status; + if (phimin > phimax) return fix_wcserr[status]; - /* Any changes needed? */ + // Any changes needed? if (phimin >= -180.0 && phimax <= 180.0) return FIXERR_NO_CHANGE; - /* Compute the new reference pixel coordinates. */ - phi0 = (phimin + phimax) / 2.0; - theta0 = 0.0; + // Compute the new reference pixel coordinates. + double phi0 = (phimin + phimax) / 2.0; + double theta0 = 0.0; + double x, y; if ((status = prjs2x(&(wcs->cel.prj), 1, 1, 1, 1, &phi0, &theta0, &x, &y, stat))) { if (status == PRJERR_BAD_PARAM) { - return wcserr_set(WCSFIX_ERRMSG(FIXERR_BAD_PARAM)); + status = FIXERR_BAD_PARAM; + } else { + status = FIXERR_NO_REF_PIX_COORD; } - return wcserr_set(WCSFIX_ERRMSG(FIXERR_NO_REF_PIX_COORD)); + return wcserr_set(WCSFIX_ERRMSG(status)); } - for (k = 0; k < wcs->naxis; k++) { + for (int k = 0; k < wcs->naxis; k++) { img[0][k] = 0.0; } img[0][wcs->lng] = x; img[0][wcs->lat] = y; if ((status = linx2p(&(wcs->lin), 1, 0, img[0], pix[0]))) { - return wcserr_set(WCSFIX_ERRMSG(status)); + return wcserr_set(WCSFIX_ERRMSG(fix_linerr[status])); } - /* Compute celestial coordinates at the new reference pixel. */ + // Compute celestial coordinates at the new reference pixel. if ((status = wcsp2s(wcs, 1, 0, pix[0], img[0], phi, theta, world[0], stat))) { - if (wcs->err->status == WCSERR_BAD_PIX) { - wcs->err->status = FIXERR_NO_REF_PIX_COORD; - } - return wcs->err->status; + return fix_wcserr[status]; } - /* Compute native coordinates of the celestial pole. */ - lng = 0.0; - lat = 90.0; + // Compute native coordinates of the celestial pole. + double lng = 0.0; + double lat = 90.0; (void)sphs2x(wcs->cel.euler, 1, 1, 1, 1, &lng, &lat, phi, theta); wcs->crpix[wcs->lng] = pix[0][wcs->lng]; @@ -739,5 +1137,347 @@ int cylfix(const int naxis[], struct wcsprm *wcs) wcs->crval[wcs->lat] = world[0][wcs->lat]; wcs->lonpole = phi[0] - phi0; + wcs->flag = (wcs->flag == -WCSSET) ? 1 : 0; return wcsset(wcs); } + +//---------------------------------------------------------------------------- + +// Helper function used only by wcspcx(). +static int unscramble(int n, int mapto[], int step, int type, void *vptr); + +int wcspcx( + struct wcsprm *wcs, + int dopc, + int permute, + double rotn[2]) + +{ + static const char *function = "wcspcx"; + + // Initialize if required. + if (wcs == 0x0) return FIXERR_NULL_POINTER; + struct wcserr **err = &(wcs->err); + + int status; + if (abs(wcs->flag) != WCSSET) { + if ((status = wcsset(wcs))) return fix_wcserr[status]; + } + + // Check for CDi_j usage. + double *wcscd = wcs->cd; + if ((wcs->altlin & 1) || !(wcs->altlin & 2)) { + if ((wcs->altlin & 1) && dopc == 1) { + // Recompose PCi_j + CDELTi. + wcscd = wcs->pc; + } else { + return wcserr_set(WCSERR_SET(FIXERR_BAD_PARAM), + "CDi_j is not used in this coordinate representation"); + } + } + + // Check for sequent distortions. + if (wcs->lin.disseq) { + return wcserr_set(WCSERR_SET(FIXERR_BAD_COORD_TRANS), + "Cannot handle coordinate descriptions containing sequent distortions"); + } + + + // Allocate memory in bulk for two nxn matrices. + int naxis = wcs->naxis; + double *mem; + if ((mem = calloc(2*naxis*naxis, sizeof(double))) == 0x0) { + return wcserr_set(WCSFIX_ERRMSG(FIXERR_MEMORY)); + } + + double *mat = mem; + double *inv = mem + naxis*naxis; + + // Construct the transpose of CDi_j with each element squared. + double *matij = mat; + for (int i = 0; i < naxis; i++) { + double *cdji = wcscd + i; + for (int j = 0; j < naxis; j++) { + *(matij++) = (*cdji) * (*cdji); + cdji += naxis; + } + } + + // Invert the matrix. + if ((status = matinv(naxis, mat, inv))) { + return wcserr_set(WCSERR_SET(FIXERR_SINGULAR_MTX), + "No solution for CDi_j matrix decomposition"); + } + + // Apply scaling. + double *invij = inv; + double *pcij = wcs->pc; + double *cdij = wcscd; + for (int i = 0; i < naxis; i++) { + double scl = 0.0; + for (int j = 0; j < naxis; j++) { + scl += *(invij++); + } + + scl = sqrt(scl); + wcs->cdelt[i] /= scl; + + for (int j = 0; j < naxis; j++) { + *(pcij++) = *(cdij++) * scl; + } + } + + // mapto[i] records where row i of PCi_j should move to. + int *mapto = 0x0; + if ((mapto = (int*)malloc(naxis*sizeof(int))) == 0x0) { + free(mem); + return wcserr_set(WCSFIX_ERRMSG(FIXERR_MEMORY)); + } + + for (int i = 0; i < naxis; i++) { + mapto[i] = -1; + } + + // Ensure that latitude always follows longitude. + if (wcs->lng >= 0 && wcs->lat >= 0) { + double *pci = wcs->pc + naxis*wcs->lng; + + // Take the first non-zero element in the row. + for (int j = 0; j < naxis; j++) { + if (fabs(pci[j]) != 0.0) { + mapto[wcs->lng] = j; + break; + } + } + + if (mapto[wcs->lng] == naxis-1) { + mapto[wcs->lng]--; + } + + mapto[wcs->lat] = mapto[wcs->lng] + 1; + } + + // Fill in the rest of the row permutation map. + for (int j = 0; j < naxis; j++) { + // Column j. + double *pcij = wcs->pc + j; + double colmax = 0.0; + + // Look down the column to find the absolute maximum element. + for (int i = 0; i < naxis; i++, pcij += naxis) { + if (!(mapto[i] < 0)) { + // This row is already mapped. + continue; + } + + if (fabs(*pcij) > colmax) { + mapto[i] = j; + colmax = fabs(*pcij); + } + } + } + + + // Fix the sign of CDELTi. Celestial axes are special, otherwise diagonal + // elements of the correctly permuted matrix should be positive. + for (int i = 0; i < naxis; i++) { + int chsgn; + double *pci = wcs->pc + naxis*i; + + // Celestial axes are special. + if (i == wcs->lng) { + // Longitude axis - force CDELTi < 0.0. + chsgn = (wcs->cdelt[i] > 0.0); + } else if (i == wcs->lat) { + // Latitude axis - force CDELTi > 0.0. + chsgn = (wcs->cdelt[i] < 0.0); + } else { + chsgn = (pci[mapto[i]] < 0.0); + } + + if (chsgn) { + wcs->cdelt[i] = -wcs->cdelt[i]; + + for (int j = 0; j < naxis; j++) { + // Test needed to prevent negative zeros. + if (pci[j] != 0.0) { + pci[j] = -pci[j]; + } + } + } + } + + free(mem); + + // Setting bit 3 in wcsprm::altlin stops wcsset() from reconstructing + // PCi_j and CDELTi from CDi_j. + wcs->altlin |= 8; + + + // Compute rotation angle of each basis vector of the celestial axes. + if (rotn) { + if (wcs->lng < 0 || wcs->lat < 0) { + // No celestial axes. + rotn[0] = 0.0; + rotn[1] = 0.0; + + } else { + double x, y; + x = wcs->pc[naxis*wcs->lng + mapto[wcs->lng]]; + y = wcs->pc[naxis*wcs->lat + mapto[wcs->lng]]; + rotn[0] = atan2d(y, x); + + y = -wcs->pc[naxis*wcs->lng + mapto[wcs->lat]]; + x = wcs->pc[naxis*wcs->lat + mapto[wcs->lat]]; + rotn[1] = atan2d(y, x); + } + } + + + // Permute rows? + if (permute) { + // Check whether there's anything to unscramble. + int scrambled = 0; + for (int i = 0; i < naxis; i++) { + if (mapto[i] != i) { + scrambled = 1; + break; + } + } + + if (scrambled) { + for (int i = 0; i < naxis; i++) { + // Do columns of the PCi_ja matrix. + if (unscramble(naxis, mapto, naxis, 1, wcs->pc + i)) goto cleanup; + } + if (unscramble(naxis, mapto, 1, 1, wcs->cdelt)) goto cleanup; + if (unscramble(naxis, mapto, 1, 1, wcs->crval)) goto cleanup; + if (unscramble(naxis, mapto, 1, 2, wcs->cunit)) goto cleanup; + if (unscramble(naxis, mapto, 1, 2, wcs->ctype)) goto cleanup; + + for (int ipv = 0; ipv < wcs->npv; ipv++) { + // Noting that PVi_ma axis numbers are 1-relative. + int i = wcs->pv[ipv].i - 1; + wcs->pv[ipv].i = mapto[i] + 1; + } + + for (int ips = 0; ips < wcs->nps; ips++) { + // Noting that PSi_ma axis numbers are 1-relative. + int i = wcs->ps[ips].i - 1; + wcs->ps[ips].i = mapto[i] + 1; + } + + if (wcs->altlin & 2) { + for (int i = 0; i < naxis; i++) { + // Do columns of the CDi_ja matrix. + if (unscramble(naxis, mapto, naxis, 1, wcs->cd + i)) goto cleanup; + } + } + + if (wcs->altlin & 4) { + if (unscramble(naxis, mapto, 1, 1, wcs->crota)) goto cleanup; + } + + if (unscramble(naxis, mapto, 1, 3, wcs->colax)) goto cleanup; + if (unscramble(naxis, mapto, 1, 2, wcs->cname)) goto cleanup; + if (unscramble(naxis, mapto, 1, 1, wcs->crder)) goto cleanup; + if (unscramble(naxis, mapto, 1, 1, wcs->csyer)) goto cleanup; + if (unscramble(naxis, mapto, 1, 1, wcs->czphs)) goto cleanup; + if (unscramble(naxis, mapto, 1, 1, wcs->cperi)) goto cleanup; + + // Coordinate lookup tables. + for (int itab = 0; itab < wcs->ntab; itab++) { + for (int m = 0; m < wcs->tab[itab].M; m++) { + int i = wcs->tab[itab].map[m]; + wcs->tab[itab].map[m] = mapto[i]; + } + } + + for (int iwtb = 0; iwtb < wcs->nwtb; iwtb++) { + int i = wcs->wtb[iwtb].i; + wcs->wtb[iwtb].i = mapto[i]; + } + + // Distortions? No. Prior distortions operate on pixel coordinates and + // therefore are not permuted, and sequent distortions are not handled. + } + } + + free(mapto); + + // Reset the struct. + wcs->flag = (wcs->flag == -WCSSET) ? 1 : 0; + if ((status = wcsset(wcs))) return fix_wcserr[status]; + + return FIXERR_SUCCESS; + +cleanup: + if (mapto) free(mapto); + return wcserr_set(WCSFIX_ERRMSG(FIXERR_MEMORY)); +} + + +int unscramble( + int n, + int mapto[], + int step, + int type, + void *vptr) + +{ + if (step == 0) step = 1; + + if (type == 1) { + double *dval = (double *)vptr; + double *dtmp; + if ((dtmp = (double *)malloc(n*sizeof(double))) == 0x0) { + return 1; + } + + for (int i = 0; i < n; i++) { + dtmp[mapto[i]] = dval[i*step]; + } + + for (int i = 0; i < n; i++) { + dval[i*step] = dtmp[i]; + } + + free(dtmp); + + } else if (type == 2) { + char (*cval)[72] = (char (*)[72])vptr; + char (*ctmp)[72]; + if ((ctmp = (char (*)[72])malloc(n*72*sizeof(char))) == 0x0) { + return 1; + } + + for (int i = 0; i < n; i++) { + memcpy(ctmp[mapto[i]], cval[i], 72); + } + + for (int i = 0; i < n; i++) { + memcpy(cval[i], ctmp[i], 72); + } + + free(ctmp); + + } else if (type == 3) { + int *ival = (int *)vptr; + int *itmp; + if ((itmp = (int *)malloc(n*sizeof(int))) == 0x0) { + return 1; + } + + for (int i = 0; i < n; i++) { + itmp[mapto[i]] = ival[i]; + } + + for (int i = 0; i < n; i++) { + ival[i] = itmp[i]; + } + + free(itmp); + } + + return 0; +} diff --git a/cextern/wcslib/C/wcsfix.h b/cextern/wcslib/C/wcsfix.h index 0bb1e76d06d0..72d855bc4e69 100644 --- a/cextern/wcslib/C/wcsfix.h +++ b/cextern/wcslib/C/wcsfix.h @@ -1,7 +1,6 @@ /*============================================================================ - - WCSLIB 4.14 - an implementation of the FITS WCS standard. - Copyright (C) 1995-2012, Mark Calabretta + WCSLIB 8.6 - an implementation of the FITS WCS standard. + Copyright (C) 1995-2026, Mark Calabretta This file is part of WCSLIB. @@ -18,36 +17,49 @@ You should have received a copy of the GNU Lesser General Public License along with WCSLIB. If not, see http://www.gnu.org/licenses. - Direct correspondence concerning WCSLIB to mark@calabretta.id.au - Author: Mark Calabretta, Australia Telescope National Facility, CSIRO. - http://www.atnf.csiro.au/people/Mark.Calabretta - $Id: wcsfix.h,v 4.14 2012/07/13 10:02:27 cal103 Exp $ + http://www.atnf.csiro.au/computing/software/wcs + $Id: wcsfix.h,v 8.6 2026/03/29 13:53:56 mcalabre Exp $ *============================================================================= * -* WCSLIB 4.14 - C routines that implement the FITS World Coordinate System -* (WCS) standard. Refer to -* -* "Representations of world coordinates in FITS", -* Greisen, E.W., & Calabretta, M.R. 2002, A&A, 395, 1061 (Paper I) -* -* "Representations of celestial coordinates in FITS", -* Calabretta, M.R., & Greisen, E.W. 2002, A&A, 395, 1077 (Paper II) -* -* "Representations of spectral coordinates in FITS", -* Greisen, E.W., Calabretta, M.R., Valdes, F.G., & Allen, S.L. -* 2006, A&A, 446, 747 (Paper III) -* -* Refer to the README file provided with WCSLIB for an overview of the -* library. +* WCSLIB 8.6 - C routines that implement the FITS World Coordinate System +* (WCS) standard. Refer to the README file provided with WCSLIB for an +* overview of the library. * * * Summary of the wcsfix routines * ------------------------------ -* Routines in this suite identify and translate various forms of non-standard -* construct that are known to occur in FITS WCS headers. These range from the -* translation of non-standard values for standard WCS keywords, to the repair -* of malformed coordinate representations. +* Routines in this suite identify and translate various forms of construct +* known to occur in FITS headers that violate the FITS World Coordinate System +* (WCS) standard described in +* += "Representations of world coordinates in FITS", += Greisen, E.W., & Calabretta, M.R. 2002, A&A, 395, 1061 (WCS Paper I) += += "Representations of celestial coordinates in FITS", += Calabretta, M.R., & Greisen, E.W. 2002, A&A, 395, 1077 (WCS Paper II) += += "Representations of spectral coordinates in FITS", += Greisen, E.W., Calabretta, M.R., Valdes, F.G., & Allen, S.L. += 2006, A&A, 446, 747 (WCS Paper III) += += "Representations of time coordinates in FITS - += Time and relative dimension in space", += Rots, A.H., Bunclark, P.S., Calabretta, M.R., Allen, S.L., += Manchester, R.N., & Thompson, W.T. 2015, A&A, 574, A36 (WCS Paper VII) +* +* Repairs effected by these routines range from the translation of +* non-standard values for standard WCS keywords, to the repair of malformed +* coordinate representations. Some routines are also provided to check the +* consistency of pairs of keyvalues that define the same measure in two +* different ways, for example, as a date and an MJD. +* +* A separate routine, wcspcx(), "regularizes" the linear transformation matrix +* component (PCi_j) of the coordinate transformation to make it more human- +* readable. Where a coordinate description was constructed from CDi_j, it +* decomposes it into PCi_j + CDELTi in a meaningful way. Optionally, it can +* also diagonalize the PCi_j matrix (as far as possible), i.e. undo a +* transposition of axes in the intermediate pixel coordinate system. * * Non-standard keyvalues: * ----------------------- @@ -74,8 +86,8 @@ * * Non-standard keywords: * ---------------------- -* The AIPS-convention CROTAn keywords are recognized as quasi-standard and -* as such are accomodated by the wcsprm::crota[] and translated to +* The AIPS-convention CROTAn keywords are recognized as quasi-standard +* and as such are accomodated by wcsprm::crota[] and translated to * wcsprm::pc[][] by wcsset(). These are not dealt with here, nor are any * other non-standard keywords since these routines work only on the contents * of a wcsprm struct and do not deal with FITS headers per se. In @@ -84,15 +96,20 @@ * header parser supplied with WCSLIB, refer to wcshdr.h. * * wcsfix() and wcsfixi() apply all of the corrections handled by the following -* specific functions which may also be invoked separately: +* specific functions, which may also be invoked separately: * * - cdfix(): Sets the diagonal element of the CDi_ja matrix to 1.0 if all * CDi_ja keywords associated with a particular axis are omitted. * * - datfix(): recast an older DATE-OBS date format in dateobs to year-2000 -* standard form and derive mjdobs from it if not already set. -* Alternatively, if mjdobs is set and dateobs isn't, then derive dateobs -* from it. +* standard form. Derive dateref from mjdref if not already set. +* Alternatively, if dateref is set and mjdref isn't, then derive mjdref +* from it. If both are set, then check consistency. Likewise for dateobs +* and mjdobs; datebeg and mjdbeg; dateavg and mjdavg; and dateend and +* mjdend. +* +* - obsfix(): if only one half of obsgeo[] is set, then derive the other +* half from it. If both halves are set, then check consistency. * * - unitfix(): translate some commonly used but non-standard unit strings in * the CUNITia keyvalues, e.g. 'DEG' -> 'deg'. @@ -114,8 +131,8 @@ * * wcsfixi() - Translate a non-standard WCS struct * ----------------------------------------------- -* wcsfix() applies all of the corrections handled separately by cdfix(), -* datfix(), unitfix(), spcfix(), celfix(), and cylfix(). +* wcsfixi() applies all of the corrections handled separately by cdfix(), +* datfix(), obsfix(), unitfix(), spcfix(), celfix(), and cylfix(). * * Given: * ctrl int Do potentially unsafe translations of non-standard @@ -134,15 +151,20 @@ * stat int [NWCSFIX] * Status returns from each of the functions. Use the * preprocessor macros NWCSFIX to dimension this vector -* and CDFIX, DATFIX, UNITFIX, SPCFIX, CELFIX, and CYLFIX -* to access its elements. A status value of -2 is set -* for functions that were not invoked. +* and CDFIX, DATFIX, OBSFIX, UNITFIX, SPCFIX, CELFIX, +* and CYLFIX to access its elements. A status value +* of -2 is set for functions that were not invoked. * * info struct wcserr [NWCSFIX] * Status messages from each of the functions. Use the * preprocessor macros NWCSFIX to dimension this vector -* and CDFIX, DATFIX, UNITFIX, SPCFIX, CELFIX, and CYLFIX -* to access its elements. +* and CDFIX, DATFIX, OBSFIX, UNITFIX, SPCFIX, CELFIX, +* and CYLFIX to access its elements. +* +* Note that the memory allocated by wcsfixi() for the +* message in each wcserr struct (wcserr::msg, if +* non-zero) must be freed by the user. See +* wcsdealloc(). * * Function return value: * int Status return value: @@ -154,11 +176,14 @@ * cdfix() - Fix erroneously omitted CDi_ja keywords * ------------------------------------------------- * cdfix() sets the diagonal element of the CDi_ja matrix to unity if all -* CDi_ja keywords associated with a given axis were omitted. According to +* CDi_ja keywords associated with a given axis were omitted. According to WCS * Paper I, if any CDi_ja keywords at all are given in a FITS header then those * not given default to zero. This results in a singular matrix with an * intersecting row and column of zeros. * +* cdfix() is expected to be invoked before wcsset(), which will fail if these +* errors have not been corrected. +* * Given and returned: * wcs struct wcsprm* * Coordinate transformation parameters. @@ -173,15 +198,30 @@ * datfix() - Translate DATE-OBS and derive MJD-OBS or vice versa * -------------------------------------------------------------- * datfix() translates the old DATE-OBS date format set in wcsprm::dateobs to -* year-2000 standard form (yyyy-mm-ddThh:mm:ss) and derives MJD-OBS from it if -* not already set. Alternatively, if wcsprm::mjdobs is set and -* wcsprm::dateobs isn't, then datfix() derives wcsprm::dateobs from it. If -* both are set but disagree by more than half a day then status 5 is returned. +* year-2000 standard form (yyyy-mm-ddThh:mm:ss). It derives wcsprm::dateref +* from wcsprm::mjdref if not already set. Alternatively, if dateref is set +* and mjdref isn't, then it derives mjdref from it. If both are set but +* disagree by more than 0.001 day (86.4 seconds) then an error status is +* returned. Likewise for wcsprm::dateobs and wcsprm::mjdobs; wcsprm::datebeg +* and wcsprm::mjdbeg; wcsprm::dateavg and wcsprm::mjdavg; and wcsprm::dateend +* and wcsprm::mjdend. +* +* If neither dateobs nor mjdobs are set, but wcsprm::jepoch (primarily) or +* wcsprm::bepoch is, then both are derived from it. If jepoch and/or bepoch +* are set but disagree with dateobs or mjdobs by more than 0.000002 year +* (63.2 seconds), an informative message is produced. +* +* The translations done by datfix() do not affect and are not affected by +* wcsset(). * * Given and returned: * wcs struct wcsprm* -* Coordinate transformation parameters. wcsprm::dateobs -* and/or wcsprm::mjdobs may be changed. +* Coordinate transformation parameters. +* wcsprm::dateref and/or wcsprm::mjdref may be changed. +* wcsprm::dateobs and/or wcsprm::mjdobs may be changed. +* wcsprm::datebeg and/or wcsprm::mjdbeg may be changed. +* wcsprm::dateavg and/or wcsprm::mjdavg may be changed. +* wcsprm::dateend and/or wcsprm::mjdend may be changed. * * Function return value: * int Status return value: @@ -190,13 +230,102 @@ * 1: Null wcsprm pointer passed. * 5: Invalid parameter value. * -* For returns > 1, a detailed error message is set in -* wcsprm::err if enabled, see wcserr_enable(). +* For returns >= 0, a detailed message, whether +* informative or an error message, may be set in +* wcsprm::err if enabled, see wcserr_enable(), with +* wcsprm::err.status set to FIXERR_DATE_FIX. * * Notes: -* The MJD algorithms used by datfix() are from D.A. Hatcher, 1984, QJRAS, -* 25, 53-55, as modified by P.T. Wallace for use in SLALIB subroutines CLDJ -* and DJCL. +* 1: The MJD algorithms used by datfix() are from D.A. Hatcher, 1984, QJRAS, +* 25, 53-55, as modified by P.T. Wallace for use in SLALIB subroutines +* CLDJ and DJCL. +* +* +* obsfix() - complete the OBSGEO-[XYZLBH] vector of observatory coordinates +* ------------------------------------------------------------------------- +* obsfix() completes the wcsprm::obsgeo vector of observatory coordinates. +* That is, if only the (x,y,z) Cartesian coordinate triplet or the (l,b,h) +* geodetic coordinate triplet are set, then it derives the other triplet from +* it. If both triplets are set, then it checks for consistency at the level +* of 1 metre. +* +* The operations done by obsfix() do not affect and are not affected by +* wcsset(). +* +* Given: +* ctrl int Flag that controls behaviour if one triplet is +* defined and the other is only partially defined: +* 0: Reset only the undefined elements of an +* incomplete coordinate triplet. +* 1: Reset all elements of an incomplete triplet. +* 2: Don't make any changes, check for consistency +* only. Returns an error if either of the two +* triplets is incomplete. +* +* Given and returned: +* wcs struct wcsprm* +* Coordinate transformation parameters. +* wcsprm::obsgeo may be changed. +* +* Function return value: +* int Status return value: +* -1: No change required (not an error). +* 0: Success. +* 1: Null wcsprm pointer passed. +* 5: Invalid parameter value. +* +* For returns >= 0, a detailed message, whether +* informative or an error message, may be set in +* wcsprm::err if enabled, see wcserr_enable(), with +* wcsprm::err.status set to FIXERR_OBS_FIX. +* +* Notes: +* 1: While the International Terrestrial Reference System (ITRS) is based +* solely on Cartesian coordinates, it recommends the use of the GRS80 +* ellipsoid in converting to geodetic coordinates. However, while WCS +* Paper III recommends ITRS Cartesian coordinates, Paper VII prescribes +* the use of the IAU(1976) ellipsoid for geodetic coordinates, and +* consequently that is what is used here. +* +* 2: For reference, parameters of commonly used global reference ellipsoids: +* += a (m) 1/f Standard += --------- ------------- -------------------------------- += 6378140 298.2577 IAU(1976) += 6378137 298.257222101 GRS80 += 6378137 298.257223563 WGS84 += 6378136 298.257 IERS(1989) += 6378136.6 298.25642 IERS(2003,2010), IAU(2009/2012) +* +* where f = (a - b) / a is the flattening, and a and b are the semi-major +* and semi-minor radii in metres. +* +* 3: The transformation from geodetic (lng,lat,hgt) to Cartesian (x,y,z) is +* += x = (n + hgt)*coslng*coslat, += y = (n + hgt)*sinlng*coslat, += z = (n*(1.0 - e^2) + hgt)*sinlat, +* +* where the "prime vertical radius", n, is a function of latitude +* += n = a / sqrt(1 - (e*sinlat)^2), +* +* and a, the equatorial radius, and e^2 = (2 - f)*f, the (first) +* eccentricity of the ellipsoid, are constants. obsfix() inverts these +* iteratively by writing +* += x = rho*coslng*coslat, += y = rho*sinlng*coslat, += zeta = rho*sinlat, +* +* where +* += rho = n + hgt, += = sqrt(x^2 + y^2 + zeta^2), += zeta = z / (1 - n*e^2/rho), +* +* and iterating over the value of zeta. Since e is small, a good first +* approximation is given by zeta = z. * * * unitfix() - Correct aberrant CUNITia keyvalues @@ -204,6 +333,9 @@ * unitfix() applies wcsutrn() to translate non-standard CUNITia keyvalues, * e.g. 'DEG' -> 'deg', also stripping off unnecessary whitespace. * +* unitfix() is expected to be invoked before wcsset(), which will fail if +* non-standard CUNITia keyvalues have not been translated. +* * Given: * ctrl int Do potentially unsafe translations described in the * usage notes to wcsutrn(). @@ -218,9 +350,10 @@ * 0: Success (an alias was applied). * 1: Null wcsprm pointer passed. * -* When units are translated (i.e. status 0), status -2 -* is set in the wcserr struct to allow an informative -* message to be returned. +* When units are translated (i.e. 0 is returned), an +* informative message is set in wcsprm::err if enabled, +* see wcserr_enable(), with wcsprm::err.status set to +* FIXERR_UNITS_ALIAS. * * * spcfix() - Translate AIPS-convention spectral types @@ -231,6 +364,16 @@ * * Note that if wcs::specsys is already set then it will not be overridden. * +* AIPS-convention spectral types set in CTYPEia are translated on-the-fly by +* wcsset() but without modifying wcsprm::ctype[] or wcsprm::specsys. That is, +* only the information extracted from wcsprm::ctype[] is translated when +* wcsset() fills in wcsprm::spc (spcprm struct). spcfix() modifies +* wcsprm::ctype[] so that if the header is subsequently written out, e.g. by +* wcshdo(), then it will contain translated CTYPEia keyvalues. +* +* The operations done by spcfix() do not affect and are not affected by +* wcsset(). +* * Given and returned: * wcs struct wcsprm* * Coordinate transformation parameters. wcsprm::ctype[] @@ -250,8 +393,10 @@ * 7: Ill-conditioned coordinate transformation * parameters. * -* For returns > 1, a detailed error message is set in -* wcsprm::err if enabled, see wcserr_enable(). +* For returns >= 0, a detailed message, whether +* informative or an error message, may be set in +* wcsprm::err if enabled, see wcserr_enable(), with +* wcsprm::err.status set to FIXERR_SPC_UPDTE. * * * celfix() - Translate AIPS-convention celestial projection types @@ -259,10 +404,23 @@ * celfix() translates AIPS-convention celestial projection types, NCP and * GLS, set in the ctype[] member of the wcsprm struct. * -* Two additional pv[] keyvalues are created when translating NCP. If the -* pv[] array was initially allocated by wcsini() then the array will be -* expanded if necessary. Otherwise, error 2 will be returned if two empty -* slots are not already available for use. +* Two additional pv[] keyvalues are created when translating NCP, and three +* are created when translating GLS with non-zero reference point. If the pv[] +* array was initially allocated by wcsini() then the array will be expanded if +* necessary. Otherwise, error 2 will be returned if sufficient empty slots +* are not already available for use. +* +* AIPS-convention celestial projection types set in CTYPEia are translated +* on-the-fly by wcsset() but without modifying wcsprm::ctype[], wcsprm::pv[], +* or wcsprm::npv. That is, only the information extracted from +* wcsprm::ctype[] is translated when wcsset() fills in wcsprm::cel (celprm +* struct). celfix() modifies wcsprm::ctype[], wcsprm::pv[], and wcsprm::npv +* so that if the header is subsequently written out, e.g. by wcshdo(), then it +* will contain translated CTYPEia keyvalues and the relevant PVi_ma. +* +* The operations done by celfix() do not affect and are not affected by +* wcsset(). However, it uses information in the wcsprm struct provided by +* wcsset(), and will invoke it if necessary. * * Given and returned: * wcs struct wcsprm* @@ -292,6 +450,10 @@ * cylfix() fixes WCS keyvalues for malformed cylindrical projections that * suffer from the problem described in Sect. 7.3.4 of Paper I. * +* cylfix() requires the wcsprm struct to have been set up by wcsset(), and +* will invoke it if necessary. After modification, the struct is reset on +* return with an explicit call to wcsset(). +* * Given: * naxis const int [] * Image axis lengths. @@ -321,6 +483,98 @@ * wcsprm::err if enabled, see wcserr_enable(). * * +* wcspcx() - regularize PCi_j +* --------------------------- +* wcspcx() "regularizes" the linear transformation matrix component of the +* coordinate transformation (PCi_ja) to make it more human-readable. +* +* Normally, upon encountering a FITS header containing a CDi_ja matrix, +* wcsset() simply treats it as PCi_ja and sets CDELTia to unity. However, +* wcspcx() decomposes CDi_ja into PCi_ja and CDELTia in such a way that +* CDELTia form meaningful scaling parameters. In practice, the residual +* PCi_ja matrix will often then be orthogonal, i.e. unity, or describing a +* pure rotation, axis permutation, or reflection, or a combination thereof. +* +* The decomposition is based on normalizing the length in the transformed +* system (i.e. intermediate pixel coordinates) of the orthonormal basis +* vectors of the pixel coordinate system. This deviates slightly from the +* prescription given by Eq. (4) of WCS Paper I, namely Sum(j=1,N)(PCi_ja)² = 1, +* in replacing the sum over j with the sum over i. Consequently, the columns +* of PCi_ja will consist of unit vectors. In practice, especially in cubes +* and higher dimensional images, at least some pairs of these unit vectors, if +* not all, will often be orthogonal or close to orthogonal. +* +* The sign of CDELTia is chosen to make the PCi_ja matrix as close to the, +* possibly permuted, unit matrix as possible, except that where the coordinate +* description contains a pair of celestial axes, the sign of CDELTia is set +* negative for the longitude axis and positive for the latitude axis. +* +* Optionally, rows of the PCi_ja matrix may also be permuted to diagonalize +* it as far as possible, thus undoing any transposition of axes in the +* intermediate pixel coordinate system. +* +* If the coordinate description contains a celestial plane, then the angle of +* rotation of each of the basis vectors associated with the celestial axes is +* returned. For a pure rotation the two angles should be identical. Any +* difference between them is a measure of axis skewness. +* +* The decomposition is not performed for axes involving a sequent distortion +* function that is defined in terms of CDi_ja, such as TPV, TNX, or ZPX, which +* always are. The independent variables of the polynomial are therefore +* intermediate world coordinates rather than intermediate pixel coordinates. +* Because sequent distortions are always applied before CDELTia, if CDi_ja was +* translated to PCi_ja plus CDELTia, then the distortion would be altered +* unless the polynomial coefficients were also adjusted to account for the +* change of scale. +* +* wcspcx() requires the wcsprm struct to have been set up by wcsset(), and +* will invoke it if necessary. The wcsprm struct is reset on return with an +* explicit call to wcsset(). +* +* Given and returned: +* wcs struct wcsprm* +* Coordinate transformation parameters. +* +* Given: +* dopc int If 1, then PCi_ja and CDELTia, as given, will be +* recomposed according to the above prescription. If 0, +* the operation is restricted to decomposing CDi_ja. +* +* permute int If 1, then after decomposition (or recomposition), +* permute rows of PCi_ja to make the axes of the +* intermediate pixel coordinate system match as closely +* as possible those of the pixel coordinates. That is, +* make it as close to a diagonal matrix as possible. +* However, celestial axes are special in always being +* paired, with the longitude axis preceding the latitude +* axis. +* +* All WCS entities indexed by i, such as CTYPEia, +* CRVALia, CDELTia, etc., including coordinate lookup +* tables, will also be permuted as necessary to account +* for the change to PCi_ja. This does not apply to +* CRPIXja, nor prior distortion functions. These +* operate on pixel coordinates, which are not affected +* by the permutation. +* +* Returned: +* rotn double[2] Rotation angle [deg] of each basis vector associated +* with the celestial axes. For a pure rotation the two +* angles should be identical. Any difference between +* them is a measure of axis skewness. +* +* May be set to the NULL pointer if this information is +* not required. +* +* Function return value: +* int Status return value: +* 0: Success. +* 1: Null wcsprm pointer passed. +* 2: Memory allocation failed. +* 5: CDi_j matrix not used. +* 6: Sequent distortion function present. +* +* * Global variable: const char *wcsfix_errmsg[] - Status return messages * --------------------------------------------------------------------- * Error messages to match the status value returned from each function. @@ -339,38 +593,38 @@ extern "C" { #define CDFIX 0 #define DATFIX 1 -#define UNITFIX 2 -#define SPCFIX 3 -#define CELFIX 4 -#define CYLFIX 5 -#define NWCSFIX 6 +#define OBSFIX 2 +#define UNITFIX 3 +#define SPCFIX 4 +#define CELFIX 5 +#define CYLFIX 6 +#define NWCSFIX 7 extern const char *wcsfix_errmsg[]; #define cylfix_errmsg wcsfix_errmsg enum wcsfix_errmsg_enum { - FIXERR_DATE_FIX = -4, /* The date formatting has been fixed up. */ - FIXERR_SPC_UPDATE = -3, /* Spectral axis type modified. */ - FIXERR_UNITS_ALIAS = -2, /* Units alias translation. */ - FIXERR_NO_CHANGE = -1, /* No change. */ - FIXERR_SUCCESS = 0, /* Success. */ - FIXERR_NULL_POINTER = 1, /* Null wcsprm pointer passed. */ - FIXERR_MEMORY = 2, /* Memory allocation failed. */ - FIXERR_SINGULAR_MTX = 3, /* Linear transformation matrix is - singular. */ - FIXERR_BAD_CTYPE = 4, /* Inconsistent or unrecognized coordinate - axis types. */ - FIXERR_BAD_PARAM = 5, /* Invalid parameter value. */ - FIXERR_BAD_COORD_TRANS = 6, /* Invalid coordinate transformation - parameters. */ - FIXERR_ILL_COORD_TRANS = 7, /* Ill-conditioned coordinate transformation - parameters. */ - FIXERR_BAD_CORNER_PIX = 8, /* All of the corner pixel coordinates are - invalid. */ - FIXERR_NO_REF_PIX_COORD = 9, /* Could not determine reference pixel - coordinate. */ - FIXERR_NO_REF_PIX_VAL = 10 /* Could not determine reference pixel - value. */ + FIXERR_OBSGEO_FIX = -5, // Observatory coordinates amended. + FIXERR_DATE_FIX = -4, // Date string reformatted. + FIXERR_SPC_UPDATE = -3, // Spectral axis type modified. + FIXERR_UNITS_ALIAS = -2, // Units alias translation. + FIXERR_NO_CHANGE = -1, // No change. + FIXERR_SUCCESS = 0, // Success. + FIXERR_NULL_POINTER = 1, // Null wcsprm pointer passed. + FIXERR_MEMORY = 2, // Memory allocation failed. + FIXERR_SINGULAR_MTX = 3, // Linear transformation matrix is singular. + FIXERR_BAD_CTYPE = 4, // Inconsistent or unrecognized coordinate + // axis types. + FIXERR_BAD_PARAM = 5, // Invalid parameter value. + FIXERR_BAD_COORD_TRANS = 6, // Invalid coordinate transformation + // parameters. + FIXERR_ILL_COORD_TRANS = 7, // Ill-conditioned coordinate transformation + // parameters. + FIXERR_BAD_CORNER_PIX = 8, // All of the corner pixel coordinates are + // invalid. + FIXERR_NO_REF_PIX_COORD = 9, // Could not determine reference pixel + // coordinate. + FIXERR_NO_REF_PIX_VAL = 10 // Could not determine reference pixel value. }; int wcsfix(int ctrl, const int naxis[], struct wcsprm *wcs, int stat[]); @@ -382,6 +636,8 @@ int cdfix(struct wcsprm *wcs); int datfix(struct wcsprm *wcs); +int obsfix(int ctrl, struct wcsprm *wcs); + int unitfix(int ctrl, struct wcsprm *wcs); int spcfix(struct wcsprm *wcs); @@ -390,9 +646,11 @@ int celfix(struct wcsprm *wcs); int cylfix(const int naxis[], struct wcsprm *wcs); +int wcspcx(struct wcsprm *wcs, int dopc, int permute, double rotn[2]); + #ifdef __cplusplus } #endif -#endif /* WCSLIB_WCSFIX */ +#endif // WCSLIB_WCSFIX diff --git a/cextern/wcslib/C/wcshdr.c b/cextern/wcslib/C/wcshdr.c index aa687364f39a..9c013267a8ec 100644 --- a/cextern/wcslib/C/wcshdr.c +++ b/cextern/wcslib/C/wcshdr.c @@ -1,7 +1,6 @@ /*============================================================================ - - WCSLIB 4.14 - an implementation of the FITS WCS standard. - Copyright (C) 1995-2012, Mark Calabretta + WCSLIB 8.6 - an implementation of the FITS WCS standard. + Copyright (C) 1995-2026, Mark Calabretta This file is part of WCSLIB. @@ -18,27 +17,27 @@ You should have received a copy of the GNU Lesser General Public License along with WCSLIB. If not, see http://www.gnu.org/licenses. - Direct correspondence concerning WCSLIB to mark@calabretta.id.au - Author: Mark Calabretta, Australia Telescope National Facility, CSIRO. - http://www.atnf.csiro.au/people/Mark.Calabretta - $Id: wcshdr.c,v 4.14 2012/07/13 10:02:27 cal103 Exp $ + http://www.atnf.csiro.au/computing/software/wcs + $Id: wcshdr.c,v 8.6 2026/03/29 13:53:56 mcalabre Exp $ *===========================================================================*/ #include +#include #include #include #include -#include "wcsutil.h" +#include "wcserr.h" #include "wcsmath.h" +#include "wcsutil.h" #include "wcshdr.h" +#include "wtbarr.h" #include "tab.h" +#include "dis.h" #include "wcs.h" -extern const int WCSSET; - -/* Map status return value to message. */ +// Map status return value to message. const char *wcshdr_errmsg[] = { "Success", "Null wcsprm pointer passed", @@ -47,38 +46,55 @@ const char *wcshdr_errmsg[] = { "Fatal error returned by Flex parser", "Invalid tabular parameters"}; -/* Convenience macro for invoking wcserr_set(). */ -#define WCSHDR_ERRMSG(status) WCSERR_SET(status), wcshdr_errmsg[status] +// Map error returns for lower-level routines. +const int wcshdr_taberr[] = { + WCSHDRERR_SUCCESS, // 0: TABERR_SUCCESS + WCSHDRERR_NULL_POINTER, // 1: TABERR_NULL_POINTER + WCSHDRERR_MEMORY, // 2: TABERR_MEMORY + WCSHDRERR_BAD_TABULAR_PARAMS // 3: TABERR_BAD_PARAMS + // 4: TABERR_BAD_X + // 5: TABERR_BAD_WORLD +}; + +static const int WCSSET = 137; // Matching wcs.c + +static const int DIS_DOTPD = 1024; // Matching dis.c +// Internal helper functions, not for general use. +static void wcshdo_format(int, int, const double [], char *); +static void wcshdo_tpdterm(int, int, char *); static void wcshdo_util(int, const char [], const char [], int, const char [], int, int, int, char, int, int [], char [], const char [], int *, char **, int *); -/*--------------------------------------------------------------------------*/ +// Convenience macro for invoking wcserr_set(). +#define WCSHDR_ERRMSG(status) WCSERR_SET(status), wcshdr_errmsg[status] + +//---------------------------------------------------------------------------- int wcstab(struct wcsprm *wcs) { static const char *function = "wcstab"; + // Pointers to allocated memory. char (*PSi_0a)[72] = 0x0, (*PSi_1a)[72] = 0x0, (*PSi_2a)[72] = 0x0; - int *PVi_1a = 0x0, *PVi_2a = 0x0, *PVi_3a = 0x0, *tabax, *tabidx = 0x0; - int getcrd, i, ip, itab, itabax, j, jtabax, m, naxis, ntabax, status; - struct wtbarr *wtbp; - struct tabprm *tabp; - struct wcserr **err; + int *PVi_1a = 0x0, *PVi_2a = 0x0, *PVi_3a = 0x0; + int *tabidx = 0x0; + + int status = 0; if (wcs == 0x0) return WCSHDRERR_NULL_POINTER; - err = &(wcs->err); + struct wcserr **err = &(wcs->err); - /* Free memory previously allocated by wcstab(). */ + // Free memory previously allocated by wcstab(). if (wcs->flag != -1 && wcs->m_flag == WCSSET) { if (wcs->wtb == wcs->m_wtb) wcs->wtb = 0x0; if (wcs->tab == wcs->m_tab) wcs->tab = 0x0; if (wcs->m_wtb) free(wcs->m_wtb); if (wcs->m_tab) { - for (j = 0; j < wcs->ntab; j++) { + for (int j = 0; j < wcs->ntab; j++) { tabfree(wcs->m_tab + j); } @@ -92,15 +108,16 @@ int wcstab(struct wcsprm *wcs) wcs->tab = 0x0; - /* Determine the number of -TAB axes. */ - naxis = wcs->naxis; + // Determine the number of -TAB axes. + int naxis = wcs->naxis; + int *tabax; if (!(tabax = calloc(naxis, sizeof(int)))) { return wcserr_set(WCSHDR_ERRMSG(WCSHDRERR_MEMORY)); } - ntabax = 0; - for (i = 0; i < naxis; i++) { - /* Null fill. */ + int ntabax = 0; + for (int i = 0; i < naxis; i++) { + // Null fill. wcsutil_null_fill(72, wcs->ctype[i]); if (!strcmp(wcs->ctype[i]+4, "-TAB")) { @@ -111,13 +128,12 @@ int wcstab(struct wcsprm *wcs) } if (ntabax == 0) { - /* No lookup tables. */ - status = 0; + // No lookup tables. goto cleanup; } - /* Collect information from the PSi_ma and PVi_ma keyvalues. */ + // Collect information from the PSi_ma and PVi_ma keyvalues. if (!((PSi_0a = calloc(ntabax, sizeof(char[72]))) && (PVi_1a = calloc(ntabax, sizeof(int))) && (PVi_2a = calloc(ntabax, sizeof(int))) && @@ -129,29 +145,29 @@ int wcstab(struct wcsprm *wcs) goto cleanup; } - for (itabax = 0; itabax < ntabax; itabax++) { - /* Remember that calloc() zeroes allocated memory. */ + for (int itabax = 0; itabax < ntabax; itabax++) { + // Remember that calloc() zeroes allocated memory. PVi_1a[itabax] = 1; PVi_2a[itabax] = 1; PVi_3a[itabax] = 1; } - for (ip = 0; ip < wcs->nps; ip++) { - itabax = tabax[wcs->ps[ip].i - 1]; + for (int ip = 0; ip < wcs->nps; ip++) { + int itabax = tabax[wcs->ps[ip].i - 1]; if (itabax >= 0) { switch (wcs->ps[ip].m) { case 0: - /* EXTNAME. */ + // EXTNAME. strcpy(PSi_0a[itabax], wcs->ps[ip].value); wcsutil_null_fill(72, PSi_0a[itabax]); break; case 1: - /* TTYPEn for coordinate array. */ + // TTYPEn for coordinate array. strcpy(PSi_1a[itabax], wcs->ps[ip].value); wcsutil_null_fill(72, PSi_1a[itabax]); break; case 2: - /* TTYPEn for index vector. */ + // TTYPEn for index vector. strcpy(PSi_2a[itabax], wcs->ps[ip].value); wcsutil_null_fill(72, PSi_2a[itabax]); break; @@ -159,20 +175,20 @@ int wcstab(struct wcsprm *wcs) } } - for (ip = 0; ip < wcs->npv; ip++) { - itabax = tabax[wcs->pv[ip].i - 1]; + for (int ip = 0; ip < wcs->npv; ip++) { + int itabax = tabax[wcs->pv[ip].i - 1]; if (itabax >= 0) { switch (wcs->pv[ip].m) { case 1: - /* EXTVER. */ + // EXTVER. PVi_1a[itabax] = (int)(wcs->pv[ip].value + 0.5); break; case 2: - /* EXTLEVEL. */ + // EXTLEVEL. PVi_2a[itabax] = (int)(wcs->pv[ip].value + 0.5); break; case 3: - /* Table axis number. */ + // Table axis number. PVi_3a[itabax] = (int)(wcs->pv[ip].value + 0.5); break; } @@ -180,9 +196,9 @@ int wcstab(struct wcsprm *wcs) } - /* Determine the number of independent tables. */ - for (itabax = 0; itabax < ntabax; itabax++) { - /* These have no defaults. */ + // Determine the number of independent tables. + for (int itabax = 0; itabax < ntabax; itabax++) { + // These have no defaults. if (!PSi_0a[itabax][0] || !PSi_1a[itabax][0]) { status = wcserr_set(WCSERR_SET(WCSHDRERR_BAD_TABULAR_PARAMS), "Invalid tabular parameters: PSi_0a and PSi_1a must be specified"); @@ -190,9 +206,10 @@ int wcstab(struct wcsprm *wcs) } tabidx[itabax] = -1; - for (jtabax = 0; jtabax < i; jtabax++) { - /* EXTNAME, EXTVER, EXTLEVEL, and TTYPEn for the coordinate array */ - /* must match for each axis of a multi-dimensional lookup table. */ + int jtabax; + for (jtabax = 0; jtabax < itabax; jtabax++) { + // EXTNAME, EXTVER, EXTLEVEL, and TTYPEn for the coordinate array + // must match for each axis of a multi-dimensional lookup table. if (strcmp(PSi_0a[itabax], PSi_0a[jtabax]) == 0 && strcmp(PSi_1a[itabax], PSi_1a[jtabax]) == 0 && PVi_1a[itabax] == PVi_1a[jtabax] && @@ -214,43 +231,44 @@ int wcstab(struct wcsprm *wcs) } wcs->m_tab = wcs->tab; - /* Table dimensionality; find the largest axis number. */ - for (itabax = 0; itabax < ntabax; itabax++) { - tabp = wcs->tab + tabidx[itabax]; + // Table dimensionality; find the largest axis number. + for (int itabax = 0; itabax < ntabax; itabax++) { + struct tabprm *tabp = wcs->tab + tabidx[itabax]; - /* PVi_3a records the 1-relative table axis number. */ + // PVi_3a records the 1-relative table axis number. if (PVi_3a[itabax] > tabp->M) { tabp->M = PVi_3a[itabax]; } } - for (itab = 0; itab < wcs->ntab; itab++) { + for (int itab = 0; itab < wcs->ntab; itab++) { if ((status = tabini(1, wcs->tab[itab].M, 0, wcs->tab + itab))) { - if (status == 3) status = 5; - wcserr_set(WCSHDR_ERRMSG(status)); + status = wcserr_set(WCSHDR_ERRMSG(wcshdr_taberr[status])); goto cleanup; } } - /* Copy parameters into the tabprm structs. */ - for (i = 0; i < naxis; i++) { + // Copy parameters into the tabprm structs. + for (int i = 0; i < naxis; i++) { + int itabax; if ((itabax = tabax[i]) < 0) { - /* Not a -TAB axis. */ + // Not a -TAB axis. continue; } - /* PVi_3a records the 1-relative table axis number. */ - m = PVi_3a[itabax] - 1; + // PVi_3a records the 1-relative table axis number. + int m = PVi_3a[itabax] - 1; + struct tabprm *tabp; tabp = wcs->tab + tabidx[itabax]; tabp->map[m] = i; tabp->crval[m] = wcs->crval[i]; } - /* Check for completeness. */ - for (itab = 0; itab < wcs->ntab; itab++) { - for (m = 0; m < wcs->tab[itab].M; m++) { + // Check for completeness. + for (int itab = 0; itab < wcs->ntab; itab++) { + for (int m = 0; m < wcs->tab[itab].M; m++) { if (wcs->tab[itab].map[m] < 0) { status = wcserr_set(WCSERR_SET(WCSHDRERR_BAD_TABULAR_PARAMS), "Invalid tabular parameters: the axis mapping is undefined"); @@ -260,18 +278,18 @@ int wcstab(struct wcsprm *wcs) } - /* Set up for reading the arrays; how many arrays are there? */ - for (itabax = 0; itabax < ntabax; itabax++) { - /* Does this -TAB axis have a non-degenerate index array? */ + // Set up for reading the arrays; how many arrays are there? + for (int itabax = 0; itabax < ntabax; itabax++) { + // Does this -TAB axis have a non-degenerate index array? if (PSi_2a[itabax][0]) { wcs->nwtb++; } } - /* Add one coordinate array for each table. */ + // Add one coordinate array for each table. wcs->nwtb += wcs->ntab; - /* Allocate memory for structs to be returned. */ + // Allocate memory for structs to be returned. if (!(wcs->wtb = calloc(wcs->nwtb, sizeof(struct wtbarr)))) { wcs->nwtb = 0; @@ -280,15 +298,15 @@ int wcstab(struct wcsprm *wcs) } wcs->m_wtb = wcs->wtb; - /* Set pointers for the index and coordinate arrays. */ - wtbp = wcs->wtb; - for (itab = 0; itab < wcs->ntab; itab++) { - getcrd = 1; - for (itabax = 0; itabax < ntabax; itabax++) { + // Set pointers for the index and coordinate arrays. + struct wtbarr *wtbp = wcs->wtb; + for (int itab = 0; itab < wcs->ntab; itab++) { + int getcrd = 1; + for (int itabax = 0; itabax < ntabax; itabax++) { if (tabidx[itabax] != itab) continue; if (getcrd) { - /* Coordinate array. */ + // Coordinate array. wtbp->i = itabax + 1; wtbp->m = PVi_3a[itabax]; wtbp->kind = 'c'; @@ -302,7 +320,7 @@ int wcstab(struct wcsprm *wcs) wtbp->dimlen = wcs->tab[itab].K; wtbp->arrayp = &(wcs->tab[itab].coord); - /* Signal for tabset() to take this memory. */ + // Signal for tabset() to take this memory. wcs->tab[itab].m_coord = (double *)0x1; wtbp++; @@ -310,12 +328,12 @@ int wcstab(struct wcsprm *wcs) } if (PSi_2a[itabax][0]) { - /* Index array. */ + // Index array. wtbp->i = itabax + 1; wtbp->m = PVi_3a[itabax]; wtbp->kind = 'i'; - m = wtbp->m - 1; + int m = wtbp->m - 1; strcpy(wtbp->extnam, PSi_0a[itabax]); wtbp->extver = PVi_1a[itabax]; wtbp->extlev = PVi_2a[itabax]; @@ -325,7 +343,7 @@ int wcstab(struct wcsprm *wcs) wtbp->dimlen = wcs->tab[itab].K + m; wtbp->arrayp = wcs->tab[itab].index + m; - /* Signal for tabset() to take this memory. */ + // Signal for tabset() to take this memory. wcs->tab[itab].m_indxs[m] = (double *)0x1; wtbp++; @@ -333,8 +351,6 @@ int wcstab(struct wcsprm *wcs) } } - status = 0; - cleanup: if (tabax) free(tabax); if (tabidx) free(tabidx); @@ -353,15 +369,12 @@ int wcstab(struct wcsprm *wcs) return status; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- int wcsidx(int nwcs, struct wcsprm **wcs, int alts[27]) { - int a, iwcs; - struct wcsprm *wcsp; - - for (a = 0; a < 27; a++) { + for (int a = 0; a < 27; a++) { alts[a] = -1; } @@ -369,10 +382,11 @@ int wcsidx(int nwcs, struct wcsprm **wcs, int alts[27]) return WCSHDRERR_NULL_POINTER; } - wcsp = *wcs; - for (iwcs = 0; iwcs < nwcs; iwcs++, wcsp++) { + struct wcsprm *wcsp = *wcs; + for (int iwcs = 0; iwcs < nwcs; iwcs++, wcsp++) { if (wcsp->colnum || wcsp->colax[0]) continue; + int a; if (wcsp->alt[0] == ' ') { a = 0; } else { @@ -385,20 +399,16 @@ int wcsidx(int nwcs, struct wcsprm **wcs, int alts[27]) return 0; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- int wcsbdx(int nwcs, struct wcsprm **wcs, int type, short alts[1000][28]) { - short *ip; - int a, i, icol, iwcs; - struct wcsprm *wcsp; - - for (ip = alts[0]; ip < alts[0] + 28*1000; ip++) { + for (short *ip = alts[0]; ip < alts[0] + 28*1000; ip++) { *ip = -1; } - for (icol = 0; icol < 1000; icol++) { + for (int icol = 0; icol < 1000; icol++) { alts[icol][27] = 0; } @@ -406,8 +416,9 @@ int wcsbdx(int nwcs, struct wcsprm **wcs, int type, short alts[1000][28]) return WCSHDRERR_NULL_POINTER; } - wcsp = *wcs; - for (iwcs = 0; iwcs < nwcs; iwcs++, wcsp++) { + struct wcsprm *wcsp = *wcs; + for (int iwcs = 0; iwcs < nwcs; iwcs++, wcsp++) { + int a; if (wcsp->alt[0] == ' ') { a = 0; } else { @@ -415,9 +426,9 @@ int wcsbdx(int nwcs, struct wcsprm **wcs, int type, short alts[1000][28]) } if (type) { - /* Pixel list. */ + // Pixel list. if (wcsp->colax[0]) { - for (i = 0; i < wcsp->naxis; i++) { + for (int i = 0; i < wcsp->naxis; i++) { alts[wcsp->colax[i]][a] = iwcs; alts[wcsp->colax[i]][27]++; } @@ -427,7 +438,7 @@ int wcsbdx(int nwcs, struct wcsprm **wcs, int type, short alts[1000][28]) } } else { - /* Binary table image array. */ + // Binary table image array. if (wcsp->colnum) { alts[wcsp->colnum][a] = iwcs; alts[wcsp->colnum][27]++; @@ -441,20 +452,19 @@ int wcsbdx(int nwcs, struct wcsprm **wcs, int type, short alts[1000][28]) return 0; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- int wcsvfree(int *nwcs, struct wcsprm **wcs) { - int a, status = 0; - struct wcsprm *wcsp; + int status = 0; if (wcs == 0x0) { return WCSHDRERR_NULL_POINTER; } - wcsp = *wcs; - for (a = 0; a < *nwcs; a++, wcsp++) { + struct wcsprm *wcsp = *wcs; + for (int a = 0; a < *nwcs; a++, wcsp++) { status |= wcsfree(wcsp); } @@ -466,147 +476,321 @@ int wcsvfree(int *nwcs, struct wcsprm **wcs) return status; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- +// Matching the definitions in dis.c. +#define I_DTYPE 0 // Distortion type code. +#define I_NIPARM 1 // Full (allocated) length of iparm[]. +#define I_NDPARM 2 // No. of parameters in dparm[], excl. work space. +#define I_TPDNCO 3 // No. of TPD coefficients, forward... +#define I_TPDINV 4 // ...and inverse. +#define I_TPDAUX 5 // True if auxiliary variables are used. +#define I_TPDRAD 6 // True if the radial variable is used. -int wcshdo(int relax, struct wcsprm *wcs, int *nkeyrec, char **header) +int wcshdo(int ctrl, struct wcsprm *wcs, int *nkeyrec, char **header) -/* ::: CUBEFACE and STOKES handling? */ +// ::: CUBEFACE and STOKES handling? { static const char *function = "wcshdo"; - char alt, comment[72], keyvalue[72], keyword[16], obsg[8] = "OBSG?", - obsgeo[8] = "OBSGEO-?", ptype, xtype, xyz[] = "XYZ"; - int bintab, col0, *colax, colnum, i, j, k, naxis, pixlist, primage, - status = 0; - struct wcserr **err; + const char axid[] = "xyxuvu", *cp; + const int nTPD[] = {1, 4, 7, 12, 17, 24, 31, 40, 49, 60}; + + char comment[128], keyvalue[96], keyword[16]; + int status = 0; *nkeyrec = 0; *header = 0x0; if (wcs == 0x0) return WCSHDRERR_NULL_POINTER; - err = &(wcs->err); + struct wcserr **err = &(wcs->err); - if (wcs->flag != WCSSET) { + if (abs(wcs->flag) != WCSSET) { if ((status = wcsset(wcs))) return status; } + int naxis; if ((naxis = wcs->naxis) == 0) { return 0; } - /* These are mainly for convenience. */ - alt = wcs->alt[0]; + // These are mainly for convenience. + char alt = wcs->alt[0]; if (alt == ' ') alt = '\0'; - colnum = wcs->colnum; - colax = wcs->colax; + int colnum = wcs->colnum; + int *colax = wcs->colax; - primage = 0; - bintab = 0; - pixlist = 0; + int primage = 0; + int bintab = 0; + int pixlist = 0; if (colnum) { bintab = 1; - col0 = colnum; } else if (colax[0]) { pixlist = 1; - col0 = colax[0]; } else { primage = 1; } - /* WCS dimension. */ + // Initialize floating point format control. + char format[16]; + *format = '\0'; + if (ctrl & WCSHDO_P17) { + strcpy(format, "% 20.17G"); + } else if (ctrl & WCSHDO_P16) { + strcpy(format, "% 20.16G"); + } else if (ctrl & WCSHDO_P15) { + strcpy(format, "% 20.15G"); + } else if (ctrl & WCSHDO_P14) { + strcpy(format, "% 20.14G"); + } else if (ctrl & WCSHDO_P13) { + strcpy(format, "% 20.13G"); + } else if (ctrl & WCSHDO_P12) { + strcpy(format, "%20.12G"); + } + + if (*format && (ctrl & WCSHDO_EFMT)) { + if (format[6] == 'G') { + format[6] = 'E'; + } else { + format[7] = 'E'; + } + } + + int dofmt = (*format == '\0'); + + + // WCS dimension. if (!pixlist) { sprintf(keyvalue, "%20d", naxis); - wcshdo_util(relax, "WCSAXES", "WCAX", 0, 0x0, 0, 0, 0, alt, colnum, colax, + wcshdo_util(ctrl, "WCSAXES", "WCAX", 0, 0x0, 0, 0, 0, alt, colnum, colax, keyvalue, "Number of coordinate axes", nkeyrec, header, &status); } - /* Reference pixel coordinates. */ - for (j = 0; j < naxis; j++) { - wcsutil_double2str(keyvalue, "%20.12G", wcs->crpix[j]); - wcshdo_util(relax, "CRPIX", "CRP", WCSHDO_CRPXna, "CRPX", 0, j+1, 0, alt, + // Reference pixel coordinates. + if (dofmt) wcshdo_format('G', naxis, wcs->crpix, format); + for (int j = 0; j < naxis; j++) { + wcsutil_double2str(keyvalue, format, wcs->crpix[j]); + wcshdo_util(ctrl, "CRPIX", "CRP", WCSHDO_CRPXna, "CRPX", 0, j+1, 0, alt, colnum, colax, keyvalue, "Pixel coordinate of reference point", nkeyrec, header, &status); } - /* Linear transformation matrix. */ - k = 0; - for (i = 0; i < naxis; i++) { - for (j = 0; j < naxis; j++, k++) { + // Linear transformation matrix. + if (dofmt) wcshdo_format('G', naxis*naxis, wcs->pc, format); + int k = 0; + for (int i = 0; i < naxis; i++) { + for (int j = 0; j < naxis; j++, k++) { if (i == j) { if (wcs->pc[k] == 1.0) continue; } else { if (wcs->pc[k] == 0.0) continue; } - wcsutil_double2str(keyvalue, "%20.12G", wcs->pc[k]); - wcshdo_util(relax, "PC", bintab ? "PC" : "P", WCSHDO_TPCn_ka, + wcsutil_double2str(keyvalue, format, wcs->pc[k]); + wcshdo_util(ctrl, "PC", bintab ? "PC" : "P", WCSHDO_TPCn_ka, bintab ? 0x0 : "PC", i+1, j+1, 0, alt, colnum, colax, keyvalue, "Coordinate transformation matrix element", nkeyrec, header, &status); } } - /* Coordinate increment at reference point. */ - for (i = 0; i < naxis; i++) { - wcsutil_double2str(keyvalue, "%20.12G", wcs->cdelt[i]); + // Coordinate increment at reference point. + if (dofmt) wcshdo_format('G', naxis, wcs->cdelt, format); + for (int i = 0; i < naxis; i++) { + wcsutil_double2str(keyvalue, format, wcs->cdelt[i]); comment[0] = '\0'; if (wcs->cunit[i][0]) sprintf(comment, "[%s] ", wcs->cunit[i]); strcat(comment, "Coordinate increment at reference point"); - wcshdo_util(relax, "CDELT", "CDE", WCSHDO_CRPXna, "CDLT", i+1, 0, 0, alt, + wcshdo_util(ctrl, "CDELT", "CDE", WCSHDO_CRPXna, "CDLT", i+1, 0, 0, alt, colnum, colax, keyvalue, comment, nkeyrec, header, &status); } - /* Units of coordinate increment and reference value. */ - for (i = 0; i < naxis; i++) { + // Units of coordinate increment and reference value. + for (int i = 0; i < naxis; i++) { if (wcs->cunit[i][0] == '\0') continue; sprintf(keyvalue, "'%s'", wcs->cunit[i]); - wcshdo_util(relax, "CUNIT", "CUN", WCSHDO_CRPXna, "CUNI", i+1, 0, 0, alt, + wcshdo_util(ctrl, "CUNIT", "CUN", WCSHDO_CRPXna, "CUNI", i+1, 0, 0, alt, colnum, colax, keyvalue, "Units of coordinate increment and value", nkeyrec, header, &status); } - /* Coordinate type. */ - for (i = 0; i < naxis; i++) { + // May need to alter ctype for particular distortions so do basic checks + // now. Note that SIP, TPV, DSS, TNX, and ZPX are restricted to exactly + // two axes and cannot coexist with other distortion types. + char tpdsrc[24]; + int dosip = 0, dotpd = 0, dotpv = 0; + struct disprm *dis; + + if ((dis = wcs->lin.dispre)) { + for (int i = 0; i < naxis; i++) { + if (strcmp(dis->dtype[i], "SIP") == 0) { + // Simple Imaging Polynomial (SIP). Write it in its native form + // if possible, unless specifically requested to write it as TPD. + dotpd = (dis->iparm[i][I_DTYPE] & DIS_DOTPD); + + if (!dotpd) {; + if (alt || + dis->Nhat[0] != 2 || + dis->Nhat[1] != 2 || + dis->axmap[0][0] != 0 || + dis->axmap[0][1] != 1 || + dis->axmap[1][0] != 0 || + dis->axmap[1][1] != 1 || + dis->offset[0][0] != wcs->crpix[0] || + dis->offset[0][1] != wcs->crpix[1] || + dis->offset[1][0] != wcs->crpix[0] || + dis->offset[1][1] != wcs->crpix[1] || + dis->scale[0][0] != 1.0 || + dis->scale[0][1] != 1.0 || + dis->scale[1][0] != 1.0 || + dis->scale[1][1] != 1.0) { + // Must have been read as a 'SIP' distortion, CPDISja = 'SIP'. + // Cannot be written as native SIP so write it as TPD. + dotpd = DIS_DOTPD; + } else if (strncmp(wcs->ctype[0], "RA---TAN", 8) || + strncmp(wcs->ctype[1], "DEC--TAN", 8)) { + // Must have been permuted by wcssub(). + // Native SIP doesn't have axis mapping so write it as TPD. + dotpd = DIS_DOTPD; + } + + if (dotpd) { + strcpy(tpdsrc, "SIP coordinates"); + } else { + dosip = 1; + } + } + + break; + } + } + } + + if ((dis = wcs->lin.disseq)) { + for (int i = 0; i < naxis; i++) { + if (strcmp(dis->dtype[i], "TPV") == 0) { + // TPV "projection". Write it in its native form if possible, + // unless specifically requested to write it as TPD. + dotpd = (dis->iparm[i][I_DTYPE] & DIS_DOTPD); + + if (!dotpd) {; + if (dis->axmap[wcs->lng][0] != wcs->lng || + dis->axmap[wcs->lng][1] != wcs->lat || + dis->axmap[wcs->lat][0] != wcs->lat || + dis->axmap[wcs->lat][1] != wcs->lng || + dis->offset[wcs->lng][wcs->lng] != 0.0 || + dis->offset[wcs->lng][wcs->lat] != 0.0 || + dis->offset[wcs->lat][wcs->lng] != 0.0 || + dis->offset[wcs->lat][wcs->lat] != 0.0 || + dis->scale[wcs->lng][wcs->lng] != 1.0 || + dis->scale[wcs->lng][wcs->lat] != 1.0 || + dis->scale[wcs->lat][wcs->lng] != 1.0 || + dis->scale[wcs->lat][wcs->lat] != 1.0) { + // Must have been read as a 'TPV' distortion, CPDISja = 'TPV'. + // Cannot be written as native TPV so write it as TPD. + dotpd = DIS_DOTPD; + } + + if (dotpd) { + strcpy(tpdsrc, "TPV \"projection\""); + } else { + dotpv = 1; + } + } + + break; + + } else if (strcmp(dis->dtype[i], "DSS") == 0) { + // Always written as TPD. + dotpd = DIS_DOTPD; + strcpy(tpdsrc, dis->dtype[i]); + + } else if (strncmp(dis->dtype[i], "WAT", 3) == 0) { + // Always written as TPD. + dotpd = DIS_DOTPD; + strcpy(tpdsrc, dis->dtype[i]+4); + + if (strcmp(dis->dtype[i], "DSS") == 0) { + strcpy(tpdsrc, wcs->wcsname); + } else { + strcat(tpdsrc, " \"projection\""); + } + + break; + } + } + } + + // Coordinate type. + for (int i = 0; i < naxis; i++) { if (wcs->ctype[i][0] == '\0') continue; sprintf(keyvalue, "'%s'", wcs->ctype[i]); strcpy(comment, "Coordinate type code"); + + char *ctypei = keyvalue + 1; if (i == wcs->lng || i == wcs->lat) { - if (strncmp(wcs->ctype[i], "RA--", 4) == 0) { - strcpy(comment, "Right ascension, "); - } else if (strncmp(wcs->ctype[i], "DEC-", 4) == 0) { - strcpy(comment, "Declination, "); - } else if (strncmp(wcs->ctype[i]+1, "LON", 3) == 0 || - strncmp(wcs->ctype[i]+1, "LAT", 3) == 0) { - switch (wcs->ctype[i][0]) { - case 'G': - strcpy(comment, "galactic "); - break; - case 'E': - strcpy(comment, "ecliptic "); - case 'H': - strcpy(comment, "helioecliptic "); - case 'S': - strcpy(comment, "supergalactic "); - } + // Alter ctype for particular distortions. + if (dosip) { + // It could have come in as CPDISja = 'SIP'. + strcpy(ctypei+8, "-SIP'"); + } else if (dotpv) { + // Reinstate projection code edited by wcsset(). + strcpy(ctypei+4, "-TPV'"); + } - if (i == wcs->lng) { - strcat(comment, "longitude, "); - } else { - strcat(comment, "latitude, "); + if (strncmp(ctypei+8, "-SIP", 4) == 0) { + strcpy(comment, "TAN (gnomonic) projection + SIP distortions"); + + } else if (strncmp(ctypei+4, "-TPV", 4) == 0) { + strcpy(comment, "TAN (gnomonic) projection + distortions"); + + } else { + if (strncmp(ctypei, "RA--", 4) == 0) { + strcpy(comment, "Right ascension, "); + + } else if (strncmp(ctypei, "DEC-", 4) == 0) { + strcpy(comment, "Declination, "); + + } else if (strncmp(ctypei+1, "LON", 3) == 0 || + strncmp(ctypei+1, "LAT", 3) == 0) { + ctypei[0] = toupper(ctypei[0]); + + switch (ctypei[0]) { + case 'G': + strcpy(comment, "Galactic l"); + break; + case 'E': + strcpy(comment, "Ecliptic l"); + break; + case 'H': + strcpy(comment, "Helioecliptic l"); + break; + case 'S': + strcpy(comment, "Supergalactic l"); + break; + default: + // User-defined coordinate system. + strcpy(comment, "L"); + break; + } + + if (i == wcs->lng) { + strcat(comment, "ongitude, "); + } else { + strcat(comment, "atitude, "); + } } - wcs->ctype[i][0] = toupper(wcs->ctype[i][0]); + strcat(comment, wcs->cel.prj.name); + strcat(comment, " projection"); } - strcat(comment, wcs->cel.prj.name); - strcat(comment, " projection"); - } else if (i == wcs->spec) { + char ptype, xtype; spctyp(wcs->ctype[i], 0x0, 0x0, comment, 0x0, &ptype, &xtype, 0x0); if (ptype == xtype) { strcat(comment, " (linear)"); @@ -625,23 +809,25 @@ int wcshdo(int relax, struct wcsprm *wcs, int *nkeyrec, char **header) } } - wcshdo_util(relax, "CTYPE", "CTY", WCSHDO_CRPXna, "CTYP", i+1, 0, 0, alt, + wcshdo_util(ctrl, "CTYPE", "CTY", WCSHDO_CRPXna, "CTYP", i+1, 0, 0, alt, colnum, colax, keyvalue, comment, nkeyrec, header, &status); } - /* Coordinate value at reference point. */ - for (i = 0; i < naxis; i++) { - wcsutil_double2str(keyvalue, "%20.12G", wcs->crval[i]); + // Coordinate value at reference point. + for (int i = 0; i < naxis; i++) { + if (dofmt) wcshdo_format('G', 1, wcs->crval+i, format); + wcsutil_double2str(keyvalue, format, wcs->crval[i]); comment[0] = '\0'; if (wcs->cunit[i][0]) sprintf(comment, "[%s] ", wcs->cunit[i]); strcat(comment, "Coordinate value at reference point"); - wcshdo_util(relax, "CRVAL", "CRV", WCSHDO_CRPXna, "CRVL", i+1, 0, 0, alt, + wcshdo_util(ctrl, "CRVAL", "CRV", WCSHDO_CRPXna, "CRVL", i+1, 0, 0, alt, colnum, colax, keyvalue, comment, nkeyrec, header, &status); } - /* Parameter values. */ - for (k = 0; k < wcs->npv; k++) { - wcsutil_double2str(keyvalue, "%20.12G", (wcs->pv[k]).value); + // Parameter values. + if (dofmt) strcpy(format, "%20.12G"); + for (int k = 0; k < wcs->npv; k++) { + wcsutil_double2str(keyvalue, format, (wcs->pv[k]).value); if ((wcs->pv[k]).i == (wcs->lng + 1)) { switch ((wcs->pv[k]).m) { case 1: @@ -677,254 +863,1233 @@ int wcshdo(int relax, struct wcsprm *wcs, int *nkeyrec, char **header) strcpy(comment, "Coordinate transformation parameter"); } - wcshdo_util(relax, "PV", "V", WCSHDO_PVn_ma, "PV", wcs->pv[k].i, -1, - wcs->pv[k].m, alt, colnum, colax, keyvalue, comment, - nkeyrec, header, &status); + wcshdo_util(ctrl, "PV", "V", WCSHDO_PVn_ma, "PV", wcs->pv[k].i, -1, + wcs->pv[k].m, alt, colnum, colax, keyvalue, comment, nkeyrec, header, + &status); } - for (k = 0; k < wcs->nps; k++) { + for (int k = 0; k < wcs->nps; k++) { sprintf(keyvalue, "'%s'", (wcs->ps[k]).value); - - wcshdo_util(relax, "PS", "S", WCSHDO_PVn_ma, "PS", wcs->ps[k].i, -1, + wcshdo_util(ctrl, "PS", "S", WCSHDO_PVn_ma, "PS", wcs->ps[k].i, -1, wcs->ps[k].m, alt, colnum, colax, keyvalue, - "Coordinate transformation parameter", - nkeyrec, header, &status); + "Coordinate transformation parameter", nkeyrec, header, &status); } - /* Celestial and spectral transformation parameters. */ + // Celestial and spectral transformation parameters. if (!undefined(wcs->lonpole)) { - wcsutil_double2str(keyvalue, "%20.12G", wcs->lonpole); - wcshdo_util(relax, "LONPOLE", "LONP", 0, 0x0, 0, 0, 0, alt, - colnum, colax, keyvalue, "[deg] Native longitude of celestial pole", - nkeyrec, header, &status); + wcsutil_double2str(keyvalue, format, wcs->lonpole); + wcshdo_util(ctrl, "LONPOLE", "LONP", 0, 0x0, 0, 0, 0, alt, colnum, colax, + keyvalue, "[deg] Native longitude of celestial pole", nkeyrec, header, + &status); } if (!undefined(wcs->latpole)) { - wcsutil_double2str(keyvalue, "%20.12G", wcs->latpole); - wcshdo_util(relax, "LATPOLE", "LATP", 0, 0x0, 0, 0, 0, alt, - colnum, colax, keyvalue, "[deg] Native latitude of celestial pole", - nkeyrec, header, &status); + wcsutil_double2str(keyvalue, format, wcs->latpole); + wcshdo_util(ctrl, "LATPOLE", "LATP", 0, 0x0, 0, 0, 0, alt, colnum, colax, + keyvalue, "[deg] Native latitude of celestial pole", nkeyrec, header, + &status); } - if (!undefined(wcs->restfrq)) { - wcsutil_double2str(keyvalue, "%20.12G", wcs->restfrq); - wcshdo_util(relax, "RESTFRQ", "RFRQ", 0, 0x0, 0, 0, 0, alt, - colnum, colax, keyvalue, "[Hz] Line rest frequency", - nkeyrec, header, &status); + if (wcs->restfrq != 0.0) { + wcsutil_double2str(keyvalue, format, wcs->restfrq); + wcshdo_util(ctrl, "RESTFRQ", "RFRQ", 0, 0x0, 0, 0, 0, alt, colnum, colax, + keyvalue, "[Hz] Line rest frequency", nkeyrec, header, &status); } - if (!undefined(wcs->restwav)) { - wcsutil_double2str(keyvalue, "%20.12G", wcs->restwav); - wcshdo_util(relax, "RESTWAV", "RWAV", 0, 0x0, 0, 0, 0, alt, - colnum, colax, keyvalue, "[Hz] Line rest wavelength", - nkeyrec, header, &status); + if (wcs->restwav != 0.0) { + wcsutil_double2str(keyvalue, format, wcs->restwav); + wcshdo_util(ctrl, "RESTWAV", "RWAV", 0, 0x0, 0, 0, 0, alt, colnum, colax, + keyvalue, "[Hz] Line rest wavelength", nkeyrec, header, &status); } - /* Coordinate system title. */ - if (wcs->wcsname[0]) { - sprintf(keyvalue, "'%s'", wcs->wcsname); - if (bintab) { - wcshdo_util(relax, "WCSNAME", "WCSN", 0, 0x0, 0, 0, 0, alt, - colnum, colax, keyvalue, "Coordinate system title", - nkeyrec, header, &status); - } else { - /* TWCS was a mistake. */ - wcshdo_util(relax, "WCSNAME", "TWCS", WCSHDO_WCSNna, "WCSN", 0, 0, 0, - alt, colnum, colax, keyvalue, "Coordinate system title", - nkeyrec, header, &status); - } - } + // - - - - - - - - - - - - - - - - - Auxiliary coordinate axis information. + char timeunit[16]; + sprintf(timeunit, "%.15s", wcs->timeunit[0] ? wcs->timeunit : "s"); - /* Coordinate axis title. */ + // Coordinate axis title. if (wcs->cname) { - for (i = 0; i < naxis; i++) { + for (int i = 0; i < naxis; i++) { if (wcs->cname[i][0] == '\0') continue; sprintf(keyvalue, "'%s'", wcs->cname[i]); - wcshdo_util(relax, "CNAME", "CNA", WCSHDO_CNAMna, "CNAM", i+1, 0, 0, + wcshdo_util(ctrl, "CNAME", "CNA", WCSHDO_CNAMna, "CNAM", i+1, 0, 0, alt, colnum, colax, keyvalue, "Axis name for labelling purposes", nkeyrec, header, &status); } } - /* Random error in coordinate. */ + // Random error in coordinate. if (wcs->crder) { - for (i = 0; i < naxis; i++) { + for (int i = 0; i < naxis; i++) { if (undefined(wcs->crder[i])) continue; - wcsutil_double2str(keyvalue, "%20.12G", wcs->crder[i]); + wcsutil_double2str(keyvalue, format, wcs->crder[i]); comment[0] = '\0'; if (wcs->cunit[i][0]) sprintf(comment, "[%s] ", wcs->cunit[i]); strcat(comment, "Random error in coordinate"); - wcshdo_util(relax, "CRDER", "CRD", WCSHDO_CNAMna, "CRDE", i+1, 0, 0, + wcshdo_util(ctrl, "CRDER", "CRD", WCSHDO_CNAMna, "CRDE", i+1, 0, 0, alt, colnum, colax, keyvalue, comment, nkeyrec, header, &status); } } - /* Systematic error in coordinate. */ + // Systematic error in coordinate. if (wcs->csyer) { - for (i = 0; i < naxis; i++) { + for (int i = 0; i < naxis; i++) { if (undefined(wcs->csyer[i])) continue; - wcsutil_double2str(keyvalue, "%20.12G", wcs->csyer[i]); + wcsutil_double2str(keyvalue, format, wcs->csyer[i]); comment[0] = '\0'; if (wcs->cunit[i][0]) sprintf(comment, "[%s] ", wcs->cunit[i]); strcat(comment, "Systematic error in coordinate"); - wcshdo_util(relax, "CSYER", "CSY", WCSHDO_CNAMna, "CSYE", i+1, 0, 0, + wcshdo_util(ctrl, "CSYER", "CSY", WCSHDO_CNAMna, "CSYE", i+1, 0, 0, + alt, colnum, colax, keyvalue, comment, nkeyrec, header, &status); + } + } + + // Time at zero point of phase axis. + if (wcs->czphs) { + for (int i = 0; i < naxis; i++) { + if (undefined(wcs->czphs[i])) continue; + + wcsutil_double2str(keyvalue, format, wcs->czphs[i]); + sprintf(comment, "[%s] Time at zero point of phase axis", timeunit); + wcshdo_util(ctrl, "CZPHS", "CZP", WCSHDO_CNAMna, "CZPH", i+1, 0, 0, + alt, colnum, colax, keyvalue, comment, nkeyrec, header, &status); + } + } + + // Period of phase axis. + if (wcs->cperi) { + for (int i = 0; i < naxis; i++) { + if (undefined(wcs->cperi[i])) continue; + + wcsutil_double2str(keyvalue, format, wcs->cperi[i]); + sprintf(comment, "[%s] Period of phase axis", timeunit); + wcshdo_util(ctrl, "CPERI", "CPR", WCSHDO_CNAMna, "CPER", i+1, 0, 0, alt, colnum, colax, keyvalue, comment, nkeyrec, header, &status); } } - /* Equatorial coordinate system type. */ + // - - - - - - - - - - - - - - - - - - - - - - - - Coordinate system title. + + // Coordinate system title. + if (wcs->wcsname[0]) { + sprintf(keyvalue, "'%s'", wcs->wcsname); + if (bintab) { + wcshdo_util(ctrl, "WCSNAME", "WCSN", 0, 0x0, 0, 0, 0, alt, colnum, + colax, keyvalue, "Coordinate system title", nkeyrec, header, &status); + } else { + // TWCS was a mistake. + wcshdo_util(ctrl, "WCSNAME", "TWCS", WCSHDO_WCSNna, "WCSN", 0, 0, 0, + alt, colnum, colax, keyvalue, "Coordinate system title", + nkeyrec, header, &status); + } + } + + // - - - - - - - - - - - - - - - - - Time reference system and measurement. + + // Time scale. + if (wcs->timesys[0]) { + sprintf(keyvalue, "'%s'", wcs->timesys); + wcshdo_util(ctrl, "TIMESYS", 0x0, 0, 0x0, 0, 0, 0, ' ', 0, 0x0, + keyvalue, "Time scale", nkeyrec, header, &status); + } + + // Time reference position. + if (wcs->trefpos[0]) { + sprintf(keyvalue, "'%s'", wcs->trefpos); + wcshdo_util(ctrl, "TREFPOS", 0x0, 0, 0x0, 0, 0, 0, ' ', 0, 0x0, + keyvalue, "Time reference position", nkeyrec, header, &status); + } + + // Time reference direction. + if (wcs->trefdir[0]) { + sprintf(keyvalue, "'%s'", wcs->trefdir); + wcshdo_util(ctrl, "TREFDIR", 0x0, 0, 0x0, 0, 0, 0, ' ', 0, 0x0, + keyvalue, "Time reference direction", nkeyrec, header, &status); + } + + // Ephemerides used for pathlength delay calculation. + if (wcs->plephem[0]) { + sprintf(keyvalue, "'%s'", wcs->plephem); + wcshdo_util(ctrl, "PLEPHEM", 0x0, 0, 0x0, 0, 0, 0, ' ', 0, 0x0, + keyvalue, "Ephemerides used for pathlength delays", nkeyrec, header, + &status); + } + + // Time units. + if (wcs->timeunit[0]) { + sprintf(keyvalue, "'%s'", wcs->timeunit); + wcshdo_util(ctrl, "TIMEUNIT", 0x0, 0, 0x0, 0, 0, 0, ' ', 0, 0x0, + keyvalue, "Time units", nkeyrec, header, &status); + } + + // Fiducial (reference) time. + if (wcs->mjdref[0] == 0.0 && wcs->mjdref[1] == 0.0) { + // MJD of fiducial time (simplified if it takes its default value). + wcsutil_double2str(keyvalue, format, 0.0); + wcshdo_util(ctrl, "MJDREF", 0x0, 0, 0x0, 0, 0, 0, ' ', 0, 0x0, + keyvalue, "[d] MJD of fiducial time", nkeyrec, header, &status); + + } else { + // ISO-8601 fiducial time. + if (wcs->dateref[0]) { + sprintf(keyvalue, "'%s'", wcs->dateref); + wcshdo_util(ctrl, "DATEREF", 0x0, 0, 0x0, 0, 0, 0, ' ', 0, 0x0, + keyvalue, "ISO-8601 fiducial time", nkeyrec, header, &status); + } + + if (wcs->mjdref[1] == 0.0) { + // MJD of fiducial time (no fractional part). + if (!undefined(wcs->mjdref[0])) { + wcsutil_double2str(keyvalue, format, wcs->mjdref[0]); + wcshdo_util(ctrl, "MJDREF", 0x0, 0, 0x0, 0, 0, 0, ' ', 0, 0x0, + keyvalue, "[d] MJD of fiducial time", nkeyrec, header, &status); + } + + } else { + // MJD of fiducial time, integer part. + if (!undefined(wcs->mjdref[0])) { + wcsutil_double2str(keyvalue, format, wcs->mjdref[0]); + wcshdo_util(ctrl, "MJDREFI", 0x0, 0, 0x0, 0, 0, 0, ' ', 0, 0x0, + keyvalue, "[d] MJD of fiducial time, integer part", nkeyrec, + header, &status); + } + + // MJD of fiducial time, fractional part. + if (!undefined(wcs->mjdref[1])) { + wcsutil_double2str(keyvalue, format, wcs->mjdref[1]); + wcshdo_util(ctrl, "MJDREFF", 0x0, 0, 0x0, 0, 0, 0, ' ', 0, 0x0, + keyvalue, "[d] MJD of fiducial time, fractional part", nkeyrec, + header, &status); + } + } + } + + // Clock correction. + if (!undefined(wcs->timeoffs)) { + wcsutil_double2str(keyvalue, format, wcs->timeoffs); + sprintf(comment, "[%s] Clock correction", timeunit); + wcshdo_util(ctrl, "TIMEOFFS", 0x0, 0, 0x0, 0, 0, 0, ' ', 0, 0x0, + keyvalue, comment, nkeyrec, header, &status); + } + + // - - - - - - - - - - - - - - - - - - - - - Data timestamps and durations. + + // ISO-8601 time of observation. + if (wcs->dateobs[0]) { + sprintf(keyvalue, "'%s'", wcs->dateobs); + strcpy(comment, "ISO-8601 time of observation"); + + if (ctrl & 1) { + // Allow DOBSn. + wcshdo_util(ctrl, "DATE-OBS", "DOBS", WCSHDO_DOBSn, 0x0, 0, 0, 0, ' ', + colnum, colax, keyvalue, comment, nkeyrec, header, &status); + } else { + // Force DATE-OBS. + wcshdo_util(ctrl, "DATE-OBS", 0x0, 0, 0x0, 0, 0, 0, ' ', 0, 0x0, + keyvalue, comment, nkeyrec, header, &status); + } + } + + // MJD of observation. + if (!undefined(wcs->mjdobs)) { + wcsutil_double2str(keyvalue, format, wcs->mjdobs); + wcshdo_util(ctrl, "MJD-OBS", "MJDOB", 0, 0x0, 0, 0, 0, ' ', colnum, colax, + keyvalue, "[d] MJD of observation", nkeyrec, header, &status); + } + + // Julian epoch of observation. + if (!undefined(wcs->jepoch)) { + wcsutil_double2str(keyvalue, format, wcs->jepoch); + wcshdo_util(ctrl, "JEPOCH", 0x0, 0, 0x0, 0, 0, 0, ' ', 0, 0x0, + keyvalue, "[a] Julian epoch of observation", nkeyrec, header, &status); + } + + // Besselian epoch of observation. + if (!undefined(wcs->bepoch)) { + wcsutil_double2str(keyvalue, format, wcs->bepoch); + wcshdo_util(ctrl, "BEPOCH", 0x0, 0, 0x0, 0, 0, 0, ' ', 0, 0x0, + keyvalue, "[a] Besselian epoch of observation", nkeyrec, header, + &status); + } + + // ISO-8601 time at start of observation. + if (wcs->datebeg[0]) { + sprintf(keyvalue, "'%s'", wcs->datebeg); + wcshdo_util(ctrl, "DATE-BEG", 0x0, 0, 0x0, 0, 0, 0, ' ', 0, 0x0, + keyvalue, "ISO-8601 time at start of observation", nkeyrec, header, + &status); + } + + // MJD at start of observation. + if (!undefined(wcs->mjdbeg)) { + wcsutil_double2str(keyvalue, format, wcs->mjdbeg); + wcshdo_util(ctrl, "MJD-BEG", 0x0, 0, 0x0, 0, 0, 0, ' ', 0, 0x0, + keyvalue, "[d] MJD at start of observation", nkeyrec, header, &status); + } + + // Time elapsed at start since fiducial time. + if (!undefined(wcs->tstart)) { + wcsutil_double2str(keyvalue, format, wcs->tstart); + sprintf(comment, "[%s] Time elapsed since fiducial time at start", + timeunit); + wcshdo_util(ctrl, "TSTART", 0x0, 0, 0x0, 0, 0, 0, ' ', 0, 0x0, + keyvalue, comment, nkeyrec, header, &status); + } + + // ISO-8601 time at midpoint of observation. + if (wcs->dateavg[0]) { + sprintf(keyvalue, "'%s'", wcs->dateavg); + wcshdo_util(ctrl, "DATE-AVG", "DAVG", 0, 0x0, 0, 0, 0, ' ', colnum, colax, + keyvalue, "ISO-8601 time at midpoint of observation", nkeyrec, header, + &status); + } + + // MJD at midpoint of observation. + if (!undefined(wcs->mjdavg)) { + wcsutil_double2str(keyvalue, format, wcs->mjdavg); + wcshdo_util(ctrl, "MJD-AVG", "MJDA", 0, 0x0, 0, 0, 0, ' ', colnum, colax, + keyvalue, "[d] MJD at midpoint of observation", nkeyrec, header, + &status); + } + + // ISO-8601 time at end of observation. + if (wcs->dateend[0]) { + sprintf(keyvalue, "'%s'", wcs->dateend); + wcshdo_util(ctrl, "DATE-END", 0x0, 0, 0x0, 0, 0, 0, ' ', 0, 0x0, + keyvalue, "ISO-8601 time at end of observation", nkeyrec, header, + &status); + } + + // MJD at end of observation. + if (!undefined(wcs->mjdend)) { + wcsutil_double2str(keyvalue, format, wcs->mjdend); + wcshdo_util(ctrl, "MJD-END", 0x0, 0, 0x0, 0, 0, 0, ' ', 0, 0x0, + keyvalue, "[d] MJD at end of observation", nkeyrec, header, &status); + } + + // Time elapsed at end since fiducial time. + if (!undefined(wcs->tstop)) { + wcsutil_double2str(keyvalue, format, wcs->tstop); + sprintf(comment, "[%s] Time elapsed since fiducial time at end", + timeunit); + wcshdo_util(ctrl, "TSTOP", 0x0, 0, 0x0, 0, 0, 0, ' ', 0, 0x0, + keyvalue, comment, nkeyrec, header, &status); + } + + // Exposure (integration) time. + if (!undefined(wcs->xposure)) { + wcsutil_double2str(keyvalue, format, wcs->xposure); + sprintf(comment, "[%s] Exposure (integration) time", timeunit); + wcshdo_util(ctrl, "XPOSURE", 0x0, 0, 0x0, 0, 0, 0, ' ', 0, 0x0, + keyvalue, comment, nkeyrec, header, &status); + } + + // Elapsed time (start to stop). + if (!undefined(wcs->telapse)) { + wcsutil_double2str(keyvalue, format, wcs->telapse); + sprintf(comment, "[%s] Elapsed time (start to stop)", timeunit); + wcshdo_util(ctrl, "TELAPSE", 0x0, 0, 0x0, 0, 0, 0, ' ', 0, 0x0, + keyvalue, comment, nkeyrec, header, &status); + } + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - Timing accuracy. + + // Systematic error in time measurements. + if (!undefined(wcs->timsyer)) { + wcsutil_double2str(keyvalue, format, wcs->timsyer); + sprintf(comment, "[%s] Systematic error in time measurements", timeunit); + wcshdo_util(ctrl, "TIMSYER", 0x0, 0, 0x0, 0, 0, 0, ' ', 0, 0x0, + keyvalue, comment, nkeyrec, header, &status); + } + + // Relative error in time measurements. + if (!undefined(wcs->timrder)) { + wcsutil_double2str(keyvalue, format, wcs->timrder); + sprintf(comment, "[%s] Relative error in time measurements", timeunit); + wcshdo_util(ctrl, "TIMRDER", 0x0, 0, 0x0, 0, 0, 0, ' ', 0, 0x0, + keyvalue, comment, nkeyrec, header, &status); + } + + // Time resolution. + if (!undefined(wcs->timedel)) { + wcsutil_double2str(keyvalue, format, wcs->timedel); + sprintf(comment, "[%s] Time resolution", timeunit); + wcshdo_util(ctrl, "TIMEDEL", 0x0, 0, 0x0, 0, 0, 0, ' ', 0, 0x0, + keyvalue, comment, nkeyrec, header, &status); + } + + // Reference position of timestamp in binned data. + if (!undefined(wcs->timepixr)) { + wcsutil_double2str(keyvalue, format, wcs->timepixr); + wcshdo_util(ctrl, "TIMEPIXR", 0x0, 0, 0x0, 0, 0, 0, ' ', 0, 0x0, + keyvalue, "Reference position of timestamp in binned data", nkeyrec, + header, &status); + } + + // - - - - - - - - - - - - - - - - - - Spatial & celestial reference frame. + + // Observatory coordinates. + if (!undefined(wcs->obsgeo[0]) && + !undefined(wcs->obsgeo[1]) && + !undefined(wcs->obsgeo[2])) { + + char obsgeo[16] = "OBSGEO-?", obsg[8] = "OBSG?", xyz[] = "XYZ"; + for (int k = 0; k < 3; k++) { + wcsutil_double2str(keyvalue, format, wcs->obsgeo[k]); + sprintf(comment, "[m] observatory %c-coordinate", xyz[k]); + obsgeo[7] = xyz[k]; + obsg[4] = xyz[k]; + wcshdo_util(ctrl, obsgeo, obsg, 0, 0x0, 0, 0, 0, ' ', colnum, colax, + keyvalue, comment, nkeyrec, header, &status); + } + + } else if ( + !undefined(wcs->obsgeo[3]) && + !undefined(wcs->obsgeo[4]) && + !undefined(wcs->obsgeo[5])) { + + wcsutil_double2str(keyvalue, format, wcs->obsgeo[3]); + wcshdo_util(ctrl, "OBSGEO-L", 0x0, 0, 0x0, 0, 0, 0, ' ', 0, 0x0, + keyvalue, "[deg] IAU(1976) observatory longitude", nkeyrec, header, + &status); + + wcsutil_double2str(keyvalue, format, wcs->obsgeo[4]); + wcshdo_util(ctrl, "OBSGEO-B", 0x0, 0, 0x0, 0, 0, 0, ' ', 0, 0x0, + keyvalue, "[deg] IAU(1976) observatory latitude", nkeyrec, header, + &status); + + wcsutil_double2str(keyvalue, format, wcs->obsgeo[5]); + wcshdo_util(ctrl, "OBSGEO-L", 0x0, 0, 0x0, 0, 0, 0, ' ', 0, 0x0, + keyvalue, "[m] IAU(1976) observatory height", nkeyrec, header, + &status); + } + + // Spacecraft orbit ephemeris file. + if (wcs->obsorbit[0]) { + sprintf(keyvalue, "'%s'", wcs->obsorbit); + wcshdo_util(ctrl, "OBSORBIT", 0x0, 0, 0x0, 0, 0, 0, ' ', 0, 0x0, + keyvalue, "Spacecraft orbit ephemeris file", nkeyrec, header, &status); + } + + // Equatorial coordinate system type. if (wcs->radesys[0]) { sprintf(keyvalue, "'%s'", wcs->radesys); - wcshdo_util(relax, "RADESYS", "RADE", 0, 0x0, 0, 0, 0, alt, - colnum, colax, keyvalue, "Equatorial coordinate system", - nkeyrec, header, &status); + wcshdo_util(ctrl, "RADESYS", "RADE", 0, 0x0, 0, 0, 0, alt, colnum, colax, + keyvalue, "Equatorial coordinate system", nkeyrec, header, &status); } - /* Equinox of equatorial coordinate system. */ + // Equinox of equatorial coordinate system. if (!undefined(wcs->equinox)) { - wcsutil_double2str(keyvalue, "%20.12G", wcs->equinox); - wcshdo_util(relax, "EQUINOX", "EQUI", 0, 0x0, 0, 0, 0, alt, - colnum, colax, keyvalue, "[yr] Equinox of equatorial coordinates", - nkeyrec, header, &status); + wcsutil_double2str(keyvalue, format, wcs->equinox); + wcshdo_util(ctrl, "EQUINOX", "EQUI", 0, 0x0, 0, 0, 0, alt, colnum, colax, + keyvalue, "[yr] Equinox of equatorial coordinates", nkeyrec, header, + &status); } - /* Reference frame of spectral coordinates. */ + // Reference frame of spectral coordinates. if (wcs->specsys[0]) { sprintf(keyvalue, "'%s'", wcs->specsys); - wcshdo_util(relax, "SPECSYS", "SPEC", 0, 0x0, 0, 0, 0, alt, - colnum, colax, keyvalue, "Reference frame of spectral coordinates", - nkeyrec, header, &status); + wcshdo_util(ctrl, "SPECSYS", "SPEC", 0, 0x0, 0, 0, 0, alt, colnum, colax, + keyvalue, "Reference frame of spectral coordinates", nkeyrec, header, + &status); } - /* Reference frame of spectral observation. */ + // Reference frame of spectral observation. if (wcs->ssysobs[0]) { sprintf(keyvalue, "'%s'", wcs->ssysobs); - wcshdo_util(relax, "SSYSOBS", "SOBS", 0, 0x0, 0, 0, 0, alt, - colnum, colax, keyvalue, "Reference frame of spectral observation", - nkeyrec, header, &status); + wcshdo_util(ctrl, "SSYSOBS", "SOBS", 0, 0x0, 0, 0, 0, alt, colnum, colax, + keyvalue, "Reference frame of spectral observation", nkeyrec, header, + &status); } - /* Observer's velocity towards source. */ + // Observer's velocity towards source. if (!undefined(wcs->velosys)) { - wcsutil_double2str(keyvalue, "%20.12G", wcs->velosys); - wcshdo_util(relax, "VELOSYS", "VSYS", 0, 0x0, 0, 0, 0, alt, - colnum, colax, keyvalue, "[m/s] Velocity towards source", - nkeyrec, header, &status); + wcsutil_double2str(keyvalue, format, wcs->velosys); + wcshdo_util(ctrl, "VELOSYS", "VSYS", 0, 0x0, 0, 0, 0, alt, colnum, colax, + keyvalue, "[m/s] Velocity towards source", nkeyrec, header, &status); } - /* Reference frame of source redshift. */ + // Redshift of the source. + if (!undefined(wcs->zsource)) { + wcsutil_double2str(keyvalue, format, wcs->zsource); + wcshdo_util(ctrl, "ZSOURCE", "ZSOU", 0, 0x0, 0, 0, 0, alt, colnum, colax, + keyvalue, "Redshift of the source", nkeyrec, header, &status); + } + + // Reference frame of source redshift. if (wcs->ssyssrc[0]) { sprintf(keyvalue, "'%s'", wcs->ssyssrc); - wcshdo_util(relax, "SSYSSRC", "SSRC", 0, 0x0, 0, 0, 0, alt, - colnum, colax, keyvalue, "Reference frame of source redshift", - nkeyrec, header, &status); + wcshdo_util(ctrl, "SSYSSRC", "SSRC", 0, 0x0, 0, 0, 0, alt, colnum, colax, + keyvalue, "Reference frame of source redshift", nkeyrec, header, + &status); } - /* Redshift of the source. */ - if (!undefined(wcs->zsource)) { - wcsutil_double2str(keyvalue, "%20.12G", wcs->zsource); - wcshdo_util(relax, "ZSOURCE", "ZSOU", 0, 0x0, 0, 0, 0, alt, - colnum, colax, keyvalue, "Redshift of the source", - nkeyrec, header, &status); + // Velocity orientation angle. + if (!undefined(wcs->velangl)) { + wcsutil_double2str(keyvalue, format, wcs->velangl); + wcshdo_util(ctrl, "VELANGL", "VANG", 0, 0x0, 0, 0, 0, alt, colnum, colax, + keyvalue, "[deg] Velocity orientation angle", nkeyrec, header, &status); } - /* Observatory coordinates. */ - for (k = 0; k < 3; k++) { - if (undefined(wcs->obsgeo[k])) continue; + // - - - - - - - - - - - - - - - - - - - - Additional auxiliary parameters. - wcsutil_double2str(keyvalue, "%20.12G", wcs->obsgeo[k]); - sprintf(comment, "[m] ITRF observatory %c-coordinate", xyz[k]); - obsgeo[7] = xyz[k]; - obsg[4] = xyz[k]; - wcshdo_util(relax, obsgeo, obsg, 0, 0x0, 0, 0, 0, ' ', - colnum, colax, keyvalue, comment, nkeyrec, header, &status); - } + struct auxprm *aux; + if ((aux = wcs->aux)) { + if (!undefined(aux->rsun_ref)) { + wcsutil_double2str(keyvalue, format, aux->rsun_ref); + wcshdo_util(ctrl, "RSUN_REF", 0x0, 0, 0x0, 0, 0, 0, ' ', 0, 0x0, + keyvalue, "[m] Solar radius", nkeyrec, header, &status); + } - /* MJD of observation. */ - if (!undefined(wcs->mjdobs)) { - wcsutil_double2str(keyvalue, "%20.12G", wcs->mjdobs); + if (!undefined(aux->dsun_obs)) { + wcsutil_double2str(keyvalue, format, aux->dsun_obs); + wcshdo_util(ctrl, "DSUN_OBS", 0x0, 0, 0x0, 0, 0, 0, ' ', 0, 0x0, + keyvalue, "[m] Distance from centre of Sun to observer", nkeyrec, + header, &status); + } - strcpy(comment, "[d] MJD of observation"); - if (wcs->dateobs[0]) { - if (primage || (relax & 1) == 0) { - sprintf(comment+22, " matching DATE-OBS"); - } else { - sprintf(comment+22, " matching DOBS%d", col0); + if (!undefined(aux->crln_obs)) { + wcsutil_double2str(keyvalue, format, aux->crln_obs); + wcshdo_util(ctrl, "CRLN_OBS", 0x0, 0, 0x0, 0, 0, 0, ' ', 0, 0x0, + keyvalue, "[deg] Carrington heliographic lng of observer", nkeyrec, + header, &status); + + if (!undefined(aux->hglt_obs)) { + wcsutil_double2str(keyvalue, format, aux->hglt_obs); + wcshdo_util(ctrl, "CRLT_OBS", 0x0, 0, 0x0, 0, 0, 0, ' ', 0, 0x0, + keyvalue, "[deg] Heliographic latitude of observer", nkeyrec, + header, &status); } } - wcshdo_util(relax, "MJD-OBS", "MJDOB", 0, 0x0, 0, 0, 0, ' ', - colnum, colax, keyvalue, comment, nkeyrec, header, &status); + if (!undefined(aux->hgln_obs)) { + wcsutil_double2str(keyvalue, format, aux->hgln_obs); + wcshdo_util(ctrl, "HGLN_OBS", 0x0, 0, 0x0, 0, 0, 0, ' ', 0, 0x0, + keyvalue, "[deg] Stonyhurst heliographic lng of observer", nkeyrec, + header, &status); + + if (!undefined(aux->hglt_obs)) { + wcsutil_double2str(keyvalue, format, aux->hglt_obs); + wcshdo_util(ctrl, "HGLT_OBS", 0x0, 0, 0x0, 0, 0, 0, ' ', 0, 0x0, + keyvalue, "[deg] Heliographic latitude of observer", nkeyrec, + header, &status); + } + } + + if (!undefined(aux->a_radius)) { + wcsutil_double2str(keyvalue, format, aux->a_radius); + wcshdo_util(ctrl, "A_RADIUS", 0x0, 0, 0x0, 0, 0, 0, ' ', 0, 0x0, + keyvalue, "[m] Object ellipsoid semi-major axis", nkeyrec, + header, &status); + } + + if (!undefined(aux->b_radius)) { + wcsutil_double2str(keyvalue, format, aux->b_radius); + wcshdo_util(ctrl, "B_RADIUS", 0x0, 0, 0x0, 0, 0, 0, ' ', 0, 0x0, + keyvalue, "[m] Object ellipsoid semi-intermediate axis", nkeyrec, + header, &status); + } + + if (!undefined(aux->c_radius)) { + wcsutil_double2str(keyvalue, format, aux->c_radius); + wcshdo_util(ctrl, "C_RADIUS", 0x0, 0, 0x0, 0, 0, 0, ' ', 0, 0x0, + keyvalue, "[m] Object ellipsoid semi-minor axis", nkeyrec, + header, &status); + } + + if (!undefined(aux->blon_obs)) { + wcsutil_double2str(keyvalue, format, aux->blon_obs); + wcshdo_util(ctrl, "BLON_OBS", 0x0, 0, 0x0, 0, 0, 0, ' ', 0, 0x0, + keyvalue, "[deg] Bodycentric longitude of observer", nkeyrec, + header, &status); + } + + if (!undefined(aux->blat_obs)) { + wcsutil_double2str(keyvalue, format, aux->blat_obs); + wcshdo_util(ctrl, "BLAT_OBS", 0x0, 0, 0x0, 0, 0, 0, ' ', 0, 0x0, + keyvalue, "[deg] Bodycentric latitude of observer", nkeyrec, + header, &status); + } + + if (!undefined(aux->bdis_obs)) { + wcsutil_double2str(keyvalue, format, aux->bdis_obs); + wcshdo_util(ctrl, "BDIS_OBS", 0x0, 0, 0x0, 0, 0, 0, ' ', 0, 0x0, + keyvalue, "[m] Bodycentric distance of observer", nkeyrec, + header, &status); + } } - /* MJD mid-observation time. */ - if (!undefined(wcs->mjdavg)) { - wcsutil_double2str(keyvalue, "%20.12G", wcs->mjdavg); + // - - - - - - - - - - - - - - - - - - - - - Distortion function parameters. + + char term[16]; + double *dparm, keyval; + + if (dosip) { + // Simple Imaging Polynomial (SIP) is handled by translating its dpkey + // records. Determine a suitable numerical precision for the + // polynomial coefficients to avoid trailing zeroes common to all of + // them. + dis = wcs->lin.dispre; + if (dofmt) { + struct dpkey *ikeyp = dis->dp; + int kp0 = 2; + for (int idp = 0; idp < dis->ndp; idp++, ikeyp++) { + cp = strchr(ikeyp->field, '.') + 1; + if (strncmp(cp, "SIP.", 4) != 0) continue; + wcsutil_double2str(keyvalue, "%20.13E", dpkeyd(ikeyp)); + + int kpi = 15; + while (kp0 < kpi && keyvalue[kpi] == '0') kpi--; + kp0 = kpi; + } - strcpy(comment, "[d] MJD mid-observation"); - if (wcs->dateavg[0]) { - if (primage) { - sprintf(comment+23, " matching DATE-AVG"); + int precision = kp0 - 2; + if (precision < 1) precision = 1; + if (13 < precision) precision = 13; + sprintf(format, "%%20.%dE", precision); + } + + // Ensure the coefficients are written in a human-readable sequence. + for (int j = 0; j <= 1; j++) { + // Distortion function polynomial coefficients. + wcshdo_util(ctrl, "", 0x0, 0, 0x0, 0, 0, 0, ' ', 0, 0x0, + "", "", nkeyrec, header, &status); + + if (j == 0) { + strcpy(keyword, "A_"); } else { - sprintf(comment+23, " matching DAVG%d", col0); + strcpy(keyword, "B_"); } - } - wcshdo_util(relax, "MJD-AVG", "MJDA", 0, 0x0, 0, 0, 0, ' ', - colnum, colax, keyvalue, comment, nkeyrec, header, &status); - } + int degree; + int ncoeff = dis->iparm[j][I_TPDNCO]; + for (degree = 0; degree <= 9; degree++) { + if (ncoeff <= nTPD[degree]) break; + } - /* ISO-8601 date corresponding to MJD-OBS. */ - if (wcs->dateobs[0]) { - sprintf(keyvalue, "'%s'", wcs->dateobs); + strcpy(keyword+2, "ORDER"); + sprintf(keyvalue, "%20d", degree); + sprintf(comment, "SIP polynomial degree, axis %d, pixel-to-sky", j+1); + wcshdo_util(ctrl, keyword, 0x0, 0, 0x0, 0, 0, 0, ' ', 0, 0x0, + keyvalue, comment, nkeyrec, header, &status); + + struct dpkey *ikeyp = dis->dp; + for (int idp = 0; idp < dis->ndp; idp++, ikeyp++) { + if (ikeyp->j != j+1) continue; + if ((keyval = dpkeyd(ikeyp)) == 0.0) continue; + + cp = strchr(ikeyp->field, '.') + 1; + if (strncmp(cp, "SIP.FWD.", 8) != 0) continue; + cp += 8; + strcpy(keyword+2, cp); + int p, q; + sscanf(cp, "%d_%d", &p, &q); + strncpy(term, "xxxxxxxxx", p); + strncpy(term+p, "yyyyyyyyy", q); + term[p+q] = '\0'; + + wcsutil_double2str(keyvalue, format, keyval); + sprintf(comment, "SIP distortion coefficient: %s", term); + wcshdo_util(ctrl, keyword, 0x0, 0, 0x0, 0, 0, 0, ' ', 0, 0x0, + keyvalue, comment, nkeyrec, header, &status); + } - strcpy(comment, "ISO-8601 observation date"); - if (!undefined(wcs->mjdobs)) { - if (primage) { - sprintf(comment+25, " matching MJD-OBS"); + if (dis->maxdis[j] != 0.0) { + strcpy(keyword+2, "DMAX"); + wcsutil_double2str(keyvalue, "%20.3f", dis->maxdis[j]); + wcshdo_util(ctrl, keyword, 0x0, 0, 0x0, 0, 0, 0, ' ', 0, 0x0, + keyvalue, "Maximum value of distortion function", nkeyrec, + header, &status); + } + + // Inverse distortion function polynomial coefficients. + if (dis->disx2p == 0x0) continue; + + wcshdo_util(ctrl, "", 0x0, 0, 0x0, 0, 0, 0, ' ', 0, 0x0, + "", "", nkeyrec, header, &status); + + if (j == 0) { + strcpy(keyword, "AP_"); } else { - sprintf(comment+25, " matching MJDOB%d", col0); + strcpy(keyword, "BP_"); } - } - if (relax & 1) { - /* Allow DOBSn. */ - wcshdo_util(relax, "DATE-OBS", "DOBS", WCSHDO_DOBSn, 0x0, 0, 0, 0, - ' ', colnum, colax, keyvalue, comment, nkeyrec, header, &status); - } else { - /* Force DATE-OBS. */ - wcshdo_util(relax, "DATE-OBS", 0x0, 0, 0x0, 0, 0, 0, ' ', 0, - 0x0, keyvalue, comment, nkeyrec, header, &status); + ncoeff = dis->iparm[j][I_NDPARM] - dis->iparm[j][I_TPDNCO]; + for (degree = 0; degree <= 9; degree++) { + if (ncoeff <= nTPD[degree]) break; + } + + strcpy(keyword+3, "ORDER"); + sprintf(keyvalue, "%20d", degree); + sprintf(comment, "SIP polynomial degree, axis %d, sky-to-pixel", j+1); + wcshdo_util(ctrl, keyword, 0x0, 0, 0x0, 0, 0, 0, ' ', 0, 0x0, + keyvalue, comment, nkeyrec, header, &status); + + ikeyp = dis->dp; + for (int idp = 0; idp < dis->ndp; idp++, ikeyp++) { + if (ikeyp->j != j+1) continue; + if ((keyval = dpkeyd(ikeyp)) == 0.0) continue; + + cp = strchr(ikeyp->field, '.') + 1; + if (strncmp(cp, "SIP.REV.", 8) != 0) continue; + cp += 8; + strcpy(keyword+3, cp); + int p, q; + sscanf(cp, "%d_%d", &p, &q); + strncpy(term, "xxxxxxxxx", p); + strncpy(term+p, "yyyyyyyyy", q); + term[p+q] = '\0'; + + wcsutil_double2str(keyvalue, format, keyval); + sprintf(comment, "SIP inverse coefficient: %s", term); + wcshdo_util(ctrl, keyword, 0x0, 0, 0x0, 0, 0, 0, ' ', 0, 0x0, + keyvalue, comment, nkeyrec, header, &status); + } } } - /* ISO-8601 date corresponding to MJD-OBS. */ - if (wcs->dateavg[0]) { - sprintf(keyvalue, "'%s'", wcs->dateavg); + for (int idis = 0; idis < 2; idis++) { + if (idis == 0 && (dis = wcs->lin.dispre) == 0x0) continue; + if (idis == 1 && (dis = wcs->lin.disseq) == 0x0) continue; + + for (int j = 0; j < naxis; j++) { + if (dis->disp2x[j] == 0x0) continue; + + int *iparm = dis->iparm[j]; + dparm = dis->dparm[j]; + + // Identify the distortion type. + if (dotpv) { + // TPV "projection" is handled by translating its dpkey records, + // which were originally translated from PVi_ma by wcsset(), or + // possibly input directly as a CQDISia = 'TPV' distortion type. + // Determine a suitable numerical precision for the polynomial + // coefficients to avoid trailing zeroes common to all of them. + if (dofmt) wcshdo_format('E', iparm[I_NDPARM], dparm, format); + char fmt01[8]; + sprintf(fmt01, "%.3ss", format); + + wcshdo_util(ctrl, "", 0x0, 0, 0x0, 0, 0, 0, ' ', 0, 0x0, + "", "", nkeyrec, header, &status); + + // Distortion function polynomial coefficients. + sprintf(keyword, "PV%d_", j+1); + char *kp = keyword + strlen(keyword); + + struct dpkey *ikeyp = dis->dp; + for (int idp = 0; idp < dis->ndp; idp++, ikeyp++) { + if (ikeyp->j != j+1) continue; + if ((keyval = dpkeyd(ikeyp)) == 0.0) continue; + + cp = strchr(ikeyp->field, '.') + 1; + if (strncmp(cp, "TPV.", 4) != 0) continue; + strcpy(kp, cp+4); + + // Identify the term of the TPV polynomial for human readers. + int m; + sscanf(cp+4, "%d", &m); + wcshdo_tpdterm(m, j == wcs->lng, term); + sprintf(comment, "TPV coefficient: %s", term); + + if (keyval == 1.0) { + sprintf(keyvalue, fmt01, "1.0"); + } else { + wcsutil_double2str(keyvalue, format, keyval); + } + wcshdo_util(ctrl, keyword, 0x0, 0, 0x0, 0, 0, 0, alt, 0, 0x0, + keyvalue, comment, nkeyrec, header, &status); + } - strcpy(comment, "ISO-8601 mid-observation date"); - if (!undefined(wcs->mjdavg)) { - if (primage) { - sprintf(comment+29, " matching MJD-AVG"); - } else { - sprintf(comment+29, " matching MJDA%d", col0); + } else if (strcmp(dis->dtype[j], "TPD") == 0 || dotpd || + strcmp(dis->dtype[j], "Polynomial") == 0 || + strcmp(dis->dtype[j], "Polynomial*") == 0) { + // One of the Paper IV type polynomial distortions. + wcshdo_util(ctrl, "", 0x0, 0, 0x0, 0, 0, 0, ' ', 0, 0x0, + "", "", nkeyrec, header, &status); + + if (strcmp(dis->dtype[j], "TPD") == 0) { + // Pure TPD. + dotpd = 1; + } else if (strncmp(dis->dtype[j], "Polynomial", 10) == 0) { + // Polynomial distortion. Write it as TPD by request? + dotpd = (iparm[I_DTYPE] & DIS_DOTPD); + strcpy(tpdsrc, "Polynomial distortion"); + } + + char pq = idis ? 'Q' : 'P'; + int Nhat = dis->Nhat[j]; + + // CPDISja/CQDISia + sprintf(keyword, "C%cDIS%d", pq, j+1); + if (idis == 0) { + strcpy(comment, "P = prior, "); + } else { + strcpy(comment, "Q = sequent, "); + } + + int direct = 0, doaux = 0; + if (dotpd) { + strcpy(keyvalue, "'TPD'"); + strcat(comment, "Template Polynomial Distortion"); + + // For identifying terms of the TPD polynomial. + int *axmap = dis->axmap[j]; + direct = 1; + doaux = iparm[I_TPDAUX]; + if (Nhat == 2) { + // Associate x with longitude, y with latitude. + if (axmap[0] == wcs->lng && axmap[1] == wcs->lat) { + direct = 1; + } else if (axmap[0] == wcs->lat && axmap[1] == wcs->lng) { + direct = 0; + } else { + // Non-celestial. + direct = (axmap[0] < axmap[1]); + } + } + } else { + strcpy(keyvalue, "'Polynomial'"); + strcat(comment, "general Polynomial distortion"); + } + + wcshdo_util(ctrl, keyword, 0x0, 0, 0x0, 0, 0, 0, alt, 0, 0x0, + keyvalue, comment, nkeyrec, header, &status); + + // NAXES. + sprintf(keyword, "D%c%d", pq, j+1); + sprintf(keyvalue, "'NAXES: %d'", Nhat); + if (Nhat == 1) { + strcpy(comment, "One independent variable"); + } else if (Nhat == 2) { + strcpy(comment, "Two independent variables"); + } else { + strcpy(comment, "Number of independent variables"); + } + wcshdo_util(ctrl, keyword, 0x0, 0, 0x0, 0, 0, 0, alt, 0, 0x0, + keyvalue, comment, nkeyrec, header, &status); + + // AXIS.jhat + for (int jhat = 0; jhat < Nhat; jhat++) { + int *axmap = dis->axmap[j]; + sprintf(keyvalue, "'AXIS.%d: %d'", jhat+1, axmap[jhat]+1); + if (jhat == 0) { + strcpy(comment, "1st"); + } else if (jhat == 1) { + strcpy(comment, "2nd"); + } else if (jhat == 2) { + strcpy(comment, "3rd"); + } else { + sprintf(comment, "%dth", jhat+1); + } + + sprintf(comment+strlen(comment), " independent variable: axis %d", + axmap[jhat]+1); + if (dotpd) { + // axid is "xyxuvu". + cp = axid; + if (!direct) cp++; + if (doaux) cp += 3; + sprintf(comment+strlen(comment), " (= %c)", cp[jhat]); + } + + wcshdo_util(ctrl, keyword, 0x0, 0, 0x0, 0, 0, 0, alt, 0, 0x0, + keyvalue, comment, nkeyrec, header, &status); + } + + char ctemp[32]; + + // OFFSET.jhat + if (dofmt) wcshdo_format('f', Nhat, dis->offset[j], format); + for (int jhat = 0; jhat < Nhat; jhat++) { + if (dis->offset[j][jhat] == 0.0) continue; + + wcsutil_double2str(ctemp, format, dis->offset[j][jhat]); + sprintf(keyvalue, "'OFFSET.%d: %s'", jhat+1, ctemp); + sprintf(comment, "Variable %d renormalization offset", jhat+1); + + wcshdo_util(ctrl, keyword, 0x0, 0, 0x0, 0, 0, 0, alt, 0, 0x0, + keyvalue, comment, nkeyrec, header, &status); + } + + // SCALE.jhat + if (dofmt) wcshdo_format('f', Nhat, dis->scale[j], format); + for (int jhat = 0; jhat < Nhat; jhat++) { + if (dis->scale[j][jhat] == 1.0) continue; + + wcsutil_double2str(ctemp, format, dis->scale[j][jhat]); + sprintf(keyvalue, "'SCALE.%d: %s'", jhat+1, ctemp); + sprintf(comment, "Variable %d renormalization scale", jhat+1); + + wcshdo_util(ctrl, keyword, 0x0, 0, 0x0, 0, 0, 0, alt, 0, 0x0, + keyvalue, comment, nkeyrec, header, &status); + } + + // Does the distortion function compute a correction? + if (dis->docorr[j]) { + wcshdo_util(ctrl, keyword, 0x0, 0, 0x0, 0, 0, 0, alt, 0, 0x0, + "'DOCORR: 1'", "Distortion function computes a correction", + nkeyrec, header, &status); + } else { + wcshdo_util(ctrl, keyword, 0x0, 0, 0x0, 0, 0, 0, alt, 0, 0x0, + "'DOCORR: 0'", "Distortion function computes coordinates", + nkeyrec, header, &status); + } + + if (dotpd) { + // Template Polynomial Distortion (TPD). As it may have been + // translated from SIP, TPV, DSS, TNX, ZPX, or perhaps + // Polynomial, the dpkey records may not relate to TPD. + // Output is therefore handled via dparm. + if (dofmt) wcshdo_format('E', iparm[I_NDPARM], dparm, format); + char fmt01[8]; + sprintf(fmt01, "%.3ss", format); + + // AUX.jhat.COEFF.m + if (doaux) { + for (int idp = 0; idp < 6; idp++) { + if (dparm[idp] == 0.0) { + sprintf(ctemp, fmt01, "0.0"); + } else if (dparm[idp] == 1.0) { + sprintf(ctemp, fmt01, "1.0"); + } else { + wcsutil_double2str(ctemp, format, dparm[idp]); + } + + if (idp < 3) { + sprintf(keyvalue, "'AUX.1.COEFF.%d: %s'", idp%3, ctemp); + sprintf(comment, "TPD: x = c0 + c1*u + c2*v"); + } else { + sprintf(keyvalue, "'AUX.2.COEFF.%d: %s'", idp%3, ctemp); + sprintf(comment, "TPD: y = d0 + d1*u + d2*v"); + } + + wcshdo_util(ctrl, keyword, 0x0, 0, 0x0, 0, 0, 0, alt, 0, 0x0, + keyvalue, comment, nkeyrec, header, &status); + + } + + dparm += 6; + } + + // TPD.FWD.m + for (int idp = 0; idp < iparm[I_TPDNCO]; idp++) { + if (dparm[idp] == 0.0) continue; + + if (dparm[idp] == 1.0) { + sprintf(ctemp, fmt01, "1.0"); + } else { + wcsutil_double2str(ctemp, format, dparm[idp]); + } + + int m = idp; + sprintf(keyvalue, "'TPD.FWD.%d:%s %s'", m, (m<10)?" ":"", ctemp); + wcshdo_tpdterm(m, direct, term); + sprintf(comment, "TPD coefficient: %s", term); + + wcshdo_util(ctrl, keyword, 0x0, 0, 0x0, 0, 0, 0, alt, 0, 0x0, + keyvalue, comment, nkeyrec, header, &status); + } + + // CPERRja/CQERRia + if (dis->maxdis[j] != 0.0) { + sprintf(keyword, "C%cERR%d", pq, j+1); + sprintf(keyvalue, "%20.2f", dis->maxdis[j]); + sprintf(comment, "%sMaximum absolute value of distortion", + idis?"":"[pix] "); + wcshdo_util(ctrl, keyword, 0x0, 0, 0x0, 0, 0, 0, alt, 0, 0x0, + keyvalue, comment, nkeyrec, header, &status); + } + + // Inverse distortion function polynomial coefficients. + if (dis->disx2p[j] == 0x0) continue; + + wcshdo_util(ctrl, "", 0x0, 0, 0x0, 0, 0, 0, ' ', 0, 0x0, + "", "", nkeyrec, header, &status); + + // TPD.REV.m + sprintf(keyword, "D%c%d", pq, j+1); + for (int idp = iparm[I_TPDNCO]; idp < iparm[I_NDPARM]; idp++) { + if (dparm[idp] == 0.0) continue; + + wcsutil_double2str(ctemp, format, dparm[idp]); + int m = idp - iparm[I_TPDNCO]; + sprintf(keyvalue, "'TPD.REV.%d:%s %s'", m, (m<10)?" ":"", ctemp); + wcshdo_tpdterm(m, direct, term); + sprintf(comment, "TPD coefficient: %s", term); + + wcshdo_util(ctrl, keyword, 0x0, 0, 0x0, 0, 0, 0, alt, 0, 0x0, + keyvalue, comment, nkeyrec, header, &status); + } + + } else { + // General polynomial distortion, handled via its dpkey records + // since iparm and dparm may hold a translation to TPD. + + // Do auxiliary variables first. + struct dpkey *ikeyp = dis->dp; + for (int idp = 0; idp < dis->ndp; idp++, ikeyp++) { + if (ikeyp->j != j+1) continue; + + cp = strchr(ikeyp->field, '.') + 1; + if (strncmp(cp, "NAUX", 4) != 0) continue; + + sprintf(keyvalue, "'%s: %d'", cp, dpkeyi(ikeyp)); + wcshdo_util(ctrl, keyword, 0x0, 0, 0x0, 0, 0, 0, alt, 0, 0x0, + keyvalue, "Number of auxiliary variables", nkeyrec, header, + &status); + + struct dpkey *jkeyp = dis->dp; + for (int jdp = 0; jdp < dis->ndp; jdp++, jkeyp++) { + if (jkeyp->j != j+1) continue; + + keyval = dpkeyd(jkeyp); + + cp = strchr(jkeyp->field, '.') + 1; + if (strncmp(cp, "AUX.", 4) != 0) continue; + + int m; + sscanf(cp+4, "%d", &m); + sprintf(keyvalue, "'%s:", cp); + + cp = strchr(cp+4, '.') + 1; + char *kp = keyvalue + strlen(keyvalue); + + if ((double)((int)keyval) == keyval) { + sprintf(kp, "%4d'", (int)keyval); + } else if (keyval == 0.5) { + strcat(kp, " 0.5'"); + } else { + wcsutil_double2str(kp, "%21.13E", keyval); + strcat(keyvalue, "'"); + } + + int p; + sscanf(cp+6, "%d", &p); + if (strncmp(cp, "POWER.", 4) == 0) { + if (p) { + sprintf(comment, "Aux %d: var %d power", m, p); + } else { + sprintf(comment, "Aux %d: power of sum of terms", m); + } + } else { + if (p) { + sprintf(comment, "Aux %d: var %d coefficient", m, p); + } else { + sprintf(comment, "Aux %d: offset term", m); + } + } + + wcshdo_util(ctrl, keyword, 0x0, 0, 0x0, 0, 0, 0, alt, 0, 0x0, + keyvalue, comment, nkeyrec, header, &status); + } + + break; + } + + // Do polynomial terms. + ikeyp = dis->dp; + for (int idp = 0; idp < dis->ndp; idp++, ikeyp++) { + if (ikeyp->j != j+1) continue; + + cp = strchr(ikeyp->field, '.') + 1; + if (strncmp(cp, "NTERMS", 6) != 0) continue; + + sprintf(keyvalue, "'%s: %d'", cp, dpkeyi(ikeyp)); + wcshdo_util(ctrl, keyword, 0x0, 0, 0x0, 0, 0, 0, alt, 0, 0x0, + keyvalue, "Number of terms in the polynomial", nkeyrec, header, + &status); + } + + ikeyp = dis->dp; + for (int idp = 0; idp < dis->ndp; idp++, ikeyp++) { + if (ikeyp->j != j+1) continue; + + if ((keyval = dpkeyd(ikeyp)) == 0.0) continue; + + cp = strchr(ikeyp->field, '.') + 1; + if (strncmp(cp, "TERM.", 5) != 0) continue; + + int m; + sscanf(cp+5, "%d", &m); + sprintf(keyvalue, "'%s:%s ", cp, (m<10)?" ":""); + + cp = strchr(cp+5, '.') + 1; + char *kp = keyvalue + strlen(keyvalue); + if (strncmp(cp, "VAR.", 4) == 0) { + if ((double)((int)keyval) == keyval) { + sprintf(kp, "%20d", (int)keyval); + } else { + wcsutil_double2str(kp, "%20.13f", keyval); + } + + int p; + sscanf(cp+4, "%d", &p); + if (p <= Nhat) { + sprintf(comment, "Poly term %d: var %d power", m, p); + } else { + sprintf(comment, "Poly term %d: aux %d power", m, p-Nhat); + } + + } else { + wcsutil_double2str(kp, "%20.13E", keyval); + sprintf(comment, "Poly term %d: coefficient", m); + } + strcat(keyvalue, "'"); + + wcshdo_util(ctrl, keyword, 0x0, 0, 0x0, 0, 0, 0, alt, 0, 0x0, + keyvalue, comment, nkeyrec, header, &status); + } + + // CPERRja/CQERRia + if (dis->maxdis[j] != 0.0) { + sprintf(keyword, "C%cERR%d", pq, j+1); + sprintf(keyvalue, "%20.2f", dis->maxdis[j]); + sprintf(comment, "%sMaximum absolute value of distortion", + idis?"":"[pix] "); + wcshdo_util(ctrl, keyword, 0x0, 0, 0x0, 0, 0, 0, alt, 0, 0x0, + keyvalue, comment, nkeyrec, header, &status); + } + } } } - wcshdo_util(relax, "DATE-AVG", "DAVG", 0, 0x0, 0, 0, 0, ' ', - colnum, colax, keyvalue, comment, nkeyrec, header, &status); + // DVERRa + if (dis->totdis != 0.0) { + sprintf(keyvalue, "%20.2f", dis->totdis); + sprintf(comment, "Maximum combined distortion"); + wcshdo_util(ctrl, "DVERR", 0x0, 0, 0x0, 0, 0, 0, alt, 0, 0x0, + keyvalue, comment, nkeyrec, header, &status); + } + } + + + // Add identification. + wcshdo_util(ctrl, "", 0x0, 0, 0x0, 0, 0, 0, ' ', 0, 0x0, + "", "", nkeyrec, header, &status); + + if (dotpd == DIS_DOTPD) { + // TPD by translation. + sprintf(comment, "Translated from %s to TPD by WCSLIB %s", tpdsrc, + wcslib_version(0x0)); + } else { + sprintf(comment, "WCS header keyrecords produced by WCSLIB %s", + wcslib_version(0x0)); } + wcshdo_util(ctrl, "COMMENT", 0x0, 0, 0x0, 0, 0, 0, ' ', 0, 0x0, + "", comment, nkeyrec, header, &status); + + if (status == WCSHDRERR_MEMORY) { wcserr_set(WCSHDR_ERRMSG(status)); } return status; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- +// Determine a suitable floating point format for a set of parameters. + +void wcshdo_format( + int fmt, + int nval, + const double val[], + char *format) + +{ + int emax = -999; + int emin = +999; + int precision = 0; + for (int i = 0; i < nval; i++) { + // Double precision has at least 15 significant digits, and up to 17: + // http://en.wikipedia.org/wiki/Double-precision_floating-point_format + char cval[24]; + wcsutil_double2str(cval, "%21.14E", val[i]); + + int cpi = 16; + while (2 < cpi && cval[cpi] == '0') cpi--; + + // Precision for 'E' format. + cpi -= 2; + if (precision < cpi) precision = cpi; + + // Range of significant digits for 'f' format. + int expon; + sscanf(cval+18, "%d", &expon); + + if (emax < expon) emax = expon; + expon -= cpi; + if (expon < emin) emin = expon; + } + + + if (fmt == 'G') { + // Because e.g. writing 1e4 as 10000 requires an extra digit. + emax++; + + if (emin < -15 || 15 < emax || 15 < (emax - emin)) { + fmt = 'E'; + } else { + fmt = 'f'; + } + } + + if (fmt == 'f') { + precision = -emin; + if (precision < 1) precision = 1; + if (17 < precision) precision = 17; + sprintf(format, "%%20.%df", precision); + + } else { + if (precision < 1) precision = 1; + if (14 < precision) precision = 14; + if (precision < 14) { + sprintf(format, "%%20.%dE", precision); + } else { + sprintf(format, "%%21.%dE", precision); + } + } +} + +//---------------------------------------------------------------------------- +// Construct a string that identifies the term of a TPD or TPV polynomial. + +void wcshdo_tpdterm( + int m, + int direct, + char *term) + +{ + const int nTPD[] = {1, 4, 7, 12, 17, 24, 31, 40, 49, 60}; + + int degree; + for (degree = 0; degree <= 9; degree++) { + if (m < nTPD[degree]) break; + } + + if (degree == 0) { + strcpy(term, "1"); + + } else { + int k = degree - (m - nTPD[degree-1]); + + if (k < 0) { + memcpy(term, "rrrrrrrrr", degree); + } else if (direct) { + memcpy(term, "xxxxxxxxx", k); + memcpy(term+k, "yyyyyyyyy", degree-k); + } else { + memcpy(term, "yyyyyyyyy", k); + memcpy(term+k, "xxxxxxxxx", degree-k); + } + + term[degree] = '\0'; + } +} + +//---------------------------------------------------------------------------- +// Construct a keyrecord from the components given. void wcshdo_util( int relax, @@ -945,14 +2110,12 @@ void wcshdo_util( int *status) { - char ch0, ch1, *hptr, keyword[16], *kptr; - int nbyte, nc = 47, nv; - if (*status) return; - /* Reallocate memory in blocks of 2880 bytes. */ + // Reallocate memory in blocks of 2880 bytes. + char *hptr; if ((*nkeyrec)%32 == 0) { - nbyte = ((*nkeyrec)/32 + 1) * 2880; + int nbyte = ((*nkeyrec)/32 + 1) * 2880; if (!(hptr = realloc(*header, nbyte))) { *status = WCSHDRERR_MEMORY; return; @@ -961,10 +2124,11 @@ void wcshdo_util( *header = hptr; } - /* Construct the keyword. */ + // Construct the keyword. + char keyword[32]; if (alt == ' ') alt = '\0'; if (btcol) { - /* Binary table image array. */ + // Binary table image array. if (i > 0 && j) { if (j > 0) { sprintf(keyword, "%d%d%s%d%c", i, j, tbkey, btcol, alt); @@ -980,7 +2144,7 @@ void wcshdo_util( } if ((strlen(keyword) < 8) && tlkey && (relax & level)) { - /* Use the long form. */ + // Use the long form. if (i > 0 && j) { if (j > 0) { sprintf(keyword, "%d%d%s%d%c", i, j, tlkey, btcol, alt); @@ -997,7 +2161,7 @@ void wcshdo_util( } } else if (plcol && plcol[0]) { - /* Pixel list. */ + // Pixel list. if (i > 0 && j) { if (j > 0) { sprintf(keyword, "T%s%d_%d%c", tbkey, plcol[i-1], plcol[j-1], alt); @@ -1013,7 +2177,7 @@ void wcshdo_util( } if ((strlen(keyword) < 8) && tlkey && (relax & level)) { - /* Use the long form. */ + // Use the long form. if (i > 0 && j) { if (j > 0) { sprintf(keyword, "T%s%d_%d%c", tlkey, plcol[i-1], plcol[j-1], alt); @@ -1044,30 +2208,55 @@ void wcshdo_util( } } - /* Double-up single-quotes in the keyvalue. */ - hptr = keyvalue + 1; - while (*hptr) { - if (*hptr == '\'') { - kptr = hptr++; - if (*hptr) { - ch0 = *kptr; - while (*kptr) { - ch1 = *(++kptr); - *kptr = ch0; - ch0 = ch1; + // Double-up single-quotes in string keyvalues. + if (*keyvalue == '\'') { + hptr = keyvalue + 1; + while (*hptr) { + if (*hptr == '\'') { + char *kptr = hptr++; + if (*hptr) { + char ch0 = *kptr; + while (*kptr) { + char ch1 = *(++kptr); + *kptr = ch0; + ch0 = ch1; + } + } else { + break; } } + + hptr++; + } + + // Check length. + if (strlen(keyvalue) > 70) { + // Truncate. + keyvalue[69] = '\''; + keyvalue[70] = '\0'; } - hptr++; + } else { + // Check length. + if (strlen(keyvalue) > 70) { + // Truncate. + keyvalue[70] = '\0'; + } } + int nc = 47, nv; if ((nv = strlen(keyvalue) > 20)) { - /* Rob the keycomment to make space for the keyvalue. */ + // Rob the keycomment to make space for the keyvalue. nc -= (nv - 20); } hptr = *header + (80 * ((*nkeyrec)++)); - sprintf(hptr, "%-8.8s= %-20s / %-*.*s", keyword, keyvalue, nc, nc, - keycomment); + if (*keyword == '\0') { + sprintf(hptr, "%80.80s", " "); + } else if (strcmp(keyword, "COMMENT") == 0) { + sprintf(hptr, "%-8.8s %-71.71s", keyword, keycomment); + } else { + sprintf(hptr, "%-8.8s= %-20s / %-*.*s", keyword, keyvalue, nc, nc, + keycomment); + } } diff --git a/cextern/wcslib/C/wcshdr.h b/cextern/wcslib/C/wcshdr.h index 385070701ad7..cca97bce37cb 100644 --- a/cextern/wcslib/C/wcshdr.h +++ b/cextern/wcslib/C/wcshdr.h @@ -1,7 +1,6 @@ /*============================================================================ - - WCSLIB 4.14 - an implementation of the FITS WCS standard. - Copyright (C) 1995-2012, Mark Calabretta + WCSLIB 8.6 - an implementation of the FITS WCS standard. + Copyright (C) 1995-2026, Mark Calabretta This file is part of WCSLIB. @@ -18,35 +17,42 @@ You should have received a copy of the GNU Lesser General Public License along with WCSLIB. If not, see http://www.gnu.org/licenses. - Direct correspondence concerning WCSLIB to mark@calabretta.id.au - Author: Mark Calabretta, Australia Telescope National Facility, CSIRO. - http://www.atnf.csiro.au/people/Mark.Calabretta - $Id: wcshdr.h,v 4.14 2012/07/13 10:02:27 cal103 Exp $ + http://www.atnf.csiro.au/computing/software/wcs + $Id: wcshdr.h,v 8.6 2026/03/29 13:53:56 mcalabre Exp $ *============================================================================= * -* WCSLIB 4.14 - C routines that implement the FITS World Coordinate System -* (WCS) standard. Refer to -* -* "Representations of world coordinates in FITS", -* Greisen, E.W., & Calabretta, M.R. 2002, A&A, 395, 1061 (Paper I) -* -* "Representations of celestial coordinates in FITS", -* Calabretta, M.R., & Greisen, E.W. 2002, A&A, 395, 1077 (Paper II) -* -* "Representations of spectral coordinates in FITS", -* Greisen, E.W., Calabretta, M.R., Valdes, F.G., & Allen, S.L. -* 2006, A&A, 446, 747 (Paper III) -* -* Refer to the README file provided with WCSLIB for an overview of the -* library. +* WCSLIB 8.6 - C routines that implement the FITS World Coordinate System +* (WCS) standard. Refer to the README file provided with WCSLIB for an +* overview of the library. * * * Summary of the wcshdr routines * ------------------------------ * Routines in this suite are aimed at extracting WCS information from a FITS -* file. They provide the high-level interface between the FITS file and the -* WCS coordinate transformation routines. +* file. The information is encoded via keywords defined in +* += "Representations of world coordinates in FITS", += Greisen, E.W., & Calabretta, M.R. 2002, A&A, 395, 1061 (WCS Paper I) += += "Representations of celestial coordinates in FITS", += Calabretta, M.R., & Greisen, E.W. 2002, A&A, 395, 1077 (WCS Paper II) += += "Representations of spectral coordinates in FITS", += Greisen, E.W., Calabretta, M.R., Valdes, F.G., & Allen, S.L. += 2006, A&A, 446, 747 (WCS Paper III) += += "Representations of distortions in FITS world coordinate systems", += Calabretta, M.R. et al. (WCS Paper IV, draft dated 2004/04/22), += available from http://www.atnf.csiro.au/computing/software/wcs += += "Representations of time coordinates in FITS - += Time and relative dimension in space", += Rots, A.H., Bunclark, P.S., Calabretta, M.R., Allen, S.L., += Manchester, R.N., & Thompson, W.T. 2015, A&A, 574, A36 (WCS Paper VII) +* +* These routines provide the high-level interface between the FITS file and +* the WCS coordinate transformation routines. * * Additionally, function wcshdo() is provided to write out the contents of a * wcsprm struct as a FITS header. @@ -63,7 +69,7 @@ * extension, e.g. using CFITSIO routine fits_read_wcstab() - refer to * the prologue of getwcstab.h. wcsset() will automatically take * control of this allocated memory, in particular causing it to be -* free'd by wcsfree(). +* freed by wcsfree(). * * - 4: Translate non-standard WCS usage using wcsfix(), see wcsfix.h. * @@ -104,9 +110,11 @@ * ---------------------------------------------------- * wcspih() is a high-level FITS WCS routine that parses an image header, * either that of a primary HDU or of an image extension. All WCS keywords -* defined in Papers I, II, and III are recognized, and also those used by the -* AIPS convention and certain other keywords that existed in early drafts of -* the WCS papers as explained in wcsbth() note 5. +* defined in Papers I, II, III, IV, and VII are recognized, and also those +* used by the AIPS convention and certain other keywords that existed in early +* drafts of the WCS papers as explained in wcsbth() note 5. wcspih() also +* handles keywords associated with non-standard distortion functions described +* in the prologue of dis.h. * * Given a character array containing a FITS image header, wcspih() identifies * and reads all WCS keywords for the primary coordinate representation and up @@ -117,7 +125,8 @@ * * Use wcsbth() in preference to wcspih() for FITS headers of unknown type; * wcsbth() can parse image headers as well as binary table and pixel list -* headers. +* headers, although it cannot handle keywords relating to distortion +* functions, which may only exist in an image header (primary or extension). * * Given and returned: * header char[] Character array containing the (entire) FITS image @@ -157,20 +166,26 @@ * 3: As above, but also report all non-WCS * keyrecords that were discarded, and the number * of coordinate representations (nwcs) found. -* The report is written to stderr. +* 4: As above, but also report the accepted WCS +* keyrecords, with a summary of the number +* accepted as well as rejected. +* The report is written to stderr by default, or the +* stream set by wcsprintf_set(). * * For ctrl < 0, WCS keyrecords processed by wcspih() * are removed from header[]: * -1: Remove only valid WCS keyrecords whose values * were successfully extracted, nothing is * reported. -* -2: Also remove WCS keyrecords that were rejected, -* reporting each one and the reason that it was -* rejected. +* -2: As above, but also remove WCS keyrecords that +* were rejected, reporting each one and the +* reason that it was rejected. * -3: As above, and also report the number of * coordinate representations (nwcs) found. -* -11: Same as -1 but preserving the basic keywords -* '{DATE,MJD}-{OBS,AVG}' and 'OBSGEO-{X,Y,Z}'. +* -11: Same as -1 but preserving global WCS-related +* keywords such as '{DATE,MJD}-{OBS,BEG,AVG,END}' +* and the other basic time-related keywords, and +* 'OBSGEO-{X,Y,Z,L,B,H}'. * If any keyrecords are removed from header[] it will * be null-terminated (NUL not being a legal FITS header * character), otherwise it will contain its original @@ -209,7 +224,7 @@ * 4: Fatal error returned by Flex parser. * * Notes: -* Refer to wcsbth() notes 1, 2, 3, 5, 7, and 8. +* 1: Refer to wcsbth() notes 1, 2, 3, 5, 7, and 8. * * * wcsbth() - FITS WCS parser routine for binary table and image headers @@ -227,9 +242,9 @@ * headers (it ignores NAXIS and does not rely on the presence of the TFIELDS * keyword). * -* All WCS keywords defined in Papers I, II, and III are recognized, and also -* those used by the AIPS convention and certain other keywords that existed in -* early drafts of the WCS papers as explained in note 5 below. +* All WCS keywords defined in Papers I, II, III, and VII are recognized, and +* also those used by the AIPS convention and certain other keywords that +* existed in early drafts of the WCS papers as explained in note 5 below. * * wcsbth() sets the colnum or colax[] members of the wcsprm structs that it * returns with the column number of an image array or the column numbers @@ -285,7 +300,11 @@ * 3: As above, but also report all non-WCS * keyrecords that were discarded, and the number * of coordinate representations (nwcs) found. -* The report is written to stderr. +* 4: As above, but also report the accepted WCS +* keyrecords, with a summary of the number +* accepted as well as rejected. +* The report is written to stderr by default, or the +* stream set by wcsprintf_set(). * * For ctrl < 0, WCS keyrecords processed by wcsbth() * are removed from header[]: @@ -297,8 +316,10 @@ * rejected. * -3: As above, and also report the number of * coordinate representations (nwcs) found. -* -11: Same as -1 but preserving the basic keywords -* '{DATE,MJD}-{OBS,AVG}' and 'OBSGEO-{X,Y,Z}'. +* -11: Same as -1 but preserving global WCS-related +* keywords such as '{DATE,MJD}-{OBS,BEG,AVG,END}' +* and the other basic time-related keywords, and +* 'OBSGEO-{X,Y,Z,L,B,H}'. * If any keyrecords are removed from header[] it will * be null-terminated (NUL not being a legal FITS header * character), otherwise it will contain its original @@ -407,8 +428,8 @@ * 4: WCS Paper I mistakenly defined the pixel list form of WCSNAMEa as * TWCSna instead of WCSNna; the 'T' is meant to substitute for the axis * number in the binary table form of the keyword - note that keywords -* defined in WCS Papers II and III that are not parameterised by axis -* number have identical forms for binary tables and pixel lists. +* defined in WCS Papers II, III, and VII that are not parameterized by +* axis number have identical forms for binary tables and pixel lists. * Consequently wcsbth() always treats WCSNna and TWCSna as equivalent. * * 5: wcspih() and wcsbth() interpret the "relax" argument as a vector of @@ -423,22 +444,42 @@ * * - WCSHDR_all: Accept all extensions recognized by the parser. * -* - WCSHDR_reject: Reject non-standard keywords (that are not otherwise -* accepted). A message will optionally be printed on stderr, as -* determined by the ctrl argument, and nreject will be -* incremented. +* - WCSHDR_reject: Reject non-standard keyrecords (that are not otherwise +* explicitly accepted by one of the flags below). A message will +* optionally be printed on stderr by default, or the stream set +* by wcsprintf_set(), as determined by the ctrl argument, and +* nreject will be incremented. * * This flag may be used to signal the presence of non-standard * keywords, otherwise they are simply passed over as though they -* did not exist in the header. +* did not exist in the header. It is mainly intended for testing +* conformance of a FITS header to the WCS standard. +* +* Keyrecords may be non-standard in several ways: +* +* - The keyword may be syntactically valid but with keyvalue of +* incorrect type or invalid syntax, or the keycomment may be +* malformed. * -* Useful for testing conformance of a FITS header to the WCS -* standard. +* - The keyword may strongly resemble a WCS keyword but not, in +* fact, be one because it does not conform to the standard. +* For example, "CRPIX01" looks like a CRPIXja keyword, but in +* fact the leading zero on the axis number violates the basic +* FITS standard. Likewise, "LONPOLE2" is not a valid +* LONPOLEa keyword in the WCS standard, and indeed there is +* nothing the parser can sensibly do with it. +* +* - Use of the keyword may be deprecated by the standard. Such +* will be rejected if not explicitly accepted via one of the +* flags below. +* +* - WCSHDR_strict: As for WCSHDR_reject, but also reject AIPS-convention +* keywords and all other deprecated usage that is not explicitly +* accepted. * * - WCSHDR_CROTAia: Accept CROTAia (wcspih()), * iCROTna (wcsbth()), * TCROTna (wcsbth()). -* - WCSHDR_EPOCHa: Accept EPOCHa. * - WCSHDR_VELREFa: Accept VELREFa. * wcspih() always recognizes the AIPS-convention keywords, * CROTAn, EPOCH, and VELREF for the primary representation @@ -456,12 +497,36 @@ * equivalent to PVi_ma with m = n <= 9, and is associated * exclusively with the latitude axis. * +* - WCSHDR_CD0i_0ja: Accept CD0i_0ja (wcspih()). +* - WCSHDR_PC0i_0ja: Accept PC0i_0ja (wcspih()). +* - WCSHDR_PV0i_0ma: Accept PV0i_0ja (wcspih()). +* - WCSHDR_PS0i_0ma: Accept PS0i_0ja (wcspih()). +* Allow the numerical index to have a leading zero in doubly- +* parameterized keywords, for example, PC01_01. WCS Paper I +* (Sects 2.1.2 & 2.1.4) explicitly disallows leading zeroes. +* The FITS 3.0 standard document (Sect. 4.1.2.1) states that the +* index in singly-parameterized keywords (e.g. CTYPEia) "shall +* not have leading zeroes", and later in Sect. 8.1 that "leading +* zeroes must not be used" on PVi_ma and PSi_ma. However, by an +* oversight, it is silent on PCi_ja and CDi_ja. +* +* - WCSHDR_DOBSn (wcsbth() only): Allow DOBSn, the column-specific +* analogue of DATE-OBS. By an oversight this was never formally +* defined in the standard. +* +* - WCSHDR_OBSGLBHn (wcsbth() only): Allow OBSGLn, OBSGBn, and OBSGHn, +* the column-specific analogues of OBSGEO-L, OBSGEO-B, and +* OBSGEO-H. By an oversight these were never formally defined in +* the standard. +* * - WCSHDR_RADECSYS: Accept RADECSYS. This appeared in early drafts of * WCS Paper I+II and was subsequently replaced by RADESYSa. * * wcsbth() accepts RADECSYS only if WCSHDR_AUXIMG is also * enabled. * +* - WCSHDR_EPOCHa: Accept EPOCHa. +* * - WCSHDR_VSOURCE: Accept VSOURCEa or VSOUna (wcsbth()). This appeared * in early drafts of WCS Paper III and was subsequently dropped * in favour of ZSOURCEa and ZSOUna. @@ -469,9 +534,12 @@ * wcsbth() accepts VSOURCEa only if WCSHDR_AUXIMG is also * enabled. * -* - WCSHDR_DOBSn (wcsbth() only): Allow DOBSn, the column-specific analogue -* of DATE-OBS. By an oversight this was never formally defined -* in the standard. +* - WCSHDR_DATEREF: Accept DATE-REF, MJD-REF, MJD-REFI, MJD-REFF, JDREF, +* JD-REFI, and JD-REFF as synonyms for the standard keywords, +* DATEREF, MJDREF, MJDREFI, MJDREFF, JDREF, JDREFI, and JDREFF. +* The latter buck the pattern set by the other date keywords +* ({DATE,MJD}-{OBS,BEG,AVG,END}), thereby increasing the +* potential for confusion and error. * * - WCSHDR_LONGKEY (wcsbth() only): Accept long forms of the alternate * binary table and pixel list WCS keywords, i.e. with "a" non- @@ -509,14 +577,17 @@ # iCNAMna TCNAMna : --- iCNAna --- TCNAna CNAMEia # iCRDEna TCRDEna : --- iCRDna --- TCRDna CRDERia # iCSYEna TCSYEna : --- iCSYna --- TCSYna CSYERia +# iCZPHna TCZPHna : --- iCZPna --- TCZPna CZPHSia +# iCPERna TCPERna : --- iCPRna --- TCPRna CPERIia * -* Note that CNAMEia, CRDERia, CSYERia, and their variants are -* not used by WCSLIB but are stored in the wcsprm struct as -* auxiliary information. +* Note that CNAMEia, CRDERia, CSYERia, CZPHSia, CPERIia, and +* their variants are not used by WCSLIB but are stored in the +* wcsprm struct as auxiliary information. * -* - WCSHDR_CNAMn (wcsbth() only): Accept iCNAMn, iCRDEn, iCSYEn, TCNAMn, -* TCRDEn, and TCSYEn, i.e. with "a" blank. While non-standard, -* these are the obvious analogues of iCTYPn, TCTYPn, etc. +* - WCSHDR_CNAMn (wcsbth() only): Accept iCNAMn, iCRDEn, iCSYEn, iCZPHn, +* iCPERn, TCNAMn, TCRDEn, TCSYEn, TCZPHn, and TCPERn, i.e. with +* "a" blank. While non-standard, these are the obvious analogues +* of iCTYPn, TCTYPn, etc. * * - WCSHDR_AUXIMG (wcsbth() only): Allow the image-header form of an * auxiliary WCS keyword with representation-wide scope to @@ -529,62 +600,121 @@ * * Specifically the keywords are: * -# LATPOLEa for LATPna # LONPOLEa for LONPna -# RESTFREQ for RFRQna -# RESTFRQa for RFRQna -# RESTWAVa for RWAVna +# LATPOLEa for LATPna +# VELREF - ... (No column-specific form.) +# VELREFa - ... Only if WCSHDR_VELREFa is set. * * whose keyvalues are actually used by WCSLIB, and also keywords -* that provide auxiliary information that is simply stored in -* the wcsprm struct: +* providing auxiliary information that is simply stored in the +* wcsprm struct: * +# WCSNAMEa for WCSNna ... Or TWCSna (see below). +# +# DATE-OBS for DOBSn +# MJD-OBS for MJDOBn +# +# RADESYSa for RADEna +# RADECSYS for RADEna ... Only if WCSHDR_RADECSYS is set. # EPOCH - ... (No column-specific form.) # EPOCHa - ... Only if WCSHDR_EPOCHa is set. # EQUINOXa for EQUIna -# RADESYSa for RADEna -# RADECSYS for RADEna ... Only if WCSHDR_RADECSYS is set. -# SPECSYSa for SPECna -# SSYSOBSa for SOBSna -# SSYSSRCa for SSRCna -# VELOSYSa for VSYSna -# VELANGLa for VANGna -# VELREF - ... (No column-specific form.) -# VELREFa - ... Only if WCSHDR_VELREFa is set. -# VSOURCEa for VSOUna ... Only if WCSHDR_VSOURCE is set. -# WCSNAMEa for WCSNna ... Or TWCSna (see below). -# ZSOURCEa for ZSOUna * +* where the image-header keywords on the left provide default +* values for the column specific keywords on the right. +* +* Note that, according to Sect. 8.1 of WCS Paper III, and +* Sect. 5.2 of WCS Paper VII, the following are always inherited: +* +# RESTFREQ for RFRQna +# RESTFRQa for RFRQna +# RESTWAVa for RWAVna +* +* being those actually used by WCSLIB, together with the +* following auxiliary keywords, many of which do not have binary +* table equivalents and therefore can only be inherited: +* +# TIMESYS - +# TREFPOS for TRPOSn +# TREFDIR for TRDIRn +# PLEPHEM - +# TIMEUNIT - +# DATEREF - +# MJDREF - +# MJDREFI - +# MJDREFF - +# JDREF - +# JDREFI - +# JDREFF - +# TIMEOFFS - +# +# DATE-BEG - # DATE-AVG for DAVGn -# DATE-OBS for DOBSn +# DATE-END - +# MJD-BEG - # MJD-AVG for MJDAn -# MJD-OBS for MJDOBn +# MJD-END - +# JEPOCH - +# BEPOCH - +# TSTART - +# TSTOP - +# XPOSURE - +# TELAPSE - +# +# TIMSYER - +# TIMRDER - +# TIMEDEL - +# TIMEPIXR - +# # OBSGEO-X for OBSGXn # OBSGEO-Y for OBSGYn # OBSGEO-Z for OBSGZn +# OBSGEO-L for OBSGLn +# OBSGEO-B for OBSGBn +# OBSGEO-H for OBSGHn +# OBSORBIT - +# +# SPECSYSa for SPECna +# SSYSOBSa for SOBSna +# VELOSYSa for VSYSna +# VSOURCEa for VSOUna ... Only if WCSHDR_VSOURCE is set. +# ZSOURCEa for ZSOUna +# SSYSSRCa for SSRCna +# VELANGLa for VANGna * -* where the image-header keywords on the left provide default -* values for the column specific keywords on the right. -* -* Keywords in the last group, such as MJD-OBS, apply to all -* alternate representations, so MJD-OBS would provide a default -* value for all images in the header. +* Global image-header keywords, such as MJD-OBS, apply to all +* alternate representations, and would therefore provide a +* default value for all images in the header. * * This auxiliary inheritance mechanism applies to binary table * image arrays and pixel lists alike. Most of these keywords * have no default value, the exceptions being LONPOLEa and * LATPOLEa, and also RADESYSa and EQUINOXa which provide -* defaults for each other. Thus the only potential difficulty -* in using WCSHDR_AUXIMG is that of erroneously inheriting one -* of these four keywords. +* defaults for each other. Thus one potential difficulty in +* using WCSHDR_AUXIMG is that of erroneously inheriting one of +* these four keywords. +* +* Also, beware of potential inconsistencies that may arise where, +* for example, DATE-OBS is inherited, but MJD-OBS is overridden +* by MJDOBn and specifies a different time. Pairs in this +* category are: +* += DATE-OBS/DOBSn versus MJD-OBS/MJDOBn += DATE-AVG/DAVGn versus MJD-AVG/MJDAn += RESTFRQa/RFRQna versus RESTWAVa/RWAVna += OBSGEO-[XYZ]/OBSG[XYZ]n versus OBSGEO-[LBH]/OBSG[LBH]n +* +* The wcsfixi() routines datfix() and obsfix() are provided to +* check the consistency of these and other such pairs of +* keywords. * * Unlike WCSHDR_ALLIMG, the existence of one (or all) of these * auxiliary WCS image header keywords will not by itself cause a * wcsprm struct to be created for alternate representation "a". * This is because they do not provide sufficient information to * create a non-trivial coordinate representation when used in -* conjunction with the default values of those keywords, such as -* CTYPEia, that are parameterized by axis number. +* conjunction with the default values of those keywords that are +* parameterized by axis number, such as CTYPEia. * * - WCSHDR_ALLIMG (wcsbth() only): Allow the image-header form of *all* * image header WCS keywords to provide a default value for all @@ -602,7 +732,7 @@ # WCSAXESa for WCAXna * * which defines the coordinate dimensionality, and the following -* keywords which are parameterized by axis number: +* keywords that are parameterized by axis number: * # CRPIXja for jCRPna # PCi_ja for ijPCna @@ -615,10 +745,12 @@ # CRVALia for iCRVna # PVi_ma for iVn_ma # PSi_ma for iSn_ma -* +# # CNAMEia for iCNAna # CRDERia for iCRDna # CSYERia for iCSYna +# CZPHSia for iCZPna +# CPERIia for iCPRna * * where the image-header keywords on the left provide default * values for the column specific keywords on the right. @@ -626,7 +758,7 @@ * This full inheritance mechanism only applies to binary table * image arrays, not pixel lists, because in the latter case * there is no well-defined association between coordinate axis -* number and column number. +* number and column number (see note 9 below). * * Note that CNAMEia, CRDERia, CSYERia, and their variants are * not used by WCSLIB but are stored in the wcsprm struct as @@ -702,28 +834,61 @@ * 9: The FITS WCS standard for pixel lists assumes that a pixel list * defines one and only one image, i.e. that each row of the binary table * refers to just one event, e.g. the detection of a single photon or -* neutrino. +* neutrino, for which the device "pixel" coordinates are stored in +* separate scalar columns of the table. * -* In the absence of a formal mechanism for identifying the columns -* containing pixel coordinates (as opposed to pixel values or ancillary -* data recorded at the time the photon or neutrino was detected), -* Paper I discusses how the WCS keywords themselves may be used to -* identify them. +* In the absence of a standard for pixel lists - or even an informal +* description! - let alone a formal mechanism for identifying the columns +* containing pixel coordinates (as opposed to pixel values or metadata +* recorded at the time the photon or neutrino was detected), WCS Paper I +* discusses how the WCS keywords themselves may be used to identify them. * * In practice, however, pixel lists have been used to store multiple -* images. Besides not specifying how to identify columns, the pixel -* list convention is also silent on the method to be used to associate -* table columns with image axes. -* -* wcsbth() simply collects all WCS keywords for a particular coordinate -* representation (i.e. the "a" value in TCTYna) into one wcsprm struct. -* However, these alternates need not be associated with the same table -* columns and this allows a pixel list to contain up to 27 separate -* images. As usual, if one of these representations happened to contain -* more than two celestial axes, for example, then an error would result -* when wcsset() is invoked on it. In this case the "colsel" argument -* could be used to restrict the columns used to construct the -* representation so that it only contained one pair of celestial axes. +* images. Besides not specifying how to identify columns, the pixel list +* convention is also silent on the method to be used to associate table +* columns with image axes. +* +* An additional shortcoming is the absence of a formal method for +* associating global binary-table WCS keywords, such as WCSNna or MJDOBn, +* with a pixel list image, whether one or several. +* +* In light of these uncertainties, wcsbth() simply collects all WCS +* keywords for a particular pixel list coordinate representation (i.e. +* the "a" value in TCTYna) into one wcsprm struct. However, these +* alternates need not be associated with the same table columns and this +* allows a pixel list to contain up to 27 separate images. As usual, if +* one of these representations happened to contain more than two +* celestial axes, for example, then an error would result when wcsset() +* is invoked on it. In this case the "colsel" argument could be used to +* restrict the columns used to construct the representation so that it +* only contained one pair of celestial axes. +* +* Global, binary-table WCS keywords are considered to apply to the pixel +* list image with matching alternate (e.g. the "a" value in LONPna or +* EQUIna), regardless of the table columns the image occupies. In other +* words, the column number is ignored (the "n" value in LONPna or +* EQUIna). This also applies for global, binary-table WCS keywords that +* have no alternates, such as MJDOBn and OBSGXn, which match all images +* in a pixel list. Take heed that this may lead to counterintuitive +* behaviour, especially where such a keyword references a column that +* does not store pixel coordinates, and moreso where the pixel list +* stores only a single image. In fact, as the column number, n, is +* ignored for such keywords, it would make no difference even if they +* referenced non-existent columns. Moreover, there is no requirement for +* consistency in the column numbers used for such keywords, even for +* OBSGXn, OBSGYn, and OBSGZn which are meant to define the elements of a +* coordinate vector. Although it would surely be perverse to construct a +* pixel list like this, such a situation may still arise in practice +* where columns are deleted from a binary table. +* +* The situation with global, binary-table WCS keywords becomes +* potentially even more confusing when image arrays and pixel list images +* coexist in one binary table. In that case, a keyword such as MJDOBn +* may legitimately appear multiple times with n referencing different +* image arrays. Which then is the one that applies to the pixel list +* images? In this implementation, it is the last instance that appears +* in the header, whether or not it is also associated with an image +* array. * * * wcstab() - Tabular construction routine @@ -741,14 +906,14 @@ * axis and allocates memory in the wcsprm struct for the required number of * tabprm structs. It sets as much of the tabprm struct as can be gleaned from * the image header, and also sets up an array of wtbarr structs (described in -* the prologue of wcs.h) to assist in extracting the required arrays from the -* BINTABLE extension(s). +* the prologue of wtbarr.h) to assist in extracting the required arrays from +* the BINTABLE extension(s). * * It is then up to the user to allocate memory for, and copy arrays from the * BINTABLE extension(s) into the tabprm structs. A CFITSIO routine, * fits_read_wcstab(), has been provided for this purpose, see getwcstab.h. * wcsset() will automatically take control of this allocated memory, in -* particular causing it to be free'd by wcsfree(); the user must not attempt +* particular causing it to be freed by wcsfree(); the user must not attempt * to free it after wcsset() has been called. * * Note that wcspih() and wcsbth() automatically invoke wcstab() on each of the @@ -760,7 +925,7 @@ * * wcstab() sets ntab, tab, nwtb and wtb, allocating * memory for the tab and wtb arrays. This allocated -* memory will be free'd automatically by wcsfree(). +* memory will be freed automatically by wcsfree(). * * Function return value: * int Status return value: @@ -862,7 +1027,7 @@ * on return. * * wcs struct wcsprm** -* Pointer to the array of wcsprm structs; set to 0 on +* Pointer to the array of wcsprm structs; set to 0x0 on * return. * * Function return value: @@ -886,39 +1051,92 @@ * does not contain syntactically-required keywords such as SIMPLE, NAXIS, * BITPIX, or END. * -* - Deprecated (e.g. CROTAn) or non-standard usage will be translated to -* standard (this is partially dependent on whether wcsfix() was applied). -* -* - Quantities will be converted to the units used internally, basically SI -* with the addition of degrees. -* -* - Floating-point quantities may be given to a different decimal precision. -* * - Elements of the PCi_ja matrix will be written if and only if they differ * from the unit matrix. Thus, if the matrix is unity then no elements * will be written. * +* - The redundant keywords MJDREF, JDREF, JDREFI, JDREFF, all of which +* duplicate MJDREFI + MJDREFF, are never written. OBSGEO-[LBH] are not +* written if OBSGEO-[XYZ] are defined. +* +* - Deprecated (e.g. CROTAn, RESTFREQ, VELREF, RADECSYS, EPOCH, VSOURCEa) or +* non-standard usage will be translated to standard (this is partially +* dependent on whether wcsfix() was applied). +* * - Additional keywords such as WCSAXESa, CUNITia, LONPOLEa and LATPOLEa may * appear. * +* - Quantities will be converted to the units used internally, basically SI +* with the addition of degrees. +* +* - Floating-point quantities may be given to a different decimal precision. +* * - The original keycomments will be lost, although wcshdo() tries hard to * write meaningful comments. * -* - Keyword order may be changed. +* - Keyword order will almost certainly be changed. * * Keywords can be translated between the image array, binary table, and pixel * lists forms by manipulating the colnum or colax[] members of the wcsprm * struct. * * Given: -* relax int Degree of permissiveness: -* 0: Recognize only FITS keywords defined by the -* published WCS standard. -* -1: Admit all informal extensions of the WCS -* standard. +* ctrl int Vector of flag bits that controls the degree of +* permissiveness in departing from the published WCS +* standard, and also controls the formatting of +* floating-point keyvalues. Set it to zero to get the +* default behaviour. +* +* Flag bits for the degree of permissiveness: +* WCSHDO_none: Recognize only FITS keywords defined by +* the published WCS standard. +* WCSHDO_all: Admit all recognized informal extensions +* of the WCS standard. * Fine-grained control of the degree of permissiveness * is also possible as explained in the notes below. * +* As for controlling floating-point formatting, by +* default wcshdo() uses "%20.12G" for non-parameterized +* keywords such as LONPOLEa, and attempts to make the +* header more human-readable by using the same "%f" +* format for all values of each of the following +* parameterized keywords: CRPIXja, PCi_ja, and CDELTia +* (n.b. excluding CRVALia). Each has the same field +* width and precision so that the decimal points line +* up. The precision, allowing for up to 15 significant +* digits, is chosen so that there are no excess trailing +* zeroes. A similar formatting scheme applies by +* default for distortion function parameters. +* +* However, where the values of, for example, CDELTia +* differ by many orders of magnitude, the default +* formatting scheme may cause unacceptable loss of +* precision for the lower-valued keyvalues. Thus the +* default behaviour may be overridden: +* WCSHDO_P12: Use "%20.12G" format for all floating- +* point keyvalues (12 significant digits). +* WCSHDO_P13: Use "%21.13G" format for all floating- +* point keyvalues (13 significant digits). +* WCSHDO_P14: Use "%22.14G" format for all floating- +* point keyvalues (14 significant digits). +* WCSHDO_P15: Use "%23.15G" format for all floating- +* point keyvalues (15 significant digits). +* WCSHDO_P16: Use "%24.16G" format for all floating- +* point keyvalues (16 significant digits). +* WCSHDO_P17: Use "%25.17G" format for all floating- +* point keyvalues (17 significant digits). +* If more than one of the above flags are set, the +* highest number of significant digits prevails. In +* addition, there is an anciliary flag: +* WCSHDO_EFMT: Use "%E" format instead of the default +* "%G" format above. +* Note that excess trailing zeroes are stripped off the +* fractional part with "%G" (which never occurs with +* "%E"). Note also that the higher-precision options +* eat into the keycomment area. In this regard, +* WCSHDO_P14 causes minimal disruption with "%G" format, +* while WCSHDO_P13 is appropriate with "%E". +* * Given and returned: * wcs struct wcsprm* * Pointer to a wcsprm struct containing coordinate @@ -932,7 +1150,8 @@ * header char** Pointer to an array of char holding the header. * Storage for the array is allocated by wcshdo() in * blocks of 2880 bytes (32 x 80-character keyrecords) -* and must be free'd by the user to avoid memory leaks. +* and must be freed by the user to avoid memory leaks. +* See wcsdealloc(). * * Each keyrecord is 80 characters long and is *NOT* * null-terminated, so the first keyrecord starts at @@ -955,91 +1174,96 @@ * wcsprm::err if enabled, see wcserr_enable(). * * Notes: -* wcshdo() interprets the "relax" argument as a vector of flag bits to -* provide fine-grained control over what non-standard WCS keywords to write. -* The flag bits are subject to change in future and should be set by using -* the preprocessor macros (see below) for the purpose. +* 1: wcshdo() interprets the "relax" argument as a vector of flag bits to +* provide fine-grained control over what non-standard WCS keywords to +* write. The flag bits are subject to change in future and should be set +* by using the preprocessor macros (see below) for the purpose. * -* - WCSHDO_none: Don't use any extensions. +* - WCSHDO_none: Don't use any extensions. * -* - WCSHDO_all: Write all recognized extensions, equivalent to setting each -* flag bit. +* - WCSHDO_all: Write all recognized extensions, equivalent to setting +* each flag bit. * -* - WCSHDO_safe: Write all extensions that are considered to be safe and -* recommended. +* - WCSHDO_safe: Write all extensions that are considered to be safe and +* recommended. * -* - WCSHDO_DOBSn: Write DOBSn, the column-specific analogue of DATE-OBS for -* use in binary tables and pixel lists. WCS Paper III introduced -* DATE-AVG and DAVGn but by an oversight DOBSn (the obvious analogy) -* was never formally defined by the standard. The alternative to -* using DOBSn is to write DATE-OBS which applies to the whole table. -* This usage is considered to be safe and is recommended. +* - WCSHDO_DOBSn: Write DOBSn, the column-specific analogue of DATE-OBS +* for use in binary tables and pixel lists. WCS Paper III +* introduced DATE-AVG and DAVGn but by an oversight DOBSn (the +* obvious analogy) was never formally defined by the standard. +* The alternative to using DOBSn is to write DATE-OBS which +* applies to the whole table. This usage is considered to be +* safe and is recommended. * -* - WCSHDO_TPCn_ka: WCS Paper I defined +* - WCSHDO_TPCn_ka: WCS Paper I defined * -* - TPn_ka and TCn_ka for pixel lists +* - TPn_ka and TCn_ka for pixel lists * -* but WCS Paper II uses TPCn_ka in one example and subsequently the -* errata for the WCS papers legitimized the use of +* but WCS Paper II uses TPCn_ka in one example and subsequently +* the errata for the WCS papers legitimized the use of * -* - TPCn_ka and TCDn_ka for pixel lists +* - TPCn_ka and TCDn_ka for pixel lists * -* provided that the keyword does not exceed eight characters. This -* usage is considered to be safe and is recommended because of the -* non-mnemonic terseness of the shorter forms. +* provided that the keyword does not exceed eight characters. +* This usage is considered to be safe and is recommended because +* of the non-mnemonic terseness of the shorter forms. * -* - WCSHDO_PVn_ma: WCS Paper I defined +* - WCSHDO_PVn_ma: WCS Paper I defined * -* - iVn_ma and iSn_ma for bintables and -* - TVn_ma and TSn_ma for pixel lists +* - iVn_ma and iSn_ma for bintables and +* - TVn_ma and TSn_ma for pixel lists * -* but WCS Paper II uses iPVn_ma and TPVn_ma in the examples and -* subsequently the errata for the WCS papers legitimized the use of +* but WCS Paper II uses iPVn_ma and TPVn_ma in the examples and +* subsequently the errata for the WCS papers legitimized the use +* of * -* - iPVn_ma and iPSn_ma for bintables and -* - TPVn_ma and TPSn_ma for pixel lists +* - iPVn_ma and iPSn_ma for bintables and +* - TPVn_ma and TPSn_ma for pixel lists * -* provided that the keyword does not exceed eight characters. This -* usage is considered to be safe and is recommended because of the -* non-mnemonic terseness of the shorter forms. +* provided that the keyword does not exceed eight characters. +* This usage is considered to be safe and is recommended because +* of the non-mnemonic terseness of the shorter forms. * -* - WCSHDO_CRPXna: For historical reasons WCS Paper I defined +* - WCSHDO_CRPXna: For historical reasons WCS Paper I defined * -* - jCRPXn, iCDLTn, iCUNIn, iCTYPn, and iCRVLn for bintables and -* - TCRPXn, TCDLTn, TCUNIn, TCTYPn, and TCRVLn for pixel lists +* - jCRPXn, iCDLTn, iCUNIn, iCTYPn, and iCRVLn for bintables and +* - TCRPXn, TCDLTn, TCUNIn, TCTYPn, and TCRVLn for pixel lists * -* for use without an alternate version specifier. However, because -* of the eight-character keyword constraint, in order to accommodate -* column numbers greater than 99 WCS Paper I also defined +* for use without an alternate version specifier. However, +* because of the eight-character keyword constraint, in order to +* accommodate column numbers greater than 99 WCS Paper I also +* defined * -* - jCRPna, iCDEna, iCUNna, iCTYna and iCRVna for bintables and -* - TCRPna, TCDEna, TCUNna, TCTYna and TCRVna for pixel lists +* - jCRPna, iCDEna, iCUNna, iCTYna and iCRVna for bintables and +* - TCRPna, TCDEna, TCUNna, TCTYna and TCRVna for pixel lists * -* for use with an alternate version specifier (the "a"). Like the -* PC, CD, PV, and PS keywords there is an obvious tendency to -* confuse these two forms for column numbers up to 99. It is very -* unlikely that any parser would reject keywords in the first set -* with a non-blank alternate version specifier so this usage is -* considered to be safe and is recommended. +* for use with an alternate version specifier (the "a"). Like +* the PC, CD, PV, and PS keywords there is an obvious tendency to +* confuse these two forms for column numbers up to 99. It is +* very unlikely that any parser would reject keywords in the +* first set with a non-blank alternate version specifier so this +* usage is considered to be safe and is recommended. * -* - WCSHDO_CNAMna: WCS Papers I and III defined +* - WCSHDO_CNAMna: WCS Papers I and III defined * -* - iCNAna, iCRDna, and iCSYna for bintables and -* - TCNAna, TCRDna, and TCSYna for pixel lists +* - iCNAna, iCRDna, and iCSYna for bintables and +* - TCNAna, TCRDna, and TCSYna for pixel lists * -* By analogy with the above, the long forms would be +* By analogy with the above, the long forms would be * -* - iCNAMna, iCRDEna, and iCSYEna for bintables and -* - TCNAMna, TCRDEna, and TCSYEna for pixel lists +* - iCNAMna, iCRDEna, and iCSYEna for bintables and +* - TCNAMna, TCRDEna, and TCSYEna for pixel lists * -* Note that these keywords provide auxiliary information only, none -* of them are needed to compute world coordinates. This usage is -* potentially unsafe and is not recommended at this time. +* Note that these keywords provide auxiliary information only, +* none of them are needed to compute world coordinates. This +* usage is potentially unsafe and is not recommended at this +* time. * -* - WCSHDO_WCSNna: In light of wcsbth() note 4, write WCSNna instead of -* TWCSna for pixel lists. While wcsbth() treats WCSNna and TWCSna -* as equivalent, other parsers may not. Consequently, this usage -* is potentially unsafe and is not recommended at this time. +* - WCSHDO_WCSNna: In light of wcsbth() note 4, write WCSNna instead of +* TWCSna for pixel lists. While wcsbth() treats WCSNna and +* TWCSna as equivalent, other parsers may not. Consequently, +* this usage is potentially unsafe and is not recommended at this +* time. * * * Global variable: const char *wcshdr_errmsg[] - Status return messages @@ -1061,46 +1285,60 @@ extern "C" { #define WCSHDR_none 0x00000000 #define WCSHDR_all 0x000FFFFF #define WCSHDR_reject 0x10000000 +#define WCSHDR_strict 0x20000000 #define WCSHDR_CROTAia 0x00000001 -#define WCSHDR_EPOCHa 0x00000002 -#define WCSHDR_VELREFa 0x00000004 -#define WCSHDR_CD00i00j 0x00000008 -#define WCSHDR_PC00i00j 0x00000010 -#define WCSHDR_PROJPn 0x00000020 -#define WCSHDR_RADECSYS 0x00000040 -#define WCSHDR_VSOURCE 0x00000080 -#define WCSHDR_DOBSn 0x00000100 -#define WCSHDR_LONGKEY 0x00000200 -#define WCSHDR_CNAMn 0x00000400 -#define WCSHDR_AUXIMG 0x00000800 -#define WCSHDR_ALLIMG 0x00001000 +#define WCSHDR_VELREFa 0x00000002 +#define WCSHDR_CD00i00j 0x00000004 +#define WCSHDR_PC00i00j 0x00000008 +#define WCSHDR_PROJPn 0x00000010 +#define WCSHDR_CD0i_0ja 0x00000020 +#define WCSHDR_PC0i_0ja 0x00000040 +#define WCSHDR_PV0i_0ma 0x00000080 +#define WCSHDR_PS0i_0ma 0x00000100 +#define WCSHDR_DOBSn 0x00000200 +#define WCSHDR_OBSGLBHn 0x00000400 +#define WCSHDR_RADECSYS 0x00000800 +#define WCSHDR_EPOCHa 0x00001000 +#define WCSHDR_VSOURCE 0x00002000 +#define WCSHDR_DATEREF 0x00004000 +#define WCSHDR_LONGKEY 0x00008000 +#define WCSHDR_CNAMn 0x00010000 +#define WCSHDR_AUXIMG 0x00020000 +#define WCSHDR_ALLIMG 0x00040000 -#define WCSHDR_IMGHEAD 0x00010000 -#define WCSHDR_BIMGARR 0x00020000 -#define WCSHDR_PIXLIST 0x00040000 +#define WCSHDR_IMGHEAD 0x00100000 +#define WCSHDR_BIMGARR 0x00200000 +#define WCSHDR_PIXLIST 0x00400000 -#define WCSHDO_none 0x00 -#define WCSHDO_all 0xFF -#define WCSHDO_safe 0x0F -#define WCSHDO_DOBSn 0x01 -#define WCSHDO_TPCn_ka 0x02 -#define WCSHDO_PVn_ma 0x04 -#define WCSHDO_CRPXna 0x08 -#define WCSHDO_CNAMna 0x10 -#define WCSHDO_WCSNna 0x20 +#define WCSHDO_none 0x00000 +#define WCSHDO_all 0x000FF +#define WCSHDO_safe 0x0000F +#define WCSHDO_DOBSn 0x00001 +#define WCSHDO_TPCn_ka 0x00002 +#define WCSHDO_PVn_ma 0x00004 +#define WCSHDO_CRPXna 0x00008 +#define WCSHDO_CNAMna 0x00010 +#define WCSHDO_WCSNna 0x00020 +#define WCSHDO_P12 0x01000 +#define WCSHDO_P13 0x02000 +#define WCSHDO_P14 0x04000 +#define WCSHDO_P15 0x08000 +#define WCSHDO_P16 0x10000 +#define WCSHDO_P17 0x20000 +#define WCSHDO_EFMT 0x40000 extern const char *wcshdr_errmsg[]; enum wcshdr_errmsg_enum { - WCSHDRERR_SUCCESS = 0, /* Success. */ - WCSHDRERR_NULL_POINTER = 1, /* Null wcsprm pointer passed. */ - WCSHDRERR_MEMORY = 2, /* Memory allocation failed. */ - WCSHDRERR_BAD_COLUMN = 3, /* Invalid column selection. */ - WCSHDRERR_PARSER = 4, /* Fatal error returned by Flex - parser. */ - WCSHDRERR_BAD_TABULAR_PARAMS = 5 /* Invalid tabular parameters. */ + WCSHDRERR_SUCCESS = 0, // Success. + WCSHDRERR_NULL_POINTER = 1, // Null wcsprm pointer passed. + WCSHDRERR_MEMORY = 2, // Memory allocation failed. + WCSHDRERR_BAD_COLUMN = 3, // Invalid column selection. + WCSHDRERR_PARSER = 4, // Fatal error returned by Flex + // parser. + WCSHDRERR_BAD_TABULAR_PARAMS = 5 // Invalid tabular parameters. }; int wcspih(char *header, int nkeyrec, int relax, int ctrl, int *nreject, @@ -1117,11 +1355,11 @@ int wcsbdx(int nwcs, struct wcsprm **wcs, int type, short alts[1000][28]); int wcsvfree(int *nwcs, struct wcsprm **wcs); -int wcshdo(int relax, struct wcsprm *wcs, int *nkeyrec, char **header); +int wcshdo(int ctrl, struct wcsprm *wcs, int *nkeyrec, char **header); #ifdef __cplusplus } #endif -#endif /* WCSLIB_WCSHDR */ +#endif // WCSLIB_WCSHDR diff --git a/cextern/wcslib/C/wcslib.h b/cextern/wcslib/C/wcslib.h index bc28434c7857..7323f6ae7e87 100644 --- a/cextern/wcslib/C/wcslib.h +++ b/cextern/wcslib/C/wcslib.h @@ -1,7 +1,6 @@ /*============================================================================ - - WCSLIB 4.14 - an implementation of the FITS WCS standard. - Copyright (C) 1995-2012, Mark Calabretta + WCSLIB 8.6 - an implementation of the FITS WCS standard. + Copyright (C) 1995-2026, Mark Calabretta This file is part of WCSLIB. @@ -18,15 +17,14 @@ You should have received a copy of the GNU Lesser General Public License along with WCSLIB. If not, see http://www.gnu.org/licenses. - Direct correspondence concerning WCSLIB to mark@calabretta.id.au - Author: Mark Calabretta, Australia Telescope National Facility, CSIRO. - http://www.atnf.csiro.au/people/Mark.Calabretta - $Id: wcslib.h,v 4.14 2012/07/13 10:02:27 cal103 Exp $ + http://www.atnf.csiro.au/computing/software/wcs + $Id: wcslib.h,v 8.6 2026/03/29 13:53:56 mcalabre Exp $ *============================================================================= * -* WCSLIB 4.14 - C routines that implement the FITS World Coordinate System -* (WCS) standard. +* WCSLIB 8.6 - C routines that implement the FITS World Coordinate System +* (WCS) standard. Refer to the README file provided with WCSLIB for an +* overview of the library. * * Summary of wcslib.h * ------------------- @@ -39,6 +37,7 @@ #define WCSLIB_WCSLIB #include "cel.h" +#include "dis.h" #include "fitshdr.h" #include "lin.h" #include "log.h" @@ -56,5 +55,6 @@ #include "wcstrig.h" #include "wcsunits.h" #include "wcsutil.h" +#include "wtbarr.h" -#endif /* WCSLIB_WCSLIB */ +#endif // WCSLIB_WCSLIB diff --git a/cextern/wcslib/C/wcsmath.h b/cextern/wcslib/C/wcsmath.h index 40f177289c79..fdba5602c450 100644 --- a/cextern/wcslib/C/wcsmath.h +++ b/cextern/wcslib/C/wcsmath.h @@ -1,7 +1,6 @@ /*============================================================================ - - WCSLIB 4.14 - an implementation of the FITS WCS standard. - Copyright (C) 1995-2012, Mark Calabretta + WCSLIB 8.6 - an implementation of the FITS WCS standard. + Copyright (C) 1995-2026, Mark Calabretta This file is part of WCSLIB. @@ -18,13 +17,16 @@ You should have received a copy of the GNU Lesser General Public License along with WCSLIB. If not, see http://www.gnu.org/licenses. - Direct correspondence concerning WCSLIB to mark@calabretta.id.au - Author: Mark Calabretta, Australia Telescope National Facility, CSIRO. - http://www.atnf.csiro.au/people/Mark.Calabretta - $Id: wcsmath.h,v 4.14 2012/07/13 10:02:27 cal103 Exp $ + http://www.atnf.csiro.au/computing/software/wcs + $Id: wcsmath.h,v 8.6 2026/03/29 13:53:56 mcalabre Exp $ *============================================================================= * +* WCSLIB 8.6 - C routines that implement the FITS World Coordinate System +* (WCS) standard. Refer to the README file provided with WCSLIB for an +* overview of the library. +* +* * Summary of wcsmath.h * -------------------- * Definition of mathematical constants used by WCSLIB. @@ -67,4 +69,4 @@ #define UNDEFINED 987654321.0e99 #define undefined(value) (value == UNDEFINED) -#endif /* WCSLIB_WCSMATH */ +#endif // WCSLIB_WCSMATH diff --git a/cextern/wcslib/C/wcspih.l b/cextern/wcslib/C/wcspih.l index d47e6938eb17..00753d8ce688 100644 --- a/cextern/wcslib/C/wcspih.l +++ b/cextern/wcslib/C/wcspih.l @@ -1,7 +1,6 @@ /*============================================================================ - - WCSLIB 4.14 - an implementation of the FITS WCS standard. - Copyright (C) 1995-2012, Mark Calabretta + WCSLIB 8.6 - an implementation of the FITS WCS standard. + Copyright (C) 1995-2026, Mark Calabretta This file is part of WCSLIB. @@ -18,11 +17,9 @@ You should have received a copy of the GNU Lesser General Public License along with WCSLIB. If not, see http://www.gnu.org/licenses. - Direct correspondence concerning WCSLIB to mark@calabretta.id.au - Author: Mark Calabretta, Australia Telescope National Facility, CSIRO. - http://www.atnf.csiro.au/people/Mark.Calabretta - $Id: wcspih.l,v 4.14 2012/07/13 10:02:27 cal103 Exp $ + http://www.atnf.csiro.au/computing/software/wcs + $Id: wcspih.l,v 8.6 2026/03/29 13:53:56 mcalabre Exp $ *============================================================================= * * wcspih.l is a Flex description file containing the definition of a lexical @@ -68,12 +65,21 @@ /* Options. */ %option full %option never-interactive +%option noinput %option noyywrap %option outfile="wcspih.c" %option prefix="wcspih" +%option reentrant +%option extra-type="struct wcspih_extra *" /* Indices for parameterized keywords. */ -I0 [0-9] +Z1 [0-9] +Z2 [0-9]{2} +Z3 [0-9]{3} +Z4 [0-9]{4} +Z5 [0-9]{5} +Z6 [0-9]{6} + I1 [1-9] I2 [1-9][0-9] I3 [1-9][0-9]{2} @@ -84,361 +90,479 @@ ALT [ A-Z] /* Keyvalue data types. */ INTEGER [+-]?[0-9]+ -FLOAT [+-]?([0-9]+\.?[0-9]*|\.[0-9]+)([eE][+-]?[0-9]+)? +FLOAT [+-]?([0-9]+\.?[0-9]*|\.[0-9]+)([eEdD][+-]?[0-9]+)? STRING '([^']|'')*' +RECORD '[^']*' +FIELD [a-zA-Z_][a-zA-Z_0-9.]* + +/* Inline comment syntax. */ +INLINE " "*(\/.*)? /* Exclusive start states. */ -%x CROTAi PROJPn -%x CCCCCia CCi_ja CCi_ma CCCCCCCa CCCCCCCC -%x VALUE -%x INTEGER_VAL FLOAT_VAL STRING_VAL +%x CCia CCi_ja CCCCCia CCi_ma CCCCCCCa CCCCCCCC +%x CROTAi PROJPn SIP2 SIP3 DSSAMDXY PLTDECSN +%x VALUE INTEGER_VAL FLOAT_VAL FLOAT2_VAL STRING_VAL +%x RECORD_VAL RECFIELD RECCOLON RECVALUE RECEND %x COMMENT %x DISCARD ERROR FLUSH %{ #include #include +#include #include #include #include -#include "wcs.h" -#include "wcshdr.h" #include "wcsmath.h" +#include "wcsprintf.h" #include "wcsutil.h" +#include "dis.h" +#include "wcs.h" +#include "wcshdr.h" + #define INTEGER 0 #define FLOAT 1 -#define STRING 2 +#define FLOAT2 2 +#define STRING 3 +#define RECORD 4 + +#define PRIOR 1 +#define SEQUENT 2 + +#define SIP 1 +#define DSS 2 +#define WAT 3 + +// User data associated with yyscanner. +struct wcspih_extra { + // Values passed to YY_INPUT. + char *hdr; + int nkeyrec; -#define YY_DECL int wcspih(char *header, int nkeyrec, int relax, int ctrl, \ - int *nreject, int *nwcs, struct wcsprm **wcs) + // Used in preempting the call to exit() by yy_fatal_error(). + jmp_buf abort_jmp_env; +}; + +#define YY_DECL int wcspih_scanner(char *header, int nkeyrec, int relax, \ + int ctrl, int *nreject, int *nwcs, struct wcsprm **wcs, yyscan_t yyscanner) #define YY_INPUT(inbuff, count, bufsize) \ { \ - if (wcspih_nkeyrec) { \ - strncpy(inbuff, wcspih_hdr, 80); \ + if (yyextra->nkeyrec) { \ + strncpy(inbuff, yyextra->hdr, 80); \ inbuff[80] = '\n'; \ - wcspih_hdr += 80; \ - wcspih_nkeyrec--; \ + yyextra->hdr += 80; \ + yyextra->nkeyrec--; \ count = 81; \ } else { \ count = YY_NULL; \ } \ } -/* These global variables are required by YY_INPUT. */ -char *wcspih_hdr; -int wcspih_nkeyrec; +// Preempt the call to exit() by yy_fatal_error(). +#define exit(status) longjmp(yyextra->abort_jmp_env, status); + +// Internal helper functions. +static YY_DECL; +static int wcspih_final(int ndp[], int ndq[], int distran, double dsstmp[], + char *wat[], int *nwcs, struct wcsprm **wcs); +static int wcspih_init1(int naxis, int alts[], int dpq[], int npv[], + int nps[], int ndp[], int ndq[], int auxprm, int distran, + int *nwcs, struct wcsprm **wcs); +static void wcspih_pass1(int naxis, int i, int j, char a, int distype, + int alts[], int dpq[], int *npptr); -int wcspih_final(int alts[], double epoch[], double vsource[], int *nwcs, - struct wcsprm **wcs); -int wcspih_inits(int naxis, int alts[], int npv[], int nps[], int *nwcs, - struct wcsprm **wcs); -void wcspih_naxes(int naxis, int i, int j, char a, int alts[], int *npptr); +static int wcspih_jdref(double *wptr, const double *jdref); +static int wcspih_jdrefi(double *wptr, const double *jdrefi); +static int wcspih_jdreff(double *wptr, const double *jdreff); +static int wcspih_epoch(double *wptr, const double *epoch); +static int wcspih_vsource(double *wptr, const double *vsource); -/* Used in preempting the call to exit() by yy_fatal_error(). */ -jmp_buf wcspih_abort_jmp_env; -#define exit(status) longjmp(wcspih_abort_jmp_env, status) +static int wcspih_timepixr(double timepixr); %} %% - /* Keyword indices, as used in the WCS papers, e.g. PCi_ja, PVi_ma. */ - char a; - int i, j, m; - - char *cptr, *errmsg, errtxt[80], *hptr, *keep; - int altlin, alts[27], ialt, idx, ipx, ix, jx, naxis, *npptr, - nps[27], npv[27], pass, status, valtype, voff; - double epoch[27], vsource[27]; - void *vptr, *wptr; - struct wcsprm *wcsp; - int yylex_destroy(void); - - naxis = 0; - for (ialt = 0; ialt < 27; ialt++) { + int p, q; + char *errmsg, errtxt[80], *keyname, strtmp[80], *wat[2], *watstr; + int alts[27], dpq[27], inttmp, ndp[27], ndq[27], nps[27], npv[27], + rectype; + double dbltmp, dbl2tmp[2], dsstmp[20]; + struct auxprm auxtem; + struct disprm distem; + struct wcsprm wcstem; + + int naxis = 0; + for (int ialt = 0; ialt < 27; ialt++) { alts[ialt] = 0; - npv[ialt] = 0; - nps[ialt] = 0; - epoch[ialt] = UNDEFINED; - vsource[ialt] = UNDEFINED; + dpq[ialt] = 0; + npv[ialt] = 0; + nps[ialt] = 0; + ndp[ialt] = 0; + ndq[ialt] = 0; } - /* Parameters used to implement YY_INPUT. */ - wcspih_hdr = header; - wcspih_nkeyrec = nkeyrec; + // Our handle on the input stream. + char *keyrec = header; + char *hptr = header; + char *keep = 0x0; - /* Our handle on the input stream. */ - hptr = header; - keep = 0x0; + // For keeping tallies of keywords found. *nreject = 0; - - /* Keyword parameters. */ - i = j = m = 0; - a = ' '; - - /* For decoding the keyvalue. */ - valtype = -1; - idx = -1; - vptr = 0x0; - - /* For keywords that require special handling. */ - altlin = 0; - npptr = 0x0; - - /* The data structures produced. */ + int nvalid = 0; + int nother = 0; + + // If strict, then also reject. + if (relax & WCSHDR_strict) relax |= WCSHDR_reject; + + // Keyword indices, as used in the WCS papers, e.g. PCi_ja, PVi_ma. + int i = 0; + int j = 0; + int m = 0; + char a = ' '; + + // For decoding the keyvalue. + int valtype = -1; + int distype = 0; + void *vptr = 0x0; + + // For keywords that require special handling. + int altlin = 0; + int *npptr = 0x0; + int (*chekval)(double) = 0x0; + int (*special)(double *, const double *) = 0x0; + int auxprm = 0; + int naux = 0; + int distran = 0; + int sipflag = 0; + int dssflag = 0; + int watflag = 0; + int watn = 0; + + // The data structures produced. *nwcs = 0; *wcs = 0x0; - pass = 1; + // Control variables. + int ipass = 1; + int npass = 2; - /* Return here via longjmp() invoked by yy_fatal_error(). */ - if (setjmp(wcspih_abort_jmp_env)) { - return 3; + // User data associated with yyscanner. + yyextra->hdr = header; + yyextra->nkeyrec = nkeyrec; + + // Return here via longjmp() invoked by yy_fatal_error(). + if (setjmp(yyextra->abort_jmp_env)) { + return WCSHDRERR_PARSER; } BEGIN(INITIAL); -^NAXIS" = "" "*{INTEGER} { - if (pass == 1) { +^NAXIS" = "" "*{INTEGER}{INLINE} { + keyname = "NAXISn"; + + if (ipass == 1) { sscanf(yytext, "NAXIS = %d", &naxis); - } + if (naxis < 0) naxis = 0; + BEGIN(FLUSH); - if (naxis < 0) { - errmsg = errtxt; - sprintf(errmsg, "Negative value of NAXIS ignored: %d", naxis); - naxis = 0; - BEGIN(ERROR); } else { - BEGIN(DISCARD); + sscanf(yytext, "NAXIS = %d", &i); + + if (i < 0) { + errmsg = "negative value of NAXIS ignored"; + BEGIN(ERROR); + } else { + BEGIN(DISCARD); + } } } ^WCSAXES{ALT}=" "" "*{INTEGER} { - if (pass == 1) { - sscanf(yytext, "WCSAXES%c= %d", &a, &i); - wcspih_naxes(naxis, i, 0, a, alts, 0); + sscanf(yytext, "WCSAXES%c= %d", &a, &i); + + if (i < 0) { + errmsg = "negative value of WCSAXESa ignored"; + BEGIN(ERROR); + + } else { + valtype = INTEGER; + vptr = 0x0; + + keyname = "WCSAXESa"; + BEGIN(COMMENT); } - BEGIN(FLUSH); } ^CRPIX { valtype = FLOAT; - if (pass == 2) vptr = &((*wcs)->crpix); + vptr = &(wcstem.crpix); + + keyname = "CRPIXja"; BEGIN(CCCCCia); } ^PC { valtype = FLOAT; - if (pass == 2) vptr = &((*wcs)->pc); + vptr = &(wcstem.pc); altlin = 1; + + keyname = "PCi_ja"; BEGIN(CCi_ja); } ^CD { valtype = FLOAT; - if (pass == 2) vptr = &((*wcs)->cd); + vptr = &(wcstem.cd); altlin = 2; + + keyname = "CDi_ja"; BEGIN(CCi_ja); } ^CDELT { valtype = FLOAT; - if (pass == 2) vptr = &((*wcs)->cdelt); + vptr = &(wcstem.cdelt); + + keyname = "CDELTia"; BEGIN(CCCCCia); } ^CROTA { valtype = FLOAT; - if (pass == 2) vptr = &((*wcs)->crota); + vptr = &(wcstem.crota); altlin = 4; + + keyname = "CROTAn"; BEGIN(CROTAi); } ^CUNIT { valtype = STRING; - if (pass == 2) vptr = &((*wcs)->cunit); + vptr = &(wcstem.cunit); + + keyname = "CUNITia"; BEGIN(CCCCCia); } ^CTYPE { valtype = STRING; - if (pass == 2) vptr = &((*wcs)->ctype); + vptr = &(wcstem.ctype); + + keyname = "CTYPEia"; BEGIN(CCCCCia); } ^CRVAL { valtype = FLOAT; - if (pass == 2) vptr = &((*wcs)->crval); + vptr = &(wcstem.crval); + + keyname = "CRVALia"; BEGIN(CCCCCia); } ^LONPOLE { valtype = FLOAT; - if (pass == 2) vptr = &((*wcs)->lonpole); + vptr = &(wcstem.lonpole); + + keyname = "LONPOLEa"; BEGIN(CCCCCCCa); } ^LATPOLE { valtype = FLOAT; - if (pass == 2) vptr = &((*wcs)->latpole); + vptr = &(wcstem.latpole); + + keyname = "LATPOLEa"; BEGIN(CCCCCCCa); } ^RESTFRQ { valtype = FLOAT; - if (pass == 2) vptr = &((*wcs)->restfrq); + vptr = &(wcstem.restfrq); + + keyname = "RESTFRQa"; BEGIN(CCCCCCCa); } ^RESTFREQ { - valtype = FLOAT; - if (pass == 2) vptr = &((*wcs)->restfrq); - unput(' '); - BEGIN(CCCCCCCa); + if (relax & WCSHDR_strict) { + errmsg = "the RESTFREQ keyword is deprecated, use RESTFRQa"; + BEGIN(ERROR); + + } else { + valtype = FLOAT; + vptr = &(wcstem.restfrq); + + unput(' '); + + keyname = "RESTFREQ"; + BEGIN(CCCCCCCa); + } } ^RESTWAV { valtype = FLOAT; - if (pass == 2) vptr = &((*wcs)->restwav); + vptr = &(wcstem.restwav); + + keyname = "RESTWAVa"; BEGIN(CCCCCCCa); } ^PV { valtype = FLOAT; - if (pass == 2) vptr = &((*wcs)->pv); - npptr = npv; + vptr = &(wcstem.pv); + npptr = npv; + + keyname = "PVi_ma"; BEGIN(CCi_ma); } ^PROJP { valtype = FLOAT; - if (pass == 2) vptr = &((*wcs)->pv); - npptr = npv; + vptr = &(wcstem.pv); + npptr = npv; + + keyname = "PROJPn"; BEGIN(PROJPn); } ^PS { valtype = STRING; - if (pass == 2) vptr = &((*wcs)->ps); - npptr = nps; + vptr = &(wcstem.ps); + npptr = nps; + + keyname = "PSi_ma"; BEGIN(CCi_ma); } +^VELREF{ALT}" " { + sscanf(yytext, "VELREF%c", &a); + + if (relax & WCSHDR_strict) { + errmsg = "the VELREF keyword is deprecated, use SPECSYSa"; + BEGIN(ERROR); + + } else if ((a == ' ') || (relax & WCSHDR_VELREFa)) { + valtype = INTEGER; + vptr = &(wcstem.velref); + + unput(a); + + keyname = "VELREF"; + BEGIN(CCCCCCCa); + + } else if (relax & WCSHDR_reject) { + errmsg = "VELREF keyword may not have an alternate version code"; + BEGIN(ERROR); + + } else { + BEGIN(DISCARD); + } + } + ^CNAME { valtype = STRING; - if (pass == 2) vptr = &((*wcs)->cname); + vptr = &(wcstem.cname); + + keyname = "CNAMEia"; BEGIN(CCCCCia); } ^CRDER { valtype = FLOAT; - if (pass == 2) vptr = &((*wcs)->crder); + vptr = &(wcstem.crder); + + keyname = "CRDERia"; BEGIN(CCCCCia); } ^CSYER { valtype = FLOAT; - if (pass == 2) vptr = &((*wcs)->csyer); + vptr = &(wcstem.csyer); + + keyname = "CSYERia"; BEGIN(CCCCCia); } -^DATE-AVG { - valtype = STRING; - if (pass == 2) vptr = (*wcs)->dateavg; - if (ctrl < -10) keep = wcspih_hdr - 80; - BEGIN(CCCCCCCC); - } - -^DATE-OBS { - valtype = STRING; - if (pass == 2) vptr = (*wcs)->dateobs; - if (ctrl < -10) keep = wcspih_hdr - 80; - BEGIN(CCCCCCCC); - } - -^EPOCH{ALT}" " { - sscanf(yytext, "EPOCH%c", &a); - - if (a == ' ' || relax & WCSHDR_EPOCHa) { - valtype = FLOAT; - if (pass == 2) { - vptr = epoch; - if (a >= 'A') { - vptr = (void *)((double *)vptr + alts[a-'A'+1]); - } - } - - unput(' '); - BEGIN(CCCCCCCa); - - } else if (relax & WCSHDR_reject) { - errmsg = "EPOCH keyword may not have an alternate version code"; - BEGIN(ERROR); +^CZPHS { + valtype = FLOAT; + vptr = &(wcstem.czphs); - } else { - BEGIN(DISCARD); - } + keyname = "CZPHSia"; + BEGIN(CCCCCia); } -^EQUINOX { +^CPERI { valtype = FLOAT; - if (pass == 2) vptr = &((*wcs)->equinox); - BEGIN(CCCCCCCa); + vptr = &(wcstem.cperi); + + keyname = "CPERIia"; + BEGIN(CCCCCia); } -^MJD-AVG" " { - valtype = FLOAT; - if (pass == 2) vptr = &((*wcs)->mjdavg); - if (ctrl < -10) keep = wcspih_hdr - 80; - BEGIN(CCCCCCCC); +^WCSNAME { + valtype = STRING; + vptr = wcstem.wcsname; + + keyname = "WCSNAMEa"; + BEGIN(CCCCCCCa); } -^MJD-OBS" " { - valtype = FLOAT; - if (pass == 2) vptr = &((*wcs)->mjdobs); - if (ctrl < -10) keep = wcspih_hdr - 80; +^TIMESYS" " { + valtype = STRING; + vptr = wcstem.timesys; + + keyname = "TIMESYS"; BEGIN(CCCCCCCC); } -^OBSGEO-X { - valtype = FLOAT; - if (pass == 2) vptr = (*wcs)->obsgeo; - if (ctrl < -10) keep = wcspih_hdr - 80; +^TREFPOS" " { + valtype = STRING; + vptr = wcstem.trefpos; + + keyname = "TREFPOS"; BEGIN(CCCCCCCC); } -^OBSGEO-Y { - valtype = FLOAT; - if (pass == 2) vptr = (*wcs)->obsgeo + 1; - if (ctrl < -10) keep = wcspih_hdr - 80; +^TREFDIR" " { + valtype = STRING; + vptr = wcstem.trefdir; + + keyname = "TREFDIR"; BEGIN(CCCCCCCC); } -^OBSGEO-Z { - valtype = FLOAT; - if (pass == 2) vptr = (*wcs)->obsgeo + 2; - if (ctrl < -10) keep = wcspih_hdr - 80; +^PLEPHEM" " { + valtype = STRING; + vptr = wcstem.plephem; + + keyname = "PLEPHEM"; BEGIN(CCCCCCCC); } -^RADESYS { +^TIMEUNIT { valtype = STRING; - if (pass == 2) vptr = (*wcs)->radesys; - BEGIN(CCCCCCCa); + vptr = wcstem.timeunit; + + keyname = "TIMEUNIT"; + BEGIN(CCCCCCCC); } -^RADECSYS { - if (relax & WCSHDR_RADECSYS) { +^DATEREF" " | +^DATE-REF { + if ((yytext[4] == 'R') || (relax & WCSHDR_DATEREF)) { valtype = STRING; - if (pass == 2) vptr = (*wcs)->radesys; - unput(' '); - BEGIN(CCCCCCCa); + vptr = wcstem.dateref; + + keyname = "DATEREF"; + BEGIN(CCCCCCCC); } else if (relax & WCSHDR_reject) { - errmsg = "RADECSYS is non-standard, use RADESYSa"; + errmsg = "the DATE-REF keyword is non-standard"; BEGIN(ERROR); } else { @@ -446,48 +570,17 @@ jmp_buf wcspih_abort_jmp_env; } } -^SPECSYS { - valtype = STRING; - if (pass == 2) vptr = (*wcs)->specsys; - BEGIN(CCCCCCCa); - } - -^SSYSOBS { - valtype = STRING; - if (pass == 2) vptr = (*wcs)->ssysobs; - BEGIN(CCCCCCCa); - } - -^SSYSSRC { - valtype = STRING; - if (pass == 2) vptr = (*wcs)->ssyssrc; - BEGIN(CCCCCCCa); - } - -^VELANGL { - valtype = FLOAT; - if (pass == 2) vptr = &((*wcs)->velangl); - BEGIN(CCCCCCCa); - } - -^VELOSYS { - valtype = FLOAT; - if (pass == 2) vptr = &((*wcs)->velosys); - BEGIN(CCCCCCCa); - } - -^VELREF{ALT}" " { - sscanf(yytext, "VELREF%c", &a); - - if (a == ' ' || relax & WCSHDR_VELREFa) { - valtype = INTEGER; - if (pass == 2) vptr = &((*wcs)->velref); +^MJDREF" " | +^MJD-REF" " { + if ((yytext[3] == 'R') || (relax & WCSHDR_DATEREF)) { + valtype = FLOAT2; + vptr = wcstem.mjdref; - unput(a); - BEGIN(CCCCCCCa); + keyname = "MJDREF"; + BEGIN(CCCCCCCC); } else if (relax & WCSHDR_reject) { - errmsg = "VELREF keyword may not have an alternate version code"; + errmsg = "the MJD-REF keyword is non-standard"; BEGIN(ERROR); } else { @@ -495,23 +588,18 @@ jmp_buf wcspih_abort_jmp_env; } } -^VSOURCE{ALT} { - sscanf(yytext, "VSOURCE%c", &a); - - if (relax & WCSHDR_VSOURCE) { +^MJDREFI" " | +^MJD-REFI { + if ((yytext[3] == 'R') || (relax & WCSHDR_DATEREF)) { + // Actually integer, but treated as float. valtype = FLOAT; - if (pass == 2) { - vptr = vsource; - if (a >= 'A') { - vptr = (void *)((double *)vptr + alts[a-'A'+1]); - } - } + vptr = wcstem.mjdref; - unput(' '); - BEGIN(CCCCCCCa); + keyname = "MJDREFI"; + BEGIN(CCCCCCCC); } else if (relax & WCSHDR_reject) { - errmsg = "Deprecated VSOURCEa keyword rejected"; + errmsg = "the MJD-REFI keyword is non-standard"; BEGIN(ERROR); } else { @@ -519,92 +607,36 @@ jmp_buf wcspih_abort_jmp_env; } } -^WCSNAME { - valtype = STRING; - if (pass == 2) vptr = (*wcs)->wcsname; - BEGIN(CCCCCCCa); - } - -^ZSOURCE { - valtype = FLOAT; - if (pass == 2) vptr = &((*wcs)->zsource); - BEGIN(CCCCCCCa); - } - -^END" "{77} { - yyless(0); - if (wcspih_nkeyrec) { - wcspih_nkeyrec = 0; - errmsg = "Keyrecords following the END keyrecord were ignored"; +^MJDREFF" " | +^MJD-REFF { + if ((yytext[3] == 'R') || (relax & WCSHDR_DATEREF)) { + valtype = FLOAT; + vptr = wcstem.mjdref + 1; + + keyname = "MJDREFF"; + BEGIN(CCCCCCCC); + + } else if (relax & WCSHDR_reject) { + errmsg = "the MJD-REFF keyword is non-standard"; BEGIN(ERROR); + } else { BEGIN(DISCARD); } } -^. { - BEGIN(DISCARD); - } - -{I1}{ALT}" " | -{I2}{ALT} { - sscanf(yytext, "%d%c", &i, &a); - idx = i - 1; - BEGIN(VALUE); - } - -{I3} { - /* Invalid axis number will be caught by . */ - sscanf(yytext, "%3d", &i); - BEGIN(VALUE); - } - -. { - BEGIN(DISCARD); - } - -{I1}_{I1}{ALT}" " | -{I1}_{I2}{ALT}" " | -{I2}_{I1}{ALT}" " | -{I2}_{I2}{ALT} { - sscanf(yytext, "%d_%d%c", &i, &j, &a); - if (pass == 2) { - wcsp = *wcs; - if (a != ' ') { - wcsp += alts[a-'A'+1]; - } +^JDREF" " | +^JD-REF" " { + if ((yytext[2] == 'R') || (relax & WCSHDR_DATEREF)) { + valtype = FLOAT2; + vptr = wcstem.mjdref; + special = wcspih_jdref; - idx = (i-1)*(wcsp->naxis) + j - 1; - } - BEGIN(VALUE); - } - -{I1}_{I3}{ALT} | -{I3}_{I1}{ALT} | -{I1}_{I4} | -{I2}_{I3} | -{I3}_{I2} | -{I4}_{I1} { - /* Invalid axis numbers will be caught by . */ - sscanf(yytext, "%d_%d", &i, &j); - BEGIN(VALUE); - } - -{I0}{6} { - /* This covers the defunct forms CD00i00j and PC00i00j. */ - if (((relax & WCSHDR_PC00i00j) && (altlin == 1)) || - ((relax & WCSHDR_CD00i00j) && (altlin == 2))) { - sscanf(yytext, "%3d%3d", &i, &j); - a = ' '; - if (pass == 2) { - idx = (i-1)*((*wcs)->naxis) + j - 1; - } - BEGIN(VALUE); + keyname = "JDREF"; + BEGIN(CCCCCCCC); } else if (relax & WCSHDR_reject) { - errmsg = errtxt; - sprintf(errmsg, "Defunct form of %si_ja keyword", - (altlin==1) ? "PC" : "CD"); + errmsg = "the JD-REF keyword is non-standard"; BEGIN(ERROR); } else { @@ -612,19 +644,19 @@ jmp_buf wcspih_abort_jmp_env; } } -. { - BEGIN(DISCARD); - } - -{I1}{ALT}" " | -{I2}{ALT} { - sscanf(yytext, "%d%c", &i, &a); - if (a == ' ' || relax & WCSHDR_CROTAia) { - idx = i - 1; - BEGIN(VALUE); +^JDREFI" " | +^JD-REFI { + if ((yytext[2] == 'R') || (relax & WCSHDR_DATEREF)) { + // Actually integer, but treated as float. + valtype = FLOAT; + vptr = wcstem.mjdref; + special = wcspih_jdrefi; + + keyname = "JDREFI"; + BEGIN(CCCCCCCC); } else if (relax & WCSHDR_reject) { - errmsg = "CROTAn keyword may not have an alternate version code"; + errmsg = "the JD-REFI keyword is non-standard"; BEGIN(ERROR); } else { @@ -632,68 +664,1203 @@ jmp_buf wcspih_abort_jmp_env; } } -{I3} { - sscanf(yytext, "%d", &i); - a = ' '; - idx = i - 1; - BEGIN(VALUE); - } - -. { +^JDREFF" " | +^JD-REFF { + if ((yytext[2] == 'R') || (relax & WCSHDR_DATEREF)) { + valtype = FLOAT; + vptr = wcstem.mjdref; + special = wcspih_jdreff; + + keyname = "JDREFF"; + BEGIN(CCCCCCCC); + + } else if (relax & WCSHDR_reject) { + errmsg = "the JD-REFF keyword is non-standard"; + BEGIN(ERROR); + + } else { + BEGIN(DISCARD); + } + } + +^TIMEOFFS { + valtype = FLOAT; + vptr = &(wcstem.timeoffs); + + keyname = "TIMEOFFS"; + BEGIN(CCCCCCCC); + } + +^DATE-OBS { + valtype = STRING; + vptr = wcstem.dateobs; + if (ctrl < -10) keep = keyrec; + + keyname = "DATE-OBS"; + BEGIN(CCCCCCCC); + } + +^DATE-BEG { + valtype = STRING; + vptr = wcstem.datebeg; + if (ctrl < -10) keep = keyrec; + + keyname = "DATE-BEG"; + BEGIN(CCCCCCCC); + } + +^DATE-AVG { + valtype = STRING; + vptr = wcstem.dateavg; + if (ctrl < -10) keep = keyrec; + + keyname = "DATE-AVG"; + BEGIN(CCCCCCCC); + } + +^DATE-END { + valtype = STRING; + vptr = wcstem.dateend; + if (ctrl < -10) keep = keyrec; + + keyname = "DATE-END"; + BEGIN(CCCCCCCC); + } + +^MJD-OBS" " { + valtype = FLOAT; + vptr = &(wcstem.mjdobs); + if (ctrl < -10) keep = keyrec; + + keyname = "MJD-OBS"; + BEGIN(CCCCCCCC); + } + +^MJD-BEG" " { + valtype = FLOAT; + vptr = &(wcstem.mjdbeg); + if (ctrl < -10) keep = keyrec; + + keyname = "MJD-BEG"; + BEGIN(CCCCCCCC); + } + +^MJD-AVG" " { + valtype = FLOAT; + vptr = &(wcstem.mjdavg); + if (ctrl < -10) keep = keyrec; + + keyname = "MJD-AVG"; + BEGIN(CCCCCCCC); + } + +^MJD-END" " { + valtype = FLOAT; + vptr = &(wcstem.mjdend); + if (ctrl < -10) keep = keyrec; + + keyname = "MJD-END"; + BEGIN(CCCCCCCC); + } + +^JEPOCH" " { + valtype = FLOAT; + vptr = &(wcstem.jepoch); + if (ctrl < -10) keep = keyrec; + + keyname = "JEPOCH"; + BEGIN(CCCCCCCC); + } + +^BEPOCH" " { + valtype = FLOAT; + vptr = &(wcstem.bepoch); + if (ctrl < -10) keep = keyrec; + + keyname = "BEPOCH"; + BEGIN(CCCCCCCC); + } + +^TSTART" " { + valtype = FLOAT; + vptr = &(wcstem.tstart); + if (ctrl < -10) keep = keyrec; + + keyname = "TSTART"; + BEGIN(CCCCCCCC); + } + +^TSTOP" " { + valtype = FLOAT; + vptr = &(wcstem.tstop); + if (ctrl < -10) keep = keyrec; + + keyname = "TSTOP"; + BEGIN(CCCCCCCC); + } + +^XPOSURE" " { + valtype = FLOAT; + vptr = &(wcstem.xposure); + if (ctrl < -10) keep = keyrec; + + keyname = "XPOSURE"; + BEGIN(CCCCCCCC); + } + +^TELAPSE" " { + valtype = FLOAT; + vptr = &(wcstem.telapse); + if (ctrl < -10) keep = keyrec; + + keyname = "TELAPSE"; + BEGIN(CCCCCCCC); + } + +^TIMSYER" " { + valtype = FLOAT; + vptr = &(wcstem.timsyer); + if (ctrl < -10) keep = keyrec; + + keyname = "TIMSYER"; + BEGIN(CCCCCCCC); + } + +^TIMRDER" " { + valtype = FLOAT; + vptr = &(wcstem.timrder); + if (ctrl < -10) keep = keyrec; + + keyname = "TIMRDER"; + BEGIN(CCCCCCCC); + } + +^TIMEDEL" " { + valtype = FLOAT; + vptr = &(wcstem.timedel); + if (ctrl < -10) keep = keyrec; + + keyname = "TIMEDEL"; + BEGIN(CCCCCCCC); + } + +^TIMEPIXR { + valtype = FLOAT; + vptr = &(wcstem.timepixr); + chekval = wcspih_timepixr; + if (ctrl < -10) keep = keyrec; + + keyname = "TIMEPIXR"; + BEGIN(CCCCCCCC); + } + +^OBSGEO-X { + valtype = FLOAT; + vptr = wcstem.obsgeo; + if (ctrl < -10) keep = keyrec; + + keyname = "OBSGEO-X"; + BEGIN(CCCCCCCC); + } + +^OBSGEO-Y { + valtype = FLOAT; + vptr = wcstem.obsgeo + 1; + if (ctrl < -10) keep = keyrec; + + keyname = "OBSGEO-Y"; + BEGIN(CCCCCCCC); + } + +^OBSGEO-Z { + valtype = FLOAT; + vptr = wcstem.obsgeo + 2; + if (ctrl < -10) keep = keyrec; + + keyname = "OBSGEO-Z"; + BEGIN(CCCCCCCC); + } + +^OBSGEO-L { + valtype = FLOAT; + vptr = wcstem.obsgeo + 3; + if (ctrl < -10) keep = keyrec; + + keyname = "OBSGEO-L"; + BEGIN(CCCCCCCC); + } + +^OBSGEO-B { + valtype = FLOAT; + vptr = wcstem.obsgeo + 4; + if (ctrl < -10) keep = keyrec; + + keyname = "OBSGEO-B"; + BEGIN(CCCCCCCC); + } + +^OBSGEO-H { + valtype = FLOAT; + vptr = wcstem.obsgeo + 5; + if (ctrl < -10) keep = keyrec; + + keyname = "OBSGEO-H"; + BEGIN(CCCCCCCC); + } + +^OBSORBIT { + valtype = STRING; + vptr = wcstem.obsorbit; + + keyname = "OBSORBIT"; + BEGIN(CCCCCCCC); + } + +^RADESYS { + valtype = STRING; + vptr = wcstem.radesys; + + keyname = "RADESYSa"; + BEGIN(CCCCCCCa); + } + +^RADECSYS { + if (relax & WCSHDR_RADECSYS) { + valtype = STRING; + vptr = wcstem.radesys; + + unput(' '); + + keyname = "RADECSYS"; + BEGIN(CCCCCCCa); + + } else if (relax & WCSHDR_reject) { + errmsg = "the RADECSYS keyword is deprecated, use RADESYSa"; + BEGIN(ERROR); + + } else { + BEGIN(DISCARD); + } + } + +^EPOCH{ALT}" " { + sscanf(yytext, "EPOCH%c", &a); + + if (relax & WCSHDR_strict) { + errmsg = "the EPOCH keyword is deprecated, use EQUINOXa"; + BEGIN(ERROR); + + } else if (a == ' ' || relax & WCSHDR_EPOCHa) { + valtype = FLOAT; + vptr = &(wcstem.equinox); + special = wcspih_epoch; + + unput(a); + + keyname = "EPOCH"; + BEGIN(CCCCCCCa); + + } else if (relax & WCSHDR_reject) { + errmsg = "EPOCH keyword may not have an alternate version code"; + BEGIN(ERROR); + + } else { + BEGIN(DISCARD); + } + } + +^EQUINOX { + valtype = FLOAT; + vptr = &(wcstem.equinox); + + keyname = "EQUINOXa"; + BEGIN(CCCCCCCa); + } + +^SPECSYS { + valtype = STRING; + vptr = wcstem.specsys; + + keyname = "SPECSYSa"; + BEGIN(CCCCCCCa); + } + +^SSYSOBS { + valtype = STRING; + vptr = wcstem.ssysobs; + + keyname = "SSYSOBSa"; + BEGIN(CCCCCCCa); + } + +^VELOSYS { + valtype = FLOAT; + vptr = &(wcstem.velosys); + + keyname = "VELOSYSa"; + BEGIN(CCCCCCCa); + } + +^VSOURCE{ALT} { + if (relax & WCSHDR_VSOURCE) { + valtype = FLOAT; + vptr = &(wcstem.zsource); + special = wcspih_vsource; + + yyless(7); + + keyname = "VSOURCEa"; + BEGIN(CCCCCCCa); + + } else if (relax & WCSHDR_reject) { + errmsg = "the VSOURCEa keyword is deprecated, use ZSOURCEa"; + BEGIN(ERROR); + + } else { + BEGIN(DISCARD); + } + } + +^ZSOURCE { + valtype = FLOAT; + vptr = &(wcstem.zsource); + + keyname = "ZSOURCEa"; + BEGIN(CCCCCCCa); + } + +^SSYSSRC { + valtype = STRING; + vptr = wcstem.ssyssrc; + + keyname = "SSYSSRCa"; + BEGIN(CCCCCCCa); + } + +^VELANGL { + valtype = FLOAT; + vptr = &(wcstem.velangl); + + keyname = "VELANGLa"; + BEGIN(CCCCCCCa); + } + +^RSUN_REF { + valtype = FLOAT; + auxprm = 1; + vptr = &(auxtem.rsun_ref); + + keyname = "RSUN_REF"; + BEGIN(CCCCCCCC); + } + +^DSUN_OBS { + valtype = FLOAT; + auxprm = 1; + vptr = &(auxtem.dsun_obs); + + keyname = "DSUN_OBS"; + BEGIN(CCCCCCCC); + } + +^CRLN_OBS { + valtype = FLOAT; + auxprm = 1; + vptr = &(auxtem.crln_obs); + + keyname = "CRLN_OBS"; + BEGIN(CCCCCCCC); + } + +^HGLN_OBS { + valtype = FLOAT; + auxprm = 1; + vptr = &(auxtem.hgln_obs); + + keyname = "HGLN_OBS"; + BEGIN(CCCCCCCC); + } + +^CRLT_OBS | +^HGLT_OBS { + valtype = FLOAT; + auxprm = 1; + vptr = &(auxtem.hglt_obs); + + keyname = "HGLT_OBS"; + BEGIN(CCCCCCCC); + } + +^A_RADIUS { + valtype = FLOAT; + auxprm = 1; + vptr = &(auxtem.a_radius); + + keyname = "A_RADIUS"; + BEGIN(CCCCCCCC); + } + +^B_RADIUS { + valtype = FLOAT; + auxprm = 1; + vptr = &(auxtem.b_radius); + + keyname = "B_RADIUS"; + BEGIN(CCCCCCCC); + } + +^C_RADIUS { + valtype = FLOAT; + auxprm = 1; + vptr = &(auxtem.c_radius); + + keyname = "C_RADIUS"; + BEGIN(CCCCCCCC); + } + +^BLON_OBS { + valtype = FLOAT; + auxprm = 1; + vptr = &(auxtem.blon_obs); + + keyname = "BLON_OBS"; + BEGIN(CCCCCCCC); + } + +^BLAT_OBS { + valtype = FLOAT; + auxprm = 1; + vptr = &(auxtem.blat_obs); + + keyname = "BLAT_OBS"; + BEGIN(CCCCCCCC); + } + +^BDIS_OBS { + valtype = FLOAT; + auxprm = 1; + vptr = &(auxtem.bdis_obs); + + keyname = "BDIS_OBS"; + BEGIN(CCCCCCCC); + } + +^CPDIS { + valtype = STRING; + distype = PRIOR; + vptr = &(distem.dtype); + + keyname = "CPDISja"; + BEGIN(CCCCCia); + } + +^CQDIS { + valtype = STRING; + distype = SEQUENT; + vptr = &(distem.dtype); + + keyname = "CQDISia"; + BEGIN(CCCCCia); + } + +^DP { + valtype = RECORD; + distype = PRIOR; + vptr = &(distem.dp); + npptr = ndp; + + keyname = "DPja"; + BEGIN(CCia); + } + +^DQ { + valtype = RECORD; + distype = SEQUENT; + vptr = &(distem.dp); + npptr = ndq; + + keyname = "DQia"; + BEGIN(CCia); + } + +^CPERR { + valtype = FLOAT; + distype = PRIOR; + vptr = &(distem.maxdis); + + keyname = "CPERRja"; + BEGIN(CCCCCia); + } + +^CQERR { + valtype = FLOAT; + distype = SEQUENT; + vptr = &(distem.maxdis); + + keyname = "CQERRia"; + BEGIN(CCCCCia); + } + +^DVERR { + valtype = FLOAT; + distype = PRIOR; + vptr = &(distem.totdis); + + keyname = "DVERRa"; + BEGIN(CCCCCCCa); + } + +^A_ORDER" " { + // SIP: axis 1 polynomial degree (not stored). + valtype = INTEGER; + distype = PRIOR; + vptr = 0x0; + + i = 1; + a = ' '; + + keyname = "A_ORDER"; + BEGIN(VALUE); + } + +^B_ORDER" " { + // SIP: axis 2 polynomial degree (not stored). + valtype = INTEGER; + distype = PRIOR; + vptr = 0x0; + + i = 2; + a = ' '; + + keyname = "B_ORDER"; + BEGIN(VALUE); + } + +^AP_ORDER { + // SIP: axis 1 inverse polynomial degree (not stored). + valtype = INTEGER; + distype = PRIOR; + vptr = 0x0; + + i = 1; + a = ' '; + + keyname = "AP_ORDER"; + BEGIN(VALUE); + } + +^BP_ORDER { + // SIP: axis 2 inverse polynomial degree (not stored). + valtype = INTEGER; + distype = PRIOR; + vptr = 0x0; + + i = 2; + a = ' '; + + keyname = "BP_ORDER"; + BEGIN(VALUE); + } + +^A_DMAX" " { + // SIP: axis 1 maximum distortion. + valtype = FLOAT; + distype = PRIOR; + vptr = &(distem.maxdis); + + i = 1; + a = ' '; + + keyname = "A_DMAX"; + BEGIN(VALUE); + } + +^B_DMAX" " { + // SIP: axis 2 maximum distortion. + valtype = FLOAT; + distype = PRIOR; + vptr = &(distem.maxdis); + + i = 2; + a = ' '; + + keyname = "B_DMAX"; + BEGIN(VALUE); + } + +^A_ { + // SIP: axis 1 polynomial coefficient. + i = 1; + sipflag = 2; + + keyname = "A_p_q"; + BEGIN(SIP2); + } + +^B_ { + // SIP: axis 2 polynomial coefficient. + i = 2; + sipflag = 2; + + keyname = "B_p_q"; + BEGIN(SIP2); + } + +^AP_ { + // SIP: axis 1 inverse polynomial coefficient. + i = 1; + sipflag = 3; + + keyname = "AP_p_q"; + BEGIN(SIP3); + } + +^BP_ { + // SIP: axis 2 inverse polynomial coefficient. + i = 2; + sipflag = 3; + + keyname = "BP_p_q"; + BEGIN(SIP3); + } + +^CNPIX1" " { + // DSS: LLH corner pixel coordinate 1. + valtype = FLOAT; + distype = SEQUENT; + vptr = dsstmp; + dssflag = 1; + distran = DSS; + + keyname = "CNPIX1"; + BEGIN(VALUE); + } + +^CNPIX2" " { + // DSS: LLH corner pixel coordinate 2. + valtype = FLOAT; + distype = SEQUENT; + vptr = dsstmp+1; + dssflag = 1; + distran = DSS; + + keyname = "CNPIX1"; + BEGIN(VALUE); + } + +^PPO3" " { + // DSS: plate centre x-coordinate in micron. + valtype = FLOAT; + distype = SEQUENT; + vptr = dsstmp+2; + dssflag = 1; + distran = DSS; + + keyname = "PPO3"; + BEGIN(VALUE); + } + +^PPO6" " { + // DSS: plate centre y-coordinate in micron. + valtype = FLOAT; + distype = SEQUENT; + vptr = dsstmp+3; + dssflag = 1; + distran = DSS; + + keyname = "PPO6"; + BEGIN(VALUE); + } + +^XPIXELSZ { + // DSS: pixel x-dimension in micron. + valtype = FLOAT; + distype = SEQUENT; + vptr = dsstmp+4; + dssflag = 1; + distran = DSS; + + keyname = "XPIXELSZ"; + BEGIN(VALUE); + } + +^YPIXELSZ { + // DSS: pixel y-dimension in micron. + valtype = FLOAT; + distype = SEQUENT; + vptr = dsstmp+5; + dssflag = 1; + distran = DSS; + + keyname = "YPIXELSZ"; + BEGIN(VALUE); + } + +^PLTRAH" " { + // DSS: plate centre, right ascension - hours. + valtype = FLOAT; + distype = SEQUENT; + vptr = dsstmp+6; + dssflag = 1; + distran = DSS; + + keyname = "PLTRAH"; + BEGIN(VALUE); + } + +^PLTRAM" " { + // DSS: plate centre, right ascension - minutes. + valtype = FLOAT; + distype = SEQUENT; + vptr = dsstmp+7; + dssflag = 1; + distran = DSS; + + keyname = "PLTRAM"; + BEGIN(VALUE); + } + +^PLTRAS" " { + // DSS: plate centre, right ascension - seconds. + valtype = FLOAT; + distype = SEQUENT; + vptr = dsstmp+8; + dssflag = 1; + distran = DSS; + + keyname = "PLTRAS"; + BEGIN(VALUE); + } + +^PLTDECSN { + // DSS: plate centre, declination - sign. + valtype = STRING; + distype = SEQUENT; + vptr = dsstmp+9; + dssflag = 1; + distran = DSS; + + keyname = "PLTDECSN"; + BEGIN(PLTDECSN); + } + +^PLTDECD" " { + // DSS: plate centre, declination - degrees. + valtype = FLOAT; + distype = SEQUENT; + vptr = dsstmp+10; + dssflag = 1; + distran = DSS; + + keyname = "PLTDECD"; + BEGIN(VALUE); + } + +^PLTDECM" " { + // DSS: plate centre, declination - arcmin. + valtype = FLOAT; + distype = SEQUENT; + vptr = dsstmp+11; + dssflag = 1; + distran = DSS; + + keyname = "PLTDECM"; + BEGIN(VALUE); + } + +^PLTDECS" " { + // DSS: plate centre, declination - arcsec. + valtype = FLOAT; + distype = SEQUENT; + vptr = dsstmp+12; + dssflag = 1; + distran = DSS; + + keyname = "PLTDECS"; + BEGIN(VALUE); + } + +^PLATEID" " { + // DSS: plate identification (insufficient to trigger DSS). + valtype = STRING; + distype = SEQUENT; + vptr = dsstmp+13; + dssflag = 2; + distran = 0; + + keyname = "PLATEID"; + BEGIN(VALUE); + } + +^AMDX { + // DSS: axis 1 polynomial coefficient. + i = 1; + dssflag = 3; + + keyname = "AMDXm"; + BEGIN(DSSAMDXY); + } + +^AMDY { + // DSS: axis 2 polynomial coefficient. + i = 2; + dssflag = 3; + + keyname = "AMDYm"; + BEGIN(DSSAMDXY); + } + +^WAT[12]_{Z3} { + // TNX or ZPX: string-encoded data array. + sscanf(yytext, "WAT%d_%d", &i, &m); + if (watn < m) watn = m; + watflag = 1; + + valtype = STRING; + distype = SEQUENT; + vptr = wat[i-1] + 68*(m-1); + + a = ' '; + distran = WAT; + + keyname = "WATi_m"; + BEGIN(VALUE); + } + +^END" "{77} { + if (yyextra->nkeyrec) { + yyextra->nkeyrec = 0; + errmsg = "keyrecords following the END keyrecord were ignored"; + BEGIN(ERROR); + } else { + BEGIN(DISCARD); + } + } + +^. { + BEGIN(DISCARD); + } + +{I1}{ALT}" " | +{I2}{ALT}" " | +{I1}{ALT}" " | +{I2}{ALT} { + sscanf(yytext, "%d%c", &i, &a); + BEGIN(VALUE); + } + +0{I1}{ALT}" " | +0{Z1}{I1}{ALT}" " | +0{Z2}{I1}{ALT}" " | +0{Z3}{I1}{ALT} | +0{Z4}{I1} | +0{I1}{ALT} | +0{Z1}{I1} { + if (relax & WCSHDR_reject) { + // Violates the basic FITS standard. + errmsg = "indices in parameterized keywords must not have " + "leading zeroes"; + BEGIN(ERROR); + + } else { + // Pretend we don't recognize it. + BEGIN(DISCARD); + } + } + +{Z1}{ALT}" " | +{Z2}{ALT}" " | +{Z3}{ALT}" " | +{Z4}{ALT}" " | +{Z5}{ALT} | +{Z6} | +{Z1}{ALT}" " | +{Z2}{ALT} | +{Z3} { + // Anything that has fallen through to this point must contain + // an invalid axis number. + errmsg = "axis number must exceed 0"; + BEGIN(ERROR); + } + +. { + // Let it go. + BEGIN(DISCARD); + } + +. { + if (relax & WCSHDR_reject) { + // Looks too much like a FITS WCS keyword not to flag it. + errmsg = errtxt; + sprintf(errmsg, "keyword looks very much like %s but isn't", + keyname); + BEGIN(ERROR); + + } else { + // Let it go. + BEGIN(DISCARD); + } + } + +{I1}_{I1}{ALT}" " | +{I1}_{I2}{ALT}" " | +{I2}_{I1}{ALT}" " | +{I2}_{I2}{ALT} { + sscanf(yytext, "%d_%d%c", &i, &j, &a); + BEGIN(VALUE); + } + + +0{I1}_{I1}{ALT}" " | +{I1}_0{I1}{ALT}" " | +00{I1}_{I1}{ALT} | +0{I1}_0{I1}{ALT} | +{I1}_00{I1}{ALT} | +000{I1}_{I1} | +00{I1}_0{I1} | +0{I1}_00{I1} | +{I1}_000{I1} | +0{I1}_{I2}{ALT} | +{I1}_0{I2}{ALT} | +00{I1}_{I2} | +0{I1}_0{I2} | +{I1}_00{I2} | +0{I2}_{I1}{ALT} | +{I2}_0{I1}{ALT} | +00{I2}_{I1} | +0{I2}_0{I1} | +{I2}_00{I1} | +0{I2}_{I2} | +{I2}_0{I2} { + if (((altlin == 1) && (relax & WCSHDR_PC0i_0ja)) || + ((altlin == 2) && (relax & WCSHDR_CD0i_0ja))) { + sscanf(yytext, "%d_%d%c", &i, &j, &a); + BEGIN(VALUE); + + } else if (relax & WCSHDR_reject) { + errmsg = "indices in parameterized keywords must not have " + "leading zeroes"; + BEGIN(ERROR); + + } else { + // Pretend we don't recognize it. + BEGIN(DISCARD); + } + } + +{Z1}_{Z1}{ALT}" " | +{Z2}_{Z1}{ALT}" " | +{Z1}_{Z2}{ALT}" " | +{Z3}_{Z1}{ALT} | +{Z2}_{Z2}{ALT} | +{Z1}_{Z3}{ALT} | +{Z4}_{Z1} | +{Z3}_{Z2} | +{Z2}_{Z3} | +{Z1}_{Z4} { + // Anything that has fallen through to this point must contain + // an invalid axis number. + errmsg = "axis number must exceed 0"; + BEGIN(ERROR); + } + +{Z1}-{Z1}{ALT}" " | +{Z2}-{Z1}{ALT}" " | +{Z1}-{Z2}{ALT}" " | +{Z3}-{Z1}{ALT} | +{Z2}-{Z2}{ALT} | +{Z1}-{Z3}{ALT} | +{Z4}-{Z1} | +{Z3}-{Z2} | +{Z2}-{Z3} | +{Z1}-{Z4} { + errmsg = errtxt; + sprintf(errmsg, "%s keyword must use an underscore, not a dash", + keyname); + BEGIN(ERROR); + } + +{Z2}{I1}{Z2}{I1} { + // This covers the defunct forms CD00i00j and PC00i00j. + if (((altlin == 1) && (relax & WCSHDR_PC00i00j)) || + ((altlin == 2) && (relax & WCSHDR_CD00i00j))) { + sscanf(yytext, "%3d%3d", &i, &j); + a = ' '; + BEGIN(VALUE); + + } else if (relax & WCSHDR_reject) { + errmsg = errtxt; + sprintf(errmsg, + "this form of the %s keyword is deprecated, use %s", + keyname, keyname); + BEGIN(ERROR); + + } else { + // Pretend we don't recognize it. + BEGIN(DISCARD); + } + } + +. { BEGIN(DISCARD); } {ALT} | . { - idx = -1; - if (YY_START == CCCCCCCa) { sscanf(yytext, "%c", &a); } else { unput(yytext[0]); a = 0; } + BEGIN(VALUE); } . { - BEGIN(DISCARD); + if (relax & WCSHDR_reject) { + // Looks too much like a FITS WCS keyword not to flag it. + errmsg = errtxt; + sprintf(errmsg, "invalid alternate code, keyword resembles %s " + "but isn't", keyname); + BEGIN(ERROR); + + } else { + // Pretend we don't recognize it. + BEGIN(DISCARD); + } } -{I1}_{I0}{ALT}" " | +{I1}_{Z1}{ALT}" " | {I1}_{I2}{ALT}" " | -{I2}_{I0}{ALT}" " | +{I2}_{Z1}{ALT}" " | {I2}_{I2}{ALT} { sscanf(yytext, "%d_%d%c", &i, &m, &a); - idx = -1; BEGIN(VALUE); } -{I1}_{I3}{ALT} | -{I3}_{I0}{ALT} | -{I1}_{I4} | -{I2}_{I3} | -{I3}_{I2} | -{I4}_{I0} { - /* Invalid parameters will be caught by . */ - sscanf(yytext, "%d_%d", &i, &m); - BEGIN(VALUE); +0{I1}_{Z1}{ALT}" " | +{I1}_0{Z1}{ALT}" " | +00{I1}_{Z1}{ALT} | +0{I1}_0{Z1}{ALT} | +{I1}_00{Z1}{ALT} | +000{I1}_{Z1} | +00{I1}_0{Z1} | +0{I1}_00{Z1} | +{I1}_000{Z1} | +0{I1}_{I2}{ALT} | +{I1}_0{I2}{ALT} | +00{I1}_{I2} | +0{I1}_0{I2} | +{I1}_00{I2} | +0{I2}_{Z1}{ALT} | +{I2}_0{Z1}{ALT} | +00{I2}_{Z1} | +0{I2}_0{Z1} | +{I2}_00{Z1} | +0{I2}_{I2} | +{I2}_0{I2} { + if (((valtype == FLOAT) && (relax & WCSHDR_PV0i_0ma)) || + ((valtype == STRING) && (relax & WCSHDR_PS0i_0ma))) { + sscanf(yytext, "%d_%d%c", &i, &m, &a); + BEGIN(VALUE); + + } else if (relax & WCSHDR_reject) { + errmsg = "indices in parameterized keywords must not have " + "leading zeroes"; + BEGIN(ERROR); + + } else { + // Pretend we don't recognize it. + BEGIN(DISCARD); + } + } + +{Z1}_{Z1}{ALT}" " | +{Z2}_{Z1}{ALT}" " | +{Z1}_{Z2}{ALT}" " | +{Z3}_{Z1}{ALT} | +{Z2}_{Z2}{ALT} | +{Z1}_{Z3}{ALT} | +{Z4}_{Z1} | +{Z3}_{Z2} | +{Z2}_{Z3} | +{Z1}_{Z4} { + // Anything that has fallen through to this point must contain + // an invalid axis number. + errmsg = "axis number must exceed 0"; + BEGIN(ERROR); + } + +{Z1}-{Z1}{ALT}" " | +{Z2}-{Z1}{ALT}" " | +{Z1}-{Z2}{ALT}" " | +{Z3}-{Z1}{ALT} | +{Z2}-{Z2}{ALT} | +{Z1}-{Z3}{ALT} | +{Z4}-{Z1} | +{Z3}-{Z2} | +{Z2}-{Z3} | +{Z1}-{Z4} { + errmsg = errtxt; + sprintf(errmsg, "%s keyword must use an underscore, not a dash", + keyname); + BEGIN(ERROR); } . { BEGIN(DISCARD); } -{I0}" " { +{Z1}{ALT}" " | +{Z2}{ALT} | +{Z3} { + a = ' '; + sscanf(yytext, "%d%c", &i, &a); + + if (relax & WCSHDR_strict) { + errmsg = "the CROTAn keyword is deprecated, use PCi_ja"; + BEGIN(ERROR); + + } else if ((a == ' ') || (relax & WCSHDR_CROTAia)) { + yyless(0); + BEGIN(CCCCCia); + + } else if (relax & WCSHDR_reject) { + errmsg = "CROTAn keyword may not have an alternate version code"; + BEGIN(ERROR); + + } else { + // Pretend we don't recognize it. + BEGIN(DISCARD); + } + } + +. { + yyless(0); + BEGIN(CCCCCia); + } + +{Z1}" " { if (relax & WCSHDR_PROJPn) { sscanf(yytext, "%d", &m); i = 0; a = ' '; - idx = -1; BEGIN(VALUE); } else if (relax & WCSHDR_reject) { - errmsg = "Defunct PROJPn keyword rejected"; + errmsg = "the PROJPn keyword is deprecated, use PVi_ma"; + BEGIN(ERROR); + + } else { + BEGIN(DISCARD); + } + } + +{Z2}" " | +{Z3} { + if (relax & (WCSHDR_PROJPn | WCSHDR_reject)) { + errmsg = "invalid PROJPn keyword"; BEGIN(ERROR); } else { @@ -705,20 +1872,73 @@ jmp_buf wcspih_abort_jmp_env; BEGIN(DISCARD); } +{Z1}_{Z1}" " | +{Z1}_{Z1}" " { + // SIP keywords. + valtype = FLOAT; + distype = PRIOR; + vptr = &(distem.dp); + npptr = ndp; + + a = ' '; + distran = SIP; + + sscanf(yytext, "%d_%d", &p, &q); + BEGIN(VALUE); + } + +. | +. { + BEGIN(DISCARD); + } + +{I1}" " | +{I2}" " { + // DSS keywords. + valtype = FLOAT; + distype = SEQUENT; + vptr = &(distem.dp); + npptr = ndq; + + a = ' '; + distran = DSS; + + sscanf(yytext, "%d", &m); + BEGIN(VALUE); + } + +. { + BEGIN(DISCARD); + } + +=" "+{STRING} { + // Special handling for this iconic DSS keyword. + if (1 < ipass) { + // Look for a minus sign. + sscanf(yytext, "= '%s", strtmp); + dbltmp = strcmp(strtmp, "-") ? 1.0 : -1.0; + } + + BEGIN(COMMENT); + } + +. { + BEGIN(DISCARD); + } + =" "+ { - /* Do checks on i, j & m. */ - if (i > 99 || j > 99 || m > 99) { + // Do checks on i, j & m. + if (99 < i || 99 < j || 99 < m) { if (relax & WCSHDR_reject) { - errmsg = errtxt; - if (i > 99 || j > 99) { - sprintf(errmsg, "Axis number exceeds 99"); + if (99 < i || 99 < j) { + errmsg = "axis number exceeds 99"; } else if (m > 99) { - sprintf(errmsg, "Parameter number exceeds 99"); + errmsg = "parameter number exceeds 99"; } BEGIN(ERROR); } else { - /* Pretend we don't recognize it. */ + // Pretend we don't recognize it. BEGIN(DISCARD); } @@ -727,11 +1947,15 @@ jmp_buf wcspih_abort_jmp_env; BEGIN(INTEGER_VAL); } else if (valtype == FLOAT) { BEGIN(FLOAT_VAL); + } else if (valtype == FLOAT2) { + BEGIN(FLOAT2_VAL); } else if (valtype == STRING) { BEGIN(STRING_VAL); + } else if (valtype == RECORD) { + BEGIN(RECORD_VAL); } else { errmsg = errtxt; - sprintf(errmsg, "Internal parser ERROR, bad data type: %d", + sprintf(errmsg, "internal parser ERROR, bad data type: %d", valtype); BEGIN(ERROR); } @@ -739,314 +1963,595 @@ jmp_buf wcspih_abort_jmp_env; } . { - errmsg = "Invalid KEYWORD = VALUE syntax"; + errmsg = "invalid KEYWORD = VALUE syntax"; BEGIN(ERROR); } {INTEGER} { - if (pass == 1) { - wcspih_naxes(naxis, i, j, a, alts, npptr); - BEGIN(FLUSH); + if (ipass == 1) { + BEGIN(COMMENT); } else { - if (vptr) { - /* Determine the coordinate representation. */ - for (ialt = 0; ialt < *nwcs; ialt++) { - /* The loop here is for keywords that apply */ - /* to every alternate; these have a == 0. */ - if (a >= 'A') { - ialt = alts[a-'A'+1]; - } - - wptr = vptr; - if (ialt) { - voff = (char *)(*wcs+ialt) - (char *)(*wcs); - wptr = (void *)((char *)vptr + voff); - } - - /* Apply keyword parameterization. */ - if (idx >= 0) { - wptr = *((int **)wptr) + idx; - } - - /* Read the keyvalue. */ - sscanf(yytext, "%d", (int *)wptr); + // Read the keyvalue. + sscanf(yytext, "%d", &inttmp); - if (a) break; - } - - BEGIN(COMMENT); - - } else { - errmsg = "Internal parser ERROR, null int pointer"; - BEGIN(ERROR); - } + BEGIN(COMMENT); } } . { - errmsg = "An integer value was expected"; + errmsg = "an integer value was expected"; BEGIN(ERROR); } {FLOAT} { - if (pass == 1) { - wcspih_naxes(naxis, i, j, a, alts, npptr); - BEGIN(FLUSH); + if (ipass == 1) { + BEGIN(COMMENT); } else { - if (vptr) { - /* Determine the coordinate representation. */ - for (ialt = 0; ialt < *nwcs; ialt++) { - /* The loop here is for keywords like MJD-OBS that */ - /* apply to every alternate; these have a == 0. */ - if (a >= 'A') { - ialt = alts[a-'A'+1]; - } - - wptr = vptr; - if (ialt) { - voff = (char *)(*wcs+ialt) - (char *)(*wcs); - wptr = (void *)((char *)vptr + voff); - } - - /* Apply keyword parameterization. */ - if (idx >= 0) { - wptr = *((double **)wptr) + idx; + // Read the keyvalue. + wcsutil_str2double(yytext, &dbltmp); + + if (chekval && chekval(dbltmp)) { + errmsg = "invalid keyvalue"; + BEGIN(ERROR); + } else { + BEGIN(COMMENT); + } + } + } + +. { + errmsg = "a floating-point value was expected"; + BEGIN(ERROR); + } + +{FLOAT} { + if (ipass == 1) { + BEGIN(COMMENT); - } else if (npptr == npv) { - ipx = (*wcs+ialt)->npv++; - (*wcs+ialt)->pv[ipx].i = i; - (*wcs+ialt)->pv[ipx].m = m; - wptr = &((*wcs+ialt)->pv[ipx].value); - } + } else { + // Read the keyvalue as integer and fractional parts. + wcsutil_str2double2(yytext, dbl2tmp); - /* Read the keyvalue. */ - wcsutil_str2double(yytext, "%lf", (double *)wptr); + BEGIN(COMMENT); + } + } + +. { + errmsg = "a floating-point value was expected"; + BEGIN(ERROR); + } + +{STRING} { + if (ipass == 1) { + BEGIN(COMMENT); - /* Flag the presence of PCi_ja, or CDi_ja and/or CROTAia. */ - if (altlin) { - (*wcs+ialt)->altlin |= altlin; - altlin = 0; + } else { + strtmp[0] = '\0'; + int nch = yyleng - 2; + if (0 < nch && nch < 80) { + // Copy the keyvalue minus the quotes. + strncpy(strtmp, yytext+1, nch); + strtmp[nch] = '\0'; + + // Strip off trailing blanks. + for (int jx = nch-1; jx >= 0; jx--) { + if (strtmp[jx] != ' ') { + break; } + strtmp[jx] = '\0'; + } + } - if (a) break; + // Squeeze out repeated quotes. + int ix = 0; + for (int jx = 0; jx < 72; jx++) { + if (ix < jx) { + strtmp[ix] = strtmp[jx]; } - BEGIN(COMMENT); + if (strtmp[jx] == '\0') { + break; + } else if (strtmp[jx] == '\'' && strtmp[jx+1] == '\'') { + jx++; + } - } else { - errmsg = "Internal parser ERROR, null float pointer"; - BEGIN(ERROR); + ix++; } + + BEGIN(COMMENT); } } -. { - errmsg = "A floating-point value was expected"; +. { + errmsg = "a string value was expected"; BEGIN(ERROR); } -{STRING} { - if (pass == 1) { - wcspih_naxes(naxis, i, j, a, alts, npptr); - BEGIN(FLUSH); +{RECORD} { + if (ipass == 1) { + BEGIN(COMMENT); } else { - if (vptr) { - /* Determine the coordinate representation. */ - for (ialt = 0; ialt < *nwcs; ialt++) { - /* The loop here is for keywords like DATE-OBS that */ - /* apply to every alternate; these have a == 0. */ - if (a >= 'A') { - ialt = alts[a-'A'+1]; - } - - wptr = vptr; - if (ialt) { - voff = (char *)(*wcs+ialt) - (char *)(*wcs); - wptr = (void *)((char *)vptr + voff); - } + yyless(1); - /* Apply keyword parameterization. */ - if (idx >= 0) { - wptr = *((char (**)[72])wptr) + idx; - - } else if (npptr == nps) { - ipx = (*wcs+ialt)->nps++; - (*wcs+ialt)->ps[ipx].i = i; - (*wcs+ialt)->ps[ipx].m = m; - wptr = (*wcs+ialt)->ps[ipx].value; - } + BEGIN(RECFIELD); + } + } + +. { + errmsg = "a record was expected"; + BEGIN(ERROR); + } + +{FIELD} { + strncpy(strtmp, yytext, 72); + strtmp[72] = '\0'; + BEGIN(RECCOLON); + } + +. { + errmsg = "invalid record field"; + BEGIN(ERROR); + } + +:" "+ { + BEGIN(RECVALUE); + } + +. { + errmsg = "invalid record syntax"; + BEGIN(ERROR); + } + +{INTEGER} { + rectype = 0; + sscanf(yytext, "%d", &inttmp); + BEGIN(RECEND); + } + +{FLOAT} { + rectype = 1; + wcsutil_str2double(yytext, &dbltmp); + BEGIN(RECEND); + } + +. { + errmsg = "invalid record value"; + BEGIN(ERROR); + } + +' { + BEGIN(COMMENT); + } + +{INLINE}$ { + if (ipass == 1) { + // Do first-pass bookkeeping. + wcspih_pass1(naxis, i, j, a, distype, alts, dpq, npptr); + BEGIN(FLUSH); - /* Read the keyvalue. */ - cptr = (char *)wptr; - strcpy(cptr, yytext+1); + } else if (*wcs) { + // Store the value now that the keyrecord has been validated. + int gotone = 0; + for (int ialt = 0; ialt < *nwcs; ialt++) { + // The loop here is for keywords that apply + // to every alternate; these have a == 0. + if (a >= 'A') { + ialt = alts[a-'A'+1]; + if (ialt < 0) break; + } + gotone = 1; + + if (vptr) { + if (sipflag) { + // Translate a SIP keyword into DPja. + struct disprm *disp = (*wcs)->lin.dispre; + int ipx = (disp->ndp)++; + + // SIP doesn't have alternates. + char keyword[16]; + sprintf(keyword, "DP%d", i); + sprintf(strtmp, "SIP.%s.%d_%d", (sipflag==2)?"FWD":"REV", + p, q); + if (valtype == INTEGER) { + dpfill(disp->dp+ipx, keyword, strtmp, i, 0, inttmp, 0.0); + } else { + dpfill(disp->dp+ipx, keyword, strtmp, i, 1, 0, dbltmp); + } - /* Squeeze out repeated quotes. */ - ix = 0; - for (jx = 0; jx < 72; jx++) { - if (ix < jx) { - cptr[ix] = cptr[jx]; + } else if (dssflag) { + // All DSS keywords require special handling. + if (dssflag == 1) { + // Temporary parameter for DSS used by wcspih_final(). + *((double *)vptr) = dbltmp; + + } else if (dssflag == 2) { + // Temporary parameter for DSS used by wcspih_final(). + strcpy((char *)vptr, strtmp); + + } else { + // Translate a DSS keyword into DQia. + if (m <= 13 || dbltmp != 0.0) { + struct disprm *disp = (*wcs)->lin.disseq; + int ipx = (disp->ndp)++; + + // DSS doesn't have alternates. + char keyword[16]; + sprintf(keyword, "DQ%d", i); + sprintf(strtmp, "DSS.AMD.%d", m); + dpfill(disp->dp+ipx, keyword, strtmp, i, 1, 0, dbltmp); + + // Also required by wcspih_final(). + if (m <= 3) { + dsstmp[13+(i-1)*3+m] = dbltmp; + } + } } - if (cptr[jx] == '\0') { - if (ix) cptr[ix-1] = '\0'; - break; - } else if (cptr[jx] == '\'' && cptr[jx+1] == '\'') { - jx++; + } else if (watflag) { + // String array for TNX and ZPX used by wcspih_final(). + strcpy((char *)vptr, strtmp); + + } else { + // An "ordinary" keyword. + struct wcsprm *wcsp = *wcs + ialt; + struct disprm *disp; + void *wptr; + ptrdiff_t voff; + if (auxprm) { + // Additional auxiliary parameter. + struct auxprm *auxp = wcsp->aux; + voff = (char *)vptr - (char *)(&auxtem); + wptr = (void *)((char *)auxp + voff); + + } else if (distype) { + // Distortion parameter of some kind. + if (distype == PRIOR) { + // Prior distortion. + disp = wcsp->lin.dispre; + } else { + // Sequent distortion. + disp = wcsp->lin.disseq; + } + voff = (char *)vptr - (char *)(&distem); + wptr = (void *)((char *)disp + voff); + + } else { + // A parameter that lives directly in wcsprm. + voff = (char *)vptr - (char *)(&wcstem); + wptr = (void *)((char *)wcsp + voff); } - ix++; + if (valtype == INTEGER) { + *((int *)wptr) = inttmp; + + } else if (valtype == FLOAT) { + // Apply keyword parameterization. + if (npptr == npv) { + int ipx = (wcsp->npv)++; + wcsp->pv[ipx].i = i; + wcsp->pv[ipx].m = m; + wptr = &(wcsp->pv[ipx].value); + + } else if (j) { + wptr = *((double **)wptr) + (i - 1)*(wcsp->naxis) + + (j - 1); + + } else if (i) { + wptr = *((double **)wptr) + (i - 1); + } + + if (special) { + special(wptr, &dbltmp); + } else { + *((double *)wptr) = dbltmp; + } + + // Flag presence of PCi_ja, or CDi_ja and/or CROTAia. + if (altlin) { + wcsp->altlin |= altlin; + altlin = 0; + } + + } else if (valtype == FLOAT2) { + // Split MJDREF and JDREF into integer and fraction. + if (special) { + special(wptr, dbl2tmp); + } else { + *((double *)wptr) = dbl2tmp[0]; + *((double *)wptr + 1) = dbl2tmp[1]; + } + + } else if (valtype == STRING) { + // Apply keyword parameterization. + if (npptr == nps) { + int ipx = (wcsp->nps)++; + wcsp->ps[ipx].i = i; + wcsp->ps[ipx].m = m; + wptr = wcsp->ps[ipx].value; + + } else if (j) { + wptr = *((char (**)[72])wptr) + + (i - 1)*(wcsp->naxis) + (j - 1); + + } else if (i) { + wptr = *((char (**)[72])wptr) + (i - 1); + } + + char *cptr = (char *)wptr; + strcpy(cptr, strtmp); + + } else if (valtype == RECORD) { + int ipx = (disp->ndp)++; + + char keyword[16]; + if (a == ' ') { + sprintf(keyword, "%.2s%d", keyname, i); + } else { + sprintf(keyword, "%.2s%d%c", keyname, i, a); + } + + dpfill(disp->dp+ipx, keyword, strtmp, i, rectype, inttmp, + dbltmp); + } } + } + + if (a) break; + } - if (a) break; + if (gotone) { + nvalid++; + if (ctrl == 4) { + if (distran || dssflag) { + wcsfprintf(stderr, "%.80s\n Accepted (%d) as a " + "recognized WCS convention.\n", keyrec, nvalid); + } else { + wcsfprintf(stderr, "%.80s\n Accepted (%d) as a " + "valid WCS keyrecord.\n", keyrec, nvalid); + } } - BEGIN(COMMENT); + BEGIN(FLUSH); } else { - errmsg = "Internal parser ERROR, null string pointer"; + errmsg = "syntactically valid WCS keyrecord has no effect"; BEGIN(ERROR); } + + } else { + BEGIN(FLUSH); } } -. { - errmsg = "A string value was expected"; +.*" "*\/.*$ { + errmsg = "invalid keyvalue"; BEGIN(ERROR); } -" "*\/.* | -" "* { - BEGIN(FLUSH); +[^ \/\n]*{INLINE}$ { + errmsg = "invalid keyvalue"; + BEGIN(ERROR); + } + +" "+[^\/\n].*{INLINE}$ { + errmsg = "invalid keyvalue or malformed keycomment"; + BEGIN(ERROR); } -. { - errmsg = "Malformed keycomment"; +.*$ { + errmsg = "malformed keycomment"; BEGIN(ERROR); } -.* { - if (pass == 2) { +.*$ { + if (ipass == npass) { if (ctrl < 0) { - /* Preserve discards. */ - keep = wcspih_hdr - 80; + // Preserve discards. + keep = keyrec; - } else if (ctrl > 2) { - fprintf(stderr, "%.80s\n Discarded.\n", wcspih_hdr-80); + } else if (2 < ctrl) { + nother++; + wcsfprintf(stderr, "%.80s\n Not a recognized WCS keyword.\n", + keyrec); } } BEGIN(FLUSH); } -.* { - (*nreject)++; - if (pass == 2) { +.*$ { + if (ipass == npass) { + (*nreject)++; + if (ctrl%10 == -1) { - /* Preserve rejects. */ - keep = wcspih_hdr - 80; + // Preserve rejects. + keep = keyrec; } - if (abs(ctrl%10) > 1) { - fprintf(stderr, "%.80s\n%4d: %s.\n", wcspih_hdr-80, *nreject, - errmsg); + if (1 < abs(ctrl%10)) { + wcsfprintf(stderr, "%.80s\n Rejected (%d), %s.\n", + keyrec, *nreject, errmsg); } } BEGIN(FLUSH); } .*\n { - if (pass == 2 && keep) { + if (ipass == npass && keep) { if (hptr < keep) { strncpy(hptr, keep, 80); } hptr += 80; } - i = j = m = 0; + naux += auxprm; + + // Throw away the rest of the line and reset for the next one. + i = j = 0; + m = 0; a = ' '; + + keyrec += 80; + valtype = -1; - keep = 0x0; - altlin = 0; - npptr = 0x0; + distype = 0; + vptr = 0x0; + keep = 0x0; + + altlin = 0; + npptr = 0x0; + chekval = 0x0; + special = 0x0; + auxprm = 0; + sipflag = 0; + dssflag = 0; + watflag = 0; + BEGIN(INITIAL); } <> { - /* End-of-input. */ - if (pass == 1) { - if ((status = wcspih_inits(naxis, alts, npv, nps, nwcs, wcs)) || - *nwcs == 0) { - yylex_destroy(); + // End-of-input. + int status; + if (ipass == 1) { + if ((status = wcspih_init1(naxis, alts, dpq, npv, nps, ndp, ndq, + naux, distran, nwcs, wcs)) || + (*nwcs == 0 && ctrl == 0)) { return status; } - if (abs(ctrl%10) > 2) { + if (2 < abs(ctrl%10)) { if (*nwcs == 1) { - fprintf(stderr, "Found one coordinate representation.\n"); + if (strcmp(wcs[0]->wcsname, "DEFAULTS") != 0) { + wcsfprintf(stderr, "Found one coordinate representation.\n"); + } } else { - fprintf(stderr, "Found %d coordinate representations.\n", + wcsfprintf(stderr, "Found %d coordinate representations.\n", *nwcs); } } - wcspih_hdr = header; - wcspih_nkeyrec = nkeyrec; + watstr = calloc(2*(watn*68 + 1), sizeof(char)); + wat[0] = watstr; + wat[1] = watstr + watn*68 + 1; + } + + if (ipass++ < npass) { + yyextra->hdr = header; + yyextra->nkeyrec = nkeyrec; + keyrec = header; *nreject = 0; - pass = 2; - i = j = m = 0; + i = j = 0; + m = 0; a = ' '; + valtype = -1; + distype = 0; + vptr = 0x0; + + altlin = 0; + npptr = 0x0; + chekval = 0x0; + special = 0x0; + auxprm = 0; + sipflag = 0; + dssflag = 0; + watflag = 0; - yyrestart(yyin); + yyrestart(yyin, yyscanner); } else { - yylex_destroy(); if (ctrl < 0) { *hptr = '\0'; } else if (ctrl == 1) { - fprintf(stderr, "%d WCS keyrecords were rejected.\n", - *nreject); + wcsfprintf(stderr, "%d WCS keyrecord%s rejected.\n", + *nreject, (*nreject==1)?" was":"s were"); + } else if (ctrl == 4) { + wcsfprintf(stderr, "\n"); + wcsfprintf(stderr, "%5d keyrecord%s rejected for syntax or " + "other errors,\n", *nreject, (*nreject==1)?" was":"s were"); + wcsfprintf(stderr, "%5d %s recognized as syntactically valid, " + "and\n", nvalid, (nvalid==1)?"was":"were"); + wcsfprintf(stderr, "%5d other%s were not recognized as WCS " + "keyrecords.\n", nother, (nother==1)?"":"s"); } - return wcspih_final(alts, epoch, vsource, nwcs, wcs); + status = wcspih_final(ndp, ndq, distran, dsstmp, wat, nwcs, wcs); + free(watstr); + return status; } } %% /*---------------------------------------------------------------------------- -* Determine the number of coordinate representations (up to 27) and the -* number of coordinate axes in each, and count the number of PVi_ma and -* PSi_ma keywords in each representation. +* External interface to the scanner. *---------------------------------------------------------------------------*/ -void wcspih_naxes(int naxis, int i, int j, char a, int alts[], int *npptr) +int wcspih( + char *header, + int nkeyrec, + int relax, + int ctrl, + int *nreject, + int *nwcs, + struct wcsprm **wcs) { - /* On the first pass alts[] is used to determine the number of axes */ - /* for each of the 27 possible alternate coordinate descriptions. */ - int ialt, *ip; + // Function prototypes. + int yylex_init_extra(YY_EXTRA_TYPE extra, yyscan_t *yyscanner); + int yylex_destroy(yyscan_t yyscanner); + + struct wcspih_extra extra; + yyscan_t yyscanner; + yylex_init_extra(&extra, &yyscanner); + int status = wcspih_scanner(header, nkeyrec, relax, ctrl, nreject, nwcs, + wcs, yyscanner); + yylex_destroy(yyscanner); + + return status; +} + +/*---------------------------------------------------------------------------- +* Determine the number of coordinate representations (up to 27) and the +* number of coordinate axes in each, which distortions are present, and the +* number of PVi_ma, PSi_ma, DPja, and DQia keywords in each representation. +*---------------------------------------------------------------------------*/ + +void wcspih_pass1( + int naxis, + int i, + int j, + char a, + int distype, + int alts[], + int dpq[], + int *npptr) + +{ + // On the first pass alts[] is used to determine the number of axes + // for each of the 27 possible alternate coordinate descriptions. if (a == 0) { return; } - ialt = 0; + int ialt = 0; if (a != ' ') { ialt = a - 'A' + 1; } - ip = alts + ialt; + int *ip = alts + ialt; if (*ip < naxis) { *ip = naxis; } - /* i or j can be greater than naxis. */ + // i or j can be greater than naxis. if (*ip < i) { *ip = i; } @@ -1055,6 +2560,10 @@ void wcspih_naxes(int naxis, int i, int j, char a, int alts[], int *npptr) *ip = j; } + // Type of distortions present. + dpq[ialt] |= distype; + + // Count PVi_ma, PSi_ma, DPja, or DQia keywords. if (npptr) { npptr[ialt]++; } @@ -1066,109 +2575,507 @@ void wcspih_naxes(int naxis, int i, int j, char a, int alts[], int *npptr) * initialize each of them. *---------------------------------------------------------------------------*/ -int wcspih_inits( +int wcspih_init1( int naxis, int alts[], + int dpq[], int npv[], int nps[], + int ndp[], + int ndq[], + int naux, + int distran, int *nwcs, struct wcsprm **wcs) { - int ialt, npsmax, npvmax, status = 0; - struct wcsprm *wcsp; + int status = 0; - /* Find the number of coordinate descriptions. */ + // Find the number of coordinate descriptions. *nwcs = 0; - for (ialt = 0; ialt < 27; ialt++) { + for (int ialt = 0; ialt < 27; ialt++) { if (alts[ialt]) (*nwcs)++; } - if (!(*nwcs) && naxis) { - /* NAXIS is non-zero but there were no WCS keywords with an alternate - version code; create a default WCS with blank alternate version. */ - wcspih_naxes(naxis, 0, 0, ' ', alts, 0x0); + int defaults; + if ((defaults = !(*nwcs) && naxis)) { + // NAXIS is non-zero but there were no WCS keywords with an alternate + // version code; create a default WCS with blank alternate version. + wcspih_pass1(naxis, 0, 0, ' ', 0, alts, dpq, 0x0); *nwcs = 1; } if (*nwcs) { - /* Allocate memory for the required number of wcsprm structs. */ - if (!(*wcs = calloc(*nwcs, sizeof(struct wcsprm)))) { - return 2; + // Allocate memory for the required number of wcsprm structs. + if ((*wcs = calloc(*nwcs, sizeof(struct wcsprm))) == 0x0) { + return WCSHDRERR_MEMORY; } - /* Record the current values of NPVMAX and NPSMAX. */ - npvmax = wcsnpv(-1); - npsmax = wcsnps(-1); + int ndis = 0; + if (distran == SIP) { + // DPja.NAXES and DPja.OFFSET.j to be added for SIP (see below and + // wcspih_final()). + ndp[0] += 6; + + } else if (distran == DSS) { + // DPja.NAXES to be added for DSS (see below and wcspih_final()). + ndq[0] += 2; + } - /* Initialize each wcsprm struct. */ - wcsp = *wcs; + // Initialize each wcsprm struct. + struct wcsprm *wcsp = *wcs; *nwcs = 0; - for (ialt = 0; ialt < 27; ialt++) { + for (int ialt = 0; ialt < 27; ialt++) { if (alts[ialt]) { wcsp->flag = -1; - wcsnpv(npv[ialt]); - wcsnps(nps[ialt]); - if ((status = wcsini(1, alts[ialt], wcsp))) { + int npvmax = npv[ialt]; + int npsmax = nps[ialt]; + if ((status = wcsinit(1, alts[ialt], wcsp, npvmax, npsmax, -1))) { wcsvfree(nwcs, wcs); break; } - /* Record the alternate version code. */ + // Record the alternate version code. if (ialt) { wcsp->alt[0] = 'A' + ialt - 1; } - /* On the second pass alts[] indexes the array of wcsprm structs. */ + // Record in wcsname whether this is a default description. + if (defaults) { + strncpy(wcsp->wcsname, "DEFAULTS", 72); + } + + // Any additional auxiliary keywords present? + if (naux) { + if (wcsauxi(1, wcsp)) { + return WCSHDRERR_MEMORY; + } + } + + // Any distortions present? + struct disprm *disp; + if (dpq[ialt] & 1) { + if ((disp = calloc(1, sizeof(struct disprm))) == 0x0) { + return WCSHDRERR_MEMORY; + } + + // Attach it to linprm. Also inits it. + ndis++; + int ndpmax = ndp[ialt]; + disp->flag = -1; + lindist(1, &(wcsp->lin), disp, ndpmax); + } + + if (dpq[ialt] & 2) { + if ((disp = calloc(1, sizeof(struct disprm))) == 0x0) { + return WCSHDRERR_MEMORY; + } + + // Attach it to linprm. Also inits it. + ndis++; + int ndpmax = ndq[ialt]; + disp->flag = -1; + lindist(2, &(wcsp->lin), disp, ndpmax); + } + + // On the second pass alts[] indexes the array of wcsprm structs. alts[ialt] = (*nwcs)++; wcsp++; + + } else { + // Signal that there is no wcsprm for this alt. + alts[ialt] = -1; } } - /* Restore the original values of NPVMAX and NPSMAX. */ - wcsnpv(npvmax); - wcsnps(npsmax); + + // Translated distortion? Neither SIP nor DSS have alternates, so the + // presence of keywords for either (not both together), as flagged by + // distran, necessarily refers to the primary representation. + if (distran == SIP) { + strncpy((*wcs)->lin.dispre->dtype[0], "SIP", 72); + strncpy((*wcs)->lin.dispre->dtype[1], "SIP", 72); + + // SIP doesn't have axis mapping. + (*wcs)->lin.dispre->ndp = 6; + dpfill((*wcs)->lin.dispre->dp, "DP1", "NAXES", 0, 0, 2, 0.0); + dpfill((*wcs)->lin.dispre->dp+3, "DP2", "NAXES", 0, 0, 2, 0.0); + + } else if (distran == DSS) { + strncpy((*wcs)->lin.disseq->dtype[0], "DSS", 72); + strncpy((*wcs)->lin.disseq->dtype[1], "DSS", 72); + + // The Paper IV translation of DSS doesn't require an axis mapping. + (*wcs)->lin.disseq->ndp = 2; + dpfill((*wcs)->lin.disseq->dp, "DQ1", "NAXES", 0, 0, 2, 0.0); + dpfill((*wcs)->lin.disseq->dp+1, "DQ2", "NAXES", 0, 0, 2, 0.0); + } } return status; } +/*---------------------------------------------------------------------------- +* Interpret the JDREF, JDREFI, and JDREFF keywords. +*---------------------------------------------------------------------------*/ + +int wcspih_jdref(double *mjdref, const double *jdref) + +{ + // Set MJDREF from JDREF. + if (undefined(mjdref[0] && undefined(mjdref[1]))) { + mjdref[0] = jdref[0] - 2400000.0; + mjdref[1] = jdref[1] - 0.5; + + if (mjdref[1] < 0.0) { + mjdref[0] -= 1.0; + mjdref[1] += 1.0; + } + } + + return 0; +} + +int wcspih_jdrefi(double *mjdref, const double *jdrefi) + +{ + // Set the integer part of MJDREF from JDREFI. + if (undefined(mjdref[0])) { + mjdref[0] = *jdrefi - 2400000.5; + } + + return 0; +} + + +int wcspih_jdreff(double *mjdref, const double *jdreff) + +{ + // Set the fractional part of MJDREF from JDREFF. + if (undefined(mjdref[1])) { + mjdref[1] = *jdreff; + } + + return 0; +} + + +/*---------------------------------------------------------------------------- +* Interpret EPOCHa keywords. +*---------------------------------------------------------------------------*/ + +int wcspih_epoch(double *equinox, const double *epoch) + +{ + // If EQUINOXa is currently undefined then set it from EPOCHa. + if (undefined(*equinox)) { + *equinox = *epoch; + } + + return 0; +} + + +/*---------------------------------------------------------------------------- +* Interpret VSOURCEa keywords. +*---------------------------------------------------------------------------*/ + +int wcspih_vsource(double *zsource, const double *vsource) + +{ + const double c = 299792458.0; + + // If ZSOURCEa is currently undefined then set it from VSOURCEa. + if (undefined(*zsource)) { + // Convert relativistic Doppler velocity to redshift. + double beta = *vsource/c; + *zsource = (1.0 + beta)/sqrt(1.0 - beta*beta) - 1.0; + } + + return 0; +} + + +/*---------------------------------------------------------------------------- +* Check validity of a TIMEPIXR keyvalue. +*---------------------------------------------------------------------------*/ + +int wcspih_timepixr(double timepixr) + +{ + return (timepixr < 0.0 || 1.0 < timepixr); +} + + /*---------------------------------------------------------------------------- * Interpret special keywords encountered for each coordinate representation. *---------------------------------------------------------------------------*/ int wcspih_final( - int alts[], - double epoch[], - double vsource[], - int *nwcs, + int ndp[], + int ndq[], + int distran, + double dsstmp[], + char *wat[], + int *nwcs, struct wcsprm **wcs) { - int ialt, status; - double beta, c = 299792458.0; - - for (ialt = 0; ialt < *nwcs; ialt++) { - /* Check for EPOCH overriding EQUINOXa. */ - if (undefined((*wcs+ialt)->equinox) && !undefined(epoch[ialt])) { - /* Set EQUINOXa. */ - (*wcs+ialt)->equinox = epoch[ialt]; + for (int ialt = 0; ialt < *nwcs; ialt++) { + // Interpret -TAB header keywords. + int status; + if ((status = wcstab(*wcs+ialt))) { + wcsvfree(nwcs, wcs); + return status; } - /* Check for VSOURCEa overriding ZSOURCEa. */ - if (undefined((*wcs+ialt)->zsource) && !undefined(vsource[ialt])) { - /* Convert relativistic Doppler velocity to redshift. */ - beta = vsource[ialt]/c; - (*wcs+ialt)->zsource = (1.0+beta)/sqrt(1.0 - beta*beta) - 1.0; + if (ndp[ialt] && ndq[ialt]) { + // Prior and sequent distortions co-exist in this representation; + // ensure the latter gets DVERRa. + (*wcs+ialt)->lin.disseq->totdis = (*wcs+ialt)->lin.dispre->totdis; } + } - /* Interpret -TAB header keywords. */ - if ((status = wcstab(*wcs+ialt))) { - wcsvfree(nwcs, wcs); - return status; + // Translated distortion functions; apply only to the primary WCS. + struct wcsprm *wcsp = *wcs; + if (distran == SIP) { + // SIP doesn't have alternates, nor axis mapping. + struct disprm *disp = wcsp->lin.dispre; + dpfill(disp->dp+1, "DP1", "OFFSET.1", 0, 1, 0, wcsp->crpix[0]); + dpfill(disp->dp+2, "DP1", "OFFSET.2", 0, 1, 0, wcsp->crpix[1]); + dpfill(disp->dp+4, "DP2", "OFFSET.1", 0, 1, 0, wcsp->crpix[0]); + dpfill(disp->dp+5, "DP2", "OFFSET.2", 0, 1, 0, wcsp->crpix[1]); + + } else if (distran == DSS) { + // DSS doesn't have alternates, nor axis mapping. This translation + // follows Paper IV, Sect. 5.2 using the same variable names. + double CNPIX1 = dsstmp[0]; + double CNPIX2 = dsstmp[1]; + + double Xc = dsstmp[2]/1000.0; + double Yc = dsstmp[3]/1000.0; + double Rx = dsstmp[4]/1000.0; + double Ry = dsstmp[5]/1000.0; + + double A1 = dsstmp[14]; + double A2 = dsstmp[15]; + double A3 = dsstmp[16]; + double B1 = dsstmp[17]; + double B2 = dsstmp[18]; + double B3 = dsstmp[19]; + double S = sqrt(fabs(A1*B1 - A2*B2)); + + double X0 = (A2*B3 - A3*B1) / (A1*B1 - A2*B2); + double Y0 = (A3*B2 - A1*B3) / (A1*B1 - A2*B2); + + wcsp->crpix[0] = (Xc - X0)/Rx - (CNPIX1 - 0.5); + wcsp->crpix[1] = (Yc + Y0)/Ry - (CNPIX2 - 0.5); + + wcsp->pc[0] = A1*Rx/S; + wcsp->pc[1] = -A2*Ry/S; + wcsp->pc[2] = -B2*Rx/S; + wcsp->pc[3] = B1*Ry/S; + wcsp->altlin = 1; + + wcsp->cdelt[0] = -S/3600.0; + wcsp->cdelt[1] = S/3600.0; + + double *crval = wcsp->crval; + crval[0] = (dsstmp[6] + (dsstmp[7] + dsstmp[8] /60.0)/60.0)*15.0; + crval[1] = dsstmp[10] + (dsstmp[11] + dsstmp[12]/60.0)/60.0; + if (dsstmp[9] == -1.0) crval[1] *= -1.0; + + strncpy(wcsp->ctype[0], "RA---TAN", 72); + strncpy(wcsp->ctype[1], "DEC--TAN", 72); + + sprintf(wcsp->wcsname, "DSS PLATEID %.4s", (char *)(dsstmp+13)); + + // Erase the approximate WCS provided in modern DSS headers. + wcsp->cd[0] = 0.0; + wcsp->cd[1] = 0.0; + wcsp->cd[2] = 0.0; + wcsp->cd[3] = 0.0; + + } else if (distran == WAT) { + // TNX and ZPX don't have alternates, nor axis mapping. + char *wp; + int omax, omin, wctrl[4]; + double wval; + struct disprm *disp = wcsp->lin.disseq; + + // Disassemble the core dump stored in the WATi_m strings. + int i, nterms = 0; + for (i = 0; i < 2; i++) { + char wtype[8]; + sscanf(wat[i], "wtype=%s", wtype); + + if (strcmp(wtype, "tnx") == 0) { + strncpy(disp->dtype[i], "WAT-TNX", 72); + } else if (strcmp(wtype, "zpx") == 0) { + strncpy(disp->dtype[i], "WAT-ZPX", 72); + } else { + // Could contain "tan" or something else to be ignored. + lindist(2, &(wcsp->lin), 0x0, 0); + return 0; + } + + // The PROJPn parameters are duplicated on each ZPX axis. + if (i == 1 && strcmp(wtype, "zpx") == 0) { + // Take those on the second (latitude) axis ignoring the other. + // First we have to count them and allocate space in wcsprm. + wp = wat[i]; + int npv; + for (npv = 0; npv < 30; npv++) { + if ((wp = strstr(wp, "projp")) == 0x0) break; + wp += 5; + } + + // Allocate space. + if (npv) { + wcsp->npvmax += npv; + wcsp->pv = realloc(wcsp->pv, wcsp->npvmax*sizeof(struct pvcard)); + if (wcsp->pv == 0x0) { + return WCSHDRERR_MEMORY; + } + + wcsp->m_pv = wcsp->pv; + } + + // Copy the values. + wp = wat[i]; + for (int ipv = wcsp->npv; ipv < wcsp->npvmax; ipv++) { + if ((wp = strstr(wp, "projp")) == 0x0) break; + + int m; + sscanf(wp, "projp%d=%lf", &m, &wval); + wcsp->pv[ipv].i = 2; + wcsp->pv[ipv].m = m; + wcsp->pv[ipv].value = wval; + + wp += 5; + } + + wcsp->npv += npv; + } + + // Read the control parameters. + if ((wp = strchr(wat[i], '"')) == 0x0) { + return WCSHDRERR_PARSER; + } + wp++; + + for (int m = 0; m < 4; m++) { + sscanf(wp, "%d", wctrl+m); + if ((wp = strchr(wp, ' ')) == 0x0) { + return WCSHDRERR_PARSER; + } + wp++; + } + + // How many coefficients are we expecting? + omin = (wctrl[1] < wctrl[2]) ? wctrl[1] : wctrl[2]; + omax = (wctrl[1] < wctrl[2]) ? wctrl[2] : wctrl[1]; + if (wctrl[3] == 0) { + // No cross terms. + nterms += omin + omax; + + } else if (wctrl[3] == 1) { + // Full cross terms. + nterms += omin*omax; + + } else if (wctrl[3] == 2) { + // Half cross terms. + nterms += omin*omax - omin*(omin-1)/2; + } + } + + // Allocate memory for dpkeys. + ndq[0] += 2*(1 + 1 + 4) + nterms; + + disp->ndpmax += ndq[0]; + disp->dp = realloc(disp->dp, disp->ndpmax*sizeof(struct dpkey)); + if (disp->dp == 0x0) { + return WCSHDRERR_MEMORY; + } + + disp->m_dp = disp->dp; + + + // Populate dpkeys. + int idp = disp->ndp; + for (i = 0; i < 2; i++) { + dpfill(disp->dp+(idp++), "DQ", "NAXES", i+1, 0, 2, 0.0); + + // Read the control parameters. + if ((wp = strchr(wat[i], '"')) == 0x0) { + return WCSHDRERR_PARSER; + } + wp++; + + for (int m = 0; m < 4; m++) { + sscanf(wp, "%d", wctrl+m); + if ((wp = strchr(wp, ' ')) == 0x0) { + return WCSHDRERR_PARSER; + } + wp++; + } + + // Polynomial type. + char wpoly[12]; + dpfill(disp->dp+(idp++), "DQ", "WAT.POLY", i+1, 0, wctrl[0], 0.0); + if (wctrl[0] == 1) { + // Chebyshev polynomial. + strncpy(wpoly, "CHBY", 12); + } else if (wctrl[0] == 2) { + // Legendre polynomial. + strncpy(wpoly, "LEGR", 12); + } else if (wctrl[0] == 3) { + // Polynomial is the sum of monomials. + strncpy(wpoly, "MONO", 12); + } else { + // Unknown code. + strncpy(wpoly, "UNKN", 12); + } + + // Read the scaling parameters. + char field[40]; + for (int m = 0; m < 4; m++) { + sscanf(wp, "%lf", &wval); + sprintf(field, "WAT.%c%s", (m<2)?'X':'Y', (m%2)?"MAX":"MIN"); + dpfill(disp->dp+(idp++), "DQ", field, i+1, 1, 0, wval); + + if ((wp = strchr(wp, ' ')) == 0x0) { + return WCSHDRERR_PARSER; + } + wp++; + } + + // Read the coefficients. + for (int n = 0; n < wctrl[2]; n++) { + for (int m = 0; m < wctrl[1]; m++) { + if (wctrl[3] == 0) { + if (m && n) continue; + } else if (wctrl[3] == 2) { + if (m+n > omax-1) continue; + } + + sscanf(wp, "%lf", &wval); + if (wval == 0.0) continue; + + sprintf(field, "WAT.%s.%d_%d", wpoly, m, n); + dpfill(disp->dp+(idp++), "DQ", field, i+1, 1, 0, wval); + + if ((wp = strchr(wp, ' ')) == 0x0) { + return WCSHDRERR_PARSER; + } + wp++; + } + } } + + disp->ndp = idp; } return 0; diff --git a/cextern/wcslib/C/wcsprintf.c b/cextern/wcslib/C/wcsprintf.c index 562e31dbd86f..74457755e6d2 100644 --- a/cextern/wcslib/C/wcsprintf.c +++ b/cextern/wcslib/C/wcsprintf.c @@ -1,7 +1,6 @@ /*============================================================================ - - WCSLIB 4.14 - an implementation of the FITS WCS standard. - Copyright (C) 1995-2012, Mark Calabretta + WCSLIB 8.6 - an implementation of the FITS WCS standard. + Copyright (C) 1995-2026, Mark Calabretta This file is part of WCSLIB. @@ -18,11 +17,9 @@ You should have received a copy of the GNU Lesser General Public License along with WCSLIB. If not, see http://www.gnu.org/licenses. - Direct correspondence concerning WCSLIB to mark@calabretta.id.au - Author: Mark Calabretta, Australia Telescope National Facility, CSIRO. - http://www.atnf.csiro.au/people/Mark.Calabretta - $Id: wcsprintf.c,v 4.14 2012/07/13 10:02:27 cal103 Exp $ + http://www.atnf.csiro.au/computing/software/wcs + $Id: wcsprintf.c,v 8.6 2026/03/29 13:53:56 mcalabre Exp $ *===========================================================================*/ #include @@ -36,24 +33,27 @@ static char *wcsprintf_buff = 0x0; static char *wcsprintf_bufp = 0x0; static size_t wcsprintf_size = 0; -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- int wcsprintf_set(FILE *wcsout) + { if (wcsout != 0x0) { - /* Output to file. */ + // Output to file. wcsprintf_file = wcsout; if (wcsprintf_buff != 0x0) { - /* Release the buffer. */ + // Release the buffer. free(wcsprintf_buff); wcsprintf_buff = 0x0; } } else { - /* Output to buffer. */ + // Output to buffer. + wcsprintf_file = 0x0; + if (wcsprintf_buff == 0x0) { - /* Allocate a buffer. */ + // Allocate a buffer. wcsprintf_buff = malloc(1024); if (wcsprintf_buff == NULL) { return 1; @@ -61,7 +61,7 @@ int wcsprintf_set(FILE *wcsout) wcsprintf_size = 1024; } - /* Reset pointer to the start of the buffer. */ + // Reset pointer to the start of the buffer. wcsprintf_bufp = wcsprintf_buff; *wcsprintf_bufp = '\0'; } @@ -69,42 +69,94 @@ int wcsprintf_set(FILE *wcsout) return 0; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- const char *wcsprintf_buf(void) + { return wcsprintf_buff; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- int wcsprintf(const char *format, ...) + { + char *realloc_buff; int nbytes; size_t used; va_list arg_list; if (wcsprintf_buff == 0x0 && wcsprintf_file == 0x0) { - /* Send output to stdout if wcsprintf_set() hasn't been called. */ + // Send output to stdout if wcsprintf_set() hasn't been called. wcsprintf_file = stdout; } va_start(arg_list, format); if (wcsprintf_file) { - /* Output to file. */ + // Output to file. nbytes = vfprintf(wcsprintf_file, format, arg_list); } else { - /* Output to buffer. */ + // Output to buffer. used = wcsprintf_bufp - wcsprintf_buff; if (wcsprintf_size - used < 128) { - /* Expand the buffer. */ + // Expand the buffer. wcsprintf_size += 1024; - wcsprintf_buff = realloc(wcsprintf_buff, wcsprintf_size); - if (wcsprintf_buff == NULL) { + realloc_buff = realloc(wcsprintf_buff, wcsprintf_size); + if (realloc_buff == NULL) { + free(wcsprintf_buff); + wcsprintf_buff = 0x0; + return 1; + } + wcsprintf_buff = realloc_buff; + wcsprintf_bufp = wcsprintf_buff + used; + } + + nbytes = vsprintf(wcsprintf_bufp, format, arg_list); + wcsprintf_bufp += nbytes; + } + + va_end(arg_list); + + return nbytes; +} + +//---------------------------------------------------------------------------- + +int wcsfprintf(FILE *stream, const char *format, ...) + +{ + char *realloc_buff; + int nbytes; + size_t used; + va_list arg_list; + + if (wcsprintf_buff == 0x0 && wcsprintf_file == 0x0) { + // Send output to stream if wcsprintf_set() hasn't been called. + wcsprintf_file = stream; + } + + va_start(arg_list, format); + + if (wcsprintf_file) { + // Output to file. + nbytes = vfprintf(wcsprintf_file, format, arg_list); + + } else { + // Output to buffer. + used = wcsprintf_bufp - wcsprintf_buff; + if (wcsprintf_size - used < 128) { + // Expand the buffer. + wcsprintf_size += 1024; + realloc_buff = realloc(wcsprintf_buff, wcsprintf_size); + if (realloc_buff == NULL) { + free(wcsprintf_buff); + wcsprintf_buff = 0x0; return 1; } + wcsprintf_buff = realloc_buff; wcsprintf_bufp = wcsprintf_buff + used; } diff --git a/cextern/wcslib/C/wcsprintf.h b/cextern/wcslib/C/wcsprintf.h index dad36256ac53..d22d78bc94e8 100644 --- a/cextern/wcslib/C/wcsprintf.h +++ b/cextern/wcslib/C/wcsprintf.h @@ -1,7 +1,6 @@ /*============================================================================ - - WCSLIB 4.14 - an implementation of the FITS WCS standard. - Copyright (C) 1995-2012, Mark Calabretta + WCSLIB 8.6 - an implementation of the FITS WCS standard. + Copyright (C) 1995-2026, Mark Calabretta This file is part of WCSLIB. @@ -18,28 +17,32 @@ You should have received a copy of the GNU Lesser General Public License along with WCSLIB. If not, see http://www.gnu.org/licenses. - Direct correspondence concerning WCSLIB to mark@calabretta.id.au - Author: Mark Calabretta, Australia Telescope National Facility, CSIRO. - http://www.atnf.csiro.au/people/Mark.Calabretta - $Id: wcsprintf.h,v 4.14 2012/07/13 10:02:27 cal103 Exp $ + http://www.atnf.csiro.au/computing/software/wcs + $Id: wcsprintf.h,v 8.6 2026/03/29 13:53:56 mcalabre Exp $ *============================================================================= * -* WCSLIB 4.14 - C routines that implement the FITS World Coordinate System -* (WCS) standard. +* WCSLIB 8.6 - C routines that implement the FITS World Coordinate System +* (WCS) standard. Refer to the README file provided with WCSLIB for an +* overview of the library. +* * * Summary of the wcsprintf routines * --------------------------------- -* These routines allow diagnostic output from celprt(), linprt(), prjprt(), -* spcprt(), tabprt(), wcsprt(), and wcserr_prt() to be redirected to a file or -* captured in a string buffer. Those routines all use wcsprintf() for output. +* Routines in this suite allow diagnostic output from celprt(), linprt(), +* prjprt(), spcprt(), tabprt(), wcsprt(), and wcserr_prt() to be redirected to +* a file or captured in a string buffer. Those routines all use wcsprintf() +* for output. Likewise wcsfprintf() is used by wcsbth() and wcspih(). Both +* functions may be used by application programmers to have other output go to +* the same place. * * * wcsprintf() - Print function used by WCSLIB diagnostic routines * --------------------------------------------------------------- -* wcsprintf() is used by the celprt(), linprt(), prjprt(), spcprt(), tabprt(), -* wcsprt(), and wcserr_prt() routines. Its output may be redirected to a file -* or string buffer via wcsprintf_set(). By default output goes to stdout. +* wcsprintf() is used by celprt(), linprt(), prjprt(), spcprt(), tabprt(), +* wcsprt(), and wcserr_prt() for diagnostic output which by default goes to +* stdout. However, it may be redirected to a file or string buffer via +* wcsprintf_set(). * * Given: * format char* Format string, passed to one of the printf(3) family @@ -51,13 +54,31 @@ * int Number of bytes written. * * -* wcsprintf_set() - Set output disposition for wcsprintf() -* -------------------------------------------------------- +* wcsfprintf() - Print function used by WCSLIB diagnostic routines +* ---------------------------------------------------------------- +* wcsfprintf() is used by wcsbth(), and wcspih() for diagnostic output which +* they send to stderr. However, it may be redirected to a file or string +* buffer via wcsprintf_set(). +* +* Given: +* stream FILE* The output stream if not overridden by a call to +* wcsprintf_set(). +* +* format char* Format string, passed to one of the printf(3) family +* of stdio library functions. +* +* ... mixed Argument list matching format, as per printf(3). +* +* Function return value: +* int Number of bytes written. +* +* +* wcsprintf_set() - Set output disposition for wcsprintf() and wcsfprintf() +* ------------------------------------------------------------------------- * wcsprintf_set() sets the output disposition for wcsprintf() which is used by * the celprt(), linprt(), prjprt(), spcprt(), tabprt(), wcsprt(), and -* wcserr_prt() routines. -* -* Output goes to stdout by default if wcsprintf_set() has not been called. +* wcserr_prt() routines, and for wcsfprintf() which is used by wcsbth() and +* wcspih(). * * Given: * wcsout FILE* Pointer to an output stream that has been opened for @@ -103,23 +124,27 @@ #ifndef WCSLIB_WCSPRINTF #define WCSLIB_WCSPRINTF +#include +#include + #ifdef __cplusplus extern "C" { #endif #define WCSPRINTF_PTR(str1, ptr, str2) \ if (ptr) { \ - wcsprintf("%s%#lx%s", (str1), (unsigned long)(ptr), (str2)); \ + wcsprintf("%s%#" PRIxPTR "%s", (str1), (uintptr_t)(ptr), (str2)); \ } else { \ wcsprintf("%s0x0%s", (str1), (str2)); \ } int wcsprintf_set(FILE *wcsout); int wcsprintf(const char *format, ...); +int wcsfprintf(FILE *stream, const char *format, ...); const char *wcsprintf_buf(void); #ifdef __cplusplus } #endif -#endif /* WCSLIB_WCSPRINTF */ +#endif // WCSLIB_WCSPRINTF diff --git a/cextern/wcslib/C/wcstrig.c b/cextern/wcslib/C/wcstrig.c index 264c9a6cf0c8..a3e3297eb07f 100644 --- a/cextern/wcslib/C/wcstrig.c +++ b/cextern/wcslib/C/wcstrig.c @@ -1,7 +1,6 @@ /*============================================================================ - - WCSLIB 4.14 - an implementation of the FITS WCS standard. - Copyright (C) 1995-2012, Mark Calabretta + WCSLIB 8.6 - an implementation of the FITS WCS standard. + Copyright (C) 1995-2026, Mark Calabretta This file is part of WCSLIB. @@ -18,11 +17,9 @@ You should have received a copy of the GNU Lesser General Public License along with WCSLIB. If not, see http://www.gnu.org/licenses. - Direct correspondence concerning WCSLIB to mark@calabretta.id.au - Author: Mark Calabretta, Australia Telescope National Facility, CSIRO. - http://www.atnf.csiro.au/people/Mark.Calabretta - $Id: wcstrig.c,v 4.14 2012/07/13 10:02:27 cal103 Exp $ + http://www.atnf.csiro.au/computing/software/wcs + $Id: wcstrig.c,v 8.6 2026/03/29 13:53:56 mcalabre Exp $ *===========================================================================*/ #include @@ -30,9 +27,7 @@ #include "wcsmath.h" #include "wcstrig.h" -double cosd(angle) - -double angle; +double cosd(double angle) { int i; @@ -54,11 +49,9 @@ double angle; return cos(angle*D2R); } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- -double sind(angle) - -double angle; +double sind(double angle) { int i; @@ -80,7 +73,7 @@ double angle; return sin(angle*D2R); } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- void sincosd(double angle, double *s, double *c) @@ -119,11 +112,9 @@ void sincosd(double angle, double *s, double *c) return; } -/*--------------------------------------------------------------------------*/ - -double tand(angle) +//---------------------------------------------------------------------------- -double angle; +double tand(double angle) { double resid; @@ -140,11 +131,9 @@ double angle; return tan(angle*D2R); } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- -double acosd(v) - -double v; +double acosd(double v) { if (v >= 1.0) { @@ -158,11 +147,9 @@ double v; return acos(v)*R2D; } -/*--------------------------------------------------------------------------*/ - -double asind(v) +//---------------------------------------------------------------------------- -double v; +double asind(double v) { if (v <= -1.0) { @@ -176,11 +163,9 @@ double v; return asin(v)*R2D; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- -double atand(v) - -double v; +double atand(double v) { if (v == -1.0) { @@ -194,11 +179,9 @@ double v; return atan(v)*R2D; } -/*--------------------------------------------------------------------------*/ - -double atan2d(y, x) +//---------------------------------------------------------------------------- -double x, y; +double atan2d(double y, double x) { if (y == 0.0) { diff --git a/cextern/wcslib/C/wcstrig.h b/cextern/wcslib/C/wcstrig.h index 51bf393a2923..b16101ddb1bf 100644 --- a/cextern/wcslib/C/wcstrig.h +++ b/cextern/wcslib/C/wcstrig.h @@ -1,7 +1,6 @@ /*============================================================================ - - WCSLIB 4.14 - an implementation of the FITS WCS standard. - Copyright (C) 1995-2012, Mark Calabretta + WCSLIB 8.6 - an implementation of the FITS WCS standard. + Copyright (C) 1995-2026, Mark Calabretta This file is part of WCSLIB. @@ -18,13 +17,16 @@ You should have received a copy of the GNU Lesser General Public License along with WCSLIB. If not, see http://www.gnu.org/licenses. - Direct correspondence concerning WCSLIB to mark@calabretta.id.au - Author: Mark Calabretta, Australia Telescope National Facility, CSIRO. - http://www.atnf.csiro.au/people/Mark.Calabretta - $Id: wcstrig.h,v 4.14 2012/07/13 10:02:27 cal103 Exp $ + http://www.atnf.csiro.au/computing/software/wcs + $Id: wcstrig.h,v 8.6 2026/03/29 13:53:56 mcalabre Exp $ *============================================================================= * +* WCSLIB 8.6 - C routines that implement the FITS World Coordinate System +* (WCS) standard. Refer to the README file provided with WCSLIB for an +* overview of the library. +* +* * Summary of the wcstrig routines * ------------------------------- * When dealing with celestial coordinate systems and spherical projections @@ -139,8 +141,8 @@ * atan2d() - Polar angle of (x,y), in degrees * ------------------------------------------- * atan2d() returns the polar angle, beta, in degrees, of polar coordinates -* (rho,beta) corresponding Cartesian coordinates (x,y). It is equivalent to -* the arg(x,y) function of WCS Paper II, though with transposed arguments. +* (rho,beta) corresponding to Cartesian coordinates (x,y). It is equivalent +* to the arg(x,y) function of WCS Paper II, though with transposed arguments. * * Given: * y double Cartesian y-coordinate. @@ -170,7 +172,7 @@ extern "C" { #ifdef WCSTRIG_MACRO -/* Macro implementation of the trigd functions. */ +// Macro implementation of the trigd functions. #include "wcsmath.h" #define cosd(X) cos((X)*D2R) @@ -188,7 +190,7 @@ extern "C" { #else -/* Use WCSLIB wrappers or native trigd functions. */ +// Use WCSLIB wrappers or native trigd functions. double cosd(double angle); double sind(double angle); @@ -199,14 +201,14 @@ double asind(double y); double atand(double s); double atan2d(double y, double x); -/* Domain tolerance for asin() and acos() functions. */ +// Domain tolerance for asin() and acos() functions. #define WCSTRIG_TOL 1e-10 -#endif /* WCSTRIG_MACRO */ +#endif // WCSTRIG_MACRO #ifdef __cplusplus } #endif -#endif /* WCSLIB_WCSTRIG */ +#endif // WCSLIB_WCSTRIG diff --git a/cextern/wcslib/C/wcsulex.l b/cextern/wcslib/C/wcsulex.l index f9401ab345ff..085a669b339b 100644 --- a/cextern/wcslib/C/wcsulex.l +++ b/cextern/wcslib/C/wcsulex.l @@ -1,7 +1,6 @@ /*============================================================================ - - WCSLIB 4.14 - an implementation of the FITS WCS standard. - Copyright (C) 1995-2012, Mark Calabretta + WCSLIB 8.6 - an implementation of the FITS WCS standard. + Copyright (C) 1995-2026, Mark Calabretta This file is part of WCSLIB. @@ -18,15 +17,13 @@ You should have received a copy of the GNU Lesser General Public License along with WCSLIB. If not, see http://www.gnu.org/licenses. - Direct correspondence concerning WCSLIB to mark@calabretta.id.au - Author: Mark Calabretta, Australia Telescope National Facility, CSIRO. - http://www.atnf.csiro.au/people/Mark.Calabretta - $Id: wcsulex.l,v 4.14 2012/07/13 10:02:27 cal103 Exp $ + http://www.atnf.csiro.au/computing/software/wcs + $Id: wcsulex.l,v 8.6 2026/03/29 13:53:56 mcalabre Exp $ *============================================================================= * * wcsulex.l is a Flex description file containing the definition of a -* recursive, multi-buffered lexical scanner that parses FITS units +* recursive, multi-buffered lexical scanner and parser for FITS units * specifications. * * It requires Flex v2.5.4 or later. @@ -39,9 +36,12 @@ /* Options. */ %option full %option never-interactive +%option noinput %option noyywrap %option outfile="wcsulex.c" %option prefix="wcsulex" +%option reentrant +%option extra-type="struct wcsulex_extra *" /* Exponents. */ INTEGER [+-]?[1-9][0-9]* @@ -70,7 +70,7 @@ ADD_SUP a|yr|pc|bit|[bB]yte ADD_SUB mag /* Additional recognized units for which NO metric prefixes are allowed. */ -GENERAL deg|arcmin|arcsec|mas|d|h|min|erg|Ry|u|D +GENERAL deg|arcmin|arcsec|mas|turn|min|h|d|cy|erg|Ry|u|D ASTRO [Aa]ngstrom|AU|lyr|beam|solRad|solMass|solLum|Sun DEVICE adu|bin|chan|count|ct|photon|ph|pixel|pix|voxel ADD_NONE {GENERAL}|{ASTRO}|{DEVICE} @@ -82,10 +82,6 @@ ADD_UNIT {ADD_ALL}|{ADD_SUP}|{ADD_SUB}|{ADD_NONE} %x PAREN PREFIX UNITS EXPON FLUSH %{ -/* To get the prototype for fileno() from stdio.h when gcc is invoked with - * -std=c89 (same as -ansi) or -std=c99 since we do not define YY_INPUT. */ -#define _POSIX_SOURCE 1 - #include #include #include @@ -96,43 +92,64 @@ ADD_UNIT {ADD_ALL}|{ADD_SUP}|{ADD_SUB}|{ADD_NONE} #include "wcsunits.h" #include "wcsutil.h" -#define YY_DECL int wcsulexe(const char unitstr[], int *func, double *scale, \ - double units[], struct wcserr **err) +// User data associated with yyscanner. +struct wcsulex_extra { + // Used in preempting the call to exit() by yy_fatal_error(). + jmp_buf abort_jmp_env; +}; + +#define YY_DECL int wcsulexe_scanner(const char unitstr[], int *func, \ + double *scale, double units[WCSUNITS_NTYPE], struct wcserr **err, \ + yyscan_t yyscanner) + +// Dummy definition to circumvent compiler warnings. +#define YY_INPUT(inbuff, count, bufsize) { count = YY_NULL; } + +// Preempt the call to exit() by yy_fatal_error(). +#define exit(status) longjmp(yyextra->abort_jmp_env, status); -/* Used in preempting the call to exit() by yy_fatal_error(). */ -jmp_buf wcsulex_abort_jmp_env; -#define exit(status) longjmp(wcsulex_abort_jmp_env, status) +// Internal helper functions. +static YY_DECL; %} %% - static const char *function = "wcsulexe"; - - char ctmp[72]; - int bracket = 0; - int operator = 0; - int paren = 0; - int status = 0; - int func_r, i, j; - double dexp, expon, factor, factor_r, types[WCSUNITS_NTYPE]; - YY_BUFFER_STATE buf; + static const char *function = "wcsulexe_scanner"; + void add(double *factor, double types[], double *expon, double *scale, double units[]); - int yylex_destroy(void); - *func = 0; - for (i = 0; i < WCSUNITS_NTYPE; i++) { + // Initialise returned values. + *func = 0; + *scale = 1.0; + + for (int i = 0; i < WCSUNITS_NTYPE; i++) { units[i] = 0.0; + } + + if (err) *err = 0x0; + + double types[WCSUNITS_NTYPE]; + for (int i = 0; i < WCSUNITS_NTYPE; i++) { types[i] = 0.0; } - expon = 1.0; - factor = 1.0; - *scale = 1.0; + double expon = 1.0; + double factor = 1.0; - yy_scan_string(unitstr); + int bracket = 0; + int operator = 0; + int paren = 0; + int status = 0; + + // Avert a flex-induced memory leak. + if (YY_CURRENT_BUFFER && YY_CURRENT_BUFFER->yy_input_file == stdin) { + yy_delete_buffer(YY_CURRENT_BUFFER, yyscanner); + } - /* Return here via longjmp() invoked by yy_fatal_error(). */ - if (setjmp(wcsulex_abort_jmp_env)) { + yy_scan_string(unitstr, yyscanner); + + // Return here via longjmp() invoked by yy_fatal_error(). + if (setjmp(yyextra->abort_jmp_env)) { return wcserr_set(WCSERR_SET(UNITSERR_PARSER_ERROR), "Internal units parser error parsing '%s'", unitstr); } @@ -144,7 +161,7 @@ jmp_buf wcsulex_abort_jmp_env; #endif ^" "+ { - /* Pretend initial whitespace doesn't exist. */ + // Pretend initial whitespace doesn't exist. yy_set_bol(1); } @@ -186,13 +203,13 @@ jmp_buf wcsulex_abort_jmp_env; } ^[*.] { - /* Leading binary multiply. */ + // Leading binary multiply. status = wcserr_set(WCSERR_SET(UNITSERR_DANGLING_BINOP), "Dangling binary operator in '%s'", unitstr); BEGIN(FLUSH); } -" "+ /* Discard whitespace in INITIAL context. */ +" "+ // Discard whitespace in INITIAL context. sqrt" "*"(" { expon /= 2.0; @@ -201,7 +218,7 @@ sqrt" "*"(" { } "(" { - /* Gather terms in parentheses. */ + // Gather terms in parentheses. yyless(0); BEGIN(PAREN); } @@ -255,16 +272,19 @@ sqrt" "*"(" { ")" { paren--; if (paren) { - /* Not balanced yet. */ + // Not balanced yet. yymore(); } else { - /* Balanced; strip off the outer parentheses and recurse. */ + // Balanced; strip off the outer parentheses and recurse. yytext[yyleng-1] = '\0'; - buf = YY_CURRENT_BUFFER; + int func_r; + double factor_r; status = wcsulexe(yytext+1, &func_r, &factor_r, types, err); - yy_switch_to_buffer(buf); + + YY_BUFFER_STATE buf = YY_CURRENT_BUFFER; + yy_switch_to_buffer(buf, yyscanner); if (func_r) { status = wcserr_set(WCSERR_SET(UNITSERR_FUNCTION_CONTEXT), @@ -385,118 +405,125 @@ sqrt" "*"(" { } . { - /* Internal parser error. */ + // Internal parser error. status = wcserr_set(WCSERR_SET(UNITSERR_PARSER_ERROR), "Internal units parser error parsing '%s'", unitstr); BEGIN(FLUSH); } A { - /* Ampere. */ + // Ampere. types[WCSUNITS_CHARGE] += 1.0; types[WCSUNITS_TIME] -= 1.0; BEGIN(EXPON); } a|yr { - /* Year (annum). */ + // Julian year (annum). factor *= 31557600.0; types[WCSUNITS_TIME] += 1.0; BEGIN(EXPON); } adu { - /* Analogue-to-digital converter units. */ + // Analogue-to-digital converter units. types[WCSUNITS_COUNT] += 1.0; BEGIN(EXPON); } [Aa]ngstrom { - /* Angstrom. */ + // Angstrom. factor *= 1e-10; types[WCSUNITS_LENGTH] += 1.0; BEGIN(EXPON); } arcmin { - /* Minute of arc. */ + // Minute of arc. factor /= 60.0; types[WCSUNITS_PLANE_ANGLE] += 1.0; BEGIN(EXPON); } arcsec { - /* Second of arc. */ + // Second of arc. factor /= 3600.0; types[WCSUNITS_PLANE_ANGLE] += 1.0; BEGIN(EXPON); } AU { - /* Astronomical unit. */ + // Astronomical unit. factor *= 1.49598e+11; types[WCSUNITS_LENGTH] += 1.0; BEGIN(EXPON); } barn { - /* Barn. */ + // Barn. factor *= 1e-28; types[WCSUNITS_LENGTH] += 2.0; BEGIN(EXPON); } beam { - /* Beam, as in Jy/beam. */ + // Beam, as in Jy/beam. types[WCSUNITS_BEAM] += 1.0; BEGIN(EXPON); } bin { - /* Bin (e.g. histogram). */ + // Bin (e.g. histogram). types[WCSUNITS_BIN] += 1.0; BEGIN(EXPON); } bit { - /* Bit. */ + // Bit. types[WCSUNITS_BIT] += 1.0; BEGIN(EXPON); } [bB]yte { - /* Byte. */ + // Byte. factor *= 8.0; types[WCSUNITS_BIT] += 1.0; BEGIN(EXPON); } C { - /* Coulomb. */ + // Coulomb. types[WCSUNITS_CHARGE] += 1.0; BEGIN(EXPON); } cd { - /* Candela. */ + // Candela. types[WCSUNITS_LUMINTEN] += 1.0; BEGIN(EXPON); } chan { - /* Channel. */ + // Channel. types[WCSUNITS_BIN] += 1.0; BEGIN(EXPON); } count|ct { - /* Count. */ + // Count. types[WCSUNITS_COUNT] += 1.0; BEGIN(EXPON); } +cy { + // Julian century. + factor *= 3155760000.0; + types[WCSUNITS_TIME] += 1.0; + BEGIN(EXPON); + } + D { - /* Debye. */ + // Debye. factor *= 1e-29 / 3.0; types[WCSUNITS_CHARGE] += 1.0; types[WCSUNITS_LENGTH] += 1.0; @@ -504,20 +531,20 @@ sqrt" "*"(" { } d { - /* Day. */ + // Day. factor *= 86400.0; types[WCSUNITS_TIME] += 1.0; BEGIN(EXPON); } deg { - /* Degree. */ + // Degree. types[WCSUNITS_PLANE_ANGLE] += 1.0; BEGIN(EXPON); } erg { - /* Erg. */ + // Erg. factor *= 1e-7; types[WCSUNITS_MASS] += 1.0; types[WCSUNITS_LENGTH] += 2.0; @@ -526,7 +553,7 @@ sqrt" "*"(" { } eV { - /* Electron volt. */ + // Electron volt. factor *= 1.6021765e-19; types[WCSUNITS_MASS] += 1.0; types[WCSUNITS_LENGTH] += 2.0; @@ -535,7 +562,7 @@ sqrt" "*"(" { } F { - /* Farad. */ + // Farad. types[WCSUNITS_MASS] -= 1.0; types[WCSUNITS_LENGTH] -= 2.0; types[WCSUNITS_TIME] += 3.0; @@ -544,7 +571,7 @@ sqrt" "*"(" { } G { - /* Gauss. */ + // Gauss. factor *= 1e-4; types[WCSUNITS_MASS] += 1.0; types[WCSUNITS_TIME] += 1.0; @@ -553,14 +580,14 @@ sqrt" "*"(" { } g { - /* Gram. */ + // Gram. factor *= 1e-3; types[WCSUNITS_MASS] += 1.0; BEGIN(EXPON); } H { - /* Henry. */ + // Henry. types[WCSUNITS_MASS] += 1.0; types[WCSUNITS_LENGTH] += 2.0; types[WCSUNITS_TIME] += 2.0; @@ -569,20 +596,20 @@ sqrt" "*"(" { } h { - /* Hour. */ + // Hour. factor *= 3600.0; types[WCSUNITS_TIME] += 1.0; BEGIN(EXPON); } Hz { - /* Hertz. */ + // Hertz. types[WCSUNITS_TIME] -= 1.0; BEGIN(EXPON); } J { - /* Joule. */ + // Joule. types[WCSUNITS_MASS] += 1.0; types[WCSUNITS_LENGTH] += 2.0; types[WCSUNITS_TIME] -= 2.0; @@ -590,7 +617,7 @@ sqrt" "*"(" { } Jy { - /* Jansky. */ + // Jansky. factor *= 1e-26; types[WCSUNITS_MASS] += 1.0; types[WCSUNITS_TIME] -= 2.0; @@ -598,20 +625,20 @@ sqrt" "*"(" { } K { - /* Kelvin. */ + // Kelvin. types[WCSUNITS_TEMPERATURE] += 1.0; BEGIN(EXPON); } lm { - /* Lumen. */ + // Lumen. types[WCSUNITS_LUMINTEN] += 1.0; types[WCSUNITS_SOLID_ANGLE] += 1.0; BEGIN(EXPON); } lx { - /* Lux. */ + // Lux. types[WCSUNITS_LUMINTEN] += 1.0; types[WCSUNITS_SOLID_ANGLE] += 1.0; types[WCSUNITS_LENGTH] -= 2.0; @@ -619,46 +646,46 @@ sqrt" "*"(" { } lyr { - /* Light year. */ + // Light year. factor *= 2.99792458e8 * 31557600.0; types[WCSUNITS_LENGTH] += 1.0; BEGIN(EXPON); } m { - /* Metre. */ + // Metre. types[WCSUNITS_LENGTH] += 1.0; BEGIN(EXPON); } mag { - /* Stellar magnitude. */ + // Stellar magnitude. types[WCSUNITS_MAGNITUDE] += 1.0; BEGIN(EXPON); } mas { - /* Milli-arcsec. */ + // Milli-arcsec. factor /= 3600e+3; types[WCSUNITS_PLANE_ANGLE] += 1.0; BEGIN(EXPON); } min { - /* Minute. */ + // Minute. factor *= 60.0; types[WCSUNITS_TIME] += 1.0; BEGIN(EXPON); } mol { - /* Mole. */ + // Mole. types[WCSUNITS_MOLE] += 1.0; BEGIN(EXPON); } N { - /* Newton. */ + // Newton. types[WCSUNITS_MASS] += 1.0; types[WCSUNITS_LENGTH] += 1.0; types[WCSUNITS_TIME] -= 2.0; @@ -666,7 +693,7 @@ sqrt" "*"(" { } [Oo]hm { - /* Ohm. */ + // Ohm. types[WCSUNITS_MASS] += 1.0; types[WCSUNITS_LENGTH] += 2.0; types[WCSUNITS_TIME] -= 1.0; @@ -675,7 +702,7 @@ sqrt" "*"(" { } Pa { - /* Pascal. */ + // Pascal. types[WCSUNITS_MASS] += 1.0; types[WCSUNITS_LENGTH] -= 1.0; types[WCSUNITS_TIME] -= 2.0; @@ -683,26 +710,26 @@ sqrt" "*"(" { } pc { - /* Parsec. */ + // Parsec. factor *= 3.0857e16; types[WCSUNITS_LENGTH] += 1.0; BEGIN(EXPON); } photon|ph { - /* Photon. */ + // Photon. types[WCSUNITS_COUNT] += 1.0; BEGIN(EXPON); } pixel|pix { - /* Pixel. */ + // Pixel. types[WCSUNITS_PIXEL] += 1.0; BEGIN(EXPON); } R { - /* Rayleigh. */ + // Rayleigh. factor *= 1e10 / (4.0 * PI); types[WCSUNITS_LENGTH] -= 2.0; types[WCSUNITS_TIME] -= 1.0; @@ -711,14 +738,14 @@ sqrt" "*"(" { } rad { - /* Radian. */ + // Radian. factor *= 180.0 / PI; types[WCSUNITS_PLANE_ANGLE] += 1.0; BEGIN(EXPON); } Ry { - /* Rydberg. */ + // Rydberg. factor *= 13.605692 * 1.6021765e-19; types[WCSUNITS_MASS] += 1.0; types[WCSUNITS_LENGTH] += 2.0; @@ -727,7 +754,7 @@ sqrt" "*"(" { } S { - /* Siemen. */ + // Siemen. types[WCSUNITS_MASS] -= 1.0; types[WCSUNITS_LENGTH] -= 2.0; types[WCSUNITS_TIME] += 1.0; @@ -736,13 +763,13 @@ sqrt" "*"(" { } s { - /* Second. */ + // Second. types[WCSUNITS_TIME] += 1.0; BEGIN(EXPON); } solLum { - /* Solar luminosity. */ + // Solar luminosity. factor *= 3.8268e26; types[WCSUNITS_MASS] += 1.0; types[WCSUNITS_LENGTH] += 2.0; @@ -751,48 +778,55 @@ sqrt" "*"(" { } solMass { - /* Solar mass. */ + // Solar mass. factor *= 1.9891e30; types[WCSUNITS_MASS] += 1.0; BEGIN(EXPON); } solRad { - /* Solar radius. */ + // Solar radius. factor *= 6.9599e8; types[WCSUNITS_LENGTH] += 1.0; BEGIN(EXPON); } sr { - /* Steradian. */ + // Steradian. types[WCSUNITS_SOLID_ANGLE] += 1.0; BEGIN(EXPON); } Sun { - /* Sun (with respect to). */ + // Sun (with respect to). types[WCSUNITS_SOLRATIO] += 1.0; BEGIN(EXPON); } T { - /* Tesla. */ + // Tesla. types[WCSUNITS_MASS] += 1.0; types[WCSUNITS_TIME] += 1.0; types[WCSUNITS_CHARGE] -= 1.0; BEGIN(EXPON); } +turn { + // Turn. + factor *= 360.0; + types[WCSUNITS_PLANE_ANGLE] += 1.0; + BEGIN(EXPON); + } + u { - /* Unified atomic mass unit. */ + // Unified atomic mass unit. factor *= 1.6605387e-27; types[WCSUNITS_MASS] += 1.0; BEGIN(EXPON); } V { - /* Volt. */ + // Volt. types[WCSUNITS_MASS] += 1.0; types[WCSUNITS_LENGTH] += 1.0; types[WCSUNITS_TIME] -= 2.0; @@ -801,13 +835,13 @@ sqrt" "*"(" { } voxel { - /* Voxel. */ + // Voxel. types[WCSUNITS_VOXEL] += 1.0; BEGIN(EXPON); } W { - /* Watt. */ + // Watt. types[WCSUNITS_MASS] += 1.0; types[WCSUNITS_LENGTH] += 2.0; types[WCSUNITS_TIME] -= 3.0; @@ -815,7 +849,7 @@ sqrt" "*"(" { } Wb { - /* Weber. */ + // Weber. types[WCSUNITS_MASS] += 1.0; types[WCSUNITS_LENGTH] += 2.0; types[WCSUNITS_TIME] += 1.0; @@ -824,20 +858,21 @@ sqrt" "*"(" { } . { - /* Internal parser error. */ + // Internal parser error. status = wcserr_set(WCSERR_SET(UNITSERR_PARSER_ERROR), "Internal units parser error parsing '%s'", unitstr); BEGIN(FLUSH); } " "*("**"|^) { - /* Exponentiation. */ + // Exponentiation. if (operator++) { BEGIN(FLUSH); } } " "*{INTEGER} { + int i; sscanf(yytext, " %d", &i); expon *= (double)i; add(&factor, types, &expon, scale, units); @@ -846,6 +881,7 @@ sqrt" "*"(" { } " "*"("" "*{INTEGER}" "*")" { + int i; sscanf(yytext, " (%d)", &i); expon *= (double)i; add(&factor, types, &expon, scale, units); @@ -854,6 +890,7 @@ sqrt" "*"(" { } " "*"("" "*{FRAC}" "*")" { + int i, j; sscanf(yytext, " (%d/%d)", &i, &j); expon *= (double)i / (double)j; add(&factor, types, &expon, scale, units); @@ -862,8 +899,10 @@ sqrt" "*"(" { } " "*"("" "*{FLOAT}" "*")" { + char ctmp[72]; sscanf(yytext, " (%s)", ctmp); - wcsutil_str2double(ctmp, "%lf", &dexp); + double dexp; + wcsutil_str2double(ctmp, &dexp); expon *= dexp; add(&factor, types, &expon, scale, units); operator = 0; @@ -871,7 +910,7 @@ sqrt" "*"(" { } " "*[.*]" "* { - /* Multiply. */ + // Multiply. if (operator++) { BEGIN(FLUSH); } else { @@ -881,7 +920,7 @@ sqrt" "*"(" { } " "*"(" { - /* Multiply. */ + // Multiply. if (operator) { BEGIN(FLUSH); } else { @@ -892,7 +931,7 @@ sqrt" "*"(" { } " "+ { - /* Multiply. */ + // Multiply. if (operator) { BEGIN(FLUSH); } else { @@ -902,7 +941,7 @@ sqrt" "*"(" { } " "*"/"" "* { - /* Divide. */ + // Divide. if (operator++) { BEGIN(FLUSH); } else { @@ -925,17 +964,15 @@ sqrt" "*"(" { } .* { - /* Discard any remaining input. */ + // Discard any remaining input. } <> { - /* End-of-string. */ + // End-of-string. if (YY_START == EXPON) { add(&factor, types, &expon, scale, units); } - yylex_destroy(); - if (bracket) { status = wcserr_set(WCSERR_SET(UNITSERR_UNBAL_BRACKET), "Unbalanced bracket in '%s'", unitstr); @@ -955,7 +992,7 @@ sqrt" "*"(" { } if (status) { - for (i = 0; i < WCSUNITS_NTYPE; i++) { + for (int i = 0; i < WCSUNITS_NTYPE; i++) { units[i] = 0.0; *scale = 0.0; } @@ -966,6 +1003,32 @@ sqrt" "*"(" { %% +/*---------------------------------------------------------------------------- +* External interface to the scanner. +*---------------------------------------------------------------------------*/ + +int wcsulexe( + const char unitstr[], + int *func, + double *scale, + double units[WCSUNITS_NTYPE], + struct wcserr **err) + +{ + // Function prototypes. + int yylex_init_extra(YY_EXTRA_TYPE extra, yyscan_t *yyscanner); + int yylex_destroy(yyscan_t yyscanner); + + struct wcsulex_extra extra; + yyscan_t yyscanner; + yylex_init_extra(&extra, &yyscanner); + int status = wcsulexe_scanner(unitstr, func, scale, units, err, yyscanner); + yylex_destroy(yyscanner); + + return status; +} + + /*---------------------------------------------------------------------------- * Accumulate a term in a units specification and reset work variables. *---------------------------------------------------------------------------*/ @@ -978,11 +1041,9 @@ void add( double units[]) { - int i; - *scale *= pow(*factor, *expon); - for (i = 0; i < WCSUNITS_NTYPE; i++) { + for (int i = 0; i < WCSUNITS_NTYPE; i++) { units[i] += *expon * types[i]; types[i] = 0.0; } diff --git a/cextern/wcslib/C/wcsunits.c b/cextern/wcslib/C/wcsunits.c index ddfd3447c3fc..b9b4a29b9a21 100644 --- a/cextern/wcslib/C/wcsunits.c +++ b/cextern/wcslib/C/wcsunits.c @@ -1,7 +1,6 @@ /*============================================================================ - - WCSLIB 4.14 - an implementation of the FITS WCS standard. - Copyright (C) 1995-2012, Mark Calabretta + WCSLIB 8.6 - an implementation of the FITS WCS standard. + Copyright (C) 1995-2026, Mark Calabretta This file is part of WCSLIB. @@ -18,18 +17,16 @@ You should have received a copy of the GNU Lesser General Public License along with WCSLIB. If not, see http://www.gnu.org/licenses. - Direct correspondence concerning WCSLIB to mark@calabretta.id.au - Author: Mark Calabretta, Australia Telescope National Facility, CSIRO. - http://www.atnf.csiro.au/people/Mark.Calabretta - $Id: wcsunits.c,v 4.14 2012/07/13 10:02:27 cal103 Exp $ + http://www.atnf.csiro.au/computing/software/wcs + $Id: wcsunits.c,v 8.6 2026/03/29 13:53:56 mcalabre Exp $ *===========================================================================*/ #include #include "wcsunits.h" -/* Map status return value to message. */ +// Map status return value to message. const char *wcsunits_errmsg[] = { "Success", "Invalid numeric multiplier", @@ -46,7 +43,7 @@ const char *wcsunits_errmsg[] = { "Potentially unsafe translation"}; -/* Unit types. */ +// Unit types. const char *wcsunits_types[] = { "plane angle", "solid angle", @@ -84,7 +81,7 @@ const char *wcsunits_funcs[] = { "ln", "exp"}; -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- int wcsunits( const char have[], @@ -94,11 +91,10 @@ int wcsunits( double *power) { - return wcsunitse( - have, want, scale, offset, power, 0x0); + return wcsunitse(have, want, scale, offset, power, 0x0); } -/* : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : */ +//---------------------------------------------------------------------------- int wcsunitse( const char have[], @@ -111,19 +107,27 @@ int wcsunitse( { static const char *function = "wcsunitse"; - int func1, func2, i, status; - double scale1, scale2, units1[WCSUNITS_NTYPE], units2[WCSUNITS_NTYPE]; + int status; + + // Compiler balm for premature return on error. + *scale = 0.0; + *offset = 0.0; + *power = 1.0; + int func1; + double scale1, units1[WCSUNITS_NTYPE]; if ((status = wcsulexe(have, &func1, &scale1, units1, err))) { return status; } + int func2; + double scale2, units2[WCSUNITS_NTYPE]; if ((status = wcsulexe(want, &func2, &scale2, units2, err))) { return status; } - /* Check conformance. */ - for (i = 0; i < WCSUNITS_NTYPE; i++) { + // Check conformance. + for (int i = 0; i < WCSUNITS_NTYPE; i++) { if (units1[i] != units2[i]) { return wcserr_set(WCSERR_SET(UNITSERR_BAD_UNIT_SPEC), "Mismatched units type '%s': have '%s', want '%s'", @@ -131,13 +135,9 @@ int wcsunitse( } } - *scale = 0.0; - *offset = 0.0; - *power = 1.0; - switch (func1) { case 0: - /* No function. */ + // No function. if (func2) { return wcserr_set(WCSERR_SET(UNITSERR_BAD_FUNCS), "Mismatched unit functions: have '%s' (%s), want '%s' (%s)", @@ -148,14 +148,14 @@ int wcsunitse( break; case 1: - /* log(). */ + // log(). if (func2 == 1) { - /* log(). */ + // log(). *scale = 1.0; *offset = log10(scale1 / scale2); } else if (func2 == 2) { - /* ln(). */ + // ln(). *scale = log(10.0); *offset = log(scale1 / scale2); @@ -168,14 +168,14 @@ int wcsunitse( break; case 2: - /* ln(). */ + // ln(). if (func2 == 1) { - /* log(). */ + // log(). *scale = 1.0 / log(10.0); *offset = log(scale1 / scale2); } else if (func2 == 2) { - /* ln(). */ + // ln(). *scale = 1.0; *offset = log(scale1 / scale2); @@ -188,7 +188,7 @@ int wcsunitse( break; case 3: - /* exp(). */ + // exp(). if (func2 != 3) { return wcserr_set(WCSERR_SET(UNITSERR_BAD_FUNCS), "Mismatched unit functions: have '%s' (%s), want '%s' (%s)", @@ -200,7 +200,7 @@ int wcsunitse( break; default: - /* Internal parser error. */ + // Internal parser error. return wcserr_set(WCSERR_SET(UNITSERR_PARSER_ERROR), "Internal units parser error"); } @@ -208,7 +208,7 @@ int wcsunitse( return 0; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- int wcsutrn(int ctrl, char unitstr[]) @@ -216,9 +216,13 @@ int wcsutrn(int ctrl, char unitstr[]) return wcsutrne(ctrl, unitstr, 0x0); } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- -int wcsulex(const char unitstr[], int *func, double *scale, double units[]) +int wcsulex( + const char unitstr[], + int *func, + double *scale, + double units[WCSUNITS_NTYPE]) { return wcsulexe(unitstr, func, scale, units, 0x0); diff --git a/cextern/wcslib/C/wcsunits.h b/cextern/wcslib/C/wcsunits.h index 0b677832b161..37666d50c224 100644 --- a/cextern/wcslib/C/wcsunits.h +++ b/cextern/wcslib/C/wcsunits.h @@ -1,7 +1,6 @@ /*============================================================================ - - WCSLIB 4.14 - an implementation of the FITS WCS standard. - Copyright (C) 1995-2012, Mark Calabretta + WCSLIB 8.6 - an implementation of the FITS WCS standard. + Copyright (C) 1995-2026, Mark Calabretta This file is part of WCSLIB. @@ -18,35 +17,34 @@ You should have received a copy of the GNU Lesser General Public License along with WCSLIB. If not, see http://www.gnu.org/licenses. - Direct correspondence concerning WCSLIB to mark@calabretta.id.au - Author: Mark Calabretta, Australia Telescope National Facility, CSIRO. - http://www.atnf.csiro.au/people/Mark.Calabretta - $Id: wcsunits.h,v 4.14 2012/07/13 10:02:27 cal103 Exp $ + http://www.atnf.csiro.au/computing/software/wcs + $Id: wcsunits.h,v 8.6 2026/03/29 13:53:56 mcalabre Exp $ *============================================================================= * -* WCSLIB 4.14 - C routines that implement the FITS World Coordinate System -* (WCS) standard. Refer to +* WCSLIB 8.6 - C routines that implement the FITS World Coordinate System +* (WCS) standard. Refer to the README file provided with WCSLIB for an +* overview of the library. * -* "Representations of world coordinates in FITS", -* Greisen, E.W., & Calabretta, M.R. 2002, A&A, 395, 1061 (Paper I) * -* The Flexible Image Transport System (FITS), a data format widely used in -* astronomy for data interchange and archive, is described in +* Summary of the wcsunits routines +* -------------------------------- +* Routines in this suite deal with units specifications and conversions, as +* described in * -* "Definition of The Flexible Image Transport System (FITS)", -* Hanisch, R.J., Farris, A., Greisen, E.W., et al. 2001, A&A, 376, 359 += "Representations of world coordinates in FITS", += Greisen, E.W., & Calabretta, M.R. 2002, A&A, 395, 1061 (WCS Paper I) * -* which formalizes NOST 100-2.0, a document produced by the NASA/Science -* Office of Standards and Technology, see http://fits.gsfc.nasa.gov. +* The Flexible Image Transport System (FITS), a data format widely used in +* astronomy for data interchange and archive, is described in * -* Refer to the README file provided with WCSLIB for an overview of the -* library. += "Definition of the Flexible Image Transport System (FITS), version 3.0", += Pence, W.D., Chiappetti, L., Page, C.G., Shaw, R.A., & Stobie, E. 2010, += A&A, 524, A42 - http://dx.doi.org/10.1051/0004-6361/201015362 * +* See also http://fits.gsfc.nasa.gov * -* Summary of the wcsunits routines -* -------------------------------- -* Routines in this suite deal with units specifications and conversions: +* These routines perform basic units-related operations: * * - wcsunitse(): given two unit specifications, derive the conversion from * one to the other. @@ -98,7 +96,8 @@ * If enabled, for function return values > 1, this * struct will contain a detailed error message, see * wcserr_enable(). May be NULL if an error message is -* not desired. +* not desired. Otherwise, the user is responsible for +* deleting the memory allocated for the wcserr struct. * * Function return value: * int Status return value: @@ -136,7 +135,7 @@ * unitstr char [] Null-terminated character array containing the units * specification to be translated. * -* Inline units specifications in the a FITS header +* Inline units specifications in a FITS header * keycomment are also handled. If the first non-blank * character in unitstr is '[' then the unit string is * delimited by its matching ']'. Blanks preceding '[' @@ -147,7 +146,8 @@ * If enabled, for function return values > 1, this * struct will contain a detailed error message, see * wcserr_enable(). May be NULL if an error message is -* not desired. +* not desired. Otherwise, the user is responsible for +* deleting the memory allocated for the wcserr struct. * * Function return value: * int Status return value: @@ -156,45 +156,47 @@ * 0: Success. * 9: Internal parser error. * 12: Potentially unsafe translation, whether applied -* or not (see notes). +* or not (see notes). * * Notes: -* Translation of non-standard unit specifications: apart from leading and -* trailing blanks, a case-sensitive match is required for the aliases listed -* below, in particular the only recognized aliases with metric prefixes are -* "KM", "KHZ", "MHZ", and "GHZ". Potentially unsafe translations of "D", -* "H", and "S", shown in parentheses, are optional. -* -= Unit Recognized aliases -= ---- ------------------------------------------------------------- -= Angstrom angstrom -= arcmin arcmins, ARCMIN, ARCMINS -= arcsec arcsecs, ARCSEC, ARCSECS -= beam BEAM -= byte Byte -= d day, days, (D), DAY, DAYS -= deg degree, degrees, DEG, DEGREE, DEGREES -= GHz GHZ -= h hr, (H), HR -= Hz hz, HZ -= kHz KHZ -= Jy JY -= K kelvin, kelvins, Kelvin, Kelvins, KELVIN, KELVINS -= km KM -= m metre, meter, metres, meters, M, METRE, METER, METRES, METERS -= min MIN -= MHz MHZ -= Ohm ohm -= Pa pascal, pascals, Pascal, Pascals, PASCAL, PASCALS -= pixel pixels, PIXEL, PIXELS -= rad radian, radians, RAD, RADIAN, RADIANS -= s sec, second, seconds, (S), SEC, SECOND, SECONDS -= V volt, volts, Volt, Volts, VOLT, VOLTS -= yr year, years, YR, YEAR, YEARS -* -* The aliases "angstrom", "ohm", and "Byte" for (Angstrom, Ohm, and byte) -* are recognized by wcsulexe() itself as an unofficial extension of the -* standard, but they are converted to the standard form here. +* 1: Translation of non-standard unit specifications: apart from leading and +* trailing blanks, a case-sensitive match is required for the aliases +* listed below, in particular the only recognized aliases with metric +* prefixes are "KM", "KHZ", "MHZ", and "GHZ". Potentially unsafe +* translations of "D", "H", and "S", shown in parentheses, are optional. +* += Unit Recognized aliases += ---- ---------------------------------------------------------- += Angstrom Angstroms angstrom angstroms += arcmin arcmins, ARCMIN, ARCMINS += arcsec arcsecs, ARCSEC, ARCSECS += beam BEAM += byte Byte += d day, days, (D), DAY, DAYS += deg degree, degrees, Deg, Degree, Degrees, DEG, DEGREE, += DEGREES += GHz GHZ += h hr, (H), HR += Hz hz, HZ += kHz KHZ += Jy JY += K kelvin, kelvins, Kelvin, Kelvins, KELVIN, KELVINS += km KM += m metre, meter, metres, meters, M, METRE, METER, METRES, += METERS += min MIN += MHz MHZ += Ohm ohm += Pa pascal, pascals, Pascal, Pascals, PASCAL, PASCALS += pixel pixels, PIXEL, PIXELS += rad radian, radians, RAD, RADIAN, RADIANS += s sec, second, seconds, (S), SEC, SECOND, SECONDS += V volt, volts, Volt, Volts, VOLT, VOLTS += yr year, years, YR, YEAR, YEARS +* +* The aliases "angstrom", "ohm", and "Byte" for (Angstrom, Ohm, and byte) +* are recognized by wcsulexe() itself as an unofficial extension of the +* standard, but they are converted to the standard form here. * * * wcsulexe() - FITS units specification parser @@ -240,7 +242,8 @@ * If enabled, for function return values > 1, this * struct will contain a detailed error message, see * wcserr_enable(). May be NULL if an error message is -* not desired. +* not desired. Otherwise, the user is responsible for +* deleting the memory allocated for the wcserr struct. * * Function return value: * int Status return value: @@ -344,21 +347,19 @@ extern "C" { extern const char *wcsunits_errmsg[]; enum wcsunits_errmsg_enum { - UNITSERR_SUCCESS = 0, /* Success. */ - UNITSERR_BAD_NUM_MULTIPLIER = 1, /* Invalid numeric multiplier. */ - UNITSERR_DANGLING_BINOP = 2, /* Dangling binary operator. */ - UNITSERR_BAD_INITIAL_SYMBOL = 3, /* Invalid symbol in INITIAL - context. */ - UNITSERR_FUNCTION_CONTEXT = 4, /* Function in invalid context. */ - UNITSERR_BAD_EXPON_SYMBOL = 5, /* Invalid symbol in EXPON context. */ - UNITSERR_UNBAL_BRACKET = 6, /* Unbalanced bracket. */ - UNITSERR_UNBAL_PAREN = 7, /* Unbalanced parenthesis. */ - UNITSERR_CONSEC_BINOPS = 8, /* Consecutive binary operators. */ - UNITSERR_PARSER_ERROR = 9, /* Internal parser error. */ - UNITSERR_BAD_UNIT_SPEC = 10, /* Non-conformant unit - specifications. */ - UNITSERR_BAD_FUNCS = 11, /* Non-conformant functions. */ - UNITSERR_UNSAFE_TRANS = 12 /* Potentially unsafe translation. */ + UNITSERR_SUCCESS = 0, // Success. + UNITSERR_BAD_NUM_MULTIPLIER = 1, // Invalid numeric multiplier. + UNITSERR_DANGLING_BINOP = 2, // Dangling binary operator. + UNITSERR_BAD_INITIAL_SYMBOL = 3, // Invalid symbol in INITIAL context. + UNITSERR_FUNCTION_CONTEXT = 4, // Function in invalid context. + UNITSERR_BAD_EXPON_SYMBOL = 5, // Invalid symbol in EXPON context. + UNITSERR_UNBAL_BRACKET = 6, // Unbalanced bracket. + UNITSERR_UNBAL_PAREN = 7, // Unbalanced parenthesis. + UNITSERR_CONSEC_BINOPS = 8, // Consecutive binary operators. + UNITSERR_PARSER_ERROR = 9, // Internal parser error. + UNITSERR_BAD_UNIT_SPEC = 10, // Non-conformant unit specifications. + UNITSERR_BAD_FUNCS = 11, // Non-conformant functions. + UNITSERR_UNSAFE_TRANS = 12 // Potentially unsafe translation. }; extern const char *wcsunits_types[]; @@ -390,17 +391,18 @@ int wcsunitse(const char have[], const char want[], double *scale, int wcsutrne(int ctrl, char unitstr[], struct wcserr **err); -int wcsulexe(const char unitstr[], int *func, double *scale, double units[], - struct wcserr **err); +int wcsulexe(const char unitstr[], int *func, double *scale, + double units[WCSUNITS_NTYPE], struct wcserr **err); -/* Deprecated. */ +// Deprecated. int wcsunits(const char have[], const char want[], double *scale, double *offset, double *power); int wcsutrn(int ctrl, char unitstr[]); -int wcsulex(const char unitstr[], int *func, double *scale, double units[]); +int wcsulex(const char unitstr[], int *func, double *scale, + double units[WCSUNITS_NTYPE]); #ifdef __cplusplus } #endif -#endif /* WCSLIB_WCSUNITS */ +#endif // WCSLIB_WCSUNITS diff --git a/cextern/wcslib/C/wcsutil.c b/cextern/wcslib/C/wcsutil.c index 521a1ae1bb26..355e34e793e8 100644 --- a/cextern/wcslib/C/wcsutil.c +++ b/cextern/wcslib/C/wcsutil.c @@ -1,7 +1,6 @@ /*============================================================================ - - WCSLIB 4.14 - an implementation of the FITS WCS standard. - Copyright (C) 1995-2012, Mark Calabretta + WCSLIB 8.6 - an implementation of the FITS WCS standard. + Copyright (C) 1995-2026, Mark Calabretta This file is part of WCSLIB. @@ -18,156 +17,350 @@ You should have received a copy of the GNU Lesser General Public License along with WCSLIB. If not, see http://www.gnu.org/licenses. - Direct correspondence concerning WCSLIB to mark@calabretta.id.au - Author: Mark Calabretta, Australia Telescope National Facility, CSIRO. - http://www.atnf.csiro.au/people/Mark.Calabretta - $Id: wcsutil.c,v 4.14 2012/07/13 10:02:27 cal103 Exp $ + http://www.atnf.csiro.au/computing/software/wcs + $Id: wcsutil.c,v 8.6 2026/03/29 13:53:56 mcalabre Exp $ *===========================================================================*/ #include #include +#include #include +#include #include #include "wcsutil.h" +#include "wcsmath.h" + +//---------------------------------------------------------------------------- + +void wcsdealloc(void *ptr) + +{ + free(ptr); + + return; +} + +//---------------------------------------------------------------------------- + +void wcsutil_strcvt(int n, char c, int nt, const char src[], char dst[]) + +{ + if (n <= 0) return; + + if (c != '\0') c = ' '; + + if (src == 0x0) { + if (dst) { + memset(dst, c, n); + } + + } else { + // Copy to the first NULL character. + int j; + for (j = 0; j < n; j++) { + if ((dst[j] = src[j]) == '\0') { + break; + } + } -/*--------------------------------------------------------------------------*/ + if (j < n) { + // The given string is null-terminated. + memset(dst+j, c, n-j); + + } else { + // The given string is not null-terminated. + if (c == '\0') { + // Work backwards, looking for the first non-blank. + for (j = n - 1; j >= 0; j--) { + if (dst[j] != ' ') { + break; + } + } + + j++; + if (j == n && !nt) { + dst[n-1] = '\0'; + } else { + memset(dst+j, '\0', n-j); + } + } + } + } + + if (nt) dst[n] = '\0'; + + return; +} + +//---------------------------------------------------------------------------- void wcsutil_blank_fill(int n, char c[]) { - int k; + if (n <= 0) return; - for (k = strlen(c); k < n; k++) { - c[k] = ' '; + if (c == 0x0) { + return; + } + + // Replace the terminating null and all successive characters. + for (int j = 0; j < n; j++) { + if (c[j] == '\0') { + memset(c+j, ' ', n-j); + break; + } } return; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- void wcsutil_null_fill(int n, char c[]) { - int j, k; - if (n <= 0) return; - /* Null-fill the string. */ - *(c+n-1) = '\0'; + if (c == 0x0) { + return; + } + + // Find the first NULL character. + int j; for (j = 0; j < n; j++) { if (c[j] == '\0') { - for (k = j+1; k < n; k++) { - c[k] = '\0'; - } break; } } - for (k = j-1; k > 0; k--) { - if (c[k] != ' ') break; - c[k] = '\0'; + // Ensure null-termination. + if (j == n) { + j = n - 1; + c[j] = '\0'; } - return; + // Work backwards, looking for the first non-blank. + j--; + for (; j > 0; j--) { + if (c[j] != ' ') { + break; + } + } + + if (++j < n) { + memset(c+j, '\0', n-j); + } + + return; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- -int wcsutil_allEq(int nvec, int nelem, const double *first) +int wcsutil_all_ival(int nelem, int ival, const int iarr[]) { - double v0; - const double *vp; + for (int i = 0; i < nelem; i++) { + if (iarr[i] != ival) return 0; + } + + return 1; +} + +//---------------------------------------------------------------------------- + +int wcsutil_all_dval(int nelem, double dval, const double darr[]) + +{ + for (int i = 0; i < nelem; i++) { + if (darr[i] != dval) return 0; + } + + return 1; +} +//---------------------------------------------------------------------------- + +int wcsutil_all_sval(int nelem, const char *sval, const char (*sarr)[72]) + +{ + for (int i = 0; i < nelem; i++) { + if (strncmp(sarr[i], sval, 72)) return 0; + } + + return 1; +} + +//---------------------------------------------------------------------------- + +int wcsutil_allEq(int nvec, int nelem, const double *first) + +{ if (nvec <= 0 || nelem <= 0) return 0; - v0 = *first; - for (vp = first+nelem; vp < first + nvec*nelem; vp += nelem) { + double v0 = *first; + for (const double *vp = first+nelem; vp < first + nvec*nelem; vp += nelem) { if (*vp != v0) return 0; } return 1; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- -void wcsutil_setAll(int nvec, int nelem, double *first) +int wcsutil_dblEq( + int nelem, + double tol, + const double *darr1, + const double *darr2) + +{ + if (nelem == 0) return 1; + if (nelem < 0) return 0; + + if (darr1 == 0x0 && darr2 == 0x0) return 1; + + if (tol == 0.0) { + // Handled separately for speed of execution. + for (int i = 0; i < nelem; i++) { + double dval1 = (darr1 ? darr1[i] : UNDEFINED); + double dval2 = (darr2 ? darr2[i] : UNDEFINED); + + // Undefined values must match exactly. + if (dval1 == UNDEFINED && dval2 != UNDEFINED) return 0; + if (dval1 != UNDEFINED && dval2 == UNDEFINED) return 0; + + if (dval1 != dval2) return 0; + } + + } else { + for (int i = 0; i < nelem; i++) { + double dval1 = (darr1 ? darr1[i] : UNDEFINED); + double dval2 = (darr2 ? darr2[i] : UNDEFINED); + + // Undefined values must match exactly. + if (dval1 == UNDEFINED && dval2 != UNDEFINED) return 0; + if (dval1 != UNDEFINED && dval2 == UNDEFINED) return 0; + + // Otherwise, compare within the specified tolerance. + if (fabs(dval1 - dval2) > 0.5*tol) return 0; + } + } + + return 1; +} + +//---------------------------------------------------------------------------- + +int wcsutil_intEq(int nelem, const int *iarr1, const int *iarr2) + +{ + if (nelem == 0) return 1; + if (nelem < 0) return 0; + + if (iarr1 == 0x0 && iarr2 == 0x0) return 1; + + for (int i = 0; i < nelem; i++) { + int ival1 = (iarr1 ? iarr1[i] : 0); + int ival2 = (iarr2 ? iarr2[i] : 0); + + if (ival1 != ival2) return 0; + } + + return 1; +} + +//---------------------------------------------------------------------------- + +int wcsutil_strEq(int nelem, char (*sarr1)[72], char (*sarr2)[72]) { - double v0, *vp; + if (nelem == 0) return 1; + if (nelem < 0) return 0; + + if (sarr1 == 0x0 && sarr2 == 0x0) return 1; + + for (int i = 0; i < nelem; i++) { + char *sval1 = (sarr1 ? sarr1[i] : ""); + char *sval2 = (sarr2 ? sarr2[i] : ""); + + if (strncmp(sval1, sval2, 72)) return 0; + } + + return 1; +} + +//---------------------------------------------------------------------------- +void wcsutil_setAll(int nvec, int nelem, double *first) + +{ if (nvec <= 0 || nelem <= 0) return; - v0 = *first; - for (vp = first+nelem; vp < first + nvec*nelem; vp += nelem) { + double v0 = *first; + for (double *vp = first+nelem; vp < first + nvec*nelem; vp += nelem) { *vp = v0; } } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- void wcsutil_setAli(int nvec, int nelem, int *first) { - int v0, *vp; - if (nvec <= 0 || nelem <= 0) return; - v0 = *first; - for (vp = first+nelem; vp < first + nvec*nelem; vp += nelem) { + int v0 = *first; + for (int *vp = first+nelem; vp < first + nvec*nelem; vp += nelem) { *vp = v0; } } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- void wcsutil_setBit(int nelem, const int *sel, int bits, int *array) { - int *arrp; - if (bits == 0 || nelem <= 0) return; if (sel == 0x0) { - /* All elements selected. */ - for (arrp = array; arrp < array + nelem; arrp++) { + // All elements selected. + for (int *arrp = array; arrp < array + nelem; arrp++) { *arrp |= bits; } } else { - /* Some elements selected. */ - for (arrp = array; arrp < array + nelem; arrp++) { + // Some elements selected. + for (int *arrp = array; arrp < array + nelem; arrp++) { if (*(sel++)) *arrp |= bits; } } } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- -char *wcsutil_fptr2str(int (*func)(void), char hext[]) +char *wcsutil_fptr2str(void (*fptr)(void), char hext[19]) { - unsigned char *p = (unsigned char *)(&func); - char *t = hext; - int i, *(ip[2]), j[2], le = 1, gotone = 0; - - /* Test for little-endian addresses. */ + // Test for little-endian addresses. + int *(ip[2]), j[2], le = 1; ip[0] = j; ip[1] = j + 1; + unsigned char *p = (unsigned char *)(&fptr); if ((unsigned char *)ip[0] < (unsigned char *)ip[1]) { - /* Little-endian, reverse it. */ - p += sizeof(func) - 1; + // Little-endian, reverse it. + p += sizeof(fptr) - 1; le = -1; } + char *t = hext; sprintf(t, "0x0"); t += 2; - for (i = 0; i < sizeof(func); i++) { - /* Skip leading zeroes. */ + int gotone = 0; + for (size_t i = 0; i < sizeof(fptr); i++) { + // Skip leading zeroes. if (*p) gotone = 1; if (gotone) { @@ -181,7 +374,69 @@ char *wcsutil_fptr2str(int (*func)(void), char hext[]) return hext; } -/*--------------------------------------------------------------------------*/ +//---------------------------------------------------------------------------- + +static void wcsutil_locale_to_dot(char *buf) + +{ + struct lconv *locale_data = localeconv(); + const char *decimal_point = locale_data->decimal_point; + + if (decimal_point[0] != '.' || decimal_point[1] != 0) { + size_t decimal_point_len = strlen(decimal_point); + char *inbuf = buf; + char *outbuf = buf; + + for ( ; *inbuf; inbuf++) { + if (strncmp(inbuf, decimal_point, decimal_point_len) == 0) { + *outbuf++ = '.'; + inbuf += decimal_point_len - 1; + } else { + *outbuf++ = *inbuf; + } + } + + *outbuf = '\0'; + } +} + + +void wcsutil_double2str(char *buf, const char *format, double value) + +{ + sprintf(buf, format, value); + wcsutil_locale_to_dot(buf); + + // Look for a decimal point or exponent. + char *bp = buf; + while (*bp) { + if (*bp != ' ') { + if (*bp == '.') return; + if (*bp == 'e') return; + if (*bp == 'E') return; + } + bp++; + } + + // Not found, add a fractional part. + bp = buf; + if (*bp == ' ') { + char *cp = buf + 1; + if (*cp == ' ') cp++; + + while (*cp) { + *bp = *cp; + bp++; + cp++; + } + + *bp = '.'; + bp++; + if (bp < cp) *bp = '0'; + } +} + +//---------------------------------------------------------------------------- static const char *wcsutil_dot_to_locale(const char *inbuf, char *outbuf) @@ -193,9 +448,9 @@ static const char *wcsutil_dot_to_locale(const char *inbuf, char *outbuf) char *out = outbuf; size_t decimal_point_len = strlen(decimal_point); - for ( ; *inbuf; ++inbuf) { + for ( ; *inbuf; inbuf++) { if (*inbuf == '.') { - strncpy(out, decimal_point, decimal_point_len); + memcpy(out, decimal_point, decimal_point_len); out += decimal_point_len; } else { *out++ = *inbuf; @@ -211,43 +466,76 @@ static const char *wcsutil_dot_to_locale(const char *inbuf, char *outbuf) } -int wcsutil_str2double(const char *buf, const char *format, double *value) +int wcsutil_str2double(const char *buf, double *value) { char ctmp[72]; return sscanf(wcsutil_dot_to_locale(buf, ctmp), "%lf", value) < 1; } -/*--------------------------------------------------------------------------*/ -static void wcsutil_locale_to_dot(char *buf) +int wcsutil_str2double2(const char *buf, double *value) { - struct lconv *locale_data = localeconv(); - const char *decimal_point = locale_data->decimal_point; + value[0] = 0.0; + value[1] = 0.0; - if (decimal_point[0] != '.' || decimal_point[1] != 0) { - size_t decimal_point_len = strlen(decimal_point); - char *inbuf = buf; - char *outbuf = buf; + // Get the integer part. + char ltmp[72]; + if (sscanf(wcsutil_dot_to_locale(buf, ltmp), "%lf", value) < 1) { + return 1; + } + value[0] = floor(value[0]); - for ( ; *inbuf; ++inbuf) { - if (strncmp(inbuf, decimal_point, decimal_point_len) == 0) { - *outbuf++ = '.'; - inbuf += decimal_point_len - 1; - } else { - *outbuf++ = *inbuf; + char ctmp[72]; + strcpy(ctmp, buf); + + // Look for a decimal point. + char *dptr = strchr(ctmp, '.'); + + // Look for an exponent. + char *eptr; + if ((eptr = strchr(ctmp, 'E')) == NULL) { + if ((eptr = strchr(ctmp, 'D')) == NULL) { + if ((eptr = strchr(ctmp, 'e')) == NULL) { + eptr = strchr(ctmp, 'd'); } } + } + + int exp = 0; + if (eptr) { + // Get the exponent. + if (sscanf(eptr+1, "%d", &exp) < 1) { + return 1; + } - *outbuf = '\0'; + if (!dptr) { + dptr = eptr; + eptr++; + } + + if (dptr+exp <= ctmp) { + // There is only a fractional part. + return sscanf(wcsutil_dot_to_locale(buf, ctmp), "%lf", value+1) < 1; + } else if (eptr <= dptr+exp+1) { + // There is no fractional part. + return 0; + } } -} + // Get the fractional part. + if (dptr) { + char *cptr = ctmp; + while (cptr <= dptr+exp) { + if ('0' < *cptr && *cptr <= '9') *cptr = '0'; + cptr++; + } -void wcsutil_double2str(char *buf, const char *format, double value) + if (sscanf(wcsutil_dot_to_locale(ctmp, ltmp), "%lf", value+1) < 1) { + return 1; + } + } -{ - sprintf(buf, format, value); - wcsutil_locale_to_dot(buf); + return 0; } diff --git a/cextern/wcslib/C/wcsutil.h b/cextern/wcslib/C/wcsutil.h index 858525b88659..c820b8c32111 100644 --- a/cextern/wcslib/C/wcsutil.h +++ b/cextern/wcslib/C/wcsutil.h @@ -1,7 +1,6 @@ /*============================================================================ - - WCSLIB 4.14 - an implementation of the FITS WCS standard. - Copyright (C) 1995-2012, Mark Calabretta + WCSLIB 8.6 - an implementation of the FITS WCS standard. + Copyright (C) 1995-2026, Mark Calabretta This file is part of WCSLIB. @@ -18,36 +17,101 @@ You should have received a copy of the GNU Lesser General Public License along with WCSLIB. If not, see http://www.gnu.org/licenses. - Direct correspondence concerning WCSLIB to mark@calabretta.id.au - Author: Mark Calabretta, Australia Telescope National Facility, CSIRO. - http://www.atnf.csiro.au/people/Mark.Calabretta - $Id: wcsutil.h,v 4.14 2012/07/13 10:02:27 cal103 Exp $ + http://www.atnf.csiro.au/computing/software/wcs + $Id: wcsutil.h,v 8.6 2026/03/29 13:53:56 mcalabre Exp $ *============================================================================= * +* WCSLIB 8.6 - C routines that implement the FITS World Coordinate System +* (WCS) standard. Refer to the README file provided with WCSLIB for an +* overview of the library. +* +* * Summary of the wcsutil routines * ------------------------------- -* Simple utility functions for internal use only by WCSLIB. They are -* documented here solely as an aid to understanding the code. They are not -* intended for external use - the API may change without notice! +* Simple utility functions. With the exception of wcsdealloc(), these +* functions are intended for internal use only by WCSLIB. +* +* The internal-use functions are documented here solely as an aid to +* understanding the code. They are not intended for external use - the API +* may change without notice! +* +* +* wcsdealloc() - free memory allocated by WCSLIB functions +* -------------------------------------------------------- +* wcsdealloc() invokes the free() system routine to free memory. +* Specifically, it is intended to free memory allocated (using calloc()) by +* certain WCSLIB functions (e.g. wcshdo(), wcsfixi(), fitshdr()), which it is +* the user's responsibility to deallocate. +* +* In certain situations, for example multithreading, it may be important that +* this be done within the WCSLIB sharable library's runtime environment. +* +* PLEASE NOTE: wcsdealloc() must not be used in place of the destructors for +* particular structs, such as wcsfree(), celfree(), etc. +* +* Given and returned: +* ptr void* Address of the allocated memory. +* +* Function return value: +* void +* +* +* wcsutil_strcvt() - Copy character string with padding +* ----------------------------------------------------- +* INTERNAL USE ONLY. +* +* wcsutil_strcvt() copies one character string to another up to the specified +* maximum number of characters. +* +* If the given string is null-terminated, then the NULL character copied to +* the returned string, and all characters following it up to the specified +* maximum, are replaced with the specified substitute character, either blank +* or NULL. +* +* If the source string is not null-terminated and the substitute character is +* blank, then copy the maximum number of characters and do nothing further. +* However, if the substitute character is NULL, then the last character and +* all consecutive blank characters preceding it will be replaced with NULLs. +* +* Used by the Fortran wrapper functions in translating C strings into Fortran +* CHARACTER variables and vice versa. +* +* Given: +* n int Maximum number of characters to copy. +* +* c char Substitute character, either NULL or blank (anything +* other than NULL). +* +* nt int If true, then dst is of length n+1, with the last +* character always set to NULL. +* +* src char[] Character string to be copied. If null-terminated, +* then need not be of length n, otherwise it must be. +* +* Returned: +* dst char[] Destination character string, which must be long +* enough to hold n characters. Note that this string +* will not be null-terminated if the substitute +* character is blank. +* +* Function return value: +* void * * * wcsutil_blank_fill() - Fill a character string with blanks * ---------------------------------------------------------- * INTERNAL USE ONLY. * -* wcsutil_blank_fill() pads a character string with blanks starting with the -* terminating NULL character. -* -* Used by the Fortran wrapper functions in translating C character strings -* into Fortran CHARACTER variables. +* wcsutil_blank_fill() pads a character sub-string with blanks starting with +* the terminating NULL character (if any). * * Given: -* n int Length of the character array, c[]. +* n int Length of the sub-string. * * Given and returned: -* c char[] The character string. It will not be null-terminated -* on return. +* c char[] The character sub-string, which will not be +* null-terminated on return. * * Function return value: * void @@ -57,23 +121,91 @@ * -------------------------------------------------------- * INTERNAL USE ONLY. * -* wcsutil_null_fill() strips off trailing blanks and pads the character array -* holding the string with NULL characters. +* wcsutil_null_fill() strips trailing blanks from a string (or sub-string) and +* propagates the terminating NULL character (if any) to the end of the string. +* +* If the string is not null-terminated, then the last character and all +* consecutive blank characters preceding it will be replaced with NULLs. * -* Used mainly to make character strings intelligible in the GNU debugger which -* prints the rubbish following the terminating NULL, obscuring the valid part -* of the string. +* Mainly used in the C library to strip trailing blanks from FITS keyvalues. +* Also used to make character strings intelligible in the GNU debugger, which +* prints the rubbish following the terminating NULL character, thereby +* obscuring the valid part of the string. * * Given: * n int Number of characters. * * Given and returned: -* c char[] The character string. +* c char[] The character (sub-)string. * * Function return value: * void * * +* wcsutil_all_ival() - Test if all elements an int array have a given value +* ------------------------------------------------------------------------- +* INTERNAL USE ONLY. +* +* wcsutil_all_ival() tests whether all elements of an array of type int all +* have the specified value. +* +* Given: +* nelem int The length of the array. +* +* ival int Value to be tested. +* +* iarr const int[] +* Pointer to the first element of the array. +* +* Function return value: +* int Status return value: +* 0: Not all equal. +* 1: All equal. +* +* +* wcsutil_all_dval() - Test if all elements a double array have a given value +* --------------------------------------------------------------------------- +* INTERNAL USE ONLY. +* +* wcsutil_all_dval() tests whether all elements of an array of type double all +* have the specified value. +* +* Given: +* nelem int The length of the array. +* +* dval int Value to be tested. +* +* darr const double[] +* Pointer to the first element of the array. +* +* Function return value: +* int Status return value: +* 0: Not all equal. +* 1: All equal. +* +* +* wcsutil_all_sval() - Test if all elements a string array have a given value +* --------------------------------------------------------------------------- +* INTERNAL USE ONLY. +* +* wcsutil_all_sval() tests whether the elements of an array of type +* char (*)[72] all have the specified value. +* +* Given: +* nelem int The length of the array. +* +* sval const char * +* String to be tested. +* +* sarr const char (*)[72] +* Pointer to the first element of the array. +* +* Function return value: +* int Status return value: +* 0: Not all equal. +* 1: All equal. +* +* * wcsutil_allEq() - Test for equality of a particular vector element * ------------------------------------------------------------------ * INTERNAL USE ONLY. @@ -105,11 +237,80 @@ * 1: All equal. * * +* wcsutil_dblEq() - Test for equality of two arrays of type double +* ---------------------------------------------------------------- +* INTERNAL USE ONLY. +* +* wcsutil_dblEq() tests for equality of two double-precision arrays. +* +* Given: +* nelem int The number of elements in each array. +* +* tol double Tolerance for comparison of the floating-point values. +* For example, for tol == 1e-6, all floating-point +* values in the arrays must be equal to the first 6 +* decimal places. A value of 0 implies exact equality. +* +* arr1 const double* +* The first array. +* +* arr2 const double* +* The second array +* +* Function return value: +* int Status return value: +* 0: Not equal. +* 1: Equal. +* +* +* wcsutil_intEq() - Test for equality of two arrays of type int +* ------------------------------------------------------------- +* INTERNAL USE ONLY. +* +* wcsutil_intEq() tests for equality of two int arrays. +* +* Given: +* nelem int The number of elements in each array. +* +* arr1 const int* +* The first array. +* +* arr2 const int* +* The second array +* +* Function return value: +* int Status return value: +* 0: Not equal. +* 1: Equal. +* +* +* wcsutil_strEq() - Test for equality of two string arrays +* -------------------------------------------------------- +* INTERNAL USE ONLY. +* +* wcsutil_strEq() tests for equality of two string arrays. +* +* Given: +* nelem int The number of elements in each array. +* +* arr1 const char** +* The first array. +* +* arr2 const char** +* The second array +* +* Function return value: +* int Status return value: +* 0: Not equal. +* 1: Equal. +* +* * wcsutil_setAll() - Set a particular vector element * -------------------------------------------------- * INTERNAL USE ONLY. * -* wcsutil_setAll() sets the value of a particular element in a set of vectors. +* wcsutil_setAll() sets the value of a particular element in a set of vectors +* of type double. * * Given: * nvec int The number of vectors. @@ -137,7 +338,8 @@ * -------------------------------------------------- * INTERNAL USE ONLY. * -* wcsutil_setAli() sets the value of a particular element in a set of vectors. +* wcsutil_setAli() sets the value of a particular element in a set of vectors +* of type int. * * Given: * nvec int The number of vectors. @@ -190,16 +392,15 @@ * * wcsutil_fptr2str() translates a pointer-to-function to hexadecimal string * representation for output. It is used by the various routines that print -* the contents of WCSLIB structs. Note that it is not strictly legal to -* type-pun a function pointer to void*. -* -* See stackoverflow.com/questions/2741683/how-to-format-a-function-pointer +* the contents of WCSLIB structs, noting that it is not strictly legal to +* type-pun a function pointer to void*. See +* http://stackoverflow.com/questions/2741683/how-to-format-a-function-pointer * * Given: -* fptr int (*)() Pointer to function. +* fptr void(*)() Pointer to function. * * Returned: -* hext char[] Null-terminated string. Should be at least 19 bytes +* hext char[19] Null-terminated string. Should be at least 19 bytes * in size to accomodate a 64-bit address (16 bytes in * hex), plus the leading "0x" and trailing '\0'. * @@ -211,17 +412,22 @@ * --------------------------------------------------------------------- * INTERNAL USE ONLY. * -* wcsutil_double2str() converts a double to a string, but unlike sprintf it -* ignores the locale and always uses a '.' as the decimal separator. +* wcsutil_double2str() converts a double to a string, but unlike sprintf() it +* ignores the locale and always uses a '.' as the decimal separator. Also, +* unless it includes an exponent, the formatted value will always have a +* fractional part, ".0" being appended if necessary. * * Returned: * buf char * The buffer to write the string into. * * Given: * format char * The formatting directive, such as "%f". This -* may be any of the forms accepted by sprintf, but +* may be any of the forms accepted by sprintf(), but * should only include a formatting directive and -* nothing else. +* nothing else. For "%g" and "%G" formats, unless it +* includes an exponent, the formatted value will always +* have a fractional part, ".0" being appended if +* necessary. * * value double The value to convert to a string. * @@ -230,33 +436,66 @@ * ------------------------------------------------------------------------ * INTERNAL USE ONLY. * -* wcsutil_str2double() converts a string to a double, but unlike sscanf it +* wcsutil_str2double() converts a string to a double, but unlike sscanf() it * ignores the locale and always expects a '.' as the decimal separator. * * Given: * buf char * The string containing the value * -* format char * The formatting directive, such as "%lf". This -* may be any of the forms accepted by sscanf, but -* should only include a single formatting directive. -* * Returned: * value double * The double value parsed from the string. * +* +* wcsutil_str2double2() - Translate string to doubles, ignoring the locale +* ------------------------------------------------------------------------ +* INTERNAL USE ONLY. +* +* wcsutil_str2double2() converts a string to a pair of doubles containing the +* integer and fractional parts. Unlike sscanf() it ignores the locale and +* always expects a '.' as the decimal separator. +* +* Given: +* buf char * The string containing the value +* +* Returned: +* value double[2] The double value, split into integer and fractional +* parts, parsed from the string. +* *===========================================================================*/ #ifndef WCSLIB_WCSUTIL #define WCSLIB_WCSUTIL +#ifdef __cplusplus +extern "C" { +#endif + +void wcsdealloc(void *ptr); + +void wcsutil_strcvt(int n, char c, int nt, const char src[], char dst[]); + void wcsutil_blank_fill(int n, char c[]); void wcsutil_null_fill (int n, char c[]); +int wcsutil_all_ival(int nelem, int ival, const int iarr[]); +int wcsutil_all_dval(int nelem, double dval, const double darr[]); +int wcsutil_all_sval(int nelem, const char *sval, const char (*sarr)[72]); int wcsutil_allEq (int nvec, int nelem, const double *first); + +int wcsutil_dblEq(int nelem, double tol, const double *arr1, + const double *arr2); +int wcsutil_intEq(int nelem, const int *arr1, const int *arr2); +int wcsutil_strEq(int nelem, char (*arr1)[72], char (*arr2)[72]); void wcsutil_setAll(int nvec, int nelem, double *first); void wcsutil_setAli(int nvec, int nelem, int *first); void wcsutil_setBit(int nelem, const int *sel, int bits, int *array); -char *wcsutil_fptr2str(int (*func)(void), char hext[]); -int wcsutil_str2double(const char *buf, const char *format, double *value); +char *wcsutil_fptr2str(void (*fptr)(void), char hext[19]); void wcsutil_double2str(char *buf, const char *format, double value); +int wcsutil_str2double(const char *buf, double *value); +int wcsutil_str2double2(const char *buf, double *value); + +#ifdef __cplusplus +} +#endif -#endif /* WCSLIB_WCSUTIL */ +#endif // WCSLIB_WCSUTIL diff --git a/cextern/wcslib/C/wcsutrn.l b/cextern/wcslib/C/wcsutrn.l index bb35a668cbde..e18b4f514a58 100644 --- a/cextern/wcslib/C/wcsutrn.l +++ b/cextern/wcslib/C/wcsutrn.l @@ -1,7 +1,6 @@ /*============================================================================ - - WCSLIB 4.14 - an implementation of the FITS WCS standard. - Copyright (C) 1995-2012, Mark Calabretta + WCSLIB 8.6 - an implementation of the FITS WCS standard. + Copyright (C) 1995-2026, Mark Calabretta This file is part of WCSLIB. @@ -18,11 +17,9 @@ You should have received a copy of the GNU Lesser General Public License along with WCSLIB. If not, see http://www.gnu.org/licenses. - Direct correspondence concerning WCSLIB to mark@calabretta.id.au - Author: Mark Calabretta, Australia Telescope National Facility, CSIRO. - http://www.atnf.csiro.au/people/Mark.Calabretta - $Id: wcsutrn.l,v 4.14 2012/07/13 10:02:27 cal103 Exp $ + http://www.atnf.csiro.au/computing/software/wcs + $Id: wcsutrn.l,v 8.6 2026/03/29 13:53:56 mcalabre Exp $ *============================================================================= * * wcsutrn.l is a Flex description file containing the definition of a lexical @@ -38,18 +35,17 @@ /* Options. */ %option full %option never-interactive +%option noinput %option noyywrap %option outfile="wcsutrn.c" %option prefix="wcsutrn" +%option reentrant +%option extra-type="struct wcsutrn_extra *" /* Exclusive start states. */ %x NEXT FLUSH %{ -/* To get the prototype for fileno() from stdio.h when gcc is invoked with - * -std=c89 (same as -ansi) or -std=c99 since we do not define YY_INPUT. */ -#define _POSIX_SOURCE 1 - #include #include #include @@ -58,34 +54,47 @@ #include "wcserr.h" #include "wcsunits.h" -#define YY_DECL int wcsutrne(int ctrl, char unitstr[], struct wcserr **err) +// User data associated with yyscanner. +struct wcsutrn_extra { + // Used in preempting the call to exit() by yy_fatal_error(). + jmp_buf abort_jmp_env; +}; + +#define YY_DECL int wcsutrne_scanner(int ctrl, char unitstr[], \ + struct wcserr **err, yyscan_t yyscanner) + +// Dummy definition to circumvent compiler warnings. +#define YY_INPUT(inbuff, count, bufsize) { count = YY_NULL; } -/* Used in preempting the call to exit() by yy_fatal_error(). */ -jmp_buf wcsutrn_abort_jmp_env; -#define exit(status) longjmp(wcsutrn_abort_jmp_env, status) +// Preempt the call to exit() by yy_fatal_error(). +#define exit(status) longjmp(yyextra->abort_jmp_env, status); + +// Internal helper functions. +static YY_DECL; %} %% - static const char *function = "wcsutrne"; - + static const char *function = "wcsutrne_scanner"; + + if (err) *err = 0x0; + char orig[80], subs[80]; + *orig = '\0'; + *subs = '\0'; + int bracket = 0; int unsafe = 0; int status = -1; - YY_BUFFER_STATE inbuff; - int yylex_destroy(void); - - *orig = '\0'; - *subs = '\0'; - inbuff = yy_scan_string(unitstr); + yy_delete_buffer(YY_CURRENT_BUFFER, yyscanner); + yy_scan_string(unitstr, yyscanner); *unitstr = '\0'; - /* Return here via longjmp() invoked by yy_fatal_error(). */ - if (setjmp(wcsutrn_abort_jmp_env)) { + // Return here via longjmp() invoked by yy_fatal_error(). + if (setjmp(yyextra->abort_jmp_env)) { return wcserr_set(WCSERR_SET(UNITSERR_PARSER_ERROR), - "Internal units translator error parsing '%s'", unitstr); + "Internal units translator error"); } BEGIN(INITIAL); @@ -95,22 +104,22 @@ jmp_buf wcsutrn_abort_jmp_env; #endif ^" "*"[" { - /* Looks like a keycomment. */ + // Looks like a keycomment. strcat(unitstr, "["); bracket = 1; } -" "+ /* Discard leading whitespace. */ +" "+ // Discard leading whitespace. [^A-Za-z] { - /* Non-alphabetic character. */ + // Non-alphabetic character. strcat(unitstr, yytext); if (bracket && *yytext == ']') { BEGIN(FLUSH); } } -angstrom { +Angstroms|angstroms? { strcpy(orig, yytext); strcpy(subs, "Angstrom"); BEGIN(NEXT); @@ -153,7 +162,7 @@ D { BEGIN(NEXT); } -degrees?|DEG|DEGREES? { +degrees?|Deg|Degrees?|DEG|DEGREES? { strcpy(orig, yytext); strcpy(subs, "deg"); BEGIN(NEXT); @@ -276,25 +285,25 @@ years?|YR|YEARS? { } [A-Za-z]+ { - /* Not a recognized alias. */ + // Not a recognized alias. strcpy(orig, yytext); strcpy(subs, orig); BEGIN(NEXT); } [A-Za-z]+ { - /* Reject the alias match. */ + // Reject the alias match. strcat(orig, yytext); strcpy(subs, orig); } " "+[^A-Za-z] { - /* Discard separating whitespace. */ + // Discard separating whitespace. unput(yytext[yyleng-1]); } " "+[A-Za-z] { - /* Compress separating whitespace. */ + // Compress separating whitespace. strcat(unitstr, subs); strcat(unitstr, " "); if (strcmp(orig, subs)) status = 0; @@ -304,7 +313,7 @@ years?|YR|YEARS? { } . { - /* Copy anything else unchanged. */ + // Copy anything else unchanged. strcat(unitstr, subs); if (strcmp(orig, subs)) status = 0; unput(*yytext); @@ -313,18 +322,17 @@ years?|YR|YEARS? { } .* { - /* Copy out remaining input. */ + // Copy out remaining input. strcat(unitstr, yytext); } <> { - /* End-of-string. */ + // End-of-string. if (*subs) { strcat(unitstr, subs); if (strcmp(orig, subs)) status = 0; } - yylex_destroy(); if (unsafe) { return wcserr_set(WCSERR_SET(UNITSERR_UNSAFE_TRANS), "Unsafe unit translation in '%s'", unitstr); @@ -333,3 +341,26 @@ years?|YR|YEARS? { } %% + +/*---------------------------------------------------------------------------- +* External interface to the scanner. +*---------------------------------------------------------------------------*/ + +int wcsutrne( + int ctrl, + char unitstr[], + struct wcserr **err) + +{ + // Function prototypes. + int yylex_init_extra(YY_EXTRA_TYPE extra, yyscan_t *yyscanner); + int yylex_destroy(yyscan_t yyscanner); + + struct wcsutrn_extra extra; + yyscan_t yyscanner; + yylex_init_extra(&extra, &yyscanner); + int status = wcsutrne_scanner(ctrl, unitstr, err, yyscanner); + yylex_destroy(yyscanner); + + return status; +} diff --git a/cextern/wcslib/C/wtbarr.h b/cextern/wcslib/C/wtbarr.h new file mode 100644 index 000000000000..b556c12ef680 --- /dev/null +++ b/cextern/wcslib/C/wtbarr.h @@ -0,0 +1,120 @@ +/*============================================================================ + WCSLIB 8.6 - an implementation of the FITS WCS standard. + Copyright (C) 1995-2026, Mark Calabretta + + This file is part of WCSLIB. + + WCSLIB is free software: you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + WCSLIB is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for + more details. + + You should have received a copy of the GNU Lesser General Public License + along with WCSLIB. If not, see http://www.gnu.org/licenses. + + Author: Mark Calabretta, Australia Telescope National Facility, CSIRO. + http://www.atnf.csiro.au/computing/software/wcs + $Id: wtbarr.h,v 8.6 2026/03/29 13:53:56 mcalabre Exp $ +*============================================================================= +* +* WCSLIB 8.6 - C routines that implement the FITS World Coordinate System +* (WCS) standard. Refer to the README file provided with WCSLIB for an +* overview of the library. +* +* +* Summary of the wtbarr struct +* ---------------------------- +* The wtbarr struct is used by wcstab() in extracting coordinate lookup tables +* from a binary table extension (BINTABLE) and copying them into the tabprm +* structs stored in wcsprm. +* +* +* wtbarr struct - Extraction of coordinate lookup tables from BINTABLE +* -------------------------------------------------------------------- +* Function wcstab(), which is invoked automatically by wcspih(), sets up an +* array of wtbarr structs to assist in extracting coordinate lookup tables +* from a binary table extension (BINTABLE) and copying them into the tabprm +* structs stored in wcsprm. Refer to the usage notes for wcspih() and +* wcstab() in wcshdr.h, and also the prologue to tab.h. +* +* For C++ usage, because of a name space conflict with the wtbarr typedef +* defined in CFITSIO header fitsio.h, the wtbarr struct is renamed to wtbarr_s +* by preprocessor macro substitution with scope limited to wtbarr.h itself, +* and similarly in wcs.h. +* +* int i +* (Given) Image axis number. +* +* int m +* (Given) wcstab array axis number for index vectors. +* +* int kind +* (Given) Character identifying the wcstab array type: +* - c: coordinate array, +* - i: index vector. +* +* char extnam[72] +* (Given) EXTNAME identifying the binary table extension. +* +* int extver +* (Given) EXTVER identifying the binary table extension. +* +* int extlev +* (Given) EXTLEV identifying the binary table extension. +* +* char ttype[72] +* (Given) TTYPEn identifying the column of the binary table that contains +* the wcstab array. +* +* long row +* (Given) Table row number. +* +* int ndim +* (Given) Expected dimensionality of the wcstab array. +* +* int *dimlen +* (Given) Address of the first element of an array of int of length ndim +* into which the wcstab array axis lengths are to be written. +* +* double **arrayp +* (Given) Pointer to an array of double which is to be allocated by the +* user and into which the wcstab array is to be written. +* +*===========================================================================*/ + +#ifndef WCSLIB_WTBARR +#define WCSLIB_WTBARR + +#ifdef __cplusplus +extern "C" { +#define wtbarr wtbarr_s // See prologue above. +#endif + // For extracting wcstab arrays. Matches + // the wtbarr typedef defined in CFITSIO + // header fitsio.h. +struct wtbarr { + int i; // Image axis number. + int m; // Array axis number for index vectors. + int kind; // wcstab array type. + char extnam[72]; // EXTNAME of binary table extension. + int extver; // EXTVER of binary table extension. + int extlev; // EXTLEV of binary table extension. + char ttype[72]; // TTYPEn of column containing the array. + long row; // Table row number. + int ndim; // Expected wcstab array dimensionality. + int *dimlen; // Where to write the array axis lengths. + double **arrayp; // Where to write the address of the array + // allocated to store the wcstab array. +}; + +#ifdef __cplusplus +#undef wtbarr +} +#endif + +#endif // WCSLIB_WTBARR diff --git a/cextern/wcslib/CHANGES b/cextern/wcslib/CHANGES index 9953cbc21771..94a27617e041 100644 --- a/cextern/wcslib/CHANGES +++ b/cextern/wcslib/CHANGES @@ -1,3 +1,1701 @@ +WCSLIB version 8.6 (2026/03/30) +------------------------------- + +* C library + + - In linx2p(), replaced two uses of goto statements to work around an + apparent compiler bug. Reported by Cyril Richard. + +* PGSBOX + + - PGSBOX now accounts for the possibility that the coordinate system + is not defined outside the frame. Previously this was important for + determining axis crossings and hence axis labelling. However, it is + not valid for -TAB coordinates in FITS for which extrapolation is + strictly limited to half a pixel. + +* User manual + + - Documentation generation moved to Doxygen 1.16.0 (was 1.15.0). + While the Doxygen bug reported in version 8.5 has now been fixed, + the makefile patch has been left in place for the time being. + + +WCSLIB version 8.5 (2025/12/06) +------------------------------- + +WCSLIB's RCS version files are now included in the distribution thereby +providing a documented history of all changes made since v1.0. They +will be found in RCS subdirectories in each code directory. A small +number of discontinued files are preserved with their RCS version files +having a ',x' suffix instead of ',v'. + +* C library + + - In linp2x(), quarantine NaN elements of the pixel coordinate vector + from infecting non-NaN elements in the case where the PCij matrix is + diagonal (noting that zero * NaN = NaN). Likewise in linx2p(). + Reported by Thomas Robitaille with patch. + + - Quelled nuisance compiler warnings in wcspih() and wcsbth(). + +* User manual + + - Documentation generation moved to Doxygen 1.15.0 (was 1.12.0). + The makefile patches doxygen.sty as per my Doxygen bug report 11881. + + +WCSLIB version 8.4 (2024/10/29) +------------------------------- + +* C library + + - Fixed some problems in wcs_chksum() and changed wcs_fletcher32() to + conform to the standard computation. + + - New test program, twcs_pthread. + +* User manual + + - Documentation generation moved to doxygen 1.12.0 (was 1.10.0). + + +WCSLIB version 8.3 (2024/05/14) +------------------------------- + +* C library + + - Until now, wcsset() always operated unconditionally - the wcsprm + struct was set or reset regardless of its current state. Likewise + the various *set() functions for the other structs. + + However, in some situations, particularly in threaded applications, + it is desirable to have wcsset(), etc., check the state of the + struct and return immediately if it has already been set. This may + now be accomplished by setting wcsprm::flag == 1 (instead of 0) + before calling wcsset(). Likewise for the other structs. This sets + a "bypass" flag within the struct itself. + + - A new function, wcsenq(), queries the state of the wcsprm struct, + specifically whether WCSLIB is managing its memory, whether the + struct has been set, whether or not it is in bypass mode, and + whether it is self-consistent. There are corresponding functions + for the other structs: celenq(), disenq(), linenq(), prjenq(), + spcenq(), and tabenq(). Please refer to the WCSLIB manual. + + - In the C test suite, modified twcs to test wcsenq() and also + wcsset() in bypass mode. + + - Quelled nuisance compiler warnings in wcsbth(), wcspih(), wcsp2s(), + and wcshdo(). + +* Fortran wrappers + + - Interprocedural Link Time Optimization (LTO), when used with the + strict compiler options required by some Linux distributions, may + place more stringent requirements on mixing code written in + different languages. Specifically, as far as WCSLIB is concerned, + this applies for Fortran calling C (or vice versa) where the + function parameter list includes a character variable. + + It is important to note that the existing Fortran wrappers work as + they did before, with or without LTO, that issues only arise when + strict LTO compiler options are enabled, and that wrappers without + a character argument are unaffected. + + Fortran 2003 introduced the "language-binding-spec" attribute using + the keyword BIND. The INTERFACE block for a procedure may be given + the BIND(C) attribute to specify the interface of an external, + interoperable C function. Use of this BIND(C) attribute is now + virtually mandated by LTO (with said strict compiler options). + + The WCSLIB Fortran wrappers are written in C, e.g. wcs_f.c, with a + Fortran-compliant interface, and these C functions are intended to + be called directly from Fortran applications. Three options were + considered for achieving strict LTO compliance: + + 1) Require that all existing Fortran applications be modified to + conform to the Fortran 2003 language binding specification via + the addition of INTERFACE blocks bearing the BIND(C) attribute + for the existing wrappers. + + This option is clearly untenable. + + 2) Rewrite the WCSLIB Fortran wrappers completely in Fortran 2003. + + It seems that this option may be tenable as the BIND(C) spec + allows for C-equivalent derived types. However, it would + require rewriting all of the wrappers, 5 kloc of C code, in + Fortran 2003 code that must directly manipulate the internals + of all of the WCSLIB structs. + + 3) Introduce a new layer of thin wrappers, written in Fortran + 2003, that do nothing more than define the INTERFACE to the + existing wrappers written in C and then call them. + Essentially this extracts the changes required in option (1) + into a new set of wrappers. + + This was the option chosen on the basis of simplicity - only + specific wrappers, namely those with character arguments, need + be rewrapped. It also minimises WCSLIB's exposure to Fortran + 2003, particularly for the sake of legacy astronomical packages + such as Miriad and AIPS. Further, it admits the possibility of + the optional use of this extra layer of wrappers. + + The new Fortran 2003 wrappers reside in the Fortran subdirectory in + files by the name of *_bindc.f90. By default they are not compiled + and not used. To use them, WCSLIB must be configure'd with the new + '--with-bindc' option. This causes the new BIND(C) wrappers to be + compiled, and the names of the old wrappers to be changed, for + example from wcspih_() to wcspih_c(). + + LTO compile problems reported by Eli Schwartz, Gentoo maintainer. + + - Added wrappers for celenq(), disenq(), linenq(), prjenq(), spcenq(), + tabenq(), and wcsenq(). + + - In the Fortran test suite, modified twcs to test WCSENQ and also + WCSSET in bypass mode. + +* PGSBOX + + - Changes mirroring those described above for the Fortran wrappers, + the difference being that here we have a mix of C calling Fortran + (e.g. cpgsbox() calling PGSBOX) as well as Fortran calling C (e.g. + PGWCSL calling pgwcsl_c()). As with the Fortran wrappers, the new + configure option, '--with-bindc', chooses whether to use the new + BIND(C) PGSBOX wrappers. + +* Installation + + - Added '--with-bindc' as a new configure option (or BINDC=yes from + the environment) to signal the use of the new strictly LTO-compliant + Fortran wrappers. See above. + + - Modified 'configure' to report the version of gcc used. Likewise, + for the 'show' rule in makedefs. + +* User manual + + - Quelled a nuisance compiler warning in doxextr. + + - Documentation generation moved to doxygen 1.10.0 (was 1.9.8). + + +WCSLIB version 8.2.2 (2023/11/29) +--------------------------------- + +* C library (installation) + + - In prj.c, a number of variables with global scope that are only used + internally were declared static to avoid namespace conflicts arising + in Link Time Optimization (LTO) builds of the Rwcs wrappers. This + is a patch release as it does not affect the library itself other + than in localising some symbols that were never meant to be global. + Reported by Rodrigo Carrizo with patch. + + Likewise for an internally used helper function, prjoff(). + Likewise for a handful of variables in cel.c, dis.c, lin.c, tab.c, + wcs.c, wcsfix.c, and wcshdr.c. + + +WCSLIB version 8.2.1 (2023/11/17) +--------------------------------- + +* Installation + + - With searching enabled in the HTML manual, doxygen creates a new + subdirectory, html/search, which must be installed explicitly. + + +WCSLIB version 8.2 (2023/11/16) +------------------------------- + +* C library + + - In wcshdo(), fixed character buffer overflows in the comment string + for the longitude and latitude axes triggered by some projections, + and also the formatting for generic coordinate systems. Reported by + Shu Niu. + +* User manual + + - Documentation generation moved to doxygen 1.9.8 (was 1.9.7). + + - Enabled searching in the HTML manual. + + +WCSLIB version 8.1 (2023/07/06) +------------------------------- + +Changes in the contents of the auxprm struct (in 8.0 beta) necessitated +incrementing the major version number from 7 to 8, i.e. the ABI changed. +Took the opportunity for minor tweaks to wcsprm and disprm. + +* C library + + - In disprm, changed the order of maxdis and totdis to ensure correct + alignment of doubles on 32-bit machines. + +* Fortran wrappers + + - Match changes to disprm in the C library. + + - In the Fortran test suite, inserted a brief pause between plots in + tprj2, tspc, and tpih2, which have long been a blur. + +* PGSBOX + + - Quell innocuous compiler warnings from gfortran 12.1.0. Inserted a + brief pause between plots in pgtest and cpgtest. + + +WCSLIB version 8.0 beta (2023/07/01) +------------------------------------ + +Beta test version, not publically released. + +* C library + + - Support planetary keywords A_RADIUS, B_RADIUS, C_RADIUS, BLON_OBS, + BLAT_OBS, and BDIS_OBS in auxprm by analogy with the Solar keywords + added at 7.1. Requested by Chiara Marmo (Observatoire de Paris). + + - Added wcsprm::time to the wcsprm struct to record the TIME axis + number (along with lng, lat, and spec). + + - Fixed a bug in wcspih() and wcsbth() where trailing blanks were not + stripped from string keyvalues. Reported by Naveen Dukiya. + + Changed test program tdis3 to test this. + + - Fixed a problem affecting thread safety in disp2x(), disx2p(), and + diswarp(). Reported by Mohammad Akhlaghi. + +* Fortran wrappers + + - Match changes to the C library supporting planetary keywords in + auxprm and the addition of wcsprm::time. + +* User manual + + - Documentation generation moved to doxygen 1.9.7 (was 1.9.5). + + +WCSLIB version 7.12 (2022/09/09) +-------------------------------- + +* C library + + - In wcsp2s() and wcss2p(), fixed handling of status returns from + linp2x() and linx2p() relating to distortion functions, specifically + with respect to setting the stat[] vector. Reported by Sepideh + Eskandarlou (via Mohammad Akhlaghi). + + - When extracting by axis type, wcssub() did not account for the + possibility of time axes with -TAB or -LOG algorithm codes. + Reported by Mihai Cara. + +* Utilities + + - Fixed a bit of confusion in wcsgrid relating to cfitsio file opening + syntax, e.g. file.fits+1. It now matches wcsware in that regard. + + - Portability fix in sundazel - need to define _POSIX_C_SOURCE as + 199506L in order to get the declaration of localtime_r(). Reported + by Marc Espie. + +* User manual + + - Documentation generation moved to doxygen 1.9.5 (was 1.9.3). + + +WCSLIB version 7.11 (2022/04/26) +-------------------------------- + +* C library + + - In tabini(), the default index array should contain a 1-relative + sequence: {1, 2,... N}. Previously it was 0-relative. + + +WCSLIB version 7.10 (2022/04/24) +-------------------------------- + +* C library + + - In tabcpy(), collapse degenerate table index arrays if they are + collapsed in the source struct. Minor fix for output formatting + in tabprt(). + + - In the C test suite, added calls to wcstrim() in twcsfix to tidy + the structs before printing. + +* Utilities + + - The default option in wcsware now trims the wcsprm struct before + printing it. Use the '-p' option to print the untrimmed struct. + + +WCSLIB version 7.9 (2022/03/26) +------------------------------- + +* C library + + - In wcsset(), bug fix for identifying time coordinate axes. + Reported by Mihai Cara with patch. + + +WCSLIB version 7.8 (2022/03/25) +------------------------------- + +* C library + + - In wcssub(), bug fix for tabular coordinates that change axis + number, thus requiring tabprm::map to be updated. Reported by + Mihai Cara with patch. + + Also in wcssub(): + - check that all axes of a multi-dimensional table are extracted + together, + - fixed potential memory leaks for tabular coordinate axes when + an attempt to subimage non-separable axes fails, + - the error messages for non-separable coordinate systems are + generally more informative, + - subimage extraction by coordinate type now recognises time + coordinate axis types via WCSSUB_TIME. + + - wcsset() now identifies time coordinate axes in wcsprm::types. + + - Extended test program twcssub to test the above modifications. + +* Installation + + - Bug fix in the utils makefile for creating $(MANDIR)/man1. + Reported by Aleksander Kurek. + + - Bug fix in the Fortran makefile for compiling and using 'tofits'. + Reported by Stefan Brüns. + +* User manual + + - Documentation generation moved to doxygen 1.9.3 (was 1.9.1). + + +WCSLIB version 7.7 (2021/07/12) +------------------------------- + +* C library + + - In disfree() and disset(), removed potential for double invokation + of free() on allocated memory. Reported by Cyril Richard. + + - In wcsutil_fptr2str(), fixed a bug reported by Ralf Palsa (with fix) + that caused it to be overly enthusiastic in stripping leading zeroes + off addresses of function pointers. + + - In wcspcx(), replaced variable length arrays with allocated memory + (portability issue). Reported by Mihai Cara with patch. + + - Fixed buglets in wcsbth() and linsize() uncovered by gcc 11.1.0. + + - Quelled inconsequential compiler warnings from gcc 11.1.0 concerning + wcsmix() (function prototype in wcs.h), wcstrim(), wcseulexe(), + wcsulex(), and wcsutrne(). + + - Defined _POSIX_C_SOURCE appropriately in tprj2.c and tspc.c to get + the function prototype etc. for nanosleep() from time.h. + +* Fortran wrappers + + - Quelled numerous inconsequential compiler warnings from gfortran + 11.1.0. + + - Fixed minor bugs uncovered by gfortran 11.1.0 in test programs tdis2 + and ttab3. + +* Utilities + + - Moved 'tofits' from ./C/test/ into the utilities directory as it's + generally useful, and added usage (converted to man page). + + Also made it a bit smarter in dealing with ISO/IEC 8859 and UTF-8 + encoded byte streams, in particular translating non-breaking spaces + into ordinary spaces. + +* Installation + + - Two patches for configure.ac provided by Mosč Giordano: + 1. makes it possible to build the Windows library using the MinGW + cross-compiler, + 2. makes the soname of the MacOSX library consistent with other + Unix systems. + + +WCSLIB version 7.6 (2021/04/13) +------------------------------- + +* C library + + - Bug fix in tabs2x() triggered for 1-dimensional coordinate lookup + tables on axes > 1. Reported by Mihai Cara. + + - In datfix(), don't return status 0 if no change was made (fix for + change made at release 7.4). Reported by Derek Homeier. + + - New function wcspcx() in the wcsfix suite regularizes the linear + transformation component of a coordinate description to make it more + human-readable. It decomposes CDi_ja into PCi_ja and CDELTia in + such a way that CDELTia forms meaningful scaling parameters, often + leaving an orthogonal or near-orthogonal matrix. Optionally, it can + then permute rows of this matrix to unscramble axis permutations. + + A test header may be generated from wcspcx.keyrec for input to + wcsware (not exercised as part of the standard test suite). + + - New function wcstrim() frees memory allocated by wcsinit() for + arrays in a wcsprm struct that remain unused after the struct has + been set up. + + - New functions wcssize(), auxsize(), tabsize(), linsize(), dissize(), + celsize(), prjsize(), spcsize(), and wcserr_size() compute the total + size of the relevant structs, including allocated memory. + + - In the C test suite, inserted a brief pause in tprj2 and tspc, which + otherwise have now become a blur. + +* Fortran wrappers + + - Added wrappers for wcspcx(), wcstrim(), wcssize(), auxsize(), + tabsize(), linsize(), dissize(), celsize(), prjsize(), spcsize(), + and wcserr_size(). + +* Utilities + + - Added -c, -cp, -C, and -Cp options to wcsware to apply wcspcx() + in a variety of ways, -m to apply wcstrim(), and -z to report the + total size of the wcsprm struct with a breakdown of the sizes of + its constituent structs. + + - Fixed compiler warnings for sundazel (portability issue). + +* Installation + + - Upped the required version of Flex to 2.6.0 (was 2.5.9). Problems + with Flex 2.5.39 reported by Derek Homeier. + + Also added '--disable-flex' as a new configure option to force the + use of the pre-generated Flex sources. + + +WCSLIB version 7.5 (2021/03/20) +------------------------------- + +The C code in WCSLIB is moving piecemeal to the C99 standard. In fact, +various indispensible C99 constructs have been used in WCSLIB for many +years: the long long int data type (in fitshdr() only); stdint.h, +inttypes.h, and the use of PRI formatting control (in wcsprintf(), which +is widely used by the library); and the C99-extended library function +vsnprintf() (used by wcserr for a decade). Flex-generated C code also +uses C99 extensions, though with workarounds if they are not available. + +Except in the header prologues, which are formatted in a special way for +generating the user manual, comments were changed en masse to C99 style +in release 7.3.1, and variable declarations in code that I have occasion +to modify will transition to the more general placement allowed by C99. +However, I have no plans to use any of the more esoteric features of +C99. + +* C library + + - New function, wcsccs(), changes the celestial coordinate system of a + wcsprm struct, for example, from equatorial to galactic coordinates. + The parameters that define the spherical coordinate transformation + must be provided. This allows WCSLIB to provide this functionality + without needing to know anything about specific celestial coordinate + systems, and has the advantage of making the routine completely + general. Requested by Mohammad Akhlaghi. + + Modified test program twcsfix also to test wcsccs(). + + - Fixed a problem common to all of the Flex code (fitshdr, wcsbth, + wcspih, wcsulex, and wcsutrn) that made it thread-unsafe. Reported + by Cyril Richard. + + Added a new test program, tpthreads, to test thread safety. It is + only used for code development, and not exercised as part of the + standard test suite. + + - In fitshdr(), fixed a problem that potentially could arise on + systems where sizeof(long long int) is greater than 8 (64 bits). + +* Fortran wrappers + + - Match changes to the C library: added a wrapper for wcsccs(), and + modified twcsfix.f. + + - Because null addresses cannot be passed to functions in Fortran, + wcssub_() now interprets *nsub == -1 && *axes == -1 as a signal to + do a deep copy of one wcsprm struct to another. + +* Utilities + + - New utility, sundazel, computes the local time of the Sun's passage + through the specified apparent longitude or latitude in a user- + defined coordinate system. It can also perform several other Solar + related calculations. (It is unrelated to FITS WCS, and does not + use WCSLIB.) + +* Installation + + - Added an 'uninstall' rule to the makefiles. Suggested by + Cyril Richard. + + +WCSLIB version 7.4 (2021/01/31) +------------------------------- + +* C library + + - In wcshdo(), fixed a bug introduced in release 5.9 that potentially + caused loss of numerical precision in the sprintf() formatting of + floating point keyvalues. This was triggered when a large range of + CRPIXja, PCi_ja, or CDELTia values (as three separate groups) were + formatted using an 'f' format descriptor, the range not being so + large that it would have forced wcshdo() to revert to 'E' format. + Reported by Mohammad Akhlaghi. + + Also in wcshdo(), fixed a bug introduced in release 7.1 that caused + the coefficients of the TPD distortion function not to be written to + the header. TPD and Polynomial distortion function headers will now + always include the DPja.DOCORR keyword. Reported by Derek Homeier + with patch. + + - In wcsset(), fixed a segv generated in attempting to report a non- + standard units string with wcserr message reporting disabled. + Reported by Mohammad Akhlaghi. + + In wcsutrne(), allow 'Angstroms' and 'angstroms' as additional + synonyms for 'Angstrom'. + + - In datfix(), ensure that 0 is returned if an informational message + is set in wcsprm::err. Consequent on feedback independently from + Mihai Cara and Bruce Merry. + + Clarified that informational messages may be set in wcsprm::err + for returns of 0 from datfix(), obsfix(), unitfix(), and spcfix(). + +* User manual + + - Added cautions about translating CDi_ja to PCi_ja plus CDELTia for + those historical distortion functions (TPV, TNX, ZPX) that expect to + operate on intermediate world coordinates, rather than intermediate + pixel coordinates. Consequent on feedback from Mohammad Akhlaghi. + + - Documentation generation moved to doxygen 1.9.1 (was 1.8.19). + + +WCSLIB version 7.3.1 (2020/08/17) +--------------------------------- + +There are no functional changes in this release. + +* C library +* Fortran wrappers +* PGSBOX +* Utilities + + - Changed all C code, including within the flex sources, Fortran and + PGSBOX wrappers, and test suite, to use C99 style commenting (i.e. + using //), excluding the header prologues used to generate the user + manual. + +* User manual + + - Documentation generation moved to doxygen 1.8.19 (was 1.8.18). + + +WCSLIB version 7.3 (2020/06/03) +------------------------------- + +* C library + + - wcshdo() was writing MJD-OBS twice to the header, and MJD-BEG not + at all. + + - In wcshdo(), if MJDREF assumes its default value, just write + MJDREF = 0 (not MJDREFI & MJDREFF), and omit writing DATEREF, + which, with a value of '1858-11-17', looks strange and is + potentially confusing. Reported by Thomas Robitaille. + + If the fractional part of MJDREF is zero, then just write the + integer part as MJDREF (i.e. not as MJDREFI & MJDREFF). + + - Bug fix in wcsfix() - it was writing error messages that referred + to DATE-REF and MJD-REF rather then DATEREF and MJDREF. Reported + by Mihai Cara. + + - Under control of a new flag, WCSHDR_DATEREF, added the option to + wcspih() and wcsbth() to accept DATE-REF, MJD-REF, MJD-REFI, + MJD-REFF, JDREF, JD-REFI, and JD-REFF as synonyms for the standard + keywords, DATEREF, MJDREF, MJDREFI, MJDREFF, JDREF, JDREFI, and + JDREFF. The latter buck the pattern set by the other date keywords + ({DATE,MJD}-{OBS,BEG,AVG,END}), thereby increasing the potential + for confusion and error. + +* Fortran wrappers + + - As compilers are becoming much stricter (gfortran 10), modified all + Fortran test programs to use the type-specific equivalents of the + various *PUT and *GET routines. Reported by Ole Streicher. + + - For the fitshdr wrappers, added type-specific equivalents of + KEYGET: KEYGTI, KEYGTD, and KEYGTC. + +* PGSBOX + + - Modified pgtest to use the type-specific equivalents of WCSPUT. + +* User manual + + - In the section "WCSLIB Fortran wrappers", promoted use of the type- + specific equivalents of the various *PUT and *GET routines. + + - In the section "FITS-WCS and related software", added ASCL and ADS + codes, where they exist, for all software packages mentioned. + + - Documentation generation moved to doxygen 1.8.18 (was 1.8.17). + + +WCSLIB version 7.2 (2020/03/09) +------------------------------- + +* C library + + - In wcssub(), fixed a bug relating to handling coordinate lookup + tables. Reported by Mihai Cara with fix. + + Also increased the number of coordinate axes handled from 10 to 32. + +* Installation + + - New configure option, '--disable-shared', defeats generation of the + sharable library. + + +WCSLIB version 7.1 (2020/01/01) +------------------------------- + +Changes in the contents of the wcsprm struct necessitated incrementing +the major version number from 6 to 7 (i.e. the ABI changed). + +* C library + + - In wcsset(), set wcsprm::mjdref[] to zero if neither it nor + wcsprm::dateref are defined, as per the standard. Reported by + Thomas Robitaille. + + wcsset() now also checks that the number of coordinate axes does not + exceed 32. + + - In tabs2x(), fixed an out-of-bounds array access produced by invalid + world coordinates. Reported by Mihai Cara and Michael Seifert. + + In tab.c, declare static three helper functions, tabedge(), + tabrow(), and tabvox(), used by tabs2x(). Increased the number of + coordinate axes handled by tabvox() from 16 to 32. + + - Extended datfix() to handle MJDREF/DATEREF, overlooked in the + changes in release 6.1. Also fixed the handling of MJD < 0. + + - Support Solar keywords RSUN_REF, DSUN_OBS, CRLN_OBS, CRLT_OBS, + HGLN_OBS, and HGLT_OBS by accomodating them within an auxiliary + struct, auxprm, within the wcsprm struct. Now filled by the header + parsers, wcspih() and wcsbth() and handled routinely by wcsinit(), + wcssub(), wcscompare(), wcsfree(), wcsprt(), wcsset(), and wcshdo(). + Requested by Stuart Mumford (SunPy) with input from Bill Thompson. + + - Bug fix in wcsprintf_set() for resetting the output disposition. + Reported by Mihai Cara with patch. + + - In dis.{h,c}, the DOCORR record is now handled as a first-class + value via disprm::docorr. This required changing the struct. + + - In cel.c, spc.c, wcserr.c, wcsfix.c, wcshdr.c, and wcsutil.c, + quelled compiler warnings from gcc 9.2.0 generated by + -Wmaybe-uninitialized and -Wstringop-truncation (via -Wall). + + Similarly for various programs in the test suite. + + - In various functions, quelled warnings from the Microsoft Visual C++ + compiler, mostly relating to pointer arithmetic. Reported by + Michael Seifert. + +* Fortran wrappers + + - Handled compiler warnings from gcc 9.2.0 generated by + -Wstringop-truncation (via -Wall). + + - Minor enhancements to character argument handling, including, in + prjget_(), blank-filling the returned strings matching PRJ_CODE and + PRJ_NAME, and likewise for SPC_TYPE and SPC_CODE in spcget_(). + +* Utilities + + - In wcsgrid, handled compiler warnings from gcc 9.2.0 generated by + -Wstringop-truncation (via -Wall). + +* User manual + + - In the section on "FITS-WCS and related software", added mention of + the R wrappers (Rwcs). Suggested by Aaron Robotham. + + - Added a new section detailing the limit on the number of image axes + that WCSLIB can handle (currently 32), and how this could be + increased if needed. Prompted by Thomas Robitaille. + + - Augmented the section on the Fortran wrappers, particularly with + respect to character string handling in argument lists. + + - Documentation generation moved to doxygen 1.8.17 (was 1.8.16). + + +WCSLIB version 6.4 (2019/08/15) +------------------------------- + +* Installation + + - The rule change to the Fortran makefile in v6.3 to add getwcstab_f.o + to the sharable library causes it to depend on CFITSIO to resolve + fits_get_wcstab(). Hence backed out of that change. + +* User manual + + - Documentation generation moved to doxygen 1.8.16 (was 1.8.14). + + +WCSLIB version 6.3 (2019/07/12) +------------------------------- + +* C library + + - Fixed the Polynomial and TPD distortions so that, as stipulated in + WCS Paper IV, they are now considered to return an additive + correction to be applied to the given coordinates, rather than the + corrected coordinates themselves. + + Added a new subsection to the prologue of dis.h entitled 'Historical + idiosyncrasies', which discusses this issue and other vagueries. + + Amended components of the test suite accordingly: SIPTPV.keyrec, + TPV7.keyrec, and tdis1.c. + + - Fixed memory leaks and other potential problems that arose in the + wcserr system consequent on changes made in release 6.1. + + Memory allocated by wcsfixi() for messages in the array of wcserr + structs must now be freed by the caller. Amended twcsfix.c + accordingly. + + - Plugged memory leaks arising in disset(). + + - New function wcsdealloc() provided to free memory allocated within + certain WCSLIB routines. Suggested by David Motl. + + - Eliminated a swag of inconsequential compiler warnings, particularly + those emanating from the flex sources. + +* Validation + + - The library, Fortran wrappers, utilities, and test programs now pass + runtime analysis using -fsanitize=address and -fsanitize=undefined + in gcc 8.3.0, in addition to valgrind. Also compile-time strictures + using -std=c99, -pedantic, -Wall, -Wextra, and -DFORTIFY_SOURCE=2. + Prompted by feedback from Ole Streicher. + +* Installation + + - The non-graphical tests now run reliably in parallel builds + (make CHECK=nopgplot -j8 check). Requested by Mohammed Akhlaghi. + + The graphical tests can also be run in parallel, but as PGPLOT can + only handle one stream at a time, some graphics are likely to be + lost. Nevertheless, it is a useful option, especially with runtime + analysis via -fsanitize=address, etc. + + - As CFITSIO doesn't provide a Fortran wrapper for fits_read_wcstab(), + getwcstab_f.o is now always included in the WCSLIB object library + and sharable library if CFITSIO is available (and the WCSLIB Fortran + wrappers are required). Use 'configure --without-cfitsio' to + defeat this. + + - Tidied up some aspects of the build where CFITSIO is not available. + +* User manual + + - Fixed minor formatting problems in dis.h. + + +WCSLIB version 6.2 (2018/10/20) +------------------------------- + +* C library + + - Consequent on the change in release 6.1, reapplied soothing balm to + the wtbarr struct definition in wcs.h for C++ compilation (C and + Fortran compilation being unaffected). While the object libraries + themselves are unchanged, the modified wcs.h must be installed for + compiling C++ applications. Reported by Corentin Schreiber. + +* User manual + + - Fixed minor formatting problems with the doxygen manual generation. + + +WCSLIB version 6.1 (2018/10/19) +------------------------------- + +* C library + + - Added support for time-related WCS keywords (Paper VII) by expanding + the wcsprm struct to store them as auxiliary values, now filled by + the header parsers, wcspih() and wcsbth() and handled routinely by + wcsinit(), wcssub(), wcscompare(), wcsfree(), wcsprt(), wcsset(), + and wcshdo(). + + - Augmented datfix() to do various consistency checks on the new + time-related keyvalues, and added a new routine, obsfix(), to check + consistency of the OBSGEO-[XYZLBH] observatory coordinates. + + - In the usage notes for wcsbth(), clarified that, according to WCS + Papers III and VII, certain global image header keywords are + permitted in binary table headers and are expected to be inherited + by image arrays and pixel lists, and elaborated on the difficulties + that such inheritance may cause for pixel lists. + + - Revamped message string handling in the wcserr module to allow + arbitrarily long messages while greatly reducing the sizeof the + struct. + + - Extracted the definition of the wtbarr struct from wcs.h to a + separate header file, wtbarr.h, in order to reduce the number of + irrelevant warnings generated by 'gcc -Wpadded' (primarily for + code development). Applications code that needs to access members + of the struct (unlikely) must now include wtbarr.h (or wcslib.h). + + - The WCSLIB major version number was incremented as changes to the + wcsprm struct makes the ABI of the sharable library incompatible + with executables linked with that of older releases. + +* Fortran wrappers + + - Match changes to the C library, including adding a wrapper for + obsfix(). + +* Installation + + - Several changes to configure and the makefiles aimed at facilitating + code development. + + +WCSLIB version 5.20 (2018/10/05) +-------------------------------- + +* C library + + - Added utility functions dpkeyi() and dpkeyd() to dis.c, and removed + the corresponding functions from wcsutil.c. + +* Fortran wrappers + + - New wrappers for dpkeyi() and dpkeyd(). + +* Installation + + - Reworked the makefiles to allow parallel library builds, as required + by some software distributions. Reported by Dustin Lang with + suggested patch, and Zaak Beekman. (Parallel execution of the test + suite is not supported.) + + - The introduction of "deterministic" archiving (ar(1)) broke + dependency analysis in the WCSLIB makefiles on the many systems + where it is now enabled by default (i.e. binutils configured with + --enable-deterministic-archives). + + Modified configure to force non-deterministic archiving during the + library builds, thus repairing the dependency analysis. However, + the static object libraries are now reconstructed using + deterministic archiving in the process of installing them. + + - Fixed problems with the dependency analysis, solely affecting code + development. + + +WCSLIB version 5.19.1 (2018/07/28) +---------------------------------- + +* Installation + + - Updated ./config/config.{guess,sub} to the latest versions from the + GIT repository, timestamped 2018-07-18 and 2018-07-25, respectively. + The previous pair were dated 2012-02-10 and 2012-04-18. + + +WCSLIB version 5.19 (2018/07/27) +-------------------------------- + +* C library + + - In wcssub(), fixed a bug in handling distortion functions on axes + with changed axis number in the subimage (i.e. via deletion or + addition of an axis). + + - In various routines within dis.c, wcs.c, wcshdr.c, and wcspih.l, + increased the size of various sprintf() output buffers to avert + -Wformat-overflow warnings from gcc 8.1.0 (with -DFORTIFY_SOURCE=2). + Reported by Simon Conseil. + + Also fixed other warnings in these routines from gcc 8.1.0 + relating to -Wcast-function-type, -Wmaybe-uninitialized, and + -Wunused-parameter. + + - In wcsutrn.l (the units alias translator used by wcsfix), recognise + 'Deg', 'Degree', and 'Degrees' as aliases for 'deg'. Rogue header + reported by Jim Lewis. + + - Added a note to the prologue of spx.h explaining WCSLIB's use of + Cox's air-to-vacuum transformation equation rather than the IUGG + relation cited in WCS Paper III. Noted by Benjamin Alan Weaver. + + - In the test suite, avert nuisance compiler warnings in tdis1. + Made tsphdpa more robust in handling user coordinate input. + +* PGSBOX + + - In pgwcsl_(), avert nuisance warnings from gcc 8.1.0 relating to + unused parameters (-Wunused-parameter). Also averted nuisance + compiler warnings in cpgtest. + +* Utilities + + - wcsware was not reading the -TAB table from the FITS file for + alternate descriptions, bug reported by Chiara Marmo. Nor was + wcsfix() ever invoked for them. + + Made it more robust in handling user input of coordinates for the + -x and -w options. + + - Fixed compiler warnings from gcc 8.1.0 in HPXcvt + (-Wmaybe-uninitialized), and wcsgrid (-Wformat-overflow). + +* Installation + + - Amended configure.ac to allow cross-compilation, and also updated + the auxiliary configure scripts in the config/ directory. Patch + supplied by Mosč Giordano. + + +WCSLIB version 5.18 (2018/01/10) +-------------------------------- + +* C library + + - New routines introduced to preclude altering the global variables + NPVMAX, NPSMAX, and NDPMAX, which determine how much memory to + allocate for storing PVi_ma, PSi_ma, DPja, and DQia keyvalues: + wcsinit(), lininit(), lindist(), and disinit(). These are now used + by various WCSLIB routines, such as the header parsers, which + previously temporarily altered the global variables, thus posing a + thread hazard. Testing and feedback on thread-safety issues, with + patches, from Rodrigo Tobar Carrizo. + + - The Flex scanners, fitshdr(), wcsbth(), wcspih(), wcsulexe(), and + wcsutrne(), have been rewritten as thin wrappers (with the same API) + over scanners that were modified (with changed API) as required to + use Flex's "reentrant" option. Consequently, they are now reentrant + and should be thread-safe. That also passes through to the + deprecated wrappers, wcsulex() and wcsutrn(). + + - Fixed memory leaks in lindist() and lincpy() uncovered by valgrind. + + - Test programs tfitshdr, tpih1, tpih2, and ttab3 are now careful to + free all allocated memory before exit to defeat spurious reports of + memory leaks by valgrind. + +* Fortran wrappers + + - New wrappers for wcsinit(), lininit(), lindist(), and disinit(). + +* Installation + + - configure now recognises the value of ARFLAGS obtained from the + environment. + +* User manual + + - Updates and amendments in line with the above changes. + + - Documentation generation moved to doxygen 1.8.14 (was 1.8.13). + + +WCSLIB version 5.17 (2017/09/18) +-------------------------------- + +* C library + + - Fixed a memory leak in wcspih(). Reported by Pim Schellart. + + - Fixed compiler warnings about comparison between signed and unsigned + integers in tab.c (Pey-Lian Lim). Also fixed warnings in other + functions about unused parameters. + +* Fortran wrappers + + - Fixed compiler warnings about comparison between signed and unsigned + integers in several routines. + +* PGSBOX + + - Fixed a compiler warning about comparison between signed and + unsigned ints. + +* Installation + + - Removed setgid permission on installation directories (for Fedora + Linux, reported by Sergio Pascual). + +* User manual + + - Minor updates and amendments. + + - Documentation generation moved to doxygen 1.8.13 (was 1.8.10). + + +WCSLIB version 5.16 (2017/01/15) +-------------------------------- + +* C library + + - Bug fix in spcfix() for the previous change. Reported separately by + Julian Taylor & Peter Williams. + + +WCSLIB version 5.15 (2016/04/05) +-------------------------------- + +* C library + + - Bug fix in wcsulex.l for the previous change. Reported, and fix + supplied by Tammo Jan Dijkema. + + - In spcfix(), report the value of VELREF if ctype is translated from + the AIPS convention. + + +WCSLIB version 5.14 (2016/02/07) +-------------------------------- + +* C library + + - In wcsulex.l and wcsutrn.l, applied a workaround for a memory leak + introduced by a change (bug) in flex in Aug/2012. Reported, and + fix supplied by Thomas Robitaille and Erik Bray. + +* Installation + + - In MacOSX, create symlink libwcs.dylib pointing to the dynamic + library so that WCSLIB is linked dynamically by default. Requested + by Paul Price. + + +WCSLIB version 5.13 (2016/01/26) +-------------------------------- + +* C library + + - In wcshdo(), provide floating-point format control via the 'ctrl' + argument (formerly 'relax'). + + - In wcspih(), PLATEID by itself is no longer sufficient to trigger a + DSS translation (reported by James Allen). + + - In unitfix(), ensure that the message buffer cannot be overrun, e.g. + by blank-padded unit strings (reported by Vishal Kasliwal). + + +WCSLIB version 5.12 (2015/11/15) +-------------------------------- + +* C library + + - Bug fix in wcshdo() for CRVALia precision reported by Mihai Cara. + + +WCSLIB version 5.11 (2015/10/18) +-------------------------------- + +* C library + + - Bug fixes in wcspih.l and dis.c for WAT distortions provided by + Ole Streicher. + + +WCSLIB version 5.10 (2015/10/09) +-------------------------------- + +* C library + + - In wcshdo(), allow output of floating point keyvalues to 15 + significant digits (was 14), mainly for astropy. + + - In wcspih(), allow unrecognised WAT projection types (tan, etc.) to + pass without returning an error. Reported by Michael Droettboom. + Also fixed a bug triggered by SIP keywords in unconventional order, + reported by Colin Slater, LSST. + + +WCSLIB version 5.9 (2015/07/21) +------------------------------- + +* C library + + - In disx2p(), make proper allowance for the possibility that the TPD + and SIP forward and inverse distortion polynomials may not be of the + same degree, reported by Martin Kuemmel. + + - Fix-ups in wcshdo() reported by Michael Droettboom, and several + tidy-ups as well. + + +WCSLIB version 5.8 (2015/07/08) +------------------------------- + +* C library + + - DSS (Digitized Sky Survey) coordinates are now handled via TPD, as + are the TNX and ZPX "projections". New test script tdis3 and + headers DSS.keyrec, TNX.keyrec, and ZPX.keyrec. + + - New function dishdo() can be used to set a flag that causes + wcshdo() to write headers in the form of the distortion function + used internally (usually TPD). + + - Added the capability, via DPja.DOCORR, for a distortion function to + to compute an additive correction to the undistorted coordinates + (rather than computing the distorted coordinates themselves). + + - Added auxiliary variables to TPD via DPja.AUX.jhat.COEFF.m. + + - Bug fix in wcshdo() for TPV, diagnosed by Michael Droettboom. + +* Fortran wrappers + + - Wrapper for dishdo(). + + +WCSLIB version 5.7 (2015/06/29) +------------------------------- + +* C library + + - Extended wcssub() to handle distortions (and thus wcscopy() also). + This required axis mapping to be enabled for SIP distortions within + WCSLIB. + + - wcshdo() now handles all distortions currently supported by WCSLIB: + SIP, TPV, TPD, and Polynomial. + + - Bug fixes in sphx2s() and sphs2x() for non-unit vector strides for + special-case rotations. + + - In wcsset(), modify wcsprm::ctype after translating TPV so that + subsequent calls won't try to re-translate it, the PVi_ma records + by then having been erased. Reported by Michael Droettboom. Also, + restore NDPMAX after translating TPV. + + - Bug fixes in discpy(), disprt(), disset(), and disx2p(). + + - Added disperr(), prjperr(), spcperr(), spxperr(), and tabperr() to + complete the set and make reporting errors slightly more convenient. + +* Utilities + + - wcsware has a new option, -o, to print the wcsprm struct in the form + of a FITS header using wcshdo(). Also, the -a option has been + extended to allow a 0-relative numeric index for selecting an + alternate WCS, where the alternates are sequenced alphabetically + (without gaps) following the primary representation. + +* User manual + + - Documentation generation moved to doxygen 1.8.10 (was 1.8.9.1). + + +WCSLIB version 5.6 (2015/06/14) +------------------------------- + +* C library + + - Bug fixes in wcspih() and sipset() to account for the fact that the + independent variables of the SIP polynomial are pixel coordinate + offsets from CRPIXja, not pixel coordinates per se. Diagnosed by + Michael Droettboom. + + Validated SIP and TPV handling by comparing the output of + 'wcsware -x' with that of wcstools 'xy2sky -d -n6' using astropy's + SIP test headers, and separate SIP and TPV headers originating from + the Palomar Transient Factory provided by David Shupe. + + Changed SIP.keyrec so that CRPIX1 and CRPIX2 differ in value, + thereby making tests sensitive to any confusion between the two. + + +WCSLIB version 5.5 (2015/05/05) +------------------------------- + +* C library + + - Bug fixes in wcspih.l for headers with multiple distortions, and for + distortion parameter look-alike keywords. + + - Bug fix in sipset() uncovered by valgrind. + + +WCSLIB version 5.4.1 (2015/05/01) +--------------------------------- + +* C library + + - Added a fairly lengthy section to the prologue of dis.h describing + the Paper IV keywords and the distortion functions currently + implemented. + +* User manual + + - Added a new section on WCSLIB version numbers. + + +WCSLIB version 5.4 (2015/04/21) +------------------------------- + +* C library + + - Further work on distortions: wcspih() now parses Paper IV distortion + keywords (CPDISja, CQDISia, DPja, DQia, etc.), thus handling record- + valued keywords. SIP keywords are also parsed and translated into + Paper IV keywords. + + - TPV, SIP, and whereever possible, the general Polynomial distortion + function of Paper IV are now implemented by the Template Polynomial + Distortion (TPD), that being a superset of TPV and SIP. TPD, which + can also handle 1D distortions and be used for inversions, is also + supported as a separate distortion type defined by Paper IV + keywords. TPD efficiently handles 1D distortions and distortions + without radial terms. + + - Specialized inverse distortions, such as defined by SIP, are + implemented by TPD and recognized by disx2p(), which uses them to + compute the initial approximation of its more precise iterative + solution. + + - Generalized tdis1.c so that it can do closure tests for SIP headers, + and added a SIP test header. + + +WCSLIB version 5.3.1 (2015/04/21) +--------------------------------- + +* C library + + - Bug fix in test program tdis1.c reported by Martin Kuemmel. + + +WCSLIB version 5.3 (2015/04/21) +------------------------------- + +First public release of WCSLIB 5.x with distortions capability. + +* C library + + - Changed disprm::dtype from char[16] to char[72] for reasons relating + to header parsing. + + - More armour plating for disset() and friends. They also use new + utility functions wcsutil_dpkey_int() and wcsutil_dpkey_double() to + extract numeric values from a dpkey struct. This ensures their + independence of the data type, the appropriate reporting of dpkey + values by disprt(), and (eventually) the appropriate writing of DPja + and DQia keyvalues by wcshdo(). + + - Augmented the usage comments for dpfill() to explain how numeric + values in a dpkey struct are handled. + +* Utilities + + - wcsware now uses wcserr to provide a backtrace on errors. Also + fixed a couple of minor annoyances relating to alternate + representations. + + +WCSLIB version 5.2 beta release (2015/04/15) +-------------------------------------------- + +* C library + + - Further development of disprm and related functions to implement + the FITS keywords for distortions introduced in WCS Paper IV, thus + changing disprm's ABI once again. + + disprm's model is now similar to wcsprm's handling of PVi_ma and + PSi_ma, where the parsed keyrecords are loaded into a set of pvcard + and pscard structs and wcsset() does the analysis. For disprm, the + parsed DPja or DQia keyrecords are loaded into a set of dpkey + structs for disset() to analyse. + + - New function dpfill() aids in filling a dpkey struct. Function + disparms() has been removed. It was no longer needed as + disprm::parms[][] is now a "returned" member of the struct. + + - Implementation of the general Polynomial distortion function defined + in Sect. 3.1 of WCS Paper IV. The polynomial is defined in terms of + Paper IV keywords. + + - Bug fixes in diswarp() and linwarp() reported by + Michael Droettboom. + + - Test program tdis1 now tests a TPV header for closure in two ways: + directly as a specialist TPV distortion (as before), then by + translating it into a general polynomial distortion. It then also + tests that the TPV and polynomial distortions produce the same + results. + +* Fortran wrappers + + - Changes to the wrappers reflecting changes to disprm. + + - Bug fixes in fitshdr_f.c, and test programs tdis1.f, tfitshdr.f, + twcsfix.f, and twcs.f. All reported and/or fixed by Ole Streicher. + + +WCSLIB version 5.1 beta release (2015/04/07) +-------------------------------------------- + +* C library + + - Distortions-related bug fixes to linset(), linp2x(), and linx2p(). + + - New functions diswarp() and linwarp() compute statistics of the + distortion functions over a specified domain. Changed tdis1.c to + test them. + + - Changes to disprm (so changed ABI). + +* Fortran wrappers + + - Wrappers for diswarp() and linwarp(). + + +WCSLIB version 5.0 beta release (2015/04/05) +-------------------------------------------- + +* C library + + - Implemented the framework of WCS Paper IV (the draft distortions + proposal) based on a new struct, disprm, and suite of routines with + header dis.h: disini(), disalloc(), discpy(), disfree(), disprt(), + disset(), disp2x(), and disx2p(). New test program tdis1. + + Consequent changes to the linprm struct have altered its ABI, thus + necessitating an increment in the WCSLIB major verion number. + + - wcsset() now recognises the "TPV" projection proposed in an early + draft of WCS Paper II, and translates it into a disprm distortion - + by request of Octavi Fors. Additionally, it recognises "TPU" as + equivalent to "TPV" but using a prior distortion (coming before the + linear transformation matrix) rather than sequent (coming after it). + + - In wcspih() and wcsbth(), added relaxation flags to allow PC0i_0ja, + PV0j_0ma, and PS0j_0ma (i.e. with leading zeroes on the index). + + - Added wcslib_version() to return the WCSLIB version number, as + suggested by Ole Streicher. + + - Fixed problems uncovered by valgrind in prjbchk() (reported by + Ger van Diepen), sphx2s(), sphx2s(), spcspxe(), spcxpse(), tabini(), + and wcshdo_util(). + + - Tidied up error reporting, particularly relating to translating + status returns from lower-level functions. New functions linperr() + and celperr() report error messages from the structs they contain + in addition to their own. + + - Changed output formatting of floating point values in linprt(), + celprt(), prjprt(), spcprt(), tabprt(), and wcsprt(). Updated the + test output reference files for tbth1, tpih1, twcsfix, twcssub, and + twcstab to suit. + + - Tidied up several of the test programs, mostly to free memory + explicitly before exit so that valgrind doesn't report (non-)leaks. + +* Fortran wrappers + + - Wrappers for the new distortion functions. New test programs + tdis1.f and tdis2.f. + + - Updates following changes to wcspih() and wcsbth(). + + - Added wrappers for linperr() and celperr(). Also added prjperr_(), + spcperr_(), and tabperr_() as convenient wrappers on wcserr_prt(). + + - Wrapper for wcslib_version(). + +* Utilities + + - Added a "lint" function to wcsware to check a FITS header for + conformance to the WCS standard. New test program twcslint. + +* PGSBOX + + - In PGCRLB, fixed an incorrect use of the MOD intrinsic reported by + 'gfortran -std=f95' via Jean-Baptiste Marquette. + + +WCSLIB version 4.25.1 (2015/01/05) +---------------------------------- + +* C library + + - Updated the test output reference files for tpih1, tbth1, twcsfix, + and twcstab to account for the change to wcsset() in release 4.25. + +* Fortran wrappers + + - Updated the test output reference files for tpih1, twcsfix, and + twcstab to account for the change to wcsset() in release 4.25. + +* User manual + + - Documentation generation moved to doxygen 1.8.9.1 (was 1.8.8). + + +WCSLIB version 4.25 (2014/12/15) +---------------------------------- + +* C library + + - wcsset() now supplies default values for the auxiliary keywords + EQUINOXa and RADESYSa if not present in the FITS header. + + +WCSLIB version 4.24 (2014/09/19) +---------------------------------- + +* C library + + - Changed API to wcscompare() to allow a tolerance to be specified for + floating-point comparisons. Contributed by Michael Droettboom. + +* Fortran wrappers + + Track the change to wcscompare(). + +* User manual + + - Documentation generation moved to doxygen 1.8.8 (was 1.8.4). + + - Added mention of WCSLIB in "homebrew-science" (MacOSX) in the + section on other packages. + + +WCSLIB version 4.23 (2014/05/11) +-------------------------------- + +* C library + + - New function wcscompare() compares two wcsprm structs for equality + with varying degrees of strictness. Test program twcscompare. + Contributed by Michael Droettboom. + + - In wcssub(), fixed a bug uncovered by valgrind arising from + allocation of insufficient memory for temporaries when a new axis + is added. Reported by Michael Droettboom. + + - In wcshdo(), don't write RESTFRQ or RESTWAV if zero. + + Also, to distinguish them from integer keyvalues, floating- + point values will now always be written with an exponent or + fractional part, ".0" being appended if necessary to achieve this. + Suggested by Peter Weilbacher. + + - Fixed the '-h' option in twcshdr. + +* Fortran wrappers + + - Wrapper for wcscompare(). + + +WCSLIB version 4.22 (2014/04/13) +-------------------------------- + +* C library + + - In pcox2s() and pcos2x(), use alternative projection equations for + greater numerical precision near theta == 0. In cops2x(), return + an exact result for theta at the poles. Relaxed the tolerance for + bounds checking a little in sflx2s(). + + +WCSLIB version 4.21 (2014/03/24) +-------------------------------- + +* C library + + - New function prjbchk() performs bounds checking on native spherical + coordinates. It is invoked automatically by the deprojection (x2s) + routines if prjprm::bounds&4 is set. Documented the new bounds + checking implemented by prjbchk() in the prologue entry for + wcsbchk(). Improvements to tprj1 to test the vector API of prjx2s() + and to deal better with out-of-bounds native coordinates returned by + it. + + - Bug fixes in the projection routines: in hpxx2s() relating to bounds + checking, bug introduced at 4.20, reported by Michael Droettboom; in + parx2s() and molx2s() relating to setting the stat vector; in + hpxx2s() relating to implementation of the vector API; and in + xphx2s() relating to setting an out-of-bounds value of phi. + +* Fortran wrappers + + - Wrapper for prjbchk(). Modified tprj1.f to track changes to the C + version. + + +WCSLIB version 4.20 (2013/12/18) +-------------------------------- + +* C library + + - New function, wcsbchk(), for controlling bounds checking in the + projection routines. + + - Enable bounds checking in the pixel-to-sky (x2s) direction for HPX + and XPH if prjprm::bounds&2. Tightened bounds checking in xphx2s(). + prjini() now sets prjprm::bounds = 3 to enable all bounds checking + by default. + + - Fixed an incorrect error message in wcs_units(). Also report + potentially unsafe units translations in unitfix() - both + contributed by Michael Droettboom. + +* Fortran wrappers + + - Wrapper for wcsbchk(). + +* PGSBOX + + - Fixed a few nuisance warnings from gfortran 4.7.2 relating to + implicit type conversions in pgsbox.f. + +* User manual + + - Added mention of wcsjs (Javascript) in the section on other + packages. + + +WCSLIB version 4.19 (2013/09/30) +-------------------------------- + +* C library + + - Bug fix in wcshdo(), uncovered by cppcheck and reported by David + Binderman. Fixed additional minor inconsistencies in hpxx2s(), + wcsprintf(), and wcsfprintf() reported by cppcheck. + + - Minor fix to twcstab reported by cppcheck. + + - Bug fix in wcssub() for handling (non-standard) PROJPn keywords + (uncovered by valgrind, reported by Paul Price). + + +WCSLIB version 4.18 (2013/07/13) +-------------------------------- + +* C library + + - Implemented the butterfly projection (XPH), being the polar form of + the HEALPix projection with (H,K) = (4,3). Augmented tprj1.c and + tprj2.c to suit. + + - Bug fix in celfix() when translating GLS to SFL with non-zero + reference point. + +* Fortran wrappers + + - Wrappers for the XPH projection functions and corresponding + modifications to the test programs. + + - Fixed a few inconsequential warnings from gfortran 4.7.2 relating + to implicit type conversions in the test programs. + +* Utilities + + - In HPXcvt, fixed incorrect axis scaling (CDELT1 and CDELT2) in the + XPH header, and set the PCi_ja matrix in the HPX header to a pure + 45 degree rotation with appropriate adjustment of CDELT1 and CDELT2 + (matching XPH). Also set LONPOLE to 180 degree in the XPH header + with adjustment of CRVAL1 as recommended in the errata. These + changes prompted by Paddy Leahy. Also, modified the FITS header + comment for XPH - it is no longer "experimental", and accept the + '-x' option (i.e. without qualification) as equivalent to '-xn'. + +* User manual + + - Documentation generation moved to doxygen 1.8.4 (was 1.5.6). + + +WCSLIB version 4.17 (2013/01/29) +-------------------------------- + +* C library + + - Added wcsfprintf() to wcsprint.h. Now used in wcsbth() and wcspih() + to allow diagnostic output to be redirected. Changes provided by + Michael Droettboom. + + +WCSLIB version 4.16 (2012/11/07) +-------------------------------- + +* C library + + - When wcspih() constructs a default coordinate representation it will + give it the special name "DEFAULTS", and will not report "Found one + coordinate representation" (if reporting is enabled). + +* Utilities + + - wcsware has a new option, -P, which does the same as -p except that + it won't print a wcsprm struct with the name "DEFAULTS". + + +WCSLIB version 4.15 (2012/09/26) +-------------------------------- + +* C library + + - Bug fixes in spctype(), spcspxe(), spcxpse, and spctrne() for + propagating an error status correctly from lower-level routines. + Amendments to the prologues of these routines. Reported by + Hans Terlouw. + + - Similarly for wcsunitse(), wcsulexe(), and wcsutrne(). + +* PGSBOX + + - Bug fix in PGSBOX for handling cycles in angle when both world + coordinate elements are angular. Reported by Thomas Robitaille. + + WCSLIB version 4.14 (2012/07/13) -------------------------------- @@ -144,7 +1842,7 @@ WCSLIB version 4.8.3 (2011/11/17) * C library - Minor generalization of the wcserr diagnostics to allow the return - of informative messages which are associated with negative status + of informative messages, which are associated with negative status values. wcserr_prt() will now recognize and print them as such. Added wcserr_clear() to reset (clear) a wcserr struct. @@ -1519,7 +3217,7 @@ WCSLIB version 2.9 (2002/04/03) - Fixed a bug with alias translation in wcsset(). - Added a conditional compilation directive to lin.c for Apple's - MacOS X. + MacOSX. * Fortran library @@ -1949,4 +3647,4 @@ WCSLIB version 1.0 (1995/01/31) Initial release. ------------------------------------------------------------------------ -$Id: CHANGES,v 4.14 2012/07/13 10:02:44 cal103 Exp $ +$Id: CHANGES,v 8.6 2026/03/29 13:53:57 mcalabre Exp $ diff --git a/cextern/wcslib/GNUmakefile b/cextern/wcslib/GNUmakefile index 45acb4679239..9505010cd791 100644 --- a/cextern/wcslib/GNUmakefile +++ b/cextern/wcslib/GNUmakefile @@ -1,5 +1,5 @@ #----------------------------------------------------------------------------- -# GNU makefile for building WCSLIB 4.14 +# GNU makefile for building WCSLIB 8.6 # # Summary of the main targets # --------------------------- @@ -8,6 +8,8 @@ # tests: Do 'make tests' in each subdirectory (compile test programs but # don't run them). # install: Do 'make install' in each subdirectory. +# uninstall: Deletes installed files (this release only), including the +# sharable library. # clean: Recursively delete intermediate files produced as part of the # build, e.g. object modules, core dumps, etc. # cleaner: Recursively clean, and also delete test executables, test @@ -31,10 +33,11 @@ # C/GNUmakefile. # # Author: Mark Calabretta, Australia Telescope National Facility, CSIRO. -# http://www.atnf.csiro.au/people/Mark.Calabretta -# $Id: GNUmakefile,v 4.14 2012/07/13 10:02:44 cal103 Exp $ +# http://www.atnf.csiro.au/computing/software/wcs +# $Id: GNUmakefile,v 8.6 2026/03/29 13:53:57 mcalabre Exp $ #----------------------------------------------------------------------------- # Get configure settings. +SUBDIR := . include makedefs ifeq "$(CHECK)" "nopgplot" @@ -42,7 +45,7 @@ ifeq "$(CHECK)" "nopgplot" endif .PHONY : build check chmod clean cleaner cleanest distclean install \ - realclean show tests writable + realclean show tests uninstall writable build : -@ for DIR in $(SUBDIRS) ; do \ @@ -56,7 +59,7 @@ check tests :: show -@ $(TIMER) @ for DIR in $(SUBDIRS) ; do \ echo '' ; \ - $(MAKE) -i -C $$DIR cleaner ; \ + $(MAKE) -i -C $$DIR cleaner build ; \ done -@ echo '' @ for DIR in $(TSTDIRS) ; do \ @@ -80,27 +83,31 @@ install : @ for DIR in $(INSTDIR) ; do \ $(MAKE) -k -C $$DIR $@ ; \ done + if [ ! -d "$(LIBDIR)/pkgconfig" ] ; then \ + $(INSTALL) -d -m 775 $(LIBDIR)/pkgconfig ; \ + fi + $(INSTALL) -m 444 wcslib.pc $(LIBDIR)/pkgconfig/wcslib.pc $(INSTALL) -m 444 wcsconfig.h wcsconfig_f77.h $(INCDIR) - if [ ! -d "$(DOCDIR)" ] ; then \ - $(INSTALL) -d -m 2775 $(DOCDIR) ; \ + $(INSTALL) -d -m 775 $(DOCDIR) ; \ fi $(INSTALL) -m 444 CHANGES COPYING* README $(DOCDIR) + $(INSTALL) -m 444 INSTALL THANKS VALIDATION $(DOCDIR) - if [ -h $(DOCLINK) ] ; then \ $(RM) $(DOCLINK) ; \ - $(LN_S) $(notdir $(DOCDIR)) $(DOCLINK) ; \ - fi - - if [ ! -d "$(PDFDIR)" ] ; then \ - $(INSTALL) -d -m 2775 $(PDFDIR) ; \ fi - $(INSTALL) -m 444 wcslib.pdf $(PDFDIR) - - if [ ! -d "$(HTMLDIR)/html" ] ; then \ - $(INSTALL) -d -m 2775 $(HTMLDIR)/html ; \ - fi - $(INSTALL) -m 444 html/* $(HTMLDIR)/html - if [ ! -d "$(LIBDIR)/pkgconfig" ] ; then \ - $(INSTALL) -d -m 2775 $(LIBDIR)/pkgconfig ; \ - fi - $(INSTALL) -m 444 wcslib.pc $(LIBDIR)/pkgconfig/wcslib.pc + $(LN_S) $(notdir $(DOCDIR)) $(DOCLINK) + $(MAKE) -k -C doxygen $@ + +uninstall : + @ for DIR in $(INSTDIR) ; do \ + $(MAKE) -k -C $$DIR $@ ; \ + done + - cd $(LIBDIR) && $(RM) pkgconfig/wcslib.pc + - cd $(INCDIR) && $(RM) wcsconfig*.h + - $(RM) $(DOCLINK) + - $(RM) $(DOCDIR) + $(MAKE) -k -C doxygen $@ clean cleaner : for DIR in $(SUBDIRS) doxygen ; do \ @@ -113,7 +120,8 @@ cleanest distclean realclean : done - $(RM) *.log - $(RM) -r autom4te.cache autoscan.log - - $(RM) confdefs.h conftest.* + - $(RM) -r api-sanity-check + - $(RM) configure~ confdefs.h conftest.* - $(RM) config.log config.status configure.lineno - $(RM) makedefs wcslib.pc - $(RM) wcsconfig.h wcsconfig_*.h @@ -140,12 +148,16 @@ config.status : configure -@ $(TIMER) -@ echo '' -@ echo "Environment variables that affect 'configure':" - -@ echo " CC = $${CC-(undefined)}" - -@ echo " CFLAGS = $${CFLAGS-(undefined)}" + -@ echo " FLEX = $${FLEX-(undefined)}" + -@ echo " FLFLAGS = $${FLFLAGS-(undefined)}" -@ echo " CPP = $${CPP-(undefined)}" -@ echo " CPPFLAGS = $${CPPFLAGS-(undefined)}" + -@ echo " CC = $${CC-(undefined)}" + -@ echo " CFLAGS = $${CFLAGS-(undefined)}" -@ echo " F77 = $${F77-(undefined)}" -@ echo " FFLAGS = $${FFLAGS-(undefined)}" + -@ echo " BINDC = $${BINDC-(undefined)}" + -@ echo " ARFLAGS = $${ARFLAGS-(undefined)}" -@ echo " LDFLAGS = $${LDFLAGS-(undefined)}" -@ echo '' ./configure --no-create @@ -160,20 +172,10 @@ dist : $(MAKE) -C doxygen cleanest build $(MAKE) -C utils man $(MAKE) distclean - -@ echo $(WCSLIBPKG)/C/RCS > wcslib.X - -@ echo $(WCSLIBPKG)/C/flexed/RCS >> wcslib.X - -@ echo $(WCSLIBPKG)/C/test/RCS >> wcslib.X - -@ echo $(WCSLIBPKG)/doxygen/RCS >> wcslib.X - -@ echo $(WCSLIBPKG)/Fortran/RCS >> wcslib.X - -@ echo $(WCSLIBPKG)/Fortran/test/RCS >> wcslib.X - -@ echo $(WCSLIBPKG)/makedefs >> wcslib.X - -@ echo $(WCSLIBPKG)/other >> wcslib.X - -@ echo $(WCSLIBPKG)/pgsbox/RCS >> wcslib.X - -@ echo $(WCSLIBPKG)/RCS >> wcslib.X - -@ echo $(WCSLIBPKG)/TODO >> wcslib.X - -@ echo $(WCSLIBPKG)/utils/RCS >> wcslib.X - -@ echo $(WCSLIBPKG)/wcslib.T >> wcslib.X - -@ echo $(WCSLIBPKG)/wcslib.X >> wcslib.X + -@ echo $(WCSLIBPKG)/makedefs >> wcslib.X + -@ echo $(WCSLIBPKG)/TODO >> wcslib.X + -@ echo $(WCSLIBPKG)/wcslib.T >> wcslib.X + -@ echo $(WCSLIBPKG)/wcslib.X >> wcslib.X rm -f $(WCSLIBPKG).tar.bz2 tar cf - -C .. -X wcslib.X $(WCSLIBPKG) | \ tar t | \ @@ -186,19 +188,20 @@ dist : chmod 444 $(WCSLIBPKG).tar.bz2 install_dist : - cp -fp $(WCSLIBPKG).tar.bz2 /nfs/ftp/software/wcslib/ - mv -f $(WCSLIBPKG).tar.bz2 ../wcslib-releases/ - (cd /nfs/ftp/software/wcslib/ && \ + scp -p $(WCSLIBPKG).tar.bz2 cal103@venice:/nfs/ftp/software/wcslib/ + ssh cal103@venice "cd /nfs/ftp/software/wcslib/ && \ rm -f wcslib.tar.bz2 && \ - ln -s $(WCSLIBPKG).tar.bz2 wcslib.tar.bz2) - cp -fp CHANGES wcslib.pdf ~/public_html/WCS/ - rsync --archive --delete html/ ~/public_html/WCS/wcslib/ + ln -s $(WCSLIBPKG).tar.bz2 wcslib.tar.bz2" + rsync --archive --delete html/ ../wwwatdocs/wcs/wcslib/ + cp -fp CHANGES wcslib.pdf ../wwwatdocs/wcs/ + mv -f $(WCSLIBPKG).tar.bz2 ../wcslib-releases/ + cd ../wcslib-releases/ && ./mkindex > index.html configure : configure.ac -@ echo '' -@ $(TIMER) autoconf + -@ $(RM) configure~ -# Code development overrides must be included specifically before 'configure' -# generates makedefs. +# Code development settings exported for 'configure'. -include flavours diff --git a/cextern/wcslib/INSTALL b/cextern/wcslib/INSTALL index 3e7cff9d40c1..a76048fcb38b 100644 --- a/cextern/wcslib/INSTALL +++ b/cextern/wcslib/INSTALL @@ -1,17 +1,26 @@ ------------------------------------------------------------------------------ -WCSLIB 4.14 and PGSBOX 4.14 INSTALLATION +WCSLIB 8.6 and PGSBOX 8.6 INSTALLATION -------------------------------------- WCSLIB requires an ANSI C compiler with standard ANSI C environment, that is, a standard C library and header files as defined in Appendix B of Kernigan & Ritchie, 2nd ed. +If you are running a typical Linux distro and have installed WCSLIB before, +then all you should need to do is + + tar pxvf wcslib-8.6.tar.bz2 + cd wcslib-8.6 + make install + +Otherwise, read on. + Installation of WCSLIB is handled by GNU autoconf; GNU make (referred to here as 'gmake') must be used. The WCSLIB distribution also includes PGSBOX (refer -to the README file), to unpack it type +to the README file). To unpack the tar file, type - zcat wcslib-4.14.tar.gz | tar pvxf - - cd wcslib-4.14 + bzcat wcslib-8.6.tar.bz2 | tar pvxf - + cd wcslib-8.6 then if you do not need to specify any configuration options, simply run @@ -24,11 +33,11 @@ which includes both the C library and Fortran wrappers, and also libpgsbox.a. (WARNING: The build may fail with gmake 3.79, upgrade to 3.79.1 or later.) configure tries to determine the location of the PGPLOT and CFITSIO libraries -required by some programs in the test suite. If it fails to find them you -can, if you wish, tailor the few variables found at the start of "makedefs". -Of course you do not need to exercise the test suite in order to build and -install the library - if configure fails to find anything required for that it -will issue an explicit error message. +required by some utilities (wcsware, wcsgrid) and programs in the test suite. +If it fails to find them you can, if you wish, tailor the few variables found +at the start of "makedefs". Of course you do not need to exercise the test +suite in order to build and install the library - if configure fails to find +anything required for that it will issue an explicit error message. To build and exercise the test suite use @@ -92,8 +101,8 @@ The INSTALL file provided with GNU autoconf 2.53 is appended without change. Author: Mark Calabretta, Australia Telescope National Facility, CSIRO. -http://www.atnf.csiro.au/people/Mark.Calabretta -$Id: INSTALL,v 4.14 2012/07/13 10:02:44 cal103 Exp $ +http://www.atnf.csiro.au/computing/software/wcs +$Id: INSTALL,v 8.6 2026/03/29 13:53:57 mcalabre Exp $ ============================================================================== diff --git a/cextern/wcslib/README b/cextern/wcslib/README index 5878bb415a52..06dc9f458d9a 100644 --- a/cextern/wcslib/README +++ b/cextern/wcslib/README @@ -1,8 +1,8 @@ ------------------------------------------------------------------------------ - WCSLIB 4.14 and PGSBOX 4.14 + WCSLIB 8.6 and PGSBOX 8.6 ------------------------------------------------------------------------------ - WCSLIB 4.14 - an implementation of the FITS WCS standard. - Copyright (C) 1995-2012, Mark Calabretta + WCSLIB 8.6 - an implementation of the FITS WCS standard. + Copyright (C) 1995-2026, Mark Calabretta This file is part of WCSLIB. @@ -19,11 +19,9 @@ You should have received a copy of the GNU Lesser General Public License along with WCSLIB. If not, see http://www.gnu.org/licenses. - Direct correspondence concerning WCSLIB to mark@calabretta.id.au - Author: Mark Calabretta, Australia Telescope National Facility, CSIRO. - http://www.atnf.csiro.au/people/Mark.Calabretta - $Id: README,v 4.14 2012/07/13 10:02:44 cal103 Exp $ + http://www.atnf.csiro.au/computing/software/wcs + $Id: README,v 8.6 2026/03/29 13:53:57 mcalabre Exp $ ------------------------------------------------------------------------------ Please refer to diff --git a/cextern/wcslib/THANKS b/cextern/wcslib/THANKS index 121a95450caa..b73e2a64d79b 100644 --- a/cextern/wcslib/THANKS +++ b/cextern/wcslib/THANKS @@ -2,80 +2,121 @@ I would like to acknowledge the following people who have contributed to WCSLIB and/or PGSBOX in some way since 1995 - via bug reports, patches, suggestions for improvements, positive feedback, etc. +Mohammad Akhlaghi (CEFCA & GNUastro) +James Allen (U. Sydney) James M. Anderson (MPIfR) Robbie Auld (Cardiff U.) Klaus Banse (ESO) David Barnes (ATNF/CSIRO) +Zaak Beekman (Homebrew maintainer for MacOSX) David Berry (STARLINK & JAC) Emmanuel Bertin (IAP) +David Binderman +Erik M. Bray (STScI) Jeremy Brewer (U. Pittsburgh) Wim Brouw (ATNF/CSIRO) +Stefan Brüns Peter Bunclark (IoA, U. Cambridge) +Mihai Cara (STScI/Astropy) +Rodrigo Tobar Carrizo (ICRAR/UWA) Pan Chai (GSFC/NASA) Charles Copley +Simon Conseil (CRAL) Neil Crighton Cesar Enrique Garcia Dabo (ESO) +Sepideh Eskandarlou (CEFCA & GNUastro) +Marc Espie Lindsey Davis (NOAO) +Nadezhda (Nadia) Dencheva (STScI/Astropy) Ger van Diepen (ASTRON) Patrick Dowler (CADC/NRC) Michael Droettboom (STScI) Rick Ebert (IPAC/NASA) Ken Ebisawa (GSFC/NASA) Sébastien Fabbro (Gentoo linux maintainer) +Octavi Fors (U. North Carolina) Bob Garwood (NRAO) +Mosč Giordano (Julia wrappers) Brian Glendenning (NRAO) Eric Greisen (NRAO) Michael Halle (AM/Harvard) Booth Hartley (IPAC/NASA) Phil Hodge (STScI) +Derek Homeier (Astropy) Bryan Irby (GSFC/NASA) Justin Jonas (Rhodes U.) Yves Jung (ESO) David Kaplan (KITP/UCSB) +Vishal Kasliwal (U. Pennsylvania, LSST) Daniel S. Katz (JPL/NASA) Neil Killeen (ATNF/CSIRO) David King (NRAO) +Martin Kuemmel (U.-Sternwarte Muenchen) Paul F. Kunz (SLAC/Stanford U.) +Aleksander Kurek (Jagiellonian U.) Jonas Møller Larsen (ESO) +Dustin Lang (Perimeter Inst.) +Paddy Leahy (U. Manchester) Jim Lewis (IoA, U. Cambridge) +Pey-Lian Lim (STScI) Marco Lombardi (ESO) Lars Kristian Lundin (ESO) Robert Lupton (Princeton U.) Craig Markwardt (GSFC/NASA) +Chiara Marmo (U. Paris-Sud) Malte Marquarding (ATNF/CSIRO) +Jean-Baptiste Marquette (IAP) Tom Marsh (U. Warwick) Sean Mattingly (IPAC/NASA) Dave McConnell (ATNF/CSIRO) Thomas A. McGlynn (GSFC/NASA) +Bruce Merry (SARAO) Michelle Miller (NOAO) -Doug Mink (CfA) +Jessica Mink (CfA) +David Motl (var.astro.cz) August Muench (CfA) Fergal Mullally (Princeton U.) +Stuart Mumford (SunPy) +Shu Niu (Purple Mountain Obs.) Clive Page (U. Leicester) -Sergio Pascual (U. Complutense de Madrid) -Bill Pence (NASA/GSFC) +Ralf Palsa (ESO) +Sergio Pascual (U. Complutense de Madrid, Fedora maintainer) +Bill Pence (GSFC/NASA) Olivier Perdereau (LAL/IN2P3) Dirk Petry (ESO) Ray Plante (NCSA/UIUC) +Paul Price (Princeton U.) Niruj Mohan Ramanujam (Leiden Obs) Harold Ravlin (U. Illinois) +Cyril Richard (CNRS) +Thomas Robitaille (MPIA, STScI) Boud Roukema (TCfA) Keith A. Scollick (GSFC/NASA) Arno Schoenmakers (ASTRON) +Pim Schellart (Princeton U.) +Corentin Schreiber (Oxford U.) +Eli Schwartz (Gentoo Linux maintainer) +Michael Seifert (Astropy) +Manodeep Sinha (Astropy) +Colin Slater (LSST) Hanno Spreeuw (ASTRON) Ole Streicher (Debian maintainer) +Julian Taylor (ESO) Hans Terlouw (Kapteyn, Groningen) Peter Teuben (U. Maryland) +Bill Thompson (GSFC/NASA) Harro Verkouter (JIVE) John C. Vernaleo (GSFC/NASA) Martin Vogelaar (Kapteyn, Groningen) Stephen Walton (CSUN) -Boyd Waters NRAO) +Boyd Waters (NRAO) Randall Wayth (Curtin U.) +Benjamin Alan Weaver (LBL) +Peter Weilbacher (AIP) Matthew Whiting (ATNF/CSIRO) -Peter Williams (UCB) +Peter Williams (UCB, CfA) Daren Scot Wilson (NRAO) Tony Wong (ATNF/CSIRO) -$Id: THANKS,v 4.14 2012/07/13 10:02:44 cal103 Exp $ +$Id: THANKS,v 8.6 2026/03/29 13:53:57 mcalabre Exp $ diff --git a/cextern/wcslib/VALIDATION b/cextern/wcslib/VALIDATION index d8ac1fc7d355..53f3a62cb45d 100644 --- a/cextern/wcslib/VALIDATION +++ b/cextern/wcslib/VALIDATION @@ -1,4 +1,607 @@ -Platforms on which the installation procedures and test suite were exercised +Platforms on which the installation procedures and test suite were exercised. + +WCSLIB version 8.6 (2026/03/30) +------------------------------- + +* Dell Latitude XPS 15 9560 (Intel Core i7-7700HQ, 4 cores, 8 CPUs, x86_64) + KDE Neon User Edition 6.5.2 over Ubuntu 24.04 (Noble Numbat) + uname -r (kernel version): 6.14.0-36-generic + gcc --version: gcc (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0 + gfortran --version: GNU Fortran (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0 + +WCSLIB version 8.5 (2025/12/06) +------------------------------- + +* Dell Latitude XPS 15 9560 (Intel Core i7-7700HQ, 4 cores, 8 CPUs, x86_64) + KDE Neon User Edition 6.5.2 over Ubuntu 24.04 (Noble Numbat) + uname -r (kernel version): 6.14.0-36-generic + gcc --version: gcc (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0 + gfortran --version: GNU Fortran (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0 + +WCSLIB version 8.4 (2024/10/29) +------------------------------- + +* Dell Latitude XPS 15 9560 (Intel Core i7-7700HQ, 4 cores, 8 CPUs, x86_64) + KDE Neon User Edition 6.2 over Ubuntu 22.04 (Jammy Jellyfish) + uname -r (kernel version): 6.8.0-45-generic + gcc --version: gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0 + gfortran --version: GNU Fortran (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0 + +WCSLIB version 8.3 (2024/05/14) +------------------------------- + +* Dell Latitude XPS 15 9560 (Intel Core i7-7700HQ, 4 cores, 8 CPUs, x86_64) + KDE Neon User Edition 6.0 over Ubuntu 22.04 (Jammy Jellyfish) + uname -r (kernel version): 6.5.0-28-generic + gcc --version: gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0 + gfortran --version: GNU Fortran (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0 + + and + + gcc-12 --version: gcc-12 (Ubuntu 12.3.0-1ubuntu1~22.04) 12.3.0 + gfortran-12 --version: GNU Fortran (Ubuntu 12.3.0-1ubuntu1~22.04) 12.3.0 + +WCSLIB version 8.2.2 (2023/11/29) +--------------------------------- + +* Dell Latitude XPS 15 9560 (Intel Core i7-7700HQ, 4 cores, 8 CPUs, x86_64) + KDE Neon User Edition 5.27 over Ubuntu 22.04 (Jammy Jellyfish) + uname -r (kernel version): 5.19.0-46-generic + gcc --version: gcc (Ubuntu 11.3.0-1ubuntu1~22.04.1) 11.3.0 + gfortran --version: GNU Fortran (Ubuntu 11.3.0-1ubuntu1~22.04.1) 11.3.0 + +WCSLIB version 8.2 (2023/11/16) +------------------------------- + +* Dell Latitude E6530 (Intel Core i7-3740QM, 4 cores, 8 CPUs, x86_64) + KDE Neon User Edition 5.27 over Ubuntu 22.04 (Jammy Jellyfish) + uname -r (kernel version): 5.19.0-38-generic + gcc --version: gcc (Ubuntu 11.3.0-1ubuntu1~22.04) 11.3.0 + gfortran --version: GNU Fortran (Ubuntu 11.3.0-1ubuntu1~22.04) 11.3.0 + +WCSLIB version 8.1 (2023/07/06) +------------------------------- + +* Dell Latitude XPS 15 9560 (Intel Core i7-7700HQ, 4 cores, 8 CPUs, x86_64) + KDE Neon User Edition 5.27 over Ubuntu 22.04 (Jammy Jellyfish) + uname -r (kernel version): 5.19.0-40-generic + gcc --version: gcc (Ubuntu 11.3.0-1ubuntu1~22.04.1) 11.3.0 + gfortran --version: GNU Fortran (Ubuntu 11.3.0-1ubuntu1~22.04.1) 11.3.0 + + and + + gcc-12 --version: gcc-12 (Ubuntu 12.1.0-2ubuntu1~22.04) 12.1.0 + gfortran-12 --version: GNU Fortran (Ubuntu 12.1.0-2ubuntu1~22.04) 12.1.0 + + +WCSLIB version 8.0 beta (2023/07/01) +------------------------------------ + +* Dell Latitude XPS 15 9560 (Intel Core i7-7700HQ, 4 cores, 8 CPUs, x86_64) + KDE Neon User Edition 5.27 over Ubuntu 22.04 (Jammy Jellyfish) + uname -r (kernel version): 5.19.0-40-generic + gcc --version: gcc (Ubuntu 11.3.0-1ubuntu1~22.04.1) 11.3.0 + gfortran --version: GNU Fortran (Ubuntu 11.3.0-1ubuntu1~22.04.1) 11.3.0 + + +WCSLIB version 7.13 (2022/10/07) +-------------------------------- + +* Dell Latitude XPS 15 9560 (Intel Core i7-7700HQ, 4 cores, 8 CPUs, x86_64) + KDE Neon User Edition 5.25 over Ubuntu 20.04 (Focal Fossa) + uname -r (kernel version): 5.4.0-125-generic + gcc --version: gcc (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0 + gfortran --version: GNU Fortran (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0 + + +WCSLIB version 7.12 (2022/09/09) +-------------------------------- + +* Dell Latitude XPS 15 9560 (Intel Core i7-7700HQ, 4 cores, 8 CPUs, x86_64) + KDE Neon User Edition 5.25 over Ubuntu 20.04 (Focal Fossa) + uname -r (kernel version): 5.4.0-122-generic + gcc --version: gcc (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0 + gfortran --version: GNU Fortran (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0 + + +WCSLIB version 7.11 (2022/04/26) +-------------------------------- + +* Dell Latitude XPS 15 9560 (Intel Core i7-7700HQ, 4 cores, 8 CPUs, x86_64) + KDE Neon User Edition 5.24 over Ubuntu 20.04 (Focal Fossa) + uname -r (kernel version): 5.4.0-107-generic + gcc --version: gcc (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0 + gfortran --version: GNU Fortran (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0 + + +WCSLIB version 7.10 (2022/04/24) +-------------------------------- + +* Dell Latitude XPS 15 9560 (Intel Core i7-7700HQ, 4 cores, 8 CPUs, x86_64) + KDE Neon User Edition 5.24 over Ubuntu 20.04 (Focal Fossa) + uname -r (kernel version): 5.4.0-107-generic + gcc --version: gcc (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0 + gfortran --version: GNU Fortran (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0 + + +WCSLIB version 7.9 (2022/03/26) +------------------------------- + +* Dell Latitude XPS 15 9560 (Intel Core i7-7700HQ, 4 cores, 8 CPUs, x86_64) + KDE Neon User Edition 5.23 over Ubuntu 20.04 (Focal Fossa) + uname -r (kernel version): 5.4.0-77-generic + gcc --version: gcc (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0 + gfortran --version: GNU Fortran (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0 + + +WCSLIB version 7.8 (2022/03/25) +------------------------------- + +* Dell Latitude XPS 15 9560 (Intel Core i7-7700HQ, 4 cores, 8 CPUs, x86_64) + KDE Neon User Edition 5.23 over Ubuntu 20.04 (Focal Fossa) + uname -r (kernel version): 5.4.0-77-generic + gcc --version: gcc (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0 + gfortran --version: GNU Fortran (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0 + + and + + gcc --version: gcc (GCC) 11.1.0 (local build) + gfortran --version: GNU Fortran (GCC) 11.1.0 (local build) + + +WCSLIB version 7.7 (2021/07/12) +------------------------------- + +* Dell Latitude XPS 15 9560 (Intel Core i7-7700HQ, 4 cores, 8 CPUs, x86_64) + KDE Neon User Edition 5.22 over Ubuntu 20.04 (Focal Fossa) + uname -r (kernel version): 5.4.0-77-generic + gcc --version: gcc (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0 + gfortran --version: GNU Fortran (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0 + + and + + gcc --version: gcc (GCC) 11.1.0 (local build) + gfortran --version: GNU Fortran (GCC) 11.1.0 (local build) + + +WCSLIB version 7.6 (2021/04/13) +------------------------------- + +* Dell Latitude E6530 (Intel Core i7-3740QM, 4 cores, 8 processors, x86_64) + KDE Neon User Edition 5.21 over Ubuntu 20.04 (Focal Fossa) + uname -r (kernel version): 5.4.0-67-generic + gcc --version: gcc (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0 + gfortran --version: GNU Fortran (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0 + + +WCSLIB version 7.5 (2021/03/20) +------------------------------- + +* Dell Latitude XPS 15 9560 (Intel Core i7-7700HQ, 4 cores, 8 CPUs, x86_64) + KDE Neon User Edition 5.20 over Ubuntu 20.04 (Focal Fossa) + uname -r (kernel version): 5.4.0-62-generic + gcc --version: gcc (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0 + gfortran --version: GNU Fortran (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0 + + +WCSLIB version 7.4 (2021/01/31) +------------------------------- + +* Dell Latitude XPS 15 9560 (Intel Core i7-7700HQ, 4 cores, 8 CPUs, x86_64) + KDE Neon User Edition 5.20 over Ubuntu 20.04 (Focal Fossa) + uname -r (kernel version): 5.4.0-62-generic + gcc --version: gcc (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0 + gfortran --version: GNU Fortran (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0 + + +WCSLIB version 7.3.1 (2020/08/17) +--------------------------------- + +* Dell Latitude XPS 15 9560 (Intel Core i7-7700HQ, 4 cores, 8 CPUs, x86_64) + KDE Neon User Edition 5.19 over Ubuntu 18.04 (Bionic Beaver) + uname -r (kernel version): 4.15.0-112-generic + gcc --version: gcc (GCC) 9.2.0 (local build) + gfortran --version: GNU Fortran (GCC) 9.2.0 (local build) + + +WCSLIB version 7.3 (2020/06/03) +------------------------------- + +* Dell Latitude XPS 15 9560 (Intel Core i7-7700HQ, 4 cores, 8 CPUs, x86_64) + KDE Neon User Edition 5.18 over Ubuntu 18.04 (Bionic Beaver) + uname -r (kernel version): 4.15.0-88-generic + gcc --version: gcc (GCC) 9.2.0 (local build) + gfortran --version: GNU Fortran (GCC) 9.2.0 (local build) + + +WCSLIB version 7.2 (2020/03/09) +------------------------------- + +* Dell Latitude XPS 15 9560 (Intel Core i7-7700HQ, 4 cores, 8 CPUs, x86_64) + KDE Neon User Edition 5.18 over Ubuntu 18.04 (Bionic Beaver) + uname -r (kernel version): 4.15.0-88-generic + gcc --version: gcc (GCC) 9.2.0 (local build) + gfortran --version: GNU Fortran (GCC) 9.2.0 (local build) + + +WCSLIB version 7.1 (2020/01/01) +------------------------------- + +* Dell Latitude XPS 15 9560 (Intel Core i7-7700HQ, 4 cores, 8 CPUs, x86_64) + KDE Neon User Edition 5.17 over Ubuntu 18.04 (Bionic Beaver) + uname -r (kernel version): 4.15.0-70-generic + gcc --version: gcc (GCC) 9.2.0 (local build) + gfortran --version: GNU Fortran (GCC) 9.2.0 (local build) + +* Dell Latitude D610 (Intel Pentium M, 1 processor, i686) + Debian linux 4.0 (etch) + uname -r (kernel version): 2.6.18-6-686 + gcc --version: gcc (GCC) 4.1.2 20061115 (prerelease) (Debian 4.1.1-21) + gfortran --version: GNU Fortran 95 (GCC) 4.1.2 20061115 (prerelease) + (Debian 4.1.1-21) + + +WCSLIB version 6.3 (2019/07/12) +------------------------------- + +* Dell Latitude XPS 15 9560 (Intel Core i7-7700HQ, 4 cores, 8 CPUs, x86_64) + KDE Neon User Edition 5.15 over Ubuntu 18.04 (Bionic Beaver) + uname -r (kernel version): 4.15.0-50-generic + gcc-8 --version: gcc-8 (Ubuntu 8.3.0-6ubuntu1~18.04.1) 8.3.0 + gfortran-8 --version: GNU Fortran (Ubuntu 8.3.0-6ubuntu1~18.04.1) 8.3.0 + + +WCSLIB version 6.1 (2018/10/19) +------------------------------- + +* Dell Latitude XPS 15 9560 (Intel Core i7-7700HQ, 4 cores, 8 CPUs, x86_64) + KDE Neon User Edition 5.13 over Ubuntu 18.04 (Bionic Beaver) + uname -r (kernel version): 4.15.0-29-generic + gcc --version: gcc (Ubuntu 7.3.0-16ubuntu3) 7.3.0 + gfortran --version: GNU Fortran (Ubuntu 7.3.0-16ubuntu3) 7.3.0 + +* Dell Latitude D610 (Intel Pentium M, 1 processor, i686) + Debian linux 4.0 (etch) + uname -r (kernel version): 2.6.18-6-686 + gcc --version: gcc (GCC) 4.1.2 20061115 (prerelease) (Debian 4.1.1-21) + gfortran --version: GNU Fortran 95 (GCC) 4.1.2 20061115 (prerelease) + (Debian 4.1.1-21) + + +WCSLIB version 5.20 (2018/10/05) +-------------------------------- + +* Dell Latitude XPS 15 9560 (Intel Core i7-7700HQ, 4 cores, 8 CPUs, x86_64) + KDE Neon User Edition 5.13 over Ubuntu 18.04 (Bionic Beaver) + uname -r (kernel version): 4.15.0-29-generic + gcc --version: gcc (Ubuntu 7.3.0-16ubuntu3) 7.3.0 + gfortran --version: GNU Fortran (Ubuntu 7.3.0-16ubuntu3) 7.3.0 + + +WCSLIB version 5.19 (2018/07/27) +-------------------------------- + +* Dell Latitude E6530 (Intel Core i7-3740QM, 4 cores, 8 processors, x86_64) + Debian linux 8.10 (jessie) + uname -r (kernel version): 3.16.0-4-amd64 + gcc --version: gcc-8.1.0 (GCC) 8.1.0 (local build) + gfortran --version: GNU Fortran (GCC) 8.1.0 (local build) + + and + + gcc --version: gcc (Debian 4.9.2-10) 4.9.2 + gfortran --version: GNU Fortran (Debian 4.9.2-10) 4.9.2 + + +WCSLIB version 5.18 (2018/01/10) +-------------------------------- + +* Dell Latitude XPS 15 9560 (Intel Core i7-7700HQ, 4 cores, 8 CPUs, x86_64) + KDE Neon User Edition 5.11 over Ubuntu 16.04 (Xenial Xerus) + uname -r (kernel version): 4.10.0-40-generic + gcc --version: gcc (Ubuntu 5.4.0-6ubuntu1~16.04.5) 5.4.0 20160609 + gfortran --version: GNU Fortran (Ubuntu 5.4.0-6ubuntu1~16.04.5) 5.4.0 20160609 + + +WCSLIB version 5.17 (2017/09/18) +-------------------------------- + +* Dell Latitude E6530 (Intel Core i7-3740QM, 4 cores, 8 processors, x86_64) + Debian linux 8.9 (jessie) + uname -r (kernel version): 3.16.0-4-amd64 + gcc --version: gcc (Debian 4.9.2-10) 4.9.2 + gfortran --version: GNU Fortran (Debian 4.9.2-10) 4.9.2 + + +WCSLIB version 5.16 (2017/01/15) +-------------------------------- + +* Dell Latitude D620 (Intel Centrino T2300, 2 processors, i686) + Debian linux 8.6 (jessie) + uname -r (kernel version): 3.16.0-4-686-pae + gcc --version: gcc (Debian 4.9.2-10) 4.9.2 + gfortran --version: GNU Fortran (Debian 4.9.2-10) 4.9.2 + + +WCSLIB version 5.15 (2016/04/05) +-------------------------------- + +* Dell Latitude D620 (Intel Centrino T2300, 2 processors, i686) + Debian linux 8.3 (jessie) + uname -r (kernel version): 3.16.0-4-686-pae + gcc --version: gcc (Debian 4.9.2-10) 4.9.2 + gfortran --version: GNU Fortran (Debian 4.9.2-10) 4.9.2 + + +WCSLIB version 5.14 (2016/02/07) +-------------------------------- + +* Dell Latitude D620 (Intel Centrino T2300, 2 processors, i686) + Debian linux 8.3 (jessie) + uname -r (kernel version): 3.16.0-4-686-pae + gcc --version: gcc (Debian 4.9.2-10) 4.9.2 + gfortran --version: GNU Fortran (Debian 4.9.2-10) 4.9.2 + + +WCSLIB version 5.13 (2016/01/26) +-------------------------------- + +* Dell Latitude D620 (Intel Centrino T2300, 2 processors, i686) + Debian linux 8.3 (jessie) + uname -r (kernel version): 3.16.0-4-686-pae + gcc --version: gcc (Debian 4.9.2-10) 4.9.2 + gfortran --version: GNU Fortran (Debian 4.9.2-10) 4.9.2 + + +WCSLIB version 5.12 (2015/11/15) +-------------------------------- + +* Dell Latitude D620 (Intel Centrino T2300, 2 processors, i686) + Debian linux 8.0 (jessie) + uname -r (kernel version): 3.16.0-4-686-pae + gcc --version: gcc (Debian 4.9.2-10) 4.9.2 + gfortran --version: GNU Fortran (Debian 4.9.2-10) 4.9.2 + + +WCSLIB version 5.11 (2015/10/18) +-------------------------------- + +* Dell Latitude D620 (Intel Centrino T2300, 2 processors, i686) + Debian linux 8.0 (jessie) + uname -r (kernel version): 3.16.0-4-686-pae + gcc --version: gcc (Debian 4.9.2-10) 4.9.2 + gfortran --version: GNU Fortran (Debian 4.9.2-10) 4.9.2 + + +WCSLIB version 5.10 (2015/10/09) +-------------------------------- + +* Dell Latitude D620 (Intel Centrino T2300, 2 processors, i686) + Debian linux 8.0 (jessie) + uname -r (kernel version): 3.16.0-4-686-pae + gcc --version: gcc (Debian 4.9.2-10) 4.9.2 + gfortran --version: GNU Fortran (Debian 4.9.2-10) 4.9.2 + + +WCSLIB version 5.9 (2015/07/21) +------------------------------- + +* Dell Latitude D620 (Intel Centrino T2300, 2 processors, i686) + Debian linux 8.0 (jessie) + uname -r (kernel version): 3.16.0-4-686-pae + gcc --version: gcc (Debian 4.9.2-10) 4.9.2 + gfortran --version: GNU Fortran (Debian 4.9.2-10) 4.9.2 + + +WCSLIB version 5.8 (2015/07/08) +------------------------------- + +* Dell Latitude D620 (Intel Centrino T2300, 2 processors, i686) + Debian linux 8.0 (jessie) + uname -r (kernel version): 3.16.0-4-686-pae + gcc --version: gcc (Debian 4.9.2-10) 4.9.2 + gfortran --version: GNU Fortran (Debian 4.9.2-10) 4.9.2 + + +WCSLIB version 5.7 (2015/06/29) +------------------------------- + +* Dell Latitude D620 (Intel Centrino T2300, 2 processors, i686) + Debian linux 8.0 (jessie) + uname -r (kernel version): 3.16.0-4-686-pae + gcc --version: gcc (Debian 4.9.2-10) 4.9.2 + gfortran --version: GNU Fortran (Debian 4.9.2-10) 4.9.2 + + +WCSLIB version 5.6 (2015/06/14) +------------------------------- + +* Dell Latitude D620 (Intel Centrino T2300, 2 processors, i686) + Debian linux 8.0 (jessie) + uname -r (kernel version): 3.16.0-4-686-pae + gcc --version: gcc (Debian 4.9.2-10) 4.9.2 + gfortran --version: GNU Fortran (Debian 4.9.2-10) 4.9.2 + + +WCSLIB version 5.5 (2015/05/05) +------------------------------- + +* Dell Latitude D620 (Intel Centrino T2300, 2 processors, i686) + Debian linux 7.8 (wheezy) + uname -r (kernel version): 3.2.0-4-686-pae + gcc --version: gcc (Debian 4.7.2-5) 4.7.2 + gfortran --version: GNU Fortran (Debian 4.7.2-5) 4.7.2 + + +WCSLIB version 5.4 (2015/04/21) +------------------------------- + +* Dell Latitude D620 (Intel Centrino T2300, 2 processors, i686) + Debian linux 7.8 (wheezy) + uname -r (kernel version): 3.2.0-4-686-pae + gcc --version: gcc (Debian 4.7.2-5) 4.7.2 + gfortran --version: GNU Fortran (Debian 4.7.2-5) 4.7.2 + + +WCSLIB version 5.3 (2015/04/21) +------------------------------- + +* Dell Latitude D620 (Intel Centrino T2300, 2 processors, i686) + Debian linux 7.8 (wheezy) + uname -r (kernel version): 3.2.0-4-686-pae + gcc --version: gcc (Debian 4.7.2-5) 4.7.2 + gfortran --version: GNU Fortran (Debian 4.7.2-5) 4.7.2 + + +WCSLIB version 5.2 beta release (2015/04/15) +-------------------------------------------- + +* Dell Latitude D620 (Intel Centrino T2300, 2 processors, i686) + Debian linux 7.8 (wheezy) + uname -r (kernel version): 3.2.0-4-686-pae + gcc --version: gcc (Debian 4.7.2-5) 4.7.2 + gfortran --version: GNU Fortran (Debian 4.7.2-5) 4.7.2 + +* Dell PowerEdge R710 (Intel Xeon E5530, 8 processors, amd64) + Debian linux 7.8 (wheezy) + uname -r (kernel version): 3.2.0-4-amd64 + gcc --version: gcc (Debian 4.7.2-5) 4.7.2 + gfortran --version: GNU Fortran (Debian 4.7.2-5) 4.7.2 + (Non-graphical tests only.) + +* Mac Mini (Intel Core i7, 4 cores, x86_64) + MacOSX 10.9.5 (13F1066) + uname -r (kernel version): Darwin 13.4.0 + gcc --version: gcc (GCC) 4.8.3 + gfortran --version: GNU Fortran (GCC) 4.8.3 + (Non-graphical tests only.) + + +WCSLIB version 5.1 beta release (2015/04/07) +-------------------------------------------- + +* Dell Latitude D620 (Intel Centrino T2300, 2 processors, i686) + Debian linux 7.8 (wheezy) + uname -r (kernel version): 3.2.0-4-686-pae + gcc --version: gcc (Debian 4.7.2-5) 4.7.2 + gfortran --version: GNU Fortran (Debian 4.7.2-5) 4.7.2 + + +WCSLIB version 5.0 beta release (2015/04/05) +-------------------------------------------- + +* Dell Latitude D620 (Intel Centrino T2300, 2 processors, i686) + Debian linux 7.8 (wheezy) + uname -r (kernel version): 3.2.0-4-686-pae + gcc --version: gcc (Debian 4.7.2-5) 4.7.2 + gfortran --version: GNU Fortran (Debian 4.7.2-5) 4.7.2 + +* Dell PowerEdge R710 (Intel Xeon E5530, 8 processors, amd64) + Debian linux 7.8 (wheezy) + uname -r (kernel version): 3.2.0-4-amd64 + gcc --version: gcc (Debian 4.7.2-5) 4.7.2 + gfortran --version: GNU Fortran (Debian 4.7.2-5) 4.7.2 + (Non-graphical tests only.) + +* Dell PowerEdge R820 (Intel Xeon E5-4620, 32 processors, amd64) + Debian linux 6.0.10 (squeeze) + uname -r (kernel version): 3.2.0-0.bpo.4-amd64 + gcc --version: gcc (Debian 4.4.5-8) 4.4.5 + gfortran --version: GNU Fortran (Debian 4.4.5-8) 4.4.5 + (Non-graphical tests only.) + +* Mac Mini (Intel Core i7, 4 cores, x86_64) + MacOSX 10.9.5 (13F1066) + uname -r (kernel version): Darwin 13.4.0 + gcc --version: gcc (GCC) 4.8.3 + gfortran --version: GNU Fortran (GCC) 4.8.3 + (Non-graphical tests only.) + + +WCSLIB version 4.23 (2014/05/11) +-------------------------------- + +* Dell Latitude D620 (Intel Centrino, i686) running Debian linux 7.0 (wheezy) + uname -r (kernel version): 3.2.0-4-686-pae + gcc --version: gcc (Debian 4.7.2-5) 4.7.2 + gfortran --version: GNU Fortran (Debian 4.7.2-5) 4.7.2 + + +WCSLIB version 4.22 (2014/04/13) +-------------------------------- + +* Dell Latitude D620 (Intel Centrino, i686) running Debian linux 7.0 (wheezy) + uname -r (kernel version): 3.2.0-4-686-pae + gcc --version: gcc (Debian 4.7.2-5) 4.7.2 + gfortran --version: GNU Fortran (Debian 4.7.2-5) 4.7.2 + + +* Dell PowerEdge R710 (Intel Xeon, x86_64) running Debian linux 6.0.9 (squeeze) + uname -r (kernel version): 2.6.32-5-amd64 + gcc --version: gcc (Debian 4.4.5-8) 4.4.5 + gfortran --version: GNU Fortran (Debian 4.4.5-8) 4.4.5 + + +* Mac Mini (Intel Core 2 Duo) running MacOSX 10.6.8 (10K549) + uname -r (kernel version): 10.8.0 + gcc --version: i686-apple-darwin10-gcc-4.2.1 (GCC) 4.2.1 + (Apple Inc. build 5666) (dot 3) + gfortran --version: GNU Fortran (GCC) 4.5.0 20100107 (experimental) + (Non-graphics tests only.) + + +WCSLIB version 4.21 (2014/03/24) +-------------------------------- + +* Dell Latitude D620 (Intel Centrino, i686) running Debian linux 7.0 (wheezy) + uname -r (kernel version): 3.2.0-4-686-pae + gcc --version: gcc (Debian 4.7.2-5) 4.7.2 + gfortran --version: GNU Fortran (Debian 4.7.2-5) 4.7.2 + + +WCSLIB version 4.20 (2013/12/18) +-------------------------------- + +* Dell Latitude D620 (Intel Centrino, i686) running Debian linux 7.0 (wheezy) + uname -r (kernel version): 3.2.0-4-686-pae + gcc --version: gcc (Debian 4.7.2-5) 4.7.2 + gfortran --version: GNU Fortran (Debian 4.7.2-5) 4.7.2 + + +WCSLIB version 4.19 (2013/09/30) +-------------------------------- + +* Dell Latitude D620 (Intel Centrino, i686) running Debian linux 7.0 (wheezy) + uname -r (kernel version): 3.2.0-4-686-pae + gcc --version: gcc (Debian 4.7.2-5) 4.7.2 + gfortran --version: GNU Fortran (Debian 4.7.2-5) 4.7.2 + + +WCSLIB version 4.18 (2013/07/12) +-------------------------------- + +* Dell Latitude D620 (Intel Centrino, i686) running Debian linux 7.0 (wheezy) + uname -r (kernel version): 3.2.0-4-686-pae + gcc --version: gcc (Debian 4.7.2-5) 4.7.2 + gfortran --version: GNU Fortran (Debian 4.7.2-5) 4.7.2 + + +WCSLIB version 4.17 (2013/01/29) +-------------------------------- + +* Dell Latitude D620 (Intel Centrino, i686) running Debian linux 5.0.9 (lenny) + uname -r (kernel version): 2.6.26-2-686 + gcc --version: gcc (Debian 4.3.2-1.1) 4.3.2 + gfortran --version: GNU Fortran (Debian 4.3.2-1.1) 4.3.2 + + +WCSLIB version 4.15 (2012/09/26) +-------------------------------- + +* Dell Latitude D620 (Intel Centrino, i686) running Debian linux 5.0.9 (lenny) + uname -r (kernel version): 2.6.26-2-686 + gcc --version: gcc (Debian 4.3.2-1.1) 4.3.2 + gfortran --version: GNU Fortran (Debian 4.3.2-1.1) 4.3.2 WCSLIB version 4.14 (2012/07/13) @@ -59,7 +662,7 @@ WCSLIB version 4.8 (2011/08/15) gfortran --version: GNU Fortran (Debian 4.3.2-1.1) 4.3.2 -* Marvell SheevaPlug (Feroceon 88FR131 rev 1 ARM v5L), Debian linux 6.0 +* Marvell SheevaPlug (Feroceon 88FR131 rev 1 ARM v5L), Debian linux 6.0 (squeeze) uname -r (kernel version): 2.6.32-5-kirkwood gcc --version: gcc (Debian 4.4.5-8) 4.4.5 @@ -227,4 +830,4 @@ WCSLIB version 4.4 (2009/08/06) 2004/04/23 ------------------------------------------------------------------------------ -$Id: VALIDATION,v 4.14 2012/07/13 10:02:44 cal103 Exp $ +$Id: VALIDATION,v 8.6 2026/03/29 13:53:57 mcalabre Exp $ diff --git a/cextern/wcslib/config/ax_pthread.m4 b/cextern/wcslib/config/ax_pthread.m4 new file mode 100644 index 000000000000..9f35d139149f --- /dev/null +++ b/cextern/wcslib/config/ax_pthread.m4 @@ -0,0 +1,522 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_pthread.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) +# +# DESCRIPTION +# +# This macro figures out how to build C programs using POSIX threads. It +# sets the PTHREAD_LIBS output variable to the threads library and linker +# flags, and the PTHREAD_CFLAGS output variable to any special C compiler +# flags that are needed. (The user can also force certain compiler +# flags/libs to be tested by setting these environment variables.) +# +# Also sets PTHREAD_CC and PTHREAD_CXX to any special C compiler that is +# needed for multi-threaded programs (defaults to the value of CC +# respectively CXX otherwise). (This is necessary on e.g. AIX to use the +# special cc_r/CC_r compiler alias.) +# +# NOTE: You are assumed to not only compile your program with these flags, +# but also to link with them as well. For example, you might link with +# $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS +# $PTHREAD_CXX $CXXFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS +# +# If you are only building threaded programs, you may wish to use these +# variables in your default LIBS, CFLAGS, and CC: +# +# LIBS="$PTHREAD_LIBS $LIBS" +# CFLAGS="$CFLAGS $PTHREAD_CFLAGS" +# CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS" +# CC="$PTHREAD_CC" +# CXX="$PTHREAD_CXX" +# +# In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant +# has a nonstandard name, this macro defines PTHREAD_CREATE_JOINABLE to +# that name (e.g. PTHREAD_CREATE_UNDETACHED on AIX). +# +# Also HAVE_PTHREAD_PRIO_INHERIT is defined if pthread is found and the +# PTHREAD_PRIO_INHERIT symbol is defined when compiling with +# PTHREAD_CFLAGS. +# +# ACTION-IF-FOUND is a list of shell commands to run if a threads library +# is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it +# is not found. If ACTION-IF-FOUND is not specified, the default action +# will define HAVE_PTHREAD. +# +# Please let the authors know if this macro fails on any platform, or if +# you have any other suggestions or comments. This macro was based on work +# by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help +# from M. Frigo), as well as ac_pthread and hb_pthread macros posted by +# Alejandro Forero Cuervo to the autoconf macro repository. We are also +# grateful for the helpful feedback of numerous users. +# +# Updated for Autoconf 2.68 by Daniel Richard G. +# +# LICENSE +# +# Copyright (c) 2008 Steven G. Johnson +# Copyright (c) 2011 Daniel Richard G. +# Copyright (c) 2019 Marc Stevens +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 31 + +AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD]) +AC_DEFUN([AX_PTHREAD], [ +AC_REQUIRE([AC_CANONICAL_HOST]) +AC_REQUIRE([AC_PROG_CC]) +AC_REQUIRE([AC_PROG_SED]) +AC_LANG_PUSH([C]) +ax_pthread_ok=no + +# We used to check for pthread.h first, but this fails if pthread.h +# requires special compiler flags (e.g. on Tru64 or Sequent). +# It gets checked for in the link test anyway. + +# First of all, check if the user has set any of the PTHREAD_LIBS, +# etcetera environment variables, and if threads linking works using +# them: +if test "x$PTHREAD_CFLAGS$PTHREAD_LIBS" != "x"; then + ax_pthread_save_CC="$CC" + ax_pthread_save_CFLAGS="$CFLAGS" + ax_pthread_save_LIBS="$LIBS" + AS_IF([test "x$PTHREAD_CC" != "x"], [CC="$PTHREAD_CC"]) + AS_IF([test "x$PTHREAD_CXX" != "x"], [CXX="$PTHREAD_CXX"]) + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" + AC_MSG_CHECKING([for pthread_join using $CC $PTHREAD_CFLAGS $PTHREAD_LIBS]) + AC_LINK_IFELSE([AC_LANG_CALL([], [pthread_join])], [ax_pthread_ok=yes]) + AC_MSG_RESULT([$ax_pthread_ok]) + if test "x$ax_pthread_ok" = "xno"; then + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" + fi + CC="$ax_pthread_save_CC" + CFLAGS="$ax_pthread_save_CFLAGS" + LIBS="$ax_pthread_save_LIBS" +fi + +# We must check for the threads library under a number of different +# names; the ordering is very important because some systems +# (e.g. DEC) have both -lpthread and -lpthreads, where one of the +# libraries is broken (non-POSIX). + +# Create a list of thread flags to try. Items with a "," contain both +# C compiler flags (before ",") and linker flags (after ","). Other items +# starting with a "-" are C compiler flags, and remaining items are +# library names, except for "none" which indicates that we try without +# any flags at all, and "pthread-config" which is a program returning +# the flags for the Pth emulation library. + +ax_pthread_flags="pthreads none -Kthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" + +# The ordering *is* (sometimes) important. Some notes on the +# individual items follow: + +# pthreads: AIX (must check this before -lpthread) +# none: in case threads are in libc; should be tried before -Kthread and +# other compiler flags to prevent continual compiler warnings +# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) +# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads), Tru64 +# (Note: HP C rejects this with "bad form for `-t' option") +# -pthreads: Solaris/gcc (Note: HP C also rejects) +# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it +# doesn't hurt to check since this sometimes defines pthreads and +# -D_REENTRANT too), HP C (must be checked before -lpthread, which +# is present but should not be used directly; and before -mthreads, +# because the compiler interprets this as "-mt" + "-hreads") +# -mthreads: Mingw32/gcc, Lynx/gcc +# pthread: Linux, etcetera +# --thread-safe: KAI C++ +# pthread-config: use pthread-config program (for GNU Pth library) + +case $host_os in + + freebsd*) + + # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) + # lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) + + ax_pthread_flags="-kthread lthread $ax_pthread_flags" + ;; + + hpux*) + + # From the cc(1) man page: "[-mt] Sets various -D flags to enable + # multi-threading and also sets -lpthread." + + ax_pthread_flags="-mt -pthread pthread $ax_pthread_flags" + ;; + + openedition*) + + # IBM z/OS requires a feature-test macro to be defined in order to + # enable POSIX threads at all, so give the user a hint if this is + # not set. (We don't define these ourselves, as they can affect + # other portions of the system API in unpredictable ways.) + + AC_EGREP_CPP([AX_PTHREAD_ZOS_MISSING], + [ +# if !defined(_OPEN_THREADS) && !defined(_UNIX03_THREADS) + AX_PTHREAD_ZOS_MISSING +# endif + ], + [AC_MSG_WARN([IBM z/OS requires -D_OPEN_THREADS or -D_UNIX03_THREADS to enable pthreads support.])]) + ;; + + solaris*) + + # On Solaris (at least, for some versions), libc contains stubbed + # (non-functional) versions of the pthreads routines, so link-based + # tests will erroneously succeed. (N.B.: The stubs are missing + # pthread_cleanup_push, or rather a function called by this macro, + # so we could check for that, but who knows whether they'll stub + # that too in a future libc.) So we'll check first for the + # standard Solaris way of linking pthreads (-mt -lpthread). + + ax_pthread_flags="-mt,-lpthread pthread $ax_pthread_flags" + ;; +esac + +# Are we compiling with Clang? + +AC_CACHE_CHECK([whether $CC is Clang], + [ax_cv_PTHREAD_CLANG], + [ax_cv_PTHREAD_CLANG=no + # Note that Autoconf sets GCC=yes for Clang as well as GCC + if test "x$GCC" = "xyes"; then + AC_EGREP_CPP([AX_PTHREAD_CC_IS_CLANG], + [/* Note: Clang 2.7 lacks __clang_[a-z]+__ */ +# if defined(__clang__) && defined(__llvm__) + AX_PTHREAD_CC_IS_CLANG +# endif + ], + [ax_cv_PTHREAD_CLANG=yes]) + fi + ]) +ax_pthread_clang="$ax_cv_PTHREAD_CLANG" + + +# GCC generally uses -pthread, or -pthreads on some platforms (e.g. SPARC) + +# Note that for GCC and Clang -pthread generally implies -lpthread, +# except when -nostdlib is passed. +# This is problematic using libtool to build C++ shared libraries with pthread: +# [1] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=25460 +# [2] https://bugzilla.redhat.com/show_bug.cgi?id=661333 +# [3] https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=468555 +# To solve this, first try -pthread together with -lpthread for GCC + +AS_IF([test "x$GCC" = "xyes"], + [ax_pthread_flags="-pthread,-lpthread -pthread -pthreads $ax_pthread_flags"]) + +# Clang takes -pthread (never supported any other flag), but we'll try with -lpthread first + +AS_IF([test "x$ax_pthread_clang" = "xyes"], + [ax_pthread_flags="-pthread,-lpthread -pthread"]) + + +# The presence of a feature test macro requesting re-entrant function +# definitions is, on some systems, a strong hint that pthreads support is +# correctly enabled + +case $host_os in + darwin* | hpux* | linux* | osf* | solaris*) + ax_pthread_check_macro="_REENTRANT" + ;; + + aix*) + ax_pthread_check_macro="_THREAD_SAFE" + ;; + + *) + ax_pthread_check_macro="--" + ;; +esac +AS_IF([test "x$ax_pthread_check_macro" = "x--"], + [ax_pthread_check_cond=0], + [ax_pthread_check_cond="!defined($ax_pthread_check_macro)"]) + + +if test "x$ax_pthread_ok" = "xno"; then +for ax_pthread_try_flag in $ax_pthread_flags; do + + case $ax_pthread_try_flag in + none) + AC_MSG_CHECKING([whether pthreads work without any flags]) + ;; + + *,*) + PTHREAD_CFLAGS=`echo $ax_pthread_try_flag | sed "s/^\(.*\),\(.*\)$/\1/"` + PTHREAD_LIBS=`echo $ax_pthread_try_flag | sed "s/^\(.*\),\(.*\)$/\2/"` + AC_MSG_CHECKING([whether pthreads work with "$PTHREAD_CFLAGS" and "$PTHREAD_LIBS"]) + ;; + + -*) + AC_MSG_CHECKING([whether pthreads work with $ax_pthread_try_flag]) + PTHREAD_CFLAGS="$ax_pthread_try_flag" + ;; + + pthread-config) + AC_CHECK_PROG([ax_pthread_config], [pthread-config], [yes], [no]) + AS_IF([test "x$ax_pthread_config" = "xno"], [continue]) + PTHREAD_CFLAGS="`pthread-config --cflags`" + PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" + ;; + + *) + AC_MSG_CHECKING([for the pthreads library -l$ax_pthread_try_flag]) + PTHREAD_LIBS="-l$ax_pthread_try_flag" + ;; + esac + + ax_pthread_save_CFLAGS="$CFLAGS" + ax_pthread_save_LIBS="$LIBS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" + + # Check for various functions. We must include pthread.h, + # since some functions may be macros. (On the Sequent, we + # need a special flag -Kthread to make this header compile.) + # We check for pthread_join because it is in -lpthread on IRIX + # while pthread_create is in libc. We check for pthread_attr_init + # due to DEC craziness with -lpthreads. We check for + # pthread_cleanup_push because it is one of the few pthread + # functions on Solaris that doesn't have a non-functional libc stub. + # We try pthread_create on general principles. + + AC_LINK_IFELSE([AC_LANG_PROGRAM([#include +# if $ax_pthread_check_cond +# error "$ax_pthread_check_macro must be defined" +# endif + static void *some_global = NULL; + static void routine(void *a) + { + /* To avoid any unused-parameter or + unused-but-set-parameter warning. */ + some_global = a; + } + static void *start_routine(void *a) { return a; }], + [pthread_t th; pthread_attr_t attr; + pthread_create(&th, 0, start_routine, 0); + pthread_join(th, 0); + pthread_attr_init(&attr); + pthread_cleanup_push(routine, 0); + pthread_cleanup_pop(0) /* ; */])], + [ax_pthread_ok=yes], + []) + + CFLAGS="$ax_pthread_save_CFLAGS" + LIBS="$ax_pthread_save_LIBS" + + AC_MSG_RESULT([$ax_pthread_ok]) + AS_IF([test "x$ax_pthread_ok" = "xyes"], [break]) + + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" +done +fi + + +# Clang needs special handling, because older versions handle the -pthread +# option in a rather... idiosyncratic way + +if test "x$ax_pthread_clang" = "xyes"; then + + # Clang takes -pthread; it has never supported any other flag + + # (Note 1: This will need to be revisited if a system that Clang + # supports has POSIX threads in a separate library. This tends not + # to be the way of modern systems, but it's conceivable.) + + # (Note 2: On some systems, notably Darwin, -pthread is not needed + # to get POSIX threads support; the API is always present and + # active. We could reasonably leave PTHREAD_CFLAGS empty. But + # -pthread does define _REENTRANT, and while the Darwin headers + # ignore this macro, third-party headers might not.) + + # However, older versions of Clang make a point of warning the user + # that, in an invocation where only linking and no compilation is + # taking place, the -pthread option has no effect ("argument unused + # during compilation"). They expect -pthread to be passed in only + # when source code is being compiled. + # + # Problem is, this is at odds with the way Automake and most other + # C build frameworks function, which is that the same flags used in + # compilation (CFLAGS) are also used in linking. Many systems + # supported by AX_PTHREAD require exactly this for POSIX threads + # support, and in fact it is often not straightforward to specify a + # flag that is used only in the compilation phase and not in + # linking. Such a scenario is extremely rare in practice. + # + # Even though use of the -pthread flag in linking would only print + # a warning, this can be a nuisance for well-run software projects + # that build with -Werror. So if the active version of Clang has + # this misfeature, we search for an option to squash it. + + AC_CACHE_CHECK([whether Clang needs flag to prevent "argument unused" warning when linking with -pthread], + [ax_cv_PTHREAD_CLANG_NO_WARN_FLAG], + [ax_cv_PTHREAD_CLANG_NO_WARN_FLAG=unknown + # Create an alternate version of $ac_link that compiles and + # links in two steps (.c -> .o, .o -> exe) instead of one + # (.c -> exe), because the warning occurs only in the second + # step + ax_pthread_save_ac_link="$ac_link" + ax_pthread_sed='s/conftest\.\$ac_ext/conftest.$ac_objext/g' + ax_pthread_link_step=`AS_ECHO(["$ac_link"]) | sed "$ax_pthread_sed"` + ax_pthread_2step_ac_link="($ac_compile) && (echo ==== >&5) && ($ax_pthread_link_step)" + ax_pthread_save_CFLAGS="$CFLAGS" + for ax_pthread_try in '' -Qunused-arguments -Wno-unused-command-line-argument unknown; do + AS_IF([test "x$ax_pthread_try" = "xunknown"], [break]) + CFLAGS="-Werror -Wunknown-warning-option $ax_pthread_try -pthread $ax_pthread_save_CFLAGS" + ac_link="$ax_pthread_save_ac_link" + AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])], + [ac_link="$ax_pthread_2step_ac_link" + AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])], + [break]) + ]) + done + ac_link="$ax_pthread_save_ac_link" + CFLAGS="$ax_pthread_save_CFLAGS" + AS_IF([test "x$ax_pthread_try" = "x"], [ax_pthread_try=no]) + ax_cv_PTHREAD_CLANG_NO_WARN_FLAG="$ax_pthread_try" + ]) + + case "$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG" in + no | unknown) ;; + *) PTHREAD_CFLAGS="$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG $PTHREAD_CFLAGS" ;; + esac + +fi # $ax_pthread_clang = yes + + + +# Various other checks: +if test "x$ax_pthread_ok" = "xyes"; then + ax_pthread_save_CFLAGS="$CFLAGS" + ax_pthread_save_LIBS="$LIBS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" + + # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. + AC_CACHE_CHECK([for joinable pthread attribute], + [ax_cv_PTHREAD_JOINABLE_ATTR], + [ax_cv_PTHREAD_JOINABLE_ATTR=unknown + for ax_pthread_attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do + AC_LINK_IFELSE([AC_LANG_PROGRAM([#include ], + [int attr = $ax_pthread_attr; return attr /* ; */])], + [ax_cv_PTHREAD_JOINABLE_ATTR=$ax_pthread_attr; break], + []) + done + ]) + AS_IF([test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xunknown" && \ + test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xPTHREAD_CREATE_JOINABLE" && \ + test "x$ax_pthread_joinable_attr_defined" != "xyes"], + [AC_DEFINE_UNQUOTED([PTHREAD_CREATE_JOINABLE], + [$ax_cv_PTHREAD_JOINABLE_ATTR], + [Define to necessary symbol if this constant + uses a non-standard name on your system.]) + ax_pthread_joinable_attr_defined=yes + ]) + + AC_CACHE_CHECK([whether more special flags are required for pthreads], + [ax_cv_PTHREAD_SPECIAL_FLAGS], + [ax_cv_PTHREAD_SPECIAL_FLAGS=no + case $host_os in + solaris*) + ax_cv_PTHREAD_SPECIAL_FLAGS="-D_POSIX_PTHREAD_SEMANTICS" + ;; + esac + ]) + AS_IF([test "x$ax_cv_PTHREAD_SPECIAL_FLAGS" != "xno" && \ + test "x$ax_pthread_special_flags_added" != "xyes"], + [PTHREAD_CFLAGS="$ax_cv_PTHREAD_SPECIAL_FLAGS $PTHREAD_CFLAGS" + ax_pthread_special_flags_added=yes]) + + AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT], + [ax_cv_PTHREAD_PRIO_INHERIT], + [AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]], + [[int i = PTHREAD_PRIO_INHERIT; + return i;]])], + [ax_cv_PTHREAD_PRIO_INHERIT=yes], + [ax_cv_PTHREAD_PRIO_INHERIT=no]) + ]) + AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes" && \ + test "x$ax_pthread_prio_inherit_defined" != "xyes"], + [AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], [1], [Have PTHREAD_PRIO_INHERIT.]) + ax_pthread_prio_inherit_defined=yes + ]) + + CFLAGS="$ax_pthread_save_CFLAGS" + LIBS="$ax_pthread_save_LIBS" + + # More AIX lossage: compile with *_r variant + if test "x$GCC" != "xyes"; then + case $host_os in + aix*) + AS_CASE(["x/$CC"], + [x*/c89|x*/c89_128|x*/c99|x*/c99_128|x*/cc|x*/cc128|x*/xlc|x*/xlc_v6|x*/xlc128|x*/xlc128_v6], + [#handle absolute path differently from PATH based program lookup + AS_CASE(["x$CC"], + [x/*], + [ + AS_IF([AS_EXECUTABLE_P([${CC}_r])],[PTHREAD_CC="${CC}_r"]) + AS_IF([test "x${CXX}" != "x"], [AS_IF([AS_EXECUTABLE_P([${CXX}_r])],[PTHREAD_CXX="${CXX}_r"])]) + ], + [ + AC_CHECK_PROGS([PTHREAD_CC],[${CC}_r],[$CC]) + AS_IF([test "x${CXX}" != "x"], [AC_CHECK_PROGS([PTHREAD_CXX],[${CXX}_r],[$CXX])]) + ] + ) + ]) + ;; + esac + fi +fi + +test -n "$PTHREAD_CC" || PTHREAD_CC="$CC" +test -n "$PTHREAD_CXX" || PTHREAD_CXX="$CXX" + +AC_SUBST([PTHREAD_LIBS]) +AC_SUBST([PTHREAD_CFLAGS]) +AC_SUBST([PTHREAD_CC]) +AC_SUBST([PTHREAD_CXX]) + +# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: +if test "x$ax_pthread_ok" = "xyes"; then + ifelse([$1],,[AC_DEFINE([HAVE_PTHREAD],[1],[Define if you have POSIX threads libraries and header files.])],[$1]) + : +else + ax_pthread_ok=no + $2 +fi +AC_LANG_POP +])dnl AX_PTHREAD diff --git a/cextern/wcslib/config/config.guess b/cextern/wcslib/config/config.guess old mode 100755 new mode 100644 index 43f0cdbcfd2f..ba6af63cc447 --- a/cextern/wcslib/config/config.guess +++ b/cextern/wcslib/config/config.guess @@ -1,14 +1,12 @@ #! /bin/sh # Attempt to guess a canonical system name. -# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, -# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, -# 2011 Free Software Foundation, Inc. +# Copyright 1992-2018 Free Software Foundation, Inc. -timestamp='2011-10-01' +timestamp='2018-07-18' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or +# the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but @@ -17,26 +15,22 @@ timestamp='2011-10-01' # General Public License for more details. # # You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA -# 02110-1301, USA. +# along with this program; if not, see . # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under -# the same distribution terms that you use for the rest of that program. - - -# Originally written by Per Bothner. Please send patches (context -# diff format) to and include a ChangeLog -# entry. +# the same distribution terms that you use for the rest of that +# program. This Exception is an additional permission under section 7 +# of the GNU General Public License, version 3 ("GPLv3"). # -# This script attempts to guess a canonical system name similar to -# config.sub. If it succeeds, it prints the system name on stdout, and -# exits with 0. Otherwise, it exits with 1. +# Originally written by Per Bothner; maintained since 2000 by Ben Elliston. # # You can get the latest version of this script from: -# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD +# https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess +# +# Please send patches to . + me=`echo "$0" | sed -e 's,.*/,,'` @@ -45,7 +39,7 @@ Usage: $0 [OPTION] Output the configuration name of the system \`$me' is run on. -Operation modes: +Options: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit @@ -56,9 +50,7 @@ version="\ GNU config.guess ($timestamp) Originally written by Per Bothner. -Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, -2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free -Software Foundation, Inc. +Copyright 1992-2018 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." @@ -92,8 +84,6 @@ if test $# != 0; then exit 1 fi -trap 'exit 1' 1 2 15 - # CC_FOR_BUILD -- compiler used by this script. Note that the use of a # compiler to aid in system detection is discouraged as it requires # temporary files to be created and, as you can see below, it is a @@ -104,30 +94,35 @@ trap 'exit 1' 1 2 15 # Portable tmp directory creation inspired by the Autoconf team. -set_cc_for_build=' -trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; -trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; -: ${TMPDIR=/tmp} ; - { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || - { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || - { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || - { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; -dummy=$tmp/dummy ; -tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; -case $CC_FOR_BUILD,$HOST_CC,$CC in - ,,) echo "int x;" > $dummy.c ; - for c in cc gcc c89 c99 ; do - if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then - CC_FOR_BUILD="$c"; break ; - fi ; - done ; - if test x"$CC_FOR_BUILD" = x ; then - CC_FOR_BUILD=no_compiler_found ; - fi - ;; - ,,*) CC_FOR_BUILD=$CC ;; - ,*,*) CC_FOR_BUILD=$HOST_CC ;; -esac ; set_cc_for_build= ;' +tmp= +# shellcheck disable=SC2172 +trap 'test -z "$tmp" || rm -fr "$tmp"' 1 2 13 15 +trap 'exitcode=$?; test -z "$tmp" || rm -fr "$tmp"; exit $exitcode' 0 + +set_cc_for_build() { + : "${TMPDIR=/tmp}" + # shellcheck disable=SC2039 + { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || + { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir "$tmp" 2>/dev/null) ; } || + { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir "$tmp" 2>/dev/null) && echo "Warning: creating insecure temp directory" >&2 ; } || + { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } + dummy=$tmp/dummy + case ${CC_FOR_BUILD-},${HOST_CC-},${CC-} in + ,,) echo "int x;" > "$dummy.c" + for driver in cc gcc c89 c99 ; do + if ($driver -c -o "$dummy.o" "$dummy.c") >/dev/null 2>&1 ; then + CC_FOR_BUILD="$driver" + break + fi + done + if test x"$CC_FOR_BUILD" = x ; then + CC_FOR_BUILD=no_compiler_found + fi + ;; + ,,*) CC_FOR_BUILD=$CC ;; + ,*,*) CC_FOR_BUILD=$HOST_CC ;; + esac +} # This is needed to find uname on a Pyramid OSx when run in the BSD universe. # (ghazi@noc.rutgers.edu 1994-08-24) @@ -140,12 +135,40 @@ UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown +case "$UNAME_SYSTEM" in +Linux|GNU|GNU/*) + # If the system lacks a compiler, then just pick glibc. + # We could probably try harder. + LIBC=gnu + + set_cc_for_build + cat <<-EOF > "$dummy.c" + #include + #if defined(__UCLIBC__) + LIBC=uclibc + #elif defined(__dietlibc__) + LIBC=dietlibc + #else + LIBC=gnu + #endif + EOF + eval "`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g'`" + + # If ldd exists, use it to detect musl libc. + if command -v ldd >/dev/null && \ + ldd --version 2>&1 | grep -q ^musl + then + LIBC=musl + fi + ;; +esac + # Note: order is significant - the case branches are not exclusive. -case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in +case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in *:NetBSD:*:*) # NetBSD (nbsd) targets should (where applicable) match one or - # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*, + # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*, # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently # switched to ELF, *-*-netbsd* would select the old # object file format. This provides both forward @@ -155,21 +178,31 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in # Note: NetBSD doesn't particularly care about the vendor # portion of the name. We always set it to "unknown". sysctl="sysctl -n hw.machine_arch" - UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \ - /usr/sbin/$sysctl 2>/dev/null || echo unknown)` - case "${UNAME_MACHINE_ARCH}" in + UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \ + "/sbin/$sysctl" 2>/dev/null || \ + "/usr/sbin/$sysctl" 2>/dev/null || \ + echo unknown)` + case "$UNAME_MACHINE_ARCH" in armeb) machine=armeb-unknown ;; arm*) machine=arm-unknown ;; sh3el) machine=shl-unknown ;; sh3eb) machine=sh-unknown ;; sh5el) machine=sh5le-unknown ;; - *) machine=${UNAME_MACHINE_ARCH}-unknown ;; + earmv*) + arch=`echo "$UNAME_MACHINE_ARCH" | sed -e 's,^e\(armv[0-9]\).*$,\1,'` + endian=`echo "$UNAME_MACHINE_ARCH" | sed -ne 's,^.*\(eb\)$,\1,p'` + machine="${arch}${endian}"-unknown + ;; + *) machine="$UNAME_MACHINE_ARCH"-unknown ;; esac # The Operating System including object format, if it has switched - # to ELF recently, or will in the future. - case "${UNAME_MACHINE_ARCH}" in + # to ELF recently (or will in the future) and ABI. + case "$UNAME_MACHINE_ARCH" in + earm*) + os=netbsdelf + ;; arm*|i386|m68k|ns32k|sh3*|sparc|vax) - eval $set_cc_for_build + set_cc_for_build if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ELF__ then @@ -184,40 +217,67 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in os=netbsd ;; esac + # Determine ABI tags. + case "$UNAME_MACHINE_ARCH" in + earm*) + expr='s/^earmv[0-9]/-eabi/;s/eb$//' + abi=`echo "$UNAME_MACHINE_ARCH" | sed -e "$expr"` + ;; + esac # The OS release # Debian GNU/NetBSD machines have a different userland, and # thus, need a distinct triplet. However, they do not need # kernel version information, so it can be replaced with a # suitable tag, in the style of linux-gnu. - case "${UNAME_VERSION}" in + case "$UNAME_VERSION" in Debian*) release='-gnu' ;; *) - release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` + release=`echo "$UNAME_RELEASE" | sed -e 's/[-_].*//' | cut -d. -f1,2` ;; esac # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: # contains redundant information, the shorter form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. - echo "${machine}-${os}${release}" + echo "$machine-${os}${release}${abi-}" + exit ;; + *:Bitrig:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` + echo "$UNAME_MACHINE_ARCH"-unknown-bitrig"$UNAME_RELEASE" exit ;; *:OpenBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` - echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE} + echo "$UNAME_MACHINE_ARCH"-unknown-openbsd"$UNAME_RELEASE" + exit ;; + *:LibertyBSD:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\.//'` + echo "$UNAME_MACHINE_ARCH"-unknown-libertybsd"$UNAME_RELEASE" + exit ;; + *:MidnightBSD:*:*) + echo "$UNAME_MACHINE"-unknown-midnightbsd"$UNAME_RELEASE" exit ;; *:ekkoBSD:*:*) - echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE} + echo "$UNAME_MACHINE"-unknown-ekkobsd"$UNAME_RELEASE" exit ;; *:SolidBSD:*:*) - echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE} + echo "$UNAME_MACHINE"-unknown-solidbsd"$UNAME_RELEASE" exit ;; macppc:MirBSD:*:*) - echo powerpc-unknown-mirbsd${UNAME_RELEASE} + echo powerpc-unknown-mirbsd"$UNAME_RELEASE" exit ;; *:MirBSD:*:*) - echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE} + echo "$UNAME_MACHINE"-unknown-mirbsd"$UNAME_RELEASE" + exit ;; + *:Sortix:*:*) + echo "$UNAME_MACHINE"-unknown-sortix exit ;; + *:Redox:*:*) + echo "$UNAME_MACHINE"-unknown-redox + exit ;; + mips:OSF1:*.*) + echo mips-dec-osf1 + exit ;; alpha:OSF1:*:*) case $UNAME_RELEASE in *4.0) @@ -234,63 +294,54 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` case "$ALPHA_CPU_TYPE" in "EV4 (21064)") - UNAME_MACHINE="alpha" ;; + UNAME_MACHINE=alpha ;; "EV4.5 (21064)") - UNAME_MACHINE="alpha" ;; + UNAME_MACHINE=alpha ;; "LCA4 (21066/21068)") - UNAME_MACHINE="alpha" ;; + UNAME_MACHINE=alpha ;; "EV5 (21164)") - UNAME_MACHINE="alphaev5" ;; + UNAME_MACHINE=alphaev5 ;; "EV5.6 (21164A)") - UNAME_MACHINE="alphaev56" ;; + UNAME_MACHINE=alphaev56 ;; "EV5.6 (21164PC)") - UNAME_MACHINE="alphapca56" ;; + UNAME_MACHINE=alphapca56 ;; "EV5.7 (21164PC)") - UNAME_MACHINE="alphapca57" ;; + UNAME_MACHINE=alphapca57 ;; "EV6 (21264)") - UNAME_MACHINE="alphaev6" ;; + UNAME_MACHINE=alphaev6 ;; "EV6.7 (21264A)") - UNAME_MACHINE="alphaev67" ;; + UNAME_MACHINE=alphaev67 ;; "EV6.8CB (21264C)") - UNAME_MACHINE="alphaev68" ;; + UNAME_MACHINE=alphaev68 ;; "EV6.8AL (21264B)") - UNAME_MACHINE="alphaev68" ;; + UNAME_MACHINE=alphaev68 ;; "EV6.8CX (21264D)") - UNAME_MACHINE="alphaev68" ;; + UNAME_MACHINE=alphaev68 ;; "EV6.9A (21264/EV69A)") - UNAME_MACHINE="alphaev69" ;; + UNAME_MACHINE=alphaev69 ;; "EV7 (21364)") - UNAME_MACHINE="alphaev7" ;; + UNAME_MACHINE=alphaev7 ;; "EV7.9 (21364A)") - UNAME_MACHINE="alphaev79" ;; + UNAME_MACHINE=alphaev79 ;; esac # A Pn.n version is a patched version. # A Vn.n version is a released version. # A Tn.n version is a released field test version. # A Xn.n version is an unreleased experimental baselevel. # 1.2 uses "1.2" for uname -r. - echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + echo "$UNAME_MACHINE"-dec-osf"`echo "$UNAME_RELEASE" | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz`" # Reset EXIT trap before exiting to avoid spurious non-zero exit code. exitcode=$? trap '' 0 exit $exitcode ;; - Alpha\ *:Windows_NT*:*) - # How do we know it's Interix rather than the generic POSIX subsystem? - # Should we change UNAME_MACHINE based on the output of uname instead - # of the specific Alpha model? - echo alpha-pc-interix - exit ;; - 21064:Windows_NT:50:3) - echo alpha-dec-winnt3.5 - exit ;; Amiga*:UNIX_System_V:4.0:*) echo m68k-unknown-sysv4 exit ;; *:[Aa]miga[Oo][Ss]:*:*) - echo ${UNAME_MACHINE}-unknown-amigaos + echo "$UNAME_MACHINE"-unknown-amigaos exit ;; *:[Mm]orph[Oo][Ss]:*:*) - echo ${UNAME_MACHINE}-unknown-morphos + echo "$UNAME_MACHINE"-unknown-morphos exit ;; *:OS/390:*:*) echo i370-ibm-openedition @@ -302,9 +353,9 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in echo powerpc-ibm-os400 exit ;; arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) - echo arm-acorn-riscix${UNAME_RELEASE} + echo arm-acorn-riscix"$UNAME_RELEASE" exit ;; - arm:riscos:*:*|arm:RISCOS:*:*) + arm*:riscos:*:*|arm*:RISCOS:*:*) echo arm-unknown-riscos exit ;; SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) @@ -329,38 +380,33 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in sparc) echo sparc-icl-nx7; exit ;; esac ;; s390x:SunOS:*:*) - echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + echo "$UNAME_MACHINE"-ibm-solaris2"`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`" exit ;; sun4H:SunOS:5.*:*) - echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + echo sparc-hal-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`" exit ;; sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) - echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + echo sparc-sun-solaris2"`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`" exit ;; i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) - echo i386-pc-auroraux${UNAME_RELEASE} + echo i386-pc-auroraux"$UNAME_RELEASE" exit ;; i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) - eval $set_cc_for_build - SUN_ARCH="i386" - # If there is a compiler, see if it is configured for 64-bit objects. - # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. - # This test works for both compilers. - if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then - if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ - (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ - grep IS_64BIT_ARCH >/dev/null - then - SUN_ARCH="x86_64" - fi - fi - echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + UNAME_REL="`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`" + case `isainfo -b` in + 32) + echo i386-pc-solaris2"$UNAME_REL" + ;; + 64) + echo x86_64-pc-solaris2"$UNAME_REL" + ;; + esac exit ;; sun4*:SunOS:6*:*) # According to config.sub, this is the proper way to canonicalize # SunOS6. Hard to guess exactly what SunOS6 will be like, but # it's likely to be more like Solaris than SunOS4. - echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + echo sparc-sun-solaris3"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`" exit ;; sun4*:SunOS:*:*) case "`/usr/bin/arch -k`" in @@ -369,25 +415,25 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in ;; esac # Japanese Language versions have a version number like `4.1.3-JL'. - echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` + echo sparc-sun-sunos"`echo "$UNAME_RELEASE"|sed -e 's/-/_/'`" exit ;; sun3*:SunOS:*:*) - echo m68k-sun-sunos${UNAME_RELEASE} + echo m68k-sun-sunos"$UNAME_RELEASE" exit ;; sun*:*:4.2BSD:*) UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` - test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 + test "x$UNAME_RELEASE" = x && UNAME_RELEASE=3 case "`/bin/arch`" in sun3) - echo m68k-sun-sunos${UNAME_RELEASE} + echo m68k-sun-sunos"$UNAME_RELEASE" ;; sun4) - echo sparc-sun-sunos${UNAME_RELEASE} + echo sparc-sun-sunos"$UNAME_RELEASE" ;; esac exit ;; aushp:SunOS:*:*) - echo sparc-auspex-sunos${UNAME_RELEASE} + echo sparc-auspex-sunos"$UNAME_RELEASE" exit ;; # The situation for MiNT is a little confusing. The machine name # can be virtually everything (everything which is not @@ -398,44 +444,44 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in # MiNT. But MiNT is downward compatible to TOS, so this should # be no problem. atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) - echo m68k-atari-mint${UNAME_RELEASE} + echo m68k-atari-mint"$UNAME_RELEASE" exit ;; atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) - echo m68k-atari-mint${UNAME_RELEASE} + echo m68k-atari-mint"$UNAME_RELEASE" exit ;; *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) - echo m68k-atari-mint${UNAME_RELEASE} + echo m68k-atari-mint"$UNAME_RELEASE" exit ;; milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) - echo m68k-milan-mint${UNAME_RELEASE} + echo m68k-milan-mint"$UNAME_RELEASE" exit ;; hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) - echo m68k-hades-mint${UNAME_RELEASE} + echo m68k-hades-mint"$UNAME_RELEASE" exit ;; *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) - echo m68k-unknown-mint${UNAME_RELEASE} + echo m68k-unknown-mint"$UNAME_RELEASE" exit ;; m68k:machten:*:*) - echo m68k-apple-machten${UNAME_RELEASE} + echo m68k-apple-machten"$UNAME_RELEASE" exit ;; powerpc:machten:*:*) - echo powerpc-apple-machten${UNAME_RELEASE} + echo powerpc-apple-machten"$UNAME_RELEASE" exit ;; RISC*:Mach:*:*) echo mips-dec-mach_bsd4.3 exit ;; RISC*:ULTRIX:*:*) - echo mips-dec-ultrix${UNAME_RELEASE} + echo mips-dec-ultrix"$UNAME_RELEASE" exit ;; VAX*:ULTRIX*:*:*) - echo vax-dec-ultrix${UNAME_RELEASE} + echo vax-dec-ultrix"$UNAME_RELEASE" exit ;; 2020:CLIX:*:* | 2430:CLIX:*:*) - echo clipper-intergraph-clix${UNAME_RELEASE} + echo clipper-intergraph-clix"$UNAME_RELEASE" exit ;; mips:*:*:UMIPS | mips:*:*:RISCos) - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c + set_cc_for_build + sed 's/^ //' << EOF > "$dummy.c" #ifdef __cplusplus #include /* for printf() prototype */ int main (int argc, char *argv[]) { @@ -444,23 +490,23 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in #endif #if defined (host_mips) && defined (MIPSEB) #if defined (SYSTYPE_SYSV) - printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); + printf ("mips-mips-riscos%ssysv\\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_SVR4) - printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); + printf ("mips-mips-riscos%ssvr4\\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) - printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); + printf ("mips-mips-riscos%sbsd\\n", argv[1]); exit (0); #endif #endif exit (-1); } EOF - $CC_FOR_BUILD -o $dummy $dummy.c && - dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` && - SYSTEM_NAME=`$dummy $dummyarg` && + $CC_FOR_BUILD -o "$dummy" "$dummy.c" && + dummyarg=`echo "$UNAME_RELEASE" | sed -n 's/\([0-9]*\).*/\1/p'` && + SYSTEM_NAME=`"$dummy" "$dummyarg"` && { echo "$SYSTEM_NAME"; exit; } - echo mips-mips-riscos${UNAME_RELEASE} + echo mips-mips-riscos"$UNAME_RELEASE" exit ;; Motorola:PowerMAX_OS:*:*) echo powerpc-motorola-powermax @@ -486,17 +532,17 @@ EOF AViiON:dgux:*:*) # DG/UX returns AViiON for all architectures UNAME_PROCESSOR=`/usr/bin/uname -p` - if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] + if [ "$UNAME_PROCESSOR" = mc88100 ] || [ "$UNAME_PROCESSOR" = mc88110 ] then - if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ - [ ${TARGET_BINARY_INTERFACE}x = x ] + if [ "$TARGET_BINARY_INTERFACE"x = m88kdguxelfx ] || \ + [ "$TARGET_BINARY_INTERFACE"x = x ] then - echo m88k-dg-dgux${UNAME_RELEASE} + echo m88k-dg-dgux"$UNAME_RELEASE" else - echo m88k-dg-dguxbcs${UNAME_RELEASE} + echo m88k-dg-dguxbcs"$UNAME_RELEASE" fi else - echo i586-dg-dgux${UNAME_RELEASE} + echo i586-dg-dgux"$UNAME_RELEASE" fi exit ;; M88*:DolphinOS:*:*) # DolphinOS (SVR3) @@ -513,7 +559,7 @@ EOF echo m68k-tektronix-bsd exit ;; *:IRIX*:*:*) - echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` + echo mips-sgi-irix"`echo "$UNAME_RELEASE"|sed -e 's/-/_/g'`" exit ;; ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id @@ -525,14 +571,14 @@ EOF if [ -x /usr/bin/oslevel ] ; then IBM_REV=`/usr/bin/oslevel` else - IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + IBM_REV="$UNAME_VERSION.$UNAME_RELEASE" fi - echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} + echo "$UNAME_MACHINE"-ibm-aix"$IBM_REV" exit ;; *:AIX:2:3) if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c + set_cc_for_build + sed 's/^ //' << EOF > "$dummy.c" #include main() @@ -543,7 +589,7 @@ EOF exit(0); } EOF - if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` + if $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` then echo "$SYSTEM_NAME" else @@ -557,26 +603,27 @@ EOF exit ;; *:AIX:*:[4567]) IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` - if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then + if /usr/sbin/lsattr -El "$IBM_CPU_ID" | grep ' POWER' >/dev/null 2>&1; then IBM_ARCH=rs6000 else IBM_ARCH=powerpc fi - if [ -x /usr/bin/oslevel ] ; then - IBM_REV=`/usr/bin/oslevel` + if [ -x /usr/bin/lslpp ] ; then + IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc | + awk -F: '{ print $3 }' | sed s/[0-9]*$/0/` else - IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + IBM_REV="$UNAME_VERSION.$UNAME_RELEASE" fi - echo ${IBM_ARCH}-ibm-aix${IBM_REV} + echo "$IBM_ARCH"-ibm-aix"$IBM_REV" exit ;; *:AIX:*:*) echo rs6000-ibm-aix exit ;; - ibmrt:4.4BSD:*|romp-ibm:BSD:*) + ibmrt:4.4BSD:*|romp-ibm:4.4BSD:*) echo romp-ibm-bsd4.4 exit ;; ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and - echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to + echo romp-ibm-bsd"$UNAME_RELEASE" # 4.3 with uname added to exit ;; # report: romp-ibm BSD 4.3 *:BOSX:*:*) echo rs6000-bull-bosx @@ -591,28 +638,28 @@ EOF echo m68k-hp-bsd4.4 exit ;; 9000/[34678]??:HP-UX:*:*) - HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` - case "${UNAME_MACHINE}" in - 9000/31? ) HP_ARCH=m68000 ;; - 9000/[34]?? ) HP_ARCH=m68k ;; + HPUX_REV=`echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//'` + case "$UNAME_MACHINE" in + 9000/31?) HP_ARCH=m68000 ;; + 9000/[34]??) HP_ARCH=m68k ;; 9000/[678][0-9][0-9]) if [ -x /usr/bin/getconf ]; then sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` - case "${sc_cpu_version}" in - 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 - 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 + case "$sc_cpu_version" in + 523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0 + 528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1 532) # CPU_PA_RISC2_0 - case "${sc_kernel_bits}" in - 32) HP_ARCH="hppa2.0n" ;; - 64) HP_ARCH="hppa2.0w" ;; - '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20 + case "$sc_kernel_bits" in + 32) HP_ARCH=hppa2.0n ;; + 64) HP_ARCH=hppa2.0w ;; + '') HP_ARCH=hppa2.0 ;; # HP-UX 10.20 esac ;; esac fi - if [ "${HP_ARCH}" = "" ]; then - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c + if [ "$HP_ARCH" = "" ]; then + set_cc_for_build + sed 's/^ //' << EOF > "$dummy.c" #define _HPUX_SOURCE #include @@ -645,13 +692,13 @@ EOF exit (0); } EOF - (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` + (CCOPTS="" $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null) && HP_ARCH=`"$dummy"` test -z "$HP_ARCH" && HP_ARCH=hppa fi ;; esac - if [ ${HP_ARCH} = "hppa2.0w" ] + if [ "$HP_ARCH" = hppa2.0w ] then - eval $set_cc_for_build + set_cc_for_build # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler @@ -662,23 +709,23 @@ EOF # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess # => hppa64-hp-hpux11.23 - if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | + if echo __LP64__ | (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | grep -q __LP64__ then - HP_ARCH="hppa2.0w" + HP_ARCH=hppa2.0w else - HP_ARCH="hppa64" + HP_ARCH=hppa64 fi fi - echo ${HP_ARCH}-hp-hpux${HPUX_REV} + echo "$HP_ARCH"-hp-hpux"$HPUX_REV" exit ;; ia64:HP-UX:*:*) - HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` - echo ia64-hp-hpux${HPUX_REV} + HPUX_REV=`echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//'` + echo ia64-hp-hpux"$HPUX_REV" exit ;; 3050*:HI-UX:*:*) - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c + set_cc_for_build + sed 's/^ //' << EOF > "$dummy.c" #include int main () @@ -703,11 +750,11 @@ EOF exit (0); } EOF - $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` && + $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` && { echo "$SYSTEM_NAME"; exit; } echo unknown-hitachi-hiuxwe2 exit ;; - 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) + 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:*) echo hppa1.1-hp-bsd exit ;; 9000/8??:4.3bsd:*:*) @@ -716,7 +763,7 @@ EOF *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) echo hppa1.0-hp-mpeix exit ;; - hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) + hp7??:OSF1:*:* | hp8?[79]:OSF1:*:*) echo hppa1.1-hp-osf exit ;; hp8??:OSF1:*:*) @@ -724,9 +771,9 @@ EOF exit ;; i*86:OSF1:*:*) if [ -x /usr/sbin/sysversion ] ; then - echo ${UNAME_MACHINE}-unknown-osf1mk + echo "$UNAME_MACHINE"-unknown-osf1mk else - echo ${UNAME_MACHINE}-unknown-osf1 + echo "$UNAME_MACHINE"-unknown-osf1 fi exit ;; parisc*:Lites*:*:*) @@ -751,114 +798,120 @@ EOF echo c4-convex-bsd exit ;; CRAY*Y-MP:*:*:*) - echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + echo ymp-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*[A-Z]90:*:*:*) - echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ + echo "$UNAME_MACHINE"-cray-unicos"$UNAME_RELEASE" \ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ -e 's/\.[^.]*$/.X/' exit ;; CRAY*TS:*:*:*) - echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + echo t90-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*T3E:*:*:*) - echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + echo alphaev5-cray-unicosmk"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*SV1:*:*:*) - echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + echo sv1-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' exit ;; *:UNICOS/mp:*:*) - echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + echo craynv-cray-unicosmp"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' exit ;; F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) - FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` - FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` - FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` + FUJITSU_PROC=`uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` + FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` + FUJITSU_REL=`echo "$UNAME_RELEASE" | sed -e 's/ /_/'` echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; 5000:UNIX_System_V:4.*:*) - FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` - FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'` + FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` + FUJITSU_REL=`echo "$UNAME_RELEASE" | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/'` echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) - echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} + echo "$UNAME_MACHINE"-pc-bsdi"$UNAME_RELEASE" exit ;; sparc*:BSD/OS:*:*) - echo sparc-unknown-bsdi${UNAME_RELEASE} + echo sparc-unknown-bsdi"$UNAME_RELEASE" exit ;; *:BSD/OS:*:*) - echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} + echo "$UNAME_MACHINE"-unknown-bsdi"$UNAME_RELEASE" + exit ;; + arm*:FreeBSD:*:*) + UNAME_PROCESSOR=`uname -p` + set_cc_for_build + if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ARM_PCS_VFP + then + echo "${UNAME_PROCESSOR}"-unknown-freebsd"`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`"-gnueabi + else + echo "${UNAME_PROCESSOR}"-unknown-freebsd"`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`"-gnueabihf + fi exit ;; *:FreeBSD:*:*) UNAME_PROCESSOR=`/usr/bin/uname -p` - case ${UNAME_PROCESSOR} in + case "$UNAME_PROCESSOR" in amd64) - echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; - *) - echo ${UNAME_PROCESSOR}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; + UNAME_PROCESSOR=x86_64 ;; + i386) + UNAME_PROCESSOR=i586 ;; esac + echo "$UNAME_PROCESSOR"-unknown-freebsd"`echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`" exit ;; i*:CYGWIN*:*) - echo ${UNAME_MACHINE}-pc-cygwin + echo "$UNAME_MACHINE"-pc-cygwin + exit ;; + *:MINGW64*:*) + echo "$UNAME_MACHINE"-pc-mingw64 exit ;; *:MINGW*:*) - echo ${UNAME_MACHINE}-pc-mingw32 + echo "$UNAME_MACHINE"-pc-mingw32 exit ;; - i*:windows32*:*) - # uname -m includes "-pc" on this system. - echo ${UNAME_MACHINE}-mingw32 + *:MSYS*:*) + echo "$UNAME_MACHINE"-pc-msys exit ;; i*:PW*:*) - echo ${UNAME_MACHINE}-pc-pw32 + echo "$UNAME_MACHINE"-pc-pw32 exit ;; *:Interix*:*) - case ${UNAME_MACHINE} in + case "$UNAME_MACHINE" in x86) - echo i586-pc-interix${UNAME_RELEASE} + echo i586-pc-interix"$UNAME_RELEASE" exit ;; authenticamd | genuineintel | EM64T) - echo x86_64-unknown-interix${UNAME_RELEASE} + echo x86_64-unknown-interix"$UNAME_RELEASE" exit ;; IA64) - echo ia64-unknown-interix${UNAME_RELEASE} + echo ia64-unknown-interix"$UNAME_RELEASE" exit ;; esac ;; - [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) - echo i${UNAME_MACHINE}-pc-mks - exit ;; - 8664:Windows_NT:*) - echo x86_64-pc-mks - exit ;; - i*:Windows_NT*:* | Pentium*:Windows_NT*:*) - # How do we know it's Interix rather than the generic POSIX subsystem? - # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we - # UNAME_MACHINE based on the output of uname instead of i386? - echo i586-pc-interix - exit ;; i*:UWIN*:*) - echo ${UNAME_MACHINE}-pc-uwin + echo "$UNAME_MACHINE"-pc-uwin exit ;; amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) echo x86_64-unknown-cygwin exit ;; - p*:CYGWIN*:*) - echo powerpcle-unknown-cygwin - exit ;; prep*:SunOS:5.*:*) - echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + echo powerpcle-unknown-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`" exit ;; *:GNU:*:*) # the GNU system - echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` + echo "`echo "$UNAME_MACHINE"|sed -e 's,[-/].*$,,'`-unknown-$LIBC`echo "$UNAME_RELEASE"|sed -e 's,/.*$,,'`" exit ;; *:GNU/*:*:*) # other systems with GNU libc and userland - echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu + echo "$UNAME_MACHINE-unknown-`echo "$UNAME_SYSTEM" | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"``echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`-$LIBC" + exit ;; + *:Minix:*:*) + echo "$UNAME_MACHINE"-unknown-minix exit ;; - i*86:Minix:*:*) - echo ${UNAME_MACHINE}-pc-minix + aarch64:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + aarch64_be:Linux:*:*) + UNAME_MACHINE=aarch64_be + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; alpha:Linux:*:*) case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in @@ -871,63 +924,64 @@ EOF EV68*) UNAME_MACHINE=alphaev68 ;; esac objdump --private-headers /bin/sh | grep -q ld.so.1 - if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi - echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC} + if test "$?" = 0 ; then LIBC=gnulibc1 ; fi + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + arc:Linux:*:* | arceb:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; arm*:Linux:*:*) - eval $set_cc_for_build + set_cc_for_build if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_EABI__ then - echo ${UNAME_MACHINE}-unknown-linux-gnu + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" else if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_PCS_VFP then - echo ${UNAME_MACHINE}-unknown-linux-gnueabi + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"eabi else - echo ${UNAME_MACHINE}-unknown-linux-gnueabihf + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"eabihf fi fi exit ;; avr32*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; cris:Linux:*:*) - echo cris-axis-linux-gnu + echo "$UNAME_MACHINE"-axis-linux-"$LIBC" exit ;; crisv32:Linux:*:*) - echo crisv32-axis-linux-gnu + echo "$UNAME_MACHINE"-axis-linux-"$LIBC" + exit ;; + e2k:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; frv:Linux:*:*) - echo frv-unknown-linux-gnu + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; hexagon:Linux:*:*) - echo hexagon-unknown-linux-gnu + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; i*86:Linux:*:*) - LIBC=gnu - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c - #ifdef __dietlibc__ - LIBC=dietlibc - #endif -EOF - eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC'` - echo "${UNAME_MACHINE}-pc-linux-${LIBC}" + echo "$UNAME_MACHINE"-pc-linux-"$LIBC" exit ;; ia64:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + k1om:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; m32r*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; m68*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; mips:Linux:*:* | mips64:Linux:*:*) - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c + set_cc_for_build + sed 's/^ //' << EOF > "$dummy.c" #undef CPU #undef ${UNAME_MACHINE} #undef ${UNAME_MACHINE}el @@ -941,55 +995,70 @@ EOF #endif #endif EOF - eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'` - test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; } + eval "`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^CPU'`" + test "x$CPU" != x && { echo "$CPU-unknown-linux-$LIBC"; exit; } ;; - or32:Linux:*:*) - echo or32-unknown-linux-gnu + mips64el:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + openrisc*:Linux:*:*) + echo or1k-unknown-linux-"$LIBC" + exit ;; + or32:Linux:*:* | or1k*:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; padre:Linux:*:*) - echo sparc-unknown-linux-gnu + echo sparc-unknown-linux-"$LIBC" exit ;; parisc64:Linux:*:* | hppa64:Linux:*:*) - echo hppa64-unknown-linux-gnu + echo hppa64-unknown-linux-"$LIBC" exit ;; parisc:Linux:*:* | hppa:Linux:*:*) # Look for CPU level case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in - PA7*) echo hppa1.1-unknown-linux-gnu ;; - PA8*) echo hppa2.0-unknown-linux-gnu ;; - *) echo hppa-unknown-linux-gnu ;; + PA7*) echo hppa1.1-unknown-linux-"$LIBC" ;; + PA8*) echo hppa2.0-unknown-linux-"$LIBC" ;; + *) echo hppa-unknown-linux-"$LIBC" ;; esac exit ;; ppc64:Linux:*:*) - echo powerpc64-unknown-linux-gnu + echo powerpc64-unknown-linux-"$LIBC" exit ;; ppc:Linux:*:*) - echo powerpc-unknown-linux-gnu + echo powerpc-unknown-linux-"$LIBC" + exit ;; + ppc64le:Linux:*:*) + echo powerpc64le-unknown-linux-"$LIBC" + exit ;; + ppcle:Linux:*:*) + echo powerpcle-unknown-linux-"$LIBC" + exit ;; + riscv32:Linux:*:* | riscv64:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; s390:Linux:*:* | s390x:Linux:*:*) - echo ${UNAME_MACHINE}-ibm-linux + echo "$UNAME_MACHINE"-ibm-linux-"$LIBC" exit ;; sh64*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; sh*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; sparc:Linux:*:* | sparc64:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; tile*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; vax:Linux:*:*) - echo ${UNAME_MACHINE}-dec-linux-gnu + echo "$UNAME_MACHINE"-dec-linux-"$LIBC" exit ;; x86_64:Linux:*:*) - echo x86_64-unknown-linux-gnu + echo "$UNAME_MACHINE"-pc-linux-"$LIBC" exit ;; xtensa*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; i*86:DYNIX/ptx:4*:*) # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. @@ -1003,34 +1072,34 @@ EOF # I am not positive that other SVR4 systems won't match this, # I just have to hope. -- rms. # Use sysv4.2uw... so that sysv4* matches it. - echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} + echo "$UNAME_MACHINE"-pc-sysv4.2uw"$UNAME_VERSION" exit ;; i*86:OS/2:*:*) # If we were able to find `uname', then EMX Unix compatibility # is probably installed. - echo ${UNAME_MACHINE}-pc-os2-emx + echo "$UNAME_MACHINE"-pc-os2-emx exit ;; i*86:XTS-300:*:STOP) - echo ${UNAME_MACHINE}-unknown-stop + echo "$UNAME_MACHINE"-unknown-stop exit ;; i*86:atheos:*:*) - echo ${UNAME_MACHINE}-unknown-atheos + echo "$UNAME_MACHINE"-unknown-atheos exit ;; i*86:syllable:*:*) - echo ${UNAME_MACHINE}-pc-syllable + echo "$UNAME_MACHINE"-pc-syllable exit ;; i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) - echo i386-unknown-lynxos${UNAME_RELEASE} + echo i386-unknown-lynxos"$UNAME_RELEASE" exit ;; i*86:*DOS:*:*) - echo ${UNAME_MACHINE}-pc-msdosdjgpp + echo "$UNAME_MACHINE"-pc-msdosdjgpp exit ;; - i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) - UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` + i*86:*:4.*:*) + UNAME_REL=`echo "$UNAME_RELEASE" | sed 's/\/MP$//'` if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then - echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} + echo "$UNAME_MACHINE"-univel-sysv"$UNAME_REL" else - echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} + echo "$UNAME_MACHINE"-pc-sysv"$UNAME_REL" fi exit ;; i*86:*:5:[678]*) @@ -1040,12 +1109,12 @@ EOF *Pentium) UNAME_MACHINE=i586 ;; *Pent*|*Celeron) UNAME_MACHINE=i686 ;; esac - echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} + echo "$UNAME_MACHINE-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}{$UNAME_VERSION}" exit ;; i*86:*:3.2:*) if test -f /usr/options/cb.name; then UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 @@ -1055,9 +1124,9 @@ EOF && UNAME_MACHINE=i686 (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ && UNAME_MACHINE=i686 - echo ${UNAME_MACHINE}-pc-sco$UNAME_REL + echo "$UNAME_MACHINE"-pc-sco"$UNAME_REL" else - echo ${UNAME_MACHINE}-pc-sysv32 + echo "$UNAME_MACHINE"-pc-sysv32 fi exit ;; pc:*:*:*) @@ -1065,7 +1134,7 @@ EOF # uname -m prints for DJGPP always 'pc', but it prints nothing about # the processor, so we play safe by assuming i586. # Note: whatever this is, it MUST be the same as what config.sub - # prints for the "djgpp" host, or else GDB configury will decide that + # prints for the "djgpp" host, or else GDB configure will decide that # this is a cross-build. echo i586-pc-msdosdjgpp exit ;; @@ -1077,9 +1146,9 @@ EOF exit ;; i860:*:4.*:*) # i860-SVR4 if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then - echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 + echo i860-stardent-sysv"$UNAME_RELEASE" # Stardent Vistra i860-SVR4 else # Add other i860-SVR4 vendors below as they are discovered. - echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 + echo i860-unknown-sysv"$UNAME_RELEASE" # Unknown i860-SVR4 fi exit ;; mini*:CTIX:SYS*5:*) @@ -1099,9 +1168,9 @@ EOF test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ - && { echo i486-ncr-sysv4.3${OS_REL}; exit; } + && { echo i486-ncr-sysv4.3"$OS_REL"; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ - && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; + && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;; 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4; exit; } ;; @@ -1110,28 +1179,28 @@ EOF test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ - && { echo i486-ncr-sysv4.3${OS_REL}; exit; } + && { echo i486-ncr-sysv4.3"$OS_REL"; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ - && { echo i586-ncr-sysv4.3${OS_REL}; exit; } + && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ - && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; + && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;; m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) - echo m68k-unknown-lynxos${UNAME_RELEASE} + echo m68k-unknown-lynxos"$UNAME_RELEASE" exit ;; mc68030:UNIX_System_V:4.*:*) echo m68k-atari-sysv4 exit ;; TSUNAMI:LynxOS:2.*:*) - echo sparc-unknown-lynxos${UNAME_RELEASE} + echo sparc-unknown-lynxos"$UNAME_RELEASE" exit ;; rs6000:LynxOS:2.*:*) - echo rs6000-unknown-lynxos${UNAME_RELEASE} + echo rs6000-unknown-lynxos"$UNAME_RELEASE" exit ;; PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) - echo powerpc-unknown-lynxos${UNAME_RELEASE} + echo powerpc-unknown-lynxos"$UNAME_RELEASE" exit ;; SM[BE]S:UNIX_SV:*:*) - echo mips-dde-sysv${UNAME_RELEASE} + echo mips-dde-sysv"$UNAME_RELEASE" exit ;; RM*:ReliantUNIX-*:*:*) echo mips-sni-sysv4 @@ -1142,7 +1211,7 @@ EOF *:SINIX-*:*:*) if uname -p 2>/dev/null >/dev/null ; then UNAME_MACHINE=`(uname -p) 2>/dev/null` - echo ${UNAME_MACHINE}-sni-sysv4 + echo "$UNAME_MACHINE"-sni-sysv4 else echo ns32k-sni-sysv fi @@ -1162,23 +1231,23 @@ EOF exit ;; i*86:VOS:*:*) # From Paul.Green@stratus.com. - echo ${UNAME_MACHINE}-stratus-vos + echo "$UNAME_MACHINE"-stratus-vos exit ;; *:VOS:*:*) # From Paul.Green@stratus.com. echo hppa1.1-stratus-vos exit ;; mc68*:A/UX:*:*) - echo m68k-apple-aux${UNAME_RELEASE} + echo m68k-apple-aux"$UNAME_RELEASE" exit ;; news*:NEWS-OS:6*:*) echo mips-sony-newsos6 exit ;; R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) if [ -d /usr/nec ]; then - echo mips-nec-sysv${UNAME_RELEASE} + echo mips-nec-sysv"$UNAME_RELEASE" else - echo mips-unknown-sysv${UNAME_RELEASE} + echo mips-unknown-sysv"$UNAME_RELEASE" fi exit ;; BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. @@ -1193,66 +1262,97 @@ EOF BePC:Haiku:*:*) # Haiku running on Intel PC compatible. echo i586-pc-haiku exit ;; + x86_64:Haiku:*:*) + echo x86_64-unknown-haiku + exit ;; SX-4:SUPER-UX:*:*) - echo sx4-nec-superux${UNAME_RELEASE} + echo sx4-nec-superux"$UNAME_RELEASE" exit ;; SX-5:SUPER-UX:*:*) - echo sx5-nec-superux${UNAME_RELEASE} + echo sx5-nec-superux"$UNAME_RELEASE" exit ;; SX-6:SUPER-UX:*:*) - echo sx6-nec-superux${UNAME_RELEASE} + echo sx6-nec-superux"$UNAME_RELEASE" exit ;; SX-7:SUPER-UX:*:*) - echo sx7-nec-superux${UNAME_RELEASE} + echo sx7-nec-superux"$UNAME_RELEASE" exit ;; SX-8:SUPER-UX:*:*) - echo sx8-nec-superux${UNAME_RELEASE} + echo sx8-nec-superux"$UNAME_RELEASE" exit ;; SX-8R:SUPER-UX:*:*) - echo sx8r-nec-superux${UNAME_RELEASE} + echo sx8r-nec-superux"$UNAME_RELEASE" + exit ;; + SX-ACE:SUPER-UX:*:*) + echo sxace-nec-superux"$UNAME_RELEASE" exit ;; Power*:Rhapsody:*:*) - echo powerpc-apple-rhapsody${UNAME_RELEASE} + echo powerpc-apple-rhapsody"$UNAME_RELEASE" exit ;; *:Rhapsody:*:*) - echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} + echo "$UNAME_MACHINE"-apple-rhapsody"$UNAME_RELEASE" exit ;; *:Darwin:*:*) UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown - case $UNAME_PROCESSOR in - i386) - eval $set_cc_for_build - if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then - if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ - (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ - grep IS_64BIT_ARCH >/dev/null - then - UNAME_PROCESSOR="x86_64" - fi - fi ;; - unknown) UNAME_PROCESSOR=powerpc ;; - esac - echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} + set_cc_for_build + if test "$UNAME_PROCESSOR" = unknown ; then + UNAME_PROCESSOR=powerpc + fi + if test "`echo "$UNAME_RELEASE" | sed -e 's/\..*//'`" -le 10 ; then + if [ "$CC_FOR_BUILD" != no_compiler_found ]; then + if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ + (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null + then + case $UNAME_PROCESSOR in + i386) UNAME_PROCESSOR=x86_64 ;; + powerpc) UNAME_PROCESSOR=powerpc64 ;; + esac + fi + # On 10.4-10.6 one might compile for PowerPC via gcc -arch ppc + if (echo '#ifdef __POWERPC__'; echo IS_PPC; echo '#endif') | \ + (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_PPC >/dev/null + then + UNAME_PROCESSOR=powerpc + fi + fi + elif test "$UNAME_PROCESSOR" = i386 ; then + # Avoid executing cc on OS X 10.9, as it ships with a stub + # that puts up a graphical alert prompting to install + # developer tools. Any system running Mac OS X 10.7 or + # later (Darwin 11 and later) is required to have a 64-bit + # processor. This is not true of the ARM version of Darwin + # that Apple uses in portable devices. + UNAME_PROCESSOR=x86_64 + fi + echo "$UNAME_PROCESSOR"-apple-darwin"$UNAME_RELEASE" exit ;; *:procnto*:*:* | *:QNX:[0123456789]*:*) UNAME_PROCESSOR=`uname -p` - if test "$UNAME_PROCESSOR" = "x86"; then + if test "$UNAME_PROCESSOR" = x86; then UNAME_PROCESSOR=i386 UNAME_MACHINE=pc fi - echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE} + echo "$UNAME_PROCESSOR"-"$UNAME_MACHINE"-nto-qnx"$UNAME_RELEASE" exit ;; *:QNX:*:4*) echo i386-pc-qnx exit ;; - NEO-?:NONSTOP_KERNEL:*:*) - echo neo-tandem-nsk${UNAME_RELEASE} + NEO-*:NONSTOP_KERNEL:*:*) + echo neo-tandem-nsk"$UNAME_RELEASE" exit ;; - NSE-?:NONSTOP_KERNEL:*:*) - echo nse-tandem-nsk${UNAME_RELEASE} + NSE-*:NONSTOP_KERNEL:*:*) + echo nse-tandem-nsk"$UNAME_RELEASE" exit ;; - NSR-?:NONSTOP_KERNEL:*:*) - echo nsr-tandem-nsk${UNAME_RELEASE} + NSR-*:NONSTOP_KERNEL:*:*) + echo nsr-tandem-nsk"$UNAME_RELEASE" + exit ;; + NSV-*:NONSTOP_KERNEL:*:*) + echo nsv-tandem-nsk"$UNAME_RELEASE" + exit ;; + NSX-*:NONSTOP_KERNEL:*:*) + echo nsx-tandem-nsk"$UNAME_RELEASE" exit ;; *:NonStop-UX:*:*) echo mips-compaq-nonstopux @@ -1261,18 +1361,19 @@ EOF echo bs2000-siemens-sysv exit ;; DS/*:UNIX_System_V:*:*) - echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} + echo "$UNAME_MACHINE"-"$UNAME_SYSTEM"-"$UNAME_RELEASE" exit ;; *:Plan9:*:*) # "uname -m" is not consistent, so use $cputype instead. 386 # is converted to i386 for consistency with other x86 # operating systems. - if test "$cputype" = "386"; then + # shellcheck disable=SC2154 + if test "$cputype" = 386; then UNAME_MACHINE=i386 else UNAME_MACHINE="$cputype" fi - echo ${UNAME_MACHINE}-unknown-plan9 + echo "$UNAME_MACHINE"-unknown-plan9 exit ;; *:TOPS-10:*:*) echo pdp10-unknown-tops10 @@ -1293,14 +1394,14 @@ EOF echo pdp10-unknown-its exit ;; SEI:*:*:SEIUX) - echo mips-sei-seiux${UNAME_RELEASE} + echo mips-sei-seiux"$UNAME_RELEASE" exit ;; *:DragonFly:*:*) - echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` + echo "$UNAME_MACHINE"-unknown-dragonfly"`echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`" exit ;; *:*VMS:*:*) UNAME_MACHINE=`(uname -p) 2>/dev/null` - case "${UNAME_MACHINE}" in + case "$UNAME_MACHINE" in A*) echo alpha-dec-vms ; exit ;; I*) echo ia64-dec-vms ; exit ;; V*) echo vax-dec-vms ; exit ;; @@ -1309,182 +1410,48 @@ EOF echo i386-pc-xenix exit ;; i*86:skyos:*:*) - echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//' + echo "$UNAME_MACHINE"-pc-skyos"`echo "$UNAME_RELEASE" | sed -e 's/ .*$//'`" exit ;; i*86:rdos:*:*) - echo ${UNAME_MACHINE}-pc-rdos + echo "$UNAME_MACHINE"-pc-rdos exit ;; i*86:AROS:*:*) - echo ${UNAME_MACHINE}-pc-aros + echo "$UNAME_MACHINE"-pc-aros + exit ;; + x86_64:VMkernel:*:*) + echo "$UNAME_MACHINE"-unknown-esx + exit ;; + amd64:Isilon\ OneFS:*:*) + echo x86_64-unknown-onefs exit ;; esac -#echo '(No uname command or uname output not recognized.)' 1>&2 -#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2 - -eval $set_cc_for_build -cat >$dummy.c < -# include -#endif -main () -{ -#if defined (sony) -#if defined (MIPSEB) - /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, - I don't know.... */ - printf ("mips-sony-bsd\n"); exit (0); -#else -#include - printf ("m68k-sony-newsos%s\n", -#ifdef NEWSOS4 - "4" -#else - "" -#endif - ); exit (0); -#endif -#endif - -#if defined (__arm) && defined (__acorn) && defined (__unix) - printf ("arm-acorn-riscix\n"); exit (0); -#endif - -#if defined (hp300) && !defined (hpux) - printf ("m68k-hp-bsd\n"); exit (0); -#endif - -#if defined (NeXT) -#if !defined (__ARCHITECTURE__) -#define __ARCHITECTURE__ "m68k" -#endif - int version; - version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; - if (version < 4) - printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); - else - printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); - exit (0); -#endif - -#if defined (MULTIMAX) || defined (n16) -#if defined (UMAXV) - printf ("ns32k-encore-sysv\n"); exit (0); -#else -#if defined (CMU) - printf ("ns32k-encore-mach\n"); exit (0); -#else - printf ("ns32k-encore-bsd\n"); exit (0); -#endif -#endif -#endif - -#if defined (__386BSD__) - printf ("i386-pc-bsd\n"); exit (0); -#endif - -#if defined (sequent) -#if defined (i386) - printf ("i386-sequent-dynix\n"); exit (0); -#endif -#if defined (ns32000) - printf ("ns32k-sequent-dynix\n"); exit (0); -#endif -#endif - -#if defined (_SEQUENT_) - struct utsname un; - - uname(&un); - - if (strncmp(un.version, "V2", 2) == 0) { - printf ("i386-sequent-ptx2\n"); exit (0); - } - if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ - printf ("i386-sequent-ptx1\n"); exit (0); - } - printf ("i386-sequent-ptx\n"); exit (0); - -#endif - -#if defined (vax) -# if !defined (ultrix) -# include -# if defined (BSD) -# if BSD == 43 - printf ("vax-dec-bsd4.3\n"); exit (0); -# else -# if BSD == 199006 - printf ("vax-dec-bsd4.3reno\n"); exit (0); -# else - printf ("vax-dec-bsd\n"); exit (0); -# endif -# endif -# else - printf ("vax-dec-bsd\n"); exit (0); -# endif -# else - printf ("vax-dec-ultrix\n"); exit (0); -# endif -#endif +echo "$0: unable to guess system type" >&2 -#if defined (alliant) && defined (i860) - printf ("i860-alliant-bsd\n"); exit (0); -#endif +case "$UNAME_MACHINE:$UNAME_SYSTEM" in + mips:Linux | mips64:Linux) + # If we got here on MIPS GNU/Linux, output extra information. + cat >&2 </dev/null && SYSTEM_NAME=`$dummy` && - { echo "$SYSTEM_NAME"; exit; } - -# Apollos put the system type in the environment. - -test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; } - -# Convex versions that predate uname can use getsysinfo(1) - -if [ -x /usr/convex/getsysinfo ] -then - case `getsysinfo -f cpu_type` in - c1*) - echo c1-convex-bsd - exit ;; - c2*) - if getsysinfo -f scalar_acc - then echo c32-convex-bsd - else echo c2-convex-bsd - fi - exit ;; - c34*) - echo c34-convex-bsd - exit ;; - c38*) - echo c38-convex-bsd - exit ;; - c4*) - echo c4-convex-bsd - exit ;; - esac -fi + ;; +esac cat >&2 < in order to provide the needed -information to handle your system. +If $0 has already been updated, send the following data and any +information you think might be pertinent to config-patches@gnu.org to +provide the necessary information to handle your system. config.guess timestamp = $timestamp @@ -1503,16 +1470,16 @@ hostinfo = `(hostinfo) 2>/dev/null` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` -UNAME_MACHINE = ${UNAME_MACHINE} -UNAME_RELEASE = ${UNAME_RELEASE} -UNAME_SYSTEM = ${UNAME_SYSTEM} -UNAME_VERSION = ${UNAME_VERSION} +UNAME_MACHINE = "$UNAME_MACHINE" +UNAME_RELEASE = "$UNAME_RELEASE" +UNAME_SYSTEM = "$UNAME_SYSTEM" +UNAME_VERSION = "$UNAME_VERSION" EOF exit 1 # Local variables: -# eval: (add-hook 'write-file-hooks 'time-stamp) +# eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" diff --git a/cextern/wcslib/config/config.sub b/cextern/wcslib/config/config.sub old mode 100755 new mode 100644 index 0d2cddec7f0a..52eb02e29a25 --- a/cextern/wcslib/config/config.sub +++ b/cextern/wcslib/config/config.sub @@ -1,38 +1,31 @@ #! /bin/sh # Configuration validation subroutine script. -# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, -# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, -# 2011 Free Software Foundation, Inc. +# Copyright 1992-2018 Free Software Foundation, Inc. -timestamp='2011-09-09' +timestamp='2018-07-25' -# This file is (in principle) common to ALL GNU software. -# The presence of a machine in this file suggests that SOME GNU software -# can handle that machine. It does not imply ALL GNU software can. -# -# This file is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or +# This file is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. # # You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA -# 02110-1301, USA. +# along with this program; if not, see . # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under -# the same distribution terms that you use for the rest of that program. +# the same distribution terms that you use for the rest of that +# program. This Exception is an additional permission under section 7 +# of the GNU General Public License, version 3 ("GPLv3"). -# Please send patches to . Submit a context -# diff and a properly formatted GNU ChangeLog entry. +# Please send patches to . # # Configuration subroutine to validate and canonicalize a configuration type. # Supply the specified configuration type as an argument. @@ -40,7 +33,7 @@ timestamp='2011-09-09' # Otherwise, we print the canonical config type on stdout and succeed. # You can get the latest version of this script from: -# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD +# https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub # This file is supposed to be the same for all GNU packages # and recognize all the CPU types, system types and aliases @@ -60,12 +53,11 @@ timestamp='2011-09-09' me=`echo "$0" | sed -e 's,.*/,,'` usage="\ -Usage: $0 [OPTION] CPU-MFR-OPSYS - $0 [OPTION] ALIAS +Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS Canonicalize a configuration name. -Operation modes: +Options: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit @@ -75,9 +67,7 @@ Report bugs and patches to ." version="\ GNU config.sub ($timestamp) -Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, -2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free -Software Foundation, Inc. +Copyright 1992-2018 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." @@ -104,7 +94,7 @@ while test $# -gt 0 ; do *local*) # First pass through any local machine types. - echo $1 + echo "$1" exit ;; * ) @@ -120,124 +110,455 @@ case $# in exit 1;; esac -# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). -# Here we must recognize all the valid KERNEL-OS combinations. -maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` -case $maybe_os in - nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \ - linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \ - knetbsd*-gnu* | netbsd*-gnu* | \ - kopensolaris*-gnu* | \ - storm-chaos* | os2-emx* | rtmk-nova*) - os=-$maybe_os - basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` - ;; - *) - basic_machine=`echo $1 | sed 's/-[^-]*$//'` - if [ $basic_machine != $1 ] - then os=`echo $1 | sed 's/.*-/-/'` - else os=; fi - ;; -esac +# Split fields of configuration type +IFS="-" read -r field1 field2 field3 field4 <&2 + exit 1 ;; - -ptx*) - basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` + *-*-*-*) + basic_machine=$field1-$field2 + os=$field3-$field4 ;; - -windowsnt*) - os=`echo $os | sed -e 's/windowsnt/winnt/'` + *-*-*) + # Ambiguous whether COMPANY is present, or skipped and KERNEL-OS is two + # parts + maybe_os=$field2-$field3 + case $maybe_os in + nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc \ + | linux-newlib* | linux-musl* | linux-uclibc* | uclinux-uclibc* \ + | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* \ + | netbsd*-eabi* | kopensolaris*-gnu* | cloudabi*-eabi* \ + | storm-chaos* | os2-emx* | rtmk-nova*) + basic_machine=$field1 + os=$maybe_os + ;; + android-linux) + basic_machine=$field1-unknown + os=linux-android + ;; + *) + basic_machine=$field1-$field2 + os=$field3 + ;; + esac ;; - -psos*) - os=-psos + *-*) + # Second component is usually, but not always the OS + case $field2 in + # Prevent following clause from handling this valid os + sun*os*) + basic_machine=$field1 + os=$field2 + ;; + # Manufacturers + dec* | mips* | sequent* | encore* | pc532* | sgi* | sony* \ + | att* | 7300* | 3300* | delta* | motorola* | sun[234]* \ + | unicom* | ibm* | next | hp | isi* | apollo | altos* \ + | convergent* | ncr* | news | 32* | 3600* | 3100* | hitachi* \ + | c[123]* | convex* | sun | crds | omron* | dg | ultra | tti* \ + | harris | dolphin | highlevel | gould | cbm | ns | masscomp \ + | apple | axis | knuth | cray | microblaze* \ + | sim | cisco | oki | wec | wrs | winbond) + basic_machine=$field1-$field2 + os= + ;; + *) + basic_machine=$field1 + os=$field2 + ;; + esac ;; - -mint | -mint[0-9]*) - basic_machine=m68k-atari - os=-mint + *) + # Convert single-component short-hands not valid as part of + # multi-component configurations. + case $field1 in + 386bsd) + basic_machine=i386-pc + os=bsd + ;; + a29khif) + basic_machine=a29k-amd + os=udi + ;; + adobe68k) + basic_machine=m68010-adobe + os=scout + ;; + am29k) + basic_machine=a29k-none + os=bsd + ;; + amdahl) + basic_machine=580-amdahl + os=sysv + ;; + amigaos | amigados) + basic_machine=m68k-unknown + os=amigaos + ;; + amigaunix | amix) + basic_machine=m68k-unknown + os=sysv4 + ;; + apollo68) + basic_machine=m68k-apollo + os=sysv + ;; + apollo68bsd) + basic_machine=m68k-apollo + os=bsd + ;; + aros) + basic_machine=i386-pc + os=aros + ;; + aux) + basic_machine=m68k-apple + os=aux + ;; + balance) + basic_machine=ns32k-sequent + os=dynix + ;; + blackfin) + basic_machine=bfin-unknown + os=linux + ;; + cegcc) + basic_machine=arm-unknown + os=cegcc + ;; + cray) + basic_machine=j90-cray + os=unicos + ;; + craynv) + basic_machine=craynv-cray + os=unicosmp + ;; + delta88) + basic_machine=m88k-motorola + os=sysv3 + ;; + dicos) + basic_machine=i686-pc + os=dicos + ;; + djgpp) + basic_machine=i586-pc + os=msdosdjgpp + ;; + ebmon29k) + basic_machine=a29k-amd + os=ebmon + ;; + es1800 | OSE68k | ose68k | ose | OSE) + basic_machine=m68k-ericsson + os=ose + ;; + gmicro) + basic_machine=tron-gmicro + os=sysv + ;; + go32) + basic_machine=i386-pc + os=go32 + ;; + h8300hms) + basic_machine=h8300-hitachi + os=hms + ;; + h8300xray) + basic_machine=h8300-hitachi + os=xray + ;; + h8500hms) + basic_machine=h8500-hitachi + os=hms + ;; + harris) + basic_machine=m88k-harris + os=sysv3 + ;; + hp300bsd) + basic_machine=m68k-hp + os=bsd + ;; + hp300hpux) + basic_machine=m68k-hp + os=hpux + ;; + hppaosf) + basic_machine=hppa1.1-hp + os=osf + ;; + hppro) + basic_machine=hppa1.1-hp + os=proelf + ;; + i386mach) + basic_machine=i386-mach + os=mach + ;; + vsta) + basic_machine=i386-pc + os=vsta + ;; + isi68 | isi) + basic_machine=m68k-isi + os=sysv + ;; + m68knommu) + basic_machine=m68k-unknown + os=linux + ;; + magnum | m3230) + basic_machine=mips-mips + os=sysv + ;; + merlin) + basic_machine=ns32k-utek + os=sysv + ;; + mingw64) + basic_machine=x86_64-pc + os=mingw64 + ;; + mingw32) + basic_machine=i686-pc + os=mingw32 + ;; + mingw32ce) + basic_machine=arm-unknown + os=mingw32ce + ;; + monitor) + basic_machine=m68k-rom68k + os=coff + ;; + morphos) + basic_machine=powerpc-unknown + os=morphos + ;; + moxiebox) + basic_machine=moxie-unknown + os=moxiebox + ;; + msdos) + basic_machine=i386-pc + os=msdos + ;; + msys) + basic_machine=i686-pc + os=msys + ;; + mvs) + basic_machine=i370-ibm + os=mvs + ;; + nacl) + basic_machine=le32-unknown + os=nacl + ;; + ncr3000) + basic_machine=i486-ncr + os=sysv4 + ;; + netbsd386) + basic_machine=i386-pc + os=netbsd + ;; + netwinder) + basic_machine=armv4l-rebel + os=linux + ;; + news | news700 | news800 | news900) + basic_machine=m68k-sony + os=newsos + ;; + news1000) + basic_machine=m68030-sony + os=newsos + ;; + necv70) + basic_machine=v70-nec + os=sysv + ;; + nh3000) + basic_machine=m68k-harris + os=cxux + ;; + nh[45]000) + basic_machine=m88k-harris + os=cxux + ;; + nindy960) + basic_machine=i960-intel + os=nindy + ;; + mon960) + basic_machine=i960-intel + os=mon960 + ;; + nonstopux) + basic_machine=mips-compaq + os=nonstopux + ;; + os400) + basic_machine=powerpc-ibm + os=os400 + ;; + OSE68000 | ose68000) + basic_machine=m68000-ericsson + os=ose + ;; + os68k) + basic_machine=m68k-none + os=os68k + ;; + paragon) + basic_machine=i860-intel + os=osf + ;; + parisc) + basic_machine=hppa-unknown + os=linux + ;; + pw32) + basic_machine=i586-unknown + os=pw32 + ;; + rdos | rdos64) + basic_machine=x86_64-pc + os=rdos + ;; + rdos32) + basic_machine=i386-pc + os=rdos + ;; + rom68k) + basic_machine=m68k-rom68k + os=coff + ;; + sa29200) + basic_machine=a29k-amd + os=udi + ;; + sei) + basic_machine=mips-sei + os=seiux + ;; + sps7) + basic_machine=m68k-bull + os=sysv2 + ;; + stratus) + basic_machine=i860-stratus + os=sysv4 + ;; + sun2os3) + basic_machine=m68000-sun + os=sunos3 + ;; + sun2os4) + basic_machine=m68000-sun + os=sunos4 + ;; + sun3os3) + basic_machine=m68k-sun + os=sunos3 + ;; + sun3os4) + basic_machine=m68k-sun + os=sunos4 + ;; + sun4os3) + basic_machine=sparc-sun + os=sunos3 + ;; + sun4os4) + basic_machine=sparc-sun + os=sunos4 + ;; + sun4sol2) + basic_machine=sparc-sun + os=solaris2 + ;; + sv1) + basic_machine=sv1-cray + os=unicos + ;; + symmetry) + basic_machine=i386-sequent + os=dynix + ;; + t3e) + basic_machine=alphaev5-cray + os=unicos + ;; + t90) + basic_machine=t90-cray + os=unicos + ;; + toad1) + basic_machine=pdp10-xkl + os=tops20 + ;; + tpf) + basic_machine=s390x-ibm + os=tpf + ;; + udi29k) + basic_machine=a29k-amd + os=udi + ;; + ultra3) + basic_machine=a29k-nyu + os=sym1 + ;; + v810 | necv810) + basic_machine=v810-nec + os=none + ;; + vaxv) + basic_machine=vax-dec + os=sysv + ;; + vms) + basic_machine=vax-dec + os=vms + ;; + vxworks960) + basic_machine=i960-wrs + os=vxworks + ;; + vxworks68) + basic_machine=m68k-wrs + os=vxworks + ;; + vxworks29k) + basic_machine=a29k-wrs + os=vxworks + ;; + xbox) + basic_machine=i686-pc + os=mingw32 + ;; + ymp) + basic_machine=ymp-cray + os=unicos + ;; + *) + basic_machine=$1 + os= + ;; + esac ;; esac @@ -247,23 +568,29 @@ case $basic_machine in # Some are omitted here because they have special meanings below. 1750a | 580 \ | a29k \ + | aarch64 | aarch64_be \ | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ | am33_2.0 \ - | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr | avr32 \ - | be32 | be64 \ + | arc | arceb \ + | arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv6m | armv[78][arm] \ + | avr | avr32 \ + | ba \ + | be32 | be64 \ | bfin \ - | c4x | clipper \ + | c4x | c8051 | clipper | csky \ | d10v | d30v | dlx | dsp16xx \ - | fido | fr30 | frv \ + | e2k | epiphany \ + | fido | fr30 | frv | ft32 \ | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ | hexagon \ - | i370 | i860 | i960 | ia64 \ + | i370 | i860 | i960 | ia16 | ia64 \ | ip2k | iq2000 \ + | k1om \ | le32 | le64 \ | lm32 \ | m32c | m32r | m32rle | m68000 | m68k | m88k \ - | maxq | mb | microblaze | mcore | mep | metag \ + | maxq | mb | microblaze | microblazeel | mcore | mep | metag \ | mips | mipsbe | mipseb | mipsel | mipsle \ | mips16 \ | mips64 | mips64el \ @@ -277,26 +604,31 @@ case $basic_machine in | mips64vr5900 | mips64vr5900el \ | mipsisa32 | mipsisa32el \ | mipsisa32r2 | mipsisa32r2el \ + | mipsisa32r6 | mipsisa32r6el \ | mipsisa64 | mipsisa64el \ | mipsisa64r2 | mipsisa64r2el \ + | mipsisa64r6 | mipsisa64r6el \ | mipsisa64sb1 | mipsisa64sb1el \ | mipsisa64sr71k | mipsisa64sr71kel \ + | mipsr5900 | mipsr5900el \ | mipstx39 | mipstx39el \ | mn10200 | mn10300 \ | moxie \ | mt \ | msp430 \ | nds32 | nds32le | nds32be \ - | nios | nios2 \ + | nfp \ + | nios | nios2 | nios2eb | nios2el \ | ns16k | ns32k \ - | open8 \ - | or32 \ - | pdp10 | pdp11 | pj | pjl \ + | open8 | or1k | or1knd | or32 \ + | pdp10 | pj | pjl \ | powerpc | powerpc64 | powerpc64le | powerpcle \ + | pru \ | pyramid \ - | rx \ + | riscv | riscv32 | riscv64 \ + | rl78 | rx \ | score \ - | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ + | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[234]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ | sh64 | sh64le \ | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \ | sparcv8 | sparcv9 | sparcv9b | sparcv9v \ @@ -304,7 +636,8 @@ case $basic_machine in | tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \ | ubicom32 \ | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \ - | we32k \ + | visium \ + | wasm32 \ | x86 | xc16x | xstormy16 | xtensa \ | z8k | z80) basic_machine=$basic_machine-unknown @@ -318,21 +651,29 @@ case $basic_machine in c6x) basic_machine=tic6x-unknown ;; - m6811 | m68hc11 | m6812 | m68hc12 | picochip) - # Motorola 68HC11/12. + leon|leon[3-9]) + basic_machine=sparc-$basic_machine + ;; + m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip) basic_machine=$basic_machine-unknown - os=-none + os=${os:-none} + ;; + m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65) ;; - m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) + m9s12z | m68hcs12z | hcs12z | s12z) + basic_machine=s12z-unknown + os=${os:-none} ;; ms1) basic_machine=mt-unknown ;; - strongarm | thumb | xscale) basic_machine=arm-unknown ;; - + xgate) + basic_machine=$basic_machine-unknown + os=${os:-none} + ;; xscaleeb) basic_machine=armeb-unknown ;; @@ -347,36 +688,35 @@ case $basic_machine in i*86 | x86_64) basic_machine=$basic_machine-pc ;; - # Object if more than one company name word. - *-*-*) - echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 - exit 1 - ;; # Recognize the basic CPU types with company name. 580-* \ | a29k-* \ + | aarch64-* | aarch64_be-* \ | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ - | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \ + | alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \ | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ | avr-* | avr32-* \ + | ba-* \ | be32-* | be64-* \ | bfin-* | bs2000-* \ | c[123]* | c30-* | [cjt]90-* | c4x-* \ - | clipper-* | craynv-* | cydra-* \ + | c8051-* | clipper-* | craynv-* | csky-* | cydra-* \ | d10v-* | d30v-* | dlx-* \ - | elxsi-* \ + | e2k-* | elxsi-* \ | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \ | h8300-* | h8500-* \ | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ | hexagon-* \ - | i*86-* | i860-* | i960-* | ia64-* \ + | i*86-* | i860-* | i960-* | ia16-* | ia64-* \ | ip2k-* | iq2000-* \ + | k1om-* \ | le32-* | le64-* \ | lm32-* \ | m32c-* | m32r-* | m32rle-* \ | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ - | m88110-* | m88k-* | maxq-* | mcore-* | metag-* | microblaze-* \ + | m88110-* | m88k-* | maxq-* | mcore-* | metag-* \ + | microblaze-* | microblazeel-* \ | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ | mips16-* \ | mips64-* | mips64el-* \ @@ -390,28 +730,36 @@ case $basic_machine in | mips64vr5900-* | mips64vr5900el-* \ | mipsisa32-* | mipsisa32el-* \ | mipsisa32r2-* | mipsisa32r2el-* \ + | mipsisa32r6-* | mipsisa32r6el-* \ | mipsisa64-* | mipsisa64el-* \ | mipsisa64r2-* | mipsisa64r2el-* \ + | mipsisa64r6-* | mipsisa64r6el-* \ | mipsisa64sb1-* | mipsisa64sb1el-* \ | mipsisa64sr71k-* | mipsisa64sr71kel-* \ + | mipsr5900-* | mipsr5900el-* \ | mipstx39-* | mipstx39el-* \ | mmix-* \ + | moxie-* \ | mt-* \ | msp430-* \ | nds32-* | nds32le-* | nds32be-* \ - | nios-* | nios2-* \ + | nfp-* \ + | nios-* | nios2-* | nios2eb-* | nios2el-* \ | none-* | np1-* | ns16k-* | ns32k-* \ | open8-* \ + | or1k*-* \ | orion-* \ | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \ + | pru-* \ | pyramid-* \ - | romp-* | rs6000-* | rx-* \ + | riscv-* | riscv32-* | riscv64-* \ + | rl78-* | romp-* | rs6000-* | rx-* \ | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \ | sparclite-* \ - | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx?-* \ + | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx*-* \ | tahoe-* \ | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ | tile*-* \ @@ -419,6 +767,8 @@ case $basic_machine in | ubicom32-* \ | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \ | vax-* \ + | visium-* \ + | wasm32-* \ | we32k-* \ | x86-* | x86_64-* | xc16x-* | xps100-* \ | xstormy16-* | xtensa*-* \ @@ -431,138 +781,77 @@ case $basic_machine in ;; # Recognize the various machine names and aliases which stand # for a CPU type and a company and sometimes even an OS. - 386bsd) - basic_machine=i386-unknown - os=-bsd - ;; 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) basic_machine=m68000-att ;; 3b*) basic_machine=we32k-att ;; - a29khif) - basic_machine=a29k-amd - os=-udi - ;; abacus) basic_machine=abacus-unknown ;; - adobe68k) - basic_machine=m68010-adobe - os=-scout - ;; alliant | fx80) basic_machine=fx80-alliant ;; altos | altos3068) basic_machine=m68k-altos ;; - am29k) - basic_machine=a29k-none - os=-bsd - ;; amd64) basic_machine=x86_64-pc ;; amd64-*) - basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - amdahl) - basic_machine=580-amdahl - os=-sysv + basic_machine=x86_64-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; amiga | amiga-*) basic_machine=m68k-unknown ;; - amigaos | amigados) - basic_machine=m68k-unknown - os=-amigaos - ;; - amigaunix | amix) - basic_machine=m68k-unknown - os=-sysv4 - ;; - apollo68) - basic_machine=m68k-apollo - os=-sysv - ;; - apollo68bsd) - basic_machine=m68k-apollo - os=-bsd - ;; - aros) - basic_machine=i386-pc - os=-aros - ;; - aux) - basic_machine=m68k-apple - os=-aux - ;; - balance) - basic_machine=ns32k-sequent - os=-dynix - ;; - blackfin) - basic_machine=bfin-unknown - os=-linux + asmjs) + basic_machine=asmjs-unknown ;; blackfin-*) - basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'` - os=-linux + basic_machine=bfin-`echo "$basic_machine" | sed 's/^[^-]*-//'` + os=linux ;; bluegene*) basic_machine=powerpc-ibm - os=-cnk + os=cnk ;; c54x-*) - basic_machine=tic54x-`echo $basic_machine | sed 's/^[^-]*-//'` + basic_machine=tic54x-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; c55x-*) - basic_machine=tic55x-`echo $basic_machine | sed 's/^[^-]*-//'` + basic_machine=tic55x-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; c6x-*) - basic_machine=tic6x-`echo $basic_machine | sed 's/^[^-]*-//'` + basic_machine=tic6x-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; c90) basic_machine=c90-cray - os=-unicos - ;; - cegcc) - basic_machine=arm-unknown - os=-cegcc + os=${os:-unicos} ;; convex-c1) basic_machine=c1-convex - os=-bsd + os=bsd ;; convex-c2) basic_machine=c2-convex - os=-bsd + os=bsd ;; convex-c32) basic_machine=c32-convex - os=-bsd + os=bsd ;; convex-c34) basic_machine=c34-convex - os=-bsd + os=bsd ;; convex-c38) basic_machine=c38-convex - os=-bsd - ;; - cray | j90) - basic_machine=j90-cray - os=-unicos - ;; - craynv) - basic_machine=craynv-cray - os=-unicosmp + os=bsd ;; cr16 | cr16-*) basic_machine=cr16-unknown - os=-elf + os=${os:-elf} ;; crds | unos) basic_machine=m68k-crds @@ -575,7 +864,7 @@ case $basic_machine in ;; crx) basic_machine=crx-unknown - os=-elf + os=${os:-elf} ;; da30 | da30-*) basic_machine=m68k-da30 @@ -585,50 +874,38 @@ case $basic_machine in ;; decsystem10* | dec10*) basic_machine=pdp10-dec - os=-tops10 + os=tops10 ;; decsystem20* | dec20*) basic_machine=pdp10-dec - os=-tops20 + os=tops20 ;; delta | 3300 | motorola-3300 | motorola-delta \ | 3300-motorola | delta-motorola) basic_machine=m68k-motorola ;; - delta88) - basic_machine=m88k-motorola - os=-sysv3 - ;; - dicos) - basic_machine=i686-pc - os=-dicos - ;; - djgpp) - basic_machine=i586-pc - os=-msdosdjgpp - ;; dpx20 | dpx20-*) basic_machine=rs6000-bull - os=-bosx + os=${os:-bosx} ;; - dpx2* | dpx2*-bull) + dpx2*) basic_machine=m68k-bull - os=-sysv3 + os=sysv3 ;; - ebmon29k) - basic_machine=a29k-amd - os=-ebmon + e500v[12]) + basic_machine=powerpc-unknown + os=$os"spe" ;; - elxsi) - basic_machine=elxsi-elxsi - os=-bsd + e500v[12]-*) + basic_machine=powerpc-`echo "$basic_machine" | sed 's/^[^-]*-//'` + os=$os"spe" ;; encore | umax | mmax) basic_machine=ns32k-encore ;; - es1800 | OSE68k | ose68k | ose | OSE) - basic_machine=m68k-ericsson - os=-ose + elxsi) + basic_machine=elxsi-elxsi + os=${os:-bsd} ;; fx2800) basic_machine=i860-alliant @@ -636,45 +913,13 @@ case $basic_machine in genix) basic_machine=ns32k-ns ;; - gmicro) - basic_machine=tron-gmicro - os=-sysv - ;; - go32) - basic_machine=i386-pc - os=-go32 - ;; h3050r* | hiux*) basic_machine=hppa1.1-hitachi - os=-hiuxwe2 - ;; - h8300hms) - basic_machine=h8300-hitachi - os=-hms - ;; - h8300xray) - basic_machine=h8300-hitachi - os=-xray - ;; - h8500hms) - basic_machine=h8500-hitachi - os=-hms - ;; - harris) - basic_machine=m88k-harris - os=-sysv3 + os=hiuxwe2 ;; hp300-*) basic_machine=m68k-hp ;; - hp300bsd) - basic_machine=m68k-hp - os=-bsd - ;; - hp300hpux) - basic_machine=m68k-hp - os=-hpux - ;; hp3k9[0-9][0-9] | hp9[0-9][0-9]) basic_machine=hppa1.0-hp ;; @@ -704,186 +949,82 @@ case $basic_machine in hp9k8[0-9][0-9] | hp8[0-9][0-9]) basic_machine=hppa1.0-hp ;; - hppa-next) - os=-nextstep3 - ;; - hppaosf) - basic_machine=hppa1.1-hp - os=-osf - ;; - hppro) - basic_machine=hppa1.1-hp - os=-proelf - ;; i370-ibm* | ibm*) basic_machine=i370-ibm ;; -# I'm not sure what "Sysv32" means. Should this be sysv3.2? i*86v32) - basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` - os=-sysv32 + basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'` + os=sysv32 ;; i*86v4*) - basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` - os=-sysv4 + basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'` + os=sysv4 ;; i*86v) - basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` - os=-sysv + basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'` + os=sysv ;; i*86sol2) - basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` - os=-solaris2 + basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'` + os=solaris2 ;; - i386mach) - basic_machine=i386-mach - os=-mach - ;; - i386-vsta | vsta) - basic_machine=i386-unknown - os=-vsta + j90 | j90-cray) + basic_machine=j90-cray + os=${os:-unicos} ;; iris | iris4d) basic_machine=mips-sgi case $os in - -irix*) + irix*) ;; *) - os=-irix4 + os=irix4 ;; esac ;; - isi68 | isi) - basic_machine=m68k-isi - os=-sysv - ;; - m68knommu) - basic_machine=m68k-unknown - os=-linux + leon-*|leon[3-9]-*) + basic_machine=sparc-`echo "$basic_machine" | sed 's/-.*//'` ;; m68knommu-*) - basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'` - os=-linux - ;; - m88k-omron*) - basic_machine=m88k-omron - ;; - magnum | m3230) - basic_machine=mips-mips - os=-sysv - ;; - merlin) - basic_machine=ns32k-utek - os=-sysv + basic_machine=m68k-`echo "$basic_machine" | sed 's/^[^-]*-//'` + os=linux ;; - microblaze) + microblaze*) basic_machine=microblaze-xilinx ;; - mingw32) - basic_machine=i386-pc - os=-mingw32 - ;; - mingw32ce) - basic_machine=arm-unknown - os=-mingw32ce - ;; miniframe) basic_machine=m68000-convergent ;; - *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) + *mint | mint[0-9]* | *MiNT | *MiNT[0-9]*) basic_machine=m68k-atari - os=-mint + os=mint ;; mips3*-*) - basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` + basic_machine=`echo "$basic_machine" | sed -e 's/mips3/mips64/'` ;; mips3*) - basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown - ;; - monitor) - basic_machine=m68k-rom68k - os=-coff - ;; - morphos) - basic_machine=powerpc-unknown - os=-morphos - ;; - msdos) - basic_machine=i386-pc - os=-msdos + basic_machine=`echo "$basic_machine" | sed -e 's/mips3/mips64/'`-unknown ;; ms1-*) - basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'` - ;; - mvs) - basic_machine=i370-ibm - os=-mvs - ;; - nacl) - basic_machine=le32-unknown - os=-nacl - ;; - ncr3000) - basic_machine=i486-ncr - os=-sysv4 - ;; - netbsd386) - basic_machine=i386-unknown - os=-netbsd - ;; - netwinder) - basic_machine=armv4l-rebel - os=-linux - ;; - news | news700 | news800 | news900) - basic_machine=m68k-sony - os=-newsos - ;; - news1000) - basic_machine=m68030-sony - os=-newsos + basic_machine=`echo "$basic_machine" | sed -e 's/ms1-/mt-/'` ;; news-3600 | risc-news) basic_machine=mips-sony - os=-newsos + os=newsos ;; - necv70) - basic_machine=v70-nec - os=-sysv - ;; - next | m*-next ) + next | m*-next) basic_machine=m68k-next case $os in - -nextstep* ) + nextstep* ) ;; - -ns2*) - os=-nextstep2 + ns2*) + os=nextstep2 ;; *) - os=-nextstep3 + os=nextstep3 ;; esac ;; - nh3000) - basic_machine=m68k-harris - os=-cxux - ;; - nh[45]000) - basic_machine=m88k-harris - os=-cxux - ;; - nindy960) - basic_machine=i960-intel - os=-nindy - ;; - mon960) - basic_machine=i960-intel - os=-mon960 - ;; - nonstopux) - basic_machine=mips-compaq - os=-nonstopux - ;; np1) basic_machine=np1-gould ;; @@ -896,40 +1037,26 @@ case $basic_machine in nsr-tandem) basic_machine=nsr-tandem ;; + nsv-tandem) + basic_machine=nsv-tandem + ;; + nsx-tandem) + basic_machine=nsx-tandem + ;; op50n-* | op60c-*) basic_machine=hppa1.1-oki - os=-proelf + os=proelf ;; openrisc | openrisc-*) basic_machine=or32-unknown ;; - os400) - basic_machine=powerpc-ibm - os=-os400 - ;; - OSE68000 | ose68000) - basic_machine=m68000-ericsson - os=-ose - ;; - os68k) - basic_machine=m68k-none - os=-os68k - ;; pa-hitachi) basic_machine=hppa1.1-hitachi - os=-hiuxwe2 - ;; - paragon) - basic_machine=i860-intel - os=-osf - ;; - parisc) - basic_machine=hppa-unknown - os=-linux + os=hiuxwe2 ;; parisc-*) - basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'` - os=-linux + basic_machine=hppa-`echo "$basic_machine" | sed 's/^[^-]*-//'` + os=linux ;; pbd) basic_machine=sparc-tti @@ -944,7 +1071,7 @@ case $basic_machine in basic_machine=i386-pc ;; pc98-*) - basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'` + basic_machine=i386-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; pentium | p5 | k5 | k6 | nexgen | viac3) basic_machine=i586-pc @@ -959,16 +1086,16 @@ case $basic_machine in basic_machine=i786-pc ;; pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) - basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` + basic_machine=i586-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; pentiumpro-* | p6-* | 6x86-* | athlon-*) - basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + basic_machine=i686-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) - basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + basic_machine=i686-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; pentium4-*) - basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'` + basic_machine=i786-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; pn) basic_machine=pn-gould @@ -978,39 +1105,27 @@ case $basic_machine in ppc | ppcbe) basic_machine=powerpc-unknown ;; ppc-* | ppcbe-*) - basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` + basic_machine=powerpc-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; - ppcle | powerpclittle | ppc-le | powerpc-little) + ppcle | powerpclittle) basic_machine=powerpcle-unknown ;; ppcle-* | powerpclittle-*) - basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` + basic_machine=powerpcle-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; ppc64) basic_machine=powerpc64-unknown ;; - ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` + ppc64-*) basic_machine=powerpc64-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; - ppc64le | powerpc64little | ppc64-le | powerpc64-little) + ppc64le | powerpc64little) basic_machine=powerpc64le-unknown ;; ppc64le-* | powerpc64little-*) - basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'` + basic_machine=powerpc64le-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; ps2) basic_machine=i386-ibm ;; - pw32) - basic_machine=i586-unknown - os=-pw32 - ;; - rdos) - basic_machine=i386-pc - os=-rdos - ;; - rom68k) - basic_machine=m68k-rom68k - os=-coff - ;; rm[46]00) basic_machine=mips-siemens ;; @@ -1023,10 +1138,6 @@ case $basic_machine in s390x | s390x-*) basic_machine=s390x-ibm ;; - sa29200) - basic_machine=a29k-amd - os=-udi - ;; sb1) basic_machine=mipsisa64sb1-unknown ;; @@ -1035,32 +1146,17 @@ case $basic_machine in ;; sde) basic_machine=mipsisa32-sde - os=-elf - ;; - sei) - basic_machine=mips-sei - os=-seiux + os=${os:-elf} ;; sequent) basic_machine=i386-sequent ;; - sh) - basic_machine=sh-hitachi - os=-hms - ;; sh5el) basic_machine=sh5le-unknown ;; - sh64) - basic_machine=sh64-unknown - ;; - sparclite-wrs | simso-wrs) + simso-wrs) basic_machine=sparclite-wrs - os=-vxworks - ;; - sps7) - basic_machine=m68k-bull - os=-sysv2 + os=vxworks ;; spur) basic_machine=spur-unknown @@ -1068,44 +1164,12 @@ case $basic_machine in st2000) basic_machine=m68k-tandem ;; - stratus) - basic_machine=i860-stratus - os=-sysv4 - ;; strongarm-* | thumb-*) - basic_machine=arm-`echo $basic_machine | sed 's/^[^-]*-//'` + basic_machine=arm-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; sun2) basic_machine=m68000-sun ;; - sun2os3) - basic_machine=m68000-sun - os=-sunos3 - ;; - sun2os4) - basic_machine=m68000-sun - os=-sunos4 - ;; - sun3os3) - basic_machine=m68k-sun - os=-sunos3 - ;; - sun3os4) - basic_machine=m68k-sun - os=-sunos4 - ;; - sun4os3) - basic_machine=sparc-sun - os=-sunos3 - ;; - sun4os4) - basic_machine=sparc-sun - os=-sunos4 - ;; - sun4sol2) - basic_machine=sparc-sun - os=-solaris2 - ;; sun3 | sun3-*) basic_machine=m68k-sun ;; @@ -1115,25 +1179,9 @@ case $basic_machine in sun386 | sun386i | roadrunner) basic_machine=i386-sun ;; - sv1) - basic_machine=sv1-cray - os=-unicos - ;; - symmetry) - basic_machine=i386-sequent - os=-dynix - ;; - t3e) - basic_machine=alphaev5-cray - os=-unicos - ;; - t90) - basic_machine=t90-cray - os=-unicos - ;; tile*) basic_machine=$basic_machine-unknown - os=-linux-gnu + os=linux-gnu ;; tx39) basic_machine=mipstx39-unknown @@ -1141,85 +1189,32 @@ case $basic_machine in tx39el) basic_machine=mipstx39el-unknown ;; - toad1) - basic_machine=pdp10-xkl - os=-tops20 - ;; tower | tower-32) basic_machine=m68k-ncr ;; - tpf) - basic_machine=s390x-ibm - os=-tpf - ;; - udi29k) - basic_machine=a29k-amd - os=-udi - ;; - ultra3) - basic_machine=a29k-nyu - os=-sym1 - ;; - v810 | necv810) - basic_machine=v810-nec - os=-none - ;; - vaxv) - basic_machine=vax-dec - os=-sysv - ;; - vms) - basic_machine=vax-dec - os=-vms - ;; vpp*|vx|vx-*) basic_machine=f301-fujitsu ;; - vxworks960) - basic_machine=i960-wrs - os=-vxworks - ;; - vxworks68) - basic_machine=m68k-wrs - os=-vxworks - ;; - vxworks29k) - basic_machine=a29k-wrs - os=-vxworks - ;; w65*) basic_machine=w65-wdc - os=-none + os=none ;; w89k-*) basic_machine=hppa1.1-winbond - os=-proelf + os=proelf ;; - xbox) - basic_machine=i686-pc - os=-mingw32 + x64) + basic_machine=x86_64-pc ;; xps | xps100) basic_machine=xps100-honeywell ;; xscale-* | xscalee[bl]-*) - basic_machine=`echo $basic_machine | sed 's/^xscale/arm/'` - ;; - ymp) - basic_machine=ymp-cray - os=-unicos - ;; - z8k-*-coff) - basic_machine=z8k-unknown - os=-sim - ;; - z80-*-coff) - basic_machine=z80-unknown - os=-sim + basic_machine=`echo "$basic_machine" | sed 's/^xscale/arm/'` ;; none) basic_machine=none-none - os=-none + os=${os:-none} ;; # Here we handle the default manufacturer of certain CPU types. It is in @@ -1245,10 +1240,6 @@ case $basic_machine in vax) basic_machine=vax-dec ;; - pdp10) - # there are many clones, so DEC is not a safe bet - basic_machine=pdp10-unknown - ;; pdp11) basic_machine=pdp11-dec ;; @@ -1258,9 +1249,6 @@ case $basic_machine in sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele) basic_machine=sh-unknown ;; - sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v) - basic_machine=sparc-sun - ;; cydra) basic_machine=cydra-cydrome ;; @@ -1276,11 +1264,8 @@ case $basic_machine in pmac | pmac-mpw) basic_machine=powerpc-apple ;; - *-unknown) - # Make sure to match an already-canonicalized machine name. - ;; *) - echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + echo Invalid configuration \`"$1"\': machine \`"$basic_machine"\' not recognized 1>&2 exit 1 ;; esac @@ -1288,10 +1273,10 @@ esac # Here we canonicalize certain aliases for manufacturers. case $basic_machine in *-digital*) - basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` + basic_machine=`echo "$basic_machine" | sed 's/digital.*/dec/'` ;; *-commodore*) - basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` + basic_machine=`echo "$basic_machine" | sed 's/commodore.*/cbm/'` ;; *) ;; @@ -1299,200 +1284,246 @@ esac # Decode manufacturer-specific aliases for certain operating systems. -if [ x"$os" != x"" ] +if [ x$os != x ] then case $os in - # First match some system type aliases - # that might get confused with valid system types. - # -solaris* is a basic system type, with this one exception. - -auroraux) - os=-auroraux + # First match some system type aliases that might get confused + # with valid system types. + # solaris* is a basic system type, with this one exception. + auroraux) + os=auroraux ;; - -solaris1 | -solaris1.*) - os=`echo $os | sed -e 's|solaris1|sunos4|'` + bluegene*) + os=cnk ;; - -solaris) - os=-solaris2 + solaris1 | solaris1.*) + os=`echo $os | sed -e 's|solaris1|sunos4|'` ;; - -svr4*) - os=-sysv4 + solaris) + os=solaris2 ;; - -unixware*) - os=-sysv4.2uw + unixware*) + os=sysv4.2uw ;; - -gnu/linux*) + gnu/linux*) os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` ;; - # First accept the basic system types. + # es1800 is here to avoid being matched by es* (a different OS) + es1800*) + os=ose + ;; + # Some version numbers need modification + chorusos*) + os=chorusos + ;; + isc) + os=isc2.2 + ;; + sco6) + os=sco5v6 + ;; + sco5) + os=sco3.2v5 + ;; + sco4) + os=sco3.2v4 + ;; + sco3.2.[4-9]*) + os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` + ;; + sco3.2v[4-9]* | sco5v6*) + # Don't forget version if it is 3.2v4 or newer. + ;; + scout) + # Don't match below + ;; + sco*) + os=sco3.2v2 + ;; + psos*) + os=psos + ;; + # Now accept the basic system types. # The portable systems comes first. - # Each alternative MUST END IN A *, to match a version number. - # -sysv* is not here because it comes later, after sysvr4. - -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ - | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\ - | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \ - | -sym* | -kopensolaris* \ - | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ - | -aos* | -aros* \ - | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ - | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ - | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \ - | -openbsd* | -solidbsd* \ - | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ - | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ - | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ - | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ - | -chorusos* | -chorusrdb* | -cegcc* \ - | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ - | -mingw32* | -linux-gnu* | -linux-android* \ - | -linux-newlib* | -linux-uclibc* \ - | -uxpv* | -beos* | -mpeix* | -udk* \ - | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ - | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ - | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ - | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ - | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ - | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \ - | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es*) + # Each alternative MUST end in a * to match a version number. + # sysv* is not here because it comes later, after sysvr4. + gnu* | bsd* | mach* | minix* | genix* | ultrix* | irix* \ + | *vms* | esix* | aix* | cnk* | sunos | sunos[34]*\ + | hpux* | unos* | osf* | luna* | dgux* | auroraux* | solaris* \ + | sym* | kopensolaris* | plan9* \ + | amigaos* | amigados* | msdos* | newsos* | unicos* | aof* \ + | aos* | aros* | cloudabi* | sortix* \ + | nindy* | vxsim* | vxworks* | ebmon* | hms* | mvs* \ + | clix* | riscos* | uniplus* | iris* | rtu* | xenix* \ + | knetbsd* | mirbsd* | netbsd* \ + | bitrig* | openbsd* | solidbsd* | libertybsd* \ + | ekkobsd* | kfreebsd* | freebsd* | riscix* | lynxos* \ + | bosx* | nextstep* | cxux* | aout* | elf* | oabi* \ + | ptx* | coff* | ecoff* | winnt* | domain* | vsta* \ + | udi* | eabi* | lites* | ieee* | go32* | aux* | hcos* \ + | chorusrdb* | cegcc* | glidix* \ + | cygwin* | msys* | pe* | moss* | proelf* | rtems* \ + | midipix* | mingw32* | mingw64* | linux-gnu* | linux-android* \ + | linux-newlib* | linux-musl* | linux-uclibc* \ + | uxpv* | beos* | mpeix* | udk* | moxiebox* \ + | interix* | uwin* | mks* | rhapsody* | darwin* \ + | openstep* | oskit* | conix* | pw32* | nonstopux* \ + | storm-chaos* | tops10* | tenex* | tops20* | its* \ + | os2* | vos* | palmos* | uclinux* | nucleus* \ + | morphos* | superux* | rtmk* | windiss* \ + | powermax* | dnix* | nx6 | nx7 | sei* | dragonfly* \ + | skyos* | haiku* | rdos* | toppers* | drops* | es* \ + | onefs* | tirtos* | phoenix* | fuchsia* | redox* | bme* \ + | midnightbsd*) # Remember, each alternative MUST END IN *, to match a version number. ;; - -qnx*) + qnx*) case $basic_machine in x86-* | i*86-*) ;; *) - os=-nto$os + os=nto-$os ;; esac ;; - -nto-qnx*) + hiux*) + os=hiuxwe2 ;; - -nto*) - os=`echo $os | sed -e 's|nto|nto-qnx|'` + nto-qnx*) ;; - -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ - | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \ - | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) + nto*) + os=`echo $os | sed -e 's|nto|nto-qnx|'` ;; - -mac*) - os=`echo $os | sed -e 's|mac|macos|'` + sim | xray | os68k* | v88r* \ + | windows* | osx | abug | netware* | os9* \ + | macos* | mpw* | magic* | mmixware* | mon960* | lnews*) ;; - -linux-dietlibc) - os=-linux-dietlibc + linux-dietlibc) + os=linux-dietlibc ;; - -linux*) + linux*) os=`echo $os | sed -e 's|linux|linux-gnu|'` ;; - -sunos5*) - os=`echo $os | sed -e 's|sunos5|solaris2|'` + lynx*178) + os=lynxos178 ;; - -sunos6*) - os=`echo $os | sed -e 's|sunos6|solaris3|'` + lynx*5) + os=lynxos5 ;; - -opened*) - os=-openedition + lynx*) + os=lynxos ;; - -os400*) - os=-os400 + mac*) + os=`echo "$os" | sed -e 's|mac|macos|'` ;; - -wince*) - os=-wince + opened*) + os=openedition ;; - -osfrose*) - os=-osfrose + os400*) + os=os400 ;; - -osf*) - os=-osf + sunos5*) + os=`echo "$os" | sed -e 's|sunos5|solaris2|'` ;; - -utek*) - os=-bsd + sunos6*) + os=`echo "$os" | sed -e 's|sunos6|solaris3|'` ;; - -dynix*) - os=-bsd + wince*) + os=wince ;; - -acis*) - os=-aos + utek*) + os=bsd ;; - -atheos*) - os=-atheos + dynix*) + os=bsd ;; - -syllable*) - os=-syllable + acis*) + os=aos ;; - -386bsd) - os=-bsd + atheos*) + os=atheos ;; - -ctix* | -uts*) - os=-sysv + syllable*) + os=syllable ;; - -nova*) - os=-rtmk-nova + 386bsd) + os=bsd + ;; + ctix* | uts*) + os=sysv + ;; + nova*) + os=rtmk-nova ;; - -ns2 ) - os=-nextstep2 + ns2) + os=nextstep2 ;; - -nsk*) - os=-nsk + nsk*) + os=nsk ;; # Preserve the version number of sinix5. - -sinix5.*) + sinix5.*) os=`echo $os | sed -e 's|sinix|sysv|'` ;; - -sinix*) - os=-sysv4 + sinix*) + os=sysv4 ;; - -tpf*) - os=-tpf + tpf*) + os=tpf ;; - -triton*) - os=-sysv3 + triton*) + os=sysv3 ;; - -oss*) - os=-sysv3 + oss*) + os=sysv3 ;; - -svr4) - os=-sysv4 + svr4*) + os=sysv4 ;; - -svr3) - os=-sysv3 + svr3) + os=sysv3 ;; - -sysvr4) - os=-sysv4 + sysvr4) + os=sysv4 ;; - # This must come after -sysvr4. - -sysv*) + # This must come after sysvr4. + sysv*) ;; - -ose*) - os=-ose + ose*) + os=ose ;; - -es1800*) - os=-ose + *mint | mint[0-9]* | *MiNT | MiNT[0-9]*) + os=mint ;; - -xenix) - os=-xenix + zvmoe) + os=zvmoe ;; - -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) - os=-mint + dicos*) + os=dicos ;; - -aros*) - os=-aros - ;; - -kaos*) - os=-kaos + pikeos*) + # Until real need of OS specific support for + # particular features comes up, bare metal + # configurations are quite functional. + case $basic_machine in + arm*) + os=eabi + ;; + *) + os=elf + ;; + esac ;; - -zvmoe) - os=-zvmoe + nacl*) ;; - -dicos*) - os=-dicos + ios) ;; - -nacl*) + none) ;; - -none) + *-eabi) ;; *) - # Get rid of the `-' at the beginning of $os. - os=`echo $os | sed 's/[^-]*-//'` - echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 + echo Invalid configuration \`"$1"\': system \`"$os"\' not recognized 1>&2 exit 1 ;; esac @@ -1510,173 +1541,179 @@ else case $basic_machine in score-*) - os=-elf + os=elf ;; spu-*) - os=-elf + os=elf ;; *-acorn) - os=-riscix1.2 + os=riscix1.2 ;; arm*-rebel) - os=-linux + os=linux ;; arm*-semi) - os=-aout + os=aout ;; c4x-* | tic4x-*) - os=-coff + os=coff + ;; + c8051-*) + os=elf + ;; + clipper-intergraph) + os=clix + ;; + hexagon-*) + os=elf ;; tic54x-*) - os=-coff + os=coff ;; tic55x-*) - os=-coff + os=coff ;; tic6x-*) - os=-coff + os=coff ;; # This must come before the *-dec entry. pdp10-*) - os=-tops20 + os=tops20 ;; pdp11-*) - os=-none + os=none ;; *-dec | vax-*) - os=-ultrix4.2 + os=ultrix4.2 ;; m68*-apollo) - os=-domain + os=domain ;; i386-sun) - os=-sunos4.0.2 + os=sunos4.0.2 ;; m68000-sun) - os=-sunos3 - # This also exists in the configure program, but was not the - # default. - # os=-sunos4 + os=sunos3 ;; m68*-cisco) - os=-aout + os=aout ;; mep-*) - os=-elf + os=elf ;; mips*-cisco) - os=-elf + os=elf ;; mips*-*) - os=-elf + os=elf ;; or32-*) - os=-coff + os=coff ;; *-tti) # must be before sparc entry or we get the wrong os. - os=-sysv3 + os=sysv3 ;; sparc-* | *-sun) - os=-sunos4.1.1 + os=sunos4.1.1 ;; - *-be) - os=-beos + pru-*) + os=elf ;; - *-haiku) - os=-haiku + *-be) + os=beos ;; *-ibm) - os=-aix + os=aix ;; *-knuth) - os=-mmixware + os=mmixware ;; *-wec) - os=-proelf + os=proelf ;; *-winbond) - os=-proelf + os=proelf ;; *-oki) - os=-proelf + os=proelf ;; *-hp) - os=-hpux + os=hpux ;; *-hitachi) - os=-hiux + os=hiux ;; i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) - os=-sysv + os=sysv ;; *-cbm) - os=-amigaos + os=amigaos ;; *-dg) - os=-dgux + os=dgux ;; *-dolphin) - os=-sysv3 + os=sysv3 ;; m68k-ccur) - os=-rtu + os=rtu ;; m88k-omron*) - os=-luna + os=luna ;; - *-next ) - os=-nextstep + *-next) + os=nextstep ;; *-sequent) - os=-ptx + os=ptx ;; *-crds) - os=-unos + os=unos ;; *-ns) - os=-genix + os=genix ;; i370-*) - os=-mvs - ;; - *-next) - os=-nextstep3 + os=mvs ;; *-gould) - os=-sysv + os=sysv ;; *-highlevel) - os=-bsd + os=bsd ;; *-encore) - os=-bsd + os=bsd ;; *-sgi) - os=-irix + os=irix ;; *-siemens) - os=-sysv4 + os=sysv4 ;; *-masscomp) - os=-rtu + os=rtu ;; f30[01]-fujitsu | f700-fujitsu) - os=-uxpv + os=uxpv ;; *-rom68k) - os=-coff + os=coff ;; *-*bug) - os=-coff + os=coff ;; *-apple) - os=-macos + os=macos ;; *-atari*) - os=-mint + os=mint + ;; + *-wrs) + os=vxworks ;; *) - os=-none + os=none ;; esac fi @@ -1687,79 +1724,82 @@ vendor=unknown case $basic_machine in *-unknown) case $os in - -riscix*) + riscix*) vendor=acorn ;; - -sunos*) + sunos*) vendor=sun ;; - -cnk*|-aix*) + cnk*|-aix*) vendor=ibm ;; - -beos*) + beos*) vendor=be ;; - -hpux*) + hpux*) vendor=hp ;; - -mpeix*) + mpeix*) vendor=hp ;; - -hiux*) + hiux*) vendor=hitachi ;; - -unos*) + unos*) vendor=crds ;; - -dgux*) + dgux*) vendor=dg ;; - -luna*) + luna*) vendor=omron ;; - -genix*) + genix*) vendor=ns ;; - -mvs* | -opened*) + clix*) + vendor=intergraph + ;; + mvs* | opened*) vendor=ibm ;; - -os400*) + os400*) vendor=ibm ;; - -ptx*) + ptx*) vendor=sequent ;; - -tpf*) + tpf*) vendor=ibm ;; - -vxsim* | -vxworks* | -windiss*) + vxsim* | vxworks* | windiss*) vendor=wrs ;; - -aux*) + aux*) vendor=apple ;; - -hms*) + hms*) vendor=hitachi ;; - -mpw* | -macos*) + mpw* | macos*) vendor=apple ;; - -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + *mint | mint[0-9]* | *MiNT | MiNT[0-9]*) vendor=atari ;; - -vos*) + vos*) vendor=stratus ;; esac - basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` + basic_machine=`echo "$basic_machine" | sed "s/unknown/$vendor/"` ;; esac -echo $basic_machine$os +echo "$basic_machine-$os" exit # Local variables: -# eval: (add-hook 'write-file-hooks 'time-stamp) +# eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" diff --git a/cextern/wcslib/config/elisp-comp b/cextern/wcslib/config/elisp-comp deleted file mode 100755 index 00898596b615..000000000000 --- a/cextern/wcslib/config/elisp-comp +++ /dev/null @@ -1,60 +0,0 @@ -#!/bin/sh -# Copyright (C) 1995, 2000, 2003 Free Software Foundation, Inc. -# Franc,ois Pinard , 1995. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2, or (at your option) -# any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -# As a special exception to the GNU General Public License, if you -# distribute this file as part of a program that contains a -# configuration script generated by Autoconf, you may include it under -# the same distribution terms that you use for the rest of that program. - -# This script byte-compiles all `.el' files which are part of its -# arguments, using GNU Emacs, and put the resulting `.elc' files into -# the current directory, so disregarding the original directories used -# in `.el' arguments. -# -# This script manages in such a way that all Emacs LISP files to -# be compiled are made visible between themselves, in the event -# they require or load-library one another. - -if test $# = 0; then - echo 1>&2 "No files given to $0" - exit 1 -fi - -if test -z "$EMACS" || test "$EMACS" = "t"; then - # Value of "t" means we are running in a shell under Emacs. - # Just assume Emacs is called "emacs". - EMACS=emacs -fi - -tempdir=elc.$$ - -# Cleanup the temporary directory on exit. -trap 'status=$?; rm -rf "$tempdir" && exit $status' 0 -trap '(exit $?); exit' 1 2 13 15 - -mkdir $tempdir -cp "$@" $tempdir - -( - cd $tempdir - echo "(setq load-path (cons nil load-path))" > script - $EMACS -batch -q -l script -f batch-byte-compile *.el || exit $? - mv *.elc .. -) || exit $? - -(exit 0); exit diff --git a/cextern/wcslib/config/install-sh b/cextern/wcslib/config/install-sh index f5061e7e2696..377bb8687ffe 100755 --- a/cextern/wcslib/config/install-sh +++ b/cextern/wcslib/config/install-sh @@ -1,7 +1,7 @@ #!/bin/sh # install - install a program, script, or datafile -scriptversion=2003-09-24.23 +scriptversion=2011-11-20.07; # UTC # This originates from X11R5 (mit/util/scripts/install.sh), which was # later released in X11R6 (xc/config/util/install.sh) with the @@ -35,261 +35,493 @@ scriptversion=2003-09-24.23 # FSF changes to this file are in the public domain. # # Calling this script install-sh is preferred over install.sh, to prevent -# `make' implicit rules from creating a file called install from it +# 'make' implicit rules from creating a file called install from it # when there is no Makefile. # # This script is compatible with the BSD install script, but was written -# from scratch. It can only install one file at a time, a restriction -# shared with many OS's install programs. +# from scratch. + +nl=' +' +IFS=" "" $nl" # set DOITPROG to echo to test this script # Don't use :- since 4.3BSD and earlier shells don't like it. -doit="${DOITPROG-}" - -# put in absolute paths if you don't have them in your path; or use env. vars. - -mvprog="${MVPROG-mv}" -cpprog="${CPPROG-cp}" -chmodprog="${CHMODPROG-chmod}" -chownprog="${CHOWNPROG-chown}" -chgrpprog="${CHGRPPROG-chgrp}" -stripprog="${STRIPPROG-strip}" -rmprog="${RMPROG-rm}" -mkdirprog="${MKDIRPROG-mkdir}" - -transformbasename= -transform_arg= -instcmd="$mvprog" -chmodcmd="$chmodprog 0755" -chowncmd= +doit=${DOITPROG-} +if test -z "$doit"; then + doit_exec=exec +else + doit_exec=$doit +fi + +# Put in absolute file names if you don't have them in your path; +# or use environment vars. + +chgrpprog=${CHGRPPROG-chgrp} +chmodprog=${CHMODPROG-chmod} +chownprog=${CHOWNPROG-chown} +cmpprog=${CMPPROG-cmp} +cpprog=${CPPROG-cp} +mkdirprog=${MKDIRPROG-mkdir} +mvprog=${MVPROG-mv} +rmprog=${RMPROG-rm} +stripprog=${STRIPPROG-strip} + +posix_glob='?' +initialize_posix_glob=' + test "$posix_glob" != "?" || { + if (set -f) 2>/dev/null; then + posix_glob= + else + posix_glob=: + fi + } +' + +posix_mkdir= + +# Desired mode of installed file. +mode=0755 + chgrpcmd= -stripcmd= +chmodcmd=$chmodprog +chowncmd= +mvcmd=$mvprog rmcmd="$rmprog -f" -mvcmd="$mvprog" +stripcmd= + src= dst= dir_arg= +dst_arg= -usage="Usage: $0 [OPTION]... SRCFILE DSTFILE - or: $0 -d DIR1 DIR2... +copy_on_change=false +no_target_directory= -In the first form, install SRCFILE to DSTFILE, removing SRCFILE by default. -In the second, create the directory path DIR. +usage="\ +Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE + or: $0 [OPTION]... SRCFILES... DIRECTORY + or: $0 [OPTION]... -t DIRECTORY SRCFILES... + or: $0 [OPTION]... -d DIRECTORIES... + +In the 1st form, copy SRCFILE to DSTFILE. +In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. +In the 4th, create DIRECTORIES. Options: --b=TRANSFORMBASENAME --c copy source (using $cpprog) instead of moving (using $mvprog). --d create directories instead of installing files. --g GROUP $chgrp installed files to GROUP. --m MODE $chmod installed files to MODE. --o USER $chown installed files to USER. --s strip installed files (using $stripprog). --t=TRANSFORM ---help display this help and exit. ---version display version info and exit. + --help display this help and exit. + --version display version info and exit. + + -c (ignored) + -C install only if different (preserve the last data modification time) + -d create directories instead of installing files. + -g GROUP $chgrpprog installed files to GROUP. + -m MODE $chmodprog installed files to MODE. + -o USER $chownprog installed files to USER. + -s $stripprog installed files. + -t DIRECTORY install into DIRECTORY. + -T report an error if DSTFILE is a directory. Environment variables override the default commands: - CHGRPPROG CHMODPROG CHOWNPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG + CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG + RMPROG STRIPPROG " -while test -n "$1"; do +while test $# -ne 0; do case $1 in - -b=*) transformbasename=`echo $1 | sed 's/-b=//'` - shift - continue;; + -c) ;; - -c) instcmd=$cpprog - shift - continue;; + -C) copy_on_change=true;; - -d) dir_arg=true - shift - continue;; + -d) dir_arg=true;; -g) chgrpcmd="$chgrpprog $2" - shift - shift - continue;; + shift;; - --help) echo "$usage"; exit 0;; + --help) echo "$usage"; exit $?;; - -m) chmodcmd="$chmodprog $2" - shift - shift - continue;; + -m) mode=$2 + case $mode in + *' '* | *' '* | *' +'* | *'*'* | *'?'* | *'['*) + echo "$0: invalid mode: $mode" >&2 + exit 1;; + esac + shift;; -o) chowncmd="$chownprog $2" - shift - shift - continue;; - - -s) stripcmd=$stripprog - shift - continue;; - - -t=*) transformarg=`echo $1 | sed 's/-t=//'` - shift - continue;; - - --version) echo "$0 $scriptversion"; exit 0;; - - *) if test -z "$src"; then - src=$1 - else - # this colon is to work around a 386BSD /bin/sh bug - : - dst=$1 - fi - shift - continue;; - esac -done + shift;; -if test -z "$src"; then - echo "$0: no input file specified." >&2 - exit 1 -fi + -s) stripcmd=$stripprog;; -# Protect names starting with `-'. -case $src in - -*) src=./$src ;; -esac + -t) dst_arg=$2 + # Protect names problematic for 'test' and other utilities. + case $dst_arg in + -* | [=\(\)!]) dst_arg=./$dst_arg;; + esac + shift;; -if test -n "$dir_arg"; then - dst=$src - src= + -T) no_target_directory=true;; - if test -d "$dst"; then - instcmd=: - chmodcmd= - else - instcmd=$mkdirprog - fi -else - # Waiting for this to be detected by the "$instcmd $src $dsttmp" command - # might cause directories to be created, which would be especially bad - # if $src (and thus $dsttmp) contains '*'. - if test ! -f "$src" && test ! -d "$src"; then - echo "$0: $src does not exist." >&2 - exit 1 - fi + --version) echo "$0 $scriptversion"; exit $?;; - if test -z "$dst"; then - echo "$0: no destination specified." >&2 - exit 1 - fi + --) shift + break;; + + -*) echo "$0: invalid option: $1" >&2 + exit 1;; - # Protect names starting with `-'. - case $dst in - -*) dst=./$dst ;; + *) break;; esac + shift +done - # If destination is a directory, append the input filename; won't work - # if double slashes aren't ignored. - if test -d "$dst"; then - dst=$dst/`basename "$src"` +if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then + # When -d is used, all remaining arguments are directories to create. + # When -t is used, the destination is already specified. + # Otherwise, the last argument is the destination. Remove it from $@. + for arg + do + if test -n "$dst_arg"; then + # $@ is not empty: it contains at least $arg. + set fnord "$@" "$dst_arg" + shift # fnord + fi + shift # arg + dst_arg=$arg + # Protect names problematic for 'test' and other utilities. + case $dst_arg in + -* | [=\(\)!]) dst_arg=./$dst_arg;; + esac + done +fi + +if test $# -eq 0; then + if test -z "$dir_arg"; then + echo "$0: no input file specified." >&2 + exit 1 fi + # It's OK to call 'install-sh -d' without argument. + # This can happen when creating conditional directories. + exit 0 fi -# This sed command emulates the dirname command. -dstdir=`echo "$dst" | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` +if test -z "$dir_arg"; then + do_exit='(exit $ret); exit $ret' + trap "ret=129; $do_exit" 1 + trap "ret=130; $do_exit" 2 + trap "ret=141; $do_exit" 13 + trap "ret=143; $do_exit" 15 + + # Set umask so as not to create temps with too-generous modes. + # However, 'strip' requires both read and write access to temps. + case $mode in + # Optimize common cases. + *644) cp_umask=133;; + *755) cp_umask=22;; + + *[0-7]) + if test -z "$stripcmd"; then + u_plus_rw= + else + u_plus_rw='% 200' + fi + cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; + *) + if test -z "$stripcmd"; then + u_plus_rw= + else + u_plus_rw=,u+rw + fi + cp_umask=$mode$u_plus_rw;; + esac +fi -# Make sure that the destination directory exists. +for src +do + # Protect names problematic for 'test' and other utilities. + case $src in + -* | [=\(\)!]) src=./$src;; + esac -# Skip lots of stat calls in the usual case. -if test ! -d "$dstdir"; then - defaultIFS=' - ' - IFS="${IFS-$defaultIFS}" + if test -n "$dir_arg"; then + dst=$src + dstdir=$dst + test -d "$dstdir" + dstdir_status=$? + else - oIFS=$IFS - # Some sh's can't handle IFS=/ for some reason. - IFS='%' - set - `echo "$dstdir" | sed -e 's@/@%@g' -e 's@^%@/@'` - IFS=$oIFS + # Waiting for this to be detected by the "$cpprog $src $dsttmp" command + # might cause directories to be created, which would be especially bad + # if $src (and thus $dsttmp) contains '*'. + if test ! -f "$src" && test ! -d "$src"; then + echo "$0: $src does not exist." >&2 + exit 1 + fi - pathcomp= + if test -z "$dst_arg"; then + echo "$0: no destination specified." >&2 + exit 1 + fi + dst=$dst_arg + + # If destination is a directory, append the input filename; won't work + # if double slashes aren't ignored. + if test -d "$dst"; then + if test -n "$no_target_directory"; then + echo "$0: $dst_arg: Is a directory" >&2 + exit 1 + fi + dstdir=$dst + dst=$dstdir/`basename "$src"` + dstdir_status=0 + else + # Prefer dirname, but fall back on a substitute if dirname fails. + dstdir=` + (dirname "$dst") 2>/dev/null || + expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$dst" : 'X\(//\)[^/]' \| \ + X"$dst" : 'X\(//\)$' \| \ + X"$dst" : 'X\(/\)' \| . 2>/dev/null || + echo X"$dst" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q' + ` + + test -d "$dstdir" + dstdir_status=$? + fi + fi - while test $# -ne 0 ; do - pathcomp=$pathcomp$1 - shift - test -d "$pathcomp" || $mkdirprog "$pathcomp" - pathcomp=$pathcomp/ - done -fi + obsolete_mkdir_used=false + + if test $dstdir_status != 0; then + case $posix_mkdir in + '') + # Create intermediate dirs using mode 755 as modified by the umask. + # This is like FreeBSD 'install' as of 1997-10-28. + umask=`umask` + case $stripcmd.$umask in + # Optimize common cases. + *[2367][2367]) mkdir_umask=$umask;; + .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;; + + *[0-7]) + mkdir_umask=`expr $umask + 22 \ + - $umask % 100 % 40 + $umask % 20 \ + - $umask % 10 % 4 + $umask % 2 + `;; + *) mkdir_umask=$umask,go-w;; + esac + + # With -d, create the new directory with the user-specified mode. + # Otherwise, rely on $mkdir_umask. + if test -n "$dir_arg"; then + mkdir_mode=-m$mode + else + mkdir_mode= + fi + + posix_mkdir=false + case $umask in + *[123567][0-7][0-7]) + # POSIX mkdir -p sets u+wx bits regardless of umask, which + # is incompatible with FreeBSD 'install' when (umask & 300) != 0. + ;; + *) + tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ + trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0 + + if (umask $mkdir_umask && + exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1 + then + if test -z "$dir_arg" || { + # Check for POSIX incompatibilities with -m. + # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or + # other-writable bit of parent directory when it shouldn't. + # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. + ls_ld_tmpdir=`ls -ld "$tmpdir"` + case $ls_ld_tmpdir in + d????-?r-*) different_mode=700;; + d????-?--*) different_mode=755;; + *) false;; + esac && + $mkdirprog -m$different_mode -p -- "$tmpdir" && { + ls_ld_tmpdir_1=`ls -ld "$tmpdir"` + test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" + } + } + then posix_mkdir=: + fi + rmdir "$tmpdir/d" "$tmpdir" + else + # Remove any dirs left behind by ancient mkdir implementations. + rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null + fi + trap '' 0;; + esac;; + esac + + if + $posix_mkdir && ( + umask $mkdir_umask && + $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" + ) + then : + else -if test -n "$dir_arg"; then - $doit $instcmd "$dst" \ - && { test -z "$chowncmd" || $doit $chowncmd "$dst"; } \ - && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } \ - && { test -z "$stripcmd" || $doit $stripcmd "$dst"; } \ - && { test -z "$chmodcmd" || $doit $chmodcmd "$dst"; } + # The umask is ridiculous, or mkdir does not conform to POSIX, + # or it failed possibly due to a race condition. Create the + # directory the slow way, step by step, checking for races as we go. + + case $dstdir in + /*) prefix='/';; + [-=\(\)!]*) prefix='./';; + *) prefix='';; + esac + + eval "$initialize_posix_glob" + + oIFS=$IFS + IFS=/ + $posix_glob set -f + set fnord $dstdir + shift + $posix_glob set +f + IFS=$oIFS + + prefixes= + + for d + do + test X"$d" = X && continue + + prefix=$prefix$d + if test -d "$prefix"; then + prefixes= + else + if $posix_mkdir; then + (umask=$mkdir_umask && + $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break + # Don't fail if two instances are running concurrently. + test -d "$prefix" || exit 1 + else + case $prefix in + *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; + *) qprefix=$prefix;; + esac + prefixes="$prefixes '$qprefix'" + fi + fi + prefix=$prefix/ + done + + if test -n "$prefixes"; then + # Don't fail if two instances are running concurrently. + (umask $mkdir_umask && + eval "\$doit_exec \$mkdirprog $prefixes") || + test -d "$dstdir" || exit 1 + obsolete_mkdir_used=true + fi + fi + fi -else - # If we're going to rename the final executable, determine the name now. - if test -z "$transformarg"; then - dstfile=`basename "$dst"` + if test -n "$dir_arg"; then + { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && + { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && + { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || + test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 else - dstfile=`basename "$dst" $transformbasename \ - | sed $transformarg`$transformbasename - fi - # don't allow the sed command to completely eliminate the filename. - test -z "$dstfile" && dstfile=`basename "$dst"` - - # Make a couple of temp file names in the proper directory. - dsttmp=$dstdir/_inst.$$_ - rmtmp=$dstdir/_rm.$$_ - - # Trap to clean up those temp files at exit. - trap 'status=$?; rm -f "$dsttmp" "$rmtmp" && exit $status' 0 - trap '(exit $?); exit' 1 2 13 15 - - # Move or copy the file name to the temp name - $doit $instcmd "$src" "$dsttmp" && - - # and set any options; do chmod last to preserve setuid bits. - # - # If any of these fail, we abort the whole thing. If we want to - # ignore errors from any of these, just make sure not to ignore - # errors from the above "$doit $instcmd $src $dsttmp" command. - # - { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } \ - && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } \ - && { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } \ - && { test -z "$chmodcmd" || $doit $chmodcmd "$dsttmp"; } && - - # Now remove or move aside any old file at destination location. We - # try this two ways since rm can't unlink itself on some systems and - # the destination file might be busy for other reasons. In this case, - # the final cleanup might fail but the new file should still install - # successfully. - { - if test -f "$dstdir/$dstfile"; then - $doit $rmcmd -f "$dstdir/$dstfile" 2>/dev/null \ - || $doit $mvcmd -f "$dstdir/$dstfile" "$rmtmp" 2>/dev/null \ - || { - echo "$0: cannot unlink or rename $dstdir/$dstfile" >&2 - (exit 1); exit - } + # Make a couple of temp file names in the proper directory. + dsttmp=$dstdir/_inst.$$_ + rmtmp=$dstdir/_rm.$$_ + + # Trap to clean up those temp files at exit. + trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 + + # Copy the file name to the temp name. + (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") && + + # and set any options; do chmod last to preserve setuid bits. + # + # If any of these fail, we abort the whole thing. If we want to + # ignore errors from any of these, just make sure not to ignore + # errors from the above "$doit $cpprog $src $dsttmp" command. + # + { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } && + { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } && + { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } && + { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && + + # If -C, don't bother to copy if it wouldn't change the file. + if $copy_on_change && + old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && + new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && + + eval "$initialize_posix_glob" && + $posix_glob set -f && + set X $old && old=:$2:$4:$5:$6 && + set X $new && new=:$2:$4:$5:$6 && + $posix_glob set +f && + + test "$old" = "$new" && + $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 + then + rm -f "$dsttmp" else - : - fi - } && - - # Now rename the file to the real destination. - $doit $mvcmd "$dsttmp" "$dstdir/$dstfile" -fi && + # Rename the file to the real destination. + $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || + + # The rename failed, perhaps because mv can't rename something else + # to itself, or perhaps because mv is so ancient that it does not + # support -f. + { + # Now remove or move aside any old file at destination location. + # We try this two ways since rm can't unlink itself on some + # systems and the destination file might be busy for other + # reasons. In this case, the final cleanup might fail but the new + # file should still install successfully. + { + test ! -f "$dst" || + $doit $rmcmd -f "$dst" 2>/dev/null || + { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && + { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; } + } || + { echo "$0: cannot unlink or rename $dst" >&2 + (exit 1); exit 1 + } + } && + + # Now rename the file to the real destination. + $doit $mvcmd "$dsttmp" "$dst" + } + fi || exit 1 -# The final little trick to "correctly" pass the exit status to the exit trap. -{ - (exit 0); exit -} + trap '' 0 + fi +done # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" -# time-stamp-end: "$" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" # End: diff --git a/cextern/wcslib/config/mdate-sh b/cextern/wcslib/config/mdate-sh deleted file mode 100755 index b610b47a65d1..000000000000 --- a/cextern/wcslib/config/mdate-sh +++ /dev/null @@ -1,133 +0,0 @@ -#!/bin/sh -# Get modification time of a file or directory and pretty-print it. -# Copyright (C) 1995, 1996, 1997, 2003 Free Software Foundation, Inc. -# written by Ulrich Drepper , June 1995 -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2, or (at your option) -# any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -# As a special exception to the GNU General Public License, if you -# distribute this file as part of a program that contains a -# configuration script generated by Autoconf, you may include it under -# the same distribution terms that you use for the rest of that program. - -# Prevent date giving response in another language. -LANG=C -export LANG -LC_ALL=C -export LC_ALL -LC_TIME=C -export LC_TIME - -save_arg1="$1" - -# Find out how to get the extended ls output of a file or directory. -if ls -L /dev/null 1>/dev/null 2>&1; then - ls_command='ls -L -l -d' -else - ls_command='ls -l -d' -fi - -# A `ls -l' line looks as follows on OS/2. -# drwxrwx--- 0 Aug 11 2001 foo -# This differs from Unix, which adds ownership information. -# drwxrwx--- 2 root root 4096 Aug 11 2001 foo -# -# To find the date, we split the line on spaces and iterate on words -# until we find a month. This cannot work with files whose owner is a -# user named `Jan', or `Feb', etc. However, it's unlikely that `/' -# will be owned by a user whose name is a month. So we first look at -# the extended ls output of the root directory to decide how many -# words should be skipped to get the date. - -# On HPUX /bin/sh, "set" interprets "-rw-r--r--" as options, so the "x" below. -set - x`$ls_command /` - -# Find which argument is the month. -month= -command= -until test $month -do - shift - # Add another shift to the command. - command="$command shift;" - case $1 in - Jan) month=January; nummonth=1;; - Feb) month=February; nummonth=2;; - Mar) month=March; nummonth=3;; - Apr) month=April; nummonth=4;; - May) month=May; nummonth=5;; - Jun) month=June; nummonth=6;; - Jul) month=July; nummonth=7;; - Aug) month=August; nummonth=8;; - Sep) month=September; nummonth=9;; - Oct) month=October; nummonth=10;; - Nov) month=November; nummonth=11;; - Dec) month=December; nummonth=12;; - esac -done - -# Get the extended ls output of the file or directory. -set - x`eval "$ls_command \"\$save_arg1\""` - -# Remove all preceding arguments -eval $command - -# Get the month. Next argument is day, followed by the year or time. -case $1 in - Jan) month=January; nummonth=1;; - Feb) month=February; nummonth=2;; - Mar) month=March; nummonth=3;; - Apr) month=April; nummonth=4;; - May) month=May; nummonth=5;; - Jun) month=June; nummonth=6;; - Jul) month=July; nummonth=7;; - Aug) month=August; nummonth=8;; - Sep) month=September; nummonth=9;; - Oct) month=October; nummonth=10;; - Nov) month=November; nummonth=11;; - Dec) month=December; nummonth=12;; -esac - -day=$2 - -# Here we have to deal with the problem that the ls output gives either -# the time of day or the year. -case $3 in - *:*) set `date`; eval year=\$$# - case $2 in - Jan) nummonthtod=1;; - Feb) nummonthtod=2;; - Mar) nummonthtod=3;; - Apr) nummonthtod=4;; - May) nummonthtod=5;; - Jun) nummonthtod=6;; - Jul) nummonthtod=7;; - Aug) nummonthtod=8;; - Sep) nummonthtod=9;; - Oct) nummonthtod=10;; - Nov) nummonthtod=11;; - Dec) nummonthtod=12;; - esac - # For the first six month of the year the time notation can also - # be used for files modified in the last year. - if (expr $nummonth \> $nummonthtod) > /dev/null; - then - year=`expr $year - 1` - fi;; - *) year=$3;; -esac - -# The result. -echo $day $month $year diff --git a/cextern/wcslib/config/missing b/cextern/wcslib/config/missing deleted file mode 100755 index e7ef83a1c251..000000000000 --- a/cextern/wcslib/config/missing +++ /dev/null @@ -1,360 +0,0 @@ -#! /bin/sh -# Common stub for a few missing GNU programs while installing. - -scriptversion=2003-09-02.23 - -# Copyright (C) 1996, 1997, 1999, 2000, 2002, 2003 -# Free Software Foundation, Inc. -# Originally by Fran,cois Pinard , 1996. - -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2, or (at your option) -# any later version. - -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. - -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA -# 02111-1307, USA. - -# As a special exception to the GNU General Public License, if you -# distribute this file as part of a program that contains a -# configuration script generated by Autoconf, you may include it under -# the same distribution terms that you use for the rest of that program. - -if test $# -eq 0; then - echo 1>&2 "Try \`$0 --help' for more information" - exit 1 -fi - -run=: - -# In the cases where this matters, `missing' is being run in the -# srcdir already. -if test -f configure.ac; then - configure_ac=configure.ac -else - configure_ac=configure.in -fi - -msg="missing on your system" - -case "$1" in ---run) - # Try to run requested program, and just exit if it succeeds. - run= - shift - "$@" && exit 0 - # Exit code 63 means version mismatch. This often happens - # when the user try to use an ancient version of a tool on - # a file that requires a minimum version. In this case we - # we should proceed has if the program had been absent, or - # if --run hadn't been passed. - if test $? = 63; then - run=: - msg="probably too old" - fi - ;; -esac - -# If it does not exist, or fails to run (possibly an outdated version), -# try to emulate it. -case "$1" in - - -h|--h|--he|--hel|--help) - echo "\ -$0 [OPTION]... PROGRAM [ARGUMENT]... - -Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an -error status if there is no known handling for PROGRAM. - -Options: - -h, --help display this help and exit - -v, --version output version information and exit - --run try to run the given command, and emulate it if it fails - -Supported PROGRAM values: - aclocal touch file \`aclocal.m4' - autoconf touch file \`configure' - autoheader touch file \`config.h.in' - automake touch all \`Makefile.in' files - bison create \`y.tab.[ch]', if possible, from existing .[ch] - flex create \`lex.yy.c', if possible, from existing .c - help2man touch the output file - lex create \`lex.yy.c', if possible, from existing .c - makeinfo touch the output file - tar try tar, gnutar, gtar, then tar without non-portable flags - yacc create \`y.tab.[ch]', if possible, from existing .[ch] - -Send bug reports to ." - ;; - - -v|--v|--ve|--ver|--vers|--versi|--versio|--version) - echo "missing $scriptversion (GNU Automake)" - ;; - - -*) - echo 1>&2 "$0: Unknown \`$1' option" - echo 1>&2 "Try \`$0 --help' for more information" - exit 1 - ;; - - aclocal*) - if test -z "$run" && ($1 --version) > /dev/null 2>&1; then - # We have it, but it failed. - exit 1 - fi - - echo 1>&2 "\ -WARNING: \`$1' is $msg. You should only need it if - you modified \`acinclude.m4' or \`${configure_ac}'. You might want - to install the \`Automake' and \`Perl' packages. Grab them from - any GNU archive site." - touch aclocal.m4 - ;; - - autoconf) - if test -z "$run" && ($1 --version) > /dev/null 2>&1; then - # We have it, but it failed. - exit 1 - fi - - echo 1>&2 "\ -WARNING: \`$1' is $msg. You should only need it if - you modified \`${configure_ac}'. You might want to install the - \`Autoconf' and \`GNU m4' packages. Grab them from any GNU - archive site." - touch configure - ;; - - autoheader) - if test -z "$run" && ($1 --version) > /dev/null 2>&1; then - # We have it, but it failed. - exit 1 - fi - - echo 1>&2 "\ -WARNING: \`$1' is $msg. You should only need it if - you modified \`acconfig.h' or \`${configure_ac}'. You might want - to install the \`Autoconf' and \`GNU m4' packages. Grab them - from any GNU archive site." - files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' ${configure_ac}` - test -z "$files" && files="config.h" - touch_files= - for f in $files; do - case "$f" in - *:*) touch_files="$touch_files "`echo "$f" | - sed -e 's/^[^:]*://' -e 's/:.*//'`;; - *) touch_files="$touch_files $f.in";; - esac - done - touch $touch_files - ;; - - automake*) - if test -z "$run" && ($1 --version) > /dev/null 2>&1; then - # We have it, but it failed. - exit 1 - fi - - echo 1>&2 "\ -WARNING: \`$1' is $msg. You should only need it if - you modified \`Makefile.am', \`acinclude.m4' or \`${configure_ac}'. - You might want to install the \`Automake' and \`Perl' packages. - Grab them from any GNU archive site." - find . -type f -name Makefile.am -print | - sed 's/\.am$/.in/' | - while read f; do touch "$f"; done - ;; - - autom4te) - if test -z "$run" && ($1 --version) > /dev/null 2>&1; then - # We have it, but it failed. - exit 1 - fi - - echo 1>&2 "\ -WARNING: \`$1' is needed, but is $msg. - You might have modified some files without having the - proper tools for further handling them. - You can get \`$1' as part of \`Autoconf' from any GNU - archive site." - - file=`echo "$*" | sed -n 's/.*--output[ =]*\([^ ]*\).*/\1/p'` - test -z "$file" && file=`echo "$*" | sed -n 's/.*-o[ ]*\([^ ]*\).*/\1/p'` - if test -f "$file"; then - touch $file - else - test -z "$file" || exec >$file - echo "#! /bin/sh" - echo "# Created by GNU Automake missing as a replacement of" - echo "# $ $@" - echo "exit 0" - chmod +x $file - exit 1 - fi - ;; - - bison|yacc) - echo 1>&2 "\ -WARNING: \`$1' $msg. You should only need it if - you modified a \`.y' file. You may need the \`Bison' package - in order for those modifications to take effect. You can get - \`Bison' from any GNU archive site." - rm -f y.tab.c y.tab.h - if [ $# -ne 1 ]; then - eval LASTARG="\${$#}" - case "$LASTARG" in - *.y) - SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'` - if [ -f "$SRCFILE" ]; then - cp "$SRCFILE" y.tab.c - fi - SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'` - if [ -f "$SRCFILE" ]; then - cp "$SRCFILE" y.tab.h - fi - ;; - esac - fi - if [ ! -f y.tab.h ]; then - echo >y.tab.h - fi - if [ ! -f y.tab.c ]; then - echo 'main() { return 0; }' >y.tab.c - fi - ;; - - lex|flex) - echo 1>&2 "\ -WARNING: \`$1' is $msg. You should only need it if - you modified a \`.l' file. You may need the \`Flex' package - in order for those modifications to take effect. You can get - \`Flex' from any GNU archive site." - rm -f lex.yy.c - if [ $# -ne 1 ]; then - eval LASTARG="\${$#}" - case "$LASTARG" in - *.l) - SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'` - if [ -f "$SRCFILE" ]; then - cp "$SRCFILE" lex.yy.c - fi - ;; - esac - fi - if [ ! -f lex.yy.c ]; then - echo 'main() { return 0; }' >lex.yy.c - fi - ;; - - help2man) - if test -z "$run" && ($1 --version) > /dev/null 2>&1; then - # We have it, but it failed. - exit 1 - fi - - echo 1>&2 "\ -WARNING: \`$1' is $msg. You should only need it if - you modified a dependency of a manual page. You may need the - \`Help2man' package in order for those modifications to take - effect. You can get \`Help2man' from any GNU archive site." - - file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'` - if test -z "$file"; then - file=`echo "$*" | sed -n 's/.*--output=\([^ ]*\).*/\1/p'` - fi - if [ -f "$file" ]; then - touch $file - else - test -z "$file" || exec >$file - echo ".ab help2man is required to generate this page" - exit 1 - fi - ;; - - makeinfo) - if test -z "$run" && (makeinfo --version) > /dev/null 2>&1; then - # We have makeinfo, but it failed. - exit 1 - fi - - echo 1>&2 "\ -WARNING: \`$1' is $msg. You should only need it if - you modified a \`.texi' or \`.texinfo' file, or any other file - indirectly affecting the aspect of the manual. The spurious - call might also be the consequence of using a buggy \`make' (AIX, - DU, IRIX). You might want to install the \`Texinfo' package or - the \`GNU make' package. Grab either from any GNU archive site." - file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'` - if test -z "$file"; then - file=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'` - file=`sed -n '/^@setfilename/ { s/.* \([^ ]*\) *$/\1/; p; q; }' $file` - fi - touch $file - ;; - - tar) - shift - if test -n "$run"; then - echo 1>&2 "ERROR: \`tar' requires --run" - exit 1 - fi - - # We have already tried tar in the generic part. - # Look for gnutar/gtar before invocation to avoid ugly error - # messages. - if (gnutar --version > /dev/null 2>&1); then - gnutar "$@" && exit 0 - fi - if (gtar --version > /dev/null 2>&1); then - gtar "$@" && exit 0 - fi - firstarg="$1" - if shift; then - case "$firstarg" in - *o*) - firstarg=`echo "$firstarg" | sed s/o//` - tar "$firstarg" "$@" && exit 0 - ;; - esac - case "$firstarg" in - *h*) - firstarg=`echo "$firstarg" | sed s/h//` - tar "$firstarg" "$@" && exit 0 - ;; - esac - fi - - echo 1>&2 "\ -WARNING: I can't seem to be able to run \`tar' with the given arguments. - You may want to install GNU tar or Free paxutils, or check the - command line arguments." - exit 1 - ;; - - *) - echo 1>&2 "\ -WARNING: \`$1' is needed, and is $msg. - You might have modified some files without having the - proper tools for further handling them. Check the \`README' file, - it often tells you about the needed prerequisites for installing - this package. You may also peek at any GNU archive site, in case - some other package would contain this missing \`$1' program." - exit 1 - ;; -esac - -exit 0 - -# Local variables: -# eval: (add-hook 'write-file-hooks 'time-stamp) -# time-stamp-start: "scriptversion=" -# time-stamp-format: "%:y-%02m-%02d.%02H" -# time-stamp-end: "$" -# End: diff --git a/cextern/wcslib/config/mkinstalldirs b/cextern/wcslib/config/mkinstalldirs deleted file mode 100755 index a55cff670691..000000000000 --- a/cextern/wcslib/config/mkinstalldirs +++ /dev/null @@ -1,137 +0,0 @@ -#! /bin/sh -# mkinstalldirs --- make directory hierarchy -# Original author: Noah Friedman -# Created: 1993-05-16 -# Public domain. - -scriptversion=2003-09-26.19 - -errstatus=0 -dirmode="" - -usage="\ -Usage: mkinstalldirs [-h] [--help] [--version] [-m MODE] DIR ... - -Create each directory DIR (with mode MODE, if specified), including all -leading file name components. -" - -# process command line arguments -while test $# -gt 0 ; do - case $1 in - -h | --help | --h*) # -h for help - echo "$usage" - exit 0 - ;; - -m) # -m PERM arg - shift - test $# -eq 0 && { echo "$usage" 1>&2; exit 1; } - dirmode=$1 - shift - ;; - --version) - echo "$0 $scriptversion" - exit 0 - ;; - --) # stop option processing - shift - break - ;; - -*) # unknown option - echo "$usage" 1>&2 - exit 1 - ;; - *) # first non-opt arg - break - ;; - esac -done - -for file -do - if test -d "$file"; then - shift - else - break - fi -done - -case $# in - 0) exit 0 ;; -esac - -case $dirmode in - '') - if mkdir -p -- . 2>/dev/null; then - echo "mkdir -p -- $*" - exec mkdir -p -- "$@" - else - # On NextStep and OpenStep, the `mkdir' command does not - # recognize any option. It will interpret all options as - # directories to create, and then abort because `.' already - # exists. - test -d ./-p && rmdir ./-p - test -d ./-- && rmdir ./-- - fi - ;; - *) - if mkdir -m "$dirmode" -p -- . 2>/dev/null; then - echo "mkdir -m $dirmode -p -- $*" - exec mkdir -m "$dirmode" -p -- "$@" - else - # Clean up after NextStep and OpenStep mkdir. - for d in ./-m ./-p ./-- "./$dirmode"; - do - test -d $d && rmdir $d - done - fi - ;; -esac - -for file -do - set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'` - shift - - pathcomp= - for d - do - pathcomp="$pathcomp$d" - case $pathcomp in - -*) pathcomp=./$pathcomp ;; - esac - - if test ! -d "$pathcomp"; then - echo "mkdir $pathcomp" - - mkdir "$pathcomp" || lasterr=$? - - if test ! -d "$pathcomp"; then - errstatus=$lasterr - else - if test ! -z "$dirmode"; then - echo "chmod $dirmode $pathcomp" - lasterr="" - chmod "$dirmode" "$pathcomp" || lasterr=$? - - if test ! -z "$lasterr"; then - errstatus=$lasterr - fi - fi - fi - fi - - pathcomp="$pathcomp/" - done -done - -exit $errstatus - -# Local Variables: -# mode: shell-script -# sh-indentation: 2 -# eval: (add-hook 'write-file-hooks 'time-stamp) -# time-stamp-start: "scriptversion=" -# time-stamp-format: "%:y-%02m-%02d.%02H" -# time-stamp-end: "$" -# End: diff --git a/cextern/wcslib/config/move-if-change b/cextern/wcslib/config/move-if-change deleted file mode 100755 index 5cdc3b4d78dc..000000000000 --- a/cextern/wcslib/config/move-if-change +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/sh -# Like mv $1 $2, but if the files are the same, just delete $1. -# Status is 0 if $2 is changed, 1 otherwise. -if test -r $2; then - if cmp -s $1 $2; then - echo $2 is unchanged - rm -f $1 - else - mv -f $1 $2 - fi -else - mv -f $1 $2 -fi diff --git a/cextern/wcslib/configure b/cextern/wcslib/configure index 719600e94231..8d578978a03a 100755 --- a/cextern/wcslib/configure +++ b/cextern/wcslib/configure @@ -1,85 +1,105 @@ #! /bin/sh -# From configure.ac Revision: 4.14 . +# From configure.ac Revision: 8.6 . # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.61 for WCSLIB 4.14. +# Generated by GNU Autoconf 2.71 for WCSLIB 8.6. # # Report bugs to . # -# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, -# 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. +# +# Copyright (C) 1992-1996, 1998-2017, 2020-2021 Free Software Foundation, +# Inc. +# +# # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. -## --------------------- ## -## M4sh Initialization. ## -## --------------------- ## +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh -if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then +as_nop=: +if test ${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1 +then : emulate sh NULLCMD=: - # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST -else - case `(set -o) 2>/dev/null` in - *posix*) set -o posix ;; +else $as_nop + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; esac - fi +# Reset variables that may have inherited troublesome values from +# the environment. -# PATH needs CR -# Avoid depending upon Character Ranges. -as_cr_letters='abcdefghijklmnopqrstuvwxyz' -as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' -as_cr_Letters=$as_cr_letters$as_cr_LETTERS -as_cr_digits='0123456789' -as_cr_alnum=$as_cr_Letters$as_cr_digits +# IFS needs to be set, to space, tab, and newline, in precisely that order. +# (If _AS_PATH_WALK were called with IFS unset, it would have the +# side effect of setting IFS to empty, thus disabling word splitting.) +# Quoting is to prevent editors from complaining about space-tab. +as_nl=' +' +export as_nl +IFS=" "" $as_nl" -# The user is always right. -if test "${PATH_SEPARATOR+set}" != set; then - echo "#! /bin/sh" >conf$$.sh - echo "exit 0" >>conf$$.sh - chmod +x conf$$.sh - if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then - PATH_SEPARATOR=';' - else - PATH_SEPARATOR=: - fi - rm -f conf$$.sh -fi +PS1='$ ' +PS2='> ' +PS4='+ ' -# Support unset when possible. -if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then - as_unset=unset -else - as_unset=false -fi +# Ensure predictable behavior from utilities with locale-dependent output. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# We cannot yet rely on "unset" to work, but we need these variables +# to be unset--not just set to an empty or harmless value--now, to +# avoid bugs in old shells (e.g. pre-3.0 UWIN ksh). This construct +# also avoids known problems related to "unset" and subshell syntax +# in other old shells (e.g. bash 2.01 and pdksh 5.2.14). +for as_var in BASH_ENV ENV MAIL MAILPATH CDPATH +do eval test \${$as_var+y} \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done +# Ensure that fds 0, 1, and 2 are open. +if (exec 3>&0) 2>/dev/null; then :; else exec 0&1) 2>/dev/null; then :; else exec 1>/dev/null; fi +if (exec 3>&2) ; then :; else exec 2>/dev/null; fi + +# The user is always right. +if ${PATH_SEPARATOR+false} :; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi -# IFS -# We need space, tab and new line, in precisely that order. Quoting is -# there to prevent editors from complaining about space-tab. -# (If _AS_PATH_WALK were called with IFS unset, it would disable word -# splitting by setting IFS to empty value.) -as_nl=' -' -IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. -case $0 in +as_myself= +case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break -done + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + test -r "$as_dir$0" && as_myself=$as_dir$0 && break + done IFS=$as_save_IFS ;; @@ -90,32 +110,336 @@ if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then - echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 - { (exit 1); exit 1; } + printf "%s\n" "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + + +# Use a proper internal environment variable to ensure we don't fall + # into an infinite loop, continuously re-executing ourselves. + if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then + _as_can_reexec=no; export _as_can_reexec; + # We cannot yet assume a decent shell, so we have to provide a +# neutralization value for shells without unset; and this also +# works around shells that cannot unset nonexistent variables. +# Preserve -v and -x to the replacement shell. +BASH_ENV=/dev/null +ENV=/dev/null +(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV +case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; +esac +exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} +# Admittedly, this is quite paranoid, since all the known shells bail +# out after a failed `exec'. +printf "%s\n" "$0: could not re-execute with $CONFIG_SHELL" >&2 +exit 255 + fi + # We don't want this to propagate to other subprocesses. + { _as_can_reexec=; unset _as_can_reexec;} +if test "x$CONFIG_SHELL" = x; then + as_bourne_compatible="as_nop=: +if test \${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1 +then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which + # is contrary to our usage. Disable this feature. + alias -g '\${1+\"\$@\"}'='\"\$@\"' + setopt NO_GLOB_SUBST +else \$as_nop + case \`(set -o) 2>/dev/null\` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac fi +" + as_required="as_fn_return () { (exit \$1); } +as_fn_success () { as_fn_return 0; } +as_fn_failure () { as_fn_return 1; } +as_fn_ret_success () { return 0; } +as_fn_ret_failure () { return 1; } -# Work around bugs in pre-3.0 UWIN ksh. -for as_var in ENV MAIL MAILPATH -do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var -done -PS1='$ ' -PS2='> ' -PS4='+ ' +exitcode=0 +as_fn_success || { exitcode=1; echo as_fn_success failed.; } +as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } +as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } +as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } +if ( set x; as_fn_ret_success y && test x = \"\$1\" ) +then : + +else \$as_nop + exitcode=1; echo positional parameters were not saved. +fi +test x\$exitcode = x0 || exit 1 +blah=\$(echo \$(echo blah)) +test x\"\$blah\" = xblah || exit 1 +test -x / || exit 1" + as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO + as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO + eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && + test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 +test \$(( 1 + 1 )) = 2 || exit 1" + if (eval "$as_required") 2>/dev/null +then : + as_have_required=yes +else $as_nop + as_have_required=no +fi + if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null +then : -# NLS nuisances. -for as_var in \ - LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ - LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ - LC_TELEPHONE LC_TIME +else $as_nop + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +as_found=false +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do - if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then - eval $as_var=C; export $as_var + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + as_found=: + case $as_dir in #( + /*) + for as_base in sh bash ksh sh5; do + # Try only shells that exist, to save several forks. + as_shell=$as_dir$as_base + if { test -f "$as_shell" || test -f "$as_shell.exe"; } && + as_run=a "$as_shell" -c "$as_bourne_compatible""$as_required" 2>/dev/null +then : + CONFIG_SHELL=$as_shell as_have_required=yes + if as_run=a "$as_shell" -c "$as_bourne_compatible""$as_suggested" 2>/dev/null +then : + break 2 +fi +fi + done;; + esac + as_found=false +done +IFS=$as_save_IFS +if $as_found +then : + +else $as_nop + if { test -f "$SHELL" || test -f "$SHELL.exe"; } && + as_run=a "$SHELL" -c "$as_bourne_compatible""$as_required" 2>/dev/null +then : + CONFIG_SHELL=$SHELL as_have_required=yes +fi +fi + + + if test "x$CONFIG_SHELL" != x +then : + export CONFIG_SHELL + # We cannot yet assume a decent shell, so we have to provide a +# neutralization value for shells without unset; and this also +# works around shells that cannot unset nonexistent variables. +# Preserve -v and -x to the replacement shell. +BASH_ENV=/dev/null +ENV=/dev/null +(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV +case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; +esac +exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} +# Admittedly, this is quite paranoid, since all the known shells bail +# out after a failed `exec'. +printf "%s\n" "$0: could not re-execute with $CONFIG_SHELL" >&2 +exit 255 +fi + + if test x$as_have_required = xno +then : + printf "%s\n" "$0: This script requires a shell more modern than all" + printf "%s\n" "$0: the shells that I found on your system." + if test ${ZSH_VERSION+y} ; then + printf "%s\n" "$0: In particular, zsh $ZSH_VERSION has bugs and should" + printf "%s\n" "$0: be upgraded to zsh 4.3.4 or later." else - ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var + printf "%s\n" "$0: Please tell bug-autoconf@gnu.org and +$0: mark@calabretta.id.au about your system, including any +$0: error possibly output before this message. Then install +$0: a modern shell, or manually run the script under such a +$0: shell if you do have one." fi -done + exit 1 +fi +fi +fi +SHELL=${CONFIG_SHELL-/bin/sh} +export SHELL +# Unset more variables known to interfere with behavior of common tools. +CLICOLOR_FORCE= GREP_OPTIONS= +unset CLICOLOR_FORCE GREP_OPTIONS + +## --------------------- ## +## M4sh Shell Functions. ## +## --------------------- ## +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset + + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit +# as_fn_nop +# --------- +# Do nothing but, unlike ":", preserve the value of $?. +as_fn_nop () +{ + return $? +} +as_nop=as_fn_nop + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`printf "%s\n" "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +printf "%s\n" X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + + +} # as_fn_mkdir_p + +# as_fn_executable_p FILE +# ----------------------- +# Test if FILE is an executable regular file. +as_fn_executable_p () +{ + test -f "$1" && test -x "$1" +} # as_fn_executable_p +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null +then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else $as_nop + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null +then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else $as_nop + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + +# as_fn_nop +# --------- +# Do nothing but, unlike ":", preserve the value of $?. +as_fn_nop () +{ + return $? +} +as_nop=as_fn_nop + +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with STATUS, using 1 if that was 0. +as_fn_error () +{ + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + fi + printf "%s\n" "$as_me: error: $2" >&2 + as_fn_exit $as_status +} # as_fn_error -# Required to use basename. if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr @@ -129,13 +453,17 @@ else as_basename=false fi +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi -# Name of the executable. as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || -echo X/"$0" | +printf "%s\n" X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q @@ -150,414 +478,124 @@ echo X/"$0" | } s/.*/./; q'` -# CDPATH. -$as_unset CDPATH +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits -if test "x$CONFIG_SHELL" = x; then - if (eval ":") 2>/dev/null; then - as_have_required=yes -else - as_have_required=no -fi + as_lineno_1=$LINENO as_lineno_1a=$LINENO + as_lineno_2=$LINENO as_lineno_2a=$LINENO + eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && + test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { + # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) + sed -n ' + p + /[$]LINENO/= + ' <$as_myself | + sed ' + s/[$]LINENO.*/&-/ + t lineno + b + :lineno + N + :loop + s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ + t loop + s/-\n.*// + ' >$as_me.lineno && + chmod +x "$as_me.lineno" || + { printf "%s\n" "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } - if test $as_have_required = yes && (eval ": -(as_func_return () { - (exit \$1) -} -as_func_success () { - as_func_return 0 -} -as_func_failure () { - as_func_return 1 -} -as_func_ret_success () { - return 0 -} -as_func_ret_failure () { - return 1 + # If we had to re-execute with $CONFIG_SHELL, we're ensured to have + # already done that, so ensure we don't try to do so again and fall + # in an infinite loop. This has already happened in practice. + _as_can_reexec=no; export _as_can_reexec + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensitive to this). + . "./$as_me.lineno" + # Exit status is that of the last command. + exit } -exitcode=0 -if as_func_success; then - : -else - exitcode=1 - echo as_func_success failed. -fi -if as_func_failure; then - exitcode=1 - echo as_func_failure succeeded. -fi +# Determine whether it's possible to make 'echo' print without a newline. +# These variables are no longer used directly by Autoconf, but are AC_SUBSTed +# for compatibility with existing Makefiles. +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac -if as_func_ret_success; then - : -else - exitcode=1 - echo as_func_ret_success failed. -fi +# For backward compatibility with old third-party macros, we provide +# the shell variables $as_echo and $as_echo_n. New code should use +# AS_ECHO(["message"]) and AS_ECHO_N(["message"]), respectively. +as_echo='printf %s\n' +as_echo_n='printf %s' -if as_func_ret_failure; then - exitcode=1 - echo as_func_ret_failure succeeded. -fi -if ( set x; as_func_ret_success y && test x = \"\$1\" ); then - : +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file else - exitcode=1 - echo positional parameters were not saved. -fi - -test \$exitcode = 0) || { (exit 1); exit 1; } - -( - as_lineno_1=\$LINENO - as_lineno_2=\$LINENO - test \"x\$as_lineno_1\" != \"x\$as_lineno_2\" && - test \"x\`expr \$as_lineno_1 + 1\`\" = \"x\$as_lineno_2\") || { (exit 1); exit 1; } -") 2> /dev/null; then - : + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -pR'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -pR' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -pR' + fi else - as_candidate_shells= - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - case $as_dir in - /*) - for as_base in sh bash ksh sh5; do - as_candidate_shells="$as_candidate_shells $as_dir/$as_base" - done;; - esac -done -IFS=$as_save_IFS - + as_ln_s='cp -pR' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null - for as_shell in $as_candidate_shells $SHELL; do - # Try only shells that exist, to save several forks. - if { test -f "$as_shell" || test -f "$as_shell.exe"; } && - { ("$as_shell") 2> /dev/null <<\_ASEOF -if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then - emulate sh - NULLCMD=: - # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which - # is contrary to our usage. Disable this feature. - alias -g '${1+"$@"}'='"$@"' - setopt NO_GLOB_SUBST +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' else - case `(set -o) 2>/dev/null` in - *posix*) set -o posix ;; -esac - + test -d ./-p && rmdir ./-p + as_mkdir_p=false fi +as_test_x='test -x' +as_executable_p=as_fn_executable_p -: -_ASEOF -}; then - CONFIG_SHELL=$as_shell - as_have_required=yes - if { "$as_shell" 2> /dev/null <<\_ASEOF -if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then - emulate sh - NULLCMD=: - # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which - # is contrary to our usage. Disable this feature. - alias -g '${1+"$@"}'='"$@"' - setopt NO_GLOB_SUBST -else - case `(set -o) 2>/dev/null` in - *posix*) set -o posix ;; -esac +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" -fi +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" -: -(as_func_return () { - (exit $1) -} -as_func_success () { - as_func_return 0 -} -as_func_failure () { - as_func_return 1 -} -as_func_ret_success () { - return 0 -} -as_func_ret_failure () { - return 1 -} +test -n "$DJDIR" || exec 7<&0 &1 -exitcode=0 -if as_func_success; then - : -else - exitcode=1 - echo as_func_success failed. -fi - -if as_func_failure; then - exitcode=1 - echo as_func_failure succeeded. -fi - -if as_func_ret_success; then - : -else - exitcode=1 - echo as_func_ret_success failed. -fi - -if as_func_ret_failure; then - exitcode=1 - echo as_func_ret_failure succeeded. -fi - -if ( set x; as_func_ret_success y && test x = "$1" ); then - : -else - exitcode=1 - echo positional parameters were not saved. -fi - -test $exitcode = 0) || { (exit 1); exit 1; } - -( - as_lineno_1=$LINENO - as_lineno_2=$LINENO - test "x$as_lineno_1" != "x$as_lineno_2" && - test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2") || { (exit 1); exit 1; } - -_ASEOF -}; then - break -fi - -fi - - done - - if test "x$CONFIG_SHELL" != x; then - for as_var in BASH_ENV ENV - do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var - done - export CONFIG_SHELL - exec "$CONFIG_SHELL" "$as_myself" ${1+"$@"} -fi - - - if test $as_have_required = no; then - echo This script requires a shell more modern than all the - echo shells that I found on your system. Please install a - echo modern shell, or manually run the script under such a - echo shell if you do have one. - { (exit 1); exit 1; } -fi - - -fi - -fi - - - -(eval "as_func_return () { - (exit \$1) -} -as_func_success () { - as_func_return 0 -} -as_func_failure () { - as_func_return 1 -} -as_func_ret_success () { - return 0 -} -as_func_ret_failure () { - return 1 -} - -exitcode=0 -if as_func_success; then - : -else - exitcode=1 - echo as_func_success failed. -fi - -if as_func_failure; then - exitcode=1 - echo as_func_failure succeeded. -fi - -if as_func_ret_success; then - : -else - exitcode=1 - echo as_func_ret_success failed. -fi - -if as_func_ret_failure; then - exitcode=1 - echo as_func_ret_failure succeeded. -fi - -if ( set x; as_func_ret_success y && test x = \"\$1\" ); then - : -else - exitcode=1 - echo positional parameters were not saved. -fi - -test \$exitcode = 0") || { - echo No shell found that supports shell functions. - echo Please tell autoconf@gnu.org about your system, - echo including any error possibly output before this - echo message -} - - - - as_lineno_1=$LINENO - as_lineno_2=$LINENO - test "x$as_lineno_1" != "x$as_lineno_2" && - test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2" || { - - # Create $as_me.lineno as a copy of $as_myself, but with $LINENO - # uniformly replaced by the line number. The first 'sed' inserts a - # line-number line after each line using $LINENO; the second 'sed' - # does the real work. The second script uses 'N' to pair each - # line-number line with the line containing $LINENO, and appends - # trailing '-' during substitution so that $LINENO is not a special - # case at line end. - # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the - # scripts with optimization help from Paolo Bonzini. Blame Lee - # E. McMahon (1931-1989) for sed's syntax. :-) - sed -n ' - p - /[$]LINENO/= - ' <$as_myself | - sed ' - s/[$]LINENO.*/&-/ - t lineno - b - :lineno - N - :loop - s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ - t loop - s/-\n.*// - ' >$as_me.lineno && - chmod +x "$as_me.lineno" || - { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2 - { (exit 1); exit 1; }; } - - # Don't try to exec as it changes $[0], causing all sort of problems - # (the dirname of $[0] is not the place where we might find the - # original and so on. Autoconf is especially sensitive to this). - . "./$as_me.lineno" - # Exit status is that of the last command. - exit -} - - -if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then - as_dirname=dirname -else - as_dirname=false -fi - -ECHO_C= ECHO_N= ECHO_T= -case `echo -n x` in --n*) - case `echo 'x\c'` in - *c*) ECHO_T=' ';; # ECHO_T is single tab character. - *) ECHO_C='\c';; - esac;; -*) - ECHO_N='-n';; -esac - -if expr a : '\(a\)' >/dev/null 2>&1 && - test "X`expr 00001 : '.*\(...\)'`" = X001; then - as_expr=expr -else - as_expr=false -fi - -rm -f conf$$ conf$$.exe conf$$.file -if test -d conf$$.dir; then - rm -f conf$$.dir/conf$$.file -else - rm -f conf$$.dir - mkdir conf$$.dir -fi -echo >conf$$.file -if ln -s conf$$.file conf$$ 2>/dev/null; then - as_ln_s='ln -s' - # ... but there are two gotchas: - # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. - # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. - # In both cases, we have to default to `cp -p'. - ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || - as_ln_s='cp -p' -elif ln conf$$.file conf$$ 2>/dev/null; then - as_ln_s=ln -else - as_ln_s='cp -p' -fi -rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file -rmdir conf$$.dir 2>/dev/null - -if mkdir -p . 2>/dev/null; then - as_mkdir_p=: -else - test -d ./-p && rmdir ./-p - as_mkdir_p=false -fi - -if test -x / >/dev/null 2>&1; then - as_test_x='test -x' -else - if ls -dL / >/dev/null 2>&1; then - as_ls_L_option=L - else - as_ls_L_option= - fi - as_test_x=' - eval sh -c '\'' - if test -d "$1"; then - test -d "$1/."; - else - case $1 in - -*)set "./$1";; - esac; - case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in - ???[sx]*):;;*)false;;esac;fi - '\'' sh - ' -fi -as_executable_p=$as_test_x - -# Sed expression to map a string onto a valid CPP name. -as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" - -# Sed expression to map a string onto a valid variable name. -as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" - - - -exec 7<&0 &1 - -# Name of the host. -# hostname on some systems (SVR3.2, Linux) returns a bogus exit status, -# so uname gets run too. -ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` +# Name of the host. +# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, +# so uname gets run too. +ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` # # Initializations. @@ -570,150 +608,180 @@ cross_compiling=no subdirs= MFLAGS= MAKEFLAGS= -SHELL=${CONFIG_SHELL-/bin/sh} # Identity of this package. PACKAGE_NAME='WCSLIB' -PACKAGE_TARNAME='wcslib-4.14' -PACKAGE_VERSION='4.14' -PACKAGE_STRING='WCSLIB 4.14' +PACKAGE_TARNAME='wcslib-8.6' +PACKAGE_VERSION='8.6' +PACKAGE_STRING='WCSLIB 8.6' PACKAGE_BUGREPORT='mark@calabretta.id.au' +PACKAGE_URL='' ac_unique_file="C/wcs.h" # Factoring default headers for most tests. ac_includes_default="\ -#include -#ifdef HAVE_SYS_TYPES_H -# include +#include +#ifdef HAVE_STDIO_H +# include #endif -#ifdef HAVE_SYS_STAT_H -# include -#endif -#ifdef STDC_HEADERS +#ifdef HAVE_STDLIB_H # include -# include -#else -# ifdef HAVE_STDLIB_H -# include -# endif #endif #ifdef HAVE_STRING_H -# if !defined STDC_HEADERS && defined HAVE_MEMORY_H -# include -# endif # include #endif -#ifdef HAVE_STRINGS_H -# include -#endif #ifdef HAVE_INTTYPES_H # include #endif #ifdef HAVE_STDINT_H # include #endif +#ifdef HAVE_STRINGS_H +# include +#endif +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_SYS_STAT_H +# include +#endif #ifdef HAVE_UNISTD_H # include #endif" -ac_subst_vars='SHELL -PATH_SEPARATOR -PACKAGE_NAME -PACKAGE_TARNAME -PACKAGE_VERSION -PACKAGE_STRING -PACKAGE_BUGREPORT -exec_prefix -prefix -program_transform_name -bindir -sbindir -libexecdir -datarootdir -datadir -sysconfdir -sharedstatedir -localstatedir -includedir -oldincludedir -docdir -infodir -htmldir -dvidir -pdfdir -psdir -libdir -localedir -mandir -DEFS -ECHO_C -ECHO_N -ECHO_T -LIBS -build_alias -host_alias -target_alias -LIBVER -build -build_cpu -build_vendor -build_os -ARCH -FLEX -CC -CFLAGS -LDFLAGS -CPPFLAGS -ac_ct_CC -EXEEXT -OBJEXT -CPP -GREP -EGREP +ac_header_c_list= +ac_subst_vars='LTLIBOBJS LIBOBJS -F77 -FFLAGS -ac_ct_F77 -FLIBS -RANLIB -SHRLIB -SONAME -SHRFLAGS -SHRLD -SHRSFX -SHRLN -LN_S -INSTALL_PROGRAM -INSTALL_SCRIPT -INSTALL_DATA +EXTRA_CLEAN +VALGRIND +MODE +FLAVOUR +OBSTZ +OBSLAT +OBSLNG +INSTDIR +TSTDIRS +SUBDIRS +PGPLOTLIB +PGPLOTINC XMKMF -CFITSIOINC -CFITSIOLIB GETWCSTAB -PGPLOTINC -PGPLOTLIB -SUBDIRS -TSTDIRS -INSTDIR -LTLIBOBJS' -ac_subst_files='' - ac_precious_vars='build_alias -host_alias -target_alias -CC -CFLAGS -LDFLAGS -LIBS -CPPFLAGS -CPP -F77 -FFLAGS -XMKMF' - - -# Initialize some variables set by options. -ac_init_help= -ac_init_version=false +CFITSIOLIB +CFITSIOINC +MAKEFLAGS +INSTALL_DATA +INSTALL_SCRIPT +INSTALL_PROGRAM +LN_S +SHRLN +SHRSFX +SHRLD +SHRFLAGS +SONAME +SHRLIB +RANLIB +ARFLAGS +BINDC +FLIBS +ac_ct_F77 +FFLAGS +F77 +FLFLAGS +GCC_VERSION +CPP +OBJEXT +EXEEXT +ac_ct_CC +CPPFLAGS +LDFLAGS +CFLAGS +CC +FLEX +ARCH +host_os +host_vendor +host_cpu +host +build_os +build_vendor +build_cpu +build +LIBVER +target_alias +host_alias +build_alias +LIBS +ECHO_T +ECHO_N +ECHO_C +DEFS +mandir +localedir +libdir +psdir +pdfdir +dvidir +htmldir +infodir +docdir +oldincludedir +includedir +runstatedir +localstatedir +sharedstatedir +sysconfdir +datadir +datarootdir +libexecdir +sbindir +bindir +program_transform_name +prefix +exec_prefix +PACKAGE_URL +PACKAGE_BUGREPORT +PACKAGE_STRING +PACKAGE_VERSION +PACKAGE_TARNAME +PACKAGE_NAME +PATH_SEPARATOR +SHELL' +ac_subst_files='' +ac_user_opts=' +enable_option_checking +enable_flex +enable_fortran +with_bindc +enable_shared +enable_largefile +with_cfitsio +with_cfitsiolib +with_cfitsioinc +with_pgplot +with_pgplotlib +with_pgplotinc +with_x +enable_utils +' + ac_precious_vars='build_alias +host_alias +target_alias +CC +CFLAGS +LDFLAGS +LIBS +CPPFLAGS +CPP +F77 +FFLAGS +XMKMF' + + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +ac_unrecognized_opts= +ac_unrecognized_sep= # The variables have the same names as the options, with # dashes changed to underlines. cache_file=/dev/null @@ -745,6 +813,7 @@ datadir='${datarootdir}' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' +runstatedir='${localstatedir}/run' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' @@ -769,12 +838,11 @@ do fi case $ac_option in - *=*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; - *) ac_optarg=yes ;; + *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; + *=) ac_optarg= ;; + *) ac_optarg=yes ;; esac - # Accept the important Cygnus configure options, so we can diagnose typos. - case $ac_dashdash$ac_option in --) ac_dashdash=yes ;; @@ -812,13 +880,20 @@ do datarootdir=$ac_optarg ;; -disable-* | --disable-*) - ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` # Reject names that are not valid shell variable names. - expr "x$ac_feature" : ".*[^-._$as_cr_alnum]" >/dev/null && - { echo "$as_me: error: invalid feature name: $ac_feature" >&2 - { (exit 1); exit 1; }; } - ac_feature=`echo $ac_feature | sed 's/[-.]/_/g'` - eval enable_$ac_feature=no ;; + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid feature name: \`$ac_useropt'" + ac_useropt_orig=$ac_useropt + ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=no ;; -docdir | --docdir | --docdi | --doc | --do) ac_prev=docdir ;; @@ -831,13 +906,20 @@ do dvidir=$ac_optarg ;; -enable-* | --enable-*) - ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` # Reject names that are not valid shell variable names. - expr "x$ac_feature" : ".*[^-._$as_cr_alnum]" >/dev/null && - { echo "$as_me: error: invalid feature name: $ac_feature" >&2 - { (exit 1); exit 1; }; } - ac_feature=`echo $ac_feature | sed 's/[-.]/_/g'` - eval enable_$ac_feature=\$ac_optarg ;; + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid feature name: \`$ac_useropt'" + ac_useropt_orig=$ac_useropt + ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=\$ac_optarg ;; -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ @@ -982,6 +1064,15 @@ do | -silent | --silent | --silen | --sile | --sil) silent=yes ;; + -runstatedir | --runstatedir | --runstatedi | --runstated \ + | --runstate | --runstat | --runsta | --runst | --runs \ + | --run | --ru | --r) + ac_prev=runstatedir ;; + -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \ + | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \ + | --run=* | --ru=* | --r=*) + runstatedir=$ac_optarg ;; + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ @@ -1028,22 +1119,36 @@ do ac_init_version=: ;; -with-* | --with-*) - ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` # Reject names that are not valid shell variable names. - expr "x$ac_package" : ".*[^-._$as_cr_alnum]" >/dev/null && - { echo "$as_me: error: invalid package name: $ac_package" >&2 - { (exit 1); exit 1; }; } - ac_package=`echo $ac_package | sed 's/[-.]/_/g'` - eval with_$ac_package=\$ac_optarg ;; + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid package name: \`$ac_useropt'" + ac_useropt_orig=$ac_useropt + ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=\$ac_optarg ;; -without-* | --without-*) - ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'` + ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` # Reject names that are not valid shell variable names. - expr "x$ac_package" : ".*[^-._$as_cr_alnum]" >/dev/null && - { echo "$as_me: error: invalid package name: $ac_package" >&2 - { (exit 1); exit 1; }; } - ac_package=`echo $ac_package | sed 's/[-.]/_/g'` - eval with_$ac_package=no ;; + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid package name: \`$ac_useropt'" + ac_useropt_orig=$ac_useropt + ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=no ;; --x) # Obsolete; use --with-x. @@ -1063,26 +1168,26 @@ do | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) x_libraries=$ac_optarg ;; - -*) { echo "$as_me: error: unrecognized option: $ac_option -Try \`$0 --help' for more information." >&2 - { (exit 1); exit 1; }; } + -*) as_fn_error $? "unrecognized option: \`$ac_option' +Try \`$0 --help' for more information" ;; *=*) ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` # Reject names that are not valid shell variable names. - expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null && - { echo "$as_me: error: invalid variable name: $ac_envvar" >&2 - { (exit 1); exit 1; }; } + case $ac_envvar in #( + '' | [0-9]* | *[!_$as_cr_alnum]* ) + as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; + esac eval $ac_envvar=\$ac_optarg export $ac_envvar ;; *) # FIXME: should be removed in autoconf 3.0. - echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + printf "%s\n" "$as_me: WARNING: you should use --build, --host, --target" >&2 expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && - echo "$as_me: WARNING: invalid host type: $ac_option" >&2 - : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option} + printf "%s\n" "$as_me: WARNING: invalid host type: $ac_option" >&2 + : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" ;; esac @@ -1090,23 +1195,36 @@ done if test -n "$ac_prev"; then ac_option=--`echo $ac_prev | sed 's/_/-/g'` - { echo "$as_me: error: missing argument to $ac_option" >&2 - { (exit 1); exit 1; }; } + as_fn_error $? "missing argument to $ac_option" +fi + +if test -n "$ac_unrecognized_opts"; then + case $enable_option_checking in + no) ;; + fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; + *) printf "%s\n" "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; + esac fi -# Be sure to have absolute directory names. +# Check all directory arguments for consistency. for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ - libdir localedir mandir + libdir localedir mandir runstatedir do eval ac_val=\$$ac_var + # Remove trailing slashes. + case $ac_val in + */ ) + ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` + eval $ac_var=\$ac_val;; + esac + # Be sure to have absolute directory names. case $ac_val in [\\/$]* | ?:[\\/]* ) continue;; NONE | '' ) case $ac_var in *prefix ) continue;; esac;; esac - { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 - { (exit 1); exit 1; }; } + as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" done # There might be people who depend on the old broken behavior: `$host' @@ -1120,8 +1238,6 @@ target=$target_alias if test "x$host_alias" != x; then if test "x$build_alias" = x; then cross_compiling=maybe - echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host. - If a cross compiler is detected then cross compile mode will be used." >&2 elif test "x$build_alias" != "x$host_alias"; then cross_compiling=yes fi @@ -1136,23 +1252,21 @@ test "$silent" = yes && exec 6>/dev/null ac_pwd=`pwd` && test -n "$ac_pwd" && ac_ls_di=`ls -di .` && ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || - { echo "$as_me: error: Working directory cannot be determined" >&2 - { (exit 1); exit 1; }; } + as_fn_error $? "working directory cannot be determined" test "X$ac_ls_di" = "X$ac_pwd_ls_di" || - { echo "$as_me: error: pwd does not report name of working directory" >&2 - { (exit 1); exit 1; }; } + as_fn_error $? "pwd does not report name of working directory" # Find the source files, if location was not specified. if test -z "$srcdir"; then ac_srcdir_defaulted=yes # Try the directory containing this script, then the parent directory. - ac_confdir=`$as_dirname -- "$0" || -$as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$0" : 'X\(//\)[^/]' \| \ - X"$0" : 'X\(//\)$' \| \ - X"$0" : 'X\(/\)' \| . 2>/dev/null || -echo X"$0" | + ac_confdir=`$as_dirname -- "$as_myself" || +$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_myself" : 'X\(//\)[^/]' \| \ + X"$as_myself" : 'X\(//\)$' \| \ + X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || +printf "%s\n" X"$as_myself" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q @@ -1179,13 +1293,11 @@ else fi if test ! -r "$srcdir/$ac_unique_file"; then test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." - { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2 - { (exit 1); exit 1; }; } + as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" fi ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" ac_abs_confdir=`( - cd "$srcdir" && test -r "./$ac_unique_file" || { echo "$as_me: error: $ac_msg" >&2 - { (exit 1); exit 1; }; } + cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" pwd)` # When building in place, set srcdir=. if test "$ac_abs_confdir" = "$ac_pwd"; then @@ -1211,7 +1323,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures WCSLIB 4.14 to adapt to many kinds of systems. +\`configure' configures WCSLIB 8.6 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1225,7 +1337,7 @@ Configuration: --help=short display options specific to this package --help=recursive display the short help of all the included packages -V, --version display version information and exit - -q, --quiet, --silent do not print \`checking...' messages + -q, --quiet, --silent do not print \`checking ...' messages --cache-file=FILE cache test results in FILE [disabled] -C, --config-cache alias for \`--cache-file=config.cache' -n, --no-create do not create output files @@ -1233,9 +1345,9 @@ Configuration: Installation directories: --prefix=PREFIX install architecture-independent files in PREFIX - [$ac_default_prefix] + [$ac_default_prefix] --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX - [PREFIX] + [PREFIX] By default, \`make install' will install all the files in \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify @@ -1245,25 +1357,26 @@ for instance \`--prefix=\$HOME'. For better control, use the options below. Fine tuning of the installation directories: - --bindir=DIR user executables [EPREFIX/bin] - --sbindir=DIR system admin executables [EPREFIX/sbin] - --libexecdir=DIR program executables [EPREFIX/libexec] - --sysconfdir=DIR read-only single-machine data [PREFIX/etc] - --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] - --localstatedir=DIR modifiable single-machine data [PREFIX/var] - --libdir=DIR object code libraries [EPREFIX/lib] - --includedir=DIR C header files [PREFIX/include] - --oldincludedir=DIR C header files for non-gcc [/usr/include] - --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] - --datadir=DIR read-only architecture-independent data [DATAROOTDIR] - --infodir=DIR info documentation [DATAROOTDIR/info] - --localedir=DIR locale-dependent data [DATAROOTDIR/locale] - --mandir=DIR man documentation [DATAROOTDIR/man] - --docdir=DIR documentation root [DATAROOTDIR/doc/wcslib-4.14] - --htmldir=DIR html documentation [DOCDIR] - --dvidir=DIR dvi documentation [DOCDIR] - --pdfdir=DIR pdf documentation [DOCDIR] - --psdir=DIR ps documentation [DOCDIR] + --bindir=DIR user executables [EPREFIX/bin] + --sbindir=DIR system admin executables [EPREFIX/sbin] + --libexecdir=DIR program executables [EPREFIX/libexec] + --sysconfdir=DIR read-only single-machine data [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] + --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run] + --libdir=DIR object code libraries [EPREFIX/lib] + --includedir=DIR C header files [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc [/usr/include] + --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] + --datadir=DIR read-only architecture-independent data [DATAROOTDIR] + --infodir=DIR info documentation [DATAROOTDIR/info] + --localedir=DIR locale-dependent data [DATAROOTDIR/locale] + --mandir=DIR man documentation [DATAROOTDIR/man] + --docdir=DIR documentation root [DATAROOTDIR/doc/wcslib-8.6] + --htmldir=DIR html documentation [DOCDIR] + --dvidir=DIR dvi documentation [DOCDIR] + --pdfdir=DIR pdf documentation [DOCDIR] + --psdir=DIR ps documentation [DOCDIR] _ACEOF cat <<\_ACEOF @@ -1274,26 +1387,32 @@ X features: System types: --build=BUILD configure for building on BUILD [guessed] + --host=HOST cross-compile to build programs to run on HOST [BUILD] _ACEOF fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of WCSLIB 4.14:";; + short | recursive ) echo "Configuration of WCSLIB 8.6:";; esac cat <<\_ACEOF Optional Features: + --disable-option-checking ignore unrecognized --enable/--with options --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --disable-flex don't apply flex (use pre-generated sources) --enable-fortran=ARG Fortran compiler to use --disable-fortran don't build the Fortran wrappers or PGSBOX + --disable-shared don't build the WCS shared libraries --disable-largefile omit support for large files --disable-utils don't build the WCS utilities Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --with-bindc use Fortran 2003 BIND(C) wrappers - recommended for + Link Time Optimization (LTO) --without-cfitsio eschew CFITSIO --with-cfitsiolib=DIR directory containing cfitsio library --with-cfitsioinc=DIR directory containing cfitsio header files @@ -1308,7 +1427,7 @@ Some influential environment variables: LDFLAGS linker flags, e.g. -L if you have libraries in a nonstandard directory LIBS libraries to pass to the linker, e.g. -l - CPPFLAGS C/C++/Objective C preprocessor flags, e.g. -I if + CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if you have headers in a nonstandard directory CPP C preprocessor F77 Fortran 77 compiler command @@ -1326,15 +1445,17 @@ fi if test "$ac_init_help" = "recursive"; then # If there are subdirs, report their specific --help. for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue - test -d "$ac_dir" || continue + test -d "$ac_dir" || + { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || + continue ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) - ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` + ac_dir_suffix=/`printf "%s\n" "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. - ac_top_builddir_sub=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,/..,g;s,/,,'` + ac_top_builddir_sub=`printf "%s\n" "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; @@ -1362,7 +1483,8 @@ esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix cd "$ac_dir" || { ac_status=$?; continue; } - # Check for guested configure. + # Check for configure.gnu first; this name is used for a wrapper for + # Metaconfig's "Configure" on case-insensitive file systems. if test -f "$ac_srcdir/configure.gnu"; then echo && $SHELL "$ac_srcdir/configure.gnu" --help=recursive @@ -1370,7 +1492,7 @@ ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix echo && $SHELL "$ac_srcdir/configure" --help=recursive else - echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + printf "%s\n" "$as_me: WARNING: no configuration information is in $ac_dir" >&2 fi || ac_status=$? cd "$ac_pwd" || { ac_status=$?; break; } done @@ -1379,6950 +1501,3837 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -WCSLIB configure 4.14 -generated by GNU Autoconf 2.61 +WCSLIB configure 8.6 +generated by GNU Autoconf 2.71 -Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, -2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. +Copyright (C) 2021 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF exit fi -cat >config.log <<_ACEOF -This file contains any messages produced by compilers while -running configure, to aid debugging if configure makes a mistake. -It was created by WCSLIB $as_me 4.14, which was -generated by GNU Autoconf 2.61. Invocation command line was +## ------------------------ ## +## Autoconf initialization. ## +## ------------------------ ## - $ $0 $@ +# ac_fn_c_try_compile LINENO +# -------------------------- +# Try to compile conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext conftest.beam + if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +printf "%s\n" "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext +then : + ac_retval=0 +else $as_nop + printf "%s\n" "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_compile + +# ac_fn_c_try_cpp LINENO +# ---------------------- +# Try to preprocess conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_cpp () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +printf "%s\n" "$ac_try_echo"; } >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } > conftest.i && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + } +then : + ac_retval=0 +else $as_nop + printf "%s\n" "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_cpp +# ac_fn_c_check_type LINENO TYPE VAR INCLUDES +# ------------------------------------------- +# Tests whether TYPE exists after having included INCLUDES, setting cache +# variable VAR accordingly. +ac_fn_c_check_type () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +printf %s "checking for $2... " >&6; } +if eval test \${$3+y} +then : + printf %s "(cached) " >&6 +else $as_nop + eval "$3=no" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main (void) +{ +if (sizeof ($2)) + return 0; + ; + return 0; +} _ACEOF -exec 5>>config.log +if ac_fn_c_try_compile "$LINENO" +then : + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main (void) { -cat <<_ASUNAME -## --------- ## -## Platform. ## -## --------- ## +if (sizeof (($2))) + return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO" +then : -hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` -uname -m = `(uname -m) 2>/dev/null || echo unknown` -uname -r = `(uname -r) 2>/dev/null || echo unknown` -uname -s = `(uname -s) 2>/dev/null || echo unknown` -uname -v = `(uname -v) 2>/dev/null || echo unknown` +else $as_nop + eval "$3=yes" +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +fi +eval ac_res=\$$3 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +printf "%s\n" "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno -/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` -/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` +} # ac_fn_c_check_type -/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` -/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` -/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` -/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` -/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` -/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` -/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` +# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES +# ------------------------------------------------------- +# Tests whether HEADER exists and can be compiled using the include files in +# INCLUDES, setting the cache variable VAR accordingly. +ac_fn_c_check_header_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +printf %s "checking for $2... " >&6; } +if eval test \${$3+y} +then : + printf %s "(cached) " >&6 +else $as_nop + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +#include <$2> +_ACEOF +if ac_fn_c_try_compile "$LINENO" +then : + eval "$3=yes" +else $as_nop + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +fi +eval ac_res=\$$3 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +printf "%s\n" "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno -_ASUNAME +} # ac_fn_c_check_header_compile -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - echo "PATH: $as_dir" -done -IFS=$as_save_IFS +# ac_fn_c_find_uintX_t LINENO BITS VAR +# ------------------------------------ +# Finds an unsigned integer type with width BITS, setting cache variable VAR +# accordingly. +ac_fn_c_find_uintX_t () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for uint$2_t" >&5 +printf %s "checking for uint$2_t... " >&6; } +if eval test \${$3+y} +then : + printf %s "(cached) " >&6 +else $as_nop + eval "$3=no" + # Order is important - never check a type that is potentially smaller + # than half of the expected target width. + for ac_type in uint$2_t 'unsigned int' 'unsigned long int' \ + 'unsigned long long int' 'unsigned short int' 'unsigned char'; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_includes_default +int +main (void) +{ +static int test_array [1 - 2 * !((($ac_type) -1 >> ($2 / 2 - 1)) >> ($2 / 2 - 1) == 3)]; +test_array [0] = 0; +return test_array [0]; -} >&5 + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO" +then : + case $ac_type in #( + uint$2_t) : + eval "$3=yes" ;; #( + *) : + eval "$3=\$ac_type" ;; +esac +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext + if eval test \"x\$"$3"\" = x"no" +then : -cat >&5 <<_ACEOF +else $as_nop + break +fi + done +fi +eval ac_res=\$$3 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +printf "%s\n" "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno +} # ac_fn_c_find_uintX_t -## ----------- ## -## Core tests. ## -## ----------- ## +# ac_fn_c_try_link LINENO +# ----------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_link () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext conftest.beam conftest$ac_exeext + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +printf "%s\n" "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + test -x conftest$ac_exeext + } +then : + ac_retval=0 +else $as_nop + printf "%s\n" "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 -_ACEOF + ac_retval=1 +fi + # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information + # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would + # interfere with the next link command; also delete a directory that is + # left behind by Apple's compiler. We do this before executing the actions. + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval +} # ac_fn_c_try_link -# Keep a trace of the command line. -# Strip out --no-create and --no-recursion so they do not pile up. -# Strip out --silent because we don't want to record it for future runs. -# Also quote any args containing shell meta-characters. -# Make two passes to allow for proper duplicate-argument suppression. -ac_configure_args= -ac_configure_args0= -ac_configure_args1= -ac_must_keep_next=false -for ac_pass in 1 2 -do - for ac_arg - do - case $ac_arg in - -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; - -q | -quiet | --quiet | --quie | --qui | --qu | --q \ - | -silent | --silent | --silen | --sile | --sil) - continue ;; - *\'*) - ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; - esac - case $ac_pass in - 1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;; - 2) - ac_configure_args1="$ac_configure_args1 '$ac_arg'" - if test $ac_must_keep_next = true; then - ac_must_keep_next=false # Got value, back to normal. - else - case $ac_arg in - *=* | --config-cache | -C | -disable-* | --disable-* \ - | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ - | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ - | -with-* | --with-* | -without-* | --without-* | --x) - case "$ac_configure_args0 " in - "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; - esac - ;; - -* ) ac_must_keep_next=true ;; - esac - fi - ac_configure_args="$ac_configure_args '$ac_arg'" - ;; - esac - done -done -$as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; } -$as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; } +# ac_fn_c_check_func LINENO FUNC VAR +# ---------------------------------- +# Tests whether FUNC exists, setting the cache variable VAR accordingly +ac_fn_c_check_func () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +printf %s "checking for $2... " >&6; } +if eval test \${$3+y} +then : + printf %s "(cached) " >&6 +else $as_nop + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +/* Define $2 to an innocuous variant, in case declares $2. + For example, HP-UX 11i declares gettimeofday. */ +#define $2 innocuous_$2 -# When interrupted or exit'd, cleanup temporary files, and complete -# config.log. We remove comments because anyway the quotes in there -# would cause problems or look ugly. -# WARNING: Use '\'' to represent an apostrophe within the trap. -# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. -trap 'exit_status=$? - # Save into config.log some information that might help in debugging. - { - echo - - cat <<\_ASBOX -## ---------------- ## -## Cache variables. ## -## ---------------- ## -_ASBOX - echo - # The following way of writing the cache mishandles newlines in values, -( - for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do - eval ac_val=\$$ac_var - case $ac_val in #( - *${as_nl}*) - case $ac_var in #( - *_cv_*) { echo "$as_me:$LINENO: WARNING: Cache variable $ac_var contains a newline." >&5 -echo "$as_me: WARNING: Cache variable $ac_var contains a newline." >&2;} ;; - esac - case $ac_var in #( - _ | IFS | as_nl) ;; #( - *) $as_unset $ac_var ;; - esac ;; - esac - done - (set) 2>&1 | - case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( - *${as_nl}ac_space=\ *) - sed -n \ - "s/'\''/'\''\\\\'\'''\''/g; - s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" - ;; #( - *) - sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" - ;; - esac | - sort -) - echo - - cat <<\_ASBOX -## ----------------- ## -## Output variables. ## -## ----------------- ## -_ASBOX - echo - for ac_var in $ac_subst_vars - do - eval ac_val=\$$ac_var - case $ac_val in - *\'\''*) ac_val=`echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; - esac - echo "$ac_var='\''$ac_val'\''" - done | sort - echo - - if test -n "$ac_subst_files"; then - cat <<\_ASBOX -## ------------------- ## -## File substitutions. ## -## ------------------- ## -_ASBOX - echo - for ac_var in $ac_subst_files - do - eval ac_val=\$$ac_var - case $ac_val in - *\'\''*) ac_val=`echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; - esac - echo "$ac_var='\''$ac_val'\''" - done | sort - echo - fi - - if test -s confdefs.h; then - cat <<\_ASBOX -## ----------- ## -## confdefs.h. ## -## ----------- ## -_ASBOX - echo - cat confdefs.h - echo - fi - test "$ac_signal" != 0 && - echo "$as_me: caught signal $ac_signal" - echo "$as_me: exit $exit_status" - } >&5 - rm -f core *.core core.conftest.* && - rm -f -r conftest* confdefs* conf$$* $ac_clean_files && - exit $exit_status -' 0 -for ac_signal in 1 2 13 15; do - trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal -done -ac_signal=0 - -# confdefs.h avoids OS command line length limits that DEFS can exceed. -rm -f -r conftest* confdefs.h - -# Predefined preprocessor variables. - -cat >>confdefs.h <<_ACEOF -#define PACKAGE_NAME "$PACKAGE_NAME" -_ACEOF - - -cat >>confdefs.h <<_ACEOF -#define PACKAGE_TARNAME "$PACKAGE_TARNAME" -_ACEOF - - -cat >>confdefs.h <<_ACEOF -#define PACKAGE_VERSION "$PACKAGE_VERSION" -_ACEOF - - -cat >>confdefs.h <<_ACEOF -#define PACKAGE_STRING "$PACKAGE_STRING" -_ACEOF - - -cat >>confdefs.h <<_ACEOF -#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" -_ACEOF - - -# Let the site file select an alternate cache file if it wants to. -# Prefer explicitly selected file to automatically selected ones. -if test -n "$CONFIG_SITE"; then - set x "$CONFIG_SITE" -elif test "x$prefix" != xNONE; then - set x "$prefix/share/config.site" "$prefix/etc/config.site" -else - set x "$ac_default_prefix/share/config.site" \ - "$ac_default_prefix/etc/config.site" -fi -shift -for ac_site_file -do - if test -r "$ac_site_file"; then - { echo "$as_me:$LINENO: loading site script $ac_site_file" >&5 -echo "$as_me: loading site script $ac_site_file" >&6;} - sed 's/^/| /' "$ac_site_file" >&5 - . "$ac_site_file" - fi -done - -if test -r "$cache_file"; then - # Some versions of bash will fail to source /dev/null (special - # files actually), so we avoid doing that. - if test -f "$cache_file"; then - { echo "$as_me:$LINENO: loading cache $cache_file" >&5 -echo "$as_me: loading cache $cache_file" >&6;} - case $cache_file in - [\\/]* | ?:[\\/]* ) . "$cache_file";; - *) . "./$cache_file";; - esac - fi -else - { echo "$as_me:$LINENO: creating cache $cache_file" >&5 -echo "$as_me: creating cache $cache_file" >&6;} - >$cache_file -fi - -# Check that the precious variables saved in the cache have kept the same -# value. -ac_cache_corrupted=false -for ac_var in $ac_precious_vars; do - eval ac_old_set=\$ac_cv_env_${ac_var}_set - eval ac_new_set=\$ac_env_${ac_var}_set - eval ac_old_val=\$ac_cv_env_${ac_var}_value - eval ac_new_val=\$ac_env_${ac_var}_value - case $ac_old_set,$ac_new_set in - set,) - { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 -echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} - ac_cache_corrupted=: ;; - ,set) - { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5 -echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} - ac_cache_corrupted=: ;; - ,);; - *) - if test "x$ac_old_val" != "x$ac_new_val"; then - { echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5 -echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} - { echo "$as_me:$LINENO: former value: $ac_old_val" >&5 -echo "$as_me: former value: $ac_old_val" >&2;} - { echo "$as_me:$LINENO: current value: $ac_new_val" >&5 -echo "$as_me: current value: $ac_new_val" >&2;} - ac_cache_corrupted=: - fi;; - esac - # Pass precious variables to config.status. - if test "$ac_new_set" = set; then - case $ac_new_val in - *\'*) ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; - *) ac_arg=$ac_var=$ac_new_val ;; - esac - case " $ac_configure_args " in - *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. - *) ac_configure_args="$ac_configure_args '$ac_arg'" ;; - esac - fi -done -if $ac_cache_corrupted; then - { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5 -echo "$as_me: error: changes in the environment can compromise the build" >&2;} - { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5 -echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;} - { (exit 1); exit 1; }; } -fi - - - - - - - - - - - - - - - - - - - - - - - - - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - - - - - - -cat >>confdefs.h <<_ACEOF -#define WCSLIB_VERSION $PACKAGE_VERSION -_ACEOF - - -# Library version number, same as package version. -LIBVER="$PACKAGE_VERSION" - - - -ac_aux_dir= -for ac_dir in config "$srcdir"/config; do - if test -f "$ac_dir/install-sh"; then - ac_aux_dir=$ac_dir - ac_install_sh="$ac_aux_dir/install-sh -c" - break - elif test -f "$ac_dir/install.sh"; then - ac_aux_dir=$ac_dir - ac_install_sh="$ac_aux_dir/install.sh -c" - break - elif test -f "$ac_dir/shtool"; then - ac_aux_dir=$ac_dir - ac_install_sh="$ac_aux_dir/shtool install -c" - break - fi -done -if test -z "$ac_aux_dir"; then - { { echo "$as_me:$LINENO: error: cannot find install-sh or install.sh in config \"$srcdir\"/config" >&5 -echo "$as_me: error: cannot find install-sh or install.sh in config \"$srcdir\"/config" >&2;} - { (exit 1); exit 1; }; } -fi - -# These three variables are undocumented and unsupported, -# and are intended to be withdrawn in a future Autoconf release. -# They can cause serious problems if a builder's source tree is in a directory -# whose full name contains unusual characters. -ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. -ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. -ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. - - - -# Get the system type. -# Make sure we can run config.sub. -$SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 || - { { echo "$as_me:$LINENO: error: cannot run $SHELL $ac_aux_dir/config.sub" >&5 -echo "$as_me: error: cannot run $SHELL $ac_aux_dir/config.sub" >&2;} - { (exit 1); exit 1; }; } - -{ echo "$as_me:$LINENO: checking build system type" >&5 -echo $ECHO_N "checking build system type... $ECHO_C" >&6; } -if test "${ac_cv_build+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - ac_build_alias=$build_alias -test "x$ac_build_alias" = x && - ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"` -test "x$ac_build_alias" = x && - { { echo "$as_me:$LINENO: error: cannot guess build type; you must specify one" >&5 -echo "$as_me: error: cannot guess build type; you must specify one" >&2;} - { (exit 1); exit 1; }; } -ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` || - { { echo "$as_me:$LINENO: error: $SHELL $ac_aux_dir/config.sub $ac_build_alias failed" >&5 -echo "$as_me: error: $SHELL $ac_aux_dir/config.sub $ac_build_alias failed" >&2;} - { (exit 1); exit 1; }; } - -fi -{ echo "$as_me:$LINENO: result: $ac_cv_build" >&5 -echo "${ECHO_T}$ac_cv_build" >&6; } -case $ac_cv_build in -*-*-*) ;; -*) { { echo "$as_me:$LINENO: error: invalid value of canonical build" >&5 -echo "$as_me: error: invalid value of canonical build" >&2;} - { (exit 1); exit 1; }; };; -esac -build=$ac_cv_build -ac_save_IFS=$IFS; IFS='-' -set x $ac_cv_build -shift -build_cpu=$1 -build_vendor=$2 -shift; shift -# Remember, the first character of IFS is used to create $*, -# except with old shells: -build_os=$* -IFS=$ac_save_IFS -case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac - - -ARCH="${build_cpu}-$build_os" - - - -# Look for Flex. -# Extract the first word of "flex", so it can be a program name with args. -set dummy flex; ac_word=$2 -{ echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } -if test "${ac_cv_prog_FLEX+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test -n "$FLEX"; then - ac_cv_prog_FLEX="$FLEX" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then - ac_cv_prog_FLEX="flex" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done -done -IFS=$as_save_IFS - -fi -fi -FLEX=$ac_cv_prog_FLEX -if test -n "$FLEX"; then - { echo "$as_me:$LINENO: result: $FLEX" >&5 -echo "${ECHO_T}$FLEX" >&6; } -else - { echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6; } -fi - - -if test "x$FLEX" = xflex ; then - # Version 2.5.9 or later is required. - V=`flex --version | awk '{print $NF}'` - W=`echo $V | awk -F. '{if ((($1*100 + $2)*100 + $3) < 20509) print "no"}'` - if test "x$W" != x ; then - { echo "$as_me:$LINENO: Flex version $V is too old, ignored." >&5 -echo "$as_me: Flex version $V is too old, ignored." >&6;} - FLEX= - else - { echo "$as_me:$LINENO: Using Flex version $V." >&5 -echo "$as_me: Using Flex version $V." >&6;} - fi -fi - -if test "x$FLEX" = x ; then - { echo "$as_me:$LINENO: WARNING: Flex version 2.5.9 or later does not appear to be - available, will use pre-generated sources." >&5 -echo "$as_me: WARNING: Flex version 2.5.9 or later does not appear to be - available, will use pre-generated sources." >&2;} -fi - - -# Look for an ANSI C compiler. -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu -if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. -set dummy ${ac_tool_prefix}gcc; ac_word=$2 -{ echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } -if test "${ac_cv_prog_CC+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then - ac_cv_prog_CC="${ac_tool_prefix}gcc" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done -done -IFS=$as_save_IFS - -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { echo "$as_me:$LINENO: result: $CC" >&5 -echo "${ECHO_T}$CC" >&6; } -else - { echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6; } -fi - - -fi -if test -z "$ac_cv_prog_CC"; then - ac_ct_CC=$CC - # Extract the first word of "gcc", so it can be a program name with args. -set dummy gcc; ac_word=$2 -{ echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } -if test "${ac_cv_prog_ac_ct_CC+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test -n "$ac_ct_CC"; then - ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then - ac_cv_prog_ac_ct_CC="gcc" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done -done -IFS=$as_save_IFS - -fi -fi -ac_ct_CC=$ac_cv_prog_ac_ct_CC -if test -n "$ac_ct_CC"; then - { echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 -echo "${ECHO_T}$ac_ct_CC" >&6; } -else - { echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6; } -fi - - if test "x$ac_ct_CC" = x; then - CC="" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools -whose name does not start with the host triplet. If you think this -configuration is useful to you, please write to autoconf@gnu.org." >&5 -echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools -whose name does not start with the host triplet. If you think this -configuration is useful to you, please write to autoconf@gnu.org." >&2;} -ac_tool_warned=yes ;; -esac - CC=$ac_ct_CC - fi -else - CC="$ac_cv_prog_CC" -fi - -if test -z "$CC"; then - if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. -set dummy ${ac_tool_prefix}cc; ac_word=$2 -{ echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } -if test "${ac_cv_prog_CC+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then - ac_cv_prog_CC="${ac_tool_prefix}cc" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done -done -IFS=$as_save_IFS - -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { echo "$as_me:$LINENO: result: $CC" >&5 -echo "${ECHO_T}$CC" >&6; } -else - { echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6; } -fi - - - fi -fi -if test -z "$CC"; then - # Extract the first word of "cc", so it can be a program name with args. -set dummy cc; ac_word=$2 -{ echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } -if test "${ac_cv_prog_CC+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else - ac_prog_rejected=no -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then - if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then - ac_prog_rejected=yes - continue - fi - ac_cv_prog_CC="cc" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done -done -IFS=$as_save_IFS - -if test $ac_prog_rejected = yes; then - # We found a bogon in the path, so make sure we never use it. - set dummy $ac_cv_prog_CC - shift - if test $# != 0; then - # We chose a different compiler from the bogus one. - # However, it has the same basename, so the bogon will be chosen - # first if we set CC to just the basename; use the full file name. - shift - ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" - fi -fi -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { echo "$as_me:$LINENO: result: $CC" >&5 -echo "${ECHO_T}$CC" >&6; } -else - { echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6; } -fi - - -fi -if test -z "$CC"; then - if test -n "$ac_tool_prefix"; then - for ac_prog in cl.exe - do - # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. -set dummy $ac_tool_prefix$ac_prog; ac_word=$2 -{ echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } -if test "${ac_cv_prog_CC+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then - ac_cv_prog_CC="$ac_tool_prefix$ac_prog" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done -done -IFS=$as_save_IFS - -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { echo "$as_me:$LINENO: result: $CC" >&5 -echo "${ECHO_T}$CC" >&6; } -else - { echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6; } -fi - - - test -n "$CC" && break - done -fi -if test -z "$CC"; then - ac_ct_CC=$CC - for ac_prog in cl.exe -do - # Extract the first word of "$ac_prog", so it can be a program name with args. -set dummy $ac_prog; ac_word=$2 -{ echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } -if test "${ac_cv_prog_ac_ct_CC+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test -n "$ac_ct_CC"; then - ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then - ac_cv_prog_ac_ct_CC="$ac_prog" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done -done -IFS=$as_save_IFS - -fi -fi -ac_ct_CC=$ac_cv_prog_ac_ct_CC -if test -n "$ac_ct_CC"; then - { echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 -echo "${ECHO_T}$ac_ct_CC" >&6; } -else - { echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6; } -fi - - - test -n "$ac_ct_CC" && break -done - - if test "x$ac_ct_CC" = x; then - CC="" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools -whose name does not start with the host triplet. If you think this -configuration is useful to you, please write to autoconf@gnu.org." >&5 -echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools -whose name does not start with the host triplet. If you think this -configuration is useful to you, please write to autoconf@gnu.org." >&2;} -ac_tool_warned=yes ;; -esac - CC=$ac_ct_CC - fi -fi - -fi - - -test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH -See \`config.log' for more details." >&5 -echo "$as_me: error: no acceptable C compiler found in \$PATH -See \`config.log' for more details." >&2;} - { (exit 1); exit 1; }; } - -# Provide some information about the compiler. -echo "$as_me:$LINENO: checking for C compiler version" >&5 -ac_compiler=`set X $ac_compile; echo $2` -{ (ac_try="$ac_compiler --version >&5" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compiler --version >&5") 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } -{ (ac_try="$ac_compiler -v >&5" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compiler -v >&5") 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } -{ (ac_try="$ac_compiler -V >&5" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compiler -V >&5") 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } - -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -ac_clean_files_save=$ac_clean_files -ac_clean_files="$ac_clean_files a.out a.exe b.out" -# Try to create an executable without -o first, disregard a.out. -# It will help us diagnose broken compilers, and finding out an intuition -# of exeext. -{ echo "$as_me:$LINENO: checking for C compiler default output file name" >&5 -echo $ECHO_N "checking for C compiler default output file name... $ECHO_C" >&6; } -ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` -# -# List of possible output files, starting from the most likely. -# The algorithm is not robust to junk in `.', hence go to wildcards (a.*) -# only as a last resort. b.out is created by i960 compilers. -ac_files='a_out.exe a.exe conftest.exe a.out conftest a.* conftest.* b.out' -# -# The IRIX 6 linker writes into existing files which may not be -# executable, retaining their permissions. Remove them first so a -# subsequent execution test works. -ac_rmfiles= -for ac_file in $ac_files -do - case $ac_file in - *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.o | *.obj ) ;; - * ) ac_rmfiles="$ac_rmfiles $ac_file";; - esac -done -rm -f $ac_rmfiles - -if { (ac_try="$ac_link_default" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_link_default") 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; then - # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. -# So ignore a value of `no', otherwise this would lead to `EXEEXT = no' -# in a Makefile. We should not override ac_cv_exeext if it was cached, -# so that the user can short-circuit this test for compilers unknown to -# Autoconf. -for ac_file in $ac_files '' -do - test -f "$ac_file" || continue - case $ac_file in - *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.o | *.obj ) - ;; - [ab].out ) - # We found the default executable, but exeext='' is most - # certainly right. - break;; - *.* ) - if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; - then :; else - ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` - fi - # We set ac_cv_exeext here because the later test for it is not - # safe: cross compilers may not add the suffix if given an `-o' - # argument, so we may need to know it at that point already. - # Even if this section looks crufty: it has the advantage of - # actually working. - break;; - * ) - break;; - esac -done -test "$ac_cv_exeext" = no && ac_cv_exeext= - -else - ac_file='' -fi - -{ echo "$as_me:$LINENO: result: $ac_file" >&5 -echo "${ECHO_T}$ac_file" >&6; } -if test -z "$ac_file"; then - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -{ { echo "$as_me:$LINENO: error: C compiler cannot create executables -See \`config.log' for more details." >&5 -echo "$as_me: error: C compiler cannot create executables -See \`config.log' for more details." >&2;} - { (exit 77); exit 77; }; } -fi - -ac_exeext=$ac_cv_exeext - -# Check that the compiler produces executables we can run. If not, either -# the compiler is broken, or we cross compile. -{ echo "$as_me:$LINENO: checking whether the C compiler works" >&5 -echo $ECHO_N "checking whether the C compiler works... $ECHO_C" >&6; } -# FIXME: These cross compiler hacks should be removed for Autoconf 3.0 -# If not cross compiling, check that we can run a simple program. -if test "$cross_compiling" != yes; then - if { ac_try='./$ac_file' - { (case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_try") 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - cross_compiling=no - else - if test "$cross_compiling" = maybe; then - cross_compiling=yes - else - { { echo "$as_me:$LINENO: error: cannot run C compiled programs. -If you meant to cross compile, use \`--host'. -See \`config.log' for more details." >&5 -echo "$as_me: error: cannot run C compiled programs. -If you meant to cross compile, use \`--host'. -See \`config.log' for more details." >&2;} - { (exit 1); exit 1; }; } - fi - fi -fi -{ echo "$as_me:$LINENO: result: yes" >&5 -echo "${ECHO_T}yes" >&6; } - -rm -f a.out a.exe conftest$ac_cv_exeext b.out -ac_clean_files=$ac_clean_files_save -# Check that the compiler produces executables we can run. If not, either -# the compiler is broken, or we cross compile. -{ echo "$as_me:$LINENO: checking whether we are cross compiling" >&5 -echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6; } -{ echo "$as_me:$LINENO: result: $cross_compiling" >&5 -echo "${ECHO_T}$cross_compiling" >&6; } - -{ echo "$as_me:$LINENO: checking for suffix of executables" >&5 -echo $ECHO_N "checking for suffix of executables... $ECHO_C" >&6; } -if { (ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_link") 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; then - # If both `conftest.exe' and `conftest' are `present' (well, observable) -# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will -# work properly (i.e., refer to `conftest.exe'), while it won't with -# `rm'. -for ac_file in conftest.exe conftest conftest.*; do - test -f "$ac_file" || continue - case $ac_file in - *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.o | *.obj ) ;; - *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` - break;; - * ) break;; - esac -done -else - { { echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link -See \`config.log' for more details." >&5 -echo "$as_me: error: cannot compute suffix of executables: cannot compile and link -See \`config.log' for more details." >&2;} - { (exit 1); exit 1; }; } -fi - -rm -f conftest$ac_cv_exeext -{ echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5 -echo "${ECHO_T}$ac_cv_exeext" >&6; } - -rm -f conftest.$ac_ext -EXEEXT=$ac_cv_exeext -ac_exeext=$EXEEXT -{ echo "$as_me:$LINENO: checking for suffix of object files" >&5 -echo $ECHO_N "checking for suffix of object files... $ECHO_C" >&6; } -if test "${ac_cv_objext+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -rm -f conftest.o conftest.obj -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; then - for ac_file in conftest.o conftest.obj conftest.*; do - test -f "$ac_file" || continue; - case $ac_file in - *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf ) ;; - *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` - break;; - esac -done -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -{ { echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile -See \`config.log' for more details." >&5 -echo "$as_me: error: cannot compute suffix of object files: cannot compile -See \`config.log' for more details." >&2;} - { (exit 1); exit 1; }; } -fi - -rm -f conftest.$ac_cv_objext conftest.$ac_ext -fi -{ echo "$as_me:$LINENO: result: $ac_cv_objext" >&5 -echo "${ECHO_T}$ac_cv_objext" >&6; } -OBJEXT=$ac_cv_objext -ac_objext=$OBJEXT -{ echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5 -echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6; } -if test "${ac_cv_c_compiler_gnu+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ - -int -main () -{ -#ifndef __GNUC__ - choke me -#endif - - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then - ac_compiler_gnu=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_compiler_gnu=no -fi - -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -ac_cv_c_compiler_gnu=$ac_compiler_gnu - -fi -{ echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5 -echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6; } -GCC=`test $ac_compiler_gnu = yes && echo yes` -ac_test_CFLAGS=${CFLAGS+set} -ac_save_CFLAGS=$CFLAGS -{ echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5 -echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6; } -if test "${ac_cv_prog_cc_g+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - ac_save_c_werror_flag=$ac_c_werror_flag - ac_c_werror_flag=yes - ac_cv_prog_cc_g=no - CFLAGS="-g" - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then - ac_cv_prog_cc_g=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - CFLAGS="" - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then - : -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_c_werror_flag=$ac_save_c_werror_flag - CFLAGS="-g" - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then - ac_cv_prog_cc_g=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - -fi - -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi - -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi - -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - ac_c_werror_flag=$ac_save_c_werror_flag -fi -{ echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5 -echo "${ECHO_T}$ac_cv_prog_cc_g" >&6; } -if test "$ac_test_CFLAGS" = set; then - CFLAGS=$ac_save_CFLAGS -elif test $ac_cv_prog_cc_g = yes; then - if test "$GCC" = yes; then - CFLAGS="-g -O2" - else - CFLAGS="-g" - fi -else - if test "$GCC" = yes; then - CFLAGS="-O2" - else - CFLAGS= - fi -fi -{ echo "$as_me:$LINENO: checking for $CC option to accept ISO C89" >&5 -echo $ECHO_N "checking for $CC option to accept ISO C89... $ECHO_C" >&6; } -if test "${ac_cv_prog_cc_c89+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - ac_cv_prog_cc_c89=no -ac_save_CC=$CC -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include -#include -#include -#include -/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ -struct buf { int x; }; -FILE * (*rcsopen) (struct buf *, struct stat *, int); -static char *e (p, i) - char **p; - int i; -{ - return p[i]; -} -static char *f (char * (*g) (char **, int), char **p, ...) -{ - char *s; - va_list v; - va_start (v,p); - s = g (p, va_arg (v,int)); - va_end (v); - return s; -} - -/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has - function prototypes and stuff, but not '\xHH' hex character constants. - These don't provoke an error unfortunately, instead are silently treated - as 'x'. The following induces an error, until -std is added to get - proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an - array size at least. It's necessary to write '\x00'==0 to get something - that's true only with -std. */ -int osf4_cc_array ['\x00' == 0 ? 1 : -1]; - -/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters - inside strings and character constants. */ -#define FOO(x) 'x' -int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; - -int test (int i, double x); -struct s1 {int (*f) (int a);}; -struct s2 {int (*f) (double a);}; -int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); -int argc; -char **argv; -int -main () -{ -return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; - ; - return 0; -} -_ACEOF -for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ - -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" -do - CC="$ac_save_CC $ac_arg" - rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then - ac_cv_prog_cc_c89=$ac_arg -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - -fi - -rm -f core conftest.err conftest.$ac_objext - test "x$ac_cv_prog_cc_c89" != "xno" && break -done -rm -f conftest.$ac_ext -CC=$ac_save_CC - -fi -# AC_CACHE_VAL -case "x$ac_cv_prog_cc_c89" in - x) - { echo "$as_me:$LINENO: result: none needed" >&5 -echo "${ECHO_T}none needed" >&6; } ;; - xno) - { echo "$as_me:$LINENO: result: unsupported" >&5 -echo "${ECHO_T}unsupported" >&6; } ;; - *) - CC="$CC $ac_cv_prog_cc_c89" - { echo "$as_me:$LINENO: result: $ac_cv_prog_cc_c89" >&5 -echo "${ECHO_T}$ac_cv_prog_cc_c89" >&6; } ;; -esac - - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu -{ echo "$as_me:$LINENO: checking how to run the C preprocessor" >&5 -echo $ECHO_N "checking how to run the C preprocessor... $ECHO_C" >&6; } -# On Suns, sometimes $CPP names a directory. -if test -n "$CPP" && test -d "$CPP"; then - CPP= -fi -if test -z "$CPP"; then - if test "${ac_cv_prog_CPP+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - # Double quotes because CPP needs to be expanded - for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" - do - ac_preproc_ok=false -for ac_c_preproc_warn_flag in '' yes -do - # Use a header file that comes with gcc, so configuring glibc - # with a fresh cross-compiler works. - # Prefer to if __STDC__ is defined, since - # exists even on freestanding compilers. - # On the NeXT, cc -E runs the code through the compiler's parser, - # not just through cpp. "Syntax error" is here to catch this case. - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#ifdef __STDC__ -# include -#else -# include -#endif - Syntax error -_ACEOF -if { (ac_try="$ac_cpp conftest.$ac_ext" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } >/dev/null && { - test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || - test ! -s conftest.err - }; then - : -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - # Broken: fails on valid input. -continue -fi - -rm -f conftest.err conftest.$ac_ext - - # OK, works on sane cases. Now check whether nonexistent headers - # can be detected and how. - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include -_ACEOF -if { (ac_try="$ac_cpp conftest.$ac_ext" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } >/dev/null && { - test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || - test ! -s conftest.err - }; then - # Broken: success on invalid input. -continue -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - # Passes both tests. -ac_preproc_ok=: -break -fi - -rm -f conftest.err conftest.$ac_ext - -done -# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. -rm -f conftest.err conftest.$ac_ext -if $ac_preproc_ok; then - break -fi - - done - ac_cv_prog_CPP=$CPP - -fi - CPP=$ac_cv_prog_CPP -else - ac_cv_prog_CPP=$CPP -fi -{ echo "$as_me:$LINENO: result: $CPP" >&5 -echo "${ECHO_T}$CPP" >&6; } -ac_preproc_ok=false -for ac_c_preproc_warn_flag in '' yes -do - # Use a header file that comes with gcc, so configuring glibc - # with a fresh cross-compiler works. - # Prefer to if __STDC__ is defined, since - # exists even on freestanding compilers. - # On the NeXT, cc -E runs the code through the compiler's parser, - # not just through cpp. "Syntax error" is here to catch this case. - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#ifdef __STDC__ -# include -#else -# include -#endif - Syntax error -_ACEOF -if { (ac_try="$ac_cpp conftest.$ac_ext" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } >/dev/null && { - test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || - test ! -s conftest.err - }; then - : -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - # Broken: fails on valid input. -continue -fi - -rm -f conftest.err conftest.$ac_ext - - # OK, works on sane cases. Now check whether nonexistent headers - # can be detected and how. - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include -_ACEOF -if { (ac_try="$ac_cpp conftest.$ac_ext" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } >/dev/null && { - test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || - test ! -s conftest.err - }; then - # Broken: success on invalid input. -continue -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - # Passes both tests. -ac_preproc_ok=: -break -fi - -rm -f conftest.err conftest.$ac_ext - -done -# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. -rm -f conftest.err conftest.$ac_ext -if $ac_preproc_ok; then - : -else - { { echo "$as_me:$LINENO: error: C preprocessor \"$CPP\" fails sanity check -See \`config.log' for more details." >&5 -echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check -See \`config.log' for more details." >&2;} - { (exit 1); exit 1; }; } -fi - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu -if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. -set dummy ${ac_tool_prefix}gcc; ac_word=$2 -{ echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } -if test "${ac_cv_prog_CC+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then - ac_cv_prog_CC="${ac_tool_prefix}gcc" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done -done -IFS=$as_save_IFS - -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { echo "$as_me:$LINENO: result: $CC" >&5 -echo "${ECHO_T}$CC" >&6; } -else - { echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6; } -fi - - -fi -if test -z "$ac_cv_prog_CC"; then - ac_ct_CC=$CC - # Extract the first word of "gcc", so it can be a program name with args. -set dummy gcc; ac_word=$2 -{ echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } -if test "${ac_cv_prog_ac_ct_CC+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test -n "$ac_ct_CC"; then - ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then - ac_cv_prog_ac_ct_CC="gcc" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done -done -IFS=$as_save_IFS - -fi -fi -ac_ct_CC=$ac_cv_prog_ac_ct_CC -if test -n "$ac_ct_CC"; then - { echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 -echo "${ECHO_T}$ac_ct_CC" >&6; } -else - { echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6; } -fi - - if test "x$ac_ct_CC" = x; then - CC="" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools -whose name does not start with the host triplet. If you think this -configuration is useful to you, please write to autoconf@gnu.org." >&5 -echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools -whose name does not start with the host triplet. If you think this -configuration is useful to you, please write to autoconf@gnu.org." >&2;} -ac_tool_warned=yes ;; -esac - CC=$ac_ct_CC - fi -else - CC="$ac_cv_prog_CC" -fi - -if test -z "$CC"; then - if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. -set dummy ${ac_tool_prefix}cc; ac_word=$2 -{ echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } -if test "${ac_cv_prog_CC+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then - ac_cv_prog_CC="${ac_tool_prefix}cc" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done -done -IFS=$as_save_IFS - -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { echo "$as_me:$LINENO: result: $CC" >&5 -echo "${ECHO_T}$CC" >&6; } -else - { echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6; } -fi - - - fi -fi -if test -z "$CC"; then - # Extract the first word of "cc", so it can be a program name with args. -set dummy cc; ac_word=$2 -{ echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } -if test "${ac_cv_prog_CC+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else - ac_prog_rejected=no -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then - if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then - ac_prog_rejected=yes - continue - fi - ac_cv_prog_CC="cc" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done -done -IFS=$as_save_IFS - -if test $ac_prog_rejected = yes; then - # We found a bogon in the path, so make sure we never use it. - set dummy $ac_cv_prog_CC - shift - if test $# != 0; then - # We chose a different compiler from the bogus one. - # However, it has the same basename, so the bogon will be chosen - # first if we set CC to just the basename; use the full file name. - shift - ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" - fi -fi -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { echo "$as_me:$LINENO: result: $CC" >&5 -echo "${ECHO_T}$CC" >&6; } -else - { echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6; } -fi - - -fi -if test -z "$CC"; then - if test -n "$ac_tool_prefix"; then - for ac_prog in cl.exe - do - # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. -set dummy $ac_tool_prefix$ac_prog; ac_word=$2 -{ echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } -if test "${ac_cv_prog_CC+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then - ac_cv_prog_CC="$ac_tool_prefix$ac_prog" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done -done -IFS=$as_save_IFS - -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { echo "$as_me:$LINENO: result: $CC" >&5 -echo "${ECHO_T}$CC" >&6; } -else - { echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6; } -fi - - - test -n "$CC" && break - done -fi -if test -z "$CC"; then - ac_ct_CC=$CC - for ac_prog in cl.exe -do - # Extract the first word of "$ac_prog", so it can be a program name with args. -set dummy $ac_prog; ac_word=$2 -{ echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } -if test "${ac_cv_prog_ac_ct_CC+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test -n "$ac_ct_CC"; then - ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then - ac_cv_prog_ac_ct_CC="$ac_prog" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done -done -IFS=$as_save_IFS - -fi -fi -ac_ct_CC=$ac_cv_prog_ac_ct_CC -if test -n "$ac_ct_CC"; then - { echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 -echo "${ECHO_T}$ac_ct_CC" >&6; } -else - { echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6; } -fi - - - test -n "$ac_ct_CC" && break -done - - if test "x$ac_ct_CC" = x; then - CC="" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools -whose name does not start with the host triplet. If you think this -configuration is useful to you, please write to autoconf@gnu.org." >&5 -echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools -whose name does not start with the host triplet. If you think this -configuration is useful to you, please write to autoconf@gnu.org." >&2;} -ac_tool_warned=yes ;; -esac - CC=$ac_ct_CC - fi -fi - -fi - - -test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH -See \`config.log' for more details." >&5 -echo "$as_me: error: no acceptable C compiler found in \$PATH -See \`config.log' for more details." >&2;} - { (exit 1); exit 1; }; } - -# Provide some information about the compiler. -echo "$as_me:$LINENO: checking for C compiler version" >&5 -ac_compiler=`set X $ac_compile; echo $2` -{ (ac_try="$ac_compiler --version >&5" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compiler --version >&5") 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } -{ (ac_try="$ac_compiler -v >&5" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compiler -v >&5") 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } -{ (ac_try="$ac_compiler -V >&5" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compiler -V >&5") 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } - -{ echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5 -echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6; } -if test "${ac_cv_c_compiler_gnu+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ - -int -main () -{ -#ifndef __GNUC__ - choke me -#endif - - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then - ac_compiler_gnu=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_compiler_gnu=no -fi - -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -ac_cv_c_compiler_gnu=$ac_compiler_gnu - -fi -{ echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5 -echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6; } -GCC=`test $ac_compiler_gnu = yes && echo yes` -ac_test_CFLAGS=${CFLAGS+set} -ac_save_CFLAGS=$CFLAGS -{ echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5 -echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6; } -if test "${ac_cv_prog_cc_g+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - ac_save_c_werror_flag=$ac_c_werror_flag - ac_c_werror_flag=yes - ac_cv_prog_cc_g=no - CFLAGS="-g" - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then - ac_cv_prog_cc_g=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - CFLAGS="" - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then - : -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_c_werror_flag=$ac_save_c_werror_flag - CFLAGS="-g" - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then - ac_cv_prog_cc_g=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - -fi - -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi - -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi - -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - ac_c_werror_flag=$ac_save_c_werror_flag -fi -{ echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5 -echo "${ECHO_T}$ac_cv_prog_cc_g" >&6; } -if test "$ac_test_CFLAGS" = set; then - CFLAGS=$ac_save_CFLAGS -elif test $ac_cv_prog_cc_g = yes; then - if test "$GCC" = yes; then - CFLAGS="-g -O2" - else - CFLAGS="-g" - fi -else - if test "$GCC" = yes; then - CFLAGS="-O2" - else - CFLAGS= - fi -fi -{ echo "$as_me:$LINENO: checking for $CC option to accept ISO C89" >&5 -echo $ECHO_N "checking for $CC option to accept ISO C89... $ECHO_C" >&6; } -if test "${ac_cv_prog_cc_c89+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - ac_cv_prog_cc_c89=no -ac_save_CC=$CC -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include -#include -#include -#include -/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ -struct buf { int x; }; -FILE * (*rcsopen) (struct buf *, struct stat *, int); -static char *e (p, i) - char **p; - int i; -{ - return p[i]; -} -static char *f (char * (*g) (char **, int), char **p, ...) -{ - char *s; - va_list v; - va_start (v,p); - s = g (p, va_arg (v,int)); - va_end (v); - return s; -} - -/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has - function prototypes and stuff, but not '\xHH' hex character constants. - These don't provoke an error unfortunately, instead are silently treated - as 'x'. The following induces an error, until -std is added to get - proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an - array size at least. It's necessary to write '\x00'==0 to get something - that's true only with -std. */ -int osf4_cc_array ['\x00' == 0 ? 1 : -1]; - -/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters - inside strings and character constants. */ -#define FOO(x) 'x' -int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; - -int test (int i, double x); -struct s1 {int (*f) (int a);}; -struct s2 {int (*f) (double a);}; -int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); -int argc; -char **argv; -int -main () -{ -return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; - ; - return 0; -} -_ACEOF -for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ - -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" -do - CC="$ac_save_CC $ac_arg" - rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then - ac_cv_prog_cc_c89=$ac_arg -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - -fi - -rm -f core conftest.err conftest.$ac_objext - test "x$ac_cv_prog_cc_c89" != "xno" && break -done -rm -f conftest.$ac_ext -CC=$ac_save_CC - -fi -# AC_CACHE_VAL -case "x$ac_cv_prog_cc_c89" in - x) - { echo "$as_me:$LINENO: result: none needed" >&5 -echo "${ECHO_T}none needed" >&6; } ;; - xno) - { echo "$as_me:$LINENO: result: unsupported" >&5 -echo "${ECHO_T}unsupported" >&6; } ;; - *) - CC="$CC $ac_cv_prog_cc_c89" - { echo "$as_me:$LINENO: result: $ac_cv_prog_cc_c89" >&5 -echo "${ECHO_T}$ac_cv_prog_cc_c89" >&6; } ;; -esac - - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - - case $ac_cv_prog_cc_stdc in - no) ac_cv_prog_cc_c99=no; ac_cv_prog_cc_c89=no ;; - *) { echo "$as_me:$LINENO: checking for $CC option to accept ISO C99" >&5 -echo $ECHO_N "checking for $CC option to accept ISO C99... $ECHO_C" >&6; } -if test "${ac_cv_prog_cc_c99+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - ac_cv_prog_cc_c99=no -ac_save_CC=$CC -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include -#include -#include -#include -#include - -// Check varargs macros. These examples are taken from C99 6.10.3.5. -#define debug(...) fprintf (stderr, __VA_ARGS__) -#define showlist(...) puts (#__VA_ARGS__) -#define report(test,...) ((test) ? puts (#test) : printf (__VA_ARGS__)) -static void -test_varargs_macros (void) -{ - int x = 1234; - int y = 5678; - debug ("Flag"); - debug ("X = %d\n", x); - showlist (The first, second, and third items.); - report (x>y, "x is %d but y is %d", x, y); -} - -// Check long long types. -#define BIG64 18446744073709551615ull -#define BIG32 4294967295ul -#define BIG_OK (BIG64 / BIG32 == 4294967297ull && BIG64 % BIG32 == 0) -#if !BIG_OK - your preprocessor is broken; -#endif -#if BIG_OK -#else - your preprocessor is broken; -#endif -static long long int bignum = -9223372036854775807LL; -static unsigned long long int ubignum = BIG64; - -struct incomplete_array -{ - int datasize; - double data[]; -}; - -struct named_init { - int number; - const wchar_t *name; - double average; -}; - -typedef const char *ccp; - -static inline int -test_restrict (ccp restrict text) -{ - // See if C++-style comments work. - // Iterate through items via the restricted pointer. - // Also check for declarations in for loops. - for (unsigned int i = 0; *(text+i) != '\0'; ++i) - continue; - return 0; -} - -// Check varargs and va_copy. -static void -test_varargs (const char *format, ...) -{ - va_list args; - va_start (args, format); - va_list args_copy; - va_copy (args_copy, args); - - const char *str; - int number; - float fnumber; - - while (*format) - { - switch (*format++) - { - case 's': // string - str = va_arg (args_copy, const char *); - break; - case 'd': // int - number = va_arg (args_copy, int); - break; - case 'f': // float - fnumber = va_arg (args_copy, double); - break; - default: - break; - } - } - va_end (args_copy); - va_end (args); -} - -int -main () -{ - - // Check bool. - _Bool success = false; - - // Check restrict. - if (test_restrict ("String literal") == 0) - success = true; - char *restrict newvar = "Another string"; - - // Check varargs. - test_varargs ("s, d' f .", "string", 65, 34.234); - test_varargs_macros (); - - // Check flexible array members. - struct incomplete_array *ia = - malloc (sizeof (struct incomplete_array) + (sizeof (double) * 10)); - ia->datasize = 10; - for (int i = 0; i < ia->datasize; ++i) - ia->data[i] = i * 1.234; - - // Check named initializers. - struct named_init ni = { - .number = 34, - .name = L"Test wide string", - .average = 543.34343, - }; - - ni.number = 58; - - int dynamic_array[ni.number]; - dynamic_array[ni.number - 1] = 543; - - // work around unused variable warnings - return (!success || bignum == 0LL || ubignum == 0uLL || newvar[0] == 'x' - || dynamic_array[ni.number - 1] != 543); - - ; - return 0; -} -_ACEOF -for ac_arg in '' -std=gnu99 -c99 -qlanglvl=extc99 -do - CC="$ac_save_CC $ac_arg" - rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then - ac_cv_prog_cc_c99=$ac_arg -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - -fi - -rm -f core conftest.err conftest.$ac_objext - test "x$ac_cv_prog_cc_c99" != "xno" && break -done -rm -f conftest.$ac_ext -CC=$ac_save_CC - -fi -# AC_CACHE_VAL -case "x$ac_cv_prog_cc_c99" in - x) - { echo "$as_me:$LINENO: result: none needed" >&5 -echo "${ECHO_T}none needed" >&6; } ;; - xno) - { echo "$as_me:$LINENO: result: unsupported" >&5 -echo "${ECHO_T}unsupported" >&6; } ;; - *) - CC="$CC $ac_cv_prog_cc_c99" - { echo "$as_me:$LINENO: result: $ac_cv_prog_cc_c99" >&5 -echo "${ECHO_T}$ac_cv_prog_cc_c99" >&6; } ;; -esac -if test "x$ac_cv_prog_cc_c99" != xno; then - ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c99 -else - { echo "$as_me:$LINENO: checking for $CC option to accept ISO C89" >&5 -echo $ECHO_N "checking for $CC option to accept ISO C89... $ECHO_C" >&6; } -if test "${ac_cv_prog_cc_c89+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - ac_cv_prog_cc_c89=no -ac_save_CC=$CC -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include -#include -#include -#include -/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ -struct buf { int x; }; -FILE * (*rcsopen) (struct buf *, struct stat *, int); -static char *e (p, i) - char **p; - int i; -{ - return p[i]; -} -static char *f (char * (*g) (char **, int), char **p, ...) -{ - char *s; - va_list v; - va_start (v,p); - s = g (p, va_arg (v,int)); - va_end (v); - return s; -} - -/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has - function prototypes and stuff, but not '\xHH' hex character constants. - These don't provoke an error unfortunately, instead are silently treated - as 'x'. The following induces an error, until -std is added to get - proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an - array size at least. It's necessary to write '\x00'==0 to get something - that's true only with -std. */ -int osf4_cc_array ['\x00' == 0 ? 1 : -1]; - -/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters - inside strings and character constants. */ -#define FOO(x) 'x' -int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; - -int test (int i, double x); -struct s1 {int (*f) (int a);}; -struct s2 {int (*f) (double a);}; -int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); -int argc; -char **argv; -int -main () -{ -return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; - ; - return 0; -} -_ACEOF -for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ - -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" -do - CC="$ac_save_CC $ac_arg" - rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then - ac_cv_prog_cc_c89=$ac_arg -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - -fi - -rm -f core conftest.err conftest.$ac_objext - test "x$ac_cv_prog_cc_c89" != "xno" && break -done -rm -f conftest.$ac_ext -CC=$ac_save_CC - -fi -# AC_CACHE_VAL -case "x$ac_cv_prog_cc_c89" in - x) - { echo "$as_me:$LINENO: result: none needed" >&5 -echo "${ECHO_T}none needed" >&6; } ;; - xno) - { echo "$as_me:$LINENO: result: unsupported" >&5 -echo "${ECHO_T}unsupported" >&6; } ;; - *) - CC="$CC $ac_cv_prog_cc_c89" - { echo "$as_me:$LINENO: result: $ac_cv_prog_cc_c89" >&5 -echo "${ECHO_T}$ac_cv_prog_cc_c89" >&6; } ;; -esac -if test "x$ac_cv_prog_cc_c89" != xno; then - ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c89 -else - ac_cv_prog_cc_stdc=no -fi - - -fi - - ;; -esac - { echo "$as_me:$LINENO: checking for $CC option to accept ISO Standard C" >&5 -echo $ECHO_N "checking for $CC option to accept ISO Standard C... $ECHO_C" >&6; } - if test "${ac_cv_prog_cc_stdc+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -fi - - case $ac_cv_prog_cc_stdc in - no) { echo "$as_me:$LINENO: result: unsupported" >&5 -echo "${ECHO_T}unsupported" >&6; } ;; - '') { echo "$as_me:$LINENO: result: none needed" >&5 -echo "${ECHO_T}none needed" >&6; } ;; - *) { echo "$as_me:$LINENO: result: $ac_cv_prog_cc_stdc" >&5 -echo "${ECHO_T}$ac_cv_prog_cc_stdc" >&6; } ;; -esac - - - -{ echo "$as_me:$LINENO: checking for an ANSI C-conforming const" >&5 -echo $ECHO_N "checking for an ANSI C-conforming const... $ECHO_C" >&6; } -if test "${ac_cv_c_const+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ - -int -main () -{ -/* FIXME: Include the comments suggested by Paul. */ -#ifndef __cplusplus - /* Ultrix mips cc rejects this. */ - typedef int charset[2]; - const charset cs; - /* SunOS 4.1.1 cc rejects this. */ - char const *const *pcpcc; - char **ppc; - /* NEC SVR4.0.2 mips cc rejects this. */ - struct point {int x, y;}; - static struct point const zero = {0,0}; - /* AIX XL C 1.02.0.0 rejects this. - It does not let you subtract one const X* pointer from another in - an arm of an if-expression whose if-part is not a constant - expression */ - const char *g = "string"; - pcpcc = &g + (g ? g-g : 0); - /* HPUX 7.0 cc rejects these. */ - ++pcpcc; - ppc = (char**) pcpcc; - pcpcc = (char const *const *) ppc; - { /* SCO 3.2v4 cc rejects this. */ - char *t; - char const *s = 0 ? (char *) 0 : (char const *) 0; - - *t++ = 0; - if (s) return 0; - } - { /* Someone thinks the Sun supposedly-ANSI compiler will reject this. */ - int x[] = {25, 17}; - const int *foo = &x[0]; - ++foo; - } - { /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */ - typedef const int *iptr; - iptr p = 0; - ++p; - } - { /* AIX XL C 1.02.0.0 rejects this saying - "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */ - struct s { int j; const int *ap[3]; }; - struct s *b; b->j = 5; - } - { /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */ - const int foo = 10; - if (!foo) return 0; - } - return !cs[0] && !zero.x; -#endif - - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then - ac_cv_c_const=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_cv_c_const=no -fi - -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -{ echo "$as_me:$LINENO: result: $ac_cv_c_const" >&5 -echo "${ECHO_T}$ac_cv_c_const" >&6; } -if test $ac_cv_c_const = no; then - -cat >>confdefs.h <<\_ACEOF -#define const -_ACEOF - -fi - - -{ echo "$as_me:$LINENO: checking for grep that handles long lines and -e" >&5 -echo $ECHO_N "checking for grep that handles long lines and -e... $ECHO_C" >&6; } -if test "${ac_cv_path_GREP+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - # Extract the first word of "grep ggrep" to use in msg output -if test -z "$GREP"; then -set dummy grep ggrep; ac_prog_name=$2 -if test "${ac_cv_path_GREP+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - ac_path_GREP_found=false -# Loop through the user's path and test for each of PROGNAME-LIST -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_prog in grep ggrep; do - for ac_exec_ext in '' $ac_executable_extensions; do - ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" - { test -f "$ac_path_GREP" && $as_test_x "$ac_path_GREP"; } || continue - # Check for GNU ac_path_GREP and select it if it is found. - # Check for GNU $ac_path_GREP -case `"$ac_path_GREP" --version 2>&1` in -*GNU*) - ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; -*) - ac_count=0 - echo $ECHO_N "0123456789$ECHO_C" >"conftest.in" - while : - do - cat "conftest.in" "conftest.in" >"conftest.tmp" - mv "conftest.tmp" "conftest.in" - cp "conftest.in" "conftest.nl" - echo 'GREP' >> "conftest.nl" - "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break - diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break - ac_count=`expr $ac_count + 1` - if test $ac_count -gt ${ac_path_GREP_max-0}; then - # Best one so far, save it but keep looking for a better one - ac_cv_path_GREP="$ac_path_GREP" - ac_path_GREP_max=$ac_count - fi - # 10*(2^10) chars as input seems more than enough - test $ac_count -gt 10 && break - done - rm -f conftest.in conftest.tmp conftest.nl conftest.out;; -esac - - - $ac_path_GREP_found && break 3 - done -done - -done -IFS=$as_save_IFS - - -fi - -GREP="$ac_cv_path_GREP" -if test -z "$GREP"; then - { { echo "$as_me:$LINENO: error: no acceptable $ac_prog_name could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&5 -echo "$as_me: error: no acceptable $ac_prog_name could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&2;} - { (exit 1); exit 1; }; } -fi - -else - ac_cv_path_GREP=$GREP -fi - - -fi -{ echo "$as_me:$LINENO: result: $ac_cv_path_GREP" >&5 -echo "${ECHO_T}$ac_cv_path_GREP" >&6; } - GREP="$ac_cv_path_GREP" - - -{ echo "$as_me:$LINENO: checking for egrep" >&5 -echo $ECHO_N "checking for egrep... $ECHO_C" >&6; } -if test "${ac_cv_path_EGREP+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 - then ac_cv_path_EGREP="$GREP -E" - else - # Extract the first word of "egrep" to use in msg output -if test -z "$EGREP"; then -set dummy egrep; ac_prog_name=$2 -if test "${ac_cv_path_EGREP+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - ac_path_EGREP_found=false -# Loop through the user's path and test for each of PROGNAME-LIST -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_prog in egrep; do - for ac_exec_ext in '' $ac_executable_extensions; do - ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" - { test -f "$ac_path_EGREP" && $as_test_x "$ac_path_EGREP"; } || continue - # Check for GNU ac_path_EGREP and select it if it is found. - # Check for GNU $ac_path_EGREP -case `"$ac_path_EGREP" --version 2>&1` in -*GNU*) - ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; -*) - ac_count=0 - echo $ECHO_N "0123456789$ECHO_C" >"conftest.in" - while : - do - cat "conftest.in" "conftest.in" >"conftest.tmp" - mv "conftest.tmp" "conftest.in" - cp "conftest.in" "conftest.nl" - echo 'EGREP' >> "conftest.nl" - "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break - diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break - ac_count=`expr $ac_count + 1` - if test $ac_count -gt ${ac_path_EGREP_max-0}; then - # Best one so far, save it but keep looking for a better one - ac_cv_path_EGREP="$ac_path_EGREP" - ac_path_EGREP_max=$ac_count - fi - # 10*(2^10) chars as input seems more than enough - test $ac_count -gt 10 && break - done - rm -f conftest.in conftest.tmp conftest.nl conftest.out;; -esac - - - $ac_path_EGREP_found && break 3 - done -done - -done -IFS=$as_save_IFS - - -fi - -EGREP="$ac_cv_path_EGREP" -if test -z "$EGREP"; then - { { echo "$as_me:$LINENO: error: no acceptable $ac_prog_name could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&5 -echo "$as_me: error: no acceptable $ac_prog_name could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&2;} - { (exit 1); exit 1; }; } -fi - -else - ac_cv_path_EGREP=$EGREP -fi - - - fi -fi -{ echo "$as_me:$LINENO: result: $ac_cv_path_EGREP" >&5 -echo "${ECHO_T}$ac_cv_path_EGREP" >&6; } - EGREP="$ac_cv_path_EGREP" +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $2 (); below. */ +#include +#undef $2 -{ echo "$as_me:$LINENO: checking for ANSI C header files" >&5 -echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6; } -if test "${ac_cv_header_stdc+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include -#include -#include -#include +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char $2 (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub_$2 || defined __stub___$2 +choke me +#endif int -main () +main (void) { - +return $2 (); ; return 0; } _ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then - ac_cv_header_stdc=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_cv_header_stdc=no -fi - -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - -if test $ac_cv_header_stdc = yes; then - # SunOS 4.x string.h does not declare mem*, contrary to ANSI. - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include - -_ACEOF -if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | - $EGREP "memchr" >/dev/null 2>&1; then - : -else - ac_cv_header_stdc=no -fi -rm -f -r conftest* - -fi - -if test $ac_cv_header_stdc = yes; then - # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include - -_ACEOF -if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | - $EGREP "free" >/dev/null 2>&1; then - : -else - ac_cv_header_stdc=no +if ac_fn_c_try_link "$LINENO" +then : + eval "$3=yes" +else $as_nop + eval "$3=no" fi -rm -f -r conftest* - +rm -f core conftest.err conftest.$ac_objext conftest.beam \ + conftest$ac_exeext conftest.$ac_ext fi +eval ac_res=\$$3 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +printf "%s\n" "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno -if test $ac_cv_header_stdc = yes; then - # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. - if test "$cross_compiling" = yes; then - : -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include -#include -#if ((' ' & 0x0FF) == 0x020) -# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') -# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) -#else -# define ISLOWER(c) \ - (('a' <= (c) && (c) <= 'i') \ - || ('j' <= (c) && (c) <= 'r') \ - || ('s' <= (c) && (c) <= 'z')) -# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) -#endif +} # ac_fn_c_check_func -#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) -int -main () +# ac_fn_c_try_run LINENO +# ---------------------- +# Try to run conftest.$ac_ext, and return whether this succeeded. Assumes that +# executables *can* be run. +ac_fn_c_try_run () { - int i; - for (i = 0; i < 256; i++) - if (XOR (islower (i), ISLOWER (i)) - || toupper (i) != TOUPPER (i)) - return 2; - return 0; -} -_ACEOF -rm -f conftest$ac_exeext -if { (ac_try="$ac_link" + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { ac_try='./conftest$ac_exeext' - { (case "(($ac_try" in + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' + { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - : -else - echo "$as_me: program exited with status $ac_status" >&5 -echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -( exit $ac_status ) -ac_cv_header_stdc=no -fi -rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext -fi - - -fi -fi -{ echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5 -echo "${ECHO_T}$ac_cv_header_stdc" >&6; } -if test $ac_cv_header_stdc = yes; then - -cat >>confdefs.h <<\_ACEOF -#define STDC_HEADERS 1 -_ACEOF - -fi - -# On IRIX 5.3, sys/types and inttypes.h are conflicting. - - - - - - - - - -for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ - inttypes.h stdint.h unistd.h -do -as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` -{ echo "$as_me:$LINENO: checking for $ac_header" >&5 -echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; } -if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -$ac_includes_default - -#include <$ac_header> -_ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then - eval "$as_ac_Header=yes" -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - eval "$as_ac_Header=no" -fi - -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -ac_res=`eval echo '${'$as_ac_Header'}'` - { echo "$as_me:$LINENO: result: $ac_res" >&5 -echo "${ECHO_T}$ac_res" >&6; } -if test `eval echo '${'$as_ac_Header'}'` = yes; then - cat >>confdefs.h <<_ACEOF -#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 -_ACEOF - -fi - -done - - -{ echo "$as_me:$LINENO: checking for size_t" >&5 -echo $ECHO_N "checking for size_t... $ECHO_C" >&6; } -if test "${ac_cv_type_size_t+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -$ac_includes_default -typedef size_t ac__type_new_; -int -main () -{ -if ((ac__type_new_ *) 0) - return 0; -if (sizeof (ac__type_new_)) - return 0; - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then - ac_cv_type_size_t=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_cv_type_size_t=no -fi - -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -{ echo "$as_me:$LINENO: result: $ac_cv_type_size_t" >&5 -echo "${ECHO_T}$ac_cv_type_size_t" >&6; } -if test $ac_cv_type_size_t = yes; then - : -else - -cat >>confdefs.h <<_ACEOF -#define size_t unsigned int -_ACEOF - -fi - -if test "x$ac_cv_prog_cc_stdc" = xno -o \ - "x$ac_cv_c_const" = xno -o \ - "x$ac_cv_type_size_t" = xno; then - { { echo "$as_me:$LINENO: error: - ------------------------------------------------------- - An ANSI standard C library is required to build WCSLIB. - - ERROR: WCSLIB configuration failure. - -------------------------------------------------------" >&5 -echo "$as_me: error: - ------------------------------------------------------- - An ANSI standard C library is required to build WCSLIB. - - ERROR: WCSLIB configuration failure. - -------------------------------------------------------" >&2;} - { (exit 1); exit 1; }; } -fi - -# Check for data types (suggested by autoscan - off_t is only required by -# fitshdr). -{ echo "$as_me:$LINENO: checking for off_t" >&5 -echo $ECHO_N "checking for off_t... $ECHO_C" >&6; } -if test "${ac_cv_type_off_t+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -$ac_includes_default -typedef off_t ac__type_new_; -int -main () -{ -if ((ac__type_new_ *) 0) - return 0; -if (sizeof (ac__type_new_)) - return 0; - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then - ac_cv_type_off_t=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_cv_type_off_t=no -fi + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; } +then : + ac_retval=0 +else $as_nop + printf "%s\n" "$as_me: program exited with status $ac_status" >&5 + printf "%s\n" "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_retval=$ac_status fi -{ echo "$as_me:$LINENO: result: $ac_cv_type_off_t" >&5 -echo "${ECHO_T}$ac_cv_type_off_t" >&6; } -if test $ac_cv_type_off_t = yes; then - : -else - -cat >>confdefs.h <<_ACEOF -#define off_t long int -_ACEOF + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval -fi +} # ac_fn_c_try_run +# ac_fn_c_compute_int LINENO EXPR VAR INCLUDES +# -------------------------------------------- +# Tries to find the compile-time value of EXPR in a program that includes +# INCLUDES, setting VAR accordingly. Returns whether the value could be +# computed +ac_fn_c_compute_int () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if test "$cross_compiling" = yes; then + # Depending upon the size, compute the lo and hi bounds. +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main (void) +{ +static int test_array [1 - 2 * !(($2) >= 0)]; +test_array [0] = 0; +return test_array [0]; - { echo "$as_me:$LINENO: checking for int8_t" >&5 -echo $ECHO_N "checking for int8_t... $ECHO_C" >&6; } -if test "${ac_cv_c_int8_t+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - ac_cv_c_int8_t=no - for ac_type in 'int8_t' 'int' 'long int' \ - 'long long int' 'short int' 'signed char'; do - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ + ; + return 0; +} _ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF +if ac_fn_c_try_compile "$LINENO" +then : + ac_lo=0 ac_mid=0 + while :; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -$ac_includes_default +$4 int -main () +main (void) { -static int test_array [1 - 2 * !(0 < ($ac_type) (((($ac_type) 1 << (8 - 2)) - 1) * 2 + 1))]; -test_array [0] = 0 +static int test_array [1 - 2 * !(($2) <= $ac_mid)]; +test_array [0] = 0; +return test_array [0]; ; return 0; } _ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF +if ac_fn_c_try_compile "$LINENO" +then : + ac_hi=$ac_mid; break +else $as_nop + as_fn_arith $ac_mid + 1 && ac_lo=$as_val + if test $ac_lo -le $ac_mid; then + ac_lo= ac_hi= + break + fi + as_fn_arith 2 '*' $ac_mid + 1 && ac_mid=$as_val +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext + done +else $as_nop + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -$ac_includes_default +$4 int -main () +main (void) { -static int test_array [1 - 2 * !(($ac_type) (((($ac_type) 1 << (8 - 2)) - 1) * 2 + 1) - < ($ac_type) (((($ac_type) 1 << (8 - 2)) - 1) * 2 + 2))]; -test_array [0] = 0 +static int test_array [1 - 2 * !(($2) < 0)]; +test_array [0] = 0; +return test_array [0]; ; return 0; } _ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then - : -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - case $ac_type in - int8_t) ac_cv_c_int8_t=yes ;; - *) ac_cv_c_int8_t=$ac_type ;; -esac +if ac_fn_c_try_compile "$LINENO" +then : + ac_hi=-1 ac_mid=-1 + while :; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main (void) +{ +static int test_array [1 - 2 * !(($2) >= $ac_mid)]; +test_array [0] = 0; +return test_array [0]; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO" +then : + ac_lo=$ac_mid; break +else $as_nop + as_fn_arith '(' $ac_mid ')' - 1 && ac_hi=$as_val + if test $ac_mid -le $ac_hi; then + ac_lo= ac_hi= + break + fi + as_fn_arith 2 '*' $ac_mid && ac_mid=$as_val fi - -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext + done +else $as_nop + ac_lo= ac_hi= fi - -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - test "$ac_cv_c_int8_t" != no && break - done +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi -{ echo "$as_me:$LINENO: result: $ac_cv_c_int8_t" >&5 -echo "${ECHO_T}$ac_cv_c_int8_t" >&6; } - case $ac_cv_c_int8_t in #( - no|yes) ;; #( - *) - -cat >>confdefs.h <<_ACEOF -#define int8_t $ac_cv_c_int8_t -_ACEOF -;; - esac - - - { echo "$as_me:$LINENO: checking for int16_t" >&5 -echo $ECHO_N "checking for int16_t... $ECHO_C" >&6; } -if test "${ac_cv_c_int16_t+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - ac_cv_c_int16_t=no - for ac_type in 'int16_t' 'int' 'long int' \ - 'long long int' 'short int' 'signed char'; do - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +# Binary search between lo and hi bounds. +while test "x$ac_lo" != "x$ac_hi"; do + as_fn_arith '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo && ac_mid=$as_val + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -$ac_includes_default +$4 int -main () +main (void) { -static int test_array [1 - 2 * !(0 < ($ac_type) (((($ac_type) 1 << (16 - 2)) - 1) * 2 + 1))]; -test_array [0] = 0 +static int test_array [1 - 2 * !(($2) <= $ac_mid)]; +test_array [0] = 0; +return test_array [0]; ; return 0; } _ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; +if ac_fn_c_try_compile "$LINENO" +then : + ac_hi=$ac_mid +else $as_nop + as_fn_arith '(' $ac_mid ')' + 1 && ac_lo=$as_val +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +done +case $ac_lo in #(( +?*) eval "$3=\$ac_lo"; ac_retval=0 ;; +'') ac_retval=1 ;; esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF + else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -$ac_includes_default +$4 +static long int longval (void) { return $2; } +static unsigned long int ulongval (void) { return $2; } +#include +#include int -main () +main (void) { -static int test_array [1 - 2 * !(($ac_type) (((($ac_type) 1 << (16 - 2)) - 1) * 2 + 1) - < ($ac_type) (((($ac_type) 1 << (16 - 2)) - 1) * 2 + 2))]; -test_array [0] = 0 + + FILE *f = fopen ("conftest.val", "w"); + if (! f) + return 1; + if (($2) < 0) + { + long int i = longval (); + if (i != ($2)) + return 1; + fprintf (f, "%ld", i); + } + else + { + unsigned long int i = ulongval (); + if (i != ($2)) + return 1; + fprintf (f, "%lu", i); + } + /* Do not output a trailing newline, as this causes \r\n confusion + on some platforms. */ + return ferror (f) || fclose (f) != 0; ; return 0; } _ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" +if ac_fn_c_try_run "$LINENO" +then : + echo >>conftest.val; read $3 &5 - (eval "$ac_compile") 2>conftest.er1 +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +printf "%s\n" "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>conftest.err ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_f77_werror_flag" || test ! -s conftest.err - } && test -s conftest.$ac_objext; then - : -else - echo "$as_me: failed program was:" >&5 + } && test -s conftest.$ac_objext +then : + ac_retval=0 +else $as_nop + printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 - case $ac_type in - int16_t) ac_cv_c_int16_t=yes ;; - *) ac_cv_c_int16_t=$ac_type ;; + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_f77_try_compile +ac_configure_args_raw= +for ac_arg +do + case $ac_arg in + *\'*) + ac_arg=`printf "%s\n" "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + as_fn_append ac_configure_args_raw " '$ac_arg'" +done + +case $ac_configure_args_raw in + *$as_nl*) + ac_safe_unquote= ;; + *) + ac_unsafe_z='|&;<>()$`\\"*?[ '' ' # This string ends in space, tab. + ac_unsafe_a="$ac_unsafe_z#~" + ac_safe_unquote="s/ '\\([^$ac_unsafe_a][^$ac_unsafe_z]*\\)'/ \\1/g" + ac_configure_args_raw=` printf "%s\n" "$ac_configure_args_raw" | sed "$ac_safe_unquote"`;; esac -fi +cat >config.log <<_ACEOF +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 +It was created by WCSLIB $as_me 8.6, which was +generated by GNU Autoconf 2.71. Invocation command line was + $ $0$ac_configure_args_raw -fi +_ACEOF +exec 5>>config.log +{ +cat <<_ASUNAME +## --------- ## +## Platform. ## +## --------- ## -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - test "$ac_cv_c_int16_t" != no && break - done -fi -{ echo "$as_me:$LINENO: result: $ac_cv_c_int16_t" >&5 -echo "${ECHO_T}$ac_cv_c_int16_t" >&6; } - case $ac_cv_c_int16_t in #( - no|yes) ;; #( - *) +hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` + +/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` +/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` +/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` +/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` + +_ASUNAME + +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + printf "%s\n" "PATH: $as_dir" + done +IFS=$as_save_IFS + +} >&5 + +cat >&5 <<_ACEOF + + +## ----------- ## +## Core tests. ## +## ----------- ## + +_ACEOF + + +# Keep a trace of the command line. +# Strip out --no-create and --no-recursion so they do not pile up. +# Strip out --silent because we don't want to record it for future runs. +# Also quote any args containing shell meta-characters. +# Make two passes to allow for proper duplicate-argument suppression. +ac_configure_args= +ac_configure_args0= +ac_configure_args1= +ac_must_keep_next=false +for ac_pass in 1 2 +do + for ac_arg + do + case $ac_arg in + -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + continue ;; + *\'*) + ac_arg=`printf "%s\n" "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + case $ac_pass in + 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; + 2) + as_fn_append ac_configure_args1 " '$ac_arg'" + if test $ac_must_keep_next = true; then + ac_must_keep_next=false # Got value, back to normal. + else + case $ac_arg in + *=* | --config-cache | -C | -disable-* | --disable-* \ + | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ + | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ + | -with-* | --with-* | -without-* | --without-* | --x) + case "$ac_configure_args0 " in + "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; + esac + ;; + -* ) ac_must_keep_next=true ;; + esac + fi + as_fn_append ac_configure_args " '$ac_arg'" + ;; + esac + done +done +{ ac_configure_args0=; unset ac_configure_args0;} +{ ac_configure_args1=; unset ac_configure_args1;} + +# When interrupted or exit'd, cleanup temporary files, and complete +# config.log. We remove comments because anyway the quotes in there +# would cause problems or look ugly. +# WARNING: Use '\'' to represent an apostrophe within the trap. +# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. +trap 'exit_status=$? + # Sanitize IFS. + IFS=" "" $as_nl" + # Save into config.log some information that might help in debugging. + { + echo + + printf "%s\n" "## ---------------- ## +## Cache variables. ## +## ---------------- ##" + echo + # The following way of writing the cache mishandles newlines in values, +( + for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +printf "%s\n" "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done + (set) 2>&1 | + case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + sed -n \ + "s/'\''/'\''\\\\'\'''\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" + ;; #( + *) + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) + echo -cat >>confdefs.h <<_ACEOF -#define int16_t $ac_cv_c_int16_t -_ACEOF -;; - esac + printf "%s\n" "## ----------------- ## +## Output variables. ## +## ----------------- ##" + echo + for ac_var in $ac_subst_vars + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`printf "%s\n" "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + printf "%s\n" "$ac_var='\''$ac_val'\''" + done | sort + echo + if test -n "$ac_subst_files"; then + printf "%s\n" "## ------------------- ## +## File substitutions. ## +## ------------------- ##" + echo + for ac_var in $ac_subst_files + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`printf "%s\n" "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + printf "%s\n" "$ac_var='\''$ac_val'\''" + done | sort + echo + fi - { echo "$as_me:$LINENO: checking for int32_t" >&5 -echo $ECHO_N "checking for int32_t... $ECHO_C" >&6; } -if test "${ac_cv_c_int32_t+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - ac_cv_c_int32_t=no - for ac_type in 'int32_t' 'int' 'long int' \ - 'long long int' 'short int' 'signed char'; do - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -$ac_includes_default -int -main () -{ -static int test_array [1 - 2 * !(0 < ($ac_type) (((($ac_type) 1 << (32 - 2)) - 1) * 2 + 1))]; -test_array [0] = 0 + if test -s confdefs.h; then + printf "%s\n" "## ----------- ## +## confdefs.h. ## +## ----------- ##" + echo + cat confdefs.h + echo + fi + test "$ac_signal" != 0 && + printf "%s\n" "$as_me: caught signal $ac_signal" + printf "%s\n" "$as_me: exit $exit_status" + } >&5 + rm -f core *.core core.conftest.* && + rm -f -r conftest* confdefs* conf$$* $ac_clean_files && + exit $exit_status +' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal +done +ac_signal=0 - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -$ac_includes_default -int -main () -{ -static int test_array [1 - 2 * !(($ac_type) (((($ac_type) 1 << (32 - 2)) - 1) * 2 + 1) - < ($ac_type) (((($ac_type) 1 << (32 - 2)) - 1) * 2 + 2))]; -test_array [0] = 0 +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -f -r conftest* confdefs.h - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then - : -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 +printf "%s\n" "/* confdefs.h */" > confdefs.h - case $ac_type in - int32_t) ac_cv_c_int32_t=yes ;; - *) ac_cv_c_int32_t=$ac_type ;; -esac +# Predefined preprocessor variables. -fi +printf "%s\n" "#define PACKAGE_NAME \"$PACKAGE_NAME\"" >>confdefs.h -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 +printf "%s\n" "#define PACKAGE_TARNAME \"$PACKAGE_TARNAME\"" >>confdefs.h +printf "%s\n" "#define PACKAGE_VERSION \"$PACKAGE_VERSION\"" >>confdefs.h -fi +printf "%s\n" "#define PACKAGE_STRING \"$PACKAGE_STRING\"" >>confdefs.h -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - test "$ac_cv_c_int32_t" != no && break - done -fi -{ echo "$as_me:$LINENO: result: $ac_cv_c_int32_t" >&5 -echo "${ECHO_T}$ac_cv_c_int32_t" >&6; } - case $ac_cv_c_int32_t in #( - no|yes) ;; #( - *) +printf "%s\n" "#define PACKAGE_BUGREPORT \"$PACKAGE_BUGREPORT\"" >>confdefs.h -cat >>confdefs.h <<_ACEOF -#define int32_t $ac_cv_c_int32_t -_ACEOF -;; - esac +printf "%s\n" "#define PACKAGE_URL \"$PACKAGE_URL\"" >>confdefs.h - { echo "$as_me:$LINENO: checking for uint8_t" >&5 -echo $ECHO_N "checking for uint8_t... $ECHO_C" >&6; } -if test "${ac_cv_c_uint8_t+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 +# Let the site file select an alternate cache file if it wants to. +# Prefer an explicitly selected file to automatically selected ones. +if test -n "$CONFIG_SITE"; then + ac_site_files="$CONFIG_SITE" +elif test "x$prefix" != xNONE; then + ac_site_files="$prefix/share/config.site $prefix/etc/config.site" else - ac_cv_c_uint8_t=no - for ac_type in 'uint8_t' 'unsigned int' 'unsigned long int' \ - 'unsigned long long int' 'unsigned short int' 'unsigned char'; do - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -$ac_includes_default -int -main () -{ -static int test_array [1 - 2 * !(($ac_type) -1 >> (8 - 1) == 1)]; -test_array [0] = 0 + ac_site_files="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" +fi - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then - case $ac_type in - uint8_t) ac_cv_c_uint8_t=yes ;; - *) ac_cv_c_uint8_t=$ac_type ;; -esac +for ac_site_file in $ac_site_files +do + case $ac_site_file in #( + */*) : + ;; #( + *) : + ac_site_file=./$ac_site_file ;; +esac + if test -f "$ac_site_file" && test -r "$ac_site_file"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 +printf "%s\n" "$as_me: loading site script $ac_site_file" >&6;} + sed 's/^/| /' "$ac_site_file" >&5 + . "$ac_site_file" \ + || { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "failed to load site script $ac_site_file +See \`config.log' for more details" "$LINENO" 5; } + fi +done +if test -r "$cache_file"; then + # Some versions of bash will fail to source /dev/null (special files + # actually), so we avoid doing that. DJGPP emulates it as a regular file. + if test /dev/null != "$cache_file" && test -f "$cache_file"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 +printf "%s\n" "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . "$cache_file";; + *) . "./$cache_file";; + esac + fi else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - -fi - -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - test "$ac_cv_c_uint8_t" != no && break - done + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 +printf "%s\n" "$as_me: creating cache $cache_file" >&6;} + >$cache_file fi -{ echo "$as_me:$LINENO: result: $ac_cv_c_uint8_t" >&5 -echo "${ECHO_T}$ac_cv_c_uint8_t" >&6; } - case $ac_cv_c_uint8_t in #( - no|yes) ;; #( - *) - -cat >>confdefs.h <<\_ACEOF -#define _UINT8_T 1 -_ACEOF - - -cat >>confdefs.h <<_ACEOF -#define uint8_t $ac_cv_c_uint8_t -_ACEOF -;; - esac +# Test code for whether the C compiler supports C89 (global declarations) +ac_c_conftest_c89_globals=' +/* Does the compiler advertise C89 conformance? + Do not test the value of __STDC__, because some compilers set it to 0 + while being otherwise adequately conformant. */ +#if !defined __STDC__ +# error "Compiler does not advertise C89 conformance" +#endif - { echo "$as_me:$LINENO: checking for uint16_t" >&5 -echo $ECHO_N "checking for uint16_t... $ECHO_C" >&6; } -if test "${ac_cv_c_uint16_t+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - ac_cv_c_uint16_t=no - for ac_type in 'uint16_t' 'unsigned int' 'unsigned long int' \ - 'unsigned long long int' 'unsigned short int' 'unsigned char'; do - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -$ac_includes_default -int -main () +#include +#include +struct stat; +/* Most of the following tests are stolen from RCS 5.7 src/conf.sh. */ +struct buf { int x; }; +struct buf * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; { -static int test_array [1 - 2 * !(($ac_type) -1 >> (16 - 1) == 1)]; -test_array [0] = 0 - - ; - return 0; + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; } -_ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then - case $ac_type in - uint16_t) ac_cv_c_uint16_t=yes ;; - *) ac_cv_c_uint16_t=$ac_type ;; -esac - -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not \xHH hex character constants. + These do not provoke an error unfortunately, instead are silently treated + as an "x". The following induces an error, until -std is added to get + proper ANSI mode. Curiously \x00 != x always comes out true, for an + array size at least. It is necessary to write \x00 == 0 to get something + that is true only with -std. */ +int osf4_cc_array ['\''\x00'\'' == 0 ? 1 : -1]; -fi +/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters + inside strings and character constants. */ +#define FOO(x) '\''x'\'' +int xlc6_cc_array[FOO(a) == '\''x'\'' ? 1 : -1]; -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - test "$ac_cv_c_uint16_t" != no && break - done -fi -{ echo "$as_me:$LINENO: result: $ac_cv_c_uint16_t" >&5 -echo "${ECHO_T}$ac_cv_c_uint16_t" >&6; } - case $ac_cv_c_uint16_t in #( - no|yes) ;; #( - *) +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, int *(*)(struct buf *, struct stat *, int), + int, int);' +# Test code for whether the C compiler supports C89 (body of main). +ac_c_conftest_c89_main=' +ok |= (argc == 0 || f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]); +' -cat >>confdefs.h <<_ACEOF -#define uint16_t $ac_cv_c_uint16_t -_ACEOF -;; - esac +# Test code for whether the C compiler supports C99 (global declarations) +ac_c_conftest_c99_globals=' +// Does the compiler advertise C99 conformance? +#if !defined __STDC_VERSION__ || __STDC_VERSION__ < 199901L +# error "Compiler does not advertise C99 conformance" +#endif +#include +extern int puts (const char *); +extern int printf (const char *, ...); +extern int dprintf (int, const char *, ...); +extern void *malloc (size_t); - { echo "$as_me:$LINENO: checking for uint32_t" >&5 -echo $ECHO_N "checking for uint32_t... $ECHO_C" >&6; } -if test "${ac_cv_c_uint32_t+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - ac_cv_c_uint32_t=no - for ac_type in 'uint32_t' 'unsigned int' 'unsigned long int' \ - 'unsigned long long int' 'unsigned short int' 'unsigned char'; do - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -$ac_includes_default -int -main () +// Check varargs macros. These examples are taken from C99 6.10.3.5. +// dprintf is used instead of fprintf to avoid needing to declare +// FILE and stderr. +#define debug(...) dprintf (2, __VA_ARGS__) +#define showlist(...) puts (#__VA_ARGS__) +#define report(test,...) ((test) ? puts (#test) : printf (__VA_ARGS__)) +static void +test_varargs_macros (void) { -static int test_array [1 - 2 * !(($ac_type) -1 >> (32 - 1) == 1)]; -test_array [0] = 0 - - ; - return 0; + int x = 1234; + int y = 5678; + debug ("Flag"); + debug ("X = %d\n", x); + showlist (The first, second, and third items.); + report (x>y, "x is %d but y is %d", x, y); } -_ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then - case $ac_type in - uint32_t) ac_cv_c_uint32_t=yes ;; - *) ac_cv_c_uint32_t=$ac_type ;; -esac - -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 +// Check long long types. +#define BIG64 18446744073709551615ull +#define BIG32 4294967295ul +#define BIG_OK (BIG64 / BIG32 == 4294967297ull && BIG64 % BIG32 == 0) +#if !BIG_OK + #error "your preprocessor is broken" +#endif +#if BIG_OK +#else + #error "your preprocessor is broken" +#endif +static long long int bignum = -9223372036854775807LL; +static unsigned long long int ubignum = BIG64; -fi - -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - test "$ac_cv_c_uint32_t" != no && break - done -fi -{ echo "$as_me:$LINENO: result: $ac_cv_c_uint32_t" >&5 -echo "${ECHO_T}$ac_cv_c_uint32_t" >&6; } - case $ac_cv_c_uint32_t in #( - no|yes) ;; #( - *) +struct incomplete_array +{ + int datasize; + double data[]; +}; -cat >>confdefs.h <<\_ACEOF -#define _UINT32_T 1 -_ACEOF +struct named_init { + int number; + const wchar_t *name; + double average; +}; +typedef const char *ccp; -cat >>confdefs.h <<_ACEOF -#define uint32_t $ac_cv_c_uint32_t -_ACEOF -;; - esac +static inline int +test_restrict (ccp restrict text) +{ + // See if C++-style comments work. + // Iterate through items via the restricted pointer. + // Also check for declarations in for loops. + for (unsigned int i = 0; *(text+i) != '\''\0'\''; ++i) + continue; + return 0; +} +// Check varargs and va_copy. +static bool +test_varargs (const char *format, ...) +{ + va_list args; + va_start (args, format); + va_list args_copy; + va_copy (args_copy, args); -# Check for ANSI C headers. -{ echo "$as_me:$LINENO: checking for ANSI C header files" >&5 -echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6; } -if test "${ac_cv_header_stdc+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include -#include -#include -#include + const char *str = ""; + int number = 0; + float fnumber = 0; -int -main () -{ + while (*format) + { + switch (*format++) + { + case '\''s'\'': // string + str = va_arg (args_copy, const char *); + break; + case '\''d'\'': // int + number = va_arg (args_copy, int); + break; + case '\''f'\'': // float + fnumber = va_arg (args_copy, double); + break; + default: + break; + } + } + va_end (args_copy); + va_end (args); - ; - return 0; + return *str && number && fnumber; } -_ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then - ac_cv_header_stdc=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 +' - ac_cv_header_stdc=no -fi +# Test code for whether the C compiler supports C99 (body of main). +ac_c_conftest_c99_main=' + // Check bool. + _Bool success = false; + success |= (argc != 0); -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + // Check restrict. + if (test_restrict ("String literal") == 0) + success = true; + char *restrict newvar = "Another string"; -if test $ac_cv_header_stdc = yes; then - # SunOS 4.x string.h does not declare mem*, contrary to ANSI. - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include + // Check varargs. + success &= test_varargs ("s, d'\'' f .", "string", 65, 34.234); + test_varargs_macros (); -_ACEOF -if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | - $EGREP "memchr" >/dev/null 2>&1; then - : -else - ac_cv_header_stdc=no -fi -rm -f -r conftest* + // Check flexible array members. + struct incomplete_array *ia = + malloc (sizeof (struct incomplete_array) + (sizeof (double) * 10)); + ia->datasize = 10; + for (int i = 0; i < ia->datasize; ++i) + ia->data[i] = i * 1.234; -fi + // Check named initializers. + struct named_init ni = { + .number = 34, + .name = L"Test wide string", + .average = 543.34343, + }; -if test $ac_cv_header_stdc = yes; then - # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include + ni.number = 58; -_ACEOF -if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | - $EGREP "free" >/dev/null 2>&1; then - : -else - ac_cv_header_stdc=no -fi -rm -f -r conftest* + int dynamic_array[ni.number]; + dynamic_array[0] = argv[0][0]; + dynamic_array[ni.number - 1] = 543; -fi + // work around unused variable warnings + ok |= (!success || bignum == 0LL || ubignum == 0uLL || newvar[0] == '\''x'\'' + || dynamic_array[ni.number - 1] != 543); +' -if test $ac_cv_header_stdc = yes; then - # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. - if test "$cross_compiling" = yes; then - : -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include -#include -#if ((' ' & 0x0FF) == 0x020) -# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') -# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) -#else -# define ISLOWER(c) \ - (('a' <= (c) && (c) <= 'i') \ - || ('j' <= (c) && (c) <= 'r') \ - || ('s' <= (c) && (c) <= 'z')) -# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +# Test code for whether the C compiler supports C11 (global declarations) +ac_c_conftest_c11_globals=' +// Does the compiler advertise C11 conformance? +#if !defined __STDC_VERSION__ || __STDC_VERSION__ < 201112L +# error "Compiler does not advertise C11 conformance" #endif -#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) -int -main () -{ - int i; - for (i = 0; i < 256; i++) - if (XOR (islower (i), ISLOWER (i)) - || toupper (i) != TOUPPER (i)) - return 2; - return 0; -} -_ACEOF -rm -f conftest$ac_exeext -if { (ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_link") 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { ac_try='./conftest$ac_exeext' - { (case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_try") 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - : -else - echo "$as_me: program exited with status $ac_status" >&5 -echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 +// Check _Alignas. +char _Alignas (double) aligned_as_double; +char _Alignas (0) no_special_alignment; +extern char aligned_as_int; +char _Alignas (0) _Alignas (int) aligned_as_int; -( exit $ac_status ) -ac_cv_header_stdc=no -fi -rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext -fi +// Check _Alignof. +enum +{ + int_alignment = _Alignof (int), + int_array_alignment = _Alignof (int[100]), + char_alignment = _Alignof (char) +}; +_Static_assert (0 < -_Alignof (int), "_Alignof is signed"); +// Check _Noreturn. +int _Noreturn does_not_return (void) { for (;;) continue; } -fi -fi -{ echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5 -echo "${ECHO_T}$ac_cv_header_stdc" >&6; } -if test $ac_cv_header_stdc = yes; then +// Check _Static_assert. +struct test_static_assert +{ + int x; + _Static_assert (sizeof (int) <= sizeof (long int), + "_Static_assert does not work in struct"); + long int y; +}; -cat >>confdefs.h <<\_ACEOF -#define STDC_HEADERS 1 -_ACEOF +// Check UTF-8 literals. +#define u8 syntax error! +char const utf8_literal[] = u8"happens to be ASCII" "another string"; -fi +// Check duplicate typedefs. +typedef long *long_ptr; +typedef long int *long_ptr; +typedef long_ptr long_ptr; +// Anonymous structures and unions -- taken from C11 6.7.2.1 Example 1. +struct anonymous +{ + union { + struct { int i; int j; }; + struct { int k; long int l; } w; + }; + int m; +} v1; +' +# Test code for whether the C compiler supports C11 (body of main). +ac_c_conftest_c11_main=' + _Static_assert ((offsetof (struct anonymous, i) + == offsetof (struct anonymous, w.k)), + "Anonymous union alignment botch"); + v1.i = 2; + v1.w.k = 5; + ok |= v1.i != 5; +' +# Test code for whether the C compiler supports C11 (complete). +ac_c_conftest_c11_program="${ac_c_conftest_c89_globals} +${ac_c_conftest_c99_globals} +${ac_c_conftest_c11_globals} +int +main (int argc, char **argv) +{ + int ok = 0; + ${ac_c_conftest_c89_main} + ${ac_c_conftest_c99_main} + ${ac_c_conftest_c11_main} + return ok; +} +" +# Test code for whether the C compiler supports C99 (complete). +ac_c_conftest_c99_program="${ac_c_conftest_c89_globals} +${ac_c_conftest_c99_globals} +int +main (int argc, char **argv) +{ + int ok = 0; + ${ac_c_conftest_c89_main} + ${ac_c_conftest_c99_main} + return ok; +} +" +# Test code for whether the C compiler supports C89 (complete). +ac_c_conftest_c89_program="${ac_c_conftest_c89_globals} +int +main (int argc, char **argv) +{ + int ok = 0; + ${ac_c_conftest_c89_main} + return ok; +} +" +as_fn_append ac_header_c_list " stdio.h stdio_h HAVE_STDIO_H" +as_fn_append ac_header_c_list " stdlib.h stdlib_h HAVE_STDLIB_H" +as_fn_append ac_header_c_list " string.h string_h HAVE_STRING_H" +as_fn_append ac_header_c_list " inttypes.h inttypes_h HAVE_INTTYPES_H" +as_fn_append ac_header_c_list " stdint.h stdint_h HAVE_STDINT_H" +as_fn_append ac_header_c_list " strings.h strings_h HAVE_STRINGS_H" +as_fn_append ac_header_c_list " sys/stat.h sys_stat_h HAVE_SYS_STAT_H" +as_fn_append ac_header_c_list " sys/types.h sys_types_h HAVE_SYS_TYPES_H" +as_fn_append ac_header_c_list " unistd.h unistd_h HAVE_UNISTD_H" + +# Auxiliary files required by this configure script. +ac_aux_files="install-sh config.guess config.sub" + +# Locations in which to look for auxiliary files. +ac_aux_dir_candidates="${srcdir}/config" + +# Search for a directory containing all of the required auxiliary files, +# $ac_aux_files, from the $PATH-style list $ac_aux_dir_candidates. +# If we don't find one directory that contains all the files we need, +# we report the set of missing files from the *first* directory in +# $ac_aux_dir_candidates and give up. +ac_missing_aux_files="" +ac_first_candidate=: +printf "%s\n" "$as_me:${as_lineno-$LINENO}: looking for aux files: $ac_aux_files" >&5 +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +as_found=false +for as_dir in $ac_aux_dir_candidates +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + as_found=: + printf "%s\n" "$as_me:${as_lineno-$LINENO}: trying $as_dir" >&5 + ac_aux_dir_found=yes + ac_install_sh= + for ac_aux in $ac_aux_files + do + # As a special case, if "install-sh" is required, that requirement + # can be satisfied by any of "install-sh", "install.sh", or "shtool", + # and $ac_install_sh is set appropriately for whichever one is found. + if test x"$ac_aux" = x"install-sh" + then + if test -f "${as_dir}install-sh"; then + printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}install-sh found" >&5 + ac_install_sh="${as_dir}install-sh -c" + elif test -f "${as_dir}install.sh"; then + printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}install.sh found" >&5 + ac_install_sh="${as_dir}install.sh -c" + elif test -f "${as_dir}shtool"; then + printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}shtool found" >&5 + ac_install_sh="${as_dir}shtool install -c" + else + ac_aux_dir_found=no + if $ac_first_candidate; then + ac_missing_aux_files="${ac_missing_aux_files} install-sh" + else + break + fi + fi + else + if test -f "${as_dir}${ac_aux}"; then + printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}${ac_aux} found" >&5 + else + ac_aux_dir_found=no + if $ac_first_candidate; then + ac_missing_aux_files="${ac_missing_aux_files} ${ac_aux}" + else + break + fi + fi + fi + done + if test "$ac_aux_dir_found" = yes; then + ac_aux_dir="$as_dir" + break + fi + ac_first_candidate=false -for ac_header in ctype.h errno.h limits.h locale.h math.h setjmp.h stdarg.h \ - stdio.h stdlib.h string.h -do -as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` -if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then - { echo "$as_me:$LINENO: checking for $ac_header" >&5 -echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; } -if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -fi -ac_res=`eval echo '${'$as_ac_Header'}'` - { echo "$as_me:$LINENO: result: $ac_res" >&5 -echo "${ECHO_T}$ac_res" >&6; } -else - # Is the header compilable? -{ echo "$as_me:$LINENO: checking $ac_header usability" >&5 -echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6; } -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -$ac_includes_default -#include <$ac_header> -_ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then - ac_header_compiler=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 + as_found=false +done +IFS=$as_save_IFS +if $as_found +then : - ac_header_compiler=no +else $as_nop + as_fn_error $? "cannot find required auxiliary files:$ac_missing_aux_files" "$LINENO" 5 fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 -echo "${ECHO_T}$ac_header_compiler" >&6; } -# Is the header present? -{ echo "$as_me:$LINENO: checking $ac_header presence" >&5 -echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6; } -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include <$ac_header> -_ACEOF -if { (ac_try="$ac_cpp conftest.$ac_ext" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } >/dev/null && { - test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || - test ! -s conftest.err - }; then - ac_header_preproc=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 +# These three variables are undocumented and unsupported, +# and are intended to be withdrawn in a future Autoconf release. +# They can cause serious problems if a builder's source tree is in a directory +# whose full name contains unusual characters. +if test -f "${ac_aux_dir}config.guess"; then + ac_config_guess="$SHELL ${ac_aux_dir}config.guess" +fi +if test -f "${ac_aux_dir}config.sub"; then + ac_config_sub="$SHELL ${ac_aux_dir}config.sub" +fi +if test -f "$ac_aux_dir/configure"; then + ac_configure="$SHELL ${ac_aux_dir}configure" +fi - ac_header_preproc=no +# Check that the precious variables saved in the cache have kept the same +# value. +ac_cache_corrupted=false +for ac_var in $ac_precious_vars; do + eval ac_old_set=\$ac_cv_env_${ac_var}_set + eval ac_new_set=\$ac_env_${ac_var}_set + eval ac_old_val=\$ac_cv_env_${ac_var}_value + eval ac_new_val=\$ac_env_${ac_var}_value + case $ac_old_set,$ac_new_set in + set,) + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +printf "%s\n" "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 +printf "%s\n" "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,);; + *) + if test "x$ac_old_val" != "x$ac_new_val"; then + # differences in whitespace do not lead to failure. + ac_old_val_w=`echo x $ac_old_val` + ac_new_val_w=`echo x $ac_new_val` + if test "$ac_old_val_w" != "$ac_new_val_w"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 +printf "%s\n" "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + ac_cache_corrupted=: + else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 +printf "%s\n" "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} + eval $ac_var=\$ac_old_val + fi + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 +printf "%s\n" "$as_me: former value: \`$ac_old_val'" >&2;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 +printf "%s\n" "$as_me: current value: \`$ac_new_val'" >&2;} + fi;; + esac + # Pass precious variables to config.status. + if test "$ac_new_set" = set; then + case $ac_new_val in + *\'*) ac_arg=$ac_var=`printf "%s\n" "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; + *) ac_arg=$ac_var=$ac_new_val ;; + esac + case " $ac_configure_args " in + *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. + *) as_fn_append ac_configure_args " '$ac_arg'" ;; + esac + fi +done +if $ac_cache_corrupted; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 +printf "%s\n" "$as_me: error: changes in the environment can compromise the build" >&2;} + as_fn_error $? "run \`${MAKE-make} distclean' and/or \`rm $cache_file' + and start over" "$LINENO" 5 fi +## -------------------- ## +## Main body of script. ## +## -------------------- ## -rm -f conftest.err conftest.$ac_ext -{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 -echo "${ECHO_T}$ac_header_preproc" >&6; } +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu -# So? What about this header? -case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in - yes:no: ) - { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 -echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 -echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} - ac_header_preproc=yes - ;; - no:yes:* ) - { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 -echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 -echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 -echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 -echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 -echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 -echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} - ( cat <<\_ASBOX -## ------------------------------------ ## -## Report this to mark@calabretta.id.au ## -## ------------------------------------ ## -_ASBOX - ) | sed "s/^/$as_me: WARNING: /" >&2 - ;; -esac -{ echo "$as_me:$LINENO: checking for $ac_header" >&5 -echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; } -if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - eval "$as_ac_Header=\$ac_header_preproc" -fi -ac_res=`eval echo '${'$as_ac_Header'}'` - { echo "$as_me:$LINENO: result: $ac_res" >&5 -echo "${ECHO_T}$ac_res" >&6; } -fi -if test `eval echo '${'$as_ac_Header'}'` = yes; then - cat >>confdefs.h <<_ACEOF -#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 -_ACEOF -fi -done -if test "x$ac_cv_header_stdc" = xno; then - { { echo "$as_me:$LINENO: error: - ------------------------------------------------------------------- - An ANSI standard C library is required to build WCSLIB. One of the - ANSI C header files it requires is missing or unusable. - ERROR: WCSLIB configuration failure. - -------------------------------------------------------------------" >&5 -echo "$as_me: error: - ------------------------------------------------------------------- - An ANSI standard C library is required to build WCSLIB. One of the - ANSI C header files it requires is missing or unusable. +printf "%s\n" "#define WCSLIB_VERSION $PACKAGE_VERSION" >>confdefs.h - ERROR: WCSLIB configuration failure. - -------------------------------------------------------------------" >&2;} - { (exit 1); exit 1; }; } -fi -# Checks for ANSI C library functions (suggested by autoscan - fseeko and -# stat are only required by fitshdr). +# Library version number, same as package version. +LIBVER="$PACKAGE_VERSION" + -{ echo "$as_me:$LINENO: checking for floor in -lm" >&5 -echo $ECHO_N "checking for floor in -lm... $ECHO_C" >&6; } -if test "${ac_cv_lib_m_floor+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-lm $LIBS" -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char floor (); -int -main () -{ -return floor (); - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_link") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest$ac_exeext && - $as_test_x conftest$ac_exeext; then - ac_cv_lib_m_floor=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - ac_cv_lib_m_floor=no -fi -rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -{ echo "$as_me:$LINENO: result: $ac_cv_lib_m_floor" >&5 -echo "${ECHO_T}$ac_cv_lib_m_floor" >&6; } -if test $ac_cv_lib_m_floor = yes; then - cat >>confdefs.h <<_ACEOF -#define HAVE_LIBM 1 -_ACEOF +# Get the system type. - LIBS="-lm $LIBS" -fi -{ echo "$as_me:$LINENO: checking for _LARGEFILE_SOURCE value needed for large files" >&5 -echo $ECHO_N "checking for _LARGEFILE_SOURCE value needed for large files... $ECHO_C" >&6; } -if test "${ac_cv_sys_largefile_source+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - while :; do - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include -int -main () -{ -return fseeko (stdin, 0, 0) && (fseeko) (stdin, 0, 0); - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_link") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest$ac_exeext && - $as_test_x conftest$ac_exeext; then - ac_cv_sys_largefile_source=no; break -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 + # Make sure we can run config.sub. +$SHELL "${ac_aux_dir}config.sub" sun4 >/dev/null 2>&1 || + as_fn_error $? "cannot run $SHELL ${ac_aux_dir}config.sub" "$LINENO" 5 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking build system type" >&5 +printf %s "checking build system type... " >&6; } +if test ${ac_cv_build+y} +then : + printf %s "(cached) " >&6 +else $as_nop + ac_build_alias=$build_alias +test "x$ac_build_alias" = x && + ac_build_alias=`$SHELL "${ac_aux_dir}config.guess"` +test "x$ac_build_alias" = x && + as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5 +ac_cv_build=`$SHELL "${ac_aux_dir}config.sub" $ac_build_alias` || + as_fn_error $? "$SHELL ${ac_aux_dir}config.sub $ac_build_alias failed" "$LINENO" 5 fi - -rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ - conftest$ac_exeext conftest.$ac_ext - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#define _LARGEFILE_SOURCE 1 -#include -int -main () -{ -return fseeko (stdin, 0, 0) && (fseeko) (stdin, 0, 0); - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5 +printf "%s\n" "$ac_cv_build" >&6; } +case $ac_cv_build in +*-*-*) ;; +*) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;; esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_link") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest$ac_exeext && - $as_test_x conftest$ac_exeext; then - ac_cv_sys_largefile_source=1; break -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 +build=$ac_cv_build +ac_save_IFS=$IFS; IFS='-' +set x $ac_cv_build +shift +build_cpu=$1 +build_vendor=$2 +shift; shift +# Remember, the first character of IFS is used to create $*, +# except with old shells: +build_os=$* +IFS=$ac_save_IFS +case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking host system type" >&5 +printf %s "checking host system type... " >&6; } +if test ${ac_cv_host+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test "x$host_alias" = x; then + ac_cv_host=$ac_cv_build +else + ac_cv_host=`$SHELL "${ac_aux_dir}config.sub" $host_alias` || + as_fn_error $? "$SHELL ${ac_aux_dir}config.sub $host_alias failed" "$LINENO" 5 fi -rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ - conftest$ac_exeext conftest.$ac_ext - ac_cv_sys_largefile_source=unknown - break -done fi -{ echo "$as_me:$LINENO: result: $ac_cv_sys_largefile_source" >&5 -echo "${ECHO_T}$ac_cv_sys_largefile_source" >&6; } -case $ac_cv_sys_largefile_source in #( - no | unknown) ;; - *) -cat >>confdefs.h <<_ACEOF -#define _LARGEFILE_SOURCE $ac_cv_sys_largefile_source -_ACEOF -;; +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5 +printf "%s\n" "$ac_cv_host" >&6; } +case $ac_cv_host in +*-*-*) ;; +*) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;; esac -rm -f -r conftest* +host=$ac_cv_host +ac_save_IFS=$IFS; IFS='-' +set x $ac_cv_host +shift +host_cpu=$1 +host_vendor=$2 +shift; shift +# Remember, the first character of IFS is used to create $*, +# except with old shells: +host_os=$* +IFS=$ac_save_IFS +case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac -# We used to try defining _XOPEN_SOURCE=500 too, to work around a bug -# in glibc 2.1.3, but that breaks too many other things. -# If you want fseeko and ftello with glibc, upgrade to a fixed glibc. -if test $ac_cv_sys_largefile_source != unknown; then -cat >>confdefs.h <<\_ACEOF -#define HAVE_FSEEKO 1 -_ACEOF +ARCH="${host_cpu}-$host_os" + + +# Look for Flex. +# Check whether --enable-flex was given. +if test ${enable_flex+y} +then : + enableval=$enable_flex; fi +if test "x$enable_flex" = xno ; then + FLEX= + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: Generation of flex sources disabled by request, using + pre-generated sources." >&5 +printf "%s\n" "$as_me: WARNING: Generation of flex sources disabled by request, using + pre-generated sources." >&2;} -for ac_header in stdlib.h -do -as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` -if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then - { echo "$as_me:$LINENO: checking for $ac_header" >&5 -echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; } -if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -fi -ac_res=`eval echo '${'$as_ac_Header'}'` - { echo "$as_me:$LINENO: result: $ac_res" >&5 -echo "${ECHO_T}$ac_res" >&6; } else - # Is the header compilable? -{ echo "$as_me:$LINENO: checking $ac_header usability" >&5 -echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6; } -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -$ac_includes_default -#include <$ac_header> -_ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then - ac_header_compiler=yes + # Extract the first word of "flex", so it can be a program name with args. +set dummy flex; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_FLEX+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$FLEX"; then + ac_cv_prog_FLEX="$FLEX" # Let the user override the test. else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_FLEX="flex" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS - ac_header_compiler=no +fi +fi +FLEX=$ac_cv_prog_FLEX +if test -n "$FLEX"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $FLEX" >&5 +printf "%s\n" "$FLEX" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 -echo "${ECHO_T}$ac_header_compiler" >&6; } -# Is the header present? -{ echo "$as_me:$LINENO: checking $ac_header presence" >&5 -echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6; } -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include <$ac_header> -_ACEOF -if { (ac_try="$ac_cpp conftest.$ac_ext" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } >/dev/null && { - test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || - test ! -s conftest.err - }; then - ac_header_preproc=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 + if test "x$FLEX" = xflex ; then + # Version 2.6.0 or later is required. + V=`flex --version | awk '{print $2}'` + W=`echo $V | awk -F. '{if ((($1*100 + $2)*100 + $3) < 20600) print "no"}'` + if test "x$W" != x ; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: Flex version $V is too old, ignored." >&5 +printf "%s\n" "$as_me: WARNING: Flex version $V is too old, ignored." >&2;} + FLEX= + else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Using Flex version $V." >&5 +printf "%s\n" "$as_me: Using Flex version $V." >&6;} + fi + fi - ac_header_preproc=no + if test "x$FLEX" = x ; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: Flex version 2.6.0 or later does not appear to be + available, will use pre-generated sources." >&5 +printf "%s\n" "$as_me: WARNING: Flex version 2.6.0 or later does not appear to be + available, will use pre-generated sources." >&2;} + fi fi -rm -f conftest.err conftest.$ac_ext -{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 -echo "${ECHO_T}$ac_header_preproc" >&6; } -# So? What about this header? -case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in - yes:no: ) - { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 -echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 -echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} - ac_header_preproc=yes - ;; - no:yes:* ) - { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 -echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 -echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 -echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 -echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 -echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 -echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} - ( cat <<\_ASBOX -## ------------------------------------ ## -## Report this to mark@calabretta.id.au ## -## ------------------------------------ ## -_ASBOX - ) | sed "s/^/$as_me: WARNING: /" >&2 - ;; -esac -{ echo "$as_me:$LINENO: checking for $ac_header" >&5 -echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; } -if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - eval "$as_ac_Header=\$ac_header_preproc" -fi -ac_res=`eval echo '${'$as_ac_Header'}'` - { echo "$as_me:$LINENO: result: $ac_res" >&5 -echo "${ECHO_T}$ac_res" >&6; } +# Look for an ANSI C compiler. -fi -if test `eval echo '${'$as_ac_Header'}'` = yes; then - cat >>confdefs.h <<_ACEOF -#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 -_ACEOF -fi -done -{ echo "$as_me:$LINENO: checking for GNU libc compatible malloc" >&5 -echo $ECHO_N "checking for GNU libc compatible malloc... $ECHO_C" >&6; } -if test "${ac_cv_func_malloc_0_nonnull+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test "$cross_compiling" = yes; then - ac_cv_func_malloc_0_nonnull=no -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#if defined STDC_HEADERS || defined HAVE_STDLIB_H -# include -#else -char *malloc (); -#endif -int -main () -{ -return ! malloc (0); - ; - return 0; -} -_ACEOF -rm -f conftest$ac_exeext -if { (ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_link") 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { ac_try='./conftest$ac_exeext' - { (case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_try") 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_cv_func_malloc_0_nonnull=yes + + + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_CC+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. else - echo "$as_me: program exited with status $ac_status" >&5 -echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS -( exit $ac_status ) -ac_cv_func_malloc_0_nonnull=no fi -rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +printf "%s\n" "$CC" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } fi fi -{ echo "$as_me:$LINENO: result: $ac_cv_func_malloc_0_nonnull" >&5 -echo "${ECHO_T}$ac_cv_func_malloc_0_nonnull" >&6; } -if test $ac_cv_func_malloc_0_nonnull = yes; then - -cat >>confdefs.h <<\_ACEOF -#define HAVE_MALLOC 1 -_ACEOF - +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_ac_ct_CC+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="gcc" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +printf "%s\n" "$ac_ct_CC" >&6; } else - cat >>confdefs.h <<\_ACEOF -#define HAVE_MALLOC 0 -_ACEOF - - case " $LIBOBJS " in - *" malloc.$ac_objext "* ) ;; - *) LIBOBJS="$LIBOBJS malloc.$ac_objext" - ;; -esac - - -cat >>confdefs.h <<\_ACEOF -#define malloc rpl_malloc -_ACEOF - + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } fi - - - -for ac_header in stdlib.h -do -as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` -if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then - { echo "$as_me:$LINENO: checking for $ac_header" >&5 -echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; } -if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -fi -ac_res=`eval echo '${'$as_ac_Header'}'` - { echo "$as_me:$LINENO: result: $ac_res" >&5 -echo "${ECHO_T}$ac_res" >&6; } -else - # Is the header compilable? -{ echo "$as_me:$LINENO: checking $ac_header usability" >&5 -echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6; } -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -$ac_includes_default -#include <$ac_header> -_ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then - ac_header_compiler=yes + CC=$ac_ct_CC + fi else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_header_compiler=no + CC="$ac_cv_prog_CC" fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 -echo "${ECHO_T}$ac_header_compiler" >&6; } - -# Is the header present? -{ echo "$as_me:$LINENO: checking $ac_header presence" >&5 -echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6; } -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include <$ac_header> -_ACEOF -if { (ac_try="$ac_cpp conftest.$ac_ext" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } >/dev/null && { - test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || - test ! -s conftest.err - }; then - ac_header_preproc=yes +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_CC+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS - ac_header_preproc=no fi - -rm -f conftest.err conftest.$ac_ext -{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 -echo "${ECHO_T}$ac_header_preproc" >&6; } - -# So? What about this header? -case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in - yes:no: ) - { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 -echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 -echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} - ac_header_preproc=yes - ;; - no:yes:* ) - { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 -echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 -echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 -echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 -echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 -echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 -echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} - ( cat <<\_ASBOX -## ------------------------------------ ## -## Report this to mark@calabretta.id.au ## -## ------------------------------------ ## -_ASBOX - ) | sed "s/^/$as_me: WARNING: /" >&2 - ;; -esac -{ echo "$as_me:$LINENO: checking for $ac_header" >&5 -echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; } -if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then - echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +printf "%s\n" "$CC" >&6; } else - eval "$as_ac_Header=\$ac_header_preproc" + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } fi -ac_res=`eval echo '${'$as_ac_Header'}'` - { echo "$as_me:$LINENO: result: $ac_res" >&5 -echo "${ECHO_T}$ac_res" >&6; } -fi -if test `eval echo '${'$as_ac_Header'}'` = yes; then - cat >>confdefs.h <<_ACEOF -#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 -_ACEOF + fi fi - -done - -{ echo "$as_me:$LINENO: checking for GNU libc compatible realloc" >&5 -echo $ECHO_N "checking for GNU libc compatible realloc... $ECHO_C" >&6; } -if test "${ac_cv_func_realloc_0_nonnull+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test "$cross_compiling" = yes; then - ac_cv_func_realloc_0_nonnull=no -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#if defined STDC_HEADERS || defined HAVE_STDLIB_H -# include -#else -char *realloc (); -#endif - -int -main () -{ -return ! realloc (0, 0); - ; - return 0; -} -_ACEOF -rm -f conftest$ac_exeext -if { (ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_link") 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { ac_try='./conftest$ac_exeext' - { (case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_try") 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_cv_func_realloc_0_nonnull=yes +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_CC+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. else - echo "$as_me: program exited with status $ac_status" >&5 -echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + if test "$as_dir$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS -( exit $ac_status ) -ac_cv_func_realloc_0_nonnull=no +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir$ac_word${1+' '}$@" + fi fi -rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext fi - - fi -{ echo "$as_me:$LINENO: result: $ac_cv_func_realloc_0_nonnull" >&5 -echo "${ECHO_T}$ac_cv_func_realloc_0_nonnull" >&6; } -if test $ac_cv_func_realloc_0_nonnull = yes; then - -cat >>confdefs.h <<\_ACEOF -#define HAVE_REALLOC 1 -_ACEOF - +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +printf "%s\n" "$CC" >&6; } else - cat >>confdefs.h <<\_ACEOF -#define HAVE_REALLOC 0 -_ACEOF - - case " $LIBOBJS " in - *" realloc.$ac_objext "* ) ;; - *) LIBOBJS="$LIBOBJS realloc.$ac_objext" - ;; -esac - - -cat >>confdefs.h <<\_ACEOF -#define realloc rpl_realloc -_ACEOF - + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } fi +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl.exe + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_CC+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS -{ echo "$as_me:$LINENO: checking for function prototypes" >&5 -echo $ECHO_N "checking for function prototypes... $ECHO_C" >&6; } -if test "$ac_cv_prog_cc_c89" != no; then - { echo "$as_me:$LINENO: result: yes" >&5 -echo "${ECHO_T}yes" >&6; } - -cat >>confdefs.h <<\_ACEOF -#define PROTOTYPES 1 -_ACEOF - - -cat >>confdefs.h <<\_ACEOF -#define __PROTOTYPES 1 -_ACEOF - +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +printf "%s\n" "$CC" >&6; } else - { echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } fi -{ echo "$as_me:$LINENO: checking whether setvbuf arguments are reversed" >&5 -echo $ECHO_N "checking whether setvbuf arguments are reversed... $ECHO_C" >&6; } -if test "${ac_cv_func_setvbuf_reversed+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - ac_cv_func_setvbuf_reversed=no - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include -# ifdef PROTOTYPES - int (setvbuf) (FILE *, int, char *, size_t); -# endif -int -main () -{ -char buf; return setvbuf (stdout, _IOLBF, &buf, 1); - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_link") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest$ac_exeext && - $as_test_x conftest$ac_exeext; then - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include -# ifdef PROTOTYPES - int (setvbuf) (FILE *, int, char *, size_t); -# endif -int -main () -{ -char buf; return setvbuf (stdout, &buf, _IOLBF, 1); - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_link") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest$ac_exeext && - $as_test_x conftest$ac_exeext; then - # It compiles and links either way, so it must not be declared - # with a prototype and most likely this is a K&R C compiler. - # Try running it. - if test "$cross_compiling" = yes; then - : # Assume setvbuf is not reversed when cross-compiling. -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -$ac_includes_default -int -main () -{ -/* This call has the arguments reversed. - A reversed system may check and see that the address of buf - is not _IOLBF, _IONBF, or _IOFBF, and return nonzero. */ - char buf; - if (setvbuf (stdout, _IOLBF, &buf, 1) != 0) - return 1; - putchar ('\r'); - return 0; /* Non-reversed systems SEGV here. */ - ; - return 0; -} -_ACEOF -rm -f conftest$ac_exeext -if { (ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_link") 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { ac_try='./conftest$ac_exeext' - { (case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_try") 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_cv_func_setvbuf_reversed=yes + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl.exe +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_ac_ct_CC+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else - echo "$as_me: program exited with status $ac_status" >&5 -echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="$ac_prog" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS fi -rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext fi - - - ac_cv_func_setvbuf_reversed=yes +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +printf "%s\n" "$ac_ct_CC" >&6; } else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + test -n "$ac_ct_CC" && break +done + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi fi -rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ - conftest$ac_exeext conftest.$ac_ext +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}clang", so it can be a program name with args. +set dummy ${ac_tool_prefix}clang; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_CC+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}clang" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS fi - -rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ - conftest$ac_exeext conftest.$ac_ext fi -{ echo "$as_me:$LINENO: result: $ac_cv_func_setvbuf_reversed" >&5 -echo "${ECHO_T}$ac_cv_func_setvbuf_reversed" >&6; } -if test $ac_cv_func_setvbuf_reversed = yes; then +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +printf "%s\n" "$CC" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi -cat >>confdefs.h <<\_ACEOF -#define SETVBUF_REVERSED 1 -_ACEOF fi - -{ echo "$as_me:$LINENO: checking whether lstat dereferences a symlink specified with a trailing slash" >&5 -echo $ECHO_N "checking whether lstat dereferences a symlink specified with a trailing slash... $ECHO_C" >&6; } -if test "${ac_cv_func_lstat_dereferences_slashed_symlink+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - rm -f conftest.sym conftest.file -echo >conftest.file -if test "$as_ln_s" = "ln -s" && ln -s conftest.file conftest.sym; then - if test "$cross_compiling" = yes; then - ac_cv_func_lstat_dereferences_slashed_symlink=no -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -$ac_includes_default -int -main () -{ -struct stat sbuf; - /* Linux will dereference the symlink and fail. - That is better in the sense that it means we will not - have to compile and use the lstat wrapper. */ - return lstat ("conftest.sym/", &sbuf) == 0; - ; - return 0; -} -_ACEOF -rm -f conftest$ac_exeext -if { (ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_link") 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { ac_try='./conftest$ac_exeext' - { (case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_try") 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_cv_func_lstat_dereferences_slashed_symlink=yes +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "clang", so it can be a program name with args. +set dummy clang; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_ac_ct_CC+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else - echo "$as_me: program exited with status $ac_status" >&5 -echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="clang" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS -( exit $ac_status ) -ac_cv_func_lstat_dereferences_slashed_symlink=no fi -rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext fi - - +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +printf "%s\n" "$ac_ct_CC" >&6; } else - # If the `ln -s' command failed, then we probably don't even - # have an lstat function. - ac_cv_func_lstat_dereferences_slashed_symlink=no + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } fi -rm -f conftest.sym conftest.file + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +else + CC="$ac_cv_prog_CC" fi -{ echo "$as_me:$LINENO: result: $ac_cv_func_lstat_dereferences_slashed_symlink" >&5 -echo "${ECHO_T}$ac_cv_func_lstat_dereferences_slashed_symlink" >&6; } -test $ac_cv_func_lstat_dereferences_slashed_symlink = yes && +fi -cat >>confdefs.h <<_ACEOF -#define LSTAT_FOLLOWS_SLASHED_SYMLINK 1 -_ACEOF +test -z "$CC" && { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "no acceptable C compiler found in \$PATH +See \`config.log' for more details" "$LINENO" 5; } -if test $ac_cv_func_lstat_dereferences_slashed_symlink = no; then - case " $LIBOBJS " in - *" lstat.$ac_objext "* ) ;; - *) LIBOBJS="$LIBOBJS lstat.$ac_objext" - ;; +# Provide some information about the compiler. +printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +for ac_option in --version -v -V -qversion -version; do + { { ac_try="$ac_compiler $ac_option >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +printf "%s\n" "$ac_try_echo"; } >&5 + (eval "$ac_compiler $ac_option >&5") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + sed '10a\ +... rest of stderr output deleted ... + 10q' conftest.err >conftest.er1 + cat conftest.er1 >&5 + fi + rm -f conftest.er1 conftest.err + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +done -fi - -{ echo "$as_me:$LINENO: checking whether stat accepts an empty string" >&5 -echo $ECHO_N "checking whether stat accepts an empty string... $ECHO_C" >&6; } -if test "${ac_cv_func_stat_empty_string_bug+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test "$cross_compiling" = yes; then - ac_cv_func_stat_empty_string_bug=yes -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF +cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -$ac_includes_default + int -main () +main (void) { -struct stat sbuf; - return stat ("", &sbuf) == 0; + ; return 0; } _ACEOF -rm -f conftest$ac_exeext -if { (ac_try="$ac_link" +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" +# Try to create an executable without -o first, disregard a.out. +# It will help us diagnose broken compilers, and finding out an intuition +# of exeext. +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 +printf %s "checking whether the C compiler works... " >&6; } +ac_link_default=`printf "%s\n" "$ac_link" | sed 's/ -o *conftest[^ ]*//'` + +# The possible output files: +ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" + +ac_rmfiles= +for ac_file in $ac_files +do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + * ) ac_rmfiles="$ac_rmfiles $ac_file";; + esac +done +rm -f $ac_rmfiles + +if { { ac_try="$ac_link_default" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_link") 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { ac_try='./conftest$ac_exeext' - { (case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_try") 2>&5 +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +printf "%s\n" "$ac_try_echo"; } >&5 + (eval "$ac_link_default") 2>&5 ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_cv_func_stat_empty_string_bug=no -else - echo "$as_me: program exited with status $ac_status" >&5 -echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +then : + # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. +# So ignore a value of `no', otherwise this would lead to `EXEEXT = no' +# in a Makefile. We should not override ac_cv_exeext if it was cached, +# so that the user can short-circuit this test for compilers unknown to +# Autoconf. +for ac_file in $ac_files '' +do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) + ;; + [ab].out ) + # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) + if test ${ac_cv_exeext+y} && test "$ac_cv_exeext" != no; + then :; else + ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + fi + # We set ac_cv_exeext here because the later test for it is not + # safe: cross compilers may not add the suffix if given an `-o' + # argument, so we may need to know it at that point already. + # Even if this section looks crufty: it has the advantage of + # actually working. + break;; + * ) + break;; + esac +done +test "$ac_cv_exeext" = no && ac_cv_exeext= -( exit $ac_status ) -ac_cv_func_stat_empty_string_bug=yes -fi -rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +else $as_nop + ac_file='' fi +if test -z "$ac_file" +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +printf "%s\n" "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 +{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error 77 "C compiler cannot create executables +See \`config.log' for more details" "$LINENO" 5; } +else $as_nop + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +printf "%s\n" "yes" >&6; } +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 +printf %s "checking for C compiler default output file name... " >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 +printf "%s\n" "$ac_file" >&6; } +ac_exeext=$ac_cv_exeext -fi -{ echo "$as_me:$LINENO: result: $ac_cv_func_stat_empty_string_bug" >&5 -echo "${ECHO_T}$ac_cv_func_stat_empty_string_bug" >&6; } -if test $ac_cv_func_stat_empty_string_bug = yes; then - case " $LIBOBJS " in - *" stat.$ac_objext "* ) ;; - *) LIBOBJS="$LIBOBJS stat.$ac_objext" - ;; +rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out +ac_clean_files=$ac_clean_files_save +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 +printf %s "checking for suffix of executables... " >&6; } +if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; esac - - -cat >>confdefs.h <<_ACEOF -#define HAVE_STAT_EMPTY_STRING_BUG 1 -_ACEOF - +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +printf "%s\n" "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +then : + # If both `conftest.exe' and `conftest' are `present' (well, observable) +# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will +# work properly (i.e., refer to `conftest.exe'), while it won't with +# `rm'. +for ac_file in conftest.exe conftest conftest.*; do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + break;; + * ) break;; + esac +done +else $as_nop + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details" "$LINENO" 5; } fi +rm -f conftest conftest$ac_cv_exeext +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 +printf "%s\n" "$ac_cv_exeext" >&6; } - -for ac_func in vprintf -do -as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` -{ echo "$as_me:$LINENO: checking for $ac_func" >&5 -echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6; } -if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -/* Define $ac_func to an innocuous variant, in case declares $ac_func. - For example, HP-UX 11i declares gettimeofday. */ -#define $ac_func innocuous_$ac_func - -/* System header to define __stub macros and hopefully few prototypes, - which can conflict with char $ac_func (); below. - Prefer to if __STDC__ is defined, since - exists even on freestanding compilers. */ - -#ifdef __STDC__ -# include -#else -# include -#endif - -#undef $ac_func - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char $ac_func (); -/* The GNU C library defines this for functions which it implements - to always fail with ENOSYS. Some functions are actually named - something starting with __ and the normal name is an alias. */ -#if defined __stub_$ac_func || defined __stub___$ac_func -choke me -#endif - +#include int -main () +main (void) { -return $ac_func (); +FILE *f = fopen ("conftest.out", "w"); + return ferror (f) || fclose (f) != 0; + ; return 0; } _ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (ac_try="$ac_link" +ac_clean_files="$ac_clean_files conftest.out" +# Check that the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 +printf %s "checking whether we are cross compiling... " >&6; } +if test "$cross_compiling" != yes; then + { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_link") 2>conftest.er1 +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +printf "%s\n" "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + if { ac_try='./conftest$ac_cv_exeext' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +printf "%s\n" "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest$ac_exeext && - $as_test_x conftest$ac_exeext; then - eval "$as_ac_var=yes" -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - eval "$as_ac_var=no" -fi - -rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ - conftest$ac_exeext conftest.$ac_ext + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error 77 "cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details" "$LINENO" 5; } + fi + fi fi -ac_res=`eval echo '${'$as_ac_var'}'` - { echo "$as_me:$LINENO: result: $ac_res" >&5 -echo "${ECHO_T}$ac_res" >&6; } -if test `eval echo '${'$as_ac_var'}'` = yes; then - cat >>confdefs.h <<_ACEOF -#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 -_ACEOF +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 +printf "%s\n" "$cross_compiling" >&6; } -{ echo "$as_me:$LINENO: checking for _doprnt" >&5 -echo $ECHO_N "checking for _doprnt... $ECHO_C" >&6; } -if test "${ac_cv_func__doprnt+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF +rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out +ac_clean_files=$ac_clean_files_save +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 +printf %s "checking for suffix of object files... " >&6; } +if test ${ac_cv_objext+y} +then : + printf %s "(cached) " >&6 +else $as_nop + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -/* Define _doprnt to an innocuous variant, in case declares _doprnt. - For example, HP-UX 11i declares gettimeofday. */ -#define _doprnt innocuous__doprnt - -/* System header to define __stub macros and hopefully few prototypes, - which can conflict with char _doprnt (); below. - Prefer to if __STDC__ is defined, since - exists even on freestanding compilers. */ - -#ifdef __STDC__ -# include -#else -# include -#endif - -#undef _doprnt - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char _doprnt (); -/* The GNU C library defines this for functions which it implements - to always fail with ENOSYS. Some functions are actually named - something starting with __ and the normal name is an alias. */ -#if defined __stub__doprnt || defined __stub____doprnt -choke me -#endif int -main () +main (void) { -return _doprnt (); + ; return 0; } _ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (ac_try="$ac_link" +rm -f conftest.o conftest.obj +if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_link") 2>conftest.er1 +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +printf "%s\n" "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>&5 ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest$ac_exeext && - $as_test_x conftest$ac_exeext; then - ac_cv_func__doprnt=yes -else - echo "$as_me: failed program was:" >&5 + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +then : + for ac_file in conftest.o conftest.obj conftest.*; do + test -f "$ac_file" || continue; + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else $as_nop + printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 - ac_cv_func__doprnt=no +{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot compute suffix of object files: cannot compile +See \`config.log' for more details" "$LINENO" 5; } fi - -rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ - conftest$ac_exeext conftest.$ac_ext +rm -f conftest.$ac_cv_objext conftest.$ac_ext fi -{ echo "$as_me:$LINENO: result: $ac_cv_func__doprnt" >&5 -echo "${ECHO_T}$ac_cv_func__doprnt" >&6; } -if test $ac_cv_func__doprnt = yes; then +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 +printf "%s\n" "$ac_cv_objext" >&6; } +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports GNU C" >&5 +printf %s "checking whether the compiler supports GNU C... " >&6; } +if test ${ac_cv_c_compiler_gnu+y} +then : + printf %s "(cached) " >&6 +else $as_nop + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ -cat >>confdefs.h <<\_ACEOF -#define HAVE_DOPRNT 1 -_ACEOF +int +main (void) +{ +#ifndef __GNUC__ + choke me +#endif + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO" +then : + ac_compiler_gnu=yes +else $as_nop + ac_compiler_gnu=no fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu fi -done - - - - - - - - +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 +printf "%s\n" "$ac_cv_c_compiler_gnu" >&6; } +ac_compiler_gnu=$ac_cv_c_compiler_gnu -for ac_func in floor memset pow setlocale sqrt strchr strstr -do -as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` -{ echo "$as_me:$LINENO: checking for $ac_func" >&5 -echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6; } -if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then - echo $ECHO_N "(cached) $ECHO_C" >&6 +if test $ac_compiler_gnu = yes; then + GCC=yes else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF + GCC= +fi +ac_test_CFLAGS=${CFLAGS+y} +ac_save_CFLAGS=$CFLAGS +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 +printf %s "checking whether $CC accepts -g... " >&6; } +if test ${ac_cv_prog_cc_g+y} +then : + printf %s "(cached) " >&6 +else $as_nop + ac_save_c_werror_flag=$ac_c_werror_flag + ac_c_werror_flag=yes + ac_cv_prog_cc_g=no + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -/* Define $ac_func to an innocuous variant, in case declares $ac_func. - For example, HP-UX 11i declares gettimeofday. */ -#define $ac_func innocuous_$ac_func - -/* System header to define __stub macros and hopefully few prototypes, - which can conflict with char $ac_func (); below. - Prefer to if __STDC__ is defined, since - exists even on freestanding compilers. */ - -#ifdef __STDC__ -# include -#else -# include -#endif - -#undef $ac_func - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char $ac_func (); -/* The GNU C library defines this for functions which it implements - to always fail with ENOSYS. Some functions are actually named - something starting with __ and the normal name is an alias. */ -#if defined __stub_$ac_func || defined __stub___$ac_func -choke me -#endif int -main () +main (void) { -return $ac_func (); + ; return 0; } _ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_link") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest$ac_exeext && - $as_test_x conftest$ac_exeext; then - eval "$as_ac_var=yes" -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 +if ac_fn_c_try_compile "$LINENO" +then : + ac_cv_prog_cc_g=yes +else $as_nop + CFLAGS="" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ - eval "$as_ac_var=no" -fi +int +main (void) +{ -rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ - conftest$ac_exeext conftest.$ac_ext -fi -ac_res=`eval echo '${'$as_ac_var'}'` - { echo "$as_me:$LINENO: result: $ac_res" >&5 -echo "${ECHO_T}$ac_res" >&6; } -if test `eval echo '${'$as_ac_var'}'` = yes; then - cat >>confdefs.h <<_ACEOF -#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 + ; + return 0; +} _ACEOF +if ac_fn_c_try_compile "$LINENO" +then : -fi -done - - -# System libraries that may be required by WCSLIB itself. -# SunOS, extra maths functions. -{ echo "$as_me:$LINENO: checking for cosd in -lsunmath" >&5 -echo $ECHO_N "checking for cosd in -lsunmath... $ECHO_C" >&6; } -if test "${ac_cv_lib_sunmath_cosd+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-lsunmath $LIBS" -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF +else $as_nop + ac_c_werror_flag=$ac_save_c_werror_flag + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char cosd (); int -main () +main (void) { -return cosd (); + ; return 0; } _ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_link") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest$ac_exeext && - $as_test_x conftest$ac_exeext; then - ac_cv_lib_sunmath_cosd=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_cv_lib_sunmath_cosd=no +if ac_fn_c_try_compile "$LINENO" +then : + ac_cv_prog_cc_g=yes fi - -rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi -{ echo "$as_me:$LINENO: result: $ac_cv_lib_sunmath_cosd" >&5 -echo "${ECHO_T}$ac_cv_lib_sunmath_cosd" >&6; } -if test $ac_cv_lib_sunmath_cosd = yes; then - LIBS="-lsunmath $LIBS" +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi - - -# See if we can find sincos(). - -for ac_func in sincos -do -as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` -{ echo "$as_me:$LINENO: checking for $ac_func" >&5 -echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6; } -if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then - echo $ECHO_N "(cached) $ECHO_C" >&6 +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext + ac_c_werror_flag=$ac_save_c_werror_flag +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 +printf "%s\n" "$ac_cv_prog_cc_g" >&6; } +if test $ac_test_CFLAGS; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +ac_prog_cc_stdc=no +if test x$ac_prog_cc_stdc = xno +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C11 features" >&5 +printf %s "checking for $CC option to enable C11 features... " >&6; } +if test ${ac_cv_prog_cc_c11+y} +then : + printf %s "(cached) " >&6 +else $as_nop + ac_cv_prog_cc_c11=no +ac_save_CC=$CC +cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -/* Define $ac_func to an innocuous variant, in case declares $ac_func. - For example, HP-UX 11i declares gettimeofday. */ -#define $ac_func innocuous_$ac_func +$ac_c_conftest_c11_program +_ACEOF +for ac_arg in '' -std=gnu11 +do + CC="$ac_save_CC $ac_arg" + if ac_fn_c_try_compile "$LINENO" +then : + ac_cv_prog_cc_c11=$ac_arg +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam + test "x$ac_cv_prog_cc_c11" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC +fi -/* System header to define __stub macros and hopefully few prototypes, - which can conflict with char $ac_func (); below. - Prefer to if __STDC__ is defined, since - exists even on freestanding compilers. */ +if test "x$ac_cv_prog_cc_c11" = xno +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +printf "%s\n" "unsupported" >&6; } +else $as_nop + if test "x$ac_cv_prog_cc_c11" = x +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +printf "%s\n" "none needed" >&6; } +else $as_nop + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c11" >&5 +printf "%s\n" "$ac_cv_prog_cc_c11" >&6; } + CC="$CC $ac_cv_prog_cc_c11" +fi + ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c11 + ac_prog_cc_stdc=c11 +fi +fi +if test x$ac_prog_cc_stdc = xno +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C99 features" >&5 +printf %s "checking for $CC option to enable C99 features... " >&6; } +if test ${ac_cv_prog_cc_c99+y} +then : + printf %s "(cached) " >&6 +else $as_nop + ac_cv_prog_cc_c99=no +ac_save_CC=$CC +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_c_conftest_c99_program +_ACEOF +for ac_arg in '' -std=gnu99 -std=c99 -c99 -qlanglvl=extc1x -qlanglvl=extc99 -AC99 -D_STDC_C99= +do + CC="$ac_save_CC $ac_arg" + if ac_fn_c_try_compile "$LINENO" +then : + ac_cv_prog_cc_c99=$ac_arg +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam + test "x$ac_cv_prog_cc_c99" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC +fi -#ifdef __STDC__ -# include -#else -# include -#endif +if test "x$ac_cv_prog_cc_c99" = xno +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +printf "%s\n" "unsupported" >&6; } +else $as_nop + if test "x$ac_cv_prog_cc_c99" = x +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +printf "%s\n" "none needed" >&6; } +else $as_nop + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5 +printf "%s\n" "$ac_cv_prog_cc_c99" >&6; } + CC="$CC $ac_cv_prog_cc_c99" +fi + ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c99 + ac_prog_cc_stdc=c99 +fi +fi +if test x$ac_prog_cc_stdc = xno +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C89 features" >&5 +printf %s "checking for $CC option to enable C89 features... " >&6; } +if test ${ac_cv_prog_cc_c89+y} +then : + printf %s "(cached) " >&6 +else $as_nop + ac_cv_prog_cc_c89=no +ac_save_CC=$CC +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_c_conftest_c89_program +_ACEOF +for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + if ac_fn_c_try_compile "$LINENO" +then : + ac_cv_prog_cc_c89=$ac_arg +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam + test "x$ac_cv_prog_cc_c89" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC +fi -#undef $ac_func +if test "x$ac_cv_prog_cc_c89" = xno +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +printf "%s\n" "unsupported" >&6; } +else $as_nop + if test "x$ac_cv_prog_cc_c89" = x +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +printf "%s\n" "none needed" >&6; } +else $as_nop + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 +printf "%s\n" "$ac_cv_prog_cc_c89" >&6; } + CC="$CC $ac_cv_prog_cc_c89" +fi + ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c89 + ac_prog_cc_stdc=c89 +fi +fi -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char $ac_func (); -/* The GNU C library defines this for functions which it implements - to always fail with ENOSYS. Some functions are actually named - something starting with __ and the normal name is an alias. */ -#if defined __stub_$ac_func || defined __stub___$ac_func -choke me -#endif +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu -int -main () -{ -return $ac_func (); - ; - return 0; -} +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 +printf %s "checking how to run the C preprocessor... " >&6; } +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then + if test ${ac_cv_prog_CPP+y} +then : + printf %s "(cached) " >&6 +else $as_nop + # Double quotes because $CC needs to be expanded + for CPP in "$CC -E" "$CC -E -traditional-cpp" cpp /lib/cpp + do + ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + Syntax error _ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_link") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest$ac_exeext && - $as_test_x conftest$ac_exeext; then - eval "$as_ac_var=yes" -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 +if ac_fn_c_try_cpp "$LINENO" +then : - eval "$as_ac_var=no" +else $as_nop + # Broken: fails on valid input. +continue fi +rm -f conftest.err conftest.i conftest.$ac_ext -rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ - conftest$ac_exeext conftest.$ac_ext -fi -ac_res=`eval echo '${'$as_ac_var'}'` - { echo "$as_me:$LINENO: result: $ac_res" >&5 -echo "${ECHO_T}$ac_res" >&6; } -if test `eval echo '${'$as_ac_var'}'` = yes; then - cat >>confdefs.h <<_ACEOF -#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include _ACEOF - +if ac_fn_c_try_cpp "$LINENO" +then : + # Broken: success on invalid input. +continue +else $as_nop + # Passes both tests. +ac_preproc_ok=: +break fi +rm -f conftest.err conftest.i conftest.$ac_ext + done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok +then : + break +fi + done + ac_cv_prog_CPP=$CPP -# Check the size and availability of integer data types. -{ echo "$as_me:$LINENO: checking for int" >&5 -echo $ECHO_N "checking for int... $ECHO_C" >&6; } -if test "${ac_cv_type_int+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 +fi + CPP=$ac_cv_prog_CPP else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ + ac_cv_prog_CPP=$CPP +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 +printf "%s\n" "$CPP" >&6; } +ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + Syntax error _ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF +if ac_fn_c_try_cpp "$LINENO" +then : + +else $as_nop + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -$ac_includes_default -typedef int ac__type_new_; -int -main () -{ -if ((ac__type_new_ *) 0) - return 0; -if (sizeof (ac__type_new_)) - return 0; - ; - return 0; -} +#include _ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then - ac_cv_type_int=yes +if ac_fn_c_try_cpp "$LINENO" +then : + # Broken: success on invalid input. +continue +else $as_nop + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok +then : + +else $as_nop + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details" "$LINENO" 5; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_CC+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS - ac_cv_type_int=no +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +printf "%s\n" "$CC" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + fi -{ echo "$as_me:$LINENO: result: $ac_cv_type_int" >&5 -echo "${ECHO_T}$ac_cv_type_int" >&6; } +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_ac_ct_CC+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="gcc" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS -# The cast to long int works around a bug in the HP C Compiler -# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects -# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. -# This bug is HP SR number 8606223364. -{ echo "$as_me:$LINENO: checking size of int" >&5 -echo $ECHO_N "checking size of int... $ECHO_C" >&6; } -if test "${ac_cv_sizeof_int+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +printf "%s\n" "$ac_ct_CC" >&6; } else - if test "$cross_compiling" = yes; then - # Depending upon the size, compute the lo and hi bounds. -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -$ac_includes_default - typedef int ac__type_sizeof_; -int -main () -{ -static int test_array [1 - 2 * !(((long int) (sizeof (ac__type_sizeof_))) >= 0)]; -test_array [0] = 0 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then - ac_lo=0 ac_mid=0 - while :; do - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -$ac_includes_default - typedef int ac__type_sizeof_; -int -main () -{ -static int test_array [1 - 2 * !(((long int) (sizeof (ac__type_sizeof_))) <= $ac_mid)]; -test_array [0] = 0 + CC=$ac_ct_CC + fi +else + CC="$ac_cv_prog_CC" +fi - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then - ac_hi=$ac_mid; break +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_CC+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS - ac_lo=`expr $ac_mid + 1` - if test $ac_lo -le $ac_mid; then - ac_lo= ac_hi= - break - fi - ac_mid=`expr 2 '*' $ac_mid + 1` fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +printf "%s\n" "$CC" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + fi +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_CC+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + if test "$as_dir$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done done +IFS=$as_save_IFS + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +printf "%s\n" "$CC" >&6; } else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -$ac_includes_default - typedef int ac__type_sizeof_; -int -main () -{ -static int test_array [1 - 2 * !(((long int) (sizeof (ac__type_sizeof_))) < 0)]; -test_array [0] = 0 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then - ac_hi=-1 ac_mid=-1 - while :; do - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -$ac_includes_default - typedef int ac__type_sizeof_; -int -main () -{ -static int test_array [1 - 2 * !(((long int) (sizeof (ac__type_sizeof_))) >= $ac_mid)]; -test_array [0] = 0 - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then - ac_lo=$ac_mid; break +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl.exe + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_CC+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS - ac_hi=`expr '(' $ac_mid ')' - 1` - if test $ac_mid -le $ac_hi; then - ac_lo= ac_hi= - break - fi - ac_mid=`expr 2 '*' $ac_mid` +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +printf "%s\n" "$CC" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + + test -n "$CC" && break done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl.exe +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_ac_ct_CC+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="$ac_prog" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS - ac_lo= ac_hi= fi - -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi - -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -# Binary search between lo and hi bounds. -while test "x$ac_lo" != "x$ac_hi"; do - ac_mid=`expr '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo` - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -$ac_includes_default - typedef int ac__type_sizeof_; -int -main () -{ -static int test_array [1 - 2 * !(((long int) (sizeof (ac__type_sizeof_))) <= $ac_mid)]; -test_array [0] = 0 - - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then - ac_hi=$ac_mid +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +printf "%s\n" "$ac_ct_CC" >&6; } else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_lo=`expr '(' $ac_mid ')' + 1` + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + + test -n "$ac_ct_CC" && break done -case $ac_lo in -?*) ac_cv_sizeof_int=$ac_lo;; -'') if test "$ac_cv_type_int" = yes; then - { { echo "$as_me:$LINENO: error: cannot compute sizeof (int) -See \`config.log' for more details." >&5 -echo "$as_me: error: cannot compute sizeof (int) -See \`config.log' for more details." >&2;} - { (exit 77); exit 77; }; } - else - ac_cv_sizeof_int=0 - fi ;; -esac -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -$ac_includes_default - typedef int ac__type_sizeof_; -static long int longval () { return (long int) (sizeof (ac__type_sizeof_)); } -static unsigned long int ulongval () { return (long int) (sizeof (ac__type_sizeof_)); } -#include -#include -int -main () -{ - FILE *f = fopen ("conftest.val", "w"); - if (! f) - return 1; - if (((long int) (sizeof (ac__type_sizeof_))) < 0) - { - long int i = longval (); - if (i != ((long int) (sizeof (ac__type_sizeof_)))) - return 1; - fprintf (f, "%ld\n", i); - } + if test "x$ac_ct_CC" = x; then + CC="" else - { - unsigned long int i = ulongval (); - if (i != ((long int) (sizeof (ac__type_sizeof_)))) - return 1; - fprintf (f, "%lu\n", i); - } - return ferror (f) || fclose (f) != 0; - - ; - return 0; -} -_ACEOF -rm -f conftest$ac_exeext -if { (ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_link") 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { ac_try='./conftest$ac_exeext' - { (case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; + case $cross_compiling:$ac_tool_warned in +yes:) +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_try") 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_cv_sizeof_int=`cat conftest.val` + CC=$ac_ct_CC + fi +fi + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}clang", so it can be a program name with args. +set dummy ${ac_tool_prefix}clang; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_CC+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. else - echo "$as_me: program exited with status $ac_status" >&5 -echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}clang" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS -( exit $ac_status ) -if test "$ac_cv_type_int" = yes; then - { { echo "$as_me:$LINENO: error: cannot compute sizeof (int) -See \`config.log' for more details." >&5 -echo "$as_me: error: cannot compute sizeof (int) -See \`config.log' for more details." >&2;} - { (exit 77); exit 77; }; } - else - ac_cv_sizeof_int=0 - fi fi -rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext fi -rm -f conftest.val +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +printf "%s\n" "$CC" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } fi -{ echo "$as_me:$LINENO: result: $ac_cv_sizeof_int" >&5 -echo "${ECHO_T}$ac_cv_sizeof_int" >&6; } - -cat >>confdefs.h <<_ACEOF -#define SIZEOF_INT $ac_cv_sizeof_int -_ACEOF - +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "clang", so it can be a program name with args. +set dummy clang; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_ac_ct_CC+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="clang" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS -{ echo "$as_me:$LINENO: checking for long int" >&5 -echo $ECHO_N "checking for long int... $ECHO_C" >&6; } -if test "${ac_cv_type_long_int+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +printf "%s\n" "$ac_ct_CC" >&6; } else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -$ac_includes_default -typedef long int ac__type_new_; -int -main () -{ -if ((ac__type_new_ *) 0) - return 0; -if (sizeof (ac__type_new_)) - return 0; - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then - ac_cv_type_long_int=yes + CC=$ac_ct_CC + fi else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_cv_type_long_int=no + CC="$ac_cv_prog_CC" fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi -{ echo "$as_me:$LINENO: result: $ac_cv_type_long_int" >&5 -echo "${ECHO_T}$ac_cv_type_long_int" >&6; } -# The cast to long int works around a bug in the HP C Compiler -# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects -# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. -# This bug is HP SR number 8606223364. -{ echo "$as_me:$LINENO: checking size of long int" >&5 -echo $ECHO_N "checking size of long int... $ECHO_C" >&6; } -if test "${ac_cv_sizeof_long_int+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test "$cross_compiling" = yes; then - # Depending upon the size, compute the lo and hi bounds. -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -$ac_includes_default - typedef long int ac__type_sizeof_; -int -main () -{ -static int test_array [1 - 2 * !(((long int) (sizeof (ac__type_sizeof_))) >= 0)]; -test_array [0] = 0 - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" +test -z "$CC" && { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "no acceptable C compiler found in \$PATH +See \`config.log' for more details" "$LINENO" 5; } + +# Provide some information about the compiler. +printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +for ac_option in --version -v -V -qversion -version; do + { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +printf "%s\n" "$ac_try_echo"; } >&5 + (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then - ac_lo=0 ac_mid=0 - while :; do - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF + if test -s conftest.err; then + sed '10a\ +... rest of stderr output deleted ... + 10q' conftest.err >conftest.er1 + cat conftest.er1 >&5 + fi + rm -f conftest.er1 conftest.err + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +done + +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports GNU C" >&5 +printf %s "checking whether the compiler supports GNU C... " >&6; } +if test ${ac_cv_c_compiler_gnu+y} +then : + printf %s "(cached) " >&6 +else $as_nop + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -$ac_includes_default - typedef long int ac__type_sizeof_; + int -main () +main (void) { -static int test_array [1 - 2 * !(((long int) (sizeof (ac__type_sizeof_))) <= $ac_mid)]; -test_array [0] = 0 +#ifndef __GNUC__ + choke me +#endif ; return 0; } _ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then - ac_hi=$ac_mid; break -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 +if ac_fn_c_try_compile "$LINENO" +then : + ac_compiler_gnu=yes +else $as_nop + ac_compiler_gnu=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu - ac_lo=`expr $ac_mid + 1` - if test $ac_lo -le $ac_mid; then - ac_lo= ac_hi= - break - fi - ac_mid=`expr 2 '*' $ac_mid + 1` fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 +printf "%s\n" "$ac_cv_c_compiler_gnu" >&6; } +ac_compiler_gnu=$ac_cv_c_compiler_gnu -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - done +if test $ac_compiler_gnu = yes; then + GCC=yes else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF + GCC= +fi +ac_test_CFLAGS=${CFLAGS+y} +ac_save_CFLAGS=$CFLAGS +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 +printf %s "checking whether $CC accepts -g... " >&6; } +if test ${ac_cv_prog_cc_g+y} +then : + printf %s "(cached) " >&6 +else $as_nop + ac_save_c_werror_flag=$ac_c_werror_flag + ac_c_werror_flag=yes + ac_cv_prog_cc_g=no + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -$ac_includes_default - typedef long int ac__type_sizeof_; + int -main () +main (void) { -static int test_array [1 - 2 * !(((long int) (sizeof (ac__type_sizeof_))) < 0)]; -test_array [0] = 0 ; return 0; } _ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then - ac_hi=-1 ac_mid=-1 - while :; do - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF +if ac_fn_c_try_compile "$LINENO" +then : + ac_cv_prog_cc_g=yes +else $as_nop + CFLAGS="" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -$ac_includes_default - typedef long int ac__type_sizeof_; + int -main () +main (void) { -static int test_array [1 - 2 * !(((long int) (sizeof (ac__type_sizeof_))) >= $ac_mid)]; -test_array [0] = 0 ; return 0; } _ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then - ac_lo=$ac_mid; break -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_hi=`expr '(' $ac_mid ')' - 1` - if test $ac_mid -le $ac_hi; then - ac_lo= ac_hi= - break - fi - ac_mid=`expr 2 '*' $ac_mid` -fi - -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - done -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_lo= ac_hi= -fi - -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi +if ac_fn_c_try_compile "$LINENO" +then : -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -# Binary search between lo and hi bounds. -while test "x$ac_lo" != "x$ac_hi"; do - ac_mid=`expr '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo` - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF +else $as_nop + ac_c_werror_flag=$ac_save_c_werror_flag + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -$ac_includes_default - typedef long int ac__type_sizeof_; + int -main () +main (void) { -static int test_array [1 - 2 * !(((long int) (sizeof (ac__type_sizeof_))) <= $ac_mid)]; -test_array [0] = 0 ; return 0; } _ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then - ac_hi=$ac_mid +if ac_fn_c_try_compile "$LINENO" +then : + ac_cv_prog_cc_g=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext + ac_c_werror_flag=$ac_save_c_werror_flag +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 +printf "%s\n" "$ac_cv_prog_cc_g" >&6; } +if test $ac_test_CFLAGS; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +ac_prog_cc_stdc=no +if test x$ac_prog_cc_stdc = xno +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C11 features" >&5 +printf %s "checking for $CC option to enable C11 features... " >&6; } +if test ${ac_cv_prog_cc_c11+y} +then : + printf %s "(cached) " >&6 +else $as_nop + ac_cv_prog_cc_c11=no +ac_save_CC=$CC +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_c_conftest_c11_program +_ACEOF +for ac_arg in '' -std=gnu11 +do + CC="$ac_save_CC $ac_arg" + if ac_fn_c_try_compile "$LINENO" +then : + ac_cv_prog_cc_c11=$ac_arg +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam + test "x$ac_cv_prog_cc_c11" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC +fi - ac_lo=`expr '(' $ac_mid ')' + 1` +if test "x$ac_cv_prog_cc_c11" = xno +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +printf "%s\n" "unsupported" >&6; } +else $as_nop + if test "x$ac_cv_prog_cc_c11" = x +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +printf "%s\n" "none needed" >&6; } +else $as_nop + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c11" >&5 +printf "%s\n" "$ac_cv_prog_cc_c11" >&6; } + CC="$CC $ac_cv_prog_cc_c11" +fi + ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c11 + ac_prog_cc_stdc=c11 +fi +fi +if test x$ac_prog_cc_stdc = xno +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C99 features" >&5 +printf %s "checking for $CC option to enable C99 features... " >&6; } +if test ${ac_cv_prog_cc_c99+y} +then : + printf %s "(cached) " >&6 +else $as_nop + ac_cv_prog_cc_c99=no +ac_save_CC=$CC +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_c_conftest_c99_program +_ACEOF +for ac_arg in '' -std=gnu99 -std=c99 -c99 -qlanglvl=extc1x -qlanglvl=extc99 -AC99 -D_STDC_C99= +do + CC="$ac_save_CC $ac_arg" + if ac_fn_c_try_compile "$LINENO" +then : + ac_cv_prog_cc_c99=$ac_arg +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam + test "x$ac_cv_prog_cc_c99" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +if test "x$ac_cv_prog_cc_c99" = xno +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +printf "%s\n" "unsupported" >&6; } +else $as_nop + if test "x$ac_cv_prog_cc_c99" = x +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +printf "%s\n" "none needed" >&6; } +else $as_nop + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5 +printf "%s\n" "$ac_cv_prog_cc_c99" >&6; } + CC="$CC $ac_cv_prog_cc_c99" +fi + ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c99 + ac_prog_cc_stdc=c99 +fi +fi +if test x$ac_prog_cc_stdc = xno +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C89 features" >&5 +printf %s "checking for $CC option to enable C89 features... " >&6; } +if test ${ac_cv_prog_cc_c89+y} +then : + printf %s "(cached) " >&6 +else $as_nop + ac_cv_prog_cc_c89=no +ac_save_CC=$CC +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_c_conftest_c89_program +_ACEOF +for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + if ac_fn_c_try_compile "$LINENO" +then : + ac_cv_prog_cc_c89=$ac_arg +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam + test "x$ac_cv_prog_cc_c89" != "xno" && break done -case $ac_lo in -?*) ac_cv_sizeof_long_int=$ac_lo;; -'') if test "$ac_cv_type_long_int" = yes; then - { { echo "$as_me:$LINENO: error: cannot compute sizeof (long int) -See \`config.log' for more details." >&5 -echo "$as_me: error: cannot compute sizeof (long int) -See \`config.log' for more details." >&2;} - { (exit 77); exit 77; }; } - else - ac_cv_sizeof_long_int=0 - fi ;; -esac +rm -f conftest.$ac_ext +CC=$ac_save_CC +fi + +if test "x$ac_cv_prog_cc_c89" = xno +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +printf "%s\n" "unsupported" >&6; } +else $as_nop + if test "x$ac_cv_prog_cc_c89" = x +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +printf "%s\n" "none needed" >&6; } +else $as_nop + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 +printf "%s\n" "$ac_cv_prog_cc_c89" >&6; } + CC="$CC $ac_cv_prog_cc_c89" +fi + ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c89 + ac_prog_cc_stdc=c89 +fi +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +if test "x$ac_cv_c_compiler_gnu" = xyes ; then + # Get gcc version number. + GCC_VERSION=`$CC -dumpfullversion` + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Using gcc version $GCC_VERSION" >&5 +printf "%s\n" "$as_me: Using gcc version $GCC_VERSION" >&6;} else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF + GCC_VERSION= +fi + + + +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for an ANSI C-conforming const" >&5 +printf %s "checking for an ANSI C-conforming const... " >&6; } +if test ${ac_cv_c_const+y} +then : + printf %s "(cached) " >&6 +else $as_nop + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -$ac_includes_default - typedef long int ac__type_sizeof_; -static long int longval () { return (long int) (sizeof (ac__type_sizeof_)); } -static unsigned long int ulongval () { return (long int) (sizeof (ac__type_sizeof_)); } -#include -#include + int -main () +main (void) { - FILE *f = fopen ("conftest.val", "w"); - if (! f) - return 1; - if (((long int) (sizeof (ac__type_sizeof_))) < 0) - { - long int i = longval (); - if (i != ((long int) (sizeof (ac__type_sizeof_)))) - return 1; - fprintf (f, "%ld\n", i); - } - else - { - unsigned long int i = ulongval (); - if (i != ((long int) (sizeof (ac__type_sizeof_)))) - return 1; - fprintf (f, "%lu\n", i); - } - return ferror (f) || fclose (f) != 0; +#ifndef __cplusplus + /* Ultrix mips cc rejects this sort of thing. */ + typedef int charset[2]; + const charset cs = { 0, 0 }; + /* SunOS 4.1.1 cc rejects this. */ + char const *const *pcpcc; + char **ppc; + /* NEC SVR4.0.2 mips cc rejects this. */ + struct point {int x, y;}; + static struct point const zero = {0,0}; + /* IBM XL C 1.02.0.0 rejects this. + It does not let you subtract one const X* pointer from another in + an arm of an if-expression whose if-part is not a constant + expression */ + const char *g = "string"; + pcpcc = &g + (g ? g-g : 0); + /* HPUX 7.0 cc rejects these. */ + ++pcpcc; + ppc = (char**) pcpcc; + pcpcc = (char const *const *) ppc; + { /* SCO 3.2v4 cc rejects this sort of thing. */ + char tx; + char *t = &tx; + char const *s = 0 ? (char *) 0 : (char const *) 0; + + *t++ = 0; + if (s) return 0; + } + { /* Someone thinks the Sun supposedly-ANSI compiler will reject this. */ + int x[] = {25, 17}; + const int *foo = &x[0]; + ++foo; + } + { /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */ + typedef const int *iptr; + iptr p = 0; + ++p; + } + { /* IBM XL C 1.02.0.0 rejects this sort of thing, saying + "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */ + struct s { int j; const int *ap[3]; } bx; + struct s *b = &bx; b->j = 5; + } + { /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */ + const int foo = 10; + if (!foo) return 0; + } + return !cs[0] && !zero.x; +#endif ; return 0; } _ACEOF -rm -f conftest$ac_exeext -if { (ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_link") 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { ac_try='./conftest$ac_exeext' - { (case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_try") 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_cv_sizeof_long_int=`cat conftest.val` -else - echo "$as_me: program exited with status $ac_status" >&5 -echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 +if ac_fn_c_try_compile "$LINENO" +then : + ac_cv_c_const=yes +else $as_nop + ac_cv_c_const=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_const" >&5 +printf "%s\n" "$ac_cv_c_const" >&6; } +if test $ac_cv_c_const = no; then + +printf "%s\n" "#define const /**/" >>confdefs.h + +fi + +ac_header= ac_cache= +for ac_item in $ac_header_c_list +do + if test $ac_cache; then + ac_fn_c_check_header_compile "$LINENO" $ac_header ac_cv_header_$ac_cache "$ac_includes_default" + if eval test \"x\$ac_cv_header_$ac_cache\" = xyes; then + printf "%s\n" "#define $ac_item 1" >> confdefs.h + fi + ac_header= ac_cache= + elif test $ac_header; then + ac_cache=$ac_item + else + ac_header=$ac_item + fi +done + + + + + + + + +if test $ac_cv_header_stdlib_h = yes && test $ac_cv_header_string_h = yes +then : + +printf "%s\n" "#define STDC_HEADERS 1" >>confdefs.h -( exit $ac_status ) -if test "$ac_cv_type_long_int" = yes; then - { { echo "$as_me:$LINENO: error: cannot compute sizeof (long int) -See \`config.log' for more details." >&5 -echo "$as_me: error: cannot compute sizeof (long int) -See \`config.log' for more details." >&2;} - { (exit 77); exit 77; }; } - else - ac_cv_sizeof_long_int=0 - fi fi -rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +ac_fn_c_check_type "$LINENO" "size_t" "ac_cv_type_size_t" "$ac_includes_default" +if test "x$ac_cv_type_size_t" = xyes +then : + +else $as_nop + +printf "%s\n" "#define size_t unsigned int" >>confdefs.h + fi -rm -f conftest.val + +if test "x$ac_cv_prog_cc_stdc" = xno -o \ + "x$ac_cv_c_const" = xno -o \ + "x$ac_cv_type_size_t" = xno; then + as_fn_error 1 " + ------------------------------------------------------- + An ANSI standard C library is required to build WCSLIB. + + ERROR: WCSLIB configuration failure. + -------------------------------------------------------" "$LINENO" 5 fi -{ echo "$as_me:$LINENO: result: $ac_cv_sizeof_long_int" >&5 -echo "${ECHO_T}$ac_cv_sizeof_long_int" >&6; } +# Data types used in wcs.c (results not currently used). +ac_fn_c_find_uintX_t "$LINENO" "16" "ac_cv_c_uint16_t" +case $ac_cv_c_uint16_t in #( + no|yes) ;; #( + *) -cat >>confdefs.h <<_ACEOF -#define SIZEOF_LONG_INT $ac_cv_sizeof_long_int -_ACEOF +printf "%s\n" "#define uint16_t $ac_cv_c_uint16_t" >>confdefs.h +;; + esac +ac_fn_c_find_uintX_t "$LINENO" "32" "ac_cv_c_uint32_t" +case $ac_cv_c_uint32_t in #( + no|yes) ;; #( + *) -{ echo "$as_me:$LINENO: checking for long long int" >&5 -echo $ECHO_N "checking for long long int... $ECHO_C" >&6; } -if test "${ac_cv_type_long_long_int+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -$ac_includes_default -typedef long long int ac__type_new_; -int -main () -{ -if ((ac__type_new_ *) 0) - return 0; -if (sizeof (ac__type_new_)) - return 0; - ; - return 0; -} +printf "%s\n" "#define _UINT32_T 1" >>confdefs.h + + +printf "%s\n" "#define uint32_t $ac_cv_c_uint32_t" >>confdefs.h +;; + esac + + +# Check for standard C header files required to compile the library. +headers= + for ac_header in ctype.h inttypes.h limits.h locale.h math.h setjmp.h stdarg.h stddef.h stdint.h stdio.h stdlib.h string.h +do : + as_ac_Header=`printf "%s\n" "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" +if eval test \"x\$"$as_ac_Header"\" = x"yes" +then : + cat >>confdefs.h <<_ACEOF +#define `printf "%s\n" "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then - ac_cv_type_long_long_int=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - ac_cv_type_long_long_int=no +else $as_nop + headers=no +fi + +done +if test "x$headers" = xno; then + as_fn_error 1 " + ------------------------------------------------------------------- + An ANSI standard C library is required to build WCSLIB. One of the + standard C header files it requires is missing or unusable. Please + refer to the above log. + + ERROR: WCSLIB configuration failure. + -------------------------------------------------------------------" "$LINENO" 5 fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +# Flex uses fileno() and other POSIX features whose prototypes are only +# available from glibc's stdio.h with an appropriate preprocessor macro +# definition. This cannot be set within the flex description file itself +# as stdio.h is included in the generated C code before any part of the +# description. See fileno(3) and feature_test_macros(7). +if test "x$ac_cv_c_compiler_gnu" = xyes ; then + FLFLAGS="$FLFLAGS -D_POSIX_C_SOURCE=1" fi -{ echo "$as_me:$LINENO: result: $ac_cv_type_long_long_int" >&5 -echo "${ECHO_T}$ac_cv_type_long_long_int" >&6; } -# The cast to long int works around a bug in the HP C Compiler -# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects -# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. -# This bug is HP SR number 8606223364. -{ echo "$as_me:$LINENO: checking size of long long int" >&5 -echo $ECHO_N "checking size of long long int... $ECHO_C" >&6; } -if test "${ac_cv_sizeof_long_long_int+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test "$cross_compiling" = yes; then - # Depending upon the size, compute the lo and hi bounds. -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -$ac_includes_default - typedef long long int ac__type_sizeof_; -int -main () -{ -static int test_array [1 - 2 * !(((long int) (sizeof (ac__type_sizeof_))) >= 0)]; -test_array [0] = 0 - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then - ac_lo=0 ac_mid=0 - while :; do - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF +# Check for libm. +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for floor in -lm" >&5 +printf %s "checking for floor in -lm... " >&6; } +if test ${ac_cv_lib_m_floor+y} +then : + printf %s "(cached) " >&6 +else $as_nop + ac_check_lib_save_LIBS=$LIBS +LIBS="-lm $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -$ac_includes_default - typedef long long int ac__type_sizeof_; + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +char floor (); int -main () +main (void) { -static int test_array [1 - 2 * !(((long int) (sizeof (ac__type_sizeof_))) <= $ac_mid)]; -test_array [0] = 0 - +return floor (); ; return 0; } _ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then - ac_hi=$ac_mid; break -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 +if ac_fn_c_try_link "$LINENO" +then : + ac_cv_lib_m_floor=yes +else $as_nop + ac_cv_lib_m_floor=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_m_floor" >&5 +printf "%s\n" "$ac_cv_lib_m_floor" >&6; } +if test "x$ac_cv_lib_m_floor" = xyes +then : + printf "%s\n" "#define HAVE_LIBM 1" >>confdefs.h + + LIBS="-lm $LIBS" - ac_lo=`expr $ac_mid + 1` - if test $ac_lo -le $ac_mid; then - ac_lo= ac_hi= - break - fi - ac_mid=`expr 2 '*' $ac_mid + 1` fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - done -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF +# System libraries that may be required by WCSLIB itself. +# SunOS, extra maths functions. +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for cosd in -lsunmath" >&5 +printf %s "checking for cosd in -lsunmath... " >&6; } +if test ${ac_cv_lib_sunmath_cosd+y} +then : + printf %s "(cached) " >&6 +else $as_nop + ac_check_lib_save_LIBS=$LIBS +LIBS="-lsunmath $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -$ac_includes_default - typedef long long int ac__type_sizeof_; -int -main () -{ -static int test_array [1 - 2 * !(((long int) (sizeof (ac__type_sizeof_))) < 0)]; -test_array [0] = 0 - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then - ac_hi=-1 ac_mid=-1 - while :; do - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -$ac_includes_default - typedef long long int ac__type_sizeof_; +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +char cosd (); int -main () +main (void) { -static int test_array [1 - 2 * !(((long int) (sizeof (ac__type_sizeof_))) >= $ac_mid)]; -test_array [0] = 0 - +return cosd (); ; return 0; } _ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then - ac_lo=$ac_mid; break -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_hi=`expr '(' $ac_mid ')' - 1` - if test $ac_mid -le $ac_hi; then - ac_lo= ac_hi= - break - fi - ac_mid=`expr 2 '*' $ac_mid` +if ac_fn_c_try_link "$LINENO" +then : + ac_cv_lib_sunmath_cosd=yes +else $as_nop + ac_cv_lib_sunmath_cosd=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_sunmath_cosd" >&5 +printf "%s\n" "$ac_cv_lib_sunmath_cosd" >&6; } +if test "x$ac_cv_lib_sunmath_cosd" = xyes +then : + LIBS="-lsunmath $LIBS" fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - done -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - ac_lo= ac_hi= -fi +# See if we can find sincos(). +ac_fn_c_check_func "$LINENO" "sincos" "ac_cv_func_sincos" +if test "x$ac_cv_func_sincos" = xyes +then : + printf "%s\n" "#define HAVE_SINCOS 1" >>confdefs.h -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -# Binary search between lo and hi bounds. -while test "x$ac_lo" != "x$ac_hi"; do - ac_mid=`expr '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo` - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -$ac_includes_default - typedef long long int ac__type_sizeof_; -int -main () -{ -static int test_array [1 - 2 * !(((long int) (sizeof (ac__type_sizeof_))) <= $ac_mid)]; -test_array [0] = 0 - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then - ac_hi=$ac_mid -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 +# Check the size and availability of integer data types. +# The cast to long int works around a bug in the HP C Compiler +# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects +# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. +# This bug is HP SR number 8606223364. +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking size of int" >&5 +printf %s "checking size of int... " >&6; } +if test ${ac_cv_sizeof_int+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (int))" "ac_cv_sizeof_int" "$ac_includes_default" +then : + +else $as_nop + if test "$ac_cv_type_int" = yes; then + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error 77 "cannot compute sizeof (int) +See \`config.log' for more details" "$LINENO" 5; } + else + ac_cv_sizeof_int=0 + fi +fi - ac_lo=`expr '(' $ac_mid ')' + 1` fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_int" >&5 +printf "%s\n" "$ac_cv_sizeof_int" >&6; } -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -done -case $ac_lo in -?*) ac_cv_sizeof_long_long_int=$ac_lo;; -'') if test "$ac_cv_type_long_long_int" = yes; then - { { echo "$as_me:$LINENO: error: cannot compute sizeof (long long int) -See \`config.log' for more details." >&5 -echo "$as_me: error: cannot compute sizeof (long long int) -See \`config.log' for more details." >&2;} - { (exit 77); exit 77; }; } - else - ac_cv_sizeof_long_long_int=0 - fi ;; -esac -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -$ac_includes_default - typedef long long int ac__type_sizeof_; -static long int longval () { return (long int) (sizeof (ac__type_sizeof_)); } -static unsigned long int ulongval () { return (long int) (sizeof (ac__type_sizeof_)); } -#include -#include -int -main () -{ - FILE *f = fopen ("conftest.val", "w"); - if (! f) - return 1; - if (((long int) (sizeof (ac__type_sizeof_))) < 0) - { - long int i = longval (); - if (i != ((long int) (sizeof (ac__type_sizeof_)))) - return 1; - fprintf (f, "%ld\n", i); - } - else - { - unsigned long int i = ulongval (); - if (i != ((long int) (sizeof (ac__type_sizeof_)))) - return 1; - fprintf (f, "%lu\n", i); - } - return ferror (f) || fclose (f) != 0; - ; - return 0; -} -_ACEOF -rm -f conftest$ac_exeext -if { (ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_link") 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { ac_try='./conftest$ac_exeext' - { (case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_try") 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_cv_sizeof_long_long_int=`cat conftest.val` -else - echo "$as_me: program exited with status $ac_status" >&5 -echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 +printf "%s\n" "#define SIZEOF_INT $ac_cv_sizeof_int" >>confdefs.h -( exit $ac_status ) -if test "$ac_cv_type_long_long_int" = yes; then - { { echo "$as_me:$LINENO: error: cannot compute sizeof (long long int) -See \`config.log' for more details." >&5 -echo "$as_me: error: cannot compute sizeof (long long int) -See \`config.log' for more details." >&2;} - { (exit 77); exit 77; }; } + +# The cast to long int works around a bug in the HP C Compiler +# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects +# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. +# This bug is HP SR number 8606223364. +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking size of long int" >&5 +printf %s "checking size of long int... " >&6; } +if test ${ac_cv_sizeof_long_int+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (long int))" "ac_cv_sizeof_long_int" "$ac_includes_default" +then : + +else $as_nop + if test "$ac_cv_type_long_int" = yes; then + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error 77 "cannot compute sizeof (long int) +See \`config.log' for more details" "$LINENO" 5; } else - ac_cv_sizeof_long_long_int=0 + ac_cv_sizeof_long_int=0 fi fi -rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext + fi -rm -f conftest.val +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_long_int" >&5 +printf "%s\n" "$ac_cv_sizeof_long_int" >&6; } + + + +printf "%s\n" "#define SIZEOF_LONG_INT $ac_cv_sizeof_long_int" >>confdefs.h + + +# The cast to long int works around a bug in the HP C Compiler +# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects +# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. +# This bug is HP SR number 8606223364. +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking size of long long int" >&5 +printf %s "checking size of long long int... " >&6; } +if test ${ac_cv_sizeof_long_long_int+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (long long int))" "ac_cv_sizeof_long_long_int" "$ac_includes_default" +then : + +else $as_nop + if test "$ac_cv_type_long_long_int" = yes; then + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error 77 "cannot compute sizeof (long long int) +See \`config.log' for more details" "$LINENO" 5; } + else + ac_cv_sizeof_long_long_int=0 + fi +fi + fi -{ echo "$as_me:$LINENO: result: $ac_cv_sizeof_long_long_int" >&5 -echo "${ECHO_T}$ac_cv_sizeof_long_long_int" >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_long_long_int" >&5 +printf "%s\n" "$ac_cv_sizeof_long_long_int" >&6; } -cat >>confdefs.h <<_ACEOF -#define SIZEOF_LONG_LONG_INT $ac_cv_sizeof_long_long_int -_ACEOF +printf "%s\n" "#define SIZEOF_LONG_LONG_INT $ac_cv_sizeof_long_long_int" >>confdefs.h @@ -8334,46 +5343,35 @@ _ACEOF # gcc x86_64: 32 64 64 64 if test "x$ac_cv_sizeof_long_long_int" = x8; then -cat >>confdefs.h <<\_ACEOF -#define WCSLIB_INT64 long long int -_ACEOF +printf "%s\n" "#define WCSLIB_INT64 long long int" >>confdefs.h elif test "x$ac_cv_sizeof_long_int" = x8; then -cat >>confdefs.h <<\_ACEOF -#define WCSLIB_INT64 long int -_ACEOF +printf "%s\n" "#define WCSLIB_INT64 long int" >>confdefs.h elif test "x$ac_cv_sizeof_int" = x8; then -cat >>confdefs.h <<\_ACEOF -#define WCSLIB_INT64 int -_ACEOF +printf "%s\n" "#define WCSLIB_INT64 int" >>confdefs.h fi # Does printf() have the z modifier for size_t type? Important for 64-bit. -{ echo "$as_me:$LINENO: checking for printf z format modifier for size_t type" >&5 -echo $ECHO_N "checking for printf z format modifier for size_t type... $ECHO_C" >&6; } -if test "$cross_compiling" = yes; then +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for printf z format modifier for size_t type" >&5 +printf %s "checking for printf z format modifier for size_t type... " >&6; } +if test "$cross_compiling" = yes +then : -cat >>confdefs.h <<\_ACEOF -#define MODZ "" -_ACEOF +printf "%s\n" "#define MODZ \"\"" >>confdefs.h - { echo "$as_me:$LINENO: result: assumed not" >&5 -echo "${ECHO_T}assumed not" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: assumed not" >&5 +printf "%s\n" "assumed not" >&6; } -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF +else $as_nop + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_includes_default int -main () +main (void) { char buf[64]; if (sprintf(buf, "%zu", (size_t)1) != 1) @@ -8384,53 +5382,26 @@ char buf[64]; return 0; } _ACEOF -rm -f conftest$ac_exeext -if { (ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_link") 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { ac_try='./conftest$ac_exeext' - { (case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_try") 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - -cat >>confdefs.h <<\_ACEOF -#define MODZ "z" -_ACEOF +if ac_fn_c_try_run "$LINENO" +then : - { echo "$as_me:$LINENO: result: yes" >&5 -echo "${ECHO_T}yes" >&6; } -else - echo "$as_me: program exited with status $ac_status" >&5 -echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 +printf "%s\n" "#define MODZ \"z\"" >>confdefs.h -( exit $ac_status ) + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +printf "%s\n" "yes" >&6; } +else $as_nop -cat >>confdefs.h <<\_ACEOF -#define MODZ "" -_ACEOF +printf "%s\n" "#define MODZ \"\"" >>confdefs.h - { echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } fi -rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext fi - # Starting values, may be augmented later. SUBDIRS="C" TSTDIRS="C" @@ -8446,12 +5417,14 @@ INSTDIR="C" # configure --enable-fortran=no # F77=no configure ...bash # Check whether --enable-fortran was given. -if test "${enable_fortran+set}" = set; then +if test ${enable_fortran+y} +then : enableval=$enable_fortran; fi # Check whether --enable-fortran was given. -if test "${enable_fortran+set}" = set; then +if test ${enable_fortran+y} +then : enableval=$enable_fortran; fi @@ -8462,8 +5435,8 @@ fi if test "x$F77" = xno ; then F77= - { echo "$as_me:$LINENO: WARNING: Compilation of Fortran wrappers and PGSBOX disabled" >&5 -echo "$as_me: WARNING: Compilation of Fortran wrappers and PGSBOX disabled" >&2;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: Compilation of Fortran wrappers and PGSBOX disabled." >&5 +printf "%s\n" "$as_me: WARNING: Compilation of Fortran wrappers and PGSBOX disabled." >&2;} else if test "x$F77" = x ; then @@ -8478,11 +5451,12 @@ if test -n "$ac_tool_prefix"; then do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 -{ echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } -if test "${ac_cv_prog_F77+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_F77+y} +then : + printf %s "(cached) " >&6 +else $as_nop if test -n "$F77"; then ac_cv_prog_F77="$F77" # Let the user override the test. else @@ -8490,26 +5464,30 @@ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_F77="$ac_tool_prefix$ac_prog" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done -done + done IFS=$as_save_IFS fi fi F77=$ac_cv_prog_F77 if test -n "$F77"; then - { echo "$as_me:$LINENO: result: $F77" >&5 -echo "${ECHO_T}$F77" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $F77" >&5 +printf "%s\n" "$F77" >&6; } else - { echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } fi @@ -8523,11 +5501,12 @@ if test -z "$F77"; then do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 -{ echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } -if test "${ac_cv_prog_ac_ct_F77+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_ac_ct_F77+y} +then : + printf %s "(cached) " >&6 +else $as_nop if test -n "$ac_ct_F77"; then ac_cv_prog_ac_ct_F77="$ac_ct_F77" # Let the user override the test. else @@ -8535,26 +5514,30 @@ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_F77="$ac_prog" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done -done + done IFS=$as_save_IFS fi fi ac_ct_F77=$ac_cv_prog_ac_ct_F77 if test -n "$ac_ct_F77"; then - { echo "$as_me:$LINENO: result: $ac_ct_F77" >&5 -echo "${ECHO_T}$ac_ct_F77" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_F77" >&5 +printf "%s\n" "$ac_ct_F77" >&6; } else - { echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } fi @@ -8566,12 +5549,8 @@ done else case $cross_compiling:$ac_tool_warned in yes:) -{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools -whose name does not start with the host triplet. If you think this -configuration is useful to you, please write to autoconf@gnu.org." >&5 -echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools -whose name does not start with the host triplet. If you think this -configuration is useful to you, please write to autoconf@gnu.org." >&2;} +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac F77=$ac_ct_F77 @@ -8580,50 +5559,42 @@ fi # Provide some information about the compiler. -echo "$as_me:$LINENO: checking for Fortran 77 compiler version" >&5 -ac_compiler=`set X $ac_compile; echo $2` -{ (ac_try="$ac_compiler --version >&5" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compiler --version >&5") 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } -{ (ac_try="$ac_compiler -v >&5" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compiler -v >&5") 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } -{ (ac_try="$ac_compiler -V >&5" +printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for Fortran 77 compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +for ac_option in --version -v -V -qversion; do + { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compiler -V >&5") 2>&5 +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +printf "%s\n" "$ac_try_echo"; } >&5 + (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } + if test -s conftest.err; then + sed '10a\ +... rest of stderr output deleted ... + 10q' conftest.err >conftest.er1 + cat conftest.er1 >&5 + fi + rm -f conftest.er1 conftest.err + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +done rm -f a.out # If we don't use `.F' as extension, the preprocessor is not run on the # input file. (Note that this only needs to work for GNU compilers.) ac_save_ext=$ac_ext ac_ext=F -{ echo "$as_me:$LINENO: checking whether we are using the GNU Fortran 77 compiler" >&5 -echo $ECHO_N "checking whether we are using the GNU Fortran 77 compiler... $ECHO_C" >&6; } -if test "${ac_cv_f77_compiler_gnu+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - cat >conftest.$ac_ext <<_ACEOF +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports GNU Fortran 77" >&5 +printf %s "checking whether the compiler supports GNU Fortran 77... " >&6; } +if test ${ac_cv_f77_compiler_gnu+y} +then : + printf %s "(cached) " >&6 +else $as_nop + cat > conftest.$ac_ext <<_ACEOF program main #ifndef __GNUC__ choke me @@ -8631,83 +5602,48 @@ else end _ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_f77_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then +if ac_fn_f77_try_compile "$LINENO" +then : ac_compiler_gnu=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_compiler_gnu=no +else $as_nop + ac_compiler_gnu=no fi - -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ac_cv_f77_compiler_gnu=$ac_compiler_gnu fi -{ echo "$as_me:$LINENO: result: $ac_cv_f77_compiler_gnu" >&5 -echo "${ECHO_T}$ac_cv_f77_compiler_gnu" >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_f77_compiler_gnu" >&5 +printf "%s\n" "$ac_cv_f77_compiler_gnu" >&6; } +ac_compiler_gnu=$ac_cv_f77_compiler_gnu + ac_ext=$ac_save_ext -ac_test_FFLAGS=${FFLAGS+set} +ac_test_FFLAGS=${FFLAGS+y} ac_save_FFLAGS=$FFLAGS FFLAGS= -{ echo "$as_me:$LINENO: checking whether $F77 accepts -g" >&5 -echo $ECHO_N "checking whether $F77 accepts -g... $ECHO_C" >&6; } -if test "${ac_cv_prog_f77_g+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $F77 accepts -g" >&5 +printf %s "checking whether $F77 accepts -g... " >&6; } +if test ${ac_cv_prog_f77_g+y} +then : + printf %s "(cached) " >&6 +else $as_nop FFLAGS=-g -cat >conftest.$ac_ext <<_ACEOF +cat > conftest.$ac_ext <<_ACEOF program main end _ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_f77_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then +if ac_fn_f77_try_compile "$LINENO" +then : ac_cv_prog_f77_g=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_cv_prog_f77_g=no +else $as_nop + ac_cv_prog_f77_g=no fi - -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi -{ echo "$as_me:$LINENO: result: $ac_cv_prog_f77_g" >&5 -echo "${ECHO_T}$ac_cv_prog_f77_g" >&6; } -if test "$ac_test_FFLAGS" = set; then +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_f77_g" >&5 +printf "%s\n" "$ac_cv_prog_f77_g" >&6; } +if test $ac_test_FFLAGS; then FFLAGS=$ac_save_FFLAGS elif test $ac_cv_prog_f77_g = yes; then if test "x$ac_cv_f77_compiler_gnu" = xyes; then @@ -8723,7 +5659,11 @@ else fi fi -G77=`test $ac_compiler_gnu = yes && echo yes` +if test $ac_compiler_gnu = yes; then + G77=yes +else + G77= +fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' @@ -8733,20 +5673,18 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu fi if test "x$F77" = x; then - { echo "$as_me:$LINENO: WARNING: + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: ------------------------------------------------------------------ Fortran compiler not found, will skip Fortran wrappers and PGSBOX. ------------------------------------------------------------------" >&5 -echo "$as_me: WARNING: +printf "%s\n" "$as_me: WARNING: ------------------------------------------------------------------ Fortran compiler not found, will skip Fortran wrappers and PGSBOX. ------------------------------------------------------------------" >&2;} # Best guess at Fortran name mangling for use if a compiler does ever # become available. - cat >>confdefs.h <<\_ACEOF -#define F77_FUNC(name,NAME) name ## _ -_ACEOF + printf "%s\n" "#define F77_FUNC(name,NAME) name ## _" >>confdefs.h else @@ -8757,8 +5695,8 @@ _ACEOF fi fi - { echo "$as_me:$LINENO: checking whether $F77 accepts -I" >&5 -echo $ECHO_N "checking whether $F77 accepts -I... $ECHO_C" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $F77 accepts -I" >&5 +printf %s "checking whether $F77 accepts -I... " >&6; } ac_ext=f ac_compile='$F77 -c $FFLAGS conftest.$ac_ext >&5' ac_link='$F77 -o conftest$ac_exeext $FFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' @@ -8767,39 +5705,20 @@ ac_compiler_gnu=$ac_cv_f77_compiler_gnu FFLAGS_save=$FFLAGS FFLAGS=-I. -cat >conftest.$ac_ext <<_ACEOF +cat > conftest.$ac_ext <<_ACEOF program main end _ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_f77_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then - FFLAGS="$FFLAGS_save -I."; { echo "$as_me:$LINENO: result: yes" >&5 -echo "${ECHO_T}yes" >&6; } -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - FFLAGS="$FFLAGS_save"; { echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6; } +if ac_fn_f77_try_compile "$LINENO" +then : + FFLAGS="$FFLAGS_save -I."; { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +printf "%s\n" "yes" >&6; } +else $as_nop + FFLAGS="$FFLAGS_save"; { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } fi - -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' @@ -8814,37 +5733,23 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_compile='$F77 -c $FFLAGS conftest.$ac_ext >&5' ac_link='$F77 -o conftest$ac_exeext $FFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_f77_compiler_gnu -{ echo "$as_me:$LINENO: checking how to get verbose linking output from $F77" >&5 -echo $ECHO_N "checking how to get verbose linking output from $F77... $ECHO_C" >&6; } -if test "${ac_cv_prog_f77_v+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - cat >conftest.$ac_ext <<_ACEOF +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking how to get verbose linking output from $F77" >&5 +printf %s "checking how to get verbose linking output from $F77... " >&6; } +if test ${ac_cv_prog_f77_v+y} +then : + printf %s "(cached) " >&6 +else $as_nop + cat > conftest.$ac_ext <<_ACEOF program main end _ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_f77_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then +if ac_fn_f77_try_compile "$LINENO" +then : ac_cv_prog_f77_v= # Try some options frequently used verbose output for ac_verb in -v -verbose --verbose -V -\#\#\#; do - cat >conftest.$ac_ext <<_ACEOF + cat > conftest.$ac_ext <<_ACEOF program main end @@ -8858,27 +5763,38 @@ ac_save_FFLAGS=$FFLAGS FFLAGS="$FFLAGS $ac_verb" eval "set x $ac_link" shift -echo "$as_me:$LINENO: $*" >&5 -ac_f77_v_output=`eval $ac_link 5>&1 2>&1 | grep -v 'Driving:'` -echo "$ac_f77_v_output" >&5 +printf "%s\n" "$as_me:${as_lineno-$LINENO}: $*" >&5 +# gfortran 4.3 outputs lines setting COLLECT_GCC_OPTIONS, COMPILER_PATH, +# LIBRARY_PATH; skip all such settings. +ac_f77_v_output=`eval $ac_link 5>&1 2>&1 | + sed '/^Driving:/d; /^Configured with:/d; + '"/^[_$as_cr_Letters][_$as_cr_alnum]*=/d"` +printf "%s\n" "$ac_f77_v_output" >&5 FFLAGS=$ac_save_FFLAGS -rm -f -r conftest* +rm -rf conftest* # On HP/UX there is a line like: "LPATH is: /foo:/bar:/baz" where # /foo, /bar, and /baz are search directories for the Fortran linker. # Here, we change these into -L/foo -L/bar -L/baz (and put it first): ac_f77_v_output="`echo $ac_f77_v_output | grep 'LPATH is:' | - sed 's,.*LPATH is\(: *[^ ]*\).*,\1,;s,: */, -L/,g'` $ac_f77_v_output" + sed 's|.*LPATH is\(: *[^ ]*\).*|\1|;s|: */| -L/|g'` $ac_f77_v_output" # FIXME: we keep getting bitten by quoted arguments; a more general fix # that detects unbalanced quotes in FLIBS should be implemented # and (ugh) tested at some point. case $ac_f77_v_output in - # If we are using xlf then replace all the commas with spaces. + # With xlf replace commas with spaces, + # and remove "-link" and closing parenthesis. *xlfentry*) - ac_f77_v_output=`echo $ac_f77_v_output | sed 's/,/ /g'` ;; + ac_f77_v_output=`echo $ac_f77_v_output | + sed ' + s/,/ /g + s/ -link / /g + s/) *$// + ' + ` ;; # With Intel ifc, ignore the quoted -mGLOB_options_string stuff (quoted # $LIBS confuse us, and the libraries appear later in the output anyway). @@ -8890,9 +5806,19 @@ case $ac_f77_v_output in # Doubly-quoted arguments were reported for "PGF90/x86 Linux/x86 5.0-2". *-cmdline\ * | *-ignore\ * | *-def\ *) ac_f77_v_output=`echo $ac_f77_v_output | sed "\ - s/-cmdline *'[^']*'/ /g; s/-cmdline *\"[^\"]*\"/ /g - s/-ignore *'[^']*'/ /g; s/-ignore *\"[^\"]*\"/ /g - s/-def *'[^']*'/ /g; s/-def *\"[^\"]*\"/ /g"` ;; + s/-cmdline *'[^']*'/ /g; s/-cmdline *\"[^\"]*\"/ /g + s/-ignore *'[^']*'/ /g; s/-ignore *\"[^\"]*\"/ /g + s/-def *'[^']*'/ /g; s/-def *\"[^\"]*\"/ /g"` ;; + + # If we are using fort77 (the f2c wrapper) then filter output and delete quotes. + *fort77*f2c*gcc*) + ac_f77_v_output=`echo "$ac_f77_v_output" | sed -n ' + /:[ ]\+Running[ ]\{1,\}"gcc"/{ + /"-c"/d + /[.]c"*/d + s/^.*"gcc"/"gcc"/ + s/"//gp + }'` ;; # If we are using Cray Fortran then delete quotes. *cft90*) @@ -8903,39 +5829,36 @@ esac # look for -l* and *.a constructs in the output for ac_arg in $ac_f77_v_output; do case $ac_arg in - [\\/]*.a | ?:[\\/]*.a | -[lLRu]*) - ac_cv_prog_f77_v=$ac_verb - break 2 ;; + [\\/]*.a | ?:[\\/]*.a | -[lLRu]*) + ac_cv_prog_f77_v=$ac_verb + break 2 ;; esac done done if test -z "$ac_cv_prog_f77_v"; then - { echo "$as_me:$LINENO: WARNING: cannot determine how to obtain linking information from $F77" >&5 -echo "$as_me: WARNING: cannot determine how to obtain linking information from $F77" >&2;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cannot determine how to obtain linking information from $F77" >&5 +printf "%s\n" "$as_me: WARNING: cannot determine how to obtain linking information from $F77" >&2;} fi -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - { echo "$as_me:$LINENO: WARNING: compilation failed" >&5 -echo "$as_me: WARNING: compilation failed" >&2;} +else $as_nop + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: compilation failed" >&5 +printf "%s\n" "$as_me: WARNING: compilation failed" >&2;} fi - -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi -{ echo "$as_me:$LINENO: result: $ac_cv_prog_f77_v" >&5 -echo "${ECHO_T}$ac_cv_prog_f77_v" >&6; } -{ echo "$as_me:$LINENO: checking for Fortran 77 libraries of $F77" >&5 -echo $ECHO_N "checking for Fortran 77 libraries of $F77... $ECHO_C" >&6; } -if test "${ac_cv_f77_libs+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_f77_v" >&5 +printf "%s\n" "$ac_cv_prog_f77_v" >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for Fortran 77 libraries of $F77" >&5 +printf %s "checking for Fortran 77 libraries of $F77... " >&6; } +if test ${ac_cv_f77_libs+y} +then : + printf %s "(cached) " >&6 +else $as_nop if test "x$FLIBS" != "x"; then ac_cv_f77_libs="$FLIBS" # Let the user override the test. else -cat >conftest.$ac_ext <<_ACEOF +cat > conftest.$ac_ext <<_ACEOF program main end @@ -8949,27 +5872,38 @@ ac_save_FFLAGS=$FFLAGS FFLAGS="$FFLAGS $ac_cv_prog_f77_v" eval "set x $ac_link" shift -echo "$as_me:$LINENO: $*" >&5 -ac_f77_v_output=`eval $ac_link 5>&1 2>&1 | grep -v 'Driving:'` -echo "$ac_f77_v_output" >&5 +printf "%s\n" "$as_me:${as_lineno-$LINENO}: $*" >&5 +# gfortran 4.3 outputs lines setting COLLECT_GCC_OPTIONS, COMPILER_PATH, +# LIBRARY_PATH; skip all such settings. +ac_f77_v_output=`eval $ac_link 5>&1 2>&1 | + sed '/^Driving:/d; /^Configured with:/d; + '"/^[_$as_cr_Letters][_$as_cr_alnum]*=/d"` +printf "%s\n" "$ac_f77_v_output" >&5 FFLAGS=$ac_save_FFLAGS -rm -f -r conftest* +rm -rf conftest* # On HP/UX there is a line like: "LPATH is: /foo:/bar:/baz" where # /foo, /bar, and /baz are search directories for the Fortran linker. # Here, we change these into -L/foo -L/bar -L/baz (and put it first): ac_f77_v_output="`echo $ac_f77_v_output | grep 'LPATH is:' | - sed 's,.*LPATH is\(: *[^ ]*\).*,\1,;s,: */, -L/,g'` $ac_f77_v_output" + sed 's|.*LPATH is\(: *[^ ]*\).*|\1|;s|: */| -L/|g'` $ac_f77_v_output" # FIXME: we keep getting bitten by quoted arguments; a more general fix # that detects unbalanced quotes in FLIBS should be implemented # and (ugh) tested at some point. case $ac_f77_v_output in - # If we are using xlf then replace all the commas with spaces. + # With xlf replace commas with spaces, + # and remove "-link" and closing parenthesis. *xlfentry*) - ac_f77_v_output=`echo $ac_f77_v_output | sed 's/,/ /g'` ;; + ac_f77_v_output=`echo $ac_f77_v_output | + sed ' + s/,/ /g + s/ -link / /g + s/) *$// + ' + ` ;; # With Intel ifc, ignore the quoted -mGLOB_options_string stuff (quoted # $LIBS confuse us, and the libraries appear later in the output anyway). @@ -8981,9 +5915,19 @@ case $ac_f77_v_output in # Doubly-quoted arguments were reported for "PGF90/x86 Linux/x86 5.0-2". *-cmdline\ * | *-ignore\ * | *-def\ *) ac_f77_v_output=`echo $ac_f77_v_output | sed "\ - s/-cmdline *'[^']*'/ /g; s/-cmdline *\"[^\"]*\"/ /g - s/-ignore *'[^']*'/ /g; s/-ignore *\"[^\"]*\"/ /g - s/-def *'[^']*'/ /g; s/-def *\"[^\"]*\"/ /g"` ;; + s/-cmdline *'[^']*'/ /g; s/-cmdline *\"[^\"]*\"/ /g + s/-ignore *'[^']*'/ /g; s/-ignore *\"[^\"]*\"/ /g + s/-def *'[^']*'/ /g; s/-def *\"[^\"]*\"/ /g"` ;; + + # If we are using fort77 (the f2c wrapper) then filter output and delete quotes. + *fort77*f2c*gcc*) + ac_f77_v_output=`echo "$ac_f77_v_output" | sed -n ' + /:[ ]\+Running[ ]\{1,\}"gcc"/{ + /"-c"/d + /[.]c"*/d + s/^.*"gcc"/"gcc"/ + s/"//gp + }'` ;; # If we are using Cray Fortran then delete quotes. *cft90*) @@ -9002,8 +5946,8 @@ while test $# != 1; do shift ac_arg=$1 case $ac_arg in - [\\/]*.a | ?:[\\/]*.a) - ac_exists=false + [\\/]*.a | ?:[\\/]*.a) + ac_exists=false for ac_i in $ac_cv_f77_libs; do if test x"$ac_arg" = x"$ac_i"; then ac_exists=true @@ -9011,15 +5955,15 @@ while test $# != 1; do fi done - if test x"$ac_exists" = xtrue; then - : -else + if test x"$ac_exists" = xtrue +then : + +else $as_nop ac_cv_f77_libs="$ac_cv_f77_libs $ac_arg" fi - - ;; - -bI:*) - ac_exists=false + ;; + -bI:*) + ac_exists=false for ac_i in $ac_cv_f77_libs; do if test x"$ac_arg" = x"$ac_i"; then ac_exists=true @@ -9027,9 +5971,10 @@ fi fi done - if test x"$ac_exists" = xtrue; then - : -else + if test x"$ac_exists" = xtrue +then : + +else $as_nop if test "$ac_compiler_gnu" = yes; then for ac_link_opt in $ac_arg; do ac_cv_f77_libs="$ac_cv_f77_libs -Xlinker $ac_link_opt" @@ -9038,18 +5983,37 @@ else ac_cv_f77_libs="$ac_cv_f77_libs $ac_arg" fi fi + ;; + # Ignore these flags. + -lang* | -lcrt*.o | -lc | -lgcc* | -lSystem | -libmil | -little \ + |-LANG:=* | -LIST:* | -LNO:* | -link) + ;; + -lkernel32) + # Ignore this library only on Windows-like systems. + case $host_os in + cygwin* | msys* ) ;; + *) + ac_exists=false + for ac_i in $ac_cv_f77_libs; do + if test x"$ac_arg" = x"$ac_i"; then + ac_exists=true + break + fi + done - ;; - # Ignore these flags. - -lang* | -lcrt*.o | -lc | -lgcc* | -lSystem | -libmil | -LANG:=* | -LIST:* | -LNO:*) - ;; - -lkernel32) - test x"$CYGWIN" != xyes && ac_cv_f77_libs="$ac_cv_f77_libs $ac_arg" - ;; - -[LRuYz]) - # These flags, when seen by themselves, take an argument. - # We remove the space between option and argument and re-iterate - # unless we find an empty arg or a new option (starting with -) + if test x"$ac_exists" = xtrue +then : + +else $as_nop + ac_cv_f77_libs="$ac_cv_f77_libs $ac_arg" +fi + ;; + esac + ;; + -[LRuYz]) + # These flags, when seen by themselves, take an argument. + # We remove the space between option and argument and re-iterate + # unless we find an empty arg or a new option (starting with -) case $2 in "" | -*);; *) @@ -9058,10 +6022,10 @@ fi set X $ac_arg "$@" ;; esac - ;; - -YP,*) - for ac_j in `echo $ac_arg | sed -e 's/-YP,/-L/;s/:/ -L/g'`; do - ac_exists=false + ;; + -YP,*) + for ac_j in `printf "%s\n" "$ac_arg" | sed -e 's/-YP,/-L/;s/:/ -L/g'`; do + ac_exists=false for ac_i in $ac_cv_f77_libs; do if test x"$ac_j" = x"$ac_i"; then ac_exists=true @@ -9069,17 +6033,17 @@ fi fi done - if test x"$ac_exists" = xtrue; then - : -else + if test x"$ac_exists" = xtrue +then : + +else $as_nop ac_arg="$ac_arg $ac_j" - ac_cv_f77_libs="$ac_cv_f77_libs $ac_j" + ac_cv_f77_libs="$ac_cv_f77_libs $ac_j" fi - - done - ;; - -[lLR]*) - ac_exists=false + done + ;; + -[lLR]*) + ac_exists=false for ac_i in $ac_cv_f77_libs; do if test x"$ac_arg" = x"$ac_i"; then ac_exists=true @@ -9087,17 +6051,18 @@ fi fi done - if test x"$ac_exists" = xtrue; then - : -else + if test x"$ac_exists" = xtrue +then : + +else $as_nop ac_cv_f77_libs="$ac_cv_f77_libs $ac_arg" fi - - ;; + ;; -zallextract*| -zdefaultextract) ac_cv_f77_libs="$ac_cv_f77_libs $ac_arg" ;; - # Ignore everything else. + -mllvm) ${2+shift};; # Defend against 'clang -mllvm -loopopt=0'. + # Ignore everything else. esac done # restore positional arguments @@ -9108,10 +6073,10 @@ set X $ac_save_positional; shift # must begin with a "/"). case `(uname -sr) 2>/dev/null` in "SunOS 5"*) - ac_ld_run_path=`echo $ac_f77_v_output | - sed -n 's,^.*LD_RUN_PATH *= *\(/[^ ]*\).*$,-R\1,p'` + ac_ld_run_path=`printf "%s\n" "$ac_f77_v_output" | + sed -n 's,^.*LD_RUN_PATH *= *\(/[^ ]*\).*$,-R\1,p'` test "x$ac_ld_run_path" != x && - if test "$ac_compiler_gnu" = yes; then + if test "$ac_compiler_gnu" = yes; then for ac_link_opt in $ac_ld_run_path; do ac_cv_f77_libs="$ac_cv_f77_libs -Xlinker $ac_link_opt" done @@ -9123,8 +6088,8 @@ esac fi # test "x$[]_AC_LANG_PREFIX[]LIBS" = "x" fi -{ echo "$as_me:$LINENO: result: $ac_cv_f77_libs" >&5 -echo "${ECHO_T}$ac_cv_f77_libs" >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_f77_libs" >&5 +printf "%s\n" "$ac_cv_f77_libs" >&6; } FLIBS="$ac_cv_f77_libs" @@ -9135,17 +6100,39 @@ ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $ ac_compiler_gnu=$ac_cv_c_compiler_gnu + # Tidy up FLIBS. + dirs= + libs= + for flib in $FLIBS + do + case "$flib" in + -L*) + dir=`echo "$flib" | sed -e 's/-L//'` + dir=-L`cd "$dir" && pwd` + dirs="$dirs $dir" + ;; + *) + libs="$libs $flib" + ;; + esac + done + + dirs=`for dir in $dirs ; do echo "$dir" ; done | sort -u | xargs` + + FLIBS="$dirs$libs" + # F77 name mangling (defines the F77_FUNC preprocessor macro). ac_ext=f ac_compile='$F77 -c $FFLAGS conftest.$ac_ext >&5' ac_link='$F77 -o conftest$ac_exeext $FFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_f77_compiler_gnu -{ echo "$as_me:$LINENO: checking for dummy main to link with Fortran 77 libraries" >&5 -echo $ECHO_N "checking for dummy main to link with Fortran 77 libraries... $ECHO_C" >&6; } -if test "${ac_cv_f77_dummy_main+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for dummy main to link with Fortran 77 libraries" >&5 +printf %s "checking for dummy main to link with Fortran 77 libraries... " >&6; } +if test ${ac_cv_f77_dummy_main+y} +then : + printf %s "(cached) " >&6 +else $as_nop ac_f77_dm_save_LIBS=$LIBS LIBS="$LIBS $FLIBS" ac_fortran_dm_var=F77_DUMMY_MAIN @@ -9156,11 +6143,7 @@ ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $ ac_compiler_gnu=$ac_cv_c_compiler_gnu # First, try linking without a dummy main: - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef F77_DUMMY_MAIN @@ -9172,49 +6155,25 @@ cat >>conftest.$ac_ext <<_ACEOF #endif int -main () +main (void) { ; return 0; } _ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_link") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest$ac_exeext && - $as_test_x conftest$ac_exeext; then +if ac_fn_c_try_link "$LINENO" +then : ac_cv_fortran_dummy_main=none -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_cv_fortran_dummy_main=unknown +else $as_nop + ac_cv_fortran_dummy_main=unknown fi - -rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ - conftest$ac_exeext conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam \ + conftest$ac_exeext conftest.$ac_ext if test $ac_cv_fortran_dummy_main = unknown; then for ac_func in MAIN__ MAIN_ __main MAIN _MAIN __MAIN main_ main__ _main; do - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #define $ac_fortran_dm_var $ac_func #ifdef F77_DUMMY_MAIN @@ -9226,41 +6185,19 @@ cat >>conftest.$ac_ext <<_ACEOF #endif int -main () +main (void) { ; return 0; } _ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_link") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest$ac_exeext && - $as_test_x conftest$ac_exeext; then +if ac_fn_c_try_link "$LINENO" +then : ac_cv_fortran_dummy_main=$ac_func; break -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - fi - -rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ - conftest$ac_exeext conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam \ + conftest$ac_exeext conftest.$ac_ext done fi ac_ext=f @@ -9268,37 +6205,32 @@ ac_compile='$F77 -c $FFLAGS conftest.$ac_ext >&5' ac_link='$F77 -o conftest$ac_exeext $FFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_f77_compiler_gnu ac_cv_f77_dummy_main=$ac_cv_fortran_dummy_main - rm -f -r conftest* + rm -rf conftest* LIBS=$ac_f77_dm_save_LIBS fi -{ echo "$as_me:$LINENO: result: $ac_cv_f77_dummy_main" >&5 -echo "${ECHO_T}$ac_cv_f77_dummy_main" >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_f77_dummy_main" >&5 +printf "%s\n" "$ac_cv_f77_dummy_main" >&6; } F77_DUMMY_MAIN=$ac_cv_f77_dummy_main -if test "$F77_DUMMY_MAIN" != unknown; then +if test "$F77_DUMMY_MAIN" != unknown +then : if test $F77_DUMMY_MAIN != none; then -cat >>confdefs.h <<_ACEOF -#define F77_DUMMY_MAIN $F77_DUMMY_MAIN -_ACEOF +printf "%s\n" "#define F77_DUMMY_MAIN $F77_DUMMY_MAIN" >>confdefs.h if test "x$ac_cv_fc_dummy_main" = "x$ac_cv_f77_dummy_main"; then -cat >>confdefs.h <<\_ACEOF -#define FC_DUMMY_MAIN_EQ_F77 1 -_ACEOF +printf "%s\n" "#define FC_DUMMY_MAIN_EQ_F77 1" >>confdefs.h fi fi -else - { { echo "$as_me:$LINENO: error: linking to Fortran libraries from C fails -See \`config.log' for more details." >&5 -echo "$as_me: error: linking to Fortran libraries from C fails -See \`config.log' for more details." >&2;} - { (exit 1); exit 1; }; } +else $as_nop + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "linking to Fortran libraries from C fails +See \`config.log' for more details" "$LINENO" 5; } fi - ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' @@ -9309,12 +6241,13 @@ ac_ext=f ac_compile='$F77 -c $FFLAGS conftest.$ac_ext >&5' ac_link='$F77 -o conftest$ac_exeext $FFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_f77_compiler_gnu -{ echo "$as_me:$LINENO: checking for Fortran 77 name-mangling scheme" >&5 -echo $ECHO_N "checking for Fortran 77 name-mangling scheme... $ECHO_C" >&6; } -if test "${ac_cv_f77_mangling+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - cat >conftest.$ac_ext <<_ACEOF +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for Fortran 77 name-mangling scheme" >&5 +printf %s "checking for Fortran 77 name-mangling scheme... " >&6; } +if test ${ac_cv_f77_mangling+y} +then : + printf %s "(cached) " >&6 +else $as_nop + cat > conftest.$ac_ext <<_ACEOF subroutine foobar() return end @@ -9322,23 +6255,8 @@ else return end _ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_f77_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then +if ac_fn_f77_try_compile "$LINENO" +then : mv conftest.$ac_objext cfortran_test.$ac_objext ac_save_LIBS=$LIBS @@ -9353,19 +6271,12 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu for ac_foobar in foobar FOOBAR; do for ac_underscore in "" "_"; do ac_func="$ac_foobar$ac_underscore" - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif char $ac_func (); #ifdef F77_DUMMY_MAIN @@ -9376,41 +6287,19 @@ char $ac_func (); #endif int -main () +main (void) { return $ac_func (); ; return 0; } _ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_link") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest$ac_exeext && - $as_test_x conftest$ac_exeext; then +if ac_fn_c_try_link "$LINENO" +then : ac_success=yes; break 2 -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - fi - -rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ - conftest$ac_exeext conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam \ + conftest$ac_exeext conftest.$ac_ext done done ac_ext=f @@ -9438,19 +6327,12 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_success_extra=no for ac_extra in "" "_"; do ac_func="$ac_foo_bar$ac_underscore$ac_extra" - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif char $ac_func (); #ifdef F77_DUMMY_MAIN @@ -9461,41 +6343,19 @@ char $ac_func (); #endif int -main () +main (void) { return $ac_func (); ; return 0; } _ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_link") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest$ac_exeext && - $as_test_x conftest$ac_exeext; then +if ac_fn_c_try_link "$LINENO" +then : ac_success_extra=yes; break -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - fi - -rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ - conftest$ac_exeext conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam \ + conftest$ac_exeext conftest.$ac_ext done ac_ext=f ac_compile='$F77 -c $FFLAGS conftest.$ac_ext >&5' @@ -9504,16 +6364,16 @@ ac_compiler_gnu=$ac_cv_f77_compiler_gnu if test "$ac_success_extra" = "yes"; then ac_cv_f77_mangling="$ac_case case" - if test -z "$ac_underscore"; then - ac_cv_f77_mangling="$ac_cv_f77_mangling, no underscore" + if test -z "$ac_underscore"; then + ac_cv_f77_mangling="$ac_cv_f77_mangling, no underscore" else - ac_cv_f77_mangling="$ac_cv_f77_mangling, underscore" - fi - if test -z "$ac_extra"; then - ac_cv_f77_mangling="$ac_cv_f77_mangling, no extra underscore" + ac_cv_f77_mangling="$ac_cv_f77_mangling, underscore" + fi + if test -z "$ac_extra"; then + ac_cv_f77_mangling="$ac_cv_f77_mangling, no extra underscore" else - ac_cv_f77_mangling="$ac_cv_f77_mangling, extra underscore" - fi + ac_cv_f77_mangling="$ac_cv_f77_mangling, extra underscore" + fi else ac_cv_f77_mangling="unknown" fi @@ -9522,23 +6382,19 @@ ac_compiler_gnu=$ac_cv_f77_compiler_gnu fi LIBS=$ac_save_LIBS - rm -f -r cfortran_test* conftest* -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - { { echo "$as_me:$LINENO: error: cannot compile a simple Fortran program -See \`config.log' for more details." >&5 -echo "$as_me: error: cannot compile a simple Fortran program -See \`config.log' for more details." >&2;} - { (exit 1); exit 1; }; } + rm -rf conftest* + rm -f cfortran_test* +else $as_nop + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot compile a simple Fortran program +See \`config.log' for more details" "$LINENO" 5; } fi - -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi -{ echo "$as_me:$LINENO: result: $ac_cv_f77_mangling" >&5 -echo "${ECHO_T}$ac_cv_f77_mangling" >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_f77_mangling" >&5 +printf "%s\n" "$ac_cv_f77_mangling" >&6; } ac_ext=c ac_cpp='$CPP $CPPFLAGS' @@ -9550,85 +6406,51 @@ ac_ext=f ac_compile='$F77 -c $FFLAGS conftest.$ac_ext >&5' ac_link='$F77 -o conftest$ac_exeext $FFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_f77_compiler_gnu - - case $ac_cv_f77_mangling in "lower case, no underscore, no extra underscore") - cat >>confdefs.h <<\_ACEOF -#define F77_FUNC(name,NAME) name -_ACEOF + printf "%s\n" "#define F77_FUNC(name,NAME) name" >>confdefs.h - cat >>confdefs.h <<\_ACEOF -#define F77_FUNC_(name,NAME) name -_ACEOF + printf "%s\n" "#define F77_FUNC_(name,NAME) name" >>confdefs.h ;; "lower case, no underscore, extra underscore") - cat >>confdefs.h <<\_ACEOF -#define F77_FUNC(name,NAME) name -_ACEOF + printf "%s\n" "#define F77_FUNC(name,NAME) name" >>confdefs.h - cat >>confdefs.h <<\_ACEOF -#define F77_FUNC_(name,NAME) name ## _ -_ACEOF + printf "%s\n" "#define F77_FUNC_(name,NAME) name ## _" >>confdefs.h ;; "lower case, underscore, no extra underscore") - cat >>confdefs.h <<\_ACEOF -#define F77_FUNC(name,NAME) name ## _ -_ACEOF + printf "%s\n" "#define F77_FUNC(name,NAME) name ## _" >>confdefs.h - cat >>confdefs.h <<\_ACEOF -#define F77_FUNC_(name,NAME) name ## _ -_ACEOF + printf "%s\n" "#define F77_FUNC_(name,NAME) name ## _" >>confdefs.h ;; "lower case, underscore, extra underscore") - cat >>confdefs.h <<\_ACEOF -#define F77_FUNC(name,NAME) name ## _ -_ACEOF + printf "%s\n" "#define F77_FUNC(name,NAME) name ## _" >>confdefs.h - cat >>confdefs.h <<\_ACEOF -#define F77_FUNC_(name,NAME) name ## __ -_ACEOF + printf "%s\n" "#define F77_FUNC_(name,NAME) name ## __" >>confdefs.h ;; "upper case, no underscore, no extra underscore") - cat >>confdefs.h <<\_ACEOF -#define F77_FUNC(name,NAME) NAME -_ACEOF + printf "%s\n" "#define F77_FUNC(name,NAME) NAME" >>confdefs.h - cat >>confdefs.h <<\_ACEOF -#define F77_FUNC_(name,NAME) NAME -_ACEOF + printf "%s\n" "#define F77_FUNC_(name,NAME) NAME" >>confdefs.h ;; "upper case, no underscore, extra underscore") - cat >>confdefs.h <<\_ACEOF -#define F77_FUNC(name,NAME) NAME -_ACEOF + printf "%s\n" "#define F77_FUNC(name,NAME) NAME" >>confdefs.h - cat >>confdefs.h <<\_ACEOF -#define F77_FUNC_(name,NAME) NAME ## _ -_ACEOF + printf "%s\n" "#define F77_FUNC_(name,NAME) NAME ## _" >>confdefs.h ;; "upper case, underscore, no extra underscore") - cat >>confdefs.h <<\_ACEOF -#define F77_FUNC(name,NAME) NAME ## _ -_ACEOF + printf "%s\n" "#define F77_FUNC(name,NAME) NAME ## _" >>confdefs.h - cat >>confdefs.h <<\_ACEOF -#define F77_FUNC_(name,NAME) NAME ## _ -_ACEOF + printf "%s\n" "#define F77_FUNC_(name,NAME) NAME ## _" >>confdefs.h ;; "upper case, underscore, extra underscore") - cat >>confdefs.h <<\_ACEOF -#define F77_FUNC(name,NAME) NAME ## _ -_ACEOF + printf "%s\n" "#define F77_FUNC(name,NAME) NAME ## _" >>confdefs.h - cat >>confdefs.h <<\_ACEOF -#define F77_FUNC_(name,NAME) NAME ## __ -_ACEOF + printf "%s\n" "#define F77_FUNC_(name,NAME) NAME ## __" >>confdefs.h ;; *) - { echo "$as_me:$LINENO: WARNING: unknown Fortran name-mangling scheme" >&5 -echo "$as_me: WARNING: unknown Fortran name-mangling scheme" >&2;} - ;; + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: unknown Fortran name-mangling scheme" >&5 +printf "%s\n" "$as_me: WARNING: unknown Fortran name-mangling scheme" >&2;} + ;; esac ac_ext=c @@ -9638,6 +6460,27 @@ ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $ ac_compiler_gnu=$ac_cv_c_compiler_gnu + if test "x$BINDC" = x ; then + +# Check whether --with-bindc was given. +if test ${with_bindc+y} +then : + withval=$with_bindc; +fi + + if test "x$with_bindc" = xyes ; then + BINDC=yes + fi + fi + + if test "x$BINDC" = xyes ; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: using Fortran 2003 BIND(C) wrappers." >&5 +printf "%s\n" "$as_me: using Fortran 2003 BIND(C) wrappers." >&6;} + else + BINDC= + fi + + SUBDIRS="C Fortran" TSTDIRS="C Fortran" INSTDIR="Fortran" @@ -9648,27 +6491,21 @@ fi # System-dependent system libraries (for building the sharable library). #----------------------------------------------------------------------- # Darwin (contains stubs for long double). -as_ac_Lib=`echo "ac_cv_lib_SystemStubs_printf\$LDBLStub" | $as_tr_sh` -{ echo "$as_me:$LINENO: checking for printf\$LDBLStub in -lSystemStubs" >&5 -echo $ECHO_N "checking for printf\$LDBLStub in -lSystemStubs... $ECHO_C" >&6; } -if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else +as_ac_Lib=`printf "%s\n" "ac_cv_lib_SystemStubs_printf\\$LDBLStub" | $as_tr_sh` +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for printf\$LDBLStub in -lSystemStubs" >&5 +printf %s "checking for printf\$LDBLStub in -lSystemStubs... " >&6; } +if eval test \${$as_ac_Lib+y} +then : + printf %s "(cached) " >&6 +else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-lSystemStubs $LIBS" -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF +cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif char printf\$LDBLStub (); #ifdef F77_DUMMY_MAIN @@ -9679,47 +6516,28 @@ char printf\$LDBLStub (); #endif int -main () +main (void) { return printf\$LDBLStub (); ; return 0; } _ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_link") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest$ac_exeext && - $as_test_x conftest$ac_exeext; then +if ac_fn_c_try_link "$LINENO" +then : eval "$as_ac_Lib=yes" -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - eval "$as_ac_Lib=no" +else $as_nop + eval "$as_ac_Lib=no" fi - -rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ - conftest$ac_exeext conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam \ + conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi -ac_res=`eval echo '${'$as_ac_Lib'}'` - { echo "$as_me:$LINENO: result: $ac_res" >&5 -echo "${ECHO_T}$ac_res" >&6; } -if test `eval echo '${'$as_ac_Lib'}'` = yes; then +eval ac_res=\$$as_ac_Lib + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +printf "%s\n" "$ac_res" >&6; } +if eval test \"x\$"$as_ac_Lib"\" = x"yes" +then : LIBS="$LIBS -lSystemStubs" fi @@ -9728,14 +6546,19 @@ fi # Library and installation utilities. #------------------------------------ # Static library generation. +# Ensure "non-deterministic" archives are produced during the build process. +ar rU conftest.a > /dev/null 2>&1 && ARFLAGS="U" +rm -f conftest.a + if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. set dummy ${ac_tool_prefix}ranlib; ac_word=$2 -{ echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } -if test "${ac_cv_prog_RANLIB+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_RANLIB+y} +then : + printf %s "(cached) " >&6 +else $as_nop if test -n "$RANLIB"; then ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. else @@ -9743,26 +6566,30 @@ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done -done + done IFS=$as_save_IFS fi fi RANLIB=$ac_cv_prog_RANLIB if test -n "$RANLIB"; then - { echo "$as_me:$LINENO: result: $RANLIB" >&5 -echo "${ECHO_T}$RANLIB" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5 +printf "%s\n" "$RANLIB" >&6; } else - { echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } fi @@ -9771,11 +6598,12 @@ if test -z "$ac_cv_prog_RANLIB"; then ac_ct_RANLIB=$RANLIB # Extract the first word of "ranlib", so it can be a program name with args. set dummy ranlib; ac_word=$2 -{ echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } -if test "${ac_cv_prog_ac_ct_RANLIB+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_ac_ct_RANLIB+y} +then : + printf %s "(cached) " >&6 +else $as_nop if test -n "$ac_ct_RANLIB"; then ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. else @@ -9783,26 +6611,30 @@ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_RANLIB="ranlib" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done -done + done IFS=$as_save_IFS fi fi ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB if test -n "$ac_ct_RANLIB"; then - { echo "$as_me:$LINENO: result: $ac_ct_RANLIB" >&5 -echo "${ECHO_T}$ac_ct_RANLIB" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5 +printf "%s\n" "$ac_ct_RANLIB" >&6; } else - { echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } fi if test "x$ac_ct_RANLIB" = x; then @@ -9810,12 +6642,8 @@ fi else case $cross_compiling:$ac_tool_warned in yes:) -{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools -whose name does not start with the host triplet. If you think this -configuration is useful to you, please write to autoconf@gnu.org." >&5 -echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools -whose name does not start with the host triplet. If you think this -configuration is useful to you, please write to autoconf@gnu.org." >&2;} +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac RANLIB=$ac_ct_RANLIB @@ -9825,45 +6653,66 @@ else fi -# Shared library generation. +# Shared library generation - gcc only. +# Ways of disabling shared libraries: +# configure --disable-shared +# configure --enable-shared=no +# Check whether --enable-shared was given. +if test ${enable_shared+y} +then : + enableval=$enable_shared; +fi + + +SHRLIB= +SONAME= +SHRFLAGS= +SHRLD= +SHRSFX= +SHRLN= + if test "x$ac_cv_c_compiler_gnu" = xyes ; then - SHVER=`echo "$LIBVER" | sed -e 's/\..*$//'` - - # Note that -fPIC is on by default for Macs, this just makes it obvious. - SHRFLAGS="-fPIC" - SHRLD="\$(CC) \$(SHRFLAGS)" - - case "$build_os" in - darwin*) - SHRLIB="libwcs.$LIBVER.dylib" - SONAME="libwcs.$SHVER.dylib" - SHRLD="$SHRLD -dynamiclib -single_module" - SHRLD="$SHRLD -compatibility_version $SHVER -current_version $LIBVER" - SHRLN= - - case "$build_cpu" in - powerpc*) - # Switch off -fPIC (not applicable for PowerPC Macs). - CFLAGS="$CFLAGS -mdynamic-no-pic" + if test "x$enable_shared" = xno ; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: Generation of WCS shared libraries disabled." >&5 +printf "%s\n" "$as_me: WARNING: Generation of WCS shared libraries disabled." >&2;} + + else + SHVER=`echo "$LIBVER" | sed -e 's/\..*$//'` + + # Note that -fPIC is on by default for Macs, this just makes it obvious. + SHRFLAGS="-fPIC" + SHRLD="\$(CC) \$(SHRFLAGS)" + + case "$host_os" in + darwin*) + SHRLIB="libwcs.$LIBVER.dylib" + SONAME="libwcs.$SHVER.dylib" + SHRLD="$SHRLD -dynamiclib -single_module" + SHRLD="$SHRLD -compatibility_version $SHVER -current_version $LIBVER -install_name \$(SONAME)" + SHRLN="libwcs.dylib" + + case "$host_cpu" in + powerpc*) + # Switch off -fPIC (not applicable for PowerPC Macs). + CFLAGS="$CFLAGS -mdynamic-no-pic" + ;; + esac + ;; + *mingw*) + SHRLIB="libwcs.dll.$LIBVER" + SONAME="libwcs.dll.$SHVER" + SHRLD="$SHRLD -shared -Wl,-h\$(SONAME)" + SHRLN="libwcs.dll" + ;; + *) + # Covers Linux and Solaris at least. + SHRLIB="libwcs.so.$LIBVER" + SONAME="libwcs.so.$SHVER" + SHRLD="$SHRLD -shared -Wl,-h\$(SONAME)" + SHRLN="libwcs.so" ;; esac - ;; - *) - # Covers Linux and Solaris at least. - SHRLIB="libwcs.so.$LIBVER" - SONAME="libwcs.so.$SHVER" - SHRLD="$SHRLD -shared -Wl,-h\$(SONAME)" - SHRLN="libwcs.so" - ;; - esac - -else - SHRLIB= - SONAME= - SHRFLAGS= - SHRLD= - SHRSFX= - SHRLN= + fi fi @@ -9874,18 +6723,19 @@ fi # Installation utilities. -{ echo "$as_me:$LINENO: checking whether ln -s works" >&5 -echo $ECHO_N "checking whether ln -s works... $ECHO_C" >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5 +printf %s "checking whether ln -s works... " >&6; } LN_S=$as_ln_s if test "$LN_S" = "ln -s"; then - { echo "$as_me:$LINENO: result: yes" >&5 -echo "${ECHO_T}yes" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +printf "%s\n" "yes" >&6; } else - { echo "$as_me:$LINENO: result: no, using $LN_S" >&5 -echo "${ECHO_T}no, using $LN_S" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no, using $LN_S" >&5 +printf "%s\n" "no, using $LN_S" >&6; } fi -# Find a good install program. We prefer a C program (faster), + + # Find a good install program. We prefer a C program (faster), # so one script is as good as another. But avoid the broken or # incompatible versions: # SysV /etc/install, /usr/sbin/install @@ -9898,22 +6748,28 @@ fi # SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" # OS/2's system install, which has a completely different semantic # ./install, which can be erroneously created by make from ./install.sh. -{ echo "$as_me:$LINENO: checking for a BSD-compatible install" >&5 -echo $ECHO_N "checking for a BSD-compatible install... $ECHO_C" >&6; } +# Reject install programs that cannot install multiple files. +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 +printf %s "checking for a BSD-compatible install... " >&6; } if test -z "$INSTALL"; then -if test "${ac_cv_path_install+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else +if test ${ac_cv_path_install+y} +then : + printf %s "(cached) " >&6 +else $as_nop as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - # Account for people who put trailing slashes in PATH elements. -case $as_dir/ in - ./ | .// | /cC/* | \ + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + # Account for fact that we put trailing slashes in our PATH walk. +case $as_dir in #(( + ./ | /[cC]/* | \ /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ - ?:\\/os2\\/install\\/* | ?:\\/OS2\\/INSTALL\\/* | \ + ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \ /usr/ucb/* ) ;; *) # OSF1 and SCO ODT 3.0 have their own names for install. @@ -9921,30 +6777,42 @@ case $as_dir/ in # by default. for ac_prog in ginstall scoinst install; do for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; }; then + if as_fn_executable_p "$as_dir$ac_prog$ac_exec_ext"; then if test $ac_prog = install && - grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + grep dspmsg "$as_dir$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # AIX install. It has an incompatible calling convention. : elif test $ac_prog = install && - grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + grep pwplus "$as_dir$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # program-specific install script used by HP pwplus--don't use. : else - ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" - break 3 + rm -rf conftest.one conftest.two conftest.dir + echo one > conftest.one + echo two > conftest.two + mkdir conftest.dir + if "$as_dir$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir/" && + test -s conftest.one && test -s conftest.two && + test -s conftest.dir/conftest.one && + test -s conftest.dir/conftest.two + then + ac_cv_path_install="$as_dir$ac_prog$ac_exec_ext -c" + break 3 + fi fi fi done done ;; esac -done + + done IFS=$as_save_IFS +rm -rf conftest.one conftest.two conftest.dir fi - if test "${ac_cv_path_install+set}" = set; then + if test ${ac_cv_path_install+y}; then INSTALL=$ac_cv_path_install else # As a last resort, use the slow shell script. Don't cache a @@ -9954,8 +6822,8 @@ fi INSTALL=$ac_install_sh fi fi -{ echo "$as_me:$LINENO: result: $INSTALL" >&5 -echo "${ECHO_T}$INSTALL" >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 +printf "%s\n" "$INSTALL" >&6; } # Use test -z because SunOS4 sh mishandles braces in ${var-val}. # It thinks the first close brace ends the variable substitution. @@ -9966,178 +6834,81 @@ test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' -{ echo "$as_me:$LINENO: End of primary configuration. +# Older versions of GNU make do not have the -O option, which only facilitates +# legibility of the output from parallel builds (make -j). +make --help | grep '\-O' >/dev/null 2>&1 && MAKEFLAGS="-Otarget" + + +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: End of primary configuration. " >&5 -echo "$as_me: End of primary configuration. +printf "%s\n" "$as_me: End of primary configuration. " >&6;} # The following are required to build utilities and test programs. # ---------------------------------------------------------------- -{ echo "$as_me:$LINENO: Looking for libraries etc. for utilities and test suite..." >&5 -echo "$as_me: Looking for libraries etc. for utilities and test suite..." >&6;} - -# Check for other quasi-standard header files. - -for ac_header in unistd.h -do -as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` -if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then - { echo "$as_me:$LINENO: checking for $ac_header" >&5 -echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; } -if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -fi -ac_res=`eval echo '${'$as_ac_Header'}'` - { echo "$as_me:$LINENO: result: $ac_res" >&5 -echo "${ECHO_T}$ac_res" >&6; } -else - # Is the header compilable? -{ echo "$as_me:$LINENO: checking $ac_header usability" >&5 -echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6; } -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -$ac_includes_default -#include <$ac_header> -_ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then - ac_header_compiler=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_header_compiler=no -fi - -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 -echo "${ECHO_T}$ac_header_compiler" >&6; } - -# Is the header present? -{ echo "$as_me:$LINENO: checking $ac_header presence" >&5 -echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6; } -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include <$ac_header> +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: Looking for libraries etc. for utilities and test suite..." >&5 +printf "%s\n" "$as_me: Looking for libraries etc. for utilities and test suite..." >&6;} + +# Additional standard C header files required. +headers= + for ac_header in errno.h time.h +do : + as_ac_Header=`printf "%s\n" "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" +if eval test \"x\$"$as_ac_Header"\" = x"yes" +then : + cat >>confdefs.h <<_ACEOF +#define `printf "%s\n" "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF -if { (ac_try="$ac_cpp conftest.$ac_ext" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } >/dev/null && { - test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || - test ! -s conftest.err - }; then - ac_header_preproc=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - ac_header_preproc=no +else $as_nop + headers=no fi -rm -f conftest.err conftest.$ac_ext -{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 -echo "${ECHO_T}$ac_header_preproc" >&6; } - -# So? What about this header? -case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in - yes:no: ) - { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 -echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 -echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} - ac_header_preproc=yes - ;; - no:yes:* ) - { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 -echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 -echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 -echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 -echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 -echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 -echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} - ( cat <<\_ASBOX -## ------------------------------------ ## -## Report this to mark@calabretta.id.au ## -## ------------------------------------ ## -_ASBOX - ) | sed "s/^/$as_me: WARNING: /" >&2 - ;; -esac -{ echo "$as_me:$LINENO: checking for $ac_header" >&5 -echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; } -if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - eval "$as_ac_Header=\$ac_header_preproc" -fi -ac_res=`eval echo '${'$as_ac_Header'}'` - { echo "$as_me:$LINENO: result: $ac_res" >&5 -echo "${ECHO_T}$ac_res" >&6; } +done -fi -if test `eval echo '${'$as_ac_Header'}'` = yes; then +# Other header files required by *nix library functions. + for ac_header in sys/stat.h sys/types.h unistd.h +do : + as_ac_Header=`printf "%s\n" "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" +if eval test \"x\$"$as_ac_Header"\" = x"yes" +then : cat >>confdefs.h <<_ACEOF -#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +#define `printf "%s\n" "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF +else $as_nop + headers=no fi done - - -# Large file support. -{ echo "$as_me:$LINENO: checking for _LARGEFILE_SOURCE value needed for large files" >&5 -echo $ECHO_N "checking for _LARGEFILE_SOURCE value needed for large files... $ECHO_C" >&6; } -if test "${ac_cv_sys_largefile_source+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else +if test "x$headers" = xno; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: + -------------------------------------------------------------------- + One or more of the header files required to compile the utilities + and/or test programs is missing. Continuing on a best-effort basis. + --------------------------------------------------------------------" >&5 +printf "%s\n" "$as_me: WARNING: + -------------------------------------------------------------------- + One or more of the header files required to compile the utilities + and/or test programs is missing. Continuing on a best-effort basis. + --------------------------------------------------------------------" >&2;} +fi + +# Large file support (only required by fitshdr utility). +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for _LARGEFILE_SOURCE value needed for large files" >&5 +printf %s "checking for _LARGEFILE_SOURCE value needed for large files... " >&6; } +if test ${ac_cv_sys_largefile_source+y} +then : + printf %s "(cached) " >&6 +else $as_nop while :; do - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include +#include /* for off_t */ + #include #ifdef F77_DUMMY_MAIN # ifdef __cplusplus @@ -10147,49 +6918,25 @@ cat >>conftest.$ac_ext <<_ACEOF #endif int -main () +main (void) { -return fseeko (stdin, 0, 0) && (fseeko) (stdin, 0, 0); +int (*fp) (FILE *, off_t, int) = fseeko; + return fseeko (stdin, 0, 0) && fp (stdin, 0, 0); ; return 0; } _ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_link") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest$ac_exeext && - $as_test_x conftest$ac_exeext; then +if ac_fn_c_try_link "$LINENO" +then : ac_cv_sys_largefile_source=no; break -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - fi - -rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ - conftest$ac_exeext conftest.$ac_ext - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF +rm -f core conftest.err conftest.$ac_objext conftest.beam \ + conftest$ac_exeext conftest.$ac_ext + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #define _LARGEFILE_SOURCE 1 -#include +#include /* for off_t */ + #include #ifdef F77_DUMMY_MAIN # ifdef __cplusplus @@ -10199,98 +6946,71 @@ cat >>conftest.$ac_ext <<_ACEOF #endif int -main () +main (void) { -return fseeko (stdin, 0, 0) && (fseeko) (stdin, 0, 0); +int (*fp) (FILE *, off_t, int) = fseeko; + return fseeko (stdin, 0, 0) && fp (stdin, 0, 0); ; return 0; } _ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_link") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest$ac_exeext && - $as_test_x conftest$ac_exeext; then +if ac_fn_c_try_link "$LINENO" +then : ac_cv_sys_largefile_source=1; break -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - fi - -rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ - conftest$ac_exeext conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam \ + conftest$ac_exeext conftest.$ac_ext ac_cv_sys_largefile_source=unknown break done fi -{ echo "$as_me:$LINENO: result: $ac_cv_sys_largefile_source" >&5 -echo "${ECHO_T}$ac_cv_sys_largefile_source" >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_largefile_source" >&5 +printf "%s\n" "$ac_cv_sys_largefile_source" >&6; } case $ac_cv_sys_largefile_source in #( no | unknown) ;; *) -cat >>confdefs.h <<_ACEOF -#define _LARGEFILE_SOURCE $ac_cv_sys_largefile_source -_ACEOF +printf "%s\n" "#define _LARGEFILE_SOURCE $ac_cv_sys_largefile_source" >>confdefs.h ;; esac -rm -f -r conftest* +rm -rf conftest* # We used to try defining _XOPEN_SOURCE=500 too, to work around a bug # in glibc 2.1.3, but that breaks too many other things. # If you want fseeko and ftello with glibc, upgrade to a fixed glibc. if test $ac_cv_sys_largefile_source != unknown; then -cat >>confdefs.h <<\_ACEOF -#define HAVE_FSEEKO 1 -_ACEOF +printf "%s\n" "#define HAVE_FSEEKO 1" >>confdefs.h fi # Check whether --enable-largefile was given. -if test "${enable_largefile+set}" = set; then +if test ${enable_largefile+y} +then : enableval=$enable_largefile; fi if test "$enable_largefile" != no; then - { echo "$as_me:$LINENO: checking for special C compiler options needed for large files" >&5 -echo $ECHO_N "checking for special C compiler options needed for large files... $ECHO_C" >&6; } -if test "${ac_cv_sys_largefile_CC+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for special C compiler options needed for large files" >&5 +printf %s "checking for special C compiler options needed for large files... " >&6; } +if test ${ac_cv_sys_largefile_CC+y} +then : + printf %s "(cached) " >&6 +else $as_nop ac_cv_sys_largefile_CC=no if test "$GCC" != yes; then ac_save_CC=$CC while :; do # IRIX 6.2 and later do not support large files by default, # so use the C compiler's -n32 option if that helps. - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include /* Check that off_t can represent 2**63 - 1 correctly. We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ -#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) +#define LARGE_OFF_T (((off_t) 1 << 31 << 31) - 1 + ((off_t) 1 << 31 << 31)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; @@ -10303,96 +7023,51 @@ cat >>conftest.$ac_ext <<_ACEOF #endif int -main () +main (void) { ; return 0; } _ACEOF - rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then + if ac_fn_c_try_compile "$LINENO" +then : break -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - fi - -rm -f core conftest.err conftest.$ac_objext +rm -f core conftest.err conftest.$ac_objext conftest.beam CC="$CC -n32" - rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then + if ac_fn_c_try_compile "$LINENO" +then : ac_cv_sys_largefile_CC=' -n32'; break -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - fi - -rm -f core conftest.err conftest.$ac_objext +rm -f core conftest.err conftest.$ac_objext conftest.beam break done CC=$ac_save_CC rm -f conftest.$ac_ext fi fi -{ echo "$as_me:$LINENO: result: $ac_cv_sys_largefile_CC" >&5 -echo "${ECHO_T}$ac_cv_sys_largefile_CC" >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_largefile_CC" >&5 +printf "%s\n" "$ac_cv_sys_largefile_CC" >&6; } if test "$ac_cv_sys_largefile_CC" != no; then CC=$CC$ac_cv_sys_largefile_CC fi - { echo "$as_me:$LINENO: checking for _FILE_OFFSET_BITS value needed for large files" >&5 -echo $ECHO_N "checking for _FILE_OFFSET_BITS value needed for large files... $ECHO_C" >&6; } -if test "${ac_cv_sys_file_offset_bits+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for _FILE_OFFSET_BITS value needed for large files" >&5 +printf %s "checking for _FILE_OFFSET_BITS value needed for large files... " >&6; } +if test ${ac_cv_sys_file_offset_bits+y} +then : + printf %s "(cached) " >&6 +else $as_nop while :; do - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include /* Check that off_t can represent 2**63 - 1 correctly. We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ -#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) +#define LARGE_OFF_T (((off_t) 1 << 31 << 31) - 1 + ((off_t) 1 << 31 << 31)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; @@ -10405,44 +7080,19 @@ cat >>conftest.$ac_ext <<_ACEOF #endif int -main () +main (void) { ; return 0; } _ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then +if ac_fn_c_try_compile "$LINENO" +then : ac_cv_sys_file_offset_bits=no; break -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - fi - -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #define _FILE_OFFSET_BITS 64 #include @@ -10450,7 +7100,7 @@ cat >>conftest.$ac_ext <<_ACEOF We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ -#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) +#define LARGE_OFF_T (((off_t) 1 << 31 << 31) - 1 + ((off_t) 1 << 31 << 31)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; @@ -10463,73 +7113,47 @@ cat >>conftest.$ac_ext <<_ACEOF #endif int -main () +main (void) { ; return 0; } _ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then +if ac_fn_c_try_compile "$LINENO" +then : ac_cv_sys_file_offset_bits=64; break -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - fi - -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ac_cv_sys_file_offset_bits=unknown break done fi -{ echo "$as_me:$LINENO: result: $ac_cv_sys_file_offset_bits" >&5 -echo "${ECHO_T}$ac_cv_sys_file_offset_bits" >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_file_offset_bits" >&5 +printf "%s\n" "$ac_cv_sys_file_offset_bits" >&6; } case $ac_cv_sys_file_offset_bits in #( no | unknown) ;; *) -cat >>confdefs.h <<_ACEOF -#define _FILE_OFFSET_BITS $ac_cv_sys_file_offset_bits -_ACEOF +printf "%s\n" "#define _FILE_OFFSET_BITS $ac_cv_sys_file_offset_bits" >>confdefs.h ;; esac -rm -f -r conftest* +rm -rf conftest* if test $ac_cv_sys_file_offset_bits = unknown; then - { echo "$as_me:$LINENO: checking for _LARGE_FILES value needed for large files" >&5 -echo $ECHO_N "checking for _LARGE_FILES value needed for large files... $ECHO_C" >&6; } -if test "${ac_cv_sys_large_files+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for _LARGE_FILES value needed for large files" >&5 +printf %s "checking for _LARGE_FILES value needed for large files... " >&6; } +if test ${ac_cv_sys_large_files+y} +then : + printf %s "(cached) " >&6 +else $as_nop while :; do - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include /* Check that off_t can represent 2**63 - 1 correctly. We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ -#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) +#define LARGE_OFF_T (((off_t) 1 << 31 << 31) - 1 + ((off_t) 1 << 31 << 31)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; @@ -10542,44 +7166,19 @@ cat >>conftest.$ac_ext <<_ACEOF #endif int -main () +main (void) { ; return 0; } _ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then +if ac_fn_c_try_compile "$LINENO" +then : ac_cv_sys_large_files=no; break -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - fi - -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #define _LARGE_FILES 1 #include @@ -10587,7 +7186,7 @@ cat >>conftest.$ac_ext <<_ACEOF We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ -#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) +#define LARGE_OFF_T (((off_t) 1 << 31 << 31) - 1 + ((off_t) 1 << 31 << 31)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; @@ -10600,57 +7199,43 @@ cat >>conftest.$ac_ext <<_ACEOF #endif int -main () +main (void) { ; return 0; } _ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then +if ac_fn_c_try_compile "$LINENO" +then : ac_cv_sys_large_files=1; break -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - fi - -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ac_cv_sys_large_files=unknown break done fi -{ echo "$as_me:$LINENO: result: $ac_cv_sys_large_files" >&5 -echo "${ECHO_T}$ac_cv_sys_large_files" >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_large_files" >&5 +printf "%s\n" "$ac_cv_sys_large_files" >&6; } case $ac_cv_sys_large_files in #( no | unknown) ;; *) -cat >>confdefs.h <<_ACEOF -#define _LARGE_FILES $ac_cv_sys_large_files -_ACEOF +printf "%s\n" "#define _LARGE_FILES $ac_cv_sys_large_files" >>confdefs.h ;; esac -rm -f -r conftest* +rm -rf conftest* fi fi +ac_fn_c_check_type "$LINENO" "off_t" "ac_cv_type_off_t" "$ac_includes_default" +if test "x$ac_cv_type_off_t" = xyes +then : + +else $as_nop + +printf "%s\n" "#define off_t long int" >>confdefs.h + +fi # Extra places to look for third-party libraries and header files. @@ -10658,17 +7243,19 @@ LIBDIRS= # Check whether --with-cfitsio was given. -if test "${with_cfitsio+set}" = set; then +if test ${with_cfitsio+y} +then : withval=$with_cfitsio; fi if test "x$with_cfitsio" = xno ; then - { echo "$as_me:$LINENO: WARNING: CFITSIO disabled" >&5 -echo "$as_me: WARNING: CFITSIO disabled" >&2;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: CFITSIO disabled." >&5 +printf "%s\n" "$as_me: WARNING: CFITSIO disabled." >&2;} else # Check whether --with-cfitsiolib was given. -if test "${with_cfitsiolib+set}" = set; then +if test ${with_cfitsiolib+y} +then : withval=$with_cfitsiolib; fi @@ -10678,7 +7265,8 @@ fi # Check whether --with-cfitsioinc was given. -if test "${with_cfitsioinc+set}" = set; then +if test ${with_cfitsioinc+y} +then : withval=$with_cfitsioinc; fi @@ -10697,17 +7285,19 @@ fi # Check whether --with-pgplot was given. -if test "${with_pgplot+set}" = set; then +if test ${with_pgplot+y} +then : withval=$with_pgplot; fi if test "x$with_pgplot" = xno ; then - { echo "$as_me:$LINENO: WARNING: PGPLOT disabled" >&5 -echo "$as_me: WARNING: PGPLOT disabled" >&2;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: PGPLOT disabled." >&5 +printf "%s\n" "$as_me: WARNING: PGPLOT disabled." >&2;} else # Check whether --with-pgplotlib was given. -if test "${with_pgplotlib+set}" = set; then +if test ${with_pgplotlib+y} +then : withval=$with_pgplotlib; fi @@ -10717,7 +7307,8 @@ fi # Check whether --with-pgplotinc was given. -if test "${with_pgplotinc+set}" = set; then +if test ${with_pgplotinc+y} +then : withval=$with_pgplotinc; fi @@ -10745,28 +7336,28 @@ if test "x$with_cfitsio" != xno -o \ /sw/lib" for LIBDIR in $LIBDIRS ; do - as_ac_File=`echo "ac_cv_file_$LIBDIR" | $as_tr_sh` -{ echo "$as_me:$LINENO: checking for $LIBDIR" >&5 -echo $ECHO_N "checking for $LIBDIR... $ECHO_C" >&6; } -if { as_var=$as_ac_File; eval "test \"\${$as_var+set}\" = set"; }; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else + as_ac_File=`printf "%s\n" "ac_cv_file_$LIBDIR" | $as_tr_sh` +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $LIBDIR" >&5 +printf %s "checking for $LIBDIR... " >&6; } +if eval test \${$as_ac_File+y} +then : + printf %s "(cached) " >&6 +else $as_nop test "$cross_compiling" = yes && - { { echo "$as_me:$LINENO: error: cannot check for file existence when cross compiling" >&5 -echo "$as_me: error: cannot check for file existence when cross compiling" >&2;} - { (exit 1); exit 1; }; } + as_fn_error $? "cannot check for file existence when cross compiling" "$LINENO" 5 if test -r "$LIBDIR"; then eval "$as_ac_File=yes" else eval "$as_ac_File=no" fi fi -ac_res=`eval echo '${'$as_ac_File'}'` - { echo "$as_me:$LINENO: result: $ac_res" >&5 -echo "${ECHO_T}$ac_res" >&6; } -if test `eval echo '${'$as_ac_File'}'` = yes; then +eval ac_res=\$$as_ac_File + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +printf "%s\n" "$ac_res" >&6; } +if eval test \"x\$"$as_ac_File"\" = x"yes" +then : LDFLAGS="$LDFLAGS -L$LIBDIR" -else +else $as_nop continue fi @@ -10785,74 +7376,68 @@ fi if test "x$with_cfitsio" != xno ; then # Search for CFITSIO. for INCDIR in $CFITSIO_INCDIRS $INCDIRS ; do - as_ac_File=`echo "ac_cv_file_$INCDIR/cfitsio/fitsio.h" | $as_tr_sh` -{ echo "$as_me:$LINENO: checking for $INCDIR/cfitsio/fitsio.h" >&5 -echo $ECHO_N "checking for $INCDIR/cfitsio/fitsio.h... $ECHO_C" >&6; } -if { as_var=$as_ac_File; eval "test \"\${$as_var+set}\" = set"; }; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else + as_ac_File=`printf "%s\n" "ac_cv_file_$INCDIR/cfitsio/fitsio.h" | $as_tr_sh` +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $INCDIR/cfitsio/fitsio.h" >&5 +printf %s "checking for $INCDIR/cfitsio/fitsio.h... " >&6; } +if eval test \${$as_ac_File+y} +then : + printf %s "(cached) " >&6 +else $as_nop test "$cross_compiling" = yes && - { { echo "$as_me:$LINENO: error: cannot check for file existence when cross compiling" >&5 -echo "$as_me: error: cannot check for file existence when cross compiling" >&2;} - { (exit 1); exit 1; }; } + as_fn_error $? "cannot check for file existence when cross compiling" "$LINENO" 5 if test -r "$INCDIR/cfitsio/fitsio.h"; then eval "$as_ac_File=yes" else eval "$as_ac_File=no" fi fi -ac_res=`eval echo '${'$as_ac_File'}'` - { echo "$as_me:$LINENO: result: $ac_res" >&5 -echo "${ECHO_T}$ac_res" >&6; } -if test `eval echo '${'$as_ac_File'}'` = yes; then +eval ac_res=\$$as_ac_File + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +printf "%s\n" "$ac_res" >&6; } +if eval test \"x\$"$as_ac_File"\" = x"yes" +then : CFITSIOINC="-I$INCDIR/cfitsio"; break fi - as_ac_File=`echo "ac_cv_file_$INCDIR/fitsio.h" | $as_tr_sh` -{ echo "$as_me:$LINENO: checking for $INCDIR/fitsio.h" >&5 -echo $ECHO_N "checking for $INCDIR/fitsio.h... $ECHO_C" >&6; } -if { as_var=$as_ac_File; eval "test \"\${$as_var+set}\" = set"; }; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else + as_ac_File=`printf "%s\n" "ac_cv_file_$INCDIR/fitsio.h" | $as_tr_sh` +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $INCDIR/fitsio.h" >&5 +printf %s "checking for $INCDIR/fitsio.h... " >&6; } +if eval test \${$as_ac_File+y} +then : + printf %s "(cached) " >&6 +else $as_nop test "$cross_compiling" = yes && - { { echo "$as_me:$LINENO: error: cannot check for file existence when cross compiling" >&5 -echo "$as_me: error: cannot check for file existence when cross compiling" >&2;} - { (exit 1); exit 1; }; } + as_fn_error $? "cannot check for file existence when cross compiling" "$LINENO" 5 if test -r "$INCDIR/fitsio.h"; then eval "$as_ac_File=yes" else eval "$as_ac_File=no" fi fi -ac_res=`eval echo '${'$as_ac_File'}'` - { echo "$as_me:$LINENO: result: $ac_res" >&5 -echo "${ECHO_T}$ac_res" >&6; } -if test `eval echo '${'$as_ac_File'}'` = yes; then +eval ac_res=\$$as_ac_File + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +printf "%s\n" "$ac_res" >&6; } +if eval test \"x\$"$as_ac_File"\" = x"yes" +then : CFITSIOINC="-I$INCDIR"; break fi done - { echo "$as_me:$LINENO: checking for recv in -lsocket" >&5 -echo $ECHO_N "checking for recv in -lsocket... $ECHO_C" >&6; } -if test "${ac_cv_lib_socket_recv+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for recv in -lsocket" >&5 +printf %s "checking for recv in -lsocket... " >&6; } +if test ${ac_cv_lib_socket_recv+y} +then : + printf %s "(cached) " >&6 +else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-lsocket $LIBS $LIBS" -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF +cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif char recv (); #ifdef F77_DUMMY_MAIN @@ -10863,69 +7448,44 @@ char recv (); #endif int -main () +main (void) { return recv (); ; return 0; } _ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_link") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest$ac_exeext && - $as_test_x conftest$ac_exeext; then +if ac_fn_c_try_link "$LINENO" +then : ac_cv_lib_socket_recv=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_cv_lib_socket_recv=no +else $as_nop + ac_cv_lib_socket_recv=no fi - -rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ - conftest$ac_exeext conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam \ + conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi -{ echo "$as_me:$LINENO: result: $ac_cv_lib_socket_recv" >&5 -echo "${ECHO_T}$ac_cv_lib_socket_recv" >&6; } -if test $ac_cv_lib_socket_recv = yes; then +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_socket_recv" >&5 +printf "%s\n" "$ac_cv_lib_socket_recv" >&6; } +if test "x$ac_cv_lib_socket_recv" = xyes +then : CFITSIOLIB="-lsocket" fi - { echo "$as_me:$LINENO: checking for ffopen in -lcfitsio" >&5 -echo $ECHO_N "checking for ffopen in -lcfitsio... $ECHO_C" >&6; } -if test "${ac_cv_lib_cfitsio_ffopen+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for ffopen in -lcfitsio" >&5 +printf %s "checking for ffopen in -lcfitsio... " >&6; } +if test ${ac_cv_lib_cfitsio_ffopen+y} +then : + printf %s "(cached) " >&6 +else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-lcfitsio $CFITSIOLIB $LIBS $LIBS" -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF +cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif char ffopen (); #ifdef F77_DUMMY_MAIN @@ -10936,83 +7496,56 @@ char ffopen (); #endif int -main () +main (void) { return ffopen (); ; return 0; } _ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_link") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest$ac_exeext && - $as_test_x conftest$ac_exeext; then +if ac_fn_c_try_link "$LINENO" +then : ac_cv_lib_cfitsio_ffopen=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_cv_lib_cfitsio_ffopen=no +else $as_nop + ac_cv_lib_cfitsio_ffopen=no fi - -rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ - conftest$ac_exeext conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam \ + conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi -{ echo "$as_me:$LINENO: result: $ac_cv_lib_cfitsio_ffopen" >&5 -echo "${ECHO_T}$ac_cv_lib_cfitsio_ffopen" >&6; } -if test $ac_cv_lib_cfitsio_ffopen = yes; then +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_cfitsio_ffopen" >&5 +printf "%s\n" "$ac_cv_lib_cfitsio_ffopen" >&6; } +if test "x$ac_cv_lib_cfitsio_ffopen" = xyes +then : CFITSIOLIB="-lcfitsio $CFITSIOLIB" fi if test "x$CFITSIOINC" = x -o "x$CFITSIOLIB" = x; then - { echo "$as_me:$LINENO: WARNING: CFITSIO not found, skipping CFITSIO-dependent tests." >&5 -echo "$as_me: WARNING: CFITSIO not found, skipping CFITSIO-dependent tests." >&2;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: CFITSIO not found, skipping CFITSIO-dependent tests." >&5 +printf "%s\n" "$as_me: WARNING: CFITSIO not found, skipping CFITSIO-dependent tests." >&2;} else - { echo "$as_me:$LINENO: CFITSIO appears to be available." >&5 -echo "$as_me: CFITSIO appears to be available." >&6;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: CFITSIO appears to be available." >&5 +printf "%s\n" "$as_me: CFITSIO appears to be available." >&6;} -cat >>confdefs.h <<\_ACEOF -#define HAVE_CFITSIO 1 -_ACEOF +printf "%s\n" "#define HAVE_CFITSIO 1" >>confdefs.h # Check for fits_read_wcstab, present in CFITSIO 3.004beta and later. - { echo "$as_me:$LINENO: checking for fits_read_wcstab in -lcfitsio" >&5 -echo $ECHO_N "checking for fits_read_wcstab in -lcfitsio... $ECHO_C" >&6; } -if test "${ac_cv_lib_cfitsio_fits_read_wcstab+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for fits_read_wcstab in -lcfitsio" >&5 +printf %s "checking for fits_read_wcstab in -lcfitsio... " >&6; } +if test ${ac_cv_lib_cfitsio_fits_read_wcstab+y} +then : + printf %s "(cached) " >&6 +else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-lcfitsio $CFITSIOLIB $LIBS $LIBS" -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF +cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif char fits_read_wcstab (); #ifdef F77_DUMMY_MAIN @@ -11023,107 +7556,92 @@ char fits_read_wcstab (); #endif int -main () +main (void) { return fits_read_wcstab (); ; return 0; } _ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_link") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest$ac_exeext && - $as_test_x conftest$ac_exeext; then +if ac_fn_c_try_link "$LINENO" +then : ac_cv_lib_cfitsio_fits_read_wcstab=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_cv_lib_cfitsio_fits_read_wcstab=no +else $as_nop + ac_cv_lib_cfitsio_fits_read_wcstab=no fi - -rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ - conftest$ac_exeext conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam \ + conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi -{ echo "$as_me:$LINENO: result: $ac_cv_lib_cfitsio_fits_read_wcstab" >&5 -echo "${ECHO_T}$ac_cv_lib_cfitsio_fits_read_wcstab" >&6; } -if test $ac_cv_lib_cfitsio_fits_read_wcstab = yes; then +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_cfitsio_fits_read_wcstab" >&5 +printf "%s\n" "$ac_cv_lib_cfitsio_fits_read_wcstab" >&6; } +if test "x$ac_cv_lib_cfitsio_fits_read_wcstab" = xyes +then : GETWCSTAB= -else +else $as_nop GETWCSTAB=getwcstab.o fi if test "x$GETWCSTAB" != x ; then - { echo "$as_me:$LINENO: WARNING: fits_read_wcstab not found in CFITSIO, will use + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: fits_read_wcstab not found in CFITSIO, will use getwcstab.c to compile test programs." >&5 -echo "$as_me: WARNING: fits_read_wcstab not found in CFITSIO, will use +printf "%s\n" "$as_me: WARNING: fits_read_wcstab not found in CFITSIO, will use getwcstab.c to compile test programs." >&2;} fi fi + + + + fi # PGPLOT. if test "x$F77" != x -a "x$with_pgplot" != xno ; then # Search for PGPLOT. for INCDIR in $PGPLOT_INCDIRS $INCDIRS ; do - as_ac_File=`echo "ac_cv_file_$INCDIR/pgplot/cpgplot.h" | $as_tr_sh` -{ echo "$as_me:$LINENO: checking for $INCDIR/pgplot/cpgplot.h" >&5 -echo $ECHO_N "checking for $INCDIR/pgplot/cpgplot.h... $ECHO_C" >&6; } -if { as_var=$as_ac_File; eval "test \"\${$as_var+set}\" = set"; }; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else + as_ac_File=`printf "%s\n" "ac_cv_file_$INCDIR/pgplot/cpgplot.h" | $as_tr_sh` +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $INCDIR/pgplot/cpgplot.h" >&5 +printf %s "checking for $INCDIR/pgplot/cpgplot.h... " >&6; } +if eval test \${$as_ac_File+y} +then : + printf %s "(cached) " >&6 +else $as_nop test "$cross_compiling" = yes && - { { echo "$as_me:$LINENO: error: cannot check for file existence when cross compiling" >&5 -echo "$as_me: error: cannot check for file existence when cross compiling" >&2;} - { (exit 1); exit 1; }; } + as_fn_error $? "cannot check for file existence when cross compiling" "$LINENO" 5 if test -r "$INCDIR/pgplot/cpgplot.h"; then eval "$as_ac_File=yes" else eval "$as_ac_File=no" fi fi -ac_res=`eval echo '${'$as_ac_File'}'` - { echo "$as_me:$LINENO: result: $ac_res" >&5 -echo "${ECHO_T}$ac_res" >&6; } -if test `eval echo '${'$as_ac_File'}'` = yes; then +eval ac_res=\$$as_ac_File + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +printf "%s\n" "$ac_res" >&6; } +if eval test \"x\$"$as_ac_File"\" = x"yes" +then : PGPLOTINC="-I$INCDIR/pgplot"; break fi - as_ac_File=`echo "ac_cv_file_$INCDIR/cpgplot.h" | $as_tr_sh` -{ echo "$as_me:$LINENO: checking for $INCDIR/cpgplot.h" >&5 -echo $ECHO_N "checking for $INCDIR/cpgplot.h... $ECHO_C" >&6; } -if { as_var=$as_ac_File; eval "test \"\${$as_var+set}\" = set"; }; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else + as_ac_File=`printf "%s\n" "ac_cv_file_$INCDIR/cpgplot.h" | $as_tr_sh` +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $INCDIR/cpgplot.h" >&5 +printf %s "checking for $INCDIR/cpgplot.h... " >&6; } +if eval test \${$as_ac_File+y} +then : + printf %s "(cached) " >&6 +else $as_nop test "$cross_compiling" = yes && - { { echo "$as_me:$LINENO: error: cannot check for file existence when cross compiling" >&5 -echo "$as_me: error: cannot check for file existence when cross compiling" >&2;} - { (exit 1); exit 1; }; } + as_fn_error $? "cannot check for file existence when cross compiling" "$LINENO" 5 if test -r "$INCDIR/cpgplot.h"; then eval "$as_ac_File=yes" else eval "$as_ac_File=no" fi fi -ac_res=`eval echo '${'$as_ac_File'}'` - { echo "$as_me:$LINENO: result: $ac_res" >&5 -echo "${ECHO_T}$ac_res" >&6; } -if test `eval echo '${'$as_ac_File'}'` = yes; then +eval ac_res=\$$as_ac_File + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +printf "%s\n" "$ac_res" >&6; } +if eval test \"x\$"$as_ac_File"\" = x"yes" +then : PGPLOTINC="-I$INCDIR"; break fi @@ -11134,26 +7652,20 @@ fi # PGPLOT compiled by the SUN Fortran compiler but linked with something # else. - { echo "$as_me:$LINENO: checking for iand_ in -lM77" >&5 -echo $ECHO_N "checking for iand_ in -lM77... $ECHO_C" >&6; } -if test "${ac_cv_lib_M77_iand_+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for iand_ in -lM77" >&5 +printf %s "checking for iand_ in -lM77... " >&6; } +if test ${ac_cv_lib_M77_iand_+y} +then : + printf %s "(cached) " >&6 +else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-lM77 $PGPLOTLIB $LIBS $LIBS" -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF +cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif char iand_ (); #ifdef F77_DUMMY_MAIN @@ -11164,69 +7676,44 @@ char iand_ (); #endif int -main () +main (void) { return iand_ (); ; return 0; } _ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_link") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest$ac_exeext && - $as_test_x conftest$ac_exeext; then +if ac_fn_c_try_link "$LINENO" +then : ac_cv_lib_M77_iand_=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_cv_lib_M77_iand_=no +else $as_nop + ac_cv_lib_M77_iand_=no fi - -rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ - conftest$ac_exeext conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam \ + conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi -{ echo "$as_me:$LINENO: result: $ac_cv_lib_M77_iand_" >&5 -echo "${ECHO_T}$ac_cv_lib_M77_iand_" >&6; } -if test $ac_cv_lib_M77_iand_ = yes; then +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_M77_iand_" >&5 +printf "%s\n" "$ac_cv_lib_M77_iand_" >&6; } +if test "x$ac_cv_lib_M77_iand_" = xyes +then : PGPLOTLIB="-lM77 $PGPLOTLIB" fi - { echo "$as_me:$LINENO: checking for f77_init in -lF77" >&5 -echo $ECHO_N "checking for f77_init in -lF77... $ECHO_C" >&6; } -if test "${ac_cv_lib_F77_f77_init+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for f77_init in -lF77" >&5 +printf %s "checking for f77_init in -lF77... " >&6; } +if test ${ac_cv_lib_F77_f77_init+y} +then : + printf %s "(cached) " >&6 +else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-lF77 $PGPLOTLIB $LIBS $LIBS" -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF +cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif char f77_init (); #ifdef F77_DUMMY_MAIN @@ -11237,64 +7724,42 @@ char f77_init (); #endif int -main () +main (void) { return f77_init (); ; return 0; } _ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_link") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest$ac_exeext && - $as_test_x conftest$ac_exeext; then +if ac_fn_c_try_link "$LINENO" +then : ac_cv_lib_F77_f77_init=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_cv_lib_F77_f77_init=no +else $as_nop + ac_cv_lib_F77_f77_init=no fi - -rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ - conftest$ac_exeext conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam \ + conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi -{ echo "$as_me:$LINENO: result: $ac_cv_lib_F77_f77_init" >&5 -echo "${ECHO_T}$ac_cv_lib_F77_f77_init" >&6; } -if test $ac_cv_lib_F77_f77_init = yes; then +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_F77_f77_init" >&5 +printf "%s\n" "$ac_cv_lib_F77_f77_init" >&6; } +if test "x$ac_cv_lib_F77_f77_init" = xyes +then : PGPLOTLIB="-lF77 $PGPLOTLIB" fi if test "x$F77" != xg77; then # For PGPLOT compiled with g77 but linked with something else. - { echo "$as_me:$LINENO: checking for main in -lfrtbegin" >&5 -echo $ECHO_N "checking for main in -lfrtbegin... $ECHO_C" >&6; } -if test "${ac_cv_lib_frtbegin_main+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for main in -lfrtbegin" >&5 +printf %s "checking for main in -lfrtbegin... " >&6; } +if test ${ac_cv_lib_frtbegin_main+y} +then : + printf %s "(cached) " >&6 +else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-lfrtbegin $PGPLOTLIB $LIBS $LIBS" -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF +cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -11307,69 +7772,44 @@ cat >>conftest.$ac_ext <<_ACEOF #endif int -main () +main (void) { return main (); ; return 0; } _ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_link") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest$ac_exeext && - $as_test_x conftest$ac_exeext; then +if ac_fn_c_try_link "$LINENO" +then : ac_cv_lib_frtbegin_main=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_cv_lib_frtbegin_main=no +else $as_nop + ac_cv_lib_frtbegin_main=no fi - -rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ - conftest$ac_exeext conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam \ + conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi -{ echo "$as_me:$LINENO: result: $ac_cv_lib_frtbegin_main" >&5 -echo "${ECHO_T}$ac_cv_lib_frtbegin_main" >&6; } -if test $ac_cv_lib_frtbegin_main = yes; then +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_frtbegin_main" >&5 +printf "%s\n" "$ac_cv_lib_frtbegin_main" >&6; } +if test "x$ac_cv_lib_frtbegin_main" = xyes +then : PGPLOTLIB="-lfrtbegin $PGPLOTLIB" fi - { echo "$as_me:$LINENO: checking for gerror_ in -lg2c" >&5 -echo $ECHO_N "checking for gerror_ in -lg2c... $ECHO_C" >&6; } -if test "${ac_cv_lib_g2c_gerror_+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for gerror_ in -lg2c" >&5 +printf %s "checking for gerror_ in -lg2c... " >&6; } +if test ${ac_cv_lib_g2c_gerror_+y} +then : + printf %s "(cached) " >&6 +else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-lg2c $PGPLOTLIB $LIBS $LIBS" -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF +cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif char gerror_ (); #ifdef F77_DUMMY_MAIN @@ -11380,46 +7820,27 @@ char gerror_ (); #endif int -main () +main (void) { return gerror_ (); ; return 0; } _ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_link") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest$ac_exeext && - $as_test_x conftest$ac_exeext; then +if ac_fn_c_try_link "$LINENO" +then : ac_cv_lib_g2c_gerror_=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_cv_lib_g2c_gerror_=no +else $as_nop + ac_cv_lib_g2c_gerror_=no fi - -rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ - conftest$ac_exeext conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam \ + conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi -{ echo "$as_me:$LINENO: result: $ac_cv_lib_g2c_gerror_" >&5 -echo "${ECHO_T}$ac_cv_lib_g2c_gerror_" >&6; } -if test $ac_cv_lib_g2c_gerror_ = yes; then +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_g2c_gerror_" >&5 +printf "%s\n" "$ac_cv_lib_g2c_gerror_" >&6; } +if test "x$ac_cv_lib_g2c_gerror_" = xyes +then : PGPLOTLIB="-lg2c $PGPLOTLIB" fi @@ -11431,26 +7852,20 @@ fi # to add -lgfortran to the link list without also adding -lgfortranbegin. # Doing so stops gfortran from adding -lgfortranbegin which is needed to # resolve "main". - { echo "$as_me:$LINENO: checking for _gfortran_abort in -lgfortran" >&5 -echo $ECHO_N "checking for _gfortran_abort in -lgfortran... $ECHO_C" >&6; } -if test "${ac_cv_lib_gfortran__gfortran_abort+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for _gfortran_abort in -lgfortran" >&5 +printf %s "checking for _gfortran_abort in -lgfortran... " >&6; } +if test ${ac_cv_lib_gfortran__gfortran_abort+y} +then : + printf %s "(cached) " >&6 +else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-lgfortran $PGPLOTLIB $LIBS $LIBS" -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF +cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif char _gfortran_abort (); #ifdef F77_DUMMY_MAIN @@ -11461,58 +7876,41 @@ char _gfortran_abort (); #endif int -main () +main (void) { return _gfortran_abort (); ; return 0; } _ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_link") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest$ac_exeext && - $as_test_x conftest$ac_exeext; then +if ac_fn_c_try_link "$LINENO" +then : ac_cv_lib_gfortran__gfortran_abort=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_cv_lib_gfortran__gfortran_abort=no +else $as_nop + ac_cv_lib_gfortran__gfortran_abort=no fi - -rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ - conftest$ac_exeext conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam \ + conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi -{ echo "$as_me:$LINENO: result: $ac_cv_lib_gfortran__gfortran_abort" >&5 -echo "${ECHO_T}$ac_cv_lib_gfortran__gfortran_abort" >&6; } -if test $ac_cv_lib_gfortran__gfortran_abort = yes; then +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_gfortran__gfortran_abort" >&5 +printf "%s\n" "$ac_cv_lib_gfortran__gfortran_abort" >&6; } +if test "x$ac_cv_lib_gfortran__gfortran_abort" = xyes +then : PGPLOTLIB="-lgfortran $PGPLOTLIB" fi fi # Search for X11 includes and libraries. - { echo "$as_me:$LINENO: checking for X" >&5 -echo $ECHO_N "checking for X... $ECHO_C" >&6; } + +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for X" >&5 +printf %s "checking for X... " >&6; } # Check whether --with-x was given. -if test "${with_x+set}" = set; then +if test ${with_x+y} +then : withval=$with_x; fi @@ -11522,15 +7920,50 @@ if test "x$with_x" = xno; then have_x=disabled else case $x_includes,$x_libraries in #( - *\'*) { { echo "$as_me:$LINENO: error: Cannot use X directory names containing '" >&5 -echo "$as_me: error: Cannot use X directory names containing '" >&2;} - { (exit 1); exit 1; }; };; #( - *,NONE | NONE,*) if test "${ac_cv_have_x+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else + *\'*) as_fn_error $? "cannot use X directory names containing '" "$LINENO" 5;; #( + *,NONE | NONE,*) if test ${ac_cv_have_x+y} +then : + printf %s "(cached) " >&6 +else $as_nop # One or both of the vars are not set, and there is no cached value. -ac_x_includes=no ac_x_libraries=no -rm -f -r conftest.dir +ac_x_includes=no +ac_x_libraries=no +# Do we need to do anything special at all? +ac_save_LIBS=$LIBS +LIBS="-lX11 $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#ifdef F77_DUMMY_MAIN + +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } + +#endif +int +main (void) +{ +XrmInitialize () + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO" +then : + # We can compile and link X programs with no special options. + ac_x_includes= + ac_x_libraries= +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam \ + conftest$ac_exeext conftest.$ac_ext +LIBS="$ac_save_LIBS" +# If that didn't work, only try xmkmf and file system searches +# for native compilation. +if test x"$ac_x_includes" = xno && test "$cross_compiling" = no +then : + rm -f -r conftest.dir if mkdir conftest.dir; then cd conftest.dir cat >Imakefile <<'_ACEOF' @@ -11542,7 +7975,7 @@ libdir: @echo libdir='${LIBDIR}' _ACEOF if (export CC; ${XMKMF-xmkmf}) >/dev/null 2>/dev/null && test -f Makefile; then - # GNU make sometimes prints "make[1]: Entering...", which would confuse us. + # GNU make sometimes prints "make[1]: Entering ...", which would confuse us. for ac_var in incroot usrlibdir libdir; do eval "ac_im_$ac_var=\`\${MAKE-make} $ac_var 2>/dev/null | sed -n 's/^$ac_var=//p'\`" done @@ -11561,7 +7994,7 @@ _ACEOF *) test -f "$ac_im_incroot/X11/Xos.h" && ac_x_includes=$ac_im_incroot;; esac case $ac_im_usrlibdir in - /usr/lib | /lib) ;; + /usr/lib | /usr/lib64 | /lib | /lib64) ;; *) test -d "$ac_im_usrlibdir" && ac_x_libraries=$ac_im_usrlibdir ;; esac fi @@ -11569,29 +8002,35 @@ _ACEOF rm -f -r conftest.dir fi -# Standard set of common directories for X headers. + # Standard set of common directories for X headers. # Check X11 before X11Rn because it is often a symlink to the current release. ac_x_header_dirs=' /usr/X11/include +/usr/X11R7/include /usr/X11R6/include /usr/X11R5/include /usr/X11R4/include /usr/include/X11 +/usr/include/X11R7 /usr/include/X11R6 /usr/include/X11R5 /usr/include/X11R4 /usr/local/X11/include +/usr/local/X11R7/include /usr/local/X11R6/include /usr/local/X11R5/include /usr/local/X11R4/include /usr/local/include/X11 +/usr/local/include/X11R7 /usr/local/include/X11R6 /usr/local/include/X11R5 /usr/local/include/X11R4 +/opt/X11/include + /usr/X386/include /usr/x386/include /usr/XFree86/include/X11 @@ -11609,36 +8048,15 @@ ac_x_header_dirs=' if test "$ac_x_includes" = no; then # Guess where to find include files, by looking for Xlib.h. # First, try using that file with no special directory specified. - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF -if { (ac_try="$ac_cpp conftest.$ac_ext" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } >/dev/null && { - test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || - test ! -s conftest.err - }; then +if ac_fn_c_try_cpp "$LINENO" +then : # We can compile using X headers with no special include directory. ac_x_includes= -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - +else $as_nop for ac_dir in $ac_x_header_dirs; do if test -r "$ac_dir/X11/Xlib.h"; then ac_x_includes=$ac_dir @@ -11646,8 +8064,7 @@ sed 's/^/| /' conftest.$ac_ext >&5 fi done fi - -rm -f conftest.err conftest.$ac_ext +rm -f conftest.err conftest.i conftest.$ac_ext fi # $ac_x_includes = no if test "$ac_x_libraries" = no; then @@ -11656,11 +8073,7 @@ if test "$ac_x_libraries" = no; then # Don't add to $LIBS permanently. ac_save_LIBS=$LIBS LIBS="-lX11 $LIBS" - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #ifdef F77_DUMMY_MAIN @@ -11672,40 +8085,21 @@ cat >>conftest.$ac_ext <<_ACEOF #endif int -main () +main (void) { XrmInitialize () ; return 0; } _ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_link") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest$ac_exeext && - $as_test_x conftest$ac_exeext; then +if ac_fn_c_try_link "$LINENO" +then : LIBS=$ac_save_LIBS # We can link X programs with no special library path. ac_x_libraries= -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - LIBS=$ac_save_LIBS -for ac_dir in `echo "$ac_x_includes $ac_x_header_dirs" | sed s/include/lib/g` +else $as_nop + LIBS=$ac_save_LIBS +for ac_dir in `printf "%s\n" "$ac_x_includes $ac_x_header_dirs" | sed s/include/lib/g` do # Don't even attempt the hair of trying to link an X program! for ac_extension in a so sl dylib la dll; do @@ -11716,20 +8110,21 @@ do done done fi - -rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ - conftest$ac_exeext conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam \ + conftest$ac_exeext conftest.$ac_ext fi # $ac_x_libraries = no +fi +# Record the results. case $ac_x_includes,$ac_x_libraries in #( - no,* | *,no | *\'*) + no,* | *,no | *\'*) : # Didn't find X, or a directory has "'" in its name. - ac_cv_have_x="have_x=no";; #( - *) + ac_cv_have_x="have_x=no" ;; #( + *) : # Record where we found X for the cache. ac_cv_have_x="have_x=yes\ ac_x_includes='$ac_x_includes'\ - ac_x_libraries='$ac_x_libraries'" + ac_x_libraries='$ac_x_libraries'" ;; esac fi ;; #( @@ -11739,8 +8134,8 @@ fi fi # $with_x != no if test "$have_x" != yes; then - { echo "$as_me:$LINENO: result: $have_x" >&5 -echo "${ECHO_T}$have_x" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $have_x" >&5 +printf "%s\n" "$have_x" >&6; } no_x=yes else # If each of the values was on the command line, it overrides each guess. @@ -11750,8 +8145,8 @@ else ac_cv_have_x="have_x=yes\ ac_x_includes='$x_includes'\ ac_x_libraries='$x_libraries'" - { echo "$as_me:$LINENO: result: libraries $x_libraries, headers $x_includes" >&5 -echo "${ECHO_T}libraries $x_libraries, headers $x_includes" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: libraries $x_libraries, headers $x_includes" >&5 +printf "%s\n" "libraries $x_libraries, headers $x_includes" >&6; } fi if test "x$no_x" = x; then @@ -11764,26 +8159,20 @@ fi # It is possible that other libraries may be required depending on what # graphics drivers were installed with PGPLOT. - { echo "$as_me:$LINENO: checking for deflate in -lz" >&5 -echo $ECHO_N "checking for deflate in -lz... $ECHO_C" >&6; } -if test "${ac_cv_lib_z_deflate+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for deflate in -lz" >&5 +printf %s "checking for deflate in -lz... " >&6; } +if test ${ac_cv_lib_z_deflate+y} +then : + printf %s "(cached) " >&6 +else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-lz $PGPLOTLIB $LIBS $LIBS" -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF +cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif char deflate (); #ifdef F77_DUMMY_MAIN @@ -11794,69 +8183,44 @@ char deflate (); #endif int -main () +main (void) { return deflate (); ; return 0; } _ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_link") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest$ac_exeext && - $as_test_x conftest$ac_exeext; then +if ac_fn_c_try_link "$LINENO" +then : ac_cv_lib_z_deflate=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_cv_lib_z_deflate=no +else $as_nop + ac_cv_lib_z_deflate=no fi - -rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ - conftest$ac_exeext conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam \ + conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi -{ echo "$as_me:$LINENO: result: $ac_cv_lib_z_deflate" >&5 -echo "${ECHO_T}$ac_cv_lib_z_deflate" >&6; } -if test $ac_cv_lib_z_deflate = yes; then +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_z_deflate" >&5 +printf "%s\n" "$ac_cv_lib_z_deflate" >&6; } +if test "x$ac_cv_lib_z_deflate" = xyes +then : PGPLOTLIB="-lz $PGPLOTLIB" fi - { echo "$as_me:$LINENO: checking for png_error in -lpng" >&5 -echo $ECHO_N "checking for png_error in -lpng... $ECHO_C" >&6; } -if test "${ac_cv_lib_png_png_error+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for png_error in -lpng" >&5 +printf %s "checking for png_error in -lpng... " >&6; } +if test ${ac_cv_lib_png_png_error+y} +then : + printf %s "(cached) " >&6 +else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-lpng $PGPLOTLIB $LIBS $LIBS" -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF +cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif char png_error (); #ifdef F77_DUMMY_MAIN @@ -11867,69 +8231,44 @@ char png_error (); #endif int -main () +main (void) { return png_error (); ; return 0; } _ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_link") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest$ac_exeext && - $as_test_x conftest$ac_exeext; then +if ac_fn_c_try_link "$LINENO" +then : ac_cv_lib_png_png_error=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_cv_lib_png_png_error=no +else $as_nop + ac_cv_lib_png_png_error=no fi - -rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ - conftest$ac_exeext conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam \ + conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi -{ echo "$as_me:$LINENO: result: $ac_cv_lib_png_png_error" >&5 -echo "${ECHO_T}$ac_cv_lib_png_png_error" >&6; } -if test $ac_cv_lib_png_png_error = yes; then +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_png_png_error" >&5 +printf "%s\n" "$ac_cv_lib_png_png_error" >&6; } +if test "x$ac_cv_lib_png_png_error" = xyes +then : PGPLOTLIB="-lpng $PGPLOTLIB" fi - { echo "$as_me:$LINENO: checking for pgbeg_ in -lpgplot" >&5 -echo $ECHO_N "checking for pgbeg_ in -lpgplot... $ECHO_C" >&6; } -if test "${ac_cv_lib_pgplot_pgbeg_+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for pgbeg_ in -lpgplot" >&5 +printf %s "checking for pgbeg_ in -lpgplot... " >&6; } +if test ${ac_cv_lib_pgplot_pgbeg_+y} +then : + printf %s "(cached) " >&6 +else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-lpgplot $PGPLOTLIB $FLIBS $LIBS $LIBS" -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF +cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif char pgbeg_ (); #ifdef F77_DUMMY_MAIN @@ -11940,69 +8279,44 @@ char pgbeg_ (); #endif int -main () +main (void) { return pgbeg_ (); ; return 0; } _ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_link") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest$ac_exeext && - $as_test_x conftest$ac_exeext; then +if ac_fn_c_try_link "$LINENO" +then : ac_cv_lib_pgplot_pgbeg_=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_cv_lib_pgplot_pgbeg_=no +else $as_nop + ac_cv_lib_pgplot_pgbeg_=no fi - -rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ - conftest$ac_exeext conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam \ + conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi -{ echo "$as_me:$LINENO: result: $ac_cv_lib_pgplot_pgbeg_" >&5 -echo "${ECHO_T}$ac_cv_lib_pgplot_pgbeg_" >&6; } -if test $ac_cv_lib_pgplot_pgbeg_ = yes; then +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pgplot_pgbeg_" >&5 +printf "%s\n" "$ac_cv_lib_pgplot_pgbeg_" >&6; } +if test "x$ac_cv_lib_pgplot_pgbeg_" = xyes +then : PGPLOTLIB="-lpgplot $PGPLOTLIB" fi - { echo "$as_me:$LINENO: checking for cpgbeg in -lcpgplot" >&5 -echo $ECHO_N "checking for cpgbeg in -lcpgplot... $ECHO_C" >&6; } -if test "${ac_cv_lib_cpgplot_cpgbeg+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for cpgbeg in -lcpgplot" >&5 +printf %s "checking for cpgbeg in -lcpgplot... " >&6; } +if test ${ac_cv_lib_cpgplot_cpgbeg+y} +then : + printf %s "(cached) " >&6 +else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-lcpgplot $PGPLOTLIB $FLIBS $LIBS $LIBS" -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF +cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif char cpgbeg (); #ifdef F77_DUMMY_MAIN @@ -12013,48 +8327,29 @@ char cpgbeg (); #endif int -main () +main (void) { return cpgbeg (); ; return 0; } _ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_link") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest$ac_exeext && - $as_test_x conftest$ac_exeext; then +if ac_fn_c_try_link "$LINENO" +then : ac_cv_lib_cpgplot_cpgbeg=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_cv_lib_cpgplot_cpgbeg=no +else $as_nop + ac_cv_lib_cpgplot_cpgbeg=no fi - -rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ - conftest$ac_exeext conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam \ + conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi -{ echo "$as_me:$LINENO: result: $ac_cv_lib_cpgplot_cpgbeg" >&5 -echo "${ECHO_T}$ac_cv_lib_cpgplot_cpgbeg" >&6; } -if test $ac_cv_lib_cpgplot_cpgbeg = yes; then +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_cpgplot_cpgbeg" >&5 +printf "%s\n" "$ac_cv_lib_cpgplot_cpgbeg" >&6; } +if test "x$ac_cv_lib_cpgplot_cpgbeg" = xyes +then : PGPLOTLIB="-lcpgplot $PGPLOTLIB" -else +else $as_nop PGPLOTLIB= fi @@ -12067,15 +8362,18 @@ fi # Also need the PGPLOT library to build pgtest and cpgtest. if test "x$PGPLOTLIB" = x; then - { echo "$as_me:$LINENO: WARNING: PGPLOT not found, skipping PGPLOT-dependent tests." >&5 -echo "$as_me: WARNING: PGPLOT not found, skipping PGPLOT-dependent tests." >&2;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: PGPLOT not found, skipping PGPLOT-dependent tests." >&5 +printf "%s\n" "$as_me: WARNING: PGPLOT not found, skipping PGPLOT-dependent tests." >&2;} else - { echo "$as_me:$LINENO: PGPLOT appears to be available." >&5 -echo "$as_me: PGPLOT appears to be available." >&6;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: PGPLOT appears to be available." >&5 +printf "%s\n" "$as_me: PGPLOT appears to be available." >&6;} TSTDIRS="$TSTDIRS pgsbox" fi fi + + + fi @@ -12084,16 +8382,17 @@ fi # configure --disable-utils # configure --enable-utils=no # Check whether --enable-utils was given. -if test "${enable_utils+set}" = set; then +if test ${enable_utils+y} +then : enableval=$enable_utils; fi -if test "x$enable_utils" != xno ; then +if test "x$enable_utils" = xno ; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: Compilation of WCS utilities disabled." >&5 +printf "%s\n" "$as_me: WARNING: Compilation of WCS utilities disabled." >&2;} +else SUBDIRS="$SUBDIRS utils" INSTDIR="$INSTDIR utils" -else - { echo "$as_me:$LINENO: WARNING: Compilation of WCS utilities disabled" >&5 -echo "$as_me: WARNING: Compilation of WCS utilities disabled" >&2;} fi @@ -12101,22 +8400,44 @@ fi +# Default observer coordinates for sundazel. +if test -f "$HOME/.sundazelrc"; then + . "$HOME/.sundazelrc" +fi + +if test "x$OBSLNG" = x; then + OBSLNG=0.0 + OBSLAT=0.0 + OBSTZ=0.0 +fi +# Tidy up incrementally defined variables. +FLFLAGS=`echo $FLFLAGS` +CPPFLAGS=`echo $CPPFLAGS` +CFLAGS=`echo $CFLAGS` +FFLAGS=`echo $FFLAGS` +LDFLAGS=`echo $LDFLAGS` -{ echo "$as_me:$LINENO: End of auxiliary configuration. +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: End of auxiliary configuration. " >&5 -echo "$as_me: End of auxiliary configuration. +printf "%s\n" "$as_me: End of auxiliary configuration. " >&6;} +# Set from the environment for code development. + + + + + # Do it. -{ echo "$as_me:$LINENO: Configuring files..." >&5 -echo "$as_me: Configuring files..." >&6;} +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: Configuring files..." >&5 +printf "%s\n" "$as_me: Configuring files..." >&6;} ac_config_files="$ac_config_files makedefs wcslib.pc" ac_config_headers="$ac_config_headers wcsconfig.h wcsconfig_f77.h wcsconfig_tests.h wcsconfig_utils.h" @@ -12148,12 +8469,13 @@ _ACEOF case $ac_val in #( *${as_nl}*) case $ac_var in #( - *_cv_*) { echo "$as_me:$LINENO: WARNING: Cache variable $ac_var contains a newline." >&5 -echo "$as_me: WARNING: Cache variable $ac_var contains a newline." >&2;} ;; + *_cv_*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +printf "%s\n" "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( - *) $as_unset $ac_var ;; + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done @@ -12161,8 +8483,8 @@ echo "$as_me: WARNING: Cache variable $ac_var contains a newline." >&2;} ;; (set) 2>&1 | case $as_nl`(ac_space=' '; set) 2>&1` in #( *${as_nl}ac_space=\ *) - # `set' does not quote correctly, so add quotes (double-quote - # substitution turns \\\\ into \\, and sed turns \\ into \). + # `set' does not quote correctly, so add quotes: double-quote + # substitution turns \\\\ into \\, and sed turns \\ into \. sed -n \ "s/'/'\\\\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" @@ -12178,19 +8500,30 @@ echo "$as_me: WARNING: Cache variable $ac_var contains a newline." >&2;} ;; /^ac_cv_env_/b end t clear :clear - s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + s/^\([^=]*\)=\(.*[{}].*\)$/test ${\1+y} || &/ t end s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ :end' >>confcache if diff "$cache_file" confcache >/dev/null 2>&1; then :; else if test -w "$cache_file"; then - test "x$cache_file" != "x/dev/null" && - { echo "$as_me:$LINENO: updating cache $cache_file" >&5 -echo "$as_me: updating cache $cache_file" >&6;} - cat confcache >$cache_file + if test "x$cache_file" != "x/dev/null"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 +printf "%s\n" "$as_me: updating cache $cache_file" >&6;} + if test ! -f "$cache_file" || test -h "$cache_file"; then + cat confcache >"$cache_file" + else + case $cache_file in #( + */* | ?:*) + mv -f confcache "$cache_file"$$ && + mv -f "$cache_file"$$ "$cache_file" ;; #( + *) + mv -f confcache "$cache_file" ;; + esac + fi + fi else - { echo "$as_me:$LINENO: not updating unwritable cache $cache_file" >&5 -echo "$as_me: not updating unwritable cache $cache_file" >&6;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 +printf "%s\n" "$as_me: not updating unwritable cache $cache_file" >&6;} fi fi rm -f confcache @@ -12203,14 +8536,15 @@ DEFS=-DHAVE_CONFIG_H ac_libobjs= ac_ltlibobjs= +U= for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue # 1. Remove the extension, and $U if already installed. ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' - ac_i=`echo "$ac_i" | sed "$ac_script"` + ac_i=`printf "%s\n" "$ac_i" | sed "$ac_script"` # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR # will be set to the directory where LIBOBJS objects are built. - ac_libobjs="$ac_libobjs \${LIBOBJDIR}$ac_i\$U.$ac_objext" - ac_ltlibobjs="$ac_ltlibobjs \${LIBOBJDIR}$ac_i"'$U.lo' + as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" + as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' done LIBOBJS=$ac_libobjs @@ -12218,12 +8552,14 @@ LTLIBOBJS=$ac_ltlibobjs -: ${CONFIG_STATUS=./config.status} +: "${CONFIG_STATUS=./config.status}" +ac_write_fail=0 ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files $CONFIG_STATUS" -{ echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5 -echo "$as_me: creating $CONFIG_STATUS" >&6;} -cat >$CONFIG_STATUS <<_ACEOF +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 +printf "%s\n" "$as_me: creating $CONFIG_STATUS" >&6;} +as_write_fail=0 +cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 #! $SHELL # Generated by $as_me. # Run this file to recreate the current configuration. @@ -12233,81 +8569,99 @@ cat >$CONFIG_STATUS <<_ACEOF debug=false ac_cs_recheck=false ac_cs_silent=false -SHELL=\${CONFIG_SHELL-$SHELL} -_ACEOF -cat >>$CONFIG_STATUS <<\_ACEOF -## --------------------- ## -## M4sh Initialization. ## -## --------------------- ## +SHELL=\${CONFIG_SHELL-$SHELL} +export SHELL +_ASEOF +cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh -if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then +as_nop=: +if test ${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1 +then : emulate sh - NULLCMD=: - # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which - # is contrary to our usage. Disable this feature. - alias -g '${1+"$@"}'='"$@"' - setopt NO_GLOB_SUBST -else - case `(set -o) 2>/dev/null` in - *posix*) set -o posix ;; -esac - -fi - - - - -# PATH needs CR -# Avoid depending upon Character Ranges. -as_cr_letters='abcdefghijklmnopqrstuvwxyz' -as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' -as_cr_Letters=$as_cr_letters$as_cr_LETTERS -as_cr_digits='0123456789' -as_cr_alnum=$as_cr_Letters$as_cr_digits - -# The user is always right. -if test "${PATH_SEPARATOR+set}" != set; then - echo "#! /bin/sh" >conf$$.sh - echo "exit 0" >>conf$$.sh - chmod +x conf$$.sh - if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then - PATH_SEPARATOR=';' - else - PATH_SEPARATOR=: - fi - rm -f conf$$.sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else $as_nop + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac fi -# Support unset when possible. -if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then - as_unset=unset -else - as_unset=false -fi -# IFS -# We need space, tab and new line, in precisely that order. Quoting is -# there to prevent editors from complaining about space-tab. -# (If _AS_PATH_WALK were called with IFS unset, it would disable word -# splitting by setting IFS to empty value.) +# Reset variables that may have inherited troublesome values from +# the environment. + +# IFS needs to be set, to space, tab, and newline, in precisely that order. +# (If _AS_PATH_WALK were called with IFS unset, it would have the +# side effect of setting IFS to empty, thus disabling word splitting.) +# Quoting is to prevent editors from complaining about space-tab. as_nl=' ' +export as_nl IFS=" "" $as_nl" +PS1='$ ' +PS2='> ' +PS4='+ ' + +# Ensure predictable behavior from utilities with locale-dependent output. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# We cannot yet rely on "unset" to work, but we need these variables +# to be unset--not just set to an empty or harmless value--now, to +# avoid bugs in old shells (e.g. pre-3.0 UWIN ksh). This construct +# also avoids known problems related to "unset" and subshell syntax +# in other old shells (e.g. bash 2.01 and pdksh 5.2.14). +for as_var in BASH_ENV ENV MAIL MAILPATH CDPATH +do eval test \${$as_var+y} \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done + +# Ensure that fds 0, 1, and 2 are open. +if (exec 3>&0) 2>/dev/null; then :; else exec 0&1) 2>/dev/null; then :; else exec 1>/dev/null; fi +if (exec 3>&2) ; then :; else exec 2>/dev/null; fi + +# The user is always right. +if ${PATH_SEPARATOR+false} :; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + # Find who we are. Look in the path if we contain no directory separator. -case $0 in +as_myself= +case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break -done + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + test -r "$as_dir$0" && as_myself=$as_dir$0 && break + done IFS=$as_save_IFS ;; @@ -12318,32 +8672,95 @@ if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then - echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 - { (exit 1); exit 1; } + printf "%s\n" "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 fi -# Work around bugs in pre-3.0 UWIN ksh. -for as_var in ENV MAIL MAILPATH -do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var -done -PS1='$ ' -PS2='> ' -PS4='+ ' -# NLS nuisances. -for as_var in \ - LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ - LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ - LC_TELEPHONE LC_TIME -do - if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then - eval $as_var=C; export $as_var - else - ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var + +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with STATUS, using 1 if that was 0. +as_fn_error () +{ + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi -done + printf "%s\n" "$as_me: error: $2" >&2 + as_fn_exit $as_status +} # as_fn_error + + + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset + +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null +then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else $as_nop + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null +then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else $as_nop + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + -# Required to use basename. if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr @@ -12357,13 +8774,17 @@ else as_basename=false fi +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi -# Name of the executable. as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || -echo X/"$0" | +printf "%s\n" X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q @@ -12378,131 +8799,128 @@ echo X/"$0" | } s/.*/./; q'` -# CDPATH. -$as_unset CDPATH - - - - as_lineno_1=$LINENO - as_lineno_2=$LINENO - test "x$as_lineno_1" != "x$as_lineno_2" && - test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2" || { - - # Create $as_me.lineno as a copy of $as_myself, but with $LINENO - # uniformly replaced by the line number. The first 'sed' inserts a - # line-number line after each line using $LINENO; the second 'sed' - # does the real work. The second script uses 'N' to pair each - # line-number line with the line containing $LINENO, and appends - # trailing '-' during substitution so that $LINENO is not a special - # case at line end. - # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the - # scripts with optimization help from Paolo Bonzini. Blame Lee - # E. McMahon (1931-1989) for sed's syntax. :-) - sed -n ' - p - /[$]LINENO/= - ' <$as_myself | - sed ' - s/[$]LINENO.*/&-/ - t lineno - b - :lineno - N - :loop - s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ - t loop - s/-\n.*// - ' >$as_me.lineno && - chmod +x "$as_me.lineno" || - { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2 - { (exit 1); exit 1; }; } - - # Don't try to exec as it changes $[0], causing all sort of problems - # (the dirname of $[0] is not the place where we might find the - # original and so on. Autoconf is especially sensitive to this). - . "./$as_me.lineno" - # Exit status is that of the last command. - exit -} - +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits -if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then - as_dirname=dirname -else - as_dirname=false -fi +# Determine whether it's possible to make 'echo' print without a newline. +# These variables are no longer used directly by Autoconf, but are AC_SUBSTed +# for compatibility with existing Makefiles. ECHO_C= ECHO_N= ECHO_T= -case `echo -n x` in +case `echo -n x` in #((((( -n*) - case `echo 'x\c'` in + case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. - *) ECHO_C='\c';; + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac -if expr a : '\(a\)' >/dev/null 2>&1 && - test "X`expr 00001 : '.*\(...\)'`" = X001; then - as_expr=expr -else - as_expr=false -fi +# For backward compatibility with old third-party macros, we provide +# the shell variables $as_echo and $as_echo_n. New code should use +# AS_ECHO(["message"]) and AS_ECHO_N(["message"]), respectively. +as_echo='printf %s\n' +as_echo_n='printf %s' rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir - mkdir conf$$.dir -fi -echo >conf$$.file -if ln -s conf$$.file conf$$ 2>/dev/null; then - as_ln_s='ln -s' - # ... but there are two gotchas: - # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. - # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. - # In both cases, we have to default to `cp -p'. - ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || - as_ln_s='cp -p' -elif ln conf$$.file conf$$ 2>/dev/null; then - as_ln_s=ln + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -pR'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -pR' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -pR' + fi else - as_ln_s='cp -p' + as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`printf "%s\n" "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +printf "%s\n" X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + + +} # as_fn_mkdir_p if mkdir -p . 2>/dev/null; then - as_mkdir_p=: + as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi -if test -x / >/dev/null 2>&1; then - as_test_x='test -x' -else - if ls -dL / >/dev/null 2>&1; then - as_ls_L_option=L - else - as_ls_L_option= - fi - as_test_x=' - eval sh -c '\'' - if test -d "$1"; then - test -d "$1/."; - else - case $1 in - -*)set "./$1";; - esac; - case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in - ???[sx]*):;;*)false;;esac;fi - '\'' sh - ' -fi -as_executable_p=$as_test_x + +# as_fn_executable_p FILE +# ----------------------- +# Test if FILE is an executable regular file. +as_fn_executable_p () +{ + test -f "$1" && test -x "$1" +} # as_fn_executable_p +as_test_x='test -x' +as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" @@ -12512,13 +8930,19 @@ as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" exec 6>&1 +## ----------------------------------- ## +## Main body of $CONFIG_STATUS script. ## +## ----------------------------------- ## +_ASEOF +test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 -# Save the log message, to keep $[0] and so on meaningful, and to +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# Save the log message, to keep $0 and so on meaningful, and to # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by WCSLIB $as_me 4.14, which was -generated by GNU Autoconf 2.61. Invocation command line was +This file was extended by WCSLIB $as_me 8.6, which was +generated by GNU Autoconf 2.71. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS @@ -12531,29 +8955,41 @@ on `(hostname || uname -n) 2>/dev/null | sed 1q` _ACEOF -cat >>$CONFIG_STATUS <<_ACEOF +case $ac_config_files in *" +"*) set x $ac_config_files; shift; ac_config_files=$*;; +esac + +case $ac_config_headers in *" +"*) set x $ac_config_headers; shift; ac_config_headers=$*;; +esac + + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # Files that config.status was made for. config_files="$ac_config_files" config_headers="$ac_config_headers" _ACEOF -cat >>$CONFIG_STATUS <<\_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 ac_cs_usage="\ -\`$as_me' instantiates files from templates according to the -current configuration. +\`$as_me' instantiates files and other configuration actions +from templates according to the current configuration. Unless the files +and actions are specified as TAGs, all are instantiated by default. -Usage: $0 [OPTIONS] [FILE]... +Usage: $0 [OPTION]... [TAG]... -h, --help print this help, then exit -V, --version print version number and configuration settings, then exit - -q, --quiet do not print progress messages + --config print configuration, then exit + -q, --quiet, --silent + do not print progress messages -d, --debug don't remove temporary files --recheck update $as_me by reconfiguring in the same conditions - --file=FILE[:TEMPLATE] - instantiate the configuration file FILE - --header=FILE[:TEMPLATE] - instantiate the configuration header FILE + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + --header=FILE[:TEMPLATE] + instantiate the configuration header FILE Configuration files: $config_files @@ -12561,36 +8997,44 @@ $config_files Configuration headers: $config_headers -Report bugs to ." +Report bugs to ." _ACEOF -cat >>$CONFIG_STATUS <<_ACEOF +ac_cs_config=`printf "%s\n" "$ac_configure_args" | sed "$ac_safe_unquote"` +ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\''/g"` +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_cs_config='$ac_cs_config_escaped' ac_cs_version="\\ -WCSLIB config.status 4.14 -configured by $0, generated by GNU Autoconf 2.61, - with options \\"`echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\" +WCSLIB config.status 8.6 +configured by $0, generated by GNU Autoconf 2.71, + with options \\"\$ac_cs_config\\" -Copyright (C) 2006 Free Software Foundation, Inc. +Copyright (C) 2021 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." ac_pwd='$ac_pwd' srcdir='$srcdir' INSTALL='$INSTALL' +test -n "\$AWK" || AWK=awk _ACEOF -cat >>$CONFIG_STATUS <<\_ACEOF -# If no file are specified by the user, then we need to provide default -# value. By we need to know if files were specified by the user. +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# The default lists apply if the user does not specify any file. ac_need_defaults=: while test $# != 0 do case $1 in - --*=*) + --*=?*) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` ac_shift=: ;; + --*=) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg= + ac_shift=: + ;; *) ac_option=$1 ac_optarg=$2 @@ -12603,34 +9047,41 @@ do -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) ac_cs_recheck=: ;; --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) - echo "$ac_cs_version"; exit ;; + printf "%s\n" "$ac_cs_version"; exit ;; + --config | --confi | --conf | --con | --co | --c ) + printf "%s\n" "$ac_cs_config"; exit ;; --debug | --debu | --deb | --de | --d | -d ) debug=: ;; --file | --fil | --fi | --f ) $ac_shift - CONFIG_FILES="$CONFIG_FILES $ac_optarg" + case $ac_optarg in + *\'*) ac_optarg=`printf "%s\n" "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + '') as_fn_error $? "missing file argument" ;; + esac + as_fn_append CONFIG_FILES " '$ac_optarg'" ac_need_defaults=false;; --header | --heade | --head | --hea ) $ac_shift - CONFIG_HEADERS="$CONFIG_HEADERS $ac_optarg" + case $ac_optarg in + *\'*) ac_optarg=`printf "%s\n" "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + as_fn_append CONFIG_HEADERS " '$ac_optarg'" ac_need_defaults=false;; --he | --h) # Conflict between --help and --header - { echo "$as_me: error: ambiguous option: $1 -Try \`$0 --help' for more information." >&2 - { (exit 1); exit 1; }; };; + as_fn_error $? "ambiguous option: \`$1' +Try \`$0 --help' for more information.";; --help | --hel | -h ) - echo "$ac_cs_usage"; exit ;; + printf "%s\n" "$ac_cs_usage"; exit ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil | --si | --s) ac_cs_silent=: ;; # This is an error. - -*) { echo "$as_me: error: unrecognized option: $1 -Try \`$0 --help' for more information." >&2 - { (exit 1); exit 1; }; } ;; + -*) as_fn_error $? "unrecognized option: \`$1' +Try \`$0 --help' for more information." ;; - *) ac_config_targets="$ac_config_targets $1" + *) as_fn_append ac_config_targets " $1" ac_need_defaults=false ;; esac @@ -12645,30 +9096,32 @@ if $ac_cs_silent; then fi _ACEOF -cat >>$CONFIG_STATUS <<_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 if \$ac_cs_recheck; then - echo "running CONFIG_SHELL=$SHELL $SHELL $0 "$ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6 - CONFIG_SHELL=$SHELL + set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion + shift + \printf "%s\n" "running CONFIG_SHELL=$SHELL \$*" >&6 + CONFIG_SHELL='$SHELL' export CONFIG_SHELL - exec $SHELL "$0"$ac_configure_args \$ac_configure_extra_args --no-create --no-recursion + exec "\$@" fi _ACEOF -cat >>$CONFIG_STATUS <<\_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 exec 5>>config.log { echo sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX ## Running $as_me. ## _ASBOX - echo "$ac_log" + printf "%s\n" "$ac_log" } >&5 _ACEOF -cat >>$CONFIG_STATUS <<_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACEOF -cat >>$CONFIG_STATUS <<\_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Handling of arguments. for ac_config_target in $ac_config_targets @@ -12681,9 +9134,7 @@ do "wcsconfig_tests.h") CONFIG_HEADERS="$CONFIG_HEADERS wcsconfig_tests.h" ;; "wcsconfig_utils.h") CONFIG_HEADERS="$CONFIG_HEADERS wcsconfig_utils.h" ;; - *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5 -echo "$as_me: error: invalid argument: $ac_config_target" >&2;} - { (exit 1); exit 1; }; };; + *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; esac done @@ -12693,8 +9144,8 @@ done # We use the long form for the default assignment because of an extremely # bizarre bug on SunOS 4.1.3. if $ac_need_defaults; then - test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files - test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers + test ${CONFIG_FILES+y} || CONFIG_FILES=$config_files + test ${CONFIG_HEADERS+y} || CONFIG_HEADERS=$config_headers fi # Have a temporary directory for convenience. Make it in the build tree @@ -12705,191 +9156,302 @@ fi # after its creation but before its name has been assigned to `$tmp'. $debug || { - tmp= + tmp= ac_tmp= trap 'exit_status=$? - { test -z "$tmp" || test ! -d "$tmp" || rm -fr "$tmp"; } && exit $exit_status + : "${ac_tmp:=$tmp}" + { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status ' 0 - trap '{ (exit 1); exit 1; }' 1 2 13 15 + trap 'as_fn_exit 1' 1 2 13 15 } # Create a (secure) tmp directory for tmp files. { tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && - test -n "$tmp" && test -d "$tmp" + test -d "$tmp" } || { tmp=./conf$$-$RANDOM (umask 077 && mkdir "$tmp") -} || -{ - echo "$me: cannot create a temporary directory in ." >&2 - { (exit 1); exit 1; } -} - -# -# Set up the sed scripts for CONFIG_FILES section. -# +} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 +ac_tmp=$tmp -# No need to generate the scripts if there are no CONFIG_FILES. -# This happens for instance when ./config.status config.h +# Set up the scripts for CONFIG_FILES section. +# No need to generate them if there are no CONFIG_FILES. +# This happens for instance with `./config.status config.h'. if test -n "$CONFIG_FILES"; then -_ACEOF +ac_cr=`echo X | tr X '\015'` +# On cygwin, bash can eat \r inside `` if the user requested igncr. +# But we know of no other shell where ac_cr would be empty at this +# point, so we can use a bashism as a fallback. +if test "x$ac_cr" = x; then + eval ac_cr=\$\'\\r\' +fi +ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` +if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then + ac_cs_awk_cr='\\r' +else + ac_cs_awk_cr=$ac_cr +fi + +echo 'BEGIN {' >"$ac_tmp/subs1.awk" && +_ACEOF +{ + echo "cat >conf$$subs.awk <<_ACEOF" && + echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && + echo "_ACEOF" +} >conf$$subs.sh || + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 +ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` ac_delim='%!_!# ' for ac_last_try in false false false false false :; do - cat >conf$$subs.sed <<_ACEOF -SHELL!$SHELL$ac_delim -PATH_SEPARATOR!$PATH_SEPARATOR$ac_delim -PACKAGE_NAME!$PACKAGE_NAME$ac_delim -PACKAGE_TARNAME!$PACKAGE_TARNAME$ac_delim -PACKAGE_VERSION!$PACKAGE_VERSION$ac_delim -PACKAGE_STRING!$PACKAGE_STRING$ac_delim -PACKAGE_BUGREPORT!$PACKAGE_BUGREPORT$ac_delim -exec_prefix!$exec_prefix$ac_delim -prefix!$prefix$ac_delim -program_transform_name!$program_transform_name$ac_delim -bindir!$bindir$ac_delim -sbindir!$sbindir$ac_delim -libexecdir!$libexecdir$ac_delim -datarootdir!$datarootdir$ac_delim -datadir!$datadir$ac_delim -sysconfdir!$sysconfdir$ac_delim -sharedstatedir!$sharedstatedir$ac_delim -localstatedir!$localstatedir$ac_delim -includedir!$includedir$ac_delim -oldincludedir!$oldincludedir$ac_delim -docdir!$docdir$ac_delim -infodir!$infodir$ac_delim -htmldir!$htmldir$ac_delim -dvidir!$dvidir$ac_delim -pdfdir!$pdfdir$ac_delim -psdir!$psdir$ac_delim -libdir!$libdir$ac_delim -localedir!$localedir$ac_delim -mandir!$mandir$ac_delim -DEFS!$DEFS$ac_delim -ECHO_C!$ECHO_C$ac_delim -ECHO_N!$ECHO_N$ac_delim -ECHO_T!$ECHO_T$ac_delim -LIBS!$LIBS$ac_delim -build_alias!$build_alias$ac_delim -host_alias!$host_alias$ac_delim -target_alias!$target_alias$ac_delim -LIBVER!$LIBVER$ac_delim -build!$build$ac_delim -build_cpu!$build_cpu$ac_delim -build_vendor!$build_vendor$ac_delim -build_os!$build_os$ac_delim -ARCH!$ARCH$ac_delim -FLEX!$FLEX$ac_delim -CC!$CC$ac_delim -CFLAGS!$CFLAGS$ac_delim -LDFLAGS!$LDFLAGS$ac_delim -CPPFLAGS!$CPPFLAGS$ac_delim -ac_ct_CC!$ac_ct_CC$ac_delim -EXEEXT!$EXEEXT$ac_delim -OBJEXT!$OBJEXT$ac_delim -CPP!$CPP$ac_delim -GREP!$GREP$ac_delim -EGREP!$EGREP$ac_delim -LIBOBJS!$LIBOBJS$ac_delim -F77!$F77$ac_delim -FFLAGS!$FFLAGS$ac_delim -ac_ct_F77!$ac_ct_F77$ac_delim -FLIBS!$FLIBS$ac_delim -RANLIB!$RANLIB$ac_delim -SHRLIB!$SHRLIB$ac_delim -SONAME!$SONAME$ac_delim -SHRFLAGS!$SHRFLAGS$ac_delim -SHRLD!$SHRLD$ac_delim -SHRSFX!$SHRSFX$ac_delim -SHRLN!$SHRLN$ac_delim -LN_S!$LN_S$ac_delim -INSTALL_PROGRAM!$INSTALL_PROGRAM$ac_delim -INSTALL_SCRIPT!$INSTALL_SCRIPT$ac_delim -INSTALL_DATA!$INSTALL_DATA$ac_delim -XMKMF!$XMKMF$ac_delim -CFITSIOINC!$CFITSIOINC$ac_delim -CFITSIOLIB!$CFITSIOLIB$ac_delim -GETWCSTAB!$GETWCSTAB$ac_delim -PGPLOTINC!$PGPLOTINC$ac_delim -PGPLOTLIB!$PGPLOTLIB$ac_delim -SUBDIRS!$SUBDIRS$ac_delim -TSTDIRS!$TSTDIRS$ac_delim -INSTDIR!$INSTDIR$ac_delim -LTLIBOBJS!$LTLIBOBJS$ac_delim -_ACEOF + . ./conf$$subs.sh || + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 - if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 80; then + ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` + if test $ac_delim_n = $ac_delim_num; then break elif $ac_last_try; then - { { echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5 -echo "$as_me: error: could not make $CONFIG_STATUS" >&2;} - { (exit 1); exit 1; }; } + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done +rm -f conf$$subs.sh + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && +_ACEOF +sed -n ' +h +s/^/S["/; s/!.*/"]=/ +p +g +s/^[^!]*!// +:repl +t repl +s/'"$ac_delim"'$// +t delim +:nl +h +s/\(.\{148\}\)..*/\1/ +t more1 +s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ +p +n +b repl +:more1 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t nl +:delim +h +s/\(.\{148\}\)..*/\1/ +t more2 +s/["\\]/\\&/g; s/^/"/; s/$/"/ +p +b +:more2 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t delim +' >$CONFIG_STATUS || ac_write_fail=1 +rm -f conf$$subs.awk +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +_ACAWK +cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && + for (key in S) S_is_set[key] = 1 + FS = "" -ac_eof=`sed -n '/^CEOF[0-9]*$/s/CEOF/0/p' conf$$subs.sed` -if test -n "$ac_eof"; then - ac_eof=`echo "$ac_eof" | sort -nru | sed 1q` - ac_eof=`expr $ac_eof + 1` -fi +} +{ + line = $ 0 + nfields = split(line, field, "@") + substed = 0 + len = length(field[1]) + for (i = 2; i < nfields; i++) { + key = field[i] + keylen = length(key) + if (S_is_set[key]) { + value = S[key] + line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) + len += length(value) + length(field[++i]) + substed = 1 + } else + len += 1 + keylen + } + + print line +} -cat >>$CONFIG_STATUS <<_ACEOF -cat >"\$tmp/subs-1.sed" <<\CEOF$ac_eof -/@[a-zA-Z_][a-zA-Z_0-9]*@/!b end +_ACAWK _ACEOF -sed ' -s/[,\\&]/\\&/g; s/@/@|#_!!_#|/g -s/^/s,@/; s/!/@,|#_!!_#|/ -:n -t n -s/'"$ac_delim"'$/,g/; t -s/$/\\/; p -N; s/^.*\n//; s/[,\\&]/\\&/g; s/@/@|#_!!_#|/g; b n -' >>$CONFIG_STATUS >$CONFIG_STATUS <<_ACEOF -:end -s/|#_!!_#|//g -CEOF$ac_eof +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then + sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" +else + cat +fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ + || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 _ACEOF - -# VPATH may cause trouble with some makes, so we remove $(srcdir), -# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and +# VPATH may cause trouble with some makes, so we remove sole $(srcdir), +# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and # trailing colons and then remove the whole line if VPATH becomes empty # (actually we leave an empty line to preserve line numbers). if test "x$srcdir" = x.; then - ac_vpsub='/^[ ]*VPATH[ ]*=/{ -s/:*\$(srcdir):*/:/ -s/:*\${srcdir}:*/:/ -s/:*@srcdir@:*/:/ -s/^\([^=]*=[ ]*\):*/\1/ + ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ +h +s/// +s/^/:/ +s/[ ]*$/:/ +s/:\$(srcdir):/:/g +s/:\${srcdir}:/:/g +s/:@srcdir@:/:/g +s/^:*// s/:*$// +x +s/\(=[ ]*\).*/\1/ +G +s/\n// s/^[^=]*=[ ]*$// }' fi -cat >>$CONFIG_STATUS <<\_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 fi # test -n "$CONFIG_FILES" +# Set up the scripts for CONFIG_HEADERS section. +# No need to generate them if there are no CONFIG_HEADERS. +# This happens for instance with `./config.status Makefile'. +if test -n "$CONFIG_HEADERS"; then +cat >"$ac_tmp/defines.awk" <<\_ACAWK || +BEGIN { +_ACEOF + +# Transform confdefs.h into an awk script `defines.awk', embedded as +# here-document in config.status, that substitutes the proper values into +# config.h.in to produce config.h. + +# Create a delimiter string that does not exist in confdefs.h, to ease +# handling of long lines. +ac_delim='%!_!# ' +for ac_last_try in false false :; do + ac_tt=`sed -n "/$ac_delim/p" confdefs.h` + if test -z "$ac_tt"; then + break + elif $ac_last_try; then + as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done + +# For the awk script, D is an array of macro values keyed by name, +# likewise P contains macro parameters if any. Preserve backslash +# newline sequences. + +ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* +sed -n ' +s/.\{148\}/&'"$ac_delim"'/g +t rset +:rset +s/^[ ]*#[ ]*define[ ][ ]*/ / +t def +d +:def +s/\\$// +t bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3"/p +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p +d +:bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3\\\\\\n"\\/p +t cont +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p +t cont +d +:cont +n +s/.\{148\}/&'"$ac_delim"'/g +t clear +:clear +s/\\$// +t bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/"/p +d +:bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p +b cont +' >$CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + for (key in D) D_is_set[key] = 1 + FS = "" +} +/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { + line = \$ 0 + split(line, arg, " ") + if (arg[1] == "#") { + defundef = arg[2] + mac1 = arg[3] + } else { + defundef = substr(arg[1], 2) + mac1 = arg[2] + } + split(mac1, mac2, "(") #) + macro = mac2[1] + prefix = substr(line, 1, index(line, defundef) - 1) + if (D_is_set[macro]) { + # Preserve the white space surrounding the "#". + print prefix "define", macro P[macro] D[macro] + next + } else { + # Replace #undef with comments. This is necessary, for example, + # in the case of _POSIX_SOURCE, which is predefined and required + # on some systems where configure will not decide to define it. + if (defundef == "undef") { + print "/*", prefix defundef, macro, "*/" + next + } + } +} +{ print } +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 +fi # test -n "$CONFIG_HEADERS" + -for ac_tag in :F $CONFIG_FILES :H $CONFIG_HEADERS +eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS " +shift +for ac_tag do case $ac_tag in :[FHLC]) ac_mode=$ac_tag; continue;; esac case $ac_mode$ac_tag in :[FHL]*:*);; - :L* | :C*:*) { { echo "$as_me:$LINENO: error: Invalid tag $ac_tag." >&5 -echo "$as_me: error: Invalid tag $ac_tag." >&2;} - { (exit 1); exit 1; }; };; + :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; :[FH]-) ac_tag=-:-;; :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; esac @@ -12908,7 +9470,7 @@ echo "$as_me: error: Invalid tag $ac_tag." >&2;} for ac_f do case $ac_f in - -) ac_f="$tmp/stdin";; + -) ac_f="$ac_tmp/stdin";; *) # Look for the file first in the build tree, then in the source tree # (if the path is not absolute). The absolute path cannot be DOS-style, # because $ac_f cannot contain `:'. @@ -12917,26 +9479,34 @@ echo "$as_me: error: Invalid tag $ac_tag." >&2;} [\\/$]*) false;; *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; esac || - { { echo "$as_me:$LINENO: error: cannot find input file: $ac_f" >&5 -echo "$as_me: error: cannot find input file: $ac_f" >&2;} - { (exit 1); exit 1; }; };; + as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; esac - ac_file_inputs="$ac_file_inputs $ac_f" + case $ac_f in *\'*) ac_f=`printf "%s\n" "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac + as_fn_append ac_file_inputs " '$ac_f'" done # Let's still pretend it is `configure' which instantiates (i.e., don't # use $as_me), people would be surprised to read: # /* config.h. Generated by config.status. */ - configure_input="Generated from "`IFS=: - echo $* | sed 's|^[^:]*/||;s|:[^:]*/|, |g'`" by configure." + configure_input='Generated from '` + printf "%s\n" "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' + `' by configure.' if test x"$ac_file" != x-; then configure_input="$ac_file. $configure_input" - { echo "$as_me:$LINENO: creating $ac_file" >&5 -echo "$as_me: creating $ac_file" >&6;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 +printf "%s\n" "$as_me: creating $ac_file" >&6;} fi + # Neutralize special characters interpreted by sed in replacement strings. + case $configure_input in #( + *\&* | *\|* | *\\* ) + ac_sed_conf_input=`printf "%s\n" "$configure_input" | + sed 's/[\\\\&|]/\\\\&/g'`;; #( + *) ac_sed_conf_input=$configure_input;; + esac case $ac_tag in - *:-:* | *:-) cat >"$tmp/stdin";; + *:-:* | *:-) cat >"$ac_tmp/stdin" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; esac ;; esac @@ -12946,42 +9516,7 @@ $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$ac_file" : 'X\(//\)[^/]' \| \ X"$ac_file" : 'X\(//\)$' \| \ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || -echo X"$ac_file" | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ - s//\1/ - q - } - /^X\(\/\/\)[^/].*/{ - s//\1/ - q - } - /^X\(\/\/\)$/{ - s//\1/ - q - } - /^X\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'` - { as_dir="$ac_dir" - case $as_dir in #( - -*) as_dir=./$as_dir;; - esac - test -d "$as_dir" || { $as_mkdir_p && mkdir -p "$as_dir"; } || { - as_dirs= - while :; do - case $as_dir in #( - *\'*) as_qdir=`echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #( - *) as_qdir=$as_dir;; - esac - as_dirs="'$as_qdir' $as_dirs" - as_dir=`$as_dirname -- "$as_dir" || -$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$as_dir" : 'X\(//\)[^/]' \| \ - X"$as_dir" : 'X\(//\)$' \| \ - X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || -echo X"$as_dir" | +printf "%s\n" X"$ac_file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q @@ -12999,20 +9534,15 @@ echo X"$as_dir" | q } s/.*/./; q'` - test -d "$as_dir" && break - done - test -z "$as_dirs" || eval "mkdir $as_dirs" - } || test -d "$as_dir" || { { echo "$as_me:$LINENO: error: cannot create directory $as_dir" >&5 -echo "$as_me: error: cannot create directory $as_dir" >&2;} - { (exit 1); exit 1; }; }; } + as_dir="$ac_dir"; as_fn_mkdir_p ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) - ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` + ac_dir_suffix=/`printf "%s\n" "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. - ac_top_builddir_sub=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,/..,g;s,/,,'` + ac_top_builddir_sub=`printf "%s\n" "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; @@ -13052,12 +9582,12 @@ ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix esac _ACEOF -cat >>$CONFIG_STATUS <<\_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # If the template does not know about datarootdir, expand it. # FIXME: This hack should be removed a few years after 2.60. ac_datarootdir_hack=; ac_datarootdir_seen= - -case `sed -n '/datarootdir/ { +ac_sed_dataroot=' +/datarootdir/ { p q } @@ -13065,36 +9595,37 @@ case `sed -n '/datarootdir/ { /@docdir@/p /@infodir@/p /@localedir@/p -/@mandir@/p -' $ac_file_inputs` in +/@mandir@/p' +case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in *datarootdir*) ac_datarootdir_seen=yes;; *@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) - { echo "$as_me:$LINENO: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 -echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 +printf "%s\n" "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} _ACEOF -cat >>$CONFIG_STATUS <<_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_datarootdir_hack=' s&@datadir@&$datadir&g s&@docdir@&$docdir&g s&@infodir@&$infodir&g s&@localedir@&$localedir&g s&@mandir@&$mandir&g - s&\\\${datarootdir}&$datarootdir&g' ;; + s&\\\${datarootdir}&$datarootdir&g' ;; esac _ACEOF # Neutralize VPATH when `$srcdir' = `.'. # Shell code in configure.ac might set extrasub. # FIXME: do we really want to maintain this feature? -cat >>$CONFIG_STATUS <<_ACEOF - sed "$ac_vpsub +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_sed_extra="$ac_vpsub $extrasub _ACEOF -cat >>$CONFIG_STATUS <<\_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 :t /@[a-zA-Z_][a-zA-Z_0-9]*@/!b -s&@configure_input@&$configure_input&;t t +s|@configure_input@|$ac_sed_conf_input|;t t s&@top_builddir@&$ac_top_builddir_sub&;t t +s&@top_build_prefix@&$ac_top_build_prefix&;t t s&@srcdir@&$ac_srcdir&;t t s&@abs_srcdir@&$ac_abs_srcdir&;t t s&@top_srcdir@&$ac_top_srcdir&;t t @@ -13104,119 +9635,49 @@ s&@abs_builddir@&$ac_abs_builddir&;t t s&@abs_top_builddir@&$ac_abs_top_builddir&;t t s&@INSTALL@&$ac_INSTALL&;t t $ac_datarootdir_hack -" $ac_file_inputs | sed -f "$tmp/subs-1.sed" >$tmp/out +" +eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ + >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && - { ac_out=`sed -n '/\${datarootdir}/p' "$tmp/out"`; test -n "$ac_out"; } && - { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' "$tmp/out"`; test -z "$ac_out"; } && - { echo "$as_me:$LINENO: WARNING: $ac_file contains a reference to the variable \`datarootdir' -which seems to be undefined. Please make sure it is defined." >&5 -echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' -which seems to be undefined. Please make sure it is defined." >&2;} - - rm -f "$tmp/stdin" + { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && + { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ + "$ac_tmp/out"`; test -z "$ac_out"; } && + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&5 +printf "%s\n" "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&2;} + + rm -f "$ac_tmp/stdin" case $ac_file in - -) cat "$tmp/out"; rm -f "$tmp/out";; - *) rm -f "$ac_file"; mv "$tmp/out" $ac_file;; - esac + -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; + *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; + esac \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; :H) # # CONFIG_HEADER # -_ACEOF - -# Transform confdefs.h into a sed script `conftest.defines', that -# substitutes the proper values into config.h.in to produce config.h. -rm -f conftest.defines conftest.tail -# First, append a space to every undef/define line, to ease matching. -echo 's/$/ /' >conftest.defines -# Then, protect against being on the right side of a sed subst, or in -# an unquoted here document, in config.status. If some macros were -# called several times there might be several #defines for the same -# symbol, which is useless. But do not sort them, since the last -# AC_DEFINE must be honored. -ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* -# These sed commands are passed to sed as "A NAME B PARAMS C VALUE D", where -# NAME is the cpp macro being defined, VALUE is the value it is being given. -# PARAMS is the parameter list in the macro definition--in most cases, it's -# just an empty string. -ac_dA='s,^\\([ #]*\\)[^ ]*\\([ ]*' -ac_dB='\\)[ (].*,\\1define\\2' -ac_dC=' ' -ac_dD=' ,' - -uniq confdefs.h | - sed -n ' - t rset - :rset - s/^[ ]*#[ ]*define[ ][ ]*// - t ok - d - :ok - s/[\\&,]/\\&/g - s/^\('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/ '"$ac_dA"'\1'"$ac_dB"'\2'"${ac_dC}"'\3'"$ac_dD"'/p - s/^\('"$ac_word_re"'\)[ ]*\(.*\)/'"$ac_dA"'\1'"$ac_dB$ac_dC"'\2'"$ac_dD"'/p - ' >>conftest.defines - -# Remove the space that was appended to ease matching. -# Then replace #undef with comments. This is necessary, for -# example, in the case of _POSIX_SOURCE, which is predefined and required -# on some systems where configure will not decide to define it. -# (The regexp can be short, since the line contains either #define or #undef.) -echo 's/ $// -s,^[ #]*u.*,/* & */,' >>conftest.defines - -# Break up conftest.defines: -ac_max_sed_lines=50 - -# First sed command is: sed -f defines.sed $ac_file_inputs >"$tmp/out1" -# Second one is: sed -f defines.sed "$tmp/out1" >"$tmp/out2" -# Third one will be: sed -f defines.sed "$tmp/out2" >"$tmp/out1" -# et cetera. -ac_in='$ac_file_inputs' -ac_out='"$tmp/out1"' -ac_nxt='"$tmp/out2"' - -while : -do - # Write a here document: - cat >>$CONFIG_STATUS <<_ACEOF - # First, check the format of the line: - cat >"\$tmp/defines.sed" <<\\CEOF -/^[ ]*#[ ]*undef[ ][ ]*$ac_word_re[ ]*\$/b def -/^[ ]*#[ ]*define[ ][ ]*$ac_word_re[( ]/b def -b -:def -_ACEOF - sed ${ac_max_sed_lines}q conftest.defines >>$CONFIG_STATUS - echo 'CEOF - sed -f "$tmp/defines.sed"' "$ac_in >$ac_out" >>$CONFIG_STATUS - ac_in=$ac_out; ac_out=$ac_nxt; ac_nxt=$ac_in - sed 1,${ac_max_sed_lines}d conftest.defines >conftest.tail - grep . conftest.tail >/dev/null || break - rm -f conftest.defines - mv conftest.tail conftest.defines -done -rm -f conftest.defines conftest.tail - -echo "ac_result=$ac_in" >>$CONFIG_STATUS -cat >>$CONFIG_STATUS <<\_ACEOF if test x"$ac_file" != x-; then - echo "/* $configure_input */" >"$tmp/config.h" - cat "$ac_result" >>"$tmp/config.h" - if diff $ac_file "$tmp/config.h" >/dev/null 2>&1; then - { echo "$as_me:$LINENO: $ac_file is unchanged" >&5 -echo "$as_me: $ac_file is unchanged" >&6;} + { + printf "%s\n" "/* $configure_input */" >&1 \ + && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" + } >"$ac_tmp/config.h" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 +printf "%s\n" "$as_me: $ac_file is unchanged" >&6;} else - rm -f $ac_file - mv "$tmp/config.h" $ac_file + rm -f "$ac_file" + mv "$ac_tmp/config.h" "$ac_file" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 fi else - echo "/* $configure_input */" - cat "$ac_result" + printf "%s\n" "/* $configure_input */" >&1 \ + && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ + || as_fn_error $? "could not create -" "$LINENO" 5 fi - rm -f "$tmp/out12" ;; @@ -13225,11 +9686,13 @@ echo "$as_me: $ac_file is unchanged" >&6;} done # for ac_tag -{ (exit 0); exit 0; } +as_fn_exit 0 _ACEOF -chmod +x $CONFIG_STATUS ac_clean_files=$ac_clean_files_save +test $ac_write_fail = 0 || + as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 + # configure is writing to config.log, and then calls config.status. # config.status does its own redirection, appending to config.log. @@ -13249,6 +9712,11 @@ if test "$no_create" != yes; then exec 5>>config.log # Use ||, not &&, to avoid exiting from the if with $? = 1, which # would make configure fail if this is the last instruction. - $ac_cs_success || { (exit 1); exit 1; } + $ac_cs_success || as_fn_exit 1 fi +if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 +printf "%s\n" "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} +fi + diff --git a/cextern/wcslib/configure.ac b/cextern/wcslib/configure.ac index e3a1565d315d..d623a8f9eb30 100644 --- a/cextern/wcslib/configure.ac +++ b/cextern/wcslib/configure.ac @@ -1,19 +1,14 @@ #----------------------------------------------------------------------------- # Process this file with autoconf-2.53 or later to produce a configure script. #----------------------------------------------------------------------------- -# N.B. it is necessary to run autoconf on a Mac in order for configure to -# locate the X11 dylibs for PGPLOT. Use autoconf-2.61 in MacOSX 10.6.2 or -# later to avoid spurious messages about deleting conftest.dSYM when -# configuring in MacOSX 10.6. -# # Author: Mark Calabretta, Australia Telescope National Facility, CSIRO. -# http://www.atnf.csiro.au/people/Mark.Calabretta -# $Id: configure.ac,v 4.14 2012/07/13 10:02:44 cal103 Exp $ +# http://www.atnf.csiro.au/computing/software/wcs +# $Id: configure.ac,v 8.6 2026/03/29 13:53:57 mcalabre Exp $ #----------------------------------------------------------------------------- -AC_INIT([WCSLIB], [4.14], [mark@calabretta.id.au], [wcslib-4.14]) -AC_PREREQ([2.53]) -AC_REVISION([$Revision: 4.14 $]) +AC_INIT([WCSLIB],[8.6],[mark@calabretta.id.au],[wcslib-8.6]) +AC_PREREQ([2.71]) +AC_REVISION([$Revision: 8.6 $]) AC_SUBST([PACKAGE_VERSION]) AC_DEFINE_UNQUOTED([WCSLIB_VERSION], [$PACKAGE_VERSION], [Define wcslib version]) @@ -25,35 +20,52 @@ AC_CONFIG_SRCDIR([C/wcs.h]) AC_CONFIG_AUX_DIR([config]) # Get the system type. -AC_CANONICAL_BUILD -ARCH="${build_cpu}-$build_os" +AC_CANONICAL_HOST +ARCH="${host_cpu}-$host_os" AC_SUBST([ARCH]) # Look for Flex. -AC_CHECK_PROG([FLEX], [flex], [flex], [], [], []) -if test "x$FLEX" = xflex ; then - # Version 2.5.9 or later is required. - V=`flex --version | awk '{print $NF}'` - W=`echo $V | awk -F. '{if ((($1*100 + $2)*100 + $3) < 20509) print "no"}'` - if test "x$W" != x ; then - AC_MSG_NOTICE([Flex version $V is too old, ignored.]) - FLEX= - else - AC_MSG_NOTICE([Using Flex version $V.]) +AC_ARG_ENABLE([flex], [AS_HELP_STRING([--disable-flex], + [don't apply flex (use pre-generated sources)])], []) +if test "x$enable_flex" = xno ; then + FLEX= + AC_MSG_WARN([Generation of flex sources disabled by request, using + pre-generated sources.]) + +else + AC_CHECK_PROG([FLEX], [flex], [flex], [], [], []) + if test "x$FLEX" = xflex ; then + # Version 2.6.0 or later is required. + V=`flex --version | awk '{print $2}'` + W=`echo $V | awk -F. '{if ((($1*100 + $2)*100 + $3) < 20600) print "no"}'` + if test "x$W" != x ; then + AC_MSG_WARN([Flex version $V is too old, ignored.]) + FLEX= + else + AC_MSG_NOTICE([Using Flex version $V.]) + fi fi -fi -if test "x$FLEX" = x ; then - AC_MSG_WARN([Flex version 2.5.9 or later does not appear to be + if test "x$FLEX" = x ; then + AC_MSG_WARN([Flex version 2.6.0 or later does not appear to be available, will use pre-generated sources.]) + fi fi # Look for an ANSI C compiler. AC_PROG_CPP AC_PROG_CC -AC_PROG_CC_STDC +if test "x$ac_cv_c_compiler_gnu" = xyes ; then + # Get gcc version number. + GCC_VERSION=`$CC -dumpfullversion` + AC_MSG_NOTICE([Using gcc version $GCC_VERSION]) +else + GCC_VERSION= +fi +AC_SUBST([GCC_VERSION]) + AC_C_CONST AC_TYPE_SIZE_T if test "x$ac_cv_prog_cc_stdc" = xno -o \ @@ -67,40 +79,38 @@ if test "x$ac_cv_prog_cc_stdc" = xno -o \ -------------------------------------------------------], [1]) fi -# Check for data types (suggested by autoscan - off_t is only required by -# fitshdr). -AC_TYPE_OFF_T -AC_TYPE_INT8_T -AC_TYPE_INT16_T -AC_TYPE_INT32_T -AC_TYPE_UINT8_T +# Data types used in wcs.c (results not currently used). AC_TYPE_UINT16_T AC_TYPE_UINT32_T -# Check for ANSI C headers. -AC_HEADER_STDC -AC_CHECK_HEADERS([ctype.h errno.h limits.h locale.h math.h setjmp.h stdarg.h \ - stdio.h stdlib.h string.h]) -if test "x$ac_cv_header_stdc" = xno; then +# Check for standard C header files required to compile the library. +headers= +AC_CHECK_HEADERS([ctype.h inttypes.h limits.h locale.h math.h setjmp.h + stdarg.h stddef.h stdint.h stdio.h stdlib.h string.h], + [], [headers=no]) +if test "x$headers" = xno; then AC_MSG_ERROR([ ------------------------------------------------------------------- An ANSI standard C library is required to build WCSLIB. One of the - ANSI C header files it requires is missing or unusable. + standard C header files it requires is missing or unusable. Please + refer to the above log. ERROR: WCSLIB configuration failure. -------------------------------------------------------------------], [1]) fi -# Checks for ANSI C library functions (suggested by autoscan - fseeko and -# stat are only required by fitshdr). +# Flex uses fileno() and other POSIX features whose prototypes are only +# available from glibc's stdio.h with an appropriate preprocessor macro +# definition. This cannot be set within the flex description file itself +# as stdio.h is included in the generated C code before any part of the +# description. See fileno(3) and feature_test_macros(7). +if test "x$ac_cv_c_compiler_gnu" = xyes ; then + FLFLAGS="$FLFLAGS -D_POSIX_C_SOURCE=1" +fi +AC_SUBST([FLFLAGS]) + +# Check for libm. AC_CHECK_LIB([m], [floor]) -AC_FUNC_FSEEKO -AC_FUNC_MALLOC -AC_FUNC_REALLOC -AC_FUNC_SETVBUF_REVERSED -AC_FUNC_STAT -AC_FUNC_VPRINTF -AC_CHECK_FUNCS([floor memset pow setlocale sqrt strchr strstr]) # System libraries that may be required by WCSLIB itself. # SunOS, extra maths functions. @@ -171,7 +181,7 @@ fi if test "x$F77" = xno ; then F77= - AC_MSG_WARN([Compilation of Fortran wrappers and PGSBOX disabled]) + AC_MSG_WARN([Compilation of Fortran wrappers and PGSBOX disabled.]) else if test "x$F77" = x ; then @@ -212,9 +222,46 @@ else # Fortran object modules such as pgsbox. AC_F77_LIBRARY_LDFLAGS + # Tidy up FLIBS. + dirs= + libs= + for flib in $FLIBS + do + case "$flib" in + -L*) + dir=`echo "$flib" | sed -e 's/-L//'` + dir=-L`cd "$dir" && pwd` + dirs="$dirs $dir" + ;; + *) + libs="$libs $flib" + ;; + esac + done + + dirs=`for dir in $dirs ; do echo "$dir" ; done | sort -u | xargs` + + FLIBS="$dirs$libs" + # F77 name mangling (defines the F77_FUNC preprocessor macro). AC_F77_WRAPPERS + if test "x$BINDC" = x ; then + AC_ARG_WITH([bindc], [AS_HELP_STRING([--with-bindc], + [use Fortran 2003 BIND(C) wrappers - recommended for Link Time + Optimization (LTO)])], []) + if test "x$with_bindc" = xyes ; then + BINDC=yes + fi + fi + + if test "x$BINDC" = xyes ; then + AC_MSG_NOTICE([using Fortran 2003 BIND(C) wrappers.]) + else + BINDC= + fi + AC_SUBST([BINDC]) + SUBDIRS="C Fortran" TSTDIRS="C Fortran" INSTDIR="Fortran" @@ -232,47 +279,67 @@ AC_CHECK_LIB([SystemStubs], [printf\$LDBLStub], [LIBS="$LIBS -lSystemStubs"], # Library and installation utilities. #------------------------------------ # Static library generation. +# Ensure "non-deterministic" archives are produced during the build process. +ar rU conftest.a > /dev/null 2>&1 && ARFLAGS="U" +rm -f conftest.a +AC_SUBST([ARFLAGS]) AC_PROG_RANLIB -# Shared library generation. +# Shared library generation - gcc only. +# Ways of disabling shared libraries: +# configure --disable-shared +# configure --enable-shared=no +AC_ARG_ENABLE([shared], [AS_HELP_STRING([--disable-shared], + [don't build the WCS shared libraries])], []) + +SHRLIB= +SONAME= +SHRFLAGS= +SHRLD= +SHRSFX= +SHRLN= + if test "x$ac_cv_c_compiler_gnu" = xyes ; then - SHVER=`echo "$LIBVER" | sed -e 's/\..*$//'` - - # Note that -fPIC is on by default for Macs, this just makes it obvious. - SHRFLAGS="-fPIC" - SHRLD="\$(CC) \$(SHRFLAGS)" - - case "$build_os" in - darwin*) - SHRLIB="libwcs.$LIBVER.dylib" - SONAME="libwcs.$SHVER.dylib" - SHRLD="$SHRLD -dynamiclib -single_module" - SHRLD="$SHRLD -compatibility_version $SHVER -current_version $LIBVER" - SHRLN= - - case "$build_cpu" in - powerpc*) - # Switch off -fPIC (not applicable for PowerPC Macs). - CFLAGS="$CFLAGS -mdynamic-no-pic" + if test "x$enable_shared" = xno ; then + AC_MSG_WARN([Generation of WCS shared libraries disabled.]) + + else + SHVER=`echo "$LIBVER" | sed -e 's/\..*$//'` + + # Note that -fPIC is on by default for Macs, this just makes it obvious. + SHRFLAGS="-fPIC" + SHRLD="\$(CC) \$(SHRFLAGS)" + + case "$host_os" in + darwin*) + SHRLIB="libwcs.$LIBVER.dylib" + SONAME="libwcs.$SHVER.dylib" + SHRLD="$SHRLD -dynamiclib -single_module" + SHRLD="$SHRLD -compatibility_version $SHVER -current_version $LIBVER -install_name \$(SONAME)" + SHRLN="libwcs.dylib" + + case "$host_cpu" in + powerpc*) + # Switch off -fPIC (not applicable for PowerPC Macs). + CFLAGS="$CFLAGS -mdynamic-no-pic" + ;; + esac + ;; + *mingw*) + SHRLIB="libwcs.dll.$LIBVER" + SONAME="libwcs.dll.$SHVER" + SHRLD="$SHRLD -shared -Wl,-h\$(SONAME)" + SHRLN="libwcs.dll" + ;; + *) + # Covers Linux and Solaris at least. + SHRLIB="libwcs.so.$LIBVER" + SONAME="libwcs.so.$SHVER" + SHRLD="$SHRLD -shared -Wl,-h\$(SONAME)" + SHRLN="libwcs.so" ;; esac - ;; - *) - # Covers Linux and Solaris at least. - SHRLIB="libwcs.so.$LIBVER" - SONAME="libwcs.so.$SHVER" - SHRLD="$SHRLD -shared -Wl,-h\$(SONAME)" - SHRLN="libwcs.so" - ;; - esac - -else - SHRLIB= - SONAME= - SHRFLAGS= - SHRLD= - SHRSFX= - SHRLN= + fi fi AC_SUBST([SHRLIB]) @@ -286,6 +353,11 @@ AC_SUBST([SHRLN]) AC_PROG_LN_S AC_PROG_INSTALL +# Older versions of GNU make do not have the -O option, which only facilitates +# legibility of the output from parallel builds (make -j). +make --help | grep '\-O' >/dev/null 2>&1 && MAKEFLAGS="-Otarget" +AC_SUBST([MAKEFLAGS]) + AC_MSG_NOTICE([End of primary configuration. ]) @@ -294,13 +366,24 @@ AC_MSG_NOTICE([End of primary configuration. # ---------------------------------------------------------------- AC_MSG_NOTICE([Looking for libraries etc. for utilities and test suite...]) -# Check for other quasi-standard header files. -AC_CHECK_HEADERS([unistd.h]) +# Additional standard C header files required. +headers= +AC_CHECK_HEADERS([errno.h time.h], [], [headers=no]) + +# Other header files required by *nix library functions. +AC_CHECK_HEADERS([sys/stat.h sys/types.h unistd.h], [], [headers=no]) +if test "x$headers" = xno; then + AC_MSG_WARN([ + -------------------------------------------------------------------- + One or more of the header files required to compile the utilities + and/or test programs is missing. Continuing on a best-effort basis. + --------------------------------------------------------------------]) +fi -# Large file support. +# Large file support (only required by fitshdr utility). AC_FUNC_FSEEKO AC_SYS_LARGEFILE - +AC_TYPE_OFF_T # Extra places to look for third-party libraries and header files. LIBDIRS= @@ -308,7 +391,7 @@ LIBDIRS= AC_ARG_WITH([cfitsio], [AS_HELP_STRING([--without-cfitsio], [eschew CFITSIO])], []) if test "x$with_cfitsio" = xno ; then - AC_MSG_WARN([CFITSIO disabled]) + AC_MSG_WARN([CFITSIO disabled.]) else AC_ARG_WITH([cfitsiolib], [AS_HELP_STRING([--with-cfitsiolib=DIR], [directory containing cfitsio library])], []) @@ -334,7 +417,7 @@ fi AC_ARG_WITH([pgplot], [AS_HELP_STRING([--without-pgplot], [eschew PGPLOT])], []) if test "x$with_pgplot" = xno ; then - AC_MSG_WARN([PGPLOT disabled]) + AC_MSG_WARN([PGPLOT disabled.]) else AC_ARG_WITH([pgplotlib], [AS_HELP_STRING([--with-pgplotlib=DIR], [directory containing pgplot library])], []) @@ -407,6 +490,10 @@ if test "x$with_cfitsio" != xno -o \ getwcstab.c to compile test programs.]) fi fi + + AC_SUBST([CFITSIOINC]) + AC_SUBST([CFITSIOLIB]) + AC_SUBST([GETWCSTAB]) fi # PGPLOT. @@ -483,6 +570,9 @@ if test "x$with_cfitsio" != xno -o \ TSTDIRS="$TSTDIRS pgsbox" fi fi + + AC_SUBST([PGPLOTINC]) + AC_SUBST([PGPLOTLIB]) fi @@ -492,28 +582,50 @@ fi # configure --enable-utils=no AC_ARG_ENABLE([utils], [AS_HELP_STRING([--disable-utils], [don't build the WCS utilities])], []) -if test "x$enable_utils" != xno ; then +if test "x$enable_utils" = xno ; then + AC_MSG_WARN([Compilation of WCS utilities disabled.]) +else SUBDIRS="$SUBDIRS utils" INSTDIR="$INSTDIR utils" -else - AC_MSG_WARN([Compilation of WCS utilities disabled]) fi - -AC_SUBST([CFITSIOINC]) -AC_SUBST([CFITSIOLIB]) -AC_SUBST([GETWCSTAB]) - -AC_SUBST([PGPLOTINC]) -AC_SUBST([PGPLOTLIB]) - AC_SUBST([SUBDIRS]) AC_SUBST([TSTDIRS]) AC_SUBST([INSTDIR]) + +# Default observer coordinates for sundazel. +if test -f "$HOME/.sundazelrc"; then + . "$HOME/.sundazelrc" +fi + +if test "x$OBSLNG" = x; then + OBSLNG=0.0 + OBSLAT=0.0 + OBSTZ=0.0 +fi + +AC_SUBST([OBSLNG]) +AC_SUBST([OBSLAT]) +AC_SUBST([OBSTZ]) + + +# Tidy up incrementally defined variables. +FLFLAGS=`echo $FLFLAGS` +CPPFLAGS=`echo $CPPFLAGS` +CFLAGS=`echo $CFLAGS` +FFLAGS=`echo $FFLAGS` +LDFLAGS=`echo $LDFLAGS` + AC_MSG_NOTICE([End of auxiliary configuration. ]) +# Set from the environment for code development. +AC_SUBST([FLAVOUR]) +AC_SUBST([MODE]) +AC_SUBST([VALGRIND]) +AC_SUBST([EXTRA_CLEAN]) + # Do it. AC_MSG_NOTICE([Configuring files...]) diff --git a/cextern/wcslib/flavours b/cextern/wcslib/flavours index d17f632c92fe..fbc2d897cdce 100644 --- a/cextern/wcslib/flavours +++ b/cextern/wcslib/flavours @@ -1,160 +1,137 @@ #----------------------------------------------------------------------------- -# Makefile overrides for various combinations of architecture, operating -# system and compiler. Used for development and testing only, not required -# for building WCSLIB. +# Variable definitions for various combinations of architecture, operating +# system, compiler, and purpose. Used for development and testing only, not +# required for building WCSLIB. # -# Variables like CC and CFLAGS are exported into the environment so that they -# will be seen by 'configure'. Thus, normal usage is as follows: +# Definitions in this file are only used when running 'configure'. Variables +# such as CC and CFLAGS are exported to the environment so that they will be +# seen by 'configure' and from there passed to makedefs. Thus, normal usage +# is as follows: # # make distclean -# make FLAVOUR=Linux configure -# make +# make FLAVOUR=dev configure +# +# At that point the definitions here should have propagated to makedefs. +# +# WARNING: configure may ignore compiler options added to the CC or F77 +# environment variables. They must be added to CFLAGS or FFLAGS. # # Reminder: add '-d' to FLFLAGS for debugging. # -# $Id: flavours,v 4.14 2012/07/13 10:02:44 cal103 Exp $ +# $Id: flavours,v 8.6 2026/03/29 13:53:57 mcalabre Exp $ #----------------------------------------------------------------------------- -# The list of FLAVOURs can be set on the command line. -F := $(shell echo $(FLAVOURS) | tr a-z A-Z) -ifeq "$F" "" - F := - FLAVOURS := "" -endif +F := -ifeq "$F" "LINUX" - override FLAVOURS := "" Linux Linuxp +ifeq "$(FLAVOUR)" "" + F := default + override FLAVOUR := default endif -ifeq "$F" "SUN" - override FLAVOURS := "" SUN/GNU SUN/GNU3 SUN/GNUp SUN/ANSI +ifeq "$(FLAVOUR)" "dev" + # Currently gcc 11.4.0. + F := development + CC := gcc + F77 := gfortran endif -ifeq "$F" "PURE" - override FLAVOURS := SUN/Pure SUN/Quant +ifeq "$(FLAVOUR)" "dev12" + # Currently gcc 12.3.0. + F := development + CC := gcc-12 + F77 := gfortran-12 endif -F := - - -# Various C standards handled by features.h in Linux. -FEATURES := -ifeq "$(notdir $(shell pwd))" "utils" - # To get off_t for fseeko() usage in fitshdr when gcc is invoked with the - # -std=c89 (same as -ansi) or the -std=c99 options. - FEATURES := -D_XOPEN_SOURCE +# Compiler optimization level. +ifndef OPT + OPT := 0 endif -# Linux with gcc/gfortran (also works for Darwin). -ifeq "$(FLAVOUR)" "Linux" - F := $(FLAVOUR) - export CC := gcc -std=c89 -pedantic - export CPPFLAGS := $(FEATURES) - export CFLAGS := -g -O0 -Wall -Wpadded -Wno-long-long - export FFLAGS := -g -O0 -fimplicit-none -Wall -I. - VALGRIND := valgrind -v --leak-check=yes +# Quench warnings about padding in foreign structs, particularly in fitsio.h. +ifneq "$(findstring $(SUBDIR),C Fortran pgsbox)" "" + WPADDED := -Wpadded endif -ifeq "$(FLAVOUR)" "Linuxp" - F := $(FLAVOUR) - export CC := gcc -std=c89 -pedantic - export CPPFLAGS := $(FEATURES) - export CFLAGS := -pg -g -O -Wall -Wpadded -Wno-long-long - export FFLAGS := -pg -a -g -O -fimplicit-none -Wall -I. - export LDFLAGS := -pg -g $(filter -L%, $(LDFLAGS)) - override EXTRA_CLEAN := gmon.out bb.out -endif - - -# Solaris with gcc/gfortran 4.x (lynx). -ifeq "$(FLAVOUR)" "SUN/GNU" - F := $(FLAVOUR) - export CC := gcc -std=c89 - export CPPFLAGS := $(FEATURES) - export CFLAGS := -g -Wall -Wpadded -Wno-long-long - export F77 := gfortran - export FFLAGS := -g -fimplicit-none -Wall -I. - LD := gcc +ifeq "$F" "development" + # Options for code development with gcc/gfortran. +# INSTRUMENT := -fsanitize=address # appears to be broken. + INSTRUMENT := -fsanitize=undefined + INSTRUMENT += -fstack-protector-strong + CWARNINGS := -Wall -Wextra -Wno-clobbered -Wno-long-long + ifeq "$(INSTRUMENT)" "" + # The instrumentation options produce copious "padding" warnings. + CWARNINGS += $(WPADDED) + endif + FWARNINGS := -Wall -Wno-surprising + export CC := $(CC) + export CPPFLAGS := -D_FORTIFY_SOURCE=2 + export CFLAGS := -std=c99 -pedantic + export CFLAGS += -g -O$(OPT) $(INSTRUMENT) $(CWARNINGS) + export F77 := $(F77) + export FFLAGS := -g -O$(OPT) -fimplicit-none -I. $(INSTRUMENT) $(FWARNINGS) + export LDFLAGS := $(INSTRUMENT) + ifdef VALGRIND + override VALGRIND := valgrind -v --leak-check=full --show-leak-kinds=all + override VALGRIND += --track-origins=yes + endif endif -ifeq "$(FLAVOUR)" "SUN/GNU3" +ifeq "$(FLAVOUR)" "lto" + # For LTO development. F := $(FLAVOUR) - export CC := gcc-3.1.1 -std=c89 - export CPPFLAGS := $(FEATURES) - export CFLAGS := -g -Wall -Wpadded -Wno-long-long - export F77 := g77-3.1.1 - export FFLAGS := -g -Wimplicit -Wunused -Wno-globals -I. - LD := gcc-3.1.1 -endif - -ifeq "$(FLAVOUR)" "SUN/GNUp" + export BINDC := yes + CWARNINGS := -Wall -Wextra -Wno-clobbered -Wno-long-long + FWARNINGS := -Wall -Wno-surprising + LTOFLAGS := -O1 -flto=4 -Werror=odr -Werror=lto-type-mismatch -Werror=strict-aliasing + export CC := gcc-12 + export CPPFLAGS := -D_FORTIFY_SOURCE=2 + export CFLAGS := -std=c99 -pedantic + export CFLAGS += $(LTOFLAGS) $(CWARNINGS) + export F77 := gfortran-12 + export FFLAGS := -fimplicit-none -I. $(LTOFLAGS) $(FWARNINGS) + export LDFLAGS := $(LTOFLAGS) +endif + +ifeq "$(FLAVOUR)" "profile" + # gcc with profiling (gprof). F := $(FLAVOUR) - export CC := gcc -std=c89 -pedantic - export CPPFLAGS := $(FEATURES) - export CFLAGS := -pg -a -g -O -Wall -Wpadded -Wno-long-long - export FFLAGS := -pg -a -g -O -fimplicit-none -Wall -I. - export LDFLAGS := -pg -a -g $(filter -L%, $(LDFLAGS)) + export CC := gcc + export CPPFLAGS := + export CFLAGS := -std=c99 -pedantic + export CFLAGS += -pg -g -O -Wall -Wextra -Wno-long-long $(WPADDED) + export FFLAGS := -pg -g -O -fimplicit-none -Wall -I. + export LDFLAGS := -pg -g $(filter -L%, $(LDFLAGS)) override EXTRA_CLEAN := gmon.out bb.out endif -# Solaris with SUN cc/f77. -ifeq "$(FLAVOUR)" "SUN/ANSI" - F := $(FLAVOUR) - WCSTRIG := NATIVE - export CC := cc - export CFLAGS := -g -I/usr/local/include - export F77 := f77 - export FFLAGS := -g -erroff=WDECL_LOCAL_NOTUSED - LD := f77 -endif - - -# Purify and quantify in Solaris. -ifeq "$(FLAVOUR)" "SUN/Pure" - F := $(FLAVOUR) - WCSTRIG := NATIVE - export CC := purify gcc - export CFLAGS := -g - export F77 := purify gcc - export FFLAGS := -g -Wimplicit -Wno-globals -I. - export LDFLAGS := $(filter -L%, $(LDFLAGS)) - override EXTRA_CLEAN := *_pure_p*.[ao] *.pcv .pure ../C/*_pure_p*.[ao] -endif - -ifeq "$(FLAVOUR)" "SUN/Quant" - F := $(FLAVOUR) - WCSTRIG := NATIVE - export CC := quantify gcc - export CFLAGS := -g - export F77 := quantify gcc - export FFLAGS := -g -Wimplicit -Wno-globals -I. - export LDFLAGS := $(filter -L%, $(LDFLAGS)) - override EXTRA_CLEAN := *_pure_q*.[ao] .pure -endif - -ifneq "$F" "$(FLAVOUR)" +# Check FLAVOUR. +ifeq "$F" "" override FLAVOUR := unrecognised endif -# gmake uses FC in place of configure's F77. -ifdef F77 - FC := $(F77) +# Check VALGRIND. +ifeq "$(findstring valgrind, $(VALGRIND))" "valgrind" + override MODE := interactive +else + # Unrecognised. + override VALGRIND := endif -ifndef TIMER - TIMER := date +"%a %Y/%m/%d %X %z, executing on $$HOST" +# Check MODE. +ifneq "$(MODE)" "interactive" + # Unrecognised. + override MODE := endif -ifdef FLAVOUR - TIMER := $(TIMER) ; echo " with $(FLAVOUR) FLAVOUR." +# Check EXTRA_CLEAN. +ifeq "$(EXTRA_CLEAN)" "" + EXTRA_CLEAN := endif -show :: - -@ echo 'For code development...' - -@ echo ' FLAVOURS := $(FLAVOURS)' - -@ echo ' FLAVOUR := $(FLAVOUR)' - -@ echo ' VALGRIND := $(VALGRIND)' - -@ echo ' EXTRA_CLEAN := $(EXTRA_CLEAN)' - -@ echo '' +# Pass to configure though the environment. +export FLAVOUR +export VALGRIND +export MODE +export EXTRA_CLEAN diff --git a/cextern/wcslib/makedefs.in b/cextern/wcslib/makedefs.in index 9d8f0690d487..fbc95fdbd2f4 100644 --- a/cextern/wcslib/makedefs.in +++ b/cextern/wcslib/makedefs.in @@ -1,5 +1,5 @@ #----------------------------------------------------------------------------- -# GNU makefile definitions for building WCSLIB 4.14 +# GNU makefile definitions for building WCSLIB 8.6 # # makedefs is generated from makedefs.in by configure. It contains variable # definitions and some general-purpose rules for building WCSLIB. @@ -39,11 +39,11 @@ # compiled separately without this option. # # The shared library will be installed with version number, e.g. as -# libwcs.so.4.14 or libwcs.4.14.dylib with or without the symlink +# libwcs.so.8.6 or libwcs.8.6.dylib with or without the symlink # required to make it visible to the linker (controlled by the SHRLN # variable). On Macs it is deliberately not created because its very # existence precludes static linking with the cctools linker. You can -# still link dynamically by using -lwcs.4.14. +# still link dynamically by using -lwcs.8.6. # # 4) PGPLOT is Tim Pearson's Fortran graphics library with separate C # interface available from astro.caltech.edu. It is only required by @@ -73,23 +73,26 @@ # -DDO_CFITSIO. # # Author: Mark Calabretta, Australia Telescope National Facility, CSIRO. -# http://www.atnf.csiro.au/people/Mark.Calabretta -# $Id: makedefs.in,v 4.14 2012/07/13 10:02:44 cal103 Exp $ +# http://www.atnf.csiro.au/computing/software/wcs +# $Id: makedefs.in,v 8.6 2026/03/29 13:53:57 mcalabre Exp $ #----------------------------------------------------------------------------- # Version. LIBVER := @LIBVER@ WCSLIBPKG := wcslib-@PACKAGE_VERSION@ +# Additional options for GNU make added by configure. + MAKEFLAGS += @MAKEFLAGS@ + # System architecture. ARCH := @ARCH@ # Flex and options. FLEX := @FLEX@ - FLFLAGS := + FLFLAGS := @FLFLAGS@ # C preprocessor and options. CPP := @CPP@ - CPPFLAGS := @DEFS@ + CPPFLAGS := @CPPFLAGS@ WCSTRIG := WRAPPER # C compiler and options. @@ -100,8 +103,12 @@ FC := @F77@ FFLAGS := @FFLAGS@ +# Use the Fortran 2003 BIND(C) wrappers? (Recommended for LTO.) + BINDC := @BINDC@ + # Static object library. WCSLIB := libwcs-$(LIBVER).a + ARFLAGS := @ARFLAGS@ RANLIB := @RANLIB@ # Shared (dynamic) library (see note 3 above). @@ -164,6 +171,12 @@ # Libraries required by WCSLIB itself. LIBS := @LIBS@ +# Default observer coordinates for sundazel. May be set as environment +# variables, either generally or in $HOME/.sundazelrc, which is read by +# configure. + OBSLNG := @OBSLNG@ + OBSLAT := @OBSLAT@ + OBSTZ := @OBSTZ@ #----------------------------------------------------------------------------- # You shouldn't need to change anything below here. @@ -173,9 +186,13 @@ SHELL := /bin/sh VPATH := .. # Common targets. -.PHONY : all build printenv show +.PHONY : all build FORCE printenv show + +all : show + -@ echo '' + @ $(MAKE) build -all : show build +FORCE : # Print the environment as seen by makefile rules. printenv : @@ -187,6 +204,7 @@ show :: wcsconfig.h -@ uname -a -@ echo '' -@ $(MAKE) --version | head -1 + -@ echo ' SUBDIR := $(SUBDIR)' -@ echo ' MAKEFLAGS := $(MAKEFLAGS)' -@ echo '' -@ echo 'For building and installing $(WCSLIBPKG)...' @@ -197,10 +215,15 @@ show :: wcsconfig.h -@ echo ' CPPFLAGS := $(CPPFLAGS)' -@ echo ' WCSTRIG := $(WCSTRIG)' -@ echo ' CC := $(CC)' + -@ if [ "@GCC_VERSION@" ] ; then \ + echo ' GCC version is @GCC_VERSION@' ; \ + fi -@ echo ' CFLAGS := $(CFLAGS)' -@ echo ' FC := $(FC)' -@ echo ' FFLAGS := $(FFLAGS)' + -@ echo ' BINDC := $(BINDC)' -@ echo ' WCSLIB := $(WCSLIB)' + -@ echo ' ARFLAGS := $(ARFLAGS)' -@ echo ' RANLIB := $(RANLIB)' -@ echo ' SHRLIB := $(SHRLIB)' -@ echo ' SONAME := $(SONAME)' @@ -235,6 +258,30 @@ show :: wcsconfig.h -@ echo ' FLIBS := $(FLIBS)' -@ echo ' LIBS := $(LIBS)' -@ echo '' + -@ echo 'Default observer coordinates for sundazel...' + -@ echo ' OBSLNG := $(OBSLNG)' + -@ echo ' OBSLAT := $(OBSLAT)' + -@ echo ' OBSTZ := $(OBSTZ)' + -@ echo '' + -@ echo 'For code development...' + -@ echo ' FLAVOUR := $(FLAVOUR)' + -@ echo ' MODE := $(MODE)' + -@ echo ' VALGRIND := $(VALGRIND)' + -@ echo ' EXTRA_CLEAN := $(EXTRA_CLEAN)' + -@ echo '' + +# For code development. +FLAVOUR := @FLAVOUR@ +MODE := @MODE@ +VALGRIND := @VALGRIND@ +EXTRA_CLEAN := @EXTRA_CLEAN@ + +# Check MODE. +ifeq "$(MODE)" "interactive" + # Important not to have output batched when running the test programs. + MAKEFLAGS := $(filter-out -Otarget,$(MAKEFLAGS)) -Onone +endif -# Code development overrides, for use in the code subdirectories. --include ../flavours +ifneq "$(FLAVOUR)" "default" + TIMER := $(TIMER) ; echo " with $(FLAVOUR) FLAVOUR." +endif diff --git a/cextern/wcslib/wcsconfig.h.in b/cextern/wcslib/wcsconfig.h.in index 206c99dea4ea..eb5d8248a4a7 100644 --- a/cextern/wcslib/wcsconfig.h.in +++ b/cextern/wcslib/wcsconfig.h.in @@ -1,18 +1,20 @@ /*============================================================================ -* * wcsconfig.h is generated from wcsconfig.h.in by 'configure'. It contains -* C preprocessor macro definitions for compiling WCSLIB 4.14 +* C preprocessor macro definitions for compiling WCSLIB 8.6 * * Author: Mark Calabretta, Australia Telescope National Facility, CSIRO. -* http://www.atnf.csiro.au/people/Mark.Calabretta -* $Id: wcsconfig.h.in,v 4.14 2012/07/13 10:02:44 cal103 Exp $ +* http://www.atnf.csiro.au/computing/software/wcs +* $Id: wcsconfig.h.in,v 8.6 2026/03/29 13:53:57 mcalabre Exp $ *===========================================================================*/ -/* WCSLIB library version number. */ +// wcslib_version() is available (as of 5.0). +#define HAVE_WCSLIB_VERSION + +// WCSLIB library version number. #undef WCSLIB_VERSION -/* Define to 1 if sincos() is available. */ +// Define to 1 if sincos() is available. #undef HAVE_SINCOS -/* 64-bit integer data type. */ +// 64-bit integer data type. #undef WCSLIB_INT64 diff --git a/cextern/wcslib/wcsconfig_f77.h.in b/cextern/wcslib/wcsconfig_f77.h.in index c3854ff75acc..3d0725c21073 100644 --- a/cextern/wcslib/wcsconfig_f77.h.in +++ b/cextern/wcslib/wcsconfig_f77.h.in @@ -1,21 +1,20 @@ /*============================================================================ -* * wcsconfig_f77.h is generated from wcsconfig_f77.h.in by 'configure'. It -* contains C preprocessor definitions for building the WCSLIB 4.14 Fortran +* contains C preprocessor definitions for building the WCSLIB 8.6 Fortran * wrappers. * * Author: Mark Calabretta, Australia Telescope National Facility, CSIRO. -* http://www.atnf.csiro.au/people/Mark.Calabretta -* $Id: wcsconfig_f77.h.in,v 4.14 2012/07/13 10:02:44 cal103 Exp $ +* http://www.atnf.csiro.au/computing/software/wcs +* $Id: wcsconfig_f77.h.in,v 8.6 2026/03/29 13:53:57 mcalabre Exp $ *===========================================================================*/ -/* Integer array type large enough to hold an address. Set here to int[2] for - * 64-bit addresses, but could be defined as int* on 32-bit machines. */ +// Integer array type large enough to hold an address. Set here to int[2] for +// 64-bit addresses, but could be defined as int* on 32-bit machines. typedef int iptr[2]; -/* Macro for mangling Fortran subroutine names that do not contain - * underscores. Typically a name like "WCSINI" (case-insensitive) will become - * something like "wcsini_" (case-sensitive). The Fortran wrappers, which are - * written in C, are preprocessed into names that match the latter. The macro - * takes two arguments which specify the name in lower and upper case. */ +// Macro for mangling Fortran subroutine names that do not contain +// underscores. Typically a name like "WCSINI" (case-insensitive) will become +// something like "wcsini_" (case-sensitive). The Fortran wrappers, which are +// written in C, are preprocessed into names that match the latter. The macro +// takes two arguments which specify the name in lower and upper case. #undef F77_FUNC diff --git a/cextern/wcslib/wcsconfig_tests.h.in b/cextern/wcslib/wcsconfig_tests.h.in index 003c302080d5..6a2f73aa7fe4 100644 --- a/cextern/wcslib/wcsconfig_tests.h.in +++ b/cextern/wcslib/wcsconfig_tests.h.in @@ -1,18 +1,17 @@ /*============================================================================ -* * wcsconfig_test.h is generated from wcsconfig_test.h.in by 'configure'. It -* contains C preprocessor definitions for compiling the WCSLIB 4.14 test/demo +* contains C preprocessor definitions for compiling the WCSLIB 8.6 test/demo * programs. * * Author: Mark Calabretta, Australia Telescope National Facility, CSIRO. -* http://www.atnf.csiro.au/people/Mark.Calabretta -* $Id: wcsconfig_tests.h.in,v 4.14 2012/07/13 10:02:44 cal103 Exp $ +* http://www.atnf.csiro.au/computing/software/wcs +* $Id: wcsconfig_tests.h.in,v 8.6 2026/03/29 13:53:57 mcalabre Exp $ *===========================================================================*/ #include -/* Define to 1 if the CFITSIO library is available. */ +// Define to 1 if the CFITSIO library is available. #undef HAVE_CFITSIO -/* Define to the printf format modifier for size_t type. */ +// Define to the printf format modifier for size_t type. #undef MODZ diff --git a/cextern/wcslib/wcsconfig_utils.h.in b/cextern/wcslib/wcsconfig_utils.h.in index b5aa6be1218b..98c808a5d1b7 100644 --- a/cextern/wcslib/wcsconfig_utils.h.in +++ b/cextern/wcslib/wcsconfig_utils.h.in @@ -1,35 +1,30 @@ /*============================================================================ -* * wcsconfig_utils.h is generated from wcsconfig_utils.h.in by 'configure'. -* It contains C preprocessor macro definitions for compiling the WCSLIB 4.14 +* It contains C preprocessor macro definitions for compiling the WCSLIB 8.6 * utilities. * * Author: Mark Calabretta, Australia Telescope National Facility, CSIRO. -* http://www.atnf.csiro.au/people/Mark.Calabretta -* $Id: wcsconfig_utils.h.in,v 4.14 2012/07/13 10:02:44 cal103 Exp $ +* http://www.atnf.csiro.au/computing/software/wcs +* $Id: wcsconfig_utils.h.in,v 8.6 2026/03/29 13:53:57 mcalabre Exp $ *===========================================================================*/ #include -/* Definitions for Large File Support (LFS), i.e. files larger than 2GiB, for - * the fitshdr utility. */ +// Definitions for Large File Support (LFS), i.e. files larger than 2GiB, for +// the fitshdr utility. -/* Define to 1 if fseeko() is available (for small or large files). */ +// Define to 1 if fseeko() is available (for small or large files). */ #undef HAVE_FSEEKO -/* Define _LARGEFILE_SOURCE to get prototypes from stdio.h for the LFS - * functions fseeko() and ftello() which use an off_t argument in place of a - * long. */ +// Define _LARGEFILE_SOURCE to get prototypes from stdio.h for the LFS +// functions fseeko() and ftello(), which use an off_t argument in place of +// a long int. Note that gcc by itself does not need _LARGEFILE_SOURCE set, +// but 'gcc -std=c99' does. This may create confusion if '-std=c99' is added +// as a compiler option after configuring. #undef _LARGEFILE_SOURCE -/* There seems to be a bug in autoconf that causes _LARGEFILE_SOURCE not to be - * set in Linux. This dreadful kludge gets around it for now. */ -#if (defined HAVE_FSEEKO && !defined _LARGEFILE_SOURCE) -#define _LARGEFILE_SOURCE -#endif - -/* Number of bits in a file offset (off_t) on systems where it can be set. */ +// Number of bits in a file offset (off_t) on systems where it can be set. #undef _FILE_OFFSET_BITS -/* Define for large files needed on AIX-type systems. */ +// Define for large files needed on AIX-type systems. #undef _LARGE_FILES diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 000000000000..09a969bb6590 --- /dev/null +++ b/codecov.yml @@ -0,0 +1,9 @@ +comment: off +coverage: + status: + project: + default: + target: auto + # adjust accordingly based on how flaky your tests are + # this allows a 0.01% drop from the previous base commit coverage + threshold: 0.01% diff --git a/conftest.py b/conftest.py new file mode 100644 index 000000000000..08d4c0fa1f6f --- /dev/null +++ b/conftest.py @@ -0,0 +1,72 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +# This file is the main file used when running tests with pytest directly, +# in particular if running e.g. ``pytest docs/``. + +import os + +import hypothesis + +from astropy import __version__ + +try: + from pytest_astropy_header.display import PYTEST_HEADER_MODULES, TESTED_VERSIONS +except ImportError: + PYTEST_HEADER_MODULES = {} + TESTED_VERSIONS = {} + + +# This has to be in the root dir or it will not display in CI. +def pytest_configure(config): + # Strip COLUMNS/LINES so shutil.get_terminal_size() falls back to (80, 24) + # in the test session. Interactive shells (bash, zsh) export these on + # window resize, and pytest inherits them, so otherwise tests and doctests + # that read the terminal size are non-deterministic across environments. + os.environ.pop("COLUMNS", None) + os.environ.pop("LINES", None) + + PYTEST_HEADER_MODULES["PyERFA"] = "erfa" + PYTEST_HEADER_MODULES["Cython"] = "cython" + PYTEST_HEADER_MODULES["Scikit-image"] = "skimage" + PYTEST_HEADER_MODULES["pyarrow"] = "pyarrow" + PYTEST_HEADER_MODULES["asdf-astropy"] = "asdf_astropy" + TESTED_VERSIONS["Astropy"] = __version__ + + +# This has to be in the root dir or it will not display in CI. +def pytest_report_header(config): + # This gets added after the pytest-astropy-header output. + return ( + f"CI: {os.environ.get('CI', 'undefined')}\n" + f"ARCH_ON_CI: {os.environ.get('ARCH_ON_CI', 'undefined')}\n" + f"IS_CRON: {os.environ.get('IS_CRON', 'undefined')}\n" + ) + + +# Tell Hypothesis that we might be running slow tests, to print the seed blob +# so we can easily reproduce failures from CI, and derive a fuzzing profile +# to try many more inputs when we detect a scheduled build or when specifically +# requested using the HYPOTHESIS_PROFILE=fuzz environment variable or +# `pytest --hypothesis-profile=fuzz ...` argument. + +hypothesis.settings.register_profile( + "ci", + deadline=None, + print_blob=True, + derandomize=True, + # disabling HealthCheck.differing_executors to allow double test + # see https://github.com/astropy/astropy/issues/17299 + suppress_health_check=[hypothesis.HealthCheck.differing_executors], +) +hypothesis.settings.register_profile( + "fuzzing", deadline=None, print_blob=True, max_examples=1000 +) +default = ( + "fuzzing" + if ( + os.environ.get("IS_CRON") == "true" + and os.environ.get("ARCH_ON_CI") not in ("aarch64", "ppc64le") + ) + else "ci" +) +hypothesis.settings.load_profile(os.environ.get("HYPOTHESIS_PROFILE", default)) diff --git a/distribute_setup.py b/distribute_setup.py deleted file mode 100644 index bbb6f3c25e39..000000000000 --- a/distribute_setup.py +++ /dev/null @@ -1,485 +0,0 @@ -#!python -"""Bootstrap distribute installation - -If you want to use setuptools in your package's setup.py, just include this -file in the same directory with it, and add this to the top of your setup.py:: - - from distribute_setup import use_setuptools - use_setuptools() - -If you want to require a specific version of setuptools, set a download -mirror, or use an alternate download directory, you can do so by supplying -the appropriate options to ``use_setuptools()``. - -This file can also be run as a script to install or upgrade setuptools. -""" -import os -import sys -import time -import fnmatch -import tempfile -import tarfile -from distutils import log - -try: - from site import USER_SITE -except ImportError: - USER_SITE = None - -try: - import subprocess - - def _python_cmd(*args): - args = (sys.executable,) + args - return subprocess.call(args) == 0 - -except ImportError: - # will be used for python 2.3 - def _python_cmd(*args): - args = (sys.executable,) + args - # quoting arguments if windows - if sys.platform == 'win32': - def quote(arg): - if ' ' in arg: - return '"%s"' % arg - return arg - args = [quote(arg) for arg in args] - return os.spawnl(os.P_WAIT, sys.executable, *args) == 0 - -DEFAULT_VERSION = "0.6.19" -DEFAULT_URL = "http://pypi.python.org/packages/source/d/distribute/" -SETUPTOOLS_FAKED_VERSION = "0.6c11" - -SETUPTOOLS_PKG_INFO = """\ -Metadata-Version: 1.0 -Name: setuptools -Version: %s -Summary: xxxx -Home-page: xxx -Author: xxx -Author-email: xxx -License: xxx -Description: xxx -""" % SETUPTOOLS_FAKED_VERSION - - -def _install(tarball): - # extracting the tarball - tmpdir = tempfile.mkdtemp() - log.warn('Extracting in %s', tmpdir) - old_wd = os.getcwd() - try: - os.chdir(tmpdir) - tar = tarfile.open(tarball) - _extractall(tar) - tar.close() - - # going in the directory - subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0]) - os.chdir(subdir) - log.warn('Now working in %s', subdir) - - # installing - log.warn('Installing Distribute') - if not _python_cmd('setup.py', 'install'): - log.warn('Something went wrong during the installation.') - log.warn('See the error message above.') - finally: - os.chdir(old_wd) - - -def _build_egg(egg, tarball, to_dir): - # extracting the tarball - tmpdir = tempfile.mkdtemp() - log.warn('Extracting in %s', tmpdir) - old_wd = os.getcwd() - try: - os.chdir(tmpdir) - tar = tarfile.open(tarball) - _extractall(tar) - tar.close() - - # going in the directory - subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0]) - os.chdir(subdir) - log.warn('Now working in %s', subdir) - - # building an egg - log.warn('Building a Distribute egg in %s', to_dir) - _python_cmd('setup.py', '-q', 'bdist_egg', '--dist-dir', to_dir) - - finally: - os.chdir(old_wd) - # returning the result - log.warn(egg) - if not os.path.exists(egg): - raise IOError('Could not build the egg.') - - -def _do_download(version, download_base, to_dir, download_delay): - egg = os.path.join(to_dir, 'distribute-%s-py%d.%d.egg' - % (version, sys.version_info[0], sys.version_info[1])) - if not os.path.exists(egg): - tarball = download_setuptools(version, download_base, - to_dir, download_delay) - _build_egg(egg, tarball, to_dir) - sys.path.insert(0, egg) - import setuptools - setuptools.bootstrap_install_from = egg - - -def use_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL, - to_dir=os.curdir, download_delay=15, no_fake=True): - # making sure we use the absolute path - to_dir = os.path.abspath(to_dir) - was_imported = 'pkg_resources' in sys.modules or \ - 'setuptools' in sys.modules - try: - try: - import pkg_resources - if not hasattr(pkg_resources, '_distribute'): - if not no_fake: - _fake_setuptools() - raise ImportError - except ImportError: - return _do_download(version, download_base, to_dir, download_delay) - try: - pkg_resources.require("distribute>="+version) - return - except pkg_resources.VersionConflict: - e = sys.exc_info()[1] - if was_imported: - sys.stderr.write( - "The required version of distribute (>=%s) is not available,\n" - "and can't be installed while this script is running. Please\n" - "install a more recent version first, using\n" - "'easy_install -U distribute'." - "\n\n(Currently using %r)\n" % (version, e.args[0])) - sys.exit(2) - else: - del pkg_resources, sys.modules['pkg_resources'] # reload ok - return _do_download(version, download_base, to_dir, - download_delay) - except pkg_resources.DistributionNotFound: - return _do_download(version, download_base, to_dir, - download_delay) - finally: - if not no_fake: - _create_fake_setuptools_pkg_info(to_dir) - -def download_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL, - to_dir=os.curdir, delay=15): - """Download distribute from a specified location and return its filename - - `version` should be a valid distribute version number that is available - as an egg for download under the `download_base` URL (which should end - with a '/'). `to_dir` is the directory where the egg will be downloaded. - `delay` is the number of seconds to pause before an actual download - attempt. - """ - # making sure we use the absolute path - to_dir = os.path.abspath(to_dir) - try: - from urllib.request import urlopen - except ImportError: - from urllib2 import urlopen - tgz_name = "distribute-%s.tar.gz" % version - url = download_base + tgz_name - saveto = os.path.join(to_dir, tgz_name) - src = dst = None - if not os.path.exists(saveto): # Avoid repeated downloads - try: - log.warn("Downloading %s", url) - src = urlopen(url) - # Read/write all in one block, so we don't create a corrupt file - # if the download is interrupted. - data = src.read() - dst = open(saveto, "wb") - dst.write(data) - finally: - if src: - src.close() - if dst: - dst.close() - return os.path.realpath(saveto) - -def _no_sandbox(function): - def __no_sandbox(*args, **kw): - try: - from setuptools.sandbox import DirectorySandbox - if not hasattr(DirectorySandbox, '_old'): - def violation(*args): - pass - DirectorySandbox._old = DirectorySandbox._violation - DirectorySandbox._violation = violation - patched = True - else: - patched = False - except ImportError: - patched = False - - try: - return function(*args, **kw) - finally: - if patched: - DirectorySandbox._violation = DirectorySandbox._old - del DirectorySandbox._old - - return __no_sandbox - -def _patch_file(path, content): - """Will backup the file then patch it""" - existing_content = open(path).read() - if existing_content == content: - # already patched - log.warn('Already patched.') - return False - log.warn('Patching...') - _rename_path(path) - f = open(path, 'w') - try: - f.write(content) - finally: - f.close() - return True - -_patch_file = _no_sandbox(_patch_file) - -def _same_content(path, content): - return open(path).read() == content - -def _rename_path(path): - new_name = path + '.OLD.%s' % time.time() - log.warn('Renaming %s into %s', path, new_name) - os.rename(path, new_name) - return new_name - -def _remove_flat_installation(placeholder): - if not os.path.isdir(placeholder): - log.warn('Unkown installation at %s', placeholder) - return False - found = False - for file in os.listdir(placeholder): - if fnmatch.fnmatch(file, 'setuptools*.egg-info'): - found = True - break - if not found: - log.warn('Could not locate setuptools*.egg-info') - return - - log.warn('Removing elements out of the way...') - pkg_info = os.path.join(placeholder, file) - if os.path.isdir(pkg_info): - patched = _patch_egg_dir(pkg_info) - else: - patched = _patch_file(pkg_info, SETUPTOOLS_PKG_INFO) - - if not patched: - log.warn('%s already patched.', pkg_info) - return False - # now let's move the files out of the way - for element in ('setuptools', 'pkg_resources.py', 'site.py'): - element = os.path.join(placeholder, element) - if os.path.exists(element): - _rename_path(element) - else: - log.warn('Could not find the %s element of the ' - 'Setuptools distribution', element) - return True - -_remove_flat_installation = _no_sandbox(_remove_flat_installation) - -def _after_install(dist): - log.warn('After install bootstrap.') - placeholder = dist.get_command_obj('install').install_purelib - _create_fake_setuptools_pkg_info(placeholder) - -def _create_fake_setuptools_pkg_info(placeholder): - if not placeholder or not os.path.exists(placeholder): - log.warn('Could not find the install location') - return - pyver = '%s.%s' % (sys.version_info[0], sys.version_info[1]) - setuptools_file = 'setuptools-%s-py%s.egg-info' % \ - (SETUPTOOLS_FAKED_VERSION, pyver) - pkg_info = os.path.join(placeholder, setuptools_file) - if os.path.exists(pkg_info): - log.warn('%s already exists', pkg_info) - return - - log.warn('Creating %s', pkg_info) - f = open(pkg_info, 'w') - try: - f.write(SETUPTOOLS_PKG_INFO) - finally: - f.close() - - pth_file = os.path.join(placeholder, 'setuptools.pth') - log.warn('Creating %s', pth_file) - f = open(pth_file, 'w') - try: - f.write(os.path.join(os.curdir, setuptools_file)) - finally: - f.close() - -_create_fake_setuptools_pkg_info = _no_sandbox(_create_fake_setuptools_pkg_info) - -def _patch_egg_dir(path): - # let's check if it's already patched - pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO') - if os.path.exists(pkg_info): - if _same_content(pkg_info, SETUPTOOLS_PKG_INFO): - log.warn('%s already patched.', pkg_info) - return False - _rename_path(path) - os.mkdir(path) - os.mkdir(os.path.join(path, 'EGG-INFO')) - pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO') - f = open(pkg_info, 'w') - try: - f.write(SETUPTOOLS_PKG_INFO) - finally: - f.close() - return True - -_patch_egg_dir = _no_sandbox(_patch_egg_dir) - -def _before_install(): - log.warn('Before install bootstrap.') - _fake_setuptools() - - -def _under_prefix(location): - if 'install' not in sys.argv: - return True - args = sys.argv[sys.argv.index('install')+1:] - for index, arg in enumerate(args): - for option in ('--root', '--prefix'): - if arg.startswith('%s=' % option): - top_dir = arg.split('root=')[-1] - return location.startswith(top_dir) - elif arg == option: - if len(args) > index: - top_dir = args[index+1] - return location.startswith(top_dir) - if arg == '--user' and USER_SITE is not None: - return location.startswith(USER_SITE) - return True - - -def _fake_setuptools(): - log.warn('Scanning installed packages') - try: - import pkg_resources - except ImportError: - # we're cool - log.warn('Setuptools or Distribute does not seem to be installed.') - return - ws = pkg_resources.working_set - try: - setuptools_dist = ws.find(pkg_resources.Requirement.parse('setuptools', - replacement=False)) - except TypeError: - # old distribute API - setuptools_dist = ws.find(pkg_resources.Requirement.parse('setuptools')) - - if setuptools_dist is None: - log.warn('No setuptools distribution found') - return - # detecting if it was already faked - setuptools_location = setuptools_dist.location - log.warn('Setuptools installation detected at %s', setuptools_location) - - # if --root or --preix was provided, and if - # setuptools is not located in them, we don't patch it - if not _under_prefix(setuptools_location): - log.warn('Not patching, --root or --prefix is installing Distribute' - ' in another location') - return - - # let's see if its an egg - if not setuptools_location.endswith('.egg'): - log.warn('Non-egg installation') - res = _remove_flat_installation(setuptools_location) - if not res: - return - else: - log.warn('Egg installation') - pkg_info = os.path.join(setuptools_location, 'EGG-INFO', 'PKG-INFO') - if (os.path.exists(pkg_info) and - _same_content(pkg_info, SETUPTOOLS_PKG_INFO)): - log.warn('Already patched.') - return - log.warn('Patching...') - # let's create a fake egg replacing setuptools one - res = _patch_egg_dir(setuptools_location) - if not res: - return - log.warn('Patched done.') - _relaunch() - - -def _relaunch(): - log.warn('Relaunching...') - # we have to relaunch the process - # pip marker to avoid a relaunch bug - if sys.argv[:3] == ['-c', 'install', '--single-version-externally-managed']: - sys.argv[0] = 'setup.py' - args = [sys.executable] + sys.argv - sys.exit(subprocess.call(args)) - - -def _extractall(self, path=".", members=None): - """Extract all members from the archive to the current working - directory and set owner, modification time and permissions on - directories afterwards. `path' specifies a different directory - to extract to. `members' is optional and must be a subset of the - list returned by getmembers(). - """ - import copy - import operator - from tarfile import ExtractError - directories = [] - - if members is None: - members = self - - for tarinfo in members: - if tarinfo.isdir(): - # Extract directories with a safe mode. - directories.append(tarinfo) - tarinfo = copy.copy(tarinfo) - tarinfo.mode = 448 # decimal for oct 0700 - self.extract(tarinfo, path) - - # Reverse sort directories. - if sys.version_info < (2, 4): - def sorter(dir1, dir2): - return cmp(dir1.name, dir2.name) - directories.sort(sorter) - directories.reverse() - else: - directories.sort(key=operator.attrgetter('name'), reverse=True) - - # Set correct owner, mtime and filemode on directories. - for tarinfo in directories: - dirpath = os.path.join(path, tarinfo.name) - try: - self.chown(tarinfo, dirpath) - self.utime(tarinfo, dirpath) - self.chmod(tarinfo, dirpath) - except ExtractError: - e = sys.exc_info()[1] - if self.errorlevel > 1: - raise - else: - self._dbg(1, "tarfile: %s" % e) - - -def main(argv, version=DEFAULT_VERSION): - """Install or upgrade setuptools and EasyInstall""" - tarball = download_setuptools() - _install(tarball) - - -if __name__ == '__main__': - main(sys.argv[1:]) diff --git a/docs/Makefile b/docs/Makefile index 058cf96eb10f..03886de1970f 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -34,11 +34,11 @@ help: @echo " man to make manual pages" @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" - @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: -rm -rf $(BUILDDIR) - -rm -rf _generated + -rm -rf api + -rm -rf generated html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @@ -129,6 +129,5 @@ linkcheck: "or in $(BUILDDIR)/linkcheck/output.txt." doctest: - $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest - @echo "Testing of doctests in the sources finished, look at the " \ - "results in $(BUILDDIR)/doctest/output.txt." + @echo "Run 'pytest' in the root directory to run doctests " \ + @echo "in the documentation." diff --git a/docs/_pkgtemplate.rst b/docs/_pkgtemplate.rst index f9ec53403bfd..ac432f00b340 100644 --- a/docs/_pkgtemplate.rst +++ b/docs/_pkgtemplate.rst @@ -2,7 +2,7 @@ A description of the package (`astropy.packagename`) **************************************************** -When creating a new subpackage's docuementation, this file should be +When creating a new subpackage's documentation, this file should be copied to a file "index.rst" in a directory corresponding to the name of the package. E.g., ``docs/packagename/index.rst``. And don't forget to delete this paragraph. diff --git a/docs/_static/astropy.css b/docs/_static/astropy.css new file mode 100644 index 000000000000..fffe2adbdc8a --- /dev/null +++ b/docs/_static/astropy.css @@ -0,0 +1,3 @@ +html[data-theme="dark"] ul.cooframelegend { + filter: invert(0.82) brightness(0.8) contrast(1.2); +} diff --git a/docs/_static/astropy_banner.svg b/docs/_static/astropy_banner.svg new file mode 100644 index 000000000000..a52c65ed0192 --- /dev/null +++ b/docs/_static/astropy_banner.svg @@ -0,0 +1,263 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/_static/astropy_banner_96.png b/docs/_static/astropy_banner_96.png new file mode 100644 index 000000000000..3bd96747946b Binary files /dev/null and b/docs/_static/astropy_banner_96.png differ diff --git a/docs/_static/astropy_banner_96_dark.png b/docs/_static/astropy_banner_96_dark.png new file mode 100644 index 000000000000..adb747bf36f4 Binary files /dev/null and b/docs/_static/astropy_banner_96_dark.png differ diff --git a/docs/_static/astropy_banner_dark.svg b/docs/_static/astropy_banner_dark.svg new file mode 100644 index 000000000000..aaaa1838251a --- /dev/null +++ b/docs/_static/astropy_banner_dark.svg @@ -0,0 +1,288 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/_static/astropy_logo.ico b/docs/_static/astropy_logo.ico new file mode 100644 index 000000000000..16d5af740634 Binary files /dev/null and b/docs/_static/astropy_logo.ico differ diff --git a/docs/_static/astropy_logo.pdf b/docs/_static/astropy_logo.pdf new file mode 100644 index 000000000000..b2ae8d6c8df5 Binary files /dev/null and b/docs/_static/astropy_logo.pdf differ diff --git a/docs/_templates/autosummary/base.rst b/docs/_templates/autosummary/base.rst deleted file mode 100644 index a58aa35ff9c1..000000000000 --- a/docs/_templates/autosummary/base.rst +++ /dev/null @@ -1,10 +0,0 @@ -{% if referencefile %} -.. include:: {{ referencefile }} -{% endif %} - -{{ objname }} -{{ underline }} - -.. currentmodule:: {{ module }} - -.. auto{{ objtype }}:: {{ objname }} diff --git a/docs/_templates/autosummary/class.rst b/docs/_templates/autosummary/class.rst deleted file mode 100644 index e76b633e8da7..000000000000 --- a/docs/_templates/autosummary/class.rst +++ /dev/null @@ -1,65 +0,0 @@ -{% if referencefile %} -.. include:: {{ referencefile }} -{% endif %} - -{{ objname }} -{{ underline }} - -.. currentmodule:: {{ module }} - -.. autoclass:: {{ objname }} - :show-inheritance: - - {% if '__init__' in methods %} - {{ methods.remove('__init__') }} - {% endif %} - - {% block attributes_summary %} - {% if attributes %} - - .. rubric:: Attributes Summary - - .. autosummary:: - {% for item in attributes %} - ~{{ name }}.{{ item }} - {%- endfor %} - - {% endif %} - {% endblock %} - - {% block methods_summary %} - {% if methods %} - - .. rubric:: Methods Summary - - .. autosummary:: - {% for item in methods %} - ~{{ name }}.{{ item }} - {%- endfor %} - - {% endif %} - {% endblock %} - - {% block attributes_documentation %} - {% if attributes %} - - .. rubric:: Attributes Documentation - - {% for item in attributes %} - .. autoattribute:: {{ item }} - {%- endfor %} - - {% endif %} - {% endblock %} - - {% block methods_documentation %} - {% if methods %} - - .. rubric:: Methods Documentation - - {% for item in methods %} - .. automethod:: {{ item }} - {%- endfor %} - - {% endif %} - {% endblock %} diff --git a/docs/_templates/autosummary/module.rst b/docs/_templates/autosummary/module.rst deleted file mode 100644 index 11208a25c6b1..000000000000 --- a/docs/_templates/autosummary/module.rst +++ /dev/null @@ -1,41 +0,0 @@ -{% if referencefile %} -.. include:: {{ referencefile }} -{% endif %} - -{{ objname }} -{{ underline }} - -.. automodule:: {{ fullname }} - - {% block functions %} - {% if functions %} - .. rubric:: Functions - - .. autosummary:: - {% for item in functions %} - {{ item }} - {%- endfor %} - {% endif %} - {% endblock %} - - {% block classes %} - {% if classes %} - .. rubric:: Classes - - .. autosummary:: - {% for item in classes %} - {{ item }} - {%- endfor %} - {% endif %} - {% endblock %} - - {% block exceptions %} - {% if exceptions %} - .. rubric:: Exceptions - - .. autosummary:: - {% for item in exceptions %} - {{ item }} - {%- endfor %} - {% endif %} - {% endblock %} diff --git a/docs/_templates/layout.html b/docs/_templates/layout.html new file mode 100644 index 000000000000..78aeb6c6603e --- /dev/null +++ b/docs/_templates/layout.html @@ -0,0 +1,25 @@ +{# This extension of the 'layout.html' prevents documentation for previous + versions of Astropy to be indexed by bots, e.g. googlebot or bing bot, + by inserting a robots meta tag into pages that are not in the stable or + latest branch. + + It assumes that the documentation is built by and hosted on readthedocs.org: + 1. Readthedocs.org has a global robots.txt and no option for a custom one. + 2. The readthedocs app passes additional variables to the template context, + one of them being `version_slug`. This variable is a string computed from + the tags of the branches that are selected to be built. It can be 'latest', + 'stable' or even a unique stringified version number. + + For more information, please refer to: + https://github.com/astropy/astropy/pull/7874 + http://www.robotstxt.org/meta.html + https://github.com/rtfd/readthedocs.org/blob/master/readthedocs/builds/version_slug.py +#} + +{% extends "!layout.html" %} +{%- block extrahead %} + {% if not version_slug in to_be_indexed %} + + {% endif %} + {{ super() }} +{% endblock %} diff --git a/docs/changelog.rst b/docs/changelog.rst new file mode 100644 index 000000000000..f93bbc481f3e --- /dev/null +++ b/docs/changelog.rst @@ -0,0 +1,10 @@ +.. _changelog: + +************** +Full Changelog +************** + +.. changelog:: + :towncrier: ../ + :towncrier-skip-if-empty: + :changelog_file: ../CHANGES.rst diff --git a/docs/changes/18342.other.rst b/docs/changes/18342.other.rst new file mode 100644 index 000000000000..0f731c0669a0 --- /dev/null +++ b/docs/changes/18342.other.rst @@ -0,0 +1,3 @@ +Development-only dependencies, previously defined as user-visible +extras ``dev`` and ``dev_all``, were moved to PEP 735 dependency groups, and +are thus only accessible when building astropy from source. diff --git a/docs/changes/18962.other.rst b/docs/changes/18962.other.rst new file mode 100644 index 000000000000..c83bcf571eb8 --- /dev/null +++ b/docs/changes/18962.other.rst @@ -0,0 +1 @@ +The minimum required NumPy version is now 1.25. diff --git a/docs/changes/18986.other.rst b/docs/changes/18986.other.rst new file mode 100644 index 000000000000..b2e557579749 --- /dev/null +++ b/docs/changes/18986.other.rst @@ -0,0 +1 @@ +The minimum supported version of numpy is now 2.0. diff --git a/docs/changes/19050.other.rst b/docs/changes/19050.other.rst new file mode 100644 index 000000000000..5de335b5f3d6 --- /dev/null +++ b/docs/changes/19050.other.rst @@ -0,0 +1 @@ +Upgraded WCSLIB to version 8.5, fixing NaN handling in ``linp2x()`` and ``linx2p()``. For a full list of changes - see ``astropy/cextern/wcslib/CHANGES``. diff --git a/docs/changes/19347.other.rst b/docs/changes/19347.other.rst new file mode 100644 index 000000000000..dae27136e928 --- /dev/null +++ b/docs/changes/19347.other.rst @@ -0,0 +1 @@ +Publishing files to PyPI is now done using the Trusted Publisher mechanism (https://docs.pypi.org/trusted-publishers/). diff --git a/docs/changes/19514.other.rst b/docs/changes/19514.other.rst new file mode 100644 index 000000000000..af2d42d4b0c7 --- /dev/null +++ b/docs/changes/19514.other.rst @@ -0,0 +1 @@ +Upgraded WCSLIB to version 8.6. For a full list of changes - see ``astropy/cextern/wcslib/CHANGES``. diff --git a/docs/changes/19662.other.rst b/docs/changes/19662.other.rst new file mode 100644 index 000000000000..7beb7eb12b64 --- /dev/null +++ b/docs/changes/19662.other.rst @@ -0,0 +1 @@ +Updated the bundled CFITSIO library to 4.6.4. diff --git a/docs/changes/README.rst b/docs/changes/README.rst new file mode 100644 index 000000000000..c853ce7ef545 --- /dev/null +++ b/docs/changes/README.rst @@ -0,0 +1,44 @@ +:orphan: + +Changelog +========= + +This directory contains "news fragments" which are short files that contain a +small **ReST**-formatted text that will be added to the next what's new page. + +Make sure to use full sentences with correct case and punctuation. + +Each file should be named like ``..rst``, where +```` is a pull request number, and ```` is one of: + +* ``feature``: New feature. +* ``api``: API change. +* ``bugfix``: Bug fix. +* ``perf``: Performance improvement (this should be significant enough to be measurable using the public API). +* ``other``: Other changes and additions. + +If the change concerns a sub-package, the file should go in the sub-directory +relative to this sub-package. Type ``other`` is not allowed in sub-directories. + +It is possible to add two files with different types (and text) if both +are relevant. For example a change may add a new feature but introduce an API +change. + +So for example: ``123.feature.rst`` would have the content:: + + The ``my_new_feature`` option is now available for ``my_favorite_function``. + To use it, write ``np.my_favorite_function(..., my_new_feature=True)``. + +Note the use of double-backticks for code. + +If you are unsure what pull request type to use, don't hesitate to ask in your +PR. + +You can install ``towncrier`` and run ``towncrier --draft --version 4.3`` +if you want to get a preview of how your change will look in the final release +notes. + +.. note:: + + This README was adapted from the Numpy changelog readme under the terms of + the MIT licence. diff --git a/docs/changes/config/.gitkeep b/docs/changes/config/.gitkeep new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/docs/changes/config/17571.api.rst b/docs/changes/config/17571.api.rst new file mode 100644 index 000000000000..c025cccb68f0 --- /dev/null +++ b/docs/changes/config/17571.api.rst @@ -0,0 +1,2 @@ +The ``ConfNamespace.help`` method now raises an exception if called under +Python's optimized mode (``-OO`` flag). diff --git a/docs/changes/config/19559.bugfix.rst b/docs/changes/config/19559.bugfix.rst new file mode 100644 index 000000000000..7da143c2926d --- /dev/null +++ b/docs/changes/config/19559.bugfix.rst @@ -0,0 +1,2 @@ +Disabling thread concurrency within ``set_temp_cache`` and ``set_temp_config`` +context managers, ensuring thread safety. diff --git a/docs/changes/config/19575.api.rst b/docs/changes/config/19575.api.rst new file mode 100644 index 000000000000..b6eb91b3be9e --- /dev/null +++ b/docs/changes/config/19575.api.rst @@ -0,0 +1,16 @@ +Cache and configuration directories now adhere to the XDG specification +by default. For example, the default cache location was changed from +``$HOME/.astropy/cache`` to ``$XDG_CACHE_HOME/astropy``, where +``XDG_CACHE_HOME`` itself defaults to ``$HOME/.cache``. + +Affected functions from the ``astropy.config`` namespace: + +- ``get_cache_dir`` +- ``get_cache_dir_path`` +- ``get_config_dir`` +- ``get_config_dir_path`` + +In addition, temporary directories set through ``set_temp_cache`` +or ``set_temp_config`` will now not be symlinked to default locations. +The new behavior is also meant as 100% cross platform: Windows isn't +special cased like it used to. diff --git a/docs/changes/config/19575.feature.rst b/docs/changes/config/19575.feature.rst new file mode 100644 index 000000000000..c6aae82fcec1 --- /dev/null +++ b/docs/changes/config/19575.feature.rst @@ -0,0 +1,11 @@ +Added ``astropy.config.temporary_cache_dir_path`` and +``astropy.config.temporary_config_dir_path`` context managers, which are +safer alternatives to ``astropy.config.set_temp_cache`` and +``astropy.config.set_temp_config`` respectively, and should be preferred in +new code, but are not drop-in replacements. + +Added support for ``ASTROPY_CACHE_DIR`` and ``ASTROPY_CONFIG_DIR`` +environment variables, offering a more tightly scoped alternative to +``XDG_CACHE_HOME`` and ``XDG_CONFIG_HOME`` respectively. +When both are defined, ``ASTROPY_`` -prefixed variables take precedence +over ``XDG_`` -prefixed ones. diff --git a/docs/changes/config/19616.feature.rst b/docs/changes/config/19616.feature.rst new file mode 100644 index 000000000000..930caa4ead3b --- /dev/null +++ b/docs/changes/config/19616.feature.rst @@ -0,0 +1,3 @@ +Added a ``ensure_exists`` boolean option to cache and config path getters, allowing +callers to disable directory creation on discovery with ``ensure_exists=False``. +When False, callers should handle missing directory on their own. diff --git a/docs/changes/constants/.gitkeep b/docs/changes/constants/.gitkeep new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/docs/changes/constants/18407.api.rst b/docs/changes/constants/18407.api.rst new file mode 100644 index 000000000000..318efaa0ebdb --- /dev/null +++ b/docs/changes/constants/18407.api.rst @@ -0,0 +1 @@ +CODATA 2022 has replaced CODATA 2018 as default. ``astropyconst80`` is available for ``constants`` science state, combining CODATA 2022 with IAU 2015. diff --git a/docs/changes/convolution/.gitkeep b/docs/changes/convolution/.gitkeep new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/docs/changes/convolution/18348.api.rst b/docs/changes/convolution/18348.api.rst new file mode 100644 index 000000000000..d2d6daf18d2e --- /dev/null +++ b/docs/changes/convolution/18348.api.rst @@ -0,0 +1,2 @@ +Fixes edge treatment in ``convolve(..., boundary='fill', fill_value=nan)`` to be consistent with +``convolve_fft``. diff --git a/docs/changes/convolution/18363.bugfix.rst b/docs/changes/convolution/18363.bugfix.rst new file mode 100644 index 000000000000..2a2c9250a160 --- /dev/null +++ b/docs/changes/convolution/18363.bugfix.rst @@ -0,0 +1,2 @@ +``convolve()`` and ``convolve_fft()`` now raise exceptions if the kernel is a +masked array with masked values. diff --git a/docs/changes/coordinates/.gitkeep b/docs/changes/coordinates/.gitkeep new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/docs/changes/coordinates/18362.api.rst b/docs/changes/coordinates/18362.api.rst new file mode 100644 index 000000000000..c20955dc5314 --- /dev/null +++ b/docs/changes/coordinates/18362.api.rst @@ -0,0 +1,5 @@ +``Galactocentric`` and ``LSR`` now use a ``CartesianRepresentation`` instead +of ``CartesianDifferential`` for their velocity attributes (``galcen_v_sun`` +and ``v_bary``, resp.). On input, this has little effect, since instances of +both remain accepted, but for converting the attribute to a ``Quantity``, one +now has to access the ``.xyz`` attribute instead of ``.d_xyz``. diff --git a/docs/changes/coordinates/18362.bugfix.rst b/docs/changes/coordinates/18362.bugfix.rst new file mode 100644 index 000000000000..9c3bdeba1909 --- /dev/null +++ b/docs/changes/coordinates/18362.bugfix.rst @@ -0,0 +1,2 @@ +``Galactocentric`` and ``LSR`` now raise an error if their velocities are +initialized with something that does not have velocity units. diff --git a/docs/changes/coordinates/18687.bugfix.rst b/docs/changes/coordinates/18687.bugfix.rst new file mode 100644 index 000000000000..97ed51c5743d --- /dev/null +++ b/docs/changes/coordinates/18687.bugfix.rst @@ -0,0 +1,2 @@ +The ``refresh_cache`` parameter of ``EarthLocation.of_site()`` and +``EarthLocation.get_site_names()`` methods now works. diff --git a/docs/changes/coordinates/19001.bugfix.rst b/docs/changes/coordinates/19001.bugfix.rst new file mode 100644 index 000000000000..c0975104fc27 --- /dev/null +++ b/docs/changes/coordinates/19001.bugfix.rst @@ -0,0 +1,6 @@ +Relativistic Doppler shifts are now applied to ``SpectralCoord`` in the correct +direction even if the ``astropy.units.spectral()`` equivalency is enabled. +Previously enabling the equivalency could cause wrong results with the +``SpectralCoord.to_rest()``, +``SpectralCoord.with_observer_stationary_relative_to()`` or +``SpectralCoord.with_radial_velocity_shift()`` methods. diff --git a/docs/changes/coordinates/19308.bugfix.rst b/docs/changes/coordinates/19308.bugfix.rst new file mode 100644 index 000000000000..ebb9e210d15f --- /dev/null +++ b/docs/changes/coordinates/19308.bugfix.rst @@ -0,0 +1,2 @@ +Fixed a broadcasting bug in ``astropy.uncertainty.distributions.uniform`` where +multi-dimensional inputs (``ndim >= 2``) would raise a ``ValueError``. diff --git a/docs/changes/coordinates/19330.bugfix.rst b/docs/changes/coordinates/19330.bugfix.rst new file mode 100644 index 000000000000..60df745beb2b --- /dev/null +++ b/docs/changes/coordinates/19330.bugfix.rst @@ -0,0 +1,2 @@ +Send a User-Agent header to the OpenStreetMap API in ``EarthLocation.of_address`` +to fix access denied errors. diff --git a/docs/changes/coordinates/19403.bugfix.rst b/docs/changes/coordinates/19403.bugfix.rst new file mode 100644 index 000000000000..31180d7ccf6f --- /dev/null +++ b/docs/changes/coordinates/19403.bugfix.rst @@ -0,0 +1 @@ +Fixed a missing f-string in a TypeError from ``BaseAffineTransform._apply_transform`` that caused ``{data.__class__}`` to appear literally in the error message. diff --git a/docs/changes/cosmology/.gitkeep b/docs/changes/cosmology/.gitkeep new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/docs/changes/cosmology/18800.api.rst b/docs/changes/cosmology/18800.api.rst new file mode 100644 index 000000000000..725dc15a1817 --- /dev/null +++ b/docs/changes/cosmology/18800.api.rst @@ -0,0 +1,2 @@ +Redshift arguments (``z``, ``z1``, ``z2``) in cosmology methods are now +positional-only, completing the deprecations started in v7.0 and v7.1. diff --git a/docs/changes/cosmology/18807.feature.rst b/docs/changes/cosmology/18807.feature.rst new file mode 100644 index 000000000000..7ae13af33e28 --- /dev/null +++ b/docs/changes/cosmology/18807.feature.rst @@ -0,0 +1,4 @@ +The ``angular_diameter_distance`` method now accepts two redshift arguments +to compute the angular diameter distance between objects at different redshifts. +The previous separate method ``angular_diameter_distance_z1z2`` is deprecated +and will be removed in a future version. diff --git a/docs/changes/cosmology/18809.feature.rst b/docs/changes/cosmology/18809.feature.rst new file mode 100644 index 000000000000..a7deeaa748d5 --- /dev/null +++ b/docs/changes/cosmology/18809.feature.rst @@ -0,0 +1 @@ +The trait ``astropy.cosmology.traits.NeutrinoComponent`` has been added to work with objects that have attributes and methods related to neutrinos. diff --git a/docs/changes/cosmology/18856.feature.rst b/docs/changes/cosmology/18856.feature.rst new file mode 100644 index 000000000000..0ec307850dd3 --- /dev/null +++ b/docs/changes/cosmology/18856.feature.rst @@ -0,0 +1,4 @@ +Removed deprecated module-level shim files (``astropy.cosmology.connect``, +``astropy.cosmology.core``, ``astropy.cosmology.flrw``, ``astropy.cosmology.funcs``, and +``astropy.cosmology.parameter``), which were deprecated in v7.1. All cosmology classes +and functions should be imported directly from ``astropy.cosmology``. diff --git a/docs/changes/cosmology/18874.api.rst b/docs/changes/cosmology/18874.api.rst new file mode 100644 index 000000000000..8aca8a89e8b9 --- /dev/null +++ b/docs/changes/cosmology/18874.api.rst @@ -0,0 +1,3 @@ +The ``Ob0=None`` parameter value for cosmology classes is no longer supported. +This deprecation was started in v7.0. Use ``Ob0=0`` instead to indicate zero +baryonic matter density. diff --git a/docs/changes/cosmology/18952.feature.rst b/docs/changes/cosmology/18952.feature.rst new file mode 100644 index 000000000000..b1f13440299f --- /dev/null +++ b/docs/changes/cosmology/18952.feature.rst @@ -0,0 +1,2 @@ +Registered the ``read_mrt()`` and ``write_mrt()`` methods with the Cosmology class, +enabling the import and export of cosmological data to and from MRT files. diff --git a/docs/changes/extern/.gitkeep b/docs/changes/extern/.gitkeep new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/docs/changes/io.ascii/.gitkeep b/docs/changes/io.ascii/.gitkeep new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/docs/changes/io.ascii/18505.feature.rst b/docs/changes/io.ascii/18505.feature.rst new file mode 100644 index 000000000000..641ff7f12b5f --- /dev/null +++ b/docs/changes/io.ascii/18505.feature.rst @@ -0,0 +1 @@ +Read CDS table where the data is split into multiple files diff --git a/docs/changes/io.ascii/19125.feature.rst b/docs/changes/io.ascii/19125.feature.rst new file mode 100644 index 000000000000..f984b67265e6 --- /dev/null +++ b/docs/changes/io.ascii/19125.feature.rst @@ -0,0 +1,3 @@ +Add a new ``io.ascii`` format ``mesa`` to read history and profile output files from +`MESA `_, a well-known code for stellar evolution +calculations. diff --git a/docs/changes/io.ascii/19313.bugfix.rst b/docs/changes/io.ascii/19313.bugfix.rst new file mode 100644 index 000000000000..66ce4a8d422a --- /dev/null +++ b/docs/changes/io.ascii/19313.bugfix.rst @@ -0,0 +1,2 @@ +Modify ECSV reader to handle header "meta" entries that are not marked with the +"!!omap" tag, but are otherwise valid YAML types allowed by the ECSV specification. diff --git a/docs/changes/io.fits/.gitkeep b/docs/changes/io.fits/.gitkeep new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/docs/changes/io.fits/19023.bugfix.rst b/docs/changes/io.fits/19023.bugfix.rst new file mode 100644 index 000000000000..bfca12894ad4 --- /dev/null +++ b/docs/changes/io.fits/19023.bugfix.rst @@ -0,0 +1 @@ +Fix ``getdata()``'s lower and upper keywords. diff --git a/docs/changes/io.fits/19025.bugfix.rst b/docs/changes/io.fits/19025.bugfix.rst new file mode 100644 index 000000000000..b04fb3b86933 --- /dev/null +++ b/docs/changes/io.fits/19025.bugfix.rst @@ -0,0 +1,2 @@ +Fix use of TNULL with float columns in ASCII tables. Undefined values in float +columns are now replaced by NaNs (instead of 0). diff --git a/docs/changes/io.fits/19267.api.rst b/docs/changes/io.fits/19267.api.rst new file mode 100644 index 000000000000..47d4a300d82e --- /dev/null +++ b/docs/changes/io.fits/19267.api.rst @@ -0,0 +1,4 @@ +``np.char.chararray`` being deprecated in Numpy 2.5, in a future version +``io.fits`` will return a normal array instead of a ``chararray`` for string +columns. As a consequence the special chararray methods are deprecated (e.g., +``.rstrip()`` or ``.decode()``). Use ``np.strings`` functions instead. diff --git a/docs/changes/io.fits/19294.bugfix.rst b/docs/changes/io.fits/19294.bugfix.rst new file mode 100644 index 000000000000..a78a89aa4ee8 --- /dev/null +++ b/docs/changes/io.fits/19294.bugfix.rst @@ -0,0 +1,4 @@ +Fix for ``fsspec`` configuration in ``fits.open`` for remote FITS files. ``fits.open`` +now accepts the keyword argument ``fsspec_filesystem``, which is used to configure, +e.g., the buffer block size. Remote FITS file objects are then opened with +``fsspec.filesystem.open``. diff --git a/docs/changes/io.fits/19362.bugfix.rst b/docs/changes/io.fits/19362.bugfix.rst new file mode 100644 index 000000000000..ce82b255df9e --- /dev/null +++ b/docs/changes/io.fits/19362.bugfix.rst @@ -0,0 +1 @@ +Fix a bug which caused CompImageHDU to have both SIMPLE and XTENSION keywords when initialized from a PrimaryHDU header. diff --git a/docs/changes/io.fits/19363.bugfix.rst b/docs/changes/io.fits/19363.bugfix.rst new file mode 100644 index 000000000000..a0c61a1ebf9e --- /dev/null +++ b/docs/changes/io.fits/19363.bugfix.rst @@ -0,0 +1 @@ +Fix support for reading FITS files using image tile compression with UNCOMPRESSED_DATA columns. diff --git a/docs/changes/io.fits/19367.bugfix.rst b/docs/changes/io.fits/19367.bugfix.rst new file mode 100644 index 000000000000..ed5faafc252b --- /dev/null +++ b/docs/changes/io.fits/19367.bugfix.rst @@ -0,0 +1 @@ +Fixed a bug that caused reading FITS files to not work properly on WASM when relying on the default memmap settings. diff --git a/docs/changes/io.fits/19374.feature.rst b/docs/changes/io.fits/19374.feature.rst new file mode 100644 index 000000000000..e75c4a1939fa --- /dev/null +++ b/docs/changes/io.fits/19374.feature.rst @@ -0,0 +1,7 @@ +Added a ``logical_as_bytes`` parameter to ``fits.open()`` that, when set to +``True``, causes FITS logical columns to be read as bytes (``S1``) instead of +``bool``, preserving NULL (undefined) values that would otherwise be silently +converted to ``False``. Columns read this way round-trip correctly when written +back to a FITS file, preserving ``b'T'``, ``b'F'``, and ``b'\x00'`` (NULL) +values. A warning is now emitted when NULL values are present and +``logical_as_bytes`` is not set. diff --git a/docs/changes/io.fits/19404.bugfix.rst b/docs/changes/io.fits/19404.bugfix.rst new file mode 100644 index 000000000000..a28512f6d491 --- /dev/null +++ b/docs/changes/io.fits/19404.bugfix.rst @@ -0,0 +1,4 @@ +Fixed silent data corruption in ``FITS_rec.__setitem__`` when using negative +slice indices. Assigning to slices like ``data[-2:] = new_rows`` previously +wrote to the wrong rows because negative indices were clamped to 0 instead of +being resolved relative to the array length. diff --git a/docs/changes/io.fits/19416.bugfix.rst b/docs/changes/io.fits/19416.bugfix.rst new file mode 100644 index 000000000000..2278a62e0a79 --- /dev/null +++ b/docs/changes/io.fits/19416.bugfix.rst @@ -0,0 +1 @@ +Fixed a bug that caused compressed FITS files to become corrupted when opened in update mode and modifying the header. diff --git a/docs/changes/io.fits/19438.bugfix.rst b/docs/changes/io.fits/19438.bugfix.rst new file mode 100644 index 000000000000..1a35992df565 --- /dev/null +++ b/docs/changes/io.fits/19438.bugfix.rst @@ -0,0 +1,3 @@ +Fix a bug that caused header verification for CompImageHDU to not work correctly and +in some cases produce corrupt files when the decompressed header was a primary HDU +header not an extension header. diff --git a/docs/changes/io.fits/19592.bugfix.rst b/docs/changes/io.fits/19592.bugfix.rst new file mode 100644 index 000000000000..3a9de9f60951 --- /dev/null +++ b/docs/changes/io.fits/19592.bugfix.rst @@ -0,0 +1,3 @@ +Prevent creating RICE_1 or PLIO_1 compressed files with (u)int64 data. If +possible without overflow data is converted to (u)int32, otherwise an error is +raised. diff --git a/docs/changes/io.fits/19629.bugfix.rst b/docs/changes/io.fits/19629.bugfix.rst new file mode 100644 index 000000000000..4e4cb01e3b0e --- /dev/null +++ b/docs/changes/io.fits/19629.bugfix.rst @@ -0,0 +1 @@ +Fix variable-length array logical (``PL``/``QL``) columns being written as 1/0 bytes instead of FITS T/F and read back as raw int8 (84/70) instead of bool. Files written by astropy <= 7.2.0 (which used the legacy 0x00/0x01 encoding) are still readable: such files are detected on read and decoded correctly with an ``AstropyUserWarning``. diff --git a/docs/changes/io.fits/19638.api.rst b/docs/changes/io.fits/19638.api.rst new file mode 100644 index 000000000000..0e16a52a612d --- /dev/null +++ b/docs/changes/io.fits/19638.api.rst @@ -0,0 +1,2 @@ +The ``strip_spaces`` option in ``Table.read`` to strip trailing whitespaces in +string columns is now True by default. Set it to False to keep the old behavior. diff --git a/docs/changes/io.fits/19659.feature.rst b/docs/changes/io.fits/19659.feature.rst new file mode 100644 index 000000000000..0d6a13089478 --- /dev/null +++ b/docs/changes/io.fits/19659.feature.rst @@ -0,0 +1,9 @@ +Extended the ``logical_as_bytes`` option of ``fits.open()`` to apply to +variable-length array (``PL``/``QL``) logical columns. When ``True``, +VLA logical columns are returned as ``S1`` byte arrays (one byte per +entry) so that ``b'\x00'`` (NULL) is distinguishable from ``b'F'`` +(False); ``S1`` row arrays also round-trip through writing. A warning +is emitted when NULL bytes are present in a VLA logical column read +with ``logical_as_bytes=False``, except for the ambiguous case of a +column whose heap is entirely ``\x00`` (which may instead be an +all-False column written by astropy <= 7.2.0). diff --git a/docs/changes/io.fits/19670.bugfix.rst b/docs/changes/io.fits/19670.bugfix.rst new file mode 100644 index 000000000000..5c037ccd5bc8 --- /dev/null +++ b/docs/changes/io.fits/19670.bugfix.rst @@ -0,0 +1,8 @@ +Fix a bug that caused uint64 data to be silently shifted by 2**63 when +round-tripping through compressed FITS files (the FITS BZERO=2**63 offset was +missing at compression time but still applied on read). Extend the existing +RICE_1 and PLIO_1 64-bit-to-32-bit conversion fallback to also cover uint64 +input and HCOMPRESS_1, and fix big-endian 64-bit input being silently truncated +by the conversion check instead of raising. Reject PLIO_1 with unsigned +multi-byte input outright, since the BZERO convention used to store unsigned +FITS data produces negative values that PLIO cannot encode. diff --git a/docs/changes/io.fits/19738.bugfix.rst b/docs/changes/io.fits/19738.bugfix.rst new file mode 100644 index 000000000000..5839e96cb25b --- /dev/null +++ b/docs/changes/io.fits/19738.bugfix.rst @@ -0,0 +1,4 @@ +Fix a bug that caused int8 data to be shifted by 128 when round-tripping +through compressed FITS files (the FITS BZERO=-128 offset was missing at +compression time but still applied on read), so writing ``[0, 1]`` and +reading back returned ``[-128, -127]``. diff --git a/docs/changes/io.misc/.gitkeep b/docs/changes/io.misc/.gitkeep new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/docs/changes/io.misc/19658.bugfix.rst b/docs/changes/io.misc/19658.bugfix.rst new file mode 100644 index 000000000000..2aa6b893a662 --- /dev/null +++ b/docs/changes/io.misc/19658.bugfix.rst @@ -0,0 +1,3 @@ +Fixed reading parquet files written by pandas 3.0+, where string columns are +encoded as ``large_string`` rather than ``string`` and previously round-tripped +to ``object`` dtype instead of the expected fixed-width Unicode dtype. diff --git a/docs/changes/io.registry/.gitkeep b/docs/changes/io.registry/.gitkeep new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/docs/changes/io.registry/17571.api.rst b/docs/changes/io.registry/17571.api.rst new file mode 100644 index 000000000000..f9a43ffffcb6 --- /dev/null +++ b/docs/changes/io.registry/17571.api.rst @@ -0,0 +1,2 @@ +The ``help`` methods from ``Table.read`` and ``Table.write`` now raise an +exception if called under Python's optimized mode (``-OO`` flag). diff --git a/docs/changes/io.votable/.gitkeep b/docs/changes/io.votable/.gitkeep new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/docs/changes/io.votable/18454.perf.rst b/docs/changes/io.votable/18454.perf.rst new file mode 100644 index 000000000000..42fe8bcffe0c --- /dev/null +++ b/docs/changes/io.votable/18454.perf.rst @@ -0,0 +1,10 @@ +Improve performance of Binary parsing by moving converters to Cython + +* **Numeric operations**: 47-57% faster +* **Mixed data types**: 37-45% faster +* **String operations**: 24-35% faster +* **Boolean fields**: 10-20% faster +* **Small overhead operations**: 29-43% faster + +Performance gains are consistent across dataset sizes from 200k to 1M rows. +The most substantial improvements are seen in numeric types with up to 50-60% reduction in processing time. diff --git a/docs/changes/io.votable/19499.feature.rst b/docs/changes/io.votable/19499.feature.rst new file mode 100644 index 000000000000..d46cc3234534 --- /dev/null +++ b/docs/changes/io.votable/19499.feature.rst @@ -0,0 +1 @@ +Added a ``write`` method to ``VOTableFile``. diff --git a/docs/changes/io.votable/19593.api.rst b/docs/changes/io.votable/19593.api.rst new file mode 100644 index 000000000000..0206d4c4257e --- /dev/null +++ b/docs/changes/io.votable/19593.api.rst @@ -0,0 +1,3 @@ +DataOrigin updated with IVOA version 1.2: + +* Terms changed in ``astropy.io.votable.dataorigin`` (``ivoid`` and ``editor`` renamed to ``ivoid_data`` and ``journal``) diff --git a/docs/changes/modeling/.gitkeep b/docs/changes/modeling/.gitkeep new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/docs/changes/modeling/19189.bugfix.rst b/docs/changes/modeling/19189.bugfix.rst new file mode 100644 index 000000000000..2983132014bf --- /dev/null +++ b/docs/changes/modeling/19189.bugfix.rst @@ -0,0 +1 @@ +Bugfix for ``Parameter.value`` accessor when the parameter value has not been set yet. diff --git a/docs/changes/nddata/.gitkeep b/docs/changes/nddata/.gitkeep new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/docs/changes/nddata/18862.feature.rst b/docs/changes/nddata/18862.feature.rst new file mode 100644 index 000000000000..53311a9e7d5e --- /dev/null +++ b/docs/changes/nddata/18862.feature.rst @@ -0,0 +1,4 @@ +Implemented support for the ``.flags`` attribute in +``CCDData.to_hdu()``, enabling the CCDData class to write and read +``.flags`` in FITS format. +Previously this was silent no-op even though it should have raised a ``NotImplementedError``. diff --git a/docs/changes/nddata/19389.bugfix.rst b/docs/changes/nddata/19389.bugfix.rst new file mode 100644 index 000000000000..5a7cf47501c4 --- /dev/null +++ b/docs/changes/nddata/19389.bugfix.rst @@ -0,0 +1 @@ +Fixed ``CCDData.read()`` logging a spurious info message when the unit passed by the caller matches the BUNIT value in the FITS header. diff --git a/docs/changes/samp/.gitkeep b/docs/changes/samp/.gitkeep new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/docs/changes/samp/19231.bugfix.rst b/docs/changes/samp/19231.bugfix.rst new file mode 100644 index 000000000000..cb316403bb4d --- /dev/null +++ b/docs/changes/samp/19231.bugfix.rst @@ -0,0 +1 @@ +Fixed an XML External Entity (XXE) vulnerability in SAMP XML-RPC communication by disabling external entity resolution. diff --git a/docs/changes/stats/.gitkeep b/docs/changes/stats/.gitkeep new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/docs/changes/stats/19372.bugfix.rst b/docs/changes/stats/19372.bugfix.rst new file mode 100644 index 000000000000..05d5b3d4b6aa --- /dev/null +++ b/docs/changes/stats/19372.bugfix.rst @@ -0,0 +1 @@ +Fixed pickling of ``SigmaClip`` objects when Bottleneck is installed. ``_dtype_dispatch`` returned a local closure which cannot be pickled; replaced with a picklable ``_DtypeDispatch`` class. diff --git a/docs/changes/stats/19457.bugfix.rst b/docs/changes/stats/19457.bugfix.rst new file mode 100644 index 000000000000..d43520882c01 --- /dev/null +++ b/docs/changes/stats/19457.bugfix.rst @@ -0,0 +1 @@ +Fixed missing pointer dereference in ``fast_sigma_clip.c`` that caused ``mad_buffer`` to be allocated regardless of the ``use_mad_std`` value. diff --git a/docs/changes/stats/19498.perf.rst b/docs/changes/stats/19498.perf.rst new file mode 100644 index 000000000000..34437fa205ff --- /dev/null +++ b/docs/changes/stats/19498.perf.rst @@ -0,0 +1,3 @@ +Vectorized the ``var-width`` mode in ``RipleysKEstimator`` by precomputing the +full distance matrix, replacing a triple nested Python loop. This yields +speedups of several thousand times for typical input sizes. diff --git a/docs/changes/table/.gitkeep b/docs/changes/table/.gitkeep new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/docs/changes/table/18703.feature.rst b/docs/changes/table/18703.feature.rst new file mode 100644 index 000000000000..8763d0ce1b31 --- /dev/null +++ b/docs/changes/table/18703.feature.rst @@ -0,0 +1,4 @@ +Support writing table indices to FITS, HDF5, and ECSV formats and subsequently reading +them back to round-trip the original indexed table. This stores the index data as +temporary columns in the output, similar to the mechanism for serializing mixin columns +like ``SkyCoord``. diff --git a/docs/changes/table/19123.feature.rst b/docs/changes/table/19123.feature.rst new file mode 100644 index 000000000000..cfde5f504b42 --- /dev/null +++ b/docs/changes/table/19123.feature.rst @@ -0,0 +1 @@ +Added ``conf.multidim_threshold`` configuration option to control display of multidimensional table columns. Setting this to a value like 3 will display full content for small arrays (e.g., ``[1 2 3]`` for 3-vectors), while larger arrays revert to the abbreviated ``first .. last`` format. diff --git a/docs/changes/table/19173.feature.rst b/docs/changes/table/19173.feature.rst new file mode 100644 index 000000000000..38d1c8964750 --- /dev/null +++ b/docs/changes/table/19173.feature.rst @@ -0,0 +1,4 @@ +Enable conversion of tables with multidimensional columns to pandas DataFrames: + +* Previously, ``to_pandas()`` and ``to_df()`` would raise a ``ValueError`` for columns containing lists or arrays (for example, ``[[1, 2], [3, 4]]``). +* Now these columns are automatically converted to 1D object arrays compatible with pandas. diff --git a/docs/changes/table/19198.api.rst b/docs/changes/table/19198.api.rst new file mode 100644 index 000000000000..03354aa8d672 --- /dev/null +++ b/docs/changes/table/19198.api.rst @@ -0,0 +1,5 @@ +The private ``astropy.table.np_utils`` module has been removed. It did not +contain any code that was still used except the definition of +``TableMergeError``, which should be imported from ``astropy.table``. All +other routines were long ago moved to either ``astropy.table.operations`` or +``astropy.utils.metadata``. diff --git a/docs/changes/table/19199.bugfix.rst b/docs/changes/table/19199.bugfix.rst new file mode 100644 index 000000000000..140582a5f856 --- /dev/null +++ b/docs/changes/table/19199.bugfix.rst @@ -0,0 +1,2 @@ +Tables can now be joined and stacked also if they contain columns with +user-defined data types such as ``QuadPrecDtype``. diff --git a/docs/changes/table/19358.bugfix.rst b/docs/changes/table/19358.bugfix.rst new file mode 100644 index 000000000000..f837ee97b524 --- /dev/null +++ b/docs/changes/table/19358.bugfix.rst @@ -0,0 +1,3 @@ +īģŋFixed ``AssertionError`` when passing ``numpy.bool_`` as the +``index`` argument to ``Table.to_pandas()`` or ``Table.to_dataframe()``. +The correct ``ValueError`` is now raised instead. diff --git a/docs/changes/table/19450.bugfix.rst b/docs/changes/table/19450.bugfix.rst new file mode 100644 index 000000000000..5f3e137dea8f --- /dev/null +++ b/docs/changes/table/19450.bugfix.rst @@ -0,0 +1,2 @@ +Fixed table index getting corrupted when a row assignment raises an exception +mid-update. The index is now properly restored to its original state on failure. diff --git a/docs/changes/table/19466.bugfix.rst b/docs/changes/table/19466.bugfix.rst new file mode 100644 index 000000000000..10c79a8de077 --- /dev/null +++ b/docs/changes/table/19466.bugfix.rst @@ -0,0 +1,2 @@ +Fixes a problem where deepcopying a MaskedColumn did not correctly create the ``info`` +attribute. This resulted in an inability to print the column after the deepcopy. diff --git a/docs/changes/table/19542.api.rst b/docs/changes/table/19542.api.rst new file mode 100644 index 000000000000..871f2bf47231 --- /dev/null +++ b/docs/changes/table/19542.api.rst @@ -0,0 +1,5 @@ +Setting the ``Column.dtype`` attribute is now deprecated since mutating +an array is unsafe if an array is shared, especially by multiple +threads. As an alternative, you can create a view with a new dtype +via ``column.view(dtype=new_dtype)``. This follows a similar +deprecation in numpy 2.5.0. diff --git a/docs/changes/template.rst b/docs/changes/template.rst new file mode 100644 index 000000000000..7b4dfcb21b99 --- /dev/null +++ b/docs/changes/template.rst @@ -0,0 +1,43 @@ +{% if render_title %} +{% if versiondata.name %} +{{ versiondata.name }} {{ versiondata.version }} ({{ versiondata.date }}) +{{ top_underline * ((versiondata.name + versiondata.version + versiondata.date)|length + 4)}} +{% else %} +{{ versiondata.version }} ({{ versiondata.date }}) +{{ top_underline * ((versiondata.version + versiondata.date)|length + 3)}} +{% endif %} +{% endif %} + +{% for category, val in definitions.items() %} + +{% set underline = underlines[0] %} +{{ definitions[category]['name'] }} +{{ underline * definitions[category]['name']|length }} +{% set underline = underlines[1] %} + +{% for section, _ in sections.items() %} +{% if section and category in sections[section] %} +{{section}} +{{ underline * section|length }} + +{% endif %} +{% if sections[section] and category in sections[section] %} +{% if definitions[category]['showcontent'] %} +{% for text, values in sections[section][category].items() %} +- {{ text }} [{{ values|join(', ') }}] + +{% endfor %} +{% else %} +- {{ sections[section][category]['']|join(', ') }} + +{% endif %} +{% if sections[section][category]|length == 0 %} +No significant changes. + +{% else %} +{% endif %} +{% else %} +{# No significant changes. #} +{% endif %} +{% endfor %} +{% endfor %} diff --git a/docs/changes/tests/.gitkeep b/docs/changes/tests/.gitkeep new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/docs/changes/tests/17883.api.rst b/docs/changes/tests/17883.api.rst new file mode 100644 index 000000000000..dc7c5c09a423 --- /dev/null +++ b/docs/changes/tests/17883.api.rst @@ -0,0 +1,4 @@ +API changes towards a future removal of astropy test runner: + +* Removed ``astropy.tests.command`` module that was deprecated in v6.0. +* Officially deprecated ``astropy.test``, ``astropy.tests.runner.TestRunnerBase``, and ``astropy.tests.runner.TestRunner`` (previously pending deprecation). This will also affect downstream ``packagename.test`` generated using ``TestRunner``. The deprecated functionality will be removed in a future release after a deprecation period as per Astropy deprecation policy. diff --git a/docs/changes/time/.gitkeep b/docs/changes/time/.gitkeep new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/docs/changes/time/19368.bugfix.rst b/docs/changes/time/19368.bugfix.rst new file mode 100644 index 000000000000..79a5070fe64c --- /dev/null +++ b/docs/changes/time/19368.bugfix.rst @@ -0,0 +1 @@ +Fixed missing ``goto fail`` in ``create_parser`` in ``parse_times.c`` after setting a ``ValueError`` for invalid parameter array size, preventing execution from continuing with an exception already set. diff --git a/docs/changes/timeseries/.gitkeep b/docs/changes/timeseries/.gitkeep new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/docs/changes/timeseries/17842.feature.rst b/docs/changes/timeseries/17842.feature.rst new file mode 100644 index 000000000000..cf68ecacab78 --- /dev/null +++ b/docs/changes/timeseries/17842.feature.rst @@ -0,0 +1,4 @@ +Adds support for Low Rank Approximation (LRA) to ``trig_sum``. +This is enabled by passing the new argument ``algorithm='lra'``, +which is now the default option for ``fast`` and +``fastchi2`` implementations. diff --git a/docs/changes/uncertainty/.gitkeep b/docs/changes/uncertainty/.gitkeep new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/docs/changes/units/.gitkeep b/docs/changes/units/.gitkeep new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/docs/changes/units/19055.bugfix.rst b/docs/changes/units/19055.bugfix.rst new file mode 100644 index 000000000000..c658508f0e62 --- /dev/null +++ b/docs/changes/units/19055.bugfix.rst @@ -0,0 +1,3 @@ +Fixed a bug in the ``np.average`` function when weights with units and a +different shape to the input array were passed and the optionally-returned sum +of the weights was requested. The sum of the weights now has correct units. diff --git a/docs/changes/units/19104.feature.rst b/docs/changes/units/19104.feature.rst new file mode 100644 index 000000000000..f026edd36342 --- /dev/null +++ b/docs/changes/units/19104.feature.rst @@ -0,0 +1,2 @@ +Add support for ``atol`` and ``rtol`` parameters from ``np.matrix_rank`` +when called on a ``Quantity`` object. diff --git a/docs/changes/units/19214.feature.rst b/docs/changes/units/19214.feature.rst new file mode 100644 index 000000000000..470bf0a70dbc --- /dev/null +++ b/docs/changes/units/19214.feature.rst @@ -0,0 +1 @@ +Allow 1d vector-like strings, with or without units, to be converted into ``Quantity``. diff --git a/docs/changes/units/19360.bugfix.rst b/docs/changes/units/19360.bugfix.rst new file mode 100644 index 000000000000..5516e6a7ffeb --- /dev/null +++ b/docs/changes/units/19360.bugfix.rst @@ -0,0 +1 @@ +Fixed incorrect unit returned by ``numpy.diff`` when applied to logarithmic quantities (e.g., magnitudes). diff --git a/docs/changes/utils/.gitkeep b/docs/changes/utils/.gitkeep new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/docs/changes/utils/19142.bugfix.rst b/docs/changes/utils/19142.bugfix.rst new file mode 100644 index 000000000000..ddbd2191c3cc --- /dev/null +++ b/docs/changes/utils/19142.bugfix.rst @@ -0,0 +1 @@ +Pickling ``Masked`` subclasses with an initialized ``info`` attribute no longer fails. diff --git a/docs/changes/utils/19351.bugfix.rst b/docs/changes/utils/19351.bugfix.rst new file mode 100644 index 000000000000..1211519191af --- /dev/null +++ b/docs/changes/utils/19351.bugfix.rst @@ -0,0 +1 @@ +``ShapedLikeNDArray.take()`` now raises ``NotImplementedError`` when ``out`` is passed, instead of returning the exception object. diff --git a/docs/changes/utils/19534.bugfix.rst b/docs/changes/utils/19534.bugfix.rst new file mode 100644 index 000000000000..6b4bf8d5a3b0 --- /dev/null +++ b/docs/changes/utils/19534.bugfix.rst @@ -0,0 +1,3 @@ +Ensure that ``utils.masked.get_data_and_mask`` works properly with containers, +returning ``None`` for the mask if ``masked`` is not set (instead of returning +an all-False array). diff --git a/docs/changes/utils/19594.bugfix.rst b/docs/changes/utils/19594.bugfix.rst new file mode 100644 index 000000000000..f3c85ec51eb5 --- /dev/null +++ b/docs/changes/utils/19594.bugfix.rst @@ -0,0 +1,4 @@ +The dummy file object used by ``astropy.utils.misc.silence`` to replace +``sys.stdout``/``sys.stderr`` now implements ``flush()`` and ``isatty()``, +so importing libraries that probe the stream (e.g. IPython 9.13 at import +time) no longer raises ``AttributeError`` under ``silence``. diff --git a/docs/changes/utils/19631.api.rst b/docs/changes/utils/19631.api.rst new file mode 100644 index 000000000000..3d673871eb30 --- /dev/null +++ b/docs/changes/utils/19631.api.rst @@ -0,0 +1,3 @@ +Cache directories are not created on discovery anymore. Meaning some functions +that only retrieve a file or directory by name and without populating it won't +guarantee that the path returned already exists. diff --git a/docs/changes/utils/19650.feature.rst b/docs/changes/utils/19650.feature.rst new file mode 100644 index 000000000000..0e9dee1bbb14 --- /dev/null +++ b/docs/changes/utils/19650.feature.rst @@ -0,0 +1,3 @@ +``astropy.utils.data`` functions are now more resilient +against a missing cache directory and avoid forcing its +creation on call. diff --git a/docs/changes/utils/19672.bugfix.rst b/docs/changes/utils/19672.bugfix.rst new file mode 100644 index 000000000000..5cec185fa9b9 --- /dev/null +++ b/docs/changes/utils/19672.bugfix.rst @@ -0,0 +1 @@ +Fixed a bug where values returned by ``cache_contents`` could include files and symlinks, instead of just directories. diff --git a/docs/changes/utils/19679.api.rst b/docs/changes/utils/19679.api.rst new file mode 100644 index 000000000000..aa4b6a567306 --- /dev/null +++ b/docs/changes/utils/19679.api.rst @@ -0,0 +1,2 @@ +Removed deprecated functions ``is_path_hidden`` and ``walk_skip_hidden`` +from ``astropy.utils.misc``. diff --git a/docs/changes/utils/19700.bugfix.rst b/docs/changes/utils/19700.bugfix.rst new file mode 100644 index 000000000000..b44f48635547 --- /dev/null +++ b/docs/changes/utils/19700.bugfix.rst @@ -0,0 +1 @@ +Add missing ``stacklevel`` arguments in warnings emitted from ``astropy.utils.data`` APIs diff --git a/docs/changes/utils/19701.feature.rst b/docs/changes/utils/19701.feature.rst new file mode 100644 index 000000000000..9f5f4829d0df --- /dev/null +++ b/docs/changes/utils/19701.feature.rst @@ -0,0 +1,2 @@ +Always annotate exceptions with requested url in +``astropy.utils.data.download_file``. diff --git a/docs/changes/visualization/.gitkeep b/docs/changes/visualization/.gitkeep new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/docs/changes/visualization/18994.api.rst b/docs/changes/visualization/18994.api.rst new file mode 100644 index 000000000000..33bd975faf93 --- /dev/null +++ b/docs/changes/visualization/18994.api.rst @@ -0,0 +1,4 @@ +Removed the deprecated ``min_cut`` and ``max_cut`` keywords from the +``simple_norm`` function and the ``fits2bitmap`` command-line script. +Use the ``vmin`` and ``vmax`` keywords, respectively, to specify the cut +levels. diff --git a/docs/changes/visualization/19539.feature.rst b/docs/changes/visualization/19539.feature.rst new file mode 100644 index 000000000000..8d3da7dea32d --- /dev/null +++ b/docs/changes/visualization/19539.feature.rst @@ -0,0 +1,2 @@ +Expose ``MplQuantityConverter`` class in ``astropy.visualization`` and +except numpy arrays in its ``convert`` and ``default_units`` methods. diff --git a/docs/changes/visualization/19623.feature.rst b/docs/changes/visualization/19623.feature.rst new file mode 100644 index 000000000000..012bc88239e6 --- /dev/null +++ b/docs/changes/visualization/19623.feature.rst @@ -0,0 +1,2 @@ +Added the ``imshow_simple_norm`` function to provide a more compact interface +to the ``SimpleNorm``/``simple_norm`` interface for quick visualization. diff --git a/docs/changes/wcs/.gitkeep b/docs/changes/wcs/.gitkeep new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/docs/changes/wcs/18743.feature.rst b/docs/changes/wcs/18743.feature.rst new file mode 100644 index 000000000000..193e26fd6284 --- /dev/null +++ b/docs/changes/wcs/18743.feature.rst @@ -0,0 +1 @@ +``world_to_pixel`` and ``pixel_to_world`` now can take ``Masked`` input. diff --git a/docs/changes/wcs/18902.api.rst b/docs/changes/wcs/18902.api.rst new file mode 100644 index 000000000000..4094e0fac538 --- /dev/null +++ b/docs/changes/wcs/18902.api.rst @@ -0,0 +1,5 @@ +``SpectralCoord`` conversions through the APE 14 high-level WCS API no +longer emit a warning when neither the WCS nor the input ``SpectralCoord`` +has an observer defined. Warnings are still emitted when only one of the +two has observer information (and the conversion therefore proceeds +without a velocity frame change). diff --git a/docs/changes/wcs/19064.api.rst b/docs/changes/wcs/19064.api.rst new file mode 100644 index 000000000000..719238e8177b --- /dev/null +++ b/docs/changes/wcs/19064.api.rst @@ -0,0 +1,6 @@ +``wcs.high_level_objects_to_values`` and ``wcs.values_to_high_level_objects`` +no longer require their ``low_level_wcs`` argument to be a full +``astropy.wcs.wcsapi.BaseLowLevelWCS`` instance. Any object exposing +``world_axis_object_classes`` and ``world_axis_object_components`` attributes +is accepted, and ``serialized_classes`` is now optional (treated as ``False`` +if absent). diff --git a/docs/changes/wcs/19506.bugfix.rst b/docs/changes/wcs/19506.bugfix.rst new file mode 100644 index 000000000000..9c33379c049b --- /dev/null +++ b/docs/changes/wcs/19506.bugfix.rst @@ -0,0 +1 @@ +Fixed a bug where degree units were hardcoded into the FITS WCS APE 14 ``world_to_pixel`` method. diff --git a/docs/changes/wcs/19591.bugfix.rst b/docs/changes/wcs/19591.bugfix.rst new file mode 100644 index 000000000000..9ebd28094d1f --- /dev/null +++ b/docs/changes/wcs/19591.bugfix.rst @@ -0,0 +1,9 @@ +Lazily-populated caches on a ``WCS`` (such as the internal +``world_axis_object_components``/``world_axis_object_classes`` cache) are no +longer included in the pickled state. They are regenerated on demand after +unpickling, which keeps pickling robust even when a cache entry holds a +non-picklable value. + +Fixed a bug where the ``preserve_units`` option passed to the ``WCS`` +constructor was silently reset to ``False`` when a ``WCS`` object was pickled +and unpickled. diff --git a/docs/changes/wcs/19720.bugfix.rst b/docs/changes/wcs/19720.bugfix.rst new file mode 100644 index 000000000000..904186bcbf61 --- /dev/null +++ b/docs/changes/wcs/19720.bugfix.rst @@ -0,0 +1 @@ +Fix reference-count handling after ``PyList_SetItem`` steals references. diff --git a/docs/conf.py b/docs/conf.py index 20674d3836ea..71c1d3105eff 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Licensed under a 3-clause BSD style license - see LICENSE.rst # # Astropy documentation build configuration file. @@ -8,8 +7,7 @@ # Note that not all possible configuration values are present in this file. # # All configuration values have a default. Some values are defined in -# the global Astropy configuration which is loaded here before anything else. -# See astropy.sphinx.conf for which values are set there. +# the global Astropy configuration which is loaded here before anything else. # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the @@ -21,107 +19,551 @@ # done. If the sys.path entry above is added, when the astropy.sphinx.conf # import occurs, it will import the *source* version of astropy instead of the # version installed (if invoked as "make html" or directly with sphinx), or the -# version in the build directory (if "python setup.py build_sphinx" is used). +# version in the build directory. # Thus, any C-extensions that are needed to build the documentation will *not* # be accessible, and the documentation will not build correctly. +# See sphinx_astropy.conf for which values are set there. -# Load all of the global Astropy configuration -from astropy.sphinx.conf import * +import doctest +import inspect +import operator +import os +import sys +import tomllib +import warnings +from datetime import UTC, datetime +from importlib import metadata +from pathlib import Path +from packaging.requirements import Requirement +from packaging.specifiers import SpecifierSet +from sphinx.util import logging + +# from docs import global_substitutions + +logger = logging.getLogger(__name__) + +# -- Check for missing dependencies ------------------------------------------- +missing_requirements = {} +for line in metadata.requires("astropy"): + if 'extra == "docs"' in line: + req = Requirement(line.split(";")[0]) + req_package = req.name.lower() + req_specifier = str(req.specifier) + + try: + version = metadata.version(req_package) + except metadata.PackageNotFoundError: + missing_requirements[req_package] = req_specifier + + if version not in SpecifierSet(req_specifier, prereleases=True): + missing_requirements[req_package] = req_specifier + +if missing_requirements: + msg = ( + "The following packages could not be found and are required to " + "build the documentation:\n" + "%s" + '\nPlease install the "docs" requirements.', + "\n".join([f" * {key} {val}" for key, val in missing_requirements.items()]), + ) + logger.error(msg) + sys.exit(1) + +from sphinx_astropy.conf.v3 import * # noqa: E402, F403 +from sphinx_astropy.conf.v3 import ( # noqa: E402 + exclude_patterns, + extensions, + intersphinx_mapping, + numpydoc_xref_aliases, + numpydoc_xref_astropy_aliases, + numpydoc_xref_ignore, +) + +# -- Plot configuration ------------------------------------------------------- +plot_rcparams = { + "axes.labelsize": "large", + "figure.figsize": (6, 6), + "figure.subplot.hspace": 0.5, + "savefig.bbox": "tight", + "savefig.facecolor": "none", +} +plot_apply_rcparams = True +plot_html_show_source_link = False +plot_formats = ["png", "svg", "pdf"] +# Don't use the default - which includes a numpy and matplotlib import +plot_pre_code = "" # -- General configuration ---------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. -#needs_sphinx = '1.1' +needs_sphinx = "8.2.0" # keep in sync with pyproject.toml -# The intersphinx_mapping in astropy.sphinx.conf refers to astropy for -# the benefit of affiliated packages who want to refer to objects in -# the astropy core. However, we don't want to cyclically reference -# astropy in its own build so we remove it here. -del intersphinx_mapping['astropy'] +# add any custom intersphinx for astropy +intersphinx_resolve_self = "astropy" +intersphinx_mapping.update( + { + "pyerfa": ("https://pyerfa.readthedocs.io/en/stable/", None), + "pytest": ("https://docs.pytest.org/en/stable/", None), + "ipython": ("https://ipython.readthedocs.io/en/stable/", None), + "pandas": ("https://pandas.pydata.org/docs/", None), + "sphinx_automodapi": ( + "https://sphinx-automodapi.readthedocs.io/en/stable/", + None, + ), + "asdf-astropy": ("https://asdf-astropy.readthedocs.io/en/latest/", None), + "fsspec": ("https://filesystem-spec.readthedocs.io/en/latest/", None), + "cycler": ("https://matplotlib.org/cycler/", None), + "dask": ("https://docs.dask.org/en/stable/", None), + } +) # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. -exclude_patterns.append('_templates') -exclude_patterns.append('_pkgtemplate.rst') +# .inc.rst mean *include* files, don't have sphinx process them +exclude_patterns += ["_templates", "changes", "_pkgtemplate.rst", "**/*.inc.rst"] # Add any paths that contain templates here, relative to this directory. -if 'templates_path' not in locals(): # in case parent conf.py defines it +if "templates_path" not in locals(): # in case parent conf.py defines it templates_path = [] -templates_path.append('_templates') +templates_path.append("_templates") + +extensions += [ + "matplotlib.sphinxext.roles", + "sphinx_changelog", + "sphinx_design", + "sphinxcontrib.globalsubs", +] + +# Grab minversion from pyproject.toml +with (Path(__file__).parents[1] / "pyproject.toml").open("rb") as f: + pyproject = tomllib.load(f) + +# Manually register doctest options since matplotlib 3.5 messed up allowing them +# from pytest-doctestplus +IGNORE_OUTPUT = doctest.register_optionflag("IGNORE_OUTPUT") +REMOTE_DATA = doctest.register_optionflag("REMOTE_DATA") +FLOAT_CMP = doctest.register_optionflag("FLOAT_CMP") + +# Whether to create cross-references for the parameter types in the +# Parameters, Other Parameters, Returns and Yields sections of the docstring. +numpydoc_xref_param_type = True -# This is added to the end of RST files - a good place to put substitutions to -# be used globally. -rst_epilog += """ -""" +# Words not to cross-reference. Most likely, these are common words used in +# parameter type descriptions that may be confused for classes of the same +# name. The base set comes from sphinx-astropy. We add more here. +numpydoc_xref_ignore.update( + { + "mixin", + "Any", # aka something that would be annotated with `typing.Any` + # needed in subclassing numpy # TODO! revisit + "Arguments", + "Path", + # TODO! not need to ignore. + "flag", + "bits", + } +) + +# Mappings to fully qualified paths (or correct ReST references) for the +# aliases/shortcuts used when specifying the types of parameters. +# Numpy provides some defaults +# https://github.com/numpy/numpydoc/blob/b352cd7635f2ea7748722f410a31f937d92545cc/numpydoc/xref.py#L62-L94 +# and a base set comes from sphinx-astropy. +# so here we mostly need to define Astropy-specific x-refs +numpydoc_xref_aliases.update( + { + # python & adjacent + "Any": "`~typing.Any`", + "file-like": ":term:`python:file-like object`", + "file": ":term:`python:file object`", + "path-like": ":term:`python:path-like object`", + "module": ":term:`python:module`", + "buffer-like": ":term:buffer-like", + "hashable": ":term:`python:hashable`", + # for matplotlib + "color": ":term:`color`", + # for numpy + "ints": ":class:`python:int`", + # for astropy + "number": ":term:`number`", + "Quantity": ":class:`~astropy.units.Quantity`", + "Representation": ":class:`~astropy.coordinates.BaseRepresentation`", + "writable": ":term:`writable file-like object`", + "readable": ":term:`readable file-like object`", + "BaseHDU": ":doc:`HDU `", + } +) +# Add from sphinx-astropy 1) glossary aliases 2) physical types. +numpydoc_xref_aliases.update(numpydoc_xref_astropy_aliases) + +# Turn off table of contents entries for functions and classes +toc_object_entries = False + +# Disable type hints in the API documentation. +autodoc_typehints = "none" # -- Project information ------------------------------------------------------ -project = u'Astropy' -author = u'The Astropy Developers' -copyright = u'2012, ' + author +project = "Astropy" +author = "The Astropy Developers" +copyright = f"2011–{datetime.now(tz=UTC).year}, " + author # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. -import astropy - -# The short X.Y version. -version = astropy.__version__.split('-', 1)[0] # The full version, including alpha/beta/rc tags. -release = astropy.__version__ - - -# -- Options for HTML output --------------------------------------------------- +release = metadata.version(project) +# The short X.Y version. +version = ".".join(release.split(".")[:2]) -# A NOTE ON HTML THEMES -# The global astropy configuration uses a custom theme, 'bootstrap-astropy', -# which is installed along with astropy. A different theme can be used or -# the options for this theme can be modified by overriding some of the -# variables set in the global configuration. The variables set in the -# global configuration are listed below, commented out. +# Only include dev docs in dev version. +dev = "dev" in release +if not dev: + exclude_patterns += ["development/*"] -# Add any paths that contain custom themes here, relative to this directory. -# To use a different custom theme, add the directory containing the theme. -#html_theme_path = [] +# -- Options for the module index --------------------------------------------- -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. To override the custom theme, set this to the -# name of a builtin theme or the name of a custom theme in html_theme_path. -#html_theme = None +modindex_common_prefix = ["astropy."] -# Custom sidebar templates, maps document names to template names. -#html_sidebars = {} -# The name of an image file (within the static path) to use as favicon of the -# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 -# pixels large. -#html_favicon = '' +# -- Options for HTML output --------------------------------------------------- -# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, -# using the given strftime format. -#html_last_updated_fmt = '' +html_theme_options = { + "analytics": { + "google_analytics_id": "G-R0510VK4B6", + }, + "github_url": "https://github.com/astropy/astropy", + "use_edit_page_button": True, +} # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". -html_title = '{0} v{1}'.format(project, release) +html_title = f"{project} v{release}" + +html_static_path = ["_static"] +html_css_files = ["astropy.css"] +html_copy_source = False # Output file base name for HTML help builder. -htmlhelp_basename = project + 'doc' +htmlhelp_basename = project + "doc" + +# Set canonical URL from the Read the Docs Domain +html_baseurl = os.environ.get("READTHEDOCS_CANONICAL_URL", "") +def _custom_edit_url( + github_user, + github_repo, + github_version, + doc_path, + file_name, + default_edit_page_url_template, +): + """Create custom 'edit' URLs for API modules since they are dynamically generated.""" + if file_name.startswith("api/astropy.") and file_name.endswith(".rst"): + # this is a dynamically generated API page + astropy_path = file_name.removeprefix("api/astropy.").removesuffix(".rst") + item = operator.attrgetter(astropy_path)(astropy) # noqa: F405 + if module := getattr(item, "__module__", None): + mod_dir, _, mod_file = module.rpartition(".") + new_file_name = mod_file + ".py" + # Remove wrappings, such as functools.cache in astropy.units.equivalencies. + # This also avoids "could not find source" warnings. But don't remove + # a correct one for sharedmethod. + if module != "astropy.utils.decorators": + while wrapped := getattr(item, "__wrapped__", None): + item = wrapped + try: + line_no = inspect.findsource(item)[1] + except Exception: + # Warn if not just a cosmology instance, or a wcs compiled function. + if not ( + ( + "cosmology.realizations" in file_name + and not isinstance(item, type) + ) + or "modeling.tabular.Tabular" in file_name + or "astropy.wcs" in file_name + ): + warnings.warn( + f"could not find source for {doc_path=}, {file_name=}" + ) + else: + new_file_name += f"#L{line_no + 1}" + doc_path = mod_dir.replace(".", "/") + "/" + file_name = new_file_name + else: + if "cosmology.realizations.available" in file_name: + doc_path = "astropy/cosmology/" + file_name = "realizations.py" + else: + warnings.warn(f"could not find module for {doc_path=}, {file_name=}") + # Fall back for items that do not even have a module. Hope for the best. + doc_path = "astropy" + file_name = astropy_path.replace(".", "/") + + return default_edit_page_url_template.format( + github_user=github_user, + github_repo=github_repo, + github_version=github_version, + doc_path=doc_path, + file_name=file_name, + ) + + +# A dictionary of values to pass into the template engine's context for all pages. +html_context = { + "default_mode": "light", + "version_slug": os.environ.get("READTHEDOCS_VERSION") or "", + "to_be_indexed": ["stable", "latest"], + "is_development": dev, + "github_user": "astropy", + "github_repo": "astropy", + "github_version": "main", + "doc_path": "docs", + "edit_page_url_template": "{{ astropy_custom_edit_url(github_user, github_repo, github_version, doc_path, file_name, default_edit_page_url_template) }}", + "default_edit_page_url_template": "https://github.com/{github_user}/{github_repo}/edit/{github_version}/{doc_path}{file_name}", + "astropy_custom_edit_url": _custom_edit_url, + # Tell Jinja2 templates the build is running on Read the Docs + "READTHEDOCS": os.environ.get("READTHEDOCS", "") == "True", +} + +# Add any extra paths that contain custom files (such as robots.txt or +# .htaccess) here, relative to this directory. These files are copied +# directly to the root of the documentation. +html_extra_path = ["robots.txt"] + # -- Options for LaTeX output -------------------------------------------------- # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). -latex_documents = [('index', project + '.tex', project + u' Documentation', - author, 'manual')] +latex_documents = [ + ("index", project + ".tex", project + " Documentation", author, "manual") +] + +latex_logo = "_static/astropy_logo.pdf" # -- Options for manual page output -------------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). -man_pages = [('index', project.lower(), project + u' Documentation', - [author], 1)] +man_pages = [("index", project.lower(), project + " Documentation", [author], 1)] + +# Setting this URL is required by sphinx-astropy +github_issues_url = "https://github.com/astropy/astropy/issues/" +edit_on_github_branch = "main" + +# Enable nitpicky mode - which ensures that all references in the docs +# resolve. + +nitpicky = True +show_warning_types = True +# See docs/nitpick-exceptions file for the actual listing. +nitpick_ignore = [] +for line in open("nitpick-exceptions"): + if line.strip() == "" or line.startswith("#"): + continue + dtype, target = line.split(None, 1) + nitpick_ignore.append((dtype, target.strip())) + +suppress_warnings = [ + "config.cache", # our rebuild is okay +] + +# -- Options for linkcheck output ------------------------------------------- +linkcheck_retry = 5 +linkcheck_ignore = [ + "https://journals.aas.org/manuscript-preparation/", + "https://maia.usno.navy.mil/", + "https://www.usno.navy.mil/USNO/time/gps/usno-gps-time-transfer", + "https://aa.usno.navy.mil/publications/docs/Circular_179.php", + "http://data.astropy.org", + "https://astropy-dei.orgmycology.com/", # 403 Client Error: Forbidden + "https://doi.org/", # CI blocked by service provider + "https://zenodo.org/", # 403 Client Error: Forbidden + "https://ui.adsabs.harvard.edu", # CI blocked by service provider + "https://hst-docs.stsci.edu", # CI blocked by service provider + "https://www.tandfonline.com/", # 403 Client Error: Forbidden + "https://stackoverflow.com/", # 403 Client Error: Forbidden + "https://docutils.sourceforge.io", # 403 Client Error: Forbidden + "https://ieeexplore.ieee.org/", # 418 Client Error: I'm a teapot + "https://pyfits.readthedocs.io/en/v3.2.1/", # defunct page in CHANGES.rst + "https://pkgs.dev.azure.com/astropy-project", # defunct page in CHANGES.rst + r"https://github\.com/astropy/astropy/(?:issues|pull)/\d+", +] +linkcheck_timeout = 180 +linkcheck_anchors = False +linkcheck_report_timeouts_as_broken = True +linkcheck_allow_unauthorized = False + + +def rstjinja(app, docname, source): + """Render pages as a jinja template to hide/show dev docs.""" + # Make sure we're outputting HTML + if app.builder.format != "html": + return + files_to_render = ["index_dev", "install"] + if docname in files_to_render: + logger.info("Jinja rendering %s", docname) + rendered = app.builder.templates.render_string( + source[0], app.config.html_context + ) + source[0] = rendered + + +__minimum_python_version__ = pyproject["project"]["requires-python"].replace(">=", "") + +min_versions = {} +for line in metadata.requires("astropy"): + req = Requirement(line.split(";")[0]) + min_versions[req.name.lower()] = str(req.specifier) + +# The following global_substitutions can be used throughout the +# documentation via sphinxcontrib-globalsubs. The key to the dictionary +# is the name of the case-sensitive substitution. For example, if the +# key is `"SkyCoord"`, then it can be used as `|SkyCoord|` throughout +# the documentation. + +global_substitutions: dict[str, str] = { + # NumPy + "ndarray": ":class:`numpy.ndarray`", + # Coordinates + "EarthLocation": ":class:`~astropy.coordinates.EarthLocation`", + "Angle": "`~astropy.coordinates.Angle`", + "Latitude": "`~astropy.coordinates.Latitude`", + "Longitude": ":class:`~astropy.coordinates.Longitude`", + "BaseFrame": "`~astropy.coordinates.BaseCoordinateFrame`", + "SkyCoord": ":class:`~astropy.coordinates.SkyCoord`", + "SpectralCoord": "`~astropy.coordinates.SpectralCoord`", + # Cosmology + "Cosmology": ":class:`~astropy.cosmology.Cosmology`", + "Cosmology.read": ":meth:`~astropy.cosmology.Cosmology.read`", + "Cosmology.write": ":meth:`~astropy.cosmology.Cosmology.write`", + "Cosmology.from_format": ":meth:`~astropy.cosmology.Cosmology.from_format`", + "Cosmology.to_format": ":meth:`~astropy.cosmology.Cosmology.to_format`", + "FLRW": ":class:`~astropy.cosmology.FLRW`", + "LambdaCDM": ":class:`~astropy.cosmology.LambdaCDM`", + "FlatLambdaCDM": ":class:`~astropy.cosmology.FlatLambdaCDM`", + "WMAP1": ":ref:`astropy_cosmology_realizations_WMAP1`", + "WMAP3": ":ref:`astropy_cosmology_realizations_WMAP3`", + "WMAP5": ":ref:`astropy_cosmology_realizations_WMAP5`", + "WMAP7": ":ref:`astropy_cosmology_realizations_WMAP7`", + "WMAP9": ":ref:`astropy_cosmology_realizations_WMAP9`", + "Planck13": ":ref:`astropy_cosmology_realizations_Planck13`", + "Planck15": ":ref:`astropy_cosmology_realizations_Planck15`", + "Planck18": ":ref:`astropy_cosmology_realizations_Planck18`", + "FlatCosmologyMixin": ":class:`~astropy.cosmology.FlatCosmologyMixin`", + "FlatFLRWMixin": ":class:`~astropy.cosmology.FlatFLRWMixin`", + "default_cosmology": ":class:`~astropy.cosmology.default_cosmology`", + # SAMP + "SAMPClient": ":class:`~astropy.samp.SAMPClient`", + "SAMPIntegratedClient": ":class:`~astropy.samp.SAMPIntegratedClient`", + "SAMPHubServer": ":class:`~astropy.samp.SAMPHubServer`", + "SAMPHubProxy": ":class:`~astropy.samp.SAMPHubProxy`", + # Table + "Column": ":class:`~astropy.table.Column`", + "MaskedColumn": ":class:`~astropy.table.MaskedColumn`", + "TableColumns": ":class:`~astropy.table.TableColumns`", + "Row": ":class:`~astropy.table.Row`", + "Table": ":class:`~astropy.table.Table`", + "QTable": ":class:`~astropy.table.QTable`", + # Time + "Time": ":class:`~astropy.time.Time`", + "TimeDelta": ":class:`~astropy.time.TimeDelta`", + # Timeseries + "TimeSeries": ":class:`~astropy.timeseries.TimeSeries`", + "BinnedTimeSeries": ":class:`~astropy.timeseries.BinnedTimeSeries`", + # Distribution + "Distribution": ":class:`~astropy.uncertainty.Distribution`", + # Units + "PhysicalType": ":class:`~astropy.units.PhysicalType`", + "Quantity": ":class:`~astropy.units.Quantity`", + "Unit": ":class:`~astropy.units.UnitBase`", + "StructuredUnit": ":class:`~astropy.units.StructuredUnit`", + # Utils + "Masked": ":class:`~astropy.utils.masked.Masked`", + # Minimum versions + "minimum_python_version": f"{__minimum_python_version__}", + "minimum_numpy_version": f"{min_versions['numpy']}", + "minimum_pyerfa_version": f"{min_versions['pyerfa']}", + "minimum_matplotlib_version": f"{min_versions['matplotlib']}", + "minimum_scipy_version": f"{min_versions['scipy']}", + "minimum_asdf_astropy_version": f"{min_versions['asdf-astropy']}", + "minimum_packaging_version": f"{min_versions['packaging']}", + "minimum_pyyaml_version": f"{min_versions['pyyaml']}", + "minimum_ipython_version": f"{min_versions['ipython']}", + "minimum_pyarrow_version": f"{min_versions['pyarrow']}", + "minimum_fsspec_version": f"{min_versions['fsspec']}", + "minimum_s3fs_version": f"{min_versions['s3fs']}", +} +# Because sphinxcontrib-globalsubs does not work for regular reStructuredText +# links, we first define the links and then process them into the form +# of a reStructuredText external link. + +links_to_become_substitutions: dict[str, str] = { + # Python + "Python": "https://www.python.org", + "PEP8": "https://www.python.org/dev/peps/pep-0008", + # Astropy + "Astropy mailing list": "https://mail.python.org/mailman/listinfo/astropy", + "astropy-dev mailing list": "https://groups.google.com/group/astropy-dev", + # NumPy + "NumPy": "https://numpy.org", + "numpydoc": "https://pypi.org/project/numpydoc", + # erfa + "ERFA": "https://github.com/liberfa/erfa", + "PyERFA": "https://pyerfa.readthedocs.io", + # matplotlib + "Matplotlib": "https://matplotlib.org", + # sofa + "SOFA": "https://www.iausofa.org", + # scipy + "SciPy": "https://www.scipy.org", + # packaging + "packaging": "https://packaging.pypa.io", + # IPython + "IPython": "https://ipython.org", + # pip + "pip": "https://pip.pypa.io", + # pipenv + "pipenv": "https://pipenv.pypa.io/en/latest", + # virtualenv + "virtualenv": "https://pypi.org/project/virtualenv", + "virtualenvwrapper": "https://pypi.org/project/virtualenvwrapper", + # conda + "conda": "https://conda.io/docs", + "miniconda": "https://docs.conda.io/en/latest/miniconda.html", + # pytest + "pytest": "https://pytest.org/en/latest/index.html", + "pytest-astropy": "https://github.com/astropy/pytest-astropy", + "pytest-doctestplus": "https://github.com/astropy/pytest-doctestplus", + "pytest-remotedata": "https://github.com/astropy/pytest-remotedata", + # fsspec + "fsspec": "https://filesystem-spec.readthedocs.io", + # s3fs + "s3fs": "https://s3fs.readthedocs.io", + # TOPCAT + "STIL": "https://www.star.bristol.ac.uk/mbt/stil", + "STILTS": "https://www.star.bristol.ac.uk/mbt/stilts", + "TOPCAT": "https://www.star.bristol.ac.uk/mbt/topcat", + # OpenAstronomy + "OpenAstronomy Packaging Guide": "https://packaging-guide.openastronomy.org/en/latest", + # Miscellaneous + "HDF5": "https://www.hdfgroup.org/HDF5", + "h5py": "https://www.h5py.org", + "Parquet": "https://parquet.apache.org", +} + +processed_links = { + key: f"`{key} <{value}>`_" for key, value in links_to_become_substitutions.items() +} + +global_substitutions |= processed_links + + +def setup(app): + # Generate the page from Jinja template + app.connect("source-read", rstjinja) diff --git a/docs/config/astropy_config.rst b/docs/config/astropy_config.rst new file mode 100644 index 000000000000..b5b3ade4685d --- /dev/null +++ b/docs/config/astropy_config.rst @@ -0,0 +1,6 @@ +.. _astropy_config_file: + +Astropy's Default Configuration File +************************************ + + .. generate_config:: astropy diff --git a/docs/config/index.rst b/docs/config/index.rst new file mode 100644 index 000000000000..98053c6edca2 --- /dev/null +++ b/docs/config/index.rst @@ -0,0 +1,523 @@ +.. _astropy_config: + +*************************************** +Configuration System (`astropy.config`) +*************************************** + +Introduction +============ + +The ``astropy`` configuration system is designed to give users control of +various parameters used in ``astropy`` or affiliated packages without delving +into the source code to make those changes. + +.. note:: + * Before version 4.3 the configuration file was created by default + when importing ``astropy``. Its existence was required, which is + no longer the case. + +Getting Started +=============== + +The ``astropy`` configuration options are most conveniently set by modifying +the configuration file. Since ``astropy`` 4.3 you need to create this file, +whereas before it was created automatically when importing ``astropy``. The +function :func:`~astropy.config.create_config_file` creates the file with all +of the default values commented out:: + + >>> from astropy.config import create_config_file + >>> create_config_file('astropy') # doctest: +SKIP + +The exact location of this file can be obtained with +:func:`~astropy.config.get_config_dir_path`:: + + >>> from astropy.config import get_config_dir_path + >>> get_config_dir_path() # doctest: +SKIP + +And you should see the location of your configuration directory. The default +configuration directory is ``$XDG_CONFIG_HOME/astropy``, but this can be +customized with :ref:`environment_variables`. + +.. note:: + See :ref:`astropy_config_file` for the content of this configuration file. + +Once you have found the configuration file, open it with your favorite editor. +It should have all of the sections you might want, with descriptions and the +type of value that is accepted. Feel free to edit this as you wish, and +any of these changes will be reflected when you next start ``astropy``. Or call +the :func:`~astropy.config.reload_config` function if you want to see your +changes immediately in your current ``astropy`` session:: + + >>> from astropy.config import reload_config + >>> reload_config() + +.. note:: + If for whatever reason your ``$HOME/.astropy`` directory is not accessible + (i.e., you have ``astropy`` running somehow as root but you are not the root + user), the best solution is to set :ref:`environment_variables` pointing to + directories you control. + +Using `astropy.config` +====================== + +Accessing Values +---------------- + +By convention, configuration parameters live inside of objects called +``conf`` at the root of each sub-package. For example, configuration +parameters related to data files live in ``astropy.utils.data.conf``. +This object has properties for getting and setting individual +configuration parameters. For instance, to get the default URL for +``astropy`` remote data, do:: + + >>> from astropy.utils.data import conf + >>> conf.dataurl + 'http://data.astropy.org/' + +Changing Values at Runtime +-------------------------- + +Changing the persistent state of configuration values is done by editing the +configuration file as described above. Values can also, however, be +modified in an active Python session by setting any of the properties +on a ``conf`` object, or using the +:meth:`~astropy.config.ConfigNamespace.set_temp` `context_manager +`_, as +long as the new value complies with the `Item Types and Validation`_. + +Example +^^^^^^^ + +.. + EXAMPLE START + Changing the Persistent State of Configuration Values at Runtime + +Suppose there is a part of your configuration file that looks like: + +.. code-block:: ini + + [utils.data] + + # URL for astropy remote data site. + dataurl = http://data.astropy.org/ + + # Time to wait for remote data query (in seconds). + remote_timeout = 10.0 + +If you wish to modify the ``remote_timeout`` value, but only for some small +section of your code, then :meth:`~astropy.config.ConfigNamespace.set_temp` +takes care of resetting the value you changed when you are done using it:: + + >>> from astropy.utils.data import conf + >>> conf.remote_timeout + 10.0 + >>> # Change remote_timeout, but only inside the with-statement. + >>> with conf.set_temp('remote_timeout', 4.5): + ... conf.remote_timeout + 4.5 + >>> conf.remote_timeout + 10.0 + +You can also modify the values at runtime directly:: + + >>> conf.dataurl + 'http://data.astropy.org/' + >>> conf.dataurl = 'http://astropydata.mywebsite.com' + >>> conf.dataurl + 'http://astropydata.mywebsite.com' + >>> conf.remote_timeout + 10.0 + >>> conf.remote_timeout = 4.5 + >>> conf.remote_timeout + 4.5 + +.. + EXAMPLE END + +Reloading Configuration +----------------------- + +Instead of modifying the variables in Python, you can also modify the +configuration files and then use the +:meth:`~astropy.config.ConfigNamespace.reload` method. + +Example +^^^^^^^ + +.. + EXAMPLE START + Modifying and Reloading Configuration Files + +If you modify the configuration file to say: + +.. code-block:: ini + + [utils.data] + + # URL for astropy remote data site. + dataurl = http://myotherdata.mywebsite.com/ + + # Time to wait for remote data query (in seconds). + remote_timeout = 6.3 + +And then run the following commands:: + + >>> conf.reload('dataurl') + >>> conf.reload('remote_timeout') + +This should update the variables with the values from the configuration file: + +.. doctest-skip:: + + >>> conf.dataurl + 'http://myotherdata.mywebsite.com/' + >>> conf.remote_timeout + 6.3 + +You can reload all configuration parameters of a ``conf`` object at +once by calling :meth:`~astropy.config.ConfigNamespace.reload` with no +parameters:: + + >>> conf.reload() + +Or if you want to reload all the configuration items at once, not just the ones +in the module ``conf`` belongs to, use the +:func:`~astropy.config.reload_config` function:: + + >>> from astropy import config + >>> config.reload_config() + +You can also reset a configuration parameter back to its default value with the +:meth:`~astropy.config.ConfigNamespace.reset` method. Note that this is the +default value defined in the Python code, which might be different from the +value in the configuration file:: + + >>> conf.reset('dataurl') + >>> conf.dataurl + 'http://data.astropy.org/' + +.. + EXAMPLE END + +Exploring Configuration +----------------------- + +To see what configuration parameters are defined for a given ``conf``:: + + >>> from astropy.utils.iers import conf + >>> list(conf) + ['auto_download', + 'auto_max_age', + ..., + 'ietf_leap_second_auto_url'] + +You can see more detailed information about a configuration parameter by +calling the :meth:`~astropy.config.ConfigNamespace.help` method:: + + >>> conf.help("auto_max_age") + ConfigItem: auto_max_age + cfgtype='float' + defaultvalue=30.0 + description='Maximum age (days) of predictive data before auto-downloading. See "Auto refresh behavior" in astropy.utils.iers documentation for details. Default is 30.' + module=astropy.utils.iers.iers + value=30.0 + +You can see information about all the configuration parameters by calling +:meth:`~astropy.config.ConfigNamespace.help` without arguments:: + + >>> conf.help() + Configuration parameters for `astropy.utils.iers`. + + ConfigItem: auto_download + cfgtype='boolean' + ... + +You can also iterate through ``conf`` in a dictionary-like fashion:: + + >>> for (key, cfgitem) in conf.items(): + ... print(f'{key} default value is {cfgitem.defaultvalue}') + auto_download default value is True + auto_max_age default value is 30.0 + ... + +Upgrading ``astropy`` +--------------------- + +Each time you upgrade to a new major version of ``astropy``, the +configuration parameters may have changed. If you want to create a new +configuration file, you can run:: + + >>> from astropy.config import create_config_file + >>> create_config_file('astropy', overwrite=True) # doctest: +SKIP + +Note that this will overwrite the existing file, so if you modified it you +may want to report your changes in the new file. Another possibility is to +have a look at the :ref:`astropy_config` to see what has changed. + +.. _config-developer: + +Adding New Configuration Items +============================== + +Configuration items should be used wherever an option or setting is +needed that is either tied to a system configuration or should persist +across sessions of ``astropy`` or an affiliated package. Options that may +affect the results of science calculations should not be configuration +items, but should instead be :class:`~astropy.utils.state.ScienceState` +instances, so it is possible to reproduce science results without them being +affected by configuration parameters set in a particular environment. +Admittedly, this is only a guideline, as the precise cases where a +configuration item is preferred over, say, a keyword option for a +function is somewhat personal preference. It is the preferred form of +persistent configuration, however, and ``astropy`` packages must all use +it (and it is recommended for affiliated packages). + +The reference guide below describes the interface for creating a +``conf`` object with a number of configuration parameters. They +should be defined at the top level (i.e., in the ``__init__.py`` of +each sub-package that has configuration items):: + + """ This is the docstring at the beginning of a module + """ + from astropy import config as _config + + class Conf(_config.ConfigNamespace): + """ + Configuration parameters for my subpackage. + """ + some_setting = _config.ConfigItem( + 1, 'Description of some_setting') + another_setting = _config.ConfigItem( + 'string value', 'Description of another_setting') + some_list = _config.ConfigItem( + [], 'Description of some_list', cfgtype='list') + another_list = _config.ConfigItem( + ['value'], 'Description of another_setting', cfgtype='list') + + # Create an instance for the user + conf = Conf() + + # implementation ... + def some_func(): + # to get the value of some of these options, I might do: + something = conf.some_setting + 2 + return conf.another_setting + ' Also, I added text.' + +For an affiliated package called, for example, ``packagename``, the +configuration file can be generated with the +:func:`~astropy.config.create_config_file` function like this:: + + >>> from astropy.config import create_config_file + >>> create_config_file('packagename') # doctest: +SKIP + +The following content would be written to the config file template: + +.. code-block:: ini + + [subpackage] + ## Description of some_setting + # some_setting = 1 + + ## Description of another_setting + # another_setting = foo + + ## Description of some_list + # some_list = , + + ## Description of another_setting + # another_list = value, + +Note that the key/value pairs are commented out. This will allow for +changing the default values in a future version of the package without +requiring the user to edit their configuration file to take advantage +of the new defaults. By convention, the descriptions of each +parameter are in comment lines starting with two hash characters +(``##``) to distinguish them from commented out key/value pairs. + +Item Types and Validation +------------------------- + +If not otherwise specified, a :class:`~astropy.config.ConfigItem` gets its type +from the type of the ``defaultvalue`` it is given when it is created. The item +can only ever have a value of this type, although in some cases a provided +value can be automatically converted. For example + +:: + + >>> conf.auto_download + True + >>> conf.auto_download = 0 + >>> conf.auto_download + False + +succeeds because the :class:`int` ``0`` can be safely converted to the +:class:`bool` `False`. On the other hand + +:: + + >>> from astropy.utils.data import conf + >>> conf.compute_hash_block_size + 65536 + >>> conf.compute_hash_block_size = 65536.0 + Traceback (most recent call last): + ... + TypeError: Provided value for configuration item compute_hash_block_size + not valid: the value "65536.0" is of the wrong type. + +fails because converting a :class:`float` to an :class:`int` can lose +information. + +Note that if you want the configuration item to be limited to a particular set +of options, you should pass in a :class:`list` as the ``defaultvalue`` option. +The first entry in the :class:`list` will be taken as the default, and the +:class:`list` as a whole gives all of the valid options. For example:: + + an_option = ConfigItem(['a', 'b', 'c'], + "This option can be 'a', 'b', or 'c'") + conf.an_option = 'b' # succeeds + conf.an_option = 'c' # succeeds + conf.an_option = 'd' # fails! + conf.an_option = 6 # fails! + +Finally, a :class:`~astropy.config.ConfigItem` can be explicitly given a type +via the ``cfgtype`` option:: + + an_int_setting = ConfigItem( + 1, 'A description.', cfgtype='integer') + ... + conf.an_int_setting = 3 # works fine + conf.an_int_setting = 4.2 # fails! + +If the default value's type does not match ``cfgtype``, the +:class:`~astropy.config.ConfigItem` cannot be created. + +In summary, the default behavior (of automatically determining ``cfgtype``) +is usually what you want. The main exception is when you want your +configuration item to be a :class:`list`. The default behavior will treat that +as a *list of options* unless you explicitly tell it that the +:class:`~astropy.config.ConfigItem` itself is supposed to be a :class:`list`:: + + # The setting must be 1, 2 or 3 + a_list_setting = ConfigItem([1, 2, 3], 'A description.') + + # The setting must be a list and is [1, 2, 3] by default + a_list_setting = ConfigItem([1, 2, 3], 'A description.', cfgtype='list') + +Details of all of the valid ``cfgtype`` items can be found in the +`validation section of the configobj manual +`_. +Below is a list of the valid values here for quick reference: + +* ``'integer'`` +* ``'float'`` +* ``'boolean'`` +* ``'string'`` +* ``'ip_addr'`` +* ``'list'`` +* ``'tuple'`` +* ``'int_list'`` +* ``'float_list'`` +* ``'bool_list'`` +* ``'string_list'`` +* ``'ip_addr_list'`` +* ``'mixed_list'`` +* ``'option'`` +* ``'pass'`` + +Usage Tips +---------- + +Keep in mind that :class:`~astropy.config.ConfigItem` objects can be +changed at runtime by users. So it is always recommended to read their +values immediately before use instead of storing their initial +value to some other variable (or used as a default for a +function). For example, the following is incorrect usage:: + + >>> from astropy.utils.data import conf + >>> conf.remote_timeout = 1.0 + >>> def some_func(val=conf.remote_timeout): + ... return val + 2 + +This works only as long as the user does not change the value of the +configuration item after the function has been defined, but if they do, the +function will not know about the change:: + + >>> some_func() + 3.0 + >>> conf.remote_timeout = 3.0 + >>> some_func() # naively should return 5.0, because 3 + 2 = 5 + 3.0 + +There are two ways around this. The typical/intended way is:: + + >>> def some_func(): + ... """ + ... The `SOME_SETTING` configuration item influences this output + ... """ + ... return conf.remote_timeout + 2 + >>> some_func() + 5.0 + >>> conf.remote_timeout = 5.0 + >>> some_func() + 7.0 + +Or, if the option needs to be available as a function parameter:: + + def some_func(val=None): + """ + If not specified, `val` is set by the `SOME_SETTING` configuration item. + """ + return (conf.remote_timeout if val is None else val) + 2 + + +Customizing Config Location in Affiliated Packages +================================================== + +The `astropy.config` package can be used by other packages. By default creating +a config object in another package will lead to a configuration file taking the +name of that package in the ``astropy`` config directory (i.e., +``/packagename.cfg``). + +It is possible to configure this behavior so that the a custom configuration +directory is created for your package, for example, +``~/.packagename/packagename.cfg``. To do this, create a ``packagename.config`` +subpackage and put the following into the ``__init__.py`` file:: + + import astropy.config as astropyconfig + + + class ConfigNamespace(astropyconfig.ConfigNamespace): + rootname = 'packagename' + + + class ConfigItem(astropyconfig.ConfigItem): + rootname = 'packagename' + +Then replace all imports of `astropy.config` with ``packagename.config``. + + +See Also +======== + +.. toctree:: + :maxdepth: 2 + + astropy_config + +:doc:`/logging` (overview of `astropy.logger`) + + +Reference/API +============= + +.. toctree:: + :maxdepth: 2 + + ref_api + +.. testcleanup:: + + >>> from astropy import config + >>> config.reload_config() + >>> from astropy.utils.iers import conf + >>> conf.auto_download = False diff --git a/docs/config/ref_api.rst b/docs/config/ref_api.rst new file mode 100644 index 000000000000..9e96cb1c9d02 --- /dev/null +++ b/docs/config/ref_api.rst @@ -0,0 +1,4 @@ +Reference/API +************* + +.. automodapi:: astropy.config diff --git a/docs/configs/index.rst b/docs/configs/index.rst deleted file mode 100644 index cf51271b1c60..000000000000 --- a/docs/configs/index.rst +++ /dev/null @@ -1,250 +0,0 @@ -.. _astropy_config: - -*************************************** -Configuration system (`astropy.config`) -*************************************** - -Introduction -============ - -The astropy configuration system is designed to give users control of various -parameters used in astropy or affiliated packages without delving into the -source code to make those changes. - - -Getting Started -=============== - -To see the configuration options, look for your astropy configuration file. -You can find it by doing:: - - from astropy.config import get_config_dir - - print get_config_dir() - -And you should see the location of your configuration directory. The standard -scheme generally puts your configuration directory in ``$HOME/.astropy/config``, -but if you've set the environment variable `XDG_CONFIG_HOME` and the -``$XDG_CONFIG_HOME/astropy`` directory exists, it will instead be there. - -Once you've found the configuration file, open it with your favorite editor. -It should have all of the sections you might want, with descriptions and the -type of the value that is accepted. Feel free to edit this as you wish, and -any of these changes will be reflected when you next start Astropy. Or, if you -want to see your changes immediately in your current Astropy session, just do:: - - from astropy.config import reload_config - - reload_config() - -.. warning:: - - The above is not true yet, because the setup doesn't automatically - populate the configuration files. Hopefully it will be true soon, though. - The :func:`~astropy.config.configuration._generate_all_config_items` - function will already do this, basically, but there has to be some - thought about how to make driver scripts that actually do this for - each user, and coordinate when they get run so that everything is - already built. - -.. note:: - If for whatever reason your ``$HOME/.astropy`` directory is not accessible - (i.e., you have astropy running somehow as root but you are not the root - user), the best solution is to set the `XDG_CONFIG_HOME` and - `XDG_CACHE_HOME` environment variables pointing to directories, and create - an ``astropy`` directory inside each of those. Both the configuration and - data download systems will then use those directories and never try to - access the ``$HOME/.astropy`` directory. - - -Using `config` -============== - -Changing Values at Run-time ---------------------------- - -The configuration system is most conveniently used by modifying -configuration files as described above. Values can also, however, be -modified in an active python session using the -:meth:`~astropy.config.configuration.ConfigurationItem.set` method. A run-time -`ConfigurationItem` object can be used to make these changes. These items -are found in the same module as the configuration section they are in, -and usually have the same name as in the configuration files, but in all -caps. Alternatively, they may be located with the -:func:`~astropy.config.configuration.get_config_items` function. - -For example, if there is a part of your configuration file that looks like:: - - [config.data] - # URL for astropy remote data site. - dataurl = http://data.astropy.org/ - - # Time to wait for remote data query (in seconds). - remote_timeout = 3.0 - - -You should be able to modify the values at run-time this way:: - - >>> from astropy.config.data import DATAURL, REMOTE_TIMEOUT - >>> DATAURL() - 'http://data.astropy.org/' - >>> DATAURL.set('http://astropydata.mywebsite.com') - >>> DATAURL() - 'http://astropydata.mywebsite.com' - >>> REMOTE_TIMEOUT() - 3.0 - >>> REMOTE_TIMEOUT.set(4.5) - >>> REMOTE_TIMEOUT() - 4.5 - -Or alternatively:: - - from astropy.config import get_config_items - - >>> items = get_config_items('astropy.config.data') - >>> items['DATAURL'].set('http://astropydata.mywebsite.com') - >>> items['REMOTE_TIMEOUT'].set('4.5') - -Note that this will *not* permanently change these values in the configuration -files - just for the current session. To change the configuration files, -after you've made your changes, you can do:: - - >>> DATAURL.save() - >>> REMOTE_TIMEOUT.save() - -Or to save all modifications to configuration items in `astropy.config.data` -(which includes the changes made above), do:: - - >>> from astropy.config import save_config - >>> save_config('astropy.config.data') - -Reloading Configuration ------------------------ - -Instead of modifying the variables in python, you can also modify the -configuration files and then reload them. For example, if you modify the -configuration file to say:: - - [config.data] - # URL for astropy remote data site. - dataurl = http://myotherdata.mywebsite.com/ - - # Time to wait for remote data query (in seconds). - remote_timeout = 6.3 - -And then run the following commands:: - - >>> DATAURL.reload() - >>> REMOTE_TIMEOUT.reload() - -This should update the variables with the values from the configuration file:: - - >>> DATAURL() - 'http://myotherdata.mywebsite.com/' - >>> REMOTE_TIMEOUT() - 6.3 - -Or if you want to reload all astropy configuration at once, use the -`~astropy.config.configuration.reload_config` function:: - - >>> config.reload_config('astropy') - - -Developer Usage ---------------- - -Configuration items should be used wherever an option or setting is -needed that is either tied to a system configuration or should persist -across sessions of astropy or an affiliated package. Admittedly, this is -only a guideline, as the precise cases where a configuration item is -preferred over, say, a keyword option for a function is somewhat personal -preference. It is the preferred form of persistent configuration, -however, and astropy packages must all use it (and it is recommended for -affiliated packages). - -The Reference guide below describes the full interface for a -`ConfigurationItem` - this is a guide for *typical* developer usage. In -almost all cases, a configuration item should be defined and used in the -following manner:: - - """ This is the docstring at the beginning of a module - """ - from astropy.config import ConfigurationItem - - SOME_OPTION = ConfigurationItem('some_option', 1, 'A description.') - ANOTHER_OPTION = ConfigurationItem('annother_opt', 'a string val', - 'A longer description of what this does.') - - ... implementation ... - def some_func(): - #to get the value of these options, I might do: - something = SOME_OPTION() + 2 - return ANOTHER_OPTION() + ' Also, I added text.' - -It is highly recommended that any configuration items be placed at the -top of a module like this, as they can then be easily found when viewing -the source code and the automated tools to generate the default -configuration files can also locate these items. - -There are a couple important gotchas to remember about using configuration -items in your code. First, it is tempting to do something like:: - - SOME_OPTION = ConfigurationItem('some_option',1,'A description.') - - def some_func(): - return SOME_OPTION + 2 # WRONG, you wanted SOME_OPTION() + 2 - -but this is incorrect, because ``SOME_OPTION`` instead of -``SOME_OPTION()`` will yield a `ConfigurationItem` object, instead of the -*value* of that item (an integer, in this case). - -The second point to keep in mind is that `ConfigurationItem` objects can -be changed at runtime by users. So you always read their values instead -of just storing their initial value to some other variable (or used as a -default for a function). For example, the following will work, but is -incorrect usage:: - - SOME_OPTION = ConfigurationItem('some_option',1,'A description.') - - def some_func(val=SOME_OPTION()): - return val + 2 - -This works fine as long as the user doesn't change its value during -runtime, but if they do, the function won't know about the change:: - - >>> some_func() - 3 - >>> SOME_OPTION.set(3) - >>> some_func() # naively should return 5, because 3 + 2 = 5 - 3 - -There are two ways around this. The typical/intended way is:: - - def some_func(): - """ - The `SOME_OPTION` configuration item influences this output - """ - return SOME_OPTION() + 2 - -Or, if the option needs to be available as a function parameter:: - - def some_func(val=None): - """ - If not specified, `val` is set by the `SOME_OPTION` configuration item. - """ - return (SOME_OPTION() if val is None else val) + 2 - - - - -See Also -======== - -:doc:`/logging` (overview of `astropy.config.logging`) - - -Reference/API -============= - -.. automodapi:: astropy.config - :no-inheritance-diagram: diff --git a/docs/conftest.py b/docs/conftest.py new file mode 100644 index 000000000000..dd98ea2dbcf8 --- /dev/null +++ b/docs/conftest.py @@ -0,0 +1,49 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +# This file needs to be included here to make sure commands such +# as ``pytest docs/...`` works, since this +# will ignore the conftest.py file at the root of the repository +# and the one in astropy/conftest.py + +import os +from pathlib import Path + +import pytest + +# Make sure we use temporary directories for the config and cache +# so that the tests are insensitive to local configuration. Note that this +# is also set in the test runner, but we need to also set it here for +# things to work properly in parallel mode +# note: session-level + autouse doesn't require a cleanup phase + + +@pytest.fixture(scope="session", autouse=True) +def _session_level_cache_dir(tmp_path_factory): + os.environ["ASTROPY_CACHE_DIR"] = str(tmp_path_factory.mktemp("astropy_cache_")) + os.environ["XDG_CACHE_HOME"] = str(tmp_path_factory.mktemp("xdg_cache_")) + + +@pytest.fixture(scope="session", autouse=True) +def _session_level_config_dir(tmp_path_factory): + os.environ["ASTROPY_CONFIG_DIR"] = str(tmp_path_factory.mktemp("astropy_config_")) + os.environ["XDG_CONFIG_HOME"] = str(tmp_path_factory.mktemp("xdg_config_")) + + +@pytest.fixture(autouse=True) +def _docdir(request): + """Run doctests in isolated tmp_path so outputs do not end up in repo.""" + # Trigger ONLY for doctestplus + doctest_plugin = request.config.pluginmanager.getplugin("doctestplus") + if isinstance(request.node.parent, doctest_plugin._doctest_textfile_item_cls): + # Don't apply this fixture to io.rst. It reads files and doesn't write. + # Implementation from https://github.com/pytest-dev/pytest/discussions/10437 + if "io.rst" not in request.node.name: + old_cwd = Path.cwd() + tmp_path = request.getfixturevalue("tmp_path") + os.chdir(tmp_path) + yield + os.chdir(old_cwd) + else: + yield + else: + yield diff --git a/docs/constants/index.rst b/docs/constants/index.rst new file mode 100644 index 000000000000..634babd6b067 --- /dev/null +++ b/docs/constants/index.rst @@ -0,0 +1,201 @@ +.. _astropy-constants: + +******************************* +Constants (`astropy.constants`) +******************************* + +.. currentmodule:: astropy.constants + +Introduction +============ + +`astropy.constants` contains a number of physical constants useful in +Astronomy. A `~astropy.constants.Constant` is a |Quantity| object with +additional metadata describing its provenance and uncertainty. + +Getting Started +=============== + +You can import a :class:`~astropy.constants.Constant` directly from the +:mod:`astropy.constants` sub-package:: + + >>> from astropy.constants import G + >>> print(G) + Name = Gravitational constant + Value = 6.6743e-11 + Uncertainty = 1.5e-15 + Unit = m3 / (kg s2) + Reference = CODATA 2022 + +Or, if you want to avoid having to explicitly import all of the constants you +need, you can do:: + + >>> from astropy import constants as const + >>> print(const.G) + Name = Gravitational constant + ... + +Constants can be used in :ref:`quantity_arithmetic` operations and +:ref:`quantity_and_numpy` just like any other |Quantity|:: + + >>> from astropy import units as u + >>> F = (const.G * 3. * const.M_sun * 100 * u.kg) / (2.2 * u.au) ** 2 + >>> print(F.to(u.N)) # doctest: +FLOAT_CMP + 0.3675671602160826 N + +Unit Conversion +=============== + +.. + EXAMPLE START + Converting Constants to Different Units + +Explicitly :ref:`quantity_unit_conversion` is often not necessary, but can be +done if needed:: + + >>> print(const.c) + Name = Speed of light in vacuum + Value = 299792458.0 + Uncertainty = 0.0 + Unit = m / s + Reference = CODATA 2022 + + >>> print(const.c.to('km/s')) + 299792.458 km / s + + >>> print(const.c.to('pc/yr')) # doctest: +FLOAT_CMP + 0.306601393788 pc / yr + +It is possible to convert most constants to `Centimeter-Gram-Second (CGS) +`_ units +using, for example:: + + >>> const.c.cgs # doctest: +FLOAT_CMP + + +However, some constants are defined with `different physical dimensions in CGS +`_ +and cannot be directly converted. Because of this ambiguity, such constants +cannot be used in expressions without specifying a system:: + + >>> 100 * const.e + Traceback (most recent call last): + ... + TypeError: Constant u'e' does not have physically compatible units + across all systems of units and cannot be combined with other + values without specifying a system (eg. e.emu) + >>> 100 * const.e.esu # doctest: +FLOAT_CMP + + +.. + EXAMPLE END + +.. _astropy-constants-prior: + +Collections of Constants (and Prior Versions) +============================================= + +Constants are organized into version modules. The constants for +``astropy`` 2.0 can be accessed in the ``astropyconst20`` module. +For example:: + + >>> from astropy.constants import astropyconst20 as const + >>> print(const.e) + Name = Electron charge + Value = 1.6021766208e-19 + Uncertainty = 9.8e-28 + Unit = C + Reference = CODATA 2014 + +The version modules contain physical and astronomical constants, and both sets +can also be chosen independently from each other. Physical `CODATA constants +`_ are in modules with names +like ``codata2010``, ``codata2014``, or ``codata2018``:: + + >>> from astropy.constants import codata2014 as const + >>> print(const.h) + Name = Planck constant + Value = 6.62607004e-34 + Uncertainty = 8.1e-42 + Unit = J s + Reference = CODATA 2014 + +Astronomical constants defined (primarily) by the International Astronomical +Union (IAU) are collected in modules with names like ``iau2012`` or ``iau2015``:: + + >>> from astropy.constants import iau2012 as const + >>> print(const.L_sun) + Name = Solar luminosity + Value = 3.846e+26 + Uncertainty = 5e+22 + Unit = W + Reference = Allen's Astrophysical Quantities 4th Ed. + + >>> from astropy.constants import iau2015 as const + >>> print(const.L_sun) + Name = Nominal solar luminosity + Value = 3.828e+26 + Uncertainty = 0.0 + Unit = W + Reference = IAU 2015 Resolution B 3 + +However, importing these prior version modules directly will lead to +inconsistencies with other subpackages that have already imported +`astropy.constants`. Notably, `astropy.units` will have already used +the default version of constants. When using prior versions of the constants +in this manner, quantities should be constructed with constants instead of units. + +To ensure consistent use of a prior version of constants in other ``astropy`` +packages (such as :mod:`astropy.units`) that import :mod:`astropy.constants`, +the physical and astronomical constants versions should be set via +:class:`~astropy.utils.state.ScienceState` classes. These must be set before +the first import of either :mod:`astropy.constants` or :mod:`astropy.units`. +For example, you can use the CODATA2010 physical constants together with the +IAU 2012 astronomical constants:: + + >>> from astropy import physical_constants, astronomical_constants + >>> physical_constants.set('codata2010') # doctest: +SKIP + + >>> physical_constants.get() # doctest: +SKIP + 'codata2010' + >>> astronomical_constants.set('iau2012') # doctest: +SKIP + + >>> astronomical_constants.get() # doctest: +SKIP + 'iau2012' + +Then all other packages that import `astropy.constants` will self-consistently +initialize with these prior versions of constants. + +The versions may also be set using values referring to the version modules:: + + >>> from astropy import physical_constants, astronomical_constants + >>> physical_constants.set('astropyconst13') # doctest: +SKIP + + >>> physical_constants.get() # doctest: +SKIP + 'codata2010' + >>> astronomical_constants.set('astropyconst13') # doctest: +SKIP + + >>> astronomical_constants.get() # doctest: +SKIP + 'iau2012' + +.. The doctest should not be skipped, ideally. See https://github.com/astropy/astropy/issues/8781 + +If :mod:`astropy.constants` or :mod:`astropy.units` have already been imported, +a :class:`RuntimeError` will be raised:: + + >>> import astropy.units + >>> from astropy import physical_constants, astronomical_constants + >>> astronomical_constants.set('astropyconst13') + Traceback (most recent call last): + ... + RuntimeError: astropy.units is already imported + +.. note that if this section gets too long, it should be moved to a separate + doc page - see the top of performance.inc.rst for the instructions on how to + do that +.. include:: performance.inc.rst + +Reference/API +============= + +.. automodapi:: astropy.constants diff --git a/docs/constants/performance.inc.rst b/docs/constants/performance.inc.rst new file mode 100644 index 000000000000..19aba1aec394 --- /dev/null +++ b/docs/constants/performance.inc.rst @@ -0,0 +1,12 @@ +.. note that if this is changed from the default approach of using an *include* + (in index.rst) to a separate performance page, the header needs to be changed + from === to ***, the filename extension needs to be changed from .inc.rst to + .rst, and a link needs to be added in the sub-package toctree + +.. _astropy-constants-performance: + +.. Performance Tips +.. ================ +.. +.. Here we provide some tips and tricks for how to optimize performance of code +.. using `astropy.constants`. diff --git a/docs/nddata/images/astropy.png b/docs/convolution/images/astropy.png similarity index 100% rename from docs/nddata/images/astropy.png rename to docs/convolution/images/astropy.png diff --git a/docs/nddata/images/original.png b/docs/convolution/images/original.png similarity index 100% rename from docs/nddata/images/original.png rename to docs/convolution/images/original.png diff --git a/docs/nddata/images/scipy.png b/docs/convolution/images/scipy.png similarity index 100% rename from docs/nddata/images/scipy.png rename to docs/convolution/images/scipy.png diff --git a/docs/convolution/index.rst b/docs/convolution/index.rst new file mode 100644 index 000000000000..3604cfdc7b7a --- /dev/null +++ b/docs/convolution/index.rst @@ -0,0 +1,435 @@ +.. _astropy_convolve: + +************************************************* +Convolution and Filtering (`astropy.convolution`) +************************************************* + +Introduction +============ + +`astropy.convolution` provides convolution functions and kernels that offer +improvements compared to the SciPy `scipy.ndimage` convolution routines, +including: + +* Proper treatment of NaN values (ignoring them during convolution and + replacing NaN pixels with interpolated values) + +* Both direct and Fast Fourier Transform (FFT) versions + +* Built-in kernels that are commonly used in Astronomy + +The following thumbnails show the difference between SciPy and +Astropy's convolve functions on an astronomical image that contains NaN +values. Scipy's function returns NaN for all pixels that are within a +kernel-sized region of any NaN value, which is often not the desired +result. + +.. plot:: + :show-source-link: + + import matplotlib.pyplot as plt + import numpy as np + from astropy.convolution import Gaussian2DKernel, convolve + from astropy.io import fits + from astropy.utils.data import get_pkg_data_filename + from scipy.ndimage import convolve as scipy_convolve + + # Load the data from data.astropy.org + filename = get_pkg_data_filename('galactic_center/gc_msx_e.fits') + hdu = fits.open(filename)[0] + + # Scale the file to have reasonable numbers + # (this is mostly so that colorbars do not have too many digits) + # Also, we crop it so you can see individual pixels + img = hdu.data[50:90, 60:100] * 1e5 + + # This example is intended to demonstrate how astropy.convolve and + # scipy.convolve handle missing data, so we start by setting the + # brightest pixels to NaN to simulate a "saturated" data set + img[img > 20] = np.nan + + # We also create a copy of the data and set those NaNs to zero. We will + # use this for the scipy convolution + img_zerod = img.copy() + img_zerod[np.isnan(img)] = 0 + + # We smooth with a Gaussian kernel with x_stddev=1 (and y_stddev=1) + # It is a 9x9 array + kernel = Gaussian2DKernel(x_stddev=1) + + # Convolution: scipy's direct convolution mode spreads out NaNs (see + # panel 2 below) + scipy_conv = scipy_convolve(img, kernel) + + # scipy's direct convolution mode run on the 'zero'd' image will not + # have NaNs, but will have some very low value zones where the NaNs were + # (see panel 3 below) + scipy_conv_zerod = scipy_convolve(img_zerod, kernel) + + # astropy's convolution replaces the NaN pixels with a kernel-weighted + # interpolation from their neighbors + astropy_conv = convolve(img, kernel) + + # Now we do a bunch of plots. In the first two plots, the originally masked + # values are marked with red X's + fig, ax = plt.subplots(nrows=2, ncols=2, figsize=(8, 8), layout="tight") + fig.subplots_adjust(left=0.05, bottom=0.05, right=0.95, top=0.95, + wspace=0.3, hspace=0.3) + ax = ax.flatten() + for axis in ax: + axis.axis('off') + + im = ax[0].imshow(img, vmin=-2.0, vmax=20.0, origin='lower', + interpolation='nearest', cmap='viridis') + y, x = np.where(np.isnan(img)) + ax[0].plot(x, y, 'rx', markersize=4) + ax[0].set_title("Input Data") + ax[0].set_xticklabels([]) + ax[0].set_yticklabels([]) + + im = ax[1].imshow(scipy_conv, vmin=-2.0, vmax=20.0, origin='lower', + interpolation='nearest', cmap='viridis') + ax[1].plot(x, y, 'rx', markersize=4) + ax[1].set_title("Scipy convolved") + ax[1].set_xticklabels([]) + ax[1].set_yticklabels([]) + + im = ax[2].imshow(scipy_conv_zerod, vmin=-2.0, vmax=20.0, origin='lower', + interpolation='nearest', cmap='viridis') + ax[2].set_title("Scipy convolved (NaN to zero)") + ax[2].set_xticklabels([]) + ax[2].set_yticklabels([]) + + im = ax[3].imshow(astropy_conv, vmin=-2.0, vmax=20.0, origin='lower', + interpolation='nearest', cmap='viridis') + ax[3].set_title("Astropy convolved") + ax[3].set_xticklabels([]) + ax[3].set_yticklabels([]) + + # we make a second plot of the amplitudes vs offset position to more + # clearly illustrate the value differences + fig2, ax2 = plt.subplots(figsize=(8, 6)) + ax2.plot(img[:, 25], label='Input data', drawstyle='steps-mid', linewidth=2, + alpha=0.5) + ax2.plot(scipy_conv[:, 25], label='SciPy convolved', drawstyle='steps-mid', + linewidth=2, alpha=0.5, marker='s') + ax2.plot(scipy_conv_zerod[:, 25], label='SciPy convolved (NaN to zero)', + drawstyle='steps-mid', linewidth=2, alpha=0.5, marker='s') + ax2.plot(astropy_conv[:, 25], label='Astropy convolved', drawstyle='steps-mid', + linewidth=2, alpha=0.5) + ax2.set(xlabel="Pixel", ylabel="Amplitude") + ax2.legend(loc='best') + plt.show() + + +Getting Started +=============== + +Two convolution functions are provided. They are imported as:: + + from astropy.convolution import convolve, convolve_fft + +and are both used as:: + + result = convolve(image, kernel) + result = convolve_fft(image, kernel) + +:func:`~astropy.convolution.convolve` is implemented as a direct convolution +algorithm, while :func:`~astropy.convolution.convolve_fft` uses a Fast Fourier +Transform (FFT). Thus, the former is better for small kernels, while the latter +is much more efficient for larger kernels. + +Example +------- + +.. + EXAMPLE START + Convolution for User-Specified Kernels + +To convolve a 1D dataset with a user-specified kernel, you can do:: + + >>> from astropy.convolution import convolve + >>> convolve([1, 4, 5, 6, 5, 7, 8], [0.2, 0.6, 0.2]) # doctest: +FLOAT_CMP + array([1.4, 3.6, 5. , 5.6, 5.6, 6.8, 6.2]) + +The ``boundary`` keyword determines how the input array is extended +beyond its boundaries. The default value is ``'fill'``, meaning values +outside of the array boundary are set to the input ``fill_value`` +(default is 0.0). Setting ``boundary='extend'`` causes values near the +edges to be extended using a constant extrapolation beyond the boundary. +The values at the end are computed assuming that any value below the +first point is ``1``, and any value above the last point is ``8``:: + + >>> from astropy.convolution import convolve + >>> convolve([1, 4, 5, 6, 5, 7, 8], [0.2, 0.6, 0.2], boundary='extend') # doctest: +FLOAT_CMP + array([1.6, 3.6, 5. , 5.6, 5.6, 6.8, 7.8]) + +For a more detailed discussion of boundary treatment, see :doc:`using`. + +.. + EXAMPLE END + +Example +------- + +.. + EXAMPLE START + Convolution for Built-In Kernels + +The convolution module also includes built-in kernels that can be imported as, +for example:: + + >>> from astropy.convolution import Gaussian1DKernel + +To use a kernel, first create a specific instance of the kernel:: + + >>> gauss = Gaussian1DKernel(stddev=2) + +``gauss`` is not an array, but a kernel object. The underlying array can be +retrieved with:: + + >>> gauss.array # doctest: +FLOAT_CMP + array([6.69162896e-05, 4.36349021e-04, 2.21596317e-03, 8.76430436e-03, + 2.69959580e-02, 6.47599366e-02, 1.20987490e-01, 1.76035759e-01, + 1.99474648e-01, 1.76035759e-01, 1.20987490e-01, 6.47599366e-02, + 2.69959580e-02, 8.76430436e-03, 2.21596317e-03, 4.36349021e-04, + 6.69162896e-05]) + +The kernel can then be used directly when calling +:func:`~astropy.convolution.convolve`: + +.. plot:: + :show-source-link: + + import numpy as np + import matplotlib.pyplot as plt + + from astropy.convolution import Gaussian1DKernel, convolve + + fig, ax = plt.subplots() + + # Generate fake data + rng = np.random.default_rng(963) + x = np.arange(1000).astype(float) + y = np.sin(x / 100.) + rng.normal(0., 1., x.shape) + y[::3] = np.nan + + # Create kernel + g = Gaussian1DKernel(stddev=50) + + # Convolve data + z = convolve(y, g) + + # Plot data before and after convolution + ax.plot(x, y, label='Data') + ax.plot(x, z, label='Convolved Data', linewidth=2) + ax.legend(loc='best') + plt.show() + +.. + EXAMPLE END + +Using ``astropy``'s Convolution to Replace Bad Data +--------------------------------------------------- + +``astropy``'s convolution methods can be used to replace bad data with values +interpolated from their neighbors. Kernel-based interpolation is useful for +handling images with a few bad pixels or for interpolating sparsely sampled +images. + +The interpolation tool is implemented and used as:: + + from astropy.convolution import interpolate_replace_nans + result = interpolate_replace_nans(image, kernel) + +Some contexts in which you might want to use kernel-based interpolation include: + + * Images with saturated pixels. Generally, these are the highest-intensity + regions in the imaged area, and the interpolated values are not reliable, + but this can be useful for display purposes. + * Images with flagged pixels (e.g., a few small regions affected by cosmic + rays or other spurious signals that require those pixels to be flagged out). + If the affected region is small enough, the resulting interpolation will have + a small effect on source statistics and may allow for robust source-finding + algorithms to be run on the resulting data. + * Sparsely sampled images such as those constructed with single-pixel + detectors. Such images will only have a few discrete points sampled across + the imaged area, but an approximation of the extended sky emission can still + be constructed. + +.. note:: + Care must be taken to ensure that the kernel is large enough to completely + cover potential contiguous regions of NaN values. + An ``AstropyUserWarning`` is raised if NaN values are detected + post-convolution, in which case the kernel size should be increased. + +Example +^^^^^^^ + +.. + EXAMPLE START + Kernel Interpolation to Fill in Flagged-Out Pixels + +The script below shows an example of kernel interpolation to fill in +flagged-out pixels: + +.. plot:: + :context: + :show-source-link: + + import numpy as np + import matplotlib.pyplot as plt + + from astropy.io import fits + from astropy.utils.data import get_pkg_data_filename + from astropy.convolution import Gaussian2DKernel, interpolate_replace_nans + + # Load the data from data.astropy.org + filename = get_pkg_data_filename('galactic_center/gc_msx_e.fits') + + hdu = fits.open(filename)[0] + img = hdu.data[50:90, 60:100] * 1e5 + + # This example is intended to demonstrate how astropy.convolve and + # scipy.convolve handle missing data, so we start by setting the brightest + # pixels to NaN to simulate a "saturated" data set + img[img > 2e1] = np.nan + + # We smooth with a Gaussian kernel with x_stddev=1 (and y_stddev=1) + # It is a 9x9 array + kernel = Gaussian2DKernel(x_stddev=1) + + # create a "fixed" image with NaNs replaced by interpolated values + fixed_image = interpolate_replace_nans(img, kernel) + + # Now we do a bunch of plots. In the first two plots, the originally masked + # values are marked with red X's + fig, axs = plt.subplots(ncols=2, figsize=(12, 6)) + + ax1 = axs[0] + im = ax1.imshow(img, vmin=-2., vmax=2.e1, origin='lower', + interpolation='nearest', cmap='viridis') + y, x = np.where(np.isnan(img)) + ax1.plot(x, y, 'rx', markersize=4) + ax1.set( + autoscale_on=False, + title="Original", + xticklabels=[], + yticklabels=[], + ) + + ax2 = axs[1] + im = ax2.imshow(fixed_image, vmin=-2., vmax=2.e1, origin='lower', + interpolation='nearest', cmap='viridis') + ax2.set( + title="Fixed", + xticklabels=[], + yticklabels=[], + ) + +.. + EXAMPLE END + +Example +^^^^^^^ + +.. + EXAMPLE START + Kernel Interpolation to Reconstruct Images from Sparse Sampling. + +This script shows the power of this technique for reconstructing images from +sparse sampling. Note that the image is not perfect: the pointlike sources +are sometimes missed, but the extended structure is very well recovered by +eye. + +.. plot:: + :context: + :show-source-link: + + import numpy as np + import matplotlib.pyplot as plt + + from astropy.io import fits + from astropy.utils.data import get_pkg_data_filename + from astropy.convolution import Gaussian2DKernel, interpolate_replace_nans + + # Load the data from data.astropy.org + filename = get_pkg_data_filename('galactic_center/gc_msx_e.fits') + + hdu = fits.open(filename)[0] + img = hdu.data[50:90, 60:100] * 1e5 + + rng = np.random.default_rng(1379) + indices = rng.integers(low=0, high=img.size, size=300) + + sampled_data = img.flat[indices] + + # Build a new, sparsely sampled version of the original image + new_img = np.tile(np.nan, img.shape) + new_img.flat[indices] = sampled_data + + # We smooth with a Gaussian kernel with x_stddev=1 (and y_stddev=1) + # It is a 9x9 array + kernel = Gaussian2DKernel(x_stddev=1) + + # create a "reconstructed" image with NaNs replaced by interpolated values + reconstructed_image = interpolate_replace_nans(new_img, kernel) + + # Now we do a bunch of plots. In the first two plots, the originally masked + # values are marked with red X's + fig, axs = plt.subplots(ncols=3, figsize=(12, 6)) + + ax1 = axs[0] + im = ax1.imshow(img, vmin=-2., vmax=2.e1, origin='lower', + interpolation='nearest', cmap='viridis') + y, x = np.where(np.isnan(img)) + ax1.set( + autoscale_on=False, + title="Original", + xticklabels=[], + yticklabels=[], + ) + + ax2 = axs[1] + im = ax2.imshow(new_img, vmin=-2., vmax=2.e1, origin='lower', + interpolation='nearest', cmap='viridis') + ax2.set( + title="Sparsely Sampled", + xticklabels=[], + yticklabels=[], + ) + + ax3 = axs[2] + im = ax3.imshow(reconstructed_image, vmin=-2., vmax=2.e1, origin='lower', + interpolation='nearest', cmap='viridis') + ax3.set( + title="Reconstructed", + xticklabels=[], + yticklabels=[], + ) + +.. + EXAMPLE END + +Using `astropy.convolution` +=========================== + +.. toctree:: + :maxdepth: 2 + + using.rst + kernels.rst + non_normalized_kernels.rst + +.. note that if this section gets too long, it should be moved to a separate + doc page - see the top of performance.inc.rst for the instructions on how to + do that +.. include:: performance.inc.rst + +Reference/API +============= + +.. toctree:: + :maxdepth: 2 + + ref_api diff --git a/docs/convolution/kernels.rst b/docs/convolution/kernels.rst new file mode 100644 index 000000000000..d983dc39f6ac --- /dev/null +++ b/docs/convolution/kernels.rst @@ -0,0 +1,390 @@ +Convolution Kernels +******************* + +Introduction and Concept +======================== + +The convolution module provides several built-in kernels to cover the most +common applications in astronomy. It is also possible to define custom kernels +from arrays or combine existing kernels to match specific applications. + +Every filter kernel is characterized by its response function. For time series +we speak of an "impulse response function" or for images we call it "point +spread function." This response function is given for every kernel by a +`~astropy.modeling.FittableModel`, which is evaluated on a grid with +:func:`~astropy.convolution.discretize_model` to obtain a kernel +array, which can be used for discrete convolution with the binned data. + + +Examples +======== + +1D Kernels +---------- + +.. + EXAMPLE START + Using 1D Kernels to Smooth Noisy Data + +One application of filtering is to smooth noisy data. In this case we +consider a noisy Lorentz curve: + +>>> import numpy as np +>>> from astropy.modeling.models import Lorentz1D +>>> from astropy.convolution import convolve, Gaussian1DKernel, Box1DKernel +>>> rng = np.random.default_rng() +>>> lorentz = Lorentz1D(1, 0, 1) +>>> x = np.linspace(-5, 5, 100) +>>> data_1D = lorentz(x) + 0.1 * (rng.random(100) - 0.5) + +Smoothing the noisy data with a `~astropy.convolution.Gaussian1DKernel` +with a standard deviation of 2 pixels: + +>>> gauss_kernel = Gaussian1DKernel(2) +>>> smoothed_data_gauss = convolve(data_1D, gauss_kernel) + +Smoothing the same data with a `~astropy.convolution.Box1DKernel` of width 5 +pixels: + +>>> box_kernel = Box1DKernel(5) +>>> smoothed_data_box = convolve(data_1D, box_kernel) + +The following plot illustrates the results: + +.. plot:: + + import numpy as np + import matplotlib.pyplot as plt + from astropy.modeling.models import Lorentz1D + from astropy.convolution import convolve, Gaussian1DKernel, Box1DKernel + + # Fake Lorentz data including noise + rng = np.random.default_rng() + lorentz = Lorentz1D(1, 0, 1) + x = np.linspace(-5, 5, 100) + data_1D = lorentz(x) + 0.1 * (rng.random(100) - 0.5) + + # Smooth data + gauss_kernel = Gaussian1DKernel(2) + smoothed_data_gauss = convolve(data_1D, gauss_kernel) + box_kernel = Box1DKernel(5) + smoothed_data_box = convolve(data_1D, box_kernel) + + # Plot data and smoothed data + fig, ax = plt.subplots() + ax.plot(x, data_1D, label='Original') + ax.plot(x, smoothed_data_gauss, label='Smoothed with Gaussian1DKernel') + ax.plot(x, smoothed_data_box, label='Smoothed with Box1DKernel') + ax.set( + xlabel='x [a.u.]', + ylabel='amplitude [a.u.]', + xlim=(-5, 5), + ylim=(-0.1, 1.5), + ) + ax.legend(prop={'size':12}) + plt.show() + + +Beside the ``astropy`` convolution functions +`~astropy.convolution.convolve` and +`~astropy.convolution.convolve_fft`, it is also possible to use +the kernels with ``numpy`` or ``scipy`` convolution by passing the ``array`` +attribute. This will be faster in most cases than the ``astropy`` convolution, +but will not work properly if NaN values are present in the data. + +>>> smoothed = np.convolve(data_1D, box_kernel.array, mode='same') + +.. + EXAMPLE END + +2D Kernels +---------- + +.. + EXAMPLE START + Using 2D Kernels to Smooth Noisy Data + +As all 2D kernels are symmetric, it is sufficient to specify the width in one +direction. Therefore the use of 2D kernels is basically the same as for 1D +kernels. Here we consider a small Gaussian-shaped source of amplitude 1 in the +middle of the image and add 10% noise: + +>>> import numpy as np +>>> from astropy.convolution import convolve, Gaussian2DKernel, Tophat2DKernel +>>> from astropy.modeling.models import Gaussian2D +>>> gauss = Gaussian2D(1, 0, 0, 3, 3) +>>> # Fake image data including noise +>>> rng = np.random.default_rng() +>>> x = np.arange(-100, 101) +>>> y = np.arange(-100, 101) +>>> x, y = np.meshgrid(x, y) +>>> data_2D = gauss(x, y) + 0.1 * (rng.random((201, 201)) - 0.5) + +Smoothing the noisy data with a +:class:`~astropy.convolution.Gaussian2DKernel` with a standard +deviation of 2 pixels: + +>>> gauss_kernel = Gaussian2DKernel(2) +>>> smoothed_data_gauss = convolve(data_2D, gauss_kernel) + +Smoothing the noisy data with a +:class:`~astropy.convolution.Tophat2DKernel` of width 5 pixels: + +>>> tophat_kernel = Tophat2DKernel(5) +>>> smoothed_data_tophat = convolve(data_2D, tophat_kernel) + +This is what the original image looks like: + +.. plot:: + + import numpy as np + import matplotlib.pyplot as plt + from astropy.modeling.models import Gaussian2D + gauss = Gaussian2D(1, 0, 0, 2, 2) + # Fake image data including noise + rng = np.random.default_rng() + x = np.arange(-100, 101) + y = np.arange(-100, 101) + x, y = np.meshgrid(x, y) + data_2D = gauss(x, y) + 0.1 * (rng.random((201, 201)) - 0.5) + fig, ax = plt.subplots() + im = ax.imshow(data_2D, origin='lower') + ax.set(xlabel='x [pixels]', ylabel='y [pixels]') + fig.colorbar(im) + plt.show() + +The following plot illustrates the differences between several 2D kernels +applied to the simulated data. Note that it has a slightly different color +scale compared to the original image. + +.. plot:: + + import numpy as np + import matplotlib.pyplot as plt + + from astropy.convolution import * + from astropy.modeling.models import Gaussian2D + + # Small Gaussian source in the middle of the image + gauss = Gaussian2D(1, 0, 0, 2, 2) + # Fake data including noise + rng = np.random.default_rng() + x = np.arange(-100, 101) + y = np.arange(-100, 101) + x, y = np.meshgrid(x, y) + data_2D = gauss(x, y) + 0.1 * (rng.random((201, 201)) - 0.5) + + # Setup kernels, including unity kernel for original image + # Choose normalization for linear scale space for RickerWavelet + + kernels = [TrapezoidDisk2DKernel(11, slope=0.2), + Tophat2DKernel(11), + Gaussian2DKernel(11), + Box2DKernel(11), + 11 ** 2 * RickerWavelet2DKernel(11), + AiryDisk2DKernel(11)] + + fig, axes = plt.subplots(nrows=2, ncols=3) + + # Plot kernels + for kernel, ax in zip(kernels, axes.flat): + smoothed = convolve(data_2D, kernel, normalize_kernel=False) + im = ax.imshow(smoothed, vmin=-0.01, vmax=0.08, origin='lower', + interpolation='None') + title = kernel.__class__.__name__ + ax.set_title(title, fontsize=12) + ax.set_yticklabels([]) + ax.set_xticklabels([]) + + cax = fig.add_axes([0.9, 0.1, 0.03, 0.8]) + fig.colorbar(im, cax=cax) + fig.subplots_adjust(left=0.05, right=0.85, top=0.95, bottom=0.05) + plt.show() + + +The Gaussian kernel has better smoothing properties compared to the Box and the +Top Hat. The Box filter is not isotropic and can produce artifacts (the source +appears rectangular). The Ricker Wavelet filter removes noise and slowly varying +structures (i.e., background), but produces a negative ring around the source. +The best choice for the filter strongly depends on the application. + +.. + EXAMPLE END + +Available Kernels +================= + +.. currentmodule:: astropy.convolution + +.. autosummary:: + + AiryDisk2DKernel + Box1DKernel + Box2DKernel + CustomKernel + Gaussian1DKernel + Gaussian2DKernel + RickerWavelet1DKernel + RickerWavelet2DKernel + Model1DKernel + Model2DKernel + Moffat2DKernel + Ring2DKernel + Tophat2DKernel + Trapezoid1DKernel + TrapezoidDisk2DKernel + +Kernel Arithmetic +================= + +Addition and Subtraction +------------------------ + +As convolution is a linear operation, kernels can be added or subtracted from +each other. They can also be multiplied with some number. + +Examples +^^^^^^^^ + +.. + EXAMPLE START + Adding and Subtracting Kernels in astropy.convolution + +One basic example of subtracting kernels would be the definition of a +Difference of Gaussian filter: + +>>> from astropy.convolution import Gaussian1DKernel +>>> gauss_1 = Gaussian1DKernel(10) +>>> gauss_2 = Gaussian1DKernel(16) +>>> DoG = gauss_2 - gauss_1 + +Another application is to convolve faked data with an instrument response +function model. For example, if the response function can be described by +the weighted sum of two Gaussians: + +>>> gauss_1 = Gaussian1DKernel(10) +>>> gauss_2 = Gaussian1DKernel(16) +>>> SoG = 4 * gauss_1 + gauss_2 + +Most times it will be necessary to normalize the resulting kernel by calling +explicitly: + +>>> SoG.normalize() + +.. + EXAMPLE END + +Convolution +----------- + +Furthermore, two kernels can be convolved with each other, which is useful when +data is filtered with two different kinds of kernels or to create a new, +special kernel. + +Examples +^^^^^^^^ + +.. + EXAMPLE START + Convolving Kernels in astropy.convolution + +To convolve two kernels with each other: + +>>> from astropy.convolution import Gaussian1DKernel, convolve +>>> gauss_1 = Gaussian1DKernel(10) +>>> gauss_2 = Gaussian1DKernel(16) +>>> broad_gaussian = convolve(gauss_2, gauss_1) # doctest: +IGNORE_WARNINGS + +Or in case of multistage smoothing: + +>>> import numpy as np +>>> from astropy.modeling.models import Lorentz1D +>>> from astropy.convolution import convolve, Gaussian1DKernel, Box1DKernel +>>> rng = np.random.default_rng() +>>> lorentz = Lorentz1D(1, 0, 1) +>>> x = np.linspace(-5, 5, 100) +>>> data_1D = lorentz(x) + 0.1 * (rng.random(100) - 0.5) + +>>> gauss = Gaussian1DKernel(3) +>>> box = Box1DKernel(5) +>>> smoothed_gauss = convolve(data_1D, gauss) +>>> smoothed_gauss_box = convolve(smoothed_gauss, box) + +You would rather do the following: + +>>> gauss = Gaussian1DKernel(3) +>>> box = Box1DKernel(5) +>>> smoothed_gauss_box = convolve(data_1D, convolve(box, gauss)) # doctest: +IGNORE_WARNINGS + +Which, in most cases, will also be faster than the first method because only +one convolution with the often times larger data array will be necessary. + +.. + EXAMPLE END + +Discretization +============== + +To obtain the kernel array for discrete convolution, the kernel's response +function is evaluated on a grid with +:func:`~astropy.convolution.discretize_model`. For the +discretization step the following modes are available: + +* Mode ``'center'`` (default) evaluates the response function on the grid by + taking the value at the center of the bin. + + >>> from astropy.convolution import Gaussian1DKernel + >>> gauss_center = Gaussian1DKernel(3, mode='center') + +* Mode ``'linear_interp'`` takes the values at the corners of the bin and + linearly interpolates the value at the center: + + >>> gauss_interp = Gaussian1DKernel(3, mode='linear_interp') + +* Mode ``'oversample'`` evaluates the response function by taking the mean on an + oversampled grid. The oversample factor can be specified with the ``factor`` + argument. If the oversample factor is too large, the evaluation becomes slow. + + >>> gauss_oversample = Gaussian1DKernel(3, mode='oversample', factor=10) + +* Mode ``'integrate'`` integrates the function over the pixel using + ``scipy.integrate.quad`` and ``scipy.integrate.dblquad``. This mode is very + slow and is only recommended when the highest accuracy is required. + +.. doctest-requires:: scipy + + >>> gauss_integrate = Gaussian1DKernel(3, mode='integrate') + +Especially in the range where the kernel width is in order of only a few pixels, +it can be advantageous to use the mode ``oversample`` or ``integrate`` to +conserve the integral on a subpixel scale. + +.. _kernel_normalization: + +Normalization +============= + +The kernel models are normalized per default (i.e., +:math:`\int_{-\infty}^{\infty} f(x) dx = 1`). But because of the limited kernel +array size, the normalization for kernels with an infinite response can differ +from one. The value of this deviation is stored in the kernel's ``truncation`` +attribute. + +The normalization can also differ from one, especially for small kernels, due +to the discretization step. This can be partly controlled by the ``mode`` +argument, when initializing the kernel. (See also +:func:`~astropy.convolution.discretize_model`.) Setting the +``mode`` to ``'oversample'`` allows us to conserve the normalization even on the +subpixel scale. + +The kernel arrays can be renormalized explicitly by calling either the +``normalize()`` method or by setting the ``normalize_kernel`` argument in the +:func:`~astropy.convolution.convolve` and +:func:`~astropy.convolution.convolve_fft` functions. The latter +method leaves the kernel itself unchanged but works with an internal normalized +version of the kernel. + +Note that for :class:`~astropy.convolution.RickerWavelet1DKernel` +and :class:`~astropy.convolution.RickerWavelet2DKernel` there is +:math:`\int_{-\infty}^{\infty} f(x) dx = 0`. To define a proper normalization, +both filters are derived from a normalized Gaussian function. diff --git a/docs/convolution/non_normalized_kernels.rst b/docs/convolution/non_normalized_kernels.rst new file mode 100644 index 000000000000..05991057f7c4 --- /dev/null +++ b/docs/convolution/non_normalized_kernels.rst @@ -0,0 +1,123 @@ +************************************ +Convolving with Unnormalized Kernels +************************************ + +There are some tasks, such as source finding, where you want to apply a filter +with a kernel that is not normalized. + +For data that are well-behaved (contain no missing or infinite values), this +can be done in one step:: + + convolve(image, kernel) + +Examples +-------- + +.. + EXAMPLE START + Convolving with Unnormalized Kernels + +For an example of applying a filter with a kernel that is not normalized, we +can try to run a commonly used peak enhancing kernel: + +.. plot:: + :context: reset + :include-source: + :align: center + + import numpy as np + import matplotlib.pyplot as plt + + from astropy.io import fits + from astropy.utils.data import get_pkg_data_filename + from astropy.convolution import CustomKernel + from scipy.signal import convolve as scipy_convolve + from astropy.convolution import convolve, convolve_fft + + + # Load the data from data.astropy.org + filename = get_pkg_data_filename('galactic_center/gc_msx_e.fits') + hdu = fits.open(filename)[0] + + # Scale the file to have reasonable numbers + # (this is mostly so that colorbars don't have too many digits) + # Also, we crop it so you can see individual pixels + img = hdu.data[50:90, 60:100] * 1e5 + + kernel = CustomKernel([[-1,-1,-1], [-1, 8, -1], [-1,-1,-1]]) + + astropy_conv = convolve(img, kernel, normalize_kernel=False, nan_treatment='fill') + #astropy_conv_fft = convolve_fft(img, kernel, normalize_kernel=False, nan_treatment='fill') + + fig, axs = plt.subplots(ncols=2, figsize=(12, 12)) + ax1 = axs[0] + im = ax1.imshow(img, vmin=-6., vmax=5.e1, origin='lower', + interpolation='nearest', cmap='viridis') + + ax2 = axs[1] + im = ax2.imshow(astropy_conv, vmin=-6., vmax=5.e1, origin='lower', + interpolation='nearest', cmap='viridis') + +.. + EXAMPLE END + +.. + EXAMPLE START + Replacing NaN Values with Interpolated Values Using Kernels + +If you have an image with missing values (NaNs), you have to replace them with +real values first. Often, the best way to do this is to replace the NaN values +with interpolated values. In the example below, we use a Gaussian kernel +with a size similar to that of our peak-finding kernel to replace the bad data +before applying the peak-finding kernel. + +.. plot:: + :context: + :include-source: + :align: center + + from astropy.convolution import Gaussian2DKernel, interpolate_replace_nans + + # Select a random set of pixels that were affected by some sort of artifact + # and replaced with NaNs (e.g., cosmic-ray-affected pixels) + rng = np.random.default_rng(42) + yinds, xinds = np.indices(img.shape) + img[rng.choice(yinds.flat, 50), rng.choice(xinds.flat, 50)] = np.nan + + # We smooth with a Gaussian kernel with x_stddev=1 (and y_stddev=1) + # It is a 9x9 array + kernel = Gaussian2DKernel(x_stddev=1) + + # interpolate away the NaNs + reconstructed_image = interpolate_replace_nans(img, kernel) + + + # apply peak-finding + kernel = CustomKernel([[-1,-1,-1], [-1, 8, -1], [-1,-1,-1]]) + + # Use the peak-finding kernel + # We have to turn off kernel normalization and set nan_treatment to "fill" + # here because `nan_treatment='interpolate'` is incompatible with non- + # normalized kernels + peaked_image = convolve(reconstructed_image, kernel, + normalize_kernel=False, + nan_treatment='fill') + + fig, axs = plt.subplots(ncols=3, figsize=(12, 12)) + ax1 = axs[0] + ax1.set_title("Image with missing data") + im = ax1.imshow(img, vmin=-6., vmax=5.e1, origin='lower', + interpolation='nearest', cmap='viridis') + + ax2 = axs[1] + ax2.set_title("Interpolated") + im = ax2.imshow(reconstructed_image, vmin=-6., vmax=5.e1, origin='lower', + interpolation='nearest', cmap='viridis') + + ax3 = axs[2] + ax3.set_title("Peak-Finding") + im = ax3.imshow(peaked_image, vmin=-6., vmax=5.e1, origin='lower', + interpolation='nearest', cmap='viridis') + +.. + EXAMPLE END diff --git a/docs/convolution/performance.inc.rst b/docs/convolution/performance.inc.rst new file mode 100644 index 000000000000..0c33f57fe63a --- /dev/null +++ b/docs/convolution/performance.inc.rst @@ -0,0 +1,14 @@ +.. note that if this is changed from the default approach of using an *include* + (in index.rst) to a separate performance page, the header needs to be changed + from === to ***, the filename extension needs to be changed from .inc.rst to + .rst, and a link needs to be added in the sub-package toctree + +.. _astropy-convolution-performance: + +Performance Tips +================ + +The :func:`~astropy.convolution.convolve` function is best suited to small +kernels, and can become very slow for larger kernels. In this case, consider +using :func:`~astropy.convolution.convolve_fft` (though note that this function +uses more memory, and consider the different padding options). diff --git a/docs/convolution/ref_api.rst b/docs/convolution/ref_api.rst new file mode 100644 index 000000000000..ad6a686cfc4f --- /dev/null +++ b/docs/convolution/ref_api.rst @@ -0,0 +1,5 @@ +Reference/API +************* + +.. automodapi:: astropy.convolution + :no-inheritance-diagram: diff --git a/docs/convolution/using.rst b/docs/convolution/using.rst new file mode 100644 index 000000000000..2405130a1ead --- /dev/null +++ b/docs/convolution/using.rst @@ -0,0 +1,108 @@ +Using the Convolution Functions +******************************* + +Overview +======== + +Two convolution functions are provided. They are imported as:: + + >>> from astropy.convolution import convolve, convolve_fft + +and are both used as:: + + >>> result = convolve(image, kernel) # doctest: +SKIP + >>> result = convolve_fft(image, kernel) # doctest: +SKIP + +:func:`~astropy.convolution.convolve` is implemented as a +direct convolution algorithm, while +:func:`~astropy.convolution.convolve_fft` uses a Fast Fourier +Transform (FFT). Thus, the former is better for small kernels, while the latter +is much more efficient for larger kernels. + +The input images and kernels should be lists or ``numpy`` arrays with either 1, +2, or 3 dimensions (and the number of dimensions should be the same for the +image and kernel). The result is a ``numpy`` array with the same dimensions as +the input image. The convolution is always done as floating point. + +The :func:`~astropy.convolution.convolve` function takes an +optional ``boundary=`` argument describing how to perform the convolution at +the edge of the array. The values for ``boundary`` can be: + +* ``None``: set the result values to zero where the kernel extends beyond the + edge of the array (default). + +* ``'fill'``: set values outside the array boundary to a constant. If this + option is specified, the constant should be specified using the + ``fill_value=`` argument, which defaults to zero. + +* ``'wrap'``: assume that the boundaries are periodic. + +* ``'extend'`` : set values outside the array to the nearest array value. + +By default, the kernel is not normalized. To normalize it prior to convolution, +use:: + + >>> result = convolve(image, kernel, normalize_kernel=True) # doctest: +SKIP + +Examples +-------- + +.. + EXAMPLE START + Smoothing Arrays with Custom Kernels + +Smooth a 1D array with a custom kernel and no boundary treatment:: + + >>> import numpy as np + >>> convolve([1, 4, 5, 6, 5, 7, 8], [0.2, 0.6, 0.2]) # doctest: +FLOAT_CMP + array([1.4, 3.6, 5. , 5.6, 5.6, 6.8, 6.2]) + +As above, but using the 'extend' algorithm for boundaries:: + + >>> convolve([1, 4, 5, 6, 5, 7, 8], [0.2, 0.6, 0.2], boundary='extend') # doctest: +FLOAT_CMP + array([1.6, 3.6, 5. , 5.6, 5.6, 6.8, 7.8]) + +If a NaN value is present in the original array, it will be +interpolated using the kernel:: + + >>> import numpy as np + >>> convolve([1, 4, 5, 6, np.nan, 7, 8], [0.2, 0.6, 0.2], boundary='extend') # doctest: +FLOAT_CMP + array([1.6 , 3.6 , 5. , 5.75, 6.5 , 7.25, 7.8 ]) + +.. + EXAMPLE END + +.. + EXAMPLE START + Constructing Kernels from Lists + +Kernels and arrays can be specified either as lists or as ``numpy`` +arrays. The following examples show how to construct a 1D array as a +list:: + + >>> kernel = [0, 1, 0] + >>> result = convolve(spectrum, kernel) # doctest: +SKIP + +A 2D array as a list:: + + >>> kernel = [[0, 1, 0], + ... [1, 2, 1], + ... [0, 1, 0]] + >>> result = convolve(image, kernel) # doctest: +SKIP + +And a 3D array as a list:: + + >>> kernel = [[[0, 0, 0], [0, 2, 0], [0, 0, 0]], + ... [[0, 1, 0], [2, 3, 2], [0, 1, 0]], + ... [[0, 0, 0], [0, 2, 0], [0, 0, 0]]] + >>> result = convolve(cube, kernel) # doctest: +SKIP + +.. + EXAMPLE END + +Kernels +======= + +The above examples use custom kernels, but `astropy.convolution` also +includes a number of built-in kernels, which are described in +:doc:`kernels`. diff --git a/docs/coordinates/angles.rst b/docs/coordinates/angles.rst new file mode 100644 index 000000000000..cb47f308bfb1 --- /dev/null +++ b/docs/coordinates/angles.rst @@ -0,0 +1,353 @@ +.. _working_with_angles: + +Working with Angles +******************* + +The angular components of the various coordinate objects are represented +by objects of the |Angle| class. While most likely to be encountered in +the context of coordinate objects, |Angle| objects can also be used on +their own wherever a representation of an angle is needed. + +.. _angle-creation: + +Creation +======== + +The creation of an |Angle| object is quite flexible and supports a wide +variety of input object types and formats. The type of the input angle(s) +can be array, scalar, tuple, string, `~astropy.units.Quantity` or another +|Angle|. This is best illustrated with a number of examples of valid ways +to create an |Angle|. + +Examples +-------- + +.. + EXAMPLE START + Different Ways to Create an Angle Object + +There are a number of ways to create an |Angle|:: + + >>> import numpy as np + >>> from astropy import units as u + >>> from astropy.coordinates import Angle + + >>> Angle('10.2345d') # String with 'd' abbreviation for degrees # doctest: +FLOAT_CMP + + >>> Angle(['10.2345d', '-20d']) # Array of strings # doctest: +FLOAT_CMP + + >>> Angle('1:2:30.43 degrees') # Sexagesimal degrees # doctest: +FLOAT_CMP + + >>> Angle('1 2 0 hours') # Sexagesimal hours # doctest: +FLOAT_CMP + + >>> Angle(np.arange(1., 8.), unit=u.deg) # Numpy array from 1..7 in degrees # doctest: +FLOAT_CMP + + >>> Angle('1°2′3â€ŗ') # Unicode degree, arcmin and arcsec symbols # doctest: +FLOAT_CMP + + >>> Angle('1°2′3â€ŗN') # Unicode degree, arcmin, arcsec symbols and direction # doctest: +FLOAT_CMP + + >>> Angle('1d2m3.4s') # Degree, arcmin, arcsec. # doctest: +FLOAT_CMP + + >>> Angle('1d2m3.4sS') # Degree, arcmin, arcsec, direction. # doctest: +FLOAT_CMP + + >>> Angle('-1h2m3s') # Hour, minute, second # doctest: +FLOAT_CMP + + >>> Angle('-1h2m3sW') # Hour, minute, second, direction # doctest: +FLOAT_CMP + + >>> Angle(10.2345 * u.deg) # From a Quantity object in degrees # doctest: +FLOAT_CMP + + >>> Angle(Angle(10.2345 * u.deg)) # From another Angle object # doctest: +FLOAT_CMP + + +.. + EXAMPLE END + +Representation +============== + +The |Angle| object also supports a variety of ways of representing the value +of the angle, both as a floating point number and as a string. + +Examples +-------- + +.. + EXAMPLE START + Representation of Angle Object Values + +There are many ways to represent the value of an |Angle|:: + + >>> a = Angle(1, u.radian) + >>> a # doctest: +FLOAT_CMP + + >>> a.radian + np.float64(1.0) + >>> a.degree # doctest: +FLOAT_CMP + np.float64(57.29577951308232) + >>> a.hour # doctest: +FLOAT_CMP + np.float64(3.8197186342054885) + >>> a.hms # doctest: +FLOAT_CMP + hms_tuple(h=np.float64(3.0), m=np.float64(49.0), s=np.float64(10.987083139758766)) + >>> a.dms # doctest: +FLOAT_CMP + dms_tuple(d=np.float64(57.0), m=np.float64(17.0), s=np.float64(44.806247096362313)) + >>> a.signed_dms # doctest: +FLOAT_CMP + signed_dms_tuple(sign=np.float64(1.0), d=np.float64(57.0), m=np.float64(17.0), s=np.float64(44.806247096362313)) + >>> (-a).dms # doctest: +FLOAT_CMP + dms_tuple(d=np.float64(-57.0), m=np.float64(-17.0), s=np.float64(-44.806247096362313)) + >>> (-a).signed_dms # doctest: +FLOAT_CMP + signed_dms_tuple(sign=np.float64(-1.0), d=np.float64(57.0), m=np.float64(17.0), s=np.float64(44.806247096362313)) + >>> a.arcminute # doctest: +FLOAT_CMP + np.float64(3437.7467707849396) + >>> f"{a}" + '1.0 rad' + >>> f"{a:latex}" + np.str_('$1\\;\\mathrm{rad}$') + >>> f"{a.to(u.deg):latex}" + np.str_('$57^\\circ17{}^\\prime44.8062471{}^{\\prime\\prime}$') + >>> a.to_string() + np.str_('1 rad') + >>> a.to_string(unit=u.degree) + np.str_('57d17m44.8062471s') + >>> a.to_string(unit=u.degree, sep=':') + np.str_('57:17:44.8062471') + >>> a.to_string(unit=u.degree, sep=('deg', 'm', 's')) + np.str_('57deg17m44.8062471s') + >>> a.to_string(unit=u.hour) + np.str_('3h49m10.98708314s') + >>> a.to_string(unit=u.hour, decimal=True) + np.str_('3.81972') + +.. + EXAMPLE END + +Usage +===== + +Angles will also behave correctly for appropriate arithmetic operations. + +Example +------- + +.. + EXAMPLE START + Arithmetic Operations Using Angle Objects + +To use |Angle| objects in arithmetic operations:: + + >>> a = Angle(1.0, u.radian) + >>> a + 0.5 * u.radian + 2 * a # doctest: +FLOAT_CMP + + >>> np.sin(a / 2) # doctest: +FLOAT_CMP + + >>> a == a # doctest: +SKIP + array(True, dtype=bool) + >>> a == (a + a) # doctest: +SKIP + array(False, dtype=bool) + +.. + EXAMPLE END + +|Angle| objects can also be used for creating coordinate objects. + +Example +------- + +.. + EXAMPLE START + Creating Coordinate Objects with Angle Objects + +To create a coordinate object using an |Angle|:: + + >>> from astropy.coordinates import ICRS + >>> ICRS(Angle(1, u.deg), Angle(0.5, u.deg)) # doctest: +FLOAT_CMP + + +.. + EXAMPLE END + +Wrapping and Bounds +=================== + +There are two utility methods for working with angles that should have bounds. +The :meth:`~astropy.coordinates.Angle.wrap_at` method allows taking an angle or +angles and wrapping to be within a single 360 degree slice. The +:meth:`~astropy.coordinates.Angle.is_within_bounds` method returns a +boolean indicating whether an angle or angles is within the specified bounds. + +.. Note:: + While creating |Angle| instances from arrays with integral data types + is technically possible (for example with ``dtype=int``), it is very + limited in functionality and in particular wrapping is not supported for + such objects. + + +Longitude and Latitude Objects +============================== + +|Longitude| and |Latitude| are two specialized subclasses of the |Angle| +class that are used for all of the spherical coordinate classes. +|Longitude| is used to represent values like right ascension, Galactic +longitude, and azimuth (for Equatorial, Galactic, and Alt-Az coordinates, +respectively). |Latitude| is used for declination, Galactic latitude, and +elevation. + +Longitude +--------- + +A |Longitude| object is distinguished from a pure |Angle| by virtue of a +``wrap_angle`` property. The ``wrap_angle`` specifies that all angle values +represented by the object will be in the range:: + + wrap_angle - 360 * u.deg <= angle(s) < wrap_angle + +The default ``wrap_angle`` is 360 deg. Setting ``'wrap_angle=180 * u.deg'`` +would instead result in values between -180 and +180 deg. Setting the +``wrap_angle`` attribute of an existing ``Longitude`` object will result in +re-wrapping the angle values in-place. For example:: + + >>> from astropy.coordinates import Longitude + >>> a = Longitude([-20, 150, 350, 360] * u.deg) + >>> a.degree # doctest: +FLOAT_CMP + array([340., 150., 350., 0.]) + >>> a.wrap_angle = 180 * u.deg + >>> a.degree # doctest: +FLOAT_CMP + array([-20., 150., -10., 0.]) + +Latitude +-------- + +A Latitude object is distinguished from a pure |Angle| by virtue +of being bounded so that:: + + -90.0 * u.deg <= angle(s) <= +90.0 * u.deg + +Any attempt to set a value outside of that range will result in a +`ValueError`. + + +Generating Angle Values +======================= + +Astropy provides utility functions for generating angular or spherical +positions, either with random sampling or with a grid of values. These functions +all return `~astropy.coordinates.BaseRepresentation` subclass instances, which +can be passed directly into coordinate frame classes or |SkyCoord| to create +random or gridded coordinate objects. + + +With Random Sampling +-------------------- + +These functions both use standard, random `spherical point picking +`_ to generate angular +positions that are uniformly distributed on the surface of the unit sphere. To +retrieve angular values only, use +`~astropy.coordinates.uniform_spherical_random_surface`. For +example, to generate 4 random angular positions:: + + >>> from astropy.coordinates import uniform_spherical_random_surface + >>> pts = uniform_spherical_random_surface(size=4) + >>> pts # doctest: +SKIP + + +To generate three-dimensional positions uniformly within a spherical volume set +by a maximum radius, instead use the +`~astropy.coordinates.uniform_spherical_random_volume` +function. For example, to generate 4 random 3D positions:: + + >>> from astropy.coordinates import uniform_spherical_random_volume + >>> pts_3d = uniform_spherical_random_volume(size=4) + >>> pts_3d # doctest: +SKIP + + +By default, the distance values returned are uniformly distributed within the +unit sphere (i.e., the distance values are dimensionless). To instead generate +random points within a sphere of a given dimensional radius, for example, 1 +parsec, pass in a |Quantity| object with the ``max_radius`` argument:: + + >>> import astropy.units as u + >>> pts_3d = uniform_spherical_random_volume(size=4, max_radius=2*u.pc) + >>> pts_3d # doctest: +SKIP + + + +On a Grid +--------- + +No grid or lattice of points on the sphere can produce equal spacing between all +grid points, but many approximate algorithms exist for generating angular grids +with nearly even spacing (for example, `see this page +`_). + +One simple and popular method in this context is the `golden spiral method +`_, which is available in +`astropy.coordinates` through the utility function +`~astropy.coordinates.golden_spiral_grid`. This function accepts +a single argument, ``size``, which specifies the number of points to generate in +the grid:: + + >>> from astropy.coordinates import golden_spiral_grid + >>> golden_pts = golden_spiral_grid(size=32) + >>> golden_pts # doctest: +FLOAT_CMP + + + + + +Comparing Spherical Point Generation Methods +-------------------------------------------- + +.. plot:: + :align: center + :context: close-figs + + import matplotlib.pyplot as plt + from astropy.coordinates import uniform_spherical_random_surface, golden_spiral_grid + + fig, axes = plt.subplots(1, 2, figsize=(10, 6), + subplot_kw=dict(projection='3d'), + constrained_layout=True) + + for func, ax in zip([uniform_spherical_random_surface, + golden_spiral_grid], axes): + pts = func(size=128) + + xyz = pts.to_cartesian().xyz + ax.scatter(*xyz) + + ax.set(xlim=(-1, 1), + ylim=(-1, 1), + zlim=(-1, 1), + xlabel='$x$', + ylabel='$y$', + zlabel='$z$') + ax.set_title(func.__name__, fontsize=14) + + fig.suptitle('128 points', fontsize=18) diff --git a/docs/coordinates/apply_space_motion.rst b/docs/coordinates/apply_space_motion.rst new file mode 100644 index 000000000000..3a50653c7690 --- /dev/null +++ b/docs/coordinates/apply_space_motion.rst @@ -0,0 +1,187 @@ +.. _astropy-coordinates-apply-space-motion: + +Accounting for Space Motion +*************************** + +The |SkyCoord| object supports updating the position of a source given its space +motion and a time at which to evaluate the new position (or a difference +between the coordinate's current time and a new one). This is +done using the :meth:`~astropy.coordinates.SkyCoord.apply_space_motion` method. + +Example +------- + +.. + EXAMPLE START + Accounting for Space Motion with SkyCoord Objects + +First we will create a |SkyCoord| object with a specified ``obstime``:: + + >>> import astropy.units as u + >>> from astropy.time import Time + >>> from astropy.coordinates import SkyCoord + >>> c = SkyCoord(l=10*u.degree, b=45*u.degree, distance=100*u.pc, + ... pm_l_cosb=34*u.mas/u.yr, pm_b=-117*u.mas/u.yr, + ... frame='galactic', + ... obstime=Time('1988-12-18 05:11:23.5')) + +We can now find the position at some other time, taking the space motion into +account. We can either specify the time difference between the observation time +and the desired time:: + + >>> c.apply_space_motion(dt=10. * u.year) # doctest: +FLOAT_CMP + + >>> c.apply_space_motion(dt=-10. * u.year) # doctest: +FLOAT_CMP + + +Or, we can specify the new time to evaluate the position at:: + + >>> c.apply_space_motion(new_obstime=Time('2017-12-18 01:12:07.3')) # doctest: +FLOAT_CMP + + +.. + EXAMPLE END + +If the |SkyCoord| object has no specified radial velocity (RV), the RV is +assumed to be 0. The new position of the source is determined assuming the +source moves in a straight line with constant velocity in an inertial frame. +There are no plans to support more complex evolution (e.g., non-inertial +frames or more complex evolution), as that is out of scope for the ``astropy`` +core package (although it may well be in-scope for a variety of affiliated +packages). + +Example: Use velocity to compute sky position at different epochs +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. + EXAMPLE START + Using Velocity to Compute Sky Position at Different Epochs + +In this example, we will use *Gaia* `TGAS +`_ astrometry for a nearby star to +compute the sky position of the source on the date that the 2MASS survey +observed that region of the sky. The TGAS astrometry is provided on the +reference epoch J2015.0, whereas the 2MASS survey occurred in the late 1990's. +For the star of interest, the proper motion is large enough that there are +appreciable differences in the sky position between the two surveys. + +After computing the previous position of the source, we will then cross-match +the source with the 2MASS catalog to compute *Gaia*-2MASS colors for this object +source. + +.. note:: + + This example requires accessing data from the *Gaia* TGAS and 2MASS + catalogs. For convenience and speed below, we have created dictionary + objects that contain the data. We retrieved the data using the Astropy + affiliated package `astroquery `_ using + the following queries:: + + import astropy.coordinates as coord + import astropy.units as u + from astroquery.gaia import Gaia + from astroquery.vizier import Vizier + + job = Gaia.launch_job("SELECT TOP 1 * FROM gaiadr1.tgas_source \ + WHERE parallax_error < 0.3 AND parallax > 5 AND pmra > 100 \ + ORDER BY random_index") + result_tgas = job.get_results()[0] + + c_tgas = coord.SkyCoord(ra=result_tgas['ra'] * u.deg, + dec=result_tgas['dec'] * u.deg) + v = Vizier(columns=["**"], catalog="II/246/out") + result_2mass = v.query_region(c_tgas, radius=1*u.arcmin)['II/246/out'] + +The TGAS data from relevant columns for this source (see queries in Note +above):: + + >>> result_tgas = dict(ra=66.44280212823296, + ... dec=-69.99366255906372, + ... parallax=22.764078749733947, + ... pmra=144.91354358297048, + ... pmdec=5.445648092997134, + ... ref_epoch=2015.0, + ... phot_g_mean_mag=7.657174523348196) + +The 2MASS data for all sources within 1 arcminute around the above position +(see queries in Note above):: + + >>> result_2mass = dict(RAJ2000=[66.421970000000002, 66.433521999999996, + ... 66.420564999999996, 66.485068999999996, + ... 66.467928999999998, 66.440815000000001, + ... 66.440454000000003], + ... DEJ2000=[-70.003722999999994, -69.990768000000003, + ... -69.992255999999998, -69.994881000000007, + ... -69.994926000000007, -69.993613999999994, + ... -69.990836999999999], + ... Jmag=[16.35, 13.663, 16.171, 16.184, 16.292, + ... 6.6420002, 12.275], + ... Hmag=[15.879, 13.955, 15.154, 15.856, 15.642, + ... 6.3660002, 12.185], + ... Kmag=[15.581, 14.238, 14.622, 15.398, 15.123, + ... 6.2839999, 12.106], + ... Date=['1998-10-24', '1998-10-24', '1998-10-24', + ... '1998-10-24', '1998-10-24', '1998-10-24', + ... '1998-10-24']) + +We will first create a |SkyCoord| object from the information provided in the +TGAS catalog. Note that we set the ``obstime`` of the object to the reference +epoch provided by the TGAS catalog (J2015.0 in Barycentric Coordinate Time):: + + >>> import astropy.units as u + >>> from astropy.coordinates import SkyCoord, Distance + >>> from astropy.time import Time + >>> c = SkyCoord(ra=result_tgas['ra'] * u.deg, + ... dec=result_tgas['dec'] * u.deg, + ... distance=Distance(parallax=result_tgas['parallax'] * u.mas), + ... pm_ra_cosdec=result_tgas['pmra'] * u.mas/u.yr, + ... pm_dec=result_tgas['pmdec'] * u.mas/u.yr, + ... obstime=Time(result_tgas['ref_epoch'], format='jyear', + ... scale='tcb')) + +We next create a |SkyCoord| object with the sky positions from the 2MASS +catalog, and an `~astropy.time.Time` object for the date of the 2MASS +observations provided in the 2MASS catalog (for the data in this region the +observation date is the same, so we take only the 0th value):: + + >>> catalog_2mass = SkyCoord(ra=result_2mass['RAJ2000'] * u.deg, + ... dec=result_2mass['DEJ2000'] * u.deg) + >>> epoch_2mass = Time(result_2mass['Date'][0]) + +We can now use the :meth:`~astropy.coordinates.SkyCoord.apply_space_motion` +method to compute the position of the TGAS source at another epoch. This uses +the proper motion and parallax information to evolve the position of the source +assuming straight-line motion:: + + >>> c_2mass_epoch = c.apply_space_motion(epoch_2mass) + +Now that we have the coordinates of the TGAS source at the 2MASS epoch, we can +do the cross-match (see also :ref:`astropy-coordinates-separations-matching`):: + + >>> idx, sep, _ = c_2mass_epoch.match_to_catalog_sky(catalog_2mass) # doctest: +SKIP + >>> sep[0].to_string() # doctest: +FLOAT_CMP +SKIP + '0d00m00.2818s' + >>> idx # doctest: +SKIP + array(5) + +The closest source it found is 0.2818 arcseconds away and corresponds to +row index 5 in the 2MASS catalog. We can then, for example, compute *Gaia*-2MASS +colors:: + + >>> G = result_tgas['phot_g_mean_mag'] + >>> J = result_2mass['Jmag'][idx] # doctest: +SKIP + >>> K = result_2mass['Kmag'][idx] # doctest: +SKIP + >>> G - J, G - K # doctest: +SKIP + (1.0151743233481962, 1.3731746233481958) + +.. + EXAMPLE END diff --git a/docs/coordinates/common_errors.rst b/docs/coordinates/common_errors.rst new file mode 100644 index 000000000000..c99ed07a2041 --- /dev/null +++ b/docs/coordinates/common_errors.rst @@ -0,0 +1,102 @@ +.. _astropy-coordinates-common-errors: + +Common mistakes +*************** + +The following are some common sources of difficulty when using `~astropy.coordinates`. + +Object Separation +----------------- + +When calculating the separation between objects, it is important to bear in mind that +:meth:`~astropy.coordinates.BaseCoordinateFrame.separation` can give a different +answer depending upon the order in which is used. +For example:: + + >>> import numpy as np + >>> from astropy import units as u + >>> from astropy.coordinates import SkyCoord, GCRS + >>> from astropy.time import Time + >>> t = Time("2010-05-22T00:00") + >>> moon = SkyCoord(104.29*u.deg, 23.51*u.deg, 359367.3*u.km, frame=GCRS(obstime=t)) + >>> star = SkyCoord(101.4*u.deg, 23.02*u.deg, frame='icrs') + >>> star.separation(moon) # doctest: +FLOAT_CMP, +SHOW_WARNINGS + + NonRotationTransformationWarning: transforming other coordinates from + to . Angular separation can + depend on the direction of the transformation. + >>> moon.separation(star) # doctest: +FLOAT_CMP, +SHOW_WARNINGS + + NonRotationTransformationWarning: transforming other coordinates from... + +Why do these give such different answers? + +The reason is that :meth:`~astropy.coordinates.BaseCoordinateFrame.separation` +gives the separation as measured in the frame of the |SkyCoord| object. +So ``star.separation(moon)`` gives the angular separation in the ICRS frame. +This is the separation as it would appear from the Solar System Barycenter. +For a geocentric observer, ``moon.separation(star)`` gives the correct answer, +since ``moon`` is in a geocentric frame. +As can be seen from the above example, by default an appropriate warning is +emitted if the coordinate transformation can cause the angular separation value +to be order-dependent. +It is possible to always suppress the warning:: + + >>> moon.separation(star, origin_mismatch="ignore") # doctest: +FLOAT_CMP + + +It is also possible to forbid coordinate transformations that are not pure +rotations:: + + >>> moon.separation(star, origin_mismatch="error") + Traceback (most recent call last): + ... + astropy.coordinates.errors.NonRotationTransformationError: refusing to + transform other coordinates from to because angular separation can depend on + the direction of the transformation + +AltAz calculations for Earth-based objects +------------------------------------------ + +One might expect that the following code snippet would produce an altitude of exactly 90 degrees:: + + >>> from astropy.coordinates import EarthLocation, AltAz + >>> from astropy.time import Time + >>> from astropy import units as u + + >>> t = Time('J2010') + >>> obj = EarthLocation(-1*u.deg, 52*u.deg, height=10.*u.km) + >>> home = EarthLocation(-1*u.deg, 52*u.deg, height=0.*u.km) + >>> aa = obj.get_itrs(t).transform_to(AltAz(obstime=t, location=home)) + >>> aa.alt # doctest: +FLOAT_CMP + + +Why is the result over three degrees away from the zenith? It is only possible to understand by taking a very careful +look at what ``obj.get_itrs(t)`` returns. This call provides the ITRS position of the source ``obj``. ITRS is +a geocentric coordinate system, and includes the aberration of light. So the code above provides the ITRS position +of a source that appears directly overhead *for an observer at the geocenter*. + +Due to aberration, the actual position of this source will be displaced from its apparent position, by an angle of +approximately 20.5 arcseconds. Because this source is about one Earth radius away, it's actual position is therefore +around 600 metres away from where it appears to be. This 600 metre shift, for an object 10 km above the Earth's surface +is an angular difference of just over three degrees - which is why this object does not appear overhead for a topocentric +observer - one on the surface of the Earth. + +The correct way to construct a |SkyCoord| object for a source that is directly overhead for a topocentric observer is +as follows:: + + >>> from astropy.coordinates import EarthLocation, AltAz, ITRS + >>> from astropy.time import Time + >>> from astropy import units as u + + >>> t = Time('J2010') + >>> obj = EarthLocation(-1*u.deg, 52*u.deg, height=10.*u.km) + >>> home = EarthLocation(-1*u.deg, 52*u.deg, height=0.*u.km) + + >>> # convert to AltAz + >>> aa = obj.get_itrs(t, location=home).transform_to(AltAz(obstime=t, location=home)) + >>> aa.alt # doctest: +FLOAT_CMP + diff --git a/docs/coordinates/definitions.rst b/docs/coordinates/definitions.rst new file mode 100644 index 000000000000..8214c4ce713e --- /dev/null +++ b/docs/coordinates/definitions.rst @@ -0,0 +1,43 @@ +.. _astropy-coordinates-definitions: + +Important Definitions +********************* + +For reference, below, we define some key terms as they are used in +`~astropy.coordinates`, due to some ambiguities that exist in the +colloquial use of these terms. Chief among these terms is the concept +of a "coordinate system." To some members of the community, "coordinate +system" means the *representation* of a point in space (e.g., "Cartesian +coordinate system" is different from "Spherical polar coordinate +system"). Another use of "coordinate system" is to mean a unique +reference frame with a particular set of reference points (e.g., "the +ICRS coordinate system" or the "J2000 coordinate system"). This second +meaning is further complicated by the fact that such systems use quite +different ways of defining a frame. + +Because of the likelihood of confusion between these meanings of +"coordinate system," `~astropy.coordinates` avoids this term wherever +possible, and instead adopts the following terms (loosely inspired by +the IAU2000 resolutions on celestial coordinate systems): + +* A "Coordinate Representation" is a particular way of describing a unique + point in a vector space. (Here, this means three-dimensional space, but future + extensions might have different dimensionality, particularly if relativistic + effects are desired.) Examples include Cartesian coordinates, cylindrical + polar, or latitude/longitude spherical polar coordinates. Note that this term + applies to the positions, *not* their velocities or other derivatives (which + are represented as "differential" classes). + +* A "Reference System" is a scheme for orienting points in a space and + describing how they transform to other systems. Examples include the ICRS, + equatorial coordinates with mean equinox, or the WGS84 geoid for + latitude/longitude on the Earth. + +* A "Coordinate Frame," "Reference Frame," or just "Frame" is a specific + realization of a reference system (e.g., the ICRF, or J2000 equatorial + coordinates). For some systems, there may be only one meaningful frame, while + others may have many different frames (differentiated by something like a + different equinox, or a different set of reference points). + +* A "Coordinate" is a combination of all of the above that specifies a unique + point. diff --git a/docs/coordinates/example_gallery_index.rst b/docs/coordinates/example_gallery_index.rst new file mode 100644 index 000000000000..92d11984d85a --- /dev/null +++ b/docs/coordinates/example_gallery_index.rst @@ -0,0 +1,16 @@ +.. _astropy-coordinates-example-gallery: + +Example Gallery +*************** + +This gallery of examples shows a variety of relatively small snippets or +examples of tasks that can be done with the ``astropy.coordinates`` sub-package. + +.. toctree:: + :maxdepth: 1 + + example_gallery_plot_galactocentric_frame + example_gallery_plot_mars_coordinate_frame + example_gallery_plot_obs_planning + example_gallery_plot_sgr_coordinate_frame + example_gallery_rv_to_gsr diff --git a/docs/coordinates/example_gallery_plot_galactocentric_frame.rst b/docs/coordinates/example_gallery_plot_galactocentric_frame.rst new file mode 100644 index 000000000000..9090c311002a --- /dev/null +++ b/docs/coordinates/example_gallery_plot_galactocentric_frame.rst @@ -0,0 +1,174 @@ +.. _sphx_glr_generated_examples_coordinates_plot_galactocentric-frame.py: + +Transforming positions and velocities to and from a Galactocentric frame +======================================================================== + +.. + EXAMPLE START + Transforming positions and velocities to and from a Galactocentric frames + +This example shows a few examples of how to use and customize the +`~astropy.coordinates.Galactocentric` frame to transform Heliocentric sky +positions, distance, proper motions, and radial velocities to a Galactocentric, +Cartesian frame, and the same in reverse. + +The main configurable parameters of the `~astropy.coordinates.Galactocentric` +frame control the position and velocity of the solar system barycenter within +the Galaxy. These are specified by setting the ICRS coordinates of the +Galactic center, the distance to the Galactic center (the sun-galactic center +line is always assumed to be the x-axis of the Galactocentric frame), and the +Cartesian 3-velocity of the sun in the Galactocentric frame. We will first +demonstrate how to customize these values, then show how to set the solar motion +instead by inputting the proper motion of Sgr A*. + +Note that, for brevity, we may refer to the solar system barycenter as just "the +sun" in the examples below. + +Let's first define a barycentric coordinate and velocity in the ICRS frame. +We will use the data for the star HD 39881 from the +`Simbad `_ database: + + +>>> import astropy.coordinates as coord +>>> from astropy import units as u +>>> c1 = coord.SkyCoord( +... ra=89.014303 * u.degree, +... dec=13.924912 * u.degree, +... distance=(37.59 * u.mas).to(u.pc, u.parallax()), +... pm_ra_cosdec=372.72 * (u.mas / u.yr), +... pm_dec=-483.69 * (u.mas / u.yr), +... radial_velocity=0.37 * (u.km / u.s), +... frame="icrs", +... ) + +This is a high proper-motion star; suppose we'd like to transform its position +and velocity to a Galactocentric frame to see if it has a large 3D velocity +as well. To use the Astropy default solar position and motion parameters, we +can do the following: + +>>> gc1 = c1.transform_to(coord.Galactocentric) + +From here, we can access the components of the resulting +`~astropy.coordinates.Galactocentric` instance to see the 3D Cartesian +velocity components: + +>>> print(gc1.v_x, gc1.v_y, gc1.v_z) # doctest: +FLOAT_CMP +30.254684717897074 km / s 171.29916086104885 km / s 18.19390627095307 km / s + +The default parameters for the `~astropy.coordinates.Galactocentric` frame +are detailed in the linked documentation, but we can modify the most commonly +changed values using the keywords ``galcen_distance``, ``galcen_v_sun``, and +``z_sun`` which set the sun-Galactic center distance, the 3D velocity vector +of the sun, and the height of the sun above the Galactic midplane, +respectively. The velocity of the sun can be specified as an +`~astropy.units.Quantity` object with velocity units and is interpreted as a +Cartesian velocity, as in the example below. Note that, as with the positions, +the Galactocentric frame is a right-handed system (i.e., the Sun is at negative +x values) so ``v_x`` is opposite of the Galactocentric radial velocity: + +>>> v_sun = [11.1, 244, 7.25] * (u.km / u.s) # [vx, vy, vz] +>>> gc_frame = coord.Galactocentric( +... galcen_distance=8 * u.kpc, galcen_v_sun=v_sun, z_sun=0 * u.pc +... ) + +We can then transform to this frame instead, with our custom parameters: + +>>> gc2 = c1.transform_to(gc_frame) +>>> print(gc2.v_x, gc2.v_y, gc2.v_z) # doctest: +FLOAT_CMP +28.427958360720748 km / s 169.69916086104888 km / s 17.70831652451455 km / s + +It is sometimes useful to specify the solar motion using the +`proper motion of Sgr A* `_ +instead of Cartesian velocity components. With an assumed distance, we can convert +proper motion components to Cartesian velocity components using `astropy.units`: + +>>> galcen_distance = 8 * u.kpc +>>> pm_gal_sgrA = [-6.379, -0.202] * (u.mas / u.yr) # from Reid & Brunthaler 2004 +>>> vy, vz = -(galcen_distance * pm_gal_sgrA).to(u.km / u.s, u.dimensionless_angles()) + +We still have to assume a line-of-sight velocity for the Galactic center, +which we will again take to be 11 km/s: + +>>> vx = 11.1 * (u.km / u.s) +>>> v_sun2 = u.Quantity([vx, vy, vz]) # List of Quantity -> a single Quantity +>>> gc_frame2 = coord.Galactocentric( +... galcen_distance=galcen_distance, galcen_v_sun=v_sun2, z_sun=0 * u.pc +... ) +>>> gc3 = c1.transform_to(gc_frame2) +>>> print(gc3.v_x, gc3.v_y, gc3.v_z) # doctest: +FLOAT_CMP +28.427958360720748 km / s 167.61484955608267 km / s 18.118916793584443 km / s + +The transformations also work in the opposite direction. This can be useful +for transforming simulated or theoretical data to observable quantities. As +an example, we will generate 4 theoretical circular orbits at different +Galactocentric radii with the same circular velocity, and transform them to +Heliocentric coordinates: + +.. plot:: + :include-source: + + >>> import matplotlib.pyplot as plt + >>> import numpy as np + >>> import astropy.coordinates as coord + >>> from astropy import units as u + >>> ring_distances = np.arange(10, 26, 5) * u.kpc + >>> circ_velocity = 220 * (u.km / u.s) + >>> phi_grid = np.linspace(90, 270, 512) * u.degree # grid of azimuths + >>> ring_rep = coord.CylindricalRepresentation( + ... rho=ring_distances[:, np.newaxis], + ... phi=phi_grid[np.newaxis], + ... z=np.zeros_like(ring_distances)[:, np.newaxis], + ... ) + >>> angular_velocity = (-circ_velocity / ring_distances).to( + ... u.mas / u.yr, u.dimensionless_angles() + ... ) + >>> ring_dif = coord.CylindricalDifferential( + ... d_rho=np.zeros(phi_grid.shape)[np.newaxis] * (u.km / u.s), + ... d_phi=angular_velocity[:, np.newaxis], + ... d_z=np.zeros(phi_grid.shape)[np.newaxis] * (u.km / u.s), + ... ) + >>> ring_rep = ring_rep.with_differentials(ring_dif) + >>> gc_rings = coord.SkyCoord(ring_rep, frame=coord.Galactocentric) + + First, let's visualize the geometry in Galactocentric coordinates. Here are + the positions and velocities of the rings; note that in the velocity plot, + the velocities of the 4 rings are identical and thus overlaid under the same + curve: + + >>> fig, axes = plt.subplots(1, 2, figsize=(12, 6)) + >>> axes[0].plot(gc_rings.x.T, gc_rings.y.T, marker="None", linewidth=3) # doctest: +IGNORE_OUTPUT + >>> axes[0].text(-8.0, 0, r"$\odot$", fontsize=20) # doctest: +IGNORE_OUTPUT + >>> axes[0].set_xlim(-30, 30) # doctest: +IGNORE_OUTPUT + >>> axes[0].set_ylim(-30, 30) # doctest: +IGNORE_OUTPUT + >>> axes[0].set_xlabel("$x$ [kpc]") # doctest: +IGNORE_OUTPUT + >>> axes[0].set_ylabel("$y$ [kpc]") # doctest: +IGNORE_OUTPUT + >>> axes[0].set_title("Positions") # doctest: +IGNORE_OUTPUT + >>> axes[1].plot(gc_rings.v_x.T, gc_rings.v_y.T, marker="None", linewidth=3) # doctest: +IGNORE_OUTPUT + >>> axes[1].set_xlim(-250, 250) # doctest: +IGNORE_OUTPUT + >>> axes[1].set_ylim(-250, 250) # doctest: +IGNORE_OUTPUT + >>> axes[1].set_xlabel(f"$v_x$ [{(u.km / u.s).to_string('latex_inline')}]") # doctest: +IGNORE_OUTPUT + >>> axes[1].set_ylabel(f"$v_y$ [{(u.km / u.s).to_string('latex_inline')}]") # doctest: +IGNORE_OUTPUT + >>> axes[1].set_title("Velocities") # doctest: +IGNORE_OUTPUT + >>> fig.tight_layout() + + Now we can transform to Galactic coordinates and visualize the rings in + observable coordinates: + + >>> gal_rings = gc_rings.transform_to(coord.Galactic) + >>> fig, ax = plt.subplots(1, 1, figsize=(8, 6)) + >>> for i in range(len(ring_distances)): + ... ax.plot( + ... gal_rings[i].l.degree, + ... gal_rings[i].pm_l_cosb.value, + ... label=str(ring_distances[i]), + ... marker="None", + ... linewidth=3, + ... ) # doctest: +IGNORE_OUTPUT + >>> ax.set_xlim(360, 0) # doctest: +IGNORE_OUTPUT + >>> ax.set_xlabel("$l$ [deg]") # doctest: +IGNORE_OUTPUT + >>> ax.set_ylabel(rf'$\mu_l \, \cos b$ [{(u.mas/u.yr).to_string("latex_inline")}]') # doctest: +IGNORE_OUTPUT + >>> ax.legend() # doctest: +IGNORE_OUTPUT + >>> plt.draw() + +.. + EXAMPLE END diff --git a/docs/coordinates/example_gallery_plot_mars_coordinate_frame.rst b/docs/coordinates/example_gallery_plot_mars_coordinate_frame.rst new file mode 100644 index 000000000000..987bb6aa3379 --- /dev/null +++ b/docs/coordinates/example_gallery_plot_mars_coordinate_frame.rst @@ -0,0 +1,115 @@ +.. _sphx_glr_generated_examples_coordinates_plot_mars-coordinate-frame.py: + +Create a new coordinate frame class for Mars +============================================ + +.. + EXAMPLE START + Create a new coordinate frame class for Mars + +This example describes how to subclass and define a custom coordinate frame for a +planetary body which can be described by a geodetic or bodycentric representation, +as discussed in :ref:`astropy:astropy-coordinates-design` and +:ref:`astropy-coordinates-create-geodetic`. + +Note that we use the frame here only to store coordinates. To use it to determine, e.g., +where to point a telescope on Earth to observe Olympus Mons, one would need to add the +frame to the transfer graph, which is beyond the scope of this example. + +To do this, first we need to define a subclass of a +`~astropy.coordinates.BaseGeodeticRepresentation` and +`~astropy.coordinates.BaseBodycentricRepresentation`, then a subclass of +`~astropy.coordinates.BaseCoordinateFrame` using the previous defined +representations. + +.. plot:: + :include-source: + + >>> import matplotlib.pyplot as plt + >>> import numpy as np + >>> from astropy import units as u + >>> from astropy.coordinates.baseframe import BaseCoordinateFrame + >>> from astropy.coordinates.representation import CartesianRepresentation + >>> from astropy.coordinates.representation.geodetic import ( + ... BaseBodycentricRepresentation, + ... BaseGeodeticRepresentation, + ... ) + >>> from astropy.visualization import quantity_support + + The first step is to create a new class, and make it a subclass of + `~astropy.coordinates.BaseGeodeticRepresentation`. + Geodetic latitudes are used and longitudes span from 0 to 360 degrees east positive + It represent a best fit of the Mars spheroid to the martian geoid (areoid): + + >>> class MarsBestFitAeroid(BaseGeodeticRepresentation): + ... """A Spheroidal representation of Mars that minimized deviations with respect to the + ... areoid following + ... Ardalan A. A, R. Karimi, and E. W. Grafarend (2010) + ... https://doi.org/10.1007/s11038-009-9342-7 + ... """ + ... _equatorial_radius = 3395.4280 * u.km + ... _flattening = 0.5227617843759314 * u.percent + + Now let's define a new geodetic representation obtained from MarsBestFitAeroid but + described by planetocentric latitudes: + + >>> class MarsBestFitOcentricAeroid(BaseBodycentricRepresentation): + ... """A Spheroidal planetocentric representation of Mars that minimized deviations with + ... respect to the areoid following + ... Ardalan A. A, R. Karimi, and E. W. Grafarend (2010) + ... https://doi.org/10.1007/s11038-009-9342-7 + ... """ + ... _equatorial_radius = 3395.4280 * u.km + ... _flattening = 0.5227617843759314 * u.percent + + As a comparison we define a new spherical frame representation, we could + have based it on `~astropy.coordinates.BaseBodycentricRepresentation` too: + + >>> class MarsSphere(BaseGeodeticRepresentation): + ... """A Spherical representation of Mars.""" + ... _equatorial_radius = 3395.4280 * u.km + ... _flattening = 0.0 * u.percent + + The new planetary body-fixed reference system will be described using the + previous defined representations: + + >>> class MarsCoordinateFrame(BaseCoordinateFrame): + ... """A reference system for Mars.""" + ... name = "Mars" + + Now we plot the differences between each component of the cartesian + representation with respect to the spherical model, assuming the point on the + surface of the body (``height = 0``): + + >>> mars_sphere = MarsCoordinateFrame( + ... lon=np.linspace(0, 360, 128) * u.deg, + ... lat=np.linspace(-90, 90, 128) * u.deg, + ... representation_type=MarsSphere, + ... ) + >>> mars = MarsCoordinateFrame( + ... lon=np.linspace(0, 360, 128) * u.deg, + ... lat=np.linspace(-90, 90, 128) * u.deg, + ... representation_type=MarsBestFitAeroid, + ... ) + >>> mars_ocentric = MarsCoordinateFrame( + ... lon=np.linspace(0, 360, 128) * u.deg, + ... lat=np.linspace(-90, 90, 128) * u.deg, + ... representation_type=MarsBestFitOcentricAeroid, + ... ) + >>> xyz_sphere = mars_sphere.represent_as(CartesianRepresentation) + >>> xyz = mars.represent_as(CartesianRepresentation) + >>> xyz_ocentric = mars_ocentric.represent_as(CartesianRepresentation) + >>> with quantity_support(): + ... fig, ax = plt.subplots(2, subplot_kw={"projection": "3d"}) + ... ax[0].scatter(*((xyz - xyz_sphere).xyz << u.km)) # doctest: +IGNORE_OUTPUT + ... ax[0].tick_params(labelsize=8) # doctest: +IGNORE_OUTPUT + ... ax[0].set(xlabel="x [km]", ylabel="y [km]", zlabel="z [km]") # doctest: +IGNORE_OUTPUT + ... ax[0].set_title("Mars-odetic spheroid difference from sphere") # doctest: +IGNORE_OUTPUT + ... ax[1].scatter(*((xyz_ocentric - xyz_sphere).xyz << u.km)) # doctest: +IGNORE_OUTPUT + ... ax[1].tick_params(labelsize=8) # doctest: +IGNORE_OUTPUT + ... ax[1].set(xlabel="x [km]", ylabel="y [km]", zlabel="z [km]") # doctest: +IGNORE_OUTPUT + ... ax[1].set_title("Mars-ocentric spheroid difference from sphere") # doctest: +IGNORE_OUTPUT + ... plt.draw() + +.. + EXAMPLE END diff --git a/docs/coordinates/example_gallery_plot_obs_planning.rst b/docs/coordinates/example_gallery_plot_obs_planning.rst new file mode 100644 index 000000000000..fd361fb81cc1 --- /dev/null +++ b/docs/coordinates/example_gallery_plot_obs_planning.rst @@ -0,0 +1,154 @@ +.. _sphx_glr_generated_examples_coordinates_plot_obs-planning.py: + +Determining and plotting the altitude/azimuth of a celestial object +=================================================================== + +.. + EXAMPLE START + Determining and plotting the altitude/azimuth of a celestial object + +This example demonstrates coordinate transformations and the creation of +visibility curves to assist with observing run planning. + +In this example, we make a `~astropy.coordinates.SkyCoord` instance for M33. +The altitude-azimuth coordinates are then found using +`astropy.coordinates.EarthLocation` and `astropy.time.Time` objects. + +This example is meant to demonstrate the capabilities of the +`astropy.coordinates` package. For more convenient and/or complex observation +planning, consider the `astroplan `_ +package. + +Let's suppose you are planning to visit picturesque Bear Mountain State Park +in New York, USA. You are bringing your telescope with you (of course), and +someone told you M33 is a great target to observe there. You happen to know +you are free at 11:00 PM local time, and you want to know if it will be up. +Astropy can answer that. + +.. plot:: + :include-source: + + >>> import matplotlib.pyplot as plt + >>> import numpy as np + >>> from astropy import units as u + >>> from astropy.coordinates import AltAz, EarthLocation, SkyCoord, get_body, get_sun + >>> from astropy.time import Time + >>> from astropy.visualization import quantity_support + + :meth:`astropy.coordinates.SkyCoord.from_name` uses Simbad to resolve object + names and retrieve coordinates. + + Get the coordinates of M33: + + >>> # m33 = SkyCoord.from_name("M33") + >>> m33 = SkyCoord(23.46206906, 30.66017511, unit="deg") + + Use `astropy.coordinates.EarthLocation` to provide the location of Bear + Mountain and set the time to 11pm Eastern Daylight Time (EDT) on 2012 July 12: + + >>> bear_mountain = EarthLocation(lat=41.3 * u.deg, lon=-74 * u.deg, height=390 * u.m) + >>> utcoffset = -4 * u.hour # EDT + >>> time = Time("2012-7-12 23:00:00") - utcoffset + + :meth:`astropy.coordinates.EarthLocation.get_site_names` can be used to get + locations of major observatories. + + Use `astropy.coordinates` to find the Alt, Az coordinates of M33 at as + observed from Bear Mountain at 11pm on 2012 July 12: + + >>> m33altaz = m33.transform_to(AltAz(obstime=time, location=bear_mountain)) + >>> print(f"M33's Altitude = {m33altaz.alt:.2}") + M33's Altitude = 0.13 deg + + This is helpful since it turns out M33 is barely above the horizon at this + time. It is more informative to find M33's airmass over the course of + the night. + + Find the Alt, Az coordinates of M33 at 100 times evenly spaced between 10 PM + and 7 AM EDT: + + >>> midnight = Time("2012-7-13 00:00:00") - utcoffset + >>> delta_midnight = np.linspace(-2, 10, 100) * u.hour + >>> frame_July13night = AltAz(obstime=midnight + delta_midnight, location=bear_mountain) + >>> m33altazs_July13night = m33.transform_to(frame_July13night) + + Convert Alt, Az to airmass with `~astropy.coordinates.AltAz.secz` attribute: + + >>> m33airmasss_July13night = m33altazs_July13night.secz + + Plot the airmass as a function of time: + + >>> with quantity_support(): + ... fig, ax = plt.subplots(1, 1, figsize=(12, 6)) + ... ax.plot(delta_midnight, m33airmasss_July13night) # doctest: +IGNORE_OUTPUT + ... ax.set_xlim(-2, 10) # doctest: +IGNORE_OUTPUT + ... ax.set_ylim(1, 4) # doctest: +IGNORE_OUTPUT + ... ax.set_xlabel("Hours from EDT Midnight") # doctest: +IGNORE_OUTPUT + ... ax.set_ylabel("Airmass [Sec(z)]") # doctest: +IGNORE_OUTPUT + ... plt.draw() + + Use :func:`~astropy.coordinates.get_sun` to find the location of the Sun at 1000 + evenly spaced times between noon on July 12 and noon on July 13: + + >>> delta_midnight = np.linspace(-12, 12, 1000) * u.hour + >>> times_July12_to_13 = midnight + delta_midnight + >>> frame_July12_to_13 = AltAz(obstime=times_July12_to_13, location=bear_mountain) + >>> sunaltazs_July12_to_13 = get_sun(times_July12_to_13).transform_to(frame_July12_to_13) + + Do the same with :func:`~astropy.coordinates.get_body` to find when the moon is + up. Be aware that this will need to download a 10 MB file from the internet + to get a precise location of the moon. + + >>> moon_July12_to_13 = get_body("moon", times_July12_to_13) + >>> moonaltazs_July12_to_13 = moon_July12_to_13.transform_to(frame_July12_to_13) + + Find the Alt, Az coordinates of M33 at those same times: + + >>> m33altazs_July12_to_13 = m33.transform_to(frame_July12_to_13) + + Make a figure illustrating nighttime and the altitudes of M33 and + the Sun over that time: + + >>> with quantity_support(): + ... fig, ax = plt.subplots(1, 1, figsize=(12, 6)) + ... ax.plot(delta_midnight, sunaltazs_July12_to_13.alt, color="r", label="Sun") # doctest: +IGNORE_OUTPUT + ... ax.plot( + ... delta_midnight, moonaltazs_July12_to_13.alt, color=[0.75] * 3, ls="--", label="Moon" + ... ) # doctest: +IGNORE_OUTPUT + ... mappable = ax.scatter( + ... delta_midnight, + ... m33altazs_July12_to_13.alt, + ... c=m33altazs_July12_to_13.az.value, + ... label="M33", + ... lw=0, + ... s=8, + ... cmap="viridis", + ... ) + ... ax.fill_between( + ... delta_midnight, + ... 0 * u.deg, + ... 90 * u.deg, + ... sunaltazs_July12_to_13.alt < (-0 * u.deg), + ... color="0.5", + ... zorder=0, + ... ) # doctest: +IGNORE_OUTPUT + ... ax.fill_between( + ... delta_midnight, + ... 0 * u.deg, + ... 90 * u.deg, + ... sunaltazs_July12_to_13.alt < (-18 * u.deg), + ... color="k", + ... zorder=0, + ... ) # doctest: +IGNORE_OUTPUT + ... fig.colorbar(mappable).set_label("Azimuth [deg]") # doctest: +IGNORE_OUTPUT + ... ax.legend(loc="upper left") # doctest: +IGNORE_OUTPUT + ... ax.set_xlim(-12 * u.hour, 12 * u.hour) # doctest: +IGNORE_OUTPUT + ... ax.set_xticks((np.arange(13) * 2 - 12) * u.hour) # doctest: +IGNORE_OUTPUT + ... ax.set_ylim(0 * u.deg, 90 * u.deg) # doctest: +IGNORE_OUTPUT + ... ax.set_xlabel("Hours from EDT Midnight") # doctest: +IGNORE_OUTPUT + ... ax.set_ylabel("Altitude [deg]") # doctest: +IGNORE_OUTPUT + ... ax.grid(visible=True) # doctest: +IGNORE_OUTPUT + ... plt.draw() + +.. + EXAMPLE END diff --git a/docs/coordinates/example_gallery_plot_sgr_coordinate_frame.rst b/docs/coordinates/example_gallery_plot_sgr_coordinate_frame.rst new file mode 100644 index 000000000000..5f752001ba84 --- /dev/null +++ b/docs/coordinates/example_gallery_plot_sgr_coordinate_frame.rst @@ -0,0 +1,237 @@ +.. _sphx_glr_generated_examples_coordinates_plot_sgr-coordinate-frame.py: + +Create a new coordinate class (for the Sagittarius stream) +========================================================== + +.. + EXAMPLE START + Create a new coordinate class (for the Sagittarius stream) + +This document describes in detail how to subclass and define a custom spherical +coordinate frame, as discussed in :ref:`astropy:astropy-coordinates-design` and +the docstring for `~astropy.coordinates.BaseCoordinateFrame`. In this example, +we will define a coordinate system defined by the plane of orbit of the +Sagittarius Dwarf Galaxy (hereafter Sgr; as defined in Majewski et al. 2003). +The Sgr coordinate system is often referred to in terms of two angular +coordinates, :math:`\Lambda,B`. + +To do this, we need to define a subclass of +`~astropy.coordinates.BaseCoordinateFrame` that knows the names and units of the +coordinate system angles in each of the supported representations. In this case +we support `~astropy.coordinates.SphericalRepresentation` with "Lambda" and +"Beta". Then we have to define the transformation from this coordinate system to +some other built-in system. Here we will use Galactic coordinates, represented +by the `~astropy.coordinates.Galactic` class. + +.. seealso:: + + The `gala package `_ + Defines a number of Astropy coordinate frames for + stellar stream coordinate systems. + + Majewski et al. 2003 + "A Two Micron All Sky Survey View of the Sagittarius + Dwarf Galaxy. I. Morphology of the Sagittarius Core and Tidal Arms", + https://arxiv.org/abs/astro-ph/0304198 + + Law & Majewski 2010 + "The Sagittarius Dwarf Galaxy: A Model for Evolution in a + Triaxial Milky Way Halo", https://arxiv.org/abs/1003.1132 + + David Law's Sgr info page + https://www.stsci.edu/~dlaw/Sgr/ + +.. plot:: + :include-source: + + >>> import matplotlib.pyplot as plt + >>> import numpy as np + >>> import astropy.coordinates as coord + >>> from astropy import units as u + >>> from astropy.coordinates import frame_transform_graph, rotation_matrix + + The first step is to create a new class, which we'll call + ``Sagittarius`` and make it a subclass of + `~astropy.coordinates.BaseCoordinateFrame`: + + >>> class Sagittarius(coord.BaseCoordinateFrame): + ... """A Heliocentric spherical coordinate system defined by the orbit + ... of the Sagittarius dwarf galaxy, as described in + ... https://ui.adsabs.harvard.edu/abs/2003ApJ...599.1082M + ... and further explained in + ... https://www.stsci.edu/~dlaw/Sgr/. + ... + ... Parameters + ... ---------- + ... representation : `~astropy.coordinates.BaseRepresentation` or None + ... A representation object or None to have no data (or use the other keywords) + ... Lambda : `~astropy.coordinates.Angle`, optional, must be keyword + ... The longitude-like angle corresponding to Sagittarius' orbit. + ... Beta : `~astropy.coordinates.Angle`, optional, must be keyword + ... The latitude-like angle corresponding to Sagittarius' orbit. + ... distance : `~astropy.units.Quantity`, optional, must be keyword + ... The Distance for this object along the line-of-sight. + ... pm_Lambda_cosBeta : `~astropy.units.Quantity`, optional, must be keyword + ... The proper motion along the stream in ``Lambda`` (including the + ... ``cos(Beta)`` factor) for this object (``pm_Beta`` must also be given). + ... pm_Beta : `~astropy.units.Quantity`, optional, must be keyword + ... The proper motion in Declination for this object (``pm_ra_cosdec`` must + ... also be given). + ... radial_velocity : `~astropy.units.Quantity`, optional, keyword-only + ... The radial velocity of this object. + ... """ + ... default_representation = coord.SphericalRepresentation + ... default_differential = coord.SphericalCosLatDifferential + ... frame_specific_representation_info = { + ... coord.SphericalRepresentation: [ + ... coord.RepresentationMapping("lon", "Lambda"), + ... coord.RepresentationMapping("lat", "Beta"), + ... coord.RepresentationMapping("distance", "distance"), + ... ] + ... } + + Breaking this down line-by-line, we define the class as a subclass of + `~astropy.coordinates.BaseCoordinateFrame`. Then we include a descriptive + docstring. The final lines are class-level attributes that specify the + default representation for the data, default differential for the velocity + information, and mappings from the attribute names used by representation + objects to the names that are to be used by the ``Sagittarius`` frame. In this + case we override the names in the spherical representations but do not do + anything with other representations like cartesian or cylindrical. + + Next we have to define the transformation from this coordinate system to some + other built-in coordinate system; we will use Galactic coordinates. We can do + this by defining functions that return transformation matrices, or by simply + defining a function that accepts a coordinate and returns a new coordinate in + the new system. Because the transformation to the Sagittarius coordinate + stem is just a spherical rotation from Galactic coordinates, we will + define a function that returns this matrix. We will start by constructing the + transformation matrix using pre-determined Euler angles and the + ``rotation_matrix`` helper function: + + >>> SGR_PHI = (180 + 3.75) * u.degree # Euler angles (from Law & Majewski 2010) + >>> SGR_THETA = (90 - 13.46) * u.degree + >>> SGR_PSI = (180 + 14.111534) * u.degree + + Generate the rotation matrix using the x-convention (see Goldstein): + + >>> SGR_MATRIX = ( + ... np.diag([1.0, 1.0, -1.0]) + ... @ rotation_matrix(SGR_PSI, "z") + ... @ rotation_matrix(SGR_THETA, "x") + ... @ rotation_matrix(SGR_PHI, "z") + ... ) + + Since we already constructed the transformation (rotation) matrix above, and + the inverse of a rotation matrix is just its transpose, the required + transformation functions are very simple: + + >>> @frame_transform_graph.transform( + ... coord.StaticMatrixTransform, coord.Galactic, Sagittarius + ... ) + ... def galactic_to_sgr(): + ... """Compute the Galactic spherical to heliocentric Sgr transformation matrix.""" + ... return SGR_MATRIX + + The decorator ``@frame_transform_graph.transform(coord.StaticMatrixTransform, coord.Galactic, Sagittarius)`` + registers this function on the + ``frame_transform_graph`` as a coordinate transformation. Inside the function, + we return the previously defined rotation matrix. + + We then register the inverse transformation by using the transpose of the + rotation matrix (which is faster to compute than the inverse): + + >>> @frame_transform_graph.transform( + ... coord.StaticMatrixTransform, Sagittarius, coord.Galactic + ... ) + ... def sgr_to_galactic(): + ... """Compute the heliocentric Sgr to spherical Galactic transformation matrix.""" + ... return SGR_MATRIX.swapaxes(-2, -1) + + Now that we have registered these transformations between ``Sagittarius`` and + `~astropy.coordinates.Galactic`, we can transform between *any* coordinate + system and ``Sagittarius`` (as long as the other system has a path to + transform to `~astropy.coordinates.Galactic`). For example, to transform from + ICRS coordinates to ``Sagittarius``, we would do: + + >>> icrs = coord.SkyCoord(280.161732 * u.degree, 11.91934 * u.degree, frame="icrs") + >>> sgr = icrs.transform_to(Sagittarius) + >>> print(sgr) + + + Or, to transform from the ``Sagittarius`` frame to ICRS coordinates (in this + case, a line along the ``Sagittarius`` x-y plane): + + >>> sgr = coord.SkyCoord( + ... Lambda=np.linspace(0, 2 * np.pi, 128) * u.radian, + ... Beta=np.zeros(128) * u.radian, + ... frame="sagittarius", + ... ) + >>> icrs = sgr.transform_to(coord.ICRS) + >>> print(icrs) # doctest: +ELLIPSIS + + + As an example, we will now plot the points in both coordinate systems: + + >>> fig, axes = plt.subplots(2, 1, figsize=(8, 10), subplot_kw={"projection": "aitoff"}) + >>> axes[0].set_title("Sagittarius") # doctest: +IGNORE_OUTPUT + >>> axes[0].plot( + ... sgr.Lambda.wrap_at(180 * u.deg).radian, + ... sgr.Beta.radian, + ... linestyle="none", + ... marker=".", + ... ) # doctest: +IGNORE_OUTPUT + >>> axes[0].grid(visible=True) # doctest: +IGNORE_OUTPUT + >>> axes[1].set_title("ICRS") # doctest: +IGNORE_OUTPUT + >>> axes[1].plot( + ... icrs.ra.wrap_at(180 * u.deg).radian, icrs.dec.radian, linestyle="none", marker="." + ... ) # doctest: +IGNORE_OUTPUT + >>> axes[1].grid(visible=True) # doctest: +IGNORE_OUTPUT + + This particular transformation is just a spherical rotation, which is a + special case of an Affine transformation with no vector offset. The + transformation of velocity components is therefore natively supported as + well: + + >>> sgr = coord.SkyCoord( + ... Lambda=np.linspace(0, 2 * np.pi, 128) * u.radian, + ... Beta=np.zeros(128) * u.radian, + ... pm_Lambda_cosBeta=np.random.uniform(-5, 5, 128) * (u.mas / u.yr), + ... pm_Beta=np.zeros(128) * (u.mas / u.yr), + ... frame="sagittarius", + ... ) + >>> icrs = sgr.transform_to(coord.ICRS) + >>> print(icrs) # doctest: +ELLIPSIS + + >>> fig, axes = plt.subplots(3, 1, figsize=(8, 10), sharex=True) + >>> axes[0].set_title("Sagittarius") # doctest: +IGNORE_OUTPUT + >>> axes[0].plot( + ... sgr.Lambda.degree, sgr.pm_Lambda_cosBeta.value, linestyle="none", marker="." + ... ) # doctest: +IGNORE_OUTPUT + >>> axes[0].set_xlabel(r"$\Lambda$ [deg]") # doctest: +IGNORE_OUTPUT + >>> axes[0].set_ylabel( + ... rf"$\mu_\Lambda \, \cos B$ [{sgr.pm_Lambda_cosBeta.unit.to_string('latex_inline')}]" + ... ) # doctest: +IGNORE_OUTPUT + >>> axes[0].grid(visible=True) # doctest: +IGNORE_OUTPUT + >>> axes[1].set_title("ICRS") # doctest: +IGNORE_OUTPUT + >>> axes[1].plot(icrs.ra.degree, icrs.pm_ra_cosdec.value, linestyle="none", marker=".") # doctest: +IGNORE_OUTPUT + >>> axes[1].set_ylabel( + ... rf"$\mu_\alpha \, \cos\delta$ [{icrs.pm_ra_cosdec.unit.to_string('latex_inline')}]" + ... ) # doctest: +IGNORE_OUTPUT + >>> axes[1].grid(visible=True) # doctest: +IGNORE_OUTPUT + >>> axes[2].set_title("ICRS") # doctest: +IGNORE_OUTPUT + >>> axes[2].plot(icrs.ra.degree, icrs.pm_dec.value, linestyle="none", marker=".") # doctest: +IGNORE_OUTPUT + >>> axes[2].set_xlabel("RA [deg]") # doctest: +IGNORE_OUTPUT + >>> axes[2].set_ylabel(rf"$\mu_\delta$ [{icrs.pm_dec.unit.to_string('latex_inline')}]") # doctest: +IGNORE_OUTPUT + >>> axes[2].grid(visible=True) # doctest: +IGNORE_OUTPUT + >>> plt.draw() + +.. + EXAMPLE END diff --git a/docs/coordinates/example_gallery_rv_to_gsr.rst b/docs/coordinates/example_gallery_rv_to_gsr.rst new file mode 100644 index 000000000000..1669cb874d65 --- /dev/null +++ b/docs/coordinates/example_gallery_rv_to_gsr.rst @@ -0,0 +1,103 @@ +.. _sphx_glr_generated_examples_coordinates_rv-to-gsr.py: + +Convert a radial velocity to the Galactic Standard of Rest (GSR) +================================================================ + +.. + EXAMPLE START + Convert a radial velocity to the Galactic Standard of Rest (GSR) + +Radial or line-of-sight velocities of sources are often reported in a +Heliocentric or Solar-system barycentric reference frame. A common +transformation incorporates the projection of the Sun's motion along the +line-of-sight to the target, hence transforming it to a Galactic rest frame +instead (sometimes referred to as the Galactic Standard of Rest, GSR). This +transformation depends on the assumptions about the orientation of the Galactic +frame relative to the bary- or Heliocentric frame. It also depends on the +assumed solar velocity vector. Here we will demonstrate how to perform this +transformation using a sky position and barycentric radial-velocity. + +Use the latest convention for the Galactocentric coordinates: + +>>> import astropy.coordinates as coord +>>> coord.galactocentric_frame_defaults.set("latest") # doctest: +IGNORE_OUTPUT + +For this example, let's work with the coordinates and barycentric radial +velocity of the star HD 155967, as obtained from +`Simbad `_: + +>>> from astropy import units as u +>>> icrs = coord.SkyCoord( +... ra=258.58356362 * u.deg, +... dec=14.55255619 * u.deg, +... radial_velocity=-16.1 * u.km / u.s, +... frame="icrs", +... ) + +Next, we need to decide on the velocity of the Sun in the assumed GSR frame. +We will use the same velocity vector as used in the +`~astropy.coordinates.Galactocentric` frame, and convert it to a +`~astropy.coordinates.CartesianRepresentation` object using the +``.to_cartesian()`` method of the +`~astropy.coordinates.CartesianDifferential` object ``galcen_v_sun``: + +>>> v_sun = coord.Galactocentric().galcen_v_sun.to_cartesian() + +We now need to get a unit vector in the assumed Galactic frame from the sky +position in the ICRS frame above. We will use this unit vector to project the +solar velocity onto the line-of-sight: + +>>> gal = icrs.transform_to(coord.Galactic) +>>> cart_data = gal.data.to_cartesian() +>>> unit_vector = cart_data / cart_data.norm() + +Now we project the solar velocity using this unit vector: + +>>> v_proj = v_sun.dot(unit_vector) + +Finally, we add the projection of the solar velocity to the radial velocity +to get a GSR radial velocity: + +>>> rv_gsr = icrs.radial_velocity + v_proj +>>> print(rv_gsr) # doctest: +FLOAT_CMP +123.30460087379765 km / s + +We could wrap this in a function so we can control the solar velocity and +reuse the above code: + +>>> def rv_to_gsr(c, v_sun=None): +... """Transform a barycentric radial velocity to the Galactic Standard of Rest +... (GSR). +... +... Parameters +... ---------- +... c : `~astropy.coordinates.BaseCoordinateFrame` subclass instance +... The radial velocity, associated with a sky coordinates, to be +... transformed. +... v_sun : `~astropy.units.Quantity`, optional +... The 3D velocity of the solar system barycenter in the GSR frame. +... Defaults to the same solar motion as in the +... `~astropy.coordinates.Galactocentric` frame. +... +... Returns +... ------- +... v_gsr : `~astropy.units.Quantity` +... The input radial velocity transformed to a GSR frame. +... """ +... if v_sun is None: +... v_sun = coord.Galactocentric().galcen_v_sun.to_cartesian() +... +... gal = c.transform_to(coord.Galactic) +... cart_data = gal.data.to_cartesian() +... unit_vector = cart_data / cart_data.norm() +... +... v_proj = v_sun.dot(unit_vector) +... +... return c.radial_velocity + v_proj + +>>> rv_gsr = rv_to_gsr(icrs) +>>> print(rv_gsr) # doctest: +FLOAT_CMP +123.30460087379765 km / s + +.. + EXAMPLE END diff --git a/docs/coordinates/formatting.rst b/docs/coordinates/formatting.rst new file mode 100644 index 000000000000..076e0f22c88c --- /dev/null +++ b/docs/coordinates/formatting.rst @@ -0,0 +1,47 @@ +Formatting Coordinate Strings +***************************** + +.. todo: @taldcroft should change this to start with a discussion of SkyCoord's capabilities + +Getting a string representation of a coordinate is most powerfully +approached by treating the components (e.g., RA and Dec) separately. + +Examples +-------- + +.. + EXAMPLE START + Getting and Formatting String Representations of Coordinates + +To get the string representation of a coordinate:: + + >>> from astropy.coordinates import ICRS + >>> from astropy import units as u + >>> coo = ICRS(187.70592*u.degree, 12.39112*u.degree) + >>> str(coo.ra) + ' ' + str(coo.dec) + '187d42m21.312s 12d23m28.032s' + +To get better control over the formatting, you can use the angles' +:meth:`~astropy.coordinates.Angle.to_string` method (see :doc:`angles` for +more). For example:: + + >>> rahmsstr = coo.ra.to_string(u.hour) + >>> str(rahmsstr) + '12h30m49.4208s' + >>> decdmsstr = coo.dec.to_string(u.degree, alwayssign=True) + >>> str(decdmsstr) + '+12d23m28.032s' + >>> rahmsstr + ' ' + decdmsstr + u'12h30m49.4208s +12d23m28.032s' + +You can also use Python's `format` string method to create more complex +string expressions, such as IAU-style coordinates or even full sentences:: + + >>> (f'SDSS J{coo.ra.to_string(unit=u.hourangle, sep="", precision=2, pad=True)}' + ... f'{coo.dec.to_string(sep="", precision=2, alwayssign=True, pad=True)}') + 'SDSS J123049.42+122328.03' + >>> f'The galaxy M87, at an RA of {coo.ra.hour:.2f} hours and Dec of {coo.dec.deg:.1f} degrees, has an impressive jet.' + 'The galaxy M87, at an RA of 12.51 hours and Dec of 12.4 degrees, has an impressive jet.' + +.. + EXAMPLE END diff --git a/docs/coordinates/frames.rst b/docs/coordinates/frames.rst new file mode 100644 index 000000000000..9ab579b04f3a --- /dev/null +++ b/docs/coordinates/frames.rst @@ -0,0 +1,706 @@ +.. testsetup:: + >>> from astropy.coordinates.sites import _GREENWICH as greenwich + +Using and Designing Coordinate Frames +************************************* + +In `astropy.coordinates`, as outlined in the +:ref:`astropy-coordinates-overview`, subclasses of |BaseFrame| ("frame +classes") define particular coordinate frames. They can (but do not +*have* to) contain representation objects storing the actual coordinate +data. The actual coordinate transformations are defined as functions +that transform representations between frame classes. This approach +serves to separate high-level user functionality (see :doc:`skycoord`) +and details of how the coordinates are actually stored (see +:doc:`representations`) from the definition of frames and how they are +transformed. + +Using Frame Objects +=================== + +Frames without Data +------------------- + +Frame objects have two distinct (but related) uses. The first is +storing the information needed to uniquely define a frame (e.g., +equinox, observation time). This information is stored on the frame +objects as (read-only) Python attributes, which are set when the object +is first created:: + + >>> from astropy.coordinates import ICRS, FK5 + >>> FK5(equinox='J1975') + + >>> ICRS() # has no attributes + + >>> FK5() # uses default equinox + + +The specific names of attributes available for a particular frame are available +as the keys of the ``frame_attributes`` dictionary:: + + >>> FK5.frame_attributes.keys() + dict_keys(['equinox']) + +The defaults of the frame attributes are available as: +:meth:`~astropy.coordinates.BaseCoordinateFrame.get_frame_attr_defaults`:: + + >>> FK5.get_frame_attr_defaults() + {'equinox': " + +If your frame has this class as an attribute:: + + >>> from astropy.coordinates import Attribute + >>> class Egg(BaseCoordinateFrame): + ... can = Attribute(default=Spam()) + +When it is displayed by the frame it will use the result of +``_astropy_repr_in_frame``:: + + >>> Egg() + )> + +.. + EXAMPLE END + +Defining Transformations between Frames +--------------------------------------- + +As indicated by the point (3) in the :ref:`introduction above +`, a frame class on its own is likely not very +useful until transformations are defined between it and other coordinate frame +classes. The key concept for defining transformations in ``astropy.coordinates`` +is the "frame transform graph" (in the "graph theory" sense, not "plot"), which +stores all of the transformations between the built-in frames, as well as tools +for finding the shortest paths through this graph to transform from any frame to +any other by composing the transformations. The power behind this concept is +available to user-created frames as well, meaning that once you define even one +transform from your frame to any frame in the graph, coordinates defined in your +frame can be transformed to *any* other frame in the graph. The "frame transform +graph" is available in code as ``astropy.coordinates.frame_transform_graph``, +which is an instance of the `~astropy.coordinates.TransformGraph` class. + +The transformations themselves are represented as +`~astropy.coordinates.CoordinateTransform` objects or their subclasses. The +useful subclasses/types of transformations are: + +* `~astropy.coordinates.FunctionTransform` + + A transform that is defined as a function that takes a frame object + of one frame class and returns an object of another class. + +* `~astropy.coordinates.AffineTransform` + + A transformation that includes a linear matrix operation and a translation + (vector offset). These transformations are defined by a 3x3 matrix and a + 3-vector for the offset (supplied as a Cartesian representation). The + transformation is applied to the Cartesian representation of one frame and + transforms into the Cartesian representation of the target frame. + +* `~astropy.coordinates.StaticMatrixTransform` +* `~astropy.coordinates.DynamicMatrixTransform` + + The matrix transforms are `~astropy.coordinates.AffineTransform` + transformations without a translation (i.e., only a rotation). The static + version is for the case where the matrix is independent of the frame + attributes (e.g., the ICRS->FK5 transformation, because ICRS has no frame + attributes). The dynamic case is for transformations where the + transformation matrix depends on the frame attributes of either the + to or from frame. + +Generally, it is not necessary to use these classes directly. Instead, +use methods on the ``frame_transform_graph`` that can be used as function +decorators. Define functions that either do the actual +transformation (for `~astropy.coordinates.FunctionTransform`), or that compute +the necessary transformation matrices to transform. Then decorate the functions +to register these transformations with the frame transform graph:: + + from astropy.coordinates import frame_transform_graph + + @frame_transform_graph.transform(DynamicMatrixTransform, ICRS, FK5) + def icrs_to_fk5(icrscoord, fk5frame): + ... + + @frame_transform_graph.transform(DynamicMatrixTransform, FK5, ICRS) + def fk5_to_icrs(fk5coord, icrsframe): + ... + +If the transformation to your coordinate frame of interest is not +representable by a matrix operation, you can also specify a function to do +the actual transformation, and pass the +`~astropy.coordinates.FunctionTransform` class to the transform graph +decorator instead:: + + @frame_transform_graph.transform(FunctionTransform, FK4NoETerms, FK4) + def fk4_no_e_to_fk4(fk4noecoord, fk4frame): + ... + +Furthermore, the ``frame_transform_graph`` does some caching and +optimization to speed up transformations after the first attempt to go +from one frame to another, and shortcuts steps where relevant (for +example, combining multiple static matrix transforms into a single +matrix). Hence, in general, it is better to define whatever are the +most natural transformations for a user-defined frame, rather than +worrying about optimizing or caching a transformation to speed up the +process. + +For a demonstration of how to define transformation functions that also work for +transforming velocity components, see +:ref:`astropy-coordinate-transform-with-velocities`. + + +Supporting Velocity Data in Frames +---------------------------------- + +As alluded to by point (4) in the :ref:`introduction above +`, the examples we have seen above mostly deal with +customizing frame behavior for positional information. (For some context about +how velocities are handled in ``astropy.coordinates``, it may be useful to read +the overview: :ref:`astropy-coordinate-custom-frame-with-velocities`.) + +When defining a frame class, it is also possible to set a +``default_differential`` (analogous to ``default_representation``), and to +customize how velocity data components are named. Expanding on our custom frame +example above, we can use `~astropy.coordinates.RepresentationMapping` to +override ``Differential`` component names. The default ``Differential`` +components are typically named after the corresponding ``Representation`` +component, preceded by ``d_``. So, for example, the longitude ``Differential`` +component is, by default, ``d_lon``. However, there are some defaults to be +aware of. Here, if we set the default ``Differential`` class to also be +Spherical, it will implement a set of default "nicer" names for the velocity +components, mapping ``pm_R`` to ``d_lon``, ``pm_D`` to ``d_lat``, and +``radial_velocity`` to ``d_distance`` (taking the previously overridden +longitude and latitude component names):: + + >>> class MyFrame4WithVelocity(BaseCoordinateFrame): + ... # Specify how coordinate values are represented when outputted + ... default_representation = r.SphericalRepresentation + ... default_differential = r.SphericalDifferential + ... + ... # Override component names (e.g., "ra" instead of "lon") + ... frame_specific_representation_info = { + ... r.SphericalRepresentation: [RepresentationMapping('lon', 'R'), + ... RepresentationMapping('lat', 'D')], + ... r.CartesianRepresentation: [RepresentationMapping('x', 'a'), + ... RepresentationMapping('y', 'b'), + ... RepresentationMapping('z', 'c')] + ... } + >>> fr = MyFrame4WithVelocity(R=1*u.deg, D=2*u.deg, + ... pm_R=3*u.mas/u.yr, pm_D=4*u.mas/u.yr) + >>> fr # doctest: +FLOAT_CMP + + +If you want to override the default "nicer" names, you can specify a new key in +the ``frame_specific_representation_info`` for any of the ``Differential`` +classes, for example:: + + >>> class MyFrame4WithVelocity2(BaseCoordinateFrame): + ... # Specify how coordinate values are represented when outputted + ... default_representation = r.SphericalRepresentation + ... default_differential = r.SphericalDifferential + ... + ... # Override component names (e.g., "ra" instead of "lon") + ... frame_specific_representation_info = { + ... r.SphericalRepresentation: [RepresentationMapping('lon', 'R'), + ... RepresentationMapping('lat', 'D')], + ... r.CartesianRepresentation: [RepresentationMapping('x', 'a'), + ... RepresentationMapping('y', 'b'), + ... RepresentationMapping('z', 'c')], + ... r.SphericalDifferential: [RepresentationMapping('d_lon', 'pm1'), + ... RepresentationMapping('d_lat', 'pm2'), + ... RepresentationMapping('d_distance', 'rv')] + ... } + >>> fr = MyFrame4WithVelocity2(R=1*u.deg, D=2*u.deg, + ... pm1=3*u.mas/u.yr, pm2=4*u.mas/u.yr) + >>> fr # doctest: +FLOAT_CMP + + + +Final Notes +----------- + +You can also define arbitrary methods for any added functionality you +want your frame to have that is unique to that frame. These methods will +be available in any |SkyCoord| that is created using your user-defined +frame. + +For examples of defining frame classes, the first place to look is +at the source code for the frames that are included in ``astropy`` +(available at ``astropy.coordinates.builtin_frames``). These are not +special-cased, but rather use all of the same API and features available to +user-created frames. + +.. topic:: Examples: + + See also :ref:`sphx_glr_generated_examples_coordinates_plot_sgr-coordinate-frame.py` + and :ref:`sphx_glr_generated_examples_coordinates_plot_mars-coordinate-frame.py` + for a more annotated example of defining a new coordinate frame. diff --git a/docs/coordinates/galactocentric.rst b/docs/coordinates/galactocentric.rst new file mode 100644 index 000000000000..27741df32153 --- /dev/null +++ b/docs/coordinates/galactocentric.rst @@ -0,0 +1,262 @@ +.. _coordinates-galactocentric: + +************************************************** +Description of the Galactocentric Coordinate Frame +************************************************** + +While many other frames implemented in `astropy.coordinates` are standardized in +some way (e.g., defined by the IAU), there is no standard Milky Way +reference frame with the center of the Milky Way as its origin. (This is +distinct from `~astropy.coordinates.Galactic` coordinates, which point +toward the Galactic Center but have their origin in the Solar System). +The `~astropy.coordinates.Galactocentric` frame +class is meant to be flexible enough to support all common definitions of such a +transformation, but with reasonable default parameter values, such as the solar +velocity relative to the Galactic center, the solar height above the Galactic +midplane, etc. Below, :ref:`we describe our generalized definition of the +transformation ` from the +ICRS to/from Galactocentric coordinates, and :ref:`describe how to customize the +default Galactocentric parameters +` that are used when the +`~astropy.coordinates.Galactocentric` frame is initialized without explicitly +passing in parameter values. + + +.. _astropy-coordinates-galactocentric-transformation: + +Definition of the Transformation +================================ + +This document describes the mathematics behind the transformation from +`~astropy.coordinates.ICRS` to `~astropy.coordinates.Galactocentric` +coordinates. This is described in detail here on account of the mathematical +subtleties and the fact that there is no official standard/definition for this +frame. For examples of how to use this transformation in code, see the +the *Examples* section of the `~astropy.coordinates.Galactocentric` class +documentation. + +We assume that we start with a 3D position in the ICRS reference frame: +a Right Ascension, Declination, and heliocentric distance, +:math:`(\alpha, \delta, d)`. We can convert this to a Cartesian position using +the standard transformation from Cartesian to spherical coordinates: + +.. math:: + + \begin{aligned} + x_{\rm icrs} &= d\cos{\alpha}\cos{\delta}\\ + y_{\rm icrs} &= d\sin{\alpha}\cos{\delta}\\ + z_{\rm icrs} &= d\sin{\delta}\\ + \boldsymbol{r}_{\rm icrs} &= \begin{pmatrix} + x_{\rm icrs}\\ + y_{\rm icrs}\\ + z_{\rm icrs} + \end{pmatrix}\end{aligned} + +The first transformation rotates the :math:`x_{\rm icrs}` axis so that the new +:math:`x'` axis points towards the Galactic Center (GC), specified by the ICRS +position :math:`(\alpha_{\rm GC}, \delta_{\rm GC})` (in the +`~astropy.coordinates.Galactocentric` frame, this is controlled by the frame +attribute ``galcen_coord``): + +.. math:: + + \begin{aligned} + \boldsymbol{R}_1 &= \begin{bmatrix} + \cos\delta_{\rm GC}& 0 & \sin\delta_{\rm GC}\\ + 0 & 1 & 0 \\ + -\sin\delta_{\rm GC}& 0 & \cos\delta_{\rm GC}\end{bmatrix}\\ + \boldsymbol{R}_2 &= + \begin{bmatrix} + \cos\alpha_{\rm GC}& \sin\alpha_{\rm GC}& 0\\ + -\sin\alpha_{\rm GC}& \cos\alpha_{\rm GC}& 0\\ + 0 & 0 & 1 + \end{bmatrix}.\end{aligned} + +The transformation thus far has aligned the :math:`x'` axis with the +vector pointing from the Sun to the GC, but the :math:`y'` and +:math:`z'` axes point in arbitrary directions. We adopt the +orientation of the Galactic plane as the normal to the north pole of +Galactic coordinates defined by the IAU +(`Blaauw et. al. 1960 `_). +This extra “roll” angle, :math:`\eta`, was measured by transforming a grid +of points along :math:`l=0` to this interim frame and minimizing the square +of their :math:`y'` positions. We find: + +.. math:: + + \begin{aligned} + \eta &= 58.5986320306^\circ\\ + \boldsymbol{R}_3 &= + \begin{bmatrix} + 1 & 0 & 0\\ + 0 & \cos\eta & \sin\eta\\ + 0 & -\sin\eta & \cos\eta + \end{bmatrix}\end{aligned} + +The full rotation matrix thus far is: + +.. math:: + + \begin{gathered} + \boldsymbol{R} = \boldsymbol{R}_3 \boldsymbol{R}_1 \boldsymbol{R}_2 = \\ + \begin{bmatrix} + \cos\alpha_{\rm GC}\cos\delta_{\rm GC}& \cos\delta_{\rm GC}\sin\alpha_{\rm GC}& \sin\delta_{\rm GC}\\ + -\cos\alpha_{\rm GC}\sin\delta_{\rm GC}\sin\eta - \sin\alpha_{\rm GC}\cos\eta & -\sin\alpha_{\rm GC}\sin\delta_{\rm GC}\sin\eta + \cos\alpha_{\rm GC}\cos\eta & \cos\delta_{\rm GC}\sin\eta\\ + -\cos\alpha_{\rm GC}\sin\delta_{\rm GC}\cos\eta + \sin\alpha_{\rm GC}\sin\eta & -\sin\alpha_{\rm GC}\sin\delta_{\rm GC}\cos\eta - \cos\alpha_{\rm GC}\sin\eta & \cos\delta_{\rm GC}\cos\eta + \end{bmatrix}\end{gathered} + +With the rotated position vector +:math:`\boldsymbol{R}\boldsymbol{r}_{\rm icrs}`, we can now subtract the +distance to the GC, :math:`d_{\rm GC}`, which is purely along the +:math:`x'` axis: + +.. math:: + + \begin{aligned} + \boldsymbol{r}' &= \boldsymbol{R}\boldsymbol{r}_{\rm icrs} - d_{\rm GC}\hat{\boldsymbol{x}}_{\rm GC}.\end{aligned} + +where :math:`\hat{\boldsymbol{x}}_{\rm GC} = (1,0,0)^{\mathsf{T}}`. + +The final transformation accounts for the (specified) height of the Sun above +the Galactic midplane by rotating about the final :math:`y''` axis by +the angle :math:`\theta= \sin^{-1}(z_\odot / d_{\rm GC})`: + +.. math:: + + \begin{aligned} + \boldsymbol{H} &= + \begin{bmatrix} + \cos\theta & 0 & \sin\theta\\ + 0 & 1 & 0\\ + -\sin\theta & 0 & \cos\theta + \end{bmatrix}\end{aligned} + +where :math:`z_\odot` is the measured height of the Sun above the +midplane. + +The full transformation is then: + +.. math:: \boldsymbol{r}_{\rm GC} = \boldsymbol{H} \left( \boldsymbol{R}\boldsymbol{r}_{\rm icrs} - d_{\rm GC}\hat{\boldsymbol{x}}_{\rm GC}\right). + +.. topic:: Examples: + + For an example of how to use the `~astropy.coordinates.Galactocentric` + frame, see + :ref:`sphx_glr_generated_examples_coordinates_plot_galactocentric-frame.py`. + + +.. _astropy-coordinates-galactocentric-defaults: + +Controlling the Default Frame Parameters +======================================== + +All of the frame-defining parameters of the +`~astropy.coordinates.Galactocentric` frame are customizable and can be set by +passing arguments in to the `~astropy.coordinates.Galactocentric` initializer. +However, it is often convenient to use the frame without having to pass in every +parameter. Hence, the class comes with reasonable default values for these +parameters, but more precise measurements of the solar position or motion in the +Galaxy are constantly being made. The default values of the +`~astropy.coordinates.Galactocentric` frame attributes will therefore be updated +as necessary with subsequent releases of ``astropy``. We therefore provide a +mechanism to globally or locally control the default parameter values used in +this frame through the `~astropy.coordinates.galactocentric_frame_defaults` +`~astropy.utils.state.ScienceState` class. + +The `~astropy.coordinates.galactocentric_frame_defaults` class controls the +default parameter settings in `~astropy.coordinates.Galactocentric` by mapping a +set of string names to particular choices of the parameter values. For an +up-to-date list of valid names, see the docstring of +`~astropy.coordinates.galactocentric_frame_defaults`, but these names are things +like ``'pre-v4.0'``, which sets the default parameter values to their original +definition (i.e. pre-astropy-v4.0) values, and ``'v4.0'``, which sets the +default parameter values to a more modern set of measurements as updated in +Astropy version 4.0. Also, custom sets of measurements can be registered to +`~astropy.coordinates.galactocentric_frame_defaults` and used like the +built-in options. + +`~astropy.coordinates.galactocentric_frame_defaults` also tracks the +references (i.e. scientific papers that define the parameter values) for all +parameter values, as well as any further specified metadata information. + +As with other `~astropy.utils.state.ScienceState` subclasses, the +`~astropy.coordinates.galactocentric_frame_defaults` class can be used to +globally set the frame defaults at runtime. + +Examples +-------- + +.. + EXAMPLE START + Setting Galactocentric Coordinate Frame Defaults at Runtime + +The default parameter values can be seen by initializing the +`~astropy.coordinates.Galactocentric` frame with no arguments: + +:: + + >>> from astropy.coordinates import Galactocentric + >>> Galactocentric() + , galcen_distance=8.122 kpc, galcen_v_sun=(12.9, 245.6, 7.78) km / s, z_sun=20.8 pc, roll=0.0 deg)> + +These default values can be modified using this class:: + + >>> from astropy.coordinates import galactocentric_frame_defaults + >>> _ = galactocentric_frame_defaults.set('v4.0') # doctest: +SKIP + >>> Galactocentric() # doctest: +SKIP + , galcen_distance=8.122 kpc, galcen_v_sun=(12.9, 245.6, 7.78) km / s, z_sun=20.8 pc, roll=0.0 deg)> + >>> _ = galactocentric_frame_defaults.set('pre-v4.0') # doctest: +SKIP + >>> Galactocentric() # doctest: +SKIP + , galcen_distance=8.3 kpc, galcen_v_sun=(11.1, 232.24, 7.25) km / s, z_sun=27.0 pc, roll=0.0 deg)> + +The default parameters can also be updated by using this class as a context +manager to change the default parameter values locally to a piece of your code:: + + >>> with galactocentric_frame_defaults.set('pre-v4.0'): + ... print(Galactocentric()) # doctest: +FLOAT_CMP + , galcen_distance=8.3 kpc, galcen_v_sun=(11.1, 232.24, 7.25) km / s, z_sun=27.0 pc, roll=0.0 deg)> + +Again, changing the default parameter values will not affect frame +attributes that are explicitly specified:: + + >>> import astropy.units as u + >>> with galactocentric_frame_defaults.set('pre-v4.0'): + ... print(Galactocentric(galcen_distance=8.0*u.kpc)) # doctest: +FLOAT_CMP + , galcen_distance=8.0 kpc, galcen_v_sun=(11.1, 232.24, 7.25) km / s, z_sun=27.0 pc, roll=0.0 deg)> + + +Additional parameter sets may be registered, for instance to use the Dehnen & Binney (1998) measurements of the solar motion. We can also add metadata, such as the 1-sigma errors:: + + >>> state = galactocentric_frame_defaults.get_from_registry("v4.0") + >>> state["parameters"]["galcen_v_sun"] = (10.00, 225.25, 7.17) * (u.km / u.s) + >>> state["references"]["galcen_v_sun"] = "http://www.adsabs.harvard.edu/full/1998MNRAS.298..387D" + >>> state["error"] = {"galcen_v_sun": (0.36, 0.62, 0.38) * (u.km / u.s)} + >>> galactocentric_frame_defaults.register(name="DB1998", **state) + +Just as in the previous examples, the new parameter set can be get / set:: + + >>> state = galactocentric_frame_defaults.get_from_registry("DB1998") + >>> print(state["error"]["galcen_v_sun"]) # doctest: +FLOAT_CMP + [0.36 0.62 0.38] km / s + +.. + EXAMPLE END + +Unless set with the `~astropy.coordinates.galactocentric_frame_defaults` class, +the default parameter values for the `~astropy.coordinates.Galactocentric` +frame are set to ``'latest'``, meaning that the default parameter values may +change if you update Astropy. If you use the +`~astropy.coordinates.Galactocentric` frame without specifying all parameter +values explicitly, we therefore suggest manually setting the frame default set +manually in any science code that depends sensitively on the choice of, e.g., +solar motion or the other frame parameters. For example, in such code, we +recommend adding something like this to your import block (here using +``'v4.0'`` as an example):: + + >>> import astropy.coordinates as coord + >>> coord.galactocentric_frame_defaults.set('v4.0') # doctest: +SKIP diff --git a/docs/coordinates/index.rst b/docs/coordinates/index.rst new file mode 100644 index 000000000000..9d9c14be18eb --- /dev/null +++ b/docs/coordinates/index.rst @@ -0,0 +1,607 @@ +.. Force downloading of sites.json so that future doctest output isn't +.. cluttered with "Downloading ... [done]". This can be removed once we have a +.. better way of ignoring output lines based on pattern-matching, e.g.: +.. https://github.com/astropy/pytest-doctestplus/issues/11 + +.. testsetup:: + + >>> from astropy.coordinates import EarthLocation + >>> EarthLocation._get_site_registry(refresh_cache=True) #doctest: +REMOTE_DATA +IGNORE_OUTPUT + +.. _astropy-coordinates: + +******************************************************* +Astronomical Coordinate Systems (`astropy.coordinates`) +******************************************************* + +Introduction +============ + +The `~astropy.coordinates` package provides classes for representing a variety +of celestial/spatial coordinates and their velocity components, as well as tools +for converting between common coordinate systems in a uniform way. + +Getting Started +=============== + +The best way to start using `~astropy.coordinates` is to use the |SkyCoord| +class. |SkyCoord| objects are instantiated by passing in positions (and +optional velocities) with specified units and a coordinate frame. Sky positions +are commonly passed in as `~astropy.units.Quantity` objects and the frame is +specified with the string name. + +.. + EXAMPLE START + Using the SkyCoord Class + +To create a |SkyCoord| object to represent an ICRS (Right ascension [RA], +Declination [Dec]) sky position:: + + >>> from astropy import units as u + >>> from astropy.coordinates import SkyCoord + >>> c = SkyCoord(ra=10.625*u.degree, dec=41.2*u.degree, frame='icrs') + +The initializer for |SkyCoord| is very flexible and supports inputs provided in +a number of convenient formats. The following ways of initializing a coordinate +are all equivalent to the above:: + + >>> c = SkyCoord(10.625, 41.2, frame='icrs', unit='deg') + >>> c = SkyCoord('00h42m30s', '+41d12m00s', frame='icrs') + >>> c = SkyCoord('00h42.5m', '+41d12m') + >>> c = SkyCoord('00 42 30 +41 12 00', unit=(u.hourangle, u.deg)) + >>> c = SkyCoord('00:42.5 +41:12', unit=(u.hourangle, u.deg)) + >>> c # doctest: +FLOAT_CMP + + +The examples above illustrate a few rules to follow when creating a +coordinate object: + +- Coordinate values can be provided either as unnamed positional arguments or + via keyword arguments like ``ra`` and ``dec``, or ``l`` and ``b`` (depending + on the frame). +- The coordinate ``frame`` keyword is optional because it defaults to + `~astropy.coordinates.ICRS`. +- Angle units must be specified for all components, either by passing in a + `~astropy.units.Quantity` object (e.g., ``10.5*u.degree``), by including them + in the value (e.g., ``'+41d12m00s'``), or via the ``unit`` keyword. + +.. + EXAMPLE END + +|SkyCoord| and all other `~astropy.coordinates` objects also support +array coordinates. These work in the same way as single-value coordinates, but +they store multiple coordinates in a single object. When you are going +to apply the same operation to many different coordinates (say, from a +catalog), this is a better choice than a list of |SkyCoord| objects, +because it will be *much* faster than applying the operation to each +|SkyCoord| in a ``for`` loop. Like the underlying `~numpy.ndarray` instances +that contain the data, |SkyCoord| objects can be sliced, reshaped, etc., +and can be used with functions like `numpy.moveaxis`, etc., that affect the +shape:: + + >>> import numpy as np + >>> c = SkyCoord(ra=[10, 11, 12, 13]*u.degree, dec=[41, -5, 42, 0]*u.degree) + >>> c + + >>> c[1] + + >>> c.reshape(2, 2) + + >>> np.roll(c, 1) + + + +Coordinate Access +----------------- + +Once you have a coordinate object you can access the components of that +coordinate (e.g., RA, Dec) to get string representations of the full +coordinate. + +The component values are accessed using (typically lowercase) named attributes +that depend on the coordinate frame (e.g., ICRS, Galactic, etc.). For the +default, ICRS, the coordinate component names are ``ra`` and ``dec``:: + + >>> c = SkyCoord(ra=10.68458*u.degree, dec=41.26917*u.degree) + >>> c.ra # doctest: +FLOAT_CMP + + >>> c.ra.hour # doctest: +FLOAT_CMP + np.float64(0.7123053333333335) + >>> c.ra.hms # doctest: +FLOAT_CMP + hms_tuple(h=np.float64(0.0), m=np.float64(42.0), s=np.float64(44.299200000000525)) + >>> c.dec # doctest: +FLOAT_CMP + + >>> c.dec.degree # doctest: +FLOAT_CMP + np.float64(41.26917) + >>> c.dec.radian # doctest: +FLOAT_CMP + np.float64(0.7202828960652683) + +Coordinates can be converted to strings using the +:meth:`~astropy.coordinates.SkyCoord.to_string` method:: + + >>> c = SkyCoord(ra=10.68458*u.degree, dec=41.26917*u.degree) + >>> c.to_string('decimal') + '10.6846 41.2692' + >>> c.to_string('dms') + '10d41m04.488s 41d16m09.012s' + >>> c.to_string('hmsdms') + '00h42m44.2992s +41d16m09.012s' + +For additional information see the section on :ref:`working_with_angles`. + +Transformation +-------------- + +One convenient way to transform to a new coordinate frame is by accessing +the appropriately named attribute. + +.. + EXAMPLE START + Transforming to a New Coordinate Frame + +To get the coordinate in the `~astropy.coordinates.Galactic` frame use:: + + >>> c_icrs = SkyCoord(ra=10.68458*u.degree, dec=41.26917*u.degree, frame='icrs') + >>> c_icrs.galactic # doctest: +FLOAT_CMP + + +For more control, you can use the `~astropy.coordinates.SkyCoord.transform_to` +method, which accepts a frame name, frame class, or frame instance:: + + >>> c_fk5 = c_icrs.transform_to('fk5') # c_icrs.fk5 does the same thing + >>> c_fk5 # doctest: +FLOAT_CMP + + + >>> from astropy.coordinates import FK5 + >>> c_fk5.transform_to(FK5(equinox='J1975')) # precess to a different equinox # doctest: +FLOAT_CMP + + +.. + EXAMPLE END + +This form of `~astropy.coordinates.SkyCoord.transform_to` also makes it +possible to convert from celestial coordinates to +`~astropy.coordinates.AltAz` coordinates, allowing the use of |SkyCoord| +as a tool for planning observations. For a more complete example of +this, see :ref:`sphx_glr_generated_examples_coordinates_plot_obs-planning.py`. + +Some coordinate frames such as `~astropy.coordinates.AltAz` require Earth +rotation information (UT1-UTC offset and/or polar motion) when transforming +to/from other frames. These Earth rotation values are automatically downloaded +from the International Earth Rotation and Reference Systems (IERS) service when +required. See :ref:`utils-iers` for details of this process. + +Representation +-------------- + +So far we have been using a spherical coordinate representation in all of our +examples, and this is the default for the built-in frames. Frequently it is +convenient to initialize or work with a coordinate using a different +representation such as Cartesian or Cylindrical. This can be done by setting +the ``representation_type`` for either |SkyCoord| objects or low-level frame +coordinate objects. + +.. + EXAMPLE START + Working with Nonspherical Coordinate Representations + +To initialize or work with a coordinate using a different representation such +as Cartesian or Cylindrical:: + + >>> c = SkyCoord(x=1, y=2, z=3, unit='kpc', representation_type='cartesian') + >>> c # doctest: +FLOAT_CMP + + >>> c.x, c.y, c.z # doctest: +FLOAT_CMP + (, , ) + + >>> c.representation_type = 'cylindrical' + >>> c # doctest: +FLOAT_CMP + + +For all of the details see :ref:`astropy-skycoord-representations`. + +.. + EXAMPLE END + +Distance +-------- + +|SkyCoord| and the individual frame classes also support specifying a distance +from the frame origin. The origin depends on the particular coordinate frame; +this can be, for example, centered on the earth, centered on the solar system +barycenter, etc. + +.. + EXAMPLE START + Specifying a Distance with SkyCoord + +Two angles and a distance specify a unique point in 3D space, which also allows +converting the coordinates to a Cartesian representation:: + + >>> c = SkyCoord(ra=10.68458*u.degree, dec=41.26917*u.degree, distance=770*u.kpc) + >>> c.cartesian.x # doctest: +FLOAT_CMP + + >>> c.cartesian.y # doctest: +FLOAT_CMP + + >>> c.cartesian.z # doctest: +FLOAT_CMP + + +With distances assigned, |SkyCoord| convenience methods are more powerful, as +they can make use of the 3D information. For example, to compute the physical, +3D separation between two points in space:: + + >>> c1 = SkyCoord(ra=10*u.degree, dec=9*u.degree, distance=10*u.pc, frame='icrs') + >>> c2 = SkyCoord(ra=11*u.degree, dec=10*u.degree, distance=11.5*u.pc, frame='icrs') + >>> c1.separation_3d(c2) # doctest: +FLOAT_CMP + + +.. + EXAMPLE END + +Convenience Methods +------------------- + +|SkyCoord| defines a number of convenience methods that support, for example, +computing on-sky (i.e., angular) and 3D separations between two coordinates. + +.. + EXAMPLE START + SkyCoord Convenience Methods + +To compute on-sky and 3D separations between two coordinates:: + + >>> c1 = SkyCoord(ra=10*u.degree, dec=9*u.degree, frame='icrs') + >>> c2 = SkyCoord(ra=11*u.degree, dec=10*u.degree, frame='fk5') + >>> c1.separation(c2) # Differing frames handled correctly # doctest: +FLOAT_CMP + + +Or cross-matching catalog coordinates (detailed in +:ref:`astropy-coordinates-matching`):: + + >>> target_c = SkyCoord(ra=10*u.degree, dec=9*u.degree, frame='icrs') + >>> # read in coordinates from a catalog... + >>> catalog_c = ... # doctest: +SKIP + >>> idx, sep, _ = target_c.match_to_catalog_sky(catalog_c) # doctest: +SKIP + +.. + EXAMPLE END + +The `astropy.coordinates` sub-package also provides a quick way to get +coordinates for named objects, assuming you have an active internet +connection. The `~astropy.coordinates.SkyCoord.from_name` method of |SkyCoord| +uses `Sesame `_ to retrieve coordinates +for a particular named object. + +.. + EXAMPLE START + Retrieving Coordinates for a Named Object with SkyCoord + +To retrieve coordinates for a particular named object:: + + >>> SkyCoord.from_name("PSR J1012+5307") # doctest: +REMOTE_DATA +FLOAT_CMP + + +In some cases, the coordinates are embedded in the catalog name of the object. +For such object names, `~astropy.coordinates.SkyCoord.from_name` is able +to parse the coordinates from the name if given the ``parse=True`` option. +For slow connections, this may be much faster than a sesame query for the same +object name. It's worth noting, however, that the coordinates extracted in this +way may differ from the database coordinates by a few deci-arcseconds, so only +use this option if you do not need sub-arcsecond accuracy for your coordinates:: + + >>> SkyCoord.from_name("CRTS SSS100805 J194428-420209", parse=True) # doctest: +FLOAT_CMP + + +.. + EXAMPLE END + +For sites (primarily observatories) on the Earth, `astropy.coordinates` provides +a quick way to get an |EarthLocation| - the +:meth:`~astropy.coordinates.EarthLocation.of_site` classmethod: + +.. doctest-remote-data:: + + >>> from astropy.coordinates import EarthLocation + >>> apo = EarthLocation.of_site('Apache Point Observatory') + >>> apo # doctest: +FLOAT_CMP + + +To see the list of site names available, use +:meth:`~astropy.coordinates.EarthLocation.get_site_names`:: + + >>> EarthLocation.get_site_names() # doctest: +REMOTE_DATA + ['ALMA', 'AO', 'ARCA', ...] + +Both :meth:`~astropy.coordinates.EarthLocation.of_site` and +:meth:`~astropy.coordinates.EarthLocation.get_site_names`, +`astropy.coordinates` attempt to access the site registry from the +`astropy-data repository `_ and will +save the registry in the user's local cache (see :ref:`utils-data`). +The cached version of the site registry is not updated automatically, +but the latest version may be downloaded using the ``refresh_cache=True`` +option of these methods. If you would like a site to be added to the registry, +issue a pull request to the `astropy-data repository +`_. + +For arbitrary Earth addresses (e.g., not observatory sites), use the +:meth:`~astropy.coordinates.EarthLocation.of_address` classmethod to retrieve +the latitude and longitude. This works with fully specified addresses, location +names, city names, etc: + +.. doctest-remote-data:: + + >>> EarthLocation.of_address('1002 Holy Grail Court, St. Louis, MO') # doctest: +FLOAT_CMP + + +By default the `OpenStreetMap Nominatim service +`_ is used, but by providing a +`Google Geocoding API key +`_ with +the ``google_api_key`` argument it is possible to use Google Maps instead. It +is also possible to query the height of the location in addition to its +longitude and latitude, but only with the Google queries:: + + >>> EarthLocation.of_address("Cape Town", get_height=True) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + ValueError: Currently, `get_height` only works when using the Google + geocoding API... + +.. note:: + :meth:`~astropy.coordinates.SkyCoord.from_name`, + :meth:`~astropy.coordinates.EarthLocation.of_site`, and + :meth:`~astropy.coordinates.EarthLocation.of_address` are designed for + convenience, not accuracy. If you need accurate coordinates for an + object you should find the appropriate reference and input the coordinates + manually, or use more specialized functionality like that in the `astroquery + `_ or `astroplan + `_ affiliated packages. + + Also note that these methods retrieve data from the internet to + determine the celestial or geographic coordinates. The online data may be + updated, so if you need to guarantee that your scripts are reproducible + in the long term, see the :doc:`remote_methods` section. + +This functionality can be combined to do more complicated tasks like computing +barycentric corrections to radial velocity observations (also a supported +high-level |SkyCoord| method - see :ref:`astropy-coordinates-rv-corrs`): + +.. doctest-remote-data:: + + >>> from astropy.time import Time + >>> obstime = Time('2017-2-14') + >>> target = SkyCoord.from_name('M31') + >>> keck = EarthLocation.of_site('Keck') + >>> target.radial_velocity_correction(obstime=obstime, location=keck).to('km/s') # doctest: +FLOAT_CMP + + +While ``astropy.coordinates`` does not natively support converting an Earth +location to a timezone, the longitude and latitude can be retrieved from any +`~astropy.coordinates.EarthLocation` object, which could then be passed to any +third-party package that supports timezone solving, such as `timezonefinder +`_, in which case you might have to +pass in their ``.degree`` attributes. + +The resulting timezone name could then be used with any packages that support +time zone definitions, such as the `zoneinfo `_: + +.. doctest-remote-data:: + + >>> import datetime + >>> from zoneinfo import ZoneInfo + >>> tz = ZoneInfo('America/Phoenix') + >>> dt = datetime.datetime(2021, 4, 12, 20, 0, 0, tzinfo=tz) + + +Velocities (Proper Motions and Radial Velocities) +------------------------------------------------- + +In addition to positional coordinates, `~astropy.coordinates` supports storing +and transforming velocities. These are available both via the lower-level +:doc:`coordinate frame classes `, and via |SkyCoord| objects:: + + >>> sc = SkyCoord(1*u.deg, 2*u.deg, radial_velocity=20*u.km/u.s) + >>> sc # doctest: +FLOAT_CMP + + +For more details on velocity support (and limitations), see the +:doc:`velocities` page. + +.. _astropy-coordinates-masks: + +Masks +----- + +Sometimes you may have incomplete information about objects, e.g., some have +distances while others have not. `~astropy.coordinates` supports using masks +for such purposes, using the |Masked| class:: + + >>> from astropy.utils.masked import Masked + >>> distance = Masked([0.1, np.nan]*u.kpc, mask=[False, True]) + >>> sc = SkyCoord([1., 2.]*u.hourangle, [3., 4.]*u.deg, distance=distance) + >>> sc + + +The masks propagates as you would expect:: + + >>> sc.separation(sc[0]) # doctest: +FLOAT_CMP + + >>> sc.separation_3d(sc[0]) # doctest: +FLOAT_CMP + + >>> gcrs = sc.gcrs # doctest: +SHOW_WARNINGS +IGNORE_OUTPUT + RuntimeWarning: invalid value encountered in ld... + RuntimeWarning: invalid value encountered in anp... + >>> gcrs # doctest: +FLOAT_CMP + + +In the last example, you will notice that the angles of the second item have +become masked too. This is because the distance is required in the conversion. +Indeed, because we put in ``NaN``, we get not only the warnings during +the conversion, but also ``NaN`` in the unmasked converted angles:: + + >>> gcrs.unmasked # doctest: +FLOAT_CMP + + +In principle, by using a "good guess" for the distance, this can be avoided:: + + >>> distance2 = Masked([0.1, 1.]*u.kpc, mask=[False, True]) + >>> sc2 = SkyCoord([1., 2.]*u.hourangle, [3., 4.]*u.deg, distance=distance2) + >>> gcrs2 = sc2.gcrs + >>> gcrs2 # doctest: +FLOAT_CMP + + >>> gcrs2.unmasked # doctest: +FLOAT_CMP + + +.. warning:: + Support for masks is new in astropy 7.0, and likely incomplete. + Please report any problems you find. + +.. _astropy-coordinates-overview: + +Overview of `astropy.coordinates` Concepts +========================================== + +.. note:: + More detailed information and justification of the design is available in + `APE (Astropy Proposal for Enhancement) 5 + `_. + +Here we provide an overview of the package and associated framework. +This background information is not necessary for using `~astropy.coordinates`, +particularly if you use the |SkyCoord| high-level class, but it is helpful for +more advanced usage, particularly creating your own frame, transformations, or +representations. Another useful piece of background information are some +:ref:`astropy-coordinates-definitions` as they are used in +`~astropy.coordinates`. + +`~astropy.coordinates` is built on a three-tiered system of objects: +representations, frames, and a high-level class. Representations +classes are a particular way of storing a three-dimensional data point +(or points), such as Cartesian coordinates or spherical polar +coordinates. Frames are particular reference frames like FK5 or ICRS, +which may store their data in different representations, but have +well-defined transformations between each other. These transformations are +all stored in the ``astropy.coordinates.frame_transform_graph``, and new +transformations can be created by users. Finally, the high-level class +(|SkyCoord|) uses the frame classes, but provides a more accessible +interface to these objects as well as various convenience methods and +more string-parsing capabilities. + +Separating these concepts makes it easier to extend the functionality of +`~astropy.coordinates`. It allows representations, frames, and +transformations to be defined or extended separately, while still +preserving the high-level capabilities and ease-of-use of the |SkyCoord| +class. + +.. topic:: Examples: + + See :ref:`sphx_glr_generated_examples_coordinates_plot_obs-planning.py` for + an example of using the `~astropy.coordinates` functionality to prepare for + an observing run. + +Using `astropy.coordinates` +=========================== + +More detailed information on using the package is provided on separate pages, +listed below. + +.. toctree:: + :maxdepth: 1 + + angles + skycoord + transforming + solarsystem + satellites + formatting + matchsep + representations + frames + velocities + apply_space_motion + spectralcoord + stokescoord + galactocentric + remote_methods + common_errors + definitions + inplace + example_gallery_index + +In addition, another resource for the capabilities of this package is the +``astropy.coordinates.tests.test_api_ape5`` testing file. It showcases most of +the major capabilities of the package, and hence is a useful supplement to +this document. You can see it by either downloading a copy of the Astropy +source code, or typing the following in an IPython session:: + + In [1]: from astropy.coordinates.tests import test_api_ape5 + In [2]: test_api_ape5?? + + +.. note that if this section gets too long, it should be moved to a separate + doc page - see the top of performance.inc.rst for the instructions on how to + do that +.. include:: performance.inc.rst + +.. _astropy-coordinates-seealso: + +See Also +======== + +Some references that are particularly useful in understanding subtleties of the +coordinate systems implemented here include: + +* `USNO Circular 179 `_ + A useful guide to the IAU 2000/2003 work surrounding ICRS/IERS/CIRS and + related problems in precision coordinate system work. +* `Standards Of Fundamental Astronomy `_ + The definitive implementation of IAU-defined algorithms. The "SOFA Tools + for Earth Attitude" document is particularly valuable for understanding + the latest IAU standards in detail. +* `IERS Conventions (2010) `_ + An exhaustive reference covering the ITRS, the IAU2000 celestial coordinates + framework, and other related details of modern coordinate conventions. +* Meeus, J. "Astronomical Algorithms" + A valuable text describing details of a wide range of coordinate-related + problems and concepts. +* `Revisiting Spacetrack Report #3 `_ + A discussion of the simplified general perturbation (SGP) for satellite orbits, with a description of + the True Equator Mean Equinox (TEME) coordinate frame. + + +Built-in Frames and Transformations +=================================== + +.. automodule:: astropy.coordinates.builtin_frames + + +.. _astropy-coordinates-api: + +Reference/API +============= + +.. toctree:: + :maxdepth: 2 + + ref_api diff --git a/docs/coordinates/inplace.rst b/docs/coordinates/inplace.rst new file mode 100644 index 000000000000..ed5cf07cb40e --- /dev/null +++ b/docs/coordinates/inplace.rst @@ -0,0 +1,53 @@ +.. _astropy-coordinates-fast-in-place: + +Fast In-Place Modification of Coordinates +***************************************** + +For some applications the recommended method of +:ref:`astropy-coordinates-modifying-in-place` may not be fast enough due to the +extensive validation performed in that process to ensure correctness. Likewise, +you may find that creating another coordinate frame with different data using +`~astropy.coordinates.BaseCoordinateFrame.realize_frame` does not meet your +performance requirements. + +For these high-performance situations, you can directly modify in-place the +representation data in the frame object as shown in this example:: + + >>> import astropy.units as u + >>> from astropy.coordinates import SkyCoord + >>> sc = SkyCoord([1,2],[3,4], unit='deg') + >>> sc.data.lon[()] = [10, 20] * u.deg + >>> sc.data.lat[1] = 40 * u.deg + + >>> sc.cache.clear() # IMPORTANT TO DO THIS! + + >>> sc # doctest: +FLOAT_CMP + + +Notice that the ``.data`` representation object uses different names for the +components than in the coordinate object. If you wish to inspect the +mapping between frame attributes (e.g., ``.ra``) and representation attributes +(e.g., ``.lon``) you can look at the following dictionary:: + + >>> sc.representation_component_names + {'ra': 'lon', 'dec': 'lat', 'distance': 'distance'} + +.. warning:: + + You *must* include the step to clear the cache as shown. Failing to do so + will cause the object to be inconsistent and likely result in incorrect + results. `~astropy.coordinates.SkyCoord` + and `~astropy.coordinates.BaseCoordinateFrame` cache various kinds of + information for performance reasons, so you need clear the cache so that + the new representation values are used when required. + +You should note that the only way to modify the data in a frame is by using +the ``.data`` attribute directly and not the aliases for components on the +frame. For example the following will *appear* to give a correct +result but it does not actually modify the underlying representation data:: + + >>> sc.ra[1] = 20 * u.deg # THIS IS WRONG + +This problem is related to the current implementation of performance-based +caching and cannot be easily resolved. diff --git a/docs/coordinates/matchsep.rst b/docs/coordinates/matchsep.rst new file mode 100644 index 000000000000..194a2713a4f2 --- /dev/null +++ b/docs/coordinates/matchsep.rst @@ -0,0 +1,349 @@ +.. _astropy-coordinates-separations-matching: + +Separations, Offsets, Catalog Matching, and Related Functionality +***************************************************************** + +`astropy.coordinates` contains commonly-used tools for comparing or +matching coordinate objects. Of particular importance are those for +determining separations between coordinates and those for matching a +coordinate (or coordinates) to a catalog. These are mainly implemented +as methods on the coordinate objects. + +In the examples below, we will assume that the following imports have already +been executed:: + + >>> import astropy.units as u + >>> from astropy.coordinates import SkyCoord + +Separations +=========== + +The on-sky separation can be computed with +:meth:`~astropy.coordinates.BaseCoordinateFrame.separation`, +which computes the great-circle distance (*not* the small-angle approximation):: + + >>> c1 = SkyCoord('5h23m34.5s', '-69d45m22s', frame='icrs') + >>> c2 = SkyCoord('0h52m44.8s', '-72d49m43s', frame='fk5') + >>> sep = c1.separation(c2) + >>> sep # doctest: +FLOAT_CMP + + +The returned object is an `~astropy.coordinates.Angle` instance, so it +is possible to access the angle in any of several equivalent angular +units:: + + >>> sep.radian # doctest: +FLOAT_CMP + np.float64(0.36208800460262563) + >>> sep.hour # doctest: +FLOAT_CMP + np.float64(1.3830742984029318) + >>> sep.arcminute # doctest: +FLOAT_CMP + np.float64(1244.7668685626384) + >>> sep.arcsecond # doctest: +FLOAT_CMP + np.float64(74686.0121137583) + +Also note that the two input coordinates were not in the same frame — +one is automatically converted to match the other, ensuring that even +though they are in different frames, the separation is determined +consistently. + +In addition to the on-sky separation described above, +:meth:`~astropy.coordinates.BaseCoordinateFrame.separation_3d` will +determine the 3D distance between two coordinates that have ``distance`` +defined:: + + >>> c1 = SkyCoord('5h23m34.5s', '-69d45m22s', distance=70*u.kpc, frame='icrs') + >>> c2 = SkyCoord('0h52m44.8s', '-72d49m43s', distance=80*u.kpc, frame='icrs') + >>> sep = c1.separation_3d(c2) + >>> sep # doctest: +FLOAT_CMP + + + +Offsets +======= + +Closely related to angular separations are offsets between coordinates. The key +distinction for offsets is generally the concept of a "from" and "to" coordinate +rather than the single scalar angular offset of a separation. +`~astropy.coordinates` contains conveniences to compute some of the common +offsets encountered in astronomy. + +The first piece of such functionality is the +:meth:`~astropy.coordinates.BaseCoordinateFrame.position_angle` method. +This method computes the position angle between one +|SkyCoord| instance and another (passed as the argument) following the +astronomy convention (positive angles East of North):: + + >>> c1 = SkyCoord(1*u.deg, 1*u.deg, frame='icrs') + >>> c2 = SkyCoord(2*u.deg, 2*u.deg, frame='icrs') + >>> c1.position_angle(c2).to(u.deg) # doctest: +FLOAT_CMP + + +The combination of :meth:`~astropy.coordinates.BaseCoordinateFrame.separation` +and :meth:`~astropy.coordinates.BaseCoordinateFrame.position_angle` thus give a +set of directional offsets. +To do the inverse operation — determining the new +"destination" coordinate given a separation and position angle — the +:meth:`~astropy.coordinates.SkyCoord.directional_offset_by` method is provided:: + + >>> c1 = SkyCoord(1*u.deg, 1*u.deg, frame='icrs') + >>> position_angle = 45 * u.deg + >>> separation = 1.414 * u.deg + >>> c1.directional_offset_by(position_angle, separation) # doctest: +FLOAT_CMP + + +This technique is also useful for computing the midpoint (or indeed any point) +between two coordinates in a way that accounts for spherical geometry +(i.e., instead of averaging the RAs/Decs separately):: + + >>> coord1 = SkyCoord(0*u.deg, 0*u.deg, frame='icrs') + >>> coord2 = SkyCoord(1*u.deg, 1*u.deg, frame='icrs') + >>> pa = coord1.position_angle(coord2) + >>> sep = coord1.separation(coord2) + >>> coord1.directional_offset_by(pa, sep/2) # doctest: +FLOAT_CMP + + +There is also a :meth:`~astropy.coordinates.SkyCoord.spherical_offsets_to` +method for computing angular offsets (e.g., small shifts like you might give a +telescope operator to move from a bright star to a fainter target):: + + >>> bright_star = SkyCoord('8h50m59.75s', '+11d39m22.15s', frame='icrs') + >>> faint_galaxy = SkyCoord('8h50m47.92s', '+11d39m32.74s', frame='icrs') + >>> dra, ddec = bright_star.spherical_offsets_to(faint_galaxy) + >>> dra.to(u.arcsec) # doctest: +FLOAT_CMP + + >>> ddec.to(u.arcsec) # doctest: +FLOAT_CMP + + +The conceptual inverse of +:meth:`~astropy.coordinates.SkyCoord.spherical_offsets_to` is also available as +a method on any |SkyCoord| object: +:meth:`~astropy.coordinates.SkyCoord.spherical_offsets_by`, which accepts two +angular offsets (in longitude and latitude) and returns the coordinates at the +offset location:: + + >>> target_star = SkyCoord(86.75309*u.deg, -31.5633*u.deg, frame='icrs') + >>> target_star.spherical_offsets_by(1.3*u.arcmin, -0.7*u.arcmin) # doctest: +FLOAT_CMP + + +.. _astropy-skyoffset-frames: + +"Sky Offset" Frames +------------------- + +To extend the concept of spherical offsets, `~astropy.coordinates` has +a frame class :class:`~astropy.coordinates.SkyOffsetFrame` +which creates distinct frames that are centered on a specific point. +These are known as "sky offset frames," as they are a convenient way to create +a frame centered on an arbitrary position on the sky suitable for computing +positional offsets (e.g., for astrometry):: + + >>> from astropy.coordinates import SkyOffsetFrame, ICRS + >>> center = ICRS(10*u.deg, 45*u.deg) + >>> center.transform_to(SkyOffsetFrame(origin=center)) # doctest: +FLOAT_CMP + ): (lon, lat) in deg + (0., 0.)> + >>> target = ICRS(11*u.deg, 46*u.deg) + >>> target.transform_to(SkyOffsetFrame(origin=center)) # doctest: +FLOAT_CMP + ): (lon, lat) in deg + (0.69474685, 1.00428706)> + + +Alternatively, the convenience method +:meth:`~astropy.coordinates.SkyCoord.skyoffset_frame` lets you create a sky +offset frame from an existing |SkyCoord|:: + + >>> center = SkyCoord(10*u.deg, 45*u.deg) + >>> aframe = center.skyoffset_frame() + >>> target.transform_to(aframe) # doctest: +FLOAT_CMP + ): (lon, lat) in deg + (0.69474685, 1.00428706)> + >>> other = SkyCoord(9*u.deg, 44*u.deg, frame='fk5') + >>> other.transform_to(aframe) # doctest: +FLOAT_CMP + ): (lon, lat) in deg + (-0.71943945, -0.99556216)> + +.. note:: + + While sky offset frames *appear* to be all the same class, this not the + case: the sky offset frame for each different type of frame for ``origin`` is + actually a distinct class. E.g., ``SkyOffsetFrame(origin=ICRS(...))`` + yields an object of class ``SkyOffsetICRS``, *not* ``SkyOffsetFrame``. + While this is not important for most uses of this class, it is important for + things like type-checking, because something like + ``SkyOffsetFrame(origin=ICRS(...)).__class__ is SkyOffsetFrame`` will + *not* be ``True``, as it would be for most classes. + +This same frame is also useful as a tool for defining frames that are relative +to a specific, known object useful for hierarchical physical systems like galaxy +groups. For example, objects around M31 are sometimes shown in a coordinate +frame aligned with standard ICRA RA/Dec, but on M31:: + + >>> m31 = SkyCoord(10.6847083*u.deg, 41.26875*u.deg, frame='icrs') + >>> ngc147 = SkyCoord(8.3005*u.deg, 48.5087389*u.deg, frame='icrs') + >>> ngc147_inm31 = ngc147.transform_to(m31.skyoffset_frame()) + >>> xi, eta = ngc147_inm31.lon, ngc147_inm31.lat + >>> xi # doctest: +FLOAT_CMP + + >>> eta # doctest: +FLOAT_CMP + + +.. note:: + + Currently, distance information in the ``origin`` of a + :class:`~astropy.coordinates.SkyOffsetFrame` is not + used to compute any part of the transform. The ``origin`` is only used for + on-sky rotation. This may change in the future, however. + + +.. _astropy-coordinates-matching: + +Matching Catalogs +================= + +`~astropy.coordinates` leverages the coordinate framework to make it +possible to find the closest coordinates in a catalog to a desired set +of other coordinates. For example, assuming ``ra1``/``dec1`` and +``ra2``/``dec2`` are NumPy arrays loaded from some file: + +.. testsetup:: + >>> ra1 = [5.3517] + >>> dec1 = [-5.2328] + >>> distance1 = 1344 + >>> ra2 = [6.459] + >>> dec2 = [-16.4258] + >>> distance2 = 8.611 + +.. doctest-requires:: scipy + + >>> c = SkyCoord(ra=ra1*u.degree, dec=dec1*u.degree) + >>> catalog = SkyCoord(ra=ra2*u.degree, dec=dec2*u.degree) + >>> idx, d2d, d3d = c.match_to_catalog_sky(catalog) + +The distances returned ``d3d`` are 3-dimensional distances. +Unless both source (``c``) and catalog (``catalog``) coordinates have +associated distances, this quantity assumes that all sources are at a distance +of 1 (dimensionless). + +You can also find the nearest 3D matches, different from the on-sky +separation shown above only when the coordinates were initialized with +a ``distance``: + +.. doctest-requires:: scipy + + >>> c = SkyCoord(ra=ra1*u.degree, dec=dec1*u.degree, distance=distance1*u.kpc) + >>> catalog = SkyCoord(ra=ra2*u.degree, dec=dec2*u.degree, distance=distance2*u.kpc) + >>> idx, d2d, d3d = c.match_to_catalog_3d(catalog) + +Now ``idx`` are indices into ``catalog`` that are the closest objects to each +of the coordinates in ``c``, ``d2d`` are the on-sky distances between them, and +``d3d`` are the 3-dimensional distances. Because coordinate objects support +indexing, ``idx`` enables easy access to the matched set of coordinates in +the catalog: + +.. doctest-requires:: scipy + + >>> d3d # doctest: +FLOAT_CMP + + >>> matches = catalog[idx] + >>> matches.separation_3d(c) # doctest: +FLOAT_CMP + + >>> dra, ddec = c.spherical_offsets_to(matches) + +This functionality can also be accessed from the +:func:`~astropy.coordinates.match_coordinates_sky` and +:func:`~astropy.coordinates.match_coordinates_3d` functions. These +will work on either |SkyCoord| objects *or* the lower-level frame classes: + +.. doctest-requires:: scipy + + >>> from astropy.coordinates import match_coordinates_sky + >>> idx, d2d, d3d = match_coordinates_sky(c, catalog) + >>> idx, d2d, d3d = match_coordinates_sky(c.frame, catalog.frame) + +It is possible to impose a separation constraint (e.g., the maximum separation to be +considered a match) by creating a boolean mask with ``d2d`` or ``d3d``. For example: + +.. doctest-requires:: scipy + + >>> max_sep = 1.0 * u.arcsec + >>> idx, d2d, d3d = c.match_to_catalog_3d(catalog) + >>> sep_constraint = d2d < max_sep + >>> c_matches = c[sep_constraint] + >>> catalog_matches = catalog[idx[sep_constraint]] + +Now, ``c_matches`` and ``catalog_matches`` are the matched sources in ``c`` +and ``catalog``, respectively, which are separated by less than 1 arcsecond. + +.. _astropy-searching-coordinates: + +Searching around Coordinates +============================ + +Closely related functionality can be used to search for *all* coordinates within +a certain distance (either 3D distance or on-sky) of another set of coordinates. +The ``search_around_*`` methods (and functions) provide this functionality, +with an interface very similar to ``match_coordinates_*``: + +.. doctest-requires:: scipy + + >>> import numpy as np + >>> idxc, idxcatalog, d2d, d3d = catalog.search_around_sky(c, 1*u.deg) + >>> np.all(d2d < 1*u.deg) + np.True_ + +.. doctest-requires:: scipy + + >>> idxc, idxcatalog, d2d, d3d = catalog.search_around_3d(c, 1*u.kpc) + >>> np.all(d3d < 1*u.kpc) + np.True_ + +The key difference for these methods is that there can be multiple (or no) +matches in ``catalog`` around any locations in ``c``. Hence, indices into both +``c`` and ``catalog`` are returned instead of just indices into ``catalog``. +These can then be indexed back into the two |SkyCoord| objects, or, for that +matter, any array with the same order: + +.. doctest-requires:: scipy + + >>> np.all(c[idxc].separation(catalog[idxcatalog]) == d2d) + np.True_ + >>> np.all(c[idxc].separation_3d(catalog[idxcatalog]) == d3d) + np.True_ + >>> print(catalog_objectnames[idxcatalog]) #doctest: +SKIP + ['NGC 1234' 'NGC 4567' ...] + +Note, though, that this dual-indexing means that ``search_around_*`` does not +work well if one of the coordinates is a scalar, because the returned index +would not make sense for a scalar:: + + >>> scalarc = SkyCoord(ra=1*u.deg, dec=2*u.deg, distance=distance1*u.kpc) + >>> idxscalarc, idxcatalog, d2d, d3d = catalog.search_around_sky(scalarc, 1*u.deg) # doctest: +SKIP + ValueError: One of the inputs to search_around_sky is a scalar. + +As a result (and because the ``search_around_*`` algorithm is inefficient in +the scalar case), the best approach for this scenario is to instead +use the ``separation*`` methods: + +.. doctest-requires:: scipy + + >>> d2d = scalarc.separation(catalog) + >>> catalogmsk = d2d < 1*u.deg + >>> d3d = scalarc.separation_3d(catalog) + >>> catalog3dmsk = d3d < 1*u.kpc + +The resulting ``catalogmsk`` or ``catalog3dmsk`` variables are boolean arrays +rather than arrays of indices, but in practice they usually can be used in +the same way as ``idxcatalog`` from the above examples. If you definitely do +need indices instead of boolean masks, you can do: + +.. doctest-requires:: scipy + + >>> idxcatalog = np.where(catalogmsk)[0] + >>> idxcatalog3d = np.where(catalog3dmsk)[0] diff --git a/docs/coordinates/performance.inc.rst b/docs/coordinates/performance.inc.rst new file mode 100644 index 000000000000..6b0fb6e49572 --- /dev/null +++ b/docs/coordinates/performance.inc.rst @@ -0,0 +1,279 @@ +.. note that if this is changed from the default approach of using an *include* + (in index.rst) to a separate performance page, the header needs to be changed + from === to ***, the filename extension needs to be changed from .inc.rst to + .rst, and a link needs to be added in the subpackage toctree + +.. _astropy-coordinates-performance: + +Performance Tips +================ + +If you are using |SkyCoord| for many different coordinates, you will see much +better performance if you create a single |SkyCoord| with arrays of coordinates +as opposed to creating individual |SkyCoord| objects for each individual +coordinate:: + + >>> coord = SkyCoord(ra_array, dec_array, unit='deg') # doctest: +SKIP + +Frame attributes can be arrays too, as long as the coordinate data and all of +the frame attributes have shapes that are compatible according to +:doc:`Numpy broadcasting rules `: + + +.. testsetup:: + + >>> from astropy.coordinates import FK4 + >>> from astropy import units as u + +:: + + >>> coord = FK4(1 * u.deg, 2 * u.deg, obstime=["J2000", "J2001"]) + >>> coord.shape + (2,) + +In addition, looping over a |SkyCoord| object can be slow. If you need to +transform the coordinates to a different frame, it is much faster to transform a +single |SkyCoord| with arrays of values as opposed to looping over the +|SkyCoord| and transforming them individually. + +Finally, for more advanced users, note that you can use broadcasting to +transform |SkyCoord| objects into frames with vector properties. + +.. + EXAMPLE START + Performance Tips for Transforming SkyCoord Objects + +To use broadcasting to transform |SkyCoord| objects into frames with vector +properties:: + + >>> from astropy.coordinates import SkyCoord, EarthLocation + >>> from astropy import coordinates as coord + >>> from astropy.coordinates import golden_spiral_grid + >>> from astropy.time import Time + >>> from astropy import units as u + >>> import numpy as np + + >>> # 1000 locations in a grid on the sky + >>> coos = SkyCoord(golden_spiral_grid(size=1000)) + + >>> # 300 times over the space of 10 hours + >>> times = Time.now() + np.linspace(-5, 5, 300)*u.hour + + >>> # note the use of broadcasting so that 300 times are broadcast against 1000 positions + >>> lapalma = EarthLocation.from_geocentric(5327448.9957829, -1718665.73869569, 3051566.90295403, unit='m') + >>> aa_frame = coord.AltAz(obstime=times[:, np.newaxis], location=lapalma) + + >>> # calculate alt-az of each object at each time. + >>> aa_coos = coos.transform_to(aa_frame) # doctest: +REMOTE_DATA +IGNORE_WARNINGS + +.. + EXAMPLE END + + +Broadcasting Over Frame Data and Attributes +------------------------------------------- + +.. + EXAMPLE START + Broadcasting Over Frame Data and Attributes + +Frames in `astropy.coordinates` support +:doc:`Numpy broadcasting rules ` over both +frame data and frame attributes. This makes it easy and fast to do positional +astronomy calculations and transformations on sweeps of parameters. + +Where this really shines is doing fast observability calculations over arrays. +The following example constructs an `~astropy.coordinates.EarthLocation` array +of length :samp:`{L}`, a `~astropy.coordinates.SkyCoord` array of length +:samp:`{M}`, and a `~astropy.time.Time` array of length :samp:`N`. It uses +Numpy broadcasting rules to evaluate a boolean array of shape +:samp:`({L}, {M}, {N})` that is `True` for those observing locations, times, +and sky coordinates, for which the target is above an altitude limit:: + + >>> from astropy.coordinates import EarthLocation, AltAz, SkyCoord + >>> from astropy.coordinates.angles import uniform_spherical_random_surface + >>> from astropy.time import Time + >>> from astropy import units as u + >>> import numpy as np + + >>> L = 25 + >>> M = 100 + >>> N = 50 + + >>> # Earth locations of length L + >>> c = uniform_spherical_random_surface(L) + >>> locations = EarthLocation.from_geodetic(c.lon, c.lat) + + >>> # Celestial coordinates of length M + >>> coords = SkyCoord(uniform_spherical_random_surface(M)) + + >>> # Observation times of length N + >>> obstimes = Time('2023-08-04') + np.linspace(0, 24, N) * u.hour + + >>> # AltAz coordinates of shape (L, M, N) + >>> frame = AltAz( + ... location=locations[:, np.newaxis, np.newaxis], + ... obstime=obstimes[np.newaxis, np.newaxis, :]) + >>> altaz = coords[np.newaxis, :, np.newaxis].transform_to(frame) # doctest: +REMOTE_DATA + + >>> min_altitude = 30 * u.deg + >>> is_above_altitude_limit = (altaz.alt > min_altitude) # doctest: +REMOTE_DATA + >>> is_above_altitude_limit.shape # doctest: +REMOTE_DATA + (25, 100, 50) + +.. + EXAMPLE END + + +Improving Performance for Arrays of ``obstime`` +----------------------------------------------- + +The most expensive operations when transforming between observer-dependent coordinate +frames (e.g. ``AltAz``) and sky-fixed frames (e.g. ``ICRS``) are the calculation +of the orientation and position of Earth. + +If |SkyCoord| instances are transformed for a large number of closely spaced ``obstime``, +these calculations can be sped up by factors up to 100, whilst still keeping micro-arcsecond precision, +by utilizing interpolation instead of calculating Earth orientation parameters for each individual point. + +.. + EXAMPLE START + Improving performance for obstime arrays + +To use interpolation for the astrometric values in coordinate transformation, use:: + + >>> from astropy.coordinates import SkyCoord, EarthLocation, AltAz + >>> from astropy.coordinates.erfa_astrom import erfa_astrom, ErfaAstromInterpolator + >>> from astropy.time import Time + >>> from time import perf_counter + >>> import numpy as np + >>> import astropy.units as u + + + >>> # array with 10000 obstimes + >>> obstime = Time('2010-01-01T20:00') + np.linspace(0, 6, 10000) * u.hour + >>> location = EarthLocation(lon=-17.89 * u.deg, lat=28.76 * u.deg, height=2200 * u.m) + >>> frame = AltAz(obstime=obstime, location=location) + >>> crab = SkyCoord(ra='05h34m31.94s', dec='22d00m52.2s') + + >>> # transform with default transformation and print duration + >>> t0 = perf_counter() + >>> crab_altaz = crab.transform_to(frame) # doctest:+IGNORE_WARNINGS +REMOTE_DATA + >>> print(f'Transformation took {perf_counter() - t0:.2f} s') # doctest:+IGNORE_OUTPUT + Transformation took 1.77 s + + >>> # transform with interpolating astrometric values + >>> t0 = perf_counter() + >>> with erfa_astrom.set(ErfaAstromInterpolator(300 * u.s)): # doctest:+REMOTE_DATA + ... crab_altaz_interpolated = crab.transform_to(frame) # doctest:+IGNORE_WARNINGS +REMOTE_DATA + >>> print(f'Transformation took {perf_counter() - t0:.2f} s') # doctest:+IGNORE_OUTPUT + Transformation took 0.03 s + + >>> err = crab_altaz.separation(crab_altaz_interpolated) # doctest:+IGNORE_WARNINGS +REMOTE_DATA + >>> print(f'Mean error of interpolation: {err.to(u.microarcsecond).mean():.4f}') # doctest:+ELLIPSIS +REMOTE_DATA + Mean error of interpolation: 0.0... uarcsec + + >>> # To set erfa_astrom for a whole session, use it without context manager: + >>> erfa_astrom.set(ErfaAstromInterpolator(300 * u.s)) # doctest:+SKIP + +.. + EXAMPLE END + + +Here, we look into choosing an appropriate ``time_resolution``. +We will transform a single sky coordinate for lots of observation times from +``ICRS`` to ``AltAz`` and evaluate precision and runtime for different values +for ``time_resolution`` compared to the non-interpolating, default approach. + +.. plot:: + :include-source: + :context: reset + + from time import perf_counter + + import numpy as np + import matplotlib.pyplot as plt + + from astropy.coordinates.erfa_astrom import erfa_astrom, ErfaAstromInterpolator + from astropy.coordinates import SkyCoord, EarthLocation, AltAz + from astropy.time import Time + import astropy.units as u + + rng = np.random.default_rng(1337) + + n_coords = 10_000 + time_delta = 1 * u.hour + # n_coords times randomly distributed over time_delta + t = Time('2020-01-01T20:00:00') + rng.random(n_coords) * time_delta + + location = EarthLocation( + lon=-17.89 * u.deg, lat=28.76 * u.deg, height=2200 * u.m + ) + + # A celestial object in ICRS + # crab = SkyCoord.from_name("Crab Nebula") + crab = SkyCoord(83.6287, 22.0147, unit="deg") + + # target horizontal coordinate frame + altaz = AltAz(obstime=t, location=location) + + # the reference transform using no interpolation + t0 = perf_counter() + no_interp = crab.transform_to(altaz) + reference = perf_counter() - t0 + print(f'No Interpolation took {reference:.4f} s') + + # now the interpolating approach for different time resolutions + resolutions = 10.0**np.arange(-1, 5) * u.s + times = [] + seps = [] + + for resolution in resolutions: + with erfa_astrom.set(ErfaAstromInterpolator(resolution)): + t0 = perf_counter() + interp = crab.transform_to(altaz) + duration = perf_counter() - t0 + + print( + f'Interpolation with {resolution.value: 9.1f} {str(resolution.unit)}' + f' resolution took {duration:.4f} s' + f' ({reference / duration:5.1f}x faster) ' + ) + seps.append(no_interp.separation(interp)) + times.append(duration) + + seps = u.Quantity(seps) + + fig, (ax1, ax2) = plt.subplots( + nrows=2, + sharex=True, + gridspec_kw=dict(height_ratios=[2, 1]), + ) + + ax1.plot( + resolutions.to_value(u.s), + seps.mean(axis=1).to_value(u.microarcsecond), + 'o', label='mean', + ) + + for p in [25, 50, 75, 95]: + ax1.plot( + resolutions.to_value(u.s), + np.percentile(seps.to_value(u.microarcsecond), p, axis=1), + 'o', label=f'{p}%', color='C1', alpha=p / 100, + ) + + ax1.set_title(f'Transformation of SkyCoord with {n_coords:,} obstimes over {time_delta}') + + ax1.legend() + ax1.set_xscale('log') + ax1.set_yscale('log') + ax1.set_ylabel('Angular distance to no interpolation / Âĩas') + + ax2.plot(resolutions.to_value(u.s), reference / np.array(times), 's') + ax2.set_yscale('log') + ax2.set_ylabel('Speedup') + ax2.set_xlabel('time resolution / s') + + ax2.yaxis.grid() + fig.tight_layout() diff --git a/docs/coordinates/ref_api.rst b/docs/coordinates/ref_api.rst new file mode 100644 index 000000000000..dc8cbb202ec1 --- /dev/null +++ b/docs/coordinates/ref_api.rst @@ -0,0 +1,4 @@ +Reference/API +************* + +.. automodapi:: astropy.coordinates diff --git a/docs/coordinates/remote_methods.rst b/docs/coordinates/remote_methods.rst new file mode 100644 index 000000000000..0c641fe48a77 --- /dev/null +++ b/docs/coordinates/remote_methods.rst @@ -0,0 +1,74 @@ +.. _astropy-coordinates-remote: + +Usage Tips/Suggestions for Methods That Access Remote Resources +*************************************************************** + +There are currently two methods that rely on getting remote data to work. + +The first is the :class:`~astropy.coordinates.SkyCoord` :meth:`~astropy.coordinates.SkyCoord.from_name` method, which uses +`Sesame `_ to retrieve coordinates +for a particular named object:: + + >>> from astropy.coordinates import SkyCoord + >>> SkyCoord.from_name("PSR J1012+5307") # doctest: +REMOTE_DATA +FLOAT_CMP + + +.. testsetup:: + + >>> from astropy.coordinates import EarthLocation + >>> apo = EarthLocation(-1463969.3018517173, -5166673.342234327, 3434985.7120456537, unit='m') + +The second is the :class:`~astropy.coordinates.EarthLocation` :meth:`~astropy.coordinates.EarthLocation.of_site` method, which +provides a similar quick way to get an +:class:`~astropy.coordinates.EarthLocation` from an observatory name:: + + >>> from astropy.coordinates import EarthLocation + >>> apo = EarthLocation.of_site('Apache Point Observatory') # doctest: +SKIP + >>> apo # doctest: +FLOAT_CMP + + +The full list of available observatory names can be obtained with + :meth:`astropy.coordinates.EarthLocation.get_site_names`. + +.. testsetup:: + + >>> loc = EarthLocation(-1994502.60430614, -5037538.54232911, 3358104.99690298, unit='m') + +While these methods are convenient, there are several considerations to take +into account: + +* Since these methods access online data, the data may evolve over time (for + example, the accuracy of coordinates might improve, and new observatories + may be added). Therefore, this means that a script using these and currently + running may give a different answer in five years. Therefore, users concerned + with reproducibility should not use these methods in their final scripts, + but can instead use them to get the values required, and then hard-code them + into the scripts. For example, we can check the coordinates of the Kitt + Peak Observatories using:: + + >>> loc = EarthLocation.of_site('Kitt Peak') # doctest: +SKIP + + Note that this command requires an internet connection. + + We can then view the actual Cartesian coordinates for the observatory: + + >>> loc # doctest: +FLOAT_CMP + + + This can then be converted into code:: + + >>> loc = EarthLocation(-1994502.6043061386, -5037538.54232911, 3358104.9969029757, unit='m') + + This latter line can then be included in a script and will ensure that the + results stay the same over time. + +* The online data may not be accurate enough for your purposes. If maximum + accuracy is paramount, we recommend that you determine the celestial or + Earth coordinates yourself and hard-code these, rather than using the + convenience methods. + +* These methods will not function if an internet connection is not available. + Therefore, if you need to work on a script while offline, follow the + instructions in the first bullet point above to hard-code the coordinates + before going offline. diff --git a/docs/coordinates/representations.rst b/docs/coordinates/representations.rst new file mode 100644 index 000000000000..30169f77b991 --- /dev/null +++ b/docs/coordinates/representations.rst @@ -0,0 +1,749 @@ +.. _astropy-coordinates-representations: + +Using and Designing Coordinate Representations +********************************************** + +Points in a 3D vector space can be represented in different ways, such as +Cartesian, spherical polar, cylindrical, and so on. These underlie the way +coordinate data in `astropy.coordinates` is represented, as described in the +:ref:`astropy-coordinates-overview`. Below, we describe how you can use them on +their own as a way to convert between different representations, including +ones not built-in, and to do simple vector arithmetic. + +The built-in representation classes are: + +* `~astropy.coordinates.CartesianRepresentation`: Cartesian + coordinates ``x``, ``y``, and ``z``. +* `~astropy.coordinates.SphericalRepresentation`: spherical + polar coordinates represented by a longitude (``lon``), a latitude + (``lat``), and a distance (``distance``). The latitude is a value ranging + from -90 to 90 degrees. +* `~astropy.coordinates.UnitSphericalRepresentation`: + spherical polar coordinates on a unit sphere, represented by a longitude + (``lon``) and latitude (``lat``). +* `~astropy.coordinates.PhysicsSphericalRepresentation`: + spherical polar coordinates, represented by an inclination (``theta``) and + azimuthal angle (``phi``), and radius ``r``. The inclination goes from 0 to + 180 degrees, and is related to the latitude in the + `~astropy.coordinates.SphericalRepresentation` by + ``theta = 90 deg - lat``. +* `~astropy.coordinates.CylindricalRepresentation`: + cylindrical polar coordinates, represented by a cylindrical radius + (``rho``), azimuthal angle (``phi``), and height (``z``). + + +Astropy also offers a `~astropy.coordinates.BaseGeodeticRepresentation` and +a `~astropy.coordinates.BaseBodycentricRepresentation` useful to +create specific representations on spheroidal bodies. + +`~astropy.coordinates.BaseGeodeticRepresentation` is the coordinate representation on +a surface of a spheroid (an ellipsoid with equal +equatorial radii), represented by a longitude (``lon``) a geodetic latitude (``lat``) +and a height (``height``) above the surface. +The geodetic latitude is defined by the angle +between the vertical to the surface at a specific point of the spheroid and its +projection onto the equatorial plane. +The latitude is a value ranging from -90 to 90 degrees, the longitude from 0 to 360 +degrees, the height is the elevation above the surface of the spheroid (measured +perpendicular to the surface). + +`~astropy.coordinates.BaseBodycentricRepresentation` is the coordinate representation +recommended by the Cartographic Coordinates & Rotational Elements Working Group +(see for example its `2019 report `_): the bodycentric latitude +and longitude are spherical latitude and longitude relative to the barycenter of the +body, the height is the distance from the spheroid surface (measured radially). +The latitude is a value ranging from -90 to 90 degrees, the longitude from 0 to 360 +degrees. + +`~astropy.coordinates.BaseGeodeticRepresentation` is used internally for the standard +Earth ellipsoids used in +`~astropy.coordinates.EarthLocation` +(`~astropy.coordinates.WGS84GeodeticRepresentation`, +`~astropy.coordinates.WGS72GeodeticRepresentation`, and +`~astropy.coordinates.GRS80GeodeticRepresentation`). +`~astropy.coordinates.BaseGeodeticRepresentation` and +`~astropy.coordinates.BaseBodycentricRepresentation` +can be customized as described in :ref:`astropy-coordinates-create-geodetic`. + +.. Note:: + For information about using and changing the representation of + `~astropy.coordinates.SkyCoord` objects, see the + :ref:`astropy-skycoord-representations` section. + +Instantiating and Converting +============================ + +Representation classes are instantiated with `~astropy.units.Quantity` +objects:: + + >>> from astropy import units as u + >>> from astropy.coordinates.representation import CartesianRepresentation + >>> car = CartesianRepresentation(3 * u.kpc, 5 * u.kpc, 4 * u.kpc) + >>> car # doctest: +FLOAT_CMP + + +Array `~astropy.units.Quantity` objects can also be passed to +representations. They will have the expected shape, which can be changed using +methods with the same names as those for `~numpy.ndarray`, such as ``reshape``, +``ravel``, etc.:: + + >>> x = u.Quantity([[1., 0., 0.], [3., 5., 3.]], u.m) + >>> y = u.Quantity([[0., 2., 0.], [4., 0., -4.]], u.m) + >>> z = u.Quantity([[0., 0., 3.], [0., 12., -12.]], u.m) + >>> car_array = CartesianRepresentation(x, y, z) + >>> car_array # doctest: +FLOAT_CMP + + >>> car_array.shape + (2, 3) + >>> car_array.ravel() # doctest: +FLOAT_CMP + + +Representations can be converted to other representations using the +``represent_as`` method:: + + >>> from astropy.coordinates.representation import SphericalRepresentation, CylindricalRepresentation + >>> sph = car.represent_as(SphericalRepresentation) + >>> sph # doctest: +FLOAT_CMP + + >>> cyl = car.represent_as(CylindricalRepresentation) + >>> cyl # doctest: +FLOAT_CMP + + +All representations can be converted to each other without loss of +information, with the exception of +`~astropy.coordinates.UnitSphericalRepresentation`. This class +is used to store the longitude and latitude of points but does not contain +any distance to the points, and assumes that they are located on a unit and +dimensionless sphere:: + + >>> from astropy.coordinates.representation import UnitSphericalRepresentation + >>> sph_unit = car.represent_as(UnitSphericalRepresentation) + >>> sph_unit # doctest: +FLOAT_CMP + + +Converting back to Cartesian, the absolute scaling information has been +removed, and the points are still located on a unit sphere:: + + >>> sph_unit = car.represent_as(UnitSphericalRepresentation) + >>> sph_unit.represent_as(CartesianRepresentation) # doctest: +FLOAT_CMP + + + +Array Values and NumPy Array Method Analogs +=========================================== + +Array `~astropy.units.Quantity` objects can also be passed to representations, +and such representations can be sliced, reshaped, etc., using the same methods +as are available to `~numpy.ndarray`. Corresponding functions, as well as +others that affect the shape, such as `~numpy.atleast_1d` and +`~numpy.rollaxis`, work as expected. + +Example +------- + +.. + EXAMPLE START + Array Values and NumPy Array Method Analogs + +To pass array `~astropy.units.Quantity` objects to representations:: + + >>> import numpy as np + >>> x = np.linspace(0., 5., 6) + >>> y = np.linspace(10., 15., 6) + >>> z = np.linspace(20., 25., 6) + >>> car_array = CartesianRepresentation(x * u.m, y * u.m, z * u.m) + >>> car_array + + +To manipulate using methods and ``numpy`` functions:: + + >>> car_array.reshape(3, 2) + + >>> car_array[2] + + >>> car_array[2] = car_array[1] + >>> car_array[:3] + + >>> np.roll(car_array, 1) + + +And to set elements using other representation classes (as long +as they are compatible in their units and number of dimensions):: + + >>> car_array[2] = SphericalRepresentation(0*u.deg, 0*u.deg, 99*u.m) + >>> car_array[:3] # doctest: +FLOAT_CMP + + >>> car_array[0] = UnitSphericalRepresentation(0*u.deg, 0*u.deg) + Traceback (most recent call last): + ... + ValueError: value must be representable as CartesianRepresentation without loss of information. + +.. + EXAMPLE END + +.. _astropy-coordinates-representations-arithmetic: + +Vector Arithmetic +================= + +Representations support basic vector arithmetic such as taking the norm, +multiplying with and dividing by quantities, and taking dot and cross products, +as well as adding, subtracting, summing and taking averages of representations, +and multiplying with matrices. + +.. Note:: All arithmetic except the matrix multiplication works with + non-Cartesian representations as well. For taking the norm, multiplication, + and division, this uses just the non-angular components, while for the other + operations the representation is converted to Cartesian internally before + the operation is done, and the result is converted back to the original + representation. Hence, for optimal speed it may be best to work using + Cartesian representations. + +Examples +-------- + +.. + EXAMPLE START + Vector Arithmetic Operations with Representation Objects + +To see how vector arithmetic operations work with representation objects, +consider the following examples:: + + >>> car_array = CartesianRepresentation([[1., 0., 0.], [3., 5., 3.]] * u.m, + ... [[0., 2., 0.], [4., 0., -4.]] * u.m, + ... [[0., 0., 3.], [0.,12.,-12.]] * u.m) + >>> car_array # doctest: +FLOAT_CMP + + >>> car_array.norm() # doctest: +FLOAT_CMP + + >>> car_array / car_array.norm() # doctest: +FLOAT_CMP + + >>> (car_array[1] - car_array[0]) / (10. * u.s) # doctest: +FLOAT_CMP + + >>> car_array.sum() # doctest: +FLOAT_CMP + + >>> car_array.mean(axis=0) # doctest: +FLOAT_CMP + + + >>> unit_x = UnitSphericalRepresentation(0.*u.deg, 0.*u.deg) + >>> unit_y = UnitSphericalRepresentation(90.*u.deg, 0.*u.deg) + >>> unit_z = UnitSphericalRepresentation(0.*u.deg, 90.*u.deg) + >>> car_array.dot(unit_x) # doctest: +FLOAT_CMP + + >>> car_array.dot(unit_y) # doctest: +FLOAT_CMP + + >>> car_array.dot(unit_z) # doctest: +FLOAT_CMP + + >>> car_array.cross(unit_x) # doctest: +FLOAT_CMP + + + >>> from astropy.coordinates import rotation_matrix + >>> rotation = rotation_matrix(90 * u.deg, axis='z') + >>> rotation # doctest: +FLOAT_CMP + array([[ 6.12323400e-17, 1.00000000e+00, 0.00000000e+00], + [-1.00000000e+00, 6.12323400e-17, 0.00000000e+00], + [ 0.00000000e+00, 0.00000000e+00, 1.00000000e+00]]) + >>> car_array.transform(rotation) # doctest: +FLOAT_CMP + + +.. + EXAMPLE END + +.. _astropy-coordinates-differentials: + +Differentials and Derivatives of Representations +================================================ + +In addition to positions in 3D space, coordinates also deal with proper motions +and radial velocities, which require a way to represent differentials of +coordinates (i.e., finite realizations) of derivatives. To support this, the +representations all have corresponding ``Differential`` classes, which can hold +offsets or derivatives in terms of the components of the representation class. +Adding such an offset to a representation means the offset is taken in the +direction of the corresponding coordinate. (Although for any representation +other than Cartesian, this is only defined relative to a specific location, as +the unit vectors are not invariant.) + +Examples +-------- + +.. + EXAMPLE START + Differentials and Derivatives of Representations + +To see how the ``Differential`` classes of representations works, consider the +following:: + + >>> from astropy.coordinates import SphericalRepresentation, SphericalDifferential + >>> sph_coo = SphericalRepresentation(lon=0.*u.deg, lat=0.*u.deg, + ... distance=1.*u.kpc) + >>> sph_derivative = SphericalDifferential(d_lon=1.*u.arcsec/u.yr, + ... d_lat=0.*u.arcsec/u.yr, + ... d_distance=0.*u.km/u.s) + >>> sph_derivative.to_cartesian(base=sph_coo) # doctest: +FLOAT_CMP + + +Note how the conversion to Cartesian can only be done using a ``base``, since +otherwise the code cannot know what direction an increase in longitude +corresponds to. For ``lon=0``, this is in the ``y`` direction. Now, to get +the coordinates at two later times:: + + >>> sph_coo + sph_derivative * [1., 3600*180/np.pi] * u.yr # doctest: +FLOAT_CMP + + +The above shows how addition is not to longitude itself, but in the direction +of increasing longitude: for the large shift, by the equivalent of one radian, +the distance has increased as well (after all, a source will likely not move +along a curve on the sky!). This also means that the order of operations is +important:: + + >>> big_offset = SphericalDifferential(1.*u.radian, 0.*u.radian, 0.*u.kpc) + >>> sph_coo + big_offset + big_offset # doctest: +FLOAT_CMP + + >>> sph_coo + (big_offset + big_offset) # doctest: +FLOAT_CMP + + +.. + EXAMPLE END + +.. + EXAMPLE START + Working with Proper Motions and Radial Velocities in Differential Objects + +Often, you may have just a proper motion or a radial velocity, but not both:: + + >>> from astropy.coordinates import UnitSphericalDifferential, RadialDifferential + >>> radvel = RadialDifferential(1000*u.km/u.s) + >>> sph_coo + radvel * 1. * u.Myr # doctest: +FLOAT_CMP + + >>> pm = UnitSphericalDifferential(1.*u.mas/u.yr, 0.*u.mas/u.yr) + >>> sph_coo + pm * 1. * u.Myr # doctest: +FLOAT_CMP + + >>> pm + radvel # doctest: +FLOAT_CMP + + >>> sph_coo + (pm + radvel) * 1. * u.Myr # doctest: +FLOAT_CMP + + +Note in the above that the proper motion is defined strictly as a change in +longitude (i.e., it does not include a ``cos(latitude)`` term). There are +special classes where this term is included:: + + >>> from astropy.coordinates import UnitSphericalCosLatDifferential + >>> sph_lat60 = SphericalRepresentation(lon=0.*u.deg, lat=60.*u.deg, + ... distance=1.*u.kpc) + >>> pm = UnitSphericalDifferential(1.*u.mas/u.yr, 0.*u.mas/u.yr) + >>> pm # doctest: +FLOAT_CMP + + >>> pm_coslat = UnitSphericalCosLatDifferential(1.*u.mas/u.yr, 0.*u.mas/u.yr) + >>> pm_coslat # doctest: +FLOAT_CMP + + >>> sph_lat60 + pm * 1. * u.Myr # doctest: +FLOAT_CMP + + >>> sph_lat60 + pm_coslat * 1. * u.Myr # doctest: +FLOAT_CMP + + +Close inspections shows that indeed the changes are as expected. The systems +with and without ``cos(latitude)`` can be converted to each other, provided you +supply the ``base`` (representation):: + + >>> usph_lat60 = sph_lat60.represent_as(UnitSphericalRepresentation) + >>> pm_coslat2 = pm.represent_as(UnitSphericalCosLatDifferential, + ... base=usph_lat60) + >>> pm_coslat2 # doctest: +FLOAT_CMP + + >>> sph_lat60 + pm_coslat2 * 1. * u.Myr # doctest: +FLOAT_CMP + + +.. Note:: At present, the differential classes are generally meant to work with + first derivatives, but they do not check the units of the inputs to enforce + this. Passing in second derivatives (e.g., acceleration values with + acceleration units) will succeed, but any transformations that occur through + re-representation of the differential will not necessarily be correct. + +.. + EXAMPLE END + +Attaching ``Differential`` Objects to ``Representation`` Objects +================================================================ + +``Differential`` objects can be attached to ``Representation`` objects as a way +to encapsulate related information into a single object. ``Differential`` +objects can be passed in to the initializer of any of the built-in +``Representation`` classes. + +Example +------- + +.. + EXAMPLE START + Attaching Differential Objects to Representation Objects + +To store a single velocity differential with a position:: + + >>> from astropy.coordinates import representation as r + >>> dif = r.SphericalDifferential(d_lon=1 * u.mas/u.yr, + ... d_lat=2 * u.mas/u.yr, + ... d_distance=3 * u.km/u.s) + >>> rep = r.SphericalRepresentation(lon=0.*u.deg, lat=0.*u.deg, + ... distance=1.*u.kpc, + ... differentials=dif) + >>> rep # doctest: +FLOAT_CMP + + >>> rep.differentials # doctest: +FLOAT_CMP + {'s': } + +.. + EXAMPLE END + +The ``Differential`` objects are stored as a Python dictionary on the +``Representation`` object with keys equal to the (string) unit with which the +differential derivatives are taken (converted to SI). + +.. + EXAMPLE START + Differential and Representation Object Storage + +In this case the key is ``'s'`` (second) because the ``Differential`` units are +velocities, a time derivative. Passing a single differential to the +``Representation`` initializer will automatically generate the necessary key +and store it in the differentials dictionary, but a dictionary is required to +specify multiple differentials:: + + >>> dif2 = r.SphericalDifferential(d_lon=4 * u.mas/u.yr**2, + ... d_lat=5 * u.mas/u.yr**2, + ... d_distance=6 * u.km/u.s**2) + >>> rep = r.SphericalRepresentation(lon=0.*u.deg, lat=0.*u.deg, + ... distance=1.*u.kpc, + ... differentials={'s': dif, 's2': dif2}) + >>> rep.differentials['s'] # doctest: +FLOAT_CMP + + >>> rep.differentials['s2'] # doctest: +FLOAT_CMP + + +.. + EXAMPLE END + +.. + EXAMPLE START + Attaching Differential Objects to a Representation after Creation + +``Differential`` objects can also be attached to a ``Representation`` after +creation:: + + >>> rep = r.CartesianRepresentation(x=1 * u.kpc, y=2 * u.kpc, z=3 * u.kpc) + >>> dif = r.CartesianDifferential(*[1, 2, 3] * u.km/u.s) + >>> rep = rep.with_differentials(dif) + >>> rep # doctest: +FLOAT_CMP + + +This works for array data as well, as long as the shape of the +``Differential`` data is the same as that of the ``Representation``:: + + >>> xyz = np.arange(12).reshape(3, 4) * u.au + >>> d_xyz = np.arange(12).reshape(3, 4) * u.km/u.s + >>> rep = r.CartesianRepresentation(*xyz) + >>> dif = r.CartesianDifferential(*d_xyz) + >>> rep = rep.with_differentials(dif) + >>> rep # doctest: +FLOAT_CMP + + +.. + EXAMPLE END + +.. + EXAMPLE START + Converting Positional Data to a New Representation + +As with a ``Representation`` instance without a differential, to convert the +positional data to a new representation, use the ``.represent_as()``:: + + >>> rep.represent_as(r.SphericalRepresentation) # doctest: +FLOAT_CMP + + +However, by passing just the desired representation class, only the +``Representation`` has changed, and the differentials are dropped. To +re-represent both the ``Representation`` and any ``Differential`` objects, you +must specify target classes for the ``Differential`` as well:: + + >>> rep2 = rep.represent_as(r.SphericalRepresentation, r.SphericalDifferential) + >>> rep2 # doctest: +FLOAT_CMP + + >>> rep2.differentials['s'] # doctest: +FLOAT_CMP + + +.. + EXAMPLE END + +.. + EXAMPLE START + Shape-Changing Operations with Differential Objects + +Shape-changing operations (e.g., reshapes) are propagated to all +``Differential`` objects because they are guaranteed to have the same shape as +their host ``Representation`` object:: + + >>> rep.shape + (4,) + >>> rep.differentials['s'].shape + (4,) + >>> new_rep = rep.reshape(2, 2) + >>> new_rep.shape + (2, 2) + >>> new_rep.differentials['s'].shape + (2, 2) + +This also works for slicing:: + + >>> new_rep = rep[:2] + >>> new_rep.shape + (2,) + >>> new_rep.differentials['s'].shape + (2,) + +Operations on representations that return `~astropy.units.Quantity` objects (as +opposed to other ``Representation`` instances) still work, but only operate on +the positional information, for example:: + + >>> rep.norm() # doctest: +FLOAT_CMP + + +Operations that involve combining or scaling representations or pairs of +representation objects that contain differentials will currently fail, but +support for some operations may be added in future versions:: + + >>> rep + rep + Traceback (most recent call last): + ... + TypeError: Operation 'add' is not supported when differentials are attached to a CartesianRepresentation. + +If you have a ``Representation`` with attached ``Differential`` objects, you +can retrieve a copy of the ``Representation`` without the ``Differential`` +object and use this ``Differential``-free object for any arithmetic operation:: + + >>> 15 * rep.without_differentials() # doctest: +FLOAT_CMP + + +.. + EXAMPLE END + +.. _astropy-coordinates-create-repr: + +Creating Your Own Representations +================================= + +To create your own representation class, your class must inherit from the +`~astropy.coordinates.BaseRepresentation` class. This base has an ``__init__`` +method that will put all arguments components through their initializers, +verify they can be broadcast against each other, and store the components on +``self`` as the name prefixed with '_'. Furthermore, through its metaclass it +provides default properties for the components so that they can be accessed +using ``.``. For the machinery to work, the following +must be defined: + +* ``attr_classes`` class attribute (:class:`dict`): + + Defines through its keys the names of the components (as well as the default + order), and through its values defines the class of which they should be + instances (which should be `~astropy.units.Quantity` or a subclass, or + anything that can initialize it). + +* ``from_cartesian`` class method: + + Takes a `~astropy.coordinates.CartesianRepresentation` object and + returns an instance of your class. + +* ``to_cartesian`` method: + + Returns a `~astropy.coordinates.CartesianRepresentation` object. + +* ``__init__`` method (optional): + + If you want more than the basic initialization and checks provided by the + base representation class, or just an explicit signature, you can define your + own ``__init__``. In general, it is recommended to stay close to the + signature assumed by the base representation, ``__init__(self, comp1, comp2, + comp3, copy=True)``, and use ``super`` to call the base representation + initializer. + +Once you do this, you will then automatically be able to call ``represent_as`` +to convert other representations to/from your representation class. Your +representation will also be available for use in |SkyCoord| and all frame +classes. + +A representation class may also have a ``_unit_representation`` attribute +(although it is not required). This attribute points to the appropriate +"unit" representation (i.e., a representation that is dimensionless). This is +probably only meaningful for subclasses of +`~astropy.coordinates.SphericalRepresentation`, where it is assumed that it +will be a subclass of `~astropy.coordinates.UnitSphericalRepresentation`. + +Finally, if you wish to also use offsets in your coordinate system, two further +methods should be defined (please see +`~astropy.coordinates.SphericalRepresentation` for an example): + +* ``unit_vectors`` method: + + Returns a ``dict`` with a + `~astropy.coordinates.CartesianRepresentation` of unit vectors in the + direction of each component. + +* ``scale_factors`` method: + + Returns a ``dict`` with a `~astropy.units.Quantity` for each component with + the appropriate physical scale factor for a unit change in that direction. + +And furthermore you should define a ``Differential`` class based on +`~astropy.coordinates.BaseDifferential`. This class only needs to define: + +* ``base_representation`` attribute: + + A link back to the representation for which this differential holds. + + +In pseudo-code, this means that a class will look like:: + + class MyRepresentation(BaseRepresentation): + + attr_classes = { + "comp1": ComponentClass1, + "comp2": ComponentClass2, + "comp3": ComponentClass3, + } + + # __init__ is optional + def __init__(self, comp1, comp2, comp3, copy=True): + super().__init__(comp1, comp2, comp3, copy=copy) + ... + + @classmethod + def from_cartesian(self, cartesian): + ... + return MyRepresentation(...) + + def to_cartesian(self): + ... + return CartesianRepresentation(...) + + # if differential motion is needed + def unit_vectors(self): + ... + return {'comp1': CartesianRepresentation(...), + 'comp2': CartesianRepresentation(...), + 'comp3': CartesianRepresentation(...)} + + def scale_factors(self): + ... + return {'comp1': ..., + 'comp2': ..., + 'comp3': ...} + + class MyDifferential(BaseDifferential): + base_representation = MyRepresentation + +.. _astropy-coordinates-create-geodetic: + +Creating Your Own Geodetic and Bodycentric Representations +---------------------------------------------------------- + +If you would like to use geodetic coordinates on planetary bodies other than the Earth, +you can define a new class that inherits from +`~astropy.coordinates.BaseGeodeticRepresentation` or +`~astropy.coordinates.BaseBodycentricRepresentation`. +The equatorial radius and flattening must be both assigned via the attributes +`_equatorial_radius` and `_flattening`. + +For example the spheroid describing Mars as in the +`1979 IAU standard `_ could be defined like:: + + class IAUMARS1979GeodeticRepresentation(BaseGeodeticRepresentation): + + _equatorial_radius = 3393400.0 * u.m + _flattening = 0.518650 * u.percent + +The bodycentric coordinate system representing Mars as in the +`2000 IAU standard `_ could be defined as:: + + class IAUMARS2000BodycentricRepresentation(BaseBodycentricRepresentation): + + _equatorial_radius = 3396190.0 * u.m + _flattening = 0.5886008 * u.percent diff --git a/docs/coordinates/satellites.rst b/docs/coordinates/satellites.rst new file mode 100644 index 000000000000..125377f3c371 --- /dev/null +++ b/docs/coordinates/satellites.rst @@ -0,0 +1,133 @@ +.. _astropy-coordinates-satellites: + +Working with Earth Satellites Using Astropy Coordinates +******************************************************* +This document discusses Two-Line Element ephemerides and the True Equator, Mean Equinox frame. +For satellite ephemerides given directly in geocentric ITRS coordinates +(e.g. `ILRS ephemeris format `_) +please see the example transform to `~astropy.coordinates.AltAz` below starting with +the geocentric ITRS coordinate frame. + +Satellite data is normally provided in the Two-Line Element (TLE) format +(see `here `_ +for a definition). These datasets are designed to be used in combination +with a theory for orbital propagation model to predict the positions +of satellites. + +The history of such models is discussed in detail in +`Vallado et al (2006) `_ +who also provide a reference implementation of the SGP4 orbital propagation +code, designed to be compatible with the TLE sets provided by the United +States Department of Defense, which are available from a source like +`Celestrak `_. + +The output coordinate frame of the SGP4 model is the True Equator, Mean Equinox +frame (TEME), which is one of the frames built-in to `astropy.coordinates`. +TEME is an Earth-centered inertial frame (i.e., it does not rotate with respect +to the stars). Several definitions exist; ``astropy`` uses the implementation described +in `Vallado et al (2006) `_. + +Finding TEME Coordinates from TLE Data +====================================== + +There is currently no support in `astropy.coordinates` for computing satellite orbits +from TLE orbital element sets. Full support for handling TLE files is available in +the `Skyfield `_ library, but some advice for dealing +with satellite data in ``astropy`` is below. + +.. EXAMPLE START Using sgp4 to get a TEME coordinate + +You will need some external library to compute the position and velocity of the satellite from the +TLE orbital elements. The `SGP4 `_ library can do this. An example +of using this library to find the `~astropy.coordinates.TEME` coordinates of a satellite is: + +.. doctest-requires:: sgp4 + + >>> from sgp4.api import Satrec + >>> from sgp4.api import SGP4_ERRORS + >>> s = '1 25544U 98067A 19343.69339541 .00001764 00000-0 38792-4 0 9991' + >>> t = '2 25544 51.6439 211.2001 0007417 17.6667 85.6398 15.50103472202482' + >>> satellite = Satrec.twoline2rv(s, t) + +The ``satellite`` object has a method, ``satellite.sgp4``, that will try to compute the TEME position +and velocity at a given time: + +.. doctest-requires:: sgp4 + + >>> from astropy.time import Time + >>> t = Time(2458827.362605, format='jd') + >>> error_code, teme_p, teme_v = satellite.sgp4(t.jd1, t.jd2) # in km and km/s + >>> if error_code != 0: + ... raise RuntimeError(SGP4_ERRORS[error_code]) + +Now that we have the position and velocity in kilometers and kilometers per second, we can create a +position in the `~astropy.coordinates.TEME` reference frame: + +.. doctest-requires:: sgp4 + + >>> from astropy.coordinates import TEME, CartesianDifferential, CartesianRepresentation + >>> from astropy import units as u + >>> teme_p = CartesianRepresentation(teme_p*u.km) + >>> teme_v = CartesianDifferential(teme_v*u.km/u.s) + >>> teme = TEME(teme_p.with_differentials(teme_v), obstime=t) + +.. EXAMPLE END + +Note how we are careful to set the observed time of the `~astropy.coordinates.TEME` frame to +the time at which we calculated satellite position. + +Transforming TEME to Other Coordinate Systems +============================================= + +Once you have satellite positions in `~astropy.coordinates.TEME` coordinates they can be transformed +into any `astropy.coordinates` frame. + +For example, to find the overhead latitude, longitude, and height of the satellite: + +.. EXAMPLE START Transforming TEME + +.. doctest-requires:: sgp4 + + >>> from astropy.coordinates import ITRS + >>> itrs_geo = teme.transform_to(ITRS(obstime=t)) + >>> location = itrs_geo.earth_location + >>> location.geodetic # doctest: +FLOAT_CMP + GeodeticLocation(lon=, lat=, height=) + +.. testsetup:: + + >>> from astropy.coordinates import EarthLocation + >>> siding_spring = EarthLocation(-4680888.60272112, 2805218.44653429, -3292788.0804506, unit='m') + +Or, if you want to find the altitude and azimuth of the satellite from a particular location: + +.. note:: + In this example, the intermediate step of manually setting up a topocentric `~astropy.coordinates.ITRS` + frame is necessary in order to avoid the change in stellar aberration that would occur if a direct + transform from geocentric to topocentric coordinates using ``transform_to`` was used. Please see + the documentation of the `~astropy.coordinates.ITRS` frame for more details. + +.. doctest-requires:: sgp4 + + >>> from astropy.coordinates import EarthLocation, AltAz + >>> siding_spring = EarthLocation.of_site('aao') # doctest: +SKIP + >>> topo_itrs_repr = itrs_geo.cartesian.without_differentials() - siding_spring.get_itrs(t).cartesian + >>> itrs_topo = ITRS(topo_itrs_repr, obstime = t, location=siding_spring) + >>> aa = itrs_topo.transform_to(AltAz(obstime=t, location=siding_spring)) + >>> aa.alt # doctest: +FLOAT_CMP + + >>> aa.az # doctest: +FLOAT_CMP + + +For a stationary observer, velocity in the `~astropy.coordinates.ITRS` is independent of location, +so if you want to carry the velocity to the topocentric frame, you can do so as follows: + +.. doctest-requires:: sgp4 + + >>> itrs_geo_p = itrs_geo.cartesian.without_differentials() + >>> itrs_geo_v = itrs_geo.cartesian.differentials['s'] + >>> topo_itrs_p = itrs_geo_p - siding_spring.get_itrs(t).cartesian + >>> topo_itrs_repr = topo_itrs_p.with_differentials(itrs_geo_v) + >>> itrs_topo = ITRS(topo_itrs_repr, obstime = t, location=siding_spring) + +.. EXAMPLE END diff --git a/docs/coordinates/skycoord.rst b/docs/coordinates/skycoord.rst new file mode 100644 index 000000000000..bb99f84c2aac --- /dev/null +++ b/docs/coordinates/skycoord.rst @@ -0,0 +1,1260 @@ +.. _astropy-coordinates-high-level: + +Using the SkyCoord High-Level Class +*********************************** + +The |SkyCoord| class provides a simple and flexible user interface for +celestial coordinate representation, manipulation, and transformation between +coordinate frames. This is a high-level class that serves as a wrapper +around the low-level coordinate frame classes like `~astropy.coordinates.ICRS` +and `~astropy.coordinates.FK5` which do most of the heavy lifting. + +The key distinctions between |SkyCoord| and the low-level classes +(:doc:`frames`) are as follows: + +- The |SkyCoord| object can maintain the union of frame attributes for all + built-in and user-defined coordinate frames in the + ``astropy.coordinates.frame_transform_graph``. Individual frame classes hold + only the required attributes (e.g., equinox, observation time, or observer + location) for that frame. This means that a transformation from + `~astropy.coordinates.FK4` (with equinox and observation time) to + `~astropy.coordinates.ICRS` (with neither) and back to + `~astropy.coordinates.FK4` via the low-level classes would not remember the + original equinox and observation time. Since the |SkyCoord| object stores + all attributes, such a round-trip transformation will return to the same + coordinate object. + +- The |SkyCoord| class is more flexible with inputs to accommodate a wide + variety of user preferences and available data formats, whereas the frame + classes expect to receive quantity-like objects with angular units. + +- The |SkyCoord| class has a number of convenience methods that are useful + in typical analysis. + +- At present, |SkyCoord| objects can use only coordinate frames that have + transformations defined in the ``astropy.coordinates.frame_transform_graph`` + transform graph object. + +Creating SkyCoord Objects +========================= + +The |SkyCoord| class accepts a wide variety of inputs for initialization. +At a minimum, these must provide one or more celestial coordinate values +with unambiguous units. Typically you must also specify the coordinate +frame, though this is not required. + +Common patterns are shown below. In this description the values in upper +case like ``COORD`` or ``FRAME`` represent inputs which are described in detail +in the `Initialization Syntax`_ section. Elements in square brackets like +``[unit=UNIT]`` are optional. +:: + + SkyCoord(COORD, [FRAME], keyword_args ...) + SkyCoord(LON, LAT, [frame=FRAME], [unit=UNIT], keyword_args ...) + SkyCoord([FRAME], =LON, =LAT, keyword_args ...) + +The examples below illustrate common ways of initializing a |SkyCoord| object. +These all reflect initializing using spherical coordinates, which is the +default for all built-in frames. In order to understand working with coordinates +using a different representation, such as Cartesian or cylindrical, see the +section on `Representations`_. First, some imports:: + + >>> from astropy.coordinates import SkyCoord # High-level coordinates + >>> from astropy.coordinates import ICRS, Galactic, FK4, FK5 # Low-level frames + >>> from astropy.coordinates import Angle, Latitude, Longitude # Angles + >>> import astropy.units as u + >>> import numpy as np + +Examples +-------- + +.. + EXAMPLE START + Initializing SkyCoord Objects Using Spherical Coordinates + +The coordinate values and frame specification can be provided using +positional and keyword arguments. First we show positional arguments for +RA and Dec:: + + >>> SkyCoord(10, 20, unit='deg') # Defaults to ICRS # doctest: +FLOAT_CMP + + + >>> SkyCoord([1, 2, 3], [-30, 45, 8], frame='icrs', unit='deg') # doctest: +FLOAT_CMP + + +Notice that the first example above does not explicitly give a frame. In +this case, the default is taken to be the ICRS system (approximately +correct for "J2000" equatorial coordinates). It is always better to +explicitly specify the frame when it is known to be ICRS, however, as +anyone reading the code will be better able to understand the intent. + +String inputs in common formats are acceptable, and the frame can be supplied +as either a class type like `~astropy.coordinates.FK4`, an instance of a +frame class, a `~astropy.coordinates.SkyCoord` instance (from which the frame +will be extracted), or the lowercase version of a frame name as a string, for +example, ``"fk4"``:: + + >>> coords = ["1:12:43.2 +1:12:43", "1 12 43.2 +1 12 43"] + >>> sc = SkyCoord(coords, frame=FK4, unit=(u.hourangle, u.deg), obstime="J1992.21") + >>> sc = SkyCoord(coords, frame=FK4(obstime="J1992.21"), unit=(u.hourangle, u.deg)) + >>> sc = SkyCoord(coords, frame='fk4', unit='hourangle,deg', obstime="J1992.21") + + >>> sc = SkyCoord("1h12m43.2s", "+1d12m43s", frame=Galactic) # Units from strings + >>> sc = SkyCoord("1h12m43.2s +1d12m43s", frame=Galactic) # Units from string + >>> sc = SkyCoord(l="1h12m43.2s", b="+1d12m43s", frame='galactic') + >>> sc = SkyCoord("1h12.72m +1d12.71m", frame='galactic') + +Note that frame instances with data and `~astropy.coordinates.SkyCoord` +instances can only be passed as frames using the ``frame=`` keyword argument +and not as positional arguments. + +For representations that have ``ra`` and ``dec`` attributes you can supply a +coordinate string in a number of other common formats. Examples include:: + + >>> sc = SkyCoord("15h17+89d15") + >>> sc = SkyCoord("275d11m15.6954s+17d59m59.876s") + >>> sc = SkyCoord("8 00 -5 00.6", unit=(u.hour, u.deg)) + >>> sc = SkyCoord("J080000.00-050036.00", unit=(u.hour, u.deg)) + >>> sc = SkyCoord("J1874221.31+122328.03", unit=u.deg) + +Astropy `~astropy.units.Quantity`-type objects are acceptable and encouraged +as a form of input:: + + >>> ra = Longitude([1, 2, 3], unit=u.deg) # Could also use Angle + >>> dec = np.array([4.5, 5.2, 6.3]) * u.deg # Astropy Quantity + >>> sc = SkyCoord(ra, dec, frame='icrs') + >>> sc = SkyCoord(ra=ra, dec=dec, frame=ICRS, obstime='2001-01-02T12:34:56') + +Finally, it is possible to initialize from a low-level coordinate frame object. + + >>> c = FK4(1 * u.deg, 2 * u.deg) + >>> sc = SkyCoord(c, obstime='J2010.11', equinox='B1965') # Override defaults + +A key subtlety highlighted here is that when low-level objects are created they +have certain default attribute values. For instance, the +`~astropy.coordinates.FK4` frame uses ``equinox='B1950.0`` and +``obstime=equinox`` as defaults. If this object is used to initialize a +|SkyCoord| it is possible to override the low-level object attributes that were +not explicitly set. If the coordinate above were created with +``c = FK4(1 * u.deg, 2 * u.deg, equinox='B1960')`` then creating a |SkyCoord| +with a different ``equinox`` would raise an exception. + +.. + EXAMPLE END + +Initialization Syntax +--------------------- + +For spherical representations, which are the most common and are the default +input format for all built-in frames, the syntax for |SkyCoord| is given +below:: + + SkyCoord(COORD, [FRAME | frame=FRAME], [unit=UNIT], keyword_args ...) + SkyCoord(LON, LAT, [DISTANCE], [FRAME | frame=FRAME], [unit=UNIT], keyword_args ...) + SkyCoord([FRAME | frame=FRAME], =LON, =LAT, [unit=UNIT], + keyword_args ...) + +In the above description, elements in all capital letters (e.g., ``FRAME``) +describe a user input of that element type. Elements in square brackets are +optional. For nonspherical inputs, see the `Representations`_ section. + + +LON, LAT +^^^^^^^^ + +Longitude and latitude value can be specified as separate positional arguments. +The following options are available for longitude and latitude: + +- Single angle value: + + - |Quantity| object + - Plain numeric value with ``unit`` keyword specifying the unit + - Angle string which is formatted for :ref:`angle-creation` of + |Longitude| or |Latitude| objects + +- List or |Quantity| array, or NumPy array of angle values +- |Angle|, |Longitude|, or |Latitude| object, which can be scalar or + array-valued + +.. note:: + + While |SkyCoord| is flexible with respect to specifying longitude and + latitude component inputs, the frame classes expect to receive + |Quantity|-like objects with angular units (i.e., |Angle| or |Quantity|). + For example, when specifying components, the frame classes (e.g., ``ICRS``) + must be created as + + >>> ICRS(0 * u.deg, 0 * u.deg) # doctest: +FLOAT_CMP + + + and other methods of flexible initialization (that work with |SkyCoord|) + will not work + + >>> ICRS(0, 0, unit=u.deg) # doctest: +SKIP + UnitTypeError: Longitude instances require units equivalent to 'rad', but no unit was given. + +DISTANCE +^^^^^^^^ + +The distance to the object from the frame center can be optionally specified: + +- Single distance value: + + - |Quantity| or `~astropy.coordinates.Distance` object + - Plain numeric value for a dimensionless distance + - Plain numeric value with ``unit`` keyword specifying the unit + +- List, or |Quantity|, or `~astropy.coordinates.Distance` array, or NumPy array + of angle values + + +.. _coordinates-initialization-coord: + +COORD +^^^^^ + +This input form uses a single object to supply coordinate data. For the case +of spherical coordinate frames, the coordinate can include one or more +longitude and latitude pairs in one of the following ways: + +- Single coordinate string with a LON and LAT value separated by a space. The + respective values can be any string which is formatted for + :ref:`angle-creation` of |Longitude| or |Latitude| objects, respectively. +- List or NumPy array of such coordinate strings. +- List of (LON, LAT) tuples, where each LON and LAT are scalars (not arrays). +- ``N x 2`` NumPy or |Quantity| array of values where the first column is + longitude and the second column is latitude, for example, + ``[[270, -30], [355, +85]] * u.deg``. +- List of (LON, LAT, DISTANCE) tuples. +- ``N x 3`` NumPy or |Quantity| array of values where columns are + longitude, latitude, and distance, respectively. + +The input can also be more generalized objects that are not necessarily +represented in the standard spherical coordinates: + +- Coordinate frame object (e.g., ``FK4(1*u.deg, 2*u.deg, obstime='J2012.2')``). +- |SkyCoord| object (which just makes a copy of the object). +- `~astropy.coordinates.BaseRepresentation` subclass object like + `~astropy.coordinates.SphericalRepresentation`, + `~astropy.coordinates.CylindricalRepresentation`, or + `~astropy.coordinates.CartesianRepresentation`. + +**FRAME** + +This can be a `~astropy.coordinates.BaseCoordinateFrame` frame class, an +instance of such a class, or the corresponding string alias. The frame +classes that are built in to Astropy are `~astropy.coordinates.ICRS`, +`~astropy.coordinates.FK5`, `~astropy.coordinates.FK4`, +`~astropy.coordinates.FK4NoETerms`, `~astropy.coordinates.Galactic`, and +`~astropy.coordinates.AltAz`. The string aliases are lowercase versions of the +class name. + +If the frame is not supplied then you will see a special ``ICRS`` +identifier. This indicates that the frame is unspecified and operations +that require comparing coordinates (even within that object) are not allowed. + +**unit=UNIT** + +The unit specifier can be one of the following: + +- `~astropy.units.Unit` object, which is an angular unit that is equivalent to + ``Unit('radian')``. +- Single string with a valid angular unit name. +- 2-tuple of `~astropy.units.Unit` objects or string unit names specifying the + LON and LAT unit, respectively (e.g., ``('hourangle', 'degree')``). +- Single string with two unit names separated by a comma (e.g., + ``'hourangle,degree'``). + +If only a single unit is provided then it applies to both LON and LAT. + +**Other keyword arguments** + +In lieu of positional arguments to specify the longitude and latitude, the +frame-specific names can be used as keyword arguments: + +*ra*, *dec*: **LON**, **LAT** values, optional + RA and Dec for frames where these are representation, including [FIXME] + `~astropy.coordinates.ICRS`, `~astropy.coordinates.FK5`, + `~astropy.coordinates.FK4`, and `~astropy.coordinates.FK4NoETerms`. + +*l*, *b*: **LON**, **LAT** values, optional + Galactic ``l`` and ``b`` for the `~astropy.coordinates.Galactic` frame. + +The following keywords can be specified for any frame: + +*distance*: distance quantity-like, optional + Distance from reference from center to source + +*obstime*: time-like, optional + Time of observation + +*equinox*: time-like, optional + Coordinate frame equinox + +If custom user-defined frames are included in the transform graph and they +have additional frame attributes, then those attributes can also be +set via corresponding keyword arguments in the |SkyCoord| initialization. + +.. _astropy-coordinates-array-operations: + +Array Operations +================ + +It is possible to store arrays of coordinates in a |SkyCoord| object, and +manipulations done in this way will be orders of magnitude faster than +looping over a list of individual |SkyCoord| objects. + +Examples +-------- + +.. + EXAMPLE START + Storing Arrays of Coordinates in a SkyCoord Object + +To store arrays of coordinates in a |SkyCoord| object:: + + >>> ra = np.linspace(0, 36000, 1001) * u.deg + >>> dec = np.linspace(-90, 90, 1001) * u.deg + + >>> sc_list = [SkyCoord(r, d, frame='icrs') for r, d in zip(ra, dec)] # doctest: +SKIP + >>> timeit sc_gal_list = [c.galactic for c in sc_list] # doctest: +SKIP + 1 loops, best of 3: 20.4 s per loop + + >>> sc = SkyCoord(ra, dec, frame='icrs') + >>> timeit sc_gal = sc.galactic # doctest: +SKIP + 100 loops, best of 3: 21.8 ms per loop + +.. + EXAMPLE END + +.. + EXAMPLE START + Array Operations Using SkyCoord + +In addition to vectorized transformations, you can do the usual array slicing, +dicing, and selection using the same methods and attributes that you use for +`~numpy.ndarray` instances. Corresponding functions, as well as others that +affect the shape, such as `~numpy.atleast_1d` and `~numpy.rollaxis`, work as +expected. (The relevant functions have to be explicitly enabled in ``astropy`` +source code; let us know if a ``numpy`` function is not supported that you +think should work.):: + + >>> north_mask = sc.dec > 0 + >>> sc_north = sc[north_mask] + >>> len(sc_north) + 500 + >>> sc[2:4] # doctest: +FLOAT_CMP + + >>> sc[500] + + >>> sc[0:-1:100].reshape(2, 5) + + >>> np.roll(sc[::100], 1) + + +Note that similarly to the `~numpy.ndarray` methods, all but ``flatten`` try to +use new views of the data, with the data copied only if that is impossible +(as discussed, for example, in the documentation for NumPy +:func:`~numpy.reshape`). + +.. + EXAMPLE END + +.. _astropy-coordinates-modifying-in-place: + +Modifying Coordinate Objects In-place +------------------------------------- + +Coordinate values in a array-valued |SkyCoord| object can be modified in-place +(added in astropy 4.1). This requires that the new values be set from an +another |SkyCoord| object that is equivalent in all ways except for the actual +coordinate data values. In this way, no frame transformations are required and +the item setting operation is extremely robust. + +Specifically, the right hand ``value`` must be strictly consistent with the +object being modified: + +- Identical class +- Equivalent frames (`~astropy.coordinates.BaseCoordinateFrame.is_equivalent_frame`) +- Identical representation_types +- Identical representation differentials keys +- Identical frame attributes +- Identical "extra" frame attributes (e.g., ``obstime`` for an ICRS coord) + +.. + EXAMPLE START + Modifying an Array of Coordinates in a SkyCoord Object + +To modify an array of coordinates in a |SkyCoord| object use the same +syntax for a numpy array:: + + >>> sc1 = SkyCoord([1, 2] * u.deg, [3, 4] * u.deg) + >>> sc2 = SkyCoord(10 * u.deg, 20 * u.deg) + >>> sc1[0] = sc2 + >>> sc1 + + +.. + EXAMPLE END + +.. + EXAMPLE START + Inserting Coordinates into a SkyCoord Object + +You can insert a scalar or array-valued |SkyCoord| object into another +compatible |SkyCoord| object:: + + >>> sc1 = SkyCoord([1, 2] * u.deg, [3, 4] * u.deg) + >>> sc2 = SkyCoord(10 * u.deg, 20 * u.deg) + >>> sc1.insert(1, sc2) + + +.. + EXAMPLE END + +With the ability to modify a |SkyCoord| object in-place, all of the +:ref:`table_operations` such as joining, stacking, and inserting are +functional with |SkyCoord| mixin columns (so long as no masking is required). + +These methods are relatively slow because they require setting from an +existing |SkyCoord| object and they perform extensive validation to ensure +that the operation is valid. For some applications it may be necessary to +take a different lower-level approach which is described in the section +:ref:`astropy-coordinates-fast-in-place`. + +.. warning:: + + You may be tempted to try an apparently obvious way of modifying a coordinate + object in place by updating the component attributes directly, for example + ``sc1.ra[1] = 40 * u.deg``. However, while this will *appear* to give a correct + result it does not actually modify the underlying representation data. This + is related to the current implementation of performance-based caching. + The current cache implementation is similarly unable to handle in-place changes + to the representation (``.data``) or frame attributes such as ``.obstime``. + + +Attributes +========== + +The |SkyCoord| object has a number of useful attributes which come in handy. +By digging through these we will learn a little bit about |SkyCoord| and how it +works. + +To begin, one of the most important tools for +learning about attributes and methods of objects is "TAB-discovery." From +within IPython you can type an object name, the period, and then the key +to see what is available. This can often be faster than reading the +documentation:: + + >>> sc = SkyCoord(1, 2, frame='icrs', unit='deg', obstime='2013-01-02 14:25:36') + >>> sc. # doctest: +SKIP + sc.T sc.match_to_catalog_3d + sc.altaz sc.match_to_catalog_sky + sc.barycentrictrueecliptic sc.name + sc.cartesian sc.ndim + sc.cirs sc.obsgeoloc + sc.copy sc.obsgeovel + sc.data sc.obstime + sc.dec sc.obswl + sc.default_representation sc.position_angle + sc.diagonal sc.precessedgeocentric + sc.distance sc.pressure + sc.equinox sc.ra + sc.fk4 sc.ravel + sc.fk4noeterms sc.realize_frame + sc.fk5 sc.relative_humidity + sc.flatten sc.represent_as + sc.frame sc.representation_component_names + sc.frame_attributes sc.representation_component_units + sc.frame_specific_representation_info sc.representation_info + sc.from_name sc.reshape + sc.from_pixel sc.roll + sc.galactic sc.search_around_3d + sc.galactocentric sc.search_around_sky + sc.galcen_distance sc.separation + sc.gcrs sc.separation_3d + sc.geocentrictrueecliptic sc.shape + sc.get_constellation sc.size + sc.guess_from_table sc.spherical + sc.has_data sc.spherical_offsets_to + sc.hcrs sc.squeeze + sc.heliocentrictrueecliptic sc.supergalactic + sc.icrs sc.swapaxes + sc.info sc.take + sc.is_equivalent_frame sc.temperature + sc.is_frame_attr_default sc.to_pixel + sc.is_transformable_to sc.to_string + sc.isscalar sc.transform_to + sc.itrs sc.transpose + sc.location sc.z_sun + +Here we see many attributes and methods. The most recognizable may be the +longitude and latitude attributes which are named ``ra`` and ``dec`` for the +``ICRS`` frame:: + + >>> sc.ra # doctest: +FLOAT_CMP + + >>> sc.dec # doctest: +FLOAT_CMP + + +Next, notice that all of the built-in frame names ``icrs``, ``galactic``, +``fk5``, ``fk4``, and ``fk4noeterms`` are there. Through the magic of Python +properties, accessing these attributes calls the object +`~astropy.coordinates.SkyCoord.transform_to` method appropriately and returns a +new |SkyCoord| object in the requested frame:: + + >>> sc_gal = sc.galactic + >>> sc_gal # doctest: +FLOAT_CMP + + +Other attributes you may recognize are ``distance``, ``equinox``, +``obstime``, and ``shape``. + +Digging Deeper +-------------- +*[Casual users can skip this section]* + +After transforming to Galactic, the longitude and latitude values are now +labeled ``l`` and ``b``, following the normal convention for Galactic +coordinates. How does the object know what to call its values? The answer +lies in some less obvious attributes:: + + >>> sc_gal.representation_component_names + {'l': 'lon', 'b': 'lat', 'distance': 'distance'} + + >>> sc_gal.representation_component_units + {'l': Unit("deg"), 'b': Unit("deg")} + + >>> sc_gal.representation_type + + +Together these tell the object that ``l`` and ``b`` are the longitude and +latitude, and that they should both be displayed in units of degrees as +a spherical-type coordinate (and not, for example, a Cartesian coordinate). +Furthermore, the frame's ``representation_component_names`` attribute defines +the coordinate keyword arguments that |SkyCoord| will accept. + +Another important attribute is ``frame_attributes``, which defines the +additional attributes that are required to fully define the frame:: + + >>> sc_fk4 = SkyCoord(1, 2, frame='fk4', unit='deg') + >>> sc_fk4.frame_attributes # doctest: +ELLIPSIS + {'equinox': <...TimeAttribute ...>, 'obstime': <...TimeAttribute ...>} + +This example shows that the `~astropy.coordinates.FK4` frame has two +attributes, ``equinox`` and ``obstime``, that are required to fully define the +frame. + +Some trickery is happening here because many of these attributes are +actually owned by the underlying coordinate ``frame`` object which does much of +the real work. This is the middle layer in the three-tiered system of objects: +representation (spherical, Cartesian, etc.), frame (a.k.a. low-level frame +class), and |SkyCoord| (a.k.a. high-level class; see +:ref:`astropy-coordinates-overview` and +:ref:`astropy-coordinates-definitions`):: + + >>> sc.frame # doctest: +FLOAT_CMP + + + >>> sc.has_data is sc.frame.has_data + True + + >>> sc.frame. # doctest: +SKIP + sc.frame.T sc.frame.ra + sc.frame.cartesian sc.frame.ravel + sc.frame.copy sc.frame.realize_frame + sc.frame.data sc.frame.represent_as + sc.frame.dec sc.frame.representation + sc.frame.default_representation sc.frame.representation_component_names + sc.frame.diagonal sc.frame.representation_component_units + sc.frame.distance sc.frame.representation_info + sc.frame.flatten sc.frame.reshape + sc.frame.frame_attributes sc.frame.separation + sc.frame.frame_specific_representation_info sc.frame.separation_3d + sc.frame.has_data sc.frame.size + sc.frame.is_equivalent_frame sc.frame.spherical + sc.frame.is_frame_attr_default sc.frame.squeeze + sc.frame.is_transformable_to sc.frame.swapaxes + sc.frame.isscalar sc.frame.take + sc.frame.name sc.frame.transform_to + sc.frame.ndim sc.frame.transpose + + >>> sc.frame.name + 'icrs' + +The |SkyCoord| object exposes the ``frame`` object attributes as its own. Though +it might seem a tad confusing at first, this is a good thing because it makes +|SkyCoord| objects and `~astropy.coordinates.BaseCoordinateFrame` objects +behave very similarly and most routines can accept either one as input without +much bother (duck typing!). + +The lowest layer in the stack is the abstract +`~astropy.coordinates.UnitSphericalRepresentation` object: + + >>> sc_gal.frame.data # doctest: +FLOAT_CMP + + +Transformations +=============== + +The topic of transformations is covered in detail in the section on +:ref:`astropy-coordinates-transforming`. + +For completeness, here we will give some examples. Once you have defined +your coordinates and the reference frame, you can transform from that frame to +another frame. You can do this in a few different ways: if you only want the +default version of that frame, you can use attribute-style access (as mentioned +previously). For more control, you can use the +`~astropy.coordinates.SkyCoord.transform_to` method, which accepts a frame +name, frame class, frame instance, or |SkyCoord|. + +Examples +-------- + +.. + EXAMPLE START + Transforming Between Frames + +To transform from one frame to another:: + + >>> from astropy.coordinates import FK5 + >>> sc = SkyCoord(1, 2, frame='icrs', unit='deg') + >>> sc.galactic # doctest: +FLOAT_CMP + + + >>> sc.transform_to('fk5') # Same as sc.fk5 and sc.transform_to(FK5) # doctest: +FLOAT_CMP + + + >>> sc.transform_to(FK5(equinox='J1975')) # Transform to FK5 with a different equinox # doctest: +FLOAT_CMP + + +Transforming to a |SkyCoord| instance is a convenient way of ensuring that two +coordinates are in the exact same reference frame:: + + >>> sc2 = SkyCoord(3, 4, frame='fk4', unit='deg', obstime='J1978.123', equinox='B1960.0') + >>> sc.transform_to(sc2) # doctest: +FLOAT_CMP + + +.. + EXAMPLE END + +.. _astropy-skycoord-representations: + +Representations +=============== + +So far we have been using a spherical coordinate representation in all of the +examples, and this is the default for the built-in frames. Frequently it is +convenient to initialize or work with a coordinate using a different +representation such as Cartesian or cylindrical. In this section, we discuss +how to initialize an object using a different representation and how to +change the representation of an object. For more information about +representation objects themselves, see :ref:`astropy-coordinates-representations`. + +Initialization +-------------- + +Most of what you need to know can be inferred from the examples below and +by extrapolating the previous documentation for spherical representations. +Initialization requires setting the ``representation_type`` keyword and +supplying the corresponding components for that representation. + +Examples +^^^^^^^^ + +.. + EXAMPLE START + Initialization of a SkyCoord Object Using Different Representations + +To initialize an object using a representation type other than spherical:: + + >>> c = SkyCoord(x=1, y=2, z=3, unit='kpc', representation_type='cartesian') + >>> c # doctest: +FLOAT_CMP + + >>> c.x, c.y, c.z # doctest: +FLOAT_CMP + (, , ) + +Other variations include:: + + >>> SkyCoord(1, 2*u.deg, 3, representation_type='cylindrical') # doctest: +FLOAT_CMP + + + >>> SkyCoord(rho=1*u.km, phi=2*u.deg, z=3*u.m, representation_type='cylindrical') # doctest: +FLOAT_CMP + + + >>> SkyCoord(rho=1, phi=2, z=3, unit=(u.km, u.deg, u.m), representation_type='cylindrical') # doctest: +FLOAT_CMP + + + >>> SkyCoord(1, 2, 3, unit=(None, u.deg, None), representation_type='cylindrical') # doctest: +FLOAT_CMP + + +In general terms, the allowed syntax is as follows:: + + SkyCoord(COORD, [FRAME | frame=FRAME], [unit=UNIT], [representation_type=REPRESENTATION], + keyword_args ...) + SkyCoord(COMP1, COMP2, [COMP3], [FRAME | frame=FRAME], [unit=UNIT], + [representation_type=REPRESENTATION], keyword_args ...) + SkyCoord([FRAME | frame=FRAME], =COMP1, =COMP2, + =COMP3, [representation_type=REPRESENTATION], [unit=UNIT], + keyword_args ...) + +In this case, the ``keyword_args`` now includes the element +``representation_type=REPRESENTATION``. In the above description, elements in +all capital letters (e.g., ``FRAME``) describe a user input of that element +type. Elements in square brackets are optional. + +.. + EXAMPLE END + +**COMP1**, **COMP2**, **COMP3** + +Component values can be specified as separate positional arguments or as +keyword arguments. In this formalism the exact type of allowed input depends +on the details of the representation. In general, the following input forms +are supported: + +- Single value: + + - Component class object + - Plain numeric value with ``unit`` keyword specifying the unit + +- List or component class array, or NumPy array of values + +Each representation component has a specified class (the "component class") +which is used to convert generic input data into a predefined object +class with a certain unit. These component classes are expected to be +subclasses of the `~astropy.units.Quantity` class. + +**COORD** + +This input form uses a single object to supply coordinate data. The coordinate +can specify one or more coordinate positions as follows: + +- List of ``(COMP1, .., COMP)`` tuples, where each component is a scalar (not + array) and there are ``M`` components in the representation. Typically + there are three components, but some + (e.g., `~astropy.coordinates.UnitSphericalRepresentation`) + can have fewer. +- ``N x M`` NumPy or |Quantity| array of values, where ``N`` is the number + of coordinates and ``M`` is the number of components. + +**REPRESENTATION** + +The representation can be supplied either as a +`~astropy.coordinates.BaseRepresentation` class (e.g., +`~astropy.coordinates.CartesianRepresentation`) or as a string name +that is simply the class name in lowercase without the +``'representation'`` suffix (e.g., ``'cartesian'``). + +The rest of the inputs for creating a |SkyCoord| object in the general case are +the same as for spherical. + +Details +------- + +The available set of representations is dynamic and may change depending on what +representation classes have been defined. The built-in representations are: + +===================== ======================================================= + Name Class +===================== ======================================================= +``spherical`` `~astropy.coordinates.SphericalRepresentation` +``unitspherical`` `~astropy.coordinates.UnitSphericalRepresentation` +``physicsspherical`` `~astropy.coordinates.PhysicsSphericalRepresentation` +``cartesian`` `~astropy.coordinates.CartesianRepresentation` +``cylindrical`` `~astropy.coordinates.CylindricalRepresentation` +===================== ======================================================= + +Each frame knows about all of the available representations, but different +frames may use different names for the same components. A common example +is that the `~astropy.coordinates.Galactic` frame uses ``l`` and ``b`` +instead of ``ra`` and ``dec`` for the ``lon`` and ``lat`` components of +the `~astropy.coordinates.SphericalRepresentation`. + +For a particular frame, in order to see the full list of representations +and how it names all of the components, first make an instance of that frame +without any data, and then print the ``representation_info`` property:: + + >>> ICRS().representation_info # doctest: +SKIP + {astropy.coordinates...CartesianRepresentation: + {'names': ('x', 'y', 'z'), + 'units': (None, None, None)}, + astropy.coordinates...SphericalRepresentation: + {'names': ('ra', 'dec', 'distance'), + 'units': (Unit("deg"), Unit("deg"), None)}, + astropy.coordinates...UnitSphericalRepresentation: + {'names': ('ra', 'dec'), + 'units': (Unit("deg"), Unit("deg"))}, + astropy.coordinates...PhysicsSphericalRepresentation: + {'names': ('phi', 'theta', 'r'), + 'units': (Unit("deg"), Unit("deg"), None)}, + astropy.coordinates...CylindricalRepresentation: + {'names': ('rho', 'phi', 'z'), + 'units': (None, Unit("deg"), None)} + } + +This is a bit messy but it shows that for each representation there is a +``dict`` with two keys: + +- ``names``: defines how each component is named in that frame. +- ``units``: defines the units of each component when output, where ``None`` + means to not force a particular unit. + +For a particular coordinate instance you can use the ``representation_type`` +attribute in conjunction with the ``representation_component_names`` attribute +to figure out what keywords are accepted by a particular class object. The +former will be the representation class the system is expressed in (e.g., +spherical for equatorial frames), and the latter will be a dictionary mapping +names for that frame to the component name on the representation class:: + + >>> import astropy.units as u + >>> icrs = ICRS(1*u.deg, 2*u.deg) + >>> icrs.representation_type + + >>> icrs.representation_component_names + {'ra': 'lon', 'dec': 'lat', 'distance': 'distance'} + +Changing Representation +----------------------- + +The representation of the coordinate object can be changed, as shown +below. This actually does *nothing* to the object internal data which +stores the coordinate values, but it changes the external view of that +data in two ways: + +- The object prints itself in accord with the new representation. +- The available attributes change to match those of the new representation + (e.g., from ``ra, dec, distance`` to ``x, y, z``). + +Setting the ``representation_type`` thus changes a *property* of the +object (how it appears) without changing the intrinsic object itself +which represents a point in 3D space. + +Examples +^^^^^^^^ + +.. + EXAMPLE START + Changing the Representation of a Coordinate Object + +To change the representation of a coordinate object by setting the +``representation_type`` :: + + >>> c = SkyCoord(x=1, y=2, z=3, unit='kpc', representation_type='cartesian') + >>> c # doctest: +FLOAT_CMP + + + >>> c.representation_type = 'cylindrical' + >>> c # doctest: +FLOAT_CMP + + >>> c.phi.to(u.deg) # doctest: +FLOAT_CMP + + >>> c.x + Traceback (most recent call last): + ... + AttributeError: 'SkyCoord' object has no attribute 'x'... + + >>> c.representation_type = 'spherical' + >>> c # doctest: +FLOAT_CMP + + + >>> c.representation_type = 'unitspherical' + >>> c # doctest: +FLOAT_CMP + + +You can also use any representation class to set the representation:: + + >>> from astropy.coordinates import CartesianRepresentation + >>> c.representation_type = CartesianRepresentation + +Note that if all you want is a particular representation without changing the +state of the |SkyCoord| object, you should instead use the +``astropy.coordinates.SkyCoord.represent_as()`` method:: + + >>> c.representation_type = 'spherical' + >>> cart = c.represent_as(CartesianRepresentation) + >>> cart # doctest: +FLOAT_CMP + + >>> c.representation_type + + +.. + EXAMPLE END + +Example 1: Plotting random data in Aitoff projection +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. + EXAMPLE START + Plotting Random Data in Aitoff Projection + +This is an example of how to make a plot in the Aitoff projection using data +in a |SkyCoord| object. Here, a randomly generated data set will be used. + +First we need to import the required packages. We use +`matplotlib `_ here for +plotting and `numpy `_ to get the value of pi and to +generate our random data. + + >>> from astropy import units as u + >>> from astropy.coordinates import SkyCoord + >>> import numpy as np + +We now generate random data for visualization. For RA this is done in the range +of 0 and 360 degrees (``ra_random``), for DEC between -90 and +90 degrees +(``dec_random``). Finally, we multiply these values by degrees to get a +`~astropy.units.Quantity` with units of degrees. + + >>> rng = np.random.default_rng() + >>> ra_random = rng.uniform(0, 360, 100) * u.degree + >>> dec_random = rng.uniform(-90, 90, 100) * u.degree + +As the next step, those coordinates are transformed into an +`astropy.coordinates` |SkyCoord| object. + + >>> c = SkyCoord(ra=ra_random, dec=dec_random, frame='icrs') + +Because matplotlib needs the coordinates in radians and between :math:`-\pi` +and :math:`\pi`, not 0 and :math:`2\pi`, we have to convert them. +For this purpose the `astropy.coordinates.Angle` object provides a special +method, which we use here to wrap at 180: + + >>> ra_rad = c.ra.wrap_at(180 * u.deg).radian + >>> dec_rad = c.dec.radian + +As a last step, we set up the plotting environment with matplotlib using the +Aitoff projection with a specific title, a grid, filled circles as markers with +a marker size of 2, and an alpha value of 0.3. We use a figure with an x-y ratio +that is well suited for such a projection and we move the title upwards from +its usual position to avoid overlap with the axis labels. + +.. doctest-skip:: + + >>> import matplotlib.pyplot as plt + >>> fig, ax = plt.subplots(figsize=(8, 4.2), subplot_kw=dict(projection="aitoff")) + >>> ax.title("Aitoff projection of our random data") + >>> ax.grid(True) + >>> ax.scatter(ra_rad, dec_rad, marker="o", s=2, alpha=0.3) + >>> fig.subplots_adjust(top=0.95, bottom=0.0) + >>> plt.show() + + +.. plot:: + + # This is an example how to make a plot in the Aitoff projection using data + # in a SkyCoord object. Here a randomly generated data set will be used. The + # final script can be found below. + + # First we need to import the required packages. We use + # `matplotlib `_ here for + # plotting and `numpy `_ to get the value of pi and to + # generate our random data. + from astropy import units as u + from astropy.coordinates import SkyCoord + import matplotlib.pyplot as plt + import numpy as np + + # We now generate random data for visualization. For RA this is done in the range + # of 0 and 360 degrees (``ra_random``), for DEC between -90 and +90 degrees + # (``dec_random``). Finally, we multiply these values by degrees to get a + # `~astropy.units.Quantity` with units of degrees. + rng = np.random.default_rng() + ra_random = rng.uniform(0, 360, 100) * u.degree + dec_random = rng.uniform(-90, 90, 100) * u.degree + + # As the next step, those coordinates are transformed into an astropy.coordinates + # astropy.coordinates.SkyCoord object. + c = SkyCoord(ra=ra_random, dec=dec_random, frame='icrs') + + # Because matplotlib needs the coordinates in radians and between :math:`-\pi` + # and :math:`\pi`, not 0 and :math:`2\pi`, we have to convert them. + # For this purpose the `astropy.coordinates.Angle` object provides a special method, + # which we use here to wrap at 180: + ra_rad = c.ra.wrap_at(180 * u.deg).radian + dec_rad = c.dec.radian + + # As a last step we set up the plotting environment with matplotlib using the + # Aitoff projection with a specific title, a grid, filled circles as markers with + # a marker size of 2, and an alpha value of 0.3. + fig, ax = plt.subplots(figsize=(8, 4.2), subplot_kw=dict(projection="aitoff")) + ax.set_title("Aitoff projection of our random data", y=1.08) + ax.grid(True) + ax.scatter(ra_rad, dec_rad, marker="o", s=2, alpha=0.3) + fig.subplots_adjust(top=0.95, bottom=0.0) + plt.show() + +.. + EXAMPLE END + +Example 2: Plotting star positions in bulge and disk +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. + EXAMPLE START + Plotting Star Positions in Bulge and Disk + +This is a more realistic example of how to make a plot in the Aitoff projection +using data in a |SkyCoord| object. Here, a randomly generated data set +(multivariate normal distribution) for both stars in the bulge and in the disk +of a galaxy will be used. Both types will be plotted with different number +counts. + +As in the last example, we first import the required packages. + + >>> from astropy import units as u + >>> from astropy.coordinates import SkyCoord + >>> import numpy as np + +We now generate random data for visualization using +``numpy.random.Generator.multivariate_normal``. + + >>> rng = np.random.default_rng() + >>> disk = rng.multivariate_normal(mean=[0,0,0], cov=np.diag([1,1,0.5]), size=5000) + >>> bulge = rng.multivariate_normal(mean=[0,0,0], cov=np.diag([1,1,1]), size=500) + >>> galaxy = np.concatenate([disk, bulge]) + +As the next step, those coordinates are transformed into an +`astropy.coordinates` |SkyCoord| object. + + >>> c_gal = SkyCoord(galaxy, representation_type='cartesian', frame='galactic') + >>> c_gal_icrs = c_gal.icrs + +Again, as in the last example, we need to convert the coordinates in radians +and make sure they are between :math:`-\pi` and :math:`\pi`: + + >>> ra_rad = c_gal_icrs.ra.wrap_at(180 * u.deg).radian + >>> dec_rad = c_gal_icrs.dec.radian + +We use the same plotting setup as in the last example: + +.. doctest-skip:: + + >>> import matplotlib.pyplot as plt + >>> fig, ax = plt.subplots(figsize=(8, 4.2), subplot_kw=dict(projection="aitoff")) + >>> ax.set_title("Aitoff projection of our random data") + >>> ax.grid(True) + >>> ax.scatter(ra_rad, dec_rad, marker="o", s=2, alpha=0.3) + >>> fig.subplots_adjust(top=0.95, bottom=0.0) + >>> plt.show() + + +.. plot:: + + # This is more realistic example how to make a plot in the Aitoff projection + # using data in a SkyCoord object. + # Here a randomly generated data set (multivariate normal distribution) + # for both stars in the bulge and in the disk of a galaxy + # will be used. Both types will be plotted with different number counts. The + # final script can be found below. + + # As in the last example, we first import the required packages. + from astropy import units as u + from astropy.coordinates import SkyCoord + import matplotlib.pyplot as plt + import numpy as np + + # We now generate random data for visualization with + # np.random.Generator.multivariate_normal. + rng = np.random.default_rng() + disk = rng.multivariate_normal(mean=[0,0,0], cov=np.diag([1,1,0.5]), size=5000) + bulge = rng.multivariate_normal(mean=[0,0,0], cov=np.diag([1,1,1]), size=500) + galaxy = np.concatenate([disk, bulge]) + + # As the next step, those coordinates are transformed into an astropy.coordinates + # astropy.coordinates.SkyCoord object. + c_gal = SkyCoord(galaxy, representation_type='cartesian', frame='galactic') + c_gal_icrs = c_gal.icrs + + # Again, as in the last example, we need to convert the coordinates in radians + # and make sure they are between :math:`-\pi` and :math:`\pi`: + ra_rad = c_gal_icrs.ra.wrap_at(180 * u.deg).radian + dec_rad = c_gal_icrs.dec.radian + + # We use the same plotting setup as in the last example: + fig, ax = plt.subplots(figsize=(8, 4.2), subplot_kw=dict(projection="aitoff")) + ax.set_title("Aitoff projection of our random data", y=1.08) + ax.grid(True) + ax.scatter(ra_rad, dec_rad, marker="o", s=2, alpha=0.3) + fig.subplots_adjust(top=0.95, bottom=0.0) + plt.show() + +.. + EXAMPLE END + +.. _coordinates-skycoord-comparing: + +Comparing SkyCoord Objects +========================== + +There are two primary ways to compare |SkyCoord| objects to each other. First is +checking if the coordinates are within a specified distance of each other. This +is what most users should do in their science or processing analysis work +because it allows for a tolerance due to floating point representation issues. +The second is checking for exact equivalence of two objects down to the bit, +which is most useful for developers writing tests. + +The example below illustrates the floating point issue using the exact +equality comparison, where we do a roundtrip transformation +FK4 => ICRS => FK4 and then compare:: + + >>> sc1 = SkyCoord(1*u.deg, 2*u.deg, frame='fk4') + >>> sc1.icrs.fk4 == sc1 + np.False_ + +Matching Within Tolerance +------------------------- + +To test if coordinates are within a certain angular distance of one other, use the +:meth:`~astropy.coordinates.BaseCoordinateFrame.separation` method:: + + >>> sc1.icrs.fk4.separation(sc1).to(u.arcsec) # doctest: +SKIP + + >>> sc1.icrs.fk4.separation(sc1) < 1e-9 * u.arcsec + np.True_ + +Exact Equality +-------------- + +Astropy also provides an exact equality operator for coordinates. +For example, when comparing, e.g., two |SkyCoord| objects:: + + >>> left_skycoord == right_skycoord # doctest: +SKIP + +the right object must be strictly consistent with the left object for +comparison: + +- Identical class +- Equivalent frames (`~astropy.coordinates.BaseCoordinateFrame.is_equivalent_frame`) +- Identical representation_types +- Identical representation differentials keys +- Identical frame attributes +- Identical "extra" frame attributes (e.g., ``obstime`` for an ICRS coord) + +In the first example we show simple comparisons using array-valued coordinates:: + + >>> sc1 = SkyCoord([1, 2]*u.deg, [3, 4]*u.deg) + >>> sc2 = SkyCoord([1, 20]*u.deg, [3, 4]*u.deg) + + >>> sc1 == sc2 # Array-valued comparison + array([ True, False]) + >>> sc2 == sc2[1] # Broadcasting comparison with a scalar + array([False, True]) + >>> sc2[0] == sc2[1] # Scalar to scalar comparison + np.False_ + >>> sc1 != sc2 # Not equal + array([False, True]) + +In addition to numerically comparing the representation component data (which +may include velocities), the equality comparison includes strict tests that all +of the frame attributes like ``equinox`` or ``obstime`` are exactly equal. Any +mismatch in attributes will result in an exception being raised. For example:: + + >>> sc1 = SkyCoord([1, 2]*u.deg, [3, 4]*u.deg) + >>> sc2 = SkyCoord([1, 20]*u.deg, [3, 4]*u.deg, obstime='2020-01-01') + >>> sc1 == sc2 # doctest: +SKIP + ... + ValueError: cannot compare: extra frame attribute 'obstime' is not equivalent + (perhaps compare the frames directly to avoid this exception) + +In this example the ``obstime`` attribute is a so-called "extra" frame attribute +that does not apply directly to the ICRS coordinate frame. So we could compare +with the following, this time using the ``!=`` operator for variety:: + + >>> sc1.frame != sc2.frame + array([False, True]) + +One slightly special case is comparing two frames that both have no data, where +the return value is the same as ``frame1.is_equivalent_frame(frame2)``. For +example:: + + >>> from astropy.coordinates import FK4 + >>> FK4() == FK4(obstime='2020-01-01') + False + +.. _skycoord-table-conversion: + +Converting a SkyCoord to a Table +================================ + +A |SkyCoord| object can be converted to a |QTable| using its +:meth:`~astropy.coordinates.SkyCoord.to_table` method. The attributes of the +|SkyCoord| are converted to columns of the table or added to its metadata +depending on whether or not they have the same length as the |SkyCoord|. This +means that attributes such as ``obstime`` can become columns or metadata:: + + >>> from astropy.coordinates import SkyCoord + >>> from astropy.time import Time + >>> sc = SkyCoord(ra=[15, 30], dec=[-70, -50], unit=u.deg, + ... obstime=Time([2000, 2010], format='jyear')) + >>> t = sc.to_table() + >>> t + + ra dec obstime + deg deg + float64 float64 Time + ------- ------- ------- + 15.0 -70.0 2000.0 + 30.0 -50.0 2010.0 + >>> t.meta + {'representation_type': 'spherical', 'frame': 'icrs'} + + >>> sc = SkyCoord(l=[0, 20], b=[20, 0], unit=u.deg, frame='galactic', + ... obstime=Time(2000, format='jyear')) + >>> t = sc.to_table() + >>> t + + l b + deg deg + float64 float64 + ------- ------- + 0.0 20.0 + 20.0 0.0 + >>> t.meta + {'representation_type': 'spherical', 'frame': 'galactic', + 'obstime': + +The first argument should be the last major version (before any bug fix releases +and ignoring the .0 part of the version number, while the second argument should +be the ``.dev`` tag that was just after the branching of the last major version. +Finally, you will need a GitHub personal access token with default permissions +(no scopes selected). + +The output will look similar to:: + + This release of astropy contains 2573 commits in 163 merged pull requests + closing 104 issues from 98 people, 50 of which are first-time contributors + to astropy. + + * 2573 commits have been added since 5.3 + * 104 issues have been closed since 5.3 + * 163 pull requests have been merged since 5.3 + * 98 people have contributed since 5.3 + * 50 of which are new contributors + + The people who have contributed to the code for this release are: + + - Name 1 * + - Name 2 * + - Name 3 + +At this point, you will likely need to update the Astropy ``.mailmap`` file, +which maps contributor emails to names, as there are often contributors who +are not careful about using the same e-mail address for every commit, meaning +that they appear multiple times in the contributor list above, sometimes with +different spelling, and sometimes you may also just see their GitHub username +with no full name. + +The easiest way to get a full list of contributors and email addresses is +to do:: + + git shortlog -n -s -e + +Edit the ``.mailmap`` file to add entries for new email addresses for already +known contributors (matched to the appropriate canonical name/email address). +You can also try and investigate users with no name to see if you can determine +their full name from other sources - if you do, add a new entry for them in +the ``.mailmap`` file. Once you have done this, you can re-run the +``generate_releaserst.xsh`` script (you will likely need to iterate a few times). +Once you are happy with the output, copy it into the 'What's new' page for +the current release and commit this. E.g., :: + + $ git add docs/whatsnew/6.0.rst + $ git commit -m "Added contributor statistics and names" + +Push the release branch back to GitHub, e.g.:: + + $ git push upstream v6.0.x + +Switch to a new branch that tracks the ``main`` branch and update the +``docs/credits.rst`` file to include any new contributors from the above step, +and commit this and the ``.mailmap`` changes:: + + $ git checkout -b v6.0.0-mailmap-credits upstream/main + $ git add .mailmap + $ git add docs/credits.rst + $ git commit -m "Updated list of contributors and .mailmap file" + +Open a pull request to merge this into ``main`` and mark it as requiring backporting to +the release branch. + +.. _release-procedure-check-ci: + +Ensure continuous integration and intensive tests pass +------------------------------------------------------ + +Make sure that the continuous integration services (e.g., GitHub Actions or CircleCI) are passing +for the `astropy core repository`_ branch you are going to release. +Also make sure that the ReadTheDocs build is passing for the release branch. + +One of the continuous integration tasks that should be run periodically is the updates to the +IERS tables in ``astropy.utils.iers``, so check that the last run from this has been +successfully run and that related pull requests have been merged (and backported if needed). +You can also manually trigger it using its workflow dispatch option. + +You may also want to locally run the tests (with remote data on to ensure all +of the tests actually run), using tox to do a thorough test in an isolated +environment:: + + python -m pip install tox --upgrade + tox -e test-alldeps -- --remote-data=any --run-slow --run-hugemem + +Additional notes +---------------- + +Do not render the changelog with towncrier at this point. This should only be done just before the final +release. However, it is up to the discretion of the release manager whether to +open 'practice' pull requests to do this as part of the beta/release candidate +process (but they should not be merged in) - if so the process for rendering the changelog is described +in :ref:`release-procedure-render-changelog`. + +.. _release-procedure-tagging: + +Tagging the first release candidate +----------------------------------- + +Assuming all the CI passes, you should now be ready to do a first release +candidate! Ensure you have a GPG key pair available for when git needs to sign +the tag you create for the release (see e.g., +`GitHub's documentation `_ +for how to generate a key pair). + +Make sure your local release branch is up-to-date with the upstream release +branch, then tag the latest commit with the ``-s`` option, including an ``rc1`` +suffix, e.g.:: + + $ git tag -s v6.0.0rc1 -m "Tagging v6.0.0rc1" + +Push up the tag to the `astropy core repository`_, e.g.:: + + $ git push upstream v6.0.0rc1 + +.. warning:: + + It might be tempting to use the ``--tags`` argument to ``git push``, + but this should *not* be done, as it might push up some unintended tags. + +At this point if all goes well, the wheels and sdist will be build +in the release workflow and uploaded to PyPI! + +In the event there are any issues with the wheel building for the tag +(which shouldn't really happen if it was passing for the release branch), +you'll have to fix whatever the problem is. Make sure you delete the +tag:: + + git tag -d v + +Make any fixes by adding commits to the release branch (no need to remove +previous commits) e.g. via pull requests to the release branch, backports, +or direct commits on the release branch, as appropriate. Once you are +ready to try and release again, create the tag, then force push the tag +to GitHub to overwrite the previous one. + +Once the sdist and wheels are uploaded, the first release candidate is done! + +At this point create a new Wiki page under +`Astropy Project Wiki `_ with the +title "vX.Y RC testing" (replace "X.Y" with the release number) using the +`wiki of a previous RC `_ +as a template. You can now email the user and developer community advertising +the release candidate and including a link to the wiki page to report any +successes and failures. + +Additionally, you should update the release calendar by going to +https://github.com/astropy/astropy/wiki/Release-Calendar and updating the +"Actual date" column of this version's release candidate with the current date. + +Releasing subsequent release candidates +======================================= + +It is very likely that some issues will be reported with the first release +candidate. Any issues should be fixed via pull requests to the ``main`` branch +and marked for backporting to the release branch. The process for backporting +fixes is described in :ref:`release-procedure-bug-fix-backport`. + +Once you have backported any required fixes, repeat the following steps +you did for the first release candidate: + +* :ref:`release-procedure-update-whatsnew` (this should only involve updating the numbers of issues and so on, as well as potentially adding a few new contributors) +* :ref:`release-procedure-check-ci` + +You can then proceed with tagging the second release candidate, as done in +* :ref:`release-procedure-tagging` and replacing ``rc1`` with ``rc2``. + +You can potentially repeat this section for a third or even fourth release candidate if needed. Once no major issues +come up with a release candidate, you are ready to proceed to the next section. + +Releasing the final version of the feature release +================================================== + +.. _release-procedure-render-changelog: + +Rendering the changelog +----------------------- + +We now need to render the changelog with towncrier (21.9.0 or later). Since it +is a good idea to review the changelog and fix any line wrap and other issues, +we do this on a separate branch and open a pull request into the release branch +to allow for easy review. First, create and switch to a new branch based off the +release branch, e.g.:: + + $ git checkout -b v6.0.0-changelog + +Next, run towncrier and confirm that the fragments can be deleted:: + + towncrier build --version 6.0.0 + +Check the ``CHANGES.rst`` file and remove any empty sections from the new +changelog section. + +Then add and commit those changes with:: + + $ git add CHANGES.rst + $ git commit -m "Finalizing changelog for v" + +Push to GitHub and open a pull request for merging this into the release branch, +e.g. v6.0.x. + +.. note:: + + We render the changelog on the latest release branch and forward-port it + rather than rendering on ``main`` and backporting, since the latter would + render all news fragments into the changelog rather than only the ones + intended for the e.g. v6.0.x release branch. + +.. _release-procedure-checking-changelog: + +Checking the changelog +---------------------- + +Scripts are provided at https://github.com/astropy/astropy-tools/tree/main/pr_consistency +to check for consistency between milestones, labels, the presence of pull requests +in release branches, and the changelog. Follow the instructions in that repository +to make sure everything is correct for the present release. + +Tagging the final release +------------------------- + +Once the changelog pull request is merged, update your release branch to +match the upstream version, then (on the release branch), tag the merge +commit for the changelog changes with ``v`` - as described in +:ref:`release-procedure-tagging` but leaving out the ``rc1`` suffix, then +push the tag to GitHub and wait for the wheels and sdist to be uploaded to +PyPI. + +Congratulations! You have completed the release! Now there are just a few +clean-up tasks to finalize the process. + +.. _post-release-procedure: + +Post-Release procedures +----------------------- + +#. Make sure that ReadTheDocs is building the documentation for the version you + just released. Also verify that the ``stable`` ReadTheDocs version builds + correctly for the new version (both should trigger automatically). + +#. When releasing a patch release, also set the previous RTD version in the + release history to "Hidden". For example when releasing v6.0.2, set + v6.0.1 to "Hidden". This prevents the previous releases from + cluttering the list of versions that users see in the version dropdown + (the previous versions are still accessible by their URL though). + +#. If you have updated the list of contributors during the release, update the + equivalent list on the Astropy web site at + https://github.com/astropy/astropy.github.com. + +#. Cherry-pick the commit rendering the changelog and deleting the fragments and + open a PR to the astropy *main* branch. Also make sure you cherry-pick the + commit updating the ``.mailmap`` and ``docs/credits.rst`` files to the *main* + branch in a separate PR. + +#. Turn off any branch protection you might have enabled in + :ref:`release-procedure-restrict-branch`. + +#. ``conda-forge`` has a bot that automatically opens + a PR from a new PyPI (stable) release, which you need to follow up on and + merge. When the ``conda-forge`` package is ready, email the Anaconda + maintainers about the release(s) so they can update the versions in the + default channels. Typically, you should wait to make sure ``conda-forge`` and + possibly ``conda`` works before sending out the public announcement (so that + users who want to try out the new version can do so with ``conda``). + +#. Upload the release to Zenodo by creating a GitHub Release off the GitHub tag. + Click on the tag in https://github.com/astropy/astropy/tags and then click on + "Create release from tag" on the upper right. The release title is the same as the + tag. In the description, you can copy and paste a description from the previous + release, as it should be a one-liner that points to ``CHANGES.rst``. When you + are ready, click "Publish release" (the green button on bottom left). + A webhook to Zenodo will be activated and the release will appear under + https://doi.org/10.5281/zenodo.4670728 . If you encounter problems during this + step, please contact the Astropy Coordination Committee. + +#. Once the release(s) are available on the default ``conda`` channels, prepare + the public announcement. For a new feature release, copy the `latest + announcement + `_ and + edit it to update the version number and links. Once it is merged, you can + proceed to send out an email to the ``astropy-dev`` and Astropy mailing + lists. For a bugfix release, use the previous announcement as a template. + You should also coordinate with the rest of the Astropy release team and the + community engagement coordinators. + +#. If this is a feature release, update the release calendar by going to + https://github.com/astropy/astropy/wiki/Release-Calendar and updating the + "Actual date" column of this version's release with the date you performed + the release (probably the date of the tag and PyPI upload). + +#. In the main branch, update the `SECURITY.md file in the astropy repo + `_ to include the + newly released version, and as needed mark older versions as not supported. + +#. In the main branch, update the `update_astropy_iers_data_main.yml file in the astropy repo + `_ + to ensure the branches in its matrix only contain the active branches, usually + main and the new release branch you just created. + +.. _release-procedure-bug-fix: + +Maintaining Bug Fix Releases +============================ + +Astropy releases, as recommended for most Python projects, follows a +.. version scheme, where the "micro" version is also +known as a "bug fix" release. Bug fix releases should not change any +user-visible interfaces. They should only fix bugs on the previous major/minor +release and may also refactor internal APIs or include omissions from previous +releases--that is, features that were documented to exist but were accidentally +left out of the previous release. They may also include changes to docstrings +that enhance clarity but do not describe new features (e.g., more examples, +typo fixes, etc). + +Bug fix releases are typically managed by maintaining one or more bug fix +branches separate from the main branch (the release procedure below discusses +creating these branches). Typically, whenever an issue is fixed on the Astropy +main branch a decision must be made whether this is a fix that should be +included in the Astropy bug fix release. Usually the answer to this question +is "yes", though there are some issues that may not apply to the bug fix +branch. For example, it is not necessary to backport a fix to a new feature +that did not exist when the bug fix branch was first created. New features +are never merged into the bug fix branch--only bug fixes; hence the name. + +In rare cases a bug fix may be made directly into the bug fix branch without +going into the main branch first. This may occur if a fix is made to a +feature that has been removed or rewritten in the development version and no +longer has the issue being fixed. However, depending on how critical the bug +is it may be worth including in a bug fix release, as some users can be slow to +upgrade to new major/micro versions due to API changes. + +Issues are assigned to an Astropy release by way of the Milestone feature in +the GitHub issue tracker. At any given time there are at least two versions +under development: The next major/minor version, and the next bug fix release, for example: +v6.1.0 and v6.0.1. In this case, v6.0.1 is the next bug fix release and all +issues that should include fixes in that release should be assigned that +milestone. Any issues that implement new features would go into the v6.1.0 +milestone--this is any work that goes in the main branch that should not +be backported. For a more detailed set of guidelines on using milestones, see +:ref:`milestones-and-labels`. + +Before going ahead with the release, you should check that all merged pull +requests milestoned for the upcoming release have been correctly backported. +You can find more information on backporting fixes to release branches +in :ref:`release-procedure-bug-fix-backport`. + +Once you have backported any required fixes, go through the following steps +in a similar way to the initial feature release: + +* :ref:`release-procedure-check-ci` +* :ref:`release-procedure-render-changelog` +* :ref:`release-procedure-checking-changelog` + +You can then proceed with tagging the bugfix release. Make sure your local +release branch is up-to-date with the upstream release branch, then tag the +latest commit with the ``-s`` option, e.g:: + + $ git tag -s v6.0.1 -m "Tagging v6.0.1" + +Push up the tag to the `astropy core repository`_, e.g.:: + + $ git push upstream v6.0.1 + +.. note:: + + It might be tempting to use the ``--tags`` argument to ``git push``, + but this should *not* be done, as it might push up some unintended tags. + +At this point if all goes well, the wheels and sdist will be build +in the release workflow and uploaded to PyPI! + +In the event there are any issues with the wheel building for the tag +(which shouldn't really happen if it was passing for the release branch), +you'll have to fix whatever the problem is. Make sure you delete the +tag locally, e.g.:: + + git tag -d v6.0.1 + +and on GitHub:: + + git push upstream :refs/tags/v6.0.1 + +Make any fixes by adding commits to the release branch (no need to remove +previous commits) e.g. via pull requests to the release branch, backports, +or direct commits on the release branch, as appropriate. Once you are +ready to try and release again, create the tag, then force push the tag +to GitHub to overwrite the previous one. + +Once the release is done, follow the :ref:`post-release-procedure`. + +Common procedures +================= + +.. _release-procedure-bug-fix-backport: + +Backporting fixes from main +--------------------------- + +.. note:: + + The changelog script in `astropy-tools `_ + (``pr_consistency`` scripts in particular) does not know about minor releases, thus please be careful. + For example, let's say we have two branches (``main`` and ``v6.0.x``). + Both 6.0.0 and 6.0.1 releases will come out of the same v6.0.x branch. + If a PR for 6.0.1 is merged into ``main`` before 6.0.0 is released, + it should not be backported into v6.0.x branch until after 6.0.0 is + released, despite complaining from the aforementioned script. + This situation only arises in a very narrow time frame after 6.0.0 + freeze but before its release. + +Most pull requests will be backported automatically by a backport bot, which +opens pull requests with the backports against the release branch. Make sure +that any such pull requests are merged in before starting the release process +for a new bugfix release. + +In some cases, some pull requests or in some cases direct commits to ``main`` +will need to be backported manually. This is done using the ``git cherry-pick`` +command, which applies the diff from a single commit like a patch. For the sake +of example, say the current bug fix branch is 'v6.0.x', and that a bug was fixed +in main in a commit ``abcd1234``. In order to backport the fix, checkout the +v6.0.x branch (it's also good to make sure it's in sync with the `astropy core +repository`_) and cherry-pick the appropriate commit:: + + $ git checkout v6.0.x + $ git pull upstream v6.0.x + $ git cherry-pick abcd1234 + +Sometimes a cherry-pick does not apply cleanly, since the bug fix branch +represents a different line of development. This can be resolved like any +other merge conflict: Edit the conflicted files by hand, and then run +``git commit`` and accept the default commit message. If the fix being +cherry-picked has an associated changelog entry in a separate commit make +sure to backport that as well. + +What if the issue required more than one commit to fix? There are a few +possibilities for this. The easiest is if the fix came in the form of a +pull request that was merged into the main branch. Whenever GitHub merges +a pull request it generates a merge commit in the main branch. This merge +commit represents the *full* difference of all the commits in the pull request +combined. What this means is that it is only necessary to cherry-pick the +merge commit (this requires adding the ``-m 1`` option to the cherry-pick +command). For example, if ``5678abcd`` is a merge commit:: + + $ git checkout v6.0.x + $ git pull upstream v6.0.x + $ git cherry-pick -m 1 5678abcd + +In fact, because Astropy emphasizes a pull request-based workflow, this is the +*most* common scenario for backporting bug fixes, and the one requiring the +least thought. However, if you're not dealing with backporting a fix that was +not brought in as a pull request, read on. + +.. seealso:: + + :ref:`merge-commits-and-cherry-picks` for further explanation of the + cherry-pick command and how it works with merge commits. + +If not cherry-picking a merge commit there are still other options for dealing +with multiple commits. The simplest, though potentially tedious, is to +run the cherry-pick command once for each commit in the correct order. +However, as of Git 1.7.2 it is possible to merge a range of commits like so:: + + $ git cherry-pick 1234abcd..56789def + +This works fine so long as the commits you want to pick are actually congruous +with each other. In most cases this will be the case, though some bug fixes +will involve followup commits that need to back backported as well. Most bug +fixes will have an issues associated with it in the issue tracker, so make sure +to reference all commits related to that issue in the commit message. That way +it's harder for commits that need to be backported from getting lost. + +.. _astropy core repository: https://github.com/astropy/astropy +.. _signed tags: https://git-scm.com/book/en/v2/Git-Basics-Tagging#Signed-Tags +.. _cython: http://www.cython.org/ +.. _astropy-tools repository: https://github.com/astropy/astropy-tools +.. _Anaconda: https://conda.io/docs/ +.. _twine: https://packaging.python.org/key_projects/#twine +.. _generate_releaserst.xsh: https://raw.githubusercontent.com/sunpy/sunpy/main/tools/generate_releaserst.xsh diff --git a/docs/development/maintainers/testhelpers.rst b/docs/development/maintainers/testhelpers.rst new file mode 100644 index 000000000000..686e3e5a025b --- /dev/null +++ b/docs/development/maintainers/testhelpers.rst @@ -0,0 +1,109 @@ +.. _testhelpers: + +********************* +Astropy Testing Tools +********************* + +This section is primarily a reference for developers that want to understand or +add to the Astropy testing machinery. See :doc:`/development/testguide` for an +overview of running or writing the tests. + +Details +======= + +The dependencies used by the Astropy test suite are provided by a separate +package called |pytest-astropy|. This package provides the ``pytest`` +dependency itself, in addition to several ``pytest`` plugins that are used by +Astropy, and will also be of general use to other packages. + +Since the testing dependencies are not actually required to install or use +Astropy, in the ``pyproject.toml`` file they are not included under the +``[project]`` section in ``dependencies``. Instead, they are listed under the +``[project.optional-dependences]`` in named sections such as ``test``. +Furthermore, dev-only dependencies are listed under ``[dependency-groups]``, which for +instance defines a ``dev`` and a ``dev_all`` group. + +In particular the ``test`` dependencies are the minimal set of dependencies for running +astropy tests and you would use this primarily to check that tests pass *without* the +optional dependencies. This is not common and would normally be done with ``tox -e +test``. + +`astropy.tests.helper` Module +============================= + +To ease development of tests that work with Astropy, the +`astropy.tests.helper` module provides some utility functions to make +tests that use Astropy conventions or classes easier to work with, e.g., +functions to test for near-equality of `~astropy.units.Quantity` objects. + +The functionality here is not exhaustive, because +much of the useful tools are either in the standard +library, |pytest|, or `numpy.testing +`_. This module +contains primarily functionality specific to the astropy core package or +packages that follow the Astropy package template. + +Conversion Guide +---------------- + +Some long-standing functionality has been removed. +The following table maps them to what you should use instead. + +========================================================== =============================================== +Removed Use this +========================================================== =============================================== +``astropy.io.ascii.tests.common.raises`` ``pytest.raises`` +``astropy.tests.disable_internet`` ``pytest_remotedata.disable_internet`` +``astropy.tests.helper.catch_warnings`` ``pytest.warns`` +``astropy.tests.helper.enable_deprecations_as_exceptions`` https://docs.pytest.org/en/stable/warnings.html +``astropy.tests.helper.ignore_warnings`` https://docs.pytest.org/en/stable/warnings.html +``astropy.tests.helper.raises`` ``pytest.raises`` +``astropy.tests.helper.remote_data`` ``pytest.mark.remote_data`` +``astropy.tests.helper.treat_deprecations_as_exceptions`` https://docs.pytest.org/en/stable/warnings.html +``astropy.tests.plugins.display`` ``pytest-astropy-header`` package +========================================================== =============================================== + +Reference/API +------------- + +.. module:: astropy.tests.helper + +.. automodapi:: astropy.tests.helper + :no-main-docstr: + :no-inheritance-diagram: + + +Astropy Test Runner +=================== + +.. warning:: + + ``astropy.test``, ``TestRunner``, and ``TestRunnerBase`` are deprecated as of v8.0. + This will also affect downstream ``packagename.test`` generated using ``TestRunner``. + If you use any of the API referenced in this section, please consider + switching away to using ``pytest`` natively. + For example, running ``astropy.test()`` in Python is equivalent to + running ``pytest --pyargs astropy`` from the command line. + +When executing tests with ``packagename.test`` the call to pytest is controlled +by the `astropy.tests.runner.TestRunner` class. + +The `~astropy.tests.runner.TestRunner` class is used to generate the +``packagename.test`` function, the test function generates a set of command line +arguments to pytest. The arguments to pytest are defined in the +`~astropy.tests.runner.TestRunner.run_tests` method, the arguments to +``run_tests`` and their respective logic are defined in methods of +`~astropy.tests.runner.TestRunner` decorated with the +``astropy.tests.runner.keyword`` decorator. For an example of this see +`~astropy.tests.runner.TestRunnerBase`. This design makes it easy for +packages to add or remove keyword arguments to their test runners, or define a +whole new set of arguments by subclassing from +`~astropy.tests.runner.TestRunnerBase`. + +Reference/API +------------- + +.. module:: astropy.tests.runner + +.. automodapi:: astropy.tests.runner + :no-main-docstr: diff --git a/docs/development/pull_button.png b/docs/development/pull_button.png new file mode 100644 index 000000000000..2198a2ab62d7 Binary files /dev/null and b/docs/development/pull_button.png differ diff --git a/docs/development/quickstart.rst b/docs/development/quickstart.rst new file mode 100644 index 000000000000..25bad1fcb388 --- /dev/null +++ b/docs/development/quickstart.rst @@ -0,0 +1,345 @@ +.. _contributing_quickstart: + +========================= +Contributing Quickstart +========================= + +.. _contributing_environment: + +Creating a development environment +================================== + +To make and test code changes and build the documentation locally you will need to +create a development environment. If you run into problems at any stage do not hesitate +to :ref:`ask for help `. + +Set up GitHub and Git +--------------------- + +Astropy is hosted on `GitHub `_, and to +contribute, you will need a `GitHub account +`_. + +We use `Git `_ for version control and to allow many people to +work together on the project. See the `GitHub quickstart instructions +`__ for installing and +configuring git, as well as the :ref:`git-resources` page. + +If you are new to contributing to projects through forking on GitHub, see the +`GitHub documentation for contributing to projects +`_. + +Install a C compiler if needed +------------------------------ + +How to do this will depend on your platform. + +**Windows** + +You will need `Build Tools for Visual Studio +`_. + +.. note:: + You DO NOT need to install Visual Studio. + You only need "Build Tools for Visual Studio" found by + scrolling down to "All downloads" -> "Tools for Visual Studio" -> "Build Tools + for Visual Studio". + +Alternative options include: + +- Install the necessary components on the command line using `vs_BuildTools.exe `_. +- Use the `WSL `_. + +**MacOS** + +Install the Developer Tools using ``xcode-select --install``. There is no need to +install the full Xcode application and this command will install only the command line +tools and developer utilities. + +Further details and related information can be found at +https://devguide.python.org/setup/#macos. + +**Linux** + +For Linux-based installations, you won't have to install any additional components. + +.. _contributing.forking: + +Create a clone of astropy +------------------------- + +If you have not done so already, you will need your own copy of ``astropy`` to +build it and/or contribute to the source. Astropy is hosted in the `astropy GitHub repository `_ and you need to make a clone. + +First, create a `GitHub Fork +`_ by going to the `astropy project page `_ +and hitting the ``Fork`` button. + +Next, `clone `__ your GitHub fork to your machine: + +.. code-block:: shell + + git clone https://github.com/YOUR-USER-NAME/astropy.git + cd astropy + git remote add upstream https://github.com/astropy/astropy.git + git fetch upstream --tags + +This creates the directory ``astropy`` and connects your repository to the upstream +(main project) `astropy `__ repository. + +You can see the remote repositories as follows:: + + git remote --verbose + +You will see something like:: + + origin git@github.com:YOUR-USER-NAME/astropy.git (fetch) + origin git@github.com:YOUR-USER-NAME/astropy.git (push) + upstream https://github.com/astropy/astropy.git (fetch) + upstream https://github.com/astropy/astropy.git (push) + +.. _create-isolated-env: + +Create an isolated development environment +------------------------------------------ + +A key requirement is to have an isolated Python environment, meaning that it is +isolated from both your system Python and any other Python environments you may have +for doing other work. This is important because the development environment will often +be unstable and possibly broken at times, and you don't want to break your other work. + +There are many good options for doing this, including a number of virtual environment +managers (e.g., the Python standard library `venv `_ +module). Users who have a preference for a particular virtual environment manager are +encouraged to use it! + +For this quickstart guide we use the `conda `_ package +manager provided by `miniforge `_. This is a +popular choice and generally works well, especially for newcomers. It is easy to install +and use on all platforms and it makes it easy to install different Python versions which +can be useful for testing. + +Install miniforge and conda +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you do not already have ``conda`` installed, `download and install miniforge +`_. The details depend on +your system but the end result is to provide a ``conda`` executable that you can use +to create and manage isolated Python environments. + +Now create and activate an ``astropy-dev`` conda environment using the following:: + + conda create -n astropy-dev python graphviz + conda activate astropy-dev + +Note the ``graphviz`` package is required for building the documentation. + +Install the development version of astropy +------------------------------------------ + +Now you can install the development version of astropy into your new environment. This +will install the latest version of astropy from your local git repo, along with +all the dependencies needed to build and fully test astropy:: + + python -m pip install --editable . --group dev_all + +**Checking the build** + +At this point you should be able to import astropy from your locally built version:: + + python -c 'import astropy; astropy.system_info()' + +Next you may want to try running some or all of the ``astropy`` unit tests. +Running the full test suite can take a few minutes, so you may want to start with a +single sub-package (e.g. :ref:`astropy-coordinates`):: + + + # run a sub set of the test suite + pytest astropy/coordinates + + # or the whole suite + pytest + +Details on running and writing tests can be found in the :ref:`testing-guidelines` +section. + +.. _contributing.pre-commit: + +Install pre-commit +------------------ + +This is optional, but *highly recommended*. `Pre-commit `_ is a +tool that runs a number of :ref:`Continuous Integration (CI) ` checks +(e.g. code formatting) on your code before you commit it. If you skip this step then it +is likely that one or more of those CI checks will fail when you make a pull request, +resulting in lost time (yours and CI resources). + +Installation is straightforward. From the root of the astropy repository, run:: + + pre-commit install + +Now all of the styling checks will be run each time you commit changes, ensuring that +the CI formatting checks for your :ref:`pull request ` will +pass. + +.. tip:: To learn more about pre-commit, see the :ref:`pre-commit` section. + +.. _contributing.pull_request: + +Creating and submitting a pull request +====================================== + +You can contribute bug fixes, new features, and documentation updates by submitting a +GitHub pull request (PR). This section will guide you through the process. We encourage +you to :ref:`ask for help ` if you get stuck. The Astropy +community is welcoming and friendly and will help you! + +If you are new to the Astropy Project and interested to submit a large patch +(e.g., a new big feature or significant refactoring), we encourage you to first +discuss your ideas on GitHub to increase the chance of your PR +being accepted. + +Creating a branch +----------------- + +Your local ``main`` branch should always reflect the current state of astropy repository. +First ensure it's up-to-date with the main astropy repository:: + + git switch main + git pull upstream main --ff-only + +Now create a development branch for making your changes. For example:: + + git switch -c subpackage-bug-fix + +This changes your working branch from ``main`` to the ``subpackage-bug-fix`` branch. +Keep any changes in this branch specific to one bug or feature so it is clear what the +branch brings to astropy. You can have many feature branches and switch in between them +using the `git switch `_ command. + +Using a descriptive branch name can help you stay organized. For example +```io-ascii-commented-header``` might be a good name for a branch that fixes the +commented header issue `#15513 `_ in +the ``io.ascii`` sub-package. + +When you want to update the feature branch with changes in main after +you created the branch, check the section on +:ref:`updating a PR `. + +.. _contributing.commit-code: + +Making code or documentation changes +------------------------------------ + +Now comes the fun part where you use your favorite editor or IDE to make changes to the +code or documentation! At a high level this breaks into a few parts: + +- **Make changes**: Make the changes you want to make. This could be fixing a bug, + adding a new feature, or updating the documentation. +- **Test changes**: For code changes, ensure that they work as expected following the + process outlined in the :ref:`testing-guidelines` section. +- **Build documentation**: If you are updating the documentation, you will want to + :ref:`build the documentation ` to ensure that it looks good. +- **Add a changelog entry**: For most code changes you will need to + :ref:`add-changelog`. + +.. tip:: For more information and examples see :ref:`edit-flow` section. + +You can see a summary of the changes you've currently made by running: + +.. code-block:: shell + + git status + +You can then commit your all your changes to your local repository with an explanatory +`commit message `_: + +.. code-block:: shell + + git add files-that-you-changed ... + git commit -m "your commit message goes here" + +.. Important:: Never merge changes from ``upstream/main`` into your feature branch. If + changes in ``main`` require changes to our code you must :ref:`rebase`. + +.. _contributing.push-code: + +Pushing your changes +-------------------- + +When you want your changes to appear publicly on your GitHub page, push your +forked feature branch's commits:: + + git push origin --set-upstream subpackage-bug-fix + +Here ``origin`` is the default name given to your fork on GitHub. + +Now your code is on GitHub, but it is not visible to the Astropy maintainers. For that +to happen, a pull request needs to be submitted on GitHub. + +The first time you push to a new branch on GitHub, you will see a message like below +with a useful link to create a pull request:: + + remote: Create a pull request for 'subpackage-bug-fix' on GitHub by visiting: + remote: https://github.com/YOUR-USER-NAME/astropy/pull/new/subpackage-bug-fix + + +.. _quickstart-pull-request: + +Making a pull request +--------------------- + +If everything looks good, you are ready to make a pull request (PR). A PR is how +code from your local repository becomes available to the GitHub community to review and +merged into project to appear the in the next release. + +Most of the time you can just follow the link that ``git`` provided when you pushed +your branch and create the PR. If you don't have that link (and for a few more details), you can follow the :ref:`pull-request` instructions. + +Follow the instructions in the PR template and fill it out as completely as possible. + +If your PR is still a work in progress then instead of clicking "Create pull request", +click on the small down arrow next to it and select "`Create draft pull request `__". +In addition, if your commits are not ready for CI testing, you +should include ``[ci skip]`` the last commit message – but note that code formatting +checks and documentation building will still be done. Formatting and style errors *should* +already have been fixed before committing if you have locally +:ref:`installed pre-commit`; but if you have not, +you can use the :ref:`pre-commit_bot` to fix them automatically in the PR. + +Once submitted (and marked as ready), this request goes to the astropy maintainers and +they will review the PR. + +.. _contributing.update-pr: + +Updating your pull request +-------------------------- + +Based on the review you get on your pull request, you will probably need to make +some adjustments. You can follow the :ref:`code committing steps ` +again to address any feedback and update your pull request:: + + git push origin subpackage-bug-fix + +Any ``git push`` will automatically update your pull request with your branch's changes +and restart the :ref:`Continuous Integration ` checks. + +.. Important:: At this point please read (or at least skim) the sections :ref:`revise + and push`, :ref:`rebase`, and :ref:`squash-if-necessary`. The information here + covers situations that happen on occasion and can be cause trouble. As always if + you have questions, ask for help from the maintainer reviewing your PR. + +Tips for a successful pull request +---------------------------------- + +If you have made it to this point and submitted a pull request, one of the core +maintainers will take a look. To make the process as smooth and efficient as possible, +here are some tips: + +- **Reference any existing open issue** to `link to that issue `_ and close the + issue if the PR is merged. +- **Ensure you have appropriate tests**. +- **Keep your pull requests as simple as possible** -- larger PRs take longer to review. +- **When practical, limit the scope of a PR to one sub-package** -- this means fewer + required reviewers and a faster review process. +- **Ensure that CI is in a green state** -- any required failures should be addressed. diff --git a/docs/development/scripts.rst b/docs/development/scripts.rst index 88301ee9b51c..c5eece41031a 100644 --- a/docs/development/scripts.rst +++ b/docs/development/scripts.rst @@ -1,43 +1,28 @@ -============================ -Writing Command-Line Scripts -============================ +******************** +Command-Line Scripts +******************** Command-line scripts in Astropy should follow a consistent scheme to promote readability and compatibility. -The actual script should be in the ``/scripts`` directory of the Astropy -source distribution, and should do nothing aside from importing a ``main`` -function from astropy and execute it. This is partly necessary because the -"2to3" utility that converts python 2.x code to 3.x does not convert scripts. -These scripts should be executable, include ``#!/usr/bin/env python`` at the -top, and should *not* end in ``.py``. - -The ``main`` functions these scripts call should accept an optional single -argument that holds the ``sys.argv`` list, except for the script name -(e.g., ``argv[1:]``). This function can live in its own module, or be part of a -larger module that implements a class or function for astropy library use. The -``main`` function should do very little actual work - it should only parse the -arguments and pass those arguments on to some library function so that the -library function can be used programmatically when needed. +Setuptools' `"entry points"`_ are used to automatically generate wrappers with +the correct extension. The scripts can live in their own module, or be part of +a larger module that implements a class or function for astropy library use. +They should have a ``main`` function to parse the arguments and pass those +arguments on to some library function so that the library function can be used +programmatically when needed. The ``main`` function should accept an optional +single argument that holds the ``sys.argv`` list, except for the script name +(e.g., ``argv[1:]``). It must then be added to the list of entry points in the +``setup.py`` file (see the example below). + Command-line options can be parsed however desired, but the :mod:`argparse` module is recommended when possible, due to its simpler and more flexible -interface relative to the older :mod:`optparse`. :mod:`argparse` is only -available in python >=2.7 and >=3.2, however, so it should be imported as -``from astropy.util.compat import argparse`` . +interface relative to the older :mod:`optparse`. +.. _"entry points": https://setuptools.readthedocs.io/en/latest/setuptools.html#automatic-script-creation Example -------- - -Contents of ``/scripts/cmdlinescript`` :: - - #!/usr/bin/env python - # -*- coding: utf-8 -*- - """An astropy command-line script""" - - import astropy.somepackage.somemod - - astropy.somepackage.somemod.main() +======= Contents of ``/astropy/somepackage/somemod.py`` :: @@ -49,7 +34,8 @@ Contents of ``/astropy/somepackage/somemod.py`` :: ...do something else... def main(args=None): - from astropy.utils.compat import argparse + + import argparse parser = argparse.ArgumentParser(description='Process some integers.') parser.add_argument('-o', '--option', dest='op',action='store_true', @@ -61,3 +47,7 @@ Contents of ``/astropy/somepackage/somemod.py`` :: do_something(res.stuff,res.op) +Then add the script to the ``pyproject.toml`` under this section:: + + [project.scripts] + somescript = "astropy.somepackage.somemod:main" diff --git a/docs/development/sphinxext.rst b/docs/development/sphinxext.rst deleted file mode 100644 index fb3fa3833b5d..000000000000 --- a/docs/development/sphinxext.rst +++ /dev/null @@ -1,26 +0,0 @@ -================= -Sphinx extensions -================= - -This page gives the API for the custom Sphinx extensions used in Astropy. - -automodapi Extension --------------------- - -.. automodule:: astropy.sphinx.ext.automodapi - - -automodsumm Extension ---------------------- - -.. automodule:: astropy.sphinx.ext.automodsumm - - -Numpydoc Extension ------------------- - -This extension is a port of the -`numpydoc `_ extentension written by -the `numpy `_ and `scipy `_ -projects, with some tweaks for Astropy. See the code for details, as it -is quite complex and includes a variety of interrelated extensions. diff --git a/docs/development/style-guide.rst b/docs/development/style-guide.rst new file mode 100644 index 000000000000..5a67581ec175 --- /dev/null +++ b/docs/development/style-guide.rst @@ -0,0 +1,355 @@ +.. _astropy-style-guide: + +***************************** +Astropy Narrative Style Guide +***************************** + +The purpose of this style guide is to provide the Astropy community with a set +of style and formatting guidelines that can be referenced when writing Astropy +documentation. Following the guidelines offered in this style guide will bring +greater consistency and clarity to Astropy's documentation, supporting its +mission to develop a common core package for Astronomy in Python and foster an +ecosystem of interoperable astronomy packages. + +This style guide is organized alphabetically by writing topic, with usage +examples in each section, and tone and formatting guidelines at the end. + +Abbreviations +============= + +Place abbreviations such as i.e. and e.g. within parentheses, where they are +followed by a comma. Alternatively, consider using "that is" and “for example” +instead, preceded by an em dash or semicolon and followed by a comma, or +contained within em dashes. + +Examples +-------- +* The only way to modify the data in a frame is by using the ``data`` attribute + directly and not the aliases for components on the frame (i.e., the following + will not work). +* There are no plans to support more complex evolution (e.g., non-inertial + frames or more complex evolution), as that is out of scope for the ``astropy`` + core. +* Once you have a coordinate object you can access the components of that + coordinate — for example, RA or Dec — to get string representations of the + full coordinate. + +For general use and scientific terms, use abbreviations only when the +abbreviated term is well-known and widely used within the astronomy community. +For less common scientific terms, or terms specific to a given field, write out +the term or link to a resource of explanation. A good rule of thumb to follow +when deciding whether or not something should be abbreviated is: when in doubt, +write it out. + +Examples +-------- +* 1D, 2D, etc. is preferred over one-dimensional, two-dimensional, etc. +* Units such as SI and CGS can be abbreviated as is more commonly seen in the + scientific community. +* White dwarf should be written out fully instead of abbreviated as WD. +* Names of organizations or other proper nouns that employ acronyms should be + written as their known acronym, but with a hyperlink to a website or resource + for reference, for instance, `CODATA `_. + +Capitalization +============== + +Capitalize all proper nouns (names) in plain text, except when referring to +package/code names, in which case use lowercase and double backticks. Astropy +capitalized refers to The Astropy Project, while ``astropy`` lowercase and in +backticks refers to the core package. + +Examples +-------- +* Follow Astropy guidelines for contributing code. +* Affiliated packages are astronomy-related software packages that are not part + of the ``astropy`` core package. +* Provide a code example along with details of the operating system and the + Python, ``numpy``, and ``astropy`` versions you are using. + +In Documentation materials, title case capitalization is preferred in headings, +meaning capitalize first, last, and all major words in the heading, but +lowercase articles (the, a, an), prepositions (at, to, up, down, with, in, +etc.), and common coordinating conjunctions (and, but, for, or). Sentence case +capitalization is acceptable for longer example headings. + +Examples +-------- +* Building and Installing +* Frames without Data +* Checklist for Contributing Code +* Astropy Guidelines +* Importing ``astropy`` and Subpackages +* Example: Use velocity to compute sky position at different epochs + +In Tutorials and other learning materials, title case capitalization is +preferred in headings of structured introductory/template sections, but within +the tutorial, sentence case (i.e., capitalize first word and proper nouns only) +is acceptable for longer headings designating different learning/code sections. + +Contractions +============ + +Do not use contractions in formal documentation material. + +Examples +-------- +* If you are making changes that impact ``astropy`` performance, consider adding + a performance benchmark. +* You do not need to include a changelog entry. + +In all other materials, avoid use of contractions only when the tense can be +confused, such as in the case of “she is gone” versus “she has gone,” etc. + +.. _Hyphenation: + +Hyphenation +=========== + +Phrasal adjectives/compound modifiers placed before a noun should be hyphenated +to avoid confusion. + +Examples +-------- +* Astronomy-related software packages. +* Astropy provides sustainable, high-level education to the astronomy community. + +Hyphenated compound words should contain hyphens in plain text, but no hyphens +in code. + +Example +------- +* Do not forget to double-check your formatting. + +Numbers +======= + +For numbers followed by a unit or as part of a name, use the numeral. + +Examples +-------- +* 1 arcminute +* 32 degrees +* Gaia data release 2 catalog +* 1D, 2D, etc. is preferred over one-dimensional, two-dimensional, etc. + +For all other whole numbers, follow Associated Press (AP) style: spell out +numbers one through nine, and use numerals for 10 and higher, with numeral-word +combinations for millions, billions, and trillions. + +Examples +-------- +* There are two ways to build Astropy documentation. +* Follow these 11 steps. +* Measuring astrometry for about 2 billion stars. + +For casual expressions, spell out the number. + +Example +------- +* A picture is worth a thousand words. + +Punctuation +=========== + +For consistency across Astropy materials, non-U.S. punctuation will be edited +to reflect American punctuation preferences. + +**Parentheses**: punctuation belonging to parenthetical material will be placed +inside of closing parentheses, with the exception of commas to denote a small +pause coming after parenthetical material, and periods when parenthetical +material is included within another sentence. + +Examples +-------- +* (For full contributor guidelines, see our documentation.) +* Once you open a pull request (which should be opened against the ``main`` + branch), please make sure to include the following. +* In some cases, most of the required functionality is contained in a single + class (or a few classes). + +**Quotation marks**: periods and commas will be placed inside of closing +quotation marks, whether double or single. + +Examples +-------- +* Chief among these terms is the concept of a “coordinate system.” +* Because of the likelihood of confusion between these meanings of “coordinate + system,” `~astropy.coordinates` avoids this term wherever possible. + +**Hyphens vs. En Dashes vs. Em Dashes** + +Hyphens (-) should be used for phrasal adjectives and compound words (see +`Hyphenation`_ above). + +En dashes (– longer) should be used for number ranges (dates, times, pages) or +to replace the words “to” or “through,” without spaces around the dash. + +Examples +-------- +* See chapters 14–18. +* We have blocked off March 2019–May 2019 to develop a new version. + +Em dashes (— longest) can be used in place of commas, parentheses, or colons to +set off amplifying or explanatory elements. In Astropy materials, follow +Associated Press (AP) style, which calls for spaces on either side of each em +dash. + +Examples +-------- +* Several types of input angles — array, scalar, tuple, string — can be used in + the creation of an Angle object. +* The creation of an Angle object supports a variety of input angle types — + array, scalar, tuple, string, etc. + +Spelling +======== + +For consistency across Astropy materials, non-U.S. spelling will be edited to +reflect American spelling preferences. + +Example +------- +* Cross-matching catalog coordinates (versus catalogue) + +Time and Date +============= + +Use numerals when exact times are expressed. Use the 24-hour system to express +exact times. For consistency across Astropy materials, all instances of exact +times will be edited to reflect 24-hour time system preferences. + +Example +------- +* The presentation starts at 15:00. + +Express specific dates as numerals in ISO 8601 format, year-month-day. + +Example +------- +* Data from the Gaia mission was released on 2018-04-25. + +A Note About Voice and Tone +=========================== + +Across all Astropy materials in narrative sections, please follow these voice +and tone guidelines. + +Write in the present tense. + +Example +------- +* In the following section, we are going to make a plot... +* To test if your version of ``astropy`` is running correctly... + +Use the first-person inclusive plural. + +Example +------- +* We did this the long way, but next we can try it the short way... + +Use the generic pronoun “you” instead of “one.” + +Example +------- +* You can access any of the attributes on a frame by... + +Always avoid extraneous or belittling words such as “obviously,” “easily,” +“simply,” “just,” or “straightforward.” Avoid extraneous phrases like, “we just +have to do one more thing.” + +Avoid words or phrases that create worry in the mind of the reader. Instead, +use positive language that establishes confidence in the skills being learned. + +Examples +-------- +* As a best practice... +* One recommended way to... +* An important note to remember is... + +Along these lines, use "warning" directives only to note limitations in the +code, not implied limitations in the skills or knowledge of the reader. + +Documentation vs. Tutorials vs. Guides +-------------------------------------- + +Documentation +^^^^^^^^^^^^^ +Tone: academic and slightly more formal. + +* Use title case capitalization in section headings. +* Do not use contractions. + +Tutorials +^^^^^^^^^ +Tone: academic but less formal and more friendly. + +* Use title case capitalization in introductory/template headings, switch to + sentence case capitalization for learning/example section headings. +* Section headings should use the imperative mood to form a command or request + (e.g., “Download the data”). +* Contractions can be used as long as the tense is clear. + +Guides +^^^^^^ +Tone: academic but less formal and more friendly. + +* Use title case capitalization in introductory/template headings, switch to + sentence case capitalization for learning/example section headings. +* Contractions can be used as long as the tense is clear. + +Formatting Guidelines +===================== + +Astropy documentation is written in reStructuredText using the Sphinx +documentation generator. When formatting the different sections of your +documentation files, please follow these guidelines to maintain consistency in +section heading hierarchy across Astropy's RST files. + +Section headings in reStructuredText files are created by underlining (and +optionally overlining) the section title with a punctuation character the same +length as the text. + +Examples +-------- + +:: + + ************************* + This is a Chapter Heading + ************************* + +:: + + This is a Section Heading + ========================= + +Although there are no formally assigned characters to create heading level +hierarchy, as the hierarchy rendering is determined from the succession of +headings, here is a suggested convention to follow when formatting Astropy +documentation files: + +# with overline, for parts +* with overline, for chapters +=, for sections +-, for subsections +^, for subsubsections +", for paragraphs + +These guidelines follow Sphinx's recommendation in the `Sections +`_ +chapter of its reStructuredText Primer and Python's convention in the `7.3.6. +Sections `_ part of its style +guide. + +Other Writing Resources +======================= + +Some other resources that may be useful when writing Astropy documentation are: + +* Python's `Style Guide + `_ +* Sphinx's `reStructuredText Primer + `_ +* `Quick reStructuredText + `_ diff --git a/docs/development/testguide.rst b/docs/development/testguide.rst index 64a719caaca6..10162438c2f0 100644 --- a/docs/development/testguide.rst +++ b/docs/development/testguide.rst @@ -1,226 +1,220 @@ +.. doctest-skip-all + .. _testing-guidelines: -================== +****************** Testing Guidelines -================== +****************** -This section describes the testing framework and format standards for tests in -Astropy core packages (this also serves as recommendations for affiliated -packages). +This section describes the |pytest| testing framework and format standards for tests in +Astropy core, coordinated packages, and packages using the |OpenAstronomy Packaging +Guide|. It also serves as recommendations for affiliated packages. -Testing Framework -================= +.. _testing-dependencies: -The testing framework used by Astropy is the `py.test `_ -framework. +Testing Dependencies +******************** -.. _running-tests: +Most commonly, you should install the full suite of testing and development +dependencies:: -Running Tests -============= + python -m pip install --editable . --group dev_all -There are currently three different ways to invoke Astropy tests. Each method -invokes py.test to run the tests but offers different options when calling. +This will provide all dependencies for running the full test suite using `tox `__ +and |pytest|. It will also allow running tests via any IDE which +supports ``pytest`` integration. -In addition to running the Astropy tests, these methods can also be called so -that they check Python source code for -`PEP8 compliance `_. All of the PEP8 -testing options require the -`pytest-pep8 plugin `_, which must be -installed separately. - -setup.py test -------------- - -The safest way to run the astropy test suite is via the setup command ``test``. -This is invoked by running ``python setup.py test`` while in the astropy source -code directory. Run ``python setup.py test --help`` to see the options to the -test command. +.. _running-tests: -Turn on PEP8 checking by passing ``--pep8`` to the ``test`` command. This will -turn off regular testing and enable PEP8 testing. +Running Tests +************* -.. note:: +There are two different ways to run Astropy tests: ``tox`` and +``pytest``. Each of these invokes |pytest| to run +the tests but each one addresses a different use-case. - This method of running the tests defaults to the version of `py.test` that - is bundled with Astropy. To use the locally-installed version, you can set - the ``ASTROPY_USE_SYSTEM_PYTEST`` environment variable, eg.:: +tox +=== - > ASTROPY_USE_SYSTEM_PYTEST=1 python setup.py test +The most robust way to run the tests (which can also be the slowest) is +to make use of `Tox `__, which is a +general purpose tool for automating Python testing. One of the benefits of tox +is that it first creates a source distribution of the package being tested, and +installs it into a new virtual environment, along with any dependencies that are +declared in the package, before running the tests. This can therefore catch +issues related to undeclared package data, or missing dependencies. Since we use +tox to run many of the tests on continuous integration services, it can also be +used in many cases to reproduce issues seen on those services. -py.test -------- +You can run the test suite with all optional dependencies with:: -An alternative way to run tests from the command line is to switch to the source -code directory of astropy and simply type:: + tox -e test-alldeps - py.test +Other useful invocations include:: -``py.test`` will look for files that `look like tests -`_ -in the currect directory and all recursive directories then run all the code that -`looks like tests -`_ -within those files. + tox -e test # Run the tests with the minimal set of dependencies + tox -l -v # Print a description of all available test environments + tox -e codestyle # Run code style checks using ``ruff`` .. note:: - To test any compiled C/Cython extensions, you must run ``python - setup.py develop`` prior to running the py.test command-line - script. Otherwise, any tests that make use of these extensions - will not succeed. Similarly, in python 3, these tests will not - run correctly in the source code, because they need the ``2to3`` - tool to be run on them. + It is suggested that you automate the code-style checks using the provided + pre-commit hook, as described in the :ref:`pre-commit` section. -You may specify a specific test file or directory at the command line:: +You can pass options directly to ``pytest`` when running tox by adding a +``--`` after the regular tox command. For example to enable verbose output and +debugging use:: - py.test test_file.py + tox -e test -- -v --pdb -To run a specific test within a file use the ``-k`` option:: +This can be used in conjunction with the ``-P`` option provided by the +`pytest-filter-subpackage `_ +plugin to run just part of the test suite. - py.test test_file.py -k "test_function" +Note that even though ``tox`` caches information, interactive debug and test +sessions with ``tox`` can be quite slow. For this case, it may be better to +set up a virtual environment with an editable install. Here, ``tox`` can still +help by setting up a complete test environment, which one can then activate:: -You may also use the ``-k`` option to not run tests py putting a ``-`` in front -of the matching string:: + tox -e test-alldeps --develop --notest + source .tox/test-alldeps/bin/activate - py.test test_file.py -k "-test_function" +Here, we use ``--notest`` to prevent ``tox`` from running the tests, since the +idea is to do that oneself -- using the ``pytest`` commands described below, +targeting the relevant sub-package or test file. -py.test has a number of `command line usage options. -`_ +.. _running-pytest: -Turn on PEP8 testing by adding the ``--pep8`` flag to the ``py.test`` call. By -default regular tests will also be run but these can be turned off by adding -``-k pep8``:: +pytest +====== - py.test some_dir --pep8 -k pep8 +The test suite can also be run directly from the native ``pytest`` command, which is +much faster than using ``tox`` for iterative development. This assumes you are working +in an :ref:`isolated development environment`. -.. note:: - This method of running the tests uses the locally-installed version of - `py.test` rather than the bundled one, and hence will fail if the local - version it is not up-to-date enough (`py.test` 2.2 as of this writing). +In the uncommon situation that one or more compiled extensions have changed, you will +need to rebuild them by re-running the usual editable install command:: -astropy.test() --------------- + python -m pip install --editable . --group dev_all -AstroPy includes a standalone version of py.test that allows to tests -to be run even if py.test is not installed. Tests can be run from within -AstroPy with:: +It is possible to run only the tests for a particular subpackage or set of +subpackages. For example, to run only the ``wcs`` and ``utils`` tests from the +commandline:: - import astropy - astropy.test() + pytest -P wcs,utils -This will run all the default tests for AstroPy. +You can also specify a single directory, a file (``.py`` python or ``.rst`` +doc file), or a specific test to check, rerun only tests that failed in +the previous run, or require remote data:: -Tests for a specific package can be run by specifying the package in the call -to the ``test()`` function:: + pytest astropy/modeling + pytest astropy/wcs/tests/test_wcs.py + pytest astropy/units -k float_dtype_promotion + pytest astropy/units/tests/test_quantity.py::TestQuantityCreation::test_float_dtype_promotion + pytest astropy/wcs/index.rst + pytest --last-failed + pytest --remote-data=any - astropy.test('io.fits') +For more details, see the `pytest invocation guide +`_ and the +description of `caching +`_. -This method works only with package names that can be mapped to Astropy -directories. As an alternative you can test a specific directory or file -with the ``test_path`` option:: +Test-running options +==================== - astropy.test(test_path='wcs/tests/test_wcs.py') +.. _open-files: -The ``test_path`` must be specified either relative to the working directory -or absolutely. +Testing for open files +---------------------- -By default ``astropy.test()`` will skip tests which retrieve data from the -internet. To turn these tests on use the ``remote_data`` flag:: +The ``filterwarnings`` settings under ``[tool.pytest.ini_options]`` in the +``pyproject.toml`` file has an option which converts all unhandled warnings to +errors during a test run. As a result, any open file(s) that throw +``ResourceWarning`` (except the specific ones already ignored) would fail the +affected test(s). - astropy.test('io.fits', remote_data=True) +Test coverage reports +--------------------- -In addition, the ``test`` function supports any of the options that can be -passed to `pytest.main() `_, -and convenience options ``verbose=``, ``pastebin=`` and ``coverage=``. +Coverage reports can be generated using the `pytest-cov +`_ plugin (which is installed +automatically when installing pytest-astropy) by using e.g.:: -Enable PEP8 compliance testing with ``pep8=True`` in the call to -``astropy.test``. This will enable PEP8 checking and disable regular tests. + pytest --cov astropy --cov-report html -.. note:: - This method of running the tests defaults to the version of - `py.test` that is bundled with Astropy. To use the locally-installed - version, you should set the ``ASTROPY_USE_SYSTEM_PYTEST`` environment - variable (see :doc:`/configs/index`) or the `py.test` method described - above. +There is some configuration inside the ``pyproject.toml`` file that +defines files to omit as well as lines to exclude. -Regression tests -================ +Running tests in parallel +------------------------- -Any time a bug is fixed, and wherever possible, one or more regression tests -should be added to ensure that the bug is not introduced in future. Regression -tests should include the ticket URL where the bug was reported. +It is possible to speed up astropy's tests using the `pytest-xdist +`_ plugin. -Where to put tests -================== - -Package-specific tests ----------------------- +Once installed, tests can be run in parallel using the ``'-n'`` +commandline option. For example, to use 4 processes:: -Each package should include a suite of unit tests, covering as many of the -public methods/functions as possible. These tests should be included inside -each sub-package, either in a `tests` directory, or in a test.py file, e.g:: + pytest -n 4 - astropy/io/fits/tests/ +Pass ``-n auto`` to create the same number of processes as cores +on your machine. -or:: +.. _running-tests-installed-astropy: - astropy/io/fits/test.py +Running tests on an installed ``astropy`` +----------------------------------------- -``tests`` directories should contain an ``__init__.py`` file so that the tests -can be imported and so that they can use relative imports. +You can also run the tests on an installed version of ``astropy``. First you need to +ensure that the testing dependencies are installed:: -Interoperability tests ----------------------- - -Tests involving two or more sub-packages should be included in:: - - astropy/tests/ + python -m pip install "astropy[test]" -and using:: +Note that you can include the ``--dry-run`` option to see what would be installed. In +particular ``astropy`` itself should not be re-installed since it already exists. Then +from any directory other than an ``astropy`` source repository, run the following:: - astropy.test() + pytest --pyargs astropy -then runs both these interoperability tests, and all the unit tests in the -sub-packages. This functionality is especially important for people who install -packages through bundles and package managers, where the original source code -for the tests is not immediately available. +You can also include other ``pytest`` options as needed. +.. _writing-tests: Writing tests -============= +************* -``py.test`` has the following test discovery rules: +``pytest`` has the following test discovery rules: * ``test_*.py`` or ``*_test.py`` files * ``Test`` prefixed classes (without an ``__init__`` method) * ``test_`` prefixed functions and methods -Consult the `test discovery rules -`_ +Consult the :ref:`test discovery rules ` for detailed information on how to name files and tests so that they are -automatically discovered by ``py.test``. +automatically discovered by |pytest|. Simple example --------------- +============== The following example shows a simple function and a test to test this function:: def func(x): + """Add one to the argument.""" return x + 1 def test_answer(): + """Check the return value of func() for an example argument.""" assert func(3) == 5 If we place this in a ``test.py`` file and then run:: - py.test test.py + pytest test.py The result is:: ============================= test session starts ============================== - python: platform darwin -- Python 2.7.2 -- pytest-1.1.1 - test object 1: /Users/tom/tmp/test.py + python: platform darwin -- Python 3.x.x -- pytest-x.x.x + test object 1: /Users/username/tmp/test.py test.py F @@ -235,71 +229,140 @@ The result is:: test.py:5: AssertionError =========================== 1 failed in 0.07 seconds =========================== +Where to put tests +================== + +Package-specific tests +---------------------- + +Each package should include a suite of unit tests, covering as many of +the public methods/functions as possible. These tests should be +included inside each sub-package, e.g:: + + astropy/io/fits/tests/ + +``tests`` directories should contain an ``__init__.py`` file so that +the tests can be imported and so that they can use relative imports. + +Interoperability tests +---------------------- + +Tests involving two or more sub-packages should be included in:: + + astropy/tests/ + +Regression tests +================ + +Any time a bug is fixed, and wherever possible, one or more regression tests +should be added to ensure that the bug is not introduced in future. Regression +tests should include the ticket URL where the bug was reported. + +.. _data-files: + Working with data files ------------------------ +======================= Tests that need to make use of a data file should use the -`~astropy.config.data.get_data_fileobj` or -`~astropy.config.data.get_data_filename` functions. These functions search -locally first, and then on the astropy data server or an arbitrary URL, and -return a file-like object or a local filename, respectively. They automatically -cache the data locally if remote data is obtained, and from then on the local -copy will be used transparently. +`~astropy.utils.data.get_pkg_data_fileobj` or +`~astropy.utils.data.get_pkg_data_filename` functions. These functions +search locally first, and then on the astropy data server or an arbitrary +URL, and return a file-like object or a local filename, respectively. They +automatically cache the data locally if remote data is obtained, and from +then on the local copy will be used transparently. See the next section for +note specific to dealing with the cache in tests. They also support the use of an MD5 hash to get a specific version of a data file. This hash can be obtained prior to submitting a file to the astropy -data server by using the `~astropy.config.data.compute_hash` function on a +data server by using the `~astropy.utils.data.compute_hash` function on a local copy of the file. -Tests that may retrieve remote data should be marked with the ``@remote_data`` -decorator. Tests marked with this decorator will be skipped by default by -``astropy.test()`` to prevent test runs from taking too long. These tests can -be run by ``astropy.test()`` by adding the ``remote_data=True`` flag. -Turn on the remote data tests at the command line with -``py.test --remote-data``. +Tests that may retrieve remote data should be marked with the +``@pytest.mark.remote_data`` decorator, or, if a doctest, flagged with the +``REMOTE_DATA`` flag. Tests marked in this way will be skipped by default by +``pytest`` to prevent test runs from taking too long. These tests can be run +with ``pytest --remote-data=any``. + +It is possible to mark tests using +``@pytest.mark.remote_data(source='astropy')``, which can be used to indicate +that the only required data is from the http://data.astropy.org server. To +enable just these tests, you can run the +tests with ``pytest --remote-data=astropy``. + +For more information on the ``pytest-remotedata`` plugin, see +|pytest-remotedata|. Examples -^^^^^^^^ -:: +-------- +.. code-block:: python from ...config import get_data_filename - from ...tests.helper import remote_data def test_1(): + """Test version using a local file.""" #if filename.fits is a local file in the source distribution datafile = get_data_filename('filename.fits') # do the test - @remote_data + @pytest.mark.remote_data def test_2(): + """Test version using a remote file.""" #this is the hash for a particular version of a file stored on the #astropy data server. datafile = get_data_filename('hash/94935ac31d585f68041c08f87d1a19d4') # do the test + def doctest_example(): + """ + >>> datafile = get_data_filename('hash/94935') # doctest: +REMOTE_DATA + """ + pass + The ``get_remote_test_data`` will place the files in a temporary directory indicated by the ``tempfile`` module, so that the test files will eventually get removed by the system. In the long term, once test data files become too large, we will need to design a mechanism for removing test data immediately. +Tests that use the file cache +----------------------------- + +By default, Astropy's test configuration sets up a clean file cache in a temporary +directory that is used only for that test run and then destroyed. This is to +ensure consistency between test runs, as well as to not clutter users' caches +(i.e., the cache directory returned by `~astropy.config.get_cache_dir_path`) with +test files. + +However, some test authors (especially for affiliated packages) may find it +desirable to cache files downloaded during a test run in a more permanent +location (e.g., for large data sets). To this end the +`~astropy.config.temporary_cache_dir_path` helper may be used. It can be used either as +a context manager within a test to temporarily set the cache to a custom +location, or as a *decorator* that takes effect for an entire test function +(not including setup or teardown, which would have to be decorated separately). + +Furthermore, it is possible to change the location of the cache directory +for the duration of the test run via :ref:`environment_variables`. + + Tests that create files ------------------------ +======================= + +Some tests involve writing files. These files should not be saved permanently. +The :ref:`pytest 'tmp_path' fixture ` allows for the +convenient creation of temporary directories, which ensures test files will be +cleaned up. Temporary directories can also be helpful in the case where the +tests are run in an environment where ``pytest`` would otherwise not have write +access. -Tests may often be run from directories where users do not have write permissions -so tests which create files should always do so in temporary directories. This -can be done with the `py.test tmpdir function argument -`_ -or with Python's built-in `tempfile module -`_. Setting up/Tearing down tests ------------------------------ +============================= In some cases, it can be useful to run a series of tests requiring something to be set up first. There are four ways to do this: Module-level setup/teardown -^^^^^^^^^^^^^^^^^^^^^^^^^^^ +--------------------------- If the ``setup_module`` and ``teardown_module`` functions are specified in a file, they are called before and after all the tests in the file respectively. @@ -307,12 +370,15 @@ These functions take one argument, which is the module itself, which makes it very easy to set module-wide variables:: def setup_module(module): + """Initialize the value of NUM.""" module.NUM = 11 def add_num(x): + """Add pre-defined NUM to the argument.""" return x + NUM def test_42(): + """Ensure that add_num() adds the correct NUM to its argument.""" added = add_num(42) assert added == 53 @@ -322,34 +388,41 @@ the functions in the file access it:: import os def setup_module(module): + """Store a copy of the remote test file.""" module.DATAFILE = get_remote_test_data('94935ac31d585f68041c08f87d1a19d4') def test(): + """Perform test using cached remote input file.""" f = open(DATAFILE, 'rb') # do the test def teardown_module(module): + """Clean up remote test file copy.""" os.remove(DATAFILE) -Class-level -^^^^^^^^^^^ +Class-level setup/teardown +-------------------------- Tests can be organized into classes that have their own setup/teardown -functions. In the following :: +functions. In the following:: def add_nums(x, y): + """Add two numbers.""" return x + y - class TestAdd42(object): + class TestAdd42: + """Test for add_nums with y=42.""" def setup_class(self): self.NUM = 42 def test_1(self): + """Test behavior for a specific input value.""" added = add_nums(11, self.NUM) assert added == 53 def test_2(self): + """Test behavior for another input value.""" added = add_nums(13, self.NUM) assert added == 55 @@ -359,34 +432,38 @@ functions. In the following :: In the above example, the ``setup_class`` method is called first, then all the tests in the class, and finally the ``teardown_class`` is called. -Method-level -^^^^^^^^^^^^ +Method-level setup/teardown +--------------------------- There are cases where one might want setup and teardown methods to be run before and after *each* test. For this, use the ``setup_method`` and ``teardown_method`` methods:: def add_nums(x, y): + """Add two numbers.""" return x + y - class TestAdd42(object): + class TestAdd42: + """Test for add_nums with y=42.""" def setup_method(self, method): self.NUM = 42 def test_1(self): + """Test behavior for a specific input value.""" added = add_nums(11, self.NUM) assert added == 53 def test_2(self): + """Test behavior for another input value.""" added = add_nums(13, self.NUM) assert added == 55 def teardown_method(self, method): pass -Function-level -^^^^^^^^^^^^^^ +Function-level setup/teardown +----------------------------- Finally, one can use ``setup_function`` and ``teardown_function`` to define a setup/teardown mechanism to be run before and after each function in a module. @@ -396,19 +473,65 @@ These take one argument, which is the function being tested:: pass def test_1(self): + """First test.""" # do test def test_2(self): + """Second test.""" # do test - def teardown_method(function): + def teardown_function(function): pass +Property-based tests +==================== + +`Property-based testing +`_ +lets you focus on the parts of your test that matter, by making more +general claims - "works for any two numbers" instead of "works for 1 + 2". +Imagine if random testing gave you minimal, non-flaky failing examples, +and a clean way to describe even the most complicated data - that's +property-based testing! + +``pytest-astropy`` includes a dependency on `Hypothesis +`_, so installation is easy - +you can just read the docs or `work through the tutorial +`_ +and start writing tests like:: + + from astropy.coordinates import SkyCoord + from hypothesis import given, strategies as st + + @given( + st.builds(SkyCoord, ra=st.floats(0, 360), dec=st.floats(-90, 90)) + ) + def test_coordinate_transform(coord): + """Test that sky coord can be translated from ICRS to Galactic and back.""" + assert coord == coord.galactic.icrs # floating-point precision alert! + +Other properties that you could test include: + +- Round-tripping from image to sky coordinates and back should be lossless + for distortion-free mappings, and otherwise always below 10^-5 px. +- Take a moment in time, round-trip it through various frames, and check it + hasn't changed or lost precision. (or at least not by more than a nanosecond) +- IO routines losslessly round-trip data that they are expected to handle +- Optimised routines calculate the same result as unoptimised, within tolerances + +This is a great way to start contributing to Astropy, and has already found +bugs in time handling. See issue `#9017 `_ +and pull request `#9532 `_ for details! + +(and if you find Hypothesis useful in your research, +`please cite it `_!) + + Parametrizing tests -------------------- +=================== -If you want to run a test several times for slightly different values, then -it can be advantageous to use the ``py.test`` option to parametrize tests. +If you want to run a test several times for slightly different values, +you can use ``pytest`` to avoid writing separate tests. For example, instead of writing:: def test1(): @@ -420,98 +543,424 @@ For example, instead of writing:: def test3(): assert type('c') == str -You can use the ``parametrize`` decorator to loop over the different -inputs:: +You can use the ``@pytest.mark.parametrize`` decorator to concisely +create a test function for each input:: @pytest.mark.parametrize(('letter'), ['a', 'b', 'c']) def test(letter): + """Check that the input is a string.""" assert type(letter) == str -Using py.test helper functions ------------------------------- +As a guideline, use ``parametrize`` if you can enumerate all possible +test cases and each failure would be a distinct issue, and Hypothesis +when there are many possible inputs or you only want a single simple +failure to be reported. + +Tests requiring optional dependencies +===================================== + +For tests that test functions or methods that require optional dependencies +(e.g., Scipy), pytest should be instructed to skip the test if the dependencies +are not present, as the ``astropy`` tests should succeed even if an optional +dependency is not present. ``astropy`` provides a list of boolean flags that +test whether optional dependencies are installed (at import time). For example, +to load the corresponding flag for Scipy and mark a test to skip if Scipy is not +present, use:: -If your tests need to use `py.test helper functions -`_, such as ``pytest.raises``, -import ``pytest`` into your test module like so:: + import pytest + from astropy.utils.compat.optional_deps import HAS_SCIPY - from ...tests.helper import pytest + @pytest.mark.skipif(not HAS_SCIPY, reason='scipy is required') + def test_that_uses_scipy(): + ... -You may need to adjust the relative import to work for the depth of your module. -``tests.helper`` imports ``pytest`` either from the user's system or ``extern.pytest`` -if the user does not have py.test installed. This is so that users need not -install py.test to run AstroPy's tests. +These variables should exist for all of Astropy's optional dependencies; a +complete list of supported flags can be found in +``astropy.utils.compat.optional_deps``. -Using data in tests -=================== +Any new optional dependencies should be added to that file, as well as to the +relevant entries in the ``pyproject.toml`` file in the +``[project.optional-dependencies]`` section; typically, under ``all`` for +dependencies used in user-facing code (e.g., ``h5py``, which is used to write +tables to HDF5 format), and in ``test_all`` for dependencies only used in tests +(e.g., ``skyfield``, which is used to cross-check the accuracy of coordinate +transforms). -Tests can include very small datafiles, but any files significantly larger -than the source code should be placed on a remote server. The base URL for the -test files will be:: +Testing warnings +================ - http://data.astropy.org/ +In order to test that warnings are triggered as expected in certain +situations, +|pytest| provides its own context manager +:ref:`pytest.warns ` that, completely +analogously to ``pytest.raises`` (see below) allows to probe explicitly +for specific warning classes and, through the optional ``match`` argument, +messages. Note that when no warning of the specified type is +triggered, this will make the test fail. When checking for optional, +but not mandatory warnings, ``pytest.warns()`` can be used to catch and +inspect them. -and files will be accessed by their MD5 hash, for example:: +.. note:: - http://data.astropy.org/94935ac31d585f68041c08f87d1a19d4 + With |pytest| there is also the option of using the + :ref:`recwarn ` function argument to test that + warnings are triggered within the entire embedding function. + This method has been found to be problematic in at least one case + (`pull request 1174 `_). -Tests then retrieve data via this URL. This implicitly allows versioning, -since different versions of data files will have different hashes. Old data -files should not be removed, so that tests can be run in any version of -AstroPy. +Testing exceptions +================== -The details of the server implementation have yet to be decided, but using -these static hash-based URLs ensures that even if we change the backend, the -URL will remain the same. +Just like the handling of warnings described above, tests that are +designed to trigger certain errors should verify that an exception of +the expected type is raised in the expected place. This is efficiently +done by running the tested code inside the +:ref:`pytest.raises ` +context manager. Its optional ``match`` argument allows to check the +error message for any patterns using ``regex`` syntax. For example the +matches ``pytest.raises(OSError, match=r'^No such file')`` and +``pytest.raises(OSError, match=r'or directory$')`` would be equivalent +to ``assert str(err).startswith(No such file)`` and ``assert +str(err).endswith(or directory)``, respectively, on the raised error +message ``err``. +For matching multi-line messages you need to pass the ``(?s)`` +:ref:`flag ` +to the underlying ``re.search``, as in the example below:: + + with pytest.raises(fits.VerifyError, match=r'(?s)not upper.+ Illegal key') as excinfo: + hdu.verify('fix+exception') + assert str(excinfo.value).count('Card') == 2 + +This invocation also illustrates how to get an ``ExceptionInfo`` object +returned to perform additional diagnostics on the info. + +Testing configuration parameters +================================ + +In order to ensure reproducibility of tests, all configuration items +are reset to their default values when ``pytest`` starts up. + +Sometimes you'll want to test the behavior of code when a certain +configuration item is set to a particular value. In that case, you +can use the `astropy.config.ConfigItem.set_temp` context manager to +temporarily set a configuration item to that value, test within that +context, and have it automatically return to its original value. + +For example:: + + def test_pprint(): + from ... import conf + with conf.set_temp('max_lines', 6): + # ... +Marking blocks of code to exclude from coverage +=============================================== -Tests requiring optional dependencies -===================================== +Blocks of code may be ignored by the coverage testing by adding a +comment containing the phrase ``pragma: no cover`` to the start of the +block:: -For tests that test functions or methods that require optional dependencies (e.g. Scipy), pytest should be instructed to skip the test if the dependencies are not present. The following example shows how this should be done:: + if this_rarely_happens: # pragma: no cover + this_call_is_ignored() - import pytest +.. _image-tests: - try: - import scipy - HAS_SCIPY = True - except ImportError: - HAS_SCIPY = False +Image tests with pytest-mpl +=========================== - @pytest.mark.skipif('not HAS_SCIPY') - def test_that_uses_scipy(): +Running image tests +------------------- + +We make use of the `pytest-mpl `_ +plugin to write tests where we can compare the output of plotting commands +with reference files on a pixel-by-pixel basis (this is used for instance in +:ref:`astropy.visualization.wcsaxes `). We use the `hybrid mode +`_ with +hashes and images. + +To run the Astropy tests with the image comparison, use e.g.:: + + tox -e py311-test-image-mpl380-cov + +However, note that the output can be sensitive to the operating system and +specific version of libraries such as freetype. In general, using tox will +result in the version of freetype being pinned, but the hashes will only be +correct when running the tests on Linux. Therefore, if using another operating +system, we do not recommend running the image tests locally and instead it is +best to rely on these running in an controlled continuous integration +environment. + +Writing image tests +------------------- + +The `README.rst `__ +for the plugin contains information on writing tests with this plugin. Once you +have added a test, and push this to a pull request, you will likely start seeing +a test failure because the figure hash is missing from the hash libraries +(see the next section for how to proceed). + +Rather than use the ``@pytest.mark.mpl_image_compare`` decorator directly, you +should make use of the ``@figure_test`` convenience decorator which +sets the default tolerance and style to be consistent across the astropy core +package, and also automatically enables access to remote data:: + + from astropy.tests.figures import figure_test + + @figure_test + def test_figure(): + fig, ax = plt.subplots() ... + return fig -In this way, the test is run if Scipy is present, and skipped if not. No tests should fail simply because an optional dependency is not present. +You can optionally pass keyword arguments to ``@figure_test`` and these will be +passed on to ``mpl_image_compare``:: -Test coverage reports -===================== + @figure_test(savefig_kwargs={'bbox_inches': 'tight'}) + def test_figure(): + ... -Astropy can use `coverage.py -`_ to generate test coverage -reports. To generate a test coverage report, use:: +Failing tests +------------- - python setup.py test --coverage +When existing tests start failing, it is usually either because of a change in +astropy itself, or a change in Matplotlib. New tests will also fail if you have +not yet updated the hash library. -There is a `coveragerc -`_ file that -defines files to omit as well as lines to exclude. It is installed -along with astropy so that the `astropy.test` function can use it. In -the source tree, it is at `astropy/tests/coveragerc`. +In all cases, you can view a webpage with all the existing figures where you can +check whether any of the figures are now wrong, or if all is well. The link to +the page for each tox environment that has been run will be provided in the +list of statuses for pull requests, and can also be found in the CircleCI +logs. If any changes/additions look good, you can download from the summary page +a JSON file with the hashes which you can use to replace the existing one in +``astropy/tests/figures``. -Marking blocks of code to exclude from coverage ------------------------------------------------ +New hash libraries +------------------ -Blocks of code may be ignored by adding a comment containing the -phrase ``pragma: no cover`` to the start of the block:: +When adding a new tox environment for image testing, such as for a new Matplotlib +or Python version, the tests will fail as the hash library does not exist yet. +Instead of generating it from scratch, you can simply copy a +previous version of the JSON file and update any failing hashes as described +in `Failing tests`_. - if this_rarely_happens: # pragma: no cover - this_call_is_ignored() +Generating reference images +--------------------------- + +You do not need to generate reference images for new tests or updated reference +images for changed tests - when pull requests are merged, a CircleCI job will automatically +update the reference images in the `astropy-figure-tests `_ +repository. + +.. _doctests: + +Writing doctests +**************** + +A doctest in Python is a special kind of test that is embedded in a +function, class, or module's docstring, or in the narrative Sphinx +documentation, and is formatted to look like a Python interactive +session--that is, they show lines of Python code entered at a ``>>>`` +prompt followed by the output that would be expected (if any) when +running that code in an interactive session. + +The idea is to write usage examples in docstrings that users can enter +verbatim and check their output against the expected output to confirm that +they are using the interface properly. + +Furthermore, Python includes a :mod:`doctest` module that can detect these +doctests and execute them as part of a project's automated test suite. This +way we can automatically ensure that all doctest-like examples in our +docstrings are correct. + +The Astropy test suite automatically detects and runs any doctests in the +astropy source code or documentation, or in packages using the Astropy test +running framework. For example doctests and detailed documentation on how to +write them, see the full :mod:`doctest` documentation. + +For more information on the ``pytest-doctestplus`` plugin used by Astropy, see +|pytest-doctestplus|. + +.. _skipping-doctests: -Blocks of code that are intended to run only in Python 2.x or 3.x may -also be marked so that they will be ignored when appropriate by -`coverage.py`:: +Skipping doctests +================= + +Sometimes it is necessary to write examples that look like doctests but that +are not actually executable verbatim. An example may depend on some external +conditions being fulfilled, for example. In these cases there are a few ways to +skip a doctest: + +1. Next to the example add a comment like: ``# doctest: +SKIP``. For example: + + .. code-block:: none + + >>> import os + >>> os.listdir('.') # doctest: +SKIP + + In the above example we want to direct the user to run ``os.listdir('.')`` + but we don't want that line to be executed as part of the doctest. + + To skip tests that require fetching remote data, use the ``REMOTE_DATA`` + flag instead. This way they can be turned on using the + ``--remote-data`` flag when running the tests: + + .. code-block:: none + + >>> datafile = get_data_filename('hash/94935') # doctest: +REMOTE_DATA + +2. Astropy's test framework adds support for a special ``__doctest_skip__`` + variable that can be placed at the module level of any module to list + functions, classes, and methods in that module whose doctests should not + be run. That is, if it doesn't make sense to run a function's example + usage as a doctest, the entire function can be skipped in the doctest + collection phase. + + The value of ``__doctest_skip__`` should be a list of wildcard patterns + for all functions/classes whose doctests should be skipped. For example:: + + __doctest_skip__ = ['myfunction', 'MyClass', 'MyClass.*'] + + skips the doctests in a function called ``myfunction``, the doctest for a + class called ``MyClass``, and all *methods* of ``MyClass``. + + Module docstrings may contain doctests as well. To skip the module-level + doctests include the string ``'.'`` in ``__doctest_skip__``. + + To skip all doctests in a module:: + + __doctest_skip__ = ['*'] + +3. In the Sphinx documentation, a doctest section can be skipped by + making it part of a ``doctest-skip`` directive:: + + .. doctest-skip:: + + >>> # This is a doctest that will appear in the documentation, + >>> # but will not be executed by the testing framework. + >>> 1 / 0 # Divide by zero, ouch! + + It is also possible to skip all doctests below a certain line using + a ``doctest-skip-all`` comment. Note the lack of ``::`` at the end + of the line here:: + + .. doctest-skip-all + + All doctests below here are skipped... + +4. ``__doctest_requires__`` is a way to list dependencies for specific + doctests. It should be a dictionary mapping wildcard patterns (in the same + format as ``__doctest_skip__``) to a list of one or more modules that should + be *importable* in order for the tests to run. For example, if some tests + require the scipy module to work they will be skipped unless ``import + scipy`` is possible. It is also possible to use a tuple of wildcard + patterns as a key in this dict:: + + __doctest_requires__ = {('func1', 'func2'): ['scipy']} + + Having this module-level variable will require ``scipy`` to be importable + in order to run the doctests for functions ``func1`` and ``func2`` in that + module. + + In the Sphinx documentation, a doctest requirement can be notated with the + ``doctest-requires`` directive:: + + .. doctest-requires:: scipy + + >>> import scipy + >>> scipy.hamming(...) + + +Skipping output +=============== + +One of the important aspects of writing doctests is that the example output +can be accurately compared to the actual output produced when running the +test. + +The doctest system compares the actual output to the example output verbatim +by default, but this not always feasible. For example the example output may +contain the ``__repr__`` of an object which displays its id (which will change +on each run), or a test that expects an exception may output a traceback. + +The simplest way to generalize the example output is to use the ellipses +``...``. For example:: + + >>> 1 / 0 + Traceback (most recent call last): + ... + ZeroDivisionError: integer division or modulo by zero + +This doctest expects an exception with a traceback, but the text of the +traceback is skipped in the example output--only the first and last lines +of the output are checked. See the :mod:`doctest` documentation for +more examples of skipping output. + +Ignoring all output +------------------- + +Another possibility for ignoring output is to use the +``# doctest: +IGNORE_OUTPUT`` flag. This allows a doctest to execute (and +check that the code executes without errors), but allows the entire output +to be ignored in cases where we don't care what the output is. This differs +from using ellipses in that we can still provide complete example output, just +without the test checking that it is exactly right. For example:: + + >>> print('Hello world') # doctest: +IGNORE_OUTPUT + We don't really care what the output is as long as there were no errors... + +.. _handling-float-output: + +Handling float output +===================== - if sys.version_info[0] >= 3: # pragma: py3 - do_it_the_python3_way() - else: # pragma: py2 - do_it_the_python2_way() +Some doctests may produce output that contains string representations of +floating point values. Floating point representations are often not exact and +contain roundoffs in their least significant digits. Depending on the platform +the tests are being run on (different Python versions, different OS, etc.) the +exact number of digits shown can differ. Because doctests work by comparing +strings this can cause such tests to fail. + +To address this issue, the ``pytest-doctestplus`` plugin provides support for a +``FLOAT_CMP`` flag that can be used with doctests. For example: + +.. code-block:: none + + >>> 1.0 / 3.0 # doctest: +FLOAT_CMP + 0.333333333333333311 + +When this flag is used, the expected and actual outputs are both parsed to find +any floating point values in the strings. Those are then converted to actual +Python `float` objects and compared numerically. This means that small +differences in representation of roundoff digits will be ignored by the +doctest. The values are otherwise compared exactly, so more significant +(albeit possibly small) differences will still be caught by these tests. + +Continuous integration +********************** + +Overview +======== + +Astropy uses the following continuous integration (CI) services: + +* `GitHub Actions `_ for + Linux, OS X, and Windows setups + (Note: GitHub Actions does not have "allowed failures" yet, so you might + see a fail job reported for your PR with "(Allowed Failure)" in its name. + Still, some failures might be real and related to your changes, so check + it anyway!) +* `CircleCI `_ for visualization tests + +These continuously test the package for each commit and pull request that is +pushed to GitHub to notice when something breaks. + +In some cases, you may see failures on continuous integration services that +you do not see locally, for example because the operating system is different, +or because the failure happens with only 32-bit Python. + +Maintainers have the option to run :ref:`comparative benchmark ` using GitHub Actions +to test a new pull request against the current ``main`` branch. It uses the benchmarks +from `astropy-benchmarks `_. +It is important to note that these benchmarks can be flaky as they run on +virtual machines (and thus shared hardware) but they should give a general +idea of the performance impact of a pull request. diff --git a/docs/development/vision.rst b/docs/development/vision.rst index 82f9bd841ad1..2b46ae27873b 100644 --- a/docs/development/vision.rst +++ b/docs/development/vision.rst @@ -1,8 +1,10 @@ +:orphan: + .. _vision: -============================================ +******************************************** Vision for a Common Astronomy Python Package -============================================ +******************************************** The following document summarizes a vision for a common Astronomy Python package, and how we can best all work together to achieve this. In the @@ -23,7 +25,7 @@ tasks, and thus reduce the number of dependencies, reduce duplication of functionality, and increase consistency of their interfaces. Procedure ---------- +========= With the help of the community, the coordination committee will start by identifying a few of key areas where initial development/consolidation will be @@ -55,7 +57,7 @@ for inclusion in the core will need to follow the layout of a ‘template’ package that will be provided before development starts. Dependencies ------------- +============ Affiliated packages should be able to be imported with only the following dependencies: @@ -87,11 +89,11 @@ packages being integrated into the core package are consistent with those already in the core package. Initially, no dependency on GUI toolkits will be allowed in the core package. -If the community reaches agrees on a single toolkit that could be used, then -this toolkit will be allowed (but will only be imported as needed). +If the community reaches an agreement on a single toolkit that could be used, +then this toolkit will be allowed (but will only be imported as needed). Keeping track of affiliated packages ------------------------------------- +==================================== Affiliated packages will be listed in a central location (in addition to PyPI) that will allow an easy installation of all the affiliated packages, for @@ -100,7 +102,7 @@ affiliated packages. The core package will also include mechanisms to facilitate this installation process. Existing Packages ------------------ +================= Developers who already have existing packages will be encouraged to continue supporting them for the benefit of users until the core library is considered diff --git a/docs/development/worked_example_switch_branch.png b/docs/development/worked_example_switch_branch.png new file mode 100644 index 000000000000..8df761e88beb Binary files /dev/null and b/docs/development/worked_example_switch_branch.png differ diff --git a/docs/development/workflow/branch_dropdown.png b/docs/development/workflow/branch_dropdown.png deleted file mode 100644 index 1bb7a577732c..000000000000 Binary files a/docs/development/workflow/branch_dropdown.png and /dev/null differ diff --git a/docs/development/workflow/development_workflow.rst b/docs/development/workflow/development_workflow.rst deleted file mode 100644 index 1e689b787f9e..000000000000 --- a/docs/development/workflow/development_workflow.rst +++ /dev/null @@ -1,573 +0,0 @@ -.. _development-workflow: - -======================= -Workflow for Developers -======================= - -In the present document, we refer to the Astropy ``master`` branch, as the -*trunk*. - -.. _forking: - -Creating a fork -=============== - -You need to do this only once for each package you want to contribute to. The -instructions here are very similar to the instructions at -http://help.github.com/fork-a-repo/ |emdash| please see that page for more -details. We're repeating some of it here just to give the specifics for the -Astropy_ project, and to suggest some default names. - -Set up and configure a GitHub account -------------------------------------- - -If you don't have a GitHub account, go to the GitHub page, and make one. - -You then need to configure your account to allow write access |emdash| see -the *Generating SSH keys* help on `GitHub Help`_. - -Create your own fork of a repository ------------------------------------- - -The following example shows how to fork the core ``astropy`` repository, but the same applies to other packages: - -#. Log into your GitHub account. - -#. Go to the `Astropy GitHub`_ home. - -#. Click on the *fork* button: - - .. image:: forking_button.png - - Now, after a short pause and some 'Hardcore forking action', you - should find yourself at the home page for your own forked copy of Astropy_. - -Setting up the fork to work on ------------------------------- - -.. _linking-to-upstream: - -Overview -^^^^^^^^ - -This is done using:: - - git clone git@github.com:your-user-name/astropy.git - cd astropy - git remote add upstream git://github.com/astropy/astropy.git - -In detail -^^^^^^^^^ - -#. Clone your fork to the local computer:: - - git clone git@github.com:your-user-name/astropy.git - -#. Change directory to your new repo:: - - cd astropy - - Then type:: - - git branch -a - - to show you all branches. You'll get something like:: - - * master - remotes/origin/master - - This tells you that you are currently on the ``master`` branch, and - that you also have a ``remote`` connection to ``origin/master``. - What remote repository is ``remote/origin``? Try ``git remote -v`` to - see the URLs for the remote. They will point to your github fork. - - Now you want to connect to the Astropy repository, so you can - merge in changes from the trunk:: - - cd astropy - git remote add upstream git://github.com/astropy/astropy.git - - ``upstream`` here is just the arbitrary name we're using to refer to the - main Astropy_ repository. - - Note that we've used ``git://`` for the URL rather than ``git@``. The - ``git://`` URL is read only. This means we that we can't accidentally (or - deliberately) write to the upstream repo, and we are only going to use it - to merge into our own code. - - Just for your own satisfaction, show yourself that you now have a new - 'remote', with ``git remote -v show``, giving you something like:: - - upstream git://github.com/astropy/astropy.git (fetch) - upstream git://github.com/astropy/astropy.git (push) - origin git@github.com:your-user-name/astropy.git (fetch) - origin git@github.com:your-user-name/astropy.git (push) - - Your fork is now set up correctly, and you are ready to hack away. - -Workflow summary -================ - -This section gives a summary of the workflow once you have successfully forked -the repository, and details are given for each of these steps in the following -sections. - -* Don't use your ``master`` branch for anything. Consider deleting it. - -* When you are starting a new set of changes, fetch any changes from the - trunk, and start a new *feature branch* from that. - -* Make a new branch for each separable set of changes |emdash| "one task, one - branch" (`ipython git workflow`_). - -* Name your branch for the purpose of the changes - e.g. - ``bugfix-for-issue-14`` or ``refactor-database-code``. - -* If you can possibly avoid it, avoid merging trunk or any other branches into - your feature branch while you are working. - -* If you do find yourself merging from the trunk, consider :ref:`rebase-on-trunk` - -* Ask on the `astropy-dev mailing list`_ if you get stuck. - -* Ask for code review! - -This way of working helps to keep work well organized, with readable history. -This in turn makes it easier for project maintainers (that might be you) to -see what you've done, and why you did it. - -See `linux git workflow`_ and `ipython git workflow`_ for some explanation. - -Deleting your master branch -=========================== - -It may sound strange, but deleting your own ``master`` branch can help reduce -confusion about which branch you are on. See `deleting master on github`_ for -details. - -.. _update-mirror-trunk: - -Updating the mirror of trunk -============================ - -From time to time you should fetch the upstream (trunk) changes from github:: - - git fetch upstream - -This will pull down any commits you don't have, and set the remote branches to -point to the right commit. For example, 'trunk' is the branch referred to by -(remote/branchname) ``upstream/master`` - and if there have been commits since -you last checked, ``upstream/master`` will change after you do the fetch. - -.. _make-feature-branch: - -Making a new feature branch -=========================== - -When you are ready to make some changes to the code, you should start a new -branch. Branches that are for a collection of related edits are often called -'feature branches'. - -Making an new branch for each set of related changes will make it easier for -someone reviewing your branch to see what you are doing. - -Choose an informative name for the branch to remind yourself and the rest of -us what the changes in the branch are for. For example ``add-ability-to-fly``, -or ``buxfix-for-issue-42``. - -:: - - # Update the mirror of trunk - git fetch upstream - - # Make new feature branch starting at current trunk - git branch my-new-feature upstream/master - git checkout my-new-feature - -Generally, you will want to keep your feature branches on your public github_ -fork. To do this, you `git push`_ this new branch up to your -github repo. Generally (if you followed the instructions in these pages, and -by default), git will have a link to your GitHub repo, called ``origin``. You -push up to your own repo on GitHub with:: - - git push origin my-new-feature - -In git >= 1.7 you can ensure that the link is correctly set by using the -``--set-upstream`` option:: - - git push --set-upstream origin my-new-feature - -From now on git will know that ``my-new-feature`` is related to the -``my-new-feature`` branch in the GitHub repo. - -.. _edit-flow: - -The editing workflow -==================== - -Overview --------- - -:: - - git add my_new_file - git commit -am 'NF - some message' - git push - -In more detail --------------- - -#. Make some changes - -#. See which files have changed with ``git status`` (see `git status`_). - You'll see a listing like this one:: - - # On branch ny-new-feature - # Changed but not updated: - # (use "git add ..." to update what will be committed) - # (use "git checkout -- ..." to discard changes in working directory) - # - # modified: README - # - # Untracked files: - # (use "git add ..." to include in what will be committed) - # - # INSTALL - no changes added to commit (use "git add" and/or "git commit -a") - -#. Check what the actual changes are with ``git diff`` (`git diff`_). - -#. Add any new files to version control ``git add new_file_name`` (see - `git add`_). - -#. Add any modified files that you want to commit using - ``git add modified_file_name`` (see `git add`_). - -#. Once you are ready to commit, check with ``git status`` which files are about to be committed:: - - # Changes to be committed: - # (use "git reset HEAD ..." to unstage) - # - # modified: README - - Then use ``git commit -m 'A commit message'``. The ``m`` flag just - signals that you're going to type a message on the command line. The `git - commit`_ manual page might also be useful. - -#. To push the changes up to your forked repo on github, do a ``git - push`` (see `git push`_). - -Asking for your changes to be reviewed or merged -================================================ - -When you are ready to ask for someone to review your code and consider a merge: - -#. Go to the URL of your forked repo, say - ``http://github.com/your-user-name/astropy``. - -#. Use the 'Switch Branches' dropdown menu near the top left of the page to - select the branch with your changes: - - .. image:: branch_dropdown.png - -#. Click on the 'Pull request' button: - - .. image:: pull_button.png - - Enter a title for the set of changes, and some explanation of what you've - done. Say if there is anything you'd like particular attention for - like a - complicated change or some code you are not happy with. - - If you don't think your request is ready to be merged, just say so in your - pull request message. This is still a good way of getting some preliminary - code review. - -Some other things you might want to do -====================================== - -Delete a branch on github -------------------------- - -:: - - # change to the master branch (if you still have one, otherwise change to - # another branch) - git checkout master - - # delete branch locally - git branch -D my-unwanted-branch - - # delete branch on github - git push origin :my-unwanted-branch - -(Note the colon ``:`` before ``test-branch``. See also: -http://github.com/guides/remove-a-remote-branch - -Several people sharing a single repository ------------------------------------------- - -If you want to work on some stuff with other people, where you are all -committing into the same repository, or even the same branch, then just -share it via github. - -First fork Astropy into your account, as from :ref:`forking`. - -Then, go to your forked repository GitHub page, say -``http://github.com/your-user-name/astropy`` - -Click on the 'Admin' button, and add anyone else to the repo as a -collaborator: - - .. image:: pull_button.png - -Now all those people can do:: - - git clone git@githhub.com:your-user-name/astropy.git - -Remember that links starting with ``git@`` use the ssh protocol and are -read-write; links starting with ``git://`` are read-only. - -Your collaborators can then commit directly into that repo with the -usual:: - - git commit -am 'ENH - much better code' - git push origin master # pushes directly into your repo - -Explore your repository ------------------------ - -To see a graphical representation of the repository branches and -commits:: - - gitk --all - -To see a linear list of commits for this branch:: - - git log - -You can also look at the `network graph visualizer`_ for your github -repo. - -Finally the :ref:`fancy-log` ``lg`` alias will give you a reasonable -text-based graph of the repository. - -.. _rebase-on-trunk: - -Rebasing on trunk ------------------ - -Let's say you thought of some work you'd like to do. You -:ref:`update-mirror-trunk` and :ref:`make-feature-branch` called -``cool-feature``. At this stage trunk is at some commit, let's call it E. Now -you make some new commits on your ``cool-feature`` branch, let's call them A, -B, C. Maybe your changes take a while, or you come back to them after a while. -In the meantime, trunk has progressed from commit E to commit (say) G:: - - A---B---C cool-feature - / - D---E---F---G trunk - -At this stage you consider merging trunk into your feature branch, and you -remember that this here page sternly advises you not to do that, because the -history will get messy. Most of the time you can just ask for a review, and -not worry that trunk has got a little ahead. But sometimes, the changes in -trunk might affect your changes, and you need to harmonize them. In this -situation you may prefer to do a rebase. - -Rebase takes your changes (A, B, C) and replays them as if they had been made -to the current state of ``trunk``. In other words, in this case, it takes the -changes represented by A, B, C and replays them on top of G. After the rebase, -your history will look like this:: - - A'--B'--C' cool-feature - / - D---E---F---G trunk - -See `rebase without tears`_ for more detail. - -To do a rebase on trunk:: - - # Update the mirror of trunk - git fetch upstream - - # Go to the feature branch - git checkout cool-feature - - # Make a backup in case you mess up - git branch tmp cool-feature - - # Rebase cool-feature onto trunk - git rebase --onto upstream/master upstream/master cool-feature - -In this situation, where you are already on branch ``cool-feature``, the last -command can be written more succinctly as:: - - git rebase upstream/master - -When all looks good you can delete your backup branch:: - - git branch -D tmp - -If it doesn't look good you may need to have a look at -:ref:`recovering-from-mess-up`. - -If you have made changes to files that have also changed in trunk, this may -generate merge conflicts that you need to resolve - see the `git rebase`_ man -page for some instructions at the end of the "Description" section. There is -some related help on merging in the git user manual - see `resolving a -merge`_. - -.. _recovering-from-mess-up: - -Recovering from mess-ups ------------------------- - -Sometimes, you mess up merges or rebases. Luckily, in git it is relatively -straightforward to recover from such mistakes. - -If you mess up during a rebase:: - - git rebase --abort - -If you notice you messed up after the rebase:: - - # Reset branch back to the saved point - git reset --hard tmp - -If you forgot to make a backup branch:: - - # Look at the reflog of the branch - git reflog show cool-feature - - 8630830 cool-feature@{0}: commit: BUG: io: close file handles immediately - 278dd2a cool-feature@{1}: rebase finished: refs/heads/my-feature-branch onto 11ee694744f2552d - 26aa21a cool-feature@{2}: commit: BUG: lib: make seek_gzip_factory not leak gzip obj - ... - - # Reset the branch to where it was before the botched rebase - git reset --hard cool-feature@{2} - -.. _rewriting-commit-history: - -Rewriting commit history ------------------------- - -.. note:: - - Do this only for your own feature branches. - -There's an embarrassing typo in a commit you made? Or perhaps the you -made several false starts you would like the posterity not to see. - -This can be done via *interactive rebasing*. - -Suppose that the commit history looks like this:: - - git log --oneline - eadc391 Fix some remaining bugs - a815645 Modify it so that it works - 2dec1ac Fix a few bugs + disable - 13d7934 First implementation - 6ad92e5 * masked is now an instance of a new object, MaskedConstant - 29001ed Add pre-nep for a copule of structured_array_extensions. - ... - -and ``6ad92e5`` is the last commit in the ``cool-feature`` branch. Suppose we -want to make the following changes: - -* Rewrite the commit message for ``13d7934`` to something more sensible. -* Combine the commits ``2dec1ac``, ``a815645``, ``eadc391`` into a single one. - -We do as follows:: - - # make a backup of the current state - git branch tmp HEAD - # interactive rebase - git rebase -i 6ad92e5 - -This will open an editor with the following text in it:: - - pick 13d7934 First implementation - pick 2dec1ac Fix a few bugs + disable - pick a815645 Modify it so that it works - pick eadc391 Fix some remaining bugs - - # Rebase 6ad92e5..eadc391 onto 6ad92e5 - # - # Commands: - # p, pick = use commit - # r, reword = use commit, but edit the commit message - # e, edit = use commit, but stop for amending - # s, squash = use commit, but meld into previous commit - # f, fixup = like "squash", but discard this commit's log message - # - # If you remove a line here THAT COMMIT WILL BE LOST. - # However, if you remove everything, the rebase will be aborted. - # - -To achieve what we want, we will make the following changes to it:: - - r 13d7934 First implementation - pick 2dec1ac Fix a few bugs + disable - f a815645 Modify it so that it works - f eadc391 Fix some remaining bugs - -This means that (i) we want to edit the commit message for ``13d7934``, and -(ii) collapse the last three commits into one. Now we save and quit the -editor. - -Git will then immediately bring up an editor for editing the commit message. -After revising it, we get the output:: - - [detached HEAD 721fc64] FOO: First implementation - 2 files changed, 199 insertions(+), 66 deletions(-) - [detached HEAD 0f22701] Fix a few bugs + disable - 1 files changed, 79 insertions(+), 61 deletions(-) - Successfully rebased and updated refs/heads/my-feature-branch. - -and the history looks now like this:: - - 0f22701 Fix a few bugs + disable - 721fc64 ENH: Sophisticated feature - 6ad92e5 * masked is now an instance of a new object, MaskedConstant - -If it went wrong, recovery is again possible as explained :ref:`above -`. - -Converting a GitHub issue to a pull request -------------------------------------------- - -Sometimes you have a branch in your own GitHub repository designed to -fix one particular issue. If that issue is listed on GitHub, a natural -way to address it is to convert the issue to a pull request by -attaching code with the fix to the issue. This can currently only be -done using the GitHub API (there's no button or anything on the web -site that does it, at least as of 2/6/2012). There are two options to do this: - -* You can use the script at https://gist.github.com/1750715 which will - do this for you automatically - just download the script and run it as - a python command-line script, using the ``python issue2pr.py --help`` - option to determine the precise usage. - -* You can use the ``hub`` command-line utility provided `here - `_ by GitHub. Once installed, you can - attach a branch to a pull request by doing:: - - hub pull-request -i -b astropy:master -h : - - where ```` is the ID of the issue, ```` is the username, and - ```` is the name of the branch you want to attach to the - issue. For example:: - - hub pull-request -i 42 -b astropy:master -h galahad:feature - - will attach the ``feature`` branch from ``galahad``'s Astropy - repository to issue 42. - - The ``hub`` command can do a lot more to interact with GitHub, so be - sure to read their documentation. For example, you can fetch all - branches of a repository for a given user by doing:: - - hub fetch - -.. include:: links.inc diff --git a/docs/development/workflow/forking_button.png b/docs/development/workflow/forking_button.png deleted file mode 100644 index d0e04134d4d0..000000000000 Binary files a/docs/development/workflow/forking_button.png and /dev/null differ diff --git a/docs/development/workflow/git_configure.rst b/docs/development/workflow/git_configure.rst deleted file mode 100644 index e258eef57d16..000000000000 --- a/docs/development/workflow/git_configure.rst +++ /dev/null @@ -1,168 +0,0 @@ -.. _configure-git: - -================ - Configuring git -================ - -.. _git-config-basic: - -Bare Minimum -============ - -The only absolutely necessary configuration step is identifying yourself and your contact info:: - - git config --global user.name "Your Name" - git config --global user.email you@yourdomain.example.com - -Once you've done this, you can actually ignore the rest of the document unless you want to customize the behavior of git. - -Overview -======== - -Your personal git configurations are saved in the ``.gitconfig`` file in -your home directory. - -Here is an example ``.gitconfig`` file:: - - [user] - name = Your Name - email = you@yourdomain.example.com - - [alias] - ci = commit -a - co = checkout - st = status - stat = status - br = branch - wdiff = diff --color-words - - [core] - editor = vim - - [merge] - log = true - -You can edit this file directly or you can use the ``git config --global`` -command:: - - git config --global user.name "Your Name" - git config --global user.email you@yourdomain.example.com - git config --global alias.ci "commit -a" - git config --global alias.co checkout - git config --global alias.st "status -a" - git config --global alias.stat "status -a" - git config --global alias.br branch - git config --global alias.wdiff "diff --color-words" - git config --global core.editor vim - git config --global merge.summary true - -To set up on another computer, you can copy your ``~/.gitconfig`` file, -or run the commands above. - -In detail -========= - -user.name and user.email ------------------------- - -It is good practice to tell git_ who you are, for labeling any changes -you make to the code. The simplest way to do this is from the command -line:: - - git config --global user.name "Your Name" - git config --global user.email you@yourdomain.example.com - -This will write the settings into your git configuration file, which -should now contain a user section with your name and email:: - - [user] - name = Your Name - email = you@yourdomain.example.com - -Of course you'll need to replace ``Your Name`` and ``you@yourdomain.example.com`` -with your actual name and email address. - -Aliases -------- - -You might well benefit from some aliases to common commands. - -For example, you might well want to be able to shorten ``git checkout`` -to ``git co``. Or you may want to alias ``git diff --color-words`` -(which gives a nicely formatted output of the diff) to ``git wdiff`` - -The following ``git config --global`` commands:: - - git config --global alias.ci "commit -a" - git config --global alias.co checkout - git config --global alias.st "status -a" - git config --global alias.stat "status -a" - git config --global alias.br branch - git config --global alias.wdiff "diff --color-words" - -will create an ``alias`` section in your ``.gitconfig`` file with contents -like this:: - - [alias] - ci = commit -a - co = checkout - st = status -a - stat = status -a - br = branch - wdiff = diff --color-words - -Editor ------- - -You may also want to make sure that your editor of choice is used :: - - git config --global core.editor vim - -Merging -------- - -To enforce summaries when doing merges (``~/.gitconfig`` file again):: - - [merge] - log = true - -Or from the command line:: - - git config --global merge.log true - -.. _fancy-log: - -Fancy log output ----------------- - -This is a very nice alias to get a fancy log output; it should go in the -``alias`` section of your ``.gitconfig`` file:: - - lg = log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)[%an]%Creset' --abbrev-commit --date=relative - -You use the alias with:: - - git lg - -and it gives graph / text output something like this (but with color!):: - - * 6d8e1ee - (HEAD, origin/my-fancy-feature, my-fancy-feature) NF - a fancy file (45 minutes ago) [Matthew Brett] - * d304a73 - (origin/placeholder, placeholder) Merge pull request #48 from hhuuggoo/master (2 weeks ago) [Jonathan Terhorst] - |\ - | * 4aff2a8 - fixed bug 35, and added a test in test_bugfixes (2 weeks ago) [Hugo] - |/ - * a7ff2e5 - Added notes on discussion/proposal made during Data Array Summit. (2 weeks ago) [Corran Webster] - * 68f6752 - Initial implimentation of AxisIndexer - uses 'index_by' which needs to be changed to a call on an Axes object - this is all very sketchy right now. (2 weeks ago) [Corr - * 376adbd - Merge pull request #46 from terhorst/master (2 weeks ago) [Jonathan Terhorst] - |\ - | * b605216 - updated joshu example to current api (3 weeks ago) [Jonathan Terhorst] - | * 2e991e8 - add testing for outer ufunc (3 weeks ago) [Jonathan Terhorst] - | * 7beda5a - prevent axis from throwing an exception if testing equality with non-axis object (3 weeks ago) [Jonathan Terhorst] - | * 65af65e - convert unit testing code to assertions (3 weeks ago) [Jonathan Terhorst] - | * 956fbab - Merge remote-tracking branch 'upstream/master' (3 weeks ago) [Jonathan Terhorst] - | |\ - | |/ - -Thanks to Yury V. Zaytsev for posting it. - -.. include:: links.inc diff --git a/docs/development/workflow/git_install.rst b/docs/development/workflow/git_install.rst deleted file mode 100644 index 284d7a9d902c..000000000000 --- a/docs/development/workflow/git_install.rst +++ /dev/null @@ -1,41 +0,0 @@ -.. _install-git: - -=============== - Installing git -=============== - -The instructions here are adapted from http://book.git-scm.com/2_installing_git.html - -Debian/Ubuntu -------------- - -:: - - sudo apt-get install git-core - -Fedora ------- - -:: - - sudo yum install git-core - -MacOS X -------- - -There are several ways to install git on Mac. The easiest is to simply download the OS X installer (git-osx-installer_). If you have MacPorts installed, you can also do:: - - sudo port install git-core - -If you have Fink installed, you can do:: - - sudo apt-get install git - -In addition, you may want to use a GUI to manage your git repositories. A good example of a free Mac GUI is `GitX `_. Other (non-free) examples include `Tower `_ and `SourceTree `_. GitHub have also recently released `GitHub for Mac `_. - -Windows -------- - -Download and install msysGit_ - -.. include:: links.inc diff --git a/docs/development/workflow/git_links.inc b/docs/development/workflow/git_links.inc deleted file mode 100644 index 2421b3fe6da7..000000000000 --- a/docs/development/workflow/git_links.inc +++ /dev/null @@ -1,61 +0,0 @@ -.. This (-*- rst -*-) format file contains commonly used link targets - and name substitutions. It may be included in many files, - therefore it should only contain link targets and name - substitutions. Try grepping for "^\.\. _" to find plausible - candidates for this list. - -.. NOTE: reST targets are - __not_case_sensitive__, so only one target definition is needed for - nipy, NIPY, Nipy, etc... - -.. git stuff -.. _git: http://git-scm.com/ -.. _github: http://github.com -.. _GitHub Help: http://help.github.com -.. _msysgit: http://code.google.com/p/msysgit/downloads/list -.. _git-osx-installer: http://code.google.com/p/git-osx-installer/downloads/list -.. _subversion: http://subversion.tigris.org/ -.. _git cheat sheet: http://github.com/guides/git-cheat-sheet -.. _pro git book: http://progit.org/ -.. _git svn crash course: http://git-scm.com/course/svn.html -.. _learn.github: http://learn.github.com/ -.. _network graph visualizer: http://github.com/blog/39-say-hello-to-the-network-graph-visualizer -.. _git user manual: http://schacon.github.com/git/user-manual.html -.. _git tutorial: http://schacon.github.com/git/gittutorial.html -.. _git community book: http://book.git-scm.com/ -.. _git ready: http://www.gitready.com/ -.. _git casts: http://www.gitcasts.com/ -.. _Fernando's git page: http://www.fperez.org/py4science/git.html -.. _git magic: http://www-cs-students.stanford.edu/~blynn/gitmagic/index.html -.. _git concepts: http://www.eecs.harvard.edu/~cduan/technical/git/ -.. _git clone: http://schacon.github.com/git/git-clone.html -.. _git checkout: http://schacon.github.com/git/git-checkout.html -.. _git commit: http://schacon.github.com/git/git-commit.html -.. _git push: http://schacon.github.com/git/git-push.html -.. _git pull: http://schacon.github.com/git/git-pull.html -.. _git add: http://schacon.github.com/git/git-add.html -.. _git status: http://schacon.github.com/git/git-status.html -.. _git diff: http://schacon.github.com/git/git-diff.html -.. _git log: http://schacon.github.com/git/git-log.html -.. _git branch: http://schacon.github.com/git/git-branch.html -.. _git remote: http://schacon.github.com/git/git-remote.html -.. _git rebase: http://schacon.github.com/git/git-rebase.html -.. _git config: http://schacon.github.com/git/git-config.html -.. _why the -a flag?: http://www.gitready.com/beginner/2009/01/18/the-staging-area.html -.. _git staging area: http://www.gitready.com/beginner/2009/01/18/the-staging-area.html -.. _tangled working copy problem: http://tomayko.com/writings/the-thing-about-git -.. _git management: http://kerneltrap.org/Linux/Git_Management -.. _linux git workflow: http://www.mail-archive.com/dri-devel@lists.sourceforge.net/msg39091.html -.. _git parable: http://tom.preston-werner.com/2009/05/19/the-git-parable.html -.. _git foundation: http://matthew-brett.github.com/pydagogue/foundation.html -.. _deleting master on github: http://matthew-brett.github.com/pydagogue/gh_delete_master.html -.. _rebase without tears: http://matthew-brett.github.com/pydagogue/rebase_without_tears.html -.. _resolving a merge: http://schacon.github.com/git/user-manual.html#resolving-a-merge -.. _ipython git workflow: http://mail.scipy.org/pipermail/ipython-dev/2010-October/006746.html - -.. other stuff -.. _python: http://www.python.org - -.. |emdash| unicode:: U+02014 - -.. vim: ft=rst diff --git a/docs/development/workflow/git_resources.rst b/docs/development/workflow/git_resources.rst deleted file mode 100644 index 61d40e7eeca3..000000000000 --- a/docs/development/workflow/git_resources.rst +++ /dev/null @@ -1,59 +0,0 @@ -.. _git-resources: - -============= -Git resources -============= - -Tutorials and summaries -======================= - -* `GitHub Help`_ has an excellent series of how-to guides. -* `learn.github`_ has an excellent series of tutorials -* The `pro git book`_ is a good in-depth book on git. -* A `git cheat sheet`_ is a page giving summaries of common commands. -* The `git user manual`_ -* The `git tutorial`_ -* The `git community book`_ -* `git ready`_ |emdash| a nice series of tutorials -* `git casts`_ |emdash| video snippets giving git how-tos. -* `git magic`_ |emdash| extended introduction with intermediate detail -* The `git parable`_ is an easy read explaining the concepts behind git. -* `git foundation`_ expands on the `git parable`_. -* Fernando Perez' git page |emdash| `Fernando's git page`_ |emdash| many - links and tips -* A good but technical page on `git concepts`_ -* `git svn crash course`_: git for those of us used to subversion_ - -Advanced git workflow -===================== - -There are many ways of working with git; here are some posts on the -rules of thumb that other projects have come up with: - -* Linus Torvalds on `git management`_ -* Linus Torvalds on `linux git workflow`_ . Summary; use the git tools - to make the history of your edits as clean as possible; merge from - upstream edits as little as possible in branches where you are doing - active development. - -Manual pages online -=================== - -You can get these on your own machine with (e.g) ``git help push`` or -(same thing) ``git push --help``, but, for convenience, here are the -online manual pages for some common commands: - -* `git add`_ -* `git branch`_ -* `git checkout`_ -* `git clone`_ -* `git commit`_ -* `git config`_ -* `git diff`_ -* `git log`_ -* `git pull`_ -* `git push`_ -* `git remote`_ -* `git status`_ - -.. include:: links.inc diff --git a/docs/development/workflow/index.rst b/docs/development/workflow/index.rst deleted file mode 100644 index 6611fad841ad..000000000000 --- a/docs/development/workflow/index.rst +++ /dev/null @@ -1,52 +0,0 @@ -.. _using-git: - -Contributing To/Developing Astropy or Affiliated Packages -========================================================= - -Summary -------- - -Any contributions to the core Astropy package, whether bug fixes, -improvements to the documentation, or new functionality, can be done via -*pull requests* on GitHub. The workflow for this is described below. - -However, substantial contributions, such as whole new sub-packages, can -first be developed as *affiliated packages* then submitted to the Astropy -core via pull requests once ready (as described in :ref:`vision`). Teams working on affiliated packages are free to choose whatever version control system they wish, but ultimately the affiliated package should be merged into a fork of the Astropy repository in order to be submitted as a pull request (this merging can be done either by the team or by one of the core maintainers). - -Getting started with git ------------------------- - -The only absolutely necessary configuration step is identifying yourself and -your contact info:: - - git config --global user.name "Your Name" - git config --global user.email you@yourdomain.example.com - -The following sections cover the installation of the git software, the basic -configuration, and links to resources to learn more about using git. -However, you can also directly go to the `GitHub help pages -`_ which offer a great introduction to git and -GitHub. - -.. toctree:: - :maxdepth: 1 - - git_install - git_configure - git_resources - -Workflow --------- - -The following two sections describe the workflow for the Astropy core -package, but teams working on affiliated packages that have chosen to use -git are encouraged to also follow these guidelines internally. - -.. toctree:: - :maxdepth: 1 - - development_workflow - maintainer_workflow - -If for any reason developers do not wish to or cannot contribute via pull requests, they can submit a patch as described in :doc:`patches`. \ No newline at end of file diff --git a/docs/development/workflow/known_projects.inc b/docs/development/workflow/known_projects.inc deleted file mode 100644 index 297235287796..000000000000 --- a/docs/development/workflow/known_projects.inc +++ /dev/null @@ -1,41 +0,0 @@ -.. Known projects - -.. PROJECTNAME placeholders -.. _PROJECTNAME: http://neuroimaging.scipy.org -.. _`PROJECTNAME github`: http://github.com/nipy -.. _`PROJECTNAME mailing list`: http://projects.scipy.org/mailman/listinfo/nipy-devel - -.. numpy -.. _numpy: hhttp://numpy.scipy.org -.. _`numpy github`: http://github.com/numpy/numpy -.. _`numpy mailing list`: http://mail.scipy.org/mailman/listinfo/numpy-discussion - -.. scipy -.. _scipy: http://www.scipy.org -.. _`scipy github`: http://github.com/scipy/scipy -.. _`scipy mailing list`: http://mail.scipy.org/mailman/listinfo/scipy-dev - -.. nipy -.. _nipy: http://nipy.org/nipy -.. _`nipy github`: http://github.com/nipy/nipy -.. _`nipy mailing list`: http://mail.scipy.org/mailman/listinfo/nipy-devel - -.. ipython -.. _ipython: http://ipython.scipy.org -.. _`ipython github`: http://github.com/ipython/ipython -.. _`ipython mailing list`: http://mail.scipy.org/mailman/listinfo/IPython-dev - -.. dipy -.. _dipy: http://nipy.org/dipy -.. _`dipy github`: http://github.com/Garyfallidis/dipy -.. _`dipy mailing list`: http://mail.scipy.org/mailman/listinfo/nipy-devel - -.. nibabel -.. _nibabel: http://nipy.org/nibabel -.. _`nibabel github`: http://github.com/nipy/nibabel -.. _`nibabel mailing list`: http://mail.scipy.org/mailman/listinfo/nipy-devel - -.. marsbar -.. _marsbar: http://marsbar.sourceforge.net -.. _`marsbar github`: http://github.com/matthew-brett/marsbar -.. _`MarsBaR mailing list`: https://lists.sourceforge.net/lists/listinfo/marsbar-users diff --git a/docs/development/workflow/links.inc b/docs/development/workflow/links.inc deleted file mode 100644 index 20f4dcfffd4a..000000000000 --- a/docs/development/workflow/links.inc +++ /dev/null @@ -1,4 +0,0 @@ -.. compiling links file -.. include:: known_projects.inc -.. include:: this_project.inc -.. include:: git_links.inc diff --git a/docs/development/workflow/maintainer_workflow.rst b/docs/development/workflow/maintainer_workflow.rst deleted file mode 100644 index 41447636e5ad..000000000000 --- a/docs/development/workflow/maintainer_workflow.rst +++ /dev/null @@ -1,105 +0,0 @@ -.. _maintainer-workflow: - -######################## -Workflow for Maintainers -######################## - -This page is for maintainers |emdash| those of us who merge our own or other -peoples' changes into the upstream repository. - -Being as how you're a maintainer, you are completely on top of the basic stuff -in :ref:`development-workflow`. - -******************************************************* -Integrating changes via the web interface (recommended) -******************************************************* - -Whenever possible, merge pull requests automatically via the pull request manager on GitHub. Merging should only be done manually if there is a really good reason to do this! - -Make sure that pull requests do not contain a messy history with merges, etc. If this is the case, then follow the manual instructions, and make sure the fork is rebased to tidy the history before committing. - -**************************** -Integrating changes manually -**************************** - -First, check out the ``astropy`` repository. The instructions in :ref:`linking-to-upstream` add a remote that has read-only -access to the upstream repo. Being a maintainer, you've got read-write access. - -It's good to have your upstream remote have a scary name, to remind you that -it's a read-write remote:: - - git remote add upstream-rw git@github.com:astropy/astropy.git - git fetch upstream-rw - -Let's say you have some changes that need to go into trunk -(``upstream-rw/master``). - -The changes are in some branch that you are currently on. For example, you are -looking at someone's changes like this:: - - git remote add someone git://github.com/someone/astropy.git - git fetch someone - git branch cool-feature --track someone/cool-feature - git checkout cool-feature - -So now you are on the branch with the changes to be incorporated upstream. The -rest of this section assumes you are on this branch. - -A few commits -============= - -If there are only a few commits, consider rebasing to upstream:: - - # Fetch upstream changes - git fetch upstream-rw - - # Rebase - git rebase upstream-rw/master - -Remember that, if you do a rebase, and push that, you'll have to close any -github pull requests manually, because github will not be able to detect the -changes have already been merged. - -A long series of commits -======================== - -If there are a longer series of related commits, consider a merge instead:: - - git fetch upstream-rw - git merge --no-ff upstream-rw/master - -The merge will be detected by github, and should close any related pull -requests automatically. - -Note the ``--no-ff`` above. This forces git to make a merge commit, rather -than doing a fast-forward, so that these set of commits branch off trunk then -rejoin the main history with a merge, rather than appearing to have been made -directly on top of trunk. - -Check the history -================= - -Now, in either case, you should check that the history is sensible and you -have the right commits:: - - git log --oneline --graph - git log -p upstream-rw/master.. - -The first line above just shows the history in a compact way, with a text -representation of the history graph. The second line shows the log of commits -excluding those that can be reached from trunk (``upstream-rw/master``), and -including those that can be reached from current HEAD (implied with the ``..`` -at the end). So, it shows the commits unique to this branch compared to trunk. -The ``-p`` option shows the diff for these commits in patch form. - -Push to trunk -============= - -:: - - git push upstream-rw my-new-feature:master - -This pushes the ``my-new-feature`` branch in this repository to the ``master`` -branch in the ``upstream-rw`` repository. - -.. include:: links.inc diff --git a/docs/development/workflow/patches.rst b/docs/development/workflow/patches.rst deleted file mode 100644 index 0153466fa7bb..000000000000 --- a/docs/development/workflow/patches.rst +++ /dev/null @@ -1,112 +0,0 @@ -:orphan: - -.. _basic-workflow: - -================ -Creating patches -================ - -Overview --------- - -If you haven't already configured git:: - - git config --global user.name "Your Name" - git config --global user.email you@yourdomain.example.com - -Then, the workflow is the following:: - - # Get the repository if you don't have it - git clone git://github.com/astropy/astropy.git - - # Make a branch for your patching - cd astropy - git branch the-fix-im-thinking-of - git checkout the-fix-im-thinking-of - - # hack, hack, hack - - # Tell git about any new files you've made - git add somewhere/tests/test_my_bug.py - - # Commit work in progress as you go - git commit -am 'BF - added tests for Funny bug' - - # hack hack, hack - - # Commit work - git commit -am 'BF - added fix for Funny bug' - - # Make the patch files - git format-patch -M -C master - -Then, send the generated patch files to the `astropy-dev mailing list`_ |emdash| -where we will thank you warmly. - -In detail ---------- - -#. Tell git who you are so it can label the commits you've - made:: - - git config --global user.name "Your Name" - git config --global user.email you@yourdomain.example.com - - This is only necessary if you haven't already done this, and you haven't - followed :ref:`configure-git`. - -#. If you don't already have one, clone a copy of the - Astropy_ repository:: - - git clone git://github.com/astropy/astropy.git - cd astropy - -#. Make a 'feature branch'. This will be where you work on your bug fix. It's - nice and safe and leaves you with access to an unmodified copy of the code - in the main branch:: - - git branch the-fix-im-thinking-of - git checkout the-fix-im-thinking-of - -#. Do some edits, and commit them as you go:: - - # hack, hack, hack - - # Tell git about any new files you've made - git add somewhere/tests/test_my_bug.py - - # Commit work in progress as you go - git commit -am 'BF - added tests for Funny bug' - - # hack hack, hack - - # Commit work - git commit -am 'BF - added fix for Funny bug' - - Note the ``-am`` options to ``commit``. The ``m`` flag just - signals that you're going to type a message on the command - line. The ``a`` flag |emdash| you can just take on faith |emdash| - or see `why the -a flag?`_. - -#. When you have finished, check you have committed all your changes:: - - git status - -#. Finally, make your commits into patches. You want all the commits since you - branched from the ``master`` branch:: - - git format-patch -M -C master - - You will now have several files named for the commits:: - - 0001-BF-added-tests-for-Funny-bug.patch - 0002-BF-added-fix-for-Funny-bug.patch - - Send these files to the `astropy-dev mailing list`_. - -When you are done, to switch back to the main copy of the -code, just return to the ``master`` branch:: - - git checkout master - -.. include:: links.inc diff --git a/docs/development/workflow/pull_button.png b/docs/development/workflow/pull_button.png deleted file mode 100644 index e5031681b97b..000000000000 Binary files a/docs/development/workflow/pull_button.png and /dev/null differ diff --git a/docs/development/workflow/this_project.inc b/docs/development/workflow/this_project.inc deleted file mode 100644 index 6bc95b3eb080..000000000000 --- a/docs/development/workflow/this_project.inc +++ /dev/null @@ -1,6 +0,0 @@ -.. Astropy -.. _Astropy: http://astropy.org -.. _`Astropy GitHub`: http://github.com/astropy/astropy - -.. _`Astropy mailing list`: http://mail.scipy.org/mailman/listinfo/astropy -.. _`astropy-dev mailing list`: http://groups.google.com/group/astropy-dev diff --git a/docs/environment_variables.rst b/docs/environment_variables.rst new file mode 100644 index 000000000000..0f8f5e5ad1f0 --- /dev/null +++ b/docs/environment_variables.rst @@ -0,0 +1,39 @@ +.. currentmodule:: astropy + +.. _environment_variables: + +********************* +Environment variables +********************* + +Cache and configuration locations +================================= + +Since version 8.0.0, astropy follows the XDG specification by default, on every platform +(including non-Linux ones). As a result, the default cache location is +``$XDG_CACHE_HOME/astropy``, where ``XDG_CACHE_HOME`` itself defaults to +``$HOME/.cache``. The same goes for configuration, replacing ``cache`` with ``config``, +and preserving case. + +In addition to these, and since v8.0.0, astropy supports comparable, tool-specific +environment variables for finer control: + +.. glossary:: + + ``ASTROPY_CACHE_DIR`` + takes precedence over ``XDG_CACHE_HOME`` and defines an + entire path (as opposed to ``XDG_CACHE_HOME`` which only defines the *parent* + directory of the one used by astropy). Its value must represent an absolute path, + and must not point to file. Invalid values are ignored with a warning when the + variable is evaluated. + See :ref:`utils-data` for how to programmatically set or get the location of the + corresponding directory at runtime. + + ``ASTROPY_CONFIG_DIR`` + takes precedence over ``XDG_CONFIG_HOME`` and defines an + entire path (as opposed to ``XDG_CONFIG_HOME`` which only defines the *parent* + directory of the one used by astropy). Its value must represent an absolute path, + and must not point to file. Invalid values are ignored with a warning when the + variable is evaluated. + See :ref:`astropy_config` for how to programmatically set or get the location of + the corresponding directory at runtime. diff --git a/docs/glossary.rst b/docs/glossary.rst new file mode 100644 index 000000000000..3a4fb7080211 --- /dev/null +++ b/docs/glossary.rst @@ -0,0 +1,106 @@ +.. currentmodule:: astropy + +**************** +Astropy Glossary +**************** + +.. glossary:: + + (``n``,) + A parenthesized number followed by a comma denotes a tuple with one + element. The trailing comma distinguishes a one-element tuple from a + parenthesized ``n``. + This is from NumPy; see https://numpy.org/doc/stable/glossary.html#term-n. + + -like + ``-like`` is an instance of the ``Class`` or a valid initializer argument + for ``Class`` as ``Class(value)``. E.g. :class:`~astropy.units.Quantity`-like + includes ``"2 * u.km"`` because ``astropy.units.Quantity("2 * u.km")`` works. + + ['physical type'] + The physical type of a quantity can be annotated in square brackets + following a `~astropy.units.Quantity` (or similar :term:`quantity-like`). + + For example, ``distance : quantity-like ['length']`` + + angle-like + :term:`quantity-like` and a valid initializer for `~astropy.coordinates.Angle`. + The ``unit`` must be an angular. A string input is interpreted as an angle as + described in the `~astropy.coordinates.Angle` documentation. + + buffer-like + Object that implements `Python's buffer protocol + `_. + + coordinate-like + :class:`~astropy.coordinates.BaseCoordinateFrame` subclass instance, or a + :class:`~astropy.coordinates.SkyCoord` (or subclass) instance, or a valid + initializer as described in :ref:`coordinates-initialization-coord`. + + file-like (readable) + :term:`python:file-like object` object that supports reading with a method ``read``. + + For a formal definition see :class:`~astropy.io.typing.ReadableFileLike`. + + file-like (writeable) + :term:`python:file-like object` object that supports writing with a method ``write``. + + For a formal definition see :class:`~astropy.io.typing.WriteableFileLike`. + + frame-like + :class:`~astropy.coordinates.BaseCoordinateFrame` subclass or subclass instance or + a valid Frame name (string). + + length-like + :term:`quantity-like` and a valid initializer for + :class:`~astropy.coordinates.Distance`. The ``unit`` must be a convertible to a + unit of length. + + number + Any scalar numeric type. e.g. `float` or `int` or ``numpy.number``. + + quantity-like + `~astropy.units.Quantity` (or subclass) instance, a number or `array-like + `_ object, or a string + which is a valid initializer for `~astropy.units.Quantity`. + + For a formal definition see :obj:`~astropy.units.typing.QuantityLike`. + + table-like + :class:`~astropy.table.Table` (or subclass) instance or valid initializer for + :class:`~astropy.table.Table` as described in :ref:`construct_table`. Common types + include ``dict[list]``, ``list[dict]``, ``list[list]``, and `~numpy.ndarray` + (structured array). + + time-like + :class:`~astropy.time.Time` (or subclass) instance or a valid initializer for + :class:`~astropy.time.Time`, e.g. `str`, array-like[str], `~datetime.datetime`, or + `~numpy.datetime64`. + + trait type + In short, a trait type is a class with the following properties: + + - It is a class that can be used as a mixin to add functionality to another class. + - It should never be instantiated directly. + - It should not be used as a base class for other classes, but only as a mixin. + - It can define methods, properties, and attributes -- any of which can be abstract. + - It can be generic, i.e. it can have type parameters. + - It can subclass other traits, but should have a linear MRO. + + These are the same set of properties as orthogonal mixin classes, with the added + emphasis that they can serve as compiled types, if so enabled by a compilation system such as `mypyc `_. + + unit-like + :class:`~astropy.units.UnitBase` subclass instance or a valid initializer for + :class:`~astropy.units.Unit`, e.g., `str` or scalar `~astropy.units.Quantity`. + + +Optional Packages' Glossary +*************************** + +.. currentmodule:: matplotlib.pyplot + +.. glossary:: + + color + Any valid Matplotlib color. diff --git a/docs/impact_health.rst b/docs/impact_health.rst new file mode 100644 index 000000000000..02f476744c53 --- /dev/null +++ b/docs/impact_health.rst @@ -0,0 +1,71 @@ +################# +Impact and Health +################# + +The figures on this page give a sense of the level of impact the ``astropy`` +codebase has on the astronomy community and research in the field, as well as +the amount of development effort contributed over time (the codebase's "health"). +Assessments of The Astropy Project's engagement with the diverse astronomy +community are an equally important but separate consideration and are detailed +in: + +- `Astropy community engagement study `_ + +- `Astropy diversity, equity and inclusion study `_ + +Concerning the codebase, a major positive trend is that citations to ``astropy``, +as a proxy for its usage in research, have been consistently growing since The +Astropy Project was created. However, the amount of developer resources has +remained small, with a limited number of volunteer maintainers met with an +increasing amount of development responsibility. Hence, we welcome any help +that you can give! + +First considering the citations, this figure estimates ``astropy``'s usage in +astronomy research by its yearly publication impact, using citations drawn from +the NASA ADS database. The continual growth in citations shows the broad and +increasing usage of ``astropy`` in astronomy and scientific research, as well as +the high impact that contributions to ``astropy`` can have. + +|Citation figure| + +To next visualize the size of the developer community contributing to the +``astropy`` core library, this figure shows the number of people authoring commits +over time. While each year ``astropy`` is cited more in papers, the amount of +developers and especially those contributing multiple times is modest and +largely static. + +|Commits figure| + +The workload to maintain ``astropy`` can be traced through the number of issues and +pull requests open and closed in the ``astropy`` core library over time. The +long-term increase in overall pull requests, along with a comparable amount of +opens and closes on average, shows that the small number of maintainers is +increasingly taxed. The discrepancy between issue opens and closes over time +reinforces this. + +|Issue PR history figure| + +In short, ``astropy`` would greatly benefit from more developers, whose +contributions would reach a significant fraction of research in astronomy. This +figure shows the number of open issues and pull requests for each subpackage in +``astropy``. In addition to indicating which functionalities are used more heavily +by the community at present, it gives a sense of where you could start if +you're interested in contributing to ``astropy``. + +|Open issue PR figure| + +.. |Citation figure| image:: https://github.com/astropy/repo_stats/blob/cache/cache/astropy_citations.png?raw=true + :width: 800 + :alt: Astropy citations + +.. |Commits figure| image:: https://github.com/astropy/repo_stats/blob/cache/cache/astropy_authors.png?raw=true + :width: 800 + :alt: Astropy commit author history + +.. |Issue PR history figure| image:: https://github.com/astropy/repo_stats/blob/cache/cache/astropy_issues_PRs.png?raw=true + :width: 800 + :alt: Astropy issue and pull request history + +.. |Open issue PR figure| image:: https://github.com/astropy/repo_stats/blob/cache/cache/astropy_open_items.png?raw=true + :width: 800 + :alt: Astropy open issues and pull requests diff --git a/docs/importing_astropy.rst b/docs/importing_astropy.rst new file mode 100644 index 000000000000..ce2e7dfbe395 --- /dev/null +++ b/docs/importing_astropy.rst @@ -0,0 +1,69 @@ +************************************** +Importing ``astropy`` and Sub-packages +************************************** + +In order to encourage consistency among users in importing and using Astropy +functionality, we have put together the following guidelines. + +Since most of the functionality in Astropy resides in sub-packages, importing +``astropy`` as:: + + >>> import astropy + +is not very useful. Instead, it's best to import the desired sub-package +with the syntax:: + + >>> from astropy import subpackage # doctest: +SKIP + +For example, to access the FITS-related functionality, you can import +`astropy.io.fits` with:: + + >>> from astropy.io import fits + >>> hdulist = fits.open('data.fits') # doctest: +SKIP + +In specific cases, we have recommended shortcuts in the documentation for +specific sub-packages. For example:: + + >>> from astropy import units as u + >>> from astropy import coordinates as coord + >>> coord.SkyCoord(ra=10.68458*u.deg, dec=41.26917*u.deg, frame='icrs') # doctest: +FLOAT_CMP + + +Finally, in some cases, most of the required functionality is contained in a +single class (or a few classes). In those cases, the class can be directly +imported:: + + >>> from astropy.cosmology import WMAP7 + >>> from astropy.table import Table + >>> from astropy.wcs import WCS + +Note that for clarity, and to avoid any issues, we recommend **never** +importing any Astropy functionality using ``*``, for example:: + + >>> from astropy.io.fits import * # NOT recommended + +Some components of Astropy started off as standalone packages (e.g. PyFITS, +PyWCS), so in cases where Astropy needs to be used as a drop-in replacement, +the following syntax is also acceptable:: + + >>> from astropy.io import fits as pyfits + +********************************* +Getting Started with Sub-packages +********************************* + +Because different sub-packages have very different functionalities, each +sub-package has its own getting started guide. These can be found by browsing +the sections listed in the :ref:`user-docs`. + +You can also look at docstrings for a particular package or object, or access +their documentation using the `~astropy.utils.misc.find_api_page` function. For +example, :: + + >>> from astropy import find_api_page + >>> from astropy.units import Quantity + >>> find_api_page(Quantity) # doctest: +SKIP + +will bring up the documentation for the `~astropy.units.Quantity` class +in your browser. diff --git a/docs/index.rst b/docs/index.rst index 99f24fe887b4..ad601e63f836 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,68 +1,131 @@ -.. Astropy documentation master file, created by - sphinx-quickstart on Tue Jul 26 02:59:34 2011. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. +.. Hide the left hand sidebar on the home page because it's empty -################################### -Welcome to Astropy's Documentation! -################################### +.. raw:: html -.. image:: logo/astropy_banner_96.png + -****************** -User Documentation -****************** +:tocdepth: 3 -.. toctree:: - :maxdepth: 1 +.. _astropy-docs-index: + +################################################# +astropy: A Community Python Library for Astronomy +################################################# + +**Version**: |release| - :ref:`whatsnew-8.1` - overview - install - nddata/index - table/index - cosmology/index - io/fits/index - io/ascii/index - io/vo/index - wcs/index - tools/index - utils/index - configs/index - logging - creditsandlicense - -.. _developer-docs: - -*********************** -Developer Documentation -*********************** - -The developer documentation contains instructions for how to contribute to -Astropy or affiliated packages, as well as coding, documentation, and -testing guidelines. +**Useful links**: +:ref:`Installation ` | +`Issues & Ideas `__ | +:ref:`astropy-org-help` | +:ref:`astropy-org-contribute` | +:ref:`astropy-org-about` + +The ``astropy`` package contains key functionality and common tools needed for +performing astronomy and astrophysics with Python. It is at the core of the +:ref:`Astropy Project `, which aims to enable +the community to develop a robust ecosystem of :ref:`astropy-org-affiliated` +covering a broad range of needs for astronomical research, data +processing, and data analysis. + +.. Important:: If you use Astropy for work presented in a publication + or talk please help the project via proper + :ref:`astropy-org-acknowledge`. This also applies to use of + software or :ref:`astropy-org-affiliated` that depend on the astropy core + package. .. toctree:: :maxdepth: 1 + :hidden: + + index_getting_started + index_user_docs + index_dev + index_project_details + +.. grid:: 2 + :gutter: 2 + + .. grid-item-card:: Getting Started + :link: index_getting_started + :link-type: doc + :text-align: center + + :material-outlined:`directions_run;8em;sd-text-secondary` + + New to Astropy? Check out the getting started guides. They contain an + introduction to astropy's main concepts and links to additional tutorials. + + .. grid-item-card:: User Guide + :link: index_user_docs + :link-type: doc + :text-align: center + + :material-outlined:`menu_book;8em;sd-text-secondary` + + The user guide provides in-depth information on the key concepts + of astropy with useful background information and explanation. + + .. grid-item-card:: Learn Astropy + :link: https://learn.astropy.org + :link-type: url + :text-align: center + + :material-outlined:`psychology;8em;sd-text-secondary` + + Learn how to use Python for astronomy through tutorials and guides that cover + Astropy and other packages in the astronomy Python ecosystem. + + .. grid-item-card:: Astropy Packages + :link: astropy-org-affiliated + :link-type: ref + :text-align: center + + :material-outlined:`inventory_2;8em;sd-text-secondary` + + The Astropy Project ecosystem includes numerous `Coordinated + `_ and `Affiliated + `_ packages. + Coordinated packages are maintained by the Project. + + .. grid-item-card:: Contributor's Guide + :link: index_dev + :link-type: doc + :text-align: center + + :material-outlined:`person_add;8em;sd-text-secondary` + + Saw a typo in the documentation? Want to improve + existing functionalities? The contributing guidelines will show + you how to improve astropy. + + .. grid-item-card:: Project Details + :link: index_project_details + :link-type: doc + :text-align: center + + :material-outlined:`more_horiz;8em;sd-text-secondary` + + What's new in the latest release, changelog, and other project details. + +.. image:: https://github.com/astropy/repo_stats/blob/cache/cache/astropy_user_stats_light.png?raw=true + :class: only-light + :target: https://docs.astropy.org/en/latest/impact_health.html + :alt: Astropy User Statistics + +.. image:: https://github.com/astropy/repo_stats/blob/cache/cache/astropy_user_stats_dark.png?raw=true + :class: only-dark + :target: https://docs.astropy.org/en/latest/impact_health.html + :alt: Astropy User Statistics - development/vision - development/workflow/index - development/codeguide - development/docguide - development/testguide - development/building_packaging - development/scripts - development/sphinxext - -****************** -Indices and Tables -****************** - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` +.. _feedback@astropy.org: mailto:feedback@astropy.org +.. _affiliated packages: https://www.astropy.org/affiliated/ diff --git a/docs/index_dev.rst b/docs/index_dev.rst new file mode 100644 index 000000000000..0f0a728c4f0f --- /dev/null +++ b/docs/index_dev.rst @@ -0,0 +1,81 @@ +.. _developer-docs: + +************ +Contributing +************ + +The contributor documentation contains instructions for how to contribute to ``astropy`` or +affiliated packages. This includes setting up a development environment, installing and +testing the development version, as well as coding, documentation, and testing +guidelines. + +For newcomers the process may initially seem overwhelming, but with a little patience +and practice you will see that it is not so complex. The key is to follow the steps +outlined here and :ref:`ask for help ` if you get stuck. +The Astropy community is welcoming and friendly and will help you! + +{% if is_development %} + +This is divided into two sections, first a quickstart guide that provides an +introduction to the development workflow, followed by a number of detailed guides that +cover provide a deeper dive and a reference for both developers and maintainers. + +.. Important:: There are useful ways to contribute to Astropy without diving + into the developer workflow which is described here. For an + an overview see the :ref:`astropy-org-contribute` page. + + +Contributing quickstart +----------------------- + +This section provides a contributing quickstart guide for Astropy. With minor changes the +process will apply to contributing updates to coordinated and many affiliated packages. + +.. Important:: All contributions must comply with our + `AI policy `_. + +.. toctree:: + :maxdepth: 2 + + development/quickstart + +Now that you have created your development environment and gotten familiar with the +process, you should now read through the detailed tutorial below to see a real-life +example of a simple bug fix. This includes more explanation of the steps and good +advice for making a code change. + +.. toctree:: + :maxdepth: 1 + + development/git_edit_workflow_examples + +Congratulations, now you are ready to be an Astropy contributor! If you are not sure where to contribute, take a look at the `Good First Issues +`_ +list. These issues are the most accessible ones if you are not familiar with the Astropy +source code. + +Details +------- + +.. toctree:: + :maxdepth: 1 + + development/development_details + development/codeguide + development/testguide + development/docguide + development/style-guide + development/git_resources + development/scripts + development/ccython + development/maintainers/index + +.. Note:: Parts of this guide were adapted from the + :ref:`pandas developer documentation `. Astropy is grateful to the ``pandas`` team for their documentation efforts. + +{%else%} + +To read the developer documentation, you will need to go to the +`latest developer version of the documentation `_. + +{%endif%} diff --git a/docs/index_getting_started.rst b/docs/index_getting_started.rst new file mode 100644 index 000000000000..1899826b6ad2 --- /dev/null +++ b/docs/index_getting_started.rst @@ -0,0 +1,14 @@ +.. _getting-started: + +*************** +Getting Started +*************** + +:ref:`whatsnew-8.1` + +.. toctree:: + :maxdepth: 1 + + install + importing_astropy + Tutorials diff --git a/docs/index_project_details.rst b/docs/index_project_details.rst new file mode 100644 index 000000000000..83bd706a0e70 --- /dev/null +++ b/docs/index_project_details.rst @@ -0,0 +1,16 @@ +.. _project-details: + +*************** +Project Details +*************** + +.. toctree:: + :maxdepth: 1 + + whatsnew/index + changelog + lts_policy + known_issues + credits + impact_health + license diff --git a/docs/index_user_docs.rst b/docs/index_user_docs.rst new file mode 100644 index 000000000000..a4e7c7856ffd --- /dev/null +++ b/docs/index_user_docs.rst @@ -0,0 +1,60 @@ +.. _user-docs: + +********** +User Guide +********** + +.. toctree:: + :caption: Data structures and transformations + :maxdepth: 1 + + constants/index + units/index + nddata/index + table/index + time/index + timeseries/index + coordinates/index + wcs/index + modeling/index + uncertainty/index + +.. toctree:: + :caption: File I/O + :maxdepth: 2 + + io/overview + io/unified + +.. toctree:: + :maxdepth: 1 + + io/fits/index + io/ascii/index + io/votable/index + io/misc + +.. toctree:: + :caption: Computations and utilities + :maxdepth: 1 + + cosmology/index + convolution/index + utils/iers + visualization/index + stats/index + utils/masked/index + samp/index + +.. toctree:: + :caption: Nuts and bolts + :maxdepth: 1 + + config/index + io/registry + io/typing + logging + warnings + utils/index + environment_variables + glossary diff --git a/docs/install.rst b/docs/install.rst index ae54b91d3543..1b83f7becad6 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -1,173 +1,309 @@ +.. _installing-astropy: + ************ Installation ************ +Overview +======== + +The first step to installing ``astropy`` is to ensure that you have a Python +environment which is **isolated** from your system Python installation. This is +important because ``astropy`` has many dependencies, and you do not want to accidentally +break your system by installing incompatible versions of these dependencies. + +For this installation guide we use the `conda `_ +package manager provided by `miniforge `_. +This is a popular choice and works well, especially for newcomers. It is easy to install +and use on all platforms and it makes it easy to install the latest Python version. If +you already have a ``miniforge``-based Python environment then you can skip to +:ref:`installing-astropy-with-pip`. + +Another option for more experienced users is a virtual environment manager such as the +Python standard library `venv `_ module. +There are numerous resources available to help you set up a virtual environment in this +manner if you choose this option. + +.. note:: + We **do not recommend** using ``astropy`` with an existing `miniconda + `_ or `Anaconda Python + `_ distribution. The ``astropy`` package provided + by Anaconda Inc. in the ``defaults`` channel can be outdated and these distributions + can require a license for use at a large organisation. Instead, use ``miniforge`` as + described below. + +Once you have a Python environment set up, you will install ``astropy`` using |pip| or +|conda|. Here we document using |pip| because it is easier to install the optional +dependencies, but feel free to use |conda| if you prefer. + +Install ``miniforge`` +===================== + +You will install Python by first installing `miniforge +`__. This provides the `conda +package manager `_ with the default remote package +repository set to the community-led `conda-forge `_ channel. + +In a new terminal (miniforge Prompt on Windows) run ``conda list`` to test that the +install has worked. + +Create Python Environment +========================= + +To create a new Python environment for ``astropy`` and other packages, start by +launching a terminal (under a UNIX-like system) or the miniforge Prompt (under Windows). +Now we will create and activate a new virtual environment to install ``astropy`` into: + +.. code-block:: bash + + $ conda create --channel conda-forge --name astropy python + $ conda activate astropy + +In this case the environment we have created is named ``astropy`` but you can use any +name you like. + +In the future when you make a new terminal, you will need to run ``conda activate +astropy`` to activate this environment. + +.. _installing-astropy-with-pip: + +Install ``astropy`` +=================== + +You can install ``astropy`` and the rest of your dependencies using either |pip| or +|conda|. Both methods are fully supported and will work well. + +.. warning:: + Once you have created your base Python environment with |conda|, you should try to + stick with one method for installing new packages in your environment. In particular, + |conda| is not aware of packages installed with |pip| and may overwrite them. + +Using pip +--------- +To install ``astropy`` and your choice of :ref:`dependencies `, run +one of the following commands:: + + python -m pip install astropy # Minimum required dependencies + python -m pip install "astropy[recommended]" # Recommended dependencies + python -m pip install "astropy[all]" # All optional dependencies + +In most cases, this will install a pre-compiled version of ``astropy`` (called a +*wheel*). However, if you are installing astropy on an uncommon platform, astropy will be +installed from a source file. In this unusual case you will need a C compiler to be +installed (see `Build from source`_ below) for the installation to succeed. + +.. warning:: Do **not** install ``astropy`` or other packages using ``sudo`` or any + elevated privilege. + +Using conda +----------- +To install ``astropy`` and the minimal set of required dependencies, run:: + + conda install --channel conda-forge astropy + +Install the recommended dependencies with:: + + conda install --channel conda-forge scipy matplotlib + +Install the optional dependencies with:: + + conda install --channel conda-forge ipython jupyter dask h5py pyarrow \ + beautifulsoup4 html5lib bleach pandas sortedcontainers pytz jplephem mpmath \ + asdf-astropy bottleneck fsspec s3fs certifi + +Testing +------- +You can test that your newly installed version of ``astropy`` is working via the +`documentation on how to test your installed version of astropy +`_. + +.. _astropy-main-req: + Requirements ============ -Astropy has the following strict requirements: +``astropy`` has the following strict requirements: -- `Python `_ 2.6, 2.7, 3.1 or 3.2 +- |Python| |minimum_python_version| or later -- `Numpy `_ 1.4 or later +- |NumPy| |minimum_numpy_version| or later -Astropy also depends on other projects for optional features. +- |PyERFA| |minimum_pyerfa_version| or later -- `scipy `_: To power a variety of features. -- `xmllint `_: To validate VOTABLE XML files. +- `PyYAML `_ |minimum_pyyaml_version| or later -.. TODO: Link to the planned dependency checker/installer tool. +- |packaging| |minimum_packaging_version| or later -Installing Astropy -================== +``astropy`` also depends on a number of other packages for optional features. +The following are particularly recommended: -Using `pip` ------------ +- |SciPy| |minimum_scipy_version| or later: To power a variety of features + in several modules. -To install Astropy with `pip`, simply run:: +- |Matplotlib| |minimum_matplotlib_version| or later: To provide plotting + functionality that `astropy.visualization` enhances. - pip install astropy +The further dependencies provide more specific features: -Binary installers ------------------ +- `h5py `_: To read/write + :class:`~astropy.table.Table` objects from/to HDF5 files. -No binary installers are available at this time. +- `BeautifulSoup `_: To read + :class:`~astropy.table.table.Table` objects from HTML files. +- `html5lib `_: To read + :class:`~astropy.table.table.Table` objects from HTML files using the + `pandas `_ reader. -Testing Astropy ---------------- +- `bleach `_: Used to sanitize text when + disabling HTML escaping in the :class:`~astropy.table.Table` HTML writer. -The easiest way to test your installed version of astropy is running -correctly is to use the :func:`astropy.test` function:: +- `ipydatagrid `_: Used in + :meth:`astropy.table.Table.show_in_notebook` to display the Astropy table + in Jupyter notebook for ``backend="ipydatagrid"``. - import astropy - astropy.test() +- `xmllint `_: To validate VOTABLE XML files. + This is a command line tool installed outside of Python. -The tests should run and print out any failures, which you can report at -the `Astropy issue tracker `_. +- `pandas `_: To convert + :class:`~astropy.table.Table` objects from/to pandas DataFrame objects. +- `sortedcontainers `_ for faster + ``SCEngine`` indexing engine with ``Table``, although this may still be + slower in some cases than the default indexing engine. -Building from source -==================== +- `pytz `_: To specify and convert between + timezones. -Prerequisites -------------- +- `jplephem `_: To retrieve JPL + ephemeris of Solar System objects. -You will need a compiler suite and the development headers for Python -and Numpy in order to build Astropy. Using the package manager for -your platform will usually be the easiest route. +- `setuptools `_: Used for discovery of + entry points which are used to insert fitters into `astropy.modeling.fitting`. -The `instructions for building Numpy from source -`_ are also a good -resource for setting up your environment to build Python packages. +- `mpmath `_: Used for the 'kraft-burrows-nousek' + interval in `~astropy.stats.poisson_conf_interval`. -Obtaining the source packages ------------------------------ +- `asdf-astropy `_ |minimum_asdf_astropy_version| or later: Enables the + serialization of various Astropy classes into a portable, hierarchical, + human-readable representation. -Source packages -^^^^^^^^^^^^^^^ +- `bottleneck `_: Improves the performance + of sigma-clipping and other functionality that may require computing + statistics on arrays with NaN values. -Source tarballs of past releases and the current development branch of -astropy can be downloaded from https://github.com/astropy/astropy/downloads +- `certifi `_: Useful when downloading + files from HTTPS or FTP+TLS sites in case Python is not able to locate + up-to-date root CA certificates on your system; this package is usually + already included in many Python installations (e.g., as a dependency of + the ``requests`` package). -Development repository -^^^^^^^^^^^^^^^^^^^^^^ +- `pyarrow `_ |minimum_pyarrow_version| or later: + To read/write :class:`~astropy.table.Table` objects from/to Parquet files. -The latest development version of Astropy can be cloned from github -using this command:: +- |fsspec| |minimum_fsspec_version| or later: Enables access to :ref:`subsets + of remote FITS files ` without having to download the entire file. - git clone git://github.com/astropy/astropy.git +- |s3fs| |minimum_s3fs_version| or later: Enables access to files hosted in + AWS S3 cloud storage. -.. note:: +However, note that these packages require installation only if those particular +features are needed. ``astropy`` will import even if these dependencies are not +installed. + +The following packages can optionally be used when testing: + +- |pytest-astropy|: See :ref:`sourcebuildtest` - If you wish to participate in the development of Astropy, see - :ref:`developer-docs`. This document covers only the basics - necessary to install Astropy. +- `pytest-xdist `_: Used for + distributed testing. -Building and Installing ------------------------ +- `pytest-mpl `_: Used for testing + with Matplotlib figures. -Astropy uses the Python `distutils framework -`_ for building and -installing. +- `objgraph `_: Used only in tests to test for reference leaks. -To build Astropy (from the root of the source tree):: +- |IPython| |minimum_ipython_version| or later: + Used for testing the notebook interface of `~astropy.table.Table`. - python setup.py build +- `coverage `_: Used for code coverage + measurements. -To install Astropy (from the root of the source tree):: +- `skyfield `_: Used for testing Solar System + coordinates. - python setup.py install +- `sgp4 `_: Used for testing satellite positions. -External C libraries -^^^^^^^^^^^^^^^^^^^^ +- `tox `_: Used to automate testing + and documentation builds. -The Astropy source ships with the C source code of a number of -libraries. By default, these internal copies are used to build -Astropy. However, if you wish to use the system-wide installation of -one of those libraries, you can pass one or more of the -`--use-system-X` flags to the `setup.py build` command. +.. _sourcebuildinstructions: -For example, to build Astropy using the system `libexpat`, use:: +Build from Source +================= - python setup.py build --use-system-expat +{% if is_development %} -To build using all of the system libraries, use:: +If you want to build the code from source, follow the instructions for +:ref:`contributing_environment`. Note that instead of cloning from your fork, you can +choose to clone from the main repository:: - python setup.py build --use-system-libraries + git clone https://github.com/astropy/astropy.git + cd astropy -To see which system libraries Astropy knows how to build against, use:: +Building the documentation is typically not necessary unless you are +developing code or documentation or do not have internet access, because +the stable, latest, and archived versions of Astropy's documentation are +available at `docs.astropy.org `_ . The process +is described in :ref:`builddocs`. - python setup.py build --help +{%else%} -As with all distutils commandline options, they may also be provided -in a `setup.cfg` in the same directory as `setup.py`. For example, to -use the system `libexpat`, add the following to the `setup.cfg` file:: +See the `latest documentation on how to build astropy from source `_. - [build] - use_system_expat=1 +{%endif%} -.. _builddocs: +.. _sourcebuildtest: -Building documentation +Test Source Code Build ---------------------- -.. note:: - Building the documentation is in general not necessary unless you - are writing new documentation or do not have internet access, because - the latest (and archive) versions of astropy's documentation should - be available at `docs.astropy.org `_ . +{% if is_development %} + +The easiest way to run the tests in a source checkout of ``astropy`` +is to use `tox `_:: -Building the documentation requires the Astropy source code and some additional -packages: + tox -e test-alldeps - - `Sphinx `_ (and its dependencies) 1.0 or later +There are also alternative methods of :ref:`running-tests` if you +would like more control over the testing process. - - `Graphviz `_ +{%else%} -There are two ways to build the Astropy documentation. The most straightforward -way is to execute the command (from the astropy source directory):: +See the `latest documentation on how to run the tests in a source +checkout of astropy `_. - python setup.py build_sphinx +{%endif%} -The documentation will be built in the ``docs/_build/html`` directory, and can -be read by pointing a web browser to ``docs/_build/html/index.html``. -The above method builds the API documentation from the source code. -Alternatively, you can do:: +.. _install_astropy_nightly: - cd docs - make html +Install Pre-built Development Version +===================================== -And the documentation will be generated in the same location, but using the -*installed* version of Astropy. +Most nights a development snapshot of ``astropy`` will be compiled. +This is useful if you want to test against a development version of astropy but +do not want to have to build it yourselves. You can see the +`available astropy dev snapshots page `_ +to find out what is currently being offered. -Testing your Astropy build --------------------------- +Installing these "nightlies" of ``astropy`` can be achieved by using ``pip``:: -The easiest way to test that your Astropy built correctly (without -installing astropy) is to run this from the root of the source tree:: + python -m pip install --upgrade --extra-index-url https://pypi.anaconda.org/astropy/simple astropy --pre - python setup.py test +The extra index URL tells ``pip`` to check the ``pip`` index on +pypi.anaconda.org, where the nightlies are stored, and the ``--pre`` command +tells ``pip`` to install pre-release versions (in this case ``.dev`` releases). -There are also alternative methods of :ref:`running-tests`. +You can test this installation by running the tests as described in the section +`Running tests on an installed astropy `_. diff --git a/docs/io/ascii/base_classes.rst b/docs/io/ascii/base_classes.rst index c9f4fea6275e..bd8fa58ca9d8 100644 --- a/docs/io/ascii/base_classes.rst +++ b/docs/io/ascii/base_classes.rst @@ -2,20 +2,20 @@ .. _base_class_elements: -Base class elements ----------------------------- +Base Class Elements +******************* The key elements in :mod:`astropy.io.ascii` are: -* :class:`~astropy.io.ascii.core.Column`: Internal storage of column properties and data () -* :class:`Reader `: Base class to handle reading and writing tables. -* :class:`Inputter `: Get the lines from the table input. -* :class:`Splitter `: Split the lines into string column values. -* :class:`Header `: Initialize output columns based on the table header or user input. -* :class:`Data `: Populate column data from the table. -* :class:`Outputter `: Convert column data to the specified output format, e.g. `numpy` structured array. +* :class:`~astropy.io.ascii.Column`: internal storage of column properties and data. +* :class:`Reader `: base class to handle reading and writing tables. +* :class:`Inputter `: gets the lines from the table input. +* :class:`Splitter `: splits the lines into string column values. +* :class:`Header `: initializes output columns based on the table header or user input. +* :class:`Data `: populates column data from the table. +* :class:`Outputter `: converts column data to the specified output format (e.g., ``numpy`` structured array). Each of these elements is an inheritable class with attributes that control the -corresponding functionality. In this way the large number of tweakable -parameters is modularized into managable groups. Where it makes sense these -attributes are actually functions that make it easy to handle special cases. +corresponding functionality. In this way, the large number of tunable +parameters are modularized into manageable groups. In certain places these +attributes are actually functions for handling special cases. diff --git a/docs/io/ascii/ecsv.rst b/docs/io/ascii/ecsv.rst new file mode 100644 index 000000000000..671386b66d59 --- /dev/null +++ b/docs/io/ascii/ecsv.rst @@ -0,0 +1,511 @@ +.. _ecsv_format: + +ECSV Format +=========== + +The `Enhanced Character-Separated Values (ECSV) format +`_ can be used to +write ``astropy`` `~astropy.table.Table` or `~astropy.table.QTable` datasets to +a text-only human readable data file and then read the table back without loss +of information. The format stores column specifications like unit and data type +along with table metadata by using a YAML header data structure. The +actual tabular data are stored in a standard character separated values (CSV) +format, giving compatibility with a wide variety of non-specialized CSV table +readers. + +.. attention:: + + The ECSV format is the recommended way to store Table data in a + human-readable text file. This includes use cases from informal + use in science research to production pipelines and data systems. + + In addition to Python, ECSV is supported in |TOPCAT| + and in the Java |STIL| library. + +Usage +----- + +Reading +""""""" +When reading an ECSV format table we recommend using ``format="ecsv"`` in the call to +``Table.read()`` or ``QTable.read()``. This option uses a code base that was added in +astropy 7.2. With this option you can select one of three built-in engines to read the +actual CSV data: ``"io.ascii"`` (default), ``"pyarrow"`` (via `pyarrow.read_csv() +`_), or ``"pandas"`` (via +`pandas.read_csv`). The latter two require the corresponding optional package +dependency to be installed. + +For a large gzipped ECSV file, the ``pyarrow`` engine is about 15 times faster than ``io.ascii`` +and the ``pandas`` engine is about 3 times faster. Both of these engines are also more +memory efficient than ``io.ascii``. + +Details on this option are available in `~astropy.io.misc.ecsv.read_ecsv` or by +interactively running ``Table.read.help(format="ecsv")`` in Python. + +You can also use ``format="ascii.ecsv"``, which uses the legacy code base that was the +only astropy ECSV reader prior to astropy 7.2. This option is the default if +you read a table file with an ``.ecsv`` extension without supplying the ``format`` +explicitly, e.g., ``QTable.read("my_data.ecsv")``. This default is planned to change in +a future version of astropy and the legacy reader will be deprecated and later removed. + +Writing +""""""" + +When writing in the ECSV format there are only two choices for the delimiter, +either space or comma, with space being the default. Any other value of +``delimiter`` will give an error. For reading the delimiter is specified within +the file itself. + +Apart from the delimiter, the only other applicable write arguments are +``names``, ``include_names``, and ``exclude_names``. All other arguments will be +either ignored or raise an error. + +Simple Table +------------ +.. + EXAMPLE START + Writing Data Tables as ECSV: Simple Table + +The following writes a table as a simple space-delimited file. The +ECSV format is auto-selected due to ``.ecsv`` suffix:: + + >>> import numpy as np + >>> from astropy.table import Table + >>> data = Table() + >>> data['a'] = np.array([1, 2], dtype=np.int8) + >>> data['b'] = np.array([1, 2], dtype=np.float32) + >>> data['c'] = np.array(['hello', 'world']) + >>> data.write('my_data.ecsv') # doctest: +SKIP + +The contents of ``my_data.ecsv`` are shown below:: + + # %ECSV 1.0 + # --- + # datatype: + # - {name: a, datatype: int8} + # - {name: b, datatype: float32} + # - {name: c, datatype: string} + # schema: astropy-2.0 + a b c + 1 1.0 hello + 2 2.0 world + +The ECSV header is the section prefixed by the ``#`` comment character. An ECSV +file must start with the ``%ECSV `` line. The ``datatype`` element +defines the list of columns and the ``schema`` relates to astropy-specific +extensions that are used for writing `Mixin Columns`_. + +.. + EXAMPLE END + +Masked Data +----------- + +You can write masked (or "missing") data in the ECSV format in two different +ways, either using an empty string to represent missing values or by splitting +the masked columns into separate data and mask columns. + +Empty String +"""""""""""" + +The first (default) way uses an empty string as a marker in place of +masked values. This is a bit more common outside of ``astropy`` and does not +require any astropy-specific extensions. + + >>> from astropy.table import MaskedColumn + >>> t = Table() + >>> t['x'] = MaskedColumn([1.0, 2.0, 3.0], unit='m', dtype='float32') + >>> t['x'][1] = np.ma.masked + >>> t['y'] = MaskedColumn([False, True, False], dtype='bool') + >>> t['y'][0] = np.ma.masked + + >>> t.write('my_data.ecsv', format='ascii.ecsv', overwrite=True) # doctest: +SKIP + +The contents of ``my_data.ecsv`` are shown below:: + + # %ECSV 1.0 + # --- + # datatype: + # - {name: x, unit: m, datatype: float32} + # - {name: y, datatype: bool} + # schema: astropy-2.0 + x y + 1.0 "" + "" True + 3.0 False + +To read this back, you would run the following:: + + >>> Table.read('my_data.ecsv') # doctest: +SKIP + + x y + m + float32 bool + ------- ----- + 1.0 -- + -- True + 3.0 False + +Data + Mask +""""""""""" + +The second way is to tell the writer to break any masked column into a data +column and a mask column by supplying the ``serialize_method='data_mask'`` +argument:: + + >>> t.write('my_data.ecsv', serialize_method='data_mask', overwrite=True) # doctest: +SKIP + +There are two main reasons you might want to do this: + +- Storing the data "under the mask" instead of replacing it with an empty string. +- Writing a string column that contains empty strings which are not masked. + +The contents of ``my_data.ecsv`` are shown below. First notice that there are +two new columns ``x.mask`` and ``y.mask`` that have been added, and these explicitly +record the mask values for those columns. Next notice now that the ECSV +header is a bit more complex and includes the astropy-specific extensions that +tell the reader how to interpret the plain CSV columns ``x, x.mask, y, y.mask`` +and reassemble them back into the appropriate masked columns. +:: + + # %ECSV 1.0 + # --- + # datatype: + # - {name: x, unit: m, datatype: float32} + # - {name: x.mask, datatype: bool} + # - {name: y, datatype: bool} + # - {name: y.mask, datatype: bool} + # meta: !!omap + # - __serialized_columns__: + # x: + # __class__: astropy.table.column.MaskedColumn + # data: !astropy.table.SerializedColumn {name: x} + # mask: !astropy.table.SerializedColumn {name: x.mask} + # y: + # __class__: astropy.table.column.MaskedColumn + # data: !astropy.table.SerializedColumn {name: y} + # mask: !astropy.table.SerializedColumn {name: y.mask} + # schema: astropy-2.0 + x x.mask y y.mask + 1.0 False False True + 2.0 True True False + 3.0 False False False + +.. note:: + + For the security minded, the ``__class__`` value must within an allowed list + of astropy classes that are trusted by the reader. You cannot use an + arbitrary class here. + +.. + EXAMPLE START + Using ECSV Format to Write Astropy Tables with Masked or Missing Data + +Per-column control +@@@@@@@@@@@@@@@@@@ + +In rare cases it may be necessary to specify the serialization method for each +column individually. This is shown in the example below:: + + >>> from astropy.table.table_helpers import simple_table + >>> t = simple_table(masked=True) + >>> t['c'][0] = "" # Valid empty string in data + >>> t +
+ a b c + int64 float64 str1 + ----- ------- ---- + -- 1.0 + 2 2.0 -- + 3 -- e + +Now we tell ECSV writer to output separate data and mask columns for the +string column ``'c'``: + +.. doctest-skip:: + + >>> t['c'].info.serialize_method['ecsv'] = 'data_mask' + >>> ascii.write(t, format='ecsv') + # %ECSV 1.0 + # --- + # datatype: + # - {name: a, datatype: int64} + # - {name: b, datatype: float64} + # - {name: c, datatype: string} + # - {name: c.mask, datatype: bool} + # meta: !!omap + # - __serialized_columns__: + # c: + # __class__: astropy.table.column.MaskedColumn + # data: !astropy.table.SerializedColumn {name: c} + # mask: !astropy.table.SerializedColumn {name: c.mask} + # schema: astropy-2.0 + a b c c.mask + "" 1.0 "" False + 2 2.0 d True + 3 "" e False + +When you read this back in, both the empty (zero-length) string and the masked +``'d'`` value in the column ``'c'`` will be preserved. + +.. + EXAMPLE END + +.. _ecsv_format_mixin_columns: + +Mixin Columns +------------- + +It is possible to store not only standard `~astropy.table.Column` and +`~astropy.table.MaskedColumn` objects to ECSV but also the following +:ref:`mixin_columns`: + +- `astropy.time.Time` +- `astropy.time.TimeDelta` +- `astropy.units.Quantity` +- `astropy.coordinates.Latitude` +- `astropy.coordinates.Longitude` +- `astropy.coordinates.Angle` +- `astropy.coordinates.Distance` +- `astropy.coordinates.EarthLocation` +- `astropy.coordinates.SkyCoord` +- `astropy.table.NdarrayMixin` +- Coordinate representation types such as `astropy.coordinates.SphericalRepresentation` + +In general, a mixin column may contain multiple data components as well as +object attributes beyond the standard `~astropy.table.Column` attributes like +``format`` or ``description``. Storing such mixin columns is done by replacing +the mixin column with column(s) representing the underlying data component(s) +and then inserting metadata which informs the reader of how to reconstruct the +original column. For example, a `~astropy.coordinates.SkyCoord` mixin column in +``'spherical'`` representation would have data attributes ``ra``, ``dec``, +``distance``, along with object attributes like ``representation_type`` or +``frame``. + +.. + EXAMPLE START + Writing a Table with a SkyCoord Column in ECSV Format + +This example demonstrates writing a `~astropy.table.QTable` that has `~astropy.time.Time` +and `~astropy.coordinates.SkyCoord` mixin columns:: + + >>> from astropy.coordinates import SkyCoord + >>> import astropy.units as u + >>> from astropy.table import QTable + + >>> sc = SkyCoord(ra=[1, 2] * u.deg, dec=[3, 4] * u.deg) + >>> sc.info.description = 'flying circus' + >>> q = [1, 2] * u.m + >>> q.info.format = '.2f' + >>> t = QTable() + >>> t['c'] = [1, 2] + >>> t['q'] = q + >>> t['sc'] = sc + + >>> t.write('my_data.ecsv') # doctest: +SKIP + +The contents of ``my_data.ecsv`` are below:: + + # %ECSV 1.0 + # --- + # datatype: + # - {name: c, datatype: int64} + # - {name: q, unit: m, datatype: float64, format: .2f} + # - {name: sc.ra, unit: deg, datatype: float64} + # - {name: sc.dec, unit: deg, datatype: float64} + # meta: !!omap + # - __serialized_columns__: + # q: + # __class__: astropy.units.quantity.Quantity + # __info__: {format: .2f} + # unit: !astropy.units.Unit {unit: m} + # value: !astropy.table.SerializedColumn {name: q} + # sc: + # __class__: astropy.coordinates.sky_coordinate.SkyCoord + # __info__: {description: flying circus} + # dec: !astropy.table.SerializedColumn + # __class__: astropy.coordinates.angles.Latitude + # unit: &id001 !astropy.units.Unit {unit: deg} + # value: !astropy.table.SerializedColumn {name: sc.dec} + # frame: icrs + # ra: !astropy.table.SerializedColumn + # __class__: astropy.coordinates.angles.Longitude + # unit: *id001 + # value: !astropy.table.SerializedColumn {name: sc.ra} + # wrap_angle: !astropy.coordinates.Angle + # unit: *id001 + # value: 360.0 + # representation_type: spherical + # schema: astropy-2.0 + c q sc.ra sc.dec + 1 1.0 1.0 3.0 + 2 2.0 2.0 4.0 + +The ``'__class__'`` keyword gives the fully-qualified class name and must be +one of the specifically allowed ``astropy`` classes. There is no option to add +user-specified allowed classes. The ``'__info__'`` keyword contains values for +standard `~astropy.table.Column` attributes like ``description`` or ``format``, +for any mixin columns that are represented by more than one serialized column. + +.. + EXAMPLE END + +.. _ecsv_format_masked_columns: + +Multidimensional Columns +------------------------ + +Using ECSV it is possible to write a table that contains multidimensional +columns (both masked and unmasked). This is done by encoding each element as a +string using JSON. This functionality works for all column types that are +supported by ECSV including :ref:`mixin_columns`. This capability is added in +astropy 4.3 and ECSV version 1.0. + +.. + EXAMPLE START + Using ECSV Format to Write Astropy Tables with Multidimensional Columns + +We start by defining a table with 2 rows where each element in the second column +``'b'`` is itself a 3x2 array:: + + >>> t = Table() + >>> t['a'] = ['x', 'y'] + >>> t['b'] = np.arange(12, dtype=np.float64).reshape(2, 3, 2) + >>> t +
+ a b + str1 float64[3,2] + ---- ------------ + x 0.0 .. 5.0 + y 6.0 .. 11.0 + + >>> t['b'][0] + array([[0., 1.], + [2., 3.], + [4., 5.]]) + +Now we can write this to ECSV and observe how the N-d column ``'b'`` has been +written as a string with ``datatype: string``. Notice also that the column +descriptor for the column includes the new ``subtype: float64[3,2]`` attribute +specifying the type and shape of each item. + +.. doctest-skip:: + + >>> ascii.write(t, format='ecsv') # doctest: +SKIP + # %ECSV 1.0 + # --- + # datatype: + # - {name: a, datatype: string} + # - {name: b, datatype: string, subtype: 'float64[3,2]'} + # schema: astropy-2.0 + a b + x [[0.0,1.0],[2.0,3.0],[4.0,5.0]] + y [[6.0,7.0],[8.0,9.0],[10.0,11.0]] + +When you read this back in, the sequence of JSON-encoded column items are then +decoded using JSON back into the original N-d column. + +.. + EXAMPLE END + +Variable-length arrays +---------------------- + +ECSV supports storing multidimensional columns is when the length of each array +element may vary. This data structure is supported in the `FITS standard +`_. While ``numpy`` does not +natively support variable-length arrays, it is possible to represent such a +structure using an object-type array of typed ``np.ndarray`` objects. This is how +the ``astropy`` FITS reader outputs a variable-length array. + +This capability is added in astropy 4.3 and ECSV version 1.0. + +Most commonly variable-length arrays have a 1-d array in each cell of the +column. You might a column with 1-d ``np.ndarray`` cells having lengths of 2, 5, +and 3 respectively. + +The ECSV standard and ``astropy`` also supports arbitrary N-d arrays in each +cell, where all dimensions except the last one must match. For instance you +could have a column with ``np.ndarray`` cells having shapes of ``(4,4,2)``, +``(4,4,5)``, and ``(4,4,3)`` respectively. + +.. + EXAMPLE START + Using ECSV Format to Write Astropy Tables with Variable-Length Arrays + +The example below shows writing a variable-length 1-d array to ECSV. Notice the +new ECSV column attribute ``subtype: 'int64[null]'``. The ``[null]`` indicates a +variable length for the one dimension. If we had been writing the N-d example +above the subtype would have been ``int64[4,4,null]``. + +.. doctest-skip:: + + >>> t = Table() + >>> t['a'] = np.empty(3, dtype=object) + >>> t['a'] = [np.array([1, 2], dtype=np.int64), + ... np.array([3, 4, 5], dtype=np.int64), + ... np.array([6, 7, 8, 9], dtype=np.int64)] + >>> ascii.write(t, format='ecsv') + # %ECSV 1.0 + # --- + # datatype: + # - {name: a, datatype: string, subtype: 'int64[null]'} + # schema: astropy-2.0 + a + [1,2] + [3,4,5] + [6,7,8,9] + +.. + EXAMPLE END + +Object arrays +------------- + +ECSV can store object-type columns with simple Python objects consisting of +``dict``, ``list``, ``str``, ``int``, ``float``, ``bool`` and ``None`` elements. +More precisely, any object that can be serialized to `JSON +`__ using the standard library `json +`__ package is supported. + +.. + EXAMPLE START + Using ECSV Format to Write Astropy Tables with Object Arrays + +The example below shows writing an object array to ECSV. Because JSON requires +a double-quote around strings, and because ECSV requires ``""`` to represent +a double-quote within a string, one tends to get double-double quotes in this +representation. + +.. doctest-skip:: + + >>> t = Table() + >>> t['a'] = np.array([{'a': 1}, + ... {'b': [2.5, None]}, + ... True], dtype=object) + >>> ascii.write(t, format='ecsv') + # %ECSV 1.0 + # --- + # datatype: + # - {name: a, datatype: string, subtype: json} + # schema: astropy-2.0 + a + "{""a"":1}" + "{""b"":[2.5,null]}" + true + +.. + EXAMPLE END + +Meta data +--------- + +The ECSV format allows for different data structures in the header "meta" element. +Typically, this will be a mapping (what Python calls a dict), but it could also be a list or +nested structure. +Astropy's `~astropy.table.Table` requires a `dict` for table meta data. Thus, +the reader will attempt to convert the ECSV header "meta" to a dict or store the entire structure +in a single entry in the table meta. +On writing, that dict will be written back in astropy's form, so ECSV files that were not +originally written by astropy will preserve the content, but not the formatting of the +header "meta" element. diff --git a/docs/io/ascii/extension_classes.rst b/docs/io/ascii/extension_classes.rst index 902634ec482d..31b18303b912 100644 --- a/docs/io/ascii/extension_classes.rst +++ b/docs/io/ascii/extension_classes.rst @@ -2,26 +2,32 @@ .. _extension_reader_classes: -Extension Reader classes ------------------------- +Extension Reader Classes +************************ -The following classes extend the base :class:`~astropy.io.ascii.core.BaseReader` functionality to handle reading and writing -different table formats. Some, such as the :class:`~astropy.io.ascii.Basic` Reader class -are fairly general and include a number of configurable attributes. Others +The following classes extend the base :class:`~astropy.io.ascii.BaseReader` functionality to handle reading and writing +different table formats. Some, such as the :class:`~astropy.io.ascii.Basic` Reader class +are fairly general and include a number of configurable attributes. Others such as :class:`~astropy.io.ascii.Cds` or :class:`~astropy.io.ascii.Daophot` are specialized to read certain well-defined but idiosyncratic formats. -* :class:`~astropy.io.ascii.latex.AASTex`: AASTeX `deluxetable` used for AAS journals -* :class:`~astropy.io.ascii.basic.Basic`: basic table with customizable delimiters and header configurations -* :class:`~astropy.io.ascii.cds.Cds`: `CDS format table `_ (also Vizier and ApJ machine readable tables) -* :class:`~astropy.io.ascii.basic.CommentedHeader`: column names given in a line that begins with the comment character -* :class:`~astropy.io.ascii.daophot.Daophot`: table from the IRAF DAOphot package -* :class:`~astropy.io.ascii.fixedwidth.FixedWidth`: table with fixed-width columns (see also :ref:`fixed_width_gallery`) -* :class:`~astropy.io.ascii.fixedwidth.FixedWidthNoHeader`: table with fixed-width columns and no header -* :class:`~astropy.io.ascii.fixedwidth.FixedWidthTwoLine`: table with fixed-width columns and a two-line header -* :class:`~astropy.io.ascii.ipac.Ipac`: `IPAC format table `_ -* :class:`~astropy.io.ascii.latex.Latex`: LaTeX table with datavalue in the `tabular` environment -* :class:`~astropy.io.ascii.basic.NoHeader`: basic table with no header where columns are auto-named -* :class:`~astropy.io.ascii.basic.Rdb`: tab-separated values with an extra line after the column definition line -* :class:`~astropy.io.ascii.basic.Tab`: tab-separated values - +* :class:`~astropy.io.ascii.AASTex`: AASTeX `deluxetable `_ used for AAS journals. +* :class:`~astropy.io.ascii.Basic`: basic table with customizable delimiters and header configurations. +* :class:`~astropy.io.ascii.Cds`: `CDS format table `_ (also Vizier and ApJ machine readable tables). +* :class:`~astropy.io.ascii.CommentedHeader`: column names given in a line that begins with the comment character. +* :class:`~astropy.io.ascii.Csv`: comma-separated values. +* :class:`~astropy.io.ascii.Daophot`: table from the IRAF DAOphot package. +* :class:`~astropy.io.ascii.FixedWidth`: table with fixed-width columns (see also :ref:`fixed_width_gallery`). +* :class:`~astropy.io.ascii.FixedWidthNoHeader`: table with fixed-width columns and no header. +* :class:`~astropy.io.ascii.FixedWidthTwoLine`: table with fixed-width columns and a two-line header. +* :class:`~astropy.io.ascii.HTML`: HTML format table contained in a
tag. +* :class:`~astropy.io.ascii.Ipac`: `IPAC format table `_. +* :class:`~astropy.io.ascii.Latex`: LaTeX table with datavalue in the ``tabular`` environment. +* :class:`~astropy.io.ascii.Mesa`: MESA stellar evolution code output format. +* :class:`~astropy.io.ascii.Mrt`: `AAS Machine-Readable Table format `_. +* :class:`~astropy.io.ascii.NoHeader`: basic table with no header where columns are auto-named. +* :class:`~astropy.io.ascii.Rdb`: tab-separated values with an extra line after the column definition line. +* :class:`~astropy.io.ascii.RST`: `reStructuredText simple format table `_. +* :class:`~astropy.io.ascii.SExtractor`: `SExtractor format table `_. +* :class:`~astropy.io.ascii.Tab`: tab-separated values. +* :class:`~astropy.io.ascii.Tdat`: Transportable Database Aggregate Table format diff --git a/docs/io/ascii/fast_ascii_io.rst b/docs/io/ascii/fast_ascii_io.rst new file mode 100644 index 000000000000..121f585b666b --- /dev/null +++ b/docs/io/ascii/fast_ascii_io.rst @@ -0,0 +1,200 @@ +.. include:: references.txt + +.. _fast_ascii_io: + +Fast ASCII I/O +************** + +While :mod:`astropy.io.ascii` was designed with flexibility and extensibility +in mind, there is also a less flexible but significantly faster Cython/C engine +for reading and writing ASCII files. By default, |read| and |write| will +attempt to use this engine when dealing with compatible formats. The following +formats are currently compatible with the fast engine: + + * ``basic`` + * ``commented_header`` + * ``csv`` + * ``no_header`` + * ``rdb`` + * ``tab`` + +The fast engine can also be enabled through the format parameter by prefixing +a compatible format with "fast" and then an underscore. In this case, or +when enforcing the fast engine by either setting ``fast_reader='force'`` +or explicitly setting any of the :ref:`fast_conversion_opts`, |read| +will not fall back on an ordinary reader if fast reading fails. + +.. Note:: The fast engine only supports ASCII-encoded data, so reading or writing + unicode text is not possible with the fast engine. For unicode support with large + files, consider using the :ref:`Pandas Table I/O interface `. + +Examples +-------- + +.. + EXAMPLE START + Read and Write a CSV File Using Fast ASCII + +To open a CSV file and write it back out:: + + >>> from astropy.table import Table + >>> t = ascii.read('file.csv', format='fast_csv') # doctest: +SKIP + >>> t.write('output.csv', format='ascii.fast_csv') # doctest: +SKIP + +To disable the fast engine, specify ``fast_reader=False`` or +``fast_writer=False``. For example:: + + >>> t = ascii.read('file.csv', format='csv', fast_reader=False) # doctest: +SKIP + >>> t.write('file.csv', format='csv', fast_writer=False) # doctest: +SKIP + +.. Note:: Guessing and Fast reading + + By default |read| will try to guess the format of the input data by + successively trying different formats until one succeeds + (see the section on :ref:`guess_formats`). For each supported + format it will first try the fast, then the slow version of that + reader. Without any additional options this means that both some pure + Python readers with no fast implementation and the Python versions + of some readers will be tried before getting to some of the fast + readers. To bypass them entirely, a fast reader should be explicitly + requested as above. + + **For optimum performance** however, it is recommended to turn off + guessing entirely (``guess=False``) or narrow down the format options + as much as possible by specifying the format (e.g., ``format='csv'``) + and/or other options such as the delimiter. + +.. + EXAMPLE END + +Reading +======= + +Since the fast engine is not part of the ordinary :mod:`astropy.io.ascii` +infrastructure, fast readers raise an error when passed certain +parameters which are not implemented in the fast reader infrastructure. +In this case |read| will fall back on the ordinary reader, unless the +fast reader has been explicitly requested (see above). +These parameters are: + + * Negative ``header_start`` (except for commented-header format) + * Negative ``data_start`` + * ``data_start=None`` + * ``comment`` string not of length 1 + * ``delimiter`` string not of length 1 + * ``quotechar`` string not of length 1 + * ``converters`` + * ``outputter_cls`` + * ``inputter_cls`` + * ``data_splitter_cls`` + * ``header_splitter_cls`` + +.. _fast_conversion_opts: + +Fast Conversion Options +----------------------- + +In addition to ``True`` and ``False``, the parameter ``fast_reader`` can also +be a ``dict`` specifying any of two additional parameters, +``use_fast_converter`` and ``exponent_style``. + +Example +======= + +.. + EXAMPLE START + Fast Conversion Options for Faster Table Reading + +To specify additional parameters using ``fast_reader``:: + + >>> ascii.read('data.txt', format='basic', + ... fast_reader={'use_fast_converter': True}) # doctest: +SKIP + +.. + EXAMPLE END + +These options allow for even faster table reading when enabled, but both are +disabled by default because they come with some caveats. + +Setting ``use_fast_converter`` to be ``True`` enables a faster but +slightly imprecise conversion method for floating-point values, as described +below. + +The ``exponent_style`` parameter allows to define a different character +from the default ``'e'`` for exponential formats in the input file. +The special setting ``'fortran'`` enables auto-detection of any valid +exponent character under Fortran notation. For details see the section on +:ref:`fortran_style_exponents`. + +Fast Converter +-------------- + +Input floating-point values should ideally be converted to the +nearest possible floating-point approximation; that is, the conversion +should be correct within half of the distance between the two closest +representable values, or 0.5 `ULP +`__. The ordinary readers, +as well as the default fast reader, are guaranteed to convert floating-point +values within 0.5 ULP, but there is also a faster and less accurate +conversion method accessible via ``use_fast_converter``. If the input +data has less than about fifteen significant figures, or if accuracy is +relatively unimportant, this converter might be the best option in +performance-critical scenarios. + +For values with a reasonably small number of +significant figures, the fast converter is guaranteed to produce an optimal +conversion (within 0.5 ULP). Once the number of significant figures exceeds +the precision of 64-bit floating-point values, the fast converter is no +longer guaranteed to be within 0.5 ULP, but about 60% of values end up +within 0.5 ULP and about 90% within 1.0 ULP. + +Reading Large Tables +-------------------- + +For reading very large tables using the fast reader, see the section on +:ref:`chunk_reading`. + +Writing +======= + +The fast engine supports the same functionality as the ordinary writing engine +and is generally about two to four times faster than the ordinary engine. +The speed advantage of the faster engine is greatest for integer data and least +for floating-point data; the fast engine is around 3.6 times faster for a +sample file including a mixture of floating-point, integer, and text data. +Also note that stripping string values slows down the writing process, so +specifying ``strip_whitespace=False`` can improve performance. + +Speed Gains +=========== + +The fast ASCII engine was designed based on the general parsing strategy +used in the `Pandas `__ data analysis library, so +its performance is generally comparable (although slightly slower by +default) to the Pandas ``read_csv`` method. + +The ``genfromtxt`` and the ordinary :mod:`astropy.io.ascii` reader +are very similar in terms of speed, while ``read_csv`` is slightly faster +than the fast engine for integer and floating-point data; for pure +floating-point data, enabling the fast converter yields a speedup of about +50%. Also note that Pandas uses the exact same method as the fast +converter in Astropy when converting floating-point data. + +The difference in performance between the fast engine and Pandas for +text data depends on the extent to which data values are repeated, as +Pandas is almost twice as fast as the fast engine when every value is +identical and the reverse is true when values are randomized. This is +because the fast engine uses fixed-size NumPy string arrays for +text data, while Pandas uses variable-size object arrays and uses an +underlying set to avoid copying repeated values. + +Overall, the fast engine tends to be around four or five times faster than +the ordinary ASCII engine. If the input data is very large (generally +about 100,000 rows or greater), and particularly if the data does not +contain primarily integer data or repeated string values. + +Another point worth noting is that the fast engine uses memory mapping +if a filename is supplied as input. If you want to avoid this for whatever +reason, supply an open file object instead. However, this will generally +be less efficient from both a time and a memory perspective, as the entire +file input will have to be read at once. diff --git a/docs/io/ascii/fixed_width_gallery.rst b/docs/io/ascii/fixed_width_gallery.rst index 6e193354e708..cbf2615f2f31 100644 --- a/docs/io/ascii/fixed_width_gallery.rst +++ b/docs/io/ascii/fixed_width_gallery.rst @@ -2,12 +2,12 @@ .. _fixed_width_gallery: -Fixed-width Gallery -------------------- +Fixed-Width Gallery +******************* Fixed-width tables are those where each column has the same width for every row -in the table. This is commonly used to make tables easy to read for humans or -FORTRAN codes. It also reduces issues with quoting and special characters, +in the table. This is commonly used to make tables easy to read for humans or +Fortran codes. It also reduces issues with quoting and special characters, for example:: Col1 Col2 Col3 Col4 @@ -16,23 +16,27 @@ for example:: 2.4 's worlds 2 2 There are a number of common variations in the formatting of fixed-width tables -which :mod:`astropy.io.ascii` can read and write. The most signicant difference is -whether there is no header line (:class:`~astropy.io.ascii.FixedWidthNoHeader`), one +which :mod:`astropy.io.ascii` can read and write. The most significant +difference is whether there is no header line (:class:`~astropy.io.ascii.FixedWidthNoHeader`), one header line (:class:`~astropy.io.ascii.FixedWidth`), or two header lines -(:class:`~astropy.io.ascii.FixedWidthTwoLine`). Next, there are variations in the -delimiter character, whether the delimiter appears on either end ("bookends"), -and padding around the delimiter. +(:class:`~astropy.io.ascii.FixedWidthTwoLine`). Next, there are variations in +the delimiter character, like whether the delimiter appears on either end +("bookends"), or if there is padding around the delimiter. Details are available in the class API documentation, but the easiest way to -understand all the options and their interactions is by example. +understand all of the options and their interactions is by example. -Reading -^^^^^^^ +Reading +======= -FixedWidth -"""""""""" +.. + EXAMPLE START + Reading Fixed-Width Tables -**Nice, typical fixed format table** +Fixed Width +----------- + +**Nice, typical, fixed-format table:** :: >>> from astropy.io import ascii @@ -42,11 +46,15 @@ FixedWidth ... | 1.2 | "hello" | ... | 2.4 |'s worlds| ... """ - >>> ascii.read(table, Reader=ascii.FixedWidth) - rec.array([(1.2, '"hello"'), (2.4, "'s worlds")], - dtype=[('Col1', '>> ascii.read(table, format='fixed_width') +
+ Col1 Col2 + float64 str9 + ------- --------- + 1.2 "hello" + 2.4 's worlds + +**Typical fixed-format table with col names provided:** :: >>> table = """ @@ -55,23 +63,31 @@ FixedWidth ... | 1.2 | "hello" | ... | 2.4 |'s worlds| ... """ - >>> ascii.read(table, Reader=ascii.FixedWidth, names=('name1', 'name2')) - rec.array([(1.2, '"hello"'), (2.4, "'s worlds")], - dtype=[('name1', '>> ascii.read(table, format='fixed_width', names=['name1', 'name2']) +
+ name1 name2 + float64 str9 + ------- --------- + 1.2 "hello" + 2.4 's worlds + +**Weird input table with data values chopped by col extent:** :: >>> table = """ ... Col1 | Col2 | - ... 1.2 "hello" + ... 1.2 "hello" ... 2.4 sdf's worlds ... """ - >>> ascii.read(table, Reader=ascii.FixedWidth) - rec.array([(1.2, '"hel'), (2.4, "df's wo")], - dtype=[('Col1', '>> ascii.read(table, format='fixed_width') +
+ Col1 Col2 + float64 str7 + ------- ------- + 1.2 "hel + 2.4 df's wo + +**Table with double delimiters:** :: >>> table = """ @@ -80,13 +96,16 @@ FixedWidth ... | Mary | 555-2134 |192.168.1.12X| ... | Bob | 555-4527 | 192.168.1.9X| ... """ - >>> ascii.read(table, Reader=ascii.FixedWidth) - rec.array([('John', '555-1234', '192.168.1.10'), - ('Mary', '555-2134', '192.168.1.12'), - ('Bob', '555-4527', '192.168.1.9')], - dtype=[('Name', '|S4'), ('Phone', '|S8'), ('TCP', '|S12')]) - -**Table with space delimiter** + >>> ascii.read(table, format='fixed_width') +
+ Name Phone TCP + str4 str8 str12 + ---- -------- ------------ + John 555-1234 192.168.1.10 + Mary 555-2134 192.168.1.12 + Bob 555-4527 192.168.1.9 + +**Table with space delimiter:** :: >>> table = """ @@ -95,15 +114,18 @@ FixedWidth ... Mary 555-2134 192.168.1.12 ... Bob 555-4527 192.168.1.9 ... """ - >>> ascii.read(table, Reader=ascii.FixedWidth, delimiter=' ') - rec.array([('John', '555-1234', '192.168.1.10'), - ('Mary', '555-2134', '192.168.1.12'), - ('Bob', '555-4527', '192.168.1.9')], - dtype=[('Name', '|S4'), ('--Phone-', '|S8'), ('----TCP-----', '|S12')]) - -**Table with no header row and auto-column naming.** - -Use header_start and data_start keywords to indicate no header line. + >>> ascii.read(table, format='fixed_width', delimiter=' ') +
+ Name --Phone- ----TCP----- + str4 str8 str12 + ---- -------- ------------ + John 555-1234 192.168.1.10 + Mary 555-2134 192.168.1.12 + Bob 555-4527 192.168.1.9 + +**Table with no header row and auto-column naming:** + +Use ``header_start`` and ``data_start`` keywords to indicate no header line. :: >>> table = """ @@ -111,33 +133,42 @@ Use header_start and data_start keywords to indicate no header line. ... | Mary | 555-2134 |192.168.1.12| ... | Bob | 555-4527 | 192.168.1.9| ... """ - >>> ascii.read(table, Reader=ascii.FixedWidth, - ... header_start=None, data_start=0) - rec.array([('John', '555-1234', '192.168.1.10'), - ('Mary', '555-2134', '192.168.1.12'), - ('Bob', '555-4527', '192.168.1.9')], - dtype=[('col1', '|S4'), ('col2', '|S8'), ('col3', '|S12')]) - -**Table with no header row and with col names provided.** - -Second and third rows also have hanging spaces after final "|". Use header_start and data_start -keywords to indicate no header line. + >>> ascii.read(table, format='fixed_width', + ... header_start=None, data_start=0) +
+ col1 col2 col3 + str4 str8 str12 + ---- -------- ------------ + John 555-1234 192.168.1.10 + Mary 555-2134 192.168.1.12 + Bob 555-4527 192.168.1.9 + +**Table with no header row and with col names provided:** + +Second and third rows also have hanging spaces after final "|". Use +header_start and data_start keywords to indicate no header line. :: - >>> table = ["| John | 555-1234 |192.168.1.10|" - ... "| Mary | 555-2134 |192.168.1.12| " + >>> table = ["| John | 555-1234 |192.168.1.10|", + ... "| Mary | 555-2134 |192.168.1.12| ", ... "| Bob | 555-4527 | 192.168.1.9| "] - >>> ascii.read(table, Reader=ascii.FixedWidth, - ... header_start=None, data_start=0, - ... names=('Name', 'Phone', 'TCP')) - rec.array([('John', '555-1234', '192.168.1.10')], - dtype=[('Name', '|S4'), ('Phone', '|S8'), ('TCP', '|S12')]) - -FixedWidthNoHeader -"""""""""""""""""" - -**Table with no header row and auto-column naming. Use the FixedWidthNoHeader -convenience class.** + >>> ascii.read(table, format='fixed_width', + ... header_start=None, data_start=0, + ... names=('Name', 'Phone', 'TCP')) +
+ Name Phone TCP + str4 str8 str12 + ---- -------- ------------ + John 555-1234 192.168.1.10 + Mary 555-2134 192.168.1.12 + Bob 555-4527 192.168.1.9 + + +Fixed Width No Header +--------------------- + +**Table with no header row and auto-column naming. Use the +``fixed_width_no_header`` format for convenience:** :: >>> table = """ @@ -145,17 +176,20 @@ convenience class.** ... | Mary | 555-2134 |192.168.1.12| ... | Bob | 555-4527 | 192.168.1.9| ... """ - >>> ascii.read(table, Reader=ascii.FixedWidthNoHeader) - rec.array([('John', '555-1234', '192.168.1.10'), - ('Mary', '555-2134', '192.168.1.12'), - ('Bob', '555-4527', '192.168.1.9')], - dtype=[('col1', '|S4'), ('col2', '|S8'), ('col3', '|S12')]) - -**Table with no delimiter with column start and end values specified.** - -This uses the col_starts and col_ends keywords. Note that the -col_ends values are inclusive so a position range of 0 to 5 -will select the first 6 characters. + >>> ascii.read(table, format='fixed_width_no_header') +
+ col1 col2 col3 + str4 str8 str12 + ---- -------- ------------ + John 555-1234 192.168.1.10 + Mary 555-2134 192.168.1.12 + Bob 555-4527 192.168.1.9 + +**Table with no delimiter with column start and end values specified:** + +This uses the col_starts and col_ends keywords. Note that the +col_ends values are inclusive so a position range of zero to five +will select the first six characters. :: >>> table = """ @@ -165,49 +199,121 @@ will select the first 6 characters. ... Mary 555- 2134 192.168.1.12 ... Bob 555- 4527 192.168.1.9 ... """ - >>> ascii.read(table, Reader=ascii.FixedWidthNoHeader, + >>> ascii.read(table, format='fixed_width_no_header', ... names=('Name', 'Phone', 'TCP'), ... col_starts=(0, 9, 18), ... col_ends=(5, 17, 28), ... ) - rec.array([('John', '555- 1234', '192.168.1.'), - ('Mary', '555- 2134', '192.168.1.'), - ('Bob', '555- 4527', '192.168.1')], - dtype=[('Name', '|S4'), ('Phone', '|S9'), ('TCP', '|S10')]) +
+ Name Phone TCP + str4 str9 str10 + ---- --------- ---------- + John 555- 1234 192.168.1. + Mary 555- 2134 192.168.1. + Bob 555- 4527 192.168.1 + +**Table with no delimiter with only column start or end values specified:** + +If only the col_starts keyword is given, it is assumed that each column +ends where the next column starts, and the final column ends at the same +position as the longest line of data. -FixedWidthTwoLine -""""""""""""""""" +Conversely, if only the col_ends keyword is given, it is assumed that the first +column starts at position zero and that each successive column starts +immediately after the previous one. -**Typical fixed format table with two header lines with some cruft** +The two examples below read the same table and produce the same result. :: >>> table = """ - ... Col1 Col2 - ... ---- --------- - ... 1.2xx"hello" - ... 2.4 's worlds + ... #1 9 19 <== Column start indexes + ... #| | | <== Column start positions + ... #<------><--------><-------------> <== Inferred column positions + ... John 555- 1234 192.168.1.10 + ... Mary 555- 2134 192.168.1.123 + ... Bob 555- 4527 192.168.1.9 + ... Bill 555-9875 192.255.255.255 ... """ - >>> ascii.read(table, Reader=ascii.FixedWidthTwoLine) - rec.array([(1.2, '"hello"'), (2.4, "'s worlds")], - dtype=[('Col1', '>> ascii.read(table, + ... format='fixed_width_no_header', + ... names=('Name', 'Phone', 'TCP'), + ... col_starts=(1, 9, 19), + ... ) +
+ Name Phone TCP + str4 str9 str15 + ---- --------- --------------- + John 555- 1234 192.168.1.10 + Mary 555- 2134 192.168.1.123 + Bob 555- 4527 192.168.1.9 + Bill 555-9875 192.255.255.255 + + >>> ascii.read(table, + ... format='fixed_width_no_header', + ... names=('Name', 'Phone', 'TCP'), + ... col_ends=(8, 18, 32), + ... ) +
+ Name Phone TCP + str4 str9 str14 + ---- --------- -------------- + John 555- 1234 192.168.1.10 + Mary 555- 2134 192.168.1.123 + Bob 555- 4527 192.168.1.9 + Bill 555-9875 192.255.255.25 + + +Fixed Width Two Line +-------------------- -**Restructured text table** +**Typical fixed-format table with two header lines with some cruft:** +:: + + >>> table = """ + ... Col1 Col2 + ... ---- --------- + ... 1.2xx"hello" + ... 2.4 's worlds + ... """ + >>> ascii.read(table, format='fixed_width_two_line') +
+ Col1 Col2 + float64 str9 + ------- --------- + 1.2 "hello" + 2.4 's worlds + +.. + EXAMPLE END + +.. + EXAMPLE START + Reading a reStructuredText Table + +**reStructuredText table:** :: >>> table = """ ... ======= =========== - ... Col1 Col2 + ... Col1 Col2 ... ======= =========== - ... 1.2 "hello" + ... 1.2 "hello" ... 2.4 's worlds ... ======= =========== ... """ - >>> ascii.read(table, Reader=ascii.FixedWidthTwoLine, + >>> ascii.read(table, format='fixed_width_two_line', ... header_start=1, position_line=2, data_end=-1) - rec.array([(1.2, '"hello"'), (2.4, "'s worlds")], - dtype=[('Col1', ' + Col1 Col2 + float64 str9 + ------- --------- + 1.2 "hello" + 2.4 's worlds + +.. + EXAMPLE END -**Text table designed for humans and test having position line before the header line.** +**Text table designed for humans and test having position line before the header line:** :: >>> table = """ @@ -218,18 +324,26 @@ FixedWidthTwoLine ... | 2.4 | 's worlds| ... +------+----------+ ... """ - >>> ascii.read(table, Reader=ascii.FixedWidthTwoLine, delimiter='+', + >>> ascii.read(table, format='fixed_width_two_line', delimiter='+', ... header_start=1, position_line=0, data_start=3, data_end=-1) - rec.array([(1.2, '"hello"'), (2.4, "'s worlds")], - dtype=[('Col1', ' + Col1 Col2 + float64 str9 + ------- --------- + 1.2 "hello" + 2.4 's worlds Writing -^^^^^^^ +======= + +.. + EXAMPLE START + Writing Fixed-Width Tables -FixedWidth -"""""""""" +Fixed Width +----------- -**Define input values ``dat`` for all write examples.** +**Define input values ``dat`` for all write examples:** :: >>> table = """ @@ -237,108 +351,217 @@ FixedWidth ... | 1.2 | "hello" | 1 | a | ... | 2.4 | 's worlds | 2 | 2 | ... """ - >>> dat = ascii.read(table, Reader=ascii.FixedWidth) + >>> dat = ascii.read(table, format='fixed_width') -**Write a table as a normal fixed width table.** +**Write a table as a normal fixed-width table:** :: - >>> ascii.write(dat, Writer=ascii.FixedWidth) + >>> ascii.write(dat, format='fixed_width') | Col1 | Col2 | Col3 | Col4 | | 1.2 | "hello" | 1 | a | | 2.4 | 's worlds | 2 | 2 | -**Write a table as a fixed width table with no padding.** +**Write a table as a fixed-width table with no padding:** :: - >>> ascii.write(dat, Writer=ascii.FixedWidth, delimiter_pad=None) + >>> ascii.write(dat, format='fixed_width', delimiter_pad=None) |Col1| Col2|Col3|Col4| | 1.2| "hello"| 1| a| | 2.4|'s worlds| 2| 2| -**Write a table as a fixed width table with no bookend.** +**Write a table as a fixed-width table with no bookend:** :: - >>> ascii.write(dat, Writer=ascii.FixedWidth, bookend=False) + >>> ascii.write(dat, format='fixed_width', bookend=False) Col1 | Col2 | Col3 | Col4 1.2 | "hello" | 1 | a 2.4 | 's worlds | 2 | 2 -**Write a table as a fixed width table with no delimiter.** +**Write a table as a fixed-width table with no delimiter:** :: - >>> ascii.write(dat, Writer=ascii.FixedWidth, bookend=False, delimiter=None) + >>> ascii.write(dat, format='fixed_width', bookend=False, delimiter=None) Col1 Col2 Col3 Col4 1.2 "hello" 1 a 2.4 's worlds 2 2 -**Write a table as a fixed width table with no delimiter and formatting.** +**Write a table as a fixed-width table with no delimiter and formatting:** :: - >>> ascii.write(dat, Writer=ascii.FixedWidth, + >>> ascii.write(dat, format='fixed_width', ... formats={'Col1': '%-8.3f', 'Col2': '%-15s'}) | Col1 | Col2 | Col3 | Col4 | | 1.200 | "hello" | 1 | a | | 2.400 | 's worlds | 2 | 2 | -FixedWidthNoHeader -"""""""""""""""""" +Fixed Width No Header +--------------------- -**Write a table as a normal fixed width table.** +**Write a table as a normal fixed-width table:** :: - >>> ascii.write(dat, Writer=ascii.FixedWidthNoHeader) + >>> ascii.write(dat, format='fixed_width_no_header') | 1.2 | "hello" | 1 | a | | 2.4 | 's worlds | 2 | 2 | -**Write a table as a fixed width table with no padding.** +**Write a table as a fixed-width table with no padding:** :: - >>> ascii.write(dat, Writer=ascii.FixedWidthNoHeader, delimiter_pad=None) + >>> ascii.write(dat, format='fixed_width_no_header', delimiter_pad=None) |1.2| "hello"|1|a| |2.4|'s worlds|2|2| -**Write a table as a fixed width table with no bookend.** +**Write a table as a fixed-width table with no bookend:** :: - >>> ascii.write(dat, Writer=ascii.FixedWidthNoHeader, bookend=False) + >>> ascii.write(dat, format='fixed_width_no_header', bookend=False) 1.2 | "hello" | 1 | a 2.4 | 's worlds | 2 | 2 -**Write a table as a fixed width table with no delimiter.** +**Write a table as a fixed-width table with no delimiter:** :: - >>> ascii.write(dat, Writer=ascii.FixedWidthNoHeader, bookend=False, + >>> ascii.write(dat, format='fixed_width_no_header', bookend=False, ... delimiter=None) 1.2 "hello" 1 a 2.4 's worlds 2 2 -FixedWidthTwoLine -""""""""""""""""" +Fixed Width Two Line +-------------------- -**Write a table as a normal fixed width table.** +**Write a table as a normal fixed-width table:** :: - >>> ascii.write(dat, Writer=ascii.FixedWidthTwoLine) + >>> ascii.write(dat, format='fixed_width_two_line') Col1 Col2 Col3 Col4 ---- --------- ---- ---- 1.2 "hello" 1 a 2.4 's worlds 2 2 -**Write a table as a fixed width table with space padding and '=' position_char.** +**Write a table as a fixed width table with space padding and '=' position_char:** :: - >>> ascii.write(dat, Writer=ascii.FixedWidthTwoLine, + >>> ascii.write(dat, format='fixed_width_two_line', ... delimiter_pad=' ', position_char='=') Col1 Col2 Col3 Col4 ==== ========= ==== ==== 1.2 "hello" 1 a 2.4 's worlds 2 2 -**Write a table as a fixed width table with no bookend.** +**Write a table as a fixed-width table with no bookend:** :: - >>> ascii.write(dat, Writer=ascii.FixedWidthTwoLine, bookend=True, delimiter='|') + >>> ascii.write(dat, format='fixed_width_two_line', bookend=True, delimiter='|') |Col1| Col2|Col3|Col4| |----|---------|----|----| | 1.2| "hello"| 1| a| | 2.4|'s worlds| 2| 2| + +.. + EXAMPLE END + +Custom Header Rows +================== + +The ``fixed_width``, ``fixed_width_two_line``, and ``rst`` formats normally include a +single row with the column names in the header. However, for these formats you can +customize the column attributes which appear as header rows. The available +column attributes are ``name``, ``dtype``, ``format``, ``description`` and +``unit``. This is done by listing the desired the header rows using the +``header_rows`` keyword argument. + +.. + EXAMPLE START + Custom Header Rows with Fixed Width + +:: + >>> from astropy.table.table_helpers import simple_table + >>> dat = simple_table(size=3, cols=4) + >>> dat["a"].info.unit = "m" + >>> dat["d"].info.unit = "m/s" + >>> dat["b"].info.format = ".2f" + >>> dat["c"].info.description = "C column" + >>> ascii.write( + ... dat, + ... format="fixed_width", + ... header_rows=["name", "unit", "format", "description"], + ... ) + | a | b | c | d | + | m | | | m / s | + | | .2f | | | + | | | C column | | + | 1 | 1.00 | c | 4 | + | 2 | 2.00 | d | 5 | + | 3 | 3.00 | e | 6 | + +In this example the 1st row is the ``name``, the 2nd row is the ``unit``, and +so forth. You must supply the ``name`` value in the ``header_rows`` list in +order to get an output with the column name included. + +A table with non-standard header rows can be read back in the same way, using +the same list of ``header_rows``:: + + >>> txt = """\ + ... | int32 | float32 | >> dat = ascii.read( + ... txt, + ... format="fixed_width", + ... header_rows=["dtype", "name", "unit", "format", "description"], + ... ) + >>> dat.info +
+ name dtype unit format description + ---- ------- ----- ------ ----------- + a int32 m + b float32 .2f + c str4 C column + d uint8 m / s + +.. + EXAMPLE END + +.. + EXAMPLE START + Custom Header Rows with Fixed Width Two Line + +The same idea can be used with the ``fixed_width_two_line`` format:: + + >>> txt = """\ + ... a b c d + ... int64 float64 >> dat = ascii.read( + ... txt, + ... format="fixed_width_two_line", + ... header_rows=["name", "dtype", "unit"], + ... ) + >>> dat +
+ a b c d + m m / s + int64 float64 str1 int64 + ----- ------- ---- ----- + 1 1.0 c 4 + 2 2.0 d 5 + 3 3.0 e 6 + +.. + EXAMPLE END + +Note that the ``two_line`` in the ``fixed_width_two_line`` format name refers to +the default situation where the header consists two lines, a row of column names +and a row of separator lines. This is a bit of a misnomer when using +``header_rows``. diff --git a/docs/io/ascii/index.rst b/docs/io/ascii/index.rst index 41abc6b5f36a..7a53459a7acd 100644 --- a/docs/io/ascii/index.rst +++ b/docs/io/ascii/index.rst @@ -1,32 +1,49 @@ .. include:: references.txt +.. _io-ascii: + ********************************* -ASCII Tables (`astropy.io.ascii`) +Text Tables (`astropy.io.ascii`) ********************************* -Introduction -============ +`astropy.io.ascii` provides methods for reading and writing a wide range of +text data table formats via built-in :ref:`extension_reader_classes`. The +emphasis is on flexibility and convenience of use, although readers can +optionally use a less flexible C-based engine for reading and writing for +improved performance. + +.. note:: + + It is strongly encouraged to use the :ref:`Unified I/O Text Tables + ` interface rather than using :mod:`astropy.io.ascii` directly. + + For reading large CSV files, the astropy :ref:`PyArrow CSV ` + reader is the fastest option, while for writing large data tables to CSV, the + :ref:`Table - Pandas interface ` is an option to consider. -`astropy.io.ascii` provides methods for reading and writing a wide range of ASCII data table -formats via built-in :ref:`extension_reader_classes`. The emphasis is on flexibility and ease of use. + Additional information is available in the :ref:`Unified I/O ` and + :ref:`Unified I/O Table Data ` pages. -The following formats are supported: +The following shows a few of the text formats that are available, while the +section on `Supported formats`_ contains the full list. -* :class:`~astropy.io.ascii.latex.AASTex`: AASTeX `deluxetable` used for AAS journals -* :class:`~astropy.io.ascii.basic.Basic`: basic table with customizable delimiters and header configurations -* :class:`~astropy.io.ascii.cds.Cds`: `CDS format table `_ (also Vizier and ApJ machine readable tables) -* :class:`~astropy.io.ascii.basic.CommentedHeader`: column names given in a line that begins with the comment character -* :class:`~astropy.io.ascii.daophot.Daophot`: table from the IRAF DAOphot package -* :class:`~astropy.io.ascii.fixedwidth.FixedWidth`: table with fixed-width columns (see also :ref:`fixed_width_gallery`) -* :class:`~astropy.io.ascii.ipac.Ipac`: `IPAC format table `_ -* :class:`~astropy.io.ascii.latex.Latex`: LaTeX table with datavalue in the `tabular` environment -* :class:`~astropy.io.ascii.basic.NoHeader`: basic table with no header where columns are auto-named -* :class:`~astropy.io.ascii.basic.Rdb`: tab-separated values with an extra line after the column definition line -* :class:`~astropy.io.ascii.basic.Tab`: tab-separated values +* :class:`~astropy.io.ascii.Basic`: basic table with customizable delimiters and header configurations +* :class:`~astropy.io.ascii.Cds`: `CDS format table `_ (also Vizier) +* :class:`~astropy.io.ascii.Daophot`: table from the IRAF DAOphot package +* :class:`~astropy.io.ascii.Ecsv`: :ref:`ecsv_format` for lossless round-trip of data tables (**recommended**) +* :class:`~astropy.io.ascii.FixedWidth`: table with fixed-width columns (see also :ref:`fixed_width_gallery`) +* :class:`~astropy.io.ascii.Ipac`: `IPAC format table `_ +* :class:`~astropy.io.ascii.HTML`: HTML format table contained in a
tag +* :class:`~astropy.io.ascii.Latex`: LaTeX table with datavalue in the ``tabular`` environment +* :class:`~astropy.io.ascii.Mesa`: MESA stellar evolution code output format -The :mod:`astropy.io.ascii` package is built on a modular and extensible class -structure with independent :ref:`base_class_elements` so that new formats can -be easily accomodated. +* :class:`~astropy.io.ascii.Mrt`: AAS `Machine-Readable Tables (MRT) `_) +* :class:`~astropy.io.ascii.SExtractor`: `SExtractor format table `_ + +The strength of `astropy.io.ascii` is the support for astronomy-specific +formats (often with metadata) and specialized data types such as +:ref:`SkyCoord `, :ref:`Time +`, and :ref:`Quantity `. Getting Started =============== @@ -34,52 +51,107 @@ Getting Started Reading Tables -------------- -The majority of commonly encountered ASCII tables can be easily read with the |read| -function. Assume you have a file named ``sources.dat`` with the following contents:: +The majority of commonly encountered text tables can be read with the +|read| function. Assume you have a file named ``sources.dat`` with the +following contents:: obsid redshift X Y object 3102 0.32 4167 4085 Q1250+568-A 877 0.22 4378 3892 "Source 82" -This table can be read with the following:: +.. testsetup:: + >>> from pathlib import Path + >>> from tempfile import TemporaryDirectory + >>> tempdir = TemporaryDirectory() + >>> datadir = Path(tempdir.name) + >>> (datadir / "sources.dat").write_text( + ... "obsid redshift X Y object\n" + ... "3102 0.32 4167 4085 Q1250+568-A\n" + ... "877 0.22 4378 3892 \"Source 82\"\n" + ... ) + 118 + +This table can be read with the following (assuming that the path to the data directory +is set like this: ``datadir=Path('path/to/my/data')``):: >>> from astropy.io import ascii - >>> data = ascii.read("sources.dat") - >>> print data + >>> data = ascii.read(datadir / "sources.dat") + >>> print(data) obsid redshift X Y object ----- -------- ---- ---- ----------- 3102 0.32 4167 4085 Q1250+568-A 877 0.22 4378 3892 Source 82 +.. testcleanup:: + + >>> tempdir.cleanup() + The first argument to the |read| function can be the name of a file, a string -representation of a table, or a list of table lines. By default |read| will -try to `guess the table format <#guess-table-format>`_ by trying all the -supported formats. If this does not work (for unusually formatted tables) then -one needs give astropy.io.ascii additional hints about the format, for -example:: +representation of a table, or a list of table lines. The return value +(``data`` in this case) is a :ref:`Table ` object. - >>> lines = ['objID & osrcid & xsrcid ', - '----------------------- & ----------------- & -------------', - ' 277955213 & S000.7044P00.7513 & XS04861B6_005', - ' 889974380 & S002.9051P14.7003 & XS03957B7_004'] - >>> data = ascii.read(lines, data_start=2, delimiter='&') - >>> print data - objID osrcid xsrcid - --------- ----------------- ------------- - 277955213 S000.7044P00.7513 XS04861B6_005 - 889974380 S002.9051P14.7003 XS03957B7_004 +By default, |read| will try to :ref:`guess the table format ` +by trying most of the `supported formats`_. + +.. Warning:: + + Guessing the file format might be convenient, but has two disadvantages: + + - It is often slow for large files because the reader + tries parsing the file with every allowed format until one succeeds. + - Tables sometimes match multiple formats and the first one that succeeds + might not be the one you expected + (:ref:`example `). + + Thus, it is recommended to disable guessing with ``guess=False`` and + explicitly give the table format (e.g. ``format='csv'``) whenever possible. + +If guessing the format does not work, as in the case for unusually formatted +tables, you may need to give `astropy.io.ascii` additional hints about +the format. + +To specify specific data types for one or more columns, use the ``converters`` +argument (see :ref:`io-ascii-read-converters` for details). For instance if the +``obsid`` is actually a string identifier (instead of an integer) you can read +the table with the code below. This also illustrates using the preferred +:ref:`Table interface ` for reading:: + + >>> from astropy.table import Table + >>> sources = """ + ... target observatory obsid + ... TW_Hya Chandra 22178 + ... MP_Mus XMM 0406030101""" + >>> data = Table.read(sources, format='ascii', converters={'obsid': str}) + >>> data +
+ target observatory obsid + str6 str7 str10 + ------ ----------- ---------- + TW_Hya Chandra 22178 + MP_Mus XMM 0406030101 Writing Tables -------------- -The |write| function provides a way to write a data table as a formatted ASCII -table. For example the following writes a table as a simple space-delimited -file:: +The |write| function provides a way to write a data table as a formatted text +table. Most of the input table :ref:`supported_formats` for reading are also +available for writing. This provides a great deal of flexibility in the format +for writing. + +.. + EXAMPLE START + Writing Data Tables as Formatted Text Tables + +The following shows how to write a formatted text table using the |write| +function:: - >>> x = np.array([1, 2, 3]) - >>> y = x ** 2 + >>> import numpy as np + >>> from astropy.io import ascii + >>> from astropy.table import Table >>> data = Table() - >>> ascii.write([x, y], names=['x', 'y'], 'values.dat') + >>> data['x'] = np.array([1, 2, 3], dtype=np.int32) + >>> data['y'] = data['x'] ** 2 + >>> ascii.write(data, 'values.dat', overwrite=True) The ``values.dat`` file will then contain:: @@ -88,39 +160,165 @@ The ``values.dat`` file will then contain:: 2 4 3 9 -All of the input Reader formats supported by `astropy.io.ascii` for reading are -also supported for writing. This provides a great deal of flexibility in the -format for writing. The example below writes the data as a LaTeX table, using -the option to send the output to ``sys.stdout`` instead of a file:: +It is also possible and encouraged to use the write functionality from +:mod:`astropy.io.ascii` through a higher level interface in the :ref:`Data +Tables ` package (see :ref:`table_io` for more details). For +example:: + + >>> data.write('values.dat', format='ascii', overwrite=True) + +.. attention:: **ECSV is recommended** + + For a reproducible text version of your table, we recommend using the + :ref:`ecsv_format`. This stores all the table meta-data (in particular the + column types and units) to a comment section at the beginning while + maintaining compatibility with most plain CSV readers. It also allows storing + richer data like `~astropy.coordinates.SkyCoord` or multidimensional or + variable-length columns. ECSV is also supported in Java by |STIL| and + |TOPCAT| (see :ref:`ecsv_format`). - >>> ascii.write(data, sys.stdout, Writer=ascii.Latex) - \begin{table} - \begin{tabular}{cc} - x & y \\ - 1 & 1 \\ - 2 & 4 \\ - 3 & 9 \\ - \end{tabular} - \end{table} +To write our simple example table to ECSV we use:: + + >>> data.write('values.ecsv', overwrite=True) # doctest: +SKIP + +The ``.ecsv`` extension is recognized and implies using ECSV (equivalent to +``format='ascii.ecsv'``). The ``values.ecsv`` file will then contain:: + + # %ECSV 1.0 + # --- + # datatype: + # - {name: x, datatype: int32} + # - {name: y, datatype: int32} + # schema: astropy-2.0 + x y + 1 1 + 2 4 + 3 9 + +.. + EXAMPLE END + +.. _supported_formats: + +Supported Formats +================= + +A full list of the supported ``format`` values and corresponding format types +for text tables is given below. The ``Write`` column indicates which formats +support write functionality, and the ``Fast`` column indicates which formats +are compatible with the fast Cython/C engine for reading and writing. + +========================= ===== ==== ============================================================================================ + Format Write Fast Description +========================= ===== ==== ============================================================================================ +``aastex`` Yes :class:`~astropy.io.ascii.AASTex`: AASTeX deluxetable used for AAS journals +``basic`` Yes Yes :class:`~astropy.io.ascii.Basic`: Basic table with custom delimiters +``cds`` Yes :class:`~astropy.io.ascii.Cds`: CDS format table +``commented_header`` Yes Yes :class:`~astropy.io.ascii.CommentedHeader`: Column names in a commented line +``csv`` Yes Yes :class:`~astropy.io.ascii.Csv`: Basic table with comma-separated values +``daophot`` :class:`~astropy.io.ascii.Daophot`: IRAF DAOphot format table +``ecsv`` Yes :class:`~astropy.io.ascii.Ecsv`: Enhanced CSV format (**recommended**) +``fixed_width`` Yes :class:`~astropy.io.ascii.FixedWidth`: Fixed width +``fixed_width_no_header`` Yes :class:`~astropy.io.ascii.FixedWidthNoHeader`: Fixed-width with no header +``fixed_width_two_line`` Yes :class:`~astropy.io.ascii.FixedWidthTwoLine`: Fixed-width with second header line +``html`` Yes :class:`~astropy.io.ascii.HTML`: HTML format table +``ipac`` Yes :class:`~astropy.io.ascii.Ipac`: IPAC format table +``latex`` Yes :class:`~astropy.io.ascii.Latex`: LaTeX table +``mesa`` No :class:`~astropy.io.ascii.Mesa`: MESA stellar evolution code format +``mrt`` Yes :class:`~astropy.io.ascii.Mrt`: AAS Machine-Readable Table format +``no_header`` Yes Yes :class:`~astropy.io.ascii.NoHeader`: Basic table with no headers +``qdp`` Yes :class:`~astropy.io.ascii.QDP`: Quick and Dandy Plotter files +``rdb`` Yes Yes :class:`~astropy.io.ascii.Rdb`: Tab-separated with a type definition header line +``rst`` Yes :class:`~astropy.io.ascii.RST`: reStructuredText simple format table +``sextractor`` :class:`~astropy.io.ascii.SExtractor`: SExtractor format table +``tab`` Yes Yes :class:`~astropy.io.ascii.Tab`: Basic table with tab-separated values +``tdat`` Yes :class:`~astropy.io.ascii.Tdat`: Transportable Database Aggregate Table format +========================= ===== ==== ============================================================================================ + +Getting Help +============ -Using `io.ascii` -================ +Some formats have additional options that can be set to control the behavior of the +reader or writer. For more information on these options, you can either see the +documentation for the specific format class (e.g. :class:`~astropy.io.ascii.HTML`) or +use the ``help`` function of the ``read`` or ``write`` functions. For example: + +.. doctest-skip:: + + >>> ascii.read.help() # Common help for all formats + >>> ascii.read.help("html") # Common help plus "html" format-specific args + >>> ascii.write.help("latex") # Common help plus "latex" format-specific args + +Using `astropy.io.ascii` +======================== The details of using `astropy.io.ascii` are provided in the following sections: +Reading tables +--------------- + .. toctree:: :maxdepth: 2 read + +Writing tables +--------------- + +.. toctree:: + :maxdepth: 2 + write + +ECSV Format +----------- + +.. toctree:: + :maxdepth: 2 + + ecsv + +Fixed-Width Gallery +-------------------- + +.. toctree:: + :maxdepth: 2 + fixed_width_gallery + +Fast ASCII Engine +----------------- + +.. toctree:: + :maxdepth: 2 + + fast_ascii_io + +Base Class Elements +------------------- + +.. toctree:: + :maxdepth: 2 + base_classes + +Extension Reader Classes +------------------------ + +.. toctree:: + :maxdepth: 2 + extension_classes +.. note that if this section gets too long, it should be moved to a separate + doc page - see the top of performance.inc.rst for the instructions on how to do + that +.. include:: performance.inc.rst Reference/API ============= -.. automodapi:: astropy.io.ascii - +.. toctree:: + :maxdepth: 2 + ref_api diff --git a/docs/io/ascii/performance.inc.rst b/docs/io/ascii/performance.inc.rst new file mode 100644 index 000000000000..5a74658966ab --- /dev/null +++ b/docs/io/ascii/performance.inc.rst @@ -0,0 +1,33 @@ +.. note that if this is changed from the default approach of using an *include* + (in index.rst) to a separate performance page, the header needs to be changed + from === to ***, the filename extension needs to be changed from .inc.rst to + .rst, and a link needs to be added in the subpackage toctree + +.. _astropy-io-ascii-performance: + +Performance Tips +================ + +By default, when trying to read a file the reader will guess the format, which +involves trying to read it with many different readers. For better performance +when dealing with large tables, it is recommended to specify the format and any +options explicitly, and turn off guessing as well. + +Example +------- + +.. + EXAMPLE START + Performance Tips for Reading Large Tables with astropy.io.ascii + +If you are reading a simple CSV file with a one-line header with column names, +the following:: + + read('example.csv', format='basic', delimiter=',', guess=False) # doctest: +SKIP + +can be at least an order of magnitude faster than:: + + read('example.csv') # doctest: +SKIP + +.. + EXAMPLE END diff --git a/docs/io/ascii/read.rst b/docs/io/ascii/read.rst index 3af58b724ca7..47fad0b253ba 100644 --- a/docs/io/ascii/read.rst +++ b/docs/io/ascii/read.rst @@ -1,238 +1,492 @@ -.. _astropy.io.ascii_read: - .. include:: references.txt -Reading tables --------------- +.. _astropy.io.ascii_read: -The majority of commonly encountered ASCII tables can be easily read with the |read| +Reading Tables +************** + +The majority of commonly encountered text tables can be read with the |read| function:: >>> from astropy.io import ascii - >>> data = ascii.read(table) + >>> data = ascii.read(table) # doctest: +SKIP + +Here ``table`` is the name of a file, a string representation of a table, or a +list of table lines. The return value (``data`` in this case) is a :ref:`Table +` object. + +Help on the ``read()`` function arguments is available interactively as shown in +this example: + +.. doctest-skip:: + + >>> ascii.read.help() # Common help for all formats + >>> ascii.read.help("html") # Common help plus "html" format-specific args + +By default, |read| will try to `guess the table format <#guess-table-format>`_ +by trying all of the supported formats. + +.. Warning:: + + Guessing the file format is often slow for large files because the reader + tries parsing the file with every allowed format until one succeeds. + For large files it is recommended to disable guessing with ``guess=False``. + +.. + EXAMPLE START + Reading Text Tables Using astropy.io.ascii + +For unusually formatted tables where guessing does not work, give additional +hints about the format:: -where ``table`` is the name of a file, a string representation of a table, or a -list of table lines. By default |read| will try to `guess the table format <#guess-table-format>`_ -by trying all the supported formats. If this does not work (for unusually -formatted tables) then one needs give `astropy.io.ascii` additional hints about the -format, for example:: + >>> lines = ['objID & osrcid & xsrcid ', + ... '----------------------- & ----------------- & -------------', + ... ' 277955213 & S000.7044P00.7513 & XS04861B6_005', + ... ' 889974380 & S002.9051P14.7003 & XS03957B7_004'] + >>> data = ascii.read(lines, data_start=2, delimiter='&') + >>> print(data) + objID osrcid xsrcid + --------- ----------------- ------------- + 277955213 S000.7044P00.7513 XS04861B6_005 + 889974380 S002.9051P14.7003 XS03957B7_004 - >>> data = astropy.io.ascii.read('t/nls1_stackinfo.dbout', data_start=2, delimiter='|') - >>> data = astropy.io.ascii.read('t/simple.txt', quotechar="'") - >>> data = astropy.io.ascii.read('t/simple4.txt', Reader=ascii.NoHeader, delimiter='|') +Other examples are as follows:: + + >>> data = astropy.io.ascii.read('data/nls1_stackinfo.dbout', data_start=2, delimiter='|') # doctest: +SKIP + >>> data = astropy.io.ascii.read('data/simple.txt', quotechar="'") # doctest: +SKIP + >>> data = astropy.io.ascii.read('data/simple4.txt', format='no_header', delimiter='|') # doctest: +SKIP + >>> data = astropy.io.ascii.read('data/tab_and_space.txt', delimiter=r'\s') # doctest: +SKIP + +If the format of a file is known (e.g., it is a fixed-width table or an IPAC +table), then it is more efficient and reliable to provide a value for the +``format`` argument from one of the values in the :ref:`supported_formats`. For +example:: + + >>> data = ascii.read(lines, format='fixed_width_two_line', delimiter='&') + +See the :ref:`guess_formats` section for additional details on format guessing. + +.. + EXAMPLE END + +For simpler formats such as CSV, |read| will automatically try reading with the +Cython/C parsing engine, which is significantly faster than the ordinary Python +implementation (described in :ref:`fast_ascii_io`). If the fast engine fails, +|read| will fall back on the Python reader by default. The argument +``fast_reader`` can be specified to control this behavior. For example, to +disable the fast engine:: + + >>> data = ascii.read(lines, format='csv', fast_reader=False) + +For reading very large tables see the section on :ref:`chunk_reading` or +use `pandas `_ (see Note below). + +.. Note:: + + Reading a table which contains unicode characters is supported with the + pure Python readers by specifying the ``encoding`` parameter. The fast + C-readers do not support unicode. For large data files containing unicode, + we recommend reading the file using `pandas `_ + and converting to a :ref:`Table ` via the :ref:`Table - + Pandas interface `. The |read| function accepts a number of parameters that specify the detailed -table format. Different Reader classes can define different defaults, so the -descriptions below sometimes mention "typical" default values. This refers to -the :class:`~astropy.io.ascii.Basic` reader and other similar Reader classes. +table format. Different formats can define different defaults, so the +descriptions below sometimes mention "typical" default values. This refers to +the :class:`~astropy.io.ascii.Basic` format reader and other similar character-separated formats. + +.. _io_ascii_read_parameters: Parameters for ``read()`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +========================= -**table** : input table +**table** : input table There are four ways to specify the table to be read: - - Name of a file (string) + - Path to a file (string) - Single string containing all table lines separated by newlines - File-like object with a callable read() method - List of strings where each list element is a table line - The first two options are distinguished by the presence of a newline in the string. - This assumes that valid file names will not normally contain a newline. + The first two options are distinguished by the presence of a newline in the + string. This assumes that valid file names will not normally contain a + newline, and a valid table input will at least contain two rows. + Note that a table read in ``no_header`` format can legitimately consist + of a single row; in this case passing the string as a list with a single + item will ensure that it is not interpreted as a file name. -**Reader** : Reader class (default= :class:`~astropy.io.ascii.Basic`) - This specifies the top-level format of the ASCII table, for example +**format** : file format (default='basic') + This specifies the top-level format of the text table; for example, if it is a basic character delimited table, fixed format table, or - a CDS-compatible table, etc. The value of this parameter must - be a Reader class. For basic usage this means one of the - built-in :ref:`extension_reader_classes`. - -**guess**: try to guess table format (default=True) - If set to True then |read| will try to guess the table format by cycling - through a number of possible table format permuations and attemping to read - the table in each case. See the `Guess table format`_ section for further details. - + a CDS-compatible table, etc. The value of this parameter must + be one of the :ref:`supported_formats`. + +**guess** : try to guess table format (default=None) + If set to True, then |read| will try to guess the table format by cycling + through a number of possible table format permutations and attempting to read + the table in each case. See the `Guess table format`_ section for further details. + **delimiter** : column delimiter string A one-character string used to separate fields which typically defaults to - the space character. Other common values might be "\\s" (whitespace), "," or - "|" or "\\t" (tab). A value of "\\s" allows any combination of the tab and + the space character. Other common values might be "\\s" (whitespace), "," or + "|" or "\\t" (tab). A value of "\\s" allows any combination of the tab and space characters to delimit columns. **comment** : regular expression defining a comment line in table - If the ``comment`` regular expression matches the beginning of a table line then that line - will be discarded from header or data processing. For the :class:`~astropy.io.ascii.Basic` Reader this - defaults to "\\s*#" (any whitespace followed by #). + If the ``comment`` regular expression matches the beginning of a table line + then that line will be discarded from header or data processing. For the + ``basic`` format this defaults to "\\s*#" (any whitespace followed by #). **quotechar** : one-character string to quote fields containing special characters - This specifies the quote character and will typically be either the single or double - quote character. This is can be useful for reading text fields with spaces in a space-delimited - table. The default is typically the double quote. - -**header_start** : line index for the header line not counting comment lines - This specifies in the line index where the header line will be found. Comment lines are - not included in this count and the counting starts from 0 (first non-comment line has index=0). - If set to None this indicates that there is no header line and the column names - will be auto-generated. The default is dependent on the Reader. - -**data_start**: line index for the start of data not counting comment lines - This specifies in the line index where the data lines begin where the counting starts - from 0 and does not include comment lines. The default is dependent on the Reader. - -**data_end**: line index for the end of data (can be negative to count from end) - If this is not None then it allows for excluding lines at the end that are not - valid data lines. A negative value means to count from the end, so -1 would - exclude the last line, -2 the last two lines, and so on. - -**converters**: dict of data type converters - See the `Converters`_ section for more information. - -**names**: list of names corresponding to each data column - Define the complete list of names for each data column. This will override - names found in the header (if it exists). If not supplied then + This specifies the quote character and will typically be either the single or + double quote character. This is can be useful for reading text fields with + spaces in a space-delimited table. The default is typically the double quote. + +**header_start** : line index for the header line + This includes only significant non-comment lines and counting starts at 0. If + set to None this indicates that there is no header line and the column names + will be auto-generated. See `Specifying header and data location`_ for more + details. + +**data_start** : line index for the start of data counting + This includes only significant non-comment lines and counting starts at 0. + See `Specifying header and data location`_ for more details. + +**data_end** : line index for the end of data + This includes only significant non-comment lines and can be negative to count + from end. See `Specifying header and data location`_ for more details. + +**encoding**: encoding to read the file (``default=None``) + When `None` use `locale.getpreferredencoding` as an encoding. This matches + the default behavior of the built-in `open` when no ``mode`` argument is + provided. + +**converters** : ``dict`` specifying output data types + See the :ref:`io-ascii-read-converters` section for examples. Each key in the + dictionary is a column name or else a name matching pattern including + wildcards. The value is one of: + + - Python data type or numpy dtype such as ``int`` or ``np.float32`` + - list of such types which is tried in order until conversion is successful + - list of converter tuples (this is not common, but see the + `~astropy.io.ascii.convert_numpy` function for an example). + +**names** : list of names corresponding to each data column + Define the complete list of names for each data column. This will override + names found in the header (if it exists). If not supplied then use names from the header or auto-generated names if there is no header. -**include_names**: list of names to include in output +**include_names** : list of names to include in output From the list of column names found from the header or the ``names`` - parameter, select for output only columns within this list. If not supplied + parameter, select for output only columns within this list. If not supplied, then include all names. - -**exclude_names**: list of names to exlude from output - Exclude these names from the list of output columns. This is applied *after* - the ``include_names`` filtering. If not specified then no columns are excluded. -**fill_values**: fill value specifier of lists - This can be used to fill missing values in the table or replace strings with special meaning. - See the `Replace bad or missing values`_ section for more information and examples. +**exclude_names** : list of names to exclude from output + Exclude these names from the list of output columns. This is applied *after* + the ``include_names`` filtering. If not specified then no columns are excluded. -**fill_include_names**: list of column names, which are affected by ``fill_values``. - If not supplied, then ``fill_values`` can affect all columns. +**fill_values** : list of fill value specifiers + Specify input table entries which should be masked in the output table + because they are bad or missing. See the `Bad or missing values`_ section + for more information and examples. The default is that any blank table + values are treated as missing. -**fill_exclude_names**: list of column names, which are not affected by ``fill_values``. - If not supplied, then ``fill_values`` can affect all columns. +**fill_include_names** : list of column names affected by ``fill_values`` + This is a list of column names (found from the header or the ``names`` + parameter) for all columns where values will be filled. `None` (the default) will + apply ``fill_values`` to all columns. -**Outputter**: Outputter class +**fill_exclude_names** : list of column names not affected by ``fill_values`` + This is a list of column names (found from the header or the ``names`` + parameter) for all columns where values will be **not** be filled. + This parameter takes precedence over ``fill_include_names``. A value + of `None` (default) does not exclude any columns. + +**fast_reader** : whether to use the C engine + This can be ``True`` or ``False``, and also be a ``dict`` with options. + (see :ref:`fast_ascii_io`) + +**outputter_cls** : Outputter class This converts the raw data tables value into the - output object that gets returned by |read|. The default is - :class:`~astropy.io.ascii.core.TableOutputter`, which returns a - :class:`~astropy.table.Table` object. The other option is - :class:`~astropy.io.ascii.core.NumpyOutputter` which returns a `numpy` record - array. If ``fill_values`` are specified then - :class:`~astropy.io.ascii.core.NumpyOutputter` is used and a masked array - is returned. - -**Inputter**: Inputter class - This is generally not specified. + output object that gets returned by |read|. The default is + :class:`~astropy.io.ascii.TableOutputter`, which returns a + :class:`~astropy.table.Table` object (see :ref:`Data Tables `). -**data_Splitter**: Splitter class to split data columns +**inputter_cls** : Inputter class + This is generally not specified. -**header_Splitter**: Splitter class to split header columns +**data_splitter_cls** : Splitter class to split data columns + +**header_splitter_cls** : Splitter class to split header columns + +Specifying Header and Data Location +=================================== + +The three parameters ``header_start``, ``data_start``, and ``data_end`` make it +possible to read a table file that has extraneous non-table data included. +This is a case where you need to help out `astropy.io.ascii` and tell it where +to find the header and data. + +When a file is processed into a header and data components, any blank lines +(which might have whitespace characters) and commented lines (starting with the +comment character, typically ``#``) are stripped out *before* the header and +data parsing code sees the table content. + +Example +------- + +.. + EXAMPLE START + Specifying Header and Data Location for Text Tables + +To use the parameters ``header_start``, ``data_start``, and ``data_end`` +to read a table with non-table data included, take the file below. The column +on the left is not part of the file but instead shows how `astropy.io.ascii` is +viewing each line and the line count index. :: + + Index Table content + ------ ---------------------------------------------------------------- + - | # This is the start of my data file + - | + 0 | Automatically generated by my_script.py at 2012-01-01T12:13:14 + 1 | Run parameters: None + 2 | Column header line: + - | + 3 | x y z + - | + 4 | Data values section: + - | + 5 | 1 2 3 + 6 | 4 5 6 + - | + 7 | Run completed at 2012:01-01T12:14:01 + +In this case you would have ``header_start=3``, ``data_start=5``, and +``data_end=7``. The convention for ``data_end`` follows the normal Python +slicing convention where to select data rows 5 and 6 you would do +``rows[5:7]``. For ``data_end`` you can also supply a negative index to +count backward from the end, so ``data_end=-1`` (like ``rows[5:-1]``) would +work in this case. + +.. + EXAMPLE END .. _replace_bad_or_missing_values: -Replace bad or missing values -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -:mod:`astropy.io.ascii` can replace string values in the input data before they are -converted. The most common use case is probably a table which contains string -values that are not a valid representation of a number, e.g. ``"..."`` for a -missing value or ``""``. If :mod:`astropy.io.ascii` cannot convert all elements in a -column to a numeric type, it will format the column as strings. To avoid this, -``fill_values`` can be used at the string level to fill missing values with the -following syntax, which replaces ```` with ```` before the type -conversion is done:: - - fill_values = | [, , ...] - = (, , , , ...) - -Within the ```` tuple the ```` and ```` values must be -strings. These two values are then followed by zero or more column names. If -column names are included the replacement is limited to those columns listed. -If no columns are specified then the replacement is done in every column, -subject to filtering by ``fill_include_names`` and ``fill_exclude_names`` (see -below). - -The ``fill_values`` parameter in |read| takes a single ```` or a -list of ```` tuples. If several ```` apply to a single -occurence of ```` then the first one determines the ```` value. For -instance the following will replace an empty data value in the ``x`` or ``y`` -columns with "1e38" while empty values in any other column will get "-999":: - - >>> ascii.read(table, fill_values=[('', '1e38', 'x', 'y'), ('', '-999')]) - -The following shows an example where string information needs to be exchanged -before the conversion to float values happens. Here ``no_rain`` and ``no_snow`` is replaced by ``0.0``:: - - >>> table = ['day rain snow', # column names - #--- ------- -------- - 'Mon 3.2 no_snow', - 'Tue no_rain 1.1', - 'Wed 0.3 no_snow'] - >>> print ascii.read(table, fill_values=[('no_rain', '0.0'), ('no_snow', '0.0')]) - [('Mon', 3.2, --) ('Tue', --, 1.1) ('Wed', 0.3, --)] - -Sometimes these rules apply only to specific columns in the table. Columns can be selected with -``fill_include_names`` or excluded with ``fill_exclude_names``. Also, column names can be -given directly with fill_values:: - - >>> asciidata = ['text,no1,no2', 'text1,1,1.',',2,'] - >>> print ascii.read(asciidata, fill_values = ('', 'nan','no1','no2'), delimiter = ',') - [('text1', 1, 1.0) ('', 2, --)] - -Here, the empty value ``''`` in column ``no2`` is replaced by ``nan``, but the ``text`` -column remains unaltered. - -When ``fill_values`` is specified then |read| returns a `NumPy masked array -`_ instead of the -default `~astropy.table.Table` object. See the description of the -:class:`~astropy.io.ascii.core.NumpyOutputter` class for information on disabling -masked arrays. - -Guess table format -^^^^^^^^^^^^^^^^^^^^^^ -If the ``guess`` parameter in |read| is set to True (which is the default) then +Bad or Missing Values +===================== + +text data tables can contain bad or missing values. A common case is when a +table contains blank entries with no available data. + +Examples +-------- + +.. + EXAMPLE START + Text Tables with Bad or Missing Values + +Take this example of a table with blank entries:: + + >>> weather_data = """ + ... day,precip,type + ... Mon,1.5,rain + ... Tues,, + ... Wed,1.1,snow + ... """ + +By default, |read| will interpret blank entries as being bad/missing and output +a masked Table with those entries masked out by setting the corresponding mask +value set to ``True``:: + + >>> dat = ascii.read(weather_data) + >>> print(dat) + day precip type + ---- ------ ---- + Mon 1.5 rain + Tues -- -- + Wed 1.1 snow + +If you want to replace the masked (missing) values with particular values, set +the masked column ``fill_value`` attribute and then get the "filled" version of +the table. This looks like the following:: + + >>> dat['precip'].fill_value = -999 + >>> dat['type'].fill_value = 'N/A' + >>> print(dat.filled()) + day precip type + ---- ------ ---- + Mon 1.5 rain + Tues -999.0 N/A + Wed 1.1 snow + +Text tables may have other indicators of bad or missing data as well. For +example, a table may contain string values that are not a valid representation +of a number (e.g., ``"..."``), or a table may have special values like ``-999`` +that are chosen to indicate missing data. The |read| function has a flexible +system to accommodate these cases by marking specified character sequences in +the input data as "missing data" during the conversion process. Whenever +missing data is found the output will be a masked table. + +This is done with the ``fill_values`` keyword argument, which can be set to a +single missing-value specification ```` or a list of ```` tuples:: + + fill_values = | [, , ...] + = (, '0', , , ...) + +When reading a table, the second element of a ```` should always +be the string ``'0'``, otherwise you may get unexpected behavior [#f1]_. By +default, the ```` is applied to all columns unless column name +strings are supplied. An alternate way to limit the columns is via the +``fill_include_names`` and ``fill_exclude_names`` keyword arguments in |read|. + +In the example below we read back the weather table after filling the missing +values in with typical placeholders:: + + >>> table = ['day precip type', + ... ' Mon 1.5 rain', + ... 'Tues -999.0 N/A', + ... ' Wed 1.1 snow'] + >>> t = ascii.read(table, fill_values=[('-999.0', '0', 'precip'), ('N/A', '0', 'type')]) + >>> print(t) + day precip type + ---- ------ ---- + Mon 1.5 rain + Tues -- -- + Wed 1.1 snow + +.. note:: + + The default in |read| is ``fill_values=('','0')``. This marks blank entries as being + missing for any data type (int, float, or string). If ``fill_values`` is explicitly + set in the call to |read| then the default behavior of marking blank entries as missing + no longer applies. For instance setting ``fill_values=None`` will disable this + auto-masking without setting any other fill values. This can be useful for a string + column where one of values happens to be ``""``. + + +.. [#f1] The requirement to put the ``'0'`` there is the legacy of an old + interface which is maintained for backward compatibility and also to + match the format of ``fill_value`` for reading with the format of + ``fill_value`` used for writing tables. On reading, the second + element of the ```` tuple can actually be an arbitrary + string value which replaces occurrences of the ```` + string in the input stream prior to type conversion. This ends up + being the value "behind the mask", which should never be directly + accessed. Only the value ``'0'`` is neutral when attempting to detect + the column data type and perform type conversion. For instance if you + used ``'nan'`` for the ```` value then integer columns + would wind up as float. + +.. + EXAMPLE END + +Selecting columns for masking +----------------------------- +The |read| function provides the parameters ``fill_include_names`` and ``fill_exclude_names`` +to select which columns will be used in the ``fill_values`` masking process described above. + +.. + EXAMPLE START + Using the ``fill_include_names`` and ``fill_exclude_names`` parameters for Text tables + +The use of these parameters is not common but in some cases can considerably simplify +the code required to read a table. The following gives a simple example to illustrate how +``fill_include_names`` and ``fill_exclude_names`` can be used +in the most basic and typical cases:: + + >>> from astropy.io import ascii + >>> lines = ['a,b,c,d', '1.0,2.0,3.0,4.0', ',,,'] + >>> ascii.read(lines) +
+ a b c d + float64 float64 float64 float64 + ------- ------- ------- ------- + 1.0 2.0 3.0 4.0 + -- -- -- -- + + >>> ascii.read(lines, fill_include_names=['a', 'c']) +
+ a b c d + float64 str3 float64 str3 + ------- ---- ------- ---- + 1.0 2.0 3.0 4.0 + -- -- + + >>> ascii.read(lines, fill_exclude_names=['a', 'c']) +
+ a b c d + str3 float64 str3 float64 + ---- ------- ---- ------- + 1.0 2.0 3.0 4.0 + -- -- + +.. + EXAMPLE END + +.. _guess_formats: + +Guess Table Format +================== + +If the ``guess`` parameter in |read| is set to True, then |read| will try to guess the table format by cycling through a number of -possible table format permutations and attemping to read the table in each case. -The first format which succeeds and will be used to read the table. To succeed -the table must be successfully parsed by the Reader and satisfy the following -column requirements: +possible table format permutations and attempting to read the table in each +case. The first format which succeeds will be used to read the table. To +succeed, the table must be successfully parsed by the Reader and satisfy the +following column requirements: - * At least two table columns - * No column names are a float or int number - * No column names begin or end with space, comma, tab, single quote, double quote, or - a vertical bar (|). + * At least two table columns. + * No column names are a float or int number. + * No column names begin or end with space, comma, tab, single quote, double + quote, or a vertical bar (|). These requirements reduce the chance for a false positive where a table is -successfully parsed with the wrong format. A common situation is a table -with numeric columns but no header row, and in this case ``astropy.io.ascii`` will -auto-assign column names because of the restriction on column names that +successfully parsed with the wrong format. A common situation is a table +with numeric columns but no header row, and in this case `astropy.io.ascii` will +auto-assign column names because of the restriction on column names that look like a number. +Guess Order +----------- + The order of guessing is shown by this Python code:: - - for Reader in (Rdb, Tab, Cds, Daophot, Ipac): - read(Reader=Reader) - for Reader in (CommentedHeader, Basic, NoHeader): + + for format in ("ecsv", "fixed_width_two_line", "rst", "fast_basic", "basic", + "fast_rdb", "rdb", "fast_tab", "tab", "cds", "daophot", "sextractor", + "ipac", "latex", "aastex"): + read(format=format) + + for format in ("commented_header", "fast_basic", "basic", "fast_noheader", "noheader"): for delimiter in ("|", ",", " ", "\\s"): for quotechar in ('"', "'"): - read(Reader=Reader, delimiter=delimiter, quotechar=quotechar) + read(format=format, delimiter=delimiter, quotechar=quotechar) -Note that the :class:`~astropy.io.ascii.FixedWidth` derived-readers are not included -in the default guess sequence (this causes problems), so to read such tables -one must explicitly specify the reader class with the ``Reader`` keyword. +Note that the :class:`~astropy.io.ascii.FixedWidth` derived-readers are not +included in the default guess sequence (this causes problems), so to read such +tables you must explicitly specify the format with the ``format`` keyword. Also +notice that formats compatible with the fast reading engine attempt to use the +fast engine before the ordinary reading engine. If none of the guesses succeed in reading the table (subject to the column -requirements) a final try is made using just the user-supplied parameters but -without checking the column requirements. In this way a table with only one +requirements), a final try is made using just the user-supplied parameters but +without checking the column requirements. In this way, a table with only one column or column names that look like a number can still be successfully read. -The guessing process respects any values of the Reader, delimiter, and -quotechar parameters that were supplied to the read() function. Any guesses -that would conflict are skipped. For example the call:: +The guessing process respects any values of the format, delimiter, and +quotechar parameters as well as options for the fast reader that were +supplied to the read() function. Any guesses that would conflict are +skipped. For example, the call:: - >>> data = astropy.io.ascii.read(table, Reader=NoHeader, quotechar="'") + >>> data = ascii.read(table, format="no_header", quotechar="'") would only try the four delimiter possibilities, skipping all the conflicting -Reader and quotechar combinations. +format and quotechar combinations. Similarly, with any setting of +``fast_reader`` that requires use of the fast engine, only the fast +variants in the format list above will be tried. + +Disabling +--------- Guessing can be disabled in two ways:: @@ -241,91 +495,689 @@ Guessing can be disabled in two ways:: data = astropy.io.ascii.read(table, guess=False) # disable for this call astropy.io.ascii.set_guess(False) # set default to False globally data = astropy.io.ascii.read(table) # guessing disabled - -Converters -^^^^^^^^^^^^^^ + +Debugging +--------- + +In order to get more insight into the guessing process and possibly debug if +something is not working as expected, use the +`~astropy.io.ascii.get_read_trace()` function. This returns a traceback of the +attempted read formats for the last call to `~astropy.io.ascii.read()`. + +Comments and Metadata +===================== + +Any comment lines detected during reading are inserted into the output table +via the ``comments`` key in the table's ``.meta`` dictionary. + +Example +------- + +.. + EXAMPLE START + Comments and Metadata in Text Tables + +Comment lines detected during reading are inserted into the output table as +such:: + + >>> table='''# TELESCOPE = 30 inch + ... # TARGET = PV Ceph + ... # BAND = V + ... MJD mag + ... 55555 12.3 + ... 55556 12.4''' + >>> dat = ascii.read(table) + >>> print(dat.meta['comments']) + ['TELESCOPE = 30 inch', 'TARGET = PV Ceph', 'BAND = V'] + +While :mod:`astropy.io.ascii` will not do any post-processing on comment lines, +custom post-processing can be accomplished by rereading with the metadata line +comments. Here is one example, where comments are of the form "# KEY = VALUE":: + + >>> header = ascii.read(dat.meta['comments'], delimiter='=', + ... format='no_header', names=['key', 'val']) + >>> print(header) + key val + --------- ------- + TELESCOPE 30 inch + TARGET PV Ceph + BAND V + +.. + EXAMPLE END + +.. _io-ascii-read-converters: + +Converters for Specifying Dtype +=============================== :mod:`astropy.io.ascii` converts the raw string values from the table into numeric data types by using converter functions such as the Python ``int`` and -``float`` functions. For example ``int("5.0")`` will fail while float("5.0") -will succeed and return 5.0 as a Python float. +``float`` functions or numpy dtype types such as ``np.float64``. The default converters are:: - default_converters = [astropy.io.ascii.convert_numpy(numpy.int), - astropy.io.ascii.convert_numpy(numpy.float), - astropy.io.ascii.convert_numpy(numpy.str)] - -These take advantage of the :func:`~astropy.io.ascii.core.convert_numpy` -function which returns a 2-element tuple ``(converter_func, converter_type)`` -as described in the previous section. The type provided to -:func:`~astropy.io.ascii.core.convert_numpy` must be a valid `numpy type -`_, for example -``numpy.int``, ``numpy.uint``, ``numpy.int8``, ``numpy.int64``, -``numpy.float``, ``numpy.float64``, ``numpy.str``. + default_converters = [int, float, str] The default converters for each column can be overridden with the ``converters`` keyword:: - >>> converters = {'col1': [astropy.io.ascii.convert_numpy(numpy.uint)], - 'col2': [astropy.io.ascii.convert_numpy(numpy.float32)]} - >>> ascii.read('file.dat', converters=converters) + >>> import numpy as np + >>> converters = {'col1': np.uint, + ... 'col2': np.float32} + >>> ascii.read('file.dat', converters=converters) # doctest: +SKIP -Advanced customization -^^^^^^^^^^^^^^^^^^^^^^^^^^^ +In addition to single column names you can use wildcards via `fnmatch` to +select multiple columns. For example, we can set the format for all columns +with a name starting with "col" to an unsigned integer while applying default +converters to all other columns in the table:: + + >>> import numpy as np + >>> converters = {'col*': np.uint} + >>> ascii.read('file.dat', converters=converters) # doctest: +SKIP + +.. + EXAMPLE START + Reading True / False values as boolean type + +The value in the converters ``dict`` can also be a list of types, in which case +these will be tried in order. This allows for flexible type conversions. For +example, imagine you get read the following table:: + + >>> txt = """\ + ... a b c + ... --- --- ----- + ... 1 3.5 True + ... 2 4.0 False""" + >>> t = ascii.read(txt, format='fixed_width_two_line') + +By default the ``True`` and ``False`` values will be interpreted as strings. +However, if you want those values to be read as booleans you can do the +following:: + + >>> converters = {'*': [int, float, bool, str]} + >>> t = ascii.read(txt, format='fixed_width_two_line', converters=converters) + >>> print(t['c'].dtype) + bool + +.. + EXAMPLE END + +Advanced usage +-------------- + +Internally type conversion uses the :func:`~astropy.io.ascii.convert_numpy` +function which returns a two-element tuple ``(converter_func, converter_type)``. +This two-element tuple can be used as the value in a ``converters`` dict. +The type provided to +:func:`~astropy.io.ascii.convert_numpy` must be a valid `NumPy type +`_ such as +``numpy.int``, ``numpy.uint``, ``numpy.int8``, ``numpy.int64``, +``numpy.float``, ``numpy.float64``, or ``numpy.str``. + +It is also possible to directly pass an arbitrary conversion function as the +``converter_func`` element of the two-element tuple. + +.. _fortran_style_exponents: + +Fortran-Style Exponents +======================= + +The :ref:`fast converter ` available with the C +input parser provides an ``exponent_style`` option to define a custom +character instead of the standard ``'e'`` for exponential formats in +the input file, to read, for example, Fortran-style double precision +numbers like ``'1.495978707D+13'``: + + >>> ascii.read('double.dat', format='basic', guess=False, + ... fast_reader={'exponent_style': 'D'}) # doctest: +SKIP + +The special setting ``'fortran'`` is provided to allow for the +auto-detection of any valid Fortran exponent character (``'E'``, +``'D'``, ``'Q'``), as well as of triple-digit exponents prefixed with no +character at all (e.g., ``'2.1127123261674622-107'``). +All values and exponent characters in the input data are +case-insensitive; any value other than the default ``'E'`` implies the +automatic setting of ``'use_fast_converter': True``. + +.. _advanced_customization: + +Advanced Customization +====================== Here we provide a few examples that demonstrate how to extend the base -functionality to handle special cases. To go beyond these simple examples the +functionality to handle special cases. To go beyond these examples, the best reference is to read the code for the existing :ref:`extension_reader_classes`. -**Define a custom reader functionally** -:: +Examples +-------- - def read_rdb_table(table): - reader = astropy.io.ascii.Basic() - reader.header.splitter.delimiter = '\t' - reader.data.splitter.delimiter = '\t' - reader.header.splitter.process_line = None - reader.data.splitter.process_line = None - reader.data.start_line = 2 +.. + EXAMPLE START + Advanced Customization to Extend Base Functionality of astropy.io.ascii - return reader.read(table) +For special cases, these examples demonstrate how to extend the base +functionality of `astropy.io.ascii`. **Define custom readers by class inheritance** -:: - # Note: Tab and Rdb are already included in astropy.io.ascii for convenience. - class Tab(astropy.io.ascii.Basic): - def __init__(self): - astropy.io.ascii.Basic.__init__(self) - self.header.splitter.delimiter = '\t' - self.data.splitter.delimiter = '\t' - # Don't strip line whitespace since that includes tabs - self.header.splitter.process_line = None - self.data.splitter.process_line = None - # Don't strip data value spaces since that is significant in TSV tables - self.data.splitter.process_val = None - self.data.splitter.skipinitialspace = False - - class Rdb(astropy.io.ascii.Tab): - def __init__(self): - astropy.io.ascii.Tab.__init__(self) - self.data.start_line = 2 +The most useful way to define a new reader class is by inheritance. +This is the way all of the built-in readers are defined, so there are plenty +of examples in the code. + +In most cases, you will define one class to handle the header, +one class that handles the data, and a reader class that ties it all together. +Here is an example from the code that defines a reader that is just like +the basic reader, but header and data start in different lines of the file:: + + >>> # Note: NoHeader is already included in astropy.io.ascii for convenience. + >>> from astropy.io.ascii.basic import BasicHeader, BasicData, Basic + >>> + >>> class NoHeaderHeader(BasicHeader): + ... """Reader for table header without a header + ... + ... Set the start of header line number to `None`, which tells the basic + ... reader there is no header line. + ... """ + ... start_line = None + >>> + >>> class NoHeaderData(BasicData): + ... """Reader for table data without a header + ... + ... Data starts at first uncommented line since there is no header line. + ... """ + ... start_line = 0 + >>> + >>> class NoHeader(Basic): + ... """Read a table with no header line. Columns are autonamed using + ... header.auto_format which defaults to "col%d". Otherwise this reader + ... the same as the :class:`Basic` class from which it is derived. Example:: + ... + ... # Table data + ... 1 2 "hello there" + ... 3 4 world + ... """ + ... _format_name = 'custom_no_header' + ... _description = 'Basic table with no headers' + ... header_class = NoHeaderHeader + ... data_class = NoHeaderData + +In a slightly more involved case, the implementation can also override some of +the methods in the base class:: + + >>> # Note: CommentedHeader is already included in astropy.io.ascii for convenience. + >>> class CommentedHeaderHeader(BasicHeader): + ... """Header class for which the column definition line starts with the + ... comment character. See the :class:`CommentedHeader` class for an example. + ... """ + ... def process_lines(self, lines): + ... """Return only lines that start with the comment regexp. For these + ... lines strip out the matching characters.""" + ... re_comment = re.compile(self.comment) + ... for line in lines: + ... match = re_comment.match(line) + ... if match: + ... yield line[match.end():] + ... + ... def write(self, lines): + ... lines.append(self.write_comment + self.splitter.join(self.colnames)) + >>> + >>> + >>> class CommentedHeader(Basic): + ... """Read a file where the column names are given in a line that begins with + ... the header comment character. ``header_start`` can be used to specify the + ... line index of column names, and it can be a negative index (for example -1 + ... for the last commented line). The default delimiter is the + ... character.:: + ... + ... # col1 col2 col3 + ... # Comment line + ... 1 2 3 + ... 4 5 6 + ... """ + ... _format_name = 'custom_commented_header' + ... _description = 'Column names in a commented line' + ... + ... header_class = CommentedHeaderHeader + ... data_class = NoHeaderData + +**Application: Write a "fixed_width" table with a "commented_header"** + +This module provides formats for tables where the header line is marked with a comment +character and a separate class that writes fixed-width tables, but there is no functionality +to write a fixed-width table with a commented header. Fixed width tables can be easier to read +by eye because the rows are aligned and certain other programs require the header line to be +commented. So, we now want to make a writer that can write this format; for this example we do +not bother to work out how to read this format, but just raise an error on reading: + + >>> from astropy.io.ascii.fixedwidth import FixedWidthData, FixedWidth + >>> + >>> class FixedWidthDataCommentedHeaderData(FixedWidthData): + ... def write(self, lines): + ... lines = super().write(lines) + ... lines[0] = self.write_comment + lines[0] + ... for i in range(1, len(lines)): + ... lines[i] = ' ' * len(self.write_comment) + lines[i] + ... return lines + >>> + >>> class FixedWidthCommentedHeader(FixedWidth): + ... _format_name = "fixed_width_commented_header" + ... _description = "Fixed width with commented header" + ... + ... data_class = FixedWidthDataCommentedHeaderData + ... + ... def read(self, table): + ... raise NotImplementedError + +This new format is automatically added to the list of formats that can be read by +the :ref:`io_registry` (note that our format has no mechanism to write out the units): + + >>> import sys + >>> import astropy.units as u + >>> from astropy.table import Table + >>> tab = Table({'v': [15.4, 223.45] * u.km/u.s, 'type': ['star', 'jet']}) + >>> tab.write(sys.stdout, format='ascii.fixed_width', delimiter=None) + v type + 15.4 star + 223.45 jet + >>> tab.write(sys.stdout, format='ascii.commented_header') + # v type + 15.4 star + 223.45 jet + >>> tab.write(sys.stdout, format='ascii.fixed_width_commented_header', delimiter=None) + # v type + 15.4 star + 223.45 jet + +.. testcleanup:: + + >>> from astropy.io import registry + >>> from astropy.io.ascii.core import FORMAT_CLASSES + >>> for format_name in ['custom_no_header', 'custom_commented_header', 'fixed_width_commented_header']: + ... registry.unregister_reader(f"ascii.{format_name}", Table) + ... registry.unregister_writer(f"ascii.{format_name}", Table) + ... del FORMAT_CLASSES[format_name] + +**Define a custom reader functionally** + +Instead of defining a new class, it is also possible to obtain an instance +of a reader, and then to modify the properties of this one reader instance +in a function:: + + >>> from astropy.io import ascii + >>> + >>> def read_rdb_table(table): + ... reader = ascii.Basic() + ... reader.header.splitter.delimiter = '\t' + ... reader.data.splitter.delimiter = '\t' + ... reader.header.splitter.process_line = None + ... reader.data.splitter.process_line = None + ... reader.data.start_line = 2 + ... + ... return reader.read(table) + **Create a custom splitter.process_val function** :: - # The default process_val() normally just strips whitespace. - # In addition have it replace empty fields with -999. - def process_val(x): - """Custom splitter process_val function: Remove whitespace at the beginning - or end of value and substitute -999 for any blank entries.""" - x = x.strip() - if x == '': - x = '-999' - return x - - # Create an RDB reader and override the splitter.process_val function - rdb_reader = astropy.io.ascii.get_reader(Reader=astropy.io.ascii.Rdb) - rdb_reader.data.splitter.process_val = process_val + >>> # The default process_val() normally just strips whitespace. + >>> # In addition have it replace empty fields with -999. + >>> def process_val(x): + ... """Custom splitter process_val function: Remove whitespace at the beginning + ... or end of value and substitute -999 for any blank entries.""" + ... x = x.strip() + ... if x == '': + ... x = '-999' + ... return x + >>> + >>> # Create an RDB reader and override the splitter.process_val function + >>> rdb_reader = ascii.get_reader(reader_cls=ascii.Rdb) + >>> rdb_reader.data.splitter.process_val = process_val + +.. + EXAMPLE END + +.. _chunk_reading: + +Reading Large Tables in Chunks +============================== + +The default process for reading ASCII tables is not memory efficient and may +temporarily require much more memory than the size of the file (up to a factor +of 5 to 10). In cases where the temporary memory requirement exceeds available +memory this can cause significant slowdown when disk cache gets used. + +In this situation, there is a way to read the table in smaller chunks which are +limited in size. There are two possible ways to do this: + +- Read the table in chunks and aggregate the final table along the way. This + uses only somewhat more memory than the final table requires. +- Use a Python generator function to return a `~astropy.table.Table` object for + each chunk of the input table. This allows for scanning through arbitrarily + large tables since it never returns the final aggregate table. + +The chunk reading functionality is most useful for very large tables, so this is +available only for the :ref:`fast_ascii_io` readers. The following formats are +supported: ``tab``, ``csv``, ``no_header``, ``rdb``, and ``basic``. The +``commented_header`` format is not directly supported, but as a workaround one +can read using the ``no_header`` format and explicitly supply the column names +using the ``names`` argument. + +In order to read a table in chunks you must provide the ``fast_reader`` keyword +argument with a ``dict`` that includes the ``chunk_size`` key with the value +being the approximate size (in bytes) of each chunk of the input table to read. +In addition, if you provide a ``chunk_generator`` key which is set to +``True``, then instead of returning a single table for the whole input it +returns an iterator that provides a table for each chunk of the input. + +Examples +-------- + +.. + EXAMPLE START + Reading Large Tables in Chunks with astropy.io.ascii + +.. testsetup:: + + >>> # For performance we don't actually make a > 100 MB table. + >>> # The code works this way, too. + >>> tab = Table({'Vmag': [7] * 10}) + >>> tab.write('large_table.csv') + +To read an entire table while limiting peak memory usage: +:: + + # Read a large CSV table in 100 Mb chunks. + + tbl = ascii.read('large_table.csv', format='csv', guess=False, + fast_reader={'chunk_size': 100 * 1000000}) + +To read the table in chunks with an iterator, we iterate over a CSV table and +select all rows where the ``Vmag`` column is less than 8.0 (e.g., all stars in +table brighter than 8.0 mag). We collect all of these subtables and then stack +them at the end. +:: + + from astropy.table import vstack + + # tbls is an iterator over the chunks (no actual reading done yet) + tbls = ascii.read('large_table.csv', format='csv', guess=False, + fast_reader={'chunk_size': 100 * 1000000, + 'chunk_generator': True}) + + out_tbls = [] + + # At this point the file is actually read in chunks. + for tbl in tbls: + bright = tbl['Vmag'] < 8.0 + if np.count_nonzero(bright): + out_tbls.append(tbl[bright]) + + out_tbl = vstack(out_tbls) + +.. testcleanup:: + + >>> import pathlib + >>> pathlib.Path.unlink('large_table.csv') + +.. Note:: **Performance** + + Specifying the ``format`` explicitly and using ``guess=False`` is a good idea + for large tables. This prevents unnecessary guessing in the typical case + where the format is already known. + + The ``chunk_size`` should generally be set to the largest value that is + reasonable given available system memory. There is overhead associated + with processing each chunk, so the fewer chunks the better. + + .. + EXAMPLE END + +.. _io_ascii_how_to_examples: + +How to Find and Fix Problems Reading a Table +============================================ + +The purpose of this section is to provide a few examples how we can +deal with tables that fail to read. + +.. _io_ascii_should_specify_format: + +Specify as much detail as possible about the format +--------------------------------------------------- +One of the most common ways to read text tables with astropy is to get the +reader guess the format. This is convenient, but it takes extra time because +the reader tries different formats until one of them looks like it's +working (see :ref:`guess_formats` for details on the guessing process) and +sometimes that's not the format you expect. +Thus, if you know the format of the table, is it safer and faster to specify +as much detail as possible. + +Here is an example: + + >>> tab_text = """ + ... , a, b + ... 0, x, 3 + ... 1, y, d + ... """ + +This could be read either as table with three rows and no header (and a missing +data entry in the first column) or the first row could be the column names. +In either case, the commas could be part of the data of a space-delimited table +or they could be delimiters for a comma-delimited table. +If we explicitly set the format, astropy will read it for any of these cases. + +The "no_header" format will try a space-delimited table first, so all the columns will +come out to be string columns: + + >>> ascii.read(tab_text, format="no_header") +
+ col1 col2 col3 + str2 str2 str1 + ---- ---- ---- + , a, b + 0, x, 3 + 1, y, d + +If we set the delimiter to a comma, then the first column will be an integer: + + >>> ascii.read(tab_text, format="no_header", delimiter=",") +
+ col1 col2 col3 + int64 str1 str1 + ----- ---- ---- + -- a b + 0 x 3 + 1 y d + +We can also read it using a format that expects a header line and a comma delimiter. +In this case, only the name for the first column with an empty name will be auto-assigned: + + >>> ascii.read(tab_text, format='csv') +
+ col0 a b + int64 str1 str1 + ----- ---- ---- + 0 x 3 + 1 y d + +However, if we let astropy guess the format, it cannot know what is intended. When +guessing tries out different format with ``ascii.read(tab_text)`` the first one that +matches in this particular example is the "no header with comma delimiter" format. +While the astropy developers spent a lot of time trying to make the guessing process +return what a human would naturally expect, there is no way to make it work for all cases. +Thus, it is safest to always specify the format if known. + + +Sometimes it is easy to obtain the data in a more structured format that +more clearly defines columns and metadata, e.g. a FITS or VO/XML table, or +a text table that uses a different column separator (e.g. comma instead of +white space) or fixed-width columns. +In that case, the fastest solution can be to simply download or export the +data again in a different format. + +Find the Problem +---------------- +Usually, `astropy.io.ascii.read` tries many different formats until one +succeeds in reading. If it works, that saves you from finding and +setting right options for reading. However, if it fails to find any combination +of format and format options that correctly parses the file, then you will get +a long exception message which shows every format that was tried and ends +with this advice:: + + ************************************************************************ + ** ERROR: Unable to guess table format with the guesses listed above. ** + ** ** + ** To figure out why the table did not read, use guess=False and ** + ** fast_reader=False, along with any appropriate arguments to read(). ** + ** In particular specify the format and any known attributes like the ** + ** delimiter. ** + ************************************************************************ + +To expand on this a bit, you probably know from looking at the file +what format it is in, which must be one of the :ref:`supported_formats`. +For instance maybe it is a basic space-delimited file but has the header +line as a comment like below, which corresponds to the ``commented_header`` +format:: + + >>> table = """# name id + ... Jill 1232 + ... Jack Johnson 456""" + +In order to find the actual problem with the reading this file, you would do:: + + >>> ascii.read(table, format='commented_header', delimiter=' ', guess=False, fast_reader=False) + Traceback (most recent call last): + ... + astropy.io.ascii.core.InconsistentTableError: Number of header columns (2) inconsistent with data columns (3) at data line 1 + Header values: ['name', 'id'] + Data values: ['Jack', 'Johnson', '456'] + +At this point you can see that the problem is that the 2nd data line has 3 columns while the header says there should be only 2. You might be initially confused by the ``data line 1`` since the problem was in the 3rd line of the file. There are two things happening here. First, ``data line 1`` refers to the count of data lines and does not include any header lines, blank lines, or commented out lines. Second, the count starts from zero, so that ``1`` is the 2nd data line. +See the :ref:`guess_formats` section for additional details on format guessing. + + +Make the Table Easier to Read +----------------------------- + +Sometimes, the parameters for `astropy.io.ascii.read` to specify, for example +``format``, ``delimiter``, ``comment``, ``quote_char``, ``header_start``, +``data_start``, ``data_end``, and ``encoding`` are not enough. +To read just a single table that has a format close to, but not identical +with, any of the :ref:`supported_formats`, the fastest solution may be to open +that one table file in a text editor to modify it until it does conform to a +format that can be read. On the other hand, if we need to +read tables of that specific format again and again, it is better to find a way +to read them with `~astropy.io.ascii` without modifying every file by hand. + + +Badly formatted header line +^^^^^^^^^^^^^^^^^^^^^^^^^^^ +The following table will fail to parse (raising an +`~astropy.io.ascii.InconsistentTableError`) because the header line looks as +if there were three columns, while in fact, there are only two:: + + Name spectral type + Vega A0 + Altair A7 + +Opening this file in a text editor to fix the format is easy:: + + Name "spectral type" + Vega A0 + Altair A7 + +or:: + + Name spectral_type + Vega A0 + Altair A7 + +With either of the above changes you can read the file with no problem using default settings. + +.. + EXAMPLE START + Make a table easier to read + +To read the table without editing the files, we need to ignore the badly formatted header line and +pass in the names of the column ourselves. +That can be done without any modification of the table file by setting the ``data_start`` parameter:: + + >>> table = """ + ... Star spectral type + ... Vega A0 + ... Altair A7 + ... """ + >>> ascii.read(table, names=["Star", "spectral type"], data_start=1) +
+ Star spectral type + str6 str2 + ------ ------------- + Vega A0 + Altair A7 + +.. + EXAMPLE END + +Badly formatted data line +^^^^^^^^^^^^^^^^^^^^^^^^^ + +Similar principles apply to badly formatted data lines. Here is a +table where the number of columns is not consistent (``alpha Cen`` +should be written as ``"alpha Cen"`` to make clear that the two words +"alpha" and "Cen" are part of the same column):: + + Star SpT + Vega A0 + alpha Cen G2V+K1 + +When we try to read that with ``guess=False``, astropy throws an +`astropy.io.ascii.InconsistentTableError`:: + + >>> from astropy.io import ascii + >>> table = ''' + ... Star SpT + ... Vega A0 + ... alpha Cen G2V+K1 + ... ''' + >>> ascii.read(table, guess=False) + Traceback (most recent call last): + ... + astropy.io.ascii.core.InconsistentTableError: Number of header columns (2) inconsistent with data columns in data line 1 + +This points us to the line with the problem, here line 1 (starting to count +after the header lines and counting the data lines from 0 as usual in Python). In this table with +just two lines the problem is easy to spot, but for longer tables, the line number is +very helpful. We can now fix that line by hand in the file by adding quotes +around ``"alpha Cen"``. Then we can try to read the table again and see +if it works or if there is a another badly formatted data line. + +.. _io-ascii-read-gaia-tables: + +Reading Gaia Data Tables +======================== + +.. note:: + + The recommended way to access Gaia is via its + `astroquery.gaia `_ module. + However, if you need to access its data file separately via + ``astropy``, then read on. + +Gaia data tables are available in `ECSV +`_ format including detailed +metadata for the tables and columns (e.g., column descriptions, units, and data types). +For example the DR3 tables are at http://cdn.gea.esac.esa.int/Gaia/gdr3/gaia_source/. + +The DR3 data files are not strictly compliant with the ECSV standard because they use +the marker ``null`` to indicate a missing value instead of the required ``""``. In order +to read these files correctly with the full metadata, we need to tell the ECSV reader +to treat ``null`` as the missing value:: + + >>> from astropy.table import QTable + >>> dat = QTable.read( + ... "GaiaSource_000000-003111.csv.gz", + ... format="ascii.ecsv", + ... fill_values=("null", "0") + ... ) # doctest: +SKIP diff --git a/docs/io/ascii/ref_api.rst b/docs/io/ascii/ref_api.rst new file mode 100644 index 000000000000..f4fff26884a2 --- /dev/null +++ b/docs/io/ascii/ref_api.rst @@ -0,0 +1,4 @@ +Reference/API +************* + +.. automodapi:: astropy.io.ascii diff --git a/docs/io/ascii/references.txt b/docs/io/ascii/references.txt index 2dc67b9d2a96..58fefccda0a6 100644 --- a/docs/io/ascii/references.txt +++ b/docs/io/ascii/references.txt @@ -1,4 +1,3 @@ -.. |read| replace:: :func:`~astropy.io.ascii.ui.read` -.. |write| replace:: :func:`~astropy.io.ascii.ui.write` -.. |Table| replace:: :class:`~astropy.table.Table` -.. _structured array: http://docs.scipy.org/doc/numpy/user/basics.rec.html +.. |read| replace:: :func:`~astropy.io.ascii.read` +.. |write| replace:: :func:`~astropy.io.ascii.write` +.. _structured array: https://numpy.org/doc/stable/user/basics.rec.html diff --git a/docs/io/ascii/toc.txt b/docs/io/ascii/toc.txt index 02df2d59aa8f..779c0a5b5af2 100644 --- a/docs/io/ascii/toc.txt +++ b/docs/io/ascii/toc.txt @@ -5,4 +5,5 @@ write base_classes fixed_width_gallery + fast_ascii_io ascii_api diff --git a/docs/io/ascii/write.rst b/docs/io/ascii/write.rst index 8100751b3e24..cbee040acb11 100644 --- a/docs/io/ascii/write.rst +++ b/docs/io/ascii/write.rst @@ -1,20 +1,42 @@ -.. _astropy.io.ascii_write: - .. include:: references.txt -Writing tables --------------- +.. _astropy.io.ascii_write: + +Writing Tables +============== -:mod:`astropy.io.ascii` is able to write ASCII tables out to a file or file-like +:mod:`astropy.io.ascii` is able to write text tables out to a file or file-like object using the same class structure and basic user interface as for reading -tables. +tables. -The |write| function provides a way to write a data table as a formatted ASCII table. For example:: +Help on the ``write()`` function arguments is available interactively as shown in +this example: + +.. doctest-skip:: >>> from astropy.io import ascii - >>> x = np.array([1, 2, 3]) - >>> y = x ** 2 - >>> ascii.write([x, y], names=['x', 'y'], 'values.dat') + >>> ascii.write.help() # Common help for all formats + >>> ascii.write.help("html") # Common help plus "html" format-specific args + +The |write| function provides a way to write a data table as a +formatted text table. + +Examples +-------- + +.. + EXAMPLE START + Writing Text Tables Using astropy.io.ascii + +To write a formatted text table using the |write| function:: + + >>> import numpy as np + >>> from astropy.io import ascii + >>> from astropy.table import Table + >>> data = Table() + >>> data['x'] = np.array([1, 2, 3], dtype=np.int32) + >>> data['y'] = data['x'] ** 2 + >>> ascii.write(data, 'values.dat', overwrite=True) # doctest: +SKIP The ``values.dat`` file will then contain:: @@ -23,13 +45,43 @@ The ``values.dat`` file will then contain:: 2 4 3 9 -All of the input Reader table formats supported by `astropy.io.ascii` for -reading are also supported for writing. This provides a great deal of -flexibility in the format for writing. The example below writes the data as a +It is also possible and encouraged to use the write functionality from +:mod:`astropy.io.ascii` through a higher level interface in the :ref:`Data +Tables ` package (see :ref:`table_io` for more details). For +example:: + + >>> data.write('values.dat', format='ascii', overwrite=True) # doctest: +SKIP + +For a more reproducible text version of your table, we recommend using the +:ref:`ecsv_format`. This stores all the table meta-data (in particular the +column types and units) to a comment section at the beginning while still +maintaining compatibility with most plain CSV readers. It also allows storing +richer data like `~astropy.coordinates.SkyCoord` or multidimensional or +variable-length columns. For our simple example:: + + >>> data.write('values.ecsv', overwrite=True) # doctest: +SKIP + +The ``.ecsv`` extension is recognized and implies using ECSV (equivalent to +``format='ascii.ecsv'``). The ``values.ecsv`` file will then contain:: + + # %ECSV 1.0 + # --- + # datatype: + # - {name: x, datatype: int32} + # - {name: y, datatype: int32} + # schema: astropy-2.0 + x y + 1 1 + 2 4 + 3 9 + +Most of the input table :ref:`supported_formats` for +reading are also available for writing. This provides a great deal of +flexibility in the format for writing. The example below writes the data as a LaTeX table, using the option to send the output to ``sys.stdout`` instead of a file:: - >>> ascii.write(data, sys.stdout, Writer=ascii.Latex) + >>> ascii.write(data, format='latex') # doctest: +SKIP \begin{table} \begin{tabular}{cc} x & y \\ @@ -39,175 +91,352 @@ file:: \end{tabular} \end{table} -Input data format -^^^^^^^^^^^^^^^^^^^ - -The input `table` argument to |write| can be any value that is supported for -initializing a |Table| object. This is documented in detail in the -:ref:`construct_table` section and includes creating a table with a list of -columns, a dictionary of columns, or from `numpy` arrays (either structured or -homogeneous). The sections below show a few examples. - -Table or NumPy structured array -""""""""""""""""""""""""""""""" - -An AstroPy |Table| object or a NumPy `structured array`_ (or record array) can -serve as input to the |write| function. - -:: - - >>> from astropy.io import ascii - >>> from astropy.table import Table - - >>> data = Table({'a': [1, 2, 3], - 'b': [4.0, 5.0, 6.0]}, - names=['a', 'b']) - >>> ascii.write(data, sys.stdout) - a b - 1 4.0 - 2 5.0 - 3 6.0 - - >>> data = np.array([(1, 2., 'Hello'), (2, 3., "World")], - dtype=('i4,f4,a10')) - >>> ascii.write(data, sys.stdout) - f0 f1 f2 - 1 2.0 Hello - 2 3.0 World - -The output of :mod:`astropy.io.ascii.read` is a |Table| or NumPy array data -object that can be an input to the |write| function. +There is also a faster Cython engine for writing simple formats, +which is enabled by default for these formats (see :ref:`fast_ascii_io`). +To disable this engine, use the parameter ``fast_writer``:: -:: + >>> ascii.write(data, 'values.csv', format='csv', fast_writer=False) # doctest: +SKIP - >>> data = astropy.io.ascii.read('t/daophot.dat', Reader=astropy.io.ascii.Daophot) - >>> astropy.io.ascii.write(data, 'space_delimited_table.dat') +.. + EXAMPLE END +.. Note:: -List of lists -""""""""""""" + For most supported formats one can write a masked table and then read it back + without losing information about the masked table entries. This is + accomplished by using a blank string entry to indicate a masked (missing) + value. See the :ref:`replace_bad_or_missing_values` section for more + information. -A list of Python lists (or any iterable object) can be used as input:: +.. _io_ascii_write_parameters: - >>> x = [1, 2, 3] - >>> y = [4, 5.2, 6.1] - >>> z = ['hello', 'world', '!!!'] - >>> data = [x, y, z] - - >>> ascii.write(data, sys.stdout) - col0 col1 col2 - 1 4.0 hello - 2 5.2 world - 3 6.1 !!! - -The `data` object does not contain information about the column names so -|Table| has chosen them automatically. To specify the names, provide the -`names` keyword argument. This example also shows excluding one of the columns -from the output:: +Parameters for ``write()`` +-------------------------- - >>> ascii.write(data, sys.stdout, names=['x', 'y', 'z'], exclude_names=['y']) - x z - 1 hello - 2 world - 3 !!! +The |write| function accepts a number of parameters that specify the detailed +output table format. Each of the :ref:`supported_formats` is handled by a +corresponding Writer class that can define different defaults, so the +descriptions below sometimes mention "typical" default values. This refers to +the :class:`~astropy.io.ascii.Basic` writer and other similar Writer classes. +Some output format Writer classes (e.g., :class:`~astropy.io.ascii.Latex` or +:class:`~astropy.io.ascii.AASTex`) accept additional keywords that can +customize the output further. See the documentation of these classes for +details. -Dict of lists -""""""""""""" +**output**: output specifier + There are two ways to specify the output for the write operation: -A dictionary containing iterable objects can serve as input to |write|. Each -dict key is taken as the column name while the value must be an iterable object -containing the corresponding column values. + - Name of a file (string) + - File-like object (from open(), StringIO, etc.) + +**table**: input table + Any value that is supported for initializing a |Table| object (see + :ref:`construct_table`). This includes a table with a list of columns, a + dictionary of columns, or from `numpy` arrays (either structured or + homogeneous). + +**format**: output format (default='basic') + This specifies the format of the text table to be written, such as a basic + character delimited table, fixed-format table, or a CDS-compatible table, + etc. The value of this parameter must be one of the :ref:`supported_formats`. + +**delimiter**: column delimiter string + A one-character string used to separate fields which typically defaults to + the space character. Other common values might be "," or "|" or "\\t". + +**comment**: string defining start of a comment line in output table + For the :class:`~astropy.io.ascii.Basic` Writer this defaults to "# ". + Which comments are written and how depends on the format chosen. + The comments are defined as a list of strings in the input table + ``meta['comments']`` element. Comments in the metadata of the given + |Table| will normally be written before the header, although + :class:`~astropy.io.ascii.CommentedHeader` writes table comments after the + commented header. To disable writing comments, set ``comment=False``. -Since a Python dictionary is not ordered the output column order will be -unpredictable unless the ``names`` argument is provided. +**formats**: dict of data type converters + For each key (column name) use the given value to convert the column data to + a string. If the format value is string-like, then it is used as a Python + format statement (e.g., '%0.2f' % value). If it is a callable function, then + that function is called with a single argument containing the column value to + be converted. Example:: -:: + astropy.io.ascii.write(table, sys.stdout, formats={'XCENTER': '%12.1f', + 'YCENTER': lambda x: round(x, 1)}, - >>> data = {'x': [1, 2, 3], - 'y': [4, 5.2, 6.1], - 'z': ['hello', 'world', '!!!']} - >>> ascii.write(data, sys.stdout, names=['x', 'y', 'z']) - x y z - 1 4.0 hello - 2 5.2 world - 3 6.1 !!! +**names**: list of output column names + Define the complete list of output column names to write for the data table, + overriding the existing column names. +**include_names**: list of names to include in output + From the list of column names found from the data table or the ``names`` + parameter, select for output only columns within this list. If not supplied + then include all names. -Parameters for ``write()`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +**exclude_names**: list of names to exclude from output + Exclude these names from the list of output columns. This is applied *after* + the ``include_names`` filtering. If not specified then no columns are excluded. -The |write| function accepts a number of parameters that specify the detailed -output table format. Different Reader classes can define different defaults, so the -descriptions below sometimes mention "typical" default values. This refers to -the :class:`~astropy.io.ascii.Basic` reader and other similar Reader classes. +**fill_values**: list of fill value specifiers + This can be used to fill missing values in the table or replace values with special meaning. -Some Reader classes, e.g. :class:`~astropy.io.ascii.Latex` or :class:`~astropy.io.ascii.AASTex` -accept aditional keywords, that can customize the output further. See the documentation -of these classes for details. + See the :ref:`replace_bad_or_missing_values` section for more information on + the syntax. The syntax is almost the same as when reading a table. + There is a special value ``astropy.io.ascii.masked`` that is used to say + "output this string for all masked values in a masked table" (the default is + to use an empty string ``""``):: + + >>> import sys + >>> from astropy.table import Table, Column, MaskedColumn + >>> from astropy.io import ascii + >>> t = Table([(1, 2), (3, 4)], names=('a', 'b'), masked=True) + >>> t['a'].mask = [True, False] + >>> ascii.write(t, sys.stdout) + a b + "" 3 + 2 4 + >>> ascii.write(t, sys.stdout, fill_values=[(ascii.masked, 'N/A')]) + a b + N/A 3 + 2 4 + + Note that when writing a table, all values are converted to strings before + any value is replaced. Because ``fill_values`` only replaces cells that + are an exact match to the specification, you need to provide the string + representation (stripped of whitespace) for each value. For example, in + the following commands ``-99`` is formatted with two digits after the + comma, so we need to replace ``-99.00`` and not ``-99``:: + + >>> t = Table([(-99, 2), (3, 4)], names=('a', 'b')) + >>> ascii.write(t, sys.stdout, fill_values = [('-99.00', 'no data')], + ... formats={'a': '%4.2f'}) + a b + "no data" 3 + 2.00 4 + + Similarly, if you replace a value in a column that has a fixed length format + (e.g., ``'f4.2'``), then the string you want to replace must have the same + number of characters. In the example above, ``fill_values=[(' nan',' N/A')]`` + would work. + +**fill_include_names**: list of column names, which are affected by ``fill_values`` + If not supplied, then ``fill_values`` can affect all columns. -**output** : output specifier - There are two ways to specify the output for the write operation: +**fill_exclude_names**: list of column names, which are not affected by ``fill_values`` + If not supplied, then ``fill_values`` can affect all columns. - - Name of a file (string) - - File-like object (from open(), StringIO, etc) +**fast_writer**: whether to use the fast Cython writer + If this parameter is ``None`` (which it is by default), |write| will attempt + to use the faster writer (described in :ref:`fast_ascii_io`) if possible. + Specifying ``fast_writer=False`` disables this behavior. -**table** : input table - Any value that is supported for initializing a |Table| object (see :ref:`construct_table`). +.. _cds_mrt_format: -**Writer** : Writer class (default= :class:`~astropy.io.ascii.Basic`) - This specifies the top-level format of the ASCII table to be written, for - example if it is a basic character delimited table, fixed format table, or a - CDS-compatible table, etc. The value of this parameter must be a Reader - class. For basic usage this means one of the built-in :ref:`extension_reader_classes`. - Note: Reader classes and Writer classes are synonymous, in other - words Reader classes can also write, but for historical reasons they are - called Reader classes. +Machine-Readable Table Format +----------------------------- -**delimiter** : column delimiter string - A one-character string used to separate fields which typically defaults to the space character. - Other common values might be "," or "|" or "\\t". +The American Astronomical Society Journals' `Machine-Readable Table (MRT) +`_ format consists of single file with +the table description header and the table data itself. MRT is similar to the +`CDS `_ format standard, but differs +in the table description sections and the lack of a separate ``ReadMe`` file. +Astropy does not support writing in the CDS format. -**comment** : string defining a comment line in table - For the :class:`~astropy.io.ascii.Basic` Reader this defaults to "#". +The :class:`~astropy.io.ascii.Mrt` writer supports writing tables to MRT format. -**formats**: dict of data type converters - For each key (column name) use the given value to convert the column data to a string. - If the format value is string-like then it is used as a Python format statement, - e.g. '%0.2f' % value. If it is a callable function then that function - is called with a single argument containing the column value to be converted. - Example:: +.. note:: - astropy.io.ascii.write(table, sys.stdout, formats={'XCENTER': '%12.1f', - 'YCENTER': lambda x: round(x, 1)}, + The metadata of the table, apart from column ``unit``, ``name`` and + ``description``, are not written in the output file. Placeholders for + the title, authors, and table name fields are put into the output file and + can be edited after writing. -**names**: list of names corresponding to each data column - Define the complete list of names for each data column. This will override - names determined from the data table (if available). If not supplied then - use names from the data table or auto-generated names. +Examples +"""""""" -**include_names**: list of names to include in output - From the list of column names found from the data table or the ``names`` - parameter, select for output only columns within this list. If not supplied - then include all names. - -**exclude_names**: list of names to exlude from output - Exclude these names from the list of output columns. This is applied *after* - the ``include_names`` filtering. If not specified then no columns are excluded. +.. + EXAMPLE START + Writing MRT Format Tables Using astropy.io.ascii -**fill_values**: fill value specifier of lists - This can be used to fill missing values in the table or replace values with special meaning. - The syntax is the same as used on input. - See the :ref:`replace_bad_or_missing_values` section for more information on the syntax. - When writing a table, all values are converted to strings, before any value is replaced. Thus, - you need to provide the string representation (stripped of whitespace) for each value. - Example:: +The command ``ascii.write(format='mrt')`` writes an ``astropy`` `~astropy.table.Table` +to the MRT format. Section dividers ``---`` and ``===`` are used to divide the table +into different sections, with the last section always been the actual data. - astropy.io.ascii.write(table, sys.stdout, fill_values = [('nan', 'no data'), - ('-999.0', 'no data')]) +As the MRT standard requires, +for columns that have a ``unit`` attribute not set to ``None``, +the unit names are tabulated in the Byte-By-Byte +description of the column. When columns do not contain any units, ``---`` is put instead. +A ``?`` is prefixed to the column description in the Byte-By-Byte for ``Masked`` +columns or columns that have null values, indicating them as such. -**fill_include_names**: list of column names, which are affected by ``fill_values``. - If not supplied, then ``fill_values`` can affect all columns. +The example below initializes a table with columns that have a ``unit`` attribute and +has masked values. -**fill_exclude_names**: list of column names, which are not affected by ``fill_values``. - If not supplied, then ``fill_values`` can affect all columns. + >>> from astropy.io import ascii + >>> from astropy.table import Table, Column, MaskedColumn + >>> from astropy import units as u + >>> table = Table() + >>> table['Name'] = ['ASASSN-15lh', 'ASASSN-14li'] + >>> # MRT Standard requires all quantities in SI units. + >>> temperature = [0.0334, 0.297] * u.K + >>> table['Temperature'] = temperature.to(u.keV, equivalencies=u.temperature_energy()) + >>> table['nH'] = Column([0.025, 0.0188], unit=u.Unit(10**22)) + >>> table['Flux'] = ([2.044 * 10**-11] * u.erg * u.cm**-2).to(u.Jy * u.Unit(10**12)) + >>> table['Flux'] = MaskedColumn(table['Flux'], mask=[True, False]) + >>> table['magnitude'] = [u.Magnitude(25), u.Magnitude(-9)] + +Note that for columns with `~astropy.time.Time`, `~astropy.time.TimeDelta` and related values, +the writer does not do any internal conversion or modification. These columns should be +converted to regular columns with proper ``unit`` and ``name`` attribute before writing +the table. Thus:: + + >>> from astropy.time import Time, TimeDelta + >>> from astropy.timeseries import TimeSeries + >>> ts = TimeSeries(time_start=Time('2019-01-01'), time_delta=2*u.day, n_samples=1) + >>> table['Obs'] = Column(ts.time.decimalyear, description='Time of Observation') + >>> table['Cadence'] = Column(TimeDelta(100.0, format='sec').datetime.seconds, + ... unit=u.s) + +Columns that are `~astropy.coordinates.SkyCoord` objects or columns with +values that are such objects are recognized as such, and some predefined labels and +description is used for them. Coordinate columns that have `~astropy.coordinates.SphericalRepresentation` +are additionally sub-divided into their coordinate component columns. Representations that have +``ra`` and ``dec`` components are divided into their ``hour``-``min``-``sec`` +and ``deg``-``arcmin``-``arcsec`` components respectively. Whereas columns with +``SkyCoord`` objects in the ``Galactic`` or any of the ``Ecliptic`` frames are divided +into their latitude(``ELAT``/``GLAT``) and longitude components (``ELON``/``GLAT``) only. +The original table remains accessible as such, while the file is written from a modified +copy of the table. The new coordinate component columns are appended to the end of the table. + +It should be noted that the default precision of the latitude, longitude and seconds (of arc) +columns is set at a default number of 12, 10 and 9 digits after the decimal for ``deg``, ``sec`` +and ``arcsec`` values, respectively. This default is set to match a machine precision of 1e-15 +relative to the original ``SkyCoord`` those columns were extracted from. +As all other columns, the format can be expliclty set by passing the ``formats`` keyword to the +``write`` function or by setting the ``format`` attribute of individual columns (the latter +will only work for columns that are not decomposed). +To customize the number of significant digits, presicions should therefore be specified in the +``formats`` dictionary for the *output* column names, such as +``formats={'RAs': '07.4f', 'DEs': '06.3f'}`` or ``formats={'GLAT': '+10.6f', 'GLON': '9.6f'}`` +for milliarcsecond accuracy. Note that the forms with leading zeros for the seconds and +including the sign for latitudes are recommended for better consistency and readability. + +The following code illustrates the above. + + >>> from astropy.coordinates import SkyCoord + >>> table['coord'] = [SkyCoord.from_name('ASASSN-15lh'), + ... SkyCoord.from_name('ASASSN-14li')] # doctest: +REMOTE_DATA + >>> table.write('coord_cols.dat', format='ascii.mrt') # doctest: +SKIP + >>> table['coord'] = table['coord'].geocentrictrueecliptic # doctest: +REMOTE_DATA + >>> table['Temperature'].format = '.5E' # Set default column format. + >>> table.write('ecliptic_cols.dat', format='ascii.mrt') # doctest: +SKIP + +After execution, the contents of ``coords_cols.dat`` will be:: + + Title: + Authors: + Table: + ================================================================================ + Byte-by-byte Description of file: table.dat + -------------------------------------------------------------------------------- + Bytes Format Units Label Explanations + -------------------------------------------------------------------------------- + 1-11 A11 --- Name Description of Name + 13-23 E11.6 keV Temperature [0.0/0.01] Description of Temperature + 25-30 F6.4 10+22 nH [0.01/0.03] Description of nH + 32-36 F5.3 10+12Jy Flux ? Description of Flux + 38-42 E5.1 mag magnitude [0.0/3981.08] Description of magnitude + 44-49 F6.1 --- Obs [2019.0/2019.0] Time of Observation + 51-53 I3 s Cadence [100] Description of Cadence + 55-56 I2 h RAh Right Ascension (hour) + 58-59 I2 min RAm Right Ascension (minute) + 61-73 F13.10 s RAs Right Ascension (second) + 75 A1 --- DE- Sign of Declination + 76-77 I2 deg DEd Declination (degree) + 79-80 I2 arcmin DEm Declination (arcmin) + 82-93 F12.9 arcsec DEs Declination (arcsec) + -------------------------------------------------------------------------------- + Notes: + -------------------------------------------------------------------------------- + ASASSN-15lh 2.87819e-09 0.0250 1e-10 2019.0 100 22 02 15.4500000000 -61 39 34.599996000 + ASASSN-14li 2.55935e-08 0.0188 2.044 4e+03 2019.0 100 12 48 15.2244072000 +17 46 26.496624000 + +And the file ``ecliptic_cols.dat`` will look like:: + + Title: + Authors: + Table: + ================================================================================ + Byte-by-byte Description of file: table.dat + -------------------------------------------------------------------------------- + Bytes Format Units Label Explanations + -------------------------------------------------------------------------------- + 1- 11 A11 --- Name Description of Name + 13- 23 E11.6 keV Temperature [0.0/0.01] Description of Temperature + 25- 30 F6.4 10+22 nH [0.01/0.03] Description of nH + 32- 36 F5.3 10+12Jy Flux ? Description of Flux + 38- 42 E5.1 mag magnitude [0.0/3981.08] Description of magnitude + 44- 49 F6.1 --- Obs [2019.0/2019.0] Time of Observation + 51- 53 I3 s Cadence [100] Description of Cadence + 55- 70 F16.12 deg ELON Ecliptic Longitude (geocentrictrueecliptic) + 72- 87 F16.12 deg ELAT Ecliptic Latitude (geocentrictrueecliptic) + -------------------------------------------------------------------------------- + Notes: + -------------------------------------------------------------------------------- + ASASSN-15lh 2.87819e-09 0.0250 1e-10 2019.0 100 306.224208650096 -45.621789850825 + ASASSN-14li 2.55935e-08 0.0188 2.044 4e+03 2019.0 100 183.754980099243 21.051410763027 + +Finally, MRT has some specific naming conventions for columns +(``_). For example, if a column contains +the mean error for the data in a column named ``label``, then this column should be named ``e_label``. +These kinds of relative column naming cannot be enforced by the MRT writer +because it does not know what the column data means and thus, the relation between the +columns cannot be figured out. Therefore, it is up to the user to use ``Table.rename_columns`` +to appropriately rename any columns before writing the table to MRT format. +The following example shows a similar situation, using the option to send the output to +``sys.stdout`` instead of a file:: + + >>> table['error'] = [1e4, 450] * u.Jy # Error in the Flux values. + >>> outtab = table.copy() # So that changes don't affect the original table. + >>> outtab.rename_column('error', 'e_Flux') + >>> # re-order so that related columns are placed next to each other. + >>> outtab = outtab['Name', 'Obs', 'coord', 'Cadence', 'nH', 'magnitude', + ... 'Temperature', 'Flux', 'e_Flux'] # doctest: +REMOTE_DATA + + >>> ascii.write(outtab, format='mrt') # doctest: +SKIP + Title: + Authors: + Table: + ================================================================================ + Byte-by-byte Description of file: table.dat + -------------------------------------------------------------------------------- + Bytes Format Units Label Explanations + -------------------------------------------------------------------------------- + 1- 11 A11 --- Name Description of Name + 13- 18 F6.1 --- Obs [2019.0/2019.0] Time of Observation + 20- 22 I3 s Cadence [100] Description of Cadence + 24- 29 F6.4 10+22 nH [0.01/0.03] Description of nH + 31- 35 E5.1 mag magnitude [0.0/3981.08] Description of magnitude + 37- 47 E11.6 keV Temperature [0.0/0.01] Description of Temperature + 49- 53 F5.3 10+12Jy Flux ? Description of Flux + 55- 61 F7.1 Jy e_Flux [450.0/10000.0] Description of e_Flux + 63- 78 F16.12 deg ELON Ecliptic Longitude (geocentrictrueecliptic) + 80- 95 F16.12 deg ELAT Ecliptic Latitude (geocentrictrueecliptic) + -------------------------------------------------------------------------------- + Notes: + -------------------------------------------------------------------------------- + ASASSN-15lh 2019.0 100 0.0250 1e-10 2.87819e-09 10000.0 306.224208650096 -45.621789850825 + ASASSN-14li 2019.0 100 0.0188 4e+03 2.55935e-08 2.044 450.0 183.754980099243 21.051410763027 + +.. + EXAMPLE END + +.. attention:: + + The MRT writer currently supports automatic writing of a single coordinate column + in ``Tables``. For tables with more than one coordinate column of a given kind + (e.g. equatorial, galactic or ecliptic), only the first found coordinate column + will be decomposed into its component columns, and the rest of the coordinate + columns of the same type will be converted to string columns. Thus users should take + care that the additional coordinate columns are dealt with (e.g. by converting them + to unique ``float``-valued columns) before using ``SkyCoord`` methods. diff --git a/docs/io/fits/api/cards.rst b/docs/io/fits/api/cards.rst index af22cc3c53f7..ad0ab08c0121 100644 --- a/docs/io/fits/api/cards.rst +++ b/docs/io/fits/api/cards.rst @@ -1,31 +1,13 @@ .. currentmodule:: astropy.io.fits Cards ------ +***** :class:`Card` -^^^^^^^^^^^^^ +============= .. autoclass:: Card :members: :inherited-members: :undoc-members: :show-inheritance: - -Deprecated Interfaces -^^^^^^^^^^^^^^^^^^^^^ - -The following classes and functions are deprecated as of the PyFITS 3.1 header -refactoring, though they are currently still available for backwards-compatibility. - -.. autoclass:: CardList - :members: - :undoc-members: - :show-inheritance: - -.. autofunction:: create_card - -.. autofunction:: create_card_from_string - -.. autofunction:: upper_key - diff --git a/docs/io/fits/api/diff.rst b/docs/io/fits/api/diff.rst index 1e14069b0632..fb6458a42813 100644 --- a/docs/io/fits/api/diff.rst +++ b/docs/io/fits/api/diff.rst @@ -1,46 +1,46 @@ Differs -------- +******* .. automodule:: astropy.io.fits.diff .. currentmodule:: astropy.io.fits :class:`FITSDiff` -^^^^^^^^^^^^^^^^^ +================= .. autoclass:: FITSDiff :members: :inherited-members: :show-inheritance: :class:`HDUDiff` -^^^^^^^^^^^^^^^^ +================ .. autoclass:: HDUDiff :members: :inherited-members: :show-inheritance: :class:`HeaderDiff` -^^^^^^^^^^^^^^^^^^^ +=================== .. autoclass:: HeaderDiff :members: :inherited-members: :show-inheritance: :class:`ImageDataDiff` -^^^^^^^^^^^^^^^^^^^^^^ +====================== .. autoclass:: ImageDataDiff :members: :inherited-members: :show-inheritance: :class:`RawDataDiff` -^^^^^^^^^^^^^^^^^^^^ +==================== .. autoclass:: RawDataDiff :members: :inherited-members: :show-inheritance: :class:`TableDataDiff` -^^^^^^^^^^^^^^^^^^^^^^ +====================== .. autoclass:: TableDataDiff :members: :inherited-members: diff --git a/docs/io/fits/api/files.rst b/docs/io/fits/api/files.rst index 15af19dac9f0..9514524437cc 100644 --- a/docs/io/fits/api/files.rst +++ b/docs/io/fits/api/files.rst @@ -1,44 +1,48 @@ .. currentmodule:: astropy.io.fits File Handling and Convenience Functions ---------------------------------------- +*************************************** :func:`open` -^^^^^^^^^^^^ +============ .. autofunction:: open :func:`writeto` -^^^^^^^^^^^^^^^ +=============== .. autofunction:: writeto :func:`info` -^^^^^^^^^^^^ +============ .. autofunction:: info +:func:`printdiff` +================= +.. autofunction:: printdiff + :func:`append` -^^^^^^^^^^^^^^ +============== .. autofunction:: append :func:`update` -^^^^^^^^^^^^^^ +============== .. autofunction:: update :func:`getdata` -^^^^^^^^^^^^^^^ +=============== .. autofunction:: getdata :func:`getheader` -^^^^^^^^^^^^^^^^^ +================= .. autofunction:: getheader :func:`getval` -^^^^^^^^^^^^^^ +============== .. autofunction:: getval :func:`setval` -^^^^^^^^^^^^^^ +============== .. autofunction:: setval :func:`delval` -^^^^^^^^^^^^^^ +============== .. autofunction:: delval diff --git a/docs/io/fits/api/hdulists.rst b/docs/io/fits/api/hdulists.rst index 6df042360646..da1afdbf410d 100644 --- a/docs/io/fits/api/hdulists.rst +++ b/docs/io/fits/api/hdulists.rst @@ -1,12 +1,12 @@ .. currentmodule:: astropy.io.fits HDU Lists ---------- +********* .. inheritance-diagram:: HDUList :class:`HDUList` -^^^^^^^^^^^^^^^^ +================ .. autoclass:: HDUList :members: diff --git a/docs/io/fits/api/hdus.rst b/docs/io/fits/api/hdus.rst index 1c8a84038eda..5433f6f38156 100644 --- a/docs/io/fits/api/hdus.rst +++ b/docs/io/fits/api/hdus.rst @@ -1,7 +1,11 @@ .. currentmodule:: astropy.io.fits -Header Data Units ------------------ +Header Data Unit +**************** + +Header Data Units are the fundamental container structure of the FITS format +consisting of a ``data`` member and its associated metadata in a ``header``. +They are defined in ``astropy.io.fits.hdu``. The :class:`ImageHDU` and :class:`CompImageHDU` classes are discussed in the section on :ref:`Images`. @@ -10,33 +14,33 @@ The :class:`TableHDU` and :class:`BinTableHDU` classes are discussed in the section on :ref:`Tables`. :class:`PrimaryHDU` -^^^^^^^^^^^^^^^^^^^ +=================== .. autoclass:: PrimaryHDU :members: :inherited-members: :show-inheritance: :class:`GroupsHDU` -^^^^^^^^^^^^^^^^^^ +================== .. autoclass:: GroupsHDU :members: :inherited-members: :show-inheritance: :class:`GroupData` -^^^^^^^^^^^^^^^^^^ +================== .. autoclass:: GroupData :members: :show-inheritance: :class:`Group` -^^^^^^^^^^^^^^ -.. autoclass:: GroupData +-------------- +.. autoclass:: Group :members: :show-inheritance: :class:`StreamingHDU` -^^^^^^^^^^^^^^^^^^^^^ +===================== .. autoclass:: StreamingHDU :members: :inherited-members: diff --git a/docs/io/fits/api/headers.rst b/docs/io/fits/api/headers.rst index d8eb18738472..02a399e7b3f2 100644 --- a/docs/io/fits/api/headers.rst +++ b/docs/io/fits/api/headers.rst @@ -1,10 +1,10 @@ .. currentmodule:: astropy.io.fits Headers -------- +******* :class:`Header` -^^^^^^^^^^^^^^^ +=============== .. autoclass:: Header :members: diff --git a/docs/io/fits/api/images.rst b/docs/io/fits/api/images.rst index b3b80f994d59..cee287d42c47 100644 --- a/docs/io/fits/api/images.rst +++ b/docs/io/fits/api/images.rst @@ -3,10 +3,10 @@ .. _images: Images ------- +****** `ImageHDU` -^^^^^^^^^^ +========== .. autoclass:: ImageHDU :members: @@ -14,9 +14,25 @@ Images :show-inheritance: `CompImageHDU` -^^^^^^^^^^^^^^ +============== .. autoclass:: CompImageHDU :members: :inherited-members: :show-inheritance: + +`Section` +========= + +.. autoclass:: Section + :members: + :inherited-members: + :show-inheritance: + +`CompImageSection` +================== + +.. autoclass:: CompImageSection + :members: + :inherited-members: + :show-inheritance: diff --git a/docs/io/fits/api/index.rst b/docs/io/fits/api/index.rst new file mode 100644 index 000000000000..e45f5f50a727 --- /dev/null +++ b/docs/io/fits/api/index.rst @@ -0,0 +1,17 @@ +Reference/API +************* + + +.. toctree:: + :maxdepth: 3 + + files.rst + hdulists.rst + hdus.rst + headers.rst + cards.rst + tables.rst + images.rst + diff.rst + verification.rst + tiled_compression.rst diff --git a/docs/io/fits/api/tables.rst b/docs/io/fits/api/tables.rst index 1c9138e9560d..909c072b811f 100644 --- a/docs/io/fits/api/tables.rst +++ b/docs/io/fits/api/tables.rst @@ -3,44 +3,44 @@ .. _tables: Tables ------- +****** :class:`BinTableHDU` -^^^^^^^^^^^^^^^^^^^^ +==================== .. autoclass:: BinTableHDU :members: :inherited-members: :show-inheritance: :class:`TableHDU` -^^^^^^^^^^^^^^^^^ +================= .. autoclass:: TableHDU :members: :inherited-members: :show-inheritance: :class:`Column` -^^^^^^^^^^^^^^^ +=============== .. autoclass:: Column :members: :inherited-members: :show-inheritance: :class:`ColDefs` -^^^^^^^^^^^^^^^^ +================ .. autoclass:: ColDefs :members: :inherited-members: :show-inheritance: :class:`FITS_rec` -^^^^^^^^^^^^^^^^^ +================= .. autoclass:: FITS_rec :members: :show-inheritance: :class:`FITS_record` -^^^^^^^^^^^^^^^^^^^^ +==================== .. autoclass:: FITS_record :members: :inherited-members: @@ -48,16 +48,16 @@ Tables Table Functions -^^^^^^^^^^^^^^^ - -:func:`new_table` -""""""""""""""""" -.. autofunction:: new_table +=============== :func:`tabledump` -""""""""""""""""" +----------------- .. autofunction:: tabledump :func:`tableload` -""""""""""""""""" +----------------- .. autofunction:: tableload + +:func:`table_to_hdu` +-------------------- +.. autofunction:: table_to_hdu diff --git a/docs/io/fits/api/tiled_compression.rst b/docs/io/fits/api/tiled_compression.rst new file mode 100644 index 000000000000..3a033d21e251 --- /dev/null +++ b/docs/io/fits/api/tiled_compression.rst @@ -0,0 +1,27 @@ +***************** +Tiled Compression +***************** + +.. warning:: + This module is in development (so marked as private), anything may change in future releases. + This documentation is provided to aid in further development of this submodule and related functionality. + +This module implements the compression and decompression algorithms, and associated functionality for FITS Tiled Image Compression. +The goal of this submodule is to expose a useful Python API, which different functionality can be built on for reading these files. + +The functionality is roughly split up into the following sections: + +1. Low level compression and decompression functions implemented in cfitsio (for all algorithms other than the GZIP ones, which use the Python stdlib). +2. The quantize and dequantize functions from cfitsio. +3. A Python C-API module which wraps all the compression and quantize cfitsio functions. +4. `numcodecs `__ style ``Codec`` classes for each compression algorithms. +5. `~astropy.io.fits.hdu.compressed._tiled_compression.compress_image_data` and + `~astropy.io.fits.hdu.compressed._tiled_compression.decompress_image_data_section` functions which + are called from `~astropy.io.fits.CompImageHDU`. + + +.. automodapi:: astropy.io.fits.hdu.compressed._tiled_compression + +.. automodapi:: astropy.io.fits.hdu.compressed._codecs + +.. automodapi:: astropy.io.fits.hdu.compressed._quantization diff --git a/docs/io/fits/api/verification.rst b/docs/io/fits/api/verification.rst index b5ed4cdd3dd9..5415d892e478 100644 --- a/docs/io/fits/api/verification.rst +++ b/docs/io/fits/api/verification.rst @@ -2,25 +2,25 @@ .. _verify: -Verification options --------------------- +Verification Options +******************** -There are 5 options for the ``output_verify`` argument of the following methods -of :class:`HDUList`: :meth:`~HDUList.close`, :meth:`~HDUList.writeto`, and -:meth:`~HDUList.flush`, or the :meth:`~_BaseHDU.writeto` method on any HDU -object. In these cases, the verification option is passed to a :meth:`verify` +There are five options for the ``output_verify`` argument of the following +methods of :class:`HDUList`: :meth:`~HDUList.close`, :meth:`~HDUList.writeto`, +and :meth:`~HDUList.flush`, or the ``_BaseHDU.writeto`` method on any HDU +object. In these cases, the verification option is passed to a ``verify`` call within these methods. -exception -^^^^^^^^^ +``'exception'`` +=============== This option will raise an exception if any FITS standard is violated. This is -the default option for output (i.e. when :meth:`~HDUList.writeto`, -:meth:`~HDUList.close`, or :meth:`~HDUList.flush` is called. If a user wants to +the default option for output (i.e., when :meth:`~HDUList.writeto`, +:meth:`~HDUList.close`, or :meth:`~HDUList.flush` is called). If a user wants to overwrite this default on output, the other options listed below can be used. -ignore -^^^^^^ +``'ignore'`` +============ This option will ignore any FITS standard violation. On output, it will write the HDU List content to the output FITS file, whether or not it is conforming @@ -38,14 +38,14 @@ The ``ignore`` option is useful in these situations, for example: No warning message will be printed out. This is like a silent warn (see below) option. -fix -^^^ +``'fix'`` +========= -This option wil try to fix any FITS standard violations. It is not always +This option will try to fix any FITS standard violations. It is not always possible to fix such violations. In general, there are two kinds of FITS -standard violation: fixable and not fixable. For example, if a keyword has a -floating number with an exponential notation in lower case 'e' (e.g. 1.23e11) -instead of the upper case 'E' as required by the FITS standard, it's a fixable +standard violations: fixable and not fixable. For example, if a keyword has a +floating number with an exponential notation in lower case 'e' (e.g., 1.23e11) +instead of the upper case 'E' as required by the FITS standard, it is a fixable violation. On the other hand, a keyword name like ``P.I.`` is not fixable, since it will not know what to use to replace the disallowed periods. If a violation is fixable, this option will print out a message noting it is fixed. @@ -53,20 +53,20 @@ If it is not fixable, it will throw an exception. The principle behind the fixing is do no harm. For example, it is plausible to 'fix' a :class:`Card` with a keyword name like ``P.I.`` by deleting it, but -Astropy will not take such action to hurt the integrity of the data. +``astropy`` will not take such action to hurt the integrity of the data. -Not all fixes may be the "correct" fix, but at least Astropy will try to make -the fix in such a way that it will not throw off other FITS readers. +Not all fixes may be the "correct" fix, but at least ``astropy`` will try to +make the fix in such a way that it will not throw off other FITS readers. -silentfix -^^^^^^^^^ +``'silentfix'`` +=============== Same as fix, but will not print out informative messages. This may be useful in -a large script where the user does not want excessive harmless messages. If the -violation is not fixable, it will still throw an exception. +a large script where the user does not want excessive harmless messages. If +the violation is not fixable, it will still throw an exception. -warn -^^^^ +``'warn'`` +========== This option is the same as the ignore option but will send warning messages. It will not try to fix any FITS standard violations whether fixable or not. diff --git a/docs/io/fits/appendix/faq.rst b/docs/io/fits/appendix/faq.rst index f9f141c2061f..3402284e6929 100644 --- a/docs/io/fits/appendix/faq.rst +++ b/docs/io/fits/appendix/faq.rst @@ -1,555 +1,196 @@ -PyFITS FAQ ----------- +.. _io-fits-faq: + +astropy.io.fits FAQ +******************* .. contents:: General Questions -^^^^^^^^^^^^^^^^^ +================= -What is PyFITS? -""""""""""""""" +What is PyFITS and how does it relate to ``astropy``? +----------------------------------------------------- -PyFITS_ is a library written in, and for use with the Python_ programming -language for reading, writing, and manipulating FITS_ formatted files. It -includes a high-level interface to FITS headers with the ability for high and +PyFITS_ is a library written in, and for use with the |Python| programming +language for reading, writing, and manipulating FITS_ formatted files. It +includes a high-level interface to FITS headers with the ability for high- and low-level manipulation of headers, and it supports reading image and table -data as Numpy_ arrays. It also supports more obscure and non-standard formats +data as |NumPy| arrays. It also supports more obscure and nonstandard formats found in some FITS files. -PyFITS includes two command-line utilities for working with FITS files: -fitscheck, which can verify and write FITS checksums; and fitsdiff, which can -analyze and display the differences between two FITS files. +The `astropy.io.fits` module is identical to PyFITS but with the names changed. +When the development of ``astropy`` began, it was clear that one of the core +requirements would be a FITS reader. Rather than starting from scratch, +PyFITS — being the most flexible FITS reader available for Python — was ported +into ``astropy``. There are plans to gradually phase out PyFITS as a stand-alone +module and deprecate it in favor of `astropy.io.fits`. See more about this in +the next question. Although PyFITS is written mostly in Python, it includes an optional module -written in C that's required to read/write compressed image data. However, +written in C that is required to read/write compressed image data. However, the rest of PyFITS functions without this extension module. -.. _PyFITS: http://www.stsci.edu/institute/software_hardware/pyfits -.. _Python: http://www.python.org -.. _FITS: http://fits.gsfc.nasa.gov/ -.. _Numpy: http://numpy.scipy.org/ +.. _PyFITS: https://github.com/spacetelescope/pyfits +.. _FITS: https://fits.gsfc.nasa.gov/ + What is the development status of PyFITS? -""""""""""""""""""""""""""""""""""""""""" +----------------------------------------- -PyFITS is written and maintained by the Science Software Branch at the `Space +PyFITS was written and maintained by the Science Software Branch at the `Space Telescope Science Institute`_, and is licensed by AURA_ under a `3-clause BSD -license`_ (see `LICENSE.txt`_ in the PyFITS source code). - -PyFITS' current primary developer and active maintainer is `Erik Bray`_, though -patch submissions are welcome from anyone. It has a `Trac site`_ where the -source code can be browsed, and where bug reports may be submitted. The source -code resides primarily in an `SVN repository`_ which allows anonymous -checkouts, though a `Git mirror`_ also exists. PyFITS also has a `GitHub -site`_. - -The current stable release series is 3.0.x. Each 3.0.x release tries to -contain only bug fixes, and to not introduce any significant behavioral or API -changes (though this isn't guaranteed to be perfect). The upcoming 3.1 release -will contain new features and some API changes, though will try maintain as -much backwards-compatibility as possible. After the 3.1 release there may be -further 3.0.x releases for bug fixes only where possible. Older versions of -PyFITS (2.4 and earlier) are no longer actively supported. - -PyFITS is also included as a major component of upcoming Astropy_ project as -the :mod:`astropy.io.fits` module. The goal is for Astropy to eventually serve -as a drop-in replacement for PyFITS (it even includes a legacy-compatibility -mode where the :mod:`astropy.io.fits` module can still be imported as `pyfits`. -However, for the time being PyFITS will still be released as an independent -product as well, until such time that the Astropy project proves successful and -widely-adopted. - -.. _Space Telescope Science Institute: http://www.stsci.edu/ -.. _AURA: http://www.aura-astronomy.org/ -.. _3-clause BSD license: http://en.wikipedia.org/wiki/BSD_licenses#3-clause_license_.28.22New_BSD_License.22_or_.22Modified_BSD_License.22.29 -.. _LICENSE.txt: https://trac.assembla.com/pyfits/browser/trunk/LICENSE.txt -.. _Erik Bray: mailto:embray@stsci.edu -.. _Trac site: https://trac.assembla.com/pyfits/ -.. _SVN repository: https://subversion.assembla.com/svn/pyfits/ -.. _Git mirror: git://github.com/iguananaut/PyFITS.git -.. _GitHub site: https://github.com/iguananaut/PyFITS - - -Build and Installation Questions -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Is PyFITS available on Windows? -""""""""""""""""""""""""""""""" - -Yes--the majority of PyFITS is pure Python, and can be installed and used on -any platform that supports Python (>=2.5). However, PyFITS includes an -optional C extension module for reading/writing compressed image HDUs. As most -Windows systems are not configured to compile C source code, binary installers -are also available for Windows. Though PyFITS can also be installed from -source even on Windows systems without a compiler by disabling the compression -module. See `How do I install PyFITS from source on Windows?`_ for more -details. - -Where is the Windows installer for version X of PyFITS on version Y of Python? -"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" - -Every official PyFITS build for Windows is eventually uploaded to PyPI_. This -includes builds for every major Python release from 2.5.x and up, except for -3.0 as there is no official Numpy release for Python 3.0 on Windows. The one -binary module included in these builds was linked with Numpy 1.6.1, though it -should work with other recent Numpy versions. - -Sometimes the Windows binary installers don't go up immediately after every -PyFITS release. But if they appear missing they should go up within another -business day or two. This has gotten better with recent releases thanks to -some automation. - -.. _PyPI: http://pypi.python.org/pypi/pyfits - -Why is the PyFITS installation failing on Windows? -"""""""""""""""""""""""""""""""""""""""""""""""""" - -The most likely cause of installation failure on Windows is if building/ -installing from source fails due to the lack of a compiler for the optional C -extension module. Such a failure would produce an error that looks something -like:: - - building 'pyfits.compression' extension - error: Unable to find vcvarsall.bat - -Your best bet in cases like this is to install from one of the binary -executable installers available for Windows on PyPI. However, there are still -cases where you may need to install from source: For example, it's difficult to -use the binary installers with virtualenv. See `How do I install PyFITS from -source on Windows?`_ for more detailed instructions on building on Windows. - -For other installation errors not mentioned by this FAQ, please contact -help@stsci.edu with a description of the problem. - -How do I install PyFITS from source on Windows? -""""""""""""""""""""""""""""""""""""""""""""""" - -There are a few options for building/installing PyFITS from source on Windows. - -First of all, as mentioned elsewhere, most of PyFITS is pure-Python. Only the -C extension module for reading/writing compressed images needs to be compiled. -If you don't need compressed image support, PyFITS can be installed without it. - -In future releases this will hopefully be even easier, but for now it's -necessary to edit one file in order to disable the extension module. Locate -the `setup.cfg`_ file at the root of the PyFITS source code. This is the file -that describes what needs to be installed for PyFITS. Find the line that reads -``[extension=pyfits.compression]``. This is the section that lists what needs -to be compiled for the extension module. Comment out every line in the -extension section by prepending it with a ``#`` character (stopping at the -``[build_ext]`` line). It should look like this:: - - ... - scripts = scripts/fitscheck - - #[extension=pyfits.compression] - #sources = - # src/compress.c - # src/fits_hcompress.c - # src/fits_hdecompress.c - # src/fitsio.c - # src/pliocomp.c - # src/compressionmodule.c - # src/quantize.c - # src/ricecomp.c - # src/zlib.c - # src/inffast.c - # src/inftrees.c - # src/trees.c - #include_dirs = numpy - # Squelch a handful of warnings (which actually cause pip to break in tox and - # other environments due to gcc outputting non-ASCII characters in some - # terminals; see python issue6135) - #extra_compile_args = - # -Wno-unused-function - # -Wno-strict-prototypes - - [build_ext] - ... - -With these lines properly commented out, rerun ``python setup.py install``, and -it should skip building/installing the compression module. PyFITS will work -fine with out it, but will issue warnings when encountering a compressed image -that it can't read. - -If you do need to compile the compression module, this can still be done on -Windows with just a little extra work. By default, Python tries to compile -extension modules with the same compiler that Python itself was compiled with. - -To check what compiler Python was built with, the easiest way is to run:: - - python -c "import platform; print platform.python_compiler()" - -For the official builds of recent Python versions this should be something -like:: - - MSC v.1500 32 bit (Intel) - -For unofficial Windows distributions of Python, such as ActiveState, EPD, or -Cygwin, your mileage may vary. - -As it so happens, MSC v.15xx is the compiler version included with Visual -C++ 2008. Luckily, Microsoft distributes a free version of this as `Visual C++ -Express Edition`_. So for building Python extension modules on Windows this is -one of the simpler routes. Just install the free VC++ 2008. It should install -a link to the Start Menu at All Programs->Microsoft Visual C++ Express -Edition->Visual Studio Tools->Visual Studio 2008 Command Prompt. - -If you run that link, it should launch a command prompt with reasonable -environment variables set up for using Visual C++. Then change directories to -your copy of the PyFITS source code and re-run ``python setup.py install``. -You may also need to comment out the ``extra_compile_args`` option in the -``setup.cfg`` file (its value is the two lines under it after the equal sign). -Though the need to manually disable this option for MSC will be fixed in a -future PyFITS version. - -Another option is to use gcc through `MinGW`_, which is in fact how the PyFITS -releases for Windows are currently built. This article provides a good -overview of how to set this up: http://seewhatever.de/blog/?p=217 - -.. _setup.cfg: https://trac.assembla.com/pyfits/browser/trunk/setup.cfg -.. _Visual C++ Express Edition: http://www.microsoft.com/visualstudio/en-us/products/2008-editions/express -.. _MinGW: http://www.mingw.org/ - -Is PyFITS available for Mac OSX? -"""""""""""""""""""""""""""""""" - -Yes, but there is no binary package specifically for OSX (such as a .dmg, for -example). For OSX just download, build, and install the source package. This -is generally easier on OSX than it is on Windows, thanks to the more -developer-friendly environment. - -The only major problem with building on OSX seems to occur for some users of -10.7 Lion, with misconfigured systems. See the next question for details on -that. - -To build PyFITS without the optional compression module, follow the -instructions in `How do I install PyFITS from source on Windows?`_. - -Why is the PyFITS installation failing on OSX Lion (10.7)? -"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" - -There is a common problem that affects all Python packages with C extension -modules (not just PyFITS) for some users of OSX 10.7. What usually occurs is -that when building the package several errors will be output, ending with -something like:: - - unable to execute gcc-4.2: No such file or directory - error: command 'gcc-4.2' failed with exit status 1 - -There are a few other errors like it that can occur. The problem is that when -you build a C extension, by default it will try to use the same compiler that -your Python was built with. In this case, since you're using the 32-bit -universal build of Python it's trying to use the older gcc-4.2 and is trying -to build with PPC support, which is no longer supported in Xcode. - -In this case the best solution is to install the x86-64 build of Python for -OSX (http://www.python.org/ftp/python/2.7.2/python-2.7.2-macosx10.6.dmg for -2.7.2). In fact, this is the build version officially supported for use on -Lion. Other, unofficial Python builds such as from `MacPorts`_ may also work. - -.. _MacPorts: http://astrofrog.github.com/macports-python/ - -How do I find out what version of PyFITS I have installed? -"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" - -To output the PyFITS version from the command line, run:: - - $ python -c 'import pyfits; print(pyfits.__version__)' - -When PyFITS is installed with stsci_python, it is also possible to check the -installed SVN revision by importing ``pyfits.svn_version``. Then use -``dir(pyfits.svn_version)`` to view a list of available attributes. A -feature like this will be available soon in standalone versions of PyFITS as -well. - -How do I run the tests for PyFITS? -"""""""""""""""""""""""""""""""""" - -Currently the best way to run the PyFITS tests is to download the source code, -either from a source release or from version control, and to run the tests out -of the source. It is not necessary to install PyFITS to run the tests out of -the source code. - -The PyFITS tests require `nose`_ to run. nose can be installed on any Python -version using pip or easy_install. See the nose documentation for more -details. - -With nose installed, it is simple to run the tests on Python 2.x:: - - $ python setup.py nosetests - -If PyFITS has not already been built, this will build it automatically, then -run the tests. This does not cause PyFITS to be installed. - -On Python 3.x the situation is a little more complicated. This is due to the -fact that PyFITS' source code is not Python 3-compatible out of the box, but -has to be run through the 2to3 converter. Normally when you build/install -PyFITS on Python 3.x, the 2to3 conversion is performed automatically. -Unfortunately, nose does not know to use the 2to3'd source code, and will -instead try to import and test the unconverted source code. - -To work around this, it is necessary to first build PyFITS (which will run the -source through 2to3):: - - $ python setup.py build - -Then run the ``nosetests`` command, but pointing it to the ``build`` tree -where the 2to3'd source code and tests reside, using the ``-w`` switch:: - - $ python setup.py nosetests -w build/lib.linux-x86_64-3.2 - -where the exact path of the ``build/lib.*`` directory will vary depending on -your platform and Python version. - -.. _nose: http://readthedocs.org/docs/nose/en/latest/ +license`_. -How can I build a copy of the PyFITS documentation for my own use? -"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +It is now exclusively developed as a component of ``astropy`` +(`astropy.io.fits`) rather than as a stand-alone module. There are a few +reasons for this: The first is simply to reduce development effort; the +overhead of maintaining both PyFITS *and* `astropy.io.fits` in separate code +bases is nontrivial. The second is that there are many features of ``astropy`` +(units, tables, etc.) from which the `astropy.io.fits` module can benefit +greatly. Since PyFITS is already integrated into ``astropy``, it makes more +sense to continue development there rather than make ``astropy`` a dependency +of PyFITS. -First of all, it's worth pointing out that the documentation for the latest -version of PyFITS can always be downloaded in `PDF form -`_ or browsed -online in `HTML `_. There are also plans -to make the docs for older versions of PyFITS, as well as up-to-date -development docs available online. +PyFITS' past primary developer and active maintainer was Erik Bray. There +is a `GitHub project`_ for PyFITS, but PyFITS is not actively developed anymore +so patches and issue reports should be posted on the Astropy issue tracker. -Otherwise, to build your own version of the docs either for offline use, or to -build the development version of the docs there are a few reqirements. The -most import requirement is `Sphinx`_, which is the toolkit used to generate -the documentation. Use ``pip install sphinx`` or ``easy_install sphinx`` to -install Sphinx. Using pip or easy_install will install the correct versions -of Sphinx's basic dependencies, which include docutils, Jinja2, and Pygments. +The current (and last) stable release is 3.4.0. -Next, the docs require STScI's custom Sphinx theme, `stsci.sphinxext`_. It's -a simple pure-Python pacakge and can be installed with pip or easy_install. - -The next requirement is `numpydoc`_, which is not normally installed with -Numpy itself. Install it with pip or easy_install. Numpy is also required, -though it is of course a requirement of PyFITS itself. - -Finally, it is necessary to have `matplotlib`_, specifically for -matplotlib.sphinxext. This is perhaps the most onerous requirement if you do -not already have it instaled. Please refer to the matplotlib documentation for -details on downloading and installing matplotlib. - -It is also necessary to install PyFITS itself in order to generate the API -documentation. For this reason, it is a good idea to install Sphinx and -PyFITS into a `virtualenv`_ in order to build the development version of the -docs (see below). - -With all the requirements installed, change directories into the `docs/` -directory in the PyFITS source code, and run:: - - $ make html - -to build the HTML docs, which will be output to `build/html`. To build the -docs in other formats, please refer to the Sphinx documentation. - -To summarize, assuming that you already have Numpy and Matplotlib on your -Python installation, perform the following steps from within the PyFITS source -code:: - - $ virtualenv --system-site-packages pyfits-docs - $ source pyfits-docs/bin/activate - $ pip install sphinx - $ pip install numpydoc - $ pip install stsci.sphinxext - $ python setup.py install pyfits - $ cd docs/ - $ make html - - -.. _Sphinx: http://sphinx.pocoo.org/ -.. _stsci.sphinxext: http://pypi.python.org/pypi/stsci.sphinxext -.. _numpydoc: http://pypi.python.org/pypi/numpydoc -.. _matplotlib: http://matplotlib.sourceforge.net/ -.. _virtualenv: http://pypi.python.org/pypi/virtualenv +.. _Space Telescope Science Institute: https://www.stsci.edu/ +.. _AURA: https://www.aura-astronomy.org/ +.. _3-clause BSD license: https://en.wikipedia.org/wiki/BSD_licenses#3-clause_license_.28.22New_BSD_License.22_or_.22Modified_BSD_License.22.29 +.. _GitHub project: https://github.com/spacetelescope/PyFITS Usage Questions -^^^^^^^^^^^^^^^ +=============== -Something didn't work as I expected. Did I do something wrong? -""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +Something did not work as I expected. Did I do something wrong? +--------------------------------------------------------------- -Possibly. But if you followed the documentation and things still did not work +Possibly. But if you followed the documentation and things still did not work as expected, it is entirely possible that there is a mistake in the -documentation, a bug in the code, or both. So feel free to report it as a -bug. There are also many, many corner cases in FITS files, with new ones -discovered almost every week. PyFITS is always improving, but does not -support all cases perfectly. There are some features of the FITS format -(scaled data, for example) that are difficult to support correctly and can -sometimes cause unexpected behavior. +documentation, a bug in the code, or both. So feel free to report it as a bug. +There are also many, many corner cases in FITS files, with new ones discovered +almost every week. `astropy.io.fits` is always improving, but does not support +all cases perfectly. There are some features of the FITS format (scaled data, +for example) that are difficult to support correctly and can sometimes cause +unexpected behavior. For the most common cases, however, such as reading and updating FITS headers, -images, and tables, PyFITS should be very stable and well-tested. Before -every PyFITS release it is ensured that all its tests pass on a variety of -platforms, and those tests cover the majority of use-cases (until new -corner cases are discovered). +images, and tables, `astropy.io.fits` is very stable and well-tested. Before +every ``astropy`` release it is ensured that all of its tests pass on a variety +of platforms, and those tests cover the majority of use cases (until new corner +cases are discovered). + -PyFITS crashed and output a long string of code. What do I do? -""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +``astropy`` crashed and output a long string of code. What do I do? +------------------------------------------------------------------- -This listing of code is what is knows as a `stack trace`_ (or in Python -parlance a "traceback"). When an unhandled exception occurs in the code, +This listing of code is what is known as a `stack trace`_ (or in Python +parlance a "traceback"). When an unhandled exception occurs in the code causing the program to end, this is a way of displaying where the exception occurred and the path through the code that led to it. -As PyFITS is meant to be used as a piece in other software projects, some -exceptions raised by PyFITS are by design. For example, one of the most -common exceptions is a `KeyError` when an attempt is made to read the value of -a non-existent keyword in a header:: +As ``astropy`` is meant to be used as a piece in other software projects, some +exceptions raised by ``astropy`` are by design. For example, one of the most +common exceptions is a `KeyError` when an attempt is made to read +the value of a nonexistent keyword in a header:: - >>> import pyfits - >>> h = pyfits.Header() + >>> from astropy.io import fits + >>> h = fits.Header() >>> h['NAXIS'] Traceback (most recent call last): - File "", line 1, in - File "/path/to/pyfits/header.py", line 125, in __getitem__ - return self._cards[self._cardindex(key)].value - File "/path/to/pyfits/header.py", line 1535, in _cardindex - raise KeyError("Keyword %r not found." % keyword) + ... KeyError: "Keyword 'NAXIS' not found." This indicates that something was looking for a keyword called "NAXIS" that -does not exist. If an error like this occurs in some other software that uses -PyFITS, it may indicate a bug in that software, in that it expected to find a -keyword that didn't exist in a file. +does not exist. If an error like this occurs in some other software that uses +``astropy``, it may indicate a bug in that software, in that it expected to +find a keyword that did not exist in a file. Most "expected" exceptions will output a message at the end of the traceback -giving some idea of why the exception occurred and what to do about it. The +giving some idea of why the exception occurred and what to do about it. The more vague and mysterious the error message in an exception appears, the more -likely that it was caused by a bug in PyFITS. So if you're getting an -exception and you really don't know why or what to do about it, feel free to +likely that it was caused by a bug in ``astropy``. So if you are getting an +exception and you really do not know why or what to do about it, feel free to report it as a bug. -.. _stack trace: http://en.wikipedia.org/wiki/Stack_trace +.. _stack trace: https://en.wikipedia.org/wiki/Stack_trace -Why does opening a file work in CFITSIO, ds9, etc. but not in PyFITS? -""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" + +Why does opening a file work in CFITSIO, ds9, etc., but not in ``astropy``? +--------------------------------------------------------------------------- As mentioned elsewhere in this FAQ, there are many unusual corner cases when -dealing with FITS files. It's possible that a file should work, but isn't -support due to a bug. Sometimes it's even possible for a file to work in an -older version of PyFITS, but not a newer version due to a regression that -isn't tested for yet. +dealing with FITS files. It is possible that a file should work, but is not +supported due to a bug. Sometimes it is even possible for a file to work in an +older version of ``astropy``, but not a newer version due to a regression +that has not been tested for yet. Another problem with the FITS format is that, as old as it is, there are many -conventions that appear in files from certain sources that do not meet the -FITS standard. And yet they are so common-place that it is necessary to -support them in any FITS readers. CONTINUE cards are one such example. There -are non-standard conventions supported by PyFITS that are not supported by -CFITSIO and vice-versa. You may have hit one of those cases. - -If PyFITS is having trouble opening a file, a good way to rule out whether not -the problem is with PyFITS is to run the file through the `fitsverify`_. For -smaller files you can even use the `online FITS verifier`_. These use CFITSIO -under the hood, and should give a good indication of whether or not there is -something erroneous about the file. If the file is malformatted, fitsverify -will output errors and warnings. - -If fitsverify confirms no problems with a file, and PyFITS is still having -trouble opening it (especially if it produces a traceback) then it's likely -there is a bug in PyFITS. - -.. _fitsverify: http://heasarc.gsfc.nasa.gov/docs/software/ftools/fitsverify/ -.. _online FITS verifier: http://fits.gsfc.nasa.gov/fits_verify.html - -How do I turn off the warning messages PyFITS keeps outputting to my console? -""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" - -PyFITS uses Python's built-in `warnings`_ subsystem for informating about -exceptional conditions in the code that are recoverable, but that the user may -way to be informed of. One of the most common warnings in PyFITS occurs when -updating a header value in such a way that the comment must be truncated to -preserve space:: - - Card is too long, comment is truncated. - -Any console output generated by PyFITS can be assumed to be from the warnings -subsystem. Fortunately there are two easy ways to quiet these warnings: - - 1. Using the `-W option`_ to the ``python`` executable. Just start Python - like:: - - $ python -Wignore - - or for short:: - - $ python -Wi - - and all warning output will be silenced. +conventions that appear in files from certain sources that do not meet the FITS +standard. And yet they are so commonplace that it is necessary to support +them in any FITS readers. CONTINUE cards are one such example. There are +nonstandard conventions supported by ``astropy`` that are not supported by +CFITSIO and possibly vice versa. You may have hit one of those cases. - 2. Warnings can be silenced programatically from anywhere within a script. - For example, to disable all warnings in a script, add something like:: +If ``astropy`` is having trouble opening a file, a good way to rule out whether +not the problem is with ``astropy`` is to run the file through the `fitsverify`_ +program. For smaller files you can even use the `online FITS verifier`_. +These use CFITSIO under the hood, and should give a good indication of whether +or not there is something erroneous about the file. If the file is +malformatted, fitsverify will output errors and warnings. - import warnings - warnings.filterwarnings('ignore') +If fitsverify confirms no problems with a file, and ``astropy`` is still having +trouble opening it (especially if it produces a traceback), then it is possible +there is a bug in ``astropy``. - Another option, instead of ``ignore`` is ``once``, which causes any warning - to be output only once within the session, rather than repeatedly (such as in - a loop). There are many more ways to filter warnings with ``-W`` and the - warnings module. For example, it is possible to silence only specific - warning messages. Please refer to the Python documentation for more details, - or ask at help@stsci.edu. +.. _fitsverify: https://heasarc.gsfc.nasa.gov/docs/software/ftools/fitsverify/ +.. _online FITS verifier: https://fits.gsfc.nasa.gov/fits_verify.html -.. _warnings: http://docs.python.org/library/warnings.html -.. _-W option: http://docs.python.org/using/cmdline.html#cmdoption-W -How can I check if my code is using deprecated PyFITS features? -""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +How do I turn off the warning messages ``astropy`` outputs to my console? +------------------------------------------------------------------------- -PyFITS 3.0 included a major reworking of the code and some of the APIs. Most -of the differences are just renaming functions to use a more consistent naming -scheme. For example the ``createCard()`` function was renamed to -``create_card()`` for consistency with a ``lower_case_underscore`` naming -scheme for functions. - -There are a few other functions and attributes that were deprecated either -because they were renamed to something simpler or more consistent, or because -they were redundant or replaced. - -Eventually all deprecated features will be removed in future PyFITS versions -(though there will be significant warningsin advance). It is best to check -whether your code is using deprecatd features sooner rather than later. +``astropy`` uses Python's built-in `warnings`_ subsystem for informing about +exceptional conditions in the code that are recoverable, but that the user may +want to be informed of. One of the most common warnings in `astropy.io.fits` +occurs when updating a header value in such a way that the comment must be +truncated to preserve space:: -On Python 2.5, all deprecation warnigns are displayed by default, so you may -have already discovered them. However, on Python 2.6 and up, deprecation -warnings are *not* displayed by default. To show all deprecation warnings, -start Python like:: + Card is too long, comment is truncated. - $ python -Wd +Any console output generated by ``astropy`` can be assumed to be from the +warnings subsystem. See Astropy's documentation on the :ref:`python-warnings` +for more information on how to control and quiet warnings. -Most deprecation issues can be fixed with a simple find/replace. The warnings -displayed will let you know how to replace the old interface. +.. _warnings: https://docs.python.org/3/library/warnings.html -If you have a lot of old code that was written for older versions of PyFITS it -would be worth doing this. PyFITS 3.1 introduces a significant rewrite of the -Header interface, and contains even more deprecations. -What convention does PyFITS use for indexing, such as of image coordinates? -""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +What convention does ``astropy`` use for indexing, such as of image coordinates? +-------------------------------------------------------------------------------- -All arrays and sequences in PyFITS use a zero-based indexing scheme. For +All arrays and sequences in ``astropy`` use a zero-based indexing scheme. For example, the first keyword in a header is ``header[0]``, not ``header[1]``. -This is in accordance with Python itself, as well as C, on which PyFITS is +This is in accordance with Python itself, as well as C, on which Python is based. This may come as a surprise to veteran FITS users coming from IRAF, where -1-based indexing is typically used, due to its origins in FORTRAN. +1-based indexing is typically used, due to its origins in Fortran. -Likewise, the top-left pixel in an N x N array is ``data[0,0]``. The indices +Likewise, the top-left pixel in an N x N array is ``data[0,0]``. The indices for 2-dimensional arrays are row-major order, in that the first index is the -row number, and the second index is the column number. Or put in terms of -axes, the first axis is the y-axis, and the second axis is the x-axis. This is -the opposite of column-major order, which is used by FORTRAN and hence FITS. +row number, and the second index is the column number. Or put in terms of +axes, the first axis is the y-axis, and the second axis is the x-axis. This is +the opposite of column-major order, which is used by Fortran and hence FITS. For example, the second index refers to the axis specified by NAXIS1 in the FITS header. In general, for N-dimensional arrays, row-major orders means that the right-most axis is the one that varies the fastest while moving over the -array data linearly. For example, the 3-dimensional array:: +array data linearly. For example, the 3-dimensional array:: [[[1, 2], [3, 4]], @@ -564,163 +205,176 @@ Since 2 immediately follows 1, you can see that the right-most (or inner-most) axis is the one that varies the fastest. The discrepancy in axis-ordering may take some getting used to, but it is a -necessary evil. Since most other Python and C software assumes row-major -ordering, trying to enforce column-major ordering in arrays returned by PyFITS -is likely to cause more difficulties than it's worth. - -How do I open a very large image that won't fit in memory? -"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +necessary evil. Since most other Python and C software assumes row-major +ordering, trying to enforce column-major ordering in arrays returned by +``astropy`` is likely to cause more difficulties than it is worth. -Prior to PyFITS 3.1, when the data portion of an HDU is accessed, the data is -read into memory in its entirety. For example:: - >>> hdul = pyfits.open('myimage.fits') - >>> hdul[0].data - ... +How do I open a very large image that will not fit in memory? +------------------------------------------------------------- -reads the entire image array from disk into memory. For very large images or -tables this is clearly undesirable, if not impossible given the available -resources. +`astropy.io.fits.open` has an option to access the data portion of an +HDU by memory mapping using `mmap`_. In ``astropy`` this is used by default. -However, ``pyfits.open()`` has an option to access the data portion of an HDU -by memory mapping using `mmap`_. What this means is that accessing the data -as in the example above only reads portions of the data into memory on demand. -For example, if I request just a slice of the image, such as -``hdul[0].data[100:200]``, then just rows 100-200 will be read into memory. -This happens transparently, as though the entire image were already in memory. -This works the same way for tables. For most cases this is your best bet for -working with large files. +What this means is that accessing the data as in the example above only reads +portions of the data into memory on demand. For example, if we request just a +slice of the image, such as ``hdul[0].data[100:200]``, then only rows 100-200 +will be read into memory. This happens transparently, as though the entire +image were already in memory. This works the same way for tables. For most +cases this is your best bet for working with large files. -To use memory mapping, just add the ``memmap=True`` argument to -``pyfits.open()``. +To ensure use of memory mapping, add the ``memmap=True`` argument to +:func:`fits.open `. Likewise, using ``memmap=False`` will +force data to be read entirely into memory. -In PyFITS 3.1, the mmap support is improved enough that ``memmap=True`` is the -default for all ``pyfits.open()`` calls. The default can also be controlled -through an environment variable called ``PYFITS_USE_MEMMAP``. Setting this to -``0`` will disable mmap by default. +The default can also be controlled through a configuration option called +``USE_MEMMAP``. Setting this to ``0`` will disable mmap by default. Unfortunately, memory mapping does not currently work as well with scaled image data, where BSCALE and BZERO factors need to be applied to the data to -yield physical values. Currently this requires enough memory to hold the +yield physical values. Currently this requires enough memory to hold the entire array, though this is an area that will see improvement in the future. -An alternative, which currently only works for image data (that is, -non-tables) is the sections interface. It is largely replaced by the better -support for memmap, but may still be useful on systems with more limited -virtual-memory space, such as on 32-bit systems. Support for scaled image -data is flakey with sections too, though that will be fixed. See `the PyFITS -documentation -`_ -for more details on working with sections. +An alternative, which currently only works for image data (that is, non-tables) +is the sections interface. It is largely replaced by the better support for +mmap, but may still be useful on systems with more limited virtual memory +space, such as on 32-bit systems. Support for scaled image data is flaky with +sections too, though that will be fixed. See the documentation on :ref:`image +sections ` for more details on using this interface. + +.. _mmap: https://en.wikipedia.org/wiki/Mmap -.. _mmap: http://en.wikipedia.org/wiki/Mmap +.. _sphx_glr_generated_examples_io_skip_create-large-fits.py: How can I create a very large FITS file from scratch? -""""""""""""""""""""""""""""""""""""""""""""""""""""" +----------------------------------------------------- + +This example demonstrates how to create a large file (larger than will fit in +memory) from scratch using `astropy.io.fits`. + +Normally to create a single image FITS file one would do something like: + +.. code:: python + + import os + import numpy as np + from astropy.io import fits + + data = np.zeros((40000, 40000), dtype=np.float64) + hdu = fits.PrimaryHDU(data=data) -This is a very common issue, but unfortunately PyFITS does not come with any -built-in facilities for creating large files (larger than will fit in memory) -from scratch (though it may in the future). +Then use the `astropy.io.fits.writeto()` method to write out the new file to disk: -Normally to create a single image FITS file one would do something like:: +.. code:: python - >> data = numpy.zeros((40000, 40000), dtype=numpy.float64) - >> hdu = pyfits.PrimaryHDU(data=data) - >> hdu.writeto('large.fits') + hdu.writeto("large.fits") -However, a 40000 x 40000 array of doubles is nearly twelve gigabytes! Most -systems won't be able to create that in memory just to write out to disk. In +However, a 40000 x 40000 array of doubles is nearly twelve gigabytes! Most +systems won't be able to create that in memory just to write out to disk. In order to create such a large file efficiently requires a little extra work, and a few assumptions. -First, it is helpful to anticpate about how large (as in, how many keywords) -the header will have in it. FITS headers must be written in 2880 byte -blocks--large enough for 36 keywords per block (including the END keyword in -the final block). Typical headers have somewhere between 1 and 4 blocks, +First, it is helpful to anticipate about how large (as in, how many keywords) +the header will have in it. FITS headers must be written in 2880 byte +blocks, large enough for 36 keywords per block (including the END keyword in +the final block). Typical headers have somewhere between 1 and 4 blocks, though sometimes more. Since the first thing we write to a FITS file is the header, we want to write enough header blocks so that there is plenty of padding in which to add new -keywords without having to resize the whole file. Say you want the header to -use 4 blocks by default. Then, excluding the END card which PyFITS will add -automatically, create the header and pad it out to 36 * 4 cards like so:: - - >>> data = numpy.zeros((100, 100), dtype=numpy.float64) - # This is a stub array that we'll be using the initialize the HDU; its - # exact size is irrelevant, as long as it has the desired number of - # dimensions - >>> hdu = pyfits.PrimaryHDU(data=data) - >>> header = hdu.header - >>> while len(header) < (36 * 4 - 1): - ... header.append() # Adds a blank card to the end +keywords without having to resize the whole file. Say you want the header to +use 4 blocks by default. Then, excluding the END card which Astropy will add +automatically, create the header and pad it out to 36 * 4 cards. + +Create a stub array to initialize the HDU; its +exact size is irrelevant, as long as it has the desired number of +dimensions: + +.. code:: python + + data = np.zeros((100, 100), dtype=np.float64) + hdu = fits.PrimaryHDU(data=data) + header = hdu.header + while len(header) < (36 * 4 - 1): + header.append() # Adds a blank card to the end Now adjust the NAXISn keywords to the desired size of the array, and write -*only* the header out to a file. Using the ``hdu.writeto()`` method will -cause PyFITS to "helpfully" reset the NAXISn keywords to match the size of the -dummy array:: +only the header out to a file. Using the ``hdu.writeto()`` method will cause +astropy to "helpfully" reset the NAXISn keywords to match the size of the +dummy array. That is because it works hard to ensure that only valid FITS +files are written. Instead, we can write just the header to a file using the +`astropy.io.fits.Header.tofile` method: - >>> header['NAXIS1'] = 40000 - >>> header['NAXIS2'] = 40000 - >>> header.tofile('large.fits') +.. code:: python -Finally, we need to grow out the end of the file to match the length of the -data (plus the length of the header). This can be done very efficiently on + header["NAXIS1"] = 40000 + header["NAXIS2"] = 40000 + header.tofile("large.fits") + +Finally, grow out the end of the file to match the length of the +data (plus the length of the header). This can be done very efficiently on most systems by seeking past the end of the file and writing a single byte, -like so:: +like so: + +.. code:: python - >>> with open('large.fits', 'rb+') as fobj: - ... fobj.seek(len(header.tostring()) + (40000 * 40000 * 8) - 1) - ... # The -1 is to account for the final byte that we are about to - ... # write - ... fobj.write('\0') + with open("large.fits", "rb+") as fobj: + # Seek past the length of the header, plus the length of the + # Data we want to write. + # 8 is the number of bytes per value, i.e. abs(header['BITPIX'])/8 + # (this example is assuming a 64-bit float) + file_length = len(header.tostring()) + (40000 * 40000 * 8) + # FITS files must be a multiple of 2880 bytes long; the final -1 + # is to account for the final byte that we are about to write. + file_length = ((file_length + 2880 - 1) // 2880) * 2880 - 1 + fobj.seek(file_length) + fobj.write(b"\0") + +More generally, this can be written: + +.. code:: python + + shape = tuple(header[f"NAXIS{ii}"] for ii in range(1, header["NAXIS"] + 1)) + with open("large.fits", "rb+") as fobj: + file_length = len(header.tostring()) + (np.prod(shape) * np.abs(header["BITPIX"] // 8)) + file_length = ((file_length + 2880 - 1) // 2880) * 2880 - 1 + fobj.seek(file_length) + fobj.write(b"\0") On modern operating systems this will cause the file (past the header) to be -filled with zeros out to the ~12GB needed to hold a 40000 x 40000 image. On +filled with zeros out to the ~12GB needed to hold a 40000 x 40000 image. On filesystems that support sparse file creation (most Linux filesystems, but not -HFS+) this is a very fast, efficient operation. On other systems your mileage -may vary. +the HFS+ filesystem used by most Macs) this is a very fast, efficient +operation. On other systems your mileage may vary. This isn't the only way to build up a large file, but probably one of the -safest. This method can also be used to create large multi-extension FITS +safest. This method can also be used to create large multi-extension FITS files, with a little care. -For creating very large tables, this method may also be used. Though it can -be difficult to determine ahead of time how many rows a table will need. In -general, use of PyFITS is discouraged for the creation and manipulation of -large tables. The FITS format itself is not designed for efficient on-disk or -in-memory manipulation of table structures. For large, heavy-duty table data -it might be better too look into using `HDF5`_ through the `PyTables`_ -library. - -PyTables makes use of Numpy under the hood, and can be used to write binary -table data to disk in the same format required by FITS. It is then possible -to serialize your table to the FITS format for distribution. At some point +For creating very large tables, this method may also be used, though it can be +difficult to determine ahead of time how many rows a table will need. In +general, use of the `astropy.io.fits` module is currently discouraged for the +creation and manipulation of large tables. The FITS format itself is not +designed for efficient on-disk or in-memory manipulation of table structures. +For large, heavy-duty table data it might be better too look into using `HDF5`_ +through the `PyTables`_ library. The :ref:`Astropy Table ` +interface can provide an abstraction layer between different on-disk table +formats as well (for example, for converting a table between FITS and HDF5). + +PyTables makes use of NumPy under the hood, and can be used to write binary +table data to disk in the same format required by FITS. It is then possible +to serialize your table to the FITS format for distribution. At some point this FAQ might provide an example of how to do this. -.. _HDF5: http://www.hdfgroup.org/HDF5/ -.. _PyTables: http://www.pytables.org/moin - -How do I create a multi-extension FITS file from scratch? -""""""""""""""""""""""""""""""""""""""""""""""""""""""""" - -When you open a FITS file with ``pyfits.open()``, a ``pyfits.HDUList`` object -is returned, which holds all the HDUs in the file. This ``HDUList`` class is -a subclass of Python's builtin ``list``, and can be created from scratch and -used as such:: +.. _HDF5: https://www.hdfgroup.org/HDF5/ +.. _PyTables: http://www.pytables.org/ - >>> new_hdul = pyfits.HDUList() - >>> new_hdul.append(pyfits.ImageHDU()) - >>> new_hdul.append(pyfits.ImageHDU()) - >>> new_hdul.writeto('test.fits') - -That will create a new multi-extension FITS file with two empty IMAGE -extensions (a default PRIMARY HDU is prepended automatically if one was not -provided manually). +.. _fits-scaled-data-faq: Why is an image containing integer data being converted unexpectedly to floats? -""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +------------------------------------------------------------------------------- -If the header for your image contains non-trivial values for the optional +If the header for your image contains nontrivial values for the optional BSCALE and/or BZERO keywords (that is, BSCALE != 1 and/or BZERO != 0), then the raw data in the file must be rescaled to its physical values according to the formula:: @@ -728,78 +382,468 @@ the formula:: physical_value = BZERO + BSCALE * array_value As BZERO and BSCALE are floating point values, the resulting value must be a -float as well. If the original values were 16-bit integers, the resulting -values are single-precision (32-bit) floats. If the original values were -32-bit integers the resulting values are double-precision (64-bit floats). +float as well. If the original values were 16-bit integers, the resulting +values are single-precision (32-bit) floats. If the original values were +32-bit integers, the resulting values are double-precision (64-bit floats). -This automatic scaling can easily catch you of guard if you're not expecting -it, because it doesn't happen until the data portion of the HDU is accessed -(to allow things like updating the header without rescaling the data). For +This automatic scaling can easily catch you off guard if you are not expecting +it, because it does not happen until the data portion of the HDU is accessed +(to allow for things like updating the header without rescaling the data). For example:: - >>> hdul = pyfits.open('scaled.fits') - >>> image = hdul['SCI', 1] + >>> fits_scaledimage_filename = fits.util.get_testdata_filepath('scale.fits') + + >>> hdul = fits.open(fits_scaledimage_filename) + >>> image = hdul[0] >>> image.header['BITPIX'] - 32 + 16 >>> image.header['BSCALE'] - 2.0 + 0.045777764213996 >>> data = image.data # Read the data into memory - >>> data.dtype - dtype('float64') # Got float64 despite BITPIX = 32 (32-bit int) + >>> data.dtype.name # Got float32 despite BITPIX = 16 (16-bit int) + 'float32' >>> image.header['BITPIX'] # The BITPIX will automatically update too - -64 + -32 >>> 'BSCALE' in image.header # And the BSCALE keyword removed False The reason for this is that once a user accesses the data they may also -manipulate it and perform calculations on it. If the data were forced to -remain as integers, a great deal of precision is lost. So it is best to err +manipulate it and perform calculations on it. If the data were forced to +remain as integers, a great deal of precision is lost. So it is best to err on the side of not losing data, at the cost of causing some confusion at first. -If the data must be returned to integers before saving, use the ``scale()`` -method:: +If the data must be returned to integers before saving, use the +`~astropy.io.fits.ImageHDU.scale` method:: >>> image.scale('int32') >>> image.header['BITPIX'] 32 + >>> hdul.close() + +Alternatively, if a file is opened with ``mode='update'`` along with the +``scale_back=True`` argument, the original BSCALE and BZERO scaling will +be automatically reapplied to the data before saving. Usually this is +not desirable, especially when converting from floating point values back to +unsigned integer values. But this may be useful in cases where the raw +data needs to be modified corresponding to changes in the physical values. + +To prevent rescaling from occurring at all (which is good for updating headers +— even if you do not intend for the code to access the data, it is good to err +on the side of caution here), use the ``do_not_scale_image_data`` argument when +opening the file:: -To prevent rescaling from occurring at all (good for updating headers--even if -you don't intend for the code to access the data, it's good to err on the side -of caution here), use the ``do_not_scale_image_data`` argument when opening -the file:: + >>> hdul = fits.open(fits_scaledimage_filename, do_not_scale_image_data=True) + >>> image = hdul[0] + >>> image.data.dtype.name + 'int16' + >>> hdul.close() - >>> hdul = pyfits.open('scaled.fits', do_not_scale_image_data=True) - >>> image = hdul['SCI', 1] - >>> image.data.dtype - dtype('int32') Why am I losing precision when I assign floating point values in the header? -"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +---------------------------------------------------------------------------- -The FITS standard allows two formats for storing floating-point numbers in a -header value. The "fixed" format requires the ASCII representation of the +The FITS standard allows two formats for storing floating point numbers in a +header value. The "fixed" format requires the ASCII representation of the number to be in bytes 11 through 30 of the header card, and to be -right-justified. This leaves a standard number of characters for any comment +right-justified. This leaves a standard number of characters for any comment string. The fixed format is not wide enough to represent the full range of values that -can be stored in a 64-bit float with full precision. So FITS also supports a +can be stored in a 64-bit float with full precision. So FITS also supports a "free" format in which the ASCII representation can be stored anywhere, using the full 70 bytes of the card (after the keyword). -Currently PyFITS only supports writing fixed format (it can read both +Currently ``astropy`` only supports writing fixed format (it can read both formats), so all floating point values assigned to a header are stored in the -fixed format. There are plans to add support for more flexible formatting. +fixed format. There are plans to add support for more flexible formatting. -In the meantime it is possible to add or update cards by manually formatting -the card image:: +In the meantime, it is possible to add or update cards by manually formatting +the card image from a string, as it should appear in the FITS file:: - >>> c = pyfits.Card.fromstring('FOO = 1234567890.123456789') - >>> h = pyfits.Header() + >>> c = fits.Card.fromstring('FOO = 1234567890.123456789') + >>> h = fits.Header() >>> h.append(c) >>> h FOO = 1234567890.123456789 -As long as you don't assign new values to 'FOO' via ``h['FOO'] = 123``, PyFITS -will maintain the header value exactly as you formatted it. +As long as you do not assign new values to 'FOO' via ``h['FOO'] = 123``, will +maintain the header value exactly as you formatted it (as long as it is valid +according to the FITS standard). + + +Why is reading rows out of a FITS table so slow? +------------------------------------------------ + +Underlying every table data array returned by `astropy.io.fits` is a ``numpy`` +`~numpy.recarray` which is a ``numpy`` array type specifically for representing +structured array data (i.e., a table). As with normal image arrays, ``astropy`` +accesses the underlying binary data from the FITS file via mmap (see the +question "`What performance differences are there between astropy.io.fits and +fitsio?`_" for a deeper explanation of this). The underlying mmap is then +exposed as a `~numpy.recarray` and in general this is a very efficient way to +read the data. + +However, for many (if not most) FITS tables it is not all that simple. For +many columns there are conversions that have to take place between the actual +data that is "on disk" (in the FITS file) and the data values that are returned +to the user. For example, FITS binary tables represent boolean values +differently from how ``numpy`` expects them to be represented, "Logical" columns +need to be converted on the fly to a format ``numpy`` (and hence the user) can +understand. This issue also applies to data that is linearly scaled via the +``TSCALn`` and ``TZEROn`` header keywords. + +Supporting all of these "FITS-isms" introduces a lot of overhead that might +not be necessary for all tables, but are still common nonetheless. That is +not to say it cannot be faster even while supporting the peculiarities of +FITS — CFITSIO, for example, supports all of the same features but is orders of +magnitude faster. ``astropy`` could do much better here too, and there are many +known issues causing slowdown. There are plenty of opportunities for speedups, +and patches are welcome. In the meantime, for high-performance applications +with FITS tables some users might find the ``fitsio`` library more to their +liking. + + +I am opening many FITS files in a loop and getting OSError: Too many open files +------------------------------------------------------------------------------- + +Say you have some code like: + +.. code:: python + + from astropy.io import fits + + for filename in filenames: + with fits.open(filename) as hdul: + for hdu in hdul: + hdu_data = hdul.data + # Do some stuff with the data + + +The details may differ, but the qualitative point is that the data to many +HDUs and/or FITS files are being accessed in a loop. This may result in +an exception like:: + + Traceback (most recent call last): + File "", line 2, in + OSError: [Errno 24] Too many open files: 'my_data.fits' + +As explained in the :ref:`note on working with large files `, +because ``astropy`` uses mmap by default to read the data in a FITS file, even +if you correctly close a file with :meth:`HDUList.close +` a handle is kept open to that file so +that the memory-mapped data array can still continue to be read transparently. + +The way ``numpy`` supports mmap is such that the file mapping is not closed +until the overlying `~numpy.ndarray` object has no references to it and is freed +memory. However, when looping over a large number of files (or even just HDUs) +rapidly, this may not happen immediately. Or in some cases if the HDU object +persists, the data array attached to it may persist too. The recommended +workaround is to *manually* delete the ``.data`` attribute on the HDU object so +that the `~numpy.ndarray` reference is freed and the mmap can be closed: + +.. code:: python + + from astropy.io import fits + + for filename in filenames: + with fits.open(filename) as hdul: + for hdu in hdul: + hdu_data = hdul.data + # Do some stuff with the data + # ... + # Don't need the data anymore; delete all references to it + # so that it can be garbage collected + del hdu_data + del hdu.data + + +In some extreme cases files are opened and closed fast enough that Python's +garbage collector does not free them (and hence free the file handles) often +enough. To mitigate this, your code can manually force a garbage collection +by calling :func:`gc.collect` at the end of the loop. + +In a future release it will be more convenient to automatically perform this +sort of cleanup when closing FITS files, where needed. + +Using header['NAXIS2'] += 1 does not add another row to my Table +---------------------------------------------------------------- + +``NAXIS`` and similar keywords are FITS *structural* keywords and should not be +modified by the user. They are automatically updated by :mod:`astropy.io.fits` +when checking the validity of the data and headers. See :ref:`structural_keywords` +for more information. + +To add rows to a table, you can modify the actual data. + +Comparison with Other FITS Readers +================================== + +What is the difference between astropy.io.fits and fitsio? +---------------------------------------------------------- + +The `astropy.io.fits` module (originally PyFITS) is a "pure Python" FITS +reader in that all of the code for parsing the FITS file format is in Python, +though ``numpy`` is used to provide access to the FITS data via the +`~numpy.ndarray` interface. `astropy.io.fits` currently also accesses the +`CFITSIO `_ to support the +FITS Tile Compression convention, but this feature is optional. It does not +use CFITSIO outside of reading compressed images. + +`fitsio `_, on the other hand, is a Python +wrapper for the CFITSIO library. All of the heavy lifting of reading the FITS +format is handled by CFITSIO, while ``fitsio`` provides a better way to use +object-oriented API, including providing a ``numpy`` interface to FITS files +read from CFITSIO. Much of it is written in C (to provide the interface between +Python and CFITSIO), and the rest is in Python. The Python end mostly +provides the documentation and user-level API. + +Because ``fitsio`` wraps CFITSIO it inherits most of its strengths and +weaknesses, though it has an added strength of providing a more convenient +API than if one were to use CFITSIO directly. + + +Why did Astropy adopt PyFITS as its FITS reader instead of fitsio? +------------------------------------------------------------------ + +When the Astropy Project was first started it was clear from the start that +one of its core components should be a submodule for reading and writing FITS +files, as many other components would be likely to depend on this +functionality. At the time, the ``fitsio`` package was in its infancy (it +goes back to roughly 2011) while PyFITS had already been established (going +back to before the year 2000). It was already a mature package with support +for the vast majority of FITS files found in the wild, including outdated +formats such as "Random Groups" FITS files still used extensively in the +radio astronomy community. + +Although many aspects of PyFITS' interface have evolved over the years, much +of it has also remained the same, and is already familiar to astronomers +working with FITS files in Python. Most of if not all existing training +materials were also based around PyFITS. PyFITS was developed at STScI, which +also put forward significant resources to develop Astropy, with an eye toward +integrating Astropy into STScI's own software stacks. As most of the Python +software at STScI uses PyFITS, it was the only practical choice for making that +transition. + +Finally, although CFITSIO (and by extension ``fitsio``) can read any FITS files +that conform to the FITS standard, it does not support all of the nonstandard +conventions that have been added to FITS files in the wild. While it does have +some support for some of these conventions (such as CONTINUE cards and, to a +limited extent, HIERARCH cards), it is not easy to add support for other +conventions to a large and complex C codebase. + +PyFITS' object-oriented design makes supporting nonstandard conventions +somewhat easier in most cases, and as such PyFITS can be more flexible in the +types of FITS files it can read and return *useful* data from. This includes +better support for files that fail to meet the FITS standard, but still contain +useful data that should be readable enough to correct any violations of the +FITS standard. For example, a common error in non-English speaking regions is +to insert non-ASCII characters into FITS headers. This is not a valid FITS +file, but should still be readable in some sense. Supporting structural errors +such as this is more difficult in CFITSIO which assumes a more rigid structure. + + +What performance differences are there between astropy.io.fits and fitsio? +-------------------------------------------------------------------------- + +There are two main performance areas to look at: reading/parsing FITS headers +and reading FITS data (image-like arrays as well as tables). + +In the area of headers, ``fitsio`` is significantly faster in most cases. This +is due in large part to the (almost) pure C implementation (due to the use of +CFITSIO), but also due to fact that it is more rigid and does not support as +many local conventions and other special cases as `astropy.io.fits` tries to +support in its pure Python implementation. + +That said, the difference is small and only likely to be a bottleneck either +when opening files containing thousands of HDUs, or reading the headers out +of thousands of FITS files in succession (in either case the difference is +not even an order of magnitude). + +Where data is concerned the situation is a little more complicated, and +requires some understanding of how `astropy.io.fits` is implemented versus +CFITSIO and ``fitsio``. First, it is important to understand how they differ in +terms of memory management. + +`astropy.io.fits` uses mmap, by default, to provide access to the raw +binary data in FITS files. Mmap is a system call (or in most cases these days +a wrapper in your libc for a lower-level system call) which allows user-space +applications to essentially do the same thing your OS is doing when it uses a +pagefile (swap space) for virtual memory: it allows data in a file on disk to +be paged into physical memory one page (or in practice usually several pages) +at a time on an as-needed basis. These cached pages of the file are also +accessible from all processes on the system, so multiple processes can read +from the same file with little additional overhead. In the case of reading +over all of the data in the file, the performance difference between using mmap +versus reading the entire data into physical memory at once can vary widely +between systems, hardware, and depending on what else is happening on the +system at the moment, but mmap is almost always going to be better. + +In principle, it requires more overhead since accessing each page will result in +a page fault and the system requires more requests to the disk. But in +practice, the OS will optimize this pretty aggressively, especially for the most +common case of sequential access — also in reality, reading the entire thing +into memory is still going to result in a whole lot of page faults too. For +random access, having all of the data in physical memory is always going to be +best, though with mmap it is usually going to be pretty good too. (Most users +do not normally access all of the data in a file in a totally random order — +usually a few sections of it will be accessed most frequently, so the OS will +keep those pages in physical memory as best it can.) For the most general case +of reading FITS files (or most large data on disk) this is therefore the best +choice, especially for casual users, and is hence enabled by default. + +CFITSIO/``fitsio``, on the other hand, does not assume the existence of +technologies like mmap and page caching. Thus it implements its own LRU cache +of I/O buffers that store sections of FITS files read from disk in memory in +FITS' famous 2880 byte chunk size. The I/O buffers are used heavily in +particular for keeping the headers in memory. Though for large data reads (for +example, reading an entire image from a file), it *does* bypass the cache and +instead does a read directly from disk into a user-provided memory buffer. + +However, even when CFITSIO reads direct from the file, this is still largely +less efficient than using mmap. Normally when your OS reads a file from disk, +it caches as much of that read as it can in physical memory (in its page cache) +so that subsequent access to those same pages does not require a subsequent +expensive disk read. This happens when using mmap too, since the data has to +be copied from disk into RAM at some point. The difference is that when using +mmap to access the data, the program is able to read that data *directly* out +of the OS's page cache (as long as it is only being read). On the other hand, +when reading data from a file into a local buffer such as with fread(), the +data is first read into the page cache (if not already present) and then copied +from the page cache into the local buffer. So every read performs at least one +additional memory copy per page read (requiring twice as much physical memory, +and possibly lots of paging if the file is large and pages need to dropped from +the cache). + +The user API for CFITSIO usually works by having the user allocate a memory +buffer large enough to hold the image/table they want to read (or at least the +section they are interested in). There are some helper functions for +determining the appropriate amount of space to allocate. Then you pass in +a pointer to your buffer and CFITSIO handles all of the reading (usually using +the process described above), and copies the results into your user buffer. For +large reads, it reads directly from the file into your buffer, though if the +data needs to be scaled it makes a stop in CFITSIO's own buffer first, then +writes the rescaled values out to the user buffer (if rescaling has been +requested). Regardless, this means that if your program wishes to hold an +entire image in memory at once it will use as much RAM as the size of the +data. For most applications it is better (and sufficient) to work on +smaller sections of the data, but this requires extra complexity. Using mmap +on the other hand makes managing this complexity more efficient. + +An informal test demonstrates this difference. This test was performed on four +simple FITS images (one of which is a cube) of dimensions 256x256, 1024x1024, +4096x4096, and 256x1024x1024. Each image was generated before the test and +filled with randomized 64-bit floating point values. A similar test was +performed using both `astropy.io.fits` and ``fitsio``. A handle to the FITS +file is opened using each library's basic semantics, and then the entire data +array of the files is copied into a temporary array in memory (for example, if +we were blitting the image to a video buffer). For ``astropy`` the test is +written: + +.. code:: python + + def read_test_astropy(filename): + with fits.open(filename, memmap=True) as hdul: + data = hdul[0].data + c = data.copy() + +The test was timed in IPython on a Linux system with kernel version 2.6.32, a +6-core Intel Xeon X5650 CPU clocked at 2.67 GHz per core, and 11.6 GB of RAM +using: + +.. code:: python + + for filename in filenames: + print(filename) + %timeit read_test_astropy(filename) + +where ``filenames`` is just a list of the aforementioned generated sample +files. The results were:: + + 256x256.fits + 1000 loops, best of 3: 1.28 ms per loop + 1024x1024.fits + 100 loops, best of 3: 4.24 ms per loop + 4096x4096.fits + 10 loops, best of 3: 60.6 ms per loop + 256x1024x1024.fits + 1 loops, best of 3: 1.15 s per loop + +For ``fitsio`` the test was: + +.. code:: python + + def read_test_fitsio(filename): + with fitsio.FITS(filename) as f: + data = f[0].read() + c = data.copy() + +This was also run in a loop over all of the sample files, producing the +results:: + + 256x256.fits + 1000 loops, best of 3: 476 Âĩs per loop + 1024x1024.fits + 100 loops, best of 3: 12.2 ms per loop + 4096x4096.fits + 10 loops, best of 3: 136 ms per loop + 256x1024x1024.fits + 1 loops, best of 3: 3.65 s per loop + +It should be made clear that the sample files were rewritten with new random +data between the ``astropy`` test and the fitsio test, so they were not reading +the same data from the OS's page cache. Fitsio was much faster on the small +(256x256) image because in that case the time is dominated by parsing the +headers. As already explained, this is much faster in CFITSIO. However, as +the data size goes up and the header parsing no longer dominates the time, +`astropy.io.fits` using mmap is roughly twice as fast. This discrepancy is +almost entirely due to it requiring roughly half as many in-memory copies +to read the data, as explained earlier. That said, more extensive benchmarking +could be very interesting. + +This is also not to say that `astropy.io.fits` does better in all cases. There +are some cases where it is currently blown away by fitsio. See the subsequent +question. + + +Why is fitsio so much faster than ``astropy`` at reading tables? +---------------------------------------------------------------- + +In many cases it is not: there is either no difference, or it may be a little +faster in ``astropy`` depending on what you are trying to do with the table and +what types of columns or how many columns the table has. There are some +cases, however, where ``fitsio`` can be radically faster, mostly for reasons +explained above in "`Why is reading rows out of a FITS table so slow?`_" + +In principle a table is no different from, say, an array of pixels. But +instead of pixels each element of the array is some kind of record structure +(for example, two floats, a boolean, and a 20-character string field). Just as +a 64-bit float is an 8 byte record in an array, a row in such a table can be +thought of as a 37 byte (in the case of the previous example) record in a 1D +array of rows. So in principle everything that was explained in the answer to +the question "`What performance differences are there between astropy.io.fits +and fitsio?`_" applies just as well to tables as it does to any other array. + +However, FITS tables have many additional complexities that sometimes preclude +streaming the data directly from disk, and instead require transformation from +the on-disk FITS format to a format more immediately useful to the user. A +common example is how FITS represents boolean values in binary tables. +Another significantly more complicated example, is variable length arrays. + +As explained in "`Why is reading rows out of a FITS table so slow?`_", +`astropy.io.fits` does not currently handle some of these cases as +efficiently as it could, in particular in cases where a user only wishes to +read a few rows out of a table. Fitsio, on the other hand, has a better +interface for copying one row at a time out of a table and performing the +necessary transformations on that row *only*, rather than on the entire column +or columns that the row is taken from. As such, for many cases ``fitsio`` gets +much better performance and should be preferred for many performance-critical +table operations. + +Fitsio also exposes a microlanguage (implemented in CFITSIO) for making +efficient SQL-like queries of tables (single tables only though — no joins or +anything like that). This format, described in the `CFITSIO documentation +`_ can +in some cases perform more efficient selections of rows than might be possible +with ``numpy`` alone, which requires creating an intermediate mask array in +order to perform row selection. diff --git a/docs/io/fits/appendix/header_transition.rst b/docs/io/fits/appendix/header_transition.rst new file mode 100644 index 000000000000..9df6c08f5419 --- /dev/null +++ b/docs/io/fits/appendix/header_transition.rst @@ -0,0 +1,419 @@ +.. currentmodule:: astropy.io.fits +.. doctest-skip-all + +.. _header-transition-guide: + +********************************* +Header Interface Transition Guide +********************************* + +.. note:: + + This guide was originally included with the release of PyFITS 3.1, and + still references PyFITS in many places, though the examples have been + updated for ``astropy.io.fits``. It is still useful here for informational + purposes, though Astropy has always used the PyFITS 3.1 Header interface. + +PyFITS v3.1 included an almost complete rewrite of the :class:`Header` +interface. Although the new interface is largely compatible with the old +interface (whether due to similarities in the design, or backwards-compatibility +support), there are enough differences that a full explanation of the new +interface is merited. + +Background +========== + +Prior to 3.1, PyFITS users interacted with FITS headers by way of three +different classes: :class:`Card`, ``CardList``, and :class:`Header`. + +The Card class represents a single header card with a keyword, value, and +comment. It also contains all of the machinery for parsing FITS header cards, +given the 80-character string, or "card image" read from the header. + +The CardList class is actually a subclass of Python's `list` built-in. It was +meant to represent the actual list of cards that make up a header. That is, it +represents an ordered list of cards in the physical order that they appear in +the header. It supports the usual list methods for inserting and appending new +cards into the list. It also supports `dict`-like keyword access, where +``cardlist['KEYWORD']`` would return the first card in the list with the given +keyword. + +A lot of the functionality for manipulating headers was actually buried in the +CardList class. The Header class was more of a wrapper around CardList that +added a little bit of abstraction. It also implemented a partial dict-like +interface, though for Headers a keyword lookup returned the header value +associated with that keyword, not the Card object, and almost every +method on the Header class was just performing some operations on the +underlying CardList. + +The problem was that there were certain things a user could *only* do by +directly accessing the CardList, such as look up the comments on a card or +access cards that have duplicate keywords, such as HISTORY. Another +long-standing misfeature was that slicing a Header object actually returned a +CardList object, rather than a new Header. For all but the simplest use cases, +working with CardList objects was largely unavoidable. + +But it was realized that CardList is really an implementation detail +not representing any element of a FITS file distinct from the header itself. +Users familiar with the FITS format know what a header is, but it is not clear +how a "card list" is distinct from that, or why operations go through the +Header object, while some have to be performed through the CardList. + +So the primary goal of this redesign was to eliminate the ``CardList`` class +altogether, and make it possible for users to perform all header manipulations +directly through :class:`Header` objects. It also tried to present headers as +similarly as possible to a more familiar data structure — an ordered mapping +(or :class:`~collections.OrderedDict` in Python) for ease of use by new users +less familiar with the FITS format, though there are still many added +complexities for dealing with the idiosyncrasies of the FITS format. + + +Deprecation Warnings +==================== + +A few older methods on the :class:`Header` class have been marked as deprecated, +either because they have been renamed to a more `PEP 8`_-compliant name, or +because have become redundant due to new features. To check if your code is +using any deprecated methods or features, run your code with ``python -Wd``. +This will output any deprecation warnings to the console. + +Two of the most common deprecation warnings related to Headers are: + +- ``Header.has_key``: this has been deprecated since PyFITS 3.0, + just as Python's `dict.has_key` is deprecated. To check a key's presence + in a mapping object like `dict` or :class:`Header`, use the ``key in d`` + syntax. This has long been the preference in Python. + +- ``Header.ascardlist`` and ``Header.ascard``: these were used to + access the ``CardList`` object underlying a header. They should still + work, and return a skeleton CardList implementation that should support most + of the old CardList functionality. But try removing as much of this as + possible. If direct access to the :class:`Card` objects making up a header + is necessary, use :attr:`Header.cards`, which returns an iterator over the + cards. More on that below. + +.. _PEP 8: https://www.python.org/dev/peps/pep-0008/ + +New Header Design +================= + +The new :class:`Header` class is designed to work as a drop-in replacement for +a `dict` via `duck typing`_. That is, although it is not a subclass of `dict`, +it implements all of the same methods and interfaces. In particular, it is +similar to an :class:`~collections.OrderedDict` in that the order of insertions +is preserved. However, Header also supports many additional features and +behaviors specific to the FITS format. It should also be noted that while the +old Header implementation also had a dict-like interface, it did not implement +the *entire* dict interface as the new Header does. + +Although the new Header is used like a dict/mapping in most cases, it also +supports a `list` interface. The list-like interface is a bit idiosyncratic in +that in some contexts the Header acts like a list of values, in others like a +list of keywords, and in a few contexts like a list of :class:`Card` objects. +This may be the most difficult aspect of the new design, but there is a logic +to it. + +As with the old Header implementation, integer index access is supported: +``header[0]`` returns the value of the first keyword. However, the +:meth:`Header.index` method treats the header as though it is a list of +keywords and returns the index of a given keyword. For example:: + + >>> header.index('BITPIX') + 2 + +:meth:`Header.count` is similar to `list.count` and also takes a keyword as +its argument:: + + >>> header.count('HISTORY') + 20 + +A good rule of thumb is that any item access using square brackets ``[]`` +returns *value* in the header, whether using keyword or index lookup. Methods +like :meth:`~Header.index` and :meth:`~Header.count` that deal with the order +and quantity of items in the Header generally work on keywords. Finally, +methods like :meth:`~Header.insert` and :meth:`~Header.append` that add new +items to the header work on cards. + +Aside from the list-like methods, the new Header class works very similarly to +the old implementation for most basic use cases and should not present too many +surprises. There are differences, however: + +- As before, the Header() initializer can take a list of :class:`Card` objects + with which to fill the header. However, now any iterable may be used. It is + also important to note that *any* Header method that accepts :class:`Card` + objects can also accept 2-tuples or 3-tuples in place of Cards. That is, + either a ``(keyword, value, comment)`` tuple or a ``(keyword, value)`` tuple + (comment is assumed blank) may be used anywhere in place of a Card object. + This is even preferred, as it involves less typing. For example:: + + >>> from astropy.io import fits + >>> header = fits.Header([('A', 1), ('B', 2), ('C', 3, 'A comment')]) + >>> header + A = 1 + B = 2 + C = 3 / A comment + +- As demonstrated in the previous example, the ``repr()`` for a Header (that is, + the text that is displayed when entering a Header object in the Python + console as an expression), shows the header as it would appear in a FITS file. + This inserts newlines after each card so that it is readable regardless of + terminal width. It is *not* necessary to use ``print header`` to view this. + Entering ``header`` displays the header contents as it would appear in the + file (sans the END card). + +- ``len(header)`` is now supported (previously it was necessary to do + ``len(header.ascard)``). This returns the total number of cards in the + header, including blank cards, but excluding the END card. + +- FITS supports having duplicate keywords, although they are generally in error + except for commentary keywords like COMMENT and HISTORY. PyFITS now supports + reading, updating, and deleting duplicate keywords; instead of using the + keyword by itself, use a ``(keyword, index)`` tuple. For example, + ``('HISTORY', 0)`` represents the first HISTORY card, ``('HISTORY', 1)`` + represents the second HISTORY card, and so on. In fact, when a keyword is + used by itself, it is shorthand for ``(keyword, 0)``. It is now possible to + delete an accidental duplicate like so:: + + >>> del header[('NAXIS', 1)] + + This will remove an accidental duplicate NAXIS card from the header. + +- Even if there are duplicate keywords, keyword lookups like + ``header['NAXIS']`` will always return the value associated with the first + copy of that keyword, with one exception: commentary keywords like COMMENT + and HISTORY are expected to have duplicates. So ``header['HISTORY']``, for + example, returns the whole sequence of HISTORY values in the correct order. + This list of values can be sliced arbitrarily. For example, to view the last + three history entries in a header:: + + >>> hdulist[0].header['HISTORY'][-3:] + reference table oref$laf13367o_pct.fits + reference table oref$laf13369o_apt.fits + Heliocentric correction = 16.225 km/s + +- Subscript assignment can now be used to add new keywords to the header. Just + as with a normal `dict`, ``header['NAXIS'] = 1`` will either update the NAXIS + keyword if it already exists, or add a new NAXIS keyword with a value of + ``1`` if it does not exist. In the old interface this would return a + `KeyError` if NAXIS did not exist, and the only way to add a new + keyword was through the update() method. + + By default, new keywords added in this manner are added to the end of the + header, with a few FITS-specific exceptions: + + * If the header contains extra blank cards at the end, new keywords are added + before the blanks. + + * If the header ends with a list of commentary cards — for example, a sequence + of HISTORY cards — those are kept at the end, and new keywords are inserted + before the commentary cards. + + * If the keyword is a commentary keyword like COMMENT or HISTORY (or an empty + string for blank keywords), a *new* commentary keyword is always added and + appended to the last commentary keyword of the same type. For example, + HISTORY keywords are always placed after the last history keyword:: + + >>> header = fits.Header() + >>> header['COMMENT'] = 'Comment 1' + >>> header['HISTORY'] = 'History 1' + >>> header['COMMENT'] = 'Comment 2' + >>> header['HISTORY'] = 'History 2' + >>> header + COMMENT Comment 1 + COMMENT Comment 2 + HISTORY History 1 + HISTORY History 2 + + These behaviors represent a sensible default behavior for keyword assignment, + and the same behavior as :meth:`~Header.update` in the old Header + implementation. The default behaviors may still be bypassed through the use + of other assignment methods like the :meth:`Header.set` and + :meth:`Header.append` methods described later. + +- It is now also possible to assign a value and a comment to a keyword + simultaneously using a tuple:: + + >>> header['NAXIS'] = (2, 'Number of axis') + + This will update the value and comment of an existing keyword, or add a new + keyword with the given value and comment. + +- There is a new :attr:`Header.comments` attribute which lists all of the + comments associated with keywords in the header (not to be confused with + COMMENT cards). This allows viewing and updating the comments on specific + cards:: + + >>> header.comments['NAXIS'] + Number of axis + >>> header.comments['NAXIS'] = 'Number of axes' + >>> header.comments['NAXIS'] + Number of axes + +- When deleting a keyword from a header, do not assume that the keyword already + exists. In the old Header implementation, this action would silently do + nothing. For backwards-compatibility, it is still okay to delete a + nonexistent keyword, but a warning will be raised. In the future this + *will* be changed so that trying to delete a nonexistent keyword raises a + `KeyError`. This is for consistency with the behavior of Python dicts. So + unless you know for certain that a keyword exists before deleting it, it is + best to do something like:: + + >>> try: + ... del header['BITPIX'] + ... except KeyError: + ... pass + + Or if you prefer to look before you leap:: + + >>> if 'BITPIX' in header: + ... del header['BITPIX'] + +- ``del header`` now supports slices. For example, to delete the last three + keywords from a header:: + + >>> del header[-3:] + +- Two headers can now be compared for equality — previously no two Header + objects were the same. Now they compare as equal if they contain the exact + same content. That is, this requires strict equality. + +- Two headers can now be added with the '+' operator, which returns a copy of + the left header extended by the right header with :meth:`~Header.extend`. + Assignment addition is also possible. + +- The Header.update() method used commonly with the old Header API has been + renamed to :meth:`Header.set`. The primary reason for this change is very + simple: Header implements the `dict` interface, which already has a method + called update(), but that behaves differently from the old Header.update(). + + The details of the new update() can be read in the API docs, but it is very + similar to `dict.update`. It also supports backwards compatibility with the + old update() by analysis of the arguments passed to it, so existing code will + not break immediately. However, this *will* cause a deprecation warning to + be output if they are enabled. It is best, for starters, to replace all + update() calls with set(). Recall, also, that direct assignment is now + possible for adding new keywords to a header. So by and large the only + reason to prefer using :meth:`Header.set` is its capability of inserting or + moving a keyword to a specific location using the ``before`` or ``after`` + arguments. + +- Slicing a Header with a slice index returns a new Header containing only + those cards contained in the slice. As mentioned earlier, it used to be that + slicing a Header returned a card list — something of a misfeature. In + general, objects that support slicing ought to return an object of the same + type when you slice them. + + Likewise, wildcard keywords used to return a CardList object — now they + return a new Header similarly to a slice. For example:: + + >>> header['NAXIS*'] + + returns a new header containing only the NAXIS and NAXISn cards from the + original header. + +.. _duck typing: https://en.wikipedia.org/wiki/Duck_typing + + +Transition Tips +=============== + +The above may seem like a lot, but the majority of existing code using PyFITS +to manipulate headers should not need to be updated, at least not immediately. +The most common operations still work the same. + +As mentioned above, it would be helpful to run your code with ``python -Wd`` to +enable deprecation warnings — that should be a good idea of where to look to +update your code. + +If your code needs to be able to support older versions of PyFITS +simultaneously with PyFITS 3.1, things are slightly trickier, but not by +much — the deprecated interfaces will not be removed for several more versions +because of this. + +- The first change worth making, which is supported by any PyFITS version in + the last several years, is to remove any use of ``Header.has_key`` and + replace it with ``keyword in header`` syntax. It is worth making this change + for any dict as well, since `dict.has_key` is deprecated. Running the + following regular expression over your code may help with most (but not all) + cases:: + + s/([^ ]+)\.has_key\(([^)]+)\)/\2 in \1/ + +- If possible, replace any calls to Header.update() with Header.set() (though + do not bother with this if you need to support older PyFITS versions). Also, + if you have any calls to Header.update() that can be replaced with simple + subscript assignments (e.g., ``header['NAXIS'] = (2, 'Number of axes')``) do + that too, if possible. + +- Find any code that uses ``header.ascard`` or ``header.ascardlist()``. First + ascertain whether that code really needs to work directly on Card objects. + If that is definitely the case, go ahead and replace those with + ``header.cards`` — that should work without too much fuss. If you do need to + support older versions, you may keep using ``header.ascard`` for now. + +- In the off chance that you have any code that slices a header, it is best to + take the result of that and create a new Header object from it. For + example:: + + >>> new_header = fits.Header(old_header[2:]) + + This avoids the problem that in PyFITS <= 3.0 slicing a Header returns a + CardList by using the result to initialize a new Header object. This will + work in both cases (in PyFITS 3.1, initializing a Header with an existing + Header just copies it, à la `list`). + +- As mentioned earlier, locate any code that deletes keywords with ``del`` and + make sure they either look before they leap (``if keyword in header:``) or + ask forgiveness (``try/except KeyError:``). + +Other Gotchas +------------- + +- As mentioned above, it is not necessary to enter ``print header`` to display + a header in an interactive Python prompt. Entering ``>>> header`` + by itself is sufficient. Using ``print`` usually will *not* display the + header readably, because it does not include line breaks between the header + cards. The reason is that Python has two types of string representations. + One is returned when a user calls ``str(header)``, which happens automatically + when you ``print`` a variable. In the case of the Header class this actually + returns the string value of the header as it is written literally in the + FITS file, which includes no line breaks. + + The other type of string representation happens when one calls + ``repr(header)``. The `repr` of an object is meant to be a useful + string "representation" of the object; in this case the contents of the + header but with line breaks between the cards and with the END card and + trailing padding stripped off. This happens automatically when + a user enters a variable at the Python prompt by itself without a ``print`` + call. + +- The current version of the FITS Standard (3.0) states in section 4.2.1 + that trailing spaces in string values in headers are not significant and + should be ignored. PyFITS < 3.1 *did* treat trailing spaces as significant. + For example, if a header contained: + + KEYWORD1= 'Value ' + + then ``header['KEYWORD1']`` would return the string ``'Value '`` exactly, + with the trailing spaces intact. The new Header interface fixes this by + automatically stripping trailing spaces, so that ``header['KEYWORD1']`` would + return just ``'Value'``. + + There is, however, one convention used by the IRAF CCD mosaic task for + representing its TNX World Coordinate System and ZPX World Coordinate System + nonstandard WCS that uses a series of keywords in the form ``WATj_nnn``, + which store a text description of coefficients for a nonlinear distortion + projection. It uses its own microformat for listing the coefficients as a + string, but the string is long, and thus broken up into several of these + ``WATj_nnn`` keywords. Correct recombination of these keywords requires + treating all whitespace literally. This convention either overlooked or + predated the prescribed treatment of whitespace in the FITS standard. + + To get around this issue, a global variable ``fits.STRIP_HEADER_WHITESPACE`` + was introduced. Temporarily setting + ``fits.STRIP_HEADER_WHITESPACE.set(False)`` before reading keywords affected + by this issue will return their values with all trailing whitespace intact. + + A future version of PyFITS may be able to detect use of conventions like this + contextually and behave according to the convention, but in most cases the + default behavior of PyFITS is to behave according to the FITS Standard. diff --git a/docs/io/fits/appendix/history.rst b/docs/io/fits/appendix/history.rst index a254868efe1d..695847648861 100644 --- a/docs/io/fits/appendix/history.rst +++ b/docs/io/fits/appendix/history.rst @@ -1,9 +1,11 @@ +.. doctest-skip-all + astropy.io.fits History -======================= +*********************** -Prior to its inclusion in Astropy, the `astropy.io.fits` package was a stand- -alone package called `PyFITS`_. Though for the time being active development -is continuing on PyFITS, that development is also being merged into Astropy. +Prior to its inclusion in Astropy, the `astropy.io.fits` package was a +stand-alone package called `PyFITS`_. PyFITS is no longer actively maintained, and +its development is now solely in Astropy. This page documents the release history of PyFITS prior to its merge into Astropy. @@ -12,11 +14,816 @@ Astropy. :local: -3.1.0 (unreleased) --------------------- +3.4.0 (2016-01-29) +================== + +This is the last released version of PyFITS as a standalone package. + + +3.3.0 (2014-07-17) +================== + +New Features +------------ + +- Added new verification options ``fix+ignore``, ``fix+warn``, + ``fix+exception``, ``silentfix+ignore``, ``silentfix+warn``, and + ``silentfix+exception`` which give more control over how to report fixable + errors as opposed to unfixable errors. See the "Verification" section in + the PyFITS documentation for more details. + +API Changes +----------- + +- The ``pyfits.new_table`` function is now fully deprecated (though will not + be removed for a long time, considering how widely it is used). + + Instead please use the more explicit ``pyfits.BinTableHDU.from_columns`` to + create a new binary table HDU, and the similar + ``pyfits.TableHDU.from_columns`` to create a new ASCII table. These + otherwise accept the same arguments as ``pyfits.new_table`` which is now + just a wrapper for these. + +- The ``.fromstring`` classmethod of each HDU type has been simplified such + that, true to its namesake, it only initializes an HDU from a string + containing its header *and* data. (spacetelescope/PyFITS#64) + +- Fixed an issue where header wildcard matching (for example + ``header['DATE*']``) can be used to match *any* characters that might appear + in a keyword. Previously this only matched keywords containing characters + in the set ``[0-9A-Za-z_]``. Now this can also match a hyphen ``-`` and any + other characters, as some conventions like ``HIERARCH`` and record-valued + keyword cards allow a wider range of valid characters than standard FITS + keywords. + +- This will be the *last* release to support the following APIs that have been + marked deprecated since PyFITS v3.1: + + - The ``CardList`` class, which was part of the old header implementation. + + - The ``Card.key`` attribute. Use ``Card.keyword`` instead. + + - The ``Card.cardimage`` and ``Card.ascardimage`` attributes. Use simply + ``Card.image`` or ``str(card)`` instead. + + - The ``create_card`` factory function. Simply use the normal ``Card`` + constructor instead. + + - The ``create_card_from_string`` factory function. Use ``Card.fromstring`` + instead. + + - The ``upper_key`` function. Use ``Card.normalize_keyword`` method instead + (this is not unlikely to be used outside of PyFITS itself, but it was + technically public API). + + - The usage of ``Header.update`` with ``Header.update(keyword, value, + comment)`` arguments. ``Header.update`` should only be used analogously + to ``dict.update``. Use ``Header.set`` instead. + + - The ``Header.ascard`` attribute. Use ``Header.cards`` instead for a list + of all the ``Card`` objects in the header. + + - The ``Header.rename_key`` method. Use ``Header.rename_keyword`` instead. + + - The ``Header.get_history`` method. Use ``header['HISTORY']`` instead + (normal keyword lookup). + + - The ``Header.get_comment`` method. Use ``header['COMMENT']`` instead. + + - The ``Header.toTxtFile`` method. Use ``header.totextfile`` instead. + + - The ``Header.fromTxtFile`` method. Use ``Header.fromtextfile`` instead. + + - The ``pyfits.tdump`` and ``tcreate`` functions. Use ``pyfits.tabledump`` + and ``pyfits.tableload`` respectively. + + - The ``BinTableHDU.tdump`` and ``tcreate`` methods. Use + ``BinTableHDU.dump`` and ``BinTableHDU.load`` respectively. + + - The ``txtfile`` argument to the ``Header`` constructor. Use + ``Header.fromfile`` instead. + + - The ``startColumn`` and ``endColumn`` arguments to the ``FITS_record`` + constructor. These are unlikely to be used by any user code. + + These deprecated interfaces will be removed from the development version of + PyFITS following the v3.3 release (they will still be available in any + v3.3.x bugfix releases, however). + +Other Changes and Additions +--------------------------- + +- PyFITS has switched to a unified code base which supports Python 2.5 through + 3.4 simultaneously without translation. This *shouldn't* have any + significant performance impacts, but please report if anything seems + noticeably slower. As a reminder, support for Python 2.5 will be ended + after PyFITS 3.3.x. + +- Warnings for deprecated APIs in PyFITS are now always displayed by default. + This is in line with a similar change made recently to Astropy: + https://github.com/astropy/astropy/pull/1871 + To disable PyFITS deprecation warnings in scripts one may call + ``pyfits.ignore_deprecation_warnings()`` after importing PyFITS. + +- ``Card`` objects have a new ``is_blank`` attribute which returns ``True`` if + the card represents a blank card (no keyword, value, or comment) and + ``False`` otherwise. + +Bug Fixes +--------- + +- Fixed a regression where it was not possible to save an empty "compressed" + image to a file (in this case there is nothing to compress, hence the + quotes, but trying to do so caused a crash). (spacetelescope/PyFITS#69) + +- Fixed a regression that may have been introduced in v3.2.1 with writing + compressed image HDUs, particularly compressed images using a non-empty + GZIP_COMPRESSED_DATA column. (spacetelescope/#71) + + +3.2.4 (2014-06-02) +================== + +- Fixed a regression where multiple consecutive calls of the ``writeto`` + method on the same HDU but to different files could lead to corrupt data or + crashes on the subsequent calls after the first. (spacetelescope/PyFITS#40) + + +3.2.3 (2014-05-14) +================== + +- Nominal support for Python 3.4. + +- Fixed a bug with using the ``tabledump`` and ``tableload`` functions with + tables containing array columns (columns in which each element is an array + instead of a single scalar value). (spacetelescope/PyFITS#22) + +- Fixed an issue where PyFITS allowed newline characters in header values and + comments. (spacetelescope/PyFITS#51) + +- Fixed pickling of ``FITS_rec`` (table data) objects. + (spacetelescope/PyFITS#53) + +- Improved behavior when writing large compressed images on OSX by removing an + unnecessary check for platform architecture. (spacetelescope/PyFITS#57) + +- Allow reading FITS files from file-like objects that do not have a + ``.closed`` attribute (and as such may not even have an "open" vs. "closed" + concept). (spacetelescope/PyFITS#56) + +- Fixed duplicate insertion of commentary keywords on compressed image + headers. (spacetelescope/PyFITS#58) + +- Fixed minor issue with comparison of header commentary card values. + (spacetelescope/PyFITS#59) + + +3.1.6 (2014-05-14) +================== + +- Nominal support for Python 3.4. + +- Fixed a bug with using the ``tabledump`` and ``tableload`` functions with + tables containing array columns (columns in which each element is an array + instead of a single scalar value). (Backported from 3.2.3) + +- Fixed an issue where PyFITS allowed newline characters in header values and + comments. (Backported from 3.2.3) + +- Fixed pickling of ``FITS_rec`` (table data) objects. + (Backported from 3.2.3) + +- Improved behavior when writing large compressed images on OSX by removing an + unnecessary check for platform architecture. (Backported from 3.2.3) + +- Allow reading FITS files from file-like objects that do not have a + ``.closed`` attribute (and as such may not even have an "open" vs. "closed" + concept). (Backported from 3.2.3) + +- Fixed minor issue with comparison of header commentary card values. + (Backported from 3.2.3) + + +3.2.2 (2014-03-25) +================== + +- Fixed a regression on deletion of record-valued keyword cards using + the Header wildcard syntax. This was intended to be fixed before the + v3.2.1 release. + + +3.1.5 (2014-03-25) +================== + +- Fixed a regression on deletion of record-valued keyword cards using + the Header wildcard syntax. This was intended to be fixed before the + v3.1.4 release. + + +3.2.1 (2014-03-04) +================== + +- Nominal support for the upcoming Python 3.4. + +- Added missing features from the ``Header.insert()`` method that were + intended for inclusion in the original 3.1 release: In addition to + accepting an integer index as the first argument, it also supports supplying + a keyword name as the first argument for insertion relative to a specific + keyword. It also now supports an optional ``after`` argument. If + ``after=True`` the insertion is made below the insertion point instead + of above it. (spacetelescope/PyFITS#12) + +- Fixed support for broadcasting of values assigned to table columns. + (spacetelescope/PyFITS#48) + +- A grab bag of minor performance improvements in headers. + (spacetelescope/PyFITS#46) + +- Fix an unrelated error that occurred when instantiating a ``ColDefs`` object + with invalid input. + +- Fixed an issue where opening an image containing pseudo-unsigned integers + and immediately writing it to a new file using the ``writeto`` method would + drop the scale factors that identified the data as unsigned. + +- Fixed a bug where writing a file with ``checksum=True`` did not add the + checksum on new files. (spacetelescope/PyFITS#8) + +- Fixed an issue where validating an HDU's checksums removed the checksum from + that HDU's header entirely (even if it was valid.) + +- Fixed checksums on compressed images, so that the ``ZHECKSUM`` and + ``ZDATASUM`` contain a checksum of the original image HDU, while + ``CHECKSUM`` and ``DATASUM`` contain checksums of the compressed image HDU. + This feature was supposed to be supported in 3.2, but the support was buggy. + +- Fixed an issue where the size of the heap was sometimes not computed + properly when writing an existing table containing variable-length array + columns to a new FITS file. This could result in corruption in the new FITS + file. (spacetelescope/PyFITS#47) + +- Fixed issue with updates to the header of ``CompImageHDU`` objects not being + preserved on save. (spacetelescope/PyFITS#23) + +- Fixed a bug where a boolean value of ``True`` in a header could not be + replaced with the integer 1, and likewise for ``False`` and 0 and vice + versa. + +- Fixed an issue similar to the above one but for numeric values--now + replacing a header value with an equivalent numeric value will up/downcast + that value. For example replacing '0' with '0.0' will write '0.0' to the + header so that it is returned as a floating point value. Likewise a float + can be downcast to an integer. (spacetelescope/PyFITS#49) + +- A handful of Python 3 compatibility fixes, especially for compatibility + with the upcoming Python 3.4. + +- Fixed unrelated crash when a header contains an invalid END card (for + example "END = "). This resulted in a cryptic traceback. Now headers like + this will detect "clearly intended" END cards and produce a warning about + their invalidity and fix them. (#217) + +- Allowed a sequence of ``Column`` objects to be passed in as the main + argument to ``FITS_rec.from_columns`` as the documentation suggests should + be possible. + +- Fixed a display formatting issue with fitsdiff where sometimes it did not + show the difference between two floating point numbers if they were the same + up to some low number of digits. (spacetelescope/PyFITS#21) + +- Fixed an issue where Python 2 sometimes allowed non-ASCII strings to be + assigned as header values if they were assigned as old-style ``str`` objects + and not ``unicode`` objects. (spacetelescope/PyFITS#37) + + +3.1.4 (2014-03-04) +================== + +- Added missing features from the ``Header.insert()`` method that were + intended for inclusion in the original 3.1 release: In addition to + accepting an integer index as the first argument, it also supports supplying + a keyword name as the first argument for insertion relative to a specific + keyword. It also now supports an optional ``after`` argument. If + ``after=True`` the insertion is made below the insertion point instead + of above it. (Backported from 3.2.1) + +- A grab bag of minor performance improvements in headers. + (Backported from 3.2.1) + +- Fixed an issue where opening an image containing pseudo-unsigned integers + and immediately writing it to a new file using the ``writeto`` method would + drop the scale factors that identified the data as unsigned. + (Backported from 3.2.1) + +- Fixed a bug where writing a file with ``checksum=True`` did not add the + checksum on new files. (Backported from 3.2.1) + +- Fixed an issue where validating an HDU's checksums removed the checksum from + that HDU's header entirely (even if it was valid.) + (Backported from 3.2.1) + +- Fixed an issue where the size of the heap was sometimes not computed + properly when writing an existing table containing variable-length array + columns to a new FITS file. This could result in corruption in the new FITS + file. (Backported from 3.2.1) + +- Fixed a bug where a boolean value of ``True`` in a header could not be + replaced with the integer 1, and likewise for ``False`` and 0 and vice + versa. (Backported from 3.2.1) + +- Fixed an issue similar to the above one but for numeric values--now + replacing a header value with an equivalent numeric value will up/downcast + that value. For example replacing '0' with '0.0' will write '0.0' to the + header so that it is returned as a floating point value. Likewise a float + can be downcast to an integer. (Backported from 3.2.1) + +- Fixed unrelated crash when a header contains an invalid END card (for + example "END = "). This resulted in a cryptic traceback. Now headers like + this will detect "clearly intended" END cards and produce a warning about + their invalidity and fix them. (Backported from 3.2.1) + +- Fixed a display formatting issue with fitsdiff where sometimes it did not + show the difference between two floating point numbers if they were the same + up to some low number of digits. (Backported from 3.2.1) + +- Fixed an issue where Python 2 sometimes allowed non-ASCII strings to be + assigned as header values if they were assigned as old-style ``str`` objects + and not ``unicode`` objects. (Backported from 3.2.1) + + +3.0.13 (2014-03-04) +=================== + +- Fixed a bug where writing a file with ``checksum=True`` did not add the + checksum on new files. (Backported from 3.2.1) + +- Fixed an issue where validating an HDU's checksums removed the checksum from + that HDU's header entirely (even if it was valid.) + (Backported from 3.2.1) + + +3.2 (2013-11-26) +================ Highlights -^^^^^^^^^^ +---------- + +- Rewrote CFITSIO-based backend for handling tile compression of FITS files. + It now uses a standard CFITSIO instead of heavily modified pieces of CFITSIO + as before. PyFITS ships with its own copy of CFITSIO v3.35 which supports + the latest version of the Tiled Image Convention (v2.3), but system + packagers may choose instead to strip this out in favor of a + system-installed version of CFITSIO. Earlier versions may work, but nothing + earlier than 3.28 has been tested yet. (#169) + +- Added support for reading and writing tables using the Q format for columns. + The Q format is identical to the P format (variable-length arrays) except + that it uses 64-bit integers for the data descriptors, allowing more than + 4 GB of variable-length array data in a single table. (#160) + +- Added initial support for table columns containing pseudo-unsigned integers. + This is currently enabled by using the ``uint=True`` option when opening + files; any table columns with the correct BZERO value will be interpreted + and returned as arrays of unsigned integers. + +- Some refactoring of the table and ``FITS_rec`` modules in order to better + separate the details of the FITS binary and ASCII table data structures from + the HDU data structures that encapsulate them. Most of these changes should + not be apparent to users (but see API Changes below). + + +API Changes +----------- + +- Assigning to values in ``ColDefs.names``, ``ColDefs.formats``, + ``ColDefs.nulls`` and other attributes of ``ColDefs`` instances that return + lists of column properties is no longer supported. Assigning to those lists + will no longer update the corresponding columns. Instead, please just + modify the ``Column`` instances directly (``Column.name``, ``Column.null``, + etc.) + +- The ``pyfits.new_table`` function is marked "pending deprecation". This + does not mean it will be removed outright or that its functionality has + changed. It will likely be replaced in the future for a function with + similar, if not subtly different functionality. A better, if not slightly + more verbose approach is to use ``pyfits.FITS_rec.from_columns`` to create + a new ``FITS_rec`` table--this has the same interface as + ``pyfits.new_table``. The difference is that it returns a plan ``FITS_rec`` + array, and not an HDU instance. This ``FITS_rec`` object can then be used + as the data argument in the constructors for ``BinTableHDU`` (for binary + tables) or ``TableHDU`` (for ASCII tables). This is analogous to creating + an ``ImageHDU`` by passing in an image array. + ``pyfits.FITS_rec.from_columns`` is just a simpler way of creating a + FITS-compatible recarray from a FITS column specification. + +- The ``updateHeader``, ``updateHeaderData``, and ``updateCompressedData`` + methods of the ``CompDataHDU`` class are pending deprecation and moved to + internal methods. The operation of these methods depended too much on + internal state to be used safely by users; instead they are invoked + automatically in the appropriate places when reading/writing compressed image + HDUs. + +- The ``CompDataHDU.compData`` attribute is pending deprecation in favor of + the clearer and more PEP-8 compatible ``CompDataHDU.compressed_data``. + +- The constructor for ``CompDataHDU`` has been changed to accept new keyword + arguments. The new keyword arguments are essentially the same, but are in + underscore_separated format rather than camelCase format. The old arguments + are still pending deprecation. + +- The internal attributes of HDU classes ``_hdrLoc``, ``_datLoc``, and + ``_datSpan`` have been replaced with ``_header_offset``, ``_data_offset``, + and ``_data_size`` respectively. The old attribute names are still pending + deprecation. This should only be of interest to advanced users who have + created their own HDU subclasses. + +- The following previously deprecated functions and methods have been removed + entirely: ``createCard``, ``createCardFromString``, ``upperKey``, + ``ColDefs.data``, ``setExtensionNameCaseSensitive``, ``_File.getfile``, + ``_TableBaseHDU.get_coldefs``, ``Header.has_key``, ``Header.ascardlist``. + + If you run your code with a previous version of PyFITS (>= 3.0, < 3.2) with + the ``python -Wd`` argument, warnings for all deprecated interfaces still in + use will be displayed. + +- Interfaces that were pending deprecation are now fully deprecated. These + include: ``create_card``, ``create_card_from_string``, ``upper_key``, + ``Header.get_history``, and ``Header.get_comment``. + +- The ``.name`` attribute on HDUs is now directly tied to the HDU's header, so + that if ``.header['EXTNAME']`` changes so does ``.name`` and vice-versa. + +- The ``pyfits.file.PYTHON_MODES`` constant dict was renamed to + ``pyfits.file.PYFITS_MODES`` which better reflects its purpose. This is + rarely used by client code, however. Support for the old name will be + removed by PyFITS 3.4. + + +Other Changes and Additions +--------------------------- + +- The new compression code also adds support for the ZQUANTIZ and ZDITHER0 + keywords added in more recent versions of this FITS Tile Compression spec. + This includes support for lossless compression with GZIP. (#198) By default + no dithering is used, but the ``SUBTRACTIVE_DITHER_1`` and + ``SUBTRACTIVE_DITHER_2`` methods can be enabled by passing the correct + constants to the ``quantize_method`` argument to the ``CompImageHDU`` + constructor. A seed can be manually specified, or automatically generated + using either the system clock or checksum-based methods via the + ``dither_seed`` argument. See the documentation for ``CompImageHDU`` for + more details. (#198) (spacetelescope/PYFITS#32) + +- Images compressed with the Tile Compression standard can now be larger than + 4 GB through support of the Q format. (#159) + +- All HDUs now have a ``.ver`` ``.level`` attribute that returns the value of + the EXTVAL and EXTLEVEL keywords from that HDU's header, if the exist. This + was added for consistency with the ``.name`` attribute which returns the + EXTNAME value from the header. + +- Then ``Column`` and ``ColDefs`` classes have new ``.dtype`` attributes + which give the Numpy dtype for the column data in the first case, and the + full Numpy compound dtype for each table row in the latter case. + +- There was an issue where new tables created defaulted the values in all + string columns to '0.0'. Now string columns are filled with empty strings + by default--this seems a less surprising default, but it may cause + differences with tables created with older versions of PyFITS. + +- Improved round-tripping and preservation of manually assigned column + attributes (``TNULLn``, ``TSCALn``, etc.) in table HDU headers. + (astropy/astropy#996) + + +Bug Fixes +--------- + +- Binary tables containing compressed images may, optionally, contain other + columns unrelated to the tile compression convention. Although this is an + uncommon use case, it is permitted by the standard. (#159) + +- Reworked some of the file I/O routines to allow simpler, more consistent + mapping between OS-level file modes ('rb', 'wb', 'ab', etc.) and the more + "PyFITS-specific" modes used by PyFITS like "readonly" and "update". + That is, if reading a FITS file from an open file object, it doesn't matter + as much what "mode" it was opened in so long as it has the right + capabilities (read/write/etc.) Also works around bugs in the Python io + module in 2.6+ with regard to file modes. (spacetelescope/PyFITS#33) + +- Fixed an obscure issue that can occur on systems that don't have flush to + memory-mapped files implemented (namely GNU Hurd). (astropy/astropy#968) + + +3.1.3 (2013-11-26) +================== + +- Disallowed assigning NaN and Inf floating point values as header values, + since the FITS standard does not define a way to represent them in. Because + this is undefined, the previous behavior did not make sense and produced + invalid FITS files. (spacetelescope/PyFITS#11) + +- Added a workaround for a bug in 64-bit OSX that could cause truncation when + writing files greater than 2^32 bytes in size. (spacetelescope/PyFITS#28) + +- Fixed a long-standing issue where writing binary tables did not correctly + write the TFORMn keywords for variable-length array columns (they omitted + the max array length parameter of the format). This was thought fixed in + v3.1.2, but it was only fixed there for compressed image HDUs and not for + binary tables in general. + +- Fixed an obscure issue that can occur on systems that don't have flush to + memory-mapped files implemented (namely GNU Hurd). (Backported from 3.2) + + +3.0.12 (2013-11-26) +=================== + +- Disallowed assigning NaN and Inf floating point values as header values, + since the FITS standard does not define a way to represent them in. Because + this is undefined, the previous behavior did not make sense and produced + invalid FITS files. (Backported from 3.1.3) + +- Added a workaround for a bug in 64-bit OSX that could cause truncation when + writing files greater than 2^32 bytes in size. (Backported from 3.1.3) + +- Fixed a long-standing issue where writing binary tables did not correctly + write the TFORMn keywords for variable-length array columns (they omitted + the max array length parameter of the format). This was thought fixed in + v3.1.2, but it was only fixed there for compressed image HDUs and not for + binary tables in general. (Backported from 3.1.3) + +- Fixed an obscure issue that can occur on systems that don't have flush to + memory-mapped files implemented (namely GNU Hurd). (Backported from 3.2) + + +3.1.3 (unreleased) +================== + +- Disallowed assigning NaN and Inf floating point values as header values, + since the FITS standard does not define a way to represent them in. Because + this is undefined, the previous behavior did not make sense and produced + invalid FITS files. (spacetelescope/PyFITS#11) + + +3.0.12 (unreleased) +=================== + +- Disallowed assigning NaN and Inf floating point values as header values, + since the FITS standard does not define a way to represent them in. Because + this is undefined, the previous behavior did not make sense and produced + invalid FITS files. (Backported from 3.1.3) + +- Added a workaround for a bug in 64-bit OSX that could cause truncation when + writing files greater than 2^32 bytes in size. (Backported from 3.1.3) + + +3.1.2 (2013-04-22) +================== + +- When an error occurs opening a file in fitsdiff the exception message will + now at least mention which file had the error. (#168) + +- Fixed support for opening gzipped FITS files by filename in a writeable mode + (PyFITS has supported writing to gzip files for some time now, but only + enabled it when GzipFile objects were passed to ``pyfits.open()`` due to + some legacy code preventing full gzip support. (#195) + +- Added a more helpful error message in the case of malformatted FITS files + that contain non-float NULL values in an ASCII table but are missing the + required TNULLn keywords in the header. (#197) + +- Fixed an (apparently long-standing) issue where writing compressed images + did not correctly write the TFORMn keywords for variable-length array + columns (they omitted the max array length parameter of the format). (#199) + +- Slightly refactored how tables containing variable-length array columns are + handled to add two improvements: Fixes an issue where accessing the data + after a call to the ``pyfits.getdata`` convenience function caused an + exception, and allows the VLA data to be read from an existing mmap of the + FITS file. (#200) + +- Fixed a bug that could occur when opening a table containing + multi-dimensional columns (i.e. via the TDIMn keyword) and then writing it + out to a new file. (#201) + +- Added use of the console_scripts entry point to install the fitsdiff and + fitscheck scripts, which if nothing else provides better Windows support. + The generated scripts now override the ones explicitly defined in the + scripts/ directory (which were just trivial stubs to begin with). (#202) + +- Fixed a bug on Python 3 where attempting to open a non-existent file on + Python 3 caused a seemingly unrelated traceback. (#203) + +- Fixed a bug in fitsdiff that reported two header keywords containing NaN + as value as different. (#204) + +- Fixed an issue in the tests that caused some tests to fail if pyfits is + installed with read-only permissions. (#208) + +- Fixed a bug where instantiating a ``BinTableHDU`` from a numpy array + containing boolean fields converted all the values to ``False``. (#215) + +- Fixed an issue where passing an array of integers into the constructor of + ``Column()`` when the column type is floats of the same byte width caused the + column array to become garbled. (#218) + +- Fixed inconsistent behavior in creating CONTINUE cards from byte strings + versus Unicode strings in Python 2--CONTINUE cards can now be created + properly from Unicode strings (so long as they are convertible to ASCII). + (spacetelescope/PyFITS#1) + +- Fixed a couple cases where creating a new table using TDIMn in some of the + columns could caused a crash. (spacetelescope/PyFITS#3) + +- Fixed a bug in parsing HIERARCH keywords that do not have a space after + the first equals sign (before the value). (spacetelescope/PyFITS#5) + +- Prevented extra leading whitespace on HIERARCH keywords from being treated + as part of the keyword. (spacetelescope/PyFITS#6) + +- Fixed a bug where HIERARCH keywords containing lower-case letters was + mistakenly marked as invalid during header validation. + (spacetelescope/PyFITS#7) + +- Fixed an issue that was ancillary to (spacetelescope/PyFITS#7) where the + ``Header.index()`` method did not work correctly with HIERARCH keywords + containing lower-case letters. + + +3.0.11 (2013-04-17) +=================== + +- Fixed support for opening gzipped FITS files by filename in a writeable mode + (PyFITS has supported writing to gzip files for some time now, but only + enabled it when GzipFile objects were passed to ``pyfits.open()`` due to + some legacy code preventing full gzip support. Backported from 3.1.2. (#195) + +- Added a more helpful error message in the case of malformatted FITS files + that contain non-float NULL values in an ASCII table but are missing the + required TNULLn keywords in the header. Backported from 3.1.2. (#197) + +- Fixed an (apparently long-standing) issue where writing compressed images did + not correctly write the TFORMn keywords for variable-length array columns + (they omitted the max array length parameter of the format). Backported from + 3.1.2. (#199) + +- Slightly refactored how tables containing variable-length array columns are + handled to add two improvements: Fixes an issue where accessing the data + after a call to the ``pyfits.getdata`` convenience function caused an + exception, and allows the VLA data to be read from an existing mmap of the + FITS file. Backported from 3.1.2. (#200) + +- Fixed a bug that could occur when opening a table containing + multi-dimensional columns (i.e. via the TDIMn keyword) and then writing it + out to a new file. Backported from 3.1.2. (#201) + +- Fixed a bug on Python 3 where attempting to open a non-existent file on + Python 3 caused a seemingly unrelated traceback. Backported from 3.1.2. + (#203) + +- Fixed a bug in fitsdiff that reported two header keywords containing NaN + as value as different. Backported from 3.1.2. (#204) + +- Fixed an issue in the tests that caused some tests to fail if pyfits is + installed with read-only permissions. Backported from 3.1.2. (#208) + +- Fixed a bug where instantiating a ``BinTableHDU`` from a numpy array + containing boolean fields converted all the values to ``False``. Backported + from 3.1.2. (#215) + +- Fixed an issue where passing an array of integers into the constructor of + ``Column()`` when the column type is floats of the same byte width caused the + column array to become garbled. Backported from 3.1.2. (#218) + +- Fixed a couple cases where creating a new table using TDIMn in some of the + columns could caused a crash. Backported from 3.1.2. + (spacetelescope/PyFITS#3) + + +3.1.1 (2013-01-02) +================== + +This is a bug fix release for the 3.1.x series. + +Bug Fixes +--------- + +- Improved handling of scaled images and pseudo-unsigned integer images in + compressed image HDUs. They now work more transparently like normal image + HDUs with support for the ``do_not_scale_image_data`` and ``uint`` options, + as well as ``scale_back`` and ``save_backup``. The ``.scale()`` method + works better too. (#88) + +- Permits non-string values for the EXTNAME keyword when reading in a file, + rather than throwing an exception due to the malformatting. Added + verification for the format of the EXTNAME keyword when writing. (#96) + +- Added support for EXTNAME and EXTVER in PRIMARY HDUs. That is, if EXTNAME + is specified in the header, it will also be reflected in the ``.name`` + attribute and in ``pyfits.info()``. These keywords used to be verboten in + PRIMARY HDUs, but the latest version of the FITS standard allows them. + (#151) + +- HCOMPRESS can again be used to compress data cubes (and higher-dimensional + arrays) so long as the tile size is effectively 2-dimensional. In fact, + PyFITS will automatically use compatible tile sizes even if they're not + explicitly specified. (#171) + +- Added support for the optional ``endcard`` parameter in the + ``Header.fromtextfile()`` and ``Header.totextfile()`` methods. Although + ``endcard=False`` was a reasonable default assumption, there are still text + dumps of FITS headers that include the END card, so this should have been + more flexible. (#176) + +- Fixed a crash when running fitsdiff on two empty (that is, zero row) tables. + (#178) + +- Fixed an issue where opening files containing random groups HDUs in update + mode could cause an unnecessary rewrite of the file even if none of the + data is modified. (#179) + +- Fixed a bug that could caused a deadlock in the filesystem on OSX if PyFITS + is used with Numpy 1.7 in some cases. (#180) + +- Fixed a crash when generating diff reports from diffs using the + ``ignore_comments`` options. (#181) + +- Fixed some bugs with FITS WCS distortion paper record-valued keyword cards: + + - Cards that looked kind of like RVKCs but were not intended to be were + over-permissively treated as such--commentary keywords like COMMENT and + HISTORY were particularly affected. (#183) + + - Looking up a card in a header by its standard FITS keyword only should + always return the raw value of that card. That way cards containing + values that happen to valid RVKCs but were not intended to be will still + be treated like normal cards. (#184) + + - Looking up a RVKC in a header with only part of the field-specifier (for + example "DP1.AXIS" instead of "DP1.AXIS.1") was implicitly treated as a + wildcard lookup. (#184) + +- Fixed a crash when diffing two FITS files where at least one contains a + compressed image HDU which was not recognized as an image instead of a + table. (#187) + +- Fixed bugs in the backwards compatibility layer for the ``CardList.index`` + and ``CardList.count`` methods. (#190) + +- Improved ``__repr__`` and text file representation of cards with long values + that are split into CONTINUE cards. (#193) + +- Fixed a crash when trying to assign a long (> 72 character) value to blank + ('') keywords. This also changed how blank keywords are represented--there + are still exactly 8 spaces before any commentary content can begin; this + *may* affect the exact display of header cards that assumed there could be + fewer spaces in a blank keyword card before the content begins. However, the + current approach is more in line with the requirements of the FITS standard. + (#194) + + +3.0.10 (2013-01-02) +=================== + +- Improved handling of scaled images and pseudo-unsigned integer images in + compressed image HDUs. They now work more transparently like normal image + HDUs with support for the ``do_not_scale_image_data`` and ``uint`` options, + as well as ``scale_back`` and ``save_backup``. The ``.scale()`` method + works better too. Backported from 3.1.1. (#88) + +- Permits non-string values for the EXTNAME keyword when reading in a file, + rather than throwing an exception due to the malformatting. Added + verification for the format of the EXTNAME keyword when writing. Backported + from 3.1.1. (#96) + +- Added support for EXTNAME and EXTVER in PRIMARY HDUs. That is, if EXTNAME + is specified in the header, it will also be reflected in the ``.name`` + attribute and in ``pyfits.info()``. These keywords used to be verbotten in + PRIMARY HDUs, but the latest version of the FITS standard allows them. + Backported from 3.1.1. (#151) + +- HCOMPRESS can again be used to compress data cubes (and higher-dimensional + arrays) so long as the tile size is effectively 2-dimensional. In fact, + PyFITS will not automatically use compatible tile sizes even if they're not + explicitly specified. Backported from 3.1.1. (#171) + +- Fixed a bug when writing out files containing zero-width table columns, + where the TFIELDS keyword would be updated incorrectly, leaving the table + largely unreadable. Backported from 3.1.0. (#174) + +- Fixed an issue where opening files containing random groups HDUs in update + mode could cause an unnecessary rewrite of the file even if none of the + data is modified. Backported from 3.1.1. (#179) + +- Fixed a bug that could caused a deadlock in the filesystem on OSX if PyFITS + is used with Numpy 1.7 in some cases. Backported from 3.1.1. (#180) + + +3.1 (2012-08-08) +================ + +Highlights +---------- - The ``Header`` object has been significantly reworked, and ``CardList`` objects are now deprecated (their functionality folded into the ``Header`` @@ -30,7 +837,7 @@ Highlights Features below. API Changes -^^^^^^^^^^^ +----------- - The ``Header`` class has been rewritten, and the ``CardList`` class is deprecated. Most of the basic details of working with FITS headers are @@ -38,11 +845,11 @@ API Changes in some areas that will be of interest to advanced users, and to application developers. For full details of the changes, see the "Header Interface Transition Guide" section in the PyFITS documentation. See ticket #64 on - the PyFITS Trac for futher details and background. Some highlights are + the PyFITS Trac for further details and background. Some highlights are listed below: * The Header class now fully implements the Python dict interface, and can - be used interchangably with a dict, where the keys are header keywords. + be used interchangeably with a dict, where the keys are header keywords. * New keywords can be added to the header using normal keyword assignment (previously it was necessary to use ``Header.update`` to add new @@ -121,7 +928,7 @@ API Changes - A new global variable ``pyfits.EXTENSION_NAME_CASE_SENSITIVE`` was added. This serves as a replacement for ``pyfits.setExtensionNameCaseSensitive`` which is not deprecated and may be removed in a future version. To enable - case-sensitivity of extension names (i.e. treat 'sci' as distict from 'SCI') + case-sensitivity of extension names (i.e. treat 'sci' as distinct from 'SCI') set ``pyfits.EXTENSION_NAME_CASE_SENSITIVE = True``. The default is ``False``. (r1139) @@ -145,16 +952,15 @@ API Changes and removed when the file is saved. New Features -^^^^^^^^^^^^ +------------ -- Added support for the proposed "FITS" extension HDU type. See - http://listmgr.cv.nrao.edu/pipermail/fitsbits/2002-April/001094.html. FITS - HDUs contain an entire FITS file embedded in their data section. `FitsHDU` +- Added support for the proposed "FITS" extension HDU type. FITS + HDUs contain an entire FITS file embedded in their data section. ``FitsHDU`` objects work like other HDU types in PyFITS. Their ``.data`` attribute returns the raw data array. However, they have a special ``.hdulist`` attribute which processes the data as a FITS file and returns it as an in-memory HDUList object. FitsHDU objects also support a - ``FitsHDU.fromhdulist()`` classmethod which returns a new `FitsHDU` object + ``FitsHDU.fromhdulist()`` classmethod which returns a new ``FitsHDU`` object that embeds the supplied HDUList. (#80) - Added a new ``.is_image`` attribute on HDU objects, which is True if the HDU @@ -192,8 +998,16 @@ New Features mode). This may be especially useful when working with scaled image data. (#121) +Changes in Behavior +------------------- + +- Warnings from PyFITS are not output to stderr by default, instead of stdout + as it has been for some time. This is contrary to most users' expectations + and makes it more difficult for them to separate output from PyFITS from the + desired output for their scripts. (r1319) + Bug Fixes -^^^^^^^^^ +--------- - Fixed ``pyfits.tcreate()`` (now ``pyfits.tableload()``) to be more robust when encountering blank lines in a column definition file (#14) @@ -207,7 +1021,7 @@ Bug Fixes This allowed for the implementation of ``HDUList.fromstring`` described above. (#90) -- Fixed a rare corner case where, in some use cases, (mildly, recoverably) +- Fixed a rare corner case where, in some use cases, (mildly, recoverable) malformatted float values in headers were not properly returned as floats. (#137) @@ -224,12 +1038,49 @@ Bug Fixes - Fixed a bug where ``ImageHDU.scale(option='old')`` wasn't working at all--it was not restoring the image to its original BSCALE and BZERO values. (#162) +- Fixed a bug when writing out files containing zero-width table columns, + where the TFIELDS keyword would be updated incorrectly, leaving the table + largely unreadable. This fix will be backported to the 3.0.x series in + version 3.0.10. (#174) + + +3.0.9 (2012-08-06) +================== + +This is a bug fix release for the 3.0.x series. + +Bug Fixes +--------- + +- Fixed ``Header.values()``/``Header.itervalues()`` and ``Header.items()``/ + ``Header.iteritems()`` to correctly return the different values for + duplicate keywords (particularly commentary keywords like HISTORY and + COMMENT). This makes the old Header implementation slightly more compatible + with the new implementation in PyFITS 3.1. (#127) + + .. note:: + This fix did not change the existing behavior from earlier PyFITS + versions where ``Header.keys()`` returns all keywords in the header with + duplicates removed. PyFITS 3.1 changes that behavior, so that + ``Header.keys()`` includes duplicates. + +- Fixed a bug where ``ImageHDU.scale(option='old')`` wasn't working at all--it + was not restoring the image to its original BSCALE and BZERO values. (#162) + +- Fixed a bug where opening a file containing compressed image HDUs in + 'update' mode and then immediately closing it without making any changes + caused the file to be rewritten unnecessarily. (#167) + +- Fixed two memory leaks that could occur when writing compressed image data, + or in some cases when opening files containing compressed image HDUs in + 'update' mode. (#168) + 3.0.8 (2012-06-04) ---------------------- +================== Changes in Behavior -^^^^^^^^^^^^^^^^^^^ +------------------- - Prior to this release, image data sections did not work with scaled data--that is, images with non-trivial BSCALE and/or BZERO values. @@ -239,7 +1090,7 @@ Changes in Behavior extends that support for general BSCALE+BZERO values. Bug Fixes -^^^^^^^^^ +--------- - Fixed a bug that prevented updates to values in boolean table columns from being saved. This turned out to be a symptom of a deeper problem that could @@ -249,7 +1100,7 @@ Bug Fixes could, in some circumstances, cause headers (and the rest of the file after that point) to be misread. (#142) -- Fixed support for scaled image data and psuedo-unsigned ints in image data +- Fixed support for scaled image data and pseudo-unsigned ints in image data sections (``hdu.section``). Previously this was not supported at all. At some point support was supposedly added, but it was buggy and incomplete. Now the feature seems to work much better. (#143) @@ -280,16 +1131,16 @@ Bug Fixes 3.0.7 (2012-04-10) ----------------------- +================== Changes in Behavior -^^^^^^^^^^^^^^^^^^^ +------------------- - Slices of GroupData objects now return new GroupData objects instead of extended multi-row _Group objects. This is analogous to how PyFITS 3.0 fixed FITS_rec slicing, and should have been fixed for GroupData at the same time. The old behavior caused bugs where functions internal to Numpy expected that - slicing an ndarray would return a new ndarray. As this is a rare usecase + slicing an ndarray would return a new ndarray. As this is a rare use case with a rare feature most users are unlikely to be affected by this change. - The previously internal _Group object for representing individual group @@ -302,7 +1153,7 @@ Changes in Behavior HDUs. It was unnecessary to modify this value. Bug Fixes -^^^^^^^^^ +--------- - Fixed GroupData objects to return new GroupData objects when sliced instead of _Group record objects. See "Changes in behavior" above for more details. @@ -310,7 +1161,7 @@ Bug Fixes - Fixed slicing of Group objects--previously it was not possible to slice slice them at all. -- Made it possible to assign `np.bool_` objects as header values. (#123) +- Made it possible to assign ``np.bool_`` objects as header values. (#123) - Fixed overly strict handling of the EXTEND keyword; see "Changes in behavior" above. (#124) @@ -322,9 +1173,9 @@ Bug Fixes - Fixed a bug where the values of the PTYPEn keywords in a random groups HDU were forced to be all lower-case when saving the file. (#130) -- Removed an unnecessary inline import in `ExtensionHDU.__setattr__` that was +- Removed an unnecessary inline import in ``ExtensionHDU.__setattr__`` that was causing some slowdown when opening files containing a large number of - extensions, plus a few other small (but not insignficant) performance + extensions, plus a few other small (but not insignificant) performance improvements thanks to Julian Taylor. (#133) - Fixed a regression where header blocks containing invalid end-of-header @@ -337,10 +1188,10 @@ Bug Fixes 3.0.6 (2012-02-29) ------------------- +================== Highlights -^^^^^^^^^^ +---------- The main reason for this release is to fix an issue that was introduced in PyFITS 3.0.5 where merely opening a file containing scaled data (that is, with @@ -356,7 +1207,7 @@ This release also fixes a few Windows-specific bugs found through more extensive Windows testing, and other miscellaneous bugs. Bug Fixes -^^^^^^^^^ +--------- - More accurate error messages when opening files containing invalid header cards. (#109) @@ -386,7 +1237,7 @@ Bug Fixes 3.0.5 (2012-01-30) ------------------- +================== - Fixed a crash that could occur when accessing image sections of files opened with memmap=True. (r1211) @@ -419,14 +1270,14 @@ Bug Fixes - Fixed a crash that could occur in Python 3 when opening files with checksum checking enabled. (r1336) -- Fixed a small bug that could cause a crash in the `StreamingHDU` interface +- Fixed a small bug that could cause a crash in the ``StreamingHDU`` interface when using Numpy below version 1.5. -- Fixed a crash that could occur when creating a new `CompImageHDU` from an +- Fixed a crash that could occur when creating a new ``CompImageHDU`` from an array of big-endian data. (#104) - Fixed a crash when opening a file with extra zero padding at the end. - Though FITS files should not have such padding, it's not explictly forbidden + Though FITS files should not have such padding, it's not explicitly forbidden by the format either, and PyFITS shouldn't stumble over it. (#106) - Fixed a major slowdown in opening tables containing large columns of string @@ -434,7 +1285,7 @@ Bug Fixes 3.0.4 (2011-11-22) ------------------- +================== - Fixed a crash when writing HCOMPRESS compressed images that could happen on Python 2.5 and 2.6. (r1217) @@ -478,7 +1329,7 @@ Bug Fixes 3.0.3 (2011-10-05) ------------------- +================== - Fixed several small bugs involving corner cases in record-valued keyword cards (#70) @@ -496,7 +1347,7 @@ Bug Fixes 3.0.2 (2011-09-23) ------------------- +================== - The ``BinTableHDU.tcreate`` method and by extension the ``pyfits.tcreate`` function don't get tripped up by blank lines anymore (#14) @@ -525,7 +1376,7 @@ Bug Fixes original file permissions (#79) - Fixed the handling of TDIMn keywords; 3.0 added support for them, but got - the axis order backards (they were treated as though they were row-major) + the axis order backwards (they were treated as though they were row-major) (#82) - Fixed a crash when a FITS file containing scaled data is opened and @@ -537,7 +1388,7 @@ Bug Fixes 3.0.1 (2011-09-12) ------------------- +================== - Fixed a bug where updating a header card comment could cause the value to be lost if it had not already been read from the card image string. @@ -555,7 +1406,7 @@ Bug Fixes - Fixed a bug where writing a table with zero rows could fail in some cases (#72) -- Miscellanous small bug fixes that were causing some tests to fail, +- Miscellaneous small bug fixes that were causing some tests to fail, particularly on Python 3 (#74, #75) - Fixed a bug where creating a table column from an array in non-native byte @@ -564,7 +1415,7 @@ Bug Fixes 3.0.0 (2011-08-23) --------------------- +==================== - Contains major changes, bumping the version to 3.0 @@ -617,7 +1468,7 @@ Bug Fixes - Calls to deprecated functions will display a Deprecation warning. However, in Python 2.7 and up Deprecation warnings are ignored by default, so run - Python with the `-Wd` option to see if you're using any deprecated + Python with the ``-Wd`` option to see if you're using any deprecated functions. If we get close to actually removing any functions, we might make the Deprecation warnings display by default. @@ -636,8 +1487,8 @@ Bug Fixes - Added support for binary table fields with zero width (#42) -- Added support for wider integer types in ASCII tables; although this is non- - standard, some GEIS images require it (#45) +- Added support for wider integer types in ASCII tables; although this is + non-standard, some GEIS images require it (#45) - Fixed a bug that caused the index_of() method of HDULists to crash when the HDUList object is created from scratch (#48) @@ -670,12 +1521,12 @@ Bug Fixes 2.4.0 (2011-01-10) --------------------- +==================== The following enhancements were added: - Checksum support now correctly conforms to the FITS standard. pyfits supports reading and writing both the old checksums and new - standard-compliant checksums. The `fitscheck` command-line utility is + standard-compliant checksums. The ``fitscheck`` command-line utility is provided to verify and update checksums. - Added a new optional keyword argument ``do_not_scale_image_data`` @@ -746,7 +1597,7 @@ The following bugs were fixed: 2.3.1 (2010-06-03) --------------------- +==================== The following bugs were fixed: @@ -756,13 +1607,13 @@ The following bugs were fixed: 2.3 (2010-05-11) ------------------- +================== The following enhancements were made: - Completely eliminate support for numarray. -- Rework pyfits documention to use Sphinx. +- Rework pyfits documentation to use Sphinx. - Support python 2.6 and future division. @@ -795,7 +1646,7 @@ The following enhancements were made: 64 data is written to a file, the data will be reverse scaled into a signed integer 16, 32, or 64 array and written out to the file along with the appropriate BSCALE/BZERO header cards. Note that for backward - compatability, the 'uint16' keyword argument will still be accepted in the + compatibility, the 'uint16' keyword argument will still be accepted in the open function when handling unsigned integer 16 conversion. - Provided the capability to access the data for a column of a fits table by @@ -811,7 +1662,7 @@ The following enhancements were made: >>> print table.field('c2') # this is the data for column 2 ['abc' 'xy'] >>> print table['c2'] # this is also the data for column 2 - array(['abc', 'xy '], dtype='\|S3') + array(['abc', 'xy '], dtype='|S3') >>> print table[1] # this is the data for row 1 (2, 'xy', 6.6999997138977054, True) @@ -1098,13 +1949,13 @@ The following bugs were fixed: variable length format column from character data (PA format). - Modified installation code so that when installing on Windows, when a C++ - compiler compatable with the Python binary is not found, the installation + compiler compatible with the Python binary is not found, the installation completes with a warning that all optional extension modules failed to build. Previously, an Error was issued and the installation stopped. 2.2.2 (2009-10-12) --------------------- +==================== Updates described in this release are only supported in the NUMPY version of pyfits. @@ -1117,7 +1968,7 @@ The following bugs were fixed: 2.2.1 (2009-10-06) --------------------- +==================== Updates described in this release are only supported in the NUMPY version of pyfits. @@ -1133,7 +1984,7 @@ The following bugs were fixed: 2.2 (2009-09-23) ------------------- +================== Updates described in this release are only supported in the NUMPY version of pyfits. @@ -1159,7 +2010,7 @@ The following enhancements were made: >>> hdul.insert(2,hdu) -- Provided the capability to handle unicode input for file names. +- Provided the capability to handle Unicode input for file names. - Provided support for integer division required by Python 3.0. @@ -1207,7 +2058,7 @@ The following bugs were fixed: 2.1.1 (2009-04-22) -------------------- +=================== Updates described in this release are only supported in the NUMPY version of pyfits. @@ -1224,7 +2075,7 @@ The following bugs were fixed: 2.1 (2009-04-14) ------------------- +================== Updates described in this release are only supported in the NUMPY version of pyfits. @@ -1314,7 +2165,7 @@ The following bugs were fixed: 2.0.1 (2009-02-03) --------------------- +==================== Updates described in this release are only supported in the NUMPY version of pyfits. @@ -1326,7 +2177,7 @@ The following bugs were fixed: 2.0 (2009-01-30) ------------------- +================== Updates described in this release are only supported in the NUMPY version of pyfits. @@ -1483,11 +2334,11 @@ The following bugs were fixed: an ImageHDU header with a PCOUNT card that is missing or has a value other than 0. -.. _[1]: http://fits.gsfc.nasa.gov/registry/tilecompression.html +.. _[1]: https://fits.gsfc.nasa.gov/registry/tilecompression.html 1.4.1 (2008-11-04) --------------------- +==================== Updates described in this release are only supported in the NUMPY version of pyfits. @@ -1506,7 +2357,7 @@ The following bugs were fixed: 1.4 (2008-07-07) ------------------- +================== Updates described in this release are only supported in the NUMPY version of pyfits. @@ -1532,7 +2383,7 @@ The following enhancements were made: an underlying file object on which the function will be performed. - Added support for record-valued keyword cards as introduced in the "FITS WCS - Paper IV proposal for representing a more general distortion model". + proposal for representing a more general distortion model". - Record-valued keyword cards are string-valued cards where the string is interpreted as a definition giving a record field name, and its floating @@ -1544,7 +2395,7 @@ The following enhancements were made: the standard FITS ASCII representation of a floating point number, and these are separated by a colon followed by a single blank. - The grammer for field-specifier is:: + The grammar for field-specifier is:: field-specifier: field @@ -1697,7 +2548,7 @@ The following enhancements were made: DP1 = 'AUX.1.COEFF.1: 0.000488' DP1 = 'AUX.2.COEFF.2: 0.00097656' - - The CardList keys() method will allow the retrivial of all of the key + - The CardList keys() method will allow the retrieval of all of the key values in the CardList. For example: >>> cl=hdr['DP1.AXIS.*'] @@ -1707,7 +2558,7 @@ The following enhancements were made: >>> cl.keys() ['DP1.AXIS.1', 'DP1.AXIS.2'] - - The CardList values() method will allow the retrivial of all of the values + - The CardList values() method will allow the retrieval of all of the values in the CardList. For example: >>> cl=hdr['DP1.AXIS.*'] @@ -1749,13 +2600,7 @@ The following enhancements were made: >>> del cl[0] >>> print cl['DP1.AXIS.1'] Traceback (most recent call last): - File "", line 1, in - File "NP_pyfits.py", line 977, in __getitem__ - return self.ascard[key].value - File "NP_pyfits.py", line 1258, in __getitem__ - _key = self.index_of(key) - File "NP_pyfits.py", line 1403, in index_of - raise KeyError, 'Keyword %s not found.' % `key` + ... KeyError: "Keyword 'DP1.AXIS.1' not found." >>> hdr['DP1.AXIS.1'] 4.0 @@ -1818,19 +2663,19 @@ The following enhancements were made: the appropriate card object given an input string. These two methods are also available as convenience functions: - >>> c1 = pyfits.RecordValuedKeywordCard.createCard('DP1','AUX: 1','comment) + >>> c1 = pyfits.RecordValuedKeywordCard.createCard('DP1','AUX: 1','comment') or - >>> c1 = pyfits.createCard('DP1','AUX: 1','comment) + >>> c1 = pyfits.createCard('DP1','AUX: 1','comment') >>> print type(c1) <'pyfits.NP_pyfits.RecordValuedKeywordCard'> - >>> c1 = pyfits.RecordValuedKeywordCard.createCard('DP1','AUX 1','comment) + >>> c1 = pyfits.RecordValuedKeywordCard.createCard('DP1','AUX 1','comment') or - >>> c1 = pyfits.createCard('DP1','AUX 1','comment) + >>> c1 = pyfits.createCard('DP1','AUX 1','comment') >>> print type(c1) <'pyfits.NP_pyfits.Card'> @@ -1885,7 +2730,7 @@ The following bugs were fixed: 1.3 (2008-02-22) ------------------- +================== Updates described in this release are only supported in the NUMPY version of pyfits. @@ -1902,7 +2747,7 @@ The following enhancements were made: stpyfits. - Added a new feature to allow trailing HDUs to be deleted from a fits file - without actually reading the data from the file. + without actually reading the data from the file. - This supports a JWST requirement to delete a trailing HDU from a file whose primary Image HDU is too large to be read on a 32 bit machine. @@ -1978,11 +2823,11 @@ The following bugs were fixed: windows platform using a drive letter in the file specification caused a misleading IOError exception to be raised. -.. _[2]: http://stsdas.stsci.edu/pytools/stpyfits +.. _[2]: https://stscitools.readthedocs.io/en/latest/stpyfits.html 1.1 (2007-06-15) ------------------- +================== - Modified to use either NUMPY or NUMARRAY. @@ -2001,7 +2846,7 @@ The following bugs were fixed: 1.0.1 (2006-03-24) --------------------- +==================== The changes to PyFITS were primarily to improve the docstrings and to reclassify some public functions and variables as private. Readgeis and @@ -2018,11 +2863,11 @@ stsci_python release. 1.0 (2005-11-01) ------------------- +================== Major Changes since v0.9.6: -- Added support for the HEIRARCH convention +- Added support for the HIERARCH convention - Added support for iteration and slicing for HDU lists @@ -2051,10 +2896,10 @@ Minor changes since v0.9.6: - Add output verification in methods flush() and close(). -- Modify the the design of the open() function to remove the output_verify +- Modify the design of the open() function to remove the output_verify argument. -- Remove the groups argument in GroupsHDU's contructor. +- Remove the groups argument in GroupsHDU's constructor. - Redesign the column definition class to make its column components more accessible. Also to make it conducive for higher level functionalities, @@ -2075,7 +2920,7 @@ PyFITS Version 1.0 REQUIRES Python 2.3 or later. 0.9.6 (2004-11-11) --------------------- +==================== Major changes since v0.9.3: @@ -2096,11 +2941,11 @@ Some minor changes: 0.9.3 (2004-07-02) --------------------- +==================== Changes since v0.9.0: -- Lazy instanciation of full Headers/Cards for all HDU's when the file is +- Lazy instantiation of full Headers/Cards for all HDU's when the file is opened. At the open, only extracts vital info (e.g. NAXIS's) from the header parts. This change will speed up the performance if the user only needs to access one extension in a multi-extension FITS file. @@ -2109,14 +2954,14 @@ Changes since v0.9.0: binary table. At the user interface, they are converted to Boolean arrays for easy manipulation. For example, if the column's TFORM is "11X", internally the data is stored in 2 bytes, but the user will see, at each row - of this column, a Boolean array of 11 elements. + of this column, a Boolean array of 11 elements. - Fix a bug such that when a table extension has no data, it will not try to scale the data when updating/writing the HDU list. 0.9 (2004-04-27) ------------------- +================== Changes since v0.8.0: @@ -2140,7 +2985,7 @@ Changes since v0.8.0: 0.8.0 (2003-08-19) --------------------- +==================== **NOTE:** This version will only work with numarray Version 0.6. In addition, earlier versions of PyFITS will not work with numarray 0.6. Therefore, both @@ -2156,7 +3001,7 @@ Changes since 0.7.6: - Support of complex columns - Modify the __getitem__ method in FITS_rec. In order to make sure the scaled - quantities are also viewing ths same data as the original FITS_rec, all + quantities are also viewing the same data as the original FITS_rec, all fields need to be "touched" when __getitem__ is called. - Add a new attribute mmobject for HDUList, and close the memmap object when @@ -2182,7 +3027,7 @@ Changes since 0.7.5: order to make pyfits to work under Python 2.2. (2 occurrences) - Modify the "update" method in the Header class to use the "fixed-format" - card even if the card already exists. This is to avoid the mis-alignment as + card even if the card already exists. This is to avoid the misalignment as shown below: After running drizzle on ACS images it creates a CD matrix whose elements @@ -2200,40 +3045,40 @@ Changes since 0.7.5: - Change some internal variables to make their appearance more consistent: old name new name - + __octalRegex _octalRegex __readblock() _readblock() __formatter() _formatter(). __value_RE _value_RE - __numr _numr - __comment_RE _comment_RE - __keywd_RE _keywd_RE + __numr _numr + __comment_RE _comment_RE + __keywd_RE _keywd_RE __number_RE _number_RE. tmpName() _tmpName() dimShape _dimShape ErrList _ErrList - -- Move up the module description. Move the copywright statement to the bottom + +- Move up the module description. Move the copyright statement to the bottom and assign to the variable __credits__. - change the following line: self.__dict__ = input.__dict__ - to + to self.__setstate__(input.__getstate__()) in order for pyfits to run under numarray 0.4. - edit _readblock to add the (optional) firstblock argument and raise IOError - if the the first 8 characters in the first block is not 'SIMPLE ' or + if the first 8 characters in the first block is not 'SIMPLE ' or 'XTENSION'. Edit the function open to check for IOError to skip the last null filled block(s). Edit readHDU to add the firstblock argument. 0.7.5 (2002-08-16) --------------------- +==================== Changes since v0.7.3: @@ -2258,7 +3103,7 @@ Changes since v0.7.3: 0.7.3 (2002-07-12) --------------------- +==================== Changes since v0.7.2: @@ -2290,83 +3135,83 @@ Changes since v0.7.2: the comment must be string type to avoid exception. 0.7.2.1 (2002-06-25) ----------------------- +====================== -A couple of bugs were addressed in this version. +A couple of bugs were addressed in this version. - Fix a bug in _add_commentary(). Due to a change in index_of() during version 0.6.5.5, _add_commentary needs to be modified to avoid exception if the key is not present in the header already. This affects (fixes) add_history(), - add_comment(), and add_blank(). + add_comment(), and add_blank(). - Fix a bug in __getattr__() in Card class. The change made in 0.7.2 to rstrip the comment must be string type to avoid exception. 0.7.2 (2002-06-19) --------------------- +==================== -The two major improvements from Version 0.6.2 are: +The two major improvements from Version 0.6.2 are: - support reading tables with "scaled" columns (e.g. tscal/tzero, Boolean, and ASCII tables) - a prototype output verification. -This version of PyFITS requires numarray version 0.3.4. +This version of PyFITS requires numarray version 0.3.4. -Other changes include: +Other changes include: - Implement the new HDU hierarchy proposed earlier this year. This in turn - reduces some of the redundant methods common to several HDU classes. - + reduces some of the redundant methods common to several HDU classes. + - Add 3 new methods to the Header class: add_history, add_comment, and add_blank. - The table attributes _columns are now .columns and the attributes in ColDefs are now all without the underscores. So, a user can get a list of column - names by: hdu.columns.names. + names by: hdu.columns.names. - The "fill" argument in the new_table method now has a new meaning:
If set to true (=1), it will fill the entire new table with zeros/blanks. Otherwise (=0), just the extra rows/cells are filled with zeros/blanks. - Fill values other than zero/blank are now not possible. + Fill values other than zero/blank are now not possible. - Add the argument output_verify to the open method and writeto method. Not - in the flush or close methods yet, due to possible complication. + in the flush or close methods yet, due to possible complication. - A new copy method for tables, the copy is totally independent from the table - it copies from. + it copies from. - The tostring() call in writeHDUdata takes up extra space to store the string - object. Use tofile() instead, to save space. + object. Use tofile() instead, to save space. - Make changes from _byteswap to _byteorder, following corresponding changes - in numarray and recarray. + in numarray and recarray. -- Insert(update) EXTEND in PrimaryHDU only when header is None. +- Insert(update) EXTEND in PrimaryHDU only when header is None. -- Strip the trailing blanks for the comment value of a card. +- Strip the trailing blanks for the comment value of a card. - Add seek(0) right after the __buildin__.open(0), because for the 'ab+' mode, the pointer is at the end after open in Linux, but it is at the beginning in - Solaris. + Solaris. - Add checking of data against header, update header keywords (NAXIS's, - BITPIX) when they don't agree with the data. + BITPIX) when they don't agree with the data. -- change version to __version__. +- change version to __version__. There are also many other minor internal bug fixes and -technical changes. +technical changes. 0.6.2 (2002-02-12) --------------------- +==================== -This version requires numarray version 0.2. +This version requires numarray version 0.2. -Things not yet supported but are part of future development: +Things not yet supported but are part of future development: - Verification and/or correction of FITS objects being written to disk so that they are legal FITS. This is being added now and should be available in @@ -2393,4 +3238,4 @@ Things not yet supported but are part of future development: - Support for tables with TNULL values. This awaits an enhancement to numarray to support mask arrays (planned). (At least a couple of months off). -.. _PyFITS: http://www.stsci.edu/resources/software_hardware/pyfits +.. _PyFITS: https://github.com/spacetelescope/pyfits diff --git a/docs/io/fits/images/Blue.jpg b/docs/io/fits/images/Blue.jpg deleted file mode 100644 index ac9fa4c10609..000000000000 Binary files a/docs/io/fits/images/Blue.jpg and /dev/null differ diff --git a/docs/io/fits/images/Green.jpg b/docs/io/fits/images/Green.jpg deleted file mode 100644 index ed73ad5effe3..000000000000 Binary files a/docs/io/fits/images/Green.jpg and /dev/null differ diff --git a/docs/io/fits/images/Hs-2009-14-a-web.jpg b/docs/io/fits/images/Hs-2009-14-a-web.jpg deleted file mode 100644 index 42277a0dbdaa..000000000000 Binary files a/docs/io/fits/images/Hs-2009-14-a-web.jpg and /dev/null differ diff --git a/docs/io/fits/images/Red.jpg b/docs/io/fits/images/Red.jpg deleted file mode 100644 index 8a4e74c0a3c8..000000000000 Binary files a/docs/io/fits/images/Red.jpg and /dev/null differ diff --git a/docs/io/fits/index.rst b/docs/io/fits/index.rst index b584d5223d91..b6ac1ee6966c 100644 --- a/docs/io/fits/index.rst +++ b/docs/io/fits/index.rst @@ -1,7 +1,9 @@ .. currentmodule:: astropy.io.fits +.. _astropy-io-fits: + ************************************** -FITS File handling (`astropy.io.fits`) +FITS File Handling (`astropy.io.fits`) ************************************** Introduction @@ -11,324 +13,618 @@ The :mod:`astropy.io.fits` package provides access to FITS files. FITS (Flexible Image Transport System) is a portable file standard widely used in the astronomy community to store images and tables. +.. note:: + + If you want to read or write a single table in FITS format, the + recommended method is via the :ref:`table_io` interface. In particular + see the :ref:`Unified I/O FITS ` section. Likewise, for CCD image + data with a physical unit (e.g., ``electron``), see the :ref:`Unified Image Data` section. + + +.. _tutorial: + Getting Started =============== This section provides a quick introduction of using :mod:`astropy.io.fits`. The goal is to demonstrate the package's basic features without getting into too -much detail. If you are a first time user or have never used Astropy or PyFITS, -this is where you should start. +much detail. If you are a first time user or have never used ``astropy`` or +PyFITS, this is where you should start. See also the :ref:`FAQ ` +for answers to common questions and issues. + Reading and Updating Existing FITS Files ---------------------------------------- -Opening a FITS file +Opening a FITS File ^^^^^^^^^^^^^^^^^^^ -Once the `astropy.io.fits` package is loaded using the standard convention\ [#f1]_, we can open an existing FITS file: +.. note:: + + The ``astropy.io.fits.util.get_testdata_filepath()`` function, + used in the examples here, returns file path for test data shipped with ``astropy``. + To work with your own data instead, please use :func:`astropy.io.fits.open` or :ref:`io-fits-intro-convenience-functions`, + which take either the relative or absolute path as string or :term:`python:path-like object`. + +Once the `astropy.io.fits` package is loaded using the standard convention +[#f1]_, we can open an existing FITS file:: >>> from astropy.io import fits - >>> hdulist = fits.open('input.fits') - -The :func:`open` function has several optional arguments which -will be discussed in a later chapter. The default mode, as in the above -example, is "readonly". The open function returns an object called an -:class:`HDUList` which is a Python list-like collection of HDU objects. An HDU -(Header Data Unit) is the highest level component of the FITS file structure. -So, after the above open call, ``hdulist[0]`` is the primary HDU, -``hdulist[1]``, if any, is the first extension HDU, etc. It should be noted -that Astropy is using zero-based indexing when referring to HDUs and header -cards, though the FITS standard (which was designed with FORTRAN in mind) uses -one-based indexing. + >>> fits_image_filename = fits.util.get_testdata_filepath('test0.fits') + + >>> hdul = fits.open(fits_image_filename) + +The :func:`open` function has several optional arguments which will be +discussed in a later chapter. The default mode, as in the above example, is +"readonly". The open function returns an object called an :class:`HDUList` +which is a `list`-like collection of HDU objects. An HDU (Header Data Unit) is +the highest level component of the FITS file structure, consisting of a header +and (typically) a data array or table. + +After the above open call, ``hdul[0]`` is the primary HDU, ``hdul[1]`` is +the first extension HDU, etc. (if there are any extensions), and so on. It +should be noted that ``astropy`` uses zero-based indexing when referring to +HDUs and header cards, though the FITS standard (which was designed with +Fortran in mind) uses one-based indexing. The :class:`HDUList` has a useful method :meth:`HDUList.info`, which summarizes the content of the opened FITS file: - >>> hdulist.info() - Filename: test1.fits - No. Name Type Cards Dimensions Format - 0 PRIMARY PrimaryHDU 220 () int16 - 1 SCI ImageHDU 61 (800, 800) float32 - 2 SCI ImageHDU 61 (800, 800) float32 - 3 SCI ImageHDU 61 (800, 800) float32 - 4 SCI ImageHDU 61 (800, 800) float32 + >>> hdul.info() + Filename: ...test0.fits + No. Name Ver Type Cards Dimensions Format + 0 PRIMARY 1 PrimaryHDU 138 () + 1 SCI 1 ImageHDU 61 (40, 40) int16 + 2 SCI 2 ImageHDU 61 (40, 40) int16 + 3 SCI 3 ImageHDU 61 (40, 40) int16 + 4 SCI 4 ImageHDU 61 (40, 40) int16 After you are done with the opened file, close it with the :meth:`HDUList.close` method: - >>> hdulist.close() + >>> hdul.close() + +You can avoid closing the file manually by using :func:`open` as context +manager:: + + >>> with fits.open(fits_image_filename) as hdul: + ... hdul.info() + Filename: ...test0.fits + No. Name Ver Type Cards Dimensions Format + 0 PRIMARY 1 PrimaryHDU 138 () + 1 SCI 1 ImageHDU 61 (40, 40) int16 + 2 SCI 2 ImageHDU 61 (40, 40) int16 + 3 SCI 3 ImageHDU 61 (40, 40) int16 + 4 SCI 4 ImageHDU 61 (40, 40) int16 -The headers will still be accessible after the HDUList is closed. The data may -or may not be accessible depending on whether the data are touched and if they -are memory-mapped, see later chapters for detail. +After exiting the ``with`` scope the file will be closed automatically. That is +(generally) the preferred way to open a file in Python, because it will close +the file even if an exception happens. + +If the file is opened with ``lazy_load_hdus=False``, all of the headers will +still be accessible after the HDUList is closed. The headers and data may or +may not be accessible depending on whether the data are touched and if they +are memory-mapped; see later chapters for detail. + +.. _fits-large-files: Working with large files """""""""""""""""""""""" The :func:`open` function supports a ``memmap=True`` argument that allows the array data of each HDU to be accessed with mmap, rather than being read into -memory all at once. This is particularly useful for working with very large -arrays that cannot fit entirely into physical memory. +memory all at once. This is particularly useful for working with very large +arrays that cannot fit entirely into physical memory. Here ``memmap=True`` by +default, and this value is obtained from the configuration item ``astropy.io.fits.Conf.use_memmap``. This has minimal impact on smaller files as well, though some operations, such -as reading the array data sequentially, may incur some additional overhead. On -32-bit systems arrays larger than 2-3 GB cannot be mmap'd (which is fine, -because by that point you're likely to run out of physical memory anyways), but +as reading the array data sequentially, may incur some additional overhead. On +32-bit systems, arrays larger than 2 to 3 GB cannot be mmap'd (which is fine, +because by that point you are likely to run out of physical memory anyways), but 64-bit systems are much less limited in this respect. +.. warning:: + When opening a file with ``memmap=True``, because of how mmap works this + means that when the HDU data is accessed (i.e., ``hdul[0].data``) another + handle to the FITS file is opened by mmap. This means that even after + calling ``hdul.close()`` the mmap still holds an open handle to the data so + that it can still be accessed by unwary programs that were built with the + assumption that the .data attribute has all of the data in-memory. + + In order to force the mmap to close, either wait for the containing + ``HDUList`` object to go out of scope, or manually call + ``del hdul[0].data``. (This works so long as there are no other references + held to the data array.) + +.. _fits-cloud-files: + +Working with remote and cloud-hosted files +"""""""""""""""""""""""""""""""""""""""""" + +The :func:`open` function supports a ``use_fsspec`` argument which allows file +paths to be opened using |fsspec|. +The ``fsspec`` package supports a range of remote and distributed storage +backends such as Amazon and Google Cloud Storage. For example, you can access a +Hubble Space Telescope image located in the Hubble's public +Amazon S3 bucket as follows: + +.. doctest-requires:: fsspec + + >>> # Location of a large Hubble archive image in Amazon S3 (213 MB) + >>> uri = "s3://stpubdata/hst/public/j8pu/j8pu0y010/j8pu0y010_drc.fits" + ... + >>> # Extract a 10-by-20 pixel cutout image + >>> with fits.open(uri, use_fsspec=True, fsspec_kwargs={"anon": True}) as hdul: # doctest: +REMOTE_DATA + ... cutout = hdul[1].section[10:20, 30:50] + +Note that the example above obtains a cutout image using the `ImageHDU.section` +attribute rather than the traditional `ImageHDU.data` attribute. +The use of ``.section`` ensures that only the necessary parts of the FITS +image are transferred from the server, rather than downloading the entire data +array. This trick can significantly speed up your code if you require small +subsets of large FITS files located on slow (remote) storage systems. +See :ref:`fits_io_cloud` for additional information on working with remote FITS +files in this way. + +.. seealso:: :ref:`fits_io_cloud`. + + +Unsigned integers +""""""""""""""""" + +Due to the FITS format's Fortran origins, FITS does not natively support +unsigned integer data in images or tables. However, there is a common +convention to store unsigned integers as signed integers, along with a +*shift* instruction (a ``BZERO`` keyword with value ``2 ** (BITPIX - 1)``) to +shift up all signed integers to unsigned integers. For example, when writing +the value ``0`` as an unsigned 32-bit integer, it is stored in the FITS +file as ``-32768``, along with the header keyword ``BZERO = 32768``. + +``astropy`` recognizes and applies this convention by default, so that all data +that looks like it should be interpreted as unsigned integers is automatically +converted (this applies to both images and tables). + +Even with ``uint=False``, the ``BZERO`` shift is still applied, but the +returned array is of "float64" type. To disable scaling/shifting entirely, use +``do_not_scale_image_data=True`` (see :ref:`fits-scaled-data-faq` in the FAQ +for more details). + +Working with compressed files +""""""""""""""""""""""""""""" + +.. note:: + + Files that use compressed HDUs within the FITS file are discussed + in :ref:`Compressed Image Data `. + + +The :func:`open` function will seamlessly open FITS files that have been +compressed with gzip, bzip2, pkzip, lzma or Unix's compress (LZW compression). +Note that in this context we are talking about a FITS file that has been +compressed with one of these utilities (e.g., a .fits.gz file). + +There are some limitations when working with compressed files. For example, +with Zip files that contain multiple compressed files, only the first file will +be accessible. Also bzip2 and lzma do not support the append or update access +mode and LZW-compressed files do not support any writing modes (including append +or update). + +When writing a file (e.g., with the :func:`writeto` function), compression will +be determined based on the filename extension given, or the compression used in +a pre-existing file that is being written to. + + +Working with non-standard files +""""""""""""""""""""""""""""""" +When `astropy.io.fits` reads a FITS file which does not conform to the FITS +standard it will try to make an educated interpretation of non-compliant fields. +This may not always succeed and may trigger warnings when accessing headers or +exceptions when writing to file. Verification of fields written to an output +file can be controlled with the ``output_verify`` parameter of :func:`open`. +Files opened for reading can be verified and fixed with method +``HDUList.verify``. This method is typically invoked after opening the file +but before accessing any headers or data:: + + >>> with fits.open(fits_image_filename) as hdul: + ... hdul.verify('fix') + ... data = hdul[1].data + +In the above example, the call to ``hdul.verify("fix")`` requests that `astropy.io.fits` +fix non-compliant fields and print informative messages. Other options in addition to ``"fix"`` +are described under FITS :ref:`fits_io_verification` + +.. seealso:: FITS :ref:`fits_io_verification`. -Working With a FITS Header -^^^^^^^^^^^^^^^^^^^^^^^^^^ +Working with FITS Headers +^^^^^^^^^^^^^^^^^^^^^^^^^ As mentioned earlier, each element of an :class:`HDUList` is an HDU object with -attributes of header and data, which can be used to access the header keywords -and the data. +``.header`` and ``.data`` attributes, which can be used to access the header +and data portions of the HDU. -For those unfamiliar with FITS headers, they consist of a list of "cards", -where a card contains a keyword, a value, and a comment. The keyword and -comment must both be strings, whereas the value can be a string or an integer, -float, or complex number. Keywords are usually unique within a header, except -in a few special cases. +For those unfamiliar with FITS headers, they consist of a list of 80 byte +"cards", where a card contains a keyword, a value, and a comment. The keyword +and comment must both be strings, whereas the value can be a string or an +integer, floating point number, complex number, or ``True``/``False``. Keywords +are usually unique within a header, except in a few special cases. -The header attribute is a Header instance, another Astropy object. To get the -value associated with a header keyword, simply do (a la Python dicts): +The header attribute is a :class:`Header` instance, another ``astropy`` object. +To get the value associated with a header keyword, do (à la Python dicts):: - >>> hdulist[0].header['targname'] - 'NGC121' + >>> hdul = fits.open(fits_image_filename) + >>> hdul[0].header['DATE'] + '01/04/99' -to get the value of the keyword targname, which is a string 'NGC121'. +to get the value of the keyword "DATE", which is a string '01/04/99'. Although keyword names are always in upper case inside the FITS file, -specifying a keyword name with Astropy is case-insensitive, for the user's +specifying a keyword name with ``astropy`` is case-insensitive for the user's convenience. If the specified keyword name does not exist, it will raise a -:exc:`KeyError` exception. +`KeyError` exception. + +We can also get the keyword value by indexing (à la Python lists):: -We can also get the keyword value by indexing (a la Python lists): + >>> hdul[0].header[7] + 32768.0 - >>> hdulist[0].header[27] - 96 +This example returns the eighth (like Python lists, it is 0-indexed) keyword's +value — a float — 32768.0. -This example returns the 28th (like Python lists, it is 0-indexed) keyword's -value--an integer--96. +Similarly, it is possible to update a keyword's value in ``astropy``, either +through keyword name or index:: -Similarly, it is easy to update a keyword's value in Astropy, either through -keyword name or index: + >>> hdr = hdul[0].header + >>> hdr['targname'] = 'NGC121-a' + >>> hdr[27] = 99 - >>> prihdr = hdulist[0].header - >>> prihdr['targname'] = 'NGC121-a' - >>> prihdr[27] = 99 +Please note however that almost all application code should update header +values via their keyword name and not via their positional index. This is +because most FITS keywords may appear at any position in the header. It is also possible to update both the value and comment associated with a -keyword by assigning them as a tuple: +keyword by assigning them as a tuple:: - >>> prihdr = hdulist[0].header - >>> prihdr['targname'] = ('NGC121-a', 'the observation target') - >>> prihdr['targname'] + >>> hdr = hdul[0].header + >>> hdr['targname'] = ('NGC121-a', 'the observation target') + >>> hdr['targname'] 'NGC121-a' - >>> prihdr.comments['targname'] + >>> hdr.comments['targname'] 'the observation target' -Like a dict, one may also use the above syntax to add a new keyword/value pair -(and optionally a comment as well). In this case the new card is appended to -the end of the header (unless it's a commentary keyword such as COMMENT or +Like a dict, you may also use the above syntax to add a new keyword/value pair +(and optionally a comment as well). In this case the new card is appended to +the end of the header (unless it is a commentary keyword such as COMMENT or HISTORY, in which case it is appended after the last card with that keyword). -Another way to either update an existing card or append a new one is to use the -:meth:`Header.set` method: +Another way to either update an existing card or append a new one is to use the +:meth:`Header.set` method:: - >>> prihdr.set('observer', 'Edwin Hubble') + >>> hdr.set('observer', 'Edwin Hubble') Comment or history records are added like normal cards, though in their case a new card is always created, rather than updating an existing HISTORY or COMMENT -card: - - >>> prihdr['history'] = 'I updated this file 2/26/09' - >>> prihdr['comment'] = 'Edwin Hubble really knew his stuff' - >>> prihdr['comment'] = 'I like using HST observations' - >>> prihdr['comment'] +card:: + + >>> hdr['history'] = 'I updated this file 2/26/09' + >>> hdr['comment'] = 'Edwin Hubble really knew his stuff' + >>> hdr['comment'] = 'I like using HST observations' + >>> hdr['history'] + I updated this file 2/26/09 + >>> hdr['comment'] Edwin Hubble really knew his stuff I like using HST observations Note: Be careful not to confuse COMMENT cards with the comment value for normal cards. -To updating existing COMMENT or HISTORY cards, reference them by index: +To update existing COMMENT or HISTORY cards, reference them by index:: + + >>> hdr['history'][0] = 'I updated this file on 2/27/09' + >>> hdr['history'] + I updated this file on 2/27/09 + >>> hdr['comment'][1] = 'I like using JWST observations' + >>> hdr['comment'] + Edwin Hubble really knew his stuff + I like using JWST observations - >>> prihdr['history'][0] = 'I updated this file on 2/26/09' - >>> prihdr['history'] - I updated this file on 2/26/09 To see the entire header as it appears in the FITS file (with the END card and -padding stripped), simply enter the header object by itself, or print -repr(header): +padding stripped), enter the header object by itself, or +``print(repr(hdr))``:: - >>> header + >>> hdr # doctest: +ELLIPSIS SIMPLE = T / file does conform to FITS standard BITPIX = 16 / number of bits per data pixel NAXIS = 0 / number of data axes - ...all cards are shown... - >>> print repr(header) - ...identical... + ... + >>> print(repr(hdr)) # doctest: +ELLIPSIS + SIMPLE = T / file does conform to FITS standard + BITPIX = 16 / number of bits per data pixel + NAXIS = 0 / number of data axes + ... -It's also possible to view a slice of the header: +Entering only ``print(hdr)`` will also work, but may not be very legible +on most displays, as this displays the header as it is written in the FITS file +itself, which means there are no line breaks between cards. This is a common +source of confusion for new users. - >>> header[:2] +It is also possible to view a slice of the header:: + + >>> hdr[:2] SIMPLE = T / file does conform to FITS standard BITPIX = 16 / number of bits per data pixel Only the first two cards are shown above. To get a list of all keywords, use the :meth:`Header.keys` method just as you -would with a dict: +would with a dict:: - >>> prihdr.keys() + >>> list(hdr.keys()) # doctest: +ELLIPSIS ['SIMPLE', 'BITPIX', 'NAXIS', ...] +.. note:: + + See also :ref:`io-fits-intro-convenience-functions`. + +.. _structural_keywords: -Working With Image Data +Structural Keywords +""""""""""""""""""" + +FITS keywords mix up both metadata and critical information about the file structure +that is needed to parse the file. These *structural* keywords are managed internally by +:mod:`astropy.io.fits` and, in general, should not be touched by the user. Instead one +should use the related attributes of the `astropy.io.fits` classes (see examples below). + +The specific set of structural keywords used by the FITS standard varies with HDU type. +The following table lists which keywords are associated with each HDU type: + +.. csv-table:: Structural Keywords + :header: "HDU Type", "Structural Keywords" + :widths: 20, 20 + + "All", "``SIMPLE``, ``BITPIX``, ``NAXIS``" + ":class:`PrimaryHDU`", "``EXTEND``" + ":class:`ImageHDU`, :class:`TableHDU`, :class:`BinTableHDU`", "``PCOUNT``, ``GCOUNT``" + ":class:`GroupsHDU`", "``NAXIS1``, ``GCOUNT``, ``PCOUNT``, ``GROUPS``" + ":class:`TableHDU`, :class:`BinTableHDU`", "``TFIELDS``, ``TFORM``, ``TBCOL``" + +There are many other reserved keywords, for instance for the data scaling, or for table's column +attributes, as described in the `FITS Standard `__. +Most of these are accessible via attributes of the :class:`Column` or HDU objects, for instance +``hdu.name`` to set ``EXTNAME``, or ``hdu.ver`` for ``EXTVER``. Structural keywords are checked +and/or updated as a consequence of common operations. For example, when: + +1. Setting the data. The ``NAXIS*`` keywords are set from the data shape (``.data.shape``), and ``BITPIX`` + from the data type (``.data.dtype``). +2. Setting the header. Its keywords are updated based on the data properties (as above). +3. Writing a file. All the necessary keywords are deleted, updated or added to the header. +4. Calling an HDU's verify method (e.g., :func:`PrimaryHDU.verify`). Some keywords can be fixed automatically. + +In these cases any hand-written values users might assign to those keywords will be overwritten. + +Working with Image Data ^^^^^^^^^^^^^^^^^^^^^^^ +.. note:: + This section describes reading and writing image data in the FITS format using the + `~astropy.io.fits` package directly. For CCD image data with a unit, you should + consider using the :ref:`Unified Image Data` interface with the + :ref:`CCDData class `. This provides the capability to load data, + uncertainty and mask from a multi-extension FITS (MEF) file. + If an HDU's data is an image, the data attribute of the HDU object will return -a numpy ndarray object. Refer to the numpy documentation for details on -manipulating these numerical arrays. +a ``numpy`` `~numpy.ndarray` object. Refer to the ``numpy`` documentation for +details on manipulating these numerical arrays:: + + >>> data = hdul[1].data - >>> scidata = hdulist[1].data +Here, ``data`` points to the data object in the second HDU (the first HDU, +``hdul[0]``, being the primary HDU) which corresponds to the 'SCI' +extension. Alternatively, you can access the extension by its extension name +(specified in the EXTNAME keyword):: -Here, scidata points to the data object in the second HDU (the first HDU, -``hdulist[0]``, being the primary HDU) in ``hdulist``, which corresponds to -the 'SCI' extension. Alternatively, you can access the extension by its -extension name (specified in the EXTNAME keyword): + >>> data = hdul['SCI'].data - >>> scidata = hdulist['SCI'].data +If there is more than one extension with the same EXTNAME, the EXTVER value +needs to be specified along with the EXTNAME as a tuple; for example:: -If there is more than one extension with the same EXTNAME, EXTVER's value needs -to be specified as the second argument, e.g.: + >>> data = hdul['sci',2].data - >>> scidata = hdulist['sci',2].data +Note that the EXTNAME is also case-insensitive. -The returned numpy object has many attributes and methods for a user to get -information about the array, e. g.: +The returned ``numpy`` object has many attributes and methods for a user to get +information about the array, for example:: - >>> scidata.shape - (800, 800) - >>> scidata.dtype.name - 'float32' + >>> data.shape + (40, 40) + >>> data.dtype.name + 'int16' -Since image data is a numpy object, we can slice it, view it, and perform -mathematical operations on it. To see the pixel value at x=5, y=2: +Since image data is a ``numpy`` object, we can slice it, view it, and perform +mathematical operations on it. To see the pixel value at x=5, y=2:: - >>> print scidata[1, 4] + >>> print(data[1, 4]) + 348 -Note that, like C (and unlike FORTRAN), Python is 0-indexed and the indices -have the slowest axis first and fast axis last, i.e. for a 2-D image, the fast -axis (X-axis) which corresponds to the FITS NAXIS1 keyword, is the second -index. Similarly, the 1-indexed sub-section of x=11 to 20 (inclusive) and y=31 -to 40 (inclusive) would be given in Python as: +Note that, like C (and unlike Fortran), Python is 0-indexed and the indices +have the slowest axis first and fastest changing axis last; that is, for a 2D +image, the fast axis (X-axis) which corresponds to the FITS NAXIS1 keyword, is +the second index. Similarly, the 1-indexed subsection of x=11 to 20 +(inclusive) and y=31 to 40 (inclusive) would be given in Python as:: - >>> scidata[30:40, 10:20] + >>> data[30:40, 10:20] + array([[350, 349, 349, 348, 349, 348, 349, 347, 350, 348], + [348, 348, 348, 349, 348, 349, 347, 348, 348, 349], + [348, 348, 347, 349, 348, 348, 349, 349, 349, 349], + [349, 348, 349, 349, 350, 349, 349, 347, 348, 348], + [348, 348, 348, 348, 349, 348, 350, 349, 348, 349], + [348, 347, 349, 349, 350, 348, 349, 348, 349, 347], + [347, 348, 347, 348, 349, 349, 350, 349, 348, 348], + [349, 349, 350, 348, 350, 347, 349, 349, 349, 348], + [349, 348, 348, 348, 348, 348, 349, 347, 349, 348], + [349, 349, 349, 348, 350, 349, 349, 350, 348, 350]], dtype='>i2') -To update the value of a pixel or a sub-section: +To update the value of a pixel or a subsection:: - >>> scidata[30:40, 10:20] = scidata[1, 4] = 999 + >>> data[30:40, 10:20] = data[1, 4] = 999 -This example changes the values of both the pixel \[1, 4] and the sub-section -\[30:40, 10:20] to the new value of 999. See the `Numpy documentation`_ for +This example changes the values of both the pixel \[1, 4] and the subsection +\[30:40, 10:20] to the new value of 999. See the `Numpy documentation`_ for more details on Python-style array indexing and slicing. The next example of array manipulation is to convert the image data from counts -to flux: +to flux:: - >>> photflam = hdulist[1].header['photflam'] - >>> exptime = prihdr['exptime'] - >>> scidata \*= photflam / exptime + >>> photflam = hdul[1].header['photflam'] + >>> exptime = hdr['exptime'] + >>> data = data * photflam / exptime + >>> hdul.close() -This example performs the math on the array in-place, thereby keeping the -memory usage to a minimum. +Note that performing an operation like this on an entire image requires holding +the entire image in memory. This example performs the multiplication in-place +so that no copies are made, but the original image must first be able to fit in +main memory. For most observations this should not be an issue on modern +personal computers. -If at this point you want to preserve all the changes you made and write it to -a new file, you can use the :meth:`HDUList.writeto` method (see below). +If at this point you want to preserve all of the changes you made and write it +to a new file, you can use the :meth:`HDUList.writeto` method (see below). -.. _Numpy documentation: http://docs.scipy.org/doc/numpy/reference/arrays.indexing.html +.. _Numpy documentation: https://numpy.org/doc/stable/reference/routines.indexing.html +.. note:: -Working With Table Data + See more information in :doc:`/io/fits/usage/image`. + +Working with Table Data ^^^^^^^^^^^^^^^^^^^^^^^ -If you are familiar with the record array in numpy, you will find the table -data is basically a record array with some extra properties. But familiarity -with record arrays is not a prerequisite for this Guide. +.. note:: + This section describes reading and writing table data in the FITS format + using the `~astropy.io.fits` package directly. If you want to read or write a single + entire table in FITS format, the you should consider using the :ref:`table_io` + interface (e.g., ``QTable.read``). See the :ref:`Unified I/O FITS ` + section for details. Like images, the data portion of a FITS table extension is in the ``.data`` -attribute: +attribute:: + + >>> fits_table_filename = fits.util.get_testdata_filepath('tb.fits') + >>> hdul = fits.open(fits_table_filename) + >>> data = hdul[1].data # assuming the first extension is a table + >>> hdul.close() + +If you are familiar with ``numpy`` `~numpy.recarray` (record array) objects, you +will find the table data is basically a record array with some extra +properties. But familiarity with record arrays is not a prerequisite for this +guide. - >>> hdulist = fits.open('table.fits') - >>> tbdata = hdulist[1].data # assuming the first extension is a table +To see the first row of the table:: -To see the first row of the table: + >>> print(data[0]) + (np.int32(1), 'abc', np.float64(3.7000000715255736), np.False_) - >>> print tbdata[0] - (1, 'abc', 3.7000002861022949, 0) +Each row in the table is a :class:`FITS_record` object which looks like a +(Python) tuple containing elements of heterogeneous data types. In this +example: an integer, a string, a floating point number, and a Boolean value. So +the table data are just an array of such records. More commonly, a user is +likely to access the data in a column-wise way. This is accomplished by using +the :meth:`~FITS_rec.field` method. To get the first column (or "field" in +NumPy parlance — it is used here interchangeably with "column") of the table, +use:: -Each row in the table is a :class:`FITS_rec` object which looks like a (Python) -tuple containing elements of heterogeneous data types. In this example: an -integer, a string, a floating point number, and a Boolean value. So the table -data are just an array of such records. More commonly, a user is likely to -access the data in a column-wise way. This is accomplished by using the -:meth:`~FITS_rec.field` method. To get the first column (or field) of the -table, use: + >>> data.field(0) + array([1, 2]...) - >>> tbdata.field(0) - array([1, 2]) +A ``numpy`` object with the data type of the specified field is returned. -A numpy object with the data type of the specified field is returned. +Like header keywords, a column can be referred either by index, as above, or by +name:: -Like header keywords, a field can be referred either by index, as above, or by -name: + >>> data.field('c1') + array([1, 2]...) - >>> tbdata.field('id') - array([1, 2]) +When accessing a column by name, dict-like access is also possible (and even +preferable):: -But how do we know what field names we've got? First, let's introduce another -attribute of the table HDU: the :attr:`~HDUList.columns` attribute: + >>> data['c1'] + array([1, 2]...) - >>> cols = hdulist[1].columns +In most cases it is preferable to access columns by their name, as the column +name is entirely independent of its physical order in the table. As with +header keywords, column names are case-insensitive. + +But how do we know what columns we have in a table? First, we will introduce +another attribute of the table HDU: the :attr:`~BinTableHDU.columns` +attribute:: + + >>> cols = hdul[1].columns This attribute is a :class:`ColDefs` (column definitions) object. If we use the -:meth:`ColDefs.info` method: +:meth:`ColDefs.info` method from the interactive prompt:: >>> cols.info() - name: - ['c1', 'c2', 'c3', 'c4'] - format: - ['1J', '3A', '1E', '1L'] - unit: - ['', '', '', ''] - null: - [-2147483647, '', '', ''] - bscale: - ['', '', 3, ''] - bzero: - ['', '', 0.40000000000000002, ''] - disp: - ['I11', 'A3', 'G15.7', 'L6'] - start: - ['', '', '', ''] - dim: - ['', '', '', ''] - -it will show all its attributes, such as names, formats, bscales, bzeros, etc. -We can also get these properties individually, e.g.: + name: + ['c1', 'c2', 'c3', 'c4'] + format: + ['1J', '3A', '1E', '1L'] + unit: + ['', '', '', ''] + null: + [-2147483647, '', '', ''] + bscale: + ['', '', 3, ''] + bzero: + ['', '', 0.4, ''] + disp: + ['I11', 'A3', 'G15.7', 'L6'] + start: + ['', '', '', ''] + dim: + ['', '', '', ''] + coord_type: + ['', '', '', ''] + coord_unit: + ['', '', '', ''] + coord_ref_point: + ['', '', '', ''] + coord_ref_value: + ['', '', '', ''] + coord_inc: + ['', '', '', ''] + time_ref_pos: + ['', '', '', ''] + +it will show the attributes of all columns in the table, such as their names, +formats, bscales, bzeros, etc. A similar output that will display the column +names and their formats can be printed from within a script with:: + + >>> hdul[1].columns + ColDefs( + name = 'c1'; format = '1J'; null = -2147483647; disp = 'I11' + name = 'c2'; format = '3A'; disp = 'A3' + name = 'c3'; format = '1E'; bscale = 3; bzero = 0.4; disp = 'G15.7' + name = 'c4'; format = '1L'; disp = 'L6' + ) + +We can also get these properties individually; for example:: >>> cols.names - ['ID', 'name', 'mag', 'flag'] + ['c1', 'c2', 'c3', 'c4'] returns a (Python) list of field names. -Since each field is a numpy object, we'll have the entire arsenal of numpy -tools to use. We can reassign (update) the values: +Since each field is a ``numpy`` object, we will have the entire arsenal of +``numpy`` tools to use. We can reassign (update) the values:: + + >>> data['c4'][:] = 0 + +take the mean of a column:: - >>> tbdata.field('flag')[:] = 0 + >>> data['c3'].mean() # doctest: +FLOAT_CMP + np.float64(5.19999989271164) +and so on. Save File Changes ^^^^^^^^^^^^^^^^^ @@ -338,19 +634,25 @@ header or data, the user can use :meth:`HDUList.writeto` to save the changes. This takes the version of headers and data in memory and writes them to a new FITS file on disk. Subsequent operations can be performed to the data in memory and written out to yet another different file, all without recopying the -original data to (more) memory. +original data to (more) memory: - >>> hdulist.writeto('newimage.fits') +.. code:: python + + hdul.writeto('newtable.fits') will write the current content of ``hdulist`` to a new disk file newfile.fits. If a file was opened with the update mode, the :meth:`HDUList.flush` method can -also be used to write all the changes made since :func:`open`, back to the +also be used to write all of the changes made since :func:`open`, back to the original file. The :meth:`~HDUList.close` method will do the same for a FITS -file opened with update mode. +file opened with update mode: + +.. code:: python + + with fits.open('original.fits', mode='update') as hdul: + # Change something in hdul. + hdul.flush() # changes are written back to original.fits - >>> f = fits.open('original.fits', mode='update') - ... # making changes in data and/or header - >>> f.flush() # changes are written back to original.fits + # closing the file will also flush any changes and prevent further writing Creating a New FITS File @@ -360,132 +662,225 @@ Creating a New Image File ^^^^^^^^^^^^^^^^^^^^^^^^^ So far we have demonstrated how to read and update an existing FITS file. But -how about creating a new FITS file from scratch? Such tasks are very easy in -Astropy for an image HDU. We'll first demonstrate how to create a FITS file -consisting only the primary HDU with image data. +how about creating a new FITS file from scratch? Such tasks are very convenient +in ``astropy`` for an image HDU. We will first demonstrate how to create a FITS +file consisting of only the primary HDU with image data. -First, we create a numpy object for the data part: +First, we create a ``numpy`` object for the data part:: >>> import numpy as np - >>> n = np.arange(100) # a simple sequence from 0 to 99 + >>> data = np.arange(100.0) # a simple sequence of floats from 0.0 to 99.0 -Next, we create a :class:`PrimaryHDU` object to encapsulate the data: +Next, we create a :class:`PrimaryHDU` object to encapsulate the data:: - >>> hdu = fits.PrimaryHDU(n) + >>> hdu = fits.PrimaryHDU(data=data) -We then create a HDUList to contain the newly created primary HDU, and write to -a new file: +We then create an :class:`HDUList` to contain the newly created primary HDU, and write to +a new file:: - >>> hdulist = fits.HDUList([hdu]) - >>> hdulist.writeto('new.fits') + >>> hdul = fits.HDUList([hdu]) + >>> hdul.writeto('new1.fits') -That's it! In fact, Astropy even provides a shortcut for the last two lines to -accomplish the same behavior: +That is it! In fact, ``astropy`` even provides a shortcut for the last two +lines to accomplish the same behavior:: - >>> hdu.writeto('new.fits') + >>> hdu.writeto('new2.fits') + +This will write a single HDU to a FITS file without having to manually +encapsulate it in an :class:`HDUList` object first. Creating a New Table File ^^^^^^^^^^^^^^^^^^^^^^^^^ -To create a table HDU is a little more involved than image HDU, because a +.. note:: + + If you want to create a **binary** FITS table with no other HDUs, + you can use :class:`~astropy.table.Table` instead and then write to FITS. + This is less complicated than "lower-level" FITS interface:: + + >>> from astropy.table import Table + >>> t = Table([[1, 2], [4, 5], [7, 8]], names=('a', 'b', 'c')) + >>> t.write('table1.fits', format='fits') + + The equivalent code using ``astropy.io.fits`` would look like this: + + >>> from astropy.io import fits + >>> import numpy as np + >>> c1 = fits.Column(name='a', array=np.array([1, 2]), format='K') + >>> c2 = fits.Column(name='b', array=np.array([4, 5]), format='K') + >>> c3 = fits.Column(name='c', array=np.array([7, 8]), format='K') + >>> t = fits.BinTableHDU.from_columns([c1, c2, c3]) + >>> t.writeto('table2.fits') + +To create a table HDU is a little more involved than an image HDU, because a table's structure needs more information. First of all, tables can only be an extension HDU, not a primary. There are two kinds of FITS table extensions: -ASCII and binary. We'll use binary table examples here. +ASCII and binary. We will use binary table examples here. To create a table from scratch, we need to define columns first, by constructing the :class:`Column` objects and their data. Suppose we have two columns, the first containing strings, and the second containing floating point -numbers: +numbers:: - >>> from astropy.io import fits >>> import numpy as np >>> a1 = np.array(['NGC1001', 'NGC1002', 'NGC1003']) >>> a2 = np.array([11.1, 12.3, 15.2]) >>> col1 = fits.Column(name='target', format='20A', array=a1) >>> col2 = fits.Column(name='V_mag', format='E', array=a2) -Next, create a :class:`ColDefs` (column-definitions) object for all columns: +.. note:: + + It is not necessary to create a :class:`Column` object explicitly + if the data is stored in a + `structured array `_. + +Next, create a :class:`ColDefs` (column-definitions) object for all columns:: >>> cols = fits.ColDefs([col1, col2]) -Now, create a new binary table HDU object by using the :func:`new_table()` -function: +Now, create a new binary table HDU object by using the +:func:`BinTableHDU.from_columns` function:: - >>> tbhdu = fits.new_table(cols) + >>> hdu = fits.BinTableHDU.from_columns(cols) This function returns (in this case) a :class:`BinTableHDU`. -Of course, you can do this more concisely: +The data structure used to represent FITS tables is called a :class:`FITS_rec` +and is derived from the :class:`numpy.recarray` interface. When creating +a new table HDU the individual column arrays will be assembled into a single +:class:`FITS_rec` array. - >>> from astropy.io import fits - >>> tbhdu = fits.new_table( - ... fits.ColDefs([fits.Column(name='target', format='20A', array=a1), - ... fits.Column(name='V_mag', format='E', array=a2)])) +You can create a :class:`BinTableHDU` more concisely without creating intermediate +variables for the individual columns and without manually creating a +:class:`ColDefs` object:: + + >>> hdu = fits.BinTableHDU.from_columns( + ... [fits.Column(name='target', format='20A', array=a1), + ... fits.Column(name='V_mag', format='E', array=a2)]) + +Now you may write this new table HDU directly to a FITS file like so:: + + >>> hdu.writeto('table3.fits') + +This shortcut will automatically create a minimal primary HDU with no data and +prepend it to the table HDU to create a valid FITS file. If you require +additional data or header keywords in the primary HDU you may still create a +:class:`PrimaryHDU` object and build up the FITS file manually using an +:class:`HDUList`, as described in the next section. + +Creating a Multi-Extension FITS (MEF) file +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +In the previous examples we created files with a single meaningful extension (a +:class:`PrimaryHDU` or :class:`BinTableHDU`). To create a file with multiple +extensions we need to create extension HDUs and append them to an :class:`HDUList`. + +First, we create some data for Image extensions and we place the data into +separate :class:`PrimaryHDU` and :class:`ImageHDU` objects:: -As before, we create a :class:`PrimaryHDU` object to encapsulate the data: + >>> import numpy as np + >>> primary_hdu = fits.PrimaryHDU(data=np.ones((3, 3))) + >>> image_hdu = fits.ImageHDU(data=np.ones((100, 100)), name="MYIMAGE") + >>> image_hdu2 = fits.ImageHDU(data=np.ones((10, 10, 10)), name="MYCUBE") + +A multi-extension FITS file is not constrained to be only imaging or table data, we +can mix them. To show this we'll use the example from the previous section to make a +:class:`BinTableHDU`:: + + >>> c1 = fits.Column(name='a', array=np.array([1, 2]), format='K') + >>> c2 = fits.Column(name='b', array=np.array([4, 5]), format='K') + >>> c3 = fits.Column(name='c', array=np.array([7, 8]), format='K') + >>> table_hdu = fits.BinTableHDU.from_columns([c1, c2, c3]) + +Now when we create the :class:`HDUList` we list all extensions we want to +include:: + + >>> hdul = fits.HDUList([primary_hdu, image_hdu, table_hdu]) + +Because :class:`HDUList` acts like a :class:`list` we can also append, for example, +an :class:`ImageHDU` to an already existing :class:`HDUList`:: + + >>> hdul.append(image_hdu2) - >>> hdu = fits.PrimaryHDU(n) +Multi-extension :class:`HDUList` are treated just like those with only a +:class:`PrimaryHDU`, so to save the file use :func:`HDUList.writeto` as shown above. -We then create a HDUList containing both the primary HDU and the newly created -table extension, and write to a new file: +.. note:: - >>> thdulist = fits.HDUList([hdu, tbhdu]) - >>> thdulist.writeto('table.fits') + The FITS standard enforces all files to have exactly one :class:`PrimaryHDU` that + is the first HDU present in the file. This standard is enforced during the call to + :func:`HDUList.writeto` and an error will be raised if it is not met. See the + ``output_verify`` option in :func:`HDUList.writeto` for ways to fix or ignore these + warnings. -If this will be the only extension of the new FITS file and you only have a -minimal primary HDU with no data, Astropy again provides a shortcut: +In the previous example the :class:`PrimaryHDU` contained actual data. In some cases it +is desirable to have a minimal :class:`PrimaryHDU` with only basic header information. +To do this, first create a new :class:`Header` object to encapsulate any keywords you want +to include in the primary HDU, then as before create a :class:`PrimaryHDU`:: - >>> tbhdu.writeto('table.fits') + >>> hdr = fits.Header() + >>> hdr['OBSERVER'] = 'Edwin Hubble' + >>> hdr['COMMENT'] = "Here's some commentary about this FITS file." + >>> empty_primary = fits.PrimaryHDU(header=hdr) -Alternatively, you can append it to the hdulist we have already created from -the image file section: +When we create a new primary HDU with a custom header as in the above example, +this will automatically include any additional header keywords that are +*required* by the FITS format (keywords such as ``SIMPLE`` and ``NAXIS`` for +example). In general, users should not have to manually manage such keywords, +and should only create and modify observation-specific informational keywords. - >>> hdulist.append(tbhdu) +We then create an HDUList containing both the primary HDU and any other HDUs want:: -So far, we have covered the most basic features of `astropy.io.fits`. In the -following chapters we'll show more advanced examples and explain options in -each class and method. + >>> hdul = fits.HDUList([empty_primary, image_hdu2, table_hdu]) +.. _io-fits-intro-convenience-functions: Convenience Functions --------------------- -`astropy.io.fits` also provides several high level ("convenience") functions. -Such a convenience function is a "canned" operation to achieve one simple task. +`astropy.io.fits` also provides several high-level ("convenience") functions. +Such a convenience function is a "canned" operation to achieve one task. By using these "convenience" functions, a user does not have to worry about -opening or closing a file, all the housekeeping is done implicitly. +opening or closing a file; all of the housekeeping is done implicitly. + +.. warning:: + + These functions are useful for interactive Python sessions and less complex + analysis scripts, but should not be used for application code, as they + are highly inefficient. For example, each call to :func:`getval` + requires re-parsing the entire FITS file. Code that makes repeated use + of these functions should instead open the file with :func:`open` + and access the data structures directly. The first of these functions is :func:`getheader`, to get the header of an HDU. Here are several examples of getting the header. Only the file name is required for this function. The rest of the arguments are optional and flexible to -specify which HDU the user wants to get: +specify which HDU the user wants to access:: >>> from astropy.io.fits import getheader - >>> getheader('in.fits') # get default HDU (=0), i.e. primary HDU's header - >>> getheader('in.fits', 0) # get primary HDU's header - >>> getheader('in.fits', 2) # the second extension - # the HDU with EXTNAME='sci' (if there is only 1) - >>> getheader('in.fits', 'sci') - # the HDU with EXTNAME='sci' and EXTVER=2 - >>> getheader('in.fits', 'sci', 2) - >>> getheader('in.fits', ('sci', 2)) # use a tuple to do the same - >>> getheader('in.fits', ext=2) # the second extension - # the 'sci' extension, if there is only 1 - >>> getheader('in.fits', extname='sci') - # the HDU with EXTNAME='sci' and EXTVER=2 - >>> getheader('in.fits', extname='sci', extver=2) - # ambiguous specifications will raise an exception, DON'T DO IT!! - >>> getheader('in.fits', ext=('sci',1), extname='err', extver=2) + >>> hdr = getheader(fits_image_filename) # get default HDU (=0), i.e. primary HDU's header + >>> hdr = getheader(fits_image_filename, ext=0) # get primary HDU's header + >>> hdr = getheader(fits_image_filename, ext=2) # the second extension + >>> hdr = getheader(fits_image_filename, extname='sci') # the first HDU with EXTNAME='SCI' + >>> hdr = getheader(fits_image_filename, extname='sci', extver=2) # HDU with EXTNAME='SCI' and EXTVER=2 + >>> hdr = getheader(fits_image_filename, ext=('sci', 2)) # use a tuple to do the same + +Ambiguous specifications will raise an exception:: + + >>> getheader(fits_image_filename, ext=('sci', 1), extname='err', extver=2) + Traceback (most recent call last): + ... + TypeError: Redundant/conflicting extension arguments(s): ... After you get the header, you can access the information in it, such as getting -and modifying a keyword value: +and modifying a keyword value:: - >>> from astropy.io.fits import getheader - >>> hdr = getheader('in.fits', 1) # get first extension's header - >>> filter = hdr['filter'] # get the value of the keyword "filter' - >>> val = hdr[10] # get the 11th keyword's value - >>> hdr['filter'] = 'FW555' # change the keyword value + >>> fits_image_2_filename = fits.util.get_testdata_filepath('o4sp040b0_raw.fits') + >>> hdr = getheader(fits_image_2_filename, ext=0) # get primary hdu's header + >>> filter = hdr['filter'] # get the value of the keyword "filter' + >>> val = hdr[10] # get the 11th keyword's value + >>> hdr['filter'] = 'FW555' # change the keyword value For the header keywords, the header is like a dictionary, as well as a list. The user can access the keywords either by name or by numeric index, as @@ -493,67 +888,106 @@ explained earlier in this chapter. If a user only needs to read one keyword, the :func:`getval` function can further simplify to just one call, instead of two as shown in the above -examples: +examples:: >>> from astropy.io.fits import getval - >>> flt = getval('in.fits', 'filter', 1) # get 1st extension's keyword - # FILTER's value - >>> val = getval('in.fits', 10, 'sci', 2) # get the 2nd sci extension's - # 11th keyword's value + >>> # get 0th extension's keyword FILTER's value + >>> getval(fits_image_2_filename, 'filter', ext=0) + 'Clear' + + >>> # get the 2nd sci extension's 11th keyword's value + >>> getval(fits_image_2_filename, 10, extname='sci', extver=2) + False + +To edit a single header value in the header for extension 0, use the +:func:`setval` function. For example, to change the value of the "filter" +keyword:: + + >>> fits.setval(fits_image_2_filename, "filter", value="FW555") # doctest: +SKIP + +This can also be used to create a new keyword-value pair ("card" in FITS +lingo):: + + >>> fits.setval(fits_image_2_filename, "ANEWKEY", value="some value") # doctest: +SKIP The function :func:`getdata` gets the data of an HDU. Similar to :func:`getheader`, it only requires the input FITS file name while the extension is specified through the optional arguments. It does have one extra optional argument header. If header is set to True, this function will return -both data and header, otherwise only data is returned. +both data and header, otherwise only data is returned:: >>> from astropy.io.fits import getdata - >>> dat = getdata('in.fits', 'sci', 3) # get 3rd sci extension's data - # get 1st extension's data and header - >>> data, hdr = getdata('in.fits', 1, header=True) + >>> # get 3rd sci extension's data: + >>> data = getdata(fits_image_filename, extname='sci', extver=3) + >>> # get 1st extension's data AND header: + >>> data, hdr = getdata(fits_image_filename, ext=1, header=True) The functions introduced above are for reading. The next few functions -demonstrate convenience functions for writing: +demonstrate convenience functions for writing:: - >>> fits.writeto('out.fits', data, header) + >>> fits.writeto('out.fits', data, hdr) The :func:`writeto` function uses the provided data and an optional header to write to an output FITS file. - >>> fits.append('out.fits', data, header) +:: + + >>> fits.append('out.fits', data, hdr) The :func:`append` function will use the provided data and the optional header to append to an existing FITS file. If the specified output file does not exist, it will create one. - >>> from astropy.io.fits import update - >>> update(file, dat, hdr, 'sci') # update the 'sci' extension - >>> update(file, dat, 3) # update the 3rd extension - >>> update(file, dat, hdr, 3) # update the 3rd extension - >>> update(file, dat, 'sci', 2) # update the 2nd SCI extension - >>> update(file, dat, 3, header=hdr) # update the 3rd extension - >>> update(file, dat, header=hdr, ext=5) # update the 5th extension +.. code:: python + + from astropy.io.fits import update + update(filename, dat, hdr, 'sci') # update the 'sci' extension + update(filename, dat, 3) # update the 3rd extension + update(filename, dat, hdr, 3) # update the 3rd extension + update(filename, dat, 'sci', 2) # update the 2nd SCI extension + update(filename, dat, 3, header=hdr) # update the 3rd extension + update(filename, dat, header=hdr, ext=5) # update the 5th extension The :func:`update` function will update the specified extension with the input -data/header. The 3rd argument can be the header associated with the data. If -the 3rd argument is not a header, it (and other positional arguments) are +data/header. The third argument can be the header associated with the data. If +the third argument is not a header, it (and other positional arguments) are assumed to be the extension specification(s). Header and extension specs can also be keyword arguments. +The :func:`printdiff` function will print a difference report of two FITS files, +including headers and data. The first two arguments must be two FITS +filenames or FITS file objects with matching data types (i.e., if using strings +to specify filenames, both inputs must be strings). The third +argument is an optional extension specification, with the same call format +of :func:`getheader` and :func:`getdata`. In addition you can add any keywords +accepted by the :class:`FITSDiff` class. + +.. code:: python + + from astropy.io.fits import printdiff + # get a difference report of ext 2 of inA and inB + printdiff('inA.fits', 'inB.fits', ext=2) + # ignore HISTORY and COMMENT keywords + printdiff('inA.fits', 'inB.fits', ignore_keywords=('HISTORY','COMMENT') + Finally, the :func:`info` function will print out information of the specified -FITS file: - - >>> fits.info('test0.fits') - Filename: test0.fits - No. Name Type Cards Dimensions Format - 0 PRIMARY PrimaryHDU 138 () Int16 - 1 SCI ImageHDU 61 (400, 400) Int16 - 2 SCI ImageHDU 61 (400, 400) Int16 - 3 SCI ImageHDU 61 (400, 400) Int16 - 4 SCI ImageHDU 61 (400, 400) Int16 - -Using `io.fits` -=============== +FITS file:: + + >>> fits.info(fits_image_filename) + Filename: ...test0.fits + No. Name Ver Type Cards Dimensions Format + 0 PRIMARY 1 PrimaryHDU 138 () + 1 SCI 1 ImageHDU 61 (40, 40) int16 + 2 SCI 2 ImageHDU 61 (40, 40) int16 + 3 SCI 3 ImageHDU 61 (40, 40) int16 + 4 SCI 4 ImageHDU 61 (40, 40) int16 + +This is one of the most useful convenience functions for getting an overview of +what a given file contains without looking at any of the details. + + +Using `astropy.io.fits` +======================= .. toctree:: :maxdepth: 2 @@ -564,7 +998,27 @@ Using `io.fits` usage/unfamiliar usage/scripts usage/misc - usage/examples + usage/cloud + +Command-Line Utilities +====================== + +For convenience, several of ``astropy``'s sub-packages install utility programs +on your system which allow common tasks to be performed without having +to open a Python interpreter. These utilities include: + +- `~astropy.io.fits.scripts.fitsheader`: prints the headers of a FITS file. + +- `~astropy.io.fits.scripts.fitscheck`: verifies and optionally rewrites + the CHECKSUM and DATASUM keywords of a FITS file. + +- :ref:`fitsdiff`: compares two FITS files and reports the differences. + +- :ref:`fits2bitmap`: converts FITS images to bitmaps, including scaling and + stretching. + +- :ref:`wcslint `: checks the :ref:`WCS ` keywords in a + FITS file for compliance against the standards. Other Information ================= @@ -573,26 +1027,25 @@ Other Information :maxdepth: 1 appendix/faq + appendix/header_transition appendix/history +.. note that if this section gets too long, it should be moved to a separate + doc page - see the top of performance.inc.rst for the instructions on how to do + that + +.. include:: performance.inc.rst + Reference/API ============= -.. toctree:: - :maxdepth: 3 +.. automodule:: astropy.io.fits - api/files.rst - api/hdulists.rst - api/hdus.rst - api/headers.rst - api/cards.rst - api/tables.rst - api/images.rst - api/diff.rst - api/verification.rst +.. toctree:: + :maxdepth: 2 + api/index.rst .. rubric:: Footnotes .. [#f1] For legacy code only that already depends on PyFITS, it's acceptable to continue using "from astropy.io import fits as pyfits". - diff --git a/docs/io/fits/performance.inc.rst b/docs/io/fits/performance.inc.rst new file mode 100644 index 000000000000..cb3827a8dae8 --- /dev/null +++ b/docs/io/fits/performance.inc.rst @@ -0,0 +1,52 @@ +.. note that if this is changed from the default approach of using an *include* + (in index.rst) to a separate performance page, the header needs to be changed + from === to ***, the filename extension needs to be changed from .inc.rst to + .rst, and a link needs to be added in the subpackage toctree + +.. _astropy-io-fits-performance: + +Performance Tips +================ + +Use dask for lazy compute +------------------------- + +It is possible to set the data array for :class:`~astropy.io.fits.PrimaryHDU` +and :class:`~astropy.io.fits.ImageHDU` to a `dask `_ array. +If this is written to disk, the dask array will be computed as it is being +written, which will avoid using excessive memory: + +.. doctest-requires:: dask + + >>> import dask.array as da + >>> array = da.random.random((1000, 1000)) + >>> from astropy.io import fits + >>> hdu = fits.PrimaryHDU(data=array) + >>> hdu.writeto('test_dask.fits') + +Arbitrary padding end of file will degrade performance +------------------------------------------------------ + +As discussed in detail in +`GitHub issue 19296 `_, +arbitrary padding at the end of a FITS file might cause the parser +to inefficiently search for the END card of the next header. +Therefore, we recommend that astropy users to not blindly open +untrusted large FITS files without independently verifying their fidelity +first. + +.. TODO: determine whether the following is quantitatively true, and either +.. uncomment or remove. + +.. Turn off memmap to run faster but use more memory +.. ------------------------------------------------- +.. +.. By default, :func:`astropy.io.fits.open` will open files using memory-mapping, +.. which means that the data is not necessarily read into memory until it is +.. needed. While memory-efficient, if memory is not a concern for you, you may +.. find that you can get better performance by turning memory mapping off, which +.. forces the data to be read into memory immediately: +.. +.. .. doctest-skip:: +.. +.. >>> fits.open('example.fits', memmap=False) diff --git a/docs/io/fits/usage/cloud.rst b/docs/io/fits/usage/cloud.rst new file mode 100644 index 000000000000..92ac44b87eb7 --- /dev/null +++ b/docs/io/fits/usage/cloud.rst @@ -0,0 +1,265 @@ +.. currentmodule:: astropy.io.fits + +.. _fits_io_cloud: + +Obtaining subsets from cloud-hosted FITS files +********************************************** + +Astropy offers support for extracting data from FITS files stored in the cloud. +Specifically, the `astropy.io.fits.open` function accepts the ``use_fsspec`` +and ``fsspec_kwargs`` parameters, which allow remote files to be accessed in an +efficient way using the |fsspec| package. + +``fsspec`` is an optional dependency of Astropy which supports reading +files from a range of remote and distributed storage backends, such as Amazon +and Google Cloud Storage. This chapter explains its use. + +.. note:: + + The examples in this chapter require ``fsspec`` which is an optional + dependency of Astropy. See :ref:`installing-astropy` for details on + installing optional dependencies. + + +Subsetting FITS files hosted on an HTTP web server +================================================== + +A common use case for ``fsspec`` is to read subsets of FITS data from a web +server which supports serving partial files via the +`Range Requests `__ feature of the +HTTP protocol. Most web servers support serving portions of files in this way. + +For example, let's assume you want to retrieve data from a large image obtained +by the Hubble Space Telescope available at the following url:: + + >>> # Download link for a large Hubble archive image (213 MB) + >>> url = "https://mast.stsci.edu/api/v0.1/Download/file/?uri=mast:HST/product/j8pu0y010_drc.fits" + +This file can be opened by passing the url to `astropy.io.fits.open`. +By default, Astropy will download the entire file to local disc before opening +it. This works fine for small files but tends to require a lot of time and +memory for large files. + +You can improve the performance for large files by passing the parameter +``use_fsspec=True`` to `open`. This will make Astropy use ``fsspec`` +to download only the necessary parts of the FITS file. +For example: + +.. doctest-requires:: fsspec + + >>> from astropy.io import fits + ... + >>> # `fits.open` will download the primary header + >>> with fits.open(url, use_fsspec=True) as hdul: # doctest: +REMOTE_DATA + ... + ... # Download a single header + ... header = hdul[1].header + ... + ... # Download a single data array + ... image = hdul[1].data + ... + ... # Download a 10-by-20 pixel cutout by using .section + ... cutout = hdul[2].section[10:20, 30:50] + +The example above requires less time and memory than would be required to +download the entire file. This is because ``fsspec`` is able to leverage +two *lazy data loading* features available in Astropy: + +1. The ``lazy_load_hdus`` parameter offered by `open` takes care of loading HDU + header and data attributes on demand rather than reading all HDUs at once. + This parameter is set to ``True`` by default. You do not need to pass it + explicitly, unless you changed its default value in the + :ref:`astropy:astropy_config`. +2. The `ImageHDU.section` and `CompImageHDU.section` properties enables a + subset of a data array to be read into memory without downloading the entire + image or cube. See the :ref:`astropy:data-sections` part of the documentation + for more details. + +Additional tips for achieving good performance when working with remote files +are provided in the :ref:`astropy:optimizing_fsspec` section further down +this page. + +.. note:: + + The `ImageHDU.section` and `CompImageHDU.section` feature is only efficient + for files that are not externally compressed (such as ``.fits.gz`` files). + Files that are compressed using internal tile compression should work properly. + Use ``.section`` on an externally compressed image will cause the whole FITS + file to be downloaded. + + +Subsetting FITS files hosted in Amazon S3 cloud storage +======================================================= + +The FITS file used in the example above also happens to be available via +Amazon cloud storage, where it is stored in a `public S3 bucket +`__ at the following location:: + + >>> s3_uri = "s3://stpubdata/hst/public/j8pu/j8pu0y010/j8pu0y010_drc.fits" + +With ``use_fsspec`` enabled, you can obtain a small cutout from a file stored +in Amazon S3 cloud storage in the same way as above. When opening paths with +prefix ``s3://`` (Amazon S3 Storage) or ``gs://`` (Google Cloud Storage), +`open` will automatically default to ``use_fsspec=True`` for convenience. +For example: + +.. doctest-requires:: fsspec + + >>> # Download a small 10-by-20 pixel cutout from a FITS file stored in Amazon S3 + >>> with fits.open(s3_uri, fsspec_kwargs={"anon": True}) as hdul: # doctest: +REMOTE_DATA + ... cutout = hdul[1].section[10:20, 30:50] + +Obtaining cutouts from Amazon S3 in this way may be particularly performant if +your code is running on a server in the same Amazon cloud region as the data. + +.. note:: + + To open paths with prefix ``s3://``, fsspec requires an optional dependency + called |s3fs|. A ``ModuleNotFoundError`` will be raised if this dependency + is missing. See :ref:`installing-astropy` for details on installing optional + dependencies. + + +Working with Amazon S3 access credentials +----------------------------------------- + +In the example above, we passed ``fsspec_kwargs={"anon": True}`` to enable the +data to be retrieved in an anonymous way without providing Amazon cloud access +credentials. This is possible because the data is located in a public S3 +bucket which has been configured to allow anonymous access. + +In some cases you may want to access data stored in an Amazon S3 data bucket +that is private or uses the "Requester Pays" feature. You will have to provide +a secret access key in this case to avoid encountering a ``NoCredentialsError``. +You can use the ``fsspec_kwargs`` parameter to pass extra arguments, such as +access keys, to the `fsspec.open` function as follows: + +.. doctest-skip:: + + >>> fsspec_kwargs = {"key": "YOUR-SECRET-KEY-ID", + ... "secret": "YOUR-SECRET-KEY"} + >>> with fits.open(s3_uri, fsspec_kwargs=fsspec_kwargs) as hdul: + ... cutout = hdul[2].section[10:20, 30:50] + +.. warning:: + + Including secret access keys inside Python code is dangerous because you + may accidentally end up revealing your keys when you share your code with + others. A better practice is to store your access keys via a configuration + file or environment variables. See the |s3fs| documentation for guidance. + + +Using :class:`~astropy.nddata.Cutout2D` with cloud-hosted FITS files +==================================================================== + +The examples above used the `ImageHDU.section` feature to download +small cutouts given a set of pixel coordinates. For astronomical images it is +often more convenient to obtain cutouts based on a sky position and angular +size rather than array coordinates. For this reason, Astropy provides the +`astropy.nddata.Cutout2D` tool which makes it easy to obtain cutouts informed +by an image's World Coordinate System (`~astropy.wcs.WCS`). + +This cutout tool can be used in combination with ``fsspec`` and ``.section``. +For example, assume you happen to know that the image we opened above contains +a nice edge-on galaxy at the following position:: + + >>> # Approximate location of an edge-on galaxy + >>> from astropy.coordinates import SkyCoord + >>> position = SkyCoord('10h01m41.13s 02d25m20.58s') + +We also know that the radius of the galaxy is approximately 5 arcseconds:: + + >>> # Approximate size of the galaxy + >>> from astropy import units as u + >>> size = 5*u.arcsec + +Given this sky position and radius, we can use `~astropy.nddata.Cutout2D` +in combination with ``use_fsspec=True`` and ``.section`` as follows: + +.. doctest-requires:: fsspec + + >>> from astropy.nddata import Cutout2D + >>> from astropy.wcs import WCS + ... + >>> with fits.open(s3_uri, use_fsspec=True, fsspec_kwargs={"anon": True}) as hdul: # doctest: +REMOTE_DATA + ... wcs = WCS(hdul[1].header) + ... cutout = Cutout2D(hdul[1].section, # use `.section` rather than `.data`! + ... position=position, + ... size=size, + ... wcs=wcs) + +See :ref:`cutout_images` for more details on this feature. + + +.. _optimizing_fsspec: + +Performance improvement tips for subsetting remote FITS files +============================================================= + +In the examples above we explained that it is important to use the +``use_fsspec=True`` feature in combination with the ``lazy_load_hdus=True`` +parameter and the ``ImageHDU.section`` feature to obtain good performance. + +There are two additional factors which significantly impact the performance +you will encounter, namely: (i) the structure of the FITS file, and (ii) the caching +and block size configuration of ``fsspec``. The remainder of this section +briefly explains these two factors. + +Matching the FITS file structure to the data slicing patterns +------------------------------------------------------------- + +The order in which multi-dimensional data is organized inside FITS files plays +a major role in the subsetting performance. + +Astropy uses the row-major order for indexing FITS data. This means that the +right-most axis is the one that varies the fastest inside the file. +Put differently, the data for the right-most dimension tends to be located in +contiguous regions of the file and is therefore the easiest to extract. + +For example, in the case of a 2D image, the slice ``.section[0, :]`` can be +obtained by downloading one contiguous region of bytes from the file. +In contrast, the slice ``.section[:, 0]`` requires accessing bytes spread +across the entire image array. The same is true for higher dimensions, +for example, obtaining the slice ``.section[0, :, :]`` from a 3D cube +will tend to be much faster than requesting ``.section[:, :, 0]``. + +Obtaining slices of data that are well matched to the internal layout of +the FITS file generally yields the best performance. +If subsetting performance is important to you, you may have to consider +modifying your FITS files to ensure that the ordering of the dimensions +is well-matched to your data slicing patterns. + +Configuring the ``fsspec`` block size and download strategy +----------------------------------------------------------- + +The ``fsspec`` package supports different data reading and caching strategies +which aim to find a balance between the number of network requests on one hand +and the total amount of data transferred on the other hand. By default, +``fsspec`` will attempt to download data in large contiguous blocks using a +buffered *read ahead* strategy, similar to the strategy that is employed +when operating systems load local files into memory. + +You can tune the performance of ``fsspec``'s buffering strategy by passing custom +``block_size`` and ``cache_type`` parameters to `fsspec.filesystem`, and passing +the filesystem into the ``fsspec_filesystem`` keyword argument in `astropy.io.fits.open`. +For example, we can configure fsspec to make buffered reads with a minimum +``block_size`` of 1 MB as follows: + +.. doctest-requires:: fsspec + + >>> import fsspec + >>> fsspec_fs = fsspec.filesystem( + ... protocol="s3", + ... anon=True, + ... block_size=1_000_000, + ... cache_type="bytes" + ... ) + >>> with fits.open( + ... s3_uri, fsspec_filesystem=fsspec_fs, fsspec_kwargs={"anon": True} + ... ) as hdul: # doctest: +REMOTE_DATA + ... cutout = hdul[1].section[10:20, 30:50] + +The ideal configuration will depend on the latency and throughput of the +network, as well as the exact shape and volume of the data you seek to obtain. + +See the |fsspec| documentation for more information on its options. diff --git a/docs/io/fits/usage/examples.rst b/docs/io/fits/usage/examples.rst deleted file mode 100644 index ce3c82a10795..000000000000 --- a/docs/io/fits/usage/examples.rst +++ /dev/null @@ -1,72 +0,0 @@ -Examples --------- - -Converting a 3-color image (JPG) to separate FITS images -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. figure:: ../images/Hs-2009-14-a-web.jpg - :scale: 100 % - :align: center - :alt: Starting image - -.. container:: figures - - .. figure:: ../images/Red.jpg - :target: ../images/Red.jpg - :scale: 50 - :alt: Red color information - - Red color information - - .. figure:: ../images/Green.jpg - :target: ../images/Green.jpg - :scale: 50 - :alt: Green color information - - Green color information - - .. figure:: ../images/Blue.jpg - :target: ../images/Blue.jpg - :scale: 50 - :alt: Blue color information - - Blue color information - -:: - - #!/usr/bin/env python - import numpy - import Image - - from astropy.io import fits - - #get the image and color information - image = Image.open('hs-2009-14-a-web.jpg') - #image.show() - xsize, ysize = image.size - r, g, b = image.split() - rdata = r.getdata() # data is now an array of length ysize\*xsize - gdata = g.getdata() - bdata = b.getdata() - - # create numpy arrays - npr = numpy.reshape(rdata, (ysize, xsize)) - npg = numpy.reshape(gdata, (ysize, xsize)) - npb = numpy.reshape(bdata, (ysize, xsize)) - - # write out the fits images, the data numbers are still JUST the RGB - # scalings; don't use for science - red = fits.PrimaryHDU(data=npr) - red.header['LATOBS'] = "32:11:56" # add spurious header info - red.header['LONGOBS'] = "110:56" - red.writeto('red.fits') - - green = fits.PrimaryHDU(data=npg) - green.header['LATOBS'] = "32:11:56" - green.header['LONGOBS'] = "110:56" - green.writeto('green.fits') - - blue = fits.PrimaryHDU(data=npb) - blue.header['LATOBS'] = "32:11:56" - blue.header['LONGOBS'] = "110:56" - blue.writeto('blue.fits') diff --git a/docs/io/fits/usage/headers.rst b/docs/io/fits/usage/headers.rst index c58d35c070aa..8616c31d2b8e 100644 --- a/docs/io/fits/usage/headers.rst +++ b/docs/io/fits/usage/headers.rst @@ -1,82 +1,90 @@ .. currentmodule:: astropy.io.fits FITS Headers ------------- +************ -In the next three chapters, more detailed information as well as examples will -be explained for manipulating the header, the image data, and the table data +In the next three chapters, more detailed information including examples will +be explained for manipulating FITS headers, image/array data, and table data respectively. Header of an HDU -^^^^^^^^^^^^^^^^ +================ -Every HDU normally has two components: header and data. In Astropy these two -components are accessed through the two attributes of the HDU, -:attr:`~_BaseHDU.header` and :attr:`~_BaseHDU.data`. +Every Header Data Unit (HDU) normally has two components: header and data. In +``astropy`` these two components are accessed through the two attributes of the +HDU, ``hdu.header`` and ``hdu.data``. -While an HDU may have empty data, i.e. the .data attribute is None, any HDU -will always have a header. When an HDU is created with a constructor, e.g. -``hdu = PrimaryHDU(data, header)``, the user may supply the header value from -an existing HDU's header and the data value from a numpy array. If the -defaults (``None``) are used, the new HDU will have the minimal required -keywords for an HDU of that type: +While an HDU may have empty data (i.e., the ``.data`` attribute is `None`), any +HDU will always have a header. When an HDU is created with a constructor (e.g., +``hdu = PrimaryHDU(data, header)``), the user may supply the header value from +an existing HDU's header and the data value from a ``numpy`` array. If the +defaults (None) are used, the new HDU will have the minimal required keywords +for an HDU of that type:: + >>> from astropy.io import fits >>> hdu = fits.PrimaryHDU() - >>> hdu.header # show the all of the header cards - SIMPLE = T / conforms to FITS standard - BITPIX = 8 / array data type - NAXIS = 0 / number of array dimensions - EXTEND = T + >>> hdu.header # show the all of the header cards + SIMPLE = T / conforms to FITS standard + BITPIX = 8 / array data type + NAXIS = 0 / number of array dimensions + EXTEND = T -A user can use any header and any data to construct a new HDU. Astropy will -strip the required keywords from the input header first and then add back the -required keywords compatible to the new HDU. So, a user can use a table HDU's -header to construct an image HDU and vice versa. The constructor will also -ensure the data type and dimension information in the header agree with the -data. +A user can use any header and any data to construct a new HDU. ``astropy`` will +strip any keywords that describe the data structure leaving only your +informational keywords. Later it will add back in the required structural +keywords for compatibility with the new HDU and any data added to it. So, a +user can use a table HDU's header to construct an image HDU and vice versa. The +constructor will also ensure the data type and dimension information in the +header agree with the data. The Header Attribute -^^^^^^^^^^^^^^^^^^^^ +==================== Value Access, Updating, and Creating -"""""""""""""""""""""""""""""""""""" - -As shown in the Quick Tutorial, keyword values can be accessed via keyword name -or index of an HDU's header attribute. Here is a quick summary: - - >>> hdulist = fits.open('input.fits') # open a FITS file - >>> prihdr = hdulist[0].header # the primary HDU header - >>> print prihdr[3] # get the 4th keyword's value - 10 - >>> prihdr[3] = 20 # change its value - >>> prihdr['DARKCORR'] # get the value of the keyword 'darkcorr' +------------------------------------ + +As shown in the :ref:`Getting Started ` tutorial, keyword values can +be accessed via keyword name or index of an HDU's header attribute. You can +also use the wildcard character ``*`` to get the keyword value pairs that match +your search string. Here is a quick summary:: + + >>> fits_image_filename = fits.util.get_testdata_filepath('test0.fits') + >>> hdul = fits.open(fits_image_filename) # open a FITS file + >>> hdr = hdul[0].header # the primary HDU header + >>> print(hdr[34]) # get the 2nd keyword's value + 96 + >>> hdr[34] = 20 # change its value + >>> hdr['DARKCORR'] # get the value of the keyword 'darkcorr' 'OMIT' - >>> prihdr['darkcorr'] = 'PERFORM' # change darkcorr's value + >>> hdr['DARKCOR*'] # get keyword values using wildcard matching + DARKCORR= 'OMIT ' / Do dark correction: PERFORM, OMIT, COMPLETE + >>> hdr['darkcorr'] = 'PERFORM' # change darkcorr's value -Keyword names are case-insenstive except in a few special cases (see the -sections on HIERARCH card and record-valued cards). Thus, ``prihdr['abc']``, -``prihdr['ABC']``, or ``prihdr['aBc']`` are all equivalent. +Keyword names are case-insensitive except in a few special cases (see the +sections on HIERARCH card and record-valued cards). Thus, ``hdr['abc']``, +``hdr['ABC']``, or ``hdr['aBc']`` are all equivalent. -Like with python :class:`dict`\s, new keywords can also be added to the header -using assignment syntax: +As with Python's :class:`dict` type, new keywords can also be added to the +header using assignment syntax:: - >>> 'DARKCORR' in header # Check for existence + >>> hdr = hdul[1].header + >>> 'DARKCORR' in hdr # Check for existence False - >>> header['DARKCORR'] = 'OMIT' # Add a new DARKCORR keyword + >>> hdr['DARKCORR'] = 'OMIT' # Add a new DARKCORR keyword -You can also add a new value *and* comment by assigning them as a tuple: +You can also add a new value *and* comment by assigning them as a tuple:: - >>> header['DARKCORR'] = ('OMIT', 'Dark Image Subtraction') + >>> hdr['DARKCORR'] = ('OMIT', 'Dark Image Subtraction') .. note:: An important point to note when adding new keywords to a header is that by default they are not appended *immediately* to the end of the file. - Rather, they are appended to the last non-commentary keyword. This is in + Rather, they are appended to the last non-commentary keyword. This is in order to support the common use case of always having all HISTORY keywords - grouped together at the end of a header. A new non-commentary keyword will + grouped together at the end of a header. A new non-commentary keyword will be added at the end of the existing keywords, but before any HISTORY/COMMENT keywords at the end of the header. @@ -84,65 +92,66 @@ You can also add a new value *and* comment by assigning them as a tuple: * Use the :meth:`Header.append` method with the ``end=True`` argument: - >>> header.append(('DARKCORR', 'OMIT', 'Dark Image Subtraction'), - end=True) + >>> hdr.append(('DARKCORR', 'OMIT', 'Dark Image Subtraction'), end=True) This forces the new keyword to be added at the actual end of the header. * The :meth:`Header.insert` method will always insert a new keyword exactly where you ask for it: - >>> header.insert(20, ('DARKCORR', 'OMIT', 'Dark Image Subtraction')) + >>> del hdr['DARKCORR'] # Delete previous insertion for doctest + >>> hdr.insert(20, ('DARKCORR', 'OMIT', 'Dark Image Subtraction')) - This inserts the DARKCORR keyword before the 20th keyword in the header - no matter what it is. + This inserts the DARKCORR keyword before the 20th keyword in the + header no matter what it is. A keyword (and its corresponding card) can be deleted using the same index/name -syntax: +syntax:: - >>> del prihdr[3] # delete the 2nd keyword - >>> del prihdr['abc'] # get the value of the keyword 'abc' + >>> del hdr[3] # delete the 2nd keyword + >>> del hdr['DARKCORR'] # delete the value of the keyword 'DARKCORR' Note that, like a regular Python list, the indexing updates after each delete, -so if ``del prihdr[3]`` is done two times in a row, the 4th and 5th keywords -are removed from the original header. Likewise, ``del prihdr[-1]`` will delete +so if ``del hdr[3]`` is done two times in a row, the fourth and fifth keywords +are removed from the original header. Likewise, ``del hdr[-1]`` will delete the last card in the header. -It is also possible to delete an entire range of cards using the slice syntax: +It is also possible to delete an entire range of cards using the slice syntax:: - >>> del prihdr[3:5] + >>> del hdr[3:5] -The method :meth:`Header.set` is another way to update they value or comment -associated with an existing keyword, or to create a new keyword. Most of its -functionality can be duplicated with the dict-like syntax shown above. But in -some cases it might be more clear. It also has the advantage of allowing one -to either move cards within the header, or specify the location of a new card -relative to existing cards: +The method :meth:`Header.set` is another way to update the value or comment +associated with an existing keyword, or to create a new keyword. Most of its +functionality can be duplicated with the dict-like syntax shown above. But in +some cases it might be more clear. It also has the advantage of allowing a user +to either move cards within the header or specify the location of a new card +relative to existing cards:: - >>> prihdr.set('target', 'NGC1234', 'target name') - >>> # place the next new keyword before the 'target' keyword - >>> prihdr.set('newkey', 666, before='target') # comment is optional + >>> hdr.set('target', 'NGC1234', 'target name') + >>> # place the next new keyword before the 'TARGET' keyword + >>> hdr.set('newkey', 666, before='TARGET') # comment is optional >>> # place the next new keyword after the 21st keyword - >>> prihdr.set('newkey2', 42.0, 'another new key', after=20) + >>> hdr.set('newkey2', 42.0, 'another new key', after=20) In FITS headers, each keyword may also have a comment associated with it -explaining its purpose. The comments associated with each keyword are accessed -through the :attr:`~Header.comments` attribute: +explaining its purpose. The comments associated with each keyword are accessed +through the :attr:`~Header.comments` attribute:: - >>> header['NAXIS'] + >>> hdr['NAXIS'] 2 - >>> header.comments['NAXIS'] - the number of image axes - >>> header.comments['NAXIS'] = 'The number of image axes' # Update - -Comments can be accessed in all the same ways that values are accessed, whether -by keyword name or card index. Slices are also possible. The only difference -is that you go through ``header.comments`` instead of just ``header`` by + >>> hdr.comments['NAXIS'] + 'number of data axes' + >>> hdr.comments['NAXIS'] = 'The number of image axes' # Update + >>> hdul.close() # close the HDUList again + +Comments can be accessed in all of the same ways that values are accessed, +whether by keyword name or card index. Slices are also possible. The only +difference is that you go through ``hdr.comments`` instead of just ``hdr`` by itself. COMMENT, HISTORY, and Blank Keywords -"""""""""""""""""""""""""""""""""""" +------------------------------------ Most keywords in a FITS header have unique names. If there are more than two cards sharing the same name, it is the first one accessed when referred by @@ -151,22 +160,34 @@ name. The duplicates can only be accessed by numeric indexing. There are three special keywords (their associated cards are sometimes referred to as commentary cards), which commonly appear in FITS headers more than once. They are (1) blank keyword, (2) HISTORY, and (3) COMMENT. Unlike other -keywords, when accessing these keywords they are returned as a list: +keywords, when accessing these keywords they are returned as a list:: - >>> prihdr['HISTORY'] + >>> filename = fits.util.get_testdata_filepath('history_header.fits') + >>> with fits.open(filename) as hdul: # open a FITS file + ... hdr = hdul[0].header + + >>> hdr['HISTORY'] I updated this file on 02/03/2011 I updated this file on 02/04/2011 - .... -These lists can be sliced like any other list. For example, to diplay just the -last HISTORY entry, use ``prihdr['history'][-1]``. Existing commentary cards +These lists can be sliced like any other list. For example, to display just the +last HISTORY entry, use ``hdr['history'][-1]``. Existing commentary cards can also be updated by using the appropriate index number for that card. New commentary cards can be added like any other card by using the dict-like -keyword assignment syntax, or by using the :meth:`Header.set` method. However, +keyword assignment syntax, or by using the :meth:`Header.set` method. However, unlike with other keywords, a new commentary card is always added and appended to the last commentary card with the same keyword, rather than to the end of -the header. Here is an example: +the header. + +Example +^^^^^^^ + +.. + EXAMPLE START + Manipulating FITS Headers in astropy.io.fits + +To add a new commentary card:: >>> hdu.header['HISTORY'] = 'history 1' >>> hdu.header[''] = 'blank 1' @@ -195,178 +216,231 @@ commentary card by using the :meth:`Header.insert` method. Ironically, there is no comment in a commentary card, only a string value. +.. + EXAMPLE END + +Undefined Values +---------------- + +FITS headers can have undefined values and these are represented in Python +with the special value `None`. `None` can be used when assigning values +to a `~astropy.io.fits.Header` or `~astropy.io.fits.Card`. + + >>> hdr = fits.Header() + >>> hdr['UNDEF'] = None + >>> hdr['UNDEF'] is None + True + >>> repr(hdr) + 'UNDEF = ' + >>> hdr.append('UNDEF2') + >>> hdr['UNDEF2'] is None + True + >>> hdr.append(('UNDEF3', None, 'Undefined value')) + >>> str(hdr.cards[-1]) + 'UNDEF3 = / Undefined value ' + Card Images -^^^^^^^^^^^ +=========== A FITS header consists of card images. A card image in a FITS header consists of a keyword name, a value, and -optionally a comment. Physically, it takes 80 columns (bytes)--without carriage -return--in a FITS file's storage format. In Astropy, each card image is +optionally a comment. Physically, it takes 80 columns (bytes) — without carriage +return — in a FITS file's storage format. In ``astropy``, each card image is manifested by a :class:`Card` object. There are also special kinds of cards: commentary cards (see above) and card images taking more than one 80-column -card image. The latter will be discussed later. +card image. The latter will be discussed later. Most of the time the details of dealing with cards are handled by the :class:`Header` object, and it is not necessary to directly manipulate cards. -In fact, most :class:`Header` methods that accept a (keyword, value) or -(keyword, value, comment) tuple as an argument can also take a :class:`Card` -object as an argument. :class:`Card` objects are just wrappers around that -header that provide the logic for parsing and formatting individual cards in a -header. But there's nothing gained by manually using a :class:`Card` object, -except to examine how a card might appear in a header before actually adding it -to the header. +In fact, most :class:`Header` methods that accept a ``(keyword, value)`` or +``(keyword, value, comment)`` tuple as an argument can also take a +:class:`Card` object as an argument. :class:`Card` objects are just wrappers +around such tuples that provide the logic for parsing and formatting individual +cards in a header. There is usually nothing gained by manually using a +:class:`Card` object, except to examine how a card might appear in a header +before actually adding it to the header. A new Card object is created with the :class:`Card` constructor: -``Card(key, value, comment)``. For example: +``Card(key, value, comment)``. + +Example +------- + +.. + EXAMPLE START + Card Images in FITS Headers in astropy.io.fits + +To create a new Card object:: >>> c1 = fits.Card('TEMP', 80.0, 'temperature, floating value') - >>> c2 = fits.Card('DETECTOR', 1) # comment is optional + >>> c2 = fits.Card('DETECTOR', 1) # comment is optional >>> c3 = fits.Card('MIR_REVR', True, - ... 'mirror reversed? Boolean value) + ... 'mirror reversed? Boolean value') >>> c4 = fits.Card('ABC', 2+3j, 'complex value') >>> c5 = fits.Card('OBSERVER', 'Hubble', 'string value') - >>> print c1; print c2; print c3; print c4; print c5 # show the card images - TEMP = 80.0 / temperature, floating value - DETECTOR= 1 / - MIR_REVR= T / mirror reversed? Boolean value - ABC = (2.0, 3.0) / complex value - OBSERVER= 'Hubble ' / string value + >>> print(c1); print(c2); print(c3); print(c4); print(c5) # show the cards + TEMP = 80.0 / temperature, floating value + DETECTOR= 1 + MIR_REVR= T / mirror reversed? Boolean value + ABC = (2.0, 3.0) / complex value + OBSERVER= 'Hubble ' / string value Cards have the attributes ``.keyword``, ``.value``, and ``.comment``. Both ``.value`` and ``.comment`` can be changed but not the ``.keyword`` attribute. +In other words, once a card is created, it is created for a specific, immutable +keyword. The :meth:`Card` constructor will check if the arguments given are conforming to the FITS standard and has a fixed card image format. If the user wants to create a card with a customized format or even a card which is not conforming -to the FITS standard (e.g. for testing purposes), the :meth:`Card.fromstring` +to the FITS standard (e.g., for testing purposes), the :meth:`Card.fromstring` class method can be used. -Cards can be verified with :meth:`Card.verify`. The non-standard card ``c2`` in +Cards can be verified with :meth:`Card.verify`. The nonstandard card ``c2`` in the example below is flagged by such verification. More about verification in -Astropy will be discussed in a later chapter. +``astropy`` will be discussed in a later chapter. + +:: - >>> c1 = fits.Card.fromstring('ABC = 3.456D023') + >>> c1 = fits.Card.fromstring('ABC = 3.456D023') >>> c2 = fits.Card.fromstring("P.I. ='Hubble'") - >>> print c1; print c2 - ABC = 3.456D023 + >>> print(c1) + ABC = 3.456D023 + >>> print(c2) # doctest: +SKIP P.I. ='Hubble' - >>> c2.verify() + >>> c2.verify() # doctest: +SKIP Output verification result: Unfixable error: Illegal keyword name 'P.I.' A list of the :class:`Card` objects underlying a :class:`Header` object can be -accessed with the :attr:`Header.cards` attribute. This list is only meant for -observing, and should not be directly manipulated. In fact, it is only a -copy--modifications to it will not affect the header it came from. Use the -methods provided by the :class:`Header` class instead. +accessed with the :attr:`Header.cards` attribute. This list is only meant for +observing, and should not be directly manipulated. In fact, it is only a +copy — modifications to it will not affect the header from which it came. Use +the methods provided by the :class:`Header` class instead. + +.. + EXAMPLE END CONTINUE Cards -^^^^^^^^^^^^^^ - -The fact that the FITS standard only allows up to 8 characters for the keyword -name and 80 characters to contain the keyword, the value, and the comment is -restrictive for certain applications. To allow long string values for keywords, -a proposal was made in: - - http://legacy.gsfc.nasa.gov/docs/heasarc/ofwg/docs/ofwg_recomm/r13.html - -by using the CONTINUE keyword after the regular 80-column containing the -keyword. Astropy does support this convention, even though it is not a FITS -standard. The examples below show the use of CONTINUE is automatic for long -string values. - - >>> header = fits.Header() - >>> header['abc'] = 'abcdefg' * 20 - >>> header - ABC = 'abcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcd&' - CONTINUE 'efgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefga&' - CONTINUE 'bcdefg&' - >>> header['abc'] +============== + +The fact that the FITS standard only allows up to eight characters for the +keyword name and 80 characters to contain the keyword, the value, and the +comment is restrictive for certain applications. To allow long string values +for keywords, a proposal was made in: + + https://heasarc.gsfc.nasa.gov/docs/heasarc/ofwg/docs/ofwg_recomm/r13.html + +by using the CONTINUE keyword after the regular 80 column containing the +keyword. ``astropy`` does support this convention, which is a part of the FITS +standard since version 4.0. + +Examples +-------- + +.. + EXAMPLE START + CONTINUE Cards for Long String Values in astropy.io.fits + +The examples below show that the use of CONTINUE is automatic for long +string values:: + + >>> hdr = fits.Header() + >>> hdr['abc'] = 'abcdefg' * 20 + >>> hdr + ABC = 'abcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcd&' + CONTINUE 'efgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefga&' + CONTINUE 'bcdefg' + >>> hdr['abc'] 'abcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefg' >>> # both value and comments are long - >>> header['abc'] = ('abcdefg' * 10, 'abcdefg' * 10) - >>> header - ABC = 'abcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcd&' - CONTINUE 'efg&' - CONTINUE '&' / abcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefga - CONTINUE '&' / bcdefg - -Note that when a CONTINUE card is used, at the end of each 80-characters card + >>> hdr['abc'] = ('abcdefg' * 10, 'abcdefg' * 10) + >>> hdr + ABC = 'abcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcd&' + CONTINUE 'efg&' + CONTINUE '&' / abcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefga + CONTINUE '' / bcdefg + +Note that when a CONTINUE card is used, at the end of each 80-character card image, an ampersand is present. The ampersand is not part of the string value. -Also, there is no "=" at the 9th column after CONTINUE. In the first example, -the entire 240 characters is treated by Astropy as a single card. So, if it is -the nth card in a header, the (n+1)th card refers to the next keyword, not the -next CONTINUE card. As such, CONTINUE cards are transparently handled by -Astropy as a single logical card, and it's generally not necessary to worry -about the details of the format. Keywords that resolve to a set of CONTINUE +Also, there is no "=" at the ninth column after CONTINUE. In the first example, +the entire 240 characters is treated by ``astropy`` as a single card. So, if it +is the nth card in a header, the (n+1)th card refers to the next keyword, not +the next CONTINUE card. As such, CONTINUE cards are transparently handled by +``astropy`` as a single logical card, and it is generally not necessary to worry +about the details of the format. Keywords that resolve to a set of CONTINUE cards can be accessed and updated just like regular keywords. +.. + EXAMPLE END + HIERARCH Cards -^^^^^^^^^^^^^^ - -For keywords longer than 8 characters, there is a convention originated at ESO -to facilitate such use. It uses a special keyword HIERARCH with the actual long -keyword following. Astropy supports this convention as well. - -If a keyword contains more than 8 characters Astropy will automatically use a -HIERARCH card, but will also issue a warning in case this is in error. -However, one may explicitly request a HIERARCH card by prepending the keyword -with 'HIERARCH ' (just as it would appear in the header). For example, -``header['HIERARCH abcdefghi']`` will create the keyword ``abcdefghi`` without -displaying a warning. Once created, HIERARCH keywords can be accessed like any -other: ``header['abcdefghi']``, without prepending 'HIERARCH' to the keyword. -HIEARARCH keywords also differ from normal FITS keywords in that they are -case-sensitive. - -Examples follow: - - >>> c = fits.Card('abcdefghi', 10) - Keyword name 'abcdefghi' is greater than 8 characters; a HIERARCH card will - be created. - >>> print c +============== + +For keywords longer than eight characters, there is a convention originated at +the European Southern Observatory (ESO) to facilitate such use. It uses a +special keyword HIERARCH with the actual long keyword following. ``astropy`` +supports this convention as well. + +If a keyword contains more than eight characters ``astropy`` will automatically +use a HIERARCH card, but will also issue a warning in case this is in error. +However, you may explicitly request a HIERARCH card by prepending the keyword +with 'HIERARCH ' (just as it would appear in the header). For example, +``hdr['HIERARCH abcdefghi']`` will create the keyword ``abcdefghi`` without +displaying a warning. Once created, HIERARCH keywords can be accessed like any +other: ``hdr['abcdefghi']``, without prepending 'HIERARCH' to the keyword. + +Examples +-------- + +.. + EXAMPLE START + HIERARCH Cards for Keywords Longer than Eight Characters in astropy.io.fits + +``astropy`` will use a HIERARCH card and issue a warning when keywords contain +more than eight characters:: + + >>> # this will result in a Warning because a HIERARCH card is implicitly created + >>> c = fits.Card('abcdefghi', 10) # doctest: +SKIP + >>> print(c) # doctest: +SKIP HIERARCH abcdefghi = 10 >>> c = fits.Card('hierarch abcdefghi', 10) - >>> print c + >>> print(c) HIERARCH abcdefghi = 10 - >>> h = fits.PrimaryHDU() - >>> h.header['hierarch abcdefghi'] = 99 - >>> h.header['abcdefghi'] + >>> hdu = fits.PrimaryHDU() + >>> hdu.header['hierarch abcdefghi'] = 99 + >>> hdu.header['abcdefghi'] 99 - >>> h.header['abcdefghi'] = 10 - >>> h.header['abcdefghi'] + >>> hdu.header['abcdefghi'] = 10 + >>> hdu.header['abcdefghi'] 10 - >>> h.header['ABCDEFGHI'] - Traceback (most recent call last): - File "", line 1, in - File "astropy/io/fits/header.py", line 121, in __getitem__ - return self._cards[self._cardindex(key)].value - File "astropy/io/fits/header.py", line 1106, in _cardindex - raise KeyError("Keyword %r not found." % keyword) - KeyError: "Keyword 'ABCDEFGI.' not found." - >>> h.header - SIMPLE = T / conforms to FITS standard - BITPIX = 8 / array data type - NAXIS = 0 / number of array dimensions - EXTEND = T - HIERARCH abcdefghi = 1000 + >>> hdu.header + SIMPLE = T / conforms to FITS standard + BITPIX = 8 / array data type + NAXIS = 0 / number of array dimensions + EXTEND = T + HIERARCH abcdefghi = 10 + +.. + EXAMPLE END .. note:: A final point to keep in mind about the :class:`Header` class is that much of its design is intended to abstract away quirks about the FITS format. - This is why, for example, it will automatically created CONTINUE and - HIEARARCH cards. The Header is just a data structure, and as user you - shouldn't have to worry about how it ultimately gets serialized to a header + This is why, for example, it will automatically create CONTINUE and + HIERARCH cards. The Header is just a data structure, and as a user you + should not have to worry about how it ultimately gets serialized to a header in a FITS file. - Though there are some areas where it's almost impossible to hide away the - quirks of the FITS format, Astropy tries to make it so that you have to - think about it as little as possible. If there are any areas where you - have concern yourself unncessarily about how the header is constructed, - then let help@stsci.edu know, as there are probably areas where this can be + Though there are some areas where it is almost impossible to hide away the + quirks of the FITS format, ``astropy`` tries to make it so that you have to + think about it as little as possible. If there are any areas that are left + vague or difficult to understand about how the header is constructed, please + let us know, as there are probably areas where this can be improved on even more. diff --git a/docs/io/fits/usage/image.rst b/docs/io/fits/usage/image.rst index 4a0534255761..c3bec5a536da 100644 --- a/docs/io/fits/usage/image.rst +++ b/docs/io/fits/usage/image.rst @@ -1,20 +1,20 @@ .. currentmodule:: astropy.io.fits Image Data ----------- +********** -In this chapter, we'll discuss the data component in an image HDU. +In this chapter, we will discuss the data component in an image HDU. Image Data as an Array -^^^^^^^^^^^^^^^^^^^^^^ +====================== A FITS primary HDU or an image extension HDU may contain image data. The -following discussions apply to both of these HDU classes. In Astropy, for most -cases, it is just a simple numpy array, having the shape specified by the NAXIS -keywords and the data type specified by the BITPIX keyword - unless the data is -scaled, see next section. Here is a quick cross reference between allowed -BITPIX values in FITS images and the numpy data types: +following discussions apply to both of these HDU classes. For most cases in +``astropy``, it is a ``numpy`` array, having the shape specified by the NAXIS +keywords and the data type specified by the BITPIX keyword — unless the data is +scaled, in which case see the next section. Here is a quick cross reference +between allowed BITPIX values in FITS images and the ``numpy`` data types: .. parsed-literal:: @@ -22,182 +22,273 @@ BITPIX values in FITS images and the numpy data types: 8 numpy.uint8 (note it is UNsigned integer) 16 numpy.int16 32 numpy.int32 + 64 numpy.int64 -32 numpy.float32 -64 numpy.float64 -To recap the fact that in numpy the arrays are 0-indexed and the axes are +To recap, in ``numpy`` the arrays are 0-indexed and the axes are ordered from slow to fast. So, if a FITS image has NAXIS1=300 and NAXIS2=400, -the numpy array of its data will have the shape of (400, 300). - -Here is a summary of reading and updating image data values: - - >>> f = fits.open('image.fits') # open a FITS file - >>> scidata = f[1].data # assume the first extension is an image - >>> print scidata[1,4] # get the pixel value at x=5, y=2 - >>> scidata[30:40, 10:20] # get values of the subsection - # from x=11 to 20, y=31 to 40 (inclusive) - >>> scidata[1,4] = 999 # update a pixel value - >>> scidata[30:40, 10:20] = 0 # update values of a subsection - >>> scidata[3] = scidata[2] # copy the 3rd row to the 4th row +the ``numpy`` array of its data will have the shape of (400, 300). + +Examples +-------- + +.. note:: + + The ``astropy.io.fits.util.get_testdata_filepath()`` function, + used in the examples here, returns file path for test data shipped with ``astropy``. + To work with your own data instead, please use :func:`astropy.io.fits.open` or :ref:`io-fits-intro-convenience-functions`, + which take either the relative or absolute path as string or :term:`python:path-like object`. + +.. + EXAMPLE START + Image Data as an Array in astropy.io.fits + +Here is a summary of reading and updating image data values:: + + >>> from astropy.io import fits + >>> fits_image_filename = fits.util.get_testdata_filepath('test0.fits') + + >>> with fits.open(fits_image_filename) as hdul: # open a FITS file + ... data = hdul[1].data # assume the first extension is an image + >>> print(data[1, 4]) # get the pixel value at x=5, y=2 + 313 + >>> # get values of the subsection from x=11 to 20, y=31 to 40 (inclusive) + >>> data[30:40, 10:20] + array([[314, 314, 313, 312, 313, 313, 313, 313, 313, 312], + [314, 314, 312, 313, 313, 311, 313, 312, 312, 314], + [314, 315, 313, 313, 313, 313, 315, 312, 314, 312], + [314, 313, 313, 314, 311, 313, 313, 313, 313, 313], + [313, 314, 312, 314, 312, 314, 314, 315, 313, 313], + [312, 311, 311, 312, 312, 312, 312, 313, 311, 312], + [314, 314, 314, 314, 312, 313, 314, 314, 314, 311], + [314, 313, 312, 313, 313, 314, 312, 312, 311, 314], + [313, 313, 313, 314, 313, 313, 315, 313, 312, 313], + [314, 313, 313, 314, 313, 312, 312, 314, 310, 314]], dtype='>i2') + >>> data[1,4] = 999 # update a pixel value + >>> data[30:40, 10:20] = 0 # update values of a subsection + >>> data[3] = data[2] # copy the 3rd row to the 4th row Here are some more complicated examples by using the concept of the "mask -array". The first example is to change all negative pixel values in scidata to +array." The first example is to change all negative pixel values in ``data`` to zero. The second one is to take logarithm of the pixel values which are -positive: +positive:: - >>> scidata[scidata<0] = 0 - >>> scidata[scidata>0] = numpy.log(scidata[scidata>0]) + >>> data[data < 0] = 0 + >>> import numpy as np + >>> data[data > 0] = np.log(data[data > 0]) -These examples show the concise nature of numpy array operations. +These examples show the concise nature of ``numpy`` array operations. + +.. + EXAMPLE END Scaled Data -^^^^^^^^^^^ +=========== -Sometimes an image is scaled, i.e. the data stored in the file is not the +Sometimes an image is scaled; that is, the data stored in the file is not the image's physical (true) values, but linearly transformed according to the equation: .. parsed-literal:: - physical value = BSCALE\*(storage value) + BZERO + physical value = BSCALE \* (storage value) + BZERO BSCALE and BZERO are stored as keywords of the same names in the header of the -same HDU. The most common use of scaled image is to store unsigned 16-bit -integer data because FITS standard does not allow it. In this case, the stored -data is signed 16-bit integer (BITPIX=16) with BZERO=32768 (2\*\*15), BSCALE=1. +same HDU. The most common use of a scaled image is to store unsigned 16-bit +integer data because the FITS standard does not allow it. In this case, the +stored data is signed 16-bit integer (BITPIX=16) with BZERO=32768 +(:math:`2^{15}`), BSCALE=1. Reading Scaled Image Data -""""""""""""""""""""""""" +------------------------- Images are scaled only when either of the BSCALE/BZERO keywords are present in the header and either of their values is not the default value (BSCALE=1, BZERO=0). -For unscaled data, the data attribute of an HDU in Astropy is a numpy array of -the same data type as specified by the BITPIX keyword. For scaled image, the -.data attribute will be the physical data, i.e. already transformed from the -storage data and may not be the same data type as prescribed in BITPIX. This -means an extra step of copying is needed and thus the corresponding memory -requirement. This also means that the advantage of memory mapping is reduced -for scaled data. +For unscaled data, the data attribute of an HDU in ``astropy`` is a ``numpy`` +array of the same data type specified by the BITPIX keyword. For a scaled +image, the ``.data`` attribute will be the physical data (i.e., already +transformed from the storage data and may not be the same data type as +prescribed in BITPIX). This means an extra step of copying is needed and thus +the corresponding memory requirement. This also means that the advantage of +memory mapping is reduced for scaled data. For floating point storage data, the scaled data will have the same data type. For integer data type, the scaled data will always be single precision floating -point (numpy.float32). Here is an example of what happens to such a file, -before and after the data is touched - - >>> f = fits.open('scaled_uint16.fits') - >>> hdu = f[1] - >>> print hdu.header['bitpix'], hdu.header['bzero'] - 16 32768 - >>> print hdu.data # once data is touched, it is scaled - [ 11. 12. 13. 14. 15.] +point (``numpy.float32``). + +Example +^^^^^^^ + +.. + EXAMPLE START + Reading Scaled Image Data with astropy.io.fits + +Here is an example of what happens to scaled data, before and after the data is +touched:: + + >>> fits_scaledimage_filename = fits.util.get_testdata_filepath('scale.fits') + + >>> hdul = fits.open(fits_scaledimage_filename) + >>> hdu = hdul[0] + >>> hdu.header['bitpix'] + 16 + >>> hdu.header['bzero'] + 1500.0 + >>> hdu.data[0, 0] # once data is touched, it is scaled # doctest: +FLOAT_CMP + np.float32(557.7563) >>> hdu.data.dtype.name 'float32' - >>> print hdu.header['bitpix'] # BITPIX is also updated + >>> hdu.header['bitpix'] # BITPIX is also updated -32 - # BZERO and BSCALE are removed after the scaling - >>> print hdu.header['bzero'] - KeyError: "Keyword 'bzero' not found." + >>> # BZERO and BSCALE are removed after the scaling + >>> hdu.header['bzero'] + Traceback (most recent call last): + ... + KeyError: "Keyword 'BZERO' not found." .. warning:: - An important caveat to be aware of when dealing with scaled data in PyFITS, - is that when accessing the data via the .data attribute, the data is - automatically scaled with the BZERO and BSCALE parameters. If the file was - opened in "update" mode, it will be saved with the rescaled data. This - surprising behavior is a compromise to err on the side of not losing data: - If some floating point calculations were made on the data, rescaling it - when saving could result in a loss of information. + + An important caveat to be aware of when dealing with scaled data in + ``astropy``, is that when accessing the data via the ``.data`` attribute, + the data is automatically scaled with the BZERO and BSCALE parameters. If + the file was opened in "update" mode, it will be saved with the rescaled + data. This surprising behavior is a compromise to err on the side of not + losing data: if some floating point calculations were made on the data, + rescaling it when saving could result in a loss of information. To prevent this automatic scaling, open the file with the - ``do_not_scale_image_data=True`` argument to ``fits.open()``. This is + ``do_not_scale_image_data=True`` argument to ``fits.open()``. This is especially useful for updating some header values, while ensuring that the data is not modified. - One may also manually reapply scale parameters by using ``hdu.scale()`` - (see below). Alternately, one may open files with the ``scale_back=True`` - argument. This assures that the original scaling is preserved when saving. + You may also manually reapply scale parameters by using ``hdu.scale()`` + (see below). Alternately, you may open files with the ``scale_back=True`` + argument. This assures that the original scaling is preserved when saving + even when the physical values are updated. In other words, it reapplies + the scaling to the new physical values upon saving. + +.. + EXAMPLE END Writing Scaled Image Data -""""""""""""""""""""""""" +------------------------- + +With the extra processing and memory requirement, we discourage the use of +scaled data as much as possible. However, ``astropy`` does provide ways to +write scaled data with the `~ImageHDU.scale` method. + +Examples +^^^^^^^^ -With the extra processing and memory requirement, we discourage users to use -scaled data as much as possible. However, Astropy does provide ways to write -scaled data with the scale(type, option, bscale, bzero) method. Here are a few -examples: +.. + EXAMPLE START + Writing Scaled Image Data in astropy.io.fits + +To write scaled data with the `~ImageHDU.scale` method:: >>> # scale the data to Int16 with user specified bscale/bzero >>> hdu.scale('int16', bzero=32768) - >>> # scale the data to Int32 with the min/max of the data range - >>> hdu.scale('int32', 'minmax') - >>> # scale the data, using the original BSCALE/BZERO - >>> hdu.scale('int32', 'old') + >>> # scale the data to Int32 with the min/max of the data range, emits + >>> # RuntimeWarning: overflow encountered in short_scalars + >>> hdu.scale('int32', 'minmax') # doctest: +SKIP + >>> # scale the data, using the original BSCALE/BZERO, emits + >>> # RuntimeWarning: invalid value encountered in add + >>> hdu.scale('int32', 'old') # doctest: +SKIP + >>> hdul.close() The first example above shows how to store an unsigned short integer array. -Great caution must be exercised when using the :meth:`~ImageHDU.scale` method. +Caution must be exercised when using the :meth:`~ImageHDU.scale` method. The :attr:`~ImageHDU.data` attribute of an image HDU, after the :meth:`~ImageHDU.scale` call, will become the storage values, not the physical values. So, only call :meth:`~ImageHDU.scale` just before writing out to FITS -files, i.e. calls of :meth:`~HDUList.writeto`, :meth:`~HDUList.flush`, or -:meth:`~HDUList.close`. No further use of the data should be exercised. Here is +files (i.e., calls of :meth:`~HDUList.writeto`, :meth:`~HDUList.flush`, or +:meth:`~HDUList.close`). No further use of the data should be exercised. Here is an example of what happens to the :attr:`~ImageHDU.data` attribute after the -:meth:`~ImageHDU.scale` call: +:meth:`~ImageHDU.scale` call:: - >>> hdu = fits.PrimaryHDU(numpy.array([0., 1, 2, 3])) - >>> print hdu.data - [ 0. 1. 2. 3.] + >>> hdu = fits.PrimaryHDU(np.array([0., 1, 2, 3])) + >>> print(hdu.data) # doctest: +FLOAT_CMP + [0. 1. 2. 3.] >>> hdu.scale('int16', bzero=32768) - >>> print hdu.data # now the data has storage values + >>> print(hdu.data) # now the data has storage values [-32768 -32767 -32766 -32765] >>> hdu.writeto('new.fits') +.. + EXAMPLE END .. _data-sections: Data Sections -^^^^^^^^^^^^^ +============= -When a FITS image HDU's .data is accessed, either the whole data is copied into -memory (in cases of NOT using memory mapping or if the data is scaled) or a -virtual memory space equivalent to the data size is allocated (in the case of -memory mapping of non-scaled data). If there are several very large image HDUs -being accessed at the same time, the system may run out of memory. +When a FITS image HDU's :attr:`~ImageHDU.data` is accessed, either the whole +data is copied into memory (in cases of NOT using memory mapping or if the data +is scaled) or a virtual memory space equivalent to the data size is allocated +(in the case of memory mapping of non-scaled data). If there are several very +large image HDUs being accessed at the same time, the system may run out of +memory. -If a user does not need the entire image(s) at the same time, e.g. processing -images(s) ten rows at a time, the :attr:`~ImageHDU.section` attribute of an +If a user does not need the entire image(s) at the same time (e.g., processing +the images(s) ten rows at a time), the :attr:`~ImageHDU.section` attribute of an HDU can be used to alleviate such memory problems. -With PyFITS' improved support for memory-mapping, the sections feature is not -as necessary as it used to be for handling very large images. However, if the -image's data is scaled with non-trivial BSCALE/BZERO values, accessing the data -in sections may still be necessary under the current implementation. Memmap is -also insufficient for loading images large than ~4 GB on a 32-bit system--in -such cases it may be necessary to use sections. - -Here is an example of getting the median image from 3 input images of the size -5000x5000: - - >>> f1 = fits.open('file1.fits') - >>> f2 = fits.open('file2.fits') - >>> f3 = fits.open('file3.fits') - >>> output = numpy.zeros(5000 * 5000) - >>> for i in range(50): - ... j = i * 100 - ... k = j + 100 - ... x1 = f1[1].section[j:k,:] - ... x2 = f2[1].section[j:k,:] - ... x3 = f3[1].section[j:k,:] - ... # use scipy.stsci.image's median function - ... output[j:k] = image.median([x1, x2, x3]) +With ``astropy``'s improved support for memory-mapping, the sections feature is +not as necessary as it used to be for handling large images stored in local files. +However, it remains very useful in the following circumstances: + +* If the image's data is scaled with non-trivial BSCALE/BZERO values, accessing the + data in sections may still be necessary under the current implementation. +* Memory mapping is insufficient for loading images larger than 2 to 4 GB on a 32-bit + system — in such cases it may be necessary to use sections. +* Memory mapping does not work for accessing remote FITS files. + In this case sections may be your only option. See :ref:`astropy:fits_io_cloud`. + +In addition, for compressed FITS files, :attr:`CompImageHDU.section` can be used +to access and decompress only parts of the data, and can provide a significant +speedup. Note however that accessing data using :attr:`CompImageHDU.section` will +always load tiles one at a time from disk, and therefore when accessing a large +fraction of the data (or slicing it in a way that would cause most tiles to be +loaded) you may obtain better performance by using :attr:`CompImageHDU.data`. + +Example +------- + +.. + EXAMPLE START + Data Sections in astropy.io.fits + +Here is an example of getting the median image from three input images of the +size 5000x5000. + +.. code:: python + + hdul1 = fits.open('file1.fits') + hdul2 = fits.open('file2.fits') + hdul3 = fits.open('file3.fits') + output = np.zeros((5000, 5000)) + for i in range(50): + j = i * 100 + k = j + 100 + x1 = hdul1[0].section[j:k,:] + x2 = hdul2[0].section[j:k,:] + x3 = hdul3[0].section[j:k,:] + output[j:k, :] = np.median([x1, x2, x3], axis=0) Data in each :attr:`~ImageHDU.section` does not need to be contiguous for -memory savings to be possible. PyFITS will do its best to join together +memory savings to be possible. ``astropy`` will do its best to join together discontiguous sections of the array while reading as little as possible into -memory. +main memory. + +Sections cannot currently be assigned. Any modifications made to a data +section are not saved back to the original file. -Sections cannot be assigned to. Any modifications made to a data section are -not saved back to the original file. +.. + EXAMPLE END diff --git a/docs/io/fits/usage/misc.rst b/docs/io/fits/usage/misc.rst index 29409227bb22..8552477c56a2 100644 --- a/docs/io/fits/usage/misc.rst +++ b/docs/io/fits/usage/misc.rst @@ -1,61 +1,14 @@ .. currentmodule:: astropy.io.fits Miscellaneous Features ----------------------- +********************** -In this chapter, we'll describe some of the miscellaneous features of Astropy. +This section describes some of the miscellaneous features of :mod:`astropy.io.fits`. - -Warning Messages -^^^^^^^^^^^^^^^^ - -Astropy uses the Python warnings module to issue warning messages. The user can -suppress the warnings using the python command line argument ``-W"ignore"`` -when starting an interactive python session. For example: - -.. parsed-literal:: - - python -W"ignore" - -The user may also use the command line argument when running a python script as -follows: - -.. parsed-literal:: - - python -W"ignore" myscript.py - -It is also possible to suppress warnings from within a python script. For -instance, the warnings issued from a single call to the writeto convenience -function may be suppressed from within a python script as follows: - -.. parsed-literal:: - - import warnings - from astropy.io import fits - - # ... - - warnings.resetwarnings() - warnings.filterwarnings('ignore', category=UserWarning, append=True) - fits.writeto(file, im, clobber=True) - warnings.resetwarnings() - warnings.filterwarnings('always', category=UserWarning, append=True) - - # ... - -Astropy also issues warnings when deprecated API features are used. In Python -2.7 and up deprecation warnings are ignored by default. To run Python with -deprecation warnings enabled, either start Python with the ``-Wall`` argument, -or you can enable deprecation warnings specifically with ``-Wd``. - -In Python versions below 2.7, if you wish to *squelch* deprecation warnings, -you can start Python with ``-Wi::Deprecation``. This sets all deprecation -warnings to ignored. See -http://docs.python.org/using/cmdline.html#cmdoption-unittest-discover-W -for more information on the -W argument. +.. _io-fits-differs: Differs -^^^^^^^ +======= The :mod:`astropy.io.fits.diff` module contains several facilities for generating and reporting the differences between two FITS files, or two @@ -66,11 +19,36 @@ differences between either two FITS files on disk, or two existing :class:`HDUList` objects (or some combination thereof). Likewise, the :class:`HeaderDiff` class can be used to find the differences -just between two :class:`Header` objects. Other available differs include +just between two :class:`Header` objects. Other available differs include :class:`HDUDiff`, :class:`ImageDataDiff`, :class:`TableDataDiff`, and :class:`RawDataDiff`. Each of these classes are instantiated with two instances of the objects that -they diff. The returned diff instance has a number of attributes starting with -``.diff_`` that describe differences between the two objects. See the API -documentation for details on the different differ classes. +they diff. The returned diff instance has a number of attributes starting with +``.diff_`` that describe differences between the two objects. + +Example +------- + +.. + EXAMPLE START + Generating Differences Between FITS Files Using astropy.io.fits.diff + +The :class:`HeaderDiff` class can be used to find the differences +between two :class:`Header` objects like so:: + + >>> from astropy.io import fits + >>> header1 = fits.Header([('KEY_A', 1), ('KEY_B', 2)]) + >>> header2 = fits.Header([('KEY_A', 3), ('KEY_C', 4)]) + >>> diff = fits.diff.HeaderDiff(header1, header2) + >>> diff.identical + False + >>> diff.diff_keywords + (['KEY_B'], ['KEY_C']) + >>> diff.diff_keyword_values + defaultdict(..., {'KEY_A': [(1, 3)]}) + +See the API documentation for details on the different differ classes. + +.. + EXAMPLE END diff --git a/docs/io/fits/usage/scripts.rst b/docs/io/fits/usage/scripts.rst index 392892e3cd87..dd08dac63d3d 100644 --- a/docs/io/fits/usage/scripts.rst +++ b/docs/io/fits/usage/scripts.rst @@ -1,25 +1,35 @@ Executable Scripts ------------------- +****************** -Astropy installs a couple of useful utility programs on your system that are -built with Astropy. +``astropy`` installs a couple of useful utility programs on your system that are +built with ``astropy``. + +fitsinfo +======== +.. automodule:: astropy.io.fits.scripts.fitsinfo + +fitsheader +========== +.. automodule:: astropy.io.fits.scripts.fitsheader fitscheck -^^^^^^^^^ +========= .. automodule:: astropy.io.fits.scripts.fitscheck -With Astropy installed, please run ``fitscheck --help`` to see the full program -usage documentation. +With ``astropy`` installed, please run ``fitscheck --help`` to see the full +program usage documentation. + +.. _fitsdiff: fitsdiff -^^^^^^^^ +======== .. currentmodule:: astropy.io.fits -fitsdiff provides a thin command-line wrapper around the :class:`FITSDiff` -interface--it outputs the report from a :class:`FITSDiff` of two FITS files, +``fitsdiff`` provides a thin command-line wrapper around the :class:`FITSDiff` +interface. It outputs the report from a :class:`FITSDiff` of two FITS files, and like common diff-like commands returns a 0 status code if no differences were found, and 1 if differences were found: -With Astropy installed, please run ``fitscheck --help`` to see the full program -usage documentation. +With ``astropy`` installed, please run ``fitsdiff --help`` to see the full +program usage documentation. diff --git a/docs/io/fits/usage/table.rst b/docs/io/fits/usage/table.rst index f41840df46c1..80ccce9de953 100644 --- a/docs/io/fits/usage/table.rst +++ b/docs/io/fits/usage/table.rst @@ -1,186 +1,290 @@ + .. currentmodule:: astropy.io.fits Table Data ----------- +********** -In this chapter, we'll discuss the data component in a table HDU. A table will +In this chapter, we will discuss the data component in a table HDU. A table will always be in an extension HDU, never in a primary HDU. -There are two kinds of table in the FITS standard: binary tables and ASCII +There are two kinds of tables in the FITS standard: binary tables and ASCII tables. Binary tables are more economical in storage and faster in data access and manipulation. ASCII tables store the data in a "human readable" form and -therefore takes up more storage space as well as more processing time since the -ASCII text need to be parsed back into numerical values. +therefore take up more storage space as well as more processing time since the +ASCII text needs to be parsed into numerical values. + +.. note:: + If you want to read or write a single table in FITS format then the + most convenient method is often via the high-level :ref:`table_io`. In + particular see the :ref:`Unified I/O FITS ` section. Table Data as a Record Array -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +============================ What is a Record Array? -""""""""""""""""""""""" +----------------------- -A record array is an array which contains records (i.e. rows) of heterogeneous -data types. Record arrays are available through the records module in the numpy -library. Here is a simple example of record array: +A record array is an array which contains records (i.e., rows) of heterogeneous +data types. Record arrays are available through the records module in the NumPy +library. - >>> from numpy import rec - >>> bright = rec.array([(1,'Sirius', -1.45, 'A1V'), - ... (2,'Canopus', -0.73, 'F0Ib'), - ... (3,'Rigil Kent', -0.1, 'G2V')], - ... formats='int16,a20,float32,a10', - ... names='order,name,mag,Sp') +Here is a sample record array:: -In this example, there are 3 records (rows) and 4 fields (columns). The first -field is a short integer, second a character string (of length 20), third a -floating point number, and fourth a character string (of length 10). Each -record has the same (heterogeneous) data structure. + >>> import numpy as np + >>> bright = np.rec.array([(1,'Sirius', -1.45, 'A1V'), + ... (2,'Canopus', -0.73, 'F0Ib'), + ... (3,'Rigil Kent', -0.1, 'G2V')], + ... formats='int16,S20,float32,S10', + ... names='order,name,mag,Sp') + +In this example, there are three records (rows) and four fields (columns). The +first field is a short integer, the second a character string (of length 20), +the third a floating point number, and the fourth a character string (of length +10). Each record has the same (heterogeneous) data structure. + +The underlying data structure used for FITS tables is a class called +:class:`FITS_rec` which is a specialized subclass of `numpy.recarray`. A +:class:`FITS_rec` can be instantiated directly from a +numpy recarray:: + + >>> from astropy.io import fits + >>> data = fits.FITS_rec(bright) + +You may also instantiate a new :class:`FITS_rec` from a list of `astropy.io.fits.Column` +objects using the :meth:`FITS_rec.from_columns` class method. This has the +exact same semantics as :meth:`BinTableHDU.from_columns` and +:meth:`TableHDU.from_columns`, except that it only returns an actual FITS_rec +array and not a whole HDU object. Metadata of a Table -""""""""""""""""""" +------------------- -The data in a FITS table HDU is basically a record array, with added -attributes. The metadata, i.e. information about the table data, are stored in +The data in a FITS table HDU is basically a record array with added +attributes. The metadata (i.e., information about the table data) are stored in the header. For example, the keyword TFORM1 contains the format of the first field, TTYPE2 the name of the second field, etc. NAXIS2 gives the number of -records(rows) and TFIELDS gives the number of fields (columns). For FITS +records (rows) and TFIELDS gives the number of fields (columns). For FITS tables, the maximum number of fields is 999. The data type specified in TFORM -is represented by letter codes for binary tables and a FORTRAN-like format +is represented by letter codes for binary tables and a Fortran-like format string for ASCII tables. Note that this is different from the format specifications when constructing a record array. Reading a FITS Table -"""""""""""""""""""" - -Like images, the .data attribute of a table HDU contains the data of the table. -To recap, the simple example in the Quick Tutorial: - - >>> f = fits.open('bright_stars.fits') # open a FITS file - >>> tbdata = f[1].data # assume the first extension is a table - >>> print tbdata[:2] # show the first two rows - [(1, 'Sirius', -1.4500000476837158, 'A1V'), - (2, 'Canopus', -0.73000001907348633, 'F0Ib')] - - >>> print tbdata.field('mag') # show the values in field "mag" - [-1.45000005 -0.73000002 -0.1 ] - >>> print tbdata.field(1) # field can be referred by index too - ['Sirius' 'Canopus' 'Rigil Kent'] - >>> scidata[1,4] = 999 # update a pixel value - >>> scidata[30:40, 10:20] = 0 # update values of a subsection - >>> scidata[3] = scidata[2] # copy the 3rd row to the 4th row - -Note that in Astropy, when using the ``field()`` method, it is 0-indexed while -the suffixes in header keywords, such as TFORM is 1-indexed. So, -``tbdata.field(0)`` is the data in the column with the name specified in TTYPE1 +-------------------- + +Like images, the ``.data`` attribute of a table HDU contains the data of the +table. + +Example +^^^^^^^ + +.. note:: + + The ``astropy.io.fits.util.get_testdata_filepath()`` function, + used in the examples here, returns file path for test data shipped with ``astropy``. + To work with your own data instead, please use :func:`astropy.io.fits.open` or :ref:`io-fits-intro-convenience-functions`, + which take either the relative or absolute path as string or :term:`python:path-like object`. + +.. + EXAMPLE START + Reading a FITS Table with astropy.io.fits + +To read a FITS Table:: + + + >>> from astropy.io import fits + >>> fits_table_filename = fits.util.get_testdata_filepath('btable.fits') + + >>> hdul = fits.open(fits_table_filename) # open a FITS file + >>> data = hdul[1].data # assume the first extension is a table + >>> # show the first two rows + >>> first_two_rows = data[:2] + >>> first_two_rows # doctest: +SKIP + [(1, 'Sirius', -1.45000005, 'A1V') (2, 'Canopus', -0.73000002, 'F0Ib')] + >>> # show the values in field "mag" + >>> magnitudes = data['mag'] + >>> magnitudes # doctest: +SKIP + array([-1.45000005, -0.73000002, -0.1 ], dtype=float32) + >>> # columns can be referenced by index too + >>> names = data.field(1) + >>> names.tolist() # doctest: +SKIP + ['Sirius', 'Canopus', 'Rigil Kent'] + >>> hdul.close() + +Note that in ``astropy``, when using the ``field()`` method, it is 0-indexed +while the suffixes in header keywords such as TFORM is 1-indexed. So, +``data.field(0)`` is the data in the column with the name specified in TTYPE1 and format in TFORM1. -**Warning:** The FITS format allows table columns with a zero-width data -format, such as '0D'. This is probably intended as a space-saving measure on -files in which that column contains no data. In such files, the zero-width -columns are ommitted when accessing the table data, so the indexes of fields -might change when using the ``field()`` method. For this reason, if you expect -to encounter files containg zero-width columns it is recommended to access -fields by name rather than by index. +.. warning:: + + The FITS format allows table columns with a zero-width data format, such as + ``'0D'``. This is probably intended as a space-saving measure on files in + which that column contains no data. In such files, the zero-width columns + are omitted when accessing the table data, so the indexes of fields might + change when using the ``field()`` method. For this reason, if you expect + to encounter files containing zero-width columns it is recommended to access + fields by name rather than by index. + +.. + EXAMPLE END Table Operations -^^^^^^^^^^^^^^^^ +================ Selecting Records in a Table -"""""""""""""""""""""""""""" +---------------------------- Like image data, we can use the same "mask array" idea to pick out desired records from a table and make a new table out of it. -In the next example, assuming the table's second field having the name -'magnitude', an output table containing all the records of magnitude > 5 from -the input table is generated: +Examples +^^^^^^^^ - >>> from astropy.io import fits - >>> t = fits.open('table.fits') - >>> tbdata = t[1].data - >>> mask = tbdata.field('magnitude') > 5 - >>> newtbdata = tbdata[mask] - >>> hdu = fits.BinTableHDU(newtbdata) - >>> hdu.writeto('newtable.fits') +.. + EXAMPLE START + Selecting Records in a Table Using a "Mask Array" + +Assuming the table's second field as having the name 'magnitude', an output +table containing all the records of magnitude > -0.5 from the input table is +generated:: + >>> with fits.open(fits_table_filename) as hdul: + ... data = hdul[1].data + ... mask = data['mag'] > -0.5 + ... newdata = data[mask] + ... hdu = fits.BinTableHDU(data=newdata) + ... hdu.writeto('newtable.fits') + +It is also possible to update the data from the HDU object in-place:: + + >>> with fits.open(fits_table_filename) as hdul: + ... hdu = hdul[1] + ... mask = hdu.data['mag'] > -0.5 + ... hdu.data = hdu.data[mask] + ... hdu.writeto('newtable2.fits') + +.. + EXAMPLE END Merging Tables -"""""""""""""" +-------------- + +Merging different tables is very convenient in ``astropy``. + +Examples +^^^^^^^^ + +.. + EXAMPLE START + Merging FITS Tables -Merging different tables is straightforward in Astropy. Simply merge the column -definitions of the input tables: +To merge the column definitions of the input tables:: - >>> t1 = fits.open('table1.fits') - >>> t2 = fits.open('table2.fits') - # the column attribute is the column definitions - >>> t = t1[1].columns + t2[1].columns - >>> hdu = fits.new_table(t) - >>> hdu.writeto('newtable.fits') + >>> fits_other_table_filename = fits.util.get_testdata_filepath('table.fits') + + >>> with fits.open(fits_table_filename) as hdul1: + ... with fits.open(fits_other_table_filename) as hdul2: + ... new_columns = hdul1[1].columns + hdul2[1].columns + ... new_hdu = fits.BinTableHDU.from_columns(new_columns) + >>> new_columns + ColDefs( + name = 'order'; format = 'I' + name = 'name'; format = '20A' + name = 'mag'; format = 'E' + name = 'Sp'; format = '10A' + name = 'target'; format = '20A' + name = 'V_mag'; format = 'E' + ) The number of fields in the output table will be the sum of numbers of fields -of the input tables. Users have to make sure the input tables don't share any +of the input tables. Users have to make sure the input tables do not share any common field names. The number of records in the output table will be the largest number of records of all input tables. The expanded slots for the originally shorter table(s) will be zero (or blank) filled. +Another version of this example can be used to append a new column to a +table. Updating an existing table with a new column is generally more +difficult than it is worth, but you can "append" a column to a table by creating +a new table with columns from the existing table plus the new column(s):: + + >>> with fits.open(fits_table_filename) as hdul: + ... orig_table = hdul[1].data + ... orig_cols = orig_table.columns + >>> new_cols = fits.ColDefs([ + ... fits.Column(name='NEWCOL1', format='D', + ... array=np.zeros(len(orig_table))), + ... fits.Column(name='NEWCOL2', format='D', + ... array=np.zeros(len(orig_table)))]) + >>> hdu = fits.BinTableHDU.from_columns(orig_cols + new_cols) + +Now ``newtable.fits`` contains a new table with the original table, plus the +two new columns filled with zeros. + +.. + EXAMPLE END Appending Tables -"""""""""""""""" +---------------- Appending one table after another is slightly trickier, since the two tables -may have different field attributes. Here are two examples. The first is to -append by field indices, the second one is to append by field names. In both -cases, the output table will inherit column attributes (name, format, etc.) of -the first table. - - >>> t1 = fits.open('table1.fits') - >>> t2 = fits.open('table2.fits') - # one way to find the number of records - >>> nrows1 = t1[1].data.shape[0] - # another way to find the number of records - >>> nrows2 = t2[1].header['naxis2'] - # total number of rows in the table to be generated - >>> nrows = nrows1 + nrows2 - >>> hdu = fits.new_table(t1[1].columns, nrows=nrows) - # first case, append by the order of fields - >>> for i in range(len(t1[1].columns)): - ... hdu.data.field(i)[nrows1:]=t2[1].data.field(i) - # or, second case, append by the field names - >>> for name in t1[1].columns.names: - ... hdu.data.field(name)[nrows1:]=t2[1].data.field(name) - # write the new table to a FITS file - >>> hdu.writeto('newtable.fits') +may have different field attributes. + +Examples +^^^^^^^^ + +.. + EXAMPLE START + Appending to FITS Tables + +Here, the first example is to append by field indices, and the second one is to +append by field names. In both cases, the output table will inherit the column +attributes (name, format, etc.) of the first table:: + >>> with fits.open(fits_table_filename) as hdul1: + ... with fits.open(fits_table_filename) as hdul2: + ... nrows1 = hdul1[1].data.shape[0] + ... nrows2 = hdul2[1].data.shape[0] + ... nrows = nrows1 + nrows2 + ... hdu = fits.BinTableHDU.from_columns(hdul1[1].columns, nrows=nrows) + ... for colname in hdul1[1].columns.names: + ... hdu.data[colname][nrows1:] = hdul2[1].data[colname] + +.. + EXAMPLE END Scaled Data in Tables -^^^^^^^^^^^^^^^^^^^^^ +===================== A table field's data, like an image, can also be scaled. Scaling in a table has a more generalized meaning than in images. In images, the physical data is a simple linear transformation from the storage data. The table fields do have -such construct too, where BSCALE and BZERO are stored in the header as TSCALn -and TZEROn. In addition, Boolean columns and ASCII tables' numeric fields are +such a construct too, where BSCALE and BZERO are stored in the header as TSCALn +and TZEROn. In addition, boolean columns and ASCII tables' numeric fields are also generalized "scaled" fields, but without TSCAL and TZERO. All scaled fields, like the image case, will take extra memory space as well as processing. So, if high performance is desired, try to minimize the use of scaled fields. -All the scalings are done for the user, so the user only sees the physical -data. Thus, this no need to worry about scaling back and forth between the +All of the scalings are done for the user, so the user only sees the physical +data. Thus, there is no need to worry about scaling back and forth between the physical and storage column values. Creating a FITS Table -^^^^^^^^^^^^^^^^^^^^^ +===================== +.. _column_creation: Column Creation -""""""""""""""" +--------------- To create a table from scratch, it is necessary to create individual columns first. A :class:`Column` constructor needs the minimal information of column @@ -195,17 +299,18 @@ name and format. Here is a summary of all allowed formats for a binary table: B Unsigned byte 1 I 16-bit integer 2 J 32-bit integer 4 - K 64-bit integer 4 + K 64-bit integer 8 A character 1 - E single precision floating point 4 - D double precision floating point 8 + E single precision float (32-bit) 4 + D double precision float (64-bit) 8 C single precision complex 8 M double precision complex 16 P array descriptor 8 + Q array descriptor 16 -We'll concentrate on binary tables in this chapter. ASCII tables will be +We will concentrate on binary tables in this chapter. ASCII tables will be discussed in a later chapter. The less frequently used X format (bit array) and -P format (used in variable length tables) will also be discussed in a later +P format (used in :ref:`variable_length_array_tables`) will also be discussed in a later chapter. Besides the required name and format arguments in constructing a @@ -227,88 +332,346 @@ header keywords and descriptions: disp TDISP display format dim TDIM multi-dimensional array spec start TBCOL starting position for ASCII table + coord_type TCTYP coordinate/axis type + coord_unit TCUNI coordinate/axis unit + coord_ref_point TCRPX pixel coordinate of the reference point + coord_ref_value TCRVL coordinate value at reference point + coord_inc TCDLT coordinate increment at reference point + time_ref_pos TRPOS reference position for a time coordinate column + ascii specifies a column for an ASCII table array the data of the column +Examples +^^^^^^^^ -Here are a few Columns using various combination of these arguments: +.. + EXAMPLE START + Creating a FITS Table + +Here are a few Columns using various combinations of the optional arguments:: - >>> import numpy as np - >>> from fits import Column >>> counts = np.array([312, 334, 308, 317]) >>> names = np.array(['NGC1', 'NGC2', 'NGC3', 'NGC4']) - >>> c1 = Column(name='target', format='10A', array=names) - >>> c2 = Column(name='counts', format='J', unit='DN', array=counts) - >>> c3 = Column(name='notes', format='A10') - >>> c4 = Column(name='spectrum', format='1000E') - >>> c5 = Column(name='flag', format='L', array=[1, 0, 1, 1]) + >>> values = np.arange(2*2*4).reshape(4, 2, 2) + >>> col1 = fits.Column(name='target', format='10A', array=names) + >>> col2 = fits.Column(name='counts', format='J', unit='count', array=counts) + >>> col3 = fits.Column(name='notes', format='A10') + >>> col4 = fits.Column(name='spectrum', format='10E') + >>> col5 = fits.Column(name='flag', format='L', array=[True, False, True, True]) + >>> col6 = fits.Column(name='intarray', format='4I', dim='(2, 2)', array=values) In this example, formats are specified with the FITS letter codes. When there is a number (>1) preceding a (numeric type) letter code, it means each cell in -that field is a one-dimensional array. In the case of column c4, each cell is -an array (a numpy array) of 1000 elements. - -For character string fields, the number can be either before or after the -letter 'A' and they will mean the same string size. So, for columns c1 and c3, -they both have 10 characters in each of their cells. For numeric data type, the -dimension number must be before the letter code, not after. - -After the columns are constructed, the :func:`new_table` function can be used to -construct a table HDU. We can either go through the column definition object: - - >>> coldefs = fits.ColDefs([c1, c2, c3, c4, c5]) - >>> tbhdu = fits.new_table(coldefs) - -or directly use the :func:`new_table` function: - - >>> tbhdu = fits.new_table([c1, c2, c3, c4, c5]) - -A look of the newly created HDU's header will show that relevant keywords are -properly populated: - - >>> tbhdu.header - XTENSION = 'BINTABLE' / binary table extension - BITPIX = 8 / array data type - NAXIS = 2 / number of array dimensions - NAXIS1 = 4025 / length of dimension 1 - NAXIS2 = 4 / length of dimension 2 - PCOUNT = 0 / number of group parameters - GCOUNT = 1 / number of groups - TFIELDS = 5 / number of table fields - TTYPE1 = 'target ' - TFORM1 = '10A ' - TTYPE2 = 'counts ' - TFORM2 = 'J ' - TUNIT2 = 'DN ' - TTYPE3 = 'notes ' - TFORM3 = '10A ' - TTYPE4 = 'spectrum' - TFORM4 = '1000E ' - TTYPE5 = 'flag ' - TFORM5 = 'L ' - -**Warning:** It should be noted that when creating a new table with -:func:`new_table`, an in-memory copy of all of the input column arrays is -created. This is because it is not guaranteed that the columns are arranged -contiguously in memory in row-major order (in fact, they are most likely not), -so they have to be combined into a new array. +that field is a one-dimensional array. In the case of column "col4", each cell +is an array (a NumPy array) of 10 elements. And in the case of column "col6", +with the use of the "dim" argument, each cell is a multi-dimensional array of +2x2 elements. + +For character string fields, the number should be to the *left* of the letter +'A' when creating binary tables, and should be to the *right* when creating +ASCII tables. However, as this is a common confusion, both formats are +understood when creating binary tables (note, however, that upon writing to a +file the correct format will be written in the header). So, for columns "col1" +and "col3", they both have 10 characters in each of their cells. For numeric +data type, the dimension number must be before the letter code, not after. + +After the columns are constructed, the :meth:`BinTableHDU.from_columns` class +method can be used to construct a table HDU. We can either go through the +column definition object:: + + >>> coldefs = fits.ColDefs([col1, col2, col3, col4, col5, col6]) + >>> hdu = fits.BinTableHDU.from_columns(coldefs) + >>> coldefs + ColDefs( + name = 'target'; format = '10A' + name = 'counts'; format = 'J'; unit = 'count' + name = 'notes'; format = '10A' + name = 'spectrum'; format = '10E' + name = 'flag'; format = 'L' + name = 'intarray'; format = '4I'; dim = '(2, 2)' + ) + +or directly use the :meth:`BinTableHDU.from_columns` method:: + + >>> hdu = fits.BinTableHDU.from_columns([col1, col2, col3, col4, col5, col6]) + >>> hdu.columns + ColDefs( + name = 'target'; format = '10A' + name = 'counts'; format = 'J'; unit = 'count' + name = 'notes'; format = '10A' + name = 'spectrum'; format = '10E' + name = 'flag'; format = 'L' + name = 'intarray'; format = '4I'; dim = '(2, 2)' + ) + +.. note:: + + Users familiar with older versions of ``astropy`` will wonder what + happened to ``astropy.io.fits.new_table``. :meth:`BinTableHDU.from_columns` + and its companion for ASCII tables :meth:`TableHDU.from_columns` are the + same in the arguments they accept and their behavior, but make it + more explicit as to what type of table HDU they create. + +A look at the newly created HDU's header will show that relevant keywords are +properly populated:: + + >>> hdu.header + XTENSION= 'BINTABLE' / binary table extension + BITPIX = 8 / array data type + NAXIS = 2 / number of array dimensions + NAXIS1 = 73 / length of dimension 1 + NAXIS2 = 4 / length of dimension 2 + PCOUNT = 0 / number of group parameters + GCOUNT = 1 / number of groups + TFIELDS = 6 / number of table fields + TTYPE1 = 'target ' + TFORM1 = '10A ' + TTYPE2 = 'counts ' + TFORM2 = 'J ' + TUNIT2 = 'count ' + TTYPE3 = 'notes ' + TFORM3 = '10A ' + TTYPE4 = 'spectrum' + TFORM4 = '10E ' + TTYPE5 = 'flag ' + TFORM5 = 'L ' + TTYPE6 = 'intarray' + TFORM6 = '4I ' + TDIM6 = '(2, 2) ' + +.. warning:: + + It should be noted that when creating a new table with + :meth:`BinTableHDU.from_columns`, an in-memory copy of all of the input + column arrays is created. This is because it is not guaranteed that the + columns are arranged contiguously in memory in row-major order (in fact, + they are most likely not), so they have to be combined into a new array. However, if the array data *is* already contiguous in memory, such as in an existing record array, a kludge can be used to create a new table HDU without -any copying. First, create the Columns as before, but without using the -``array=`` argument: +any copying. First, create the Columns as before, but without using the +``array=`` argument:: - >>> c1 = Column(name='target', format='10A') - ... + >>> col1 = fits.Column(name='target', format='10A') -Then call :func:`new_table`: +Then call :meth:`BinTableHDU.from_columns`:: - >>> tbhdu = fits.new_table([c1, c2, c3, c4, c5]) + >>> hdu = fits.BinTableHDU.from_columns([col1, col2, col3, col4, col5]) This will create a new table HDU as before, with the correct column -definitions, but an empty data section. Now simply assign your array directly +definitions, but an empty data section. Now you can assign your array directly to the HDU's data attribute: - >>> tbhdu.data = mydata +.. doctest-skip:: + + >>> hdu.data = mydata + +In a future version of ``astropy``, table creation will be simplified and this +process will not be necessary. + +.. + EXAMPLE END + +.. _fits_time_column: + +FITS Tables with Time Columns +============================= + +The `FITS Time standard paper +`_ defines the formats +and keywords used to represent timing information in FITS files. The ``astropy`` +FITS package provides support for reading and writing native +`~astropy.time.Time` columns and objects using this format. This is done +within the :ref:`table_io_fits` unified I/O interface and examples of usage can +be found in the :ref:`fits_astropy_native` section. The support is not +complete and only a subset of the full standard is implemented. + +Example +------- + +.. + EXAMPLE START + FITS Tables with Time Columns + +The following is an example of a Header extract of a binary table (event list) +with a time column: + +.. parsed-literal:: + + COMMENT ---------- Globally valid key words ---------------- + TIMESYS = ’TT ’ / Time system + MJDREF = 50814.000000000000 / MJD zero point for (native) TT (= 1998-01-01) + MJD-OBS = 53516.257939301īŋŧīŋŧ / MJD for observation in (native) TT + + COMMENT ---------- Time Column ----------------------- + TTYPE1 = ’Time ’ / S/C TT corresponding to mid-exposure + TFORM1 = ’2D ’ / format of field + TUNIT1 = ’s ’ + TCTYP1 = ’TT ’ + TCNAM1 = ’Terrestrial Time’ / This is TT + TCUNI1 = ’s ’ + +.. + EXAMPLE END + +However, the FITS standard and the ``astropy`` Time object are not perfectly +mapped and some compromises must be made. To help the user understand how the +``astropy`` code deals with these situations, the following text describes the +approach that ``astropy`` takes in some detail. + +To create FITS columns which adhere to the FITS Time standard, we have taken +into account the following important points stated in the `FITS Time paper +`_. + +The strategy used to store `~astropy.time.Time` columns in FITS tables is to +create a `~astropy.io.fits.Header` with the appropriate time coordinate +global reference keywords and the column-specific override keywords. The +module ``astropy.io.fits.fitstime`` deals with the reading and writing of +Time columns. + +The following keywords set the Time Coordinate Frame: + +* TIME SCALE + + The most important of all of the metadata is the time scale which is a + specification for measuring time. + + .. parsed-literal:: + + **TIMESYS** (string-valued) + Time scale; default UTC + + **TCTYPn** (string-valued) + Column-specific override keyword + + The global time scale may be overridden by a time scale recorded in the table + equivalent keyword ``TCTYPn`` for time coordinates in FITS table columns. + ``TCTYna`` is used for alternate coordinates. + +* TIME REFERENCE + + The reference point in time to which all times in the HDU are relative. + Since there are no context-specific reference times in case there are + multiple time columns in the same table, we need to adjust the reference + times for the columns using some other keywords. + + The reference point in time shall be specified through one of the three + following keywords, which are listed in decreasing order of preference: + + .. parsed-literal:: + + **MJDREF** (floating-valued) + Reference time in MJD + + **JDREF** (floating-valued) + Reference time in JD + + **DATEREF** (datetime-valued) + Reference time in ISO-8601 + + The time reference keywords (MJDREF, JDREF, DATEREF) are interpreted using the + time scale specified in ``TIMESYS``. + + .. note:: + + If none of the three keywords are present, there is no problem as long as + all times in the HDU are expressed in ISO-8601 ``Datetime Strings`` format: + ``CCYY-MM-DD[Thh:mm:ss[.s...]]`` (e.g., ``"2015-04-05T12:22:33.8"``); + otherwise MJDREF = 0.0 must be assumed. + + The value of the reference time has global validity for all time values, + but it does not have a particular time scale associated with it. Thus we + need to use ``TCRVLn`` (time coordinate reference value) keyword to + compensate for the time scale differences. + +* TIME REFERENCE POSITION + + The reference position, specified by the keyword ``TREFPOS``, specifies the + spatial location at which the time is valid, either where the observation was + made or the point in space for which light-time corrections have been applied. + This may be a standard location (such as ``GEOCENTER`` or ``TOPOCENTER``) or + a point in space defined by specific coordinates. + + .. parsed-literal:: + + **TREFPOS** (string-valued) + Time reference position; default TOPOCENTER + + **TRPOSn** (string-valued) + Column-specific override keyword + + .. note:: + + For TOPOCENTER, we need to specify the observatory location + (ITRS Cartesian coordinates or geodetic latitude/longitude/height) in the + ``OBSGEO-*`` keywords. + +* TIME REFERENCE DIRECTION + + If any pathlength corrections have been applied to the time stamps (i.e., if + the reference position is not ``TOPOCENTER`` for observational data), the + reference direction that is used in calculating the pathlength delay should + be provided in order to maintain a proper analysis trail of the data. + However, this is useful only if there is also information available on the + location from where the observation was made (the observatory location). + + The reference direction is indicated through a reference to specific keywords. + These keywords may explicitly hold the direction or indicate columns holding + the coordinates. + + .. parsed-literal:: + + **TREFDIR** (string-valued) + Pointer to time reference direction + + **TRDIRn** (string-valued) + Column-specific override keyword + +* TIME UNIT + + The FITS standard recommends the time unit to be one of the allowed ones + in the specification. + + .. parsed-literal:: + + **TIMEUNIT** (string-valued) + Time unit; default s + + **TCUNIn** (string-valued) + Column-specific override + +* TIME OFFSET + + It is sometimes convenient to be able to apply a uniform clock correction + in bulk by putting that number in a single keyword. A second use + for a time offset is to set a zero offset to a relative time series, + allowing zero-relative times, or higher precision, in the time stamps. + Its default value is zero. + + .. parsed-literal:: + + **TIMEOFFS** (floating-valued) + This has global validity + +* The absolute, relative errors and time resolution, time binning can be used + when needed. + + +The following keywords define the global time informational keywords: + +* DATE and DATE-* keywords + + These define the date of HDU creation and observation in ISO-8601. + ``DATE`` is in UTC if the file is constructed on the Earth’s surface + and others are in the time scale given by ``TIMESYS``. + +* MJD-* keywords + + These define the same as above, but in ``MJD`` (Modified Julian Date). -In a future version of Astropy table creation will be simplified and this -process won't be necessary. +The implementation writes a subset of the above FITS keywords, which map +to the Time metadata. Time is intrinsically a coordinate and hence shares +keywords with the ``World Coordinate System`` specification for spatial +coordinates. Therefore, while reading FITS tables with time columns, +the verification that a coordinate column is indeed time is done using +the FITS WCS standard rules and suggestions. diff --git a/docs/io/fits/usage/unfamiliar.rst b/docs/io/fits/usage/unfamiliar.rst index 323f9a353037..b4da5f01ec2c 100644 --- a/docs/io/fits/usage/unfamiliar.rst +++ b/docs/io/fits/usage/unfamiliar.rst @@ -1,96 +1,124 @@ .. currentmodule:: astropy.io.fits Less Familiar Objects ---------------------- +********************* -In this chapter, we'll discuss less frequently used FITS data structures. They +In this chapter, we will discuss less frequently used FITS data structures. They include ASCII tables, variable length tables, and random access group FITS files. ASCII Tables -^^^^^^^^^^^^ +============ -FITS standard supports both binary and ASCII tables. In ASCII tables, all the -data are stored in a human readable text form, so it takes up more space and -extra processing to parse the text for numeric data. +FITS standard supports both binary and ASCII tables. In ASCII tables, all of the +data are stored in a human-readable text form, so it takes up more space and +extra processing to parse the text for numeric data. Depending on how the +columns are formatted, floating point data may also lose precision. -In Astropy, the interface for ASCII tables and binary tables is basically the -same, i.e. the data is in the ``.data`` attribute and the ``field()`` method -is used to refer to the columns and returns a numpy array. When reading the -table, Astropy will automatically detect what kind of table it is. +In ``astropy``, the interface for ASCII tables and binary tables is basically +the same (i.e., the data is in the ``.data`` attribute and the ``field()`` +method is used to refer to the columns and returns a ``numpy`` array). When +reading the table, ``astropy`` will automatically detect what kind of table it +is. + +:: >>> from astropy.io import fits - >>> hdus = fits.open('ascii_table.fits') - >>> hdus[1].data[:1] - FITS_rec( - ... [(10.123000144958496, 37)], - ... dtype=[('a', '>f4'),('b','>i4')]) - >>> hdus[1].data.field('a') - array([ 10.12300014, 5.19999981, 15.60999966, 0. , - 345. ], dtype=float32) - >>> hdus[1].data.formats + >>> filename = fits.util.get_testdata_filepath('ascii.fits') + >>> hdul = fits.open(filename) + >>> hdul[1].data[:1] # doctest: +SKIP + FITS_rec([(10.123, 37)], + dtype=(numpy.record, {'names':['a','b'], 'formats':['S10','S5'], 'offsets':[0,11], 'itemsize':16})) + >>> hdul[1].data['a'] + array([ 10.123, 5.2 , 15.61 , nan, 345. ]) + >>> hdul[1].data.formats ['E10.4', 'I5'] + >>> hdul.close() Note that the formats in the record array refer to the raw data which are ASCII -strings (therefore 'a11' and 'a5'), but the .formats attribute of data retains -the original format specifications ('E10.4' and 'I5'). +strings (therefore 'a11' and 'a5'), but the ``.formats`` attribute of data +retains the original format specifications ('E10.4' and 'I5'). +.. _creating_ascii_table: Creating an ASCII Table -""""""""""""""""""""""" +----------------------- Creating an ASCII table from scratch is similar to creating a binary table. The difference is in the Column definitions. The columns/fields in an ASCII table are more limited than in a binary table. It does not allow more than one -numerical value in a cell. Also, it only supports a subset of what allowed in a -binary table, namely character strings, integer, and (single and double +numerical value in a cell. Also, it only supports a subset of what is allowed +in a binary table, namely character strings, integer, and (single and double precision) floating point numbers. Boolean and complex numbers are not allowed. The format syntax (the values of the TFORM keywords) is different from that of a -binary table, they are: +binary table. They are: .. parsed-literal:: Aw Character string Iw (Decimal) Integer - Fw.d Single precision real - Ew.d Single precision real, in exponential notation + Fw.d Double precision real + Ew.d Double precision real, in exponential notation Dw.d Double precision real, in exponential notation -where, w is the width, and d the number of digits after the decimal point. The +where w is the width, and d the number of digits after the decimal point. The syntax difference between ASCII and binary tables can be confusing. For example, -a field of 3-character string is specified '3A' in a binary table and as 'A3' in -an ASCII table. - -The other difference is the need to specify the table type when using either -:meth:`ColDef` or :func:`new_table`. - -The default value for tbtype is ``BinTableHDU``. - - >>> - # Define the columns - >>> import numpy as np - >>> from astropy.io import fits - >>> a1 = np.array(['abcd', 'def']) - >>> r1 = np.array([11., 12.]) - >>> c1 = fits.Column(name='abc', format='A3', array=a1) - >>> c2 = fits.Column(name='def', format='E', array=r1, - ... bscale=2.3, bzero=0.6) - >>> c3 = fits.Column(name='t1', format='I', array=[91, 92, 93]) - # Create the table - >>> x = fits.ColDefs([c1, c2, c3], tbtype='TableHDU') - >>> hdu = fits.new_table(x, tbtype='TableHDU') - # Or, simply, - >>> hdu = fits.new_table([c1, c2, c3], tbtype='TableHDU') - >>> hdu.writeto('ascii.fits') - >>> hdu.data - FITS_rec([('abcd', 11.0, 91), ('def', 12.0, 92), ('', 0.0, 93)], - dtype=[('abc', '|S3'), ('def', '|S14'), ('t1', '|S10')]) +a field of 3-character string is specified as '3A' in a binary table and as +'A3' in an ASCII table. + +The other difference is the need to specify the table type when using the +:meth:`TableHDU.from_columns` method, and that `Column` should be provided the +``ascii=True`` argument in order to be unambiguous. + +.. note:: + Although binary tables are more common in most FITS files, earlier versions + of the FITS format only supported ASCII tables. That is why the class + :class:`TableHDU` is used for representing ASCII tables specifically, + whereas :class:`BinTableHDU` is more explicit that it represents a binary + table. These names come from the value ``XTENSION`` keyword in the tables' + headers, which is ``TABLE`` for ASCII tables and ``BINTABLE`` for binary + tables. + +:meth:`TableHDU.from_columns` can be used like so:: + + >>> import numpy as np + + >>> a1 = np.array(['abcd', 'def']) + >>> r1 = np.array([11., 12.]) + >>> col1 = fits.Column(name='abc', format='A3', array=a1, ascii=True) + >>> col2 = fits.Column(name='def', format='E', array=r1, bscale=2.3, + ... bzero=0.6, ascii=True) + >>> col3 = fits.Column(name='t1', format='I', array=[91, 92, 93], ascii=True) + >>> hdu = fits.TableHDU.from_columns([col1, col2, col3]) + >>> hdu.data + FITS_rec([('abc', np.float64(11.0), np.int32(91)), + ('def', np.float64(12.0), np.int32(92)), + ('', np.float64(0.0), np.int32(93))], + dtype=(numpy.record, [('abc', 'S3'), ('def', 'S15'), ('t1', 'S10')])) + +It should be noted that when the formats of the columns are unambiguously +specific to ASCII tables it is not necessary to specify ``ascii=True`` in +the :class:`ColDefs` constructor. In this case there *is* ambiguity because +the format code ``'I'`` represents a 16-bit integer in binary tables, while in +ASCII tables it is not technically a valid format. ASCII table format codes +technically require a character width for each column, such as ``'I10'`` to +create a column that can hold integers up to 10 characters wide. + +However, ``astropy`` allows the width specification to be omitted in some cases. +When it is omitted from ``'I'`` format columns the minimum width needed to +accurately represent all integers in the column is used. The only problem with +using this shortcut is its ambiguity with the binary table ``'I'`` format, so +specifying ``ascii=True`` is a good practice (though ``astropy`` will still +figure out what you meant in most cases). + + +.. _variable_length_array_tables: Variable Length Array Tables -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +============================ The FITS standard also supports variable length array tables. The basic idea is that sometimes it is desirable to have tables with cells in the same field @@ -101,97 +129,135 @@ different cells. A variable length array table can have one or more fields (columns) which are variable length. The rest of the fields (columns) in the same table can still -be regular, fixed-length ones. Astropy will automatically detect what kind of -field it is during reading; no special action is needed from the user. The data -type specification (i.e. the value of the TFORM keyword) uses an extra letter -'P' and the format is +be regular, fixed-length ones. +The data for the variable-length arrays in a table are not +stored in the main data table; they are stored in a supplemental +data area, the heap, following the main data table. +``astropy`` will automatically detect what kind +of field it is during reading; no special action is needed from the user. The +data type specification (i.e., the value of the TFORM keyword) uses an extra +letter 'P' (or 'Q') and the format is: .. parsed-literal:: rPt(max) -where r is 0, 1, or absent, t is one of the letter code for regular table data -type (L, B, X, I, J, etc. currently, the X format is not supported for variable -length array field in Astropy), and max is the maximum number of elements. So, -for a variable length field of int32, The corresponding format spec is, -e.g. 'PJ(100)'. - - >>> f = fits.open('variable_length_table.fits') - >>> print f[1].header['tform5'] - 1PI(20) - >>> print f[1].data.field(4)[:3] - [array([1], dtype=int16) array([88, 2], dtype=int16) - array([ 1, 88, 3], dtype=int16)] - -The above example shows a variable length array field of data type int16 and its -first row has one element, second row has 2 elements etc. Accessing variable -length fields is almost identical to regular fields, except that operations on -the whole filed are usually not possible. A user has to process the field row by -row. +where ``r`` may be 0 or 1 (typically omitted, as it is not applicable to +variable length arrays), ``t`` is one of the letter codes for basic data types +(L, B, I, J, etc.; currently, the X format is not supported for variable length +array field in ``astropy``), and ``max`` is the maximum number of elements of +any array in the column. So, for a variable length field of int16, the +corresponding format spec is, for example, 'PJ(100)'. +What is stored in the main data table field is an array descriptor. +This consists of two 32-bit signed integer values in the case of ’P’ format, +(or two 64-bit signed integer values in the case of ’Q’ format): +the number of elements (array length) of the stored array, +followed by the zero-indexed byte offset of the first +element of the array, measured from the start of the heap area. + +.. note:: + While P format uses 32-bit signed integers, the FITS standard does not define + the meaning for negative values. P format indexes from byte 0 to + :math:`2^{31} - 1`. + Depending on the format of the variable arrays (int or float or double) and + the number of rows it might be necessary to use the Q format to allocate enough + heap space. + +Example +------- + +.. + EXAMPLE START + Accessing Variable Length Array Columns in FITS Tables + +This example shows a variable length array field of data type int16:: + + >>> filename = fits.util.get_testdata_filepath('variable_length_table.fits') + >>> hdul = fits.open(filename) + >>> hdul[1].header['tform1'] + 'PI(3)' + >>> print(hdul[1].data.field(0)) + [array([45, 56], dtype=int16) array([11, 12, 13], dtype=int16)] + >>> hdul.close() + +In this field the first row has one element, the second row has two elements, +etc. Accessing variable length fields is almost identical to regular fields, +except that operations on the whole field simultaneously are usually not +possible. A user has to process the field row by row as though they are +independent arrays. + +.. + EXAMPLE END Creating a Variable Length Array Table -"""""""""""""""""""""""""""""""""""""" +-------------------------------------- Creating a variable length table is almost identical to creating a regular table. The only difference is in the creation of field definitions which are variable length arrays. First, the data type specification will need the 'P' letter, and secondly, the field data must be an objects array (as included in -the numpy module). Here is an example of creating a table with two fields, one -is regular and the other variable length array. +the ``numpy`` module). + +Example +^^^^^^^ + +.. + EXAMPLE START + Creating a Variable Length Array Column in a FITS Table + +Here is an example of creating a table with two fields; one is regular and the +other a variable length array:: + + >>> col1 = fits.Column( + ... name='var', format='PI()', + ... array=np.array([[45, 56], [11, 12, 13]], dtype=np.object_)) + >>> col2 = fits.Column(name='xyz', format='2I', array=[[11, 3], [12, 4]]) + >>> hdu = fits.BinTableHDU.from_columns([col1, col2]) + >>> data = hdu.data + >>> data # doctest: +SKIP + FITS_rec([([45, 56], [11, 3]), ([11, 12, 13], [12, 4])], + dtype=(numpy.record, [('var', '>> hdu.writeto('variable_length_table.fits') + >>> with fits.open('variable_length_table.fits') as hdul: + ... print(repr(hdul[1].header)) + XTENSION= 'BINTABLE' / binary table extension + BITPIX = 8 / array data type + NAXIS = 2 / number of array dimensions + NAXIS1 = 12 / length of dimension 1 + NAXIS2 = 2 / length of dimension 2 + PCOUNT = 10 / number of group parameters + GCOUNT = 1 / number of groups + TFIELDS = 2 / number of table fields + TTYPE1 = 'var ' + TFORM1 = 'PI(3) ' + TTYPE2 = 'xyz ' + TFORM2 = '2I ' - >>> from astropy.io import fits - >>> import numpy as np - >>> c1 = fits.Column(name='var', format='PJ()', - ... array=np.array([[45., 56] - [11, 12, 13]], - ... dtype=np.object)) - >>> c2 = fits.Column(name='xyz', format='2I', array=[[11, 3], [12, 4]]) - # the rest is the same as a regular table. - # Create the table HDU - >>> tbhdu = fits.new_table([c1, c2]) - >>> print tbhdu.data - FITS_rec([(array([45, 56]), array([11, 3], dtype=int16)), - (array([11, 12, 13]), array([12, 4], dtype=int16))], - dtype=[('var', '>> tbhdu.writeto('var_table.fits') - >>> hdu = fits.open('var_table.fits') - # Note that heap info is taken care of (PCOUNT) when written to FITS file. - >>> print hdu[1].header.ascardlist() - XTENSION= 'BINTABLE' / binary table extension - BITPIX = 8 / array data type - NAXIS = 2 / number of array dimensions - NAXIS1 = 12 / length of dimension 1 - NAXIS2 = 2 / length of dimension 2 - PCOUNT = 20 / number of group parameters - GCOUNT = 1 / number of groups - TFIELDS = 2 / number of table fields - TTYPE1 = 'var ' - TFORM1 = 'PJ(3) ' - TTYPE2 = 'xyz ' - TFORM2 = '2I ' +.. + EXAMPLE END +.. _random-groups: Random Access Groups -^^^^^^^^^^^^^^^^^^^^ +==================== Another less familiar data structure supported by the FITS standard is the random access group. This convention was established before the binary table extension was introduced. In most cases its use can now be superseded by the binary table. It is mostly used in radio interferometry. -Like Primary HDUs, a Random Access Group HDU is always the first HDU of a FITS +Like primary HDUs, a Random Access Group HDU is always the first HDU of a FITS file. Its data has one or more groups. Each group may have any number (including 0) of parameters, together with an image. The parameters and the image have the same data type. -All groups in the same HDU have the same data structure, i.e. same data type +All groups in the same HDU have the same data structure, that is, same data type (specified by the keyword BITPIX, as in image HDU), same number of parameters (specified by PCOUNT), and the same size and shape (specified by NAXISn keywords) of the image data. The number of groups is specified by GCOUNT and the keyword NAXIS1 is always 0. Thus the total data size for a Random Access -Group HDU is +Group HDU is: .. parsed-literal:: @@ -199,313 +265,438 @@ Group HDU is Header and Summary -"""""""""""""""""" +------------------ Accessing the header of a Random Access Group HDU is no different from any -other HDU. Just use the .header attribute. +other HDU; you can use the .header attribute. The content of the HDU can similarly be summarized by using the -:meth:`HDUList.info` method: +:meth:`HDUList.info` method:: - >>> f = fits.open('random_group.fits') - >>> print f[0].header['groups'] + >>> filename = fits.util.get_testdata_filepath('group.fits') + >>> hdul = fits.open(filename) + >>> hdul[0].header['groups'] True - >>> print f[0].header['gcount'] - 7956 - >>> print f[0].header['pcount'] - 6 - >>> f.info() - Filename: random_group.fits - No. Name Type Cards Dimensions Format - 0 AN GroupsHDU 158 (3, 4, 1, 1, 1) Float32 7956 Groups - 6 Parameters + >>> hdul[0].header['gcount'] + 10 + >>> hdul[0].header['pcount'] + 3 + >>> hdul.info() + Filename: ...group.fits + No. Name Ver Type Cards Dimensions Format + 0 PRIMARY 1 GroupsHDU 15 (5, 3, 1, 1) float32 10 Groups 3 Parameters Data: Group Parameters -"""""""""""""""""""""" +---------------------- -The data part of a random access group HDU is, like other HDUs, in the +The data part of a Random Access Group HDU is, like other HDUs, in the ``.data`` attribute. It includes both parameter(s) and image array(s). -1. show the data in 100th group, including parameters and data +Examples +^^^^^^^^ + +.. + EXAMPLE START + Group Parameters in Random Access Group HDUs - >>> print f[0].data[99] - (-8.1987486677035799e-06, 1.2010923615889215e-05, - -1.011189139244005e-05, 258.0, 2445728., 0.10, array([[[[[ 12.4308672 , - 0.56860745, 3.99993873], - [ 12.74043655, 0.31398511, 3.99993873], - [ 0. , 0. , 3.99993873], - [ 0. , 0. , 3.99993873]]]]], dtype=float32)) +To show the contents of the third group, including parameters and data:: -The data first lists all the parameters, then the image array, for the + >>> hdul[0].data[2] # doctest: +FLOAT_CMP + (np.float32(2.1), np.float32(42.0), np.float32(42.0), array([[[[30., 31., 32., 33., 34.], + [35., 36., 37., 38., 39.], + [40., 41., 42., 43., 44.]]]], dtype='>f4')) + +The data first lists all of the parameters, then the image array, for the specified group(s). As a reminder, the image data in this file has the shape of -(1,1,1,4,3) in Python or C convention, or (3,4,1,1,1) in IRAF or FORTRAN +(1,1,1,4,3) in Python or C convention, or (3,4,1,1,1) in IRAF or Fortran convention. To access the parameters, first find out what the parameter names are, with the -.parnames attribute: +``.parnames`` attribute:: - >>> f[0].data.parnames # get the parameter names - ['uu--', 'vv--', 'ww--', 'baseline', 'date', 'date'] + >>> hdul[0].data.parnames # get the parameter names + ['abc', 'xyz', 'xyz'] The group parameter can be accessed by the :meth:`~GroupData.par` method. Like the table :meth:`~FITS_rec.field` method, the argument can be either index or -name: - - >>> print f[0].data.par(0)[99] # Access group parameter by name or by index - -8.1987486677035799e-06 - >>> print f[0].data.par('uu--')[99] - -8.1987486677035799e-06 - -Note that the parameter name 'date' appears twice. This is a feature in the -random access group, and it means to add the values together. Thus: - - >>> - # Duplicate group parameter name 'date' for 5th and 6th parameters - >>> print f[0].data.par(4)[99] - 2445728.0 - >>> print f[0].data.par(5)[99] - 0.10 - # When accessed by name, it adds the values together if the name is shared - # by more than one parameter - >>> print f[0].data.par('date')[99] - 2445728.10 - -The :meth`~GroupData.par` is a method for either the entire data object or one +name:: + + >>> hdul[0].data.par(0)[8] # Access group parameter by name or by index # doctest: +FLOAT_CMP + np.float32(8.1) + >>> hdul[0].data.par('abc')[8] # doctest: +FLOAT_CMP + np.float32(8.1) + +Note that the parameter name 'xyz' appears twice. This is a feature in the +random access group, and it means to add the values together. Thus:: + + >>> hdul[0].data.parnames # get the parameter names + ['abc', 'xyz', 'xyz'] + >>> hdul[0].data.par(1)[8] # Duplicate parameter name 'xyz' + np.float32(42.0) + >>> hdul[0].data.par(2)[8] + np.float32(42.0) + >>> # When accessed by name, it adds the values together if the name is + >>> # shared by more than one parameter + >>> hdul[0].data.par('xyz')[8] + np.float64(84.0) + +The :meth:`~GroupData.par` is a method for either the entire data object or one data item (a group). So there are two possible ways to get a group parameter for a certain group, this is similar to the situation in table data (with its -:meth:`~FITS_rec.field` method): +:meth:`~FITS_rec.field` method):: - >>> - # Access group parameter by selecting the row (group) number last - >>> print f[0].data.par(0)[99] - -8.1987486677035799e-06 - # Access group parameter by selecting the row (group) number first - >>> print f[0].data[99].par(0) - -8.1987486677035799e-06 + >>> hdul[0].data.par(0)[8] # doctest: +FLOAT_CMP + np.float32(8.1) + >>> hdul[0].data[8].par(0) # doctest: +FLOAT_CMP + np.float32(8.1) On the other hand, to modify a group parameter, we can either assign the new value directly (if accessing the row/group number last) or use the -:meth:`~GroupData.setpar` method (if accessing the row/group number first). The -method :meth:`~GroupData.setpar` is also needed for updating by name if the -parameter is shared by more than one parameters: - - >>> - # Update group parameter when selecting the row (group) number last - >>> f[0].data.par(0)[99] = 99. - >>> - # Update group parameter when selecting the row (group) number first - >>> f[0].data[99].setpar(0, 99.) # or setpar('uu--', 99.) - >>> - # Update group parameter by name when the name is shared by more than - # one parameters, the new value must be a tuple of constants or sequences - >>> f[0].data[99].setpar('date', (2445729., 0.3)) - >>> f[0].data[:3].setpar('date', (2445729., [0.11, 0.22, 0.33])) - >>> f[0].data[:3].par('date') - array([ 2445729.11 , 2445729.22 , 2445729.33000001]) - +:meth:`~Group.setpar` method (if accessing the row/group number first). The +method :meth:`~Group.setpar` is also needed for updating by name if the +parameter is shared by more than one parameters:: + + >>> # Update group parameter when selecting the row (group) number last + >>> hdul[0].data.par(0)[8] = 99. + >>> # Update group parameter when selecting the row (group) number first + >>> hdul[0].data[8].setpar(0, 99.) # or: + >>> hdul[0].data[8].setpar('abc', 99.) + >>> # Update group parameter by name when the name is shared by more than + >>> # one parameters, the new value must be a tuple of constants or + >>> # sequences + >>> hdul[0].data[8].setpar('xyz', (2445729., 0.3)) + >>> hdul[0].data[8:].par('xyz') # doctest: +FLOAT_CMP + array([2.44572930e+06, 8.40000000e+01]) + +.. + EXAMPLE END Data: Image Data -"""""""""""""""" +---------------- The image array of the data portion is accessible by the -:attr:`~GroupData.data` attribute of the data object. A numpy array is -returned: +:attr:`~GroupData.data` attribute of the data object. A ``numpy`` array is +returned:: - >>> print f[0].data.data[99] - array([[[[[ 12.4308672 , 0.56860745, 3.99993873], - [ 12.74043655, 0.31398511, 3.99993873], - [ 0. , 0. , 3.99993873], - [ 0. , 0. , 3.99993873]]]]], type=float32) + >>> print(hdul[0].data.data[8]) # doctest: +FLOAT_CMP + [[[[120. 121. 122. 123. 124.] + [125. 126. 127. 128. 129.] + [130. 131. 132. 133. 134.]]]] + >>> hdul.close() Creating a Random Access Group HDU -"""""""""""""""""""""""""""""""""" - -To create a random access group HDU from scratch, use :meth:`GroupData` to -encapsulate the data into the group data structure, and use :meth:`GroupsHDU` -to create the HDU itself: - - >>> - # Create the image arrays. The first dimension is the number of groups. - >>> imdata = numpy.arange(100., shape=(10, 1, 1, 2, 5)) - # Next, create the group parameter data, we'll have two parameters. - # Note that the size of each parameter's data is also the number of groups. - # A parameter's data can also be a numeric constant. - >>> pdata1 = numpy.arange(10) + 0.1 +---------------------------------- + +To create a Random Access Group HDU from scratch, use :class:`GroupData` to +encapsulate the data into the group data structure, and use :class:`GroupsHDU` +to create the HDU itself. + +Example +^^^^^^^ + +.. + EXAMPLE START + Creating a Random Access Group HDU in a FITS File + +To create a Random Access Group HDU:: + + >>> # Create the image arrays. The first dimension is the number of groups. + >>> imdata = np.arange(150.0).reshape(10, 1, 1, 3, 5) + >>> # Next, create the group parameter data, we'll have two parameters. + >>> # Note that the size of each parameter's data is also the number of + >>> # groups. + >>> # A parameter's data can also be a numeric constant. + >>> pdata1 = np.arange(10) + 0.1 >>> pdata2 = 42 - # Create the group data object, put parameter names and parameter data - # in lists assigned to their corresponding arguments. - # If the data type (bitpix) is not specified, the data type of the image - # will be used. - >>> x = fits.GroupData(imdata, parnames=['abc', 'xyz'], - ... pardata=[pdata1, pdata2], bitpix=-32) - # Now, create the GroupsHDU and write to a FITS file. + >>> # Create the group data object, put parameter names and parameter data + >>> # in lists assigned to their corresponding arguments. + >>> # If the data type (bitpix) is not specified, the data type of the + >>> # image will be used. + >>> x = fits.GroupData(imdata, bitpix=-32, + ... parnames=['abc', 'xyz', 'xyz'], + ... pardata=[pdata1, pdata2, pdata2]) + >>> # Now, create the GroupsHDU and write to a FITS file. >>> hdu = fits.GroupsHDU(x) >>> hdu.writeto('test_group.fits') - >>> print hdu.header.ascardlist()[:] - SIMPLE = T / conforms to FITS standard - BITPIX = -32 / array data type - NAXIS = 5 / number of array dimensions - NAXIS1 = 0 - NAXIS2 = 5 - NAXIS3 = 2 - NAXIS4 = 1 - NAXIS5 = 1 - EXTEND = T - GROUPS = T / has groups - PCOUNT = 2 / number of parameters - GCOUNT = 10 / number of groups - PTYPE1 = 'abc ' - PTYPE2 = 'xyz ' - >>> print hdu.data[:2] - FITS_rec[ - (0.10000000149011612, 42.0, array([[[[ 0., 1., 2., 3., 4.], - [ 5., 6., 7., 8., 9.]]]], dtype=float32)), - (1.1000000238418579, 42.0, array([[[[ 10., 11., 12., 13., 14.], - [ 15., 16., 17., 18., 19.]]]], dtype=float32)) - ] - + >>> hdu.header + SIMPLE = T / conforms to FITS standard + BITPIX = -32 / array data type + NAXIS = 5 / number of array dimensions + NAXIS1 = 0 + NAXIS2 = 5 + NAXIS3 = 3 + NAXIS4 = 1 + NAXIS5 = 1 + EXTEND = T + GROUPS = T / has groups + PCOUNT = 3 / number of parameters + GCOUNT = 10 / number of groups + PTYPE1 = 'abc ' + PTYPE2 = 'xyz ' + PTYPE3 = 'xyz ' + >>> data = hdu.data + >>> hdu.data # doctest: +SKIP + GroupData([ (0.1 , 42., 42., [[[[ 0., 1., 2., 3., 4.], [ 5., 6., 7., 8., 9.], [ 10., 11., 12., 13., 14.]]]]), + (1.10000002, 42., 42., [[[[ 15., 16., 17., 18., 19.], [ 20., 21., 22., 23., 24.], [ 25., 26., 27., 28., 29.]]]]), + (2.0999999 , 42., 42., [[[[ 30., 31., 32., 33., 34.], [ 35., 36., 37., 38., 39.], [ 40., 41., 42., 43., 44.]]]]), + (3.0999999 , 42., 42., [[[[ 45., 46., 47., 48., 49.], [ 50., 51., 52., 53., 54.], [ 55., 56., 57., 58., 59.]]]]), + (4.0999999 , 42., 42., [[[[ 60., 61., 62., 63., 64.], [ 65., 66., 67., 68., 69.], [ 70., 71., 72., 73., 74.]]]]), + (5.0999999 , 42., 42., [[[[ 75., 76., 77., 78., 79.], [ 80., 81., 82., 83., 84.], [ 85., 86., 87., 88., 89.]]]]), + (6.0999999 , 42., 42., [[[[ 90., 91., 92., 93., 94.], [ 95., 96., 97., 98., 99.], [100., 101., 102., 103., 104.]]]]), + (7.0999999 , 42., 42., [[[[105., 106., 107., 108., 109.], [110., 111., 112., 113., 114.], [115., 116., 117., 118., 119.]]]]), + (8.10000038, 42., 42., [[[[120., 121., 122., 123., 124.], [125., 126., 127., 128., 129.], [130., 131., 132., 133., 134.]]]]), + (9.10000038, 42., 42., [[[[135., 136., 137., 138., 139.], [140., 141., 142., 143., 144.], [145., 146., 147., 148., 149.]]]])], + dtype=(numpy.record, [('abc', '>> f = fits.open('compressed_image.fits') - >>> print f[1].header - XTENSION= 'IMAGE ' / extension type - BITPIX = 16 / array data type - NAXIS = 2 / number of array dimensions - NAXIS1 = 512 / length of data axis - NAXIS2 = 512 / length of data axis - PCOUNT = 0 / number of parameters - GCOUNT = 1 / one data group (required keyword) - EXTNAME = 'COMPRESSED' / name of this binary table extension +.. + EXAMPLE START + Accessing Compressed FITS Image HDU Headers -The contents of the corresponding binary table HDU may be accessed using the -hidden ``._header`` attribute. However, all user interface with the HDU header -should be accomplished through the image header (the ``.header`` attribute). +The content of the decompressed HDU header may be accessed using the ``.header`` attribute:: - >>> f = fits.open('compressed_image.fits') - >>> print f[1]._header - XTENSION= 'BINTABLE' / binary table extension - BITPIX = 8 / 8-bit bytes - NAXIS = 2 / 2-dimensional binary table - NAXIS1 = 8 / width of table in bytes - NAXIS2 = 512 / number of rows in table - PCOUNT = 157260 / size of special data area - GCOUNT = 1 / one data group (required keyword) - TFIELDS = 1 / number of fields in each row - TTYPE1 = 'COMPRESSED_DATA' / label for field 1 - TFORM1 = '1PB(384)' / data format of field: variable length array - ZIMAGE = T / extension contains compressed image - ZBITPIX = 16 / data type of original image - ZNAXIS = 2 / dimension of original image - ZNAXIS1 = 512 / length of original image axis - ZNAXIS2 = 512 / length of original image axis - ZTILE1 = 512 / size of tiles to be compressed - ZTILE2 = 1 / size of tiles to be compressed - ZCMPTYPE= 'RICE_1 ' / compression algorithm - ZNAME1 = 'BLOCKSIZE' / compression block size - ZVAL1 = 32 / pixels per block - EXTNAME = 'COMPRESSED' / name of this binary table extension + >>> filename = fits.util.get_testdata_filepath('compressed_image.fits') + + >>> hdul = fits.open(filename) + >>> hdul[1].header + XTENSION= 'IMAGE ' / Image extension + BITPIX = 16 / data type of original image + NAXIS = 2 / dimension of original image + NAXIS1 = 10 / length of original image axis + NAXIS2 = 10 / length of original image axis + PCOUNT = 0 / number of parameters + GCOUNT = 1 / number of groups The contents of the HDU can be summarized by using either the :func:`info` -convenience function or method: - - >>> fits.info('compressed_image.fits') - Filename: compressed_image.fits - No. Name Type Cards Dimensions Format - 0 PRIMARY PrimaryHDU 6 () int16 - 1 COMPRESSED CompImageHDU 52 (512, 512) int16 - >>> - >>> f = fits.open('compressed_image.fits') - >>> f.info() - Filename: compressed_image.fits - No. Name Type Cards Dimensions Format - 0 PRIMARY PrimaryHDU 6 () int16 - 1 COMPRESSED CompImageHDU 52 (512, 512) int16 - >>> +convenience function or method:: + + >>> fits.info(filename) + Filename: ...compressed_image.fits + No. Name Ver Type Cards Dimensions Format + 0 PRIMARY 1 PrimaryHDU 4 () + 1 COMPRESSED_IMAGE 1 CompImageHDU 7 (10, 10) int16 + + >>> hdul.info() + Filename: ...compressed_image.fits + No. Name Ver Type Cards Dimensions Format + 0 PRIMARY 1 PrimaryHDU 4 () + 1 COMPRESSED_IMAGE 1 CompImageHDU 7 (10, 10) int16 +.. + EXAMPLE END Data -"""" +---- As with the header, the data of a compressed image HDU appears to the user as -standard uncompressed image data. The actual data is stored in the fits file -as Binary Table data containing at least one column (COMPRESSED_DATA). Each -row of this variable-length column contains the byte stream that was generated -as a result of compressing the corresponding image tile. Several optional -columns may also appear. These include, UNCOMPRESSED_DATA to hold the -uncompressed pixel values for tiles that cannot be compressed, ZSCALE and ZZERO -to hold the linear scale factor and zero point offset which may be needed to -transform the raw uncompressed values back to the original image pixel values, -and ZBLANK to hold the integer value used to represent undefined pixels (if -any) in the image. - -The content of the HDU data may be accessed using the ``.data`` attribute: - - >>> f = fits.open('compressed_image.fits') - >>> f[1].data - array([[38, 43, 35, ..., 45, 43, 41], - [36, 41, 37, ..., 42, 41, 39], - [38, 45, 37, ..., 42, 35, 43], - ..., - [49, 52, 49, ..., 41, 35, 39], - [57, 52, 49, ..., 40, 41, 43], - [53, 57, 57, ..., 39, 35, 45]], dtype=int16) +standard uncompressed image data. The actual data is stored in the FITS file +as binary table data containing at least one column (COMPRESSED_DATA). Each +row of this variable length column contains the byte stream that was generated +as a result of compressing the corresponding image tile. Several optional +columns may also appear. These include GZIP_COMPRESSED_DATA to hold the +gzip-compressed data for tiles that cannot be compressed by the selected +algorithm, as well as ZSCALE and ZZERO to hold the linear scale factor and zero +point offset which may be needed to transform the raw uncompressed values back +to the original image pixel values, and ZBLANK to hold the integer value used to +represent undefined pixels (if any) in the image. + +Example +^^^^^^^ + +.. + EXAMPLE START + Accessing Compressed FITS Image HDU Data + +The contents of the uncompressed HDU data may be accessed using the ``.data`` +attribute:: + + >>> hdul[1].data + array([[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [10, 11, 12, 13, 14, 15, 16, 17, 18, 19], + [20, 21, 22, 23, 24, 25, 26, 27, 28, 29], + [30, 31, 32, 33, 34, 35, 36, 37, 38, 39], + [40, 41, 42, 43, 44, 45, 46, 47, 48, 49], + [50, 51, 52, 53, 54, 55, 56, 57, 58, 59], + [60, 61, 62, 63, 64, 65, 66, 67, 68, 69], + [70, 71, 72, 73, 74, 75, 76, 77, 78, 79], + [80, 81, 82, 83, 84, 85, 86, 87, 88, 89], + [90, 91, 92, 93, 94, 95, 96, 97, 98, 99]], dtype=int16) + >>> hdul.close() + +The compressed data can be accessed via the ``.compressed_data`` attribute, but +this rarely needs be accessed directly. It may be useful for performing direct +copies of the compressed data without needing to decompress it first. + +.. + EXAMPLE END Creating a Compressed Image HDU -""""""""""""""""""""""""""""""" +------------------------------- -To create a compressed image HDU from scratch, simply construct a +To create a compressed image HDU from scratch, construct a :class:`CompImageHDU` object from an uncompressed image data array and its -associated image header. From there, the HDU can be treated just like any +associated image header. From there, the HDU can be treated just like any other image HDU. +Example +^^^^^^^ + +.. + EXAMPLE START + Creating a Compressed FITS Image HDU + +To create a compressed image HDU:: + + >>> imageData = np.arange(100).astype('i2').reshape(10, 10) + >>> imageHeader = fits.Header() >>> hdu = fits.CompImageHDU(imageData, imageHeader) >>> hdu.writeto('compressed_image.fits') - >>> The API documentation for the :class:`CompImageHDU` initializer method describes the possible options for constructing a :class:`CompImageHDU` object. + +.. + EXAMPLE END + + +Supported Integer Data Types +---------------------------- + +Not every compression algorithm can be used with every integer data type. The +table below summarizes which combinations work, including the cases where +``astropy`` accepts the input only when the values lie within a more +restricted range. + +.. list-table:: + :header-rows: 1 + :stub-columns: 1 + + * - Compression + - ``int16`` + - ``int32`` + - ``int64`` + - ``uint8`` + - ``uint16`` + - ``uint32`` + - ``uint64`` + * - ``GZIP_1`` + - ✅ + - ✅ + - âš ī¸ [1]_ + - ✅ + - ✅ + - ✅ + - âš ī¸ [1]_ + * - ``GZIP_2`` + - ✅ + - ✅ + - âš ī¸ [1]_ + - ✅ + - ✅ + - ✅ + - âš ī¸ [1]_ + * - ``RICE_1`` + - ✅ + - ✅ + - 🟡 [2]_ + - ✅ + - ✅ + - ✅ + - 🟡 [2]_ + * - ``HCOMPRESS_1`` + - ✅ + - ✅ + - 🟡 [2]_ + - ✅ + - ✅ + - ✅ + - 🟡 [2]_ + * - ``PLIO_1`` + - 🟡 [3]_ + - 🟡 [3]_ + - 🟡 [2]_ [3]_ + - ✅ + - ❌ [4]_ + - ❌ [4]_ + - ❌ [4]_ + * - ``NOCOMPRESS`` + - ✅ + - ✅ + - ✅ + - ✅ + - ✅ + - ✅ + - ✅ + +Legend: + +* ✅ Full support: any value within the type's range round-trips losslessly. +* 🟡 Partial support: works only when input values satisfy the numeric + restriction in the corresponding footnote; a ``ValueError`` is raised + otherwise. +* âš ī¸ Caveat: round-trips correctly within ``astropy``, but the resulting + file may not be readable by other FITS libraries (see footnote). +* ❌ Not supported: writing the data raises a ``ValueError``. + +.. [1] ``astropy`` writes a standards-compliant file, but ``cfitsio`` and + tools built on top of it (including ``funpack``, ``fitsio``, and DS9) do + not currently support reading 64-bit integer images compressed with + ``GZIP_1`` or ``GZIP_2``. The file round-trips correctly when read by + ``astropy`` itself. + +.. [2] 64-bit integer input is converted to a 32-bit type on write. The + conversion succeeds only if every input value fits in the corresponding + 32-bit range: ``[-2**31, 2**31 - 1]`` for signed and ``[0, 2**32 - 1]`` + for unsigned. Otherwise a ``ValueError`` is raised. When the conversion + succeeds an ``AstropyUserWarning`` is emitted to signal the precision + change. + +.. [3] ``PLIO_1`` is designed for pixel masks and supports only non-negative + integer values up to ``2**24 - 1`` (``16777215``). Negative values or + values above this limit cause a ``ValueError`` at write time. For + ``int64`` input both this restriction and the 32-bit conversion in + footnote [2]_ apply. + +.. [4] ``PLIO_1`` cannot store unsigned 16-, 32-, or 64-bit integers. Use + ``RICE_1``, ``HCOMPRESS_1``, ``GZIP_1``, or ``GZIP_2`` for unsigned data + that does not fit in ``uint8``. diff --git a/docs/io/fits/usage/verification.rst b/docs/io/fits/usage/verification.rst index bbb1c7dcf16e..6ab9beb8c199 100644 --- a/docs/io/fits/usage/verification.rst +++ b/docs/io/fits/usage/verification.rst @@ -1,13 +1,15 @@ .. currentmodule:: astropy.io.fits +.. _fits_io_verification: + Verification ------------- +************ -Astropy has built in a flexible scheme to verify FITS data being conforming to -the FITS standard. The basic verification philosophy in Astropy is to be -tolerant in input and strict in output. +``astropy`` has built in a flexible scheme to verify FITS data conforming to +the FITS standard. The basic verification philosophy in ``astropy`` is to be +tolerant with input and strict with output. -When Astropy reads a FITS file which is not conforming to FITS standard, it +When ``astropy`` reads a FITS file which does not conform to FITS standard, it will not raise an error and exit. It will try to make the best educated interpretation and only gives up when the offending data is accessed and no unambiguous interpretation can be reached. @@ -19,19 +21,19 @@ not be held up because of a minor standard violation. FITS Standard -^^^^^^^^^^^^^ +============= Since FITS standard is a "loose" standard, there are many places the violation can occur and to enforce them all will be almost impossible. It is not uncommon for major observatories to generate data products which are not 100% FITS -compliant. Some observatories have also developed their own sub-standard -(dialect?) and some of these become so prevalent that they become de facto +compliant. Some observatories have also developed their own nonstandard +dialect and some of these are so prevalent that they have become de facto standards. Examples include the long string value and the use of the CONTINUE card. The violation of the standard can happen at different levels of the data -structure. Astropy's verification scheme is developed on these hierarchical -levels. Here are the 3 Astropy verification levels: +structure. ``astropy``'s verification scheme is developed on these hierarchical +levels. Here are the three ``astropy`` verification levels: 1. The HDU List @@ -39,33 +41,38 @@ levels. Here are the 3 Astropy verification levels: 3. Each Card in the HDU Header -These three levels correspond to the three categories of PyFITS objects: -:class:`HDUList`, any HDU (e.g. :class:`PrimaryHDU`, :class:`ImageHDU`, etc.), +These three levels correspond to the three categories of objects: +:class:`HDUList`, any HDU (e.g., :class:`PrimaryHDU`, :class:`ImageHDU`, etc.), and :class:`Card`. They are the only objects having the ``verify()`` method. -Most other classes in astropy.io.fits do not have a ``verify()`` method. +Most other classes in `astropy.io.fits` do not have a ``verify()`` method. If ``verify()`` is called at the HDU List level, it verifies standard compliance at all three levels, but a call of ``verify()`` at the Card level -will only check the compliance of that Card. Since Astropy is tolerant when +will only check the compliance of that Card. Since ``astropy`` is tolerant when reading a FITS file, no ``verify()`` is called on input. On output, ``verify()`` is called with the most restrictive option as the default. Verification Options -^^^^^^^^^^^^^^^^^^^^ +==================== -There are 5 options for all verify(option) calls in Astropy. In addition, they -available for the ``output_verify`` argument of the following methods: -``close()``, ``writeto()``, and ``flush()``. In these cases, they are passed to -a ``verify()`` call within these methods. The 5 options are: +There are several options accepted by all verify(option) calls in ``astropy``. +In addition, they available for the ``output_verify`` argument of the following +methods: ``close()``, ``writeto()``, and ``flush()``. In these cases, they are +passed to a ``verify()`` call within these methods. The available options are: **exception** -This option will raise an exception, if any FITS standard is violated. This is -the default option for output (i.e. when ``writeto()``, ``close()``, or -``flush()`` is called. If a user wants to overwrite this default on output, the +This option will raise an exception if any FITS standard is violated. This is +the default option for output (i.e., when ``writeto()``, ``close()``, or +``flush()`` is called). If a user wants to overwrite this default on output, the other options listed below can be used. +**warn** + +This option is the same as the ignore option but will send warning messages. It +will not try to fix any FITS standard violations whether fixable or not. + **ignore** This option will ignore any FITS standard violation. On output, it will write @@ -74,11 +81,11 @@ to the FITS standard. The ignore option is useful in the following situations: -1. An input FITS file with non-standard formatting is read and the user wants - to copy or write out to an output file. The non-standard formatting will be +1. An input FITS file with nonstandard formatting is read and the user wants + to copy or write out to an output file. The nonstandard formatting will be preserved in the output file. -2. A user wants to create a non-standard FITS file on purpose, possibly for +2. A user wants to create a nonstandard FITS file on purpose, possibly for testing or consistency. No warning message will be printed out. This is like a silent warning option @@ -89,7 +96,7 @@ No warning message will be printed out. This is like a silent warning option This option will try to fix any FITS standard violations. It is not always possible to fix such violations. In general, there are two kinds of FITS standard violations: fixable and non-fixable. For example, if a keyword has a -floating number with an exponential notation in lower case 'e' (e.g. 1.23e11) +floating number with an exponential notation in lower case 'e' (e.g., 1.23e11) instead of the upper case 'E' as required by the FITS standard, it is a fixable violation. On the other hand, a keyword name like 'P.I.' is not fixable, since it will not know what to use to replace the disallowed periods. If a violation @@ -97,11 +104,11 @@ is fixable, this option will print out a message noting it is fixed. If it is not fixable, it will throw an exception. The principle behind fixing is to do no harm. For example, it is plausible to -'fix' a Card with a keyword name like 'P.I.' by deleting it, but Astropy will -not take such action to hurt the integrity of the data. +'fix' a Card with a keyword name like 'P.I.' by deleting it, but ``astropy`` +will not take such action to hurt the integrity of the data. -Not all fixes may be the "correct" fix, but at least Astropy will try to make -the fix in such a way that it will not throw off other FITS readers. +Not all fixes may be the "correct" fix, but at least ``astropy`` will try to +make the fix in such a way that it will not throw off other FITS readers. **silentfix** @@ -109,36 +116,47 @@ Same as fix, but will not print out informative messages. This may be useful in a large script where the user does not want excessive harmless messages. If the violation is not fixable, it will still throw an exception. -**warn** +In addition the following combined options are available: -This option is the same as the ignore option but will send warning messages. It -will not try to fix any FITS standard violations whether fixable or not. + * **fix+ignore** + * **fix+warn** + * **fix+exception** + * **silentfix+ignore** + * **silentfix+warn** + * **silentfix+exception** + +These options combine the semantics of the basic options. For example, +``silentfix+exception`` is actually equivalent to just ``silentfix`` in that +fixable errors will be fixed silently, but any unfixable errors will raise an +exception. On the other hand, ``silentfix+warn`` will issue warnings for +unfixable errors, but will stay silent about any fixed errors. Verifications at Different Data Object Levels -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +============================================= -We'll examine what Astropy's verification does at the three different levels: +We will examine what ``astropy``'s verification does at the three different +levels: Verification at HDUList -""""""""""""""""""""""" +----------------------- At the HDU List level, the verification is only for two simple cases: -1. Verify that the first HDU in the HDU list is a Primary HDU. This is a - fixable case. The fix is to insert a minimal Primary HDU into the HDU list. +1. Verify that the first HDU in the HDU list is a primary HDU. This is a + fixable case. The fix is to insert a minimal primary HDU into the HDU list. -2. Verify second or later HDU in the HDU list is not a Primary HDU. Violation - will not be fixable. +2. Verify the second or later HDU in the HDU list is not a primary HDU. + Violation will not be fixable. Verification at Each HDU -"""""""""""""""""""""""" +------------------------ For each HDU, the mandatory keywords, their locations in the header, and their values will be verified. Each FITS HDU has a fixed set of required keywords in -a fixed order. For example, the Primary HDU's header must at least have the +a fixed order. For example, the primary HDU's header must at least have the following keywords: .. parsed-literal:: @@ -148,174 +166,222 @@ following keywords: NAXIS = 0 If any of the mandatory keywords are missing or in the wrong order, the fix -option will fix them: - - >>> hdu.header # has a 'bad' header - SIMPLE = T / - NAXIS = 0 - BITPIX = 8 / - >>> hdu.verify('fix') # fix it - Output verification result: - 'BITPIX' card at the wrong place (card 2). Fixed by moving it to the right - place (card 1). - >>> h.header # voila! - SIMPLE = T / conforms to FITS standard - BITPIX = 8 / array data type - NAXIS = 0 - +option will fix them:: + + >>> from astropy.io import fits + >>> filename = fits.util.get_testdata_filepath('verify.fits') + >>> hdul = fits.open(filename) + >>> hdul[0].header + SIMPLE = T / conforms to FITS standard + NAXIS = 0 / NUMBER OF AXES + BITPIX = 8 / BITS PER PIXEL + >>> hdul[0].verify('fix') # doctest: +SHOW_WARNINGS + VerifyWarning: Verification reported errors: + VerifyWarning: 'BITPIX' card at the wrong place (card 2). + Fixed by moving it to the right place (card 1). + VerifyWarning: Note: astropy.io.fits uses zero-based indexing. + >>> hdul[0].header # voila! + SIMPLE = T / conforms to FITS standard + BITPIX = 8 / BITS PER PIXEL + NAXIS = 0 / NUMBER OF AXES + >>> hdul.close() Verification at Each Card -""""""""""""""""""""""""" +------------------------- The lowest level, the Card, also has the most complicated verification -possibilities. Here is a lit of fixable and not fixable Cards: +possibilities. + +Examples +^^^^^^^^ + +.. + EXAMPLE START + Verification at Each Card in astropy.io.fits + +Here is a list of fixable and not fixable Cards: Fixable Cards: -1. floating point numbers with lower case 'e' or 'd' +1. Floating point numbers with lower case 'e' or 'd':: + + >>> from astropy.io import fits + >>> c = fits.Card.fromstring('FIX1 = 2.1e23') + >>> c.verify('silentfix') + >>> print(c) + FIX1 = 2.1E23 + +2. The equal sign is before column nine in the card image:: -2. the equal sign is before column 9 in the card image + >>> c = fits.Card.fromstring('FIX2= 2') + >>> c.verify('silentfix') + >>> print(c) + FIX2 = 2 -3. string value without enclosing quotes +3. String value without enclosing quotes:: -4. missing equal sign before column 9 in the card image + >>> c = fits.Card.fromstring('FIX3 = string value without quotes') + >>> c.verify('silentfix') + >>> print(c) + FIX3 = 'string value without quotes' -5. space between numbers and E or D in floating point values +4. Missing equal sign before column nine in the card image. -6. unparseable values will be "fixed" as a string +5. Space between numbers and E or D in floating point values:: -Here are some examples of fixable cards: + >>> c = fits.Card.fromstring('FIX5 = 2.4 e 03') + >>> c.verify('silentfix') + >>> print(c) + FIX5 = 2.4E03 - >>> hdu.header[4:] # has a bunch of fixable cards - FIX1 = 2.1e23 - FIX2= 2 - FIX3 = string value without quotes - FIX4 2 - FIX5 = 2.4 e 03 - FIX6 = '2 10 ' - # can still access the values before the fix - >>> hdu.header[5] - 2 - >>> hdu.header['fix4'] - 2 - >>> hdu.header['fix5'] - 2400.0 - >>> hdu.verify('silentfix') - >>> hdu.header[4:] - FIX1 = 2.1E23 - FIX2 = 2 - FIX3 = 'string value without quotes' - FIX4 = 2 - FIX5 = 2.4E03 - FIX6 = '2 10 ' +6. Unparsable values will be "fixed" as a string:: + + >>> c = fits.Card.fromstring('FIX6 = 2 10 ') + >>> c.verify('fix+warn') # doctest: +SHOW_WARNINGS + VerifyWarning: Verification reported errors: + VerifyWarning: Card 'FIX6' is not FITS standard + (invalid value string: '2 10'). + Fixed 'FIX6' card to meet the FITS standard. + VerifyWarning: Note: astropy.io.fits uses zero-based indexing. + >>> print(c) + FIX6 = '2 10 ' Unfixable Cards: -1. illegal characters in keyword name +1. Illegal characters in keyword name. -We'll summarize the verification with a "life-cycle" example: +We will summarize the verification with a "life-cycle" example:: - >>> h = fits.PrimaryHDU() # create a PrimaryHDU - # Try to add an non-standard FITS keyword 'P.I.' (FITS does no allow '.' - # in the keyword), if using the update() method - doesn't work! - >>> h['P.I.'] = 'Hubble' - ValueError: Illegal keyword name 'P.I.' - # Have to do it the hard way (so a user will not do this by accident) - # First, create a card image and give verbatim card content (including - # the proper spacing, but no need to add the trailing blanks) + >>> h = fits.PrimaryHDU() # create a PrimaryHDU + >>> # Try to add an non-standard FITS keyword 'P.I.' (FITS does no allow + >>> # '.' in the keyword), if using the update() method - doesn't work! + >>> h.header['P.I.'] = 'Hubble' # doctest: +SHOW_WARNINGS + VerifyWarning: Keyword name 'P.I.' is greater than 8 characters or + contains characters not allowed by the FITS standard; + a HIERARCH card will be created. + >>> # Have to do it the hard way (so a user will not do this by accident) + >>> # First, create a card image and give verbatim card content (including + >>> # the proper spacing, but no need to add the trailing blanks) >>> c = fits.Card.fromstring("P.I. = 'Hubble'") - # then append it to the header - >>> h.header.append(c) - # Now if we try to write to a FITS file, the default output verification - # will not take it. - >>> h.writeto('pi.fits') - Output verification result: - HDU 0: - Card 4: - Unfixable error: Illegal keyword name 'P.I.' - ...... - raise VerifyError - VerifyError - # Must set the output_verify argument to 'ignore', to force writing a - # non-standard FITS file + >>> h.header.append(c) # then append it to the header + >>> # Now if we try to write to a FITS file, the default output + >>> # verification will not take it. + >>> h.writeto('pi.fits') # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + VerifyError: HDU 0: + Card 5: + Card 'P.I. ' is not FITS standard (equal sign not at column 8). + Illegal keyword name 'P.I. ' + >>> # Must set the output_verify argument to 'ignore', to force writing a + >>> # non-standard FITS file >>> h.writeto('pi.fits', output_verify='ignore') - # Now reading a non-standard FITS file - # astropy.io.fits is magnanimous in reading non-standard FITS files - >>> hdus = fits.open('pi.fits') - >>> hdus[0].header - SIMPLE = T / conforms to FITS standard - BITPIX = 8 / array data type - NAXIS = 0 / number of array dimensions - EXTEND = T - P.I. = 'Hubble' - # even when you try to access the offending keyword, it does NOT complain - >>> hdus[0].header['p.i.'] + >>> # Now reading a non-standard FITS file + >>> # astropy.io.fits is magnanimous in reading non-standard FITS files + >>> hdul = fits.open('pi.fits') + >>> hdul[0].header # doctest: +SHOW_WARNINGS + SIMPLE = T / conforms to FITS standard + BITPIX = 8 / array data type + NAXIS = 0 / number of array dimensions + EXTEND = T + HIERARCH P.I. = 'Hubble ' + P.I. = 'Hubble ' + VerifyWarning: Verification reported errors: + VerifyWarning: Card 'P.I. ' is not FITS standard (equal sign + not at column 8). Fixed 'P.I. ' card to meet the FITS standard. + VerifyWarning: Unfixable error: Illegal keyword name 'P.I. ' + VerifyWarning: Note: astropy.io.fits uses zero-based indexing. + >>> # even when you try to access the offending keyword, it does NOT + >>> # complain + >>> hdul[0].header['p.i.'] 'Hubble' - # But if you want to make sure if there is anything wrong/non-standard, - # use the verify() method - >>> hdus.verify() - Output verification result: - HDU 0: - Card 4: - Unfixable error: Illegal keyword name 'P.I.' - - -Verification using the FITS Checksum Keyword Convention -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + >>> # But if you want to make sure if there is anything wrong/non-standard, + >>> # use the verify() method + >>> hdul.verify() # doctest: +SHOW_WARNINGS + VerifyWarning: Verification reported errors: + VerifyWarning: HDU 0: + VerifyWarning: Card 5: + VerifyWarning: Illegal keyword name 'P.I. ' + VerifyWarning: Note: astropy.io.fits uses zero-based indexing. + >>> hdul.close() + +.. + EXAMPLE END + +Verification Using the FITS Checksum Keyword Convention +======================================================= The North American FITS committee has reviewed the FITS Checksum Keyword -Convention for possible adoption as a FITS Standard. This convention provides -an integrity check on information contained in FITS HDUs. The convention -consists of two header keyword cards: CHECKSUM and DATASUM. The CHECKSUM +Convention for possible adoption as a FITS Standard. This convention provides +an integrity check on information contained in FITS HDUs. The convention +consists of two header keyword cards: CHECKSUM and DATASUM. The CHECKSUM keyword is defined as an ASCII character string whose value forces the 32-bit 1's complement checksum accumulated over all the 2880-byte FITS logical records -in the HDU to equal negative zero. The DATASUM keyword is defined as a +in the HDU to equal negative zero. The DATASUM keyword is defined as a character string containing the unsigned integer value of the 32-bit 1's -complement checksum of the data records in the HDU. Verifying the the +complement checksum of the data records in the HDU. Verifying the accumulated checksum is still equal to negative zero provides a fairly reliable way to determine that the HDU has not been modified by subsequent data processing operations or corrupted while copying or storing the file on physical media. -In order to avoid any impact on performance, by default Astropy will not verify -HDU checksums when a file is opened or generate checksum values when a file is -written. In fact, CHECKSUM and DATASUM cards are automatically removed from -HDU headers when a file is opened, and any CHECKSUM or DATASUM cards are -stripped from headers when a HDU is written to a file. In order to verify the +In order to avoid any impact on performance, by default ``astropy`` will not +verify HDU checksums when a file is opened or generate checksum values when a +file is written. In fact, CHECKSUM and DATASUM cards are automatically removed +from HDU headers when a file is opened, and any CHECKSUM or DATASUM cards are +stripped from headers when an HDU is written to a file. In order to verify the checksum values for HDUs when opening a file, the user must supply the checksum keyword argument in the call to the open convenience function with a value of -True. When this is done, any checksum verification failure will cause a -warning to be issued (via the warnings module). If checksum verification is +True. When this is done, any checksum verification failure will cause a +warning to be issued (via the warnings module). If checksum verification is requested in the open, and no CHECKSUM or DATASUM cards exist in the HDU -header, the file will open without comment. Similarly, in order to output the +header, the file will open without comment. Similarly, in order to output the CHECKSUM and DATASUM cards in an HDU header when writing to a file, the user must supply the checksum keyword argument with a value of True in the call to -the writeto function. It is possible to write only the DATASUM card to the +the ``writeto()`` function. It is possible to write only the DATASUM card to the header by supplying the checksum keyword argument with a value of 'datasum'. -Here are some examples: - - >>> # Open the file pix.fits verifying the checksum values for all HDUs - >>> hdul = fits.open('pix.fits', checksum=True) - >>> - >>> # Open the file in.fits where checksum verification fails for the - >>> # primary HDU - >>> hdul = fits.open('in.fits', checksum=True) - Warning: Checksum verification failed for HDU #0. - >>> - >>> # Create file out.fits containing an HDU constructed from data and - >>> # header containing both CHECKSUM and DATASUM cards. - >>> fits.writeto('out.fits', data, header, checksum=True) - >>> - >>> # Create file out.fits containing all the HDUs in the HDULIST - >>> # hdul with each HDU header containing only the DATASUM card - >>> hdul.writeto('out.fits', checksum='datasum') - >>> - >>> # Create file out.fits containing the HDU hdu with both CHECKSUM - >>> # and DATASUM cards in the header - >>> hdu.writeto('out.fits', checksum=True) - >>> - >>> # Append a new HDU constructed from array data to the end of - >>> # the file existingfile.fits with only the appended HDU - >>> # containing both CHECKSUM and DATASUM cards. - >>> fits.append('existingfile.fits', data, checksum=True) +Examples +-------- + +.. + EXAMPLE START + Verification Using the FITS Checksum Keyword Convention + +To verify the checksum values for HDUs when opening a file:: + + >>> # Open the file checksum.fits verifying the checksum values for all HDUs + >>> filename = fits.util.get_testdata_filepath('checksum.fits') + >>> hdul = fits.open(filename, checksum=True) + >>> hdul.close() + >>> # Open the file in.fits where checksum verification fails + >>> filename = fits.util.get_testdata_filepath('checksum_false.fits') + >>> hdul = fits.open(filename, checksum=True) # doctest: +SHOW_WARNINGS + AstropyUserWarning: Checksum verification failed for HDU ('PRIMARY', 1). + AstropyUserWarning: Datasum verification failed for HDU ('PRIMARY', 1). + AstropyUserWarning: Checksum verification failed for HDU ('RATE', 1). + AstropyUserWarning: Datasum verification failed for HDU ('RATE', 1). + >>> # Create file out.fits containing an HDU constructed from data + >>> # containing both CHECKSUM and DATASUM cards. + >>> data = hdul[0].data + >>> fits.writeto('out.fits', data=data, checksum=True) + >>> hdun = fits.open('out.fits', checksum=True) + >>> hdun.close() + + >>> # Create file out.fits containing all the HDUs in the HDULIST + >>> # hdul with each HDU header containing only the DATASUM card + >>> hdul.writeto('out2.fits', checksum='datasum') + + >>> # Create file out.fits containing the HDU hdu with both CHECKSUM + >>> # and DATASUM cards in the header + >>> hdu = hdul[1] + >>> hdu.writeto('out3.fits', checksum=True) + + >>> # Append a new HDU constructed from array data to the end of + >>> # the file existingfile.fits with only the appended HDU + >>> # containing both CHECKSUM and DATASUM cards. + >>> fits.append('out3.fits', data, checksum=True) + >>> hdul.close() + +.. + EXAMPLE END diff --git a/docs/io/misc.rst b/docs/io/misc.rst new file mode 100644 index 000000000000..587aa3ea2f3a --- /dev/null +++ b/docs/io/misc.rst @@ -0,0 +1,20 @@ +************************************************************** +ECVS, HDF5, Parquet, PyArrow CSV, YAML (`astropy.io.misc`) +************************************************************** + +The `astropy.io.misc` module contains miscellaneous input/output routines that +do not fit elsewhere, and are often used by other ``astropy`` sub-packages. For +example, `astropy.io.misc.hdf5` contains functions to read/write +:class:`~astropy.table.Table` objects from/to HDF5 files, but these +should not be imported directly by users. Instead, users can access this +functionality via the :class:`~astropy.table.Table` class itself (see +:ref:`table_io`). Routines that are intended to be used directly by users are +listed in the `astropy.io.misc` section. + +Reference/API +============= + +.. toctree:: + :maxdepth: 2 + + misc_ref_api diff --git a/docs/io/misc_ref_api.rst b/docs/io/misc_ref_api.rst new file mode 100644 index 000000000000..c9c78752c58c --- /dev/null +++ b/docs/io/misc_ref_api.rst @@ -0,0 +1,15 @@ +Reference/API +************* + +.. automodapi:: astropy.io.misc + +.. automodapi:: astropy.io.misc.ecsv + +.. automodapi:: astropy.io.misc.hdf5 + + +.. automodapi:: astropy.io.misc.parquet + +.. automodapi:: astropy.io.misc.pyarrow.csv + +.. automodapi:: astropy.io.misc.yaml diff --git a/docs/io/overview.rst b/docs/io/overview.rst new file mode 100644 index 000000000000..63fda68c451f --- /dev/null +++ b/docs/io/overview.rst @@ -0,0 +1,34 @@ +Overview of Astropy File I/O +**************************** + +Astropy provides two main interfaces for reading and writing data: + +- :ref:`High-level Unified I/O ` interface that is designed to be consistent + and easy to use. This allows working with different types of data such as + :ref:`tables `, :ref:`images `, and even + :ref:`cosmologies `. +- Low-level I/O sub-packages that are directly responsible for + reading and writing data in specific formats such as :ref:`FITS ` + or :ref:`VOTable `. + +In general we recommend starting with the high-level interface unless you have a +specific need for the low-level interface. + +.. list-table:: Comparison of high-level and low-level interfaces + :widths: 50 50 + :header-rows: 1 + + * - High-level Unified I/O + - Low-level I/O + * - Use ``read()`` and ``write()`` class methods of + output data class, e.g., ``data = QTable.read("data.fits")`` returns a + `~astropy.table.QTable`. + - Interfaces are specific to format, e.g., ``hdus = fits.open("data.fits")`` + returns an `~astropy.io.fits.HDUList`. + * - Read and write entire file at once. + - Support varies, e.g., :ref:`FITS ` has memory-mapped + read access. + * - Automatically determine file format in common cases. + - Specify format explicitly. + * - Help documentation via class method, e.g., ``QTable.read.help("fits")``. + - Help documentation varies, e.g., ``help(fits.open)`` or API docs. diff --git a/docs/io/registry.rst b/docs/io/registry.rst new file mode 100644 index 000000000000..f8ba21a22044 --- /dev/null +++ b/docs/io/registry.rst @@ -0,0 +1,202 @@ +.. _io_registry: + +************************************ +I/O Registry (`astropy.io.registry`) +************************************ + +.. note:: + + The I/O registry is only meant to be used directly by users who want to + define their own custom readers/writers. Users who want to find out more + about what built-in formats are supported by :class:`~astropy.table.Table` + by default should see :ref:`table_io`. + Likewise :ref:`cosmology_io` for built-in formats supported by + :class:`~astropy.cosmology.Cosmology`. + No built-in formats are currently defined for + :class:`~astropy.nddata.NDData`, but this will be added in future. + +Introduction +============ + +The I/O registry is a submodule used to define the readers/writers available +for the :class:`~astropy.table.Table`, :class:`~astropy.nddata.NDData`, +and :class:`~astropy.cosmology.Cosmology` classes. + + +Custom Read/Write Functions +=========================== + +This section demonstrates how to create a custom reader/writer. A reader is +written as a function that can take any arguments except ``format`` (which is +needed when manually specifying the format — see below) and returns an +instance of the :class:`~astropy.table.Table` or +:class:`~astropy.nddata.NDData` classes (or subclasses). + + +Examples +-------- + +.. + EXAMPLE START + Using astropy.io.registry to Create a Custom Reader/Writer + +Here we assume that we are trying to write a reader/writer for the +:class:`~astropy.table.Table` class:: + + >>> from astropy.table import Table + + >>> def my_table_reader(filename, some_option=1): + ... # Read in the table by any means necessary + ... return table # should be an instance of Table + +Such a function can then be registered with the I/O registry:: + + >>> from astropy.io import registry + >>> registry.register_reader('my-table-format', Table, my_table_reader) + +where the first argument is the name of the format, the second argument is the +class that the function returns an instance for, and the third argument is the +reader itself. + +We can then read in a table with:: + + >>> d = Table.read('my_table_file.mtf', format='my-table-format') # doctest: +SKIP + +In practice, it would be nice to have the ``read`` method automatically +identify that this file is in the ``my-table-format`` format, so we can +construct a function that can recognize these files, which we refer to here as +an *identifier* function. + +An identifier function should take a first argument that is a string +which indicates whether the identifier is being called from ``read`` or +``write``, and should then accept an arbitrary number of positional and keyword +arguments via ``*args`` and ``**kwargs``, which are the arguments passed to +the ``read`` method. + +In the above case, we can write a function that only looks at +filenames (but in practice, this function could even look at the first few +bytes of the file, for example). The only requirement for the identifier +function is that it return a boolean indicating whether the input matches that +expected for the format. In our example, we want to automatically recognize +files with filenames ending in ``.mtf`` as being in the ``my-table-format`` +format:: + + >>> import os + >>> + >>> def identify_mtf(origin, *args, **kwargs): + ... return (isinstance(args[0], str) and + ... os.path.splitext(args[0].lower())[1] == '.mtf') + +.. note:: + + Identifier functions should be prepared for arbitrary input — in + particular, the first argument may not be a filename or file object, so it + should not assume that this is the case. + +We then register this identifier function, similarly to the reader function:: + + >>> registry.register_identifier('my-table-format', Table, identify_mtf) + +Having registered this function, we can then do:: + + >>> t = Table.read('catalog.mtf') # doctest: +SKIP + +If multiple formats match the current input, then an exception is +raised, and similarly if no format matches the current input. In that +case, the format should be explicitly given with the ``format=`` +keyword argument. + +It is also possible to create custom writers. To go with our custom reader +above, we can write a custom writer:: + + >>> def my_table_writer(table, filename, overwrite=False): + ... # Write the table out to a file + ... return None # generally None, but other values are not forbidden. + +Writer functions should take a dataset object (either an instance of the +:class:`~astropy.table.Table` or :class:`~astropy.nddata.NDData` +classes or subclasses), and any number of subsequent positional and keyword +arguments — although as for the reader, the ``format`` keyword argument cannot +be used. + +We then register the writer:: + + >>> registry.register_writer('my-table-format', Table, my_table_writer) + +We can write the table out to a file: + +.. testsetup:: + >>> t = Table() + +>>> t.write('catalog_new.mtf', format='my-table-format') + +Since we have already registered the identifier function, we can also do:: + + >>> t.write('catalog_new.mtf') + +.. testcleanup:: + >>> registry.unregister_reader('my-table-format', Table) + >>> registry.unregister_writer('my-table-format', Table) + >>> registry.unregister_identifier('my-table-format', Table) + + +.. + EXAMPLE END + + +Registries, local and default +============================= + +.. versionchanged:: 5.0 + +As of Astropy 5.0 the I/O registry submodule has switched to a class-based +architecture, allowing for the creation of custom registries. +The three supported registry types are read-only -- +:class:`~astropy.io.registry.UnifiedInputRegistry` -- +write-only -- :class:`~astropy.io.registry.UnifiedOutputRegistry` -- +and read/write -- :class:`~astropy.io.registry.UnifiedIORegistry`. + + >>> from astropy.io.registry import UnifiedIORegistry + >>> example_reg = UnifiedIORegistry() + >>> print([m for m in dir(example_reg) if not m.startswith("_")]) + ['available_registries', 'delay_doc_updates', 'get_formats', 'get_reader', + 'get_writer', 'identify_format', 'read', 'register_identifier', + 'register_reader', 'register_writer', 'unregister_identifier', + 'unregister_reader', 'unregister_writer', 'write'] + +For backward compatibility all the methods on this registry have corresponding +module-level functions, which work with the default global read/write registry. +These functions were used in the previous examples. This new registry is empty. + + >>> example_reg.get_formats() +
+ Data class Format Read Write Auto-identify + float64 float64 float64 float64 float64 + ---------- ------- ------- ------- ------------- + +We can register read / write / identify methods with this registry object: + + >>> example_reg.register_reader('my-table-format', Table, my_table_reader) + >>> example_reg.get_formats() +
+ Data class Format Read Write Auto-identify + str5 str15 str3 str2 str2 + ---------- --------------- ---- ----- ------------- + Table my-table-format Yes No No + + +What is the use of a custom registries? + + 1. To make read-only or write-only registries. + 2. To allow for different readers for the same format. + 3. To allow for an object to have different *kinds* of readers and writers. + E.g. |Cosmology| which supports both file I/O and object conversion. + + +Reference/API +============= + +.. toctree:: + :maxdepth: 2 + + registry_ref_api diff --git a/docs/io/registry_ref_api.rst b/docs/io/registry_ref_api.rst new file mode 100644 index 000000000000..6ef75d74d0e1 --- /dev/null +++ b/docs/io/registry_ref_api.rst @@ -0,0 +1,4 @@ +Reference/API +************* + +.. automodapi:: astropy.io.registry diff --git a/docs/io/typing.rst b/docs/io/typing.rst new file mode 100644 index 000000000000..f8d1f07049c4 --- /dev/null +++ b/docs/io/typing.rst @@ -0,0 +1,48 @@ +******************************** +I/O Typing (`astropy.io.typing`) +******************************** + + +``astropy.io`` provides type annotations through the :mod:`astropy.io.typing` module. +These type annotations allow users to specify the expected types of variables, function +parameters, and return values when working with I/O. By using type annotations, +developers can improve code readability, catch potential type-related errors early, and +enable better code documentation and tooling support. + +For example, the following function uses type annotations to specify that the +``filename`` parameter can be any type of path-like object (e.g. a string, byte-string, +or pathlib.Path object). + +.. code-block:: python + + from astropy.io import fits + from astropy.io.typing import PathLike + + def read_fits_file(filename: PathLike) -> fits.HDUList: + return fits.open(filename) + + +The :mod:`astropy.io.typing` module also provides type aliases for file-like objects +that support reading and writing. The following example uses the +:class:`~astropy.io.typing.ReadableFileLike` type alias to specify that the ``fileobj`` +parameter can be any file-like object that supports reading. Using a +:class:`~typing.TypeVar`, the return type of the function is specified to be the same +type as the file-like object can read. + + +.. code-block:: python + + from typing import TypeVar + from astropy.io.typing import ReadableFileLike + + R = TypeVar('R') # type of object returned by fileobj.read() + + def read_from_file(fileobj: ReadableFileLike[R]) -> R: + """Reads from a file-like object and returns the result.""" + return fileobj.read() + + +Reference/API +============= + +.. automodapi:: astropy.io.typing diff --git a/docs/io/unified.rst b/docs/io/unified.rst new file mode 100644 index 000000000000..b9702d269532 --- /dev/null +++ b/docs/io/unified.rst @@ -0,0 +1,70 @@ +.. _table_io: +.. _io-unified: + +High-level Unified File I/O +*************************** + +``astropy`` uses the :ref:`I/O Registry ` to provide a unified interface +for reading and writing data in different formats. For many common cases this will +streamline the process of file I/O and reduce the need to learn the separate details of +all of the low-level I/O packages within ``astropy``. + +.. toctree:: + :maxdepth: 2 + + unified_image + unified_table + +**Overview** + +The fundamental idea for the unified interface is that each data container class such as +`~astropy.table.Table` or `~astropy.nddata.CCDData` has class methods ``read()`` +and ``write()`` that can be used to read and write data. + +The first positional argument to these methods specifies the input or output. In +general the input can be a file name, a file-like object or a URL. For some formats, +most notably the :ref:`table_io_ascii` formats, the input can also be a string or +list of strings representing the data. The output can be a file name or a file-like +object. + +The file format is specified using the ``format`` keyword argument. This is required +unless the format can be uniquely determined from the file name or file content. + +**Example** + +The example below shows how to read a table in the specialized DAOphot format and write +it back to FITS format. Notice that FITS is a format where the interface recognizes the +format automatically from the file name, so the ``format`` argument is not needed. +In this example we use a file that is installed with astropy:: + + >>> from astropy.table import Table + >>> from astropy.utils.data import get_pkg_data_filename + >>> photometry_file = get_pkg_data_filename('data/daophot.dat', + ... package='astropy.io.ascii.tests') + >>> t = Table.read(photometry_file, format='ascii.daophot') + >>> t.write('photometry.fits') # doctest: +IGNORE_WARNINGS + +The FITS writer will issue a few warnings because the units read from the DAOphot +file do not match the FITS conventions, but the data in the file is perfectly fine. + +.. testcleanup:: + + >>> import os + >>> os.remove('photometry.fits') + +Each file format is handled by a specific reader or writer, and each of those +functions will have its own set of arguments. + +**Getting Help** + +To get help on the available arguments for each format, use the ``help()`` method of the +appropriate ``read()`` or ``write()`` class method, e.g., `astropy.table.Table.read`. +In the examples below we do not show the long output: + + >>> from astropy.table import Table + >>> from astropy.nddata import CCDData + >>> CCDData.read.help('fits') # doctest: +IGNORE_OUTPUT + >>> Table.read.help('ascii') # doctest: +IGNORE_OUTPUT + >>> Table.read.help('ascii.latex') # doctest: +IGNORE_OUTPUT + >>> Table.write.help('hdf5') # doctest: +IGNORE_OUTPUT + >>> Table.write.help('csv') # doctest: +IGNORE_OUTPUT diff --git a/docs/io/unified_image.rst b/docs/io/unified_image.rst new file mode 100644 index 000000000000..54cea76a8fcc --- /dev/null +++ b/docs/io/unified_image.rst @@ -0,0 +1,44 @@ +.. _io_unified_image: + +Image Data +========== + +Reading and writing CCD image data in the unified I/O interface is supported +though the :ref:`CCDData class ` using FITS file format: + +.. testsetup:: + >>> from astropy.nddata import CCDData + >>> ccd = CCDData([1, 2, 3], unit='adu') + >>> ccd.write('image.fits') + +:: + + >>> # Read CCD image + >>> from astropy.nddata import CCDData + >>> ccd = CCDData.read('image.fits') + + >>> # Write back CCD image + >>> ccd.write('new_image.fits') + +.. testcleanup:: + >>> del ccd + >>> import os + >>> os.remove('new_image.fits') + +Note that the unit is stored in the ``BUNIT`` keyword in the header on saving, +and is read from the header if it is present. + +Detailed help on the available keyword arguments for reading and writing +can be obtained via the ``help()`` method as follows:: + + >>> from astropy.nddata import CCDData + >>> CCDData.read.help('fits') # Get help on the CCDData FITS reader # doctest: +ELLIPSIS + ========================================= + CCDData.read(format='fits') documentation + ========================================= + ... + >>> CCDData.write.help('fits') # Get help on the CCDData FITS writer # doctest: +ELLIPSIS + ========================================== + CCDData.write(format='fits') documentation + ========================================== + ... diff --git a/docs/io/unified_table.rst b/docs/io/unified_table.rst new file mode 100644 index 000000000000..9840191c7cf1 --- /dev/null +++ b/docs/io/unified_table.rst @@ -0,0 +1,324 @@ +.. _read_write_tables: + +Table Data +========== + +The :class:`~astropy.table.QTable` and :class:`~astropy.table.Table` classes includes two +methods, :meth:`~astropy.table.Table.read` and :meth:`~astropy.table.Table.write`, that +make it possible to read from and write to files. A number of formats are supported (see +`Built-in table readers/writers`_) and new file formats and extensions can be registered +with the :class:`~astropy.table.Table` class (see :ref:`I/O Registry `). + +.. toctree:: + :maxdepth: 1 + :caption: Table Formats + + unified_table_text + unified_table_fits + unified_table_hdf5 + unified_table_parquet + unified_table_votable + +.. + EXAMPLE START + Reading a DAOPhot Table + +To use this interface, first import the :class:`~astropy.table.Table` class, +then call the :class:`~astropy.table.Table` +:meth:`~astropy.table.Table.read` method with the name of the file and +the file format, for instance ``'ascii.daophot'``: + +.. testsetup:: + >>> import os + >>> with open('photometry.dat', 'w') as f: # doctest: +IGNORE_OUTPUT + ... f.write("#N ID XCENTER YCENTER\n") + ... f.write("#U ## pixel pixel \n") + ... f.write("#F %-9d %-10.3f %-10.3f\n") + ... f.write("#\n") + ... f.write("14 138.538 256.405\n") + ... f.write("18 18.114 280.170\n") + >>> from astropy.table import Table + >>> t = Table.read('photometry.dat', format='ascii.daophot') + >>> t.write('table.tex', format='latex') + +>>> from astropy.table import Table +>>> t = Table.read('photometry.dat', format='ascii.daophot') + + +.. testcleanup:: + + >>> os.remove('photometry.dat') + +.. + EXAMPLE END + +.. + EXAMPLE START + Reading a Table directly from the Internet + +It is possible to load tables directly from the Internet using URLs. For +example, download tables from Vizier catalogues in CDS format +(``'ascii.cds'``): + +.. doctest-remote-data:: + + >>> from astropy.table import Table + >>> t = Table.read("ftp://cdsarc.unistra.fr/pub/cats/VII/253/snrs.dat", + ... readme="ftp://cdsarc.unistra.fr/pub/cats/VII/253/ReadMe", + ... format="ascii.cds") + +For certain file formats the format can be automatically detected, for +example, from the filename extension:: + + >>> t = Table.read('table.tex') + +.. + EXAMPLE END + +.. + EXAMPLE START + Writing a LaTeX Table + +For writing a table, the format can be explicitly specified:: + + >>> t.write('some_filename', format='latex') + +.. testcleanup:: + + >>> import pathlib + >>> pathlib.Path.unlink('table.tex') + >>> pathlib.Path.unlink('some_filename') + +As for the :meth:`~astropy.table.Table.read` method, the format may +be automatically identified in some cases. + +The underlying file handler will also automatically detect various +compressed data formats and uncompress them as far as +supported by the Python installation (see +:meth:`~astropy.utils.data.get_readable_fileobj`). + +For writing, you can also specify details about the `Table serialization +methods`_ via the ``serialize_method`` keyword argument. This allows +fine control of the way to write out certain columns, for instance +writing an ISO format Time column as a pair of JD1/JD2 floating +point values (for full resolution) or as a formatted ISO date string. + +.. + EXAMPLE END + +.. _built_in_readers_writers: + +Built-In Table Readers/Writers +------------------------------ + +The :class:`~astropy.table.Table` class has built-in support for various input +and output formats including :ref:`table_io_ascii`, +:ref:`table_io_fits`, :ref:`table_io_hdf5`, :ref:`table_io_pandas`, +:ref:`table_io_parquet`, and :ref:`table_io_votable`. + +A full list of the supported formats and corresponding classes is shown in the +table below. The ``Write`` column indicates those formats that support write +functionality, and the ``Suffix`` column indicates the filename suffix +indicating a particular format. If the value of ``Suffix`` is ``auto``, the +format is auto-detected from the file itself. Not all formats support +auto-detection. + +=========================== ===== ====== ============================================================================================ + Format Write Suffix Description +=========================== ===== ====== ============================================================================================ + ascii Yes Text table in most supported formats (uses guessing) + ascii.aastex Yes :class:`~astropy.io.ascii.AASTex`: AASTeX deluxetable used for AAS journals + ascii.basic Yes :class:`~astropy.io.ascii.Basic`: Basic table with custom delimiters + ascii.cds No :class:`~astropy.io.ascii.Cds`: CDS format table + ascii.commented_header Yes :class:`~astropy.io.ascii.CommentedHeader`: Column names in a commented line + ascii.csv Yes .csv :class:`~astropy.io.ascii.Csv`: Basic table with comma-separated values + ascii.daophot No :class:`~astropy.io.ascii.Daophot`: IRAF DAOphot format table + ascii.ecsv Yes .ecsv :class:`~astropy.io.ascii.Ecsv`: Basic table with Enhanced CSV (supporting metadata) + ascii.fixed_width Yes :class:`~astropy.io.ascii.FixedWidth`: Fixed width +ascii.fixed_width_no_header Yes :class:`~astropy.io.ascii.FixedWidthNoHeader`: Fixed width with no header + ascii.fixed_width_two_line Yes :class:`~astropy.io.ascii.FixedWidthTwoLine`: Fixed width with second header line + ascii.html Yes .html :class:`~astropy.io.ascii.HTML`: HTML table + ascii.ipac Yes :class:`~astropy.io.ascii.Ipac`: IPAC format table + ascii.latex Yes .tex :class:`~astropy.io.ascii.Latex`: LaTeX table + ascii.mesa No .data :class:`~astropy.io.ascii.Mesa`: MESA stellar evolution code output format + ascii.mrt Yes :class:`~astropy.io.ascii.Mrt`: AAS Machine-Readable Table format + ascii.no_header Yes :class:`~astropy.io.ascii.NoHeader`: Basic table with no headers + ascii.qdp Yes .qdp :class:`~astropy.io.ascii.QDP`: Quick and Dandy Plotter files + ascii.rdb Yes .rdb :class:`~astropy.io.ascii.Rdb`: Tab-separated with a type definition header line + ascii.rst Yes .rst :class:`~astropy.io.ascii.RST`: reStructuredText simple format table + ascii.sextractor No :class:`~astropy.io.ascii.SExtractor`: SExtractor format table + ascii.tab Yes :class:`~astropy.io.ascii.Tab`: Basic table with tab-separated values + ascii.tdat Yes .tdat :class:`~astropy.io.ascii.Tdat`: Transportable Database Aggregate Table format + fits Yes auto :mod:`~astropy.io.fits`: Flexible Image Transport System file + hdf5 Yes auto |HDF5|: Hierarchical Data Format binary file + jsviewer Yes JavaScript viewer format (write-only) + pandas.csv Yes :func:`pandas.read_csv` and :meth:`pandas.DataFrame.to_csv` + pandas.fwf No :func:`pandas.read_fwf` (fixed width format) + pandas.html Yes :func:`pandas.read_html` and :meth:`pandas.DataFrame.to_html` + pandas.json Yes :func:`pandas.read_json` and :meth:`pandas.DataFrame.to_json` + parquet Yes auto |Parquet|: Apache Parquet binary file + parquet.votable Yes Parquet file(s) with VOTable metadata + pyarrow.csv No :func:`~astropy.io.misc.pyarrow.csv.read_csv`: Performant CSV reader + votable Yes auto :mod:`~astropy.io.votable`: Table format used by Virtual Observatory (VO) initiative + votable.parquet Yes Parquet serialization of VOTables. Specify this format for writing, reading is automatic. +=========================== ===== ====== ============================================================================================ + +Details +------- + +.. _table_serialization_methods: + +Table Serialization Methods +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +``astropy`` supports fine-grained control of the way to write out (serialize) +the columns in a Table. For instance, if you are writing an ISO format +Time column to an ECSV text table file, you may want to write this as a pair +of JD1/JD2 floating point values for full resolution (perfect "round-trip"), +or as a formatted ISO date string so that the values are easily readable by +your other applications. + +The default method for serialization depends on the format (FITS, ECSV, HDF5). +For instance HDF5 is a binary format and so it would make sense to store a Time +object as JD1/JD2, while ECSV is a flat text format and commonly you +would want to see the date in the same format as the Time object. The defaults +also reflect an attempt to minimize compatibility issues between ``astropy`` +versions. For instance, it was possible to write Time columns to ECSV as +formatted strings in a version prior to the ability to write as JD1/JD2 +pairs, so the current default for ECSV is to write as formatted strings. + +The two classes which have configurable serialization methods are `~astropy.time.Time` +and `~astropy.table.MaskedColumn`. The defaults for each format are listed below: + +====== ==================== =============== +Format Time MaskedColumn +====== ==================== =============== +FITS ``jd1_jd2`` ``null_value`` +ECSV ``formatted_value`` ``null_value`` +HDF5 ``jd1_jd2`` ``data_mask`` +YAML ``jd2_jd2`` --- +====== ==================== =============== + +Examples +"""""""" + +.. + EXAMPLE START + Table Serialization Methods in astropy.io + +Start by making a table with a Time column and masked column:: + + >>> import sys + >>> from astropy.time import Time + >>> from astropy.table import Table, MaskedColumn + + >>> t = Table(masked=True) + >>> t['tm'] = Time(['2000-01-01', '2000-01-02']) + >>> t['mc1'] = MaskedColumn([1.0, 2.0], mask=[True, False]) + >>> t['mc2'] = MaskedColumn([3.0, 4.0], mask=[False, True]) + >>> t +
+ tm mc1 mc2 + Time float64 float64 + ----------------------- ------- ------- + 2000-01-01 00:00:00.000 -- 3.0 + 2000-01-02 00:00:00.000 2.0 -- + +Now specify that you want all `~astropy.time.Time` columns written as JD1/JD2 +and the ``mc1`` column written as a data/mask pair and write to ECSV:: + + >>> serialize_method = {Time: 'jd1_jd2', 'mc1': 'data_mask'} + >>> t.write(sys.stdout, format='ascii.ecsv', serialize_method=serialize_method) # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE + # %ECSV 1.0 + ... + # schema: astropy-2.0 + tm.jd1 tm.jd2 mc1 mc1.mask mc2 + 2451544.0 0.5 1.0 True 3.0 + 2451546.0 -0.5 2.0 False "" + +(Spaces added for clarity) + +Notice that the ``tm`` column has been replaced by the ``tm.jd1`` and ``tm.jd2`` +columns, and likewise a new column ``mc1.mask`` has appeared and it explicitly +contains the mask values. When this table is read back with the ``ascii.ecsv`` +reader then the original columns are reconstructed. + +The ``serialize_method`` argument can be set in two different ways: + +- As a single string like ``data_mask``. This value then applies to every + column, and is a convenient strategy for a masked table with no Time columns. +- As a `dict`, where the key can be either a single column name or a class (as + shown in the example above), and the value is the corresponding serialization + method. + +.. + EXAMPLE END + +Reading and Writing Column Objects +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. EXAMPLE START: Reading and Writing Column Objects + +Individual table columns do not have their own functions for reading and writing +but it is easy to select just a single column (here "obstime") from a table for writing:: + + >>> from astropy.time import Time + >>> tab = Table({'name': ['AB Aur', 'SU Aur'], + ... 'obstime': Time(['2013-05-23T14:23:12', '2011-11-11T11:11:11'])}) + >>> tab[['obstime']].write('obstime.fits') + +Note the notation ``[['obstime']]`` in the last line - indexing a table with a list of strings +gives us a new table with the columns given by the strings. Since the inner list has only +one element, the resulting table has only one column. + +Then, we can read back that single-column table and extract the column from it:: + + >>> col = Table.read('obstime.fits').columns[0] + >>> type(col) + + +.. testcleanup:: + + >>> import pathlib + >>> pathlib.Path.unlink('obstime.fits') + +.. EXAMPLE END + +Note on Filenames +^^^^^^^^^^^^^^^^^ +Both the :meth:`~astropy.table.Table.read` and +:meth:`~astropy.table.Table.write` methods can accept file paths of the form +``~/data/file.csv`` or ``~username/data/file.csv``. These tilde-prefixed paths +are expanded in the same way as is done by many command-line utilities, to +represent the home directory of the current or specified user, respectively. + +Command-Line Utility +^^^^^^^^^^^^^^^^^^^^ + +.. note:: + + In v7.1, the ``showtable`` command is now deprecated to avoid a name clash on Debian; + use ``showtable-astropy`` instead. The deprecated command will be removed in a future + release. + +For convenience, the command-line tool ``showtable-astropy`` can be used to print the +content of tables for the formats supported by the unified I/O interface. + +Example +""""""" + +.. + EXAMPLE START + Viewing the Contents of a Table on the Command Line + +To view the contents of a table on the command line:: + + $ showtable-astropy astropy/io/fits/tests/data/table.fits + + target V_mag + ------- ----- + NGC1001 11.1 + NGC1002 12.3 + NGC1003 15.2 + +To get full documentation on the usage and available options, do ``showtable-astropy --help``. diff --git a/docs/io/unified_table_fits.rst b/docs/io/unified_table_fits.rst new file mode 100644 index 000000000000..99df4b9236ac --- /dev/null +++ b/docs/io/unified_table_fits.rst @@ -0,0 +1,626 @@ +.. _table_io_fits: + + +FITS +---- + +Reading and writing tables in `FITS `_ format is +supported with ``format='fits'``. In most cases, existing FITS files should be +automatically identified as such based on the header of the file, but if not, +or if writing to disk, then the format should be explicitly specified. + +Reading +^^^^^^^ + +If a FITS table file contains only a single table, then it can be read in +as shown below. In this example we use a file that is installed with astropy:: + + >>> from astropy.table import Table + >>> from astropy.utils.data import get_pkg_data_filename + >>> chandra_events = get_pkg_data_filename('data/chandra_time.fits', + ... package='astropy.io.fits.tests') + >>> t = Table.read(chandra_events) + +A benefit of using the unified interface to read the table is that it will reconstruct +any :ref:`mixin_columns` that were written to that HDU. + +If more than one table is present in the file, you can select the HDU +by index or by name:: + + >>> t = Table.read(chandra_events, hdu="EVENTS") + +In this case if the ``hdu`` argument is omitted, then the first table found +will be read in and a warning will be emitted. + +You can also read a table from the HDUs of an in-memory FITS file. :: + + >>> from astropy.io import fits + >>> with fits.open(chandra_events) as hdul: + ... t = Table.read(hdul["EVENTS"]) + +If a column contains unit information, it will have an associated +`astropy.units` object:: + + >>> t["energy"].unit + Unit("eV") + +It is also possible to get directly a table with columns as +`~astropy.units.Quantity` objects by using the `~astropy.table.QTable` class:: + + >>> from astropy.table import QTable + >>> t2 = QTable.read(chandra_events, hdu=1) + >>> t2['energy'] + + +Writing +^^^^^^^ + +To write a table ``t`` to a new file:: + + >>> t.write('new_table.fits') + +If the file already exists and you want to overwrite it, then set the +``overwrite`` keyword:: + + >>> t.write('existing_table.fits', overwrite=True) + +If you want to append a table to an existing file, set the ``append`` +keyword:: + + >>> t.write('existing_table.fits', append=True) + + +.. testcleanup:: + + >>> import pathlib + >>> pathlib.Path.unlink('new_table.fits') + >>> pathlib.Path.unlink('existing_table.fits') + +Alternatively, you can use the convenience function +:func:`~astropy.io.fits.table_to_hdu` to create a single +binary table HDU and insert or append that to an existing +:class:`~astropy.io.fits.HDUList`. + +There is support for writing a table which contains :ref:`mixin_columns` such +as `~astropy.time.Time` or `~astropy.coordinates.SkyCoord`. This uses FITS +``COMMENT`` cards to capture additional information needed order to fully +reconstruct the mixin columns when reading back from FITS. The information is a +Python `dict` structure which is serialized using YAML. + +Keywords +^^^^^^^^ + +The FITS keywords associated with an HDU table are represented in the ``meta`` +ordered dictionary attribute of a :ref:`Table `. After reading +a table you can view the available keywords in a readable format using: + + >>> for key, value in t.meta.items(): + ... print(f'{key} = {value}') + EXTNAME = EVENTS + HDUNAME = EVENTS + TLMIN2 = 0 + ... + +This does not include the "internal" FITS keywords that are required to specify +the FITS table properties (e.g., ``NAXIS``, ``TTYPE1``). ``HISTORY`` and +``COMMENT`` keywords are treated specially and are returned as a list of + + >>> t.meta['MY_KEYWD'] = 'my value' + >>> t.meta['COMMENT'] = ['First comment', 'Second comment', 'etc'] + >>> t.write('my_table.fits', overwrite=True) + +.. testcleanup:: + >>> pathlib.Path.unlink('my_table.fits') + +The keyword names (e.g., ``MY_KEYWD``) will be automatically capitalized prior +to writing. + +.. _fits_astropy_native: + + +TDISPn Keyword +^^^^^^^^^^^^^^ + +TDISPn FITS keywords will map to and from the `~astropy.table.Column` ``format`` +attribute if the display format is convertible to and from a Python display +format. Below are the rules used for both conversion directions. + +TDISPn to Python format string +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +TDISPn format characters are defined in the table below. + +============ ================================================================ + Format Description +============ ================================================================ +Aw Character +Lw Logical +Iw.m Integer +Bw.m Binary, integers only +Ow.m Octal, integers only +Zw.m Hexadecimal, integers only +Fw.d Floating-point, fixed decimal notation +Ew.dEe Floating-point, exponential notation +ENw.d Engineering; E format with exponent multiple of three +ESw.d Scientific; same as EN but non-zero leading digit if not zero +Gw.dEe General; appears as F if significance not lost, also E +Dw.dEe Floating-point, exponential notation, double precision +============ ================================================================ + +Where w is the width in characters of displayed values, m is the minimum number +of digits displayed, d is the number of digits to the right of decimal, and e +is the number of digits in the exponent. The .m and Ee fields are optional. + +The A (character), L (logical), F (floating point), and G (general) display +formats can be directly translated to Python format strings. The other formats +need to be modified to match Python display formats. + +For the integer formats (I, B, O, and Z), the width (w) value is used to add +space padding to the left of the column value. The minimum number (m) value is +not used. For the E, G, D, EN, and ES formats (floating point exponential) the +width (w) and precision (d) are both used, but the exponential (e) is not used. + +Python format string to TDISPn +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The conversion from Python format strings back to TDISPn is slightly more +complicated. + +Python strings map to the TDISP format A if the Python formatting string does +not contain right space padding. It will accept left space padding. The same +applies to the logical format L. + +The integer formats (decimal integer, binary, octal, hexadecimal) map to the +I, B, O, and Z TDISP formats respectively. Integer formats do not accept a +zero padded format string or a format string with no left padding defined (a +width is required in the TDISP format standard for the Integer formats). + +For all float and exponential values, zero padding is not accepted. There +must be at least a width or precision defined. If only a width is defined, +there is no precision set for the TDISPn format. If only a precision is +defined, the width is set to the precision plus an extra padding value +depending on format type, and both are set in the TDISPn format. Otherwise, +if both a width and precision are present they are both set in the TDISPn +format. A Python ``f`` or ``F`` map to TDISP F format. The Python ``g`` or +``G`` map to TDISP G format. The Python ``e`` and ``E`` map to TDISP E format. + +.. _unified_table_fits_masked_columns: + +Masked Columns +^^^^^^^^^^^^^^ + +Tables that contain `~astropy.table.MaskedColumn` columns can be written to +FITS. By default this will replace the masked data elements with certain +sentinel values according to the FITS standard: + +- ``NaN`` for float columns. +- Value of ``TNULLn`` for integer columns, as defined by the column + ``fill_value`` attribute. +- Null string for string columns (not currently implemented). + +When the file is read back those elements are marked as masked in the returned +table, but see `issue #4708 `_ +for problems in all three cases. It is possible to deactivate the masking with +``mask_invalid=False``. + +The FITS standard has a few limitations: + +- Not all data types are supported (e.g., logical / boolean). +- Integer columns require picking one value as the NULL indicator. If + all possible values are represented in valid data (e.g., an unsigned + int columns with all 256 possible values in valid data), then there + is no way to represent missing data. +- The masked data values are permanently lost, precluding the possibility + of later unmasking the values. + +``astropy`` provides a work-around for this limitation that users can choose to +use. The key part is to use the ``serialize_method='data_mask'`` keyword +argument when writing the table. This tells the FITS writer to split each masked +column into two separate columns, one for the data and one for the mask. +When it gets read back that process is reversed and the two columns are +merged back into one masked column. + +:: + + >>> from astropy.table.table_helpers import simple_table + >>> t = simple_table(masked=True) + >>> t['d'] = [False, False, True] + >>> t['d'].mask = [True, False, False] + >>> t +
+ a b c d + int64 float64 str1 bool + ----- ------- ---- ----- + -- 1.0 c -- + 2 2.0 -- False + 3 -- e True + +:: + + >>> t.write('data.fits', serialize_method='data_mask', overwrite=True) + >>> Table.read('data.fits') +
+ a b c d + int64 float64 bytes1 bool + ----- ------- ------ ----- + -- 1.0 c -- + 2 2.0 -- False + 3 -- e True + +.. warning:: This option goes outside of the established FITS standard for + representing missing data, so users should be careful about choosing this + option, especially if other (non-``astropy``) users will be reading the + file(s). Behind the scenes, ``astropy`` is converting the masked columns + into two distinct data and mask columns, then writing metadata into + ``COMMENT`` cards to allow reconstruction of the original data. + +``astropy`` Native Objects (Mixin Columns) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +It is possible to store not only standard `~astropy.table.Column` objects to a +FITS table HDU, but also any ``astropy`` native objects +(:ref:`mixin_columns`) within a `~astropy.table.Table` or +`~astropy.table.QTable`. This includes `~astropy.time.Time`, +`~astropy.units.Quantity`, `~astropy.coordinates.SkyCoord`, and many others. + +In general, a mixin column may contain multiple data components as well as +object attributes beyond the standard Column attributes like ``format`` or +``description``. Abiding by the rules set by the FITS standard requires the +mapping of these data components and object attributes to the appropriate FITS +table columns and keywords. Thus, a well defined protocol has been developed +to allow the storage of these mixin columns in FITS while allowing the object to +"round-trip" through the file with no loss of data or attributes. + +Quantity +~~~~~~~~ + +A `~astropy.units.Quantity` mixin column in a `~astropy.table.QTable` is +represented in a FITS table using the ``TUNITn`` FITS column keyword to +incorporate the unit attribute of Quantity. For example:: + + >>> from astropy.table import QTable + >>> import astropy.units as u + >>> t = QTable([[1, 2] * u.angstrom]) + >>> t.write('my_table.fits', overwrite=True) + >>> qt = QTable.read('my_table.fits') + >>> qt + + col0 + Angstrom + float64 + -------- + 1.0 + 2.0 + +.. testcleanup:: + >>> pathlib.Path.unlink('my_table.fits') + +Time +~~~~ + +``astropy`` provides the following features for reading and writing ``Time``: + +- Writing and reading `~astropy.time.Time` Table columns to and from FITS + tables. +- Reading time coordinate columns in FITS tables (compliant with the time + standard) as `~astropy.time.Time` Table columns. + +Writing and reading ``astropy`` Time columns +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +By default, a `~astropy.time.Time` mixin column within a `~astropy.table.Table` +or `~astropy.table.QTable` will be written to FITS in full precision. This will +be done using the FITS time standard by setting the necessary FITS header +keywords. + +The default behavior for reading a FITS table into a `~astropy.table.Table` +has historically been to convert all FITS columns to `~astropy.table.Column` +objects, which have closely matching properties. For some columns, however, +closer native ``astropy`` representations are possible, and you can indicate +these should be used by passing ``astropy_native=True`` (for backwards +compatibility, this is not done by default). This will convert columns +conforming to the FITS time standard to `~astropy.time.Time` instances, +avoiding any loss of precision and preserving information about the time +system if set in the fits header. + +Example +~~~~~~~ + +.. + EXAMPLE START + Writing and Reading Time Columns to/from FITS Tables + +To read a FITS table into `~astropy.table.Table`: + + >>> from astropy.time import Time + >>> from astropy.table import Table + >>> from astropy.coordinates import EarthLocation + >>> t = Table() + >>> t['a'] = Time([100.0, 200.0], scale='tt', format='mjd', + ... location=EarthLocation(-2446354, 4237210, 4077985, unit='m')) + >>> t.write('my_table.fits', overwrite=True) + >>> tm = Table.read('my_table.fits', astropy_native=True) + >>> tm['a'] +
+ ... + ... Unique source identifier (unique within a particular Data Release) (source_id) + ... + ... Right ascension (ICRS) at Ep=2016.0 (ra) + ... + ... Declination (ICRS) at Ep=2016.0 (dec) + ... + ... + ... + ... + ... + ...
368078797593645.019123737330.16323692972
557057342208044.853345087860.12810861107
794139533734444.974115339120.22751845955
"""))) + >>> table = votable.get_first_table() + >>> for field in votable.iter_fields_and_params(): + ... print(field) + + + + +We can now generate a ``SkyCoord`` object from the VOTable information:: + + >>> from astropy.coordinates import SkyCoord + >>> SkyCoord(table.array.data["RA_ICRS"], + ... table.array.data["DE_ICRS"], + ... frame=votable.get_coosys_by_id( + ... table.get_field_by_id("RA_ICRS").ref + ... ).to_astropy_frame(), + ... unit="deg") + diff --git a/docs/io/votable/dataorigin.rst b/docs/io/votable/dataorigin.rst new file mode 100644 index 000000000000..b0f5afaa9659 --- /dev/null +++ b/docs/io/votable/dataorigin.rst @@ -0,0 +1,129 @@ +Introduction +------------ + +Extract basic provenance information from VOTable header. The information is described in +DataOrigin IVOA note: https://www.ivoa.net/documents/DataOrigin/. + +DataOrigin includes both the query information (such as publisher, contact, versions, etc.) +and the Dataset origin (such as Creator, bibliographic links, URL, etc.) + +This API retrieves Metadata from INFO in VOTable. + + +Getting Started +--------------- + +For the following example, we would first reconstruct a VOTable DataOrigin based on a query to +VizieR catalogue J/AJ/167/18. In practice, you would obtain this table directly from +the VO service of interest:: + + >>> from astropy.io.votable.dataorigin import add_data_origin_info + >>> from astropy.io.votable.tree import VOTableFile + >>> from astropy.table import Column, Table + >>> # For this example, the table data itself is irrelevant. + >>> table = Table([ + ... Column(name="id", data=[1, 2, 3, 4]), + ... Column(name="bmag", unit="mag", data=[5.6, 7.9, 12.4, 11.3])]) + >>> votable = VOTableFile().from_table(table) + >>> votable.description = "Period variations of 32 contact binaries (Hong+, 2024)" + >>> # Order is important here for the example. + >>> add_data_origin_info(votable, "data_ivoid", "ivo://cds.vizier/j/aj/167/18", + ... content="IVOID of underlying data collection") + >>> add_data_origin_info(votable, "creator", "Hong K.", + ... content="First author or institution") + >>> add_data_origin_info(votable, "cites", "bibcode:2024AJ....167...18H", + ... content="Article or Data origin sources") + >>> add_data_origin_info(votable, "journal", "Astronomical Journal (AAS)", + ... content="Editor name (article)") + >>> add_data_origin_info(votable, "original_date", "2024", + ... content="Year of the article publication") + >>> # The rest in alphabetical order. + >>> add_data_origin_info(votable, "citation", "doi:10.26093/cds/vizier.51670018") + >>> add_data_origin_info(votable, "contact", "cds-question@unistra.fr") + >>> add_data_origin_info(votable, "publication_date", "2024-11-06") + >>> add_data_origin_info(votable, "publisher", "CDS") + >>> add_data_origin_info(votable, "reference_url", "https://cdsarc.cds.unistra.fr/viz-bin/cat/J/AJ/167/18") + >>> add_data_origin_info(votable, "request_date", "2025-03-05T14:18:05") + >>> add_data_origin_info(votable, "rights_uri", "https://cds.unistra.fr/vizier-org/licences_vizier.html") + >>> add_data_origin_info(votable, "server_software", "7.4.5") + >>> add_data_origin_info(votable, "service_protocol", "ivo://ivoa.net/std/ConeSearch/v1.03") + + +To extract DataOrigin from VOTable:: + + >>> from astropy.io.votable.dataorigin import extract_data_origin + >>> data_origin = extract_data_origin(votable) + >>> print(data_origin) + publisher: CDS + server_software: 7.4.5 + service_protocol: ivo://ivoa.net/std/ConeSearch/v1.03 + request_date: 2025-03-05T14:18:05 + contact: cds-question@unistra.fr + + data_ivoid: ivo://cds.vizier/j/aj/167/18 + citation: doi:10.26093/cds/vizier.51670018 + reference_url: https://cdsarc.cds.unistra.fr/viz-bin/cat/J/AJ/167/18 + rights_uri: https://cds.unistra.fr/vizier-org/licences_vizier.html + creator: Hong K. + journal: Astronomical Journal (AAS) + cites: bibcode:2024AJ....167...18H + original_date: 2024 + publication_date: 2024-11-06 + + +Contents and metadata +--------------------- + +`astropy.io.votable.dataorigin.extract_data_origin` returns a `astropy.io.votable.dataorigin.DataOrigin` (class) container which is made of: + +* a `astropy.io.votable.dataorigin.QueryOrigin` (class) container describing the request. + ``QueryOrigin`` is considered to be unique for the whole VOTable. + It includes metadata like the publisher, the contact, date of execution, query, etc. + +* a list of `astropy.io.votable.dataorigin.DatasetOrigin` (class) container for each Element having DataOrigin information. + ``DataSetOrigin`` is a basic provenance of the datasets queried. Each attribute is a list. + It includes metadata like authors, ivoid, landing pages, .... + +Examples +-------- + +Get the (Data Center) publisher and the Creator of the dataset:: + + >>> print(data_origin.query.publisher) + CDS + >>> print(data_origin.origin[0].creator) + ['Hong K.'] + +Other capabilities +------------------ + +DataOrigin container includes VO Elements: + +* Extract list of `astropy.io.votable.tree.Info`: + + >>> # get DataOrigin with the description of each INFO + >>> for dataset_origin in data_origin.origin: + ... for info in dataset_origin.infos: + ... print(f"{info.name}: {info.value} ({info.content})") + data_ivoid: ivo://cds.vizier/j/aj/167/18 (IVOID of underlying data collection) + creator: Hong K. (First author or institution) + cites: bibcode:2024AJ....167...18H (Article or Data origin sources) + journal: Astronomical Journal (AAS) (Editor name (article)) + original_date: 2024 (Year of the article publication) + ... + +* Extract tree node `astropy.io.votable.tree.Element`; + The following example extracts the citation from the header (in APA style): + + >>> # get the Title retrieved in Element + >>> origin = data_origin.origin[0] + >>> vo_elt = origin.get_votable_element() + >>> title = vo_elt.description if vo_elt else "" + >>> print(f"APA: {','.join(origin.creator)} ({origin.publication_date[0]}). {title} [Dataset]. {data_origin.query.publisher}. {origin.citation[0]}") + APA: Hong K. (2024-11-06). Period variations of 32 contact binaries (Hong+, 2024) [Dataset]. CDS. doi:10.26093/cds/vizier.51670018 + +* Add Data Origin INFO into VOTable: + + >>> from astropy.io.votable import dataorigin + >>> dataorigin.add_data_origin_info(votable, "query", "Data center name") + >>> dataorigin.add_data_origin_info(votable.resources[0], "creator", "Author name") diff --git a/docs/io/votable/index.rst b/docs/io/votable/index.rst new file mode 100644 index 000000000000..786be659baf4 --- /dev/null +++ b/docs/io/votable/index.rst @@ -0,0 +1,310 @@ +.. doctest-skip-all + +.. include:: references.txt + +.. _astropy-io-votable: + +*************************************** +VOTable Handling (`astropy.io.votable`) +*************************************** + +Introduction +============ + +The `astropy.io.votable` sub-package converts VOTable XML files to and +from ``numpy`` record arrays. + +.. note:: + + If you want to read or write a single table in VOTable format, the + recommended method is via the :ref:`table_io` interface. In particular, + see the :ref:`Unified I/O VO Tables ` section. + +Getting Started +=============== + +Reading a VOTable File +---------------------- + +To read a VOTable file, pass a file path to +`~astropy.io.votable.parse`:: + + from astropy.io.votable import parse + votable = parse("votable.xml") + +``votable`` is a `~astropy.io.votable.tree.VOTableFile` object, which +can be used to retrieve and manipulate the data and save it back out +to disk. + +Writing a VOTable File +---------------------- + +This section describes writing table data in the VOTable format using the +`~astropy.io.votable` package directly. For some cases, however, the high-level +:ref:`table_io` will often suffice and is somewhat more convenient to use. See +the :ref:`Unified I/O VOTable ` section for details. + +To save a VOTable file, call the +`~astropy.io.votable.tree.VOTableFile.to_xml` method, or equivalently +the ``write`` convenience method. Both accept either a string or +Unicode path, or a Python file-like object:: + + votable.to_xml('output.xml') + + # Equivalently: + votable.write('output.xml') + +.. note:: + + ``VOTableFile.write()`` serialises the existing ``VOTableFile`` object + as-is, preserving all metadata including PARAMs and INFOs. This is + different from ``Table.write(format='votable')``, which converts an + `~astropy.table.Table` into a new VOTable and does not preserve + VOTable-specific metadata such as PARAMs and INFOs. + +There are a number of data storage formats supported by +`astropy.io.votable`. The ``TABLEDATA`` format is XML-based and +stores values as strings representing numbers. The ``BINARY`` format +is more compact, and stores numbers in base64-encoded binary. VOTable +version 1.3 adds the ``BINARY2`` format, which allows for masking of +any data type, including integers and bit fields which cannot be +masked in the older ``BINARY`` format. The storage format can be set +on a per-table basis using the `~astropy.io.votable.tree.TableElement.format` +attribute, or globally using the +`~astropy.io.votable.tree.VOTableFile.set_all_tables_format` method:: + + votable.get_first_table().format = 'binary' + votable.set_all_tables_format('binary') + votable.to_xml('binary.xml') + +The VOTable elements +==================== + +VOTables are built from nested elements. Let's for example build a +votable containing an ``INFO`` element:: + + >>> from astropy.io.votable.tree import VOTableFile, Info + >>> vot = VOTableFile() + >>> vot.infos.append(Info(name="date_obs", value="2025-01-01")) + +These elements can be: + +- `~astropy.io.votable.tree.CooSys` +- `~astropy.io.votable.tree.TimeSys` +- `~astropy.io.votable.tree.Info` +- `~astropy.io.votable.tree.Param` +- `~astropy.io.votable.tree.Group` +- `~astropy.io.votable.tree.Resource` +- `~astropy.io.votable.tree.Link` +- `~astropy.io.votable.tree.TableElement` +- `~astropy.io.votable.tree.Field` +- `~astropy.io.votable.tree.Values` +- `~astropy.io.votable.tree.MivotBlock` + +Here are some detailed explanations on some of these elements: + +.. toctree:: + :maxdepth: 1 + + table_element + coosys_element + mivot_blocks + +Using `astropy.io.votable` +========================== + +Standard Compliance +------------------- + +`astropy.io.votable.tree.TableElement` supports the `VOTable Format Definition +Version 1.1 +`_, +`Version 1.2 +`_, +`Version 1.3 +`_, +`Version 1.4 +`_, +and `Version 1.5 +`_, +Some flexibility is provided to support the 1.0 draft version and +other nonstandard usage in the wild, see :ref:`verifying-votables` for more +details. + +.. note:: + + Each warning and VOTABLE-specific exception emitted has a number and + is documented in more detail in :ref:`warnings` and + :ref:`exceptions`. + +Output always conforms to the 1.1, 1.2, 1.3, 1.4, 1.5 spec, depending on the +input. + +.. _verifying-votables: + +Verifying VOTables +------------------ + +Many VOTable files in the wild do not conform to the VOTable specification. You +can set what should happen when a violation is encountered with the ``verify`` +keyword, which can take three values: + + * ``'ignore'`` - Attempt to parse the VOTable silently. This is the default + setting. + * ``'warn'`` - Attempt to parse the VOTable, but raise appropriate + :ref:`warnings`. It is possible to limit the number of warnings of the + same type to a maximum value using the + `astropy.io.votable.exceptions.conf.max_warnings + ` item in the + :ref:`astropy_config`. + * ``'exception'`` - Do not parse the VOTable and raise an exception. + +The ``verify`` keyword can be used with the :func:`~astropy.io.votable.parse` +or :func:`~astropy.io.votable.parse_single_table` functions:: + + from astropy.io.votable import parse + votable = parse("votable.xml", verify='warn') + +It is possible to change the default ``verify`` value through the +`astropy.io.votable.conf.verify ` item in the +:ref:`astropy_config`. + +Note that ``'ignore'`` or ``'warn'`` mean that ``astropy`` will attempt to +parse the VOTable, but if the specification has been violated then success +cannot be guaranteed. + +It is good practice to report any errors to the author of the application that +generated the VOTable file to bring the file into compliance with the +specification. + +.. _votable-serialization: + +Data Serialization Formats +-------------------------- + +VOTable supports a number of different serialization formats. + +- `TABLEDATA + `__ + stores the data in pure XML, where the numerical values are written + as human-readable strings. + +- `BINARY + `__ + is a binary representation of the data, stored in the XML as an + opaque ``base64``-encoded blob. + +- `BINARY2 + `__ + was added in VOTable 1.3, and is identical to "BINARY", except that + it explicitly records the position of missing values rather than + identifying them by a special value. + +- `FITS + `__ + stores the data in an external FITS file. This serialization is not + supported by the `astropy.io.votable` writer, since it requires + writing multiple files. + +- ``PARQUET`` + stores the data in an external PARQUET file, similar to FITS serialization. + Reading and writing is fully supported by the `astropy.io.votable` writer and + the `astropy.io.votable.parse` reader. The parquet file can be + referenced with either absolute and relative paths. The parquet + serialization can be used as part of the unified Table I/O (see next + section), by setting the ``format`` argument to ``'votable.parquet'``. + +The serialization format can be selected in two ways: + + 1) By setting the ``format`` attribute of a + `astropy.io.votable.tree.TableElement` object:: + + votable.get_first_table().format = "binary" + votable.to_xml("new_votable.xml") + + 2) By overriding the format of all tables using the + ``tabledata_format`` keyword argument when writing out a VOTable + file:: + + votable.to_xml("new_votable.xml", tabledata_format="binary") + +Converting to/from an `astropy.table.Table` +------------------------------------------- + +The VOTable standard does not map conceptually to an +`astropy.table.Table`. However, a single table within the ``VOTable`` +file may be converted to and from an `astropy.table.Table`:: + + from astropy.io.votable import parse_single_table + table = parse_single_table("votable.xml").to_table() + +As a convenience, there is also a function to create an entire VOTable +file with just a single table:: + + from astropy.io.votable import from_table, writeto + votable = from_table(table) + writeto(votable, "output.xml") + +.. note:: + + By default, ``to_table`` will use the ``ID`` attribute from the files to + create the column names for the `~astropy.table.Table` object. However, + it may be that you want to use the ``name`` attributes instead. For this, + set the ``use_names_over_ids`` keyword to `True`. Note that since field + ``names`` are not guaranteed to be unique in the VOTable specification, + but column names are required to be unique in ``numpy`` structured arrays (and + thus `astropy.table.Table` objects), the names may be renamed by appending + numbers to the end in some cases. + +Performance Considerations +-------------------------- + +File reads will be moderately faster if the ``TABLE`` element includes +an nrows_ attribute. If the number of rows is not specified, the +record array must be resized repeatedly during load. + +.. _nrows: http://www.ivoa.net/documents/REC/VOTable/VOTable-20040811.html#ToC10 + + +Data Origin +=========== + +.. _astropy-io-votable-dataorigin: + +.. include:: dataorigin.rst + + +See Also +======== + +- `VOTable Format Definition Version 1.1 + `_ + +- `VOTable Format Definition Version 1.2 + `_ + +- `VOTable Format Definition Version 1.3 + `_ + +- `VOTable Format Definition Version 1.4 + `_ + +- `VOTable Format Definition Version 1.5 + `_ + +- `MIVOT Recommendation Version 1.0 + `_ + +.. note that if this section gets too long, it should be moved to a separate + doc page - see the top of performance.inc.rst for the instructions on how to do + that +.. include:: performance.inc.rst + +Reference/API +============= + +.. toctree:: + :maxdepth: 2 + + ref_api + api_exceptions diff --git a/docs/io/votable/mivot_blocks.rst b/docs/io/votable/mivot_blocks.rst new file mode 100644 index 000000000000..d81e6891d231 --- /dev/null +++ b/docs/io/votable/mivot_blocks.rst @@ -0,0 +1,129 @@ +.. doctest-skip-all + +Reading and writing VO model annotations (MIVOT) +------------------------------------------------ + +A ``RESOURCE`` element can be a ``MIVOT`` block since VOTable version 1.5. + +Introduction +^^^^^^^^^^^^ +Model Instances in VOTables (`MIVOT `_) +defines a syntax to map VOTable data to any model serialised in VO-DML (Virtual Observatory Data Modeling Language). +This annotation schema operates as a bridge between data and the models. It associates both column/param metadata and data +from the VOTable to the data model elements (class, attributes, types, etc.). It also brings up VOTable data or +metadata that were possibly missing in the table, e.g., coordinate system description, or curation tracing. +The data model elements are grouped in an independent annotation block complying with the MIVOT XML schema which +is added as an extra resource above the table element. +The MIVOT syntax allows to describe a data structure as a hierarchy of classes. +It is also able to represent relations and compositions between them. It can moreover build up data model objects by +aggregating instances from different tables of the VOTable. + +Astropy implementation +^^^^^^^^^^^^^^^^^^^^^^ +The purpose of Astropy is not to process VO annotations. +It is just to allow related packages to get and set MIVOT blocks from/into VOTables. +For this reason, in this implementation MIVOT annotations are both imported and exported as strings. +The current implementation prevents client code from injecting into VOTables strings +that are not MIVOT serializations. + +MivotBlock implementation: + +- MIVOT blocks are handled by the :class:`astropy.io.votable.tree.MivotBlock` class. +- A MivotBlock instance can only be carried by a resource with "type=meta". +- This instance holds the XML mapping block as a string. +- MivotBlock objects are instanced by the Resource parser. +- The MivotBlock class has its own logic that operates both parsing and IO functionalities. + +Example +""""""" + +.. code-block:: xml + + + + + + ... + + + + .... +
+
+
+ +Reading a VOTable containing a MIVOT block +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +To read in a VOTable file containing or not a MIVOT Resource, pass a file path to `~astropy.io.votable.parse`: + +.. code-block:: python + + >>> from astropy.io.votable import parse + >>> from astropy.utils.data import get_pkg_data_filename + >>> votable = parse(get_pkg_data_filename("data/test.order.xml", package="astropy.io.votable.tests")) + + + + + + +The parse function will call the MIVOT parser if it detects a MIVOT block. + +Building a Resource containing a MIVOT block +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Construct the MIVOT block by passing the XML block as a parameter: + +.. code-block:: python + + >>> from astropy.io.votable import tree + >>> from astropy.io.votable.tree import MivotBlock, Resource, VOTableFile + >>> mivot_block = MivotBlock(""" + + Unit test mapping block + + + """) + +Build a new resource: + +.. code-block:: python + + >>> mivot_resource = Resource() + +Give it the type meta: + +.. code-block:: python + + >>> mivot_resource.type = "meta" + +Then add it the MIVOT block: + +.. code-block:: python + + >>> mivot_resource.mivot_block = mivot_block + +Now you have a MIVOT resource that you can add to an object Resource creating a new Resource: + +.. code-block:: python + + >>> votable = VOTableFile() + >>> r1 = Resource() + >>> r1.type = "results" + >>> r1.resources.append(mivot_resource) + +You can add an `astropy.io.votable.tree.TableElement` to the resource: + +.. code-block:: python + + >>> table = tree.TableElement(votable) + >>> r1.tables.append(t1) + >>> votable.resources.append(r1) + >>> for resource in votable.resources: + ... print(resource.mivot_block.content) + + Unit test mapping block + + diff --git a/docs/io/votable/performance.inc.rst b/docs/io/votable/performance.inc.rst new file mode 100644 index 000000000000..8a54ff093fed --- /dev/null +++ b/docs/io/votable/performance.inc.rst @@ -0,0 +1,12 @@ +.. note that if this is changed from the default approach of using an *include* + (in index.rst) to a separate performance page, the header needs to be changed + from === to ***, the filename extension needs to be changed from .inc.rst to + .rst, and a link needs to be added in the subpackage toctree + +.. _astropy-io-votable-performance: + +.. Performance Tips +.. ================ +.. +.. Here we provide some tips and tricks for how to optimize performance of code +.. using `astropy.io.votable`. diff --git a/docs/io/votable/ref_api.rst b/docs/io/votable/ref_api.rst new file mode 100644 index 000000000000..908a33a9d853 --- /dev/null +++ b/docs/io/votable/ref_api.rst @@ -0,0 +1,36 @@ +.. doctest-skip-all + +.. include:: references.txt + +Reference/API +************* + +.. automodapi:: astropy.io.votable + :no-inheritance-diagram: + :skip: VOWarning + :skip: VOTableChangeWarning + :skip: VOTableSpecWarning + :skip: UnimplementedWarning + :skip: IOWarning + :skip: VOTableSpecError + +.. automodapi:: astropy.io.votable.tree + :no-inheritance-diagram: + +.. automodapi:: astropy.io.votable.converters + :no-inheritance-diagram: + +.. automodapi:: astropy.io.votable.ucd + :no-inheritance-diagram: + +.. automodapi:: astropy.io.votable.util + :no-inheritance-diagram: + +.. automodapi:: astropy.io.votable.validator + :no-inheritance-diagram: + +.. automodapi:: astropy.io.votable.xmlutil + :no-inheritance-diagram: + +.. automodapi:: astropy.io.votable.dataorigin + :no-inheritance-diagram: diff --git a/docs/io/votable/references.txt b/docs/io/votable/references.txt new file mode 100644 index 000000000000..e4975684c303 --- /dev/null +++ b/docs/io/votable/references.txt @@ -0,0 +1,24 @@ +.. _BINARY: http://www.ivoa.net/documents/PR/VOTable/VOTable-20040322.html#ToC27 +.. _BINARY2: http://www.ivoa.net/documents/VOTable/20130315/PR-VOTable-1.3-20130315.html#sec:BIN2 +.. _COOSYS: http://www.ivoa.net/documents/VOTable/20191021/REC-VOTable-1.4-20191021.html#ToC20 +.. _DESCRIPTION: http://www.ivoa.net/documents/REC/VOTable/VOTable-20040811.html#ToC19 +.. _FIELD: http://www.ivoa.net/documents/REC/VOTable/VOTable-20040811.html#ToC24 +.. _FIELDref: http://www.ivoa.net/documents/REC/VOTable/VOTable-20040811.html#ToC31 +.. _FITS: https://fits.gsfc.nasa.gov/fits_documentation.html +.. _GROUP: http://www.ivoa.net/documents/REC/VOTable/VOTable-20040811.html#ToC31 +.. _ID: https://www.w3.org/TR/xml-id/ +.. _INFO: http://www.ivoa.net/documents/VOTable/20040811/REC-VOTable-1.1-20040811.html#ToC19 +.. _LINK: http://www.ivoa.net/documents/REC/VOTable/VOTable-20040811.html#ToC22 +.. _multidimensional arrays: http://www.ivoa.net/documents/REC/VOTable/VOTable-20040811.html#ToC12 +.. _numerical accuracy: http://www.ivoa.net/documents/REC/VOTable/VOTable-20040811.html#ToC26 +.. _PARAM: http://www.ivoa.net/documents/REC/VOTable/VOTable-20040811.html#ToC24 +.. _PARAMref: http://www.ivoa.net/documents/REC/VOTable/VOTable-20040811.html#ToC31 +.. _RESOURCE: http://www.ivoa.net/documents/REC/VOTable/VOTable-20040811.html#ToC21 +.. _TABLE: http://www.ivoa.net/documents/REC/VOTable/VOTable-20040811.html#ToC23 +.. _TABLEDATA: http://www.ivoa.net/documents/PR/VOTable/VOTable-20040322.html#ToC25 +.. _TIMESYS: http://www.ivoa.net/documents/VOTable/20191021/REC-VOTable-1.4-20191021.html#ToC21 +.. _unified content descriptor: http://www.ivoa.net/documents/REC/VOTable/VOTable-20040811.html#ToC28 +.. _unique type: http://www.ivoa.net/documents/REC/VOTable/VOTable-20040811.html#ToC29 +.. _units: http://www.ivoa.net/documents/REC/VOTable/VOTable-20040811.html#ToC27 +.. _VALUES: http://www.ivoa.net/documents/REC/VOTable/VOTable-20040811.html#ToC30 +.. _VOTABLE: http://www.ivoa.net/documents/PR/VOTable/VOTable-20040322.html#ToC9 diff --git a/docs/io/votable/table_element.rst b/docs/io/votable/table_element.rst new file mode 100644 index 000000000000..3f8034735eb9 --- /dev/null +++ b/docs/io/votable/table_element.rst @@ -0,0 +1,245 @@ +.. doctest-skip-all + +Table element +------------- + +VOTable files can contain ``RESOURCE`` elements, each of +which may contain one or more ``TABLE`` elements. The ``TABLE`` +elements contain the arrays of data. + +To get at the ``TABLE`` elements, you can write a loop over the +resources in the ``VOTABLE`` file:: + + for resource in votable.resources: + for table in resource.tables: + # ... do something with the table ... + pass + +However, if the nested structure of the resources is not important, +you can use `~astropy.io.votable.tree.VOTableFile.iter_tables` to +return a flat list of all tables:: + + for table in votable.iter_tables(): + # ... do something with the table ... + pass + +Finally, if you expect only one table in the file, it might be most convenient +to use `~astropy.io.votable.tree.VOTableFile.get_first_table`:: + + table = votable.get_first_table() + +Alternatively, there is a convenience method to parse a VOTable file and +return the first table all in one step:: + + from astropy.io.votable import parse_single_table + table = parse_single_table("votable.xml") + +From a `~astropy.io.votable.tree.TableElement` object, you can get the data itself +in the ``array`` member variable:: + + data = table.array + +This data is a ``numpy`` record array. + +The columns get their names from both the ``ID`` and ``name`` +attributes of the ``FIELD`` elements in the ``VOTABLE`` file. + +.. + EXAMPLE START + Reading a VOTable File with astropy.io.votable + +Suppose we had a ``FIELD`` specified as follows: + +.. code-block:: xml + + + + representing the ICRS declination of the center of the image. + + + +.. note:: + + The mapping from VOTable ``name`` and ``ID`` attributes to ``numpy`` + dtype ``names`` and ``titles`` is highly confusing. + + In VOTable, ``ID`` is guaranteed to be unique, but is not + required. ``name`` is not guaranteed to be unique, but is + required. + + In ``numpy`` record dtypes, ``names`` are required to be unique and + are required. ``titles`` are not required, and are not required + to be unique. + + Therefore, VOTable's ``ID`` most closely maps to ``numpy``'s + ``names``, and VOTable's ``name`` most closely maps to ``numpy``'s + ``titles``. However, in some cases where a VOTable ``ID`` is not + provided, a ``numpy`` ``name`` will be generated based on the VOTable + ``name``. Unfortunately, VOTable fields do not have an attribute + that is both unique and required, which would be the most + convenient mechanism to uniquely identify a column. + + When converting from an `astropy.io.votable.tree.TableElement` object to + an `astropy.table.Table` object, you can specify whether to give + preference to ``name`` or ``ID`` attributes when naming the + columns. By default, ``ID`` is given preference. To give + ``name`` preference, pass the keyword argument + ``use_names_over_ids=True``:: + + >>> votable.get_first_table().to_table(use_names_over_ids=True) + +This column of data can be extracted from the record array using:: + + >>> table.array['dec_targ'] + array([17.15153360566, 17.15153360566, 17.15153360566, 17.1516686826, + 17.1516686826, 17.1516686826, 17.1536197136, 17.1536197136, + 17.1536197136, 17.15375479055, 17.15375479055, 17.15375479055, + 17.1553884541, 17.15539736932, 17.15539752176, + 17.25736014763, + # ... + 17.2765703], dtype=object) + +or equivalently:: + + >>> table.array['Dec'] + array([17.15153360566, 17.15153360566, 17.15153360566, 17.1516686826, + 17.1516686826, 17.1516686826, 17.1536197136, 17.1536197136, + 17.1536197136, 17.15375479055, 17.15375479055, 17.15375479055, + 17.1553884541, 17.15539736932, 17.15539752176, + 17.25736014763, + # ... + 17.2765703], dtype=object) + +.. + EXAMPLE END + +Datatype Mappings +^^^^^^^^^^^^^^^^^ + +The datatype specified by a ``FIELD`` element is mapped to a ``numpy`` +type according to the following table: + + ================================ ========================= + VOTABLE type NumPy type + ================================ ========================= + boolean b1 + -------------------------------- ------------------------- + bit b1 + -------------------------------- ------------------------- + unsignedByte u1 + -------------------------------- ------------------------- + char (*variable length*) O - A ``bytes()`` object. + -------------------------------- ------------------------- + char (*fixed length*) S + -------------------------------- ------------------------- + unicodeChar (*variable length*) O - A `str` object + -------------------------------- ------------------------- + unicodeChar (*fixed length*) U + -------------------------------- ------------------------- + short i2 + -------------------------------- ------------------------- + int i4 + -------------------------------- ------------------------- + long i8 + -------------------------------- ------------------------- + float f4 + -------------------------------- ------------------------- + double f8 + -------------------------------- ------------------------- + floatComplex c8 + -------------------------------- ------------------------- + doubleComplex c16 + ================================ ========================= + +If the field is a fixed-size array, the data is stored as a ``numpy`` +fixed-size array. + +If the field is a variable-size array (that is, ``arraysize`` contains +a '*'), the cell will contain a Python list of ``numpy`` values. Each +value may be either an array or scalar depending on the ``arraysize`` +specifier. + +Examining Field Types +^^^^^^^^^^^^^^^^^^^^^ + +To look up more information about a field in a table, you can use the +`~astropy.io.votable.tree.TableElement.get_field_by_id` method, which returns +the `~astropy.io.votable.tree.Field` object with the given ID. + +.. + EXAMPLE START + Examining Field Types in VOTables with astropy.io.votable + +To look up more information about a field:: + + >>> field = table.get_field_by_id('Dec') + >>> field.datatype + 'char' + >>> field.unit + 'deg' + +.. note:: + Field descriptors should not be mutated. To change the set of + columns, convert the Table to an `astropy.table.Table`, make the + changes, and then convert it back. + +.. + EXAMPLE END + +Building a New Table from Scratch +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +It is also possible to build a new table, define some field datatypes, +and populate it with data. + +.. + EXAMPLE START + Building a New Table from a VOTable File + +To build a new table from a VOTable file:: + + from astropy.io.votable.tree import VOTableFile, Resource, TableElement, Field + + # Create a new VOTable file... + votable = VOTableFile() + + # ...with one resource... + resource = Resource() + votable.resources.append(resource) + + # ... with one table + table = TableElement(votable) + resource.tables.append(table) + + # Define some fields + table.fields.extend([ + Field(votable, name="filename", datatype="char", arraysize="*"), + Field(votable, name="matrix", datatype="double", arraysize="2x2")]) + + # Now, use those field definitions to create the numpy record arrays, with + # the given number of rows + table.create_arrays(2) + + # Now table.array can be filled with data + table.array[0] = ('test1.xml', [[1, 0], [0, 1]]) + table.array[1] = ('test2.xml', [[0.5, 0.3], [0.2, 0.1]]) + + # Now write the whole thing to a file. + # Note, we have to use the top-level votable file object + votable.to_xml("new_votable.xml") + +.. + EXAMPLE END + +Missing Values +^^^^^^^^^^^^^^ + +Any value in the table may be "missing". `astropy.io.votable` stores +a ``numpy`` masked array in each `~astropy.io.votable.tree.TableElement` +instance. This behaves like an ordinary ``numpy`` masked array, except +for variable-length fields. For those fields, the datatype of the +column is "object" and another ``numpy`` masked array is stored there. +Therefore, operations on variable-length columns will not work — this +is because variable-length columns are not directly supported +by ``numpy`` masked arrays. diff --git a/docs/known_issues.rst b/docs/known_issues.rst new file mode 100644 index 000000000000..382aa576c985 --- /dev/null +++ b/docs/known_issues.rst @@ -0,0 +1,339 @@ +************ +Known Issues +************ + +.. contents:: + :local: + :depth: 2 + +While most bugs and issues are managed using the `astropy issue +tracker `_, this document +lists issues that are too difficult to fix, may require some +intervention from the user to work around, or are caused by bugs in other +projects or packages. + +Issues listed on this page are grouped into two categories: The first is known +issues and shortcomings in actual algorithms and interfaces that currently do +not have fixes or workarounds, and that users should be aware of when writing +code that uses ``astropy``. Some of those issues are still platform-specific, +while others are very general. The second category is of common issues that come +up when configuring, building, or installing ``astropy``. This also includes +cases where the test suite can report false negatives depending on the context/ +platform on which it was run. + +Known Deficiencies +================== + +.. _quantity_issues: + +Quantities Lose Their Units with Some Operations +------------------------------------------------ + +Quantities are subclassed from ``numpy``'s `~numpy.ndarray` and while we have +ensured that ``numpy`` functions will work well with them, they do not always +work in functions from ``scipy`` or other packages that use ``numpy`` +internally, but ignore the subclass. Furthermore, at a few places in ``numpy`` +itself we cannot control the behaviour. For instance, care must be taken when +setting array slices using Quantities:: + + >>> import astropy.units as u + >>> import numpy as np + >>> a = np.ones(4) + >>> a[2:3] = 2*u.kg + >>> a # doctest: +FLOAT_CMP + array([1., 1., 2., 1.]) + +:: + + >>> a = np.ones(4) + >>> a[2:3] = 1*u.cm/u.m + >>> a # doctest: +FLOAT_CMP + array([1., 1., 1., 1.]) + +Either set single array entries or use lists of Quantities:: + + >>> a = np.ones(4) + >>> a[2] = 1*u.cm/u.m + >>> a # doctest: +FLOAT_CMP + array([1. , 1. , 0.01, 1. ]) + +:: + + >>> a = np.ones(4) + >>> a[2:3] = [1*u.cm/u.m] + >>> a # doctest: +FLOAT_CMP + array([1. , 1. , 0.01, 1. ]) + +Both will throw an exception if units do not cancel, e.g.:: + + >>> a = np.ones(4) + >>> a[2] = 1*u.cm + Traceback (most recent call last): + ... + TypeError: only dimensionless scalar quantities can be converted to Python scalars + + +See: https://github.com/astropy/astropy/issues/7582 + +Multiplying a `pandas.Series` with an `~astropy.units.Unit` does not produce a |Quantity| +----------------------------------------------------------------------------------------- + +Quantities may work with certain operations on `~pandas.Series` but +this behaviour is not tested. +For example, multiplying a `~pandas.Series` instance +with a unit will *not* return a |Quantity|. It will return a `~pandas.Series` +object without any unit: + +.. doctest-requires:: pandas>=2.0 + + >>> import pandas as pd + >>> import astropy.units as u + >>> a = pd.Series([1., 2., 3.]) + >>> a * u.m + 0 1.0 + 1 2.0 + 2 3.0 + dtype: float64 + +To avoid this, it is best to initialize the |Quantity| directly: + +.. doctest-requires:: pandas>=2.0 + + >>> u.Quantity(a, u.m) + + +Note that the overrides pandas provides are not complete, and +as a consequence, using the (in-place) shift operator does work: + +.. doctest-requires:: pandas>=2.0 + + >>> b = a << u.m + >>> b + + >>> a <<= u.m + >>> a + + +But this is fragile as this may stop working in future versions of +pandas if they decide to override the dunder methods. + +See: https://github.com/astropy/astropy/issues/11247 + +Using Numpy array creation functions to initialize Quantity +----------------------------------------------------------- +Trying the following example will ignore the unit: + + >>> np.full(10, 1 * u.m) + array([1., 1., 1., 1., 1., 1., 1., 1., 1., 1.]) + +However, the following works as one would expect + + >>> np.full(10, 1.0, like=u.Quantity([], u.m)) + + +and is equivalent to:: + + >>> np.full(10, 1) << u.m + + +`~numpy.zeros`, `~numpy.ones`, and `~numpy.empty` behave similarly. + +`~numpy.arange` also supports the ``like`` keyword argument + + >>> np.arange(0 * u.cm, 1 * u.cm, 1 * u.mm, like=u.Quantity([], u.cm)) + + +Also note that the unit of the output array is dictated by that of the ``stop`` +argument, and that, like for quantities generally, the data has a floating-point +dtype. If ``stop`` is a pure number, the unit of the output will default to that +of the ``like`` argument. + +As with ``~numpy.full`` and similar functions, one may alternatively move the +units outside of the call to `~numpy.arange`:: + + >>> np.arange(0, 10, 1) << u.mm + + +Or use `~numpy.linspace`: + + >>> np.linspace(0 * u.cm, 9 * u.mm, 10) + + + +Quantities Lose Their Units When Broadcasted +-------------------------------------------- + +When broadcasting Quantities, it is necessary to pass ``subok=True`` to +`~numpy.broadcast_to`, or else a bare `~numpy.ndarray` will be returned:: + + >>> q = u.Quantity(np.arange(10.), u.m) + >>> b = np.broadcast_to(q, (2, len(q))) + >>> b # doctest: +FLOAT_CMP + array([[0., 1., 2., 3., 4., 5., 6., 7., 8., 9.], + [0., 1., 2., 3., 4., 5., 6., 7., 8., 9.]]) + >>> b2 = np.broadcast_to(q, (2, len(q)), subok=True) + >>> b2 # doctest: +FLOAT_CMP + + +This is analogous to the case of passing a Quantity to `~numpy.array`:: + + >>> a = np.array(q) + >>> a # doctest: +FLOAT_CMP + array([0., 1., 2., 3., 4., 5., 6., 7., 8., 9.]) + >>> a2 = np.array(q, subok=True) + >>> a2 # doctest: +FLOAT_CMP + + +See: https://github.com/astropy/astropy/issues/7832 + +Chained Quantity comparisons to dimensionless zero can be misleading +-------------------------------------------------------------------- + +When chaining comparisons using Quantities and dimensionless zero, +the result may be misleading:: + + >>> 0 * u.Celsius == 0 * u.m # Correct + False + >>> 0 * u.Celsius == 0 == 0 * u.m # Misleading + np.True_ + +What the second comparison is really doing is this:: + + >>> (0 * u.Celsius == 0) and (0 == 0 * u.m) + np.True_ + +See: https://github.com/astropy/astropy/issues/15103 + +numpy.prod cannot be applied to Quantity +---------------------------------------- + +Using ``numpy.prod`` function on a Quantity would result in error. +This is because correctly implementing it for Quantity is fairly +difficult, since, unlike for most numpy functions, the result unit +depends on the shape of the input (rather than only on the units +of the inputs). + + >>> np.prod([1, 2, 3] * u.m) + Traceback (most recent call last): + ... + astropy.units.errors.UnitsError: Cannot use 'reduce' method on ufunc multiply with a Quantity instance as it would change the unit. + +See: https://github.com/astropy/astropy/issues/18429 + +def_unit should not be used for logarithmic unit +------------------------------------------------ + +When defining custom unit involving logarithmic unit, ``def_unit`` usage +should be avoided because it might result in surprising behavior:: + + >>> dBW = u.def_unit('dBW', u.dB(u.W)) + >>> 1 * dBW + Traceback (most recent call last): + ... + TypeError: unsupported operand type(s) for *: 'int' and 'Unit' + +Instead, it could be defined directly as such:: + + >>> dBW = u.dB(u.W) + >>> 1 * dBW + + +See: https://github.com/astropy/astropy/issues/5945 + +mmap Support for ``astropy.io.fits`` on GNU Hurd +------------------------------------------------ + +On Hurd and possibly other platforms, ``flush()`` on memory-mapped files are not +implemented, so writing changes to a mmap'd FITS file may not be reliable and is +thus disabled. Attempting to open a FITS file in writeable mode with mmap will +result in a warning (and mmap will be disabled on the file automatically). + +See: https://github.com/astropy/astropy/issues/968 + + +Color Printing on Windows +------------------------- + +Colored printing of log messages and other colored text does work in Windows, +but only when running in the IPython console. Colors are not currently +supported in the basic Python command-line interpreter on Windows. + +``numpy.int64`` does not decompose input ``Quantity`` objects +------------------------------------------------------------- + +Python's ``int()`` goes through ``__index__`` +while ``numpy.int64`` or ``numpy.int_`` do not go through ``__index__``. This +means that an upstream fix in NumPy is required in order for +``astropy.units`` to control decomposing the input in these functions:: + + >>> np.int64((15 * u.km) / (15 * u.imperial.foot)) + np.int64(1) + >>> np.int_((15 * u.km) / (15 * u.imperial.foot)) + np.int64(1) + >>> int((15 * u.km) / (15 * u.imperial.foot)) + 3280 + +To convert a dimensionless `~astropy.units.Quantity` to an integer, it is +therefore recommended to use ``int(...)``. + +Build/Installation/Test Issues +============================== + +Anaconda Users Should Upgrade with ``conda``, Not ``pip`` +--------------------------------------------------------- + +Upgrading ``astropy`` in the Anaconda Python distribution using ``pip`` can result +in a corrupted install with a mix of files from the old version and the new +version. Anaconda users should update with ``conda update astropy``. There +may be a brief delay between the release of ``astropy`` on PyPI and its release +via the ``conda`` package manager; users can check the availability of new +versions with ``conda search astropy``. + + +Locale Errors in MacOS X and Linux +---------------------------------- + +On MacOS X, you may see the following error when running ``pip``:: + + ... + ValueError: unknown locale: UTF-8 + +This is due to the ``LC_CTYPE`` environment variable being incorrectly set to +``UTF-8`` by default, which is not a valid locale setting. + +On MacOS X or Linux (or other platforms) you may also encounter the following +error:: + + ... + stderr = stderr.decode(stdio_encoding) + TypeError: decode() argument 1 must be str, not None + +This also indicates that your locale is not set correctly. + +To fix either of these issues, set this environment variable, as well as the +``LANG`` and ``LC_ALL`` environment variables to e.g. ``en_US.UTF-8`` using, in +the case of ``bash``:: + + export LANG="en_US.UTF-8" + export LC_ALL="en_US.UTF-8" + export LC_CTYPE="en_US.UTF-8" + +To avoid any issues in future, you should add this line to your e.g. +``~/.bash_profile`` or ``.bashrc`` file. + +To test these changes, open a new terminal and type ``locale``, and you should +see something like:: + + $ locale + LANG="en_US.UTF-8" + LC_COLLATE="en_US.UTF-8" + LC_CTYPE="en_US.UTF-8" + LC_MESSAGES="en_US.UTF-8" + LC_MONETARY="en_US.UTF-8" + LC_NUMERIC="en_US.UTF-8" + LC_TIME="en_US.UTF-8" + LC_ALL="en_US.UTF-8" + +If so, you can go ahead and try running ``pip`` again (in the new +terminal). diff --git a/docs/license.rst b/docs/license.rst new file mode 100644 index 000000000000..2e9c2f58b55e --- /dev/null +++ b/docs/license.rst @@ -0,0 +1,17 @@ +******** +Licenses +******** + +Astropy License +=============== + +Astropy is licensed under a 3-clause BSD style license: + +.. include:: ../LICENSE.rst + +Other Licenses +============== + +Full licenses for third-party software astropy is derived from or included +with Astropy can be found in the ``'licenses/'`` directory of the source +code distribution. diff --git a/docs/logging.rst b/docs/logging.rst index a0a4a649b12d..1c782c95ae01 100644 --- a/docs/logging.rst +++ b/docs/logging.rst @@ -2,6 +2,11 @@ Logging system ************** +.. note:: + + The Astropy logging system is meant for internal ``astropy`` usage. For use + in other packages, we recommend implementing your own logger instead. + Overview ======== @@ -24,7 +29,7 @@ levels: * ERROR: indicates a more serious issue, including exceptions -By default, only WARNING and ERROR messages are displayed, and are sent to a +By default, INFO, WARNING and ERROR messages are displayed, and are sent to a log file located at ``~/.astropy/astropy.log`` (if the file is writeable). Configuring the logging system @@ -36,11 +41,15 @@ First, import the logger:: The threshold level (defined above) for messages can be set with e.g.:: - log.setLevel('INFO') + log.setLevel('DEBUG') Color (enabled by default) can be disabled with:: - log.setColor(False) + log.disable_color() + +and enabled with:: + + log.enable_color() Warnings from ``warnings.warn`` can be logged with:: @@ -52,7 +61,7 @@ which can be disabled with:: and exceptions can be included in the log with:: - log.set_exception_logging() + log.enable_exception_logging() which can be disabled with:: @@ -111,10 +120,10 @@ emitted in the ``astropy.wcs`` sub-package. Using the configuration file ============================ -Options for the logger can be set in the ``[config.logging_helper]`` section +Options for the logger can be set in the ``[logger]`` section of the Astropy configuration file:: - [config.logging_helper] + [logger] # Threshold for the logging messages. Logging messages that are less severe # than this level will be ignored. The levels are 'DEBUG', 'INFO', 'WARNING', @@ -133,7 +142,8 @@ of the Astropy configuration file:: # Whether to always log messages to a log file log_to_file = True - # The file to log messages to + # The file to log messages to. If empty string is given, it defaults to a + # file `astropy.log` in the astropy config directory. log_file_path = '~/.astropy/astropy.log' # Threshold for logging messages to log_file_path @@ -142,10 +152,13 @@ of the Astropy configuration file:: # Format for log file entries log_file_format = '%(asctime)s, %(origin)s, %(levelname)s, %(message)s' + # The encoding (e.g., UTF-8) to use for the log file. If empty string is + # given, it defaults to the platform-preferred encoding. + log_file_encoding = "" + Reference/API ============= .. automodapi:: astropy.logger :no-inheritance-diagram: - diff --git a/docs/logo/astropy_banner.svg b/docs/logo/astropy_banner.svg deleted file mode 100644 index 4b7c9ce3aab1..000000000000 --- a/docs/logo/astropy_banner.svg +++ /dev/null @@ -1,298 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - astropyA - - - - - - - - - Community A Community Python Library for Astronomy - diff --git a/docs/logo/astropy_banner_96.png b/docs/logo/astropy_banner_96.png deleted file mode 100644 index 89ca2cef2f2d..000000000000 Binary files a/docs/logo/astropy_banner_96.png and /dev/null differ diff --git a/docs/logo/astropy_docs_32.png b/docs/logo/astropy_docs_32.png deleted file mode 100644 index 9adf55625ba4..000000000000 Binary files a/docs/logo/astropy_docs_32.png and /dev/null differ diff --git a/docs/logo/astropy_linkout.svg b/docs/logo/astropy_linkout.svg deleted file mode 100644 index 3a9a4e40101b..000000000000 --- a/docs/logo/astropy_linkout.svg +++ /dev/null @@ -1,221 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - astropy A Community - - - - diff --git a/docs/logo/astropy_linkout_20.png b/docs/logo/astropy_linkout_20.png deleted file mode 100644 index 857cc7e78364..000000000000 Binary files a/docs/logo/astropy_linkout_20.png and /dev/null differ diff --git a/docs/logo/astropy_logo.ico b/docs/logo/astropy_logo.ico deleted file mode 100644 index 652a4ed29416..000000000000 Binary files a/docs/logo/astropy_logo.ico and /dev/null differ diff --git a/docs/logo/astropy_logo_32.png b/docs/logo/astropy_logo_32.png deleted file mode 100644 index d0e320da63a8..000000000000 Binary files a/docs/logo/astropy_logo_32.png and /dev/null differ diff --git a/docs/logo/astropy_logo_64.png b/docs/logo/astropy_logo_64.png deleted file mode 100644 index 691b3cb66edf..000000000000 Binary files a/docs/logo/astropy_logo_64.png and /dev/null differ diff --git a/docs/logo/astropy_logoabovename_200.png b/docs/logo/astropy_logoabovename_200.png deleted file mode 100644 index 1386310897d9..000000000000 Binary files a/docs/logo/astropy_logoabovename_200.png and /dev/null differ diff --git a/docs/logo/astropy_logoandname.svg b/docs/logo/astropy_logoandname.svg deleted file mode 100644 index be76766cb056..000000000000 --- a/docs/logo/astropy_logoandname.svg +++ /dev/null @@ -1,290 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - astropyA - - - - - - - - - Community - diff --git a/docs/logo/astropy_logoandname_26.png b/docs/logo/astropy_logoandname_26.png deleted file mode 100644 index 96797a55f5b2..000000000000 Binary files a/docs/logo/astropy_logoandname_26.png and /dev/null differ diff --git a/docs/logo/astropy_name_26.png b/docs/logo/astropy_name_26.png deleted file mode 100644 index a3b77f1ec50f..000000000000 Binary files a/docs/logo/astropy_name_26.png and /dev/null differ diff --git a/docs/logo/credits b/docs/logo/credits deleted file mode 100644 index 7c57fc6408a2..000000000000 --- a/docs/logo/credits +++ /dev/null @@ -1 +0,0 @@ -The Astropy logo was designed by Kyle Barbary diff --git a/docs/lts_policy.rst b/docs/lts_policy.rst new file mode 100644 index 000000000000..45b6803fb1e9 --- /dev/null +++ b/docs/lts_policy.rst @@ -0,0 +1,7 @@ +******************* +LTS Backport Policy +******************* + +Starting with astropy v6.0.0, there will be no more designated Long-Term Stable +(LTS) releases of astropy - see `APE 21 +`_ for more details. diff --git a/docs/make.bat b/docs/make.bat index 93dfe92b9c98..aa930134b5de 100644 --- a/docs/make.bat +++ b/docs/make.bat @@ -37,6 +37,8 @@ if "%1" == "help" ( if "%1" == "clean" ( for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i del /q /s %BUILDDIR%\* + del /q /s api + del /q /s generated goto end ) diff --git a/docs/modeling/add-units.rst b/docs/modeling/add-units.rst new file mode 100644 index 000000000000..9183e74405a8 --- /dev/null +++ b/docs/modeling/add-units.rst @@ -0,0 +1,124 @@ +.. _add_units: + +Adding support for units in a model (Advanced) +============================================== + +Evaluation +---------- + +To make it so that your models can accept parameters with units and be evaluated +using inputs with units, you need to make sure that the +:meth:`~astropy.modeling.Model.evaluate` method works correctly with +input values and parameters with units. For simple arithmetic, this may work +out of the box since :class:`~astropy.units.Quantity` objects are understood by +a number of Numpy functions. + +If users of your models provide input during evaluation that is not compatible +with the parameter units, they may get cryptic errors such as:: + + UnitsError : Can only apply 'subtract' function to dimensionless quantities + when other argument is not a quantity (unless the latter is all + zero/infinity/nan) + +There are several attributes or properties that can be set on models that adjust +the behavior of models with units. These attributes can be changed from the +defaults in the class definition, e.g.:: + + class MyModel(Model): + input_units = {'x': u.deg} + ... + +Note that these are all optional. + +.. _models_input_units: + +``input_units`` +^^^^^^^^^^^^^^^ + +You can easily add checking of the input units by adding an ``input_units`` +property or attribute on your model class. This should return either `None` (to +indicate no constraints) or a dictionary where the keys are the input names +(e.g. ``x`` for many 1D models) and the values are the units expected, which can +be a function of the parameter units:: + + @property + def input_units(self): + if self.mean.unit is None: + return None + else: + return {'x': self.mean.unit} + +If the user then gives values with incorrect input units, a clear error will be +displayed:: + + UnitsError: Units of input 'x', (dimensionless), could not be converted to + required input units of m (length) + +Note that the input units don't have to match exactly those returned by +``input_units``, but be convertible to them. In addition, ``input_units`` can +also be specified as an attribute rather than a property in simple cases:: + + input_units = {'x': u.deg} + +.. _models_return_units: + +``return_units`` +^^^^^^^^^^^^^^^^ + +Similarly to :ref:`models_input_units`, this should be dictionary that maps the return +values of a model to units. If :meth:`~astropy.modeling.Model.evaluate` was called +with quantities but returns unitless values, the units are added to the output. +If the return values are quantities in different units, they are converted to +``return_units``. + +``input_units_strict`` +^^^^^^^^^^^^^^^^^^^^^^ + +If set to `True`, values that are passed in compatible units will be converted +to the exact units specified in ``input_units``. + +This attribute can also be a +dictionary that maps input names to a Boolean to enable converting of that input +to the specified unit. + +``input_units_equivalencies`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +This can be set to a dictionary that maps the input names to a list of +equivalencies, for example:: + + input_units_equivalencies = {'nu': u.spectral()} + +``_input_units_allow_dimensionless`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If set to `True`, values that are plain scalars or Numpy arrays can be passed to +evaluate even if ``input_units`` specifies that the input should have units. It +is up to the :meth:`~astropy.modeling.Model.evaluate` to then decide how to +handle these dimensionless values. This attribute can also be a dictionary that +maps input names to a Boolean to enable passing dimensionless values to +:meth:`~astropy.modeling.Model.evaluate` for that input. + + +Fitting +------- + +To allow models with parameters that have units to be fitted to data with units, +you will need to add a method called ``_parameter_units_for_data_units`` to your +model class. This should take two arguments ``input_units`` and +``output_units`` - ``input_units`` will be set to a dictionary with +the units of the independent variables in the data, while ``output_units`` will +be set to a dictionary with the units the dependent variables in the data (for +example, for a simple 1D model, ``input_units`` will have one key, ``x``, and +``output_units`` will have one key, ``y``). This method should then return +a dictionary giving for each parameter the units the parameter should be +converted to so that the model could be used on the data if units were removed +from both the models and the data. The following example shows the +implementation for the 1D Gaussian:: + + def _parameter_units_for_data_units(self, inputs_unit, outputs_unit): + return {'mean': inputs_unit['x'], + 'stddev': inputs_unit['x'], + 'amplitude': outputs_unit['y']} + +With this method in place, the model can then be fit to data that has units. diff --git a/docs/modeling/compound-models.rst b/docs/modeling/compound-models.rst new file mode 100644 index 000000000000..55574d7fe45b --- /dev/null +++ b/docs/modeling/compound-models.rst @@ -0,0 +1,1438 @@ +.. include:: links.inc + +.. _compound-models-intro: + +Combining Models +**************** + +Basics +====== + +While the Astropy modeling package makes it very easy to define :doc:`new +models ` either from existing functions, or by writing a +`~astropy.modeling.Model` subclass, an additional way to create new models is +by combining them using arithmetic expressions. This works with models built +into Astropy, and most user-defined models as well. For example, it is +possible to create a superposition of two Gaussians like so:: + + >>> from astropy.modeling import models + >>> g1 = models.Gaussian1D(1, 0, 0.2) + >>> g2 = models.Gaussian1D(2.5, 0.5, 0.1) + >>> g1_plus_2 = g1 + g2 + +The resulting object ``g1_plus_2`` is itself a new model. + +.. note:: + The model ``g1_plus_2`` is a `~astropy.modeling.CompoundModel` which contains + the models ``g1`` and ``g2`` without any parameter duplication. Meaning changes + to the parameters of ``g1_plus_2`` will affect the parameters of ``g1`` or ``g2`` + and vice versa; if one does not want this to occur one can copy the models prior + to adding them using the ``.copy()`` method ``g1.copy() + g2.copy()``. In + general this applies to any `~astropy.modeling.CompoundModel` constructed using a + binary operation, so that `~astropy.modeling.CompoundModel` follows the Python + convention for construction of container objects. For more information on this + please see the `API Changes in astropy.modeling `__ + +Evaluating, say, ``g1_plus_2(0.25)`` is the same as evaluating ``g1(0.25) + g2(0.25)``:: + + >>> g1_plus_2(0.25) # doctest: +FLOAT_CMP + 0.5676756958301329 + >>> g1_plus_2(0.25) == g1(0.25) + g2(0.25) + True + +This model can be further combined with other models in new expressions. + +These new compound models can also be fitted to data, like most other models +(though this currently requires one of the non-linear fitters): + +.. plot:: + :include-source: + + import warnings + import numpy as np + import matplotlib.pyplot as plt + from astropy.modeling import models, fitting + + # Generate fake data + rng = np.random.default_rng(seed=42) + g1 = models.Gaussian1D(1, 0, 0.2) + g2 = models.Gaussian1D(2.5, 0.5, 0.1) + x = np.linspace(-1, 1, 200) + y = g1(x) + g2(x) + rng.normal(0., 0.2, x.shape) + + # Now to fit the data create a new superposition with initial + # guesses for the parameters: + gg_init = models.Gaussian1D(1, 0, 0.1) + models.Gaussian1D(2, 0.5, 0.1) + fitter = fitting.SLSQPLSQFitter() + + with warnings.catch_warnings(): + # Ignore a warning on clipping to bounds from the fitter + warnings.filterwarnings('ignore', message='Values in x were outside bounds', + category=RuntimeWarning) + gg_fit = fitter(gg_init, x, y) + + # Plot the data with the best-fit model + fig, ax = plt.subplots(figsize=(8, 5)) + ax.plot(x, y, 'ko') + ax.plot(x, gg_fit(x)) + ax.set(xlabel='Position', ylabel='Flux') + +This works for 1-D models, 2-D models, and combinations thereof, though there +are some complexities involved in correctly matching up the inputs and outputs +of all models used to build a compound model. You can learn more details in +the :doc:`compound-models` documentation. + +Astropy models also support convolution through the function +`~astropy.convolution.convolve_models`, which returns a compound model. + +For instance, the convolution of two Gaussian functions is also a Gaussian +function in which the resulting mean position is the average of the +mean positions and the variance is the sum of the variances. + +.. plot:: + :include-source: + + import numpy as np + import matplotlib.pyplot as plt + from astropy.modeling import models + from astropy.convolution import convolve_models + + g1 = models.Gaussian1D(1, -1, 1) + g2 = models.Gaussian1D(1, 1, 1) + g3 = convolve_models(g1, g2) + + x = np.linspace(-3, 3, 50) + fig, ax = plt.subplots(figsize=(8, 5)) + ax.plot(x, g1(x), label='g1') + ax.plot(x, g2(x), label='g2') + ax.plot(x, g3(x), label='g3 (Convolution)') + ax.legend() + + +.. _compound-models: + +A comprehensive description +=========================== + +Some terminology +---------------- + +It is possible to create new models just by +combining existing models using the arithmetic operators ``+``, ``-``, ``*``, +``/``, and ``**``, or by model composition using ``|`` and +concatenation (explained below) with ``&``, as well as using :func:`~astropy.modeling.fix_inputs` +for :ref:`reducing the number of inputs to a model `. + + +In discussing the compound model feature, it is useful to be clear about a +few terms where there have been points of confusion: + +- The term "model" can refer either to a model *class* or a model *instance*. + + - All models in `astropy.modeling`, whether it represents some + `function `, a + `rotation `, etc., are represented in the + abstract by a model *class* --- specifically a subclass of + `~astropy.modeling.Model` --- that encapsulates the routine for + evaluating the model, a list of its required parameters, and other + metadata about the model. + + - Per typical object-oriented parlance, a model *instance* is the object + created when calling a model class with some arguments --- in most + cases values for the model's parameters. + + A model class, by itself, cannot be used to perform any computation because + most models, at least, have one or more parameters that must be specified + before the model can be evaluated on some input data. However, we can still + get some information about a model class from its representation. For + example:: + + >>> from astropy.modeling.models import Gaussian1D + >>> Gaussian1D + + Name: Gaussian1D + N_inputs: 1 + N_outputs: 1 + Fittable parameters: ('amplitude', 'mean', 'stddev') + + We can then create a model *instance* by passing in values for the three + parameters:: + + >>> my_gaussian = Gaussian1D(amplitude=1.0, mean=0, stddev=0.2) + >>> my_gaussian # doctest: +FLOAT_CMP + + + We now have an *instance* of `~astropy.modeling.functional_models.Gaussian1D` + with all its parameters (and in principle other details like fit constraints) + filled in so that we can perform calculations with it as though it were a + function:: + + >>> my_gaussian(0.2) # doctest: +FLOAT_CMP + 0.6065306597126334 + + In many cases this document just refers to "models", where the class/instance + distinction is either irrelevant or clear from context. But a distinction + will be made where necessary. + +- A *compound model* can be created by combining two or more existing model instances + which can be models that come with Astropy, :doc:`user defined models `, or + other compound models --- using Python expressions consisting of one or more of the + supported binary operators. + +- In some places the term *composite model* is used interchangeably with + *compound model*. However, this document uses the + term *composite model* to refer *only* to the case of a compound model + created from the functional composition of two or more models using the pipe + operator ``|`` as explained below. This distinction is used consistently + within this document, but it may be helpful to understand the distinction. + + +Creating compound models +------------------------ + +The only way to create compound models is +to combine existing single models and/or compound models using expressions in +Python with the binary operators ``+``, ``-``, ``*``, ``/``, ``**``, ``|``, +and ``&``, each of which is discussed in the following sections. + +The result of combining two models is a model instance:: + + >>> two_gaussians = Gaussian1D(1.1, 0.1, 0.2) + Gaussian1D(2.5, 0.5, 0.1) + >>> two_gaussians # doctest: +FLOAT_CMP + + +This expression creates a new model instance that is ready to be used for evaluation:: + + >>> two_gaussians(0.2) # doctest: +FLOAT_CMP + 0.9985190841886609 + +The ``print`` function provides more information about this object:: + + >>> print(two_gaussians) + Model: CompoundModel... + Inputs: ('x',) + Outputs: ('y',) + Model set size: 1 + Expression: [0] + [1] + Components: + [0]: + + [1]: + Parameters: + amplitude_0 mean_0 stddev_0 amplitude_1 mean_1 stddev_1 + ----------- ------ -------- ----------- ------ -------- + 1.1 0.1 0.2 2.5 0.5 0.1 + +There are a number of things to point out here: This model has six +fittable parameters. How parameters are handled is discussed further in the +section on :ref:`compound-model-parameters`. We also see that there is a +listing of the *expression* that was used to create this compound model, which +in this case is summarized as ``[0] + [1]``. The ``[0]`` and ``[1]`` refer to +the first and second components of the model listed next (in this case both +components are the `~astropy.modeling.functional_models.Gaussian1D` objects). + +Each component of a compound model is a single, non-compound model. This is +the case even when including an existing compound model in a new expression. +The existing compound model is not treated as a single model --- instead the +expression represented by that compound model is extended. An expression +involving two or more compound models results in a new expression that is the +concatenation of all involved models' expressions:: + + >>> four_gaussians = two_gaussians + two_gaussians + >>> print(four_gaussians) + Model: CompoundModel... + Inputs: ('x',) + Outputs: ('y',) + Model set size: 1 + Expression: [0] + [1] + [2] + [3] + Components: + [0]: + + [1]: + + [2]: + + [3]: + Parameters: + amplitude_0 mean_0 stddev_0 amplitude_1 ... stddev_2 amplitude_3 mean_3 stddev_3 + ----------- ------ -------- ----------- ... -------- ----------- ------ -------- + 1.1 0.1 0.2 2.5 ... 0.2 2.5 0.5 0.1 + + +Operators +--------- + +Arithmetic operators +-------------------- + +Compound models can be created from expressions that include any +number of the arithmetic operators ``+``, ``-``, ``*``, ``/``, and +``**``, which have the same meanings as they do for other numeric +objects in Python. + +.. note:: + + In the case of division ``/`` always means floating point division + --- integer division and the ``//`` operator is not supported for + models. + +As demonstrated in previous examples, for models that have a single output +the result of evaluating a model like ``A + B`` is to evaluate ``A`` and +``B`` separately on the given input, and then return the sum of the outputs of +``A`` and ``B``. This requires that ``A`` and ``B`` take the same number of +inputs and both have a single output. + +It is also possible to use arithmetic operators between models with multiple +outputs. Again, the number of inputs must be the same between the models, as +must be the number of outputs. In this case the operator is applied to the +operators element-wise, similarly to how arithmetic operators work on two Numpy +arrays. + + +.. _compound-model-composition: + +Model composition +----------------- + +The sixth binary operator that can be used to create compound models is the +composition operator, also known as the "pipe" operator ``|`` (not to be +confused with the boolean "or" operator that this implements for Python numeric +objects). A model created with the composition operator like ``M = F | G``, +when evaluated, is equivalent to evaluating :math:`g \circ f = g(f(x))`. + +.. note:: + + The fact that the ``|`` operator has the opposite sense as the functional + composition operator :math:`\circ` is sometimes a point of confusion. + This is in part because there is no operator symbol supported in Python + that corresponds well to this. The ``|`` operator should instead be read + like the `pipe operator + `_ of UNIX shell syntax: + It chains together models by piping the output of the left-hand operand to + the input of the right-hand operand, forming a "pipeline" of models, or + transformations. + +This has different requirements on the inputs/outputs of its operands than do +the arithmetic operators. For composition all that is required is that the +left-hand model has the same number of outputs as the right-hand model has +inputs. + +For simple functional models this is exactly the same as functional +composition, except for the aforementioned caveat about ordering. For +example, to create the following compound model: + +.. graphviz:: + + digraph { + in0 [shape="none", label="input 0"]; + out0 [shape="none", label="output 0"]; + redshift0 [shape="box", label="RedshiftScaleFactor"]; + gaussian0 [shape="box", label="Gaussian1D(1, 0.75, 0.1)"]; + + in0 -> redshift0; + redshift0 -> gaussian0; + gaussian0 -> out0; + } + +.. plot:: + :include-source: + + import numpy as np + import matplotlib.pyplot as plt + from astropy.modeling.models import RedshiftScaleFactor, Gaussian1D + + x = np.linspace(0, 1.2, 100) + g0 = RedshiftScaleFactor(0) | Gaussian1D(1, 0.75, 0.1) + + fig, ax = plt.subplots(figsize=(8, 5)) + ax.plot(x, g0(x), 'g--', label='$z=0$') + + for z in (0.2, 0.4, 0.6): + g = RedshiftScaleFactor(z) | Gaussian1D(1, 0.75, 0.1) + ax.plot(x, g(x), color=plt.cm.OrRd(z), + label=f'$z={z}$') + + ax.set(xlabel='Energy', ylabel='Flux') + ax.legend() + +If you wish to perform redshifting in the wavelength space instead of energy, +and would also like to conserve flux, here is another way to do it using +model *instances*: + +.. plot:: + :include-source: + + import numpy as np + import matplotlib.pyplot as plt + from astropy.modeling.models import RedshiftScaleFactor, Gaussian1D, Scale + + x = np.linspace(1000, 5000, 1000) + g0 = Gaussian1D(1, 2000, 200) # No redshift is same as redshift with z=0 + + fig, ax = plt.subplots(figsize=(8, 5)) + ax.plot(x, g0(x), 'g--', label='$z=0$') + + for z in (0.2, 0.4, 0.6): + rs = RedshiftScaleFactor(z).inverse # Redshift in wavelength space + sc = Scale(1. / (1 + z)) # Rescale the flux to conserve energy + g = rs | g0 | sc + ax.plot(x, g(x), color=plt.cm.OrRd(z), + label=f'$z={z}$') + + ax.set(xlabel='Wavelength', ylabel='Flux') + ax.legend() + +When working with models with multiple inputs and outputs, the same idea +applies. If each input is thought of as a coordinate axis, then this defines a +pipeline of transformations for the coordinates on each axis (though it does +not necessarily guarantee that these transformations are separable). For +example: + +.. graphviz:: + + digraph { + in0 [shape="none", label="input 0"]; + in1 [shape="none", label="input 1"]; + out0 [shape="none", label="output 0"]; + out1 [shape="none", label="output 1"]; + rot0 [shape="box", label="Rotation2D"]; + gaussian0 [shape="box", label="Gaussian2D(1, 0, 0, 0.1, 0.3)"]; + + in0 -> rot0; + in1 -> rot0; + rot0 -> gaussian0; + rot0 -> gaussian0; + gaussian0 -> out0; + gaussian0 -> out1; + } + +.. plot:: + :include-source: + + import numpy as np + import matplotlib.pyplot as plt + from astropy.modeling.models import Rotation2D, Gaussian2D + + x, y = np.mgrid[-1:1:0.01, -1:1:0.01] + + fig, axs = plt.subplots(figsize=(8, 2.5), ncols=3) + + for idx, (theta, ax) in enumerate(zip((0, 45, 90), axs)): + g = Rotation2D(theta) | Gaussian2D(1, 0, 0, 0.1, 0.3) + ax.imshow(g(x, y), origin='lower') + ax.set(xticks=[], yticks=[], title=rf'Rotated $ {theta}^\circ $') + +.. note:: + + The above example is a bit contrived in that + `~astropy.modeling.functional_models.Gaussian2D` already supports an + optional rotation parameter. However, this demonstrates how coordinate + rotation could be added to arbitrary models. + +Normally it is not possible to compose, say, a model with two outputs and a +function of only one input:: + + >>> from astropy.modeling.models import Rotation2D + >>> Rotation2D() | Gaussian1D() # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + ModelDefinitionError: Unsupported operands for |: Rotation2D (n_inputs=2, n_outputs=2) and Gaussian1D (n_inputs=1, n_outputs=1); n_outputs for the left-hand model must match n_inputs for the right-hand model. + +However, as we will see in the next section, +:ref:`compound-model-concatenation`, provides a means of creating models +that apply transformations to only some of the outputs from a model, +especially when used in concert with :ref:`mappings `. + + +.. _compound-model-concatenation: + +Model concatenation +------------------- + +The concatenation operator ``&``, sometimes also referred to as a "join", +combines two models into a single, fully separable transformation. That is, it +makes a new model that takes the inputs to the left-hand model, concatenated +with the inputs to the right-hand model, and returns a tuple consisting of the +two models' outputs concatenated together, without mixing in any way. In other +words, it simply evaluates the two models in parallel --- it can be thought of as +something like a tuple of models. + +For example, given two coordinate axes, we can scale each coordinate +by a different factor by concatenating two +`~astropy.modeling.functional_models.Scale` models. + +.. graphviz:: + + digraph { + in0 [shape="none", label="input 0"]; + in1 [shape="none", label="input 1"]; + out0 [shape="none", label="output 0"]; + out1 [shape="none", label="output 1"]; + scale0 [shape="box", label="Scale(factor=1.2)"]; + scale1 [shape="box", label="Scale(factor=3.4)"]; + + in0 -> scale0; + scale0 -> out0; + + in1 -> scale1; + scale1 -> out1; + } + +:: + + >>> from astropy.modeling.models import Scale + >>> separate_scales = Scale(factor=1.2) & Scale(factor=3.4) + >>> separate_scales(1, 2) # doctest: +FLOAT_CMP + (1.2, 6.8) + +We can also combine concatenation with composition to build chains of +transformations that use both "1D" and "2D" models on two (or more) coordinate +axes: + +.. graphviz:: + + digraph { + in0 [shape="none", label="input 0"]; + in1 [shape="none", label="input 1"]; + out0 [shape="none", label="output 0"]; + out1 [shape="none", label="output 1"]; + scale0 [shape="box", label="Scale(factor=1.2)"]; + scale1 [shape="box", label="Scale(factor=3.4)"]; + rot0 [shape="box", label="Rotation2D(90)"]; + + in0 -> scale0; + scale0 -> rot0; + + in1 -> scale1; + scale1 -> rot0; + + rot0 -> out0; + rot0 -> out1; + } + +:: + + >>> scale_and_rotate = ((Scale(factor=1.2) & Scale(factor=3.4)) | + ... Rotation2D(90)) + >>> scale_and_rotate.n_inputs + 2 + >>> scale_and_rotate.n_outputs + 2 + >>> scale_and_rotate(1, 2) # doctest: +FLOAT_CMP + (-6.8, 1.2) + +This is of course equivalent to an +`~astropy.modeling.projections.AffineTransformation2D` with the appropriate +transformation matrix:: + + >>> from numpy import allclose + >>> from astropy.modeling.models import AffineTransformation2D + >>> affine = AffineTransformation2D(matrix=[[0, -3.4], [1.2, 0]]) + >>> # May be small numerical differences due to different implementations + >>> allclose(scale_and_rotate(1, 2), affine(1, 2)) + True + +Other Topics +============ + +Model names +----------- + +In the above two examples another notable feature of the generated compound +model classes is that the class name, as displayed when printing the class at +the command prompt, is not "TwoGaussians", "FourGaussians", etc. Instead it is +a generated name consisting of "CompoundModel" followed by an essentially +arbitrary integer that is chosen simply so that every compound model has a +unique default name. This is a limitation at present, due to the limitation +that it is not generally possible in Python when an object is created by an +expression for it to "know" the name of the variable it will be assigned to, if +any. +It is possible to directly assign a name to the compound model instance +by using the `Model.name ` attribute:: + + >>> two_gaussians.name = "TwoGaussians" + >>> print(two_gaussians) # doctest: +SKIP + Model: CompoundModel... + Name: TwoGaussians + Inputs: ('x',) + Outputs: ('y',) + Model set size: 1 + Expression: [0] + [1] + Components: + [0]: + + [1]: + Parameters: + amplitude_0 mean_0 stddev_0 amplitude_1 mean_1 stddev_1 + ----------- ------ -------- ----------- ------ -------- + 1.1 0.1 0.2 2.5 0.5 0.1 + +.. _compound-model-indexing: + +Indexing and slicing +-------------------- + +As seen in some of the previous examples in this document, when creating a +compound model each component of the model is assigned an integer index +starting from zero. These indices are assigned simply by reading the +expression that defined the model, from left to right, regardless of the order +of operations. For example:: + + >>> from astropy.modeling.models import Const1D + >>> A = Const1D(1.1, name='A') + >>> B = Const1D(2.1, name='B') + >>> C = Const1D(3.1, name='C') + >>> M = A + B * C + >>> print(M) + Model: CompoundModel... + Inputs: ('x',) + Outputs: ('y',) + Model set size: 1 + Expression: [0] + [1] * [2] + Components: + [0]: + + [1]: + + [2]: + Parameters: + amplitude_0 amplitude_1 amplitude_2 + ----------- ----------- ----------- + 1.1 2.1 3.1 + + +In this example the expression is evaluated ``(B * C) + A`` --- that is, the +multiplication is evaluated before the addition per usual arithmetic rules. +However, the components of this model are simply read off left to right from +the expression ``A + B * C``, with ``A -> 0``, ``B -> 1``, ``C -> 2``. If we +had instead defined ``M = C * B + A`` then the indices would be reversed +(though the expression is mathematically equivalent). This convention is +chosen for simplicity --- given the list of components it is not necessary to +jump around when mentally mapping them to the expression. + +We can pull out each individual component of the compound model ``M`` by using +indexing notation on it. Following from the above example, ``M[1]`` should +return the model ``B``:: + + >>> M[1] + + +We can also take a *slice* of the compound model. This returns a new compound +model that evaluates the *subexpression* involving the models selected by the +slice. This follows the same semantics as slicing a `list` or array in Python. +The start point is inclusive and the end point is exclusive. So a slice like +``M[1:3]`` (or just ``M[1:]``) selects models ``B`` and ``C`` (and all +*operators* between them). So the resulting model evaluates just the +subexpression ``B * C``:: + + >>> print(M[1:]) + Model: CompoundModel + Inputs: ('x',) + Outputs: ('y',) + Model set size: 1 + Expression: [0] * [1] + Components: + [0]: + + [1]: + Parameters: + amplitude_0 amplitude_1 + ----------- ----------- + 2.1 3.1 + +.. note:: + + There is a change in the parameter names of a slice from versions + prior to 4.0. Previously, the parameter names were identical to that + of the model being sliced. Now, they are what is expected for a + compound model of this type apart from the model sliced. That is, + the sliced model always starts with its own relative index for its + components, thus the parameter names start with a 0 suffix. + +.. note:: + + Starting with 4.0, the behavior of slicing is more restrictive than + previously. For example if:: + + m = m1 * m2 + m3 + + and one sliced by + using ``m[1:3]``, previously that would return the model: ``m2 + m3`` + even though there was never any such submodel of m. Starting with 4.0 + a slice must correspond to a submodel (something that corresponds + to an intermediate result of the computational chain of evaluating + the compound model). So:: + + m1 * m2 + + is a submodel (i.e.,``m[:2]``) but + ``m[1:3]`` is not. Currently this also means that in simpler expressions + such as:: + + m = m1 + m2 + m3 + m4 + + where any slice should be valid in + principle, only slices that include m1 are valid since it is part of + all submodules. The order of evaluation is:: + + ((m1 + m2) + m3) + m4 + + Anyone creating compound models that wishes submodels to be available + is advised to use parentheses explicitly or define intermediate + models to be used in subsequent expressions so that they can be + extracted with a slice or simple index depending on the context. + For example, to make ``m2 + m3`` accessible by slice, define ``m`` as:: + + m = m1 + (m2 + m3) + m4. In this case ``m[1:3]`` will work. + +The new compound model for the subexpression can be evaluated +like any other:: + + >>> M[1:](0) # doctest: +FLOAT_CMP + 6.51 + +Although the model ``M`` was composed entirely of ``Const1D`` models in this +example, it was useful to give each component a unique name (``A``, ``B``, +``C``) in order to differentiate between them. This can also be used for +indexing and slicing:: + + >>> print(M['B']) + Model: Const1D + Name: B + Inputs: ('x',) + Outputs: ('y',) + Model set size: 1 + Parameters: + amplitude + --------- + 2.1 + + +In this case ``M['B']`` is equivalent to ``M[1]``. But by using the name we do +not have to worry about what index that component is in (this becomes +especially useful when combining multiple compound models). A current +limitation, however, is that each component of a compound model must have a +unique name --- if some components have duplicate names then they can only be +accessed by their integer index. + +Slicing also works with names. When using names the start and end points are +*both inclusive*:: + + >>> print(M['B':'C']) + Model: CompoundModel... + Inputs: ('x',) + Outputs: ('y',) + Model set size: 1 + Expression: [0] * [1] + Components: + [0]: + + [1]: + Parameters: + amplitude_0 amplitude_1 + ----------- ----------- + 2.1 3.1 + +So in this case ``M['B':'C']`` is equivalent to ``M[1:3]``. + +.. _compound-model-parameters: + +Parameters +---------- + +A question that frequently comes up when first encountering compound models is +how exactly all the parameters are dealt with. By now we've seen a few +examples that give some hints, but a more detailed explanation is in order. +This is also one of the biggest areas for possible improvements --- the current +behavior is meant to be practical, but is not ideal. (Some possible +improvements include being able to rename parameters, and providing a means of +narrowing down the number of parameters in a compound model.) + +As explained in the general documentation for model :ref:`parameters +`, every model has an attribute called +`~astropy.modeling.Model.param_names` that contains a tuple of all the model's +adjustable parameters. These names are given in a canonical order that also +corresponds to the order in which the parameters should be specified when +instantiating the model. + +The simple scheme used currently for naming parameters in a compound model is +this: The ``param_names`` from each component model are concatenated with each +other in order from left to right as explained in the section on +:ref:`compound-model-indexing`. However, each parameter name is appended with +``_<#>``, where ``<#>`` is the index of the component model that parameter +belongs to. For example:: + + >>> Gaussian1D.param_names + ('amplitude', 'mean', 'stddev') + >>> (Gaussian1D() + Gaussian1D()).param_names + ('amplitude_0', 'mean_0', 'stddev_0', 'amplitude_1', 'mean_1', 'stddev_1') + +For consistency's sake, this scheme is followed even if not all of the +components have overlapping parameter names:: + + >>> from astropy.modeling.models import RedshiftScaleFactor + >>> (RedshiftScaleFactor() | (Gaussian1D() + Gaussian1D())).param_names + ('z_0', 'amplitude_1', 'mean_1', 'stddev_1', 'amplitude_2', 'mean_2', + 'stddev_2') + +On some level a scheme like this is necessary in order for the compound model +to maintain some consistency with other models with respect to the interface to +its parameters. However, if one gets lost it is also possible to take +advantage of :ref:`indexing ` to make things easier. +When returning a single component from a compound model the parameters +associated with that component are accessible through their original names, but +are still tied back to the compound model:: + + >>> a = Gaussian1D(1, 0, 0.2, name='A') + >>> b = Gaussian1D(2.5, 0.5, 0.1, name='B') + >>> m = a + b + >>> m.amplitude_0 + Parameter('amplitude', value=1.0) + +is equivalent to:: + + >>> m['A'].amplitude + Parameter('amplitude', value=1.0) + +You can think of these both as different "views" of the same parameter. +Updating one updates the other:: + + >>> m.amplitude_0 = 42 + >>> m['A'].amplitude + Parameter('amplitude', value=42.0) + >>> m['A'].amplitude = 99 + >>> m.amplitude_0 + Parameter('amplitude', value=99.0) + +Note, however, that the original +`~astropy.modeling.functional_models.Gaussian1D` instance ``a`` has been +updated:: + + >>> a.amplitude + Parameter('amplitude', value=99.0) + +This is different than the behavior in versions prior to 4.0. Now compound model +parameters share the same Parameter instance as the original model. + + +.. _compound-model-mappings: + +Advanced mappings +----------------- + +We have seen in some previous examples how models can be chained together to +form a "pipeline" of transformations by using model :ref:`composition +` and :ref:`concatenation +`. To aid the creation of more complex chains of +transformations (for example for a WCS transformation) a new class of +"`mapping `" models is provided. + +Mapping models do not (currently) take any parameters, nor do they perform any +numeric operation. They are for use solely with the :ref:`concatenation +` (``&``) and :ref:`composition +` (``|``) operators, and can be used to control how +the inputs and outputs of models are ordered, and how outputs from one model +are mapped to inputs of another model in a composition. + +Currently there are only two mapping models: +`~astropy.modeling.mappings.Identity`, and (the somewhat generically named) +`~astropy.modeling.mappings.Mapping`. + +The `~astropy.modeling.mappings.Identity` mapping simply passes one or more +inputs through, unchanged. It must be instantiated with an integer specifying +the number of inputs/outputs it accepts. This can be used to trivially expand +the "dimensionality" of a model in terms of the number of inputs it accepts. +In the section on :ref:`concatenation ` we saw +an example like:: + + >>> m = (Scale(1.2) & Scale(3.4)) | Rotation2D(90) + + +.. graphviz:: + + digraph { + in0 [shape="none", label="input 0"]; + in1 [shape="none", label="input 1"]; + out0 [shape="none", label="output 0"]; + out1 [shape="none", label="output 1"]; + scale0 [shape="box", label="Scale(factor=1.2)"]; + scale1 [shape="box", label="Scale(factor=3.4)"]; + rot0 [shape="box", label="Rotation2D(90)"]; + + in0 -> scale0; + scale0 -> rot0; + + in1 -> scale1; + scale1 -> rot0; + + rot0 -> out0; + rot0 -> out1; + } + +where two coordinate inputs are scaled individually and then rotated into each +other. However, say we wanted to scale only one of those coordinates. It +would be fine to simply use ``Scale(1)`` for one them, or any other model that +is effectively a no-op. But that also adds unnecessary computational overhead, +so we might as well simply specify that that coordinate is not to be scaled or +transformed in any way. This is a good use case for +`~astropy.modeling.mappings.Identity`: + +.. graphviz:: + + digraph { + in0 [shape="none", label="input 0"]; + in1 [shape="none", label="input 1"]; + out0 [shape="none", label="output 0"]; + out1 [shape="none", label="output 1"]; + scale0 [shape="box", label="Scale(factor=1.2)"]; + identity0 [shape="box", label="Identity(1)"]; + rot0 [shape="box", label="Rotation2D(90)"]; + + in0 -> scale0; + scale0 -> rot0; + + in1 -> identity0; + identity0 -> rot0; + + rot0 -> out0; + rot0 -> out1; + } + +:: + + >>> from astropy.modeling.models import Identity + >>> m = Scale(1.2) & Identity(1) + >>> m(1, 2) # doctest: +FLOAT_CMP + (1.2, 2.0) + + +This scales the first input, and passes the second one through unchanged. We +can use this to build up more complicated steps in a many-axis WCS +transformation. If for example we had 3 axes and only wanted to scale the +first one: + +.. graphviz:: + + digraph { + in0 [shape="none", label="input 0"]; + in1 [shape="none", label="input 1"]; + in2 [shape="none", label="input 2"]; + out0 [shape="none", label="output 0"]; + out1 [shape="none", label="output 1"]; + out2 [shape="none", label="output 2"]; + scale0 [shape="box", label="Scale(1.2)"]; + identity0 [shape="box", label="Identity(2)"]; + + in0 -> scale0; + scale0 -> out0; + + in1 -> identity0; + in2 -> identity0; + identity0 -> out1; + identity0 -> out2; + } + +:: + + >>> m = Scale(1.2) & Identity(2) + >>> m(1, 2, 3) # doctest: +FLOAT_CMP + (1.2, 2.0, 3.0) + +(Naturally, the last example could also be written out ``Scale(1.2) & +Identity(1) & Identity(1)``.) + +The `~astropy.modeling.mappings.Mapping` model is similar in that it does not +modify any of its inputs. However, it is more general in that it allows inputs +to be duplicated, reordered, or even dropped outright. It is instantiated with +a single argument: a `tuple`, the number of items of which correspond to the +number of outputs the `~astropy.modeling.mappings.Mapping` should produce. A +1-tuple means that whatever inputs come in to the +`~astropy.modeling.mappings.Mapping`, only one will be output. And so on for +2-tuple or higher (though the length of the tuple cannot be greater than the +number of inputs --- it will not pull values out of thin air). The elements of +this mapping are integers corresponding to the indices of the inputs. For +example, a mapping of ``Mapping((0,))`` is equivalent to ``Identity(1)`` --- it +simply takes the first (0-th) input and returns it: + +.. graphviz:: + + digraph G { + in0 [shape="none", label="input 0"]; + + subgraph cluster_A { + shape=rect; + color=black; + label="(0,)"; + + a [shape=point, label=""]; + } + + out0 [shape="none", label="output 0"]; + + in0 -> a; + a -> out0; + } + +:: + + >>> from astropy.modeling.models import Mapping + >>> m = Mapping((0,)) + >>> m(1.0) + 1.0 + +Likewise ``Mapping((0, 1))`` is equivalent to ``Identity(2)``, and so on. +However, `~astropy.modeling.mappings.Mapping` also allows outputs to be +reordered arbitrarily: + +.. graphviz:: + + digraph G { + { + rank=same; + in0 [shape="none", label="input 0"]; + in1 [shape="none", label="input 1"]; + } + + subgraph cluster_A { + shape=rect; + color=black; + label="(1, 0)"; + + { + rank=same; + a [shape=point, label=""]; + b [shape=point, label=""]; + } + + { + rank=same; + c [shape=point, label=""]; + d [shape=point, label=""]; + } + + a -> c [style=invis]; + a -> d [constraint=false]; + b -> c [constraint=false]; + } + + { + rank=same; + out0 [shape="none", label="output 0"]; + out1 [shape="none", label="output 1"]; + } + + in0 -> a; + in1 -> b; + c -> out0; + d -> out1; + } + +:: + + >>> m = Mapping((1, 0)) + >>> m(1.0, 2.0) + (2.0, 1.0) + +.. graphviz:: + + digraph G { + { + rank=same; + in0 [shape="none", label="input 0"]; + in1 [shape="none", label="input 1"]; + in2 [shape="none", label="input 2"]; + } + + subgraph cluster_A { + shape=rect; + color=black; + label="(1, 0, 2)"; + + { + rank=same; + a [shape=point, label=""]; + b [shape=point, label=""]; + c [shape=point, label=""]; + } + + { + rank=same; + d [shape=point, label=""]; + e [shape=point, label=""]; + f [shape=point, label=""]; + } + + a -> d [style=invis]; + a -> e [constraint=false]; + b -> d [constraint=false]; + c -> f [constraint=false]; + } + + { + rank=same; + out0 [shape="none", label="output 0"]; + out1 [shape="none", label="output 1"]; + out2 [shape="none", label="output 2"]; + } + + in0 -> a; + in1 -> b; + in2 -> c; + d -> out0; + e -> out1; + f -> out2; + } + +:: + + >>> m = Mapping((1, 0, 2)) + >>> m(1.0, 2.0, 3.0) + (2.0, 1.0, 3.0) + +Outputs may also be dropped: + +.. graphviz:: + + digraph G { + { + rank=same; + in0 [shape="none", label="input 0"]; + in1 [shape="none", label="input 1"]; + } + + subgraph cluster_A { + shape=rect; + color=black; + label="(1,)"; + + { + rank=same; + a [shape=point, label=""]; + b [shape=point, label=""]; + } + + { + rank=same; + c [shape=point, label=""]; + } + + a -> c [style=invis]; + b -> c [constraint=false]; + } + + out0 [shape="none", label="output 0"]; + + in0 -> a; + in1 -> b; + c -> out0; + } + +:: + + >>> m = Mapping((1,)) + >>> m(1.0, 2.0) + 2.0 + +.. graphviz:: + + digraph G { + { + rank=same; + in0 [shape="none", label="input 0"]; + in1 [shape="none", label="input 1"]; + in2 [shape="none", label="input 2"]; + } + + subgraph cluster_A { + shape=rect; + color=black; + label="(0, 2)"; + + { + rank=same; + a [shape=point, label=""]; + b [shape=point, label=""]; + c [shape=point, label=""]; + } + + { + rank=same; + d [shape=point, label=""]; + e [shape=point, label=""]; + } + + a -> d [style=invis]; + a -> d [constraint=false]; + c -> e [constraint=false]; + } + + { + rank=same; + out0 [shape="none", label="output 0"]; + out1 [shape="none", label="output 1"]; + } + + in0 -> a; + in1 -> b; + in2 -> c; + d -> out0; + e -> out1; + } + +:: + + >>> m = Mapping((0, 2)) + >>> m(1.0, 2.0, 3.0) + (1.0, 3.0) + +Or duplicated: + +.. graphviz:: + + digraph G { + in0 [shape="none", label="input 0"]; + + subgraph cluster_A { + shape=rect; + color=black; + label="(0, 0)"; + + a [shape=point, label=""]; + + { + rank=same; + b [shape=point, label=""]; + c [shape=point, label=""]; + } + + a -> b [style=invis]; + a -> b [constraint=false]; + a -> c [constraint=false]; + } + + { + rank=same; + out0 [shape="none", label="output 0"]; + out1 [shape="none", label="output 1"]; + } + + in0 -> a; + b -> out0; + c -> out1; + } + +:: + + >>> m = Mapping((0, 0)) + >>> m(1.0) + (1.0, 1.0) + +.. graphviz:: + + digraph G { + { + rank=same; + in0 [shape="none", label="input 0"]; + in1 [shape="none", label="input 1"]; + in2 [shape="none", label="input 2"]; + } + + subgraph cluster_A { + shape=rect; + color=black; + label="(0, 1, 1, 2)"; + + { + rank=same; + a [shape=point, label=""]; + b [shape=point, label=""]; + c [shape=point, label=""]; + } + + { + rank=same; + d [shape=point, label=""]; + e [shape=point, label=""]; + f [shape=point, label=""]; + g [shape=point, label=""]; + } + + a -> d [style=invis]; + a -> d [constraint=false]; + b -> e [constraint=false]; + b -> f [constraint=false]; + c -> g [constraint=false]; + } + + { + rank=same; + out0 [shape="none", label="output 0"]; + out1 [shape="none", label="output 1"]; + out2 [shape="none", label="output 2"]; + out3 [shape="none", label="output 3"]; + } + + in0 -> a; + in1 -> b; + in2 -> c; + d -> out0; + e -> out1; + f -> out2; + g -> out3; + } + +:: + + >>> m = Mapping((0, 1, 1, 2)) + >>> m(1.0, 2.0, 3.0) + (1.0, 2.0, 2.0, 3.0) + + +A complicated example that performs multiple transformations, some separable, +some not, on three coordinate axes might look something like: + +.. graphviz:: + + digraph G { + { + rank=same; + in0 [shape="none", label="input 0"]; + in1 [shape="none", label="input 1"]; + in2 [shape="none", label="input 2"]; + } + + { + rank=same; + poly0 [shape=rect, label="Poly1D(3, c0=1, c3=1)"]; + identity0 [shape=rect, label="Identity(1)"]; + poly1 [shape=rect, label="Poly1D(2, c2=1)"]; + } + + subgraph cluster_A { + shape=rect; + color=black; + label="(0, 2, 1)"; + + { + rank=same; + a [shape=point, label=""]; + b [shape=point, label=""]; + c [shape=point, label=""]; + } + + { + rank=same; + d [shape=point, label=""]; + e [shape=point, label=""]; + f [shape=point, label=""]; + } + + a -> d [style=invis]; + d -> e [style=invis]; + a -> d [constraint=false]; + c -> e [constraint=false]; + b -> f [constraint=false]; + } + + poly2 [shape="rect", label="Poly2D(4, c0_0=1, c1_1=1, c2_2=2)"]; + gaussian0 [shape="rect", label="Gaussian1D(1, 0, 4)"]; + + { + rank=same; + out0 [shape="none", label="output 0"]; + out1 [shape="none", label="output 1"]; + } + + in0 -> poly0; + in1 -> identity0; + in2 -> poly1; + poly0 -> a; + identity0 -> b; + poly1 -> c; + d -> poly2; + e -> poly2; + f -> gaussian0; + poly2 -> out0; + gaussian0 -> out1; + } + +:: + + >>> from astropy.modeling.models import Polynomial1D as Poly1D + >>> from astropy.modeling.models import Polynomial2D as Poly2D + >>> m = ((Poly1D(3, c0=1, c3=1) & Identity(1) & Poly1D(2, c2=1)) | + ... Mapping((0, 2, 1)) | + ... (Poly2D(4, c0_0=1, c1_1=1, c2_2=2) & Gaussian1D(1, 0, 4))) + ... + >>> m(2, 3, 4) # doctest: +FLOAT_CMP + (41617.0, 0.7548396019890073) + + + +This expression takes three inputs: :math:`x`, :math:`y`, and :math:`z`. It +first takes :math:`x \rightarrow x^3 + 1` and :math:`z \rightarrow z^2`. +Then it remaps the axes so that :math:`x` and :math:`z` are passed in to the +`~astropy.modeling.polynomial.Polynomial2D` to evaluate +:math:`2x^2z^2 + xz + 1`, while simultaneously evaluating a Gaussian on +:math:`y`. The end result is a reduction down to two coordinates. You can +confirm for yourself that the result is correct. + +This opens up the possibility of essentially arbitrarily complex transformation +graphs. Currently the tools do not exist to make it easy to navigate and +reason about highly complex compound models that use these mappings, but that +is a possible enhancement for future versions. + +.. _model-reduction: + +Model Reduction +--------------- + +In order to save much duplication in the construction of complex models, it is +possible to define one complex model that covers all cases where the +variables that distinguish the models are made part of the model's input +variables. The ``fix_inputs`` function allows defining models derived from +the more complex one by setting one or more of the inputs to a constant +value. Examples of this sort of situation arise when working out +the transformations from detector pixel to RA, Dec, and lambda for +spectrographs when the slit locations may be moved (e.g., fiber fed or +commandable slit masks), or different orders may be selected (e.g., Eschelle). +In the case of order, one may have a function of pixel ``x``, ``y``, ``spectral_order`` +that map into ``RA``, ``Dec`` and ``lambda``. Without specifying ``spectral_order``, it is +ambiguous what ``RA``, ``Dec`` and ``Lambda`` corresponds to a pixel location. It +is usually possible to define a function of all three inputs. Presuming +this model is ``general_transform`` then ``fix_inputs`` may be used to define +the transform for a specific order as follows: + +:: + >>> order1_transform = fix_inputs(general_transform, {'order': 1}) # doctest: +SKIP + +creates a new compound model that takes only pixel position and generates +``RA``, ``Dec``, and ``lambda``. The ``fix_inputs`` function can be used to set input +values by position (0 is the first) or by input variable name, and more +than one can be set in the dictionary supplied. + +If the input model has a bounding_box, the generated model will have the +bounding for the input coordinate removed. + + +.. test_replace_submodel + +Replace submodels +----------------- + + +:meth:`~astropy.modeling.core.CompoundModel.replace_submodel` creates a new model by +replacing a submodel with a matching name with another submodel. The number of +inputs and outputs of the old and new submodels should match. +:: + + >>> from astropy.modeling import models + >>> shift = models.Shift(-1) & models.Shift(-1) + >>> scale = models.Scale(2) & models.Scale(3) + >>> scale.name = "Scale" + >>> model = shift | scale + >>> model(2, 1) # doctest: +FLOAT_CMP + (2.0, 0.0) + >>> new_model = model.replace_submodel('Scale', models.Rotation2D(90, name='Rotation')) + >>> new_model(2, 1) # doctest: +FLOAT_CMP + (6.12e-17, 1.0) diff --git a/docs/modeling/example-fitting-constraints.rst b/docs/modeling/example-fitting-constraints.rst new file mode 100644 index 000000000000..83a839d909da --- /dev/null +++ b/docs/modeling/example-fitting-constraints.rst @@ -0,0 +1,161 @@ +Fitting with constraints +======================== + +`~astropy.modeling.fitting` support constraints, however, different fitters support +different types of constraints. The `~astropy.modeling.fitting.Fitter.supported_constraints` +attribute shows the type of constraints supported by a specific fitter:: + + >>> from astropy.modeling import fitting + >>> fitting.LinearLSQFitter.supported_constraints + ['fixed'] + >>> fitting.TRFLSQFitter.supported_constraints + ['fixed', 'tied', 'bounds'] + >>> fitting.SLSQPLSQFitter.supported_constraints + ['bounds', 'eqcons', 'ineqcons', 'fixed', 'tied'] + +Fixed Parameter Constraint +-------------------------- + +All fitters support fixed (frozen) parameters through the ``fixed`` argument +to models or setting the `~astropy.modeling.Parameter.fixed` +attribute directly on a parameter. + +For linear fitters, freezing a polynomial coefficient means that the +corresponding term will be subtracted from the data before fitting a +polynomial without that term to the result. For example, fixing ``c0`` in a +polynomial model will fit a polynomial with the zero-th order term missing +to the data minus that constant. The fixed coefficients and corresponding terms +are restored to the fit polynomial and this is the polynomial returned from the fitter:: + + >>> import numpy as np + >>> rng = np.random.default_rng(seed=12345) + >>> from astropy.modeling import models, fitting + >>> x = np.arange(1, 10, .1) + >>> p1 = models.Polynomial1D(2, c0=[1, 1], c1=[2, 2], c2=[3, 3], + ... n_models=2) + >>> p1 # doctest: +FLOAT_CMP + + >>> y = p1(x, model_set_axis=False) + >>> n = (rng.standard_normal(y.size)).reshape(y.shape) + >>> p1.c0.fixed = True + >>> pfit = fitting.LinearLSQFitter() + >>> new_model = pfit(p1, x, y + n) # doctest: +IGNORE_WARNINGS + >>> print(new_model) # doctest: +SKIP + Model: Polynomial1D + Inputs: ('x',) + Outputs: ('y',) + Model set size: 2 + Degree: 2 + Parameters: + c0 c1 c2 + --- ------------------ ------------------ + 1.0 2.072116176718454 2.99115839177437 + 1.0 1.9818866652726403 3.0024208951927585 + +The syntax to fix the same parameter ``c0`` using an argument to the model +instead of ``p1.c0.fixed = True`` would be:: + + >>> p1 = models.Polynomial1D(2, c0=[1, 1], c1=[2, 2], c2=[3, 3], + ... n_models=2, fixed={'c0': True}) + + +Bounded Constraints +------------------- + +Bounded fitting is supported through the ``bounds`` arguments to models or by +setting `~astropy.modeling.Parameter.min` and `~astropy.modeling.Parameter.max` +attributes on a parameter. The following fitters support bounds internally: + +* `~astropy.modeling.fitting.TRFLSQFitter` +* `~astropy.modeling.fitting.DogBoxLSQFitter` +* `~astropy.modeling.fitting.SLSQPLSQFitter` + +The `~astropy.modeling.fitting.LevMarLSQFitter` algorithm uses an unsophisticated +method of handling bounds and is no longer recommended (see +:ref:`modeling-getting-started-nonlinear-notes` for more details). + +.. _tied: + +Tied Constraints +---------------- + +The `~astropy.modeling.Parameter.tied` constraint is often useful with +:ref:`Compound models `. In this example we will +read a spectrum from a file called ``spec.txt`` and simultaneously fit +Gaussians to the emission lines while linking their wavelengths and +linking the flux of the [OIII] Îģ4959 line to the [OIII] Îģ5007 line. + +.. plot:: + :include-source: + + import numpy as np + from astropy.io import ascii + from astropy.modeling import fitting, models + from astropy.utils.data import get_pkg_data_filename + from matplotlib import pyplot as plt + + fname = get_pkg_data_filename("data/spec.txt", package="astropy.modeling.tests") + spec = ascii.read(fname) + wave = spec["lambda"] + flux = spec["flux"] + + # Use the (vacuum) rest wavelengths of known lines as initial values + # for the fit. + Hbeta = 4862.721 + O3_4959 = 4960.295 + O3_5007 = 5008.239 + + # Create Gaussian1D models for each of the H-beta and [OIII] lines. + hbeta_broad = models.Gaussian1D(amplitude=15, mean=Hbeta, stddev=20) + hbeta_narrow = models.Gaussian1D(amplitude=20, mean=Hbeta, stddev=2) + o3_4959 = models.Gaussian1D(amplitude=70, mean=O3_4959, stddev=2) + o3_5007 = models.Gaussian1D(amplitude=180, mean=O3_5007, stddev=2) + + # Create a polynomial model to fit the continuum. + mean_flux = flux.mean() + cont = np.where(flux > mean_flux, mean_flux, flux) + linfitter = fitting.LinearLSQFitter() + poly_cont = linfitter(models.Polynomial1D(1), wave, cont) + + # Create a compound model for the four emission lines and the continuum. + model = hbeta_broad + hbeta_narrow + o3_4959 + o3_5007 + poly_cont + + # Tie the ratio of the intensity of the two [OIII] lines. + def tie_o3_ampl(model): + return model.amplitude_3 / 2.98 + + o3_4959.amplitude.tied = tie_o3_ampl + + # Tie the wavelengths of the two [OIII] lines + def tie_o3_wave(model): + return model.mean_3 * O3_4959 / O3_5007 + + o3_4959.mean.tied = tie_o3_wave + + # Tie the wavelengths of the two (narrow and broad) H-beta lines + def tie_hbeta_wave1(model): + return model.mean_1 + + hbeta_broad.mean.tied = tie_hbeta_wave1 + + # Tie the wavelengths of the H-beta lines to the [OIII] 5007 line + def tie_hbeta_wave2(model): + return model.mean_3 * Hbeta / O3_5007 + + hbeta_narrow.mean.tied = tie_hbeta_wave2 + + # Simultaneously fit all the emission lines and continuum. + fitter = fitting.TRFLSQFitter() + fitted_model = fitter(model, wave, flux) + fitted_lines = fitted_model(wave) + + # Plot the data and the fitted model + fig, ax = plt.subplots(figsize=(9, 6)) + ax.plot(wave, flux, label="Data") + ax.plot(wave, fitted_lines, color="C1", label="Fitted Model") + ax.legend(loc="upper left") + ax.text(4860, 45, r"$H\beta$ (broad + narrow)", rotation=90) + ax.text(4958, 68, r"[OIII] $\lambda 4959$", rotation=90) + ax.text(4995, 140, r"[OIII] $\lambda 5007$", rotation=90) + ax.set(xlim=(4700, 5100), xlabel="Wavelength (Angstrom)", ylabel="Flux") + plt.show() diff --git a/docs/modeling/example-fitting-line.rst b/docs/modeling/example-fitting-line.rst new file mode 100644 index 000000000000..315123c25fab --- /dev/null +++ b/docs/modeling/example-fitting-line.rst @@ -0,0 +1,149 @@ +.. _example_fitting_line: + +Fitting a Line +============== + +Fitting a line to (x,y) data points is a common case in many areas. +Examples fits are given for fitting, fitting using the uncertainties +as weights, and fitting using iterative sigma clipping. + +Simple Fit +---------- + +Here the (x,y) data points are fit with a line. The (x,y) data +points are simulated and have a range of uncertainties to give +a realistic example. + +.. plot:: + :include-source: + + import numpy as np + import matplotlib.pyplot as plt + from astropy.modeling import models, fitting + + # define a model for a line + line_orig = models.Linear1D(slope=1.0, intercept=0.5) + + # generate x, y data non-uniformly spaced in x + # add noise to y measurements + npts = 30 + rng = np.random.default_rng(10) + x = rng.uniform(0.0, 10.0, npts) + y = line_orig(x) + yunc = np.absolute(rng.normal(0.5, 2.5, npts)) + y += rng.normal(0.0, yunc, npts) + + # initialize a linear fitter + fit = fitting.LinearLSQFitter() + + # initialize a linear model + line_init = models.Linear1D() + + # fit the data with the fitter + fitted_line = fit(line_init, x, y) + + # plot + fig, ax = plt.subplots() + ax.plot(x, y, 'ko', label='Data') + ax.plot(x, line_orig(x), 'b-', label='Simulation Model') + ax.plot(x, fitted_line(x), 'k-', label='Fitted Model') + ax.set(xlabel='x', ylabel='y') + ax.legend() + +Fit using uncertainties +----------------------- + +Fitting can be done using the uncertainties as weights. +To get the standard weighting of 1/unc^2 for the case of +Gaussian errors, the weights to pass to the fitting are 1/unc. + +.. plot:: + :include-source: + + import numpy as np + import matplotlib.pyplot as plt + from astropy.modeling import models, fitting + + # define a model for a line + line_orig = models.Linear1D(slope=1.0, intercept=0.5) + + # generate x, y data non-uniformly spaced in x + # add noise to y measurements + npts = 30 + rng = np.random.default_rng(10) + x = rng.uniform(0.0, 10.0, npts) + y = line_orig(x) + yunc = np.absolute(rng.normal(0.5, 2.5, npts)) + y += rng.normal(0.0, yunc, npts) + + # initialize a linear fitter + fit = fitting.LinearLSQFitter() + + # initialize a linear model + line_init = models.Linear1D() + + # fit the data with the fitter + fitted_line = fit(line_init, x, y, weights=1.0/yunc) + + # plot + fig, ax = plt.subplots() + ax.errorbar(x, y, yerr=yunc, fmt='ko', label='Data') + ax.plot(x, line_orig(x), 'b-', label='Simulation Model') + ax.plot(x, fitted_line(x), 'k-', label='Fitted Model') + ax.set(xlabel='x', ylabel='y') + ax.legend() + +Iterative fitting using sigma clipping +-------------------------------------- + +When fitting, there may be data that are outliers from the fit +that can significantly bias the fitting. These outliers can +be identified and removed from the fitting iteratively. +Note that the iterative sigma clipping assumes all the data +have the same uncertainties for the sigma clipping decision. + +.. plot:: + :include-source: + + import numpy as np + import matplotlib.pyplot as plt + from astropy.stats import sigma_clip + from astropy.modeling import models, fitting + + # define a model for a line + line_orig = models.Linear1D(slope=1.0, intercept=0.5) + + # generate x, y data non-uniformly spaced in x + # add noise to y measurements + npts = 30 + rng = np.random.default_rng(10) + x = rng.uniform(0.0, 10.0, npts) + y = line_orig(x) + yunc = np.absolute(rng.normal(0.5, 2.5, npts)) + y += rng.normal(0.0, yunc, npts) + + # make true outliers + y[3] = line_orig(x[3]) + 6 * yunc[3] + y[10] = line_orig(x[10]) - 4 * yunc[10] + + # initialize a linear fitter + fit = fitting.LinearLSQFitter() + + # initialize the outlier removal fitter + or_fit = fitting.FittingWithOutlierRemoval(fit, sigma_clip, niter=3, sigma=3.0) + + # initialize a linear model + line_init = models.Linear1D() + + # fit the data with the fitter + fitted_line, mask = or_fit(line_init, x, y, weights=1.0/yunc) + filtered_data = np.ma.masked_array(y, mask=mask) + + # plot + fig, ax = plt.subplots() + ax.errorbar(x, y, yerr=yunc, fmt="ko", fillstyle="none", label="Clipped Data") + ax.plot(x, filtered_data, "ko", label="Fitted Data") + ax.plot(x, line_orig(x), 'b-', label='Simulation Model') + ax.plot(x, fitted_line(x), 'k-', label='Fitted Model') + ax.set(xlabel='x', ylabel='y') + ax.legend() diff --git a/docs/modeling/example-fitting-model-sets.rst b/docs/modeling/example-fitting-model-sets.rst new file mode 100644 index 000000000000..c59c6831e22c --- /dev/null +++ b/docs/modeling/example-fitting-model-sets.rst @@ -0,0 +1,171 @@ +.. _example-fitting-model-sets: + +Fitting Model Sets +================== + +Astropy model sets let you fit the same (linear) model to lots of independent +data sets. It solves the linear equations simultaneously, so can avoid looping. +But getting the data into the right shape can be a bit tricky. + +The time savings could be worth the effort. In the example below, if we change +the width*height of the data cube to 500*500 it takes 140 ms on a 2015 MacBook Pro +to fit the models using model sets. Doing the same fit by looping over the 500*500 models +takes 1.5 minutes, more than 600 times slower. + +In the example below, we create a 3D data cube where the first dimension is a ramp -- +for example as from non-destructive readouts of an IR detector. So each pixel has a +depth along a time axis, and flux that results a total number of counts that is +increasing with time. We will be fitting a 1D polynomial vs. time to estimate the +flux in counts/second (the slope of the fit). We will use just a small image +of 3 rows by 4 columns, with a depth of 10 non-destructive reads. + +First, import the necessary libraries: + + >>> import numpy as np + >>> rng = np.random.default_rng(seed=12345) + >>> from astropy.modeling import models, fitting + + >>> depth, width, height = 10, 3, 4 # Time is along the depth axis + >>> t = np.arange(depth, dtype=np.float64)*10. # e.g. readouts every 10 seconds + +The number of counts in neach pixel is flux*time with the addition of some Gaussian noise:: + + >>> fluxes = np.arange(1. * width * height).reshape(width, height) + >>> image = fluxes[np.newaxis, :, :] * t[:, np.newaxis, np.newaxis] + >>> image += rng.normal(0., image*0.05, size=image.shape) # Add noise + >>> image.shape + (10, 3, 4) + +Create the models and the fitter. We need N=width*height instances of the same linear, +parametric model (model sets currently only work with linear models and fitters):: + + >>> N = width * height + >>> line = models.Polynomial1D(degree=1, n_models=N) + >>> fit = fitting.LinearLSQFitter() + >>> print(f"We created {len(line)} models") + We created 12 models + +We need to get the data to be fit into the right shape. It's not possible to just feed +the 3D data cube. In this case, the time axis can be one dimensional. +The fluxes have to be organized into an array that is of shape ``width*height,depth`` -- in +other words, we are reshaping to flatten last two axes and transposing to put them first:: + + >>> pixels = image.reshape((depth, width*height)) + >>> y = pixels.T + >>> print("x axis is one dimensional: ",t.shape) + x axis is one dimensional: (10,) + >>> print("y axis is two dimensional, N by len(x): ", y.shape) + y axis is two dimensional, N by len(x): (12, 10) + +Fit the model. It fits the N models simultaneously:: + + >>> new_model = fit(line, x=t, y=y) + >>> print(f"We fit {len(new_model)} models") + We fit 12 models + +Fill an array with values computed from the best fit and reshape it to match the original:: + + >>> best_fit = new_model(t, model_set_axis=False).T.reshape((depth, height, width)) + >>> print("We reshaped the best fit to dimensions: ", best_fit.shape) + We reshaped the best fit to dimensions: (10, 4, 3) + +Now inspect the model:: + + >>> print(new_model) # doctest: +FLOAT_CMP + Model: Polynomial1D + Inputs: ('x',) + Outputs: ('y',) + Model set size: 12 + Degree: 1 + Parameters: + c0 c1 + ------------------- ------------------ + 0.0 0.0 + 0.7435257251672668 0.9788645710692938 + -2.9342067207465647 2.038294797728997 + -4.258776494573452 3.1951399579785678 + 2.364390501364263 3.9973270072631104 + 2.161531512810536 4.939542306192216 + 3.9930177540418823 5.967786182181591 + -6.825657765397985 7.2680615507233215 + -6.675677073701012 8.321048309260679 + -11.91115500400788 9.025794163936956 + -4.123655771677581 9.938564642105128 + -0.7256700167533869 10.989896974949136 + + >>> print("The new_model has a param_sets attribute with shape: ",new_model.param_sets.shape) + The new_model has a param_sets attribute with shape: (2, 12) + + >>> print(f"And values that are the best-fit parameters for each pixel:\n{new_model.param_sets}") # doctest: +FLOAT_CMP + And values that are the best-fit parameters for each pixel: + [[ 0. 0.74352573 -2.93420672 -4.25877649 2.3643905 + 2.16153151 3.99301775 -6.82565777 -6.67567707 -11.911155 + -4.12365577 -0.72567002] + [ 0. 0.97886457 2.0382948 3.19513996 3.99732701 + 4.93954231 5.96778618 7.26806155 8.32104831 9.02579416 + 9.93856464 10.98989697]] + +Plot the fit along a couple of pixels: + + >>> def plotramp(ax, t, image, best_fit, row, col): + ... ax.plot(t, image[:, row, col], '.', label=f'data pixel {row},{col}') + ... ax.plot(t, best_fit[:, row, col], '-', label=f'fit to pixel {row},{col}') + ... ax.set(xlabel='Time', ylabel='Counts') + ... ax.legend(loc='upper left') + >>> fig, ax = plt.subplots(figsize=(10, 5)) # doctest: +SKIP + >>> plotramp(ax, t, image, best_fit, 1, 1) # doctest: +SKIP + >>> plotramp(ax, t, image, best_fit, 2, 1) # doctest: +SKIP + +The data and the best fit model are shown together on one plot. + +.. plot:: + + import numpy as np + import matplotlib.pyplot as plt + from scipy import stats + from astropy.modeling import models, fitting + + # Set up the shape of the image and create the time axis + depth,width,height=10,3,4 # Time is along the depth axis + t = np.arange(depth, dtype=np.float64)*10. # e.g. readouts every 10 seconds + + # Make up a flux in each pixel + fluxes = np.arange(1.*width*height).reshape(height, width) + # Create the ramps by integrating the fluxes along the time steps + image = fluxes[np.newaxis, :, :] * t[:, np.newaxis, np.newaxis] + # Add some Gaussian noise to each sample + image += stats.norm.rvs(0., image*0.05, size=image.shape) # Add noise + + # Create the models and the fitter + N = width * height # This is how many instances we need + line = models.Polynomial1D(degree=1, n_models=N) + fit = fitting.LinearLSQFitter() + + # We need to get the data to be fit into the right shape + # In this case, the time axis can be one dimensional. + # The fluxes have to be organized into an array + # that is of shape `(width*height, depth)` + # i.e we are reshaping to flatten last two axes and + # transposing to put them first. + pixels = image.reshape((depth, width*height)) + y = pixels.T + + # Fit the model. It does the looping over the N models implicitly + new_model = fit(line, x=t, y=y) + + # Fill an array with values computed from the best fit and reshape it to match the original + best_fit = new_model(t, model_set_axis=False).T.reshape((depth, height, width)) + + + # Plot the fit along a couple of pixels + def plotramp(ax, t, image, best_fit, row, col): + ax.plot(t, image[:, row, col], '.', label=f'data pixel {row},{col}') + ax.plot(t, best_fit[:, row, col], '-', label=f'fit to pixel {row},{col}') + ax.set(xlabel='Time', ylabel='Counts') + ax.legend(loc='upper left') + + + fig, ax = plt.subplots(figsize=(10, 5)) + plotramp(ax, t, image, best_fit, 1, 1) + plotramp(ax, t, image, best_fit, 3, 2) + plt.show() diff --git a/docs/modeling/fitting.rst b/docs/modeling/fitting.rst new file mode 100644 index 000000000000..1fc7c7b01a47 --- /dev/null +++ b/docs/modeling/fitting.rst @@ -0,0 +1,193 @@ +********************** +Fitting Models to Data +********************** + +This module provides wrappers, called Fitters, around some Numpy and Scipy +fitting functions. All Fitters can be called as functions. They take an +instance of `~astropy.modeling.FittableModel` as input and modify its +``parameters`` attribute. The idea is to make this extensible and allow +users to easily add other fitters. + +Linear fitting is done using Numpy's `numpy.linalg.lstsq` function. There are +currently non-linear fitters which use `scipy.optimize.leastsq`, +`scipy.optimize.least_squares`, and `scipy.optimize.fmin_slsqp`. + +The rules for passing input to fitters are: + +* Non-linear fitters currently work only with single models (not model sets). + +* The linear fitter can fit a single input to multiple model sets creating + multiple fitted models. This may require specifying the ``model_set_axis`` + argument just as used when evaluating models; this may be required for the + fitter to know how to broadcast the input data. + +* The `~astropy.modeling.fitting.LinearLSQFitter` currently works only with + simple (not compound) models. + +* The current fitters work only with models that have a single output + (including bivariate functions such as + `~astropy.modeling.polynomial.Chebyshev2D` but not compound models that map + ``x, y -> x', y'``). + +* The units of the fitting data and the model parameters are stripped before fitting + so that the underlying ``scipy`` methods can handle this data. One should be aware + of this when fitting data with units as unit conversions will only be performed + initially. These conversions will be performed using the ``equivalencies`` + argument to the fitter combined with the ``model.input_units_equivalencies`` attribute + of the model being fit. + +.. note:: + In general, non-linear fitters do not support fitting to data which contains + non-finite values: ``NaN``, ``Inf``, or ``-Inf``. This is a limitation of the + underlying scipy library. As a consequence, an error will be raised whenever + any non-finite value is present in the data to be fitted. To avoid this error + users should "filter" the non-finite values from their data, for example + when fitting a ``model``, with a ``fitter`` using ``data`` containing non-finite + values one can "filter" these problems as follows for the 1D case:: + + # Filter non-finite values from data + mask = np.isfinite(data) + # Fit model to filtered data + model = fitter(model, x[mask], data[mask]) + + or for the 2D case:: + + # Filter non-finite values from data + mask = np.isfinite(data) + # Fit model to filtered data + model = fitter(model, x[mask], y[mask], data[mask]) + +.. _modeling-getting-started-nonlinear-notes: + +Notes on non-linear fitting +--------------------------- + +There are several non-linear fitters, which rely on several different +optimization algorithms. Which one you should choose will depend on the problem +you are trying to solve. The main recommended non-linear fitters are: + +* :class:`~astropy.modeling.fitting.TRFLSQFitter`, which uses the Trust Region Reflective + (TRF) algorithm that is particularly suitable for large sparse problems with bounds, see + `scipy.optimize.least_squares` for more details. + +* :class:`~astropy.modeling.fitting.DogBoxLSQFitter`, which uses the dogleg algorithm + with rectangular trust regions, typical use case is small problems with bounds. Not + recommended for problems with rank-deficient Jacobian, see `scipy.optimize.least_squares` + for more details. + +* :class:`~astropy.modeling.fitting.LMLSQFitter`, which uses the Levenberg-Marquardt (LM) + algorithm as implemented by `scipy.optimize.least_squares`. Does not handle bounds and/or + sparse Jacobians. Usually the most efficient method for small unconstrained problems. + If a Levenberg-Marquardt algorithm is desired for your problem, it is now recommended that + you use this fitter instead of :class:`~astropy.modeling.fitting.LevMarLSQFitter` as it + makes use of the recommended version of this algorithm in scipy. However, if your problem + makes use of bounds, you should use another non-linear fitter instead such as + :class:`~astropy.modeling.fitting.TRFLSQFitter` or :class:`~astropy.modeling.fitting.DogBoxLSQFitter` + +Note that the :class:`~astropy.modeling.fitting.LevMarLSQFitter` fitter, which +uses the Levenberg-Marquardt algorithm via the scipy legacy function +`scipy.optimize.leastsq`, is no longer recommended. This fitter supports +parameter bounds via an unsophisticated min/max condition whereby during each +step of the fitting, parameters that are out of bounds are simply reset to the +min or max of the bounds. This can cause parameters to "stick" to one of the +bounds if during the fitting process the parameter gets close to the bound. If +the models you are fitting make use of bounds, you should make use of one of the +other fitters such as :class:`~astropy.modeling.fitting.TRFLSQFitter` or +:class:`~astropy.modeling.fitting.DogBoxLSQFitter`, and if you do not need +bounds and specifically want to use the Levenberg-Marquardt algorithm, you +should use :class:`~astropy.modeling.fitting.LMLSQFitter`. + +.. _modeling-getting-started-1d-fitting: + +Simple 1-D model fitting +------------------------ + +In this section, we look at a simple example of fitting a Gaussian to a +simulated dataset. We use the `~astropy.modeling.functional_models.Gaussian1D` +and `~astropy.modeling.functional_models.Trapezoid1D` models and the +`~astropy.modeling.fitting.TRFLSQFitter` fitter to fit the data: + +.. plot:: + :include-source: + + import numpy as np + import matplotlib.pyplot as plt + from astropy.modeling import models, fitting + + # Generate fake data + rng = np.random.default_rng(0) + x = np.linspace(-5., 5., 200) + y = 3 * np.exp(-0.5 * (x - 1.3)**2 / 0.8**2) + y += rng.normal(0., 0.2, x.shape) + + # Fit the data using a box model. + # Bounds are not really needed but included here to demonstrate usage. + t_init = models.Trapezoid1D(amplitude=1., x_0=0., width=1., slope=0.5, + bounds={"x_0": (-5., 5.)}) + fit_t = fitting.TRFLSQFitter() + t = fit_t(t_init, x, y, maxiter=200) + + # Fit the data using a Gaussian + g_init = models.Gaussian1D(amplitude=1., mean=0, stddev=1.) + fit_g = fitting.TRFLSQFitter() + g = fit_g(g_init, x, y) + + # Plot the data with the best-fit model + fig, ax = plt.subplots(figsize=(8, 5)) + ax.plot(x, y, 'ko') + ax.plot(x, t(x), label='Trapezoid') + ax.plot(x, g(x), label='Gaussian') + ax.set(xlabel='Position', ylabel='Flux') + ax.legend(loc=2) + +As shown above, once instantiated, the fitter class can be used as a function +that takes the initial model (``t_init`` or ``g_init``) and the data values +(``x`` and ``y``), and returns a fitted model (``t`` or ``g``). + +.. _modeling-getting-started-2d-fitting: + +Simple 2-D model fitting +------------------------ + +Similarly to the 1-D example, we can create a simulated 2-D data dataset, and +fit a polynomial model to it. This could be used for example to fit the +background in an image. + +.. plot:: + :include-source: + + import warnings + import numpy as np + import matplotlib.pyplot as plt + from astropy.modeling import models, fitting + from astropy.utils.exceptions import AstropyUserWarning + + # Generate fake data + rng = np.random.default_rng(0) + y, x = np.mgrid[:128, :128] + z = 2. * x ** 2 - 0.5 * x ** 2 + 1.5 * x * y - 1. + z += rng.normal(0., 0.1, z.shape) * 50000. + + # Fit the data using astropy.modeling + p_init = models.Polynomial2D(degree=2) + fit_p = fitting.LMLSQFitter() + + with warnings.catch_warnings(): + # Ignore model linearity warning from the fitter + warnings.filterwarnings('ignore', message='Model is linear in parameters', + category=AstropyUserWarning) + p = fit_p(p_init, x, y, z) + + # Plot the data with the best-fit model + fig, axs = plt.subplots(figsize=(8, 2.5), ncols=3) + ax1 = axs[0] + ax1.imshow(z, origin='lower', interpolation='nearest', vmin=-1e4, vmax=5e4) + ax1.set_title("Data") + ax2 = axs[1] + ax2.imshow(p(x, y), origin='lower', interpolation='nearest', vmin=-1e4, + vmax=5e4) + ax2.set_title("Model") + ax3 = axs[2] + ax3.imshow(z - p(x, y), origin='lower', interpolation='nearest', vmin=-1e4, + vmax=5e4) + ax3.set_title("Residual") diff --git a/docs/modeling/index.rst b/docs/modeling/index.rst new file mode 100644 index 000000000000..77b5aaec24d9 --- /dev/null +++ b/docs/modeling/index.rst @@ -0,0 +1,138 @@ +.. include:: links.inc + +.. _astropy-modeling: + +*************************************** +Models and Fitting (`astropy.modeling`) +*************************************** + +Introduction +============ + +`astropy.modeling` provides a framework for representing models and performing +model evaluation and fitting. A number of predefined 1-D and 2-D models are +provided and the capability for custom, user defined models is supported. +Different fitting algorithms can be used with any model. For those fitters +with the capabilities fitting can be done using uncertainties, parameters with +bounds, and priors. + +.. _modeling-using: + +Using Modeling +============== + +.. toctree:: + :maxdepth: 2 + + Models + Compound Models + Model Parameters + Fitting + Using Units with Models and Fitting + + +.. _getting-started-example: + +A Simple Example +================ + +This simple example illustrates defining a model, +calculating values based on input x values, and using fitting data with a model. + + .. plot:: + :include-source: + + import numpy as np + import matplotlib.pyplot as plt + from astropy.modeling import models, fitting + + # define a model for a line + line_orig = models.Linear1D(slope=1.0, intercept=0.5) + + # generate x, y data non-uniformly spaced in x + # add noise to y measurements + npts = 30 + rng = np.random.default_rng(10) + x = rng.uniform(0.0, 10.0, npts) + y = line_orig(x) + y += rng.normal(0.0, 1.5, npts) + + # initialize a linear fitter + fit = fitting.LinearLSQFitter() + + # initialize a linear model + line_init = models.Linear1D() + + # fit the data with the fitter + fitted_line = fit(line_init, x, y) + + # plot the model + fig, ax = plt.subplots() + ax.plot(x, y, 'ko', label='Data') + ax.plot(x, fitted_line(x), 'k-', label='Fitted Model') + ax.set(xlabel='x', ylabel='y') + ax.legend() + +.. _advanced_topics: + +Advanced Topics +=============== + +.. toctree:: + :maxdepth: 2 + + Performance Tips + Extending Models + Extending Fitters + Adding support for units to models + Joint Fitting + Parallel Fitting + + +Pre-Defined Models +================== + +.. To be expanded to include all pre-defined models + +Some of the pre-defined models are listed and illustrated. + +.. toctree:: + :maxdepth: 2 + + 1D Models + 2D Models + Physical Models + Polynomial Models + Powerlaw Models + Spline Models + +Examples +======== + +.. toctree:: + :maxdepth: 2 + + Fitting a line + example-fitting-constraints + example-fitting-model-sets + +.. TODO list + fitting with masks + fitting with priors + fitting with units + defining 1d model + defining 2d model + fitting 2d model + defining and using a WCS/gWCS model + defining and using a Tabular1D model + statistics functions and how to make your own + compound models + + +Reference/API +============= + +.. toctree:: + :maxdepth: 2 + + reference_api diff --git a/docs/modeling/jointfitter.rst b/docs/modeling/jointfitter.rst new file mode 100644 index 000000000000..12ca0e857d11 --- /dev/null +++ b/docs/modeling/jointfitter.rst @@ -0,0 +1,231 @@ +.. _jointfitter: + +JointFitter +=========== + +There are cases where one may wish to fit multiple datasets with models that +share parameters. This is possible with the +`astropy.modeling.fitting.JointFitter`. Basically, this fitter is +setup with a list of defined models, the parameters in common between the +different models, and the initial values for those parameters. Then the fitter +is called supplying as many x and y arrays, one for each model to be fit. The +fit parameters are the result of the jointly fitting the models to the +combined datasets. + +.. note:: + The JointFitter uses the scipy.optimize.leastsq. In addition, it + does not support fixed, bounded, or tied parameters at this time. + +Example: Spectral Line +====================== + +This example is for two spectral segments with different spectral resolutions +that have the same spectral line in the wavelength region that is overlapping +between both segments. + +We will need to define a Gaussian function that has mean wavelength, area, and +width parameters. This is needed as the `astropy.modeling.functional_models.Gaussian1D` +function has mean wavelength, central intensity, and width parameters, but the +central intensity of a line will be different at different spectral resolutions, +but the area will be the same. + +First, imports needed for this example + + >>> # imports + >>> import math + >>> import numpy as np + >>> from astropy.modeling import fitting, Fittable1DModel + >>> from astropy.modeling.parameters import Parameter + >>> from astropy.modeling.functional_models import FLOAT_EPSILON + +Now define AreaGaussian1D with area instead of intensity as a parameter. +This new is modified and trimmed version of the standard Gaussian1D model. + + >>> class AreaGaussian1D(Fittable1DModel): + ... """ + ... One dimensional Gaussian model with area as a parameter. + ... + ... Parameters + ... ---------- + ... area : float or `~astropy.units.Quantity`. + ... Integrated area + ... Note: amplitude = area / (stddev * np.sqrt(2 * np.pi)) + ... mean : float or `~astropy.units.Quantity`. + ... Mean of the Gaussian. + ... stddev : float or `~astropy.units.Quantity`. + ... Standard deviation of the Gaussian with FWHM = 2 * stddev * np.sqrt(2 * np.log(2)). + ... """ + ... area = Parameter(default=1) + ... mean = Parameter(default=0) + ... + ... # Ensure stddev makes sense if its bounds are not explicitly set. + ... # stddev must be non-zero and positive. + ... stddev = Parameter(default=1, bounds=(FLOAT_EPSILON, None)) + ... + ... @staticmethod + ... def evaluate(x, area, mean, stddev): + ... """ + ... AreaGaussian1D model function. + ... """ + ... return (area / (stddev * np.sqrt(2 * np.pi))) * np.exp( + ... -0.5 * (x - mean) ** 2 / stddev ** 2 + ... ) + +Data to be fit is simulated. The 1st spectral segment will have a spectral +resolution that is a factor of 2 higher than the second segment. The first +segment will have wavelengths from 1 to 6 and the second from 4 to 10 giving +an overlapping wavelength region from 4 to 6. + + >>> # Generate fake data + >>> mean = 5.1 + >>> sigma1 = 0.2 + >>> sigma2 = 0.4 + >>> noise = 0.10 + + >>> # compute the central amplitudes so the lines in each segment have the + >>> # same area + >>> area = 1.5 + >>> amp1 = area / np.sqrt(2.0 * math.pi * sigma1 ** 2) + >>> amp2 = area / np.sqrt(2.0 * math.pi * sigma2 ** 2) + + >>> # segment 1 + >>> rng = np.random.default_rng(147) + >>> x1 = np.linspace(1.0, 6.0, 200) + >>> y1 = amp1 * np.exp(-0.5 * (x1 - mean) ** 2 / sigma1 ** 2) + >>> y1 += rng.normal(0.0, noise, x1.shape) + + >>> # segment 2 + >>> x2 = np.linspace(4.0, 10.0, 200) + >>> y2 = amp2 * np.exp(-0.5 * (x2 - mean) ** 2 / sigma2 ** 2) + >>> y2 += rng.normal(0.0, noise, x2.shape) + +Now define the models to be fit and fitter to use. Then fit the two simulated +datasets. + + >>> # define the two models to be fit + >>> gjf1 = AreaGaussian1D(area=1.0, mean=5.0, stddev=1.0) + >>> gjf2 = AreaGaussian1D(area=1.0, mean=5.0, stddev=1.0) + +.. doctest-requires:: scipy + + >>> # define the jointfitter specifying the parameters in common and their initial values + >>> fit_joint = fitting.JointFitter( + ... [gjf1, gjf2], {gjf1: ["area", "mean"], gjf2: ["area", "mean"]}, [1.0, 5.0] + ... ) + >>> + >>> # perform the fit + >>> g12 = fit_joint(x1, y1, x2, y2) + + +The resulting fit parameters show that the area and mean wavelength of the +two AreaGaussian1D models are exactly the same while the width (stddev) is +different reflecting the different spectral resolutions of the two segments. + +AreaGaussian1 parameters + +.. doctest-requires:: scipy + + >>> print(gjf1.param_names) + ('area', 'mean', 'stddev') + >>> print(gjf1.parameters) + [1.49823951 5.10494811 0.19918164] + +AreaGaussian2 parameters + +.. doctest-requires:: scipy + + >>> print(gjf1.param_names) + ('area', 'mean', 'stddev') + >>> print(gjf2.parameters) + [1.49823951 5.10494811 0.39860539] + + +The simulated data and best fit models can be plotted showing good agreement +between the two AreaGaussian1D models and the two spectral segments. + +.. plot:: + + # imports + import numpy as np + import math + import matplotlib.pyplot as plt + from astropy.modeling import fitting, Fittable1DModel + from astropy.modeling.parameters import Parameter + from astropy.modeling.functional_models import FLOAT_EPSILON + + + class AreaGaussian1D(Fittable1DModel): + """ + One dimensional Gaussian model with area as a parameter. + + Parameters + ---------- + area : float or `~astropy.units.Quantity`. + Integrated area + Note: amplitude = area / (stddev * np.sqrt(2 * np.pi)) + mean : float or `~astropy.units.Quantity`. + Mean of the Gaussian. + stddev : float or `~astropy.units.Quantity`. + Standard deviation of the Gaussian with FWHM = 2 * stddev * np.sqrt(2 * np.log(2)). + """ + + area = Parameter(default=1) + mean = Parameter(default=0) + + # Ensure stddev makes sense if its bounds are not explicitly set. + # stddev must be non-zero and positive. + stddev = Parameter(default=1, bounds=(FLOAT_EPSILON, None)) + + @staticmethod + def evaluate(x, area, mean, stddev): + """ + AreaGaussian1D model function. + """ + return (area / (stddev * np.sqrt(2 * np.pi))) * np.exp( + -0.5 * (x - mean) ** 2 / stddev ** 2 + ) + + + # Generate fake data + mean = 5.1 + sigma1 = 0.2 + sigma2 = 0.4 + noise = 0.10 + + # compute the central amplitudes so the lines in each segment have the + # same area + area = 1.5 + amp1 = area / np.sqrt(2.0 * math.pi * sigma1 ** 2) + amp2 = area / np.sqrt(2.0 * math.pi * sigma2 ** 2) + + # segment 1 + rng = np.random.default_rng(147) + x1 = np.linspace(1.0, 6.0, 200) + y1 = amp1 * np.exp(-0.5 * (x1 - mean) ** 2 / sigma1 ** 2) + y1 += rng.normal(0.0, noise, x1.shape) + + # segment 2 + x2 = np.linspace(4.0, 10.0, 200) + y2 = amp2 * np.exp(-0.5 * (x2 - mean) ** 2 / sigma2 ** 2) + y2 += rng.normal(0.0, noise, x2.shape) + + # define the two models to be fit + gjf1 = AreaGaussian1D(area=1.0, mean=5.0, stddev=1.0) + gjf2 = AreaGaussian1D(area=1.0, mean=5.0, stddev=1.0) + + # define the jointfitter specifying the parameters in common and their initial values + fit_joint = fitting.JointFitter( + [gjf1, gjf2], {gjf1: ["area", "mean"], gjf2: ["area", "mean"]}, [1.0, 5.0] + ) + + # perform the fit + g12 = fit_joint(x1, y1, x2, y2) + + # Plot the data with the best-fit models + fig, ax = plt.subplots(figsize=(8, 5)) + ax.plot(x1, y1, "bo", alpha=0.25) + ax.plot(x2, y2, "go", alpha=0.25) + ax.plot(x1, gjf1(x1), "b--", label="AreaGaussian1") + ax.plot(x2, gjf2(x2), "g--", label="AreaGaussian2") + ax.set(xlabel="Wavelength", ylabel="Flux") + ax.legend(loc=2) diff --git a/docs/modeling/links.inc b/docs/modeling/links.inc new file mode 100644 index 000000000000..93a2cb076e8b --- /dev/null +++ b/docs/modeling/links.inc @@ -0,0 +1,4 @@ +.. _Numpy broadcasting rules: https://numpy.org/doc/stable/user/basics.broadcasting.html +.. _Generalized World Coordinate System (GWCS): https://gwcs.readthedocs.io/en/latest/ +.. _ASDF: https://asdf-standard.readthedocs.io/en/latest/ +.. _SIP: https://fits.gsfc.nasa.gov/registry/sip.html diff --git a/docs/modeling/models.rst b/docs/modeling/models.rst new file mode 100644 index 000000000000..5cb2466c0813 --- /dev/null +++ b/docs/modeling/models.rst @@ -0,0 +1,969 @@ +.. include:: links.inc + +.. _models: + +****** +Models +****** + +.. _basics-models: + +Basics +====== + +The `astropy.modeling` package defines a number of models that are collected +under a single namespace as ``astropy.modeling.models``. Models behave like +parametrized functions:: + + >>> import numpy as np + >>> from astropy.modeling import models + >>> g = models.Gaussian1D(amplitude=1.2, mean=0.9, stddev=0.5) + >>> print(g) + Model: Gaussian1D + Inputs: ('x',) + Outputs: ('y',) + Model set size: 1 + Parameters: + amplitude mean stddev + --------- ---- ------ + 1.2 0.9 0.5 + +Model parameters can be accessed as attributes:: + + >>> g.amplitude + Parameter('amplitude', value=1.2) + >>> g.mean + Parameter('mean', value=0.9) + >>> g.stddev # doctest: +FLOAT_CMP + Parameter('stddev', value=0.5, bounds=(1.1754943508222875e-38, None)) + +and can also be updated via those attributes:: + + >>> g.amplitude = 0.8 + >>> g.amplitude + Parameter('amplitude', value=0.8) + +Models can be evaluated by calling them as functions:: + + >>> g(0.1) + 0.22242984036255528 + >>> g(np.linspace(0.5, 1.5, 7)) # doctest: +FLOAT_CMP + array([0.58091923, 0.71746405, 0.7929204 , 0.78415894, 0.69394278, + 0.54952605, 0.3894018 ]) + +As the above example demonstrates, in general most models evaluate array-like +inputs according to the standard `Numpy broadcasting rules`_ for arrays. +Models can therefore already be useful to evaluate common functions, +independently of the fitting features of the package. + +.. _modeling-instantiating: + + +Instantiating and Evaluating Models +----------------------------------- + +In general, models are instantiated by supplying the parameter values that +define that instance of the model to the constructor, as demonstrated in +the section on :ref:`modeling-parameters`. + +Additionally, a `~astropy.modeling.Model` instance may represent a single model +with one set of parameters, or a :ref:`Model set ` consisting +of a set of parameters each representing a different parameterization of the same +parametric model. For example, you may instantiate a single Gaussian model +with one mean, standard deviation, and amplitude. Or you may create a set +of N Gaussians, each one of which would be evaluated on, for example, a +different plane in an image cube. + +For example, a single Gaussian model may be instantiated with all scalar parameters:: + + >>> from astropy.modeling.models import Gaussian1D + >>> g = Gaussian1D(amplitude=1, mean=0, stddev=1) + >>> g # doctest: +FLOAT_CMP + + +The newly created model instance ``g`` now works like a Gaussian function +with the specific parameters. It takes a single input:: + + >>> g.inputs + ('x',) + >>> g(x=0) + 1.0 + +The model can also be called without explicitly using keyword arguments:: + + >>> g(0) + 1.0 + +Or a set of Gaussians may be instantiated by passing multiple parameter values:: + + >>> from astropy.modeling.models import Gaussian1D + >>> gset = Gaussian1D(amplitude=[1, 1.5, 2], + ... mean=[0, 1, 2], + ... stddev=[1., 1., 1.], + ... n_models=3) + >>> print(gset) # doctest: +FLOAT_CMP + Model: Gaussian1D + Inputs: ('x',) + Outputs: ('y',) + Model set size: 3 + Parameters: + amplitude mean stddev + --------- ---- ------ + 1.0 0.0 1.0 + 1.5 1.0 1.0 + 2.0 2.0 1.0 + +This model also works like a Gaussian function. The three models in +the model set can be evaluated on the same input:: + + >>> gset(1.) + array([0.60653066, 1.5 , 1.21306132]) + +or on ``N=3`` inputs:: + + >>> gset([1, 2, 3]) + array([0.60653066, 0.90979599, 1.21306132]) + +For a comprehensive example of fitting a model set see :ref:`example-fitting-model-sets`. + +Model inverses +-------------- + +All models have a `Model.inverse ` property +which may, for some models, return a new model that is the analytic inverse of +the model it is attached to. For example:: + + >>> from astropy.modeling.models import Linear1D + >>> linear = Linear1D(slope=0.8, intercept=1.0) + >>> linear.inverse + + +The inverse of a model will always be a fully instantiated model in its own +right, and so can be evaluated directly like:: + + >>> linear.inverse(2.0) + 1.25 + +It is also possible to assign a *custom* inverse to a model. This may be +useful, for example, in cases where a model does not have an analytic inverse, +but may have an approximate inverse that was computed numerically and is +represented by another model. This works even if the target model has a +default analytic inverse--in this case the default is overridden with the +custom inverse:: + + >>> from astropy.modeling.models import Polynomial1D + >>> linear.inverse = Polynomial1D(degree=1, c0=-1.25, c1=1.25) + >>> linear.inverse + + +If a custom inverse has been assigned to a model, it can be deleted with +``del model.inverse``. This resets the inverse to its default (if one exists). +If a default does not exist, accessing ``model.inverse`` raises a +`NotImplementedError`. For example polynomial models do not have a default +inverse:: + + >>> del linear.inverse + >>> linear.inverse + + >>> p = Polynomial1D(degree=2, c0=1.0, c1=2.0, c2=3.0) + >>> p.inverse + Traceback (most recent call last): + File "", line 1, in + File "astropy\modeling\core.py", line 796, in inverse + raise NotImplementedError("An analytical inverse transform has not " + NotImplementedError: No analytical or user-supplied inverse transform + has been implemented for this model. + +One may certainly compute an inverse and assign it to a polynomial model +though. + +.. note:: + + When assigning a custom inverse to a model no validation is performed to + ensure that it is actually an inverse or even approximate inverse. So + assign custom inverses at your own risk. + +Bounding Boxes +-------------- + +.. _bounding-boxes: + +Efficient Model Rendering with Bounding Boxes ++++++++++++++++++++++++++++++++++++++++++++++ + + +All `Model ` subclasses have a +`bounding_box ` attribute that +can be used to set the limits over which the model is significant. This greatly +improves the efficiency of evaluation when the input range is much larger than +the characteristic width of the model itself. For example, to create a sky model +image from a large survey catalog, each source should only be evaluated over the +pixels to which it contributes a significant amount of flux. This task can +otherwise be computationally prohibitive on an average CPU. + +The :func:`Model.render ` method can be used to +evaluate a model on an output array, or input coordinate arrays, limiting the +evaluation to the `bounding_box ` region if +it is set. This function will also produce postage stamp images of the model if +no other input array is passed. To instead extract postage stamps from the data +array itself, see :ref:`cutout_images`. + +Using the standard Bounding Box ++++++++++++++++++++++++++++++++ + +For basic usage, see `Model.bounding_box `. +By default no `~astropy.modeling.Model.bounding_box` is set, except on model +subclasses where a ``bounding_box`` property or method is explicitly defined. +The default is then the minimum rectangular region symmetric about the position +that fully contains the model. If the model does not have a finite extent, +the containment criteria are noted in the documentation. For example, see +``Gaussian2D.bounding_box``. + +.. warning:: + + Accessing the `Model.bounding_box ` + property when it has not been set, or does not have a default will + result in a ``NotImplementedError``. If this behavior is undesirable, + then one can instead use the `Model.get_bounding_box ` + method instead. This method will return the bounding box if one exists + (by setting or default) otherwise it will return ``None`` instead + of raising an error. + +A `Model.bounding_box ` default can be +set by the user to any callable. This is particularly useful for models created +with `~astropy.modeling.custom_model` or as a `~astropy.modeling.core.CompoundModel`:: + + >>> from astropy.modeling import custom_model + >>> def ellipsoid(x, y, z, x0=0, y0=0, z0=0, a=2, b=3, c=4, amp=1): + ... rsq = ((x - x0) / a) ** 2 + ((y - y0) / b) ** 2 + ((z - z0) / c) ** 2 + ... val = (rsq < 1) * amp + ... return val + ... + >>> class Ellipsoid3D(custom_model(ellipsoid)): + ... # A 3D ellipsoid model + ... def bounding_box(self): + ... return ((self.z0 - self.c, self.z0 + self.c), + ... (self.y0 - self.b, self.y0 + self.b), + ... (self.x0 - self.a, self.x0 + self.a)) + ... + >>> model1 = Ellipsoid3D() + >>> model1.bounding_box + ModelBoundingBox( + intervals={ + x0: Interval(lower=-2.0, upper=2.0) + x1: Interval(lower=-3.0, upper=3.0) + x2: Interval(lower=-4.0, upper=4.0) + } + model=Ellipsoid3D(inputs=('x0', 'x1', 'x2')) + order='C' + ) + +By default models are evaluated on any inputs. By passing a flag they can be evaluated +only on inputs within the bounding box. For inputs outside of the bounding_box a ``fill_value`` is +returned (``np.nan`` by default):: + + >>> model1(-5, 1, 1) + 0.0 + >>> model1(-5, 1, 1, with_bounding_box=True) + nan + >>> model1(-5, 1, 1, with_bounding_box=True, fill_value=-1) + -1.0 + +`Model.bounding_box ` can be set on any +model instance via the usage of the property setter. For example for a single +input model one needs to only set a tuple of the lower and upper bounds :: + + >>> from astropy.modeling.models import Polynomial1D + >>> model2 = Polynomial1D(2) + >>> model2.bounding_box = (-1, 1) + >>> model2.bounding_box + ModelBoundingBox( + intervals={ + x: Interval(lower=-1, upper=1) + } + model=Polynomial1D(inputs=('x',)) + order='C' + ) + >>> model2(-2) + 0.0 + >>> model2(-2, with_bounding_box=True) + nan + >>> model2(-2, with_bounding_box=True, fill_value=47) + 47.0 + +For multi-input models, `Model.bounding_box ` +can be set on any model instance by specifying a tuple of lower/upper bound tuples :: + + >>> from astropy.modeling.models import Polynomial2D + >>> model3 = Polynomial2D(2) + >>> model3.bounding_box = ((-2, 2), (-1, 1)) + >>> model3.bounding_box + ModelBoundingBox( + intervals={ + x: Interval(lower=-1, upper=1) + y: Interval(lower=-2, upper=2) + } + model=Polynomial2D(inputs=('x', 'y')) + order='C' + ) + >>> model3(-2, 0) + 0.0 + >>> model3(-2, 0, with_bounding_box=True) + nan + >>> model3(-2, 0, with_bounding_box=True, fill_value=7) + 7.0 + +Note that if one wants to directly recover the tuple used to formulate +a bounding box, then one can use the +`ModelBoundingBox.bounding_box() ` +method :: + + >>> model1.bounding_box.bounding_box() + ((np.float64(-4.0), np.float64(4.0)), (np.float64(-3.0), np.float64(3.0)), (np.float64(-2.0), np.float64(2.0))) + >>> model2.bounding_box.bounding_box() + (-1, 1) + >>> model3.bounding_box.bounding_box() + ((-2, 2), (-1, 1)) + +.. warning:: + + When setting multi-dimensional bounding boxes it is important to + remember that by default the tuple of tuples is assumed to be ``'C'`` ordered, + which means that the bound tuples will be ordered in the reverse order + to their respective input order. That is if the inputs are in the order + ``('x', 'y', 'z')`` then the bounds will need to be listed in ``('z', 'y', 'x')`` + order. + +The if one does not want to work directly with the default ``'C'`` ordered +bounding boxes. It is possible to use the alternate ``'F'`` ordering, which +orders the bounding box tuple in the same order as the inputs. To do this +one can use the `bind_bounding_box ` +function, and passing the ``order='F'`` keyword argument :: + + >>> from astropy.modeling import bind_bounding_box + >>> model4 = Polynomial2D(3) + >>> bind_bounding_box(model4, ((-1, 1), (-2, 2)), order='F') + >>> model4.bounding_box + ModelBoundingBox( + intervals={ + x: Interval(lower=-1, upper=1) + y: Interval(lower=-2, upper=2) + } + model=Polynomial2D(inputs=('x', 'y')) + order='F' + ) + >>> model4(-2, 0) + 0.0 + >>> model4(-2, 0, with_bounding_box=True) + nan + >>> model4(-2, 0, with_bounding_box=True, fill_value=12) + 12.0 + >>> model4.bounding_box.bounding_box() + ((-1, 1), (-2, 2)) + >>> model4.bounding_box.bounding_box(order='C') + ((-2, 2), (-1, 1)) + +.. warning:: + + Currently when combining models the bounding boxes of components are + combined only when joining models with the ``&`` operator. + For the other operators bounding boxes for compound models must be assigned + explicitly. A future release will determine the appropriate bounding box + for a compound model where possible. + +Using the Compound Bounding Box ++++++++++++++++++++++++++++++++ + +Sometimes it is useful to have multiple bounding boxes for the same model, +which are selectable when the model is evaluated. In this case, one should +consider using a `CompoundBoundingBox `. + +A common use case for this may be if the model has a single "discrete" +selector input (for example ``'slit_id'``), which among other things, +determines what bounding box should be applied to the other inputs. To +do this one needs to first define a dictionary of bounding box tuples, +with dictionary keys being the specific values of the selector input +corresponding to that specific bounding box :: + + >>> from astropy.modeling.models import Shift, Identity + >>> model1 = Shift(1) & Shift(2) & Identity(1) + >>> model1.inputs = ('x', 'y', 'slit_id') + >>> bboxes = { + ... 0: ((0, 1), (1, 2)), + ... 1: ((2, 3), (3, 4)) + ... } + +In order for the compound bounding box to function one must specify a list +of selector arguments, where the elements of this list are tuples of the input's +name and whether or not the bounding box should be applied to the selector argument +or not. In this case, it makes sense for the selector argument to be ignored :: + + >>> from astropy.modeling.core import bind_compound_bounding_box + >>> selector_args = [('slit_id', True)] + >>> bind_compound_bounding_box(model1, bboxes, selector_args, order='F') + >>> model1.bounding_box + CompoundBoundingBox( + bounding_boxes={ + (0,) = ModelBoundingBox( + intervals={ + x: Interval(lower=0, upper=1) + y: Interval(lower=1, upper=2) + } + ignored=['slit_id'] + model=CompoundModel(inputs=('x', 'y', 'slit_id')) + order='F' + ) + (1,) = ModelBoundingBox( + intervals={ + x: Interval(lower=2, upper=3) + y: Interval(lower=3, upper=4) + } + ignored=['slit_id'] + model=CompoundModel(inputs=('x', 'y', 'slit_id')) + order='F' + ) + } + selector_args = SelectorArguments( + Argument(name='slit_id', ignore=True) + ) + ) + >>> model1(0.5, 1.5, 0, with_bounding_box=True) + (1.5, 3.5, 0.0) + >>> model1(0.5, 1.5, 1, with_bounding_box=True) + (np.float64(nan), np.float64(nan), np.float64(nan)) + +Multiple selector arguments can also be used, in this case the keys of the +dictionary of bounding boxes need to be specified as tuples of values :: + + >>> model2 = Shift(1) & Shift(2) & Identity(2) + >>> model2.inputs = ('x', 'y', 'slit_x', 'slit_y') + >>> bboxes = { + ... (0, 0): ((0, 1), (1, 2)), + ... (0, 1): ((2, 3), (3, 4)), + ... (1, 0): ((4, 5), (5, 6)), + ... (1, 1): ((6, 7), (7, 8)), + ... } + >>> selector_args = [('slit_x', True), ('slit_y', True)] + >>> bind_compound_bounding_box(model2, bboxes, selector_args, order='F') + >>> model2.bounding_box + CompoundBoundingBox( + bounding_boxes={ + (0, 0) = ModelBoundingBox( + intervals={ + x: Interval(lower=0, upper=1) + y: Interval(lower=1, upper=2) + } + ignored=['slit_x', 'slit_y'] + model=CompoundModel(inputs=('x', 'y', 'slit_x', 'slit_y')) + order='F' + ) + (0, 1) = ModelBoundingBox( + intervals={ + x: Interval(lower=2, upper=3) + y: Interval(lower=3, upper=4) + } + ignored=['slit_x', 'slit_y'] + model=CompoundModel(inputs=('x', 'y', 'slit_x', 'slit_y')) + order='F' + ) + (1, 0) = ModelBoundingBox( + intervals={ + x: Interval(lower=4, upper=5) + y: Interval(lower=5, upper=6) + } + ignored=['slit_x', 'slit_y'] + model=CompoundModel(inputs=('x', 'y', 'slit_x', 'slit_y')) + order='F' + ) + (1, 1) = ModelBoundingBox( + intervals={ + x: Interval(lower=6, upper=7) + y: Interval(lower=7, upper=8) + } + ignored=['slit_x', 'slit_y'] + model=CompoundModel(inputs=('x', 'y', 'slit_x', 'slit_y')) + order='F' + ) + } + selector_args = SelectorArguments( + Argument(name='slit_x', ignore=True) + Argument(name='slit_y', ignore=True) + ) + ) + >>> model2(0.5, 1.5, 0, 0, with_bounding_box=True) + (1.5, 3.5, 0.0, 0.0) + >>> model2(0.5, 1.5, 1, 1, with_bounding_box=True) + (np.float64(nan), np.float64(nan), np.float64(nan), np.float64(nan)) + +Note that one can also specify the ordering for all the bounding boxes +comprising the compound bounding using the ``order`` keyword argument. + +Another use case for this maybe a if one wants to use multiple bounding +boxes for the same model, where the user chooses the bounding box when +evaluating the model. In this case, one must still choose a selector +argument as a fall back default for bounding box selection; however, this +argument should not be ignored by the bounding box:: + + >>> from astropy.modeling.models import Polynomial2D + >>> from astropy.modeling import bind_compound_bounding_box + >>> model = Polynomial2D(3) + >>> bboxes = { + ... 0: ((0, 1), (1, 2)), + ... 1: ((2, 3), (3, 4)) + ... } + >>> selector_args = [('x', False)] + >>> bind_compound_bounding_box(model, bboxes, selector_args, order='F') + >>> model.bounding_box + CompoundBoundingBox( + bounding_boxes={ + (0,) = ModelBoundingBox( + intervals={ + x: Interval(lower=0, upper=1) + y: Interval(lower=1, upper=2) + } + model=Polynomial2D(inputs=('x', 'y')) + order='F' + ) + (1,) = ModelBoundingBox( + intervals={ + x: Interval(lower=2, upper=3) + y: Interval(lower=3, upper=4) + } + model=Polynomial2D(inputs=('x', 'y')) + order='F' + ) + } + selector_args = SelectorArguments( + Argument(name='x', ignore=False) + ) + ) + +For the user to select the bounding box on evaluation, instead of +specifying, ``with_bounding_box=True`` as the keyword argument; the user +instead specifies ``with_bounding_box=`` :: + + >>> model(0.5, 1.5, with_bounding_box=0) + 0.0 + >>> model(0.5, 1.5, with_bounding_box=1) + nan + + +Ignoring Inputs in Bounding Boxes ++++++++++++++++++++++++++++++++++ + +Both `standard bounding box ` +and `CompoundBoundingBox ` +support ignoring specific inputs from enforcement by the bounding box. Effectively, +for multi-dimensional models one can define bounding boxes so that bounds are +only applied to a subset of the model's inputs rather than the default of enforcing +a bound of some kind on every input. Note that use of this feature is equivalent +to defining the bounds for an input to be ``[-np.inf, np.inf]``. + +.. warning:: + The ``ignored`` input feature is not available when constructing/adding bounding + boxes to models using tuples and the property interface. That is one cannot + ignore inputs when setting bounding boxes using ``model.bounding_box = (-1, 1)``. + This feature is only available via the methods + `bind_bounding_box ` and + `bind_compound_bounding_box `. + +Ignoring inputs for a bounding box can be achieved via passing a list of the input +name strings to be ignored to the ``ignored`` keyword argument in any of the main +bounding box interfaces. :: + + >>> from astropy.modeling.models import Polynomial1D + >>> from astropy.modeling import bind_bounding_box + >>> model1 = Polynomial2D(3) + >>> bind_bounding_box(model1, {'x': (-1, 1)}, ignored=['y']) + >>> model1.bounding_box + ModelBoundingBox( + intervals={ + x: Interval(lower=-1, upper=1) + } + ignored=['y'] + model=Polynomial2D(inputs=('x', 'y')) + order='C' + ) + >>> model1(-2, 0, with_bounding_box=True) + nan + >>> model1(0, 300, with_bounding_box=True) + 0.0 + +Similarly, the ignored inputs will be applied to all of the bounding boxes +contained within a compound bounding box. :: + + >>> from astropy.modeling import bind_compound_bounding_box + >>> model2 = Polynomial2D(3) + >>> bboxes = { + ... 0: {'x': (0, 1)}, + ... 1: {'x': (1, 2)} + ... } + >>> selector_args = [('x', False)] + >>> bind_compound_bounding_box(model2, bboxes, selector_args, ignored=['y'], order='F') + >>> model2.bounding_box + CompoundBoundingBox( + bounding_boxes={ + (0,) = ModelBoundingBox( + intervals={ + x: Interval(lower=0, upper=1) + } + ignored=['y'] + model=Polynomial2D(inputs=('x', 'y')) + order='F' + ) + (1,) = ModelBoundingBox( + intervals={ + x: Interval(lower=1, upper=2) + } + ignored=['y'] + model=Polynomial2D(inputs=('x', 'y')) + order='F' + ) + } + selector_args = SelectorArguments( + Argument(name='x', ignore=False) + ) + ) + >>> model2(0.5, 300, with_bounding_box=0) + 0.0 + >>> model2(0.5, 300, with_bounding_box=1) + nan + + +Efficient evaluation with `Model.render() ` +-------------------------------------------------------------------------- + +When a model is evaluated over a range much larger than the model itself, it +may be prudent to use the :func:`Model.render ` +method if efficiency is a concern. The :func:`render ` +method can be used to evaluate the model on an +array of the same dimensions. ``model.render()`` can be called with no +arguments to return a "postage stamp" of the bounding box region. + +In this example, we generate a 300x400 pixel image of 100 2D Gaussian sources. +For comparison, the models are evaluated both with and without using bounding +boxes. By using bounding boxes, the evaluation speed increases by approximately +a factor of 10 with negligible loss of information. + +.. plot:: + :include-source: + + import numpy as np + from time import time + from astropy.modeling import models + import matplotlib.pyplot as plt + from matplotlib.patches import Rectangle + + imshape = (300, 400) + y, x = np.indices(imshape) + + # Generate random source model list + rng = np.random.default_rng(0) + nsrc = 100 + model_params = [ + dict(amplitude=rng.uniform(.5, 1), + x_mean=rng.uniform(0, imshape[1] - 1), + y_mean=rng.uniform(0, imshape[0] - 1), + x_stddev=rng.uniform(2, 6), + y_stddev=rng.uniform(2, 6), + theta=rng.uniform(0, 2 * np.pi)) + for _ in range(nsrc)] + + model_list = [models.Gaussian2D(**kwargs) for kwargs in model_params] + + # Render models to image using bounding boxes + bb_image = np.zeros(imshape) + t_bb = time() + for model in model_list: + model.render(bb_image) + t_bb = time() - t_bb + + # Render models to image using full evaluation + full_image = np.zeros(imshape) + t_full = time() + for model in model_list: + model.bounding_box = None + model.render(full_image) + t_full = time() - t_full + + flux = full_image.sum() + diff = (full_image - bb_image) + max_err = diff.max() + + # Plots + fig, axs = plt.subplots(figsize=(16, 7), ncols=2) + fig.subplots_adjust(left=.05, right=.97, bottom=.03, top=.97, wspace=0.15) + + # Full model image + ax1 = axs[0] + ax1.imshow(full_image, origin='lower') + ax1.set_title(f'Full Models\nTiming: {t_full:.2f} seconds', fontsize=16) + ax1.set(xlabel='x', ylabel='y') + + # Bounded model image with boxes overplotted + ax2 = axs[1] + ax2.imshow(bb_image, origin='lower') + for model in model_list: + del model.bounding_box # Reset bounding_box to its default + dy, dx = np.diff(model.bounding_box).flatten() + pos = (model.x_mean.value - dx / 2, model.y_mean.value - dy / 2) + r = Rectangle(pos, dx, dy, edgecolor='w', facecolor='none', alpha=.25) + ax2.add_patch(r) + ax2.set_title(f'Bounded Models\nTiming: {t_bb:.2f} seconds', fontsize=16) + ax2.set(xlabel='x', ylabel='y') + + # Difference image + fig2, ax = plt.subplots(figsize=(16, 8)) + im = ax.imshow(diff, vmin=-max_err, vmax=max_err) + fig2.colorbar(im, format='%.1e') + ax.set_title(f'Difference Image\nTotal Flux Err = {((flux - np.sum(bb_image)) / flux):.0e}') + ax.set(xlabel='x', ylabel='y') + plt.show() + + + +.. _separability: + +Model Separability +------------------ + +Simple models have a boolean `Model.separable ` property. +It indicates whether the outputs are independent and is essential for computing the +separability of compound models using the :func:`~astropy.modeling.is_separable` function. +Having a separable compound model means that it can be decomposed into independent models, +which in turn is useful in many applications. +For example, it may be easier to define inverses using the independent parts of a model +than the entire model. +In other cases, tools using `Generalized World Coordinate System (GWCS)`_, +can be more flexible and take advantage of separable spectral and spatial transforms. + +If a custom subclass of `~astropy.modeling.Model` needs to override the +computation of its separability it can implement the +``_calculate_separability_matrix`` method which should return the separability +matrix for that model. + + +.. _modeling-model-sets: + +Model Sets +========== + +In some cases it is useful to describe many models of the same type but with +different sets of parameter values. This could be done simply by instantiating +as many instances of a `~astropy.modeling.Model` as are needed. But that can +be inefficient for a large number of models. To that end, all model classes in +`astropy.modeling` can also be used to represent a model **set** which is a +collection of models of the same type, but with different values for their +parameters. + +To instantiate a model set, use argument ``n_models=N`` where ``N`` is the +number of models in the set when constructing the model. The value of each +parameter must be a list or array of length ``N``, such that each item in +the array corresponds to one model in the set:: + + >>> from astropy.modeling import models + >>> g = models.Gaussian1D(amplitude=[1, 2], mean=[0, 0], + ... stddev=[0.1, 0.2], n_models=2) + >>> print(g) + Model: Gaussian1D + Inputs: ('x',) + Outputs: ('y',) + Model set size: 2 + Parameters: + amplitude mean stddev + --------- ---- ------ + 1.0 0.0 0.1 + 2.0 0.0 0.2 + +This is equivalent to two Gaussians with the parameters ``amplitude=1, mean=0, +stddev=0.1`` and ``amplitude=2, mean=0, stddev=0.2`` respectively. When +printing the model the parameter values are displayed as a table, with each row +corresponding to a single model in the set. + +The number of models in a model set can be determined using the `len` builtin:: + + >>> len(g) + 2 + +Single models have a length of 1, and are not considered a model set as such. + +When evaluating a model set, by default the input must be the same length as +the number of models, with one input per model:: + + >>> g([0, 0.1]) # doctest: +FLOAT_CMP + array([1. , 1.76499381]) + +The result is an array with one result per model in the set. It is also +possible to broadcast a single input value to all models in the set:: + + >>> g(0) # doctest: +FLOAT_CMP + array([1., 2.]) + +Or when the input is an array:: + + >>> x = np.array([[0, 0, 0], [0.1, 0.1, 0.1]]) + >>> print(x) + [[0. 0. 0. ] + [0.1 0.1 0.1]] + >>> g(x) + array([[1. , 1. , 1. ], + [1.76499381, 1.76499381, 1.76499381]]) + +Internally the shape of the inputs, outputs, and parameter values is controlled +by an attribute - ``model_set_axis``. In the above case ``model_set_axis=0``:: + + >>> g.model_set_axis + 0 + +This indicates that elements along the 0-th axis will be passed as inputs to individual models. +Sometimes it may be useful to pass inputs along a different axis, for example the 1st axis:: + + >>> x = np.array([[0, 0, 0], [0.1, 0.1, 0.1]]).T + >>> print(x) + [[0. 0.1] + [0. 0.1] + [0. 0.1]] + +Because there are two models in this model set and we are passing three inputs +along the 0th axis, evaluation will fail:: + + >>> g(x) + Traceback (most recent call last): + ... + ValueError: Input argument 'x' does not have the correct dimensions in + model_set_axis=0 for a model set with n_models=2. + +There are two ways to get around this. ``model_set_axis`` can be passed in +when the model is evaluated:: + + >>> g(x, model_set_axis=1) + array([[1. , 1.76499381], + [1. , 1.76499381], + [1. , 1.76499381]]) + +Or when the model is initialized:: + + >>> g = models.Gaussian1D(amplitude=[[1, 2]], mean=[[0, 0]], + ... stddev=[[0.1, 0.2]], n_models=2, + ... model_set_axis=1) + >>> g(x) + array([[1. , 1.76499381], + [1. , 1.76499381], + [1. , 1.76499381]]) + +Note that in the latter case, the shape of the individual parameters has changed to 2D +because now the parameters are defined along the 1st axis. + +The value of ``model_set_axis`` is either an integer number, representing the axis along which +the different parameter sets and inputs are defined, or a boolean of value ``False``, +in which case it indicates all model sets should use the same inputs on evaluation. +For example, the above model has a value of 1 for ``model_set_axis``. +If ``model_set_axis=False`` is passed the two models will be evaluated on the same input:: + + >>> g.model_set_axis + 1 + >>> result = g(x, model_set_axis=False) + >>> result + array([[[1. , 0.60653066], + [2. , 1.76499381]], + + [[1. , 0.60653066], + [2. , 1.76499381]], + + [[1. , 0.60653066], + [2. , 1.76499381]]]) + >>> result[: , 0] + array([[1. , 0.60653066], + [1. , 0.60653066], + [1. , 0.60653066]]) + >>> result[: , 1] + array([[2. , 1.76499381], + [2. , 1.76499381], + [2. , 1.76499381]]) + +Currently model sets are most useful for fitting a set of **linear** models +(:ref:`example `) +allowing a large number of models of the same type to be fitted simultaneously +(and independently from each other) to some large set of inputs, such as +fitting a polynomial to the time response of each pixel in a data cube. +This can greatly speed up the fitting process. The speed-up is due to solving +the set of equations to find the exact solution. Nonlinear models, which require +an iterative algorithm, cannot be currently fit using model sets. Model sets of nonlinear +models can only be evaluated. + +When fitting model sets it is important that data arrays are passed to the fitter +in the correct shape. The shape depends on the ``model_set_axis`` attribute of the +model to be fit. The rule is that the index of the dependent variable that corresponds +to a model set should be along the ``model_set_axis`` dimension. For example, for a +1D model set with 3 models with ``model_set_axis == 1`` the shape of ``y`` should be (x, 3):: + + >>> import numpy as np + >>> from astropy.modeling.models import Polynomial1D + >>> from astropy.modeling.fitting import LinearLSQFitter + >>> fitter = LinearLSQFitter() + >>> x = np.arange(4) + >>> y = np.array([2*x+1, x+4, x]).T + >>> print(y) + [[1 4 0] + [3 5 1] + [5 6 2] + [7 7 3]] + >>> print(y.shape) + (4, 3) + >>> m = Polynomial1D(1, n_models=3, model_set_axis=1) + >>> mfit = fitter(m, x, y) + +For 2D models with 3 models and ``model_set_axis = 0`` the shape of ``z`` should be (3, x, y):: + + >>> import numpy as np + >>> from astropy.modeling.models import Polynomial2D + >>> from astropy.modeling.fitting import LinearLSQFitter + >>> fitter = LinearLSQFitter() + >>> x = np.arange(8).reshape(2, 4) + >>> y = x + >>> z = np.asarray([2 * x + 1, x + 4, x + 3]) + >>> print(z.shape) + (3, 2, 4) + >>> m = Polynomial2D(1, n_models=3, model_set_axis=0) + >>> mfit = fitter(m, x, y, z) + +.. _modeling-asdf: + +Model Serialization (Writing a Model to a File) +=============================================== + +Models are serializable using the `ASDF`_ +format. This can be useful in many contexts, one of which is the implementation of a +`Generalized World Coordinate System (GWCS)`_. + +Serializing a model to disk is possible by assigning the object to ``AsdfFile.tree``: + +.. doctest-requires:: asdf-astropy + + >>> from asdf import AsdfFile + >>> from astropy.modeling import models + >>> rotation = models.Rotation2D(angle=23.7) + >>> f = AsdfFile() + >>> f.tree['model'] = rotation + >>> f.write_to('rotation.asdf') + +To read the file and create the model: + +.. doctest-requires:: asdf-astropy + + >>> import asdf + >>> with asdf.open('rotation.asdf') as f: + ... model = f.tree['model'] + >>> print(model) + Model: Rotation2D + Inputs: ('x', 'y') + Outputs: ('x', 'y') + Model set size: 1 + Parameters: + angle + ----- + 23.7 + +Compound models can also be serialized. Please note that some model attributes (e.g ``meta``, +``tied`` parameter constraints used in fitting), as well as model sets are not yet serializable. +For more information on serialization of models, see :ref:`asdf-astropy:asdf-astropy`. diff --git a/docs/modeling/new-fitter.rst b/docs/modeling/new-fitter.rst new file mode 100644 index 000000000000..9e26b95234c0 --- /dev/null +++ b/docs/modeling/new-fitter.rst @@ -0,0 +1,187 @@ +.. _new_fitter: + +Defining New Fitter Classes +*************************** + +This section describes how to add a new nonlinear fitting algorithm to this +package or write a user-defined fitter. In short, one needs to define an error +function and a ``__call__`` method and define the types of constraints which +work with this fitter (if any). + +The details are described below using scipy's SLSQP algorithm as an example. +The base class for all fitters is `~astropy.modeling.fitting.Fitter`:: + + class SLSQPFitter(Fitter): + supported_constraints = ['bounds', 'eqcons', 'ineqcons', 'fixed', + 'tied'] + + def __init__(self): + # Most currently defined fitters take no arguments in their + # __init__, but the option certainly exists for custom fitters + super().__init__() + +All fitters take a model (their ``__call__`` method modifies the model's +parameters) as their first argument. + +Next, the error function takes a list of parameters returned by an iteration of +the fitting algorithm and input coordinates, evaluates the model with them and +returns some type of a measure for the fit. In the example the sum of the +squared residuals is used as a measure of fitting.:: + + def objective_function(self, fps, *args): + model = args[0] + meas = args[-1] + model.fitparams(fps) + res = self.model(*args[1:-1]) - meas + return np.sum(res**2) + +The ``__call__`` method performs the fitting. As a minimum it takes all +coordinates as separate arguments. Additional arguments are passed as +necessary:: + + def __call__(self, model, x, y , maxiter=MAXITER, epsilon=EPS): + if model.linear: + raise ModelLinearityException( + 'Model is linear in parameters; ' + 'non-linear fitting methods should not be used.') + model_copy = model.copy() + init_values, _ = model_to_fit_params(model_copy) + self.fitparams = optimize.fmin_slsqp(self.errorfunc, p0=init_values, + args=(y, x), + bounds=self.bounds, + eqcons=self.eqcons, + ineqcons=self.ineqcons) + return model_copy + +Defining a Plugin Fitter +======================== + +`astropy.modeling` includes a plugin mechanism which allows fitters +defined outside of astropy's core to be inserted into the +`astropy.modeling.fitting` namespace through the use of entry points. +Entry points are references to importable objects. A tutorial on defining +entry points can be found in `setuptools' documentation `_. +Plugin fitters must to extend from the `~astropy.modeling.fitting.Fitter` +base class. For the fitter to be discovered and inserted into +`astropy.modeling.fitting` the entry points must be inserted into +the `astropy.modeling` entry point group:: + + setup( + # ... + entry_points = {'astropy.modeling': 'PluginFitterName = fitter_module:PlugFitterClass'} + ) + +This would allow users to import the ``PlugFitterName`` through `astropy.modeling.fitting` by:: + + from astropy.modeling.fitting import PlugFitterName + +One project which uses this functionality is `Saba `_ +and be can be used as a reference. + +Using a Custom Statistic Function +================================= + +This section describes how to write a new fitter with a user-defined statistic +function. The example below shows a specialized class which fits a straight +line with uncertainties in both variables. + +The following import statements are needed:: + + import numpy as np + from astropy.modeling.fitting import (_validate_model, + fitter_to_model_params, + model_to_fit_params, Fitter, + _convert_input) + from astropy.modeling.optimizers import Simplex + +First one needs to define a statistic. This can be a function or a callable +class.:: + + def chi_line(measured_vals, updated_model, x_sigma, y_sigma, x): + """ + Chi^2 statistic for fitting a straight line with uncertainties in x and + y. + + Parameters + ---------- + measured_vals : array + updated_model : `~astropy.modeling.ParametricModel` + model with parameters set by the current iteration of the optimizer + x_sigma : array + uncertainties in x + y_sigma : array + uncertainties in y + + """ + model_vals = updated_model(x) + if x_sigma is None and y_sigma is None: + return np.sum((model_vals - measured_vals) ** 2) + elif x_sigma is not None and y_sigma is not None: + weights = 1 / (y_sigma ** 2 + updated_model.parameters[1] ** 2 * + x_sigma ** 2) + return np.sum((weights * (model_vals - measured_vals)) ** 2) + else: + if x_sigma is not None: + weights = 1 / x_sigma ** 2 + else: + weights = 1 / y_sigma ** 2 + return np.sum((weights * (model_vals - measured_vals)) ** 2) + +In general, to define a new fitter, all one needs to do is provide a statistic +function and an optimizer. In this example we will let the optimizer be an +optional argument to the fitter and will set the statistic to ``chi_line`` +above:: + + class LineFitter(Fitter): + """ + Fit a straight line with uncertainties in both variables + + Parameters + ---------- + optimizer : class or callable + one of the classes in optimizers.py (default: Simplex) + """ + + def __init__(self, optimizer=Simplex): + self.statistic = chi_line + super().__init__(optimizer, statistic=self.statistic) + +The last thing to define is the ``__call__`` method:: + + def __call__(self, model, x, y, x_sigma=None, y_sigma=None, **kwargs): + """ + Fit data to this model. + + Parameters + ---------- + model : `~astropy.modeling.core.ParametricModel` + model to fit to x, y + x : array + input coordinates + y : array + input coordinates + x_sigma : array + uncertainties in x + y_sigma : array + uncertainties in y + kwargs : dict + optional keyword arguments to be passed to the optimizer + + Returns + ------ + model_copy : `~astropy.modeling.core.ParametricModel` + a copy of the input model with parameters set by the fitter + + """ + model_copy = _validate_model(model, + self._opt_method.supported_constraints) + + farg = _convert_input(x, y) + farg = (model_copy, x_sigma, y_sigma) + farg + p0, _, _ = model_to_fit_params(model_copy) + + fitparams, self.fit_info = self._opt_method( + self.objective_function, p0, farg, **kwargs) + fitter_to_model_params(model_copy, fitparams) + + return model_copy diff --git a/docs/modeling/new-model.rst b/docs/modeling/new-model.rst new file mode 100644 index 000000000000..24e252f6fe3c --- /dev/null +++ b/docs/modeling/new-model.rst @@ -0,0 +1,285 @@ +.. _modeling-new-classes: + +************************** +Defining New Model Classes +************************** + +This document describes how to add a model to the package or to create a +user-defined model. In short, one needs to define all model parameters and +write a function which evaluates the model, that is, computes the mathematical +function that implements the model. If the model is fittable, a function to +compute the derivatives with respect to parameters is required if a linear +fitting algorithm is to be used and optional if a non-linear fitter is to be +used. + + +Basic custom models +=================== + +For most cases, the `~astropy.modeling.custom_model` decorator provides an +easy way to make a new `~astropy.modeling.Model` class from an existing Python +callable. The following example demonstrates how to set up a model consisting +of two Gaussians: + +.. plot:: + :include-source: + + import numpy as np + import matplotlib.pyplot as plt + from astropy.modeling.models import custom_model + from astropy.modeling.fitting import TRFLSQFitter + + # Define model + @custom_model + def sum_of_gaussians(x, amplitude1=1.0, mean1=-1.0, sigma1=1.0, + amplitude2=1.0, mean2=1.5, sigma2=1.0): + return (amplitude1 * np.exp(-0.5 * ((x - mean1) / sigma1)**2) + + amplitude2 * np.exp(-0.5 * ((x - mean2) / sigma2)**2)) + + # Generate fake data with some noise + rng = np.random.default_rng(0) + x = np.linspace(-5., 5., 200) + m_ref = sum_of_gaussians(amplitude1=2., mean1=-0.5, sigma1=0.4, + amplitude2=0.5, mean2=2., sigma2=1.0) + y = m_ref(x) + rng.normal(0., 0.05, x.shape) + + # Fit model to data + m_init = sum_of_gaussians() + fit = TRFLSQFitter() + m = fit(m_init, x, y) + + # Plot the data and the best fit + fig, ax = plt.subplots() + ax.plot(x, y, 'o', color='k') + ax.plot(x, m(x)) + + +This decorator also supports setting a model's +`~astropy.modeling.FittableModel.fit_deriv` as well as creating models with +more than one inputs. Note that when creating a model from a function with +multiple outputs, the keyword argument ``n_outputs`` must be set to the +number of outputs of the function. It can also be used as a normal factory +function (for example ``SumOfGaussians = custom_model(sum_of_gaussians)``) +rather than as a decorator. See the `~astropy.modeling.custom_model` +documentation for more examples. + + +A step by step definition of a 1-D Gaussian model +================================================= + +The example described in `Basic custom models`_ can be used for most simple +cases, but the following section describes how to construct model classes in +general. Defining a full model class may be desirable, for example, to +provide more specialized parameters, or to implement special functionality not +supported by the basic `~astropy.modeling.custom_model` factory function. + +The details are explained below with a 1-D Gaussian model as an example. There +are two base classes for models. If the model is fittable, it should inherit +from `~astropy.modeling.FittableModel`; if not it should subclass +`~astropy.modeling.Model`. + +If the model takes parameters they should be specified as class attributes in +the model's class definition using the `~astropy.modeling.Parameter` +descriptor. All arguments to the Parameter constructor are optional, and may +include a default value for that parameter, a text description of the parameter +(useful for `help` and documentation generation), as well default constraints +and custom getters/setters for the parameter value. It is also possible to +define a "validator" method for each parameter, enabling custom code to check +whether that parameter's value is valid according to the model definition (for +example if it must be non-negative). See the example in +`Parameter.validator ` for more details. +Note, that if pickling the model is important the validator function should be +assigned directly to the instance ``Parameter._validator`` instead of using +the decorator. + +:: + + from astropy.modeling import Fittable1DModel, Parameter + + class Gaussian1D(Fittable1DModel): + n_inputs = 1 + n_outputs = 1 + + amplitude = Parameter() + mean = Parameter() + stddev = Parameter() + +The ``n_inputs`` and ``n_outputs`` class attributes must be integers +indicating the number of independent variables that are input to evaluate the +model, and the number of outputs it returns. The labels of the inputs and +outputs, ``inputs`` and ``outputs``, are generated automatically. It is possible +to overwrite the default ones by assigning the desired values in the class ``__init__`` +method, after calling ``super``. ``outputs`` and ``inputs`` must be tuples of +strings with length ``n_outputs`` and ``n_inputs`` respectively. +Outputs may have the same labels as inputs (eg. ``inputs = ('x', 'y')`` and ``outputs = ('x', 'y')``). +However, inputs must not conflict with each other (eg. ``inputs = ('x', 'x')`` is +incorrect) and likewise for outputs. + +There are two helpful base classes in the modeling package that can be used to +avoid specifying ``n_inputs`` and ``n_outputs`` for most common models. These are +`~astropy.modeling.Fittable1DModel` and `~astropy.modeling.Fittable2DModel`. +For example, the actual `~astropy.modeling.functional_models.Gaussian1D` model is +a subclass of `~astropy.modeling.Fittable1DModel`. This helps cut +down on boilerplate by not having to specify ``n_inputs``, ``n_outputs``, ``inputs`` +and ``outputs`` for many models (follow the link to Gaussian1D to see its source code, for +example). + +Fittable models can be linear or nonlinear in a regression sense. The default +value of the `~astropy.modeling.Model.linear` attribute is ``False``. Linear +models should define the ``linear`` class attribute as ``True``. Because this +model is non-linear we can stick with the default. + +Models which inherit from `~astropy.modeling.Fittable1DModel` have the +``Model._separable`` property already set to ``True``. +All other models should define this property to indicate the +:ref:`separability`. + +Next, provide methods called ``evaluate`` to evaluate the model and +``fit_deriv``, to compute its derivatives with respect to parameters. These +may be normal methods, `classmethod`, or `staticmethod`, though the convention +is to use `staticmethod` when the function does not depend on any of the +object's other attributes (i.e., it does not reference ``self``) or any of the +class's other attributes as in the case of `classmethod`. The evaluation +method takes all input coordinates as separate arguments and all of the model's +parameters in the same order they would be listed by +`~astropy.modeling.Model.param_names`. + +For this example:: + + @staticmethod + def evaluate(x, amplitude, mean, stddev): + return amplitude * np.exp((-(1 / (2. * stddev**2)) * (x - mean)**2)) + +It should be made clear that the ``evaluate`` method must be designed to take +the model's parameter values as arguments. This may seem at odds with the fact +that the parameter values are already available via attribute of the model +(eg. ``model.amplitude``). However, passing the parameter values directly to +``evaluate`` is a more efficient way to use it in many cases, such as fitting. + +Users of your model would not generally use ``evaluate`` directly. Instead +they create an instance of the model and call it on some input. The +``__call__`` method of models uses ``evaluate`` internally, but users do not +need to be aware of it. The default ``__call__`` implementation also handles +details such as checking that the inputs are correctly formatted and follow +Numpy's broadcasting rules before attempting to evaluate the model. + +Like ``evaluate``, the ``fit_deriv`` method takes as input all coordinates and +all parameter values as arguments. There is an option to compute numerical +derivatives for nonlinear models in which case the ``fit_deriv`` method should +be ``None``:: + + @staticmethod + def fit_deriv(x, amplitude, mean, stddev): + d_amplitude = np.exp(- 0.5 / stddev**2 * (x - mean)**2) + d_mean = (amplitude * + np.exp(- 0.5 / stddev**2 * (x - mean)**2) * + (x - mean) / stddev**2) + d_stddev = (2 * amplitude * + np.exp(- 0.5 / stddev**2 * (x - mean)**2) * + (x - mean)**2 / stddev**3) + return [d_amplitude, d_mean, d_stddev] + + +Note that we did *not* have to define an ``__init__`` method or a ``__call__`` +method for our model. For most models the ``__init__`` follows the same pattern, +taking the parameter values as positional arguments, followed by several optional +keyword arguments (constraints, etc.). The modeling framework automatically generates an +``__init__`` for your class that has the correct calling signature (see for +yourself by calling ``help(Gaussian1D.__init__)`` on the example model we just +defined). + +There are cases where it might be desirable to define a custom ``__init__``. +For example, the `~astropy.modeling.functional_models.Gaussian2D` model takes +an optional ``cov_matrix`` argument which can be used as an alternative way to +specify the x/y_stddev and theta parameters. This is perfectly valid so long +as the ``__init__`` determines appropriate values for the actual parameters and +then calls the super ``__init__`` with the standard arguments. Schematically +this looks something like: + +.. code-block:: python + + def __init__(self, amplitude, x_mean, y_mean, x_stddev=None, + y_stddev=None, theta=None, cov_matrix=None, **kwargs): + # The **kwargs here should be understood as other keyword arguments + # accepted by the basic Model.__init__ (such as constraints) + if cov_matrix is not None: + # Set x/y_stddev and theta from the covariance matrix + x_stddev = ... + y_stddev = ... + theta = ... + + # Don't pass on cov_matrix since it doesn't mean anything to the base + # class + super().__init__(amplitude, x_mean, y_mean, x_stddev, y_stddev, theta, + **kwargs) + + +Full example +------------ + +.. code-block:: python + + import numpy as np + from astropy.modeling import Fittable1DModel, Parameter + + class Gaussian1D(Fittable1DModel): + amplitude = Parameter() + mean = Parameter() + stddev = Parameter() + + @staticmethod + def evaluate(x, amplitude, mean, stddev): + return amplitude * np.exp((-(1 / (2. * stddev**2)) * (x - mean)**2)) + + @staticmethod + def fit_deriv(x, amplitude, mean, stddev): + d_amplitude = np.exp((-(1 / (stddev**2)) * (x - mean)**2)) + d_mean = (2 * amplitude * + np.exp((-(1 / (stddev**2)) * (x - mean)**2)) * + (x - mean) / (stddev**2)) + d_stddev = (2 * amplitude * + np.exp((-(1 / (stddev**2)) * (x - mean)**2)) * + ((x - mean)**2) / (stddev**3)) + return [d_amplitude, d_mean, d_stddev] + + +A full example of a LineModel +============================= + +This example demonstrates one other optional feature for model classes, which +is an *inverse*. An `~astropy.modeling.Model.inverse` implementation should be +a `property` that returns a new model instance (not necessarily of the same +class as the model being inverted) that computes the inverse of that model, so +that for some model instance with an inverse, ``model.inverse(model(*input)) == +input``. + +.. code-block:: python + + import numpy as np + from astropy.modeling import Fittable1DModel, Parameter + + class LineModel(Fittable1DModel): + slope = Parameter() + intercept = Parameter() + linear = True + + @staticmethod + def evaluate(x, slope, intercept): + return slope * x + intercept + + @staticmethod + def fit_deriv(x, slope, intercept): + d_slope = x + d_intercept = np.ones_like(x) + return [d_slope, d_intercept] + + @property + def inverse(self): + new_slope = self.slope ** -1 + new_intercept = -self.intercept / self.slope + return LineModel(slope=new_slope, intercept=new_intercept) + +.. note:: + + The above example is essentially equivalent to the built-in + `~astropy.modeling.functional_models.Linear1D` model. diff --git a/docs/modeling/parallel-fitting.rst b/docs/modeling/parallel-fitting.rst new file mode 100644 index 000000000000..f3fd277a403a --- /dev/null +++ b/docs/modeling/parallel-fitting.rst @@ -0,0 +1,451 @@ +.. _parallel-fitting: + +Fitting models in parallel with N-dimensional data +************************************************** + +In some cases, you may want to fit a model many times to data. For example, you +may have a spectral cube (with two celestial axes and one spectral axis) and you +want to fit a 1D model (which could be either a simple Gaussian model or a +complex compound model with multiple lines and a continuum) to each individual +spectrum in the cube. Alternatively, you may have a cube with two celestial +axes, one spectral axis, and one time axis, and you want to fit a 2D model to +each 2D celestial plane in the cube. Provided each model fit can be treated as +independent, there are significant performance benefits to carrying out these +model fits in parallel. + +The :func:`~astropy.modeling.fitting.parallel_fit_dask` function is ideally +suited to these use cases. It makes it simple to set up fitting of M-dimensional +models to N-dimensional datasets and leverages the power of the `dask +`_ package to efficiently parallelize the problem, +running it either on multiple processes of a single machine or in a distributed +environment. You do not need to know how to use dask in order to use this function, +but you will need to make sure you have `dask `_ +installed. + +Note that the approach here is different from *model sets* which are described +in :ref:`example-fitting-model-sets`, which are a way of fitting a linear model +with a vector of parameters to a data array, as in that specific case the +fitting can be truly vectorized, and will likely not benefit from the approach +described here. + +Getting started +=============== + +To demonstrate the use of this function, we will work through a simple +example of fitting a 1D model to a small spectral cube (if you are +interested in accessing the file, you can find it at +:download:`l1448_13co.fits `, +but the code below will automatically download it). + +.. The following block is to make sure 'data' and 'wcs' are defined if we are not running with --remote-data + +.. plot:: + :context: close-figs + :nofigs: + + >>> import numpy as np + >>> from astropy.wcs import WCS + >>> wcs = WCS(naxis=3) + >>> wcs.wcs.ctype = ['RA---SFL', 'DEC--SFL', 'VOPT'] + >>> wcs.wcs.crval = [57.66, 0., -9959.44378305] + >>> wcs.wcs.crpix = [-799.0, -4741.913, -187.0] + >>> wcs.wcs.cdelt = [-0.006388889, 0.006388889, 66.42361] + >>> wcs.wcs.cunit = ['deg', 'deg', 'm s-1'] + >>> wcs._naxis = [105, 105, 53] + >>> wcs.wcs.set() + >>> data = np.broadcast_to(np.exp(-(np.arange(53) - 25)**2 / 6 ** 2).reshape((53, 1, 1)), (53, 105, 105)) + +We start by downloading the cube and extracting the data and WCS: + +.. plot:: + :context: close-figs + :include-source: + :nofigs: + + >>> from astropy.wcs import WCS + >>> from astropy.io import fits + >>> from astropy.utils.data import get_pkg_data_filename + + >>> filename = get_pkg_data_filename('l1448/l1448_13co.fits') # doctest: +REMOTE_DATA + >>> with fits.open(filename) as hdulist: + ... data = hdulist[0].data + ... wcs = WCS(hdulist[0].header) # doctest: +REMOTE_DATA + +We extract a sub-cube spatially for the purpose of demonstration: + +.. plot:: + :context: close-figs + :include-source: + :nofigs: + + >>> data = data[:, 25:75, 35:85] + >>> wcs = wcs[:, 25:75, 35:85] + +This is a cube of a star-formation region traced by the 13CO line. We can look +at one of the channels: + +.. plot:: + :context: close-figs + :include-source: + :align: center + + >>> import matplotlib.pyplot as plt + >>> fig, ax = plt.subplots(subplot_kw=dict(projection=wcs, slices=('x', 'y', 20))) + >>> ax.imshow(data[20, :, :]) # doctest: +IGNORE_OUTPUT + +We can also extract a spectrum for one of the celestial positions: + +.. plot:: + :context: close-figs + :include-source: + :align: center + + >>> fig, ax = plt.subplots(subplot_kw=dict(projection=wcs, slices=(5, 5, 'x'))) + >>> ax.plot(data[:, 5, 5]) # doctest: +IGNORE_OUTPUT + +We now set up a model to fit this; we will use a simple Gaussian model, +with some reasonable initial guesses for the parameters: + +.. plot:: + :context: close-figs + :include-source: + :nofigs: + + >>> from astropy import units as u + >>> from astropy.modeling.models import Gaussian1D + >>> model = Gaussian1D(amplitude=1 * u.one, mean=4000 * u.m / u.s, stddev=500 * u.m / u.s) + +The data does not have any units in this case, so we use ``u.one`` as +the unit, which indicates it is dimensionless. + +Before fitting this to all spectra in the cube, it’s a good idea to test +the model with at least one of the spectra manually. To do this, we need to extract the x-axis of the spectra: + +.. plot:: + :context: close-figs + :include-source: + :nofigs: + + >>> import numpy as np + >>> x = wcs.pixel_to_world(0, 0, np.arange(data.shape[0]))[1] + >>> x + ) + [2528.19489695, 2594.61850695, 2661.04211695, 2727.46572695, + 2793.88933695, 2860.31294695, 2926.73655695, 2993.16016695, + ... + 5716.52817695, 5782.95178695, 5849.37539695, 5915.79900695, + 5982.22261695] m / s> + +We can now carry out the fit: + +.. plot:: + :context: close-figs + :include-source: + :nofigs: + + >>> from astropy.modeling.fitting import TRFLSQFitter + >>> fitter = TRFLSQFitter() + >>> model_fit_single = fitter(model, x, data[:, 5, 5]) + +.. plot:: + :context: close-figs + :include-source: + :align: center + + >>> fig, ax = plt.subplots() + >>> ax.plot(x, data[:, 5, 5], '.', label='data') # doctest: +IGNORE_OUTPUT + >>> ax.plot(x, model(x), label='initial model') # doctest: +IGNORE_OUTPUT + >>> ax.plot(x, model_fit_single(x), label='fitted model') # doctest: +IGNORE_OUTPUT + >>> ax.legend() # doctest: +IGNORE_OUTPUT + +The model seems to work! We can now use the +:func:`~astropy.modeling.fitting.parallel_fit_dask` function +to fit all spectra in the cube: + +.. plot:: + :context: close-figs + :include-source: + :nofigs: + + >>> from astropy.modeling.fitting import parallel_fit_dask + >>> model_fit = parallel_fit_dask(model=model, + ... fitter=fitter, + ... data=data, + ... world=wcs, + ... fitting_axes=0, + ... data_unit=u.one, + ... scheduler='synchronous') + +The arguments in this case are as follows: + +* ``model=`` is the initial model. While in our case the initial + parameters were specified as scalars, it is possible to pass in a + model that has array parameters if you want to have different initial + parameters as a function of location in the dataset. +* ``fitter=`` is the fitter instance. +* ``data=`` is the N-dimensional dataset, in our case the 3D spectral + cube. +* ``world=`` provides information about the world coordinates for the + fit, for example the spectral coordinates for a spectrum. This can be + specified in different ways, but above we have chosen to pass in the + WCS object for the dataset, from which the spectral axis coordinates + will be extracted. +* ``fitting_axes=`` specifies which axis or axes include the data to + fit. In our example, we are fitting the spectra, + which in NumPy notation is the first axis in the cube, so we specify + ``fitting_axes=0``. +* ``data_unit=`` specifies the unit to use for the data. In our case, + the data has no unit, but because we are using units for the spectral + axis, we need to specify ``u.one`` here. + +We can now take a look at the parameter maps: + +.. plot:: + :context: close-figs + :include-source: + :align: center + + >>> fig, axs = plt.subplots(figsize=(10, 5), ncols=3) + >>> ax1 = axs[0] + >>> ax1.set_title('Amplitude') # doctest: +IGNORE_OUTPUT + >>> ax1.imshow(model_fit.amplitude.value, vmin=0, vmax=5, origin='lower') # doctest: +IGNORE_OUTPUT + >>> ax2 = axs[1] + >>> ax2.set_title('Mean') # doctest: +IGNORE_OUTPUT + >>> ax2.imshow(model_fit.mean.value, vmin=2500, vmax=6000, origin='lower') # doctest: +IGNORE_OUTPUT + >>> ax3 = axs[2] + >>> ax3.set_title('Standard deviation') # doctest: +IGNORE_OUTPUT + >>> ax3.imshow(model_fit.stddev.value, vmin=0, vmax=2000, origin='lower') # doctest: +IGNORE_OUTPUT + +There are a number of pixels that appear to have issues. Inspecting the +histogram of means, we can see that a lot of values are not at all in +the spectral range we are fitting: + +.. plot:: + :context: close-figs + :include-source: + :align: center + + >>> fig, ax = plt.subplots() + >>> ax.hist(model_fit.mean.value.ravel(), bins=100) # doctest: +IGNORE_OUTPUT + >>> ax.set(yscale='log', xlabel='mean', ylabel='number') # doctest: +IGNORE_OUTPUT + +We can set the bounds on the mean and try the fit again + +.. plot:: + :context: close-figs + :include-source: + :nofigs: + + >>> model.mean.bounds = (3000, 6000) * u.km / u.s + >>> model_fit = parallel_fit_dask(model=model, + ... fitter=fitter, + ... data=data, + ... world=wcs, + ... fitting_axes=0, + ... data_unit=u.one, + ... scheduler='synchronous') + +and we can visualize the results: + +.. plot:: + :context: close-figs + :include-source: + :align: center + + >>> fig, axs = plt.subplots(figsize=(10, 5), ncols=3) + >>> ax1 = axs[0] + >>> ax1.set_title('Amplitude') # doctest: +IGNORE_OUTPUT + >>> ax1.imshow(model_fit.amplitude.value, vmin=0, vmax=5, origin='lower') # doctest: +IGNORE_OUTPUT + >>> ax2 = axs[1] + >>> ax2.set_title('Mean') # doctest: +IGNORE_OUTPUT + >>> ax2.imshow(model_fit.mean.value, vmin=2500, vmax=6000, origin='lower') # doctest: +IGNORE_OUTPUT + >>> ax3 = axs[2] + >>> ax3.set_title('Standard deviation') # doctest: +IGNORE_OUTPUT + >>> ax3.imshow(model_fit.stddev.value, vmin=0, vmax=2000, origin='lower') # doctest: +IGNORE_OUTPUT + +The amplitude map no longer contains any problematic pixels. + +World input +=========== + +The example above demonstrated that it is possible to pass in a +:class:`astropy.wcs.WCS` object to the ``world=`` argument in order to determine +the world coordinates for the fit (e.g. the spectral axis values for a spectral +fit). It is also possible to pass in a tuple of arrays - if you do this, the +tuple should have one item per fitting axis. It is most efficient to pass in a +tuple of 1D arrays, but if the world coordinates vary over the axes being +iterated over, you can also pass in a tuple of N-d arrays, giving the +coordinates of each individual pixel (it is also possible to pass in arrays that +are not 1D but also not fully N-d as long as they can be broadcasted to the data +shape). + +Multiprocessing +=============== + +By default, :func:`~astropy.modeling.fitting.parallel_fit_dask` will make use +of multi-processing to parallelize the fitting. If you write a script to +carry out the fitting, you will likely need to move your code inside a:: + + if __name__ == "__main__": + + ... + +clause as otherwise Python will execute the whole code in the script many times, +and potentially recursively, rather than just parallelizing the fitting. + +Performance +=========== + +The :func:`~astropy.modeling.fitting.parallel_fit_dask` function splits the data +into chunks, each of which is then sent to a different process. The size of +these chunks is critical to obtaining good performance. If we split the data +into one chunk per fit, the process would be inefficient due to significant +overhead from inter-process communication. Conversely, if we split the data into +fewer chunks than there are available processes, we will not utilize all the +available computational power. If we split the data into slightly more chunks +than there are processes, inefficiencies can arise as well. For example, +splitting the data into five chunks with four available processes means the four +processes will first fit four chunks, and then a single process will be held up +fitting the remaining chunk. Therefore, it is important to carefully consider +how the data is split. + +To control the splitting of the data, use the ``chunk_n_max=`` keyword argument. +This determines how many individual fits will be carried out in each chunk. For +example, when fitting a model to individual spectra in a spectral cube, setting +``chunk_n_max=100`` means each chunk will contain 100 spectra. As a general +guide, you will likely want to set this to be roughly the number of fits to be +carried out in the data divided by several times the number of available +processes. For example, if you need to fit 100,000 spectra and have 8 processes +available, setting ``chunk_n_max=1000`` would be reasonable. This configuration +would break the data into 100 chunks, meaning each process will need to handle +approximately a dozen chunks. Additionally, fitting 1,000 spectra per chunk will +take enough time to avoid being dominated by communication overhead. + +The default value for ``chunk_n_max`` is 500. + +Fit information +=============== + +When carrying out regular (non-parallel) fitting with astropy, fitters will typically +have a ``.fit_info`` attribute which contains information about the fit, such as +the number of function evaluations, parameter covariance matrix, and so on. The +information available depends on the specific fitter used. + +These fit information objects can in some cases take up more memory than the +data that was being fit in the first place, so when carrying out many fits +in parallel with :func:`~astropy.modeling.fitting.parallel_fit_dask`, this +information is not preserved by default and the ``.fit_info`` parameter on +the fitter instance is set to `None` + +However, since access to this information can be useful in some cases, it is +possible to opt-in to keeping it. Either all of the fit information can be +preserved, by setting ``fit_info=True``: + + >>> model_fit = parallel_fit_dask(model=model, + ... ... + ... fitter=fitter, + ... fit_info=True) # doctest: +SKIP + +or just specific keys (which can help reduce memory usage): + + >>> model_fit = parallel_fit_dask(model=model, + ... ... + ... fitter=fitter, + ... fit_info=('nfev', 'message', 'status')) # doctest: +SKIP + + +In these cases, the fitter's ``.fit_info`` will be set to a +:class:`~astropy.modeling.fitting.FitInfoArrayContainer` object, which internally +has a numpy object array containing all the different fit information objects. +The shape of ``.fit_info`` should be the same as the parameter arrays: + + >>> fitter.fit_info.shape # doctest: +SKIP + (50, 50) + >>> fitter.fit_info.ndim # doctest: +SKIP + 2 + +Indexing the fit info will return a specific fit information object, e.g. + + >>> fitter.fit_info[10, 20] # doctest: +SKIP + message: The maximum number of function evaluations is exceeded. + success: False + status: 0 + fun: [-2.169e-01 -2.398e-01 ... -5.502e-02 2.498e-01] + x: [ 5.352e+02 2.034e+04 3.932e+03] + cost: 0.575174901185717 + jac: [[ 3.514e-05 -2.166e-05 9.810e-05] + [ 3.793e-05 -2.329e-05 1.051e-04] + ... + [ 1.200e-03 -5.990e-04 2.197e-03] + [ 1.277e-03 -6.343e-04 2.316e-03]] + grad: [-5.634e-06 2.866e-06 -1.092e-05] + optimality: 1.0921480583423703e-05 + active_mask: [0 0 0] + nfev: 100 + njev: 93 + param_cov: [[ 5.965e+08 2.262e+09 2.913e+08] + [ 2.262e+09 8.584e+09 1.106e+09] + [ 2.913e+08 1.106e+09 1.427e+08]] + +Indexing the fit info in a way that returns a range of fits, e.g. +``fitter.fit_info[10:20, 20:30]``, will return a +:class:`~astropy.modeling.fitting.FitInfoArrayContainer` object. + +It is also possible to retrieve one of these keys for all fits as an array, e.g.: + + >>> nfev = fitter.fit_info.get_property_as_array('nfev') # doctest: +SKIP + >>> nfev.shape # doctest: +SKIP + (50, 50) + >>> nfev[0:3, 0:3] # doctest: +SKIP + array([[ 9, 8, 10], + [10, 13, 9], + [10, 13, 10]]) + >>> param_cov = fitter.fit_info.get_property_as_array('param_cov') # doctest: +SKIP + >>> param_cov.shape # doctest: +SKIP + (50, 50, 3, 3) + +Diagnostics +=========== + +One of the challenges of fitting a model many different times is understanding +what went wrong when issues arise. By default, if a fit fails with a warning or +an exception, the parameters for that fit will be set to NaN, and no warning or +exception will be shown to the user. However, it can be helpful to have more +information, such as the specific error or exception that occurred. + +You can control this by setting the ``diagnostics=`` argument. This allows you +to choose whether to output information about: + +* Failed fits with errors (``diagnostics='error'``), +* Fits with errors or warnings (``diagnostics='error+warn'``), or +* All fits (``diagnostics='all'``). + +If the ``diagnostics`` option is specified, you will also need to specify +``diagnostics_path``, which should be the path to a folder that will contain all +the output. Each fit that needs to be output will be assigned a sub-folder named +after the indices along the axes of the data (excluding the fitting axes). The +output will include (if appropriate): + +* ``error.log``, containing details of any exceptions that occurred +* ``warn.log``, containing any warnings + +You may also want to automatically create a plot of the fit, inspect the data +being fit, or examine the model. To do this, you can pass a function to +``diagnostics_callable``. See :func:`~astropy.modeling.fitting.parallel_fit_dask` +for more information about the arguments this function should accept. + +Schedulers +========== + +By default, :func:`~astropy.modeling.fitting.parallel_fit_dask` will make use of +the ``'processes'`` scheduler, which means that multiple processes on your local +machine can be used. You can override the scheduler being used with the +``scheduler=`` keyword argument. You can either set this to the name of a +scheduler (such as ``'synchronous'``), or you can set it to ``'default'`` in order +to make use of whatever is the currently active dask scheduler, which allows +you for example to set up a `dask.distributed +`_ scheduler. diff --git a/docs/modeling/parameters.rst b/docs/modeling/parameters.rst new file mode 100644 index 000000000000..bb4f1c21bded --- /dev/null +++ b/docs/modeling/parameters.rst @@ -0,0 +1,229 @@ +.. include:: links.inc + +.. _modeling-parameters: + +********** +Parameters +********** + +Basics +====== + +Most models in this package are "parametric" in the sense that each subclass +of `~astropy.modeling.Model` represents an entire family of models, each +member of which is distinguished by a fixed set of parameters that fit that +model to some dependent and independent variable(s) (also referred to +throughout the package as the outputs and inputs of the model). + +Parameters are used in three different contexts within this package: Basic +evaluation of models, fitting models to data, and providing information about +individual models to users (including documentation). + +Most subclasses of `~astropy.modeling.Model`--specifically those implementing a +specific physical or statistical model, have a fixed set of parameters that can +be specified for instances of that model. There are a few classes of models +(in particular polynomials) in which the number of parameters depends on some +other property of the model (the degree in the case of polynomials). + +Models maintain a list of parameter names, +`~astropy.modeling.Model.param_names`. Single parameters are instances of +`~astropy.modeling.Parameter` which provides a proxy for the actual parameter +values. Simple mathematical operations can be performed with them, but they +also contain additional attributes specific to model parameters, such as any +constraints on their values and documentation. + +Parameter values may be scalars *or* array values. Some parameters are +required by their very nature to be arrays (such as the transformation matrix +for an `~astropy.modeling.projections.AffineTransformation2D`). In most other +cases, however, array-valued parameters have no meaning specific to the model, +and are simply combined with input arrays during model evaluation according to +the standard `Numpy broadcasting rules`_. + +Parameter constraints +===================== + +`astropy.modeling` supports several types of parameter constraints. They are implemented +as properties of `~astropy.modeling.Parameter`, the class which defines all fittable +parameters, and can be set on individual parameters or on model instances. + +The `astropy.modeling.Parameter.fixed` constraint is boolean and indicates +whether a parameter is kept "fixed" or "frozen" during fitting. For example, fixing the +``stddev`` of a :class:`~astropy.modeling.functional_models.Gaussian1D` model +means it will be excluded from the list of fitted parameters:: + + >>> from astropy.modeling.models import Gaussian1D + >>> g = Gaussian1D(amplitude=10.2, mean=2.3, stddev=1.2) + >>> g.stddev.fixed + False + >>> g.stddev.fixed = True + >>> g.stddev.fixed + True + +`astropy.modeling.Parameter.bounds` is a tuple of numbers +setting minimum and maximum value for a parameter. ``(None, None)`` indicates +the parameter values are not bound. ``bounds`` can be set also using the +`~astropy.modeling.Parameter.min` and +`~astropy.modeling.Parameter.max` properties. Assigning ``None`` to +the corresponding property removes the bound on the parameter. For example, setting +bounds on the ``mean`` value of a :class:`~astropy.modeling.functional_models.Gaussian1D` +model can be done either by setting ``min`` and ``max``:: + + >>> g.mean.bounds + (None, None) + >>> g.mean.min = 2.2 + >>> g.mean.bounds + (2.2, None) + >>> g.mean.max = 2.4 + >>> g.mean.bounds + (2.2, 2.4) + +or using the ``bounds`` property:: + + >>> g.mean.bounds = (2.2, 2.4) + +`astropy.modeling.Parameter.tied` is a user supplied callable +which takes a model instance and returns a value for the parameter. It is most useful +with setting constraints on compounds models, for example a ratio between two parameters (:ref:`example`). + +Constraints can also be set when the model is initialized. For example:: + + >>> g = Gaussian1D(amplitude=10.2, mean=2.3, stddev=1.2, + ... fixed={'stddev': True}, + ... bounds={'mean': (2.2, 2.4)}) + >>> g.stddev.fixed + True + >>> g.mean.bounds + (2.2, 2.4) + + +Parameter examples +================== + +- Model classes can be introspected directly to find out what parameters they + accept:: + + >>> from astropy.modeling import models + >>> models.Gaussian1D.param_names + ('amplitude', 'mean', 'stddev') + + The order of the items in the ``param_names`` list is relevant--this + is the same order in which values for those parameters should be passed in + when constructing an instance of that model:: + + >>> g = models.Gaussian1D(1.0, 0.0, 0.1) + >>> g # doctest: +FLOAT_CMP + + + However, parameters may also be given as keyword arguments (in any order):: + + >>> g = models.Gaussian1D(mean=0.0, amplitude=2.0, stddev=0.2) + >>> g # doctest: +FLOAT_CMP + + + So all that really matters is knowing the names (and meanings) of the + parameters that each model accepts. More information about an individual + model can also be obtained using the `help` built-in:: + + >>> help(models.Gaussian1D) # doctest: +SKIP + +- Some types of models can have different numbers of parameters depending + on other properties of the model. In particular, the parameters of + polynomial models are their coefficients, the number of which depends on the + polynomial's degree:: + + >>> p1 = models.Polynomial1D(degree=3, c0=1.0, c1=0.0, c2=2.0, c3=3.0) + >>> p1.param_names + ('c0', 'c1', 'c2', 'c3') + >>> p1 # doctest: +FLOAT_CMP + + + For the basic `~astropy.modeling.polynomial.Polynomial1D` class the + parameters are named ``c0`` through ``cN`` where ``N`` is the degree of the + polynomial. The above example represents the polynomial :math:`3x^3 + 2x^2 + + 1`. + +- Some models also have default values for one or more of their parameters. + For polynomial models, for example, the default value of all coefficients is + zero--this allows a polynomial instance to be created without specifying any + of the coefficients initially:: + + >>> p2 = models.Polynomial1D(degree=4) + >>> p2 # doctest: +FLOAT_CMP + + +- Parameters can then be set/updated by accessing attributes on the model of + the same names as the parameters:: + + >>> p2.c4 = 1 + >>> p2.c2 = 3.5 + >>> p2.c0 = 2.0 + >>> p2 # doctest: +FLOAT_CMP + + + This example now represents the polynomial :math:`x^4 + 3.5x^2 + 2`. + +- It is possible to set the coefficients of a polynomial by passing the + parameters in a dictionary, since all parameters can be provided as keyword + arguments:: + + >>> ch2 = models.Chebyshev2D(x_degree=2, y_degree=3) + >>> coeffs = dict((name, [idx, idx + 10]) + ... for idx, name in enumerate(ch2.param_names)) + >>> ch2 = models.Chebyshev2D(x_degree=2, y_degree=3, n_models=2, + ... **coeffs) + >>> ch2.param_sets # doctest: +FLOAT_CMP + array([[ 0., 10.], + [ 1., 11.], + [ 2., 12.], + [ 3., 13.], + [ 4., 14.], + [ 5., 15.], + [ 6., 16.], + [ 7., 17.], + [ 8., 18.], + [ 9., 19.], + [10., 20.], + [11., 21.]]) + +- Or directly, using keyword arguments:: + + >>> ch2 = models.Chebyshev2D(x_degree=2, y_degree=3, + ... c0_0=[0, 10], c0_1=[3, 13], + ... c0_2=[6, 16], c0_3=[9, 19], + ... c1_0=[1, 11], c1_1=[4, 14], + ... c1_2=[7, 17], c1_3=[10, 20,], + ... c2_0=[2, 12], c2_1=[5, 15], + ... c2_2=[8, 18], c2_3=[11, 21]) + +- Individual parameters values may be arrays of different sizes and shapes:: + + >>> p3 = models.Polynomial1D(degree=2, c0=1.0, c1=[2.0, 3.0], + ... c2=[[4.0, 5.0], [6.0, 7.0], [8.0, 9.0]]) + >>> p3(2.0) # doctest: +FLOAT_CMP + array([[21., 27.], + [29., 35.], + [37., 43.]]) + + This is equivalent to evaluating the Numpy expression:: + + >>> import numpy as np + >>> c2 = np.array([[4.0, 5.0], + ... [6.0, 7.0], + ... [8.0, 9.0]]) + >>> c1 = np.array([2.0, 3.0]) + >>> c2 * 2.0**2 + c1 * 2.0 + 1.0 # doctest: +FLOAT_CMP + array([[21., 27.], + [29., 35.], + [37., 43.]]) + + Note that in most cases, when using array-valued parameters, the parameters + must obey the standard broadcasting rules for Numpy arrays with respect to + each other:: + + >>> models.Polynomial1D(degree=2, c0=1.0, c1=[2.0, 3.0], + ... c2=[4.0, 5.0, 6.0]) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + InputParameterError: Parameter u'c1' of shape (2,) cannot be broadcast + with parameter u'c2' of shape (3,). All parameter arrays must have + shapes that are mutually compatible according to the broadcasting rules. diff --git a/docs/modeling/performance.rst b/docs/modeling/performance.rst new file mode 100644 index 000000000000..be84ff7c4857 --- /dev/null +++ b/docs/modeling/performance.rst @@ -0,0 +1,19 @@ + +.. _astropy-modeling-performance: + +Performance Tips +**************** + +Initializing a compound model with many constituent models can be time consuming. +If your code uses the same compound model repeatedly consider initializing it +once and reusing the model. + +Consider the :ref:`performance tips ` that apply to +quantities when initializing and evaluating models with quantities. + +When fitting models with one of the fitter classes, by default a copy of the +model is returned, with parameters set to those determined by the fitting. If +you do not need to preserve the initial model used in the fitting, you can +optionally pass ``inplace=True`` when calling the fitter, and the parameters +will be updated on the model you supply rather than returning a copy of the +model - this can improve performance in some cases. diff --git a/docs/modeling/physical_models.rst b/docs/modeling/physical_models.rst new file mode 100644 index 000000000000..5d666a95fc4f --- /dev/null +++ b/docs/modeling/physical_models.rst @@ -0,0 +1,329 @@ +.. _predef_physicalmodels: + +*************** +Physical Models +*************** + +These are models that are physical motivated, generally as solutions to +physical problems. This is in contrast to those that are mathematically motivated, +generally as solutions to mathematical problems. + +.. _blackbody-planck-law: + +BlackBody +========= + +The :class:`~astropy.modeling.physical_models.BlackBody` model provides a model +for using `Planck's Law `_. +The blackbody function is + +.. math:: + + B_{\nu}(T) = A \frac{2 h \nu^{3} / c^{2}}{exp(h \nu / k T) - 1} + +where :math:`\nu` is the frequency, :math:`T` is the temperature, +:math:`A` is the scaling factor, +:math:`h` is the Plank constant, :math:`c` is the speed of light, and +:math:`k` is the Boltzmann constant. + +The two parameters of the model the scaling factor ``scale`` (A) and +the absolute temperature ``temperature`` (T). If the ``scale`` factor does not +have units, then the result is in units of spectral radiance, specifically +ergs/(cm^2 Hz s sr). If the ``scale`` factor is passed with spectral radiance units, +then the result is in those units (e.g., ergs/(cm^2 A s sr) or MJy/sr). +Setting the ``scale`` factor with units of ergs/(cm^2 A s sr) will give the +Planck function as :math:`B_\lambda`. +The temperature can be passed as a Quantity with any supported temperature unit. + +An example plot for a blackbody with a temperature of 10000 K and a scale of 1 is +shown below. A scale of 1 shows the Planck function with no scaling in the +default units returned by :class:`~astropy.modeling.physical_models.BlackBody`. + +.. plot:: + :include-source: + + import numpy as np + import matplotlib.pyplot as plt + + from astropy.modeling.models import BlackBody + import astropy.units as u + + wavelengths = np.logspace(np.log10(1000), np.log10(3e4), num=1000) * u.AA + + # blackbody parameters + temperature = 10000 * u.K + + # BlackBody provides the results in ergs/(cm^2 Hz s sr) when scale has no units + bb = BlackBody(temperature=temperature, scale=10000.0) + bb_result = bb(wavelengths) + + fig, ax = plt.subplots(layout='tight') + ax.plot(wavelengths, bb_result, '-') + + ax.set( + xscale="log", + xlabel=fr"$\lambda$ [{wavelengths.unit}]", + ylabel=fr"$F(\lambda)$ [{bb_result.unit}]", + ) + + plt.show() + +The :meth:`~astropy.modeling.physical_models.BlackBody.bolometric_flux` member +function gives the bolometric flux using +:math:`\sigma T^4/\pi` where :math:`\sigma` is the Stefan-Boltzmann constant. + +The :meth:`~astropy.modeling.physical_models.BlackBody.lambda_max` and +:meth:`~astropy.modeling.physical_models.BlackBody.nu_max` member functions +give the wavelength and frequency of the maximum for :math:`B_\lambda` +and :math:`B_\nu`, respectively, calculated using `Wien's Law +`_. + +Drude1D +======= + +The :class:`~astropy.modeling.physical_models.Drude1D` model provides a model +for the behavior of an electron in a material +(see `Drude Model `_). +Like the :class:`~astropy.modeling.functional_models.Lorentz1D` model, the Drude model +has broader wings than the :class:`~astropy.modeling.functional_models.Gaussian1D` +model. The Drude profile has been used to model dust features including the +2175 Angstrom extinction feature and the mid-infrared aromatic/PAH features. +The Drude function at :math:`x` is + +.. math:: + + D(x) = A \frac{(f/x_0)^2}{((x/x_0 - x_0/x)^2 + (f/x_0)^2} + +where :math:`A` is the amplitude, :math:`f` is the full width at half maximum, +and :math:`x_0` is the central wavelength. An example of a Drude1D model +with :math:`x_0 = 2175` Angstrom and :math:`f = 400` Angstrom is shown below. + +.. plot:: + :include-source: + + import numpy as np + import matplotlib.pyplot as plt + + from astropy.modeling.models import Drude1D + import astropy.units as u + + wavelengths = np.linspace(1000, 4000, num=1000) * u.AA + + # Parameters and model + mod = Drude1D(amplitude=1.0, x_0=2175. * u.AA, fwhm=400. * u.AA) + mod_result = mod(wavelengths) + + fig, ax = plt.subplots(layout="tight") + ax.plot(wavelengths, mod_result, '-') + + ax.set(xlabel=fr"$\lambda$ [{wavelengths.unit}]", ylabel=r"$D(\lambda)$") + + plt.show() + +.. _NFW: + +NFW +========= + +The :class:`~astropy.modeling.physical_models.NFW` model computes a +1-dimensional Navarro–Frenk–White profile. The dark matter density in an +NFW profile is given by: + + +.. math:: + + \rho(r)=\frac{\delta_c\rho_{c}}{r/r_s(1+r/r_s)^2} + +where :math:`\rho_{c}` is the critical density of the Universe at the redshift +of the profile, :math:`\delta_c` is the over density, and :math:`r_s` is the +scale radius of the profile. + + +This model relies on three parameters: + + ``mass`` : the mass of the profile (in solar masses if no units are provided) + + ``concentration`` : the profile concentration + + ``redshift`` : the redshift of the profile + +As well as two optional initialization variables: + + ``massfactor`` : tuple or string specifying the overdensity type and factor (default ("critical", 200)) + + ``cosmo`` : the cosmology for density calculation (default default_cosmology) + +.. note:: + Initialization of NFW profile object required before evaluation (in order to set mass + overdensity and cosmology). + + +Sample plots of an NFW profile with the following parameters are displayed below: + ``mass`` = :math:`2.0 x 10^{15} M_{sun}` + + ``concentration`` = 8.5 + + ``redshift`` = 0.63 + +The first plot is of the NFW profile density as a function of radius. +The second plot displays the profile density and radius normalized by the NFW scale +density and scale radius, respectively. The scale density and scale radius are available +as attributes ``rho_s`` and ``r_s``, and the overdensity radius can be accessed via ``r_virial``. + +.. plot:: + :include-source: + + import numpy as np + import matplotlib.pyplot as plt + from astropy.modeling.models import NFW + import astropy.units as u + from astropy import cosmology + + # NFW Parameters + mass = u.Quantity(2.0E15, u.M_sun) + concentration = 8.5 + redshift = 0.63 + cosmo = cosmology.Planck15 + massfactor = ("critical", 200) + + # Create NFW Object + n = NFW(mass=mass, concentration=concentration, redshift=redshift, cosmo=cosmo, + massfactor=massfactor) + + # Radial distribution for plotting + radii = range(1,2001,10) * u.kpc + + # Radial NFW density distribution + n_result = n(radii) + + # Plot creation + fig, axs = plt.subplots(nrows=2) + fig.suptitle('1 Dimensional NFW Profile') + + # Density profile subplot + axs[0].plot(radii, n_result, '-') + axs[0].set( + yscale='log', + xlabel=fr"$r$ [{radii.unit}]", + ylabel=fr"$\rho$ [{n_result.unit}]", + ) + + # Create scaled density / scaled radius subplot + # NFW Object + n = NFW(mass=mass, concentration=concentration, redshift=redshift, cosmo=cosmo, + massfactor=massfactor) + + # Radial distribution for plotting + radii = np.logspace(np.log10(1e-5), np.log10(2), num=1000) * u.Mpc + n_result = n(radii) + + # Scaled density / scaled radius subplot + axs[1].plot(radii / n.radius_s, n_result / n.density_s, '-') + axs[1].set( + xscale='log', + yscale='log', + xlabel=r"$r / r_s$", + ylabel=r"$\rho / \rho_s$", + ) + + # Display plot + fig.tight_layout(rect=[0, 0.03, 1, 0.95]) + plt.show() + + + +The :meth:`~astropy.modeling.physical_models.NFW.circular_velocity` member provides the circular +velocity at each position ``r`` via the equation: + + +.. math:: + + v_{circ}(r)^2=\frac{1}{x}\frac{\ln(1+cx)-(cx)/(1+cx)}{\ln(1+c)-c/(1+c)} + +where x is the ratio ``r``:math:`/r_{vir}`. Circular velocities are provided in km/s. + +A sample plot of circular velocities of an NFW profile with the following parameters is displayed +below: + + ``mass`` = :math:`2.0 x 10^{15} M_{sun}` + + ``concentration`` = 8.5 + + ``redshift`` = 0.63 + +The maximum circular velocity and radius of maximum circular velocity are available as attributes +``v_max`` and ``r_max``. + + +.. plot:: + :include-source: + + import matplotlib.pyplot as plt + from astropy.modeling.models import NFW + import astropy.units as u + from astropy import cosmology + + # NFW Parameters + mass = u.Quantity(2.0E15, u.M_sun) + concentration = 8.5 + redshift = 0.63 + cosmo = cosmology.Planck15 + massfactor = ("critical", 200) + + # Create NFW Object + n = NFW(mass=mass, concentration=concentration, redshift=redshift, cosmo=cosmo, + massfactor=massfactor) + + # Radial distribution for plotting + radii = range(1,200001,10) * u.kpc + + # NFW circular velocity distribution + n_result = n.circular_velocity(radii) + + # Plot creation + fig,ax = plt.subplots() + ax.set_title('NFW Profile Circular Velocity') + ax.plot(radii, n_result, '-') + ax.set_xscale('log') + ax.set_xlabel(fr"$r$ [{radii.unit}]") + ax.set_ylabel(r"$v_{circ}$" + f" [{n_result.unit}]") + + # Display plot + plt.tight_layout(rect=[0, 0.03, 1, 0.95]) + plt.show() + + +.. _Cosmologies: + +Cosmologies +=========== + +The instances of the |Cosmology| class (and subclasses) include +|Cosmology.to_format|, a method to convert a Cosmology to another python +object. Specifically, any redshift method can be converted to a +:class:`~astropy.modeling.FittableModel` instance using the argument +``format="astropy.model"``. +During the conversion, each |Cosmology| :class:`~astropy.cosmology.Parameter` +is converted to a :class:`astropy.modeling.Model` +:class:`~astropy.modeling.Parameter`, while the redshift-method becomes the +model's ``__call__`` / ``evaluate`` method. +This means cosmologies can now be fit with data! + +.. code-block:: + + >>> from astropy.cosmology import Planck18 + >>> model = Planck18.to_format(format="astropy.model", method="lookback_time") + >>> model + + +When finished, e.g. fitting, a model can be turned back into a |Cosmology| +using |Cosmology.from_format|. + +.. code-block:: + + >>> from astropy.cosmology import Cosmology + >>> cosmo = Cosmology.from_format(model, format="astropy.model") + >>> cosmo == Planck18 + True diff --git a/docs/modeling/polynomial_models.rst b/docs/modeling/polynomial_models.rst new file mode 100644 index 000000000000..4cf7c62394bf --- /dev/null +++ b/docs/modeling/polynomial_models.rst @@ -0,0 +1,79 @@ +.. include:: links.inc + +.. _polynomial_models: + +***************** +Polynomial Models +***************** + +.. _domain-window-note: + +Notes regarding usage of domain and window +------------------------------------------ + +Most of the polynomial models have optional domain and window attributes. +It is important to understand how they currently are interpreted, which +can be confusing since the terminology often implies something different. + +Both the domain and window attributes for a polynomial consist of a two +element list (this will change to tuples in a future release) that +indicate a range of values for input values. For 2-Dimensional polynomials +the attributes become x_domain, y_domain, x_window, and y_window. +Generally speaking, the main purpose of these attributes is to define +a linear transform between the supplied input variable and the resultant +input variable that is supplied to the polynomial. For example, if +domain = [-2, 2] and window = [-1, 1], input values will be divided by +two so that the domain maps to the window. Correspondingly the pair +domain = [0, 2], window = [-1, 1] implies that 1 will be subtracted from +the input variable before using it in the polynomial. + +Neither domain or window are meant to imply that values that fall outside +of their corresponding ranges will result in an exception, or that +such values are necessarily invalid (the latter depends on the context +of how the polynomial is being used). + +It is the case that the orthogonal polynomials are defined on a range of +[-1, 1], but nothing in the current machinery prevents them from being +evaluated outside that range. + +Domain is used in fitting polynomials to bound the input variable to map +to the defined window so that they fall within the expected [-1, 1] range +for such polynomials. That is, the fitting routine will set the domain to +map to the window range for the range of input x values supplied (so that +domain may change if the minimum and maximum x values being fit change). + +The meaning of these terms may conflict with expectations (e.g., domain +is often meant to mean the range of input values the function is valid +for). For fit results that is somewhat true, but otherwise, it is not. +The default values for ordinary polynomials is [-1, 1] for both domain +and window, which effectively signals no transformation of the input +variable. + +The terminology was adopted from numpy polynomials, which have the same +confusion in meaning. + + +1D Polynomials +-------------- + +- :class:`~astropy.modeling.polynomial.Polynomial1D` + +- :class:`~astropy.modeling.polynomial.Chebyshev1D` + +- :class:`~astropy.modeling.polynomial.Legendre1D` + +- :class:`~astropy.modeling.polynomial.Hermite1D` + +2D Polynomials +-------------- + +- :class:`~astropy.modeling.polynomial.Polynomial2D` + +- :class:`~astropy.modeling.polynomial.Chebyshev2D` + +- :class:`~astropy.modeling.polynomial.Legendre2D` + +- :class:`~astropy.modeling.polynomial.Hermite2D` + +- :class:`~astropy.modeling.polynomial.SIP` model implements the + Simple Imaging Polynomial (`SIP`_) convention diff --git a/docs/modeling/powerlaw_models.rst b/docs/modeling/powerlaw_models.rst new file mode 100644 index 000000000000..25534f174d78 --- /dev/null +++ b/docs/modeling/powerlaw_models.rst @@ -0,0 +1,17 @@ +.. _powerlaw_models: + +*************** +Powerlaw Models +*************** + +- :class:`~astropy.modeling.powerlaws.PowerLaw1D` + +- :class:`~astropy.modeling.powerlaws.BrokenPowerLaw1D` + +- :class:`~astropy.modeling.powerlaws.SmoothlyBrokenPowerLaw1D` + +- :class:`~astropy.modeling.powerlaws.ExponentialCutoffPowerLaw1D` + +- :class:`~astropy.modeling.powerlaws.LogParabola1D` + +- :class:`~astropy.modeling.powerlaws.Schechter1D` diff --git a/docs/modeling/predef_models1D.rst b/docs/modeling/predef_models1D.rst new file mode 100644 index 000000000000..eaa47a671f7c --- /dev/null +++ b/docs/modeling/predef_models1D.rst @@ -0,0 +1,161 @@ +.. _predef_models1D: + +********* +1D Models +********* + +Operations +========== + +These models perform simple mathematical operations. + +- :class:`~astropy.modeling.functional_models.Const1D` model returns the + constant replicated by the number of input x values. + +- :class:`~astropy.modeling.functional_models.Multiply` model multiples the + input x values by a factor and propagates units if the factor is + a :class:`~astropy.units.Quantity`. + +- :class:`~astropy.modeling.functional_models.RedshiftScaleFactor` model + multiples the input x values by a (1 + z) factor. + +- :class:`~astropy.modeling.functional_models.Scale` model multiples by a + factor without changing the units of the result. + +- :class:`~astropy.modeling.functional_models.Shift` model adds a constant + to the input x values. + +Shapes +====== + +These models provide shapes, often used to model general x, y data. + +- :class:`~astropy.modeling.functional_models.Linear1D` model provides a + line parameterizied by the slope and y-intercept + +- :class:`~astropy.modeling.functional_models.Sine1D` model provides a sine + parameterized by an amplitude, frequency, and phase shift. + +- :class:`~astropy.modeling.functional_models.Cosine1D` model provides a + cosine parameterized by an amplitude, frequency, and phase shift. + +.. plot:: + + import numpy as np + import matplotlib.pyplot as plt + + from astropy.modeling.models import (Linear1D, Sine1D, Cosine1D) + + x = np.linspace(-4.0, 6.0, num=100) + + fig, sax = plt.subplots(ncols=3, figsize=(10, 5), layout="tight") + ax = sax.flatten() + + linemod = Linear1D(slope=2., intercept=1.) + ax[0].plot(x, linemod(x), label="Linear1D") + + sinemod = Sine1D(amplitude=10., frequency=0.5, phase=0.) + ax[1].plot(x, sinemod(x), label="Sine1D") + ax[1].set_ylim(-11.0, 13.0) + + cosinemod = Cosine1D(amplitude=10., frequency=0.5, phase=0) + ax[2].plot(x, cosinemod(x), label="Cosine1D") + ax[2].set_ylim(-11.0, 13.0) + + for k in range(3): + ax[k].set_xlabel("x") + ax[k].set_ylabel("y") + ax[k].legend() + + plt.show() + +Profiles +======== + +These models provide profiles, often used for lines in spectra. + +- :class:`~astropy.modeling.functional_models.Box1D` model computes a box + function with an amplitude centered at x_0 with the specified width. + +- :class:`~astropy.modeling.functional_models.Gaussian1D` model computes + a Gaussian with an amplitude centered at x_0 with the specified width. + +- :class:`~astropy.modeling.functional_models.KingProjectedAnalytic1D` model + computes the analytic form of the a King model with an amplitude and + core and tidal radii. + +- :class:`~astropy.modeling.functional_models.Lorentz1D` model computes + a Lorentzian with an amplitude centered at x_0 with the specified width. + +- :class:`~astropy.modeling.functional_models.RickerWavelet1D` model computes + a RickerWavelet function with an amplitude centered at x_0 with the specified width. + +- :class:`~astropy.modeling.functional_models.Moffat1D` model computes a + Moffat function with an amplitude centered at x_0 with the specified width. + +- :class:`~astropy.modeling.functional_models.Sersic1D` model + computes a Sersic model with an amplitude with an effective radius and + the specified sersic index. + +- :class:`~astropy.modeling.functional_models.Trapezoid1D` model computes a + box with sloping sides with an amplitude centered at x_0 with the specified + width and sides with the specified slope. + +- :class:`~astropy.modeling.functional_models.Voigt1D` model computes a + Voigt function with an amplitude centered at x_0 with the specified + Lorentzian and Gaussian widths. + +.. plot:: + + import numpy as np + import matplotlib.pyplot as plt + + from astropy.modeling.models import ( + Box1D, + Gaussian1D, + RickerWavelet1D, + Moffat1D, + Lorentz1D, + Sersic1D, + Trapezoid1D, + KingProjectedAnalytic1D, + Voigt1D, + ) + + x = np.linspace(-4.0, 6.0, num=100) + r = np.logspace(-1.0, 2.0, num=100) + + fig, sax = plt.subplots(nrows=3, ncols=3, figsize=(10, 10), layout="tight") + ax = sax.flatten() + + mods = [ + Box1D(amplitude=10.0, x_0=1.0, width=1.0), + Gaussian1D(amplitude=10.0, mean=1.0, stddev=1.0), + KingProjectedAnalytic1D(amplitude=10.0, r_core=1.0, r_tide=10.0), + Lorentz1D(amplitude=10.0, x_0=1.0, fwhm=1.0), + RickerWavelet1D(amplitude=10.0, x_0=1.0, sigma=1.0), + Moffat1D(amplitude=10.0, x_0=1.0, gamma=1.0, alpha=1.), + Sersic1D(amplitude=10.0, r_eff=1.0 / 2.0, n=5), + Trapezoid1D(amplitude=10.0, x_0=1.0, width=1.0, slope=5.0), + Voigt1D(amplitude_L=10.0, x_0=1.0, fwhm_L=1.0, fwhm_G=1.0), + ] + + for k, mod in enumerate(mods): + cname = mod.__class__.__name__ + ax[k].set_title(cname) + if cname in ["KingProjectedAnalytic1D", "Sersic1D"]: + ax[k].plot(r, mod(r)) + ax[k].set_xscale("log") + ax[k].set_yscale("log") + else: + ax[k].plot(x, mod(x)) + + for k in range(len(mods)): + ax[k].set_xlabel("x") + ax[k].set_ylabel("y") + + # remove axis for any plots not used + for k in range(len(mods), len(ax)): + ax[k].axis("off") + + plt.show() diff --git a/docs/modeling/predef_models2D.rst b/docs/modeling/predef_models2D.rst new file mode 100644 index 000000000000..49801982d013 --- /dev/null +++ b/docs/modeling/predef_models2D.rst @@ -0,0 +1,137 @@ +.. _predef_models2D: + +********* +2D Models +********* + +These models take as input x and y arrays. + +Operations +========== + +These models perform simple mathematical operations. + +- :class:`~astropy.modeling.functional_models.Const2D` model returns the + constant replicated by the number of input x and y values. + +Shapes +====== + +These models provide shapes, often used to model general x, y, z data. + +- :class:`~astropy.modeling.functional_models.Planar2D` model computes + a tilted plan with specified x,y slopes and z intercept + +Profiles +======== + +These models provide profiles, often used sources in images. +All models have parameters giving the x,y location of the center and +an amplitude. + +- :class:`~astropy.modeling.functional_models.AiryDisk2D` model computes + the Airy function for a radius + +- :class:`~astropy.modeling.functional_models.Box2D` model computes a box + with x,y dimensions + +- :class:`~astropy.modeling.functional_models.Disk2D` model computes a + disk a radius + +- :class:`~astropy.modeling.functional_models.Ellipse2D` model computes + an ellipse with major and minor axis and rotation angle + +- :class:`~astropy.modeling.functional_models.Gaussian2D` model computes + a Gaussian with x,y standard deviations and rotation angle + +- :class:`~astropy.modeling.functional_models.Moffat2D` model computes + a Moffat with x,y dimensions and alpha (power index) and gamma (core width) + +- :class:`~astropy.modeling.functional_models.Lorentz2D` model computes + a Lorentz profile with x,y dimensions and full width at half maximum + +- :class:`~astropy.modeling.functional_models.RickerWavelet2D` model computes + a symmetric RickerWavelet function with the specified sigma + +- :class:`~astropy.modeling.functional_models.Sersic2D` model computes + a Sersic profile with an effective half-light radius, rotation, and + Sersic index + +- :class:`~astropy.modeling.functional_models.GeneralSersic2D` model + computes a generalized Sersic profile with an effective half-light + radius, rotation, Sersic index, and a parameter to control the shape of + the isophotes (e.g., boxy or disky) + +- :class:`~astropy.modeling.functional_models.TrapezoidDisk2D` model + computes a disk with a radius and slope + +- :class:`~astropy.modeling.functional_models.Ring2D` model computes + a ring with inner and outer radii + +.. plot:: + + import numpy as np + import math + import matplotlib.pyplot as plt + from matplotlib.colors import LogNorm + + from astropy.modeling.models import (AiryDisk2D, Box2D, Disk2D, Ellipse2D, + Gaussian2D, Moffat2D, Lorentz2D, + RickerWavelet2D, Sersic2D, + GeneralSersic2D, + TrapezoidDisk2D, Ring2D) + + x = np.linspace(-4.0, 6.0, num=100) + r = np.logspace(-1.0, 2.0, num=100) + + fig, sax = plt.subplots(nrows=5, ncols=3, figsize=(9, 12), layout="tight") + ax = sax.flatten() + + # setup the x,y coordinates + x_npts = 100 + y_npts = x_npts + x0, x1 = -4, 6 + y0, y1 = -3, 7 + x = np.linspace(x0, x1, num=x_npts) + y = np.linspace(y0, y1, num=y_npts) + X, Y = np.meshgrid(x, y) + + # plot the different 2D profiles + mods = [AiryDisk2D(amplitude=10.0, x_0=1.0, y_0=2.0, radius=1.0), + Box2D(amplitude=10.0, x_0=1.0, y_0=2.0, x_width=1.0, y_width=2.0), + Disk2D(amplitude=10.0, x_0=1.0, y_0=2.0, R_0=1.0), + Ellipse2D(amplitude=10.0, x_0=1.0, y_0=2.0, a=1.0, b=2.0, theta=math.pi/4.), + Gaussian2D(amplitude=10.0, x_mean=1.0, y_mean=2.0, x_stddev=1.0, y_stddev=2.0, theta=math.pi/4.), + Moffat2D(amplitude=10.0, x_0=1.0, y_0=2.0, alpha=3, gamma=4), + Lorentz2D(amplitude=10.0, x_0=1.0, y_0=2.0, fwhm=3), + RickerWavelet2D(amplitude=10.0, x_0=1.0, y_0=2.0, sigma=1.0), + Sersic2D(amplitude=10.0, x_0=1.0, y_0=2.0, r_eff=1.0, ellip=0.5, theta=math.pi/4.), + GeneralSersic2D(amplitude=10.0, x_0=1.0, y_0=2.0, r_eff=1.0, ellip=0.5, theta=math.pi/4., c=-1), + GeneralSersic2D(amplitude=10.0, x_0=1.0, y_0=2.0, r_eff=1.0, ellip=0.5, theta=math.pi/4., c=1), + TrapezoidDisk2D(amplitude=10.0, x_0=1.0, y_0=2.0, R_0=1.0, slope=5.0), + Ring2D(amplitude=10.0, x_0=1.0, y_0=2.0, r_in=1.0, r_out=2.0)] + + for k, mod in enumerate(mods): + cname = mod.__class__.__name__ + if cname == "AiryDisk2D": + normfunc = LogNorm(vmin=0.001, vmax=10.) + elif cname in ["Gaussian2D", "Sersic2D", "GeneralSersic2D"]: + normfunc = LogNorm(vmin=0.1, vmax=10.) + else: + normfunc = None + if cname == "GeneralSersic2D": + cname = f'{cname}, c={mod.c.value:.1f}' + ax[k].set_title(cname) + + ax[k].imshow(mod(X, Y), extent=[x0, x1, y0, y1], origin="lower", cmap="gray_r", + norm=normfunc) + + for k in range(len(mods)): + ax[k].set_xlabel("x") + ax[k].set_ylabel("y") + + # remove axis for any plots not used + for k in range(len(mods), len(ax)): + ax[k].axis("off") + + plt.show() diff --git a/docs/modeling/reference_api.rst b/docs/modeling/reference_api.rst new file mode 100644 index 000000000000..9212f389d9e7 --- /dev/null +++ b/docs/modeling/reference_api.rst @@ -0,0 +1,30 @@ +Reference/API +************* + +Capabilities +============ + +.. automodapi:: astropy.modeling +.. automodapi:: astropy.modeling.bounding_box +.. automodapi:: astropy.modeling.mappings +.. automodapi:: astropy.modeling.fitting + :inherited-members: True + :skip: SplineExactKnotsFitter + :skip: SplineInterpolateFitter + :skip: SplineSmoothingFitter + :skip: SplineSplrepFitter +.. automodapi:: astropy.modeling.optimizers +.. automodapi:: astropy.modeling.statistic +.. automodapi:: astropy.modeling.separable + +Pre-Defined Models +================== + +.. automodapi:: astropy.modeling.functional_models +.. automodapi:: astropy.modeling.physical_models +.. automodapi:: astropy.modeling.powerlaws +.. automodapi:: astropy.modeling.polynomial +.. automodapi:: astropy.modeling.projections +.. automodapi:: astropy.modeling.rotations +.. automodapi:: astropy.modeling.spline +.. automodapi:: astropy.modeling.tabular diff --git a/docs/modeling/spline_models.rst b/docs/modeling/spline_models.rst new file mode 100644 index 000000000000..b9d9783aa644 --- /dev/null +++ b/docs/modeling/spline_models.rst @@ -0,0 +1,70 @@ +.. include:: links.inc + +.. _spline_models: + +**************** +1D Spline Models +**************** + +`~astropy.modeling.spline.Spline1D` models are models which can be used +to fit a piecewise polynomial to a set of data. This means that splines +are closely tied to the method used to fit the spline to the data. Currently, +we provide three methods for fitting splines to data: + +- :class:`~astropy.modeling.spline.SplineInterpolateFitter`, which + fits an interpolating spline to the data. This means that the spline + will exactly fit all data points. + +- :class:`~astropy.modeling.spline.SplineSmoothingFitter`, which fits + a smoothing spline to the data. This means that the number of knots + is chosen to satisfy the "smoothing condition": + + .. math:: \sum_{i} \left(w_i * (y_i - spl(x_i))\right)^{2} \leq s + +- :class:`~astropy.modeling.spline.SplineExactKnotsFitter`, which fits + a spline to the data using an exact set of knots. This means that the + spline will use least-squares regression using the user supplied (interior) + knots to find the best fit spline to the data. + +.. plot:: + :include-source: + + import numpy as np + import matplotlib.pyplot as plt + from astropy.modeling.models import Spline1D + from astropy.modeling.fitting import (SplineInterpolateFitter, + SplineSmoothingFitter, + SplineExactKnotsFitter) + + rng = np.random.default_rng() + x = np.linspace(-3, 3, 50) + y = np.exp(-x**2) + 0.1 * rng.standard_normal(50) + xs = np.linspace(-3, 3, 1000) + t = [-1, 0, 1] + spl = Spline1D() + + fitter = SplineInterpolateFitter() + spl1 = fitter(spl, x, y) + + fitter = SplineSmoothingFitter() + spl2 = fitter(spl, x, y, s=0.5) + + fitter = SplineExactKnotsFitter() + spl3 = fitter(spl, x, y, t=t) + + fig, ax = plt.subplots() + ax.plot(x, y, 'ro', label="Data") + ax.plot(xs, spl1(xs), 'b-', label="Interpolating") + ax.plot(xs, spl2(xs), 'g-', label="Smoothing") + ax.plot(xs, spl3(xs), 'k-', label="Exact Knots") + ax.legend() + plt.show() + +Note that by default, splines have `degree ` 3. +In the case of these splines, the ``degree - 1`` is the number of derivatives that +are matched by the spline across knot points. So for degree 3 splines, the value, +first, and second derivatives of the spline will match across each knot point. + +.. warning:: + + Splines only support integer degrees, such that ``1 <= degree <= 5``. diff --git a/docs/modeling/units.rst b/docs/modeling/units.rst new file mode 100644 index 000000000000..1f0f9e6850f1 --- /dev/null +++ b/docs/modeling/units.rst @@ -0,0 +1,227 @@ +.. _modeling-units: + +******************************** +Support for units and quantities +******************************** + + +.. note:: The functionality presented here was recently added. If you run into + any issues, please don't hesitate to open an issue in the `issue + tracker `_. + +The `astropy.modeling` package includes partial support for the use of units and +quantities in model parameters, models, and during fitting. At this time, only +some of the built-in models (such as +:class:`~astropy.modeling.functional_models.Gaussian1D`) support units, but this +will be extended in future to all models where this is appropriate. + +Setting parameters to quantities +================================ + +Models can take :class:`~astropy.units.Quantity` objects as parameters:: + + >>> from astropy import units as u + >>> from astropy.modeling.models import Gaussian1D + >>> g1 = Gaussian1D(mean=3 * u.m, stddev=2 * u.cm, amplitude=3 * u.Jy) + +Accessing the parameter then returns a Parameter object that contains the value +and the unit:: + + >>> g1.mean + Parameter('mean', value=3.0, unit=m) + +It is then possible to access the individual properties of the parameter:: + + >>> g1.mean.name + 'mean' + >>> g1.mean.value + np.float64(3.0) + >>> g1.mean.unit + Unit("m") + +If a parameter has been initialized as a Quantity, it should always be set to a +quantity, but the units don't have to be compatible with the initial ones:: + + >>> g1.mean = 3 * u.s + >>> g1 # doctest: +FLOAT_CMP + + +To change the value of a parameter and not the unit, simply set the value +property:: + + >>> g1.mean.value = 2 + >>> g1 # doctest: +FLOAT_CMP + + +Setting a parameter which was originally set to a quantity to a scalar doesn't +work because it's ambiguous whether the user means to change just the value and +preserve the unit, or get rid of the unit:: + + >>> g1.mean = 2 # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + UnitsError : The 'mean' parameter should be given as a Quantity because it + was originally initialized as a Quantity + +On the other hand, if a parameter previously defined without units is given a +Quantity with a unit, this works because it is unambiguous:: + + >>> g2 = Gaussian1D(mean=3) + >>> g2.mean = 3 * u.m + +In other words, once units are attached to a parameter, they can't be removed +due to ambiguous meaning. + +Evaluating models with quantities +================================= + +Quantities can be passed to model during evaluation:: + + >>> g3 = Gaussian1D(mean=3 * u.m, stddev=5 * u.cm) + >>> g3(2.9 * u.m) # doctest: +FLOAT_CMP + + >>> g3(2.9 * u.s) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + UnitsError : Units of input 'x', s (time), could not be converted to + required input units of m (length) + +In this case, since the mean and standard deviation have units, the value passed +during evaluation also needs units:: + + >>> g3(3) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + UnitsError : Units of input 'x', (dimensionless), could not be converted to + required input units of m (length) + +Equivalencies +------------- + +Equivalencies require special care - a Gaussian defined in frequency space is +not a Gaussian in wavelength space for example. For this reason, we don't allow +equivalencies to be attached to the parameters themselves. Instead, we take the +approach of converting the input data to the parameter space, and any +equivalencies should be applied at evaluation time to the data (not the +parameters). + +Let's consider a model that is Gaussian in wavelength space:: + + >>> g4 = Gaussian1D(mean=3 * u.micron, stddev=1 * u.micron, amplitude=3 * u.Jy) + +By default, passing a frequency will not work: + + >>> g4(1e2 * u.THz) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + UnitsError : Units of input 'x', THz (frequency), could not be converted to + required input units of micron (length) + +But you can pass a dictionary of equivalencies to the equivalencies argument +(this needs to be a dictionary since some models can contain multiple inputs):: + + >>> g4(110 * u.THz, equivalencies={'x': u.spectral()}) # doctest: +FLOAT_CMP + + +The key of the dictionary should be the name of the inputs according to:: + + >>> g4.inputs + ('x',) + +It is also possible to set default equivalencies for the input parameters using +the input_units_equivalencies property:: + + >>> g4.input_units_equivalencies = {'x': u.spectral()} + >>> g4(110 * u.THz) # doctest: +FLOAT_CMP + + +Fitting models with units to data +================================= + +Fitting models with units to data with units should be seamless provided that +the model supports fitting with units. To demonstrate this, we start off by +generating synthetic data: + +.. plot:: + :context: reset + :include-source: + + import numpy as np + from astropy import units as u + import matplotlib.pyplot as plt + + x = np.linspace(1, 5, 30) * u.micron + y = np.exp(-0.5 * (x - 2.5 * u.micron)**2 / (200 * u.nm)**2) * u.mJy + + fig, ax = plt.subplots() + ax.plot(x, y, 'ko') + ax.set(xlabel='Wavelength (microns)', ylabel='Flux density (mJy)') + +and we then define the initial guess for the fitting and we carry out the fit as +we would without any units: + +.. plot:: + :context: + :include-source: + + from astropy.modeling import models, fitting + + g5 = models.Gaussian1D(mean=3 * u.micron, stddev=1 * u.micron, amplitude=1 * u.Jy) + + fitter = fitting.TRFLSQFitter() + + g5_fit = fitter(g5, x, y) + + fig, ax = plt.subplots() + ax.plot(x, y, 'ko') + ax.plot(x, g5_fit(x), 'r-') + ax.set(xlabel='Wavelength (microns)', ylabel='Flux density (mJy)') + +Fitting with equivalencies +-------------------------- + +Let's now consider the case where the data is not equivalent to those of the +parameters, but they are convertible via equivalencies. In this case, the +equivalencies can either be passed via a dictionary as shown higher up for the +evaluation examples: + +.. plot:: + :context: + :include-source: + + g6 = models.Gaussian1D(mean=110 * u.THz, stddev=10 * u.THz, amplitude=1 * u.Jy) + + g6_fit = fitter(g6, x, y, equivalencies={'x': u.spectral()}) + + fig, ax = plt.subplots() + ax.plot(x, g6_fit(x, equivalencies={'x': u.spectral()}), 'b-') + ax.set(xlabel='Wavelength (microns)', ylabel='Flux density (mJy)') + +In this case, the fit (in blue) is slightly worse, because a Gaussian in +frequency space (blue) is not a Gaussian in wavelength space (red). As mentioned +previously, you can also set input_units_equivalencies on the model itself to +avoid having to pass extra arguments to the fitter:: + + g6.input_units_equivalencies = {'x': u.spectral()} + g6_fit = fitter(g6, x, y) + + +.. _units-mapping: + +Support for units in otherwise unitless models +============================================== + +Some models, like polynomials, do not work intrinsically with units. Instead, +the :meth:`~astropy.modeling.core.Model.coerce_units` method provides a way to add input and return units to +unitless models by enclosing the unitless model with two instances of :class:`~astropy.modeling.mappings.UnitsMapping`. +Internally the inputs are stripped of the units before passed +to the model and units are attached to the result if ``return_units`` is specified. +The method returns a new composite model:: + + >>> from astropy.modeling import models + >>> from astropy import units as u + >>> model = models.Polynomial1D(1, c0=1, c1=2) + >>> new_model = model.coerce_units(input_units={'x': u.Hz}, return_units={'y': u.s}, + ... input_units_equivalencies={'x':u.spectral()}) + >>> new_model(10 * u.Hz) + diff --git a/docs/nddata/bitmask.rst b/docs/nddata/bitmask.rst new file mode 100644 index 000000000000..eacf7750e28c --- /dev/null +++ b/docs/nddata/bitmask.rst @@ -0,0 +1,292 @@ +.. _bitmask_details: + +******************************************************** +Utility Functions for Handling Bit Masks and Mask Arrays +******************************************************** + +It is common to use `bit fields `_, +such as integer variables whose individual bits represent some attributes, to +characterize the state of data. For example, Hubble Space Telescope (HST) uses +arrays of bit fields to characterize data quality (DQ) of HST images. See, for +example, DQ field values for `WFPC2 image data (see Table 3.3) `_ and `WFC3 image data (see Table 3.3) `_. +As you can see, the meaning assigned to various *bit flags* for the two +instruments is generally different. + +Bit fields can be thought of as tightly packed collections of bit flags. Using +`masking `_ we can "inspect" +the status of individual bits. + +One common operation performed on bit field arrays is their conversion to +boolean masks, for example, by assigning boolean `True` (in the boolean +mask) to those elements that correspond to non-zero-valued bit fields +(bit fields with at least one bit set to ``1``) or, oftentimes, by assigning +`True` to elements whose corresponding bit fields have only *specific fields* +set (to ``1``). This more sophisticated analysis of bit fields can be +accomplished using *bit masks* and the aforementioned masking operation. + +The `~astropy.nddata.bitmask` module provides two functions that facilitate +conversion of bit field arrays (i.e., DQ arrays) to boolean masks: +`~astropy.nddata.bitmask.bitfield_to_boolean_mask` converts an input bit +field array to a boolean mask using an input bit mask (or list of individual +bit flags) and `~astropy.nddata.bitmask.interpret_bit_flags` creates a bit mask +from an input list of individual bit flags. + +Creating Boolean Masks +********************** + +Overview +======== + +`~astropy.nddata.bitmask.bitfield_to_boolean_mask` by default assumes that +all input bit fields that have at least one bit turned "ON" corresponds to +"bad" data (i.e., pixels) and converts them to boolean `True` in the output +boolean mask (otherwise output boolean mask values are set to `False`). + +Often, for specific algorithms and situations, some bit flags are okay and +can be ignored. `~astropy.nddata.bitmask.bitfield_to_boolean_mask` accepts +lists of bit flags that *by default must be ignored* in the input bit fields +when creating boolean masks. + +Fundamentally, *by default*, `~astropy.nddata.bitmask.bitfield_to_boolean_mask` +performs the following operation: + +.. _main_eq: + +``(1) boolean_mask = (bitfield & ~bit_mask) != 0`` + +(Here ``&`` is bitwise ``and`` while ``~`` is the bitwise ``not`` +operation.) In the previous formula, ``bit_mask`` is a bit mask created from +individual bit flags that need to be ignored in the bit field. + +Example +------- + +.. + EXAMPLE START + Creating Boolean Masks from Bit Field Arrays + +.. _table1: + +.. table:: Table 1: Examples of Boolean Mask Computations \ + (default parameters and 8-bit data type) + + +--------------+--------------+--------------+--------------+------------+ + | Bit Field | Bit Mask | ~(Bit Mask) | Bit Field & |Boolean Mask| + | | | | ~(Bit Mask) | | + +==============+==============+==============+==============+============+ + |11011001 (217)|01010000 (80) |10101111 (175)|10001001 (137)| True | + +--------------+--------------+--------------+--------------+------------+ + |11011001 (217)|10101111 (175)|01010000 (80) |01010000 (80) | True | + +--------------+--------------+--------------+--------------+------------+ + |00001001 (9) |01001001 (73) |10110110 (182)|00000000 (0) | False | + +--------------+--------------+--------------+--------------+------------+ + |00001001 (9) |00000000 (0) |11111111 (255)|00001001 (9) | True | + +--------------+--------------+--------------+--------------+------------+ + |00001001 (9) |11111111 (255)|00000000 (0) |00000000 (0) | False | + +--------------+--------------+--------------+--------------+------------+ + +.. + EXAMPLE END + +Specifying Bit Flags +==================== + +`~astropy.nddata.bitmask.bitfield_to_boolean_mask` accepts either an integer +bit mask or lists of bit flags. Lists of bit flags will be combined into a +bit mask and can be provided either as a Python list of +**integer bit flag values** or as a comma-separated (or ``+``-separated) +list of integer bit flag values. Consider the bit mask from the first example +in `Table 1 `_. In this case ``ignore_flags`` can be set either to: + + - An integer value bit mask 80 + - A Python list indicating individual non-zero + *bit flag values:* ``[16, 64]`` + - A string of comma-separated *bit flag values or mnemonic names*: ``'16,64'``, ``'CR,WARM'`` + - A string of ``+``-separated *bit flag values or mnemonic names*: ``'16+64'``, ``'CR+WARM'`` + +Example +------- + +.. + EXAMPLE START + Specifying Bit Flags in NDData + +To specify bit flags: + + >>> from astropy.nddata import bitmask + >>> import numpy as np + >>> bitmask.bitfield_to_boolean_mask(217, ignore_flags=80) + array(True...) + >>> bitmask.bitfield_to_boolean_mask(217, ignore_flags='16,64') + array(True...) + >>> bitmask.bitfield_to_boolean_mask(217, ignore_flags=[16, 64]) + array(True...) + >>> bitmask.bitfield_to_boolean_mask(9, ignore_flags=[1, 8, 64]) + array(False...) + >>> bitmask.bitfield_to_boolean_mask([9, 10, 73, 217], ignore_flags='1,8,64') + array([False, True, False, True]...) + +It is also possible to specify the type of the output mask: + + >>> bitmask.bitfield_to_boolean_mask([9, 10, 73, 217], ignore_flags='1,8,64', dtype=np.uint8) + array([0, 1, 0, 1], dtype=uint8) + +In order to use lists of mnemonic bit flags names, one must provide a map, +a subclass of `~astropy.nddata.bitmask.BitFlagNameMap`, that can be +used to map mnemonic names to bit flag values. Normally these maps should be +provided by a third-party package supporting a specific instrument. Each bit +flag in the map may also contain a string comment following the flag value. +In the example below we define a simple mask map: + + >>> from astropy.nddata.bitmask import BitFlagNameMap + >>> class ST_DQ(BitFlagNameMap): + ... CR = 1 + ... CLOUDY = 4 + ... RAINY = 8, 'Dome closed' + ... HOT = 32 + ... DEAD = 64 + >>> bitmask.bitfield_to_boolean_mask([9, 10, 73, 217], ignore_flags='CR,RAINY,DEAD', + ... dtype=np.uint8, flag_name_map=ST_DQ) + array([0, 1, 0, 1], dtype=uint8) + +.. + EXAMPLE END + +Using Bit Flags Name Maps +========================= + +.. + EXAMPLE START + +In order to allow the use of mnemonic bit flag names to describe the flags +to be taken into consideration or ignored when creating a *boolean* mask, we +use bit flag name maps. These maps perform case-insensitive translation of +mnemonic bit flag names to the corresponding integer value. + +Bit flag name maps are subclasses of `~astropy.nddata.bitmask.BitFlagNameMap` +and can be constructed in two ways, either by directly subclassing +`~astropy.nddata.bitmask.BitFlagNameMap`, e.g., + + >>> from astropy.nddata.bitmask import BitFlagNameMap + >>> class ST_DQ(BitFlagNameMap): + ... CR = 1 + ... CLOUDY = 4 + ... RAINY = 8 + ... + >>> class ST_CAM1_DQ(ST_DQ): + ... HOT = 16 + ... DEAD = 32 + +or by using the `~astropy.nddata.bitmask.extend_bit_flag_map` class factory: + + >>> from astropy.nddata.bitmask import extend_bit_flag_map + >>> ST_DQ = extend_bit_flag_map('ST_DQ', CR=1, CLOUDY=4, RAINY=8) + >>> ST_CAM1_DQ = extend_bit_flag_map('ST_CAM1_DQ', ST_DQ, HOT=16, DEAD=32) + +.. note:: + + Bit flag values must be integer numbers that are powers of 2. + +Once constructed, bit flag values of a map cannot be modified, deleted, or +added. Adding flags to a map is allowed only through subclassing using one of +the two methods shown above or by adding lists of tuples of +the form ``('NAME', value)`` to the class. This will create a new map class +subclassed from the original map but containing the additional flags + + >>> ST_CAM1_DQ = ST_DQ + [('HOT', 16), ('DEAD', 32)] + +would result in an equivalent map as in the subclassing or class factory +examples shown above. + +Once a bit flag name map was created, the bit flag values can be accessed +either as *case-insensitive* class attributes or keys in a dictionary: + + >>> ST_CAM1_DQ.cloudy + 4 + >>> ST_CAM1_DQ['Rainy'] + 8 + +.. + EXAMPLE END + +Modifying the Formula for Creating Boolean Masks +================================================ + +`~astropy.nddata.bitmask.bitfield_to_boolean_mask` provides several parameters +that can be used to modify the formula used to create boolean masks. + +Inverting Bit Masks +------------------- + +Sometimes it is more convenient to be able to specify those bit +flags that *must be considered* when creating the boolean mask, and all other +flags should be ignored. + +Example +^^^^^^^ + +.. + EXAMPLE START + Inverting Bit Masks in NDData + +In `~astropy.nddata.bitmask.bitfield_to_boolean_mask` specifying bit flags that +must be considered when creating the boolean mask can be accomplished by +setting the parameter ``flip_bits`` to `True`. This effectively modifies +`equation (1) `_ to: + +.. _modif_eq2: + +``(2) boolean_mask = (bitfield & bit_mask) != 0`` + +So, instead of: + + >>> bitmask.bitfield_to_boolean_mask([9, 10, 73, 217], ignore_flags=[1, 8, 64]) + array([False, True, False, True]...) + +You can obtain the same result as: + + >>> bitmask.bitfield_to_boolean_mask( + ... [9, 10, 73, 217], ignore_flags=[2, 4, 16, 32, 128], flip_bits=True + ... ) + array([False, True, False, True]...) + +Note however, when ``ignore_flags`` is a comma-separated list of bit flag +values, ``flip_bits`` cannot be set to either `True` or `False`. Instead, +to flip bits of the bit mask formed from a string list of comma-separated +bit flag values, you can prepend a single ``~`` to the list: + + >>> bitmask.bitfield_to_boolean_mask([9, 10, 73, 217], ignore_flags='~2+4+16+32+128') + array([False, True, False, True]...) + +.. + EXAMPLE END + +Inverting Boolean Masks +----------------------- + +Other times, it may be more convenient to obtain an inverted mask in which +flagged data are converted to `False` instead of `True`: + +.. _modif_eq3: + +``(3) boolean_mask = (bitfield & ~bit_mask) == 0`` + +This can be accomplished by changing the ``good_mask_value`` parameter from +its default value (`False`) to `True`. + +Example +^^^^^^^ + +.. + EXAMPLE START + Inverting Boolean Masks in NDData + +To obtain an inverted mask in which flagged data are converted to `False` +instead of `True`: + + >>> bitmask.bitfield_to_boolean_mask([9, 10, 73, 217], ignore_flags=[1, 8, 64], + ... good_mask_value=True) + array([ True, False, True, False]...) + +.. + EXAMPLE END diff --git a/docs/nddata/ccddata.rst b/docs/nddata/ccddata.rst new file mode 100644 index 000000000000..5aa13a0a09a8 --- /dev/null +++ b/docs/nddata/ccddata.rst @@ -0,0 +1,249 @@ +.. _ccddata: + + +CCDData Class +============= + +Getting Started +--------------- + +Getting Data In ++++++++++++++++ + +Creating a `~astropy.nddata.CCDData` object from any array-like data using +`astropy.nddata` is convenient: + + >>> import numpy as np + >>> from astropy.nddata import CCDData + >>> from astropy.utils.data import get_pkg_data_filename + >>> ccd = CCDData(np.arange(10), unit="adu") + +Note that behind the scenes, this creates references to (not copies of) your +data when possible, so modifying the data in ``ccd`` will modify the +underlying data. + +You are **required** to provide a unit for your data. The most frequently used +units for these objects are likely to be ``adu``, ``photon``, and ``electron``, +which can be set either by providing the string name of the unit (as in the +example above) or from unit objects: + + >>> from astropy import units as u + >>> ccd_photon = CCDData([1, 2, 3], unit=u.photon) + >>> ccd_electron = CCDData([1, 2, 3], unit="electron") + +If you prefer *not* to use the unit functionality, then use the special unit +``u.dimensionless_unscaled`` when you create your `~astropy.nddata.CCDData` +images: + + >>> ccd_unitless = CCDData(np.zeros((10, 10)), + ... unit=u.dimensionless_unscaled) + +A `~astropy.nddata.CCDData` object can also be initialized from a FITS filename +or URL: + + >>> ccd = CCDData.read('my_file.fits', unit="adu") # doctest: +SKIP + >>> ccd = CCDData.read(get_pkg_data_filename('tutorials/FITS-images/HorseHead.fits'), unit="adu", cache=True) # doctest: +REMOTE_DATA +IGNORE_WARNINGS + +If there is a unit in the FITS file (in the ``BUNIT`` keyword), that will be +used, but explicitly providing a unit in ``read`` will override any unit in the +FITS file. + +There is no restriction at all on what the unit can be — any unit in +`astropy.units` or another that you create yourself will work. + +In addition, the user can specify the extension in a FITS file to use: + + >>> ccd = CCDData.read('my_file.fits', hdu=1, unit="adu") # doctest: +SKIP + +If ``hdu`` is not specified, it will assume the data is in the primary +extension. If there is no data in the primary extension, the first extension +with image data will be used. + +Metadata +++++++++ + +When initializing from a FITS file, the ``header`` property is initialized using +the header of the FITS file. Metadata is optional, and can be provided by any +dictionary or dict-like object: + + >>> ccd_simple = CCDData(np.arange(10), unit="adu") + >>> my_meta = {'observer': 'Edwin Hubble', 'exposure': 30.0} + >>> ccd_simple.header = my_meta # or use ccd_simple.meta = my_meta + +Whether the metadata is case-sensitive or not depends on how it is +initialized. A FITS header, for example, is not case-sensitive, but a Python +dictionary is. + +Getting Data Out +++++++++++++++++ + +A `~astropy.nddata.CCDData` object behaves like a ``numpy`` array (masked if the +`~astropy.nddata.CCDData` mask is set) in expressions, and the underlying +data (ignoring any mask) is accessed through the ``data`` attribute: + + >>> ccd_masked = CCDData([1, 2, 3], unit="adu", mask=[0, 0, 1]) + >>> 2 * np.ones(3) * ccd_masked # one return value will be masked + masked_array(data=[2.0, 4.0, --], + mask=[False, False, True], + fill_value=1e+20) + >>> 2 * np.ones(3) * ccd_masked.data # ignores the mask # doctest: +FLOAT_CMP + array([2., 4., 6.]) + +You can force conversion to a ``numpy`` array with: + + >>> np.asarray(ccd_masked) + array([1, 2, 3]) + >>> np.ma.array(ccd_masked.data, mask=ccd_masked.mask) + masked_array(data=[1, 2, --], + mask=[False, False, True], + fill_value=999999) + +A method for converting a `~astropy.nddata.CCDData` object to a FITS HDU list +is also available. It converts the metadata to a FITS header: + + >>> hdulist = ccd_masked.to_hdu() + +You can also write directly to a FITS file: + + >>> ccd_masked.write('my_image.fits') + +Masks and Flags ++++++++++++++++ + +Although it is not required when a `~astropy.nddata.CCDData` image is created, +you can also specify a mask and/or flags. + +A mask is a boolean array the same size as the data in which a value of +``True`` indicates that a particular pixel should be masked (*i.e.*, not be +included in arithmetic operations or aggregation). + +Flags are one or more additional arrays (of any type) whose shape matches the +shape of the data. One particularly useful type of flag is a bit planes; for +more details about bit planes and the functions ``astropy`` provides for +converting them to binary masks, see :ref:`bitmask_details`. + +A simple example on how to set flags can be: + + >>> data = np.zeros((10, 10)) + >>> ccd = CCDData(data, unit="electron") + + >>> flags = np.ones((10, 10)) # Create a simple flags array + >>> ccd = CCDData(data, unit='adu', flags=flags) + +Flags can be also set using `~astropy.nddata.FlagCollection`, which provides a +convenient interface for managing multiple flags. + + >>> ccd = CCDData(data, unit="electron") + + >>> # Create a FlagCollection with different flag types + >>> from astropy.nddata import FlagCollection + >>> flags = FlagCollection(shape=(100, 100)) + + >>> # Add different types of flags + >>> flags['COSMIC_RAY'] = np.zeros((100, 100), dtype=float) + >>> flags['SATURATED'] = np.zeros((100, 100), dtype=int) + >>> flags['BAD_PIXEL'] = np.zeros((100, 100), dtype=bool) + >>> flags['BAD_PIXEL'][50:60, 50:60] = True # Mark a region as bad + + +When writing `~astropy.nddata.CCDData` to FITS, flags are stored in additional image HDU extensions. In order +to do this, the user must explicitly provide a key or name for the flags HDU +when creating the `~astropy.nddata.CCDData` object using the ``hdu_flags``. In +case that multiple flags are set using `~astropy.nddata.FlagCollection`, they will be stored in multiple HDUs using the flag collection names, but user +must still provide a non-empty string to ``hdu_flags`` to indicate that flags should be saved. + + +WCS ++++ + +The ``wcs`` attribute of a `~astropy.nddata.CCDData` object can be set two ways. + ++ If the `~astropy.nddata.CCDData` object is created from a FITS file that has + WCS keywords in the header, the ``wcs`` attribute is set to a + `~astropy.wcs.WCS` object using the information in the FITS header. + ++ The WCS can also be provided when the `~astropy.nddata.CCDData` object is + constructed with the ``wcs`` argument. + +Either way, the ``wcs`` attribute is kept up to date if the +`~astropy.nddata.CCDData` image is trimmed. + +PSF ++++ + +The ``psf`` attributes of a `~astropy.nddata.CCDData` object can be set two ways. + ++ If the FITS file has an image HDU extension matching the appropriate name (defaulted to ``"PSFIMAGE"``), the ``psf`` attribute is loaded from that image HDU. + ++ The PSF can also be provided when the `~astropy.nddata.CCDData` object is + constructed with the ``psf`` argument. + +The ``psf`` attribute should be a normalized image representing the PSF at the center of the `~astropy.nddata.CCDData`, sized appropriately for the data; users are responsible for managing and interpreting it in context. +For more on normalizing a PSF image, see :ref:`astropy:kernel_normalization`. + +The ``psf`` attribute is set to `None` in the output of an arithmetic operation, no matter the inputs. A warning message is emitted if either of the input images contain a non-`None` PSF; users are responsible for determining the appropriate thing to do in that context. + +Uncertainty +----------- + +You can set the uncertainty directly, either by creating a +`~astropy.nddata.StdDevUncertainty` object first: + + >>> rng = np.random.default_rng() + >>> data = rng.normal(size=(10, 10), loc=1.0, scale=0.1) + >>> ccd = CCDData(data, unit="electron") + >>> from astropy.nddata.nduncertainty import StdDevUncertainty + >>> uncertainty = 0.1 * ccd.data # can be any array whose shape matches the data + >>> my_uncertainty = StdDevUncertainty(uncertainty) + >>> ccd.uncertainty = my_uncertainty + +Or by providing a `~numpy.ndarray` with the same shape as the data: + + >>> ccd.uncertainty = 0.1 * ccd.data # doctest: +ELLIPSIS + INFO: array provided for uncertainty; assuming it is a StdDevUncertainty. [...] + +In this case, the uncertainty is assumed to be +`~astropy.nddata.StdDevUncertainty`. + +Two other uncertainty classes are available for which error propagation is +also supported: `~astropy.nddata.VarianceUncertainty` and +`~astropy.nddata.InverseVariance`. Using one of these three uncertainties is +required to enable error propagation in `~astropy.nddata.CCDData`. + +If you want access to the underlying uncertainty, use its ``.array`` attribute: + + >>> ccd.uncertainty.array # doctest: +ELLIPSIS + array(...) + +Arithmetic with Images +---------------------- + +Methods are provided to perform arithmetic operations with a +`~astropy.nddata.CCDData` image and a number, an ``astropy`` +`~astropy.units.Quantity` (a number with units), or another +`~astropy.nddata.CCDData` image. + +Using these methods propagates errors correctly (if the errors are +uncorrelated), takes care of any necessary unit conversions, and applies masks +appropriately. Note that the metadata of the result is *not* set if the +operation is between two `~astropy.nddata.CCDData` objects. + + >>> result = ccd.multiply(0.2 * u.adu) + >>> uncertainty_ratio = result.uncertainty.array[0, 0]/ccd.uncertainty.array[0, 0] + >>> round(uncertainty_ratio, 5) # doctest: +FLOAT_CMP + np.float64(0.2) + >>> result.unit + Unit("adu electron") + +.. note:: + The affiliated package `ccdproc `_ provides + functions for many common data reduction operations. Those functions try to + construct a sensible header for the result and provide a mechanism for + logging the action of the function in the header. + + +The arithmetic operators ``*``, ``/``, ``+``, and ``-`` are *not* overridden. + +.. note:: + If two images have different WCS values, the ``wcs`` on the first + `~astropy.nddata.CCDData` object will be used for the resultant object. diff --git a/docs/nddata/convolution.rst b/docs/nddata/convolution.rst deleted file mode 100644 index 12c8f94fea8c..000000000000 --- a/docs/nddata/convolution.rst +++ /dev/null @@ -1,112 +0,0 @@ -Convolution -=========== - -Introduction ------------- - -``astropy.nddata`` includes a convolution function that offers -improvements compared to the scipy ``astropy.ndimage`` convolution -routines, including: - -* Proper treatment of NaN values - -* A single function for 1-D, 2-D, and 3-D convolution - -* Improved options for the treatment of edges - -* Both direct and Fast Fourier Transform (FFT) versions - -The following thumbnails show the difference between Scipy's and -Astropy's convolve functions on an Astronomical image that contains NaN -values. Scipy's function essentially returns NaN for all pixels that are -within a kernel of any NaN value, which is often not the desired result. - -.. |original| image:: images/original.png -.. |scipy| image:: images/scipy.png -.. |astropy| image:: images/astropy.png - -+-----------------------+--------------------+----------------------+ -| Original | Scipy ``convolve`` | Astropy ``convolve`` | -+-----------------------+--------------------+----------------------+ -| |original| | |scipy| | |astropy| | -+-----------------------+--------------------+----------------------+ - - -Usage ------ - -Two convolution functions are provided. They are imported as:: - - from astropy.nddata import convolve, convolve_fft - -and are both used as:: - - result = convolve(image, kernel) - result = convolve_fft(image, kernel) - -`~astropy.nddata.convolution.convolve.convolve` is implemented as a direct -convolution algorithm, while `~astropy.nddata.convolution.convolve.convolve_fft` -uses an FFT. Thus, the former is better for small kernels, while the latter -is much more efficient for larger kernels. - - -The input images and kernels should be lists or Numpy arrays with either both 1, 2, or 3 dimensions (and the number of dimensions should be the same for the image and kernel). The result is a Numpy array with the same dimensions as the input image. - -The ``convolve`` function takes an optional ``boundary=`` argument describing how to perform the convolution at the edge of the array. The values for ``boundary`` can be: - -* ``None``: set the result values to zero where the kernel extends beyond the edge of the array (default) - -* ``'fill'``: set values outside the array boundary to a constant. If this option is specified, the constant should be specified using the ``fill_value=`` argument, which defaults to zero. - -* ``'wrap'``: assume that the boundaries are periodic - -* ``'extend'`` : set values outside the array to the nearest array value - -By default, the kernel is not normalized. To normalize it prior to convolution, use:: - - result = convolve(image, kernel, normalize_kernel=True) - -Examples --------- - -Smooth a 1D array with a custom kernel and no boundary treatment:: - - >>> convolve([1, 4, 5, 6, 5, 7, 8], [0.2, 0.6, 0.2]) - array([ 0. , 3.4, 5. , 5.6, 5.6, 5.2, 0. ]) - -As above, but using the 'extend' algorithm for boundaries:: - - >>> convolve([1, 4, 5, 6, 5, 7, 8], [0.2, 0.6, 0.2], boundary='extend') - array([ 1.6, 3.6, 5. , 5.6, 5.6, 6.8, 7.8]) - -If a NaN value is present in the original array, it will be interpolated using the kernel:: - - >>> convolve([1, 4, 5, 6, np.nan, 7, 8], [0.2, 0.6, 0.2], boundary='extend') - array([ 1.6, 3.6, 5. , 5.9, 6.5, 7.1, 7.8]) - -Kernels and arrays can be specified either as lists or as Numpy arrays. The following examples show how to construct a 1-d array as a list:: - - >>> kernel = [0, 1, 0] - >>> result = convolve(spectrum, kernel) - -a 2-d array as a list:: - - >>> kernel = [[0, 1, 0], \ - [1, 2, 1], \ - [0, 1, 0]] - >>> result = convolve(image, kernel) - -and a 3-d array as a list:: - - >>> kernel = [[[0, 0, 0], [0, 2, 0], [0, 0, 0]], \ - [[0, 1, 0], [2, 3, 2], [0, 1, 0]], \ - [[0, 0, 0], [0, 2, 0], [0, 0, 0]]] - >>> result = convolve(cube, kernel) - -You can also use `~astropy.nddata.convolution.make_kernel.make_kernel` -to generate common n-dimensional kernels:: - - >>> make_kernel([3,3], 1, 'boxcar') - array([[ 0. 0. 0.] - [ 0. 1. 0.] - [ 0. 0. 0.]]) diff --git a/docs/nddata/covariance.rst b/docs/nddata/covariance.rst new file mode 100644 index 000000000000..7fc4967a735d --- /dev/null +++ b/docs/nddata/covariance.rst @@ -0,0 +1,684 @@ + +.. _nddata-covariance: + +Covariance +********** + +Overview +======== + +For a data vector, :math:`{\mathbf x} = \{x_0, x_1, ...\}`, the covariance +between any two elements :math:`x_i` and :math:`x_j` define the elements of the +*covariance matrix* + +.. math:: + + \Sigma_{ij} = \rho_{ij} \sigma_i \sigma_j, + +where :math:`\rho_{ij}` are the elements of the *correlation matrix* and +:math:`V_i \equiv \sigma^2_i` is the variance in :math:`x_i`. The covariance +matrix is, by definition, symmetric and positive-semidefinite (all eigenvalues +are non-negative). + +The `~astropy.nddata.covariance.Covariance` object is a general utility for +constructing, visualizing, and storing two-dimensional covariance matrices. To +minimize its memory footprint, the class uses sparse matrices (i.e., the module +requires `scipy.sparse`) and only stores the upper triangle of the covariance +matrix. + +The class provides two convenient *static* methods for swapping between a full +covariance matrix (`~astropy.nddata.covariance.Covariance.revert_correlation`) +and the combination of a variance vector and correlation matrix +(`~astropy.nddata.covariance.Covariance.to_correlation`). + +.. _nddata-covariance-intro: + +Introductory Examples +--------------------- + +As a general introduction to covariance matrices, let :math:`{\mathbf x}` +contain 10 measurements. Let the correlation coefficient between adjacent +measurements be 0.5 (:math:`\rho_{ij} = 0.5\ {\rm for}\ |j-i| = 1`), 0.2 for +next but one measurements (:math:`\rho_{ij} = 0.2\ {\rm for}\ |j-i| = 2`), and 0 +otherwise. If we adopt unity variance for all elements of :math:`{\mathbf x}`, +we can directly construct the (banded) covariance matrix in python as follows: + +>>> import numpy as np +>>> +>>> # Create the covariance matrix as a dense array +>>> npts = 10 +>>> c = (np.diag(np.full(npts-2, 0.2, dtype=float), k=-2) +... + np.diag(np.full(npts-1, 0.5, dtype=float), k=-1) +... + np.diag(np.full(npts, 1.0, dtype=float), k=0) +... + np.diag(np.full(npts-1, 0.5, dtype=float), k=1) +... + np.diag(np.full(npts-2, 0.2, dtype=float), k=2)) +>>> c +array([[1. , 0.5, 0.2, 0. , 0. , 0. , 0. , 0. , 0. , 0. ], + [0.5, 1. , 0.5, 0.2, 0. , 0. , 0. , 0. , 0. , 0. ], + [0.2, 0.5, 1. , 0.5, 0.2, 0. , 0. , 0. , 0. , 0. ], + [0. , 0.2, 0.5, 1. , 0.5, 0.2, 0. , 0. , 0. , 0. ], + [0. , 0. , 0.2, 0.5, 1. , 0.5, 0.2, 0. , 0. , 0. ], + [0. , 0. , 0. , 0.2, 0.5, 1. , 0.5, 0.2, 0. , 0. ], + [0. , 0. , 0. , 0. , 0.2, 0.5, 1. , 0.5, 0.2, 0. ], + [0. , 0. , 0. , 0. , 0. , 0.2, 0.5, 1. , 0.5, 0.2], + [0. , 0. , 0. , 0. , 0. , 0. , 0.2, 0.5, 1. , 0.5], + [0. , 0. , 0. , 0. , 0. , 0. , 0. , 0.2, 0.5, 1. ]]) + +In this case, the correlation matrix and the covariance matrix are *identical* +because the elements of the variance vector are all unity. + +With a correlation matrix, we can construct the covariance matrix with any +arbitrary variance vector. Continuing the example above, the following creates +a new covariance matrix with a variance vector with all elements equal to 4: + +>>> new_var = np.full(npts, 4., dtype=float) +>>> new_c = c * np.sqrt(new_var[:,None] * new_var[None,:]) +>>> new_c +array([[4. , 2. , 0.8, 0. , 0. , 0. , 0. , 0. , 0. , 0. ], + [2. , 4. , 2. , 0.8, 0. , 0. , 0. , 0. , 0. , 0. ], + [0.8, 2. , 4. , 2. , 0.8, 0. , 0. , 0. , 0. , 0. ], + [0. , 0.8, 2. , 4. , 2. , 0.8, 0. , 0. , 0. , 0. ], + [0. , 0. , 0.8, 2. , 4. , 2. , 0.8, 0. , 0. , 0. ], + [0. , 0. , 0. , 0.8, 2. , 4. , 2. , 0.8, 0. , 0. ], + [0. , 0. , 0. , 0. , 0.8, 2. , 4. , 2. , 0.8, 0. ], + [0. , 0. , 0. , 0. , 0. , 0.8, 2. , 4. , 2. , 0.8], + [0. , 0. , 0. , 0. , 0. , 0. , 0.8, 2. , 4. , 2. ], + [0. , 0. , 0. , 0. , 0. , 0. , 0. , 0.8, 2. , 4. ]]) + +Or likewise for heteroscedastic data: + +>>> new_var = (1. + np.absolute(np.arange(npts) - npts//2).astype(float))**2 +>>> new_var +array([36., 25., 16., 9., 4., 1., 4., 9., 16., 25.]) +>>> new_c = c * np.sqrt(new_var[:,None] * new_var[None,:]) +>>> new_c +array([[36. , 15. , 4.8, 0. , 0. , 0. , 0. , 0. , 0. , 0. ], + [15. , 25. , 10. , 3. , 0. , 0. , 0. , 0. , 0. , 0. ], + [ 4.8, 10. , 16. , 6. , 1.6, 0. , 0. , 0. , 0. , 0. ], + [ 0. , 3. , 6. , 9. , 3. , 0.6, 0. , 0. , 0. , 0. ], + [ 0. , 0. , 1.6, 3. , 4. , 1. , 0.8, 0. , 0. , 0. ], + [ 0. , 0. , 0. , 0.6, 1. , 1. , 1. , 0.6, 0. , 0. ], + [ 0. , 0. , 0. , 0. , 0.8, 1. , 4. , 3. , 1.6, 0. ], + [ 0. , 0. , 0. , 0. , 0. , 0.6, 3. , 9. , 6. , 3. ], + [ 0. , 0. , 0. , 0. , 0. , 0. , 1.6, 6. , 16. , 10. ], + [ 0. , 0. , 0. , 0. , 0. , 0. , 0. , 3. , 10. , 25. ]]) + +.. note:: + + The `~astropy.nddata.covariance.Covariance` class provides a convenience + function for creating a new `~astropy.nddata.covariance.Covariance` instance + with the same correlation matrix but a new variance vector; see + :ref:`here`. + +Reordering and Subsets +---------------------- + +When reordering or down-selecting subsets of the elements of :math:`\mathbf{x}`, +these changes must be propagated to the associated covariance matrix, just as +would be needed for the error vector for an uncorrelated dataset. + +The example below first generates a vector with a shuffled set of indices. The +reordered :math:`\mathbf{x}` vector would be constructed by setting +``reordered_x = x[i]`` and the covariance matrix would be reordered using +`numpy.ix_`, as follows: + +>>> rng = np.random.default_rng(99) +>>> i = np.arange(npts) +>>> rng.shuffle(i) +>>> i +array([4, 6, 0, 3, 8, 1, 2, 5, 7, 9]) +>>> reordered_c = c[np.ix_(i,i)] +>>> reordered_c +array([[1. , 0.2, 0. , 0.5, 0. , 0. , 0.2, 0.5, 0. , 0. ], + [0.2, 1. , 0. , 0. , 0.2, 0. , 0. , 0.5, 0.5, 0. ], + [0. , 0. , 1. , 0. , 0. , 0.5, 0.2, 0. , 0. , 0. ], + [0.5, 0. , 0. , 1. , 0. , 0.2, 0.5, 0.2, 0. , 0. ], + [0. , 0.2, 0. , 0. , 1. , 0. , 0. , 0. , 0.5, 0.5], + [0. , 0. , 0.5, 0.2, 0. , 1. , 0.5, 0. , 0. , 0. ], + [0.2, 0. , 0.2, 0.5, 0. , 0.5, 1. , 0. , 0. , 0. ], + [0.5, 0.5, 0. , 0.2, 0. , 0. , 0. , 1. , 0.2, 0. ], + [0. , 0.5, 0. , 0. , 0.5, 0. , 0. , 0.2, 1. , 0.2], + [0. , 0. , 0. , 0. , 0.5, 0. , 0. , 0. , 0.2, 1. ]]) + +Note that the diagonal of ``reordered_c`` is still unity (all elements of +:math:`\mathbf{x}` are perfectly correlated with themselves), but the +off-diagonal terms have been rearranged to maintain the pre-existing +correlations. + +Creating a covariance matrix for a subset of data is a very similar operation. +If we want the covariance matrix for the first 3 elements of the data vector, we +can do the following: + +>>> i = np.arange(3) +>>> sub_c = c[np.ix_(i,i)] +>>> sub_c +array([[1. , 0.5, 0.2], + [0.5, 1. , 0.5], + [0.2, 0.5, 1. ]]) + +.. note:: + + The `~astropy.nddata.covariance.Covariance` class provides a convenience + function for matching the covariance data to a slice of its parent data array; + see :ref:`here`. + +In N-dimensions +--------------- + +Covariance matrices can be constructed for arrays of higher dimensionality by +flattening the data arrays. For a row-major array flattening order, one can +adopt the convention that :math:`\Sigma_{ij}` for an image of size +:math:`(N_x,N_y)` is the covariance between image pixels :math:`I_{x_i,y_i}` and +:math:`I_{x_j,y_j}`, where :math:`i = x_i + N_x\ y_i` and :math:`j = x_j + N_x\ +y_j`. + +As an example, let the covariance matrix ``c``, used throughout this section, be +the covariance matrix for a :math:`5 \times 2` array, instead of a 10-element +vector. The complication is determining the mapping from the data array to the +relevant covariance element; we can do this using `numpy` functions as follows. +To determine the covariance between elements ``data[1,0]`` and ``data[2,0]``, we +convert the indices from the ``data`` to find a covariance of 0.2: + +>>> data_array_shape = (5,2) +>>> i_data = (np.array([1]), np.array([0])) +>>> j_data = (np.array([2]), np.array([0])) +>>> i_cov = np.ravel_multi_index(i_data, data_array_shape) +>>> j_cov = np.ravel_multi_index(j_data, data_array_shape) +>>> i_cov, j_cov +(array([2]), array([4])) +>>> c[i_cov, j_cov] +array([0.2]) + +The inverse operation (determining the indices of the data array given the +indices in the covariance matrix) uses `~numpy.unravel_index` (cf. ``i_data``): + +>>> np.unravel_index(i_cov, data_array_shape) +(array([1]), array([0])) + +.. note:: + + The `~astropy.nddata.covariance.Covariance` class provides convenience + functions for switching between the data array and covariance matrix + indexing when working with higher dimensionality data arrays; + see :ref:`here`. + +.. _nddata-covariance-construction: + +Construction +============ + +Many methods are provided to construct a `~astropy.nddata.covariance.Covariance` +object. In *all* of the following examples, the object ``c`` is the banded +covariance array created at the beginning of the :ref:`nddata-covariance-intro` +section. + +Instantiating from pre-existing arrays +-------------------------------------- + +The simplest instantiation methods are based on using data that are already +available. + +To create a `~astropy.nddata.covariance.Covariance` object from a +variance vector: + +.. doctest-requires:: scipy + + >>> from astropy.nddata.covariance import Covariance + >>> # Create from a variance vector + >>> var = np.ones(3, dtype=float) + >>> # Create from the Covariance object + >>> covar = Covariance.from_variance(var) + >>> # Test its contents + >>> print(np.array_equal(covar.to_dense(), np.identity(3))) + True + +In this case, the variance is unity for all elements of the data array such that +the covariance matrix is diagonal and identical to the identity matrix. + +To create a `~astropy.nddata.covariance.Covariance` object from a "dense" (i.e., +fully populated) covariance matrix: + +.. doctest-requires:: scipy + + >>> # Instantiate from a covariance array + >>> covar = Covariance(array=c) + >>> print(np.array_equal(covar.to_dense(), c)) + True + >>> covar.to_dense() + array([[1. , 0.5, 0.2, 0. , 0. , 0. , 0. , 0. , 0. , 0. ], + [0.5, 1. , 0.5, 0.2, 0. , 0. , 0. , 0. , 0. , 0. ], + [0.2, 0.5, 1. , 0.5, 0.2, 0. , 0. , 0. , 0. , 0. ], + [0. , 0.2, 0.5, 1. , 0.5, 0.2, 0. , 0. , 0. , 0. ], + [0. , 0. , 0.2, 0.5, 1. , 0.5, 0.2, 0. , 0. , 0. ], + [0. , 0. , 0. , 0.2, 0.5, 1. , 0.5, 0.2, 0. , 0. ], + [0. , 0. , 0. , 0. , 0.2, 0.5, 1. , 0.5, 0.2, 0. ], + [0. , 0. , 0. , 0. , 0. , 0.2, 0.5, 1. , 0.5, 0.2], + [0. , 0. , 0. , 0. , 0. , 0. , 0.2, 0.5, 1. , 0.5], + [0. , 0. , 0. , 0. , 0. , 0. , 0. , 0.2, 0.5, 1. ]]) + +.. important:: + + The last statement uses `~astropy.nddata.covariance.Covariance.to_dense` to + access the array; see :ref:`nddata-covariance-data-access`. + +Above, the base instantiation method is used; however, the +`~astropy.nddata.covariance.Covariance.from_array` method is also provided. The +primary difference is that the latter allows limits to be imposed on the +(absolute value of the) correlation or covariance values. + +Finally, note that, by default, all instantiations of a +`~astropy.nddata.covariance.Covariance` object check that the input matrix is +symmetric. If it is not, a warning is issued. To skip the check and the +warning, set ``assume_symmetric=True``. Regardless of whether or not the check +is performed, the object *only stores the upper triangle of the input matrix* +effectively meaning that any asymmetry in the matrix is lost when it is +ingested. + +Instantiating from random samples +--------------------------------- + +You can construct a covariance matrix based on samples from a distribution using +`~astropy.nddata.covariance.Covariance.from_samples`: + +.. doctest-requires:: scipy + + >>> # Set the mean to 0 for all elements + >>> m = np.zeros(npts, dtype=float) + >>> + >>> # Sample the multivariate normal distribution with the provided + >>> # mean and covariance. + >>> s = rng.multivariate_normal(m, c, size=100000) + >>> + >>> # Construct the covariance matrix from the random samples + >>> covar = Covariance.from_samples(s.T, cov_tol=0.1) + >>> + >>> # Test that the known input covariance matrix is close to the + >>> # measured covariance from the random samples + >>> print(np.all(np.absolute(c - covar.to_dense()) < 0.02)) + True + +Here, we have drawn samples from a known multivariate normal distribution with a +mean of zero (``m``) and a known covariance matrix (``c``), defined for the 10 +(``npts``) elements in the dataset (e.g., 10 pixels in a spectrum). The code +checks the reconstruction of the known covariance matrix against the result +built from these random samples. + +Instantiating from a matrix multiplication +------------------------------------------ + +Linear operations on a dataset (e.g., binning or smoothing) can be written as +matrix multiplications of the form + +.. math:: + + {\mathbf y} = {\mathbf T}\ {\mathbf x}, + +where :math:`{\mathbf T}` is a transfer matrix of size :math:`N_y\times N_x`, +:math:`{\mathbf x}` is a vector of size :math:`N_x`, and :math:`{\mathbf y}` is +a vector of length :math:`{N_y}` that results from the multiplication. If +:math:`{\mathbf \Sigma}_x` is the covariance matrix for :math:`{\mathbf x}`, then +the covariance matrix for :math:`{\mathbf y}` is + +.. math:: + + {\mathbf \Sigma}_y = {\mathbf T}\ {\mathbf \Sigma}_x\ {\mathbf T}^\top. + +The example below shows how to build a covariance matrix from a matrix +multiplication using +`~astropy.nddata.covariance.Covariance.from_matrix_multiplication`: + +.. doctest-requires:: scipy + + >>> # Construct a dataset + >>> x = np.arange(npts, dtype=float) + >>> + >>> # Construct a transfer matrix that simply selects the elements at + >>> # indices 0, 2, and 4 + >>> t = np.zeros((3,npts), dtype=float) + >>> t[0,0] = 1.0 + >>> t[1,2] = 1.0 + >>> t[2,4] = 1.0 + >>> + >>> # Get y + >>> y = np.dot(t, x) + >>> y + array([0., 2., 4.]) + >>> + >>> # Construct the covariance matrix + >>> covar = Covariance.from_matrix_multiplication(t, c) + >>> + >>> # Test the result + >>> _c = (np.diag(np.full(3-1, 0.2, dtype=float), k=-1) + ... + np.diag(np.full(3, 1.0, dtype=float), k=0) + ... + np.diag(np.full(3-1, 0.2, dtype=float), k=1)) + >>> _c + array([[1. , 0.2, 0. ], + [0.2, 1. , 0.2], + [0. , 0.2, 1. ]]) + >>> print(np.array_equal(covar.to_dense(), _c)) + True + +In N-dimensions +--------------- + +All of the instantiation methods above allow you to define the "data shape" of +the data array for the associated covariance matrix. Following the previous +N-dimensional example, let ``c`` be the covariance matrix for a :math:`5 \times +2` array, instead of a 10-element vector. + +.. doctest-requires:: scipy + + >>> data_array_shape + (5, 2) + >>> covar = Covariance(array=c, data_shape=data_array_shape) + >>> covar.to_dense() + array([[1. , 0.5, 0.2, 0. , 0. , 0. , 0. , 0. , 0. , 0. ], + [0.5, 1. , 0.5, 0.2, 0. , 0. , 0. , 0. , 0. , 0. ], + [0.2, 0.5, 1. , 0.5, 0.2, 0. , 0. , 0. , 0. , 0. ], + [0. , 0.2, 0.5, 1. , 0.5, 0.2, 0. , 0. , 0. , 0. ], + [0. , 0. , 0.2, 0.5, 1. , 0.5, 0.2, 0. , 0. , 0. ], + [0. , 0. , 0. , 0.2, 0.5, 1. , 0.5, 0.2, 0. , 0. ], + [0. , 0. , 0. , 0. , 0.2, 0.5, 1. , 0.5, 0.2, 0. ], + [0. , 0. , 0. , 0. , 0. , 0.2, 0.5, 1. , 0.5, 0.2], + [0. , 0. , 0. , 0. , 0. , 0. , 0.2, 0.5, 1. , 0.5], + [0. , 0. , 0. , 0. , 0. , 0. , 0. , 0.2, 0.5, 1. ]]) + +The covariance matrix looks identical, but the higher dimensionality will affect +its :ref:`nddata-covariance-coord-access`. + +.. _nddata-covariance-data-access: + +Accessing the data +================== + +The `~astropy.nddata.covariance.Covariance` object is primarily a storage +utility. Internally, the object only stores the upper triangle of the covariance +matrix. **This means that you should not directly access a covariance value +within the object itself**; you must use the functions described below. + +.. _nddata-covariance-covariance-access: + +Covariance Matrix +----------------- + +There are two ways to access the full covariance matrix: + +- Use `~astropy.nddata.covariance.Covariance.to_sparse` to produce a sparse matrix or + +- Use `~astropy.nddata.covariance.Covariance.to_dense` for a dense matrix. + +The output of these two methods can be used as you would use any +`scipy.sparse.csr_matrix` or `numpy.ndarray` object, respectively. + +.. _nddata-covariance-correl-access: + +Variance Vector and Correlation Matrix +-------------------------------------- + +The variance vector is stored as an accessible property +(`~astropy.nddata.covariance.Covariance.variance`), but note that the property +is immutable. + +Access to the full correlation matrix is provided using +`~astropy.nddata.covariance.Covariance.to_sparse` to produce a sparse matrix or +`~astropy.nddata.covariance.Covariance.to_dense` for a dense matrix by setting +the keyword argument ``correlation = True``. + +.. _nddata-covariance-coord-access: + +Coordinate Data +--------------- + +Although more useful as preparation for storage, the covariance data can also be +accessed in coordinate format: + +.. doctest-requires:: scipy + + >>> covar = Covariance(array=c) + >>> i, j, cij = covar.coordinate_data() + >>> print(np.array_equal(covar.to_dense()[i,j], cij)) + True + +The arrays returned by `~astropy.nddata.covariance.Covariance.coordinate_data` +provide the matrix coordinates (``i`` and ``j``) for the non-zero covariance +values (``cij``). + +.. _nddata-covariance-table: + +File IO +======= + +The primary way to write/read `~astropy.nddata.covariance.Covariance` objects is +by first parsing the data into a `~astropy.table.Table` using the +`~astropy.nddata.covariance.Covariance.to_table` method: + +.. doctest-requires:: scipy + + >>> covar = Covariance(array=c) + >>> tbl = covar.to_table() + >>> tbl.meta + {'COVSHAPE': '(10, 10)'} + >>> tbl[:3] + + INDXI INDXJ COVARIJ + int64 int64 float64 + ----- ----- ------- + 0 0 1.0 + 0 1 0.5 + 0 2 0.2 + +The output above just shows the first 3 rows of the table to demonstrate that +the non-zero elements of the covariance matrix are stored in "coordinate +format." Specifically, the data is provided in three columns: + +- ``'INDXI'``: The row index in the covariance matrix (:math:`i`). + +- ``'INDXJ'``: The column index in the covariance matrix (:math:`j`). + +- ``'COVARIJ'``: The covariance value (:math:`\Sigma_{ij}`). + +The table also contains the following metadata: + +- ``'COVSHAPE'``: The shape of the covariance matrix. + +- ``'BUNIT'``: (If defined) The string representation of the covariance units. + +- ``'COVDSHP'``: (If the dimensionality is greater than 1) The shape of the + associated data array. + +For higher dimensional arrays, the coordinate data are automatically reshaped so +that the indices correspond to the data array. For example, + +.. doctest-requires:: scipy + + >>> data_array_shape + (5, 2) + >>> covar = Covariance(array=c, data_shape=data_array_shape) + >>> tbl = covar.to_table() + >>> tbl.meta + {'COVSHAPE': '(10, 10)', 'COVDSHP': '(5, 2)'} + >>> tbl[:3] +
+ INDXI INDXJ COVARIJ + int64[2] int64[2] float64 + -------- -------- ------- + 0 .. 0 0 .. 0 1.0 + 0 .. 0 0 .. 1 0.5 + 0 .. 0 1 .. 0 0.2 + >>> tbl['INDXI'][0] + array([0, 0]) + +.. warning:: + + Recall that the storage of covariance matrices for higher + dimensional data always assumes a row-major storage order. + +The inverse operation is also provided to instantiate a +`~astropy.nddata.covariance.Covariance` object from a table. Continuing the +N-dimensional example above: + +.. doctest-requires:: scipy + + >>> _covar = Covariance.from_table(tbl) + >>> _covar.data_shape + (5, 2) + >>> _covar.to_dense() + array([[1. , 0.5, 0.2, 0. , 0. , 0. , 0. , 0. , 0. , 0. ], + [0.5, 1. , 0.5, 0.2, 0. , 0. , 0. , 0. , 0. , 0. ], + [0.2, 0.5, 1. , 0.5, 0.2, 0. , 0. , 0. , 0. , 0. ], + [0. , 0.2, 0.5, 1. , 0.5, 0.2, 0. , 0. , 0. , 0. ], + [0. , 0. , 0.2, 0.5, 1. , 0.5, 0.2, 0. , 0. , 0. ], + [0. , 0. , 0. , 0.2, 0.5, 1. , 0.5, 0.2, 0. , 0. ], + [0. , 0. , 0. , 0. , 0.2, 0.5, 1. , 0.5, 0.2, 0. ], + [0. , 0. , 0. , 0. , 0. , 0.2, 0.5, 1. , 0.5, 0.2], + [0. , 0. , 0. , 0. , 0. , 0. , 0.2, 0.5, 1. , 0.5], + [0. , 0. , 0. , 0. , 0. , 0. , 0. , 0.2, 0.5, 1. ]]) + +Use of the `~astropy.nddata.covariance.Covariance.to_table` and +`~astropy.nddata.covariance.Covariance.from_table` methods can be used with +Astropy's unified file I/O system to read and write the covariance matrices. + +For example, to write the covariance matrix to table and reload it: + +.. doctest-requires:: scipy + + >>> ofile = 'test_covar_io.fits' + >>> covar = Covariance(array=c) + >>> tbl = covar.to_table() + >>> tbl.write(ofile, format='fits') + >>> from astropy.io import fits + >>> with fits.open(ofile) as hdu: + ... hdu.info() + ... + Filename: test_covar_io.fits + No. Name Ver Type Cards Dimensions Format + 0 PRIMARY 1 PrimaryHDU 4 () + 1 1 BinTableHDU 15 27R x 3C [K, K, D] + >>> from astropy.table import Table + >>> _tbl = Table.read(ofile, format='fits') + >>> _covar = Covariance.from_table(_tbl) + >>> print(np.array_equal(covar.to_dense(), _covar.to_dense())) + True + +Utility Functions +================= + +.. _covariance-apply-new-variance: + +Renormalizing the variance +-------------------------- + +To create a new covariance matrix that maintains the same correlations as an +existing matrix but a different variance, you can apply a new variance +normalization (following the examples in the :ref:`introductory section +`). The `~astropy.nddata.covariance.Covariance` object +provides a convenience function for this. + +.. doctest-requires:: scipy + + >>> covar_var1 = Covariance(array=c) + >>> covar_var1.to_dense() + array([[1. , 0.5, 0.2, 0. , 0. , 0. , 0. , 0. , 0. , 0. ], + [0.5, 1. , 0.5, 0.2, 0. , 0. , 0. , 0. , 0. , 0. ], + [0.2, 0.5, 1. , 0.5, 0.2, 0. , 0. , 0. , 0. , 0. ], + [0. , 0.2, 0.5, 1. , 0.5, 0.2, 0. , 0. , 0. , 0. ], + [0. , 0. , 0.2, 0.5, 1. , 0.5, 0.2, 0. , 0. , 0. ], + [0. , 0. , 0. , 0.2, 0.5, 1. , 0.5, 0.2, 0. , 0. ], + [0. , 0. , 0. , 0. , 0.2, 0.5, 1. , 0.5, 0.2, 0. ], + [0. , 0. , 0. , 0. , 0. , 0.2, 0.5, 1. , 0.5, 0.2], + [0. , 0. , 0. , 0. , 0. , 0. , 0.2, 0.5, 1. , 0.5], + [0. , 0. , 0. , 0. , 0. , 0. , 0. , 0.2, 0.5, 1. ]]) + >>> var4 = np.full(c.shape[0], 4.0, dtype=float) + >>> covar_var4 = covar_var1.apply_new_variance(var4) + >>> covar_var4.to_dense() + array([[4. , 2. , 0.8, 0. , 0. , 0. , 0. , 0. , 0. , 0. ], + [2. , 4. , 2. , 0.8, 0. , 0. , 0. , 0. , 0. , 0. ], + [0.8, 2. , 4. , 2. , 0.8, 0. , 0. , 0. , 0. , 0. ], + [0. , 0.8, 2. , 4. , 2. , 0.8, 0. , 0. , 0. , 0. ], + [0. , 0. , 0.8, 2. , 4. , 2. , 0.8, 0. , 0. , 0. ], + [0. , 0. , 0. , 0.8, 2. , 4. , 2. , 0.8, 0. , 0. ], + [0. , 0. , 0. , 0. , 0.8, 2. , 4. , 2. , 0.8, 0. ], + [0. , 0. , 0. , 0. , 0. , 0.8, 2. , 4. , 2. , 0.8], + [0. , 0. , 0. , 0. , 0. , 0. , 0.8, 2. , 4. , 2. ], + [0. , 0. , 0. , 0. , 0. , 0. , 0. , 0.8, 2. , 4. ]]) + +.. _covariance-match-to-data-slice: + +Matching the covariance data to a slice of its parent data array +---------------------------------------------------------------- + +To adjust a `~astropy.nddata.covariance.Covariance` object so that it is +appropriate for a slice of its parent data array, use +`~astropy.nddata.covariance.Covariance.match_to_data_slice`. For example, to +create a matrix with every other entry: + +.. doctest-requires:: scipy + + >>> covar = Covariance(array=c) + >>> sub_covar = covar.match_to_data_slice(np.s_[::2]) + >>> sub_covar + + >>> sub_covar.to_dense() + array([[1. , 0.2, 0. , 0. , 0. ], + [0.2, 1. , 0.2, 0. , 0. ], + [0. , 0.2, 1. , 0.2, 0. ], + [0. , 0. , 0.2, 1. , 0.2], + [0. , 0. , 0. , 0.2, 1. ]]) + +or to adjust for a reordering of the parent data array: + +.. doctest-requires:: scipy + + >>> covar = Covariance(array=c) + >>> rng = np.random.default_rng(99) + >>> reorder = np.arange(covar.shape[0]) + >>> rng.shuffle(reorder) + >>> reorder + array([4, 6, 0, 3, 8, 1, 2, 5, 7, 9]) + >>> reorder_covar = covar.match_to_data_slice(reorder) + >>> reorder_covar.to_dense() + array([[1. , 0.2, 0. , 0.5, 0. , 0. , 0.2, 0.5, 0. , 0. ], + [0.2, 1. , 0. , 0. , 0.2, 0. , 0. , 0.5, 0.5, 0. ], + [0. , 0. , 1. , 0. , 0. , 0.5, 0.2, 0. , 0. , 0. ], + [0.5, 0. , 0. , 1. , 0. , 0.2, 0.5, 0.2, 0. , 0. ], + [0. , 0.2, 0. , 0. , 1. , 0. , 0. , 0. , 0.5, 0.5], + [0. , 0. , 0.5, 0.2, 0. , 1. , 0.5, 0. , 0. , 0. ], + [0.2, 0. , 0.2, 0.5, 0. , 0.5, 1. , 0. , 0. , 0. ], + [0.5, 0.5, 0. , 0.2, 0. , 0. , 0. , 1. , 0.2, 0. ], + [0. , 0.5, 0. , 0. , 0.5, 0. , 0. , 0.2, 1. , 0.2], + [0. , 0. , 0. , 0. , 0.5, 0. , 0. , 0. , 0.2, 1. ]]) + +.. _covariance-nd-indexing: + +Data-to-covariance Indexing Transformations +------------------------------------------- + +For higher dimensional arrays, two methods are provided to ease conversion +between data array and covariance matrix indexing. Following examples above, +define the ten elements in the covariance matrix as coming from a :math:`5 +\times 2` array, then find the indices in the data array for the covariance +values at indices covariance values at matrix locations ``(0,3)``, ``(1,4)``, +and ``(2,3)``: + +.. doctest-requires:: scipy + + >>> covar = Covariance(array=c, data_shape=data_array_shape) + >>> i_data, j_data = covar.covariance_to_data_indices([0,1,2], [3,4,3]) + >>> i_data + (array([0, 0, 1]), array([0, 1, 0])) + >>> j_data + (array([1, 2, 1]), array([1, 0, 1])) + +This shows that the covariance elements provide the covariance between +``data[0,0]`` and ``data[1,1]``, elements ``data[0,1]`` and ``data[2,0]``, and +elements ``data[1,0]`` and ``data[1,1]``. + +The inverse operation gives the covariance indices for a specified set of +data-array indices. Keeping the indices we defined above: + +.. doctest-requires:: scipy + + >>> i_cov, j_cov = covar.data_to_covariance_indices(i_data, j_data) + >>> i_cov, j_cov + (array([0, 1, 2]), array([3, 4, 3])) + diff --git a/docs/nddata/decorator.rst b/docs/nddata/decorator.rst new file mode 100644 index 000000000000..2077134fa65a --- /dev/null +++ b/docs/nddata/decorator.rst @@ -0,0 +1,60 @@ +********************************************* +Decorating Functions to Accept NDData Objects +********************************************* + +The `astropy.nddata` module includes a decorator +:func:`~astropy.nddata.support_nddata` that makes it convenient for developers +and users to write functions that can accept :class:`~astropy.nddata.NDData` +objects and also separate arguments. + +Consider the following function:: + + def test(data, wcs=None, unit=None, n_iterations=3): + ... + +Now say that we want to be able to call the function as ``test(nd)`` +where ``nd`` is an :class:`~astropy.nddata.NDData` instance. We can decorate +this function using :func:`~astropy.nddata.support_nddata`:: + + from astropy.nddata import support_nddata + + @support_nddata + def test(data, wcs=None, unit=None, n_iterations=3): + ... + +Which makes it so that when the user calls ``test(nd)``, the function would +automatically be called with:: + + test(nd.data, wcs=nd.wcs, unit=nd.unit) + +The decorator looks at the signature of the function and checks if any +of the arguments are also properties of the ``NDData`` object, and passes them +as individual arguments. The function can also be called with separate +arguments as if it was not decorated. + +A warning is emitted if an ``NDData`` property is set but the function does +not accept it — for example, if ``wcs`` is set, but the function cannot support +WCS objects. On the other hand, if an argument in the function does not exist +in the ``NDData`` object or is not set, it is left to its default value. + +If the function call succeeds, then the decorator returns the values from the +function unmodified by default. However, in some cases we may want to return +separate ``data``, ``wcs``, etc. if these were passed in separately, and a new +:class:`~astropy.nddata.NDData` instance otherwise. To do this, you can specify +``repack=True`` in the decorator and provide a list of the names of the output +arguments from the function:: + + @support_nddata(repack=True, returns=['data', 'wcs']) + def test(data, wcs=None, unit=None, n_iterations=3): + ... + +With this, the function will return separate values if ``test`` is called with +separate arguments, and an object with the same class type as the input if the +input is an :class:`~astropy.nddata.NDData` or subclass instance. + +Finally, the decorator can be made to restrict input to specific ``NDData`` +subclasses (and the subclasses of those) using the ``accepts`` option:: + + @support_nddata(accepts=CCDImage) + def test(data, wcs=None, unit=None, n_iterations=3): + ... diff --git a/docs/nddata/examples/cutout2d_tofits.py b/docs/nddata/examples/cutout2d_tofits.py new file mode 100644 index 000000000000..dac61aec05f9 --- /dev/null +++ b/docs/nddata/examples/cutout2d_tofits.py @@ -0,0 +1,36 @@ +# Download an example FITS file, create a 2D cutout, and save it to a +# new FITS file, including the updated cutout WCS. +from astropy.io import fits +from astropy.nddata import Cutout2D +from astropy.utils.data import download_file +from astropy.wcs import WCS + + +def download_image_save_cutout(url, position, size): + # Download the image + filename = download_file(url) + + # Load the image and the WCS + hdu = fits.open(filename)[0] + wcs = WCS(hdu.header) + + # Make the cutout, including the WCS + cutout = Cutout2D(hdu.data, position=position, size=size, wcs=wcs) + + # Put the cutout image in the FITS HDU + hdu.data = cutout.data + + # Update the FITS header with the cutout WCS + hdu.header.update(cutout.wcs.to_header()) + + # Write the cutout to a new FITS file + cutout_filename = "example_cutout.fits" + hdu.writeto(cutout_filename, overwrite=True) + + +if __name__ == "__main__": + url = "https://astropy.stsci.edu/data/photometry/spitzer_example_image.fits" + + position = (500, 300) + size = (400, 400) + download_image_save_cutout(url, position, size) diff --git a/docs/nddata/index.rst b/docs/nddata/index.rst index 2bb555d73aca..894b777fa838 100644 --- a/docs/nddata/index.rst +++ b/docs/nddata/index.rst @@ -1,72 +1,515 @@ .. _astropy_nddata: ***************************************** -N-dimensional datasets (`astropy.nddata`) +N-Dimensional Datasets (`astropy.nddata`) ***************************************** Introduction ============ -`astropy.nddata` provides the `~astropy.nddata.nddata.NDData` -class and related tools to manage n-dimensional array-based data (e.g. -CCD images, IFU Data, grid-based simulation data, ...). This is more than -just `numpy.ndarray` objects, because it provides metadata that cannot -be easily provided by a single array. +The `~astropy.nddata` package provides classes to represent images and other +gridded data, some essential functions for manipulating images, and the +infrastructure for package developers who wish to include support for the +image classes. This subpackage was developed based on `APE 7`_. -This subpackage also provides new convolution routines that differ from -Scipy in that they offer a proper treatment of NaN values. +.. _astropy_nddata_getting_started: - -Getting started +Getting Started =============== -The `~astropy.nddata.nddata.NDData` class is still under development, and -many of it's more advanced features are not yet implemented. It already -functions as an array container with metadata, however:: +NDData +------ + +The primary purpose of `~astropy.nddata.NDData` is to act as a *container* for +data, metadata, and other related information like a mask. +An `~astropy.nddata.NDData` object can be instantiated by passing it an +n-dimensional `numpy` array:: + + >>> import numpy as np >>> from astropy.nddata import NDData - >>> ndd = NDData(mydataarray, error=myerrorarray) - >>> ndd['Exposure time(s)'] = 5 + >>> array = np.zeros((12, 12, 12)) # a 3-dimensional array with all zeros + >>> ndd1 = NDData(array) + +Or something that can be converted to a `numpy.ndarray`:: + + >>> ndd2 = NDData([1, 2, 3, 4]) + >>> ndd2 + NDData([1, 2, 3, 4]) + +And can be accessed again via the ``data`` attribute:: + + >>> ndd2.data + array([1, 2, 3, 4]) + +It also supports additional properties like a ``unit`` or ``mask`` for the +data, a ``wcs`` (World Coordinate System) and ``uncertainty`` of the data and +additional ``meta`` attributes: + + >>> data = np.array([1,2,3,4]) + >>> mask = data > 2 + >>> unit = 'erg / s' + >>> from astropy.nddata import StdDevUncertainty + >>> uncertainty = StdDevUncertainty(np.sqrt(data)) # representing standard deviation + >>> meta = {'object': 'fictional data.'} + >>> ndd = NDData(data, mask=mask, unit=unit, uncertainty=uncertainty, + ... meta=meta) + >>> ndd + NDData([1, 2, —, —], unit='erg / s') + +The representation only displays the ``data``; the other attributes need to be +accessed directly, for example, ``ndd.mask`` to access the mask. + + +NDDataRef +--------- -You can, also make use of the new convolution routines. For example, if your -data is 1D, you might smooth it by a gaussian kernel by doing:: +Building upon this pure container, `~astropy.nddata.NDDataRef` implements: - >>> from astropy.nddata import convolve, make_kernel - >>> kernel = make_kernel((9,), 1.5, 'gaussian') - >>> ndd.data = convolve(ndd.data, kernel) ++ A ``read`` and ``write`` method to access ``astropy``'s unified file I/O + interface. ++ Simple arithmetic like addition, subtraction, division, and multiplication. ++ Slicing. -The convolution routines can also be used on bare arrays. +Instances are created in the same way:: -`~astropy.nddata.nddata.NDData` objects can also be easily converted to -numpy arrays:: + >>> from astropy.nddata import NDDataRef + >>> ndd = NDDataRef(ndd) + >>> ndd + NDDataRef([1, 2, —, —], unit='erg / s') + +But also support arithmetic (:ref:`nddata_arithmetic`) like addition:: + + >>> import astropy.units as u + >>> ndd2 = ndd.add([4, -3.5, 3, 2.5] * u.erg / u.s) + >>> ndd2 + NDDataRef([ 5. , -1.5, ———, ———], unit='erg / s') + +Because these operations have a wide range of options, these are not available +using arithmetic operators like ``+``. + +Slicing or indexing (:ref:`nddata_slicing`) is possible (with warnings issued if +some attribute cannot be sliced):: + + >>> ndd2[2:] # discard the first two elements # doctest: +FLOAT_CMP + NDDataRef([———, ———], unit='erg / s') + >>> ndd2[1] # get the second element # doctest: +FLOAT_CMP + NDDataRef(-1.5, unit='erg / s') + + +Working with Two-Dimensional Data Like Images +--------------------------------------------- + +Though the `~astropy.nddata` package supports any kind of gridded data, this +introduction will focus on the use of `~astropy.nddata` for two-dimensional +images. To get started, we will construct a two-dimensional image with a few +sources, some Gaussian noise, and a "cosmic ray" which we will later mask out. + +Examples +^^^^^^^^ + +.. + EXAMPLE START + Working with Two-Dimensional Data Using NDData + +First, construct a two-dimensional image with a few sources, some Gaussian +noise, and a "cosmic ray":: >>> import numpy as np - >>> arr = np.array(ndd) - >>> np.all(arr == mydataarray) - True + >>> from astropy.modeling.models import Gaussian2D + >>> rng = np.random.default_rng() + >>> y, x = np.mgrid[0:500, 0:600] + >>> data = (Gaussian2D(1, 150, 100, 20, 10, theta=0.5)(x, y) + + ... Gaussian2D(0.5, 400, 300, 8, 12, theta=1.2)(x,y) + + ... Gaussian2D(0.75, 250, 400, 5, 7, theta=0.23)(x,y) + + ... Gaussian2D(0.9, 525, 150, 3, 3)(x,y) + + ... Gaussian2D(0.6, 200, 225, 3, 3)(x,y)) + >>> data += 0.01 * rng.standard_normal((500, 600)) + >>> cosmic_ray_value = 0.997 + >>> data[100, 300:310] = cosmic_ray_value + +This image has a large "galaxy" in the lower left and the "cosmic ray" is the +horizontal line in the lower middle of the image: + +.. doctest-skip:: + + >>> import matplotlib.pyplot as plt + >>> fig, ax = plt.subplots() + >>> ax.imshow(data, origin='lower') + +.. plot:: + + import numpy as np + import matplotlib.pyplot as plt + from astropy.modeling.models import Gaussian2D + y, x = np.mgrid[0:500, 0:600] + data = (Gaussian2D(1, 150, 100, 20, 10, theta=0.5)(x, y) + + Gaussian2D(0.5, 400, 300, 8, 12, theta=1.2)(x,y) + + Gaussian2D(0.75, 250, 400, 5, 7, theta=0.23)(x,y) + + Gaussian2D(0.9, 525, 150, 3, 3)(x,y) + + Gaussian2D(0.6, 200, 225, 3, 3)(x,y)) + rng = np.random.default_rng(123456) + data += 0.01 * rng.standard_normal((500, 600)) + cosmic_ray_value = 0.997 + data[100, 300:310] = cosmic_ray_value + fig, ax = plt.subplots() + ax.imshow(data, origin='lower') + + +The "cosmic ray" can be masked out in this test image, like this:: + + >>> mask = (data == cosmic_ray_value) + +.. + EXAMPLE END + +`~astropy.nddata.CCDData` Class for Images +------------------------------------------ + +The `~astropy.nddata.CCDData` object, like the other objects in this package, +can store the data, a mask, and metadata. The `~astropy.nddata.CCDData` object +requires that a unit be specified:: + + >>> from astropy.nddata import CCDData + >>> ccd = CCDData(data, mask=mask, + ... meta={'object': 'fake galaxy', 'filter': 'R'}, + ... unit='adu') + +Slicing +------- + +Slicing works the way you would expect with the mask and, if present, +WCS, sliced appropriately:: + + >>> ccd2 = ccd[:200, :] + >>> ccd2.data.shape + (200, 600) + >>> ccd2.mask.shape + (200, 600) + >>> # Show the mask in a region around the cosmic ray: + >>> ccd2.mask[99:102, 299:311] + array([[False, False, False, False, False, False, False, False, False, + False, False, False], + [False, True, True, True, True, True, True, True, True, + True, True, False], + [False, False, False, False, False, False, False, False, False, + False, False, False]]...) + +For many applications it may be more convenient to use +`~astropy.nddata.Cutout2D`, described in `image_utilities`_. + +Image Arithmetic, Including Uncertainty +--------------------------------------- + +Methods are provided for basic arithmetic operations between images, including +propagation of uncertainties. Three uncertainty types are supported: variance +(`~astropy.nddata.VarianceUncertainty`), standard deviation +(`~astropy.nddata.StdDevUncertainty`), and inverse variance +(`~astropy.nddata.InverseVariance`). + +Examples +^^^^^^^^ + +.. + EXAMPLE START + Image Arithmetic Including Uncertainty in NDData + +This example creates an uncertainty that is Poisson error, stored as a +variance:: + + >>> from astropy.nddata import VarianceUncertainty + >>> poisson_noise = np.ma.sqrt(np.ma.abs(ccd.data)) + >>> ccd.uncertainty = VarianceUncertainty(poisson_noise ** 2) + +As a convenience, the uncertainty can also be set with a ``numpy`` array. In +that case, the uncertainty is assumed to be the standard deviation:: + + >>> ccd.uncertainty = poisson_noise + INFO: array provided for uncertainty; assuming it is a StdDevUncertainty. [astropy.nddata.ccddata] + +If we make a copy of the image and add that to the original, the uncertainty +changes as expected:: + + >>> ccd2 = ccd.copy() + >>> added_ccds = ccd.add(ccd2, handle_meta='first_found') + >>> added_ccds.uncertainty.array[0, 0] / ccd.uncertainty.array[0, 0] / np.sqrt(2) # doctest: +FLOAT_CMP + np.float64(0.99999999999999989) -If a `mask` is defined, this will result in a `~numpy.ma.MaskedArray`, so -in all cases a useable `numpy.ndarray` or subclass will result. This allows -straightforward plotting of `~astropy.nddata.nddata.NDData` objects with 1- -and 2-dimensional datasets using `matplotlib`:: +.. + EXAMPLE END - >>> from matplotlib import pyplot as plt - >>> plt.plot(ndd) +.. _nddata_reading_writing: -This works because the `matplotlib` plotting functions automatically convert -their inputs using `numpy.array`. +Reading and Writing +------------------- +A `~astropy.nddata.CCDData` can be saved to a FITS file:: -Using `nddata` -============== + >>> ccd.write('test_file.fits') + +And can also be read in from a FITS file:: + + >>> ccd2 = CCDData.read('test_file.fits') + +Note the unit is stored in the ``BUNIT`` keyword in the header on saving, and is +read from the header if it is present. + +Detailed help on the available keyword arguments for reading and writing +can be obtained via the ``help()`` method as follows: + +.. doctest-skip:: + + >>> CCDData.read.help('fits') # Get help on the CCDData FITS reader + >>> CCDData.writer.help('fits') # Get help on the CCDData FITS writer + +.. _image_utilities: + +Image Utilities +--------------- + +Cutouts +^^^^^^^ + +Though slicing directly is one way to extract a subframe, +`~astropy.nddata.Cutout2D` provides more convenient access to cutouts from the +data. + +Examples +~~~~~~~~ + +.. + EXAMPLE START + Accessing Cutouts in NDData + +This example pulls out the large "galaxy" in the lower left of the image, with +the center of the cutout at ``position``:: + + >>> from astropy.nddata import Cutout2D + >>> position = (149.7, 100.1) + >>> size = (81, 101) # pixels + >>> cutout = Cutout2D(ccd, position, size) + >>> fig, ax = plt.subplots() # doctest: +SKIP + >>> ax.imshow(cutout.data, origin='lower') # doctest: +SKIP + +.. plot:: + + import numpy as np + import matplotlib.pyplot as plt + from astropy.modeling.models import Gaussian2D + from astropy.nddata import CCDData + from astropy.nddata import Cutout2D + y, x = np.mgrid[0:500, 0:600] + data = (Gaussian2D(1, 150, 100, 20, 10, theta=0.5)(x, y) + + Gaussian2D(0.5, 400, 300, 8, 12, theta=1.2)(x,y) + + Gaussian2D(0.75, 250, 400, 5, 7, theta=0.23)(x,y) + + Gaussian2D(0.9, 525, 150, 3, 3)(x,y) + + Gaussian2D(0.6, 200, 225, 3, 3)(x,y)) + rng = np.random.default_rng(123456) + data += 0.01 * rng.standard_normal((500, 600)) + cosmic_ray_value = 0.997 + data[100, 300:310] = cosmic_ray_value + mask = (data == cosmic_ray_value) + ccd = CCDData(data, mask=mask, + meta={'object': 'fake galaxy', 'filter': 'R'}, + unit='adu') + position = (149.7, 100.1) + size = (81, 101) # pixels + cutout = Cutout2D(ccd, position, size) + fig, ax = plt.subplots() + ax.imshow(cutout.data, origin='lower') + +This cutout can also plot itself on the original image:: + + >>> plt.imshow(ccd, origin='lower') # doctest: +SKIP + >>> cutout.plot_on_original(color='white') # doctest: +SKIP + +.. plot:: + + import numpy as np + import matplotlib.pyplot as plt + from astropy.modeling.models import Gaussian2D + from astropy.nddata import CCDData, Cutout2D + y, x = np.mgrid[0:500, 0:600] + data = (Gaussian2D(1, 150, 100, 20, 10, theta=0.5)(x, y) + + Gaussian2D(0.5, 400, 300, 8, 12, theta=1.2)(x,y) + + Gaussian2D(0.75, 250, 400, 5, 7, theta=0.23)(x,y) + + Gaussian2D(0.9, 525, 150, 3, 3)(x,y) + + Gaussian2D(0.6, 200, 225, 3, 3)(x,y)) + rng = np.random.default_rng(123456) + data += 0.01 * rng.standard_normal((500, 600)) + cosmic_ray_value = 0.997 + data[100, 300:310] = cosmic_ray_value + mask = (data == cosmic_ray_value) + ccd = CCDData(data, mask=mask, + meta={'object': 'fake galaxy', 'filter': 'R'}, + unit='adu') + position = (149.7, 100.1) + size = (81, 101) # pixels + cutout = Cutout2D(ccd, position, size) + fig, ax = plt.subplots() + ax.imshow(ccd, origin='lower') + cutout.plot_on_original(color='white') + +The cutout also provides methods for finding pixel coordinates in the original +or in the cutout; recall that ``position`` is the center of the cutout in the +original image:: + + >>> position + (149.7, 100.1) + >>> cutout.to_cutout_position(position) # doctest: +FLOAT_CMP + (49.7, 40.099999999999994) + >>> cutout.to_original_position((49.7, 40.099999999999994)) # doctest: +FLOAT_CMP + (149.7, 100.1) + +For more details, including constructing a cutout from World Coordinates and +the options for handling cutouts that go beyond the bounds of the original +image, see :ref:`cutout_images`. + +.. + EXAMPLE END + +Image Resizing +^^^^^^^^^^^^^^ + +The functions `~astropy.nddata.block_reduce` and +`~astropy.nddata.block_replicate` resize images. + +Example +~~~~~~~ + +.. + EXAMPLE START + Image Resizing in NDData + +This example reduces the size of the image by a factor of 4. Note that the +result is a `numpy.ndarray`; the mask, metadata, etc. are discarded: + +.. doctest-requires:: skimage + + >>> from astropy.nddata import block_reduce, block_replicate + >>> smaller = block_reduce(ccd, 4) # doctest: +IGNORE_WARNINGS + >>> smaller + array(...) + >>> fig, ax = plt.subplots() + >>> ax.imshow(smaller, origin='lower') # doctest: +SKIP + +.. plot:: + + import numpy as np + import matplotlib.pyplot as plt + from astropy.modeling.models import Gaussian2D + from astropy.nddata import block_reduce, block_replicate + from astropy.nddata import CCDData, Cutout2D + y, x = np.mgrid[0:500, 0:600] + data = (Gaussian2D(1, 150, 100, 20, 10, theta=0.5)(x, y) + + Gaussian2D(0.5, 400, 300, 8, 12, theta=1.2)(x,y) + + Gaussian2D(0.75, 250, 400, 5, 7, theta=0.23)(x,y) + + Gaussian2D(0.9, 525, 150, 3, 3)(x,y) + + Gaussian2D(0.6, 200, 225, 3, 3)(x,y)) + rng = np.random.default_rng(123456) + data += 0.01 * rng.standard_normal((500, 600)) + cosmic_ray_value = 0.997 + data[100, 300:310] = cosmic_ray_value + mask = (data == cosmic_ray_value) + ccd = CCDData(data, mask=mask, + meta={'object': 'fake galaxy', 'filter': 'R'}, + unit='adu') + smaller = block_reduce(ccd.data, 4) + fig, ax = plt.subplots() + ax.imshow(smaller, origin='lower') + +By default, both `~astropy.nddata.block_reduce` and +`~astropy.nddata.block_replicate` conserve flux. + +.. + EXAMPLE END + +Other Image Classes +------------------- + +There are two less restrictive classes, `~astropy.nddata.NDDataArray` and +`~astropy.nddata.NDDataRef`, that can be used to hold image data. They are +primarily of interest to those who may want to create their own image class by +subclassing from one of the classes in the `~astropy.nddata` package. The main +differences between them are: + ++ `~astropy.nddata.NDDataRef` can be sliced and has methods for basic + arithmetic operations, but the user needs to use one of the uncertainty + classes to define an uncertainty. See :ref:`NDDataRef` for more detail. + Most of its properties must be set when the object is created because they + are not mutable. ++ `~astropy.nddata.NDDataArray` extends `~astropy.nddata.NDDataRef` by adding + the methods necessary for it to behave like a ``numpy`` array in expressions + and adds setters for several properties. It lacks the ability to + automatically recognize and read data from FITS files and does not attempt + to automatically set the WCS property. ++ `~astropy.nddata.CCDData` extends `~astropy.nddata.NDDataArray` by setting + up a default uncertainty class, setting up straightforward read/write to FITS + files, and automatically setting up a WCS property. + +More General Gridded Data Classes +--------------------------------- + +There are two generic classes in the ``nddata`` package that are of +interest primarily to users who either need a custom image class that goes +beyond the classes discussed so far, or who are working with gridded data that +is not an image. + ++ `~astropy.nddata.NDData` is a container class for holding general gridded + data. It includes a handful of basic attributes, but no slicing or arithmetic. + More information about this class is in :ref:`nddata_details`. ++ `~astropy.nddata.NDDataBase` is an abstract base class that developers of new + gridded data classes can subclass to declare that the new class follows the + `~astropy.nddata.NDData` interface. More details are in + :ref:`nddata_subclassing`. + +Additional Examples +=================== + +The list of packages below that use the ``nddata`` framework is intended to be +useful to either users writing their own image classes or those looking +for an image class that goes beyond what `~astropy.nddata.CCDData` does. + ++ The `SunPy project `_ uses `~astropy.nddata.NDData` as the + foundation for its + `Map classes `_. ++ The class `~astropy.nddata.NDDataRef` is used in + `specutils `_ as the basis for + `Spectrum1D `_, which adds several methods useful for + spectra. ++ The package `ndmapper `_, which + makes it easy to build reduction pipelines for optical data, uses + `~astropy.nddata.NDDataArray` as its image object. ++ The package `ccdproc `_ uses the + `~astropy.nddata.CCDData` class throughout for implementing optical/IR image + reduction. + +Using ``nddata`` +================ .. toctree:: :maxdepth: 2 - convolution.rst + ccddata.rst + utils.rst + bitmask.rst + decorator.rst + nddata.rst + covariance.rst + mixins/index.rst + subclassing.rst + +.. note that if this section gets too long, it should be moved to a separate + doc page - see the top of performance.inc.rst for the instructions on how to do + that +.. include:: performance.inc.rst Reference/API ============= -.. automodapi:: astropy.nddata - :no-inheritance-diagram: +.. toctree:: + :maxdepth: 2 + + ref_api + +.. _APE 7: https://github.com/astropy/astropy-APEs/blob/main/APE7.rst diff --git a/docs/nddata/mixins/index.rst b/docs/nddata/mixins/index.rst new file mode 100644 index 000000000000..1be69e869c32 --- /dev/null +++ b/docs/nddata/mixins/index.rst @@ -0,0 +1,9 @@ +Mixins for Added Functionality +****************************** + +.. toctree:: + :maxdepth: 2 + + ndslicing.rst + ndarithmetic.rst + ndio.rst diff --git a/docs/nddata/mixins/ndarithmetic.rst b/docs/nddata/mixins/ndarithmetic.rst new file mode 100644 index 000000000000..6c702b5f9bae --- /dev/null +++ b/docs/nddata/mixins/ndarithmetic.rst @@ -0,0 +1,412 @@ +.. _nddata_arithmetic: + +NDData Arithmetic +***************** + +Introduction +============ + +`~astropy.nddata.NDDataRef` implements the following arithmetic operations: + +- Addition: :meth:`~astropy.nddata.NDArithmeticMixin.add` +- Subtraction: :meth:`~astropy.nddata.NDArithmeticMixin.subtract` +- Multiplication: :meth:`~astropy.nddata.NDArithmeticMixin.multiply` +- Division: :meth:`~astropy.nddata.NDArithmeticMixin.divide` + +Using Basic Arithmetic Methods +============================== + +Using the standard arithmetic methods requires that the first operand +is an `~astropy.nddata.NDDataRef` instance: + + >>> from astropy.nddata import NDDataRef + >>> from astropy.wcs import WCS + >>> import numpy as np + >>> ndd1 = NDDataRef([1, 2, 3, 4]) + +While the requirement for the second operand is that it must be convertible +to the first operand. It can be a number:: + + >>> ndd1.add(3) + NDDataRef([4, 5, 6, 7]) + +Or a `list`:: + + >>> ndd1.subtract([1,1,1,1]) + NDDataRef([0, 1, 2, 3]) + +Or a `numpy.ndarray`:: + + >>> ndd1.multiply(np.arange(4, 8)) + NDDataRef([ 4, 10, 18, 28]) + >>> ndd1.divide(np.arange(1,13).reshape(3,4)) # a 3 x 4 numpy array # doctest: +FLOAT_CMP + NDDataRef([[1. , 1. , 1. , 1. ], + [0.2 , 0.33333333, 0.42857143, 0.5 ], + [0.11111111, 0.2 , 0.27272727, 0.33333333]]) + +Here, broadcasting takes care of the different dimensions. Several other +types of operands are also accepted. + +Using Arithmetic Classmethods +============================= + +Here both operands do not need to be `~astropy.nddata.NDDataRef`-like:: + + >>> NDDataRef.add(1, 3) + NDDataRef(4) + +To wrap the result of an arithmetic operation between two Quantities:: + + >>> import astropy.units as u + >>> ndd = NDDataRef.multiply([1,2] * u.m, [10, 20] * u.cm) + >>> ndd # doctest: +FLOAT_CMP + NDDataRef([10., 40.], unit='cm m') + >>> ndd.unit + Unit("cm m") + +Or take the inverse of an `~astropy.nddata.NDDataRef` object:: + + >>> NDDataRef.divide(1, ndd1) # doctest: +FLOAT_CMP + NDDataRef([1. , 0.5 , 0.33333333, 0.25 ]) + + +Possible Operands +----------------- + +The possible types of input for operands are: + ++ Scalars of any type ++ Lists containing numbers (or nested lists) ++ ``numpy`` arrays ++ ``numpy`` masked arrays ++ ``astropy`` quantities ++ Other ``nddata`` classes or subclasses + +Advanced Options +================ + +The normal Python operators ``+``, ``-``, etc. are not implemented because +the methods provide several options on how to proceed with the additional +attributes. + +Data and Unit +------------- + +For ``data`` and ``unit`` there are no parameters. Every arithmetic +operation lets the `astropy.units.Quantity`-framework evaluate the result +or fail and abort the operation. + +Adding two `~astropy.nddata.NDData` objects with the same unit works:: + + >>> ndd1 = NDDataRef([1,2,3,4,5], unit='m') + >>> ndd2 = NDDataRef([100,150,200,50,500], unit='m') + + >>> ndd = ndd1.add(ndd2) + >>> ndd.data # doctest: +FLOAT_CMP + array([101, 152, 203, 54, 505]) + >>> ndd.unit + Unit("m") + +Adding two `~astropy.nddata.NDData` objects with compatible units also works:: + + >>> ndd1 = NDDataRef(ndd1, unit='pc') + INFO: overwriting NDData's current unit with specified unit. [astropy.nddata.nddata] + >>> ndd2 = NDDataRef(ndd2, unit='lyr') + INFO: overwriting NDData's current unit with specified unit. [astropy.nddata.nddata] + + >>> ndd = ndd1.subtract(ndd2) + >>> ndd.data # doctest: +FLOAT_CMP + array([ -29.66013938, -43.99020907, -58.32027876, -11.33006969, + -148.30069689]) + >>> ndd.unit + Unit("pc") + +This will keep by default the unit of the first operand. However, units will +not be decomposed during division:: + + >>> ndd = ndd2.divide(ndd1) + >>> ndd.data # doctest: +FLOAT_CMP + array([100. , 75. , 66.66666667, 12.5 , 100. ]) + >>> ndd.unit + Unit("lyr / pc") + +Mask +---- + +The ``handle_mask`` parameter for the arithmetic operations implements what the +resulting mask will be. There are several options. + +- ``None``, the result will have no ``mask``:: + + >>> ndd1 = NDDataRef(1, mask=True) + >>> ndd2 = NDDataRef(1, mask=False) + >>> ndd1.add(ndd2, handle_mask=None).mask is None + True + +- ``"first_found"`` or ``"ff"``, the result will have the ``mask`` of the first + operand or if that is ``None``, the ``mask`` of the second operand:: + + >>> ndd1 = NDDataRef(1, mask=True) + >>> ndd2 = NDDataRef(1, mask=False) + >>> ndd1.add(ndd2, handle_mask="first_found").mask + True + >>> ndd3 = NDDataRef(1) + >>> ndd3.add(ndd2, handle_mask="first_found").mask + False + +- A function (or an arbitrary callable) that takes at least two arguments. + For example, `numpy.logical_or` is the default:: + + >>> ndd1 = NDDataRef(1, mask=np.array([True, False, True, False])) + >>> ndd2 = NDDataRef(1, mask=np.array([True, False, False, True])) + >>> ndd1.add(ndd2).mask + array([ True, False, True, True]...) + + This defaults to ``"first_found"`` in case only one ``mask`` is not None:: + + >>> ndd1 = NDDataRef(1) + >>> ndd2 = NDDataRef(1, mask=np.array([True, False, False, True])) + >>> ndd1.add(ndd2).mask + array([ True, False, False, True]...) + + Custom functions are also possible:: + + >>> def take_alternating_values(mask1, mask2, start=0): + ... result = np.zeros(mask1.shape, dtype=np.bool_) + ... result[start::2] = mask1[start::2] + ... result[start+1::2] = mask2[start+1::2] + ... return result + + This function is nonsense, but we can still see how it performs:: + + >>> ndd1 = NDDataRef(1, mask=np.array([True, False, True, False])) + >>> ndd2 = NDDataRef(1, mask=np.array([True, False, False, True])) + >>> ndd1.add(ndd2, handle_mask=take_alternating_values).mask + array([ True, False, True, True]...) + + Additional parameters can be given by prefixing them with ``mask_`` + (which will be stripped before passing it to the function):: + + >>> ndd1.add(ndd2, handle_mask=take_alternating_values, mask_start=1).mask + array([False, False, False, False]...) + >>> ndd1.add(ndd2, handle_mask=take_alternating_values, mask_start=2).mask + array([False, False, True, True]...) + +Meta +---- + +The ``handle_meta`` parameter for the arithmetic operations implements what the +resulting ``meta`` will be. The options are the same as for the ``mask``: + +- If ``None`` the resulting ``meta`` will be an empty `collections.OrderedDict`. + + >>> ndd1 = NDDataRef(1, meta={'object': 'sun'}) + >>> ndd2 = NDDataRef(1, meta={'object': 'moon'}) + >>> ndd1.add(ndd2, handle_meta=None).meta + OrderedDict() + + For ``meta`` this is the default so you do not need to pass it in this case:: + + >>> ndd1.add(ndd2).meta + OrderedDict() + +- If ``"first_found"`` or ``"ff"``, the resulting ``meta`` will be the ``meta`` + of the first operand or if that contains no keys, the ``meta`` of the second + operand is taken. + + >>> ndd1 = NDDataRef(1, meta={'object': 'sun'}) + >>> ndd2 = NDDataRef(1, meta={'object': 'moon'}) + >>> ndd1.add(ndd2, handle_meta='ff').meta + {'object': 'sun'} + +- If it is a ``callable`` it must take at least two arguments. Both ``meta`` + attributes will be passed to this function (even if one or both of them are + empty) and the callable evaluates the result's ``meta``. For example, a + function that merges these two:: + + >>> # It's expected with arithmetic that the result is not a reference, + >>> # so we need to copy + >>> from copy import deepcopy + + >>> def combine_meta(meta1, meta2): + ... if not meta1: + ... return deepcopy(meta2) + ... elif not meta2: + ... return deepcopy(meta1) + ... else: + ... meta_final = deepcopy(meta1) + ... meta_final.update(meta2) + ... return meta_final + + >>> ndd1 = NDDataRef(1, meta={'time': 'today'}) + >>> ndd2 = NDDataRef(1, meta={'object': 'moon'}) + >>> ndd1.subtract(ndd2, handle_meta=combine_meta).meta # doctest: +SKIP + {'object': 'moon', 'time': 'today'} + + Here again additional arguments for the function can be passed in using + the prefix ``meta_`` (which will be stripped away before passing it to this + function). See the description for the mask-attribute for further details. + +World Coordinate System (WCS) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The ``compare_wcs`` argument will determine what the result's ``wcs`` will be +or if the operation should be forbidden. The possible values are identical to +``mask`` and ``meta``: + +- If ``None`` the resulting ``wcs`` will be an empty ``None``. + + >>> ndd1 = NDDataRef(1, wcs=None) + >>> ndd2 = NDDataRef(1, wcs=WCS()) + >>> ndd1.add(ndd2, compare_wcs=None).wcs is None + True + +- If ``"first_found"`` or ``"ff"`` the resulting ``wcs`` will be the ``wcs`` of + the first operand or if that is ``None``, the ``meta`` of the second operand + is taken. + + >>> wcs = WCS() + >>> ndd1 = NDDataRef(1, wcs=wcs) + >>> ndd2 = NDDataRef(1, wcs=None) + >>> str(ndd1.add(ndd2, compare_wcs='ff').wcs) == str(wcs) + True + +- If it is a ``callable`` it must take at least two arguments. Both ``wcs`` + attributes will be passed to this function (even if one or both of them are + ``None``) and the callable should return ``True`` if these ``wcs`` are + identical (enough) to allow the arithmetic operation or ``False`` if the + arithmetic operation should be aborted with a ``ValueError``. If ``True`` the + ``wcs`` are identical and the first one is used for the result:: + + >>> def compare_wcs_scalar(wcs1, wcs2, allowed_deviation=0.1): + ... if wcs1 is None and wcs2 is None: + ... return True # both have no WCS so they are identical + ... if wcs1 is None or wcs2 is None: + ... return False # one has WCS, the other doesn't not possible + ... else: + ... # Consider wcs close if centers are close enough + ... return all(abs(wcs1.wcs.crpix - wcs2.wcs.crpix) < allowed_deviation) + + >>> ndd1 = NDDataRef(1, wcs=None) + >>> ndd2 = NDDataRef(1, wcs=None) + >>> ndd1.subtract(ndd2, compare_wcs=compare_wcs_scalar).wcs + + + Additional arguments can be passed in prefixing them with ``wcs_`` (this + prefix will be stripped away before passing it to the function):: + + >>> ndd1 = NDDataRef(1, wcs=WCS()) + >>> ndd1.wcs.wcs.crpix = [1, 1] + >>> ndd2 = NDDataRef(1, wcs=WCS()) + >>> ndd1.subtract(ndd2, compare_wcs=compare_wcs_scalar, wcs_allowed_deviation=2).wcs.wcs.crpix + array([1., 1.]) + + If you are using `~astropy.wcs.WCS` objects, a very handy function to use + might be:: + + >>> def wcs_compare(wcs1, wcs2, *args, **kwargs): + ... return wcs1.wcs.compare(wcs2.wcs, *args, **kwargs) + + See :meth:`astropy.wcs.Wcsprm.compare` for the arguments this comparison + allows. + +Uncertainty +----------- + +The ``propagate_uncertainties`` argument can be used to turn the propagation +of uncertainties on or off. + +- If ``None`` the result will have no uncertainty:: + + >>> from astropy.nddata import StdDevUncertainty + >>> ndd1 = NDDataRef(1, uncertainty=StdDevUncertainty(0)) + >>> ndd2 = NDDataRef(1, uncertainty=StdDevUncertainty(1)) + >>> ndd1.add(ndd2, propagate_uncertainties=None).uncertainty is None + True + +- If ``False`` the result will have the first found uncertainty. + + .. note:: + Setting ``propagate_uncertainties=False`` is generally not + recommended. + +- If ``True`` both uncertainties must be ``NDUncertainty`` subclasses that + implement propagation. This is possible for + `~astropy.nddata.StdDevUncertainty`:: + + >>> ndd1 = NDDataRef(1, uncertainty=StdDevUncertainty([10])) + >>> ndd2 = NDDataRef(1, uncertainty=StdDevUncertainty([10])) + >>> ndd1.add(ndd2, propagate_uncertainties=True).uncertainty # doctest: +FLOAT_CMP + StdDevUncertainty([14.14213562]) + +Uncertainty with Correlation +---------------------------- + +If ``propagate_uncertainties`` is ``True`` you can also give an argument +for ``uncertainty_correlation``. `~astropy.nddata.StdDevUncertainty` cannot +keep track of its correlations by itself, but it can evaluate the correct +resulting uncertainty if the correct ``correlation`` is given. + +The default (``0``) represents uncorrelated while ``1`` means correlated and +``-1`` anti-correlated. If given a `numpy.ndarray` it should represent the +element-wise correlation coefficient. + +Examples +^^^^^^^^ + +.. + EXAMPLE START + Uncertainty with Correlation in NDData + +Without correlation, subtracting an `~astropy.nddata.NDDataRef` instance from +itself results in a non-zero uncertainty:: + + >>> ndd1 = NDDataRef(1, uncertainty=StdDevUncertainty([10])) + >>> ndd1.subtract(ndd1, propagate_uncertainties=True).uncertainty # doctest: +FLOAT_CMP + StdDevUncertainty([14.14213562]) + +Given a correlation of ``1`` (because they clearly correlate) gives the +correct uncertainty of ``0``:: + + >>> ndd1 = NDDataRef(1, uncertainty=StdDevUncertainty([10])) + >>> ndd1.subtract(ndd1, propagate_uncertainties=True, + ... uncertainty_correlation=1).uncertainty # doctest: +FLOAT_CMP + StdDevUncertainty([0.]) + +Which would be consistent with the equivalent operation ``ndd1 * 0``:: + + >>> ndd1.multiply(0, propagate_uncertainties=True).uncertainty # doctest: +FLOAT_CMP + StdDevUncertainty([0.]) + +.. warning:: + The user needs to calculate or know the appropriate value or array manually + and pass it to ``uncertainty_correlation``. The implementation follows + general first order error propagation formulas. See, for example: + `Wikipedia `_. + +You can also give element-wise correlations:: + + >>> ndd1 = NDDataRef([1,1,1,1], uncertainty=StdDevUncertainty([1,1,1,1])) + >>> ndd2 = NDDataRef([2,2,2,2], uncertainty=StdDevUncertainty([2,2,2,2])) + >>> ndd1.add(ndd2,uncertainty_correlation=np.array([1,0.5,0,-1])).uncertainty # doctest: +FLOAT_CMP + StdDevUncertainty([3. , 2.64575131, 2.23606798, 1. ]) + +The correlation ``np.array([1, 0.5, 0, -1])`` would indicate that the first +element is fully correlated and the second element partially correlates, while +the third element is uncorrelated, and the fourth is anti-correlated. + +.. + EXAMPLE END + +Uncertainty with Unit +--------------------- + +`~astropy.nddata.StdDevUncertainty` implements correct error propagation even +if the unit of the data differs from the unit of the uncertainty:: + + >>> ndd1 = NDDataRef([10], unit='m', uncertainty=StdDevUncertainty([10], unit='cm')) + >>> ndd2 = NDDataRef([20], unit='m', uncertainty=StdDevUncertainty([10])) + >>> ndd1.subtract(ndd2, propagate_uncertainties=True).uncertainty # doctest: +FLOAT_CMP + StdDevUncertainty([10.00049999]) + +But it needs to be convertible to the unit for the data. diff --git a/docs/nddata/mixins/ndio.rst b/docs/nddata/mixins/ndio.rst new file mode 100644 index 000000000000..d4ae61204d34 --- /dev/null +++ b/docs/nddata/mixins/ndio.rst @@ -0,0 +1,13 @@ +.. _nddata_io: + +I/O Mixin +********* + +The I/O mixin, `~astropy.nddata.NDIOMixin`, adds ``read`` and ``write`` +methods that use the ``astropy`` I/O registry. + +The mixin itself creates the read/write methods; it does not register +any readers or writers with the I/O registry. Subclasses of +`~astropy.nddata.NDDataBase` or `~astropy.nddata.NDData` need to include this +mixin, implement a reader and writer, *and* register it with the I/O +framework. See :ref:`io_registry` for details. diff --git a/docs/nddata/mixins/ndslicing.rst b/docs/nddata/mixins/ndslicing.rst new file mode 100644 index 000000000000..c7b4ee98becf --- /dev/null +++ b/docs/nddata/mixins/ndslicing.rst @@ -0,0 +1,162 @@ +.. _nddata_slicing: + +Slicing and Indexing NDData +*************************** + +Introduction +============ + +This page only deals with peculiarities that apply to +`~astropy.nddata.NDData`-like classes. For a tutorial about slicing/indexing see the +`python documentation `_ +and `numpy documentation `_. + +.. warning:: + `~astropy.nddata.NDData` and `~astropy.nddata.NDDataRef` enforce almost no + restrictions on the properties, so it might happen that some **valid but + unusual** combinations of properties always result in an IndexError or + incorrect results. In this case, see :ref:`nddata_subclassing` on how to + customize slicing for a particular property. + + +Slicing NDDataRef +================= + +Unlike `~astropy.nddata.NDData` the class `~astropy.nddata.NDDataRef` +implements slicing or indexing. The result will be wrapped inside the same +class as the sliced object. + +Getting one element:: + + >>> import numpy as np + >>> from astropy.nddata import NDDataRef + + >>> data = np.array([1, 2, 3, 4]) + >>> ndd = NDDataRef(data) + >>> ndd[1] + NDDataRef(2) + +Getting a sliced portion of the original:: + + >>> ndd[1:3] # Get element 1 (inclusive) to 3 (exclusive) + NDDataRef([2, 3]) + +This will return a reference (and as such **not a copy**) of the original +properties, so changing a slice will affect the original:: + + >>> ndd_sliced = ndd[1:3] + >>> ndd_sliced.data[0] = 5 + >>> ndd_sliced + NDDataRef([5, 3]) + >>> ndd + NDDataRef([1, 5, 3, 4]) + +But only the one element that was indexed is affected (for example, +``ndd_sliced = ndd[1]``). The element is a scalar and changes will not +propagate to the original. + +Slicing NDDataRef Including Attributes +====================================== + +In the case that a ``mask``, or ``uncertainty`` is present, this +attribute will be sliced too:: + + >>> from astropy.nddata import StdDevUncertainty + >>> data = np.array([1, 2, 3, 4]) + >>> mask = data > 2 + >>> uncertainty = StdDevUncertainty(np.sqrt(data)) + >>> ndd = NDDataRef(data, mask=mask, uncertainty=uncertainty) + >>> ndd_sliced = ndd[1:3] + + >>> ndd_sliced.data + array([2, 3]) + + >>> ndd_sliced.mask + array([False, True]...) + + >>> ndd_sliced.uncertainty # doctest: +FLOAT_CMP + StdDevUncertainty([1.41421356, 1.73205081]) + +``unit`` and ``meta``, however, will be unaffected. + +If any of the attributes are set but do not implement slicing, an info will be +printed and the property will be kept as is:: + + >>> data = np.array([1, 2, 3, 4]) + >>> mask = False + >>> uncertainty = StdDevUncertainty(0) + >>> ndd = NDDataRef(data, mask=mask, uncertainty=uncertainty) + >>> ndd_sliced = ndd[1:3] + INFO: uncertainty cannot be sliced. [astropy.nddata.mixins.ndslicing] + INFO: mask cannot be sliced. [astropy.nddata.mixins.ndslicing] + + >>> ndd_sliced.mask + False + + +Slicing NDData with World Coordinates +------------------------------------- + +If ``wcs`` is set, it must be either implement +`~astropy.wcs.wcsapi.BaseLowLevelWCS` or `~astropy.wcs.wcsapi.BaseHighLevelWCS`. +This means that only integer or range slices without a step are supported. So +slices like ``[::10]`` or array or boolean based slices will not work. + +If you want to slice an ``NDData`` object called ``ndd`` without the WCS you can remove the +WCS from the ``NDData`` object by running: + + >>> ndd.wcs = None + + +Removing Masked Data +-------------------- + +.. warning:: + If ``wcs`` is set this will **NOT** be possible. But you can work around + this by setting the wcs attribute to `None` with ``ndd.wcs = None`` before slicing. + +By convention, the ``mask`` attribute indicates if a point is valid or invalid. +So we are able to get all valid data points by slicing with the mask. + +Examples +^^^^^^^^ + +.. + EXAMPLE START + Removing Masked Data in NDDataRef + +To get all of the valid data points by slicing with the mask:: + + >>> data = np.array([[1,2,3],[4,5,6],[7,8,9]]) + >>> mask = np.array([[0,1,0],[1,1,1],[0,0,1]], dtype=bool) + >>> uncertainty = StdDevUncertainty(np.sqrt(data)) + >>> ndd = NDDataRef(data, mask=mask, uncertainty=uncertainty) + >>> # don't forget that ~ or you'll get the invalid points + >>> ndd_sliced = ndd[~ndd.mask] + >>> ndd_sliced + NDDataRef([1, 3, 7, 8]) + + >>> ndd_sliced.mask + array([False, False, False, False]...) + + >>> ndd_sliced.uncertainty # doctest: +FLOAT_CMP + StdDevUncertainty([1. , 1.73205081, 2.64575131, 2.82842712]) + +Or all invalid points:: + + >>> ndd_sliced = ndd[ndd.mask] # without the ~ now! + >>> ndd_sliced + NDDataRef([—, —, —, —, —]) + + >>> ndd_sliced.mask + array([ True, True, True, True, True]...) + + >>> ndd_sliced.uncertainty # doctest: +FLOAT_CMP + StdDevUncertainty([1.41421356, 2. , 2.23606798, 2.44948974, 3. ]) + +.. note:: + The result of this kind of indexing (boolean indexing) will always be + one-dimensional! + +.. + EXAMPLE END diff --git a/docs/nddata/nddata.rst b/docs/nddata/nddata.rst new file mode 100644 index 000000000000..2bde1261d9b9 --- /dev/null +++ b/docs/nddata/nddata.rst @@ -0,0 +1,528 @@ +.. _nddata_details: + +NDData +****** + +Overview +======== + +:class:`~astropy.nddata.NDData` is based on `numpy.ndarray`-like ``data`` with +additional meta attributes: + ++ ``meta`` for general metadata ++ ``unit`` represents the physical unit of the data ++ ``uncertainty`` for the uncertainty of the data ++ ``mask`` indicates invalid points in the data ++ ``wcs`` represents the relationship between the data grid and world + coordinates ++ ``psf`` holds an image representation of the point spread function (PSF) + +Each of these attributes can be set during initialization or directly on the +instance. Only the ``data`` cannot be directly set after creating the instance. + +Data +==== + +The data is the base of `~astropy.nddata.NDData` and is required to be +`numpy.ndarray`-like. It is the only property that is required to create an +instance and it cannot be directly set on the instance. + +Example +------- + +.. + EXAMPLE START + Creating Instances with NumPy NDarray-like Data + +To create an instance:: + + >>> import numpy as np + >>> from astropy.nddata import NDData + >>> array = np.array([[0, 1, 0], [1, 0, 1], [0, 1, 0]]) + >>> ndd = NDData(array) + >>> ndd + NDData([[0, 1, 0], + [1, 0, 1], + [0, 1, 0]]) + +And access by the ``data`` attribute:: + + >>> ndd.data + array([[0, 1, 0], + [1, 0, 1], + [0, 1, 0]]) + +As already mentioned, it is not possible to set the data directly. So +``ndd.data = np.arange(9)`` will raise an exception. But the data can be +modified in place:: + + >>> ndd.data[1,1] = 100 + >>> ndd.data + array([[ 0, 1, 0], + [ 1, 100, 1], + [ 0, 1, 0]]) + +.. + EXAMPLE END + +Data During Initialization +-------------------------- + +During initialization it is possible to provide data that is not a +`numpy.ndarray` but convertible to one. + +Examples +^^^^^^^^ + +.. + EXAMPLE START + Data Convertible to a NumPy NDarray During Initialization + +To provide data that is convertible to a `numpy.ndarray`, you can pass a `list` +containing numerical values:: + + >>> alist = [1, 2, 3, 4] + >>> ndd = NDData(alist) + >>> ndd.data # data will be a numpy-array: + array([1, 2, 3, 4]) + +A nested `list` or `tuple` is possible, but if these contain non-numerical +values the conversion might fail. + +Besides input that is convertible to such an array, you can also use the +``data`` parameter to pass implicit additional information. For example, if the +data is another `~astropy.nddata.NDData` object it implicitly uses its +properties:: + + >>> ndd = NDData(ndd, unit = 'm') + >>> ndd2 = NDData(ndd) + >>> ndd2.data # It has the same data as ndd + array([1, 2, 3, 4]) + >>> ndd2.unit # but it also has the same unit as ndd + Unit("m") + +Another possibility is to use a `~astropy.units.Quantity` as a ``data`` +parameter:: + + >>> import astropy.units as u + >>> quantity = np.ones(3) * u.cm # this will create a Quantity + >>> ndd3 = NDData(quantity) + >>> ndd3.data # doctest: +FLOAT_CMP + array([1., 1., 1.]) + >>> ndd3.unit + Unit("cm") + +Or a `numpy.ma.MaskedArray`:: + + >>> masked_array = np.ma.array([5,10,15], mask=[False, True, False]) + >>> ndd4 = NDData(masked_array) + >>> ndd4.data + array([ 5, 10, 15]) + >>> ndd4.mask + array([False, True, False]...) + +If such an implicitly passed property conflicts with an explicit parameter, the +explicit parameter will be used and an info message will be issued:: + + >>> quantity = np.ones(3) * u.cm + >>> ndd6 = NDData(quantity, unit='m') + INFO: overwriting Quantity's current unit with specified unit. [astropy.nddata.nddata] + >>> ndd6.data # doctest: +FLOAT_CMP + array([0.01, 0.01, 0.01]) + >>> ndd6.unit + Unit("m") + +The unit of the `~astropy.units.Quantity` is being ignored and the unit is set +to the explicitly passed one. + +It might be possible to pass other classes as a ``data`` parameter as long as +they have the properties ``shape``, ``dtype``, ``__getitem__``, and +``__array__``. + +The purpose of this mechanism is to allow considerable flexibility in the +objects used to store the data while providing a useful default (``numpy`` +array). + +.. + EXAMPLE END + +Mask +==== + +The ``mask`` is being used to indicate if data points are valid or invalid. +`~astropy.nddata.NDData` does not restrict this mask in any way but it is +expected to follow the `numpy.ma.MaskedArray` convention in that the mask: + ++ Returns ``True`` for data points that are considered **invalid**. ++ Returns ``False`` for those points that are **valid**. + +Examples +-------- + +.. + EXAMPLE START + Masks Used to Indicate Valid or Invalid Data Points in NDData + +One possibility is to create a mask by using ``numpy``'s comparison operators:: + + >>> array = np.array([0, 1, 4, 0, 2]) + + >>> mask = array == 0 # Mask points containing 0 + >>> mask + array([ True, False, False, True, False]...) + + >>> other_mask = array > 1 # Mask points with a value greater than 1 + >>> other_mask + array([False, False, True, False, True]...) + +And initialize the `~astropy.nddata.NDData` instance using the ``mask`` +parameter:: + + >>> ndd = NDData(array, mask=mask) + >>> ndd.mask + array([ True, False, False, True, False]...) + +Or by replacing the mask:: + + >>> ndd.mask = other_mask + >>> ndd.mask + array([False, False, True, False, True]...) + +There is no requirement that the mask actually be a ``numpy`` array; for +example, a function which evaluates a mask value as needed is acceptable as +long as it follows the convention that ``True`` indicates a value that should +be ignored. + +.. + EXAMPLE END + +Unit +==== + +The ``unit`` represents the unit of the data values. It is required to be +`~astropy.units.Unit`-like or a string that can be converted to such a +`~astropy.units.Unit`:: + + >>> import astropy.units as u + >>> ndd = NDData([1, 2, 3, 4], unit="meter") # using a string + >>> ndd.unit + Unit("m") + +..note:: + Setting the ``unit`` on an instance is not possible. + +Uncertainties +============= + +The ``uncertainty`` represents an arbitrary representation of the error of the +data values. To indicate which kind of uncertainty representation is used, the +``uncertainty`` should have an ``uncertainty_type`` property. If no such +property is found it will be wrapped inside a +`~astropy.nddata.UnknownUncertainty`. + +The ``uncertainty_type`` should follow the `~astropy.nddata.StdDevUncertainty` +convention in that it returns a short string like ``"std"`` for an uncertainty +given in standard deviation. Other examples are +`~astropy.nddata.VarianceUncertainty` and `~astropy.nddata.InverseVariance`. + +Examples +-------- + +.. + EXAMPLE START + Setting Uncertainties During Initialization in NDData + +Like the other properties the ``uncertainty`` can be set during +initialization:: + + >>> from astropy.nddata import StdDevUncertainty, InverseVariance + >>> array = np.array([10, 7, 12, 22]) + >>> uncert = StdDevUncertainty(np.sqrt(array)) + >>> ndd = NDData(array, uncertainty=uncert) + >>> ndd.uncertainty # doctest: +FLOAT_CMP + StdDevUncertainty([3.16227766, 2.64575131, 3.46410162, 4.69041576]) + +Or on the instance directly:: + + >>> other_uncert = StdDevUncertainty([2,2,2,2]) + >>> ndd.uncertainty = other_uncert + >>> ndd.uncertainty + StdDevUncertainty([2, 2, 2, 2]) + +But it will print an info message if there is no ``uncertainty_type``:: + + >>> ndd.uncertainty = np.array([5, 1, 2, 10]) + INFO: uncertainty should have attribute uncertainty_type. [astropy.nddata.nddata] + >>> ndd.uncertainty + UnknownUncertainty([ 5, 1, 2, 10]) + +It is also possible to convert between uncertainty types:: + + >>> uncert.represent_as(InverseVariance) + InverseVariance([0.1 , 0.14285714, 0.08333333, 0.04545455]) + +.. + EXAMPLE END + +Covariance +---------- + +A `~astropy.nddata.Covariance` uncertainty type is also implemented; however, +its functionality is generally limited to construction and storage of sparse +covariance matrices. Additional functionality will be implemented as requested. +See :ref:`nddata-covariance` for more description and example usage. + +WCS +=== + +The ``wcs`` should contain a mapping from the gridded data to world +coordinates. There are no restrictions placed on the property currently but it +may be restricted to an `~astropy.wcs.WCS` object or a more generalized WCS +object in the future. + +.. note:: + Like the unit the ``wcs`` cannot be set on an instance. + +Metadata +========= + +The ``meta`` property contains all further meta information that does not fit +any other property. + +Examples +-------- + +.. + EXAMPLE START + Metadata in NDData + +If the ``meta`` property is given it must be `dict`-like:: + + >>> ndd = NDData([1,2,3], meta={'observer': 'myself'}) + >>> ndd.meta + {'observer': 'myself'} + +`dict`-like means it must be a mapping from some keys to some values. This +also includes `~astropy.io.fits.Header` objects:: + + >>> from astropy.io import fits + >>> header = fits.Header() + >>> header['observer'] = 'Edwin Hubble' + >>> ndd = NDData(np.zeros([10, 10]), meta=header) + >>> ndd.meta['observer'] + 'Edwin Hubble' + +If the ``meta`` property is not provided or explicitly set to ``None``, it will +default to an empty `collections.OrderedDict`:: + + >>> ndd.meta = None + >>> ndd.meta + OrderedDict() + + >>> ndd = NDData([1,2,3]) + >>> ndd.meta + OrderedDict() + +The ``meta`` object therefore supports adding or updating these values:: + + >>> ndd.meta['exposure_time'] = 340. + >>> ndd.meta['filter'] = 'J' + +Elements of the metadata dictionary can be set to any valid Python object:: + + >>> ndd.meta['history'] = ['calibrated', 'aligned', 'flat-fielded'] + +.. + EXAMPLE END + +Initialization with Copy +======================== + +The default way to create an `~astropy.nddata.NDData` instance is to try saving +the parameters as references to the original rather than as copy. Sometimes +this is not possible because the internal mechanics do not allow for this. + +Examples +-------- + +.. + EXAMPLE START + Creating an NDData Instance with Copy + +If the ``data`` is a `list` then during initialization this is copied +while converting to a `~numpy.ndarray`. But it is also possible to enforce +copies during initialization by setting the ``copy`` parameter to ``True``:: + + >>> array = np.array([1, 2, 3, 4]) + >>> ndd = NDData(array) + >>> ndd.data[2] = 10 + >>> array[2] # Original array has changed + np.int64(10) + + >>> ndd2 = NDData(array, copy=True) + >>> ndd2.data[2] = 3 + >>> array[2] # Original array hasn't changed. + np.int64(10) + +.. note:: + In some cases setting ``copy=True`` will copy the ``data`` twice. Known + cases are if the ``data`` is a `list` or `tuple`. + +.. + EXAMPLE END + + +Collapsing an NDData object along one or more axes +================================================== + +.. + EXAMPLE START + Collapsing an NDData object along one or more axes + +A common operation on an `~numpy.ndarray` is to take the sum, mean, +maximum, or minimum along one or more axes, reducing the dimensions +of the output. These four operations are implemented on +`~astropy.nddata.NDData` with appropriate propagation of uncertainties, +masks, and units. + +For example, let's work on the following ``data`` with a mask, unit, and +(uniform) uncertainty:: + + >>> import numpy as np + >>> import astropy.units as u + >>> from astropy.nddata import NDDataArray, StdDevUncertainty + >>> + >>> data = [ + ... [1, 2, 3], + ... [2, 3, 4] + ... ] + >>> mask = [ + ... [True, False, False], + ... [False, False, False] + ... ] + >>> uncertainty = StdDevUncertainty(np.ones_like(data)) + >>> nddata = NDDataArray(data=data, uncertainty=uncertainty, mask=mask, unit='m') + +The sum along axis ``1`` gives one result per row:: + + >>> sum_axis_1 = nddata.sum(axis=1) # this is a new NDDataArray + >>> print(np.asanyarray(sum_axis_1)) # this converts data to a numpy masked array. doctest: +FLOAT_CMP + [-- 9.0] + >>> print(sum_axis_1.uncertainty) # doctest: +FLOAT_CMP + StdDevUncertainty([1.41421356, 1.73205081]) + +The result has one masked value derived from the logical OR of the original mask +along ``axis=1``. The uncertainties are the square-root of the sum of the squares +of the input uncertainties. Since the original uncertainties were all unity, the +result is the square root of the number of unmasked data entries, +:math:`[\sqrt{2},\,\sqrt{3}]`. + +We can similarly take the mean along ``axis=1``:: + + >>> mean_axis_1 = nddata.mean(axis=1) + >>> print(np.asanyarray(mean_axis_1)) # doctest: +FLOAT_CMP + [2.5 3.0] + >>> print(mean_axis_1.uncertainty) # doctest: +FLOAT_CMP + StdDevUncertainty([0.70710678, 0.57735027]) + +The result is the mean of the values where ``mask==False``, and in this example, +the result would only have ``mask==True`` if an entire row was masked. Since the +uncertainties were given as `~astropy.nddata.StdDevUncertainty`, the propagated +uncertainties decrease proportional to the number of unmasked measurements in each +row, following :math:`[2^{-1/2},\,3^{-1/2}]`. + +There's no single, correct way of defining the uncertainties associated +with the ``min`` or ``max`` of a set of measurements, so +`~astropy.nddata.NDData` resists the temptation to guess, and returns +the minimum data value along the axis/axes, and the propagated mask, but +no uncertainties:: + + >>> min_axis_1 = nddata.min(axis=1) + >>> print(np.asanyarray(min_axis_1)) # doctest: +FLOAT_CMP + [2.0 2.0] + >>> print(min_axis_1.uncertainty) + None + +For some use cases, it may be helpful to return the uncertainty +at the same index as the minimum/maximum ``data`` value, so that +the original ``data`` retains its uncertainty. You can get this +behavior with:: + + >>> min_axis_1 = nddata.min(axis=1, propagate_uncertainties=True) + + >>> print(np.asanyarray(min_axis_1)) # doctest: +FLOAT_CMP + [2.0 2.0] + >>> print(min_axis_1.uncertainty) # doctest: +FLOAT_CMP + StdDevUncertainty([1, 1]) + +Finally, in some cases it may be useful to do perform a collapse +operation only on the unmasked values, and only return a masked +result when all of the input values are masked. If we refer back to +the first example in this section, we see that the underlying +``data`` attribute has been summed over all values, including +masked ones:: + + >>> sum_axis_1 # doctest: +FLOAT_CMP + NDDataArray([——, 9.], unit='m') + +where the first data element is masked. We can instead get the sum +for only unmasked values with the ``operation_ignores_mask`` option:: + + >>> nddata.sum(axis=1, operation_ignores_mask=True) + NDDataArray([5, 9], unit='m') + +.. + EXAMPLE END + +Converting NDData to Other Classes +================================== + +There is limited support to convert a `~astropy.nddata.NDData` instance to +other classes. In the process some properties might be lost. + + >>> data = np.array([1, 2, 3, 4]) + >>> mask = np.array([True, False, False, True]) + >>> unit = 'm' + >>> ndd = NDData(data, mask=mask, unit=unit) + +`numpy.ndarray` +--------------- + +Converting the ``data`` to an array:: + + >>> array = np.asarray(ndd.data) + >>> array + array([1, 2, 3, 4]) + +Though using ``np.asarray`` is not required, in most cases it will ensure that +the result is always a `numpy.ndarray` + +`numpy.ma.MaskedArray` +---------------------- + +Converting the ``data`` and ``mask`` to a MaskedArray:: + + + >>> masked_array = np.ma.array(ndd.data, mask=ndd.mask) + >>> masked_array + masked_array(data=[--, 2, 3, --], + mask=[ True, False, False, True], + fill_value=999999) + +`~astropy.units.Quantity` +------------------------- + +Converting the ``data`` and ``unit`` to a Quantity:: + + >>> quantity = u.Quantity(ndd.data, unit=ndd.unit) + >>> quantity # doctest: +FLOAT_CMP + + +MaskedQuantity +-------------- + +Converting the ``data``, ``unit``, and ``mask`` to a ``MaskedQuantity``:: + + >>> from astropy.utils.masked import Masked + >>> Masked(u.Quantity(ndd.data, ndd.unit), ndd.mask) # doctest: +FLOAT_CMP + diff --git a/docs/nddata/performance.inc.rst b/docs/nddata/performance.inc.rst new file mode 100644 index 000000000000..aef9ca9e37fe --- /dev/null +++ b/docs/nddata/performance.inc.rst @@ -0,0 +1,20 @@ +.. note that if this is changed from the default approach of using an *include* + (in index.rst) to a separate performance page, the header needs to be changed + from === to ***, the filename extension needs to be changed from .inc.rst to + .rst, and a link needs to be added in the subpackage toctree + +.. _astropy-nddata-performance: + +Performance Tips +================ + ++ Using the uncertainty class `~astropy.nddata.VarianceUncertainty` will + be somewhat more efficient than the other two uncertainty classes, + `~astropy.nddata.InverseVariance` and `~astropy.nddata.StdDevUncertainty`. + The latter two are converted to variance for the purposes of error + propagation and then converted from variance back to the original + uncertainty type. The performance difference should be small. ++ When possible, mask values by setting them to ``np.nan`` and use the + ``numpy`` functions and methods that automatically exclude ``np.nan``, + like ``np.nanmedian`` and ``np.nanstd``. This will typically be much + faster than using `numpy.ma.MaskedArray`. diff --git a/docs/nddata/ref_api.rst b/docs/nddata/ref_api.rst new file mode 100644 index 000000000000..2a87976a34c1 --- /dev/null +++ b/docs/nddata/ref_api.rst @@ -0,0 +1,11 @@ +Reference/API +************* + +.. automodapi:: astropy.nddata + :no-inheritance-diagram: + +.. automodapi:: astropy.nddata.bitmask + :no-inheritance-diagram: + +.. automodapi:: astropy.nddata.utils + :no-inheritance-diagram: diff --git a/docs/nddata/subclassing.rst b/docs/nddata/subclassing.rst new file mode 100644 index 000000000000..20613b4cf0ec --- /dev/null +++ b/docs/nddata/subclassing.rst @@ -0,0 +1,531 @@ +.. _nddata_subclassing: + +Subclassing +*********** + +`~astropy.nddata.NDData` +======================== + +This class serves as the base for subclasses that use a `numpy.ndarray` (or +something that presents a ``numpy``-like interface) as the ``data`` attribute. + +.. note:: + Each attribute is saved as an attribute with one leading underscore. For + example, the ``data`` is saved as ``_data`` and the ``mask`` as ``_mask``, + and so on. + +Adding Another Property +----------------------- + + >>> from astropy.nddata import NDData + + >>> class NDDataWithFlags(NDData): + ... def __init__(self, *args, **kwargs): + ... # Remove flags attribute if given and pass it to the setter. + ... self.flags = kwargs.pop('flags') if 'flags' in kwargs else None + ... super().__init__(*args, **kwargs) + ... + ... @property + ... def flags(self): + ... return self._flags + ... + ... @flags.setter + ... def flags(self, value): + ... self._flags = value + + >>> ndd = NDDataWithFlags([1,2,3]) + >>> ndd.flags is None + True + + >>> ndd = NDDataWithFlags([1,2,3], flags=[0, 0.2, 0.3]) + >>> ndd.flags + [0, 0.2, 0.3] + +.. note:: + To simplify subclassing, each setter (except for ``data``) is called during + ``__init__`` so putting restrictions on any attribute can be done inside + the setter and will also apply during instance creation. + +Customize the Setter for a Property +----------------------------------- + + >>> import numpy as np + + >>> class NDDataMaskBoolNumpy(NDData): + ... + ... @NDData.mask.setter + ... def mask(self, value): + ... # Convert mask to boolean numpy array. + ... self._mask = np.array(value, dtype=np.bool_) + + >>> ndd = NDDataMaskBoolNumpy([1,2,3]) + >>> ndd.mask = [True, False, True] + >>> ndd.mask + array([ True, False, True]...) + +Extend the Setter for a Property +-------------------------------- + +``unit``, ``meta``, and ``uncertainty`` implement some additional logic in their +setter so subclasses might define a call to the superclass and let the +super property set the attribute afterwards:: + + >>> import numpy as np + + >>> class NDDataUncertaintyShapeChecker(NDData): + ... + ... @NDData.uncertainty.setter + ... def uncertainty(self, value): + ... value = np.asarray(value) + ... if value.shape != self.data.shape: + ... raise ValueError('uncertainty must have the same shape as the data.') + ... # Call the setter of the super class in case it might contain some + ... # important logic (only True for meta, unit and uncertainty) + ... super(NDDataUncertaintyShapeChecker, self.__class__).uncertainty.fset(self, value) + ... # Unlike "super(cls_name, cls_name).uncertainty.fset" or + ... # or "NDData.uncertainty.fset" this will respect Pythons method + ... # resolution order. + + >>> ndd = NDDataUncertaintyShapeChecker([1,2,3], uncertainty=[2,3,4]) + INFO: uncertainty should have attribute uncertainty_type. [astropy.nddata.nddata] + >>> ndd.uncertainty + UnknownUncertainty([2, 3, 4]) + +Having a Setter for the Data +---------------------------- + + >>> class NDDataWithDataSetter(NDData): + ... + ... @NDData.data.setter + ... def data(self, value): + ... self._data = np.asarray(value) + + >>> ndd = NDDataWithDataSetter([1,2,3]) + >>> ndd.data = [3,2,1] + >>> ndd.data + array([3, 2, 1]) + +.. _NDDataRef: + +`~astropy.nddata.NDDataRef` +=========================== + +`~astropy.nddata.NDDataRef` itself inherits from `~astropy.nddata.NDData` so +any of the possibilities there also apply to NDDataRef. But NDDataRef also +inherits from the Mixins: + +- `~astropy.nddata.NDSlicingMixin` +- `~astropy.nddata.NDArithmeticMixin` +- `~astropy.nddata.NDIOMixin` + +Which allow additional operations. + +Add Another Arithmetic Operation +-------------------------------- + +Adding another operation is possible provided the ``data`` and ``unit`` allow +it within the framework of `~astropy.units.Quantity`. + +Examples +^^^^^^^^ + +.. + EXAMPLE START + Adding Operations When Working with NDDataRef + +To add a power function:: + + >>> from astropy.nddata import NDDataRef + >>> import numpy as np + >>> from astropy.utils import sharedmethod + + >>> class NDDataPower(NDDataRef): + ... @sharedmethod # sharedmethod to allow it also as classmethod + ... def pow(self, operand, operand2=None, **kwargs): + ... # the uncertainty doesn't allow propagation so set it to None + ... kwargs['propagate_uncertainties'] = None + ... # Call the _prepare_then_do_arithmetic function with the + ... # numpy.power ufunc. + ... return self._prepare_then_do_arithmetic(np.power, operand, + ... operand2, **kwargs) + +This can be used like the other arithmetic methods similar to +:meth:`~astropy.nddata.NDArithmeticMixin.add`. So it works when calling it +on the class or the instance:: + + >>> ndd = NDDataPower([1,2,3]) + + >>> # using it on the instance with one operand + >>> ndd.pow(3) # doctest: +ELLIPSIS + NDDataPower([ 1, 8, 27]...) + + >>> # using it on the instance with two operands + >>> ndd.pow([1,2,3], [3,4,5]) # doctest: +ELLIPSIS + NDDataPower([ 1, 16, 243]...) + + >>> # or using it as classmethod + >>> NDDataPower.pow(6, [1,2,3]) # doctest: +ELLIPSIS + NDDataPower([ 6, 36, 216]...) + +To allow propagation also with ``uncertainty`` see subclassing +`~astropy.nddata.NDUncertainty`. + +.. + EXAMPLE END + +The ``_prepare_then_do_arithmetic`` implements the relevant checks if it was +called on the class or the instance, and if one or two operands were given, +converts the operands, if necessary, to the appropriate classes. Overriding +``_prepare_then_do_arithmetic`` in subclasses should be avoided if +possible. + +Arithmetic on an Existing Property +---------------------------------- + +Customizing how an existing property is handled during arithmetic is possible +with some arguments to the function calls such as +:meth:`~astropy.nddata.NDArithmeticMixin.add`, but it is possible to hardcode +behavior too. The actual operation on the attribute (except for ``unit``) is +done in a method ``_arithmetic_*`` where ``*`` is the name of the property. + +Examples +^^^^^^^^ + +.. + EXAMPLE START + Customizing Existing Properties During Arithmetic in NDData + +To customize how the ``meta`` will be affected during arithmetic:: + + >>> from astropy.nddata import NDDataRef + + >>> from copy import deepcopy + >>> class NDDataWithMetaArithmetics(NDDataRef): + ... + ... def _arithmetic_meta(self, operation, operand, handle_mask, **kwds): + ... # the function must take the arguments: + ... # operation (numpy-ufunc like np.add, np.subtract, ...) + ... # operand (the other NDData-like object, already wrapped as NDData) + ... # handle_mask (see description for "add") + ... + ... # The meta is dict like but we want the keywords exposure to change + ... # Anticipate that one or both might have no meta and take the first one that has + ... result_meta = deepcopy(self.meta) if self.meta else deepcopy(operand.meta) + ... # Do the operation on the keyword if the keyword exists + ... if result_meta and 'exposure' in result_meta: + ... result_meta['exposure'] = operation(result_meta['exposure'], operand.data) + ... return result_meta # return it + +To trigger this method, the ``handle_meta`` argument to arithmetic methods can +be anything except ``None`` or ``"first_found"``:: + + >>> ndd = NDDataWithMetaArithmetics([1,2,3], meta={'exposure': 10}) + >>> ndd2 = ndd.add(10, handle_meta='') + >>> ndd2.meta + {'exposure': np.int64(20)} + + >>> ndd3 = ndd.multiply(0.5, handle_meta='') + >>> ndd3.meta + {'exposure': np.float64(5.0)} + +.. warning:: + To use these internal `_arithmetic_*` methods there are some restrictions on + the attributes when calling the operation: + + - ``mask``: ``handle_mask`` must not be ``None``, ``"ff"``, or + ``"first_found"``. + - ``wcs``: ``compare_wcs`` argument with the same restrictions as mask. + - ``meta``: ``handle_meta`` argument with the same restrictions as mask. + - ``uncertainty``: ``propagate_uncertainties`` must be ``None`` or evaluate + to ``False``. ``arithmetic_uncertainty`` must also accept different + arguments: ``operation``, ``operand``, ``result``, ``correlation``, + ``**kwargs``. + +.. + EXAMPLE END + +Changing the Default Argument for Arithmetic Operations +------------------------------------------------------- + +If the goal is to change the default value of an existing parameter for +arithmetic methods, such as when explicitly specifying the parameter each +time you call an arithmetic operation is too much effort, you can change the +default value of existing parameters by changing it in the method signature of +``_arithmetic``. + +Example +^^^^^^^ + +.. + EXAMPLE START + Changing the Default Argument for Arithmetic Operations in NDData + +To change the default value of an existing parameter for arithmetic methods:: + + >>> from astropy.nddata import NDDataRef + >>> import numpy as np + + >>> class NDDDiffAritDefaults(NDDataRef): + ... def _arithmetic(self, *args, **kwargs): + ... # Changing the default of handle_mask to None + ... if 'handle_mask' not in kwargs: + ... kwargs['handle_mask'] = None + ... # Call the original with the updated kwargs + ... return super()._arithmetic(*args, **kwargs) + + >>> ndd1 = NDDDiffAritDefaults(1, mask=False) + >>> ndd2 = NDDDiffAritDefaults(1, mask=True) + >>> # No mask handling logic will return no mask: + >>> ndd1.add(ndd2).mask + + >>> # But giving other values is still possible: + >>> ndd1.add(ndd2, handle_mask=np.logical_or).mask + np.True_ + + >>> ndd1.add(ndd2, handle_mask="ff").mask + False + +The parameter controlling how properties are handled are all keyword-only +so using the ``*args``, ``**kwargs`` approach allows you to only alter one +default without needing to care about the positional order of arguments. + +.. + EXAMPLE END + +Arithmetic with an Additional Property +-------------------------------------- + +This also requires overriding the ``_arithmetic`` method. Suppose we have a +``flags`` attribute again:: + + >>> from copy import deepcopy + >>> import numpy as np + + >>> class NDDataWithFlags(NDDataRef): + ... def __init__(self, *args, **kwargs): + ... # Remove flags attribute if given and pass it to the setter. + ... self.flags = kwargs.pop('flags') if 'flags' in kwargs else None + ... super().__init__(*args, **kwargs) + ... + ... @property + ... def flags(self): + ... return self._flags + ... + ... @flags.setter + ... def flags(self, value): + ... self._flags = value + ... + ... def _arithmetic(self, operation, operand, *args, **kwargs): + ... # take all args and kwargs to allow arithmetic on the other properties + ... # to work like before. + ... + ... # do the arithmetic on the flags (pop the relevant kwargs, if any!!!) + ... if self.flags is not None and operand.flags is not None: + ... result_flags = np.logical_or(self.flags, operand.flags) + ... # np.logical_or is just a suggestion you can do what you want + ... else: + ... if self.flags is not None: + ... result_flags = deepcopy(self.flags) + ... else: + ... result_flags = deepcopy(operand.flags) + ... + ... # Let the superclass do all the other attributes note that + ... # this returns the result and a dictionary containing other attributes + ... result, kwargs = super()._arithmetic(operation, operand, *args, **kwargs) + ... # The arguments for creating a new instance are saved in kwargs + ... # so we need to add another keyword "flags" and add the processed flags + ... kwargs['flags'] = result_flags + ... return result, kwargs # these must be returned + + >>> ndd1 = NDDataWithFlags([1,2,3], flags=np.array([1,0,1], dtype=bool)) + >>> ndd2 = NDDataWithFlags([1,2,3], flags=np.array([0,0,1], dtype=bool)) + >>> ndd3 = ndd1.add(ndd2) + >>> ndd3.flags + array([ True, False, True]...) + +Slicing an Existing Property +---------------------------- + +Suppose you have a class expecting a 2D ``data`` but the mask is +only 1D. This would lead to problems if you were to slice in two dimensions. + + >>> from astropy.nddata import NDDataRef + >>> import numpy as np + + >>> class NDDataMask1D(NDDataRef): + ... def _slice_mask(self, item): + ... # Multidimensional slices are represented by tuples: + ... if isinstance(item, tuple): + ... # only use the first dimension of the slice + ... return self.mask[item[0]] + ... # Let the superclass deal with the other cases + ... return super()._slice_mask(item) + + >>> ndd = NDDataMask1D(np.ones((3,3)), mask=np.ones(3, dtype=bool)) + >>> nddsliced = ndd[1:3,1:3] + >>> nddsliced.mask + array([ True, True]...) + +.. note:: + The methods slicing the attributes are prefixed by a ``_slice_*`` where ``*`` + can be ``mask``, ``uncertainty``, or ``wcs``. So overriding them is the + most convenient way to customize how the attributes are sliced. + +.. note:: + If slicing should affect the ``unit`` or ``meta`` see the next example. + + +Slicing an Additional Property +------------------------------ + +Building on the added property ``flags``, we want them to be sliceable: + + >>> class NDDataWithFlags(NDDataRef): + ... def __init__(self, *args, **kwargs): + ... # Remove flags attribute if given and pass it to the setter. + ... self.flags = kwargs.pop('flags') if 'flags' in kwargs else None + ... super().__init__(*args, **kwargs) + ... + ... @property + ... def flags(self): + ... return self._flags + ... + ... @flags.setter + ... def flags(self, value): + ... self._flags = value + ... + ... def _slice(self, item): + ... # slice all normal attributes + ... kwargs = super()._slice(item) + ... # The arguments for creating a new instance are saved in kwargs + ... # so we need to add another keyword "flags" and add the sliced flags + ... kwargs['flags'] = self.flags[item] + ... return kwargs # these must be returned + + >>> ndd = NDDataWithFlags([1,2,3], flags=[0, 0.2, 0.3]) + >>> ndd2 = ndd[1:3] + >>> ndd2.flags + [0.2, 0.3] + +If you wanted to keep just the original ``flags`` instead of the sliced ones, +you could use ``kwargs['flags'] = self.flags`` and omit the ``[item]``. + +`~astropy.nddata.NDDataBase` +============================ + +The class `~astropy.nddata.NDDataBase` is a metaclass — when subclassing it, +all properties of `~astropy.nddata.NDDataBase` *must* be overridden in the +subclass. + +Subclassing from `~astropy.nddata.NDDataBase` gives you complete flexibility +in how you implement data storage and the other properties. If your data is +stored in a ``numpy`` array (or something that behaves like a ``numpy`` array), +it may be more convenient to subclass `~astropy.nddata.NDData` instead of +`~astropy.nddata.NDDataBase`. + +Example +------- + +.. + EXAMPLE START + Implementing the NDDataBase Interface + +To implement the NDDataBase interface by creating a read-only container:: + + >>> from astropy.nddata import NDDataBase + + >>> class NDDataReadOnlyNoRestrictions(NDDataBase): + ... def __init__(self, data, unit, mask, uncertainty, meta, wcs, psf): + ... self._data = data + ... self._unit = unit + ... self._mask = mask + ... self._uncertainty = uncertainty + ... self._meta = meta + ... self._wcs = wcs + ... self._psf = psf + ... + ... @property + ... def data(self): + ... return self._data + ... + ... @property + ... def unit(self): + ... return self._unit + ... + ... @property + ... def mask(self): + ... return self._mask + ... + ... @property + ... def uncertainty(self): + ... return self._uncertainty + ... + ... @property + ... def meta(self): + ... return self._meta + ... + ... @property + ... def wcs(self): + ... return self._wcs + ... + ... @property + ... def psf(self): + ... return self._psf + + >>> # A meaningless test to show that creating this class is possible: + >>> NDDataReadOnlyNoRestrictions(1,2,3,4,5,6,7) is not None + True + +.. note:: + Actually defining an ``__init__`` is not necessary and the properties could + return arbitrary values but the properties **must** be defined. + +.. + EXAMPLE END + +Subclassing `~astropy.nddata.NDUncertainty` +=========================================== + +.. warning:: + The internal interface of NDUncertainty and subclasses is experimental and + might change in future versions. + +Subclasses deriving from `~astropy.nddata.NDUncertainty` need in order to +implement: + +- Property ``uncertainty_type`` should return a string describing the + uncertainty, for example, ``"ivar"`` for inverse variance. +- Methods for propagation: `_propagate_*` where ``*`` is the name of the + universal function (ufunc) that is used on the ``NDData`` parent. + +Creating an Uncertainty without Propagation +------------------------------------------- + +`~astropy.nddata.UnknownUncertainty` is a minimal working implementation +without error propagation. We can create an uncertainty by storing +systematic uncertainties:: + + >>> from astropy.nddata import NDUncertainty + + >>> class SystematicUncertainty(NDUncertainty): + ... @property + ... def uncertainty_type(self): + ... return 'systematic' + ... + ... def _data_unit_to_uncertainty_unit(self, value): + ... return None + ... + ... def _propagate_add(self, other_uncert, *args, **kwargs): + ... return None + ... + ... def _propagate_subtract(self, other_uncert, *args, **kwargs): + ... return None + ... + ... def _propagate_multiply(self, other_uncert, *args, **kwargs): + ... return None + ... + ... def _propagate_divide(self, other_uncert, *args, **kwargs): + ... return None + + >>> SystematicUncertainty([10]) + SystematicUncertainty([10]) diff --git a/docs/nddata/utils.rst b/docs/nddata/utils.rst new file mode 100644 index 000000000000..d72af059629e --- /dev/null +++ b/docs/nddata/utils.rst @@ -0,0 +1,367 @@ +.. _nddata_utils: + +Image Utilities +*************** + +Overview +======== + +The `astropy.nddata.utils` module includes general utility functions +for array operations. + +.. _cutout_images: + +2D Cutout Images +================ + +Getting Started +--------------- + +The `~astropy.nddata.utils.Cutout2D` class can be used to create a +postage stamp cutout image from a 2D array. If an optional +`~astropy.wcs.WCS` object is input to +`~astropy.nddata.utils.Cutout2D`, then the +`~astropy.nddata.utils.Cutout2D` object will contain an updated +`~astropy.wcs.WCS` corresponding to the cutout array. + +First, we simulate a single source on a 2D data array. If you would like to +simulate many sources, see :ref:`bounding-boxes`. + +Note: The pair convention is different for **size** and **position**! The +position is specified as (x,y), but the size is specified as (y,x). + + >>> import numpy as np + >>> from astropy.modeling.models import Gaussian2D + >>> y, x = np.mgrid[0:500, 0:500] + >>> data = Gaussian2D(1, 50, 100, 10, 5, theta=0.5)(x, y) + +Now, we can display the image: + +.. doctest-skip:: + + >>> import matplotlib.pyplot as plt + >>> fig, ax = plt.subplots() + >>> ax.imshow(data, origin='lower') + +.. plot:: + + import numpy as np + import matplotlib.pyplot as plt + from astropy.modeling.models import Gaussian2D + y, x = np.mgrid[0:500, 0:500] + data = Gaussian2D(1, 50, 100, 10, 5, theta=0.5)(x, y) + fig, ax = plt.subplots() + ax.imshow(data, origin='lower') + +Next we can create a cutout for the single object in this image. We +create a cutout centered at position ``(x, y) = (49.7, 100.1)`` with a +size of ``(ny, nx) = (41, 51)`` pixels:: + + >>> from astropy.nddata import Cutout2D + >>> from astropy import units as u + >>> position = (49.7, 100.1) + >>> size = (41, 51) # pixels + >>> cutout = Cutout2D(data, position, size) + +The ``size`` keyword can also be a `~astropy.units.Quantity` object:: + + >>> size = u.Quantity((41, 51), u.pixel) + >>> cutout = Cutout2D(data, position, size) + +Or contain `~astropy.units.Quantity` objects:: + + >>> size = (41*u.pixel, 51*u.pixel) + >>> cutout = Cutout2D(data, position, size) + +A square cutout image can be generated by passing an integer or +a scalar `~astropy.units.Quantity`:: + + >>> size = 41 + >>> cutout2 = Cutout2D(data, position, size) + + >>> size = 41 * u.pixel + >>> cutout2 = Cutout2D(data, position, size) + +The cutout array is stored in the ``data`` attribute of the +`~astropy.nddata.utils.Cutout2D` instance. If the ``copy`` keyword is +`False` (default), then ``cutout.data`` will be a view into the +original ``data`` array. If ``copy=True``, then ``cutout.data`` will +hold a copy of the original ``data``. Now we display the cutout +image: + +.. doctest-skip:: + + >>> cutout = Cutout2D(data, position, (41, 51)) + >>> fig, ax = plt.subplots() + >>> ax.imshow(cutout.data, origin='lower') + +.. plot:: + + import numpy as np + import matplotlib.pyplot as plt + from astropy.modeling.models import Gaussian2D + from astropy.nddata import Cutout2D + y, x = np.mgrid[0:500, 0:500] + data = Gaussian2D(1, 50, 100, 10, 5, theta=0.5)(x, y) + position = (49.7, 100.1) + cutout = Cutout2D(data, position, (41, 51)) + fig, ax = plt.subplots() + ax.imshow(cutout.data, origin='lower') + +The cutout object can plot its bounding box on the original data using +the :meth:`~astropy.nddata.utils.Cutout2D.plot_on_original` method: + +.. doctest-skip:: + + >>> plt.imshow(data, origin='lower') + >>> cutout.plot_on_original(color='white') + +.. plot:: + + import numpy as np + import matplotlib.pyplot as plt + from astropy.modeling.models import Gaussian2D + from astropy.nddata import Cutout2D + y, x = np.mgrid[0:500, 0:500] + data = Gaussian2D(1, 50, 100, 10, 5, theta=0.5)(x, y) + position = (49.7, 100.1) + size = (41, 51) + cutout = Cutout2D(data, position, size) + fig, ax = plt.subplots() + ax.imshow(data, origin='lower') + cutout.plot_on_original(color='white') + +Many properties of the cutout array are also stored as attributes, +including:: + + >>> # shape of the cutout array + >>> print(cutout.shape) + (41, 51) + + >>> # rounded pixel index of the input position + >>> print(cutout.position_original) + (50, 100) + + >>> # corresponding position in the cutout array + >>> print(cutout.position_cutout) + (25, 20) + + >>> # (non-rounded) input position in both the original and cutout arrays + >>> print((cutout.input_position_original, cutout.input_position_cutout)) # doctest: +FLOAT_CMP + ((49.7, 100.1), (24.700000000000003, 20.099999999999994)) + + >>> # the origin pixel in both arrays + >>> print((cutout.origin_original, cutout.origin_cutout)) + ((25, 80), (0, 0)) + + >>> # tuple of slice objects for the original array + >>> print(cutout.slices_original) + (slice(80, 121, None), slice(25, 76, None)) + + >>> # tuple of slice objects for the cutout array + >>> print(cutout.slices_cutout) + (slice(0, 41, None), slice(0, 51, None)) + +There are also two `~astropy.nddata.utils.Cutout2D` methods to convert +pixel positions between the original and cutout arrays:: + + >>> print(cutout.to_original_position((2, 1))) + (27, 81) + + >>> print(cutout.to_cutout_position((27, 81))) + (2, 1) + + +2D Cutout Modes +--------------- + +There are three modes for creating cutout arrays: ``'trim'``, +``'partial'``, and ``'strict'``. For the ``'partial'`` and ``'trim'`` +modes, a partial overlap of the cutout array and the input ``data`` +array is sufficient. For the ``'strict'`` mode, the cutout array has +to be fully contained within the ``data`` array, otherwise an +`~astropy.nddata.utils.PartialOverlapError` is raised. In all modes, +non-overlapping arrays will raise a +`~astropy.nddata.utils.NoOverlapError`. In ``'partial'`` mode, +positions in the cutout array that do not overlap with the ``data`` +array will be filled with ``fill_value``. In ``'trim'`` mode only the +overlapping elements are returned, thus the resulting cutout array may +be smaller than the requested ``size``. + +The default uses ``mode='trim'``, which can result in cutout arrays +that are smaller than the requested ``size``:: + + >>> data2 = np.arange(20.).reshape(5, 4) + >>> cutout1 = Cutout2D(data2, (0, 0), (3, 3), mode='trim') + >>> print(cutout1.data) # doctest: +FLOAT_CMP + [[0. 1.] + [4. 5.]] + >>> print(cutout1.shape) + (2, 2) + >>> print((cutout1.position_original, cutout1.position_cutout)) + ((0, 0), (0, 0)) + +With ``mode='partial'``, the cutout will never be trimmed. Instead it +will be filled with ``fill_value`` (the default is ``numpy.nan``) if +the cutout is not fully contained in the data array:: + + >>> cutout2 = Cutout2D(data2, (0, 0), (3, 3), mode='partial') + >>> print(cutout2.data) # doctest: +FLOAT_CMP + [[nan nan nan] + [nan 0. 1.] + [nan 4. 5.]] + +Note that for the ``'partial'`` mode, the positions (and several other +attributes) are calculated for on the *valid* (non-filled) cutout +values:: + + >>> print((cutout2.position_original, cutout2.position_cutout)) + ((0, 0), (1, 1)) + >>> print((cutout2.origin_original, cutout2.origin_cutout)) + ((0, 0), (1, 1)) + >>> print(cutout2.slices_original) + (slice(0, 2, None), slice(0, 2, None)) + >>> print(cutout2.slices_cutout) + (slice(1, 3, None), slice(1, 3, None)) + +Using ``mode='strict'`` will raise an exception if the cutout is not +fully contained in the data array: + +.. doctest-skip:: + + >>> cutout3 = Cutout2D(data2, (0, 0), (3, 3), mode='strict') + PartialOverlapError: Arrays overlap only partially. + + +2D Cutout from a `~astropy.coordinates.SkyCoord` Position +--------------------------------------------------------- + +The input ``position`` can also be specified as a +`~astropy.coordinates.SkyCoord`, in which case a `~astropy.wcs.WCS` +object must be input via the ``wcs`` keyword. + +First, we define a `~astropy.coordinates.SkyCoord` position and a +`~astropy.wcs.WCS` object for our data (usually this would come from +your FITS header):: + + >>> from astropy.coordinates import SkyCoord + >>> from astropy.wcs import WCS + >>> position = SkyCoord('13h11m29.96s -01d19m18.7s', frame='icrs') + >>> wcs = WCS(naxis=2) + >>> rho = np.pi / 3. + >>> scale = 0.05 / 3600. + >>> wcs.wcs.cd = [[scale*np.cos(rho), -scale*np.sin(rho)], + ... [scale*np.sin(rho), scale*np.cos(rho)]] + >>> wcs.wcs.ctype = ['RA---TAN', 'DEC--TAN'] + >>> wcs.wcs.crval = [position.ra.to_value(u.deg), + ... position.dec.to_value(u.deg)] + >>> wcs.wcs.crpix = [50, 100] + +Now we can create the cutout array using the +`~astropy.coordinates.SkyCoord` position and ``wcs`` object:: + + >>> cutout = Cutout2D(data, position, (30, 40), wcs=wcs) + >>> fig, ax = plt.subplots() # doctest: +SKIP + >>> ax.imshow(cutout.data, origin='lower') # doctest: +SKIP + +.. plot:: + + import numpy as np + import matplotlib.pyplot as plt + from astropy.modeling.models import Gaussian2D + from astropy.nddata import Cutout2D + from astropy.coordinates import SkyCoord + from astropy.wcs import WCS + y, x = np.mgrid[0:500, 0:500] + data = Gaussian2D(1, 50, 100, 10, 5, theta=0.5)(x, y) + position = SkyCoord('13h11m29.96s -01d19m18.7s', frame='icrs') + wcs = WCS(naxis=2) + rho = np.pi / 3. + scale = 0.05 / 3600. + wcs.wcs.cd = [[scale*np.cos(rho), -scale*np.sin(rho)], + [scale*np.sin(rho), scale*np.cos(rho)]] + wcs.wcs.ctype = ['RA---TAN', 'DEC--TAN'] + wcs.wcs.crval = [position.ra.value, position.dec.value] + wcs.wcs.crpix = [50, 100] + cutout = Cutout2D(data, position, (30, 40), wcs=wcs) + fig, ax = plt.subplots() + ax.imshow(cutout.data, origin='lower') + +The ``wcs`` attribute of the `~astropy.nddata.utils.Cutout2D` object now +contains the propagated `~astropy.wcs.WCS` for the cutout array. +Now we can find the sky coordinates for a given pixel in the cutout array. +Note that we need to use the ``cutout.wcs`` object for the cutout +positions:: + + >>> from astropy.wcs.utils import pixel_to_skycoord + >>> x_cutout, y_cutout = (5, 10) + >>> pixel_to_skycoord(x_cutout, y_cutout, cutout.wcs) # doctest: +FLOAT_CMP + + +We now find the corresponding pixel in the original ``data`` array and +its sky coordinates:: + + >>> x_data, y_data = cutout.to_original_position((x_cutout, y_cutout)) + >>> pixel_to_skycoord(x_data, y_data, wcs) # doctest: +FLOAT_CMP + + +As expected, the sky coordinates in the original ``data`` and the +cutout array agree. + + +2D Cutout Using an Angular ``size`` +----------------------------------- + +The input ``size`` can also be specified as a +`~astropy.units.Quantity` in angular units (e.g., degrees, arcminutes, +arcseconds, etc.). For this case, a `~astropy.wcs.WCS` object must be +input via the ``wcs`` keyword. + +For this example, we will use the data, `~astropy.coordinates.SkyCoord` +position, and ``wcs`` object from above to create a cutout with size +1.5 x 2.5 arcseconds:: + + >>> size = u.Quantity((1.5, 2.5), u.arcsec) + >>> cutout = Cutout2D(data, position, size, wcs=wcs) + >>> fig, ax = plt.subplots() # doctest: +SKIP + >>> ax.imshow(cutout.data, origin='lower') # doctest: +SKIP + +.. plot:: + + import numpy as np + import matplotlib.pyplot as plt + from astropy.modeling.models import Gaussian2D + from astropy.nddata import Cutout2D + from astropy.coordinates import SkyCoord + from astropy.wcs import WCS + from astropy import units as u + y, x = np.mgrid[0:500, 0:500] + data = Gaussian2D(1, 50, 100, 10, 5, theta=0.5)(x, y) + position = SkyCoord('13h11m29.96s -01d19m18.7s', frame='icrs') + wcs = WCS(naxis=2) + rho = np.pi / 3. + scale = 0.05 / 3600. + wcs.wcs.cd = [[scale*np.cos(rho), -scale*np.sin(rho)], + [scale*np.sin(rho), scale*np.cos(rho)]] + wcs.wcs.ctype = ['RA---TAN', 'DEC--TAN'] + wcs.wcs.crval = [position.ra.value, position.dec.value] + wcs.wcs.crpix = [50, 100] + size = u.Quantity((1.5, 2.5), u.arcsec) + cutout = Cutout2D(data, position, size, wcs=wcs) + fig, ax = plt.subplots() + ax.imshow(cutout.data, origin='lower') + + +Saving a 2D Cutout to a FITS File with an Updated WCS +===================================================== + +A `~astropy.nddata.utils.Cutout2D` object can be saved to a FITS file, +including the updated WCS object for the cutout region. In this example, we +download an example FITS image and create a cutout image. The resulting +`~astropy.nddata.utils.Cutout2D` object is then saved to a new FITS file with +the updated WCS for the cutout region. + +.. literalinclude:: examples/cutout2d_tofits.py + :language: python diff --git a/docs/nitpick-exceptions b/docs/nitpick-exceptions new file mode 100644 index 000000000000..2658d709051d --- /dev/null +++ b/docs/nitpick-exceptions @@ -0,0 +1,86 @@ +# astropy.cosmology +py:obj astropy.cosmology.realizations.default_cosmology + +# astropy.io.votable +py:class astropy.io.votable.tree.SimpleElement +py:class astropy.io.votable.tree.SimpleElementWithContent + +# astropy.modeling +py:class astropy.modeling.polynomial.PolynomialBase + +# astropy.io.fits +py:class astropy.io.fits.hdu.base.ExtensionHDU +py:class astropy.io.fits.util.NotifierMixin +py:class astropy.io.fits.hdu.compressed._codecs.Codec + +# astropy.io.misc.yaml +py:class yaml.dumper.SafeDumper +py:class yaml.loader.SafeLoader +py:class yaml.representer.SafeRepresenter +py:class yaml.scanner.Scanner +py:class yaml.constructor.SafeConstructor +py:class yaml.constructor.BaseConstructor +py:class yaml.parser.Parser +py:class yaml.representer.BaseRepresenter +py:class yaml.reader.Reader +py:class yaml.resolver.BaseResolver +py:class yaml.serializer.Serializer +py:class yaml.composer.Composer +py:class yaml.resolver.Resolver +py:class yaml.emitter.Emitter + +# astropy.units +# This is required on macOS (#9040 and #10026) and Windows (#16288). +py:obj astropy.units.function.logarithmic.m_bol + +# astropy.utils +py:class json.encoder.JSONEncoder + +# astropy.table +py:class astropy.table.column.BaseColumn +py:class astropy.table.groups.BaseGroups + +# astropy.visualization +py:obj Bbox +py:obj Transform +py:obj Figure +py:obj AbstractPathEffect +py:obj N +py:obj masked + +# astropy.wcs +py:class astropy.wcs.wcsapi.fitswcs.FITSWCSAPIMixin +py:class astropy.wcs.wcsapi.fitswcs.custom_ctype_to_ucd_mapping + +# numpy inherited docstrings +py:obj dtype +py:obj a +py:obj n +py:obj v +py:obj ndarray +py:obj args +py:obj numpy._typing.ArrayLike + +# other classes and functions that cannot be linked to +py:class xmlrpc.client.Error + +# Pending on python docs links issue #11975 +py:obj list.append +py:obj list.count +py:obj list.extend +py:obj list.index +py:obj list.insert +py:meth list.pop +py:obj list.remove +py:obj RendererBase +py:obj Artist +py:obj BboxBase + +# This list is from https://github.com/numpy/numpydoc/issues/275 +py:class None. Remove all items from D. +py:class a set-like object providing a view on D's items +py:class a set-like object providing a view on D's keys +py:class None. Update D from dict/iterable E and F. +py:class None. Update D from mapping/iterable E and F. +py:class an object providing a view on D's values +py:class a shallow copy of D diff --git a/docs/overview.rst b/docs/overview.rst deleted file mode 100644 index ade5ad0396ef..000000000000 --- a/docs/overview.rst +++ /dev/null @@ -1,71 +0,0 @@ -******** -Overview -******** - -Here we describe a broad overview of the Astropy project and its parts. - -Astropy Project Concept -======================= - -The "Astropy Project" is distinct from the `astropy` package. The -Astropy Project is a process intended to facilitate communication and -interoperability of python packages/codes in astronomy and astrophysics. -The project thus encompasses the `astropy` core package (which provides a -common framework), all "affiliated packages" (described below in -`Affiliated Packages`), and a general community aimed at bringing -resources together and not duplicating efforts. - - -`astropy` Core Package -====================== - -The `astropy` package (alternatively known as the "core" package) -contains various classes, utilities, and a packaging framework intended -to provide commonly-used astronomy tools. It is divided into a variety of -sub-packages, which are documented in the remainder of this -documentation (see :ref:`user-docs` for documentation of these components). - -The core also provides this documentation, and a variety of utilities -that simplify starting other python astronomy/astrophysics packages. As -described in the following section, these simplify the process of -creating affiliated packages. - - -Affiliated Packages -=================== - -The Astropy project includes the concept of "affiliated packages." An -affiliated package is an astronomy-related python package that is not -part of the `astropy` core source code, but has requested to be included -in the Astropy project. Such a package may be a candidate for eventual -inclusion in the main `astropy` package (although this is not required). - -There is a registry of affiliated packages at -http://affiliated.astropy.org, and in the near future, the `astropy` core -will include a tool to install affiliated packages by name. See the -:mod:`~astropy.config` module documentation for details about the -affiliated package registry and install tool. Note that affiliated -packages do not use the `astropy` namespace, which is reserved for the -core. Instead, they either use their package name directly, or -`awastropy.packagename` ("affiliated with astropy"). - -If you are interested in starting an affiliated package, or have a -package you are interested in making more compatible with astropy, the -`astropy` core package includes a variety of features that simplify and -homogenize package management. Astropy provides a `package template -`_ that provides a common -way to organize packages, to make your life simpler. You can use this -template either with a new package you are starting or an existing -package to make it more compatible with Astropy and the affiliated -package installer. See the `usage instructions in the template -`_ -for further details. - - -Community -========= - -Aside from the actual code, Astropy is also a community of -astronomy-associated users and developers that agree that sharing utilities -is healthy for the community and the science it produces. This community -is of course central to accomplishing anything with the code itself. diff --git a/docs/robots.txt b/docs/robots.txt new file mode 100644 index 000000000000..df810cf3bbb1 --- /dev/null +++ b/docs/robots.txt @@ -0,0 +1,6 @@ +User-agent: * +Allow: /*/latest/ +Allow: /en/latest/ # Fallback for bots that don't understand wildcards +Allow: /*/stable/ +Allow: /en/stable/ # Fallback for bots that don't understand wildcards +Disallow: / diff --git a/docs/rtd-pip-requirements b/docs/rtd-pip-requirements deleted file mode 100644 index 51f8fa685ad4..000000000000 --- a/docs/rtd-pip-requirements +++ /dev/null @@ -1,2 +0,0 @@ -numpy>=1.4.0 -Cython diff --git a/docs/rtd_environment.yaml b/docs/rtd_environment.yaml new file mode 100644 index 000000000000..3d8fd7179dc4 --- /dev/null +++ b/docs/rtd_environment.yaml @@ -0,0 +1,7 @@ +name: rtd313 +channels: + - conda-forge +dependencies: + - python=3.13 + - pip + - graphviz diff --git a/docs/samp/advanced_embed_samp_hub.rst b/docs/samp/advanced_embed_samp_hub.rst new file mode 100644 index 000000000000..0f0df50f5f25 --- /dev/null +++ b/docs/samp/advanced_embed_samp_hub.rst @@ -0,0 +1,138 @@ +.. doctest-skip-all + +Embedding a SAMP Hub in a GUI +***************************** + +Overview +======== + +If you wish to embed a SAMP hub in your Python Graphical User Interface (GUI) +tool, you will need to start the hub programmatically using:: + + from astropy.samp import SAMPHubServer + hub = SAMPHubServer() + hub.start() + +This launches the hub in a thread and is non-blocking. If you are not +interested in connections from web SAMP clients, then you can use:: + + from astropy.samp import SAMPHubServer + hub = SAMPHubServer(web_profile=False) + hub.start() + +This should be all you need to do. However, if you want to keep the Web +Profile active, there is an additional consideration: when a web +SAMP client connects, you will need to ask the user whether they accept the +connection (for security reasons). By default, the confirmation message is a +text-based message in the terminal, but if you have a GUI tool, you will +likely want to open a GUI dialog instead. + +To do this, you will need to define a class that handles the dialog, and then +pass an **instance** of the class to |SAMPHubServer| (not the class itself). +This class should inherit from `astropy.samp.WebProfileDialog` and add the +following: + + 1) A GUI timer callback that periodically calls + ``WebProfileDialog.handle_queue`` (available as + ``self.handle_queue``). + + 2) A ``show_dialog`` method to display a consent dialog. + It should take the following arguments: + + - ``samp_name``: The name of the application making the request. + + - ``details``: A dictionary of details about the client + making the request. The only key in this dictionary required by + the SAMP standard is ``samp.name`` which gives the name of the + client making the request. + + - ``client``: A hostname, port pair containing the client + address. + + - ``origin``: A string containing the origin of the + request. + + 3) Based on the user response, the ``show_dialog`` should call + ``WebProfileDialog.consent`` or ``WebProfileDialog.reject``. + This may, in some cases, be the result of another GUI callback. + +Example of embedding a SAMP hub in a Tk application +--------------------------------------------------- + +.. + EXAMPLE START + Embedding a SAMP Hub in a Tk Application + +The following code is a full example of a Tk application that watches for web +SAMP connections and opens the appropriate dialog:: + + import tkinter as tk + import tkinter.messagebox as tkMessageBox + + from astropy.samp import SAMPHubServer + from astropy.samp.hub import WebProfileDialog + + MESSAGE = """ + A Web application which declares to be + + Name: {name} + Origin: {origin} + + is requesting to be registered with the SAMP Hub. Pay attention + that if you permit its registration, such application will acquire + all current user privileges, like file read/write. + + Do you give your consent? + """ + + class TkWebProfileDialog(WebProfileDialog): + def __init__(self, root): + self.root = root + self.wait_for_dialog() + + def wait_for_dialog(self): + self.handle_queue() + self.root.after(100, self.wait_for_dialog) + + def show_dialog(self, samp_name, details, client, origin): + text = MESSAGE.format(name=samp_name, origin=origin) + + response = tkMessageBox.askyesno( + 'SAMP Hub', text, + default=tkMessageBox.NO) + + if response: + self.consent() + else: + self.reject() + + # Start up Tk application + root = tk.Tk() + tk.Label(root, text="Example SAMP Tk application", + font=("Helvetica", 36), justify=tk.CENTER).pack(pady=200) + root.geometry("500x500") + root.update() + + # Start up SAMP hub + h = SAMPHubServer(web_profile_dialog=TkWebProfileDialog(root)) + h.start() + + try: + # Main GUI loop + root.mainloop() + except KeyboardInterrupt: + pass + + h.stop() + +If you run the above script, a window will open that says "Example SAMP Tk +application." If you then go to the following page, for example: + +http://astrojs.github.io/sampjs/examples/pinger.html + +and click on the Ping button, you will see the dialog open in the Tk +application. Once you click on "CONFIRM," future "Ping" calls will no longer +bring up the dialog. + +.. + EXAMPLE END diff --git a/docs/samp/example_clients.rst b/docs/samp/example_clients.rst new file mode 100644 index 000000000000..b6033d3baace --- /dev/null +++ b/docs/samp/example_clients.rst @@ -0,0 +1,125 @@ +.. doctest-skip-all + +.. _vo-samp-example_clients: + + +Communication between Integrated Clients Objects +************************************************ + +As shown in :doc:`example_table_image`, the |SAMPIntegratedClient| class can be +used to communicate with other SAMP-enabled tools such as |TOPCAT|, +`SAO DS9 `_, or `Aladin Desktop `_. + +In this section, we look at how we can set up two |SAMPIntegratedClient| +instances and communicate between them. + +First, start up a SAMP hub as described in :doc:`example_hub`. + +Next, we create two clients and connect them to the hub:: + + >>> from astropy import samp + >>> client1 = samp.SAMPIntegratedClient(name="Client 1", description="Test Client 1", + ... metadata = {"client1.version":"0.01"}) + >>> client2 = samp.SAMPIntegratedClient(name="Client 2", description="Test Client 2", + ... metadata = {"client2.version":"0.25"}) + >>> client1.connect() + >>> client2.connect() + +We now define functions to call when receiving a notification, call or +response:: + + >>> def test_receive_notification(private_key, sender_id, mtype, params, extra): + ... print("Notification:", private_key, sender_id, mtype, params, extra) + + >>> def test_receive_call(private_key, sender_id, msg_id, mtype, params, extra): + ... print("Call:", private_key, sender_id, msg_id, mtype, params, extra) + ... client1.ereply(msg_id, samp.SAMP_STATUS_OK, result = {"txt": "printed"}) + + >>> def test_receive_response(private_key, sender_id, msg_id, response): + ... print("Response:", private_key, sender_id, msg_id, response) + +We subscribe client 1 to ``"samp.app.*"`` and bind it to the +related functions:: + + >>> client1.bind_receive_notification("samp.app.*", test_receive_notification) + >>> client1.bind_receive_call("samp.app.*", test_receive_call) + +We now bind message tags received by client 2 to suitable functions:: + + >>> client2.bind_receive_response("my-dummy-print", test_receive_response) + >>> client2.bind_receive_response("my-dummy-print-specific", test_receive_response) + +We are now ready to test out the clients and callback functions. Client 2 +notifies all clients using the "samp.app.echo" message type via the hub:: + + >>> client2.enotify_all("samp.app.echo", txt="Hello world!") + ['cli#2'] + Notification: 0d7f4500225981c104a197c7666a8e4e cli#2 samp.app.echo {'txt': + 'Hello world!'} {'host': 'antigone.lambrate.inaf.it', 'user': 'unknown'} + +We can also find a dictionary that specifies which clients would currently +receive ``samp.app.echo`` messages:: + + >>> print(client2.get_subscribed_clients("samp.app.echo")) + {'cli#2': {}} + +Client 2 calls all clients with the ``"samp.app.echo"`` message type using +``"my-dummy-print"`` as a message-tag:: + + >>> print(client2.call_all("my-dummy-print", + ... {"samp.mtype": "samp.app.echo", + ... "samp.params": {"txt": "Hello world!"}})) + {'cli#1': 'msg#1;;cli#hub;;cli#2;;my-dummy-print'} + Call: 8c8eb53178cb95e168ab17ec4eac2353 cli#2 + msg#1;;cli#hub;;cli#2;;my-dummy-print samp.app.echo {'txt': 'Hello world!'} + {'host': 'antigone.lambrate.inaf.it', 'user': 'unknown'} + Response: d0a28636321948ccff45edaf40888c54 cli#1 my-dummy-print + {'samp.status': 'samp.ok', 'samp.result': {'txt': 'printed'}} + +Client 2 then calls client 1 using the ``"samp.app.echo"`` message type, +tagging the message as ``"my-dummy-print-specific"``:: + + >>> try: + ... print(client2.call(client1.get_public_id(), + ... "my-dummy-print-specific", + ... {"samp.mtype": "samp.app.echo", + ... "samp.params": {"txt": "Hello client 1!"}})) + ... except samp.SAMPProxyError as e: + ... print("Error ({0}): {1}".format(e.faultCode, e.faultString)) + msg#2;;cli#hub;;cli#2;;my-dummy-print-specific + Call: 8c8eb53178cb95e168ab17ec4eac2353 cli#2 + msg#2;;cli#hub;;cli#2;;my-dummy-print-specific samp.app.echo {'txt': 'Hello + Cli 1!'} {'host': 'antigone.lambrate.inaf.it', 'user': 'unknown'} + Response: d0a28636321948ccff45edaf40888c54 cli#1 my-dummy-print-specific + {'samp.status': 'samp.ok', 'samp.result': {'txt': 'printed'}} + +We can now define a function called to test synchronous calls:: + + >>> def test_receive_sync_call(private_key, sender_id, msg_id, mtype, params, extra): + ... import time + ... print("SYNC Call:", sender_id, msg_id, mtype, params, extra) + ... time.sleep(2) + ... client1.reply(msg_id, {"samp.status": samp.SAMP_STATUS_OK, + ... "samp.result": {"txt": "printed sync"}}) + +We now bind the ``samp.test`` message type to ``test_receive_sync_call``:: + + >>> client1.bind_receive_call("samp.test", test_receive_sync_call) + >>> try: + ... # Sync call + ... print(client2.call_and_wait(client1.get_public_id(), + ... {"samp.mtype": "samp.test", + ... "samp.params": {"txt": "Hello SYNCRO client 1!"}}, + ... "10")) + ... except samp.SAMPProxyError as e: + ... # If timeout expires than a SAMPProxyError is returned + ... print("Error ({0}): {1}".format(e.faultCode, e.faultString)) + SYNC Call: cli#2 msg#3;;cli#hub;;cli#2;;sampy::sync::call samp.test {'txt': + 'Hello SYNCRO Cli 1!'} {'host': 'antigone.lambrate.inaf.it', 'user': + 'unknown'} + {'samp.status': 'samp.ok', 'samp.result': {'txt': 'printed sync'}} + +Finally, we disconnect the clients from the hub at the end:: + + >>> client1.disconnect() + >>> client2.disconnect() diff --git a/docs/samp/example_hub.rst b/docs/samp/example_hub.rst new file mode 100644 index 000000000000..57a5cd5bdbf2 --- /dev/null +++ b/docs/samp/example_hub.rst @@ -0,0 +1,48 @@ +.. doctest-skip-all + +.. _vo-samp-example_hub: + +Starting and Stopping a SAMP Hub Server +*************************************** + +There are several ways you can start up a SAMP hub: + +Using an Existing Hub +===================== + +You can start up another application that includes a hub, such as +|TOPCAT|, `SAO DS9 `_, or +`Aladin Desktop `_. + +Using the Command-Line Hub Utility +================================== + +You can make use of the ``samp_hub`` command-line utility, which is included in +``astropy``:: + + $ samp_hub + +To get more help on available options for ``samp_hub``:: + + $ samp_hub -h + +To stop the server, press control-C. + +Starting a Hub Programmatically (Advanced) +========================================== + +You can start up a hub by creating a |SAMPHubServer| instance and starting it, +either from the interactive Python prompt, or from a Python script:: + + >>> from astropy.samp import SAMPHubServer + >>> hub = SAMPHubServer() + >>> hub.start() + +You can then stop the hub by calling:: + + >>> hub.stop() + +However, this method is generally not recommended for average users because it +does not work correctly when web SAMP clients try to connect. Instead, this +should be reserved for developers who want to embed a SAMP hub in a GUI, for +example. For more information, see :doc:`advanced_embed_samp_hub`. diff --git a/docs/samp/example_table_image.rst b/docs/samp/example_table_image.rst new file mode 100644 index 000000000000..acbabff6c77c --- /dev/null +++ b/docs/samp/example_table_image.rst @@ -0,0 +1,283 @@ +.. doctest-skip-all + +.. _vo-samp-example-table-image: + +Sending and Receiving Tables and Images over SAMP +************************************************* + +In the following examples, we make use of: + +* |TOPCAT|, which is a tool to explore tabular data. +* `SAO DS9 `_, which is an image + visualization tool that can overplot catalogs. +* `Aladin Desktop `_, which is another tool that + can visualize images and catalogs. + +TOPCAT and Aladin will run a SAMP Hub if none is found, so for the following +examples you can either start up one of these applications first, or you can +start up the `astropy.samp` hub. You can start this using the following +command:: + + $ samp_hub + +Sending a Table to TOPCAT and DS9 +================================= + +The easiest way to send a VO table to TOPCAT is to make use of the +|SAMPIntegratedClient| class. Once TOPCAT is open, first instantiate a +|SAMPIntegratedClient| instance and then connect to the hub:: + + >>> from astropy.samp import SAMPIntegratedClient + >>> client = SAMPIntegratedClient() + >>> client.connect() + +Next, we have to set up a dictionary that contains details about the table to +send. This should include ``url``, which is the URL to the file, and ``name``, +which is a human-readable name for the table. The URL can be a local URL +(starting with ``file:///``):: + + >>> params = {} + >>> params["url"] = 'file:///Users/tom/Desktop/aj285677t3_votable.xml' + >>> params["name"] = "Robitaille et al. (2008), Table 3" + +.. note:: To construct a local URL, you can also make use of ``urlparse`` as + follows:: + + >>> import urlparse + >>> params["url"] = urlparse.urljoin('file:', os.path.abspath("aj285677t3_votable.xml")) + +Now we can set up the message itself. This includes the type of message (here +we use ``table.load.votable``, which indicates that a VO table should be loaded +and the details of the table that we set above):: + + >>> message = {} + >>> message["samp.mtype"] = "table.load.votable" + >>> message["samp.params"] = params + +Finally, we can broadcast this to all clients that are listening for +``table.load.votable`` messages using +:meth:`~astropy.samp.integrated_client.SAMPIntegratedClient.notify_all`:: + + >>> client.notify_all(message) + +The above message will actually be broadcast to all applications connected via +SAMP. For example, if we open `SAO DS9 `_ in +addition to TOPCAT, and we run the above command, both applications will load +the table. We can use the +:meth:`~astropy.samp.integrated_client.SAMPIntegratedClient.get_registered_clients` method to +find all of the clients connected to the hub:: + + >>> client.get_registered_clients() + ['hub', 'c1', 'c2'] + +These IDs do not mean much, but we can find out more using:: + + >>> client.get_metadata('c1') + {'author.affiliation': 'Astrophysics Group, Bristol University', + 'author.email': 'm.b.taylor@bristol.ac.uk', + 'author.name': 'Mark Taylor', + 'home.page': 'https://www.star.bristol.ac.uk/mbt/topcat/', + 'samp.description.text': 'Tool for OPerations on Catalogues And Tables', + 'samp.documentation.url': 'http://127.0.0.1:2525/doc/sun253/index.html', + 'samp.icon.url': 'http://127.0.0.1:2525/doc/images/tc_sok.gif', + 'samp.name': 'topcat', + 'topcat.version': '4.0-1'} + +We can see that ``c1`` is the TOPCAT client. We can now resend the data, but +this time only to TOPCAT, using the +:meth:`~astropy.samp.integrated_client.SAMPIntegratedClient.notify` method:: + + >>> client.notify('c1', message) + +Once finished, we should make sure we disconnect from the hub:: + + >>> client.disconnect() + +Receiving a Table from TOPCAT +============================= + +To receive a table from TOPCAT, we have to set up a client that listens for +messages from the hub. As before, we instantiate a |SAMPIntegratedClient| +instance and connect to the hub:: + + >>> from astropy.samp import SAMPIntegratedClient + >>> client = SAMPIntegratedClient() + >>> client.connect() + +We now set up a receiver class which will handle any received messages. We need +to take care to write handlers for both notifications and calls (the difference +between the two being that calls expect a reply):: + + >>> class Receiver: + ... def __init__(self, client): + ... self.client = client + ... self.received = False + ... def receive_call(self, private_key, sender_id, msg_id, mtype, params, extra): + ... self.params = params + ... self.received = True + ... self.client.reply(msg_id, {"samp.status": "samp.ok", "samp.result": {}}) + ... def receive_notification(self, private_key, sender_id, mtype, params, extra): + ... self.params = params + ... self.received = True + +And we instantiate it: + + >>> r = Receiver(client) + +We can now use the +:meth:`~astropy.samp.integrated_client.SAMPIntegratedClient.bind_receive_call` +and +:meth:`~astropy.samp.integrated_client.SAMPIntegratedClient.bind_receive_notification` +methods to tell our receiver to listen to all ``table.load.votable`` messages:: + + >>> client.bind_receive_call("table.load.votable", r.receive_call) + >>> client.bind_receive_notification("table.load.votable", r.receive_notification) + +We can now check that the message has not been received yet:: + + >>> r.received + False + +We can now broadcast the table from TOPCAT. After a few seconds, we can check +again if the message has been received:: + + >>> r.received + True + +Success! The table URL should now be available in ``r.params['url']``, so we +can do:: + + >>> from astropy.table import Table + >>> t = Table.read(r.params['url']) + Downloading http://127.0.0.1:2525/dynamic/4/t12.vot [Done] + >>> t + col1 col2 col3 col4 col5 col6 col7 col8 col9 col10 + ------------------------- -------- ------- -------- -------- ----- ---- ----- ---- ----- + SSTGLMC G000.0046+01.1431 0.0046 1.1432 265.2992 -28.3321 6.67 5.04 6.89 5.22 N + SSTGLMC G000.0106-00.7315 0.0106 -0.7314 267.1274 -29.3063 7.18 6.07 nan 5.17 Y + SSTGLMC G000.0110-01.0237 0.0110 -1.0236 267.4151 -29.4564 8.32 6.30 8.34 6.32 N + ... + +As before, we should remember to disconnect from the hub once we are done:: + + >>> client.disconnect() + +Example +======= + +.. + EXAMPLE START + Receiving and Reading a Table over SAMP + +The following is a full example of a script that can be used to receive and +read a table. It includes a loop that waits until the message is received, and +reads the table once it has:: + + import time + + from astropy.samp import SAMPIntegratedClient + from astropy.table import Table + + # Instantiate the client and connect to the hub + client=SAMPIntegratedClient() + client.connect() + + # Set up a receiver class + class Receiver: + def __init__(self, client): + self.client = client + self.received = False + def receive_call(self, private_key, sender_id, msg_id, mtype, params, extra): + self.params = params + self.received = True + self.client.reply(msg_id, {"samp.status": "samp.ok", "samp.result": {}}) + def receive_notification(self, private_key, sender_id, mtype, params, extra): + self.params = params + self.received = True + + # Instantiate the receiver + r = Receiver(client) + + # Listen for any instructions to load a table + client.bind_receive_call("table.load.votable", r.receive_call) + client.bind_receive_notification("table.load.votable", r.receive_notification) + + # We now run the loop to wait for the message in a try/finally block so that if + # the program is interrupted e.g. by control-C, the client terminates + # gracefully. + + try: + + # We test every 0.1s to see if the hub has sent a message + while True: + time.sleep(0.1) + if r.received: + t = Table.read(r.params['url']) + break + + finally: + + client.disconnect() + + # Print out table + print t + +.. + EXAMPLE END + +Sending an Image to DS9 and Aladin +================================== + +As for tables, the most convenient way to send a FITS image over SAMP is to +make use of the |SAMPIntegratedClient| class. Once Aladin or DS9 are open, +first instantiate a |SAMPIntegratedClient| instance and then connect to the hub +as before:: + + >>> from astropy.samp import SAMPIntegratedClient + >>> client = SAMPIntegratedClient() + >>> client.connect() + +Next, we have to set up a dictionary that contains details about the image to +send. This should include ``url``, which is the URL to the file, and ``name``, +which is a human-readable name for the table. The URL can be a local URL +(starting with ``file:///``):: + + >>> params = {} + >>> params["url"] = 'file:///Users/tom/Desktop/MSX_E.fits' + >>> params["name"] = "MSX Band E Image of the Galactic Center" + +See `Sending a Table to TOPCAT and DS9`_ for an example of a recommended way to +construct local URLs. Now we can set up the message itself. This includes the +type of message (here we use ``image.load.fits`` which indicates that a FITS +image should be loaded, and the details of the table that we set above):: + + >>> message = {} + >>> message["samp.mtype"] = "image.load.fits" + >>> message["samp.params"] = params + +Finally, we can broadcast this to all clients that are listening for +``table.load.votable`` messages:: + + >>> client.notify_all(message) + +As for `Sending a Table to TOPCAT and DS9`_, the +:meth:`~astropy.samp.integrated_client.SAMPIntegratedClient.notify_all` +method will broadcast the image to all listening clients, and for tables it +is possible to instead use the +:meth:`~astropy.samp.integrated_client.SAMPIntegratedClient.notify` method +to send it to a specific client. + +Once finished, we should make sure we disconnect from the hub:: + + >>> client.disconnect() + +Receiving a Table from DS9 or Aladin +==================================== + +Receiving images over SAMP is identical to `Receiving a Table from TOPCAT`_, +with the exception that the message type should be ``image.load.fits`` instead +of ``table.load.votable``. Once the URL has been received, the FITS image can +be opened with:: + + >>> from astropy.io import fits + >>> fits.open(r.params['url']) diff --git a/docs/samp/index.rst b/docs/samp/index.rst new file mode 100644 index 000000000000..d943e98d3d8b --- /dev/null +++ b/docs/samp/index.rst @@ -0,0 +1,83 @@ +.. doctest-skip-all + +.. _vo-samp: + +************************************************************* +SAMP (Simple Application Messaging Protocol) (`astropy.samp`) +************************************************************* + +`astropy.samp` is a Python implementation of the SAMP messaging system. + +Simple Application Messaging Protocol (SAMP) is an inter-process communication +system that allows different client programs, usually running on the same +computer, to communicate with each other by exchanging short messages that may +reference external data files. The protocol has been developed within the +International Virtual Observatory Alliance (IVOA) and is understood by many +desktop astronomy tools, including |TOPCAT|, `SAO DS9 `_, +and `Aladin `_. + +So by using the classes in `astropy.samp`, Python code can interact with +other running desktop clients, for instance displaying a named FITS file in DS9, +prompting Aladin to recenter on a given sky position, or receiving a message +identifying the row when a user highlights a plotted point in TOPCAT. + +The way the protocol works is that a SAMP "Hub" process must be running on the +local host, and then various client programs can connect to it. Once connected, +these clients can send messages to each other via the hub. The details are +described in the `SAMP standard `_. + +`astropy.samp` provides classes both to set up such a hub process, and to +help implement a client that can send and receive messages. It also provides a +stand-alone program ``samp_hub`` which can run a persistent hub in its own +process. Note that setting up the hub from Python is not always necessary, since +various other SAMP-aware applications may start up a hub independently; in most +cases, only one running hub is used during a SAMP session. + +The following classes are available in `astropy.samp`: + +* |SAMPHubServer|, which is used to instantiate a hub server that clients can + then connect to. +* |SAMPHubProxy|, which is used to connect to an existing hub (including hubs + started from other applications such as |TOPCAT|). +* |SAMPClient|, which is used to create a SAMP client. +* |SAMPIntegratedClient|, which is the same as |SAMPClient| except that it has + a self-contained |SAMPHubProxy| to provide a simpler user interface. + +`astropy.samp` is a full implementation of `SAMP V1.3 +`_. As well as the Standard +Profile, it supports the Web Profile, which means that it can be used to also +communicate with web SAMP clients; see the `sampjs +`_ library examples for more details. + +.. _IVOA Simple Application Messaging Protocol: http://www.ivoa.net/documents/latest/SAMP.html + +Using `astropy.samp` +==================== + +.. toctree:: + :maxdepth: 2 + + example_hub + example_table_image + example_clients + advanced_embed_samp_hub + +.. note that if this section gets too long, it should be moved to a separate + doc page - see the top of performance.inc.rst for the instructions on how to do + that +.. include:: performance.inc.rst + +Reference/API +============= + +.. toctree:: + :maxdepth: 2 + + ref_api + +Acknowledgments +=============== + +This code is adapted from the `SAMPy `__ +package written by Luigi Paioro, who has granted the Astropy Project permission +to use the code under a BSD license. diff --git a/docs/samp/performance.inc.rst b/docs/samp/performance.inc.rst new file mode 100644 index 000000000000..baef1348f042 --- /dev/null +++ b/docs/samp/performance.inc.rst @@ -0,0 +1,12 @@ +.. note that if this is changed from the default approach of using an *include* + (in index.rst) to a separate performance page, the header needs to be changed + from === to ***, the filename extension needs to be changed from .inc.rst to + .rst, and a link needs to be added in the subpackage toctree + +.. _astropy-samp-performance: + +.. Performance Tips +.. ================ +.. +.. Here we provide some tips and tricks for how to optimize performance of code +.. using `astropy.samp`. diff --git a/docs/samp/ref_api.rst b/docs/samp/ref_api.rst new file mode 100644 index 000000000000..bbf24fcf50be --- /dev/null +++ b/docs/samp/ref_api.rst @@ -0,0 +1,4 @@ +Reference/API +************* + +.. automodapi:: astropy.samp diff --git a/docs/stats/circ.rst b/docs/stats/circ.rst new file mode 100644 index 000000000000..2bcec60dc9e1 --- /dev/null +++ b/docs/stats/circ.rst @@ -0,0 +1,15 @@ +.. _stats-circular: + +******************* +Circular Statistics +******************* + +.. automodapi:: astropy.stats.circstats + + +References +---------- +.. [1] S. R. Jammalamadaka, A. SenGupta. "Topics in Circular Statistics". + Series on Multivariate Analysis, Vol. 5, 2001. +.. [2] C. Agostinelli, U. Lund. "Circular Statistics from 'Topics in + Circular Statistics (2001)'". 2015. diff --git a/docs/stats/index.rst b/docs/stats/index.rst new file mode 100644 index 000000000000..bc1170d8c571 --- /dev/null +++ b/docs/stats/index.rst @@ -0,0 +1,213 @@ +.. _stats: + +*************************************** +Astrostatistics Tools (`astropy.stats`) +*************************************** + +Introduction +============ + +The `astropy.stats` package holds statistical functions or algorithms +used in astronomy. While the `scipy.stats` and `statsmodels +`_ packages contains a +wide range of statistical tools, they are general-purpose packages and +are missing some tools that are particularly useful or specific to +astronomy. This package is intended to provide such functionality, +but *not* to replace `scipy.stats` if its implementation satisfies +astronomers' needs. + + +Getting Started +=============== + +A number of different tools are contained in the stats package, and +they can be accessed by importing them:: + + >>> from astropy import stats + +A full list of the different tools are provided below. Please see the +documentation for their different usages. For example, sigma clipping, +which is a common way to estimate the background of an image, can be +performed with the :func:`~astropy.stats.sigma_clip` function. +By default, the function returns a masked array, a type of Numpy array +used for handling missing or invalid entries. Masked arrays retain the +original data but also store another boolean array of the same shape +where ``True`` indicates that the value is masked. Most Numpy ufuncs +will understand masked arrays and treat them appropriately. +For example, consider the following dataset with a clear outlier:: + + >>> import numpy as np + >>> from astropy.stats import sigma_clip + >>> x = np.array([1, 0, 0, 1, 99, 0, 0, 1, 0]) + +The mean is skewed by the outlier:: + + >>> x.mean() + np.float64(11.333333333333334) + +Sigma-clipping (3 sigma by default) returns a masked array, +and so functions like ``mean`` will ignore the outlier:: + + >>> clipped = sigma_clip(x) + >>> clipped + masked_array(data=[1, 0, 0, 1, --, 0, 0, 1, 0], + mask=[False, False, False, False, True, False, False, False, + False], + fill_value=999999) + >>> clipped.mean() + np.float64(0.375) + +If you need to access the original data directly, you can use the +``data`` property. Combined with the ``mask`` property, you can get the +original outliers, or the values that were not clipped:: + + >>> outliers = clipped.data[clipped.mask] + >>> outliers + array([99]) + >>> valid = clipped.data[~clipped.mask] + >>> valid + array([1, 0, 0, 1, 0, 0, 1, 0]) + +For more information on masked arrays, including see the +:ref:`numpy.ma module `. + +Examples +-------- + +.. + EXAMPLE START + Sigma Clipping with Astropy Stats sigma_clip Function + +To estimate the background of an image:: + + >>> data = [1, 5, 6, 8, 100, 5, 3, 2] + >>> data_clipped = stats.sigma_clip(data, sigma=2, maxiters=5) + >>> data_clipped + masked_array(data=[1, 5, 6, 8, --, 5, 3, 2], + mask=[False, False, False, False, True, False, False, False], + fill_value=999999) + >>> np.mean(data_clipped) # doctest: +FLOAT_CMP + np.float64(4.285714285714286) + +.. + EXAMPLE END + +.. + EXAMPLE START + Sigma Clipping with Astropy Stats SigmaClip Class + +Alternatively, the :class:`~astropy.stats.SigmaClip` class provides an +object-oriented interface to sigma clipping, which also returns a +masked array by default:: + + >>> sigclip = stats.SigmaClip(sigma=2, maxiters=5) + >>> sigclip(data) + masked_array(data=[1, 5, 6, 8, --, 5, 3, 2], + mask=[False, False, False, False, True, False, False, False], + fill_value=999999) + +.. + EXAMPLE END + +.. + EXAMPLE START + Calculating Sigma Clipping Statistics + +In addition, there are also several convenience functions for making +the calculation of statistics even more convenient. For example, +:func:`~astropy.stats.sigma_clipped_stats` will return the mean, +median, and standard deviation of a sigma-clipped array:: + + >>> stats.sigma_clipped_stats(data, sigma=2, maxiters=5) # doctest: +FLOAT_CMP + (np.float64(4.285714285714286), np.float64(5.0), np.float64(2.249716535431946)) + +There are also tools for calculating :ref:`robust statistics +`, sampling the data, :ref:`circular statistics +`, confidence limits, spatial statistics, and adaptive +histograms. + +.. + EXAMPLE END + +Most tools are fairly self-contained, and include relevant examples in +their docstrings. + + +Using `astropy.stats` +===================== + +More detailed information on using the package is provided on separate pages, +listed below. + +.. toctree:: + :maxdepth: 2 + + robust.rst + circ.rst + ripley.rst + +Also see :ref:`astropy-visualization-hist`. + + +Constants +========= + +The `astropy.stats` package defines two constants useful for +converting between Gaussian sigma and full width at half maximum +(FWHM): + +.. data:: gaussian_sigma_to_fwhm + + Factor with which to multiply Gaussian 1-sigma standard deviation + to convert it to full width at half maximum (FWHM). + + >>> from astropy.stats import gaussian_sigma_to_fwhm + >>> gaussian_sigma_to_fwhm # doctest: +FLOAT_CMP + 2.3548200450309493 + +.. data:: gaussian_fwhm_to_sigma + + Factor with which to multiply Gaussian full width at half maximum + (FWHM) to convert it to 1-sigma standard deviation. + + >>> from astropy.stats import gaussian_fwhm_to_sigma + >>> gaussian_fwhm_to_sigma # doctest: +FLOAT_CMP + 0.42466090014400953 + + +See Also +======== + +* :mod:`scipy.stats` + This SciPy package contains a variety of useful statistical functions + and classes. The functionality in `astropy.stats` is intended to supplement + this, *not* replace it. + +* `statsmodels `_ + The statsmodels package provides functionality for estimating + different statistical models, tests, and data exploration. + +* `astroML `_ + The astroML package is a Python module for machine learning and + data mining. Some of the tools from this package have been + migrated here, but there are still a number of tools there that + are useful for astronomy and statistical analysis. + +* :func:`astropy.visualization.hist` + The :func:`~astropy.stats.histogram` routine and related functionality + defined here are used within the :func:`astropy.visualization.hist` + function. For a discussion of these methods for determining histogram + binnings, see :ref:`astropy-visualization-hist`. + +.. note that if this section gets too long, it should be moved to a separate + doc page - see the top of performance.inc.rst for the instructions on how to do + that +.. include:: performance.inc.rst + +Reference/API +============= + +.. toctree:: + :maxdepth: 2 + + ref_api diff --git a/docs/stats/performance.inc.rst b/docs/stats/performance.inc.rst new file mode 100644 index 000000000000..dea575690dcc --- /dev/null +++ b/docs/stats/performance.inc.rst @@ -0,0 +1,25 @@ +.. note that if this is changed from the default approach of using an *include* + (in index.rst) to a separate performance page, the header needs to be changed + from === to ***, the filename extension needs to be changed from .inc.rst to + .rst, and a link needs to be added in the subpackage toctree + +.. _astropy-stats-performance: + +Performance Tips +================ + +If you are finding sigma clipping to be slow, and if you have not already done +so, consider installing the `bottleneck `_ +package, which will speed up some of the internal computations. In addition, if +you are using standard functions for ``cenfunc`` and/or ``stdfunc``, make sure +you specify these as strings rather than passing a NumPy function — that is, +use:: + + >>> sigma_clip(array, cenfunc='median') # doctest: +SKIP + +instead of:: + + >>> sigma_clip(array, cenfunc=np.nanmedian) # doctest: +SKIP + +Using strings will allow the sigma-clipping algorithm to pick the fastest +implementation available for finding the median. diff --git a/docs/stats/ref_api.rst b/docs/stats/ref_api.rst new file mode 100644 index 000000000000..c8291a832da3 --- /dev/null +++ b/docs/stats/ref_api.rst @@ -0,0 +1,4 @@ +Reference/API +************* + +.. automodapi:: astropy.stats diff --git a/docs/stats/ripley.rst b/docs/stats/ripley.rst new file mode 100644 index 000000000000..166aaa22be47 --- /dev/null +++ b/docs/stats/ripley.rst @@ -0,0 +1,87 @@ +.. _stats-ripley: + +****************************** +Ripley's K Function Estimators +****************************** + +Spatial correlation functions have been used in the astronomical +context to estimate the probability of finding an object (e.g., a galaxy) +within a given distance of another object [1]_. + +Ripley's K function is a type of estimator used to characterize the correlation +of such spatial point processes +[2]_, [3]_, [4]_, [5]_, [6]_. +More precisely, it describes correlation among objects in a given field. +The `~astropy.stats.RipleysKEstimator` class implements some +estimators for this function which provides several methods for +edge effects correction. + +Basic Usage +=========== + +The actual implementation of Ripley's K function estimators lie in the method +``evaluate``, which take the following arguments: ``data``, ``radii``, and +optionally, ``mode``. + +The ``data`` argument is a 2D array which represents the set of observed +points (events) in the area of study. The ``radii`` argument corresponds to a +set of distances for which the estimator will be evaluated. The ``mode`` +argument takes a value on the following linguistic set +``{none, translation, ohser, var-width, ripley}``; each keyword represents a +different method to perform correction due to edge effects. See the API +documentation and references for details about these methods. + +Instances of `~astropy.stats.RipleysKEstimator` can also be used as +callables (which is equivalent to calling the ``evaluate`` method). + +Example +------- + +.. + EXAMPLE START + Using Ripley's K Function Estimators + +To use Ripley's K Function Estimators from ``astropy``'s stats sub-package: + +.. plot:: + :include-source: + + import numpy as np + from matplotlib import pyplot as plt + from astropy.stats import RipleysKEstimator + + rng = np.random.default_rng() + z = rng.uniform(low=5, high=10, size=(100, 2)) + Kest = RipleysKEstimator(area=25, x_max=10, y_max=10, x_min=5, y_min=5) + + r = np.linspace(0, 2.5, 100) + fig, ax = plt.subplots() + ax.plot(r, Kest.poisson(r), color='green', ls=':', label=r'$K_{pois}$') + ax.plot(r, Kest(data=z, radii=r, mode='none'), color='red', ls='--', + label=r'$K_{un}$') + ax.plot(r, Kest(data=z, radii=r, mode='translation'), color='black', + label=r'$K_{trans}$') + ax.plot(r, Kest(data=z, radii=r, mode='ohser'), color='blue', ls='-.', + label=r'$K_{ohser}$') + ax.plot(r, Kest(data=z, radii=r, mode='var-width'), color='green', + label=r'$K_{var-width}$') + ax.plot(r, Kest(data=z, radii=r, mode='ripley'), color='yellow', + label=r'$K_{ripley}$') + ax.legend() + +.. + EXAMPLE END + +References +========== +.. [1] Peebles, P.J.E. *The large scale structure of the universe*. + +.. [2] Ripley, B.D. *The second-order analysis of stationary point processes*. + Journal of Applied Probability. 13: 255–266, 1976. +.. [3] *Spatial descriptive statistics*. + +.. [4] Cressie, N.A.C. *Statistics for Spatial Data*, Wiley, New York. +.. [5] Stoyan, D., Stoyan, H. *Fractals, Random Shapes and Point Fields*, + Akademie Verlag GmbH, Chichester, 1992. +.. [6] *Correlation function*. + diff --git a/docs/stats/robust.rst b/docs/stats/robust.rst new file mode 100644 index 000000000000..6e619f7737e9 --- /dev/null +++ b/docs/stats/robust.rst @@ -0,0 +1,206 @@ +.. _stats-robust: + +.. testsetup:: + + >>> import numpy as np + >>> np.random.seed(0) + +***************************** +Robust Statistical Estimators +***************************** + +Robust statistics provides reliable estimates of basic statistics for complex +distributions. The statistics package includes several robust statistical +functions that are commonly used in astronomy. This includes methods for +rejecting outliers as well as statistical description of the underlying +distributions. + +In addition to the functions mentioned here, models can be fit with outlier +rejection using :func:`~astropy.modeling.fitting.FittingWithOutlierRemoval`. + +Sigma Clipping +============== + +Sigma clipping provides a fast method for identifying outliers in a +distribution. For a distribution of points, a center and a standard +deviation are calculated. Values which are less or more than a +specified number of standard deviations from a center value are +rejected. The process can be iterated to further reject outliers. + +The `astropy.stats` package provides both a functional and +object-oriented interface for sigma clipping. The function is called +:func:`~astropy.stats.sigma_clip` and the class is called +:class:`~astropy.stats.SigmaClip`. By default, they both return a +masked array where the rejected points are masked. + +Examples +-------- + +.. + EXAMPLE START + Functional Sigma Clipping with astropy.stats.sigma_clip + +We can start by generating some data that has a mean of 0 and standard +deviation of 0.2, but with outliers: + +.. doctest-requires:: scipy + + >>> import numpy as np + >>> import scipy.stats as stats + >>> rng = np.random.default_rng(0) + >>> x = np.arange(200) + >>> y = np.zeros(200) + >>> c = stats.bernoulli.rvs(0.35, size=x.shape) + >>> y += (rng.normal(0., 0.2, x.shape) + + ... c * rng.normal(3.0, 5.0, x.shape)) + +Now we can use :func:`~astropy.stats.sigma_clip` to perform sigma +clipping on the data: + +.. doctest-requires:: scipy + + >>> from astropy.stats import sigma_clip + >>> filtered_data = sigma_clip(y, sigma=3, maxiters=10) + +The output masked array then can be used to calculate statistics on +the data, fit models to the data, or otherwise explore the data. + +.. + EXAMPLE END + +.. + EXAMPLE START + Object-Oriented Sigma Clipping with the astropy.stats.SigmaClip Class + +To perform the same sigma clipping with the +:class:`~astropy.stats.SigmaClip` class: + +.. doctest-requires:: scipy + + >>> from astropy.stats import SigmaClip + >>> sigclip = SigmaClip(sigma=3, maxiters=10) + >>> print(sigclip) # doctest: +SKIP + + sigma: 3 + sigma_lower: None + sigma_upper: None + maxiters: 10 + cenfunc: + stdfunc: + >>> filtered_data = sigclip(y) + +Note that once the ``sigclip`` instance is defined above, it can be +applied to other data using the same already defined sigma-clipping +parameters. + +.. + EXAMPLE END + +For basic statistics, :func:`~astropy.stats.sigma_clipped_stats` is a +convenience function to calculate the sigma-clipped mean, median, and +standard deviation of an array. As can be seen, rejecting the +outliers returns accurate values for the underlying distribution. + +.. + EXAMPLE START + Calculating the Sigma-Clipped Mean, Median, and Standard Deviation of an Array + +To use :func:`~astropy.stats.sigma_clipped_stats` for sigma-clipped statistics +calculation: + +.. doctest-requires:: scipy + + >>> from astropy.stats import sigma_clipped_stats + >>> y.mean(), np.median(y), y.std() # doctest: +FLOAT_CMP + (np.float64(0.7068938765410144), np.float64(0.013567387681385379), np.float64(3.599605215851649)) + >>> sigma_clipped_stats(y, sigma=3, maxiters=10) # doctest: +FLOAT_CMP + (np.float64(-0.0228473012826993), np.float64(-0.02356858871405204), np.float64(0.2079616996908159)) + + +:class:`~astropy.stats.SigmaClippedStats` is a +convenience class that extends the functionality of +:func:`~astropy.stats.sigma_clipped_stats`: + +.. doctest-requires:: scipy + + >>> from astropy.stats import SigmaClippedStats + >>> stats = SigmaClippedStats(y, sigma=3, maxiters=10) + >>> stats.mean(), stats.median(), stats.std() # doctest: +FLOAT_CMP + (np.float64(-0.0228473012826993), np.float64(-0.02356858871405204), np.float64(0.2079616996908159)) + >>> stats.mode(), stats.var(), stats.mad_std() # doctest: +FLOAT_CMP + (np.float64(-0.025011163576757534), np.float64(0.043248068538293126), np.float64(0.21277510956855722)) + >>> stats.biweight_location(), stats.biweight_scale() # doctest: +FLOAT_CMP + (np.float64(-0.0183718864859565), np.float64(0.21730062377965248)) + +:func:`~astropy.stats.sigma_clip` and +:class:`~astropy.stats.SigmaClip` can be combined with other robust +statistics to provide improved outlier rejection as well. + +.. plot:: + :include-source: + + import numpy as np + import scipy.stats as stats + from matplotlib import pyplot as plt + from astropy.stats import sigma_clip, mad_std + + # Generate fake data that has a mean of 0 and standard deviation of 0.2 with outliers + rng = np.random.default_rng(0) + x = np.arange(200) + y = np.zeros(200) + c = stats.bernoulli.rvs(0.35, size=x.shape) + y += (rng.normal(0., 0.2, x.shape) + + c * rng.normal(3.0, 5.0, x.shape)) + + filtered_data = sigma_clip(y, sigma=3, maxiters=1, stdfunc=mad_std) + + # plot the original and rejected data + fig, ax = plt.subplots(figsize=(8, 5)) + ax.plot(x, y, '+', color='#1f77b4', label="original data") + ax.plot(x[filtered_data.mask], y[filtered_data.mask], 'x', + color='#d62728', label="rejected data") + ax.set(xlabel='x', ylabel='y') + ax.legend(loc=2, numpoints=1) + +.. automodapi:: astropy.stats.sigma_clipping + +.. + EXAMPLE END + +Median Absolute Deviation +========================= + +The median absolute deviation (MAD) is a measure of the spread of a +distribution and is defined as ``median(abs(a - median(a)))``. The +MAD can be calculated using `~astropy.stats.median_absolute_deviation`. For a +normal distribution, the MAD is related to the standard deviation by a factor +of 1.4826, and a convenience function, `~astropy.stats.mad_std`, is +available to apply the conversion. + +.. note:: + + A function can be supplied to the + `~astropy.stats.median_absolute_deviation` to specify the median + function to be used in the calculation. Depending on the version + of NumPy and whether the array is masked or contains irregular + values, significant performance increases can be had by + preselecting the median function. If the median function is not + specified, `~astropy.stats.median_absolute_deviation` will attempt + to select the most relevant function according to the input data. + + +Biweight Estimators +=================== + +A set of functions are included in the `astropy.stats` package that use the +biweight formalism. These functions have long been used in astronomy, +particularly to calculate the velocity dispersion of galaxy clusters [1]_. The +following set of tasks are available for biweight measurements: + +.. automodapi:: astropy.stats.biweight + + +References +---------- + +.. [1] Beers, Flynn, and Gebhardt (1990; AJ 100, 32) (https://ui.adsabs.harvard.edu/abs/1990AJ....100...32B) diff --git a/docs/table/access_table.rst b/docs/table/access_table.rst index 3a781e966171..0b25ff7720de 100644 --- a/docs/table/access_table.rst +++ b/docs/table/access_table.rst @@ -1,25 +1,22 @@ .. _access_table: -.. include:: references.txt +Accessing a Table +***************** -Accessing a table ------------------ +Accessing table properties and data is generally consistent with the basic +interface for ``numpy`` `structured arrays +`_. -Accessing the table properties and data is straightforward and is generally consistent with -the basic interface for `numpy` structured arrays. +Basics +====== -Quick overview -^^^^^^^^^^^^^^ - -For the impatient, the code below shows the basics of accessing table data. -Where relevant there is a comment about what sort of object. Except where -noted, the table access returns objects that can be modified in order to -update table data or properties. -In cases where is returned and how -the data contained in that object relate to the original table data -(i.e. whether it is a copy or reference, see :ref:`copy_versus_reference`). +For a quick overview, the code below shows the basics of accessing table data. +Where relevant, there is a comment about what sort of object is returned. +Except where noted, table access returns objects that can be modified in order +to update the original table data or properties. See also the section on +:ref:`copy_versus_reference` to learn more about this topic. -**Make table** +**Make a table** :: from astropy.table import Table @@ -31,7 +28,7 @@ the data contained in that object relate to the original table data **Table properties** :: - t.columns # Dict of table columns + t.columns # Dict of table columns (access by column name, index, or slice) t.colnames # List of column names t.meta # Dict of meta-data len(t) # Number of table rows @@ -41,61 +38,147 @@ the data contained in that object relate to the original table data t['a'] # Column 'a' t['a'][1] # Row 1 of column 'a' - t[1] # Row obj for with row 1 values + t[1] # Row 1 t[1]['a'] # Column 'a' of row 1 + t[1][1:] # Row 1, columns b and c t[2:5] # Table object with rows 2:5 t[[1, 3, 4]] # Table object with rows 1, 3, 4 (copy) t[np.array([1, 3, 4])] # Table object with rows 1, 3, 4 (copy) + t[[]] # Same table definition but with no rows of data t['a', 'c'] # Table with cols 'a', 'c' (copy) dat = np.array(t) # Copy table data to numpy structured array object + t['a'].quantity # an astropy.units.Quantity for Column 'a' + t['a'].to('km') # an astropy.units.Quantity for Column 'a' in units of kilometers + t.columns[1] # Column 1 (which is the 'b' column) + t.columns[0:2] # New table with columns 0 and 1 + +.. Note:: + Although they appear nearly equivalent, there is a factor of two performance + difference between ``t[1]['a']`` (slower, because an intermediate |Row| + object gets created) versus ``t['a'][1]`` (faster). Always use the latter + when possible. **Print table or column** :: - print t # Print formatted version of table to the screen + print(t) # Print formatted version of table to the screen t.pprint() # Same as above - t.pprint(show_units=True) # Show column units + t.pprint(show_unit=True) # Show column unit t.pprint(show_name=False) # Do not show column names - t.pprint(max_lines=-1, max_width=-1) # Print full table no matter how long / wide it is + t.pprint_all() # Print full table no matter how long / wide it is (same as t.pprint(max_lines=-1, max_width=-1)) t.more() # Interactively scroll through table like Unix "more" - print t['a'] # Formatted column values + print(t['a']) # Formatted column values t['a'].pprint() # Same as above, with same options as Table.pprint() - t['a'].more() # Interactively scroll through column + t['a'].more() # Interactively scroll through column + t['a', 'c'].pprint() # Print columns 'a' and 'c' of table lines = t.pformat() # Formatted table as a list of lines (same options as pprint) - lines = t['a'].pformat() # Formatted column valuues as a list + lines = t['a'].pformat() # Formatted column values as a list Details -^^^^^^^ +======= -For all the following examples it is assumed that the table has been created as below:: +For all of the following examples it is assumed that the table has been created +as follows:: >>> from astropy.table import Table, Column >>> import numpy as np + >>> import astropy.units as u - >>> arr = np.arange(15).reshape(5, 3) + >>> arr = np.arange(15, dtype=np.int32).reshape(5, 3) >>> t = Table(arr, names=('a', 'b', 'c'), meta={'keywords': {'key1': 'val1'}}) - >>> t['a'].format = "%6.3f" # print as a float with 3 digits after decimal point - >>> t['a'].units = 'm sec^-1' + >>> t['a'].format = "{:.3f}" # print with 3 digits after decimal point + >>> t['a'].unit = 'm sec^-1' >>> t['a'].description = 'unladen swallow velocity' - >>> print t - a b c - ------ --- --- - 0.000 1 2 - 3.000 4 5 - 6.000 7 8 - 9.000 10 11 - 12.000 13 14 - -Accessing properties -"""""""""""""""""""" + >>> print(t) + a b c + m sec^-1 + -------- --- --- + 0.000 1 2 + 3.000 4 5 + 6.000 7 8 + 9.000 10 11 + 12.000 13 14 + +.. Note:: + + In the example above the ``format``, ``unit``, and ``description`` + attributes of the |Column| were set directly. For :ref:`mixin_columns` like + |Quantity| you must set via the ``info`` attribute, for example, + ``t['a'].info.format = "{:.3f}"``. You can use the ``info`` attribute with + |Column| objects as well, so the general solution that works with any table + column is to set via the ``info`` attribute. See :ref:`mixin_attributes` for + more information. + +.. _table-summary-information: + +Summary Information +------------------- + +You can get summary information about the table as follows:: + + >>> t.info +
+ name dtype unit format description + ---- ----- -------- ------ ------------------------ + a int32 m sec^-1 {:.3f} unladen swallow velocity + b int32 + c int32 + +If called as a function then you can supply an ``option`` that specifies +the type of information to return. The built-in ``option`` choices are +``'attributes'`` (column attributes, which is the default) or ``'stats'`` +(basic column statistics). The ``option`` argument can also be a list +of available options:: + + >>> t.info('stats') # doctest: +FLOAT_CMP +
+ name mean std min max + ---- ---- ------- --- --- + a 6 4.24264 0 12 + b 7 4.24264 1 13 + c 8 4.24264 2 14 + + >>> t.info(['attributes', 'stats']) # doctest: +FLOAT_CMP +
+ name dtype unit format description mean std min max + ---- ----- -------- ------ ------------------------ ---- ------- --- --- + a int32 m sec^-1 {:.3f} unladen swallow velocity 6 4.24264 0 12 + b int32 7 4.24264 1 13 + c int32 8 4.24264 2 14 + +Columns also have an ``info`` property that has the same behavior and +arguments, but provides information about a single column:: + + >>> t['a'].info + name = a + dtype = int32 + unit = m sec^-1 + format = {:.3f} + description = unladen swallow velocity + class = Column + n_bad = 0 + length = 5 + + >>> t['a'].info('stats') # doctest: +FLOAT_CMP + name = a + mean = 6 + std = 4.24264 + min = 0 + max = 12 + n_bad = 0 + length = 5 + + +Accessing Properties +-------------------- The code below shows accessing the table columns as a |TableColumns| object, -getting the column names, table meta-data, and number of table rows. The table -meta-data is simply an ordered dictionary (OrderedDict_) by default. +getting the column names, table metadata, and number of table rows. The table +metadata is a :class:`dict` by default. :: >>> t.columns @@ -111,145 +194,296 @@ meta-data is simply an ordered dictionary (OrderedDict_) by default. 5 -Accessing data -"""""""""""""" +Accessing Data +-------------- -As expected one can access a table column by name and get an element from that +As expected you can access a table column by name and get an element from that column with a numerical index:: >>> t['a'] # Column 'a' - - array([ 0, 3, 6, 9, 12]) + + 0.000 + 3.000 + 6.000 + 9.000 + 12.000 - >>> t['a'][1] # Row 1 of column 'a' - 3 - -When a table column is printed, either with ``print`` or via the ``str()`` -built-in function, it is formatted according to the ``format`` attribute (see -:ref:`table_format_string`):: - >>> print t['a'].description, t['a'] - unladen swallow velocity 0.000, 3.000, 6.000, 9.000, 12.000 + >>> t['a'][1] # Row 1 of column 'a' + np.int32(3) + +When a table column is printed, it is formatted according to the ``format`` +attribute (see :ref:`table_format_string`). Note the difference between the +column representation above and how it appears via ``print()`` or ``str()``:: + + >>> print(t['a']) + a + m sec^-1 + -------- + 0.000 + 3.000 + 6.000 + 9.000 + 12.000 Likewise a table row and a column from that row can be selected:: >>> t[1] # Row object corresponding to row 1 - + + a b c + m sec^-1 + int32 int32 int32 + -------- ----- ----- + 3.000 4 5 >>> t[1]['a'] # Column 'a' of row 1 - 3 + np.int32(3) -A |Row| object has the same columns and meta-data as its parent table:: +A |Row| object has the same columns and metadata as its parent table:: >>> t[1].columns - >>> t[1].colnames - ['a', 'b', 'c'] + >>> t[1].meta + {'keywords': {'key1': 'val1'}} -Slicing a table returns a new table object which references to the original -data within the slice region (See :ref:`copy_versus_reference`). The table -meta-data and column definitions are copied. +Slicing a table returns a new table object with references to the original +data within the slice region (See :ref:`copy_versus_reference`). The table +metadata and column definitions are copied. :: >>> t[2:5] # Table object with rows 2:5 (reference) -
- array([(6, 7, 8), (9, 10, 11), (12, 13, 14)], - dtype=[('a', '>> print t[[1, 3, 4]] # Table object with rows 1, 3, 4 (copy) - a b c - ------ --- --- - 3.000 4 5 - 9.000 10 11 - 12.000 13 14 - - >>> print t[np.array([1, 3, 4])] # Table object with rows 1, 3, 4 (copy) - a b c - ------ --- --- - 3.000 4 5 - 9.000 10 11 - 12.000 13 14 - - >>> print t['a', 'c'] # Table with cols 'a', 'c' (copy) - a c - ------ --- - 0.000 2 - 3.000 5 - 6.000 8 - 9.000 11 - 12.000 14 - -Finally, one can access the underlying table data as a native `numpy` -structured array by creating a copy or reference with ``np.array``:: +
+ a b c + m sec^-1 + int32 int32 int32 + -------- ----- ----- + 6.000 7 8 + 9.000 10 11 + 12.000 13 14 + +It is possible to select table rows with an array of indexes or by specifying +multiple column names. This returns a copy of the original table for the +selected rows or columns. :: + + >>> print(t[[1, 3, 4]]) # Table object with rows 1, 3, 4 (copy) + a b c + m sec^-1 + -------- --- --- + 3.000 4 5 + 9.000 10 11 + 12.000 13 14 + + + >>> print(t[np.array([1, 3, 4])]) # Table object with rows 1, 3, 4 (copy) + a b c + m sec^-1 + -------- --- --- + 3.000 4 5 + 9.000 10 11 + 12.000 13 14 + + + >>> print(t['a', 'c']) # or t[['a', 'c']] or t[('a', 'c')] + ... # Table with cols 'a', 'c' (copy) + a c + m sec^-1 + -------- --- + 0.000 2 + 3.000 5 + 6.000 8 + 9.000 11 + 12.000 14 + +We can select rows from a table using conditionals to create boolean masks. A +table indexed with a boolean array will only return rows where the mask array +element is `True`. Different conditionals can be combined using the bitwise +operators. :: + + >>> mask = (t['a'] > 4) & (t['b'] > 8) # Table rows where column a > 4 + >>> print(t[mask]) # and b > 8 + ... + a b c + m sec^-1 + -------- --- --- + 9.000 10 11 + 12.000 13 14 + +Finally, you can access the underlying table data as a native ``numpy`` +structured array by creating a copy or reference with :func:`numpy.array`:: >>> data = np.array(t) # copy of data in t as a structured array >>> data = np.array(t, copy=False) # reference to data in t -Formatted printing -"""""""""""""""""" +Possibly missing columns +^^^^^^^^^^^^^^^^^^^^^^^^ + +In some cases it might not be guaranteed that a column is present in a table, +but there does exist a good default value that can be used if it is not. The +columns of a |Table| can be represented as a :class:`dict` subclass instance +through the ``columns`` attribute, which means that a replacement for missing +columns can be provided using the :meth:`dict.get` method:: + + >>> t.columns.get("b", np.zeros(len(t))) + + 1 + 4 + 7 + 10 + 13 + >>> t.columns.get("x", np.zeros(len(t))) + array([0., 0., 0., 0., 0.]) + +In case of a single |Row| it is possible to use its +:meth:`~astropy.table.Row.get` method without having to go through +``columns``:: + + >>> row = t[2] + >>> row.get("c", -1) + np.int32(8) + >>> row.get("y", -1) + -1 + + +Table Equality +-------------- + +We can check table data equality using two different methods: + +- The ``==`` comparison operators. In the general case, this returns a 1D array + with ``dtype=bool`` mapping each row to ``True`` if and only if the *entire row* + matches. For incomparable data (different ``dtype`` or unbroacastable lengths), + a boolean ``False`` is returned. + This is in contrast to the behavior of ``numpy`` where trying to compare + structured arrays might raise exceptions. +- Table :meth:`~astropy.table.Table.values_equal` to compare table values + element-wise. This returns a boolean `True` or `False` for each table + *element*, so you get a `~astropy.table.Table` of values. + +.. note:: both methods will report equality *after* broadcasting, which + matches ``numpy`` array comparison. + +Examples +^^^^^^^^ + +.. EXAMPLE START: Checking Table Equality + +To check table equality:: + + >>> t1 = Table(rows=[[1, 2, 3], + ... [4, 5, 6], + ... [7, 7, 9]], names=['a', 'b', 'c']) + >>> t2 = Table(rows=[[1, 2, -1], + ... [4, -1, 6], + ... [7, 7, 9]], names=['a', 'b', 'c']) + + >>> t1 == t2 + array([False, False, True]) + + >>> t1.values_equal(t2) # Compare to another table +
+ a b c + bool bool bool + ---- ----- ----- + True True False + True False True + True True True + + >>> t1.values_equal([2, 4, 7]) # Compare to an array column-wise +
+ a b c + bool bool bool + ----- ----- ----- + False True False + True False False + True True False + + >>> t1.values_equal(7) # Compare to a scalar column-wise +
+ a b c + bool bool bool + ----- ----- ----- + False False False + False False False + True True False + +.. EXAMPLE END + +Formatted Printing +------------------ The values in a table or column can be printed or retrieved as a formatted table using one of several methods: -- `print` statement (Python 2) or `print()` function (Python 3). -- Table :func:`~astropy.table.table.Table.more` or Column - :func:`~astropy.table.table.Column.more` methods to interactively scroll - through table values. -- Table :func:`~astropy.table.table.Table.pprint` or Column - :func:`~astropy.table.table.Column.pprint` methods to print a formatted version of +- `print()` function. +- `Table.more() ` or `Column.more() + ` methods to interactively scroll through + table values. +- `Table.pprint() ` or `Column.pprint() + ` methods to print a formatted version of the table to the screen. -- Table :func:`~astropy.table.table.Table.pformat` or Column - :func:`~astropy.table.table.Column.pformat` methods to return the formatted table - or column as a list of fixed-width strings. This could be used as a quick - way to save a table. - -These methods use column format specifications if available and -strive to make the output readable. By default, table and column printing will -not print the table larger than the available interactive screen size. If the +- `Table.pformat() ` or `Column.pformat() + ` methods to return the formatted table + or column as a list of fixed-width strings. This could be used as a quick way + to save a table. + +These methods use :ref:`table_format_string` +if available and strive to make the output readable. +By default, table and column printing will +not print the table larger than the available interactive screen size. If the screen size cannot be determined (in a non-interactive environment or on -Windows) then a default size of 25 rows by 80 columns is used. If a table is -too large then rows and/or columns are cut from the middle so it fits. For example:: +Windows) then a default size of 25 rows by 80 columns is used. If a table is +too large, then rows and/or columns are cut from the middle so it fits. + +Example +^^^^^^^ + +.. EXAMPLE START: Printing Formatted Tables + +To print a formatted table:: >>> arr = np.arange(3000).reshape(100, 30) # 100 rows x 30 columns array >>> t = Table(arr) - >>> print t - col0 col1 col2 col3 col4 col5 col6 ... col24 col25 col26 col27 col28 col29 - ---- ---- ---- ---- ---- ---- ---- ... ----- ----- ----- ----- ----- ----- - 0 1 2 3 4 5 6 ... 24 25 26 27 28 29 - 30 31 32 33 34 35 36 ... 54 55 56 57 58 59 - 60 61 62 63 64 65 66 ... 84 85 86 87 88 89 - 90 91 92 93 94 95 96 ... 114 115 116 117 118 119 - 120 121 122 123 124 125 126 ... 144 145 146 147 148 149 - 150 151 152 153 154 155 156 ... 174 175 176 177 178 179 - 180 181 182 183 184 185 186 ... 204 205 206 207 208 209 - 210 211 212 213 214 215 216 ... 234 235 236 237 238 239 - 240 241 242 243 244 245 246 ... 264 265 266 267 268 269 - ... ... ... ... ... ... ... ... ... ... ... ... ... ... - 2760 2761 2762 2763 2764 2765 2766 ... 2784 2785 2786 2787 2788 2789 - 2790 2791 2792 2793 2794 2795 2796 ... 2814 2815 2816 2817 2818 2819 - 2820 2821 2822 2823 2824 2825 2826 ... 2844 2845 2846 2847 2848 2849 - 2850 2851 2852 2853 2854 2855 2856 ... 2874 2875 2876 2877 2878 2879 - 2880 2881 2882 2883 2884 2885 2886 ... 2904 2905 2906 2907 2908 2909 - 2910 2911 2912 2913 2914 2915 2916 ... 2934 2935 2936 2937 2938 2939 - 2940 2941 2942 2943 2944 2945 2946 ... 2964 2965 2966 2967 2968 2969 - 2970 2971 2972 2973 2974 2975 2976 ... 2994 2995 2996 2997 2998 2999 + >>> from astropy.table import conf + >>> conf.max_width = 80 + >>> conf.max_lines = 20 + >>> print(t) + col0 col1 col2 col3 col4 col5 col6 ... col23 col24 col25 col26 col27 col28 col29 + ---- ---- ---- ---- ---- ---- ---- ... ----- ----- ----- ----- ----- ----- ----- + 0 1 2 3 4 5 6 ... 23 24 25 26 27 28 29 + 30 31 32 33 34 35 36 ... 53 54 55 56 57 58 59 + 60 61 62 63 64 65 66 ... 83 84 85 86 87 88 89 + 90 91 92 93 94 95 96 ... 113 114 115 116 117 118 119 + 120 121 122 123 124 125 126 ... 143 144 145 146 147 148 149 + 150 151 152 153 154 155 156 ... 173 174 175 176 177 178 179 + 180 181 182 183 184 185 186 ... 203 204 205 206 207 208 209 + 210 211 212 213 214 215 216 ... 233 234 235 236 237 238 239 + 240 241 242 243 244 245 246 ... 263 264 265 266 267 268 269 + 270 271 272 273 274 275 276 ... 293 294 295 296 297 298 299 + ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... + 2700 2701 2702 2703 2704 2705 2706 ... 2723 2724 2725 2726 2727 2728 2729 + 2730 2731 2732 2733 2734 2735 2736 ... 2753 2754 2755 2756 2757 2758 2759 + 2760 2761 2762 2763 2764 2765 2766 ... 2783 2784 2785 2786 2787 2788 2789 + 2790 2791 2792 2793 2794 2795 2796 ... 2813 2814 2815 2816 2817 2818 2819 + 2820 2821 2822 2823 2824 2825 2826 ... 2843 2844 2845 2846 2847 2848 2849 + 2850 2851 2852 2853 2854 2855 2856 ... 2873 2874 2875 2876 2877 2878 2879 + 2880 2881 2882 2883 2884 2885 2886 ... 2903 2904 2905 2906 2907 2908 2909 + 2910 2911 2912 2913 2914 2915 2916 ... 2933 2934 2935 2936 2937 2938 2939 + 2940 2941 2942 2943 2944 2945 2946 ... 2963 2964 2965 2966 2967 2968 2969 + 2970 2971 2972 2973 2974 2975 2976 ... 2993 2994 2995 2996 2997 2998 2999 + Length = 100 rows + + .. EXAMPLE END more() method -''''''''''''' +^^^^^^^^^^^^^ -In order to browse all rows of a table or column use the Table -:func:`~astropy.table.table.Table.more` or Column :func:`~astropy.table.table.Column.more` -methods. These let you interactively scroll through the rows much like the -linux ``more`` command. Once part of the table or column is displayed the -supported navigation keys are: +In order to browse all rows of a table or column use the `Table.more() +` or `Column.more() ` +methods. These let you interactively scroll through the rows much like the Unix +``more`` command. Once part of the table or column is displayed the supported +navigation keys are: | **f, space** : forward one page | **b** : back one page @@ -262,142 +496,530 @@ supported navigation keys are: | **h** : print this help pprint() method -''''''''''''''' +^^^^^^^^^^^^^^^ -In order to fully control the print output use the Table -:func:`~astropy.table.table.Table.pprint` or Column -:func:`~astropy.table.table.Column.pprint` methods. These have keyword -arguments ``max_lines``, ``max_width``, ``show_name``, ``show_units`` with -meaning as shown below:: +In order to fully control the print output use the `Table.pprint() +` or `Column.pprint() +` methods. These have keyword arguments +``max_lines``, ``max_width``, ``show_name``, ``show_unit``, and +``show_dtype``, with meanings as shown below:: >>> arr = np.arange(3000, dtype=float).reshape(100, 30) >>> t = Table(arr) >>> t['col0'].format = '%e' - >>> t['col1'].format = '%.6f' - >>> t['col0'].units = 'km**2' - >>> t['col29'].units = 'kg sec m**-2' + >>> t['col0'].unit = 'km**2' + >>> t['col29'].unit = 'kg sec m**-2' >>> t.pprint(max_lines=8, max_width=40) - col0 col1 ... col29 - ------------ ----------- ... ------ - 0.000000e+00 1.000000 ... 29.0 - 3.000000e+01 31.000000 ... 59.0 - 6.000000e+01 61.000000 ... 89.0 - ... ... ... ... - 2.940000e+03 2941.000000 ... 2969.0 - 2.970000e+03 2971.000000 ... 2999.0 - - >>> t.pprint(max_lines=8, max_width=40, show_units=True) col0 ... col29 - km**2 ... kg sec m**-2 + km2 ... kg sec m**-2 ------------ ... ------------ 0.000000e+00 ... 29.0 - 3.000000e+01 ... 59.0 ... ... ... 2.940000e+03 ... 2969.0 2.970000e+03 ... 2999.0 + Length = 100 rows + + >>> t.pprint(max_lines=8, max_width=40, show_unit=False) + col0 ... col29 + ------------ ... ------ + 0.000000e+00 ... 29.0 + ... ... ... + 2.940000e+03 ... 2969.0 + 2.970000e+03 ... 2999.0 + Length = 100 rows >>> t.pprint(max_lines=8, max_width=40, show_name=False) - 0.000000e+00 1.000000 ... 29.0 - 3.000000e+01 31.000000 ... 59.0 - 6.000000e+01 61.000000 ... 89.0 - 9.000000e+01 91.000000 ... 119.0 - ... ... ... ... - 2.910000e+03 2911.000000 ... 2939.0 - 2.940000e+03 2941.000000 ... 2969.0 - 2.970000e+03 2971.000000 ... 2999.0 + km2 ... kg sec m**-2 + ------------ ... ------------ + 0.000000e+00 ... 29.0 + 3.000000e+01 ... 59.0 + ... ... ... + 2.940000e+03 ... 2969.0 + 2.970000e+03 ... 2999.0 + Length = 100 rows + + >>> t.pprint(max_lines=8, max_width=40, show_dtype=True) + col0 col1 ... col29 + km2 ... kg sec m**-2 + float64 float64 ... float64 + ------------ ------- ... ------------ + 0.000000e+00 1.0 ... 29.0 + ... ... ... ... + 2.970000e+03 2971.0 ... 2999.0 + Length = 100 rows In order to force printing all values regardless of the output length or width -set ``max_lines`` or ``max_width`` to ``-1``, respectively. For the wide -table in this example one sees 6 lines of wrapped output like the following:: - - >>> t.pprint(max_lines=6, max_width=-1) - - col0 col1 col2 col3 col4 col5 col6 col7 col8 col - 9 col10 col11 col12 col13 col14 col15 col16 col17 col18 col19 col20 - col21 col22 col23 col24 col25 col26 col27 col28 col29 - ------------ ----------- ------ ------ ------ ------ ------ ------ ------ ---- - -- ------ ------ ------ ------ ------ ------ ------ ------ ------ ------ ----- - - ------ ------ ------ ------ ------ ------ ------ ------ ------ - 0.000000e+00 1.000000 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9 - .0 10.0 11.0 12.0 13.0 14.0 15.0 16.0 17.0 18.0 19.0 20. - 0 21.0 22.0 23.0 24.0 25.0 26.0 27.0 28.0 29.0 - 3.000000e+01 31.000000 32.0 33.0 34.0 35.0 36.0 37.0 38.0 39 - .0 40.0 41.0 42.0 43.0 44.0 45.0 46.0 47.0 48.0 49.0 50. - 0 51.0 52.0 53.0 54.0 55.0 56.0 57.0 58.0 59.0 - ... ... ... ... ... ... ... ... ... . - .. ... ... ... ... ... ... ... ... ... ... .. - . ... ... ... ... ... ... ... ... ... - 2.970000e+03 2971.000000 2972.0 2973.0 2974.0 2975.0 2976.0 2977.0 2978.0 2979 - .0 2980.0 2981.0 2982.0 2983.0 2984.0 2985.0 2986.0 2987.0 2988.0 2989.0 2990. - 0 2991.0 2992.0 2993.0 2994.0 2995.0 2996.0 2997.0 2998.0 2999.0 - -For columns the syntax and behavior of -:func:`~astropy.table.table.Column.pprint` is the same except that there is no -``max_width`` keyword argument:: +use :meth:`~astropy.table.Table.pprint_all`, which is equivalent to setting +``max_lines`` and ``max_width`` to ``-1`` in :meth:`~astropy.table.Table.pprint`. +:meth:`~astropy.table.Table.pprint_all` takes the same arguments as :meth:`~astropy.table.Table.pprint`. +For the wide table in this example you see six lines of wrapped output like the +following:: + + >>> t.pprint_all(max_lines=8) # doctest: +SKIP + col0 col1 col2 col3 col4 col5 col6 col7 col8 col9 col10 col11 col12 col13 col14 col15 col16 col17 col18 col19 col20 col21 col22 col23 col24 col25 col26 col27 col28 col29 + km2 kg sec m**-2 + ------------ ----------- ------ ------ ------ ------ ------ ------ ------ ------ ------ ------ ------ ------ ------ ------ ------ ------ ------ ------ ------ ------ ------ ------ ------ ------ ------ ------ ------ ------------ + 0.000000e+00 1.000000 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 10.0 11.0 12.0 13.0 14.0 15.0 16.0 17.0 18.0 19.0 20.0 21.0 22.0 23.0 24.0 25.0 26.0 27.0 28.0 29.0 + ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... + 2.940000e+03 2941.000000 2942.0 2943.0 2944.0 2945.0 2946.0 2947.0 2948.0 2949.0 2950.0 2951.0 2952.0 2953.0 2954.0 2955.0 2956.0 2957.0 2958.0 2959.0 2960.0 2961.0 2962.0 2963.0 2964.0 2965.0 2966.0 2967.0 2968.0 2969.0 + 2.970000e+03 2971.000000 2972.0 2973.0 2974.0 2975.0 2976.0 2977.0 2978.0 2979.0 2980.0 2981.0 2982.0 2983.0 2984.0 2985.0 2986.0 2987.0 2988.0 2989.0 2990.0 2991.0 2992.0 2993.0 2994.0 2995.0 2996.0 2997.0 2998.0 2999.0 + Length = 100 rows + +For columns, the syntax and behavior of :func:`~astropy.table.Column.pprint` is +the same except that there is no ``max_width`` keyword argument:: >>> t['col3'].pprint(max_lines=8) col3 ------ 3.0 33.0 - 63.0 ... 2943.0 2973.0 + Length = 100 rows + +Column alignment +^^^^^^^^^^^^^^^^ + +Individual columns have the ability to be aligned in a number of different +ways for an enhanced viewing experience:: + + >>> t1 = Table() + >>> t1['long column name 1'] = [1, 2, 3] + >>> t1['long column name 2'] = [4, 5, 6] + >>> t1['long column name 3'] = [7, 8, 9] + >>> t1['long column name 4'] = [700000, 800000, 900000] + >>> t1['long column name 2'].info.format = '<' + >>> t1['long column name 3'].info.format = '0=' + >>> t1['long column name 4'].info.format = '^' + >>> t1.pprint() + long column name 1 long column name 2 long column name 3 long column name 4 + ------------------ ------------------ ------------------ ------------------ + 1 4 000000000000000007 700000 + 2 5 000000000000000008 800000 + 3 6 000000000000000009 900000 + +Conveniently, alignment can be handled another way — by passing a list to the +keyword argument ``align``:: + + >>> t1 = Table() + >>> t1['column1'] = [1, 2, 3] + >>> t1['column2'] = [2, 4, 6] + >>> t1.pprint(align=['<', '0=']) + column1 column2 + ------- ------- + 1 0000002 + 2 0000004 + 3 0000006 + +It is also possible to set the alignment of all columns with a single +string value:: + + >>> t1.pprint(align='^') + column1 column2 + ------- ------- + 1 2 + 2 4 + 3 6 + +The fill character for justification can be set as a prefix to the +alignment character (see `Format Specification Mini-Language +`_ +for additional explanation). This can be done both in the ``align`` argument +and in the column ``format`` attribute. Note the interesting interaction below:: + + >>> t1 = Table([[1.0, 2.0], [1, 2]], names=['column1', 'column2']) + + >>> t1['column1'].format = '#^.2f' + >>> t1.pprint() + column1 column2 + ------- ------- + ##1.00# 1 + ##2.00# 2 + +Now if we set a global align, it seems like our original column format +got lost:: + + >>> t1.pprint(align='!<') + column1 column2 + ------- ------- + 1.00!!! 1!!!!!! + 2.00!!! 2!!!!!! + +The way to avoid this is to explicitly specify the alignment strings +for every column and use `None` where the column format should be +used:: + + >>> t1.pprint(align=[None, '!<']) + column1 column2 + ------- ------- + ##1.00# 1!!!!!! + ##2.00# 2!!!!!! pformat() method -'''''''''''''''' +^^^^^^^^^^^^^^^^ In order to get the formatted output for manipulation or writing to a file use -the Table :func:`~astropy.table.table.Table.pformat` or Column -:func:`~astropy.table.table.Column.pformat` methods. These behave just as for -:func:`~astropy.table.table.Table.pprint` but return a list corresponding to each formatted line in the -:func:`~astropy.table.table.Table.pprint` output. +the `Table.pformat() ` or `Column.pformat() +` methods. These behave just as for +:meth:`~astropy.table.Table.pprint` but return a list corresponding to each +formatted line in the :meth:`~astropy.table.Table.pprint` output. The +:meth:`~astropy.table.Table.pformat_all` method can be used to return a list +for all lines in the |Table|. >>> lines = t['col3'].pformat(max_lines=8) - >>> lines - [' col3', '------', ' 3.0', ' 33.0', ' 63.0', ' ...', '2943.0', '2973.0'] + +Hiding columns +^^^^^^^^^^^^^^ + +The |Table| class has functionality to selectively show or hide certain columns +within the table when using any of the print methods. This can be useful for +columns that are very wide or else "uninteresting" for various reasons. The +specification of which columns are outputted is associated with the table itself +so that it persists through slicing, copying, and serialization (e.g. saving to +:ref:`ecsv_format`). One use case is for specialized table subclasses that +contain auxiliary columns that are not typically useful to the user. + +The specification of which columns to include when printing is handled through +two complementary |Table| attributes: + +- `~astropy.table.Table.pprint_include_names`: column names to include, where + the default value of `None` implies including all columns. +- `~astropy.table.Table.pprint_exclude_names`: column names to exclude, where + the default value of `None` implies excluding no columns. + +Typically you should use just one of the two attributes at a time. However, +both can be set at once and the set of columns that actually gets printed +is conceptually expressed in this pseudo-code:: + + include_names = (set(table.pprint_include_names() or table.colnames) + - set(table.pprint_exclude_names() or ()) + +Examples +"""""""" +Let's start with defining a simple table with one row and six columns:: + + >>> from astropy.table.table_helpers import simple_table + >>> t = simple_table(size=1, cols=6) + >>> print(t) + a b c d e f + --- --- --- --- --- --- + 1 1.0 c 4 4.0 f + +Now you can get the value of the ``pprint_include_names`` attribute by calling +it as a function, and then include some names for printing:: + + >>> print(t.pprint_include_names()) + None + >>> t.pprint_include_names = ('a', 'c', 'e') + >>> print(t.pprint_include_names()) + ('a', 'c', 'e') + >>> print(t) + a c e + --- --- --- + 1 c 4.0 + +Now you can instead exclude some columns from printing. Note that for both +include and exclude, you can add column names that do not exist in the table. +This allows pre-defining the attributes before the table has been fully +constructed. +:: + + >>> t.pprint_include_names = None # Revert to printing all columns + >>> t.pprint_exclude_names = ('a', 'c', 'e', 'does-not-exist') + >>> print(t) + b d f + --- --- --- + 1.0 4 f + +Next you can ``add`` or ``remove`` names from the attribute:: + + >>> t = simple_table(size=1, cols=6) # Start with a fresh table + >>> t.pprint_exclude_names.add('b') # Single name + >>> t.pprint_exclude_names.add(['d', 'f']) # List or tuple of names + >>> t.pprint_exclude_names.remove('f') # Single name or list/tuple of names + >>> t.pprint_exclude_names() + ('b', 'd') + +Finally, you can temporarily set the attributes within a `context manager +`_. For +example:: + + >>> t = simple_table(size=1, cols=6) + >>> t.pprint_include_names = ('a', 'b') + >>> print(t) + a b + --- --- + 1 1.0 + + >>> # Show all (for pprint_include_names the value of None => all columns) + >>> with t.pprint_include_names.set(None): + ... print(t) + a b c d e f + --- --- --- --- --- --- + 1 1.0 c 4 4.0 f + +The specification of names for these attributes can include Unix-style globs +like ``*`` and ``?``. See `fnmatch` for details (and in particular how to +escape those characters if needed). For example:: + + >>> t = Table() + >>> t.pprint_exclude_names = ['boring*'] + >>> t['a'] = [1] + >>> t['b'] = ['b'] + >>> t['boring_ra'] = [122.0] + >>> t['boring_dec'] = [89.9] + >>> print(t) + a b + --- --- + 1 b Multidimensional columns -'''''''''''''''''''''''' +^^^^^^^^^^^^^^^^^^^^^^^^ If a column has more than one dimension then each element of the column is -itself an array. In the example below there are 3 rows, each of which is a -``2 x 2`` array. The formatted output for such a column shows only the first +itself an array. In the example below there are three rows, each of which is a +``2 x 2`` array. The formatted output for such a column shows only the first and last value of each row element and indicates the array dimensions in the column name header:: - >>> from astropy.table import Table, Column - >>> import numpy as np >>> t = Table() - >>> arr = [ np.array([[ 1, 2], - ... [10, 20]]), - ... np.array([[ 3, 4], - ... [30, 40]]), - ... np.array([[ 5, 6], - ... [50, 60]]) ] - >>> t.add_column(Column('a', arr)) + >>> arr = [ np.array([[ 1., 2.], + ... [10., 20.]]), + ... np.array([[ 3., 4.], + ... [30., 40.]]), + ... np.array([[ 5., 6.], + ... [50., 60.]]) ] + >>> t['a'] = arr >>> t['a'].shape (3, 2, 2) >>> t.pprint() - a [2,2] - ------- - 1 .. 20 - 3 .. 40 - 5 .. 60 - -In order to see all the data values for a multidimensional column use the -column representation. This uses the standard `numpy` mechanism for printing -any array:: - - >>> t['a'] - - array([[[ 1, 2], - [10, 20]], - - [[ 3, 4], - [30, 40]], - - [[ 5, 6], - [50, 60]]]) + a + ----------- + 1.0 .. 20.0 + 3.0 .. 40.0 + 5.0 .. 60.0 + + +There are two ways to see all of the data values for a multidimensional column. First, +you can set the ``astropy.table.conf.format_size_threshold`` configuration option +to a value greater than or equal to the number of items in each column row (4 in this +case). This respects any formatting options that are defined for the column. + +.. code-block:: python + + >>> from astropy.table import conf + >>> with conf.set_temp("format_size_threshold", 4): + ... t.pprint() + ... + a + ----------------------- + [[1.0 2.0] [10.0 20.0]] + [[3.0 4.0] [30.0 40.0]] + [[5.0 6.0] [50.0 60.0]] + +A second option is to print the column as a ``numpy`` array which uses the +standard ``numpy`` mechanism for printing the array:: + + >>> t['a'].data + array([[[ 1., 2.], + [10., 20.]], + [[ 3., 4.], + [30., 40.]], + [[ 5., 6.], + [50., 60.]]]) + +.. _format_stuctured_array_columns: + +Structured array columns +^^^^^^^^^^^^^^^^^^^^^^^^ + +.. EXAMPLE START: Creating a formatted Astropy Table with a Structured Column + +For columns which are structured arrays, the format string must be a a string +that uses `"new style" format strings +`_ with +parameter substitutions corresponding to the field names in the structured +array. Consider the example below including a column of parameters values where +the value, min and max are stored in the in the column as fields named ``val``, +``min``, and ``max``. By default the field values are shown as a tuple:: + + >>> pars = np.array( + ... [(1.2345678, -20, 3), + ... (12.345678, 4.5678, 33)], + ... dtype=[('val', 'f8'), ('min', 'f8'), ('max', 'f8')] + ... ) + >>> t = Table() + >>> t['a'] = [1, 2] + >>> t['par'] = pars + >>> print(t) + a par [val, min, max] + --- ------------------------- + 1 (1.2345678, -20.0, 3.0) + 2 (12.345678, 4.5678, 33.0) + + +However, setting the format string appropriately allows formatting each of the +field values and controlling the overall output:: + + >>> t['par'].info.format = '{val:6.2f} ({min:5.1f}, {max:5.1f})' + >>> print(t) + a par [val, min, max] + --- --------------------- + 1 1.23 (-20.0, 3.0) + 2 12.35 ( 4.6, 33.0) + +.. EXAMPLE END + +.. _columns_with_units: + +Columns with Units +^^^^^^^^^^^^^^^^^^ + +.. note:: + + |Table| and |QTable| instances handle entries with units differently. The + following describes |Table|. :ref:`quantity_and_qtable` explains how a + |QTable| differs from a |Table|. + +A |Column| object with units within a standard |Table| has certain +quantity-related conveniences available. To begin with, it can be converted +explicitly to a |Quantity| object via the +:attr:`~astropy.table.Column.quantity` property and the +:meth:`~astropy.table.Column.to` method:: + + >>> data = [[1., 2., 3.], [40000., 50000., 60000.]] + >>> t = Table(data, names=('a', 'b')) + >>> t['a'].unit = u.m + >>> t['b'].unit = 'km/s' + >>> t['a'].quantity # doctest: +FLOAT_CMP + + >>> t['b'].to(u.kpc/u.Myr) # doctest: +FLOAT_CMP + + +Note that the :attr:`~astropy.table.Column.quantity` property is actually +a *view* of the data in the column, not a copy. Hence, you can set the +values of a column in a way that respects units by making in-place +changes to the :attr:`~astropy.table.Column.quantity` property:: + + >>> t['b'] + + 40000.0 + 50000.0 + 60000.0 + + >>> t['b'].quantity[0] = 45000000*u.m/u.s + >>> t['b'] + + 45000.0 + 50000.0 + 60000.0 + +Even without explicit conversion, columns with units can be treated like a +|Quantity| in *some* arithmetic expressions (see the warning below for caveats +to this):: + + >>> t['a'] + .005*u.km # doctest: +FLOAT_CMP + + >>> from astropy.constants import c + >>> (t['b'] / c).decompose() # doctest: +FLOAT_CMP + + +.. warning:: + + |Table| columns do *not* always behave the same as |Quantity|. |Table| + columns act more like regular ``numpy`` arrays unless either explicitly + converted to a |Quantity| or combined with a |Quantity| using an arithmetic + operator. For example, the following does not work in the way you would + expect:: + + >>> data = [[30, 90]] + >>> t = Table(data, names=('angle',)) + >>> t['angle'].unit = 'deg' + >>> np.sin(t['angle']) # doctest: +FLOAT_CMP + + -0.988031624093 + 0.893996663601 + + This is wrong both in that it says the result is in degrees, *and* + `~numpy.sin` treated the values as radians rather than degrees. If at all in + doubt that you will get the right result, the safest choice is to either use + |QTable| or to explicitly convert to |Quantity|:: + + >>> np.sin(t['angle'].quantity) # doctest: +FLOAT_CMP + + +.. _bytestring-columns-python-3: + +Bytestring Columns +^^^^^^^^^^^^^^^^^^ + +Using bytestring columns (``numpy`` ``'S'`` dtype) is possible +with ``astropy`` tables since they can be compared with the natural +Python string (``str``) type. See `The bytes/str dichotomy in Python 3 +`_ +for a very brief overview of the difference. + +The standard method of representing strings in ``numpy`` is via the +unicode ``'U'`` dtype. The problem is that this requires 4 bytes per +character, and if you have a very large number of strings this could +fill memory and impact performance. A very common use case is that these +strings are actually ASCII and can be represented with 1 byte per character. +In ``astropy`` it is possible to work directly and conveniently with +bytestring data in |Table| and |Column| operations. + +Note that the bytestring issue is a particular problem when dealing with HDF5 +files, where character data are read as bytestrings (``'S'`` dtype) when using +the :ref:`table_io`. Since HDF5 files are frequently used to store very large +datasets, the memory bloat associated with conversion to ``'U'`` dtype is +unacceptable. + + +Examples +"""""""" + +.. EXAMPLE START: Bytestring Data in Astropy Tables + +The examples below illustrate dealing with bytestring data in ``astropy``:: + + >>> t = Table([['abc', 'def']], names=['a'], dtype=['S']) + + >>> t['a'] == 'abc' # Gives expected answer + array([ True, False]) + + >>> t['a'] == b'abc' # Still gives expected answer + array([ True, False]) + + >>> t['a'][0] == 'abc' # Expected answer + True + + >>> t['a'][0] == b'abc' # Cannot compare to bytestring + False + + >>> t['a'][0] = 'bä' + >>> t +
+ a + bytes3 + ------ + bä + def + + >>> t['a'] == 'bä' + array([ True, False]) + +.. doctest-skip:: + + >>> # Round trip unicode strings through HDF5 + >>> t.write('test.hdf5', format='hdf5', path='data', overwrite=True) + >>> t2 = Table.read('test.hdf5', format='hdf5', path='data') + >>> t2 +
+ col0 + bytes3 + ------ + bä + def + +.. EXAMPLE END diff --git a/docs/table/construct_table.rst b/docs/table/construct_table.rst index 064740a4e485..27c6d2da47d5 100644 --- a/docs/table/construct_table.rst +++ b/docs/table/construct_table.rst @@ -1,94 +1,123 @@ -.. include:: references.txt - .. _construct_table: -Constructing a table --------------------- +Constructing a Table +******************** There is great deal of flexibility in the way that a table can be initially -constructed. Details on the inputs to the |Table| -constructor are in the `Initialization Details`_ section. However, the -easiest way to understand how to make a table is by example. +constructed. Details on the inputs to the |Table| and |QTable| +constructors are in the `Initialization Details`_ section. However, the +best way to understand how to make a table is by example. Examples -^^^^^^^^ - -Much of the flexibility lies in the types of data structures -which can be used to initialize the table data. The examples below show how to -create a table from scratch with no initial data, create a table with a list of -columns, a dictionary of columns, or from `numpy` arrays (either structured or -homogeneous). +======== Setup -""""" -For the following examples you need to import the |Table| and |Column| classes -along with the `numpy` package:: +----- + +For the following examples you need to import the |QTable|, |Table|, and +|Column| classes along with the :ref:`astropy-units` package and the ``numpy`` +package:: - >>> from astropy.table import Table, Column + >>> from astropy.table import QTable, Table, Column + >>> from astropy import units as u >>> import numpy as np -Creating from scratch -""""""""""""""""""""" -A Table can be created without any initial input data or even without any -initial columns. This is useful for building tables dynamically if the initial +Creating from Scratch +--------------------- + +.. EXAMPLE START: Creating an Astropy Table from Scratch + +A |Table| can be created without any initial input data or even without any +initial columns. This is useful for building tables dynamically if the initial size, columns, or data are not known. .. Note:: - Adding columns or rows requires making a new copy of the entire - table table each time, so in the case of large tables this may be slow. + Adding rows requires making a new copy of the entire + table each time, so in the case of large tables this may be slow. + On the other hand, adding columns is fast. :: >>> t = Table() - >>> t.add_column(Column('a', [1, 4])) - >>> t.add_column(Column('b', [2.0, 5.0])) - >>> t.add_column(Column('c', ['x', 'y'])) + >>> t['a'] = [1, 4] + >>> t['b'] = [2.0, 5.0] + >>> t['c'] = ['x', 'y'] - >>> t = Table(names=('a', 'b', 'c'), dtypes=('f4', 'i4', 'S2')) + >>> t = Table(names=('a', 'b', 'c'), dtype=('f4', 'i4', 'S2')) >>> t.add_row((1, 2.0, 'x')) >>> t.add_row((4, 5.0, 'y')) + >>> t = Table(dtype=[('a', 'f4'), ('b', 'i4'), ('c', 'S2')]) + +If your data columns have physical units associated with them then we +recommend using the |QTable| class. This will allow the column to be +stored in the table as a native |Quantity| and bring the full power of +:ref:`astropy-units` to the table. See :ref:`quantity_and_qtable` for details. +:: + + >>> t = QTable() + >>> t['a'] = [1, 4] + >>> t['b'] = [2.0, 5.0] * u.cm / u.s + >>> t['c'] = ['x', 'y'] + >>> type(t['b']) + + +.. EXAMPLE END + +List of Columns +--------------- + +.. EXAMPLE START: Creating an Astropy Table from a List of Columns -List input -"""""""""" A typical case is where you have a number of data columns with the same length -defined in different variables. These might be Python lists or `numpy` arrays -or a mix of the two. These can be used to create a |Table| by putting the column -data variables into a Python list. In this case the column names are not +defined in different variables. These might be Python lists or ``numpy`` arrays +or a mix of the two. These can be used to create a |Table| by putting the column +data variables into a Python list. In this case the column names are not defined by the input data, so they must either be set using the ``names`` -keyword or they will be auto-generated as ``col``. +keyword or they will be automatically generated as ``col``. :: - >>> a = [1, 4] + >>> a = np.array([1, 4], dtype=np.int32) >>> b = [2.0, 5.0] >>> c = ['x', 'y'] >>> t = Table([a, b, c], names=('a', 'b', 'c')) >>> t -
- array([(1, 2.0, 'x'), (4, 5.0, 'y')], - dtype=[('a', ' + a b c + int32 float64 str1 + ----- ------- ---- + 1 2.0 x + 4 5.0 y + +.. EXAMPLE END **Make a new table using columns from the first table** -Once you have a `Table` then you can make new table by selecting columns -and putting this into a Python list, e.g. ``[ t['c'], t['a'] ]``:: +Once you have a |Table|, then you can make a new table by selecting columns +and putting them into a Python list (e.g., ``[ t['c'], t['a'] ]``):: >>> Table([t['c'], t['a']]) -
- array([('x', 1), ('y', 4)], - dtype=[('c', '|S1'), ('a', ' + c a + str1 int32 + ---- ----- + x 1 + y 4 **Make a new table using expressions involving columns** -The |Column| object is derived from the standard `numpy` array and can be used -directly in arithmetic expressions. This allows for a compact way of making a +The |Column| object is derived from the standard |ndarray| and can be used +directly in arithmetic expressions. This allows for a compact way of making a new table with modified column values:: >>> Table([t['a']**2, t['b'] + 10]) -
- array([(1, 12.0), (16, 15.0)], - dtype=[('a', ' + a b + int32 float64 + ----- ------- + 1 12.0 + 16 15.0 **Different types of column data** @@ -96,94 +125,236 @@ new table with modified column values:: The list input method for |Table| is very flexible since you can use a mix of different data types to initialize a table:: - >>> a = (1, 4) - >>> b = np.array([[2, 3], [5, 6]]) # vector column - >>> c = Column('axis', ['x', 'y']) - >>> arr = (a, b, c) - >>> Table(arr) # Data column named "c" has a name "axis" that table -
- array([(1, [2, 3], 'x'), (4, [5, 6], 'y')], - dtype=[('col0', '>> a = (1., 4.) + >>> b = np.array([[2, 3], [5, 6]], dtype=np.int64) # vector column + >>> c = Column(['x', 'y'], name='axis') + >>> d = u.Quantity([([1., 2., 3.], [.1, .2, .3]), + ... ([4., 5., 6.], [.4, .5, .6])], 'm,m/s') + >>> QTable([a, b, c, d]) + + col0 col1 axis col3 [f0, f1] + (m, m / s) + float64 int64[2] str1 (float64[3], float64[3]) + ------- -------- ---- ---------------------------------- + 1.0 2 .. 3 x ([1.0, 2.0, 3.0], [0.1, 0.2, 0.3]) + 4.0 5 .. 6 y ([4.0, 5.0, 6.0], [0.4, 0.5, 0.6]) Notice that in the third column the existing column name ``'axis'`` is used. -Dictionary input -"""""""""""""""" -A dictionary of column data can be used to initialize a |Table|. +Dict of Columns +--------------- + +.. EXAMPLE START: Creating an Astropy Table from a Dictionary of Columns - >>> arr = {'a': [1, 4], +A :class:`dict` of column data can be used to initialize a |Table|:: + + >>> arr = {'a': np.array([1, 4], dtype=np.int32), ... 'b': [2.0, 5.0], ... 'c': ['x', 'y']} >>> >>> Table(arr) -
- array([(1, 'x', 2.0), (4, 'y', 5.0)], - dtype=[('a', ' + a b c + int32 float64 str1 + ----- ------- ---- + 1 2.0 x + 4 5.0 y + +.. EXAMPLE END **Specify the column order and optionally the data types** :: - >>> Table(arr, names=('a', 'b', 'c'), dtypes=('f4', 'i4', 'S2')) -
- array([(1.0, 2, 'x'), (4.0, 5, 'y')], - dtype=[('a', '>> Table(arr, names=('a', 'c', 'b'), dtype=('f8', 'U2', 'i4')) +
+ a c b + float64 str2 int32 + ------- ---- ----- + 1.0 x 2 + 4.0 y 5 **Different types of column data** -The input column data can be any data type that can initialize a |Column| object:: +The input column data can be any data type that can initialize a |Column| +object:: - >>> arr = {'a': (1, 4), - 'b': np.array([[2, 3], [5, 6]]), - 'c': Column('axis', ['x', 'y'])} + >>> arr = {'a': (1., 4.), + ... 'b': np.array([[2, 3], [5, 6]], dtype=np.int64), + ... 'c': Column(['x', 'y'], name='axis')} >>> Table(arr, names=('a', 'b', 'c')) -
- array([(1, [2, 3], 'x'), (4, [5, 6], 'y')], - dtype=[('a', ' + a b c + float64 int64[2] str1 + ------- -------- ---- + 1.0 2 .. 3 x + 4.0 5 .. 6 y -Notice that the key ``'c'`` takes precendence over the existing column name -``'axis'`` in the third column. Also see that the ``'b'`` column is a vector -column where each row element is itself a 2-element array. +Notice that the key ``'c'`` takes precedence over the existing column name +``'axis'`` in the third column. Also see that the ``'b'`` column is a vector +column where each row element is itself a two-element array. **Renaming columns is not possible** :: >>> Table(arr, names=('a_new', 'b_new', 'c_new')) Traceback (most recent call last): - File "", line 2, in - File "astropy/table/table.py", line 404, in __init__ - init_func(data, names, dtypes, n_cols, copy) - File "astropy/table/table.py", line 467, in _init_from_dict - data_list = [data[name] for name in names] + ... KeyError: 'a_new' +.. _Row data: + +List of Rows +------------ + +Row-oriented data can be used to create a table using the ``rows`` +keyword argument. + +**List or tuple of data records** + +If you have row-oriented input data such as a list of records, you +need to use the ``rows`` keyword to create a table:: + + >>> data_rows = [(1, 2.0, 'x'), + ... (4, 5.0, 'y'), + ... (5, 8.2, 'z')] + >>> t = Table(rows=data_rows, names=('a', 'b', 'c')) + >>> print(t) + a b c + --- --- --- + 1 2.0 x + 4 5.0 y + 5 8.2 z + +**List of dict objects** + +You can also initialize a table with row values. This is constructed as a +list of :class:`dict` objects. The keys determine the column names:: + + >>> data = [{'a': 5, 'b': 10}, + ... {'a': 15, 'b': 20}] + >>> t = Table(rows=data) + >>> print(t) + a b + --- --- + 5 10 + 15 20 + +If there are missing keys in one or more rows then the corresponding values +will be marked as missing (masked):: + + >>> t = Table(rows=[{'a': 5, 'b': 10}, {'a': 15, 'c': 50}]) + >>> print(t) + a b c + --- --- --- + 5 10 -- + 15 -- 50 + +You can specify the column order with the ``names`` argument:: + + >>> data = [{'a': 5, 'b': 10}, + ... {'a': 15, 'b': 20}] + >>> t = Table(rows=data, names=('b', 'a')) + >>> print(t) + b a + --- --- + 10 5 + 20 15 + +If ``names`` are not provided then column ordering will be determined by +order in which they appear as the :class:`list` of :class:`dict` is iterated over. + + >>> data = [{'b': 10, 'c': 7, }, + ... {'a': 15, 'c': 35, 'b': 20}] + >>> t = Table(rows=data) + >>> print(t) + b c a + --- --- --- + 10 7 -- + 20 35 15 + +**Single row** + +You can also make a new table from a single row of an existing table:: -NumPy structured array -"""""""""""""""""""""" -The structured array is the standard mechanism in `numpy` for storing heterogenous -table data. Most scientific I/O packages that read table files (e.g. -`PyFITS `_, -`vo.table `_, -`asciitable `_) -will return the table in an object that is based on the structured array. -A structured array can be created using:: + >>> a = [1, 4] + >>> b = [2.0, 5.0] + >>> t = Table([a, b], names=('a', 'b')) + >>> t2 = Table(rows=t[1]) + +Remember that a |Row| has effectively a zero length compared to the +newly created |Table| which has a length of one. This is similar to +the difference between a scalar ``1`` (length 0) and an array such as +``np.array([1])`` with length 1. + +.. Note:: + + In the case of input data as a list of dicts or a single |Table| row, you + can supply the data as the ``data`` argument since these forms + are always unambiguous. For example, ``Table([{'a': 1}, {'a': 2}])`` is + accepted. However, a list of records must always be provided using the + ``rows`` keyword, otherwise it will be interpreted as a list of columns. + +NumPy Structured Array +---------------------- + +The `structured array `_ is +the standard mechanism in ``numpy`` for storing heterogeneous table data. Most +scientific I/O packages that read table files (e.g., `astropy.io.fits`, +`astropy.io.votable`, and `asciitable +`_) will return the table in an +object that is based on the structured array. A structured array can be +created using:: >>> arr = np.array([(1, 2.0, 'x'), ... (4, 5.0, 'y')], - ... dtype=[('a', 'i8'), ('b', 'f8'), ('c', 'S2')]) + ... dtype=[('a', 'i4'), ('b', 'f8'), ('c', 'U2')]) -From ``arr`` it is simple to create the corresponding |Table| object:: +From ``arr`` it is possible to create the corresponding |Table| object:: >>> Table(arr) -
- array([(1, 2.0, 'x'), (4, 5.0, 'y')], - dtype=[('a', ' + a b c + int32 float64 str2 + ----- ------- ---- + 1 2.0 x + 4 5.0 y -Note that in the above example and most the following ones we are creating a -table and immediately asking the interactive Python interpreter to print the -table to see what we made. In real code you might do something like:: +Note that in the above example and most of the following examples we are +creating a table and immediately asking the interactive Python interpreter to +print the table to see what we made. In real code you might do something like:: >>> table = Table(arr) - >>> print table + >>> print(table) + a b c + --- --- --- + 1 2.0 x + 4 5.0 y + +.. _structured-array-as-a-column: + +Structured Array as a Column +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +In some cases it is convenient to include a structured array as a single column +in a table. The `~astropy.coordinates.EarthLocation` class is one case in +astropy where this is done, where the structured column has three elements +``x``, ``y`` and ``z``. Another example would be a modeling parameter that has a +value, a minimum allowed value and a maximum allowed value. Here we demonstrate +including the simple structured array defined previously as a column:: + + >>> table = Table() + >>> table['name'] = ['Micah', 'Mazzy'] + >>> table['arr'] = arr + >>> print(table) + name arr [a, b, c] + ----- ------------- + Micah (1, 2.0, 'x') + Mazzy (4, 5.0, 'y') + +You can access or print a single field in the structured column as follows:: + + >>> print(table['arr']['b']) + [2. 5.] **New column names** @@ -191,30 +362,48 @@ The column names can be changed from the original values by providing the ``names`` argument:: >>> Table(arr, names=('a_new', 'b_new', 'c_new')) -
- array([(1, 2.0, 'x'), (4, 5.0, 'y')], - dtype=[('a_new', ' + a_new b_new c_new + int32 float64 str2 + ----- ------- ----- + 1 2.0 x + 4 5.0 y **New data types** -Likewise the data type for each column can by changed with ``dtypes``:: - - >>> Table(arr, dtypes=('f4', 'i4', 'S4')) -
- array([(1.0, 2, 'x'), (4.0, 5, 'y')], - dtype=[('a', '>> Table(arr, names=('a_new', 'b_new', 'c_new'), dtypes=('f4', 'i4', 'S4')) -
- array([(1.0, 2, 'x'), (4.0, 5, 'y')], - dtype=[('a_new', '>> Table(arr, dtype=('f4', 'i4', 'U4')) +
+ a b c + float32 int32 str4 + ------- ----- ---- + 1.0 2 x + 4.0 5 y + + >>> Table(arr, names=('a_new', 'b_new', 'c_new'), dtype=('f4', 'i4', 'U4')) +
+ a_new b_new c_new + float32 int32 str4 + ------- ----- ----- + 1.0 2 x + 4.0 5 y + +NumPy Homogeneous Array +----------------------- + +A ``numpy`` 1D array is treated as a single row table where each element of the +array corresponds to a column:: + + >>> Table(np.array([1, 2, 3]), names=['a', 'b', 'c'], dtype=('i8', 'i8', 'i8')) +
+ a b c + int64 int64 int64 + ----- ----- ----- + 1 2 3 + +A ``numpy`` 2D array (where all elements have the same type) can also be +converted into a |Table|. In this case the column names are not specified by the data and must either be provided by the user or will be automatically generated as ``col`` where ```` is the column number. @@ -222,365 +411,581 @@ generated as ``col`` where ```` is the column number. :: >>> arr = np.array([[1, 2, 3], - ... [4, 5, 6]]) + ... [4, 5, 6]], dtype=np.int32) >>> Table(arr) -
- array([(1, 2, 3), (4, 5, 6)], - dtype=[('col0', ' + col0 col1 col2 + int32 int32 int32 + ----- ----- ----- + 1 2 3 + 4 5 6 **Column names and types specified** :: - >>> Table(arr, names=('a_new', 'b_new', 'c_new'), dtypes=('f4', 'i4', 'S4')) -
- array([(1.0, 2, '3'), (4.0, 5, '6')], - dtype=[('a_new', '>> Table(arr, names=('a_new', 'b_new', 'c_new'), dtype=('f4', 'i4', 'U4')) +
+ a_new b_new c_new + float32 int32 str4 + ------- ----- ----- + 1.0 2 3 + 4.0 5 6 **Referencing the original data** -It is possible to reference the original data for an homogeneous array as long -as the data types are not changed:: +It is possible to reference the original data as long as the data types are not +changed:: >>> t = Table(arr, copy=False) -**Python arrays versus `numpy` arrays as input** +See the `Copy versus Reference`_ section for more information. + +**Python arrays versus NumPy arrays as input** -There is a slightly subtle issue that is important to understand in the way -that |Table| objects are created. Any data input that looks like a Python list -(including a tuple) is considered to be a list of columns. In contrast an -homogeneous `numpy` array input is interpreted as a list of rows:: +There is a slightly subtle issue that is important to understand about the way +that |Table| objects are created. Any data input that looks like a Python +:class:`list` (including a :class:`tuple`) is considered to be a list of +columns. In contrast, a homogeneous |ndarray| input is interpreted as a list of +rows:: >>> arr = [[1, 2, 3], ... [4, 5, 6]] >>> np_arr = np.array(arr) - >>> Table(arr) # Two columns, three rows -
- array([(1, 4), (2, 5), (3, 6)], - dtype=[('col0', '>> print(Table(arr)) # Two columns, three rows + col0 col1 + ---- ---- + 1 4 + 2 5 + 3 6 - >>> Table(np_arr) # Three columns, two rows -
- array([(1, 2, 3), (4, 5, 6)], - dtype=[('col0', '>> print(Table(np_arr)) # Three columns, two rows + col0 col1 col2 + ---- ---- ---- + 1 2 3 + 4 5 6 This dichotomy is needed to support flexible list input while retaining the -natural interpretation of 2-d `numpy` arrays where the first index corresponds -to data "rows" and the second index corresponds to data "columns". - -If you have a Python list which is structured as a list of data rows, use the -following trick to effectively transpose into a list of columns for -initializing a |Table| object:: - - >>> arr = [[1, 2.0, 'string'], # list of rows - [2, 3.0, 'values']] - >>> col_arr = zip(*arr) # transpose to a list of columns - >>> col_arr - [(1, 2), (2.0, 3.0), ('string', 'values')] - >>> t = Table(col_arr) - -Table columns -""""""""""""" +natural interpretation of 2D ``numpy`` arrays where the first index corresponds +to data "rows" and the second index corresponds to data "columns." + +From an Existing Table +---------------------- + +.. EXAMPLE START: Creating an Astropy Table from an Existing Table + A new table can be created by selecting a subset of columns in an existing table:: >>> t = Table(names=('a', 'b', 'c')) - >>> t2 = t['c', 'b', 'a'] # Makes a copy of the data - >>> print t2 -
- array([], - dtype=[('c', '>> t['c', 'b', 'a'] # Makes a copy of the data +
+ c b a + float64 float64 float64 + ------- ------- ------- + +An alternate way is to use the ``columns`` attribute (explained in the +`TableColumns`_ section) to initialize a new table. This lets you choose columns by their numerical index or name and supports slicing syntax:: >>> Table(t.columns[0:2]) -
- array([], - dtype=[('a', ' + a b + float64 float64 + ------- ------- >>> Table([t.columns[0], t.columns['c']]) -
- array([], - dtype=[('a', ' + a c + float64 float64 + ------- ------- + +To create a copy of an existing table that is empty (has no rows):: + + >>> t = Table([[1.0, 2.3], [2.1, 3]], names=['x', 'y']) + >>> t +
+ x y + float64 float64 + ------- ------- + 1.0 2.1 + 2.3 3.0 + + >>> tcopy = t[:0].copy() + >>> tcopy +
+ x y + float64 float64 + ------- ------- + +.. EXAMPLE END + +Empty Array of a Known Size +--------------------------- + +.. EXAMPLE START: Creating an Astropy Table from an Empty Array + +If you do know the size that your table will be, but do not know the values in +advance, you can create a zeroed |ndarray| and build the |Table| from it:: + + >>> N = 3 + >>> dtype = [('a', 'i4'), ('b', 'f8'), ('c', 'bool')] + >>> t = Table(data=np.zeros(N, dtype=dtype)) + >>> t +
+ a b c + int32 float64 bool + ----- ------- ----- + 0 0.0 False + 0 0.0 False + 0 0.0 False + +For example, you can then fill in this table row by row with values extracted +from another table, or generated on the fly:: + + >>> for i in range(len(t)): + ... t[i] = (i, 2.5*i, i % 2) + >>> t +
+ a b c + int32 float64 bool + ----- ------- ----- + 0 0.0 False + 1 2.5 True + 2 5.0 False + +.. EXAMPLE END + +SkyCoord +-------- + +A |SkyCoord| object can be converted to a |QTable| using its +:meth:`~astropy.coordinates.SkyCoord.to_table` method. For details and examples +see :ref:`skycoord-table-conversion`. + +Pandas DataFrame +---------------- + +The section on :ref:`pandas` gives details on how to initialize a |Table| using +a :class:`pandas.DataFrame` via the :func:`~astropy.table.Table.from_pandas` +class method. This provides a convenient way to take advantage of the many I/O +and table manipulation methods in `pandas `_. + +Comment Lines +------------- + +.. EXAMPLE START: Adding Comment Lines in an ASCII File + +Comment lines in a text file can be added via the ``'comments'`` key in the +table's metadata. The following will insert two comment lines in the output +text file unless ``comment=False`` is explicitly set in ``write()``:: + >>> import sys + >>> from astropy.table import Table + >>> t = Table(names=('a', 'b', 'c'), dtype=('f4', 'i4', 'S2')) + >>> t.add_row((1, 2.0, 'x')) + >>> t.meta['comments'] = ['Here is my explanatory text. This is awesome.', + ... 'Second comment line.'] + >>> t.write(sys.stdout, format='ascii') + # Here is my explanatory text. This is awesome. + # Second comment line. + a b c + 1.0 2 x + +.. EXAMPLE END Initialization Details -^^^^^^^^^^^^^^^^^^^^^^ +====================== A table object is created by initializing a |Table| class object with the following arguments, all of which are optional: -``data`` : numpy ndarray, dict, list, or Table +``data`` : |ndarray|, :class:`dict`, :class:`list`, |Table|, or table-like object, optional Data to initialize table. -``names`` : list - Specify column names -``dtypes`` : list - Specify column data types -``meta`` : dict-like - Meta-Data associated with the table -``copy`` : boolean - Copy the input data (default=True). +``masked`` : :class:`bool`, optional + Specify whether the table is masked. +``names`` : :class:`list`, optional + Specify column names. +``dtype`` : :class:`list`, optional + Specify column data types. +``meta`` : :class:`dict`, optional + Metadata associated with the table. +``copy`` : :class:`bool`, optional + Copy the input data. If the input is a |Table| the ``meta`` is always + copied regardless of the ``copy`` parameter. + Default is `True`. +``rows`` : |ndarray|, :class:`list` of lists, optional + Row-oriented data for table instead of ``data`` argument. +``copy_indices`` : :class:`bool`, optional + Copy any indices in the input data. Default is `True`. +``units`` : :class:`list`, :class:`dict`, optional + List or dict of units to apply to columns. +``descriptions`` : :class:`list`, :class:`dict`, optional + List or dict of descriptions to apply to columns. +``**kwargs`` : :class:`dict`, optional + Additional keyword args when converting table-like object. The following subsections provide further detail on the values and options for each of the keyword arguments that can be used to create a new |Table| object. data -"""" +---- The |Table| object can be initialized with several different forms for the ``data`` argument. -**numpy ndarray (structured array)** +**NumPy ndarray (structured array)** The base column names are the field names of the ``data`` structured - array. The ``names`` list (optional) can be used to select - particular fields and/or reorder the base names. The ``dtypes`` list + array. The ``names`` list (optional) can be used to select + particular fields and/or reorder the base names. The ``dtype`` list (optional) must match the length of ``names`` and is used to override the existing ``data`` types. -**numpy ndarray (homogeneous)** - The ``data`` ndarray must be at least 2-dimensional, with the first - (left-most) index corresponding to row number (table length) and the - second index corresponding to column number (table width). Higher +**NumPy ndarray (homogeneous)** + If the ``data`` is a one-dimensional |ndarray| then it is treated as a + single row table where each element of the array corresponds to a column. + + If the ``data`` is an at least two-dimensional |ndarray|, then the first + (left-most) index corresponds to row number (table length) and the + second index corresponds to column number (table width). Higher dimensions get absorbed in the shape of each table cell. - If provided the ``names`` list must match the "width" of the ``data`` - argument. The default for ``names`` is to auto-generate column names - in the form "col". If provided the ``dtypes`` list overrides the + If provided, the ``names`` list must match the "width" of the ``data`` + argument. The default for ``names`` is to auto-generate column names + in the form ``col``. If provided, the ``dtype`` list overrides the base column types and must match the length of ``names``. **dict-like** - The keys of the ``data`` object define the base column names. The - corresponding values can be Column objects, numpy arrays, or list-like - objects. The ``names`` list (optional) can be used to select - particular fields and/or reorder the base names. The ``dtypes`` list + The keys of the ``data`` object define the base column names. The + corresponding values can be |Column| objects, ``numpy`` arrays, or + list-like objects. The ``names`` list (optional) can be used to select + particular fields and/or reorder the base names. The ``dtype`` list (optional) must match the length of ``names`` and is used to override the existing or default data types. **list-like** - Each item in the ``data`` list provides a column of data values and can - can be a Column object, numpy array, or list-like object. The - ``names`` list defines the name of each column. The names will be - auto-generated if not provided (either from the ``names`` argument or - by Column objects). If provided the ``names`` argument must match the - number of items in the ``data`` list. The optional ``dtypes`` list + Each item in the ``data`` list provides a column of data values and + can be a |Column| object, |ndarray|, or list-like object. The + ``names`` list defines the name of each column. The names will be + auto-generated if not provided (either with the ``names`` argument or + by |Column| objects). If provided, the ``names`` argument must match the + number of items in the ``data`` list. The optional ``dtype`` list will override the existing or default data types and must match ``names`` in length. +**list-of-dicts** + Similar to Python's built-in :class:`csv.DictReader`, each item in the + ``data`` list provides a row of data values and must be a :class:`dict`. + The key values in each :class:`dict` define the column names. The ``names`` + argument may be supplied to specify column ordering. If ``names`` are not + provided then column ordering will be determined by the first :class:`dict` + if it contains values for all the columns, or by sorting the column names + alphabetically if it does not. The ``dtype`` list may be specified, and + must correspond to the order of output columns. + +**Table-like object** + If another table-like object has a ``__astropy_table__()`` method then + that object can be used to directly create a |Table|. See the + `table-like objects`_ section for details. + **None** - Initialize a zero-length table. If ``names`` and optionally ``dtypes`` - are provided then the corresponding columns are created. + Initialize a zero-length table. If ``names`` and optionally ``dtype`` + are provided, then the corresponding columns are created. names -""""" +----- The ``names`` argument provides a way to specify the table column names or -override the existing ones. By default the column names are either taken -from existing names (for ``ndarray`` or ``Table`` input) or auto-generated -as ``col``. If ``names`` is provided then it must be a list with the -same length as the number of columns. Any list elements with value -``None`` fall back to the default name. - -In the case where ``data`` is provided as dict of columns, the ``names`` -argument can be supplied to specify the order of columns. The ``names`` list -must then contain each of the keys in the ``data`` dict. If ``names`` is not -supplied then the order of columns in the output table is not determinate. - -dtypes -"""""" - -The ``dtypes`` argument provides a way to specify the table column data -types or override the existing types. By default the types are either -taken from existing types (for ``ndarray`` or ``Table`` input) or -auto-generated by the ``numpy.array()`` routine. If ``dtypes`` is provided -then it must be a list with the same length as the number of columns. The -values must be valid ``numpy.dtype`` initializers or ``None``. Any list -elements with value ``None`` fall back to the default type. - -In the case where `data` is provided as dict of columns, the ``dtypes`` argument -must be accompanied by a corresponding ``names`` argument in order to uniquely -specify the column ordering. +override the existing ones. By default, the column names are either taken from +existing names (for |ndarray| or |Table| input) or auto-generated as +``col``. If ``names`` is provided, then it must be a list with the same +length as the number of columns. Any list elements with value `None` fall back +to the default name. + +In the case where ``data`` is provided as a :class:`dict` of columns, the +``names`` argument can be supplied to specify the order of columns. The +``names`` list must then contain each of the keys in the ``data`` +:class:`dict`. + +dtype +----- + +The ``dtype`` argument provides a way to specify the table column data types or +override the existing types. By default, the types are either taken from +existing types (for |ndarray| or |Table| input) or auto-generated by the +:func:`numpy.array` routine. If ``dtype`` is provided then it must be a list +with the same length as the number of columns. The values must be valid +:class:`numpy.dtype` initializers or `None`. Any list elements with value +`None` fall back to the default type. meta -"""" +---- -The ``meta`` argument is simply an object that contains meta-data associated -with the table. It is recommended that this object be a dict or -OrderedDict_, but the only firm requirement is that it can be copied with -the standard library ``copy.deepcopy()`` routine. By default ``meta`` is -an empty OrderedDict_. +The ``meta`` argument is an object that contains metadata associated with the +table. It is recommended that this object be a :class:`dict`, but the +only firm requirement is that it *must be a dict-like mapping* and can +be copied with the standard library :func:`copy.deepcopy` routine. By +default, ``meta`` is an empty :class:`dict`. copy -"""" +---- + +In the case where ``data`` is either an |ndarray| object, a :class:`dict`, or +an existing |Table|, it is possible to use a reference to the existing data by +setting ``copy=False``. This has the advantage of reducing memory use and being +faster. However, you should take care because any modifications to the new +|Table| data will also be seen in the original input data. See the `Copy versus +Reference`_ section for more information. + +rows +---- + +This argument allows for providing data as a sequence of rows, in contrast +to the ``data`` keyword, which generally assumes data are a sequence of columns. +The `Row data`_ section provides details. + +copy_indices +------------ -By default the input ``data`` are copied into a new internal ``np.ndarray`` -object in the Table object. In the case where ``data`` is either an -``np.ndarray`` object or an existing ``Table``, it is possible to use a -reference to the existing data by setting ``copy=False``. This has the -advantage of reducing memory use and being faster. However one should take -care because any modifications to the new Table data will also be seen in the -original input data. See the `Copy versus Reference`_ section for more -information. +If you are initializing a |Table| from another |Table| that makes use of +:ref:`table-indexing`, then this option allows copying that table *without* +copying the indices by setting ``copy_indices=False``. By default, the indices +are copied. +units +----- + +This allows for setting the unit for one or more columns at the time of +creating the table. The input can be either a list of unit values corresponding +to each of the columns in the table (using `None` or ``''`` for no unit), or a +:class:`dict` that provides the unit for specified column names. For example:: + + >>> dat = [[1, 2], ['hello', 'world']] + >>> qt = QTable(dat, names=['a', 'b'], units=(u.m, None)) + >>> qt = QTable(dat, names=['a', 'b'], units={'a': u.m}) + +See :ref:`quantity_and_qtable` for why we used a |QTable| here instead of a +|Table|. + +descriptions +------------ + +This allows for setting the description for one or more columns at the time of +creating the table. The input can be either a list of description values +corresponding to each of the columns in the table (using `None` for no +description), or a :class:`dict` that provides the description for specified +column names. This works in the same way as the ``units`` example above. .. _copy_versus_reference: Copy versus Reference -^^^^^^^^^^^^^^^^^^^^^ +===================== -Normally when a new |Table| object is created, the input data are *copied* into -a new internal array object. This ensures that if the new table elements are -modified then the original data will not be affected. However, when creating a -table from a numpy ndarray object (structured or homogeneous), it is possible to -disable copying so that instead a memory reference to the original data is -used. This has the advantage of being faster and using less memory. However, -caution must be exercised because the new table data and original data will be -linked, as shown below:: +Normally when a new |Table| object is created, the input data are *copied*. +This ensures that if the new table elements are modified then the original data +will not be affected. However, when creating a table from an existing |Table|, +a |ndarray| object (structured or homogeneous) or a :class:`dict`, it is +possible to disable copying so that a memory reference to the original data is +used instead. This has the advantage of being faster and using less memory. +However, caution must be exercised because the new table data and original data +will be linked, as shown below:: >>> arr = np.array([(1, 2.0, 'x'), ... (4, 5.0, 'y')], ... dtype=[('a', 'i8'), ('b', 'f8'), ('c', 'S2')]) - >>> arr['a'] # column "a" of the input array - array([1, 4]) + >>> print(arr['a']) # column "a" of the input array + [1 4] >>> t = Table(arr, copy=False) >>> t['a'][1] = 99 - >>> arr['a'] # arr['a'] got changed when we modified t['a'] - array([ 1, 99]) + >>> print(arr['a']) # arr['a'] got changed when we modified t['a'] + [ 1 99] Note that when referencing the data it is not possible to change the data types -since that operation requires making a copy of the data. In this case an error +since that operation requires making a copy of the data. In this case an error occurs:: - >>> t = Table(arr, copy=False, dtypes=('f4', 'i4', 'S4')) + >>> t = Table(arr, copy=False, dtype=('f4', 'i4', 'S4')) Traceback (most recent call last): - File "", line 2, in - File "astropy/table/table.py", line 351, in __init__ - raise ValueError('Cannot specify dtypes when copy=False') - ValueError: Cannot specify dtypes when copy=False - -Another caveat in using referenced data is that you cannot add new row to the -table. This generates an error because of conflict between the two references -to the same underlying memory. Internally, adding a row may involve moving -the data to a new memory location which would corrupt the input data object. -`numpy` does not allow this:: - - >>> t.add_row([1, 2, 3]) - Traceback (most recent call last): - File "", line 1, in - File "astropy/table/table.py", line 760, in add_row - self._data.resize((newlen,), refcheck=False) - ValueError: cannot resize this array: it does not own its data + ... + ValueError: Cannot specify dtype when copy=False +Another caveat to using referenced data is that if you add a new row to the +table, the reference to the original data array is lost and the table will now +instead hold a copy of the original values (in addition to the new row). -Column and TableColumns classes -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Column and TableColumns Classes +=============================== There are two classes, |Column| and |TableColumns|, that are useful when constructing new tables. Column -"""""" +------ A |Column| object can be created as follows, where in all cases the column -``name`` is required as the first argument and one can optionally provide +``name`` should be provided as a keyword argument and you can optionally provide these values: -``description`` : str - Full description of column -``units`` : str - Physical units -``format`` : str - `Format string`_ for outputting column values -``meta`` : dict - Meta-data associated with the column - -Initialization options -'''''''''''''''''''''' +``data`` : :class:`list`, |ndarray| or `None` + Column data values. +``dtype`` : :class:`numpy.dtype` compatible value + Data type for column. +``description`` : :class:`str` + Full description of column. +``unit`` : :class:`str` + Physical unit. +``format`` : :class:`str` or function + `Format specifier`_ for outputting column values. +``meta`` : :class:`dict` + Metadata associated with the column. + +Initialization Options +^^^^^^^^^^^^^^^^^^^^^^ The column data values, shape, and data type are specified in one of two ways: -**Provide a ``data`` value and optionally a ``dtype`` value** +**Provide data but not length or shape** Examples:: - col = Column('a', data=[1, 2, 3]) # shape=(3,) - col = Column('a', data=[[1, 2], [3, 4]]) # shape=(2, 2) - col = Column('a', data=[1, 2, 3], dtype=float) - col = Column('a', np.array([1, 2, 3])) - col = Column('a', ['hello', 'world']) + col = Column([1, 2], name='a') # shape=(2,) + col = Column([[1, 2], [3, 4]], name='a') # shape=(2, 2) + col = Column([1, 2], name='a', dtype=float) + col = Column(np.array([1, 2]), name='a') + col = Column(['hello', 'world'], name='a') - The ``dtype`` argument can be any value which is an acceptable - fixed-size data-type initializer for the numpy.dtype() method. See - ``_. - Examples include: + The ``dtype`` argument can be any value which is an acceptable fixed-size + data type initializer for a :class:`numpy.dtype`. See the reference for + `data type objects + `_. Examples + include: - - Python non-string type (float, int, bool) - - Numpy non-string type (e.g. np.float32, np.int64, np.bool) - - Numpy.dtype array-protocol type strings (e.g. 'i4', 'f8', 'S15') + - Python non-string type (:class:`float`, :class:`int`, :class:`bool`). + - ``numpy`` non-string type (e.g., ``np.float32``, ``np.int64``). + - ``numpy.dtype`` array-protocol type strings (e.g., ``'i4'``, ``'f8'``, ``'U15'``). - If no ``dtype`` value is provide then the type is inferred using - ``np.array(data)``. When ``data`` is provided then the ``shape`` + If no ``dtype`` value is provided, then the type is inferred using + :func:`numpy.array`. When ``data`` is provided then the ``shape`` and ``length`` arguments are ignored. -**Provide zero or more of ``dtype``, ``shape``, ``length``** +**Provide length and optionally shape, but not data** Examples:: - col = Column('a') - col = Column('a', dtype=int, length=10, shape=(3,4)) + col = Column(name='a', length=5) + col = Column(name='a', dtype=int, length=10, shape=(3,4)) + + The default ``dtype`` is ``np.float64``. The ``shape`` argument is the array + shape of a single cell in the column. The default ``shape`` is ``()`` which means + a single value in each element. - The default ``dtype`` is ``np.float64`` and the default ``length`` is - zero. The ``shape`` argument is the array shape of a single cell in the - column. The default ``shape`` is () which means a single value in each - element. +.. note:: + + After setting the type for a column, that type cannot be changed. + If data values of a different type are assigned to the column then they + will be cast to the existing column type. .. _table_format_string: -Format string -''''''''''''' +Format Specifier +^^^^^^^^^^^^^^^^ + +The format specifier controls the output of column values when a table or column +is printed or written to a text table. In the simplest case, it is a string +that can be passed to Python's built-in :func:`format` function. For more +complicated formatting, one can also give "old style" or "new style" +format strings, or even a function: + +**Plain format specification** + +This type of string specifies directly how the value should be formatted +using a `format specification mini-language +`_ that is +quite similar to C. -The format string controls the output of column values when a table or column -is printed or written to an ASCII table. The format string can be either -"old-style" or "new-style": + ``".4f"`` will give four digits after the decimal in float format, or -**Old-style** + ``"6d"`` will give integers in six-character fields. + +**Old style format string** This corresponds to syntax like ``"%.4f" % value`` as documented in -`String formatting operations `_. +`printf-style String Formatting +`_. ``"%.4f"`` to print four digits after the decimal in float format, or - ``"%6d"`` to print an integer in a 6-character wide field. + ``"%6d"`` to print an integer in a six-character wide field. -**New-style** +**New style format string** This corresponds to syntax like ``"{:.4f}".format(value)`` as documented in `format string syntax -`_. +`_. ``"{:.4f}"`` to print four digits after the decimal in float format, or - ``"{:6d}"`` to print an integer in a 6-character wide field. - -Note that in either case any Python format string that formats exactly -one value is valid, so ``{:.4f} angstroms`` or ``Value: %12.2f`` would both work. + ``"{:6d}"`` to print an integer in a six-character wide field. + +Note that in either format string case any Python string that formats exactly +one value is valid, so ``{:.4f} angstroms`` or ``Value: %12.2f`` would both +work. + +**Function** + +.. EXAMPLE START: Initialization Options for Column Objects + +The greatest flexibility can be achieved by setting a formatting function. This +function must accept a single argument (the value) and return a string. One +caveat is that such a format function cannot be saved to file and you will get +an exception if you attempt to do so. In the +following example this is used to make a LaTeX ready output:: + + >>> t = Table([[1,2],[1.234e9,2.34e-12]], names = ('a','b')) + >>> def latex_exp(value): + ... val = f'{value:8.2}' + ... mant, exp = val.split('e') + ... # remove leading zeros + ... exp = exp[0] + exp[1:].lstrip('0') + ... return f'$ {mant} \\times 10^{{ {exp} }}$' + >>> t['b'].format = latex_exp + >>> t['a'].format = '.4f' + >>> import sys + >>> t.write(sys.stdout, format='latex') + \begin{table} + \begin{tabular}{cc} + a & b \\ + 1.0000 & $ 1.2 \times 10^{ +9 }$ \\ + 2.0000 & $ 2.3 \times 10^{ -12 }$ \\ + \end{tabular} + \end{table} + +.. EXAMPLE END + +**Format string for structured array column** + +For columns which are structured arrays, the format string must be a a string +that uses `"new style" format strings +`_ with +parameter substitutions corresponding to the field names in the structured +array. See :ref:`format_stuctured_array_columns` for an example. TableColumns -"""""""""""" +------------ Each |Table| object has an attribute ``columns`` which is an ordered dictionary that stores all of the |Column| objects in the table (see also the `Column`_ -section). Technically the ``columns`` attribute is a |TableColumns| object, +section). Technically, the ``columns`` attribute is a |TableColumns| object, which is an enhanced ordered dictionary that provides easier ways to select -multiple columns. There are a few key points to remember: +multiple columns. There are a few key points to remember: -- A |Table| can be initialized from a |TableColumns| object (copy is always True). +- A |Table| can be initialized from a |TableColumns| object (``copy`` is always + `True`). - Selecting multiple columns from a |TableColumns| object returns another |TableColumns| object. -- Select one column from a |TableColumns| object returns a |Column|. +- Selecting one column from a |TableColumns| object returns a |Column|. -So now look at the ways to select columns from a |TableColumns| object: +There are a few different ways to select columns from a |TableColumns| object: **Select columns by name** :: @@ -599,13 +1004,295 @@ So now look at the ways to select columns from a |TableColumns| object: >>> t.columns[::-1] # Reverse column order -**Select column by index or name** +**Select single columns by index or name** :: - >>> t.columns[1] # Choose columns by index - - array([], dtype=float64) + >>> t.columns[1] # Choose a column by index + + + >>> t.columns['b'] # Choose a column by name + + +.. _subclassing_table: + +Subclassing Table +================= + +For some applications it can be useful to subclass the |Table| class in order +to introduce specialized behavior. Here we address two particular use cases +for subclassing: adding custom table attributes and changing the behavior of +internal class objects. + +.. _table-custom-attributes: + +Adding Custom Table Attributes +------------------------------ + +One simple customization that can be useful is adding new attributes to +the table object. There is nothing preventing setting an attribute on an +existing table object, for example ``t.foo = 'hello'``. However, this attribute +would be ephemeral because it will be lost if the table is sliced, copied, or +pickled. Instead, you can add persistent attributes as shown in this example:: + + from astropy.table import Table, TableAttribute + + class MyTable(Table): + foo = TableAttribute() + bar = TableAttribute(default=[]) + baz = TableAttribute(default=1) + + t = MyTable([[1, 2]], foo='foo') + t.bar.append(2.0) + t.baz = 'baz' + +Some key points: + +- A custom attribute can be set when the table is created or using + the usual syntax for setting an object attribute. +- A custom attribute always has a default value, either explicitly set + in the class definition or `None`. +- The attribute values are stored in the table ``meta`` dictionary. This is + the mechanism by which they are persistent through copy, slice, and + serialization such as pickling or writing to an :ref:`ecsv_format` file. + +Changing Behavior of Internal Class Objects +------------------------------------------- + +It is also possible to change the behavior of the internal class objects which +are contained or created by a |Table|. This includes rows, columns, formatting, +and the columns container. In order to do this the subclass needs to declare +what class to use (if it is different from the built-in version). This is done +by specifying one or more of the class attributes ``Row``, ``Column``, +``MaskedColumn``, ``TableColumns``, or ``TableFormatter``. + +The following trivial example overrides all of these with do-nothing +subclasses, but in practice you would override only the necessary +subcomponents:: + + >>> from astropy.table import Table, Row, Column, MaskedColumn, TableColumns, TableFormatter + + >>> class MyRow(Row): pass + >>> class MyColumn(Column): pass + >>> class MyMaskedColumn(MaskedColumn): pass + >>> class MyTableColumns(TableColumns): pass + >>> class MyTableFormatter(TableFormatter): pass + + >>> class MyTable(Table): + ... """ + ... Custom subclass of astropy.table.Table + ... """ + ... Row = MyRow # Use MyRow to create a row object + ... Column = MyColumn # Column + ... MaskedColumn = MyMaskedColumn # Masked Column + ... TableColumns = MyTableColumns # Ordered dict holding Column objects + ... TableFormatter = MyTableFormatter # Controls table output + + +Example +^^^^^^^ + +.. EXAMPLE START: Subclassing the Table Class + +As a more practical example, suppose you have a table of data with a certain +set of fixed columns, but you also want to carry an arbitrary dictionary of +parameters for each row and then access those values using the same item access +syntax as if they were columns. It is assumed here that the extra parameters +are contained in a ``numpy`` object-dtype column named ``params``:: + + >>> from astropy.table import Table, Row + >>> class ParamsRow(Row): + ... """ + ... Row class that allows access to an arbitrary dict of parameters + ... stored as a dict object in the ``params`` column. + ... """ + ... def __getitem__(self, item): + ... if item not in self.colnames: + ... return super().__getitem__('params')[item] + ... else: + ... return super().__getitem__(item) + ... + ... def keys(self): + ... out = [name for name in self.colnames if name != 'params'] + ... params = [key.lower() for key in sorted(self['params'])] + ... return out + params + ... + ... def values(self): + ... return [self[key] for key in self.keys()] + +Now we put this into action with a trivial |Table| subclass:: + + >>> class ParamsTable(Table): + ... Row = ParamsRow + +First make a table and add a couple of rows:: + + >>> t = ParamsTable(names=['a', 'b', 'params'], dtype=['i', 'f', 'O']) + >>> t.add_row((1, 2.0, {'x': 1.5, 'y': 2.5})) + >>> t.add_row((2, 3.0, {'z': 'hello', 'id': 123123})) + >>> print(t) + a b params + --- --- ---------------------------- + 1 2.0 {'x': 1.5, 'y': 2.5} + 2 3.0 {'z': 'hello', 'id': 123123} + +Now see what we have from our specialized ``ParamsRow`` object:: + + >>> t[0]['y'] + 2.5 + >>> t[1]['id'] + 123123 + >>> t[1].keys() + ['a', 'b', 'id', 'z'] + >>> t[1].values() + [np.int32(2), np.float32(3.0), 123123, 'hello'] + +To make this example really useful, you might want to override +``Table.__getitem__()`` in order to allow table-level access to the parameter +fields. This might look something like:: + + class ParamsTable(table.Table): + Row = ParamsRow + + def __getitem__(self, item): + if isinstance(item, str): + if item in self.colnames: + return self.columns[item] + else: + # If item is not a column name then create a new MaskedArray + # corresponding to self['params'][item] for each row. This + # might not exist in some rows so mark as masked (missing) in + # those cases. + mask = np.zeros(len(self), dtype=np.bool_) + item = item.upper() + values = [params.get(item) for params in self['params']] + for ii, value in enumerate(values): + if value is None: + mask[ii] = True + values[ii] = '' + return self.MaskedColumn(name=item, data=values, mask=mask) + + # ... and then the rest of the original __getitem__ ... + +.. EXAMPLE END + +Columns and Quantities +====================== + +.. EXAMPLE START: Handling Astropy Column and Quantity Objects within Tables + +``astropy`` `~astropy.units.Quantity` objects can be handled within tables in +two complementary ways. The first method stores the `~astropy.units.Quantity` +object natively within the table via the "mixin" column protocol. See the +sections on :ref:`mixin_columns` and :ref:`quantity_and_qtable` for details, +but in brief, the key difference is using the `~astropy.table.QTable` class to +indicate that a `~astropy.units.Quantity` should be stored natively within the +table:: - >>> t.columns['b'] # Choose column by name - - array([], dtype=float64) + >>> from astropy.table import QTable + >>> from astropy import units as u + >>> t = QTable() + >>> t['velocity'] = [3, 4] * u.m / u.s + >>> type(t['velocity']) + + +For new code that is quantity-aware we recommend using `~astropy.table.QTable`, +but this may not be possible in all situations (particularly when interfacing +with legacy code that does not handle quantities) and there are +:ref:`details_and_caveats` that apply. In this case, use the +`~astropy.table.Table` class, which will convert a `~astropy.units.Quantity` to +a `~astropy.table.Column` object with a ``unit`` attribute:: + + >>> from astropy.table import Table + >>> t = Table() + >>> t['velocity'] = [3, 4] * u.m / u.s + >>> type(t['velocity']) + + >>> t['velocity'].unit + Unit("m / s") + +To learn more about using standard `~astropy.table.Column` objects with defined +units, see the :ref:`columns_with_units` section. + +.. EXAMPLE END + +.. _Table-like Objects: + +Table-like Objects +================== + +In order to improve interoperability between different table classes, an +``astropy`` |Table| object can be created directly from any other table-like +object that provides an ``__astropy_table__()`` method. In this case the +``__astropy_table__()`` method will be called as follows:: + + >>> data = SomeOtherTableClass({'a': [1, 2], 'b': [3, 4]}) # doctest: +SKIP + >>> t = QTable(data, copy=False, mask_invalid=True) # doctest: +SKIP + +Internally the following call will be made to ask the ``data`` object +to return a representation of itself as an ``astropy`` |Table|, respecting +the ``copy`` preference of the original call to ``QTable()``:: + + data.__astropy_table__(cls, copy, **kwargs) + +Here ``cls`` is the |Table| class or subclass that is being instantiated +(|QTable| in this example), ``copy`` indicates whether a copy of the values in +``data`` should be provided, and ``**kwargs`` are any extra keyword arguments +which are not valid |Table| ``__init__()`` keyword arguments. In the example +above, ``mask_invalid=True`` would end up in ``**kwargs`` and get passed to +``__astropy_table__()``. + +The implementation might choose to allow additional keyword arguments (e.g., +``mask_invalid`` which gets passed via ``**kwargs``). + +As a concise example, imagine a dict-based table class. (Note that |Table| +already can be initialized from a dict-like object, so this is a bit contrived +but does illustrate the principles involved.) Please pay attention to the +method signature:: + + def __astropy_table__(self, cls, copy, **kwargs): + +Your class implementation of this must use the ``**kwargs`` technique for +catching keyword arguments at the end. This is to ensure future compatibility +in case additional keywords are added to the internal ``table = +data.__astropy_table__(cls, copy)`` call. Including ``**kwargs`` will prevent +breakage in this case. :: + + class DictTable(dict): + """ + Trivial "table" class that just uses a dict to hold columns. + This does not actually implement anything useful that makes + this a table. + + The non-standard ``mask_invalid=False`` keyword arg here will be passed + via the **kwargs of Table __init__(). + """ + + def __astropy_table__(self, cls, copy, mask_invalid=False, **kwargs): + """ + Return an astropy Table of type ``cls``. + + Parameters + ---------- + cls : type + Astropy ``Table`` class or subclass. + copy : bool + Copy input data (True) or return a reference (False). + mask_invalid : bool, optional + Controls whether invalid values (NaNs) should be masked. + Default is False. + **kwargs : dict, optional + Additional keyword args (ignored currently). + """ + if kwargs: + warnings.warn(f'unexpected keyword args {kwargs}') + + cols = list(self.values()) + names = list(self.keys()) + + if mask_invalid: + cols = [ + Masked(col, mask=mask) if np.any(mask := np.isnan(col)) else col + for col in cols + ] + + return cls(cols, names=names, copy=copy) diff --git a/docs/table/dataframes.rst b/docs/table/dataframes.rst new file mode 100644 index 000000000000..f01a1e01b0e6 --- /dev/null +++ b/docs/table/dataframes.rst @@ -0,0 +1,283 @@ +.. doctest-skip-all + +.. _df_narwhals: + +Interfacing with DataFrames +*************************** + +The :class:`~astropy.table.Table` class provides comprehensive support for interfacing with DataFrame libraries through two complementary approaches: + +1. **Generic narwhals-based methods**: :meth:`~astropy.table.Table.to_df` and :meth:`~astropy.table.Table.from_df` for multi-backend DataFrame support +2. **Legacy pandas-specific methods**: :meth:`~astropy.table.Table.to_pandas` and :meth:`~astropy.table.Table.from_pandas` for direct pandas integration + +The narwhals-based approach uses `Narwhals `_ as a unified translation layer, enabling seamless interoperability with multiple DataFrame libraries including `pandas `_, `polars `__, `pyarrow `__, and others. + +Generic Multi-Backend Methods +============================= + +For users working with multiple DataFrame libraries or seeking broader compatibility, the generic methods provide a unified interface through narwhals. + +Supported Backends +------------------ + +The generic methods support DataFrame libraries with eager execution, including: + +* **pandas** - A popular and pioneering DataFrame library +* **polars** - High-performance DataFrame library with different handling of multidimensional data +* **pyarrow** - In-memory columnar format with good performance +* **modin** - Distributed pandas-compatible DataFrames +* **cudf** - GPU-accelerated DataFrames + +.. note:: + **Testing and Support**: **pandas**, **polars**, and **pyarrow** are directly tested in the Astropy test suite. While other narwhals-compatible backends should work in principle, they may exhibit unexpected behavior or incompatibilities. If you encounter issues with any backend, please file a bug report on the `Astropy GitHub repository `_. + +.. warning:: + **Backend Differences**: Different DataFrame libraries implement varying data models, type systems, and computational paradigms. These fundamental differences can lead to inconsistent behavior across backends, particularly with respect to data type handling, missing value representation, and memory layout. Users should verify that round-trip conversions preserve the expected data integrity for their specific use case and chosen backend. + +Basic Multi-Backend Example +--------------------------- + +.. EXAMPLE START: Using Generic Multi-Backend Methods + +Create a table and convert to different DataFrame backends:: + + >>> from astropy.table import Table + >>> t = Table() + >>> t['a'] = [1, 2, 3, 4] + >>> t['b'] = ['a', 'b', 'c', 'd'] + + # Convert to pandas DataFrame + >>> df_pandas = t.to_df("pandas") + >>> type(df_pandas) + + + # Convert to polars DataFrame + >>> df_polars = t.to_df("polars") + >>> type(df_polars) + + +Create a table from any supported DataFrame:: + + >>> t2 = Table.from_df(df_pandas) # From pandas + >>> t3 = Table.from_df(df_polars) # From polars + +.. EXAMPLE END + +Known Backend-Specific Differences +---------------------------------- + +Different DataFrame backends handle data differently: + +**Multidimensional Columns:** + - Pandas: Not supported, raises an error + - Polars: Supported as Array type for arbitrary dimensions + - PyArrow: Limited support for 1D arrays, currently unavailable. + +**Index Support:** + - Pandas: Full index support with :meth:`~astropy.table.Table.to_df` + - Other backends: Index parameter raises an error (not supported) + +**Missing Value Handling:** + - All backends use sentinel values (NaN, null) rather than Astropy's mask arrays + +Pandas-Specific Methods +======================= + +For pandas users, Astropy provides dedicated methods that offer the most direct and feature-complete integration with pandas DataFrames. + +Basic Pandas Example +-------------------- + +.. EXAMPLE START: Using Pandas-Specific Methods + +To demonstrate, we can create a minimal table:: + + >>> from astropy.table import Table + >>> t = Table() + >>> t['a'] = [1, 2, 3, 4] + >>> t['b'] = ['a', 'b', 'c', 'd'] + +Convert to a pandas DataFrame using the pandas-specific method:: + + >>> df = t.to_pandas() + >>> df + a b + 0 1 a + 1 2 b + 2 3 c + 3 4 d + >>> type(df) + + +Create a table from a pandas DataFrame:: + + >>> t2 = Table.from_pandas(df) + >>> t2 +
+ a b + int64 string8 + ----- ------- + 1 a + 2 b + 3 c + 4 d + +.. EXAMPLE END + +Pandas Index Support +-------------------- + +The pandas-specific methods provide full support for DataFrame indexing, which is a unique pandas feature:: + + >>> from astropy.time import Time + >>> tm = Time([1998, 2002], format="jyear") + >>> x = [1, 2] + >>> t = Table([tm, x], names=["tm", "x"]) + + # Use a column as the DataFrame index + >>> df = t.to_pandas(index="tm") + >>> df.index.name + 'tm' + + # Convert back including the index as a column + >>> t_back = Table.from_pandas(df, index=True) + >>> t_back.colnames + ['tm', 'x'] + +Pandas Excel Support +-------------------- + +Read an Excel file into a table by utilizing the pandas backend:: + + >>> t = Table.from_pandas(pandas.read_excel("myexceltable.xlsx")) + +When to Use Which Method +======================== + +**Use pandas-specific methods** (:meth:`~astropy.table.Table.to_pandas`, :meth:`~astropy.table.Table.from_pandas`) when: + +* Working exclusively with pandas +* Need DataFrame index support +* Want the most battle-tested and feature-complete pandas integration +* Require the best performance for pandas-specific workflows + +**Use generic methods** (:meth:`~astropy.table.Table.to_df`, :meth:`~astropy.table.Table.from_df`) when: + +* Working with multiple DataFrame backends +* Need to support polars, pyarrow, or other backends +* Building library code that should work with various DataFrame types +* Want forward compatibility as new backends are added to narwhals + +Conversion Details and Limitations +=================================== + +Both approaches share common limitations when converting between Tables and DataFrames: + +Data Type Limitations +--------------------- + +* **Multidimensional columns**: Support varies by backend. At the time of writing, Pandas does not support them, while Polars can handle them as Array types. + +* **Masked tables**: DataFrames typically use sentinel values (e.g., `numpy.nan` or `None`) for missing data, while Astropy preserves the original value under the mask. The original values under the mask are lost during conversion. + +* **Mixed-type columns**: Object dtype columns have varied support. Pandas preserves them while other backends may fail to import such data. + +Mixin Column Limitations +------------------------ + +Tables with :ref:`mixin_columns` such as `~astropy.time.Time`, `~astropy.coordinates.SkyCoord`, and |Quantity| can be converted, but **with loss of information**: + +* **Time columns**: Converted to native datetime types with reduced precision and loss of astronomical time scale information +* **SkyCoord columns**: Split into separate coordinate component columns (e.g., ``ra``, ``dec``) with loss of units and coordinate frame information +* **Quantity columns**: Converted to plain numeric columns with complete loss of unit information + +Complex Example with Both Methods +================================= + +.. EXAMPLE START: Complex DataFrame Conversion Example + +Create a table with masked and mixin columns:: + + >>> import numpy as np + >>> from astropy.table import MaskedColumn, QTable + >>> from astropy.time import Time + >>> from astropy.coordinates import SkyCoord + >>> import astropy.units as u + >>> t = QTable() + >>> t['a'] = MaskedColumn([1, 2, 3], mask=[False, True, False]) + >>> t['b'] = MaskedColumn([1.0, 2.0, 3.0], mask=[False, False, True]) + >>> t['c'] = MaskedColumn(["a", "b", "c"], mask=[True, False, False]) + >>> t['tm'] = Time(["2021-01-01", "2021-01-02", "2021-01-03"]) + >>> t['sc'] = SkyCoord(ra=[1, 2, 3] * u.deg, dec=[4, 5, 6] * u.deg) + >>> t['q'] = [1, 2, 3] * u.m + + >>> t + + a b c tm sc q + deg,deg m + int64 float64 str1 Time SkyCoord float64 + ----- ------- ---- ----------------------- -------- ------- + 1 1.0 -- 2021-01-01 00:00:00.000 1.0,4.0 1.0 + -- 2.0 b 2021-01-02 00:00:00.000 2.0,5.0 2.0 + 3 -- c 2021-01-03 00:00:00.000 3.0,6.0 3.0 + +Convert using the pandas-specific method:: + + >>> df_pandas = t.to_pandas() + >>> df_pandas + a b c tm sc.ra sc.dec q + 0 1 1.0 NaN 2021-01-01 1.0 4.0 1.0 + 1 2.0 b 2021-01-02 2.0 5.0 2.0 + 2 3 NaN c 2021-01-03 3.0 6.0 3.0 + +Convert using the generic method to pandas:: + + >>> df_generic = t.to_df("pandas") + >>> # Results are identical to df_pandas + +Convert to polars using the generic method:: + + >>> df_polars = t.to_df("polars") + >>> df_polars + shape: (3, 7) + ┌──────â”Ŧ──────â”Ŧ──────â”Ŧ─────────────────────â”Ŧ───────â”Ŧ────────â”Ŧ─────┐ + │ a ┆ b ┆ c ┆ tm ┆ sc.ra ┆ sc.dec ┆ q │ + │ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │ + │ i64 ┆ f64 ┆ str ┆ datetime[ns] ┆ f64 ┆ f64 ┆ f64 │ + ╞══════â•Ē══════â•Ē══════â•Ē═════════════════════â•Ē═══════â•Ē════════â•Ē═════╡ + │ 1 ┆ 1.0 ┆ null ┆ 2021-01-01 00:00:00 ┆ 1.0 ┆ 4.0 ┆ 1.0 │ + │ null ┆ 2.0 ┆ b ┆ 2021-01-02 00:00:00 ┆ 2.0 ┆ 5.0 ┆ 2.0 │ + │ 3 ┆ null ┆ c ┆ 2021-01-03 00:00:00 ┆ 3.0 ┆ 6.0 ┆ 3.0 │ + └──────┴──────┴──────┴─────────────────────┴───────┴────────┴─────┘ + +Convert back to tables:: + + >>> t_from_pandas = QTable.from_pandas(df_pandas) # Using pandas-specific method + >>> t_from_generic = QTable.from_df(df_pandas) # Using generic method + >>> t_from_polars = QTable.from_df(df_polars) # From polars DataFrame + +Note the data transformations that occurred: + +1. **Masked values**: Original values under the mask are lost and replaced with sentinel values +2. **Time columns**: Converted to basic datetime objects, preserving temporal information but losing astropy Time features +3. **SkyCoord columns**: Split into separate ``ra`` and ``dec`` columns, losing coordinate frame and unit information +4. **Quantity columns**: Converted to plain float columns, completely losing unit information + +.. EXAMPLE END + +Method Reference +================ + +Pandas-Specific Methods +----------------------- + +- :meth:`~astropy.table.Table.to_pandas` - Convert Table to pandas DataFrame +- :meth:`~astropy.table.Table.from_pandas` - Create Table from pandas DataFrame + +Generic Multi-Backend Methods +----------------------------- + +- :meth:`~astropy.table.Table.to_df` - Convert Table to DataFrame using specified backend +- :meth:`~astropy.table.Table.from_df` - Create Table from any narwhals-compatible DataFrame + +See the `Narwhals documentation `_ for more information about supported backends and their capabilities. diff --git a/docs/table/implementation_details.rst b/docs/table/implementation_details.rst new file mode 100644 index 000000000000..02d63083b15c --- /dev/null +++ b/docs/table/implementation_details.rst @@ -0,0 +1,38 @@ + +.. _table_implementation_details: + +Table Implementation Details +***************************** + +This page provides a brief overview of the |Table| class implementation, in +particular highlighting the internal data storage architecture. This is aimed +at developers and/or users who are interested in optimal use of the |Table| +class. + +The image below illustrates the basic architecture of the |Table| class. +The fundamental data container is an ordered dictionary of individual column +objects maintained as the ``columns`` attribute. It is via this container +that columns are managed and accessed. + +.. image:: table_architecture.png + :width: 45% + +Each |Column| (or |MaskedColumn|) object is an |ndarray| (or +:class:`numpy.ma.MaskedArray`) subclass and is the sole owner of its data. +Maintaining the table as separate columns simplifies table management +considerably. It also makes operations like adding or removing columns much +faster in comparison to implementations using a ``numpy`` structured array +container. + +As shown below, a |Row| object corresponds to a single row in the table. The +|Row| object does not create a view of the full row at any point. Instead it +manages access (e.g., ``row['a']``) dynamically by referencing the appropriate +elements of the parent table. + +.. image:: table_row.png + :width: 83% + +In some cases it is desirable to have a static copy of the full row. This is +available via the `~astropy.table.Row.as_void()` method, which creates and +returns a :class:`numpy.void` or ``numpy.ma.mvoid`` object with a copy of the +original data. diff --git a/docs/table/index.rst b/docs/table/index.rst index 8c5d92370a20..c986d12eb02d 100644 --- a/docs/table/index.rst +++ b/docs/table/index.rst @@ -1,7 +1,4 @@ -.. _astropy_table: - -.. include:: references.txt - +.. _astropy-table: ***************************** Data Tables (`astropy.table`) @@ -10,152 +7,373 @@ Data Tables (`astropy.table`) Introduction ============ -`astropy.table` provides functionality for storing and manipulating -heterogenous tables of data in a way that is familiar to `numpy` users. A few -notable features of this package are: +`astropy.table` provides a flexible and easy-to-use set of tools for working with +tabular data using an interface based on `numpy`. In addition to basic table creation, +access, and modification operations, key features include: -* Initialize a table from a wide variety of input data structures and types. -* Modify a table by adding or removing columns, changing column names, - or adding new rows of data. -* Include table and column metadata as flexible data structures. -* Specify a description, units and output formatting for columns. -* Interactively scroll through long tables similar to using ``more``. -* Create a new table by selecting rows or columns from a table. -* Full support for multidimensional columns. -* Create a table by referencing (not copying) an existing `numpy` table. +* Support columns of astropy :ref:`time `, :ref:`coordinates `, and :ref:`quantities `. +* Support multidimensional and :ref:`structured array columns `. +* Maintain the units, description, and format of columns. +* Provide flexible metadata structures for the table and individual columns. +* Perform :ref:`table_operations` like database joins, concatenation, and binning. +* Maintain a table index for fast retrieval of table items or ranges. +* Support a general :ref:`mixin protocol ` for flexible data containers in tables. +* :ref:`Read and write ` to files via the :ref:`Unified File Read/Write Interface `. +* Convert to and from `pandas.DataFrame` or ``polars.DataFrame``. -Currently `astropy.table` is used when reading an ASCII table using -`astropy.io.ascii`. Future releases of AstroPy are expected to use the |Table| -class for other subpackages such as `astropy.io.vo` and `astropy.io.fits`. +The :ref:`astropy-table-and-dataframes` page provides the rationale for maintaining +and using the dedicated `astropy.table` package instead of relying on `pandas` or ``polars``. Getting Started =============== The basic workflow for creating a table, accessing table elements, -and modifying the table is shown below. These examples show a very simple +and modifying the table is shown below. These examples demonstrate a concise case, while the full `astropy.table` documentation is available from the :ref:`using_astropy_table` section. -First create a simple table with three columns of data named ``a``, ``b``, -and ``c``. These columns have integer, float, and string values respectively:: +First create a simple table with columns of data named ``a``, ``b``, ``c``, and +``d``. These columns have integer, float, string, and |Quantity| values +respectively:: - >>> from astropy.table import Table, Column - >>> a = [1, 4, 5] - >>> b = [2.0, 5.0, 8.2] - >>> c = ['x', 'y', 'z'] - >>> t = Table([a, b, c], names=('a', 'b', 'c'), meta={'name': 'first table'}) + >>> from astropy.table import QTable + >>> import astropy.units as u + >>> import numpy as np -There are a few ways to examine the table. You can get detailed information + >>> a = np.array([1, 4, 5], dtype=np.int32) + >>> b = [2.0, 5.0, 8.5] + >>> c = ['x', 'y', 'z'] + >>> d = [10, 20, 30] * u.m / u.s + + >>> t = QTable([a, b, c, d], + ... names=('a', 'b', 'c', 'd'), + ... meta={'name': 'first table'}) + +Comments: + +- Column ``a`` is a |ndarray| with a specified ``dtype`` of ``int32``. If the + data type is not provided, the default type for integers is ``int64`` on Mac + and Linux and ``int32`` on Windows. +- Column ``b`` is a list of ``float`` values, represented as ``float64``. +- Column ``c`` is a list of ``str`` values, represented as unicode. + See :ref:`bytestring-columns-python-3` for more information. +- Column ``d`` is a |Quantity| array. Since we used |QTable|, this stores a + native |Quantity| within the table and brings the full power of + :ref:`astropy-units` to this column in the table. + +.. Note:: + + If the table data have no units or you prefer to not use |Quantity|, then you + can use the |Table| class to create tables. The **only** difference between + |QTable| and |Table| is the behavior when adding a column that has units. + See :ref:`quantity_and_qtable` and :ref:`columns_with_units` for details on + the differences and use cases. + +There are many other ways of :ref:`construct_table`, including from a list of +rows (either tuples or dicts), from a ``numpy`` structured or 2D array, by +adding columns or rows incrementally, or even converting from a |SkyCoord|, a +:class:`pandas.DataFrame`, or a ``polars.DataFrame``. + +There are a few ways of :ref:`access_table`. You can get detailed information about the table values and column definitions as follows:: >>> t -
- array([(1, 2.0, 'x'), (4, 5.0, 'y'), (5, 8.2, 'z')], - dtype=[('a', ' + a b c d + m / s + int32 float64 str1 float64 + ----- ------- ---- ------- + 1 2.0 x 10.0 + 4 5.0 y 20.0 + 5 8.5 z 30.0 + +You can get summary information about the table as follows:: + + >>> t.info + + name dtype unit class + ---- ------- ----- -------- + a int32 Column + b float64 Column + c str1 Column + d float64 m / s Quantity + +From within a `Jupyter notebook `_, the table is +displayed as a formatted HTML table (details of how it appears can be changed +by altering the `astropy.table.conf.default_notebook_table_class +` item in the +:ref:`astropy_config`: + +.. image:: table_repr_html.png + :width: 450px + +Or you can get a fancier notebook interface with :meth:`~astropy.table.Table.show_in_notebook`, +e.g., when used with ``backend="ipydatagrid"``, it comes with in-browser filtering and sort: + +.. image:: https://raw.githubusercontent.com/jupyter-widgets/ipydatagrid/main/static/ipydatagrid_1.gif + :width: 450px + :alt: Animated DataGrid usage example from ipydatagrid. + +If you print the table (either from the notebook or in a text console session) +then a formatted version appears:: -If instead you print the table then a nicely formatted version appears:: + >>> print(t) + a b c d + m / s + --- --- --- ----- + 1 2.0 x 10.0 + 4 5.0 y 20.0 + 5 8.5 z 30.0 + +If you do not like the format of a particular column, you can change it through +:ref:`the 'info' property `:: + + >>> t['b'].info.format = '7.3f' >>> print(t) - a b c - --- --- --- - 1 2.0 x - 4 5.0 y - 5 8.2 z + a b c d + m / s + --- ------- --- ----- + 1 2.000 x 10.0 + 4 5.000 y 20.0 + 5 8.500 z 30.0 For a long table you can scroll up and down through the table one page at time:: - >>> t.more() + >>> t.more() # doctest: +SKIP + +You can also display it as an HTML-formatted table in the browser:: + + >>> t.show_in_browser() # doctest: +SKIP + +Or as an interactive (searchable and sortable) javascript table:: + + >>> t.show_in_browser(jsviewer=True) # doctest: +SKIP Now examine some high-level information about the table:: >>> t.colnames - ['a', 'b', 'c'] + ['a', 'b', 'c', 'd'] >>> len(t) 3 >>> t.meta {'name': 'first table'} -Access the data by column or row using familiar `numpy` structured array syntax:: +Access the data by column or row using familiar ``numpy`` structured array +syntax:: >>> t['a'] # Column 'a' - - array([1, 4, 5]) + + 1 + 4 + 5 >>> t['a'][1] # Row 1 of column 'a' - 4 + np.int32(4) + + >>> t[1] # Row 1 of the table + + a b c d + m / s + int32 float64 str1 float64 + ----- ------- ---- ------- + 4 5.000 y 20.0 - >>> t[1] # Row obj for with row 1 values - >>> t[1]['a'] # Column 'a' of row 1 - 4 + np.int32(4) -One can retreive a subset of a table by rows (using a slice) or +You can retrieve a subset of a table by rows (using a :class:`slice`) or by columns (using column names), where the subset is returned as a new table:: >>> print(t[0:2]) # Table object with rows 0 and 1 - a b c - --- --- --- - 1 2.0 x - 4 5.0 y + a b c d + m / s + --- ------- --- ----- + 1 2.000 x 10.0 + 4 5.000 y 20.0 + - >>> t['a', 'c'] # Table with cols 'a', 'c' + >>> print(t['a', 'c']) # Table with cols 'a' and 'c' a c --- --- 1 x 4 y 5 z -Modifying table values in place is flexible and works as one would expect:: +:ref:`modify_table` in place is flexible and works as you would expect:: - >>> t['a'] = [-1, -2, -3] # Set all column values + >>> t['a'][:] = [-1, -2, -3] # Set all column values in place >>> t['a'][2] = 30 # Set row 2 of column 'a' - >>> t[1] = (8, 9.0, "W") # Set all row values + >>> t[1] = (8, 9.0, "W", 4 * u.m / u.s) # Set all values of row 1 >>> t[1]['b'] = -9 # Set column 'b' of row 1 - >>> t[0:2]['b'] = 100.0 # Set column 'c' of rows 0 and 1 + >>> t[0:2]['b'] = 100.0 # Set column 'b' of rows 0 and 1 >>> print(t) - a b c - --- ----- --- - -1 100.0 x - 8 100.0 W - 30 8.2 z + a b c d + m / s + --- ------- --- ----- + -1 100.000 x 10.0 + 8 100.000 W 4.0 + 30 8.500 z 30.0 + +Replace, add, remove, and rename columns with the following:: + + >>> t['b'] = ['a', 'new', 'dtype'] # Replace column 'b' (different from in-place) + >>> t['e'] = [1, 2, 3] # Add column 'e' + >>> del t['c'] # Delete column 'c' + >>> t.rename_column('a', 'A') # Rename column 'a' to 'A' + >>> t.colnames + ['A', 'b', 'd', 'e'] -Add, remove, and rename columns with the following:: +Adding a new row of data to the table is as follows. Note that the unit +value is given in ``cm / s`` but will be added to the table as ``0.1 m / s`` in +accord with the existing unit. - >>> t.add_column(Column('d', [1, 2, 3]))) - >>> t.remove_column('c') - >>> t.rename_column('a', 'A') - >>> t.colnames - ['A', 'b', 'd'] + >>> t.add_row([-8, 'string', 10 * u.cm / u.s, 10]) + >>> t['d'] + -Lastly, adding a new row of data to the table is as follows:: +Tables can be used for data with missing values:: - >>> t.add_row([-8, -9, 10]) - >>> len(t) - 4 + >>> from astropy.table import MaskedColumn + >>> a_masked = MaskedColumn(a, mask=[True, True, False]) + >>> t = QTable([a_masked, b, c], names=('a', 'b', 'c'), + ... dtype=('i4', 'f8', 'U1')) + >>> t + + a b c + int32 float64 str1 + ----- ------- ---- + -- 2.0 x + -- 5.0 y + 5 8.5 z + +In addition to |Quantity|, you can include certain object types like +`~astropy.time.Time`, `~astropy.coordinates.SkyCoord`, and +`~astropy.table.NdarrayMixin` in your table. These "mixin" columns behave like +a hybrid of a regular `~astropy.table.Column` and the native object type (see +:ref:`mixin_columns`). For example:: + + >>> from astropy.time import Time + >>> from astropy.coordinates import SkyCoord + >>> tm = Time(['2000:002', '2002:345']) + >>> sc = SkyCoord([10, 20], [-45, +40], unit='deg') + >>> t = QTable([tm, sc], names=['time', 'skycoord']) + >>> t + + time skycoord + deg,deg + Time SkyCoord + --------------------- ---------- + 2000:002:00:00:00.000 10.0,-45.0 + 2002:345:00:00:00.000 20.0,40.0 + +Now let us compute the interval since the launch of the `Chandra X-ray Observatory +`_ aboard `STS-93 +`_ and store this in our table as a +|Quantity| in days:: + + >>> dt = t['time'] - Time('1999-07-23 04:30:59.984') + >>> t['dt_cxo'] = dt.to(u.d) + >>> t['dt_cxo'].info.format = '.3f' + >>> print(t) + time skycoord dt_cxo + deg,deg d + --------------------- ---------- -------- + 2000:002:00:00:00.000 10.0,-45.0 162.812 + 2002:345:00:00:00.000 20.0,40.0 1236.812 .. _using_astropy_table: -Using `table` -============= +Using ``table`` +=============== The details of using `astropy.table` are provided in the following sections: +Construct Table +--------------- + .. toctree:: :maxdepth: 2 construct_table.rst + +Access Table +------------ + +.. toctree:: + :maxdepth: 2 + access_table.rst + +Modify Table +------------ + +.. toctree:: + :maxdepth: 2 + modify_table.rst + +Table Operations +---------------- + +.. toctree:: + :maxdepth: 2 + + operations.rst + +Indexing +-------- + +.. toctree:: + :maxdepth: 2 + + indexing.rst + +Masking +------- + +.. toctree:: + :maxdepth: 2 + masking.rst - io.rst + +Mixin Columns +------------- + +.. toctree:: + :maxdepth: 2 + + mixin_columns.rst + +Astropy Table and DataFrames +---------------------------- + +.. toctree:: + :maxdepth: 2 + + dataframes.rst + pandas.rst + table_and_dataframes.rst + +Implementation +-------------- + +.. toctree:: + :maxdepth: 2 + + implementation_details.rst + +.. note that if this section gets too long, it should be moved to a separate + doc page - see the top of performance.inc.rst for the instructions on how to do + that +.. include:: performance.inc.rst Reference/API ============= -.. automodapi:: astropy.table +.. toctree:: + :maxdepth: 2 -.. automodapi:: astropy.table.io_registry + ref_api diff --git a/docs/table/indexing.rst b/docs/table/indexing.rst new file mode 100644 index 000000000000..c86220f12735 --- /dev/null +++ b/docs/table/indexing.rst @@ -0,0 +1,364 @@ +.. |add_index| replace:: :func:`~astropy.table.Table.add_index` +.. |index_mode| replace:: :func:`~astropy.table.Table.index_mode` + +.. _table-indexing: + +Table Indexing +************** + +Once a |Table| has been created, it is possible to create indices on one or +more columns of the table. An index internally sorts the rows of a table based +on the index column(s), allowing for element retrieval by column value(s) and +improved performance for certain table operations. + +Creating an Index +================= + +.. EXAMPLE START: Creating Indices on Table Columns + +To create an index on a table, use the |add_index| method:: + + >>> from astropy.table import Table + >>> t = Table([(2, 3, 2, 1), (8, 7, 6, 5)], names=('a', 'b')) + >>> t.add_index('a') + +The optional argument ``unique`` may be specified to create an index with +uniquely valued elements. + +To create a composite index on multiple columns, pass a list of columns +instead:: + + >>> t.add_index(['a', 'b']) + +In particular, the first index created using the +|add_index| method is considered the default index or the "primary key." To +retrieve an index from a table, use the `~astropy.table.Table.indices` +property:: + + >>> t.indices['a'] + + a rows + --- ---- + 1 3 + 2 0 + 2 2 + 3 1>> + >>> t.indices['a', 'b'] + + a b rows + --- --- ---- + 1 5 3 + 2 6 2 + 2 8 0 + 3 7 1>> + +.. EXAMPLE END + +Row Retrieval using Indices +=========================== + +.. EXAMPLE START: Retrieving Table Rows using Indices + +Row retrieval can be accomplished using two table properties: +`~astropy.table.Table.loc` and `~astropy.table.Table.iloc`. The +`~astropy.table.Table.loc` property can be indexed either by column value, +range of column values (*including* the bounds), or a :class:`list` or +|ndarray| of column values:: + + >>> t = Table([(1, 2, 3, 4), (10, 1, 9, 9)], names=('a', 'b'), dtype=['i8', 'i8']) + >>> t.add_index('a') + >>> t.loc[2] # the row(s) where a == 2 + + a b + int64 int64 + ----- ----- + 2 1 + >>> t.loc[[1, 4]] # the row(s) where a in [1, 4] +
+ a b + int64 int64 + ----- ----- + 1 10 + 4 9 + >>> t.loc[1:3] # the row(s) where a in [1, 2, 3] +
+ a b + int64 int64 + ----- ----- + 1 10 + 2 1 + 3 9 + >>> t.loc[:] +
+ a b + int64 int64 + ----- ----- + 1 10 + 2 1 + 3 9 + 4 9 + +Using multiple indices +---------------------- +By default, `~astropy.table.Table.loc` uses the primary index, which +here is column ``'a'``. You can use a different index with the ``with_index`` method as shown below:: + + >>> t.add_index('b') + >>> t.loc.with_index('b')[8:10] +
+ a b + int64 int64 + ----- ----- + 3 9 + 4 9 + 1 10 + +The ``with_index`` method takes an index identifier as input, where the format is +flexible as shown in these examples:: + + >>> t.add_index(['a', 'b']) + >>> t.loc # defaults to primary key # doctest: +IGNORE_OUTPUT + >>> t.loc.with_index('b')[10] # doctest: +IGNORE_OUTPUT + >>> t.loc.with_index(['b'])[[10, 9]] # doctest: +IGNORE_OUTPUT + >>> t.loc.with_index('a', 'b')[1, 10] # doctest: +IGNORE_OUTPUT + >>> t.loc.with_index(['a', 'b'])[1, 10] # doctest: +IGNORE_OUTPUT + +Using a multi-column index +-------------------------- +You can create an index on multiple table columns and select table rows that match all +values of the indexed columns:: + + >>> t.add_index(["a", "b"]) + >>> t.loc.with_index("a", "b")[3, 9] + + a b + int64 int64 + ----- ----- + 3 9 + >>> t.loc.with_index("a", "b")[[(3, 9), (4, 9)]] +
+ a b + int64 int64 + ----- ----- + 3 9 + 4 9 + +The property `~astropy.table.Table.iloc` works similarly, except that the +retrieval information must be either an integer or a :class:`slice`, and +relates to the sorted order of the index rather than column values. For +example:: + + >>> t.iloc[0] # smallest row by value 'a' + + a b + int64 int64 + ----- ----- + 1 10 + >>> t.iloc.with_index('b')[1:] # all but smallest value of 'b' +
+ a b + int64 int64 + ----- ----- + 3 9 + 4 9 + 1 10 + +.. EXAMPLE END + +Effects on Performance +====================== + +Table operations change somewhat when indices are present, and there are a +number of factors to consider when deciding whether the use of indices will +improve performance. In general, indexing offers the following advantages: + +* Table grouping and sorting based on indexed column(s) both become faster. +* Retrieving values by index is faster than custom searching. + +There are certain caveats, however: + +* Creating an index requires time and memory. +* Table modifications become slower due to automatic index updates. +* Slicing a table becomes slower due to index relabeling. + +See `here +`_ +for an IPython notebook profiling various aspects of table indexing. + +Index Modes +=========== + +The |index_mode| method allows for some flexibility in the behavior of table +indexing by allowing the user to enter a specific indexing mode via a context +manager. There are currently three indexing modes: ``'freeze'``, +``'copy_on_getitem'``, and ``'discard_on_copy'``. + +.. EXAMPLE START: Table Indexing with the "freeze" Index Mode + +The ``'freeze'`` mode prevents automatic index updates whenever a column of the +index is modified, and all indices refresh themselves after the context ends:: + + >>> t = Table([(1, 2, 3, 4), (10, 1, 9, 9)], names=('a', 'b'), dtype=['i8', 'i8']) + >>> t.add_index('a') + >>> with t.index_mode('freeze'): + ... t['a'][0] = 0 + ... print(t.indices['a']) # unmodified + + a rows + --- ---- + 1 0 + 2 1 + 3 2 + 4 3>> + >>> print(t.indices['a']) # modified + + a rows + --- ---- + 0 0 + 2 1 + 3 2 + 4 3>> + +.. EXAMPLE END + +.. EXAMPLE START: Table Indexing with the "copy_on_getitem" Index Mode + +The ``'copy_on_getitem'`` mode forces columns to copy and relabel their indices +upon slicing. In the absence of this mode, table slices will preserve +indices while column slices will not:: + + >>> ca = t['a'][[1, 3]] + >>> ca.info.indices + [] + >>> with t.index_mode('copy_on_getitem'): + ... ca = t['a'][[1, 3]] + ... print(ca.info.indices) + [ + a rows + --- ---- + 2 0 + 4 1>>] + +.. EXAMPLE END + +.. EXAMPLE START: Table Indexing with the "discard_on_copy" Index Mode + +The ``'discard_on_copy'`` mode prevents indices from being copied whenever a +column or table is copied:: + + >>> t2 = Table(t) + >>> t2.indices['a'] + + a rows + --- ---- + 0 0 + 2 1 + 3 2 + 4 3>> + >>> with t.index_mode('discard_on_copy'): + ... t2 = Table(t) + ... print(t2.indices) + [] + +.. EXAMPLE END + +Updating Rows using Indices +=========================== + +.. EXAMPLE START: Updating Table Rows using Indices + +Row updates can be accomplished by assigning the table property +`~astropy.table.Table.loc` a complete row or a list of rows:: + + >>> t = Table([('w', 'x', 'y', 'z'), (10, 1, 9, 9)], names=('a', 'b'), dtype=['str', 'i8']) + >>> t.add_index('a') + >>> t.loc['x'] + + a b + str1 int64 + ---- ----- + x 1 + >>> t.loc['x'] = ['a', 12] + >>> t +
+ a b + str1 int64 + ---- ----- + w 10 + a 12 + y 9 + z 9 + >>> t.loc[['w', 'y']] +
+ a b + str1 int64 + ---- ----- + w 10 + y 9 + >>> t.loc[['w', 'z']] = [['b', 23], ['c', 56]] + >>> t +
+ a b + str1 int64 + ---- ----- + b 23 + a 12 + y 9 + c 56 + +.. EXAMPLE END + +Retrieving the Location of Rows using Indices +============================================= + +.. EXAMPLE START: Retrieving the Location of Table Rows using Indices + +Retrieval of the location of rows can be accomplished using a table property: +`~astropy.table.Table.loc_indices`. The `~astropy.table.Table.loc_indices` +property can be indexed either by column value, range of column values +(*including* the bounds), or a :class:`list` or |ndarray| of column values:: + + >>> t = Table([('w', 'x', 'y', 'z'), (10, 1, 9, 9)], names=('a', 'b'), dtype=['str', 'i8']) + >>> t.add_index('a') + >>> t.loc_indices['x'] + np.int64(1) + +.. EXAMPLE END + +Storing the Table Indices to File +================================= + +You can write a table with indices to FITS, ECVS, or HDF5 formats by supplying +``write_indices=True`` in the call to the table `~astropy.table.Table.write` method. In +this case the row index values are included in the table column data and column metadata +to describe the indices is stored. This allows efficiently restoring the table index or +indices when the data file is read back in. For an indexed table ``t``, you can do the +following:: + + >>> t.write("data.fits", write_indices=True) # doctest: +SKIP + >>> t.write("data.ecsv", write_indices=True) # doctest: +SKIP + >>> t.write("data.hdf5", write_indices=True, path="root", serialize_meta=True) # doctest: +SKIP + >>> t_fits = Table.read("data.fits", astropy_native=True) # doctest: +SKIP + >>> t_ecsv = Table.read("data.ecsv") # doctest: +SKIP + >>> t_hdf5 = Table.read("data.hdf5", path="root") # doctest: +SKIP + +Engines +======= + +When creating an index via |add_index|, the keyword argument ``engine`` may be +specified to use a particular indexing engine. The available engines are: + +* `~astropy.table.SortedArray`, a sorted array engine using an underlying + sorted |Table|. +* `~astropy.table.SCEngine`, a sorted list engine using the `Sorted Containers + `_ package. +* `~astropy.table.BST`, a Python-based binary search tree engine (not recommended). + +The SCEngine depends on the ``sortedcontainers`` dependency. The most important takeaway is that +`~astropy.table.SortedArray` (the default engine) is usually best, although +`~astropy.table.SCEngine` may be more appropriate for an index created on an +empty column since adding new values is quicker. + +The `~astropy.table.BST` engine demonstrates a simple pure Python implementation +of a search tree engine, but the performance is poor for larger tables. This +is available in the code largely as an implementation reference. diff --git a/docs/table/io.rst b/docs/table/io.rst deleted file mode 100644 index ea76bd2f61da..000000000000 --- a/docs/table/io.rst +++ /dev/null @@ -1,82 +0,0 @@ -Reading and writing Table objects ---------------------------------- - -Built-in readers/writers -^^^^^^^^^^^^^^^^^^^^^^^^ - -No built-in readers/writers have been implemented at this time. - -Creating a custom reader/writer -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The following example demonstrates how to create a reader for the -Table class. First, we can create a highly simplistic FITS reader -which just reads the data as a structured array:: - - from astropy.table import Table - - def fits_reader(filename, hdu=1): - from astropy.io import fits - data = fits.open(filename)[hdu].data - return Table(data) - -and then register it with astropy.table:: - - from astropy.table import io_registry - io_registry.register_reader('fits', fits_reader) - -Reader functions can take any arguments except ``format`` (since this -is reserved for the ``Table.read`` method) and should return a -``Table`` object. - -We can then read in a FITS table with:: - - t = Table.read('catalog.fits', format='fits') - -In practice, it would be nice to have the ``read`` method automatically -identify that this file was a FITS file, so we can construct a function that -can recognize FITS files, which we refer to here as an *identifier* -function. An identifier function should take three arguments: the first -should be a string which indicates whether the identifier is being called -from ``read`` or ``write``, and the second and third are the positional and -keyword arguments passed to ``Table.read`` respectively (and are therefore a -list and a dictionary). We can write a simplistic function that only looks -at filenames (but in practice, this function could even look at the first -few bytes of the file for example). The only requirement is that it return a -boolean indicating whether the input matches that expected for the format:: - - def fits_identify(origin, args, kwargs): - return isinstance(args[0], basestring) and \ - args[0].lower().split('.')[-1] in ['fits', 'fit'] - -We then register this identifier function with ``astropy.table``:: - - io_registry.register_identifier('fits', fits_identify) - -And we can then do:: - - t = Table.read('catalog.fits') - -If multiple formats match the current input, then an exception is -raised, and similarly if no format matches the current input. In that -case, the format should be explicitly given with the ``format=`` -keyword argument. - -Similarly, it is possible to create custom writers. To go with our simplistic FITS reader above, we can write a simplistic FITS writer:: - - def fits_writer(table, filename, clobber=False): - import numpy as np - from astropy.io import fits - fits.writeto(filename, np.array(table), clobber=clobber) - -We then register the writer:: - - io_registry.register_writer('fits', fits_writer) - -And we can then write the file out to a FITS file:: - - t.write('catalog_new.fits', format='fits') - -If we have registered the identifier as above, we can simply do:: - - t.write('catalog_new.fits') diff --git a/docs/table/masking.rst b/docs/table/masking.rst index 105cad453f41..bcd2ec32b84b 100644 --- a/docs/table/masking.rst +++ b/docs/table/masking.rst @@ -1,6 +1,224 @@ -Masking and null values ------------------------ +.. _masking_and_missing_values: -At this time the ``astropy.table`` package has no support for masking and null -values. This is to be done. +Masking and Missing Values +************************** +The `astropy.table` package provides support for masking and missing values in +a table by using the ``numpy.ma`` `masked array +`_ package to define +masked columns and by supporting :ref:`mixin_columns` that provide masking. +This allows handling tables with missing or invalid entries in much the same +manner as for standard (unmasked) tables. It is useful to be familiar with the +`masked array documentation +`_ +when using masked tables within `astropy.table`. + +In a nutshell, the concept is to define a boolean mask that mirrors +the structure of a column data array. Wherever a mask value is +`True`, the corresponding entry is considered to be missing or invalid. +Operations involving column or row access and slicing are unchanged. +The key difference is that arithmetic or reduction operations involving +columns or column slices follow the rules for `operations +on masked arrays +`_. + +.. Note:: + + Reduction operations like :func:`numpy.sum` or :func:`numpy.mean` follow the + convention of ignoring masked (invalid) values. This differs from + the behavior of the floating point ``NaN``, for which the sum of an + array including one or more ``NaN's`` will result in ``NaN``. + + For more information see NumPy Enhancement Proposals `24 + `_, `25 + `_, and `26 + `_. + +Table Creation +============== + +A masked table can be created in several ways: + +**Create a table with one or more columns as a MaskedColumn object** + + >>> from astropy.table import Table, Column, MaskedColumn + >>> a = MaskedColumn([1, 2], name='a', mask=[False, True], dtype='i4') + >>> b = Column([3, 4], name='b', dtype='i8') + >>> Table([a, b]) +
+ a b + int32 int64 + ----- ----- + 1 3 + -- 4 + +The |MaskedColumn| is the masked analog of the |Column| class and provides the +interface for creating and manipulating a column of masked data. The +|MaskedColumn| class inherits from :class:`numpy.ma.MaskedArray`, in contrast +to |Column| which inherits from |ndarray|. This distinction is the main reason +there are different classes for these two cases. + +Notice that masked entries in the table output are shown as ``--``. + +**Create a table with one or more columns as a NumPy MaskedArray** + + >>> import numpy as np + >>> a = np.ma.array([1, 2]) + >>> b = [3, 4] + >>> t = Table([a, b], names=('a', 'b')) + +**Create a table from list data containing numpy.ma.masked** + +You can use the `numpy.ma.masked` constant to indicate masked or invalid data:: + + >>> a = [1.0, np.ma.masked] + >>> b = [np.ma.masked, 'val'] + >>> Table([a, b], names=('a', 'b')) +
+ a b + float64 str3 + ------- ---- + 1.0 -- + -- val + +Initializing from lists with embedded `numpy.ma.masked` elements is +considerably slower than using :func:`numpy.ma.array` or |MaskedColumn| +directly, so if performance is a concern you should use the latter methods if +possible. + +**Add a MaskedColumn object to an existing table** + + >>> t = Table([[1, 2]], names=['a']) + >>> b = MaskedColumn([3, 4], mask=[True, False]) + >>> t['b'] = b + +**Add a new row to an existing table and specify a mask argument** + + >>> a = Column([1, 2], name='a') + >>> b = Column([3, 4], name='b') + >>> t = Table([a, b]) + >>> t.add_row([3, 6], mask=[True, False]) + +**Create a new table object and specify masked=True** + +If ``masked=True`` is provided when creating the table then every column will +be created as a |MaskedColumn|, and new columns will always be added as a +|MaskedColumn|. + + >>> Table([(1, 2), (3, 4)], names=('a', 'b'), masked=True, dtype=('i4', 'i8')) +
+ a b + int32 int64 + ----- ----- + 1 3 + 2 4 + +**Convert an existing table to a masked table** + + >>> t = Table([[1, 2], ['x', 'y']]) # standard (unmasked) table + >>> t = Table(t, masked=True, copy=False) # convert to masked table + +This operation will convert every |Column| to |MaskedColumn| and ensure that any +subsequently added columns are masked. + +Table Access +============ + +Nearly all of the standard methods for accessing and modifying data +columns, rows, and individual elements also apply to masked tables. + +There is a difference however regarding the |Row| objects that are obtained by +indexing a single row of a table. For standard tables, two such rows can be +compared for equality, but for masked tables this comparison will produce an +exception:: + + >>> t[0] == t[1] + Traceback (most recent call last): + ... + ValueError: Unable to compare rows for masked table due to numpy.ma bug + +Masking and Filling +=================== + +Both the |Table| and |MaskedColumn| classes provide attributes and methods to +support manipulating tables with missing or invalid data. + +Mask +---- + +.. EXAMPLE START: Manipulating Tables with Missing Data using Masks + +The mask for a column can be viewed and modified via the ``mask`` attribute:: + + >>> t = Table([(1, 2), (3, 4)], names=('a', 'b'), masked=True) + >>> t['a'].mask = [False, True] # Modify column mask (boolean array) + >>> t['b'].mask = [True, False] # Modify column mask (boolean array) + >>> print(t) + a b + --- --- + 1 -- + -- 4 + +Masked entries are shown as ``--`` when the table is printed. You can +view the mask directly, either at the column or table level:: + + >>> t['a'].mask + array([False, True]...) + + >>> t.mask +
+ a b + bool bool + ----- ----- + False True + True False + +To get the indices of masked elements, use an expression like:: + + >>> t['a'].mask.nonzero()[0] # doctest: +SKIP + array([1]) + +.. EXAMPLE END + +Filling +------- + +.. EXAMPLE START: Manipulating Tables with Missing Data by Filling Masked Values + +The entries which are masked (i.e., missing or invalid) can be replaced with +specified fill values. Filling a |MaskedColumn| produces a |Column|. Each +column in a masked table has a ``fill_value`` attribute that specifies the +default fill value for that column. To perform the actual replacement operation +the :meth:`~astropy.table.Table.filled` method is called. This takes an +optional argument which can override the default column ``fill_value`` +attribute. +:: + + >>> t['a'].fill_value = -99 + >>> t['b'].fill_value = 33 + + >>> print(t.filled()) + a b + --- --- + 1 33 + -99 4 + + >>> print(t['a'].filled()) + a + --- + 1 + -99 + + >>> print(t['a'].filled(999)) + a + --- + 1 + 999 + + >>> print(t.filled(1000)) + a b + ---- ---- + 1 1000 + 1000 4 + +.. EXAMPLE END diff --git a/docs/table/mixin_columns.rst b/docs/table/mixin_columns.rst new file mode 100644 index 000000000000..e215ca344a8a --- /dev/null +++ b/docs/table/mixin_columns.rst @@ -0,0 +1,414 @@ +.. |join| replace:: :func:`~astropy.table.join` + +.. _mixin_columns: + +Mixin Columns +************* + +``astropy`` tables support the concept of "mixin columns", which +allows integration of appropriate non-|Column| based class objects within a +|Table| object. These mixin column objects are not converted in any way but are +used natively. + +The available built-in mixin column classes are: + +- |Quantity| and subclasses +- |SkyCoord| and coordinate frame classes +- |Time| and :class:`~astropy.time.TimeDelta` +- :class:`~astropy.coordinates.EarthLocation` +- `~astropy.table.NdarrayMixin` + +Basic Example +============= + +.. EXAMPLE START: Using Mixin Columns in Tables + +As an example we can create a table and add a time column:: + + >>> from astropy.table import Table + >>> from astropy.time import Time + >>> t = Table() + >>> t['index'] = [1, 2] + >>> t['time'] = Time(['2001-01-02T12:34:56', '2001-02-03T00:01:02']) + >>> print(t) + index time + ----- ----------------------- + 1 2001-01-02T12:34:56.000 + 2 2001-02-03T00:01:02.000 + +The important point here is that the ``time`` column is a bona fide |Time| +object:: + + >>> t['time'] +
+ index data + int64 object + ----- -------------------------------------...- + 0 <__main__.ExampleDataClass object at ...> + 1 <__main__.ExampleDataClass object at ...> + 2 <__main__.ExampleDataClass object at ...> + 3 <__main__.ExampleDataClass object at ...> + +What happened is that the instance is seen as a scalar object, and a +|Column| with ``dtype=object`` is created, which has the same entry for +each row. The same would happen if, e.g., you set ``t['data'] = None``. + +However, you can create a function (or 'handler') which takes +an instance of the data class you want to have automatically +handled and turns it into a mixin column:: + + >>> from astropy.table.table_helpers import ArrayWrapper + >>> def handle_example_data_class(obj): + ... return ArrayWrapper(obj._data) + +You can then register this by providing the fully qualified name +of the class and the handler function:: + + >>> from astropy.table.mixins.registry import register_mixin_handler + >>> register_mixin_handler('__main__.ExampleDataClass', handle_example_data_class) + >>> t['data'] = ExampleDataClass() + >>> t +
+ index data + int64 float64 + ----- ------- + 0 0.0 + 1 1.0 + 2 3.0 + 3 4.0 + +.. testcleanup:: + + >>> from astropy.table.mixins.registry import _handlers + >>> del _handlers['__main__.ExampleDataClass'] + +Because we defined the data class as part of the example +above, the fully qualified name starts with ``__main__``, +but for a class in a third-party package, this might look +like ``package.Class`` for example. diff --git a/docs/table/modify_table.rst b/docs/table/modify_table.rst index eceabef89008..2f5d46ccf9c0 100644 --- a/docs/table/modify_table.rst +++ b/docs/table/modify_table.rst @@ -1,109 +1,458 @@ .. _modify_table: -.. include:: references.txt - -Modifying a table ------------------ +Modifying a Table +***************** The data values within a |Table| object can be modified in much the same manner -as for `numpy` structured arrays by accessing columns or rows of data and -assigning values appropriately. A key enhancement provided by the |Table| class -is the ability to easily modify the structure of the table: one can add or -remove columns, and add new rows of data. +as for ``numpy`` `structured arrays +`_ by accessing columns or +rows of data and assigning values appropriately. A key enhancement provided by +the |Table| class is the ability to modify the structure of the table: you can +add or remove columns, and add new rows of data. -Quick overview -^^^^^^^^^^^^^^ +Quick Overview +============== The code below shows the basics of modifying a table and its data. +Examples +-------- + +.. EXAMPLE START: Making a Table and Modifying Data -**Make a table** +.. _table-mod-make-a-table: + +Make a table +^^^^^^^^^^^^ :: - >>> from astropy.table import Table, Column + >>> from astropy.table import Table >>> import numpy as np >>> arr = np.arange(15).reshape(5, 3) >>> t = Table(arr, names=('a', 'b', 'c'), meta={'keywords': {'key1': 'val1'}}) -**Modify data values** +.. _table-mod-modify-data-values: + +Modify data values +^^^^^^^^^^^^^^^^^^ :: - >>> t['a'] = [1, -2, 3, -4, 5] # Set all column values - >>> t['a'][2] = 30 # Set row 2 of column 'a' - >>> t[1] = (8, 9, 10) # Set all row values - >>> t[1]['b'] = -9 # Set column 'b' of row 1 - >>> t[0:3]['c'] = 100 # Set column 'c' of rows 0, 1, 2 + >>> t['a'][:] = [1, -2, 3, -4, 5] # Set all values of column 'a' + >>> t['a'][2] = 30 # Set row 2 of column 'a' + >>> t[1] = (8, 9, 10) # Set all values of row 1 + >>> t[1]['b'] = -9 # Set column 'b' of row 1 + >>> t[0:3]['c'] = 100 # Set column 'c' of rows 0, 1, 2 -**Add a column or columns** -:: +Note that ``table[row][column]`` assignments will not work with ``numpy`` +"fancy" ``row`` indexing (in that case ``table[row]`` would be a *copy* instead +of a *view*). "Fancy" ``numpy`` indices include a :class:`list`, |ndarray|, or +:class:`tuple` of |ndarray| (e.g., the return from :func:`numpy.where`):: - >>> t.add_column(Column('d', np.arange(5))) + >>> t[[1, 2]]['a'] = [3., 5.] # doesn't change table t + >>> t[np.array([1, 2])]['a'] = [3., 5.] # doesn't change table t + >>> t[np.where(t['a'] > 3)]['a'] = 3. # doesn't change table t - # Make a new table with the same number of rows and add columns to original table - >>> t2 = Table(np.arange(25).reshape(5, 5), names=('e', 'f', 'g', 'h', 'i')) - >>> t.add_columns(t2.columns.values()) +Instead use ``table[column][row]`` order:: -**Remove columns** -:: + >>> t['a'][[1, 2]] = [3., 5.] + >>> t['a'][np.array([1, 2])] = [3., 5.] + >>> t['a'][np.where(t['a'] > 3)] = 3. + +You can also modify data columns with ``unit`` set in a way that follows +the conventions of `~astropy.units.Quantity` by using the +:attr:`~astropy.table.Column.quantity` property:: + + >>> from astropy import units as u + >>> tu = Table([[1, 2.5]], names=('a',)) + >>> tu['a'].unit = u.m + >>> tu['a'].quantity[:] = [1, 2] * u.km + >>> tu['a'] + + 1000.0 + 2000.0 + +.. note:: + + The best way to combine the functionality of the |Table| and |Quantity| + classes is to use a |QTable|. See :ref:`quantity_and_qtable` for more + information. + +.. EXAMPLE END + +.. _table-mod-add-a-column-or-columns: + +Add a column or columns +^^^^^^^^^^^^^^^^^^^^^^^ + +.. EXAMPLE START: Adding Columns to Tables + +A single column can be added to a table using syntax like adding a key-value +pair to a :class:`dict`. The value on the right hand side can be a +:class:`list` or |ndarray| of the correct size, or a scalar value that will be +`broadcast `_:: + + >>> t['d1'] = np.arange(5) + >>> t['d2'] = [1, 2, 3, 4, 5] + >>> t['d3'] = 6 # all 5 rows set to 6 + +For more explicit control, the :meth:`~astropy.table.Table.add_column` and +:meth:`~astropy.table.Table.add_columns` methods can be used to add one or +multiple columns to a table. In both cases the new column(s) can be specified as +a :class:`list`, |ndarray|, |Column|, |MaskedColumn|, or a scalar:: + + >>> from astropy.table import Column + >>> t.add_column(np.arange(5), name='aa', index=0) # Insert before first table column + >>> t.add_column(1.0, name='bb') # Add column of all 1.0 to end of table + >>> c = Column(np.arange(5), name='e') + >>> t.add_column(c, index=0) # Add Column using the existing column name 'e' + >>> t.add_columns([[1, 2, 3, 4, 5], ['v', 'w', 'x', 'y', 'z']], names=['h', 'i']) + +Finally, columns can also be added from |Quantity| objects, which automatically +sets the ``unit`` attribute on the column (but you might find it more +convenient to add a |Quantity| to a |QTable| instead, see +:ref:`quantity_and_qtable` for details):: + + >>> from astropy import units as u + >>> t['d'] = np.arange(1., 6.) * u.m + >>> t['d'] + + 1.0 + 2.0 + 3.0 + 4.0 + 5.0 - >>> t.remove_column('f') - >>> t.remove_columns(['d', 'e']) - >>> del t['g'] +.. EXAMPLE END + +.. _table-mod-remove-columns: + +Remove columns +^^^^^^^^^^^^^^ + +.. EXAMPLE START: Removing Columns from Tables + +To remove a column from a table:: + + >>> t.remove_column('d1') + >>> t.remove_columns(['aa', 'd2', 'e']) + >>> del t['d3'] >>> del t['h', 'i'] >>> t.keep_columns(['a', 'b']) -**Rename columns** -:: +.. EXAMPLE END + +.. _table-mod-replace-a-column: + +Replace a column +^^^^^^^^^^^^^^^^ + +.. EXAMPLE START: Replacing Columns in Tables + +You can entirely replace an existing column with a new column by setting the +column to any object that could be used to initialize a table column (e.g., a +:class:`list` or |ndarray|). For example, you could change the data type of the +``a`` column from ``int`` to ``float`` using:: + + >>> t['a'] = t['a'].astype(float) + +If the right-hand side value is not column-like, then an in-place update using +`broadcasting `_ +will be done, for example:: + + >>> t['a'] = 1 # Internally does t['a'][:] = 1 + +.. EXAMPLE END + +.. _table-mod-perform-a-dictionary-style-update: + +Perform a dictionary-style update +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +It is possible to perform a dictionary-style update, which adds new columns to +the table and replaces existing ones:: + + >>> t1 = Table({'name': ['foo', 'bar'], 'val': [0., 0.]}, meta={'n': 2}) + >>> t2 = Table({'val': [1., 2.], 'val2': [10., 10.]}, meta={'id': 0}) + >>> t1 |= t2 + >>> t1 +
+ name val val2 + str3 float64 float64 + ---- ------- ------- + foo 1.0 10.0 + bar 2.0 10.0 + +When using ``|=``, the other object does not need to be a |Table|, it can be +anything that can be used for :ref:`construct_table` with a compatible number +of rows:: + + >>> t1 = Table({'name': ['foo', 'bar'], 'val': [0., 0.]}, meta={'n': 2}) + >>> d = dict({'val': [1., 2.], 'val2': [10., 10.]}) + >>> t1 |= d + >>> t1 +
+ name val val2 + str3 float64 float64 + ---- ------- ------- + foo 1.0 10.0 + bar 2.0 10.0 + +It is also possible to use the ``|`` operator to merge multiple |Table| instances +into a new table:: + + >>> from astropy.table import QTable + >>> t1 = Table({'name': ['foo', 'bar'], 'val': [0., 0.]}, meta={'n': 2}) + >>> t2 = QTable({'val': [1., 2.], 'val2': [10., 10.]}, meta={'id': 0}) + >>> t3 = t1 | t2 # Create a new table as result of update + >>> t3 +
+ name val val2 + str3 float64 float64 + ---- ------- ------- + foo 1.0 10.0 + bar 2.0 10.0 + +``|`` and ``|=`` also take care of silently :ref:`merging_metadata`:: + + >>> t3.meta + {'n': 2, 'id': 0} + +The columns in the updated |Table| are going to be copies of the originals. If +you need them to be references you can use the +:meth:`~astropy.table.Table.update` method with ``copy=False``, see :ref:`copy_versus_reference` +for details. + +.. _table-mod-ensure-the-existence-of-a-column: + +Ensure the existence of a column +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +|Table| has a :meth:`~astropy.table.Table.setdefault` method, which is +analogous to :meth:`dict.setdefault`. +It adds a column with a given name to the table if such a column is not in the +table already. +The default value passed to the method will be validated and, if necessary, +converted. +Either way the (possibly just inserted) column in the table is returned:: + + >>> t0 = Table({"a": ["Ham", "Spam"]}) + >>> t0 +
+ a + str4 + ---- + Ham + Spam + >>> t0.setdefault("a", ["Breakfast"]) # Existing column + + Ham + Spam + >>> t0.setdefault("approved", False) # New column + + False + False + >>> t0 +
+ a approved + str4 bool + ---- -------- + Ham False + Spam False + +.. _table-mod-rename-columns: + +Rename columns +^^^^^^^^^^^^^^ + +.. EXAMPLE START: Renaming Columns in Tables + +To rename a column:: >>> t.rename_column('a', 'a_new') >>> t['b'].name = 'b_new' -**Add a row of data** -:: +To rename multiple columns at once:: + + >>> t.rename_columns(['a_new', 'b_new'], ['a', 'b']) + +.. EXAMPLE END + +.. _table-mod-add-a-row-of-data: + +Add a row of data +^^^^^^^^^^^^^^^^^ + +.. EXAMPLE START: Adding a Row of Data to a Table + +To add a row:: >>> t.add_row([-8, -9]) -**Sort by one more more columns** -:: +.. EXAMPLE END - >>> t.sort('b_new') - >>> t.sort(['a_new', 'b_new']) +.. _table-mod-remove-rows: -**Reverse table rows** -:: +Remove rows +^^^^^^^^^^^ + +.. EXAMPLE START: Removing Rows of Data from Tables + +To remove a row:: + + >>> t.remove_row(0) + >>> t.remove_rows(slice(4, 5)) + >>> t.remove_rows([1, 2]) + +.. EXAMPLE END + +.. _table-mod-sort-by-one-or-more-columns: + +Sort by one or more columns +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. EXAMPLE START: Sorting Columns in Tables + +To sort columns:: + + >>> t.sort('b') + >>> t.sort(['a', 'b']) + +.. EXAMPLE END + +.. _table-mod-reverse-table-rows: + +Reverse table rows +^^^^^^^^^^^^^^^^^^ + +.. EXAMPLE START: Reversing Table Rows + +To reverse the order of table rows:: >>> t.reverse() -**Modify meta-data** -:: +.. EXAMPLE END + +.. _table-mod-modify-metadata: + +Modify metadata +^^^^^^^^^^^^^^^ + +.. EXAMPLE START: Modifying Metadata in Tables + +To modify metadata:: >>> t.meta['key'] = 'value' -**Reorder columns** +.. EXAMPLE END -.. note:: +.. _table-mod-select-or-reorder-columns: - In this example, it is important that `neworder` is a tuple, and not a - list, slice, or `~numpy.ndarray`. +Select or reorder columns +^^^^^^^^^^^^^^^^^^^^^^^^^ -:: +.. EXAMPLE START: Selecting or Reordering Columns in Tables + +A new table with a subset or reordered list of columns can be +created as shown in the following example:: + + >>> t = Table(arr, names=('a', 'b', 'c')) + >>> t_acb = t['a', 'c', 'b'] - >>> t_acb = t['a','c','b'] - >>> neworder = ('a','c','b') - >>> t_acb = t[neworder] +Another way to do the same thing is to provide a list or tuple +as the item, as shown below:: + >>> new_order = ['a', 'c', 'b'] # List or tuple + >>> t_acb = t[new_order] + +.. EXAMPLE END Caveats -^^^^^^^ - -Modifying the table data and properties is fairly straightforward. There are -only a few things to keep in mind: - -- The data type for a column cannot be changed in place. In order to do this - you must make a copy of the table with the column type changed appropriately. -- Adding or removing a column will generate a new copy - in memory of all the data. If the table is very large this may be slow. -- Adding a row *may* require a new copy in memory of the table data. This - depends on the detailed layout of Python objects in memory and cannot be - reliably controlled. In some cases it may be possible to build a table - row by row in less than O(N**2) time but you cannot count on it. +======= + +Modifying the table data and properties is fairly clear-cut, but one thing +to keep in mind is that adding a row *may* require a new copy in memory of the +table data. This depends on the detailed layout of Python objects in memory +and cannot be reliably controlled. In some cases it may be possible to build a +table row by row in less than O(N**2) time but you cannot count on it. + +Another subtlety to keep in mind is that in some cases the return value of an +operation results in a new table in memory while in other cases it results in a +view of the existing table data. As an example, imagine trying to set two table +elements using column selection with ``t['a', 'c']`` in combination with row +index selection:: + + >>> t = Table([[1, 2], [3, 4], [5, 6]], names=('a', 'b', 'c')) + >>> t['a', 'c'][1] = (100, 100) + >>> print(t) + a b c + --- --- --- + 1 3 5 + 2 4 6 + +This might be surprising because the data values did not change and there +was no error. In fact, what happened is that ``t['a', 'c']`` created a +new temporary table in memory as a *copy* of the original and then updated the +first row of the copy. The original ``t`` table was unaffected and the new +temporary table disappeared once the statement was complete. The takeaway +is to pay attention to how certain operations are performed one step at +a time. + +.. _table-replace-1_3: + +In-Place Versus Replace Column Update +===================================== + +Consider this code snippet:: + + >>> t = Table([[1, 2, 3]], names=['a']) + >>> t['a'] = [10.5, 20.5, 30.5] + +There are a couple of ways this could be handled. It could update the existing +array values in-place (truncating to integer), or it could replace the entire +column with a new column based on the supplied data values. + +The answer for ``astropy`` is that the operation shown above does a *complete +replacement* of the column object. In this case it makes a new column object +with float values by internally calling ``t.replace_column('a', [10.5, 20.5, +30.5])``. In general this behavior is more consistent with Python and `pandas +`_ behavior. + +.. _table-mod-forcing-in-place-update: + +Forcing in-place update +----------------------- + +It is possible to force an in-place update of a column as follows:: + + t[colname][:] = value + +.. _table-mod-finding-the-source-of-problems: + +Finding the source of problems +------------------------------ + +In order to find potential problems related to replacing columns, there is the +option `astropy.table.conf.replace_warnings +` in the :ref:`astropy_config`. This +controls a set of warnings that are emitted under certain circumstances when a +table column is replaced. This option must be set to a list that includes zero +or more of the following string values: + +``always`` : + Print a warning every time a column gets replaced via the + ``__setitem__()`` syntax (i.e., ``t['a'] = new_col``). + +``slice`` : + Print a warning when a column that appears to be a :class:`slice` of + a parent column is replaced. + +``refcount`` : + Print a warning when the Python reference count for the + column changes. This indicates that a stale object exists that might + be used elsewhere in the code and give unexpected results. + +``attributes`` : + Print a warning if any of the standard column attributes changed. + +The default value for the ``table.conf.replace_warnings`` option is +``[]`` (no warnings). diff --git a/docs/table/operations.rst b/docs/table/operations.rst new file mode 100644 index 000000000000..f335c7711b0e --- /dev/null +++ b/docs/table/operations.rst @@ -0,0 +1,1347 @@ +.. |join| replace:: :func:`~astropy.table.join` + +.. _table_operations: + +Table Operations +**************** + +In this section we describe high-level operations that can be used to generate +a new table from one or more input tables. This includes: + +======================= + +.. list-table:: + :header-rows: 1 + :widths: 28 52 20 + + * - Documentation + - Description + - Function + * - `Grouped operations`_ + - Group tables and columns by keys + - :func:`~astropy.table.Table.group_by` + * - `Binning`_ + - Binning tables + - :func:`~astropy.table.Table.group_by` + * - `Stack vertically`_ + - Concatenate input tables along rows + - :func:`~astropy.table.vstack` + * - `Stack horizontally`_ + - Concatenate input tables along columns + - :func:`~astropy.table.hstack` + * - `Join`_ + - Database-style join of two tables + - |join| + * - `Unique rows`_ + - Unique table rows by keys + - :func:`~astropy.table.unique` + * - `Set difference`_ + - Set difference of two tables + - :func:`~astropy.table.setdiff` + * - `Table diff`_ + - Generic difference of two simple tables + - :func:`~astropy.utils.diff.report_diff_values` + + +.. _grouped-operations: + +Grouped Operations +------------------ + +.. EXAMPLE START: Grouped Operations in Tables + +Sometimes in a table or table column there are natural groups within the dataset +for which it makes sense to compute some derived values. A minimal example is a +list of objects with photometry from various observing runs:: + + >>> from astropy.table import Table + >>> obs = Table.read("""name obs_date mag_b mag_v + ... M31 2012-01-02 17.0 17.5 + ... M31 2012-01-02 17.1 17.4 + ... M101 2012-01-02 15.1 13.5 + ... M82 2012-02-14 16.2 14.5 + ... M31 2012-02-14 16.9 17.3 + ... M82 2012-02-14 15.2 15.5 + ... M101 2012-02-14 15.0 13.6 + ... M82 2012-03-26 15.7 16.5 + ... M101 2012-03-26 15.1 13.5 + ... M101 2012-03-26 14.8 14.3 + ... """, format='ascii') + >>> # Make sure magnitudes are printed with one digit after the decimal point + >>> obs['mag_b'].info.format = '{:.1f}' + >>> obs['mag_v'].info.format = '{:.1f}' + +.. EXAMPLE END + +Table Groups +^^^^^^^^^^^^ + +Now suppose we want the mean magnitudes for each object. We first group the data +by the ``name`` column with the :func:`~astropy.table.Table.group_by` method. +This returns a new table sorted by ``name`` which has a ``groups`` property +specifying the unique values of ``name`` and the corresponding table rows:: + + >>> obs_by_name = obs.group_by('name') + >>> print(obs_by_name) # doctest: +SKIP + name obs_date mag_b mag_v + ---- ---------- ----- ----- + M101 2012-01-02 15.1 13.5 << First group (index=0, key='M101') + M101 2012-02-14 15.0 13.6 + M101 2012-03-26 15.1 13.5 + M101 2012-03-26 14.8 14.3 + M31 2012-01-02 17.0 17.5 << Second group (index=4, key='M31') + M31 2012-01-02 17.1 17.4 + M31 2012-02-14 16.9 17.3 + M82 2012-02-14 16.2 14.5 << Third group (index=7, key='M83') + M82 2012-02-14 15.2 15.5 + M82 2012-03-26 15.7 16.5 + << End of groups (index=10) + >>> print(obs_by_name.groups.keys) + name + ---- + M101 + M31 + M82 + >>> print(obs_by_name.groups.indices) + [ 0 4 7 10] + +The ``groups`` property is the portal to all grouped operations with tables and +columns. It defines how the table is grouped via an array of the unique row key +values and the indices of the group boundaries for those key values. The groups +here correspond to the row slices ``0:4``, ``4:7``, and ``7:10`` in the +``obs_by_name`` table. + +The output grouped table has two important properties: + +- The groups in the order of the lexically sorted key values (``M101``, ``M31``, + ``M82`` in our example). +- The rows within each group are in the same order as they appear in the + original table. + +The initial argument (``keys``) for the :func:`~astropy.table.Table.group_by` +function can take a number of input data types: + +- Single string value with a table column name (as shown above) +- List of string values with table column names +- Another |Table| or |Column| with same length as table +- ``numpy`` structured array with same length as table +- ``numpy`` homogeneous array with same length as table + +In all cases the corresponding row elements are considered as a :class:`tuple` +of values which form a key value that is used to sort the original table and +generate the required groups. + +As an example, to get the average magnitudes for each object on each observing +night, we would first group the table on both ``name`` and ``obs_date`` as +follows:: + + >>> print(obs.group_by(['name', 'obs_date']).groups.keys) + name obs_date + ---- ---------- + M101 2012-01-02 + M101 2012-02-14 + M101 2012-03-26 + M31 2012-01-02 + M31 2012-02-14 + M82 2012-02-14 + M82 2012-03-26 + + +Manipulating Groups +^^^^^^^^^^^^^^^^^^^ + +.. EXAMPLE START: Manipulating Groups in Tables + +Once you have applied grouping to a table then you can access the individual +groups or subsets of groups. In all cases this returns a new grouped table. +For instance, to get the subtable which corresponds to the second group +(index=1) do:: + + >>> print(obs_by_name.groups[1]) + name obs_date mag_b mag_v + ---- ---------- ----- ----- + M31 2012-01-02 17.0 17.5 + M31 2012-01-02 17.1 17.4 + M31 2012-02-14 16.9 17.3 + +To get the first and second groups together use a :class:`slice`:: + + >>> groups01 = obs_by_name.groups[0:2] + >>> print(groups01) + name obs_date mag_b mag_v + ---- ---------- ----- ----- + M101 2012-01-02 15.1 13.5 + M101 2012-02-14 15.0 13.6 + M101 2012-03-26 15.1 13.5 + M101 2012-03-26 14.8 14.3 + M31 2012-01-02 17.0 17.5 + M31 2012-01-02 17.1 17.4 + M31 2012-02-14 16.9 17.3 + >>> print(groups01.groups.keys) + name + ---- + M101 + M31 + +You can also supply a ``numpy`` array of indices or a boolean mask to select +particular groups, for example:: + + >>> mask = obs_by_name.groups.keys['name'] == 'M101' + >>> print(obs_by_name.groups[mask]) + name obs_date mag_b mag_v + ---- ---------- ----- ----- + M101 2012-01-02 15.1 13.5 + M101 2012-02-14 15.0 13.6 + M101 2012-03-26 15.1 13.5 + M101 2012-03-26 14.8 14.3 + +You can iterate over the group subtables and corresponding keys with:: + + >>> for key, group in zip(obs_by_name.groups.keys, obs_by_name.groups): + ... print(f'****** {key["name"]} *******') + ... print(group) + ... print('') + ... + ****** M101 ******* + name obs_date mag_b mag_v + ---- ---------- ----- ----- + M101 2012-01-02 15.1 13.5 + M101 2012-02-14 15.0 13.6 + M101 2012-03-26 15.1 13.5 + M101 2012-03-26 14.8 14.3 + ****** M31 ******* + name obs_date mag_b mag_v + ---- ---------- ----- ----- + M31 2012-01-02 17.0 17.5 + M31 2012-01-02 17.1 17.4 + M31 2012-02-14 16.9 17.3 + ****** M82 ******* + name obs_date mag_b mag_v + ---- ---------- ----- ----- + M82 2012-02-14 16.2 14.5 + M82 2012-02-14 15.2 15.5 + M82 2012-03-26 15.7 16.5 + +.. EXAMPLE END + +Column Groups +^^^^^^^^^^^^^ + +Like |Table| objects, |Column| objects can also be grouped for subsequent +manipulation with grouped operations. This can apply both to columns within a +|Table| or bare |Column| objects. + +As for |Table|, the grouping is generated with the +:func:`~astropy.table.Table.group_by` method. The difference here is that +there is no option of providing one or more column names since that +does not make sense for a |Column|. + +Examples +~~~~~~~~ + +.. EXAMPLE START: Grouping Column Objects in Tables + +To generate grouping in columns:: + + >>> from astropy.table import Column + >>> import numpy as np + >>> c = Column([1, 2, 3, 4, 5, 6], name='a') + >>> key_vals = np.array(['foo', 'bar', 'foo', 'foo', 'qux', 'qux']) + >>> cg = c.group_by(key_vals) + + >>> for key, group in zip(cg.groups.keys, cg.groups): + ... print(f'****** {key} *******') + ... print(group) + ... print('') + ... + ****** bar ******* + a + --- + 2 + ****** foo ******* + a + --- + 1 + 3 + 4 + ****** qux ******* + a + --- + 5 + 6 + +.. EXAMPLE END + +Aggregation +^^^^^^^^^^^ + +Aggregation is the process of applying a specified reduction function to the +values within each group for each non-key column. This function must accept a +|ndarray| as the first argument and return a single scalar value. Common +function examples are :func:`numpy.sum`, :func:`numpy.mean`, and +:func:`numpy.std`. + +For the example grouped table ``obs_by_name`` from above, we compute the group +means with the :meth:`~astropy.table.groups.TableGroups.aggregate` method:: + + >>> obs_mean = obs_by_name.groups.aggregate(np.mean) # doctest: +SHOW_WARNINGS + AstropyUserWarning: Cannot aggregate column 'obs_date' with type '>> print(obs_mean) + name mag_b mag_v + ---- ----- ----- + M101 15.0 13.7 + M31 17.0 17.4 + M82 15.7 15.5 + +It seems the magnitude values were successfully averaged, but what about the +:class:`~astropy.utils.exceptions.AstropyUserWarning`? Since the ``obs_date`` +column is a string-type array, the :func:`numpy.mean` function failed and +raised an exception ``cannot perform reduceat with flexible type``. Any time this happens +:meth:`~astropy.table.groups.TableGroups.aggregate` will issue a warning and +then drop that column from the output result. Note that the ``name`` column is +one of the ``keys`` used to determine the grouping so it is automatically +ignored from aggregation. + +.. EXAMPLE START: Performing Aggregation on Grouped Tables + +From a grouped table it is possible to select one or more columns on which +to perform the aggregation:: + + >>> print(obs_by_name['mag_b'].groups.aggregate(np.mean)) + mag_b + ----- + 15.0 + 17.0 + 15.7 + +The order of the columns can be specified too:: + + >>> print(obs_by_name['name', 'mag_v', 'mag_b'].groups.aggregate(np.mean)) + name mag_v mag_b + ---- ----- ----- + M101 13.7 15.0 + M31 17.4 17.0 + M82 15.5 15.7 + + +A single column of data can be aggregated as well:: + + >>> c = Column([1, 2, 3, 4, 5, 6], name='a') + >>> key_vals = np.array(['foo', 'bar', 'foo', 'foo', 'qux', 'qux']) + >>> cg = c.group_by(key_vals) + >>> cg_sums = cg.groups.aggregate(np.sum) + >>> for key, cg_sum in zip(cg.groups.keys, cg_sums): + ... print(f'Sum for {key} = {cg_sum}') + ... + Sum for bar = 2 + Sum for foo = 8 + Sum for qux = 11 + +.. EXAMPLE END + +If the specified function has a :meth:`numpy.ufunc.reduceat` method, this will +be called instead. This can improve the performance by a factor of 10 to 100 +(or more) for large unmasked tables or columns with many relatively small +groups. It also allows for the use of certain ``numpy`` functions which +normally take more than one input array but also work as reduction functions, +like `numpy.add`. The ``numpy`` functions which should take advantage of using +:meth:`numpy.ufunc.reduceat` include: + +- `numpy.add` +- `numpy.arctan2` +- `numpy.bitwise_and` +- `numpy.bitwise_or` +- `numpy.bitwise_xor` +- `numpy.copysign` +- `numpy.divide` +- `numpy.equal` +- `numpy.floor_divide` +- `numpy.fmax` +- `numpy.fmin` +- `numpy.fmod` +- `numpy.greater_equal` +- `numpy.greater` +- `numpy.hypot` +- `numpy.left_shift` +- `numpy.less_equal` +- `numpy.less` +- `numpy.logaddexp2` +- `numpy.logaddexp` +- `numpy.logical_and` +- `numpy.logical_or` +- `numpy.logical_xor` +- `numpy.maximum` +- `numpy.minimum` +- `numpy.mod` +- `numpy.multiply` +- `numpy.not_equal` +- `numpy.power` +- `numpy.remainder` +- `numpy.right_shift` +- `numpy.subtract` +- `numpy.true_divide` + +In special cases, :func:`numpy.sum` and :func:`numpy.mean` are substituted with +their respective ``reduceat`` methods. + +Filtering +^^^^^^^^^ + +Table groups can be filtered by means of the +:meth:`~astropy.table.groups.TableGroups.filter` method. This is done by +supplying a function which is called for each group. The function +which is passed to this method must accept two arguments: + +- ``table`` : |Table| object +- ``key_colnames`` : list of columns in ``table`` used as keys for grouping + +It must then return either `True` or `False`. + +Example +~~~~~~~ + +.. EXAMPLE START: Filtering Table Groups + +The following will select all table groups with only positive values in the +non-key columns:: + + >>> def all_positive(table, key_colnames): + ... colnames = [name for name in table.colnames if name not in key_colnames] + ... for colname in colnames: + ... if np.any(table[colname] <= 0): + ... return False + ... return True + +An example of using this function is:: + + >>> t = Table.read(""" a b c + ... -2 7.0 2 + ... -2 5.0 1 + ... 1 3.0 -5 + ... 1 -2.0 -6 + ... 1 1.0 7 + ... 0 4.0 4 + ... 3 3.0 5 + ... 3 -2.0 6 + ... 3 1.0 7""", format='ascii') + >>> tg = t.group_by('a') + >>> t_positive = tg.groups.filter(all_positive) + >>> for group in t_positive.groups: + ... print(group) + ... print('') + ... + a b c + --- --- --- + -2 7.0 2 + -2 5.0 1 + + a b c + --- --- --- + 0 4.0 4 + +As can be seen only the groups with ``a == -2`` and ``a == 0`` have all +positive values in the non-key columns, so those are the ones that are selected. + +Likewise a grouped column can be filtered with the +:meth:`~astropy.table.groups.ColumnGroups.filter`, method but in this case the +filtering function takes only a single argument which is the column group. It +still must return either `True` or `False`. For example:: + + def all_positive(column): + return np.all(column > 0) + +.. EXAMPLE END + +.. _table_binning: + +Binning +------- + +A common tool in analysis is to bin a table based on some reference value. +Examples: + +- Photometry of a binary star in several bands taken over a + span of time which should be binned by orbital phase. +- Reducing the sampling density for a table by combining + 100 rows at a time. +- Unevenly sampled historical data which should binned to + four points per year. + +All of these examples of binning a table can be accomplished using +`grouped operations`_. The examples in that section are focused on the +case of discrete key values such as the name of a source. In this +section we show a concise yet powerful way of applying grouped operations to +accomplish binning on key values such as time, phase, or row number. + +The common theme in all of these cases is to convert the key value array into +a new float- or int-valued array whose values are identical for rows in the same +output bin. + +Example +^^^^^^^ + +.. EXAMPLE START: Binning a Table using Grouped Operations + +As an example, we generate a fake light curve:: + + >>> year = np.linspace(2000.0, 2010.0, 200) # 200 observations over 10 years + >>> period = 1.811 + >>> y0 = 2005.2 + >>> mag = 14.0 + 1.2 * np.sin(2 * np.pi * (year - y0) / period) + >>> phase = ((year - y0) / period) % 1.0 + >>> dat = Table([year, phase, mag], names=['year', 'phase', 'mag']) + +Now we make an array that will be used for binning the data by 0.25 year +intervals:: + + >>> year_bin = np.trunc(year / 0.25) + +This has the property that all samples in each 0.25 year bin have the same +value of ``year_bin``. Think of ``year_bin`` as the bin number for ``year``. +Then do the binning by grouping and immediately aggregating with +:func:`numpy.mean`. + + >>> dat_grouped = dat.group_by(year_bin) + >>> dat_binned = dat_grouped.groups.aggregate(np.mean) + +We can plot the results with ``plt.plot(dat_binned['year'], dat_binned['mag'], +'.')``. Alternately, we could bin into 10 phase bins:: + + >>> phase_bin = np.trunc(phase / 0.1) + >>> dat_grouped = dat.group_by(phase_bin) + >>> dat_binned = dat_grouped.groups.aggregate(np.mean) + +This time, try plotting with ``plt.plot(dat_binned['phase'], +dat_binned['mag'])``. + +.. EXAMPLE END + +.. _stack-vertically: + +Stack Vertically +---------------- + +The |Table| class supports stacking tables vertically with the +:func:`~astropy.table.vstack` function. This process is also commonly known as +concatenating or appending tables in the row direction. It corresponds roughly +to the :func:`numpy.vstack` function. + +Examples +^^^^^^^^ + +.. EXAMPLE START: Stacking (or Concatenating) Tables Vertically + +Suppose we have two tables of observations with several column names in +common:: + + >>> from astropy.table import Table, vstack + >>> obs1 = Table.read("""name obs_date mag_b logLx + ... M31 2012-01-02 17.0 42.5 + ... M82 2012-10-29 16.2 43.5 + ... M101 2012-10-31 15.1 44.5""", format='ascii') + + >>> obs2 = Table.read("""name obs_date logLx + ... NGC3516 2011-11-11 42.1 + ... M31 1999-01-05 43.1 + ... M82 2012-10-30 45.0""", format='ascii') + +Now we can stack these two tables:: + + >>> print(vstack([obs1, obs2])) + name obs_date mag_b logLx + ------- ---------- ----- ----- + M31 2012-01-02 17.0 42.5 + M82 2012-10-29 16.2 43.5 + M101 2012-10-31 15.1 44.5 + NGC3516 2011-11-11 -- 42.1 + M31 1999-01-05 -- 43.1 + M82 2012-10-30 -- 45.0 + +Notice that the ``obs2`` table is missing the ``mag_b`` column, so in the +stacked output table those values are marked as missing. This is the default +behavior and corresponds to ``join_type='outer'``. There are two other allowed +values for the ``join_type`` argument, ``'inner'`` and ``'exact'``:: + + >>> print(vstack([obs1, obs2], join_type='inner')) + name obs_date logLx + ------- ---------- ----- + M31 2012-01-02 42.5 + M82 2012-10-29 43.5 + M101 2012-10-31 44.5 + NGC3516 2011-11-11 42.1 + M31 1999-01-05 43.1 + M82 2012-10-30 45.0 + + >>> print(vstack([obs1, obs2], join_type='exact')) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + TableMergeError: Inconsistent columns in input arrays (use 'inner' + or 'outer' join_type to allow non-matching columns) + +In the case of ``join_type='inner'``, only the common columns (the intersection) +are present in the output table. When ``join_type='exact'`` is specified, then +:func:`~astropy.table.vstack` requires that all of the input tables have +exactly the same column names. + +More than two tables can be stacked by supplying a longer list of tables:: + + >>> obs3 = Table.read("""name obs_date mag_b logLx + ... M45 2012-02-03 15.0 40.5""", format='ascii') + >>> print(vstack([obs1, obs2, obs3])) + name obs_date mag_b logLx + ------- ---------- ----- ----- + M31 2012-01-02 17.0 42.5 + M82 2012-10-29 16.2 43.5 + M101 2012-10-31 15.1 44.5 + NGC3516 2011-11-11 -- 42.1 + M31 1999-01-05 -- 43.1 + M82 2012-10-30 -- 45.0 + M45 2012-02-03 15.0 40.5 + +See also the sections on `Merging metadata`_ and `Merging column attributes`_ +for details on how these characteristics of the input tables are merged in the +single output table. Note also that you can use a single table |Row| instead of +a full table as one of the inputs. + +.. EXAMPLE END + +.. _stack-horizontally: + +Stack Horizontally +------------------ + +The |Table| class supports stacking tables horizontally (in the column-wise +direction) with the :func:`~astropy.table.hstack` function. It corresponds +roughly to the :func:`numpy.hstack` function. + +Examples +^^^^^^^^ + +.. EXAMPLE START: Stacking (or Concatenating) Tables Horizontally + +Suppose we have the following two tables:: + + >>> from astropy.table import Table, hstack + >>> t1 = Table.read("""a b c + ... 1 foo 1.4 + ... 2 bar 2.1 + ... 3 baz 2.8""", format='ascii') + >>> t2 = Table.read("""d e + ... ham eggs + ... spam toast""", format='ascii') + +Now we can stack these two tables horizontally:: + + >>> print(hstack([t1, t2])) + a b c d e + --- --- --- ---- ----- + 1 foo 1.4 ham eggs + 2 bar 2.1 spam toast + 3 baz 2.8 -- -- + +As with :func:`~astropy.table.vstack`, there is an optional ``join_type`` +argument that can take values ``'inner'``, ``'exact'``, and ``'outer'``. The +default is ``'outer'``, which effectively takes the union of available rows and +masks out any missing values. This is illustrated in the example above. The +other options give the intersection of rows, where ``'exact'`` requires that +all tables have exactly the same number of rows:: + + >>> print(hstack([t1, t2], join_type='inner')) + a b c d e + --- --- --- ---- ----- + 1 foo 1.4 ham eggs + 2 bar 2.1 spam toast + + >>> print(hstack([t1, t2], join_type='exact')) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + TableMergeError: Inconsistent number of rows in input arrays (use 'inner' or + 'outer' join_type to allow non-matching rows) + +More than two tables can be stacked by supplying a longer list of tables. The +example below also illustrates the behavior when there is a conflict in the +input column names (see the section on `Column renaming`_ for details):: + + >>> t3 = Table.read("""a b + ... M45 2012-02-03""", format='ascii') + >>> print(hstack([t1, t2, t3])) + a_1 b_1 c d e a_3 b_3 + --- --- --- ---- ----- --- ---------- + 1 foo 1.4 ham eggs M45 2012-02-03 + 2 bar 2.1 spam toast -- -- + 3 baz 2.8 -- -- -- -- + +The metadata from the input tables is merged by the process described in the +`Merging metadata`_ section. Note also that you can use a single table |Row| +instead of a full table as one of the inputs. + +.. EXAMPLE END + +.. _stack-depthwise: + +Stack Depth-Wise +---------------- + +The |Table| class supports stacking columns within tables depth-wise using the +:func:`~astropy.table.dstack` function. It corresponds roughly to running the +:func:`numpy.dstack` function on the individual columns matched by name. + +Examples +^^^^^^^^ + +.. EXAMPLE START: Stacking (or Concatenating) Tables Depth-Wise + +Suppose we have tables of data for sources giving information on the enclosed +source counts for different PSF fractions:: + + >>> from astropy.table import Table, dstack + >>> src1 = Table.read("""psf_frac counts + ... 0.10 45. + ... 0.50 90. + ... 0.90 120. + ... """, format='ascii') + + >>> src2 = Table.read("""psf_frac counts + ... 0.10 200. + ... 0.50 300. + ... 0.90 350. + ... """, format='ascii') + +Now we can stack these two tables depth-wise to get a single table with the +characteristics of both sources:: + + >>> srcs = dstack([src1, src2]) + >>> print(srcs) + psf_frac counts + ---------- -------------- + 0.1 .. 0.1 45.0 .. 200.0 + 0.5 .. 0.5 90.0 .. 300.0 + 0.9 .. 0.9 120.0 .. 350.0 + +In this case the counts for the first source are accessible as +``srcs['counts'][:, 0]``, and likewise the second source counts are +``srcs['counts'][:, 1]``. + +For this function the length of all input tables must be the same. This +function can accept ``join_type`` and ``metadata_conflicts`` just like the +:func:`~astropy.table.vstack` function. The ``join_type`` argument controls how +to handle mismatches in the columns of the input table. + +See also the sections on `Merging metadata`_ and `Merging column attributes`_ +for details on how these characteristics of the input tables are merged in the +single output table. Note also that you can use a single table |Row| instead of +a full table as one of the inputs. + +.. EXAMPLE END + +.. _table-join: + +Join +---- + +The |Table| class supports the `database join +`_ operation. This provides a flexible +and powerful way to combine tables based on the values in one or more key +columns. + +Examples +^^^^^^^^ + +.. EXAMPLE START: Combining Tables using the Database Join Operation + +Suppose we have two tables of observations, the first with B and V magnitudes +and the second with X-ray luminosities of an overlapping (but not identical) +sample:: + + >>> from astropy.table import Table, join + >>> optical = Table.read("""name obs_date mag_b mag_v + ... M31 2012-01-02 17.0 16.0 + ... M82 2012-10-29 16.2 15.2 + ... M101 2012-10-31 15.1 15.5""", format='ascii') + >>> xray = Table.read(""" name obs_date logLx + ... NGC3516 2011-11-11 42.1 + ... M31 1999-01-05 43.1 + ... M82 2012-10-29 45.0""", format='ascii') + +The |join| method allows you to merge these two tables into a single table based +on matching values in the "key columns". By default, the key columns are the set +of columns that are common to both tables. In this case the key columns are +``name`` and ``obs_date``. We can find all of the observations of the same +object on the same date as follows:: + + >>> opt_xray = join(optical, xray) + >>> print(opt_xray) + name obs_date mag_b mag_v logLx + ---- ---------- ----- ----- ----- + M82 2012-10-29 16.2 15.2 45.0 + +We can perform the match by ``name`` only by providing the ``keys`` argument, +which can be either a single column name or a list of column names:: + + >>> print(join(optical, xray, keys='name')) + name obs_date_1 mag_b mag_v obs_date_2 logLx + ---- ---------- ----- ----- ---------- ----- + M31 2012-01-02 17.0 16.0 1999-01-05 43.1 + M82 2012-10-29 16.2 15.2 2012-10-29 45.0 + +This output table has all of the observations that have both optical and X-ray +data for an object (M31 and M82). Notice that since the ``obs_date`` column +occurs in both tables, it has been split into two columns, ``obs_date_1`` and +``obs_date_2``. The values are taken from the "left" (``optical``) and "right" +(``xray``) tables, respectively. + +.. EXAMPLE END + +Different Join Options +^^^^^^^^^^^^^^^^^^^^^^ + +The table joins so far are known as "inner" joins and represent the strict +intersection of the two tables on the key columns. + +.. EXAMPLE START: Table Join Options + +If you want to make a new table which has *every* row from the left table and +includes matching values from the right table when available, this is known as a +left join:: + + >>> print(join(optical, xray, join_type='left')) + name obs_date mag_b mag_v logLx + ---- ---------- ----- ----- ----- + M101 2012-10-31 15.1 15.5 -- + M31 2012-01-02 17.0 16.0 -- + M82 2012-10-29 16.2 15.2 45.0 + +Two of the observations do not have X-ray data, as indicated by the ``--`` in +the table. You might be surprised that there is no X-ray data for M31 in the +output. Remember that the default matching key includes both ``name`` and +``obs_date``. Specifying the key as only the ``name`` column gives:: + + >>> print(join(optical, xray, join_type='left', keys='name')) + name obs_date_1 mag_b mag_v obs_date_2 logLx + ---- ---------- ----- ----- ---------- ----- + M101 2012-10-31 15.1 15.5 -- -- + M31 2012-01-02 17.0 16.0 1999-01-05 43.1 + M82 2012-10-29 16.2 15.2 2012-10-29 45.0 + +Likewise you can construct a new table with every row of the right table and +matching left values (when available) using ``join_type='right'``. + +To make a table with the union of rows from both tables do an "outer" join:: + + >>> print(join(optical, xray, join_type='outer')) + name obs_date mag_b mag_v logLx + ------- ---------- ----- ----- ----- + M101 2012-10-31 15.1 15.5 -- + M31 1999-01-05 -- -- 43.1 + M31 2012-01-02 17.0 16.0 -- + M82 2012-10-29 16.2 15.2 45.0 + NGC3516 2011-11-11 -- -- 42.1 + +In all the above cases the output join table will be sorted by the key +column(s) and in general will not preserve the row order of the input tables. + +Finally, you can do a "Cartesian" join, which is the Cartesian product of all +available rows. In this case there are no key columns (and supplying the +``keys`` argument is an error):: + + >>> print(join(optical, xray, join_type='cartesian')) + name_1 obs_date_1 mag_b mag_v name_2 obs_date_2 logLx + ------ ---------- ----- ----- ------- ---------- ----- + M31 2012-01-02 17.0 16.0 NGC3516 2011-11-11 42.1 + M31 2012-01-02 17.0 16.0 M31 1999-01-05 43.1 + M31 2012-01-02 17.0 16.0 M82 2012-10-29 45.0 + M82 2012-10-29 16.2 15.2 NGC3516 2011-11-11 42.1 + M82 2012-10-29 16.2 15.2 M31 1999-01-05 43.1 + M82 2012-10-29 16.2 15.2 M82 2012-10-29 45.0 + M101 2012-10-31 15.1 15.5 NGC3516 2011-11-11 42.1 + M101 2012-10-31 15.1 15.5 M31 1999-01-05 43.1 + M101 2012-10-31 15.1 15.5 M82 2012-10-29 45.0 + +.. EXAMPLE END + +Non-Identical Key Column Names +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. EXAMPLE START: Joining Tables with Unique Key Column Names + +To use the |join| function with non-identical key column names, use the +``keys_left`` and ``keys_right`` arguments. In the following example one table +has a ``'name'`` column while the other has an ``'obj_id'`` column:: + + >>> optical = Table.read("""name obs_date mag_b mag_v + ... M31 2012-01-02 17.0 16.0 + ... M82 2012-10-29 16.2 15.2 + ... M101 2012-10-31 15.1 15.5""", format='ascii') + >>> xray_1 = Table.read("""obj_id obs_date logLx + ... NGC3516 2011-11-11 42.1 + ... M31 1999-01-05 43.1 + ... M82 2012-10-29 45.0""", format='ascii') + +In order to perform a match based on the names of the objects, do the +following:: + + >>> print(join(optical, xray_1, keys_left='name', keys_right='obj_id')) + name obs_date_1 mag_b mag_v obj_id obs_date_2 logLx + ---- ---------- ----- ----- ------ ---------- ----- + M31 2012-01-02 17.0 16.0 M31 1999-01-05 43.1 + M82 2012-10-29 16.2 15.2 M82 2012-10-29 45.0 + +The ``keys_left`` and ``keys_right`` arguments can also take a list of column +names or even a list of column-like objects. The latter case allows specifying +the matching key column values independent of the tables being joined. + +.. EXAMPLE END + +Identical Key Values +^^^^^^^^^^^^^^^^^^^^ + +.. EXAMPLE START: Joining Tables with Identical Key Values + +The |Table| join operation works even if there are multiple rows with identical +key values. For example, the following tables have multiple rows for the column +``'key'``:: + + >>> from astropy.table import Table, join + >>> left = Table([[0, 1, 1, 2], ['L1', 'L2', 'L3', 'L4']], names=('key', 'L')) + >>> right = Table([[1, 1, 2, 4], ['R1', 'R2', 'R3', 'R4']], names=('key', 'R')) + >>> print(left) + key L + --- --- + 0 L1 + 1 L2 + 1 L3 + 2 L4 + >>> print(right) + key R + --- --- + 1 R1 + 1 R2 + 2 R3 + 4 R4 + +Doing an outer join on these tables shows that what is really happening is a +`Cartesian product `_. For +each matching key, every combination of the left and right tables is +represented. When there is no match in either the left or right table, the +corresponding column values are designated as missing:: + + >>> print(join(left, right, join_type='outer')) + key L R + --- --- --- + 0 L1 -- + 1 L2 R1 + 1 L2 R2 + 1 L3 R1 + 1 L3 R2 + 2 L4 R3 + 4 -- R4 + +An inner join is the same but only returns rows where there is a key match in +both the left and right tables:: + + >>> print(join(left, right, join_type='inner')) + key L R + --- --- --- + 1 L2 R1 + 1 L2 R2 + 1 L3 R1 + 1 L3 R2 + 2 L4 R3 + +Conflicts in the input table names are handled by the process described in the +section on `Column renaming`_. See also the sections on `Merging metadata`_ and +`Merging column attributes`_ for details on how these characteristics of the +input tables are merged in the single output table. + +.. EXAMPLE END + +Merging Details +--------------- + +When combining two or more tables there is the need to merge certain +characteristics in the inputs and potentially resolve conflicts. This +section describes the process. + +Column Renaming +^^^^^^^^^^^^^^^ + +In cases where the input tables have conflicting column names, there +is a mechanism to generate unique output column names. There are two +keyword arguments that control the renaming behavior: + +``table_names`` + List of strings that provide names for the tables being joined. + By default this is ``['1', '2', ...]``, where the numbers correspond to + the input tables. + +``uniq_col_name`` + String format specifier with a default value of ``'{col_name}_{table_name}'``. + +This is best understood by example using the ``optical`` and ``xray`` tables +in the |join| example defined previously:: + + >>> print(join(optical, xray, keys='name', + ... table_names=['OPTICAL', 'XRAY'], + ... uniq_col_name='{table_name}_{col_name}')) + name OPTICAL_obs_date mag_b mag_v XRAY_obs_date logLx + ---- ---------------- ----- ----- ------------- ----- + M31 2012-01-02 17.0 16.0 1999-01-05 43.1 + M82 2012-10-29 16.2 15.2 2012-10-29 45.0 + +.. _merging_metadata: + +Merging Metadata +^^^^^^^^^^^^^^^^ + +|Table| objects can have associated metadata: + +- ``Table.meta``: table-level metadata as an ordered dictionary +- ``Column.meta``: per-column metadata as an ordered dictionary + +The table operations described here handle the task of merging the metadata in +the input tables into a single output structure. Because the metadata can be +arbitrarily complex there is no unique way to do the merge. The current +implementation uses a recursive algorithm with four rules: + +- :class:`dict` elements are merged by keys. +- Conflicting :class:`list` or :class:`tuple` elements are concatenated. +- Conflicting :class:`dict` elements are merged by recursively calling the + merge function. +- Conflicting elements that are not :class:`list`, :class:`tuple`, or + :class:`dict` will follow the following rules: + + - If both metadata values are identical, the output is set to this value. + - If one of the conflicting metadata values is `None`, the other value is + picked. + - If both metadata values are different and neither is `None`, the one for + the last table in the list is picked. + +By default, a warning is emitted in the last case (both metadata values are not +`None`). The warning can be silenced or made into an exception using the +``metadata_conflicts`` argument to :func:`~astropy.table.hstack`, +:func:`~astropy.table.vstack`, or +:func:`~astropy.table.join`. The ``metadata_conflicts`` option can be set to: + +- ``'silent'`` – no warning is emitted, the value for the last table is silently + picked. +- ``'warn'`` – a warning is emitted, the value for the last table is picked. +- ``'error'`` – an exception is raised. + +The default strategies for merging metadata can be augmented or customized by +defining subclasses of the `~astropy.utils.metadata.MergeStrategy` base class. +In most cases you will also use +:func:`~astropy.utils.metadata.enable_merge_strategies` for enabling the custom +strategies. The linked documentation strings provide details. + +Merging Column Attributes +^^^^^^^^^^^^^^^^^^^^^^^^^ + +In addition to the table and column ``meta`` attributes, the column attributes +``unit``, ``format``, and ``description`` are merged by going through the input +tables in order and taking the last value which is defined (i.e., is not +`None`). + +Example +~~~~~~~ + +.. EXAMPLE START: Merging Column Attributes in a Table + +To merge column attributes ``unit``, ``format``, or ``description``:: + + >>> from astropy.table import Column, Table, vstack + >>> col1 = Column([1], name='a') + >>> col2 = Column([2], name='a', unit='cm') + >>> col3 = Column([3], name='a', unit='m') + >>> t1 = Table([col1]) + >>> t2 = Table([col2]) + >>> t3 = Table([col3]) + >>> out = vstack([t1, t2, t3]) # doctest: +SHOW_WARNINGS + MergeConflictWarning: In merged column 'a' the 'unit' attribute does + not match (cm != m). Using m for merged output + >>> out['a'].unit + Unit("m") + +The rules for merging are the same as for `Merging metadata`_, and the +``metadata_conflicts`` option also controls the merging of column attributes. + +.. EXAMPLE END + +.. _astropy-table-join-functions: + +Joining Coordinates and Custom Join Functions +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Source catalogs that have |SkyCoord| coordinate columns can be joined using +cross-matching of the coordinates with a specified distance threshold. This is +a special case of a more general problem of "fuzzy" matching of key column +values, where instead of an exact match we require only an approximate match. +This is supported using the ``join_funcs`` argument. + +.. warning:: + + The coordinate and distance table joins discussed in this section are most + applicable in the case where the relevant entries in at least one of the + tables are all separated from one another by more than twice the join + distance. If this is not satisfied then the join results may be unexpected. + + This is a consequence of the algorithm which effectively finds clusters of + nearby points (an "equivalence class") and assigns a unique cluster + identifier to each entry in both tables. This assumes the join matching + function is a transitive relation where ``join_func(A, B)`` and + ``join_func(B, C)`` implies ``join_func(A, C)``. With multiple matches on + both left and right sides it is possible for the cluster of points having a + single cluster identifier to expand in size beyond the distance threshold. + + Users should be especially aware of this issue if additional join keys + are provided beyond the ``join_funcs``. The code does not do a "pre-join" + on the other keys, so the possibility of having overlaps within the distance + in both tables is higher. + +Example +~~~~~~~ + +.. EXAMPLE START: Joining a Table on Coordinates + +To join two tables on a |SkyCoord| key column we use the ``join_funcs`` keyword +to supply a :class:`dict` of functions that specify how to match a particular +key column by name. In the example below we are joining on the ``sc`` column, +so we provide the following argument:: + + join_funcs={'sc': join_skycoord(0.2 * u.deg)} + +This tells |join| to match the ``sc`` key column using the join function +:func:`~astropy.table.join_skycoord` with a matching distance threshold of 0.2 +deg. Under the hood this calls +:meth:`~astropy.coordinates.SkyCoord.search_around_sky` or +:meth:`~astropy.coordinates.SkyCoord.search_around_3d` to do the +cross-matching. The default is to use +:meth:`~astropy.coordinates.SkyCoord.search_around_sky` (angle) matching, but +:meth:`~astropy.coordinates.SkyCoord.search_around_3d` (length or +dimensionless) is also available. This is specified using the ``distance_func`` +argument of :func:`~astropy.table.join_skycoord`, which can also be a function +that matches the input and output API of +:meth:`~astropy.coordinates.SkyCoord.search_around_sky`. + +Now we show the whole process: + +.. doctest-requires:: scipy + + >>> from astropy.coordinates import SkyCoord + >>> import astropy.units as u + >>> from astropy.table import Table, join, join_skycoord + +.. doctest-requires:: scipy + + >>> sc1 = SkyCoord([0, 1, 1.1, 2], [0, 0, 0, 0], unit='deg') + >>> sc2 = SkyCoord([1.05, 0.5, 2.1], [0, 0, 0], unit='deg') + +.. doctest-requires:: scipy + + >>> t1 = Table([sc1, [0, 1, 2, 3]], names=['sc', 'idx']) + >>> t2 = Table([sc2, [0, 1, 2]], names=['sc', 'idx']) + +.. doctest-requires:: scipy + + >>> t12 = join(t1, t2, keys='sc', join_funcs={'sc': join_skycoord(0.2 * u.deg)}) + >>> print(t12) + sc_id sc_1 idx_1 sc_2 idx_2 + deg,deg deg,deg + ----- ------- ----- -------- ----- + 1 1.0,0.0 1 1.05,0.0 0 + 1 1.1,0.0 2 1.05,0.0 0 + 2 2.0,0.0 3 2.1,0.0 2 + +The joined table has matched the sources within 0.2 deg and created a new +column ``sc_id`` with a unique identifier for each source. + +.. EXAMPLE END + +You might be wondering what is happening in the join function defined above, +especially if you are interested in defining your own such function. This could +be done in order to allow fuzzy word matching of tables, for example joining +tables of people by name where the names do not always match exactly. + +The first thing to note here is that the :func:`~astropy.table.join_skycoord` +function actually returns a function itself. This allows specifying a variable +match distance via a function enclosure. The requirement of the join function +is that it accepts two arguments corresponding to the two key columns, and +returns a tuple of ``(ids1, ids2)``. These identifiers correspond to the +identification of each column entry with a unique matched source. + +.. doctest-requires:: scipy + + >>> join_func = join_skycoord(0.2 * u.deg) + >>> join_func(sc1, sc2) # Associate each coordinate with unique source ID + (array([3, 1, 1, 2]), array([1, 4, 2])) + +If you would like to write your own fuzzy matching function, we suggest starting +from the source code for :func:`~astropy.table.join_skycoord` or +:func:`~astropy.table.join_distance`. + +Join on Distance +~~~~~~~~~~~~~~~~ + +The example above focused on joining on a |SkyCoord|, but you can also join on +a generic distance between column values using the +:func:`~astropy.table.join_distance` join function. This can apply to 1D or 2D +(vector) columns. This will look very similar to the coordinates example, but +here there is a bit more flexibility. The matching is done using +:class:`scipy.spatial.KDTree` and +:meth:`scipy.spatial.KDTree.query_ball_tree`, and the behavior of these can be +controlled via the ``kdtree_args`` and ``query_args`` arguments, respectively. + +.. _unique-rows: + +Unique Rows +----------- + +Sometimes it makes sense to use only rows with unique key columns or even +fully unique rows from a table. This can be done using the above described +:meth:`~astropy.table.Table.group_by` method and ``groups`` attribute, or with +the :func:`~astropy.table.unique` convenience function. The +:func:`~astropy.table.unique` function returns a sorted table containing the +first row for each unique ``keys`` column value. If no ``keys`` is provided, it +returns a sorted table containing all of the fully unique rows. + +Example +^^^^^^^ + +.. EXAMPLE START: Grouping Unique Rows in Tables + +An example of a situation where you might want to use rows with unique key +columns is a list of objects with photometry from various observing +runs. Using ``'name'`` as the only ``keys``, it returns with the first +occurrence of each of the three targets:: + + >>> from astropy import table + >>> obs = table.Table.read("""name obs_date mag_b mag_v + ... M31 2012-01-02 17.0 17.5 + ... M82 2012-02-14 16.2 14.5 + ... M101 2012-01-02 15.1 13.5 + ... M31 2012-01-02 17.1 17.4 + ... M101 2012-01-02 15.1 13.5 + ... M82 2012-02-14 16.2 14.5 + ... M31 2012-02-14 16.9 17.3 + ... M82 2012-02-14 15.2 15.5 + ... M101 2012-02-14 15.0 13.6 + ... M82 2012-03-26 15.7 16.5 + ... M101 2012-03-26 15.1 13.5 + ... M101 2012-03-26 14.8 14.3 + ... """, format='ascii') + >>> unique_by_name = table.unique(obs, keys='name') + >>> print(unique_by_name) + name obs_date mag_b mag_v + ---- ---------- ----- ----- + M101 2012-01-02 15.1 13.5 + M31 2012-01-02 17.0 17.5 + M82 2012-02-14 16.2 14.5 + +Using multiple columns as ``keys``:: + + >>> unique_by_name_date = table.unique(obs, keys=['name', 'obs_date']) + >>> print(unique_by_name_date) + name obs_date mag_b mag_v + ---- ---------- ----- ----- + M101 2012-01-02 15.1 13.5 + M101 2012-02-14 15.0 13.6 + M101 2012-03-26 15.1 13.5 + M31 2012-01-02 17.0 17.5 + M31 2012-02-14 16.9 17.3 + M82 2012-02-14 16.2 14.5 + M82 2012-03-26 15.7 16.5 + +.. EXAMPLE END + +.. _set-difference: + +Set Difference +-------------- + +A set difference will tell you the elements that are contained in the first set +but not in the other. This concept can be applied to rows of a table by using +the :func:`~astropy.table.setdiff` function. You provide the function with two +input tables and it will return all rows in the first table which do not occur +in the second table. + +The optional ``keys`` parameter specifies the names of columns that are used to +match table rows. This can be a subset of the full list of columns, but both +the first and second tables must contain all columns specified by ``keys``. +If not provided, then ``keys`` defaults to all column names in the first table. + +If no different rows are found, the :func:`~astropy.table.setdiff` function +will return an empty table. + +Example +^^^^^^^ + +.. EXAMPLE START: Using Set Difference in Tables + +The example below illustrates finding the set difference of two observation +lists using a common subset of the columns in two tables.:: + + >>> from astropy.table import Table, setdiff + >>> cat_1 = Table.read("""name obs_date mag_b mag_v + ... M31 2012-01-02 17.0 16.0 + ... M82 2012-10-29 16.2 15.2 + ... M101 2012-10-31 15.1 15.5""", format='ascii') + >>> cat_2 = Table.read(""" name obs_date logLx + ... NGC3516 2011-11-11 42.1 + ... M31 2012-01-02 43.1 + ... M82 2012-10-29 45.0""", format='ascii') + >>> sdiff = setdiff(cat_1, cat_2, keys=['name', 'obs_date']) + >>> print(sdiff) + name obs_date mag_b mag_v + ---- ---------- ----- ----- + M101 2012-10-31 15.1 15.5 + +In this example there is a column in the first table that is not +present in the second table, so the ``keys`` parameter must be used to specify +the desired column names. + +.. EXAMPLE END + +.. _table-diff: + +Table Diff +---------- + +To compare two tables, you can use +:func:`~astropy.utils.diff.report_diff_values`, which would produce a report +identical to :ref:`FITS diff `. + +Example +^^^^^^^ + +.. EXAMPLE START: Using Table Diff to Compare Tables + +The example below illustrates finding the difference between two tables:: + + >>> from astropy.table import Table + >>> from astropy.utils.diff import report_diff_values + >>> import sys + >>> cat_1 = Table.read("""name obs_date mag_b mag_v + ... M31 2012-01-02 17.0 16.0 + ... M82 2012-10-29 16.2 15.2 + ... M101 2012-10-31 15.1 15.5""", format='ascii') + >>> cat_2 = Table.read("""name obs_date mag_b mag_v + ... M31 2012-01-02 17.0 16.5 + ... M82 2012-10-29 16.2 15.2 + ... M101 2012-10-30 15.1 15.5 + ... NEW 2018-05-08 nan 9.0""", format='ascii') + >>> identical = report_diff_values(cat_1, cat_2, fileobj=sys.stdout) + name obs_date mag_b mag_v + ---- ---------- ----- ----- + a> M31 2012-01-02 17.0 16.0 + ? ^ + b> M31 2012-01-02 17.0 16.5 + ? ^ + M82 2012-10-29 16.2 15.2 + a> M101 2012-10-31 15.1 15.5 + ? ^ + b> M101 2012-10-30 15.1 15.5 + ? ^ + b> NEW 2018-05-08 nan 9.0 + >>> identical + False + +.. EXAMPLE END diff --git a/docs/table/pandas.rst b/docs/table/pandas.rst new file mode 100644 index 000000000000..3d3c6d1eb2c1 --- /dev/null +++ b/docs/table/pandas.rst @@ -0,0 +1,11 @@ +.. doctest-skip-all + +.. _pandas: + +Interfacing with the Pandas Package +*********************************** + +.. note:: + Since Astropy 7.2, this documentation has been expanded and moved to :ref:`df_narwhals`. + The linked paragraph covers both pandas-specific methods and generic multi-backend + DataFrame support through narwhals. diff --git a/docs/table/performance.inc.rst b/docs/table/performance.inc.rst new file mode 100644 index 000000000000..58ee1d4b43b4 --- /dev/null +++ b/docs/table/performance.inc.rst @@ -0,0 +1,78 @@ +.. note that if this is changed from the default approach of using an *include* + (in index.rst) to a separate performance page, the header needs to be changed + from === to ***, the filename extension needs to be changed from .inc.rst to + .rst, and a link needs to be added in the subpackage toctree + +.. doctest-skip-all + +.. _astropy-table-performance: + +Performance Tips +================ + +Constructing |Table| objects row by row using +:meth:`~astropy.table.Table.add_row` can be very slow:: + + >>> from astropy.table import Table + >>> t = Table(names=['a', 'b']) + >>> for i in range(100): + ... t.add_row((1, 2)) + +If you do need to loop in your code to create the rows, a much faster approach +is to construct a list of rows and then create the |Table| object at the very +end:: + + >>> rows = [] + >>> for i in range(100): + ... rows.append((1, 2)) + >>> t = Table(rows=rows, names=['a', 'b']) + +Writing a |Table| with |MaskedColumn| to ``.ecsv`` using +:meth:`~astropy.table.Table.write` can be very slow:: + + >>> from astropy.table import Table + >>> import numpy as np + >>> x = np.arange(10000, dtype=float) + >>> tm = Table([x], masked=True) + >>> tm.write('tm.ecsv', overwrite=True) + +If you want to write ``.ecsv`` using :meth:`~astropy.table.Table.write`, +then use ``serialize_method='data_mask'``. +This uses the non-masked version of data and it is faster:: + + >>> tm.write('tm.ecsv', overwrite=True, serialize_method='data_mask') + +Read FITS with memmap=True +-------------------------- + +By default :meth:`~astropy.table.Table.read` will read the whole table into +memory, which can take a lot of memory and can take a lot of time, depending on +the table size and file format. In some cases, it is possible to only read a +subset of the table by choosing the option ``memmap=True``. + +For FITS binary tables, the data is stored row by row, and it is possible to +read only a subset of rows, but reading a full column loads the whole table data +into memory:: + + >>> import numpy as np + >>> from astropy.table import Table + >>> tbl = Table({'a': np.arange(1e7), + ... 'b': np.arange(1e7, dtype=float), + ... 'c': np.arange(1e7, dtype=float)}) + >>> tbl.write('test.fits', overwrite=True) + >>> table = Table.read('test.fits', memmap=True) # Very fast, doesn't actually load data + >>> table2 = tbl[:100] # Fast, will read only first 100 rows + >>> print(table2) # Accessing column data triggers the read + a b c + ---- ---- ---- + 0.0 0.0 0.0 + 1.0 1.0 1.0 + 2.0 2.0 2.0 + ... ... ... + 98.0 98.0 98.0 + 99.0 99.0 99.0 + Length = 100 rows + >>> col = table['my_column'] # Will load all table into memory + +:meth:`~astropy.table.Table.read` does not support ``memmap=True`` +for the HDF5 and text file formats. diff --git a/docs/table/ref_api.rst b/docs/table/ref_api.rst new file mode 100644 index 000000000000..bd4440fe2488 --- /dev/null +++ b/docs/table/ref_api.rst @@ -0,0 +1,12 @@ +Reference/API +************* + +Capabilities +============ + +.. automodapi:: astropy.table + +Notebook Backends +================= + +.. automodapi:: astropy.table.notebook_backends diff --git a/docs/table/references.txt b/docs/table/references.txt deleted file mode 100644 index d2eb3918423c..000000000000 --- a/docs/table/references.txt +++ /dev/null @@ -1,5 +0,0 @@ -.. |Row| replace:: :class:`~astropy.table.table.Row` -.. |Table| replace:: :class:`~astropy.table.table.Table` -.. |Column| replace:: :class:`~astropy.table.table.Column` -.. |TableColumns| replace:: :class:`~astropy.table.table.TableColumns` -.. _OrderedDict: http://docs.python.org/library/collections.html#collections.OrderedDict diff --git a/docs/table/table_and_dataframes.rst b/docs/table/table_and_dataframes.rst new file mode 100644 index 000000000000..5c8794a15cf3 --- /dev/null +++ b/docs/table/table_and_dataframes.rst @@ -0,0 +1,90 @@ +.. _astropy-table-and-dataframes: + +Astropy Table and DataFrames +============================ + +`Pandas `_ is a popular data manipulation library for Python +that provides a `~pandas.DataFrame` object which is similar to `astropy.table`. A common +question is why Astropy does not use `~pandas.DataFrame` as the base table object, or a DataFrame-like object from another library such as ``polars``. +The answer stems from a number of domain-specific requirements related to astronomical data and analysis. + +Units and Quantities +-------------------- + +Astronomy is a physical science, and the data often have units associated with +them. The `astropy.table` package natively supports |Quantity| columns, which are a +powerful way to attach units to array data and perform unit-aware operations. In +addition, the base `~astropy.table.Column` class holds a ``unit`` attribute as +metadata to allow tracking of the units of the data for applications not using +|Quantity|. + +*At the time of writing, neither pandas nor polars provide support for units.* + +Multi-dimensional and Structured Columns +---------------------------------------- + +Astronomers deal with images, spectra, and other multi-dimensional data that are +commonly stored in a table. An example is a source catalog with an image thumbnail and a +spectrum for each source. Structured columns are less common, but are useful for storing +vectorized data like an `~astropy.coordinates.EarthLocation` in a table. + +*Pandas is not able to natively store multi-dimensional or structured columns.* +*There is limited support for multi-dimensional columns in Polars.* + +Lossless representation of FITS and VOTable data via metadata +------------------------------------------------------------- + +The `astropy.table` package strives to provide lossless representation of FITS and +VOTable data. This means that when you read a FITS or VOTable file into a table and then +write it back out, the data will be effectively identical. This is made possible by +robust support for table and column metadata which allows storing and propagating common +column information such as the unit, description, and format. For VOTable data, more +information like the UCD is maintained. + +*Pandas provides limited support for metadata, but as of late-2024 it is highlighted as +"experimental" in the documentation.* +*Polars has limited support for metadata.* + +Time and Coordinates +-------------------- + +Time and coordinates are fundamental to astronomy, and astropy provides robust support +for them with the `~astropy.time.Time` and `~astropy.coordinates.SkyCoord` classes. +Arrays of times and coordinates can be natively stored in `astropy.table`, meaning that +the full power of these objects is available when working with them as columns within a +table. + +*Pandas supports* `timeseries +`_ *data, but with key +limitations*: + +- Leap seconds are not supported. In many circumstances (for instance planning an + observation) this limitation is not acceptable. +- Pandas times are stored with 64-bit precision, which is not sufficient for some + astronomical applications. Astropy uses 128-bit precision for time to allow + sub-nanosecond precision over the age of the universe. +- Different :ref:`time scales ` common in astronomy (e.g., TAI, UT1) are + not supported. +- :ref:`Time formats ` used in astronomy such as the FITS time format are + not supported. + +*Pandas does not support sky coordinate columns.* +*Limitations are similar for Polars as well.* + +Responsiveness to Community Needs +--------------------------------- + +The `astropy.table` package is developed by the Astropy community, which is focused on +the needs of astronomers and astrophysicists. This means that the development of the +package can be responsive to the needs of this community and we can develop features +without being constrained by the potential impact to the far broader user base of +Pandas or Polars. + +Interoperability +---------------- + +We recognize that Pandas is a popular library and that there are many users who are +familiar with it. For this reason, we have made it easy to convert between +`astropy.table` and DataFrame-like objects, such as `~pandas.DataFrame` and ``polars.DataFrame`` through the `narwhals `_ translation layer as documented in :ref:`df_narwhals`. +This allows users to take advantage of all supported features of both packages as needed, +within the limitations stated above. diff --git a/docs/table/table_architecture.png b/docs/table/table_architecture.png new file mode 100644 index 000000000000..79666298a4ad Binary files /dev/null and b/docs/table/table_architecture.png differ diff --git a/docs/table/table_repr_html.png b/docs/table/table_repr_html.png new file mode 100644 index 000000000000..82d27e19012b Binary files /dev/null and b/docs/table/table_repr_html.png differ diff --git a/docs/table/table_row.png b/docs/table/table_row.png new file mode 100644 index 000000000000..0cdcbaefda4d Binary files /dev/null and b/docs/table/table_row.png differ diff --git a/docs/time/index.rst b/docs/time/index.rst new file mode 100644 index 000000000000..a68af35c78bc --- /dev/null +++ b/docs/time/index.rst @@ -0,0 +1,1802 @@ +.. _astropy-time: + +******************************* +Time and Dates (`astropy.time`) +******************************* + +Introduction +============ + +The `astropy.time` package provides functionality for manipulating times and +dates. Specific emphasis is placed on supporting time scales (e.g., UTC, TAI, +UT1, TDB) and time representations (e.g., JD, MJD, ISO 8601) that are used in +astronomy and required to calculate, for example, sidereal times and barycentric +corrections. The `astropy.time` package is based on fast and memory efficient +|PyERFA| wrappers around the |ERFA| time and calendar routines. + +All time manipulations and arithmetic operations are done internally using two +64-bit floats to represent time. Floating point algorithms from [#]_ are used so +that the |Time| object maintains sub-nanosecond precision over times spanning +the age of the universe. + +.. [#] Shewchuk, 1997, Discrete & Computational Geometry 18(3):305-363 + +Getting Started +=============== + +The usual way to use `astropy.time` is to create a |Time| object by +supplying one or more input time values as well as the `time format`_ and `time +scale`_ of those values. The input time(s) can either be a single scalar like +``"2010-01-01 00:00:00"`` or a list or a ``numpy`` array of values as shown +below. In general, any output values have the same shape (scalar or array) as +the input. + +Examples +-------- + +.. EXAMPLE START: Creating a Time Object with astropy.time + +To create a |Time| object: + + >>> import numpy as np + >>> from astropy.time import Time + >>> times = ['1999-01-01T00:00:00.123456789', '2010-01-01T00:00:00'] + >>> t = Time(times, format='isot', scale='utc') + >>> t +